パラメータにはADCのピン番号を指定します。ADCのピン番号はPIN_ANALOGUE::やBRD_APPTWELITE::に定義されているので、こちらを利用しています。周期的に実行されるADCの値は、タイミングによってはavailable通知前のより新しい値が読み出されることがあります。このアクトでは32Hzと比較的ゆっくりの周期で処理しているため、available判定直後に処理すれば問題にはなりませんが、変換周期が短い場合、loop()中で比較的長い時間のかかる処理をしている場合は注意が必要です。Analogueには、変換終了後に割り込みハンドラ内から呼び出されるコールバック関数を指定することが出来ます。このコールバック関数からFIFOキューに値を格納する処理を行い、アプリケーションループ内ではキューの値を逐次読み出すといった非同期処理を行います。Timer0Timer0は32Hzで動作しています。秒32回の処理をします。ここでは、ちょうど1秒になったところで送信処理をしています。Timer0transmit();}}}AppTweliteでは約1秒おきに定期送信を行っています。Timer0がavailableになったときにu16ctをインクリメントします。このカウンタ値をもとに、32回カウントが終わればtransmit()を呼び出し無線パケットを送信しています。u8DI_BMとau16AI[]の値判定は、初期化直後かどうかの判定です。DI1..DI4やAI1..AI4の値が格納されていない場合は何もしません。transmit()無線パケットの送信要求をTWENETに行う関数です。本関数が終了した時点では、まだ無線パケットの処理は行われません。実際に送信が完了するのは、送信パラメータ次第ですが、数ms後以降になります。ここでは代表的な送信要求方法について解説します。transmit()MWX_APIRETはuint32_t型のデータメンバを持つ戻り値を取り扱うクラスです。MSB(bit31)が成功失敗、それ以外が戻り値として利用するものです。{ネットワークオブジェクトをthe_twelite.network.use<NWK_SIMPLE>()で取得します。そのオブジェクトを用いて.prepare_tx_packet()によりpktオブジェクトを取得します。ここではif文の条件判定式の中で宣言しています。宣言したpktオブジェクトはif節の終わりまで有効です。pktオブジェクトはbool型の応答をし、ここではTWENETの送信要求キューに空きがあって送信要求を受け付ける場合にtrue、空きがない場合にfalseとなります。//インタラクティブモード画面中ではない!}インタラクティブモードの画面が表示されているときは、画面出力を抑制します。20ms)パケットの設定はthe_tweliteの初期化設定のように<<演算子を用いて行います。パラメータに送信先アドレスを指定します。0x00なら自分が子機で親機宛に、0xFEなら自分が親機で任意の子機宛のブロードキャストという意味です。パラメータに再送回数を指定します。例の1は再送回数が1回、つまり合計2回パケットを送ります。無線パケット1回のみの送信では条件が良くても数%程度の失敗はあります。送信遅延を設定します。一つ目のパラメータは、送信開始までの最低待ち時間、2番目が最長の待ち時間です。この場合は送信要求を発行後におよそ0msから50msの間で送信を開始します。3番目が再送間隔です。最初のパケットが送信されてから10ms置きに再送を行うという意味です。パケットのデータペイロードペイロードは積載物という意味ですが、無線パケットでは「送りたいデータ本体」という意味でよく使われます。無線パケットのデータにはデータ本体以外にもアドレス情報などいくつかの補助情報が含まれます。送受信を正しく行うために、データペイロードのデータ並び順を意識するようにしてください。ここでは以下のようなデータ順とします。このデータ順に合わせてデータペイロードを構築します。(0..1023)データペイロードには90バイト格納できます(実際にはあと数バイト格納できます)。IEEE802.15.4の無線パケットの1バイトは貴重です。できるだけ節約して使用することを推奨します。1パケットで送信できるデータ量に限りがあります。パケットを分割する場合は分割パケットの送信失敗などを考慮する必要がありコストは大きくつきます。1バイト余分に送信するのに、およそ16μ秒×送信時の電流に相当するエネルギーが消費され、特に電池駆動のアプリケーションには大きく影響します。上記の例は、解説のためある程度の妥協をしています。識別子は1バイトの簡単なものにすべきですし、VCCの電圧値は8ビットに丸めてもかまわないでしょう。各AI1..AI4の値は10bitで、合計40bit=5バイトに対して6バイト使用しています。上記のデータペイロードのデータ構造を実際に構築してみます。型のコンテナとして参照できます。このコンテナに上記の仕様に基づいてデータを構築します。0xFF);上記のように記述できますがMWXライブラリでは、データペイロード構築のための補助関数pack_bytes()を用意しています。values}pack_bytes()の最初のパラメータはコンテナを指定します。この場合はpkt.get_payload()です。そのあとのパラメータは可変数引数でpack_bytes()で対応する型の値を必要な数だけ指定します。pack_bytes()は内部で.push_back()メソッドを呼び出して末尾に指定した値を追記していきます。3行目のmake_pair()は標準ライブラリの関数でstd::pairを生成します。文字列型の混乱(具体的にはペイロードの格納時にヌル文字を含めるか含めないか)を避けるための指定です。make_pair()の1番目のパラメータに文字列型(char*やuint8_t*型、uint8_t[]など)を指定します。2番目のパラメータはペイロードへの格納バイト数です。4行目はuint8_t型でDI1..DI4のビットマップを書き込みます。7-9行目ではau16AI配列の値を順に書き込んでいます。この値はuint16_t型で2バイトですが、ビッグエンディアンの並びで書き込みます。7行目のfor文はC++で導入された範囲for文です。end()によるイテレータによるアクセスが可能なコンテナクラスなどは、この構文が使用できます。(ユニバーサル参照)で型の指定も省略してます。通常のfor文に書き換えると以下のようになります。au16AI[i]);}これでパケットの準備は終わりです。あとは、送信要求を行います。pkt.transmit();パケットを送信するにはpktオブジェクトのpkt.transmit()メソッドを用います。戻り値としてMWX_APIRET型を返していますが、このアクトでは使っていません。戻り値には、要求の成功失敗の情報と要求に対応する番号が格納されています。送信完了まで待つ処理を行う場合は、この戻り値の値を利用します。on_rx_packet()無線パケットが受信できたときは、受信イベントとしてon_rx_packet()が呼び出されます。the_twelite.receiverによる手続きでは一旦受信パケットを内部キュー(2パケットまで格納)に格納してからの処理でしたが、on_rx_packet()ではTWENETライブラリからのコールバックから直接呼び出され、より取りこぼし等が発生しにくい手続きです。loop()文中で長時間処理を止めてしまうような記述を行うと、同じように取りこぼしの原因となります。ここでは、相手方から伝えられたDI1..DI4の値とAI1..AI4の値を、自身のDO1..DO4とPWM1..PWM4に設定します。&handled)受信パケットのデータrxをパラメータとして渡されます。rxから無線パケットのアドレス情報やデータペイロードにアクセスします。パラメータhandledは通常利用しません。rx.get_addr_src_lid());}受信パケットデータには、送信元のアドレス(32bitのロングアドレスと8bitの論理アドレス)などの情報を参照しています。インタラクティブモード画面が表示されているときは出力を抑制します。<NWK_SIMPLE>では、8bitの論理IDと32bitのロングアドレスの2種類が常にやり取りされます。送り先を指定する場合はロングアドレスか論理アドレスのいずれかを指定します。受信時には両方のアドレスが含まれます。パケットの識別MWXライブラリにはtransmit()の時に使ったpack_bytes()の対になる関数expand_bytes()が用意されています。);1行目ではデータ格納のためのchar型の配列を宣言しています。サイズが5バイトなのは末尾にヌル文字を含め、文字出力などでの利便性を挙げるためです。末尾の{}は初期化の指定で、5バイト目を0にすれば良いのですが、ここでは配列全体をデフォルトの方法で初期化、つまり0にしています。2行目でexpand_bytes()により4バイト文字列を取り出しています。パラメータにコンテナ型を指定しない理由は、この続きを読み出すための読み出し位置を把握する必要があるためです。1番目のパラメータでコンテナの先頭イテレータ(uint8_t*ポインタ)を指定します。.begin()メソッドにより取得できます。2番目のパラメータはコンテナの末尾の次を指すイテレータで.end()メソッドで取得できます。2番目はコンテナの末尾を超えた読み出しを行わないようにするためです。3番目に読み出す変数を指定しますが、ここでもmake_pair()によって文字列配列とサイズのペアを指定します。このアクトでは、パケット長が間違っていた場合などのエラーチェックを省いています。チェックを厳格にしたい場合は、expand_bytes()の戻り値により判定してください。末尾を超えたアクセスの場合はnullptr(ヌルポインタ)を戻します。読み出した4バイト文字列の識別子が、このアクトで指定した識別子と異なる場合は、このパケットを処理しません。}TWENETではアプリケーションIDと物理的な無線チャネルが合致する場合は、どのアプリケーションもたとえ種別が違ったとしても、受信することが出来ます。他のアプリケーションで作成したパケットを意図しない形で受信しない目的で、このような識別子やデータペイロードの構造などのチェックを行い、偶然の一致が起きないように対処することを推奨します。シンプルネットワーク<NWK_SIMPLE>でのパケット構造の要件も満足する必要があるため、シンプルネットワークを使用しない他のアプリケーションが同じ構造のパケットを定義しない限り(非常にまれと思われます)、パケットの混在受信は発生しません。データペイロードの取得DI1..DI4の値とAI1..AI4の値を別の変数に格納します。au16AI_remote[4]);先ほどのexpand_bytes()の戻り値npを1番目のパラメータにしています。先に読み取った4バイト文字列識別子の次から読み出す指定です。2番目のパラメータは同様です。3番目以降のパラメータはデータペイロードの並びに一致した型の変数を、送り側のデータ構造と同じ順番で並べています。この処理が終われば、指定した変数にペイロードから読み出した値が格納されます。確認のためシリアルポートへ出力します。インタラクティブモード画面が表示されているときは出力は抑制します。mwx::crlf;数値のフォーマット出力が必要になるのでformat()を用いています。>>演算子向けにprintf()と同じ構文を利用できるようにしたヘルパークラスですが、引数の数が4つまでに制限されています。(Serial.printfmt()には引数の数の制限がありません。"DI:0010"のようにDI1..DI4のビットマップを4桁で表示します。Vcc/AI1..AI4の値を順に整数で出力します。7行目のmwx::crlfは改行文字列を出力します。信号の出力これで必要なデータの展開が終わったので、あとはボード上のDO1..DO4とPWM1..PWM4の値を変更します。au16AI_remote[4]);digitalWrite()はディジタル出力の値を変更します。1番目のパラメータはピン番号で、2番目はHIGH(VCCレベル)かLOW(GNDレベル)を指定します。Timer?.change_duty()はPWM出力のデューティ比を変更します。最大値が1023でないことに注意してください。ライブラリ内で実行される割り算のコストが大きいため、2のべき乗である1024を最大値としています。0にするとGNDレベル、1024にするとVCCレベル相当の出力になります。