serparser
シリアル書式入出力 (mwx::serial_parser)
シリアル書式の入出力のために用います。内部に解釈済みのバイナリ系列を保持するバッファを 持ち、入力時は1バイトずつ系列を読み出し書式に従い内部バッファに格納し、系列の解釈が完了した時点で完了状態になるものです。反対に出力時は内部バッファから所定の出力書式に従いバッファを出力します。
// serparser_attach : 既存のバッファを用いる
serparser_attach
// serparser : Nバイトのバッファを内部に確保する
serparser_local<N>
// serparser_heap : ヒープ領域にバッファを確保する
serparser_heap
begin()
の初期化のパラメータで渡す書式の種別です。ここではアスキー形式とバイナリー形式の2種類があります。定数 | 種別 |
---|---|
uint8_t PARSER::ASCII = 1 | アスキー形式 |
uint8_t PARSER::BINARY = 2 | バイナリー形式 |
バイナリ形式の取り扱いはアスキー形式に比べ、必要なツールや確認方法を含め一般に取り扱いが煩雑になります。通常はアスキー形式をお使いください。
アスキー形式は、バイナリで構成されたデータ列を文字列で表現する方法です。
例えばバイト列で
00A01301FF123456
をアスキー形式で表現すると、以下のようになります。先頭は :
で B1
がチェックサム、終端は [CR:0x0d][LF:0x0a]
となります。:00A01301FF123456B1[CR][LF]
終端のチェックサムを省略できます。チェックサムからCRLFの系列を
X
に置き換えます。文字化けによる誤ったデータ系列には弱くなりますが、実験などでデータを送付したいときに便利です。:00A01301FF123456X
====== | 元データのバイト数 | バイト数 | 解説 |
---|---|---|---|
ヘッダ | | 1 | : (0x3A) コロンを指定します。 |
データ部 | N | 2N | 元データの各バイトをアスキー文字列2文字(A-F は大文字)で表現します。
例えば 0x1F は 1 (0x31) F (0x46) と表現します。 |
チェックサム | | 2 | データ部の各バイトの和を8ビット幅で計算し2の補数をとります。つまりデータ部の各バイトの総和+チェックサムバイトを8ビット幅で計算すると0になります。
チェックサムバイトをアスキー文字列2文字で表現します。
例えば 00A01301FF123456 では 0x00 + 0xA0 + ... + 0x56 = 0x4F となり、この二の補数は0xB1 です。(つまり 0x4F + 0xB1 = 0x00) |
フッタ | | 2 | [CR] (0x0D) [LF] (0x0A) を指定する。 |
通常はアスキー形式を利用してください。
マイコン間通信での実装を考えるとバイナリ形式のほうが効率的ですが、実験などでの送受信の確認にはバイナリ通信に対応した特別なターミナルなどを準備する必要があり、チェックサムの計算も必須です。アスキー形式より利用の難易度は高くなります。
バイナリ形式は、バイナリで構成されたデータ列にヘッダとチェックサムを付加して送付する方法です。
例えば
00A01301FF123456
をバイナリ形式で表現すると、以下のようになります。0xA5 0x5A 0x80 0x08 0x00 0xA0 0x13 0x01 0xFF 0x12 0x34 0x56 0x3D
====== | 元データのバイト数 | 形式におけるバイト数 | 解説 |
---|---|---|---|
ヘッダ | | 2 | 0xA5 0x5A を指定します。 |
データ長 | | 2 | データ長はビッグエンディアン形式の2バイトで、MSB (0x8000) を設定した上、データ部の長さを指定します。
例えばデータ部の長さが 8 バイトなら 0x80 0x08 を指定します。 |
データ部 | N | N | 元データを指定します。 |
チェックサム | | 1 | データ部の各バイトの XOR を計算します。
例えばデータ部が 00A01301FF123456 なら 0x00 xor 0xA0 xor ... 0x56 = 0x3D となります。 |
フッタ | | (1) | チェックサムが事実上の終端です。無線モジュールからの出力では 0x04 (EOT) が付加されます。 |
// serparser_attach : 既存のバッファを用いる
serparser_attach p1;
uint8_t buff[128];
p1.begin(ARSER::ASCII, buff, 0, 128);
// serparser : Nバイトのバッファを内部に確保する
serparser p2<128>;
p2.begin(PARSER::ASCII);
// serparser_heap : ヒープ領域にバッファを確保する
serparser_heap p3;
p3.begin(PARSER::ASCII, 128);
宣言にはメモリの確保クラスを指定します。この指定は煩雑であるため、上述のように別名定義を行っています。
クラス名(別名定義)
メモリ確保 | 内容 |
serparser_attach | すでにあるバッファを begin() にて指 定する |
serparser_local<N> | Nバイトのバッファを内部に確保する |
serparser_heap | begin() メソッドのパラメータで指定したサイズをヒープに確保する |
メモリ確保クラスに応じた
begin()
メソッドを呼び出します。void begin(uint8_t ty, uint8_t *p, uint16_t siz, uint16_t max_siz)
この定義は、特に、データ列を書式出力したい場合に用います(
>>
演算子参照)void begin(uint8_t ty)
void begin(uint8_t ty, uint16_t siz)
一度確保したヒープ領域は解放できません。
BUFTYPE& get_buf()
内部バッファを返す。バッファは
smplbuf<uint8_t, alloc>
型となります。inline bool parse(uint8_t b)
入力文字を処理する。書式入力の入力文字列を1バイト受け取 り書式に従い解釈します。例えばASCII書式では
:00112233X
のような系列を入力として受け取りますが : 0 0 ... X
の順で1バイトずつ入力し、最後の X
を入力した時点で書式の解釈を完了し、完了済みと報告します。parse()
のパラメータは入力バイト、戻り値は解釈完了であればtrue
を戻します。parse()
で読み出し完了になったとき、次のparse()
を実行すると読み出し中のステータスに戻ります。while (Serial.available()) {
int c = Serial.read();
if (SerialParser.parse(c)) {
// 書式解釈完了、b に得られたデータ列(smplbuf<uint8_t>)
auto&& b = SerialParser.get_buf();
// 以下は得られたデータ列に対する処理を行う
if (b[0] == 0xcc) {
// ...
}
}
}
operator bool()
true
ならparse()
により読み出しが完了した状態で、false
なら解釈中となります。while (Serial.available()) {
int c = Serial.read();
SerialParser.parse(c);
if(SerialParser) {
// 書式解釈完了、b に得られたデータ列(smplbuf<uint8_t>)
auto&& b = SerialParser.get_buf();
// ...
}
}
内部バッファを書式形式でストリーム(Serial)に対して出力します。
uint8_t u8buf[] = { 0x11, 0x22, 0x33, 0xaa, 0xbb, 0xcc };
ser_parser pout;
pout.begin(ARSER::ASCII, u8buf, 6, 6); // u8bufの6バイトを指定
Serial << pout;// Serialに書式出力 -> :112233AABBCC??[CR][LF]
最終更新 10mo ago