概述
CFIOT内置了丰富的API接口,以模块的形式类聚这些API接口,并导入到内置的LUA虚拟机,供用户的LUA脚本调用,用户可使用CFIOT云端通讯协议远程更新LUA脚本和管理虚拟机,如关闭、启动虚拟机,以及清除脚本等操作。
CFIOT支持多个LUA虚拟机并行运行,充分发挥多核CPU并行计算优势,虚拟机个数由用户的LUA脚本目录数决定,虚拟机之间可以相互通信。
CFIOT目前提供了json、basic、mqtt、hardware、websocket、media等LUA模块,基本满足用户业务逻辑开发要求,可根据用户需求定制特定LUA模块。
LUA语言
Lua 是一种轻量小巧的脚本语言, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。
- 轻量级: 它用标准C语言编写并以源代码形式开放,编译后仅仅一百余K,可以很方便的嵌入别的程序里。
- 可扩展: Lua提供了非常易于使用的扩展接口和机制:由宿主语言(通常是C或C++)提供这些功能,Lua可以使用它们,就像是本来就内置的功能一样。
- 其它特性:
- 支持面向过程(procedure-oriented)编程和函数式编程(functional programming);
- 自动内存管理;只提供了一种通用类型的表(table),用它可以实现数组,哈希表,集合,对象;
- 语言内置模式匹配;闭包(closure);函数也可以看做一个值;提供多线程(协同进程,并非操作系统所支持的线程)支持;
- 通过闭包和table可以很方便地支持面向对象编程所需要的一些关键机制,比如数据抽象,虚函数,继承和重载等。
Lua 教程:https://www.runoob.com/lua/lua-tutorial.html
LUA脚本目录
对于复杂的业务逻辑,我们建议用户的LUA脚本文件组织到不同的目录下,可以解耦和充分利用CPU计算资源,一个目录下可以存放多个后缀为.lua的脚本,有且仅有一个main.lua文件,main.lua是启动脚本,CFIOT内置的LUA虚拟机管理器,会为每一个目录创建LUA虚拟机,并首先启动main.lua这个脚本。
DEMO程序
我们提供了一个DEMO程序,包含两个目录app1和app2,分别存放一个main.lua文件。
app1下main.go
json = require("json")
basic=require("basic")
mqtt=require("mqtt")
hardware=require("hardware")
websocket=require("websocket")
media=require("media")
local Debug=true
local Timer1_data={count=0,name="Timer1"}
local Timer2_data={count=0,name="Timer2"}
local Gps_data={Longitude=0,Latitude=0,N_S="",E_W="",Speed=0,Direction=0,Mode="",DateTime=""}
local Parameters={}
local LuaVMName=this().LuaVMName--当前虚拟机的名称
function testWebsocket()
--Websocket测试
local websocket_OnMessage = function(msg)--消息处理函数
local msgObj=json.decode(msg)
local channel=msgObj.channel
if channel~=nil
then
local DeviceNo=msgObj.DeviceNo
local Data=msgObj.Data
local info=channel..DeviceNo..json.encode(Data)
websocket.SendMessage(info)
end
end
websocket.SetOnMessageHandler(LuaVMName,websocket_OnMessage)--设置消息处理回调函数
websocket.SendMessage("testWebsocket")
end
function testMQTT()
--MQTT测试
if mqtt.WaitMQTTClientReady()--等待mqtt主通道准备好
then
local MQTTSettings=mqtt.GetMQTTSettings()--获取MQTT参数设置
mqtt.Publish(MQTTSettings.PublishTopic,0,false,json.encode(MQTTSettings))--采用默认发布主题推送mqtt参数
mqtt.AddSubscribeTopic("CloudEmbedded/testlua")--订阅mqtt主题
mqtt.AddSubscribeTopic("CloudEmbedded/testlua1")
local mqtt_OnMessage = function(msg)--消息处理函数
info=msg.Toppic .. ":" .. msg.Payload
mqtt.Publish("CloudEmbedded/lua/print",0,false,info)
if (msg.Toppic=="CloudEmbedded/testlua1")
then
mqtt.DeleteSubscribeTopic("CloudEmbedded/testlua1")--删除订阅主题
end
end
mqtt.SetOnMessageHandler(LuaVMName,mqtt_OnMessage)--设置消息处理回调函数
mqtt.Publish("CloudEmbedded/test123",0,false,"test")
end
end
function testJson()
--json测试
--[====[
json相关函数
》json字符串转table
json.decode(str)
》table转json字符串
json.encode(obj)
]====]
local FirmwareInfoStr=basic.GetFirmwareInfo()
mqtt.Publish("CloudEmbedded/lua/print",0,false, FirmwareInfoStr)
basic.print(FirmwareInfoStr,Debug)--打印固件信息
FirmwareInfo=json.decode(FirmwareInfoStr)
local Revisionstr=FirmwareInfo.Revision--固件版本号
local ModuleState=FirmwareInfo.ModuleState--模块状态
local IMEIstr=ModuleState.IMEI--模块序列号
local CCIDstr=ModuleState.CCID--sim卡标识
local CSQstr=ModuleState.CSQ--信号强度
local CSQtimestr=ModuleState.CSQtime--信号时间
local data={IMEI=IMEIstr,CCID=CCIDstr,CSQ=CSQstr,CSQtime=CSQtimestr,Revision=Revisionstr}
mqtt.Publish("CloudEmbedded/lua/print",0,false, json.encode(data))
end
function testTimer()
--定时器测试
--[====[
定时器相关函数
》增加定时器器
AddTimer(name,Interval,Handler)
name:定时器名字
Interval:定时器间隔
Handler:定时器执行函数
》删除定时器
DeleteTimer(name)
》检测定时器是否存在
TimerExist(name)
》启动全部定时器
StartTimer()
》停止所有定时器
StopTimer()
注意:增加定时器器会自动执行StartTimer(),删除最后一个定时器后自动执行StopTimer()
]====]
--增加一个2秒间隔的定时器Timer1
basic.AddTimer(LuaVMName,"Timer1",2000,function()
Timer1_data.count=Timer1_data.count+1
mqtt.Publish("CloudEmbedded/lua/print",0,false, json.encode(Timer1_data))
end)
--增加一个5秒间隔的定时器Timer2
local Timer2=function()
Timer2_data.count=Timer2_data.count+1
mqtt.Publish("CloudEmbedded/lua/print",0,false,json.encode(Timer2_data))
end
basic.AddTimer(LuaVMName,"Timer2",5000,Timer2)
--增加一个1秒间隔的定时器deleteTimer
basic.AddTimer(LuaVMName,"deleteTimer",1000,function()
if (Timer1_data.count>=10)
then
Timer1_data.count=0
basic.DeleteTimer(LuaVMName,"Timer1")
mqtt.Publish("CloudEmbedded/lua/print",0,false, "Delete Timer1")
end
if (Timer2_data.count>=10)
then
Timer2_data.count=0
basic.DeleteTimer(LuaVMName,"Timer2")
mqtt.Publish("CloudEmbedded/lua/print",0,false, "Delete Timer2")
end
if (not basic.TimerExist(LuaVMName,"Timer1") and not basic.TimerExist(LuaVMName,"Timer2"))
then
basic.DeleteTimer(LuaVMName,"deleteTimer")
mqtt.Publish("CloudEmbedded/lua/print",0,false, "The timers are all deleted")
end
end)
end
function testHardware()
--测试硬件
--[====[
》发送AT指令
SendAt(cmd,Formatter,Timeout)
cmd:AT指令内容
Formatter:格式化标签,将AT响应内容格式化,如果为空,则返回原始数据,需要手动编写格式化函数
Timeout:AT响应内容超时时间,单位毫秒,连续响应数据超时返回
》发送字符短信
SendSMS(No,Content)
No:短信号码
Content:短信内容
》设置短信处理回调函数
SetOnSMSHandler(fn)
》设置gps处理回调函数
SetOnGPSHandler(fn)
]====]
mqtt.Publish("CloudEmbedded/lua/print",0,false, "testHardware")
local atResponseStr=hardware.SendAt("at+cgsn\r","",100)
local Revisionstr=hardware.SendAt("ati\r","Revision",100)
local IMEIstr=hardware.SendAt("at+cgsn\r","imei",100)
local CCIDstr=hardware.SendAt("at+qccid\r","ccid",100)
local CSQstr=hardware.SendAt("at+csq\r","CSQ",100)
local data={IMEI=IMEIstr,CCID=CCIDstr,CSQ=CSQstr,cgsnResponse=atResponseStr,Revision=Revisionstr}
mqtt.Publish("CloudEmbedded/lua/print",0,false, json.encode(data))
hardware.SendSMS("15395016712","send sms")
local LteModule_OnSMS = function(msg)--短信处理函数
local info="sms:" .. msg.No .. ":" .. msg.Content
mqtt.Publish("CloudEmbedded/lua/print",0,false,info)
end
hardware.SetOnSMSHandler(LuaVMName,LteModule_OnSMS)--设置短信处理回调函数
local GPSModular_OnGPS = function(data)--gps处理函数
Gps_data.Longitude=data.Longitude
Gps_data.Latitude=data.Latitude
Gps_data.N_S=data.N_S
Gps_data.E_W=data.E_W
Gps_data.Speed=data.Speed
Gps_data.Direction=data.Direction
Gps_data.Mode=data.Mode
Gps_data.DateTime=data.DateTime
mqtt.Publish("CloudEmbedded/lua/print",0,false,json.encode(Gps_data))
end
hardware.SetOnGPSHandler(LuaVMName,GPSModular_OnGPS)--设置gps处理回调函数
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("CloudEmbedded/lua/print",0,false,info)
end
hardware.SetHardwareOnEvent(LuaVMName,Hardware_OnEvent)
local Serial1_OnData= function(data)--串口1数据处理函数
--16进程字符数组 data.hexArray
--byte数组data.byteArray
--local info="Serial1_OnData:" .. data.hex
print(data.hexArray,data.byteArray)
--mqtt.Publish("CloudEmbedded/lua/print",0,false,info)
end
hardware.SetSerialWriteDataSleep("Serial1",30)--设置串口发送数据包后延迟时间,单位毫秒,延时是在CFIOT固件中执行,防止粘包
hardware.SetSerialOnDataHandler(LuaVMName,"Serial1",Serial1_OnData)
hardware.SerialOpen("Serial1",9600,8,"None",1)--打开串口1
hardware.SerialWriteData("Serial1","01 05 00 00 FF 00 8C 3A")
hardware.SerialWriteData("Serial1","01 05 00 00 00 00 CD CA")
hardware.SerialWriteDataByte("Serial1",{0x01,0x05,0x00,0x00,0xFF,0x00,0x8C,0X3A})
hardware.SerialWriteDataByte("Serial1",{0x01,0x05,0x00,0x00,0x00,0x00,0xCD,0XCA})
end
function testVMMessage()
--测试虚拟机收发消息
local VM_OnMessage= function(data)--接收其他虚拟机消息函数
mqtt.Publish("CloudEmbedded/lua/print",0,false,json.encode(data))
end
basic.SetOnMessageHandler(LuaVMName,VM_OnMessage)
local msg={Source=LuaVMName,Dst="app2"}
basic.SendMessage(LuaVMName,"app2",msg)--给app2虚拟机发消息
end
function testMedia()
--测试摄像机,拍照、推流等
media.SearchAllIPCamera()--onvif搜索摄像机
media.GetIPCameraParameters("")--获取摄像机信息,json字符串
media.GetIPCameraState("")--获取摄像机状态信息,json字符串
media.Screenshot("007D50117625","SecondStreamProfileToken_640_352","testScreenshot","/Media")--摄像机拍照
media.VideoRecording("007D50117625","SecondStreamProfileToken_640_352","testVideoRecording","/Media",10,"flv")
media.StartPushStream("007D50117625","SecondStreamProfileToken_640_352","rtmp://tx.direct.huya.com/huyalive/1199558764847-1199558764847-6888343667394952147-2399117653150-10057-A-1603817515-1?seq=1605155499051&type=simple","","","")
end
function Start()
--[[
basic.Sleep()延时函数请慎用,不应该出现在定时器、回调函数里,只能用于初始化阶段
]]
basic.SetDebug(LuaVMName,Debug)--当开启Debug模式,虚拟机运行异常信息会上行到主要上传通道
basic.SetWatchdogTimeout(LuaVMName,300)--设置看门狗超时,单位秒,>=20,喂狗信号超时会导致虚拟机重启
--增加一个2秒间隔的定时器MainTimer,可以作为主控制线程
basic.AddTimer(LuaVMName,"MainTimer",2000,function()
basic.Watchdog(LuaVMName,1)--发送看门狗信号
end)
Parameters=json.decode(basic.GetParameters())--固件参数
if(Parameters.ServerSettingsData.ProtocolType==3)--判断上传通道的协议类型,后续上传数据要根据这个协议类型选择相应的通道
then
testWebsocket()
elseif(Parameters.ServerSettingsData.ProtocolType==4)
then
testMQTT()
else
end
basic.Sleep(30000)--延时60秒
testJson()
testTimer()
basic.Sleep(60000)--延时60秒
testHardware()
testVMMessage()
testMedia()
end
Start()
app2下main.lua
json = require("json")
basic=require("basic")
mqtt=require("mqtt")
hardware=require("hardware")
local Debug=true
local LuaVMName=this().LuaVMName--当前虚拟机的名称
function Start()
basic.SetDebug(LuaVMName,Debug)--当开启Debug模式,虚拟机运行异常信息会上行到主要上传通道
basic.SetWatchdogTimeout(LuaVMName,300)--设置看门狗超时,单位秒,>=20,喂狗信号超时会导致虚拟机重启
--增加一个2秒间隔的定时器MainTimer,可以作为主控制线程
basic.AddTimer(LuaVMName,"MainTimer",2000,function()
basic.Watchdog(LuaVMName,1)--发送看门狗信号
end)
mqtt.WaitMQTTClientReady()--等待mqtt主通道准备好
--测试虚拟机收消息
local VM_OnMessage= function(data)--接收其他虚拟机消息函数
mqtt.Publish("CloudEmbedded/lua/print",0,false,json.encode(data))
local msg={Source=LuaVMName,Dst="app1"}
basic.SendMessage(LuaVMName,"app1",msg)--给app1虚拟机发消息
end
basic.SetOnMessageHandler(LuaVMName,VM_OnMessage)
end
Start()
开发步骤
首先我们要使用云端通讯协议来控制CF-IOT设备下载您的LUA脚本固件,并启动LUA虚拟机运行脚本,LUA脚本固件应使用ZIP压缩包格式。
为了方便调试,我们将设备协议类型设置为MQTT,采用一台免费并可网页调试的MQTT服务器来完成相关功能测试。
MQTT调试网址:http://mqtt.p2hp.com/websocket/
CF-IOT云端通讯协议调试:https://blog.csdn.net/c1991786907/article/details/109570972
在MQTT调试页面,设置好订阅主题和发布主题,首先下发开发者密钥,鉴权成功后,我们就可以使用LUA虚拟机相关协议来调试和运行LUA脚本。
将以上demo程序压缩为zip格式文件,文件名可以随意,压缩包内目录结构如下:
下载LUA脚本
{
"channel": "LuaVM_Upgrading",
"DeviceNo": "864977047765106",
"Data": {
"URL": "http://www.***/lua.zip"
}
}
下载结果会以事件上行
{
"channel": "LuaVM_onEvent",
"DeviceNo": "862607059076223",
"Data": {
"EventName": "LuaVM_Upgrading_Success",
"Message": ""
}
}
启动虚拟机
{
"channel": "LuaVM_Load",
"DeviceNo": "862607059076223",
"Data": null
}
停止虚拟机
{
"channel": "LuaVM_Stop",
"DeviceNo": "862607059076223",
"Data": null
}
脚本清除
{
"channel": "LuaVM_Clear",
"DeviceNo": "862607059076223",
"Data": null
}