main.lua
cf_iot=require("cf_iot") common=require("common") json=require("json") mqtt=require("mqtt") hardware=require("hardware") media=require("media") PackagePath=this().PackagePath--当前虚拟机加载用户自定义模块的根目录 LuaLibPath=this().LuaLibPath--当前虚拟机加载用户自定义模块的根目录 package.path = PackagePath .. [[/?/init.lua;]] .. PackagePath .. [[/?/init.lua;]] .. PackagePath .. [[/?.lua;]] .. package.path package.path = LuaLibPath .. [[/?/init.lua;]] .. LuaLibPath .. [[/?.lua;]] .. package.path require "General_Function" require "Parser.Parser_Relay_AiErSai"--继电器分析器 require "OBJ.OBJ_EA"--用于表示加湿器、加热器等电器的一般类别,有开关、更新状态等方法 require "Parser.Parser_Sensor_Temp_Humidity_jdrk"--温湿度传感器分析器 require "OBJ.OBJ_Sensor_Temp_Humidity"--温湿度传感器,有读取、更新等方法 local FirmwareInfo=json.decode(cf_iot.GetFirmwareInfo()) --[[ Config存储各通道的485节点配置新闻 ]] local Config={ mqtt={ Subscribe_Topic="cfiot/cmd_channel",--订阅主题名,接受后台的命令 Publish_Topic="cfiot/data_channel"--发布主题名,报告数据 }, HardwareChannel={ Serial1={ Node_Parser={}, MyDevice={} }, Serial2={ Node_Parser=节点分析器列表 { Node_Address=1, Parser_Relay=Parser_Relay_AiErSai }, { Node_Address=2, Parser_Relay=Parser_Relay_AiErSai }, { Node_Address=3, Parser_Relay=Parser_Sensor_Temp_Humidity_jdrk } }, MyDevice={-我的设备列表 Heater=OBJ_EA:new({-加热器相关属性,通道-节点地址-开关编号- Cmd_Channel="Serial2", Node_Address=1, Switch_Number=1, State="Off", Parser_Relay=Parser_Relay_AiErSai }), Humidifier=OBJ_EA:new({-加湿器相关属性,通道-节点地址-开关编号-继电器分析器 Cmd_Channel="Serial2", Node_Address=1, Switch_Number=2, State="Off", Parser_Relay=Parser_Relay_AiErSai }), ElectricFan=OBJ_EA:new({-电风扇相关属性,通道-节点地址-开关编号- Cmd_Channel="Serial2", Node_Address=2, Switch_Number=1, State="Off", Parser_Relay=Parser_Relay_AiErSai }), Sensor_Temp_Humidity=OBJ_Sensor_Temp_Humidity:new({-温湿度传感器 Cmd_Channel="Serial2", Node_Address=3, Data={ Temp=nil, Humidity=nil }, Parser_Relay=Parser_Sensor_Temp_Humidity_jdrk }) } }, Serial3={ Node_Parser={}, MyDevice={} }, Serial4={ Node_Parser={}, MyDevice={} } } } --[[ ServerConfig存储服务器配置信息 ]] local ServerConfig={ Enable={ AutomaticControl=true } } function HardwareChannel_DataParsing(Channel,data) local Node_Address=data.byteArray[1]-获取数据的节点地址 for key,node in pairs(Config.HardwareChannel[Channel].Node_Parser) do if node.Node_Address==Node_Address then node.Parser_Relay.ParserRouting(data)--开始数据分析路由 break end end for key,Device in pairs(Config.HardwareChannel[Channel].MyDevice) do if Device.Node_Address==Node_Address then Device:UpdateStatus()-更新设备状态 end end end function mqtt_init() --[[ 服务器端json数据格式 { "msgId":"Enable", "data":{ "AutomaticControl":true } } ]] if mqtt.WaitMQTTClientReady()--等待mqtt准备好主通道 then mqtt.AddSubscribeTopic(Config.mqtt.Subscribe_Topic)--订阅mqtt主题 local mqtt_OnMessage = function(msg)--信息处理函数 if (msg.Toppic==Config.mqtt.Subscribe_Topic) then local tb=json.decode(msg.Payload) if tb.msgId=="Enable" then ServerConfig.Enable.AutomaticControl=tb.data.AutomaticControl end end end mqtt.SetOnMessageHandler(LuaVMName,mqtt_OnMessage)--设置消息处理回调函数 end end function hardware_init() hardware.SetSerialDataRoute(1)-设置串口数据路由,0:服务器,1:lua虚拟机 local Hardware_OnEvent= function(data)--硬件事件回调函数 local portName=data.portName local EventName=data.EventName local Message=data.Message local info=portName ..":".. EventName .. ":" .. Message mqtt.Publish(Config.mqtt.Publish_Topic,0,false,info) end hardware.SetHardwareOnEvent(LuaVMName,Hardware_OnEvent) local Serial2_OnData= function(data)--串口2数据处理函数 --16进程字符数组 data.hexArray --byte数组data.byteArray --print(data.hexArray,data.byteArray) HardwareChannel_DataParsing("Serial2",data) end hardware.SetSerialWriteDataSleep("Serial2",50)-设置串口发送数据包后的延迟时间,单位毫秒,延时是在CFIOT在固件中执行,防止粘包 hardware.SetSerialOnDataHandler(LuaVMName,"Serial2",Serial2_OnData) hardware.SerialOpen("Serial2",9600,8,"None",1)--打开串口2 end function main() Debug=true LuaVMName=this().LuaVMName--目前虚拟机的名称 cf_iot.SetDebug(LuaVMName,Debug) cf_iot.SetWatchdogTimeout(LuaVMName,300) mqtt_init() hardware_init() --看门狗定时器 cf_iot.AddTimer(LuaVMName,"MainTimer",5000,function() cf_iot.Watchdog(LuaVMName,1)-发送看门狗信号 end) --信号采集定时器 cf_iot.AddTimer(LuaVMName,"SignalAcquisitionTimer",25000,function() --采集温湿度 local Channel=Config.HardwareChannel["Serial2"] Channel.MyDevice.Sensor_Temp_Humidity:ReadData() Channel.MyDevice.Heater:ReadData() Channel.MyDevice.ElectricFan:ReadData() end) --定时器的自动控制 cf_iot.AddTimer(LuaVMName,"AutomaticControlTimer",20000,function() if not(ServerConfig.Enable.AutomaicControl) then
return
end
local Channel=Config.HardwareChannel["Serial2"]
local Temp=Channel.MyDevice.Sensor_Temp_Humidity.Data.Temp
local Humidity=Channel.MyDevice.Sensor_Temp_Humidity.Data.Humidity
if Temp==nil or Humidity==nil then
return
end
if Temp<20.5 then
Channel.MyDevice.Heater:Power("On")--打开加热器
Channel.MyDevice.ElectricFan:Power("On")--打开电风扇吹热风
elseif Temp>=20.5 then
Channel.MyDevice.Heater:Power("Off")--关闭加热器
Channel.MyDevice.ElectricFan:Power("Off")--关闭电风扇
end
if Humidity<=50 then
Channel.MyDevice.Humidifier:Power("On")--打开加湿器
elseif Humidity>=55 then
Channel.MyDevice.Humidifier:Power("Off")--关闭加湿器
end
end)
--数据上传定时器
cf_iot.AddTimer(LuaVMName,"UploadDataTimer",30000,function()
local Channel=Config.HardwareChannel["Serial2"]
local data={
IMEI=FirmwareInfo.ModuleState.IMEI,
CCID=FirmwareInfo.ModuleState.CCID,
State={
Heater=Channel.MyDevice.Heater.State,
ElectricFan=Channel.MyDevice.ElectricFan.State,
Humidifier=Channel.MyDevice.Humidifier.State
},
Sensor={
Temp=Channel.MyDevice.Sensor_Temp_Humidity.Data.Temp,
Humidity=Channel.MyDevice.Sensor_Temp_Humidity.Data.Humidity
}
}
mqtt.Publish(Config.mqtt.Publish_Topic,0,false,json.encode(data))
end)
end
OBJ_EA.lua
--[[
定义一个电器类
Node_Address:节点地址,即485控制器上的总线地址
Switch_Number:开关编号,即继电器控制板上的第几路开关
Cmd_Channel:命令通道,即发送开关命令所用的通道,对应控制网关上的串口TTL电平
]]
OBJ_EA={--相关属性
Cmd_Channel="Serial2",
Node_Address=1,
Switch_Number=1,
State="Off",
Parser_Relay=nil
}
function OBJ_EA:Power(v)
--为加热器Heater增加一个控制开关
--v:电源选项,On:开机,Off:关机
--local info=self.Node_Address .. ":" .. self.Switch_Number .. "" .. v
--print(info)
local cmd=self.Parser_Relay.get_Switch_Cmd(v,self.Node_Address,self.Switch_Number)
if cmd~=nil then
hardware.SerialWriteDataByte(self.Cmd_Channel,cmd)
--General_Function.printTable(cmd)
end
end
function OBJ_EA:ReadData()
--下发读取继电器状态数据
local cmd=self.Parser_Relay.get_ReadData_Cmd(self.Node_Address)
if cmd~=nil then
hardware.SerialWriteDataByte(self.Cmd_Channel,cmd)
--General_Function.printTable(cmd)
end
end
function OBJ_EA:UpdateStatus()
--更新状态
if self.Parser_Relay.data["g"..self.Switch_Number]==1 then
self.State="On"
else
self.State="Off"
end
end
-- 派生类的方法 new
function OBJ_EA:new (obj)
setmetatable(obj, self)
self.__index = self
return obj
end
return OBJ_EA
OBJ_Sensor_Temp_Humidity.lua
--[[
定义一个温湿度传感器
Node_Address:节点地址,即485控制器上的总线地址
Switch_Number:开关编号,即继电器控制板上的第几路开关
Cmd_Channel:命令通道,即发送开关命令所用的通道,对应控制网关上的串口TTL电平
]]
OBJ_Sensor_Temp_Humidity={--相关属性
Cmd_Channel="Serial2",
Node_Address=1,
Parser_Relay=nil,
Data={
Temp=nil,
Humidity=nil
}
}
function OBJ_Sensor_Temp_Humidity:ReadData()
--下发读取温湿度数据
local cmd=self.Parser_Relay.get_ReadData_Cmd(self.Node_Address)
if cmd~=nil
then
hardware.SerialWriteDataByte(self.Cmd_Channel,cmd)
--General_Function.printTable(cmd)
end
end
function OBJ_Sensor_Temp_Humidity:UpdateStatus()
--更新温湿度数据
if self.Parser_Relay ~= nil
then
self.Data.Temp=self.Parser_Relay.data.Temp
self.Data.Humidity=self.Parser_Relay.data.Humidity
end
end
-- 派生类的方法 new
function OBJ_Sensor_Temp_Humidity:new (obj)
setmetatable(obj, self)
self.__index = self
return obj
end
return OBJ_Sensor_Temp_Humidity
Parser_Relay_AiErSai.lua
Parser_Relay_AiErSai={}
--[[
data:每路继电器状,一个板卡最多支持8路继电器
]]
Parser_Relay_AiErSai.data={
g1=0,
g2=0,
g3=0,
g4=0,
g5=0,
g7=0,
g8=0
}
--[[
get_Switch_Cmd(data)
功能:打开继电器
参数:data={hexArray={},byteArray={}}
]]
function Parser_Relay_AiErSai.get_Switch_Cmd(v,Node_Address,Switch_Number)
if not(Switch_Number>=1 and Switch_Number<=8)
then
return nil
end
local s=0x00
if v=="On"
then
s=0xff
elseif v=="Off"
then
s=0x00
end
local cmd={Node_Address,5,0,(Switch_Number-1),s,0}
local r=common.CRC16(cmd)
table.insert(cmd, r[1])
table.insert(cmd, r[2])
return cmd
end
--[[
get_ReadData_Cmd(data)
功能:获取继电器状态
参数:data={hexArray={},byteArray={}}
]]
function Parser_Relay_AiErSai.get_ReadData_Cmd(Node_Address)
local cmd={Node_Address,0x01,0x00,0x00,0x00,0x08}
local r=common.CRC16(cmd)
table.insert(cmd, r[1])
table.insert(cmd, r[2])
return cmd
end
--[[
ParserRouting(data)
功能:解析器路由,根据功能码选择数据解析函数
参数:data={hexArray={},byteArray={}}
]]
function Parser_Relay_AiErSai.ParserRouting(data)
--数据CRC16验证
if not(General_Function.DataChecking(data))
then
return
end
--解析器路由
local cmdID=data.hexArray[2]
if(cmdID=="01") then
Parser_Relay_AiErSai.ParserState(data)
end
if(cmdID=="05") then
Parser_Relay_AiErSai.ParserState1(data)
end
end
--[[
ParserState(data)
作用:解析继电器状态
]]
function Parser_Relay_AiErSai.ParserState(data)
local t,s=common.byte2bin(data.byteArray[4])
Parser_Relay_AiErSai.data.g1=t[8]
Parser_Relay_AiErSai.data.g2=t[7]
Parser_Relay_AiErSai.data.g3=t[6]
Parser_Relay_AiErSai.data.g4=t[5]
Parser_Relay_AiErSai.data.g5=t[4]
Parser_Relay_AiErSai.data.g6=t[3]
Parser_Relay_AiErSai.data.g7=t[2]
Parser_Relay_AiErSai.data.g8=t[1]
--General_Function.printTable(Parser_Relay_AiErSai.data)
end
function Parser_Relay_AiErSai.ParserState1(data)
local state=data.hexArray[5]..data.hexArray[6]
if (state=="ff00") then
Parser_Relay_AiErSai.data["g"..(data.byteArray[4]+1)]=1
else
Parser_Relay_AiErSai.data["g"..(data.byteArray[4]+1)]=0
end
end
function Parser_Relay_AiErSai.test()
local data={
hexArray={"ff","01","01","01","a1","a0"},
byteArray={255,1,1,1,161,160}
}
Parser_Relay_AiErSai.ParserRouting(data)
end
return Parser_Relay_AiErSai
Parser_Sensor_Temp_Humidity_jdrk.lua
--[[
建大仁科温湿度传感器解析器
]]
Parser_Sensor_Temp_Humidity_jdrk={}
--[[
data:温度和湿度
]]
Parser_Sensor_Temp_Humidity_jdrk.data={
Temp=nil,
Humidity=nil
}
--[[
get_ReadData_Cmd(data)
功能:获取读温湿度的数据包
参数:data={hexArray={},byteArray={}}
]]
function Parser_Sensor_Temp_Humidity_jdrk.get_ReadData_Cmd(Node_Address)
local cmd={Node_Address,0x03,0x00,0x00,0x00,0x02}
local r=common.CRC16(cmd)
table.insert(cmd, r[1])
table.insert(cmd, r[2])
return cmd
end
--[[
ParserRouting(data)
功能:解析器路由,根据功能码选择数据解析函数
参数:data={hexArray={},byteArray={}}
]]
function Parser_Sensor_Temp_Humidity_jdrk.ParserRouting(data)
--数据CRC16验证
if not(General_Function.DataChecking(data))
then
return
end
--解析器路由
local cmdID=data.hexArray[2]
if(cmdID=="03") then
Parser_Sensor_Temp_Humidity_jdrk.ParserTempAndHumidity(data)
end
end
--[[
ParserTempAndHumidity(data)
作用:解析温湿度
03 03 04 01 BC 00 7F 58 0B
]]
function Parser_Sensor_Temp_Humidity_jdrk.ParserTempAndHumidity(data)
Parser_Sensor_Temp_Humidity_jdrk.data.Humidity=common.H_To_O(data.hexArray[4]..data.hexArray[5]) / 10
local t,s=common.byte2bin(data.byteArray[6])
if t[1]==0
then
Parser_Sensor_Temp_Humidity_jdrk.data.Temp=common.H_To_O(data.hexArray[6]..data.hexArray[7]) /10
else
Parser_Sensor_Temp_Humidity_jdrk.data.Temp=common.H_To_NgO(data.hexArray[6]..data.hexArray[7]) /10 * -1
end
end
return Parser_Sensor_Temp_Humidity_jdrk
General_Function.lua
General_Function={}
function General_Function.printTable(tb)
for k, v in pairs(tb) do
print(k,v)
end
end
--[[
LUA 5.1 CRC16/Modbus 校验 纯LUA实现
https://blog.csdn.net/u013625451/article/details/103241737
性能不行,不建议使用,可以使用cf_iot提供的
]]
function General_Function.And(num1,num2)
local tmp1 = num1
local tmp2 = num2
local ret = 0
local count = 0
repeat
local s1 = tmp1 % 2
local s2 = tmp2 % 2
if s1 == s2 and s1 == 1 then
ret = ret + 2^count
end
tmp1 = math.modf(tmp1/2)
tmp2 = math.modf(tmp2/2)
count = count + 1
until(tmp1 == 0 and tmp2 == 0)
return ret
end
function General_Function.Xor(num1,num2)
local tmp1 = num1
local tmp2 = num2
local ret = 0
local count = 0
repeat
local s1 = tmp1 % 2
local s2 = tmp2 % 2
if s1 ~= s2 then
ret = ret + 2^count
end
tmp1 = math.modf(tmp1/2)
tmp2 = math.modf(tmp2/2)
count = count + 1
until(tmp1 == 0 and tmp2 == 0)
return ret
end
function General_Function.bit_rshift(value,n)
value = math.modf(value / (2^n))
return value
end
function General_Function.CRC16(arr)
local tmp = 0xffff
for i=1,#arr do
tmp = General_Function.Xor(arr[i],tmp)
for j=1,8 do
local tmp1 = General_Function.And(tmp,0x01)
if tmp1 == 1 then
tmp = General_Function.bit_rshift(tmp,1)
tmp = General_Function.Xor(tmp,0xa001)
else
tmp = General_Function.bit_rshift(tmp,1)
end
end
end
local ret1 = (tmp % 256)
local ret2 = math.modf( tmp / 256)
return {ret1,ret2}
end
--[[
https://stackoverflow.com/questions/24821045/does-lua-have-something-like-pythons-slice
]]
function General_Function.slice(tbl, first, last, step)
local sliced = {}
for i = first or 1, last or #tbl, step or 1 do
sliced[#sliced+1] = tbl[i]
end
return sliced
end
--[[
http://love2d.org/forums/viewtopic.php?f=4&t=84988&start=10&__cf_chl_captcha_tk__=6d04f55cf83896b2cb788fe655b479ab248a7f14-1610387423-0-AWqM-FI75XLbX1zsV8rb6xSnmZ0Ko7qZweX5EBFhNkG2oLcBX9SlHAA-W-R-CnqDhbC9Ju8bQ0nhEfDk0ixd24bynxLM4rEGwo8zedXQA6izYB445Wo374uui9pYxPcWQRijBZqoQkhuuHoC5DQU4hsf5H9r_ABQiaTlXVh6bSC83Zy9SuOkfOrQyiBwoIVK4DyZsgvLd80lKnisBbJ-Swdg4n8XybtXayXNJDxHr7PBDBzVggprEhBI2phDYF5X6bEEfeLd8ZBOalNT0E-tMxTOL-kc6X-JGkuhC0SkVz6KnX2GR104z42ZEjpwcx0QclscEXBppazk8sFlEAQTRdjwINJ6HyMCxI0LzNu1efaPJ8AdduM_aGBq5owK6gx9NstnS58Y7nWNEyb_aSZw-CzlbgkV7IyyokkckLBhYTTwFUr2p-Jvz4Izm6dvPub8JRJr-xqYQ63Xq8xjweeAeEV0kdoiNTmEVoVHIJbwotDeVf0CM63SqIrfr3yvLRVOvxupr3SAs0BRoJAc50_qkCHd_7l3hy1TIxEURZPBkswzuEb5lftCOGiPkGXnCRkrzNAyZg5Jp80Z9WMRxDiLizZkt8snWxjQ8Dth8mZGRGb5N-SmSsoXrzFzxb5PQjLT1KxyWB1iIwkxqK_wq9uJJHA
https://www.cnblogs.com/v5captain/p/11739414.html
]]
--10进制转换2进制
function General_Function.byte2bin(n)
local t = {}
for i=7,0,-1 do
t[#t+1] = math.floor(n / 2^i)
n = n % 2^i
end
return t,table.concat(t)
end
--10进制转换2进制,可以表示大数
function General_Function.byte2bin1(n)
local t, d = {}, 0
d = math.log(n)/math.log(2) -- binary logarithm
for i=math.floor(d+1),0,-1 do
t[#t+1] = math.floor(n / 2^i)
n = n % 2^i
end
return t,table.concat(t)
end
--[[
DataChecking(data)
作用:检测数据有效性
]]
function General_Function.DataChecking(data)
if #data.byteArray<3
then
return false
end
local len=#data.byteArray
local array=General_Function.slice(data.byteArray,1,len-2)
local r=common.CRC16(array)
if (r[1]==data.byteArray[len-1] and r[2]==data.byteArray[len])
then
return true
else
return false
end
end
return General_Function