# The MWM5 Library

ライブラリについて

{% hint style="success" %}
[資料の取り扱いについて](https://twelite.gitbook.io/general/about_documents)をご参照ください。  お気付きの点がありましたら、当サポート窓口にご連絡いただければ幸いです。
{% endhint %}

{% hint style="warning" %}
本資料は MWM5  バージョン 0.4 に対応します。
{% endhint %}

本ライブラリMWM5は、M5StackとTWELITE をシリアル(UART)接続して、M5Stack上でTWELITEを利用することを目的としています。

* TWELITEシリアルデータ形式のパーサーライブラリ
* M5Stack上でターミナル画面描画を行うライブラリ

![標準アプリ App\_Twelite のパケットデータ表示例](https://1309285009-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Lz_q8yNGtrx39HyLX6-%2F-M074LC8B8fFOP2Jn_x6%2F-M075t8ubyfn1uXj0amX%2F20200215-DSC04564.jpg?alt=media\&token=c218e972-0cdb-431f-8f3d-d822311a3b47)

![TWELITE PALのパケット表示例](https://1309285009-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Lz_q8yNGtrx39HyLX6-%2F-M074LC8B8fFOP2Jn_x6%2F-M075t8wcG5R0TPwoRpa%2F20200215-DSC04557.jpg?alt=media\&token=8944bebe-58d5-4ed4-8296-b0aaccd0677a)


# Changes & Download

変更履歴、ダウンロード

## 0.4.2 - 2020-02-17

> <https://github.com/monowireless/mwm5/releases/tag/0.4.2>

初期バージョン


# License

ライセンス

## ソフトウェアのライセンス

本パッケージ内で、ライセンス上特別な記述のないものは、モノワイヤレスオープンソースソフトウェア使用許諾契約書を適用します。詳細はパッケージ付属の文書を参照してください。

本ソフトウェアについては、モノワイヤレス株式会社が正式にサポートを行うものではありません。お問い合わせにはご回答できない場合もございます。予めご了承ください。

不具合などのご報告に対してモノワイヤレス株式会社は、修正や改善をお約束するものではありません。

また導入パッケージなどお客様の環境に依存して動作しない場合もございます。

```cpp
/* Copyright (C) 2020 Mono Wireless Inc. All Rights Reserved.  *
 * Released under MW-OSSLA-*J,*E (MONO WIRELESS OPEN SOURCE    *
 * SOFTWARE LICENSE AGREEMENT).                                */
```

### MW-OSSLA 概要

{% hint style="warning" %}
ライセンスの定義はライセンス文書に従います。また本概要はライセンス文書の解釈に影響を与えるものでありません。
{% endhint %}

* 無保証です
* MW-OSSLA下で改変や再配布が可能です
* 商用利用を目的としない場合は、モノワイヤレス株式会社の製品を利用しない場合であっても、本ソフトウェアを利用することができます
* 商用利用を目的とする場合は、[モノワイヤレスソフトウェア使用許諾契約書](https://mono-wireless.com/jp/products/TWE-NET/license.html)(MW-SLA)を添付したソースコードを配布し、ML-SLA下のソフトウェアを利用してください
  * MW-OSSLA下のソフトウェアに対し、MW-SLAを添付してソフトウェアを配布することが出来ます
    * 公開したり当社への連絡の必要はありませんが、手続きとして一旦MW-SLAを添付したソフトウェアとして配布してください
  * MW-SLAを添付したソフトウェアは、商用・非商用にかかわりなく、モノワイヤレス株式会社の製品上で利用しなければなりません

## 本ドキュメントの利用について

* 本ドキュメントは無保証です
* 本ドキュメントの著作権はモノワイヤレス株式会社が有します
* 本ドキュメントはライブラリの一部として取り扱い、MW-OSSLA下で取り扱われます


# Getting started


# Environment

環境

以下の環境で開発しています。

動作等の不都合がある場合は、下記の環境を参考にしてください。

| 環境             | 内容                  |
| -------------- | ------------------- |
| OS             | Windows10 バージョン1903 |
| M5Stack ハードウェア | M5stack BASIC       |
| M5Stackライブラリ   | 0.2.9               |


# SDK

開発環境

開発環境は以下のリンクを参照ください。

> M5Stack Library - <https://github.com/m5stack/M5Stack>

最初に上記ライブラリに含まれるサンプルスケッチをビルド、書き込みを行ってください。

### MWM5ライブラリのインストール

MWM5ライブラリは <https://github.com/monowireless/mwm5> で配布しています。ソースファイル一式をダウンロードしてください。本ドキュメント記載時点に一致するバージョンは[変更履歴](/master-2/changes)に記載があります。

変更履歴にある zip ファイルをダウンロードして、開発環境の指定するlibraryディレクトリに展開するか、開発環境の「zip形式のライブラリのインストール」を行ってください。アンインストールはライブラリディレクトリを削除します。


# hardware

ハードウェア接続

TWELITE DIPとの接続例です。シリアルポート(UART)のTX,RXのみのシンプルな接続です。

```
[M5Stack 15P]

    GPIO03
    GPIO01
    GPIO16 --- TWELITE DIO6/UART0 TX (TWELITE DIP #10)
    GPIO17 --- TWELITE DIO7/UART0 RX (TWELITE DIP #3)
    GPIO02
    GPIO05
    GPIO25
    GPIO26
    GPIO35
    GPIO36
       RST
       BAT
       3V3 --- TWELITE VCC (TWELITE DIP #28)
        5V
       GND --- TWELITE GND (TWELITE DIP #1,14)
```


# examples

サンプルコード


# AppTwelite\_Recv

標準アプリケーション

TWELITE DIPに工場出荷時に書き込まれている標準アプリ(App\_Twelite)のシリアルメッセージを解釈してLCD画面上に表示します。

![画面例](https://1309285009-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Lz_q8yNGtrx39HyLX6-%2F-M074LC8B8fFOP2Jn_x6%2F-M075t8ubyfn1uXj0amX%2F20200215-DSC04564.jpg?alt=media\&token=c218e972-0cdb-431f-8f3d-d822311a3b47)

{% hint style="info" %}
M5stickに接続する前に書き込んだTWELITE DIPが無線パケットを受信してデータを出力するかPCで確認しておいてください。
{% endhint %}

M5Stack側のTWELITE DIPは、以下のような接続をしておきます。以下の配線はTWELITE DIPを親機設定(M1=GND)とした配線をしています。AI1-4はアナログポートがオープンになり不定な入力を防ぐためです(App\_TweliteではVCCレベルを入力した場合、そのポートを未使用とする意味を持たせています）

```
                          TWELITE DIP
                 -01:GND              VCC:28-------M5 3V3
                 -02:D14(SCL)    (M3 ) D3:27-
  M5 GPIO16-------03:D7 (RX )    (M2 ) D2:26-
                 -04:D5 (Pw1)    (AI4) D1:25----VCC
                 -05:D18(DO1)    (AI3) A2:24----VCC
                 -06:C  (Pw2)    (AI2) D0:23----VCC
                 -07:M  (Pw3)    (AI1) A1:22----VCC
                 -08:D19(DO2)         RST:21-
                 -09:D4 (DO3)    (BPS)D17:20-
  M5 GPIO17-------10:D6 (TX )    (SDA)D15:19-
                 -11:D8 (Pw4)    (DI4)D16:18-
                 -12:D9 (DO4)    (DI3)D11:17-
           GND----13:D10(M1 )    (DI2)D13:16-
  M5 GND----------14:GND         (DI1)D12:15-
```

## setup()

```cpp
Serial2.setRxBufferSize(512);
Serial2.begin(115200, SERIAL_8N1, 16, 17);
```

シリアルポートの初期化を行っています。

```
setup_screen();
```

LCDスクリーンの初期化を行います。処理の内容はscreen.cにあり、[ターミナル](/master-2/getting-started/using-library/terminal)画面の初期設定を行います。

## loop()

```
check_for_serial();
```

シリアルポートの入力チェックを行います。入力したデータを `the_input_uart` キューにいったん投入します。

```
process_input();
```

シリアルポートから入力データを処理します。ここでは[パーサー](/master-2/getting-started/using-library/parser)に文字列を投入します。パーサーによりシリアル電文が解釈できた場合は、`update_screen()`を呼び出しターミナル画面に文字を更新します。

```
check_for_refresh();
```

LCD画面上のターミナル画面領域を書き換えます。処理の内容はscreen.cにあります。

## update\_screen()

受信したパケットデータを読み取り、画面表示を更新します。

```cpp
  trm << "\033[H"; // カーソルホーム
...
		if (b) trm << "\033[1;1H" // カーソルを１行目に移動
			"\033[1;30;45mﾀｲﾑCT\033[0m" "  " // ボールド、文字色、背景色設定
			"\033[1;30;45mId#\033[0m" " "
			"\033[1;30;45mｼﾘｱﾙ番号\033[0m";
		if (b) trm << "\033[3;1H" // カーソルを３行目に移動
			"\033[1;30;45m D1 \033[0m"
			" \033[1;30;45m D2 \033[0m"
			" \033[1;30;45m D3 \033[0m"
			" \033[1;30;45m D4 \033[0m";
```

この関数内では[エスケープシーケンス](/master-2/references/untitled/tweterm/esc-sequence)を用いて画面の表示位置などを制御しています。

```cpp
		TwePacketTwelite& x = refTwePacketTwelite(spLastPacket);
		if (c) trm << "\033[2;1H"
		   << printfmt("%5d  %3d %8X", 
		      x.u16timestamp, x.u8addr_src, x.u32addr_src);
		if (c) trm << "\033[4;1H " 
			<< (x.DI1 ? "\033[31m" "●" : "\033[35m" "〇") << "\033[0m   "
			<< (x.DI2 ? "\033[32m" "●" : "\033[35m" "〇") << "\033[0m   "
			<< (x.DI3 ? "\033[33m" "●" : "\033[35m" "〇") << "\033[0m   "
			<< (x.DI4 ? "\033[34m" "●" : "\033[35m" "〇") << "\033[0m";
```

パケットデータは`spLastPacket`に格納されています。これの内容を紐解くには[`retTwePacketTwelite()`](/master-2/references/parser/twefmt/packet-types/twepackettwelite#sptwepacketkarano)を呼び出します。xの値を読み出して、これに対応して画面を更新します。


# PAL\_Recv

TWELITE PAL

TWELITE PAL のパケットデータの表示を行います。

![画面例](https://1309285009-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Lz_q8yNGtrx39HyLX6-%2F-M074LC8B8fFOP2Jn_x6%2F-M075t8vg6MDdIkzcYy6%2F20200215-DSC04558.jpg?alt=media\&token=e4b33db8-1ab5-4fac-97f7-aa9275d5230c)

TWELITE PALの親機用ファームウェア(App\_PAL-Parent-BLUE.binまたはApp\_PAL-Parent-RED.bin)をTWELITE DIPにあらかじめ書き込んでおきます。

{% hint style="info" %}
M5Stackに接続する前に書き込んだTWELITE DIPが無線パケットを受信してデータを出力するかPCで確認しておいてください。
{% endhint %}

M5Stack側のTWELITE DIPは、以下のような接続をしておきます。

```
                      TWELITE DIP
                 -01:GND        VCC:28-------M5 3V3
                 -02:D14         D3:27-
  M5 GPIO16-------03:D7          D2:26-
                 -04:D5          D1:25-
                 -05:D18         A2:24-
                 -06:C           D0:23-
                 -07:M           A1:22-
                 -08:D19        RST:21-
                 -09:D4         D17:20-
  M5 GPIO17-------10:D6         D15:19-
                 -11:D8         D16:18-
                 -12:D9         D11:17-
                 -13:D10        D13:16-
  M5 GND----------14:GND        D12:15-
```

## setup()

```cpp
Serial2.setRxBufferSize(512);
Serial2.begin(115200, SERIAL_8N1, 16, 17);
```

シリアルポートの初期化を行っています。

```
setup_screen();
```

LCDスクリーンの初期化を行います。処理の内容はscreen.cにあり、[ターミナル](/master-2/getting-started/using-library/terminal)画面の初期設定を行います。

```
pkt_data.init_screen(fmt_title);
```

パケットデータの管理クラスの初期化を行います。

## loop()

```
check_for_serial();
```

シリアルポートの入力チェックを行います。入力したデータを `the_input_uart` キューにいったん投入します。

```
process_input();
```

シリアルポートから入力データを処理します。ここでは[パーサー](/master-2/getting-started/using-library/parser)に文字列を投入します。パーサーによりシリアル電文が解釈できた場合は、`update_screen()`を呼び出しターミナル画面に文字を更新します。

```
check_for_refresh();
```

LCD画面上のターミナル画面領域を書き換えます。処理の内容はscreen.cにあります。

## pkt\_data

入力データの管理と表示を行う関数です。

このクラスでは、パケットデータに含まれる送信元論理IDごとの[`spTwePacket`](/master-2/references/parser/twefmt/twepacket)データを保存しています。新しいデータを受信したら都度更新します。

画面表示は論理ID１から順に、最後のデータまで１行ずつ表示します。１画面に収まらない場合は、次のページに表示します。

### init\_screen(), reinit\_screen()

```cpp
void init_screen(const char *fmt_status) 
void reinit_screen()
```

`init_screen()`は初回の初期化を行います。`fmt_status`は上部タイトルエリアへの出力文字を指定します。

フォント変更などで画面サイズに変化があった場合は`reinit_screen()`を呼び出します。

### add\_entry()

```cpp
bool add_entry(spTwePacket spobj)
```

受信パケットデータを登録します。内部的にはIDに対応した`spTwePacket`の配列に値を入れます。

### next\_page(), prev\_page(), set\_page()

```cpp
void next_page()
void prev_page()
void set_page(int entry)
```

ページ設定を行います。画面の更新をするため、直後に`update_term()`を呼び出します。

`set_page()`は`entry`に対応する論理IDが含まれるページに移動します。

### update\_status()

```cpp
void update_status()
```

上部タイトルエリアの文字列を再設定します。

### update\_term()

```cpp
void update_term()
void update_term(spTwePacket pal_upd, bool update_all)
```

ターミナル画面の表示文字列の更新を行います。

パラメータなしで呼び出すか、update\_all=trueで呼び出すと、ターミナル全体の再描画を行います。

`pal_upd`を指定し`update_all=false`の場合は、`pal_upd`の表示行のみを更新します。


# Using Library

ライブラリの使用

ソースコード上でライブラリを使用するには `#include <mwm5.h>` をソースコード上に記述します。

```cpp
#include <mwm5.h>

#include <Arduino.h>
#include <M5Stack.h>
```

* Arduino.h, M5Stack.h は mwm5.hより後でインクルードしてください。
* mwm5.hには`using namespace`を用いて、ライブラリ中の名前空間を可視にし省略できる宣言が含まれています。これら名前空間を明示的に記述したい場合は替わりに`#include <twelite.hpp>`を記述してください。

ここでは、以下の解説が含まれます。

* [パーサーライブラリの使用方法](/master-2/getting-started/using-library/parser)
* [ターミナルライブラリの使用方法](/master-2/getting-started/using-library/terminal)


# Parser

シリアル電文パーサーの利用例

TWELITE 無線マイコンから UART (シリアルポート)経由での電文書式を解釈します。電文書式は、可読性・エラー検出などを目的として、伝送したいデータ列に対して所定の変換を行います。

ここではアスキー形式の解釈を行う`AsciiParser`について解説します。

### アスキー形式

アスキー形式は、バイナリで構成されたデータ列を文字列で表現する方法です。TWELITE無線マイコンでは最も良く用いられる形式です。

例えばバイト列で `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) を指定する。                                                                                                                                                                                                      |

## AsciiParser の使用方法

### オブジェクトの生成

```cpp
// serial parser
AsciiParser parse_ascii(256);
```

上記の例では `parser_ascii` というオブジェクトを内部バッファ256バイトで生成しています。このバッファサイズは、書式の解釈後に必要なバイト数です。

{% hint style="info" %}
ASCII形式では実際のバイト数の約２倍の書式になります。例えば書式が200バイトの系列の場合は、実データは約100バイトになります。
{% endhint %}

無線パケットの最大格納バイト数が100バイト強であるため、この例では余裕をもって256バイトのバッファとしています。

### バイト列の解釈

このAsciiParserは１バイトずつの処理を行います。シリアルポートからは１バイトずつデータが到着するためです。

```cpp
while (Serial.available()) {
		int c = Serial.read();
		parse_ascii << char_t(c);
		
		if (parse_ascii) {
		 		// completed!
		}
}
```

上記例では、シリアルポートから１バイト読み出しては`parse_acsii`に１バイトずつ<<演算子を用いて読み込ませています。

直後の`if(pars_ascii)`での判定は、**アスキー形式の系列が正しく解釈できたかどうか**を判定しています。

### データ列の取り出し

```cpp
if (parse_ascii) {
    auto&& p = parse_ascii.get_payload();
}
```

解釈済みの系列は`get_payload()`メソッドにて取得できます。`get_payload()`は[`SmplBuf_Byte`](/master-2/references/basics/tweutils/simplebuffer/smplbuf_byte)の参照型を戻します。

以下のように配列にアクセスできます。

```cpp
auto&& p = parse_ascii.get_payload();

if (p[0] == 0x80) { ... } // 最初の要素
int len = p.length();     // 要素数
for (auto&& x : p) {      // 各要素にアクセス
  Serial.print(x, HEX);
  Serial.print(" "); }
```

## データ列の識別と対応オブジェクトの取得

### パケットの生成

```cpp
auto&& pkt = newTwePacket(parse_ascii.get_payload());

E_PKT pkt_typ = identify_packet_type(pkt);

if (pkt_typ == E_PKT::PKT_PAL) {
  // TWELITE PAL (App_PAL)
} else {
  // unknown packet type
}
```

`newTwePacket()`は入力されたデータ系列を解釈して、`spTwePacket`型のオブジェクトを生成します。このオブジェクトは`TwePacket`型のデータを格納するスマートポインタ`std::shared_ptr`で、メモリ管理を簡素化することができます。

パケット種別は`E_PKT`列挙体で定義されています。ここではTWELITE PALの出力形式`PKT_PAL`の解釈を行います。

{% hint style="info" %}
`std::shared_ptr`の利用は、パケット情報を他にコピーして利用する場合を想定しています。例えば履歴配列に保存して、過去の情報を参照するような場合です。

オブジェクトは参照カウンタにより管理されていて、所有者が0になれば破棄されます。
{% endhint %}

解釈されたパケットの種別は`identify_packet_type()`により判別します。パケット種別は`E_PKT`列挙体で定義されています。

### PALパケットの解釈

```cpp
if (pkt == E_PKT::PKT_PAL) {
    auto&& pal = refTwePacketPal(pkt);
    
    if (pal.u8palpcb == E_PAL_PCB::MAG) {
      // for MAG
    } else
    if (pal.u8palpcb == E_PAL_PCB::AMB) {
      // for AMB
    }
}
```

解釈したパケットが`E_PKT::PKT_PAL`と判定された場合は、`refTwePacketPal()`により`TwePacketPal`型として参照できます。上記コード例ではユニバーサル参照`auto&&`を用いて型名を推測させています。

### PALボード種別解釈

```cpp
if (pal.u8palpcb == E_PAL_PCB::MAG) {
    PalMag mag = pal.get_PalMag();
    
    if (mag.u8MagStat == 0) {
       // closed
    } else {
       // opened
    }
}
```

PALのボード種別に応じたオブジェクトを生成して、ボード種別特有のデータにアクセスすることが出来ます。

上記の例では、MAG(OPEN-CLOSE SENSE PAL)のオブジェクト`mag`を生成しています。ここではオープンクローズに応じた分岐に`mag.u8MagStat`を読み出しています。


# Terminal

ターミナル(コンソール）クラスの利用例

M5stackの320x200ドット液晶に固定幅ターミナルウインドウを表示します。

以下は画面出力の例です。

```cpp
#include <mwm5.h>

#include <Arduino.h>
#include <M5Stack.h>

// ターミナルオブジェクトの生成
TWETerm_M5_Console the_screen(
    64, 20,
    { 0, 16, 320, 192 }, M5);

void setup() {
    // begin M5
    M5.begin(true, false, true, false);  // LCD, SD, Serial, I2C

    // create font as #10 (東雲フォント16)
    createFontShinonome16(10, 0, 0);
    
    the_screen.set_font(10);     // set font #10
		the_screen.set_color(ALMOST_WHITE, M5.Lcd.color565(90, 0, 50);
		                             // set color (fg, bg)
		the_screen.set_cursor(2);    // 0: no 1: curosr 2: blink cursor
		the_screen.force_refresh();  // repaint now.
		
		// initial message
		the_screen << "the_screen:" << crlf;
}

void loop() {
    // refresh screen
    static uint32_t t_last;
    uint32_t t_now = millis();
    
    if (t_now - t_last > 32) {
        the_screen.refresh();
        t_last = t_now;
    }
    
    // press A button
    if (M5.BtnA.wasReleased()) {
        the_screen << "hello world!" << crlf;
    }
    if (M5.BtnB.wasReleased()) {
        // force clear with whole redraw.
        the_screen.clear_screen();
    }
    if (M5.BtnC.wasReleased()) {
        // clear screen and set cursor home.
        the_screen << "\033[2J\[033H";
    }
}
```

まずターミナルオブジェクトを生成します。ここでは最大64カラム、最大20行、左上から (0,16)の位置に(320,192)サイズの領域とします。

```cpp
// ターミナルオブジェクトの生成
TWETerm_M5_Console the_screen(
    64, 20,
    { 0, 16, 320, 192 }, M5);
```

次に「東雲フォント16ドット」をフォントID=10で生成しています。フォントを管理するオブジェクトはライブラリ内部で生成・管理されます。

```cpp
void setup() {
  ...
  // create font as #10 (東雲フォント16)
  createFontShinonome16(10, 0, 0);
```

ターミナルにフォントなどを基本的な設定を行います。フォントを先ほどのフォントID=10として指定し、フォントの文字色と背景色を指定します。カーソルを2(ブリンク表示)とします。最後に`forece_refresh()`にて初期描画を行います。

```cpp
void setup() {
  ...
  the_screen.set_font(10);     // set font #10
  the_screen.set_color(ALMOST_WHITE, color565(90, 0, 50);
                               // set color (fg, bg)
  the_screen.set_cursor(2);    // 0: no 1: curosr 2: blink cursor
  the_screen.force_refresh();  // repaint now.
```

`setup()`が終了したら`loop()`での周期実行中に、一定時間ごとに画面の描画を行います。ここでは32ms以上経過したら再描画するようにしています。

```cpp
void loop() {
    // refresh screen
    static uint32_t t_last;
    uint32_t t_now = millis();
    
    if (t_now - t_last > 32) {
        the_screen.refresh(); // 再描画！
        t_last = t_now;
    }
```

以下の例ではボ タン Aでは"hello world!" + 改行の出力、ボタンBでは画面のクリア、ボタンCも同様に画面のクリア（エスケープシーケンスによる）を行います。

```cpp
void loop() {
    ... 
    
    // press A button
    if (M5.BtnA.wasReleased()) {
        the_screen << "hello world!" << crlf;
    }
    if (M5.BtnB.wasReleased()) {
        // force clear with whole redraw.
        the_screen.clear_screen();
    }
    if (M5.BtnC.wasReleased()) {
        // clear screen and set cursor home.
        the_screen << "\033[2J\[033H";
    }
```


# References


# Parser

シリアル電文パーサー

&#x20;パーサーは、シリアルポートからの電文を解釈し、その内容を読み取ります。

* [TWESERCMD](/master-2/references/parser/twesercmd) - 電文形式を解釈します
* [TWEFMT](/master-2/references/parser/twefmt) - 解釈した電文の内容を読み取ります


# TWESERCMD

書式パーサー

書式パーサーは、IParserを基底クラスとして、書式ごとに派生クラスを実装します。

```
      [IParser]
          ↑
     [AsciiParser]
```

パーサーは、シリアルポートのように１バイトずつ入力されるデバイスを想定し、１バイト単位での解釈を行い、都度状態を遷移する状態遷移マシンとして実装されます。


# IParser

パーサーの基底クラス

パーサーオブジェクトに１バイトずつ電文を投入することで、電文系列を解釈する状態遷移マシンです。

## メソッド

### Parse(), operator << ()

```cpp
IParser& Parse(uint8_t u8b)
IParser& operator << (char_t c)
```

パーサーに１バイト入力します。入力のたびにパーサーの状態が変化し、パーサーの解釈が完了すると`state()`が`E_TWESERCMD_COMPLETE`に変化し解釈完了状態となります。

### state()

```cpp
uint8_t state()
```

パーサーの状態を取得します。

### operator bool(), is\_complete()

```cpp
operator bool()
bool is_complete()
```

パーサーの状態が`E_TWESERCMD_COMPLETE`の場合`true`になります。

### length()

```cpp
uint16_t length()
```

パーサーで解釈済みのバイト列のデータ長を返します。

### operator\[]\()

```cpp
uint8_t operator[](int i)
```

パーサーの解釈済みのバイト列にアクセスします。

### get\_payload()

```cpp
SmplBuf_Byte& get_payload()
```

パーサーの解釈済みのバイト列を格納した配列クラス`SmplBuf_Byte`を参照します。

### reinit()

```cpp
virtual void reinit()
```

パーサーの解釈途中の内容を破棄し、新たな解釈を始めます。

### operator << ()

```cpp
IStreamOut& operator << (TWE::IStreamOut& lhs, IParser& rhs)
IStreamOut& operator << (TWETERM::ITerm& lhs, IParser& rhs)
```

IStreamOutをベースクラスにもつストリームオブジェクトに、書式出力します。

## メソッド

### \_u8Parse()

```cpp
virtual uint8_t _u8Parse(char_t u8b) = 0
```

１バイト入力して解釈を進める仮想関数です。派生クラスにより実装されます。

### \_vOutput()

```cpp
virtual inline void _vOutput(SmplBuf_Byte& bobj, IStreamOut& p) = 0
```

バイト配列`bobj`に格納されるバイト列に対応する書式をストリーム`p`に出力する仮想関数です。派生クラスにより実装されます。

## 状態

| 状態名                          |    値    | 状態                     |
| ---------------------------- | :-----: | ---------------------- |
| `E_TWESERCMD_EMPTY`          |    0    | 解釈前で、まだ系列のヘッダも認識できていない |
|                              | 1..0x7F | 解釈中                    |
| `E_TWESERCMD_COMPLETE`       |   0x80  | 系列が正しく解釈できた            |
| `E_TWESERCMD_ERROR`          |   0x81  | 系列の解釈にエラーがあった          |
| `E_TWESERCMD_CHECKSUM_ERROR` |   0x82  | 系列は得られたがチェックサムエラーだった   |


# AsciiParser

アスキー形式のパーサー

アスキー書式の解釈を行うパーサーですが、`TWESYS::TimeOut`クラスをベースクラスに持つことで、タイムアウト処理を行っています。

## メソッド

### AsciiParser() - コンストラクタ

```cpp
AsciiParser(size_t maxbuffsiz)
AsciiParser(SmplBuf_Byte& bobj)
```

パーサーオブジェクトを生成します。

生成時のパラメータに`maxbuffsiz`を与えると、`maxbuffsiz`をバッファサイズとして動的にメモリ確保して、パーサーを初期化します。

あらかじめ生成された`SmplBuf_Byte`配列`bobj`を参照して、パーサーを初期化することもできます。

### \_u8Parse()

```cpp
uint8_t AsciiParser::_u8Parse(char_t u8byte)
```

アスキー書式の解釈アルゴリズムを実装します。バイトの入力のたびにタイムアウトのチェックを行います。

### \_vOutput()

```cpp
void AsciiParser::_vOutput(TWEUTILS::SmplBuf_Byte& bobj, TWE::IStreamOut& p)
```

書式出力を行います。`s_Output()`メソッドを呼び出します。

### s\_Output(), vPutByte()

```cpp
static void vPutByte(uint8_t u8byte, uint8_t* pu8lrc, TWE::IStreamOut& p)
static void s_vOutput(TWEUTILS::SmplBuf_Byte& bobj, TWE::IStreamOut& p)
```

`vPutByte()`は、ストリームに対して与えられたバイト`u8byte`をアスキー２文字で出力します。例えば0x9Aであれば"9A"という２バイト文字になります。

`s_vOutput()`は、ストリームに対して、与えられたバイト配列`SmplBuf_Byte`のバイト列をアスキー形式で出力します。


# TWEFMT

パケット定義

書式を解釈して得られたデータ列は、受信したパケット情報が含まれます。ここでは、このデータ列のことをパケットデータと呼びます。

パケットデータは[`TwePacket`](/master-2/references/parser/twefmt/twepacket)クラスで表現されます。`TwePacket`クラスのデータを解釈することで、その種別を判定し、各アプリケーションや接続ハードウェアに応じたデータ構造となります。`TwePacket`クラスは基底クラスでさらにパケット種別ごとの派生クラスとなります。

このオブジェクトは無線パケットのデータ量に準じたメモリ領域を消費し、また、アプリケーションでは多数のパケットを保持することも考えられます。メモリ管理を簡略化するため`TwePacket`をスマートポインタ`std:shared_ptr<TwePacket>`にて管理します。このスマートポインタを`spTwePacket`に`typedef`しています。

以下に、TWELITE PALのパケットデータの場合のクラス関係を示します。`TwePacketPal`の基底クラスの一つ`DataPal`はTWELITE PAL特有のデータを格納しています。TWELITE PALには、さらに接続されるセンサーパルによって格納すべきデータが異なります。`TwePacketPal`からさらに`PalAmb`や`PalMot`を生成します。例えば`PalAmb`には温室センサーの値や照度センサーの値が格納されます。この`TwePacketPal`をスマートポインタ`spTwePacket`という形に生成するのが`newTwePacket()`です。

```
    [spTwePacket] ←(生成)- newTwePacket()
          ◆ 
          ｜(shared_ptr)
          ｜
      [TwePacket]    [DataPal]
              ↑        ↑
            [TwePacketPal] -(生成)-> [PalAmb], [PalMot], ...
```

`spTwePacket`型はスマートポインタですので、オブジェクトのコピー渡しによる記述を行うことで、コピーのオーバーヘッドを最小にしつつ、メモリーの管理を自動化できます。以下の例はパケットの履歴を管理する単純なクラスです。

```cpp
struct _pkt_hist {
	spTwePacket _pkt[16]; // パケットの履歴
	int _i;

	_pkt_hist() : _i(-1), _pkt{} {}
	void add_entry(spTwePacket p) { _pkt[++_i & 0xF] = p; }
	spTwePacket get_entry(int i) { return _pkt[(i + _i) & 0xF]; }
} pkt_hist;

void loop() {
  spTwePacket pkt, pkt_last;
  
  // 新たなパケットオブジェクト
  pkt = newTwePacket(p, len);
  pkt_hist.add_entry(pkt); // 履歴に保存する

  // ひとつ前のパケットオブジェクト
  pkt_last = pkt_hist.get_entry(-1);
}
```


# TwePacket, spTwePacket

パケットオブジェクト

パケットデータは種別によってデータ構造が違いますが、様々な種類のパケットを一元管理するための基底クラスです。

```cpp
class TwePacket;
typedef std::shared_ptr<TwePacket> spTwePacket;
```

`spTwePacket` はメモリ管理のためのスマートポインタです。`std::shared_ptr`を用いています。

TwePacketクラスは、パケットデータのパケット種別の管理を行います。また、パケットデータの解釈を行うための仮想関数parse()メソッドを定義しています。パケット特有のデータ構造に基づく解釈やデータの保存等の取り扱いは、派生クラスに実装します。

## メソッド

### TwePacket() - コンストラクタ

```cpp
TwePacket(E_PKT ptyp = E_PKT::PKT_ERROR)
```

デフォルトでは、未解釈状態として `E_PKT::PKT_ERROR` で初期化します。

### \~TwePacket() - デストラクタ

```cpp
virtual ~TwePacket()
```

### get\_type()

```cpp
E_PKT get_type()
```

パケットの種別を[`E_PKT`](/master-2/references/parser/twefmt/twepacket/e_pkt)型で返します。

### parse()

```cpp
virtual E_PKT parse(uint8_t* p, uint8_t u8len)
```

パケットデータのバイト列を与えて、パケットデータを解釈する。

派生クラスで、そのパケットに対応するデータ構造を解釈するための実装を行います。

戻り値は [`E_PKT`](/master-2/references/parser/twefmt/twepacket/e_pkt)型 で、成功時は解釈されたパケット種別を、エラー時に `E_PKT::PKT_ERROR` を返します。


# E\_PKT

パケット種別定義

以下のパケットに対応します。

| 名前           | 解説                                                                                                                |
| ------------ | ----------------------------------------------------------------------------------------------------------------- |
| PKT\_ERROR   | パケット解釈前やパケット種別が特定できないなど、TwePacketには意味のあるデータが格納されていない                                                              |
| PKT\_TWELITE | 標準アプリ App\_Twelite の [0x81 コマンド](https://mono-wireless.com/jp/products/TWE-APPS/App_Twelite/step3-81.html)を解釈したもの |
| PKT\_PAL     | [TWELITE PAL](https://mono-wireless.com/jp/products/TWE-APPS/App_pal/index.html)のシリアル形式を解釈したもの                    |


# idenify\_packet\_type()

パケット種別判定

パケットデータのバイト列を入力として、パケットの種別を判定します。また既に生成済みの[`spTwePacket`](/master-2/references/parser/twefmt/twepacket)オブジェクトの種別を返します。戻り値は[E\_PKT](/master-2/references/parser/twefmt/twepacket/e_pkt)です。

```cpp
E_PKT identify_packet_type(uint8_t* p, uint8_t u8len)
E_PKT identify_packet_type(SmplBuf_Byte& sbuff)

E_PKT identify_packet_type(spTwePacket& sp)
```


# newTwePacket()

パケットデータの解釈とオブジェクト生成

パケットデータのバイト列を入力として、パケット種別の判定と、種別に応じた[`spTwePacket`](/master-2/references/parser/twefmt/twepacket)オブジェクトを生成します。

```cpp
spTwePacket newTwePacket(
		uint8_t* p,
		uint8_t u8len,
		E_PKT eType = E_PKT::PKT_ERROR)

spTwePacket newTwePacket(
		TWEUTILS::SmplBuf_Byte& sbuff,
		E_PKT eType = E_PKT::PKT_ERROR)
```

事前に[`identify_packet_type()`](/master-2/references/parser/twefmt/twepacket/idenify_packet_type)を用いてパケットの種別[`E_PKT`](/master-2/references/parser/twefmt/twepacket/e_pkt)が特定できている場合は`eType`を与えます。

戻り値は[`spTwePacket`](/master-2/references/parser/twefmt/twepacket)です。


# refTwePacket()

spTwePacketオブジェクトの参照

本関数は[`spTwePacket`](/master-2/references/parser/twefmt/twepacket)オブジェクトを[`TwePacket&`](/master-2/references/parser/twefmt/twepacket)として参照します。

```cpp
TwePacket& refTwePacket(spTwePacket& p)

//例
  auto&& pkt = newTwePacket(p, len); // パケットの生成
  
  if (refTwePacket(pkt).get_type() == E_PKT::PKT_TWELITE) {
    // App_Twelite の処理
  }
```

{% hint style="info" %}
この関数は、`->`演算子や`*`演算子を極力使用しない方針でライブラリを設定しているため、スマートポインタの参照を行うために用意しています。

上記の判定式を`(pkt && pkt->get_type() == E_PKT::PKT_TWELITE)`と記述しても同じ判定が得られます。
{% endhint %}


# Packet Types

パケット種別ごとの定義


# TwePacketTwelite

`TwePacketTwelite`クラスは、標準アプリApp\_Tweliteの0x81コマンドを解釈したものです。

```cpp
class TwePacketTwelite : public TwePacket, public DataTwelite { ... };
```

パケットデータ内の諸情報は[`parse()`](/master-2/references/parser/twefmt/twepacket#parse)実行後に[`DataTwelite`](/master-2/references/parser/twefmt/packet-types/twepackettwelite/datatwelite)に格納されます。

### spTwePacketからの参照

```cpp
TwePacketTwelite& refTwePacketTwelite(spTwePacket& p)
```

`spTwePacket`オブジェクトから`TwePacketTwelite`オブジェクトを参照します。`spTwePacket`に`TwePacketTwelite`以外が格納されている場合は、未解釈のオブジェクトを戻します。

### 実行例

```cpp
if (parse_ascii) { // アスキー形式のパーサーの解釈完了
  if (identify_packet_type(parse_ascii.get_payload()) == E_PKT::PKT_TWELITE) {
    auto&& pkt = newTwePacket(parse_ascii.get_payload(), E_PKT::PKT_TWELITE);
    
    if (pkt == E_PKT::PKT_TWELITE) { // パケット解釈成功時
      auto&& x = refTwePacketTwelite(pkt);
      
  		the_screen // LCDスクリーン上のターミナルに情報を表示
  				<< printfmt(":Lq=%d:Ad=%08X", x.u8lqi, x.u32addr_src)
  				<< printfmt(":ID=%02X", x.u8addr_src)
  				<< printfmt(":DI=%04b", x.DI_mask)
  				;
    }
  }
}
```


# DataTwelite

App\_Twelite データ

`TwePacketTwelite`のデータ部分。

```cpp
struct DataTwelite {
		//送信元のシリアル#
		uint32_t u32addr_src;
		
		// 送信元の論理ID
		uint8_t u8addr_src;

		// 宛先の論理ID
		uint8_t u8addr_dst;

		// 送信時のタイムスタンプ
		uint16_t u16timestamp;

		// 低レイテンシ送信時のフラグ
		bool b_lowlatency_tx;

		// リピート中継回数
		uint16_t u8rpt_cnt;

		// LQI値
		uint16_t u8lqi;

		// DIの状態 (true がアクティブ Lo,GND)
		bool DI1, DI2, DI3, DI4;
		// DIの状態ビットマップ (LSBから順にDI1,2,3,4)
		uint8_t DI_mask;

		// DIアクティブならtrue (過去にアクティブになったことがある)
		bool DI1_active, DI2_active, DI3_active, DI4_active;
		// DIのアクティブビットマップ(LSBから順にDI1,2,3,4)
		uint8_t DI_active_mask;

		// モジュールの電源電圧[mV]
		uint16_t u16Volt;

		// AD値 [mV]
		uint16_t u16Adc1, u16Adc2, u16Adc3, u16Adc4;
		// ADがアクティブ（有効）なら 1 になるビットマップ (LSBから順にAD1,2,3,4)
		uint8_t Adc_active_mask;
};
```


# TwePacketPal

TWELITE PALのパケット

`TwePacketPal`クラスは、TWELITE PALのパケットデータを解釈したものです。このクラスはTWELITE PAL（センサーデータなど上り方向）共通に取り扱います。

```cpp
class TwePacketPal : public TwePacket, public DataPal { ... };
```

PAL共通データは[`DataPal`](/master-2/references/parser/twefmt/packet-types/twepacketpal/datapal)に定義されています。

PALの各センサー基板特有のデータを取り出すためのジェネレータ関数を用意しています。

### spTwePacketからの参照

```cpp
TwePacketPal& refTwePacketPal(spTwePacket& p)
```

`spTwePacket`オブジェクトから`TwePacketPal`オブジェクトを参照します。`spTwePacket`に`TwePacketPal`以外が格納されている場合は、未解釈のオブジェクトを戻します。

### 実行例

```cpp
if (parse_ascii) { // アスキー形式のパーサーの解釈完了
  if (identify_packet_type(parse_ascii.get_payload()) == E_PKT::PKT_PAL) {
    auto&& pkt = newTwePacket(parse_ascii.get_payload(), E_PKT::PKT_PAL);
    
    if (pkt == E_PKT::PKT_PAL) { // パケット解釈成功時
      auto&& pal = refTwePacketPal(pkt);
      
      // 開閉センサーパル
      if (pal.u8palpcb == E_PAL_PCB::MAG) {
        PalMag mag = pal.get_PalMag();
        if (mag.u8MagStat == 0) {
          // OPEN
        } else {
          // CLOSE
        }
      }
    }
  }
}
```

## ジェネレータ関数

センサーPALの各種データを取り出すためのジェネレータ関数です。

### get\_PalMag()

```cpp
PalMag get_PalMag()
```

開閉センサーパルのデータ[`PalMag`](/master-2/references/parser/twefmt/packet-types/twepacketpal/palbase/palmag)を取り出します。

### get\_PalAmb()

```
PalAmb get_PalAmb()
```

環境センサーパルのデータ[`PalAmb`](/master-2/references/parser/twefmt/packet-types/twepacketpal/palbase/palamb)を取り出します。

### get\_PalMot()

```
PalMot get_PalMot()
```

動作センサーパルのデータ[`PalMot`](/master-2/references/parser/twefmt/packet-types/twepacketpal/palbase/palmot)を取り出します。


# DataPal

PAL共通データ

PALは接続されるセンサーなどによってパケットデータ構造が異なりますが、`DataPal`では共通部のデータ構造を保持します。

```cpp
struct DataPal {
	uint8_t u8lqi;        // LQI値

	uint32_t u32addr_rpt; // 中継器のアドレス

	uint32_t u32addr_src; // 送信元のアドレス
	uint8_t u8addr_src;   // 送信元の論理アドレス

	uint16_t u16seq;      // シーケンス番号

	E_PAL_PCB u8palpcb;		// PAL基板の種別
	uint8_t u8palpcb_rev;	// PAL基板のレビジョン
	uint8_t u8sensors;		// データに含まれるセンサーデータの数 (MSB=1はエラー)
	uint8_t u8snsdatalen; // センサーデータ長(バイト数), MSB=1は動的確保

	uint8_t au8snsdata[32]; // センサーデータ（解釈前の生データ）
	std::unique_ptr<uint8_t[]> uptr_snsdata; // センサーデータ（動的確保）
};
```

PALのパケットデータ構造は大まかに２つのブロックからなり、全てのPAL共通部と個別のデータ部になります。個別のデータ部は、パケットの解釈を行わずそのまま格納しています。取り扱いを単純化するため32バイトを超えるデータは動的に確保する`uptr_snsdata`に格納します。

個別のデータ部は、PalBaseをベースクラスに持つ構造体に格納されます。この構造体は、TwePacketPalに定義されるジェネレータ関数により生成されます。


# E\_PAL\_PCB

PAL基板種別

下記のPAL基板に対応します。

| 名前    | 解説                              |
| ----- | ------------------------------- |
| NOPCB | 基板未接続、エラー                       |
| MAG   | マグネットセンサー付きのMAG (開閉センサーパル)      |
| AMB   | 温湿度センサー、照度センサー付きのAMB (環境センサーパル) |
| MOT   | 加速度センサー付きのMOT (動作センサーパル)        |


# PalBase

PALセンサー共通データ

PALの各センサーのデータ構造体はすべて`PalBase`を継承します。センサーデータの格納状況`u32StoredMask`とモジュール電源電圧`u16Volt`の情報が含まれます。

```cpp
	struct PalBase {
		uint32_t u32StoredMask;
		uint16_t u16Volt;
	};
```

派生構造体に定義される`STORE_COMP_MASK`と`u32StoreMask`が一致すれば、全てのセンサーのデータが適切に解釈され、格納されていることになります。


# PalMag

開閉センサーパル(MAG)のセンサーデータ

```cpp
struct PalMag : public PalBase {
	const uint8_t U8VARS_CT = 2; // センサー数
	const uint32_t STORE_COMP_MASK = (1 << U8VARS_CT) - 1; // 全コンプのマスク

	uint8_t u8MagStat; // 開閉状況 (0:磁石が遠い, 1/2:磁石検出)
	uint8_t bRegularTransmit; // 定期送信の時に 1, 磁石検出では 0
};
```


# PalAmb

環境センサーパル(AMB)のセンサーデータ

```cpp
struct PalAmb : public PalBase {
	const uint8_t U8VARS_CT = 4; // センサー数
	const uint32_t STORE_COMP_MASK = (1 << U8VARS_CT) - 1; // 全コンプマスク

	int16_t i16Temp;  // 温度 (x100)
	uint16_t u16Humd; // 湿度 (x100 %)
	uint32_t u32Lumi; // 照度 (lux相当)
};
```


# PalMot

動作センサーパル(MOT)のセンサーデータ

```cpp
struct PalMot : public PalBase {
	const uint8_t U8VARS_CT = 17; // センサー数
	const uint32_t STORE_COMP_MASK = 3; // 電圧と加速度サンプル１あればコンプとする

	uint8_t u8samples; // 格納サンプル数
	int16_t i16X[16];  // X軸 (ミリG)
	int16_t i16Y[16];  // Y軸
	int16_t i16Z[16];  // Z軸
};
```

※ 各サンプルの連続性を確認するには、パケットのシーケンス番号の抜けが無いことを確認してください。


# Terminal

ターミナル（コンソール）

本ライブラリのターミナル（コンソール）は、文字列ベースの画面を構成することを目的としてます。

以下に設計時の考慮事項を記載します。

* 固定幅の画面構成を行うこと
* 日本語の表示が可能であること
* ソースコード中に直接日本語文字列を含められるようにすること
  * UTF-8でソースコードを記述する前提とする
  * 内部処理をUnicodeとすること
  * UTF-8デコードが出来るようにすること
* 旧来のキャラクター型のインタフェースを実装できるよう、いくつかのエスケープシーケンスを実装しておくこと
  * ただしANSIエスケープシーケンスの完全な互換性を目的とはしない
* 文字色、背景色、太字といった表示属性に対応すること
* カーソルを表示・非表示にできること
* 画面の順方向のスクロールに対応すること
* カラム数を超えて文字列を出力した場合は、折り返しを行えること
  * 右端カラムへの文字出力を行った場合、その時点では折り返しを行わないようにすること
    * 上記を実装は、折り返し処理の実装より優先すること
* 複数のターミナル表示を画面上に同時に表示できること
* 毎回全画面書き換えといったような描画パフォーマンスの悪い実装でないこと
  * 変更がある行のみを書き換える行単位の描画を行うようにした
* フォントを選択できること
  * 配布可能なフォントをライブラリ内に同梱しておくこと
  * より大きな文字を表示するため、倍角表示に対応すること

本ライブラリのターミナルは、大まかに分けて２要素から構成されます。

* ターミナル画面上の文字列を管理するクラスや関数をまとめた [TWETERM](/master-2/references/untitled/tweterm)
* フォントの管理と描画に関連するクラスや関数をまとめた [TWEFONT](/master-2/references/untitled/twefont)


# TWETERM

ターミナル(コンソール)

`namesace TWETERM`はターミナル（コンソール）画面を実装するためのクラスや関数などをまとめています。

基本的なクラス構造は以下のようになっています。クラス[`ITerm`](/master-2/references/untitled/tweterm/iterm)ではターミナルの文字列バッファとその処理、[`TWETerm_M5_Console`](/master-2/references/untitled/tweterm/tweterm_m5_console)クラスはITermの文字列描画部分を実装したものです。[IStreamOut](/master-2/references/basics/twe/untitled)はITerm派生クラスオブジェクトが文字列をターミナルに投入するための基本的な手続きを提供しています。

```
    [IStreamOut]
         ↑
      [ITerm]
         ↑
 [TWETerm_M5_Console]
```


# ESC Sequence

エスケープシーケンス

エスケープシーケンスは ESC 文字 '\033' で始まり何文字かで完結する制御コードです。

本ライブラリが動作するプログラム上や、シリアルポートの先にあるマイコンから制御文字をターミナルに投入することで、様々な画面制御（画面のクリア、カーソルの移動、色などの表示属性の変更）を行うことが出来ます。

以下に対応するエスケープシーケンスを記述します。表中の ESC はエスケープ文字 '\033'、イタリックの *n* や *m* は数字の入力です。

| エスケープシーケンス                       | 意味                                                                                                                                         |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| ESC \[ *n*  A                    | カーソルを*n*行上に移動する。（*n*省略時は1行）                                                                                                                |
| ESC \[ *n* B                     | カーソルを*n*行下に移動する。（*n*省略時は1行）                                                                                                                |
| ESC \[ *n* C                     | カーソルを*n*列右に移動する。（*n*省略時は1列）                                                                                                                |
| ESC \[ *n* D                     | カーソルを*n*列左に移動する。（*n*省略時は1列）                                                                                                                |
| ESC \[ *n* G                     | カーソルのカラム*n*の位置に移動する (*n*省略時は1カラム目＝行頭)                                                                                                      |
| ESC \[ *n* ; *m* H               | カーソル位置を行*n*列*m*に移動する。先頭位置の場合は1を指定します。*n*;*m*を省略した場合は左上ホームポジションにカーソルを移動します。                                                                 |
| ESC \[ n ; *m* f                 | ESC \[ *n* ; *m* Hに同じ。                                                                                                                     |
| ESC \[ 2 J                       | 画面をクリアしてカーソルをホームポジションに移動する。                                                                                                                |
| ESC \[ *n* K                     | <p><em>n</em>=0 または省略 カーソル行より後ろを削除する</p><p><em>n</em>=1 カーソル行より前を削除する</p><p><em>n</em>=2 行全体を削除する</p>                                      |
| ESC \[ *n1* ; *n2* ; ...; *n4* m | <p>描画属性の設定を行う。<em>n1</em> .. <em>n4</em> は任意数指定できる。</p><p>1 → 太字</p><p>7 → 反転</p><p>30 .. 37 → 文字色</p><p>40 .. 47 → 背景色</p><p>0 → 属性抹消</p> |

{% hint style="warning" %}
`ITerm`で対応するエスケープシーケンスは、ANSIターミナル互換を目的としたものではありません。解釈や仕様には違いがあります。
{% endhint %}


# TermAttr

`ITerm`型のターミナルオブジェクトに対して、`<<`演算子を用いてターミナル属性を指定するヘルパークラス。

```cpp
void putmsg(ITerm& trm, const char *msg) {
    trm << TermAttr(TERM_COLOR_FG_RED | TERM_BOLD);
    trm << msg;
    trm << TermAttr(TERM_ATTR_OFF);
}
```

上記の例では、出力ターミナルを`trm`に対して、文字色を赤で太字にして`msg`を出力し、属性をクリアします。

### TermAttr() - コンストラクタ

```cpp
TermAttr(GChar::tAttr attr = 0)
```

attrをパラメータとします。attrは後述の**GChar::tAttr 定数***一覧*で指定します。

指定は以下の組み合わせになります。

* TERM\_ATTR\_OFF (すべての属性のクリア)
* 以下の論理和
  * TERM\_BOLD
  * TERM\_REVERSE
  * 文字色のいずれか
  * 背景色のいずれか

## GChar::tAttr 定数

#### 色設定以外の属性

| 設定                    | 意味             |
| --------------------- | -------------- |
| TERM\_ATTR\_OFF = 0x0 | すべての属性をクリアする   |
| TERM\_BOLD            | 文字を太字にする       |
| TERM\_REVERSE         | 背景色と文字色を反転表示する |

#### 文字色

| 設定                       | 意味   |
| ------------------------ | ---- |
| TERM\_COLOR\_FG\_BLACK   | 黒    |
| TERM\_COLOR\_FG\_RED     | 赤    |
| TERM\_COLOR\_FG\_GREEN   | 緑    |
| TERM\_COLOR\_FG\_YELLOW  | 黄    |
| TERM\_COLOR\_FG\_BLUE    | 青    |
| TERM\_COLOR\_FG\_MAGENTA | マゼンタ |
| TERM\_COLOR\_FG\_CYAN    | シアン  |
| TERM\_COLOR\_FG\_WHITE   | 白    |

#### 背景色

| 設定                       | 意味   |
| ------------------------ | ---- |
| TERM\_COLOR\_BG\_BLACK   | 黒    |
| TERM\_COLOR\_BG\_RED     | 赤    |
| TERM\_COLOR\_BG\_GREEN   | 緑    |
| TERM\_COLOR\_BG\_YELLOW  | 黄    |
| TERM\_COLOR\_BG\_BLUE    | 青    |
| TERM\_COLOR\_BG\_MAGENTA | マゼンタ |
| TERM\_COLOR\_BG\_CYAN    | シアン  |
| TERM\_COLOR\_BG\_WHITE   | 白    |


# ITerm

ターミナル用文字バッファ管理クラス

&#x20;ターミナル（コンソール）の基底クラスで、画面上の文字列を管理する。このクラスは、実際の画面描画についての手続きは含まれず、このクラスを継承したサブクラスによって画面描画を実装します。

## メソッド

### ITerm() - コンストラクタ

```cpp
ITerm(uint8_t u8c, uint8_t u8l)
ITerm(uint8_t u8c, uint8_t u8l, SimpBuf_GChar* pAryLines, GChar* pBuff)
```

カラム数`u8c`と行数`u8l`を指定して、ターミナルを構築する。カラム数と行数はターミナルで管理できる最大の値を指定する。ターミナルのサイズ変更を行った場合でも各々の最大の値を超える変更は行われない。

`pAryLines`と`pBuff`を指定する場合は、ITerm内でのメモリ確保は行われず、外部で確保済みの配列を利用する。

```cpp
// 静的メモリ確保例
const uint8_t U8COL = 64;
const uint8_t U8LINE = 20;
static TWETERM::GChar screen_buf[U8COL * U8LINE];   // pBuff
static TWETERM::SimpBuf_GChar screen_lines[U8LINE]; // pAryLines
```

### \~ITrem() - デストラクタ

```cpp
virtual ~ITerm()
```

動的にメモリを確保した場合は、そのメモリ領域を破棄します。

### clear(), home(), clear\_screen()

```cpp
void clear()
void home()
void clear_screen()
```

`clear()`は画面バッファのクリア、`home()`はカーソル位置をホームポジションに移動、`clear_screen()`は両者を実行します。

### refresh(), force\_refresh()

```cpp
virtual void refresh() = 0
void force_refresh()
```

サブクラスにより実装される画面更新描画のためのメソッドです。描画方法は２種類あり、メンバー変数`u32Dirty`に定義されるビットマスクに対応した行のみを再描画するものと、`force_refresh()`メソッドによる画面全体を再描画するものがあります。

画面全体の再描画では、いったん背景を背景色で塗りつぶしてから再描画します。初回の描画では`force_refresh()`を行うようにしてください。

### write()

```cpp
ITerm& write(wchar_t c)
ITerm& write(char_t c)
```

ターミナルに１文字書き出します。カーソル位置に文字を書き出します。16bit `wchar_t`型のUnicodeを渡します。

`char_t` (char) 型のパラメータを渡した場合は、入力をUTF-8として取り扱います。例えば0x7F までのASCII文字はそのまま`write(wchar_t)`が呼び出され、３バイトのUTF-8エンコードされた日本語文字は、連続して３バイトを投入した時点で`write(wchar_t)`が呼び出されます。

{% hint style="info" %}
日本語文字セットを表示するためのフォントの取り扱いについては[TWEFONT::FontDef](/master-2/references/untitled/twefont/fontdef)を参照してください。
{% endhint %}

### get\_height(),  get\_width()

```cpp
uint8_t get_height() // 行数
uint8_t get_width()  // カラム数
```

ターミナルの行数、カラム数を返す。

### operator <<

```cpp
ITerm& operator << (const char *s)
TWE::IStreamOut& operator << (TWE::IStreamSpecial& sc)
TWE::IStreamOut& operator << (ITerm& t, char_t c)
TWE::IStreamOut& operator << (ITerm& t, wchar_t c)
TWE::IStreamOut& operator << (ITerm& t, int i)
TWE::IStreamOut& operator << (ITerm& t, TermAttr a)
TWE::IStreamOut& operator << (TWE::IStreamOut& t, TermAttr a)
```

`<<`演算子を用いてターミナルに文字列を書き出します。

| パラメータ              | 意味                                  |
| ------------------ | ----------------------------------- |
| const char \*s     | 文字列 s をターミナルに書き出す (UTF-8のデコードを行います) |
| IStreamSpecial& sc | crlf （改行）など特殊文字を出力する                |
| char\_t c          | 文字 c  をターミナルに書き出す (UTF-8のでコードを行います) |
| wchar\_t c         | 文字 c (Unicode)をターミナルに書き出す           |
| int i              | printf("%d", i) に該当する出力を行います        |
| TermAttr a         | 文字属性を設定します                          |

## メソッド (サブクラス実装用)

| メソッド名                  | 解説                                                                                                                                                                                                                                                     |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| post\_refresh()        | サブクラスでのrefresh()実装で、最後に呼び出す必要があります。必要な変数の初期化を行います。                                                                                                                                                                                                     |
| resize\_screen()       | 指定されたカラム数・行数に従い、バッファを再構成します。コンストラクタ指定の初期値を超えた指定はできません。                                                                                                                                                                                                 |
| column\_idx\_to\_vis() | <p><code>uint16\_t column\_idx\_to\_vis(int16\_t idx , int16\_t lin)</code><br>Unicodeで管理されている画面バッファー上の行位置・カラム位置（ともに0が先頭位置)から、画面上のカラム位置を計算します。日本語文字のような２文字幅文字を２カラムとして計算します。 "abcあいう"という文字列が格納された行のカラム位置4は"い"の文字が格納されますが、本関数で画面上のカラム位置を計算すると5になります。</p> |
| column\_vis\_to\_idx() | <p>column\_vis\_to\_idx(int16\_t c\_vis, int16\_t lin)</p><p>画面上の行位置、カラム位置（ともに0が先頭位置)から、画面バッファー上のカラム位置を計算します。日本語文字のような２文字幅文字を２カラムとして計算します。 "abcあいう"という文字列が格納された行の画面上のカラム位置5または6は"い"の文字が格納されますが、本関数で画面バッファー上のカラム位置を計算すると4になります。</p>                     |
|                        |                                                                                                                                                                                                                                                        |


# TWETerm\_M5\_Console

M5Stack用のLcd描画ターミナル

M5Stack の 320x240 LCD 用のターミナルの実装です。[`ITerm`](/master-2/references/untitled/tweterm/iterm)を実装しています。

本クラスは`namespace TWEARD`内に定義されます。

## メソッド

### TWETerm\_M5\_Console  - コンストラクタ

```cpp
TWETerm_M5_Console(
			uint8_t u8c, uint8_t u8l,
			Rect drawArea, M5Stack& _M5)

TWETerm_M5_Console(
			uint8_t u8c, uint8_t u8l,
			TWETERM::SimpBuf_GChar* pAryLines, TWETERM::GChar* pBuff,
			Rect drawArea, M5Stack& _M5)
```

[`ITerm`のコンストラクタ](/master-2/references/untitled/tweterm/iterm#iterm-konsutorakuta)に`drawArea`と`_M5`のパラメータが追加されています。

`drawArea`は、LCD内のターミナル描画エリアを決めます。`Rect`構造体で指定しx,y,w,hを指定します。(x,y)は領域の左上の座標、(w,h)は領域の幅と高さです。

`_M5`は、M5Stackのグローバルインスタンス `M5`を指定します。

#### 例：

```cpp
TWETerm_M5_Console the_screen(64, 20, { 0, 16, 320, 192 }, M5);
```

カラム最大値を64、行数の最大値を20、左上座標を (0, 16)、領域サイズを (320, 192) として `the_screen` オブジェクトを構築します。

## メソッド

### refresh()

`ITerm::refresh()`の実装です。この関数により画面の描画を行います。`loop()`関数内で定期的に呼び出します。

本実装では、パフォーマンスの向上のため、原則として描画変更の必要にある行のみを上書きします。画面全領域を再描画したい場合は`force_refresh()`メソッドを呼び出します。

以下の例では32msごとに描画を行います。

```cpp
void loop() {
		static uint32_t u32mills;

		uint32_t u32now = millis();
		if (u32now - u32mills > 32) {
			the_screen.refresh();

			u32mills = u32now;
		}
}
```

### set\_font()

```cpp
void set_font(uint8_t u8id, uint8_t u8col_request = 0, uint8_t u8row_request = 0)
```

フォントを指定します。

`u8id`はフォントIDを指定します。

`u8col_request`は、設定したいカラム数を指定します。指定した数値が領域サイズに対して大きい場合は指定領域に入るように値が丸められます。0を指定した場合は、領域サイズから計算できる最大のカラム数に設定されます。

`u8row_request`は、設定したい行数を指定します。指定した数値が領域サイズに対して大きい場合は指定領域に入るように値が丸められます。0を指定した場合は、領域サイズから計算できる最大の行数に設定されます。

### font\_width(), font\_height(), font\_id()

```cpp
uint8_t font_id()
uint8_t font_width()
uint8_t font_height()
```

`font_id()`は、指定したフォントのIDを返します。

`font_width()`は、指定したフォントの幅をピクセル数で返します。ダブル幅のピクセル数は、この値の２倍になります。

`font_height()`は、指定したフォントの高さをピクセル数で返します。

### set\_color()

```cpp
void set_color(uint16_t color, uint16_t bgcolor = 0)
```

ターミナルの文字色と背景色を指定します。

`color`は文字色を指定します。

`bgcolor`は背景色を指定します。

{% hint style="info" %}
色は565形式の16bit値です。`TWEARD::color565()`関数で計算します。

```cpp
uint16_t c = color565(255, 127, 0); // R:255, G:127, B:0
```

{% endhint %}

{% hint style="warning" %}
白色は `ALMOST_WHITE` で指定します。color565(255,255,255) または WHITE を指定すると描画が崩れます。
{% endhint %}

### set\_color\_table()

```cpp
void set_color_table(const uint16_t* ptbl)
```

ターミナルで使用できる８色のテーブルを指定します。`ptbl`は`uint16_t`型の配列で８つの要素が必要です。

#### 例

```cpp
static const uint16_t COLTBL_MAIN[8] = {
	BLACK,
	RED,
	GREEN,
	YELLOW,
	color565(127, 127, 255), // BLUE,
	color565(255, 0, 142), // MAGENTA,
	CYAN,
	ALMOST_WHITE
};

the_screen.set_color_table(COLTBL_MAIN);
```

上記の例では青とマゼンダの色調を変えたテーブルを指定し、ターミナルオブジェクト `the_screen`に指定しています。

## ユーティリティ関数

### color565()

```cpp
constexpr uint16_t color565(uint8_t r, uint8_t g, uint8_t b)
```

`r, g, b` を指定して、565形式の色コードを生成します。


# TWEFONT

フォント定義や描画

`namespace TWEFONT` には、フォントの定義やフォント描画のための手続きをまとめています。

{% hint style="warning" %}
このフォントライブラリは M5Stack 標準のライブラリのフォントには準じていないため M5Stackでのフォント描画APIなどで使用することが出来ません。
{% endhint %}

## フォントの生成

フォントは`FontDef`クラスにより管理されます。フォントごとに用意される`FontDef`クラスオブジェクトのジェネレータ関数により生成され、ライブラリ内部で生成時に指定したフォントIDと紐づけて管理されます。フォントは最大７つまで定義できます。フォント作成時に字間・行間・倍角を指定することができます。同じフォントに対して複数のフォントIDの登録が可能です。

下記の例では、フォントID 10 に16ドットの東雲フォント（縦倍角・横倍角指定）を、フォントIDを11に同じフォントですが倍角指定なし、行間を１ピクセルとしたフォント定義を行います。

```cpp
TWEFONT::createFontShinonome16(10, 0, 0,
        TWEFONT::U32_OPT_FONT_TATEBAI | TWEFONT::U32_OPT_FONT_YOKOBAI);
TWEFONT::createFontShinonome16(11, 1, 0);
```

コンパイル時に、ジェネレータ関数`createFont???()`を呼び出されたフォントのデータがリンクされます。

{% hint style="info" %}
フォントの登録した種類だけROM容量が必要になります。最小限のフォントを選択するようにしてください。
{% endhint %}

## フォントデータについて

本ライブラリには、作者が事実上パブリックドメイン（著作権等取扱はソースヘッダに含まれるクレジットを参照ください）を宣言しているフォントをいくつか含めています。

本ライブラリに含めるにあたって、以下の調整を行っています。

* 大本がBDF形式を変換し、描画ルーチンに適したデータ構造とした
* これらフォントをUnicodeとして取り扱うための参照テーブルを用意した
* latin1補助文字 U+00A0～u+00FFについて、フォント定義があるものについては収録した
* JIS X201 半角カナ U+FF61～U+FF9F について、フォント定義があるものについては収録した
* 常用漢字（2645 文字）のフォントデータと、全収録（東雲フォントのみ、一部未収録字形があります）を用意した

### 東雲フォント (12,14,16ドット)

```
●東雲フォントライセンス
																	2001
									 The Electronic Font Open Laboratory
										http://openlab.ring.gr.jp/efont/

このアーカイブに含まれるすべてのフォントデータ、ドキュメント、スクリプ
ト類はすべて Public Domain で提供されています 。

但し、日本に於いては現時点で著作権を放棄することは法律上不可能であり、
AUTHORS に列挙されている作者がその権利を行使しないと宣言することで実質
的な Public Domain であるとします。

自由な改造、他フォーマットへの変換、組込み、再配布を行うことができます。
同時に、これらはすべて完全に無保証です。
```

12,14,16ドット版をライブラリに含めています。

常用漢字のみのジェネレータ (`createFontShinonome12()`, `createFontShinonome12()` , `createFontShinonome16()`)または、全収録のジェネレータ (`createFontShinonome12_full()`) ,`createFontShinonome14_full()` ,`createFontShinonome16_full()` )を呼び出します。

{% hint style="info" %}
全収録版は字形データが多いためより多くのROM容量が必要です。同じフォントサイズのデータで常用版と全収録版の両方を登録する意味はありません。常用漢字版の字形データは全収録版に包含されるためです。
{% endhint %}

### MP+フォント (10ドット, 12ドット)

```
 M+ BITMAP FONTS            Copyright 2002-2005  COZ <coz@users.sourceforge.jp>
 -

 LICENSE




 These fonts are free softwares.
 Unlimited permission is granted to use, copy, and distribute it, with
 or without modification, either commercially and noncommercially.
 THESE FONTS ARE PROVIDED "AS IS" WITHOUT WARRANTY.
```

ジェネレータ `createFontMP10()`または `createFontMP12()` を呼び出します。

### 8x6 LCD フォント

ジェネレータ `createFontLcd8x6()` を呼び出します。

latin拡張文字や日本語フォントは含まれません。

このフォントはいずれかのジェネレータ関数`createFont???()`が呼び出されたときに、デフォルトとしてフォントID=0に登録されます。

## フォントIDについて

フォントIDは作成したフォントごとに割り当てられます。

IDは 0..32 の値を指定可能ですが、ユーザが登録できるのは 1..32 で最大７フォント登録できます。

ID=0 のフォントは 8x6 LCD フォントに割り当てられます。

## フォント情報の取得

フォント情報にアクセスするためには、[`queryFont()`](/master-2/references/untitled/twefont/queryfont)により[`FontDef`](/master-2/references/untitled/twefont/fontdef)オブジェクトを取得し、諸情報を得ます。

```cpp
auto&& font = queryFont(10); // ID10のFontDefオブジェクトを参照
TWE::fPrintf(the_screen, "\nFont: %s\n      ID=%d H:%d W:%d W_CHRs:%d",
			font.font_name, // フォント名
			font.get_font_code(), // フォントID (=10)
			font.height, // フォントデータの高さ（行間は含まれない）
			font.width, // フォントデータの幅（シングル幅、文字間スペースは含まれない）
			font.font_wide_count // ダブル幅の登録文字数
			);
```

## ターミナルでの使用

ターミナルにフォントを指定するには、フォントの生成を行い、フォントIDをターミナルオブジェクトに指定します。フォント指定後は[`force_refresh()`](/master-2/references/untitled/tweterm/iterm#refresh-force_refresh)メソッドによる再描画を行います。

```cpp
// ターミナル the_screen の宣言
TWETerm_M5_Console the_screen(64, 20, { 0, 16, 320, 192 }, M5);

void setup() {
    // ... M5の初期化など
    M5.begin(true, false, true, false);
    
    // フォントの生成と登録
    createFontMP12(13, 0, 0);
    the_screen.set_font(13); // フォントIDの指定
    the_screen.force_refresh(); // 再描画
}
```

ターミナルのフォント変更は、[`set_font()`](/master-2/references/untitled/tweterm/tweterm_m5_console#set_font)によりフォントを指定し、その後、[`clear_screen()`](/master-2/references/untitled/tweterm/iterm#clear-home-clear_screen)と[`force_refresh()`](/master-2/references/untitled/tweterm/iterm#refresh-force_refresh)を呼び出します。

```cpp
the_screen.set_font(11);    // フォントの指定
the_screen.clear_screen();  // ターミナルを文字バッファをクリアする
the_screen.force_refresh(); // 完全再描画を行う
```

## フォントの描画について

[`drawChar()`](/master-2/references/untitled/twefont/drawchar)関数を用いて描画することができます。


# FontDef

フォント定義クラス

フォント定義と関連する手続きをまとめたクラスです。

このクラスオブジェクトはフォントジェネレータ[`createFont???()`](/master-2/references/untitled/twefont/createfont)によりライブラリ内部で生成・管理され、ユーザがコンストラクタを用いて直接オブジェクトを生成することはありません。

## メソッド（ユーザ利用）

### get\_width()

```cpp
uint8_t get_width()
uint8_t get_width(uint16_t wc)
```

パラメータを省略した場合は、フォントのシングル幅文字の幅を返します。この値にはフォント生成時に指定した文字間スペースも含まれます。

`wc`を指定した場合は、Unicode `wc`に対応するフォントの幅を返します。日本語などダブル幅のフォントの場合は、シングル幅の２倍の値が戻ります。

### get\_height()

```cpp
uint8_t get_height()
```

フォントの高さを返します。この値にはフォント生成時に指定した行間スペースも含まれます。

### is\_default()

```cpp
bool is_default()
```

デフォルトフォントのオブジェクトである場合 `true` を返します。

{% hint style="info" %}
デフォルトフォントのオブジェクトは[`createFont???()`](/master-2/references/untitled/twefont/createfont)や[`queryFont()`](/master-2/references/untitled/twefont/queryfont)のエラー時など例外時にも利用されます。
{% endhint %}

### get\_font\_code()

```cpp
uint8_t get_font_code()
```

フォントIDを取得します。

## メソッド（内部利用）

### find\_font\_index()

```cpp
int find_font_index(uint16_t c)
```

Unicode `c` に対応する、ダブル幅フォント定義配列インデックスを検索します。

戻り値は、字形データが存在する場合は、インデックス配列のインデックス（データ配列のインデックスが計算できる）、存在しない場合は`-1`を返します。

フォント定義は、インデックス配列、データ配列の２つから構成されます。インデックス配列の各値は昇順に並んだ Unicode になっていて、データ配列のインデックスに対応しています。

以下の例ではインデックス配列の IDX=829 が U+5a2f で "娯" という文字です。データ配列の対応する番地を参照すれば、この字形データが格納されています。

```cpp
// インデックス配列 M+10dotフォント
const uint16_t font_mplus_f10j_idx[FONT_MP10_DBL_CHARS] {
  0x0391, // IDX=0 U+0391
  0x0392, // IDX=1 U+0392
  0x0393, // IDX=2 U+0392
  ...
  0x5a2f, // IDX=892 U+5a2f (娯)
  ...
}

// データ配列
const uint8_t font_mplus_f10j_data[FONT_MP10_DBL_CHARS*FONT_MP10_DATA_ROWS*2] = {
  ...
  0x00,0x00,0x47,0x80,0x54,0x80,0xf7,0x80,0x50,0x00,
　  0x5f,0x80,0xa0,0x80,0xff,0x80,0x45,0x00,0x58,0x80, // 3864/u5a2f idx=892
  ...
};

```

インデックス配列内の値は昇順に並ぶよう構成した目的は、本関数で実装されている二分探索を利用するためです。


# createFont???()

フォントジェネレータ関数

フォントジェネレータ関数は、収録フォントごとに定義されています。関数パラメータは共通で、以下のようになります。

```cpp
const FontDef& createFontShinonome16(
    uint8_t id,
    uint8_t line_space = 0,
    uint8_t char_space = 0,
    uint32_t u32Opt = 0);
```

上記は東雲フォント16ドット版（常用漢字収録）のジェネレータの例です。

`id`は、ユーザが指定するフォントID。

`line_space`はフォントの行間スペースをピクセル数で指定します。

`char_space`は文字間スペースでピクセルで指定します。文字間スペースはシングル幅のフォントの指定です。ダブル幅のフォントの場合は倍になります。

`u32Opt`は、フォントのオプションを指定します。オプションは`U32_OPT_FONT_TATEBAI`と`U32_OPT_FONT_YOKOBAI`があり、論理和で指定します。

フォントジェネレータの戻り値は`FontDef&`になっています。この戻り値はライブラリ内部のフォント管理テーブルに生成されたオブジェクトへの参照です。既に登録済みのIDであるばあいは、そのIDに対して上書きを行います。登録できなかった場合は`.is_default()`メソッドが`true`になるデフォルトフォントが返されます。

```cpp
if (createFontShinonome16(11).is_default()) {
    // フォント作成のエラー
}
```


# drawChar()

フォントの描画

フォントをスクリーン上に描画します。

以下の例では、Bボタンを押すたびに、事前に生成したフォントID=10のフォントを用いて固定の文字列を描画します。

```cpp
void loop() {
	if (M5.BtnB.wasReleased()) {
		static int idx = 0;
		const char msg[3][16] = {
		   	"ABCD1234", "あいうえ", "やあ世界" };
		
		auto&& font = queryFont(10); // use font ID=10
		
		drawChar(
			font,         // フォント指定
			0, 240 - 30,  // 左上座標 (X,Y)
			msg[idx],     // 文字列
			RED,          // 文字色
			BLACK,        // 背景色
			0x01);        // オプション 0x01:BOLD
		
		idx++; if (idx >= 3) idx = 0;
	}
}
```

## 関数定義

```cpp
int16_t drawChar(const FontDef& font, int32_t x, int32_t y, 
    uint16_t c, uint32_t color, uint32_t bg, uint8_t opt);
    
int16_t drawChar(const FontDef& font, int32_t x, int32_t y,
    const char *s, uint32_t color, uint32_t bg, uint8_t opt);
    
int16_t drawChar(const FontDef& font, int32_t x, int32_t y,
    const uint16_t* s, uint32_t color, uint32_t bg, uint8_t opt);
```

`font`を用い、左上座標(`x`,`y`)に、文字色`fg`、背景色`bg`、オプション`opt`で文字を描画します。

`uint16_t c`をパラメターとして与えた場合は、Unicode `c` に対応する文字を１文字描画します。

`const char *s`をパラメータとして与えた場合は、`s`をUTF-8としてデコードし、文字列として出力します。

`const uint16_t* s`をパラメータとして与えた場合は、Unicode文字列として描画します。

`opt`はオプションのビットマップです。以下の指定が可能です。

* `0x01` - 太字指定
* `0x02` - カーソルの描画

戻り値は、描画が行われればX(幅)方向に描画したピクセル数を返し、エラーなどが発生したときは0を返します。

## 実装について

実装時では以下のM5StackのAPIを利用しています。

* `M5.Lcd.startWrite()`
* `M5.Lcd.setWindow()`
* `M5.Lcd.endWrite()`
* `tft_Write_16()`


# queryFont()

フォントクラスオブジェクトの取得

[`createFont???()`](/master-2/references/untitled/twefont/createfont) によりフォント生成したクラスオブジェクトを参照します。

## 関数定義

```cpp
const FontDef& queryFont(uint8_t id)
```

idにはフォント生成時に指定したフォントIDを指定します。

戻り値は [`FontDef&`](/master-2/references/untitled/twefont/fontdef) で、ライブラリ内のフォント管理配列内に格納されたオブジェクトへの参照です。

idに0を指定すると、フォントID=0のデフォルトフォントを戻します。存在しないIDを指定した場合も、どうようにデフォルトフォントを戻します。`FontDef`オブジェクトがデフォルトフォントかどうかはメソッド`.is_default()`により判定できます。


# Basics

汎用クラス・関数


# TWE

基本クラス、関数


# IStreamOut

本クラスは `namespace TWE` 内に定義されています。

出力ストリームの基底クラスで、以下のメソッドが定義されており、１バイトの出力、改行文字など特殊クラスを受け付けるためのメソッドが定義されています。

```cpp
virtual inline IStreamOut& operator ()(char_t c) = 0
virtual inline IStreamOut& write_w(wchar_t c)
```

`operator ()` は`char_t`型の１文字を出力するための仮想関数です。`write_w()`は`wchar_t`型の出力に対応します。

ストリームへの出力は`<<`演算子を用います。最終的には上記の出力用の関数が呼び出されます。

以下の例はITermクラスでの実装例です。

```cpp
class ITerm : public TWE::IStreamOut {
  // ... 各種定義
  
  ITerm& write(char_t c) {
    // ... １バイト入力を受け付ける処理
  }
  
  // StreamOut::operator () の実装
  TWE::IStreamOut& operator ()(char_t c) {
    write(c);
    return *this;
  }
};
```

## operator <<

```
inline IStreamOut& operator << (char_t c)
inline IStreamOut& operator << (wchar_t c)
inline IStreamOut& operator << (IStreamSpecial& sc)
```

operator << の右オペランドとして以下の型に対応します。

| 右オペランドの型          | 解説                           |
| ----------------- | ---------------------------- |
| `char_t`          | １バイト出力する                     |
| `const char *`    | 文字列を出力する                     |
| `wchar_t`         | ワイド文字を出力する（派生クラスで対応がある場合）    |
| `IStreamSpecial&` | 特殊文字列を出力する                   |
| `printfmt`        | printf()に相当する出力を行う           |
| `const int`       | printf("%d", *n*)に相当する出力を行う  |
| `double`          | printf("%.3%, *n*)に相当する出力を行う |

{% hint style="info" %}
曖昧性の解決のため、派生クラスで明示的なオーバーライドが必要になる場合があります。

```cpp
inline ITerm& operator << (ITerm& t, char_t c)
   { *static_cast<TWE::IStreamOut*>(&t) << c; return t; } 
```

{% endhint %}

## IStreamSpecial

特殊な文字列などを指定するためのオブジェクトを定義するための基底クラスです。

```cpp
class IStreamSpecial {
	public:
		virtual inline IStreamOut& operator ()(IStreamOut& of) = 0;
};
```

派生クラスとして CR LF (0x0d 0x0a) を出力する IStream\_endl が定義されています。

```cpp
class IStream_endl : public IStreamSpecial {
  inline IStreamOut& operator ()(IStreamOut& of) { 
    // ... CR LF の出力
    of ('\r');
    of ('\n');
  }
};

IStream_endl crlf;
```

オブジェクト`crlf`は以下のように使用します。

```cpp
the_screen << "Hello World!" << crlf;
```

##


# Print Formatted

printfmt, fPrintf(), snPrintf()

printf, sprintfに対応する処理を行います。

{% hint style="info" %}
本ライブラリではMacro Poland氏のprintf,sprintfライブラリを利用しています。\
<https://github.com/mpaland/printf>
{% endhint %}

## printfmt

ストリーム[`IStreamOut`](/master-2/references/basics/twe/untitled)オブジェクトに対して`<<`演算子の右オペランドとして利用します。

```cpp
printfmt(const char*fmt, ...)
ISteramOut& operator << (IStreamOut& strm, printfmt f)

//例
  the_screen << printfmt("%04X", 0x12ab) << crlf;
```

`printfmt`クラスのコストラクタのパラメータの１番目`fmt`に書式を指定します。以降のパラメータはC++テンプレートのパラメータパックで実装されており可変数引数となっています。printfのように書式に対応した引数を指定します。printfと違い引数の数は最大４つに制限されます。

## fPrintf()

ストリーム[`IStreamOut`](/master-2/references/basics/twe/untitled)オブジェクトを出力先としてfprintfと同じ処理を行います。

```cpp
int fPrintf(TWE::IStreamOut& fp, const char* format, ...)
```

１番目の引数がストリームオブジェクトとなる点を除きfprintfと同じ使い方です。

## snPrintf()

snprintfの処理を行います。

```cpp
int snPrintf(char* buffer, size_t count, const char* format, ...)
```


# TWEUTILS

ユーティリティ関数


# FixedQueue

固定長キュー

std::queueは、ブロック単位でのメモリ確保を行い動的にキューのサイズを拡張していきますが、本クラスではメモリサイズを抑制し固定長のキューを実装します。

```cpp
template<typename T>
class FixedQueue : public std::queue<T> { ... };
```

本クラスは`std::queue`を継承しています。

## バッファのブロックサイズ

GCCの場合はバッファのブロックサイズを定義することができます。

本ライブラリでの利用を想定し以下のように64バイトにしています。

```cpp
#if __GNUC__
#define _GLIBCXX_DEQUE_BUF_SIZE 64
     // smaller chunk for deque in GCC, the dafalt size
     // 512bytes are too big for embedded systems.
#endif

#include <deque>
#include <queue>
```

## メソッド

### FixedQueue() - コンストラクタ

```cpp
FixedQueue(std::size_t size)
```

`size`を最大値としてキューを生成します。

### push()

```cpp
bool push(T value)
```

キューに要素を追加します。キューが一杯になると何もせず`false`を返します。

### pop()

```cpp
void pop()
```

キューから要素を削除します。

### front()

```cpp
T& std::queue<T>::front()
```

要素にアクセスします。

### empty()

```cpp
bool std::queue<T>::empty()
```

キューが空の場合`true`を返します。


# SimpleBuffer

固定バッファ長の配列クラス

可変長の配列ですが、最大長は固定の配列クラスです。

```cpp
template <class T>
class SimpleBuffer { ... };
```

配列のメモリは、外部の固定バッファを参照する方法と、内部にコストラクタで確保する方法の２種類があります。

{% hint style="warning" %}
外部のバッファを参照する場合、参照先のメモリを安全に利用できるようユーザプログラムで管理しなければなりません。
{% endhint %}

## メソッド

### SimpleBuffer() - コストラクタ

```cpp
SimpleBuffer()
SimpleBuffer(T* p, uint16_t u16len, uint16_t u16maxlen)
SimpleBuffer(uint16_t u16maxlen)
```

パラメータなしのコンストラクタは、バッファ未登録として初期化します。バッファが未登録の場合は`length_max()`が0になります。このオブジェクトを配列として利用するには`attach()`メソッドにより改めてバッファを登録する必要があります。

外部のバッファを参照する場合は、バッファへのポインタ`p`、配列の初期長`u16len`、配列の最大長`u16maxlen`を指定します。

メモリを動的確保するには`u16maxlen`のみを指定するコンストラクタを呼び出します。

### 代入演算子、コピーコンストラクタ

```cpp
SimpleBuffer(const SimpleBuffer<T>& ref)
SimpleBuffer<T>& operator = (const SimpleBuffer<T>& ref)
```

コピー元が、外部メモリ参照の場合は、コピー元の参照先をそのままコピーします。

コピー元が、内部にメモリ確保している場合は、コピー元のメモリ領域をそのまま利用します。新たにメモリのコピーを作成するわけではありません。本クラスでの内部確保したメモリは、スマートポインタ`shared_ptr`で管理されます。コピー先とコピー元すべてのオブジェクトが破棄された時点で、メモリ領域を破棄します。

### attach()

```cpp
void attach(T* p, uint16_t l, uint16_t lm)
```

配列の参照先を再設定します。

{% hint style="warning" %}
内部メモリ確保のオブジェクトの場合、内部メモリのスマートポインタを破棄しません。

一時的に部分配列として取り扱うといった使い方を想定します。以下の例では、128バイトの長さの`buf`を生成した後に、先頭17バイト目から末尾までの部分配列`buf_sub`を生成しています。

```cpp
SimpleBuffer<uint8_t> buf(128), buf_sub;

buf_sub = buf;
buf_sub.attach(buf.begin() + 16, 0, buf.max_length() - 16);
```

{% endhint %}

### begin(), end()

```cpp
T* begin()
T* end()
```

配列の先頭ポインタ、末尾＋１のアドレスのポインタを返します。STLのアルゴリズムや範囲for文などで利用されます。

配列バッファの先頭ポインタを得るときに`begin()`を用います。

### append()

⇒`push_back()`

### push\_back()

```cpp
bool append(T c)
void push_back(T c) { append(c); }
```

配列の末尾に要素を追加します。要素が追加できないときはfalseが戻ります。

### length(), length\_max()

⇒`size(), capacity()`

### size(), capacity()

```cpp
uint16_t length()
uint16_t size() { return length(); }

uint16_t length_max()
uint16_t capacity() { return length_max(); }
```

`size()`は配列の長さ、`capacity()`は配列の最大長を返します。

### redim()

⇒`reserve()`

### reserve()

```cpp
bool redim(uint16_t len)
bool reserve(uint16_t len) { return redim(len); }
```

配列の長さを変更します。現在の長さより大きくなる場合は要素型`T`のデフォルトの初期化方法`T{}`にて初期化されます。数値型なら0です。

### operator \[]

```cpp
inline T& operator [] (int i)
```

配列へのアクセス手段を提供します。

インデックス`i`は、負の値の場合は配列の末尾からのインデックスとなります。`-1`が末尾になります。


# SmplBuf\_Byte

バイト配列

```cpp
typedef SimpleBuffer<uint8_t> SmplBuf_Byte;
```

`uint8_t`型の[`SimpleBuffer`](/master-2/references/basics/tweutils/simplebuffer)です。


# The MWM5 Library / TWELITE STAGE

ライブラリについて

{% hint style="success" %}
[資料の取り扱いについて](https://twelite.gitbook.io/general/about_documents)をご参照ください。  お気付きの点がありましたら、当サポート窓口にご連絡いただければ幸いです。
{% endhint %}

{% hint style="warning" %}
本資料は MWM5  バージョン 0.8 に対応します。
{% endhint %}

本ライブラリMWM5は、M5StackとTWELITE をシリアル(UART)接続して、M5Stack上でTWELITEを利用することを目的としています。またライブラリを活用した [TWELITE STAGE](https://mono-wireless.com/jp/products/stage/) (開発環境) のソースコードも含まれています。

* TWELITEシリアルデータ形式のパーサーライブラリ
* ターミナル画面描画を行うライブラリ
* Windows/Linux/macOS向けウインドウ描画ライブラリ
* TWELITE STAGE (TWELITE 開発・評価環境)

![標準アプリ App\_Twelite のパケットデータ表示例](https://488903175-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Lz_q8yNGtrx39HyLX6-%2F-M074LC8B8fFOP2Jn_x6%2F-M075t8ubyfn1uXj0amX%2F20200215-DSC04564.jpg?alt=media\&token=c218e972-0cdb-431f-8f3d-d822311a3b47)

![TWELITE PALのパケット表示例](https://488903175-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Lz_q8yNGtrx39HyLX6-%2F-M074LC8B8fFOP2Jn_x6%2F-M075t8wcG5R0TPwoRpa%2F20200215-DSC04557.jpg?alt=media\&token=8944bebe-58d5-4ed4-8296-b0aaccd0677a)


# Changes & Download

変更履歴、ダウンロード

## 0.8.9 - 2020-05-29

> <https://github.com/monowireless/mwm5/releases/tag/0.8.9>

* ウインドウアイコンの追加
* BINファイル一覧画面での最大リスト数の制約を緩和 (win/linux/mac)
* Glancerビューアの追加
* 解説文面等の調整
* コンソール画面の描画の調整
* ファーム書き込み後の移動先画面(インタラクティブモードかターミナルか）の設定が動作していなかった
* Alt(or Cmd)+W の割り当てを変更
* その他不具合の修正

## 0.8.6 - 2020-04-27

> <https://github.com/monowireless/mwm5/releases/tag/0.8.6>

* TWELITE STAGE (MWSDK) 2020-04 linux版に収録

## 0.8.5 - 2020-04-24

> <https://github.com/monowireless/mwm5/releases/tag/0.8.5>

* TWELITE STAGE (MWSDK) 2020-04 に収録
* TWELITE STAGE アプリを追加
* Windows10, macOS, Linux 用のビルド対応&#x20;
* 設定ライブラリ twesettings を追加

## 0.4.2 - 2020-02-17

> <https://github.com/monowireless/mwm5/releases/tag/0.4.2>

初期バージョン


# License

ライセンス

## ソフトウェアのライセンス

本パッケージ内で、ライセンス上特別な記述のないものは、モノワイヤレスオープンソースソフトウェア使用許諾契約書を適用します。詳細はパッケージ付属の文書を参照してください。

本ソフトウェアについては、モノワイヤレス株式会社が正式にサポートを行うものではありません。お問い合わせにはご回答できない場合もございます。予めご了承ください。

不具合などのご報告に対してモノワイヤレス株式会社は、修正や改善をお約束するものではありません。

また導入パッケージなどお客様の環境に依存して動作しない場合もございます。

```cpp
/* Copyright (C) 2020 Mono Wireless Inc. All Rights Reserved.  *
 * Released under MW-OSSLA-*J,*E (MONO WIRELESS OPEN SOURCE    *
 * SOFTWARE LICENSE AGREEMENT).                                */
```

### MW-OSSLA 概要

{% hint style="warning" %}
ライセンスの定義はライセンス文書に従います。また本概要はライセンス文書の解釈に影響を与えるものでありません。
{% endhint %}

* 無保証です
* MW-OSSLA下で改変や再配布が可能です
* 商用利用を目的としない場合は、モノワイヤレス株式会社の製品を利用しない場合であっても、本ソフトウェアを利用することができます
* 商用利用を目的とする場合は、[モノワイヤレスソフトウェア使用許諾契約書](https://mono-wireless.com/jp/products/TWE-NET/license.html)(MW-SLA)を添付したソースコードを配布し、ML-SLA下のソフトウェアを利用してください
  * MW-OSSLA下のソフトウェアに対し、MW-SLAを添付してソフトウェアを配布することが出来ます
    * 公開したり当社への連絡の必要はありませんが、手続きとして一旦MW-SLAを添付したソフトウェアとして配布してください
  * MW-SLAを添付したソフトウェアは、商用・非商用にかかわりなく、モノワイヤレス株式会社の製品上で利用しなければなりません
* src/twesettings には MW-SLA と MW-OSSLA のデュアルライセンスが適用されます。

## 本ドキュメントの利用について

* 本ドキュメントは無保証です
* 本ドキュメントの著作権はモノワイヤレス株式会社が有します
* 本ドキュメントはライブラリの一部として取り扱い、MW-OSSLA下で取り扱われます


# Getting started


# Environment

環境

以下の環境で開発しています。

動作等の不都合がある場合は、下記の環境を参考にしてください。

| 環境             | 内容                  |
| -------------- | ------------------- |
| OS             | Windows10 バージョン1903 |
| M5Stack ハードウェア | M5stack BASIC       |
| M5Stackライブラリ   | 0.2.9               |


# Building

ビルド（コンパイル）について

## ESP32(M5Stack)

開発環境は以下のリンクを参照ください。

> M5Stack Library - <https://github.com/m5stack/M5Stack>

最初に上記ライブラリに含まれるサンプルスケッチをビルド、書き込みを行ってください。

### MWM5ライブラリのインストール

MWM5ライブラリは <https://github.com/monowireless/mwm5> で配布しています。ソースファイル一式をダウンロードしてください。本ドキュメント記載時点に一致するバージョンは[変更履歴](/0.8/changes)に記載があります。

変更履歴にある zip ファイルをダウンロードして、開発環境の指定するlibraryディレクトリに展開するか、開発環境の「zip形式のライブラリのインストール」を行ってください。アンインストールはライブラリディレクトリを削除します。

### 外部ライブラリ

* PS2Keyboard - [ http://www.pjrc.com/teensy/td\_libs\_PS2Keyboard.html](< http://www.pjrc.com/teensy/td_libs_PS2Keyboard.html>)\
  esp32/PS2Keyboard.7z をライブラリディレクトリに展開してください。ビルド時に必要になります。\
  PS2Keyboard プロジェクトの成果物に対して 1) 日本語レイアウトのキーボード対応 2) カーソルキー等の取り扱いの微調整を実施しています。修正内容はソースコードを参照ください。

## Windows10

Windows10ではVisual Studio 2019でビルドを行っています。また MingW32 用のビルド定義も含めています。

#### MingW64

以下が必要になります。

* make
* gcc9 (gcc-9, g++-9)

## macOS

以下が必要になります。

* make\
  XCodeに付随します。OS バージョンにもよりますが、ターミナルで make と入力すればインストーラーが起動します。
* gcc9 (gcc-9, g++-9) \
  homebrew のパッケージをダウンロードして利用します。

{% hint style="info" %}
macOS 10.15 (Catalina) では、clangを用いることも可能です。オプション`OSX_COMPILERTYPE=clang`を追加してmakeしてください。

※ C++17 の filesystem ライブラリがバージョン依存になります。10.15以前ではビルドできず、またビルドしたバイナリを10.14以前で動作させることもできません。
{% endhint %}

## Linux

以下が必要になります。

* gcc9 (gcc-9, g++-9)
* SDL2 開発者向けライブラリ (`libsdl2-dev`)\
  SDL2 のライブラリやヘッダファイルは、ソースパッケージに添付していますが、依存ライブラリのリンクに必要です。

## ビルド方法

### M5Stack

以下を参考にしてください。

> M5Stack Library - <https://github.com/m5stack/M5Stack>

### Windows Visual Studio

1. プロジェクト MWM5.sln を開きます。
2. Sketch＞TWELITE\_Stage をスタートアッププロジェクトにします。\
   必須ではありませんが、デバッガを起動する際などにこのプロジェクトが選択されます。
3. Debug/Release のいずれかを選択します。
4. 32bit/64bit のいずれかを選択します。
5. Sketch＞TWELITE\_Stage を右クリックしてビルドを選択します。
6. 実行形式は msc\Release msc\Debug などのディレクトリに格納されます。

### macOS, Linux, Windows (MingW64)

1. コマンドプロンプト、シェルを開きます。
2. make, gcc-9, g++-9 が動作することを確認します。
3. {ソースコードを展開したディレクトリ}`/examples/`{ビルドしたいプロジェクト:例 TWELITE\_Stage}`/build` に移動します。
4. make を実行します。

#### make オプション

| オプション                   | 内容                             |
| ----------------------- | ------------------------------ |
| DEBUG\_BUILD=1          | デバッグビルドを行います。デバッガを使用する場合に使います。 |
| OSX\_COMPILERTYPE=clang | clang でビルドします (OS X のみ)        |

#### make ファイルについて

make のルール本体は {ソースコードを展開したディレクトリ}`/mkfiles` 以下に格納しています。

| ファイル名          | 内容               |
| -------------- | ---------------- |
| mwm5.mk        | コンパイルするファイルなどの定義 |
| rules.mk       | コンパイルのルール        |
| arch.mk        | 実行OSの判定          |
| arch\_linux.mk | Linux用の諸定義       |
| arch\_mac.mk   | macOS用の諸定義       |
| arch\_win.mk   | MingW64用の諸定義     |

## デバッグについて

VSCode用のTWELITE STAGE 用のデバッガ起動定義(`.vscode/launch.json`)を含めています。環境に合わせて利用ください。

#### 参考OS X用の定義

```
{
    "name": "(lldb/mac) TWELITE_Stage",
    "type": "cppdbg",
    "request": "launch",
    "program": "${workspaceFolder}/examples/TWELITE_Stage/build/TWELITE_Stage-debug.command",
    "args": [],
    "stopAtEntry": true,
    // "cwd": "${workspaceFolder}",
    "cwd": "${workspaceFolder}/../MWSDK",
    "environment": [],
    "externalConsole": false,
    "MIMode": "lldb"
}
```

* `stopAtEntry`は、`main()`の先頭で停止する設定で false でも構わない。
* `externalConsole`は、`false`でないと起動しない。
* `cwd`は、TWELITE STAGEの場合 MWSDKディレクトリを指定する。


# Hardware

ハードウェア接続

## Windows, macOS, Linux

MONOSTICK または TWELITE-R と TWELITE モジュールを接続し、必要に応じて各OS向けのデバイスドライバの設定を行います。

参考: [TWELITE STAGE の解説](https://stage.twelite.info/)

## ESP32 (M5Stack)

TWELITE DIPとの接続例です。シリアルポート(UART)のTX,RXのみのシンプルな接続です。この接続では TWELITE 上のファームウェアの書き込みなどが行えません。

```
[M5Stack 15P]

    GPIO03
    GPIO01
    GPIO16 --- TWELITE DIO6/UART0 TX (TWELITE DIP #10)
    GPIO17 --- TWELITE DIO7/UART0 RX (TWELITE DIP #3)
    GPIO02
    GPIO05
    GPIO25
    GPIO26
    GPIO35
    GPIO36
       RST
       BAT
       3V3 --- TWELITE VCC (TWELITE DIP #28)
        5V
       GND --- TWELITE GND (TWELITE DIP #1,14)
```

以下の配線をすることで PGM/RESET/SET ピンが接続されます。TWELITE STAGEアプリケーションを利用する場合は、下記の配線を行ってください。

{% hint style="warning" %}
ハードウェアの接続構成によっては、結線してはいけない場合もあります。ハードウェアの接続については M5Stack の資料も参考の上、十分注意して行ってください。

例： M5Stack faces の GPIO05 ピンは、キーパッドの割り込みピン（出力）が接続されているため、M5Stack側で出力制御を行うことはできません（故障の原因になりえます）。TWELITE STAGEアプリケーションでは、キーパッドに対応するI2Cデバイスを検出した場合は、出力制御しないようにしています。
{% endhint %}

```
[M5Stack 15P]

    GPIO03
    GPIO01
    GPIO16 --- TWELITE DIO6/UART0 TX (TWELITE DIP #10)
    GPIO17 --- TWELITE DIO7/UART0 RX (TWELITE DIP #3)
    GPIO02 --- TWELITE PGM   (TWELITE DIP #7)
    GPIO05 --- TWELITE DIO12 (TWELITE DIP #15)
    GPIO25
    GPIO26 --- TWELITE RESET (TWELITE DIP #21)
    GPIO35
    GPIO36
       RST
       BAT
       3V3 --- TWELITE VCC (TWELITE DIP #28)
        5V
       GND --- TWELITE GND (TWELITE DIP #1,14)
```


# Examples

サンプルコード


# AppTwelite\_Recv

標準アプリケーション

TWELITE DIPに工場出荷時に書き込まれている標準アプリ(App\_Twelite)のシリアルメッセージを解釈してLCD画面上に表示します。

![画面例](https://488903175-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Lz_q8yNGtrx39HyLX6-%2F-M074LC8B8fFOP2Jn_x6%2F-M075t8ubyfn1uXj0amX%2F20200215-DSC04564.jpg?alt=media\&token=c218e972-0cdb-431f-8f3d-d822311a3b47)

{% hint style="info" %}
M5stickに接続する前に書き込んだTWELITE DIPが無線パケットを受信してデータを出力するかPCで確認しておいてください。
{% endhint %}

M5Stack側のTWELITE DIPは、以下のような接続をしておきます。以下の配線はTWELITE DIPを親機設定(M1=GND)とした配線をしています。AI1-4はアナログポートがオープンになり不定な入力を防ぐためです(App\_TweliteではVCCレベルを入力した場合、そのポートを未使用とする意味を持たせています）

```
                          TWELITE DIP
                 -01:GND              VCC:28-------M5 3V3
                 -02:D14(SCL)    (M3 ) D3:27-
  M5 GPIO16-------03:D7 (RX )    (M2 ) D2:26-
                 -04:D5 (Pw1)    (AI4) D1:25----VCC
                 -05:D18(DO1)    (AI3) A2:24----VCC
                 -06:C  (Pw2)    (AI2) D0:23----VCC
                 -07:M  (Pw3)    (AI1) A1:22----VCC
                 -08:D19(DO2)         RST:21-
                 -09:D4 (DO3)    (BPS)D17:20-
  M5 GPIO17-------10:D6 (TX )    (SDA)D15:19-
                 -11:D8 (Pw4)    (DI4)D16:18-
                 -12:D9 (DO4)    (DI3)D11:17-
           GND----13:D10(M1 )    (DI2)D13:16-
  M5 GND----------14:GND         (DI1)D12:15-
```

## setup()

```cpp
Serial2.setRxBufferSize(512);
Serial2.begin(115200, SERIAL_8N1, 16, 17);
```

シリアルポートの初期化を行っています。

```
setup_screen();
```

LCDスクリーンの初期化を行います。処理の内容はscreen.cにあり、[ターミナル](/0.8/getting-started/using-library/terminal)画面の初期設定を行います。

## loop()

```
check_for_serial();
```

シリアルポートの入力チェックを行います。入力したデータを `the_input_uart` キューにいったん投入します。

```
process_input();
```

シリアルポートから入力データを処理します。ここでは[パーサー](/0.8/getting-started/using-library/parser)に文字列を投入します。パーサーによりシリアル電文が解釈できた場合は、`update_screen()`を呼び出しターミナル画面に文字を更新します。

```
check_for_refresh();
```

LCD画面上のターミナル画面領域を書き換えます。処理の内容はscreen.cにあります。

## update\_screen()

受信したパケットデータを読み取り、画面表示を更新します。

```cpp
  trm << "\033[H"; // カーソルホーム
...
		if (b) trm << "\033[1;1H" // カーソルを１行目に移動
			"\033[1;30;45mﾀｲﾑCT\033[0m" "  " // ボールド、文字色、背景色設定
			"\033[1;30;45mId#\033[0m" " "
			"\033[1;30;45mｼﾘｱﾙ番号\033[0m";
		if (b) trm << "\033[3;1H" // カーソルを３行目に移動
			"\033[1;30;45m D1 \033[0m"
			" \033[1;30;45m D2 \033[0m"
			" \033[1;30;45m D3 \033[0m"
			" \033[1;30;45m D4 \033[0m";
```

この関数内では[エスケープシーケンス](/0.8/references/untitled/tweterm/esc-sequence)を用いて画面の表示位置などを制御しています。

```cpp
		TwePacketTwelite& x = refTwePacketTwelite(spLastPacket);
		if (c) trm << "\033[2;1H"
		   << printfmt("%5d  %3d %8X", 
		      x.u16timestamp, x.u8addr_src, x.u32addr_src);
		if (c) trm << "\033[4;1H " 
			<< (x.DI1 ? "\033[31m" "●" : "\033[35m" "〇") << "\033[0m   "
			<< (x.DI2 ? "\033[32m" "●" : "\033[35m" "〇") << "\033[0m   "
			<< (x.DI3 ? "\033[33m" "●" : "\033[35m" "〇") << "\033[0m   "
			<< (x.DI4 ? "\033[34m" "●" : "\033[35m" "〇") << "\033[0m";
```

パケットデータは`spLastPacket`に格納されています。これの内容を紐解くには[`retTwePacketTwelite()`](/0.8/references/parser/twefmt/packet-types/twepackettwelite#sptwepacketkarano)を呼び出します。xの値を読み出して、これに対応して画面を更新します。


# PAL\_Recv

TWELITE PAL

TWELITE PAL のパケットデータの表示を行います。

![画面例](https://488903175-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Lz_q8yNGtrx39HyLX6-%2F-M074LC8B8fFOP2Jn_x6%2F-M075t8vg6MDdIkzcYy6%2F20200215-DSC04558.jpg?alt=media\&token=e4b33db8-1ab5-4fac-97f7-aa9275d5230c)

TWELITE PALの親機用ファームウェア(App\_PAL-Parent-BLUE.binまたはApp\_PAL-Parent-RED.bin)をTWELITE DIPにあらかじめ書き込んでおきます。

{% hint style="info" %}
M5Stackに接続する前に書き込んだTWELITE DIPが無線パケットを受信してデータを出力するかPCで確認しておいてください。
{% endhint %}

M5Stack側のTWELITE DIPは、以下のような接続をしておきます。

```
                      TWELITE DIP
                 -01:GND        VCC:28-------M5 3V3
                 -02:D14         D3:27-
  M5 GPIO16-------03:D7          D2:26-
                 -04:D5          D1:25-
                 -05:D18         A2:24-
                 -06:C           D0:23-
                 -07:M           A1:22-
                 -08:D19        RST:21-
                 -09:D4         D17:20-
  M5 GPIO17-------10:D6         D15:19-
                 -11:D8         D16:18-
                 -12:D9         D11:17-
                 -13:D10        D13:16-
  M5 GND----------14:GND        D12:15-
```

## setup()

```cpp
Serial2.setRxBufferSize(512);
Serial2.begin(115200, SERIAL_8N1, 16, 17);
```

シリアルポートの初期化を行っています。

```
setup_screen();
```

LCDスクリーンの初期化を行います。処理の内容はscreen.cにあり、[ターミナル](/0.8/getting-started/using-library/terminal)画面の初期設定を行います。

```
pkt_data.init_screen(fmt_title);
```

パケットデータの管理クラスの初期化を行います。

## loop()

```
check_for_serial();
```

シリアルポートの入力チェックを行います。入力したデータを `the_input_uart` キューにいったん投入します。

```
process_input();
```

シリアルポートから入力データを処理します。ここでは[パーサー](/0.8/getting-started/using-library/parser)に文字列を投入します。パーサーによりシリアル電文が解釈できた場合は、`update_screen()`を呼び出しターミナル画面に文字を更新します。

```
check_for_refresh();
```

LCD画面上のターミナル画面領域を書き換えます。処理の内容はscreen.cにあります。

## pkt\_data

入力データの管理と表示を行う関数です。

このクラスでは、パケットデータに含まれる送信元論理IDごとの[`spTwePacket`](/0.8/references/parser/twefmt/twepacket)データを保存しています。新しいデータを受信したら都度更新します。

画面表示は論理ID１から順に、最後のデータまで１行ずつ表示します。１画面に収まらない場合は、次のページに表示します。

### init\_screen(), reinit\_screen()

```cpp
void init_screen(const char *fmt_status) 
void reinit_screen()
```

`init_screen()`は初回の初期化を行います。`fmt_status`は上部タイトルエリアへの出力文字を指定します。

フォント変更などで画面サイズに変化があった場合は`reinit_screen()`を呼び出します。

### add\_entry()

```cpp
bool add_entry(spTwePacket spobj)
```

受信パケットデータを登録します。内部的にはIDに対応した`spTwePacket`の配列に値を入れます。

### next\_page(), prev\_page(), set\_page()

```cpp
void next_page()
void prev_page()
void set_page(int entry)
```

ページ設定を行います。画面の更新をするため、直後に`update_term()`を呼び出します。

`set_page()`は`entry`に対応する論理IDが含まれるページに移動します。

### update\_status()

```cpp
void update_status()
```

上部タイトルエリアの文字列を再設定します。

### update\_term()

```cpp
void update_term()
void update_term(spTwePacket pal_upd, bool update_all)
```

ターミナル画面の表示文字列の更新を行います。

パラメータなしで呼び出すか、update\_all=trueで呼び出すと、ターミナル全体の再描画を行います。

`pal_upd`を指定し`update_all=false`の場合は、`pal_upd`の表示行のみを更新します。


# Using Library

ライブラリの使用

ソースコード上でライブラリを使用するには `#include <mwm5.h>` をソースコード上に記述します。

```cpp
#include <mwm5.h>
#include <M5Stack.h>
```

* Arduino.h, M5Stack.h は mwm5.hより後でインクルードしてください。
* mwm5.hには`using namespace`を用いて、ライブラリ中の名前空間を可視にし省略できる宣言が含まれています。これら名前空間を明示的に記述したい場合は替わりに`#include <twelite.hpp>`を記述してください。

ここでは、以下の解説が含まれます。

* [パーサーライブラリの使用方法](/0.8/getting-started/using-library/parser)
* [ターミナルライブラリの使用方法](/0.8/getting-started/using-library/terminal)


# Parser

シリアル電文パーサーの利用例

TWELITE 無線マイコンから UART (シリアルポート)経由での電文書式を解釈します。電文書式は、可読性・エラー検出などを目的として、伝送したいデータ列に対して所定の変換を行います。

ここではアスキー形式の解釈を行う`AsciiParser`について解説します。

### アスキー形式

アスキー形式は、バイナリで構成されたデータ列を文字列で表現する方法です。TWELITE無線マイコンでは最も良く用いられる形式です。

例えばバイト列で `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) を指定する。                                                                                                                                                                                                      |

## AsciiParser の使用方法

### オブジェクトの生成

```cpp
// serial parser
AsciiParser parse_ascii(256);
```

上記の例では `parser_ascii` というオブジェクトを内部バッファ256バイトで生成しています。このバッファサイズは、書式の解釈後に必要なバイト数です。

{% hint style="info" %}
ASCII形式では実際のバイト数の約２倍の書式になります。例えば書式が200バイトの系列の場合は、実データは約100バイトになります。
{% endhint %}

無線パケットの最大格納バイト数が100バイト強であるため、この例では余裕をもって256バイトのバッファとしています。

### バイト列の解釈

このAsciiParserは１バイトずつの処理を行います。シリアルポートからは１バイトずつデータが到着するためです。

```cpp
while (Serial.available()) {
		int c = Serial.read();
		parse_ascii << char_t(c);
		
		if (parse_ascii) {
		 		// completed!
		}
}
```

上記例では、シリアルポートから１バイト読み出しては`parse_acsii`に１バイトずつ<<演算子を用いて読み込ませています。

直後の`if(pars_ascii)`での判定は、**アスキー形式の系列が正しく解釈できたかどうか**を判定しています。

### データ列の取り出し

```cpp
if (parse_ascii) {
    auto&& p = parse_ascii.get_payload();
}
```

解釈済みの系列は`get_payload()`メソッドにて取得できます。`get_payload()`は[`SmplBuf_Byte`](/0.8/references/basics/tweutils/simplebuffer/smplbuf_byte)の参照型を戻します。

以下のように配列にアクセスできます。

```cpp
auto&& p = parse_ascii.get_payload();

if (p[0] == 0x80) { ... } // 最初の要素
int len = p.length();     // 要素数
for (auto&& x : p) {      // 各要素にアクセス
  Serial.print(x, HEX);
  Serial.print(" "); }
```

## データ列の識別と対応オブジェクトの取得

### パケットの生成

```cpp
auto&& pkt = newTwePacket(parse_ascii.get_payload());

E_PKT pkt_typ = identify_packet_type(pkt);

if (pkt_typ == E_PKT::PKT_PAL) {
  // TWELITE PAL (App_PAL)
} else {
  // unknown packet type
}
```

`newTwePacket()`は入力されたデータ系列を解釈して、`spTwePacket`型のオブジェクトを生成します。このオブジェクトは`TwePacket`型のデータを格納するスマートポインタ`std::shared_ptr`で、メモリ管理を簡素化することができます。

パケット種別は`E_PKT`列挙体で定義されています。ここではTWELITE PALの出力形式`PKT_PAL`の解釈を行います。

{% hint style="info" %}
`std::shared_ptr`の利用は、パケット情報を他にコピーして利用する場合を想定しています。例えば履歴配列に保存して、過去の情報を参照するような場合です。

オブジェクトは参照カウンタにより管理されていて、所有者が0になれば破棄されます。
{% endhint %}

解釈されたパケットの種別は`identify_packet_type()`により判別します。パケット種別は`E_PKT`列挙体で定義されています。

### PALパケットの解釈

```cpp
if (pkt == E_PKT::PKT_PAL) {
    auto&& pal = refTwePacketPal(pkt);
    
    if (pal.u8palpcb == E_PAL_PCB::MAG) {
      // for MAG
    } else
    if (pal.u8palpcb == E_PAL_PCB::AMB) {
      // for AMB
    }
}
```

解釈したパケットが`E_PKT::PKT_PAL`と判定された場合は、`refTwePacketPal()`により`TwePacketPal`型として参照できます。上記コード例ではユニバーサル参照`auto&&`を用いて型名を推測させています。

### PALボード種別解釈

```cpp
if (pal.u8palpcb == E_PAL_PCB::MAG) {
    PalMag mag = pal.get_PalMag();
    
    if (mag.u8MagStat == 0) {
       // closed
    } else {
       // opened
    }
}
```

PALのボード種別に応じたオブジェクトを生成して、ボード種別特有のデータにアクセスすることが出来ます。

上記の例では、MAG(OPEN-CLOSE SENSE PAL)のオブジェクト`mag`を生成しています。ここではオープンクローズに応じた分岐に`mag.u8MagStat`を読み出しています。


# Terminal

ターミナル(コンソール）クラスの利用例

M5stackの320x200ドット液晶に固定幅ターミナルウインドウを表示します。

以下は画面出力の例です。

```cpp
#include <mwm5.h>

#include <Arduino.h>
#include <M5Stack.h>

// ターミナルオブジェクトの生成
TWETerm_M5_Console the_screen(
    64, 20,
    { 0, 16, 320, 192 }, M5);

void setup() {
    // begin M5
    M5.begin(true, false, true, false);  // LCD, SD, Serial, I2C

    // create font as #10 (東雲フォント16)
    createFontShinonome16(10, 0, 0);
    
    the_screen.set_font(10);     // set font #10
		the_screen.set_color(ALMOST_WHITE, M5.Lcd.color565(90, 0, 50);
		                             // set color (fg, bg)
		the_screen.set_cursor(2);    // 0: no 1: curosr 2: blink cursor
		the_screen.force_refresh();  // repaint now.
		
		// initial message
		the_screen << "the_screen:" << crlf;
}

void loop() {
    // refresh screen
    static uint32_t t_last;
    uint32_t t_now = millis();
    
    if (t_now - t_last > 32) {
        the_screen.refresh();
        t_last = t_now;
    }
    
    // press A button
    if (M5.BtnA.wasReleased()) {
        the_screen << "hello world!" << crlf;
    }
    if (M5.BtnB.wasReleased()) {
        // force clear with whole redraw.
        the_screen.clear_screen();
    }
    if (M5.BtnC.wasReleased()) {
        // clear screen and set cursor home.
        the_screen << "\033[2J\[033H";
    }
}
```

まずターミナルオブジェクトを生成します。ここでは最大64カラム、最大20行、左上から (0,16)の位置に(320,192)サイズの領域とします。

```cpp
// ターミナルオブジェクトの生成
TWETerm_M5_Console the_screen(
    64, 20,
    { 0, 16, 320, 192 }, M5);
```

次に「東雲フォント16ドット」をフォントID=10で生成しています。フォントを管理するオブジェクトはライブラリ内部で生成・管理されます。

```cpp
void setup() {
  ...
  // create font as #10 (東雲フォント16)
  createFontShinonome16(10, 0, 0);
```

ターミナルにフォントなどを基本的な設定を行います。フォントを先ほどのフォントID=10として指定し、フォントの文字色と背景色を指定します。カーソルを2(ブリンク表示)とします。最後に`forece_refresh()`にて初期描画を行います。

```cpp
void setup() {
  ...
  the_screen.set_font(10);     // set font #10
  the_screen.set_color(ALMOST_WHITE, color565(90, 0, 50);
                               // set color (fg, bg)
  the_screen.set_cursor(2);    // 0: no 1: curosr 2: blink cursor
  the_screen.force_refresh();  // repaint now.
```

`setup()`が終了したら`loop()`での周期実行中に、一定時間ごとに画面の描画を行います。ここでは32ms以上経過したら再描画するようにしています。

```cpp
void loop() {
    // refresh screen
    static uint32_t t_last;
    uint32_t t_now = millis();
    
    if (t_now - t_last > 32) {
        the_screen.refresh(); // 再描画！
        t_last = t_now;
    }
```

以下の例ではボ タン Aでは"hello world!" + 改行の出力、ボタンBでは画面のクリア、ボタンCも同様に画面のクリア（エスケープシーケンスによる）を行います。

```cpp
void loop() {
    ... 
    
    // press A button
    if (M5.BtnA.wasReleased()) {
        the_screen << "hello world!" << crlf;
    }
    if (M5.BtnB.wasReleased()) {
        // force clear with whole redraw.
        the_screen.clear_screen();
    }
    if (M5.BtnC.wasReleased()) {
        // clear screen and set cursor home.
        the_screen << "\033[2J\[033H";
    }
```


# References


# Parser

シリアル電文パーサー

&#x20;パーサーは、シリアルポートからの電文を解釈し、その内容を読み取ります。

* [TWESERCMD](/0.8/references/parser/twesercmd) - 電文形式を解釈します
* [TWEFMT](/0.8/references/parser/twefmt) - 解釈した電文の内容を読み取ります


# TWESERCMD

書式パーサー

書式パーサーは、IParserを基底クラスとして、書式ごとに派生クラスを実装します。

```
      [IParser]
          ↑
     [AsciiParser]
```

パーサーは、シリアルポートのように１バイトずつ入力されるデバイスを想定し、１バイト単位での解釈を行い、都度状態を遷移する状態遷移マシンとして実装されます。


# IParser

パーサーの基底クラス

パーサーオブジェクトに１バイトずつ電文を投入することで、電文系列を解釈する状態遷移マシンです。

## メソッド

### Parse(), operator << ()

```cpp
IParser& Parse(uint8_t u8b)
IParser& operator << (char_t c)
```

パーサーに１バイト入力します。入力のたびにパーサーの状態が変化し、パーサーの解釈が完了すると`state()`が`E_TWESERCMD_COMPLETE`に変化し解釈完了状態となります。

### state()

```cpp
uint8_t state()
```

パーサーの状態を取得します。

### operator bool(), is\_complete()

```cpp
operator bool()
bool is_complete()
```

パーサーの状態が`E_TWESERCMD_COMPLETE`の場合`true`になります。

### length()

```cpp
uint16_t length()
```

パーサーで解釈済みのバイト列のデータ長を返します。

### operator\[]\()

```cpp
uint8_t operator[](int i)
```

パーサーの解釈済みのバイト列にアクセスします。

### get\_payload()

```cpp
SmplBuf_Byte& get_payload()
```

パーサーの解釈済みのバイト列を格納した配列クラス`SmplBuf_Byte`を参照します。

### reinit()

```cpp
virtual void reinit()
```

パーサーの解釈途中の内容を破棄し、新たな解釈を始めます。

### operator << ()

```cpp
IStreamOut& operator << (TWE::IStreamOut& lhs, IParser& rhs)
IStreamOut& operator << (TWETERM::ITerm& lhs, IParser& rhs)
```

IStreamOutをベースクラスにもつストリームオブジェクトに、書式出力します。

## メソッド

### \_u8Parse()

```cpp
virtual uint8_t _u8Parse(char_t u8b) = 0
```

１バイト入力して解釈を進める仮想関数です。派生クラスにより実装されます。

### \_vOutput()

```cpp
virtual inline void _vOutput(SmplBuf_Byte& bobj, IStreamOut& p) = 0
```

バイト配列`bobj`に格納されるバイト列に対応する書式をストリーム`p`に出力する仮想関数です。派生クラスにより実装されます。

## 状態

| 状態名                          |    値    | 状態                     |
| ---------------------------- | :-----: | ---------------------- |
| `E_TWESERCMD_EMPTY`          |    0    | 解釈前で、まだ系列のヘッダも認識できていない |
|                              | 1..0x7F | 解釈中                    |
| `E_TWESERCMD_COMPLETE`       |   0x80  | 系列が正しく解釈できた            |
| `E_TWESERCMD_ERROR`          |   0x81  | 系列の解釈にエラーがあった          |
| `E_TWESERCMD_CHECKSUM_ERROR` |   0x82  | 系列は得られたがチェックサムエラーだった   |


# AsciiParser

アスキー形式のパーサー

アスキー書式の解釈を行うパーサーですが、`TWESYS::TimeOut`クラスをベースクラスに持つことで、タイムアウト処理を行っています。

## メソッド

### AsciiParser() - コンストラクタ

```cpp
AsciiParser(size_t maxbuffsiz)
AsciiParser(SmplBuf_Byte& bobj)
```

パーサーオブジェクトを生成します。

生成時のパラメータに`maxbuffsiz`を与えると、`maxbuffsiz`をバッファサイズとして動的にメモリ確保して、パーサーを初期化します。

あらかじめ生成された`SmplBuf_Byte`配列`bobj`を参照して、パーサーを初期化することもできます。

### \_u8Parse()

```cpp
uint8_t AsciiParser::_u8Parse(char_t u8byte)
```

アスキー書式の解釈アルゴリズムを実装します。バイトの入力のたびにタイムアウトのチェックを行います。

### \_vOutput()

```cpp
void AsciiParser::_vOutput(TWEUTILS::SmplBuf_Byte& bobj, TWE::IStreamOut& p)
```

書式出力を行います。`s_Output()`メソッドを呼び出します。

### s\_Output(), vPutByte()

```cpp
static void vPutByte(uint8_t u8byte, uint8_t* pu8lrc, TWE::IStreamOut& p)
static void s_vOutput(TWEUTILS::SmplBuf_Byte& bobj, TWE::IStreamOut& p)
```

`vPutByte()`は、ストリームに対して与えられたバイト`u8byte`をアスキー２文字で出力します。例えば0x9Aであれば"9A"という２バイト文字になります。

`s_vOutput()`は、ストリームに対して、与えられたバイト配列`SmplBuf_Byte`のバイト列をアスキー形式で出力します。


# TWEFMT

パケット定義

書式を解釈して得られたデータ列は、受信したパケット情報が含まれます。ここでは、このデータ列のことをパケットデータと呼びます。

パケットデータは[`TwePacket`](/0.8/references/parser/twefmt/twepacket)クラスで表現されます。`TwePacket`クラスのデータを解釈することで、その種別を判定し、各アプリケーションや接続ハードウェアに応じたデータ構造となります。`TwePacket`クラスは基底クラスでさらにパケット種別ごとの派生クラスとなります。

このオブジェクトは無線パケットのデータ量に準じたメモリ領域を消費し、また、アプリケーションでは多数のパケットを保持することも考えられます。メモリ管理を簡略化するため`TwePacket`をスマートポインタ`std:shared_ptr<TwePacket>`にて管理します。このスマートポインタを`spTwePacket`に`typedef`しています。

以下に、TWELITE PALのパケットデータの場合のクラス関係を示します。`TwePacketPal`の基底クラスの一つ`DataPal`はTWELITE PAL特有のデータを格納しています。TWELITE PALには、さらに接続されるセンサーパルによって格納すべきデータが異なります。`TwePacketPal`からさらに`PalAmb`や`PalMot`を生成します。例えば`PalAmb`には温室センサーの値や照度センサーの値が格納されます。この`TwePacketPal`をスマートポインタ`spTwePacket`という形に生成するのが`newTwePacket()`です。

```
    [spTwePacket] ←(生成)- newTwePacket()
          ◆ 
          ｜(shared_ptr)
          ｜
      [TwePacket]    [DataPal]
              ↑        ↑
            [TwePacketPal] -(生成)-> [PalAmb], [PalMot], ...
```

`spTwePacket`型はスマートポインタですので、オブジェクトのコピー渡しによる記述を行うことで、コピーのオーバーヘッドを最小にしつつ、メモリーの管理を自動化できます。以下の例はパケットの履歴を管理する単純なクラスです。

```cpp
struct _pkt_hist {
	spTwePacket _pkt[16]; // パケットの履歴
	int _i;

	_pkt_hist() : _i(-1), _pkt{} {}
	void add_entry(spTwePacket p) { _pkt[++_i & 0xF] = p; }
	spTwePacket get_entry(int i) { return _pkt[(i + _i) & 0xF]; }
} pkt_hist;

void loop() {
  spTwePacket pkt, pkt_last;
  
  // 新たなパケットオブジェクト
  pkt = newTwePacket(p, len);
  pkt_hist.add_entry(pkt); // 履歴に保存する

  // ひとつ前のパケットオブジェクト
  pkt_last = pkt_hist.get_entry(-1);
}
```


# TwePacket, spTwePacket

パケットオブジェクト

パケットデータは種別によってデータ構造が違いますが、様々な種類のパケットを一元管理するための基底クラスです。

```cpp
class TwePacket;
typedef std::shared_ptr<TwePacket> spTwePacket;
```

`spTwePacket` はメモリ管理のためのスマートポインタです。`std::shared_ptr`を用いています。

TwePacketクラスは、パケットデータのパケット種別の管理を行います。また、パケットデータの解釈を行うための仮想関数parse()メソッドを定義しています。パケット特有のデータ構造に基づく解釈やデータの保存等の取り扱いは、派生クラスに実装します。

## メソッド

### TwePacket() - コンストラクタ

```cpp
TwePacket(E_PKT ptyp = E_PKT::PKT_ERROR)
```

デフォルトでは、未解釈状態として `E_PKT::PKT_ERROR` で初期化します。

### \~TwePacket() - デストラクタ

```cpp
virtual ~TwePacket()
```

### get\_type()

```cpp
E_PKT get_type()
```

パケットの種別を[`E_PKT`](/0.8/references/parser/twefmt/twepacket/e_pkt)型で返します。

### parse()

```cpp
virtual E_PKT parse(uint8_t* p, uint8_t u8len)
```

パケットデータのバイト列を与えて、パケットデータを解釈する。

派生クラスで、そのパケットに対応するデータ構造を解釈するための実装を行います。

戻り値は [`E_PKT`](/0.8/references/parser/twefmt/twepacket/e_pkt)型 で、成功時は解釈されたパケット種別を、エラー時に `E_PKT::PKT_ERROR` を返します。


# E\_PKT

パケット種別定義

以下のパケットに対応します。

| 名前           | 解説                                                                                                                |
| ------------ | ----------------------------------------------------------------------------------------------------------------- |
| PKT\_ERROR   | パケット解釈前やパケット種別が特定できないなど、TwePacketには意味のあるデータが格納されていない                                                              |
| PKT\_TWELITE | 標準アプリ App\_Twelite の [0x81 コマンド](https://mono-wireless.com/jp/products/TWE-APPS/App_Twelite/step3-81.html)を解釈したもの |
| PKT\_PAL     | [TWELITE PAL](https://mono-wireless.com/jp/products/TWE-APPS/App_pal/index.html)のシリアル形式を解釈したもの                    |


# idenify\_packet\_type()

パケット種別判定

パケットデータのバイト列を入力として、パケットの種別を判定します。また既に生成済みの[`spTwePacket`](/0.8/references/parser/twefmt/twepacket)オブジェクトの種別を返します。戻り値は[E\_PKT](/0.8/references/parser/twefmt/twepacket/e_pkt)です。

```cpp
E_PKT identify_packet_type(uint8_t* p, uint8_t u8len)
E_PKT identify_packet_type(SmplBuf_Byte& sbuff)

E_PKT identify_packet_type(spTwePacket& sp)
```


# newTwePacket()

パケットデータの解釈とオブジェクト生成

パケットデータのバイト列を入力として、パケット種別の判定と、種別に応じた[`spTwePacket`](/0.8/references/parser/twefmt/twepacket)オブジェクトを生成します。

```cpp
spTwePacket newTwePacket(
		uint8_t* p,
		uint8_t u8len,
		E_PKT eType = E_PKT::PKT_ERROR)

spTwePacket newTwePacket(
		TWEUTILS::SmplBuf_Byte& sbuff,
		E_PKT eType = E_PKT::PKT_ERROR)
```

事前に[`identify_packet_type()`](/0.8/references/parser/twefmt/twepacket/idenify_packet_type)を用いてパケットの種別[`E_PKT`](/0.8/references/parser/twefmt/twepacket/e_pkt)が特定できている場合は`eType`を与えます。

戻り値は[`spTwePacket`](/0.8/references/parser/twefmt/twepacket)です。


# refTwePacket()

spTwePacketオブジェクトの参照

本関数は[`spTwePacket`](/0.8/references/parser/twefmt/twepacket)オブジェクトを[`TwePacket&`](/0.8/references/parser/twefmt/twepacket)として参照します。

```cpp
TwePacket& refTwePacket(spTwePacket& p)

//例
  auto&& pkt = newTwePacket(p, len); // パケットの生成
  
  if (refTwePacket(pkt).get_type() == E_PKT::PKT_TWELITE) {
    // App_Twelite の処理
  }
```

{% hint style="info" %}
この関数は、`->`演算子や`*`演算子を極力使用しない方針でライブラリを設定しているため、スマートポインタの参照を行うために用意しています。

上記の判定式を`(pkt && pkt->get_type() == E_PKT::PKT_TWELITE)`と記述しても同じ判定が得られます。
{% endhint %}


# Packet Types

パケット種別ごとの定義


# TwePacketTwelite

`TwePacketTwelite`クラスは、標準アプリApp\_Tweliteの0x81コマンドを解釈したものです。

```cpp
class TwePacketTwelite : public TwePacket, public DataTwelite { ... };
```

パケットデータ内の諸情報は[`parse()`](/0.8/references/parser/twefmt/twepacket#parse)実行後に[`DataTwelite`](/0.8/references/parser/twefmt/packet-types/twepackettwelite/datatwelite)に格納されます。

### spTwePacketからの参照

```cpp
TwePacketTwelite& refTwePacketTwelite(spTwePacket& p)
```

`spTwePacket`オブジェクトから`TwePacketTwelite`オブジェクトを参照します。`spTwePacket`に`TwePacketTwelite`以外が格納されている場合は、未解釈のオブジェクトを戻します。

### 実行例

```cpp
if (parse_ascii) { // アスキー形式のパーサーの解釈完了
  if (identify_packet_type(parse_ascii.get_payload()) == E_PKT::PKT_TWELITE) {
    auto&& pkt = newTwePacket(parse_ascii.get_payload(), E_PKT::PKT_TWELITE);
    
    if (pkt == E_PKT::PKT_TWELITE) { // パケット解釈成功時
      auto&& x = refTwePacketTwelite(pkt);
      
  		the_screen // LCDスクリーン上のターミナルに情報を表示
  				<< printfmt(":Lq=%d:Ad=%08X", x.u8lqi, x.u32addr_src)
  				<< printfmt(":ID=%02X", x.u8addr_src)
  				<< printfmt(":DI=%04b", x.DI_mask)
  				;
    }
  }
}
```


# DataTwelite

App\_Twelite データ

`TwePacketTwelite`のデータ部分。

```cpp
struct DataTwelite {
		//送信元のシリアル#
		uint32_t u32addr_src;
		
		// 送信元の論理ID
		uint8_t u8addr_src;

		// 宛先の論理ID
		uint8_t u8addr_dst;

		// 送信時のタイムスタンプ
		uint16_t u16timestamp;

		// 低レイテンシ送信時のフラグ
		bool b_lowlatency_tx;

		// リピート中継回数
		uint16_t u8rpt_cnt;

		// LQI値
		uint16_t u8lqi;

		// DIの状態 (true がアクティブ Lo,GND)
		bool DI1, DI2, DI3, DI4;
		// DIの状態ビットマップ (LSBから順にDI1,2,3,4)
		uint8_t DI_mask;

		// DIアクティブならtrue (過去にアクティブになったことがある)
		bool DI1_active, DI2_active, DI3_active, DI4_active;
		// DIのアクティブビットマップ(LSBから順にDI1,2,3,4)
		uint8_t DI_active_mask;

		// モジュールの電源電圧[mV]
		uint16_t u16Volt;

		// AD値 [mV]
		uint16_t u16Adc1, u16Adc2, u16Adc3, u16Adc4;
		// ADがアクティブ（有効）なら 1 になるビットマップ (LSBから順にAD1,2,3,4)
		uint8_t Adc_active_mask;
};
```


# TwePacketPal

TWELITE PALのパケット

`TwePacketPal`クラスは、TWELITE PALのパケットデータを解釈したものです。このクラスはTWELITE PAL（センサーデータなど上り方向）共通に取り扱います。

```cpp
class TwePacketPal : public TwePacket, public DataPal { ... };
```

PAL共通データは[`DataPal`](/0.8/references/parser/twefmt/packet-types/twepacketpal/datapal)に定義されています。

PALの各センサー基板特有のデータを取り出すためのジェネレータ関数を用意しています。

### spTwePacketからの参照

```cpp
TwePacketPal& refTwePacketPal(spTwePacket& p)
```

`spTwePacket`オブジェクトから`TwePacketPal`オブジェクトを参照します。`spTwePacket`に`TwePacketPal`以外が格納されている場合は、未解釈のオブジェクトを戻します。

### 実行例

```cpp
if (parse_ascii) { // アスキー形式のパーサーの解釈完了
  if (identify_packet_type(parse_ascii.get_payload()) == E_PKT::PKT_PAL) {
    auto&& pkt = newTwePacket(parse_ascii.get_payload(), E_PKT::PKT_PAL);
    
    if (pkt == E_PKT::PKT_PAL) { // パケット解釈成功時
      auto&& pal = refTwePacketPal(pkt);
      
      // 開閉センサーパル
      if (pal.u8palpcb == E_PAL_PCB::MAG) {
        PalMag mag = pal.get_PalMag();
        if (mag.u8MagStat == 0) {
          // OPEN
        } else {
          // CLOSE
        }
      }
    }
  }
}
```

## ジェネレータ関数

センサーPALの各種データを取り出すためのジェネレータ関数です。

### get\_PalMag()

```cpp
PalMag get_PalMag()
```

開閉センサーパルのデータ[`PalMag`](/0.8/references/parser/twefmt/packet-types/twepacketpal/palbase/palmag)を取り出します。

### get\_PalAmb()

```
PalAmb get_PalAmb()
```

環境センサーパルのデータ[`PalAmb`](/0.8/references/parser/twefmt/packet-types/twepacketpal/palbase/palamb)を取り出します。

### get\_PalMot()

```
PalMot get_PalMot()
```

動作センサーパルのデータ[`PalMot`](/0.8/references/parser/twefmt/packet-types/twepacketpal/palbase/palmot)を取り出します。


# DataPal

PAL共通データ

PALは接続されるセンサーなどによってパケットデータ構造が異なりますが、`DataPal`では共通部のデータ構造を保持します。

```cpp
struct DataPal {
	uint8_t u8lqi;        // LQI値

	uint32_t u32addr_rpt; // 中継器のアドレス

	uint32_t u32addr_src; // 送信元のアドレス
	uint8_t u8addr_src;   // 送信元の論理アドレス

	uint16_t u16seq;      // シーケンス番号

	E_PAL_PCB u8palpcb;		// PAL基板の種別
	uint8_t u8palpcb_rev;	// PAL基板のレビジョン
	uint8_t u8sensors;		// データに含まれるセンサーデータの数 (MSB=1はエラー)
	uint8_t u8snsdatalen; // センサーデータ長(バイト数), MSB=1は動的確保

	uint8_t au8snsdata[32]; // センサーデータ（解釈前の生データ）
	std::unique_ptr<uint8_t[]> uptr_snsdata; // センサーデータ（動的確保）
};
```

PALのパケットデータ構造は大まかに２つのブロックからなり、全てのPAL共通部と個別のデータ部になります。個別のデータ部は、パケットの解釈を行わずそのまま格納しています。取り扱いを単純化するため32バイトを超えるデータは動的に確保する`uptr_snsdata`に格納します。

個別のデータ部は、PalBaseをベースクラスに持つ構造体に格納されます。この構造体は、TwePacketPalに定義されるジェネレータ関数により生成されます。


# E\_PAL\_PCB

PAL基板種別

下記のPAL基板に対応します。

| 名前    | 解説                              |
| ----- | ------------------------------- |
| NOPCB | 基板未接続、エラー                       |
| MAG   | マグネットセンサー付きのMAG (開閉センサーパル)      |
| AMB   | 温湿度センサー、照度センサー付きのAMB (環境センサーパル) |
| MOT   | 加速度センサー付きのMOT (動作センサーパル)        |


# PalBase

PALセンサー共通データ

PALの各センサーのデータ構造体はすべて`PalBase`を継承します。センサーデータの格納状況`u32StoredMask`とモジュール電源電圧`u16Volt`の情報が含まれます。

```cpp
	struct PalBase {
		uint32_t u32StoredMask;
		uint16_t u16Volt;
	};
```

派生構造体に定義される`STORE_COMP_MASK`と`u32StoreMask`が一致すれば、全てのセンサーのデータが適切に解釈され、格納されていることになります。


# PalMag

開閉センサーパル(MAG)のセンサーデータ

```cpp
struct PalMag : public PalBase {
	const uint8_t U8VARS_CT = 2; // センサー数
	const uint32_t STORE_COMP_MASK = (1 << U8VARS_CT) - 1; // 全コンプのマスク

	uint8_t u8MagStat; // 開閉状況 (0:磁石が遠い, 1/2:磁石検出)
	uint8_t bRegularTransmit; // 定期送信の時に 1, 磁石検出では 0
};
```


# PalAmb

環境センサーパル(AMB)のセンサーデータ

```cpp
struct PalAmb : public PalBase {
	const uint8_t U8VARS_CT = 4; // センサー数
	const uint32_t STORE_COMP_MASK = (1 << U8VARS_CT) - 1; // 全コンプマスク

	int16_t i16Temp;  // 温度 (x100)
	uint16_t u16Humd; // 湿度 (x100 %)
	uint32_t u32Lumi; // 照度 (lux相当)
};
```


# PalMot

動作センサーパル(MOT)のセンサーデータ

```cpp
struct PalMot : public PalBase {
	const uint8_t U8VARS_CT = 17; // センサー数
	const uint32_t STORE_COMP_MASK = 3; // 電圧と加速度サンプル１あればコンプとする

	uint8_t u8samples; // 格納サンプル数
	int16_t i16X[16];  // X軸 (ミリG)
	int16_t i16Y[16];  // Y軸
	int16_t i16Z[16];  // Z軸
};
```

※ 各サンプルの連続性を確認するには、パケットのシーケンス番号の抜けが無いことを確認してください。


# Terminal

ターミナル（コンソール）

本ライブラリのターミナル（コンソール）は、文字列ベースの画面を構成することを目的としてます。

以下に設計時の考慮事項を記載します。

* 固定幅の画面構成を行うこと
* 日本語の表示が可能であること
* ソースコード中に直接日本語文字列を含められるようにすること
  * UTF-8でソースコードを記述する前提とする
  * 内部処理をUnicodeとすること
  * UTF-8デコードが出来るようにすること
* 旧来のキャラクター型のインタフェースを実装できるよう、いくつかのエスケープシーケンスを実装しておくこと
  * ただしANSIエスケープシーケンスの完全な互換性を目的とはしない
* 文字色、背景色、太字といった表示属性に対応すること
* カーソルを表示・非表示にできること
* 画面の順方向のスクロールに対応すること
* カラム数を超えて文字列を出力した場合は、折り返しを行えること
  * 右端カラムへの文字出力を行った場合、その時点では折り返しを行わないようにすること
    * 上記を実装は、折り返し処理の実装より優先すること
* 複数のターミナル表示を画面上に同時に表示できること
* 毎回全画面書き換えといったような描画パフォーマンスの悪い実装でないこと
  * 変更がある行のみを書き換える行単位の描画を行うようにした
* フォントを選択できること
  * 配布可能なフォントをライブラリ内に同梱しておくこと
  * より大きな文字を表示するため、倍角表示に対応すること

本ライブラリのターミナルは、大まかに分けて２要素から構成されます。

* ターミナル画面上の文字列を管理するクラスや関数をまとめた [TWETERM](/0.8/references/untitled/tweterm)
* フォントの管理と描画に関連するクラスや関数をまとめた [TWEFONT](/0.8/references/untitled/twefont)


# TWETERM

ターミナル(コンソール)

`namesace TWETERM`はターミナル（コンソール）画面を実装するためのクラスや関数などをまとめています。

基本的なクラス構造は以下のようになっています。クラス[`ITerm`](/0.8/references/untitled/tweterm/iterm)ではターミナルの文字列バッファとその処理、[`TWETerm_M5_Console`](/0.8/references/untitled/tweterm/tweterm_m5_console)クラスはITermの文字列描画部分を実装したものです。[IStreamOut](/0.8/references/basics/twe/untitled)はITerm派生クラスオブジェクトが文字列をターミナルに投入するための基本的な手続きを提供しています。

```
    [IStreamOut]
         ↑
      [ITerm]
         ↑
 [TWETerm_M5_Console]
```


# ESC Sequence

エスケープシーケンス

エスケープシーケンスは ESC 文字 '\033' で始まり何文字かで完結する制御コードです。

本ライブラリが動作するプログラム上や、シリアルポートの先にあるマイコンから制御文字をターミナルに投入することで、様々な画面制御（画面のクリア、カーソルの移動、色などの表示属性の変更）を行うことが出来ます。

以下に対応するエスケープシーケンスを記述します。表中の ESC はエスケープ文字 '\033'、イタリックの *n* や *m* は数字の入力です。

| エスケープシーケンス                       | 意味                                                                                                                                         |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| ESC \[ *n*  A                    | カーソルを*n*行上に移動する。（*n*省略時は1行）                                                                                                                |
| ESC \[ *n* B                     | カーソルを*n*行下に移動する。（*n*省略時は1行）                                                                                                                |
| ESC \[ *n* C                     | カーソルを*n*列右に移動する。（*n*省略時は1列）                                                                                                                |
| ESC \[ *n* D                     | カーソルを*n*列左に移動する。（*n*省略時は1列）                                                                                                                |
| ESC \[ *n* G                     | カーソルのカラム*n*の位置に移動する (*n*省略時は1カラム目＝行頭)                                                                                                      |
| ESC \[ *n* ; *m* H               | カーソル位置を行*n*列*m*に移動する。先頭位置の場合は1を指定します。*n*;*m*を省略した場合は左上ホームポジションにカーソルを移動します。                                                                 |
| ESC \[ n ; *m* f                 | ESC \[ *n* ; *m* Hに同じ。                                                                                                                     |
| ESC \[ 2 J                       | 画面をクリアしてカーソルをホームポジションに移動する。                                                                                                                |
| ESC \[ *n* K                     | <p><em>n</em>=0 または省略 カーソル行より後ろを削除する</p><p><em>n</em>=1 カーソル行より前を削除する</p><p><em>n</em>=2 行全体を削除する</p>                                      |
| ESC \[ *n1* ; *n2* ; ...; *n4* m | <p>描画属性の設定を行う。<em>n1</em> .. <em>n4</em> は任意数指定できる。</p><p>1 → 太字</p><p>7 → 反転</p><p>30 .. 37 → 文字色</p><p>40 .. 47 → 背景色</p><p>0 → 属性抹消</p> |

{% hint style="warning" %}
`ITerm`で対応するエスケープシーケンスは、ANSIターミナル互換を目的としたものではありません。解釈や仕様には違いがあります。
{% endhint %}


# TermAttr

`ITerm`型のターミナルオブジェクトに対して、`<<`演算子を用いてターミナル属性を指定するヘルパークラス。

```cpp
void putmsg(ITerm& trm, const char *msg) {
    trm << TermAttr(TERM_COLOR_FG_RED | TERM_BOLD);
    trm << msg;
    trm << TermAttr(TERM_ATTR_OFF);
}
```

上記の例では、出力ターミナルを`trm`に対して、文字色を赤で太字にして`msg`を出力し、属性をクリアします。

### TermAttr() - コンストラクタ

```cpp
TermAttr(GChar::tAttr attr = 0)
```

attrをパラメータとします。attrは後述の**GChar::tAttr 定数***一覧*で指定します。

指定は以下の組み合わせになります。

* TERM\_ATTR\_OFF (すべての属性のクリア)
* 以下の論理和
  * TERM\_BOLD
  * TERM\_REVERSE
  * 文字色のいずれか
  * 背景色のいずれか

## GChar::tAttr 定数

#### 色設定以外の属性

| 設定                    | 意味             |
| --------------------- | -------------- |
| TERM\_ATTR\_OFF = 0x0 | すべての属性をクリアする   |
| TERM\_BOLD            | 文字を太字にする       |
| TERM\_REVERSE         | 背景色と文字色を反転表示する |

#### 文字色

| 設定                       | 意味   |
| ------------------------ | ---- |
| TERM\_COLOR\_FG\_BLACK   | 黒    |
| TERM\_COLOR\_FG\_RED     | 赤    |
| TERM\_COLOR\_FG\_GREEN   | 緑    |
| TERM\_COLOR\_FG\_YELLOW  | 黄    |
| TERM\_COLOR\_FG\_BLUE    | 青    |
| TERM\_COLOR\_FG\_MAGENTA | マゼンタ |
| TERM\_COLOR\_FG\_CYAN    | シアン  |
| TERM\_COLOR\_FG\_WHITE   | 白    |

#### 背景色

| 設定                       | 意味   |
| ------------------------ | ---- |
| TERM\_COLOR\_BG\_BLACK   | 黒    |
| TERM\_COLOR\_BG\_RED     | 赤    |
| TERM\_COLOR\_BG\_GREEN   | 緑    |
| TERM\_COLOR\_BG\_YELLOW  | 黄    |
| TERM\_COLOR\_BG\_BLUE    | 青    |
| TERM\_COLOR\_BG\_MAGENTA | マゼンタ |
| TERM\_COLOR\_BG\_CYAN    | シアン  |
| TERM\_COLOR\_BG\_WHITE   | 白    |


# ITerm

ターミナル用文字バッファ管理クラス

&#x20;ターミナル（コンソール）の基底クラスで、画面上の文字列を管理する。このクラスは、実際の画面描画についての手続きは含まれず、このクラスを継承したサブクラスによって画面描画を実装します。

## メソッド

### ITerm() - コンストラクタ

```cpp
ITerm(uint8_t u8c, uint8_t u8l)
ITerm(uint8_t u8c, uint8_t u8l, SimpBuf_GChar* pAryLines, GChar* pBuff)
```

カラム数`u8c`と行数`u8l`を指定して、ターミナルを構築する。カラム数と行数はターミナルで管理できる最大の値を指定する。ターミナルのサイズ変更を行った場合でも各々の最大の値を超える変更は行われない。

`pAryLines`と`pBuff`を指定する場合は、ITerm内でのメモリ確保は行われず、外部で確保済みの配列を利用する。

```cpp
// 静的メモリ確保例
const uint8_t U8COL = 64;
const uint8_t U8LINE = 20;
static TWETERM::GChar screen_buf[U8COL * U8LINE];   // pBuff
static TWETERM::SimpBuf_GChar screen_lines[U8LINE]; // pAryLines
```

### \~ITrem() - デストラクタ

```cpp
virtual ~ITerm()
```

動的にメモリを確保した場合は、そのメモリ領域を破棄します。

### clear(), home(), clear\_screen()

```cpp
void clear()
void home()
void clear_screen()
```

`clear()`は画面バッファのクリア、`home()`はカーソル位置をホームポジションに移動、`clear_screen()`は両者を実行します。

### refresh(), force\_refresh()

```cpp
virtual void refresh() = 0
void force_refresh()
```

サブクラスにより実装される画面更新描画のためのメソッドです。描画方法は２種類あり、メンバー変数`u32Dirty`に定義されるビットマスクに対応した行のみを再描画するものと、`force_refresh()`メソッドによる画面全体を再描画するものがあります。

画面全体の再描画では、いったん背景を背景色で塗りつぶしてから再描画します。初回の描画では`force_refresh()`を行うようにしてください。

### write()

```cpp
ITerm& write(wchar_t c)
ITerm& write(char_t c)
```

ターミナルに１文字書き出します。カーソル位置に文字を書き出します。16bit `wchar_t`型のUnicodeを渡します。

`char_t` (char) 型のパラメータを渡した場合は、入力をUTF-8として取り扱います。例えば0x7F までのASCII文字はそのまま`write(wchar_t)`が呼び出され、３バイトのUTF-8エンコードされた日本語文字は、連続して３バイトを投入した時点で`write(wchar_t)`が呼び出されます。

{% hint style="info" %}
日本語文字セットを表示するためのフォントの取り扱いについては[TWEFONT::FontDef](/0.8/references/untitled/twefont/fontdef)を参照してください。
{% endhint %}

### get\_height(),  get\_width()

```cpp
uint8_t get_height() // 行数
uint8_t get_width()  // カラム数
```

ターミナルの行数、カラム数を返す。

### operator <<

```cpp
ITerm& operator << (const char *s)
TWE::IStreamOut& operator << (TWE::IStreamSpecial& sc)
TWE::IStreamOut& operator << (ITerm& t, char_t c)
TWE::IStreamOut& operator << (ITerm& t, wchar_t c)
TWE::IStreamOut& operator << (ITerm& t, int i)
TWE::IStreamOut& operator << (ITerm& t, TermAttr a)
TWE::IStreamOut& operator << (TWE::IStreamOut& t, TermAttr a)
```

`<<`演算子を用いてターミナルに文字列を書き出します。

| パラメータ              | 意味                                  |
| ------------------ | ----------------------------------- |
| const char \*s     | 文字列 s をターミナルに書き出す (UTF-8のデコードを行います) |
| IStreamSpecial& sc | crlf （改行）など特殊文字を出力する                |
| char\_t c          | 文字 c  をターミナルに書き出す (UTF-8のでコードを行います) |
| wchar\_t c         | 文字 c (Unicode)をターミナルに書き出す           |
| int i              | printf("%d", i) に該当する出力を行います        |
| TermAttr a         | 文字属性を設定します                          |

## メソッド (サブクラス実装用)

| メソッド名                  | 解説                                                                                                                                                                                                                                                     |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| post\_refresh()        | サブクラスでのrefresh()実装で、最後に呼び出す必要があります。必要な変数の初期化を行います。                                                                                                                                                                                                     |
| resize\_screen()       | 指定されたカラム数・行数に従い、バッファを再構成します。コンストラクタ指定の初期値を超えた指定はできません。                                                                                                                                                                                                 |
| column\_idx\_to\_vis() | <p><code>uint16\_t column\_idx\_to\_vis(int16\_t idx , int16\_t lin)</code><br>Unicodeで管理されている画面バッファー上の行位置・カラム位置（ともに0が先頭位置)から、画面上のカラム位置を計算します。日本語文字のような２文字幅文字を２カラムとして計算します。 "abcあいう"という文字列が格納された行のカラム位置4は"い"の文字が格納されますが、本関数で画面上のカラム位置を計算すると5になります。</p> |
| column\_vis\_to\_idx() | <p>column\_vis\_to\_idx(int16\_t c\_vis, int16\_t lin)</p><p>画面上の行位置、カラム位置（ともに0が先頭位置)から、画面バッファー上のカラム位置を計算します。日本語文字のような２文字幅文字を２カラムとして計算します。 "abcあいう"という文字列が格納された行の画面上のカラム位置5または6は"い"の文字が格納されますが、本関数で画面バッファー上のカラム位置を計算すると4になります。</p>                     |
|                        |                                                                                                                                                                                                                                                        |


# TWETerm\_M5\_Console

M5Stack用のLcd描画ターミナル

M5Stack の 320x240 LCD 用のターミナルの実装です。[`ITerm`](/0.8/references/untitled/tweterm/iterm)を実装しています。

本クラスは`namespace TWEARD`内に定義されます。

## メソッド

### TWETerm\_M5\_Console  - コンストラクタ

```cpp
TWETerm_M5_Console(
			uint8_t u8c, uint8_t u8l,
			Rect drawArea, M5Stack& _M5)

TWETerm_M5_Console(
			uint8_t u8c, uint8_t u8l,
			TWETERM::SimpBuf_GChar* pAryLines, TWETERM::GChar* pBuff,
			Rect drawArea, M5Stack& _M5)
```

[`ITerm`のコンストラクタ](/0.8/references/untitled/tweterm/iterm#iterm-konsutorakuta)に`drawArea`と`_M5`のパラメータが追加されています。

`drawArea`は、LCD内のターミナル描画エリアを決めます。`Rect`構造体で指定しx,y,w,hを指定します。(x,y)は領域の左上の座標、(w,h)は領域の幅と高さです。

`_M5`は、M5Stackのグローバルインスタンス `M5`を指定します。

#### 例：

```cpp
TWETerm_M5_Console the_screen(64, 20, { 0, 16, 320, 192 }, M5);
```

カラム最大値を64、行数の最大値を20、左上座標を (0, 16)、領域サイズを (320, 192) として `the_screen` オブジェクトを構築します。

## メソッド

### refresh()

`ITerm::refresh()`の実装です。この関数により画面の描画を行います。`loop()`関数内で定期的に呼び出します。

本実装では、パフォーマンスの向上のため、原則として描画変更の必要にある行のみを上書きします。画面全領域を再描画したい場合は`force_refresh()`メソッドを呼び出します。

以下の例では32msごとに描画を行います。

```cpp
void loop() {
		static uint32_t u32mills;

		uint32_t u32now = millis();
		if (u32now - u32mills > 32) {
			the_screen.refresh();

			u32mills = u32now;
		}
}
```

### set\_font()

```cpp
void set_font(uint8_t u8id, uint8_t u8col_request = 0, uint8_t u8row_request = 0)
```

フォントを指定します。

`u8id`はフォントIDを指定します。

`u8col_request`は、設定したいカラム数を指定します。指定した数値が領域サイズに対して大きい場合は指定領域に入るように値が丸められます。0を指定した場合は、領域サイズから計算できる最大のカラム数に設定されます。

`u8row_request`は、設定したい行数を指定します。指定した数値が領域サイズに対して大きい場合は指定領域に入るように値が丸められます。0を指定した場合は、領域サイズから計算できる最大の行数に設定されます。

### font\_width(), font\_height(), font\_id()

```cpp
uint8_t font_id()
uint8_t font_width()
uint8_t font_height()
```

`font_id()`は、指定したフォントのIDを返します。

`font_width()`は、指定したフォントの幅をピクセル数で返します。ダブル幅のピクセル数は、この値の２倍になります。

`font_height()`は、指定したフォントの高さをピクセル数で返します。

### set\_color()

```cpp
void set_color(uint16_t color, uint16_t bgcolor = 0)
```

ターミナルの文字色と背景色を指定します。

`color`は文字色を指定します。

`bgcolor`は背景色を指定します。

{% hint style="info" %}
色は565形式の16bit値です。`TWEARD::color565()`関数で計算します。

```cpp
uint16_t c = color565(255, 127, 0); // R:255, G:127, B:0
```

{% endhint %}

{% hint style="warning" %}
白色は `ALMOST_WHITE` で指定します。color565(255,255,255) または WHITE を指定すると描画が崩れます。
{% endhint %}

### set\_color\_table()

```cpp
void set_color_table(const uint16_t* ptbl)
```

ターミナルで使用できる８色のテーブルを指定します。`ptbl`は`uint16_t`型の配列で８つの要素が必要です。

#### 例

```cpp
static const uint16_t COLTBL_MAIN[8] = {
	BLACK,
	RED,
	GREEN,
	YELLOW,
	color565(127, 127, 255), // BLUE,
	color565(255, 0, 142), // MAGENTA,
	CYAN,
	ALMOST_WHITE
};

the_screen.set_color_table(COLTBL_MAIN);
```

上記の例では青とマゼンダの色調を変えたテーブルを指定し、ターミナルオブジェクト `the_screen`に指定しています。

## ユーティリティ関数

### color565()

```cpp
constexpr uint16_t color565(uint8_t r, uint8_t g, uint8_t b)
```

`r, g, b` を指定して、565形式の色コードを生成します。


# TWEFONT

フォント定義や描画

`namespace TWEFONT` には、フォントの定義やフォント描画のための手続きをまとめています。

{% hint style="warning" %}
このフォントライブラリは M5Stack 標準のライブラリのフォントには準じていないため M5Stackでのフォント描画APIなどで使用することが出来ません。
{% endhint %}

## フォントの生成

フォントは`FontDef`クラスにより管理されます。フォントごとに用意される`FontDef`クラスオブジェクトのジェネレータ関数により生成され、ライブラリ内部で生成時に指定したフォントIDと紐づけて管理されます。フォントは最大７つまで定義できます。フォント作成時に字間・行間・倍角を指定することができます。同じフォントに対して複数のフォントIDの登録が可能です。

下記の例では、フォントID 10 に16ドットの東雲フォント（縦倍角・横倍角指定）を、フォントIDを11に同じフォントですが倍角指定なし、行間を１ピクセルとしたフォント定義を行います。

```cpp
TWEFONT::createFontShinonome16(10, 0, 0,
        TWEFONT::U32_OPT_FONT_TATEBAI | TWEFONT::U32_OPT_FONT_YOKOBAI);
TWEFONT::createFontShinonome16(11, 1, 0);
```

コンパイル時に、ジェネレータ関数`createFont???()`を呼び出されたフォントのデータがリンクされます。

{% hint style="info" %}
フォントの登録した種類だけROM容量が必要になります。最小限のフォントを選択するようにしてください。
{% endhint %}

## フォントデータについて

本ライブラリには、作者が事実上パブリックドメイン（著作権等取扱はソースヘッダに含まれるクレジットを参照ください）を宣言しているフォントをいくつか含めています。

本ライブラリに含めるにあたって、以下の調整を行っています。

* 大本がBDF形式を変換し、描画ルーチンに適したデータ構造とした
* これらフォントをUnicodeとして取り扱うための参照テーブルを用意した
* latin1補助文字 U+00A0～u+00FFについて、フォント定義があるものについては収録した
* JIS X201 半角カナ U+FF61～U+FF9F について、フォント定義があるものについては収録した
* 常用漢字（2645 文字）のフォントデータと、全収録（東雲フォントのみ、一部未収録字形があります）を用意した

### 東雲フォント (12,14,16ドット)

```
●東雲フォントライセンス
																	2001
									 The Electronic Font Open Laboratory
										http://openlab.ring.gr.jp/efont/

このアーカイブに含まれるすべてのフォントデータ、ドキュメント、スクリプ
ト類はすべて Public Domain で提供されています 。

但し、日本に於いては現時点で著作権を放棄することは法律上不可能であり、
AUTHORS に列挙されている作者がその権利を行使しないと宣言することで実質
的な Public Domain であるとします。

自由な改造、他フォーマットへの変換、組込み、再配布を行うことができます。
同時に、これらはすべて完全に無保証です。
```

12,14,16ドット版をライブラリに含めています。

常用漢字のみのジェネレータ (`createFontShinonome12()`, `createFontShinonome12()` , `createFontShinonome16()`)または、全収録のジェネレータ (`createFontShinonome12_full()`) ,`createFontShinonome14_full()` ,`createFontShinonome16_full()` )を呼び出します。

{% hint style="info" %}
全収録版は字形データが多いためより多くのROM容量が必要です。同じフォントサイズのデータで常用版と全収録版の両方を登録する意味はありません。常用漢字版の字形データは全収録版に包含されるためです。
{% endhint %}

### MP+フォント (10ドット, 12ドット)

```
 M+ BITMAP FONTS            Copyright 2002-2005  COZ <coz@users.sourceforge.jp>
 -

 LICENSE




 These fonts are free softwares.
 Unlimited permission is granted to use, copy, and distribute it, with
 or without modification, either commercially and noncommercially.
 THESE FONTS ARE PROVIDED "AS IS" WITHOUT WARRANTY.
```

ジェネレータ `createFontMP10()`または `createFontMP12()` を呼び出します。

### 8x6 LCD フォント

ジェネレータ `createFontLcd8x6()` を呼び出します。

latin拡張文字や日本語フォントは含まれません。

このフォントはいずれかのジェネレータ関数`createFont???()`が呼び出されたときに、デフォルトとしてフォントID=0に登録されます。

## フォントIDについて

フォントIDは作成したフォントごとに割り当てられます。

IDは 0..32 の値を指定可能ですが、ユーザが登録できるのは 1..32 で最大７フォント登録できます。

ID=0 のフォントは 8x6 LCD フォントに割り当てられます。

## フォント情報の取得

フォント情報にアクセスするためには、[`queryFont()`](/0.8/references/untitled/twefont/queryfont)により[`FontDef`](/0.8/references/untitled/twefont/fontdef)オブジェクトを取得し、諸情報を得ます。

```cpp
auto&& font = queryFont(10); // ID10のFontDefオブジェクトを参照
TWE::fPrintf(the_screen, "\nFont: %s\n      ID=%d H:%d W:%d W_CHRs:%d",
			font.font_name, // フォント名
			font.get_font_code(), // フォントID (=10)
			font.height, // フォントデータの高さ（行間は含まれない）
			font.width, // フォントデータの幅（シングル幅、文字間スペースは含まれない）
			font.font_wide_count // ダブル幅の登録文字数
			);
```

## ターミナルでの使用

ターミナルにフォントを指定するには、フォントの生成を行い、フォントIDをターミナルオブジェクトに指定します。フォント指定後は[`force_refresh()`](/0.8/references/untitled/tweterm/iterm#refresh-force_refresh)メソッドによる再描画を行います。

```cpp
// ターミナル the_screen の宣言
TWETerm_M5_Console the_screen(64, 20, { 0, 16, 320, 192 }, M5);

void setup() {
    // ... M5の初期化など
    M5.begin(true, false, true, false);
    
    // フォントの生成と登録
    createFontMP12(13, 0, 0);
    the_screen.set_font(13); // フォントIDの指定
    the_screen.force_refresh(); // 再描画
}
```

ターミナルのフォント変更は、[`set_font()`](/0.8/references/untitled/tweterm/tweterm_m5_console#set_font)によりフォントを指定し、その後、[`clear_screen()`](/0.8/references/untitled/tweterm/iterm#clear-home-clear_screen)と[`force_refresh()`](/0.8/references/untitled/tweterm/iterm#refresh-force_refresh)を呼び出します。

```cpp
the_screen.set_font(11);    // フォントの指定
the_screen.clear_screen();  // ターミナルを文字バッファをクリアする
the_screen.force_refresh(); // 完全再描画を行う
```

## フォントの描画について

[`drawChar()`](/0.8/references/untitled/twefont/drawchar)関数を用いて描画することができます。


# FontDef

フォント定義クラス

フォント定義と関連する手続きをまとめたクラスです。

このクラスオブジェクトはフォントジェネレータ[`createFont???()`](/0.8/references/untitled/twefont/createfont)によりライブラリ内部で生成・管理され、ユーザがコンストラクタを用いて直接オブジェクトを生成することはありません。

## メソッド（ユーザ利用）

### get\_width()

```cpp
uint8_t get_width()
uint8_t get_width(uint16_t wc)
```

パラメータを省略した場合は、フォントのシングル幅文字の幅を返します。この値にはフォント生成時に指定した文字間スペースも含まれます。

`wc`を指定した場合は、Unicode `wc`に対応するフォントの幅を返します。日本語などダブル幅のフォントの場合は、シングル幅の２倍の値が戻ります。

### get\_height()

```cpp
uint8_t get_height()
```

フォントの高さを返します。この値にはフォント生成時に指定した行間スペースも含まれます。

### is\_default()

```cpp
bool is_default()
```

デフォルトフォントのオブジェクトである場合 `true` を返します。

{% hint style="info" %}
デフォルトフォントのオブジェクトは[`createFont???()`](/0.8/references/untitled/twefont/createfont)や[`queryFont()`](/0.8/references/untitled/twefont/queryfont)のエラー時など例外時にも利用されます。
{% endhint %}

### get\_font\_code()

```cpp
uint8_t get_font_code()
```

フォントIDを取得します。

## メソッド（内部利用）

### find\_font\_index()

```cpp
int find_font_index(uint16_t c)
```

Unicode `c` に対応する、ダブル幅フォント定義配列インデックスを検索します。

戻り値は、字形データが存在する場合は、インデックス配列のインデックス（データ配列のインデックスが計算できる）、存在しない場合は`-1`を返します。

フォント定義は、インデックス配列、データ配列の２つから構成されます。インデックス配列の各値は昇順に並んだ Unicode になっていて、データ配列のインデックスに対応しています。

以下の例ではインデックス配列の IDX=829 が U+5a2f で "娯" という文字です。データ配列の対応する番地を参照すれば、この字形データが格納されています。

```cpp
// インデックス配列 M+10dotフォント
const uint16_t font_mplus_f10j_idx[FONT_MP10_DBL_CHARS] {
  0x0391, // IDX=0 U+0391
  0x0392, // IDX=1 U+0392
  0x0393, // IDX=2 U+0392
  ...
  0x5a2f, // IDX=892 U+5a2f (娯)
  ...
}

// データ配列
const uint8_t font_mplus_f10j_data[FONT_MP10_DBL_CHARS*FONT_MP10_DATA_ROWS*2] = {
  ...
  0x00,0x00,0x47,0x80,0x54,0x80,0xf7,0x80,0x50,0x00,
　  0x5f,0x80,0xa0,0x80,0xff,0x80,0x45,0x00,0x58,0x80, // 3864/u5a2f idx=892
  ...
};

```

インデックス配列内の値は昇順に並ぶよう構成した目的は、本関数で実装されている二分探索を利用するためです。


# createFont???()

フォントジェネレータ関数

フォントジェネレータ関数は、収録フォントごとに定義されています。関数パラメータは共通で、以下のようになります。

```cpp
const FontDef& createFontShinonome16(
    uint8_t id,
    uint8_t line_space = 0,
    uint8_t char_space = 0,
    uint32_t u32Opt = 0);
```

上記は東雲フォント16ドット版（常用漢字収録）のジェネレータの例です。

`id`は、ユーザが指定するフォントID。

`line_space`はフォントの行間スペースをピクセル数で指定します。

`char_space`は文字間スペースでピクセルで指定します。文字間スペースはシングル幅のフォントの指定です。ダブル幅のフォントの場合は倍になります。

`u32Opt`は、フォントのオプションを指定します。オプションは`U32_OPT_FONT_TATEBAI`と`U32_OPT_FONT_YOKOBAI`があり、論理和で指定します。

フォントジェネレータの戻り値は`FontDef&`になっています。この戻り値はライブラリ内部のフォント管理テーブルに生成されたオブジェクトへの参照です。既に登録済みのIDであるばあいは、そのIDに対して上書きを行います。登録できなかった場合は`.is_default()`メソッドが`true`になるデフォルトフォントが返されます。

```cpp
if (createFontShinonome16(11).is_default()) {
    // フォント作成のエラー
}
```


# drawChar()

フォントの描画

フォントをスクリーン上に描画します。

以下の例では、Bボタンを押すたびに、事前に生成したフォントID=10のフォントを用いて固定の文字列を描画します。

```cpp
void loop() {
	if (M5.BtnB.wasReleased()) {
		static int idx = 0;
		const char msg[3][16] = {
		   	"ABCD1234", "あいうえ", "やあ世界" };
		
		auto&& font = queryFont(10); // use font ID=10
		
		drawChar(
			font,         // フォント指定
			0, 240 - 30,  // 左上座標 (X,Y)
			msg[idx],     // 文字列
			RED,          // 文字色
			BLACK,        // 背景色
			0x01);        // オプション 0x01:BOLD
		
		idx++; if (idx >= 3) idx = 0;
	}
}
```

## 関数定義

```cpp
int16_t drawChar(const FontDef& font, int32_t x, int32_t y, 
    uint16_t c, uint32_t color, uint32_t bg, uint8_t opt);
    
int16_t drawChar(const FontDef& font, int32_t x, int32_t y,
    const char *s, uint32_t color, uint32_t bg, uint8_t opt);
    
int16_t drawChar(const FontDef& font, int32_t x, int32_t y,
    const uint16_t* s, uint32_t color, uint32_t bg, uint8_t opt);
```

`font`を用い、左上座標(`x`,`y`)に、文字色`fg`、背景色`bg`、オプション`opt`で文字を描画します。

`uint16_t c`をパラメターとして与えた場合は、Unicode `c` に対応する文字を１文字描画します。

`const char *s`をパラメータとして与えた場合は、`s`をUTF-8としてデコードし、文字列として出力します。

`const uint16_t* s`をパラメータとして与えた場合は、Unicode文字列として描画します。

`opt`はオプションのビットマップです。以下の指定が可能です。

* `0x01` - 太字指定
* `0x02` - カーソルの描画

戻り値は、描画が行われればX(幅)方向に描画したピクセル数を返し、エラーなどが発生したときは0を返します。

## 実装について

実装時では以下のM5StackのAPIを利用しています。

* `M5.Lcd.startWrite()`
* `M5.Lcd.setWindow()`
* `M5.Lcd.endWrite()`
* `tft_Write_16()`


# queryFont()

フォントクラスオブジェクトの取得

[`createFont???()`](/0.8/references/untitled/twefont/createfont) によりフォント生成したクラスオブジェクトを参照します。

## 関数定義

```cpp
const FontDef& queryFont(uint8_t id)
```

idにはフォント生成時に指定したフォントIDを指定します。

戻り値は [`FontDef&`](/0.8/references/untitled/twefont/fontdef) で、ライブラリ内のフォント管理配列内に格納されたオブジェクトへの参照です。

idに0を指定すると、フォントID=0のデフォルトフォントを戻します。存在しないIDを指定した場合も、どうようにデフォルトフォントを戻します。`FontDef`オブジェクトがデフォルトフォントかどうかはメソッド`.is_default()`により判定できます。


# Basics

汎用クラス・関数


# TWE

基本クラス、関数




---

[Next Page](/llms-full.txt/1)

