描述
该项目涵盖了开发基于Seeed LoRa-E5的Arduino风速/方向传感器的过程,并将其安装在能够连接到DigiKey公司总部大楼屋顶气象平台的室外外壳中。该项目还包括将传感器节点添加到现有的基于ChirpStack的私有LoRaWAN网络和Machinechat的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 48eu - 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是一个基于易于使用的硬件和软件的开源电子平台。
背景
这篇文章是一个后续项目,建立在相关的技术论坛文章的基础上,使用 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.492 MPH (2.4 km/h)。触点闭合可以通过风向标电缆上RJ-11连接器(引脚2和3)的两个内导体进行监测。
风向由风向标上的8个开关决定,每个开关都有自己的电阻器。随着风向标方向的改变,风向标上的磁铁会使各个开关闭合。外部电阻器用来设置分压器,这样就可以监测电压输出来确定风向标的位置。RJ-11连接器的两个外部导体(引脚1和引脚4)用于风向标。(注:详细信息请参阅Sparkfun气象仪连接指南)
实现
对于该项目,外部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 pin | Grove LoRa-E5 连接器 | 风传感器连接器 J1 |
---|---|---|
GND | GND | 1 |
3 v3 | 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;
}
}
eewiki/machinechat/blob/master/Seeeduino Xiao/Xiao_LoraE5CayenneLPP_WindRev1.ino
确定Grove LoRa-E5板设备EUI
在Arduino中,编译并上传Xiao_LoraE5CayenneLPP_WindRev1。进入Seeeduino Xiao并启用串行监视器。检查串行监视器输出以确定Grove LoRa-E5设备的EUI。
将LoRa-E5传感器节点添加到ChirpStack LoRaWAN网络服务器中
(注意:这个项目和下面的步骤假设一个基于ChirpStack的私有LoRaWAN网络是活跃的,并且在LoRa-E5传感器节点的范围内,如果没有参考TechForum帖子
使用 Machinechat 和 Seeed SenseCAP 建立一个私有的 LoRaWAN 传感器网络)
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自定义数据收集器
修改ChirpStack,增加HTTP集成,用于将LoRaWAN元数据和传感器数据转发到指定IP地址。Machinechat的通用LoRaWAN自定义数据收集器插件用于监听指定的IP地址并解析LoRaWAN数据以供审查(启用调试时)并在JEDI Pro平台中使用。
(注意:自定义数据收集器包含两个文件:lorawan-linux.bin和config。可从Machinechat获取: https://support.machinechat.io/hc/en-us/articles/6046199010327-Generic-LoRaWAN-Custom-Data-Collector-Beta-for-JEDI-PRO-Linux )
1 - 在ChirpStack中启用HTTP集成。
在ChirpStack集成界面中选择“Add”。
2 - 配置HTTP集成
选择“JSON”为有效载荷封送器,添加IP地址(使用相同的IP配置。为Endpoint URL 添加ip地址,然后选择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中,选择“设置”选项卡,然后选择“数据收集器”,然后选择“添加收集器”。(注意:如果您之前已经将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——使用 Machinechat 和 Seeed SenseCAP 建立一个私有的 LoRaWAN 传感器网络
- Sparkfun -气象仪表连接指南