HakoHiro が 2024年01月31日05時38分56秒 に編集
初版
タイトルの変更
LoRa通信による 距離の離れたレンタル農園の気象条件ロギング
タグの変更
SPRESENSE
spresense
LoRa
BME680
GPS
PlatformIO
メイン画像の変更
記事種類の変更
製作品
ライセンスの変更
(MIT) The MIT License
本文の変更
# 概要 少し距離の離れた(1.5km程度)のレンタル農園の気象条件をモニター、ロギングする。専業農家ではなく、かつ自宅直近ではないため 状況を自宅から確認できるようにする。また、趣味の範囲のため 有料の通信サービスは避けたい。その為には到達距離も長く、無料で通信できる LoRaを使用する。幸い今回のコンテストでは、LoRaボードが2Set提供されるため、Spresenseが2Setあれば実現可能です。 # 使用機器 - Spresense+拡張ボード 2Set - Spresense HDRカメラボード - ILI9341搭載2.8インチSPI制御TFT液晶 MSP2807 - SPRESENSE用Qwiic接続基板 - LoRa Add-onボード(DTH-SSLR) アンテナ含む 2Set - BME680 温湿度・気圧・ガスセンサモジュールキット 秋月電子通商 # 気象ステーション ## 外観 ![気象ステーション](https://camo.elchika.com/14e5fb55a74cf457eff9f64c3185b653b304943c/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38333537363361302d393332382d343665392d383165622d3234323436633435373839342f33386533356339362d616131642d346361652d616332612d336462633461646162396564/) ![キャプションを入力できます](https://camo.elchika.com/2e52a92d194d3df08a964c2e105fb56600c5cdc5/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38333537363361302d393332382d343665392d383165622d3234323436633435373839342f30333035613063352d343436622d343438622d613463382d666434633765623064346636/) 外部で単独で動作できるように、ケースに組み込みました。また、USBバッテリーで駆動しています。ただ、まだまだ防水の点などの考慮が不足です。また、省電力の努力は全くしておりません。それでも2日ほどの連続稼働は充分可能でした。 ## 動作状況 気象ステーション部では、気象データを一定期間で採取して、液晶ディスプレイに表示する他にLoRa通信を利用して、受信側に送信します。液晶には、直近の30個のデータをリングバッファに溜め込んで表示します。 ## ソフトウェア概要 - BME680 圧力、温度、湿度、ガス検出ができます。CO2の検出ではないですが、換気のタイミングを調べるのには使えると思います。 Adafruit BME680 Libraryを使用。 - LoRa E220-900T22S(JP) モジュールを使用。 spresense_e220900t22s_jp_libをライブラリにセット - GPS Spresense内蔵のGPSを 時刻合わせのために使用。室内だったら3分ほど屋外だったら1分程度でGPSを拾い、時刻を合わすことが出来る。時刻を拾ったらRTCに時刻を設定。 位置測定は使用していない。 - 液晶 TFT液晶表示の為に Adafruitのライブラリを使用。 Adafruit ILI9341 Adafruit GFX Library - リングバッファ 最近のデータを保持するためにリングバッファ(サーキュラーバッファ)を使用。 rlogiacco/CircularBuffer - HDR Camera 今回のウエザーステーションには組み込まなかったが、単体で確認している。 CQ出版 トラ技2023年7月号 エンヤ ヒロカズ氏の『暗くても明るくても鮮明!ソニーHDRカメラの実力』が非常に参考になった。HDRカメラの情報はあまりなく、設定の情報はこれが一番! 液晶に動的表示、SDカードにキャプチャー画像の保存まで、ダウンロードソースほとんど修正なしで確認できる。 ## ソースコード ```arduino:WeatherStation #include <RTC.h> #include <GNSS.h> #include <Wire.h> #include <Adafruit_BME680.h> #include <SPI.h> #include <Adafruit_GFX.h> #include <Adafruit_ILI9341.h> #include <vector> #include "spresense_e220900t22s_jp_lib.h" #include <CircularBuffer.h> #define TFT_DC 9 #define TFT_CS -1 #define TFT_RST 8 #define MY_TIMEZONE_IN_SECONDS (9 * 60 * 60) // JST Adafruit_ILI9341 tft = Adafruit_ILI9341(&SPI, TFT_DC, TFT_CS, TFT_RST); Adafruit_BME680 bme; CLoRa lora; SpGnss Gnss; // dataを30個のリングバッファに格納する CircularBuffer<String,30> buffer; String readString; // LoRa設定値 struct LoRaConfigItem_t config = { 0x0000, // 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 有効 0b01, // transmitting_power 13 dBm 0x00, // own_channel 0 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 }; // void printClock(RtcTime &rtc); void updateClock(void); void updateData(RtcTime &rtc); void setup() { tft.begin(40000000); tft.setRotation(3); tft.fillScreen(ILI9341_BLACK); tft.setTextSize(1); tft.setTextColor(ILI9341_GREEN); Wire.begin(); if (!bme.begin()) { tft.setCursor(0,12); tft.println("Could not find BME680"); while (1); } // E220-900T22S(JP)へのLoRa初期設定 if (lora.InitLoRaModule(config)) { tft.println("LoRa init error"); return; } else { tft.println("Lora init ok"); } // // ノーマルモード(M0=0,M1=0)へ移行する // // SerialMon.printf("switch to normal mode\n"); // // lora.SwitchToNormalMode(); // 送信モード(M0=1,M1=0)へ移行する(バグフィックス) // thanks chrmlinux03様 // 『LoRaE220_900T22S_JPのサンプルが間違ってたので修正したよっ』 tft.println("LoRa switch to sending mode"); lora.SwitchToWORSendingMode(); // Set up BME680 oversampling and filter initialization bme.setTemperatureOversampling(BME680_OS_8X); bme.setHumidityOversampling(BME680_OS_2X); bme.setPressureOversampling(BME680_OS_4X); bme.setIIRFilterSize(BME680_FILTER_SIZE_3); bme.setGasHeater(320, 150); // 320*C for 150 ms tft.println("GPS clock "); // Initialize RTC at first RTC.begin(); // Initialize and start GNSS library int ret; ret = Gnss.begin(); assert(ret == 0); ret = Gnss.start(); assert(ret == 0); readString = "No data"; for(int i=0; i<30;i++){ buffer.push(readString); } } void loop() { if (!bme.performReading()){ tft.println("failed to BME680 reading"); return; } // Wait for GNSS data if (Gnss.waitUpdate()) { SpNavData NavData; // Get the UTC time Gnss.getNavData(&NavData); SpGnssTime *time = &NavData.time; // Check if the acquired UTC time is accurate if (time->year >= 2000) { RtcTime now = RTC.getTime(); // Convert SpGnssTime to RtcTime RtcTime gps(time->year, time->month, time->day, time->hour, time->minute, time->sec, time->usec * 1000); // Set the time difference gps += MY_TIMEZONE_IN_SECONDS; int diff = now - gps; if (abs(diff) >= 1) { RTC.setTime(gps); } } } // GPSから現在時間を取得、BME680から気象Dataを取得 // 現在は30秒毎 一定周期で data取得 updateClock(); } void updateData(RtcTime &rtc) { char timebuf[12]; char bmebuf[50]; char sendbuf[62]; sprintf(timebuf,"%02d:%02d:%02d ",rtc.hour(),rtc.minute(),rtc.second()); sprintf(bmebuf,"%02.1fC %04ld hPa %02.1f%% %05.1fkOhms",bme.temperature,bme.pressure/100,bme.humidity,bme.gas_resistance/1000.0); tft.fillScreen(ILI9341_BLACK); readString = timebuf; readString += bmebuf; readString.toCharArray(sendbuf,62); buffer.push(readString); tft.fillScreen(ILI9341_BLACK); tft.setCursor(0,0); //LoRaへData送信 if (lora.SendFrame(config, (uint8_t *)sendbuf, strlen(sendbuf)) == 0) { Serial.println("send succeeded."); } else { Serial.println("send failed."); } //LCD画面にリングバッファDataを表示 for (int i=0; i<30; i++){ readString = buffer[i]; tft.println(readString); } } void updateClock() { static RtcTime old; RtcTime now = RTC.getTime(); // get Data every 30sec // if (now.minute()/10 != old.minute()/10) { // if (now.minute() != old.minute()) { // if (now.second()/10 != old.second()/10) { if (now.second()/30 != old.second()/30) { updateData(now); old = now; } } ``` # 受信側 ## 外観 ![受信側](https://camo.elchika.com/53a4bc51153ee8ca71c33216baef341c435594af/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38333537363361302d393332382d343665392d383165622d3234323436633435373839342f66623861323331622d376334612d346535372d626334392d616233633939326564356265/) 受信側は基本PCに繋いでDataを確認します。先々は一旦、M5Stack等で受けWebデータにしたいと思っています。PCが自由になりますからね。 ## 動作状況 ![受信動作シリアルモニタ](https://camo.elchika.com/2dd4d3519a8090ab20fbf3c5c70c0723d1ffcc07/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38333537363361302d393332382d343665392d383165622d3234323436633435373839342f38366135653762362d653935322d343966622d386139612d306335346632326231376531/) ## ソフトウェア概要 - LoRa:送信側(WeatherStation)と同様 E220-900T22S(JP) モジュールを使用。 spresense_e220900t22s_jp_libをライブラリにセット ## ソースコード ```arduino:ほぼexampleと同じ // LoRaRecieve #include <vector> #include "spresense_e220900t22s_jp_lib.h" CLoRa lora; struct RecvFrameE220900T22SJP_t data; void setup() { // put your setup code here, to run once: pinMode(LED0, OUTPUT); pinMode(LED1, OUTPUT); pinMode(LED2, OUTPUT); pinMode(LED3, OUTPUT); /* Set serial baudrate. */ Serial.begin(115200); /* Wait HW initialization done. */ sleep(1); Serial.printf("program start\n"); // LoRa設定値 struct LoRaConfigItem_t config = { 0x0000, // 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 有効 0b01, // transmitting_power 13 dBm 0x00, // own_channel 0 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("init error\n"); return; } else { Serial.printf("init ok\n"); } // ノーマルモード(M0=0,M1=0)へ移行する SerialMon.printf("switch to normal mode\n"); lora.SwitchToNormalMode(); // LoRa受信 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(10); } } void loop() { // put your main code here, to run repeatedly: digitalWrite(LED0, HIGH); delay(100); digitalWrite(LED1, HIGH); delay(100); digitalWrite(LED2, HIGH); delay(100); digitalWrite(LED3, HIGH); delay(1000); digitalWrite(LED0, LOW); delay(100); digitalWrite(LED1, LOW); delay(100); digitalWrite(LED2, LOW); delay(100); digitalWrite(LED3, LOW); delay(1000); } ``` # LoRa評価 - 通信距離 少なくとも1.5kmの距離(北海道ですので、あまり建物は立て込んでいません)はOK。もっと長距離を確認したいところだが、後述の耐低温特性の問題でテストできない状況です。 - 通信安定性 もっと色々テストしたいところだが、ちょっとテストしている分には問題なし。 - 不安点 - 耐低温特性 なにせ、こちらは北海道函館で、北海道の中では温かい方なのですが、屋外に設置し通信テストをした際、途中から(−5℃以下)LoRaイニシャル異常が発生し、しばらく復帰しない事が何回かありました。バッテリーも低温では厳しいのでその点が心配です。もっとも冬季はレンタル農園もやることがないのでハングアップしても問題ないのですがね。 ちなみに、最初LoRaボードのテストを開始した際、動かなかったのですが、chrmlinux03様のelchka投稿 『LoRaE220_900T22S_JPのサンプルが間違ってたので修正したよっ』で助けられました。また、12月中を過ぎてから、電圧特性不良とのことで、代替え品を送付いただきました。こちらから申し出たわけではないのですが、無料で送付いただきありがとうございました。 # 現状の問題点 - 耐低温特性の確認 - Camera組み込み - 通信安定性の評価が不十分 ## VSCode + PlatformIO 当初、VSCode + PlatformIOにて1月中旬まで安定して開発できていました。最近になってコンパイルは問題ないのですが、Spresenseに書き込みができなくなり四苦八苦した上に時間切れで諦めました。仕方なくArduino IDEで開発を続けましたが、使い勝手は格段に落ちますね。そこそこ個別モジュールはPlatformIOにて開発していたので、なんとかなりました。 # 感想 - LoRa:以前はLoRa規格のボードは結構高価で、開発に二の足を踏む状態だったのですが、低価格のボードが出てきて活用の機運が高まって来たと思います。もっと活用例、ライブラリが充実してくれば嬉しいです。 - 画像通信 LoRaで低速でも良いので画像通信ができればありがたいです。有料サービス、有料ライブラリはあるのですが、なかなか良いものが無い。有料だったらLoRa以外にもいろいろ手段があるのですが、faxの様に低速でも、分割でも良いので画像が送れるのなら、Spresenseの様な強力、低電力CPUの得意分野になると思います。動体検知して、監視異常の画像を送る等・・・ # 今後の課題 - VSCode+PlatformIOの開発環境 Arduino IDEはげんなりなので、上記開発環境のアップロード問題を解決したい。 - 画像通信 なかなか自前で開発はしんどい。Faxの開発もしたことはあるのですが、とにかくいいライブラリが提供されるのを待ちます。 - GPSの活用 今回は時間取得にだけGPSを使ったが、LoRaと組み合わせればもっと面白い活用がありそう。