Parent_MONOSTICK

親機アプリケーション(MONOSTICK用)

MONOSTICKを親機として使用するアクトです。子機からのパケットのデータペイロードをシリアルポートに出力します。サンプルアクトの多くのサンプルでのパケットを表示することが出来ます。

このアクトには以下が含まれます。

  • 無線パケットの受信

  • 受信したパケットのデータ解釈

  • インタラクティブモードの設定 - <STG_STD>

  • バイト列のアスキー形式への変換 - serparser

アクトの機能

  • サンプルアクトの子機からのパケットを受信して、シリアルポートへ出力する。

アクトの使い方

必要なTWELITEと配線

役割

親機

MONOSTICK BLUEまたはRED

子機

サンプルアクトの子機設定 (例: Slp_Wk_and_Tx, PAL_AMB, PAL_MAG, PAL_MOT???など)

最初は以下のデフォルトの設定にて確認してください。

  • アプリケーションID: 0x1234abcd

  • チャネル: 13

アクトの解説

宣言部

インクルード

// use twelite mwx c++ template library
#include <TWELITE>
#include <MONOSTICK>
#include <NWK_SIMPLE>
#include <STG_STD>

MONOSTICK用のボードビヘイビア<MONOSTICK>をインクルードしています。このボードサポートには、LEDの制御、ウォッチドッグ対応が含まれます。

  • <NWK_SIMPLE> 簡易中継ネットの定義を読み込みます

  • <STG_STD> インタラクティブモードの定義を読み込みます。

その他

// application ID
const uint32_t DEFAULT_APP_ID = 0x1234abcd;
// channel
const uint8_t DEFAULT_CHANNEL = 13;
// option bits
uint32_t OPT_BITS = 0;
/*** function prototype */
bool analyze_payload(packet_rx& rx);

デフォルト値や関数プロトタイプなどの宣言をしています。

setup()

auto&& brd = the_twelite.board.use<MONOSTICK>();
auto&& set = the_twelite.settings.use<STG_STD>();
auto&& nwk = the_twelite.network.use<NWK_SIMPLE>();

setup()では、まず<MONOSTICK>ボードビヘイビア、<STG_STD>インタラクティブモード ビヘイビア、<NWK_SIMPLE>ビヘイビアをuse<>を用い読み込みます。この手続きは必ずsetup()内で行います。

set << SETTINGS::appname("PARENT"); // 設定画面中のタイトル
set << SETTINGS::appid_default(DEFAULT_APP_ID); // アプリケーションIDデフォルト
set << SETTINGS::ch_default(DEFAULT_CHANNEL); // チャネルデフォルト
set << SETTINGS::lid_default(0x00); // LIDデフォルト
set.hide_items(E_STGSTD_SETID::OPT_DWORD2, E_STGSTD_SETID::OPT_DWORD3, E_STGSTD_SETID::OPT_DWORD4, E_STGSTD_SETID::ENC_KEY_STRING, E_STGSTD_SETID::ENC_MODE);
set.reload(); // 設定を不揮発性メモリから読み出す
OPT_BITS = set.u32opt1(); // 読み込み例(オプションビット)

続いてインタラクティブモードの設定と設定値の読み出しを行います。<STG_STD>インタラクティブモードでは、標準的な項目が用意されていますが、作成するアクトごとにいくつかのカスタマイズを行えるようになっています。

  • appname→ 設定画面中のタイトル行にでるアクト名称

  • appid_default→ デフォルトのアプリケーションID

  • ch_default→ デフォルトのチャネル

  • lid_default→ デバイスID(LID)のデフォルト値

  • .hide_items()→ 項目の非表示設定

設定値を読み出す前には必ず.reload()を呼び出します。設定値は.u32opt1()のように設定値ごとに読み出し用のメソッドが用意されています。

the_twelite
<< set // インタラクティブモードの設定を反映
<< TWENET::rx_when_idle() // 受信するように指定
;
// Register Network
nwk << set; // インタラクティブモードの設定を反映
nwk << NWK_SIMPLE::logical_id(0x00) // LIDだけは再設定
;

いくつかの設定値は<STG_STD>オブジェクトを用いて直接反映することが出来ます。また、DIPスイッチの設定などにより特定の値を書き換えたいような場合などは、反映されたあとに別個に値を書き換えることも出来ます。上記の例ではthe_tweliteオブジェクトにアプリケーションID、チャネル、無線出力などを設定し、nwkオブジェクトに対してLIDと再送回数の設定をしてから、再度LIDを0に設定し直しています。

brd.set_led_red(LED_TIMER::ON_RX, 200); // RED (on receiving)
brd.set_led_yellow(LED_TIMER::BLINK, 500); // YELLOW (blinking)

<MONOSTICK>ボードビヘイビアではLED点灯制御のための手続きを利用できます。

1行目では赤色のLEDを無線パケットを受信したら200ms点灯する設定をしています。最初のパラメータはLED_TIMER::ON_RXが無線パケット受信時を意味します。2番目は点灯時間をmsで指定します。

2行目はLEDの点滅指定です。1番目のパラメータはLED_TIMER::BLINKが点滅の指定で、2番目のパラメータは点滅のON/OFF切り替え時間です。500msごとにLEDが点灯、消灯(つまり1秒周期の点滅)を繰り返します。

the_twelite.begin(); // start twelite!

the_tweliteを開始するための手続きです。act0..4では出てきませんでしたがthe_tweliteの設定や各種ビヘイビアの登録を行った場合は、必ず呼び出すようにしてください。

loop()

このサンプルではloop()中の処理はありません。

void loop() {
}

on_rx_packet()

パケットを受信したときに呼び出されるコールバック関数です。この例では受信したパケットデータに対していくつかの出力を行っています。

void on_rx_packet(packet_rx& rx, bool_t &handled) {
Serial << ".. coming packet (" << int(millis()&0xffff) << ')' << mwx::crlf;
...
// packet analyze
analyze_payload(rx);
}

analyze_payload()

関数の末尾で呼び出されるanalyze_payload()は、いくつかのサンプルアクトのパケットを解釈するコードが含まれています。サンプルアクト中のパケット生成部分と対応させてコードを参照してください。

bool b_handled = false;
uint8_t fourchars[4]{};
auto&& np = expand_bytes(
rx.get_payload().begin(), rx.get_payload().end()
, fourchars
);
if (np == nullptr) return;
// display fourchars at first
Serial
<< fourchars
<< format("(ID=%d/LQ=%d)", rx.get_addr_src_lid(), rx.get_lqi())
<< "-> ";

この関数では最初に4文字識別データをfourchars[5]配列に読み込みます。

読み込みはexpand_bytes()関数を用います。この関数の第1・第2パラメータはC++の標準ライブラリの作法に倣い、受信パケットのペイロード部の先頭ポインタ.begin()と末尾ポインタの次.end()を与えます。続くパラメータは可変引数として、読み込むデータ変数を与えます。戻り値はエラー時はnullptr、それ以外は次の解釈ポインタとなります。末尾まで解釈した場合は.end()が戻ります。ここでのパラメータはuint8_t fourchars[4]です。

この記述で対応するのは配列長さNが指定されるuint8_t[N]型のみで、uint8*型、char*型、char[]型などを用いる場合は、make_pair(char*,int)を用いた指定が必要になります。

char fourchars[5]{}; // 終端文字\0も含め5バイト確保する
auto&& np = expand_bytes(
rx.get_payload().begin(), rx.get_payload().end()
, make_pair((char *)fourchars, 4)
);

つづいて4バイトヘッダに対応した処理を行います。ここではサンプルアクトSlp_Wk_and_Txのパケットを解釈し内容を表示します。

// Slp_Wk_and_Tx
if (!b_handled && !strncmp(fourchars, "TXSP", 4)) {
b_handled = true;
uint32_t tick_ms;
uint16_t u16work_ct;
np = expand_bytes(np, rx.get_payload().end()
, tick_ms
, u16work_ct
);
if (np != nullptr) {
Serial << format("Tick=%d WkCt=%d", tick_ms, u16work_ct);
} else {
Serial << ".. error ..";
}
}

他の解釈部の判定をスキップするようにb_handledtrueに設定します。

"TXSP"のパケットではuint32_t型のシステムタイマーカウントと、uint16_t型のダミーカウンタの値が格納されています。各々変数を宣言してexpand_bytes()関数を用い読み込みます。上述と違うのは、読み出しの最初のポインタとして第一パラメータがnpとなっている点です。tick_msu16work_ctをパラメータとして与え、ビッグエンディアン形式のバイト列としてペイロードに格納された値を読み出します。

読み出しに成功すれば内容を出力して終了です。

独自のアスキー書式を定義して出力する

ユーザが定義した並び順でアスキー形式により構成します。

smplbuf_u8<128> buf;
mwx::pack_bytes(buf
, uint8_t(rx.get_addr_src_lid()) // 送信元の論理ID
, uint8_t(0xCC) // 0xCC
, uint8_t(rx.get_psRxDataApp()->u8Seq) // パケットのシーケンス番号
, uint32_t(rx.get_addr_src_long()) // 送信元のシリアル番号
, uint32_t(rx.get_addr_dst()) // 宛先アドレス
, uint8_t(rx.get_lqi()) // LQI:受信品質
, uint16_t(rx.get_length()) // 以降のバイト数
, rx.get_payload() // データペイロード
);
serparser_attach pout;
pout.begin(PARSER::ASCII, buf.begin(), buf.size(), buf.size());
Serial << "FMT PACKET -> ";
pout >> Serial;
Serial << mwx::flush;

1行目はアスキー書式に変換する前のデータ列を格納するバッファをローカルオブジェクトとして宣言しています。

2行目はpack_bytes()を用いてデータ列を先ほどのbufに格納します。データ構造はソースコードのコメントを参照ください。pack_bytes()のパラメータにはsmplbuf_u8 (smplbuf<uint8_t, ???>)形式のコンテナを指定することもできます。

パケットのシーケンス番号は、<NWK_SIMPLE>で自動設定され、送信パケット順に割り振られます。この値はパケットの重複検出に用いられます。

LQI (Link Quality Indicator)は受信時の電波強度に相当する値で、値が大きければ大きいほどより強い電界強度で受信できていることになります。ただしこの値と物理量との厳格な関連は定義されていませんし、環境のノイズと相対的なものでLQIがより大きな値であってもノイズも多ければ通信の成功率も低下することになります。

13,14,17行目は、シリアルパーサーの宣言と設定、出力です。

NWK_SIMPLEのヘッダを含めてダンプ出力する

最初の出力(if(0)により実行されないようになっています)は<NWK_SIMPLE>の制御データを含めたデータをすべて表示します。制御データは11バイトあります。通常は制御情報を直接参照することはありませんが、あくまでも参考です。

serparser_attach pout;
pout.begin(PARSER::ASCII, rx.get_psRxDataApp()->auData,
rx.get_psRxDataApp()->u8Len, rx.get_psRxDataApp()->u8Len);
Serial << "RAW PACKET -> ";
pout >> Serial;
Serial << mwx::flush;
// 参考:制御部のパケット構造
// uint8_t : 0x01 固定
// uint8_t : 送信元のLID
// uint32_t : 送信元のロングアドレス(シリアル番号)
// uint32_t : 宛先アドレス
// uint8_t : 中継回数

1行目は出力用のシリアルパーサをローカルオブジェクトとして宣言しています。内部にバッファを持たず、外部のバッファを流用し、パーサーの出力機能を用いて、バッファ内のバイト列をアスキー形式出力します。

2行目はシリアルパーサーのバッファを設定します。すでにあるデータ配列、つまり受信パケットのペイロード部を指定します。serparser_attach poutは、既にあるバッファを用いたシリアルパーサーの宣言です。pout.begin()の1番目のパラメータは、パーサーの対応書式をPARSER::ASCIIつまりアスキー形式として指定しています。2番目はバッファの先頭アドレス。3番目はバッファ中の有効なデータ長、4番目はバッファの最大長を指定します。出力用で書式解釈に使わないため4番目のパラメータは3番目と同じ値を入れています。

6行目でシリアルポートへ>>演算子を用いて出力しています。

7行目のSerial << mwx::flushは、ここで出力が終わっていないデータの出力が終わるまで待ち処理を行う指定です。(Serial.flush()も同じ処理です)