JN5189に内蔵されている NTAG I2C コントローラ (NT3H2211相当) を使用するための手続きをまとめています。
単なる EEPROM として使うほか、NFC リーダとの通信を行うことができます。通信方式には、EEPROM を使った NFC Forum Type 2 Tag として振る舞う標準 EEPROM モードと、Pass-throughな独自のプロトコルによってコマンド-レスポンスのやりとりを行う拡張 SRAM モードがあります。
コントローラはI2C接続されていますが、mwf::periph::i2cは用いません。専用の API と I2C2 ポートを使用します。
EEPROM の使用
JN5189 に単体の EEPROM は搭載されておらず、EEPROM を使用する場合は NTAG I2C コントローラの EEPROM を使用します。 この手続きにより読み書きできるのは 1KB の領域(NT3H2211 の I2C block address 64-127)です。
EEPROM を扱うコード例
#include "mwf_periph_ntag.hpp"
void func() {
// create the_ntag class object.
if (!mwf::the_ntag) {
mwf::ntag::global_init_ntag_manager();
}
// initialize
mwf::the_ntag->init();
// write 128bytes
uint8_t xfer1[128];
for (unsigned i = 0; i < 128; i++) xfer1[i] = i;
mwf::the_ntag->write_user_area(0x00, xfer1);
// read 128bytes
uint8_t xfer2[128];
mwf::the_ntag->read_user_area(0x00, xfer2);
}
全般
global_init_ntag_manager(), global_deinit_ntag_manager()
static void global_init_ntag_manager();
static void global_deinit_ntag_manager();
the_ntagクラスオブジェクトの生成と破棄を行います。
init(), deinit()
void init();
void deinit();
デバイスアクセスのための初期化と利用終了手続きを行います。初期化init()には300usecの待ち処理が含まれます。
EEPROM のみ
write_user_area()
bool write_user_area(uint16_t addr, const uint8_t *p, uint16_t len);
template <unsigned N> bool write_user_area(uint8_t addr, const uint8_t (&buf)[N]);
EEPROMのユーザエリアにバイト列を書き込みます。
addrは開始アドレスで0..1023を指定します。pまたはbufは書込みデータのバッファです。lenまたはNはデータバイト数です。
read_user_area()
bool read_user_area(uint16_t addr, uint8_t *p, uint16_t len);
template <unsigned N> bool read_user_area(uint8_t addr, const uint8_t (&buf)[N]);
EEPROMのユーザエリアからバイト列を読み出します。
addrは開始アドレスで0..1023を指定します。pまたはbufは読み出し先のデータのバッファです。lenまたはNはデータバイト数です。
NFC の使用
NFC 標準 EEPROM モード
NFC 標準 EEPROM モードでは、本体あるいはリーダから EEPROM の領域に NDEF メッセージを書き込んだり、NDEF メッセージを読み出したりすることができます。一般的な NFC タグと互換性があるものの、同様に転送速度は低速であり、一度のセッションで双方向通信はできません。NFC Tools(Android / iOS)などの汎用アプリを使用できます。

EEPROM モードの動作
汎用的な NFC Forum Type 2 Tag として振る舞い、NDEF URI レコードの書き込みや Text レコードの読み書きを行う act サンプルを以下に示します。
#include <TWELITE>
#include "mwf_periph_ntag.hpp"
/*** the setup procedure (called on boot) */
void setup() {
Serial << "--- NFC Tag Text Example ---" << crlf;
}
/*** the begin procedure (called after boot) */
void begin() {
mwf::the_ntag->disable_pthru_mode(); // enabled by default
mwf::the_ntag->write_uri("twelite.net");
Serial << "Set an URL as default. Can be overwritten with text via UART or NFC." << crlf;
}
/*** the loop procedure (called every event) */
void loop() {
// NFC output text buffer
static char output_text[128];
static char* output_text_ptr = &output_text[0];
// Write serial input to NFC output
if (Serial.available()) {
char c;
Serial >> c;
Serial << c; // echo back
*(output_text_ptr++) = c;
if (c == '\n') {
*output_text_ptr = '\0';
mwf::the_ntag->write_text(output_text);
Serial << format("Set: %s", output_text) << crlf;
output_text_ptr = &output_text[0];
}
}
// Read NFC input if available
if (mwf::the_ntag->available()) {
char type;
mwf::the_ntag->read_ndef_record_type(&type);
if (type == 'T') { // Text record
char input_text[128];
char input_lang[3];
mwf::the_ntag->read_text(input_text, input_lang);
Serial << format("Read: %s", input_text) << crlf;
}
}
}
- TWENET の初期化時に 拡張 SRAM モードを有効化するため、
begin()内で無効化します write_uri()で NDEF URI レコードを書き込むことができます- スマホをタッチすればブラウザを開くことができます
write_text()で NDEF Text レコードを書き込むことができます- 新たな NDEF メッセージがリーダによって書き込まれると、
available()がtrueを返します。 read_ndef_record_type()で書き込まれた NDEF レコードの種別を確認します- 注)一つの NDEF メッセージにつき、一つの NDEF レコードを想定しています
read_text()で NDEF Text メッセージを読み取ります
NFC 拡張 SRAM モード
NFC 拡張 SRAM モードでは、リーダが送るコマンドに対して、端末が応答します。コマンドとレスポンスは一つまたは複数のフレームで構成されます。SRAM は 64バイトの領域を持ち、これをフレームごとの転送に使用します。転送速度は速く、データを一度のセッションで打ち返すことができます。

SRAM モードの動作
TWELITE アプリは、以下のように定めた MWish (Mono Wireless Interactive Shell) 形式のフレームをやり取りします。

MWish フレーム
TWELITE アプリ(Android / iOS)のコマンド入力に応答する act サンプルを以下に示します。
ここでは、次のようにテキストを返すコマンド foo を実装しています。

TWELITE アプリで動作する様子
foo->bar- foo に対して bar を返します
foo 3->barbarbar- 引数に指定した数だけ bar を繰り返します。
foo 1000といった大容量の転送をテストできます
- 引数に指定した数だけ bar を繰り返します。
foo -u->BAR-uオプション(フラグ)により、大文字の BAR を返します
foo -u 3->BARBARBAR-uオプションと引数は同時に適用できます
#include <TWELITE>
#include "mwf_periph_ntag.hpp"
using namespace mwf::periph;
bool handle_commands(const ntag::MWishCommand* const c, ntag::MWishResponse* const r) {
Serial << "Received user command " << c->command_str
<< format(" with flag(s) \"%s\" and data \"%s\"", c->flags, c->data) << crlf;
// Command foo [-u] [times] implementation:
// "foo" -> "bar"
// "foo 3" -> "barbarbar"
// "foo -u" -> "BAR"
// "foo -u 3" -> "BARBARBAR"
if (c->is_command("foo") and c->get_data_type() == ntag::PTHRU_DATA_TYPE::RAW_STRING) {
int times = strtol(reinterpret_cast<char*>(c->data), nullptr, 10);
if (times < 1 or times * 3 > ntag::MWishResponse::DATA_MAX_SIZE - 1) { times = 1; }
for (int i = 0; i < times; i++) {
if (c->contains_flag('u')) {
r->printf("BAR");
} else {
r->printf("bar");
}
}
return true;
}
return false; // No such command
}
/*** the setup procedure (called on boot) */
void setup() {
Serial << "--- NFC App Command Example ---" << crlf;
}
/*** the begin procedure (called after boot) */
void begin() {
mwf::the_ntag->enable_pthru_mode(); // enabled by default
mwf::the_ntag->attach_pthru_command_handler(handle_commands);
Serial << "Registered user command(s)." << crlf;
}
/*** the loop procedure (called every event) */
void loop() {
// Update NFC pass-through process
mwf::the_ntag->update_pthru();
}
- TWENET の初期化時に 拡張 SRAM モードは有効化されますが、
begin()内で明示的に有効化しています attach_pthru_command_handler()で コマンドハンドラを処理するコールバック関数を登録できます- ここでは、冒頭の
handle_commands()を登録しています ntag::MWishCommandで与えられるcに対してntag::MWishResponseのrを埋めてゆきますc->is_command()でコマンド文字列(最大7文字)を照合していますc->data()で引数として与えられた文字列を取得できます。ここではstrtol()で数値に変換していますc->contains_flag()でオプションフラグ(最大7つ)の有無を照合できます。ここでは-uオプションを検知していますr->printf()で文字列を格納できます。ここではbarまたはBARを格納しています
- ここでは、冒頭の
loop()内でupdate_pthru()を呼び出します
NFC 全般
get_ntag_context()
NTAG 関連のコンテキストを取得します。
ntag_context
NTAG 関連の状態変数を格納しています。
| 型 | 名称 | 説明 |
|---|---|---|
uint8_t | u8_i2c_address | I2Cアドレス |
bool | b_wake_enabled | スリープ起床の有効フラグ |
uint8_t | u8_latest_checksum | 最新のNDEFレコードのチェックサム |
bool | b_pthru_enabled | SRAMモードの有効フラグ |
_FD_STATE | fd_state | タグの検出状態 |
_PTHRU_STATE | pthru_state | SRAMモードの通信状態 |
void (*)() | fp_fd_in_additional | 近接時の追加ハンドラ |
void (*)() | fp_fd_out_additional | 離脱時の追加ハンドラ |
_FD_STATE
private フィールド検出の状態を表します。
NONEリーダなしOUTリーダ離脱INリーダ近接
_PTHRU_STATE
private SRAM モードの状態を表します。
NOT_PRESENTリーダなしRECEIVE_COMMANDコマンドの(初回)フレームを受信中RECEIVE_COMMAND_EXコマンドの拡張フレームを受信中SEND_RESPONSEレスポンスのフレームを送信中
enable_wakeup_source()
void enable_wakeup_source();
NFC によるスリープからの起床を有効化します。
disable_wakeup_source()
void disable_wakeup_source();
NFC によるスリープからの起床を無効化します。
check_if_wokeup_via_nfc()
static bool check_if_wokeup_via_nfc();
NFC によってスリープから起床した際は true を返します。
attach_callback_fd_in()
void attach_callback_fd_in(void (*callback)());
リーダに検出された際のコールバック関数を登録します。
attach_callback_fd_out()
void attach_callback_fd_out(void (*callback)());
リーダが離脱した際のコールバック関数を登録します。
NFC 標準 EEPROM モード
標準のEEPROMモードを使うには、TWENET の初期化後に mwf::the_ntag->disable_pthru_mode()を呼び出してください。TWENETは初期化時に拡張SRAMモードを有効化します。act の場合は begin() {} を利用できます。
void begin() {
mwf::the_ntag->disable_pthru_mode();
}
write_ndef_record()
int write_ndef_record(const uint8_t* const ndef_record,
const int ndef_record_len, const bool lock_after = false);
任意の NDEF レコードを書き込みます。
write_uri()
int write_uri(const char* const uri, const URI_TYPE uri_type = URI_TYPE::HTTPS,
const bool lock_after = false);
標準の URI レコードを書き込みます。
URI_TYPE
NAなしHTTP_WWWhttp://wwwHTTPS_WWWhttps://wwwHTTPhttp://HTTPShttps://TELtel://MAILTOmailto://
write_text()
int write_text(const char* const text, const char* const lang_code = "en",
const bool lock_after = false);
標準のテキストレコードを書き込みます。
write_ascii()
int write_ascii(const char* const ascii, const int ascii_len,
const bool lock_after = false);
Type に A を指定した独自の NDEF 拡張形式として、アスキー文字列を書き込みます。
write_binary()
int write_binary(const uint8_t* const binary, const int binary_len,
const bool lock_after = false);
Type に B を指定した独自の NDEF 拡張形式として、バイナリデータを書き込みます。
get_field_detection_status()
uint8_t get_field_detection_status();
フィールド検出の状態 _FD_STATE を取得します。
available()
bool available();
メインループから呼び出します。NFC リーダが遠ざかった際に、新たな NDEF レコードを受信していたら true を返します。
read_is_ndef_record_new()
bool read_is_ndef_record_new();
新たな NDEF レコードを受信していたら true を返します。
read_ndef_record()
int read_ndef_record(uint8_t* const ndef_record, int* const ndef_record_len);
任意の NDEF レコードを読み出します。
read_ndef_record_type()
int read_ndef_record_type(char* const ndef_record_type);
任意の NDEF レコードの type を読み出します。
read_ndef_record_length()
int read_ndef_record_length(int* const ndef_record_len);
任意の NDEF レコードの長さを読み出します。
read_is_ndef_record_short()
int read_is_ndef_record_short(bool* const is_record_short);
NDEF レコードが short レコードである場合は true を返します。
read_uri()
int read_uri(char* const uri, URI_TYPE* const uri_type);
標準の URI レコードを読み出します。
read_text()
int read_text(char* const text, char* const lang_code);
標準のテキストレコードを読み出します。
read_ascii()
int read_ascii(char* const ascii);
Type に A を指定した独自の NDEF 拡張形式として、アスキー文字列を読み出します。
read_binary()
int read_binary(uint8_t* const binary);
Type に B を指定した独自の NDEF 拡張形式として、バイナリデータを読み出します。
NFC 拡張 SRAM モード
enable_pthru_mode()
void enable_pthru_mode();
SRAM モードを有効化します(既定は EEPROM モード)。
disable_pthru_mode()
void disable_pthru_mode();
SRAM モードを無効化します。
update_pthru()
void update_pthru();
メインループから呼び出し、SRAM モードの状態遷移を行います。
MWishCommand
Mono Wireless interactive shell command
拡張 SRAM モードで NFC リーダから送信されるコマンドデータを扱います。シングルトンのオブジェクトです。
定数
| 型 | 名称 | 説明 |
|---|---|---|
int | DATA_MAX_SIZE | 最大データ長 8192 バイト |
メンバ変数
| 型 | 名称 | 説明 |
|---|---|---|
const char | magic_number | マジックナンバー"ZAMA" |
uint8_t | major_version | プロトコルバージョン |
uint8_t | minor_version | プロトコルバージョン |
uint8_t | patch_version | プロトコルバージョン |
uint16_t | transaction_id | トランザクションID |
int | total_data_length | 全データ長 |
char | data_type | データ種別 |
uint8_t | lang_id | 言語ID |
uint16_t | pin | PIN |
char[8] | command_str | コマンド文字列 |
char[8] | flags | オプションフラグ |
uint8_t* const | data | 読み出しデータ領域 |
getInstance()
static MWishCommand& getInstance();
シングルトンのオブジェクトへの参照を返します。
get_data_type()
PTHRU_DATA_TYPE get_data_type() const;
データ種別を取得します。
PTHRU_DATA_TYPE
RAW_STRING文字列RAW_BYTESバイナリデータPROTOBUFProtocol Buffers
is_completed()
bool is_completed() const;
すべての(分割)フレームの格納が完了していれば true を返します。
is_japanese()
bool is_japanese() const;
格納したコマンドの言語IDが 2 であり、日本語であれば true を返します。
is_command()
bool is_command(const char* const str) const;
格納したコマンド文字列が引数と一致している場合は true を返します。
contains_flag()
bool contains_flag(const char flag) const;
引数に指定したフラグ(オプション)がコマンドに含まれている場合は true を返します。
parse_new()
bool parse_new(const uint8_t* const frame);
コマンドを初期化し、新たなフレームを解釈させます。
complete_with()
bool complete_with(const uint8_t* const another_frame);
追加のフレームを渡し、データを補完します。
MWishResponse
Mono Wireless interactive shell response
拡張 SRAM モードで NFC リーダへ送信する応答データを扱います。シングルトンのオブジェクトです。
定数
| 型 | 名称 | 説明 |
|---|---|---|
int | DATA_MAX_SIZE | 最大データ長 8192 バイト |
メンバ変数
| 型 | 名称 | 説明 |
|---|---|---|
const MWishCommand* | command | コマンド |
int | total_data_length | 全データ長 |
char | data_type | データ種別 |
uint8_t | lang_id | 言語ID |
uint16_t | pin | PIN |
int | frames_serialized | 書き出したフレーム数 |
bool | finished | 書き出し完了ならtrue |
int | restart_after | 応答後にリセットする際の待機時間 |
uint8_t* const | data | 書き出しデータ領域 |
getInstance()
static MWishResponse& getInstance();
シングルトンのオブジェクトへの参照を返します。
get_command()
const MWishCommand* const get_command() const;
応答を返す対象のコマンドへのポインタを取得します。
get_data_type()
PTHRU_DATA_TYPE get_data_type() const;
データ種別を取得します。
PTHRU_DATA_TYPE
A文字列BバイナリデータPProtocol Buffers
set_data_type()
void set_data_type(const PTHRU_DATA_TYPE type);
データ種別を設定します。
get_data_len()
void set_data_len(const int len);
データ長を設定します。
reset_data_len()
void reset_data_len();
データ長を初期化します。
get_data_ptr()
uint8_t* get_data_ptr() const;
データ列へのポインタを取得します。
get_data_ptr_as()
template<typename Target> Target get_data_ptr_as() const;
データ列へのポインタを指定した形式で取得します。
get_pin()
uint16_t get_pin() const;
PINを取得します。
set_pin()
void set_pin(const uint16_t code);
PINを設定します。
is_serialization_finished()
bool is_serialization_finished() const;
データの書き出しが終わった際は true を返します。
restart_after_response()
void restart_after_response(const int warm_start_dur = -1);
応答の直後に直接スリープ状態へ移行する、または応答の直後に決められた時間の間スリープさせます。
is_restart_required()
bool is_restart_required() const;
応答後にスリープする場合は true を返します。
is_cold_restart_required()
bool is_cold_restart_required() const;
応答直後にリセットする場合は true を返します。
get_warm_restart_duration()
int get_warm_restart_duration() const;
応答後にスリープする時間をミリ秒単位で返します。
printf()
void printf(const char* format, ...);
データ種別を文字列に設定し、指定された文字列を設定します。
prepare_for()
void prepare_for(const MWishCommand& command);
指定されたコマンドのために応答データを初期化します。
serialize()
bool serialize(uint8_t* const destination);
指定された領域に応答データを書き出します。
MWishCommandHandler
using MWishCommandHandler = bool (*)(const MWishCommand* const command_received,
MWishResponse* const response_to_send);
コマンドに対する応答の処理を登録するためのハンドラの定義です。
attach_pthru_command_handler()
void attach_pthru_command_handler(MWishCommandHandler handler,
bool is_system = false);
コマンドに対する応答の処理を登録するためのハンドラを登録します(is_system = true でシステム向け)。
sys_ev_handler 関連
on_sleep()
スリープ前に利用終了の手続きを行います。
on_wakeup()
スリープ時に初期化済みの場合は、再度初期化します。再初期化が不要な場合は deinit() してからスリープしてください。