yunipocket: シリアル通信プロトコルの設計

... これめっちゃ奥が深い。。
基本構造
複数のバイトストリームを 短い パケットに分割して送受信する。短さが重要なのは、
- マイコン側のプロトコルバッファを小さくするため
- 長いメッセージの送受信中(ファームウェアアップロード等)にレイテンシ要求の厳しいメッセージ(MIDI等)を送受信できるようにするため
受け入れ可能なレイテンシは通信速度との兼ね合いで決まってくる。例えば、いわゆる115200bpsのUARTは1バイトの送信に10ビット時間掛かる。最悪遅延を20msにしたければ、20msで送信できるメッセージ長 115200 / 1000(秒) * 20(ミリ秒) / 10(ビット時間)= 230.4 バイト よりも短いパケット長を採用する必要がある。
ワイヤプロトコルにはバリエーションを持たせる。たとえば、一部のプロトコル、Bluetooth LEにおけるGATTとかEthernetはパケット単位での送受信が可能なのでパケット長フィールドは不要と言えるが、UARTやTCPのようにバイト単位での送受信を行うプロトコルも存在する。 -- 効率のためには、不要なところではパケット長の省略を行えるようにする必要があるだろう。FCSやCRCのようなビット化けチェックも同様となる。
プロトコル1: チェックサム入りバイトストリーム
フィールド | 長さ | 内容 |
---|---|---|
ヘッダ | 1 | M |
セッションID | 1 | セッションID(<100) + Continue bit |
長さ(N) | 1 | ペイロード部の長さ |
ペイロード | N | セッションIDに紐付いたペイロード |
CRC | 1 | CRC8 |
チェックサムなしのバイトストリームプロトコルは今のところ規定しない。TCPなりWebRTCなりの性能を生かすためには専用のプロトコルが必要になるので。。
セッションIDは下位7bitが有効で、最上位ビットはcontinue bitとして使用する。ビットが 0
の場合はパケットを終端し、 1
の場合はパケットを継続する。例えばdebug printのような終端の無いものはこのビットは常に 0
となる。
ヘッダ部の必要性は微妙なところだが、確実に受信側をエラー状態にする簡単な方法として用意することにした。 M
以外のデータを255(最大パケット長) + α バイト送信するとエラーが返却されるので、そのエラー以降にパケットを送出することでout of sync状態から復帰できる。
プロトコル2: ショートパケット
フィールド | 長さ | 内容 |
---|---|---|
セッションID | 1 | セッションID(<100) + Continue bit |
ペイロード | N | セッションIDに紐付いたペイロード |

セッション0
セッションID == 0 は特殊な用途で使用する。
00: デバッグコンソール
デバッグコンソールは 0
で始まるパケットで、いわゆるdebug printを実現する。 ...入力を実装するかは未定。。
00 <Console-Address> <Target-Address> data ...
コンソールアドレスは以下で固定されている。
アドレス | 内容 |
---|---|
00 |
カーネル(通常) |
01 |
カーネル(アテンション/エラー) |
02 |
アプリケーション(通常) |
03 |
アプリケーション(アテンション/エラー) |
04
以降は予約とする。
01: エラーレポート / リセットリクエスト
エラーレポートパケットは 1
で始まるパケットで、通信路上のエラーを報告する。
-
01 00 00
-- リセットリクエスト(全てのセッションを破棄する)。 -
01 01
-- CRCエラーまたはフレーミングエラー。
ホストからリセットリクエストを送信する場合は、
01 00 <Target-Address>
02、03: ディスカバリ
ホストはディスカバリリクエストを以下の形式で行える。
02
... パラメータは特にない。
ターゲットは以下の形式で応答する。
03 <Service-ID> <Service-Index> <Extra> ...
Service-IDは4バイトの識別子で、 MIDI
であればMIDI1.0トランスポートのように、いわゆるFOURCC形式で指定する。後述のコネクションリクエストはService-Indexで指定する。末尾は _END
サービスとなる。
04、05: コネクションリクエスト
ホストは 04
に続けて Service-Index を指定することで接続をリクエストする。
04 <Service-Index> <arg> <opcode>
ターゲットは 05
に続けてセッションIDを払い出す。 ホストからセッションIDを指定することはできない 。(ターゲットによるセッションIDのハードコードを可能にするため)
opcode == 0 の時は接続を新規に作成する。 1つのサービスに2つ以上のコネクションを作成することはできない 。opcode == 1 の時は、 argに指定したセッションを破棄する。
05 <Session-Id> <Error-Code> message ...
ゼロ番のセッションIDが払い出されることは無い。エラーコードとメッセージの内容は未定。