LoRaモジュールE220-900T22S（JP）のWakeOnRadioを使ってみる

# はじめに
クレアリンクテクノロジー [E220-900T22S（JP）](https://dragon-torch.tech/cat-components/rf-modules/e220-900t22s-jp/)
SEMTEC社LoRaチップ「LLCC68」 搭載LoRaモジュール

このLoRaモジュールにはWOR（Wake on Radio）機能があり省電力なIoTに役立つので使ってみようと思います

# WORとは
>WOR（Wake on Radio）対応で、省電力待機状態のモジュールをワイヤレスプリアンブル信号によって起こして受信させることができ、それによってデバイスへ割り込みを発生させるなどし、機器全体の省電力制御をワイヤレスで実現することが可能です。

https://dragon-torch.tech/cat-components/rf-modules/e220-900t22s-jp/

![スクリーンショット 2024-02-02 224901.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261581/30133b2b-62e8-99bb-eb1d-bd375ce95c96.png)

E220-900T22S（JP）データシート
https://dragon-torch.tech/wp-content/uploads/2023/12/data_sheet_Rev1.5.pdf

AUXピンがLOWになることでマイコンを起動させます

こちらのライブラリのexampleを参考に[Seeed　XiaoESP32C3](https://www.switch-science.com/products/8348)を使ってみました（ピン数は少なめですが1000円以下でコンパクトでセンサー用にばらまくにはおすすめのマイコンボードです）

https://github.com/xreef/LoRa_E32_Series_Library/blob/master/examples/manageWakeUpOfMicrocontroller/esp32_e32_wake_up_from_WOR_deep_sleep/esp32_e32_wake_up_from_WOR_deep_sleep.ino
なおこのLoRa_E32_Series_LibraryはE220-900T22S（JP）に今のところ非対応です


# XiaoESP32C3のピン配置
![755a3b84-f4b8-41fe-8aec-e1054749151e.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261581/5d6c1d7e-7912-f0ae-ade6-19ef44109d9b.png)


![スクリーンショット 2024-02-03 085147.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261581/ffb69308-ac39-25e2-0e4a-474858d57fdf.png)

https://www.espressif.com/sites/default/files/documentation/esp32-c3_datasheet_en.pdf

https://docs.espressif.com/projects/esp-idf/en/v5.0/esp32c3/api-reference/system/sleep_modes.html#configuring-ios

ここでDeepSleepからGPIOWakeupに使えるピンはVDD3P3_RTCによって電力供給されるパッド **GPIO0 ～ 5** とあり
このボードではD0~D3の4本のみでこれをLoRaモジュールの **AUX** ピン用に使います


### 主に使う関数
- **gpio_hold_en**(gpio_num_t gpio_num)
https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/api-reference/peripherals/gpio.html?highlight=gpio_hold_en#_CPPv412gpio_hold_en10gpio_num_t
- **gpio_hold_dis**(gpio_num_t gpio_num)
https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/api-reference/peripherals/gpio.html?highlight=gpio_hold_en#_CPPv413gpio_hold_dis10gpio_num_t
-  **gpio_deep_sleep_hold_en**(void)
https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/api-reference/peripherals/gpio.html?highlight=gpio_deep_sleep_hold_en#_CPPv423gpio_deep_sleep_hold_env

- **esp_deep_sleep_enable_gpio_wakeup**(uint64_t gpio_pin_mask, esp_deepsleep_gpio_wake_up_mode_t mode)
https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/api-reference/system/sleep_modes.html?highlight=esp_deep_sleep_enable_gpio_wakeup#_CPPv433esp_deep_sleep_enable_gpio_wakeup8uint64_t33esp_deepsleep_gpio_wake_up_mode_t
- **esp_deep_sleep_start**(void)
https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/api-reference/system/sleep_modes.html?highlight=esp_deep_sleep_start#_CPPv421esp_light_sleep_startv

(注)EPS32やESP32S3とESP32C3とでいくつか関数の名前や仕様が異なる場合があります　今回はESP32C3についての関数です

# LoRaモジュールの設定
- 送信‐受信でUART Serial Port Rate、Air Data Rate、SF(拡散率）、BW（帯域幅）、周波数CH、WOR サイクルを同じに設定します
また注意点として
>⚫ 周波数チャンネルが 923.5 MHz〜928.0MHz の範囲、すなわち
BW:125kHz で CH:15-37 を選択した場合、
BW:250kHz で CH:14-36 を選択した場合、
および BW:500kHz で CH:13-30 を選択した場合
➢ 送信時間が 400ms 制限の規定があるため、レジスタでの設定値によらずサブパケッ
ト長が 32byte に変更されます。また、プリアンブル送信にかかる時間が制限を超え
てしまうため、WOR モードは利用しないでください。

# 省電力ゲートウェイ

乾電池駆動のセンサー子機とソーラー電池駆動のLTEゲートウェイを以下のような構成で考えてみました
例えば子機からの送信が数秒/回で送信間隔が1時間のとき親機側は常時受信を待ち受けてる必要がなく大幅に省電力化できます



##  親機が定期的に子機を起こして終わったらDeepSleep
・NormalMode :　送信:可　受信：可

・WORSendingMode　送信：WORモード　受信：可

・WORReceivingMode　送信：不可　受信：WORモード

・ConfigurationMode  設定変更モード/Deepsleepモード:　送信:不可　受信：不可
```mermaid
sequenceDiagram
    親機　-->>+ 親機 : TimerWakeUp
    親機　-->>+ 親機 : LTE 接続 
    親機　-->> 親機 : WORSendingMode
    親機 ->>+ 子機1 : WOR(WakeOnRadio)で子機1を起こす
    親機 ->>+ 子機2 : WOR(WakeOnRadio)で子機2を起こす
    親機 ->>+ 子機3 : WOR(WakeOnRadio)で子機3を起こす
    子機1 -->>+ 子機1 : GPIOWakeUp
    子機2 -->>+ 子機2 : GPIOWakeUp
    子機2 -->> 子機2 : sleep 10
    子機3 -->>+ 子機3 : GPIOWakeUp
    子機3 -->> 子機3 : sleep 20

    子機1 -->>子機1 : NormalMode
    子機1 ->> 親機 : センサー値送信 
    子機1 -->>子機1 : WORReceivingMode
    子機1 -->>- 子機1 : MCU DeepSleep 
    親機 -->> 親機 : LTE送信
    
    子機2 -->> 子機2 : NormalMode
    子機2 ->> 親機 : センサー値送信 
    子機2 -->> 子機2 : WORReceivingMode
    子機2 -->>- 子機2 : MCU DeepSleep 
    親機 -->> 親機 : LTE送信

    子機3 -->> 子機3 :NormalMode
    子機3 ->> 親機 : センサー値送信 
    子機3 -->> 子機3 : WORReceivingMode
    子機3 -->>- 子機3 : MCU DeepSleep 
    親機 -->> 親機 : LTE送信
    親機　-->>- 親機　: LTE切断
    親機　-->> 親機　: ConfigurationMode
    親機　-->>- 親機　: DeepSleep



```

```cpp:sensorWakeUp.ino
#include <Arduino.h>
#define SerialMon Serial
#define SerialLoRa Serial1

// E220-900T22S(JP)へのピンアサイン
#define LoRa_ModeSettingPin_M0 GPIO_NUM_2//D0 
#define LoRa_ModeSettingPin_M1 GPIO_NUM_3//D1
#define LoRa_Rx_ESP_TxPin GPIO_NUM_21//D6
#define LoRa_Tx_ESP_RxPin GPIO_NUM_20//D7
#define LoRa_AUXPin GPIO_NUM_4//D2

// E220-900T22S(JP)のbaud rate
#define LoRa_BaudRate 9600
int16_t senserID = 0x0001;
RTC_DATA_ATTR uint16_t bootCount = 0;
esp_sleep_source_t  wakeup_reason;

uint8_t conf[] ={0xc0, 0x00, 0x08, 
                senserID >> 8, //ADDH
                senserID & 0xff, //ADDL
                0b01110000, // baud_rate 9600 bps  SF:9 BW:125
                0b11100000, //subpacket_size 32, rssi_ambient_noise_flag on, transmitting_power 13 dBm
                0x09, //own_channel
                0b11000111, //RSSI on ,fix mode,wor_cycle 4000 ms
                0x00, //CRYPT
                0x00};
struct  msgStruct{ 
  char targetAdressH = 0x00;//GateWay adress 0x0000
  char targetAdressL = 0x00;
  char targetChannel = 0x09;
  uint16_t myadress = senserID;
  uint16_t bootcount ;
} msg;


  /**
   * @brief ノーマルモード(M0=0,M1=0)へ移行する
   */
void SwitchToNormalMode(void){
  digitalWrite(LoRa_ModeSettingPin_M0, 0);
  digitalWrite(LoRa_ModeSettingPin_M1, 0);
  delay(100);
}

  /**
   * @brief WOR受信モード(M0=0,M1=1)へ移行する
   */
  void SwitchToWORReceivingMode(void){
  digitalWrite(LoRa_ModeSettingPin_M0, 0);
  digitalWrite(LoRa_ModeSettingPin_M1, 1);
  delay(100);
}

  /**
   * @brief コンフィグ/sleepモード(M0=1,M1=1)へ移行する
   */
void SwitchToConfigurationMode(void){
  digitalWrite(LoRa_ModeSettingPin_M0, 1);
  digitalWrite(LoRa_ModeSettingPin_M1, 1);
  delay(100);
}

void IRAM_ATTR deep_sleep(){
    SwitchToWORReceivingMode();
	delay(100);
    Serial.println();
    gpio_hold_en(LoRa_ModeSettingPin_M0);
    gpio_hold_en(LoRa_ModeSettingPin_M1);
    esp_deep_sleep_enable_gpio_wakeup(BIT(LoRa_AUXPin), ESP_GPIO_WAKEUP_GPIO_LOW);
    gpio_deep_sleep_hold_en();
      //Go to sleep now
    Serial.println("Going to sleep now");
    delay(1000);
    esp_deep_sleep_start();
}
void wakeup_cause_print(esp_sleep_source_t  wakeup_reason) {
  switch (wakeup_reason) {
    case 0: Serial.println("Wakeup caused by ESP_SLEEP_WAKEUP_UNDEFINED"); break;
    case 1: Serial.println("Wakeup caused by ESP_SLEEP_WAKEUP_ALL"); break;
    case 2: Serial.println("Wakeup caused by ESP_SLEEP_WAKEUP_EXT0"); break;
    case 3: Serial.println("Wakeup caused by ESP_SLEEP_WAKEUP_EXT1"); break;
    case 4: Serial.println("Wakeup caused by ESP_SLEEP_WAKEUP_TIMER"); break;
    case 5: Serial.println("Wakeup caused by ESP_SLEEP_WAKEUP_TOUCHPAD"); break;
    case 6: Serial.println("Wakeup caused by ESP_SLEEP_WAKEUP_ULP"); break;
    case 7: Serial.println("Wakeup caused by ESP_SLEEP_WAKEUP_GPIO"); break;
    case 8: Serial.println("Wakeup caused by ESP_SLEEP_WAKEUP_UART"); break;
    case 9: Serial.println("Wakeup caused by ESP_SLEEP_WAKEUP_WIFI"); break;
    case 10: Serial.println("Wakeup caused by ESP_SLEEP_WAKEUP_COCPU"); break;
    case 11: Serial.println("Wakeup caused by ESP_SLEEP_WAKEUP_COCPU_TRAP_TRIG"); break;
    case 12: Serial.println("Wakeup caused by ESP_SLEEP_WAKEUP_BT"); break;
    default: Serial.println("Wakeup was not caused by deep sleep"); break;
  }
}

void setup() {
  pinMode(LoRa_ModeSettingPin_M0, OUTPUT);
  pinMode(LoRa_ModeSettingPin_M1, OUTPUT);
  SerialMon.begin(9600);
  delay(500);
  // E220-900T22S(JP)へのLoRa初期設定
  SerialLoRa.end(); 
  delay(1000); 
  SerialLoRa.begin(LoRa_BaudRate, SERIAL_8N1, LoRa_Tx_ESP_RxPin,LoRa_Rx_ESP_TxPin);
  wakeup_reason = esp_sleep_get_wakeup_cause();
  wakeup_cause_print(wakeup_reason);
  
  if (ESP_SLEEP_WAKEUP_GPIO  == wakeup_reason) {
    Serial.println("Waked up from external GPIO!");
    gpio_hold_dis(LoRa_ModeSettingPin_M0);
    gpio_hold_dis(LoRa_ModeSettingPin_M1);
    gpio_deep_sleep_hold_dis();
    SwitchToNormalMode();
    delay(100);
  }else{
    Serial.println("Waked up from nomal power on!");
    SerialMon.printf("\n sensorID: %d\n",senserID);
    SwitchToConfigurationMode();
    while(!digitalRead(LoRa_AUXPin)){}
    SerialMon.printf("I send conf\r\n");
    for (size_t i = 0; i < sizeof(conf); i++)
    {
      SerialMon.printf(" %02x",conf[i]);
    }
    SerialLoRa.write((uint8_t *)&conf, sizeof(conf));
    delay(10000);
    deep_sleep();
  }
  Serial.println("Wake and start ");
  SwitchToNormalMode();
  msg.bootcount = bootCount;
  Serial.printf("boot:%d \n" ,msg.bootcount);
  SerialLoRa.flush();
  uint8_t payload[]={msg.targetAdressH, msg.targetAdressL, msg.targetChannel ,
                    msg.myadress & 0xff ,msg.myadress >> 8 ,
                    msg.bootcount & 0xff, msg.bootcount >> 8, 
                    };
  SerialMon.printf("I send data\r\n");
  for (size_t i = 0; i < sizeof(payload); i++)
  {
    SerialMon.printf(" %02x",payload[i]);
  }
  SerialMon.println();
  SerialLoRa.write((uint8_t *)&payload, sizeof(payload));
  SerialLoRa.flush();
  delay(100);
  bootCount++;
  deep_sleep();
}

void loop() {}

```

```cpp:gateway.ino
#include <Arduino.h>
#define SerialMon Serial
#define SerialLoRa Serial1
// E220-900T22S(JP)へのピンアサイン
//(略）
int16_t gatewayAddress = 0x0000;
uint8_t conf[] ={0xc0, 0x00, 0x08, 
                gatewayAddress >> 8, //ADDH
                gatewayAddress & 0xff, //ADDL
                0b01110000, // baud_rate 9600 bps  SF:9 BW:125
                0b11100000, //subpacket_size 32, rssi_ambient_noise_flag on, transmitting_power 13 dBm
                0x09, //own_channel
                0b11000111, //RSSI on ,fix mode,wor_cycle 4000 ms
                0x00, //CRYPT
                0x00};
struct  msgStruct{ 
  char targetAdressH = 0xFF;//broadcast adress 0xFFFF
  char targetAdressL = 0xFF;
  char targetChannel = 0x09;
  uint16_t sensorAdress ;
  uint16_t bootcount ;
} msg;

  /**
   * @brief ノーマルモード(M0=0,M1=0)へ移行する
   */
void SwitchToNormalMode(void){
  digitalWrite(LoRa_ModeSettingPin_M0, 0);
  digitalWrite(LoRa_ModeSettingPin_M1, 0);
  delay(100);
}

  /**
   * @brief WOR送信モード(M0=1,M1=0)へ移行する
   */
  void SwitchToWORSendingMode(void){
  digitalWrite(LoRa_ModeSettingPin_M0, 1);
  digitalWrite(LoRa_ModeSettingPin_M1, 0);
  delay(100);
}

  /**
   * @brief コンフィグ/sleepモード(M0=1,M1=1)へ移行する
   */
void SwitchToConfigurationMode(void){
  digitalWrite(LoRa_ModeSettingPin_M0, 1);
  digitalWrite(LoRa_ModeSettingPin_M1, 1);
  delay(100);
}

void setup(){
  pinMode(LoRa_ModeSettingPin_M0, OUTPUT);
  pinMode(LoRa_ModeSettingPin_M1, OUTPUT);
  SerialMon.begin(9600);
  delay(500);
  SerialLoRa.end(); 
  delay(1000); 
  SerialLoRa.begin(LoRa_BaudRate, SERIAL_8N1, LoRa_Tx_ESP_RxPin,LoRa_Rx_ESP_TxPin);                
  // E220-900T22S(JP)へのLoRa初期設定
  SwitchToConfigurationMode();
  SerialMon.printf("I send conf\r\n");
  for (size_t i = 0; i < sizeof(conf); i++)
  {
    SerialMon.printf(" %02x",conf[i]);
  }
  SerialLoRa.write((uint8_t *)&conf, sizeof(conf));
  SwitchToWORSendingMode();
  SerialLoRa.flush();
  //この送信は子機を起こすためでメッセージ内容は受信されない
  uint8_t payload[]={msg.targetAdressH, msg.targetAdressL, msg.targetChannel ,0x00}; 
  SerialMon.printf("I send data\r\n");
  for (size_t i = 0; i < sizeof(payload); i++)
  {
    SerialMon.printf(" %02x",payload[i]);
  }
  SerialMon.println();
  SerialLoRa.write((uint8_t *)&payload, sizeof(payload));
  SerialLoRa.flush();
  delay(100);
  SwitchToNormalMode(void);
}

void loop(){
 //センサーから受信してどこかに送信したりの処理

 //子機からの送信が全て終わったらDeepSpeep
 
}
  
```


# WORでラズパイを起動させる
ラズベリーパイはGPIO3ピンをLOWにすると起動するようになっています。そこでこのLoRaモジュールを使って遠隔で起動することが出来ます
自動起動のプログラムで終了後シャットダウンさせるようにすれば必要なときだけ動かすような用途に利用できます
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">ラズパイの遠隔起動<br>あらかじめ設定を書き込んでおいたLoRaモジュールを<br>VCC　‐　5v<br>GND　‐　GND<br>AUX　‐　GPIO3<br>M0　‐　GND<br>M1　‐　3.3v<br>とラズパイに接続<br>一度起動してシャットダウンさせた状態からWORで起動できた <a href="https://t.co/4c3iULFmQF">https://t.co/4c3iULFmQF</a> <a href="https://t.co/lc545GdXAz">pic.twitter.com/lc545GdXAz</a></p>&mdash; mnl_t (@mnlt18) <a href="https://twitter.com/mnlt18/status/1754119144398803318?ref_src=twsrc%5Etfw">February 4, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>