このページのみ全てのページ
GitBook提供
1 / 71

1.0

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

The MWM5 Library / TWELITE STAGE

ライブラリについて

資料の取り扱いについてをご参照ください。 お気付きの点がありましたら、当サポート窓口にご連絡いただければ幸いです。

本資料の記述内容はライブラリとして取り扱い、ライブラリに適用されるMW-OSSLAを適用します。

本資料はlatestバージョンで、開発中の情報も含まれます。

本ライブラリMWM5は、M5StackとTWELITE をシリアル(UART)接続して、M5Stack上でTWELITEを利用することを目的としています。またライブラリを活用した (開発環境) のソースコードも含まれています。

  • TWELITEシリアルデータ形式のパーサーライブラリ

  • ターミナル画面描画を行うライブラリ

  • Windows/Linux/macOS向けウインドウ描画ライブラリ

  • TWELITE STAGE (TWELITE 開発・評価環境)

TWELITE STAGE
標準アプリ App_Twelite のパケットデータ表示例
TWELITE PALのパケット表示例

Parser

シリアル電文パーサー

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

  • TWESERCMD - 電文形式を解釈します

  • TWEFMT - 解釈した電文の内容を読み取ります

Examples

サンプルコード

Basics

汎用クラス・関数

TWEUTILS

ユーティリティ関数

Environment

環境

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

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

Using Library

ライブラリの使用

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

  • Arduino.h, M5Stack.h は mwm5.hより後でインクルードしてください。

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

PalEvent

PALイベントは、センサーなどの情報を直接送るのではなく、センサー情報を加工し一定の条件が成立したときに送信される情報です。例えば加速度センサーの静止状態から一定以上の加速度が検出された場合などです。

イベントデータが存在する場合はTwePacketPalの.is_PalEvent()がtrueになることで判定でき、.get_PalEvent()によりPalEventデータ構造を得られます。

Packet Types

パケット種別ごとの定義

対応するパケット種別は定義を参照してください。

Examples (console)

MWM5ライブラリには、シリアル入出力、画面出力など多岐にわたるコードが含まれます。ここでは、コマンドライン(コンソール)画面上で動作標準入出力のみを用いる単純なサンプルについて解説します。

サンプルはexamples_con/以下に格納します。

  • : App_Wings(または各アプリのUART出力)を解釈して、各パケットメッセージの概要を表示します。

TWESERCMD

書式パーサー

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

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

環境

内容

OS

Windows10 バージョン1903

M5Stack ハードウェア

M5stack BASIC

M5Stackライブラリ

0.2.9

E_PKT
glancer_con

Getting started

References

TWE

基本クラス、関数

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

  • パーサーライブラリの使用方法

  • ターミナルライブラリの使用方法

#include <mwm5.h>
#include <M5Stack.h>
	struct PalEvent {
		// uint8_t b_stored;       // 格納されていたら true
		uint8_t u8event_source; // 予備
		uint8_t u8event_id;     // イベントID
		uint32_t u32event_param;// イベントパラメータ
	};
      [IParser]
          ↑
     [AsciiParser]

License

ライセンス

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

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

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

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

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

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

idenify_packet_type()

パケット種別判定

パケットデータのバイト列を入力として、パケットの種別を判定します。また既に生成済みのspTwePacketオブジェクトの種別を返します。戻り値はE_PKTです。

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)

DataAppIO

TwePacketAppIOのデータ部分。

struct DataAppIO {
		//送信元のシリアル#
		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の状態ビットマップ (LSBから順にDI1,2,3,4,...)
		uint8_t DI_mask;

		// DIのアクティブ(使用なら1)ビットマップ(LSBから順にDI1,2,3,4,...)
		uint8_t DI_active_mask;
		
		// DIが割り込み由来かどうかのビットマップ(LSBから順にDI1,2,3,4,...)
		uint16_t DI_int_mask;
};

refTwePacket()

spTwePacketオブジェクトの参照

本関数はspTwePacketオブジェクトをTwePacket&として参照します。

TwePacket& refTwePacket(spTwePacket& p)

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

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

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

DataPal

PAL共通データ

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

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 u8pkt_type;  // Packet タイプ (0:PalDataInfo付, 1:シンプル)
	uint8_t u8sensors;		// データに含まれるセンサーデータの数 (MSB=1はエラー)
	uint8_t u8snsdatalen; // センサーデータ長(バイト数), MSB=1は動的確保

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

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

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

u8pkt_typeが0の時はPalDataInfoデータ構造が付与された形式となります。比較的複雑な情報を格納するための形式です。1の場合はPAL基板ごとに決められた標準的な形式でデータが格納されています。

PalDataInfo

PAL/CUEのパケットがどのような原因で送信されたかなどを付加する補助情報です。例えばタイマー送信かセンサーの発報によるものかなどの情報が含まれます。TwePacketPalが本構造体を継承しており単独では使用しません。

struct PalDataInfo {
  E_SNSCD e_data_source;
  E_CAUSE e_data_cause;
};

メンバー名

内容

e_data_source

パケット送信を行う原因となるセンサーやタイマー。

e_data_cause

e_data_sourceの状態が変化した場合に送信されたからといった要因を示す。E_CAUSEに定義され、例えば「VALUE_OVER_LIMITセンサー値が閾値以上」といった要因が定義される。

newTwePacket()

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

パケットデータのバイト列を入力として、パケット種別の判定と、種別に応じたspTwePacketオブジェクトを生成します。

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()を用いてパケットの種別E_PKTが特定できている場合はeTypeを与えます。

戻り値はspTwePacketです。

PalMot

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

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

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

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

queryFont()

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

createFont???() によりフォント生成したクラスオブジェクトを参照します。

関数定義

const FontDef& queryFont(uint8_t id)

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

戻り値は FontDef& で、ライブラリ内のフォント管理配列内に格納されたオブジェクトへの参照です。

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

SmplBuf_ByteL<N>

template <int N> using SmplBuf_ByteL = 
SimpleBufferL<uint8_t, N, _SimpleBuffer_DummyStreamOut, 1>;

uint8_t 型のバイト配列ですが、メモリ確保をローカル変数とします。一時的なオブジェクトして利用することを想定しています。

PalMag

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

struct PalMag : public PalBase {
	const uint8_t U8VARS_CT = 2; // センサー数
	const uint32_t STORE_COMP_MASK = (1 << U8VARS_CT) - 1; // 全コンプのマスク
	
  uint16_t u16Volt; // 電源電圧
	uint8_t u8MagStat; // 開閉状況 (0:磁石が遠い, 1/2:磁石検出)
	uint8_t bRegularTransmit; // 定期送信の時に 1, 磁石検出では 0
};

PalAmb

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

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

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

SmplBuf_WCharL<int>

template <int N> using SmplBuf_WCharL =
SimpleBufferL<wchar_t, N, _SimpleBuffer_DummyStreamOut, 1>;

uint8_t 型のバイト配列ですが、メモリ確保をローカル変数とします。一時的なオブジェクトして利用することを想定しています。

E_CAUSE

パケット送信要因。PalDataInfo::e_data_causeで使用されます。

名前

解説

EVENT

イベントが発生した。

VALUE_CHANGED

センサー値が変更された。

VALUE_OVER_LIMIT

センサー値が閾値以上である。

VALUE_UNDER_LIMIT

TwePacketAppIO

TwePacketAppIOクラスは、標準アプリApp_IOのシリアルメッセージ(0x81)を解釈したものです。

class TwePacketAppIO : public TwePacket, public DataAppIO { ... };

パケットデータ内の諸情報はparse()実行後にDataTweliteに格納されます。

spTwePacketからの参照

TwePacketAppIO& refTwePacketAppIO(spTwePacket& p)

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

実行例

TwePacket, spTwePacket

パケットオブジェクト

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

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

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

メソッド

TwePacket() - コンストラクタ

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

~TwePacket() - デストラクタ

get_type()

パケットの種別を型で返します。

parse()

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

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

戻り値は 型 で、成功時は解釈されたパケット種別を、エラー時に E_PKT::PKT_ERROR を返します。

TwePacketTwelite

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

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

パケットデータ内の諸情報はparse()実行後にDataTweliteに格納されます。

spTwePacketからの参照

TwePacketTwelite& refTwePacketTwelite(spTwePacket& p)

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

実行例

TwePacketAppUart

TwePacketAppUartクラスは、App_UARTの拡張書式を親機・中継器アプリApp_Wingsで受信したときの形式です。

class TwePacketAppUART : public TwePacket, public DataAppUART

spTwePacketからの参照

TwePacketAppUART& refTwePacketAppUART(spTwePacket& p)

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

実行例

E_PAL_DATA_TYPE

TWELITE PAL/CUEのパケットはTwePacketPal::get_PalDataType()により、どういった形式で格納されているかを判定します。

E_PKT

パケット種別定義

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

App_Wings の親機で出力されるアスキー書式に対応します。

TwePacketActStd

TwePacketActStdクラスは、アクト(Act)のサンプルなどの無線パケットを親機・中継器アプリApp_Wingsで受信したときの形式です。(App_UARTの拡張形式とほぼ同じになります)

spTwePacketからの参照

spTwePacketオブジェクトからTwePacketActStdオブジェクトを参照します。spTwePacketに

AsciiParser

アスキー形式のパーサー

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

メソッド

createFont???()

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

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

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

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

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

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

u32Optは、フォントのオプションを指定します。オプションはU32_OPT_FONT_TATEBAIとU32_OPT_FONT_YOKOBAI

DataAppUART (Act用)

TwePacketActStdのデータ部。 (書式はApp_UARTと同一でDataAppUART型を流用しています)

DataAppUART

TwePacketAppUartのデータ部。

E_PAL_PCB

PAL基板種別

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

TWETERM

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

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

基本的なクラス構造は以下のようになっています。クラスではターミナルの文字列バッファとその処理、クラスはITermの文字列描画部分を実装したものです。はITerm派生クラスオブジェクトが文字列をターミナルに投入するための基本的な手続きを提供しています。

SmplBuf_ByteSL<N>

uint8_t型のバイト配列ですが、メモリ確保をローカル変数とします。一時的なオブジェクトして利用することを想定しています。

またIStreamOutを継承しているため<<演算子などを利用したバッファへのデータ投入が可能です。wchar_t型の文字列に対してはUTF-8への変換を行います。

SmplBuf_WChar

wchar_t型のです。

MWM5では文字列を表す配列として多用されています。

SmplBuf_Byte

バイト配列

uint8_t型のです。

PalBase

PALセンサー共通データ

PALの各センサーのデータ構造体はすべてPalBaseを継承します。センサーデータの格納状況u32StoredMaskの情報が含まれます。

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

DataTwelite

App_Twelite データ

TwePacketTweliteのデータ部分。

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

センサー値が閾値以下である。

VALUE_WITHIN_RANGE

センサー値が範囲内である。

NODEF

要因としては未定義である。

NOTICE

LED付きのMOT (通知パル)

CUE

TWELITE CUE

名前

解説

NOPCB

基板未接続、エラー

MAG

マグネットセンサー付きのMAG (開閉センサーパル)

AMB

温湿度センサー、照度センサー付きのAMB (環境センサーパル)

MOT

加速度センサー付きのMOT (動作センサーパル)

MOT 動作センサーパル, NOTICE (通知パル) などで、特定条件を要件としてイベントを送信した場合。

EX_CUE_STD

TWELITE CUE 標準の形式。パケットプロパティ、イベントが含まれます。

TwePacketPal::u8palpcbによる判定の場合、より新しい形式に対応できません。

MAG/AMB/MOTの3種類の場合、下記例の^7の位置にある値(PAL基板種別)のMSB(0x80)が設定されている場合に限りu8palpcbによる判定からget_PalMag(),get_PalAmb() ,get_PalMot() によりオブジェクトを取り出すことが出来ます。

:80000000A8001C82012B1E01808103113008020D0C1130010203E40000000101EC6E
 ^^^^^^^1^2^^^3^^^^^^^4^5^6^7^8​^9^a^b^c^^^d^e^f^g^h^^^i^j^k^l^m^n^o^p

定義

内容

MAG_STD

マグネットセンサー付きのMAG (開閉センサーパル) 標準の形式。

AMB_STD

温湿度センサー、照度センサー付きのAMB (環境センサーパル) 標準の形式。

MOT_STD

加速度センサー付きのMOT (動作センサーパル) 標準の形式。

EVENT_ONLY

if (parse_ascii) { // アスキー形式のパーサーの解釈完了
  auto&& pkt = newTwePacket(parse_ascii.get_payload());
		
	if (identify_packet_type(pkt) == E_PKT::PKT_APPIO) { // パケット解釈成功時
	  auto&& x = refTwePacketAppIO(pkt);
		  
		the_screen // LCDスクリーン上のターミナルに情報を表示
				<< printfmt(":Lq=%d:Ad=%08X", x.u8lqi, x.u32addr_src)
				<< printfmt(":ID=%02X", x.u8addr_src)
				<< printfmt(":DI=%08b", x.DI_mask)
				;
	}
}
E_PKT
E_PKT
if (parse_ascii) { // アスキー形式のパーサーの解釈完了
  auto&& pkt = newTwePacket(parse_ascii.get_payload());
		
	if (identify_packet_type(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)
				;
	}
}
if (parse_ascii) { // アスキー形式のパーサーの解釈完了
  auto&& pkt = newTwePacket(parse_ascii.get_payload());
    
    if (identify_packet_type(pkt) == E_PKT::PKT_APPUART) { // パケット解釈成功時
      auto&& x = refTwePacketAppUART(pkt);
      
  		the_screen // LCDスクリーン上のターミナルに情報を表示
  				<< printfmt(":Lq=%d:Ad=%08X", x.u8lqi, x.u32addr_src)
  				<< printfmt(":ID=%02X", x.u8addr_src)
  				;
  
      if (x.payload.length() > 0) {
        auto p = act.payload.begin();
        
        // HEXで表示
        the_screen << ":DATA=";
        for(; p != x.payload.end(); ++p) {
    			the_screen << printfmt("%02X", *p);
    		}
      }
    }
  }
}
struct DataAppUART {
		/**
		 * 送信元のアドレス
		 */
		uint32_t u32addr_src;

		/**
		 * 送信先のアドレス
		 */
		uint32_t u32addr_dst;

		/**
		 * 送信元の論理ID
		 */
		uint8_t u8addr_src;

		/**
		 * 宛先の論理ID
		 */
		uint8_t u8addr_dst;

		/**
		 * LQI値
		 */
		uint8_t u8lqi;

		/**
		 * 未使用
		 */
		uint8_t u8response_id;

		/**
		 * データ部の長さ
		 */
		uint16_t u16paylen;

		/**
		 * データ部配列
		 */
		TWEUTILS::SmplBuf_Byte payload;
	};
struct DataAppUART {
		/**
		 * 送信元のアドレス
		 */
		uint32_t u32addr_src;

		/**
		 * 送信先のアドレス
		 */
		uint32_t u32addr_dst;

		/**
		 * 送信元の論理ID
		 */
		uint8_t u8addr_src;

		/**
		 * 宛先の論理ID
		 */
		uint8_t u8addr_dst;

		/**
		 * LQI値
		 */
		uint8_t u8lqi;

		/**
		 * 未使用
		 */
		uint8_t u8response_id;

		/**
		 * データ部の長さ
		 */
		uint16_t u16paylen;

		/**
		 * データ部配列
		 */
		TWEUTILS::SmplBuf_Byte payload;
	};
    [IStreamOut]
         ↑
      [ITerm]
         ↑
 [TWETerm_M5_Console]
ITerm
TWETerm_M5_Console
IStreamOut
template <int N> using SmplBuf_ByteSL =
SimpleBufferL<uint8_t, N, TWE::IStreamOut, 1>;
SmplBuf_ByteSL<64> t;

t << "hello";
t << printfmt("-%d", 123);

t.clear();
t << L"こんにちわ"; // convert into UTF-8

t.clear();
t << SmplBuf_WChar(L"こんにちは");
typedef SimpleBuffer<wchar_t, _SimpleBuffer_DummyStreamOut, 1> SmplBuf_WChar;
SimpleBuffer
typedef SimpleBuffer<uint8_t, _SimpleBuffer_DummyStreamOut, 1> SmplBuf_Byte;
SimpleBuffer
	struct PalBase {
		uint32_t u32StoredMask;
	};
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;
};
TwePacketActStd
以外が格納されている場合は、未解釈のオブジェクトを戻します。

実行例

class TwePacketActStd : public TwePacketAppUART
TwePacketActStd& refTwePacketActStd(spTwePacket& p)
AsciiParser() - コンストラクタ

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

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

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

_u8Parse()

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

_vOutput()

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

s_Output(), vPutByte()

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

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

があり、論理和で指定します。

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

const FontDef& createFontShinonome16(
    uint8_t id,
    uint8_t line_space = 0,
    uint8_t char_space = 0,
    uint32_t u32Opt = 0);
if (createFontShinonome16(11).is_default()) {
    // フォント作成のエラー
}
TwePacket(E_PKT ptyp = E_PKT::PKT_ERROR)
virtual ~TwePacket()
E_PKT get_type()
virtual E_PKT parse(uint8_t* p, uint8_t u8len)
if (parse_ascii) { // アスキー形式のパーサーの解釈完了
  auto&& pkt = newTwePacket(parse_ascii.get_payload());
    
    if (identify_packet_type(pkt) == E_PKT::PKT_ACT_STD) { // パケット解釈成功時
      auto&& x = refTwePacketActStd(pkt);
      
  		the_screen // LCDスクリーン上のターミナルに情報を表示
  				<< printfmt(":Lq=%d:Ad=%08X", x.u8lqi, x.u32addr_src)
  				<< printfmt(":ID=%02X", x.u8addr_src)
  				;
  
      if (x.payload.length() > 5) {
        auto p = act.payload.begin();
        
        // 先頭4バイトはActコードで無線パケット先頭に設定した4文字アルファベット
        the_screen << ":FOURCC=";
        for(int i = 0; i < 4; ++i, ++p)
            the_screen << printfmt("%c", *p);
        
        // 残りはHEXで表示(Actコードごとによって違う)
        the_screen << ":DATA=";
        for(; p != x.payload.end(); ++p) {
    			the_screen << printfmt("%02X", *p);
    		}
      }
    }
  }
}
AsciiParser(size_t maxbuffsiz)
AsciiParser(SmplBuf_Byte& bobj)
uint8_t AsciiParser::_u8Parse(char_t u8byte)
void AsciiParser::_vOutput(TWEUTILS::SmplBuf_Byte& bobj, TWE::IStreamOut& p)
static void vPutByte(uint8_t u8byte, uint8_t* pu8lrc, TWE::IStreamOut& p)
static void s_vOutput(TWEUTILS::SmplBuf_Byte& bobj, TWE::IStreamOut& p)

標準アプリ App_Twelite の を解釈したもの

PKT_PAL

のシリアル形式を解釈したもの

PKT_APPIO

リモコンアプリ のを解釈したもの

PKT_APPUART

シリアル通信アプリ のを解釈したもの。

PKT_APPTAG

無線タグアプリApp_TagのUARTメッセージを解釈したもの。センサ固有部分は解釈されずpayloadとしてバイト列を報告します。

PKT_ACT_STD

のサンプルなどで使用される出力書式。

名前

解説

PKT_ERROR

パケット解釈前やパケット種別が特定できないなど、TwePacketには意味のあるデータが格納されていない

PKT_TWELITE

Terminal

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

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

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

  • 固定幅の画面構成を行うこと

  • 日本語の表示が可能であること

  • ソースコード中に直接日本語文字列を含められるようにすること

    • UTF-8でソースコードを記述する前提とする

    • 内部処理をUnicodeとすること

    • UTF-8デコードが出来るようにすること

  • 旧来のキャラクター型のインタフェースを実装できるよう、いくつかのエスケープシーケンスを実装しておくこと

    • ただしANSIエスケープシーケンスの完全な互換性を目的とはしない

  • 文字色、背景色、太字といった表示属性に対応すること

  • カーソルを表示・非表示にできること

  • 画面の順方向のスクロールに対応すること

  • カラム数を超えて文字列を出力した場合は、折り返しを行えること

    • 右端カラムへの文字出力を行った場合、その時点では折り返しを行わないようにすること

      • 上記を実装は、折り返し処理の実装より優先すること

  • 複数のターミナル表示を画面上に同時に表示できること

  • 毎回全画面書き換えといったような描画パフォーマンスの悪い実装でないこと

    • 変更がある行のみを書き換える行単位の描画を行うようにした

  • フォントを選択できること

    • 配布可能なフォントをライブラリ内に同梱しておくこと

    • より大きな文字を表示するため、倍角表示に対応すること

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

  • ターミナル画面上の文字列を管理するクラスや関数をまとめた

  • フォントの管理と描画に関連するクラスや関数をまとめた

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

Print Formatted

printfmt, fPrintf(), snPrintf()

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

本ライブラリではMacro Poland氏のprintf,sprintfライブラリを利用しています。 https://github.com/mpaland/printf

printfmt

ストリームオブジェクトに対して<<演算子の右オペランドとして利用します。

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

fPrintf()

ストリームオブジェクトを出力先としてfprintfと同じ処理を行います。

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

snPrintf()

snprintfの処理を行います。

Terminal

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

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

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

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

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

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

AppTwelite_Recv

標準アプリケーション

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

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

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

drawChar()

フォントの描画

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

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

関数定義

fontを用い、左上座標(x,y

InputQueue

固定長キューFixedQueueを利用し、入力キューとしていくつかのメソッドを追加しています。

  • キューが一杯になった後にpush()で要素を追加した場合は、末尾の要素を抹消して追加します。

TWETERM
TWEFONT
0x81 コマンド
TWELITE PAL
App_IO
UARTメッセージ
App_UART
拡張書式
アクト(Act)
    [spTwePacket] ←(生成)- newTwePacket()
          ◆ 
          |(shared_ptr)
          |
      [TwePacket]    [DataPal]
              ↑        ↑
            [TwePacketPal] -(生成)-> [PalAmb], [PalMot], ...
IStreamOut
IStreamOut
)に、文字色
fg
、背景色
bg
、オプション
opt
で文字を描画します。

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

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

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

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

#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";
    }
}
定義・メンバー変数

メソッド

FixedQueue() - コンストラクタ

nを最大値としてキューを初期化・生成します。

setup()

nを最大値としてキューを初期化・生成します。

push()

キューに要素を追加します。

キューが一杯になった後にpush()で要素を追加した場合は、末尾の要素を抹消して追加します。

pop_front(), read()

キューの先頭要素を戻り値し、またキューよりその要素を削除します。

is_full()

キューが一杯になるとtrueを返します。

available()

キューに要素があればtrue(!=0)、なければfalse(==0)といった判定を目的とします。

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);
}
printfmt(const char*fmt, ...)
ISteramOut& operator << (IStreamOut& strm, printfmt f)

//例
  the_screen << printfmt("%04X", 0x12ab) << crlf;
int fPrintf(TWE::IStreamOut& fp, const char* format, ...)
int snPrintf(char* buffer, size_t count, const char* format, ...)
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;
	}
}
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);
// ターミナルオブジェクトの生成
TWETerm_M5_Console the_screen(
    64, 20,
    { 0, 16, 320, 192 }, M5);
void setup() {
  ...
  // create font as #10 (東雲フォント16)
  createFontShinonome16(10, 0, 0);
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.
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;
    }
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";
    }
template <typename T>
class InputQueue
protected:
		std::unique_ptr<TWEUTILS::FixedQueue<T>> _cue;
InputQueue(size_type n)
void setup(size_type n)
void push(T c)
T pop_front()
T read()
bool is_full()
int available()
setup()

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

LCDスクリーンの初期化を行います。処理の内容はscreen.cにあり、ターミナル画面の初期設定を行います。

loop()

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

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

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

update_screen()

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

この関数内ではエスケープシーケンスを用いて画面の表示位置などを制御しています。

パケットデータはspLastPacketに格納されています。これの内容を紐解くにはretTwePacketTwelite()を呼び出します。xの値を読み出して、これに対応して画面を更新します。

画面例

TwePacketPal

TWELITE PALのパケット

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

PAL共通データはDataPalに定義されています。

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

spTwePacketからの参照

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

実行例

パケットデータ定関数

has_palEvent()

パケットにイベント情報が含まれるかを判定します。

get_PalDataType()

パケットの種別を判定します。種別はとして判定されます。

has_data_info()

パケットプロパティ(パケットの補助情報)が含まれているか判定します。含まれている場合はPalDataInfoで定義されるメンバーにアクセスできます。

is_data_source_timer()

パケットプロパティが存在する場合に呼び出しは有効。タイマー由来からの送信の場合trueを返します。

ジェネレータ関数

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

get_PalMag()

.u8palpcb==E_PAL_PCB::MAGの場合、開閉センサーパルのデータを取り出します。

get_PalAmb()

.u8palpcb==E_PAL_PCB::AMBの場合、環境センサーパルのデータを取り出します。

get_PalMot()

.u8palpcb==E_PAL_PCB::MOTの場合、動作センサーパルのデータを取り出します。

get_PalEvent()

.is_PalEvent()がtrueの場合PalEvent(PALイベント)を取り出します。

Hardware

ハードウェア接続

Windows, macOS, Linux

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

参考: TWELITE STAGE の解説

ESP32 (M5Stack)

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

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

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

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

Raspberry Pi 3B

Raspberry Pi では /dev/srial0 (TWELITE STAGEのマニュアル参照) を選択した場合(raspi-config などの設定等により異なる場合があります)、以下の配線にてシリアルポートの経由でTWELITE無線マイコンとのUART接続を行います。

  • TWELITE, RaspberryPi ともに製造元のマニュアルを参照ください。

  • DIP# は TWELITE DIP のピン番号です。

  • 上記配線は TWELITE 無線マイコンモジュールが安定稼働することを保証するものではありません。

ESC Sequence

エスケープシーケンス

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

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

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

エスケープシーケンス

意味

ESC [ n A

カーソルをn行上に移動する。(n省略時は1行)

ESC [ n B

カーソルをn行下に移動する。(n省略時は1行)

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

PAL_Recv

TWELITE PAL

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

画面例

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

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

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

setup()

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

LCDスクリーンの初期化を行います。処理の内容はscreen.cにあり、画面の初期設定を行います。

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

loop()

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

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

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

pkt_data

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

このクラスでは、パケットデータに含まれる送信元論理IDごとのデータを保存しています。新しいデータを受信したら都度更新します。

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

init_screen(), reinit_screen()

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

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

add_entry()

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

next_page(), prev_page(), set_page()

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

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

update_status()

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

update_term()

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

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

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

FontDef

フォント定義クラス

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

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

メソッド(ユーザ利用)

get_width()

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

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

get_height()

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

is_default()

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

デフォルトフォントのオブジェクトはやのエラー時など例外時にも利用されます。

get_font_code()

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

メソッド(内部利用)

find_font_index()

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

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

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

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

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

FixedQueue

固定長キュー

固定長のキューを実装します。

定義・メンバー変数

メソッド

FixedQueue() - コンストラクタ

nを最大値としてキューを初期化・生成します。

setup()

nを最大値としてキューを初期化・生成します。

push()

キューに要素を追加します。

push_no_assign()

キューに要素を追加しますが、キュー内部にはデータコピーせず、替わりにキュー内部の要素へのポインタを返します。

pop()

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

front()

先頭要素にアクセスします。

back()

末尾要素にアクセスします。

operator[] ()

配列のようにインデックスを指定して、キューの要素にアクセスします。

pop_front()

キューの先頭要素を戻り値し、またキューよりその要素を削除します。(pop()とfront()の組み合わせです)

empty()

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

size()

現在キューにあるアイテム個数を返します。

capacity()

キューの最大長を返します。

TWETerm_M5_Console

M5Stack用のLcd描画ターミナル

M5Stack の 320x240 LCD 用のターミナルの実装です。を実装しています。

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

メソッド

                          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-
Serial2.setRxBufferSize(512);
Serial2.begin(115200, SERIAL_8N1, 16, 17);
setup_screen();
check_for_serial();
process_input();
check_for_refresh();
  trm << "\033[H"; // カーソルホーム
...
		if (b) trm << "\033[1;1H" // カーソルを1行目に移動
			"\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" // カーソルを3行目に移動
			"\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";
		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";
class TwePacketPal : public TwePacket, public DataPal { ... };
TwePacketPal& refTwePacketPal(spTwePacket& p)
                      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-
template <typename T>
class FixedQueue
	public:
		typedef uint16_t size_type;
		typedef T value_type;

	private:
		std::unique_ptr<T[]> _p;
		size_type _head;
		size_type _tail;
		size_type _ct;
		size_type _size;

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

n=0 または省略 カーソル行より後ろを削除する   ※ 背景色を変更して呼び出すと行末まで指定した背景色でクリアします。

n=1 カーソル行より前を削除する

n=2 行全体を削除する

ESC [ n1 ; n2 ; ...; n4 m

描画属性の設定を行う。n1 .. n4 は任意数指定できる。

1 → 太字

7 → 反転

30 .. 37 → 文字色

40 .. 47 → 背景色

0 → 属性抹消

E_PAL_DATA_TYPE
PalMag
PalAmb
PalMot
createFont???()
queryFont()
TWETerm_M5_Console - コンストラクタ

ITermのコンストラクタにdrawAreaと_M5のパラメータが追加されています。

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

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

例:

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

メソッド

refresh()

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

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

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

set_font()

フォントを指定します。

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

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

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

font_width(), font_height(), font_id()

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

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

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

set_color()

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

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

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

色は565形式の16bit値です。TWEARD::color565()関数で計算します。

白色は ALMOST_WHITE で指定します。color565(255,255,255) または WHITE を指定すると描画が崩れます。

set_color_table()

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

例

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

ユーティリティ関数

color565()

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

ITerm
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
        }
      }
    }
  }
}
bool has_PalEvent()
bool is_PalEvent()
E_PAL_DATA_TYPE get_PalDataType()
bool has_data_info()
bool is_data_source_timer()
PalMag get_PalMag()
PalAmb get_PalAmb()
PalMot get_PalMot()
PalEvent get_PalEvent()
[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)
[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)
 [TWELITE]               [RaspberryPi]
  GND  ------------------ Gound (#6,#9,#14,#20,#25,#30,#34,#39のいずれか)
  TXD(DIO6,DIP#10) ------ GPIO15/UART0 RXD (#10)
  PRG(SPIMISO,DIP#7) ---- GPIO23 (#16)
  RXD(DIO7,DIP#3) ------- GPIO14/UART0 TXD (#8)
  RST(RESETN,DIP#21) ---- GPIO22 (#15)
  VCC  ------------------ 3V3 (#1,#17のいずれか)
  SET(DIO12,DIP#15) ----- GPIO12 (#32)
uint8_t get_width()
uint8_t get_width(uint16_t wc)
uint8_t get_height()
bool is_default()
uint8_t get_font_code()
int find_font_index(uint16_t c)
// インデックス配列 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
  ...
};
FixedQueue(size_type n)
void setup(size_type n)
void setup(size_type n)
void push(T&& c)
void push(const T& c)
T* push_no_assign()
void pop()
T& front()
T& back()
T& operator[] (int i)
T pop_front()
bool empty()
size_type size()
size_type capacity()
uint16_t c = color565(255, 127, 0); // R:255, G:127, B:0
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)
TWETerm_M5_Console the_screen(64, 20, { 0, 16, 320, 192 }, M5);
void loop() {
		static uint32_t u32mills;

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

			u32mills = u32now;
		}
}
void set_font(uint8_t u8id, uint8_t u8col_request = 0, uint8_t u8row_request = 0)
uint8_t font_id()
uint8_t font_width()
uint8_t font_height()
void set_color(uint16_t color, uint16_t bgcolor = 0)
void set_color_table(const uint16_t* ptbl)
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);
constexpr uint16_t color565(uint8_t r, uint8_t g, uint8_t b)
ターミナル
パーサー
spTwePacket

TermAttr

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

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

TermAttr() - コンストラクタ

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

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

  • TERM_ATTR_OFF (すべての属性のクリア)

  • 以下の論理和

    • TERM_BOLD

    • TERM_REVERSE

GChar::tAttr 定数

色設定以外の属性

文字色

背景色

SimpleBuffer

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

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

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

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

template 引数

いくつかの派生クラスを定義するためtemplateによる定義となっています。

メソッド

SimpleBuffer() - コストラクタ

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

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

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

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

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

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

attach()

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

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

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

begin(), end()

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

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

append()

⇒push_back()

push_back()

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

length(), length_max()

⇒size(), capacity()

size(), capacity()

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

redim()

⇒reserve()

reserve()

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

operator []

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

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

IParser

パーサーの基底クラス

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

メソッド

Parse(), operator << ()

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

state()

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

operator bool(), is_complete()

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

length()

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

operator[]()

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

get_payload()

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

set_payload()

パーサーオブジェクトを用いて出力を構築する際に、データ列をコピーします。コピー元はSmplBuf_Byteデータ構造またはuint8_t*型の先頭と終端+1ポインタの組み合わせです。

reinit()

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

operator << ()

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

メソッド

_u8Parse()

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

_vOutput()

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

状態

IStreamOut

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

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

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

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

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

operator <<

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

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

IStreamSpecial

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

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

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

Changes & Download

変更履歴、ダウンロード

1.0.8 - 2021-09-09 (MWSDK2021_09)

Building

ビルド(コンパイル)について

ESP32(M5Stack)

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

M5Stack Library -

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

ITerm

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

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

メソッド

ITerm() - コンストラクタ

Serial2.setRxBufferSize(512);
Serial2.begin(115200, SERIAL_8N1, 16, 17);
setup_screen();
pkt_data.init_screen(fmt_title);
check_for_serial();
process_input();
check_for_refresh();
void init_screen(const char *fmt_status) 
void reinit_screen()
bool add_entry(spTwePacket spobj)
void next_page()
void prev_page()
void set_page(int entry)
void update_status()
void update_term()
void update_term(spTwePacket pal_upd, bool update_all)
void putmsg(ITerm& trm, const char *msg) {
    trm << TermAttr(TERM_COLOR_FG_RED | TERM_BOLD);
    trm << msg;
    trm << TermAttr(TERM_ATTR_OFF);
}
TermAttr(GChar::tAttr attr = 0)
template <class T, 
          class SOUT=_SimpleBuffer_DummyStreamOut,
          int is_string_type=0>
class SimpleBuffer : public SOUT
IParser& Parse(uint8_t u8b)
IParser& operator << (char_t c)
virtual inline IStreamOut& operator ()(char_t c) = 0
virtual inline IStreamOut& write_w(wchar_t c)
class ITerm : public TWE::IStreamOut {
  // ... 各種定義
  
  ITerm& write(char_t c) {
    // ... 1バイト入力を受け付ける処理
  }
  
  // StreamOut::operator () の実装
  TWE::IStreamOut& operator ()(char_t c) {
    write(c);
    return *this;
  }
};

文字色のいずれか

  • 背景色のいずれか

  • マゼンタ

    TERM_COLOR_FG_CYAN

    シアン

    TERM_COLOR_FG_WHITE

    白

    マゼンタ

    TERM_COLOR_BG_CYAN

    シアン

    TERM_COLOR_BG_WHITE

    白

    設定

    意味

    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_BG_BLACK

    黒

    TERM_COLOR_BG_RED

    赤

    TERM_COLOR_BG_GREEN

    緑

    TERM_COLOR_BG_YELLOW

    黄

    TERM_COLOR_BG_BLUE

    青

    TERM_COLOR_FG_MAGENTA

    TERM_COLOR_BG_MAGENTA

    引数

    意味

    T

    データ型

    SOUT

    IStreamOut 派生クラスを継承することで<<演算子などを用いたデータ追加を可能とします。 IStreamOutクラスを継承するとメンバー変数が増えるため、デフォルトはダミーとなっています。

    is_sting_type

    1を指定すると、文字列型 (uint8_tやwchar_t) の場合に、NUL終端を意識したコードを有効にします。

    • バッファを確保する際に終端文字分を余分に確保する

    • 固定長配列によるコピーコンストラクトなどではNUL文字までをコピー対象とする

    系列の解釈にエラーがあった

    E_TWESERCMD_CHECKSUM_ERROR

    0x82

    系列は得られたがチェックサムエラーだった

    状態名

    値

    状態

    E_TWESERCMD_EMPTY

    0

    解釈前で、まだ系列のヘッダも認識できていない

    1..0x7F

    解釈中

    E_TWESERCMD_COMPLETE

    0x80

    系列が正しく解釈できた

    E_TWESERCMD_ERROR

    0x81

    printf("%d", n)に相当する出力を行う

    double

    printf("%.3%, n)に相当する出力を行う

    右オペランドの型

    解説

    char_t

    1バイト出力する

    const char *

    文字列を出力する

    wchar_t

    ワイド文字を出力する(派生クラスで対応がある場合)

    IStreamSpecial&

    特殊文字列を出力する

    printfmt

    printf()に相当する出力を行う

    const int

    SimpleBuffer()
    SimpleBuffer(T* p, uint16_t u16len, uint16_t u16maxlen)
    SimpleBuffer(uint16_t u16maxlen)
    SimpleBuffer(const SimpleBuffer<T>& ref)
    SimpleBuffer<T>& operator = (const SimpleBuffer<T>& ref)
    void attach(T* p, uint16_t l, uint16_t lm)
    SimpleBuffer<uint8_t> buf(128), buf_sub;
    
    buf_sub = buf;
    buf_sub.attach(buf.begin() + 16, 0, buf.max_length() - 16);
    T* begin()
    T* end()
    bool append(T c)
    void push_back(T c) { append(c); }
    uint16_t length()
    uint16_t size() { return length(); }
    
    uint16_t length_max()
    uint16_t capacity() { return length_max(); }
    bool redim(uint16_t len)
    bool reserve(uint16_t len) { return redim(len); }
    inline T& operator [] (int i)
    uint8_t state()
    operator bool()
    bool is_complete()
    uint16_t length()
    uint8_t operator[](int i)
    SmplBuf_Byte& get_payload()
    template <typename T>
    inline void set_payload(T& bobj)
    
    inline void set_payload(uint8_t *b, uint8_t *e)
    
    //例: buffに出力書式を格納する
    AsciiParser format_ascii; // パーサー(出力用)
    SmplBuf_ByteSL<256> buff; // 出力バッファ
    uint8_t buff_raw[] = "ABCDE"; // データ列
    
    format_ascii.set_payload(buff_raw, buff_raw + sizeof(buff_raw));
    
    buff << format_ascii;
    for(auto x : buff) putchar(x);
    
    virtual void reinit()
    IStreamOut& operator << (TWE::IStreamOut& lhs, IParser& rhs)
    IStreamOut& operator << (TWETERM::ITerm& lhs, IParser& rhs)
    virtual uint8_t _u8Parse(char_t u8b) = 0
    virtual inline void _vOutput(SmplBuf_Byte& bobj, IStreamOut& p) = 0
    inline IStreamOut& operator << (char_t c)
    inline IStreamOut& operator << (wchar_t c)
    inline IStreamOut& operator << (IStreamSpecial& sc)
    inline ITerm& operator << (ITerm& t, char_t c)
       { *static_cast<TWE::IStreamOut*>(&t) << c; return t; } 
    class IStreamSpecial {
    	public:
    		virtual inline IStreamOut& operator ()(IStreamOut& of) = 0;
    };
    class IStream_endl : public IStreamSpecial {
      inline IStreamOut& operator ()(IStreamOut& of) { 
        // ... CR LF の出力
        of ('\r');
        of ('\n');
      }
    };
    
    IStream_endl crlf;
    the_screen << "Hello World!" << crlf;
    ライブラリ

    汎用ライブラリとしての変更点を記載します。TWELITE_StageについてはTWELITE STAGEアプリを参照してください。

    [1.0.8]

    • ボーレート設定のためのインタフェースを追加 (set_baud_app())

    • sdl_main.c でメインループの終了時に1秒後にスレッドから_exit()を強制するようにした

    • 環境変数の内部設定を強化 (ビルド実行のためのMWSDKのディレクトリなど)

    • TWELITE_Stage 関連の機能修正

    [1.0.6]

    • RaspberryPi 用のビルド整備を行った

    [1.0.5]

    • シリアルポートについて raspberry pi では /dev/serial0 を利用できるようにした

    • シリアルポートのボーレートを 115200bps から変更できるようにした

    • シリアルポートについて専用設定していないFTDIデバイス(FT232,FT230)を開けるようにした

    • シリアルポートについて Windows の COM ポートを調べる手続き(_query_extra_device_info()を追加した

    1.0.3 - 2020-12-03 (MWSDK2020_12)

    https://github.com/monowireless/mwm5/releases/tag/1.0.3

    TWELITE STAGE アプリの変更点

    https://stage.twelite.info/v/1.0/changes

    ライブラリ

    汎用ライブラリとしての変更点を記載します。TWELITE_StageについてはTWELITE STAGEアプリを参照してください。

    [1.0.3]

    • Apple Silicon 対応

      • osx/sersrv_ftdiを作成、シリアルポートの入出力を標準入出力経由で行う。FTDI D2XX arm64用ライブラリが現時点で存在しないため、このコマンドをx86_64としてビルドしTWELITE_Stageアプリケーションから開くようにした

      • TweCmdPipeInOut(twe_file.cpp, hpp)クラスの追加。コマンドの標準入出力を処理する

    • Windows, macOS の SDL2 ライブラリをビルド。バージョン2.0.12、スタティックライブラリのみ

    [1.0.2]

    • シリアルポートの再オープン処理をファームウェア書き換え前などに挿入しするため Serial2.reopen() を追加

    • ファームウェア書き換え時のベリファイに対応(twe_firmprog.cpp, hpp)

    [1.0.0]

    • TWELITE CUE 対応

    • Windows はスタティックリンクに対応

    • Raspberry Pi3B の暫定対応

    • シリアルポート関連のソースの整理 (src/gen/serial_???)

    • パーサーのコード整理を実施 (src/twe_fmt???)

    • ITerm::move_cursorの戻り値をITerm&とした。

    • ITerm& ITerm::operator() (int x, int y)の追加。カーソル移動を容易に。

    [0.9.10]

    • make_full_path() のパラメータに const_char* を許容するようにした

    • TweCwdでサンプル等のワークスペース(Act_samples, Wkt_TweAppsなど)のサーチパスを 1. 起動時のCWD

      1. 実行形式格納場所 3. MWSDK/.. 4. MWSDK とした。

    0.9.8 - 2020-10-08

    https://github.com/monowireless/mwm5/releases/tag/0.9.8

    TWELITE STAGE アプリ (example/TWELITE_Stage)

    • ビューア一覧表示に[ウェブ]ボタンを追加し、関連サイトを開く機能を追加

    • ビューア>コマンダーの追加

      • 標準アプリ 0x80 コマンド

      • NOTICE PALのLED制御 (App_Wingsにコマンドを送付)

    • ビューア>PALビューアのNOTICE PAL対応。

    • Actエクストラのメニューを追加

      • Act_samplesより高度なもの

      • 外部のオープンソースライブラリ(センサー手続きなど)を利用したもの

    • マウスによる操作を拡大 (リスト、ボタン、タブ)

      • マウス移動でフォーカス、左クリックで確定、右クリックは[ESC]キー入力

    • 画面表示負荷の低減

      • アプリケーションがバックグラウンドの時はスクリーンセーバーを無効にした

      • アプリケーションがバックグラウンドの時は、描画回数を減らして CPU 負荷を減らした

    • ビルドプロジェクト(Act, TWE_Apps, Actエクストラ)の一覧の機能強化

      • 項目選択時に下部に概要を表示 (000desc.txtを読み込む。TWE_Descクラスにより処理)

      • プロジェクトフォルダを開く(またはVSCodeで開く)機能

      • 関連ウェブサイトを開く機能

    • ログ(シリアルポート入出力)機能の追加

      • (Alt/Cmd+L)でログの開始・終了

      • ログファイルを {TWELITE_Stage 実行形式のあるディレクトリ}/log に格納

      • ファイル名は twestage_{日付-時刻}.log

    • その他、変更・修正など

      • シリアル(FTDI)デバイス名、IDの表示方法を変更

      • App_UARTでインタラクティブモードに遷移しなかった問題を修正

      • ディレクトリドロップ時の挙動を変更した (これまではバイナリ書き込みになる場合があったが、メニュー遷移とした)

    ライブラリ

    • NOTICE PALへの対応 (パーサー)

    • パーサーを標準入出力で動作できるようにビルド定義やソースを調整 (examples_con/glancer)

    • ITermの機能追加、不具合修正

      • すでに文字列が表示されている部分にダブルバイト文字を上書きしたとき、上書きした以降の表示が左右にズレていたが、完全ではないながらズレを抑制するコードを追加

      • ITerm::clear_line() を追加。行表示を抹消と、空白で埋める(表示は変わらないが

        空白データが行末まで追加される)

      • ITerm::move_cursor()を追加。エスケースシーケンスの記述が煩雑であったため。

    • GUI関連クラスの追加

      • TWE_Widget : ウィジェットの基底クラス

      • TWE_WidSet_Buttons : 複数ボタンの一括管理

    • その他変更、修正など

      • the_app.set_nextapp() を廃止し the_app.exit() に統合

      • the_keyboard.peek_a_byte() を追加

    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 用のビルド対応

    • 設定ライブラリ twesettings を追加

    0.4.2 - 2020-02-17

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

    初期バージョン

    https://github.com/monowireless/mwm5/releases/tag/1.0.8
    MWM5ライブラリのインストール

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

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

    外部ライブラリ

    • PS2Keyboard - 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 のパッケージをダウンロードして利用します。

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

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

    Linux

    以下が必要になります。

    • gcc9 (gcc-9, g++-9)

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

    ビルド方法

    M5Stack

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

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

    TWELITE_Stage アプリは examples/TWELITE_Stage にあります。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用の諸定義

    デバッグについて

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

    参考OS X用の定義

    • stopAtEntryは、main()の先頭で停止する設定で false でも構わない。

    • externalConsoleは、falseでないと起動しない。

    • cwdは、TWELITE STAGEの場合 MWSDKディレクトリを指定する。

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

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

    ~ITrem() - デストラクタ

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

    clear(), home(), clear_screen()

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

    clear_line()

    指定行をクリアする。fill_blankをtrueにすると指定行を空白で埋める。

    refresh(), force_refresh()

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

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

    write()

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

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

    日本語文字セットを表示するためのフォントの取り扱いについてはTWEFONT::FontDefを参照してください。

    get_height(), get_width()

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

    move_cursor()

    カーソル位置を指定場所に移動する。

    戻り値はITerm&(自身)で続けて出力メソッドなどを記述できる。

    operator <<

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

    パラメータ

    意味

    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) に該当する出力を行います

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

    メソッド名

    解説

    post_refresh()

    サブクラスでのrefresh()実装で、最後に呼び出す必要があります。必要な変数の初期化を行います。

    resize_screen()

    指定されたカラム数・行数に従い、バッファを再構成します。コンストラクタ指定の初期値を超えた指定はできません。

    column_idx_to_vis()

    uint16_t column_idx_to_vis(int16_t idx , int16_t lin) Unicodeで管理されている画面バッファー上の行位置・カラム位置(ともに0が先頭位置)から、画面上のカラム位置を計算します。日本語文字のような2文字幅文字を2カラムとして計算します。 "abcあいう"という文字列が格納された行のカラム位置4は"い"の文字が格納されますが、本関数で画面上のカラム位置を計算すると5になります。

    column_vis_to_idx()

    column_vis_to_idx(int16_t c_vis, int16_t lin)

    画面上の行位置、カラム位置(ともに0が先頭位置)から、画面バッファー上のカラム位置を計算します。日本語文字のような2文字幅文字を2カラムとして計算します。 "abcあいう"という文字列が格納された行の画面上のカラム位置5または6は"い"の文字が格納されますが、本関数で画面バッファー上のカラム位置を計算すると4になります。

    {
        "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"
    }
    ITerm(uint8_t u8c, uint8_t u8l)
    ITerm(uint8_t u8c, uint8_t u8l, SimpBuf_GChar* pAryLines, GChar* pBuff)
    // 静的メモリ確保例
    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
    virtual ~ITerm()
    void clear()
    void home()
    void clear_screen()
    void clear_line(uint8_t line, bool fill_blank = false)
    virtual void refresh() = 0
    void force_refresh()
    ITerm& write(wchar_t c)
    ITerm& write(char_t c)
    uint8_t get_height() // 行数
    uint8_t get_width()  // カラム数
    ITerm& move_cursor(uint8_t cols, uint8_t lines)
    ITerm& operator ()(int cols, int lines)
    
    //例
    the_screen(1, 3) << "HELLO"; // 2カラム目、4行目に移動して HELLO を出力
    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)

    ALt+Shift+m mwxライブラリ、Alt+Shift+t twesettingsライブラリを開く機能

  • ビルドメニュー中で選択中のフォルダやビルドエラーファイルを開けるようにした。

  • Shift+Alt/Cmd+L でログファイルディレクトリを開く

    ターミナル長押し時[C]でリセットに加え、画面クリアするようにした。

    TWE_WidSet_Tabs : タブ選択
  • TWE_Buttons : ボタンクラス

  • TWE_ListView : リスト、機能拡張

    • ステータスラインを設定できるように(最終行をページ番号とページ移動用のボタンとした)

    • listViewでエントリ数がページ行数の整数倍の時の振る舞いの不具合対応

  • アプリハンドラーのデータコンテキストを APP_HNDLR_DC, APP_HNDLR::use() を追加

    • アプリハンドラーを用いる場合、サブアプリのデストラクタで APP_HNDLR::on_close() を呼ぶように変更。

  • SimpleBuffer<uint8_t>::c_str() を uint8_t* ではなく const char* 型を戻すメンバ関数として再定義

  • SimpleBuffer<wchar_t>(const wchar_t (&)[N]) のコンストラクタに不具合があった

  • 文字列比較 beginsWith(), beginsWith_NoCase()を追加

  • arch_win.mk

    MingW64用の諸定義

    TermAttr a

    文字属性を設定します

    TWEFONT

    フォント定義や描画

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

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

    フォントの生成

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

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

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

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

    フォントデータについて

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

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

    • 大本がBDF形式を変換し、描画ルーチンに適したデータ構造とした

    • これらフォントをUnicodeとして取り扱うための参照テーブルを用意した

    • latin1補助文字 U+00A0~u+00FFについて、フォント定義があるものについては収録した

    • JIS X201 半角カナ U+FF61~U+FF9F について、フォント定義があるものについては収録した

    日本語フォントのジェネレータ

    ジェネレータ関数createFont???()には収録文字数に応じて3種類のジェネレータを用意しています。文字セットの生成は{MWM5ライブラリ}/fontsフォルダにあるスクリプトによって行います。

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

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

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

    常用漢字のみのジェネレータ (createFontShinonome12???(), createFontShinonome14???() , createFontShinonome16???())を呼び出します。

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

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

    8x6 LCD フォント

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

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

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

    フォントIDについて

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

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

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

    フォント情報の取得

    フォント情報にアクセスするためには、によりオブジェクトを取得し、諸情報を得ます。

    ターミナルでの使用

    ターミナルにフォントを指定するには、フォントの生成を行い、フォントIDをターミナルオブジェクトに指定します。フォント指定後はメソッドによる再描画を行います。

    ターミナルのフォント変更は、によりフォントを指定し、その後、とを呼び出します。

    フォントの描画について

    関数を用いて描画することができます。

    常用漢字(2645 文字)のフォントデータと、全収録(東雲フォントのみ、一部未収録字形があります)を用意した

    ジェネレータ

    ワイド幅 文字数

    収録

    createFont???_mini()

    576

    ASCII, latin1拡張, JIS-X201(半角カナ:0xA1-DF), かな,カナ,記号(選別), 漢字(選別:MWM5ソース中に出現する文字をスクリプトにより抽出・追加), スクリプト指定文字

    createFont???_std()

    createFont???()

    2645

    ASCII, latin1拡張, JIS-X201(半角カナ:0xA1-DF), かな,カナ,記号,常用漢字,スクリプト指定文字

    createFont???_full()

    6867

    ASCII, latin1拡張, JIS-X201(半角カナ:0xA1-DF), かな,カナ,記号,漢字,スクリプト指定文字

    queryFont()
    FontDef
    force_refresh()
    set_font()
    clear_screen()
    force_refresh()
    drawChar()
    TWEFONT::createFontShinonome16(10, 0, 0,
            TWEFONT::U32_OPT_FONT_TATEBAI | TWEFONT::U32_OPT_FONT_YOKOBAI);
    TWEFONT::createFontShinonome16(11, 1, 0);
    ●東雲フォントライセンス
    																	2001
    									 The Electronic Font Open Laboratory
    										http://openlab.ring.gr.jp/efont/
    
    このアーカイブに含まれるすべてのフォントデータ、ドキュメント、スクリプ
    ト類はすべて Public Domain で提供されています 。
    
    但し、日本に於いては現時点で著作権を放棄することは法律上不可能であり、
    AUTHORS に列挙されている作者がその権利を行使しないと宣言することで実質
    的な Public Domain であるとします。
    
    自由な改造、他フォーマットへの変換、組込み、再配布を行うことができます。
    同時に、これらはすべて完全に無保証です。
     M+ BITMAP FONTS            Copyright 2002-2005  COZ <[email protected]>
     -
    
     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.
    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 // ダブル幅の登録文字数
    			);
    // ターミナル 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(); // 再描画
    }
    the_screen.set_font(11);    // フォントの指定
    the_screen.clear_screen();  // ターミナルを文字バッファをクリアする
    the_screen.force_refresh(); // 完全再描画を行う

    Parser

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

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

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

    アスキー形式

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

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

    :00A01301FF123456B1[CR][LF]

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

    :00A01301FF123456X

    定義

    AsciiParser の使用方法

    オブジェクトの生成

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

    ASCII形式では実際のバイト数の約2倍の書式になります。例えば書式が200バイトの系列の場合は、実データは約100バイトになります。

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

    バイト列の解釈

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

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

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

    データ列の取り出し

    解釈済みの系列はget_payload()メソッドにて取得できます。get_payload()はの参照型を戻します。

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

    ASCII書式での出力

    Parserオブジェクトにデータ列が格納されている場合、>>演算子を用いることで書式をシリアルポート(Serial)やターミナル(ITerm)などに出力することが出来ます。

    通常は、解釈用のParserオブジェクトと出力用のParserオブジェクトは別々に用意します。

    解釈用のオブジェクトはシリアル入力など都度処理が行われます。このオブジェクトを一時的なデータ出力の目的で使用すると、解釈中の内部データが破壊されます。

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

    パケットの生成

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

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

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

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

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

    PALパケットの解釈

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

    PALのパケット書式は様々な派生があります。

    解釈時にまずイベントデータがあるかどうか取り出します。イベントが含まれるパケットはにより判定できます。イベントのみ受信できれば良い場合はで得られたイベントデータを参照して終了です。

    イベント以外の情報が欲しい場合は、一旦を用いてパケットのデータ構造を特定します。特定したデータ構造に応じたデータ取得を行います。

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

    2

    データ部の各バイトの和を8ビット幅で計算し2の補数をとります。つまりデータ部の各バイトの総和+チェックサムバイトを8ビット幅で計算すると0になります。 チェックサムバイトをアスキー文字列2文字で表現します。 例えば 00A01301FF123456 では 0x00 + 0xA0 + ... + 0x56 = 0x4F となり、この二の補数は0xB1 です。(つまり 0x4F + 0xB1 = 0x00)

    フッタ

    2

    [CR] (0x0D) [LF] (0x0A) を指定する。

    ======

    元データのバイト数

    バイト数

    解説

    ヘッダ

    1

    :(0x3A) コロンを指定します。

    データ部

    N

    2N

    元データの各バイトをアスキー文字列2文字(A-F は大文字)で表現します。 例えば 0x1F は 1 (0x31) F (0x46) と表現します。

    チェックサム

    SmplBuf_Byte
    pal.has_PalEvent()
    pal.get_PalEvent()
    pal.get_PalDataType()
    PalMag mag
    mag.u8MagStat

    // serial parser
    AsciiParser parse_ascii(256);
    while (Serial.available()) {
    		int c = Serial.read();
    		parse_ascii << char_t(c);
    		
    		if (parse_ascii) {
    		 		// completed!
    		}
    }
    if (parse_ascii) {
        auto&& p = parse_ascii.get_payload();
    }
    auto&& p = parse_ascii.get_payload();
    
    if (p[0] == 0x80) { ... } // 最初の要素
    int len = p.length();     // 要素数
    for (auto&& x : p) {      // 各要素にアクセス
      Serial.print(x, HEX);
      Serial.print(" "); }
    inline void IParser::operator >> (TWE::IStreamOut& fo_putchar)
    
    // パーサーオブジェクトの宣言
    AsciiParser o_parser(128);
    
    // コード例 
    auto&& b = o_parser.get_payload(); // SmplBuf_Byte&としてデータ列を参照
    b.clear();
    b << 0x00;
    b << 0x11;
    b << 0x22;
    b << 0x33;
    
    // ターミナルへ出力(ITerm)
    o_parser >> the_screen; // the_screen はターミナル(ITerm)
    //  -> :001122339A[改行]
    
    // バッファーへ出力
    SmplBuf_ByteSL<128> b1;
    o_parser >> b1;
    //  -> ":001122339A[CR][LF]"
    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
    }
    if (pkt == E_PKT::PKT_PAL) {
    	auto&& pal = refTwePacketPal(pkt);
    	PalEvent ev;
    
    	// acquire event data.
    	if (pal.has_PalEvent()) {
    		ev = pal.get_PalEvent();
    	}
    
    	switch(pal.get_PalDataType()) {
    	case E_PAL_DATA_TYPE::EVENT_ONLY: 
    			break;
    	
    	case E_PAL_DATA_TYPE::MAG_STD:
    			break;
    	
    	case E_PAL_DATA_TYPE::AMB_STD:
    			break;
    	
    	case E_PAL_DATA_TYPE::EX_CUE_STD:
    			break;
    	}
    case E_PAL_DATA_TYPE::MAG_STD:
      {
        // generate pal board specific data structure.
    		PalMag mag = pal.get_PalMag();
    		switch(mag.u8MagStat) {
    		case 0: /* no magnet */ break;
    		case 1: /* N Pole */ break;
    		case 2: /* S Pole */ break;
    		}
      }
      break;

    glancer_con

    App_Wings(または各アプリのUART出力)を解釈して、各パケットメッセージの概要を表示します。

    glancerは「glance(一目する)人」の意味の英単語です。

    ファイル構成

    プロジェクトフォルダ

    プロジェクトフォルダのソースコードはglancer.cppのみです。残りの必要なコードはsrc/..を参照します。

    他のプロジェクトなどに移植する場合

    MWM5ライブラリ全てのファイルは必要ありません。以下に必要なファイルを列挙します。

    上記で必要なファイル等は本ドキュメントと相違ある場合もあります。問題があればビルド時にエラーが表示されますので、エラーファイルに記述される"~not found"といったメッセージを頼りに足りないファイルをコピーしてください。

    ビルド

    ビルドに必要な定義

    定義

    内容

    TWE_STDINOUT_ONLY

    必須。標準入出力のみでビルドしたい場合に必要な定義です。

    TWE_HAS_MILLIS

    システム時間[ms]を得るmillis()を実装した場合は定義します。定義するとパケット情報にタイムスタンプ[ms]情報が含まれます。

    ※ millis() の実装には、システム時間を取得するためのOS依存のシステムコールが必要です。

    ビルド方法(VC++, Windows)

    examples/glancer_con というプロジェクト定義がありますので、こちらをビルドしてください。

    ビルド方法 (Makefile, gcc)

    make (GNU) と g++ (gcc より) が必要です。事前にインストールを済ませてください。

    Makefileを確認してください。以下の定義は必要に応じて書き換えます。

    定義名

    CXX

    g++-9 など実行名が違う場合に書き換えます。

    ※ Makefile 中で動作環境を判定している部分があり、この部分に定義が含まれます。

    DEFINES

    必要に応じて。 -D??? の定義をコンパイラに与えます。

    CFLAGS

    必要に応じて。コンパイルオプションをコンパイラに与えます。

    makeコマンドを実行します。

    millis()の実装

    実行例

    標準入力(通常の1バイト入力を用いているため改行が入力されるまでは入力文字列は評価されません)にアスキー形式のUARTメッセージを入力します。出力はPKT...となります。

    先頭の項目について解説します。

    項目

    内容

    Typ=

    です。

    Lq=

    LQI値が格納されます。

    Ad=

    32bitの送信元アドレスと8bitの送信元論理アドレスが格納されます。

    Vmv=

    電源電圧です。

    ※ メッセージにデータがない場合は 0000 となります。

    Tms=

    メッセージを受信したシステム時間が格納されます。

    ※ millis()を実装しておく必要があります。

    ソースコードの構造

    解説 Parser についても参照ください。

    ヘッダ部

    必要な標準ライブラリヘッダとMWM5ライブラリヘッダを読み込みます。

    MWM5ライブラリでは細かく名前空間が使用されていますが、利用時には煩雑ですのですべてusing namespaceにより省略可能とします。

    AsciiParserのクラスインスタンスを生成しておきます。

    メインループ

    このサンプルでは標準入力から1バイトずつ入力(std::cin::get())して、これをASCIIパーサーに投入します。

    標準入力ではシステムによって改行コードが異なりますが、ここでは'r'または'\n'のいずれが来ても'\r'を投入するようにしています。本パーサーは末尾の'\r'のみを評価するためです。

    投入後書式が完了した場合はparse_asciiはtrueを戻します。ここでデータを解釈します。

    ASCIIパーサーにより得られたバイト列は.get_payload()メソッドにより配列SmplBuf_Byte型として取り出すことが出来ます。

    このサンプルでは得られたバイト列を解釈して、どの種別のUARTメッセージなのか、何が格納されているのかを取り出せるようにします。

    バイト列を解釈する手続きはnewTwePacket()にバイト列paylを与えます。戻り値としてクラスオブジェクトpktを生成します。このオブジェクトはspTwePacket型の(メモリ管理を行う)スマートオブジェクトで、TwePacket型のオブジェクトを格納します。

    実際にはpktは種別に応じたTwePacket型の派生クラスのインスタンスが格納されています。この派生クラスの種別を確認するのがidentify_pkt_type()で、パケット種別としてE_PKT型の値を返します。

    パケット種別が得られたら、パケット種別ごとの表示関数print_???(spTwePacket)を呼び出しています。

    ここから先はパケット種別ごとに手続きが違います。以下にApp_Tweliteの解釈例、App_PALの解釈例を示します。

    print_app_twelite()

    App_Twelite の UARTメッセージの解釈を行います。まずspTwePacket型のままでは、App_Twelite特有のデータにアクセスできません。

    まずrefTwePacketTwelite()を呼びます。戻り値はTwePacketTweliteの参照型です。App_TweliteのUARTメッセージを解釈した特有のデータが含まれます。例えば.DI1はDI1のHigh/Low状態を示します。

    print_pal()

    TWELITE PALのUARTメッセージを解釈します。TWELITE PALは接続される基板によって異なるUARTメッセージが出力されます。このためデータ取得も一旦PALとして解釈してから、さらに個別のPAL基板のデータを取り出します。

    最初にPAL共通データをrefTwePacketPal()により取り出します。

    最初にパケットにイベントが含まれるかどうかの判定を行います。イベントパケットは例えば通知(NOTICE)PALの加速度センサーが静止状態から加速度を検出した(タップ)といった現象をイベント番号として伝えるものです。イベントパケットにも様々なデータが付加されますが、通常はイベント番号を参照します。

    パケットの種別を.get_PalDataType()により判定します。

    PAL基板の種別がわかったら、PAL基板の種別に応じた手続き(.get_PalMag()など)により、PAL基板特有のデータが含まれるクラスオブジェクトを得ます。

    上記の例では開閉センサーPALの磁気センサーの状態を得ています。

    examples/glancer      --- Makefile
    msc/MSC_glancer_con   --- VC++ Project 定義
    twe_fmt.cpp
    twe_fmt_actstd.cpp
    twe_fmt_appio.cpp
    twe_fmt_apptag.cpp
    twe_fmt_appuart.cpp
    twe_fmt_common.cpp
    twe_fmt_pal.cpp
    twe_fmt_twelite.cpp
    twe_sercmd.cpp
    twe_sercmd_ascii.cpp
    twe_utils_crc8.cpp
    
    twe_common.hpp
    twe_fmt.hpp
    twe_fmt_actstd.hpp
    twe_fmt_appio.hpp
    twe_fmt_apptag.hpp
    twe_fmt_appuart.hpp
    twe_fmt_common.hpp
    twe_fmt_pal.hpp
    twe_fmt_twelite.hpp
    twe_sercmd.hpp
    twe_sercmd_ascii.hpp
    twe_stream.hpp
    twe_utils.hpp
    twe_utils_crc8.hpp
    twe_utils_fixedque.hpp
    twe_utils_simplebuffer.hpp
    #if defined(_MSC_VER) || defined(__MINGW32__)
    #include "windows.h"
    # pragma comment(lib, "secur32.lib")
    # pragma comment(lib, "winmm.lib")
    # pragma comment(lib, "dmoguids.lib")
    # pragma comment(lib, "wmcodecdspuuid.lib")
    # pragma comment(lib, "msdmo.lib")
    # pragma comment(lib, "Strmiids.lib")
    #elif defined(__APPLE__) || defined(__linux)
    #include <sys/time.h>
    #endif
    
    uint32_t millis() {
    #if defined(_MSC_VER) || defined(__MINGW32__)
    	return (uint32_t)timeGetTime();
    #elif defined(__APPLE__) || defined(__linux)
    	timeval time;
    	gettimeofday(&time, NULL);
    	long ms = (time.tv_sec * 1000) + (time.tv_usec / 1000);
    	return (uint32_t)ms;
    #else
    	# warning "no millis() implementation."
    	return 0;
    #endif
    }
    :78811501C98201015A000391000C2E00810301FFFFFFFFFB
    PKT:Typ=1:Lq=201:Ad=0x8201015a(0x78):Vmv=3118:Tms=2656:App_Twelite:DI1..4=LHHH:AI1..4=(0028,----,----,----)
    :78A0028201015AFFFFFFFFA8000700112233AABBCCC6
    PKT:Typ=4:Lq=168:Ad=0x8201015a(0x78):Vmv=0000:Tms=5430:App_UARTMSG=0x00112233aabbcc
    :8000000084811F810EFF6D04808205113008020AEB11300102035A0501000209E3010200020E3A02030004000001BE6C00
    PKT:Typ=2:Lq=132:Ad=0x810eff6d(0x04):Vmv=2795:Tms=3904:PAL_AMB:TEMP=25.31:HUMD=36:LUMI=446
    #include <iostream>
    #include <iomanip> 
    #include <string>
    
    #include "twe_common.hpp"
    #include "twe_sercmd_ascii.hpp"
    #include "twe_fmt.hpp"
    
    using namespace TWE;
    using namespace TWEUTILS;
    using namespace TWESERCMD;
    using namespace TWEFMT;
    
    AsciiParser parse_ascii(256);
    int c;
    while ((c = std::cin.get()) >= 0) {
    	if (c == '\r' || c == '\n') {
    		parse_ascii << char_t(0x0d); // always 0x0d (this parser does not check the following 0x0a)
    	}
    	else {
    		parse_ascii << char_t(c);
    	}
    
    	if (parse_ascii) {
    		std::cout << "PKT";
    		auto&& payl = parse_ascii.get_payload(); // payload data array
    		auto&& pkt = newTwePacket(payl);         // packet object (contains minimum information)
    		auto&& typ = identify_packet_type(pkt);  // packet type
    		std::cout << ":Typ=" << int(typ);
    
    		if (typ != E_PKT::PKT_ERROR) {
    		   // ... display common packet info 
    		}
    		
    		// display each packet information
    		switch (typ) {
    			case E_PKT::PKT_PAL: print_pal(pkt); break;
    			case E_PKT::PKT_ACT_STD: print_act(pkt); break;
    			case E_PKT::PKT_TWELITE: print_app_twelite(pkt); break;
    			case E_PKT::PKT_APPIO: print_app_io(pkt); break;
    			case E_PKT::PKT_APPUART: print_app_uart(pkt); break;
    			case E_PKT::PKT_APPTAG: print_app_tag(pkt); break;
    			default: print_unknown(payl); // e.g. UART message of App_Twelite, App_UART(simple format), or corrupted.
    		}
      }
    }
    	if (parse_ascii) {
    auto&& payl = parse_ascii.get_payload();
    auto&& pkt = newTwePacket(payl);
    auto&& typ = identify_packet_type(pkt);  // packet type
    void print_app_twelite(spTwePacket pkt) {
    	auto&& atw = refTwePacketTwelite(pkt);
    	std::cout << ":DI1..4="
    			<< char(atw.DI1 ? 'L' : 'H') 
    			<< char(atw.DI2 ? 'L' : 'H')
    			<< char(atw.DI3 ? 'L' : 'H')
    			<< char(atw.DI4 ? 'L' : 'H');
    	...
    void print_pal(spTwePacket pkt) {
    	auto&& pal = refTwePacketPal(pkt);
    	PalEvent ev;
    
    	// acquire event data.
    	if (pal.has_PalEvent()) {
    		ev = pal.get_PalEvent();
    	}
    
    	switch(pal.get_PalDataType()) {
    		case E_PAL_DATA_TYPE::EVENT_ONLY: 
    		  /* イベントのみが含まれるパケットの場合 */ break;
    
    		case E_PAL_DATA_TYPE::MAG_STD:
    			/* MAG PAL の表示コード */ break;
    		
    		case E_PAL_DATA_TYPE::MOT_STD:
    			/* MOT PAL の表示コード */ break;
    		
    		case E_PAL_DATA_TYPE::AMB_STD:
    			/* AMB PAL の表示コード */ break;
    			
    		case E_PAL_DATA_TYPE::EX_CUE_STD:
    			/* TWELITE CUE のメッセージ */ break;		
    	}
    auto&& pal = refTwePacketPal(pkt);
    if (pal.is_PalEvent()) {
    switch(pal.get_PalDataType()) {
    case E_PAL_DATA_TYPE::MAG_STD:
    {
    			PalMag mag = pal.get_PalMag();
    			std::cout << ":STAT=" << int(mag.u8MagStat);
    			...
    パケットの種別

    the_app

    アプリ記述setup(),loop()の切り替え

    アプリケーションはsetup(),loop()の2つの関数で記述されますが、例えば1画面ごとに大きく振る舞いが違うもの(ここではサブアプリと呼びます)を複数定義して、メニューで切り替えるといった動作を行う場合は、そのままsetup(),loop()内に記述しては煩雑になります。また変数についても管理する必要があります。

    setup(), loop(), 関連する変数の切り替えを簡素化するためMWM5ライブラリでは以下の2つの仕組みを実装しています。

    • サブアプリ : setup(), loop(), 関連変数をクラスとしてまとめ、切り替える

    • アプリハンドラ: サブアプリ内で setup(), loop() を切り替える

    サブアプリ

    上記例は説明に必要な最小限のコードのみを記述します。APP_DEFを継承したApp_Foo,App_Barが、2種類のサブアプリの定義となり、各々がsetup(),loop()メンバー関数を定義しています。実際には振る舞いごとに必要なメンバー変数やその他メンバー関数が定義されます。サブアプリの登録や切り替えはライブラリ定義のクラスオブジェクトthe_appに対して操作を行います。s_change_app()関数はサブアプリを切り替える要求(サブアプリ終了時など)に対して、実際の切り替え部を実装していて、この関数をsetup()中で呼び出すthe_app.setup()のパラメータとして渡しておきます。loop()中にはthe_app.loop()を記述しておきます。

    サブアプリの定義

    void setup()とvoid loop()を記述しておきます。setup()はサブアプリが初期化されるときに1度だけ呼び出されます。loop()はthe_app.loop()関数中で呼び出されます。

    サブアプリの切り替え

    実行中のサブアプリが終了する場合に呼び出されます。サブアプリは1以上のIDにより種別を管理し、切り替えのために関数int (*)(THE_APP_MGR&, int, int, int)を実装しておきます。

    パラメータn_appselには、切り替えたいアプリケーションのIDが渡されます。パラメータはthe_app.exit()で指定した値です。-1が渡された場合は、デフォルトのサブアプリに切り替えるよう記述しておきます。

    • パラメータprev_appには、直前のサブアプリIDです。直前が無ければ0となります。

    • パラメータexit_idは、サブアプリがthe_app.exit()で指定した値です。

    n_appselで次に動作させるサブアプリを指定し、exit_idはサブアプリに対するパラメータとして用います。

    サブアプリ切り替え関数内部ではthe_app.new_app<T>()(Tは次に生成するサブアプリのクラス)を呼び出しています。この呼び出しによりクラスTのオブジェクトが生成され、同時にこれまで動作していたサブアプリのオブジェクトは破棄されます。

    the_app.new_app<T>()

    setup()とサブアプリ切り替え関数内で呼び出し、サブアプリのオブジェクトを生成し、同時に現在のサブアプリを破棄します。

    最初に動作させるサブアプリはsetup()で生成します。

    • テンプレートパラメータTはサブアプリ定義クラスでAPP_DEFを継承している必要があります。

    • テンプレートパラメータargsは、コンストラクタに渡されるパラメータです。オブジェクトはnew T(std::forward(args)...)で生成されます。サブアプリがパラメータを指定しないデフォルトコンストラクタで構築される場合は何も指定しません。パラメータを指定する場合はサブアプリのコンストラクタの定義が必要です(例えばApp_Foo::App_Bar(int)を定義しておいてthe_app.new_app<App_Bar>(exit_id))。

    the_app.exit()

    サブアプリのloop()内で呼び出し、loop()を抜けた直後にサブアプリを切り替えます。

    上記の例のように大本のloop()からはthe_app.loop()が呼び出され、さらにサブアプリのloop()が呼び出されます。サブアプリのloop()中でthe_app.exit()が呼び出されると、the_app.loop()内でアプリケーションの切り替えが行われます。実際に切り替えているのはユーザが定義するサブアプリ切り替え関数です。

    • パラメータexit_codeは、サブアプリの終了コードを指定します。任意の数を指定できます。

    • パラメータnext_appは、次のサブアプリを明示的に指定する意味合いの識別子です。-1はデフォルトのサブアプリ、0は切り替えを行わない、1以上が次のサブアプリの識別子と意味付けをしています。

    exit_codeとnext_appは、そのままサブアプリ切り替え関数に渡されます。サブアプリ切り替え関数は、その2つのパラメータを参照して次のサブアプリを決定します。

    the_app.query_appobj()

    大本のloop()からthe_appで生成したサブアプリのクラスオブジェクトを参照し、さらにサブアプリが指定したvoid*型のポインタデータを取得します。

    例えば、サブアプリ構築時に共通のクラスオブジェクトを持っていて(例えばメイン画面となるターミナル画面)、大本のloop()から共通のオブジェクトにアクセスします(例えばメイン画面上の文字列を取得)。

    アプリハンドラ

    アプリハンドラAPP_HNDLRは、サブアプリ中での簡易的なsetup(),loop()処理の切り替えを行います。例えば、リスト一覧→リスト選択後、エラーといった複数の画面を切り替えるような動作です。

    サブアプリの実装ではAPPDEFを継承しましたが、さらにAPP_HNDLR<S>(Sはサブアプリクラスを指定します)を継承します。サブアプリ中に処理関数(アプリハンドラ)を実装し、アプリハンドラを切り替えます。

    上記がアプリハンドラを用いたときのソース構造の例です。

    hndr_scr_def()とhndr_scr_next()がアプリハンドラ関数です。パラメータとしてevとargが与えられ、evはEV_SETUP,EV_LOOP,EV_EXITのいずれかになり、setup(),loop()と終了処理に対応します。

    アプリハンドラ関数

    アプリハンドラ関数はsetup(),loop()に相当する処理を行います。加えてハンドラが切り替えられる、つまり現在登録されているハンドラが終了するときの処理を記述できます。

    パラメータevに対応した処理を記述します。

    パラメータargは、APP_HNDLR::new_hnbdlr()やAPP_HNDLR::loop()でパラメータが与えられたときに、値が格納されます。

    APP_HNDLR::new_hndlr()

    アプリハンドラを初回生成する(サブアプリクラスのsetup())、またはアプリハンドラ内から次のアプリハンドラへ切り替えます。

    • パラメータhnd_nextはアプリハンドラ関数(メンバー関数のポインタ)を指定します。

    • パラメータargはアプリハンドラ関数のEV_SETUP呼び出し時のパラメータを指定します。(EV_SETUPによる初期化時の処理を分岐したいときに利用します)

    APP_HNDLR::loop()

    サブアプリのloop()から毎回呼び出します。

    • パラメータargを指定するとアプリハンドラにその値が渡されます。

    APP_HNDLR::on_close()

    サブアプリのデストラクタから呼び出すようにしてください。

    この記述を省略した場合、サブアプリのオブジェクトが破棄されるときに、アプリハンドラも破棄されますが EX_EXIT メッセージが呼び出されません。

    アプリハンドラごとの独自データ

    アプリハンドラは、サブアプリ内でsetup(),loop()コンテキスト切り替えの仕組みで、サブアプリ下でのメンバー関数の実行という携帯です。そのため、アクセスできるデータはサブアプリのメンバー変数となります。

    ここでは、アプリハンドラ独自のデータを保持するクラスを追加します。

    データを格納するクラスの準備

    APPHNDLR_DCを継承したクラスを準備します。このクラスにはアプリハンドラ内で使用するデータやメンバー関数を定義します。以下の例ではDC_MyAppHndlrというクラスを定義しています。

    以下の定義を行う必要があります。

    • static const int CLS_ID : データ格納クラス種別を識別するための個別のIDです。

    • int get_class_id() : CLS_IDを戻すメンバ関数です。

    • コンストラクタ : サブアプリクラスオブジェクトをパラメータとします。このオブジェクトは上記の例ではappに保存しDC_MyAppHndlrからサブアプリにアクセスする目的で利用します。

    サブアプリでのfriend宣言

    DC_MyAppHndlrはサブアプリのメンバー変数や関数にアクセスすることも多いためfriend宣言しておきます。もちろん一般のクラス設計のようにpublicインタフェースを用意してカプセル化してもよいのですが、この場合、カプセル化の利点があまりなく記述が煩雑になるためです。

    アプリハンドラ内での利用

    アプリハンドラではAPP_HNDLR::use<>()を用いてデータオブジェクトを取得します。オブジェクトの生成・破棄は暗黙に行われます。別のアプリハンドラに切り替えられたときにデータオブジェクトが破棄されます。

    上記の例では、アプリハンドラ関数の最初でdcを取得しています。初回呼び出しにオブジェクトが生成され、それ以降は生成されたオブジェクトを参照します。

    あとは生成されたdcオブジェクトを操作します。

    継承したAPPHNDLR_DCをAPPHNDLR_DC(CLS_ID)として初期化します。

    EV_SETUP

    setup()に相当し、アプリハンドラが登録・切替時に呼び出される。

    EV_LOOP

    loop()に相当し、都度呼び出される。

    EV_EXIT

    アプリハンドラの切り替えられ、現在のハンドラが終了する際に呼び出される。

    #include <mwm5.h>
    
    // サブアプリ1
    class App_Foo : public class APP_DEF {
    public:
      void setup() { ... }
      void loop() { ... }
    }
    
    // サブアプリ2
    class App_Bar : public class APP_DEF {
    public:
      void setup() { ... }
      void loop() { ... }
    }
    
    // サブアプリ切り替え関数
    static int s_change_app(
                  TWE::APP_MGR& the_app, int n_appsel
                , int prev_app, int exit_id) {
        if (n_appsel == 1) the_app.new_app<App_Foo>();
        else if (n_appsel == 2) the_app.new_app<App_Bar>();
        else n_appsel = 0;
        
        return n_appsel;
    }
    
    // setup
    void setup() {
        the_app.setup(s_change_app); // サブアプリ切り替え関数の登録
        the_app.new_app<App_Foo>(); // 最初に起動するサブアプリ
    }
    
    // loop()
    void loop() {
      	the_app.loop(); // 必須の呼び出し
    }
    class App_Foo : public class APP_DEF {
    public:
      void setup() { ... }
      void loop() { ... }
    }
    // APP_MGR内のtypedef
    typedef int (*PF_CHANGE_APP)(
        THE_APP_MGR& the_app,
        int n_appsel,
        int prev_app,
        int exit_id);
    
    // 例: App_Foo と App_Bar で切り替える
    int s_change_app(
                  TWE::APP_MGR& the_app, int n_appsel
                , int prev_app, int exit_id) {
        if (n_appsel == 1 || n_appsel == -1)
            the_app.new_app<App_Foo>();
        else if (n_appsel == 2)
            the_app.new_app<App_Bar>();
        else n_appsel = 0;
        
        return n_appsel;
    }
    template <class T, class... Args>
    void APP_MGR::new_app(Args&&... args)
    
    // setup 例
    void setup() {
        the_app.setup(s_change_app);
        the_app.new_app<App_Foo>(); // 最初に起動するサブアプリ
    }
    
    // サブアプリ切り替え関数例
    static int s_change_app(
                  TWE::APP_MGR& the_app, int n_appsel
                , int prev_app, int exit_id) {
        if (n_appsel == 1 || n_appsel == -1)
            the_app.new_app<App_Bar>();
        }
    }
    void APP_MGR::exit(int exit_code, int next_app = NEXT_APP_DEFAULT)
    
    // loop() の呼び出し階層
    loop() // 大本のloop()
      the_app.loop() // APP_MGRのloop()
      
        App_Foo::loop() // サブアプリのloop()
          ...
          if (..) { // 何かの条件でサブアプリを終了する
            the_app.exit(0);
            return; // 通常はここでreturn
          }
        // App_Foo::loop()が終了した
        
        // exit がコールされていた場合は、
        // サブアプリ切り替え関数を呼び出し、
        // 次のサブアプリに切り替える。
      }
    }
    APP_DEF* APP_MGR::query_appobj() // the_app.qurery_appobj()
    
    // サブアプリ定義クラス内で利用
    void APPDEF::set_appobj(void* p)
    void* APPDEF::get_appobj()
    
    // サブアプリ例
    class App_Foo {
      TWETerm_M5_Console _scr;
    public:
      App_Foo : _scr(..) {
        // 共通オブジェクトの登録
        set_appobj((void*)static_cast<ITerm*>(&_scr));
      }
      void setup();
      void loop();
    };
    
    // 大本のloop()
    void loop() {
      ..
      // APPDEFオブジェクトの取得
      if (auto p = the_app.query_appobj()) {
        // 登録されたvoid*をITerm*へ強制キャスト
        if (ITerm* ptrm = reinterpret_cast<ITerm*>(p->get_appobj())) {
          ... // ptrm を用いた操作
        }
      }
    }
    
    
    class App_Foo : public APPDEF, public APP_HNDLR<App_Foo> {
      // アプリハンドラ1
    	void hndr_scr_def(event_type ev, arg_type arg = 0) {
    		switch (ev) {
    			case EV_SETUP:
    			  // setup()に相当、最初に1回だけ呼ばれる
    			break;
    			case EV_LOOP:
    			  // loop()に相当
            if (..) { // ある条件で次のアプリハンドラに遷移したい
              // アプリハンドラ切り替え
              APP_HNDLR::new_hndlr(&App_Foo::hndlr_scr_next);
              return; // 通常はここでreturn
            }
    			break;
    			case EV_EXIT;
    			  // 終了時=アプリハンドラ切り替え時に呼び出される
    			break;
    	}
    	
    	// ハンドラ2
    	void hndlr_scr_next(event_type ev, arg_type arg = 0) {
    	  ..
    	}
    	
    public:
      ~App_Foo() { 
        APP_HNDLR::on_close();
      }
      void setup() {
        ...
        
        // 最初のアプリハンドラを登録する
        APP_HNDLR::new_hndlr(&App_Commander::hndr_screen_simple);
      }
      
      void loop() {
        // 必須呼び出し
        APP_HNDLR::loop();
        ...
      }
    };
    // APP_HNDLR<T>内のtypedef
    typedef void (T::* tpf_func_handler)(event_type ev, arg_type evarg)
    class App_Foo : public APP_DEF, public APP_HNDLR<App_Foo> {
      ..
    	void hndr_scr_def(event_type ev, arg_type arg = 0) {
    		switch (ev) {
    			case EV_SETUP:
    			  // setup()に相当、最初に1回だけ呼ばれる
    			break;
    			case EV_LOOP:
    			  // loop()に相当
            if (..) { // ある条件で次のアプリハンドラに遷移したい
              // アプリハンドラ切り替え
              APP_HNDLR::new_hndlr(&App_Foo::hndlr_scr_next);
              return; // 通常はここでreturn
            }
    			break;
    			case EV_EXIT;
    			  // 終了時=アプリハンドラ切り替え時に呼び出される
    			  // 注: サブアプリのデストラクタで APPHNDLR::on_close() を
    			  //     記述してください
    			break;
    	}
    };
    void APP_HNDLR::new_hndlr(tpf_func_handler hnd_next, arg_type arg = 0)
    void APP_HNDLR::loop(arg_type arg = 0)
    void APP_HNDLR::on_close()
    /// APPHNDLR_DCを継承したデータ格納クラスの定義 
    // publicアクセスで良いのでclassではなくstructで定義しています
    struct DC_MyAppHndlr : public APPHNDLR_DC {
      static const int CLS_ID = 0;
    	int get_class_id() { return CLS_ID; }
    	
    	MySubApp &app;
    	
    	// メンバ変数と操作関数
      int data1;
      void incr() { data1++ }
      
      // サブアプリ内のthe_screenに対する操作
      void display() { app.the_screen << data1; }
      
      // コンストラクタ
      DC_MyAppHndlr(MySubApp& app_) : 
                  app(app_) // サブアプリオブジェクトを保存
                , APPHNDLR_DC(CLS_ID) // APPHNDLR_DCの初期化
                , data1(0) // メンバ変数の初期化
                {}
    }
    struct DC_My_AppHndlr;
    
    //サブアプリ定義
    class MySubApp : public TWE::APP_DEF, public APP_HNDLR<MySubApp> {
      ..
      
      void hndr_MyApp(event_type ev, arg_type arg = 0); // アプリハンドラ関数
      frined struct DC_MyAppHndlr; // friend宣言
    }
    void MySubApp::hndr_MyApp(event_type ev, arg_type arg) {
      auto&& dc = APP_HNDLR::use<DC_My_AppHndlr>();
      
      switch(ev) {
      case EV_SETUP:
        ...
      break;
      
      case EV_LOOP:
        if (..) dc.incr();
      break;
      
      case EV_EXIT:
      break;
      }
    }