简介:
该设备是一款基于《ShineBlink 低代码核心模组》开发的《4G_Lora远程土壤环境监测器》设备,可实现4G或Lora远程获取土壤环境信息(温度/水分/电导率/PH)的功能。
功能:
- 支持土壤环境(温度/水分/电导率/PH)信息采集
- 支持 4G cat1 通讯
- 支持 TCP + Json 接入云端
- 支持 MQTT + Json 接入云端
- 支持ShineBlink免开发云+小程序
- 支持 Lora 通讯(可选)
- 支持 uA 级低功耗休眠(可选)
- 支持 GPS 定位及对时(可选)
应用领域:
-
智慧农业
-
智慧养殖
-
智慧气象/水文
-
智慧城市
开源资料地址:
FlexLua SmartSensor: 本仓库包含各种4G/Lora/WiFi/RS485 Modbus远程采集传感器数据的项目。
----------------------配置信息开始----------------------
--Part1: 系统配置
SysMode = 0 --0:4G_TCP, 1:4G_Mqtt, 2:4G_ShineBlinkCloud, 3:LoraModbusHex
SysGpsUse = "NO_GPS" --NO_GPS: 不带GPS功能, AUTO_AGPS:带GPS功能
SysWorkInterval = 10 --单位秒,系统采集传感器并发送数据的间隔周期。
--如果SysSleepEn=1,则SysWorkInterval周期间隔内会断电进入低功耗状态
SysSleepEn = 0 --0:不使用低功耗休眠 1:使用。该功能必须外挂BateryFriend才有用
--SysMyID可任意设定,用作设备的唯一标识,
--也可以用LIB_GetSysUniID()函数获取的芯片唯一ID,形如"761A6617E803F78402"
SysMyID = "Test01" -- SysMyID = LIB_GetSysUniID()
--Part2: Tcp参数(SysMode=0时才有用)
TcpServerIp = "122.114.122.174" --需要连接的服务器IP地址
TcpServerPort = 38556 --需要连接的服务器端口号
--Part3: MQTT参数(SysMode=1时才有用)
MqttServerAddr = "mqtt.ctwing.cn" --天翼云MQTT服务器ip地址或域名
MqttServerPort = 1883 --MQTT服务器端口号
MqttClientID = "15589964DTU01" --产品ID"15589964" + 设备编号"DTU01"
MqttUserName = "ShineBlink" --建议填写为用户自己的天翼物联网平台(AIoT)用户名
MqttPassword = "lMmugH2yURmY2uJqkNby-zQHGJ67ngjMAYR6kkGQmko" --采用的一型一密,特征串
MqttSubTopic = "device_control"--订阅用,用于接收服务器下发的数据
MqttPubTopic = "$device_send" --发布用,用于向服务器发送数据
--Part4: Lora参数(SysMode=3时才有用)
--Lora从机(slave)地址(范围:0-65535)
LoraAddr = 0x01
--Lora对端中心点(center)地址(范围:0-65535)
LoraTargetAddr = 0x00
--Lora通信信道(范围:410-441MHz),相互通信的Lora节点必须在同一信道
LoraChannel = 433
--Lora无线速率(范围:0.3, 1.2, 4.8, 9.6, 19.2kpbs),速率越低通信质量越高
LoraBaudrate = "9.6kpbs"
--Lora发射功率(范围:11, 14, 17, 20dB),该值越大通信质量越高,但功耗会增加
LoraTxPwr = "20dB"
--Part5: Modbus RS485传感器配置
MbAddr = 0x01 --传感器出厂默认modbus地址, 1~250
MbBaudRate = "BAUDRATE_4800" --485总线的通信速率
--Part6: 传感器相关的配置(用户无需更改)
UI = "[1_Wa_%_0_1000][1_Te_C_-400_800][1_Us_us_0_20000][1_Ph_01ph_30_900]" --UI必须和SensorStrFormat一致
SensorStrFormat = "\"Wa\":%d,\"Te\":%d,\"Us\":%d,\"Ph\":%d"
SensorHeatTime = 30 --读取传感器前需等待传感器预热30秒
SensorStrInvalid = string.format(SensorStrFormat, 0,0,0,0) --默认非法值
----------------------配置信息结束----------------------
--定义通过 Modbus协议 485读取传感器数值函数
function GetSensorValue()
local res,wa,te,us,ph,str
print("Tx:"..LIB_HexTabToHexStr(LIB_MbRtuMasterSendTrans("03", MbAddr, 0, 4)))
--下发读保持寄存器命令(设备地址=MbAddr,起始地址=0,读取的寄存器个数=4)
--土壤环境传感器寄存器地址0000H~0003H存储:含水量,温度值,电导率,PH值
LIB_Uart1BlockSend(LIB_MbRtuMasterSendTrans("03", MbAddr, 0, 4))
--等待0.2秒的应答时间
LIB_DelayMs(200)
--判断刚刚的命令是否收到应答数据
recv_flag,recv_tab = LIB_Uart1Recv()
if recv_flag == 1 then
--解析从机发来的应答数据
result,r = LIB_MbRtuMasterRecvTrans("03",recv_tab)
print("Rx:"..LIB_HexTabToHexStr(recv_tab))
if result > 0 then --获取传感器结果
res = 1
wa = r[1] --含水率,单位:0.1%
te = LIB_BC("BYTE16_I16", r[2]) --温度,单位:0.1℃
us = r[3] --电导率,单位:1us
ph = r[4] --PH值,单位:0.1
else
err_code = -128 - result
print(string.format("err=%d, data=%s",err_code, LIB_HexTabToHexStr(recv_tab)))
res = 0
end
else
print("Sensor unconnected!")
res = 0
end
if res == 1 then
str = string.format(SensorStrFormat, wa,te,us,ph)
else
str = SensorStrInvalid
end
return res,str
end
--定义通过Modbus协议向BatteryFriend获取电池电压的函数
function GetBatteryFriendVoltage()
local res
local vol
print("Tx:"..LIB_HexTabToHexStr(LIB_MbRtuMasterSendTrans("03", 254, 1000, 1)))
--下发读保持寄存器命令(设备地址=254,起始地址=1000,个数=1)
--BatteryFriend的寄存器地址1000存放着电池电压,单位0.1v
LIB_Uart1BlockSend(LIB_MbRtuMasterSendTrans("03", 254, 1000, 1))
--等待0.2秒的应答时间
LIB_DelayMs(200)
--判断刚刚的命令是否收到应答数据
recv_flag,recv_tab = LIB_Uart1Recv()
if recv_flag == 1 then
--解析从机发来的应答数据
result,r = LIB_MbRtuMasterRecvTrans("03",recv_tab)
print("Rx:"..LIB_HexTabToHexStr(recv_tab))
if result > 0 then --获取传感器结果
res = 1
vol = r[1]*0.1
else--打印modbus应答错误码以及应答数据的Hex形式
err_code = -128 - result
print(string.format("err=%d, data=%s",err_code, LIB_HexTabToHexStr(recv_tab)))
res = 0
vol = 0.0
end
else
print("BatteryFriend unconnected!")
res = 0
vol = 0.0
end
return res,vol
end
--定义通过Modbus协议向BatteryFriend设置低功耗休眠时间,单位:秒
function SetBatteryFriendSleep(Ns)
local res
print("Tx:"..LIB_HexTabToHexStr(LIB_MbRtuMasterSendTrans("06", 254, 1001, Ns)))
--下发写单个寄存器命令(设备地址=254,起始地址=1001,休眠时间=Ns秒)
--BatteryFriend的寄存器地址1001用来设置休眠时间,单位1秒
LIB_Uart1BlockSend(LIB_MbRtuMasterSendTrans("06", 254, 1001, Ns))
--等待0.2秒的应答时间
LIB_DelayMs(200)
--判断刚刚的命令是否收到应答数据
recv_flag,recv_tab = LIB_Uart1Recv()
if recv_flag == 1 then
--解析从机发来的应答数据
result,r = LIB_MbRtuMasterRecvTrans("06",recv_tab)
print("Rx:"..LIB_HexTabToHexStr(recv_tab))
if result > 0 then --获取传感器结果
res = 1
else--打印modbus应答错误码以及应答数据的Hex形式
err_code = -128 - result
print(string.format("err=%d, data=%s",err_code, LIB_HexTabToHexStr(recv_tab)))
res = 0
end
else
print("BatteryFriend unconnected!")
res = 0
end
return res
end
--定义10毫秒定时器的回调函数,函数名字必须是LIB_10msTimerCallback
function LIB_10msTimerCallback()
timer_ms = timer_ms + 10
timer1_ms = timer1_ms + 10
timer_cnt = timer_cnt + 10
LIB_GpioToggle("D11") --喂硬件看门狗
--每隔0.5秒根据4G模状态更新LED灯颜色
if timer_ms >= 500 then
timer_ms = 0
State,IMSI,ICCID,CSQ = LIB_Cat1StatusQuery()
if State == "PowerOn" then --4G模块已上电但没发现sim卡
RGB(0,0,0) --白
elseif State == "SimOK" then --4G模块发现sim卡
RGB(0,1,1) --红
elseif State == "Attached" then --4G模块已连上附近的基站
RGB(1,1,0) --蓝
elseif State == "Connected" then --4G模块已和服务器连结
RGB(1,0,1) --绿
else --4G模块还未开始工作
RGB(1,1,1) --全灭
end
end
end
--延时N秒函数
function DelayS(N)
timer1_ms = 0
target_ms = N*1000
while timer1_ms <= target_ms do
end
end
--RGB三色灯控制函数
function RGB(R,G,B)
LIB_GpioWrite("D0",R)
LIB_GpioWrite("D1",G)
LIB_GpioWrite("D3",B)
end
---------------------系统初始化开始-----------------------
--LIB_SystemLogEnable() --需要看详细log.txt日志时才用
--配置D0,D1,D3为普通GPIO输出,控制LED_R,LED_G,LED_B
LIB_GpioOutputConfig("D0","STANDARD")
LIB_GpioOutputConfig("D1","STANDARD")
LIB_GpioOutputConfig("D3","STANDARD")
RGB(1,1,1) --一上电Led三色灯默认都不亮
--配置D11为普通输出,控制看门狗
LIB_GpioOutputConfig("D11","STANDARD")
--配置Uart1开始工作,并且D8引脚作为自动切换485收发芯片的引脚
--当用LIB_Uart1BlockSend发送时,D8会自动变成低电平,发送完后会立刻变成高电平
LIB_Uart1Rs485Config(MbBaudRate,"D8")
if SysMode == 0 then --4G_TCP
--配置4G模组需要连接的tcp服务器IP,端口号,心跳包间隔时间0秒(不使用心跳机制)。
LIB_Cat1TcpUdpEC800Config("UART0","D5","HIGH","D6","HIGH",TcpServerIp,TcpServerPort,0,"TCP",SysGpsUse)
elseif SysMode == 1 then --4G Mqtt
--设置4G模块占用TX0、RX0、D5、D6引脚,MQTT模式,KeepAlive周期180秒,不开启GPS功能
LIB_Cat1MqttEC800Config("UART0","D5","HIGH","D6","HIGH",MqttServerAddr,MqttServerPort,MqttClientID,MqttUserName,MqttPassword,180,MqttSubTopic,"QOS0",SysGpsUse)
elseif SysMode == 2 then --4G ShineBlink Cloud
--设置外部4G模块连接云
LIB_CloudConfig("S0", "4G_EC800", 0, "SBK_01", UI, SysGpsUse)
elseif SysMode == 3 then -- Lora Modbus Hex transport
--设置Lora参数
LIB_LoraConfig("UART0","D5","D6",LoraAddr,LoraChannel,LoraBaudrate,LoraTxPwr)
else
end
--使能系统10毫秒定时器开始工作
timer_ms = 0
timer1_ms = 0
timer_cnt = 0
LIB_10msTimerConfig("ENABLE")
---------------------系统初始化结束-----------------------
--开始大循环
while(GC(1) == true)
do
--------------步骤(一)通过RS485 Modbus RTU协议读取传感器的值-----------------
DelayS(SensorHeatTime) --读取传感器前的预热时间(秒)
SensorStr = nil
result,str = GetSensorValue()
if result == 1 then
--获取传感器json对象字符串
SensorStr = str
end
-------------步骤(二)获取GPS定位信息和UTC时间(EC800M 4G模组才具备此功能)-------------
GpsAndUtcStr = nil
if SysGpsUse == "AUTO_AGPS" then
timer_cnt = 0
while(timer_cnt < 60000)--最多等待60秒给GPS搜星定位时间
do
--如果收到GPS信息
flag,lo,la,al,utc,r6,r7,r8,r9,r10,r11,r12,r13,r14,r15,r16 = LIB_Cat1GpsQuery()
if flag == 1 then
--生成json对象字符串
GpsAndUtcStr = string.format("\"Longtitude\":%.1f, \"Latitude\":%.1f, \"UTC\":%d", lo,la,utc)
timer_cnt = 60000 --既然获取到GPS信息了就没必要停留了,尽早退出循环
end
end
end
-------------步骤(三)获取电池电压信息(如果外挂了BatteryFriend电池低功耗管理器)-------------
--通过Modbus RTU协议从BatteryFriend读取电池电压值
BatteryVolStr = nil
if SysSleepEn == 1 then
result,batvol = GetBatteryFriendVoltage()
if result == 1 then
--生成json对象字符串
BatteryVolStr = string.format("\"BatVol\":%.1f", batvol)
end
end
--------------步骤(四)构造需要发送的Json字符串---------------------------
JsonStr = string.format("{\"Uid\":\"%s\"", SysMyID)
if GpsAndUtcStr ~= nil then
JsonStr = JsonStr..","..GpsAndUtcStr
end
if BatteryVolStr ~= nil then
JsonStr = JsonStr..","..BatteryVolStr
end
if SensorStr ~= nil then
JsonStr = JsonStr..","..SensorStr
JsonStrForShineBlinkCloud = "{"..SensorStr.."}"
else
JsonStrForShineBlinkCloud = "{"..SensorStrInvalid.."}"
end
JsonStr = JsonStr.."}"
--最终合成JsonStr字符串形如:
--{"Uid":"Test01","Longtitude":119.2, "Latitude":26.1, "UTC":1679488305,"Temperature":25.2, "Humidity":76.7}
print("JsonStr:"..JsonStr)
print("SbcJsonStr:"..JsonStrForShineBlinkCloud)
--------------步骤(五)通过4G或Lora向远端服务器发送数据-----------------
Connect = 0
--前三种工作模式都需要判断4G模组和服务器建立连接后才能发送
if SysMode <= 2 then
timer_cnt = 0
while(timer_cnt < 60000)--最多等待60秒
do
State,IMSI,ICCID,CSQ = LIB_Cat1StatusQuery()
if State == "Connected" then
timer_cnt = 60000 --既然可以发送4G数据了,就赶紧退出循环
Connect = 1
end
end
else --Lora发送不需考虑connect
Connect = 1
end
if Connect == 1 then
if SysMode == 0 then --4G TCP 发送Json传感器数据
LIB_Cat1TcpUdpEC800Send(LIB_StrToTab(JsonStr))
elseif SysMode == 1 then --4G Mqtt 发送Json传感器数据
LIB_Cat1MqttEC800SendPub("QOS0", MqttPubTopic, JsonStr)
elseif SysMode == 2 then --4G 向ShineBlink云发送Json传感器数据
LIB_CloudSend(JsonStrForShineBlinkCloud)
elseif SysMode == 3 then --通过Lora向中心节点发送Json字符串
LIB_LoraSend(LoraTargetAddr, LIB_StrToTab(JsonStr))
RGB(1,0,1) --绿灯闪0.05秒
LIB_DelayMs(50)
RGB(1,1,1)
else
end
DelayS(2) --等待2秒让消息发送出去
end
--------------步骤(六)判断是否需要进入低功耗休眠(需外挂BatteryFriend电池低功耗管理器)-----------------
if SysSleepEn == 1 then
--通过Modbus RTU协议向BatteryFriend下发休眠指令及时长(SysWorkInterval秒)
result = SetBatteryFriendSleep(SysWorkInterval)
if result == 0 then
print("Can not enter sleep!")
end
end
--如果上面没有成功进入低功耗休眠掉电(没有外挂BatteryFriend)
--则延时SysWorkInterval秒后重新执行本循环
DelayS(SysWorkInterval)
end