环境监测对于人类来说至关重要,空气质量、土壤构成、环境噪音、温湿度等对我们的农业发展、城市建设、日常生活都会造成影响。以温湿度为例:温湿度高低会影响农作物的生长、影响工程建设进度、影响设备运作,如果我们能够精确地监测温湿度数据、及时作出调整,就能促进整个行业的高效运行。
基于这样的需求考虑,“如何搭建一个良好的环境监测系统”成为了我们需要思考的核心问题。
通常来说,搭建一个良好的环境监测系统,最关键的是要建立实时的环境监测预警系统,并保存完整的历史数据。以产品测试行业为例,当环境不利于产品测试时,只有及时预警,才能采取有效措施调节室内环境,从而避免监测系统损坏。同时也可以根据保存下来的实时数据进行具体分析。
由此可以知道,我们需要的是一套传输距离远、功耗低、还能实时保存数据的环境监测系统。
基于瑞科慧联(RAK)的模块化产品 WisBlock 以及软件平台 RUI 3 搭建出来的 LoRaWAN® 无线环境监测预警系统,恰好符合这一点。WisBlock + RUI3 的环境监测预警系统可以做到全天 24 小时全面准确地进行环境监测。该系统也被广泛地应用在实验室温湿度环境监测、办公区环境监测、商超冰柜温度监测、农场大棚温湿度检测等多个领域。
今天我就带大家一起学习快速地搭建这套 LoRa® 无线温湿度监测系统。
注意:要保持网关与传感器所属的频段相同,本案例我使用的频段均为 CN470。
1、打开 Arduino IDE,进入“文件 > 首选项”
打开 Arduino IDE
2、单击图中图标,修改“附加开发板管理器网址”选项,将 RAK4631-R WisBlock Core 添加中 Arduino 开发板管理器中。
在 Arduino IDE 上修改“附加开发板管理器网址”
3、现在复制下方 URL 并粘贴至下图所示区域。如果已存在其他链接,将上述链接粘贴至新的一行。完成后,单击“好”。
https://raw.githubusercontent.com/RAKWireless/RAKwireless-Arduino-BSP-Index/main/package_rakwireless.com_rui_index.json
在Arduino IDE上粘贴复制好的URL
4、重启 Arduino IDE。进入“工具 > 开发板:“RAK4631” > 开发板管理器”。
重启 Arduino IDE 并执行操作
5、在搜索框中输入“RAK”,窗口会自动出现可用的 RAKwireless WisBlock Core Boards,选择“RAKwireless RUI nRF Boards”并安装。
选择并安装 RAKwireless RUI nRF Boards
6、BSP 安装完成后,根据图中路径选择 RAKwireless WisBlock Core 模块。
选择 RAKwireless WisBlock Core 模块
7、代码烧录
/**
@file Environment_Monitoring ino
@author rakwireless com
@brief This sketch demonstrate how to get environment data from BME680
and send the data to lora gateway
@version 0 1
@date 2020-07-28
@copyright Copyright (c) 2020
**/
#include
#include
#include // Click to install library: http://librarymanager/All#Adafruit_BME680
Adafruit_BME680 bme;
/*************************************
LoRaWAN band setting:
RAK_REGION_EU433
RAK_REGION_CN470
RAK_REGION_RU864
RAK_REGION_IN865
RAK_REGION_EU868
RAK_REGION_US915
RAK_REGION_AU915
RAK_REGION_KR920
RAK_REGION_AS923
*************************************/
#define OTAA_BAND (RAK_REGION_CN470)
#define OTAA_DEVEUI {0x20, 0x17, 0x06, 0x15, 0x0E, 0x00, 0x00, 0x15}
#define OTAA_APPEUI {0x20, 0x17, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00}
#define OTAA_APPKEY {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}
// Might need adjustments
#define SEALEVELPRESSURE_HPA (1010.0)
uint8_t buffer[13]="";
void bme680_get()
{
Serial.print("Temperature = ");
Serial.print(bme.temperature);
Serial.println(" C");
Serial.print("Pressure = ");
Serial.print(bme.pressure / 100.0);
Serial.println(" hPa");
Serial.print("Humidity = ");
Serial.print(bme.humidity);
Serial.println(" %");
Serial.print("Gas = ");
Serial.print(bme.gas_resistance / 1000.0);
Serial.println(" KOhms");
Serial.println();
uint16_t t = bme.temperature * 100;
uint32_t pre = bme.pressure / 100.0 * 100;
uint16_t h = bme.humidity * 100;
uint32_t gas = bme.gas_resistance;
uint32_t i = 0;
//result: T=28.25C, RH=50.00%, P=958.57hPa, G=100406 Ohms
buffer[i++] = 0x01;
buffer[i++] = (uint8_t)(t >> 8);
buffer[i++] = (uint8_t)t;
buffer[i++] = (uint8_t)(h >> 8);
buffer[i++] = (uint8_t)h;
buffer[i++] = (uint8_t)((pre & 0xFF000000) >> 24);
buffer[i++] = (uint8_t)((pre & 0x00FF0000) >> 16);
buffer[i++] = (uint8_t)((pre & 0x0000FF00) >> 8);
buffer[i++] = (uint8_t)(pre & 0x000000FF);
buffer[i++] = (uint8_t)((gas & 0xFF000000) >> 24);
buffer[i++] = (uint8_t)((gas & 0x00FF0000) >> 16);
buffer[i++] = (uint8_t)((gas & 0x0000FF00) >> 8);
buffer[i++] = (uint8_t)(gas & 0x000000FF);
/** Send the data package */
if (api.lorawan.send(sizeof(buffer), (uint8_t *)buffer, 2, true, 1))
{
Serial.println("Sending is requested");
}
else
{
Serial.println("Sending failed");
}
}
void init_bme680()
{
Wire.begin();
if (!bme.begin(0x76)) {
Serial.println("Could not find a valid BME680 sensor, check wiring!");
return;
}
// Set up 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
}
void lora_init()
{
// OTAA Device EUI MSB first
uint8_t node_device_eui[8] = OTAA_DEVEUI;
// OTAA Application EUI MSB first
uint8_t node_app_eui[8] = OTAA_APPEUI;
// OTAA Application Key MSB first
uint8_t node_app_key[16] = OTAA_APPKEY;
if (!api.lorawan.appeui.set(node_app_eui, 8)) {
Serial.printf("LoRaWan OTAA - set application EUI is incorrect! \r\n");
return;
}
if (!api.lorawan.appkey.set(node_app_key, 16)) {
Serial.printf("LoRaWan OTAA - set application key is incorrect! \r\n");
return;
}
if (!api.lorawan.deui.set(node_device_eui, 8)) {
Serial.printf("LoRaWan OTAA - set device EUI is incorrect! \r\n");
return;
}
if (!api.lorawan.band.set(OTAA_BAND)) {
Serial.printf("LoRaWan OTAA - set band is incorrect! \r\n");
return;
}
if (!api.lorawan.deviceClass.set(RAK_LORA_CLASS_A)) {
Serial.printf("LoRaWan OTAA - set device class is incorrect! \r\n");
return;
}
if (!api.lorawan.njm.set(RAK_LORA_OTAA)) // Set the network join mode to OTAA
{
Serial.printf
("LoRaWan OTAA - set network join mode is incorrect! \r\n");
return;
}
if (!api.lorawan.join()) // Join to Gateway
{
Serial.printf("LoRaWan OTAA - join fail! \r\n");
return;
}
/** Wait for Join success */
while (api.lorawan.njs.get() == 0) {
Serial.print("Wait for LoRaWAN join...");
api.lorawan.join();
delay(10000);
}
if (!api.lorawan.adr.set(true)) {
Serial.printf
("LoRaWan OTAA - set adaptive data rate is incorrect! \r\n");
return;
}
if (!api.lorawan.rety.set(1)) {
Serial.printf("LoRaWan OTAA - set retry times is incorrect! \r\n");
return;
}
if (!api.lorawan.cfm.set(1)) {
Serial.printf("LoRaWan OTAA - set confirm mode is incorrect! \r\n");
return;
}
/** Check LoRaWan Status*/
Serial.printf("Duty cycle is %s\r\n", api.lorawan.dcs.get()? "ON" : "OFF"); // Check Duty Cycle status
Serial.printf("Packet is %s\r\n", api.lorawan.cfm.get()? "CONFIRMED" : "UNCONFIRMED"); // Check Confirm status
uint8_t assigned_dev_addr[4] = { 0 };
api.lorawan.daddr.get(assigned_dev_addr, 4);
Serial.printf("Device Address is %02X%02X%02X%02X\r\n", assigned_dev_addr[0], assigned_dev_addr[1], assigned_dev_addr[2], assigned_dev_addr[3]); // Check Device Address
Serial.println("");
}
void setup()
{
//Initialize the built in LED
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
// Initialize Serial for debug output
Serial.begin(115200);
time_t serial_timeout = millis();
while (!Serial)
{
if ((millis() - serial_timeout) < 5000)
{
delay(100);
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
}
else
{
break;
}
}
delay(1000);
Serial.println("=====================================");
Serial.println("Welcome to RAK4630 LoRaWan!!!");
lora_init();
init_bme680();
}
void loop()
{
if (! bme.performReading()) {
Serial.println("Failed to perform reading :(");
return;
}
bme680_get();
delay(10000);
}
本节为将传感器节点连接至 TTN(The Things Network)平台的操作过程。
登录 TTN网站,将会看到以下页面:
TTN 网站的控制台页面
进入添加 Application 界面,注册相关信息后,单击页面底部的“Create application”。
添加应用
进入应用程序概述页面,单击右下角“Add end devices”,在 TTN 平台注册新设备。
应用程序概述页面
在“Register end device”页面中,单击“Manually”页签。
配置 Frequency plan、LoRaWAN version、Regional Parameters version,并添加设备 DevEUI、AppEUI、AppKey。
注册设备
设备频段应与网关频段相同。
DevEUI、AppEUI、AppKey 请在传感器示例代码中获取,切记需要删除各个数字前的“0x”以及“,”。
获取 DevEUI、AppEUI、AppKey
单击"Register and device",完成设备注册,注册结果将汇总如下图所示。
设备信息
重启设备后,设备自动入网并定时向服务器传输实时监测数据。单击See all activity→,查看传输数据。
环境监测数据获取
添加 Datacake 集成到 TTN
具体可参考:https://docs.datacake.de/lorawan/lns/thethingsindustries
在这里,我假设已经设置 Datacake 帐户,创建好了应用程序,并准备添加设备。如果大家没有创建好账户,可以通过 Datacake 文档了解创建步骤。
在 Datacake 应用程序中,单击“设备”以查看设备的概述。单击“添加设备”按钮开始。
在下一个屏幕中,选择 LoRaWAN New device并点击“Next”
选择New product并写入Product Name
然后,向下滚动窗口,直到看到该部分。在此处选择“TTN V3”,选择它,然后单击“下一步”按钮。
在下一步中,我们必须输入节点的 Dev EUI 和名称。Dev EUI 必须与之前在 TTN V3应用程序中选择的节点匹配。输入信息后,再次点击“下一步”按钮。
根据我们在 Datacake 上的帐户,以及已经注册的设备数量,在不同的支付选项之间进行选择。完成此处选择后,按“添加1个设备”按钮。
现在,可以在 Datacake 的概述屏幕中看到新设备。
要完成设备的配置,请在概述中单击它。在下一个窗口中,选择 。Configuration
在配置中,向下滚动,直到看到该部分。在这里,必须要输入我们的自定义有效负载解码器才能获取到达的数据。正如图中所示,解码器返回一个嵌套的 JSON 数组,其中每个 JSON 条目都有两个字段。之后会转发到可视化界面,就能让传感器进行数据传输了。Payload Decoderfieldvaluefieldvalue
为了使事情变得更容易,我准备了一个有效负载解码器,它将会与此示例配合使用,而且它也可以轻松扩展与我们其他的 WisBlock 解决方案一起使用。
为了节省时间,我将代码放在这里。使用以下内容覆盖示例负载解码器:
function Decoder(payload, port) {
if(payload[0] === 0x01) {
return [
{
field: "TEMPERATURE",
value: (payload[1] << 8 | payload[2]) / 100
},
{
field: "HUMIDITY",
value: (payload[3] << 8 | + payload[4]) / 100
},
{
field: "PRESSURE",
value: (payload[8] | (payload[7] << 8) | (payload[6] << 16) | (payload[5] << 24)) / 100
},
{
field: "GAS",
value: payload[12] | (payload[11] << 8) | (payload[10] << 16) | (payload[9] << 24)
},
];
}
}
现在,我们检查解码器是否正常工作。向下滚动并点击“保存”按钮以更新解码器。
然后,一直向上滚动并选择选项卡。如果此时设备处于联机状态并且已发送数据,那么就可以 .DebugDebug Log。
如果这有效,接下来就要做更多的工作了。Datacake 使用字段为可视化查找正确的数据,但必须要先定义所有字段。
返回到配置选项卡并向下滚动,直到看到该部分。到达那里后,单击添加字段按钮。Fields
在窗口中,选择数据类型、字段名称和单位。Add Field
注意: 这里必须与负载解码器中分配的字段名称匹配。因此,对于“温度”,正确的字段名称是“温度”。Identifier。
输入所有信息后,点击“添加字段”按钮。
必须重复此步骤,直到定义了将包含传感器数据的所有四个字段。最后,应该如下所示:Fields
到这里,我们就完成所有的准备工作。现在,我们用一些漂亮的图表来显示数据。
返回设备概述并打开选项卡。目前,它是空的。需要添加新的微件,单击右侧的小编辑图标。Dashboard
现在,我们会看到一个添加小部件按钮。单击它开始。
在下一个屏幕中,会看到一系列不同的可视化选项。我选择了这里。Chart
下一个窗口有四个选项卡。从选项卡开始。就可以在此处输入图表标题。Basic
在 Field 选项卡中,选择 Temperature
现在,它将显示带有温度数据的第一个小部件。我们可以使用微件右下角的小箭头调整大小。
对其他字段重复这些步骤后,就可以看到包含接收数据的图表。主要要单击旁边的黄色图标保存你的操作,然后返回主仪表板视图。HumidityPressureAir QualityUnsaved changes
现在,来自环境传感器的数据以一个很好的可视化效果呈现出来了,我们可以从任何地方访问和查看。
添加Rules
这里我们可以对由设备测量值引起的事件执行操作。它们允许设置不同的触发器和警报,例如:
1、通过电子邮件或短信发送通知消息
2、通过网络挂钩调用外部服务
3、将下行链路发送到 LoRa® 设备
这里可以从工作区的侧边栏菜单创建和访问规则。与设备相关的规则(一个或多个条件引用设备)还会显示在设备的“规则”选项卡中。
通过单击右上角的“添加规则”按钮创建第一个规则。
我们在这里通过设置温度的告警值通过邮件的方式提醒
当温度高于 26℃ 时,Datacake 将会通过邮件的方式通知,而且显示当前的温度
希望这个教程能够对大家有所帮助,如果大家有任何问题,也可以留言告诉我们。
全部0条评论
快来发表一下你的评论吧 !