この記事について
前回構築したPrivate LoRa GWに接続するLoRa DeviceをESP32ボードとLoRaモジュール評価ボードで構築します。下図の色付き部分です。
- edge node用ESP32ボードにLoRaモジュール評価ボードを接続しPrivate LoRaデバイス化
- edge node用ESP32を仮想エッジデバイスと見立てて監視対象の数値をLoRa GWにpub
させます。
デバイス
LoRaモジュール評価ボード
LoRaモジュール評価ボード(E220-900T22S(JP)-EV1)
README.md・サンプルコード
ESP32ボード
あまり使用例がないボードですがAmazonで入手可能なACEIRMC D1 Mini NodeMCU ESP32 ESP-WROOM-32 IoT 開発ボードを使いました。
当時参照したDocのURLが探しても見つからない為、見つけたら後追いでリンクを貼ります。
本稿の話とは絡まないですが、たまねぎブログさん、行き当たりばったり電子工作さんが比較的最近丁寧なレポートをまとめておられますので利用検討の際は助けになるかと思います。
Arduino
前回まで2回のPythonによる構築時に設定したsetting.iniの内容をデータとしてアップロードするためにファイルアップローダーpluginが必要になります。
記事執筆時点(2024.07.07)でもArduino IDE 2.X系でファイルアップローダーが使えなかったので1.x系を使用します。
ボードについてはESP32 DEV MODULE 3.x系のバグは解消していないようなので2.0.x系の利用を推奨します(というか修正できなかった)。
IDE:1.8.18
ボード:ESP32 DEV MODULE 2.0.17
追加ライブラリ:M5Core2 0.1.7
ファイルアップローダーplugin:ESP32FS-1.1
サンプルスケッチ
構成
ダウンロードファイルの内訳です。
- README:esp32_e220900t22s_jp_lib/docs/README.md
- スケッチsample code:esp32_e220900t22s_jp_lib/example/lora_multi_task/lora_multi_task.ino
- ライブラリー:esp32_e220900t22s_jp_lib/src
- iniファイル:esp32_e220900t22s_jp_lib/docs/e220900t22s_jp_lora_config.ini
スケッチ名変更
今回スケッチ名をE220_D1miniESP32_aws-2としてlora_multi_task.inoのファイル名を変更します。
iniファイルを格納するsub directoryをdataとするとスケッチdirectoryの構成は以下のようになります。
E220_D1miniESP32_aws-2
┣ data
┃ ┗ e220900t22s_jp_lora_config.ini
┣ E220_D1miniESP32_aws-2.ino
┣ esp32_e220900t22s_jp_lib.cpp
┗ esp32_e220900t22s_jp_lib.h
スケッチ
スケッチ名:E220_D1miniESP32_aws-2のプロジェクトを書き込んだESP32ボードで構築したPrivate LoRa GW とLoRa通信するためにサンプルファイルを一部変更します。
iniファイル
own_address
Pi GWでは0、Pi LoRa Deviceは1にしたので2にします。
wor_cycle
Pi GWに合わせて3000にします。
encryption_key
Pi GWに合わせて1234にします。
target_address
ffffにします。
own_address=2
baud_rate=9600
bw=125
sf=9
subpacket_size=200
transmitting_power=13
own_channel=0
wor_cycle=3000
encryption_key=1234
target_address=ffff
target_channel=0
lib.cpp
主にiniファイルの変更箇所を変更します。出力も追加します。
.
.
void CLoRa::SetDefaultConfigValue(struct LoRaConfigItem_t &config) {
const LoRaConfigItem_t default_config = {
0x0000, // own_address 0
0b011, // baud_rate 9600 bps
0b10000, // air_data_rate SF:9 BW:125
0b00, // subpacket_size 200
0b0, // rssi_ambient_noise_flag 無効
0b0, // transmission_pause_flag 有効
0b01, // transmitting_power 13 dBm
0x00, // own_channel 0
0b1, // rssi_byte_flag 有効
0b1, // transmission_method_type 固定送信モード
0b0, // lbt_flag 有効
0b101, // wor_cycle 3000 msに変更
0x0000, // encryption_key 0
0x0000, // target_address 0
0x00}; // target_channel 0
config = default_config;
}
.
.
int CLoRa::OpenConfigFile(const char *filename,
struct LoRaConfigItem_t &config) {
int ret = 0;
// フラッシュメモリのファイルを開く
File file = SPIFFS.open(filename, FILE_READ);
if (!file) {
SerialMon.printf("file open failed.\n");
return 1;
}
while (file.available()) {
String key = file.readStringUntil('=');
String val = file.readStringUntil('\n');
//追加
SerialMon.printf("%s", key.c_str());
SerialMon.printf("%s", val.c_str());
key.toLowerCase();
// 設定値を内部変数に読み取る
ReadConfigValue(key.c_str(), val.c_str());
}
// 設定値をセットする
SetConfigValue(config);
//追加
SerialMon.printf("Sat default values.\n");
return ret;
}
.
.
// wor_cycle
switch (wor_cycle_val) {
case 500:
config.wor_cycle = 0b000;
break;
case 1000:
config.wor_cycle = 0b001;
break;
case 1500:
config.wor_cycle = 0b010;
break;
case 2000:
config.wor_cycle = 0b011;
break;
//3000を追加
case 3000:
config.wor_cycle = 0b101;
break;
default:
err = 1;
SerialMon.printf("wor_cycle invalid value.\n");
SerialMon.printf("default wor_cycle value is used.\n");
break;
}
.
.
lib.h
ボードのピンアサインを変更します。
LoRaモジュール評価ボード(E220-900T22S(JP)-EV1)のピンをこれらのport番号のピンと接続します。
この5種類以外にESP32とE220ボードのVCC(5V)とGNDも接続します。
.
.
// E220-900T22S(JP)へのピンアサイン
#define LoRa_ModeSettingPin_M0 23
#define LoRa_ModeSettingPin_M1 19
#define LoRa_RxPin 16
#define LoRa_TxPin 17
#define LoRa_AUXPin 18
.
,
E220_D1miniESP32_aws-2.ino
主な変更はLoRaSendTask()のロジックです。
サンプルコードではconsole入力をpubしますが今回は0~1000の乱数を発生させて10秒毎にpubします。
実使用場面を想定してwifiも無効にしておきます。
識別子は、own_addressを2にしたのでtopicのサフィックスとなるdevice_noも2にします。
修正したコードは以下です。
#include "esp32_e220900t22s_jp_lib.h"
#include <Arduino.h>
#include <FS.h>
#include <SD.h>
#include <SPI.h>
//参照追加
#include <WiFi.h>
CLoRa lora;
struct LoRaConfigItem_t config;
struct RecvFrameE220900T22SJP_t data;
/** prototype declaration **/
void LoRaRecvTask(void *pvParameters);
void LoRaSendTask(void *pvParameters);
void ReadDataFromConsole(char *msg, int max_msg_len);
void setup() {
// put your setup code here, to run once:
SerialMon.begin(9600);
delay(1000); // SerialMon init wait
SerialMon.println();
//wifi無効化
WiFi.disconnect(true);
// LoRa設定値の読み込み
if (lora.LoadConfigSetting(CONFIG_FILENAME, config)) {
SerialMon.printf("Loading Configfile failed. The default value is set.\n");
} else {
SerialMon.printf("Loading Configfile succeeded.\n");
}
// E220-900T22S(JP)へのLoRa初期設定
if (lora.InitLoRaModule(config)) {
SerialMon.printf("init error\n");
return;
}
// ノーマルモード(M0=0,M1=0)へ移行する
lora.SwitchToNormalMode();
// マルチタスク
xTaskCreateUniversal(LoRaRecvTask, "LoRaRecvTask", 8192, NULL, 1, NULL,
APP_CPU_NUM);
xTaskCreateUniversal(LoRaSendTask, "LoRaSendTask", 8192, NULL, 1, NULL,
APP_CPU_NUM);
}
void loop() {
}
void LoRaRecvTask(void *pvParameters) {
while (1) {
if (lora.RecieveFrame(&data) == 0) {
SerialMon.printf("recv data:\n");
for (int i = 0; i < data.recv_data_len; i++) {
SerialMon.printf("%c", data.recv_data[i]);
}
SerialMon.printf("\n");
SerialMon.printf("hex dump:\n");
for (int i = 0; i < data.recv_data_len; i++) {
SerialMon.printf("%02x ", data.recv_data[i]);
}
SerialMon.printf("\n");
SerialMon.printf("RSSI: %d dBm\n", data.rssi);
SerialMon.printf("\n");
SerialMon.flush();
}
delay(1);
}
}
void LoRaSendTask(void *pvParameters) {
while (1) {
//ランダムな整数を生成してmsgとして出力
int count = random(0, 1001);
char msg[200] = {0};
snprintf(msg, sizeof(msg), "%08d", count);
// device No.2をmsg文字列に追加
strcat(msg, "\n2\n");
if (lora.SendFrame(config, (uint8_t *)msg, strlen(msg)) == 0) {
SerialMon.printf("send succeeded.\n");
SerialMon.printf("\n");
} else {
SerialMon.printf("send failed.\n");
SerialMon.printf("\n");
}
SerialMon.flush();
delay(10000);
}
}
void ReadDataFromConsole(char *msg, int max_msg_len) {
int len = 0;
char *start_p = msg;
while (len < max_msg_len) {
if (SerialMon.available() > 0) {
char incoming_byte = SerialMon.read();
if (incoming_byte == 0x00 || incoming_byte > 0x7F)
continue;
*(start_p + len) = incoming_byte;
// 最短で3文字(1文字 + CR LF)
if (incoming_byte == 0x0a && len >= 2 && (*(start_p + len - 1)) == 0x0d) {
break;
}
len++;
}
delay(1);
}
// msgからCR LFを削除
len = strlen(msg);
for (int i = 0; i < len; i++) {
if (msg[i] == 0x0D || msg[i] == 0x0A) {
msg[i] = '\0';
}
}
}
iniファイル書き込み
ツール > ESP32 Sketch Data Upload を実行すると/dataに格納したiniファイルがuploadされます。
スケッチ書込み
通常の「マイコンボードに書き込む」を実行します。
実行結果
通常のArduino sketchと同様に書き込みが正常終了した時点で起動しています。
シリアルモニタでも出力を確認できます。
Private LoRa GWのgateway.pyを起動させるとsubします。
user@privateLoRaAP1:~ $ python main.py /dev/ttyS0 --rssi
serial port:
/dev/ttyS0
receive waiting...
payload: b''
Connected
topic: privateLoRaAP1/LoRaDev2
count : 00000897
{'Timestamp': 1720220384, 'count': 897}
topic: privateLoRaAP1/LoRaDev2
count : 00000309
{'Timestamp': 1720220394, 'count': 309}
ラズパイ LoRa Deviceのsend.pyも実行します。
GW brokerで乱数と8888がsubされているのが確認できます。
.
.
.
{'Timestamp': 1720220485, 'count': 863}
topic: privateLoRaAP1/LoRaDev1
count : 8888
{'Timestamp': 1720220489, 'count': 8888}
topic: privateLoRaAP1/LoRaDev2
count : 00000068
{'Timestamp': 1720220495, 'count': 68}
topic: privateLoRaAP1/LoRaDev1
count : 8888
{'Timestamp': 1720220499, 'count': 8888}
topic: privateLoRaAP1/LoRaDev2
count : 00000453
{'Timestamp': 1720220505, 'count': 453}
.
.
.
AWS IoT CoreのMQTTクライアントでもdevice_no:1(topic:privateLoRaAP1/LoRaDev1)とdevice_no:2(topic:privateLoRaAP1/LoRaDev2)からsub出来ているのが確認できました。