# The MWX Library

Mono Wireless C++ Library for TWELITE.

{% hint style="success" %}
[資料の取り扱いについて](https://twelite.gitbook.io/general/about_documents)をご参照ください。  お気付きの点がありましたら、当サポート窓口にご連絡いただければ幸いです。
{% endhint %}

{% hint style="warning" %}
このページには 0.1.3 (MWSDK2020\_05) の情報が含まれます。
{% endhint %}

MWX ライブラリは、TWELITE 無線モジュールのコード表記を簡素化することを目的としています。MWXで作成されたプログラムを**アクト** act と呼びます。アクトにはループによる記述と、イベントによる記述（**ビヘイビア** behavior と呼びます）の二種類があります。

本ページではアクトの主な特徴を紹介します。

* ループによる記述 (`setup(), loop()`)。小規模な機能を記述するのに向いています。

```cpp
#include <TWELITE>
const uint8_t PIN_LED = 5;

void setup() {
  pinMode(PIN_LED, OUTPUT);
}

void loop() {
  if (TickTimer.available()) {
    uint32 t_now = millis();
    
    // blink LED every 1024ms
    digitalWrite(PIN_LED, (t_now >> 10) & 1 ? HIGH : LOW);
  }
}	
```

* イベントドリブンのアプリケーション記述。各種イベント・割り込みハンドラの定義、アプリケーションの複雑な振る舞いを記述するのに向いたステートマシンをクラス内に定義して見通しの良いコードを記述できます。この記述を**ビヘイビア**と呼びます。

```cpp
// myApp.hpp
...
class myApp : MWX_APPDEFS_CRTP(myApp) {
...
  void loop() {
    // main loop
  }
  
  void receive(mwx::packet_rx& rx) {
    // on receive
  }
};

// myApp.cpp
...
MWX_DIO_EVENT(12, uint32_t arg) {
		// on event from DIO12
}
```

* ペリフェラルの手続きを簡素化。よく使われる UART, I2C, SPI, ADC, DIO, タイマー, パルスカウンタを取り扱うクラスオブジェクトを定義しています。

```cpp
void loop() {
  while(Serial.available() { 
    auto x = Serial.read(); ... } // serial message
  if (Analogue.available() {
    auto x = Analogue.read(...); } // adc values
  if (Buttons.available() { 
    Buttons.read(...); } // DIO changes
  if (the_twelite.receiver.available()) { 
    auto&& rx = the_twelite.receiver.read(); } // on rx packet
}
```

* シンプルな中継ネットワークを定義。この中継ネットワークは TWELITE 標準アプリケーションと同等の実装で、個体アドレスの管理は 8bit の論理IDで行うこと、ネットワーク構築のための通信を行わないため電源投入後すぐにネットワーク宛に無線パケットを送ることができる点が特徴です。

```cpp
#include <TWELITE>
#include <NWK_SIMPLE>

void setup() {
  ...
  auto&& nwksmpl = the_twelite.network.use<NWK_SIMPLE>();
	nwksmpl << NWK_SIMPLE::logical_id(0xFE) 
	           // set Logical ID. (0xFE means a child device with no ID)
	        << NWK_SIMPLE::repeat_max(3);
	           // can repeat a packet up to three times.
}

void loop() {
  ...
  vTransmit();
  ...
}

void vTransmit() {
  if (auto&& pkt =
    the_twelite.network.use<NWK_SIMPLE>().prepare_tx_packet(); 
  pkt << tx_addr(0x00)  // to parent 
	  	<< tx_retry(0x3); // set retry
	
	pack_bytes(pkt.get_payload() // prepare payload data
	    , uint8_t(0x01)
	    , uint16_t(analogRead(PIN_ANALOGUE::A1))
	    , uint16_t(analogRead_mv(PIN_ANALOGUE::VCC)));
	
	pkt.transmit(); // transmit!
}
```

{% hint style="info" %}
TWELITE 標準アプリケーションとのパケットの相互通信はできませんが、以下の点で自由度が上がっています。

* 論理IDは 0 が親機である点は同様ですが、子機アドレスとして 0x01..0xEF を利用できるため、識別数を 200 以上とすることができます。

* 原則３回までとしていた中継回数について、最大数を64回までを設定できるようにしています。（※ パケットが遠回りして一定時間経過後に戻ってきた場合、重複パケットの管理テーブルがクリアされ中継済みのパケットであっても、再中継が発生する場合があります。中継回数を大きくする設定は注意してください）
  {% endhint %}

* PAL や MONOSTICK 向けのボード定義。ボード上のセンサーなどを容易に取り扱えます。

```cpp
#include <TWELITE>
#include <PAL_AMB> // include the board support of PAL_AMB

void setup() {
	auto&& brd = the_twelite.board.use<PAL_AMB>(); // use PAL AMB
	uint8_t u8dip = brd.get_DIP_SW();   // check DIP s/w status
	brd.set_led(LED_TIMER::BLINK, 100); // LED switchs on/off every 100ms
	...
	
	// start capture of sensors
	brd.sns_SHTC3.begin();
}

void loop() {
	if (TickTime.available()) { // check every ms
		auto&& brd = the_twelite.board.use<PAL_AMB>();

		if (brd.sns_LTR308ALS.available()) {
		  Serial << brd.sns_SHTC3..get_temp();
		} else {
		  // notify sensor that 1ms passed.
			brd.sns_SHTC3.process_ev(E_EVENT_TICK_TIMER);
		}
	}
}
```


# 改版履歴

{% hint style="info" %}
軽微な修正にあたるものは、本改版履歴には記載を行わずGitHub上の改版のみとなります。必要に応じて修正を引用ください。
{% endhint %}

## 更新方法

TWELITE STAGE の配布パッケージリリース後の修正・追加分などはGitHubレポジトリに格納しております。必要に応じて配布パッケージの位置を差し替えて利用いただくようお願いいたします。

MWSDKの他の更新が必要になる場合があります。更新時のリリース記述を参照してください。MWSDKの更新については[こちら](https://sdk.twelite.info/latest)を参照ください。

### MWXライブラリコードの更新方法

ライブラリのソースコードは GitHub (<https://github.com/monowireless/mwx>)にて公開しています。ライブラリのソースコードの差し替えは、以下の手順で行ってください。

1. 各リリースのリンクよりGitのクローンを行うか zip 形式でソースコードをダウンロードします。
2. 以下のディレクトリの内容を差し替えます。

```
.../MWSTAGE/              --- TWELITE STAGE 配布ディレクトリ
        .../MWSDK         --- MWSDKディレクトリ
              .../TWENET/current/src/mwx <--　このディレクトリを差し替える
```

## 0.1.3 - 2020-05-29

MWSDK2020\_05 に対応

* 重複チェッカ duplicate\_checker の初期化等に不備があり期待通りの除去を行っていなかった
* format() の実装を機種依存の少ないものとした。また、引数を最大８までとした。64bit引数が含まれる場合は引数の数は制限される。

> <https://github.com/monowireless/mwx/releases/tag/0.1.3>

{% hint style="warning" %}
修正は MWSDK2020\_05 を前提としています。

本修正については、更新を推奨します。
{% endhint %}

## 0.1.2 - 2020-04-24

MWSDK2020\_04 に対応

* Timer0..4の初期化の問題を修正
* mwx::format() の内部処理を変更
* インタラクティブモード対応のための実験的なコードの追加

> <https://github.com/monowireless/mwx/releases/tag/0.1.2>

{% hint style="warning" %}
本修正は MWSDK2020\_04 を前提としています。

本修正については、更新を推奨します。
{% endhint %}

## 0.1.1 - 2020-02-28

パケット内の中継フラグの扱いについての問題を修正

> <https://github.com/monowireless/mwx/releases/tag/0.1.1>

{% hint style="warning" %}
本修正については、更新を推奨します。
{% endhint %}

## 0.1.0 - 2019-12-23

初版リリース (SDL 2019/12月号収録)

> <https://github.com/monowireless/mwx/releases/tag/0.1.0>


# MWXライブラリについて

MWX ライブラリは、TWELITE モジュールのプログラムをより容易にかつ拡張性を高めるために設計されています。これまでMWSDKで利用していた TWENET C ライブラリを基本とし、MWXライブラリはアプリケーション開発層のライブラリとして開発しております。

```
   Act (USER APPs)... 
+-----------------------+
| MWX C++ LIB           |
+---------------+       |
| TWENET C LIB  |       |
+------------+----------+
| MAC LAYER  | AHI APIs |
+-----------------------+
| TWELITE HARDWARE      |
+-----------------------+
```

MWX ライブラリの名称は Mono Wireless C++ Library for TWELITE です。MW は MonoWireless から、また C++ -> CXX -> double X -> WX。この MW と WX を重ねて MWX になりました。

このライブラリを用いて記述したコードを「**アクト(act)**」と呼びます。

###

### 表記等について

本解説での表記について記載します。

#### auto&&

ユニバーサル参照と呼ばれ、標準ライブラリなどで良く用いられます。当ライブラリでもほとんどの場合`auto&&` と記載します。

{% hint style="info" %}
`auto` はC言語ではローカル変数（自動変数）を宣言する際のキーワードとなっていますが、ここでは型推論により宣言を行う意味です。C++のテンプレート構文では非常に煩雑な型名になることが多く、同時に型名を明示的に記述しなくても実装できる場面で便利な記法です。

下記の例では、`v`の型に対する最大値最小値を発見する標準ライブラリのアルゴリズム `std::minmax_element` を用いた例で、結果の戻り値を `auto`により宣言しています。この場合、autoで推論された型は`std::pair<int, int>`になります。

```cpp
#include <algorithm>
int v[] = { 1, 3, -1, 5, 10 };
auto&& result = std::minmax_element(std::begin(v), std::end(v));
Serial << "min=" << int(result.first)
       << ",max=" << int(result.second);
```

`auto &&`の`&&`ついては、厳格な意味合いは専門書籍などを紐解いていただく必要がありますが、ここでは「戻りが参照型（C言語でいうポインタ渡しに近い）であっても値であっても、気にせず宣言できる」とお考え下さい。
{% endhint %}

#### 名前空間について

`namespace, inline namespace, using` を用いて、名前の再定義などを行っています。解説中でも一部省略して記載しています。

### 制限事項 (TWENET)

MWXライブラリは、下層に位置する各ライブラリ・機能（TWNET Cライブラリでの機能、また半導体ベンダが提供するマイコン・ペリフェラル機能、IEEE802.15.4の機能）について、その全てに対応する目的では開発しておりません。

### 制限事項 (C++の利用)

MWXライブラリはC++言語で記述されておりアクトの記述においても C++ での記述を行うことになります。しかしながらC++言語であってもすべての機能が使えるわけではありません。特に以下の点に注意してください。

* `new, new[]` 演算子でのメモリ確保は行えますが、確保したメモリを破棄することはできません。C++ライブラリで動的メモリ確保をするものは殆どが事実上利用不可能です。
* グローバルオブジェクトのコンストラクタが呼び出されません。\
  参考：必要な場合は、初期化関数(`setup()`) で `new ((void*)&obj_global) class_foo();` のように初期化することでコンストラクタの呼び出しを含めた初期化を行えます。
* 例外 `exception`が使用できません。
* 仮想関数 `virtual` が使用できません。
* 上記の制約があるためSTLなどC++標準ライブラリの一部のみの利用となります。

※ 当社で把握しているものについての記載です。

{% hint style="warning" %}
標準ライブラリについては利用可否、また利用できそうなものについての包括的な検証は行っておりません。動作の不都合が確認できた場合は、別の方法で実装するようにしてください。
{% endhint %}

### ライブラリのソースコードについて

ソースコードは以下から参照できます。

* `{MWSDKインストールディレクトリ}/TWENET/current/src/mwx`
* [`https://github.com/monowireless/mwx`](https://github.com/monowireless/mwx)


# License

## 保証・ライセンス

本パッケージ内で、ライセンス上特別な記述のないものは、[モノワイヤレスソフトウェア使用許諾契約書](https://mono-wireless.com/jp/products/TWE-NET/license.html)(MW-SLA)を適用します。

本ドキュメントについても、本ライブラリパッケージ部の一部としてMW-SLA下の取り扱いとします。

本ソフトウェアについては、モノワイヤレス株式会社が正式にサポートを行うものではありません。お問い合わせにはご回答できない場合もございます。予めご了承ください。

不具合などのご報告に対してモノワイヤレス株式会社は、修正や改善をお約束するものではありません。

また導入パッケージなどお客様の環境に依存して動作しない場合もございます。

```python
# Copyright (C) 2019 Mono Wireless Inc. All Rights Reserved.
# Released under MW-SLA-*J,*E (MONO WIRELESS SOFTWARE LICENSE
# AGREEMENT)
```


# 用語

本資料で利用する用語について補足します。

{% hint style="warning" %}
用語の解説は、規格などで定められる定義に沿っていない場合があります。
{% endhint %}

## 一般的な用語

### SDK (TWELITE SDK, MWSDK)

ソフトウェア開発環境

{% hint style="info" %}
TWELITE無線マイコンのソフトウェア開発用のSDKをTWELITE SDK (またはMWSDK)と呼称します。
{% endhint %}

### IEEE802.15.4

TWELITE無線モジュールが利用する無線規格です。MWXライブラリを使用する限り、無線規格の詳細を意識する必要はありません。

### パケット

無線通信における最小の通信単位です。

最大量は通信方式や通信時の設定によって変わりますが、MWXライブラリ標準の通信\<NWK\_SIMPLE>では、ユーザが１パケットで送信できるデータ量は９０バイトです。

### ペイロード

「貨物」といった意味合いですが、無線パケットに含まれるデータ本体のことをいいます。

### ノード

「点・節」といった意味合いですが、無線ネットワーク内の無線局のことを言います。

## MWXライブラリ特有の用語

### アクト

本ライブラリを用いて作成したプログラム。そのソースコードまたは動作するプログラムのことを言います。

### [ビヘイビア](/v0.1.3/api-reference/behavior)

アクトの中でも特にイベント形式のプログラム。そのソースコードまたは動作するプログラムのことを言います。

ビヘイビアは１つのクラス定義による記述で、TWENETからのコールバック関数やイベントや割り込み処理を記述しひとまとめにしています。MWXライブラリでは以下の３種類のビヘイビアがあります。

* アプリケーションビヘイビア：イベントドリブンでのアプリケーション記述を行い、ユーザが定義するクラス。
* ボードビヘイビア：TWELITE無線モジュールを実装するボードの機能利用を簡素化するためのクラス。
* ネットワークビヘイビア：無線ネットワークの手続きを簡素化するためのクラス。

ビヘイビア名は `< >` で括って表記します。例えばシンプル中継ネットワークのビヘイビア名は `<NWK_SIMPLE>` です。

### クラスオブジェクト

本ライブラリの解説では、ライブラリで最初からグローバル宣言されたオブジェクトをクラスオブジェクトと呼称します。`Serial`, `Wire` などです。これらクラスオブジェクトは手続きなしまたは開始手続きを行うことで利用できます。

メモリを比較的多く消費するクラスオブジェクトは、初期化手続きの際(`.setup()`または`.begin()`メソッド)に初期化パラメータに沿ったメモリを確保します。

## C++に関する用語

{% hint style="info" %}
一般の用語です。C言語の知識を前提に解説します。
{% endhint %}

### C++

C++言語のこと。

{% hint style="info" %}
MWXライブラリはC++とC言語によって記述されています。
{% endhint %}

### C++11

C++規格のバージョンの一つ。2011年式のC++といった意味合いで、2011年にISOで規格化されています。直前のC++03から大きく機能拡張されています。C++14, C++17といったより新しいバージョンがあります。

{% hint style="info" %}
MWXライブラリはC++11で追加された機能や構文を用いて実装されています。[MWSDKのコンパイラ対応](/v0.1.3/about_mwx/design_policy#c-yan-yu-gui-ge)はC++11までとなります。
{% endhint %}

### クラス

あるデータに注目して、その手続きをひとまとめにしたもの。構造体に、その構造体を取り扱うための手続きが含まれています。実際にはもっと深い話題に発展しますが、専門書を参考にしてください。

C++においては、キーワードの `struct`と `class`は本質的には同じもので、いずれのキーワードで宣言してもクラスとなります。

```cpp
struct myhello {
  int _i;
  void say_hello() { printf("hello %d\n", _i); }
};
```

上記のクラス定義をC言語でも行った場合、例えば以下のようになります。

```cpp
typedef struct _c_myhello {
  int _i;
  void (*pf_say_hello)(struct _c_myhello *);
} c_myhello;

void say_hello(c_myhello*p) { p->pf_say_hello(); }
void init_c_my_hello(c_myhello*p) {
  p->pf_say_hello = say_hello;
}
```

### ラッパークラス

既存のC言語のライブラリやその内部の構造体などをクラスに包含し、C++特有の機能性を追加するなどして、利用の便を図ったものです。解説中でも「～構造体をラップした」といった記述をする場合があります。

{% hint style="info" %}
MWXライブラリは、TWENETのCライブラリのラッパークラスと、新たに実装したクラス群との組み合わせになっています。
{% endhint %}

### メソッド・メンバー関数

クラスに定義される関数で、クラスに紐付いています。

```cpp
struct myhello {
  int _i;
  void say_hello() { printf("hello %d\n", _i); } //メソッド
};
```

### オブジェクト（インスタンス）

クラスを実体化（メモリ確保）したもの。

```cpp
void func() {
    myhello obj_hello; // obj_helloがmyhelloクラスのオブジェクト
    obj_hello._i = 10;
    obj_hello.say_hello();
}
```

本解説ではオブジェクトとインスタンスは同じ意味合いとして取り扱っています。

### コンストラクタ

オブジェクト生成時の初期化手続き。

```cpp
struct myhello {
  int _i;
  void say_hello() { printf("hello %d\n", _i); }
  
  myhello(int i = 0) : _i(i) {} // コンストラクタ
};

void my_main() {
  myhello helo(10); // ここでコンストラクタが呼び出され_i=10にセットされる
}
```

### デストラクタ

コンストラクタと対になってオブジェクトが破棄されるときの手続きです。

```cpp
struct myhello {
  int _i;
  void say_hello() { printf("hello! %d\n", _i); }
  
  myhello(int i = 0) : _i(i) {} // コンストラクタ
  ~myhello() {
    printf("good bye! %d\n", _i);
  } //デストラクタ
};
```

### 抽象クラス

C++では仮想クラスによりポリモーフィズム（多態性）を実現します。具体的には`virtual`キーワードで指定た純粋仮想関数を定義したクラスです。

```cpp
struct Base {
  virtual void say_hello() = 0;
};

struct DeriveEng : public Base {
  void say_hello() { printf("Hello!"); }
};

struct DeriveJpn : public Base {
  void say_hello() { printf("Kontiwa!"); }
};
```

{% hint style="warning" %}
MWXライブラリでは、コンパイラの制限や性能上の理由で仮想関数を使用しません。ポリモーフィズムを実現するのに[別の手法](/v0.1.3/about_mwx/design_policy#crtpnishitatenpurtopatn)を用いています。
{% endhint %}

### スコープ

C/C++言語では `{ }` で括った範囲と考えてください。この中で生成したオブジェクトは、スコープから出るときに破棄されます。この時デストラクタが呼び出されます。

以下は、明示的にスコープを設定したものです。`helo2`は8行目まで実行された時点で破棄され、デストラクタが呼び出されます。

```cpp
void my_main() {
  myhello helo1(1);
  helo1.say_hello();
  
  {
    myhello helo2(2);
    helo2.say_hello();
  }
}

// hello! 1
// hello! 2
// good bye! 2
// good bye! 1
```

MWXライブラリでは以下のような記法を用いています。ここではif文の条件判定式内で宣言（C89といった旧いC言語ではこういった場所での宣言はできません）されたオブジェクトの有効期間は、if文の`{}`内になります。

```cpp
struct myhello {
  int _i;
  void say_hello() { printf("hello! %d\n", _i); }
  operator bool() { return true; } // if()での判定用の演算子
  
  myhello(int i = 0) : _i(i) {} // コンストラクタ
  ~myhello() { printf("good bye! %d\n", _i); } // コンストラクタ
};

// myhello オブジェクトを生成する関数 (ジェネレータ)
myhello gen_greeting() { return my_hello(); }

void my_main() {
  if (myhello x = gen_greeting()) {
    // myhelloのオブジェクト x は if文中有効
    x.say_hello();
  }
  // if 分を抜けるときにオブジェクトxは破棄される
}
```

例えば二線シリアルバスなど、開始と終了の手続きがあって、その間だけオブジェクトによってバスを操作するような手続きです。オブジェクトの生成後、バスの接続が適切であればif文のtrue節が実行され、生成したオブジェクトによってバスの書き込みまたは読み出しを行います。バスの読み書き操作が終了したらif文を脱出し、この時デストラクタが呼び出され、バスの利用終了手続きが行われます。

```cpp
const uint8_t DEV_ADDR = 0x70;
if (auto&& wrt = Wire.get_writer(DEV_ADDR)) { //バスの初期化、接続判定
	wrt(SHTC3_TRIG_H); // 書き出し
	wrt(SHTC3_TRIG_L);
} // バスの利用終了手続き
```

### 名前空間 (namespace)

定義名の重複を避けるためC++では名前空間が積極的に用いられます。名前空間にある定義にアクセスするには`::`を用います。

```cpp
namespace MY_NAME { // 名前空間の宣言
  const uint8_t MYVAL1 = 0x00;
}

...
void my_main() {
  uint8_t i = MY_NAME::MYVAL1; // MY_NAME の参照
}

```

### テンプレート (template)

テンプレートはC言語のマクロを拡張したものと考えてください。

```cpp
template <typename T, int N>
class myary {
  T _buf[N];
public:
  myary() : _buf{} {}
  T operator [] (int i) { return _buf[i % N]; }
};

myary<int, 10> a1; // int 型で要素数10の配列
myary<char, 128> a2; // char 型の要素数128の配列
```

この例では、簡単な配列を定義しています。`T`と`N`はテンプレートのパラメータで、`T`は型名を`N`は数値を指定し、`T`型で要素数`N`の配列クラスを定義しています。

### nullptr

C++11ではNULLポインタを`nullptr`と記述するようになりました。

### 参照型

C++では、参照型を利用できます。これはポインタによるアクセスに似ていますが、必ずオブジェクトを参照しなければならないという制約があります。

以下のような参照渡しのパラメータを持つ関数では`i`の値を`incr()`内で書き換えることが出来ます。

```cpp
void incr(int& lhs, int rhs) { lhs += rhs; }

void my_main() {
  int i = 10; j = 20;
  incr(i, j);
}
```

テンプレートの解説例ですが`operator[]`の戻り型を`T&`に変更しています。こうすることで`a[0]=1`のように配列内部のデータに対して直接代入操作ができるようになります。

```cpp
template <typename T, int N>
class myary {
  T _buf[N];
public:
  myary() : _buf{} {}
  T& operator [] (int i) { return _buf[i % N]; }
};

myary<int, 10> a1;
void my_main() {
  a1[0] = 1;
  a1[1] = 2;
}
```

{% hint style="info" %}
MWXライブラリのプログラミングインタフェースは、原則といてポインタ型の利用をせず、参照型を用いています。
{% endhint %}

### 型推論

C++11 では型推論の`auto`キーワードが導入されています。これはコンパイラが初期化の記述からそのオブジェクトの型を推論するため、具体的な型名の記述を省略できます。これはtemplateを用いたクラス名が非常に長くなるような場合に効果的です。

解説中では多くの場合ユニバーサル参照と呼ばれる`auto&&`を用いています。ユニバーサル参照については、ここでは参照渡しの場合も意識せずに記述できるものと考えてください。

```cpp
auto&& p = std::make_pair("HELLO", 5);
       // const char* と int のペア std::pair
```

### コンテナ

配列など特定のデータ型のオブジェクトを複数個格納するためのクラスをコンテナと呼びます。テンプレートの例で挙げた`myary`のような配列クラスもコンテナと呼んでいます。

{% hint style="info" %}
MWXライブラリでは、配列クラス`smplbuf`とFIFOキュークラス`smplque`を用意しています。
{% endhint %}

### イテレータ, .begin(), .end()

C言語で言うところのポインタ（もちろんC++でも同じようにポインタは使えます）を拡張した概念です。C言語のポインタは、メモリが連続した要素を先頭から末尾まで連続的にアクセスする手段と考えることが出来ます。FIFOキューを考えてみます。もっとも単純なキューの実装はリングバッファによるものですが、メモリーの連続性はありません。こういったデータ構造であっても、イテレータを用いるとポインタと同じように記述できます。

イテレータを取得するため`.begin()`,`.end()` のメソッドが用いられます。コンテナの**先頭**を指すイテレータを`.begin()`で取得します。**末尾の次**を指すイテレータを`.end()`で取得します。末尾ではなく、末尾の次である理由にはforやwhile文のループ記述の明快さ、コンテナに格納される要素数が0の場合の取り扱いが挙げられます。

```cpp
my_queue que; // my_queue はキューのクラス

auto&& p = que.begin();
auto&& e = que.end();

while(p != e) {
  some_process(*p);
  ++p;
}

```

上記では、`que`の各要素について、イテレータ`p`を用いて各要素に`some_process()`を適用しています。`p`は`++`演算子によって次の要素を指すイテレータとしてインクリメントしています。本来ポインタでは記述できないデータ構造を持つコンテナであっても、このようにポインタを用いた処理と同じような処理が出来ます。

`.end()`が末尾の次を示すため、while文の終了判定は`(p != e)`のように簡潔です。キューに要素がない場合は`.begin()`は`.end()`と同じイテレータを返します。（何も格納されていない要素のイテレータの次ですから、最初に格納すべき領域を示すイテレータと考えればよいでしょう）

メモリ上で連続したコンテナの場合、通常、そのイテレータは通常のポインタとなります。その操作時に大きなオーバーヘッドにはならないことが期待できます。

### C++標準ライブラリ

C++の標準ライブラリにはSTL(Standard Template Library)が含まれます。MWXライブラリでの一部を利用しています。

{% hint style="warning" %}
TWELITE向けのC/C++コンパイラの[制約](/v0.1.3/about_mwx#zhi-xian-shi-xiang)から、利用できる機能はごく一部です。
{% endhint %}

### アルゴリズム

例えば最大や最小値を求めるといった処理をC言語では型に応じて別々に記述していました。こういったコードは型の部分だけ違って他は同じといったものも少なくありません。C++ではtemplateやイテレータなどを用いて、こういった処理を型に依存せず記述することができます。これをアルゴリズムと呼んでいます。

```cpp
// 任意のイテレータをパラメータとし最大値を持つイテレータを戻す
template <class Iter>
Iter find_max(Iter b, Iter e) {
  Iter m = b; ++b;
  while(b != e) {
    if (*b > *m) { m = b; }
    ++b;
  }
  return m;
}
```

例えば上記のように最大値を求めるアルゴリズムです。このアルゴリズムは型に依存しません。(ジェネリックプログラミングと呼ばれます）

```cpp
#include <algorithm>

auto&& minmax = std::minmax_element( // 最大最小を得るアルゴリズム
  que.begin(), que.end());

auto&& min_val = *minmax.first;
auto&& max_val = *minmax.second;
```

ここでは`que`のイテレータを指定し、その最大と最小を得るアルゴリズム`std::minmax_elenet`を適用しています。`std::minmax_elemet`はC++標準ライブラリ内に定義されています。その戻り値は任意の２つの値を組み合わせる`std::pair`です。このアルゴリズムは、イテレータの示す要素同士で`<,>,==`といった演算子での比較が出来れば最大と最小を計算してくれます。戻り型もイテレータの型から導かれます。


# 設計情報

MWX ライブラリ内で用いる C++ 言語について、その仕様、制限事項、本書記載留意事項、設計メモを記載します。

{% hint style="danger" %}
**MWXライブラリでアプリケーション記述する場合は、このページを読み飛ばしても差し支えありません。**
{% endhint %}

{% hint style="info" %}
このページはライブラリの動作の理解や改造などのためライブラリソースコードを参照する場面を想定しています。ライブラリを利用するのと比べより高度なC++言語に関する知識を前提となります。
{% endhint %}

## 設計方針について

* アプリケーションのループ記述では、一般によく用いられる API 体系に近い記述を出来ることを目的とするが、TWELITEの特性に合わせた実装とする。
* TWENET はイベントドリブンによるコード記述であり、これを扱えるようにクラス化を行う。上記クラス化によりアプリケーションのふるまいをカプセル化できるようにする。
* イベントドリブンとループの記述は共存できる形とする。
* 代表的なペリフェラルはクラス化して手続きを簡素化する。可能な限りループ記述でアクセスできるようにする。
* 当社で販売する MONOSTICK/PAL といったボードを利用する手続きをクラス化し手続きを簡素化する。（例えば外部のウォッチドッグタイマーの利用を自動化する）
* アプリケーションクラスやボードクラスは、ポリモーフィズムの考え方を導入し、統一した手続きによって利用できるようにする。（例えば、いくつかの振る舞いをするアプリケーションクラスを始動時にロードするような場合、また TWENET C ライブラリの接続部のコードを都度定義しなくてよいようにするため）。
* C++の機能については、特に制限を設けず利用する。例えば、無線パケットを取り扱うにあたり煩雑なパケット構築、分解といった代表的な手続きを簡略化する手段を提供する。
* 演算子 `->` を極力使用しないようにし、原則として参照型による API とする。

{% hint style="info" %}
限られた時間で実装を進めているため、細かい点まで網羅した設計ではありませんが、設計・実装等でお気づきの点がありましたら、当社サポートにご連絡ください。
{% endhint %}

## C++ コンパイラについて

#### バージョン

gcc version 4.7.4

#### C++言語規格

C++11 (コンパイラ対応状況は一般の情報を参考にしてください）

* <https://gcc.gnu.org/gcc-4.7/cxx0x_status.html>&#x20;
* <https://cpprefjp.github.io/implementation-status.html>&#x20;

## C++ の制限事項

※ 当社で把握しているものについての記載です。

* `new, new[]` 演算子でのメモリ確保は行えますが、確保したメモリを破棄することはできません。C++ライブラリで動的メモリ確保をするものは殆どが事実上利用不可能です。一度だけ生成してそれ以降破棄しないオブジェクトに使用しています。
* グローバルオブジェクトのコンストラクタが呼び出されません。\
  参考：必要な場合は、初期化関数(`setup()`) で `new ((void*)&obj_global) class_foo();` のように初期化することでコンストラクタの呼び出しを含めた初期化を行えます。
* 例外 `exception`が使用できません。
* 仮想関数 `virtual`が使用できません。

## 設計メモ

本節ではMWXライブラリのコードを参照する際に理解の助けとなる情報を記載します。

### 現状の実装

限られた時間で実装を進めているため、詳細部分の整備が十分でない場合があります。例えば const に対する考慮は多くのクラスで十分なされていません。

### 名前空間

名前空間について、以下の方針としています。

* 定義は原則として共通の名前空間`mwx`に配置する。
* 名前空間の識別子なしで利用できるようにしたいが、一部の定義は識別子を必須としたい。
* クラス名については比較的長い命名とし、ユーザが利用するものは別名定義とする。

クラス・関数・定数は一部の例外を除き`mwx`名(正確には`inline namespace L1` で囲んだ`mwx::L1`)の名前空間内に定義しています。`inline namespace`を指定しているのは、`mwx::`の指定を必須とする定義と、必須としない定義を共存させるためです。&#x20;

殆どの定義は`using namespace`により名前空間名を指定しなくても良いようになっています。これらの指定はライブラリ内の`using_mwx_def.hpp`で行っています。

```cpp
// at some header file.
namespace mwx {
  inline namespace L1 {
    class foobar {
      // class definition...
    };
  }
}

// at using_mwx_def.hpp
using namespace mwx::L1; // mwx::L1 内の定義は mwx:: なしでアクセスできる
                         // しかし mwx::L2 は mwx:: が必要。
```

例外的に比較的短い名前については`mwx::crlf, mwx::flush`のように指定します。これらは`inline namespace`の`mwx::L2`の名前空間に配置されています。`using namespace mwx::L2;`を指定することで名前空間名の指定なしで利用できるようになります。

また、いくつかのクラス名は`using`指定をしています。

MWXライブラリ内で利用する`std::make_pair`を`using`指定しています。

### CRTP(奇妙に再帰したテンプレートパターン)&#x20;

仮想関数 (virtual), 実行時型情報(RTTI) が利用できない、かつ利用できるようにしたとしても、パフォーマンス面で難があるため、これに代わる設計手法として [CRTP (Curiously recurring template pattern : 奇妙に再帰したテンプレートパターン）](https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern)を用いています。CRTPは、継承元の親クラスから子クラスのメソッドを呼び出すためのテンプレートパターンです。

以下の例では `Base` を継承した `Derived` クラスに `interface()` というインタフェースを実装する例です。`Base`からは`Derived::print()`メソッドを呼び出しています。

```cpp
template <class T>
class Base {
public:
  void intrface() {
    T* derived = static_cast<T*>(this);
    derived->prt();
  }
};

class Derived : public class Base<Derived> {
  void prt() {
     // print message here!
     my_print("foo");
  }
}
```

MWXライブラリで利用されている主要クラスは以下です。

* イベント処理の基本部分`mwx::appdefs_crtp`
* ステートマシン`public mwx::processev_crtp`
* ストリーム `mwx::stream`

### CRTP での仮想化

CRTPクラスは、継承元のクラスはインスタンスごとに違います。このため、親クラスにキャストして、同じ仲間として取り扱うといったこともできませんし、仮想関数(`virtual`)やRTTI（実行時型情報）を用いたような高度なポリモーフィズムも使うことが出来ません。

以下は上述のCRTPの例を、仮想関数で実装した例です。CRTPでは`Base* b[2]`のように同じ配列にインスタンスをまとめて管理することは、そのままではできません。

```cpp
class Base {
	virtual void prt() = 0;
public:
	void intrface() { prt(); }
};

class Derived1 : public Base {
	void prt() { my_print("foo"); }
};

class Derived2 : public Base {
	void prt() { my_print("bar"); }
};

Derived1 d1;
Derived2 d2;
Base* b[2] = { &d1, &d2 };

void tst() {
	for (auto&& x : b) { x->intrface(); }
}
```

MWXライブラリでは、CRTP のクラスインスタンスを格納するための専用クラスを定義し、このクラスに同様のインタフェースを定義することで解決しています。以下にコード例を挙げます。

```cpp
class VBase {
public:
	void* p_inst;
	void (*pf_intrface)(void* p);

public:
	void intrface() {
		if (p_inst != nullptr) {
			pf_intrface(p_inst);
		}
	}
};

template <class T>
class Base {
	friend class VBase;
	static void s_intrface(void* p) {
		T* derived = static_cast<T*>(p);
		derived->intrface();
	}
public:
	void intrface() {
		T* derived = static_cast<T*>(this);
		derived->prt();
	}
};

class Derived1 : public Base<Derived1> {
	friend class Base<Derived1>;
	void prt() { my_print("foo"); }
};

class Derived2 : public Base<Derived2> {
	friend class Base<Derived2>;
	void prt() { my_print("bar"); }
};

Derived1 d1;
Derived2 d2;

VBase b[2];

void tst() {
	b[0] = d1;
	b[1] = d2;

	for (auto&& x : b) {
		x.intrface();
	}
}
```

`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`による判定を行っています。

```cpp
#include <type_trails>

class Derived1 : public Base<Derived1> {
public:
   static const uint8_t TYPE_ID = 1;
}

class Derived1 : public Base<Derived1> {
public:
   static const uint8_t TYPE_ID = 2;
}

class VBase {
  uint8_t type_id;
public:
	
	template <class T>
	void operator = (T& t) {
		static_assert(std::is_base_of<Base<T>, T>::value == true,
						"is not base of Base<T>.");

		type_id = T::TYPE_ID;
		p_inst = &t;
		pf_intrface = T::s_intrface;
	}
	
  template <class T>
  T& get() {
    static_assert(std::is_base_of<Base<T>, T>::value == true,
					  "is not base of Base<T>.");
			
		if(T::TYPE_ID == type_id) {
			return *reinterpret_cast<T*>(p_inst);
		} else {
			// panic code here!
		}
  }
}

Derived1 d1;
Derived2 d2;

VBase b[2];

void tst() {
	b[0] = d1;
	b[1] = d2;
	
  Derived1 e1 = b[0].get<Derived1>(); // OK
  Derived2 e2 = b[1].get<Derived2>(); // OK
  
  Derived2 e3 = b[1].get<Derived1>(); // PANIC!
}

```

### new, new\[] 演算子

TWELITEモジュールのマイコンには十分なメモリもなければ、高度なメモリ管理もありません。しかしマイコンのメモリマップの末尾からスタックエリアまでの領域はヒープ領域として、必要に応じて確保できる領域があります。以下にメモリマップの概要を図示します。APPがアプリケーションコードで確保されたRAM領域、HEAPはヒープ領域、STACKはスタック領域です。

```
|====APP====:==HEAP==..   :==STACK==|
0                                  32KB
```

たとえ`delete`できなくても`new`演算子が有用である場面も想定されます。そのため、以下のように`new, new[]`演算子を定義しています。`pvHear_Alloc()`は半導体ライブラリで提供されているメモリ確保の関数で、`u32HeapStart, u32HeapEnd`も同様です。`0xdeadbeef`はダミーアドレスです。beefがdeadなのは変だとかいう指摘はしないでください。

```cpp
void* operator new(size_t size) noexcept {
    if (u32HeapStart + size > u32HeapEnd) {
        return (void*)0xdeadbeef;
    } else {
        void *blk = pvHeap_Alloc(NULL, size, 0);   
        return blk;
    }
}
void* operator new[](size_t size) noexcept {
    return operator new(size); }
void operator delete(void* ptr) noexcept {}
void operator delete[](void* ptr) noexcept {}
```

例外も使えないため失敗したときの対処はありません。また、メモリ容量を意識せず確保を続けた場合、スタック領域と干渉する可能性もあります。

{% hint style="info" %}
システム（MAC層など）が確保するメモリは約2.5KBです。
{% endhint %}

### コンテナクラス

MWXライブラリでは、マイコンのリソースが小さい点、メモリーの動的確保ができない点を考慮し標準ライブラリで提供されるコンテナクラスの利用はせず、シンプルなコンテナクラスを２種類定義しています。コンテナクラスにはイテレータや`begin(), end()`メソッドを定義しているため、範囲for文やSTLのアルゴリズムの一部を利用できます。

```cpp
smplbuf<int16_t, alloc_local<int16_t, 16>> buf;
buf.push_back(-1); // push_back() は末尾に追加
buf.push_back(2);
...
buf.push_back(10);

//範囲for文
for(auto&& x : buf) { Serial << int(x) << ',' }
//アルゴリズム std::minmax
auto&& minmax = std::minmax_element(buf.begin(), buf.end());
Serial << "Min=" << int(*minmax.first)
       << ",Max=" << int(*minmax.second);

```

| クラス名      | 概要                                                                                                                                   |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `smplbuf` | <p>配列クラスで、最大領域 (capacity) と最大領域範囲内で都度サイズを指定できる利用領域(size)を管理します。</p><p>また本クラスは stream インタフェースを実装しているため、<< 演算子を用いてデータを書き込むことができます。</p> |
| `smplque` | FIFOキューを実装しています。キューのサイズはテンプレートのパラメータで決定します。割り込み禁止を用いキューを操作するためのテンプレート引数もあります。                                                        |

####

#### コンテナクラスのメモリについて

コンテナクラスではメモリの確保方法を`template`引数のパラメータとして指定します。

| クラス名           | 内容                                                                                            |
| -------------- | --------------------------------------------------------------------------------------------- |
| `alloc_attach` | <p>すでに確保済みのバッファメモリを指定する。</p><p>Cライブラリ向けに確保したメモリ領域を管理したいとき、同じバッファ領域の分断領域として処理したい時などに使用します。</p> |
| `alloc_static` | <p>クラス内に静的配列として確保する。</p><p>事前にサイズが決まっていたり、一時利用の領域として使用します。</p>                                |
| `alloc_heap`   | <p>ヒープ領域に確保する。<br>システムのヒープに確保後は破棄できませんが、初期化時にアプリケーションの設定などに従い領域を確保するといった使い方に向いています。</p>       |

### 可変数引数

MWXライブラリでは、バイト列やビット列の操作、`printf`相当の処理を行う処理に可変数引数を用いています。下記の例は指定のビット位置に1をセットする処理です。

```cpp
// packing bits with given arguments, which specifies bit position.
//   pack_bits(5, 0, 1) -> (b100011) bit0,1,5 are set.

// 再帰取り出しの一番最初の関数
template <typename Head>
constexpr uint32_t pack_bits(Head head) { return  1UL << head; }

// head を取り出し、残りのパラメータを再帰呼び出しにて pack_bits に転送
template <typename Head, typename... Tail>
constexpr uint32_t pack_bits(Head head, Tail&&... tail) {
  return (1UL << head) | pack_bits(std::forward<Tail>(tail)...);
}

// コンパイル後、以下の２つは同じ結果になります。
constexpr uint32_t b1 = pack_bits(1, 4, 0, 8);
// b1 and b2 are the same! 
const uint32_t b2 = (1UL << 1)|(1UL << 4)|(1UL << 0)|(1UL << 8);
```

この処理では template のパラメータパック (`typename...` の部分) で、再帰的な処理を行い引数の展開を行っています。上記の例では`constexpr`の指定があるため、コンパイル時に計算が行われマクロ定義や`b2`のような`const`値の指定と同等の結果になります。また変数を引数として動的に計算する関数としても振る舞うこともできます。

以下の例では、`expand_bytes`関数により、受信パケットのデータ列からローカル変数に値を格納しています。パラメータパックを用いた場合各引数の型を把握できるため、下記のように、受信パケットのバイト列から、サイズや異なる型にあった値を格納することができます。

```cpp
auto&& rx = the_twelite.receiver.read(); // 受信パケット

// 展開後のパケットの内容を格納する変数
// パケットのペイロードはバイト列で以下のように並んでいる。
//   [B0][B1][B2][B3][B4][B5][B6][B7][B8][B9][Ba][Bb]
//   <message       ><adc*  ><vcc*  ><timestamp*    >
//   * 数値型はビッグエンディアン並び
uint8_t msg[MSG_LEN];
uint16_t adcval, volt;
uint32_t timestamp;

// expand packet payload
expand_bytes(rx.get_payload().begin(), rx.get_payload().end()
		, msg       // 4bytes of msg
		, adcval    // 2bytes, A1 value [0..1023]
	  , volt      // 2bytes, Module VCC[mV]
	  , timestamp // 4bytes of timestamp
);
```

### イテレータ

イテレータはポインタの抽象化で、例えばメモリの連続性のないようなデータ構造においても、あたかもポインタを使ったようにデータ構造にアクセスできる効果があります。

{% hint style="info" %}
C++のSTLでは、`begin()`メソッドで得られるコンテナの先頭を示すイテレータと、`end()`メソッドで得られるコンテナの末尾の「次」を示すイテレータの組み合わせが良く用いられます。

コンテナの末尾の「次」を`end()`としているのは、以下のような記述を想定しているためです。MWXライブラリでもこれに倣ってコンテナの実装を行っています。

```cpp
smplque<uint8_t, alloc_local<uint8_t, 5> > que;
que.push('a'); que.push('b'); que.pop(); que.push('c'); ...

auto&& p = que.begin();
auto&& e = que.end();

while(p != e) { // pがeまで進んだ＝全要素処理した
  Serial << *p;
  ++p; // イテレータのインクリメントは前置演算子を使います。
  　　　// この場合、p++ と記述すると、コンパイラによる最適化が行われる可能性は
       // 高いものの、コード上はイテレータのコピーが発生します。
}
```

イテレータを標準ライブラリの仕様に適合させることで、範囲for文が利用できたり、標準ライブラリのアルゴリズムを利用できるようになります。

```csharp
#include <algorithm>
#include <cctype>

// ラムダ式による文字変換
std::for_each(que.begin(), que.end(), 
  [](uint8_t& x) { x = std::toupper(x); });

// 範囲for文
for (uint8_t x : que) {
  Serial << x;
}
```

（MWXライブラリではC++標準ライブラリに対する適合度や互換性についての検証は行っていません。動作確認の上利用ください）
{% endhint %}

以下の例では、通常のポインタでは連続的なアクセスができないFIFOキューのイテレータ、さらに、FIFOキューの構造体の特定メンバー(例ではX軸)のみを抽出するイテレータを利用する例です。

```cpp
// XYZTの４軸構造体を要素とする要素数５のキュー
smplque<axis_xyzt, alloc_local<axis_xyzt, 5> > que;

// テスト用にデータを投入
que.push(axis_xyzt(1, 2, 3, 4));
que.push(axis_xyzt(5, 2, 3, 4));
...

// 構造体としてのイテレータを用いたアクセス
for (auto&& e : v) { Serial << int(e.x) << ','; }

// キューの中の X 軸を取り出す
auto&& vx = get_axis_x(que);
// X軸のイテレータを用いたアクセス
for (auto&& e : vx) { Serial << int(e) << ','; }

// int16_t要素のイテレータなので、STLのアルゴリズム（最大最小）が使える
auto&& minmax = std::minmax_element(vx.begin(), vx.end());
```

 以下は `smplque` クラスのイテレータの実装の抜粋です。このイテレータでは、キューオブジェクトの実体と、インデックスにより管理しています。キューのメモリが不連続になる（末尾の次は先頭を指す必要があるリングバッファ構造）部分は`smplque::operator []`で解決しています。オブジェクトのアドレスが一致することとインデックスが一致すればイテレータは同じものを指していることになります。

この実装部分には `<iterator>` が要求する typedef なども含まれ、より多くのSTLのアルゴリズムが適用できるようになります。

```cpp
class iter_smplque {
	typedef smplque<T, alloc, INTCTL> BODY;

private:
	uint16_t _pos; // index
	BODY* _body;   // point to original object

public: // for <iterator>
	typedef iter_smplque self_type;
	typedef T value_type;
	typedef T& reference;
	typedef T* pointer;
	typedef std::forward_iterator_tag iterator_category;
	typedef int difference_type;

public: // pick some methods
	inline reference operator *() {
		return (*_body)[_pos];
	}
	
	inline self_type& operator ++() {
		_pos++;
		return *this;
	}
};
```

構造体を格納したコンテナ中の、特定構造体メンバーだけアクセスするイテレータは少々煩雑です。構造体のメンバーにアクセスするメンバー関数を予め定義しておきます。このメンバー関数をパラメータ（`R& (T::*get)()`）としたテンプレートを定義します。`Iter`はコンテナクラスのイテレータ型です。

```cpp
struct axis_xyzt {
    int16_t x, y, z;
    uint16_t t;
    int16_t& get_x() { return x; }
    int16_t& get_y() { return y; }
    int16_t& get_z() { return z; }
};

template <class Iter, typename T, typename R, R& (T::*get)()>
class _iter_axis_xyzt {
    Iter _p;
    
public:
    inline self_type& operator ++() {
        _p++;
        return *this; }

    inline reference operator *() {
        return (*_p.*get)(); }
};

template <class Ixyz, class Cnt>
class _axis_xyzt_iter_gen {
    Cnt& _c;
    
public:
    _axis_xyzt_iter_gen(Cnt& c) : _c(c) {}
    Ixyz begin() { return Ixyz(_c.begin()); }
    Ixyz end() { return Ixyz(_c.end()); }
};

// 長いので using で短縮
template <typename T, int16_t& (axis_xyzt::*get)()>
using _axis_xyzt_axis_ret = _axis_xyzt_iter_gen<
    _iter_axis_xyzt<typename T::iterator, axis_xyzt, int16_t, get>, T>;

// X 軸を取り出すジェネレータ
template <typename T>
_axis_xyzt_axis_ret<T, &axis_xyzt::get_x>
get_axis_x(T& c) {
    return _axis_xyzt_axis_ret<T, &axis_xyzt::get_x>(c);
}
```

値にアクセスする`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`クラスでも同様に利用できます。

### 割り込み・イベント・状態ハンドラの実装

ユーザ定義クラスによりアプリケーション動作を記述するため、代表的なハンドラは必須メソッドとして定義が必要ですが、それ以外に多数ある割り込みハンドラ、イベントハンドラ、ステートマシンの状態ハンドラをすべて定義するのは煩雑です。ユーザが定義したものだけ定義され、それのみのコードが実行されるのが理想です。

```cpp
class my_app_def {
public: // 必須メソッドの定義
	void network_event(twe::packet_ev_nwk& pEvNwk) {}
	void receive(twe::packet_rx& rx) {}
	void transmit_complete(twe::packet_ev_tx& pEvTx) {}
	void loop() {}
	void on_sleep(uint32_t& val) {}
	void warmboot(uint32_t& val) {}
	void wakeup(uint32_t& val) {}
	
public: // これらを必須記述とするのは煩雑
  // DIO割り込みハンドラ 20種類ある
  // DIOイベントハンドラ 20種類ある
  // タイマー割り込みハンドラ 5種類ある
  // タイマーイベントハンドラ 5種類ある
  // ...
}
```

MWXライブラリでは、数の多い DIO割り込みハンドラ（TWELITEハード上は単一の割り込みですが、利用しやすさのためDIO一つずつにハンドラを割り当てることにしました）などを、テンプレートによる空のハンドラーとして定義した上、ユーザ定義のメンバー関数をそのテンプレートの特殊化することにより定義する手法を採用しました。

```cpp
// hpp file
class my_app_def : class app_defs<my_app_def>, ... {
  // 空のハンドラ
  template<int N> void int_dio_handler(uint32_t arg, uint8_t& handled) { ; }

  ...   
  // 12番だけ実装する
  
public:
  // TWENET から呼び出されるコールバック関数
  uint8 cbTweNet_u8HwInt(uint32 u32DeviceId, uint32 u32ItemBitmap);
};

// cpp file
template <>
void my_app_def::int_dio_handler<12>(uint32_t arg, uint8_t& handled) {
  digitalWrite(5, LOW);
  handled = true;
  return;
}

void cbTweNet_u8HwInt(uint32 u32DeviceId, uint32 u32ItemBitmap) {
  uint8_t b_handled = FALSE;
  switch(u32DeviceId) {
  	case E_AHI_DEVICE_SYSCTRL:
      if (u32ItemBitmap & (1UL << 0)){int_dio_handler<0>(0, b_handled);}
      if (u32ItemBitmap & (1UL << 1)){int_dio_handler<1>(1, b_handled);}
      ...
      if (u32ItemBitmap & (1UL << 12)){int_dio_handler<12>(12, b_handled);}
      ...
      if (u32ItemBitmap & (1UL << 19)){int_dio_handler<19>(19, b_handled);}
    break;
  }
}

```

実際のユーザ記述コードは、マクロ化やヘッダファイルのインクルードを行うことで、簡素化されていますが、上記は解説のために必要なコードを含めています。

TWENETからの割り込みハンドラから`my_app_def::cbTweNet_u8HwInt()` が呼び出されます。cppファイル中では、`int_dio_handler<12>`のみが特殊化されて記載された内容でインスタンス化されます。12番以外はhppファイル中のテンプレートからインスタンス化されます。結局以下のように展開されることになります。

```cpp
  	case E_AHI_DEVICE_SYSCTRL:
      if (u32ItemBitmap & (1UL << 0)){;}
      if (u32ItemBitmap & (1UL << 1)){;}
      ...
      if (u32ItemBitmap & (1UL << 12)){
          int_dio_handler<12>(12, b_handled);}
      ...
      if (u32ItemBitmap & (1UL << 19)){;}
      break;
      
    // ↓　↓　↓
    
    // 結局、このように最適化されることが期待できる。
   	case E_AHI_DEVICE_SYSCTRL:
      if (u32ItemBitmap & (1UL << 12)){
        // int_dio_handler<12> もinline展開
        digitalWrite(5, LOW);
        handled = true;
      }
      break;
    
```

最終的に、コンパイラの最適化により12番以外のコードは無意味と判断されコード中から消えてしまうことが期待できます（ただし、上記のように最適化されることを保証するものではありません）。

つまりユーザコード上では12番の割り込み時の振る舞いを定義したいときは`int_dio_handler<12>` を記述するだけで良い、ということになります（注：DIO割り込みを有効にするには `attachInterrupt()` を呼び出す必要があります）。登録しないハンドラはコンパイル時の最適化により低コストな呼び出しで済むことが期待できます。

{% hint style="info" %}
ユーザが関数を定義したときにこれを有効にし、定義しない場合は別の関数を呼び出す手法として、リンク時の解決方法があります。下記のように`__attribute__((weak))` の指定します。ユーザコードで `wakeup()` 関数が定義された場合は、ユーザーコードを関数をリンクし、未定義の場合は中身が空の関数をリンクします。

```cpp
// mwx_appcore.cpp
void wakeup() __attribute__((weak));
void wakeup() { }
```

上記ハンドラの実装においてはweak指定したメンバー変数を明示的に生成する必要があり、またinline化による最適化が行いにくいため使用しませんが、`wakeup()`といったいくつかのTWENETからのコールバック関数の受け皿としてweak指定の関数を定義しています。
{% endhint %}

### Streamクラス

ストリームクラスは、主にUART(シリアルポート)の入出力に用います。MWXライブラリでは、出力用の手続きを主に定義しています。一部入力用の定義もあります。

ここでは派生クラスが必要とする実装について解説します。

```cpp
template <class D>
class stream {
protected:
	void* pvOutputContext; // TWE_tsFILE*
public:
  inline D* get_Derived() { return static_cast<D*>(this); }
	inline D& operator << (char c) {
		get_Derived()->write(c);
		return *get_Derived();
	}
};

class serial_jen : public mwx::stream<serial_jen> {
public:
 	inline size_t write(int n) {
		return (int)SERIAL_bTxChar(_serdef._u8Port, n);
	}
};
```

上記は1文字書き出す`write()`メソッドの実装です。親クラスの`stream<serial_jen>`からはキャストを実行する`get_Drived()`メソッドを用いて、`serial_jen::write()`メソッドにアクセスしています。

必要に応じて `write(), read(), flush(), available()` といったメソッドを定義します。

書式出力にはMarco Paland氏による[printfライブラリ](https://github.com/mpaland/printf)を利用しています。MWXライブラリから利用するための実装が必要になります。下記の例で派生クラスの`serial_jen`で必要なことは1バイト出力のための `vOutput()` メソッドを定義することと、`vOutput()`がstaticメソッドであるため出力のための補助情報を親クラスの`pvOutputContext`に保存することです。

```cpp
template <class D>
class stream {
protected:
	void* pvOutputContext; // TWE_tsFILE*
public:
	inline tfcOutput get_pfcOutout() { return get_Derived()->vOutput; }
	
	inline D& operator << (int i) {
		(size_t)fctprintf(get_pfcOutout(), pvOutputContext, "%d", i);
		return *get_Derived();
	}
};

class serial_jen : public mwx::stream<serial_jen> {
	using SUPER = mwx::stream<serial_jen>;
	TWE_tsFILE* _psSer; // シリアル出力のためのローレベル構造体
public:
  void begin() {
    SUPER::pvOutputContext = (void*)_psSer;
  }
  
	static void vOutput(char out, void* vp) {
		TWE_tsFILE* fp = (TWE_tsFILE*)vp;
		fp->fp_putc(out, fp);
	}
};
```

`get_pfcOutput()`により、派生クラスで定義した`vOutput()`関数を指定し、そのパラメータとして`pvOutputContext`が渡されます。上記の例では`<<`演算子がint型で呼び出されたとき`serial_jen::vOutput()`とUART用に設定済みの`TWE_tsFILE*`を`fctprintf()`関数に渡しています。

### Wire, SPIのワーカーオブジェクト

`Wire`クラスでは、2線デバイスとの送信・受信時に、通信開始から終了までを管理する必要があります。ワーカーオブジェクトを利用する記述について内容を記述します。

```cpp
if (auto&& wrt = Wire.get_writer(SHTC3_ADDRESS)) {
	Serial << "{I2C SHTC3 connected.";
	wrt << SHTC3_TRIG_H;
	wrt << SHTC3_TRIG_L;
	Serial << " end}";
}
```

`periph_twowire::writer` クラスの抜粋です。streamインタフェースを実装するために `mwx::stream<writer>` を継承しています。steamインタフェースを利用するために `write()` と `vOutput()`メソッドの実装を行っています。

コンストラクタでは2線シリアルの通信開始を、デストラクタで通信終了のメソッドを呼び出しています。また、`operator bool()`演算子では、2線シリアルのデバイスの通信開始に成功した場合 `true` を返すようになっています。

```cpp
class periph_twowire {
public:
	class writer : public mwx::stream<writer> {
		friend class mwx::stream<writer>;
		periph_twowire& _wire;
	
	public:
		writer(periph_twowire& ref, uint8_t devid) : _wire(ref) {
	  	_wire.beginTransmission(devid); // コンストラクタで通信開始
		}
	
		~writer() {
			_wire.endTransmission(); // デストラクタで通信終了
		}
	
		operator bool() {
			return (_wire._mode == periph_twowire::MODE_TX);
		}
	
	private: // stream interface
		inline size_t write(int n) {
			return _wire.write(val);
		}
	
		// for upper class use
		static void vOutput(char out, void* vp) {
			periph_twowire* p_wire = (periph_twowire*)vp;
			if (p_wire != nullptr) {
				p_wire->write(uint8_t(out));
			}
		}
	};
	
public:
	writer get_writer(uint8_t address) {
		return writer(*this, address);
	}
};
class periphe_twowire Wire; // global instance

// ユーザコード
if (auto&& wrt = Wire.get_writer(SHTC3_ADDRESS)) {
	Serial << "{I2C SHTC3 connected.";
	wrt << SHTC3_TRIG_H;
	wrt << SHTC3_TRIG_L;
	Serial << " end}";
}
```

`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型として評価されるため、この振舞を変更します。

```cpp
			writer& operator << (int v) {
				_wire.write(uint8_t(v & 0xFF));
				return *this;
			}
```

ここではint型の値については8bitに切り詰めて、その値を出力しています。


# インストール・ビルド

Install and Build

MWXライブラリを用いてアプリケーションを記述（本書ではアクトと呼びます）し、実行するために開発環境のセットアップが必要です。


# 環境 (OSなど)

{% hint style="warning" %}
開発環境を構築するためには、ソフトウェア群のインストール、またこれらの利用許諾に同意する必要があります。また、PC、ワークステーション上でセキュリティ設定等が必要になる場合があります。

* 配布時には十分注意しておりますが、ウィルスなどの確認はお客様のほうでも留意いただくようお願いいたします。
* お客様のセキュリティの考え方や運用（例：外部アプリケーションのインストールの可否）については、お客様の環境の管理者にご確認ください。

また、開発環境をインストールまた動作するにあたり、OSが介在し設定等必要になる場合があります（例：開発元が不明なアプリケーションの実行。開発環境または紹介するツール群の多くは、アプリケーションは開発元を証明する仕組みが組み込まれせん）。設定方法については、一般の情報を参考いただくようお願いいたします。
{% endhint %}

MWXライブラリを用いてアプリケーションを記述するには以下が必要です。

* MWSDK(ソフトウェア開発環境)
* 開発用エディタ(Microsoft社のVisualStudio Codeを紹介します)

{% tabs %}
{% tab title="Windows10" %}
コンパイラのツールチェインなどは比較的環境への依存度が低いため、多くの環境で動作することが期待できますが、サポート中のWindows10バージョンを推奨します。動作環境の差異により動作しないような場合は、当社で確認している環境を参考に別途環境を用意してください。

以下、開発で使用しているバージョンを挙げます。

* Windows10 #1809, #1903
* .NET CLR v4.0.30319
* FTDI社のドライバが動作していること (MONOSTICK, TWELITE Rを動作させるため)

{% hint style="info" %}
WSL (Windows Subsystem Linux) 環境下でもコンパイラを動作させることが出来ます。ただしコンパイルのみでファームウェアの書き換え等はWindows10上のユーティリティから実施してください。

WSL環境は必須ではありません。
{% endhint %}
{% endtab %}

{% tab title="Linux" %}
コンパイラのツールチェインなどは比較的環境への依存度が低いため、多くの環境で動作することが期待できますが、現在サポート中のディストリビューションを推奨します。動作環境の差異により動作しないような場合は、当社で確認している環境を参考に別途環境を用意してください。

以下、開発で使用しているバージョンを挙げます。

* Ubuntu 16.04 LTS 64bit
* Ubuntu 18.04 LTS 64bit

＊32bitのシステムはサポートしません。
{% endtab %}

{% tab title="macOS" %}
コンパイラのツールチェインなどは比較的環境への依存度が低いため、多くの環境で動作することが期待できますが、現在サポート中のディストリビューションを推奨します。動作環境の差異により動作しないような場合は、当社で確認している環境を参考に別途環境を用意してください。

以下、開発で使用しているバージョンを挙げます。

* macOS 14.06
  {% endtab %}
  {% endtabs %}

### Visual Studio Code など開発環境について

開発環境を動作させるための環境や使用方法については、その開発元やコミュニティの情報を参照ください。

{% hint style="info" %}
コード記述作業の効率面で Visual Studio Code (VSCode) の利用を推奨します。

MWXライブラリでは、C言語の開発に比べ、読み込むヘッダファイルが多くなるため、VSCode上でのコード解釈等にはより多くのPCのリソースを要求します。
{% endhint %}

### ビルド環境による差異

{% hint style="warning" %}
Linux/WSL環境下/macOSのビルド結果はWindows10の結果と異なります。通常系の動作で差異が見られることは当社が把握する限りありませんが、特にgccのLTOを無効にしているためバイナリサイズが数%程度大きくなる傾向にあります。

動作等に疑問を感じた際は、必ず Windows10 上のビルドを実施し再現することを確認してから、お問い合わせください。
{% endhint %}


# TWELITE SDK のインストール

{% hint style="success" %}
MWSDK 2020-04 以降は TWELITE STAGE アーカイブに含まれます。

TWELITE STAGEアプリからビルドする場合は、`MWSDK_ROOT` 環境変数の設定が不要になります。

MWSDK 2019-12 と同じの方法でビルドする場合は、TWELITE STAGE アーカイブ中の MWSDK ディレクトリの構成は大きく変わっておりませんので、読み替えて利用ください。

TWELITE STAGE については以下をご覧ください。

* [インストール・使用方法](https://mono-wireless.com/jp/products/stage)
* [インストール補足情報](https://stage.twelite.info/)
  {% endhint %}

TWELITE SDK (MWSDK) をインストールします。本書ではMWSDKと記載します。

{% hint style="warning" %}
TWELITE SDKは**TWENET MWX C++ ライブラリ対応版**をダウンロードしてください。
{% endhint %}

### 1. MWSDK 圧縮アーカイブファイルのダウンロード

MWX 対応 SDK の圧縮アーカイブファイルを[ダウンロード](https://mono-wireless.com/jp/products/TWE-NET/index.html#MWX)してください。

### 2. 圧縮アーカイブの展開

圧縮アーカイブファイルを展開してください。

{% hint style="danger" %}
展開先のディレクトリ名にはスペース、日本語名が含まれてはいけません。
{% endhint %}

### 3. 環境変数の設定

`MWSDK_ROOT`, `MWSDK_ROOT_WINNAME`(Windows10のみ) の設定が必要です。

{% tabs %}
{% tab title="Windows10" %}
ここでは展開後のディレクトリ名を `C:\MWSDK` とします。別のディレクトリにインストールした場合は、読み替えてください。

`C:\MWSDK\SET_ENV.CMD` を実行してください。以下の環境変数を設定します。

* `MWSDK_ROOT`
* `MWSDK_ROOT_WINNAME`

例えば以下のような設定になります。

```
MWSDK_ROOT=C:/MWSDK/
MW_ROOT_WINNAME=C:\MWSDK\
```

{% hint style="warning" %}
インストールしたPC上からMWSDKをアンインストールするには以下を行ってください。

* `UNSET_ENV.cmd`を実行してください。環境変数の設定を解除します。
* MWSDKディレクトリを削除してください。
  {% endhint %}
  {% endtab %}

{% tab title="Linux" %}
開発環境やシェルに `MWX_ROOT`環境変数を反映されるように設定してください。

方法はいくつかありますが、ホームディレクトリの`.profile`（ファイルがなければ新しく作成してください）に以下の設定を追加します。この設定でVSCodeのビルドまで可能です。

`MWSDK_ROOT=/foo/bar/MWSDK/`\
`export MWSDK_ROOT`

エディタを使用せずに追加するには以下のようにコマンド入力します。`$`はプロンプトで環境によって表示が違います。`/foo/bar/MSWSDK`の部分はインストールしたディレクトリに応じて書き換えてください。

```bash
$ cd $HOME
$ echo MWSDK_ROOT=/foo/bar/MWSDK>>.profile
$ echo export MWSDK_ROOT>>.profile
```

{% endtab %}

{% tab title="macOS" %}
開発環境やシェルに `MWX_ROOT`環境変数を反映されるように設定してください。

方法はいくつかありますが、ホームディレクトリの`.profile`（ファイルがなければ新しく作成してください）に以下の設定を追加します。この設定でVSCodeのビルドまで可能です。

`MWSDK_ROOT=/foo/bar/MWSDK/`\
`export MWSDK_ROOT`

エディタを使用せずに追加するには以下のようにコマンド入力します。`$`はプロンプトで環境によって表示が違います。`/foo/bar/MSWSDK`の部分はインストールしたディレクトリに応じて書き換えてください。

```bash
$ cd $HOME
$ echo MWSDK_ROOT=/foo/bar/MWSDK>>.profile
$ echo export MWSDK_ROOT>>.profile
```

{% hint style="info" %}
環境全体に`MWSDK_ROOT`を適用にするにはLaunchDを用います。

VS Codeの一部の設定で環境変数を参照していますが、ビルドには必須ではありません。
{% endhint %}
{% endtab %}
{% endtabs %}

### 4. ライブラリの修正の適用

SDKに収録された時点から、ライブラリソースコードに修正がある場合があります。[改版履歴](/v0.1.3/revisions)を参照の上、必要に応じでライブラリソースコードを差し替えてください。

<br>


# VS Codeのインストール

MWSDKでは、アクト（ソースコード）記述をより容易に行うため、VisualStudio Code(VS Code)を紹介しています。添付のアクトには、VS Codeで適切にコード解釈が行われるように設定したファイルが含まれます。

{% hint style="warning" %}
VS Codeはソースファイルやヘッダファイルを読み込み、ソースコードを解釈し、これによりソースコードの記述の助けとなる関数定義情報や、関数・メソッド名の補完などを行います。C言語の開発に比べて、MWXライブラリでは読み込まれるヘッダファイルの分量が多くなります。環境によってはエディタの動作が重く感じる場合があります。
{% endhint %}

## VSCode のインストール

{% hint style="danger" %}
当サポートでは VSCode のインストール方法、使用方法のお問い合わせについては対応いたしません。一般で得られる情報を参照ください。

環境によっては、インストールのためにセキュリティ設定などが必要になる場合があります。インストールの可否はシステム管理者にご確認の上、手順は配布元や一般の情報を参考にしてください。
{% endhint %}

VSCode では、以下のことができます。

* ソースコードの編集
* GIT への接続（お客様が独自にソース管理を GIT 上で行う場合）
* ソースコード解釈に基づく intellisense（＊全ての定義が正確に解釈されることを保証するわけではありません）

VSCode はリンク先より入手してください。

{% embed url="<https://code.visualstudio.com>" %}

###

### プラグインの導入

Visual Studio Code が C/C++ 言語の記述を解釈できるようにするために、プラグインをインストールします。

* C/C++ for Visual Studio Code

## 注記事項

{% hint style="warning" %}
MWXライブラリのサンプルには .vscode の定義を含めています。この定義は MWSDK\_ROOT 環境変数を用い、ライブラリのソースコード(`{MWSDK_ROOT}/TWENET/current`以下)を特定しています。
{% endhint %}

{% hint style="warning" %}
VS Code のソースコードの解釈はコンパイラでの解釈とは完全には一致しません。またソースコードの編集状況によっては解釈がより不完全な状態になる場合もあります。
{% endhint %}


# アクトのビルド

MWXライブラリで記述したアプリケーションプログラムをアクト(Act)と呼びます。まずは、これをビルドして書き込みます。

* ビルドディレクトリ構成について
* ビルドスクリプトについて
* VS Code でのビルドについて

{% hint style="info" %}
本ページでは、いくつかのビルド方法を記載していますが、いずれの方法も最終的にはmakeコマンドを実行しています。詳細は[Makefileの解説](/v0.1.3/install_n_build/makefile)を参照ください。
{% endhint %}

## ビルドディレクトリ構成について

MWSDKをインストールしたディレクトリ`MWSDK_ROOT (例 C:\MWSDK)`を開きます。以下のような構成になっています。

```
MWSDK_ROOT
  |
  +-ChipLib      : 半導体ライブラリ
  +-License      : ソフトウェア使用許諾契約書
  +-MkFiles      : makefile
  +-Tools        : コンパイラ等のツール一式
  +-TWENET       : TWENET/MWXライブラリ
  +-Act_samples  : アクトサンプル
  ...
```

アクトファイルは`Act_samples` 以下に格納しています。（以下は一部割愛しています）

```
Act_samples
  |
  +-CoreAppTwelite    : App_TweLiteと同じ構成のボード用のアクト
  +-PAL_AMB           : 環境センス PAL 用のアクト
  +-PAL_MAG           : 開閉センス PAL 用のアクト
  +-PAL_MOT           : 動作センス PAL 用のアクト
  ..
  +-Parent-MONOSTICK  : 親機アクト、MONOSTICK用
  +-PingPong          : PingPong アクト
  +-PulseCounter      : パルスカウンタを利用したアクト
  +-act0              : スクラッチ（とりあえず書いてみる）用アクト
```

これらのアクトは、MWXライブラリの記述の参考となるシンプルな例ですが、多くのアクトは以下の機能を有しています。

* センサー値を取得する
* センサー値取得後、無線パケットを親機宛に送信する
* 送信完了後、一定時間スリープする（または割り込みを待つ）

`Parent-MONOSTICK`のアクトによりパケットの受信と表示を行っています。この親機用のアクトは、アスキー形式で出力しています。 (`:00112233AABBCC...FF[CR][LF]` のような : で始まり、途中は１６進数のバイトをアスキー文字2字で表現する形式です。末尾の??は同様に2字のバイトとなりますがLRCというチェックサムバイトになります。参考：[アスキー形式](https://mono-wireless.com/jp/products/TWE-APPS/App_Uart/mode_format_ascii.html))

実際に動作させてみるときは、以下の組み合わせを試してみてください。

| 親                                                        | 子                                                     | 解説                                                                  |
| -------------------------------------------------------- | ----------------------------------------------------- | ------------------------------------------------------------------- |
| [BRD\_APPTWELITE](/v0.1.3/act_samples/brd_apptwelite)    | [BRD\_APPTWELITE](/v0.1.3/act_samples/brd_apptwelite) | 親機はM1ピンをLOW(GNDレベル)にして起動する。通常モード（常時稼働）にて、App\_TweLiteのような動作を確認できます。 |
| [PingPong](/v0.1.3/act_samples/pingpong)                 | [PingPong](/v0.1.3/act_samples/pingpong)              | 子機同士2台使って動作します。片方から Ping パケットを送ると、相手方から Pong パケットが戻ってきます。           |
| [Parent-MONOSTICK](/v0.1.3/act_samples/parent_monostick) | その他                                                   | 子機用のアクトのパケット送信を確認できます。                                              |

では、アクトの中から PingPong のディレクトリの中を見てみましょう。

{% hint style="info" %}
`Act_samples` にある他のアクトもビルドできます。その場合、ディレクトリ名・ファイル名は読み替えるようにしてください。
{% endhint %}

```
Act_samples
  +-PingPong
    +-PingPong.cpp   : アクトファイル
    +-build          : ビルドディレクトリ
    +-.vscode        : VS Code 用の設定ファイル
```

必ずディレクトリ直下にディレクトリと同名の `.cpp` ファイルが必要です。

{% hint style="info" %}
小規模なアクトならこの`.cpp`ファイル内に記述します。規模が大きくなってきたときは[Makefileの解説](/v0.1.3/install_n_build/makefile)を参考にして複数のファイルに分割してビルドすることが出来ます。
{% endhint %}

{% hint style="warning" %}
`PingPong` ディレクトリ直下にアクトファイル `PingPong.cpp` があります。ディレクトリ名を変更した場合は、必ず `.cpp` ファイルの名前もディレクトリ名と同名にします。
{% endhint %}

次にビルドディレクトリを開きます。

```
Act_samples
  +-PingPong
    +-build
      +-Makefile        : makefile
      +-build-BLUE.cmd  : TWELITE BLUE 用ビルドスクリプト
      +-build-RED.cmd   : TWELITE RED 用ビルドスクリプト
      +-build-clean.cmd : obj_* ファイル削除
```

ビルドに必要なスクリプトと`Makefile`が格納されています。

## ビルドスクリプト(Windows10)

{% hint style="warning" %}
macOS, Linux では[VS Code](/v0.1.3/install_n_build/building-act#vs-code-denobirudonitsuite)または[コマンドライン](/v0.1.3/install_n_build/building-act#komandorainlinuxmacoswskdenobirudo)によるビルドを行います。
{% endhint %}

ビルドスクリプトは、エラーがあまり出ることのない、完成済みのアクト、サンプル提供のアクトなどに向いています。

以下は **Windows10用のスクリプト（バッチファイル）**&#x3067;す。

| バッチファイル名       | 内容            |
| -------------- | ------------- |
| build-BLUE.cmd | TWELITE BLUE用 |
| build-RED.cmd  | TWELITE RED用  |

実行後にbinファイルが生成され`PingPong_BLUE_???.bin` または`PingPong_RED_???.bin` のファイル名になります。???はバージョン番号やライブラリのバージョン文字に置き換わります。

![PingPong アクトのディレクトリを開く](/files/-LwLMAIFEDxX9S1cck1H)

![build-BLUE.cmd をダブルクリック(実行)](/files/-LwLMVpnf2VXtOisvczY)

![ビルド結果](/files/-LwLLxtxWKFfzJb32xDq)

![BINファイル(PingPong\_BLUE\_L1203\_V0-1-0.bin) が出来ている](/files/-LwLMlA0GbTmwRBhOG26)

{% hint style="success" %}
BINファイルが出来上がればビルド成功です。
{% endhint %}

{% hint style="info" %}
`objs_BLUE`ディレクトリはビルド中に生成された中間ファイルです。削除してもかまいません。
{% endhint %}

{% hint style="warning" %}
ビルドを迅速にするためパラレルビルドのオプションを設定しています。このためエラーが出た場合はエラーメッセージを視認しづらくなっています。

エラーメッセージを効率的に参照したい場合は VS Code などの開発ツールの利用を推奨します。
{% endhint %}

### クリーン（中間ファイルの削除）

`build-clean.cmd`を実行すれば、`objs_` で始まるディレクトリを消去します。BINファイルは消去しません。

{% hint style="warning" %}
ビルドがうまくいかない場合は、まずエラーメッセージを確認して下さい。errorという文字列が含まれる行中のメッセージから、エラー原因が容易に特定できる場合も少なくありません。

`objs_???` ディレクトリにある中間ファイルを削除してから再実行してみてください。（他の環境でビルドした中間ファイルが残っていると`make clean`を含めすべての操作が失敗します)
{% endhint %}

## コマンドライン(Linux/macOS/WSK)でのビルド

コマンドライン環境でのビルドについて補足します。

{% hint style="warning" %}
Windows10 では特別な理由がない限り**WSL (Windows Subsystem for Linux)は、MWSDKでアクトをビルドするのに必須ではありません**。WSLについて、既に知見があり特に利用したい方のみ本節をご覧ください。ここではWSLのインストール方法や使用方法については紹介しません。

WSL のディストリビューション Ubuntu 18.04 を前提として記述します。

WSL で動作するプログラム書き込みツールは用意していません。Windows10上のTWE-Programmerを利用ください。
{% endhint %}

{% hint style="warning" %}
コマンドライン(bash)についての利用の知識が必要です。
{% endhint %}

{% hint style="danger" %}
OS環境によっては各実行プログラムの動作時にセキュリティ警告が出る場合があります。警告を抑制する設定が必要になります。（**警告を抑制してプログラムを動作する運用を行うか**は、お客自身またはシステム管理者に相談の上、ご判断ください）
{% endhint %}

コマンドラインでのビルドは、bash(Bourne-again shell)が動作するウインドウで`make`を実行します。事前に環境変数`MWSDK_ROOT`が正しく設定されていることを確認してください。例えば`C:/MWSDK`にインストールした場合は `~/.profile` に以下のような設定を行います。

```bash
MWSDK_ROOT=/mnt/c/MWSDK
export MWSDK_ROOT
```

コマンドライン(bash)よりmakeを実行します。`make`がない場合はパッケージをインストールする必要があります。

```bash
$ make

Command 'make' not found, but can be installed with:

sudo apt install make
sudo apt install make-guile

$ sudo apt install make
...
```

{% hint style="info" %}

* Linux/WSL環境では`make`または`build-essential`パッケージをインストールします。
* macOS環境ではXcodeでCommand Line Toolsをインストールします。
  {% endhint %}

ビルドは以下のようになります。

```
$ cd $MWSDK_ROOT
$ cd Act_samples/PingPong/build
$ pwd
/mnt/c/MWSDK/Act_samples/PingPong/build

$ ls
... ファイル一覧の表示

$ rm -rfv objs_*
... 念のため中間ファイルを削除

$ make TWELITE=BLUE
... BLUE用に通常ビルド

$ make -j8 TWELITE=BLUE
... BLUE用にパラレルビルド(同時に8プロセス)
```

### 中間ファイルについて

ビルドが行われると objs\_??? ディレクトリが作成され、その中に中間ファイルが生成されます。このファイルはコンパイルした環境に依存しているため、他の環境のファイルが残っているとmakeがエラーとなりビルドが失敗します。

{% hint style="warning" %}
makeがエラーとなった場合は**直接`objs_???`ディレクトリ**を削除してください。
{% endhint %}

### コマンド例

詳細は[Makefileの解説](/v0.1.3/install_n_build/makefile)をご覧ください。

| コマンド例               | 解説                |
| ------------------- | ----------------- |
| `make TWELITE=BLUE` | TWELITE BLUE用にビルド |
| `make TWELITE=RED`  | TWELITE RED用にビルド  |
| `make cleanall`     | 中間ファイルの削除         |

## VS Code でのビルドについて

{% hint style="danger" %}
OS環境によっては各実行プログラムの動作時にセキュリティ警告が出る場合があります。警告を抑制する設定が必要になります。（**警告を抑制してプログラムを動作する運用を行うか**は、お客自身またはシステム管理者に相談の上、ご判断ください）
{% endhint %}

VS Code では、Act\_samplesディレクトリ直下にあるワークスペースファイルを開くか、Act\_samples以下のアクトディレクトリを開きます。

{% hint style="warning" %}
以下の例では英語インタフェースの画面例で、ワークスペースを開いています。
{% endhint %}

ワークスペースを開き `[Terminal>Run Task...]` を選択します。

![ビルドタスクの一覧表示](/files/-LvnbnyWLoG4kiJv3eba)

ビルドするTWELITE無線モジュールの種別(BLUE/RED)とアクト名を選択します。以下の例ではBuild for TWELITE BLUE PingPong (TWELITE BLUE用/PingPongアクト) を選択しています。選択後すぐにビルドが始まります。

![ビルドタスクの選択](/files/-LvncUdWIAhXZSqLQEqU)

ビルド中の経過は画面下部の TERMINAL 部分に出力されます。

![ビルド経過](/files/-LwLL5jKS3QDHPFtNvq4)

{% hint style="success" %}
正しくビルドできた場合、上記画面例の反転表示部のように .elf ファイルが生成されるメッセージがサイズ情報(`text data bss dec hex filename`と記載がある部分)とともに出力されます。

またBINファイル（上記では `PingPong_BLUE_???.bin`）ファイルがbuildディレクトリ下に出来上がっているはずです。確認してみてください。
{% endhint %}

{% hint style="info" %}
ビルド定義には Windows10 のファイルシステムに適合しないディレクトリ名 (例：`/c/User/...`)を変換する(例：`C:/User/...`) 定義を追加しています。

変換は完全ではありませんが、コンパイルメッセージからエラーが発生しているファイル名と行番号を抽出できます。

`.vscode/tasks.json` 中の実行コマンドは `sh -c "make ... | sed -E -e s#..."` のようにコマンド呼び出しすることで、出力メッセージ中のドライブ名相当部の文字列を書き換えています。

```
...
"windows": {
    "command": "sh",
    "args": [
        "-c", "make TWELITE=BLUE 2>&1 | sed -E -e s#\\(/mnt\\)?/\\([a-zA-Z]\\)/#\\\\\\2:/#g"
    ],
```

{% endhint %}

{% hint style="warning" %}
ビルドがうまくいかない場合は、まず**エラーメッセージを確認**して下さい。**errorという文字列が含まれる行**中のメッセージから、エラー原因が容易に特定できる場合も少なくありません。

念のため、クリーン(`objs_???` ディレクトリにある中間ファイルの削除)を行い、ビルドを再実行してみてください。（他の環境でビルドした中間ファイルが残っていると`make clean`を含めすべての操作が失敗します)
{% endhint %}

### WSLでのビルド

{% hint style="warning" %}
事前にコマンドライン(bash)でビルドが出来ることを確認しておいてください。
{% endhint %}

WSL環境でビルドしたい場合は、`.vscode/tasks.json`を編集してください。以下の例の４行目 `"windows":` 始まる行を追加（またはコメントアウト）します。

```
// .vscode/tasks.json
{
    "version": "2.0.0",
    "windows": { "options": { "shell": { "executable": "bash.exe", "args": ["-c", "-l"] } } },
    "tasks": [
    ...
```


# アクトの実行

ここではアクト(BINファイル)の書き込みと実行について解説します。

{% hint style="warning" %}
この節はWindows10環境のユーティリティTWE-Programmerを用いた解説です。

Linux/macOSでは[tweterm.py](/v0.1.3/install_n_build/runtheact/tweterm.py)を用います。**本ページでの書き込み・実行の流れを参照いただいた上**、tweterm.pyを利用ください。
{% endhint %}

{% hint style="info" %}
TWE-Programmer, tweterm.pyはBINファイルの書き込み機能とターミナル機能がありますが、ターミナル機能については各システム用のターミナルソフトを利用することもできます。

* Windows10: TeraTerm
* Linux/macOS: screen など
  {% endhint %}

## BINファイルの書き込みと実行

出来上がったBINファイルをTWELITEに書き込むにはTWE-Programmerを使用します。詳しい使い方は[こちら](https://tweprogrammer.twelite.info)を参照ください。

{% hint style="danger" %}
BINファイルが出来上がれば、実機で動作させることになります。繊細な電子部品ですので、取り扱いには十分注意してください。以下に代表的な注意事項を記載します。

特にTWELITE Rを用いている場合は、多くの場合電子基板がケースなどを介さず直接外部に触れる状態で使用するため、意図しないショートやノイズなどUSBデバイスが正常に動作しない状態になる場合があります。

この場合は、アプリケーションを終了させUSBデバイスを抜き差しすることで通常は回復します。最悪、USBデバイスの破損やPCの破損も考えられます。

電子基板の取り扱いには十分注意してください。

* 回路の間違い
  * 電源を入れる前には**もう一度**回路を確認してください。
  * 電池の**逆差しや過大電圧**には注意してください。
* 静電気
  * 人感がない電圧であっても半導体の故障になりえます。大掛かりな対応をしなくとも、金属部に触れてから作業する・リストバンド・専用マットなど簡易にできる対応でも相応の効果はあります。
* 金属物などが触れることでのショート
  * 電子基板の近くには金属物がないようにしてください。クリップなどが散らかっているとこれが原因でショートすることもありますし、電池が大放電して加熱する危険な状況も考えられます。
    {% endhint %}

### 1. 事前に MONOSTICK や TWELITE R をPCに差し込んでドライバのセットアップを終了しておく。

デバイスが認識されれば、新たにCOMポートが追加されます。複数あってわかりにくい場合は、抜いた状態でのCOMポートと刺した状態でのCOMポートの様子を観察してみてください。

![デバイスマネージャーの画面例](/files/-LvnknGMeGTCS2xeH2vn)

{% hint style="warning" %}
デバイスマネージャでデバイスの存在が確認できない場合は、[FTDI社のドライバ](https://www.ftdichip.com/Drivers/VCP.htm)導入を行ってみてください。FT232R用のドライバを選択します。
{% endhint %}

### 2. TWE-Programmer.exe を起動する。

{MWSDKインストールディレクトリ}/Tools/TWE-Programmerディレクトリを開き、TWE-Programmer.exe をダブルクリックします。

![](/files/-LvnjtR1KVIOUxoItiCm)

TWE-Programmer.exe を起動すると以下のような画面になります。画面例では、起動前に MONOSTICK を差し込んでいて COM6 に割り当てられています。

![](/files/-Lvnm0V1m7iNTi7o6gfN)

{% hint style="warning" %}
MONOSTICK や TWELITE R が表示されない場合は以下を試してください。

* TWE-Programmer を終了する。
* MONOSTICK や TWELITE RをPCから抜く（取り外す）。
* もう一度MONOSTICK や TWELITE Rを差し込む。
* TWE-Programmer を起動する。

これでもうまくいかない場合は以下も試してください。

* PCから可能な限り他のUSBデバイスを取り外す。
* PCを再起動してからMONOSTICK や TWELITE Rを差し込んでみる。
* FTDI社のドライバを再導入してみる。
  {% endhint %}

### 3. TWE-Programmer 上で BIN ファイルを指定する。

![](/files/-LvnnjzpnCU7VqjhAm1l)

ファイルダイアログから BIN ファイルを指定します。

![BINファイルの選択](/files/-LwLN9PDZeVc0V7HLvr5)

### 4. BINファイルの書き込み

BINファイルの書き込みは、BINファイルを指定すると自動で行われます。

![BINファイル書き込み中](/files/-LvnoiAACFyEsWY4JnAd)

### 5-1. 書き込み成功時

{% hint style="success" %}
書き込みが成功するとTWELITEは自動でリセットして、ターミナルが表示されます（ターミナル接続するにチェックが入っている場合）。

MWSDK添付のアクトファイルは始動時にメッセージを表示します。何か表示されれば書き込みは無事終了し、TWELITE 無線モジュール上のアクトが無事に動作していることが確認できます。
{% endhint %}

![書き込み後（ターミナルが起動し始動メッセージが表示）](/files/-Lvnowd9TQD9XlRpbN0D)

{% hint style="info" %}
ターミナルを開いたままで、アクトを編集して再度書き込みたいときは`Ctrl+Alt+W`キーを押します。
{% endhint %}

### 5-2. 書き込み失敗時

書き込みに失敗したときは、ターミナルは開かず、TWE-Programmerのウインドウ背景がピンク色になります。

![書き込み失敗時の画面例](/files/-LvntEIOLAMH4O2Qnfzw)

{% hint style="warning" %}
書き込みが失敗したときは (再)書き込みボタンを押して、もう一度書き込みを実行してください。

TWE-Programmer が反応しないなど、うまくいかない場合は、以下を実行した上、USBへの接続からやり直してください。

* TWE-Programmer を終了する
* MONOSTICK や TWELITE Rを取り外す

場合によってはPCの再起動を行ってください。
{% endhint %}

{% hint style="warning" %}
TWELITE PAL での書き込み中に、ハードウェア・ウォッチドッグタイマーが作動し書き込みが中断する場合があります。再度、書き込みを行ってください。
{% endhint %}


# tweterm.py

本プログラムは pyftdi (<https://github.com/eblot/pyftdi>) ライブラリサンプルスクリプト pyterm.py に TWELITE 用のファームウェア書き込みスクリプトを組み込んだものです。以下の機能があります。

* TWELITE 用ファームウェアの書き込み　(TWELITE R/MONOSTICK)
* シリアルポートでの動作振る舞いの確認

{% hint style="warning" %}
本スクリプトの OS X での実行には Python3 インタプリタが必要です。コマンドライン環境ならびに Python インタプリタの取り扱いに慣れた方を対象とします。
{% endhint %}

{% hint style="warning" %}
本スクリプトを Linux で動作させるには同等のパッケージ (libusb-dev, pyserial, pyftdi) を用意します。コマンドライン環境ならびに Python インタプリタの取り扱いに慣れた方を対象とします。

* 参考環境：Ubuntu 18.04 (x86-64 64bit), Python 3.6.5&#x20;
  {% endhint %}

## 動作環境・必要パッケージ

* 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) |

## インストール

### パッケージのインストール

{% hint style="warning" %}
以下の手順はパッケージ管理システムのバージョンアップや仕様変更で、そのまま適用できない場合があります。一般の情報などを参考にしてください。
{% endhint %}

{% tabs %}
{% tab title="OS X" %}
以下では、Homebrew を用いた新しく全パッケージをインストールする例をご紹介します。

#### Homebrew

Homebrew をインストールします。

```bash
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
```

#### &#x20; Python3

```bash
$ brew install python3
```

#### &#x20;libusb

```bash
$ brew install libusb
```

#### pyserial

```bash
$ pip3 install pyserial
```

#### pyftdi

```bash
$ pip3 install pyftdi
```

{% endtab %}

{% tab title="Linux" %}
お使いのディストリビューションのパッケージ導入法を調べてください。以下のパッケージが必要です。

* python3.5 以降 (多くの場合導入されています)
* libusb-dev
* pyserial
* pyftdi

パッケージ導入コマンド例。

```bash
$ sudo apt-get install libusb-dev
$ sudo apt-get install python3-pip
$ pip3 install pyserial
$ pip3 install pyftdi
```

{% endtab %}
{% endtabs %}

### 実行

TWELITE SDK をインストールしたディレクトリを `${TWELITESDK}` と記載します。

スクリプトは以下になります。

```bash
${TWELITESDK}/Tools/tweterm/tweterm.py
```

スクリプトに実行許可を与えます。

```bash
$ chmod +x ${TWELITESDK}/Tools/tweterm/tweterm.py
```

必要に応じて環境変数 PATH に追加しておきます。

```bash
$ PATH=${TWELITESDK}/Tools/tweterm:$PATH
```

## 利用方法

### USBドライバのアンロード

{% hint style="warning" %}
libusb と OS のドライバが競合するため、ドライバをアンロード（無効に）しておきます。
{% endhint %}

{% tabs %}
{% tab title="OS X" %}
FTDI 関連のドライバをアンロードします。

```bash
$ sudo kextunload -b com.apple.driver.AppleUSBFTDI
```

{% endtab %}

{% tab title="Linux" %}
ドライバのアンロードは不要です。

{% hint style="warning" %}
エラーが出る場合は、ドライバをアンロードを試してみてください。

```bash
$ sudo rmmod ftdi_sio
$ sudo rmmod usbserial
```

{% endhint %}
{% endtab %}
{% endtabs %}

### &#x20; コマンドパラメータ

| パラメータ                     | 解説                                                   |
| ------------------------- | ---------------------------------------------------- |
| `-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 無線モジュールに入力文字列が送付されます。&#x20;

| 入力キー                           | 解説                                                                  |
| ------------------------------ | ------------------------------------------------------------------- |
| `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`                     | 書式の解釈を停止します。                                                        |

{% hint style="info" %}
書式解釈中は TWELITE からの電文は解釈できた電文のみ表示し、キーボードの入力はエコーバックされますが、アスキー形式の電文が完成した時に TWELITE    に送付されます。
{% endhint %}

## \[実行例]

実行例中では、適宜改行を挟んでいます。

{% hint style="warning" %}
エラーが発生する場合は、シリアルポートの権限の問題かもしれません。root権限(sudo等)で実行します。
{% endhint %}

#### ＜最初にデバイスがリストされるかを確認します＞

```bash
$ tweterm.py -p ftdi:///?
Available interfaces:
  ftdi://ftdi:232:MW19ZZUB/1   (MONOSTICK)
  Please specify the USB device
```

#### ＜ファームウェアを書き込んでターミナルを起動します＞

以下の例では App\_UART (UART 通信アプリ) を書き込み、起動メッセージを確認しています。

```bash
$ tweterm.py -p ftdi://ftdi:232:MW19ZZUB/1 -b 115200 -F ../App_Uart_Master_RED_L1101_V1-2-15.bin 
*** TWE Wrting firmware ... ../App_Uart_Master_RED_L1101_V1-2-15.bin
MODEL: TWEModel.TWELite
SER: 102eebd

 file info: 0f 03 000b
erasing sect #0..#1..#2..
0%..10%..20%..30%..40%..50%..60%..70%..80%..90%..done - 10.24 kb/s
Entering minicom mode

!INF TWE UART APP V1-02-15, SID=0x8102EEBD, LID=0x78
8102EEBD:0> 
```

#### ＜Ctrl+Cを入力すると制御プロンプトが表示されます＞

```
*** r:reset i:+++ A:ASCFMT B:BINFMT x:exit>
```

#### ＜続けて i を入力します。インタラクティブモードに入ります＞

```
*** r:reset i:+++ A:ASCFMT B:BINFMT x:exit>[+ + +]
--- CONFIG/TWE UART APP V1-02-15/SID=0x8102eebd/LID=0x00 -E ---
 a: set Application ID (0x67720103) 
 i: set Device ID (121=0x79) 
... ＜省略＞ 
```

通常通り + + + と３回入力しても同じ結果になります。  &#x20;

#### ＜書式の解釈の例：App\_UART をバイナリ形式に設定しておきます＞

インタラクティブモードで  `m` `B` `Enter`  `S`  と順に入力します。  &#x20;

```
--- CONFIG/TWE UART APP V1-02-15/SID=0x8102eebd/LID=0x00 -E ---
 a: set Application ID (0x67720103) 
 i: set Device ID (121=0x79) 
 c: set Channels (18) 
 x: set RF Conf (3) 
 r: set Role (0x0) 
 l: set Layer (0x1) 
 b: set UART baud (38400) 
 B: set UART option (8N1) 
 m: set UART mode (B)*
 h: set handle name [sadkasldja] 
 C: set crypt mode (0) 
 o: set option bits (0x00000000) 
---
 S: save Configuration
 R: reset to Defaults
 
!INF Write config Success
!INF RESET SYSTEM...
```

#### ＜書式の解釈の例＞

以下の例ではApp\_UARTがバイナリ形式で入出力を行ないます。`Ctrl+C B` を入力します。&#x20;

```
*** r:reset i:+++ A:ASCFMT B:BINFMT x:exit>
[FMT: console ASCII, serial BINARY]
```

この状態では入出力は書式形式となります。キーボードの入力はアスキー形式、 TWELITE からの電文はバイナリ形式として解釈します。

TWELITE からの電文を受け取る例を示します。一番簡単な方法は TWELITE をリセットします。`Ctrl+C r`  を入力します。&#x20;

```
*** r:reset i:+++ A:ASCFMT B:BINFMT x:exit>[RESET TWE]
[dbf1677201030001020f008102eebd0000]
```

出力された `[dbf...]` がTWELITEからの電文で実際は`0xdb 0xf1 0x67...` と続くバイナリ列になります。 &#x20;

反対に TWELITE に電文を送る場合はアスキー書式で入力します。`:7800112233AABBCCDDX` と入力します。 ここでは ペイロードが 0x7800112233AABBCCDD のデータをバイナリ形式で TWELITE に送付しています。直後に応答として `[dba18001]` が戻ってきています。

```
:7800112233AABBCCDDX[dba18001]
```

#### ＜終了します＞

`Ctrl+C Ctrl+C` を入力します。&#x20;

```
*** r:reset i:+++ A:ASCFMT B:BINFMT x:exit>[
[EXIT]
Bye
```


# 新しいプロジェクトの作成

新しいプロジェクトの作成は、すでにあるサンプルアクトのディレクトリを別の名前でコピーし、ファイル名の編集を行います。

{% hint style="warning" %}
コピー先のディレクトリは MWSDK 配下のディレクトリでなくても構いません。ただし、ディレクトリ名に**空白文字や日本語名が含まれてはいけません**。

MWSDK以外のディレクトリにコピーした場合 VS Code のワークスペース定義の一部が機能しなくなります。ワークスペースにmwxライブラリのソースコードディレクトリが追加されている場合は、新たに設定してください。

ライブラリソース設定をしなくても**ビルドには影響はありません**。より深いコード解釈をVSCode上で行い、編集効率を上げるための設定です。
{% endhint %}

プロジェクトのファイル構造は以下のようになっています（ここでは `PingPong` を例に挙げます）。

```
Act_samples
  +-PingPong
    +-PingPong.cpp   : アクトファイル
    +-build          : ビルドディレクトリ
    +-.vscode        : VS Code 用の設定ファイル    
```

この `PingPong` ディレクトリを別の場所（ただしディレクトリ名に日本語や空白が含まない）にコピーします。

```
SomeDir
  +-AlphaBravo
    +-PingPong.cpp -> AplhaBravo.cpp ※ファイル名を変更
    +-build          : ビルドディレクトリ
    +-.vscode        : VS Code 用の設定ファイル
```

編集の必要があるのは、`PingPong.cpp` のファイル名です。これをディレクトリ名と同じ`AlphaBravo.cpp`に変更します。

{% hint style="success" %}
`build\build-BLUE.cmd` を実行してBINファイルが生成されれば完了です（Windows10）。

Linux/WSL/macOS では`make TWELITE=BLUE`を実行して、ビルドが成功するか確認します。
{% endhint %}

{% hint style="info" %}

* ~~プロジェクトのディレクトリ名と同じ名前の .cpp ファイルは必須です。~~ (MWSDK 2020-04 では、任意のファイル名でOKになりました)
* ビルド対象のファイルを追加する場合は build/Makefile を編集します。編集方法は [Makefile の解説](/v0.1.3/install_n_build/makefile)をご覧ください。
  {% endhint %}


# ビルド定義 Makefile

Makefileはbuild/Makefileに格納されています。makeコマンドを実行することで、アクトをビルドするよう予め定義されています。

{% hint style="info" %}
**MWSDK 2020-04 では、プロジェクトディレクトリ中の .cpp ファイルを自動で検出するため、通常は Makefile の修正は不要です。**

**ソースファイルをサブディレクトリに格納するような場合は、編集が必要になります。**

**MWSDK 2019-12では、.cpp ファイルが複数ある場合は、Makefile の編集が必要です。**
{% endhint %}

{% hint style="warning" %}
プロジェクトディレクトリを他の環境からコピーしたあとには、必ず`build/objs_???`ディレクトリを削除してください。他の環境での中間ファイルが残っているとmakeがエラーになります。

`(MWSDK 2020-04)` USE\_APPDEPS=0 を付加して clean してから、改めて make コマンドを実行することでエラーを回避できます。

`$ make USE_APPDEPS=0 TWELITE=BLUE clean`\
`...`\
`$ make TWELITE=BLUE`

{% endhint %}

## makeのパラメータ

###

### TWELITE=

ビルド対象をBLUEまたはREDで指定します。TWELITE BLUEなら`make TWELITE=BLUE`と指定します。

### all

ビルドを実行します。通常は省略して`make TWELITE=BLUE`のように実行します。

### clean

 ビルドの中間ファイルを削除します。`make TWELITE=BLUE clean`のように実行します。

### cleanall

すべての中間ファイルを削除します。`make cleanall`のように実行します。buildディレクトリのobjs\_???ディレクトリをすべて削除するのと同じです。

### USE\_APPDEPS=0 または 1

1 (デフォルト) を設定すると、ファイルの依存関係をもとに、ビルドファイルを決定します。例えば、ヘッダファイルに変更があった場合に関連するソースファイルが再コンパイル対象となります。

0 では依存関係を評価しません。0 に設定した場合、矛盾ある中間ファイルが残っていても makefile がエラーになりません。

## Makefile 定義

アクトの規模に応じて、また、ビヘイビアの定義をする場合には、通常はソースファイルを分割してビルドします。

ビルドファイルの一つは{プロジェクトフォルダ名.cpp}です。

他にファイルを定義する場合は、プロジェクトフォルダの`build/Makefile`を編集します。

```perl
##############################################################################
# Copyright (C) 2019 Mono Wireless Inc. All Rights Reserved.
# Released under MW-SLA-*J,*E (MONO WIRELESS SOFTWARE LICENSE
# AGREEMENT). 
##############################################################################
# USER PROJECT BUILD DEFINITION.
##############################################################################

#####################################################################
### set TWELITE model
TWELITE ?= BLUE
#TWELITE = RED

#####################################################################
### set application version (MUST SET THIS.)
VERSION_MAIN = 0
VERSION_SUB  = 1
VERSION_VAR  = 0

#####################################################################
### set an additional source file
###   the default file name is dirname.

### for C++ files compiled with g++ (must have .cpp suffix)
APPSRC_CXX += myAppBhvParent.cpp
APPSRC_CXX += myAppBhvParent-handlers.cpp
APPSRC_CXX += myAppBhvChild.cpp
APPSRC_CXX += myAppBhvChild-handlers.cpp

### for C files compiled with gcc (must have .c suffix)
#APPSRC += my_c_file.c

### Additional Src/Include Path
# if set, find source files from given dirs.
#
APP_COMMON_SRC_DIR_ADD1 = ../Parent
APP_COMMON_SRC_DIR_ADD2 = ../Child
#APP_COMMON_SRC_DIR_ADD3 = 
#APP_COMMON_SRC_DIR_ADD4 =

#####################################################################
### set misc option for compiler

### C++ flags passed to g++
# e.g. CXXFLAGS += -DMY_DEFS
#CXXFLAGS +=

### C++/C flags passed to g++/gcc
# e.g. CFLAGS += -DMY_DEFS
#CFLAGS +=

### include opts
# e.g. INCFLAGS += -I../my_common_src/
#INCFLAGS +=

### optimize flag (default is -Os, normally no need to change)
#OPTFLAG=-O2

#####################################################################
### must include mwx.mk (the makefile body part.)
MWSDK_PATH?=$(realpath $(MWSDK_ROOT))
include $(MWSDK_PATH)/MkFiles/mwx.mk
#####################################################################
```

上記は[サンプルPAL\_AMB-bihavior](/v0.1.3/api-reference/behavior/pal_amb-behavior)でのMakefileの例です。

### VERSION\_???

```perl
### set application version (MUST SET THIS.)
VERSION_MAIN = 0
VERSION_SUB  = 1
VERSION_VAR  = 0
```

バージョン番号を指定します。ビルド結果ファイル名に反映されます。

コンパイル中は `-DVERSION_MAIN=0` `-DVERSION_SUB=1` `-DVERSION_VAR=0`のように定義として渡されます。

### ソースファイルの追加

{% hint style="success" %}
`(MWSDK 2020-04)` サブディレクトリにファイルを配置しない場合は、追加指定は不要になりました。プロジェクトファイルにある .c .cpp ファイルがすべて追加されます。&#x20;
{% endhint %}

ソースファイルを追加する際に必要なのは`APPSRC_CXX`と`APP_COMMON_SRC_DIR_ADD?`です。

{% hint style="info" %}
サブディレクトリにソースファイルを配置する場合は必ずディレクトリ APP\_COMMON\_SRC\_DIR\_ADD? の指定が必要です。
{% endhint %}

ソースファイル名をAPPSRC\_CXXに追記します。このファイル名には**ディレクトリ名が含まれてはいけません**。サブディレクトリにあるものもディレクトリなしで指定します（つまり同じファイル名がサブディレクトリにある場合は、ビルドが失敗します）

```perl
APPSRC_CXX += myAppBhvParent.cpp
APPSRC_CXX += myAppBhvParent-handlers.cpp
APPSRC_CXX += myAppBhvChild.cpp
APPSRC_CXX += myAppBhvChild-handlers.cpp
```

次にソースファイルがプロジェクトディレクトリ以外の場所に格納されている場合の検索パスを指定します。**最大４つまで**設定できます。

```perl
APP_COMMON_SRC_DIR_ADD1 = ../Parent
APP_COMMON_SRC_DIR_ADD2 = ../Child
```

ディレクトリの指定は**Makefileからの相対パス**になります。

### コンパイル・リンカオプション

その他にもいくつかのオプションをコンパイラ・リンカに渡すことができます。

| 指定         | 内容                                                 |
| ---------- | -------------------------------------------------- |
| `CXXFLAGS` | C++ソースファイルに対してコンパイルオプションを指定します。                    |
| `CFLAGS`   | C/C++ソースファイルに対してコンパイルオプションを指定します。                  |
| `INCFLAGS` | ヘッダファイルのインクルードファイル指定をします。                          |
| `OPTFLAGS` | 特別な理由があって-Os以外のコンパイルオプションを適用したい場合に定義します。           |
| `LDFLAGS`  | リンカオプションを指定します。(上記Makefileのコメントには記述はありませんが指定は可能です) |


# サンプルアクト

Sample Acts

アクトの動作を理解するため、いくつかのサンプルを用意しています。

{% hint style="info" %}
**サンプルは MWSDK をインストールしたディレクトリにある `Act_samples` にあります。**
{% endhint %}

最初に[BRD\_APPTWELITE](/v0.1.3/act_samples/brd_apptwelite)と[Parent\_MONOSTICK](/v0.1.3/act_samples/parent_monostick)の解説に目を通すようにしてください。

### 最新版の入手

{% hint style="info" %}
最新版のコードや MWSDK バージョン間の修正履歴を確認する目的で Github にソース一式を置いています。以下のリンク先を参照してください。

<https://github.com/monowireless/Act_samples>
{% endhint %}

### 共通の記述

アクトのサンプル中で以下の項目は共通の設定項目になり、以下で解説します。

```cpp
const uint32_t APP_ID = 0x1234abcd;
const uint8_t CHANNEL = 13;
const char APP_FOURCHAR[] = "BAT1";
```

{% hint style="info" %}
サンプルアクト共通として以下の設定をしています。

* アプリケーションID `0x1234abcd`
* チャネル `13`

アプリケーションIDとチャネルはともに他のネットワークと混在しないようにする仕組みです。

**アプリケーションIDが異なる者同士は、チャネルが同じであっても混信することはありません**。ただし、別のアプリケーションIDのシステムが頻繁に無線送信しているような場合はその無線送信が妨害となりますので影響は出ます。

チャネルは通信に使う周波数を決めます。TWELITE無線モジュールでは原則として**１６個のチャネル**が利用でき、通常のシステムでは実施しないような極めて例外的な場合を除き、**違うチャネルとは通信できません**。

サンプルアクト共通の仕様として、パケットのペイロード(データ部)の先頭には4バイトの文字列(`APP_FOURCHAR[]`)を格納しています。種別の識別性には1バイトで十分ですが、解説のための記述です。こういった**システム特有の識別子やデータ構造**を含めるのも混信対策の一つです。
{% endhint %}


# BRD\_APPTWELITE

IO通信(標準アプリケーションApp\_Tweliteの基本機能)

App\_TweLite で必要な配線と同じ配線想定したボードサポート `<BRD_APPTWELITE>` を用いたサンプルです。

{% hint style="warning" %}
このサンプルは App\_TweLite と通信できません。
{% endhint %}

## アクトの機能

* M1を読み取り、親機か子機を決める。
* DI1-DI4 の値を読み取ります。Buttons クラスにより、チャタリングの影響を小さくするため、連続で同じ値になったときにはじめて変化が通知されます。変化があったときには通信を行います。
* AI1-AI4 の値を読み取ります。
* DIの変化または１秒おきに、DI1-4, AI1-4, VCC の値を、自身が親機の場合は子機へ、子機の場合は親機宛に送信します。
* 受信したパケットの値に応じで DO1-4, PWM1-4 に設定する。

## アクトの使い方

### 必要なTWELITEと配線例

| 役割 | 例                                                                                                                                           |
| -- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| 親機 | <p><a href="https://mono-wireless.com/jp/products/TWE-Lite-DIP/index.html">TWELITE DIP</a></p><p>最低限 M1=GND, DI1:ボタン, DO1:LEDの配線をしておく。</p>  |
| 子機 | <p><a href="https://mono-wireless.com/jp/products/TWE-Lite-DIP/index.html">TWELITE DIP</a></p><p>最低限 M1=オープン, DI1:ボタン, DO1:LEDの配線をしておく。</p> |

![配線例 (AI1-AI4は省略可)](/files/-Lvo3PkTuRvduUV3vsfL)

## アクトの解説

### インクルード

```cpp
// use twelite mwx c++ template library
#include <TWELITE>
#include <NWK_SIMPLE>
#include <BRD_APPTWELITE>
```

全てのアクトで`<TWELITE>`をインクルードします。ここでは、シンプルネットワーク [`<NWK_SIMPLE>`](/v0.1.3/networks/nwk_simple) とボードサポート `<BRD_APPTWELITE>`をインクルードしておきます。

### 宣言部

```cpp
/*** Config part */
// application ID
const uint32_t APP_ID = 0x1234abcd;

// channel
const uint8_t CHANNEL = 13;

/*** function prototype */
MWX_APIRET transmit();
void receive();

/*** application defs */
const char APP_FOURCHAR[] = "BAT1";
uint8_t u8devid = 0;

uint16_t au16AI[5];
uint8_t u8DI_BM;
```

* サンプルアクト共通宣言
* 長めの処理を関数化しているため、そのプロトタイプ宣言（送信と受信）
* アプリケーション中のデータ保持するための変数

### セットアップ setup()

```cpp
void setup() {
	// 変数の初期化
	for(auto&& x : au16AI) x = 0xFFFF;
	u8DI_BM = 0xFF;

	/*** SETUP section */
	// App_Twelite仕様のボード定義をシステムに登録します。
	auto&& brd = the_twelite.board.use<BRD_APPTWELITE>();

	// check DIP sw settings
	u8devid = (brd.get_M1()) ? 0x00 : 0xFE;

	// setup analogue
	Analogue.setup(true, ANALOGUE::KICK_BY_TIMER0); // setup analogue read (check every 16ms)

	// setup buttons
	Buttons.setup(5); // init button manager with 5 history table.

	// the twelite main class
	the_twelite
		<< TWENET::appid(APP_ID)    // set application ID (identify network group)
		<< TWENET::channel(CHANNEL) // set channel (pysical channel)
		<< TWENET::rx_when_idle();  // open receive circuit (if not set, it can't listen packts from others)

	// Register Network
	auto&& nwksmpl = the_twelite.network.use<NWK_SIMPLE>();
	nwksmpl << NWK_SIMPLE::logical_id(u8devid); // set Logical ID. (0x00 means parent device)

	/*** BEGIN section */
	// start ADC capture
	Analogue.begin(pack_bits(
						BRD_APPTWELITE::PIN_AI1,
						BRD_APPTWELITE::PIN_AI2,
						BRD_APPTWELITE::PIN_AI3,
						BRD_APPTWELITE::PIN_AI4,
				   	PIN_ANALOGUE::VCC)); // _start continuous adc capture.

	// Timer setup
	Timer0.begin(32, true); // 32hz timer

	// start button check
	Buttons.begin(pack_bits(
						BRD_APPTWELITE::PIN_DI1,
						BRD_APPTWELITE::PIN_DI2,
						BRD_APPTWELITE::PIN_DI3,
						BRD_APPTWELITE::PIN_DI4),
					5, 		// history count
					4);  	// tick delta (change is detected by 5*4=20ms consequtive same values)	


	the_twelite.begin(); // start twelite!

	/*** INIT message */
	Serial << "--- BRD_APPTWELITE(" << int(u8devid) << ") ---" << mwx::crlf;
}
```

大まかな流れは、各部の初期設定、各部の開始となっています。

#### the\_twelite

このオブジェクトはTWENETの中核としてふるまいます。

```cpp
auto&& brd = the_twelite.board.use<BRD_APPTWELITE>();
```

ボードの登録（このアクトでは`<BRD_APPTWELITE>`を登録しています）。以下のように use の後に `<>` で登録したいボードの名前を指定します。

ユニバーサル参照(`auto&&`)にて得られた戻り値として、参照型でのボードオブジェクトが得られます。このオブジェクトにはボード特有の操作や定義が含まれます。

```cpp
u8devid = (brd.get_M1()) ? 0x00 : 0xFE;
```

ここではボードオブジェクトを用い`brd.get_M1()`を呼び出すことでM1ピンの設定を読み出します。`1`がスイッチをセットしGND側になっていることを示します。M1 がセットされた場合に`u8devid`を`0x00`に、そうでなければ`0xFE`を指定しています。これはネットワーク上での役割が親機(`0x00`)であるか子機(`0xFE`)あるかを決定します。この値は `<NWK_SIMPLE>`の設定に使用します。

```cpp
	// the twelite main class
	the_twelite
		<< TWENET::appid(APP_ID)    // set application ID (identify network group)
		<< TWENET::channel(CHANNEL) // set channel (pysical channel)
		<< TWENET::rx_when_idle();  // open receive circuit (if not set, it can't listen packts from others)
```

`the_twelite` に設定を反映するには `<<` を用います。

* `TWENET::appid(APP_ID)` アプリケーションIDの指定
* `TWENET::channel(CHANNEL)` チャネルの指定
* `TWENET::rx_when_idle()` 受信回路をオープンにする指定

{% hint style="info" %}
`<<, >>`演算子は本来ビットシフト演算子ですが、その意味合いと違った利用とはなります。MWXライブラリ内では、C++標準ライブラリでの入出力利用に倣ってライブラリ中では上記のような設定やシリアルポートの入出力で利用しています。

```cpp
// 以下の記述は MWX ライブラリでは利用できません。
#include <iostream>
std::cout << "hello world" << std::endl;
```

{% endhint %}

次にネットワークを登録します。

```cpp
auto&& nwksmpl = the_twelite.network.use<NWK_SIMPLE>();
nwksmpl << NWK_SIMPLE::logical_id(u8devid);
```

1行目は、ボードの登録と同じ書き方で `<>` には `<NWK_SIMPLE>`を指定します。

2行目は、`<NWK_SIMPLE>`の設定です。先ほどボードから得たM1ピンの状態によって親機アドレス(`0x00`)か子機アドレス(`0xFE`)を格納した`u8devid`を指定します。

```cpp
the_twelite.begin(); // start twelite!
```

`setup()` 関数の末尾で `the_twelite.begin()` を実行しています。

#### Analogue

ADC(アナログディジタルコンバータ)を取り扱うクラスオブジェクトです。

```cpp
Analogue.setup(true, ANALOGUE::KICK_BY_TIMER0);
```

初期化`Analogue.setup()`で行います。パラメータの`true`はADC回路の安定までその場で待つ指定です。2番目のパラメータは、ADCの開始をTimer0に同期して行う指定です。

```cpp
	Analogue.begin(pack_bits(
						BRD_APPTWELITE::PIN_AI1,
						BRD_APPTWELITE::PIN_AI2,
						BRD_APPTWELITE::PIN_AI3,
						BRD_APPTWELITE::PIN_AI4,
				   	PIN_ANALOGUE::VCC));
```

ADCを開始するには`Analogue.begin()`を呼びます。パラメータはADC対象のピンに対応するビットマップです。

ビットマップを指定するのに`pack_bits()`関数を用います。可変数引数の関数で、各引数には１を設定するビット位置を指定します。例えば`pack_bits(1,3,5)`なら２進数で `101010`の値が戻ります。この関数は`constexpr`指定があるため、パラメータが定数のみであれば定数に展開されます。

パラメータと指定される`BRD_APPTWELITE::`には`PIN_AI1..4`が定義されています。App\_Tweliteで用いるAI1..AI4に対応します。AI1=ADC1, AI2=DIO0, AI3=ADC2, AI4=DIO2 と割り当てられています。`PIN_ANALOGUE::`にはADCで利用できるピンの一覧が定義されています。

{% hint style="info" %}
初回を除き ADC の開始は、割り込みハンドラ内で行います。
{% endhint %}

#### Buttons

DIO (ディジタル入力) の値の変化を検出します。Buttonsでは、メカ式のボタンのチャタリング（摺動）の影響を軽減するため、一定回数同じ値が検出されてから、値の変化とします。

```cpp
Buttons.setup(5);
```

初期化は `Buttons.setup()`で行います。パラメータの 5 は、値の確定に必要な検出回数ですが、設定可能な最大値を指定します。内部的にはこの数値をもとに内部メモリの確保を行っています。

```cpp
Buttons.begin(pack_bits(
						BRD_APPTWELITE::PIN_DI1,
						BRD_APPTWELITE::PIN_DI2,
						BRD_APPTWELITE::PIN_DI3,
						BRD_APPTWELITE::PIN_DI4),
					5, 		// history count
					4);  	// tick delta
```

開始は `Buttons.begin()` で行います。１番目のパラメータは検出対象のDIOです。`BRD_APPTWELITE::`に定義される`PIN_DI1-4` (DI1-DI4) を指定しています。２番めのパラメータは状態を確定するのに必要な検出回数です。３番めのパラメータは検出間隔です。`4`を指定しているので4msごとに5回連続で同じ値が検出できた時点で、HIGH, LOWの状態が確定します。

{% hint style="info" %}
ButtonsでのDIO状態の検出はイベントハンドラで行います。イベントハンドラは、割り込み発生後にアプリケーションループで呼ばれるため割り込みハンドラに比べ遅延が発生します。
{% endhint %}

#### Timer0

```cpp
Timer0.begin(32, true); // 32hz timer
```

App\_Twelite ではアプリケーションの制御をタイマー起点で行っているため、このアクトでも同じようにタイマー割り込み・イベントを動作させます。もちろん1msごとに動作しているシステムのTickTimerを用いても構いません。

上記の例の１番目のパラメータはタイマーの周波数で32Hzを指定しています。２番目のパラメータを`true`にするとソフトウェア割り込みが有効になります。

`Timer0.begin()`を呼び出したあと、タイマーが稼働します。

#### Serial

Serial オブジェクトは、初期化や開始手続きなく利用できます。

```cpp
Serial << "--- BRD_APPTWELITE("
       << int(u8devid) 
       << ") ---" << mwx::crlf;
```

上記の例では `"--- BRD_APPTWELITE(254) ---\r\n"` といった文字列を表示します。ここでは１０進数の数値文字列として表示するために`int(u8devid)`としています。`int`型でない場合は出力の意味合いが変わりバイトの書き出しになるので注意してください。

### ループ loop()

ループ関数は TWENET ライブラリのメインループからコールバック関数として呼び出されます。ここでは、利用するオブジェクトが available になるのを待って、その処理を行うのが基本的な記述です。ここではアクトで使用されているいくつかのオブジェクトの利用について解説します。

{% hint style="warning" %}
TWENET ライブラリのメインループは、事前にFIFOキューに格納された受信パケットや割り込み情報などをイベントとして処理し、そののち`loop()`が呼び出されます。`loop()`を抜けた後は CPU が DOZE モードに入り、低消費電流で新たな割り込みが発生するまでは待機します。

したがってCPUが常に稼働していることを前提としたコードはうまく動作しません。
{% endhint %}

```cpp
/*** loop procedure (called every event) */
void loop() {
	if (Buttons.available()) {
		uint32_t bp, bc;
		Buttons.read(bp, bc);

		u8DI_BM = uint8_t(collect_bits(bp, 
							BRD_APPTWELITE::PIN_DI4,   // bit3
							BRD_APPTWELITE::PIN_DI3,   // bit2
							BRD_APPTWELITE::PIN_DI2,   // bit1
							BRD_APPTWELITE::PIN_DI1)); // bit0

		transmit();
	}

	if (Analogue.available()) {
		au16AI[0] = Analogue.read(PIN_ANALOGUE::VCC);
		au16AI[1] = Analogue.read_raw(BRD_APPTWELITE::PIN_AI1);
		au16AI[2] = Analogue.read_raw(BRD_APPTWELITE::PIN_AI2);
		au16AI[3] = Analogue.read_raw(BRD_APPTWELITE::PIN_AI3);
		au16AI[4] = Analogue.read_raw(BRD_APPTWELITE::PIN_AI4);
	}

	if (Timer0.available()) {
		static uint8_t u16ct;
		u16ct++;

		if (u8DI_BM != 0xFF && au16AI[0] != 0xFFFF) { // finished the first capture
			if ((u16ct % 32) == 0) { // every 32ticks of Timer0
				transmit();
			}
		}
	}

	// receive RF packet.
  if (the_twelite.receiver.available()) {
		receive();
	}
}
```

#### Buttons

DIO(ディジタルIO)の入力変化を検出したタイミングで available になり、`Buttons.read()`により読み出します。

```cpp
	if (Buttons.available()) {
		uint32_t bp, bc;
		Buttons.read(bp, bc);
```

１番目のパラメータは、現在のDIOのHIGH/LOWのビットマップで、bit0から順番にDIO0,1,2,.. と並びます。例えば DIO12 であれば `bp & (1UL << 12)` を評価すれば HIGH / LOW が判定できます。ビットが1になっているものがHIGHになります。

{% hint style="info" %}
初回のIO状態確定時は MSB (bit31) に１がセットされます。スリープ復帰時も初回の確定処理を行います。
{% endhint %}

次にビットマップから値を取り出して`u8DI_BM`に格納しています。ここではMWXライブラリで用意した`collect_bits()`関数を用いています。

```cpp
u8DI_BM = uint8_t(collect_bits(bp, 
		BRD_APPTWELITE::PIN_DI4,   // bit3
		BRD_APPTWELITE::PIN_DI3,   // bit2
		BRD_APPTWELITE::PIN_DI2,   // bit1
		BRD_APPTWELITE::PIN_DI1)); // bit0

/* collect_bits は以下の処理を行います。
u8DI_BM = 0;
if (bp & (1UL << BRD_APPTWELITE::PIN_DI1)) u8DI_BM |= 1;
if (bp & (1UL << BRD_APPTWELITE::PIN_DI2)) u8DI_BM |= 2;
if (bp & (1UL << BRD_APPTWELITE::PIN_DI3)) u8DI_BM |= 4;
if (bp & (1UL << BRD_APPTWELITE::PIN_DI4)) u8DI_BM |= 8;
*/
```

`collect_bits()` は、上述の`pack_bits()`と同様のビット位置の整数値を引数とします。可変数引数の関数で、必要な数だけパラメータを並べます。上記の処理では bit0 は DI1、bit1 は DI2、bit2 は DI3、bit3 は DI4の値としてu8DI\_BMに格納しています。

App\_Twelite では、DI1から4に変化があった場合に無線送信しますので、`Buttons.available()`を起点に送信処理を行います。`transmit()`処理の内容は後述します。

```cpp
transmit();
```

#### Analogue

ADCのアナログディジタル変換が終了した直後の`loop()`で available になります。次の ADC が開始するまでは、データは直前に取得されたものとして読み出すことが出来ます。

```cpp
if (Analogue.available()) {
	au16AI[0] = Analogue.read(PIN_ANALOGUE::VCC);
	au16AI[1] = Analogue.read_raw(BRD_APPTWELITE::PIN_AI1);
	au16AI[2] = Analogue.read_raw(BRD_APPTWELITE::PIN_AI2);
	au16AI[3] = Analogue.read_raw(BRD_APPTWELITE::PIN_AI3);
	au16AI[4] = Analogue.read_raw(BRD_APPTWELITE::PIN_AI4);
}
```

ADC値を読むには `Analogue.read()` または `Analogue.read_raw()` メソッドを用います。`read()`はmVに変換した値、`read_raw()`は 0..1023 のADC値となります。パラメータにはADCのピン番号を指定します。ADCのピン番号は`PIN_ANALOGUE::`や`BRD_APPTWELITE::`に定義されているので、こちらを利用しています。

{% hint style="warning" %}
周期的に実行されるADCの値は、タイミングによってはavailable通知前のより新しい値が読み出されることがあります。

このアクトでは32Hzと比較的ゆっくりの周期で処理しているため、available判定直後に処理すれば問題にはなりませんが、変換周期が短い場合、`loop()`中で比較的長い時間のかかる処理をしている場合は注意が必要です。

`Analogue`には、変換終了後に割り込みハンドラ内から呼び出されるコールバック関数を指定することが出来ます。例えば、このコールバック関数からFIFOキューに値を格納する処理を行い、アプリケーションループ内ではキューの値を逐次読み出すといった非同期処理を行います。
{% endhint %}

#### Timer0

`Timer0`は32Hzで動作しています。タイマー割り込みが発生直後の `loop()` で available になります。つまり、秒３２回の処理をします。ここでは、ちょうど１秒になったところで送信処理をしています。

```cpp
if (Timer0.available()) {
	static uint8_t u16ct;
	u16ct++;

	if (u8DI_BM != 0xFF && au16AI[0] != 0xFFFF) { // finished the first capture
		if ((u16ct % 32) == 0) { // every 32ticks of Timer0
			transmit();
		}
	}
}
```

AppTweliteでは約１秒おきに定期送信を行っています。`Timer0`がavailableになったときに`u16ct`をインクリメントします。このカウンタ値をもとに、３２回カウントが終われば`transmit()`を呼び出し無線パケットを送信しています。

`u8DI_BM`と`au16AI[]`の値判定は、初期化直後かどうかの判定です。まだDI1..DI4やAI1..AI4の値が格納されていない場合は何もしません。

#### the\_twelite.receiver

受信パケットがある場合の処理です。

```cpp
if (the_twelite.receiver.available()) {
	receive();
}
```

無線パケットを受信後の`loop()`では`the_twelite.receiver.available()`が`true`を返します。受信パケットの取り扱いについては `receive()` 関数の解説で行います。

{% hint style="warning" %}
TWENETがパケットを受信してから時間をたってからの受信パケットの参照は安全ではありません。

内部のデータが新しい受信パケットの内容に上書きされ、この新しい内容を参照することになります。availableになってから速やかに処理するように`loop()`を記述してください。内部的には、１パケット分余分に保持できる余裕はあります。
{% endhint %}

### transmit()

無線パケットの送信要求をTWENETに行う関数です。本関数が終了した時点では、まだ無線パケットの処理は行われません。実際に送信が完了するのは、送信パラメータ次第ですが、数ms後以降になります。ここでは代表的な送信要求方法について解説します。

```cpp
MWX_APIRET transmit() {
	if (auto&& pkt = the_twelite.network.use<NWK_SIMPLE>().prepare_tx_packet()) {
		Serial << "..DI=" << format("%04b ", u8DI_BM);
		Serial << format("ADC=%04d/%04d/%04d/%04d ", au16AI[1], au16AI[2], au16AI[3], au16AI[4]);
		Serial << "Vcc=" << format("%04d ", au16AI[0]);
		Serial << " --> transmit" << mwx::crlf;

		// set tx packet behavior
		pkt << tx_addr(u8devid == 0 ? 0xFE : 0x00)  // 0..0xFF (LID 0:parent, FE:child w/ no id, FF:LID broad cast), 0x8XXXXXXX (long address)
			<< tx_retry(0x1) // set retry (0x1 send two times in total)
			<< tx_packet_delay(0,50,10); // send packet w/ delay (send first packet with randomized delay from 100 to 200ms, repeat every 20ms)

		// prepare packet payload
		pack_bytes(pkt.get_payload() // set payload data objects.
			, make_pair(APP_FOURCHAR, 4) // string should be paired with length explicitly.
			, uint8_t(u8DI_BM)
		);

		for (auto&& x : au16AI) {
			pack_bytes(pkt.get_payload(), uint16_t(x)); // adc values
		}
		
		// do transmit 
		return pkt.transmit();
	}
	return MWX_APIRET(false, 0);
}
```

####

#### 関数プロトタイプ

```
MWX_APIRET transmit()
```

`MWX_APIRET`は`uint32_t`型のデータメンバを持つ戻り値を取り扱うクラスです。MSB(bit31)が成功失敗、それ以外が戻り値として利用するものです。

#### ネットワークオブジェクトとパケットオブジェクトの取得

```cpp
	if (auto&& pkt = the_twelite.network.use<NWK_SIMPLE>().prepare_tx_packet()) {
```

ネットワークオブジェクトを`the_twelite.network.use<NWK_SIMPLE>()`で取得します。そのオブジェクトを用いて`.prepare_tx_packet()`により`pkt`オブジェクトを取得します。

ここではif文の条件判定式の中で宣言しています。宣言した`pkt`オブジェクトはif節の終わりまで有効です。pktオブジェクトはbool型の応答をし、ここではTWENETの送信要求キューに空きがあって送信要求を受け付ける場合に`true`、空きがない場合に`false`となります。

#### パケットの送信設定

```cpp
pkt << tx_addr(u8devid == 0 ? 0xFE : 0x00)  // 0..0xFF (LID 0:parent, FE:child w/ no id, FF:LID broad cast), 0x8XXXXXXX (long address)
		<< tx_retry(0x1) // set retry (0x3 send four times in total)
		<< tx_packet_delay(0,50,10); // send packet w/ delay (send first packet with randomized delay from 100 to 200ms, repeat every 20ms)
```

パケットの設定は`the_twelite`の初期化設定のように`<<`演算子を用いて行います。

* `tx_addr()` パラメータに送信先アドレスを指定します。`0x00`なら自分が子機で親機宛に、`0xFE`なら自分が親機で任意の子機宛のブロードキャストという意味です。
* `tx_retry()` パラメータに再送回数を指定します。例の`1`は再送回数が１回、つまり合計２回パケットを送ります。無線パケット１回のみの送信では条件が良くても数％程度の失敗はあります。
* `tx_packet_delay()` 送信遅延を設定します。一つ目のパラメータは、送信開始までの最低待ち時間、２番目が最長の待ち時間です。この場合は送信要求を発行後におよそ0msから50msの間で送信を開始します。３番目が再送間隔です。最初のパケットが送信されてから10ms置きに再送を行うという意味です。

#### パケットのデータペイロード

ペイロードは積載物という意味ですが、無線パケットでは「送りたいデータ本体」という意味でよく使われます。無線パケットのデータにはデータ本体以外にもアドレス情報などいくつかの補助情報が含まれます。

送受信を正しく行うために、データペイロードのデータ並び順を意識するようにしてください。ここでは以下のようなデータ順とします。このデータ順に合わせてデータペイロードを構築します。

```cpp
# 先頭バイトのインデックス: データ型 : バイト数 : 内容

00: uint8_t[4] : 4 : ４文字識別子
04: uint8_t    : 1 : DI1..4のビットマップ
06: uint16_t   : 2 : Vccの電圧値
08: uint16_t   : 2 : AI1のADC値 (0..1023)
10: uint16_t   : 2 : AI2のADC値 (0..1023)
12: uint16_t   : 2 : AI3のADC値 (0..1023)
14: uint16_t   : 2 : AI4のADC値 (0..1023)
```

{% hint style="info" %}
データペイロードには90バイト格納できます（実際にはあと数バイト格納できます）。

IEEE802.15.4の無線パケットの１バイトは貴重です。できるだけ節約して使用することを推奨します。１パケットで送信できるデータ量に限りがあります。パケットを分割する場合は分割パケットの送信失敗などを考慮する必要がありコストは大きくつきます。また１バイト余分に送信するのに、およそ16μ秒×送信時の電流に相当するエネルギーが消費され、特に電池駆動のアプリケーションには大きく影響します。

上記の例は、解説のためある程度の妥協をしています。節約を考える場合 00: の識別子は１バイトの簡単なものにすべきですし、Vccの電圧値は８ビットに丸めてもかまわないでしょう。また各AI1..AI4の値は10bitで、合計40bit=5バイトに対して6バイト使用しています。
{% endhint %}

上記のデータペイロードのデータ構造を実際に構築してみます。データペイロードは `pkt.get_payload()` により `simplbuf<uint8_t>` 型のコンテナとして参照できます。このコンテナに上記の仕様に基づいてデータを構築します。

```cpp
auto&& payl = pkt.get_payload();
payl.reserve(16); // １６バイトにリサイズ
payl[00] = APP_FOURCHAR[0];
payl[01] = APP_FOURCHAR[1];
...
payl[08] = (au16AI[0] & 0xFF00) >> 8; //Vcc
payl[09] = (au16AI[0] & 0xFF);
...
payl[14] = (au16AI[4] & 0xFF00) >> 8; // AI4
payl[15] = (au16AI[4] & 0xFF);
```

上記のように記述できますがMWXライブラリでは、データペイロード構築のための補助関数`pack_bytes()`を用意しています。

```cpp
// prepare packet payload
pack_bytes(pkt.get_payload() // set payload data objects.
	, make_pair(APP_FOURCHAR, 4) // string should be paired with length explicitly.
	, uint8_t(u8DI_BM)
);

for (auto&& x : au16AI) {
	pack_bytes(pkt.get_payload(), uint16_t(x)); // adc values
}
```

`pack_bytes`の最初のパラメータはコンテナを指定します。この場合は`pkt.get_payload()`です。

そのあとのパラメータは可変数引数で`pack_bytes`で対応する型の値を必要な数だけ指定します。`pack_bytes`は内部で`.push_back()`メソッドを呼び出して末尾に指定した値を追記していきます。

3行目の`make_pair()`は標準ライブラリの関数で`std::pair`を生成します。文字列型の混乱（具体的にはペイロードの格納時にヌル文字を含めるか含めないか）を避けるための指定です。`make_pair()`の1番目のパラメータに文字列型(`char*`や`uint8_t*`型、`uint8_t[]`など)を指定します。2番目のパラメータはペイロードへの格納バイト数です。

4行目は`uint8_t`型でDI1..DI4のビットマップを書き込みます。

7-9行目では`au16AI`配列の値を順に書き込んでいます。この値は`uint16_t`型で2バイトですが、ビッグエンディアンの並びで書き込みます。

{% hint style="info" %}
7行目のfor文はC++で導入された**範囲for**文です。サイズのわかっている配列や`begin(), end()`によるイテレータによるアクセスが可能なコンテナクラスなどは、この構文が使用できます。au16AIの型もコンパイル時に判定できるため `auto&&` （ユニバーサル参照）で型の指定も省略してます。

通常のfor文に書き換えると以下のようになります。

```cpp
for(int i = 0; i < sizeof(au16AI)/sizeof(uint16_t)); i++) {
  pack_bytes(pkt.get_payload(), au16AI[i]);
}
```

{% endhint %}

これでパケットの準備は終わりです。あとは、送信要求を行います。

```cpp
return pkt.transmit();
```

パケットを送信するには`pkt`オブジェクトの`pkt.transmit()`メソッドを用います。戻り値として`MWX_APIRET`型を返していますが、このアクトでは使っていません。

{% hint style="info" %}
戻り値には、要求の成功失敗の情報と要求に対応する番号が格納されています。送信完了まで待つ処理を行う場合は、この戻り値の値を利用します。
{% endhint %}

### receive()

パケットの受信が確認できた(つまり`the_twelite.receiver.available()`が`true`になった)ときの処理です。ここでは、相手方から伝えられたDI1..DI4の値とAI1..AI4の値を、自身のDO1..DO4とPWM1..PWM4に設定します。

```cpp
void receive() {	
	auto&& rx = the_twelite.receiver.read();

  // expand the packet payload
	char fourchars[5]{};
	auto&& np = expand_bytes(rx.get_payload().begin(), rx.get_payload().end()
		, make_pair((uint8_t*)fourchars, 4)  // 4bytes of msg
    );

	// check header
	if (strncmp(APP_FOURCHAR, fourchars, 4)) { return; }

	// read rest of payload
	uint8_t u8DI_BM_remote = 0xff;
	uint16_t au16AI_remote[5];
	expand_bytes(np, rx.get_payload().end()
		, u8DI_BM_remote
		, au16AI_remote[0]
		, au16AI_remote[1]
		, au16AI_remote[2]
		, au16AI_remote[3]
		, au16AI_remote[4]
	);

	Serial << format("DI:%04b", u8DI_BM_remote & 0x0F);
	for (auto&& x : au16AI_remote) {
		Serial << format("/%04d", x);
	}
	Serial << mwx::crlf;

	// set local DO
	digitalWrite(BRD_APPTWELITE::PIN_DO1, (u8DI_BM_remote & 1) ? HIGH : LOW);
	digitalWrite(BRD_APPTWELITE::PIN_DO2, (u8DI_BM_remote & 2) ? HIGH : LOW);
	digitalWrite(BRD_APPTWELITE::PIN_DO3, (u8DI_BM_remote & 4) ? HIGH : LOW);
	digitalWrite(BRD_APPTWELITE::PIN_DO4, (u8DI_BM_remote & 8) ? HIGH : LOW);

	// set local PWM : duty is set 0..1024, so 1023 is set 1024.
	Timer1.change_duty(au16AI_remote[1] == 1023 ? 1024 : au16AI_remote[1]);
	Timer2.change_duty(au16AI_remote[2] == 1023 ? 1024 : au16AI_remote[2]);
	Timer3.change_duty(au16AI_remote[3] == 1023 ? 1024 : au16AI_remote[3]);
	Timer4.change_duty(au16AI_remote[4] == 1023 ? 1024 : au16AI_remote[4]);
}

```

まず受信パケットのデータ`rx`を取得します。`rx`からアドレス情報やデータペイロードにアクセスします。

```cpp
auto&& rx = the_twelite.receiver.read();
```

次の行では、受信パケットデータには、送信元のアドレス（32bitのロングアドレスと8bitの論理アドレス）などの情報を参照しています。

```cpp
Serial << format("..receive(%08x/%d) : ",
   rx.get_addr_src_long(), rx.get_addr_src_lid());
```

{% hint style="info" %}
`<NWK_SIMPLE>`では、8bitの論理IDと32bitのロングアドレスの２種類が常にやり取りされます。送り先を指定する場合はロングアドレスか論理アドレスのいずれかを指定します。受信時には両方のアドレスが含まれます。
{% endhint %}

MWXライブラリには`transmit()`の時に使った`pack_bytes()`の対になる関数`expand_bytes()`が用意されています。

```cpp
char fourchars[5]{};
auto&& np = expand_bytes(rx.get_payload().begin(), rx.get_payload().end()
	, make_pair((uint8_t*)fourchars, 4)  // 4bytes of msg
  );
```

1行目ではデータ格納のための`char`型の配列を宣言しています。サイズが5バイトなのは末尾にヌル文字を含め、文字出力などでの利便性を挙げるためです。末尾の`{}`は初期化の指定で、5バイト目を0にすれば良いのですが、ここでは配列全体をデフォルトの方法で初期化、つまり`0`にしています。

2行目で`expand_bytes()`により4バイト文字列を取り出しています。パラメータにコンテナ型を指定しない理由は、この続きを読み出すための読み出し位置を把握する必要があるためです。1番目のパラメータでコンテナの先頭イテレータ（`uint8_t*`ポインタ）を指定します。`.begin()`メソッドにより取得できます。2番目のパラメータはコンテナの末尾の次を指すイテレータで`.end()`メソッドで取得できます。2番目はコンテナの末尾を超えた読み出しを行わないようにするためです。

3番目に読み出す変数を指定しますが、ここでも`make_pair`によって文字列配列とサイズのペアを指定します。

{% hint style="info" %}
このアクトでは、パケット長が間違っていた場合などのエラーチェックを省いています。チェックを厳格にしたい場合は、`expand_bytes()`の戻り値により判定してください。

`expand_bytes()`の戻り値は `uint8_t*` ですが、末尾を超えたアクセスの場合は`nullptr（ヌルポインタ）`を戻します。
{% endhint %}

読み出した4バイト文字列の識別子が、このアクトで指定した識別子と異なる場合は、このパケットを処理しません。

```cpp
if (strncmp(APP_FOURCHAR, fourchars, 4)) { return; }
```

{% hint style="info" %}
TWENETではアプリケーションIDと物理的な無線チャネルが合致する場合は、どのアプリケーションもたとえ種別が違ったとしても、受信することが出来ます。他のアプリケーションで作成したパケットを意図しない形で受信しない目的で、このような識別子やデータペイロードの構造などのチェックを行い、偶然の一致が起きないように対処することを推奨します。

シンプルネットワーク`<NWK_SIMPLE>`でのパケット構造の要件も満足する必要があるため、シンプルネットワークを使用しない他のアプリケーションが同じ構造のパケットを定義しない限り（非常にまれと思われます）、パケットの混在受信は発生しません。
{% endhint %}

続いて、データ部分の取得です。DI1..DI4の値とAI1..AI4の値を別の変数に格納します。

```cpp
	// read rest of payload
	uint8_t u8DI_BM_remote = 0xff;
	uint16_t au16AI_remote[5];
	expand_bytes(np, rx.get_payload().end()
		, u8DI_BM_remote
		, au16AI_remote[0]
		, au16AI_remote[1]
		, au16AI_remote[2]
		, au16AI_remote[3]
		, au16AI_remote[4]
	);
```

先ほどの`expand_bytes()`の戻り値`np`を1番目のパラメータにしています。先に読み取った4バイト文字列識別子の次から読み出す指定です。2番目のパラメータは同様です。

3番目以降のパラメータは**データペイロードの並びに一致した型**の変数を、**送り側のデータ構造と同じ順番**で並べています。この処理が終われば、指定した変数にペイロードから読み出した値が格納されます。

確認のためシリアルポートへ出力します。

```cpp
Serial << format("DI:%04b", u8DI_BM_remote & 0x0F);
for (auto&& x : au16AI_remote) {
	Serial << format("/%04d", x);
}
Serial << mwx::crlf;
```

数値のフォーマット出力が必要になるので`format()`を用いています。`>>`演算子向けに`printf()`と同じ構文を利用できるようにしたヘルパークラスですが、引数の数が４つまでに制限されています。（`Serial.printfmt()`には引数の数の制限がありません。）

１行目の `"DI:%04b"` は`"DI:0010"`のようにDI1..DI4のビットマップを４桁で表示します。３行目の`"/%04d"`は`"/3280/0010/0512/1023/1023"`のように Vcc/AI1..AI4の値を順に整数で出力します。５行目の`mwx::crlf`は改行文字列を出力します。

これで必要なデータの展開が終わったので、あとはボード上のDO1..DO4とPWM1..PWM4の値を変更します。

```cpp
// set local DO
digitalWrite(BRD_APPTWELITE::PIN_DO1, (u8DI_BM_remote & 1) ? HIGH : LOW);
digitalWrite(BRD_APPTWELITE::PIN_DO2, (u8DI_BM_remote & 2) ? HIGH : LOW);
digitalWrite(BRD_APPTWELITE::PIN_DO3, (u8DI_BM_remote & 4) ? HIGH : LOW);
digitalWrite(BRD_APPTWELITE::PIN_DO4, (u8DI_BM_remote & 8) ? HIGH : LOW);

// set local PWM : duty is set 0..1024, so 1023 is set 1024.
Timer1.change_duty(au16AI_remote[1] == 1023 ? 1024 : au16AI_remote[1]);
Timer2.change_duty(au16AI_remote[2] == 1023 ? 1024 : au16AI_remote[2]);
Timer3.change_duty(au16AI_remote[3] == 1023 ? 1024 : au16AI_remote[3]);
Timer4.change_duty(au16AI_remote[4] == 1023 ? 1024 : au16AI_remote[4]);
```

`digitalWrite()`はディジタル出力の値を変更します。１番目のパラメータはピン番号で、２番目は`HIGH`(Vccレベル)か`LOW`(GNDレベル)を指定します。

`Timer?.change_duty()`はPWM出力のデューティ比を変更します。パラメータにデューティ比 0..1024 を指定します。最大値が1023でないことに注意してください（ライブラリ内で実行される割り算のコストが大きいため2のべき乗である1024を最大値としています）。`0`にするとGNDレベル、`1024`にするとVccレベル相当の出力になります。


# PingPong

2台のシリアル接続しているTWELITEの片方からPING(ピン)の無線パケットを送信すると、他方からPONG(ポン)の無線パケットが返ってきます。

## アクトの使い方

### 必要なTWELITE

いずれかを２台。

* [MONOSTICK BLUE または RED](https://mono-wireless.com/jp/products/MoNoStick/index.html)
* [TWELITE R](https://mono-wireless.com/jp/products/TWE-LITE-R/index.html) でUART接続されている[TWELITE DIP](https://mono-wireless.com/jp/products/TWE-Lite-DIP/index.html)など

## アクトの解説

### インクルード

```cpp
// use twelite mwx c++ template library
#include <TWELITE>
#include <NWK_SIMPLE>
```

全てのアクトで`<TWELITE>`をインクルードします。ここでは、シンプルネットワーク [`<NWK_SIMPLE>`](/v0.1.3/networks/nwk_simple) をインクルードしておきます。

### 宣言部

```cpp
// application ID
const uint32_t APP_ID = 0x1234abcd;

// channel
const uint8_t CHANNEL = 13;

// DIO pins
const uint8_t PIN_BTN = 12;

/*** function prototype */
void vTransmit(const char* msg, uint32_t addr);

/*** application defs */
// packet message
const int MSG_LEN = 4;
const char MSG_PING[] = "PING";
const char MSG_PONG[] = "PONG";
```

* サンプルアクト共通宣言
* 長めの処理を関数化しているため、そのプロトタイプ宣言（送信と受信）
* アプリケーション中のデータ保持するための変数

### セットアップ setup()

```cpp
void setup() {
	/*** SETUP section */
	Buttons.setup(5); // init button manager with 5 history table.
	Analogue.setup(true, 50); // setup analogue read (check every 50ms)

	// the twelite main class
	the_twelite
		<< TWENET::appid(APP_ID)    // set application ID (identify network group)
		<< TWENET::channel(CHANNEL) // set channel (pysical channel)
		<< TWENET::rx_when_idle();  // open receive circuit (if not set, it can't listen packts from others)

	// Register Network
	auto&& nwksmpl = the_twelite.network.use<NWK_SIMPLE>();
	nwksmpl << NWK_SIMPLE::logical_id(0xFE) // set Logical ID. (0xFE means a child device with no ID)
	        << NWK_SIMPLE::repeat_max(3);   // can repeat a packet up to three times. (being kind of a router)

	/*** BEGIN section */
	Buttons.begin(pack_bits(PIN_BTN), 5, 10); // check every 10ms, a change is reported by 5 consequent values.
	Analogue.begin(pack_bits(PIN_ANALOGUE::A1, PIN_ANALOGUE::VCC)); // _start continuous adc capture.

	the_twelite.begin(); // start twelite!

	/*** INIT message */
	Serial << "--- PingPong sample (press 't' to transmit) ---" << mwx::crlf;
}
```

大まかな流れは、各部の初期設定、各部の開始となっています。

#### the\_twelite

このオブジェクトはTWENETを操作するための中核クラスオブジェクトです。

```cpp
	// the twelite main class
	the_twelite
		<< TWENET::appid(APP_ID)    // set application ID (identify network group)
		<< TWENET::channel(CHANNEL) // set channel (pysical channel)
		<< TWENET::rx_when_idle();  // open receive circuit (if not set, it can't listen packts from others)
```

`the_twelite` に設定を反映するには `<<` を用います。

* `TWENET::appid(APP_ID)` アプリケーションIDの指定
* `TWENET::channel(CHANNEL)` チャネルの指定
* `TWENET::rx_when_idle()` 受信回路をオープンにする指定

{% hint style="info" %}
`<<, >>`演算子は本来ビットシフト演算子ですが、その意味合いと違った利用とはなります。MWXライブラリ内では、C++標準ライブラリでの入出力利用に倣ってライブラリ中では上記のような設定やシリアルポートの入出力で利用しています。

```cpp
// 以下の記述は MWX ライブラリでは利用できません。
#include <iostream>
std::cout << "hello world" << std::endl;
```

{% endhint %}

次にネットワークを登録します。

```cpp
auto&& nwksmpl = the_twelite.network.use<NWK_SIMPLE>();
nwksmpl << NWK_SIMPLE::logical_id(0xFE);
        << NWK_SIMPLE::repeat_max(3);
```

1行目は、ボードの登録と同じ書き方で `<>` には `<NWK_SIMPLE>`を指定します。

2行目は、`<NWK_SIMPLE>`の設定で、`0xFE`(**ID未設定の子機**)という指定を行います。

3行目は、中継回数の最大値を指定しています。この解説では中継には触れませんが、複数台で動作させたときにパケットの中継が行われます。

```cpp
the_twelite.begin(); // start twelite!
```

`setup()` 関数の末尾で `the_twelite.begin()` を実行しています。

#### Analogue

ADC(アナログディジタルコンバータ)を取り扱うクラスオブジェクトです。

```cpp
Analogue.setup(true);
```

初期化`Analogue.setup()`で行います。パラメータの`true`はADC回路の安定までその場で待つ指定です。

```cpp
Analogue.begin(pack_bits(PIN_ANALOGUE::A1, PIN_ANALOGUE::VCC), 50); 
```

ADCを開始するには`Analogue.begin()`を呼びます。パラメータはADC対象のピンに対応するビットマップです。

ビットマップを指定するのに`pack_bits()`関数を用います。可変数引数の関数で、各引数には１を設定するビット位置を指定します。例えば`pack_bits(1,3,5)`なら２進数で `101010`の値が戻ります。この関数は`constexpr`指定があるため、パラメータが定数のみであれば定数に展開されます。

パラメータには`PIN_ANALOGUE::A1`（ADC0）と`PIN_ANALOGUE::VCC`（モジュール電源電圧）が指定されています。

2番目のパラメータには`50`が指定されています。ADCの動作はデフォルトではTickTimerで開始されていて、

{% hint style="info" %}
初回を除き ADC の開始は、割り込みハンドラ内で行います。
{% endhint %}

#### Buttons

DIO (ディジタル入力) の値の変化を検出します。Buttonsでは、メカ式のボタンのチャタリング（摺動）の影響を軽減するため、一定回数同じ値が検出されてから、値の変化とします。

```cpp
Buttons.setup(5);
```

初期化は `Buttons.setup()`で行います。パラメータの 5 は、値の確定に必要な検出回数ですが、設定可能な最大値を指定します。内部的にはこの数値をもとに内部メモリの確保を行っています。

```cpp
Buttons.begin(pack_bits(PIN_BTN),
					5, 		// history count
					10);  	// tick delta
```

開始は `Buttons.begin()` で行います。１番目のパラメータは検出対象のDIOです。`BRD_APPTWELITE::`に定義される`PIN_BTN` (12) を指定しています。２番めのパラメータは状態を確定するのに必要な検出回数です。３番めのパラメータは検出間隔です。`10`を指定しているので10msごとに5回連続で同じ値が検出できた時点で、HIGH, LOWの状態が確定します。

{% hint style="info" %}
ButtonsでのDIO状態の検出はイベントハンドラで行います。イベントハンドラは、割り込み発生後にアプリケーションループで呼ばれるため割り込みハンドラに比べ遅延が発生します。
{% endhint %}

#### Serial

Serial オブジェクトは、初期化や開始手続きなく利用できます。

```cpp
Serial << "--- PingPong sample (press 't' to transmit) ---" << mwx::crlf;
```

シリアルポートへの文字列出力を行います。`mwx::crlf`は改行文字です。

### ループ loop()

ループ関数は TWENET ライブラリのメインループからコールバック関数として呼び出されます。ここでは、利用するオブジェクトが available になるのを待って、その処理を行うのが基本的な記述です。ここではアクトで使用されているいくつかのオブジェクトの利用について解説します。

{% hint style="warning" %}
TWENET ライブラリのメインループは、事前にFIFOキューに格納された受信パケットや割り込み情報などをイベントとして処理し、そののち`loop()`が呼び出されます。`loop()`を抜けた後は CPU が DOZE モードに入り、低消費電流で新たな割り込みが発生するまでは待機します。

したがってCPUが常に稼働していることを前提としたコードはうまく動作しません。
{% endhint %}

```cpp
void loop() {
	  // read from serial
		while(Serial.available())  {
				int c = Serial.read();
				Serial << mwx::crlf << char(c) << ':';
				switch(c) {
				    case 't':
				    	  vTransmit(MSG_PING, 0xFF);
				        break;
				    default:
							  break;
				}
		}


	// Button press
	if (Buttons.available()) {
		uint32_t btn_state, change_mask;
		Buttons.read(btn_state, change_mask);

		// Serial << fmt("<BTN %b:%b>", btn_state, change_mask);
		if (!(change_mask & 0x80000000) && (btn_state && (1UL << PIN_BTN))) {
			// PIN_BTN pressed
			vTransmit(MSG_PING, 0xFF);
		}
	}

	// receive RF packet.
    while (the_twelite.receiver.available()) {
		auto&& rx = the_twelite.receiver.read();

		// rx >> Serial; // debugging (display longer packet information)

		uint8_t msg[MSG_LEN];
		uint16_t adcval, volt;
		uint32_t timestamp;

		// expand packet payload (shall match with sent packet data structure, see pack_bytes())
		expand_bytes(rx.get_payload().begin(), rx.get_payload().end()
					, msg       // 4bytes of msg
								//   also can be -> std::make_pair(&msg[0], MSG_LEN)
					, adcval    // 2bytes, A1 value [0..1023]
				    , volt      // 2bytes, Module VCC[mV]
					, timestamp // 4bytes of timestamp
        );
		
		// if PING packet, respond pong!
        if (!strncmp((const char*)msg, "PING", MSG_LEN)) {
			// transmit a PONG packet with specifying the address.
            vTransmit(MSG_PONG, rx.get_psRxDataApp()->u32SrcAddr);
        }

		// display the packet
		Serial << format("<RX ad=%x/lq=%d/ln=%d/sq=%d:" // note: up to 4 args!
                    , rx.get_psRxDataApp()->u32SrcAddr
                    , rx.get_lqi()
                    , rx.get_length()
					, rx.get_psRxDataApp()->u8Seq
                    )
				<< format(" %s AD=%d V=%d TS=%dms>" // note: up to 4 args!
					, msg
					, adcval
					, volt
					, timestamp
					)
               << mwx::crlf
			   << mwx::flush;
	}
}
```

#### Serial

```cpp
		while(Serial.available())  {
				int c = Serial.read();
				Serial << mwx::crlf << char(c) << ':';
				switch(c) {
				    case 't':
				    	  vTransmit(MSG_PING, 0xFF);
				        break;
				    default:
							  break;
				}
		}
```

`Serial.available()`が`true`の間はシリアルポートからの入力があります。内部のFIFOキューに格納されるためある程度の余裕はありますが、速やかに読み出すようにします。データの読み出しは`Serial.read()`を呼びます。

ここでは`'t'`キーの入力に対応して`vTransmit()`関数を呼び出しPINGパケットを送信します。

#### Buttons

DIO(ディジタルIO)の入力変化を検出したタイミングで available になり、`Buttons.read()`により読み出します。

```cpp
	if (Buttons.available()) {
		uint32_t btn_state, change_mask;
		Buttons.read(btn_state, change_mask);
```

１番目のパラメータは、現在のDIOのHIGH/LOWのビットマップで、bit0から順番にDIO0,1,2,.. と並びます。例えば DIO12 であれば `btn_state & (1UL << 12)` を評価すれば HIGH / LOW が判定できます。ビットが1になっているものがHIGHになります。

{% hint style="info" %}
初回のIO状態確定時は MSB (bit31) に１がセットされます。スリープ復帰時も初回の確定処理を行います。
{% endhint %}

```cpp
// Serial << fmt("<BTN %b:%b>", btn_state, change_mask);
if (!(change_mask & 0x80000000) && (btn_state && (1UL << PIN_BTN))) {
	// PIN_BTN pressed
	vTransmit(MSG_PING, 0xFF);
```

初回確定以外の場合かつPIN\_BTNのボタンが**離された**タイミングで`vTransmit()`を呼び出しています。押したタイミングにするには`(!(btn_state && (1UL << PIN_BTN)))`のように条件を論理反転します。

#### Timer0

`Timer0`は32Hzで動作しています。タイマー割り込みが発生直後の `loop()` で available になります。つまり、秒３２回の処理をします。ここでは、ちょうど１秒になったところで送信処理をしています。

```cpp
if (Timer0.available()) {
	static uint8_t u16ct;
	u16ct++;

	if (u8DI_BM != 0xFF && au16AI[0] != 0xFFFF) { // finished the first capture
		if ((u16ct % 32) == 0) { // every 32ticks of Timer0
			transmit();
		}
	}
}
```

AppTweliteでは約１秒おきに定期送信を行っています。`Timer0`がavailableになったときに`u16ct`をインクリメントします。このカウンタ値をもとに、３２回カウントが終われば`transmit()`を呼び出し無線パケットを送信しています。

`u8DI_BM`と`au16AI[]`の値判定は、初期化直後かどうかの判定です。まだDI1..DI4やAI1..AI4の値が格納されていない場合は何もしません。

#### the\_twelite.receiver

受信パケットがある場合の処理です。

```cpp
while (the_twelite.receiver.available()) {
		auto&& rx = the_twelite.receiver.read();

		uint8_t msg[MSG_LEN];
		uint16_t adcval, volt;
		uint32_t timestamp;

		// expand packet payload (shall match with sent packet data structure, see pack_bytes())
		expand_bytes(rx.get_payload().begin(), rx.get_payload().end()
					, msg       // 4bytes of msg
											//   also can be -> std::make_pair(&msg[0], MSG_LEN)
					, adcval    // 2bytes, A1 value [0..1023]
				  , volt      // 2bytes, Module VCC[mV]
					, timestamp // 4bytes of timestamp
        );
		
		// if PING packet, respond pong!
    if (!strncmp((const char*)msg, "PING", MSG_LEN)) {
				// transmit a PONG packet with specifying the address.
        vTransmit(MSG_PONG, rx.get_psRxDataApp()->u32SrcAddr);
    }

		// display the packet
		Serial << format("<RX ad=%x/lq=%d/ln=%d/sq=%d:" // note: up to 4 args!
                    , rx.get_psRxDataApp()->u32SrcAddr
                    , rx.get_lqi()
                    , rx.get_length()
					, rx.get_psRxDataApp()->u8Seq
                    )
				<< format(" %s AD=%d V=%d TS=%dms>" // note: up to 4 args!
					, msg
					, adcval
					, volt
					, timestamp
					)
               << mwx::crlf
			   << mwx::flush;
	}
```

無線パケットを受信後の`loop()`では`the_twelite.receiver.available()`が`true`を返します。受信パケットの取り扱いについては `receive()` 関数の解説で行います。

{% hint style="warning" %}
TWENETがパケットを受信してから時間をたってからの受信パケットの参照は安全ではありません。

内部のデータが新しい受信パケットの内容に上書きされ、この新しい内容を参照することになります。availableになってから速やかに処理するように`loop()`を記述してください。内部的には、１パケット分余分に保持できる余裕はあります。
{% endhint %}

まず受信パケットのデータ`rx`を取得します。`rx`からアドレス情報やデータペイロードにアクセスします。

```cpp
while (the_twelite.receiver.available()) {
		auto&& rx = the_twelite.receiver.read();
```

次の行では、受信パケットデータには、送信元のアドレス（32bitのロングアドレスと8bitの論理アドレス）などの情報を参照しています。

```cpp
Serial << format("..receive(%08x/%d) : ",
   rx.get_addr_src_long(), rx.get_addr_src_lid());
```

{% hint style="info" %}
`<NWK_SIMPLE>`では、8bitの論理IDと32bitのロングアドレスの２種類が常にやり取りされます。送り先を指定する場合はロングアドレスか論理アドレスのいずれかを指定します。受信時には両方のアドレスが含まれます。
{% endhint %}

MWXライブラリには`transmit()`の時に使った`pack_bytes()`の対になる関数`expand_bytes()`が用意されています。

```cpp
uint8_t msg[MSG_LEN];
uint16_t adcval, volt;
uint32_t timestamp;

// expand packet payload (shall match with sent packet data structure, see pack_bytes())
expand_bytes(rx.get_payload().begin(), rx.get_payload().end()
		, msg       // 4bytes of msg
								//   also can be -> std::make_pair(&msg[0], MSG_LEN)
		, adcval    // 2bytes, A1 value [0..1023]
	  , volt      // 2bytes, Module VCC[mV]
		, timestamp // 4bytes of timestamp
    );
```

1行目から3行目までは、データを格納する変数を指定しています。

6行目で`expand_bytes()`によりパケットのペイロードのデータを変数に格納します。1番目のパラメータでコンテナの先頭イテレータ（`uint8_t*`ポインタ）を指定します。`.begin()`メソッドにより取得できます。2番目のパラメータはコンテナの末尾の次を指すイテレータで`.end()`メソッドで取得できます。2番目はコンテナの末尾を超えた読み出しを行わないようにするためです。

3番目以降のパラメータに変数を列挙します。列挙した順番にペイロードの読み出しとデータ格納が行われます。

{% hint style="info" %}
このアクトでは、パケット長が間違っていた場合などのエラーチェックを省いています。チェックを厳格にしたい場合は、`expand_bytes()`の戻り値により判定してください。

`expand_bytes()`の戻り値は `uint8_t*` ですが、末尾を超えたアクセスの場合は`nullptr（ヌルポインタ）`を戻します。
{% endhint %}

`msg`に読み出した4バイト文字列の識別子が`"PING"`の場合はPONGメッセージを送信する処理です。

```cpp
if (!strncmp((const char*)msg, "PING", MSG_LEN)) {
    vTransmit(MSG_PONG, rx.get_psRxDataApp()->u32SrcAddr);
}
```

続いて到着したパケット情報を表示します。

```cpp
		Serial << format("<RX ad=%x/lq=%d/ln=%d/sq=%d:" // note: up to 4 args!
                    , rx.get_psRxDataApp()->u32SrcAddr
                    , rx.get_lqi()
                    , rx.get_length()
										, rx.get_psRxDataApp()->u8Seq
                    )
           << format(" %s AD=%d V=%d TS=%dms>" // note: up to 4 args!
                    , msg
                    , adcval
                    , volt
                    , timestamp
                    )
         << mwx::crlf
			   << mwx::flush;
```

数値のフォーマット出力が必要になるので`format()`を用いています。`>>`演算子向けにprintf()と同じ構文を利用できるようにしたヘルパークラスですが、**引数の数が４つまでに制限**されています。（`Serial.printfmt()`には引数の数の制限がありません。）

mwx::crlfは改行文字(CR LF)を、mwx::flushは出力完了待ちを指定します。

### transmit()

無線パケットの送信要求をTWENETに行う関数です。本関数が終了した時点では、まだ無線パケットの処理は行われません。実際に送信が完了するのは、送信パラメータ次第ですが、数ms後以降になります。ここでは代表的な送信要求方法について解説します。

```cpp
void vTransmit(const char* msg, uint32_t addr) {
	Serial << "vTransmit()" << mwx::crlf;

	if (auto&& pkt = the_twelite.network.use<NWK_SIMPLE>().prepare_tx_packet()) {
		// set tx packet behavior
		pkt << tx_addr(addr)  // 0..0xFF (LID 0:parent, FE:child w/ no id, FF:LID broad cast), 0x8XXXXXXX (long address)
			<< tx_retry(0x3) // set retry (0x3 send four times in total)
			<< tx_packet_delay(100,200,20); // send packet w/ delay (send first packet with randomized delay from 100 to 200ms, repeat every 20ms)

		// prepare packet payload
		pack_bytes(pkt.get_payload() // set payload data objects.
			, make_pair(msg, MSG_LEN) // string should be paired with length explicitly.
			, uint16_t(analogRead(PIN_ANALOGUE::A1)) // possible numerical values types are uint8_t, uint16_t, uint32_t. (do not put other types)
			, uint16_t(analogRead_mv(PIN_ANALOGUE::VCC)) // A1 and VCC values (note: alalog read is valid after the first (Analogue.available() == true).)
			, uint32_t(millis()) // put timestamp here.
		);
	
		// do transmit 
		pkt.transmit();
	}
}
```

####

#### ネットワークオブジェクトとパケットオブジェクトの取得

```cpp
	if (auto&& pkt = the_twelite.network.use<NWK_SIMPLE>().prepare_tx_packet()) {
```

ネットワークオブジェクトを`the_twelite.network.use<NWK_SIMPLE>()`で取得します。そのオブジェクトを用いて`.prepare_tx_packet()`により`pkt`オブジェクトを取得します。

ここではif文の条件判定式の中で宣言しています。宣言した`pkt`オブジェクトはif節の終わりまで有効です。pktオブジェクトはbool型の応答をし、ここではTWENETの送信要求キューに空きがあって送信要求を受け付ける場合に`true`、空きがない場合に`false`となります。

#### パケットの送信設定

```cpp
		pkt << tx_addr(addr)  // 0..0xFF (LID 0:parent, FE:child w/ no id, FF:LID broad cast), 0x8XXXXXXX (long address)
			<< tx_retry(0x3) // set retry (0x3 send four times in total)
			<< tx_packet_delay(100,200,20); // send packet w/ delay (send first packet with randomized delay from 100 to 200ms, repeat every 20ms)
```

パケットの設定は`the_twelite`の初期化設定のように`<<`演算子を用いて行います。

* `tx_addr()` パラメータに送信先アドレスを指定します。`0x00`なら自分が子機で親機宛に、`0xFE`なら自分が親機で任意の子機宛のブロードキャストという意味です。
* `tx_retry()` パラメータに再送回数を指定します。例の`3`は再送回数が3回、つまり合計4回パケットを送ります。無線パケット１回のみの送信では条件が良くても数％程度の失敗はあります。
* `tx_packet_delay()` 送信遅延を設定します。一つ目のパラメータは、送信開始までの最低待ち時間、２番目が最長の待ち時間です。この場合は送信要求を発行後におよそ100msから200msの間で送信を開始します。３番目が再送間隔です。最初のパケットが送信されてから20ms置きに再送を行うという意味です。

#### パケットのデータペイロード

ペイロードは積載物という意味ですが、無線パケットでは「送りたいデータ本体」という意味でよく使われます。無線パケットのデータにはデータ本体以外にもアドレス情報などいくつかの補助情報が含まれます。

送受信を正しく行うために、データペイロードのデータ並び順を意識するようにしてください。ここでは以下のようなデータ順とします。このデータ順に合わせてデータペイロードを構築します。

```
# 先頭バイトのインデックス: データ型 : バイト数 : 内容

00: uint8_t[4] : 4 : ４文字識別子
08: uint16_t   : 2 : AI1のADC値 (0..1023)
06: uint16_t   : 2 : Vccの電圧値 (2000..3600)
10: uint32_t   : 4 : millis()システム時間
```

{% hint style="info" %}
データペイロードには90バイト格納できます（実際にはあと数バイト格納できます）。

IEEE802.15.4の無線パケットの１バイトは貴重です。できるだけ節約して使用することを推奨します。１パケットで送信できるデータ量に限りがあります。パケットを分割する場合は分割パケットの送信失敗などを考慮する必要がありコストは大きくつきます。また１バイト余分に送信するのに、およそ16μ秒×送信時の電流に相当するエネルギーが消費され、特に電池駆動のアプリケーションには大きく影響します。
{% endhint %}

上記のデータペイロードのデータ構造を実際に構築してみます。データペイロードは `pkt.get_payload()` により `simplbuf<uint8_t>` 型のコンテナとして参照できます。このコンテナに上記の仕様に基づいてデータを構築します。

上記のように記述できますがMWXライブラリでは、データペイロード構築のための補助関数`pack_bytes()`を用意しています。

```cpp
// prepare packet payload
pack_bytes(pkt.get_payload() // set payload data objects.
	, make_pair(msg, MSG_LEN) // string should be paired with length explicitly.
	, uint16_t(analogRead(PIN_ANALOGUE::A1)) // possible numerical values types are uint8_t, uint16_t, uint32_t. (do not put other types)
	, uint16_t(analogRead_mv(PIN_ANALOGUE::VCC)) // A1 and VCC values (note: alalog read is valid after the first (Analogue.available() == true).)
	, uint32_t(millis()) // put timestamp here.
);
```

`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()`を利用しています。

これでパケットの準備は終わりです。あとは、送信要求を行います。

```cpp
pkt.transmit();
```

パケットを送信するには`pkt`オブジェクトの`pkt.transmit()`メソッドを用います。

{% hint style="info" %}
このアクトでは使用しませんが、戻り値には、要求の成功失敗の情報と要求に対応する番号が格納されています。送信完了まで待つ処理を行う場合は、この戻り値の値を利用します。
{% endhint %}


# Parent\_MONOSTICK

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

MONOSTICKを親機として使用するアクトです。子機からのパケットのデータペイロードをシリアルポートに出力します。

{% hint style="success" %}
このアクトの解説の前に[BRD\_APPTWELITEの解説](/v0.1.3/act_samples/brd_apptwelite)をご覧ください。
{% endhint %}

## アクトの機能

* サンプルアクトの子機からのパケットを受信して、アスキー形式で出力する。

## アクトの使い方

### 必要なTWELITEと配線

| 役割 | 例                                                                                                                                                                                                                                                        |
| -- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 親機 | [MONOSTICK BLUEまたはRED](https://mono-wireless.com/jp/products/MoNoStick/)                                                                                                                                                                                 |
| 子機 | サンプルアクトの子機設定 (例: [BLUE PAL または RED PAL](https://mono-wireless.com/jp/products/twelite-pal/BnR/index.html) +[AMBIENT SENSE PAL](https://mono-wireless.com/jp/products/twelite-pal/sense/amb-pal.html)に[PAL\_AMBアクト](/v0.1.3/act_samples/pal_amb)を書き込んだもの) |

## アクトの解説

### インクルード

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

MONOSTICK用のボードビヘイビア[`<MONOSTICK>`](/v0.1.3/boards/less-than-monostick-greater-than)をインクルードしています。このボードサポートには、LEDの制御、ウォッチドッグ対応が含まれます。

### setup()

```cpp
void setup() {
	/*** SETUP section */
	auto&& brd = the_twelite.board.use<MONOSTICK>();
	brd.set_led_red(LED_TIMER::ON_RX, 200); // RED (on receiving)
	brd.set_led_yellow(LED_TIMER::BLINK, 500); // YELLOW (blinking)

	// the twelite main class
	the_twelite
		<< TWENET::appid(APP_ID)    // set application ID (identify network group)
		<< TWENET::channel(CHANNEL) // set channel (pysical channel)
		<< TWENET::rx_when_idle();  // open receive circuit (if not set, it can't listen packts from others)

	// Register Network
	auto&& nwksmpl = the_twelite.network.use<NWK_SIMPLE>();
	nwksmpl << NWK_SIMPLE::logical_id(0x00); // set Logical ID. (0x00 means parent device)

	/*** BEGIN section */
	the_twelite.begin(); // start twelite!

	/*** INIT message */
	Serial << "--- MONOSTICK_Parent act ---" << mwx::crlf;
}
```

ボードサポートの使用とLEDの動作を設定しています。

```cpp
auto&& brd = the_twelite.board.use<MONOSTICK>();
brd.set_led_red(LED_TIMER::ON_RX, 200); // RED (on receiving)
brd.set_led_yellow(LED_TIMER::BLINK, 500); // YELLOW (blinking)
```

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

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

```cpp
auto&& nwksmpl = the_twelite.network.use<NWK_SIMPLE>();
nwksmpl << NWK_SIMPLE::logical_id(0x00);
```

ここでは`NWK_SIMPLE::logical_id(0x00)`を設定し親機(論理ID=0x00)であることを指定します。

### loop()

パケットを受信したとき、その内容を表示します。ここでは２種類の表示を行っています。

```cpp
void loop() {
	// receive RF packet.
    while (the_twelite.receiver.available()) {
		auto&& rx = the_twelite.receiver.read();

		Serial << ".. coming packet (" << int(millis()&0xffff) << ')' << mwx::crlf;

		// output type1 (raw packet)
		//   uint8_t  : 0x01
		//   uint8_t  : src addr (LID)
		//   uint32_t : src addr (long)
		//   uint32_t : dst addr (LID/long)
		//   uint8_t  : repeat count
		//     total 11 bytes of header.
		//     
		//   N        : payload
		{
		  serparser_attach pout;
			pout.begin(PARSER::ASCII, rx.get_psRxDataApp()->auData, 
			    rx.get_psRxDataApp()->u8Len, rx.get_psRxDataApp()->u8Len);

			Serial << "RAW PACKET -> ";
			pout >> Serial;
			Serial << mwx::flush;
		}

		// output type2 
		{
			smplbuf_u8<128> buf;
			mwx::pack_bytes(buf
				, uint8_t(rx.get_addr_src_lid())		// src addr (LID)
				, uint8_t(0xCC)							        // cmd id (0xCC, fixed)
				, uint8_t(rx.get_psRxDataApp()->u8Seq)	// seqence number
				, uint32_t(rx.get_addr_src_long())	// src addr (long)
				, uint32_t(rx.get_addr_dst())			  // dst addr
				, uint8_t(rx.get_lqi())					    // LQI
				, uint16_t(rx.get_length())				  // payload length
				, rx.get_payload() 					      	// payload
			);

			serparser_attach pout;
			pout.begin(PARSER::ASCII, buf.begin(), buf.size(), buf.size());
			
			Serial << "FMT PACKET -> ";
			pout >> Serial;
			Serial << mwx::flush;
		}
	}
}
```

最初の出力は`<NWK_SIMPLE>`の制御データを含めたデータをすべて表示します。制御データは11バイトあります。通常は制御情報を直接参照することはありませんが、あくまでも参考です。

```cpp
serparser_attach pout;
pout.begin(PARSER::ASCII, rx.get_psRxDataApp()->auData, 
    rx.get_psRxDataApp()->u8Len, rx.get_psRxDataApp()->u8Len);

Serial << "RAW PACKET -> ";
pout >> Serial;
Serial << mwx::flush;

// 参考：制御部のパケット構造
// uint8_t  : 0x01 固定
// uint8_t  : 送信元のLID
// uint32_t : 送信元のロングアドレス(シリアル番号)
// uint32_t : 宛先アドレス
// uint8_t  : 中継回数
```

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

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

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

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

２番目の出力はより実践的です。ユーザが定義した並び順で書式を構成します。

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

serparser_attach pout;
pout.begin(PARSER::ASCII, buf.begin(), buf.size(), buf.size());

Serial << "FMT PACKET -> ";
pout >> Serial;
Serial << mwx::flush;
```

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

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

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

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

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


# PAL\_AMB

[環境センサーパル AMBIENT SENSE PAL](https://mono-wireless.com/jp/products/twelite-pal/sense/amb-pal.html) を用い、センサー値の取得を行います。

{% hint style="success" %}
このアクトの解説の前に[BRD\_APPTWELITEの解説](/v0.1.3/act_samples/brd_apptwelite)をご覧ください。

受信の確認のため[Parent\_MONOSTICK](/v0.1.3/act_samples/parent_monostick)の解説をご覧ください。
{% endhint %}

## アクトの機能

* 環境センサーパル AMPIENT SENSE PAL を用い、センサー値の取得を行います。
* コイン電池で動作させるための、スリープ機能を利用します。

## アクトの使い方

### 必要なTWELITE

| 役割 | 例                                                                                                                                                                                            |
| -- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 親機 | <p><a href="https://mono-wireless.com/jp/products/MoNoStick/">MONOSTICK BLUEまたはRED</a></p><p>アクト<a href="/pages/-Lvtl_DIu32MgNvsUahk">Parent\_MONOSTICK</a>を動作させる。</p>                       |
| 子機 | [BLUE PAL または RED PAL](https://mono-wireless.com/jp/products/twelite-pal/BnR/index.html) +[環境センサーパル AMBIENT SENSE PAL](https://mono-wireless.com/jp/products/twelite-pal/sense/amb-pal.html) |

## アクトの解説

### インクルード

```cpp
#include <TWELITE>
#include <NWK_SIMPLE>
#include <PAL_AMB> // include the board support of PAL_AMB
```

環境センサーパル [`<PAL_AMB>`](/v0.1.3/boards/pal/pal_amb) のボードビヘイビアをインクルードします。

### setup()

```cpp
void setup() {
	/*** SETUP section */
	// use PAL_AMB board support.
	auto&& brd = the_twelite.board.use<PAL_AMB>();
	// now it can read DIP sw status.
	u8ID = (brd.get_DIPSW_BM() & 0x07) + 1;
	if (u8ID == 0) u8ID = 0xFE; // 0 is to 0xFE

	// LED setup (use periph_led_timer, which will re-start on wakeup() automatically)
	brd.set_led(LED_TIMER::BLINK, 10); // blink (on 10ms/ off 10ms)

	// the twelite main object.
	the_twelite
		<< TWENET::appid(APP_ID)     // set application ID (identify network group)
		<< TWENET::channel(CHANNEL); // set channel (pysical channel)

	// Register Network
	auto&& nwk = the_twelite.network.use<NWK_SIMPLE>();
	nwk << NWK_SIMPLE::logical_id(u8ID); // set Logical ID. (0xFE means a child device with no ID)

	/*** BEGIN section */
	Wire.begin(); // start two wire serial bus.
	Analogue.begin(pack_bits(PIN_ANALOGUE::A1, PIN_ANALOGUE::VCC)); // _start continuous adc capture.

	the_twelite.begin(); // start twelite!

	startSensorCapture(); // start sensor capture!

	/*** INIT message */
	Serial << "--- PAL_AMB:" << FOURCHARS << " ---" << mwx::crlf;
}
```

最初にボードサポート [`<PAL_AMB>`](/v0.1.3/act_samples/parent_monostick) を登録します。ボードサポートの初期化時にセンサーやDIOの初期化が行われます。最初に行うのは、ボードのDIP SWなどの状態を確認してから、ネットワークの設定などを行うといった処理が一般的だからです。

```cpp
auto&& brd = the_twelite.board.use<PAL_AMB>();

u8ID = (brd.get_DIPSW_BM() & 0x07) + 1;
if (u8ID == 0) u8ID = 0xFE; // 0 is to 0xFE
```

ここでは、ボード上の４ビットDIP SWのうち３ビットを読み出して子機のIDとして設定しています。0の場合は、ID無しの子機(`0xFE`)とします。

LEDの設定を行います。ここでは 10ms おきに ON/OFF の点滅の設定をします（スリープを行い起床時間が短いアプリケーションでは、起床中は点灯するという設定とほぼ同じ意味合いになります）。

```cpp
	brd.set_led(LED_TIMER::BLINK, 10); // blink (on 10ms/ off 10ms)
```

このアクトではもっぱら無線パケットを送信しますので、TWENET の設定では動作中に受信回路をオープンにする指定（`TWENET::rx_when_idle()`）は含めません。

```cpp
	the_twelite
		<< TWENET::appid(APP_ID)     // set application ID (identify network group)
		<< TWENET::channel(CHANNEL); // set channel (pysical channel)
```

ボード上のセンサーはI2Cバスを用いますので、バスを利用開始しておきます。

```cpp
Wire.begin(); // start two wire serial bus.
```

ボード上のセンサーの取得を開始します。[`startSensorCapture()`](/v0.1.3/act_samples/pal_amb#startsensorcapture)の解説を参照ください。

```
startSensorCapture();
```

### loop()

```cpp
void loop() {
	auto&& brd = the_twelite.board.use<PAL_AMB>();

	// mostly process every ms.
	if (TickTimer.available()) {
		
		//  wait until sensor capture finish
		if (!brd.sns_LTR308ALS.available()) {
			// this will take around 50ms.
			// note: to save battery life, perform sleeping to wait finish of sensor capture.
			brd.sns_LTR308ALS.process_ev(E_EVENT_TICK_TIMER);
		}

		if (!brd.sns_SHTC3.available()) {
			brd.sns_SHTC3.process_ev(E_EVENT_TICK_TIMER);
		}

		// now sensor data is ready.
		if (brd.sns_LTR308ALS.available() && brd.sns_SHTC3.available() && !b_transmit) {
			Serial << "..finish sensor capture." << mwx::crlf
				<< "  LTR308ALS: lumi=" << int(brd.sns_LTR308ALS.get_luminance()) << mwx::crlf
				<< "  SHTC3    : temp=" << brd.sns_SHTC3.get_temp() << 'C' << mwx::crlf
				<< "             humd=" << brd.sns_SHTC3.get_humid() << '%' << mwx::crlf
				<< mwx::flush;

			 // get new packet instance.
			if (auto&& pkt = the_twelite.network.use<NWK_SIMPLE>().prepare_tx_packet()) {
				// set tx packet behavior
				pkt << tx_addr(0x00)  // 0..0xFF (LID 0:parent, FE:child w/ no id, FF:LID broad cast), 0x8XXXXXXX (long address)
					<< tx_retry(0x1) // set retry (0x1 send two times in total)
					<< tx_packet_delay(0, 0, 2); // send packet w/ delay

				// prepare packet payload
				pack_bytes(pkt.get_payload() // set payload data objects.
					, make_pair(FOURCHARS, 4)  // just to see packet identification, you can design in any.
					, uint32_t(brd.sns_LTR308ALS.get_luminance()) // luminance
					, uint16_t(brd.sns_SHTC3.get_temp())
					, uint16_t(brd.sns_SHTC3.get_humid())
				);

				// do transmit
				MWX_APIRET ret = pkt.transmit();
				Serial << "..transmit request by id = " << int(ret.get_value()) << '.' << mwx::crlf << mwx::flush;

				if (ret) {
					u8txid = ret.get_value() & 0xFF;
					b_transmit = true;
				}
				else {
					// fail to request
					sleepNow();
				}
			}
		}
	}

	// wait to complete transmission.
	if (b_transmit) {
		if (the_twelite.tx_status.is_complete(u8txid)) {		
			Serial << "..transmit complete." << mwx::crlf << mwx::flush;

			// now sleeping
			sleepNow();
		}
	}
}
```

このアクトでは、1ms周期で呼び出させる（アプリケーションループの呼び出しタイミングは不定期な遅延が発生します）TickTimerのイベントを起点としてセンサーの読み出しや送信要求を行います。`TickTimer.available()`が`true`になったときに処理を行います。これによりおよそ1msごとにif節内の処理を行います。

```cpp
	if (TickTimer.available()) {
```

ボード上のセンサーは `.sns_LTR308ALS` または `.sns_SHTC3` という名前でアクセスでき、このオブジェクトに操作を行います。センサーの完了待ちを行います。まだセンサーの取得が終わっていない場合(`.available()`が`false`)はセンサーに対して時間経過のイベント(`.process_ev(E_EVENT_TICK_TIMER)`)を送付します。

```cpp
		if (!brd.sns_LTR308ALS.available()) {
			brd.sns_LTR308ALS.process_ev(E_EVENT_TICK_TIMER);
		}

		if (!brd.sns_SHTC3.available()) {
			brd.sns_SHTC3.process_ev(E_EVENT_TICK_TIMER);
		}
```

上記２つのセンサーがavailableになった時点で、無線送信などの処理を行います。

```cpp
		if (brd.sns_LTR308ALS.available() 
						&& brd.sns_SHTC3.available() && !b_transmit) {
```

送信手続きについては他のアクトのサンプルと同様です。ここでは、再送１回、再送遅延を最小にする設定になっています。

```cpp
	pkt << tx_addr(0x00)  // 親機0x00宛
		<< tx_retry(0x1)    // リトライ1回
		<< tx_packet_delay(0, 0, 2); // 遅延は最小限
```

パケットにセンサーデータを書き込み際に、センサーデータを取得しています。センサーの値を取得するメソッドはセンサーごとに違います。

```cpp
	// prepare packet payload
	pack_bytes(pkt.get_payload() 
		, make_pair(FOURCHARS, 4)  
		, uint32_t(brd.sns_LTR308ALS.get_luminance()) // luminance
		, uint16_t(brd.sns_SHTC3.get_temp_cent())
		, uint16_t(brd.sns_SHTC3.get_humid_per_dmil())
	);
```

照度センサーは`.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()` によりスリープします。

```cpp
	// do transmit
	MWX_APIRET ret = pkt.transmit();

	if (ret) {
		u8txid = ret.get_value() & 0xFF;
		b_transmit = true;
	}
	else {
		// fail to request
		sleepNow();
	}
```

`loop()` 中 `b_transmit` が `true` になっている場合は、送信完了チェックを行い、完了すれば `sleepNow()` によりスリープします。

```cpp
// wait to complete transmission.
if (b_transmit) {
    if (the_twelite.tx_status.is_complete(u8txid)) {        
        Serial << "..transmit complete." << mwx::crlf << mwx::flush;

        // now sleeping
        sleepNow();
    }
}
```

送信完了に確認は `the_twelite.tx_status.is_complete(u8txid)` で行っています。`u8txid`は送信時に戻り値として戻されたID値です。

###

### startSensorCapture()

ボード上のセンサーの取得を開始します。多くのセンサーは、取得を開始してから完了するまで数msから数十msの時間を必要とします。

```cpp
void startSensorCapture() {
	auto&& brd = the_twelite.board.use<PAL_AMB>();

	// start sensor capture
	brd.sns_SHTC3.begin();
	brd.sns_LTR308ALS.begin();
	b_transmit = false;
}
```

5行目で、温湿度センサー SHTC3 のデータ取得を開始します。

6行目で、照度センサー LTR308ALS のデータ取得を開始します。

7行目は、送信完了フラグをクリアします。

### sleepNow()

スリープに入る手続きをまとめています。

```cpp
void sleepNow() {
	uint32_t u32ct = 1750 + random(0,500);
	Serial << "..sleeping " << int(u32ct) << "ms." << mwx::crlf << mwx::flush;

	the_twelite.sleep(u32ct);
}
```

ここでは、起床までの時間を乱数により 1750ms から 2250ms の間に設定しています。これにより他の同じような周期で送信するデバイスのパケットとの連続的な衝突を避けます。

{% hint style="info" %}
周期が完全に一致すると、互いのパケットで衝突が起き通信が困難になります。通常は時間の経過とともにタイマー周期が互いにずれるため、しばらくすると通信が回復し、また時間がたつと衝突が起きるという繰り返しになります。
{% endhint %}

3行目、この例ではシリアルポートからの出力を待ってスリープに入ります。通常は消費エネルギーを最小化したいため、スリープ前のシリアルポートの出力は最小限（または無し）にします。

{% hint style="warning" %}
スリープ前にflushを行うと、出力が不安定になる場合があります。
{% endhint %}

スリープに入るには `the_twelite.sleep()` を呼びます。この呼び出しの中で、ボード上のハードウェアのスリープ前の手続きなどが行われます。たとえばLEDは消灯します。

パラメータとしてスリープ時間をmsで指定しています。

{% hint style="danger" %}
TWELITE PAL では、必ず60秒以内に一度起床し、ウォッチドッグタイマーをリセットしなければなりません。スリープ時間は`60000`を超えないように指定してください。
{% endhint %}

### wakeup()

スリープから復帰し起床すると `wakeup()` が呼び出されます。そのあと`loop()` が都度呼び出されます。`wakeup()`の前に、UARTなどの各ペリフェラルやボード上のデバイスのウェイクアップ処理が行われます。例えばLEDの点灯制御を再始動します。

```cpp
void wakeup() {
	Serial	<< mwx::crlf
			<< "--- PAL_AMB:" << FOURCHARS << " wake up ---"
			<< mwx::crlf
			<< "..start sensor capture again."
			<< mwx::crlf;
	startSensorCapture();
}
```

ここではセンサーのデータ取得を開始しています。

## 応用編

#### より安全な実装

より安全な実装として、このアクトでは以下の例外処理を強化します。

* センサー取得部分でavailableにならなかった場合の例外処理が記述されてません。
* 送信完了待ちで送信完了が通知されない場合の例外処理が記述されていません。

上記いずれもタイムアウト処理を行います。現在の時間は `millis()` により取得できます。

```cpp
uint32_t t_start;

  // 時間待ちの処理を開始した時点でタイムスタンプを保存
  t_start = millis();

...

  // loop() でタイムアウトのチェック
  if (millis() - t_start > 100) {
    sleepNow();
  }
```

#### 消費エネルギーの削減

アクト [PAL\_AMB-UseNap ](/v0.1.3/act_samples/pal_amb-usenap)は、センサーのデータ取得待ちをスリープで行い、より低消費エネルギーで動作できます。


# PAL\_AMB-usenap

[PAL\_AMB ](/v0.1.3/act_samples/pal_amb)のサンプルを少し改良して、センサーデータ取得中の待ち時間（約50ms)を、スリープで待つようにします。

{% hint style="success" %}
このアクトの解説の前に[PAL\_AMB](/v0.1.3/act_samples/pal_amb)のアクトの解説をご覧ください。
{% endhint %}

## アクトの解説

### begin()

`begin()`関数は`setup()`関数を終了し(そのあとTWENETの初期化が行われる)一番最初の`loop()`の直前で呼ばれます。

```cpp
void begin() {
	sleepNow(); // the first time is just sleeping.
}
```

`setup()`終了後に初回スリープを実行します。`setup()`中にセンサーデータ取得を開始していますが、この結果は評価せず、センサーを事前に一度は動かしておくという意味あいで、必ずしも必要な手続きではありません。

### wakeup()

起床後の手続きです。以下の処理を行います。

* まだセンサーデータの取得開始をしていない場合、センサーデータ取得を行い、短いスリープに入る。
* 直前にセンサーデータ取得開始を行ったので、データを確認して無線送信する。

```cpp
void wakeup() {
	if (!b_senser_started) {
		// delete/make shorter this message if power requirement is harder.	
		Serial	<< mwx::crlf
				<< "--- PAL_AMB:" << FOURCHARS << " wake up ---"
				<< mwx::crlf
				<< "..start sensor capture again."
				<< mwx::crlf;

		startSensorCapture();
		b_senser_started = true;

		napNow(); // short period sleep.
	} else {
		Serial << "..wake up from short nap.." << mwx::crlf;

		auto&& brd = the_twelite.board.use<PAL_AMB>();

		b_senser_started = false;

		// tell sensors waking up.
		brd.sns_LTR308ALS.process_ev(E_EVENT_START_UP);
		brd.sns_SHTC3.process_ev(E_EVENT_START_UP);
	}
}
```

上記の分岐をグローバル変数の`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()`に移行し、無線パケットが送信されます。

{% hint style="warning" %}
センサーに通知するイベントは必要な時間待ちが終わったかどうかを判定するために使われます。実際時間が経過しているかどうかは`napNow()`で正しい時間を設定したかどうかで決まります。短い時間で起床した場合は、必要とされる時間経過に足りないため、続く処理でセンサーデータが得られないなどのエラーが出ることが想定されます。
{% endhint %}

### napNow()

ごく短いスリープを実行する。

```cpp
void napNow() {
	uint32_t u32ct = 100;
	Serial << "..nap " << int(u32ct) << "ms." << mwx::crlf;
	the_twelite.sleep(u32ct, false, false, TWENET::SLEEP_WAKETIMER_SECONDARY);
}
```

sleepのパラメータの2番目をtrueにすると前回のスリープ復帰時刻をもとに次の復帰時間を調整します。常に5秒おきに起床したいような場合設定します。

3番目をtrueにするとメモリーを保持しないスリープになります。復帰後はwakup()は呼び出されじ、電源再投入と同じ処理になります。

4番目はウェイクアップタイマーの2番目を使う指定です。ここでは1番目は通常のスリープに使用して、2番目を短いスリープに用いています。このアクトでは2番目を使う強い理由はありませんが、例えば上述の5秒おきに起床したいような場合、短いスリープに1番目のタイマーを用いてしまうとカウンター値がリセットされてしまい、経過時間の補正計算が煩雑になるため2番目のタイマーを使用します。

{% hint style="info" %}
あまり短いスリープ時間を設定してもスリープ復帰後のシステムの再初期化などのエネルギーコストと釣り合いません。目安として最小時間を30-50ms程度とお考え下さい。
{% endhint %}


# PAL\_AMB-behavior

[ビヘイビア](/v0.1.3/api-reference/behavior)の記述サンプルです。詳細は[こちら](/v0.1.3/api-reference/behavior/pal_amb-behavior)を参照ください。


# PAL\_MAG

[開閉センサーパル OPEN-CLOSE SENSE PAL](https://mono-wireless.com/jp/products/twelite-pal/sense/amb-pal.html) を用い、センサー値の取得を行います。

{% hint style="success" %}
このアクトの解説の前に[BRD\_APPTWELITEの解説](/v0.1.3/act_samples/brd_apptwelite)をご覧ください。

受信の確認のため[Parent\_MONOSTICK](/v0.1.3/act_samples/parent_monostick)の解説をご覧ください。
{% endhint %}

## アクトの機能

* 開閉センサーパル OPEN-CLOSE SENSE PAL を用い、磁気センサーの検出時に割り込み起床し、無線送信します。
* コイン電池で動作させるための、スリープ機能を利用します。

## アクトの使い方

### 必要なTWELITE

| 役割 | 例                                                                                                                                                                                               |
| -- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 親機 | <p><a href="https://mono-wireless.com/jp/products/MoNoStick/">MONOSTICK BLUEまたはRED</a></p><p>アクト<a href="/pages/-Lvtl_DIu32MgNvsUahk">Parent\_MONOSTICK</a>を動作させる。</p>                          |
| 子機 | [BLUE PAL または RED PAL](https://mono-wireless.com/jp/products/twelite-pal/BnR/index.html) +[開閉センサーパル OPEN-CLOSE SENSE PAL](https://mono-wireless.com/jp/products/twelite-pal/sense/amb-pal.html) |

## アクトの解説

### インクルード

```cpp
#include <TWELITE>
#include <NWK_SIMPLE>
#include <PAL_>
```

開閉センサーパルのボード ビヘイビア[`<PAL_MAG>`](/v0.1.3/boards/pal/pal_mag)をインクルードします。

### setup()

```cpp
void setup() {
	/*** SETUP section */
	// use PAL_AMB board support.
	auto&& brd = the_twelite.board.use<PAL_MAG>();
	// now it can read DIP sw status.
	u8ID = (brd.get_DIPSW_BM() & 0x07) + 1;
	if (u8ID == 0) u8ID = 0xFE; // 0 is to 0xFE

	// LED setup (use periph_led_timer, which will re-start on wakeup() automatically)
	brd.set_led(LED_TIMER::BLINK, 10); // blink (on 10ms/ off 10ms)

	// the twelite main object.
	the_twelite
		<< TWENET::appid(APP_ID)     // set application ID (identify network group)
		<< TWENET::channel(CHANNEL); // set channel (pysical channel)

	// Register Network
	auto&& nwk = the_twelite.network.use<NWK_SIMPLE>();
	nwk << NWK_SIMPLE::logical_id(u8ID); // set Logical ID. (0xFE means a child device with no ID)

	/*** BEGIN section */
	the_twelite.begin(); // start twelite!

	/*** INIT message */
	Serial << "--- PAL_MAG:" << FOURCHARS << " ---" << mwx::crlf;
}
```

最初にボードビヘイビア`<PAL_MAG>`を登録します。ボードビヘイビアの初期化時にセンサーやDIOの初期化が行われます。最初に行うのは、ボードのDIP SWなどの状態を確認してから、ネットワークの設定などを行うといった処理が一般的だからです。

```cpp
auto&& brd = the_twelite.board.use<PAL_MAG>();

u8ID = (brd.get_DIPSW_BM() & 0x07) + 1;
if (u8ID == 0) u8ID = 0xFE; // 0 is to 0xFE
```

ここでは、ボード上の４ビットDIP SWのうち３ビットを読み出して子機のIDとして設定しています。0の場合は、ID無しの子機(`0xFE`)とします。

LEDの設定を行います。ここでは 10ms おきに ON/OFF の点滅の設定をします（スリープを行い起床時間が短いアプリケーションでは、起床中は点灯するという設定とほぼ同じ意味合いになります）。

```cpp
	brd.set_led(LED_TIMER::BLINK, 10); // blink (on 10ms/ off 10ms)
```

### begin()

`begin()`関数は`setup()`関数を終了し(そのあとTWENETの初期化が行われる)一番最初の`loop()`の直前で呼ばれます。

```cpp
void begin() {
	sleepNow(); // the first time is just sleeping.
}
```

`setup()`終了後に`sleepNow()`を呼び出し初回スリープを実行します。

### sleepNow()

```cpp
void sleepNow() {
	uint32_t u32ct = 60000;
	
	pinMode(PAL_MAG::PIN_SNS_OUT1, PIN_MODE::WAKE_FALLING);
	pinMode(PAL_MAG::PIN_SNS_OUT2, PIN_MODE::WAKE_FALLING);

	the_twelite.sleep(u32ct);
}
```

スリープに入るまえに磁気センサーのDIOピンの割り込み設定をします。`pinMode()`を用います。２番めのパラメータは`PIN_MODE::WAKE_FALLING`を指定しています。これはHIGHからLOWへピンの状態が変化したときに起床する設定です。

7行目で`the_twelite.sleep()`でスリープを実行します。パラメータの60000は、TWELITE PAL ボードのウォッチドッグをリセットするために必要な起床設定です。リセットしないと60秒経過後にハードリセットがかかります。

###

### wakeup()

スリープから復帰し起床すると `wakeup()` が呼び出されます。そのあと`loop()` が都度呼び出されます。`wakeup()`の前に、UARTなどの各ペリフェラルやボード上のデバイスのウェイクアップ処理（ウォッチドッグタイマーのリセットなど）が行われます。例えばLEDの点灯制御を再始動します。

```cpp
void wakeup() {
	if (the_twelite.is_wokeup_by_wktimer()) {
		sleepNow();
	}
}
```

ここではウェイクアップタイマーからの起床の場合(`the_twelite.is_wokeup_by_wktimer()`)は再びスリープを実行します。これは上述のウォッチドッグタイマーのリセットを行う目的のみの起床です。

磁気センサーの検出時の起床の場合は、このままloop()処理に移行します。

### loop()

ここでは、検出された磁気センサーのDIOの確認を行い、パケットの送信を行い、パケット送信完了後に再びスリープを実行します。

```cpp
void loop() {
	if (!b_transmit) {
		if (auto&& pkt = 
      the_twelite.network.use<NWK_SIMPLE>().prepare_tx_packet())

			uint8_t b_north = 
			  the_twelite.is_wokeup_by_dio(PAL_MAG::PIN_SNS_NORTH);
			uint8_t b_south = 
			  the_twelite.is_wokeup_by_dio(PAL_MAG::PIN_SNS_SOUTH);
	
			Serial << "..sensor north=" << int(b_north) 
			       << " south=" << int(b_south) << mwx::crlf;
	
			// set tx packet behavior
			pkt << tx_addr(0x00)
				<< tx_retry(0x1)
				<< tx_packet_delay(0, 0, 2);
	
			// prepare packet payload
			pack_bytes(pkt.get_payload()
				, make_pair(FOURCHARS, 4) 
				, b_north
				, b_south
			);
	
			// do transmit
			MWX_APIRET ret = pkt.transmit();
	
			if (ret) {
				u8txid = ret.get_value() & 0xFF;
				b_transmit = true;
			}
			else {
				// fail to request
				sleepNow();
			}
		} else {
		  sleepNow();
		}
	} else { 
		if (the_twelite.tx_status.is_complete(u8txid)) {		
			b_transmit = 0;
			sleepNow();
		}
	}
}
```

`b_transmit`変数によって`loop()`内の振る舞いを制御しています。送信要求が成功した後、この値を1にセットしパケット送信完了待ちを行います。

```cpp
	if (!b_transmit) {
```

磁気センサーの検出DIOピンの確認を行います。検出ピンは二種類あります。N極検知とS極検知です。単に磁石が近づいたことだけを知りたいならいずれかのピンの検出されたことが条件となります。

```cpp
uint8_t b_north = 
  the_twelite.is_wokeup_by_dio(PAL_MAG::PIN_SNS_NORTH);
uint8_t b_south = 
  the_twelite.is_wokeup_by_dio(PAL_MAG::PIN_SNS_SOUTH);
```

起床要因のピンを確認するには`the_twelite.is_wokeup_by_dio()`を用います。パラメータはピン番号です。戻り値をuint8\_tに格納しているのはパケットのペイロードに格納するためです。

通信条件の設定やペイロードにデータを格納後、送信を行います。

```cpp
// do transmit
MWX_APIRET ret = pkt.transmit();
```

その後、`loop()` 中 `b_transmit` が `true` になっている場合は、完了チェックを行い、完了すれば `sleepNow()` によりスリープします。

```cpp
if (the_twelite.tx_status.is_complete(u8txid)) {		
	b_transmit = 0;
	sleepNow();
}
```

送信完了に確認は `the_twelite.tx_status.is_complete(u8txid)` で行っています。`u8txid`は送信時に戻り値として戻されたID値です。


# PAL\_MOT

[動作センサーパル MOTION SENSE PAL](https://mono-wireless.com/jp/products/twelite-pal/sense/amb-pal.html) を用い、センサー値の取得を行います。

{% hint style="success" %}
このアクトの解説の前に[BRD\_APPTWELITEの解説](/v0.1.3/act_samples/brd_apptwelite)をご覧ください。

受信の確認のため[Parent\_MONOSTICK](/v0.1.3/act_samples/parent_monostick)の解説をご覧ください。
{% endhint %}

## アクトの機能

* 動作センサーパル MOTION SENSE PAL を用い、加速度センサーの加速度を連続的に計測し、無線送信します。
* コイン電池で動作させるための、スリープ機能を利用します。

## アクトの使い方

### 必要なTWELITE

| 役割 | 例                                                                                                                                                                                           |
| -- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 親機 | <p><a href="https://mono-wireless.com/jp/products/MoNoStick/">MONOSTICK BLUEまたはRED</a></p><p>アクト<a href="/pages/-Lvtl_DIu32MgNvsUahk">Parent\_MONOSTICK</a>を動作させる。</p>                      |
| 子機 | [BLUE PAL または RED PAL](https://mono-wireless.com/jp/products/twelite-pal/BnR/index.html) +[動作センサーパル MOTION SENSE PAL](https://mono-wireless.com/jp/products/twelite-pal/sense/amb-pal.html) |

## アクトの解説

### インクルード

```cpp
#include <TWELITE>
#include <NWK_SIMPLE>
#include <PAL_>
```

 動作センサーパルのボードビヘイビア[`<PAL_MOT>`](/v0.1.3/boards/pal/pal_mot)をインクルードします。

### setup()

```cpp
void setup() {
	/*** SETUP section */
	// board
	auto&& brd = the_twelite.board.use<PAL_MOT>();
	brd.set_led(LED_TIMER::BLINK, 100);

	// the twelite main class
	the_twelite
		<< TWENET::appid(APP_ID)     
		<< TWENET::channel(CHANNEL);

	// Register Network
	auto&& nwk = the_twelite.network.use<NWK_SIMPLE>();
	nwk	<< NWK_SIMPLE::logical_id(0xFE); 
	
	/*** BEGIN section */
	the_twelite.begin(); // start twelite!
	brd.sns_MC3630.begin(SnsMC3630::Settings(
		SnsMC3630::MODE_LP_14HZ, SnsMC3630::RANGE_PLUS_MINUS_4G));

	/*** INIT message */
	Serial << "--- PAL_MOT(Cont):" << FOURCHARS 
				 << " ---" << mwx::crlf;
}

```

最初にボードビヘイビア`<PAL_MOT>`を登録します。ボードビヘイビアの初期化時にセンサーやDIOの初期化が行われます。最初に行うのは、ボードのDIP SWなどの状態を確認してから、ネットワークの設定などを行うといった処理が一般的だからです。

```cpp
auto&& brd = the_twelite.board.use<PAL_MOT>();

u8ID = (brd.get_DIPSW_BM() & 0x07) + 1;
if (u8ID == 0) u8ID = 0xFE; // 0 is to 0xFE
```

ここでは、ボード上の４ビットDIP SWのうち３ビットを読み出して子機のIDとして設定しています。0の場合は、ID無しの子機(`0xFE`)とします。

LEDの設定を行います。ここでは 10ms おきに ON/OFF の点滅の設定をします（スリープを行い起床時間が短いアプリケーションでは、起床中は点灯するという設定とほぼ同じ意味合いになります）。

```cpp
	brd.set_led(LED_TIMER::BLINK, 10); // blink (on 10ms/ off 10ms)
```

#### 加速度センサーの初期化

```cpp
	brd.sns_MC3630.begin(SnsMC3630::Settings(
		SnsMC3630::MODE_LP_14HZ, SnsMC3630::RANGE_PLUS_MINUS_4G));
```

加速度センサーの計測を開始します。加速度センサーの設定(`SnsMC3630::Settings`)には計測周波数と測定レンジを指定します。ここでは14HZの計測(`SnsMC3630::MODE_LP_14HZ`)で、±4Gのレンジ(`SnsMC3630::RANGE_PLUS_MINUS_4G`)で計測します。

開始後は加速度センサーの計測が秒14回行われ、その値はセンサー内部のFIFOキューに保存されます。センサーに28回分の計測が終わった時点で通知されます。

### begin()

`begin()`関数は`setup()`関数を終了し(そのあとTWENETの初期化が行われる)一番最初の`loop()`の直前で呼ばれます。

```cpp
void begin() {
	sleepNow(); // the first time is just sleeping.
}
```

`setup()`終了後に`sleepNow()`を呼び出し初回スリープを実行します。

### sleepNow()

```cpp
void sleepNow() {
	pinMode(PAL_MOT::PIN_SNS_INT, WAKE_FALLING);
	the_twelite.sleep(60000, false);
}
```

スリープに入るまえに加速度センサーのDIOピンの割り込み設定をします。FIFOキューが一定数まで到達したときに発生する割り込みです。`pinMode()`を用います。２番めのパラメータは`PIN_MODE::WAKE_FALLING`を指定しています。これはHIGHからLOWへピンの状態が変化したときに起床する設定です。

３行目で`the_twelite.sleep()`でスリープを実行します。パラメータの60000は、TWELITE PAL ボードのウォッチドッグをリセットするために必要な起床設定です。リセットしないと60秒経過後にハードリセットがかかります。

###

### wakeup()

加速度センサーのFIFO割り込みにより、スリープから復帰し起床すると `wakeup()` が呼び出されます。そのあと`loop()` が都度呼び出されます。`wakeup()`の前に、UARTなどの各ペリフェラルやボード上のデバイスのウェイクアップ処理（ウォッチドッグタイマーのリセットなど）が行われます。例えばLEDの点灯制御を再始動します。

```cpp
void wakeup() {
	Serial << "--- PAL_MOT(Cont):" << FOURCHARS
	       << " wake up ---" << mwx::crlf;

	b_transmit = false;
	txid[0] = 0xFFFF;
	txid[1] = 0xFFFF;
}
```

ここでは`loop()`で使用する変数の初期化を行っています。

### loop()

ここでは、加速度センサー内のFIFOキューに格納された加速度情報を取り出し、これをもとにパケット送信を行います。パケット送信完了後に再びスリープを実行します。

```cpp
void loop() {
	auto&& brd = the_twelite.board.use<PAL_MOT>();

	if (!b_transmit) {
		if (!brd.sns_MC3630.available()) {
			Serial << "..sensor is not available." 
					<< mwx::crlf << mwx::flush;
			sleepNow();
		}

		// send a packet
		Serial << "..finish sensor capture." << mwx::crlf
			<< "  seq=" << int(brd.sns_MC3630.get_que().back().t) 
			<< "/ct=" << int(brd.sns_MC3630.get_que().size());

		// calc average in the queue.
		{
			int32_t x = 0, y = 0, z = 0;
			for (auto&& v: brd.sns_MC3630.get_que()) {
				x += v.x;
				y += v.y;
				z += v.z;
			}
			x /= brd.sns_MC3630.get_que().size();
			y /= brd.sns_MC3630.get_que().size();
			z /= brd.sns_MC3630.get_que().size();

			Serial << format("/ave=%d,%d,%d", x, y, z) << mwx::crlf;
		}

		for (int ip = 0; ip < 2; ip++) {
			if(auto&& pkt = 
				the_twelite.network.use<NWK_SIMPLE>().prepare_tx_packet())
				
				// set tx packet behavior
				pkt << tx_addr(0x00)
					<< tx_retry(0x1)
					<< tx_packet_delay(0, 0, 2);
			
				// prepare packet (first)
				uint8_t siz = (brd.sns_MC3630.get_que().size() >= MAX_SAMP_IN_PKT)
									? MAX_SAMP_IN_PKT : brd.sns_MC3630.get_que().size();
				uint16_t seq = brd.sns_MC3630.get_que().front().t;
			
				pack_bytes(pkt.get_payload()
					, make_pair(FOURCHARS, 4)
					, seq 
					, siz
				);

				// store sensor data (36bits into 5byts, alas 4bits are not used...)
				for (int i = 0; i < siz; i++) {
					auto&& v = brd.sns_MC3630.get_que().front();
					uint32_t v1;

					v1  = ((uint16_t(v.x/2) & 4095) << 20)  // X:12bits
						| ((uint16_t(v.y/2) & 4095) <<  8)  // Y:12bits
						| ((uint16_t(v.z/2) & 4095) >>  4); // Z:8bits from MSB
					uint8_t v2 = (uint16_t(v.z/2) & 255);   // Z:4bits from LSB
					pack_bytes(pkt.get_payload(), v1, v2); // add into pacekt entry.
					brd.sns_MC3630.get_que().pop(); // pop an entry from queue.
				}

				// perform transmit
				MWX_APIRET ret = pkt.transmit();

				if (ret) {
					Serial << "..txreq(" << int(ret.get_value()) << ')';
					txid[ip] = ret.get_value() & 0xFF;
				} else {
					sleepNow();
				}
			}
		}

		// finished tx request
		b_transmit = true;
	} else {
		if(		the_twelite.tx_status.is_complete(txid[0])
			 && the_twelite.tx_status.is_complete(txid[1]) ) {

			sleepNow();
		}
	}
}

```

`b_transmit`変数によって`loop()`内の振る舞いを制御しています。送信要求が成功した後、この値を1にセットしパケット送信完了待ちを行います。

```cpp
	if (!b_transmit) {
```

最初にセンサーがavailableかどうかを確認します。割り込み起床後であるため、availableでないのは通常ではなく、そのままスリープします。

```cpp
if (!brd.sns_MC3630.available()) {
	Serial << "..sensor is not available." 
			<< mwx::crlf << mwx::flush;
	sleepNow();
}
```

無線送信パケットでは使用しないのですが、取り出した加速度の情報を確認してみます。

```cpp
Serial << "..finish sensor capture." << mwx::crlf
	<< "  seq=" << int(brd.sns_MC3630.get_que().front().t) 
	<< "/ct=" << int(brd.sns_MC3630.get_que().size());

// calc average in the queue.
{
	int32_t x = 0, y = 0, z = 0;
	for (auto&& v: brd.sns_MC3630.get_que()) {
		x += v.x;
		y += v.y;
		z += v.z;
	}
	x /= brd.sns_MC3630.get_que().size();
	y /= brd.sns_MC3630.get_que().size();
	z /= brd.sns_MC3630.get_que().size();

	Serial << format("/ave=%d,%d,%d", x, y, z) << mwx::crlf;
}
```

加速度センサーの計測結果は`brd.sns_MC3630.get_que()`で得られるFIFOキューに格納されます。

加速度センサーの計測結果を格納している構造体 `axis_xyzt` は x, y, z の三軸の情報に加え、続き番号 t が格納されています。

格納されているサンプル数はキューのサイズ(`brd.sns_MC3630.get_que().size()`)を読み出すことで確認できます。通常は２８サンプルですが処理の遅延等によりもう少し進む場合もあります。最初のサンプルは`front()`で取得することができます。その続き番号は`front().t`になります。

ここでは、サンプルをキューから取り出す前にサンプルの平均をとってみます。キューの各要素にはfor文(`for (auto&& v: brd.sns_MC3630.get_que()) { ... }`) でアクセスできます。for文内の `v.x, v.y, v.z` が各要素になります。ここでは各要素の合計を計算しています。for文終了後は要素数で割ることで平均を計算しています。

次にパケットを生成して送信要求を行いますが、データ量が大きいため２回に分けて送信します。そのため送信処理がfor文で２回行われます。

```cpp
		for (int ip = 0; ip < 2; ip++) {
```

送信するパケットに含めるサンプル数とサンプル最初の続き番号をパケットのペイロードの先頭部分に格納します。

```cpp
// prepare packet (first)
uint8_t siz = (brd.sns_MC3630.get_que().size() >= MAX_SAMP_IN_PKT)
? MAX_SAMP_IN_PKT : brd.sns_MC3630.get_que().size();
uint16_t seq = brd.sns_MC3630.get_que().front().t;

pack_bytes(pkt.get_payload()
	, make_pair(FOURCHARS, 4)
	, seq 
	, siz
);
```

最後に加速度データを格納します。先程は平均値の計算のためにキューの各要素を参照のみしましたが、ここではキューから１サンプルずつ読み出してパケットのペイロードに格納します。

```cpp
for (int i = 0; i < siz; i++) {
	auto&& v = brd.sns_MC3630.get_que().front();
	uint32_t v1;

	v1  = ((uint16_t(v.x/2) & 4095) << 20)  // X:12bits
		| ((uint16_t(v.y/2) & 4095) <<  8)  // Y:12bits
		| ((uint16_t(v.z/2) & 4095) >>  4); // Z:8bits from MSB
	uint8_t v2 = (uint16_t(v.z/2) & 255);   // Z:4bits from LSB
	pack_bytes(pkt.get_payload(), v1, v2); // add into pacekt entry.
	brd.sns_MC3630.get_que().pop(); // pop an entry from queue.
}
```

加速度センサーからのデータキューの先頭を読み出すのは`.front()`を用います。読みだした後`.pop()`を用いて先頭キューを開放します。

加速度センサーから取得されるデータは1Gを1000としたミリGの単位です。レンジを±4Gとしているため、12bitの範囲に入るように２で割って格納します。データ数を節約するため最初の4バイトにX,Y軸とZ軸の上位8bitを格納し、次の1バイトにZ軸の下位4bitの合計5バイトを生成します。

２回分の送信待ちを行うため送信IDは`txid[]`配列に格納します。

```cpp
MWX_APIRET ret = pkt.transmit();

if (ret) {
	Serial << "..txreq(" << int(ret.get_value()) << ')';
	txid[ip] = ret.get_value() & 0xFF;
} else {
	sleepNow();
}
```

その後、`loop()` 中 `b_transmit` が `true`になっている場合は、完了チェックを行い、完了すれば `sleepNow()` によりスリープします。

```cpp
} else {
	if(		the_twelite.tx_status.is_complete(txid[0])
		 && the_twelite.tx_status.is_complete(txid[1]) ) {

		sleepNow();
	}
}
```

送信完了に確認は `the_twelite.tx_status.is_complete()` で行っています。`txid[]`は送信時に戻り値として戻されたID値です。


# PAL\_MOT-oneshot

[PAL\_MOT](/v0.1.3/act_samples/pal_mot)アクトでは連続的に加速度データを取得して都度無線送信していました。このアクトではスリープ復帰後に数サンプル加速度データを取得しそのデータを送ります。

{% hint style="success" %}
このアクトの解説の前に[PAL\_MOT](/v0.1.3/act_samples/pal_mot)のアクトの解説をご覧ください。
{% endhint %}

本サンプルは、収録バージョンによって差が大きいため本ページでは２つの解説を記載します。

* [v2 ... 状態変数を用いた loop() 実装に書き換え](/v0.1.3/act_samples/pal_mot-oneshot#akutono-v-2)
* [初版 ... MWSDK2020\_05 版の SDK 添付](/v0.1.3/act_samples/pal_mot-oneshot#akutono)

## アクトの解説 (v2)

{% hint style="warning" %}
※ 最新のコードは「[サンプルアクト＞最新版の入手」](/v0.1.3/act_samples#no)を参照ください。
{% endhint %}

起床→加速度センサーの取得開始→加速度センサーのFIFO割り込み待ち→加速度センサーのデータの取り出し→無線送信→スリープという

起床→加速度センサーの取得開始→加速度センサーのFIFO割り込み待ち→加速度センサーのデータの取り出し→無線送信→スリープという流れになります。

{% hint style="info" %}
加速度センサーは、FIFOキューが一杯になるとFIFOキューへのデータ追加を停止します。
{% endhint %}

### 状態変数

```cpp
enum class E_STATE {
	INIT = 0,
	START_CAPTURE,
	WAIT_CAPTURE,
	REQUEST_TX,
	WAIT_TX,
	EXIT_NORMAL,
	EXIT_FATAL
} eState;
```

列挙体として `eState` 変数を宣言しています。

### begin()

```cpp
void begin() { 
	// sleep immediately, waiting for the first capture.
	sleepNow();
}
```

`setup()`を終了した後に呼ばれます。ここでは初回スリープを実行しています。

### wakeup()

```cpp
void wakeup() {
	Serial << crlf << "--- PAL_MOT(OneShot):" 
	       << FOURCHARS << " wake up ---" << crlf;
	eState = E_STATE::INIT;
}
```

起床後は状態変数`eState`を初期状態INITにセットしています。この後`loop()`が実行されます。

### loop()

```cpp
void loop() {
	auto&& brd = the_twelite.board.use<PAL_MOT>();
	bool loop_more;
	do {
	  loop_more = false;
	  switch(eState) {
 	    ...
	  }
	} while(loop_more);
```

`loop()` の基本構造は状態変数`eState`によるswitch ... case節です。eStateの初期状態はINITです。`loop_more`は状態変数を書き換えた直後、`loop()`を抜ける前にもう一度実行したいときに`true`にセットします。

以下では各case節を解説します。`eState`の初期状態はINITです。

```cpp
			case E_STATE::INIT:
				brd.sns_MC3630.get_que().clear(); // clear queue in advance (just in case).
				loop_more = true;
				eState = E_STATE::START_CAPTURE;
			break;
```

状態INITでは、初期化（結果格納用のキューのクリア）を行います。

```cpp
			case E_STATE::START_CAPTURE:
				u32tick_capture = millis();
				brd.sns_MC3630.begin(
					// 400Hz, +/-4G range, get four samples (can be one sample)
					SnsMC3630::Settings(
						SnsMC3630::MODE_LP_400HZ, SnsMC3630::RANGE_PLUS_MINUS_4G, 4)); 
				eState = E_STATE::WAIT_CAPTURE;
			break;
```

状態START\_CAPTUREでは、MC3630センサーのFIFO取得を開始します。ここでは400Hzで4サンプル取得できた時点でFIFO割り込みが発生する設定にしています。タイムアウトのチェックのため、開始時点のシステム時刻を`u32tick_capture`に格納します。

```cpp
			case E_STATE::WAIT_CAPTURE:
				if (brd.sns_MC3630.available()) {
					brd.sns_MC3630.end(); // stop now!
					eState = E_STATE::REQUEST_TX; loop_more = true;
				} else if ((millis() - u32tick_capture) > 100) {
					Serial << crlf << "!!!FATAL: SENSOR CAPTURE TIMEOUT.";
					eState = E_STATE::EXIT_FATAL;
				}
			break;
```

状態WAIT\_CAPTUREでは、FIFO割り込みを待ちます。割り込みが発生し結果格納用のキューにデータが格納されると`sns_MC3630.available()`が`true`になります。

タイムアウトした場合は状態EXIT\_FATALに遷移します。

```cpp
			case E_STATE::REQUEST_TX:
				u32tick_tx = millis();
				txid = TxReq();
				if (txid) {
					eState = E_STATE::WAIT_TX;
				} else {
					Serial << crlf << "!!!FATAL: TX REQUEST FAILS.";
					eState = E_STATE::EXIT_FATAL;
				}
			break;
```

状態REQUEST\_TXではローカル定義関数TxReq()を呼び出し、得られたセンサーデータの処理と送信パケットの生成、そうし尿級を行います。タイムアウトのチェックのため、開始時点のシステム時刻を`u32tick_tx`に格納します。

```cpp
			case E_STATE::WAIT_TX:
				if(the_twelite.tx_status.is_complete(txid.get_value())) {
					eState = E_STATE::EXIT_NORMAL; loop_more = true;
				} else if (millis() - u32tick_tx > 100) {
					Serial << crlf << "!!!FATAL: TX TIMEOUT.";
					eState = E_STATE::EXIT_FATAL;
				}
			break;
```

状態WAIT\_TXでは、無線パケットの送信完了を待ちます。

タイムアウト時には状態EXIT\_FATALに遷移します。

```cpp
			case E_STATE::EXIT_NORMAL:
				sleepNow();
			break;

			case E_STATE::EXIT_FATAL:
				Serial << flush;
				the_twelite.reset_system();
			break;
```

一連の動作が完了したときは状態EXIT\_NORMALに遷移しローカル定義の関数`sleepNow()`を呼び出しスリープを実行します。またエラーを検出した場合は状態EXIT\_FATALに遷移し、システムリセットを行います。

### MWX\_APIRET TxReq()

この関数では、センサーより得られたサンプル値の取得と、複数サンプルの平均値の計算、

```cpp
int32_t x = 0, y = 0, z = 0;
for (auto&& v: brd.sns_MC3630.get_que()) {
	x += v.x;
	y += v.y;
	z += v.z;
}
x /= brd.sns_MC3630.get_que().size();
y /= brd.sns_MC3630.get_que().size();
z /= brd.sns_MC3630.get_que().size();
```

取得サンプルの平均値を計算します。

{% hint style="info" %}
ここでは除算を行っていますが、TWELITEマイコンには除算回路がないため、計算時間を要する演算となります。例えば以下のような改良が考えられます。

* サンプル数を２のべき乗として、その数を変数に入れず直接指定した除算を行う（ビットシフトによる演算に最適化されます）。
* 平均値を求めず、合計値とサンプル数を送り、受信先で計算する。
  {% endhint %}

```cpp
auto&& x_minmax = std::minmax_element(
	get_axis_x_iter(brd.sns_MC3630.get_que().begin()),
	get_axis_x_iter(brd.sns_MC3630.get_que().end()));

brd.sns_MC3630.get_que().clear(); // clean up the queue
```

X軸の最大値と最小値を計算します。

ここではイテレータと`std::minmax_element()`アルゴリズムを用いて計算します。`get_axis_x_iter`はキューのイテレータをパラメータとして、`axis_xyzt`構造体の`.x`にアクセスするものです。

{% hint style="info" %}
C++ Standard Template Library のアルゴリズムを使用する例として`std::mimmax_element`紹介していますが、上述のforループ内で最大、最小を求めても構いません。
{% endhint %}

ここでキューをクリア`.sns_MC3630.get_que().clear()`しています。

```cpp
	// prepare tx packet
	if (auto&& pkt = the_twelite.network.use<NWK_SIMPLE>().prepare_tx_packet()) {		
		// set tx packet behavior
		pkt << tx_addr(0x00)  // 0..0xFF (LID 0:parent, FE:child w/ no id, FF:LID broad cast), 0x8XXXXXXX (long address)
			<< tx_retry(0x1) // set retry (0x1 send two times in total)
			<< tx_packet_delay(0, 0, 2); // send packet w/ delay
		
		// prepare packet (first)
		pack_bytes(pkt.get_payload() // set payload data objects.
				, make_pair(FOURCHARS, 4)  // just to see packet identification, you can design in any.
				, uint16_t(x)
				, uint16_t(y)
				, uint16_t(z)
				, uint16_t(*x_minmax.first)  // minimum of captured x
				, uint16_t(*x_minmax.second) // maximum of captured x
			);

		// perform transmit
		ret = pkt.transmit();

		if (ret) {
			Serial << "..txreq(" << int(ret.get_value()) << ')';
		}
```

最期にパケットの生成と送信を要求を行います。パケットには X, Y, Z 軸の加速度、X軸の最小値,Y軸の最小値を含めています。

## アクトの解説 (初版)

{% hint style="warning" %}
MWSDK2020\_05 版の SDK 添付のサンプルコードです。&#x20;

※ 最新のコードは「[サンプルアクト＞最新版の入手」](/v0.1.3/act_samples#no)を参照ください。
{% endhint %}

起床→加速度センサーの取得開始→加速度センサーのFIFO割り込み待ち→加速度センサーのデータの取り出し→無線送信→スリープという流れになります。

{% hint style="info" %}
加速度センサーは、FIFOキューが一杯になるとFIFOキューへのデータ追加を停止します。
{% endhint %}

### wakeup()

起床後加速度センサーを稼働させます。

```cpp
void wakeup() {
	Serial << mwx::crlf << "--- PAL_MOT(OneShot):" << FOURCHARS << " wake up ---" << mwx::crlf;
	auto&& brd = the_twelite.board.use<PAL_MOT>();

	brd.sns_MC3630.get_que().clear(); // clear queue in advance (just in case).
	brd.sns_MC3630.begin(SnsMC3630::Settings(
			SnsMC3630::MODE_LP_400HZ, SnsMC3630::RANGE_PLUS_MINUS_4G, 4)); 
				// 400Hz, +/-4G range, get four samples (can be one sample)

	b_transmit = false;
	txid = 0xFFFF;
}
```

加速度センサーの結果を保存するキューの内容を抹消(`.sns_MC3630.get_que().clear()`)しておきます。加速度センサーのサンプルの取得忘れがあったり、また停止させるまでに次のサンプルが取得したような場合を想定します。

ここで加速度センサーを都度開始します。設定は400Hz,±4G,FIFO割り込みは4サンプルとしています。4サンプルも必要ない場合は1サンプルの設定でも構いません。

### loop()

```cpp
void loop() {
	auto&& brd = the_twelite.board.use<PAL_MOT>();

	if (!b_transmit) {
		if (brd.sns_MC3630.available()) {
			brd.sns_MC3630.end(); // stop now!

			Serial << "..finish sensor capture." << mwx::crlf
				<< "  ct=" << int(brd.sns_MC3630.get_que().size());

			// get all samples and average them.
			int32_t x = 0, y = 0, z = 0;
			for (auto&& v: brd.sns_MC3630.get_que()) {
				x += v.x;
				y += v.y;
				z += v.z;
			}
			x /= brd.sns_MC3630.get_que().size();
			y /= brd.sns_MC3630.get_que().size();
			z /= brd.sns_MC3630.get_que().size();

			Serial << format("/ave=%d,%d,%d", x, y, z) << mwx::crlf;

			// just see X axis, min and max
			//auto&& x_axis = get_axis_x(brd.sns_MC3630.get_que());
			//auto&& x_minmax = std::minmax_element(x_axis.begin(), x_axis.end());
			auto&& x_minmax = std::minmax_element(
				get_axis_x_iter(brd.sns_MC3630.get_que().begin()),
				get_axis_x_iter(brd.sns_MC3630.get_que().end()));

			brd.sns_MC3630.get_que().clear(); // clean up the queue

			// prepare tx packet
			if (auto&& pkt = nwk.the_twelite.network.use<NWK_SIMPLE>()) {
				auto&& pkt = nwk.prepare_tx_packet(); // get new packet instance.

				// set tx packet behavior
				pkt << tx_addr(0x00)  // 0..0xFF (LID 0:parent, FE:child w/ no id, FF:LID broad cast), 0x8XXXXXXX (long address)
					<< tx_retry(0x1) // set retry (0x1 send two times in total)
					<< tx_packet_delay(0, 0, 2); // send packet w/ delay
				
				// prepare packet (first)
				pack_bytes(pkt.get_payload() // set payload data objects.
						, make_pair(FOURCHARS, 4)  // just to see packet identification, you can design in any.
						, uint16_t(x)
						, uint16_t(y)
						, uint16_t(z)
						, uint16_t(*x_minmax.first)  // minimum of captured x
						, uint16_t(*x_minmax.second) // maximum of captured x
					);

				// perform transmit
				MWX_APIRET ret = pkt.transmit();
				
				if (ret) {
					Serial << "..txreq(" << int(ret.get_value()) << ')';
					txid = ret.get_value() & 0xFF;
				} else {
					sleepNow();
				}
				
				// finished tx request
				b_transmit = true;
			}
		}
	} else {
		// wait until transmit completion.
		if(the_twelite.tx_status.is_complete(txid)) {
			sleepNow();
		}
	}
}

```

このアクトでは、サンプル取得後、すぐに加速度センサーの動作を停止します。

```cpp
if (brd.sns_MC3630.available()) {
	brd.sns_MC3630.end(); // stop now!
```

取得サンプルの平均値を計算します。

```cpp
int32_t x = 0, y = 0, z = 0;
for (auto&& v: brd.sns_MC3630.get_que()) {
	x += v.x;
	y += v.y;
	z += v.z;
}
x /= brd.sns_MC3630.get_que().size();
y /= brd.sns_MC3630.get_que().size();
z /= brd.sns_MC3630.get_que().size();
```

X軸の最大値と最小値を計算します。

```cpp
auto&& x_minmax = std::minmax_element(
	get_axis_x_iter(brd.sns_MC3630.get_que().begin()),
	get_axis_x_iter(brd.sns_MC3630.get_que().end()));

brd.sns_MC3630.get_que().clear(); // clean up the queue
```

ここではイテレータと`std::minmax_element()`アルゴリズムを用いて計算します。`get_axis_x_iter`はキューのイテレータをパラメータとして、`axis_xyzt`構造体の`.x`にアクセスするものです。

最後にキューをクリア`.sns_MC3630.get_que().clear()`しています。


# PulseCounter

[パルスカウンター](/v0.1.3/api-reference/predefined_objs/pulsecounter)を用いたアクト例です。

パルスカウンターは、マイコンを介在せず信号の立ち上がりまたは立ち下りの回数を計数するものです。不定期のパルスを計数し一定回数までカウントが進んだ時点で無線パケットで回数を送信するといった使用方法が考えられます。

{% hint style="success" %}
このアクトの解説の前に[BRD\_APPTWELITEの解説](/v0.1.3/act_samples/brd_apptwelite)をご覧ください。

受信の確認のため[Parent\_MONOSTICK](/v0.1.3/act_samples/parent_monostick)の解説をご覧ください。
{% endhint %}

## アクトの機能

* 子機側のDIO8に接続したパルスを計数し、一定時間経過後または一定数のカウントを検出した時点で無線送信する。
* 子機側はスリープしながら動作する。<br>

## アクトの使い方

### 必要なTWELITE

| 役割 | 例                                                                                                                                                                                                                                                                                                                           |
| -- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 親機 | <p><a href="https://mono-wireless.com/jp/products/MoNoStick/">MONOSTICK BLUEまたはRED</a></p><p>アクト<a href="/pages/-Lvtl_DIu32MgNvsUahk">Parent\_MONOSTICK</a>を動作させる。</p>                                                                                                                                                      |
| 子機 | <p>１．<a href="https://mono-wireless.com/jp/products/TWE-Lite-DIP/index.html">TWELITE DIP</a></p><p>２．<a href="https://mono-wireless.com/jp/products/twelite-pal/BnR/index.html">BLUE PAL または RED PAL</a> +<a href="https://mono-wireless.com/jp/products/twelite-pal/sense/amb-pal.html">環境センサーパル AMBIENT SENSE PAL</a></p> |

## アクトの解説

### setup()

```cpp
// Pulse Counter setup
PulseCounter.setup();
```

パルスカウンターの初期化を行います。

### begin()

```cpp
void begin() {
	// start the pulse counter capturing
	PulseCounter.begin(
		  100 // 100 count to wakeup
		, PIN_INT_MODE::FALLING // falling edge
		);

	sleepNow();
}
```

パルスカウンターの動作を開始し、初回スリープを実行します。`PulseCounter.begin()`の最初のパラメータは、起床割り込みを発生させるためのカウント数`100`で、２番目は立ち下がり検出`PIN_INT_MODE::FALLING`を指定しています。

### wakeup()

```cpp
void wakeup() {
	Serial	<< mwx::crlf
			<< "--- Pulse Counter:" << FOURCHARS << " wake up ---"
			<< mwx::crlf;

	if (!PulseCounter.available()) {
		Serial << "..pulse counter does not reach the reference value." << mwx::crlf;
		sleepNow();
	}
}
```

起床時に`PulseCounter.available()`を確認しています。availableつまり`true`になっていると、指定したカウント数以上のカウントになっていることを示します。ここでは`false`の場合再スリープしています。

カウント数が指定以上の場合は`loop()`で送信処理と送信完了待ちを行います。

### loop()

```cpp
uint16_t u16ct = PulseCounter.read();
```

パルスカウント値の読み出しを行います。読み出した後カウンタはリセットされます。


# WirelessUART

WirelessUARTはシリアル通信を行います。

{% hint style="success" %}
このアクトの解説の前に[BRD\_APPTWELITEの解説](/v0.1.3/act_samples/brd_apptwelite)をご覧ください。
{% endhint %}

## アクトの機能

* ２台のUART接続のTWELITE同士をアスキー書式で通信する。

## アクトの使い方

### 必要なTWELITE

いずれかを２台。

* [MONOSTICK BLUE または RED](https://mono-wireless.com/jp/products/MoNoStick/index.html)
* [TWELITE R](https://mono-wireless.com/jp/products/TWE-LITE-R/index.html) でUART接続されている[TWELITE DIP](https://mono-wireless.com/jp/products/TWE-Lite-DIP/index.html)など

## アクトの解説

### setup()

```cpp
void setup() {
	/*** SETUP section */
	// the twelite main class
	the_twelite
		<< TWENET::appid(APP_ID)    // set application ID (identify network group)
		<< TWENET::channel(CHANNEL) // set channel (pysical channel)
		<< TWENET::rx_when_idle();  // open receive circuit (if not set, it can't listen packts from others)

	// Register Network
	auto&& nwk = the_twelite.network.use<NWK_SIMPLE>();
	
	uid = random(1, 5); // set uid by random() (1..4)
	nwk	<< NWK_SIMPLE::logical_id(uid); // set Logical ID. (0xFE means a child device with no ID)

	/*** BEGIN section */
	SerialParser.begin(PARSER::ASCII, 128); // Initialize the serial parser
	the_twelite.begin(); // start twelite!

	/*** INIT message */
	Serial << "--- WirelessUart (id=" << int(uid) << ") ---" << mwx::crlf;
}
```

論理IDは`random(1,5)`により1,2,3,4のいずれかの値に割り当てています。通常は、論理IDを設定保存したデータ、またはDIP SWといったハードウェアの情報から生成します。

```cpp
SerialParser.begin(PARSER::ASCII, 128); 
```

[シリアルパーサー](/v0.1.3/api-reference/predefined_objs/serialparser)を初期化します。

### loop()

```cpp
while(Serial.available())  {
	if (SerialParser.parse(Serial.read())) {
		Serial << ".." << SerialParser;
		const uint8_t* b = SerialParser.get_buf().begin();
		uint8_t addr = *b; ++b; // the first byte is destination address.
		transmit(addr, b, SerialParser.get_buf().end());
	}
}
```

シリアルからのデータ入力があった時点で、シリアルパーサーに１バイト入力します。アスキー形式が最後まで受け付けられた時点で`SerialParser.parse()`は`true`を戻します。

`SerialParser`は内部バッファに対して`smplbuf`でアクセスできます。上の例ではバッファの１バイト目を送信先のアドレスとして取り出し、２バイト目から末尾までを`transmit()`関数に渡します。

パケットを受信したときには、送信元を先頭バイトにし続くペイロードを格納したバッファ`smplbuf_u8<128> buf`を生成し、出力用のシリアルパーサー`serparser_attach pout`からシリアルに出力しています。

```cpp
if (the_twelite.receiver.available()) {
	auto&& rx = the_twelite.receiver.read();
	
	// check the packet header.
	const uint8_t* p = rx.get_payload().begin();
	if (rx.get_length() > 4 && !strncmp((const char*)p, (const char*)FOURCHARS, 4)) {
		Serial << format("..rx from %08x/%d", rx.get_addr_src_long(), rx.get_addr_src_lid()) << mwx::crlf;

		smplbuf_u8<128> buf;
		mwx::pack_bytes(buf			
				, uint8_t(rx.get_addr_src_lid())            // src addr (LID)
				, make_pair(p+4, rx.get_payload().end()) );	// data body

		serparser_attach pout;
		pout.begin(PARSER::ASCII, buf.begin(), buf.size(), buf.size());
		Serial << pout;
	}
}
```

## テスト用のコマンド

{% hint style="warning" %}
テストデータは必ず**ペースト機能**を用いてターミナルに入力してください。入力にはタイムアウトがあるためです。

参考： TWE ProgrammerやTeraTermでのペーストはAlt+Vを用います。
{% endhint %}

{% hint style="info" %}
入力の末尾にCR LFが必要です。

最初はCR LFが省略できるXで終わる系列を試してください。終端文字列が入力されない場合は、その系列は無視されます。
{% endhint %}

### 例

```
:FE00112233X

:FE001122339C

```

任意の子機宛に`00112233`を送付します。

### 例

```
:03AABBCC00112233X

:03AABBCC0011223366

```

子機３番に対して`AABBCC00112233`を送付します。

{% hint style="info" %}
TWE Programmerのターミナル機能を用いて送付する場合は、Alt+Vキーを用いてペーストします。
{% endhint %}


# Slp\_Wk\_and\_Tx

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

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

{% hint style="success" %}
このアクトの解説の前に[BRD\_APPTWELITEの解説](/v0.1.3/act_samples/brd_apptwelite)をご覧いただくことを推奨します。
{% endhint %}

{% hint style="info" %}
TWELITE STAGE 2020\_05 には収録されていません。以下のリンク（GitHub)より入手ください。

<https://github.com/monowireless/Act_samples>
{% endhint %}

## アクトの機能

* 起動後、速やかにスリープする
  1. `setup()` : 初期化する
  2. `begin()` : スリープに遷移する
* スリープ起床後、状態変数を初期化し、以下の順に動作を行う
  1. `wakeup()`: スリープからの起床、各初期化を行う
  2. `loop()`状態INIT->WORK\_JOBに遷移: 何らかの処理を行う（このActでは 1ms ごとの TickCount ごとにカウンタを更新し 100 カウント後にTX状態に進む）
  3. `loop()` 状態TX: 送信要求を行う
  4. `loop()` 状態WAIT\_TX: 送信完了待ちを行う
  5. `loop()` 状態EXIT\_NORMAL: スリープする (1. に戻る)
* `loop()` 状態EXIT\_FATAL: エラーが発生した場合は、モジュールリセットする

## アクトの解説

### インクルード

```cpp
#include <TWELITE>
#include <NWK_SIMPLE>

#include "Common.h"
```

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

`Common.h` には基本的な定義に加え、以下の列挙体が定義されています。こちらを状態変数として利用します。

```cpp
enum class E_STATE {
    INIT = 0,    // INIT STATE
    WORK_JOB,    // do some job (e.g sensor capture)
    TX,          // reuest transmit
    WAIT_TX,     // wait its completion
    EXIT_NORMAL, // normal exiting.
    EXIT_FATAL   // has a fatal error (will do system reset)
};
```

### setup()

```cpp
void setup() {
	/*** SETUP section */
	txreq_stat = MWX_APIRET(false, 0);

	// the twelite main class
	the_twelite
		<< TWENET::appid(APP_ID)    // set application ID (identify network group)
		<< TWENET::channel(CHANNEL) // set channel (pysical channel)
		<< TWENET::rx_when_idle(false);  // open receive circuit (if not set, it can't listen packts from others)

	// Register Network
	auto&& nwk = the_twelite.network.use<NWK_SIMPLE>();
	nwk	<< NWK_SIMPLE::logical_id(DEVICE_ID); // set Logical ID. 

	/*** BEGIN section */
	the_twelite.begin(); // start twelite!

	/*** INIT message */
	Serial << "--- Sleep an Tx Act ---" << crlf;
}
```

`the_twelite`クラスオブジェクトの初期化とネットワーク `<NWK_SIMPLE>` の登録を行います。

### begin()

```cpp
void begin() {
	Serial << "..begin (run once at boot)" << crlf;
	SleepNow();
}
```

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

### wakeup()

```cpp
void wakeup() {
	Serial << crlf << int(millis()) << ":wake up!" << crlf;
	eState = E_STATE::INIT;
}
```

起床直後に呼び出されます。ここでは状態変数`eState`を初期状態`E_STATE::INIT`にセットします。この後`loop()`が呼び出されます。

### loop()

```cpp
void loop() {
	bool loop_more; // if set, one more loop on state machine.
	do {
		loop_more = false; // set no-loop at the initial.
		switch(eState) {
			case E_STATE::INIT: 
				eState = E_STATE::WORK_JOB; loop_more = true;
				break;
			case E_STATE::WORK_JOB:
			  if (TickTimer.available())
			    if(--dummy_work_count == 0)
						eState = E_STATE::TX: loop_more = true;
				break;
			case E_STATE::TX:
			  txreq_stat = vTransmit();
				if(txreq_stat) {
				  u32millis_tx = millis();
				  eState = E_STATE::WAIT_TX;
				} else {
				  eState = E_STATE::EXIT_FATAL; loop_more = true;
				}
			  break;
			case E_STATE::WAIT_TX:
			  if (the_twelite.tx_status.is_complete(txreq_stat.get_value())) {
			    eState = E_STATE::EXIT_NORMAL; loop_more = true;
			  } else if (millis() - u32millis_tx > 100) {
			    eState = E_STATE::EXIT_FATAL; loop_more = true;
			  }
			  break;
			case E_STATE::EXIT_NORMAL:
				SleepNow();
				break;
			case E_STATE::EXIT_FATAL:
				the_twelite.reset_system();
				break;
		}
	} while (loop_more);
```

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

このコードでは`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状態ではシステムリセットを行います。

### void SleepNow()

```cpp
void SleepNow() {
	uint16_t u16dur = SLEEP_DUR;
	u16dur = random(SLEEP_DUR - SLEEP_DUR_TERMOR, SLEEP_DUR + SLEEP_DUR_TERMOR);

	Serial << int(millis()) << ":sleeping for " << int(u16dur) << "ms" << crlf << mwx::flush;
	the_twelite.sleep(u16dur, false);
}
```

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

### MWX\_APIRET vTransmit()

```cpp
MWX_APIRET vTransmit() {
	Serial << int(millis()) << ":vTransmit()" << crlf;

	if (auto&& pkt = the_twelite.network.use<NWK_SIMPLE>().prepare_tx_packet()) {
		// set tx packet behavior
		pkt << tx_addr(0x00)  // 0..0xFF (LID 0:parent, FE:child w/ no id, FF:LID broad cast), 0x8XXXXXXX (long address)
			<< tx_retry(0x1) // set retry (0x3 send four times in total)
			<< tx_packet_delay(0,0,2); // send packet w/ delay (send first packet with randomized delay from 0 to 0ms, repeat every 2ms)

		// prepare packet payload
		pack_bytes(pkt.get_payload() // set payload data objects.
			, make_pair(FOURCC, 4) // string should be paired with length explicitly.
			, uint32_t(millis()) // put timestamp here.
		);
		
		// do transmit 
		//return nwksmpl.transmit(pkt);
		return pkt.transmit(); 
	}

	return MWX_APIRET(false, 0);
}
```

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

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


# API

Act/behavior Programming Interface

{% hint style="danger" %}
MWXライブラリのAPIは、今後、改善を目的として仕様の変更を行う場合があります。
{% endhint %}


# クラスオブジェクト

クラスオブジェクトは、MWXライブラリであらかじめ定義されたオブジェクトで、TWENETを取り扱う`the_twelite`、ペリフェラルの利用のためのオブジェクトが定義されています。

各オブジェクトは`.setup()`, `.begin()`メソッドの呼び出しを行って初期化する必要があります。(UART0を利用する`Serial`オブジェクトのみ初期化は必要ありません)


# the\_twelite

TWENET 利用の中核クラス (mwx::twenet)

`the_twelite`オブジェクトは、TWENETの利用手続きをまとめたもので、無線の基本設定やスリープ等の手続きなど無線マイコンを操作するための手続きが含まれます。

## 概要

`the_twelite`は`setup()`関数内で設定と開始`the_twelite.begin()`を行います。`setup()`以外では設定は行えません。

```cpp
void setup() {
  ...
 	the_twelite
		<< TWENET::appid(APP_ID)    
		<< TWENET::channel(CHANNEL) 
		<< TWENET::rx_when_idle();  
  ...
  the_twelite.begin();
}
```

上記の例では、アプリケーションIDの設定、通信チャネルの設定、受信回路の設定を行っています。

様々な手続きが含まれます。

```cpp
// シリアル番号を得る
uint32_t u32hwser = the_twelite.get_hw_serial();

// チャネルを 11 に設定する
the_twelite.change_channel(11);

// １秒のスリープを行う
the_twelite.sleep(1000);

// リセットを行う
the_twelite.reset_system();
```

また無線ネットワークを取り扱うクラスやボード対応をまとめたクラス、ユーザ記述のイベントドリブン処理を行うクラスを登録できるようになっています。このクラスを登録することにより、専用化した機能を手軽に利用できるようになります。これらのクラスを本解説中では「[**ビヘイビア**](/v0.1.3/api-reference/behavior)」と呼称します。

```cpp
void setup() {
	/*** SETUP section */
	// use PAL_AMB board support.
	auto&& brd = the_twelite.board.use<PAL_AMB>();
	
	...
	
	// Register Network
	auto&& nwk = the_twelite.network.use<NWK_SIMPLE>();
	nwk << NWK_SIMPLE::logical_id(u8ID);

```

上記の例では環境センサーパル`<PAL_AMB>`と、シンプル中継ネットワーク`<NWK_SIMPLE>`の２種類を登録しています。これらを登録することにより環境センサーパル上のセンサーなどハードウェアを簡易に取り扱うことが出来ます。また煩雑な無線パケットの取り扱いについて中継の処理や重複で届いたパケットの自動破棄などの機能を暗黙に持たせることが出来ます。

## メソッド

{% hint style="warning" %}
MWXライブラリには、ここで紹介したメソッド以外にも定義されています。

アクト記述には直接関係ないもの、設定しても有効に機能しないもの、内部的に使用されているものが含まれます。
{% endhint %}

### `<<`演算子 (設定)

オブジェクト`the_twelite`の初期設定を行うために`<<`演算子を用います。

以下に挙げる設定用のクラスオブジェクトを入力とし、設定をしなければデフォルト値が適用されます。

#### TWENET::appid(uint32\_t id)

パラメータ`id`に指定したアプリケーションIDを設定します。これは必須指定です。

設定の読み出しは `uint32_t the_twelite.get_appid()` で行います。

#### TWENET::channel(uint8\_t ch)

パラメータ`ch`に指定したチャネル番号（`11`..`26`)を設定します。

設定の読み出しは`uint8_t the_twelite.get_channel()`で行います。

#### TWENET::tx\_power(uint8\_t pw)

パラメータ`pw`に指定した出力設定を(`0`..`3`)を設定します。デフォルトは(3:出力減衰無し)です。

設定値の読み出しは`uint8_t the_twelite.get_tx_power()`で行います。

#### TWENET::rx\_when\_idle(uint8\_t bEnable)

パラメータ`bEnable`が`1`であれば常に受信回路を動作させ、他からの無線パケットを受信できるようになります。デフォルトは`0`で、もっぱら送信専用となります。

設定値の読み出しは`uint8_t the_twelite.get_rx_when_idle()`で行います。

{% hint style="warning" %}
MWXライブラリコード中には他にも設定項目がありますが、現時点ではライブラリの機能に無関係な設定であったり、設定を行うと矛盾を起こす可能性があるものです。
{% endhint %}

### begin()

```cpp
void begin()
```

事前に設定（`<<`演算子参照)や、ビヘイビアの登録を済ませた後に実行します。通常は`setup()`関数内の一番最後に記述します。

* `the_twelite` 設定完了
* ビヘイビアの初期化

{% hint style="warning" %}
TWENETの初期化は `setup()` 関数が終了した後にも実行されます。多くの処理はTWENETが終了した後に実行するようになっているため、ここでは初期化以外の処理を行わないようにしてください。
{% endhint %}

#### 例

```cpp
void setup() {
	// use PAL_AMB board support.
	auto&& brd = the_twelite.board.use<PAL_AMB>();
	
	// settings
 	the_twelite
		<< TWENET::appid(APP_ID)    
		<< TWENET::channel(CHANNEL) 
		<< TWENET::rx_when_idle();  
	
	// Register Network
	auto&& nwk = the_twelite.network.use<NWK_SIMPLE>();
	nwk << NWK_SIMPLE::logical_id(u8ID);
	
	// somo others
	
	// begin the TWENET!
	the_twelite.begin();
}
```

### change\_channel()

```cpp
inline bool change_channel(uint8_t u8Channel)
```

チャネル設定を変更します。失敗時にはチャネルは変更されず`false`を戻します。

### get\_channel\_phys()

```cpp
uint8_t get_channel_phys()
```

現在設定中のチャネル番号(11..26)を取得する。MAC層のAPIより取得します。

### get\_hw\_serial()

```cpp
inline uint32_t get_hw_serial()
```

モジュールのシリアル番号を取得します。

### sleep()

```cpp
inline void sleep(
        uint32_t u32Periodms, 
        bool_t bPeriodic = true, 
        bool_t bRamOff = false, 
        uint8_t u8Device = TWENET::SLEEP_WAKETIMER_PRIMARY)
```

モジュールをスリープさせる。

| パラメータ         | 解説                                                                                                                                             |
| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `u32Periodms` | スリープ時間\[ms]                                                                                                                                    |
| `bPeriodic`   | <p>前回の起床時間をもとに次の起床時間を再計算する。<br>※次の起床タイミングが迫っているなどの理由で、現在のタイミングからになる場合もあります。</p>                                                                |
| `bRamoff`     | `true`に設定すると、RAMを保持しないスリープになる（起床後は`wakeup()`ではなく`setup()`から再初期化する必要がある)                                                                        |
| `u8Device`    | <p>スリープに用いるウェイクアップタイマーの指定。</p><p><code>TWENET::SLEEP\_WAKETIMER\_PRIMARY</code>または <code>TWENET::SLEEP\_WAKETIMER\_SECONDARY</code>を指定する。 </p> |

{% hint style="info" %}
スリープ前に組み込みオブジェクトやビヘイビアの `on_sleep()` メソッドが呼び出され、スリープ前の手続きを行います。スリープ復帰後は反対に `on_wakeup()` メソッドにより復帰処理が行われます。
{% endhint %}

### is\_wokeup\_by\_dio()

```cpp
bool is_wokeup_by_dio(uint8_t port)
```

スリープからの復帰要因が指定したディジタルピンである場合に`true`を返します。

### is\_wokeup\_by\_wktimer()

```cpp
bool is_wokeup_by_wktimer()
```

スリープからの復帰要因がウェイクアップタイマーである場合に`true`を返します。

## ビヘイビア

`twe_twelite`には３つのビヘイビアを登録でき、これらを格納する以下のクラスオブジェクトを定義されています。

* `network` :  ネットワークを実装するビヘイビアです。通常は[`<NWK_SIMPLE>`](/v0.1.3/networks/nwk_simple)を登録します。
* `board`: ボード対応のビヘイビアです。ボード上の各デバイス利用手続きが付加されます。
* `app`: ユーザアプリケーションを記述したビヘイビアです。割り込みやイベント記述、ステートマシンによる状態遷移によるふるまいの記述が可能です。また複数のアプリケーション記述を定義しておいて、起動時に全く振る舞いの違うアプリケーションを選択する記述が容易に行えます。

### use\<B>()

```cpp
// 例
auto&& brd = the_twelite.board.use<PAL_AMB>();
```

ビヘイビア\<B>を登録します。登録は[`setup()`](/v0.1.3/api-reference/functions/setup)内で行います。戻り値は登録したビヘイビアに対応するオブジェクトの参照です。

登録後は登録時と同じ書式でオブジェクトの取得を行います。

{% hint style="danger" %}
誤ったビヘイビアを指定した場合は、パニック動作（無限ループ）となりプログラムの動作が停止します。
{% endhint %}

{% hint style="info" %}
グローバル変数としてビヘイビアのオブジェクトを宣言することを想定していません。利用都度`use<B>()`を用いてください。

```cpp
void loop() {
  auto&& nwk = the_twelite.network.use<NWK_SIMPLE>();
}
```

ただし、グローバル変数にオブジェクトのポインタを定義して以下のように記述することは可能です。（MWXライブラリでは原則としてポインタ型の利用を最小限にとどめ参照型を利用する方針ですので、下記のような記述は推奨しません）

```cpp
NWK_SIMPLE *pNwk = nullptr;

setup() {
  auto&& nwk = the_twelite.network.use<NWK_SIMPLE>();
	pNwk = &nwk;
}

void transmit() {
  if (auto&& pkt = pNwk->prepare_tx_packet()) {
    ...
  }
}
```

{% endhint %}

## クラスオブジェクト

`the_twelite`には上述の`board`, `network`, `app`の３つのクラスオブジェクトが定義されていますが他に以下が定義されています。

### tx\_status

送信完了状態を通知する。

{% hint style="info" %}
イベントドリブンのビヘイビアの記述では[transmit\_complete()コールバック](/v0.1.3/api-reference/behavior#transmit_complete)で管理します。
{% endhint %}

#### is\_complete()

```cpp
bool is_complete(uint8_t cbid)
```

指定したIDのパケットが送信完了したときに`true`を返す。

#### is\_success()

```cpp
bool is_success(uint8_t cbid)
```

指定したIDのパケットが送信完了し、かつ送信成功したときに`true`を返す。

### receiver

受信パケットを取得する。

{% hint style="info" %}
イベントドリブンのビヘイビアの記述では[receive()コールバック](/v0.1.3/api-reference/behavior#receive)で取得します。
{% endhint %}

{% hint style="warning" %}
`read()`メソッドで得られる受信パケットデータは、続くパケットが受信処理時に上書きされる設計となっています。`available`直後に読み出してなにか短い処理をする場合は問題になることはありませんが、原則として読み出し→アプリケーションが使うため必要なデータのコピー→`loop()`の終了を速やかに行います。例えば`loop()`中で長い`delay()`を行うと受信パケットの取りこぼしなどが発生します。
{% endhint %}

#### available()

```cpp
bool available()
```

まだ読み出していない受信パケットが存在する場合に`true`を返す。

#### read()

```cpp
packet_rx& read()
```

パケットを読み出します。


# Analogue

ADC (mwx::periph\_analogue.hpp)

Analogueは、ADCの実行と値の取得を行います。一度に複数のチャネルを連続取得でき、またこれをタイマーなどの周期に合わせて逐次実行可能です。

## 定数

### ピンの定義

| 定数                                                                                                   | 種別            | 標準アプリでのピン名 |
| ---------------------------------------------------------------------------------------------------- | ------------- | :--------: |
| `uint8_t PIN_ANALOGUE::A1 = 0`                                                                       | ADC1ピン        |     AI1    |
| `uint8_t PIN_ANALOGUE::A2 = 1`                                                                       | ADC2ピン        |     AI3    |
| <p><code>uint8\_t PIN\_ANALOGUE::A3 = 2</code></p><p><code>uint8\_t PIN\_ANALOGUE::D0 = 2</code></p> | ADC3ピン (DIO0) |     AI2    |
| <p><code>uint8\_t PIN\_ANALOGUE::A4 = 3</code></p><p><code>uint8\_t PIN\_ANALOGUE::D1 = 3</code></p> | ADC4ピン (DIO1) |     AI4    |
| `uint8_t PIN_ANALOGUE::VCC = 4`                                                                      | Vcc 電源電圧      |            |

{% hint style="warning" %}
標準アプリ(App\_Twelite)では、半導体データシート中のピン名ADC2/ADC3が、TWELITE DIPの並びにあわせてAI3/AI2 となっています。ご注意ください。
{% endhint %}

## メソッド

### setup()

```cpp
void setup(
        bool bWaitInit = false,
        uint8_t kick_ev = E_AHI_DEVICE_TICK_TIMER,
        void (*fp_on_finish)() = nullptr) 
```

ADCの初期化を行います。setup()では、半導体内部のレギュレータの始動、周期実行するためのタイマーデバイスの指定、指定チャネルすべてのADCが終了したときに呼び出されるコールバック関数の指定します。

| パラメータ          | 解説                                                                                                                                                                                                |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `bWaitInit`    | `true`を指定すると、半導体内部のレギュレータの初期化を待つ。                                                                                                                                                                 |
| `kick_ev`      | <p>周期実行に指定するタイマーデバイスを指定する。指定可能なデバイスは、以下の５種類で、初回以外は割り込みハンドラ内でADが開始される。</p><p><code>E\_AHI\_DEVICE\_TICK\_TIMER (TickTimer)</code></p><p><code>E\_AHI\_DEVICE\_TIMER0 .. 4 (Timer0 .. 4)</code></p> |
| `fp_on_finish` | 指定されたポートすべてのADCが終了後に、割り込みハンドラ内から呼び出されるコールバック関数。ADC計測値をFIFOキューなどに別途格納したい場合に利用する。                                                                                                                   |

### begin()

```cpp
void begin(uint8_t bmPorts, uint8_t capt_tick = 1)
```

１番目のパラメータにはADCを行いたいポートを指定します。ポートの指定はピンの定義で述べたポート番号に対応するビットをセットしたビットマップになります。例えば `PIN_ANALOGUE::A2`と`PIN_ANALOGUE::VCC`の２つのピンの値を得たい場合は `(1 <<PIN_ANALOGUE::A1 | 1<<PIN_ANALOGUE::VCC )`を指定します。[`pack_bits`](/v0.1.3/api-reference/functions-1/utility/pack_bits)を用い`pack_bits(PIN_ANALOGUE::A1,PIN_ANALOGUE::VCC)`のように記述することもできます。

`begin()`の呼び出し後、速やかに最初のADC処理が開始され、その終了割り込から次のピンの処理を開始します。すべての処理が終われば(指定されている場合）コールバック関数が呼び出されます。次のタイマー割り込みが発生まで待ってから新たなADC処理を開始します。

２番目のパラメータは、ACを開始するまでのタイマー割り込みの回数を指定します。例えば`TickTimer`は1msごとに呼び出されますが、パラメータに`16`を指定すれば 16msごとの処理になります。

```cpp
void begin()
```

デフォルトのADCピン(`PIN_ANALOGUE::A1`,`PIN_ANALOGUE::A2`)を指定してADC処理を開始します。`end()`では中断したADC処理を再開します。

### end()

```cpp
void end()
```

ADC処理を終了し、半導体内部のレギュレータを停止します。

### available()

```cpp
inline bool available()
```

ADCの値が取得後に`true`になります。本関数により確認した後は次のADC完了までは`false`です。

### read(), read\_raw()

```cpp
inline int16_t read(uint8_t s)
inline int16_t read_raw(uint8_t s)
```

ADC値を読み出します。パラメータには読み出したいADCピンを指定します。`read()`はmVに変換した読み値、`read_raw()`はADCの値(0..1023)を戻します。

{% hint style="info" %}
Vccは`read()`で読み出すことを推奨します。`read_raw()`の値からmVに変換するには、特別な変換式を適用する必要があるためです。
{% endhint %}

{% hint style="warning" %}
ADC完了(available)後、次のADC処理が実行するタイミング付近まで遅れて値を読み出すと、次のADC値が戻される場合があります。ADCの処理は割り込みハンドラで実施されているため`loop()`の処理中であっても値が更新されるためです。
{% endhint %}

## ADC割り込みハンドラ

ADCの割り込みハンドラは`setup()`の呼び出し時に`periph_analogue::ADC_handler()`に設定されます。

## スリープ時の振る舞い

ADCが`begin()`により周期実行状態であれば、スリープ復帰後もADC処理を再開します。


# Buttons

ディジタル入力管理クラス (mwx::periph\_buttons)

ディジタル入力の変化を検出します。このクラスは、同じ検出値が複数回得られたときに変化を検出します。メカ式のボタンのチャタリングの影響を小さくするのに有効です。

## メソッド

### setup()

```cpp
void setup(uint8_t max_history);
```

パラメータの`max_history`は、`begin()`で設定可能な参照回数の最大値です。ここではメモリーの確保と初期化を行います。

### begin()

```cpp
void begin(uint32_t bmPortMask,
				   uint8_t u8HistoryCount,
				   uint16_t tick_delta);
```

`Buttons`の動作を開始します。１番目のパラメータ`bmPortMask`は監視対象のディジタル入力のビットマップを指定します。bit 0がDIO 0, ... , bit N がDIO Nに対応します。複数指定することができます。２番目の`u8HistoryCount`は値の確定をするのに必要な回数です。３番目の`tick_delta`は値の確認を行う間隔をmsで指定します。

値の確定には`u8HistoryCount*tick_delta`\[ms]かかることになります。例えば`u8HistoryCount`=5, `tick_delta`=4の場合は、状態の確定に最低約20msかかります。

確認は`TickTimer`のイベントハンドラで行っています。割り込みハンドラではないので、処理等の遅延の影響を受けますが、メカ式ボタン等のチャタリング抑制には十分です。

### end()

```cpp
void end()
```

`Buttons`の動作を終了します。

### available()

```cpp
inline bool available()
```

変化が検出されたときに`true`を返します。`read()`を実行するとクリアされます。

### read()

```cpp
bool read(uint32_t& u32port, uint32_t& u32changed)
```

availableになったとき呼び出します。`u32port`は現在の入力DIOのビットマップ、`u32changed`は変化が検出されたDIOのビットマップです。

Buttonsが動作していない場合は`false`を返します。

## 動作について

### 初回の値確定

Buttonsが動作を開始した時点では、DIOの入力状態は未確定です。値が確定した時点でavailableになります。このとき`read()`で読み出すビットマップのMSB(bit31)が１にセットされます。

動作確定を要するため、入力値が定常的に変化するポートを監視する目的では利用できません。

### スリープ

スリープ前にButtonsが稼働状態であれば、復帰後に再開します。再開後、初回確定を行います。


# PulseCounter

パルスカウンタ (mwx::periph\_pulse\_counter)

パルスカウンタは、マイコンのCPUが稼働していない時もパルスを読み取り計数する回路です。パルスカウンターは２系統あります。PC0は`PulseCounter0`, PC1は`PulseCounter1`に割り当てられます。

ま&#x305F;**`PulseCounter`**&#x306F;**`PulseCounter1`**&#x306E;別名です。

## メソッド

### begin()

```cpp
void begin(uint16_t refct = 0, 
           E_PIN_INT_MODE edge = PIN_INT_MODE::FALLING,
           uint8_t debounce = 0)
```

オブジェクトを初期化し、計数を開始します。１番目のパラメータ`refct`は割り込みやavailable判定の基準となるカウント数です。この数を超えたときにアプリケーションに報告されます。また`refct`には0を指定することもできます。この場合は、スリープ起床要因にはなりません。

２番目のパラメータ`edge`は割り込みが立ち会がり(`PIN_INT_MODE::RISING`)か立下り(`PIN_INT_MODE::FALLING`)を指定します。

３番目の`debounce`は、0,1,2,3の値をとります。1,2,3の設定はノイズの影響を小さくするため値の変化の検出に連続した同じ値を要する設定です。

|  設定 | 連続サンプル数 | 最大検出周波数 |
| :-: | :-----: | :-----: |
| `0` |    -    |  100Khz |
| `1` |    2    |  3.7Khz |
| `2` |    4    |  2.2Khz |
| `3` |    8    |  1.2Khz |

### end()

```cpp
void end()
```

検出を中止します。

### available()

```cpp
inline bool available()
```

指定カウント数(`begin()`の`refct`)が０の場合は、カウントが１以上で`true`を返します。

指定カウント数(`begin()`の`refct`)が1以上の場合は、検出回数が指定カウント数を超えた場合に`true`となります。

### read()

```cpp
uint16_t read()
```

カウント値を読み出します。読み出し後にカウント値を０にリセットします。


# Serial

TWELITE の UART0 ポート  (mwx::serial\_jen)

[`mwx::stream`](/v0.1.3/api-reference/classes/twe-stream) を実装し TWELITE の UART0 で入出力する。

* `Serial`オブジェクトはシステム起動時に UART0, 115200 bps で初期化され、ライブラリ内で初期化処理が行われます。ユーザコード上は、`setup()`から利用できます。
* `Serial1`オブジェクトは、ライブラリ内で用意されていますが、初期化処理は行っていません。UART1を有効化するためには、必要な初期化手続き `Serial1.setup(), Serial1.begin()` を行ってください。

{% hint style="danger" %}
起動直後の `setup(), wakeup()` やスリープ直前の `flush` 処理で、出力が不安定になる場合があります。
{% endhint %}

###

### setup()

```cpp
void setup(uint16_t buf_tx, uint16_t buf_rx)
```

オブジェクトの初期化を行う。

* TX/RX用のFIFOバッファのメモリ確保
* TWE\_tsFILE 構造体のメモリ確保

{% hint style="warning" %}
`Serial`(UART0) は ライブラリ内で `setup()` の呼び出しが自動で行われます。ユーザによる呼び出しを行う必要はありません。

また、`Serial` (UART0) のバッファサイズは、コンパイル時に決定されます。マクロ `MWX_SER_TX_BUFF` (未指定時は 768), `MWX_SER_RX_BUFF`(未指定時 256) により変更できます。
{% endhint %}

| パラメータ    | 解説              |
| -------- | --------------- |
| `buf_tx` | TX用のFIFOバッファサイズ |
| `buf_rx` | RX用のFIFOバッファサイズ |

### begin()

```cpp
void begin(unsigned long speed = 115200, uint8_t config = 0x06)
```

ハードウェアの初期化を行う。

{% hint style="warning" %}
`Serial` (UART0) は ライブラリ内で `begin()` の呼び出しが自動で行われます。ユーザによる呼び出しを行う必要はありません。
{% endhint %}

| パラメータ    | 解説                |
| -------- | ----------------- |
| `speed`  | UART のボーレートを指定する。 |
| `config` | 未使用 (8N1固定)       |

{% hint style="info" %}
指定したボーレートの下２桁の数値は０に丸めて処理します。またハードウェアの制限により指定したボーレートより誤差が生じます。
{% endhint %}

### end()

（未実装）ハードウェアの使用を停止する。

### get\_tsFile()

```cpp
TWE_tsFILE* get_tsFile();
```

Cライブラリで利用する `TWE_tsFILE*` 形式での構造体を得る。

{% hint style="info" %}
Serial (UART) では、`_sSerial` 構造体が定義されています。
{% endhint %}


# SerialParser

シリアルポート向き書式入力 (mwx::serial\_parser)

この組み込みクラスはシリアルポートでの書式入力に利用することを想定して組み込みオブジェクトとして定義しています。

初期化時(`begin()`)にヒープから内部で使用するバッファ領域を確保する`mwx::serial_parser<mwx::alloc_heap<uint8_t>>`型として定義されています。

詳細はクラス [serparser](/v0.1.3/api-reference/classes/ser_parser) を参照してください。


# SPI

SPIバス (MASTER) の読み書きを行います。

## 注意事項

## 定数

| 定数                                                                       | 意味               |
| ------------------------------------------------------------------------ | ---------------- |
| <p><code>const uint8\_t</code><br><code>SPI\_CONF::MSBFIRST</code></p>   | MSB を先頭ビットにする    |
| <p><code>const uint8\_t</code><br><code>SPI\_CONF::LSBFIRST</code></p>   | LSB を先頭ビットにする    |
| <p><code>const uint8\_t</code><br><code>SPI\_CONF::SPI\_MODE0</code></p> | SPI MODE 0 に設定する |
| <p><code>const uint8\_t</code><br><code>SPI\_CONF::SPI\_MODE1</code></p> | SPI MODE 1 に設定する |
| <p><code>const uint8\_t</code><br><code>SPI\_CONF::SPI\_MODE2</code></p> | SPI MODE 2 に設定する |
| <p><code>const uint8\_t</code><br><code>SPI\_CONF::SPI\_MODE3</code></p> | SPI MODE 3 に設定する |

## 初期化と終了

SPIバスの利用手続きは`begin()`メソッドによります。

### begin()

```cpp
void begin(uint8_t slave_select, SPISettings settings)
SPISettings(uint32_t clock, uint8_t bitOrder, uint8_t dataMode)
```

ハードウェアの初期化を行います。

{% hint style="warning" %}
スリープ復帰後にも本処理が必須です。
{% endhint %}

| パラメータ          | 解説                                                                                                                                                                                                                                                                                                           |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `slave_select` | <p>使用するSPIスレーブのセレクトピンを指定する。</p><p><code>0 : DIO19</code></p><p><code>1 : DIO0 (DIO 19 は予約されます)</code></p><p><code>2 : DIO1 (DIO 0,19 は予約されます)</code></p>                                                                                                                                                     |
| `settings`     | <p>SPIのバス設定を指定します。<br><br><code>clock</code>\[hz]でSPIバスの周波数を指定します。指定した周波数に近いディバイザが選択されます。16Mhzまたは16Mhzを偶数で割った値になります。<br></p><p><code>bitOrder</code>は<code>SPI\_CONF::MSBFIRST</code>か<code>SPI\_CONF::LSBFIRST</code>を指定します。<br></p><p><code>dataMode</code>は<code>SPI\_CONF::SPIMODE0..3</code>を指定します。</p> |

###

#### 例

```cpp
void setup() {
  ...
  SPI.begin(0, SPISettings(2000000, SPI_CONF::MSBFIRST, SPI_CONF::SPI_MODE3));
  ...
}

void wakeip() {
  ...
  SPI.begin(0, SPISettings(2000000, SPI_CONF::MSBFIRST, SPI_CONF::SPI_MODE3));
  ...
}
```

### end()

```cpp
void end()
```

SPIのハードウェアの利用を終了します。

## 読み書き

読み書きの手続きは、以下の2種類あります。いずれかを選択して利用します。

* [メンバ関数版 (以下のメンバ関数を用いた入出力)](/v0.1.3/api-reference/predefined_objs/spi/spi-member)\
  `beginTransaction(), endTransaction(), transfer(), transfer16(), transfer32()`
* [ヘルパークラス版(stream機能が使用可能)](/v0.1.3/api-reference/predefined_objs/spi/spi-helperclass)\
  `transceiver`

####


# SPI (メンバ関数版)

`begin()`メソッドによりハードウェアの初期化を行った後、`beginTransaction()`によりバスの読み書きができるようになります。`beginTransaction()`を実行するとSPIのセレクトピンが選択されます。読み書きは`transfer()`関数を用います。SPIは読み出しと書き込みを同時に実行します。

### beginTransaction()

```cpp
void beginTransaction()
void beginTransaction(SPISettings settings)
```

バスの利用開始を行います。SPIのセレクトピンをセットします。

`settings`パラメータを与えて呼び出した場合は、バスの設定を行います。

### endTransaction()

```cpp
void endTransaction()
```

バスの利用を終了します。SPIのセレクトピンを解除します。

### transfer(), transfer16(), transfer32()

```cpp
inline uint8_t transfer(uint8_t data)
inline uint16_t transfer16(uint16_t data)
inline uint32_t transfer32(uint32_t data)
```

&#x20;バスの読み書きを行います。`trasnfer()`は8bit、`transfer16()`は16bit、`transfer32()`は32bitの転送を行います。


# SPI (ヘルパークラス版)

ヘルパークラス版はより抽象度が高い実装です。読み書きを行うオブジェクト `transceiver` を生成することが、バスの利用開始となり、オブジェクトを破棄するとバス利用の終了手続きを行います。

if文の判定式内でオブジェクトの生成を行うことで、オブジェクトの有効期間はif節内のスコープに限定され、if節を抜けた時点でオブジェクトは破棄され、その時点でバスの利用終了の手続きを行います。

```cpp
uint8_t c;
if (auto&& trs = SPI.get_rwer()) { // オブジェクトの生成とデバイスの通信判定
   // このスコープ(波かっこ)内が trs の有効期間。
   trs << 0x00; // 0x00 を mwx::stream インタフェースで書き出し
   trs >> c;    // 読み出したデータをcに格納。
} 
// if 節を抜けるところで wrt は破棄され、バスの利用終了
```

また、読み書きオブジェクトは、`mwx::stream`インタフェースを実装しているため`<<`演算子などを利用することができます。

* バスの利用開始と終了をオブジェクトの有効期間と一致させることで、ソースコードの見通しを良くし、また終了手続きの記述漏れなどを防ぎます
* `mwx::stream`インタフェースによる読み書き手続きを統一します

## 読み書き

読み込み処理とその終了手続きをスコープ内 `if() { ... }` で行うためのヘルパークラスを用いた読み込み方法。

```cpp
inline uint8_t _spi_single_op(uint8_t cmd, uint8_t arg) {
    uint8_t d0, d1;
    if (auto&& x = SPI.get_rwer()) {
        d0 = x.transfer(cmd); (void)d0;
        d1 = x.transfer(arg);
        // (x << (cmd)) >> d0;
        // (x << (arg)) >> d1;
    }

    return d1;
}
```

上記では `get_rwer()`  メソッドにより生成された `x`  オブジェクトを用いて１バイトずつ読み書きを行っています。

1. `if(...)` 内で `x` オブジェクトを生成します。同時にSPIバスのセレクトピンをセットします。（型は、型推論によるユニバーサル参照 `auto&&` で解決しています。）
2. 生成した `x` オブジェクトには `operator bool ()` が定義されており、判定式の評価として利用される。SPIバスの場合は常に `true` となる。
3. `x` オブジェクトには `uint8_t transfer(uint8_t)` メソッドが定義されていて、これを呼び出すことでSPIに対して8bitの読み書き転送を行。
4. `if() { ... }` スコープの末尾で `x` のデストラクタが呼び出され、SPIバスのセレクトピンを解除します。

### get\_rwer()

```cpp
periph_spi::transceiver get_rwer()
```

SPIバスの読み書きに用いるワーカーオブジェクトを取得します。

## ワーカーオブジェクト

### transfer() transfer16() transfer32()

```cpp
uint8_t transfer(uint8_t val)
uint16_t transfer16(uint16_t val)
uint32_t transfer32(uint32_t val)
```

それぞれ8bit,16bit,32bitの転送を行い、読み取り結果を書き込んだデータ幅と同じデータ幅で返す。

### <<演算子

```cpp
operator << (int c)
operator << (uint8_t c)
operator << (uint16_t c) 
operator << (uint32_t c)
```

`int`型,`uint8_t`型は8bitの転送を行います。

`uint16_t`型、`uint32_t`型は、それぞれ16bitの転送、32bitの転送を行います。

転送結果は最大16バイトの内部FIFOキューに格納され `>>` 演算子により読み出します。バッファが大きくないので、転送都度読み出すことを想定します。

### >>演算子

```cpp
operator >> (uint8_t& c)
operator >> (uint16_t& c)
operator >> (uint32_t& c)

null_stream(size_t i = 1)
operator >> (null_stream&& p)
```

直前の転送と同じデータ幅の変数を指定します。

読み出した結果が不要の場合はnull\_stream()オブジェクトを使用します。iで指定したデータバイト分だけ読み飛ばします。


# TickTimer

システムタイマー (mwx::periph\_ticktimer)

TickTimerはTWENETの内部制御用に利用され、暗黙に実行されています。タイマーの周期は1msです。`loop()`中でTickTimerイベントにより1msごとの処理を記述する目的で`available()`メソッドのみを定義しています。

{% hint style="warning" %}
必ず1ms刻みでavailableになる訳ではない点に注意してください。

ユーザプログラムの記述内容や、システム内部の割り込み処理などが要因で、大きな遅延が発生することもあり、イベントが飛ばされるような場合もあります。

```cpp
void loop() {
  if (TickTimer.available()) {
    if ((millis() & 0x3FF) == 0) { // これは処理されない場合がある
      Serial << '*';
    }
  }
}
```

{% endhint %}

## メソッド

available()

```cpp
inline bool available()
```

TickTimer割り込み発生後にセットされ、その直後の`loop()`で`true`になります。`loop()`終了後にクリアされます。


# Timer0 .. 4

タイマー, PWM (mwx::periph\_timer)

タイマーでは、指定周期でのソフトウェア割り込みを発生させる目的、指定周期でPWM出力を行う２つの機能があります。TWELITE無線モジュールには0..4まで合計５つのタイマーが利用可能です。

組み込みオブジェクト名は `Timer0..4` ですが、このページでは `TimerX`と記載します。

## メソッド

### setup()

```cpp
void setup()
```

タイマーを初期化します。この呼び出しにより必要なメモリ領域の確保を行います。

### begin()

```cpp
void begin(uint16_t u16Hz, bool b_sw_int = true, bool b_pwm_out = false)
```

タイマーを開始します。１番目のパラメータは、タイマーの周期でHzで指定します。２番目のパラメータを`true`にするとソフトウェア割り込みが有効になります。３番目のパラメータを`true`にするとPWM出力を有効にします。

{% hint style="info" %}
`change_hz()`で周波数を変更することが出来ます。`change_hz()`では`begin()`の指定より細かい指定が可能です。

`change_duty()`でPWM出力のデューティー比を変更できます。

割り込みハンドラの処理を記述するには、[アプリケーションビヘイビア](/v0.1.3/api-reference/behavior)の定義が必要です。
{% endhint %}

### end()

```cpp
void end()
```

タイマーの動作を停止します。

### available()

```cpp
inline bool available()
```

タイマー割り込みが発生した直後の`loop()`で`true`になり、`loop()`が終了すれば`false`になります。

### change\_duty()

```cpp
void change_duty(uint16_t duty, uint16_t duty_max = 1024)
```

デューティー比の設定を行う。１番目のパラメータにデューティ比を指定します（小さい値を指定すると波形の平均はGNDレベルに近づき、大きい値を指定するとVccレベルに近づく）。２番目のパラメータはデューティ比の最大値を指定します。

{% hint style="warning" %}
`duty_max`は`1024,4096,16384`のいずれかの指定を推奨します。

内部でのカウント値の計算に除算が発生しますが、これら３つに限りビットシフトによる演算を行っていますが、これ以外の値では計算量の大きい除算処理が実行されます。
{% endhint %}

### change\_hz()

```cpp
void change_hz(uint16_t hz, uint16_t mil = 0) 
```

タイマーの周波数を設定します。２番目のパラメータは周波数の小数点３桁分の値を整数で指定します。例えば 10.4 Hz としたい場合は `hz=10, mil=400` と指定します。


# Wire

二線シリアル(I2C) master の読み書き (mwx::periph\_wire)

二線シリアル(I2C) master の読み書きを行います。

#### 型定義

以下の定義型で引数や戻り値の型を記載します。

```cpp
typedef uint8_t size_type;
typedef uint8_t value_type;
```

## 注意事項

{% hint style="warning" %}
API 中に STOP ビットの扱いが厳格でない呼び出しを行うものもあります。
{% endhint %}

{% hint style="info" %}
`write(), writer::operator() ()` には、本解説以外にもいくつか引数が定義されている。

* 固定配列型 \
  `uint8_t cmds[]={11,12};`\
  `...`\
  `Wire.write(cmds);`
* `initializer_list<>` 型\
  `Wire.write({11,12})`
  {% endhint %}

## 初期化と終了

### Wire インスタンスの生成

ライブラリ内でインスタンスの生成と必要な初期化は行われます。ユーザコードでは `Wire.begin()` を呼び出すことで利用可能となります。

`requestFrom()` メソッドを用いる場合、データを一時保管するための FIFO キューのサイズを指定できます。コンパイル時にマクロ`MWX_TWOWIRE_BUFF` に必要なバイト数を指定してコンパイルする。デフォルトは 32 バイトです。

> 例： `-DMWX_TWOWIRE_BUFF=128`

### begin()

```cpp
void begin(
    const size_type u8mode = WIRE_100KHZ,
    bool b_portalt = false)
```

ハードウェアの初期化を行います。

{% hint style="danger" %}
初期化せずにWireの操作を行うとTWELITE無線モジュールがハングアップします。
{% endhint %}

{% hint style="warning" %}
スリープからの起床時は、スリープ直前で動作していた場合、直前の状態に復帰します。
{% endhint %}

| パラメータ       | 解説                                                             |
| ----------- | -------------------------------------------------------------- |
| `u8mode`    | <p>バススピードを指定する。</p><p>WIRE\_100KHZ または WIRE\_400KHZ を指定する。</p> |
| `b_portalt` | ハードウェアのピン割り当てを変更する。                                            |

### 例

```cpp
void setup() {
    ...
    Wire.begin();
    ...
}

void wakeup() {
    ...
    Wire.begin();
    ...
}
```

## 読み書き

読み書きの手続きは、以下の2種類あります。いずれかを選択して利用します。

* [メンバ関数版 (以下のメンバ関数を用いた入出力)](/v0.1.3/api-reference/predefined_objs/wire/wire-member)\
  `requestFrom(), beginTransmission(), endTransmission(), write()`
* [ヘルパークラス版(stream機能が使用可能)](/v0.1.3/api-reference/predefined_objs/wire/wire-helperclass)\
  `reader, writer`

####

## その他

### プローブ（デバイスの存在判定)

```cpp
bool probe(uint8_t address)
```

`address` で指定したデバイスが応答するかを確認します。デバイスが存在する場合は `true` が戻ります。


# Wire (メンバ関数版)

メンバ関数を利用した方法は、抽象度が比較的低く、C言語ライブラリで提供されるような一般的なAPI体系に倣っています。二線シリアルバスの操作手続きがより直感的です。

ただしバスの利用の開始と終了を明示的に意識して記述する必要があります。

## 読み込み

### requestFrom()

```cpp
size_type requestFrom(
    uint8_t u8address,
    size_type length,
    bool b_send_stop = true)
```

指定バイト数分を一括で読み出します。読みだした結果はキューに保存されるため、直後にキューが空になるまで `.read()` メソッドを呼び出すようにしてください。

| パラメータ              | 解説                                |
| ------------------ | --------------------------------- |
| `u8address`        | 読み出し対象のI2Cアドレス                    |
| `length`           | 読み出しバイト数                          |
| `b_send_stop=true` | `true` の時、読み込み終了時に`STOP`ビットを設定する。 |
| 戻り値型 `size_type`   | 読み出したバイト数。 `0` は読み出しの失敗。          |

#### コード例

```cpp
int len = Wire.requestFrom(0x70, 6);
for (int i = 0; i < 6; i++) {
  if (Wire.available()) {
		  au8data[i] = Wire.read();
    Serial.print(buff[i], HEX);
  }
}
// skip the rest (just in case)
// while (Wire.available()) Wire.read(); // normally, not necessary.

```

## 書き出し

書き出し処理は、`beginTransmission()` を実行後、`write()` メソッドにより行います。一連の書き出しが終了したら `endTranmission()` を呼びます。

```cpp
	#define DEV_ADDR (0x70)
	const uint8_t msg[2] = 
	  {SHTC3_SOFT_RST_H, SHTC3_SOFT_RST_L};

	Wire.beginTransmission(DEV_ADDR);
	Wire.write(msg, sizeof(msg));
	Wire.endTransmission();
```

###

### beginTransmission()

```cpp
void beginTransmission(uint8_t address)
```

書き出しの転送を初期化する。書き出し処理終了後、速やかに `endTransmission()` を呼び出す。&#x20;

| パラメータ       | 解説             |
| ----------- | -------------- |
| `u8address` | 書き出し対象のI2Cアドレス |

###

### write(value)

```cpp
size_type write(const value_type value)
```

１バイトの書き出しを行う。

| パラメータ           | 解説                 |
| --------------- | ------------------ |
| `value`         | 書き込むバイト            |
| 戻り値 `size_type` | 書き込んだバイト数。`0`はエラー。 |

### write(\*value, quantity)

```cpp
size_type write(
  const value_type* value,
  size_type quantity)
```

バイト列の書き出しを行います。

| パラメータ           | 解説               |
| --------------- | ---------------- |
| `*value`        | 書き込むバイト列         |
| `size_type`     | バイト数             |
| 戻り値 `size_type` | 書き込んだバイト数。0はエラー。 |

### endTransmission()

```cpp
uint8_t endTransmission(bool sendStop = true)
```

書き出しの終了処理を行います。&#x20;

| パラメータ             | 解説             |
| ----------------- | -------------- |
| `sendStop = true` | STOPビットを発行します。 |
| 戻り値 `uint8_t`     | 0: 成功 4: 失敗    |


# Wire (ヘルパークラス版)

ヘルパークラス版はより抽象度が高い実装です。読み書きに対応するオブジェクト `reader, writer` を生成することがバスの利用開始となり、オブジェクトを破棄するとバス利用の終了手続きを行います。

if文の判定式内でオブジェクトの生成を行うことで、オブジェクトの有効期間はif節内のスコープに限定され、if節を抜けた時点でオブジェクトは破棄され、その時点でバスの利用終了の手続きを行います。

```cpp
if (auto&& wrt = Wire.get_writer(...)) { // オブジェクトの生成とデバイスの通信判定
   // このスコープ(波かっこ)内が wrt の有効期間。
   wrt << 0x00; // 0x00 を mwx::stream インタフェースで書き出し
} 
// if 節を抜けるところで wrt は破棄され、バスの利用終了
```

また読み書きオブジェクトは`mwx::stream`インタフェースを実装しているため`<<`演算子などを利用することができます。

* バスの利用開始と終了をオブジェクトの有効期間と一致させることで、ソースコードの見通しを良くし、また終了手続きの記述漏れなどを防ぐ
* `mwx::stream`インタフェースによる読み書き手続きの統一

## 読み込み

読み込み処理とその終了手続きをスコープ内 `if() { ... }` で行うためのヘルパークラスを用いた読み込み方法です。

```cpp
const uint8_t DEV_ADDR = 0x70;
uint8_t au8data[6];
if (auto&& rdr = Wire.get_reader(DEV_ADDR, 6)) {
		for (auto&& c: au8data) {
			c = rdr();
		}
}

// same above
uint16_t u16temp, u16humd;
uint8_t u8temp_csum, u8humd_csum;
if (auto&& rdr = Wire.get_reader(SHTC3_ADDRESS, 6)) {
		rdr >> u16temp;
		rdr >> u8temp_csum;
		rdr >> u16humd;
		rdr >> u8humd_csum;
}
```

上記では `get_readr()`  メソッドにより生成された `rdr`  オブジェクトを用いて１バイトずつ読み出しします。 メソッドのパラメータには読み込みたい二線シリアル ID を指定します。

1. `if(...)` 内で `rdr` オブジェクトを生成。（型は、型推論によるユニバーサル参照 `auto&&` で解決しています。）
2. 生成した `rdr` オブジェクトには `operator bool ()` が定義されており、判定式の評価として利用される。指定された ID により通信が可能であれば `true` となる。
3. `rdr` オブジェクトには `int operator () (void)` 演算子が定義されていて、これを呼び出すことで２線シリアルバスから１バイトのデータを読み出す。読み込みに失敗したときは `-1` が戻り、成功した場合は読み込んだバイト値が戻る。
4. `if() { ... }` スコープの末尾で `rdr` のデストラクタが呼び出され、二線シリアルバスの `STOP` を行う。

### get\_reader(addr, read\_count=0)

```cpp
periphe_wire::reader
get_reader(uint8_t addr, uint8_t read_count = 0)
```

I2C 読み出しに用いるワーカーオブジェクトを取得します。

| パラメータ        | 解説                                                                                    |
| ------------ | ------------------------------------------------------------------------------------- |
| `addr`       | 読み込み用のI2Cアドレス                                                                         |
| `read_count` | 読み出しバイト数（この値を指定すると最後の転送で STOP ビットを発行する）。0を指定した場合は STOP ビットなしとなる（デバイスによっては動作するものもあります） |

## 書き出し (writer)

書き出し処理とその終了手続きをスコープ内 `if() { ... }` で行うためのヘルパークラスを用いた読み込み方法です。

```cpp
const uint8_t DEV_ADDR = 0x70;
if (auto&& wrt = Wire.get_writer(DEV_ADDR)) {
	wrt(SHTC3_TRIG_H);
	wrt(SHTC3_TRIG_L);
}

// same above
if (auto&& wrt = Wire.get_writer(DEV_ADDR)) {
	wrt << SHTC3_TRIG_H; // int type is handled as uint8_t
	wrt << SHTC3_TRIG_L;
}

```

上記では `get_writer()`  メソッドにより生成された `wrt`  オブジェクトを用いて１バイトずつ書き出す。 メソッドのパラメータには読み出したい二線シリアル ID を指定します。

1. `if(...)` 内で `wrt` オブジェクトを生成する。（型名は長くなるため auto で解決）
2. 生成した `wrt` オブジェクトには `operator bool ()` が定義されており、判定式の評価として利用される。指定された ID により通信が可能であれば `true` となる。
3. `wrt` オブジェクトには `int operator () (void)` 演算子が定義されていて、これを呼び出すことで２線シリアルバスに１バイトのデータを書き出しす。失敗したときは `-1` が戻り、成功した場合は書き込んだバイト値が戻る。
4. `if() { ... }` スコープの末尾で `wrt` のデストラクタが呼び出され、二線シリアルバスの `STOP` を行う。

### get\_writer()

```cpp
periph_wire::writer
get_writer(uint8_t addr)
```

I2C書き出しに用いるワーカーオブジェクトを取得します。

| パラメータ  | 解説            |
| ------ | ------------- |
| `addr` | 書き出し用のI2Cアドレス |

## ワーカーオブジェクト (writer)

### <<演算子

```cpp
operator << (int c)
operator << (uint8_t c)
operator << (uint16_t c) 
operator << (uint32_t c)
```

`int`型,`uint8_t`型は8bitの転送を行います。

### ()演算子

```cpp
operator() (uint8_t val)
operator() (int val)
```

１バイト書き出す。

## ワーカーオブジェクト  (reader)

### >>演算子

```cpp
operator >> (uint8_t& c)
operator >> (uint16_t& c)
operator >> (uint32_t& c)
operator >> (uint8_t(&c)[N]) // Nバイトの固定配列
```

それぞれのデータ型のサイズ分だけ読み出します。

### ()演算子

```cpp
int operator() (bool b_stop = false)

//例
uint8_t dat[6];
if (auto&& rdr = Wire.get_reader(0x70)) {
  for(uint8_t& x : dat) {
    x = rdr();
  }
}
```

１バイト読み出します。エラーがある場合は-1を戻し、正常時は読み出したバイト値を戻します。

`b_stop`を`true`にすると、その読み出しにおいてSTOPビットを発行します。

## 例

以下の例は、環境センサーパルの温湿度センサーSHTC3の計測例です。

```cpp
Wire.begin();
// reset (may not necessary...)
if (auto&& wrt = Wire.get_writer(0x70)) {
	wrt << 0x80; // SHTC3_SOFT_RST_H
	wrt << 0x05; // SHTC3_SOFT_RST_L
}

delay(5); // wait some

// start read
if (auto&& wrt = Wire.get_writer(0x70)) {
	wrt << 0x60; // SHTC3_TRIG_H
	wrt << 0x9C; // SHTC3_TRIG_L
}

delay(10); // wait some

// read result
uint16_t u16temp, u16humd;
uint8_t u8temp_csum, u8humd_csum;
if (auto&& rdr = Wire.get_reader(0x70, 6)) {
	rdr >> u16temp;
	rdr >> u8temp_csum;
	rdr >> u16humd;
	rdr >> u8humd_csum;
}

// checksum 0x31, init=0xFF
if (CRC8_u8CalcU16(u16temp, 0xff) != u8temp_csum) {
	Serial << format("{SHTC3 T CKSUM %x}", u8temp_csum); }
if (CRC8_u8CalcU16(u16humd, 0xff) != u8humd_csum) {
	Serial << format("{SHTC3 H CKSUM %x}", u8humd_csum); }

// calc temp/humid (degC x 100, % x 100)
int16_t i16Temp = (int16_t)(-4500 + ((17500 * int32_t(u16temp)) >> 16));
int16_t i16Humd = (int16_t)((int32_t(u16humd) * 10000) >> 16);

Serial << "temp=" << int(i16Temp) 
	   << ",humid=" << int(i16Humd) << mwx::crlf;
```


# クラス


# MWX\_APIRET

32bit型をラップしたAPI戻り値クラス。MSB(bit31)は失敗、成功のフラグ。bit0..30は戻り値を格納するために使用します。

```cpp
class MWX_APIRET {
	uint32_t _code;
public:
	MWX_APIRET() : _code(0) {}
	MWX_APIRET(bool b) {
	  _code = b ? 0x80000000 : 0;
  }
	MWX_APIRET(bool b, uint32_t val) {
		_code = (b ? 0x80000000 : 0) + (val & 0x7fffffff);
	}
	inline bool is_success() const { return ((_code & 0x80000000) != 0); }
	inline bool is_fail() const { return ((_code & 0x80000000) == 0); }
	inline uint32_t get_value() const { return _code & 0x7fffffff; }
	inline operator uint32_t() const { return get_value(); }
	inline operator bool() const { return is_success(); }
};
```

## コンストラクタ

```cpp
MWX_APIRET()
MWX_APIRET(bool b)
MWX_APIRET(bool b, uint32_t val)
```

デフォルトコンストラクタは`false`,`0`の組み合わせで構築します。

また`bool`型と`uint32_t`型をパラメータとする明示的な構築も可能です。

`bool`型のコンストラクタを実装しているため、以下のように`true`/`false`を用いることができます。

```cpp
MWX_APIRET myfunc() {
  if (...) return true;
  else false;
}
```

## メソッド

### is\_success(), operator bool()

```cpp
inline bool is_success() 
inline operator bool()
```

MSBに`1`がセットされていれば`true`を返す。

```cpp
inline bool is_fail()
```

MSBが`0`の場合に`true`を返す。

```cpp
inline uint32_t get_value()
inline operator uint32_t()
```

bit0..30の値部を取得する。


# alloc

コンテナクラス(`smplbuf`, `smplque`)のテンプレート引数として指定し、内部で利用するメモリの確保または領域指定します。

{% hint style="warning" %}
このクラスはユーザコードから直接呼び出すものではありませんが、内部的にコンテナの宣言に用いられています。
{% endhint %}

| 内容                      |                     |
| ----------------------- | ------------------- |
| `alloc_attach<T>`       | すでにあるバッファを指定する      |
| `alloc_local<T, int N>` | Nバイトのバッファを内部に静的確保する |
| `alloc_heap<T>`         | 指定したサイズをヒープに確保する    |

`alloc_attach`や`alloc_heap`ではメモリ確保クラスに応じた初期化メソッド (`begin()`)を実行する必要があります。

## 初期化

```cpp
void attach(T* p, int n) // alloc_attach
void init_local()        // alloc_local
void init_heap(int n)    // alloc_heap
```

バッファー`p`・サイズ`n`で初期化します。

## メソッド

### alloc\_size()

```cpp
uint16_t alloc_size()
```

バッファのサイズを返す。

### &#x20;\_is\_attach(), \_is\_local(), \_is\_heap()

想定したallocクラスと違うメソッド呼び出し記述に対して、static\_assertのように、コンパイルエラーを発生させるためのメソッドです。


# axis\_xyzt

３軸の加速度センサーの値を格納するための構造体ですが、コンテナクラスに格納したときの利便性を上げるための手続きを追加しています。

```cpp
struct axis_xyzt {
    int16_t x;
    int16_t y;
    int16_t z;
    uint16_t t;
};
```

### get\_axis\_{x,y,z}\_iter()

```cpp
/*戻り型は長いテンプレート型名なのでauto&&と記載します*/
auto&& get_axis_x_iter(Iter p)
auto&& get_axis_y_iter(Iter p)
auto&& get_axis_z_iter(Iter p)
```

`axis_xyzt`を格納したコンテナクラスのイテレータをパラメータとして、X, Y, Z 軸のいずれかの要素にアクセスするイテレータを生成します。

以下の例では、`buf.begin()`, `buf.end()`をX軸用のイテレータとしてアルゴリズム`std::minmax_element`に用いています。

```cpp
#include <algorithm>

void myfunc() {  
  // コンテナクラス
  smplbuf_local<axis_xyzt, 10> buf;
  
  // テスト用にデータを投入
  buf[0] = { 1, 2, 3, 4 };
  buf[1] = { 2, 3, 4, 5 };
  ...
  
  // 最大、最小値を得るアルゴリズム
  auto&& minmax = std::minmax_element(
    get_axis_x_iter(buf.begin()),
    get_axis_x_iter(buf.end()));
  
  Serial << "min=" << int(*minmax.first)
        << ",max=" << int(*minmax.second) << mwx::crlf;
}
```

### get\_axis\_{x,y,z}()

```cpp
/*戻り型は長いテンプレート型名なのでauto&&と記載します*/
auto&& get_axis_x(T& c)
auto&& get_axis_y(T& c)
auto&& get_axis_z(T& c)
```

`axis_xyzt`を格納したコンテナクラスのXYZ軸のいずれかの軸を取り出した仮想的なコンテナクラスを生成する関数です。この生成したクラスには`begin()`と`end()`メソッドのみ実装されています。この`begin()`と`end()`メソッドで取得できるイテレータは前節[get\_axis\_{x,y,z}\_iter()](/v0.1.3/api-reference/classes/axis_xyzt#get_axis_-x-y-z-_iter)のイテレータと同じものになります。

```cpp
#include <algorithm>

void myfunc() {
  // コンテナクラス
  smplbuf_local<axis_xyzt, 10> buf;
  
  // テスト用にデータを投入
  buf[0] = { 1, 2, 3, 4 };
  buf[1] = { 2, 3, 4, 5 };
  ...
  
  // キューの中の X 軸を取り出す
  auto&& vx = get_axis_x(que);
  
  // 範囲for文の利用
  for (auto&& e : vx) { Serial << int(e) << ','; }
  
  // 最大、最小値を得るアルゴリズム
  auto&& minmax = std::minmax_element(
      vx.begin(), vx.end());
                          
  Serial << "min=" << int(*minmax.first)
        << ",max=" << int(*minmax.second) << mwx::crlf;
}
```


# packet\_rx

このクラスはTWENETの`tsRxDataApp`構造体のラッパークラスです。

このクラスオブジェクトは、[ビヘイビア](/v0.1.3/api-reference/behavior)の[コールバック関数](/v0.1.3/api-reference/behavior#receive)または`the_twelite.receiver.read()`により取得できます。

`packet_rx`では、特にパケットのデータペイロードを`smplbuf`コンテナで取り扱えるようにし、`expand_bytes()`などのユーティリティ関数によりペイロードの解釈記述を簡素化しています。

{% hint style="info" %}
現時点では、シンプル中継ネットワーク`<NWK_SIMPLE>`で必要とされるものを中心にメソッド等のインタフェースを実装しています。
{% endhint %}

## メソッド

### get\_payload()

```cpp
smplbuf_u8_attach& get_payload()
```

パケットのデータペイロードを取得する。

{% hint style="info" %}
`<NWK_SIMPLE>`を用いた場合は、先頭に`<NWK_SIMPLE>`用のヘッダデータがあります。戻りとして参照されるコンテナは、このヘッダ部分を除いた部分配列になります。ヘッダ部分まで参照したいときは`get_psRxDataApp()`により`tsRxDataApp`構造体を参照してください。
{% endhint %}

### get\_psRxDataApp()

```cpp
const tsRxDataApp* get_psRxDataApp() 
```

TWENET Cライブラリの受信構造体を得る。

### get\_length()

```cpp
uint8_t get_length()
```

ペイロードのデータ長を返す。`.get_payload().size()`と同じ値になる。

### get\_lqi()

```cpp
uint8_t get_lqi()
```

LQI値 (Link Quality Indicator)を得る。

{% hint style="info" %}
LQIとは電波通信品質を示す値です。0から255までの数値で表されます。

ちなみに、いくつかの段階で評価する場合は、50未満(悪い -80dbm 未満)、50～100(やや悪い)、100～150(良好)、150以上(アンテナの近傍)といった区分けも可能です。これらは目安である点にご留意ください。
{% endhint %}

### get\_addr\_src\_long(), get\_addr\_src\_lid()

```cpp
uint32_t get_addr_src_long()
uint8_t get_addr_src_lid()
```

送信元のアドレスを得る。

`get_addr_src_long()`は送信元のシリアル番号で、MSB(bit31)が必ず１になります。

`get_addr_src_lid()`は送信元の論理IDで`0x00`-`0xFE`までの値をとります（`<NWK_SIMPLE>`で指定する論理IDです）。

### get\_addr\_dst()

```cpp
uint32_t get_addr_dst()
```

宛先アドレスを得ます。

宛先アドレスは、送信元で指定され、宛先の種別によって値の範囲が変わります。

| 値                   | 解説                        |
| ------------------- | ------------------------- |
| MSB(bit31)がセットされている | 宛先としてシリアル番号を指定しています。      |
| `0x00`-`0xFF`       | 宛先として論理ID(8bit)が指定されています。 |


# packet\_tx

このクラスはTWENET Cライブラリの`tsTxDataApp`構造体のラッパクラスで、このクラスをベースとした派生クラスのオブジェクトをネットワークビヘイビアより取得して利用します。

```cpp
if (auto&& pkt = the_twelite.network.use<NWK_SIMPLE>().prepare_tx_packet()) {
	pkt << tx_addr(0x00)
		<< tx_retry(0x1)
		<< tx_packet_delay(0,50,10);
		
	pack_bytes(pkt.get_payload()
		, make_pair("APP1", 4)
		, uint8_t(u8DI_BM)
	);
  
  pkt.transmit();
}
```

## オブジェクトの生成

ネットワークビヘイビアの `.prepare_tx_packet()` によって行います。

```cpp
if (auto&& pkt = the_twelite.network.use<NWK_SIMPLE>().prepare_tx_packet()) {
  ...
}
```

上記の例では`the_twelite.network.use<NWK_SIMPLE>()`によってネットワークビヘイビアのオブジェクトを取り出します。このオブジェクトの`.prepare_tx_packet()` によってオブジェクト`pkt`が生成されます。型名はauto&&で推論されていますが`packet_tx`の派生クラスになります。

この`pkt`オブジェクトは、まず、`()`内の条件判定にて`true`か`false`を返します。`false`が返ってくるのは、送信用のキューが一杯でこれ以上要求が追加できない時です。

## 送信設定

無線パケットには宛先情報など相手に届けるための様々な設定を行います。設定には設定内容を含むオブジェクトを<<演算子の右辺値に与えます。

```cpp
pkt << tx_addr(0x00)
	  << tx_retry(0x1)
  	<< tx_packet_delay(0,50,10);
```

以下に設定に用いるオブジェクトについて記載します。

{% hint style="warning" %}
各設定の利用可否や意味合いは、ネットワーク ビヘイビアの仕様によります。
{% endhint %}

### tx\_addr

```cpp
tx_addr(uint32_t addr)
```

宛先アドレス`addr`を指定します。宛先アドレスの値については、ネットワークビヘイビアの仕様を参照してください。

{% tabs %}
{% tab title="\<NWK\_SIMPLE>" %}
MSB(bit31=`0x80000000`)がセットされるアドレスは、無線モジュールのシリアル番号宛という意味になります。

`0x00`..`0xEF`は、8bitの論理IDを意味します。0xFEは子機宛(`0x01`..`0xEF`)の同報通信（ブロードキャスト）、`0xFF`は親機子機関係なく同報通信（ブロードキャスト）します。
{% endtab %}
{% endtabs %}

### tx\_retry

```cpp
tx_retry(uint8_t u8count, bool force_retry = false)
```

再送回数の指定を行います。再送回数はu8countで指定します。force\_retryは、送信が成功しようがしまいが、指定回数の再送を行う設定です。

{% tabs %}
{% tab title="\<NWK\_SIMPLE>" %}
ネットワークビヘイビア`<NWK_SIMPLE>`では、同じ内容のパケットを`u8count+1`回送信します。

`force_retry`の設定は無視されます。
{% endtab %}
{% endtabs %}

### tx\_packet\_delay

```cpp
tx_packet_delay(uint16_t u16DelayMin,
                uint16_t u16DelayMax,
                uint16_t u16RetryDur)
```

パケットを送信するまでの遅延と再送間隔を設定します。`u16DelayMin`と`u16DelayMax`の２つの値をミリ秒\[ms]で指定します。送信要求をしてからこの間のどこかのタイミングで送信を開始します。再送間隔を`u16RetryDur`の値\[ms]で指定します。再送間隔は一定です。

{% hint style="warning" %}
内部処理の都合で指定通りのタイミングで送信処理が始まらない場合もあります。また、IEEE802.15.4の処理でもパケット創出までの時間ブレが発生します。これらのタイミングのブレは、多くのシステムではパケットの衝突回避を行う上で有効な手立てとなります。

厳格なタイミングでのパケット送信は、IEEE802.15.4の規格の性質上、例外的な使用方法とお考え下さい。
{% endhint %}

{% tabs %}
{% tab title="\<NWK\_SIMPLE>" %}
この指定は有効です。

{% hint style="warning" %}
最初の送信から１秒を超えて再送され到達した同一パケットについては、新たなパケットが到達したとして重複除外が為されません。再送間隔を長く設定したり、中継でのパケット到達遅延により１秒を超えて同じパケットを受信する場合があります。

重複パケットの処理の設定は`<NWK_SIMPLE>`ビヘイビアの初期化で設定できます。
{% endhint %}
{% endtab %}
{% endtabs %}

### tx\_process\_immediate

```cpp
tx_process_immediate()
```

パケット送信を「できるだけ速やかに」実行するように要求する設定です。TWENETでのパケット送信処理は、1msごとに動作するTickTimer起点で行われています。この設定をすることで、要求後速やかにパケット送信要求が処理されます。もちろん、`tx_packet_delay(0,0,0)`以外の設定では意味がない指定になります。

他のパケット送信処理が行われている場合は、通常の処理になります。

{% tabs %}
{% tab title="\<NWK\_SIMPLE>" %}
この指定は有効です。
{% endtab %}
{% endtabs %}

### tx\_ack\_required

```cpp
tx_ack_required()
```

無線パケット通信では、送信完了後、送信相手先からACK（アック）という短い無線電文を得て、送信成功とする送信方法があります。このオプションを設定することで、ACK付き送信を行います。

{% tabs %}
{% tab title="\<NWK\_SIMPLE>" %}
{% hint style="warning" %}
`<NWK_SIMPLE>`では、この指定は**無効**です。コンパイルエラーになります。

`<NWK_SIMPLE>`は、シンプルに動作する中継ネットワークの実装を目的としており、ACK付きの通信は行いません。
{% endhint %}
{% endtab %}
{% endtabs %}

### tx\_addr\_broadcast

```
tx_addr_broadcast()
```

ブロードキャストの指定を行います。

{% tabs %}
{% tab title="\<NWK\_SIMPLE>" %}
{% hint style="warning" %}
`<NWK_SIMPLE>`では、この指定は**無効**です。コンパイルエラーになります。
{% endhint %}

替わりに宛先アドレス`tx_addr(0xFF)` （ブロードキャスト）または`tx_addr(0xFE)`（子機宛のブロードキャスト）を指定します。
{% endtab %}
{% endtabs %}

### tx\_packet\_type\_id

```cpp
tx_packet_type_id(uint8_t)
```

0..7の指定ができるTWENET無線パケットのタイプIDを指定します。

{% tabs %}
{% tab title="\<NWK\_SIMPLE>" %}
{% hint style="warning" %}
`<NWK_SIMPLE>`では、この指定は**無効**です。コンパイルエラーになります。
{% endhint %}

`<NWK_SIMPLE>`ではタイプIDを内部的に使用しています。ユーザは使用できません。
{% endtab %}
{% endtabs %}


# serparser

シリアル書式入出力 (mwx::serial\_parser)

シリアル書式の入出力のために用います。内部に解釈済みのバイナリ系列を保持するバッファを持ち、入力時は１バイトずつ系列を読み出し書式に従い内部バッファに格納し、系列の解釈が完了した時点で完了状態になるものです。反対に出力時は内部バッファから所定の出力書式に従いバッファを出力します。

メモリバッファ取り扱い方法([`alloc`](/v0.1.3/api-reference/classes/alloc))に応じて３種類のクラス名が定義されています。

```cpp
// serparser_attach : 既存のバッファを用いる
serparser_attach

// serparser : Nバイトのバッファを内部に確保する
serparser_local<N>

// serparser_heap : ヒープ領域にバッファを確保する
serparser_heap
```

## 定数(書式種別)

`begin()`の初期化のパラメータで渡す書式の種別です。ここではアスキー形式とバイナリー形式の２種類があります。

| 定数                           | 種別      |
| ---------------------------- | ------- |
| `uint8_t PARSER::ASCII = 1`  | アスキー形式  |
| `uint8_t PARSER::BINARY = 2` | バイナリー形式 |

{% hint style="warning" %}
バイナリ形式の取り扱いはアスキー形式に比べ、必要なツールや確認方法を含め一般に取り扱いが煩雑になります。通常はアスキー形式をお使いください。
{% endhint %}

## 形式について

### アスキー形式

アスキー形式は、バイナリで構成されたデータ列を文字列で表現する方法です。

例えばバイト列で `00A01301FF123456` をアスキー形式で表現すると、以下のようになります。先頭は `:` で `B1` がチェックサム、終端は `[CR:0x0d][LF:0x0a]` となります。

> `:00A01301FF123456B1[CR][LF]`

終端のチェックサムを省略できます。チェックサムからCRLFの系列を`X`に置き換えます。文字化けによる誤ったデータ系列には弱くなりますが、実験などでデータを送付したいときに便利です。

> `:00A01301FF123456X`

#### 定義

| ====== | 元データのバイト数 | バイト数 | 解説                                                                                                                                                                                                                                    |
| ------ | :-------: | :--: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| ヘッダ    |           |   1  | `:`(0x3A) コロンを指定します。                                                                                                                                                                                                                  |
| データ部   |     N     |  2N  | <p>元データの各バイトをアスキー文字列２文字（A-F は大文字）で表現します。<br>例えば 0x1F は <code>1</code> (0x31) <code>F</code> (0x46) と表現します。</p>                                                                                                                        |
| チェックサム |           |   2  | <p>データ部の各バイトの和を８ビット幅で計算し２の補数をとります。つまりデータ部の各バイトの総和＋チェックサムバイトを８ビット幅で計算すると０になります。<br>チェックサムバイトをアスキー文字列２文字で表現します。<br>例えば <code>00A01301FF123456</code> では 0x00 + 0xA0 + ... + 0x56 = 0x4F となり、この二の補数は0xB1 です。(つまり 0x4F + 0xB1 = 0x00)</p> |
| フッタ    |           |   2  | \[CR] (0x0D) \[LF] (0x0A) を指定する。                                                                                                                                                                                                      |

### バイナリ形式

{% hint style="warning" %}
通常は**アスキー形式**を利用してください。

マイコン間通信での実装を考えるとバイナリ形式のほうが効率的ですが、実験などでの送受信の確認にはバイナリ通信に対応した特別なターミナルなどを準備する必要があり、チェックサムの計算も必須です。アスキー形式より利用の難易度は高くなります。
{% endhint %}

バイナリ形式は、バイナリで構成されたデータ列にヘッダとチェックサムを付加して送付する方法です。

例えば `00A01301FF123456`をバイナリ形式で表現すると、以下のようになります。

> `0xA5 0x5A 0x80 0x08 0x00 0xA0 0x13 0x01 0xFF 0x12 0x34 0x56 0x3D`

#### 定義

| ====== | 元データのバイト数 | 形式におけるバイト数 | 解説                                                                                                                    |
| ------ | :-------: | :--------: | --------------------------------------------------------------------------------------------------------------------- |
| ヘッダ    |           |      2     | `0xA5 0x5A` を指定します。                                                                                                   |
| データ長   |           |      2     | <p>データ長はビッグエンディアン形式の２バイトで、MSB (0x8000) を設定した上、データ部の長さを指定します。<br>例えばデータ部の長さが 8 バイトなら<code>0x80 0x08</code> を指定します。</p> |
| データ部   |     N     |      N     | 元データを指定します。                                                                                                           |
| チェックサム |           |      1     | <p>データ部の各バイトの XOR を計算します。<br>例えばデータ部が <code>00A01301FF123456</code>なら 0x00 xor 0xA0 xor ... 0x56 = 0x3D となります。</p>    |
| フッタ    |           |     (1)    | チェックサムが事実上の終端です。無線モジュールからの出力では `0x04` (EOT) が付加されます。                                                                  |

## メソッド

### 宣言, begin()

```cpp
// serparser_attach : 既存のバッファを用いる
serparser_attach p1;

uint8_t buff[128];
p1.begin(ARSER::ASCII, buff, 0, 128);


// serparser : Nバイトのバッファを内部に確保する
serparser p2<128>;
p2.begin(PARSER::ASCII);


// serparser_heap : ヒープ領域にバッファを確保する
serparser_heap p3;
p3.begin(PARSER::ASCII, 128);
```

宣言にはメモリの確保クラスを指定します。この指定は煩雑であるため、上述のように別名定義を行っています。

| <p>クラス名（別名定義）<br>メモリ確保</p> | 内容                                   |
| -------------------------- | ------------------------------------ |
| `serparser_attach`         | すでにあるバッファを`begin()`にて指定する            |
| `serparser_local<N>`       | Nバイトのバッファを内部に確保する                    |
| `serparser_heap`           | `begin()`メソッドのパラメータで指定したサイズをヒープに確保する |

メモリ確保クラスに応じた`begin()`メソッドを呼び出します。

#### serparser\_attach&#x20;

```cpp
void begin(uint8_t ty, uint8_t *p, uint16_t siz, uint16_t max_siz)
```

`ty`で指定する[形式](/v0.1.3/api-reference/classes/ser_parser#nitsuite)で、`p`で指定したバッファを用います。バッファの最大長は`max_siz`で、バッファの有効データ長を`siz`で指定します。

この定義は、特に、データ列を書式出力したい場合に用います(`>>` 演算子参照)

#### serparser\_local\<N> - 内部にバッファを確保する

```cpp
void begin(uint8_t ty)
```

`ty`で指定する[形式](/v0.1.3/api-reference/classes/ser_parser#nitsuite)で初期化を行います。

#### serparser\_heap - ヒープに確保

```cpp
void begin(uint8_t ty, uint16_t siz)
```

`ty`で指定する[形式](/v0.1.3/api-reference/classes/ser_parser#nitsuite)で、`siz`で指定したサイズをヒープに確保して初期化します。

{% hint style="warning" %}
一度確保したヒープ領域は解放できません。
{% endhint %}

### get\_buf()

```cpp
BUFTYPE& get_buf()
```

内部バッファを返す。バッファは `smplbuf<uint8_t, alloc>` 型となります。

### parse()

```cpp
inline bool parse(uint8_t b)
```

入力文字を処理する。書式入力の入力文字列を１バイト受け取り書式に従い解釈します。例えばASCII書式では`:00112233X`のような系列を入力として受け取りますが `: 0 0 ... X` の順で１バイトずつ入力し、最後の `X` を入力した時点で書式の解釈を完了し、完了済みと報告します。

`parse()`のパラメータは入力バイト、戻り値は解釈完了であれば`true`を戻します。

{% hint style="info" %}
`parse()`で読み出し完了になったとき、次の`parse()`を実行すると読み出し中のステータスに戻ります。
{% endhint %}

#### 例

```cpp
while (Serial.available()) {
    int c = Serial.read();
    
    if (SerialParser.parse(c)) {
        // 書式解釈完了、b に得られたデータ列(smplbuf<uint8_t>)
        auto&& b = SerialParser.get_buf();
        
        // 以下は得られたデータ列に対する処理を行う
        if (b[0] == 0xcc) {
          // ...
        }
    }
}
```

### operator bool()

```cpp
operator bool() 
```

`true`なら`parse()`により読み出しが完了した状態で、`false`なら解釈中となります。

#### 例 (parse()の例は以下のように書き換えられる)

```cpp
while (Serial.available()) {
    int c = Serial.read();
    
    SerialParser.parse(c);
    
    if(SerialParser) {
        // 書式解釈完了、b に得られたデータ列(smplbuf<uint8_t>)
        auto&& b = SerialParser.get_buf();
        // ...
    }
}
```

### << 演算子

内部バッファを書式形式でストリーム(Serial)に対して出力します。

#### 例

```cpp
uint8_t u8buf[] = { 0x11, 0x22, 0x33, 0xaa, 0xbb, 0xcc };

ser_parser pout;
pout.begin(ARSER::ASCII, u8buf, 6, 6); // u8bufの6バイトを指定

Serial << pout;// Serialに書式出力 -> :112233AABBCC??[CR][LF] 
```


# smplbuf

内部が配列構造のコンテナクラスです。初期化時にバッファの最大サイズを決定しますが、その最大サイズまでの範囲で可変長の配列として振る舞います。

```cpp
template <typename T, int N> smplbuf_local
template <typename T> smplbuf_attach
template <typename T> smplbuf_heap
```

`smplbuf`は要素の型`T`と[メモリの確保方法`alloc`](/v0.1.3/api-reference/classes/alloc)で指定したメモリ領域に対して配列の操作を提供するコンテナクラスです。`alloc`の指定は煩雑であるため`using`を用いた別名定義が行っています。

オブジェクトの宣言例です。宣言の直後に初期化用のメソッド呼び出しを行います。いずれも初期化直後の最大サイズは128バイトで、サイズは0です。必要に応じてサイズを拡張しながら使用します。

```cpp
// 内部に固定配列
smplbuf_local<uint8_t, 128> b1;
b1.init_local(); //グローバル定義でなければ省略可能

// すでにある配列を利用する
uint8_t buf[128];
smplbuf_attach<uint8_t> b2;
b2.attach(buf, 0, 128);

// ヒープに確保する
smplbuf_heap<uint8_t> b3;
b3.init_heap(128); 
```

上記の`uint8_t`型に限り別名定義があります。

```cpp
template <int N>
smplbuf_u8
// smplbuf<uint8_t, alloc_local<uint8_t, N>>

smplbuf_u8_attach
// smplbuf<uint8_t, alloc_attach<uint8_t>>

smplbuf_u8_heap
// smplbuf<uint8_t, alloc_heap<uint8_t>>
```

通常の配列のように\[]演算子などを用いて要素にアクセスできますし、イテレータを用いたアクセスも可能です。

```cpp
smplbuf_u8<32> b1;
b1.reserve(5);

b1[0] = 1;
b1[1] = 4;
b1[2] = 9;
b1[3] = 16;
b1[4] = 25;

for(uint8_t x : b1) {
  Serial << int(x) << ",";
}
```

`push_back()`メソッドを定義しています。末尾にデータを追記するタイプのアルゴリズムが使用可能になります。

## 宣言・初期化

```cpp
smplbuf_local<T,N>
smplbuf_local<T,N>::init_local()

smplbuf_attach<T>
smplbuf_attach<T>::attach(T* buf, uint16_t size, uint16_t N)

smplbuf_heap<T>
smplbuf_heap<T>::init_heap(uint16_t N)

// 例
// 内部に固定配列
smplbuf_local<uint8_t, 128> b1;
b1.init_local();

// すでにある配列を利用する
uint8_t buf[128];
smplbuf_attach<uint8_t> b2;
b2.attach(buf, 0, 128);

// ヒープに確保する
smplbuf_heap<uint8_t> b3;
b3.init_heap(128); 
```

型`T`でサイズ`N`のコンテナを宣言します。宣言後に初期化のメソッドを呼び出します。

`smplbuf_attach`では、使用するバッファの先頭ポインタ`T* buf`と配列の初期サイズ`size`と最大サイズ`N`を指定します。

`smplbuf_local`のみ、ローカルオブジェクトとして宣言する場合は、初期化メソッド`.init_local()`を省略できます。

{% hint style="danger" %}
`alloc_local`でグローバルオブジェクトを生成する場合は、`smplbuf`コンテナの使用前に`.init_local()`メソッドを呼び出します。
{% endhint %}

## 初期化子リスト

```cpp
void in_some_func() {
    smplbuf_local<uint8_t, 5> b1;
    b1.init_local();
    
    b1 = { 0, 1, 2, 3, 4 };
    
    smplbuf_local<uint8_t, 5> b2{0, 1, 2, 3, 4};
}
```

初期化子リスト（イニシャライザリスト）`{ ... }` によるメンバーの初期化をできます。`smplbuf_local`のローカル宣言でのコンストラクタでの利用を除き、初期化のメソッドを呼び出した後に有効です。

* 代入演算子の右辺値 (`smplbuf_local`, `smplbuf_attach`, `smplbuf_heap`)
* コンストラクタ(`smplbuf_local`のローカル宣言、グローバル宣言は不可)

## メソッド

### append(), push\_back(), pop\_back()

```cpp
inline bool append(T&& c)
inline bool append(const T& c)
inline void push_back(T&& c)
inline void push_back(const T& c)
```

末尾にメンバー`c`を追加します。`append()`の戻り値は`bool`で、バッファが一杯で追加できないときに`false`が返ります。

`pop_back()`は末尾のエントリを抹消します。

### empty(), size(), capacity()

```cpp
inline bool empty()
inline bool is_end()
inline uint16_t size()
inline uint16_t capacity()
```

`empty()`は配列に要素が格納されていない場合に`true`を戻します。`is_end()`は反対に配列サイズ一杯まで要素が格納されているときに`true`を戻します。

`size()`は配列の要素数を返します。

`capacity()`は配列の最大格納数を返します。

### reserve(), reserve\_hear(), redim()

```cpp
inline bool reserve(uint16_t len)
inline void reserve_head(uint16_t len)
inline void redim(uint16_t len)
```

`reserve()`は配列のサイズを拡張します。配列が格納されていない領域はデフォルトで初期化されます。

`reserve_hear()`は配列の先頭部に指定したサイズの領域を確保します。コンテナオブジェクトからは参照できない領域となります。例えばパケットペイロードのヘッダ部分を読み飛ばした部分配列にアクセスするようなコンテナとして利用できるようにします。確保した領域を戻しすべてアクセスできるようにコンテナを戻すには確保時と同じ負の値を与えます。

`redim()`は利用領域のサイズを変更します。`reserve()`と違い、未使用領域の初期化を行いません。

### operator \[]

```cpp
inline T& operator [] (int i) 
inline T operator [] (int i) const
```

要素にアクセスします。

`i`に負の値を与えるとバッファー末尾からの要素となります。`-1`の場合は末尾の要素、`-2`は末尾から一つ手前となります。


# smplque

FIFOキューを構造のコンテナクラスです。

```cpp
template <typename T, int N, class Intr> smplbuf_local
template <typename T, class Intr> smplbuf_attach
template <typename T, class Intr> smplbuf_heap
```

`smplque`は要素の型`T`と[メモリの確保方法`alloc`](/v0.1.3/api-reference/classes/alloc)で指定したメモリ領域に対してFIFOキューの操作を提供するコンテナクラスです。`alloc`の指定は煩雑であるため`using`を用いた別名定義が行っています。

{% hint style="warning" %}
要素型は原則として数値や数値などを格納する構造体を想定しています。デストラクタによる破棄手続きが必要なオブジェクトを格納することを想定していません（キューから要素を抹消する際にオブジェクトを抹消する処理をしていないため）。
{% endhint %}

宣言時に割り込み禁止設定を行うクラス`Intr`を登録することが出来ます。このクラスは指定しない場合は、割り込み禁止制御を行わない通常の動作となります。

オブジェクトの宣言例です。宣言の直後に初期化用のメソッド呼び出しを行います。いずれも初期化直後の最大サイズは128バイトで、サイズは0です。必要に応じてサイズを拡張しながら使用します。

```cpp
// 内部に固定配列
smplque_local<uint8_t, 128> q1;
q1.init_local();

// すでにある配列を利用する
uint8_t buf[128];
smplque_attach<uint8_t> q2;
q2.attach(buf, 128);

// ヒープに確保する
smplque_heap<uint8_t> q3;
q3.init_heap(128);
```

FIFOキューですので`push()`,`pop()`,`front()`といったメソッドを用いて操作します。

```cpp
smplque_local<int, 32> q1;
q1.init_local();

q1.push(1);
q1.push(4);
q1.push(9);
q1.push(16);
q1.push(25);

while(!q1.empty()) {
	Serial << int(q1.front()) << ',';
	q1.pop();
}
// output -> 1,4,9,16,25,
```

イテレータによるアクセスも可能です。

```cpp
smplque_local<int, 32> q1;
q1.init_local();

q1.push(1);
q1.push(4);
q1.push(9);
q1.push(16);
q1.push(25);

for(int x : q1) {
	Serial << int(x) << ',';
}

auto&& minmax = std::minmax_element(q1.begin(), q1.end());
Serial <<  "min=" << int(*minmax.first)
	     << ",max=" << int(*minmax.second);
// output -> 1,4,9,16,25,min=1,max=25[]
```

## 宣言・初期化

```cpp
smplbuf_local<T,N>
smplbuf_local<T,N>::init_local()

smplbuf_attach<T>
smplbuf_attach<T>::attach(T* buf, uint16_t N)

smplbuf_heap<T>
smplbuf_heap<T>::init_heap(uint16_t N); 

//例
// 内部に固定配列
smplque_local<uint8_t, 128> q1;
q1.init_local();

// すでにある配列を利用する
uint8_t buf[128];
smplque_attach<uint8_t> q2;
q2.attach(buf, 128);

// ヒープに確保する
smplque_heap<uint8_t> q3;
q3.init_heap(128); 
```

型`T`でサイズ`N`のコンテナを宣言します。宣言後に初期化のメソッドを呼び出します。

`smplque_local`のみ、ローカルオブジェクトとして宣言する場合は、初期化メソッド`.init_local()`を省略できます。

{% hint style="danger" %}
`smplque_local`のグローバルオブジェクトを生成する場合は、`smplbuf`コンテナの使用前に`.init_local()`メソッドを呼び出します。
{% endhint %}

## メソッド

### push(), pop(), front(), back()

```cpp
inline void push(T&& c)
inline void push(T& c)
inline void pop()
inline T& front()
inline T& back()

inline T& pop_front()
```

`push()`はエントリをキューに追加します。

`pop()`はエントリをキューから抹消します。

`front()`は先頭のエントリ（一番最初に追加されたもの)を参照します。

`back()`は末尾のエントリ（一番最後に追加されたもの)を参照します。

`pop_front()`は先頭のエントリを戻り値として参照し、同時にそのエントリをキューから抹消します。

### empty(), size(), is\_full()

```cpp
inline bool empty()
inline bool is_full()
inline uint16_t size()
inline uint16_t capacity()
```

`empty()`は配列に要素が格納されていない場合に`true`を戻します。`is_full()`は反対に配列サイズ一杯まで要素が格納されているときに`true`を戻します。

`size()`はキューに格納されている要素数を返します。

`capacity()`はキューの最大格納数を返します。

### clear()

```cpp
inline void clear()
```

キューのすべての要素を抹消します。

### operator \[]

```cpp
inline T& operator[] (int i)
```

要素にアクセスします。`0`が最初に追加した要素です。

## イテレータ

```cpp
inline smplque::iterator begin()
inline smplque::iterator end()
```

`begin()`と`end()`によるイテレータを取得できます。イテレータの先頭はキューの最初に登録した要素です。イテレータを用いることで範囲for文やアルゴリズムが利用できます。

応用として[axis\_xyzt構造体の特定のメンバーに注目したイテレータによるアクセス](/v0.1.3/api-reference/classes/axis_xyzt)があります。


# mwx::stream

入出力ストリーム

入出力ストリームを処理する上位クラスです。

* CRTP (Curiously Recurring Template Pattern) 手法を用いたポリモーフィズムにより、いくつかのクラス(`Serial, Wire, SPI, smplbuf`) にインタフェースを提供します。
  * CRTP では下位クラスは `template class Derived : public stream<Derived>;`のように定義し、上位クラスからも下位クラスのメソッドを参照します。
* 本クラスでは `print` メソッド、`<<` 演算子などの共通処理の定義を行い、下位クラスで実装した `write()` メソッドなどを呼び出すことで、仮想関数を用いるのと近い実装を行っています。

## インタフェース（下位クラスで実装）

下位クラスでは、以下に列挙する関数を実装します。

### available()

```cpp
int available()

// example
while(Serial.available()) {
  int c = Serial.read();
  // ... any
}
```

入力が存在する場合は 1、存在しない場合は 0 を返します。

| パラメータ     | 解説               |
| --------- | ---------------- |
| 戻り値 `int` | 0: データなし 1:データあり |

{% hint style="warning" %}
本実装の戻り値はバッファ長ではありません。
{% endhint %}

###

### flush()

```cpp
void flush()

// example
Serial.println("long long word .... ");
Serial.flush();
```

出力をフラッシュ（出力完了まで待つ）します。

### read()

```cpp
int read()

// example
int c;
while (-1 != (c = read())) {
    // any
}
```

ストリームより１バイトデータを入力します。データが存在しない場合は `-1` を戻します。

### write()

```cpp
size_t write(int c)

// example
Serial.write(0x30);
```

ストリームに１バイト出力します。

| パラメータ        | 解説                  |
| ------------ | ------------------- |
| `n`          | 出力したい文字。            |
| 戻り値 `size_t` | 出力が成功すれば 1、失敗すれば 0。 |

### vOutput()

```cpp
static void vOutput(char out, void* vp)
```

１バイト出力を行うスタティック関数です。クラスメソッドではないため、メンバー変数等の情報は利用できません。替わりにパラメータとして渡される vp にクラスインスタンスへのポインタを渡します。

このスタティック関数は内部的に利用され`fctprintf()`の１バイト出力関数として関数ポインタが渡ります。これを用いて`print`メソッドなどを実装しています。

| パラメータ | 解説                                                           |
| ----- | ------------------------------------------------------------ |
| `out` | 出力したい文字                                                      |
| `vp`  | <p>クラスインスタンスへのポインタ<br>通常は、元のクラスにキャストして write() メソッドを呼び出す</p> |

## インタフェース

### putchar()

```cpp
void mwx::stream::putchar(char c)

// example
Serial.putchar('A');
// result -> A
```

１バイト出力します。

### print(), println()

```cpp
size_t print(T val, int base = DEC) // T: 整数型
size_t print(double val, int place = 2)
size_t print(const char*str)
size_t print(std::initializer_list<int>)

// example
Serial.print("the value is ");
Serial.print(123, DEC);
Serial.println(".");
// result -> the value is 123.

Serial.print(123.456, 1);
// result -> 123.5

Serial.print({ 0x12, 0x34, 0xab, 0xcd });
// will output 4byte of 0x12 0x34 0xab 0xcd in binary.
```

各種整形出力を行います。

| パラメータ        | 解説                                                        |
| ------------ | --------------------------------------------------------- |
| `val`        | 整形出力したい数値型                                                |
| `base`       | <p>出力形式</p><p>BIN 二進数 / OCT ８進数 / DEC 10進数 / HEX 16進数</p> |
| `place`      | 小数点以下の桁数                                                  |
|              |                                                           |
| 戻り値 `size_t` | 書き出したバイト数                                                 |

### printfmt

```cpp
size_t printfmt(const char* format, ...);

// example 
Serial.printfmt("the value is %d.", 123);
// result -> the value is 123.
```

printf 形式での出力を行います。

> TWESDK/TWENET/current/src/printf/README.md 参照

### operator <<

```cpp
// examples
Serial << "this value is" // const char*
       << int(123)
       << '.';
       << mwx::crlf;
// result -> this value is 123.

Serial << fromat("this value is %d.", 123) << twe::crlf;
// result -> this value is 123.

Serial << mwx::flush; // flush here

Serial << bigendian(0x1234abcd);
// will output 4byte of 0x12 0x34 0xab 0xcd in binary.

Serial << int(0x30) // output 0x30=48, "48"
       << '/'
       << uint8_t(0x31); // output '1', not "48"
// result -> 48/1

smplbuf<char,16> buf = { 0x12, 0x34, 0xab, 0xcd };
Serail << but.to_stream();
// will output 4byte of 0x12 0x34 0xab 0xcd in binary.

Seiral << make_pair(buf.begin(), buf.end());
// will output 4byte of 0x12 0x34 0xab 0xcd in binary.

Serial << bytelist({ 0x12, 0x34, 0xab, 0xcd });
// will output 4byte of 0x12 0x34 0xab 0xcd in binary.

```

| 引数型                             | 解説                                                                                  |
| ------------------------------- | ----------------------------------------------------------------------------------- |
| `char`                          | １バイト出力 （数値としてフォーマットはしない）                                                            |
| `int`                           | 整数出力 (printf の "%d")                                                                |
| `double`                        | 数値出力 (printf の "%.2f")                                                              |
| `uint8_t`                       | １バイト出力する(char型と同様)                                                                  |
| `uint16_t`                      | ２バイト出力する(ビッグエンディアン順)                                                                |
| `uint32_t`                      | ４バイト出力する(ビッグエンディアン順)                                                                |
| `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>` のデータを出力する                                                        |

{% hint style="info" %}
バイト列として出力する際は、`uint8_t, uint16_t, uint32_t` 型にキャストします。また文字列として数値出力する場合は明示的に`int`形にキャストするようにしてください。
{% endhint %}

### operator >>

```cpp
// examples
Serial << "this value is" // const char*
       << int(123)
       << '.';
       << mwx::crlf;
// result -> this value is 123.

// example: SPI transmission
uint8_t val;
if (auto&& x = SPI.get_rwer()) {
  (x << (0x61)) >> null_stream(1); // discard read byte
  (x << (0x00)) >> val; // read a byte to val
}
```

入力処理を行います。

{% hint style="danger" %}
`setup()` 内では実行できません。
{% endhint %}

| 引数型                  | 解説                 |
| -------------------- | ------------------ |
| `uint8_t`            | １バイト入力             |
| `uint16_t`           | ２バイト入力（ビッグエンディアン順） |
| `uint32_t`           | ４バイト入力（ビッグエンディアン順） |
| `null_stream(int n)` | nバイト読み捨てる          |

### set\_timeout(), get\_error\_status(),  clear\_error\_status()

```cpp
uint8_t get_error_status()
void clear_error_status()
void set_timeout(uint8_t centisec)

// example
Serial.set_timeout(100); // set timeout as 1000ms.
uint32_t u32val;
Serial >> u32val;
```

入力タイムアウトとエラーを管理します。`set_timeout()` によりタイムアウト時間を指定し、>>演算子により入力処理を行います。所定時間内までに入力が得られない場合は `get_error_status()` によりエラー値を読み出せます。`clear_error_status()`によりエラー状況をクリアします。

| 引数型        | 解説                      |
| ---------- | ----------------------- |
| `centisec` | 1/100秒単位でタイムアウト時間を設定します |

#### エラー値

| 値 | 意味    |
| - | ----- |
| 0 | エラーなし |
| 1 | エラー状況 |


# format (mwx::mwx\_format)

mwx::stream に printf の書式を入力

`mwx::stream` の << 演算子に対してフォーマット書式を書き出すヘルパークラスです。ライブラリ内では `Using format=mwx::mwx_format;` として別名定義しています。

```cpp
Serial << format("formatted print: %.2f", (double)3123 / 100.) << crlf;

// formatted print: 31.23[改行]
```

{% hint style="warning" %}
可変数引数リストに登録できる引数は最大４つです。
{% endhint %}

* コンストラクタで受け取った引数リストを、パラメータパックの展開機能を用いてクラス内部変数に格納する
* `operator <<` が呼び出された時点で、`fctprintf()` を呼び出し、ストリームにデータを書き出す

### コンストラクタ

```cpp
format(const char *fmt, ...)
```

コンストラクタでは、書式のポインタとパラメータを保存します。続く `<<`演算子による呼び出しでフォーマットを解釈して出力処理を行います。

| パラメータ | 解説                                                                                                          |
| ----- | ----------------------------------------------------------------------------------------------------------- |
| `fmt` | <p>フォーマット書式。</p><p>TWESDK/TWENET/current/src/printf/README.md 参照</p>                                        |
| `...` | <p>フォーマット書式に応じたパラメータ。<br>※ 最大数は４で、５つ以上のパラメータではコンパイルエラーとなる。</p><p>※ 書式との整合性はチェックしないため、不整合な入力に対しては安全ではない。</p> |

{% hint style="warning" %}
`fmt`は本オブジェクトが破棄されるまで、アクセス可能であることが必要です。
{% endhint %}


# mwx::bigendian

twe::stream にビッグエンディアン順で数値型のデータを出力する

`mwx::stream` の `<<` 演算子に対して数値型をビッグエンディアンのバイト列で出力するヘルパークラスです。

```cpp
Serial << mwx::bigendian(0x1234abcdUL);

// output binary -> 0x12 0x34 0xab 0xcd
```

### コンストラクタ

```cpp
template <typename T>
bigendian::bigendian(T v)
```

| パラメータ | 解説                             |
| ----- | ------------------------------ |
| `v`   | `uint16_t` または `uint32_t` の型の値 |


# mwx::crlf

twe::stream に改行コードを出力する

`mwx::stream` の `<<` 演算子に対して改行コード (CR LF) を出力するためのヘルパークラスのインスタンスです。

```cpp
Serial << "hello world!" << mwx::crlf;
```


# mwx::flush

twe::stream へのバッファ出力をフラッシュする。

`mwx::stream` の出力バッファをフラッシュする。`flush()` メソッドを呼び出すヘルパークラスへのインスタンス。

```cpp
for (int i = 0; i < 127; ++i) {
    Serial << "hello world! (" << i << ")" << twe::endl << twe::flush;
}
```

* シリアルポートの場合は出力完了までポーリング待ちを行う
* `mwx::simpbuf` バッファの場合は `0x00` を末尾に出力する（サイズは変更しない）


# コールバック関数

アプリケーションの記述を行うコールバック関数です。コールバックはシステム（ライブラリ）から呼び出されるという意味です。ユーザがいくつかのコールバック関数を定義することでシステムの振る舞いを記述します。

以下のコールバック関数は必須定義です。

* `setup()`
* `loop()`

それ以外の関数は定義しない場合は、何も実行しない空の関数が替わりにリンクされます。

### 通常のコールバック呼び出し順序

```
init_coldboot()
  ↓ (TWENET内部処理:初期化1)
setup()
  ↓（TWENET内部処理:初期化2)
begin() --- 初回のみ
  ↓
loop() <--+
  ↓       |イベント処理、ビヘイビア処理
CPU DOZE -+
```

{% hint style="warning" %}
正確なふるまいを参照したい方はソースコード`mwx_appcore.cpp`を参照してください。
{% endhint %}

### スリープ復帰時のコールバック呼び出し順序

```
the_twelite.sleep()
  ↓ sleeping...
 
 
init_warmboot()
  ↓ (TWENET内部処理:初期化3)
wakeup()
  ↓（TWENET内部処理:初期化4)
loop() <--+
  ↓       |イベント処理、ビヘイビア処理
CPU DOZE -+
```

{% hint style="warning" %}
正確なふるまいを参照したい方はソースコード`mwx_appcore.cpp`を参照してください。
{% endhint %}


# setup()

コード実行の初期に呼び出され、初期化コードを記述します。

{% hint style="warning" %}
TWENETの初期化は `setup()` 関数が終了した後にも実行されます。多くの処理はTWENETが終了した後に実行するようになっているため、ここでは初期化以外の処理を行わないようにしてください。

注意すべき事項を以下に列挙します。

* スリープ`the_twenet.sleep()`の実行はできません。初期化後速やかにスリープしたいときは`begin()`関数内に最初のスリープ処理を記述してください。
* `delay()`関数は後述の処理\*に置き換えられます。この場合、パラメータの`ms`はミリ秒を指定するものではありませんが、`setup()`の32Mhzクロックに併せてミリ秒指定に近くなるように調整されています。<br>

\* `delay()`の代替処理

```cpp
static inline void delay(uint32_t ms) {
		volatile uint32_t ct = ms * CT_MAGIC;
		while (ct > 0) {
			--ct;
		}
}
```

{% endhint %}


# begin()

`loop()`関数の初回コールの手前で一度だけ呼び出されます。TWENET の初期化は終了しているので`setup()`のような制約を考慮する必要はありません。

主な使い方は、

* 始動メッセージの表示
* 始動直後のスリープ遷移
* setup()で始動することに不都合がある処理

{% hint style="info" %}
このコールバック関数定義は省略可能です。
{% endhint %}


# loop()

アプリケーションのメインループです。ループ終了後はCPUがDOZEモードに遷移し低消費電流で次の割り込みを待ちます。

アクトの記述では、ほとんどの処理がこのループ内に記述されます。

{% hint style="info" %}
バック関数定義は省略可能です。
{% endhint %}


# wakeup()

スリープから起床したときに`loop()`に移行する前に呼ばれ、スリープ復帰後の初期化処理や復帰状態によって処理を分岐するための手続きを含めます。

{% hint style="info" %}
センサーの読み出しなどの処理のみで`loop()`での処理がないときは、この関数内で再びスリープを実行できます。
{% endhint %}

{% hint style="info" %}
このコールバック関数定義は省略可能です。
{% endhint %}


# init\_coldboot()

{% hint style="danger" %}
通常は使用しません。
{% endhint %}

ペリフェラルAPIも初期化もされていない、コード実行の再初期に呼び出されます。

{% hint style="info" %}
このコールバック関数定義は省略可能です。
{% endhint %}


# init\_warmboot()

{% hint style="danger" %}
通常は使用しません。
{% endhint %}

スリープ復帰後、ペリフェラルAPIが初期化されない再初期に呼び出されます。

{% hint style="info" %}
この関数では割り込み要因の検出を行うことができます。
{% endhint %}

{% hint style="info" %}
このコールバック関数定義は省略可能です。
{% endhint %}


# ビヘイビア

ビヘイビアは、指定の方法でクラスを定義することで、[`the_twelite`](/v0.1.3/api-reference/predefined_objs/the_twelite)クラスオブジェクトに登録できるようになります。登録したビヘイビアはTWENETに組み込まれて動作し、ユーザコードではアプリケーションの振る舞いを記述できるようになります。ループでの記述ではできなかったTWENETからの割り込みやイベントのコールバック関数を定義することが出来ます。ループでの記述に比べ、定義が多くなりますが、より複雑なアプリケーションを記述するのに向いています。

{% hint style="success" %}
ビヘイビアのサンプル[PAL\_AMB-behavior](/v0.1.3/api-reference/behavior/pal_amb-behavior)を参照してください。
{% endhint %}

## クラス定義 (.hpp)

ビヘイビアの定義には下記に示すようなクラス定義が必要です。

```cpp
class MY_APP_CLASS: MWX_APPDEFS_CRTP(MY_APP_CLASS)
{
public:
    static const uint8_t TYPE_ID = 0x01;

    // load common definition for handlers
    #define __MWX_APP_CLASS_NAME MY_APP_CLASS
    #include "_mwx_cbs_hpphead.hpp"
    #undef __MWX_APP_CLASS_NAME

public:
    // constructor
    MY_APP_CLASS() {}

    void _setup() {}    
    void _begin() {}

public:
    // TWENET callback handler (mandate)
    void loop() {}
    void on_sleep(uint32_t & val) {}
    void warmboot(uint32_t & val) {}
    void wakeup(uint32_t & val) {}

    void on_create(uint32_t& val) { _setup();  }
    void on_begin(uint32_t& val) { _begin(); }
    void on_message(uint32_t& val) { }

public:
    void network_event(mwx::packet_ev_nwk& pEvNwk) {}
    void receive(mwx::packet_rx& rx) {}
    void transmit_complete(mwx::packet_ev_tx& evTx) {}
};
```

上記の例ではMY\_APP\_CLASSという名前でビヘイビアクラスを定義しています。いくつかの箇所にMY\_APP\_CLASSの記述が必要です。

```cpp
class MY_APP_CLASS: MWX_APPDEFS_CRTP(MY_APP_CLASS)
```

クラス名の定義と、ベース（親）クラスの定義をします。`MWX_APPDEFS_CRTP()`はマクロです。

```cpp
    #define __MWX_APP_CLASS_NAME MY_APP_CLASS
    #include "_mwx_cbs_hpphead.hpp"
    #undef __MWX_APP_CLASS_NAME
```

ここでは必要な定義を `#include` により取り込んでいます。

```cpp
MY_APP_CLASS() {}
```

コンストラクタの定義です。

## メソッド

### loop()

メインループで、グローバル定義の`loop()`と同じ役割の関数です。

### on\_create()

`on_create()`はオブジェクト生成時（`use<>()`メソッド）に呼び出されます。`val`は将来の拡張のためのパラメータです。

### on\_begin()

`on_begin()`は`setup()`終了後に呼び出されます。`val`は将来の拡張のためのパラメータです。

### on\_sleep()

スリープ前に呼び出されます。`val`は将来の拡張のためのパラメータです。

### warmboot()

スリープ復帰時の初期段階で呼び出されます。`val`は将来の拡張のためのパラメータです。

この時点でまだペリフェラルが初期化されていません。スリープ起床要因の確認ができます。

### wakeup()

スリープ復帰時に呼び出されます。`val`は将来の拡張のためのパラメータです。

{% hint style="info" %}
ここでスリープ呼び出しも可能です。
{% endhint %}

### receive()

```cpp
void receive(mwx::packet_rx& rx)
```

パケットが受信されたとき、受信したパケット情報を`rx`として呼び出されます。

### transmit\_complete()

```cpp
void transmit_complete(mwx::packet_ev_tx& evTx)
```

パケット送信完了時に送信情報を`evTx`として呼び出されます。`evTx.u8CbId`が送信時のIDで`evTx.bStatus`が送信の成功(`1`)失敗(`0`)を示すフラグです。

## ハンドラの定義 (.cpp)

ビヘイビアのハンドラ（割り込み、イベント、状態定義）はcppファイルに定義します。ファイルは分割できず、全てのハンドラ定義を一つのファイル中に記述します。

{% hint style="warning" %}
ハンドラの定義をしないビヘイビアの場合でも、必ず、下記のcppファイルを作成します。
{% endhint %}

cppファイルの冒頭と末尾にはMWXライブラリの必要な定義(`#include "_mwx_cbs_cpphead.hpp"`)をインクルードする必要があります。

```cpp
#include <TWELITE>
#include "myAppClass.hpp" // ビヘイビア定義ファイル

/*****************************************************************/
// MUST DEFINE CLASS NAME HERE
#define __MWX_APP_CLASS_NAME MY_APP_CLASS
#include "_mwx_cbs_cpphead.hpp" // 冒頭の定義
/*****************************************************************/
```

ファイルの冒頭には、上記のようにビヘイビア定義の.hppファイルをインクルードします。`__MWX_APP_CLASS_NAME`にはビヘイビアのクラス名を指定します。上記では`MY_APP_CLASS`です。

```cpp
/*****************************************************************/
// common procedure (DO NOT REMOVE)
#include "_mwx_cbs_cpptail.cpp"
// MUST UNDEF CLASS NAME HERE
#undef __MWX_APP_CLASS_NAME
/*****************************************************************/
```

ファイルの末尾では必要な定義(`#include "_mwx_cbs_cpptail.cpp"`)をインクルードします。

ハンドラ定義は以下の例のように記述します。定義の種別については後述します。定義用のマクロを用いて利用したいハンドラの定義を記述します。利用しないハンドラは記述しないようにしてください。

`MWX_???_INT()`は割り込みハンドラの定義、`MWX_???_EVENT()`はイベントハンドラの定義、`MWX_STATE()`はステートマシンの状態定義です。

```cpp
// TickTimer割り込み
MWX_TICKTIMER_INT(uint32_t arg, uint8_t& handled) {
	// blink LED
	digitalWrite(PAL_AMB::PIN_LED, 
	      ((millis() >> 9) & 1) ? PIN_STATE::HIGH : PIN_STATE::LOW);
}

// PAL_AMB::PIN_BIN(12)のイベント
MWX_DIO_EVENT(PAL_AMB::PIN_BTN, uint32_t arg) {
	Serial << "Button Pressed" << mwx::crlf;
	
	static uint32_t u32tick_last;
	uint32_t tick = millis();

	if (tick - u32tick_last > 100) {
		PEV_Process(E_ORDER_KICK, 0UL);
	}

	u32tick_last = tick;
}

// 状態 STATE_0 の動作定義
MWX_STATE(E_MWX::STATE_0, uint32_t ev, uint32_t evarg) {
	if (ev == E_EVENT_START_UP) {
		Serial << "[STATE_0:START_UP]" << mwx::crlf;	
	} else
	if (ev == E_ORDER_KICK) {
		PEV_SetState(E_MWX::STATE_1);
	}
}

// 状態 STATE_1 の動作定義	
MWX_STATE(E_MWX::STATE_1, uint32_t ev, uint32_t evarg) {
	if (ev == E_EVENT_NEW_STATE) {
		Serial << "[STATE_1]" << mwx::crlf;	
	} else
	if (ev == E_ORDER_KICK) {
		PEV_SetState(E_MWX::STATE_2);
	} else
	if (ev == E_EVENT_TICK_SECOND) {
		Serial << "<1>";
	}	
}
```

## 割り込み・イベントハンドラ

割り込みハンドラは、マイコンの割り込みが発生したときに現在実行中のコードを中断して実行されます。このため、可能な限り短い処理を記述することが望ましく、また、変数等の操作に対しても細心の注意が必要です。

割り込みハンドラのパラメータには`uint8_t& handled`があり、この値を`true`にセットすることで、続くイベント呼び出しを行いません。

`handled`が`false`のまま割り込みハンドラを終了した場合、アプリケーションループ（通常コード）の中でイベントハンドラが呼び出されます。イベントハンドラには`handled`のパラメータはありません。イベントハンドラは通常コードですので、比較的大きな処理を行うことが出来ます。イベントハンドラのオーバーヘッドも発生するため、頻繁な割り込み都度呼び出されるような処理の場合、処理しきれなくなる可能性があります。また、イベントの発生はシステム内部のFIFOキューにより処理されるため、一定時間内に処理できない場合はイベントが消失する場合もあります。

以下にハンドラ関数定義用のマクロの解説を行います。

### DIO

```cpp
MWX_DIO_INT(N, uint32_t arg, uint8_t& handled)
MWX_DIO_EVENT(N, arg)
```

DIO(ディジタルIO)割り込み・イベントです。`N`は対象DIOの番号を指定します。`arg`は将来の拡張のための定義です。

割り込みを発生させるためには[`pinMode()`](/v0.1.3/api-reference/functions-1/dio/pinmode)による適切な入力設定, [`attachDioInt()`](/v0.1.3/api-reference/functions-1/dio/attachintdio)による割り込み開始の設定が必要です。

### TICKTIMER

```cpp
MWX_TICKTIMER_INT(uint32_t arg, uint8_t& handled)
MWX_TICKTIMER_EVENT(uint32_t arg)
```

TickTimer割り込み・イベントです。`arg`は将来の拡張のための定義です。

{% hint style="danger" %}
TickTimerの`handled`フラグを`true`にセットしてはいけません。TWENETが動作しなくなります。
{% endhint %}

### TIMER

```cpp
MWX_TIMER_INT(N, uint32_t arg, uint8_t& handled)
MWX_TIMER_EVENT(N, uint32_t arg)
```

タイマー割り込み・イベントです。`N`は対象タイマーの番号を指定します。`arg`は将来の拡張のための定義です。

割り込みを発生させるためには、[Timerオブジェクト](/v0.1.3/api-reference/predefined_objs/timers)をソフトウェア割り込みを有効にして開始します。

### その他

{% hint style="warning" %}
MWXライブラリで標準的に定義しない、その他の割り込み・イベントの定義です。AHIペリフェラルマニュアルの理解が必要です。
{% endhint %}

その他の割り込み・イベントは以下のハンドラ関数で受けることが出来ます。これらは将来的に専用のハンドラが定義された場合、利用できなくなります。

```cpp
MWX_MISC_INT(uint32_t arg, uint32_t arg2, handled)
MWX_MISC_EVENT(auint32_t rg, uint32_t arg2)
```

ペリフェラル (AHI) の割り込みハンドラの`u32DeviceId`が`arg`、`u32ItemBitmap`が`arg2`に対応します。

## 状態マシン

状態マシン（ステートマシン）は、メッセージを受け取り、そのメッセージに応じて状態を遷移させながら動作させるアプリケーションの記述方法です。

[PAL\_AMB-behavior](/v0.1.3/api-reference/behavior/pal_amb-behavior)サンプルでは、センサーの動作開始からセンサーの値取得、無線パケット送信から送信完了まで、スリープ遷移といったアプリケーションの動作の流れを記述しています。実例として参考にしてください。

受け取るイベントは以下となります。

| イベント名                 | 内容                                                                                                                                                                                                                                                |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `E_EVENT_START_UP`    | <p>システム始動時に呼び出される。電源投入直後はパラメータが<code>0</code>で呼び出されます。<strong>実行初期であるため、通常処理を行う状態に遷移する場合は一旦begin()メソッドから<code>PEV\_Process()</code>を呼び出し動作を開始させます。</strong><br><br>スリープ復帰後も呼び出されるがパラメータは<code>0</code>以外です。<strong>この状態からは通常処理を行えます。</strong></p> |
| `E_EVENT_NEW_STATE`   | 状態遷移直後に新しい状態で呼び出されます。ある状態に遷移したときに最初に実行される処理を記述します。                                                                                                                                                                                                |
| `E_EVENT_TICK_TIMER`  | 1msごとのTickTimerで呼び出されます。                                                                                                                                                                                                                          |
| `E_EVENT_TICK_SECOND` | １秒毎に呼び出されます。                                                                                                                                                                                                                                      |

### PEV\_SetState()

```cpp
void PEV_SetState(uint32_t s)
```

状態を`s`に設定します。

状態ハンドラを抜けると次の状態に遷移し、続けて`E_EVENTS_NEW_STATE`イベントで状態ハンドラが呼び出されます。

### PEV\_u32Elaspsed\_ms()

```cpp
uint32_t PEV_u32Elaspsed_ms()
```

状態遷移してからの経過時間\[ms]を返します。タイムアウトを管理するような目的で使用します。

```cpp
MWX_STATE(MY_APP_CHILD::STATE_TX, uint32_t ev, uint32_t evarg) {
  ...
  
	if (PEV_u32Elaspsed_ms() > 100) {
		// does not finish TX!
		Serial << "[STATE_TX] FATAL, TX does not finish!" << mwx::crlf << mwx::flush;
		the_twelite.reset_system();
	}
}
```

上記の例では100ms経過した時点でシステムリセットを行います。

### PEV\_Process()

```cpp
void PEV_Process(uint32_t ev, uint32_t u32evarg) {
```

状態ハンドラ外から呼び出します。状態ハンドラをイベント`ev`パラメータ`u32evarg`で実行します。

```cpp
void transmit_complete(mwx::packet_ev_tx& txev) {
    Serial << "..txcomp=" << int(txev.u8CbId) << mwx::crlf;
    PEV_Process(E_ORDER_KICK, txev.u8CbId); // pass the event to state machine
}
```

送信完了イベントを状態マシンに伝えます。つまり状態ハンドラの呼び出しを行います。

{% hint style="danger" %}
直接状態ハンドラを呼び出すことは行わないでください。`E_EVENT_NEW_STATE`が実行されないなどの問題が発生します。
{% endhint %}

### PEV\_KeepStateOnWakeup()

```cpp
void PEV_KeepStateOnWakeup()
```

スリープ直前に設定します。スリープ復帰後に、直前の状態を維持します。つまり、スリープを開始した状態で`E_EVENT_START_UP`で状態ハンドラが呼び出されます。

### PEV\_is\_coldboot()

```cpp
bool PEV_is_coldboot(uint32_t ev, uint32_t u32evarg)
```

イベントが起床時の`E_EVENT_START_UP`かどうか判定します。

### PEV\_is\_warmboot()

```cpp
bool PEV_is_warmboot(uint32_t ev, uint32_t u32evarg)
```

イベントがスリープ復帰時の`E_EVENT_START_UP`かどうか判定します。


# PAL\_AMB-behavior

[環境センサーパル AMBIENT SENSE PAL](https://mono-wireless.com/jp/products/twelite-pal/sense/amb-pal.html) を用い、センサー値の取得を行います。

* [ビヘイビア](/v0.1.3/api-reference/behavior)を用いた親機子機の記述を行っています。
* センサーを値を得るのに[ボードビヘイビア](/v0.1.3/boards/pal/pal_amb)の機能を使わず[`Wire`](/v0.1.3/api-reference/predefined_objs/wire)を用いて直接記述しています。
* 子機はステートマシンによる[状態遷移](/v0.1.3/api-reference/behavior#mashin)により記述しています。

{% hint style="success" %}
このアクトの解説の前に[BRD\_APPTWELITEの解説](/v0.1.3/act_samples/brd_apptwelite)、[PAL\_AMBの解説](/v0.1.3/act_samples/pal_amb)、[PAL\_AMB-usenapの解説](/v0.1.3/act_samples/pal_amb-usenap)を参照してください。また[ビヘイビアの解説](/v0.1.3/api-reference/behavior)についても参照ください。
{% endhint %}

{% hint style="info" %}
このサンプルは[ビヘイビア](/v0.1.3/api-reference/behavior)の記述方法のサンプルです。ビヘイビアはより複雑なアプリケーションを記述する際に用います。
{% endhint %}

## アクトの機能

* 環境センサーパル AMBIENT SENSE PAL を用い、センサー値の取得を行います。
* コイン電池で動作させるための、スリープ機能を利用します。

## アクトの使い方

### TWELITEの準備

| 役割 | 例                                                                                                                                                                                            |
| -- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 親機 | [MONOSTICK BLUEまたはRED](https://mono-wireless.com/jp/products/MoNoStick/)                                                                                                                     |
| 子機 | [BLUE PAL または RED PAL](https://mono-wireless.com/jp/products/twelite-pal/BnR/index.html) +[環境センサーパル AMBIENT SENSE PAL](https://mono-wireless.com/jp/products/twelite-pal/sense/amb-pal.html) |

{% hint style="warning" %}
親機にPALを使用する場合は、コイン電池での動作はできません。目安として50mA以上の電流を安定して得られるような電源環境を用意してください。
{% endhint %}

## ファイル構成

* PAL\_AMB-behavior.hpp : `setup()`のみの定義です。DIP-SWを読み出し、D1..D3が上位置の場合は親機として動作し、それ以外は子機としてDIP SWに対応するIDをセットします。
* Parent/myAppBhvParent.hpp : 親機用のビヘイビアクラス定義
* Parent/myAppBhvParent.cpp : 実装&#x20;
* Parent/myAppBhvParent-handlers.cpp : ハンドラーの実装
* Parent/myAppBhvParent.hpp : 子機用のビヘイビアクラス定義
* Parent/myAppBhvParent.cpp : 実装
* Parent/myAppBhvParent-handlers.cpp : ハンドラーの実装

親機のビヘイビア名は`<MY_APP_PARENT>`、子機は`<MY_APP_CHILD>`です。

{% hint style="info" %}
ビルドファイルの追加は[Makefileの解説](/v0.1.3/install_n_build/makefile)を参照してください。
{% endhint %}

## 初期化 setup()

```cpp
// now read DIP sw status can be read.
u8ID = (brd.get_DIPSW_BM() & 0x07);

// Register App Behavior (set differnt Application by DIP SW settings)
if (u8ID == 0) {
	// put settings to the twelite main object.
	the_twelite
		<< TWENET::appid(APP_ID)     // set application ID (identify network group)
		<< TWENET::channel(CHANNEL)  // set channel (pysical channel)
		<< TWENET::rx_when_idle();   // open RX channel

	the_twelite.app.use<MY_APP_PARENT>();
} else {		
	// put settings to the twelite main object.
	the_twelite
		<< TWENET::appid(APP_ID)     // set application ID (identify network group)
		<< TWENET::channel(CHANNEL); // set channel (pysical channel)

	the_twelite.app.use<MY_APP_CHILD>();
}
```

DIP SWの読み値が0の場合は親機用のビヘイビア`<MY_APP_PARENT>`を、それ以外の場合は子機用のビヘイビア`<MY_APP_CHILD>`を登録します。

{% hint style="warning" %}
親機がMONOSTICKの場合は、PAL用のDIP SWの読み値は0となり、親機としてふるまいます。ただしこの動作はMONOSTICKの仕様として定義されているものではありません。
{% endhint %}

## 親機のビヘイビア

親機はスリープをしない受信機としてふるまい、子機からのパケットを受信したときにシリアルポートにパケットの情報を出力します。

### MY\_APP\_PARENT::receive()

```cpp
void MY_APP_PARENT::receive(mwx::packet_rx& rx) {
	uint8_t msg[4];
	uint32_t lumi;
	uint16_t u16temp, u16humid;

	// expand packet payload (shall match with sent packet data structure, see pack_bytes())
	auto&& np = expand_bytes(rx.get_payload().begin(), rx.get_payload().end(), msg);
	
	// if PING packet, respond pong!
	if (!strncmp((const char*)msg, (const char*)FOURCHARS, 4)) {
		// get rest of data
		expand_bytes(np, rx.get_payload().end(), lumi, u16temp, u16humid);

		// print them
		Serial << format("Packet(%x:%d/lq=%d/sq=%d): ",
							rx.get_addr_src_long(), rx.get_addr_src_lid(),
							rx.get_lqi(), rx.get_psRxDataApp()->u8Seq)
			   << "temp=" << double(int16_t(u16temp)/100.0)
			   << "C humid=" << double(int16_t(u16humid)/100.0)
			   << "% lumi=" << int(lumi)
			   << mwx::crlf << mwx::flush;
    }
}
```

親機用がパケットを受信したときは、パケットの先頭４文字が照合（`FOURCHARS`）できれば、そのパケット内容を表示します。

### MY\_APP\_PARENT::MWX\_TICKTIMER\_INT()

```cpp
MWX_TICKTIMER_INT(uint32_t arg, uint8_t& handled) {
  // blink LED
  digitalWrite(PAL_AMB::PIN_LED, 
    ((millis() >> 9) & 1) ? PIN_STATE::HIGH : PIN_STATE::LOW);
}
```

親機の割り込みハンドラはLEDの点滅を行います。

### MY\_APP\_PARENT::MWX\_DIO\_EVENT(PAL\_AMB::PIN\_BTN)

```cpp
MWX_DIO_EVENT(PAL_AMB::PIN_BTN, uint32_t arg) {
	Serial << "Button Pressed" << mwx::crlf;
	
	static uint32_t u32tick_last;
	uint32_t tick = millis();

	if (tick - u32tick_last > 100) {
		PEV_Process(E_ORDER_KICK, 0UL);
	}

	u32tick_last = tick;
}
```

PAL上のボタン(5)が押されたときには、状態マシンに対して`E_ORDER_KICK`イベントを発行します。

### MY\_APP\_PARENT::MWX\_STATE(E\_MWX::STATE\_0 .. 3)

状態マシンは、状態遷移の参考として記述したもので、アプリケーションの動作上意味のあるものではありません。ボタンから送付されるE\_ORDER\_KICKイベントによる状態遷移や、タイムアウトなどを実行しています。

## 子機のビヘイビア

子機の動作の流れはPAL\_AMB-usenapと同じです。初回スリープから「起床→センサー動作開始→短いスリープ→起床→センサー値取得→無線送信→無線送信完了待ち→スリープ」を繰り返します。

### MY\_APP\_CHILD::on\_begin()

```cpp
void _begin() {
    // sleep immediately.
    Serial << "..go into first sleep (1000ms)" << mwx::flush;
    the_twelite.sleep(1000);
}
```

`on_begin()`から呼び出される`_begin()`関数では、初回スリープを実行しています。

(※`_begin()`関数で本処理を記述せず`on_begin()`に直接記述してもかまいません)

### MY\_APP\_CHILD::wakeup()

```cpp
void wakeup(uint32_t & val) {
    Serial << mwx::crlf << "..wakeup" << mwx::crlf;
    // init wire device.
    Wire.begin();
    
    // turn on LED
    digitalWrite(PAL_AMB::PIN_LED, PIN_STATE::LOW);

    // KICK it!
    PEV_Process(E_ORDER_KICK, 0); // pass the event to state machine
}
```

スリープからの起床処理を記述しています。

ここで初回の`Wire.begin()`を実行しています。２回目以降のスリープ起床時では冗長な記述です。この処理は`on_begin()`に移動してもかまいません。

### MY\_APP\_CHILD::transmit\_complete()

```cpp
void transmit_complete(mwx::packet_ev_tx& txev) {
    Serial << "..txcomp=" << int(txev.u8CbId) << mwx::crlf;
    PEV_Process(E_ORDER_KICK, txev.u8CbId); // pass the event to state machine
}
```

送信完了時に状態マシンに対して`E_ORDER_KICK`メッセージを処理します。

### MY\_APP\_CHILD::transmit\_complete()

```cpp
static const uint8_t STATE_IDLE = E_MWX::STATE_0;
static const uint8_t STATE_SENSOR = E_MWX::STATE_1;
static const uint8_t STATE_TX = E_MWX::STATE_2;
static const uint8_t STATE_SLEEP = E_MWX::STATE_3;
```

状態名を定義しています。

### MY\_APP\_CHILD::shtc3\_???()

```cpp
MWX_APIRET MY_APP_CHILD::shtc3_start()
MWX_APIRET MY_APP_CHILD::shtc3_read()
```

SHTC3用のセンサー取得実装例です。送付コマンド等の詳細はSHTC3のデータシートなどを参考にしてください。

### MY\_APP\_CHILD::ltr308als\_???()

```cpp
MWX_APIRET MY_APP_CHILD::ltr308als_read()
MWX_APIRET MY_APP_CHILD::ltr308als_start()
static MWX_APIRET WireWriteAngGet(uint8_t addr, uint8_t cmd)
```

LTR308ALSのセンサー取得実装例です。送付コマンド等の詳細はLTR308ALSのデータシートなどを参考にしてください。

`WireWriteAndGet()`は`addr`のデバイスに対して`cmd`を１バイト送信してから、１バイト受信して値を返します。

### MY\_APP\_CHILD::STATE\_IDLE (0)

```cpp
MWX_STATE(MY_APP_CHILD::STATE_IDLE, uint32_t ev, uint32_t evarg) {
	if (PEV_is_coldboot(ev,evarg)) {
		Serial << "[STATE_IDLE:START_UP(" << int(evarg) << ")]" << mwx::crlf;
		// then perform the first sleep at on_begin().
	} else
	if (PEV_is_warmboot(ev,evarg)) {
		Serial << "[STATE_IDLE:START_UP(" << int(evarg) << ")]" << mwx::crlf;
		PEV_SetState(STATE_SENSOR);
	}
}
```

0番の状態は特別な意味を持ちます。起動直後またはスリープ復帰後の状態です。

起動直後`PEV_is_coldboot(ev,evarg)`判定が`true`になって呼び出されます。`on_begin()`から、そのままスリープしてしまうため、状態遷移するようなコードも含まれません。**この時点では主要な初期化がまだ終わっていませんので、無線パケットの送信など複雑な処理を行うことが出来ません。**&#x305D;のような処理を行うための最初の状態遷移を行うためには`on_begin()`からイベントを送り、そのイベントに従って状態遷移を行います。

スリープ復帰後は`PEV_is_warmboot(ev,evarg)`が`true`になる呼び出しが最初にあります。`PEV_SetState()`を呼び出し`STATE_SENSOR`状態に遷移します。

### MY\_APP\_CHILD::STATE\_SENSOR

```cpp
MWX_STATE(MY_APP_CHILD::STATE_SENSOR, uint32_t ev, uint32_t evarg) {
	if (ev == E_EVENT_NEW_STATE) {
		Serial << "[STATE_SENSOR:NEW] Start Sensor." << mwx::crlf;

		// start sensor capture
		shtc3_start();
		ltr308als_start();

		// take a nap waiting finish of capture.
		Serial << "..nap for 66ms" << mwx::crlf;
		Serial.flush();
		PEV_KeepStateOnWakeup(); // stay this state on waking up.
		the_twelite.sleep(66, false, false, TWENET::SLEEP_WAKETIMER_SECONDARY);
	} else
	if (PEV_is_warmboot(ev,evarg)) {
		// on wakeup, code starts here.
		Serial << "[STATE_SENSOR:START_UP] Wakeup." << mwx::crlf;

		PEV_SetState(STATE_TX);
	}
}
```

スリープ復帰後`STATE_IDLE`から遷移したとき、`STATE_SENSOR`の状態ハンドラが続けて呼び出されます。この時のイベント`ev`は`E_EVENT_NEW_STATE`です。

ここではSHTC3, LTR308ALSの２センサーの動作開始を行います。一定時間経過すれば、センサーはデータ取得可能な状態になります。この時間待ちを`66`ms設定のスリープで行います。スリープ前に`PEV_KeepStateOnWakeup()`が呼ばれている点に注意してください。この呼び出しを行うと、スリープ復帰後の状態は`STATE_IDLE`ではなく、スリープしたときの状態、つまり`STATE_SENSOR`となります。

短いスリープから復帰すると`PEV_is_warmboot(ev,evarg)`判定が`true`となる呼び出しが最初に発生します。この呼び出し時点で、無線パケットの送信などを行うことが出来ます。`STATE_TX`に遷移します。

### MY\_APP\_CHILD::STATE\_TX

```cpp
MWX_STATE(MY_APP_CHILD::STATE_TX, uint32_t ev, uint32_t evarg)
	static int u8txid;

	if (ev == E_EVENT_NEW_STATE) {
		Serial << "[STATE_TX:NEW]" << mwx::crlf;
		u8txid = -1;

		auto&& r1 = shtc3_read();
		auto&& r2 = ltr308als_read();

		Serial << "..shtc3 t=" << int(i16Temp) << ", h=" << int(i16Humd) << mwx::crlf;
		Serial << "..ltr308als l=" << int(u32Lumi) << mwx::crlf;

		if (r1 && r2) {
			if (auto&& pkt = the_twelite.network.use<NWK_SIMPLE>().prepare_tx_packet()) {
```

ここでは`E_EVENT_NEW_STATE`イベントの時に、センサーデータ読み出し、無線パケットの送信手続きに入ります。送信手続きの詳細は他のアクトサンプル例を参考にしてください。

```cpp
void transmit_complete(mwx::packet_ev_tx& txev) {
    Serial << "..txcomp=" << int(txev.u8CbId) << mwx::crlf;
    PEV_Process(E_ORDER_KICK, txev.u8CbId); // pass the event to state machine
}

    // ↓　↓　↓ メッセージ送付

} else if (ev == E_ORDER_KICK && evarg == uint32_t(u8txid)) {
		Serial << "[STATE_TX] SUCCESS TX(" << int(evarg) << ')' << mwx::crlf;
		PEV_SetState(STATE_SLEEP);
}
```

送信完了まちの処理はループでのアクト記述と違い、`transmit_complete()`からの`PEV_Process()`によるメッセージを待つことで完了確認としています。メッセージを受け取った時点でスリープします。スリープ処理は`STATE_SLEEP`に遷移することで行っています。

```cpp
	if (PEV_u32Elaspsed_ms() > 100) {
		// does not finish TX!
		Serial << "[STATE_TX] FATAL, TX does not finish!" << mwx::crlf << mwx::flush;
		the_twelite.reset_system();
	}
```

最後にタイムアウト処理を行っています。万が一送信パケットの完了メッセージが戻ってこなかった場合を想定します。`PEV_u32Elaspsed_ms()`はその状態に遷移してからの経過時間を\[ms]で返します。時間経過した場合は、上記では（このタイムアウトは余程のことだとして）システムリセット`the_twelite.reset_system()`を行います。

### MY\_APP\_CHILD::STATE\_SLEEP

```cpp
MWX_STATE(MY_APP_CHILD::STATE_SLEEP, uint32_t ev, uint32_t evarg) {
	if (ev == E_EVENT_NEW_STATE) {
		Serial << "..sleep for 5000ms" << mwx::crlf;
		pinMode(PAL_AMB::PIN_BTN, PIN_MODE::WAKE_FALLING_PULLUP);
		digitalWrite(PAL_AMB::PIN_LED, PIN_STATE::HIGH);
		Serial.flush();

		the_twelite.sleep(5000); // regular sleep
	}
}
```

スリープを行います。前の状態から遷移した直後の`E_EVENT_NEW_STATE`に記述します。スリープ直前に他のイベントが呼ばれる可能性がありますので、必ず１回だけ実行される判定式の中で`the_twelite.sleep()`を実行してください。


# 関数


# システム関数


# millis()

システム時刻\[ms]を得ます。

```cpp
uint32_t millis()
```

システム時刻はTickTimerの割り込みで更新されます。


# delay()

ポーリングによる時間待ちを行います。

```cpp
void delay(uint32_t ms)
```

`ms`にて与えられた期間待ち処理を行います。

時間の計測はTickTimerのカウントによって行っています。また長い時間待ちを行う場合はCPUのクロックを低下してポーリング処理を行います。

{% hint style="info" %}
`delay()`を呼び出してから約5ms経過するごとにTWELITEマイコン内部のウォッチドッグ処理を行います。

※例えば`while(1) delay(1);` を実行した場合は、`delay()`内部で5ms経過しないためウォッチドッグ処理が行われず、一定時間後リセットが実行されます。
{% endhint %}

{% hint style="warning" %}
`setup(), wakeup()`関数内では、TickTimerがまだ動作していないため、whileループによる時間待ち処理になります。この場合、指定値との誤差は大きくなります。このループカウンタは32Mhzに合わせて調整しています。これら関数内でCPUクロックを変化させた場合は、そのクロックに比例した誤差が発生します。

パラメータに1,2といった短い時間を指定した場合は、誤差が大きくなる場合があります。
{% endhint %}


# random()

乱数を生成します。

```cpp
uint32_t random(uint32_t maxval)
uint32_t random(uint32_t minval, uint32_t maxval)
```

１行目は`0..(maxval-1)`の値を戻します。**maxvalの値が最大値ではない**ことに注意してください。

２行目は`minval..maxval-1`の値を戻します。


# DIO 汎用ディジタルIO

汎用ディジタルIO（DIO）の操作には以下の関数を利用します。

* `pinMode()`
* `digitalWrite()`
* `digitalRead()`
* `attachIntDio()`
* `detachIntDio()`

## 定数

### ピン名と番号

| 定義                                      | 名称        |
| --------------------------------------- | --------- |
| `const uint8_t PIN_DIGITAL::DIO0 .. 19` | DIOピン0～19 |
| `const uint8_t PIN_DIGITAL::DO0 .. 1`   | DOピン0,1   |

### ピンのモード（DIO0..19)

以下の列挙値は型名 `E_PIN_MODE`で取り扱われます。

| 定義                              | プルアップ | 名称           |
| ------------------------------- | :---: | ------------ |
| `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`      |   有   | 入力状態に戻す      |

### ピンのモード（DO0,1)

以下の列挙値は型名 `E_PIN_MODE`で取り扱われます。

| 定義                           | 名称           |
| ---------------------------- | ------------ |
| `PIN_MODE::OUTPUT`           | 出力           |
| `PIN_MODE::OUTPUT_INIT_HIGH` | 出力(初期状態HIGH) |
| `PIN_MODE::OUTPUT_INIT_LOW`  | 出力(初期状態LOW)  |
| `PIN_MODE::DISABLE_OUTPUT`   | 出力設定をやめる     |

### ピンの状態

以下の列挙値は型名 `E_PIN_STATE`で取り扱われます。

| 定義                |  値  | 名称               |
| ----------------- | :-: | ---------------- |
| `PIN_STATE::HIGH` |  1  | HIGHレベル(=Vccレベル) |
| `PIN_STATE::LOW`  |  0  | LOWレベル(=GNDレベル)  |

### ピンの立ち上がり、立下り

以下の列挙値は型名 `E_PIN_INT_MODE`で取り扱われます。

| 定義                      | 名称    |
| ----------------------- | ----- |
| `PIN_INT_MODE::FALLING` | 立ち下り  |
| `PIN_INT_MODE::RISING`  | 立ち上がり |


# pinMode()

DIO(汎用ディジタルIO)ピンの設定を行います。

```cpp
void pinMode(uint8_t u8pin, E_PIN_MODE mode)
```

この関数では DIO0..19 と、DO0,1のピンの状態を変更できます。設定内容は `E_PIN_MODE` の列挙値の[DIOの解説](/v0.1.3/api-reference/functions-1/dio#pinnomdodio-0-19)と[DOの解説](/v0.1.3/api-reference/functions-1/dio#pinnomdodo-0-1)を参照してください。

{% hint style="danger" %}
DO0,1は特殊なピンで、原則として他の目的で利用されるものですが、出力としても設定可能です。ただしハード的な制約があるピンですので、利用には注意が必要です。

両方のピンは、電源投入時にHIGHレベルが担保される必要があります。不安定な電圧をとったりするような回路構成の場合、モジュールが起動しないなどの問題が出ます。
{% endhint %}


# digitalWrite()

ディジタル出力ピンの設定を変更します。

```cpp
static inline void digitalWrite(uint8_t u8pin, E_PIN_STATE ulVal)
```

事前に`pinMode()`にて設定対象のピンを出力用に設定しておきます。１番目のパラメータは、設定対象のピン番号を指定します。２番目のパラメータは`HIGH`か`LOW`のいずれかを指定します。

{% hint style="info" %}
入力が `E_PIN_STATE` 型となっています。`E_PIN_STATE`から`int`型への変換演算子は定義していませんので、数値による直接の入力はできないようになっています。
{% endhint %}


# digitalRead()

入力設定のポートの値を読み出す。

```cpp
static inline E_PIN_STATE digitalRead(uint8_t u8pin)
```

事前に入力に設定したピンの入力値を`LOW`または`HIGH`で得ます。

{% hint style="info" %}
`E_PIN_STATE`型から`int`型への変換演算子は定義していないため、数値型への直接的な代入はできません。
{% endhint %}


# attachIntDio()

DIO割り込みを有効にします。

```cpp
void attachIntDio(uint8_t u8pin, E_PIN_INT_MODE mode)
```

事前に入力設定したピンに対して、１番目のパラメータは割り込みを有効にしたいピン番号で、２番目は割り込み方向（[立ち上がり、立ち下がり](/v0.1.3/api-reference/functions-1/dio#pinnochigariri)）を指定します。

{% hint style="info" %}
割り込みハンドラ、イベントハンドラの記述は[アプリケーションビヘイビア](/v0.1.3/api-reference/behavior)で行います。
{% endhint %}

## 例

DIO5のピンがHIGHからLOWに変化したときに割り込みが発生する設定を行う。

```cpp
void setup() {
  the_twelite.app.use<myAppClass>();
  
  pinMode(PIN_DIGITAL::DIO5, PIN_MODE::INPUT_PULLUP);
  attachIntDio(PIN_DIGITAL::DIO5, PIN_INT_MODE::FALLING);
}

void loop() {
  ;
}
```

#### myAppClass.hpp

```cpp
class myAppClass: public mwx::BrdPal, MWX_APPDEFS_CRTP(myAppClasslMot)
{

};
```

アプリケーションビヘイビア`myAppClass`の基本定義。詳細は省略している。

#### myAppClass.cpp

```cpp
/*****************************************************************/
// MUST DEFINE CLASS NAME HERE
#define __MWX_APP_CLASS_NAME myAppClass
#include "_mwx_cbs_cpphead.hpp"
/*****************************************************************/

MWX_DIO_INT(PIN_DIGITAL::DIO5, uint32_t arg, uint8_t& handled) {
  static uint8_t ct;
  digitalWrite(PIN_DIGITAL::DIO12, (++ct & 1) ? HIGH : LOW);
	handled = false; // if true, no further event.
}

MWX_DIO_EVENT(PIN_DIGITAL::DIO5, uint32_t arg) {
  Serial << '*';	
}

/*****************************************************************/
// common procedure (DO NOT REMOVE)
#include "_mwx_cbs_cpptail.cpp"
// MUST UNDEF CLASS NAME HERE
#undef __MWX_APP_CLASS_NAME
} // mwx
/*****************************************************************/

```

アプリケーションビヘイビア`myAppClass`の割り込みハンドラの記述。DIO5の割り込み発生時にDIO12の出力設定を反転させ、割り込みハンドラが終了してから発生するイベントではシリアルポート`Serial`に`*`を表示する。


# detachIntDio()

割り込みハンドラの登録を解除します。

```cpp
void detachIntDio(uint8_t u8pin)
```


# ユーティリティ関数


# pack\_bits()

指定したビット位置に1をセットします。

```cpp
constexpr uint32_t pack_bits(...)
```

パラメータは可変数引数で指定でき、各パラメータはビット位置を指定する0..31の整数を指定する。例えば`pack_bits(1,3,6)`と指定すると`((1UL<<1)|(1UL<<3)|(1UL<<6))`を返します。

{% hint style="info" %}
`constexpr`は定数による計算が可能な場合はコンパイル時に定数展開します。
{% endhint %}


# collect\_bits()

整数から指定したビット位置の値を取得し、指定した順番のビットマップを作成します。

```cpp
constexpr uint32_t collect_bits(uint32_t bm, ...)
```

  パラメータbmに指定する値から、その後の可変数パラメータで指定する0..31のビット位置に対応する値を取り出します。取り出した値はパラメータ順に並べビットマップとして戻り値になります。

ビットマップの並び順は、最初のパラメータを上位ビットとし末尾のパラメータがbit0になります。

```cpp
uint32_t b1 = 0x12; // (b00010010)
uint32_t b2 = collect_bits(b1, 4, 2, 1, 0); 
  // bit4->1, bit2->0, bit1->1, bit0->0
  // b2=0x10 (b1010)
```

例ではb1のビット4,2,1,0を取り出すと (1,0,1,0) になります。これをb1010として0x10のように計算されます。&#x20;


# pack\_bytes()

要素データを並べてバイト列を生成します。

```cpp
uint8_t* pack_bytes(uint8_t* b, uint8_t* e, ...)
```

`pack_bytes`はコンテナクラスの`begin()`,`end()`イテレータをパラメータとし、続くパラメータで指定されるデータをバイト列としてコンテナに書き込みます。

可変引数パラメータに与えるデータは以下に示すとおりです。

| データ型                 | バイト数 | 解説                                                 |
| -------------------- | :--: | -------------------------------------------------- |
| `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()`で生成できる。 |

```cpp
smplbuf_u8& pack_bytes(smplbuf_u8& c, ...)
```

`pack_bytes`はコンテナオブジェクトをパラメータとし、続くパラメータで指定されるデータをバイト列としてコンテナに書き込みます。コンテナの`.push_back()`メソッドで末尾に追加します。

可変引数パラメータに与えるデータは以下に示すとおりです。

| データ型                 |    バイト数   | 解説                                                    |
| -------------------- | :-------: | ----------------------------------------------------- |
| `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()`)のデータを格納する。 |

## 例

```cpp
auto&& rx = the_twelite.receiver.read();

smplbuf<uint8_t, 128> buf;
mwx::pack_bytes(buf
	, uint8_t(rx.get_addr_src_lid())	// src addr (LID)
	, uint8_t(0xCC)							      // cmd id (0xCC, fixed)
	, uint8_t(rx.get_psRxDataApp()->u8Seq)	// seqence number
	, uint32_t(rx.get_addr_src_long())		// src addr (long)
	, uint32_t(rx.get_addr_dst())			// dst addr
	, uint8_t(rx.get_lqi())					  // LQI
	, uint16_t(rx.get_length())				// payload length
	, rx.get_payload() 						    // payload
);
```

この例では受信パケットの各属性やペイロードを別のバッファ`buf`に再格納しています。


# expand\_bytes()

バイト列を分解し変数に格納します。

```cpp
const uint8_t* expand_bytes(
        const uint8_t* b, const uint8_t* e, ...)
```

`expand_bytes()`は、パラメータに`uint8_t*`型のイテレータの組み合わせを指定します。これは解析対象の先頭と末尾の次のイテレータの指定となります。`e`の位置まで解析が進んだ場合はエラーとなり`nullptr`を返します。

展開にエラーがない場合は、次の読み出しを行うイテレータを戻します。

可変数パラメータには以下を指定します。

| バイト数                 | データ長 | 解説                                               |
| -------------------- | :--: | ------------------------------------------------ |
| `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()`で生成できる |

## 例

```cpp
auto&& rx = the_twelite.receiver.read();

char fourchars[5]{}; 
auto&& np = 
	expand_bytes(rx.get_payload().begin(), rx.get_payload().end()
		, make_pair((uint8_t*)fourchars, 4)
    );

// read rest of payload
uint8_t u8DI_BM_remote = 0xff;
uint16_t au16AI_remote[5];
expand_bytes(np, rx.get_payload().end()
	, u8DI_BM_remote
	, au16AI_remote[0]
	, au16AI_remote[1]
	, au16AI_remote[2]
	, au16AI_remote[3]
	, au16AI_remote[4]
);
```

この例では、まず４バイトの文字列を読み出しています。ここでは`make_pair()`を用いて明示的に４バイト分のデータを読み出します。

戻されたイテレータ`np`をもとに、次のデータを読み出します。次のデータは`uint8_t`型、あとは`uint16_t`型が５つ続いています。


# CRC8, XOR, LRC

チェックサムの計算で良く用いられる値です。

```cpp
uint8_t CRC8_u8Calc(uint8_t *pu8Data, uint8_t size, uint8_t init=0)
uint8_t CRC8_u8CalcU32(uint32_t u32c, uint8_t init=0)
uint8_t CRC8_u8CalcU16(uint16_t u16c, uint8_t init=0)
uint8_t XOR_u8Calc(uint8_t *pu8Data, uint8_t size)
uint8_t LRC_u8Calc(uint8_t* pu8Data, uint8_t size)
```

CRC8, XOR, LRC([アスキー形式](/v0.1.3/api-reference/classes/ser_parser#asuk)で使用)の計算を行います。

`CRC8_u8CalcU16(), CRC8_u8CalcU32()`は`u16c, u32c`をビッグエンディアン並びとして、CRC8を計算します。

{% hint style="info" %}
CRC8は、計算式や初期値などによって種類がありますが、本ライブラリでは多項式を`X^8+X^5+X^4+1`(Polynomial Valueを0x31)をとしたものを使用しています。これはCRC8-CCITT や CRC8-Maximと呼ばれることがあります。
{% endhint %}

{% hint style="info" %}
XORは各要素の排他的論理和 XOR をとったものです。
{% endhint %}

{% hint style="info" %}
LRCは各要素の値の合計を計算し、下位８ビットの２の補数を取ります。結果、チェックサムを含め全要素を足し算すると０になります。
{% endhint %}


# ボード

Board behaviors

ボードビヘイビアは、TWELITE無線マイコンに接続したハードウェアを取り扱うための手続きが含まれます。

* 定数の定義（ピン番号など）
* ハードウェアの初期化
* センサー等の取り扱い


# \<BRD\_APPTWELITE>

標準アプリケーションApp\_Tweliteと同じ配線を想定したボードビヘイビアです。定数定義と、M1-M3,BPSピンの読み出し機能があります。

## 定数

以下の定数を定義しています。`BRD_APPTWELITE::PIN_DI1`のようにアクセスできます。

```cpp
static const uint8_t PIN_DI1 = mwx::PIN_DIGITAL::DIO12;
static const uint8_t PIN_DI2 = mwx::PIN_DIGITAL::DIO13;
static const uint8_t PIN_DI3 = mwx::PIN_DIGITAL::DIO11;
static const uint8_t PIN_DI4 = mwx::PIN_DIGITAL::DIO16;

static const uint8_t PIN_DO1 = mwx::PIN_DIGITAL::DIO18;
static const uint8_t PIN_DO2 = mwx::PIN_DIGITAL::DIO19;
static const uint8_t PIN_DO3 = mwx::PIN_DIGITAL::DIO4;
static const uint8_t PIN_DO4 = mwx::PIN_DIGITAL::DIO9;

static const uint8_t PIN_M1 = mwx::PIN_DIGITAL::DIO10;
static const uint8_t PIN_M2 = mwx::PIN_DIGITAL::DIO2;
static const uint8_t PIN_M3 = mwx::PIN_DIGITAL::DIO3;
static const uint8_t PIN_BPS = mwx::PIN_DIGITAL::DIO17;

static const uint8_t PIN_AI1 = mwx::PIN_ANALOGUE::A1;
static const uint8_t PIN_AI2 = mwx::PIN_ANALOGUE::A3;
static const uint8_t PIN_AI3 = mwx::PIN_ANALOGUE::A2;
static const uint8_t PIN_AI4 = mwx::PIN_ANALOGUE::A4;
```

## メソッド

DIP SW (M1 M2 M3 BPS) ピンの値を取得するためのメソッドが用意されています。

```cpp
inline uint8_t get_M1()
inline uint8_t get_M2()
inline uint8_t get_M3()
inline uint8_t get_BPS()
inline uint8_t get_DIPSW_BM()
```

戻り値はHIGH, LOWではなく、`0`がセットされていない(HIGH側)、`1`がスイッチがセットされる(LOW側)という意味です。

`get_DIPSW_BM()`は、bit0から順にM1,M2,M3,BPSピンの値を返します。

{% hint style="warning" %}
この値はシステム起動時に確認されて以降は、スイッチを操作しても更新されません。
{% endhint %}


# \<MONOSTICK>

MONOSTICK用のボードビヘイビアです。内蔵ウォッチドッグタイマーの制御とLED点灯用の手続きが含まれます。

## 定数

以下の定義が利用可能になります。

```cpp
const uint8_t PIN_LED = mwx::PIN_DIGITAL::DIO16;  // LED

const uint8_t PIN_WDT = mwx::PIN_DIGITAL::DIO9;     // WDT (shall tick < 1sec)
const uint8_t PIN_WDT_EN = mwx::PIN_DIGITAL::DIO11; // WDT (LO as WDT enabled)

const uint8_t PIN_LED_YELLOW = mwx::PIN_DIGITAL::DO1; // YELLOW LED
```

`MONOSTICK::PIN_LED`のようにアクセスできます。

## ハードの初期化

```cpp
pinMode(PIN_LED, OUTPUT_INIT_HIGH);
pinMode(PIN_WDT, OUTPUT_INIT_LOW);
pinMode(PIN_WDT_EN, OUTPUT_INIT_LOW);
pinMode(PIN_LED_YELLOW, OUTPUT);
```

上記のコードのように各ピンが初期化されます。

### ウォッチドッグタイマー

起動時、スリープ起床時、起動後一定時間経過後に外部のウォッチドッグタイマーを再セットします。

{% hint style="warning" %}
ウォッチドッグタイマーのタイムアウトは１秒です。

MONOSTICKでは通常スリープするアプリケーションを実行しませんが、その場合は`MONOSTICK::PIN_WDT_EN`をHIGHにしてからスリープします。
{% endhint %}

## メソッド

### set\_led()

```cpp
void set_led_red(uint8_t mode, uint16_t tick)
void set_led_yellow(uint8_t mode, uint16_t tick)


```

LED(赤、黄)の制御を行います。

{% hint style="warning" %}
ボードビヘイビアでの制御を行わない場合は、このメソッドを呼び出さないでください。
{% endhint %}

{% hint style="warning" %}
黄色の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);`
  {% endhint %}

{% hint style="warning" %}
黄色のLED(`MONOSTICK::PIN_LED_YELLOW`)は、スリープ中に点灯させることはできません。
{% endhint %}

`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]だけ点灯します。                                              |

{% hint style="info" %}
スリープ復帰後も復帰前の設定が維持されます。
{% endhint %}


# PAL

TWELITE PALのハードには共通部分があり、ボードビヘイビアも共通ハードについては、共通のインタフェースを定義しています。

## 定数

以下の定義が利用可能になります。

```cpp
static const uint8_t PIN_BTN = 12; // button (as SET)
static const uint8_t PIN_LED = 5;  // LED
static const uint8_t PIN_WDT = 13; // WDT (shall tick every 60sec)

static const uint8_t PIN_D1 = 1; // DIP SW1
static const uint8_t PIN_D2 = 2; // DIP SW2
static const uint8_t PIN_D3 = 3; // DIP SW3
static const uint8_t PIN_D4 = 4; // DIP SW4

static const uint8_t PIN_SNS_EN = 16;
static const uint8_t PIN_SNS_INT = 17;
```

`PAL_AMB::PIN_BTN`のようにアクセスできます。

## ハードの初期化

```cpp
pinMode(PIN_BTN, INPUT_PULLUP);
pinMode(PIN_LED, OUTPUT_INIT_HIGH);
pinMode(PIN_WDT, OUTPUT_INIT_HIGH);

pinMode(PIN_D1, INPUT_PULLUP);
pinMode(PIN_D2, INPUT_PULLUP);
pinMode(PIN_D3, INPUT_PULLUP);
pinMode(PIN_D4, INPUT_PULLUP);
```

上記のコードのように各ピンが初期化されます。

### ウォッチドッグタイマー

起動時、スリープ起床時、起動後一定時間経過後に外部のウォッチドッグタイマーを再セットします。

{% hint style="warning" %}
ウォッチドッグタイマーをタイムアウトしないためにTWELITEを60秒以内の設定（キャリブレーション済み内部CRタイマー使用時）で起床してください。
{% endhint %}

## メソッド

### set\_led()

```cpp
void set_led(uint8_t mode, uint16_t tick)
```

LED(D1)の制御を行います。

{% hint style="warning" %}
ボードビヘイビアでの制御を行わない場合は、このメソッドを呼び出さないでください。
{% endhint %}

`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]だけ点灯します。                                              |

{% hint style="info" %}
スリープ復帰後も復帰前の設定が維持されます。
{% endhint %}

### led\_one\_shot()

```cpp
void led_one_shot(uint16_t tick)
```

指定期間だけLEDを点灯します。`set_led()`の機能と同時には使えません。

### get\_D1() .. D4(), get\_DIPSW\_BM()

```cpp
inline uint8_t get_D1()
inline uint8_t get_D2()
inline uint8_t get_D3()
inline uint8_t get_D4()
inline uint8_t get_DIPSW_BM()
```

`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`とした和を返します。

{% hint style="warning" %}
D1..D4の`HIGH(1),LOW(0)`の値とは反対になります。

DIP SWの`LOW(0)`側がセットされた、つまり、`1`の値を持つと意味付けしているためです。
{% endhint %}

{% hint style="warning" %}
この値はシステム起動時に確認されて以降は、スイッチを操作しても更新されません。
{% endhint %}


# \<PAL\_AMB>

[環境センサーパル AMBIENT SENSE PAL](https://mono-wireless.com/jp/products/twelite-pal/sense/amb-pal.html) のボードビヘイビアです。

[共通定義](/v0.1.3/boards/pal)に加えボード上のセンサーを取り扱えるようになっています。

* 温湿度センサー SHTC3
* 照度センサー LTR308ALS

```cpp
void setup() {
  auto&& brd = the_twelite.board.use<PAL_AMB>();
}
```

## メンバーオブジェクト

### sns\_SHTC3

[SHTC3センサー](/v0.1.3/sensor_object/shtc3)のオブジェクトです。

### sns\_LTR308ALS

[LTR308ALSセンサー](/v0.1.3/sensor_object/ltr-308als)のオブジェクトです。


# \<PAL\_MAG>

[開閉センサーパル OPEN-CLOSE SENSE PAL](https://mono-wireless.com/jp/products/twelite-pal/sense/amb-pal.html) のボードビヘイビアです。

```cpp
void setup() {
  auto&& brd = the_twelite.board.use<PAL_MAG>();
}
```

開閉センサーパルのセンサーは磁気センサーで、２本の信号線の割り込みの入力のみです。

```cpp
const uint8_t PAL_MAG::PIN_SNS_NORTH = 16;
const uint8_t PAL_MAG::PIN_SNS_OUT1 = 16;
const uint8_t PAL_MAG::PIN_SNS_SOUTH = 17;
const uint8_t PAL_MAG::PIN_SNS_OUT2 = 17;
```

PAL\_MAG::PIN\_SNS\_NORTHはセンサーがN極を検出したとき、PAL\_MAG::PIN\_SNS\_SOUTHはセンサーがN極を検出したときに割り込みが入ります。

スリープ前に以下の設定をしておきます。

```cpp
pinMode(PAL_MAG::PIN_SNS_OUT1, PIN_MODE::WAKE_FALLING);
pinMode(PAL_MAG::PIN_SNS_OUT2, PIN_MODE::WAKE_FALLING);
```

起床時に起床要因のIOを確認します。

```cpp
uint8_t b_north = 
  the_twelite.is_wokeup_by_dio(PAL_MAG::PIN_SNS_NORTH);
uint8_t b_south = 
  the_twelite.is_wokeup_by_dio(PAL_MAG::PIN_SNS_SOUTH);
```


# \<PAL\_MOT>

[動作センサーパル MOTION SENSE PAL](https://mono-wireless.com/jp/products/twelite-pal/sense/amb-pal.html) のボードビヘイビアです。

[共通定義](/v0.1.3/boards/pal)に加えボード上のセンサーを取り扱えるようになっています。

* 加速度センサー MC3630

```cpp
void setup() {
  auto&& brd = the_twelite.board.use<PAL_MOT>();
}
```

## メンバーオブジェクト

### sns\_MC3630

[MC3630センサー](/v0.1.3/sensor_object/mc3630)のオブジェクトです。

###


# センサー

Sensor behaviors

センサーの手続きを定型化したクラスを用意しています。

## センサー取り扱いのための手続き

センサーの取り扱い前に`Wire.begin()`を実施しておいてください。スリープ復帰後は、Wireの再初期化は自動で行われるため特別な記述は必要ありません（注：ユーザコード上から明示的に `Wire.end()` を呼び出した場合は、再初期化を `wakeup()` に記述します）

```cpp
void setup() {
  auto&& brd = the_twelite.board.use<PAL_AMB>();
  ..
  Wire.begin();
	brd.sns_SHTC3.begin();
	brd.sns_LTR308ALS.begin();
}
```

読み出し開始後の手続きはセンサーの種類ごとに違いますが例えば`<PAL_AMB>`のセンサーは２つとも時間経過を管理します。時間経過をセンサーオブジェクトに伝えるには `process_ev()` メソッドを用います。

```cpp
void loop() {
	auto&& brd = the_twelite.board.use<PAL_AMB>();

	// mostly process every ms.
	if (TickTimer.available()) {
		//  wait until sensor capture finish
		if (!brd.sns_LTR308ALS.available()) {
			brd.sns_LTR308ALS.process_ev(E_EVENT_TICK_TIMER);
		}

		if (!brd.sns_SHTC3.available()) {
			brd.sns_SHTC3.process_ev(E_EVENT_TICK_TIMER);
		}
..
```

上記の例では1msおきのTickTimerを起点にして時間経過を伝えています。`E_EVENT_TICK_TIMER`はセンサーオブジェクトに1msの経過を伝えるものです。

スリープ復帰などで十分な時間が経過したときは替わりに`E_EVENT_START_UP`を渡します。センサーオブジェクトは速やかに読み出し可能として処理されます。

{% hint style="warning" %}
いずれの処理も現実世界の時間の経過と一致することを保証しているわけではありません。実際の経過時間が不足している場合は、センサーがエラーを返したり、期待しない値を返すことになります。
{% endhint %}

## 共通メソッド

### setup()

```cpp
void setup(uint32_t arg1 = 0, uint32_t arg2 = 0) 
```

センサーの初期化を行います。

### begin(), end()

```cpp
void begin(uint32_t arg1 = 0, uint32_t arg2 = 0)
void end()
```

センサーの取得を開始, 終了する。

### process\_ev()

```cpp
void process_ev(uint32_t arg1, uint32_t arg2 = 0)
```

待ち時間処理のセンサーの場合は`arg1`に`E_EVENT_TICK_TIMER`または`E_EVENT_START_UP`を与え時間の経過を知らせます。このメソッド呼出し後に、必要な時間が経過していればavailableになり、センサー値の読み出しが可能になります。

### available()

```cpp
bool available()
```

センサーが読み出し条件を満足したときに`true`を返します。

### probe()

```cpp
bool probe()
```

（対応しているセンサーのみ）センサーが接続されているときに`true`を返します。

{% hint style="warning" %}
`probe()`直後の初回の通信が失敗することがある。
{% endhint %}


# SHTC3 - 温湿度センサー

I2Cバスを利用した温湿度センサーです。

{% hint style="warning" %}
ボードビヘイビア `<PAL_AMB>` を読み込んだ時のみ使用可能です。`begin()`以外の共通メソッドの手続きはボードビヘイビア中で実行されています。
{% endhint %}

## 処理の流れ

1. `Wire.begin()`: バスの初期化
2. `.begin()`: センサーの動作開始
3. 時間待ち5ms
4. `.get_luminance()`: 値の読み出し

## 動作に必要な手続き

### Wireバス

`begin()`メソッド呼び出し前に`Wire.begin()`によりWireが動作状態にしておきます。

### スリープ復帰時の手続き

スリープ直前もWireバスが動作状態にしておきます（スリープ復帰後自動でWireを回復します）。

## メソッド

### get\_temp(), get\_temp\_cent()

```cpp
double get_temp()
int16_t get_temp_cent()
```

温度を読み出す。`get_temp()`は℃で、`get_temp_cent()`は℃の100倍の値を整数値で返します。

エラー時は-32760～-32768の値が返ります。

### get\_humid(), get\_humid\_per\_dmil()

```cpp
double get_humid()
int16_t get_humid_per_dmil()
```

湿度を読み出す。`get_humid()`は%で、`get_humid_per_dmil()`は%の100倍の値を整数値で返します。

エラー時は-32760～-32768の値が返ります。

## 共通メソッド

### setup()

```cpp
void setup() 
```

センサー用のメモリ領域の確保や初期化を行います。

### begin(), end()

```cpp
void begin()
void end()
```

センサーの取得を開始します。センサーの値を読み出すまで約5ms待ち時間が必要です。

`end()`には対応しません。

### process\_ev()

```cpp
void process_ev(uint32_t arg1, uint32_t arg2 = 0)
```

待ち時間処理のセンサーの場合は`arg1`に`E_EVENT_TICK_TIMER`または`E_EVENT_START_UP`を与え時間の経過を知らせます。このメソッド呼出し後に、必要な時間が経過していればavailableになり、センサー値の読み出しが可能になります。

### available()

```cpp
bool available()
```

センサーが読み出し条件を満足したときに`true`を返します。

### probe()

```cpp
bool probe()
```

センサーが接続されているときに`true`を返します。


# LTR-308ALS - 照度センサー

I2Cバスを利用した照度センサーです。

{% hint style="warning" %}
ボードビヘイビア `<PAL_AMB>` を読み込んだ時のみ使用可能です。`begin()`以外の共通メソッドの手続きはボードビヘイビア中で実行されています。
{% endhint %}

## 処理の流れ

1. `Wire.begin()`: バスの初期化
2. `.begin()`: センサーの動作開始
3. 時間待ち50ms
4. `.get_luminance()`: 値の読み出し

## 動作に必要な手続き

### Wireバス

`.begin()`メソッド呼び出し前に`Wire.begin()`によりWireが動作状態にしておきます。

### スリープ復帰時の手続き

スリープ直前もWireバスが動作状態にしておきます（スリープ復帰後自動でWireを回復します）。

## メソッド

### get\_luminance()

```cpp
uint32_t get_luminance()
```

照度\[lx]を整数値で返します。

エラーの時は`-1`が返ります。

## 共通メソッド

### setup()

```cpp
void setup() 
```

センサー用のメモリ領域の確保や初期化を行います。

### begin(), end()

```cpp
void begin()
void end()
```

センサーの取得を開始します。センサーの値を読み出すまで約50ms待ち時間が必要です。

`end()`には対応しません。

### process\_ev()

```cpp
void process_ev(uint32_t arg1, uint32_t arg2 = 0)
```

待ち時間処理のセンサーの場合は`arg1`に`E_EVENT_TICK_TIMER`または`E_EVENT_START_UP`を与え時間の経過を知らせます。このメソッド呼出し後に、必要な時間が経過していればavailableになり、センサー値の読み出しが可能になります。

### available()

```cpp
bool available()
```

センサーが読み出し条件を満足したときに`true`を返します。

### probe()

```cpp
bool probe()
```

センサーが接続されているときに`true`を返します。


# MC3630 - 加速度センサー

SPIバスを用いた加速度センサーです。

{% hint style="warning" %}
ボードビヘイビア `<PAL_MOT>` を読み込んだ時のみ使用可能です。`begin(), available()`以外の共通メソッドの手続きはボードビヘイビア中で実行されています。
{% endhint %}

## 動作の流れ

1. `.begin()`: センサーの動作開始
2. `PIN_SNS_INT`割り込み または `available()`: FIFOキューが規定数に達する
3. `.get_que()`: FIFOキューからのデータを取得する

## 動作に必要な手続き

### SPI バス

特にありません。

### スリープ手続き

PIN\_SNS\_INT割り込みによる起床を行うため、スリープ前に以下の設定行います。

```cpp
pinMode(PAL_MOT::PIN_SNS_INT, WAKE_FALLING);
```

### スリープ復帰時の手続き

`.wakeup()`メソッドの呼び出しが必要です。この処理は`<PAL_MOT>`ボードビヘイビア中で実行されています。

{% hint style="warning" %}
半導体内部のFIFOキューが一杯になっても読み出さなかった場合は、データ取得は終了し、新たな値は格納されません。
{% endhint %}

## データ構造

各サンプルは[`axis_xyzt`](/v0.1.3/api-reference/classes/axis_xyzt)構造体を要素とするキュー[`smplque`](/v0.1.3/api-reference/classes/smplque)に格納されます。メンバーx,y,zはそれぞれX,Y,Z軸に対応します。

```cpp
struct axis_xyzt {
  int16_t x;
  int16_t y;
  int16_t z;
  uint16_t t;
};
```

各軸の値は1Gを1000とした値として格納されます。`t`はサンプルの番号で`0`から順番にサンプルごとに割り振られます。

## メソッド

### read()

```cpp
uint8_t read()
```

半導体のFIFOキューからデータを読み出します。読みだしたバイト数が戻りますが`.get_que()`で参照するキューのサイズに格納されるデータ数を読み出すようにしてください。

{% hint style="warning" %}
スリープ復帰後に`<PAL_MOT>`では`read()`が行われます。
{% endhint %}

### get\_que()

```cpp
smplque<axis_xyzt>& get_que()
```

加速度のサンプルを取得します。キューは[`axis_xyzt`](/v0.1.3/api-reference/classes/axis_xyzt)を要素とした[`smplque`](/v0.1.3/api-reference/classes/smplque)です。availableになってから速やかにキューを空にする必要があります。

## 共通メソッド

### setup()

```cpp
void setup() 
```

このセンサーでは`setup()`を使用しません。

### begin(), end()

```cpp
void begin(uint32_t conf)
void end()
```

`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  |

{% hint style="warning" %}
非公式設定はMC3630のデータシートに記述がないもので**設定時の動作は未定義**となります。お客様のほうでの動作確認の上利用下さい。非公式設定にかかわる問題やご質問について弊社サポートでは対応いたしかねます。
{% endhint %}

| 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()

```cpp
void process_ev(uint32_t arg1, uint32_t arg2 = 0)
```

このセンサーでは`process_ev()`を使用しません。

### available()

```cpp
bool available()
```

センサーにデータが読み出され内部のキューにデータが保存されていると`true`を戻します。

### probe()

```cpp
bool probe()
```

このセンサーでは`probe()`は使用できません。

### wakeup()

```cpp
void wakeup()
```

スリープ復帰後のSPIバスの再初期化を行い、加速度データを読み出します。


# ネットワーク

Network behaviors

ネットワーク ビヘイビアは、IEEE802.15.4のMAC層のパケットの送受信に対して、アドレス定義、配送制御などを行います。

* [\<NWK\_SIMPLE>](/v0.1.3/networks/nwk_simple)


# シンプル中継ネット \<NWK\_SIMPLE>

シンプルな中継ネットワークを実装したネットワークビヘイビアです。

```cpp
auto&& nwksmpl = the_twelite.network.use<NWK_SIMPLE>();
nwksmpl << NWK_SIMPLE::logical_id(0xFE)
        << NWK_SIMPLE::repeat_max(3);
```

上記はネットワークの利用宣言と設定例です。詳細は後述しますが、ネットワークのアドレスについての考え方など基本的な内容をまず解説します。

{% hint style="info" %}
実際の利用例は[サンプルアクトの解説](/v0.1.3/act_samples)をご覧ください。
{% endhint %}

このネットワークでの各無線局は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番台のパケットはしばらく前に送信されたと考えられる)続き番号はタイムアウトとして重複対象から除外します。

重複チェッカでの考慮すべきことは以下になります。

{% hint style="info" %}
一般的な使用条件（例：中継段数が３以下で１秒あたりのパケットも１０局未満から到達）では、特別な考慮は不要です。
{% endhint %}

* チェック可能な要素数(数を増やせばメモリ消費とチェックのための処理時間が増える)
* タイムアウト時間の設定

デフォルトではタイムアウトは１秒で、チェックする無線局の数は16です。つまり中継パケットがまわりまわって１秒以上経過した場合、重複パケットとみなされななくなります。また短期的に16を超える無線局からのパケットが到達した場合、超過した無線局については重複チェックが出来なくなります。

中継段数や段数が少なくとも中継局の数が多い場合、再送を非常に長い間隔で行う場合は、設定を考慮すべき場合もあります。

## 宣言・登録

ネットワークビヘイビア`<NWK_SIMPLE>`を利用例を挙げます。

```cpp
#include <TWELITE>
#include <NWK_SIMPLE>

void setup() {
  ...
  
  auto&& nwk = the_twelite.network.use<NWK_SIMPLE>();
}
```

２行目で`<NWK_SIMPLE>`の定義情報をインクルードします。７行目で`the_twelite`に`<NWK_SIMPLE>`を登録します。

## 設定

`<NWK_SIMPLE>`登録後に設定を行います。

```cpp
void setup() {
  auto&& nwksmpl = the_twelite.network.use<NWK_SIMPLE>();
  nwksmpl << NWK_SIMPLE::logical_id(0xFE);
}
```

設定は`<<`演算子で行います。

### `<<`演算子 (設定)

オブジェクト`the_twelite`の初期設定を行うために`<<`演算子を用います。

以下に挙げる設定用のクラスオブジェクトを入力とし、設定をしなければデフォルト値が適用されます。

#### NWK\_SIMPLE::logical\_id(uint8\_t id)

パラメータ`id`に指定した論理デバイスIDを設定します。デフォルトは`0xFE`(ID未設定子機)です。

#### NWK\_SIMPLE::repeat\_max(uint8\_t val)

パラメータ`val`に指定した回数を最大中継回数とします。デフォルトは`2`です。

中継をさせたくない場合は`0`を指定します。

{% hint style="info" %}
[受信回路を動作させない設定](/v0.1.3/api-reference/predefined_objs/the_twelite#twenet-rx_when_idle-uint-8-_t-benable)をした場合、TWELITE無線マイコンが無線パケットを受信できないため、必然的に、中継動作は行われなくなります。
{% endhint %}

#### NWK\_SIMPLE::dup\_check(uint8\_t maxnodes, uint16\_t timeout\_ms, uint8\_t tickscale)

重複パケットの検出アルゴリズムのパラメータです。

{% hint style="warning" %}
中継段数が多かったり、パケットの再送間隔が長い場合は考慮が必要ですが、**一般的な利用での設定変更は必要ありません。**
{% endhint %}

* `maxnodes`は履歴を保持するため無線局（ノード）の数です。ノード数を少なく設定した場合、短期間に設定以上のノードからのパケットが来た場合、重複除外できないノードが出てきます。重複除外できない場合、受信時に複数回データが表示される、必要以上に再中継してしまうといった問題が出ます。デフォルトは`16`です。１ノード当たり21バイトメモリを消費します。
* `timeout_ms`は履歴を抹消するまでのタイムアウト時間\[ms]です。タイムアウトは続き番号のブロック単位で管理されていて、ブロック単位でタイムアウト処理が行われます。デフォルトは`1000`\[ms]です。
* `tickscale`はタイムアウトを管理するための時間単位で`2^tickscale`\[ms]となります。時間は7bitで管理されるため、`127*(2^tickscale) > timeout_ms`になるように設定します。デフォルトは`5`(32ms)です。

## メソッド

### prepare\_tx\_packet()

```cpp
// 型名はpacket_tx_nwk_simple<NWK_SIMPLE>ですがauto&&と記載します。
auto&&  preare_tx_packet()

//例
if (auto&& pkt = 
  the_twelite.network.use<NWK_SIMPLE>().prepare_tx_packet()) {
  ...
  pkt.transmit();
}
```

送信オブジェクトを取得します。オブジェクトは[`packet_tx`](/v0.1.3/api-reference/classes/packet_tx)の派生クラスになります。このオブジェクトに送信アドレスやペイロードを格納し`.transmit()`メソッドで送信を行います。

このオブジェクトには`bool`演算子が定義されています。オブジェクト生成時にTWENETが送信要求を受け付けられない場合は`false`を返します。

## 送信オブジェクト

`.prepare_tx_packet()`メソッドにて取得した送信オブジェクトのメソッドです。

### bool演算子

```cpp
operator bool()

// 例
if (auto&& pkt = 
  the_twelite.network.use<NWK_SIMPLE>().prepare_tx_packet()) {
```

オブジェクト生成時にTWENETが送信要求を受け付けられない場合は`false`を返します。

### transmit()

```cpp
MWX_APIRET transmit()

// 例
uint8_t txid;

if (auto&& pkt = 
  the_twelite.network.use<NWK_SIMPLE>().prepare_tx_packet()) {
  
  ...
  
  MWX_APIRET ret = pkt.transmit();
  if (ret) {
    txid = pkt.get_value();
  }
}
```

パケットの送信処理を行います。`MWX_APIRET`が`true`の場合に送信要求が成功ですが、この要求時点では送信処理が始まりません。

パケットの送信IDは[`MWX_APIRET`](/v0.1.3/api-reference/classes/mwx_apiret)の`.get_value()`で得られる値部に格納されます。[`the_twelite.x_status.is_complete()`](/v0.1.3/api-reference/predefined_objs/the_twelite#tx_status)または[`transmit_complete()`](/v0.1.3/api-reference/behavior#transmit_complete)より送信完了を確認できます。


# The MWX Library (v0.1.5)

Mono Wireless C++ Library for TWELITE.

{% hint style="success" %}
[資料の取り扱いについて](https://twelite.gitbook.io/general/about_documents)をご参照ください。  お気付きの点がありましたら、当サポート窓口にご連絡いただければ幸いです。
{% endhint %}

{% hint style="warning" %}
このページには 0.1.5 (MWSDK2020\_08\_UNOFFICIAL) の情報が含まれます。
{% endhint %}

MWX ライブラリは、TWELITE 無線モジュールのコード表記を簡素化することを目的としています。MWXで作成されたプログラムを**アクト** act と呼びます。アクトにはループによる記述と、イベントによる記述（**ビヘイビア** behavior と呼びます）の二種類があります。

本ページではアクトの主な特徴を紹介します。

* ループによる記述 (`setup(), loop()`)。小規模な機能を記述するのに向いています。

```cpp
#include <TWELITE>
const uint8_t PIN_LED = 5;

void setup() {
  pinMode(PIN_LED, OUTPUT);
}

void loop() {
  if (TickTimer.available()) {
    uint32 t_now = millis();
    
    // blink LED every 1024ms
    digitalWrite(PIN_LED, (t_now >> 10) & 1 ? HIGH : LOW);
  }
}	
```

* イベントドリブンのアプリケーション記述。各種イベント・割り込みハンドラの定義、アプリケーションの複雑な振る舞いを記述するのに向いたステートマシンをクラス内に定義して見通しの良いコードを記述できます。この記述を**ビヘイビア**と呼びます。

```cpp
// myApp.hpp
...
class myApp : MWX_APPDEFS_CRTP(myApp) {
...
  void loop() {
    // main loop
  }
  
  void receive(mwx::packet_rx& rx) {
    // on receive
  }
};

// myApp.cpp
...
MWX_DIO_EVENT(12, uint32_t arg) {
		// on event from DIO12
}
```

* ペリフェラルの手続きを簡素化。よく使われる UART, I2C, SPI, ADC, DIO, タイマー, パルスカウンタを取り扱うクラスオブジェクトを定義しています。

```cpp
void loop() {
  while(Serial.available() { 
    auto x = Serial.read(); ... } // serial message
  if (Analogue.available() {
    auto x = Analogue.read(...); } // adc values
  if (Buttons.available() { 
    Buttons.read(...); } // DIO changes
  if (the_twelite.receiver.available()) { 
    auto&& rx = the_twelite.receiver.read(); } // on rx packet
}
```

* シンプルな中継ネットワークを定義。この中継ネットワークは TWELITE 標準アプリケーションと同等の実装で、個体アドレスの管理は 8bit の論理IDで行うこと、ネットワーク構築のための通信を行わないため電源投入後すぐにネットワーク宛に無線パケットを送ることができる点が特徴です。

```cpp
#include <TWELITE>
#include <NWK_SIMPLE>

void setup() {
  ...
  auto&& nwksmpl = the_twelite.network.use<NWK_SIMPLE>();
	nwksmpl << NWK_SIMPLE::logical_id(0xFE) 
	           // set Logical ID. (0xFE means a child device with no ID)
	        << NWK_SIMPLE::repeat_max(3);
	           // can repeat a packet up to three times.
}

void loop() {
  ...
  vTransmit();
  ...
}

void vTransmit() {
  if (auto&& pkt =
    the_twelite.network.use<NWK_SIMPLE>().prepare_tx_packet(); 
  pkt << tx_addr(0x00)  // to parent 
	  	<< tx_retry(0x3); // set retry
	
	pack_bytes(pkt.get_payload() // prepare payload data
	    , uint8_t(0x01)
	    , uint16_t(analogRead(PIN_ANALOGUE::A1))
	    , uint16_t(analogRead_mv(PIN_ANALOGUE::VCC)));
	
	pkt.transmit(); // transmit!
}
```

{% hint style="info" %}
TWELITE 標準アプリケーションとのパケットの相互通信はできませんが、以下の点で自由度が上がっています。

* 論理IDは 0 が親機である点は同様ですが、子機アドレスとして 0x01..0xEF を利用できるため、識別数を 200 以上とすることができます。

* 原則３回までとしていた中継回数について、最大数を64回までを設定できるようにしています。（※ パケットが遠回りして一定時間経過後に戻ってきた場合、重複パケットの管理テーブルがクリアされ中継済みのパケットであっても、再中継が発生する場合があります。中継回数を大きくする設定は注意してください）
  {% endhint %}

* PAL や MONOSTICK 向けのボード定義。ボード上のセンサーなどを容易に取り扱えます。

```cpp
#include <TWELITE>
#include <PAL_AMB> // include the board support of PAL_AMB

void setup() {
	auto&& brd = the_twelite.board.use<PAL_AMB>(); // use PAL AMB
	uint8_t u8dip = brd.get_DIP_SW();   // check DIP s/w status
	brd.set_led(LED_TIMER::BLINK, 100); // LED switchs on/off every 100ms
	...
	
	// start capture of sensors
	brd.sns_SHTC3.begin();
}

void loop() {
	if (TickTime.available()) { // check every ms
		auto&& brd = the_twelite.board.use<PAL_AMB>();

		if (brd.sns_LTR308ALS.available()) {
		  Serial << brd.sns_SHTC3..get_temp();
		} else {
		  // notify sensor that 1ms passed.
			brd.sns_SHTC3.process_ev(E_EVENT_TICK_TIMER);
		}
	}
}
```


# 改版履歴

{% hint style="info" %}
軽微な修正にあたるものは、本改版履歴には記載を行わずGitHub上の改版のみとなります。必要に応じて修正を引用ください。
{% endhint %}

## 更新方法

TWELITE STAGE の配布パッケージリリース後の修正・追加分などはGitHubレポジトリに格納しております。必要に応じて配布パッケージの位置を差し替えて利用いただくようお願いいたします。

MWSDKの他の更新が必要になる場合があります。更新時のリリース記述を参照してください。MWSDKの更新については[こちら](https://sdk.twelite.info/latest)を参照ください。

### MWXライブラリコードの更新方法

ライブラリのソースコードは GitHub (<https://github.com/monowireless/mwx>)にて公開しています。ライブラリのソースコードの差し替えは、以下の手順で行ってください。

1. 各リリースのリンクよりGitのクローンを行うか zip 形式でソースコードをダウンロードします。
2. 以下のディレクトリの内容を差し替えます。

```
.../MWSTAGE/              --- TWELITE STAGE 配布ディレクトリ
        .../MWSDK         --- MWSDKディレクトリ
              .../TWENET/current/src/mwx <--　このディレクトリを差し替える
```

## リリース前の更新

> <https://github.com/monowireless/mwx/wiki>

リリース前の更新については上記に掲載する場合があります。

## 0.1.5 - 2020-08-05

| ライブラリ名      | 依存バージョン                                                                 |
| ----------- | ----------------------------------------------------------------------- |
| mwx         | [0.1.5](https://github.com/monowireless/mwx/releases/tag/0.1.5)         |
| twesettings | [0.2.5](https://github.com/monowireless/twesettings/releases/tag/0.2.5) |
| TWENET C    | 1.3.4                                                                   |

#### 一括ダウンロード

> [MWSDK2020\_08\_UNOFFICIAL](https://github.com/monowireless/MWSDK_COMMON_SNAP/releases/tag/MWSDK2020_08_UNOFFICIAL) ([README.md](https://github.com/monowireless/MWSDK_COMMON_SNAP/blob/master/README.md))

#### 主な改定内容

* [設定ビヘイビア（インタラクティブモード機能）](/v0.1.5/settings)を追加
* チャネルマネージャ [`chmgr`](/v0.1.5/api-reference/predefined_objs/the_twelite#twenet-chmgr-uint-8-_t-ch1-18-uint-8-_t-ch2-0-uint-8-_t-ch3-0) の実装

## 0.1.4 - 2020-07-29 (MWSDK2020\_07\_UNOFFICIAL)

| ライブラリ名      | 依存バージョン                                                                 |
| ----------- | ----------------------------------------------------------------------- |
| mwx         | [0.1.4](https://github.com/monowireless/mwx/releases/tag/0.1.4)         |
| twesettings | [0.2.4](https://github.com/monowireless/twesettings/releases/tag/0.2.4) |
| TWENET C    | 1.3.3                                                                   |

####

#### 一括ダウンロード

> [MWSDK2020\_07\_UNOFFICIAL](https://github.com/monowireless/MWSDK_COMMON_SNAP/releases/tag/MWSDK2020_07_UNOFFICIAL) ([README.md](https://github.com/monowireless/MWSDK_COMMON_SNAP/blob/master/README.md))

#### 主な改定内容

* `delayMilliseconds()` の追加
* `digitalReadBitmap()` の追加
* `delay()` の精度向上
* `Serial1` インスタンスが定義されていない問題を修正
* `Analogue`の割り込みハンドラが呼び出されない問題を修正

## 0.1.3 - 2020-05-29

MWSDK2020\_05 に対応

* 重複チェッカ duplicate\_checker の初期化等に不備があり期待通りの除去を行っていなかった
* format() の実装を機種依存の少ないものとした。また、引数を最大８までとした。64bit引数が含まれる場合は引数の数は制限される。

> <https://github.com/monowireless/mwx/releases/tag/0.1.3>

{% hint style="warning" %}
修正は MWSDK2020\_05 を前提としています。

本修正については、更新を推奨します。
{% endhint %}

## 0.1.2 - 2020-04-24

MWSDK2020\_04 に対応

* Timer0..4の初期化の問題を修正
* mwx::format() の内部処理を変更
* インタラクティブモード対応のための実験的なコードの追加

> <https://github.com/monowireless/mwx/releases/tag/0.1.2>

{% hint style="warning" %}
本修正は MWSDK2020\_04 を前提としています。

本修正については、更新を推奨します。
{% endhint %}

## 0.1.1 - 2020-02-28

パケット内の中継フラグの扱いについての問題を修正

> <https://github.com/monowireless/mwx/releases/tag/0.1.1>

{% hint style="warning" %}
本修正については、更新を推奨します。
{% endhint %}

## 0.1.0 - 2019-12-23

初版リリース (SDL 2019/12月号収録)

> <https://github.com/monowireless/mwx/releases/tag/0.1.0>


# MWXライブラリについて

MWX ライブラリは、TWELITE モジュールのプログラムをより容易にかつ拡張性を高めるために設計されています。これまでMWSDKで利用していた TWENET C ライブラリを基本とし、MWXライブラリはアプリケーション開発層のライブラリとして開発しております。

```
   Act (USER APPs)... 
+-----------------------+
| MWX C++ LIB           |
+---------------+       |
| TWENET C LIB  |       |
+------------+----------+
| MAC LAYER  | AHI APIs |
+-----------------------+
| TWELITE HARDWARE      |
+-----------------------+
```

MWX ライブラリの名称は Mono Wireless C++ Library for TWELITE です。MW は MonoWireless から、また C++ -> CXX -> double X -> WX。この MW と WX を重ねて MWX になりました。

このライブラリを用いて記述したコードを「**アクト(act)**」と呼びます。

###

### 表記等について

本解説での表記について記載します。

#### auto&&

ユニバーサル参照と呼ばれ、標準ライブラリなどで良く用いられます。当ライブラリでもほとんどの場合`auto&&` と記載します。

{% hint style="info" %}
`auto` はC言語ではローカル変数（自動変数）を宣言する際のキーワードとなっていますが、ここでは型推論により宣言を行う意味です。C++のテンプレート構文では非常に煩雑な型名になることが多く、同時に型名を明示的に記述しなくても実装できる場面で便利な記法です。

下記の例では、`v`の型に対する最大値最小値を発見する標準ライブラリのアルゴリズム `std::minmax_element` を用いた例で、結果の戻り値を `auto`により宣言しています。この場合、autoで推論された型は`std::pair<int, int>`になります。

```cpp
#include <algorithm>
int v[] = { 1, 3, -1, 5, 10 };
auto&& result = std::minmax_element(std::begin(v), std::end(v));
Serial << "min=" << int(result.first)
       << ",max=" << int(result.second);
```

`auto &&`の`&&`ついては、厳格な意味合いは専門書籍などを紐解いていただく必要がありますが、ここでは「戻りが参照型（C言語でいうポインタ渡しに近い）であっても値であっても、気にせず宣言できる」とお考え下さい。
{% endhint %}

#### 名前空間について

`namespace, inline namespace, using` を用いて、名前の再定義などを行っています。解説中でも一部省略して記載しています。

### 制限事項 (TWENET)

MWXライブラリは、下層に位置する各ライブラリ・機能（TWNET Cライブラリでの機能、また半導体ベンダが提供するマイコン・ペリフェラル機能、IEEE802.15.4の機能）について、その全てに対応する目的では開発しておりません。

### 制限事項 (C++の利用)

MWXライブラリはC++言語で記述されておりアクトの記述においても C++ での記述を行うことになります。しかしながらC++言語であってもすべての機能が使えるわけではありません。特に以下の点に注意してください。

* `new, new[]` 演算子でのメモリ確保は行えますが、確保したメモリを破棄することはできません。C++ライブラリで動的メモリ確保をするものは殆どが事実上利用不可能です。
* グローバルオブジェクトのコンストラクタが呼び出されません。\
  参考：必要な場合は、初期化関数(`setup()`) で `new ((void*)&obj_global) class_foo();` のように初期化することでコンストラクタの呼び出しを含めた初期化を行えます。
* 例外 `exception`が使用できません。
* 仮想関数 `virtual` が使用できません。
* 上記の制約があるためSTLなどC++標準ライブラリの一部のみの利用となります。

※ 当社で把握しているものについての記載です。

{% hint style="warning" %}
標準ライブラリについては利用可否、また利用できそうなものについての包括的な検証は行っておりません。動作の不都合が確認できた場合は、別の方法で実装するようにしてください。
{% endhint %}

### ライブラリのソースコードについて

ソースコードは以下から参照できます。

* `{MWSDKインストールディレクトリ}/TWENET/current/src/mwx`
* [`https://github.com/monowireless/mwx`](https://github.com/monowireless/mwx)




---

[Next Page](/llms-full.txt/1)

