mnlt が 2024年01月20日20時59分40秒 に編集
コメント無し
本文の変更
spresense + LTE拡張ボード +Lora Add-onボードを使い 広範囲(数km)散らばるセンサーデータをプライベートLora通信でゲートウェイに集め、 LTE CatMを使いクラウドに送信することで、Wifi環境もなく電源も限られた屋外でのIoTが 出来るようになる この記事ではLora子機から送信されたデータをMQTTブローカーにPublishする部分を紹介します 子機の構成やMQTTで受信してからデータベースに送信し地図やグラフで表示するところについては省略します 以下を参考にしてください
[Io田んぼ](https://protopedia.net/prototype/3932)
https://github.com/mnltake/IoTanbo spreseseをマルチコアを使うことでメインコアでLTEの送信とサブコアでLoraの受信を行う spresenseの省電力と軽量なMQTTを使うことで太陽光の独立電源のみで動作可能(予定) ## 部品 ・Spresenseメインボード ・LTE拡張ボード ・SPRESENSE用 LoRa Add-onボード E220-900T22S(JP)搭載 ・simカード(povo.jp) ・ソーラーパネル+18650 バッテリー+[充電モジュール](https://www.amazon.co.jp/gp/product/B09HCC3NWJ/ref=ppx_yo_dt_b_asin_title_o09_s00?ie=UTF8&psc=1) ## 配線 メインボード、LTE、Loraはそのまま挿し込むだけ 充電モジュールの+/-にソーラーパネル、B+/ーにバッテリー、OUT+/ーと昇圧電源モジュールのVIN+/ー、昇圧電源モジュールのOUT+/ーをSpresenseLTE拡張ボードのMAIN POWER/GNDに接続します またバッテリー電圧監視用にSpresenseLTE拡張ボードのVoltage Jamperを5V に、A5ピンとB+を接続します ## ソースコード ```cpp: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(); } } ``` ```cpp: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); } } } ```