# Slp\_Wk\_and\_Tx

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

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

{% hint style="success" %}
このアクトの解説の前に[BRD\_APPTWELITEの解説](/v0.1.5/act_samples/brd_apptwelite.md)をご覧いただくことを推奨します。
{% 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をデータ部に格納します。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://mwx.twelite.info/v0.1.5/act_samples/slp_wk_and_tx.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
