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...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
MWX ライブラリは、TWELITE モジュールのプログラムをより容易にかつ拡張性を高めるために設計されています。これまでMWSDKで利用していた TWENET C ライブラリを基本とし、MWXライブラリはアプリケーション開発層のライブラリとして開発しております。
MWX ライブラリの名称は Mono Wireless C++ Library for TWELITE です。MW は MonoWireless から、また C++ -> CXX -> double X -> WX。この MW と WX を重ねて MWX になりました。
このライブラリを用いて記述したコードを「アクト(act)」と呼びます。
本解説での表記について記載します。
ユニバーサル参照と呼ばれ、標準ライブラリなどで良く用いられます。当ライブラリでもほとんどの場合auto&&
と記載します。
auto
はC言語ではローカル変数(自動変数)を宣言する際のキーワードとなっていますが、ここでは型推論により宣言を行う意味です。C++のテンプレート構文では非常に煩雑な型名になることが多く、同時に型名を明示的に記述しなくても実装できる場面で便利な記法です。
下記の例では、v
の型に対する最大値最小値を発見する標準ライブラリのアルゴリズム std::minmax_element
を用いた例で、結果の戻り値を auto
により宣言しています。この場合、autoで推論された型はstd::pair<int, int>
になります。
auto &&
の&&
ついては、厳格な意味合いは専門書籍などを紐解いていただく必要がありますが、ここでは「戻りが参照型(C言語でいうポインタ渡しに近い)であっても値であっても、気にせず宣言できる」とお考え下さい。
namespace, inline namespace, using
を用いて、名前の再定義などを行っています。解説中でも一部省略して記載しています。
MWXライブラリは、下層に位置する各ライブラリ・機能(TWNET Cライブラリでの機能、また半導体ベンダが提供するマイコン・ペリフェラル機能、IEEE802.15.4の機能)について、その全てに対応する目的では開発しておりません。
MWXライブラリはC++言語で記述されておりアクトの記述においても C++ での記述を行うことになります。しかしながらC++言語であってもすべての機能が使えるわけではありません。特に以下の点に注意してください。
new, new[]
演算子でのメモリ確保は行えますが、確保したメモリを破棄することはできません。C++ライブラリで動的メモリ確保をするものは殆どが事実上利用不可能です。
グローバルオブジェクトのコンストラクタが呼び出されません。
参考:必要な場合は、初期化関数(setup()
) で new ((void*)&obj_global) class_foo();
のように初期化することでコンストラクタの呼び出しを含めた初期化を行えます。
例外 exception
が使用できません。
仮想関数 virtual
が使用できません。
上記の制約があるためSTLなどC++標準ライブラリの一部のみの利用となります。
※ 当社で把握しているものについての記載です。
標準ライブラリについては利用可否、また利用できそうなものについての包括的な検証は行っておりません。動作の不都合が確認できた場合は、別の方法で実装するようにしてください。
ソースコードは以下から参照できます。
{MWSDKインストールディレクトリ}/TWENET/current/src/mwx
Install and Build
MWXライブラリを用いてアプリケーションを記述(本書ではアクトと呼びます)し、実行するために開発環境のセットアップが必要です。
Mono Wireless C++ Library for TWELITE.
資料の取り扱いについてをご参照ください。 お気付きの点がありましたら、当サポート窓口にご連絡いただければ幸いです。
このページには 0.1.3 (MWSDK2020_05) の情報が含まれます。
MWX ライブラリは、TWELITE 無線モジュールのコード表記を簡素化することを目的としています。MWXで作成されたプログラムをアクト act と呼びます。アクトにはループによる記述と、イベントによる記述(ビヘイビア behavior と呼びます)の二種類があります。
本ページではアクトの主な特徴を紹介します。
ループによる記述 (setup(), loop()
)。小規模な機能を記述するのに向いています。
イベントドリブンのアプリケーション記述。各種イベント・割り込みハンドラの定義、アプリケーションの複雑な振る舞いを記述するのに向いたステートマシンをクラス内に定義して見通しの良いコードを記述できます。この記述をビヘイビアと呼びます。
ペリフェラルの手続きを簡素化。よく使われる UART, I2C, SPI, ADC, DIO, タイマー, パルスカウンタを取り扱うクラスオブジェクトを定義しています。
シンプルな中継ネットワークを定義。この中継ネットワークは TWELITE 標準アプリケーションと同等の実装で、個体アドレスの管理は 8bit の論理IDで行うこと、ネットワーク構築のための通信を行わないため電源投入後すぐにネットワーク宛に無線パケットを送ることができる点が特徴です。
TWELITE 標準アプリケーションとのパケットの相互通信はできませんが、以下の点で自由度が上がっています。
論理IDは 0 が親機である点は同様ですが、子機アドレスとして 0x01..0xEF を利用できるため、識別数を 200 以上とすることができます。
原則3回までとしていた中継回数について、最大数を64回までを設定できるようにしています。(※ パケットが遠回りして一定時間経過後に戻ってきた場合、重複パケットの管理テーブルがクリアされ中継済みのパケットであっても、再中継が発生する場合があります。中継回数を大きくする設定は注意してください)
PAL や MONOSTICK 向けのボード定義。ボード上のセンサーなどを容易に取り扱えます。
本パッケージ内で、ライセンス上特別な記述のないものは、モノワイヤレスソフトウェア使用許諾契約書(MW-SLA)を適用します。
本ドキュメントについても、本ライブラリパッケージ部の一部としてMW-SLA下の取り扱いとします。
本ソフトウェアについては、モノワイヤレス株式会社が正式にサポートを行うものではありません。お問い合わせにはご回答できない場合もございます。予めご了承ください。
不具合などのご報告に対してモノワイヤレス株式会社は、修正や改善をお約束するものではありません。
また導入パッケージなどお客様の環境に依存して動作しない場合もございます。
軽微な修正にあたるものは、本改版履歴には記載を行わずGitHub上の改版のみとなります。必要に応じて修正を引用ください。
TWELITE STAGE の配布パッケージリリース後の修正・追加分などはGitHubレポジトリに格納しております。必要に応じて配布パッケージの位置を差し替えて利用いただくようお願いいたします。
MWSDKの他の更新が必要になる場合があります。更新時のリリース記述を参照してください。MWSDKの更新についてはこちらを参照ください。
ライブラリのソースコードは GitHub (https://github.com/monowireless/mwx)にて公開しています。ライブラリのソースコードの差し替えは、以下の手順で行ってください。
各リリースのリンクよりGitのクローンを行うか zip 形式でソースコードをダウンロードします。
以下のディレクトリの内容を差し替えます。
MWSDK2020_05 に対応
重複チェッカ duplicate_checker の初期化等に不備があり期待通りの除去を行っていなかった
format() の実装を機種依存の少ないものとした。また、引数を最大8までとした。64bit引数が含まれる場合は引数の数は制限される。
修正は MWSDK2020_05 を前提としています。
本修正については、更新を推奨します。
MWSDK2020_04 に対応
Timer0..4の初期化の問題を修正
mwx::format() の内部処理を変更
インタラクティブモード対応のための実験的なコードの追加
本修正は MWSDK2020_04 を前提としています。
本修正については、更新を推奨します。
パケット内の中継フラグの扱いについての問題を修正
本修正については、更新を推奨します。
初版リリース (SDL 2019/12月号収録)
本プログラムは pyftdi (https://github.com/eblot/pyftdi) ライブラリサンプルスクリプト pyterm.py に TWELITE 用のファームウェア書き込みスクリプトを組み込んだものです。以下の機能があります。
TWELITE 用ファームウェアの書き込み (TWELITE R/MONOSTICK)
シリアルポートでの動作振る舞いの確認
本スクリプトの OS X での実行には Python3 インタプリタが必要です。コマンドライン環境ならびに Python インタプリタの取り扱いに慣れた方を対象とします。
本スクリプトを Linux で動作させるには同等のパッケージ (libusb-dev, pyserial, pyftdi) を用意します。コマンドライン環境ならびに Python インタプリタの取り扱いに慣れた方を対象とします。
参考環境:Ubuntu 18.04 (x86-64 64bit), Python 3.6.5
Mac OS X または Linux
python3.5 以降
libusb
pyserial
pyftdi
以下の環境で開発、動作確認を実施しました。ただし、これら環境で動作を保証するものではありません。
環境
Mac OS X 10.11.6, Python3.5.1 (2018/05)
Ubuntu 18.04, Python3.6.7 (2018/05)
Mac OS X 10.14.2, Python3.7.2 (2019/01)
以下の手順はパッケージ管理システムのバージョンアップや仕様変更で、そのまま適用できない場合があります。一般の情報などを参考にしてください。
お使いのディストリビューションのパッケージ導入法を調べてください。以下のパッケージが必要です。
python3.5 以降 (多くの場合導入されています)
libusb-dev
pyserial
pyftdi
パッケージ導入コマンド例。
TWELITE SDK をインストールしたディレクトリを ${TWELITESDK}
と記載します。
スクリプトは以下になります。
スクリプトに実行許可を与えます。
必要に応じて環境変数 PATH に追加しておきます。
libusb と OS のドライバが競合するため、ドライバをアンロード(無効に)しておきます。
FTDI 関連のドライバをアンロードします。
ドライバのアンロードは不要です。
エラーが出る場合は、ドライバをアンロードを試してみてください。
パラメータ
解説
-p ftdi:///?
または -p
デバイス一覧を表示します。
-p [デバイス名]
デバイスを指定します。
ftdi:///1
他にデバイスがない時。
ftdi://::MW19ZZUB/1
シリアル番号による指定。`
-b [ボーレート]
ボーレートを指定します。
-b 115200
115200bps を指定。
-F [ファームウェア]
ファームウェアを書き込みます。
-F App_Twelite.bin
ファイル名が App_Twelite.binを書き込みます。
--no-color
文字のカラー出力を抑制します。
--no-term
ファームウェアの書き込みのみを実施しターミナルを開きません。
Ctrl+C
キーを入力することで制御プロンプトを表示し、いくつかの特別な操作が可能です。それ以外の場合は、直接 TWELITE 無線モジュールに入力文字列が送付されます。
入力キー
解説
Ctrl+C Ctrl+C
ターミナルを終了します。
Ctrl+C Ctrl+R
または Ctrl+C r
TWELITE 無線マイコンをリセットします。
Ctrl+C Ctrl+I
または Ctrl+C i
インタラクティブモードへ遷移する + + + コマンドを入力します。インタラクティブモード対応ファームウェアのみ有効です。
Ctrl+C A
書式の解釈を開始します。TWELITE無線マイコンからの出力とキー入力に対して、アスキー形式の解釈を行います。
Ctrl+C B
書式の解釈を開始します。TWELITE無線マイコンからの出力に対してバイナリ形式の解釈を行います。キー入力はアスキー形式で解釈します。
Ctrl+C N
書式の解釈を停止します。
書式解釈中は TWELITE からの電文は解釈できた電文のみ表示し、キーボードの入力はエコーバックされますが、アスキー形式の電文が完成した時に TWELITE に送付されます。
実行例中では、適宜改行を挟んでいます。
エラーが発生する場合は、シリアルポートの権限の問題かもしれません。root権限(sudo等)で実行します。
以下の例では App_UART (UART 通信アプリ) を書き込み、起動メッセージを確認しています。
通常通り + + + と3回入力しても同じ結果になります。
インタラクティブモードで m
B
Enter
S
と順に入力します。
以下の例ではApp_UARTがバイナリ形式で入出力を行ないます。Ctrl+C B
を入力します。
この状態では入出力は書式形式となります。キーボードの入力はアスキー形式、 TWELITE からの電文はバイナリ形式として解釈します。
TWELITE からの電文を受け取る例を示します。一番簡単な方法は TWELITE をリセットします。Ctrl+C r
を入力します。
出力された [dbf...]
がTWELITEからの電文で実際は0xdb 0xf1 0x67...
と続くバイナリ列になります。
反対に TWELITE に電文を送る場合はアスキー書式で入力します。:7800112233AABBCCDDX
と入力します。 ここでは ペイロードが 0x7800112233AABBCCDD のデータをバイナリ形式で TWELITE に送付しています。直後に応答として [dba18001]
が戻ってきています。
Ctrl+C Ctrl+C
を入力します。
本資料で利用する用語について補足します。
用語の解説は、規格などで定められる定義に沿っていない場合があります。
ソフトウェア開発環境
TWELITE無線マイコンのソフトウェア開発用のSDKをTWELITE SDK (またはMWSDK)と呼称します。
TWELITE無線モジュールが利用する無線規格です。MWXライブラリを使用する限り、無線規格の詳細を意識する必要はありません。
無線通信における最小の通信単位です。
最大量は通信方式や通信時の設定によって変わりますが、MWXライブラリ標準の通信<NWK_SIMPLE>では、ユーザが1パケットで送信できるデータ量は90バイトです。
「貨物」といった意味合いですが、無線パケットに含まれるデータ本体のことをいいます。
「点・節」といった意味合いですが、無線ネットワーク内の無線局のことを言います。
本ライブラリを用いて作成したプログラム。そのソースコードまたは動作するプログラムのことを言います。
アクトの中でも特にイベント形式のプログラム。そのソースコードまたは動作するプログラムのことを言います。
ビヘイビアは1つのクラス定義による記述で、TWENETからのコールバック関数やイベントや割り込み処理を記述しひとまとめにしています。MWXライブラリでは以下の3種類のビヘイビアがあります。
アプリケーションビヘイビア:イベントドリブンでのアプリケーション記述を行い、ユーザが定義するクラス。
ボードビヘイビア:TWELITE無線モジュールを実装するボードの機能利用を簡素化するためのクラス。
ネットワークビヘイビア:無線ネットワークの手続きを簡素化するためのクラス。
ビヘイビア名は < >
で括って表記します。例えばシンプル中継ネットワークのビヘイビア名は <NWK_SIMPLE>
です。
本ライブラリの解説では、ライブラリで最初からグローバル宣言されたオブジェクトをクラスオブジェクトと呼称します。Serial
, Wire
などです。これらクラスオブジェクトは手続きなしまたは開始手続きを行うことで利用できます。
メモリを比較的多く消費するクラスオブジェクトは、初期化手続きの際(.setup()
または.begin()
メソッド)に初期化パラメータに沿ったメモリを確保します。
一般の用語です。C言語の知識を前提に解説します。
C++言語のこと。
MWXライブラリはC++とC言語によって記述されています。
C++規格のバージョンの一つ。2011年式のC++といった意味合いで、2011年にISOで規格化されています。直前のC++03から大きく機能拡張されています。C++14, C++17といったより新しいバージョンがあります。
MWXライブラリはC++11で追加された機能や構文を用いて実装されています。MWSDKのコンパイラ対応はC++11までとなります。
あるデータに注目して、その手続きをひとまとめにしたもの。構造体に、その構造体を取り扱うための手続きが含まれています。実際にはもっと深い話題に発展しますが、専門書を参考にしてください。
C++においては、キーワードの struct
と class
は本質的には同じもので、いずれのキーワードで宣言してもクラスとなります。
上記のクラス定義をC言語でも行った場合、例えば以下のようになります。
既存のC言語のライブラリやその内部の構造体などをクラスに包含し、C++特有の機能性を追加するなどして、利用の便を図ったものです。解説中でも「~構造体をラップした」といった記述をする場合があります。
MWXライブラリは、TWENETのCライブラリのラッパークラスと、新たに実装したクラス群との組み合わせになっています。
クラスに定義される関数で、クラスに紐付いています。
クラスを実体化(メモリ確保)したもの。
本解説ではオブジェクトとインスタンスは同じ意味合いとして取り扱っています。
オブジェクト生成時の初期化手続き。
コンストラクタと対になってオブジェクトが破棄されるときの手続きです。
C++では仮想クラスによりポリモーフィズム(多態性)を実現します。具体的にはvirtual
キーワードで指定た純粋仮想関数を定義したクラスです。
MWXライブラリでは、コンパイラの制限や性能上の理由で仮想関数を使用しません。ポリモーフィズムを実現するのに別の手法を用いています。
C/C++言語では { }
で括った範囲と考えてください。この中で生成したオブジェクトは、スコープから出るときに破棄されます。この時デストラクタが呼び出されます。
以下は、明示的にスコープを設定したものです。helo2
は8行目まで実行された時点で破棄され、デストラクタが呼び出されます。
MWXライブラリでは以下のような記法を用いています。ここではif文の条件判定式内で宣言(C89といった旧いC言語ではこういった場所での宣言はできません)されたオブジェクトの有効期間は、if文の{}
内になります。
例えば二線シリアルバスなど、開始と終了の手続きがあって、その間だけオブジェクトによってバスを操作するような手続きです。オブジェクトの生成後、バスの接続が適切であればif文のtrue節が実行され、生成したオブジェクトによってバスの書き込みまたは読み出しを行います。バスの読み書き操作が終了したらif文を脱出し、この時デストラクタが呼び出され、バスの利用終了手続きが行われます。
定義名の重複を避けるためC++では名前空間が積極的に用いられます。名前空間にある定義にアクセスするには::
を用います。
テンプレートはC言語のマクロを拡張したものと考えてください。
この例では、簡単な配列を定義しています。T
とN
はテンプレートのパラメータで、T
は型名をN
は数値を指定し、T
型で要素数N
の配列クラスを定義しています。
C++11ではNULLポインタをnullptr
と記述するようになりました。
C++では、参照型を利用できます。これはポインタによるアクセスに似ていますが、必ずオブジェクトを参照しなければならないという制約があります。
以下のような参照渡しのパラメータを持つ関数ではi
の値をincr()
内で書き換えることが出来ます。
テンプレートの解説例ですがoperator[]
の戻り型をT&
に変更しています。こうすることでa[0]=1
のように配列内部のデータに対して直接代入操作ができるようになります。
MWXライブラリのプログラミングインタフェースは、原則といてポインタ型の利用をせず、参照型を用いています。
C++11 では型推論のauto
キーワードが導入されています。これはコンパイラが初期化の記述からそのオブジェクトの型を推論するため、具体的な型名の記述を省略できます。これはtemplateを用いたクラス名が非常に長くなるような場合に効果的です。
解説中では多くの場合ユニバーサル参照と呼ばれるauto&&
を用いています。ユニバーサル参照については、ここでは参照渡しの場合も意識せずに記述できるものと考えてください。
配列など特定のデータ型のオブジェクトを複数個格納するためのクラスをコンテナと呼びます。テンプレートの例で挙げたmyary
のような配列クラスもコンテナと呼んでいます。
MWXライブラリでは、配列クラスsmplbuf
とFIFOキュークラスsmplque
を用意しています。
C言語で言うところのポインタ(もちろんC++でも同じようにポインタは使えます)を拡張した概念です。C言語のポインタは、メモリが連続した要素を先頭から末尾まで連続的にアクセスする手段と考えることが出来ます。FIFOキューを考えてみます。もっとも単純なキューの実装はリングバッファによるものですが、メモリーの連続性はありません。こういったデータ構造であっても、イテレータを用いるとポインタと同じように記述できます。
イテレータを取得するため.begin()
,.end()
のメソッドが用いられます。コンテナの先頭を指すイテレータを.begin()
で取得します。末尾の次を指すイテレータを.end()
で取得します。末尾ではなく、末尾の次である理由にはforやwhile文のループ記述の明快さ、コンテナに格納される要素数が0の場合の取り扱いが挙げられます。
上記では、que
の各要素について、イテレータp
を用いて各要素にsome_process()
を適用しています。p
は++
演算子によって次の要素を指すイテレータとしてインクリメントしています。本来ポインタでは記述できないデータ構造を持つコンテナであっても、このようにポインタを用いた処理と同じような処理が出来ます。
.end()
が末尾の次を示すため、while文の終了判定は(p != e)
のように簡潔です。キューに要素がない場合は.begin()
は.end()
と同じイテレータを返します。(何も格納されていない要素のイテレータの次ですから、最初に格納すべき領域を示すイテレータと考えればよいでしょう)
メモリ上で連続したコンテナの場合、通常、そのイテレータは通常のポインタとなります。その操作時に大きなオーバーヘッドにはならないことが期待できます。
C++の標準ライブラリにはSTL(Standard Template Library)が含まれます。MWXライブラリでの一部を利用しています。
TWELITE向けのC/C++コンパイラの制約から、利用できる機能はごく一部です。
例えば最大や最小値を求めるといった処理をC言語では型に応じて別々に記述していました。こういったコードは型の部分だけ違って他は同じといったものも少なくありません。C++ではtemplateやイテレータなどを用いて、こういった処理を型に依存せず記述することができます。これをアルゴリズムと呼んでいます。
例えば上記のように最大値を求めるアルゴリズムです。このアルゴリズムは型に依存しません。(ジェネリックプログラミングと呼ばれます)
ここではque
のイテレータを指定し、その最大と最小を得るアルゴリズムstd::minmax_elenet
を適用しています。std::minmax_elemet
はC++標準ライブラリ内に定義されています。その戻り値は任意の2つの値を組み合わせるstd::pair
です。このアルゴリズムは、イテレータの示す要素同士で<,>,==
といった演算子での比較が出来れば最大と最小を計算してくれます。戻り型もイテレータの型から導かれます。
開発環境を構築するためには、ソフトウェア群のインストール、またこれらの利用許諾に同意する必要があります。また、PC、ワークステーション上でセキュリティ設定等が必要になる場合があります。
配布時には十分注意しておりますが、ウィルスなどの確認はお客様のほうでも留意いただくようお願いいたします。
お客様のセキュリティの考え方や運用(例:外部アプリケーションのインストールの可否)については、お客様の環境の管理者にご確認ください。
また、開発環境をインストールまた動作するにあたり、OSが介在し設定等必要になる場合があります(例:開発元が不明なアプリケーションの実行。開発環境または紹介するツール群の多くは、アプリケーションは開発元を証明する仕組みが組み込まれせん)。設定方法については、一般の情報を参考いただくようお願いいたします。
MWXライブラリを用いてアプリケーションを記述するには以下が必要です。
MWSDK(ソフトウェア開発環境)
開発用エディタ(Microsoft社のVisualStudio Codeを紹介します)
コンパイラのツールチェインなどは比較的環境への依存度が低いため、多くの環境で動作することが期待できますが、サポート中のWindows10バージョンを推奨します。動作環境の差異により動作しないような場合は、当社で確認している環境を参考に別途環境を用意してください。
以下、開発で使用しているバージョンを挙げます。
Windows10 #1809, #1903
.NET CLR v4.0.30319
FTDI社のドライバが動作していること (MONOSTICK, TWELITE Rを動作させるため)
WSL (Windows Subsystem Linux) 環境下でもコンパイラを動作させることが出来ます。ただしコンパイルのみでファームウェアの書き換え等はWindows10上のユーティリティから実施してください。
WSL環境は必須ではありません。
コンパイラのツールチェインなどは比較的環境への依存度が低いため、多くの環境で動作することが期待できますが、現在サポート中のディストリビューションを推奨します。動作環境の差異により動作しないような場合は、当社で確認している環境を参考に別途環境を用意してください。
以下、開発で使用しているバージョンを挙げます。
Ubuntu 16.04 LTS 64bit
Ubuntu 18.04 LTS 64bit
*32bitのシステムはサポートしません。
コンパイラのツールチェインなどは比較的環境への依存度が低いため、多くの環境で動作することが期待できますが、現在サポート中のディストリビューションを推奨します。動作環境の差異により動作しないような場合は、当社で確認している環境を参考に別途環境を用意してください。
以下、開発で使用しているバージョンを挙げます。
macOS 14.06
開発環境を動作させるための環境や使用方法については、その開発元やコミュニティの情報を参照ください。
コード記述作業の効率面で Visual Studio Code (VSCode) の利用を推奨します。
MWXライブラリでは、C言語の開発に比べ、読み込むヘッダファイルが多くなるため、VSCode上でのコード解釈等にはより多くのPCのリソースを要求します。
Linux/WSL環境下/macOSのビルド結果はWindows10の結果と異なります。通常系の動作で差異が見られることは当社が把握する限りありませんが、特にgccのLTOを無効にしているためバイナリサイズが数%程度大きくなる傾向にあります。
動作等に疑問を感じた際は、必ず Windows10 上のビルドを実施し再現することを確認してから、お問い合わせください。
MWSDK 2020-04 以降は TWELITE STAGE アーカイブに含まれます。
TWELITE STAGEアプリからビルドする場合は、MWSDK_ROOT
環境変数の設定が不要になります。
MWSDK 2019-12 と同じの方法でビルドする場合は、TWELITE STAGE アーカイブ中の MWSDK ディレクトリの構成は大きく変わっておりませんので、読み替えて利用ください。
TWELITE STAGE については以下をご覧ください。
TWELITE SDK (MWSDK) をインストールします。本書ではMWSDKと記載します。
TWELITE SDKはTWENET MWX C++ ライブラリ対応版をダウンロードしてください。
MWX 対応 SDK の圧縮アーカイブファイルをダウンロードしてください。
圧縮アーカイブファイルを展開してください。
展開先のディレクトリ名にはスペース、日本語名が含まれてはいけません。
MWSDK_ROOT
, MWSDK_ROOT_WINNAME
(Windows10のみ) の設定が必要です。
ここでは展開後のディレクトリ名を C:\MWSDK
とします。別のディレクトリにインストールした場合は、読み替えてください。
C:\MWSDK\SET_ENV.CMD
を実行してください。以下の環境変数を設定します。
MWSDK_ROOT
MWSDK_ROOT_WINNAME
例えば以下のような設定になります。
インストールしたPC上からMWSDKをアンインストールするには以下を行ってください。
UNSET_ENV.cmd
を実行してください。環境変数の設定を解除します。
MWSDKディレクトリを削除してください。
開発環境やシェルに MWX_ROOT
環境変数を反映されるように設定してください。
方法はいくつかありますが、ホームディレクトリの.profile
(ファイルがなければ新しく作成してください)に以下の設定を追加します。この設定でVSCodeのビルドまで可能です。
MWSDK_ROOT=/foo/bar/MWSDK/
export MWSDK_ROOT
エディタを使用せずに追加するには以下のようにコマンド入力します。$
はプロンプトで環境によって表示が違います。/foo/bar/MSWSDK
の部分はインストールしたディレクトリに応じて書き換えてください。
開発環境やシェルに MWX_ROOT
環境変数を反映されるように設定してください。
方法はいくつかありますが、ホームディレクトリの.profile
(ファイルがなければ新しく作成してください)に以下の設定を追加します。この設定でVSCodeのビルドまで可能です。
MWSDK_ROOT=/foo/bar/MWSDK/
export MWSDK_ROOT
エディタを使用せずに追加するには以下のようにコマンド入力します。$
はプロンプトで環境によって表示が違います。/foo/bar/MSWSDK
の部分はインストールしたディレクトリに応じて書き換えてください。
環境全体にMWSDK_ROOT
を適用にするにはLaunchDを用います。
VS Codeの一部の設定で環境変数を参照していますが、ビルドには必須ではありません。
SDKに収録された時点から、ライブラリソースコードに修正がある場合があります。改版履歴を参照の上、必要に応じでライブラリソースコードを差し替えてください。
MWX ライブラリ内で用いる C++ 言語について、その仕様、制限事項、本書記載留意事項、設計メモを記載します。
MWXライブラリでアプリケーション記述する場合は、このページを読み飛ばしても差し支えありません。
このページはライブラリの動作の理解や改造などのためライブラリソースコードを参照する場面を想定しています。ライブラリを利用するのと比べより高度なC++言語に関する知識を前提となります。
アプリケーションのループ記述では、一般によく用いられる API 体系に近い記述を出来ることを目的とするが、TWELITEの特性に合わせた実装とする。
TWENET はイベントドリブンによるコード記述であり、これを扱えるようにクラス化を行う。上記クラス化によりアプリケーションのふるまいをカプセル化できるようにする。
イベントドリブンとループの記述は共存できる形とする。
代表的なペリフェラルはクラス化して手続きを簡素化する。可能な限りループ記述でアクセスできるようにする。
当社で販売する MONOSTICK/PAL といったボードを利用する手続きをクラス化し手続きを簡素化する。(例えば外部のウォッチドッグタイマーの利用を自動化する)
アプリケーションクラスやボードクラスは、ポリモーフィズムの考え方を導入し、統一した手続きによって利用できるようにする。(例えば、いくつかの振る舞いをするアプリケーションクラスを始動時にロードするような場合、また TWENET C ライブラリの接続部のコードを都度定義しなくてよいようにするため)。
C++の機能については、特に制限を設けず利用する。例えば、無線パケットを取り扱うにあたり煩雑なパケット構築、分解といった代表的な手続きを簡略化する手段を提供する。
演算子 ->
を極力使用しないようにし、原則として参照型による API とする。
限られた時間で実装を進めているため、細かい点まで網羅した設計ではありませんが、設計・実装等でお気づきの点がありましたら、当社サポートにご連絡ください。
gcc version 4.7.4
C++11 (コンパイラ対応状況は一般の情報を参考にしてください)
※ 当社で把握しているものについての記載です。
new, new[]
演算子でのメモリ確保は行えますが、確保したメモリを破棄することはできません。C++ライブラリで動的メモリ確保をするものは殆どが事実上利用不可能です。一度だけ生成してそれ以降破棄しないオブジェクトに使用しています。
グローバルオブジェクトのコンストラクタが呼び出されません。
参考:必要な場合は、初期化関数(setup()
) で new ((void*)&obj_global) class_foo();
のように初期化することでコンストラクタの呼び出しを含めた初期化を行えます。
例外 exception
が使用できません。
仮想関数 virtual
が使用できません。
本節ではMWXライブラリのコードを参照する際に理解の助けとなる情報を記載します。
限られた時間で実装を進めているため、詳細部分の整備が十分でない場合があります。例えば const に対する考慮は多くのクラスで十分なされていません。
名前空間について、以下の方針としています。
定義は原則として共通の名前空間mwx
に配置する。
名前空間の識別子なしで利用できるようにしたいが、一部の定義は識別子を必須としたい。
クラス名については比較的長い命名とし、ユーザが利用するものは別名定義とする。
クラス・関数・定数は一部の例外を除きmwx
名(正確にはinline namespace L1
で囲んだmwx::L1
)の名前空間内に定義しています。inline namespace
を指定しているのは、mwx::
の指定を必須とする定義と、必須としない定義を共存させるためです。
殆どの定義はusing namespace
により名前空間名を指定しなくても良いようになっています。これらの指定はライブラリ内のusing_mwx_def.hpp
で行っています。
例外的に比較的短い名前についてはmwx::crlf, mwx::flush
のように指定します。これらはinline namespace
のmwx::L2
の名前空間に配置されています。using namespace mwx::L2;
を指定することで名前空間名の指定なしで利用できるようになります。
また、いくつかのクラス名はusing
指定をしています。
MWXライブラリ内で利用するstd::make_pair
をusing
指定しています。
仮想関数 (virtual), 実行時型情報(RTTI) が利用できない、かつ利用できるようにしたとしても、パフォーマンス面で難があるため、これに代わる設計手法として CRTP (Curiously recurring template pattern : 奇妙に再帰したテンプレートパターン)を用いています。CRTPは、継承元の親クラスから子クラスのメソッドを呼び出すためのテンプレートパターンです。
以下の例では Base
を継承した Derived
クラスに interface()
というインタフェースを実装する例です。Base
からはDerived::print()
メソッドを呼び出しています。
MWXライブラリで利用されている主要クラスは以下です。
イベント処理の基本部分mwx::appdefs_crtp
ステートマシンpublic mwx::processev_crtp
ストリーム mwx::stream
CRTPクラスは、継承元のクラスはインスタンスごとに違います。このため、親クラスにキャストして、同じ仲間として取り扱うといったこともできませんし、仮想関数(virtual
)やRTTI(実行時型情報)を用いたような高度なポリモーフィズムも使うことが出来ません。
以下は上述のCRTPの例を、仮想関数で実装した例です。CRTPではBase* b[2]
のように同じ配列にインスタンスをまとめて管理することは、そのままではできません。
MWXライブラリでは、CRTP のクラスインスタンスを格納するための専用クラスを定義し、このクラスに同様のインタフェースを定義することで解決しています。以下にコード例を挙げます。
VBase
クラスのメンバ変数 p_inst
は、Base <T>
型のオブジェクトへのポインタを格納し、pf_intrface
は Base<T>::s_intrface
へのメンバ関数ポインタです。 Base<T>::s_intrface
は、自身のオブジェクトインスタンスを引数として渡され、T
型にstatic_cast
することでT::intrface
メソッドを呼び出します。
VBase
への格納は、ここでは =
演算子のオーバーロードによって実装しています(ソース例は後述)。
上記の例ではb[0].intrface()
の呼び出しを行う際には、VBase::pf_intrface
関数ポインタを参照しBase<Derived1>::s_intrface()
が呼び出されることになります。さらにDerived1::intrface()
の呼び出しを行うことになります。この部分はコンパイラによるinline展開が期待できます。
VBase
型から元のDerived1
やDerived2
への変換を行うことも、強制的なキャストにより可能ですが、void*
で格納されたポインタの型を直接知る方法はありません。完全に安全な方法はないものの、以下のようにクラスごとに一意のID(TYPE_ID
)を設けて、キャスト実行時(get()
メソッド)にIDのチェックを行うようにしています。違う型を指定して get()
メソッドを呼び出したときは、エラーメッセージを表示するといった対処になります。
Base<T>
型としてのポインタが格納されるとT
型に正しく変換できない可能性(T
が多重継承している場合など)あるため、<type_trails>
のis_base_of
によりBase<T>
型の派生であることをコンパイル時に static_assert
による判定を行っています。
TWELITEモジュールのマイコンには十分なメモリもなければ、高度なメモリ管理もありません。しかしマイコンのメモリマップの末尾からスタックエリアまでの領域はヒープ領域として、必要に応じて確保できる領域があります。以下にメモリマップの概要を図示します。APPがアプリケーションコードで確保されたRAM領域、HEAPはヒープ領域、STACKはスタック領域です。
たとえdelete
できなくてもnew
演算子が有用である場面も想定されます。そのため、以下のようにnew, new[]
演算子を定義しています。pvHear_Alloc()
は半導体ライブラリで提供されているメモリ確保の関数で、u32HeapStart, u32HeapEnd
も同様です。0xdeadbeef
はダミーアドレスです。beefがdeadなのは変だとかいう指摘はしないでください。
例外も使えないため失敗したときの対処はありません。また、メモリ容量を意識せず確保を続けた場合、スタック領域と干渉する可能性もあります。
システム(MAC層など)が確保するメモリは約2.5KBです。
MWXライブラリでは、マイコンのリソースが小さい点、メモリーの動的確保ができない点を考慮し標準ライブラリで提供されるコンテナクラスの利用はせず、シンプルなコンテナクラスを2種類定義しています。コンテナクラスにはイテレータやbegin(), end()
メソッドを定義しているため、範囲for文やSTLのアルゴリズムの一部を利用できます。
クラス名
概要
smplbuf
配列クラスで、最大領域 (capacity) と最大領域範囲内で都度サイズを指定できる利用領域(size)を管理します。
また本クラスは stream インタフェースを実装しているため、<< 演算子を用いてデータを書き込むことができます。
smplque
FIFOキューを実装しています。キューのサイズはテンプレートのパラメータで決定します。割り込み禁止を用いキューを操作するためのテンプレート引数もあります。
コンテナクラスではメモリの確保方法をtemplate
引数のパラメータとして指定します。
クラス名
内容
alloc_attach
すでに確保済みのバッファメモリを指定する。
Cライブラリ向けに確保したメモリ領域を管理したいとき、同じバッファ領域の分断領域として処理したい時などに使用します。
alloc_static
クラス内に静的配列として確保する。
事前にサイズが決まっていたり、一時利用の領域として使用します。
alloc_heap
ヒープ領域に確保する。 システムのヒープに確保後は破棄できませんが、初期化時にアプリケーションの設定などに従い領域を確保するといった使い方に向いています。
MWXライブラリでは、バイト列やビット列の操作、printf
相当の処理を行う処理に可変数引数を用いています。下記の例は指定のビット位置に1をセットする処理です。
この処理では template のパラメータパック (typename...
の部分) で、再帰的な処理を行い引数の展開を行っています。上記の例ではconstexpr
の指定があるため、コンパイル時に計算が行われマクロ定義やb2
のようなconst
値の指定と同等の結果になります。また変数を引数として動的に計算する関数としても振る舞うこともできます。
以下の例では、expand_bytes
関数により、受信パケットのデータ列からローカル変数に値を格納しています。パラメータパックを用いた場合各引数の型を把握できるため、下記のように、受信パケットのバイト列から、サイズや異なる型にあった値を格納することができます。
イテレータはポインタの抽象化で、例えばメモリの連続性のないようなデータ構造においても、あたかもポインタを使ったようにデータ構造にアクセスできる効果があります。
C++のSTLでは、begin()
メソッドで得られるコンテナの先頭を示すイテレータと、end()
メソッドで得られるコンテナの末尾の「次」を示すイテレータの組み合わせが良く用いられます。
コンテナの末尾の「次」をend()
としているのは、以下のような記述を想定しているためです。MWXライブラリでもこれに倣ってコンテナの実装を行っています。
イテレータを標準ライブラリの仕様に適合させることで、範囲for文が利用できたり、標準ライブラリのアルゴリズムを利用できるようになります。
(MWXライブラリではC++標準ライブラリに対する適合度や互換性についての検証は行っていません。動作確認の上利用ください)
以下の例では、通常のポインタでは連続的なアクセスができないFIFOキューのイテレータ、さらに、FIFOキューの構造体の特定メンバー(例ではX軸)のみを抽出するイテレータを利用する例です。
以下は smplque
クラスのイテレータの実装の抜粋です。このイテレータでは、キューオブジェクトの実体と、インデックスにより管理しています。キューのメモリが不連続になる(末尾の次は先頭を指す必要があるリングバッファ構造)部分はsmplque::operator []
で解決しています。オブジェクトのアドレスが一致することとインデックスが一致すればイテレータは同じものを指していることになります。
この実装部分には <iterator>
が要求する typedef なども含まれ、より多くのSTLのアルゴリズムが適用できるようになります。
構造体を格納したコンテナ中の、特定構造体メンバーだけアクセスするイテレータは少々煩雑です。構造体のメンバーにアクセスするメンバー関数を予め定義しておきます。このメンバー関数をパラメータ(R& (T::*get)()
)としたテンプレートを定義します。Iter
はコンテナクラスのイテレータ型です。
値にアクセスするoperator *
この上述のメンバー関数を呼び出しています。(*_p
はaxis_xyzt
構造体で、(*_p.*get)()
は、T::*get
に&axis_xyzt::get_x
を指定した場合_p->get_x()
を呼び出します)
_axis_xyzt_iter_gen
クラスはbegin(), end()
のみを実装し、上記のイテレータを生成します。これで範囲for文やアルゴリズムが利用できるようになります。
このクラス名は非常に長くなりソースコード中に記述するのは困難です。このクラスを生成するためのジェネレータ関数を用意します。下記の例では末尾の行の get_axis_x()
です。このジェネレータ関数を用いることで冒頭のようなauto&& vx = get_axis_x(que);
といった簡潔な記述になります。
また、この軸だけを抽出するイテレータは、配列型のsmplbuf
クラスでも同様に利用できます。
ユーザ定義クラスによりアプリケーション動作を記述するため、代表的なハンドラは必須メソッドとして定義が必要ですが、それ以外に多数ある割り込みハンドラ、イベントハンドラ、ステートマシンの状態ハンドラをすべて定義するのは煩雑です。ユーザが定義したものだけ定義され、それのみのコードが実行されるのが理想です。
MWXライブラリでは、数の多い DIO割り込みハンドラ(TWELITEハード上は単一の割り込みですが、利用しやすさのためDIO一つずつにハンドラを割り当てることにしました)などを、テンプレートによる空のハンドラーとして定義した上、ユーザ定義のメンバー関数をそのテンプレートの特殊化することにより定義する手法を採用しました。
実際のユーザ記述コードは、マクロ化やヘッダファイルのインクルードを行うことで、簡素化されていますが、上記は解説のために必要なコードを含めています。
TWENETからの割り込みハンドラからmy_app_def::cbTweNet_u8HwInt()
が呼び出されます。cppファイル中では、int_dio_handler<12>
のみが特殊化されて記載された内容でインスタンス化されます。12番以外はhppファイル中のテンプレートからインスタンス化されます。結局以下のように展開されることになります。
最終的に、コンパイラの最適化により12番以外のコードは無意味と判断されコード中から消えてしまうことが期待できます(ただし、上記のように最適化されることを保証するものではありません)。
つまりユーザコード上では12番の割り込み時の振る舞いを定義したいときはint_dio_handler<12>
を記述するだけで良い、ということになります(注:DIO割り込みを有効にするには attachInterrupt()
を呼び出す必要があります)。登録しないハンドラはコンパイル時の最適化により低コストな呼び出しで済むことが期待できます。
ユーザが関数を定義したときにこれを有効にし、定義しない場合は別の関数を呼び出す手法として、リンク時の解決方法があります。下記のように__attribute__((weak))
の指定します。ユーザコードで wakeup()
関数が定義された場合は、ユーザーコードを関数をリンクし、未定義の場合は中身が空の関数をリンクします。
上記ハンドラの実装においてはweak指定したメンバー変数を明示的に生成する必要があり、またinline化による最適化が行いにくいため使用しませんが、wakeup()
といったいくつかのTWENETからのコールバック関数の受け皿としてweak指定の関数を定義しています。
ストリームクラスは、主にUART(シリアルポート)の入出力に用います。MWXライブラリでは、出力用の手続きを主に定義しています。一部入力用の定義もあります。
ここでは派生クラスが必要とする実装について解説します。
上記は1文字書き出すwrite()
メソッドの実装です。親クラスのstream<serial_jen>
からはキャストを実行するget_Drived()
メソッドを用いて、serial_jen::write()
メソッドにアクセスしています。
必要に応じて write(), read(), flush(), available()
といったメソッドを定義します。
書式出力にはMarco Paland氏によるprintfライブラリを利用しています。MWXライブラリから利用するための実装が必要になります。下記の例で派生クラスのserial_jen
で必要なことは1バイト出力のための vOutput()
メソッドを定義することと、vOutput()
がstaticメソッドであるため出力のための補助情報を親クラスのpvOutputContext
に保存することです。
get_pfcOutput()
により、派生クラスで定義したvOutput()
関数を指定し、そのパラメータとしてpvOutputContext
が渡されます。上記の例では<<
演算子がint型で呼び出されたときserial_jen::vOutput()
とUART用に設定済みのTWE_tsFILE*
をfctprintf()
関数に渡しています。
Wire
クラスでは、2線デバイスとの送信・受信時に、通信開始から終了までを管理する必要があります。ワーカーオブジェクトを利用する記述について内容を記述します。
periph_twowire::writer
クラスの抜粋です。streamインタフェースを実装するために mwx::stream<writer>
を継承しています。steamインタフェースを利用するために write()
と vOutput()
メソッドの実装を行っています。
コンストラクタでは2線シリアルの通信開始を、デストラクタで通信終了のメソッドを呼び出しています。また、operator bool()
演算子では、2線シリアルのデバイスの通信開始に成功した場合 true
を返すようになっています。
get_writer()
メソッドによりオブジェクトwrt
を生成します。この時にオブジェクトのコピーは通常発生しません。C++コンパイラのRVO(Return Value Optimization)という最適化により、writer
はwrt
に直接生成されるためコピーは発生せず、コンストラクタで実行されているバスの初期化を多重に行ったりすることはありません。ただしRVOはC++の仕様では保証されておらず、念のためMWXライブラリ中ではコピー、代入演算子の削除、moveコンストラクタを定義しています(moveコンストラクタが評価される可能性はないと考えられますが)。
if節の中の wrt
は、まずコンストラクタにより初期化され同時に通信開始します。通信開始でエラーがなければ、条件判定時のbool演算子がtrue
を返し、if節スコープ内の処理が行われます。スコープを脱出するとデストラクタにより、2線シリアルバスの利用終了処理を行います。通信の相手先がない場合は false
が戻り、wrt
オブジェクトは破棄されます。
Wire, SPI特有の定義としてoperator << (int)
の定義をオーバーライドしています。ストリームのデフォルトの振る舞いは、数値を文字列に変換して出力するのですが、WireやSPIで数値文字列をバスに書き込むことは稀で、反対に設定値など数値型のリテラルをそのまま入力したいことが多いのですが、数値型リテラルは多くの場合int型として評価されるため、この振舞を変更します。
ここではint型の値については8bitに切り詰めて、その値を出力しています。
新しいプロジェクトの作成は、すでにあるサンプルアクトのディレクトリを別の名前でコピーし、ファイル名の編集を行います。
コピー先のディレクトリは MWSDK 配下のディレクトリでなくても構いません。ただし、ディレクトリ名に空白文字や日本語名が含まれてはいけません。
MWSDK以外のディレクトリにコピーした場合 VS Code のワークスペース定義の一部が機能しなくなります。ワークスペースにmwxライブラリのソースコードディレクトリが追加されている場合は、新たに設定してください。
ライブラリソース設定をしなくてもビルドには影響はありません。より深いコード解釈をVSCode上で行い、編集効率を上げるための設定です。
プロジェクトのファイル構造は以下のようになっています(ここでは PingPong
を例に挙げます)。
この PingPong
ディレクトリを別の場所(ただしディレクトリ名に日本語や空白が含まない)にコピーします。
編集の必要があるのは、PingPong.cpp
のファイル名です。これをディレクトリ名と同じAlphaBravo.cpp
に変更します。
build\build-BLUE.cmd
を実行してBINファイルが生成されれば完了です(Windows10)。
Linux/WSL/macOS ではmake TWELITE=BLUE
を実行して、ビルドが成功するか確認します。
プロジェクトのディレクトリ名と同じ名前の .cpp ファイルは必須です。 (MWSDK 2020-04 では、任意のファイル名でOKになりました)
ビルド対象のファイルを追加する場合は build/Makefile を編集します。編集方法は Makefile の解説をご覧ください。
Sample Acts
アクトの動作を理解するため、いくつかのサンプルを用意しています。
サンプルは MWSDK をインストールしたディレクトリにある Act_samples
にあります。
最初にBRD_APPTWELITEとParent_MONOSTICKの解説に目を通すようにしてください。
最新版のコードや MWSDK バージョン間の修正履歴を確認する目的で Github にソース一式を置いています。以下のリンク先を参照してください。
アクトのサンプル中で以下の項目は共通の設定項目になり、以下で解説します。
サンプルアクト共通として以下の設定をしています。
アプリケーションID 0x1234abcd
チャネル 13
アプリケーションIDとチャネルはともに他のネットワークと混在しないようにする仕組みです。
アプリケーションIDが異なる者同士は、チャネルが同じであっても混信することはありません。ただし、別のアプリケーションIDのシステムが頻繁に無線送信しているような場合はその無線送信が妨害となりますので影響は出ます。
チャネルは通信に使う周波数を決めます。TWELITE無線モジュールでは原則として16個のチャネルが利用でき、通常のシステムでは実施しないような極めて例外的な場合を除き、違うチャネルとは通信できません。
サンプルアクト共通の仕様として、パケットのペイロード(データ部)の先頭には4バイトの文字列(APP_FOURCHAR[]
)を格納しています。種別の識別性には1バイトで十分ですが、解説のための記述です。こういったシステム特有の識別子やデータ構造を含めるのも混信対策の一つです。
Makefileはbuild/Makefileに格納されています。makeコマンドを実行することで、アクトをビルドするよう予め定義されています。
MWSDK 2020-04 では、プロジェクトディレクトリ中の .cpp ファイルを自動で検出するため、通常は Makefile の修正は不要です。
ソースファイルをサブディレクトリに格納するような場合は、編集が必要になります。
MWSDK 2019-12では、.cpp ファイルが複数ある場合は、Makefile の編集が必要です。
プロジェクトディレクトリを他の環境からコピーしたあとには、必ずbuild/objs_???
ディレクトリを削除してください。他の環境での中間ファイルが残っているとmakeがエラーになります。
(MWSDK 2020-04)
USE_APPDEPS=0 を付加して clean してから、改めて make コマンドを実行することでエラーを回避できます。
$ make USE_APPDEPS=0 TWELITE=BLUE clean
...
$ make TWELITE=BLUE
ビルド対象をBLUEまたはREDで指定します。TWELITE BLUEならmake TWELITE=BLUE
と指定します。
ビルドを実行します。通常は省略してmake TWELITE=BLUE
のように実行します。
ビルドの中間ファイルを削除します。make TWELITE=BLUE clean
のように実行します。
すべての中間ファイルを削除します。make cleanall
のように実行します。buildディレクトリのobjs_???ディレクトリをすべて削除するのと同じです。
1 (デフォルト) を設定すると、ファイルの依存関係をもとに、ビルドファイルを決定します。例えば、ヘッダファイルに変更があった場合に関連するソースファイルが再コンパイル対象となります。
0 では依存関係を評価しません。0 に設定した場合、矛盾ある中間ファイルが残っていても makefile がエラーになりません。
アクトの規模に応じて、また、ビヘイビアの定義をする場合には、通常はソースファイルを分割してビルドします。
ビルドファイルの一つは{プロジェクトフォルダ名.cpp}です。
他にファイルを定義する場合は、プロジェクトフォルダのbuild/Makefile
を編集します。
上記はサンプルPAL_AMB-bihaviorでのMakefileの例です。
バージョン番号を指定します。ビルド結果ファイル名に反映されます。
コンパイル中は -DVERSION_MAIN=0
-DVERSION_SUB=1
-DVERSION_VAR=0
のように定義として渡されます。
(MWSDK 2020-04)
サブディレクトリにファイルを配置しない場合は、追加指定は不要になりました。プロジェクトファイルにある .c .cpp ファイルがすべて追加されます。
ソースファイルを追加する際に必要なのはAPPSRC_CXX
とAPP_COMMON_SRC_DIR_ADD?
です。
サブディレクトリにソースファイルを配置する場合は必ずディレクトリ APP_COMMON_SRC_DIR_ADD? の指定が必要です。
ソースファイル名をAPPSRC_CXXに追記します。このファイル名にはディレクトリ名が含まれてはいけません。サブディレクトリにあるものもディレクトリなしで指定します(つまり同じファイル名がサブディレクトリにある場合は、ビルドが失敗します)
次にソースファイルがプロジェクトディレクトリ以外の場所に格納されている場合の検索パスを指定します。最大4つまで設定できます。
ディレクトリの指定はMakefileからの相対パスになります。
その他にもいくつかのオプションをコンパイラ・リンカに渡すことができます。
指定
内容
CXXFLAGS
C++ソースファイルに対してコンパイルオプションを指定します。
CFLAGS
C/C++ソースファイルに対してコンパイルオプションを指定します。
INCFLAGS
ヘッダファイルのインクルードファイル指定をします。
OPTFLAGS
特別な理由があって-Os以外のコンパイルオプションを適用したい場合に定義します。
LDFLAGS
リンカオプションを指定します。(上記Makefileのコメントには記述はありませんが指定は可能です)
ここではアクト(BINファイル)の書き込みと実行について解説します。
この節はWindows10環境のユーティリティTWE-Programmerを用いた解説です。
Linux/macOSではtweterm.pyを用います。本ページでの書き込み・実行の流れを参照いただいた上、tweterm.pyを利用ください。
TWE-Programmer, tweterm.pyはBINファイルの書き込み機能とターミナル機能がありますが、ターミナル機能については各システム用のターミナルソフトを利用することもできます。
Windows10: TeraTerm
Linux/macOS: screen など
出来上がったBINファイルをTWELITEに書き込むにはTWE-Programmerを使用します。詳しい使い方はこちらを参照ください。
BINファイルが出来上がれば、実機で動作させることになります。繊細な電子部品ですので、取り扱いには十分注意してください。以下に代表的な注意事項を記載します。
特にTWELITE Rを用いている場合は、多くの場合電子基板がケースなどを介さず直接外部に触れる状態で使用するため、意図しないショートやノイズなどUSBデバイスが正常に動作しない状態になる場合があります。
この場合は、アプリケーションを終了させUSBデバイスを抜き差しすることで通常は回復します。最悪、USBデバイスの破損やPCの破損も考えられます。
電子基板の取り扱いには十分注意してください。
回路の間違い
電源を入れる前にはもう一度回路を確認してください。
電池の逆差しや過大電圧には注意してください。
静電気
人感がない電圧であっても半導体の故障になりえます。大掛かりな対応をしなくとも、金属部に触れてから作業する・リストバンド・専用マットなど簡易にできる対応でも相応の効果はあります。
金属物などが触れることでのショート
電子基板の近くには金属物がないようにしてください。クリップなどが散らかっているとこれが原因でショートすることもありますし、電池が大放電して加熱する危険な状況も考えられます。
デバイスが認識されれば、新たにCOMポートが追加されます。複数あってわかりにくい場合は、抜いた状態でのCOMポートと刺した状態でのCOMポートの様子を観察してみてください。
デバイスマネージャでデバイスの存在が確認できない場合は、FTDI社のドライバ導入を行ってみてください。FT232R用のドライバを選択します。
{MWSDKインストールディレクトリ}/Tools/TWE-Programmerディレクトリを開き、TWE-Programmer.exe をダブルクリックします。
TWE-Programmer.exe を起動すると以下のような画面になります。画面例では、起動前に MONOSTICK を差し込んでいて COM6 に割り当てられています。
MONOSTICK や TWELITE R が表示されない場合は以下を試してください。
TWE-Programmer を終了する。
MONOSTICK や TWELITE RをPCから抜く(取り外す)。
もう一度MONOSTICK や TWELITE Rを差し込む。
TWE-Programmer を起動する。
これでもうまくいかない場合は以下も試してください。
PCから可能な限り他のUSBデバイスを取り外す。
PCを再起動してからMONOSTICK や TWELITE Rを差し込んでみる。
FTDI社のドライバを再導入してみる。
ファイルダイアログから BIN ファイルを指定します。
BINファイルの書き込みは、BINファイルを指定すると自動で行われます。
書き込みが成功するとTWELITEは自動でリセットして、ターミナルが表示されます(ターミナル接続するにチェックが入っている場合)。
MWSDK添付のアクトファイルは始動時にメッセージを表示します。何か表示されれば書き込みは無事終了し、TWELITE 無線モジュール上のアクトが無事に動作していることが確認できます。
ターミナルを開いたままで、アクトを編集して再度書き込みたいときはCtrl+Alt+W
キーを押します。
書き込みに失敗したときは、ターミナルは開かず、TWE-Programmerのウインドウ背景がピンク色になります。
書き込みが失敗したときは (再)書き込みボタンを押して、もう一度書き込みを実行してください。
TWE-Programmer が反応しないなど、うまくいかない場合は、以下を実行した上、USBへの接続からやり直してください。
TWE-Programmer を終了する
MONOSTICK や TWELITE Rを取り外す
場合によってはPCの再起動を行ってください。
TWELITE PAL での書き込み中に、ハードウェア・ウォッチドッグタイマーが作動し書き込みが中断する場合があります。再度、書き込みを行ってください。
MWXライブラリで記述したアプリケーションプログラムをアクト(Act)と呼びます。まずは、これをビルドして書き込みます。
ビルドディレクトリ構成について
ビルドスクリプトについて
VS Code でのビルドについて
本ページでは、いくつかのビルド方法を記載していますが、いずれの方法も最終的にはmakeコマンドを実行しています。詳細はMakefileの解説を参照ください。
MWSDKをインストールしたディレクトリMWSDK_ROOT (例 C:\MWSDK)
を開きます。以下のような構成になっています。
アクトファイルはAct_samples
以下に格納しています。(以下は一部割愛しています)
これらのアクトは、MWXライブラリの記述の参考となるシンプルな例ですが、多くのアクトは以下の機能を有しています。
センサー値を取得する
センサー値取得後、無線パケットを親機宛に送信する
送信完了後、一定時間スリープする(または割り込みを待つ)
Parent-MONOSTICK
のアクトによりパケットの受信と表示を行っています。この親機用のアクトは、アスキー形式で出力しています。 (:00112233AABBCC...FF[CR][LF]
のような : で始まり、途中は16進数のバイトをアスキー文字2字で表現する形式です。末尾の??は同様に2字のバイトとなりますがLRCというチェックサムバイトになります。参考:アスキー形式)
実際に動作させてみるときは、以下の組み合わせを試してみてください。
親
子
解説
親機はM1ピンをLOW(GNDレベル)にして起動する。通常モード(常時稼働)にて、App_TweLiteのような動作を確認できます。
子機同士2台使って動作します。片方から Ping パケットを送ると、相手方から Pong パケットが戻ってきます。
その他
子機用のアクトのパケット送信を確認できます。
では、アクトの中から PingPong のディレクトリの中を見てみましょう。
Act_samples
にある他のアクトもビルドできます。その場合、ディレクトリ名・ファイル名は読み替えるようにしてください。
必ずディレクトリ直下にディレクトリと同名の .cpp
ファイルが必要です。
小規模なアクトならこの.cpp
ファイル内に記述します。規模が大きくなってきたときはMakefileの解説を参考にして複数のファイルに分割してビルドすることが出来ます。
PingPong
ディレクトリ直下にアクトファイル PingPong.cpp
があります。ディレクトリ名を変更した場合は、必ず .cpp
ファイルの名前もディレクトリ名と同名にします。
次にビルドディレクトリを開きます。
ビルドに必要なスクリプトとMakefile
が格納されています。
ビルドスクリプトは、エラーがあまり出ることのない、完成済みのアクト、サンプル提供のアクトなどに向いています。
以下は Windows10用のスクリプト(バッチファイル)です。
バッチファイル名
内容
build-BLUE.cmd
TWELITE BLUE用
build-RED.cmd
TWELITE RED用
実行後にbinファイルが生成されPingPong_BLUE_???.bin
またはPingPong_RED_???.bin
のファイル名になります。???はバージョン番号やライブラリのバージョン文字に置き換わります。
BINファイルが出来上がればビルド成功です。
objs_BLUE
ディレクトリはビルド中に生成された中間ファイルです。削除してもかまいません。
ビルドを迅速にするためパラレルビルドのオプションを設定しています。このためエラーが出た場合はエラーメッセージを視認しづらくなっています。
エラーメッセージを効率的に参照したい場合は VS Code などの開発ツールの利用を推奨します。
build-clean.cmd
を実行すれば、objs_
で始まるディレクトリを消去します。BINファイルは消去しません。
ビルドがうまくいかない場合は、まずエラーメッセージを確認して下さい。errorという文字列が含まれる行中のメッセージから、エラー原因が容易に特定できる場合も少なくありません。
objs_???
ディレクトリにある中間ファイルを削除してから再実行してみてください。(他の環境でビルドした中間ファイルが残っているとmake clean
を含めすべての操作が失敗します)
コマンドライン環境でのビルドについて補足します。
Windows10 では特別な理由がない限りWSL (Windows Subsystem for Linux)は、MWSDKでアクトをビルドするのに必須ではありません。WSLについて、既に知見があり特に利用したい方のみ本節をご覧ください。ここではWSLのインストール方法や使用方法については紹介しません。
WSL のディストリビューション Ubuntu 18.04 を前提として記述します。
WSL で動作するプログラム書き込みツールは用意していません。Windows10上のTWE-Programmerを利用ください。
コマンドライン(bash)についての利用の知識が必要です。
OS環境によっては各実行プログラムの動作時にセキュリティ警告が出る場合があります。警告を抑制する設定が必要になります。(警告を抑制してプログラムを動作する運用を行うかは、お客自身またはシステム管理者に相談の上、ご判断ください)
コマンドラインでのビルドは、bash(Bourne-again shell)が動作するウインドウでmake
を実行します。事前に環境変数MWSDK_ROOT
が正しく設定されていることを確認してください。例えばC:/MWSDK
にインストールした場合は ~/.profile
に以下のような設定を行います。
コマンドライン(bash)よりmakeを実行します。make
がない場合はパッケージをインストールする必要があります。
Linux/WSL環境ではmake
またはbuild-essential
パッケージをインストールします。
macOS環境ではXcodeでCommand Line Toolsをインストールします。
ビルドは以下のようになります。
ビルドが行われると objs_??? ディレクトリが作成され、その中に中間ファイルが生成されます。このファイルはコンパイルした環境に依存しているため、他の環境のファイルが残っているとmakeがエラーとなりビルドが失敗します。
makeがエラーとなった場合は直接objs_???
ディレクトリを削除してください。
詳細はMakefileの解説をご覧ください。
コマンド例
解説
make TWELITE=BLUE
TWELITE BLUE用にビルド
make TWELITE=RED
TWELITE RED用にビルド
make cleanall
中間ファイルの削除
OS環境によっては各実行プログラムの動作時にセキュリティ警告が出る場合があります。警告を抑制する設定が必要になります。(警告を抑制してプログラムを動作する運用を行うかは、お客自身またはシステム管理者に相談の上、ご判断ください)
VS Code では、Act_samplesディレクトリ直下にあるワークスペースファイルを開くか、Act_samples以下のアクトディレクトリを開きます。
以下の例では英語インタフェースの画面例で、ワークスペースを開いています。
ワークスペースを開き [Terminal>Run Task...]
を選択します。
ビルドするTWELITE無線モジュールの種別(BLUE/RED)とアクト名を選択します。以下の例ではBuild for TWELITE BLUE PingPong (TWELITE BLUE用/PingPongアクト) を選択しています。選択後すぐにビルドが始まります。
ビルド中の経過は画面下部の TERMINAL 部分に出力されます。
正しくビルドできた場合、上記画面例の反転表示部のように .elf ファイルが生成されるメッセージがサイズ情報(text data bss dec hex filename
と記載がある部分)とともに出力されます。
またBINファイル(上記では PingPong_BLUE_???.bin
)ファイルがbuildディレクトリ下に出来上がっているはずです。確認してみてください。
ビルド定義には Windows10 のファイルシステムに適合しないディレクトリ名 (例:/c/User/...
)を変換する(例:C:/User/...
) 定義を追加しています。
変換は完全ではありませんが、コンパイルメッセージからエラーが発生しているファイル名と行番号を抽出できます。
.vscode/tasks.json
中の実行コマンドは sh -c "make ... | sed -E -e s#..."
のようにコマンド呼び出しすることで、出力メッセージ中のドライブ名相当部の文字列を書き換えています。
ビルドがうまくいかない場合は、まずエラーメッセージを確認して下さい。errorという文字列が含まれる行中のメッセージから、エラー原因が容易に特定できる場合も少なくありません。
念のため、クリーン(objs_???
ディレクトリにある中間ファイルの削除)を行い、ビルドを再実行してみてください。(他の環境でビルドした中間ファイルが残っているとmake clean
を含めすべての操作が失敗します)
事前にコマンドライン(bash)でビルドが出来ることを確認しておいてください。
WSL環境でビルドしたい場合は、.vscode/tasks.json
を編集してください。以下の例の4行目 "windows":
始まる行を追加(またはコメントアウト)します。
MWSDKでは、アクト(ソースコード)記述をより容易に行うため、VisualStudio Code(VS Code)を紹介しています。添付のアクトには、VS Codeで適切にコード解釈が行われるように設定したファイルが含まれます。
VS Codeはソースファイルやヘッダファイルを読み込み、ソースコードを解釈し、これによりソースコードの記述の助けとなる関数定義情報や、関数・メソッド名の補完などを行います。C言語の開発に比べて、MWXライブラリでは読み込まれるヘッダファイルの分量が多くなります。環境によってはエディタの動作が重く感じる場合があります。
当サポートでは VSCode のインストール方法、使用方法のお問い合わせについては対応いたしません。一般で得られる情報を参照ください。
環境によっては、インストールのためにセキュリティ設定などが必要になる場合があります。インストールの可否はシステム管理者にご確認の上、手順は配布元や一般の情報を参考にしてください。
VSCode では、以下のことができます。
ソースコードの編集
GIT への接続(お客様が独自にソース管理を GIT 上で行う場合)
ソースコード解釈に基づく intellisense(*全ての定義が正確に解釈されることを保証するわけではありません)
VSCode はリンク先より入手してください。
Visual Studio Code が C/C++ 言語の記述を解釈できるようにするために、プラグインをインストールします。
C/C++ for Visual Studio Code
MWXライブラリのサンプルには .vscode の定義を含めています。この定義は MWSDK_ROOT 環境変数を用い、ライブラリのソースコード({MWSDK_ROOT}/TWENET/current
以下)を特定しています。
VS Code のソースコードの解釈はコンパイラでの解釈とは完全には一致しません。またソースコードの編集状況によっては解釈がより不完全な状態になる場合もあります。
IO通信(標準アプリケーションApp_Tweliteの基本機能)
App_TweLite で必要な配線と同じ配線想定したボードサポート <BRD_APPTWELITE>
を用いたサンプルです。
このサンプルは App_TweLite と通信できません。
M1を読み取り、親機か子機を決める。
DI1-DI4 の値を読み取ります。Buttons クラスにより、チャタリングの影響を小さくするため、連続で同じ値になったときにはじめて変化が通知されます。変化があったときには通信を行います。
AI1-AI4 の値を読み取ります。
DIの変化または1秒おきに、DI1-4, AI1-4, VCC の値を、自身が親機の場合は子機へ、子機の場合は親機宛に送信します。
受信したパケットの値に応じで DO1-4, PWM1-4 に設定する。
役割
例
親機
最低限 M1=GND, DI1:ボタン, DO1:LEDの配線をしておく。
子機
最低限 M1=オープン, DI1:ボタン, DO1:LEDの配線をしておく。
全てのアクトで<TWELITE>
をインクルードします。ここでは、シンプルネットワーク <NWK_SIMPLE>
とボードサポート <BRD_APPTWELITE>
をインクルードしておきます。
サンプルアクト共通宣言
長めの処理を関数化しているため、そのプロトタイプ宣言(送信と受信)
アプリケーション中のデータ保持するための変数
大まかな流れは、各部の初期設定、各部の開始となっています。
このオブジェクトはTWENETの中核としてふるまいます。
ボードの登録(このアクトでは<BRD_APPTWELITE>
を登録しています)。以下のように use の後に <>
で登録したいボードの名前を指定します。
ユニバーサル参照(auto&&
)にて得られた戻り値として、参照型でのボードオブジェクトが得られます。このオブジェクトにはボード特有の操作や定義が含まれます。
ここではボードオブジェクトを用いbrd.get_M1()
を呼び出すことでM1ピンの設定を読み出します。1
がスイッチをセットしGND側になっていることを示します。M1 がセットされた場合にu8devid
を0x00
に、そうでなければ0xFE
を指定しています。これはネットワーク上での役割が親機(0x00
)であるか子機(0xFE
)あるかを決定します。この値は <NWK_SIMPLE>
の設定に使用します。
the_twelite
に設定を反映するには <<
を用います。
TWENET::appid(APP_ID)
アプリケーションIDの指定
TWENET::channel(CHANNEL)
チャネルの指定
TWENET::rx_when_idle()
受信回路をオープンにする指定
<<, >>
演算子は本来ビットシフト演算子ですが、その意味合いと違った利用とはなります。MWXライブラリ内では、C++標準ライブラリでの入出力利用に倣ってライブラリ中では上記のような設定やシリアルポートの入出力で利用しています。
次にネットワークを登録します。
1行目は、ボードの登録と同じ書き方で <>
には <NWK_SIMPLE>
を指定します。
2行目は、<NWK_SIMPLE>
の設定です。先ほどボードから得たM1ピンの状態によって親機アドレス(0x00
)か子機アドレス(0xFE
)を格納したu8devid
を指定します。
setup()
関数の末尾で the_twelite.begin()
を実行しています。
ADC(アナログディジタルコンバータ)を取り扱うクラスオブジェクトです。
初期化Analogue.setup()
で行います。パラメータのtrue
はADC回路の安定までその場で待つ指定です。2番目のパラメータは、ADCの開始をTimer0に同期して行う指定です。
ADCを開始するにはAnalogue.begin()
を呼びます。パラメータはADC対象のピンに対応するビットマップです。
ビットマップを指定するのにpack_bits()
関数を用います。可変数引数の関数で、各引数には1を設定するビット位置を指定します。例えばpack_bits(1,3,5)
なら2進数で 101010
の値が戻ります。この関数はconstexpr
指定があるため、パラメータが定数のみであれば定数に展開されます。
パラメータと指定されるBRD_APPTWELITE::
にはPIN_AI1..4
が定義されています。App_Tweliteで用いるAI1..AI4に対応します。AI1=ADC1, AI2=DIO0, AI3=ADC2, AI4=DIO2 と割り当てられています。PIN_ANALOGUE::
にはADCで利用できるピンの一覧が定義されています。
初回を除き ADC の開始は、割り込みハンドラ内で行います。
DIO (ディジタル入力) の値の変化を検出します。Buttonsでは、メカ式のボタンのチャタリング(摺動)の影響を軽減するため、一定回数同じ値が検出されてから、値の変化とします。
初期化は Buttons.setup()
で行います。パラメータの 5 は、値の確定に必要な検出回数ですが、設定可能な最大値を指定します。内部的にはこの数値をもとに内部メモリの確保を行っています。
開始は Buttons.begin()
で行います。1番目のパラメータは検出対象のDIOです。BRD_APPTWELITE::
に定義されるPIN_DI1-4
(DI1-DI4) を指定しています。2番めのパラメータは状態を確定するのに必要な検出回数です。3番めのパラメータは検出間隔です。4
を指定しているので4msごとに5回連続で同じ値が検出できた時点で、HIGH, LOWの状態が確定します。
ButtonsでのDIO状態の検出はイベントハンドラで行います。イベントハンドラは、割り込み発生後にアプリケーションループで呼ばれるため割り込みハンドラに比べ遅延が発生します。
App_Twelite ではアプリケーションの制御をタイマー起点で行っているため、このアクトでも同じようにタイマー割り込み・イベントを動作させます。もちろん1msごとに動作しているシステムのTickTimerを用いても構いません。
上記の例の1番目のパラメータはタイマーの周波数で32Hzを指定しています。2番目のパラメータをtrue
にするとソフトウェア割り込みが有効になります。
Timer0.begin()
を呼び出したあと、タイマーが稼働します。
Serial オブジェクトは、初期化や開始手続きなく利用できます。
上記の例では "--- BRD_APPTWELITE(254) ---\r\n"
といった文字列を表示します。ここでは10進数の数値文字列として表示するためにint(u8devid)
としています。int
型でない場合は出力の意味合いが変わりバイトの書き出しになるので注意してください。
ループ関数は TWENET ライブラリのメインループからコールバック関数として呼び出されます。ここでは、利用するオブジェクトが available になるのを待って、その処理を行うのが基本的な記述です。ここではアクトで使用されているいくつかのオブジェクトの利用について解説します。
TWENET ライブラリのメインループは、事前にFIFOキューに格納された受信パケットや割り込み情報などをイベントとして処理し、そののちloop()
が呼び出されます。loop()
を抜けた後は CPU が DOZE モードに入り、低消費電流で新たな割り込みが発生するまでは待機します。
したがってCPUが常に稼働していることを前提としたコードはうまく動作しません。
DIO(ディジタルIO)の入力変化を検出したタイミングで available になり、Buttons.read()
により読み出します。
1番目のパラメータは、現在のDIOのHIGH/LOWのビットマップで、bit0から順番にDIO0,1,2,.. と並びます。例えば DIO12 であれば bp & (1UL << 12)
を評価すれば HIGH / LOW が判定できます。ビットが1になっているものがHIGHになります。
初回のIO状態確定時は MSB (bit31) に1がセットされます。スリープ復帰時も初回の確定処理を行います。
次にビットマップから値を取り出してu8DI_BM
に格納しています。ここではMWXライブラリで用意したcollect_bits()
関数を用いています。
collect_bits()
は、上述のpack_bits()
と同様のビット位置の整数値を引数とします。可変数引数の関数で、必要な数だけパラメータを並べます。上記の処理では bit0 は DI1、bit1 は DI2、bit2 は DI3、bit3 は DI4の値としてu8DI_BMに格納しています。
App_Twelite では、DI1から4に変化があった場合に無線送信しますので、Buttons.available()
を起点に送信処理を行います。transmit()
処理の内容は後述します。
ADCのアナログディジタル変換が終了した直後のloop()
で available になります。次の ADC が開始するまでは、データは直前に取得されたものとして読み出すことが出来ます。
ADC値を読むには Analogue.read()
または Analogue.read_raw()
メソッドを用います。read()
はmVに変換した値、read_raw()
は 0..1023 のADC値となります。パラメータにはADCのピン番号を指定します。ADCのピン番号はPIN_ANALOGUE::
やBRD_APPTWELITE::
に定義されているので、こちらを利用しています。
周期的に実行されるADCの値は、タイミングによってはavailable通知前のより新しい値が読み出されることがあります。
このアクトでは32Hzと比較的ゆっくりの周期で処理しているため、available判定直後に処理すれば問題にはなりませんが、変換周期が短い場合、loop()
中で比較的長い時間のかかる処理をしている場合は注意が必要です。
Analogue
には、変換終了後に割り込みハンドラ内から呼び出されるコールバック関数を指定することが出来ます。例えば、このコールバック関数からFIFOキューに値を格納する処理を行い、アプリケーションループ内ではキューの値を逐次読み出すといった非同期処理を行います。
Timer0
は32Hzで動作しています。タイマー割り込みが発生直後の loop()
で available になります。つまり、秒32回の処理をします。ここでは、ちょうど1秒になったところで送信処理をしています。
AppTweliteでは約1秒おきに定期送信を行っています。Timer0
がavailableになったときにu16ct
をインクリメントします。このカウンタ値をもとに、32回カウントが終わればtransmit()
を呼び出し無線パケットを送信しています。
u8DI_BM
とau16AI[]
の値判定は、初期化直後かどうかの判定です。まだDI1..DI4やAI1..AI4の値が格納されていない場合は何もしません。
受信パケットがある場合の処理です。
無線パケットを受信後のloop()
ではthe_twelite.receiver.available()
がtrue
を返します。受信パケットの取り扱いについては receive()
関数の解説で行います。
TWENETがパケットを受信してから時間をたってからの受信パケットの参照は安全ではありません。
内部のデータが新しい受信パケットの内容に上書きされ、この新しい内容を参照することになります。availableになってから速やかに処理するようにloop()
を記述してください。内部的には、1パケット分余分に保持できる余裕はあります。
無線パケットの送信要求をTWENETに行う関数です。本関数が終了した時点では、まだ無線パケットの処理は行われません。実際に送信が完了するのは、送信パラメータ次第ですが、数ms後以降になります。ここでは代表的な送信要求方法について解説します。
MWX_APIRET
はuint32_t
型のデータメンバを持つ戻り値を取り扱うクラスです。MSB(bit31)が成功失敗、それ以外が戻り値として利用するものです。
ネットワークオブジェクトをthe_twelite.network.use<NWK_SIMPLE>()
で取得します。そのオブジェクトを用いて.prepare_tx_packet()
によりpkt
オブジェクトを取得します。
ここではif文の条件判定式の中で宣言しています。宣言したpkt
オブジェクトはif節の終わりまで有効です。pktオブジェクトはbool型の応答をし、ここではTWENETの送信要求キューに空きがあって送信要求を受け付ける場合にtrue
、空きがない場合にfalse
となります。
パケットの設定はthe_twelite
の初期化設定のように<<
演算子を用いて行います。
tx_addr()
パラメータに送信先アドレスを指定します。0x00
なら自分が子機で親機宛に、0xFE
なら自分が親機で任意の子機宛のブロードキャストという意味です。
tx_retry()
パラメータに再送回数を指定します。例の1
は再送回数が1回、つまり合計2回パケットを送ります。無線パケット1回のみの送信では条件が良くても数%程度の失敗はあります。
tx_packet_delay()
送信遅延を設定します。一つ目のパラメータは、送信開始までの最低待ち時間、2番目が最長の待ち時間です。この場合は送信要求を発行後におよそ0msから50msの間で送信を開始します。3番目が再送間隔です。最初のパケットが送信されてから10ms置きに再送を行うという意味です。
ペイロードは積載物という意味ですが、無線パケットでは「送りたいデータ本体」という意味でよく使われます。無線パケットのデータにはデータ本体以外にもアドレス情報などいくつかの補助情報が含まれます。
送受信を正しく行うために、データペイロードのデータ並び順を意識するようにしてください。ここでは以下のようなデータ順とします。このデータ順に合わせてデータペイロードを構築します。
データペイロードには90バイト格納できます(実際にはあと数バイト格納できます)。
IEEE802.15.4の無線パケットの1バイトは貴重です。できるだけ節約して使用することを推奨します。1パケットで送信できるデータ量に限りがあります。パケットを分割する場合は分割パケットの送信失敗などを考慮する必要がありコストは大きくつきます。また1バイト余分に送信するのに、およそ16μ秒×送信時の電流に相当するエネルギーが消費され、特に電池駆動のアプリケーションには大きく影響します。
上記の例は、解説のためある程度の妥協をしています。節約を考える場合 00: の識別子は1バイトの簡単なものにすべきですし、Vccの電圧値は8ビットに丸めてもかまわないでしょう。また各AI1..AI4の値は10bitで、合計40bit=5バイトに対して6バイト使用しています。
上記のデータペイロードのデータ構造を実際に構築してみます。データペイロードは pkt.get_payload()
により simplbuf<uint8_t>
型のコンテナとして参照できます。このコンテナに上記の仕様に基づいてデータを構築します。
上記のように記述できますがMWXライブラリでは、データペイロード構築のための補助関数pack_bytes()
を用意しています。
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文です。サイズのわかっている配列やbegin(), end()
によるイテレータによるアクセスが可能なコンテナクラスなどは、この構文が使用できます。au16AIの型もコンパイル時に判定できるため auto&&
(ユニバーサル参照)で型の指定も省略してます。
通常のfor文に書き換えると以下のようになります。
これでパケットの準備は終わりです。あとは、送信要求を行います。
パケットを送信するにはpkt
オブジェクトのpkt.transmit()
メソッドを用います。戻り値としてMWX_APIRET
型を返していますが、このアクトでは使っていません。
戻り値には、要求の成功失敗の情報と要求に対応する番号が格納されています。送信完了まで待つ処理を行う場合は、この戻り値の値を利用します。
パケットの受信が確認できた(つまりthe_twelite.receiver.available()
がtrue
になった)ときの処理です。ここでは、相手方から伝えられたDI1..DI4の値とAI1..AI4の値を、自身のDO1..DO4とPWM1..PWM4に設定します。
まず受信パケットのデータrx
を取得します。rx
からアドレス情報やデータペイロードにアクセスします。
次の行では、受信パケットデータには、送信元のアドレス(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()
の戻り値により判定してください。
expand_bytes()
の戻り値は uint8_t*
ですが、末尾を超えたアクセスの場合はnullptr(ヌルポインタ)
を戻します。
読み出した4バイト文字列の識別子が、このアクトで指定した識別子と異なる場合は、このパケットを処理しません。
TWENETではアプリケーションIDと物理的な無線チャネルが合致する場合は、どのアプリケーションもたとえ種別が違ったとしても、受信することが出来ます。他のアプリケーションで作成したパケットを意図しない形で受信しない目的で、このような識別子やデータペイロードの構造などのチェックを行い、偶然の一致が起きないように対処することを推奨します。
シンプルネットワーク<NWK_SIMPLE>
でのパケット構造の要件も満足する必要があるため、シンプルネットワークを使用しない他のアプリケーションが同じ構造のパケットを定義しない限り(非常にまれと思われます)、パケットの混在受信は発生しません。
続いて、データ部分の取得です。DI1..DI4の値とAI1..AI4の値を別の変数に格納します。
先ほどのexpand_bytes()
の戻り値np
を1番目のパラメータにしています。先に読み取った4バイト文字列識別子の次から読み出す指定です。2番目のパラメータは同様です。
3番目以降のパラメータはデータペイロードの並びに一致した型の変数を、送り側のデータ構造と同じ順番で並べています。この処理が終われば、指定した変数にペイロードから読み出した値が格納されます。
確認のためシリアルポートへ出力します。
数値のフォーマット出力が必要になるのでformat()
を用いています。>>
演算子向けにprintf()
と同じ構文を利用できるようにしたヘルパークラスですが、引数の数が4つまでに制限されています。(Serial.printfmt()
には引数の数の制限がありません。)
1行目の "DI:%04b"
は"DI:0010"
のようにDI1..DI4のビットマップを4桁で表示します。3行目の"/%04d"
は"/3280/0010/0512/1023/1023"
のように Vcc/AI1..AI4の値を順に整数で出力します。5行目のmwx::crlf
は改行文字列を出力します。
これで必要なデータの展開が終わったので、あとはボード上のDO1..DO4とPWM1..PWM4の値を変更します。
digitalWrite()
はディジタル出力の値を変更します。1番目のパラメータはピン番号で、2番目はHIGH
(Vccレベル)かLOW
(GNDレベル)を指定します。
Timer?.change_duty()
はPWM出力のデューティ比を変更します。パラメータにデューティ比 0..1024 を指定します。最大値が1023でないことに注意してください(ライブラリ内で実行される割り算のコストが大きいため2のべき乗である1024を最大値としています)。0
にするとGNDレベル、1024
にするとVccレベル相当の出力になります。
親機アプリケーション(MONOSTICK用)
MONOSTICKを親機として使用するアクトです。子機からのパケットのデータペイロードをシリアルポートに出力します。
このアクトの解説の前にをご覧ください。
サンプルアクトの子機からのパケットを受信して、アスキー形式で出力する。
ボードサポートの使用とLEDの動作を設定しています。
2行目では赤色のLEDを無線パケットを受信したら200ms点灯する設定をしています。最初のパラメータはLED_TIMER::ON_RX
が無線パケット受信時を意味します。2番目は点灯時間をmsで指定します。
3行目はLEDの点滅指定です。1番目のパラメータはLED_TIMER::BLINK
が点滅の指定で、2番目のパラメータは点滅のON/OFF切り替え時間です。500msごとにLEDが点灯、消灯(つまり1秒周期の点滅)を繰り返します。
ここではNWK_SIMPLE::logical_id(0x00)
を設定し親機(論理ID=0x00)であることを指定します。
パケットを受信したとき、その内容を表示します。ここでは2種類の表示を行っています。
最初の出力は<NWK_SIMPLE>
の制御データを含めたデータをすべて表示します。制御データは11バイトあります。通常は制御情報を直接参照することはありませんが、あくまでも参考です。
1行目は出力用のシリアルパーサをローカルオブジェクトとして宣言しています。内部にバッファを持たず、外部のバッファを流用し、パーサーの出力機能を用いて、バッファ内のバイト列を書式出力します。
2行目はシリアルパーサーのバッファを設定します。すでにあるデータ配列、つまり受信パケットのペイロード部を指定します。serparser_attach pout
は、既にあるバッファを用いたシリアルパーサーの宣言です。pout.begin()
の1番目のパラメータは、パーサーの対応書式をPARSER::ASCII
つまりアスキー形式として指定しています。2番目はバッファの先頭アドレス。3番目はバッファ中の有効なデータ長、4番目はバッファの最大長を指定します。出力用で書式解釈に使わないため4番目のパラメータは3番目と同じ値を入れています。
6行目でシリアルポートへ>>
演算子を用いて出力しています。
7行目のSerial << mwx::flush
は、ここで出力が終わっていないデータの出力が終わるまで待ち処理を行う指定です。(Serial.flush()
も同じ処理です)
2番目の出力はより実践的です。ユーザが定義した並び順で書式を構成します。
1行目はアスキー書式に変換する前のデータ列を格納するバッファをローカルオブジェクトとして宣言しています。
2行目はpack_bytes()
を用いてデータ列を先ほどのbuf
に格納します。データ構造はソースコードのコメントを参照ください。pack_bytes()
のパラメータにはsmplbuf_u8 (smplbuf<uint8_t, ???>)
形式のコンテナを指定することもできます。
パケットのシーケンス番号は、<NWK_SIMPLE>
で自動設定され、送信パケット順に割り振られます。この値はパケットの重複検出に用いられます。
LQI (Link Quality Indicator)は受信時の電波強度に相当する値で、値が大きければ大きいほどより強い電界強度で受信できていることになります。ただしこの値と物理量との厳格な関連は定義されていませんし、環境のノイズと相対的なものでLQIがより大きな値であってもノイズも多ければ通信の成功率も低下することになります。
13,14,17行目は、シリアルパーサーの宣言と設定、出力です。
2台のシリアル接続しているTWELITEの片方からPING(ピン)の無線パケットを送信すると、他方からPONG(ポン)の無線パケットが返ってきます。
いずれかを2台。
でUART接続されているなど
全てのアクトで<TWELITE>
をインクルードします。ここでは、シンプルネットワーク をインクルードしておきます。
サンプルアクト共通宣言
長めの処理を関数化しているため、そのプロトタイプ宣言(送信と受信)
アプリケーション中のデータ保持するための変数
大まかな流れは、各部の初期設定、各部の開始となっています。
このオブジェクトはTWENETを操作するための中核クラスオブジェクトです。
the_twelite
に設定を反映するには <<
を用います。
TWENET::appid(APP_ID)
アプリケーションIDの指定
TWENET::channel(CHANNEL)
チャネルの指定
TWENET::rx_when_idle()
受信回路をオープンにする指定
<<, >>
演算子は本来ビットシフト演算子ですが、その意味合いと違った利用とはなります。MWXライブラリ内では、C++標準ライブラリでの入出力利用に倣ってライブラリ中では上記のような設定やシリアルポートの入出力で利用しています。
次にネットワークを登録します。
1行目は、ボードの登録と同じ書き方で <>
には <NWK_SIMPLE>
を指定します。
2行目は、<NWK_SIMPLE>
の設定で、0xFE
(ID未設定の子機)という指定を行います。
3行目は、中継回数の最大値を指定しています。この解説では中継には触れませんが、複数台で動作させたときにパケットの中継が行われます。
setup()
関数の末尾で the_twelite.begin()
を実行しています。
ADC(アナログディジタルコンバータ)を取り扱うクラスオブジェクトです。
初期化Analogue.setup()
で行います。パラメータのtrue
はADC回路の安定までその場で待つ指定です。
ADCを開始するにはAnalogue.begin()
を呼びます。パラメータはADC対象のピンに対応するビットマップです。
ビットマップを指定するのにpack_bits()
関数を用います。可変数引数の関数で、各引数には1を設定するビット位置を指定します。例えばpack_bits(1,3,5)
なら2進数で 101010
の値が戻ります。この関数はconstexpr
指定があるため、パラメータが定数のみであれば定数に展開されます。
パラメータにはPIN_ANALOGUE::A1
(ADC0)とPIN_ANALOGUE::VCC
(モジュール電源電圧)が指定されています。
2番目のパラメータには50
が指定されています。ADCの動作はデフォルトではTickTimerで開始されていて、
初回を除き ADC の開始は、割り込みハンドラ内で行います。
DIO (ディジタル入力) の値の変化を検出します。Buttonsでは、メカ式のボタンのチャタリング(摺動)の影響を軽減するため、一定回数同じ値が検出されてから、値の変化とします。
初期化は Buttons.setup()
で行います。パラメータの 5 は、値の確定に必要な検出回数ですが、設定可能な最大値を指定します。内部的にはこの数値をもとに内部メモリの確保を行っています。
開始は Buttons.begin()
で行います。1番目のパラメータは検出対象のDIOです。BRD_APPTWELITE::
に定義されるPIN_BTN
(12) を指定しています。2番めのパラメータは状態を確定するのに必要な検出回数です。3番めのパラメータは検出間隔です。10
を指定しているので10msごとに5回連続で同じ値が検出できた時点で、HIGH, LOWの状態が確定します。
ButtonsでのDIO状態の検出はイベントハンドラで行います。イベントハンドラは、割り込み発生後にアプリケーションループで呼ばれるため割り込みハンドラに比べ遅延が発生します。
Serial オブジェクトは、初期化や開始手続きなく利用できます。
シリアルポートへの文字列出力を行います。mwx::crlf
は改行文字です。
ループ関数は TWENET ライブラリのメインループからコールバック関数として呼び出されます。ここでは、利用するオブジェクトが available になるのを待って、その処理を行うのが基本的な記述です。ここではアクトで使用されているいくつかのオブジェクトの利用について解説します。
TWENET ライブラリのメインループは、事前にFIFOキューに格納された受信パケットや割り込み情報などをイベントとして処理し、そののちloop()
が呼び出されます。loop()
を抜けた後は CPU が DOZE モードに入り、低消費電流で新たな割り込みが発生するまでは待機します。
したがってCPUが常に稼働していることを前提としたコードはうまく動作しません。
Serial.available()
がtrue
の間はシリアルポートからの入力があります。内部のFIFOキューに格納されるためある程度の余裕はありますが、速やかに読み出すようにします。データの読み出しはSerial.read()
を呼びます。
ここでは't'
キーの入力に対応してvTransmit()
関数を呼び出しPINGパケットを送信します。
DIO(ディジタルIO)の入力変化を検出したタイミングで available になり、Buttons.read()
により読み出します。
1番目のパラメータは、現在のDIOのHIGH/LOWのビットマップで、bit0から順番にDIO0,1,2,.. と並びます。例えば DIO12 であれば btn_state & (1UL << 12)
を評価すれば HIGH / LOW が判定できます。ビットが1になっているものがHIGHになります。
初回のIO状態確定時は MSB (bit31) に1がセットされます。スリープ復帰時も初回の確定処理を行います。
初回確定以外の場合かつPIN_BTNのボタンが離されたタイミングでvTransmit()
を呼び出しています。押したタイミングにするには(!(btn_state && (1UL << PIN_BTN)))
のように条件を論理反転します。
Timer0
は32Hzで動作しています。タイマー割り込みが発生直後の loop()
で available になります。つまり、秒32回の処理をします。ここでは、ちょうど1秒になったところで送信処理をしています。
AppTweliteでは約1秒おきに定期送信を行っています。Timer0
がavailableになったときにu16ct
をインクリメントします。このカウンタ値をもとに、32回カウントが終わればtransmit()
を呼び出し無線パケットを送信しています。
u8DI_BM
とau16AI[]
の値判定は、初期化直後かどうかの判定です。まだDI1..DI4やAI1..AI4の値が格納されていない場合は何もしません。
受信パケットがある場合の処理です。
無線パケットを受信後のloop()
ではthe_twelite.receiver.available()
がtrue
を返します。受信パケットの取り扱いについては receive()
関数の解説で行います。
TWENETがパケットを受信してから時間をたってからの受信パケットの参照は安全ではありません。
内部のデータが新しい受信パケットの内容に上書きされ、この新しい内容を参照することになります。availableになってから速やかに処理するようにloop()
を記述してください。内部的には、1パケット分余分に保持できる余裕はあります。
まず受信パケットのデータrx
を取得します。rx
からアドレス情報やデータペイロードにアクセスします。
次の行では、受信パケットデータには、送信元のアドレス(32bitのロングアドレスと8bitの論理アドレス)などの情報を参照しています。
<NWK_SIMPLE>
では、8bitの論理IDと32bitのロングアドレスの2種類が常にやり取りされます。送り先を指定する場合はロングアドレスか論理アドレスのいずれかを指定します。受信時には両方のアドレスが含まれます。
MWXライブラリにはtransmit()
の時に使ったpack_bytes()
の対になる関数expand_bytes()
が用意されています。
1行目から3行目までは、データを格納する変数を指定しています。
6行目でexpand_bytes()
によりパケットのペイロードのデータを変数に格納します。1番目のパラメータでコンテナの先頭イテレータ(uint8_t*
ポインタ)を指定します。.begin()
メソッドにより取得できます。2番目のパラメータはコンテナの末尾の次を指すイテレータで.end()
メソッドで取得できます。2番目はコンテナの末尾を超えた読み出しを行わないようにするためです。
3番目以降のパラメータに変数を列挙します。列挙した順番にペイロードの読み出しとデータ格納が行われます。
このアクトでは、パケット長が間違っていた場合などのエラーチェックを省いています。チェックを厳格にしたい場合は、expand_bytes()
の戻り値により判定してください。
expand_bytes()
の戻り値は uint8_t*
ですが、末尾を超えたアクセスの場合はnullptr(ヌルポインタ)
を戻します。
msg
に読み出した4バイト文字列の識別子が"PING"
の場合はPONGメッセージを送信する処理です。
続いて到着したパケット情報を表示します。
数値のフォーマット出力が必要になるのでformat()
を用いています。>>
演算子向けにprintf()と同じ構文を利用できるようにしたヘルパークラスですが、引数の数が4つまでに制限されています。(Serial.printfmt()
には引数の数の制限がありません。)
mwx::crlfは改行文字(CR LF)を、mwx::flushは出力完了待ちを指定します。
無線パケットの送信要求をTWENETに行う関数です。本関数が終了した時点では、まだ無線パケットの処理は行われません。実際に送信が完了するのは、送信パラメータ次第ですが、数ms後以降になります。ここでは代表的な送信要求方法について解説します。
ネットワークオブジェクトをthe_twelite.network.use<NWK_SIMPLE>()
で取得します。そのオブジェクトを用いて.prepare_tx_packet()
によりpkt
オブジェクトを取得します。
ここではif文の条件判定式の中で宣言しています。宣言したpkt
オブジェクトはif節の終わりまで有効です。pktオブジェクトはbool型の応答をし、ここではTWENETの送信要求キューに空きがあって送信要求を受け付ける場合にtrue
、空きがない場合にfalse
となります。
パケットの設定はthe_twelite
の初期化設定のように<<
演算子を用いて行います。
tx_addr()
パラメータに送信先アドレスを指定します。0x00
なら自分が子機で親機宛に、0xFE
なら自分が親機で任意の子機宛のブロードキャストという意味です。
tx_retry()
パラメータに再送回数を指定します。例の3
は再送回数が3回、つまり合計4回パケットを送ります。無線パケット1回のみの送信では条件が良くても数%程度の失敗はあります。
tx_packet_delay()
送信遅延を設定します。一つ目のパラメータは、送信開始までの最低待ち時間、2番目が最長の待ち時間です。この場合は送信要求を発行後におよそ100msから200msの間で送信を開始します。3番目が再送間隔です。最初のパケットが送信されてから20ms置きに再送を行うという意味です。
ペイロードは積載物という意味ですが、無線パケットでは「送りたいデータ本体」という意味でよく使われます。無線パケットのデータにはデータ本体以外にもアドレス情報などいくつかの補助情報が含まれます。
送受信を正しく行うために、データペイロードのデータ並び順を意識するようにしてください。ここでは以下のようなデータ順とします。このデータ順に合わせてデータペイロードを構築します。
データペイロードには90バイト格納できます(実際にはあと数バイト格納できます)。
IEEE802.15.4の無線パケットの1バイトは貴重です。できるだけ節約して使用することを推奨します。1パケットで送信できるデータ量に限りがあります。パケットを分割する場合は分割パケットの送信失敗などを考慮する必要がありコストは大きくつきます。また1バイト余分に送信するのに、およそ16μ秒×送信時の電流に相当するエネルギーが消費され、特に電池駆動のアプリケーションには大きく影響します。
上記のデータペイロードのデータ構造を実際に構築してみます。データペイロードは pkt.get_payload()
により simplbuf<uint8_t>
型のコンテナとして参照できます。このコンテナに上記の仕様に基づいてデータを構築します。
上記のように記述できますがMWXライブラリでは、データペイロード構築のための補助関数pack_bytes()
を用意しています。
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,5,6行目は、数値型の値 (uint8_t
, uint16_t
, uint32_t
)を格納します。符号付などの数値型、char
型など同じ数値型であっても左記の3つの型にキャストして投入します。
analogRead()
とanalogRead_mv()
は、ADCの結果を取得するものです。前者はADC値(0..1023)、後者は電圧[mv](0..2470)となります。モジュールの電源電圧は内部的に分圧抵抗の値を読んでいるためその変換を行うadalogRead_mv()
を利用しています。
これでパケットの準備は終わりです。あとは、送信要求を行います。
パケットを送信するにはpkt
オブジェクトのpkt.transmit()
メソッドを用います。
このアクトでは使用しませんが、戻り値には、要求の成功失敗の情報と要求に対応する番号が格納されています。送信完了まで待つ処理を行う場合は、この戻り値の値を利用します。
の記述サンプルです。詳細はを参照ください。
のサンプルを少し改良して、センサーデータ取得中の待ち時間(約50ms)を、スリープで待つようにします。
このアクトの解説の前にのアクトの解説をご覧ください。
begin()
関数はsetup()
関数を終了し(そのあとTWENETの初期化が行われる)一番最初のloop()
の直前で呼ばれます。
setup()
終了後に初回スリープを実行します。setup()
中にセンサーデータ取得を開始していますが、この結果は評価せず、センサーを事前に一度は動かしておくという意味あいで、必ずしも必要な手続きではありません。
起床後の手続きです。以下の処理を行います。
まだセンサーデータの取得開始をしていない場合、センサーデータ取得を行い、短いスリープに入る。
直前にセンサーデータ取得開始を行ったので、データを確認して無線送信する。
上記の分岐をグローバル変数のb_sensor_started
により制御しています。!b_sensor_started
の場合はセンサー取得開始(startSensorCapture()
)を行い、napNow()
により短いスリープに入ります。時間は100msです。
napNow()
によるスリープ復帰後、b_sensor_started==true
の節が実行されます。ここでは、2つのセンサーに対してE_EVENT_START_UP
イベントを通知しています。このイベントは、センサーの取得が終了するのに十分な時間が経過したことを意味します。この通知をもとにsns_LTR308ALS
とsns_SHTC3
はavailableになります。この後loop()
に移行し、無線パケットが送信されます。
センサーに通知するイベントは必要な時間待ちが終わったかどうかを判定するために使われます。実際時間が経過しているかどうかはnapNow()
で正しい時間を設定したかどうかで決まります。短い時間で起床した場合は、必要とされる時間経過に足りないため、続く処理でセンサーデータが得られないなどのエラーが出ることが想定されます。
ごく短いスリープを実行する。
sleepのパラメータの2番目をtrueにすると前回のスリープ復帰時刻をもとに次の復帰時間を調整します。常に5秒おきに起床したいような場合設定します。
3番目をtrueにするとメモリーを保持しないスリープになります。復帰後はwakup()は呼び出されじ、電源再投入と同じ処理になります。
4番目はウェイクアップタイマーの2番目を使う指定です。ここでは1番目は通常のスリープに使用して、2番目を短いスリープに用いています。このアクトでは2番目を使う強い理由はありませんが、例えば上述の5秒おきに起床したいような場合、短いスリープに1番目のタイマーを用いてしまうとカウンター値がリセットされてしまい、経過時間の補正計算が煩雑になるため2番目のタイマーを使用します。
あまり短いスリープ時間を設定してもスリープ復帰後のシステムの再初期化などのエネルギーコストと釣り合いません。目安として最小時間を30-50ms程度とお考え下さい。
を用い、センサー値の取得を行います。
このアクトの解説の前にをご覧ください。
受信の確認のための解説をご覧ください。
開閉センサーパル OPEN-CLOSE SENSE PAL を用い、磁気センサーの検出時に割り込み起床し、無線送信します。
コイン電池で動作させるための、スリープ機能を利用します。
最初にボードビヘイビア<PAL_MAG>
を登録します。ボードビヘイビアの初期化時にセンサーやDIOの初期化が行われます。最初に行うのは、ボードのDIP SWなどの状態を確認してから、ネットワークの設定などを行うといった処理が一般的だからです。
ここでは、ボード上の4ビットDIP SWのうち3ビットを読み出して子機のIDとして設定しています。0の場合は、ID無しの子機(0xFE
)とします。
LEDの設定を行います。ここでは 10ms おきに ON/OFF の点滅の設定をします(スリープを行い起床時間が短いアプリケーションでは、起床中は点灯するという設定とほぼ同じ意味合いになります)。
begin()
関数はsetup()
関数を終了し(そのあとTWENETの初期化が行われる)一番最初のloop()
の直前で呼ばれます。
setup()
終了後にsleepNow()
を呼び出し初回スリープを実行します。
スリープに入るまえに磁気センサーのDIOピンの割り込み設定をします。pinMode()
を用います。2番めのパラメータはPIN_MODE::WAKE_FALLING
を指定しています。これはHIGHからLOWへピンの状態が変化したときに起床する設定です。
7行目でthe_twelite.sleep()
でスリープを実行します。パラメータの60000は、TWELITE PAL ボードのウォッチドッグをリセットするために必要な起床設定です。リセットしないと60秒経過後にハードリセットがかかります。
スリープから復帰し起床すると wakeup()
が呼び出されます。そのあとloop()
が都度呼び出されます。wakeup()
の前に、UARTなどの各ペリフェラルやボード上のデバイスのウェイクアップ処理(ウォッチドッグタイマーのリセットなど)が行われます。例えばLEDの点灯制御を再始動します。
ここではウェイクアップタイマーからの起床の場合(the_twelite.is_wokeup_by_wktimer()
)は再びスリープを実行します。これは上述のウォッチドッグタイマーのリセットを行う目的のみの起床です。
磁気センサーの検出時の起床の場合は、このままloop()処理に移行します。
ここでは、検出された磁気センサーのDIOの確認を行い、パケットの送信を行い、パケット送信完了後に再びスリープを実行します。
b_transmit
変数によってloop()
内の振る舞いを制御しています。送信要求が成功した後、この値を1にセットしパケット送信完了待ちを行います。
磁気センサーの検出DIOピンの確認を行います。検出ピンは二種類あります。N極検知とS極検知です。単に磁石が近づいたことだけを知りたいならいずれかのピンの検出されたことが条件となります。
起床要因のピンを確認するにはthe_twelite.is_wokeup_by_dio()
を用います。パラメータはピン番号です。戻り値をuint8_tに格納しているのはパケットのペイロードに格納するためです。
通信条件の設定やペイロードにデータを格納後、送信を行います。
その後、loop()
中 b_transmit
が true
になっている場合は、完了チェックを行い、完了すれば sleepNow()
によりスリープします。
送信完了に確認は the_twelite.tx_status.is_complete(u8txid)
で行っています。u8txid
は送信時に戻り値として戻されたID値です。
を用い、センサー値の取得を行います。
このアクトの解説の前にをご覧ください。
受信の確認のための解説をご覧ください。
環境センサーパル AMPIENT SENSE PAL を用い、センサー値の取得を行います。
コイン電池で動作させるための、スリープ機能を利用します。
ここでは、ボード上の4ビットDIP SWのうち3ビットを読み出して子機のIDとして設定しています。0の場合は、ID無しの子機(0xFE
)とします。
LEDの設定を行います。ここでは 10ms おきに ON/OFF の点滅の設定をします(スリープを行い起床時間が短いアプリケーションでは、起床中は点灯するという設定とほぼ同じ意味合いになります)。
このアクトではもっぱら無線パケットを送信しますので、TWENET の設定では動作中に受信回路をオープンにする指定(TWENET::rx_when_idle()
)は含めません。
ボード上のセンサーはI2Cバスを用いますので、バスを利用開始しておきます。
このアクトでは、1ms周期で呼び出させる(アプリケーションループの呼び出しタイミングは不定期な遅延が発生します)TickTimerのイベントを起点としてセンサーの読み出しや送信要求を行います。TickTimer.available()
がtrue
になったときに処理を行います。これによりおよそ1msごとにif節内の処理を行います。
ボード上のセンサーは .sns_LTR308ALS
または .sns_SHTC3
という名前でアクセスでき、このオブジェクトに操作を行います。センサーの完了待ちを行います。まだセンサーの取得が終わっていない場合(.available()
がfalse
)はセンサーに対して時間経過のイベント(.process_ev(E_EVENT_TICK_TIMER)
)を送付します。
上記2つのセンサーがavailableになった時点で、無線送信などの処理を行います。
送信手続きについては他のアクトのサンプルと同様です。ここでは、再送1回、再送遅延を最小にする設定になっています。
パケットにセンサーデータを書き込み際に、センサーデータを取得しています。センサーの値を取得するメソッドはセンサーごとに違います。
照度センサーは.get_luminance() : uint32_t
で得られます。
温湿度センサーは以下のように取得できます。
.get_temp_cent()
: int16_t
: 1℃を100とした温度 (25.6 ℃なら 2560)
.get_temp()
: float
: float値 (25.6 ℃なら 25.6)
.get_humid_dmil()
: int16_t
: 1%を100とした湿度 (56.8%なら 5680)
.get_temp()
: float
: float値 (56.8%なら 56.8)
得られた値のうち温度値は int16_t
ですが、送信パケットのデータ構造は符号なしで格納するため、uint16_t
にキャストしています。
送信が成功すれば b_tansmit
を true
にし、u8txid
に送信パケットの識別IDを格納し、完了待ちします。送信が失敗すれば sleepNow()
によりスリープします。
loop()
中 b_transmit
が true
になっている場合は、送信完了チェックを行い、完了すれば sleepNow()
によりスリープします。
送信完了に確認は the_twelite.tx_status.is_complete(u8txid)
で行っています。u8txid
は送信時に戻り値として戻されたID値です。
ボード上のセンサーの取得を開始します。多くのセンサーは、取得を開始してから完了するまで数msから数十msの時間を必要とします。
5行目で、温湿度センサー SHTC3 のデータ取得を開始します。
6行目で、照度センサー LTR308ALS のデータ取得を開始します。
7行目は、送信完了フラグをクリアします。
スリープに入る手続きをまとめています。
ここでは、起床までの時間を乱数により 1750ms から 2250ms の間に設定しています。これにより他の同じような周期で送信するデバイスのパケットとの連続的な衝突を避けます。
周期が完全に一致すると、互いのパケットで衝突が起き通信が困難になります。通常は時間の経過とともにタイマー周期が互いにずれるため、しばらくすると通信が回復し、また時間がたつと衝突が起きるという繰り返しになります。
3行目、この例ではシリアルポートからの出力を待ってスリープに入ります。通常は消費エネルギーを最小化したいため、スリープ前のシリアルポートの出力は最小限(または無し)にします。
スリープ前にflushを行うと、出力が不安定になる場合があります。
スリープに入るには the_twelite.sleep()
を呼びます。この呼び出しの中で、ボード上のハードウェアのスリープ前の手続きなどが行われます。たとえばLEDは消灯します。
パラメータとしてスリープ時間をmsで指定しています。
TWELITE PAL では、必ず60秒以内に一度起床し、ウォッチドッグタイマーをリセットしなければなりません。スリープ時間は60000
を超えないように指定してください。
スリープから復帰し起床すると wakeup()
が呼び出されます。そのあとloop()
が都度呼び出されます。wakeup()
の前に、UARTなどの各ペリフェラルやボード上のデバイスのウェイクアップ処理が行われます。例えばLEDの点灯制御を再始動します。
ここではセンサーのデータ取得を開始しています。
より安全な実装として、このアクトでは以下の例外処理を強化します。
センサー取得部分でavailableにならなかった場合の例外処理が記述されてません。
送信完了待ちで送信完了が通知されない場合の例外処理が記述されていません。
上記いずれもタイムアウト処理を行います。現在の時間は millis()
により取得できます。
アクトでは連続的に加速度データを取得して都度無線送信していました。このアクトではスリープ復帰後に数サンプル加速度データを取得しそのデータを送ります。
このアクトの解説の前にのアクトの解説をご覧ください。
本サンプルは、収録バージョンによって差が大きいため本ページでは2つの解説を記載します。
※ 最新のコードは「を参照ください。
起床→加速度センサーの取得開始→加速度センサーのFIFO割り込み待ち→加速度センサーのデータの取り出し→無線送信→スリープという
起床→加速度センサーの取得開始→加速度センサーのFIFO割り込み待ち→加速度センサーのデータの取り出し→無線送信→スリープという流れになります。
加速度センサーは、FIFOキューが一杯になるとFIFOキューへのデータ追加を停止します。
列挙体として eState
変数を宣言しています。
setup()
を終了した後に呼ばれます。ここでは初回スリープを実行しています。
起床後は状態変数eState
を初期状態INITにセットしています。この後loop()
が実行されます。
loop()
の基本構造は状態変数eState
によるswitch ... case節です。eStateの初期状態はINITです。loop_more
は状態変数を書き換えた直後、loop()
を抜ける前にもう一度実行したいときにtrue
にセットします。
以下では各case節を解説します。eState
の初期状態はINITです。
状態INITでは、初期化(結果格納用のキューのクリア)を行います。
状態START_CAPTUREでは、MC3630センサーのFIFO取得を開始します。ここでは400Hzで4サンプル取得できた時点でFIFO割り込みが発生する設定にしています。タイムアウトのチェックのため、開始時点のシステム時刻をu32tick_capture
に格納します。
状態WAIT_CAPTUREでは、FIFO割り込みを待ちます。割り込みが発生し結果格納用のキューにデータが格納されるとsns_MC3630.available()
がtrue
になります。
タイムアウトした場合は状態EXIT_FATALに遷移します。
状態REQUEST_TXではローカル定義関数TxReq()を呼び出し、得られたセンサーデータの処理と送信パケットの生成、そうし尿級を行います。タイムアウトのチェックのため、開始時点のシステム時刻をu32tick_tx
に格納します。
状態WAIT_TXでは、無線パケットの送信完了を待ちます。
タイムアウト時には状態EXIT_FATALに遷移します。
一連の動作が完了したときは状態EXIT_NORMALに遷移しローカル定義の関数sleepNow()
を呼び出しスリープを実行します。またエラーを検出した場合は状態EXIT_FATALに遷移し、システムリセットを行います。
この関数では、センサーより得られたサンプル値の取得と、複数サンプルの平均値の計算、
取得サンプルの平均値を計算します。
ここでは除算を行っていますが、TWELITEマイコンには除算回路がないため、計算時間を要する演算となります。例えば以下のような改良が考えられます。
サンプル数を2のべき乗として、その数を変数に入れず直接指定した除算を行う(ビットシフトによる演算に最適化されます)。
平均値を求めず、合計値とサンプル数を送り、受信先で計算する。
X軸の最大値と最小値を計算します。
ここではイテレータとstd::minmax_element()
アルゴリズムを用いて計算します。get_axis_x_iter
はキューのイテレータをパラメータとして、axis_xyzt
構造体の.x
にアクセスするものです。
C++ Standard Template Library のアルゴリズムを使用する例としてstd::mimmax_element
紹介していますが、上述のforループ内で最大、最小を求めても構いません。
ここでキューをクリア.sns_MC3630.get_que().clear()
しています。
最期にパケットの生成と送信を要求を行います。パケットには X, Y, Z 軸の加速度、X軸の最小値,Y軸の最小値を含めています。
MWSDK2020_05 版の SDK 添付のサンプルコードです。
起床→加速度センサーの取得開始→加速度センサーのFIFO割り込み待ち→加速度センサーのデータの取り出し→無線送信→スリープという流れになります。
加速度センサーは、FIFOキューが一杯になるとFIFOキューへのデータ追加を停止します。
起床後加速度センサーを稼働させます。
加速度センサーの結果を保存するキューの内容を抹消(.sns_MC3630.get_que().clear()
)しておきます。加速度センサーのサンプルの取得忘れがあったり、また停止させるまでに次のサンプルが取得したような場合を想定します。
ここで加速度センサーを都度開始します。設定は400Hz,±4G,FIFO割り込みは4サンプルとしています。4サンプルも必要ない場合は1サンプルの設定でも構いません。
このアクトでは、サンプル取得後、すぐに加速度センサーの動作を停止します。
取得サンプルの平均値を計算します。
X軸の最大値と最小値を計算します。
ここではイテレータとstd::minmax_element()
アルゴリズムを用いて計算します。get_axis_x_iter
はキューのイテレータをパラメータとして、axis_xyzt
構造体の.x
にアクセスするものです。
最後にキューをクリア.sns_MC3630.get_que().clear()
しています。
を用い、センサー値の取得を行います。
このアクトの解説の前にをご覧ください。
受信の確認のための解説をご覧ください。
動作センサーパル MOTION SENSE PAL を用い、加速度センサーの加速度を連続的に計測し、無線送信します。
コイン電池で動作させるための、スリープ機能を利用します。
最初にボードビヘイビア<PAL_MOT>
を登録します。ボードビヘイビアの初期化時にセンサーやDIOの初期化が行われます。最初に行うのは、ボードのDIP SWなどの状態を確認してから、ネットワークの設定などを行うといった処理が一般的だからです。
ここでは、ボード上の4ビットDIP SWのうち3ビットを読み出して子機のIDとして設定しています。0の場合は、ID無しの子機(0xFE
)とします。
LEDの設定を行います。ここでは 10ms おきに ON/OFF の点滅の設定をします(スリープを行い起床時間が短いアプリケーションでは、起床中は点灯するという設定とほぼ同じ意味合いになります)。
加速度センサーの計測を開始します。加速度センサーの設定(SnsMC3630::Settings
)には計測周波数と測定レンジを指定します。ここでは14HZの計測(SnsMC3630::MODE_LP_14HZ
)で、±4Gのレンジ(SnsMC3630::RANGE_PLUS_MINUS_4G
)で計測します。
開始後は加速度センサーの計測が秒14回行われ、その値はセンサー内部のFIFOキューに保存されます。センサーに28回分の計測が終わった時点で通知されます。
begin()
関数はsetup()
関数を終了し(そのあとTWENETの初期化が行われる)一番最初のloop()
の直前で呼ばれます。
setup()
終了後にsleepNow()
を呼び出し初回スリープを実行します。
スリープに入るまえに加速度センサーのDIOピンの割り込み設定をします。FIFOキューが一定数まで到達したときに発生する割り込みです。pinMode()
を用います。2番めのパラメータはPIN_MODE::WAKE_FALLING
を指定しています。これはHIGHからLOWへピンの状態が変化したときに起床する設定です。
3行目でthe_twelite.sleep()
でスリープを実行します。パラメータの60000は、TWELITE PAL ボードのウォッチドッグをリセットするために必要な起床設定です。リセットしないと60秒経過後にハードリセットがかかります。
加速度センサーのFIFO割り込みにより、スリープから復帰し起床すると wakeup()
が呼び出されます。そのあとloop()
が都度呼び出されます。wakeup()
の前に、UARTなどの各ペリフェラルやボード上のデバイスのウェイクアップ処理(ウォッチドッグタイマーのリセットなど)が行われます。例えばLEDの点灯制御を再始動します。
ここではloop()
で使用する変数の初期化を行っています。
ここでは、加速度センサー内のFIFOキューに格納された加速度情報を取り出し、これをもとにパケット送信を行います。パケット送信完了後に再びスリープを実行します。
b_transmit
変数によってloop()
内の振る舞いを制御しています。送信要求が成功した後、この値を1にセットしパケット送信完了待ちを行います。
最初にセンサーがavailableかどうかを確認します。割り込み起床後であるため、availableでないのは通常ではなく、そのままスリープします。
無線送信パケットでは使用しないのですが、取り出した加速度の情報を確認してみます。
加速度センサーの計測結果はbrd.sns_MC3630.get_que()
で得られるFIFOキューに格納されます。
加速度センサーの計測結果を格納している構造体 axis_xyzt
は x, y, z の三軸の情報に加え、続き番号 t が格納されています。
格納されているサンプル数はキューのサイズ(brd.sns_MC3630.get_que().size()
)を読み出すことで確認できます。通常は28サンプルですが処理の遅延等によりもう少し進む場合もあります。最初のサンプルはfront()
で取得することができます。その続き番号はfront().t
になります。
ここでは、サンプルをキューから取り出す前にサンプルの平均をとってみます。キューの各要素にはfor文(for (auto&& v: brd.sns_MC3630.get_que()) { ... }
) でアクセスできます。for文内の v.x, v.y, v.z
が各要素になります。ここでは各要素の合計を計算しています。for文終了後は要素数で割ることで平均を計算しています。
次にパケットを生成して送信要求を行いますが、データ量が大きいため2回に分けて送信します。そのため送信処理がfor文で2回行われます。
送信するパケットに含めるサンプル数とサンプル最初の続き番号をパケットのペイロードの先頭部分に格納します。
最後に加速度データを格納します。先程は平均値の計算のためにキューの各要素を参照のみしましたが、ここではキューから1サンプルずつ読み出してパケットのペイロードに格納します。
加速度センサーからのデータキューの先頭を読み出すのは.front()
を用います。読みだした後.pop()
を用いて先頭キューを開放します。
加速度センサーから取得されるデータは1Gを1000としたミリGの単位です。レンジを±4Gとしているため、12bitの範囲に入るように2で割って格納します。データ数を節約するため最初の4バイトにX,Y軸とZ軸の上位8bitを格納し、次の1バイトにZ軸の下位4bitの合計5バイトを生成します。
2回分の送信待ちを行うため送信IDはtxid[]
配列に格納します。
その後、loop()
中 b_transmit
が true
になっている場合は、完了チェックを行い、完了すれば sleepNow()
によりスリープします。
送信完了に確認は the_twelite.tx_status.is_complete()
で行っています。txid[]
は送信時に戻り値として戻されたID値です。
MONOSTICK用のボードビヘイビアをインクルードしています。このボードサポートには、LEDの制御、ウォッチドッグ対応が含まれます。
開閉センサーパルのボード ビヘイビアをインクルードします。
環境センサーパル のボードビヘイビアをインクルードします。
最初にボードサポート を登録します。ボードサポートの初期化時にセンサーやDIOの初期化が行われます。最初に行うのは、ボードのDIP SWなどの状態を確認してから、ネットワークの設定などを行うといった処理が一般的だからです。
ボード上のセンサーの取得を開始します。の解説を参照ください。
アクト は、センサーのデータ取得待ちをスリープで行い、より低消費エネルギーで動作できます。
※ 最新のコードは「を参照ください。
動作センサーパルのボードビヘイビアをインクルードします。
役割
例
親機
子機
サンプルアクトの子機設定 (例: BLUE PAL または RED PAL +AMBIENT SENSE PALにPAL_AMBアクトを書き込んだもの)
役割
例
親機
アクトParent_MONOSTICKを動作させる。
子機
役割
例
親機
アクトParent_MONOSTICKを動作させる。
子機
役割
例
親機
アクトParent_MONOSTICKを動作させる。
子機
クラスオブジェクトは、MWXライブラリであらかじめ定義されたオブジェクトで、TWENETを取り扱うthe_twelite
、ペリフェラルの利用のためのオブジェクトが定義されています。
各オブジェクトは.setup()
, .begin()
メソッドの呼び出しを行って初期化する必要があります。(UART0を利用するSerial
オブジェクトのみ初期化は必要ありません)
WirelessUARTはシリアル通信を行います。
このアクトの解説の前にBRD_APPTWELITEの解説をご覧ください。
2台のUART接続のTWELITE同士をアスキー書式で通信する。
いずれかを2台。
TWELITE R でUART接続されているTWELITE DIPなど
論理IDはrandom(1,5)
により1,2,3,4のいずれかの値に割り当てています。通常は、論理IDを設定保存したデータ、またはDIP SWといったハードウェアの情報から生成します。
シリアルパーサーを初期化します。
シリアルからのデータ入力があった時点で、シリアルパーサーに1バイト入力します。アスキー形式が最後まで受け付けられた時点でSerialParser.parse()
はtrue
を戻します。
SerialParser
は内部バッファに対してsmplbuf
でアクセスできます。上の例ではバッファの1バイト目を送信先のアドレスとして取り出し、2バイト目から末尾までをtransmit()
関数に渡します。
パケットを受信したときには、送信元を先頭バイトにし続くペイロードを格納したバッファsmplbuf_u8<128> buf
を生成し、出力用のシリアルパーサーserparser_attach pout
からシリアルに出力しています。
テストデータは必ずペースト機能を用いてターミナルに入力してください。入力にはタイムアウトがあるためです。
参考: TWE ProgrammerやTeraTermでのペーストはAlt+Vを用います。
入力の末尾にCR LFが必要です。
最初はCR LFが省略できるXで終わる系列を試してください。終端文字列が入力されない場合は、その系列は無視されます。
任意の子機宛に00112233
を送付します。
子機3番に対してAABBCC00112233
を送付します。
TWE Programmerのターミナル機能を用いて送付する場合は、Alt+Vキーを用いてペーストします。
TWENET 利用の中核クラス (mwx::twenet)
the_twelite
オブジェクトは、TWENETの利用手続きをまとめたもので、無線の基本設定やスリープ等の手続きなど無線マイコンを操作するための手続きが含まれます。
the_twelite
はsetup()
関数内で設定と開始the_twelite.begin()
を行います。setup()
以外では設定は行えません。
上記の例では、アプリケーションIDの設定、通信チャネルの設定、受信回路の設定を行っています。
様々な手続きが含まれます。
また無線ネットワークを取り扱うクラスやボード対応をまとめたクラス、ユーザ記述のイベントドリブン処理を行うクラスを登録できるようになっています。このクラスを登録することにより、専用化した機能を手軽に利用できるようになります。これらのクラスを本解説中では「ビヘイビア」と呼称します。
上記の例では環境センサーパル<PAL_AMB>
と、シンプル中継ネットワーク<NWK_SIMPLE>
の2種類を登録しています。これらを登録することにより環境センサーパル上のセンサーなどハードウェアを簡易に取り扱うことが出来ます。また煩雑な無線パケットの取り扱いについて中継の処理や重複で届いたパケットの自動破棄などの機能を暗黙に持たせることが出来ます。
MWXライブラリには、ここで紹介したメソッド以外にも定義されています。
アクト記述には直接関係ないもの、設定しても有効に機能しないもの、内部的に使用されているものが含まれます。
<<
演算子 (設定)オブジェクトthe_twelite
の初期設定を行うために<<
演算子を用います。
以下に挙げる設定用のクラスオブジェクトを入力とし、設定をしなければデフォルト値が適用されます。
パラメータid
に指定したアプリケーションIDを設定します。これは必須指定です。
設定の読み出しは uint32_t the_twelite.get_appid()
で行います。
パラメータch
に指定したチャネル番号(11
..26
)を設定します。
設定の読み出しはuint8_t the_twelite.get_channel()
で行います。
パラメータpw
に指定した出力設定を(0
..3
)を設定します。デフォルトは(3:出力減衰無し)です。
設定値の読み出しはuint8_t the_twelite.get_tx_power()
で行います。
パラメータbEnable
が1
であれば常に受信回路を動作させ、他からの無線パケットを受信できるようになります。デフォルトは0
で、もっぱら送信専用となります。
設定値の読み出しはuint8_t the_twelite.get_rx_when_idle()
で行います。
MWXライブラリコード中には他にも設定項目がありますが、現時点ではライブラリの機能に無関係な設定であったり、設定を行うと矛盾を起こす可能性があるものです。
事前に設定(<<
演算子参照)や、ビヘイビアの登録を済ませた後に実行します。通常はsetup()
関数内の一番最後に記述します。
the_twelite
設定完了
ビヘイビアの初期化
TWENETの初期化は setup()
関数が終了した後にも実行されます。多くの処理はTWENETが終了した後に実行するようになっているため、ここでは初期化以外の処理を行わないようにしてください。
チャネル設定を変更します。失敗時にはチャネルは変更されずfalse
を戻します。
現在設定中のチャネル番号(11..26)を取得する。MAC層のAPIより取得します。
モジュールのシリアル番号を取得します。
モジュールをスリープさせる。
パラメータ
解説
u32Periodms
スリープ時間[ms]
bPeriodic
前回の起床時間をもとに次の起床時間を再計算する。 ※次の起床タイミングが迫っているなどの理由で、現在のタイミングからになる場合もあります。
bRamoff
true
に設定すると、RAMを保持しないスリープになる(起床後はwakeup()
ではなくsetup()
から再初期化する必要がある)
u8Device
スリープに用いるウェイクアップタイマーの指定。
TWENET::SLEEP_WAKETIMER_PRIMARY
または TWENET::SLEEP_WAKETIMER_SECONDARY
を指定する。
スリープ前に組み込みオブジェクトやビヘイビアの on_sleep()
メソッドが呼び出され、スリープ前の手続きを行います。スリープ復帰後は反対に on_wakeup()
メソッドにより復帰処理が行われます。
スリープからの復帰要因が指定したディジタルピンである場合にtrue
を返します。
スリープからの復帰要因がウェイクアップタイマーである場合にtrue
を返します。
twe_twelite
には3つのビヘイビアを登録でき、これらを格納する以下のクラスオブジェクトを定義されています。
network
: ネットワークを実装するビヘイビアです。通常は<NWK_SIMPLE>
を登録します。
board
: ボード対応のビヘイビアです。ボード上の各デバイス利用手続きが付加されます。
app
: ユーザアプリケーションを記述したビヘイビアです。割り込みやイベント記述、ステートマシンによる状態遷移によるふるまいの記述が可能です。また複数のアプリケーション記述を定義しておいて、起動時に全く振る舞いの違うアプリケーションを選択する記述が容易に行えます。
ビヘイビア<B>を登録します。登録はsetup()
内で行います。戻り値は登録したビヘイビアに対応するオブジェクトの参照です。
登録後は登録時と同じ書式でオブジェクトの取得を行います。
誤ったビヘイビアを指定した場合は、パニック動作(無限ループ)となりプログラムの動作が停止します。
グローバル変数としてビヘイビアのオブジェクトを宣言することを想定していません。利用都度use<B>()
を用いてください。
ただし、グローバル変数にオブジェクトのポインタを定義して以下のように記述することは可能です。(MWXライブラリでは原則としてポインタ型の利用を最小限にとどめ参照型を利用する方針ですので、下記のような記述は推奨しません)
the_twelite
には上述のboard
, network
, app
の3つのクラスオブジェクトが定義されていますが他に以下が定義されています。
送信完了状態を通知する。
イベントドリブンのビヘイビアの記述ではtransmit_complete()コールバックで管理します。
指定したIDのパケットが送信完了したときにtrue
を返す。
指定したIDのパケットが送信完了し、かつ送信成功したときにtrue
を返す。
受信パケットを取得する。
イベントドリブンのビヘイビアの記述ではreceive()コールバックで取得します。
read()
メソッドで得られる受信パケットデータは、続くパケットが受信処理時に上書きされる設計となっています。available
直後に読み出してなにか短い処理をする場合は問題になることはありませんが、原則として読み出し→アプリケーションが使うため必要なデータのコピー→loop()
の終了を速やかに行います。例えばloop()
中で長いdelay()
を行うと受信パケットの取りこぼしなどが発生します。
まだ読み出していない受信パケットが存在する場合にtrue
を返す。
パケットを読み出します。
Act/behavior Programming Interface
MWXライブラリのAPIは、今後、改善を目的として仕様の変更を行う場合があります。
Slp_Wk_and_Tx は、定期起床後、何か実行(センサーデータの取得など)を行って、その結果を無線パケットとして送信するようなアプリケーションを想定した、テンプレートソースコードです。
setup(), loop()
の形式では、どうしても loop()
中が判読しづらい条件分岐が発生しがちです。本Actでは、loop()
中を switch構文による単純な状態遷移を用いることで、コードの見通しを良くしています。
このアクトの解説の前にBRD_APPTWELITEの解説をご覧いただくことを推奨します。
TWELITE STAGE 2020_05 には収録されていません。以下のリンク(GitHub)より入手ください。
起動後、速やかにスリープする
setup()
: 初期化する
begin()
: スリープに遷移する
スリープ起床後、状態変数を初期化し、以下の順に動作を行う
wakeup()
: スリープからの起床、各初期化を行う
loop()
状態INIT->WORK_JOBに遷移: 何らかの処理を行う(このActでは 1ms ごとの TickCount ごとにカウンタを更新し 100 カウント後にTX状態に進む)
loop()
状態TX: 送信要求を行う
loop()
状態WAIT_TX: 送信完了待ちを行う
loop()
状態EXIT_NORMAL: スリープする (1. に戻る)
loop()
状態EXIT_FATAL: エラーが発生した場合は、モジュールリセットする
パケット送信を行うため <NWK_SIMPLE>
をインクルードしています。また、アプリケーションIDなど基本的な定義は "Common.h"
に記述しています。
Common.h
には基本的な定義に加え、以下の列挙体が定義されています。こちらを状態変数として利用します。
the_twelite
クラスオブジェクトの初期化とネットワーク <NWK_SIMPLE>
の登録を行います。
setup()
の直後に一度だけ呼び出されます。SleepNow()
関数夜を呼び出して初回のスリープ手続きを行います。
起床直後に呼び出されます。ここでは状態変数eState
を初期状態E_STATE::INIT
にセットします。この後loop()
が呼び出されます。
上記のコードは、実際のコードを簡略化したものです。
このコードではloop_more
を条件としてdo..while()
構文のループになっています。これはloop()
を脱出せずに次の状態のコード(case節)を実行する目的です。
wakeup()
でINIT状態にセットされていますので初回のloop()
ではINITが評価されます。ここは変数の初期化しているだけです。続く状態の処理を行うのにloop()
を脱出する必要がないためloop_more
をtrue
にしています。
WORK_JOB状態ではTichTimer.available()
になるたびにカウンタを減算し0になったら次の状態TXに遷移します。
TX状態ではvTransmit()
関数を呼び出しパケット送信要求を行います。この時点ではまだパケットが送信されていないため、この時点でスリープをしてはいけません。パケット送信要求が成功したらWAIT_TX状態に遷移します。タイムアウトの管理のため、この時点でのシステム時間u32millis_tx
を保存します。失敗したらEXIT_FATALに遷移します(モジュールのリセット実行)。
WAIT_TX状態では送信IDに対応する送信が完了したかを判定し、完了したらEXIT_NORMAL、100ms以上経過したらEXIT_FATAL状態に遷移します。
EXIT_NORMAL状態はSleepNow()
を呼び出し、スリープします。
EXIT_FATAL状態ではシステムリセットを行います。
周期スリープを行います。スリープ時間はrandom()
関数を用いて、一定の時間ブレを作っています。これは複数のデバイスの送信周期が同期した場合、著しく失敗率が上がる場合があるためです。
ID=0x00
の親機宛に無線パケットの送信要求を行います。格納されるデータはActサンプルで共通に使われている4文字識別子(FOURCC
)に加え、システム時間[ms]が含まれます。
MWX_APIRET
はuint32_t
型をラップしたクラスで、MSBを失敗成功のフラグとし、以下31ビットをデータとして用いています。pkt.transmit()
の戻り型になっており、送信要求の成功と失敗ならびに送信IDをデータ部に格納します。
パルスカウンターを用いたアクト例です。
パルスカウンターは、マイコンを介在せず信号の立ち上がりまたは立ち下りの回数を計数するものです。不定期のパルスを計数し一定回数までカウントが進んだ時点で無線パケットで回数を送信するといった使用方法が考えられます。
このアクトの解説の前にBRD_APPTWELITEの解説をご覧ください。
受信の確認のためParent_MONOSTICKの解説をご覧ください。
子機側のDIO8に接続したパルスを計数し、一定時間経過後または一定数のカウントを検出した時点で無線送信する。
子機側はスリープしながら動作する。
役割
例
親機
子機
パルスカウンターの初期化を行います。
パルスカウンターの動作を開始し、初回スリープを実行します。PulseCounter.begin()
の最初のパラメータは、起床割り込みを発生させるためのカウント数100
で、2番目は立ち下がり検出PIN_INT_MODE::FALLING
を指定しています。
起床時にPulseCounter.available()
を確認しています。availableつまりtrue
になっていると、指定したカウント数以上のカウントになっていることを示します。ここではfalse
の場合再スリープしています。
カウント数が指定以上の場合はloop()
で送信処理と送信完了待ちを行います。
パルスカウント値の読み出しを行います。読み出した後カウンタはリセットされます。
TWELITE の UART0 ポート (mwx::serial_jen)
を実装し TWELITE の UART0 で入出力する。
Serial
オブジェクトはシステム起動時に UART0, 115200 bps で初期化され、ライブラリ内で初期化処理が行われます。ユーザコード上は、setup()
から利用できます。
Serial1
オブジェクトは、ライブラリ内で用意されていますが、初期化処理は行っていません。UART1を有効化するためには、必要な初期化手続き Serial1.setup(), Serial1.begin()
を行ってください。
起動直後の setup(), wakeup()
やスリープ直前の flush
処理で、出力が不安定になる場合があります。
オブジェクトの初期化を行う。
TX/RX用のFIFOバッファのメモリ確保
TWE_tsFILE 構造体のメモリ確保
Serial
(UART0) は ライブラリ内で setup()
の呼び出しが自動で行われます。ユーザによる呼び出しを行う必要はありません。
また、Serial
(UART0) のバッファサイズは、コンパイル時に決定されます。マクロ MWX_SER_TX_BUFF
(未指定時は 768), MWX_SER_RX_BUFF
(未指定時 256) により変更できます。
ハードウェアの初期化を行う。
Serial
(UART0) は ライブラリ内で begin()
の呼び出しが自動で行われます。ユーザによる呼び出しを行う必要はありません。
指定したボーレートの下2桁の数値は0に丸めて処理します。またハードウェアの制限により指定したボーレートより誤差が生じます。
(未実装)ハードウェアの使用を停止する。
Cライブラリで利用する TWE_tsFILE*
形式での構造体を得る。
Serial (UART) では、_sSerial
構造体が定義されています。
ディジタル入力管理クラス (mwx::periph_buttons)
ディジタル入力の変化を検出します。このクラスは、同じ検出値が複数回得られたときに変化を検出します。メカ式のボタンのチャタリングの影響を小さくするのに有効です。
パラメータのmax_history
は、begin()
で設定可能な参照回数の最大値です。ここではメモリーの確保と初期化を行います。
Buttons
の動作を開始します。1番目のパラメータbmPortMask
は監視対象のディジタル入力のビットマップを指定します。bit 0がDIO 0, ... , bit N がDIO Nに対応します。複数指定することができます。2番目のu8HistoryCount
は値の確定をするのに必要な回数です。3番目のtick_delta
は値の確認を行う間隔をmsで指定します。
値の確定にはu8HistoryCount*tick_delta
[ms]かかることになります。例えばu8HistoryCount
=5, tick_delta
=4の場合は、状態の確定に最低約20msかかります。
確認はTickTimer
のイベントハンドラで行っています。割り込みハンドラではないので、処理等の遅延の影響を受けますが、メカ式ボタン等のチャタリング抑制には十分です。
Buttons
の動作を終了します。
変化が検出されたときにtrue
を返します。read()
を実行するとクリアされます。
availableになったとき呼び出します。u32port
は現在の入力DIOのビットマップ、u32changed
は変化が検出されたDIOのビットマップです。
Buttonsが動作していない場合はfalse
を返します。
Buttonsが動作を開始した時点では、DIOの入力状態は未確定です。値が確定した時点でavailableになります。このときread()
で読み出すビットマップのMSB(bit31)が1にセットされます。
動作確定を要するため、入力値が定常的に変化するポートを監視する目的では利用できません。
スリープ前にButtonsが稼働状態であれば、復帰後に再開します。再開後、初回確定を行います。
ADC (mwx::periph_analogue.hpp)
Analogueは、ADCの実行と値の取得を行います。一度に複数のチャネルを連続取得でき、またこれをタイマーなどの周期に合わせて逐次実行可能です。
標準アプリ(App_Twelite)では、半導体データシート中のピン名ADC2/ADC3が、TWELITE DIPの並びにあわせてAI3/AI2 となっています。ご注意ください。
ADCの初期化を行います。setup()では、半導体内部のレギュレータの始動、周期実行するためのタイマーデバイスの指定、指定チャネルすべてのADCが終了したときに呼び出されるコールバック関数の指定します。
begin()
の呼び出し後、速やかに最初のADC処理が開始され、その終了割り込から次のピンの処理を開始します。すべての処理が終われば(指定されている場合)コールバック関数が呼び出されます。次のタイマー割り込みが発生まで待ってから新たなADC処理を開始します。
2番目のパラメータは、ACを開始するまでのタイマー割り込みの回数を指定します。例えばTickTimer
は1msごとに呼び出されますが、パラメータに16
を指定すれば 16msごとの処理になります。
デフォルトのADCピン(PIN_ANALOGUE::A1
,PIN_ANALOGUE::A2
)を指定してADC処理を開始します。end()
では中断したADC処理を再開します。
ADC処理を終了し、半導体内部のレギュレータを停止します。
ADCの値が取得後にtrue
になります。本関数により確認した後は次のADC完了まではfalse
です。
ADC値を読み出します。パラメータには読み出したいADCピンを指定します。read()
はmVに変換した読み値、read_raw()
はADCの値(0..1023)を戻します。
Vccはread()
で読み出すことを推奨します。read_raw()
の値からmVに変換するには、特別な変換式を適用する必要があるためです。
ADC完了(available)後、次のADC処理が実行するタイミング付近まで遅れて値を読み出すと、次のADC値が戻される場合があります。ADCの処理は割り込みハンドラで実施されているためloop()
の処理中であっても値が更新されるためです。
ADCの割り込みハンドラはsetup()
の呼び出し時にperiph_analogue::ADC_handler()
に設定されます。
ADCがbegin()
により周期実行状態であれば、スリープ復帰後もADC処理を再開します。
パルスカウンタ (mwx::periph_pulse_counter)
パルスカウンタは、マイコンのCPUが稼働していない時もパルスを読み取り計数する回路です。パルスカウンターは2系統あります。PC0はPulseCounter0
, PC1はPulseCounter1
に割り当てられます。
またPulseCounter
はPulseCounter1
の別名です。
オブジェクトを初期化し、計数を開始します。1番目のパラメータrefct
は割り込みやavailable判定の基準となるカウント数です。この数を超えたときにアプリケーションに報告されます。またrefct
には0を指定することもできます。この場合は、スリープ起床要因にはなりません。
2番目のパラメータedge
は割り込みが立ち会がり(PIN_INT_MODE::RISING
)か立下り(PIN_INT_MODE::FALLING
)を指定します。
3番目のdebounce
は、0,1,2,3の値をとります。1,2,3の設定はノイズの影響を小さくするため値の変化の検出に連続した同じ値を要する設定です。
検出を中止します。
指定カウント数(begin()
のrefct
)が0の場合は、カウントが1以上でtrue
を返します。
指定カウント数(begin()
のrefct
)が1以上の場合は、検出回数が指定カウント数を超えた場合にtrue
となります。
カウント値を読み出します。読み出し後にカウント値を0にリセットします。
シリアルポート向き書式入力 (mwx::serial_parser)
この組み込みクラスはシリアルポートでの書式入力に利用することを想定して組み込みオブジェクトとして定義しています。
初期化時(begin()
)にヒープから内部で使用するバッファ領域を確保するmwx::serial_parser<mwx::alloc_heap<uint8_t>>
型として定義されています。
詳細はクラス を参照してください。
begin()
メソッドによりハードウェアの初期化を行った後、beginTransaction()
によりバスの読み書きができるようになります。beginTransaction()
を実行するとSPIのセレクトピンが選択されます。読み書きはtransfer()
関数を用います。SPIは読み出しと書き込みを同時に実行します。
バスの利用開始を行います。SPIのセレクトピンをセットします。
settings
パラメータを与えて呼び出した場合は、バスの設定を行います。
バスの利用を終了します。SPIのセレクトピンを解除します。
バスの読み書きを行います。trasnfer()
は8bit、transfer16()
は16bit、transfer32()
は32bitの転送を行います。
メンバ関数を利用した方法は、抽象度が比較的低く、C言語ライブラリで提供されるような一般的なAPI体系に倣っています。二線シリアルバスの操作手続きがより直感的です。
ただしバスの利用の開始と終了を明示的に意識して記述する必要があります。
指定バイト数分を一括で読み出します。読みだした結果はキューに保存されるため、直後にキューが空になるまで .read()
メソッドを呼び出すようにしてください。
書き出し処理は、beginTransmission()
を実行後、write()
メソッドにより行います。一連の書き出しが終了したら endTranmission()
を呼びます。
書き出しの転送を初期化する。書き出し処理終了後、速やかに endTransmission()
を呼び出す。
1バイトの書き出しを行う。
バイト列の書き出しを行います。
書き出しの終了処理を行います。
タイマー, PWM (mwx::periph_timer)
タイマーでは、指定周期でのソフトウェア割り込みを発生させる目的、指定周期でPWM出力を行う2つの機能があります。TWELITE無線モジュールには0..4まで合計5つのタイマーが利用可能です。
組み込みオブジェクト名は Timer0..4
ですが、このページでは TimerX
と記載します。
タイマーを初期化します。この呼び出しにより必要なメモリ領域の確保を行います。
タイマーを開始します。1番目のパラメータは、タイマーの周期でHzで指定します。2番目のパラメータをtrue
にするとソフトウェア割り込みが有効になります。3番目のパラメータをtrue
にするとPWM出力を有効にします。
change_hz()
で周波数を変更することが出来ます。change_hz()
ではbegin()
の指定より細かい指定が可能です。
change_duty()
でPWM出力のデューティー比を変更できます。
割り込みハンドラの処理を記述するには、の定義が必要です。
タイマーの動作を停止します。
タイマー割り込みが発生した直後のloop()
でtrue
になり、loop()
が終了すればfalse
になります。
デューティー比の設定を行う。1番目のパラメータにデューティ比を指定します(小さい値を指定すると波形の平均はGNDレベルに近づき、大きい値を指定するとVccレベルに近づく)。2番目のパラメータはデューティ比の最大値を指定します。
duty_max
は1024,4096,16384
のいずれかの指定を推奨します。
内部でのカウント値の計算に除算が発生しますが、これら3つに限りビットシフトによる演算を行っていますが、これ以外の値では計算量の大きい除算処理が実行されます。
タイマーの周波数を設定します。2番目のパラメータは周波数の小数点3桁分の値を整数で指定します。例えば 10.4 Hz としたい場合は hz=10, mil=400
と指定します。
ヘルパークラス版はより抽象度が高い実装です。読み書きを行うオブジェクト transceiver
を生成することが、バスの利用開始となり、オブジェクトを破棄するとバス利用の終了手続きを行います。
if文の判定式内でオブジェクトの生成を行うことで、オブジェクトの有効期間はif節内のスコープに限定され、if節を抜けた時点でオブジェクトは破棄され、その時点でバスの利用終了の手続きを行います。
また、読み書きオブジェクトは、mwx::stream
インタフェースを実装しているため<<
演算子などを利用することができます。
バスの利用開始と終了をオブジェクトの有効期間と一致させることで、ソースコードの見通しを良くし、また終了手続きの記述漏れなどを防ぎます
mwx::stream
インタフェースによる読み書き手続きを統一します
読み込み処理とその終了手続きをスコープ内 if() { ... }
で行うためのヘルパークラスを用いた読み込み方法。
上記では get_rwer()
メソッドにより生成された x
オブジェクトを用いて1バイトずつ読み書きを行っています。
if(...)
内で x
オブジェクトを生成します。同時にSPIバスのセレクトピンをセットします。(型は、型推論によるユニバーサル参照 auto&&
で解決しています。)
生成した x
オブジェクトには operator bool ()
が定義されており、判定式の評価として利用される。SPIバスの場合は常に true
となる。
x
オブジェクトには uint8_t transfer(uint8_t)
メソッドが定義されていて、これを呼び出すことでSPIに対して8bitの読み書き転送を行。
if() { ... }
スコープの末尾で x
のデストラクタが呼び出され、SPIバスのセレクトピンを解除します。
SPIバスの読み書きに用いるワーカーオブジェクトを取得します。
それぞれ8bit,16bit,32bitの転送を行い、読み取り結果を書き込んだデータ幅と同じデータ幅で返す。
int
型,uint8_t
型は8bitの転送を行います。
uint16_t
型、uint32_t
型は、それぞれ16bitの転送、32bitの転送を行います。
転送結果は最大16バイトの内部FIFOキューに格納され >>
演算子により読み出します。バッファが大きくないので、転送都度読み出すことを想定します。
直前の転送と同じデータ幅の変数を指定します。
読み出した結果が不要の場合はnull_stream()オブジェクトを使用します。iで指定したデータバイト分だけ読み飛ばします。
システムタイマー (mwx::periph_ticktimer)
TickTimerはTWENETの内部制御用に利用され、暗黙に実行されています。タイマーの周期は1msです。loop()
中でTickTimerイベントにより1msごとの処理を記述する目的でavailable()
メソッドのみを定義しています。
必ず1ms刻みでavailableになる訳ではない点に注意してください。
ユーザプログラムの記述内容や、システム内部の割り込み処理などが要因で、大きな遅延が発生することもあり、イベントが飛ばされるような場合もあります。
available()
TickTimer割り込み発生後にセットされ、その直後のloop()
でtrue
になります。loop()
終了後にクリアされます。
二線シリアル(I2C) master の読み書き (mwx::periph_wire)
二線シリアル(I2C) master の読み書きを行います。
以下の定義型で引数や戻り値の型を記載します。
API 中に STOP ビットの扱いが厳格でない呼び出しを行うものもあります。
write(), writer::operator() ()
には、本解説以外にもいくつか引数が定義されている。
固定配列型
uint8_t cmds[]={11,12};
...
Wire.write(cmds);
initializer_list<>
型
Wire.write({11,12})
ライブラリ内でインスタンスの生成と必要な初期化は行われます。ユーザコードでは Wire.begin()
を呼び出すことで利用可能となります。
requestFrom()
メソッドを用いる場合、データを一時保管するための FIFO キューのサイズを指定できます。コンパイル時にマクロMWX_TWOWIRE_BUFF
に必要なバイト数を指定してコンパイルする。デフォルトは 32 バイトです。
例:
-DMWX_TWOWIRE_BUFF=128
ハードウェアの初期化を行います。
初期化せずにWireの操作を行うとTWELITE無線モジュールがハングアップします。
スリープからの起床時は、スリープ直前で動作していた場合、直前の状態に復帰します。
読み書きの手続きは、以下の2種類あります。いずれかを選択して利用します。
address
で指定したデバイスが応答するかを確認します。デバイスが存在する場合は true
が戻ります。
アクトを動作させる。
1.
2. +
beginTransaction(), endTransaction(), transfer(), transfer16(), transfer32()
transceiver
1番目のパラメータにはADCを行いたいポートを指定します。ポートの指定はピンの定義で述べたポート番号に対応するビットをセットしたビットマップになります。例えば PIN_ANALOGUE::A2
とPIN_ANALOGUE::VCC
の2つのピンの値を得たい場合は (1 <<PIN_ANALOGUE::A1 | 1<<PIN_ANALOGUE::VCC )
を指定します。を用いpack_bits(PIN_ANALOGUE::A1,PIN_ANALOGUE::VCC)
のように記述することもできます。
requestFrom(), beginTransmission(), endTransmission(), write()
reader, writer
パラメータ
解説
speed
UART のボーレートを指定する。
config
未使用 (8N1固定)
パラメータ
解説
slave_select
使用するSPIスレーブのセレクトピンを指定する。
0 : DIO19
1 : DIO0 (DIO 19 は予約されます)
2 : DIO1 (DIO 0,19 は予約されます)
settings
SPIのバス設定を指定します。
clock
[hz]でSPIバスの周波数を指定します。指定した周波数に近いディバイザが選択されます。16Mhzまたは16Mhzを偶数で割った値になります。
bitOrder
はSPI_CONF::MSBFIRST
かSPI_CONF::LSBFIRST
を指定します。
dataMode
はSPI_CONF::SPIMODE0..3
を指定します。
パラメータ
解説
bWaitInit
true
を指定すると、半導体内部のレギュレータの初期化を待つ。
kick_ev
周期実行に指定するタイマーデバイスを指定する。指定可能なデバイスは、以下の5種類で、初回以外は割り込みハンドラ内でADが開始される。
E_AHI_DEVICE_TICK_TIMER (TickTimer)
E_AHI_DEVICE_TIMER0 .. 4 (Timer0 .. 4)
fp_on_finish
指定されたポートすべてのADCが終了後に、割り込みハンドラ内から呼び出されるコールバック関数。ADC計測値をFIFOキューなどに別途格納したい場合に利用する。
パラメータ
解説
u8address
書き出し対象のI2Cアドレス
パラメータ
解説
value
書き込むバイト
戻り値 size_type
書き込んだバイト数。0
はエラー。
パラメータ
解説
*value
書き込むバイト列
size_type
バイト数
戻り値 size_type
書き込んだバイト数。0はエラー。
パラメータ
解説
sendStop = true
STOPビットを発行します。
戻り値 uint8_t
0: 成功 4: 失敗
パラメータ
解説
u8mode
バススピードを指定する。
WIRE_100KHZ または WIRE_400KHZ を指定する。
b_portalt
ハードウェアのピン割り当てを変更する。
定数
意味
const uint8_t
SPI_CONF::MSBFIRST
MSB を先頭ビットにする
const uint8_t
SPI_CONF::LSBFIRST
LSB を先頭ビットにする
const uint8_t
SPI_CONF::SPI_MODE0
SPI MODE 0 に設定する
const uint8_t
SPI_CONF::SPI_MODE1
SPI MODE 1 に設定する
const uint8_t
SPI_CONF::SPI_MODE2
SPI MODE 2 に設定する
const uint8_t
SPI_CONF::SPI_MODE3
SPI MODE 3 に設定する
定数
種別
標準アプリでのピン名
uint8_t PIN_ANALOGUE::A1 = 0
ADC1ピン
AI1
uint8_t PIN_ANALOGUE::A2 = 1
ADC2ピン
AI3
uint8_t PIN_ANALOGUE::A3 = 2
uint8_t PIN_ANALOGUE::D0 = 2
ADC3ピン (DIO0)
AI2
uint8_t PIN_ANALOGUE::A4 = 3
uint8_t PIN_ANALOGUE::D1 = 3
ADC4ピン (DIO1)
AI4
uint8_t PIN_ANALOGUE::VCC = 4
Vcc 電源電圧
設定
連続サンプル数
最大検出周波数
0
-
100Khz
1
2
3.7Khz
2
4
2.2Khz
3
8
1.2Khz
パラメータ
解説
u8address
読み出し対象のI2Cアドレス
length
読み出しバイト数
b_send_stop=true
true
の時、読み込み終了時にSTOP
ビットを設定する。
戻り値型 size_type
読み出したバイト数。 0
は読み出しの失敗。
パラメータ
解説
buf_tx
TX用のFIFOバッファサイズ
buf_rx
RX用のFIFOバッファサイズ
コンテナクラス(smplbuf
, smplque
)のテンプレート引数として指定し、内部で利用するメモリの確保または領域指定します。
このクラスはユーザコードから直接呼び出すものではありませんが、内部的にコンテナの宣言に用いられています。
内容
alloc_attach<T>
すでにあるバッファを指定する
alloc_local<T, int N>
Nバイトのバッファを内部に静的確保する
alloc_heap<T>
指定したサイズをヒープに確保する
alloc_attach
やalloc_heap
ではメモリ確保クラスに応じた初期化メソッド (begin()
)を実行する必要があります。
バッファーp
・サイズn
で初期化します。
バッファのサイズを返す。
想定したallocクラスと違うメソッド呼び出し記述に対して、static_assertのように、コンパイルエラーを発生させるためのメソッドです。
このクラスはTWENETのtsRxDataApp
構造体のラッパークラスです。
このクラスオブジェクトは、ビヘイビアのコールバック関数またはthe_twelite.receiver.read()
により取得できます。
packet_rx
では、特にパケットのデータペイロードをsmplbuf
コンテナで取り扱えるようにし、expand_bytes()
などのユーティリティ関数によりペイロードの解釈記述を簡素化しています。
現時点では、シンプル中継ネットワーク<NWK_SIMPLE>
で必要とされるものを中心にメソッド等のインタフェースを実装しています。
パケットのデータペイロードを取得する。
<NWK_SIMPLE>
を用いた場合は、先頭に<NWK_SIMPLE>
用のヘッダデータがあります。戻りとして参照されるコンテナは、このヘッダ部分を除いた部分配列になります。ヘッダ部分まで参照したいときはget_psRxDataApp()
によりtsRxDataApp
構造体を参照してください。
TWENET Cライブラリの受信構造体を得る。
ペイロードのデータ長を返す。.get_payload().size()
と同じ値になる。
LQI値 (Link Quality Indicator)を得る。
LQIとは電波通信品質を示す値です。0から255までの数値で表されます。
ちなみに、いくつかの段階で評価する場合は、50未満(悪い -80dbm 未満)、50~100(やや悪い)、100~150(良好)、150以上(アンテナの近傍)といった区分けも可能です。これらは目安である点にご留意ください。
送信元のアドレスを得る。
get_addr_src_long()
は送信元のシリアル番号で、MSB(bit31)が必ず1になります。
get_addr_src_lid()
は送信元の論理IDで0x00
-0xFE
までの値をとります(<NWK_SIMPLE>
で指定する論理IDです)。
宛先アドレスを得ます。
宛先アドレスは、送信元で指定され、宛先の種別によって値の範囲が変わります。
値
解説
MSB(bit31)がセットされている
宛先としてシリアル番号を指定しています。
0x00
-0xFF
宛先として論理ID(8bit)が指定されています。
ヘルパークラス版はより抽象度が高い実装です。読み書きに対応するオブジェクト reader, writer
を生成することがバスの利用開始となり、オブジェクトを破棄するとバス利用の終了手続きを行います。
if文の判定式内でオブジェクトの生成を行うことで、オブジェクトの有効期間はif節内のスコープに限定され、if節を抜けた時点でオブジェクトは破棄され、その時点でバスの利用終了の手続きを行います。
また読み書きオブジェクトはmwx::stream
インタフェースを実装しているため<<
演算子などを利用することができます。
バスの利用開始と終了をオブジェクトの有効期間と一致させることで、ソースコードの見通しを良くし、また終了手続きの記述漏れなどを防ぐ
mwx::stream
インタフェースによる読み書き手続きの統一
読み込み処理とその終了手続きをスコープ内 if() { ... }
で行うためのヘルパークラスを用いた読み込み方法です。
上記では get_readr()
メソッドにより生成された rdr
オブジェクトを用いて1バイトずつ読み出しします。 メソッドのパラメータには読み込みたい二線シリアル ID を指定します。
if(...)
内で rdr
オブジェクトを生成。(型は、型推論によるユニバーサル参照 auto&&
で解決しています。)
生成した rdr
オブジェクトには operator bool ()
が定義されており、判定式の評価として利用される。指定された ID により通信が可能であれば true
となる。
rdr
オブジェクトには int operator () (void)
演算子が定義されていて、これを呼び出すことで2線シリアルバスから1バイトのデータを読み出す。読み込みに失敗したときは -1
が戻り、成功した場合は読み込んだバイト値が戻る。
if() { ... }
スコープの末尾で rdr
のデストラクタが呼び出され、二線シリアルバスの STOP
を行う。
I2C 読み出しに用いるワーカーオブジェクトを取得します。
パラメータ
解説
addr
読み込み用のI2Cアドレス
read_count
読み出しバイト数(この値を指定すると最後の転送で STOP ビットを発行する)。0を指定した場合は STOP ビットなしとなる(デバイスによっては動作するものもあります)
書き出し処理とその終了手続きをスコープ内 if() { ... }
で行うためのヘルパークラスを用いた読み込み方法です。
上記では get_writer()
メソッドにより生成された wrt
オブジェクトを用いて1バイトずつ書き出す。 メソッドのパラメータには読み出したい二線シリアル ID を指定します。
if(...)
内で wrt
オブジェクトを生成する。(型名は長くなるため auto で解決)
生成した wrt
オブジェクトには operator bool ()
が定義されており、判定式の評価として利用される。指定された ID により通信が可能であれば true
となる。
wrt
オブジェクトには int operator () (void)
演算子が定義されていて、これを呼び出すことで2線シリアルバスに1バイトのデータを書き出しす。失敗したときは -1
が戻り、成功した場合は書き込んだバイト値が戻る。
if() { ... }
スコープの末尾で wrt
のデストラクタが呼び出され、二線シリアルバスの STOP
を行う。
I2C書き出しに用いるワーカーオブジェクトを取得します。
パラメータ
解説
addr
書き出し用のI2Cアドレス
int
型,uint8_t
型は8bitの転送を行います。
1バイト書き出す。
それぞれのデータ型のサイズ分だけ読み出します。
1バイト読み出します。エラーがある場合は-1を戻し、正常時は読み出したバイト値を戻します。
b_stop
をtrue
にすると、その読み出しにおいてSTOPビットを発行します。
以下の例は、環境センサーパルの温湿度センサーSHTC3の計測例です。
3軸の加速度センサーの値を格納するための構造体ですが、コンテナクラスに格納したときの利便性を上げるための手続きを追加しています。
axis_xyzt
を格納したコンテナクラスのイテレータをパラメータとして、X, Y, Z 軸のいずれかの要素にアクセスするイテレータを生成します。
以下の例では、buf.begin()
, buf.end()
をX軸用のイテレータとしてアルゴリズムstd::minmax_element
に用いています。
axis_xyzt
を格納したコンテナクラスのXYZ軸のいずれかの軸を取り出した仮想的なコンテナクラスを生成する関数です。この生成したクラスにはbegin()
とend()
メソッドのみ実装されています。このbegin()
とend()
メソッドで取得できるイテレータは前節get_axis_{x,y,z}_iter()のイテレータと同じものになります。
このクラスはTWENET CライブラリのtsTxDataApp
構造体のラッパクラスで、このクラスをベースとした派生クラスのオブジェクトをネットワークビヘイビアより取得して利用します。
ネットワークビヘイビアの .prepare_tx_packet()
によって行います。
上記の例ではthe_twelite.network.use<NWK_SIMPLE>()
によってネットワークビヘイビアのオブジェクトを取り出します。このオブジェクトの.prepare_tx_packet()
によってオブジェクトpkt
が生成されます。型名はauto&&で推論されていますがpacket_tx
の派生クラスになります。
このpkt
オブジェクトは、まず、()
内の条件判定にてtrue
かfalse
を返します。false
が返ってくるのは、送信用のキューが一杯でこれ以上要求が追加できない時です。
無線パケットには宛先情報など相手に届けるための様々な設定を行います。設定には設定内容を含むオブジェクトを<<演算子の右辺値に与えます。
以下に設定に用いるオブジェクトについて記載します。
各設定の利用可否や意味合いは、ネットワーク ビヘイビアの仕様によります。
宛先アドレスaddr
を指定します。宛先アドレスの値については、ネットワークビヘイビアの仕様を参照してください。
MSB(bit31=0x80000000
)がセットされるアドレスは、無線モジュールのシリアル番号宛という意味になります。
0x00
..0xEF
は、8bitの論理IDを意味します。0xFEは子機宛(0x01
..0xEF
)の同報通信(ブロードキャスト)、0xFF
は親機子機関係なく同報通信(ブロードキャスト)します。
再送回数の指定を行います。再送回数はu8countで指定します。force_retryは、送信が成功しようがしまいが、指定回数の再送を行う設定です。
ネットワークビヘイビア<NWK_SIMPLE>
では、同じ内容のパケットをu8count+1
回送信します。
force_retry
の設定は無視されます。
パケットを送信するまでの遅延と再送間隔を設定します。u16DelayMin
とu16DelayMax
の2つの値をミリ秒[ms]で指定します。送信要求をしてからこの間のどこかのタイミングで送信を開始します。再送間隔をu16RetryDur
の値[ms]で指定します。再送間隔は一定です。
内部処理の都合で指定通りのタイミングで送信処理が始まらない場合もあります。また、IEEE802.15.4の処理でもパケット創出までの時間ブレが発生します。これらのタイミングのブレは、多くのシステムではパケットの衝突回避を行う上で有効な手立てとなります。
厳格なタイミングでのパケット送信は、IEEE802.15.4の規格の性質上、例外的な使用方法とお考え下さい。
この指定は有効です。
最初の送信から1秒を超えて再送され到達した同一パケットについては、新たなパケットが到達したとして重複除外が為されません。再送間隔を長く設定したり、中継でのパケット到達遅延により1秒を超えて同じパケットを受信する場合があります。
重複パケットの処理の設定は<NWK_SIMPLE>
ビヘイビアの初期化で設定できます。
パケット送信を「できるだけ速やかに」実行するように要求する設定です。TWENETでのパケット送信処理は、1msごとに動作するTickTimer起点で行われています。この設定をすることで、要求後速やかにパケット送信要求が処理されます。もちろん、tx_packet_delay(0,0,0)
以外の設定では意味がない指定になります。
他のパケット送信処理が行われている場合は、通常の処理になります。
この指定は有効です。
無線パケット通信では、送信完了後、送信相手先からACK(アック)という短い無線電文を得て、送信成功とする送信方法があります。このオプションを設定することで、ACK付き送信を行います。
<NWK_SIMPLE>
では、この指定は無効です。コンパイルエラーになります。
<NWK_SIMPLE>
は、シンプルに動作する中継ネットワークの実装を目的としており、ACK付きの通信は行いません。
ブロードキャストの指定を行います。
<NWK_SIMPLE>
では、この指定は無効です。コンパイルエラーになります。
替わりに宛先アドレスtx_addr(0xFF)
(ブロードキャスト)またはtx_addr(0xFE)
(子機宛のブロードキャスト)を指定します。
0..7の指定ができるTWENET無線パケットのタイプIDを指定します。
<NWK_SIMPLE>
では、この指定は無効です。コンパイルエラーになります。
<NWK_SIMPLE>
ではタイプIDを内部的に使用しています。ユーザは使用できません。
内部が配列構造のコンテナクラスです。初期化時にバッファの最大サイズを決定しますが、その最大サイズまでの範囲で可変長の配列として振る舞います。
smplbuf
は要素の型T
とメモリの確保方法alloc
で指定したメモリ領域に対して配列の操作を提供するコンテナクラスです。alloc
の指定は煩雑であるためusing
を用いた別名定義が行っています。
オブジェクトの宣言例です。宣言の直後に初期化用のメソッド呼び出しを行います。いずれも初期化直後の最大サイズは128バイトで、サイズは0です。必要に応じてサイズを拡張しながら使用します。
上記のuint8_t
型に限り別名定義があります。
通常の配列のように[]演算子などを用いて要素にアクセスできますし、イテレータを用いたアクセスも可能です。
push_back()
メソッドを定義しています。末尾にデータを追記するタイプのアルゴリズムが使用可能になります。
型T
でサイズN
のコンテナを宣言します。宣言後に初期化のメソッドを呼び出します。
smplbuf_attach
では、使用するバッファの先頭ポインタT* buf
と配列の初期サイズsize
と最大サイズN
を指定します。
smplbuf_local
のみ、ローカルオブジェクトとして宣言する場合は、初期化メソッド.init_local()
を省略できます。
alloc_local
でグローバルオブジェクトを生成する場合は、smplbuf
コンテナの使用前に.init_local()
メソッドを呼び出します。
初期化子リスト(イニシャライザリスト){ ... }
によるメンバーの初期化をできます。smplbuf_local
のローカル宣言でのコンストラクタでの利用を除き、初期化のメソッドを呼び出した後に有効です。
代入演算子の右辺値 (smplbuf_local
, smplbuf_attach
, smplbuf_heap
)
コンストラクタ(smplbuf_local
のローカル宣言、グローバル宣言は不可)
末尾にメンバーc
を追加します。append()
の戻り値はbool
で、バッファが一杯で追加できないときにfalse
が返ります。
pop_back()
は末尾のエントリを抹消します。
empty()
は配列に要素が格納されていない場合にtrue
を戻します。is_end()
は反対に配列サイズ一杯まで要素が格納されているときにtrue
を戻します。
size()
は配列の要素数を返します。
capacity()
は配列の最大格納数を返します。
reserve()
は配列のサイズを拡張します。配列が格納されていない領域はデフォルトで初期化されます。
reserve_hear()
は配列の先頭部に指定したサイズの領域を確保します。コンテナオブジェクトからは参照できない領域となります。例えばパケットペイロードのヘッダ部分を読み飛ばした部分配列にアクセスするようなコンテナとして利用できるようにします。確保した領域を戻しすべてアクセスできるようにコンテナを戻すには確保時と同じ負の値を与えます。
redim()
は利用領域のサイズを変更します。reserve()
と違い、未使用領域の初期化を行いません。
要素にアクセスします。
i
に負の値を与えるとバッファー末尾からの要素となります。-1
の場合は末尾の要素、-2
は末尾から一つ手前となります。
mwx::stream に printf の書式を入力
mwx::stream
の << 演算子に対してフォーマット書式を書き出すヘルパークラスです。ライブラリ内では Using format=mwx::mwx_format;
として別名定義しています。
可変数引数リストに登録できる引数は最大4つです。
コンストラクタで受け取った引数リストを、パラメータパックの展開機能を用いてクラス内部変数に格納する
operator <<
が呼び出された時点で、fctprintf()
を呼び出し、ストリームにデータを書き出す
コンストラクタでは、書式のポインタとパラメータを保存します。続く <<
演算子による呼び出しでフォーマットを解釈して出力処理を行います。
パラメータ
解説
fmt
フォーマット書式。
TWESDK/TWENET/current/src/printf/README.md 参照
...
フォーマット書式に応じたパラメータ。 ※ 最大数は4で、5つ以上のパラメータではコンパイルエラーとなる。
※ 書式との整合性はチェックしないため、不整合な入力に対しては安全ではない。
fmt
は本オブジェクトが破棄されるまで、アクセス可能であることが必要です。
入出力ストリーム
入出力ストリームを処理する上位クラスです。
CRTP (Curiously Recurring Template Pattern) 手法を用いたポリモーフィズムにより、いくつかのクラス(Serial, Wire, SPI, smplbuf
) にインタフェースを提供します。
CRTP では下位クラスは template class Derived : public stream<Derived>;
のように定義し、上位クラスからも下位クラスのメソッドを参照します。
本クラスでは print
メソッド、<<
演算子などの共通処理の定義を行い、下位クラスで実装した write()
メソッドなどを呼び出すことで、仮想関数を用いるのと近い実装を行っています。
下位クラスでは、以下に列挙する関数を実装します。
入力が存在する場合は 1、存在しない場合は 0 を返します。
パラメータ
解説
戻り値 int
0: データなし 1:データあり
本実装の戻り値はバッファ長ではありません。
出力をフラッシュ(出力完了まで待つ)します。
ストリームより1バイトデータを入力します。データが存在しない場合は -1
を戻します。
ストリームに1バイト出力します。
パラメータ
解説
n
出力したい文字。
戻り値 size_t
出力が成功すれば 1、失敗すれば 0。
1バイト出力を行うスタティック関数です。クラスメソッドではないため、メンバー変数等の情報は利用できません。替わりにパラメータとして渡される vp にクラスインスタンスへのポインタを渡します。
このスタティック関数は内部的に利用されfctprintf()
の1バイト出力関数として関数ポインタが渡ります。これを用いてprint
メソッドなどを実装しています。
パラメータ
解説
out
出力したい文字
vp
クラスインスタンスへのポインタ 通常は、元のクラスにキャストして write() メソッドを呼び出す
1バイト出力します。
各種整形出力を行います。
パラメータ
解説
val
整形出力したい数値型
base
出力形式
BIN 二進数 / OCT 8進数 / DEC 10進数 / HEX 16進数
place
小数点以下の桁数
戻り値 size_t
書き出したバイト数
printf 形式での出力を行います。
TWESDK/TWENET/current/src/printf/README.md 参照
引数型
解説
char
1バイト出力 (数値としてフォーマットはしない)
int
整数出力 (printf の "%d")
double
数値出力 (printf の "%.2f")
uint8_t
1バイト出力する(char型と同様)
uint16_t
2バイト出力する(ビッグエンディアン順)
uint32_t
4バイト出力する(ビッグエンディアン順)
format()
printf 形式での出力
mwx::crlf
改行 CRLF の出力
mwx::flush
出力のフラッシュ
bigendian()
数値型をビッグエンディアン順で出力する。(右辺値)
std::pair<T*, T*>
バイト型の begin(), end()
ポインタを格納したペア。make_pair
により生成できる。T
は uint8_t
型を想定する。(右辺値)
bytelist()
std::initializer_list
を用いるバイト列の出力
smplbuf<uint8_t>
smplbuf<uint8_t>
のデータを出力する
smplbuf<uint8_t>::to_stream()
smplbuf<uint8_t>
のデータを出力する
バイト列として出力する際は、uint8_t, uint16_t, uint32_t
型にキャストします。また文字列として数値出力する場合は明示的にint
形にキャストするようにしてください。
入力処理を行います。
setup()
内では実行できません。
引数型
解説
uint8_t
1バイト入力
uint16_t
2バイト入力(ビッグエンディアン順)
uint32_t
4バイト入力(ビッグエンディアン順)
null_stream(int n)
nバイト読み捨てる
入力タイムアウトとエラーを管理します。set_timeout()
によりタイムアウト時間を指定し、>>演算子により入力処理を行います。所定時間内までに入力が得られない場合は get_error_status()
によりエラー値を読み出せます。clear_error_status()
によりエラー状況をクリアします。
引数型
解説
centisec
1/100秒単位でタイムアウト時間を設定します
値
意味
0
エラーなし
1
エラー状況
シリアル書式入出力 (mwx::serial_parser)
シリアル書式の入出力のために用います。内部に解釈済みのバイナリ系列を保持するバッファを持ち、入力時は1バイトずつ系列を読み出し書式に従い内部バッファに格納し、系列の解釈が完了した時点で完了状態になるものです。反対に出力時は内部バッファから所定の出力書式に従いバッファを出力します。
メモリバッファ取り扱い方法(alloc
)に応じて3種類のクラス名が定義されています。
begin()
の初期化のパラメータで渡す書式の種別です。ここではアスキー形式とバイナリー形式の2種類があります。
定数
種別
uint8_t PARSER::ASCII = 1
アスキー形式
uint8_t PARSER::BINARY = 2
バイナリー形式
バイナリ形式の取り扱いはアスキー形式に比べ、必要なツールや確認方法を含め一般に取り扱いが煩雑になります。通常はアスキー形式をお使いください。
アスキー形式は、バイナリで構成されたデータ列を文字列で表現する方法です。
例えばバイト列で 00A01301FF123456
をアスキー形式で表現すると、以下のようになります。先頭は :
で B1
がチェックサム、終端は [CR:0x0d][LF:0x0a]
となります。
:00A01301FF123456B1[CR][LF]
終端のチェックサムを省略できます。チェックサムからCRLFの系列をX
に置き換えます。文字化けによる誤ったデータ系列には弱くなりますが、実験などでデータを送付したいときに便利です。
:00A01301FF123456X
======
元データのバイト数
バイト数
解説
ヘッダ
1
:
(0x3A) コロンを指定します。
データ部
N
2N
元データの各バイトをアスキー文字列2文字(A-F は大文字)で表現します。
例えば 0x1F は 1
(0x31) F
(0x46) と表現します。
チェックサム
2
データ部の各バイトの和を8ビット幅で計算し2の補数をとります。つまりデータ部の各バイトの総和+チェックサムバイトを8ビット幅で計算すると0になります。
チェックサムバイトをアスキー文字列2文字で表現します。
例えば 00A01301FF123456
では 0x00 + 0xA0 + ... + 0x56 = 0x4F となり、この二の補数は0xB1 です。(つまり 0x4F + 0xB1 = 0x00)
フッタ
2
[CR] (0x0D) [LF] (0x0A) を指定する。
通常はアスキー形式を利用してください。
マイコン間通信での実装を考えるとバイナリ形式のほうが効率的ですが、実験などでの送受信の確認にはバイナリ通信に対応した特別なターミナルなどを準備する必要があり、チェックサムの計算も必須です。アスキー形式より利用の難易度は高くなります。
バイナリ形式は、バイナリで構成されたデータ列にヘッダとチェックサムを付加して送付する方法です。
例えば 00A01301FF123456
をバイナリ形式で表現すると、以下のようになります。
0xA5 0x5A 0x80 0x08 0x00 0xA0 0x13 0x01 0xFF 0x12 0x34 0x56 0x3D
======
元データのバイト数
形式におけるバイト数
解説
ヘッダ
2
0xA5 0x5A
を指定します。
データ長
2
データ長はビッグエンディアン形式の2バイトで、MSB (0x8000) を設定した上、データ部の長さを指定します。
例えばデータ部の長さが 8 バイトなら0x80 0x08
を指定します。
データ部
N
N
元データを指定します。
チェックサム
1
データ部の各バイトの XOR を計算します。
例えばデータ部が 00A01301FF123456
なら 0x00 xor 0xA0 xor ... 0x56 = 0x3D となります。
フッタ
(1)
チェックサムが事実上の終端です。無線モジュールからの出力では 0x04
(EOT) が付加されます。
宣言にはメモリの確保クラスを指定します。この指定は煩雑であるため、上述のように別名定義を行っています。
クラス名(別名定義) メモリ確保
内容
serparser_attach
すでにあるバッファをbegin()
にて指定する
serparser_local<N>
Nバイトのバッファを内部に確保する
serparser_heap
begin()
メソッドのパラメータで指定したサイズをヒープに確保する
メモリ確保クラスに応じたbegin()
メソッドを呼び出します。
ty
で指定する形式で、p
で指定したバッファを用います。バッファの最大長はmax_siz
で、バッファの有効データ長をsiz
で指定します。
この定義は、特に、データ列を書式出力したい場合に用います(>>
演算子参照)
ty
で指定する形式で初期化を行います。
ty
で指定する形式で、siz
で指定したサイズをヒープに確保して初期化します。
一度確保したヒープ領域は解放できません。
内部バッファを返す。バッファは smplbuf<uint8_t, alloc>
型となります。
入力文字を処理する。書式入力の入力文字列を1バイト受け取り書式に従い解釈します。例えばASCII書式では:00112233X
のような系列を入力として受け取りますが : 0 0 ... X
の順で1バイトずつ入力し、最後の X
を入力した時点で書式の解釈を完了し、完了済みと報告します。
parse()
のパラメータは入力バイト、戻り値は解釈完了であればtrue
を戻します。
parse()
で読み出し完了になったとき、次のparse()
を実行すると読み出し中のステータスに戻ります。
true
ならparse()
により読み出しが完了した状態で、false
なら解釈中となります。
内部バッファを書式形式でストリーム(Serial)に対して出力します。
アプリケーションのメインループです。ループ終了後はCPUがDOZEモードに遷移し低消費電流で次の割り込みを待ちます。
アクトの記述では、ほとんどの処理がこのループ内に記述されます。
バック関数定義は省略可能です。
loop()
関数の初回コールの手前で一度だけ呼び出されます。TWENET の初期化は終了しているのでsetup()
のような制約を考慮する必要はありません。
主な使い方は、
始動メッセージの表示
始動直後のスリープ遷移
setup()で始動することに不都合がある処理
このコールバック関数定義は省略可能です。
FIFOキューを構造のコンテナクラスです。
smplque
は要素の型T
とで指定したメモリ領域に対してFIFOキューの操作を提供するコンテナクラスです。alloc
の指定は煩雑であるためusing
を用いた別名定義が行っています。
要素型は原則として数値や数値などを格納する構造体を想定しています。デストラクタによる破棄手続きが必要なオブジェクトを格納することを想定していません(キューから要素を抹消する際にオブジェクトを抹消する処理をしていないため)。
宣言時に割り込み禁止設定を行うクラスIntr
を登録することが出来ます。このクラスは指定しない場合は、割り込み禁止制御を行わない通常の動作となります。
オブジェクトの宣言例です。宣言の直後に初期化用のメソッド呼び出しを行います。いずれも初期化直後の最大サイズは128バイトで、サイズは0です。必要に応じてサイズを拡張しながら使用します。
FIFOキューですのでpush()
,pop()
,front()
といったメソッドを用いて操作します。
イテレータによるアクセスも可能です。
型T
でサイズN
のコンテナを宣言します。宣言後に初期化のメソッドを呼び出します。
smplque_local
のみ、ローカルオブジェクトとして宣言する場合は、初期化メソッド.init_local()
を省略できます。
smplque_local
のグローバルオブジェクトを生成する場合は、smplbuf
コンテナの使用前に.init_local()
メソッドを呼び出します。
push()
はエントリをキューに追加します。
pop()
はエントリをキューから抹消します。
front()
は先頭のエントリ(一番最初に追加されたもの)を参照します。
back()
は末尾のエントリ(一番最後に追加されたもの)を参照します。
pop_front()
は先頭のエントリを戻り値として参照し、同時にそのエントリをキューから抹消します。
empty()
は配列に要素が格納されていない場合にtrue
を戻します。is_full()
は反対に配列サイズ一杯まで要素が格納されているときにtrue
を戻します。
size()
はキューに格納されている要素数を返します。
capacity()
はキューの最大格納数を返します。
キューのすべての要素を抹消します。
要素にアクセスします。0
が最初に追加した要素です。
begin()
とend()
によるイテレータを取得できます。イテレータの先頭はキューの最初に登録した要素です。イテレータを用いることで範囲for文やアルゴリズムが利用できます。
アプリケーションの記述を行うコールバック関数です。コールバックはシステム(ライブラリ)から呼び出されるという意味です。ユーザがいくつかのコールバック関数を定義することでシステムの振る舞いを記述します。
以下のコールバック関数は必須定義です。
setup()
loop()
それ以外の関数は定義しない場合は、何も実行しない空の関数が替わりにリンクされます。
正確なふるまいを参照したい方はソースコードmwx_appcore.cpp
を参照してください。
正確なふるまいを参照したい方はソースコードmwx_appcore.cpp
を参照してください。
コード実行の初期に呼び出され、初期化コードを記述します。
TWENETの初期化は setup()
関数が終了した後にも実行されます。多くの処理はTWENETが終了した後に実行するようになっているため、ここでは初期化以外の処理を行わないようにしてください。
注意すべき事項を以下に列挙します。
スリープthe_twenet.sleep()
の実行はできません。初期化後速やかにスリープしたいときはbegin()
関数内に最初のスリープ処理を記述してください。
delay()
関数は後述の処理*に置き換えられます。この場合、パラメータのms
はミリ秒を指定するものではありませんが、setup()
の32Mhzクロックに併せてミリ秒指定に近くなるように調整されています。
* delay()
の代替処理
twe::stream へのバッファ出力をフラッシュする。
mwx::stream
の出力バッファをフラッシュする。flush()
メソッドを呼び出すヘルパークラスへのインスタンス。
シリアルポートの場合は出力完了までポーリング待ちを行う
mwx::simpbuf
バッファの場合は 0x00
を末尾に出力する(サイズは変更しない)
twe::stream に改行コードを出力する
mwx::stream
の <<
演算子に対して改行コード (CR LF) を出力するためのヘルパークラスのインスタンスです。
応用としてがあります。
通常は使用しません。
ペリフェラルAPIも初期化もされていない、コード実行の再初期に呼び出されます。
このコールバック関数定義は省略可能です。
スリープから起床したときにloop()
に移行する前に呼ばれ、スリープ復帰後の初期化処理や復帰状態によって処理を分岐するための手続きを含めます。
センサーの読み出しなどの処理のみでloop()
での処理がないときは、この関数内で再びスリープを実行できます。
このコールバック関数定義は省略可能です。
通常は使用しません。
スリープ復帰後、ペリフェラルAPIが初期化されない再初期に呼び出されます。
この関数では割り込み要因の検出を行うことができます。
このコールバック関数定義は省略可能です。
システム時刻[ms]を得ます。
システム時刻はTickTimerの割り込みで更新されます。
ビヘイビアは、指定の方法でクラスを定義することで、the_twelite
クラスオブジェクトに登録できるようになります。登録したビヘイビアはTWENETに組み込まれて動作し、ユーザコードではアプリケーションの振る舞いを記述できるようになります。ループでの記述ではできなかったTWENETからの割り込みやイベントのコールバック関数を定義することが出来ます。ループでの記述に比べ、定義が多くなりますが、より複雑なアプリケーションを記述するのに向いています。
ビヘイビアのサンプルPAL_AMB-behaviorを参照してください。
ビヘイビアの定義には下記に示すようなクラス定義が必要です。
上記の例ではMY_APP_CLASSという名前でビヘイビアクラスを定義しています。いくつかの箇所にMY_APP_CLASSの記述が必要です。
クラス名の定義と、ベース(親)クラスの定義をします。MWX_APPDEFS_CRTP()
はマクロです。
ここでは必要な定義を #include
により取り込んでいます。
コンストラクタの定義です。
メインループで、グローバル定義のloop()
と同じ役割の関数です。
on_create()
はオブジェクト生成時(use<>()
メソッド)に呼び出されます。val
は将来の拡張のためのパラメータです。
on_begin()
はsetup()
終了後に呼び出されます。val
は将来の拡張のためのパラメータです。
スリープ前に呼び出されます。val
は将来の拡張のためのパラメータです。
スリープ復帰時の初期段階で呼び出されます。val
は将来の拡張のためのパラメータです。
この時点でまだペリフェラルが初期化されていません。スリープ起床要因の確認ができます。
スリープ復帰時に呼び出されます。val
は将来の拡張のためのパラメータです。
ここでスリープ呼び出しも可能です。
パケットが受信されたとき、受信したパケット情報をrx
として呼び出されます。
パケット送信完了時に送信情報をevTx
として呼び出されます。evTx.u8CbId
が送信時のIDでevTx.bStatus
が送信の成功(1
)失敗(0
)を示すフラグです。
ビヘイビアのハンドラ(割り込み、イベント、状態定義)はcppファイルに定義します。ファイルは分割できず、全てのハンドラ定義を一つのファイル中に記述します。
ハンドラの定義をしないビヘイビアの場合でも、必ず、下記のcppファイルを作成します。
cppファイルの冒頭と末尾にはMWXライブラリの必要な定義(#include "_mwx_cbs_cpphead.hpp"
)をインクルードする必要があります。
ファイルの冒頭には、上記のようにビヘイビア定義の.hppファイルをインクルードします。__MWX_APP_CLASS_NAME
にはビヘイビアのクラス名を指定します。上記ではMY_APP_CLASS
です。
ファイルの末尾では必要な定義(#include "_mwx_cbs_cpptail.cpp"
)をインクルードします。
ハンドラ定義は以下の例のように記述します。定義の種別については後述します。定義用のマクロを用いて利用したいハンドラの定義を記述します。利用しないハンドラは記述しないようにしてください。
MWX_???_INT()
は割り込みハンドラの定義、MWX_???_EVENT()
はイベントハンドラの定義、MWX_STATE()
はステートマシンの状態定義です。
割り込みハンドラは、マイコンの割り込みが発生したときに現在実行中のコードを中断して実行されます。このため、可能な限り短い処理を記述することが望ましく、また、変数等の操作に対しても細心の注意が必要です。
割り込みハンドラのパラメータにはuint8_t& handled
があり、この値をtrue
にセットすることで、続くイベント呼び出しを行いません。
handled
がfalse
のまま割り込みハンドラを終了した場合、アプリケーションループ(通常コード)の中でイベントハンドラが呼び出されます。イベントハンドラにはhandled
のパラメータはありません。イベントハンドラは通常コードですので、比較的大きな処理を行うことが出来ます。イベントハンドラのオーバーヘッドも発生するため、頻繁な割り込み都度呼び出されるような処理の場合、処理しきれなくなる可能性があります。また、イベントの発生はシステム内部のFIFOキューにより処理されるため、一定時間内に処理できない場合はイベントが消失する場合もあります。
以下にハンドラ関数定義用のマクロの解説を行います。
DIO(ディジタルIO)割り込み・イベントです。N
は対象DIOの番号を指定します。arg
は将来の拡張のための定義です。
割り込みを発生させるためにはpinMode()
による適切な入力設定, attachDioInt()
による割り込み開始の設定が必要です。
TickTimer割り込み・イベントです。arg
は将来の拡張のための定義です。
TickTimerのhandled
フラグをtrue
にセットしてはいけません。TWENETが動作しなくなります。
タイマー割り込み・イベントです。N
は対象タイマーの番号を指定します。arg
は将来の拡張のための定義です。
割り込みを発生させるためには、Timerオブジェクトをソフトウェア割り込みを有効にして開始します。
MWXライブラリで標準的に定義しない、その他の割り込み・イベントの定義です。AHIペリフェラルマニュアルの理解が必要です。
その他の割り込み・イベントは以下のハンドラ関数で受けることが出来ます。これらは将来的に専用のハンドラが定義された場合、利用できなくなります。
ペリフェラル (AHI) の割り込みハンドラのu32DeviceId
がarg
、u32ItemBitmap
がarg2
に対応します。
状態マシン(ステートマシン)は、メッセージを受け取り、そのメッセージに応じて状態を遷移させながら動作させるアプリケーションの記述方法です。
PAL_AMB-behaviorサンプルでは、センサーの動作開始からセンサーの値取得、無線パケット送信から送信完了まで、スリープ遷移といったアプリケーションの動作の流れを記述しています。実例として参考にしてください。
受け取るイベントは以下となります。
イベント名
内容
E_EVENT_START_UP
システム始動時に呼び出される。電源投入直後はパラメータが0
で呼び出されます。実行初期であるため、通常処理を行う状態に遷移する場合は一旦begin()メソッドからPEV_Process()
を呼び出し動作を開始させます。
スリープ復帰後も呼び出されるがパラメータは0
以外です。この状態からは通常処理を行えます。
E_EVENT_NEW_STATE
状態遷移直後に新しい状態で呼び出されます。ある状態に遷移したときに最初に実行される処理を記述します。
E_EVENT_TICK_TIMER
1msごとのTickTimerで呼び出されます。
E_EVENT_TICK_SECOND
1秒毎に呼び出されます。
状態をs
に設定します。
状態ハンドラを抜けると次の状態に遷移し、続けてE_EVENTS_NEW_STATE
イベントで状態ハンドラが呼び出されます。
状態遷移してからの経過時間[ms]を返します。タイムアウトを管理するような目的で使用します。
上記の例では100ms経過した時点でシステムリセットを行います。
状態ハンドラ外から呼び出します。状態ハンドラをイベントev
パラメータu32evarg
で実行します。
送信完了イベントを状態マシンに伝えます。つまり状態ハンドラの呼び出しを行います。
直接状態ハンドラを呼び出すことは行わないでください。E_EVENT_NEW_STATE
が実行されないなどの問題が発生します。
スリープ直前に設定します。スリープ復帰後に、直前の状態を維持します。つまり、スリープを開始した状態でE_EVENT_START_UP
で状態ハンドラが呼び出されます。
イベントが起床時のE_EVENT_START_UP
かどうか判定します。
イベントがスリープ復帰時のE_EVENT_START_UP
かどうか判定します。
乱数を生成します。
1行目は0..(maxval-1)
の値を戻します。maxvalの値が最大値ではないことに注意してください。
2行目はminval..maxval-1
の値を戻します。
環境センサーパル AMBIENT SENSE PAL を用い、センサー値の取得を行います。
このアクトの解説の前にBRD_APPTWELITEの解説、PAL_AMBの解説、PAL_AMB-usenapの解説を参照してください。またビヘイビアの解説についても参照ください。
このサンプルはビヘイビアの記述方法のサンプルです。ビヘイビアはより複雑なアプリケーションを記述する際に用います。
環境センサーパル AMBIENT SENSE PAL を用い、センサー値の取得を行います。
コイン電池で動作させるための、スリープ機能を利用します。
役割
例
親機
子機
親機にPALを使用する場合は、コイン電池での動作はできません。目安として50mA以上の電流を安定して得られるような電源環境を用意してください。
PAL_AMB-behavior.hpp : setup()
のみの定義です。DIP-SWを読み出し、D1..D3が上位置の場合は親機として動作し、それ以外は子機としてDIP SWに対応するIDをセットします。
Parent/myAppBhvParent.hpp : 親機用のビヘイビアクラス定義
Parent/myAppBhvParent.cpp : 実装
Parent/myAppBhvParent-handlers.cpp : ハンドラーの実装
Parent/myAppBhvParent.hpp : 子機用のビヘイビアクラス定義
Parent/myAppBhvParent.cpp : 実装
Parent/myAppBhvParent-handlers.cpp : ハンドラーの実装
親機のビヘイビア名は<MY_APP_PARENT>
、子機は<MY_APP_CHILD>
です。
ビルドファイルの追加はMakefileの解説を参照してください。
DIP SWの読み値が0の場合は親機用のビヘイビア<MY_APP_PARENT>
を、それ以外の場合は子機用のビヘイビア<MY_APP_CHILD>
を登録します。
親機がMONOSTICKの場合は、PAL用のDIP SWの読み値は0となり、親機としてふるまいます。ただしこの動作はMONOSTICKの仕様として定義されているものではありません。
親機はスリープをしない受信機としてふるまい、子機からのパケットを受信したときにシリアルポートにパケットの情報を出力します。
親機用がパケットを受信したときは、パケットの先頭4文字が照合(FOURCHARS
)できれば、そのパケット内容を表示します。
親機の割り込みハンドラはLEDの点滅を行います。
PAL上のボタン(5)が押されたときには、状態マシンに対してE_ORDER_KICK
イベントを発行します。
状態マシンは、状態遷移の参考として記述したもので、アプリケーションの動作上意味のあるものではありません。ボタンから送付されるE_ORDER_KICKイベントによる状態遷移や、タイムアウトなどを実行しています。
子機の動作の流れはPAL_AMB-usenapと同じです。初回スリープから「起床→センサー動作開始→短いスリープ→起床→センサー値取得→無線送信→無線送信完了待ち→スリープ」を繰り返します。
on_begin()
から呼び出される_begin()
関数では、初回スリープを実行しています。
(※_begin()
関数で本処理を記述せずon_begin()
に直接記述してもかまいません)
スリープからの起床処理を記述しています。
ここで初回のWire.begin()
を実行しています。2回目以降のスリープ起床時では冗長な記述です。この処理はon_begin()
に移動してもかまいません。
送信完了時に状態マシンに対してE_ORDER_KICK
メッセージを処理します。
状態名を定義しています。
SHTC3用のセンサー取得実装例です。送付コマンド等の詳細はSHTC3のデータシートなどを参考にしてください。
LTR308ALSのセンサー取得実装例です。送付コマンド等の詳細はLTR308ALSのデータシートなどを参考にしてください。
WireWriteAndGet()
はaddr
のデバイスに対してcmd
を1バイト送信してから、1バイト受信して値を返します。
0番の状態は特別な意味を持ちます。起動直後またはスリープ復帰後の状態です。
起動直後PEV_is_coldboot(ev,evarg)
判定がtrue
になって呼び出されます。on_begin()
から、そのままスリープしてしまうため、状態遷移するようなコードも含まれません。この時点では主要な初期化がまだ終わっていませんので、無線パケットの送信など複雑な処理を行うことが出来ません。そのような処理を行うための最初の状態遷移を行うためにはon_begin()
からイベントを送り、そのイベントに従って状態遷移を行います。
スリープ復帰後はPEV_is_warmboot(ev,evarg)
がtrue
になる呼び出しが最初にあります。PEV_SetState()
を呼び出しSTATE_SENSOR
状態に遷移します。
スリープ復帰後STATE_IDLE
から遷移したとき、STATE_SENSOR
の状態ハンドラが続けて呼び出されます。この時のイベントev
はE_EVENT_NEW_STATE
です。
ここではSHTC3, LTR308ALSの2センサーの動作開始を行います。一定時間経過すれば、センサーはデータ取得可能な状態になります。この時間待ちを66
ms設定のスリープで行います。スリープ前にPEV_KeepStateOnWakeup()
が呼ばれている点に注意してください。この呼び出しを行うと、スリープ復帰後の状態はSTATE_IDLE
ではなく、スリープしたときの状態、つまりSTATE_SENSOR
となります。
短いスリープから復帰するとPEV_is_warmboot(ev,evarg)
判定がtrue
となる呼び出しが最初に発生します。この呼び出し時点で、無線パケットの送信などを行うことが出来ます。STATE_TX
に遷移します。
ここではE_EVENT_NEW_STATE
イベントの時に、センサーデータ読み出し、無線パケットの送信手続きに入ります。送信手続きの詳細は他のアクトサンプル例を参考にしてください。
送信完了まちの処理はループでのアクト記述と違い、transmit_complete()
からのPEV_Process()
によるメッセージを待つことで完了確認としています。メッセージを受け取った時点でスリープします。スリープ処理はSTATE_SLEEP
に遷移することで行っています。
最後にタイムアウト処理を行っています。万が一送信パケットの完了メッセージが戻ってこなかった場合を想定します。PEV_u32Elaspsed_ms()
はその状態に遷移してからの経過時間を[ms]で返します。時間経過した場合は、上記では(このタイムアウトは余程のことだとして)システムリセットthe_twelite.reset_system()
を行います。
スリープを行います。前の状態から遷移した直後のE_EVENT_NEW_STATE
に記述します。スリープ直前に他のイベントが呼ばれる可能性がありますので、必ず1回だけ実行される判定式の中でthe_twelite.sleep()
を実行してください。
ポーリングによる時間待ちを行います。
ms
にて与えられた期間待ち処理を行います。
時間の計測はTickTimerのカウントによって行っています。また長い時間待ちを行う場合はCPUのクロックを低下してポーリング処理を行います。
delay()
を呼び出してから約5ms経過するごとにTWELITEマイコン内部のウォッチドッグ処理を行います。
※例えばwhile(1) delay(1);
を実行した場合は、delay()
内部で5ms経過しないためウォッチドッグ処理が行われず、一定時間後リセットが実行されます。
setup(), wakeup()
関数内では、TickTimerがまだ動作していないため、whileループによる時間待ち処理になります。この場合、指定値との誤差は大きくなります。このループカウンタは32Mhzに合わせて調整しています。これら関数内でCPUクロックを変化させた場合は、そのクロックに比例した誤差が発生します。
パラメータに1,2といった短い時間を指定した場合は、誤差が大きくなる場合があります。
入力設定のポートの値を読み出す。
事前に入力に設定したピンの入力値をLOW
またはHIGH
で得ます。
E_PIN_STATE
型からint
型への変換演算子は定義していないため、数値型への直接的な代入はできません。
ディジタル出力ピンの設定を変更します。
事前にpinMode()
にて設定対象のピンを出力用に設定しておきます。1番目のパラメータは、設定対象のピン番号を指定します。2番目のパラメータはHIGH
かLOW
のいずれかを指定します。
入力が E_PIN_STATE
型となっています。E_PIN_STATE
からint
型への変換演算子は定義していませんので、数値による直接の入力はできないようになっています。
割り込みハンドラの登録を解除します。
整数から指定したビット位置の値を取得し、指定した順番のビットマップを作成します。
パラメータbmに指定する値から、その後の可変数パラメータで指定する0..31のビット位置に対応する値を取り出します。取り出した値はパラメータ順に並べビットマップとして戻り値になります。
ビットマップの並び順は、最初のパラメータを上位ビットとし末尾のパラメータがbit0になります。
例ではb1のビット4,2,1,0を取り出すと (1,0,1,0) になります。これをb1010として0x10のように計算されます。
バイト列を分解し変数に格納します。
expand_bytes()
は、パラメータにuint8_t*
型のイテレータの組み合わせを指定します。これは解析対象の先頭と末尾の次のイテレータの指定となります。e
の位置まで解析が進んだ場合はエラーとなりnullptr
を返します。
展開にエラーがない場合は、次の読み出しを行うイテレータを戻します。
可変数パラメータには以下を指定します。
この例では、まず4バイトの文字列を読み出しています。ここではmake_pair()
を用いて明示的に4バイト分のデータを読み出します。
戻されたイテレータnp
をもとに、次のデータを読み出します。次のデータはuint8_t
型、あとはuint16_t
型が5つ続いています。
指定したビット位置に1をセットします。
パラメータは可変数引数で指定でき、各パラメータはビット位置を指定する0..31の整数を指定する。例えばpack_bits(1,3,6)
と指定すると((1UL<<1)|(1UL<<3)|(1UL<<6))
を返します。
constexpr
は定数による計算が可能な場合はコンパイル時に定数展開します。
DIO割り込みを有効にします。
事前に入力設定したピンに対して、1番目のパラメータは割り込みを有効にしたいピン番号で、2番目は割り込み方向()を指定します。
割り込みハンドラ、イベントハンドラの記述はで行います。
DIO5のピンがHIGHからLOWに変化したときに割り込みが発生する設定を行う。
アプリケーションビヘイビアmyAppClass
の基本定義。詳細は省略している。
アプリケーションビヘイビアmyAppClass
の割り込みハンドラの記述。DIO5の割り込み発生時にDIO12の出力設定を反転させ、割り込みハンドラが終了してから発生するイベントではシリアルポートSerial
に*
を表示する。
+
定義
名称
const uint8_t PIN_DIGITAL::DIO0 .. 19
DIOピン0~19
const uint8_t PIN_DIGITAL::DO0 .. 1
DOピン0,1
定義
プルアップ
名称
PIN_MODE::INPUT
無
入力
PIN_MODE::OUTPUT
無
出力
PIN_MODE::INPUT_PULLUP
有
入力
PIN_MODE::OUTPUT_INIT_HIGH
無
出力(初期状態HIGH)
PIN_MODE::OUTPUT_INIT_LOW
無
出力(初期状態LOW)
PIN_MODE::WAKE_FALLING
無
入力、起床ピン、立下り
PIN_MODE::WAKE_RISING
無
入力、起床ピン、立上り
PIN_MODE::WAKE_FALLING_PULLUP
有
入力、起床ピン、立下り
PIN_MODE::WAKE_RISING_PULLUP
有
入力、起床ピン、立上り
PIN_MODE::DISABLE_OUTPUT
有
入力状態に戻す
定義
名称
PIN_MODE::OUTPUT
出力
PIN_MODE::OUTPUT_INIT_HIGH
出力(初期状態HIGH)
PIN_MODE::OUTPUT_INIT_LOW
出力(初期状態LOW)
PIN_MODE::DISABLE_OUTPUT
出力設定をやめる
定義
値
名称
PIN_STATE::HIGH
1
HIGHレベル(=Vccレベル)
PIN_STATE::LOW
0
LOWレベル(=GNDレベル)
定義
名称
PIN_INT_MODE::FALLING
立ち下り
PIN_INT_MODE::RISING
立ち上がり
バイト数
データ長
解説
uint8_t
1
uint16_t
2
ビッグエンディアン並びとして展開する
uint32_t
4
ビッグエンディアン並びとして展開する
uint8_t[N]
N
uint8_t
型の固定長配列
std::pair<char*,N>
N
char*
,uint8_t*
型の配列と配列長のペアmake_pair()
で生成できる
データ型
バイト数
解説
uint8_t
1
uint16_t
2
ビッグエンディアン並びで格納される
uint32_t
4
ビッグエンディアン並びで格納される
uint8_t[N]
N
uint8_t
型の固定長配列
std::pair<char*,N>
N
char*
,uint8_t*
型の配列と配列長のペア。make_pair()
で生成できる。
データ型
バイト数
解説
uint8_t
1
uint16_t
2
ビッグエンディアン並びで格納される
uint32_t
4
ビッグエンディアン並びで格納される
uint8_t[N]
N
uint8_t
型の固定長配列
std::pair<char*,N>
N
char*
,uint8_t*
型の配列と配列長のペア。make_pair()
で生成できる。
smplbuf_u8?
.size()
uint8_t
型のsmplbuf<>
コンテナ。コンテナ長(.size()
)のデータを格納する。
チェックサムの計算で良く用いられる値です。
CRC8, XOR, LRC(アスキー形式で使用)の計算を行います。
CRC8_u8CalcU16(), CRC8_u8CalcU32()
はu16c, u32c
をビッグエンディアン並びとして、CRC8を計算します。
CRC8は、計算式や初期値などによって種類がありますが、本ライブラリでは多項式をX^8+X^5+X^4+1
(Polynomial Valueを0x31)をとしたものを使用しています。これはCRC8-CCITT や CRC8-Maximと呼ばれることがあります。
XORは各要素の排他的論理和 XOR をとったものです。
LRCは各要素の値の合計を計算し、下位8ビットの2の補数を取ります。結果、チェックサムを含め全要素を足し算すると0になります。
Board behaviors
ボードビヘイビアは、TWELITE無線マイコンに接続したハードウェアを取り扱うための手続きが含まれます。
定数の定義(ピン番号など)
ハードウェアの初期化
センサー等の取り扱い
環境センサーパル AMBIENT SENSE PAL のボードビヘイビアです。
共通定義に加えボード上のセンサーを取り扱えるようになっています。
温湿度センサー SHTC3
照度センサー LTR308ALS
SHTC3センサーのオブジェクトです。
LTR308ALSセンサーのオブジェクトです。
TWELITE PALのハードには共通部分があり、ボードビヘイビアも共通ハードについては、共通のインタフェースを定義しています。
以下の定義が利用可能になります。
PAL_AMB::PIN_BTN
のようにアクセスできます。
上記のコードのように各ピンが初期化されます。
起動時、スリープ起床時、起動後一定時間経過後に外部のウォッチドッグタイマーを再セットします。
ウォッチドッグタイマーをタイムアウトしないためにTWELITEを60秒以内の設定(キャリブレーション済み内部CRタイマー使用時)で起床してください。
LED(D1)の制御を行います。
ボードビヘイビアでの制御を行わない場合は、このメソッドを呼び出さないでください。
mode
は以下のパラメータを取ります。tick
は点灯時間[ms]を指定しますが、詳細はmode
の解説を参照してください。
指定
意味
LED_TIMER::BLINK
LEDを点滅させます。tick
に与える時間[ms]ごとにON/OFFが切り替わります。スリープ復帰後はカウントをリセットし点灯状態から始まります。
LED_TIMER::ON_RX
パケットの受信時にtick
に与える時間[ms]だけ点灯します。
LED_TIMER::ON_TX_COMP
送信完了時にtick
に与える時間[ms]だけ点灯します。
スリープ復帰後も復帰前の設定が維持されます。
指定期間だけLEDを点灯します。set_led()
の機能と同時には使えません。
get_D1() .. get_D4()
はDIP SWがHIGH(スイッチが上)の時0
、LOW(スイッチが下)のとき1
を返します。
get_DIPSW_BM()
はDIP SWの設定値を0..15
で返します。SW1==LOW を1
, SW2 == LOWを2
, SW3 == LOWを4
, SW4 == LOWを8
とした和を返します。
D1..D4のHIGH(1),LOW(0)
の値とは反対になります。
DIP SWのLOW(0)
側がセットされた、つまり、1
の値を持つと意味付けしているためです。
この値はシステム起動時に確認されて以降は、スイッチを操作しても更新されません。
I2Cバスを利用した温湿度センサーです。
ボードビヘイビア <PAL_AMB>
を読み込んだ時のみ使用可能です。begin()
以外の共通メソッドの手続きはボードビヘイビア中で実行されています。
Wire.begin()
: バスの初期化
.begin()
: センサーの動作開始
時間待ち5ms
.get_luminance()
: 値の読み出し
begin()
メソッド呼び出し前にWire.begin()
によりWireが動作状態にしておきます。
スリープ直前もWireバスが動作状態にしておきます(スリープ復帰後自動でWireを回復します)。
温度を読み出す。get_temp()
は℃で、get_temp_cent()
は℃の100倍の値を整数値で返します。
エラー時は-32760~-32768の値が返ります。
湿度を読み出す。get_humid()
は%で、get_humid_per_dmil()
は%の100倍の値を整数値で返します。
エラー時は-32760~-32768の値が返ります。
センサー用のメモリ領域の確保や初期化を行います。
センサーの取得を開始します。センサーの値を読み出すまで約5ms待ち時間が必要です。
end()
には対応しません。
待ち時間処理のセンサーの場合はarg1
にE_EVENT_TICK_TIMER
またはE_EVENT_START_UP
を与え時間の経過を知らせます。このメソッド呼出し後に、必要な時間が経過していればavailableになり、センサー値の読み出しが可能になります。
センサーが読み出し条件を満足したときにtrue
を返します。
センサーが接続されているときにtrue
を返します。
MONOSTICK用のボードビヘイビアです。内蔵ウォッチドッグタイマーの制御とLED点灯用の手続きが含まれます。
以下の定義が利用可能になります。
MONOSTICK::PIN_LED
のようにアクセスできます。
上記のコードのように各ピンが初期化されます。
起動時、スリープ起床時、起動後一定時間経過後に外部のウォッチドッグタイマーを再セットします。
ウォッチドッグタイマーのタイムアウトは1秒です。
MONOSTICKでは通常スリープするアプリケーションを実行しませんが、その場合はMONOSTICK::PIN_WDT_EN
をHIGHにしてからスリープします。
LED(赤、黄)の制御を行います。
ボードビヘイビアでの制御を行わない場合は、このメソッドを呼び出さないでください。
黄色のLED(MONOSTICK::PIN_LED_YELLOW
)はSPIMISOピン(半導体のピン名DO1)です。本ボードビヘイビアではPWM制御による点灯用のメソッドや手続きは含まれません。必要に応じて以下の記述を行います。
set_led_yellow()
は呼び出さないようにして下さい。
始動後にPWM出力の初期化を別途行います。SPIMISOピンはApp_Twelite標準アプリケーションではPWM3に対応し、Timer3クラスオブジェクトにより制御できます。
スリープ復帰後にPWM出力の初期化を別途行います。その際、DO1の出力設定を解除します。
PWM設定前にDO1の出力設定を解除してください。
pinMode(PIN_LED_YELLOW, DISABLE_OUTPUT);
黄色のLED(MONOSTICK::PIN_LED_YELLOW
)は、スリープ中に点灯させることはできません。
mode
は以下のパラメータを取ります。tick
は点灯時間[ms]を指定しますが、詳細はmode
の解説を参照してください。
スリープ復帰後も復帰前の設定が維持されます。
標準アプリケーションApp_Tweliteと同じ配線を想定したボードビヘイビアです。定数定義と、M1-M3,BPSピンの読み出し機能があります。
以下の定数を定義しています。BRD_APPTWELITE::PIN_DI1
のようにアクセスできます。
DIP SW (M1 M2 M3 BPS) ピンの値を取得するためのメソッドが用意されています。
戻り値はHIGH, LOWではなく、0
がセットされていない(HIGH側)、1
がスイッチがセットされる(LOW側)という意味です。
get_DIPSW_BM()
は、bit0から順にM1,M2,M3,BPSピンの値を返します。
この値はシステム起動時に確認されて以降は、スイッチを操作しても更新されません。
指定
意味
LED_TIMER::BLINK
LEDを点滅させます。tick
に与える時間[ms]ごとにON/OFFが切り替わります。スリープ復帰後はカウントをリセットし点灯状態から始まります。
LED_TIMER::ON_RX
パケットの受信時にtick
に与える時間[ms]だけ点灯します。
LED_TIMER::ON_TX_COMP
送信完了時にtick
に与える時間[ms]だけ点灯します。
動作センサーパル MOTION SENSE PAL のボードビヘイビアです。
共通定義に加えボード上のセンサーを取り扱えるようになっています。
加速度センサー MC3630
MC3630センサーのオブジェクトです。
SPIバスを用いた加速度センサーです。
ボードビヘイビア <PAL_MOT>
を読み込んだ時のみ使用可能です。begin(), available()
以外の共通メソッドの手続きはボードビヘイビア中で実行されています。
.begin()
: センサーの動作開始
PIN_SNS_INT
割り込み または available()
: FIFOキューが規定数に達する
.get_que()
: FIFOキューからのデータを取得する
特にありません。
PIN_SNS_INT割り込みによる起床を行うため、スリープ前に以下の設定行います。
.wakeup()
メソッドの呼び出しが必要です。この処理は<PAL_MOT>
ボードビヘイビア中で実行されています。
半導体内部のFIFOキューが一杯になっても読み出さなかった場合は、データ取得は終了し、新たな値は格納されません。
各サンプルはaxis_xyzt
構造体を要素とするキューsmplque
に格納されます。メンバーx,y,zはそれぞれX,Y,Z軸に対応します。
各軸の値は1Gを1000とした値として格納されます。t
はサンプルの番号で0
から順番にサンプルごとに割り振られます。
半導体のFIFOキューからデータを読み出します。読みだしたバイト数が戻りますが.get_que()
で参照するキューのサイズに格納されるデータ数を読み出すようにしてください。
スリープ復帰後に<PAL_MOT>
ではread()
が行われます。
加速度のサンプルを取得します。キューはaxis_xyzt
を要素としたsmplque
です。availableになってから速やかにキューを空にする必要があります。
このセンサーではsetup()
を使用しません。
conf
で指定した設定で初期化します。
conf[0:15]
(bit0-15) : サンプリングモード、conf[16:23]
(bit16-23): 加速度のレンジ、conf[24:31]
(bit24-31) : 割り込み発生までのサンプル数を設定します。
conf[0:15] サンプルモード
内容
MODE_LP_1HZ_UNOFFICIAL
1Hz Low Power (非公式設定)
MODE_LP_2HZ_UNOFFICIAL
2Hz Low Power (非公式設定)
MODE_LP_7HZ_UNOFFICIAL
7Hz Low Power (非公式設定)
MODE_LP_14HZ
14Hz Low Power (デフォルト)
MODE_LP_28HZ
28Hz Low Power
MODE_LP_54HZ
54Hz Low Power
MODE_LP_105HZ
105Hz Low Power
MODE_LP_210HZ
210Hz Low Power
MODE_LP_400HZ
400Hz Low Power
MODE_ULP_25HZ
25Hz Ultra Low Power
MODE_ULP_50HZ
50Hz Ultra Low Power
MODE_ULP_100HZ
100Hz Ultra Low Power
MODE_ULP_190HZ
190Hz Ultra Low Power
MODE_ULP_380HZ
380Hz Ultra Low Power
非公式設定はMC3630のデータシートに記述がないもので設定時の動作は未定義となります。お客様のほうでの動作確認の上利用下さい。非公式設定にかかわる問題やご質問について弊社サポートでは対応いたしかねます。
conf[16:23] 加速度レンジ
内容
RANGE_PLUS_MINUS_8G
±8G (デフォルト)
RANGE_PLUS_MINUS_4G
±4G
RANGE_PLUS_MINUS_2G
±2G
RANGE_PLUS_MINUS_1G
±1G
このセンサーではprocess_ev()
を使用しません。
センサーにデータが読み出され内部のキューにデータが保存されているとtrue
を戻します。
このセンサーではprobe()
は使用できません。
スリープ復帰後のSPIバスの再初期化を行い、加速度データを読み出します。
開閉センサーパル OPEN-CLOSE SENSE PAL のボードビヘイビアです。
開閉センサーパルのセンサーは磁気センサーで、2本の信号線の割り込みの入力のみです。
PAL_MAG::PIN_SNS_NORTHはセンサーがN極を検出したとき、PAL_MAG::PIN_SNS_SOUTHはセンサーがN極を検出したときに割り込みが入ります。
スリープ前に以下の設定をしておきます。
起床時に起床要因のIOを確認します。
Sensor behaviors
センサーの手続きを定型化したクラスを用意しています。
センサーの取り扱い前にWire.begin()
を実施しておいてください。スリープ復帰後は、Wireの再初期化は自動で行われるため特別な記述は必要ありません(注:ユーザコード上から明示的に Wire.end()
を呼び出した場合は、再初期化を wakeup()
に記述します)
読み出し開始後の手続きはセンサーの種類ごとに違いますが例えば<PAL_AMB>
のセンサーは2つとも時間経過を管理します。時間経過をセンサーオブジェクトに伝えるには process_ev()
メソッドを用います。
上記の例では1msおきのTickTimerを起点にして時間経過を伝えています。E_EVENT_TICK_TIMER
はセンサーオブジェクトに1msの経過を伝えるものです。
スリープ復帰などで十分な時間が経過したときは替わりにE_EVENT_START_UP
を渡します。センサーオブジェクトは速やかに読み出し可能として処理されます。
いずれの処理も現実世界の時間の経過と一致することを保証しているわけではありません。実際の経過時間が不足している場合は、センサーがエラーを返したり、期待しない値を返すことになります。
センサーの初期化を行います。
センサーの取得を開始, 終了する。
待ち時間処理のセンサーの場合はarg1
にE_EVENT_TICK_TIMER
またはE_EVENT_START_UP
を与え時間の経過を知らせます。このメソッド呼出し後に、必要な時間が経過していればavailableになり、センサー値の読み出しが可能になります。
センサーが読み出し条件を満足したときにtrue
を返します。
(対応しているセンサーのみ)センサーが接続されているときにtrue
を返します。
probe()
直後の初回の通信が失敗することがある。
I2Cバスを利用した照度センサーです。
ボードビヘイビア <PAL_AMB>
を読み込んだ時のみ使用可能です。begin()
以外の共通メソッドの手続きはボードビヘイビア中で実行されています。
Wire.begin()
: バスの初期化
.begin()
: センサーの動作開始
時間待ち50ms
.get_luminance()
: 値の読み出し
.begin()
メソッド呼び出し前にWire.begin()
によりWireが動作状態にしておきます。
スリープ直前もWireバスが動作状態にしておきます(スリープ復帰後自動でWireを回復します)。
照度[lx]を整数値で返します。
エラーの時は-1
が返ります。
センサー用のメモリ領域の確保や初期化を行います。
センサーの取得を開始します。センサーの値を読み出すまで約50ms待ち時間が必要です。
end()
には対応しません。
待ち時間処理のセンサーの場合はarg1
にE_EVENT_TICK_TIMER
またはE_EVENT_START_UP
を与え時間の経過を知らせます。このメソッド呼出し後に、必要な時間が経過していればavailableになり、センサー値の読み出しが可能になります。
センサーが読み出し条件を満足したときにtrue
を返します。
センサーが接続されているときにtrue
を返します。
シンプルな中継ネットワークを実装したネットワークビヘイビアです。
上記はネットワークの利用宣言と設定例です。詳細は後述しますが、ネットワークのアドレスについての考え方など基本的な内容をまず解説します。
実際の利用例はサンプルアクトの解説をご覧ください。
このネットワークでの各無線局は8bitの論理IDで識別されます。この値は起動時に各無線局が独自に設定します。論理IDは重複しても構いませんが、重複したことを前提とした通信を行う必要があります。
各無線局のIDを設定します。通常はネットワークには親機の役割の無線局と子機の役割の無線局を配置します。子機のみのネットワークも運用できます。
また子機は中継機の役割にもなります。
無線局の識別ID
役割
0x00
親局
0x01
..0xEF
子局
0xFE
IDを割り振らない子局
論理IDを宛先として指定できますが、0xFE
,0xFF
は特別な意味を持ちます。下表に宛先指定についてまとめます。
宛先ID
意味
0x00
子局から親局を指定する。親局からの指定は無効。
0x01
..0xEF
特定の子局を指定する。
0xFE
すべての子局を指定する同報通信(ブロードキャスト)。
0xFF
すべての無線局を指定する同報通信(ブロードキャスト)。
また、無線局を特定するのに32bitで指定するシリアル番号も利用できます。
パケットの配送は、IEEE802.15.4のブロードキャストを用います。ACKを用いないため、配送の成功が送信元では判別できませんが、替わりに要求に合った成功率が得られる適当な再送回数を設定したうえ、到達確認が必要な場合は通常パケットの通信を用います。
大規模で頻繁な通信を行う場合は非効率に見えるかもしれませんが、もっぱらデータ収集のみを行い、比較的中継段数が少ないネットワークの場合などより効率的になる場合もあります。
またネットワークを構築するための通信を必要としないため、障害等例外的な状況においても全く通信が止まってしまうといったことが原理的に少なくなります。親機が受信状態かつ子機からの無線到達範囲にあって、子機がパケットを送信しさえすれば、多くの場合親機は受信できます。ネットワークの構築のために通信が必要なネットワークでは、一旦、設定情報などが失われた後は、再度親機と子機間の通信確立のための通信が完了しなければデータが送れません。ネットワークビヘイビア<NWK_SIMPLE>
がその命名にシンプルとしているのは、こういった理由があります。
このシンプルネットワークを動作させるためには、多くの場合複数回届く再送パケット(同一パケット)を無視する必要があります。<NWK_SIMPLE>
での同一パケットの識別は送信元のシリアル番号と送信時のパケットの続き番号によって行います(重複チェッカと呼びます)。続き番号は0..63として、順番に割り当てられ、近い時間に届くパケットの続き番号は近い番号であるという仮定を置いています。一定時間以上経過した番号的に遠い(今10番を受信したとしたら40番台のパケットはしばらく前に送信されたと考えられる)続き番号はタイムアウトとして重複対象から除外します。
重複チェッカでの考慮すべきことは以下になります。
一般的な使用条件(例:中継段数が3以下で1秒あたりのパケットも10局未満から到達)では、特別な考慮は不要です。
チェック可能な要素数(数を増やせばメモリ消費とチェックのための処理時間が増える)
タイムアウト時間の設定
デフォルトではタイムアウトは1秒で、チェックする無線局の数は16です。つまり中継パケットがまわりまわって1秒以上経過した場合、重複パケットとみなされななくなります。また短期的に16を超える無線局からのパケットが到達した場合、超過した無線局については重複チェックが出来なくなります。
中継段数や段数が少なくとも中継局の数が多い場合、再送を非常に長い間隔で行う場合は、設定を考慮すべき場合もあります。
ネットワークビヘイビア<NWK_SIMPLE>
を利用例を挙げます。
2行目で<NWK_SIMPLE>
の定義情報をインクルードします。7行目でthe_twelite
に<NWK_SIMPLE>
を登録します。
<NWK_SIMPLE>
登録後に設定を行います。
設定は<<
演算子で行います。
<<
演算子 (設定)オブジェクトthe_twelite
の初期設定を行うために<<
演算子を用います。
以下に挙げる設定用のクラスオブジェクトを入力とし、設定をしなければデフォルト値が適用されます。
パラメータid
に指定した論理デバイスIDを設定します。デフォルトは0xFE
(ID未設定子機)です。
パラメータval
に指定した回数を最大中継回数とします。デフォルトは2
です。
中継をさせたくない場合は0
を指定します。
受信回路を動作させない設定をした場合、TWELITE無線マイコンが無線パケットを受信できないため、必然的に、中継動作は行われなくなります。
重複パケットの検出アルゴリズムのパラメータです。
中継段数が多かったり、パケットの再送間隔が長い場合は考慮が必要ですが、一般的な利用での設定変更は必要ありません。
maxnodes
は履歴を保持するため無線局(ノード)の数です。ノード数を少なく設定した場合、短期間に設定以上のノードからのパケットが来た場合、重複除外できないノードが出てきます。重複除外できない場合、受信時に複数回データが表示される、必要以上に再中継してしまうといった問題が出ます。デフォルトは16
です。1ノード当たり21バイトメモリを消費します。
timeout_ms
は履歴を抹消するまでのタイムアウト時間[ms]です。タイムアウトは続き番号のブロック単位で管理されていて、ブロック単位でタイムアウト処理が行われます。デフォルトは1000
[ms]です。
tickscale
はタイムアウトを管理するための時間単位で2^tickscale
[ms]となります。時間は7bitで管理されるため、127*(2^tickscale) > timeout_ms
になるように設定します。デフォルトは5
(32ms)です。
送信オブジェクトを取得します。オブジェクトはpacket_tx
の派生クラスになります。このオブジェクトに送信アドレスやペイロードを格納し.transmit()
メソッドで送信を行います。
このオブジェクトにはbool
演算子が定義されています。オブジェクト生成時にTWENETが送信要求を受け付けられない場合はfalse
を返します。
.prepare_tx_packet()
メソッドにて取得した送信オブジェクトのメソッドです。
オブジェクト生成時にTWENETが送信要求を受け付けられない場合はfalse
を返します。
パケットの送信処理を行います。MWX_APIRET
がtrue
の場合に送信要求が成功ですが、この要求時点では送信処理が始まりません。
パケットの送信IDはMWX_APIRET
の.get_value()
で得られる値部に格納されます。the_twelite.x_status.is_complete()
またはtransmit_complete()
より送信完了を確認できます。