この章では、本体に搭載されている赤外線通信モジュールをアプリケーションで利用するために用意されている、IR ライブラリによる本体間通信について説明します。
CTR-SDK には、赤外線通信による本体間通信の情報を確認する開発用ツール(IrCommunicatorChecker)が付属しています。
SNAKE の拡張アプリで赤外線通信による本体間通信を行うアプリケーションは CTR - SNAKE 間の通信テストを行い、処理速度の差による通信障害が発生しないことを必ず確認してください。
アプリケーションから本体間赤外線通信を行う際、赤外線通信を利用する他の機能を使用中ならば、その機能を先に終了させてください。
赤外線通信は以下の機能で利用されています。
- 拡張スライドパッド
- NFP ( CTR のみ)
14.1. nn::ir::Communicator クラス
赤外線通信モジュールを利用した本体間通信を行う関数は、すべて nn::ir::Communicator
クラスの static 関数として定義されています。
nn::ir::Communicator
クラスでは 1 対 1 での本体間通信のみをサポートしています。3DS 以外の機器との通信や複数の本体との通信に使用することはできません。
14.1.1. セキュリティ
送受信されるデータの暗号化と完全性検証はライブラリ内で行われます。また、送信のたびにインクリメントされるシーケンス番号を送信パケットに付与することで再送攻撃を防いでいます。
14.1.2. パケット
IR ライブラリが送受信するパケットには、通信制御用の IR ライブラリヘッダとセキュリティ情報などが含まれる本体間通信ヘッダをユーザーデータに付加したものが使用されます。
ヘッダ情報が付加されたときのユーザーデータのサイズは以下の関数で取得することができます。
size_t nn::ir::Communicator::GetPacketSize(size_t dataSize); size_t nn::ir::Communicator::CalculateBufferSizeToCommunicate(size_t dataSize);
dataSize
にはユーザーデータのサイズを指定します。
GetPacketSize()
はパケット保存領域のサイズ計算に、CalculateBufferSizeToCommunicate()
は送受信バッファのサイズ計算に利用します。
14.1.3. スリープへの対応
スリープに移行すると、接続が切断され、実行中の処理は強制的に終了されます。また、未送信のパケットや受信処理をしていないデータもすべて破棄されます。
スリープによる切断では相手の機器に切断要求は送信されません。相手に切断を通知する場合は、スリープ前に切断処理を実行し、その完了を確認してからスリープに移行してください。
14.1.4. 通信可能範囲
14.2. 初期化
nn::ir::Communicator
クラスの Initialize()
を呼び出すことで、IR ライブラリの初期化が行われます。
nn::Result nn::ir::Communicator::Initialize( void* pBuf, size_t bufSize, size_t receiveBufferDataSize, size_t receiveBufferManagementSize, size_t sendBufferDataSize, size_t sendBufferManagementSize);
pBuf
と bufSize
には、送受信されるパケットの管理などにライブラリが使用するバッファとそのサイズを指定します。ライブラリに渡すバッファはアプリケーションで事前に確保しますが、先頭アドレスが nn::ir::Communicator::BUFFER_ALIGNMENT
( 4096 Byte ) のアライメント、サイズが nn::ir::Communicator::BUFFER_UNITSIZE
( 4096 Byte ) の倍数でなければなりません。また、デバイスメモリから確保したバッファは使用できません。
ライブラリに渡されたバッファは複数の領域に分けて使用されます。各領域のサイズは、receiveBufferDataSize
、receiveBufferManagementSize
、sendBufferDataSize
、sendBufferManagementSize
で指定します。サイズ指定の詳細については、「14.2.1. ライブラリに与えるバッファ」を参照してください。
通信速度は 115200 bps 固定です。3DS の赤外線通信では、1 Byte のデータ送信にスタートビットとエンドビットの 2 ビットが付与されます。このため、実データの転送速度(bps)は、ボーレートの値に 8/10 を乗算した値となり、92160 bps(11520 Byte/s)となります。
14.2.1. ライブラリに与えるバッファ
IR ライブラリの送受信処理は非同期関数として実装されています。初期化関数でライブラリに与えるバッファは、主に送受信されるパケットを一時的に保存するために使用されます。
ライブラリに渡されたバッファは、以下の領域に区切られて使用されます。
各領域のサイズは、以下のように計算します。
14.2.1.1. 予約領域
ライブラリの動作に必要な固定サイズの領域です。
予約領域のサイズは、以下の関数で取得することができます。
size_t nn::ir::Communicator::GetReservedSize();
14.2.1.2. 送信/受信パケット管理領域
送受信されるパケットの管理情報が格納される領域です。
同時に保持する最大のパケット数をもとにサイズを計算します。接続処理のために、送信、受信ともに少なくとも GetManagementSize(1)
で返されるサイズを指定しなければなりません。
管理情報のサイズは、以下の関数で取得することができます。
size_t nn::ir::Communicator::GetManagementSize(s32 dataNum);
dataNum
に同時に保持する最大のパケット数を指定します。
14.2.1.3. 送信/受信パケット保存領域
送受信されるパケットが格納される領域です。接続処理のために、送信、受信ともに少なくとも 32 バイト以上のサイズでなければなりません。
データのサイズが可変の場合、領域のサイズには、最大サイズのデータで算出したパケットサイズ以上の値を指定しなければなりません。
データのサイズが固定の場合、領域のサイズには、1 データで算出したパケットのサイズに最大のパケット数を乗算した値を指定することができます。
以下の関数で、データのサイズからパケットサイズを計算することができます。
size_t nn::ir::Communicator::GetPacketSize(size_t dataSize);
dataSize
にデータのサイズを指定します。
受信パケット保存領域の注意点
受信パケット保存領域には、ノイズによって発生する不正なパケットなどが保存されることがあります。そのため、そのあとに受信した正常なパケットが確実に保存されるように、受信パケット保存領域は実際に受信するパケットよりも大きなサイズで指定することを推奨します。例えば、各領域のサイズを決定したあと、使用されない領域のサイズを受信パケット保存領域のサイズに加算する方法があります。
以下のコード例では、サイズ固定のデータを送受信する場合に、受信パケット保存領域のサイズを最大限に確保しています。
// バッファと送受信するデータのサイズ、保存するパケット数 static u8 buffer[4096] NN_ATRRIBUTE_ALIGN(4096); size_t sendDataSize = 100; size_t sendPacketNum = 10; size_t recvDataSize = 50; size_t recvPacketNum = 20; // 必要なサイズを計算 size_t sendBufferSize = nn::ir::Communicator::GetPacketSize(sendDataSize) * sendPacketNum; size_t sendManagementSize = nn::ir::Communicator::GetManagementSize(sendPacketNum); size_t receiveBufferSize = nn::ir::Communicator::GetPacketSize(recvDataSize) * recvPacketNum; size_t receiveManagementSize = nn::ir::Communicator::GetManagementSize(recvPacketNum); size_t reservedSize = nn::ir::Communicator::GetReservedSize(); // 領域の総和がバッファサイズ以下であるかをチェック NN_ASSERT((sendBufferSize + sendManagementSize + receiveBufferSize + receiveManagementSize + reservedSize) <= sizeof(buffer)); // 使用されない領域のサイズを受信パケット保存領域のサイズに加算 receiveBufferSize = sizeof(buffer) - (sendManagementSize + sendBufferSize + receiveManagementSize + reservedSize);
14.3. 接続
3DS 同士が赤外線通信を始めるには接続処理を行い、お互いを通信機器として認証しなければなりません。接続要求を待ち受ける側と要求する側で、呼び出す関数は明確に分かれています。待ち受ける側は WaitConnection()
を、要求する側は RequireConnection()
を呼び出してください。いずれの関数も非同期で処理を行いますので、接続処理が完了する前に制御が戻ります。
static nn::Result nn::ir::Communicator::WaitConnection(); static nn::Result nn::ir::Communicator::WaitConnection( nn::fnd::TimeSpan sendReplyDelay); static nn::Result nn::ir::Communicator::RequireConnection(); static nn::ir::ConnectionStatus nn::ir::Communicator::GetConnectionStatus(); static nn::ir::TryingToConnectStatus nn::ir::Communicator::GetTryingToConnectStatus();
非同期処理のため、接続処理が完了したかどうかは GetConnectionStatus()
で得られるライブラリ全体の状態で確認します。接続処理が完了していれば、返り値に CONNECTION_STATUS_CONNECTED
が返されます。また、CONNECTION_STATUS_TRYING_TO_CONNECT
が返されたときは、GetTryingToConnectStatus()
で詳細な処理状況を取得することができます。そのほかの返り値については「14.7. 接続状態の取得」を参照してください。
以下に、GetTryingToConnectStatus()
の返り値の一覧を示します。
返り値 |
説明 |
---|---|
|
接続処理中ではありません。 |
|
接続要求を送信中です。 |
|
接続要求への応答を待っています。 |
|
接続要求を待っています。 |
|
接続要求への応答を送信中です。 |
14.3.1. 自動接続
同じメニューから赤外線通信を行うように、2 台の 3DS のどちらが接続要求を待ち受けるのかが明確ではない場合は自動接続を利用します。
自動接続では、待ち受け側と要求側を自動的に切り替え、2 台の 3DS が待ち受け側と要求側に分かれたタイミングで接続処理が行われます。そのため、その時点での組み合わせによって接続関係が変化します。
static nn::Result nn::ir::Communicator::AutoConnection(); static nn::Result nn::ir::Communicator::AutoConnection( nn::fnd::TimeSpan sendReplyDelay, nn::fnd::TimeSpan waitRequestMin, nn::fnd::TimeSpan waitRequestMax, nn::fnd::TimeSpan waitReplyMin, nn::fnd::TimeSpan waitReplyMax);
下図は、自動接続の動作を表した模式図です。
引数なしで呼び出した場合は、初期化時のボーレートをもとにライブラリが計算した値が使用されます。アプリケーションで行っているほかの処理の影響を受け、引数なしで呼び出した自動接続では接続が行われない場合があります。そのような場合は、各引数を以下の点に注意しながらアプリケーションで指定してください。
sendReplyDelay
は接続要求パケットを受信してから、接続応答パケットを送信するまでの時間です。デフォルトでは 3 ミリ秒が使用されています。少なくとも、相手の送受信が切り替わるまでの時間(3 ミリ秒)が必要です。
waitRequestMin
と waitRequestMax
は接続要求パケットの送信と接続応答パケットの受信待ちを行う最小時間と最大時間です。waitRequestMin
は少なくとも、接続要求パケットの送信時間と接続応答パケットの受信時間、送受信の切り替え時間(3 ミリ秒)の合計よりも大きくなければなりません。
waitReplyMin
と waitReplyMax
は接続要求パケットの受信待ちと接続応答パケットの送信を行う最小時間と最大時間です。waitReplyMin
は少なくとも、接続要求パケットの受信時間と接続応答パケットの送信時間、送受信の切り替え時間(3 ミリ秒)の合計よりも大きくなければなりません。
接続要求パケットの大きさは CONNECTION_REQUEST_PACKET_SIZE
(8 バイト)で、接続応答パケットの大きさは CONNECTION_REPLY_PACKET_SIZE
(5 バイト)で定義されています。送受信にかかる時間は実データの転送速度で計算します。パケットサイズの定義はバイト単位のため、ビット単位の転送速度から計算する場合は注意してください。
14.3.2. 接続関係
GetConnectionRole()
で接続関係を取得することができます。接続関係によって、IR ライブラリの動作が異なることはありません。赤外線通信での役割を決定する指針として利用してください。
static nn::ir::ConnectionRole nn::ir::Communicator::GetConnectionRole(void);
以下に、GetConnectionRole()
の返り値の一覧を示します。
返り値 |
説明 |
---|---|
|
接続処理前もしくは接続処理中のため、接続関係は決定していません。 |
|
接続を要求して接続状態になりました。
|
|
接続要求を受け付けて接続状態になりました。
|
14.4. 通信 ID の確認
接続処理が完了したあとは、異なるアプリケーションとの送受信を行わないように、事前にお互いの通信 ID を確認する必要があります。なお、通信 ID の確認が完了するまで、送信関数や受信関数を呼び出したときにエラーが返されます。
確認に使用する通信 ID は CreateCommunicationId()
で取得します。
static bit32 nn::ir::Communicator::CreateCommunicationId( bit32 uniqueId, bool isDemo = false);
uniqueId
には、弊社がタイトルごとに割り当てたユニーク ID を指定します。異なるタイトル間で通信を行う際は、どちらか片方の ID を指定してください。
isDemo
には、ユニーク ID が共通となる製品版とダウンロードアプリ型体験版の間で赤外線通信を行いたくない場合に、体験版側のみ true
を指定してください。体験版との通信を行うかどうかに関わらず、製品版では必ず false
を指定してください。
弊社からユニーク ID を割り当てられていないタイトルや実験プログラムで赤外線通信を使用する場合は、uniqueId
にゲームソフト試作用コード(0xFF000~0xFF3FF)を指定してください。ただし、製品版では必ず弊社より割り当てられたユニーク ID を指定してください。
通信 ID の確認は、一方の 3DS で RequireToConfirmId()
を、もう一方で WaitToConfirmId()
を呼び出して行います。両方で指定された通信 ID と通信モード識別 ID、パスフレーズのすべてが一致する場合に両関数は成功し、送信関数や受信関数を使用することができるようになります。
static nn::Result nn::ir::Communicator::RequireToConfirmId( u8 subId, bit32 communicationId, const char passphrase[], size_t passphraseLength); static nn::Result nn::ir::Communicator::WaitToConfirmId( u8 subId, bit32 communicationId, const char passphrase[], size_t passphraseLength, nn::fnd::TimeSpan timeout); static bool nn::ir::Communicator::IsConfirmedId();
communicationId
には、CreateCommunicationId()
で生成された通信 ID を使用してください。
subId
は、アプリケーションの複数のシーンで本体間通信を利用する場合に、異なるシーン間で通信が成立しないようにするための通信モード識別 ID です。シーンごとに異なる値を設定してください。
passphrase
で指定するパスフレーズは、赤外線通信のパケットを暗号化するための鍵となりますので、容易に見破られるような文字列は避けて指定してください。パスフレーズはその長さが IR_PASSPHRASE_LENGTH_MIN
以上、IR_PASSPHRASE_LENGTH_MAX
以下の範囲で指定できます。
passphraseLength
には、パスフレーズの長さを指定します。
RequireToConfirmedId()
と WaitToConfirmedId()
の両関数は、暗黙的にアプリケーションに割り当てられている nn::os::Event
クラスを 1 つ消費することに注意してください。
通信 ID の確認が完了していても、切断もしくは再接続を行ったあとはそのたびに確認を行わなければなりません。
なお、すでに ID の確認が行われているかどうかは IsConfirmedId()
で確認することができます。
14.5. 送信
通信 ID の確認が完了したあとであれば、Send()
を呼び出すことで赤外線通信によるデータの送信を行うことができます。
static nn::Result nn::ir::Communicator::Send( void *pBuffer, size_t dataSize, size_t bufferSize, bool restore = false); static void nn::ir::Communicator::GetSendEvent(nn::os::Event* pEvent); static nn::Result nn::ir::Communicator::GetLatestSendErrorResult(bool clear); static void nn::ir::Communicator::GetSendSizeFreeAndUsed( size_t *pSizeFree, s32 *pCountFree, size_t *pSizeUsed, s32 *pCountUsed);
pBuffer
と bufferSize
には、送信バッファとそのサイズを指定します。ただし、送信バッファのサイズは送信するデータのサイズを CalculateBufferSizeToCommunicate()
に渡して得たサイズ以上でなければなりません。また、先頭アドレスが nn::ir::Communicator::SEND_BUFFER_ALIGNMENT
( 4 Byte ) のアライメントでなければなりません。
dataSize
には、送信するデータのサイズを指定します。送信可能なデータサイズは 0 ~ 16316 Byte です。
送信されるデータは pBuffer
の先頭から dataSize
の範囲にあるデータです。送信が完了すると、バッファの内容は暗号化やヘッダの付加などが施された状態になります。送信後に元のデータが必要な場合は restore
に true
を指定してください。ただし、送信のみを行うよりも処理に時間がかかります。
実際の送信処理は非同期で行われ、Send()
はライブラリに与えられたバッファに送信パケットを保存した時点で制御を戻します。実際に送信が行われたタイミングは、GetSendEvent()
で取得できるイベントに通知されます。pEvent
に渡されたイベントは自動リセットイベントとして初期化されます。送信処理でエラーが発生したかどうかは、GetLatestSendErrorResult()
で取得する Result
クラスで判断することができます。引数 clear
に true
を渡して呼び出した場合は、ライブラリで保存されていた Result
クラスのエラーがクリアされます。
ライブラリはバッファの状態を監視しており、できる限り早く送信パケットを赤外線通信モジュールから送信します。もし送信関数を短い期間で複数回実行した場合、ライブラリは一時的にバッファに複数のパケットをため、やがて先に保存されたパケットから順に取り出して送信します。
大きなパケットの送信や繰り返し送信することによって、保存したいパケットサイズの合計がバッファの容量を超えてしまった場合、ライブラリはそれ以上のパケットを保存せず破棄します。そのため、GetSendSizeFreeAndUsed()
で送信パケットの管理領域と保存領域に空きがあるかを確認してから送信することを推奨します。特に、保存領域にはパケットとして保存されるため、GetPacketSize()
で取得するパケットサイズ分の空きが必要であることに注意してください。
14.6. 受信
通信 ID の確認が完了したあとであれば、Receive()
を呼び出すことで赤外線通信によるデータの受信を行うことができます。ライブラリは接続処理のあと、送信すべきパケットを保持していないときには常にデータを受信する状態になっているため、厳密にいうと Receive()
は受信を行う関数ではなく、受信していたデータをバッファから取り出す関数です。受信したデータが正しいものであるかどうかは Receive()
で取り出すまで判別できません。
static nn::Result nn::ir::Communicator::Receive( void* pDst, size_t size, size_t *pReceiveSize, s32 *pRemainCount); static void nn::ir::Communicator::GetReceiveEvent(nn::os::Event* pEvent); static nn::Result nn::ir::Communicator::GetLatestReceiveErrorResult( bool clear); static nn::Result nn::ir::Communicator::GetNextReceiveDataSize(size_t *pSize); static nn::Result nn::ir::Communicator::DropNextReceiveData(s32 *pRemainCount); static void nn::ir::Communicator::GetReceiveSizeFreeAndUsed( size_t *pSizeFree, s32 *pCountFree, size_t *pSizeUsed, s32 *pCountUsed);
pDst
と size
には、受信バッファとそのサイズを指定します。受信バッファの先頭アドレスは nn::ir::Communicator::RECEIVE_BUFFER_ALIGNMENT
( 4 Byte ) のアライメントでなければなりません。また、受信バッファには暗号化やヘッダの付加などが施された状態のデータが一時的に保存されるため、そのサイズは実際に受信するデータのサイズを CalculateBufferSizeToCommunicate()
に渡して得たサイズ以上でなければなりません。なお、受信に必要となるバッファのサイズは GetNextReceiveDataSize()
であらかじめ取得することができます。
一度の呼び出しで 1 パケット分のデータが取り出されます。取り出されたデータのサイズは pReceiveSize
に格納されます。また、データを格納すると同時に、pRemainCount
には取り出すことのできる受信データの残数が格納されます。保存されているデータをすべて取り出したい場合は残数が 0 になるまで繰り返し呼び出してください。
GetReceiveEvent()
で取得できるイベントには、受信パケットがバッファに保存されたタイミングが通知されますので、受信したデータをすぐに取り出したい場合に利用してください。pEvent
に渡されたイベントは自動リセットイベントとして初期化されます。受信処理でエラーが発生したかどうかは、GetLatestReceiveErrorResult()
で取得する Result
クラスで判断することができます。引数 clear
に true
を渡して呼び出した場合は、ライブラリで保存されていた Result
クラスのエラーがクリアされます。
GetNextReceiveDataSize()
で取得したデータのサイズが想定する受信データのサイズとは明らかに異なるため、次に受信する予定のデータが不要であると判断した場合は、1 パケット分のデータを DropNextReceiveData()
で破棄することができます。
パケットの受信よりも受信データを取り出す頻度が低く、保存したいパケットサイズの合計がバッファの容量を超えてしまった場合、ライブラリはそれ以上のパケットを保存せず破棄します。GetReceiveSizeFreeAndUsed()
で受信パケットの管理領域と保存領域に空きがあるかを確認することができますので、受信の管理に利用してください。
受信データが残っている状態で現在の接続が切断された場合、次の接続を開始するまでの間は、残っているデータを受信できます。
14.7. 接続状態の取得
GetConnectionStatus()
で接続状態を取得することができます。また、接続状態が変化したタイミングは、GetConnectionStatusEvent()
で取得できるイベントに通知されます。pEvent
に渡されたイベントは自動リセットイベントとして初期化されます。
static void nn::ir::Communicator::GetConnectionStatusEvent( nn::os::Event* pEvent); static nn::ir::ConnectionStatus nn::ir::Communicator::GetConnectionStatus();
以下に、GetConnectionStatus()
の返り値の一覧を示します。
返り値 |
説明 |
---|---|
|
赤外線モジュールが停止している状態です。 |
|
接続処理中を示す状態です。 |
|
接続状態です。 |
|
切断処理中を示す状態です。 |
|
故障などの致命的なエラーが発生している状態です。 |
14.7.1. 故障時の対応
接続状態が CONNECTION_STATUS_FATAL_ERROR
のときは、赤外線モジュールが故障した可能性があります。このとき、Finalize()
を除く、Result
を返す関数すべてが常に nn::ir::ResultFatalError()
を返すようになります。以降の処理で赤外線通信を利用することはできなくなりますので、すぐに IR ライブラリを終了させてください。
赤外線モジュールが故障した可能性がある一方、この状態は電源を入れなおすことで復旧する可能性もあります。そのため、ユーザーに対して、故障の可能性と対応についてメッセージを表示することを推奨します。
14.8. 切断
赤外線通信の切断は Disconnect()
の呼び出しで行われます。
static nn::Result nn::ir::Communicator::Disconnect(void); static nn::Result nn::ir::Communicator::ClearSendBuffer(void); static nn::Result nn::ir::Communicator::ClearReceiveBuffer(void);
Disconnect()
は、通信相手に切断要求を送信します。また、接続処理の途中であれば処理を中断します。切断処理は非同期で行われるため、処理が完了する前に制御が戻ります。
呼び出した時点で未処理の送信パケットがある場合、送信パケットを破棄して切断処理を行いますので、必要であれば、すべてのパケットの送信完了を確認してから本関数を実行してください。未送信のパケットすべてを明示的に破棄する場合は ClearSendBuffer()
を呼び出してください。
切断される前に受信したパケットについては、切断処理のあとでも Receive()
で取り出すことができます。しかし、次の接続および接続認証処理を開始した時点で破棄されることに注意してください。受信しているすべてのパケットを明示的に破棄する場合は ClearReceiveBuffer()
を呼び出してください。