6. NFP ライブラリの利用方法

NFP ライブラリをアプリケーションで利用する際には、以下の点に注意して実装してください。

  • NFP ライブラリは、SNAKE と CTR で挙動が変わる可能性があります。必ず SNAKE、CTR の両方で動作確認をお願いします。
  • SNAKE 上で NFP ライブラリは、必ず本体内蔵 NFC を使用するモードで動作します。これはデバッガによる強制 CTR 互換モード時も同様です。NFC リーダー/ライターを使用する場合は CTR をご用意ください。
  • NFP ライブラリはマルチスレッド対応されていません。同時に複数のスレッドから NFP ライブラリの関数を呼び出さないでください。
  • 関数の実行に成功した場合は基本的に nn::ResultSuccess が返されますが、実行の成否判定は必ず IsSuccess() または IsFailure() で行ってください。
  • nn::nfp::ResultInvalidUsage に属する返り値(nn::nfp::ResultInvalidArgumentnn::nfp::ResultInvalidPointernn::nfp::ResultBufferIsSmall)は実装エラーです。開発の段階でこれらのエラーが返されないように実装してください。
  • NFP ライブラリで提供されている関数には、完了までに長時間要するものが多くあります。特にライブラリの初期化は最大で 5 秒程度、NFP タグへの書き込みは 1 ~ 2 秒程度の時間が必要です。そのため NFP ライブラリの関数は、メインループとは別のスレッドから呼び出すことを推奨します。
  • タグを検知したあと、タグが通信可能範囲内から離れるなど、タグとの通信が行えない状態になるとタグ喪失通知がアプリケーションに通知されます。この時点で NFP ライブラリのステートは RW_DEACTIVE に遷移し、マウントされていたタグはアンマウントされます。
    この状態を把握せずに NFP ライブラリの関数を呼び出すと、ほとんどの関数は nn::nfp::ResultInvalidOperation を返します。アプリケーションはタグ喪失通知のハンドリングを必ず行い、検知していたタグにアクセスできる状態にあるかを NFP ライブラリの関数を呼び出す前に確認するようにしてください。
  • NFP ライブラリの関数を実行している間に本体がスリープ状態や無線オフモードに遷移した場合、その多くは返り値として nn::nfp::ResultSleep または nn::nfp::ResultWifiOff を返します。これらの返り値が返されると、NFP ライブラリのステートは INIT に戻り、タグの検知は停止され、マウントされていたタグもアンマウントされた状態になっています。つまり、nn::nfp::Unmount() および nn::nfp::StopDetection() が呼ばれたのと同じ状態になります。
    スリープ状態から復帰したり無線オンモードに遷移したりしても NFP ライブラリは自動的にもとの状態に戻すことはありませんので、アプリケーションは nn::nfp::StartDetection() でタグの検知から再開する必要があります。
  • HOMEメニューやスリープ状態に遷移する前に、アプリ専用領域への書き込みで nn::nfp::Flush() まで完了していない場合は、nn::nfp::Flush() による書き込みが完了するまで HOMEメニューやスリープ状態への遷移を遅らせても構いません。もしくは、NFP タグへの書き込みを行う間は HOMEボタン禁止アイコンを表示し、HOMEメニューへの遷移を行わないことで対処しても構いません。
  • HOMEメニュー、及びアプレットに遷移する前に nn::nfp::Finalize() を呼び出して、 NFP ライブラリのステートを NONE に遷移させてください。

 

NFP ライブラリの関数にはバックアップデータへのアクセスや NFP タグへのアクセスを行うものがあり、それらの関数を過度に呼び出すことはハードウェアや NFP タグの寿命を縮める原因となります。

そのため、ユーザーが意図的に操作を繰り返した場合を除いて、以下の関数を呼び出す頻度が過剰にならないように実装してください。

表 6-1. バックアップデータまたは NFP タグへのアクセスを行う関数
関数 バックアップデータからの読み込み バックアップデータへの書き込み NFP タグへの書き込み
nn::nfp::Initialize() 常時 バックアップ用のセーブデータが存在しない場合 なし
nn::nfp::Mount() 常時 初めてマウントするタグ、もしくは別の機器で書き換えられたタグをマウントした場合 なし
nn::nfp::Restore() 常時 なし 常時
nn::nfp::Flush() なし 常時 常時
nn::nfp::CreateApplicationArea() なし 常時 常時

6.1. 初期化

NFP ライブラリの初期化は nn::nfp::Initialize() を呼び出して行います。この関数の処理には通常数百ミリ秒程度、その本体で初めての実行では 5 秒程度の時間がかかるため、メインスレッド以外で呼び出すことを強く推奨します。また、アプリケーションの起動中ではなく、アプリケーションで画面更新が行えるようになってから呼び出すことを推奨します。

NFP ライブラリの関数のほとんどは初期化が完了する前に呼び出すと nn::nfp::ResultInvalidOperation を返します。初期化済みの状態で nn::nfp::Initialize() を呼び出した場合も nn::nfp::ResultInvalidOperation が返されますが、この場合については処理成功として扱うことができます。

NFP ライブラリの初期化に失敗したときに nn::nfp::ResultNeedRetry が返されることがあります。このエラーが返された場合は再度 nn::nfp::Initialize() を呼び出してください。なお、1 秒程度経過してから呼び出すことで初期化に成功する可能性が高くなります。

表 6-2. nn::nfp::Initialize() が返す可能性のある返り値
返り値 説明
nn::ResultSuccess 初期化に成功しました。
nn::nfp::ResultInvalidOperation すでに初期化済みです。
nn::nfp::ResultNeedRetry リトライしてください。
nn::nfp::ResultIrFunctionError IrDA モジュールが故障している可能性があります。

6.1.1. タグ検知に関する通知を受け取るイベントの設定

タグ検知を開始する前に、タグ検知に関連する NFP ライブラリからの通知をアプリケーションで受け取るイベントを設定する必要があります。

NFP ライブラリからアプリケーションへは、以下のタイミングでタグ検知に関連する通知が行われます。

  • NFC モジュールの通信可能範囲にタグを発見した(タグ発見通知)
  • 発見していたタグが通信可能範囲から離れた(タグ喪失通知)
  • タグを検知している状態で nn::nfp::StopDetection(), nn::nfp::Disconnect() または nn::nfp::Finalize() を呼び出した(タグ喪失通知)

タグ発見通知に対する設定は nn::nfp::SetActivateEvent() で行い、タグ喪失通知に対する設定は nn::nfp::SetDeactivateEvent() で行います。

それぞれの関数に渡す引数は nn::os::Event クラスのインスタンスへのポインタです。NFP ライブラリがイベントの管理を行いますので、渡すインスタンスの初期化や解放はアプリケーションで行う必要はありません。

これらの関数はタグ検知を開始する前(ステートが INIT である間)に呼び出す必要があります。

表 6-3. nn::nfp::SetActivateEvent() および nn::nfp::SetDeactivateEvent() が返す可能性のある返り値
返り値 説明
nn::ResultSuccess イベントの設定に成功しました。
nn::nfp::ResultInvalidPointer 引数で渡されたポインタが正しく指定されていません。
nn::nfp::ResultInvalidOperation ライブラリのステートがこの関数を呼び出すのにふさわしくありません。

6.1.1.1. タグ発見通知に対する処理

NFP ライブラリがタグを発見したことは、nn::nfp::SetActivateEvent() で設定したイベントがシグナル状態になることでアプリケーションに通知されます。

タグが発見された段階で、以下の処理が可能になります。

6.1.1.2. タグ喪失通知に対する処理

通信可能範囲から離れるなどによって、NFP ライブラリが発見していたタグを見失ったことは、nn::nfp::SetDeactivateEvent() で設定したイベントがシグナル状態になることでアプリケーションに通知されます。この通知を受け取った時点で NFP ライブラリのステートが RW_DEACTIVE に遷移しているため、タグの検知を継続する場合は nn::nfp::StartDetection() を再度呼び出す必要があります。

タグを見失った原因は nn::nfp::GetConnectionStatus() で取得することができるタグの接続状態から判断することができます。タグの接続状態は nn::nfp::ConnectionStatus 構造体で定義されており、nn::nfp::GetConnectionStatus() には構造体へのポインタを渡す必要があります。

nn::nfp::GetConnectionStatus() の処理に成功すると、引数で渡した構造体に現在のタグの接続状態が設定されます。接続状態の詳細(切断理由)は nn::nfp::DeactivateReason 列挙子で定義され、構造体の deactivateReason メンバに設定されています。

表 6-4. 接続状態の詳細(切断理由)
定義 接続状態 説明
BEFORE_ACTIVATION 未発見 まだタグを発見できていません。
TAG_IS_ACTIVE 接続中 タグへのアクセスが可能です。
DETECT_ERROR 接続失敗 正常なタグだと認識できていません。タグのデータが破損している可能性があります。
CONNECTION_LOST 切断 タグが通信可能範囲から離れた、通信状況が悪化したなどの理由により接続が維持できなくなりました。
UNKNOWN_DEACTIVATE_REASON 切断 ライブラリが認知できない理由で接続が切断されました。
表 6-5. nn::nfp::GetConnectionStatus() が返す可能性のある返り値
返り値 説明
nn::ResultSuccess 接続状態の取得に成功しました。
nn::nfp::ResultInvalidPointer 引数で渡された情報に不備があり、処理に失敗しました。このエラーは実装エラーですので、開発の段階で発生しないように対処してください。
nn::nfp::ResultInvalidOperation ライブラリのステートがこの関数を呼び出すのにふさわしくありません。

6.2. タグ検知の開始

タグ検知の開始は nn::nfp::StartDetection() で行います。

タグ検知を行っている間、NFP ライブラリはタグを発見したかどうかや発見していたタグと通信可能であるかどうかを定期的に判定します。タグ検知の状態が変化する要因が発生してから、タグ発見がアプリケーションに通知されるまでには、 SNAKE で最大 500 ミリ秒、 CTR で最大1秒程度のタイムラグがあります。タグ喪失がアプリケーションに通知されるまでには最大 2 秒程度のタイムラグがあります。そのため、タグがタッチされたり、タグが離されたりしたタイミングをシビアに判定するような実装を行わないでください。また、 SNAKE では下画面液晶の中央にタグが置かれることを前提としています。具体的には、液晶の中心から前後左右方向に ± 10mm 、上下方向に + 5mm が正確にタグを認識できる範囲となります。 CLOSER でも同様です。このため、液晶の中央以外にタグを置くように誘導する遊び方やユーザーインタフェースの設計は避けてください。

SNAKE ではタグの検知に使用する電磁波がノイズとなり、タッチパネルからの入力が不安定になってしまいます。この状態でタッチパネルの入力を使用するとアプリケー ションの誤動作に繋がるため、 SNAKE ではタグの検知中にタッチパネルを利用できないように制限が課されています。具体的には、 nn::nfp::StartDetection() を実行してから nn::nfp::StopDetection()nn::nfp::Finalize() でタグの検知が停止されるまでの間、 nn::hid::CTR::TouchPanelStatusstatus が常に 0 (ペンアップ)に設定されます。

CTR では NFC リーダー/ライターでタグを検知するため、 SNAKE と同じような制限は課されていません。ただし、 CTR でタッチパネルを使用する場合には SNAKE 向けに異なる UI を用意するなどして、 CTR と SNAKE の両方でアプリケーションが操作できることを確認してください。

NFP ライブラリではタグを発見した瞬間に消費電力や発熱が最大になります。 タグを置いた状態で nn::nfp::StartDetection()nn::nfp::StopDetection() を繰り返すと、タグの発見が短い期間で連続して発生することになり、 SNAKE では設計上許容される発熱量を超えてシステムが不安定になります。 CTR ではこの問題は発生しませんが、電力を消費することには変わりありません。そのため、 SNAKE  、CTR 共に、高頻度で nn::nfp::StartDetection() を実行できないように制限が課されています。具体的には、 10 秒という時間枠で nn::nfp::StartDetection() を任意のタイミングで 10 回実行できますが、 11 回以上は実行できないようになっています。これは、nn::nfp::StartDetection() の実行に際して、 10 回前の nn::nfp::StartDetection() の実行から 10 秒経過するまで、その nn::nfp::StartDetection() の実行をブロックすることで実現しています。既に 10 秒経過している場合や、過去の実行回数が 10 回未満の場合は、ブロックされません。

表 6-6. nn::nfp::StartDetection() が返す可能性のある返り値
返り値 説明
nn::ResultSuccess タグ検知を開始しました。
nn::nfp::ResultInvalidOperation ライブラリのステートがこの関数を呼び出すのにふさわしくありません。
nn::nfp::ResultSleep スリープ状態に遷移しているため、処理に失敗しました。
nn::nfp::ResultWifiOff 無線オフモードのため、処理に失敗しました。

6.3. タグ情報の取得

タグ情報の取得は nn::nfp::GetTagInfo() の呼び出しで行います。タグ情報は nn::nfp::TagInfo 構造体で定義されており、nn::nfp::GetTagInfo() には構造体へのポインタを渡す必要があります。

表 6-7. nn::nfp::GetTagInfo() が返す可能性のある返り値
返り値 説明
nn::ResultSuccess タグ情報の取得に成功しました。
nn::nfp::ResultInvalidPointer 引数で渡された情報に不備があり、処理に失敗しました。このエラーは実装エラーですので、開発の段階で発生しないように対処してください。
nn::nfp::ResultInvalidOperation ライブラリのステートがこの関数を呼び出すのにふさわしくありません。
nn::nfp::ResultSleep スリープ状態に遷移しているため、処理に失敗しました。
nn::nfp::ResultWifiOff 無線オフモードのため、処理に失敗しました。
補足:

NFP ライブラリでは、タグの UID が取得できるのはタグ情報からのみとなっています。NFP タグのアプリ専用領域へのアクセスを行うアプリケーションは、事前に nn::nfp::GetTagInfo() でタグ情報を取得しておくことを推奨します。

タグ情報は NFP ライブラリでアクセス可能な種類のタグであれば取得できる情報のみが記録されているため、発見されたタグが NFP タグであるかどうかまでは分かりません。

6.4. タグのマウント

NFP タグの情報にアクセスするには、まず nn::nfp::Mount() または nn::nfp::MountRom() を呼び出して、発見されたタグをマウントする必要があります。

ともに NFP タグでなければマウントが行われませんが、nn::nfp::MountRom() でマウントした場合はアクセス可能な情報に制限があり、タグのデータが壊れていてもマウントが可能な点が異なります。

以下のように、発見されたタグが NFP タグであるかどうかを返り値で判断することができます。

表 6-8. タグの状態と各関数の返り値
タグの状態 nn::nfp::Mount() nn::nfp::MountRom()
NFP タグではない nn::nfp::ResultNotSupported
NFP タグだが未知のフォーマットバージョン nn::nfp::ResultInvalidFormatVersion
NFP タグであり、タグのデータが正常 nn::ResultSuccess

NFP タグであり、タグのデータが壊れている

(タグのバックアップデータが存在しない)

nn::nfp::ResultNeedFormat nn::ResultSuccess

NFP タグであり、タグのデータが壊れている

(タグのバックアップデータが存在する)

nn::nfp::ResultNeedRestore nn::ResultSuccess

発見されたタグが NFP タグでない場合は nn::nfp::ResultNotSupported が返されます。

発見されたタグが NFP タグであっても、未知のフォーマットバージョンでありマウントできない場合には nn::nfp::ReultInvalidFormatVersion が返されます。

発見されたタグが NFP タグであり、タグのマウントに成功した場合は nn::ResultSuccess が返されます。

発見されたタグが NFP タグであり、タグのデータが壊れている場合は nn::nfp::ResultNeedRestore もしくは nn::nfp::ResultNeedFormat が返されます。前者の返り値に対しては nn::nfp::Restore() を呼び出してアプリケーションでタグのデータを修復することができます。後者の返り値に対してはアプリケーションでタグのデータを修復することができませんので、NFP タグの初期化を「amiibo設定」で行う必要があります。

返り値に nn::nfp::ResultTagNotFound が返された場合は NFP ライブラリのステートが RW_DEACTIVE に遷移しているため、タグの検知を継続する場合は nn::nfp::StartDetection() を再度呼び出す必要があります。

返り値に nn::nfp::ResultNeedRetry が返された場合は、再度 nn::nfp::Mount() を呼び出すことでマウントに成功する可能性があります。

補足:

タグのマウントには最大 500 ミリ秒程度必要です。

リトライする場合は 100 ミリ秒程度の間隔をあけて行うことを推奨します。

表 6-9. nn::nfp::Mount() が返す可能性のある返り値
返り値 説明
nn::ResultSuccess タグのマウントに成功しました。
nn::nfp::ResultInvalidOperation ライブラリのステートがこの関数を呼び出すのにふさわしくありません。
nn::nfp::ResultSleep スリープ状態に遷移しているため、処理に失敗しました。
nn::nfp::ResultWifiOff 無線オフモードのため、処理に失敗しました。
nn::nfp::ResultNotSupported NFP タグではありません。
nn::nfp::ResultNeedRestore タグのデータが壊れているため、nn::nfp::Restore() でタグのデータを修復してください。
nn::nfp::ResultNeedFormat タグのデータが壊れているため、「amiibo設定」で初期化する必要があります。
nn::nfp::ResultTagNotFound タグが見つかりませんでした。タグが通信可能範囲から離されたり、別のタグに変えられていたりすることが原因です。
nn::nfp::ResultNeedRetry マウント中にエラーが発生しました。再度実行することで処理に成功する可能性があります。
nn::nfp::ResultInvalidFormatVersion 対応していないバージョンのタグです。対応していないタグであることをユーザーに通知してください。
表 6-10. nn::nfp::MountRom() が返す可能性のある返り値
返り値 説明
nn::ResultSuccess タグのマウントに成功しました。
nn::nfp::ResultInvalidOperation ライブラリのステートがこの関数を呼び出すのにふさわしくありません。
nn::nfp::ResultSleep スリープ状態に遷移しているため、処理に失敗しました。
nn::nfp::ResultWifiOff 無線オフモードのため、処理に失敗しました。
nn::nfp::ResultNotSupported NFP タグではありません。
nn::nfp::ResultTagNotFound タグが見つかりませんでした。タグが通信可能範囲から離されたり、別のタグに変えられていたりすることが原因です。
nn::nfp::ResultInvalidFormatVersion 対応していないバージョンのタグです。対応していないタグであることをユーザーに通知してください。

6.4.1. タグの復旧

nn::nfp::Mount()nn::nfp::ResultNeedRestore を返した場合、nn::nfp::Restore() を呼び出して、アプリケーションで NFP タグの復旧を行うことができます。

返り値に nn::nfp::ResultNeedRetry が返された場合は再度 nn::nfp::Restore() を呼び出すことでタグの復旧に成功する可能性があります。

補足:

タグの復旧には数秒程度かかる可能性があります。

リトライする場合は 100 ミリ秒程度の間隔をあけて行うことを推奨します。

復旧されるデータは、タグのマウント時およびタグへの書き込みを行う直前に NFP ライブラリがバックアップしたデータです。そのため、NFP タグへの書き込みに失敗したあとに復旧した場合は、書き込み前のデータではなく、書き込もうとしたデータで復旧されることになります。

表 6-11. nn::nfp::Restore() が返す可能性のある返り値
返り値 説明
nn::ResultSuccess タグの復旧に成功しました。
nn::nfp::ResultInvalidOperation ライブラリのステートがこの関数を呼び出すのにふさわしくありません。
nn::nfp::ResultSleep スリープ状態に遷移しているため、処理に失敗しました。
nn::nfp::ResultWifiOff 無線オフモードのため、処理に失敗しました。
nn::nfp::ResultNotSupported NFP タグではありません。
nn::nfp::ResultNotBroken タグが壊れていないため、復旧する必要はありません。
nn::nfp::ResultTagNotFound タグが見つかりませんでした。タグが通信可能範囲から離されたり、別のタグに変えられていたりすることが原因です。 
nn::nfp::ResultOperationFailed 書き込み中にエラーが発生しました。タグの内容が正常に復旧できなかった可能性があります。
nn::nfp::ResultNeedRetry 書き込み処理の過程でエラーが発生しました。再実行してください。
nn::nfp::ResultConnectCanceled 内部エラーにより NFC リーダー/ライターとの接続が切断されました。
nn::nfp::ResultBackupError バックアップデータへのアクセスに失敗しました。この NFP タグは復旧できません。

6.5. 「amiibo設定」の起動

「amiibo設定」の起動には以下の手順が必要です。

  1. 「amiibo設定」の起動に必要な情報を設定する。
  2. NFP ライブラリの終了処理を行う。
  3. 「amiibo設定」を起動する。

最初に、「amiibo設定」の起動に必要な情報を nn::nfp::Parameter 構造体に設定します。

nn::nfp::InitializeParameter() で初期化してから、アプリケーションから「amiibo設定」を呼び出す際に必要な情報を input メンバ(nn::nfp::Input 構造体)に設定してください。

「amiibo設定」内でタグの検知を行う際にアプリケーションから指定されたタグであるかどうかを判定する必要があります。そのため、必ず tagInfo には nn::nfp::GetTagInfo() で事前に取得したタグ情報を設定してください。

そのほかの modeisRegisteredregisterInfocommonInfo の各メンバには、ユースケースごとに以下の値を設定してください。

表 6-12. 「amiibo設定」を起動する際のユースケースごとの設定値
ユースケース mode isRegistered registerInfo commonInfo
nn::nfp::Mount()nn::nfp::ResultNeedRestore を返した AMIIBO_SETTINGS_RESTORE false NULL NULL
nn::nfp::GetNfpRegisterInfo()nn::nfp::ResultNeedRegister を返した AMIIBO_SETTINGS_NICKNAME_OWNER false NULL nn::nfp::GetNfpCommonInfo() で取得した情報
nn::nfp::GetNfpRegisterInfo()nn::ResultSuccess を返したが、ニックネームが空文字列だった AMIIBO_SETTINGS_NICKNAME_OWNER true nn::nfp::GetNfpRegisterInfo() で取得した情報 nn::nfp::GetNfpCommonInfo() で取得した情報
nn::nfp::OpenApplicationArea()nn::nfp::ResultAccessIdMisMatch を返したので、アプリ専用領域を削除したい AMIIBO_SETTINGS_ERASE_GAMEDATA true nn::nfp::GetNfpRegisterInfo() で取得した情報 nn::nfp::GetNfpCommonInfo() で取得した情報

mode メンバに設定する値は nn::nfp::AmiiboSettingsMode 列挙子で定義されており、「amiibo設定」を起動する際のモードを指定するためのものです。

表 6-13. 「amiibo設定」の起動モード
定義 説明
AMIIBO_SETTINGS_RESTORE タグを復旧します。
AMIIBO_SETTINGS_NICKNAME_OWNER オーナー登録とニックネームの設定を行います。
AMIIBO_SETTINGS_ERASE_GAMEDATA アプリ専用領域の削除を行います。

「amiibo設定」の呼び出しは nn::nfp::StartAmiiboSettings() で行います。この関数を呼び出す前に nn::nfp::Finalize() で NFP ライブラリの終了処理を行う必要があります。そのため、「amiibo設定」から戻ったあとに NFP ライブラリを利用する場合は NFP ライブラリの初期化から再開しなければなりません。

nn::nfp::StartAmiiboSettings()false を返した場合は、「amiibo設定」の起動に失敗しています。エラーの原因としては、以下のものが考えられます。

  • 事前に NFP ライブラリの終了処理(nn::nfp::Finalize() の呼び出し)を行っていない。
  • 「amiibo設定」がインストールされていない(nn::nfp::IsAmiiboSettingsAvailable()false を返す)。
  • パラメータ設定が間違っている。
  • アプレットの起動に失敗した。

なお、Parameter.output.result には「amiibo設定」で NFP タグへの変更が行われたかどうかが格納されます。このメンバが nn::nfp::AMIIBO_SETTINGS_RESULT_SUCCESS ならば「amiibo設定」で NFP タグへの変更が行われたことを示し、それ以外ならばユーザーが処理をキャンセルするなどして NFP タグへの変更が行われていないことを示しています。

ほかのライブラリアプレットと同様に、nn::nfp::StartAmiiboSettings() から制御が戻されたときには、「amiibo設定」の動作中に電源ボタンが押下されるなどでアプリケーションの終了要求が発生していないかを nn::applet::IsExpectedToProcessPowerButton() および nn::applet::IsExpectedToCloseApplication() でチェックしてください。

6.6. NFP タグへのアクセス

タグのマウントが成功すると、NFP タグの以下の情報にアクセスすることができるようになります。

  • 共用領域
  • アプリ専用領域
注意:

アプリ専用領域は領域を作成したアプリケーションと同じアクセスID を持つアプリケーションでなければアクセスすることができません。

nn::nfp::MountRom() でマウントした場合は、共用領域の一部の情報にのみアクセスすることができます。

6.6.1. 共用領域へのアクセス

マウントされた NFP タグの情報をアプリケーションで使用する前に、nn::nfp::GetNfpCommonInfo() または nn::nfp::GetNfpRomInfo() で共用領域の情報を取得し、アプリケーションが対応している NFP タグかどうかを判断してください。

注意:

nn::nfp::MountRom() でマウントしていた場合、nn::nfp::GetNfpCommonInfo()nn::nfp::ResultInvalidOperation を返します。

nn::nfp::GetNfpCommonInfo() で取得する共用領域の情報は nn::nfp::CommonInfo 構造体で定義されており、関数には構造体へのポインタを渡す必要があります。

nn::nfp::GetNfpRomInfo() で取得する共用領域の情報は nn::nfp::RomInfo 構造体で定義されており、関数には構造体へのポインタを渡す必要があります。

アプリケーションが対応している NFP タグであるかどうかはキャラクターID(characterId)のみで判断してください。なお、対応するキャラクターID のリストはアプリケーションで用意する必要があります。

注意:

キャラクターID 以外の情報をアプリケーションで使用することは、現在禁止されています。

表 6-14. nn::nfp::GetNfpCommonInfo() が返す可能性のある返り値
返り値 説明
nn::ResultSuccess 情報の取得に成功しました。
nn::nfp::ResultInvalidPointer 引数で渡されたポインタが正しく指定されていません。
nn::nfp::ResultInvalidOperation ライブラリのステートがこの関数を呼び出すのにふさわしくありません。
nn::nfp::ResultSleep スリープ状態に遷移しているため、処理に失敗しました。
nn::nfp::ResultWifiOff 無線オフモードのため、処理に失敗しました。
表 6-15. nn::nfp::GetNfpRomInfo() が返す可能性のある返り値
返り値 説明
nn::ResultSuccess 情報の取得に成功しました。
nn::nfp::ResultInvalidPointer 引数で渡されたポインタが正しく指定されていません。
nn::nfp::ResultInvalidOperation ライブラリのステートがこの関数を呼び出すのにふさわしくありません。
nn::nfp::ResultSleep スリープ状態に遷移しているため、処理に失敗しました。
nn::nfp::ResultWifiOff 無線オフモードのため、処理に失敗しました。

共用領域には nn::nfp:GetNfpCommonInfo()nn::nfp::GetNfpRomInfo() で取得する情報のほかに、nn::nfp::GetNfpRegisterInfo() で取得する登録情報があります。

注意:

nn::nfp::MountRom() でマウントしていた場合、nn::nfp::GetNfpRegisterInfo()nn::nfp::ResultInvalidOperation を返します。

登録情報は nn::nfp::RegisterInfo 構造体で定義されており、nn::nfp::GetNfpRegisterInfo() には構造体へのポインタを渡す必要があります。

返り値に nn::nfp::ResultNeedRegister が返された場合、マウントされた NFP タグは「amiibo設定」によるオーナー登録が行われていません。登録情報を必須とするアプリケーションは「amiibo設定」を起動してください。

補足:

「amiibo設定」の起動方法については「6.5. 「amiibo設定」の起動」を参照してください。

取得した登録情報にはオーナーとして登録された Mii のデータ(miiData)、amiibo のニックネーム(nickName)、ニックネームの表示に必要なフォントのリージョン(fontRegion)が記録されています。

Mii のデータをアプリケーションで利用するには、似顔絵ライブラリが必要です。アプリケーションで Mii を表示する方法については似顔絵ライブラリのドキュメントを参照してください。

ニックネームは UTF-16 BE で格納されていますので、必要に応じてエンディアンの変換などを行ってください。なお、終端文字は NULL(0x0000)です。ニックネームをアプリケーションで表示する際には、フォントリージョンで指定された内蔵フォントで表示するか、アプリケーションで使用しているフォントにない文字を代替文字で表示するなどの対応を行い、画面表示が崩れたり、アプリケーションの進行に影響が出たりしないようにしてください。

オーナー登録がされていれば、Mii のデータは必ず設定されていますが、ニックネームは設定されていない(空文字列である)場合があります。ニックネームの登録を必須とするアプリケーションは nn::nfp::ResultNeedRegister と同じ手順で「amiibo設定」を起動してください。

補足:

ニックネームを表示するアプリケーションは、amiibo のニックネームとオーナーの Mii のニックネームの扱いに注意してください。特に、ニックネームがオーナーの Mii の名前であるかのような表示にならないようにしてください。また、「(オーナーの名前)のamiibo」や「(amiibo のキャラクター名)」、「amiibo」、「アミーボ」のように、ニックネームが設定されていない場合でも代替となる文字列が表示されるようにしてください。

注意:

国情報(country)をアプリケーションで使用することは、現在禁止されています。

表 6-16. nn::nfp::GetNfpRegisterInfo() が返す可能性のある返り値
返り値 説明
nn::ResultSuccess 情報の取得に成功しました。
nn::nfp::ResultInvalidPointer 引数で渡されたポインタが正しく指定されていません。
nn::nfp::ResultInvalidOperation ライブラリのステートがこの関数を呼び出すのにふさわしくありません。
nn::nfp::ResultSleep スリープ状態に遷移しているため、処理に失敗しました。
nn::nfp::ResultWifiOff 無線オフモードのため、処理に失敗しました。
nn::nfp::ResultNeedRegister 「amiibo設定」によるオーナー登録が行われていません。

6.6.2. アプリ専用領域へのアクセス

アプリ専用領域はアクセスID によってアクセス権限の制御が行われています。そのため、アプリ専用領域へのアクセスを開始するには、最初に nn::nfp::OpenApplicationArea() を呼び出して、アプリケーションにアクセス権限があるかを確認する必要があります。

返り値に nn::nfp::ResultAccessIdMisMatch が返された場合、アプリ専用領域は違うアクセスID によって作成されたものですので、アプリケーションにはアクセス権限がありません。この場合、読み込みおよび書き込みの両方が制限されます。アプリケーションからアプリ専用領域を削除する方法はなく、このアプリケーション用にアプリ専用領域を利用したい場合は「amiibo設定」で削除する必要があります。

返り値に nn::nfp::ResultNeedCreate が返された場合、マウントされた NFP タグにはアプリ専用領域がない状態ですので、アプリケーションでアプリ専用領域を作成してください。

表 6-17. nn::nfp::OpenApplicationArea() が返す可能性のある返り値
返り値 説明
nn::ResultSuccess 処理に成功しました。アプリ専用領域へのアクセスが可能です。
nn::nfp::ResultInvalidOperation ライブラリのステートがこの関数を呼び出すのにふさわしくありません。
nn::nfp::ResultSleep スリープ状態に遷移しているため、処理に失敗しました。
nn::nfp::ResultWifiOff 無線オフモードのため、処理に失敗しました。
nn::nfp::ResultAccessIdMisMatch ほかのアクセスID で作成されたアプリ専用領域のため、アクセスすることができません。
nn::nfp::ResultNeedCreate アプリ専用領域が作成されていません。

6.6.2.1. アプリ専用領域の作成

アプリ専用領域の作成は nn::nfp::CreateApplicationArea() を呼び出して行います。

この関数に渡す nn::nfp::ApplicationAreaCreateInfo 構造体は、nn::nfp::InitializeCreateInfo() で初期化してから構造体の各メンバに値を設定してください。

アプリ専用領域に書き込むデータを CAFE でも読み込む場合、CAFE と CTR/SNAKE ではエンディアンが違うため、どちらかに統一することを推奨します。

補足:

アプリ専用領域の作成には数秒程度かかる可能性があります。

リトライする場合は 100 ミリ秒程度の間隔をあけて行うことを推奨します。

関数内で nn::nfp::Flush() と同じ動作を行っているため、nn::nfp::CreateApplication() に続けて nn::nfp::Flush() を呼び出す必要はありません。

表 6-18. nn::nfp::CreateApplicationArea() が返す可能性のある返り値
返り値 説明
nn::ResultSuccess 処理に成功しました。アプリ専用領域は作成されました。
nn::nfp::ResultInvalidArgument 引数で渡された情報に不備があり、処理に失敗しました。このエラーは実装エラーですので、開発の段階で発生しないように対処してください。
nn::nfp::ResultInvalidPointer 引数で渡された情報に不備があり、処理に失敗しました。このエラーは実装エラーですので、開発の段階で発生しないように対処してください。
nn::nfp::ResultInvalidOperation ライブラリのステートがこの関数を呼び出すのにふさわしくありません。
nn::nfp::ResultSleep スリープ状態に遷移しているため、処理に失敗しました。
nn::nfp::ResultWifiOff 無線オフモードのため、処理に失敗しました。
nn::nfp::ResultNotSupported 対応していないタグです。別のタグに変えられている可能性があります。
nn::nfp::ResultAlreadyCreated すでにアプリ専用領域が作成されています。
nn::nfp::ResultTagNotFound タグが見つかりませんでした。タグが通信可能範囲から離されたり、別のタグに変えられていたりすることが原因です。
nn::nfp::ResultNeedRetry 書き込み中にエラーが発生しました。再度実行することで処理に成功する可能性があります。
nn::nfp::ResultOperationFailed 書き込み中にエラーが発生しました。タグの内容が破損した可能性があります。
nn::nfp::ResultConnectCanceled 内部エラーにより NFC リーダー/ライターとの接続が切断されました。

以下のサンプルコードではリトライ回数を 3 回までとしています。

コード 6-1. アプリ専用領域の作成(サンプルコード)
#define MY_ACCESS_ID 0x12345678
#define MY_APPLICATION_AREA_SIZE 128
nn::Result result;
bit8 writeData[MY_APPLICATION_AREA_SIZE];

// 最初に設定情報を初期化
nn::nfp::ApplicationAreaCreateInfo createInfo;
result = nn::nfp::InitializeCreateInfo(&createInfo);
if (result.IsFailure())
{
    // エラー
    return;
}

// 初期化データを用意
{
    memset(writeData, NULL, sizeof(writeData));
    // ビッグエンディアンでデータを書き込みます。
    writeData[0] = 'I';
    writeData[1] = 'N';
    writeData[2] = 'I';
    writeData[3] = 'T';
    writeData[4] = 0x00;
    writeData[5] = 0x00;
    writeData[6] = 0x00;
    writeData[7] = 0x01;
}
// 初期化に必要なデータを設定
createInfo.accessId = MY_ACCESS_ID;
createInfo.pInitialData = writeData;
createInfo.initialDataSize = sizeof(writeData);

// アプリ専用領域の作成
int retryCount = 0;
while (true)
{
    result = nn::nfp::CreateApplicationArea(createInfo);
    if (result.IsSuccess()) break;
    if (result.IsFailure())
    {
        if (nn::nfp::ResultNeedRetry::Includes(result))
        {
            // リトライは3回まで
            retryCount++;
            if (retryCount < 3)
            {
                // 0.1秒まってからリトライ
                nn::os::Thread::Sleep(100);
                continue;
            }
            // 処理失敗。もう一度タグの検知からやり直すなどの対処が必要
        }
        else if (nn::nfp::ResultAlreadyCreated::Includes(result))
        {
            // すでにアプリ専用領域が作成済み
            // データの整合性を確認して問題がなければ成功
        }
        else if (nn::nfp::ResultNotSupported::Includes(result))
        {
            // タグが差し替えられた可能性あり
        }
        else if (nn::nfp::ResultOperationFailed::Includes(result))
        {
            // タグが破損した可能性あり
        }
        else if (nn::nfp::ResultTagNotFound::Includes(result))
        {
            // タグが離された可能性あり
        }
        else if (nn::nfp::ResultInvalidOperation::Includes(result))
        {
            // 呼び出せないステートに遷移している
        }
        else if (nn::nfp::ResultWifiOff::Includes(result))
        {
            // 無線オフモードのため失敗
        }
        else if (nn::nfp::ResultSleep::Includes(result))
        {
            // スリープモードのため失敗
        }
        else if (nn::nfp::ResultConnectCanceled::Includes(result))
        {
            // NFC リーダー/ライターとの接続が切断したため失敗
        }
        else
        {
            // そのほかのエラー
        }
        return;
    }
}
// nn::nfp::Flush() を呼び出す必要はありません

6.6.2.2. アプリ専用領域の読み込み

アプリ専用領域に書き込まれているデータを読み込むには nn::nfp::ReadApplicationArea() を呼び出します。

アプリ専用領域の先頭から指定されたバイト数の内容をバッファに読み込むことができます。アプリ専用領域から読み込み可能なデータの最大サイズは nn::nfp::GetCommonInfo() で取得した CommonInfo 構造体の applicationAreaSize メンバから取得することができます。

表 6-19. nn::nfp::ReadApplicationArea() が返す可能性のある返り値
返り値 説明
nn::ResultSuccess 処理に成功しました。
nn::nfp::ResultInvalidArgument 引数で渡された情報に不備があり、処理に失敗しました。このエラーは実装エラーですので、開発の段階で発生しないように対処してください。
nn::nfp::ResultInvalidPointer 引数で渡された情報に不備があり、処理に失敗しました。このエラーは実装エラーですので、開発の段階で発生しないように対処してください。
nn::nfp::ResultInvalidOperation ライブラリのステートがこの関数を呼び出すのにふさわしくありません。
nn::nfp::ResultSleep スリープ状態に遷移しているため、処理に失敗しました。
nn::nfp::ResultWifiOff 無線オフモードのため、処理に失敗しました。

6.6.2.3. アプリ専用領域への書き込み

アプリ専用領域への書き込みは nn::nfp::WriteApplicationArea()nn::nfp::Flush() を呼び出して行います。

nn::nfp::WriteApplicationArea() の実行には、書き込み対象の NFP タグの UID が必要です。事前に nn::nfp::GetTagInfo() で取得した TagInfo 構造体の tagId メンバを引数に渡してください。アプリ専用領域に書き込むデータは、NFP ライブラリがタグに書き込む情報に合わせて、ビッグエンディアンにすることを推奨します。

nn::nfp::WriteApplicationArea() の実行が完了した時点では、まだ NFP タグのアプリ専用領域への書き込みは行われず、NFP ライブラリ内でキャッシュされている状態になっています。NFP タグのアプリ専用領域への書き込みは nn::nfp::Flush() で行われます。

補足:

NFP タグのアプリ専用領域への書き込みには数秒程度かかる可能性があります。

リトライする場合は 100 ミリ秒程度の間隔をあけて行うことを推奨します。

表 6-20. nn::nfp::WriteApplicationArea() が返す可能性のある返り値
返り値 説明
nn::ResultSuccess 処理に成功しました。
nn::nfp::ResultInvalidArgument 引数で渡された情報に不備があり、処理に失敗しました。このエラーは実装エラーですので、開発の段階で発生しないように対処してください。
nn::nfp::ResultInvalidPointer 引数で渡された情報に不備があり、処理に失敗しました。このエラーは実装エラーですので、開発の段階で発生しないように対処してください。
nn::nfp::ResultInvalidOperation ライブラリのステートがこの関数を呼び出すのにふさわしくありません。
nn::nfp::ResultSleep スリープ状態に遷移しているため、処理に失敗しました。
nn::nfp::ResultWifiOff 無線オフモードのため、処理に失敗しました。
nn::nfp::ResultUidMisMatch 指定された UID とタグの UID が異なります。
表 6-21. nn::nfp::Flush() が返す可能性のある返り値
返り値 説明
nn::ResultSuccess 処理に成功しました。
nn::nfp::ResultInvalidOperation ライブラリのステートがこの関数を呼び出すのにふさわしくありません。
nn::nfp::ResultSleep スリープ状態に遷移しているため、処理に失敗しました。
nn::nfp::ResultWifiOff 無線オフモードのため、処理に失敗しました。
nn::nfp::ResultNotSupported 対応していないタグです。別のタグに変えられている可能性があります。
nn::nfp::ResultTagNotFound タグが見つかりませんでした。タグが通信可能範囲から離されたり、別のタグに変えられていたりすることが原因です。
nn::nfp::ResultOperationFailed 書き込み中にエラーが発生しました。タグの内容が破損した可能性があります。
nn::nfp::ResultNeedRetry 書き込み中にエラーが発生しました。再度実行することで処理に成功する可能性があります。
nn::nfp::ResultConnectCanceled 内部エラーにより NFC リーダー/ライターとの接続が切断されました。

6.7. タグのマウント解除

タグのマウント解除は nn::nfp::Unmount() で行います。

表 6-22. nn::nfp::Unmount() が返す可能性のある返り値
返り値 説明
nn::ResultSuccess 処理に成功しました。
nn::nfp::ResultInvalidOperation ライブラリのステートがこの関数を呼び出すのにふさわしくありません。
nn::nfp::ResultSleep スリープ状態に遷移しているため、処理に失敗しました。
nn::nfp::ResultWifiOff 無線オフモードのため、処理に失敗しました。

6.8. タグ検知の終了

タグ検知の終了は nn::nfp::StopDetection() で行います。

タグがマウントされている状態で呼び出された場合、NFP ライブラリ内でマウントの解除が行われます。

なお、タグが検知された状態で nn::nfp::StopDetection() を呼び出した場合は、タグ喪失通知がアプリケーションに通知されます。

表 6-23. nn::nfp::StopDetection() が返す可能性のある返り値
返り値 説明
nn::ResultSuccess 処理に成功しました。
nn::nfp::ResultInvalidOperation ライブラリのステートがこの関数を呼び出すのにふさわしくありません。
nn::nfp::ResultSleep スリープ状態に遷移しているため、処理に失敗しました。
nn::nfp::ResultWifiOff 無線オフモードのため、処理に失敗しました。
nn::nfp::ResultConnectCanceled 内部エラーにより NFC リーダー/ライターとの接続が切断されました。
nn::nfp::ResultIrFunctionError IrDA モジュールが故障している可能性があります。

6.9. 終了

NFP ライブラリの終了処理は nn::nfp::Finalize() を呼び出して行います。関数内でタグのマウント解除やタグ検知の終了が行われ、タグを検知していた場合はタグ喪失通知の通知も行われます。

表 6-24. nn::nfp::Finalize() が返す可能性のある返り値
返り値 説明
nn::ResultSuccess 処理に成功しました。
nn::nfp::ResultInvalidOperation すでに終了済みです。

6.10. 3DS のアプリケーションで利用する場合

3DS のアプリケーションで NFP ライブラリを利用する場合、HOMEボタン、スリープ、電源ボタンといった 3DS の機能に対応した実装が必要になります。

CTR 向けの NFC リーダー/ライターは、CTR 本体の赤外線通信を利用しています。そのため、機器の接続および切断、機器からの通知のハンドリングを行う必要があります。

SNAKE, CTR 共に影響がある問題として、システムに負荷がかかっている場合、NFP ライブラリの動作に問題が発生することがあります。

6.10.1. HOMEボタンへの対処

HOME ボタンが押されたことを検知した場合は、 HOME メニューへ遷移する前に nn::nfp::Finalize() を呼び出して、 NFP ライブラリのステートを NONE に遷移させてください。アプレットを起動する前にも同じ操作が必要です。この操作を忘れた場合、 Release ビルドではシステムが自動的に NFP ライブラリを NONE 状態に遷移させますが、 Debug ビルドや Development ビルドではアサートが発生してプログラムの動作が停止します。

アプリ専用領域にデータを書き込んでいる最中など、 NFP タグのデータを保護する必要がある場合は HOME ボタン禁止アイコンを表示して HOME メニューへの遷移を行わなくてもかまいません。通常、データの書き込みは 1 ~ 2 秒で終了します。

コード 6-2. HOMEボタンへの対処のサンプルコード
// HOME メニューへの遷移をチェック
if (nn::applet::IsExpectedToProcessHomeButton())
{
    // NFP タグへの書き込みを行っているならば HOME メニューに遷移しない
    if (_isNfpTagWritingInProgress())
    {
        // HOME ボタン禁止アイコンを表示する
        _displyHomeNixSign();
        nn::applet::ClearHomeButtonState();
    }
    else
    {
        // NFP タグへのアクセスを停止する
        // HOME メニューに遷移する前に nn::nfp::Finalize() で
        // ステートを NONE に遷移させる必要があります
        _stopNfpTagAccess();
        _finalizeNfpTagAccess();
        // HOME メニュー遷移前の処理
        _prepareHomeMenu();
        // HOME メニューに遷移する
        nn::applet::ProcessHomeButtonAndWait();
        if (nn::applet::IsExpectedToCloseApplication())
        {
            // アプリケーションを終了させる
            _closeApplication();
        }
        // HOME メニューからの復帰処理
        _returnFromHomeMenu();
        // すぐにタグの検知を再開するならばここで行う(nn::nfp::StartDetection()を呼び出す)
        if (_needRestartNfpTagAccess()) _startNfpTagAccess();
    }
}

6.10.2. スリープ要求への対処

NFP ライブラリはスリープ状態に遷移したときにステートを INIT にまで自動的に遷移させますが、アプリ専用領域にデータを書き込んでいる最中など、 NFP タグのデータを保護する必要がある場合はアプリケーションでハンドリングしてスリープの承諾を保留することを推奨します。

コード 6-3. スリープ要求への対処のサンプルコード
// スリープ要求への対処
// スリープ問い合わせコールバック内で返答を保留していることを前提とする
if (nn::applet::IsExpectedToReplySleepQuery())
{
    // NFPタグへの書き込みを行っているかをチェック
    if (_isNfpTagWritingInProgress())
    {
        // NFPタグへの書き込みを行っているならばまだスリープしない
    }
    else
    {
        // スリープ前の処理
        _prepareSleep();
        // スリープへの遷移を承諾する
        nn::applet::ReplySleepQuery(nn::applet::REPLY_ACCEPT);
        // スリープ復帰コールバックなどでイベントをシグナル状態にするのを待つ
        _waitAwakeEvent();
        // スリープからの復帰処理
        _returnFromSleep();
        // すぐにタグの検知を再開するならばここで行う(nn::nfp::StartDetection()を呼び出す)
        if (_needRestartNfpTagAccess()) _startNfpTagAccess();
    }
}

6.10.3. 電源ボタンへの対処

電源ボタンが押された場合、アプリケーションは速やかに電源メニューを表示しなければなりません。電源メニューへ遷移する前に nn::nfp::Finalize() を呼び出して、 NFP ライブラリのステートを NONE に遷移させてください。この操作を忘れた場合、 Release ビルドではシステムが自動的に NFP ライブラリを NONE 状態に遷移させますが、 Debug ビルドや Development ビルドではアサートが発生してプログラムの動作が停止します。

ただし、アプリ専用領域にデータを書き込んでいる最中など、 NFP タグのデータを保護する必要がある場合は書き込みが完了するのを待ってから電源メニューを表示することができます。通常、データの書き込みは 1 ~ 2 秒で終了します。

コード 6-4. 電源ボタンへの対処のサンプルコード
// 電源ボタンへの対処
if (nn::applet::IsExpectedToProcessPowerButton())
{
    // NFP タグへの書き込みを行っているかをチェック
    if (_isNfpTagWritingInProgress())
    {
        // NFP タグへの書き込みが終了するまでは電源ボタンをハンドリングしない
    }
    else
    {
        // NFP タグへのアクセスを停止する
        // 電源メニューに遷移する前に nn::nfp::Finalize() で
        // ステートを NONE に遷移させる必要があります
        _stopNfpTagAccess();
        _finalizeNfpTagAccess();
        // 電源メニューへの遷移前に必要な処理を行う
        _preparePowerButtonMenu();
        // 電源メニューを表示する
        nn::applet::ProcessPowerButtonAndWait();
        if (nn::applet::IsExpectedCloseApplication())
        {
            // アプリケーションの終了処理
            _closeApplication();
        }
        else
        {
            // 電源メニューからの復帰
            _returnFromPowerButtonMenu();
        }
    }
}

6.10.4. CTR 対応に必要な実装

CTR 向けの NFC リーダー/ライターを利用する場合は、アプリケーションから明示的に機器への接続を行わなければなりません。また、タグの発見と喪失の通知のほかに、機器の接続と切断の状態をハンドリングする必要があります。

NFC リーダー/ライターの利用に関連する関数は以下の 4 つです。

  • nn::nfp::Connect()
  • nn::nfp::GetTargetConnectionStatus()
  • nn::nfp::GetConnectResult()
  • nn::nfp::Disconnect()

nn::nfp::Connect() は NFP ライブラリに対して機器への接続を要求します。この関数は非同期関数となっており、接続の成功、失敗は nn::nfp::GetTargetConnectionStatus() で取得した値が nn::nfp::TARGET_CONNECTED もしくは nn::nfp::TARGET_DISCONNECTED になっているかで判断します。

NFC リーダー/ライターと CTR が正しく向き合っている場合、接続が完了するまでには数百ミリ秒程度の時間がかかります。ただし、NFC リーダー/ライターの電源を入れた直後に接続を試みると、NFC リーダー/ライダー内部の初期化処理のために、接続が完了するまで 1 秒程度かかることがあります。NFC リーダー/ライターと CTR が向き合っていない場合は、タイムアウトまで接続を試みるため、 1 秒後に nn::nfp::TARGET_DISCONNECTED 状態になります。

なお、SNAKE で nn::nfp::GetTargetConnectionStatus() を呼び出した場合は何も処理をせず、即座に nn::nfp::TARGET_CONNECTED が取得されます。

表 6-25. nn::nfp::GetTargetConnectionStatus() で取得できる値
取得した値 NFC リーダー/ライター との接続状態
nn::nfp::TARGET_DISCONNECTED 切断
nn::nfp::TARGET_CONNECTING 接続中
nn::nfp::TARGET_CONNECTED 接続
補足:

ユーザーが CTR を持って遊んでいる場合等、NFC リーダー/ライターとの向きが合わなくなると、比較的短時間で切断されることになります。
ユーザーに切断を意識させたくない場合、アプリケーション内部で常に接続状態を維持するように、 nn::nfp::TARGET_DISCONNECTED 状態であれば nn::nfp::Connect() を自動で呼び出すような作りにすることも可能です。

nn::nfp::GetConnectResult() は、接続が失敗した場合に、失敗原因を取得するために呼び出します。呼び出しは nn::nfp::GetTargetConnectionStatus() で取得した値が nn::nfp::TARGET_DISCONNECTED になった後に実行してください。

なお、SNAKE で nn::nfp::GetConnectResult() を呼び出した場合は何も処理をせず、即座に nn::ResultSuccess が取得されます。

表 6-26. nn::nfp::GetConnectResult() で取得できる失敗要因
取得した失敗要因 説明
nn::ResultSuccess 接続に成功、もしくは Connect() の呼び出しに関係のない切断が発生しました。
nn::nfp::ResultConnectCanceled 接続処理がキャンセル、もしくは内部エラーにより失敗しました。
nn::nfp::ResultTimeOutError 接続が成功せずタイムアウトしました。
nn::nfp::ResultUpdateRequired NFC リーダー/ライターのアップデートが必要です。
nn::nfp::ResultIrFunctionError IrDA モジュールが故障している可能性があります。
nn::nfp::ResultNfcTargetError NFC リーダー/ライターが故障している可能性があります。
補足:

NFC リーダー/ライターと接続が失敗した場合のエラーハンドリングについては 5.11.4. NFC リーダー/ライター接続シーケンス を参照してください。

nn::nfp::Disconnect() はアプリケーションから明示的に機器への接続を切断する場合に呼び出します。処理の完了までには数十ミリ秒程度の時間がかかります。nn::nfp::StartDetection() でタグの検知中であれば、内部で nn::nfp::StopDetection() を行って切断と同時にタグ検知を停止するので、処理の完了までの時間が合計 100 ミリ秒程度かかるようになります。

NFC リーダー/ライターから切断された状態で呼び出すと nn::nfp::ResultInvalidOperation を返しますので、 nn::nfp::GetTargetConnectionStatus() で、 NFC リーダー/ライターとの接続状況を正確に把握しておくことを推奨します。

なお、SNAKE で nn::nfp::Disconnect() を呼び出した場合は、内部で nn::nfp::StopDetection() のみを行い、タグの検知を停止します。

補足:

NFC リーダー/ライターの電池残量チェックは、nn::nfp::Connect() で CTR と接続した直後に、一度だけ行いますが、以降は接続が切断されるまで行いません。
その為、接続状態を保持したまま数分間連続でタグの検出、書き込みを続けると、電池残量警告である赤色LED点灯無しに、いきなり NFC リーダー/ライターの電源断が発生する可能性があります。

連続でタグの検出、書き込みをするような使い方をする場合、定期的(1分毎等)に nn::nfp::Disconnect, nn::nfp::Connect を繰り返して使用することを推奨します。

また、使用している電池の種類によっては、上記電源断の発生後に電源を入れなおすと、LEDが青色に点灯することがありますが、こちらは仕様となります。
そのまま NFC リーダー/ライターを使用した場合、再度電源断が発生する前に LED が赤色になります。 

以下はタグ検知の開始と停止のサンプルコードです。

コード 6-5. タグ検知開始および停止のサンプルコード
bool _willTerminate = false;
bool _connected = false;
nn::Result _lastResult = nn::ResultSuccess();
nn::nfp::TargetConnectionStatus _targetConnectionStatus;

// タグ発見通知イベント、nn::nfp::SetActivateEvent() で設定
nn::os::Event _eventActivate;

// タグ喪失通知イベント、nn::nfp::SetDeactivateEvent() で設定
nn::os::Event _eventDeactivate;

// タグ検知の開始
nn::Result _startDetection()
{
    // CTR独自の実装
    if (!nn::os::IsRunOnSnake())
    {
        // 外付けNFCの接続
        if (!_connected)
        {
            _lastResult = nn::nfp::Connect();
            if (_lastResult.IsSuccess())
            {
                // 接続処理完了を待ち受ける
                while (1)
                {
                    nn::os::Thread::Sleep(nn::fnd::TimeSpan::FromMilliSeconds(500));
                    nn::nfp::GetTargetConnectionStatus(&_targetConnectionStatus);
                    if (_targetConnectionStatus == nn::nfp::TARGET_CONNECTED)
                    {
                        // 接続に成功
                        _connected = true;
                        break;
                    }
                    else if (_targetConnectionStatus == nn::nfp::TARGET_DISCONNECTED)
                    {
                        // 接続に失敗、もしくは接続タイムアウト
                        _connected = false;
                        nn::nfp::GetConnectResult(&_lastResult);
                        return _lastResult;
                    }
                }
            }
            else
            {
                if (nn::nfp::ResultInvalidOperation::Includes(_lastResult))
                {
                    // ステートがおかしい
                }
                else
                {
                    // そのほかのエラーはPANICで停止させる
                    NN_PANIC_WITH_RESULT(_lastResult);
                }
                return _lastResult;
            }
        }
    }

    // 検知開始
    _lastResult = nn::nfp::StartDetection();
    if (_lastResult.IsSuccess())
    {
        // 処理成功
        // タグでタッチするように促す
    }
    else if (nn::nfp::ResultWifiOff::Includes(_lastResult))
    {
        // 無線オフモード
    }
    else if (nn::nfp::ResultSleep::Includes(_lastResult))
    {
        // スリープ
    }
    else if (nn::nfp::ResultInvalidOperation::Includes(_lastResult))
    {
        // ステートがおかしい
    }
    else
    {
        // そのほかのエラーはPANICで停止させる
        NN_PANIC_WITH_RESULT(_lastResult);
    }
    return _lastResult;
}

// タグ検知の停止
nn::Result _stopDetection(bool forceDisconnect = false)
{
    // 検知停止
    _lastResult = nn::nfp::StopDetection();
    if (_lastResult.IsSuccess())
    {
        // 処理成功
    }
    else if (nn::nfp::ResultWifiOff::Includes(_lastResult))
    {
        // 無線オフモード
    }
    else if (nn::nfp::ResultSleep::Includes(_lastResult))
    {
        // スリープ
    }
    else if (nn::nfp::ResultInvalidOperation::Includes(_lastResult))
    {
        // ステートがおかしい
    }
    else
    {
        // そのほかのエラーはPANICで停止させる
        NN_PANIC_WITH_RESULT(_lastResult);
    }

    // CTR独自の実装
    if (!nn::os::IsRunOnSnake())
    {
        // 外付けNFCの切断
        if (forceDisconnect && _connected)
        {
            _connected = false;
            _lastResult = nn::nfp::Disconnect();
            if (_lastResult.IsSuccess())
            {
                // 処理成功
            }
            else if (nn::nfp::ResultInvalidOperation::Includes(_lastResult))
            {
                // ステートがおかしい
            }
            else
            {
                // そのほかのエラーはPANICで停止させる
                NN_PANIC_WITH_RESULT(_lastResult);
            }
        }
    }    
    return _lastResult;
}

// NFPライブラリからの通知を待ち受けるスレッド関数
void _procTagDetection()
{
    const int waitMilliSeconds = 500;
    nn::os::WaitObject* events[2] = {
        &_eventActivate, &_eventDeactivate };
    while (!_willTerminate)
    {
        const int eventIndex = nn::os::WaitObject::WaitAny(
            events, 2, nn::fnd::TimeSpan::FromMilliSeconds(waitMilliSeconds));
        if (eventIndex == 0)
        {
            _activate();
        }
        else if (eventIndex == 1)
        {
            _deactivate();
        }
        else
        {
            nn::nfp::TargetConnectionStatus currentTargetConnectionStatus;
            nn::nfp::GetTargetConnectionStatus(&currentTargetConnectionStatus);
            if( _targetConnectionStatus != currentTargetConnectionStatus )
            {
                if (currentTargetConnectionStatus == nn::nfp::TARGET_CONNECTED)
                {
                    _connected = true;
                }
                else if (currentTargetConnectionStatus == nn::nfp::TARGET_DISCONNECTED)
                {
                    _connected = false;
                }
                _targetConnectionStatus = currentTargetConnectionStatus;
            }
        }
    }
}

 

 

6.10.5. システムに負荷がかかっている場合の NFP ライブラリの動作

システムに高い負荷が加わっている場合、 NFP ライブラリの使用時に以下のような問題が発生します。

  • NFP タグの読み書きが極端に遅くなる
  • NFP タグの書き込みに失敗する可能性が高くなる
  • NFC リーダー/ライターから切断される
    • NFC リーダー/ライターから切断される現象については、タグが検出されている状態で発生しやすくなっています。NFP ライブラリのステートとしては RW_ACTIVE, RW_MOUNT, RW_MOUNT_ROM の場合にあたります。

そのため、アプリケーション開発時には、以下の点に注意して頂く必要があります。

1. 以下に挙げるデバイスを不必要に使用しないようにする

  • カメラ、YUV/RGB 変換、マイク、加速度、ジャイロ、無線、DSP、デバッグパッド(※1)
  • 特に、以下のデバイスの使用はシステムコアへの負荷が大きくなりますので、ご注意ください。
    • マイク(高サンプリングレートでの使用)、加速度、無線、デバッグパッド

2. システムコアのアプリへの割り当て時間を不必要に大きくしない
システムコアのアプリへの割り当て時間が減らすことで、 1. で挙げたデバイスを使用することによって発生する障害を緩和できる可能性があります。システムコアのアプリでの利用については 「3DS パフォーマンス TIPS」の「システムコアで行われる処理」を参照してください。

3. グラフィックスの描画命令(3D コマンド、コマンドリクエスト)を発行する数を減らす
描画命令の発行数以外にも「3DS パフォーマンス TIPS」の「グラフィックス処理」の項目に書かれている内容を適用することで、システムの負荷を下げることができます。

また、上記とは反対に NFP ライブラリを利用することによって発生するシステムの負荷増大によって、グラフィックスの処理落ちやサウンドノイズが発生することがあります。この現象についても、上記で挙げたデバイスの使用を止めることで改善することがあります。

(※1)デバッグパッドはアプリケーションで使用していなくとも、デバッガに接続しているだけで負荷が発生しますので注意してください。

 

参考までに、サンプルデモの demo1 をベースに、NFP ライブラリを使用する機能と、ローカル通信、ジャイロセンサ、DSP を使用する機能を追加したサンプルプログラムを使用して、CTR で NFC リーダー/ライターを使用した場合について、NFC リーダー/ライターから切断された頻度について記載します。

このデモでの NFP ライブラリの使用方法は、タグを検出した後 RW_ACTIVE と RW_MOUNT 状態を行き来するだけの使い方になります。そのため、常時 NFC リーダー/ライターからの切断が発生しやすい状態になっていることになります。

有効化したデバイス(使用している設定) 無効化したデバイス 切断頻度(1時間あたり)
DSP(aac)、ローカル通信(2台通信 1Mbps) カメラ、YUV/RGB 変換、マイク、加速度、ジャイロ、デバッグパッド 0.25回
マイク(SAMPLING_RATE_32730)、DSP(aacdec)、ローカル通信(2台通信 1Mbps) カメラ、 YUV/RGB 変換、加速度、ジャイロ、デバッグパッド 1回
カメラ(FRAME_RATE_30)、DSP(aacdec)、加速度、ジャイロ、ローカル通信(2台通信 1Mbps) マイク、デバッグパッド 0回

マイク(SAMPLING_RATE_32730)、カメラ(FRAME_RATE_30)、DSP(aacdec)、加速度、ジャイロ

ローカル通信、デバッグパッド 1.5回

マイク(SAMPLING_RATE_8180)、カメラ(FRAME_RATE_30)、DSP(aacdec)、加速度、ジャイロ

ローカル通信、デバッグパッド 0回

demo1 は一般的なアプリケーションと比較してシステムコアに与える負荷が小さいため、実際のアプリケーション開発では上記よりも切断頻度が上がる可能性が高いことに注意してください。