#問題
マイコン触っているとPCとのインターフェースにUARTを使うことがよくある。(UART-USB変換ICとか入れて)
単純なデータであればASCIIコードの文字列でやり取りすればいい。
例えばこんな感じ
元データ
{文字列}
変換後
<STX>{文字列}<ETX>
(<STX>=0x02,<ETX>=0x03)
フレームはSTX
が来たら開始、ETX
が来たら終了となる。
さて、送りたいデータが増えてくると、テキストではなく生バイト列で送りたくなってくる。
しかしバイト列には当然0x00
~0xFF
が含まれ得るので、
何も考えずに文字列と同じようにSTX
とETX
で挟んで送ると
データに0x02
や0x03
が含まれる場合、意図しないところでフレームの切れ目を認識してしまう。
ではどうするか?
先人たちの知恵を調べた。
(一番楽なのはUARTを投げ捨てることだが。)
#時間を空ける
マイコン側が面倒。
#データリンク拡張を使う
制御文字のDLE=0x10
を使う方法。
送りたいデータ中の0X10
を特別扱いする:
送りたいデータ中にDLE
が入っていた場合にはDLE DLE
に置換する。
要するにエスケープシーケンス。
{データ}
|
V
<DLE><STX>{変換データ}<DLE><ETX>
変換前
00 02 10 1A 00 10 10
変換後(除Header,Trailer)
00 02 10 10 1A 00 10 10 10 10
特に難しいロジックでもなく、フレーム検出、変換の実装が容易。
ただし、元データ中にDLE
が多く含まれると、変換後のフレーム長がやたら長くなる。
全データがDLE
なら変換後はデータ部分が2倍になってしまう。
#文字列に変換する
バイナリデータをASCIIで印字可能な文字列に変換すれば、制御文字を使用して送信できる。
変換方法に例えばBase64を使うと、データサイズ約33%の増加で文字列化できる。
Base64エンコード/デコード処理のマイコン向け実装もネットの海に転がっている。
#Consistent Overhead Byte Stuffing1 (COBS)
長さが同じなら、どんなデータでも変換後のオーバーヘッドがだいたい等しくなるような変換方法。
0x00
を特殊なバイトとして、データ中に0x00
が出る場合は別のバイトで置換する。
- フレームの切れ目は
0x00
で表現する:元データ先頭と末尾に0x00
を追加する。 - 末尾以外の
0x00
を、次の0x00
までのバイト長(=0x00
~0x00
間にあるバイト数+1)で置換する。 - 次に
0x00
が出てくるまでのバイト長が0xFF
以上になる場合、0xFF
に置換して、0xFF
後に次の0x00
が出てくるまでの(残りの)バイト長を入れる。
フレームの切れ目が0x00
固定なので簡単にフレームを検出可能。
オーバーヘッドがそこそこ安定している。(254byte以下なら確実に+2byte)
ただし変換が若干面倒。
##変換例
元データ + 末尾00
変換データ
01 (00)
02 01 00
00 (00)
01 01 00
00 00 (00)
01 01 01 00
01 02 00 03 04 00 (00)
03 01 02 03 03 04 01 00
01 02 ... FE (00)
FF 01 02 ... FE 00
01 02 ... FE FF (00)
FF 01 02 ... FE 02 FF 00
#エラーチェック付きヘッダーをつける
フレームの長さが書かれたヘッダーを付ける。
当然長さ情報だけではフレームの区切りがわからないので、
一緒にヘッダーのエラーチェックなどをつける。
多分シリアルポート界では定番。とあるGNNS付き9軸+αセンサーもこうなっていた。
Generic Framing Procedure(GFP)だと
長さ2byte + ヘッダーエラーチェック2byte
オーバーヘッドが固定。
変換もヘッダー付け外しするだけなので簡単。
ただしフレーム開始の検出が(他の方法に比べ)面倒。