Slp_Wk_and_Tx

Slp_Wk_and_Tx

Slp_Wk_and_Tx は、定期起床後、何か実行(センサーデータの取得など)を行って、その結果を無線パケットとして送信するようなアプリケーションを想定した、テンプレートソースコードです。

setup(), loop() の形式では、どうしても loop() 中が判読しづらい条件分岐が発生しがちです。本Actでは、loop()中をSM_SIMPLEステートマシンを用いて _switch_構文による単純な状態遷移を用いることで、コードの見通しを良くしています。

アクトの機能

  • 起動後、初期化処理を経て、一旦スリープする

    1. setup()初期化する

    2. begin() スリープ実行する

  • スリープ起床後、状態変数を初期化し、以下の順に動作を行う

    1. wakeup()スリープからの起床、各初期化を行う

    2. loop()状態INIT->WORK_JOBに遷移: 何らかの処理を行う(このActでは 1ms ごとの TickCount ごとにカウンタを更新し乱数で決めたカウント後にTX状態に進む)

    3. loop() 状態TX送信要求を行う

    4. loop() 状態WAIT_TX送信完了待ちを行う

    5. loop() 状態EXIT_NORMALスリープする (1. に戻る)

  • loop() 状態EXIT_FATAL エラーが発生した場合は、モジュールリセットする

アクトの解説

宣言部

インクルード

パケット送信を行うため <NWK_SIMPLE> をインクルードしています。また、アプリケーションIDなど基本的な定義は "Common.h" に記述しています。

状態定義

loop()内の順次処理を記述うするため、このサンプルではステートマシン(状態遷移)の考え方を用います。ごく単純な状態遷移の処理をまとめた<SM_SIMPLE>を用います。

Common.h に以下の状態に対応する列挙体 STATE が定義されています。

状態を示す列挙体STATEを用いてSM_SIMPLEステートマシン(状態遷移)を宣言します。

ここで宣言されたstepは、状態の管理、タイムアウト、処理待ちを行うための機能が含まれています。

センサーデータ

このサンプルではセンサーデーターの処理は行いませんが、ダミーデータを用意しておきます。

setup()

変数やクラスオブジェクトの初期化を行います。

  • stepステートマシンの初期化

  • the_tweliteクラスオブジェクトの初期化

  • ネットワーク <NWK_SIMPLE> の登録と初期化(DEVICE_IDの登録)を行います。

つづいてクラスオブジェクトやハードウェアなどの開始処理を行います。

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

begin()

setup()の直後に一度だけ呼び出されます。SleepNow()関数夜を呼び出して初回のスリープ手続きを行います。

wakeup()

起床直後に呼び出されます。ここではセンサーデータ領域の初期化と、起床時のメッセージを出力しています。

loop()

上記のコードは、実際のコードを簡略化したものです。

この制御構造はSM_SIMPLEステートマシンを利用しています。do..while() 構文のループになっています。ループの中は_switch case_節となっていて、.state()で得られた状態により処理を分岐しています。状態の遷移は.next()を呼び出しステートマシン内の内部変数を新しい状態値に書き換えます。

step.b_more_loop()は、.next()により状態遷移があった場合_true_に設定されます。これは状態遷移が発生したときloop()を脱出せずに次の状態のコード(case節)を実行する目的です。

以下に各状態の解説を行います。

STATE::INIT

ダミーーのセンサー値を初期化します。一つは加算カウンタ、一つはカウンター停止値でランダムに決定しています。

STATE::WORK_JOB

WORK_JOB状態では1msごとのタイマー単位で処理します。TickタイマーごとにTickTimer.available()になります。Tickタイマーごとにカウンタを加算しdummy_work_ct_maxになったら、次の状態STATE::TXに遷移します。

STATE::TX

Transmit()関数を呼び出しパケット送信要求を行います。送信要求が成功した場合はSTATE::WAIT_TXEVENTに遷移して送信完了を待つことになります。ここでは完了待ちとしてSM_SIMPLEステートマシンのタイムアウトとフラッグ機能を用います(待ちループ中での変数値の変化により判定する単純なものです)。

単一の送信要求が失敗することは通常想定しませんが、失敗時はSTATE::EXIT_FATALとして例外処理する状態に遷移します。

Transmit()関数はMWX_APIRETオブジェクトを返しますが、このオブジェクトは_bool_型の成功の可否と、最大31ビットの値を保持しています。_bool_型として評価できますから、if_文の判定は送信要求が成功したら_true、失敗したら_false_を返します。

STATE::WAIT_TX

送信完了待ちは後述のon_tx_comp()によりステートマシン機能のフラッグをセットすることで判定しています。タイムアウトは.is_timeout()を呼び出すことで.set_timeout()を行ったときからの経過時間により判定します。

送信が成功しても失敗しても通常は完了通知がありますが、タイムアウトを設け例外処理のための状態STATE::EXIT_FATALに遷移します。

STATE::EXIT_NORMAL

SleepNow()を呼び出して、スリープ処理に入ります。

STATE::EXIT_FATAL

重大なエラーとして、システムリセットを行います。

SleepNow()

周期スリープを行います。スリープ時間はrandom()関数を用いて、一定の時間ブレを作っています。これは複数のデバイスの送信周期が同期した場合、著しく失敗率が上がる場合があるためです。

スリープ前にはSM_SIMPLEステートマシンの状態を.on_sleep()を呼び出してセットしておきます。

Transmit()

ID=0x00の親機宛に無線パケットの送信要求を行います。格納されるデータはActサンプルで共通に使われている4文字識別子(FOURCC)に加え、システム時間[ms]とダミーセンサー値(sensor.dummy_work_ct_now)を格納します。

まず最初に送信パケットを格納するオブジェクトを取得します。このオブジェクトを操作し、送信データや条件を設定します。

mwx ライブラリでは、_if_文中でオブジェクトを取得し、そのオブジェクトの_bool_判定で_true_の場合に処理を行う記述を採用しています。ここではthe_twelite.network.use<NWK_SIMPLE>()によりボードオブジェクトを取得し、ボードオブジェクトの.prepare_tx_packet()によりパケットオブジェクトを取得しています。パケットオブジェクトの取得失敗は通常想定しませんが、失敗時は送信キューが一杯で送信要求が受け付けられない場合です。このサンプルは単一の送信のみですから、エラーは想定外の重大な問題に限られます。

得られたpktオブジェクトに対して、送信条件(宛先や再送など)を<<演算子を用いて設定します。tx_addrはパケットの宛先を指定します。tx_retryは再送回数、tx_packet_delayは送信遅延の指定です。

パケットのペイロード(データ部分)はpkt.get_payload()により得られるsmblbuf<uint8_t>派生の配列です。この配列に対して直接値を設定しても構いませんが、ここではpack_bytes()を用いた値の設定を行います。

ペイロードの最大長は上記の例では91バイトですが、詳しくはNWK_SIMPLEパケット構造と最大長を参照ください。

この関数は可変数引数により指定できます。一番最初のパラメータは.get_payload()より得られた配列オブジェクトです。

  • make_pair(FOURCC,4) : _make_pair_はC++標準ライブラリのもので、_std::pair_オブジェクトを生成します。文字列型に対して先頭から4バイト分を書き出すという意味になります。 (文字列型の配列は終端を含める、含めないといった話題が混乱を生むため、明示的に書き出すバイト数を指定するために、このような指定をします)

  • _uint32_t_型のデータを指定するとビッグエンディアン並びで4バイト分のデータを書き込みます。

  • _uint16_t_型のデータについても同様です。

uint8_t 型のポインタを用いてデータの書き込みを行うことも出来ます。

.get_payload()から得られた配列オブジェクトは、何も格納されていないサイズ0の配列ですが、この配列にデータを書き込むことでサイズが拡張され(実際は内部の固定長のバッファに対してデータを書き込み、内部管理のデータサイズを更新します)、最終的なサイズがペイロードのデータサイズです。

ここでは.begin()を用いて_uint8_t*_のポインタを得て、このポインタを用いてデータを書き込み、最後に書き込んだサイズを.redim()で設定します。

S_OCTET(), S_WORD(), S_DWORD()といった関数を書き込みに用いていますが、例えばS_OCTET(p, 'H')*p = 'H'; p++;と同じ処理を行うポインタを用いたデータ書き込みです。

最後の.redim()は配列のサイズをバッファの初期化をせずに変更する手続きです。.resize()を呼び出すとすべて0クリアされます。

最後に.transmit()を呼び出して、送信要求を行います。戻り値はMWX_APIRET型です。要求後、実際の送信が行われますが、送信パラメータや送信サイズにもよりますが、完了まで数ms~数十ms程度はかかります。完了時にはon_tx_comp()が呼び出されます。

``MWX_APIRETuint32_t型をラップしたクラスで、MSBを失敗成功のフラグとし、以下31ビットをデータとして用いています。pkt.transmit()の戻り型になっており、送信要求の成功と失敗(_bool_型へのキャスト)ならびに送信IDをデータ部(.get_value())に格納しています。

on_tx_comp()

送信完了時に呼び出されるシステムイベントです。ここでは.set_flag()により完了としています。

最終更新