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 .. [[/?.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.AutomaticControl) 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