# serparser

シリアル書式の入出力のために用います。内部に解釈済みのバイナリ系列を保持するバッファを持ち、入力時は１バイトずつ系列を読み出し書式に従い内部バッファに格納し、系列の解釈が完了した時点で完了状態になるものです。反対に出力時は内部バッファから所定の出力書式に従いバッファを出力します。

メモリバッファ取り扱い方法([`alloc`](/latest1/api-reference/classes/alloc.md))に応じて３種類のクラス名が定義されています。

```cpp
// serparser_attach : 既存のバッファを用いる
serparser_attach

// serparser : Nバイトのバッファを内部に確保する
serparser_local<N>

// serparser_heap : ヒープ領域にバッファを確保する
serparser_heap
```

## 定数(書式種別)

`begin()`の初期化のパラメータで渡す書式の種別です。ここではアスキー形式とバイナリー形式の２種類があります。

| 定数                           | 種別      |
| ---------------------------- | ------- |
| `uint8_t PARSER::ASCII = 1`  | アスキー形式  |
| `uint8_t PARSER::BINARY = 2` | バイナリー形式 |

{% hint style="warning" %}
バイナリ形式の取り扱いはアスキー形式に比べ、必要なツールや確認方法を含め一般に取り扱いが煩雑になります。通常はアスキー形式をお使いください。
{% endhint %}

## 形式について

### アスキー形式

アスキー形式は、バイナリで構成されたデータ列を文字列で表現する方法です。

例えばバイト列で `00A01301FF123456` をアスキー形式で表現すると、以下のようになります。先頭は `:` で `B1` がチェックサム、終端は `[CR:0x0d][LF:0x0a]` となります。

> `:00A01301FF123456B1[CR][LF]`

終端のチェックサムを省略できます。チェックサムからCRLFの系列を`X`に置き換えます。文字化けによる誤ったデータ系列には弱くなりますが、実験などでデータを送付したいときに便利です。

> `:00A01301FF123456X`

#### 定義

| ====== | 元データのバイト数 | バイト数 | 解説                                                                                                                                                                                                                                    |
| ------ | :-------: | :--: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| ヘッダ    |           |   1  | `:`(0x3A) コロンを指定します。                                                                                                                                                                                                                  |
| データ部   |     N     |  2N  | <p>元データの各バイトをアスキー文字列２文字（A-F は大文字）で表現します。<br>例えば 0x1F は <code>1</code> (0x31) <code>F</code> (0x46) と表現します。</p>                                                                                                                        |
| チェックサム |           |   2  | <p>データ部の各バイトの和を８ビット幅で計算し２の補数をとります。つまりデータ部の各バイトの総和＋チェックサムバイトを８ビット幅で計算すると０になります。<br>チェックサムバイトをアスキー文字列２文字で表現します。<br>例えば <code>00A01301FF123456</code> では 0x00 + 0xA0 + ... + 0x56 = 0x4F となり、この二の補数は0xB1 です。(つまり 0x4F + 0xB1 = 0x00)</p> |
| フッタ    |           |   2  | \[CR] (0x0D) \[LF] (0x0A) を指定する。                                                                                                                                                                                                      |

### バイナリ形式

{% hint style="warning" %}
通常は**アスキー形式**を利用してください。

マイコン間通信での実装を考えるとバイナリ形式のほうが効率的ですが、実験などでの送受信の確認にはバイナリ通信に対応した特別なターミナルなどを準備する必要があり、チェックサムの計算も必須です。アスキー形式より利用の難易度は高くなります。
{% endhint %}

バイナリ形式は、バイナリで構成されたデータ列にヘッダとチェックサムを付加して送付する方法です。

例えば `00A01301FF123456`をバイナリ形式で表現すると、以下のようになります。

> `0xA5 0x5A 0x80 0x08 0x00 0xA0 0x13 0x01 0xFF 0x12 0x34 0x56 0x3D`

#### 定義

| ====== | 元データのバイト数 | 形式におけるバイト数 | 解説                                                                                                                    |
| ------ | :-------: | :--------: | --------------------------------------------------------------------------------------------------------------------- |
| ヘッダ    |           |      2     | `0xA5 0x5A` を指定します。                                                                                                   |
| データ長   |           |      2     | <p>データ長はビッグエンディアン形式の２バイトで、MSB (0x8000) を設定した上、データ部の長さを指定します。<br>例えばデータ部の長さが 8 バイトなら<code>0x80 0x08</code> を指定します。</p> |
| データ部   |     N     |      N     | 元データを指定します。                                                                                                           |
| チェックサム |           |      1     | <p>データ部の各バイトの XOR を計算します。<br>例えばデータ部が <code>00A01301FF123456</code>なら 0x00 xor 0xA0 xor ... 0x56 = 0x3D となります。</p>    |
| フッタ    |           |     (1)    | チェックサムが事実上の終端です。無線モジュールからの出力では `0x04` (EOT) が付加されます。                                                                  |

## メソッド

### 宣言, begin()

```cpp
// 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);
```

宣言にはメモリの確保クラスを指定します。この指定は煩雑であるため、上述のように別名定義を行っています。

| <p>クラス名（別名定義）<br>メモリ確保</p> | 内容                                   |
| -------------------------- | ------------------------------------ |
| `serparser_attach`         | すでにあるバッファを`begin()`にて指定する            |
| `serparser_local<N>`       | Nバイトのバッファを内部に確保する                    |
| `serparser_heap`           | `begin()`メソッドのパラメータで指定したサイズをヒープに確保する |

メモリ確保クラスに応じた`begin()`メソッドを呼び出します。

#### serparser\_attach

```cpp
void begin(uint8_t ty, uint8_t *p, uint16_t siz, uint16_t max_siz)
```

`ty`で指定する[形式](#nitsuite)で、`p`で指定したバッファを用います。バッファの最大長は`max_siz`で、バッファの有効データ長を`siz`で指定します。

この定義は、特に、データ列を書式出力したい場合に用います(`>>` 演算子参照)

#### serparser\_local\<N> - 内部にバッファを確保する

```cpp
void begin(uint8_t ty)
```

`ty`で指定する[形式](#nitsuite)で初期化を行います。

#### serparser\_heap - ヒープに確保

```cpp
void begin(uint8_t ty, uint16_t siz)
```

`ty`で指定する[形式](#nitsuite)で、`siz`で指定したサイズをヒープに確保して初期化します。

{% hint style="warning" %}
一度確保したヒープ領域は解放できません。
{% endhint %}

### get\_buf()

```cpp
BUFTYPE& get_buf()
```

内部バッファを返す。バッファは `smplbuf<uint8_t, alloc>` 型となります。

### parse()

```cpp
inline bool parse(uint8_t b)
```

入力文字を処理する。書式入力の入力文字列を１バイト受け取り書式に従い解釈します。例えばASCII書式では`:00112233X`のような系列を入力として受け取りますが `: 0 0 ... X` の順で１バイトずつ入力し、最後の `X` を入力した時点で書式の解釈を完了し、完了済みと報告します。

`parse()`のパラメータは入力バイト、戻り値は解釈完了であれば`true`を戻します。

{% hint style="info" %}
`parse()`で読み出し完了になったとき、次の`parse()`を実行すると読み出し中のステータスに戻ります。
{% endhint %}

#### 例

```cpp
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()

```cpp
operator bool() 
```

`true`なら`parse()`により読み出しが完了した状態で、`false`なら解釈中となります。

#### 例 (parse()の例は以下のように書き換えられる)

```cpp
while (Serial.available()) {
    int c = Serial.read();
    
    SerialParser.parse(c);
    
    if(SerialParser) {
        // 書式解釈完了、b に得られたデータ列(smplbuf<uint8_t>)
        auto&& b = SerialParser.get_buf();
        // ...
    }
}
```

### << 演算子

内部バッファを書式形式でストリーム(Serial)に対して出力します。

#### 例

```cpp
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] 
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://mwx.twelite.info/latest1/api-reference/classes/ser_parser.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
