spresense + LTE拡張ボード +Lora Add-onボードを使い
広範囲(数km)散らばるセンサーデータをプライベートLora通信でゲートウェイに集め、
LTE CatMを使いクラウドに送信することで、Wifi環境もなく電源も限られた屋外でのIoTが
出来るようになる
この記事ではLora子機から送信されたデータをMQTTブローカーにPublishする部分を紹介します
子機の構成やMQTTで受信してからデータベースに送信し地図やグラフで表示するところについては省略します
以下を参考にしてください
Io田んぼ
https://github.com/mnltake/IoTanbo
spreseseをマルチコアを使うことでメインコアでLTEの送信とサブコアでLoraの受信を行う
spresenseの省電力と軽量なMQTTを使うことで太陽光の独立電源のみで動作可能(予定)
部品
・Spresenseメインボード
・LTE拡張ボード
・SPRESENSE用 LoRa Add-onボード E220-900T22S(JP)搭載
・simカード(povo.jp)
・ソーラーパネル+18650 バッテリー+充電モジュール
配線
メインボード、LTE、Loraはそのまま挿し込むだけ
充電モジュールの+/-にソーラーパネル、B+/ーにバッテリー、OUT+/ーと昇圧電源モジュールのVIN+/ー、昇圧電源モジュールのOUT+/ーをSpresenseLTE拡張ボードのMAIN POWER/GNDに接続します
またバッテリー電圧監視用にSpresenseLTE拡張ボードのVoltage Jamperを5V に、A5ピンとB+を接続します
ソースコード
maincore.ino
#include <Arduino.h>
// libraries
#include <RTC.h>
#include <MP.h>
#include <LowPower.h>
#include <LTE.h>
#include <LTEScanNetworks.h>
#include <ArduinoMqttClient.h>
#include <Arduino_JSON.h>
// APN name
#define APP_LTE_APN "povo.jp" // replace your APN
/* APN authentication settings
* Ignore these parameters when setting LTE_NET_AUTHTYPE_NONE.
*/
#define APP_LTE_USER_NAME "" // replace with your username
#define APP_LTE_PASSWORD "" // replace with your password
// APN IP type
#define APP_LTE_IP_TYPE (LTE_NET_IPTYPE_V4V6) // IP : IPv4v6
// APN authentication type
#define APP_LTE_AUTH_TYPE (LTE_NET_AUTHTYPE_CHAP) // Authentication : CHAP
#define APP_LTE_RAT (LTE_NET_RAT_CATM) // RAT : LTE-M (LTE Cat-M1)
// MQTT broker
#define BROKER_NAME "yourMQTTbroker host URL" // replace with your broker
#define BROKER_PORT 1883 // port 8883 is the default for MQTT over TLS.
// MQTT topic
#define MQTT_TOPIC "LTE/SPRESENSE" // replace with your topic
const int keepAlive = 600;
const long pollInterval = 3*60*1000;
unsigned long previousMillis = 0;
LTE lteAccess;
LTEScanNetworks modemScan;
LTEClient client;
MqttClient mqttClient(client);
struct msgStruct{
uint8_t conf_0 = 0xFF;
uint8_t conf_1 = 0xFF;
uint8_t channel = 0x00;
uint16_t sensorID ;
uint16_t water ;
int16_t rssi;
uint16_t bootcount;
volatile int status; /* 0:ready, 1:busy */
volatile bool toCloud = false;
} ;
/* Attach to the LTE network */
void doAttach()
{
char apn[LTE_NET_APN_MAXLEN] = APP_LTE_APN;
LTENetworkAuthType authtype = APP_LTE_AUTH_TYPE;
char user_name[LTE_NET_USER_MAXLEN] = APP_LTE_USER_NAME;
char password[LTE_NET_PASSWORD_MAXLEN] = APP_LTE_PASSWORD;
Serial.println("=========== APN information ===========");
Serial.print("Access Point Name : ");
Serial.println(apn);
Serial.print("Authentication Type: ");
Serial.println(authtype == LTE_NET_AUTHTYPE_CHAP ? "CHAP" :
authtype == LTE_NET_AUTHTYPE_NONE ? "NONE" : "PAP");
if (authtype != LTE_NET_AUTHTYPE_NONE) {
Serial.print("User Name : ");
Serial.println(user_name);
Serial.print("Password : ");
Serial.println(password);
}
while (true) {
/* Power on the modem and Enable the radio function. */
if (lteAccess.begin() != LTE_SEARCHING) {
Serial.println("Could not transition to LTE_SEARCHING.");
Serial.println("Please check the status of the LTE board.");
for (;;) {
sleep(1);
}
}
/* The connection process to the APN will start.
* If the synchronous parameter is false,
* the return value will be returned when the connection process is started.
*/
if (lteAccess.attach(APP_LTE_RAT,
apn,
user_name,
password,
authtype,
APP_LTE_IP_TYPE,
false) == LTE_CONNECTING) {
Serial.println("Attempting to connect to network.");
break;
}
/* If the following logs occur frequently, one of the following might be a cause:
* - APN settings are incorrect
* - SIM is not inserted correctly
* - If you have specified LTE_NET_RAT_NBIOT for APP_LTE_RAT,
* your LTE board may not support it.
*/
Serial.println("An error has occurred. Shutdown and retry the network attach preparation process after 1 second.");
lteAccess.shutdown();
sleep(1);
}
}
void setup()
{
// Open serial communications and wait for port to open
Serial.begin(115200);
while (!Serial);
// pinMode(PIN_A5,INPUT);
pinMode(LED0,OUTPUT);//LTE attach
pinMode(LED2,OUTPUT);//MQTT poll
pinMode(LED3,OUTPUT);//MQTT pub
LowPower.begin();
int ret;
int subid = 1;
ret = MP.begin(subid);
if (ret < 0) {
printf("MP.begin(%d) error = %d\n", subid, ret);
}
MP.RecvTimeout(MP_RECV_POLLING);
/* Connect LTE network */
ledOn(LED0);
doAttach();
// Wait for the modem to connect to the LTE network.
Serial.println("Waiting for successful attach.");
LTEModemStatus modemStatus = lteAccess.getStatus();
while(LTE_READY != modemStatus) {
if (LTE_ERROR == modemStatus) {
/* If the following logs occur frequently, one of the following might be a cause:
* - Reject from LTE network
*/
Serial.println("An error has occurred. Shutdown and retry the network attach process after 1 second.");
lteAccess.shutdown();
sleep(1);
doAttach();
}
sleep(1);
modemStatus = lteAccess.getStatus();
}
Serial.println("attach succeeded.");
ledOff(LED0);
Serial.print("Attempting to connect to the MQTT broker: ");
Serial.println(BROKER_NAME);
mqttClient.setKeepAliveInterval(keepAlive);
if (!mqttClient.connect(BROKER_NAME, BROKER_PORT)) {
Serial.print("MQTT connection failed! Error code = ");
Serial.println(mqttClient.connectError());
// do nothing forevermore:
for (;;)
sleep(30);
LowPower.reboot();
}
// mqttClient.setCleanSession(false);
Serial.println("You're connected to the MQTT broker!");
}
void loop()
{
int subid = 1;
int8_t msgid;
msgStruct *msg;
JSONVar msgJson;
unsigned long currentMillis = millis();
int vbat = int((analogRead(PIN_A5)*5000.0/1023.0));//mV
if (currentMillis - previousMillis > pollInterval) {
ledOn(LED2);
if (!mqttClient.connected()) {
ledOn(LED0);
Serial.print("MQTT connection failed! Error code = ");
Serial.println(mqttClient.connectError());
lteAccess.shutdown();
sleep(1);
doAttach();
if (mqttClient.connect(BROKER_NAME, BROKER_PORT)){
ledOff(LED0);
}else{
sleep(30);
LowPower.reboot();
}
}
mqttClient.poll();
Serial.println("mqtt Poll ");
previousMillis = currentMillis;
ledOff(LED2);
}
if ((MP.Recv(&msgid, &msg, subid) > 0) && (msg->toCloud == true)){
msg->status = 1;
if (!mqttClient.connected()) {
ledOn(LED0);
Serial.print("MQTT connection failed! Error code = ");
Serial.println(mqttClient.connectError());
lteAccess.shutdown();
sleep(1);
doAttach();
mqttClient.connect(BROKER_NAME, BROKER_PORT);
}else{
ledOff(LED0);
}
ledOn(LED3);
msgJson["sensor"] = "lteGW";
msgJson["ID"] = String(msg->sensorID );
msgJson["water"] = msg->water;
msgJson["bootcount"] = msg->bootcount;
msgJson["rssi"] = msg->rssi;
msgJson["vbat"] = vbat;
String msgString = JSON.stringify(msgJson);
// Publish to broker
Serial.print("Sending message to topic: ");
Serial.println(MQTT_TOPIC);
Serial.print("Publish: ");
Serial.println(msgString);
mqttClient.beginMessage(MQTT_TOPIC);
mqttClient.print(msgString);
mqttClient.endMessage();
msg->toCloud = false;
msg->status = 0;
mqttClient.poll();
previousMillis = currentMillis;
ledOff(LED3);
}
// delay(10);
// 低電圧時はDeepsleep
if (vbat < 3000){
lteAccess.shutdown();
LowPower.deepSleep(60*60);//1hour sleep
}
//1日に一回再起動
if (currentMillis > 24*60*60*1000UL) {
lteAccess.shutdown();
LowPower.reboot();
}
}
subcore1.ino
#include <Arduino.h>
#include <MP.h>
#include <vector>
#include "spresense_e220900t22s_jp_lib.h"
#define OWN_ADDRESS 0xFFFF
#define OWN_CHANNEL 0x09
uint16_t sensorID;
struct msgStruct{
uint8_t conf_0 = 0xFF;
uint8_t conf_1 = 0xFF;
uint8_t channel = 0x09;
uint16_t sensorID ;
uint16_t water ;
int16_t rssi;
uint16_t bootcount;
volatile int status; /* 0:ready, 1:busy */
volatile bool toCloud = false;
} ;
uint8_t config[] ={0xc0, 0x00, 0x08,
OWN_ADDRESS >> 8, //ADDH
OWN_ADDRESS & 0xff, //ADDL
0b01110000, // baud_rate 9600 bps SF:9 BW:125
0b11100000, //subpacket_size 32, rssi_ambient_noise_flag on, transmitting_power 13 dBm
OWN_CHANNEL, //own_channel
0b10000011, //RSSI on ,fix mode,wor_cycle 2000 ms
0x00, //CRYPT
0x00};
#define SUBID "Sub1"
#define MY_MSGID 1
msgStruct msg;
CLoRa lora;
struct RecvFrameE220900T22SJP_t data;
void setup()
{
MP.begin();
Serial.begin(115200);
/* Wait HW initialization done. */
sleep(1);
Serial.printf("(subcore) start\n");
memset(&msg, 0, sizeof(msg));
pinMode(LED1,OUTPUT);//Lora recv
// LoRa設定値
struct LoRaConfigItem_t config = {
0xFFFF, // own_address 0
0b011, // baud_rate 9600 bps
0b10000, // air_data_rate SF:9 BW:125
0b00, // subpacket_size 200
0b1, // rssi_ambient_noise_flag 有効
0b0, // transmission_pause_flag 有効
0b00, // transmitting_power 13 dBm
0x09, // own_channel 9
0b1, // rssi_byte_flag 有効
0b1, // transmission_method_type 固定送信モード
0b0, // lbt_flag 有効
0b011, // wor_cycle 2000 ms
0x0000, // encryption_key 0
0x0000, // target_address 0
0x00 // target_channel 0
};
// E220-900T22S(JP)へのLoRa初期設定
if (lora.InitLoRaModule(config)) {
SerialMon.printf("(subcore)Lora init error\n");
return;
} else {
Serial.printf("(subcore)Lora init ok\n");
}
// ノーマルモード(M0=0,M1=0)へ移行する
SerialMon.printf("(subcore)switch to normal mode\n");
lora.SwitchToNormalMode();
}
void loop()
{
int ret;
if (lora.RecieveFrame(&data) == 0) {
ledOn(LED1);
SerialMon.printf("(subcore)Lora recv data:\n");
SerialMon.printf("(subcore)hex dump:\n");
for (int i = 0; i < data.recv_data_len; i++) {
SerialMon.printf("%02x ", data.recv_data[i]);
}
SerialMon.println();
msg.sensorID = data.recv_data[3] | (data.recv_data[4]<<8) ;
msg.water = data.recv_data[5] | (data.recv_data[6]<<8) ;
msg.bootcount = data.recv_data[7] | (data.recv_data[8]<<8) ;
msg.rssi = data.rssi;
if (msg.status == 0) {
/* status -> busy */
msg.status = 1;
msg.toCloud = true;
/* Send to MainCore */
ret = MP.Send(MY_MSGID , &msg);
if (ret < 0) {
printf("(subcore)MP.Send error = %d\n", ret);
}
ledOff(LED1);
}
}
}
投稿者の人気記事
-
mnlt
さんが
2023/12/25
に
編集
をしました。
(メッセージ: 初版)
-
mnlt
さんが
2023/12/25
に
編集
をしました。
-
mnlt
さんが
2024/01/20
に
編集
をしました。
-
mnlt
さんが
2024/01/20
に
編集
をしました。
ログインしてコメントを投稿する