描述
這專案涵蓋了開發基於 Seeed LoRa-E5 的 Arduino 風速/方向感測器的過程,並將其安裝在能夠連接到 DigiKey 公司總部大樓屋頂氣象平台的戶外外殼中。專案還包括將感測器節點新增至現有的基於 ChirpStack 的私有 LoRaWAN 網路和 Machinecha t的 JEDI Pro IoT 軟體平台。LoRa 感測器使用 Seeeduino Xiao, Grove LoRa-E5 無線電板,以及 Sparkfun 天氣儀表套件的風速計和風向標硬體。LoRaWAN 網路使用 Seeed的 IP67 等級工業 LoRaWAN 閘道器將 LoRa 感測器資料包轉送到 Seeed 的 ReServer 上運行的專用 ChirpStack LoRaWAN 網路伺服器(安裝了 Ubuntu linux)。Machinechat 的 JEDI Pro IoT 平台運行在同一個 ReServer 上。
硬體
- Seeed reServer 102110560
reServer 基於 ODYSSEY X86 v2 主機板,支援 Intel®Core™11 Gen. i3 和 Intel UHD Graphics 48EUs - Seeed SenseCAP 室外閘道器 - LoRaWAN US915MHz 102991155
SenseCAP LoRaWAN 閘道器基於 LoRaWAN 協議,適用於低功耗、遠距離的環境資料擷取和監測。 - Seeed Grove LoRa-E5 板 113020091
STM32WLE5JC, SX126x LoRa RF Grove 平台評估擴充板 - Seeed Seeeduino Xiao板 102010388
SAMD21G18 Seeeduino Xiao 系列 ARM®Cortex®-M0 MCU 32 位元嵌入式評估板 - Sparkfun 氣象儀表套件 SEN-15901
該套件提供了三個核心組件的天氣測量:風速,風向和降雨量(注意:這篇文章使用風速計和風向標感測器,雨量計感測器計劃在未來的文章中介紹)。
軟體
- JEDI Pro或JEDI Pro SSE
適用於物聯網資料收集、視覺化、監控和資料儲存的軟體,可整合到物聯網解決方案中。功能包括:收集來自感測器、設備和機器的數據;構建直觀的實時和歷史數據以及系統視圖儀表板;創建規則,自動監控和響應數據情況;透過電子郵件和短信接收警報通知。JEDI Pro SSE 是 JEDI Pro 的 Seeed 工作室版本,為 Seeed 的 SenseCAP LoRaWAN 感測器系列增加了一個資料收集器 - ChirpStack
ChirpStack 開源 LoRaWAN 網路伺服器堆疊為 LoRaWAN 網路提供開源元件。模組化的架構使得在現有的基礎設施內整合成為可能。 - Arduino
Arduino 是一個基於易於使用的硬體和軟體的開源電子平台。
背景
這篇文章是一個後續項目,建立在相關的技術論壇文章「Set up a private LoRaWAN Sensor Network with Machinechat and Seeed SenseCAP 」的基礎上,文章中使用 Machinechat 和 Seeed SenseCAP 建立一個私有的 LoRaWAN 感測器網絡,其中詳細介紹了使用 DigiKey 上現成的硬體和軟體建立一個私有的 LoRaWAN 物聯網感測器網路。相關專案中使用的軟體包括 Machinechat 的 JEDI Pro 應用軟體和 ChirpStack 的 LoRaWAN 網路伺服器應用程式。專案中使用的硬體包括 Seeed reServer x86 伺服器和 SenseCAP 室外 LoRaWAN 閘道器。對於這篇文章,Seeeduino Xiao, Grove LoRa-E5 板和 Sparkfun 氣象儀套件被設定為在LoRaWAN 網路上報告風速和風向。
Sparkfun 氣象儀表套件風速計和風向標
風速是由杯式風速計測量,旋轉磁鐵導致接觸關閉一次每轉。觸點每秒關閉一次,對應的風速為1.492MPH(2.4km/h)。觸點閉合可以透過風向標電纜上 RJ-11 連接器(接腳 2 和 3)的兩個內導體進行監控。
風向由風向標上的 8 個開關決定,每個開關都有自己的電阻。隨著風向標方向的改變,風向標上的磁鐵會使各個開關閉合。外部電阻器用來設定分壓器,這樣就可以監控電壓輸出來決定風向標的位置。RJ-11 連接器的兩個外部導體(引腳 1 和引腳 4)用於風向標。(註:詳細資料請參閱Sparkfun 氣象儀連接指南)
DigiKey 天氣平台
有關 DigiKey 天氣平台的詳細資訊,請訪問另一文章「DigiKey Weather Platform」。
實現
對於該項目,外部 USB 5V 電源為 Seeeduino Xiao 供電,Xiao 的 3V3 輸出為 Grove LoRa-E5 板和天氣感測器硬體供電。Xiao UART 連接到 Grove LoRa-E5 的 TX/RX 接腳,Xiao 輸入接腳 1 和 2 連接到風感測器風速計和風向標。
Xiao 和 LoRa-E5 板安裝在室外外殼(Hammod 型號 RL6225),+5V 電源和風感測器風速計/風向標使用 M8 面板安裝連接器和電纜組件連接。
Arduino 應用程式碼在 Seeeduino Xiao 上運行,向 LoRa-E5 發送 AT 命令,並監控輸入引腳 1 和 2。應用程式加入 LoRa 網絡,監控輸入引腳以計算風速/風向數據,以 CayenneLPP 格式編碼數據,透過 LoRa 發送數據,延遲並循環返回下一次讀取。下面的原理圖說明了電路是如何連接和實現的(參考 Scheme-it 項目:LoRaE5_Xiao_WindSensorM8cables)。
模組與風感測器連接器 J1 的電氣連接如下圖所示:
Seeeduino Xiao 腳 | Grove LoRa-E5 連接器 | 風感應器連接器 J1 |
---|---|---|
GND | GND | 1 |
3V3 | VCC | 2 |
1 | 4 (風向標) | |
2 | 3 (風速計) | |
6 | RX | |
7 | TX |
設定 Seeeduino Xiao, Grove LoRa-E5 和風速計/風向標
1 - 在 Seeeduino Xiao 上設定 Arduino。參考連結 Seeeduino Xiao 入門
2 - 安裝應用程式所需的庫。透過 Arduino 的庫管理器添加這些庫:
3 - 程式碼演練(檔案名稱:Xiao_LoraE5CayenneLPP_WindRev1.ino)
初始設定
// below code is based on Seeed LoRaE5 example code and modified to NOT use the display on the Seeeduino Xiao expansion board (just uses a Xiao connected to a LoRaE5
// Grove expansion board. Seeed example code at https://wiki.seeedstudio.com/Grove_LoRa_E5_New_Version/
//
//note: all Seeed LoRaE5 Grove boards have example code App key of "2B7E151628AED2A6ABF7158809CF4F3C" so needs to be changed
// this version changes out the DHT11 sensor for Sparkfun weather meter kit for wind speed and direction
// Anemometer measuring code based on below:
//More Information at: https://www.aeq-web.com/
//Version 2.0 | 11-NOV-2020
//
//SBR modifications
//SensorPin is connected to RC pulldown circuit on anemometer switch
//Wind vane pin is connected to voltage divider circuit for vane resistors
#include <Arduino.h>
#include <CayenneLPP.h>
// anemometer and direction parameters
const int RecordTime = 3; //Define Measuring Time (Seconds)
const int SensorPin = 2; // the number of the sensorpin
const int VanePin = 1; // pin# connected to wind vane
const int ledPin = 13; // the number of the LED pin
int InterruptCounter;
float WindSpeed;
float DirWind; //wind direction voltage
float angleWind; //wind vane angle
CayenneLPP lpp(51); //setup Cayenne LPP (low power payload) buffer - per documentation 51 bytes is safe to send
static char recv_buf[512];
static bool is_exist = false;
static bool is_join = false;
static int led = 0;
int buf_size; //Cayenne LPP buffer payload size
int Pointer; //pointer used in Cayenne LPP buffer
int Offset = 12; //offset to where Cayenne LPP payload data starts
int Loop1; //loop counter in LoRa payload builder
int Loop2; //loop counter in LoRa payload builder
int Loop3 = 0; //loop counter in LoRa parameter send
static int at_send_check_response(char *p_ack, int timeout_ms, char *p_cmd, ...)
{
int ch;
int num = 0;
int index = 0;
int startMillis = 0;
va_list args;
memset(recv_buf, 0, sizeof(recv_buf));
va_start(args, p_cmd);
Serial1.printf(p_cmd, args);
Serial.printf(p_cmd, args);
va_end(args);
delay(200);
startMillis = millis();
if (p_ack == NULL)
{
return 0;
}
do
{
while (Serial1.available() > 0)
{
ch = Serial1.read();
recv_buf[index++] = ch;
Serial.print((char)ch);
delay(2);
}
if (strstr(recv_buf, p_ack) != NULL)
{
return 1;
}
} while (millis() - startMillis < timeout_ms);
return 0;
}
static void recv_prase(char *p_msg)
{
if (p_msg == NULL)
{
return;
}
char *p_start = NULL;
int data = 0;
int rssi = 0;
int snr = 0;
p_start = strstr(p_msg, "RX");
if (p_start && (1 == sscanf(p_start, "RX: \"%d\"\r\n", &data)))
{
Serial.println(data);
led = !!data;
if (led)
{
digitalWrite(LED_BUILTIN, LOW);
}
else
{
digitalWrite(LED_BUILTIN, HIGH);
}
}
p_start = strstr(p_msg, "RSSI");
if (p_start && (1 == sscanf(p_start, "RSSI %d,", &rssi)))
{
Serial.println(rssi);
}
p_start = strstr(p_msg, "SNR");
if (p_start && (1 == sscanf(p_start, "SNR %d", &snr)))
{
Serial.println(snr);
}
}
void setup(void)
{
Serial.begin(9600);
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);
// anemeometer and wind vane setup
pinMode(SensorPin, INPUT); //use input to count interrupts of anemometer
pinMode(VanePin, INPUT); //use input to read analog voltage of wind vane output
Serial1.begin(9600); //UART serial1 connection to LoRaE5
Serial.print("E5 LORAWAN TEST\r\n");
if (at_send_check_response("+AT: OK", 100, "AT\r\n"))
{
is_exist = true;
at_send_check_response("+ID: AppEui", 1000, "AT+ID\r\n");
at_send_check_response("+MODE: LWOTAA", 1000, "AT+MODE=LWOTAA\r\n");
//at_send_check_response("+DR: EU868", 1000, "AT+DR=EU868\r\n"); //original
at_send_check_response("+DR: US915", 1000, "AT+DR=US915\r\n");
//at_send_check_response("+CH: NUM", 1000, "AT+CH=NUM,0-2\r\n"); //original
at_send_check_response("+CH: NUM", 1000, "AT+CH=NUM,8-15,65\r\n"); // configure channels to match chirpstack
at_send_check_response("+KEY: APPKEY", 1000, "AT+KEY=APPKEY,\"2B7E151628AED2A6ABF7158809CF4F3C\"\r\n");
at_send_check_response("+CLASS: C", 1000, "AT+CLASS=A\r\n");
at_send_check_response("+PORT: 8", 1000, "AT+PORT=8\r\n");
delay(200);
is_join = true;
}
else
{
is_exist = false;
Serial.print("No E5 module found.\r\n");
}
}
主循環 - 確定風速和風向,以 CayenneLPP 格式編碼並作為 LoRa 有效負載資料發送
void loop(void)
//void loop()
{
// measure windspeed
measure();
// measure wind vane voltage
DirWind = analogRead(VanePin)*3.33;
// calculate wind direction in degrees
vaneAngle();
Serial.print("Windspeed: ");
Serial.print(WindSpeed);
Serial.print(" mph ");
Serial.print("Wind Direction: ");
Serial.print(angleWind);
Serial.println(" degrees");
//***************** start of Cayenne LPP code **************************
// due to character byte limitatations in LoRa payload buffer, need to alternate between
// the two wind parameters when sending out parameter payload
Serial.print("Loop3 = "); //debug code
Serial.println(Loop3); //debug code
if (Loop3 == 0)
{
lpp.reset();
lpp.addAnalogOutput(1, WindSpeed); //channel 1, windspeed value
Loop3 = 1;
}
else
{
lpp.reset();
lpp.addAnalogOutput(2, angleWind); //channel 2, wind direction value
Loop3 = 0;
}
buf_size = lpp.getSize();
Serial.print("Cayenne LPP buffer size = ");
Serial.println(buf_size);
uint8_t *payload = lpp.getBuffer();
char cmd[128];
// Lmsg is just test code that probably can be deleted?
char Lmsg[4];
for (unsigned char i = 0; i < lpp.getSize(); i++)
{
sprintf(Lmsg, "%02X", (char)payload[i]);
Serial.print(Lmsg);
}
Serial.println(" payload");
//**************end of payload builder code**************************
// start of LoRa communication code
if (is_exist)
{
int ret = 0;
if (is_join)
{
ret = at_send_check_response("+JOIN: Network joined", 12000, "AT+JOIN\r\n");
if (ret)
{
is_join = false; //resets join check?
}
else
{
at_send_check_response("+ID: AppEui", 1000, "AT+ID\r\n");
Serial.print("JOIN failed!\r\n\r\n");
delay(5000);
}
}
else
{
char cmd[128];
//sprintf(cmd, "AT+CMSGHEX=\"%04X%04X\"\r\n", (int)temp, (int)humi); //original
//****** add in CayenneLPP ecoding ******
sprintf(cmd, "AT+CMSGHEX=\"%02X\"\r\n", (char)payload[0]); //first part of confirmed message
Serial.println("debug - print cmd at start of loop");
Serial.println(cmd);
// add data payload to LoRa message by looping thru Cayenne LPP data buffer
char TestMsg[2];
for (Loop1 = 0; Loop1 < buf_size; Loop1++)
{
Pointer = (Loop1*2) + Offset;
sprintf(TestMsg, "%02X", (char)payload[Loop1]);
// write data buffer character to LoRa message
for (Loop2 = 0; Loop2 < 2; Loop2++)
{
cmd[Loop2 + Pointer] = TestMsg[Loop2];
}
}
// create end of message characters for LoRa message (need ",return,null characters)
char EndMsg[20];
sprintf(EndMsg, "test\"%02X\"\r\n", (char)payload[0]);
// add ", return and null characters to end of LoRa message
for (unsigned char i = 2; i < 7; i++)
{
cmd[Pointer + i] = EndMsg[5 + i];
}
Serial.println("debug - print cmd at end of loop");
Serial.println(cmd);
//******** end of CayenneLPP encodeding *********
ret = at_send_check_response("Done", 12000, cmd); //sends cmd command over LoRa - increase time from 5000 to 12000
if (ret)
{
recv_prase(recv_buf);
}
else
{
Serial.print("Send failed!\r\n\r\n");
}
delay(5000);
}
Serial.println(" in main loop checking LoRa then wait 5 minutes");
delay(300000); //main delay 300 seconds
}
else
{
delay(1000);
}
}
感測器子程式
//count anemometer pulses and calculate windspeed
void measure() {
InterruptCounter = 0;
attachInterrupt(digitalPinToInterrupt(SensorPin), countup, RISING);
delay(1000 * RecordTime);
detachInterrupt(digitalPinToInterrupt(SensorPin));
WindSpeed = (float)InterruptCounter / (float)RecordTime * 2.4;
}
void countup() {
InterruptCounter++;
}
//determine wind direction angle based on measured wind vane voltage
void vaneAngle() {
angleWind = 99.9;
if (DirWind < 270)
{
angleWind = 112.5;
}
else if ((DirWind >= 270) and (DirWind <= 310))
{
angleWind = 67.5 ;
}
else if ((DirWind > 310) and (DirWind <= 400))
{
angleWind = 90.0 ;
}
else if ((DirWind >= 400) and (DirWind < 600))
{
angleWind = 157.5 ;
}
else if ((DirWind >= 600) and (DirWind < 750))
{
angleWind = 135.0 ;
}
else if ((DirWind >= 750) and (DirWind < 850))
{
angleWind = 202.5 ;
}
else if ((DirWind >= 850) and (DirWind < 1150))
{
angleWind = 180.0 ;
}
else if ((DirWind >= 1150) and (DirWind < 1450))
{
angleWind = 22.5 ;
}
else if ((DirWind >= 1450) and (DirWind < 1900))
{
angleWind = 45.0 ;
}
else if ((DirWind >= 1900) and (DirWind < 2050))
{
angleWind = 247.5 ;
}
else if ((DirWind >= 2050) and (DirWind < 2250))
{
angleWind = 225.0 ;
}
else if ((DirWind >= 2250) and (DirWind < 2500))
{
angleWind = 337.5;
}
else if ((DirWind >= 2500) and (DirWind < 2700))
{
angleWind = 0.0;
}
else if ((DirWind >= 2700) and (DirWind < 2850))
{
angleWind = 292.5;
}
else if ((DirWind >= 2850) and (DirWind < 3000))
{
angleWind = 315.0;
}
else if (DirWind >= 3000)
{
angleWind = 270.0;
}
}
Xiao_LoraE5CayenneLPP_WindRev1.ino 應用程式的最新原始碼位於 github 上,連結如下:
確定Grove LoRa-E5板設備EUI
在 Arduino 中,編譯並上傳 Xiao_LoraE5CayenneLPP_WindRev1。進入 Seeeduino Xiao 並啟用串行監視器。檢查串行監視器輸出以確定 Grove LoRa-E5 設備的 EUI。
將 LoRa-E5 感測器節點加入到 ChirpStack LoRaWAN 網路伺服器中
(注意:這個專案和下面的步驟假設一個基於 ChirpStack 的私有 LoRaWAN 網路是活躍的,並且在 LoRa-E5 感測器節點的範圍內,如果沒有,請參考TechForum 另一文章「Set up a private LoRaWAN Sensor Network with Machinechat and Seeed SenseCAP」。
1 - 在 ChirpStack 中,選擇設備設定檔並建立。名稱設備設定檔「Seeed LoRaE5」,LoRaWAN MAC 版本選擇「1.0.2」,LoRaWAN 區域參數版本選擇「A」,ADR 演算法選擇「Default ADR algorithm」,上行間隔輸入「3600」。在JOIN(OTAA/ABP)標籤中,選取「裝置支援OTAA」。在CODEC 標籤中,在CODEC下拉清單中選擇「Cayenne LPP」。
2 - 在 ChirpStack 中,選擇應用程序,然後選擇「FarmTest」,然後選擇建立。裝置名稱輸入「LoRaE5wind」,裝置描述輸入「description」,Grove LoRa-E5 板輸入「Device EUI」(由上一步驟「Determine Grove LoRa-E5 board device EUI 」確定),裝置設定檔輸入「STM32WL Sensors」,選擇 CREATE DEVICE。(注意:對於初始測試和演示,您可能需要勾選「Disable frame-counter validation」)
3 - 新增設備的應用密鑰。輸入應用鍵「2B7E151628AED2A6ABF7158809CF4F3C」(注意:這是 LoRa-E5 中的預設鍵,更改請參考 LoRa-E5 AT 命令規範的 key 部分)並選擇 SET DEVICE-KEYS。
設定和測試 ChirpStack HTTP整合與 JEDI Pro 通用 LoRaWAN 自訂資料收集器
1 - 在 ChirpStack 中啟用 HTTP 整合。
在 ChirpStack 整合介面中選擇「Add」。
2 - 配置 HTTP 集成
選擇「JSON」為有效載荷編組器(Payload Marshaler),為 Endpoint URL 新增 IP 位址(使用相同的 IP 配置config.yml 檔),然後選擇 ADD INTEGRATION
3 - 拷貝「lorawan-linux.bin 」 和「config.yml 」 到 Ubuntu Linux Mini-PC 上安裝 JEDI Pro 的 ~/jedi/plugins 目錄下。修改配置。 yml 檔案啟用調試,並指定 IP 監聽位址。
(注意:如果你之前已經安裝了 lorawan-linux.bin 和 config. yml 檔案。yml 檔案對於不同的感測器,你所需要做的就是編輯 config.yml 如步驟 5 所示,添加風速和風角度參數的信息)
4 - 在命令列中使用「./lorawan-linux.bin ./config.yml」運行自訂插件。在 Ubuntu Linux Mini-PC 的終端機上運行 yml ,並監控輸出資料。資料應該類似於下面(注意:記得讓 lorawan-linux.bin檔案可執行):
5 - 編輯 config.yml 檔。將 LoRaWAN 資料對應到 JEDI Pro 資料參數並關閉偵錯。對於這個專案範例,編輯 propertyNames 以便 LoRaWAN cSproperty: " anslogOutput.1 “ 對應 mcProperty: “ WindSpeed ” 和 LoRaWAN cSproperty:“ analogOutput.2 ” 對應 mcProperty: “ WindAngle ” 。透過設定「setDebug:」為 false 來關閉偵錯並儲存檔案。
設定 JEDI Pro 自訂資料收集器和資料儀表板
在 JEDI Pro 中,選擇「Settings 設定」選項卡,然後選擇「Data Collectors」,然後選擇「ADD COLLECTOR」。(注意:如果您之前已經將 LoRaWAN 自訂資料收集器新增至 JEDI 應用程式中,則無需執行此步驟)
如下圖所示配置 Collector。將資料收集器命名為「LoRaWAN」(或您喜歡的任何名稱),選擇「收集器類型」為「自訂插件」,選擇「LoRaWAN -linux.bin」作為插件可執行文件,輸入config.yml 文件(例如:「/home/scottr/jedi/plugins/config.yml」為 Plug-in 選項,勾選「Run As Background Process and Monitor」複選框,然後選擇「VALIDATE PLUG-IN」以驗證功能。
在 JEDI Pro 中,選擇「Data Dashboards」,然後選擇「+」來新增一個圖表。
為風速和風角度配置資料圖表,並選擇「Add」以包含在資料儀表板中(參見下面的範例)
結論
Arduino、Seeeduino Xiao、LoRa-E5 無線電和 Sparkfun 的氣象儀套件的組合提供了一個靈活的風感測器平台,可以透過 LoRa 提供風速和風向資料。接著配置 ChirpStack 的 HTTP 整合和 Machinechat 的通用 LoRaWAN 自訂資料擷取器,將風感測器資料匯入 JEDI Pro,用於物聯網資料收集、視覺化、監控和資料儲存。可以根據需要輕鬆修改範例程式碼以用於其他感測器。
參考資料
Seeed - Getting Started with reServer
Seeed - SenseCAP室外閘道器 - LoRaWAN US915MHz
Seeed - 102991155 4G LoRa 閘道器 乙太網路
Seeed - WIO-E5 開發板
Seeed - LoRa-E5 AT命令
Machinechat 的 JEDI One 物聯網平台入門
Machinechat - Set up a private LoRaWAN Sensor Network with Machinechat and Seeed SenseCAP
Sparkfun - 氣象儀表連接指南