M
M
MWX Library
検索…
PAL_MOT-single
このアクトではスリープ復帰後に数サンプル加速度データを取得しそのデータを送ります。
のアクトには以下が含まれます。
  • 無線パケットの送受信
  • インタラクティブモードによる設定 - <STG_STD>
  • ステートマシンによる状態遷移制御 - <SM_SIMPLE>
  • <PAL_MOT>または<CUE>ボードビヘイビアによるボード操作

アクトの解説

起床→加速度センサーの取得開始→加速度センサーのFIFO割り込み待ち→加速度センサーのデータの取り出し→無線送信→スリープという流れになります。
加速度センサーは、FIFOキューが一杯になるとFIFOキューへのデータ追加を停止します。

宣言部

インクルード

1
#include <TWELITE> // MWXライブラリ基本
2
#include <NWK_SIMPLE> // ネットワーク
3
#include <SM_SIMPLE> // ステートマシン(状態遷移)
4
#include <STG_STD> // インタラクティブモード
5
6
/*** board selection (choose one) */
7
#define USE_PAL_MOT
8
//#define USE_CUE
9
// board dependend definitions.
10
#if defined(USE_PAL_MOT)
11
#define BRDN PAL_MOT
12
#define BRDC <PAL_MOT>
13
#elif defined(USE_CUE)
14
#define BRDN CUE
15
#define BRDC <CUE>
16
#endif
17
// include board support
18
#include BRDC
Copied!
MOT PALまたはTWELITE CUEに対応するため、インクルード部分はマクロになっています。USE_PAL_MOTまたは、USE_CUEのいずれかを定義します。
USE_PAL_MOT が定義されている場合は動作センサーパルのボードビヘイビア<PAL_MOT>をインクルードしています。

状態定義

1
enum class E_STATE : uint8_t {
2
INTERACTIVE = 255,
3
INIT = 0,
4
START_CAPTURE,
5
WAIT_CAPTURE,
6
REQUEST_TX,
7
WAIT_TX,
8
EXIT_NORMAL,
9
EXIT_FATAL
10
};
11
SM_SIMPLE<E_STATE> step;
Copied!
loop()中の順次処理を行うために状態を定義し、またステートマシンstepを宣言します。

センサーデータ格納

1
struct {
2
int32_t x_ave, y_ave, z_ave;
3
int32_t x_min, y_min, z_min;
4
int32_t x_max, y_max, z_max;
5
uint16_t n_seq;
6
uint8_t n_samples;
7
} sensor;
Copied!
センサーデータを格納するためのデータ構造です。

setup()

1
/// load board and settings objects
2
auto&& brd = the_twelite.board.use BRDC (); // load board support
3
auto&& set = the_twelite.settings.use<STG_STD>(); // load save/load settings(interactive mode) support
4
auto&& nwk = the_twelite.network.use<NWK_SIMPLE>(); // load network support
Copied!
ボード、設定、ネットワークの各ビヘイビアオブジェクトの登録を行います。

インタラクティブモード

1
// settings: configure items
2
set << SETTINGS::appname("MOT");
3
set << SETTINGS::appid_default(DEFAULT_APP_ID); // set default appID
4
set << SETTINGS::ch_default(DEFAULT_CHANNEL); // set default channel
5
set << SETTINGS::lid_default(0x1); // set default LID
6
set.hide_items(E_STGSTD_SETID::OPT_DWORD2, E_STGSTD_SETID::OPT_DWORD3, E_STGSTD_SETID::OPT_DWORD4, E_STGSTD_SETID::ENC_KEY_STRING, E_STGSTD_SETID::ENC_MODE);
7
8
// if SET=LOW is detected, start with intaractive mode.
9
if (digitalRead(brd.PIN_SET) == PIN_STATE::LOW) {
10
set << SETTINGS::open_at_start();
11
brd.set_led(LED_TIMER::BLINK, 300); // slower blink
12
step.next(STATE::INTERACTIVE);
13
return;
14
}
15
16
// load settings
17
set.reload(); // load from EEPROM.
18
OPT_BITS = set.u32opt1(); // this value is not used in this example.
Copied!
インタラクティブモードの初期化を行います。
まず、設定項目の調整を行います。ここでは、メニュー項目で表示されるタイトル名SETTINGS::appname、アプリケーションIDのデフォルト値の設定SETTINGS::appid_default、チャネルのデフォルトSETTINGS::ch_default、論理デバイスIDのデフォルトSETTINGS::lid_default、非表示項目の設定.hide_items()を行います。
このサンプルでは起動時にSETピンがLOである場合にインタラクティブモードに遷移します。digitalRead(brd.PIN_SET)によりピンがLOであることを確認できた場合は、SETTINGS::open_at_start()を指定します。この指定によりsetup()を抜けた後に速やかにインタラクティブモード画面が表示されます。画面が表示されてもbegin()loop()が実行されます。このサンプルでは状態STATE::INTERACTIVEとしてloop()中ではスリープなどの動作はせず何もしないようにします。
続いて設定値を読み出します。設定値を読むには必ず.reload()を実行します。このサンプルではオプションビット設定.u32opt1()を読み出します。

the_twelite

1
the_twelite << set;
Copied!
the_tweliteは、システムの基本的な振る舞いを管理するクラスオブジェクトです。このオブジェクトは、setup()内でアプリケーションIDやチャネルなど様々な初期化を行います。
ここではインタラクティブモードの設定値の一部を反映しています。
インタラクティブモード設定で反映した項目を別の設定に変更したい場合は、続いて上書きしたい設定を行います。
1
the_twelite << set;// インタラクティブモード
2
the_twelite << twenet::channel(19); // chを19に上書き設定
Copied!

NWK_SIMPLEオブジェクト

1
nwk << set;
Copied!
ネットワークビヘイビアオブジェクトに対しても設定を行います。インタラクティブモードの論理デバイスID(LID)と再送設定が反映されます。

その他、ハードウェアの初期化など

1
brd.set_led(LED_TIMER::BLINK, 100);
Copied!
LEDのブリンク設定などを行います。

begin()

1
void begin() {
2
auto&& set = the_twelite.settings.use<STG_STD>();
3
if (!set.is_screen_opened()) {
4
// sleep immediately, waiting for the first capture.
5
sleepNow();
6
}
7
}
Copied!
setup()を終了した後に呼ばれます。ここでは初回スリープを実行しています。ただしインタラクティブモードの画面が表示される場合はスリープしません。

wakeup()

1
void wakeup() {
2
Serial << crlf << "--- PAL_MOT(OneShot):"
3
<< FOURCHARS << " wake up ---" << crlf;
4
eState = E_STATE::INIT;
5
}
Copied!
起床後は状態変数eStateを初期状態INITにセットしています。この後loop()が実行されます。

loop()

1
void loop() {
2
auto&& brd = the_twelite.board.use<PAL_MOT>();
3
4
do {
5
switch(step.state()) {
6
case STATE::INTERACTIVE:
7
break;
8
...
9
} while(step.b_more_loop());
10
}
Copied!
loop() の基本構造は<SM_STATE>ステートマシンstateを用いswitch ... case節での制御です。初期状態はSTATE::INITまたはSTATE::INTERACTIVEです。

STATE::INTERACTIVE

インタラクティブモード画面が表示されているときの状態です。何もしません。この画面ではSerialの入出力はインタラクティブモードが利用します。

STATE::INIT

初期状態のINITです。
1
case STATE::INIT:
2
brd.sns_MC3630.get_que().clear(); // clear queue in advance (just in case).
3
memset(&sensor, 0, sizeof(sensor)); // clear sensor data
4
step.next(STATE::START_CAPTURE);
5
break;
Copied!
状態INITでは、初期化(結果格納用のキューのクリア)や結果格納用のデータ構造の初期化を行います。STATE::START_CAPTUREに遷移します。この遷移設定後、もう一度whileループが実行されます。

STATE::CAPTURE

1
case STATE::START_CAPTURE:
2
brd.sns_MC3630.begin(
3
// 400Hz, +/-4G range, get four samples and will average them.
4
SnsMC3630::Settings(
5
SnsMC3630::MODE_LP_400HZ, SnsMC3630::RANGE_PLUS_MINUS_4G, N_SAMPLES));
6
7
step.set_timeout(100);
8
step.next(STATE::WAIT_CAPTURE);
9
break;
Copied!
状態START_CAPTUREでは、MC3630センサーのFIFO取得を開始します。ここでは400Hzで4サンプル取得できた時点でFIFO割り込みが発生する設定にしています。
例外処理のためのタイムアウトの設定と、次の状態STATE::WAIT_CAPTUREに遷移します。

STATE::WAIT_CAPTURE

1
case STATE::WAIT_CAPTURE:
2
if (brd.sns_MC3630.available()) {
3
brd.sns_MC3630.end(); // stop now!
Copied!
状態WAIT_CAPTUREでは、FIFO割り込みを待ちます。割り込みが発生し結果格納用のキューにデータが格納されるとsns_MC3630.available()trueになります。sns_MC3630.end()を呼び出し処理を終了します。
1
sensor.n_samples = brd.sns_MC3630.get_que().size();
2
if (sensor.n_samples) sensor.n_seq = brd.sns_MC3630.get_que()[0].get_t();
3
...
Copied!
サンプル数とサンプルのシーケンス番号を取得します。
1
// get all samples and average them.
2
for (auto&& v: brd.sns_MC3630.get_que()) {
3
sensor.x_ave += v.x;
4
sensor.y_ave += v.y;
5
sensor.z_ave += v.z;
6
}
7
8
if (sensor.n_samples == N_SAMPLES) {
9
// if N_SAMPLES == 2^n, division is much faster.
10
sensor.x_ave /= N_SAMPLES;
11
sensor.y_ave /= N_SAMPLES;
12
sensor.z_ave /= N_SAMPLES;
13
}
14
...
Copied!
すべてのサンプルデータに対して読み出し、平均値をとる処理をします。
1
// can also be:
2
// int32_t x_max = -999999, x_min = 999999;
3
// for (auto&& v: brd.sns_MC3630.get_que()) {
4
// if (v.x >= x_max) x_max = v.x;
5
// if (v.y <= x_min) x_min = v.x;
6
// ...
7
// }
8
auto&& x_minmax = std::minmax_element(
9
get_axis_x_iter(brd.sns_MC3630.get_que().begin()),
10
get_axis_x_iter(brd.sns_MC3630.get_que().end()));
11
sensor.x_min = *x_minmax.first;
12
sensor.x_max = *x_minmax.second;
13
...
Copied!
ここでは取得されたサンプルに対して、各軸に対応するイテレータを用い最大・最小を得ています。
C++ Standard Template Library のアルゴリズムを使用する例としてstd::mimmax_element紹介していますが、コメント内のようにforループ内で最大、最小を求めても構いません。
1
if (brd.sns_MC3630.available()) {
2
...
3
brd.sns_MC3630.get_que().clear(); // clean up the queue
4
step.next(STATE::REQUEST_TX); // next state
5
} else if (step.is_timeout()) {
6
Serial << crlf << "!!!FATAL: SENSOR CAPTURE TIMEOUT.";
7
step.next(STATE::EXIT_FATAL);
8
}
9
break;
Copied!
.sns_MC3630.get_que().clear()を呼び出し、キューにあるデータをクリアします。これを呼び出さないと続くサンプル取得ができません。その後STATE::REQUEST_TX状態に遷移します。
.is_timeout()はタイムアウトをチェックします。タイムアウト時は異常としてSTATE::EXIT_FATALに遷移します。

STATE::REQUEST_TX

1
case STATE::REQUEST_TX:
2
if (TxReq()) {
3
step.set_timeout(100);
4
step.clear_flag();
5
step.next(STATE::WAIT_TX);
6
} else {
7
Serial << crlf << "!!!FATAL: TX REQUEST FAILS.";
8
step.next(STATE::EXIT_FATAL);
9
}
10
break;
Copied!
状態REQUEST_TXではローカル定義関数TxReq()を呼び出し、得られたセンサーデータの処理と送信パケットの生成・送信を行います。送信要求は送信キューの状態などで失敗することがあります。送信要求が成功した場合、TxReq()はtrueとして戻りますが、まだ送信は行われません。送信完了はon_tx_comp()コールバックが呼び出されます。
また.clear_flag()により送信完了を知らせるためのフラグをクリアしておきます。同時にタイムアウトも設定します。

E_SATE::WAIT_TX

1
case STATE::WAIT_TX:
2
if (step.is_flag_ready()) {
3
step.next(STATE::EXIT_NORMAL);
4
}
5
if (step.is_timeout()) {
6
Serial << crlf << "!!!FATAL: TX TIMEOUT.";
7
step.next(STATE::EXIT_FATAL);
8
}
9
break;
Copied!
状態STATE::WAIT_TXでは、無線パケットの送信完了を待ちます。フラグはon_tx_comp()コールバック関数でセットされ、セット後に.is_flag_ready()trueになります。

E_SATE::EXIT_NORMAL, FATAL

1
case STATE::EXIT_NORMAL:
2
sleepNow();
3
break;
4
5
case STATE::EXIT_FATAL:
6
Serial << flush;
7
the_twelite.reset_system();
8
break;
Copied!
一連の動作が完了したときは状態STATE::EXIT_NORMALに遷移しローカル定義の関数sleepNow()を呼び出しスリープを実行します。またエラーを検出した場合は状態STATE::EXIT_FATALに遷移し、システムリセットを行います。

MWX_APIRET TxReq()

1
MWX_APIRET TxReq() {
2
auto&& brd = the_twelite.board.use<PAL_MOT>();
3
MWX_APIRET ret = false;
4
5
// prepare tx packet
6
if (auto&& pkt = the_twelite.network.use<NWK_SIMPLE>().prepare_tx_packet()) {
7
// set tx packet behavior
8
pkt << tx_addr(0x00) // 0..0xFF (LID 0:parent, FE:child w/ no id, FF:LID broad cast), 0x8XXXXXXX (long address)
9
<< tx_retry(0x1) // set retry (0x1 send two times in total)
10
<< tx_packet_delay(0, 0, 2); // send packet w/ delay
11
12
// prepare packet (first)
13
pack_bytes(pkt.get_payload() // set payload data objects.
14
, make_pair(FOURCHARS, 4) // just to see packet identification, you can design in any.
15
, uint16_t(sensor.n_seq)
16
, uint8_t(sensor.n_samples)
17
, uint16_t(sensor.x_ave)
18
, uint16_t(sensor.y_ave)
19
, uint16_t(sensor.z_ave)
20
, uint16_t(sensor.x_min)
21
, uint16_t(sensor.y_min)
22
, uint16_t(sensor.z_min)
23
, uint16_t(sensor.x_max)
24
, uint16_t(sensor.y_max)
25
, uint16_t(sensor.z_max)
26
);
27
28
// perform transmit
29
ret = pkt.transmit();
30
31
if (ret) {
32
Serial << "..txreq(" << int(ret.get_value()) << ')';
33
}
34
}
35
36
return ret;
37
}
Copied!
最期にパケットの生成と送信を要求を行います。パケットには続き番号、サンプル数、XYZの平均値、XYZの最小サンプル値、XYZの最大サンプル値を含めます。

sleepNow()

1
void sleepNow() {
2
Serial << crlf << "..sleeping now.." << crlf;
3
Serial.flush();
4
step.on_sleep(false); // reset state machine.
5
the_twelite.sleep(3000, false); // set longer sleep (PAL must wakeup less than 60sec.)
6
}
Copied!
スリープの手続きです。
  • シリアルポートはスリープ前にSerial.flush()を呼び出してすべて出力しておきます。
  • <SM_SIMPLE>ステートマシンはon_sleep()を行う必要があります。
最終更新 1mo ago