最终提交
This commit is contained in:
535
4G/源代码/lib/aLiYun.lua
Normal file
535
4G/源代码/lib/aLiYun.lua
Normal file
@@ -0,0 +1,535 @@
|
||||
--- 模块功能:阿里云物联网套件客户端功能.
|
||||
-- 目前的产品节点类型仅支持“设备”,设备认证方式支持“一机一密和“一型一密”
|
||||
-- @module aLiYun
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2018.04.16
|
||||
|
||||
require"log"
|
||||
require"http"
|
||||
require"mqtt"
|
||||
|
||||
module(..., package.seeall)
|
||||
|
||||
local rrpc_state = false
|
||||
local sProductKey,sProductSecret,sGetDeviceNameFnc,sGetDeviceSecretFnc,sSetDeviceSecretFnc
|
||||
local sRegion = "cn-shanghai"
|
||||
--实例ID,根据此ID来判断是否需要一型一密免预注册认证
|
||||
local sInstanceId
|
||||
--连接方式
|
||||
local sConnectMode,sConnectHost,sConnectPort,sGetClientIdFnc,sGetUserNameFnc,sGetPasswordFnc
|
||||
local sKeepAlive,sCleanSession,sWill
|
||||
local isSleep = false--休眠,不去重连服务器
|
||||
local isAuthSleep =false --休眠,不去重连鉴权
|
||||
local sErrHandleCo,sErrHandleCb,sErrHandleTmout
|
||||
|
||||
local outQueue =
|
||||
{
|
||||
SUBSCRIBE = {},
|
||||
PUBLISH = {},
|
||||
}
|
||||
|
||||
local evtCb = {}
|
||||
|
||||
local function insert(type,topic,qos,payload,cbFnc,cbPara)
|
||||
table.insert(outQueue[type],{t=topic,q=qos,p=payload,cb=cbFnc,para=cbPara})
|
||||
end
|
||||
|
||||
local function remove(type)
|
||||
if #outQueue[type]>0 then return table.remove(outQueue[type],1) end
|
||||
end
|
||||
|
||||
local function procSubscribe(client)
|
||||
local i
|
||||
for i=1,#outQueue["SUBSCRIBE"] do
|
||||
if not client:subscribe(outQueue["SUBSCRIBE"][i].t,outQueue["SUBSCRIBE"][i].q) then
|
||||
outQueue["SUBSCRIBE"] = {}
|
||||
return false,"procSubscribe"
|
||||
end
|
||||
end
|
||||
outQueue["SUBSCRIBE"] = {}
|
||||
return true
|
||||
end
|
||||
|
||||
local function procReceive(client)
|
||||
local r,data,s
|
||||
while true do
|
||||
r,data,s = client:receive(60000,"aliyun_publish_ind")
|
||||
--接收到数据
|
||||
if r and data~="timeout" then
|
||||
log.info("aLiYun.procReceive",data.topic,string.toHex(data.payload))
|
||||
--OTA消息
|
||||
if data.topic=="/ota/device/upgrade/"..sProductKey.."/"..sGetDeviceNameFnc() then
|
||||
if aLiYunOta and aLiYunOta.upgrade then
|
||||
aLiYunOta.upgrade(data.payload)
|
||||
end
|
||||
--其他消息
|
||||
else
|
||||
if evtCb["receive"] then evtCb["receive"](data.topic,data.qos,data.payload) end
|
||||
end
|
||||
|
||||
--如果有等待发送的数据,则立即退出本循环
|
||||
if #outQueue["PUBLISH"]>0 then
|
||||
return true,"procReceive"
|
||||
else
|
||||
if sErrHandleCo then coroutine.resume(sErrHandleCo,"feed") end
|
||||
end
|
||||
elseif data == "aliyun_publish_ind" and s:find("disconnect") then--主动断开
|
||||
client:disconnect()
|
||||
return false,"procReceive"
|
||||
elseif data == "aliyun_publish_ind" and s:find("send") then--来数据要发了
|
||||
log.info("aliyun aliyun_publish_ind")
|
||||
return true,"procReceive"
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
return data=="timeout" or r,"procReceive"
|
||||
end
|
||||
|
||||
local function procSend(client)
|
||||
if not procSubscribe(client) then
|
||||
return false,"procSubscribe"
|
||||
end
|
||||
while #outQueue["PUBLISH"]>0 do
|
||||
local item = table.remove(outQueue["PUBLISH"],1)
|
||||
local result = client:publish(item.t,item.p,item.q)
|
||||
if item.cb then item.cb(result,item.para) end
|
||||
if not result then
|
||||
return false,"procSend"
|
||||
else
|
||||
if sErrHandleCo then coroutine.resume(sErrHandleCo,"feed") end
|
||||
end
|
||||
end
|
||||
return true,"procSend"
|
||||
end
|
||||
|
||||
--- 断开阿里云物联网套件的连接,并且不再重连
|
||||
-- @return nil
|
||||
-- @usage
|
||||
-- aLiYun.sleep()
|
||||
function sleep()
|
||||
isSleep = true
|
||||
log.info("aLiYun.sleep","open sleep, stop try reconnect")
|
||||
sys.publish("aliyun_publish_ind","disconnect")
|
||||
end
|
||||
|
||||
--- 重新打开阿里云物联网套件的连接
|
||||
-- @return nil
|
||||
-- @usage
|
||||
-- aLiYun.wakeup()
|
||||
function wakeup()
|
||||
isSleep = false
|
||||
sys.publish("ALITUN_WAKEUP")
|
||||
log.info("aLiYun.wakeup","exit sleep")
|
||||
end
|
||||
--- 查看打开阿里云物联网套件的是否允许连接状态
|
||||
-- @return bool 是否允许连接阿里云
|
||||
-- @usage
|
||||
-- local ar = aLiYun.sleepStatus()
|
||||
function sleepStatus()
|
||||
return isSleep
|
||||
end
|
||||
--- 断开阿里云物联网套件的鉴权连接,并且不再重连
|
||||
-- @return nil
|
||||
-- @usage
|
||||
-- aLiYun.Authsleep()
|
||||
function Authsleep()
|
||||
isAuthSleep = true
|
||||
log.info("aLiYun.Authsleep","open sleep, stop try reconnect")
|
||||
sys.publish("aliyun_publish_ind","disconnect")
|
||||
end
|
||||
--- 重新打开阿里云物联网套件的鉴权连接
|
||||
-- @return nil
|
||||
-- @usage
|
||||
-- aLiYun.Authwakeup()
|
||||
function Authwakeup()
|
||||
isAuthSleep = false
|
||||
sys.publish("ALITUN_Auth_WAKEUP")
|
||||
log.info("aLiYun.auth_wakeup","exit sleep")
|
||||
end
|
||||
--- 查看打开阿里云物联网套件的是否允许鉴权状态
|
||||
-- @return bool 是否允许连接阿里云
|
||||
-- @usage
|
||||
-- local ar = aLiYun.AuthSleepStatus()
|
||||
function AuthSleepStatus()
|
||||
return isAuthSleep
|
||||
end
|
||||
|
||||
function clientDataTask(host,tPorts,clientId,user,password)
|
||||
local retryConnectCnt = 0
|
||||
local portIdx = 0
|
||||
while true do
|
||||
if isSleep then sys.waitUntil("ALITUN_WAKEUP") end
|
||||
|
||||
if not socket.isReady() then
|
||||
retryConnectCnt = 0
|
||||
--等待网络环境准备就绪,超时时间是5分钟
|
||||
sys.waitUntil("IP_READY_IND",300000)
|
||||
end
|
||||
|
||||
if socket.isReady() then
|
||||
local mqttClient = mqtt.client(clientId,sKeepAlive or 240,user,password,sCleanSession,sWill)
|
||||
portIdx = portIdx%(#tPorts)+1
|
||||
|
||||
if mqttClient:connect(host,tonumber(tPorts[portIdx]),sConnectMode=="direct" and "tcp" or "tcp_ssl") then
|
||||
retryConnectCnt = 0
|
||||
if aLiYunOta and aLiYunOta.connectCb then aLiYunOta.connectCb(true,sProductKey,sGetDeviceNameFnc()) end
|
||||
if evtCb["connect"] then evtCb["connect"](true) end
|
||||
|
||||
local result,prompt = procSubscribe(mqttClient)
|
||||
if result then
|
||||
local procs,k,v = {procSend,procReceive}
|
||||
while true do
|
||||
for k,v in pairs(procs) do
|
||||
result,prompt = v(mqttClient)
|
||||
if not result then log.warn("aLiYun.clientDataTask."..prompt.." error") break end
|
||||
end
|
||||
if not result then break end
|
||||
if sErrHandleCo then coroutine.resume(sErrHandleCo,"feed") end
|
||||
end
|
||||
else
|
||||
log.warn("aLiYun.clientDataTask."..prompt.." error")
|
||||
end
|
||||
|
||||
while #outQueue["PUBLISH"]>0 do
|
||||
local item = table.remove(outQueue["PUBLISH"],1)
|
||||
if item.cb then item.cb(false,item.para) end
|
||||
end
|
||||
if aLiYunOta and aLiYunOta.connectCb then aLiYunOta.connectCb(false,sProductKey,sGetDeviceNameFnc()) end
|
||||
if evtCb["connect"] then evtCb["connect"](false) end
|
||||
else
|
||||
retryConnectCnt = retryConnectCnt+1
|
||||
if evtCb["reconnect"] then evtCb["reconnect"]() end
|
||||
end
|
||||
|
||||
mqttClient:disconnect()
|
||||
if retryConnectCnt>=5 then link.shut() retryConnectCnt=0 end
|
||||
sys.wait(2000)
|
||||
else
|
||||
--进入飞行模式,20秒之后,退出飞行模式
|
||||
net.switchFly(true)
|
||||
sys.wait(20000)
|
||||
net.switchFly(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function getDeviceSecretCb(result,prompt,head,body)
|
||||
log.info("aLiYun.getDeviceSecretCb",result,prompt)
|
||||
if result and body then
|
||||
local tJsonDecode = json.decode(body)
|
||||
if tJsonDecode and tJsonDecode["data"] and tJsonDecode["data"]["deviceSecret"] and tJsonDecode["data"]["deviceSecret"]~="" then
|
||||
sSetDeviceSecretFnc(tJsonDecode["data"]["deviceSecret"])
|
||||
end
|
||||
end
|
||||
sys.publish("GetDeviceSecretEnd")
|
||||
|
||||
end
|
||||
|
||||
local function authCbFnc(result,statusCode,head,body)
|
||||
log.info("aLiYun.authCbFnc",result,statusCode,body)
|
||||
sys.publish("ALIYUN_AUTH_IND",result,statusCode,body)
|
||||
end
|
||||
|
||||
local function getBody(tag)
|
||||
if tag=="auth" then
|
||||
local data = "clientId"..sGetDeviceNameFnc().."deviceName"..sGetDeviceNameFnc().."productKey"..sProductKey
|
||||
local signKey= sGetDeviceSecretFnc()
|
||||
local sign = crypto.hmac_md5(data,data:len(),signKey,signKey:len())
|
||||
return "productKey="..sProductKey.."&sign="..sign.."&clientId="..sGetDeviceNameFnc().."&deviceName="..sGetDeviceNameFnc()
|
||||
elseif tag=="register" then
|
||||
local random=rtos.tick()
|
||||
local data = "deviceName"..sGetDeviceNameFnc().."productKey"..sProductKey.."random"..random
|
||||
local sign = crypto.hmac_md5(data,data:len(),sProductSecret,sProductSecret:len())
|
||||
return "productKey="..sProductKey.."&deviceName="..sGetDeviceNameFnc().."&random="..random.."&sign="..sign.."&signMethod=HmacMD5"
|
||||
end
|
||||
end
|
||||
|
||||
function clientAuthTask()
|
||||
while not socket.isReady() do sys.waitUntil("IP_READY_IND") end
|
||||
while true do
|
||||
if isAuthSleep then sys.waitUntil("ALITUN_Auth_WAKEUP") end
|
||||
local retryCnt,authBody = 0,getBody("auth")
|
||||
while true do
|
||||
http.request("POST",
|
||||
"https://iot-auth."..sRegion..".aliyuncs.com/auth/devicename",
|
||||
nil,{["Content-Type"]="application/x-www-form-urlencoded"},authBody,20000,authCbFnc)
|
||||
|
||||
local _,result,statusCode,body = sys.waitUntil("ALIYUN_AUTH_IND")
|
||||
--log.info("aLiYun.clientAuthTask1",result and statusCode=="200",body)
|
||||
local invalidSign
|
||||
if result and statusCode=="200" then
|
||||
local tJsonDecode,result = json.decode(body)
|
||||
--log.info("aLiYun.clientAuthTask2",result,tJsonDecode["message"],tJsonDecode["data"])
|
||||
if result and tJsonDecode["message"]=="success" and tJsonDecode["data"] and type(tJsonDecode["data"])=="table" then
|
||||
--log.info("aLiYun.clientAuthTask3",tJsonDecode["data"]["iotId"],tJsonDecode["data"]["iotToken"])
|
||||
if tJsonDecode["data"]["iotId"] and tJsonDecode["data"]["iotId"]~="" and tJsonDecode["data"]["iotToken"] and tJsonDecode["data"]["iotToken"]~="" then
|
||||
if evtCb["auth"] then evtCb["auth"](true) end
|
||||
local ports,host,returnMqtt = {}
|
||||
if tJsonDecode["data"]["resources"] and type(tJsonDecode["data"]["resources"])=="table" then
|
||||
if tJsonDecode["data"]["resources"]["mqtt"] then
|
||||
returnMqtt,host = true,tJsonDecode["data"]["resources"]["mqtt"]["host"]
|
||||
table.insert(ports,tJsonDecode["data"]["resources"]["mqtt"]["port"])
|
||||
end
|
||||
end
|
||||
|
||||
sys.taskInit(clientDataTask,returnMqtt and host or sProductKey..".iot-as-mqtt."..sRegion..".aliyuncs.com",#ports~=0 and ports or {1883},sGetDeviceNameFnc(),tJsonDecode["data"]["iotId"],tJsonDecode["data"]["iotToken"])
|
||||
return
|
||||
end
|
||||
end
|
||||
if body and body:match("invalid sign") then
|
||||
invalidSign = true
|
||||
end
|
||||
end
|
||||
|
||||
if sProductSecret and invalidSign then
|
||||
http.request("POST","https://iot-auth."..sRegion..".aliyuncs.com/auth/register/device",nil,
|
||||
{['Content-Type']="application/x-www-form-urlencoded"},
|
||||
getBody("register"),30000,getDeviceSecretCb)
|
||||
sys.waitUntil("GetDeviceSecretEnd")
|
||||
sys.wait(1000)
|
||||
authBody = getBody("auth")
|
||||
end
|
||||
|
||||
retryCnt = retryCnt+1
|
||||
if retryCnt==3 then
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if evtCb["auth"] then evtCb["auth"](false) end
|
||||
sys.wait(5000)
|
||||
end
|
||||
end
|
||||
|
||||
local function directProc()
|
||||
log.info("rrpc",rrpc_state)
|
||||
local clientId
|
||||
clientId = (sGetClientIdFnc and sGetClientIdFnc() or sGetDeviceNameFnc()).."|securemode=3,timestamp=2524608000000,signmethod=hmacsha1"..(rrpc_state and ",ext=1" or "").."|"
|
||||
log.info("clientId",clientId)
|
||||
local userName = sGetUserNameFnc and sGetUserNameFnc() or (sGetDeviceNameFnc().."&"..sProductKey)
|
||||
|
||||
local password
|
||||
|
||||
if sGetPasswordFnc then
|
||||
password = sGetPasswordFnc()
|
||||
else
|
||||
local content = "clientId"..(sGetClientIdFnc and sGetClientIdFnc() or sGetDeviceNameFnc()).."deviceName"..sGetDeviceNameFnc().."productKey"..sProductKey.."timestamp2524608000000"
|
||||
local signKey= sGetDeviceSecretFnc()
|
||||
password = crypto.hmac_sha1(content,content:len(),signKey,signKey:len())
|
||||
end
|
||||
|
||||
log.info("aLiYun.directProc",clientId,userName,password)
|
||||
|
||||
sys.taskInit(clientDataTask,sConnectHost or (sProductKey..".iot-as-mqtt."..sRegion..".aliyuncs.com"),{sConnectPort},clientId,userName,password)
|
||||
end
|
||||
|
||||
local function clientDirectTask()
|
||||
while not socket.isReady() do sys.waitUntil("IP_READY_IND") end
|
||||
|
||||
local tm=os.time()
|
||||
|
||||
--一机一密
|
||||
if sProductSecret==nil then
|
||||
directProc()
|
||||
--一型一密
|
||||
else
|
||||
local clientId
|
||||
--预注册
|
||||
if sInstanceId==nil then
|
||||
clientId = (sGetClientIdFnc and sGetClientIdFnc() or sGetDeviceNameFnc()).."|securemode=2,authType=register,random="..tm..",signmethod=hmacsha1"..(rrpc_state and ",ext=1" or "").."|"
|
||||
--免预注册
|
||||
else
|
||||
clientId = (sGetClientIdFnc and sGetClientIdFnc() or sGetDeviceNameFnc()).."|securemode=-2,authType=regnwl,random="..tm..",signmethod=hmacsha1,instanceId="..sInstanceId.."|"
|
||||
end
|
||||
|
||||
local userName = sGetUserNameFnc and sGetUserNameFnc() or (sGetDeviceNameFnc().."&"..sProductKey)
|
||||
|
||||
local content = "deviceName"..sGetDeviceNameFnc().."productKey"..sProductKey.."random"..tm
|
||||
local signKey= sProductSecret
|
||||
local password = crypto.hmac_sha1(content,content:len(),signKey,signKey:len())
|
||||
|
||||
|
||||
while true do
|
||||
if isAuthSleep then sys.waitUntil("ALITUN_Auth_WAKEUP") end
|
||||
local mqttClient = mqtt.client(clientId,sKeepAlive or 240,userName,password)
|
||||
|
||||
local r,ack = mqttClient:connect(sConnectHost,sConnectPort,"tcp_ssl")
|
||||
if r then
|
||||
local result,data = mqttClient:receive(60000)
|
||||
--接收到数据
|
||||
if result then
|
||||
log.info("aLiYun.clientDirectTask register rsp",data.topic,data.payload)
|
||||
local tJsonDecode,res = json.decode(data.payload)
|
||||
if res and tJsonDecode["deviceName"] and tJsonDecode["deviceSecret"] then
|
||||
sSetDeviceSecretFnc(tJsonDecode["deviceSecret"])
|
||||
sys.wait(1000)
|
||||
mqttClient:disconnect()
|
||||
directProc()
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
mqttClient:disconnect()
|
||||
if ack==4 then
|
||||
directProc()
|
||||
break
|
||||
else
|
||||
sys.wait(5000)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- 配置阿里云物联网套件的产品信息和设备信息
|
||||
-- @string productKey 产品标识
|
||||
-- @string[opt=nil] productSecret 产品密钥
|
||||
-- 一机一密认证方案时,此参数传入nil
|
||||
-- 一型一密认证方案时,此参数传入真实的产品密钥
|
||||
-- @function getDeviceNameFnc 获取设备名称的函数
|
||||
-- @function getDeviceSecretFnc 获取设备密钥的函数
|
||||
-- @function[opt=nil] setDeviceSecretFnc 设置设备密钥的函数,一型一密认证方案才需要此参数
|
||||
-- @return nil
|
||||
-- @usage
|
||||
-- aLiYun.setup("b0FMK1Ga5cp",nil,getDeviceNameFnc,getDeviceSecretFnc)
|
||||
-- aLiYun.setup("a1AoVqkCIbG","7eCdPyR6fYPntFcM",getDeviceNameFnc,getDeviceSecretFnc,setDeviceSecretFnc)
|
||||
function setup(productKey,productSecret,getDeviceNameFnc,getDeviceSecretFnc,setDeviceSecretFnc)
|
||||
sProductKey,sProductSecret,sGetDeviceNameFnc,sGetDeviceSecretFnc,sSetDeviceSecretFnc = productKey,productSecret,getDeviceNameFnc,getDeviceSecretFnc,setDeviceSecretFnc
|
||||
if sConnectMode=="direct" then
|
||||
sys.taskInit(clientDirectTask)
|
||||
else
|
||||
sys.taskInit(clientAuthTask)
|
||||
end
|
||||
end
|
||||
|
||||
--- 设置MQTT数据通道的参数
|
||||
-- @number[opt=1] cleanSession 1/0
|
||||
-- @table[opt=nil] will 遗嘱参数,格式为{qos=,retain=,topic=,payload=}
|
||||
-- @number[opt=240] keepAlive 单位秒
|
||||
-- @return nil
|
||||
-- @usage
|
||||
-- aLiYun.setMqtt(0)
|
||||
-- aLiYun.setMqtt(1,{qos=0,retain=1,topic="/willTopic",payload="will payload"})
|
||||
-- aLiYun.setMqtt(1,{qos=0,retain=1,topic="/willTopic",payload="will payload"},120)
|
||||
function setMqtt(cleanSession,will,keepAlive)
|
||||
sCleanSession,sWill,sKeepAlive = cleanSession,will,keepAlive
|
||||
end
|
||||
|
||||
|
||||
--- 设置地域region id
|
||||
-- @string region 地域id字符串,参考:https://help.aliyun.com/document_detail/40654.html?spm=a2c4g.11186623.2.16.c0a63f82Z7qCtA#concept-h4v-j5k-xdb
|
||||
-- @return nil
|
||||
-- @usage
|
||||
-- 设置华北1:aLiYun.setRegion("cn-qingdao")
|
||||
-- 设置华东1:aLiYun.setRegion("cn-hangzhou")
|
||||
-- 设置华南1:aLiYun.setRegion("cn-shenzhen")
|
||||
function setRegion(region)
|
||||
sRegion = region
|
||||
end
|
||||
|
||||
-- 设置企业版实例id
|
||||
-- @string id,企业版实例id
|
||||
-- @return nil
|
||||
-- @usage
|
||||
-- aLiYun.setInstanceId(iot-060a1234")
|
||||
function setInstanceId(id)
|
||||
sInstanceId = id
|
||||
end
|
||||
|
||||
--- 设置连接方式
|
||||
-- @string mode 连接方式,支持如下几种方式:
|
||||
-- "direct"表示MQTT-TCP直连
|
||||
-- @string host 服务器地址
|
||||
-- @number port 服务器端口
|
||||
-- @function getClientIdFnc 获取mqtt,client,id的函数
|
||||
-- @function getUserNameFnc 获取mqtt,client,userName的函数
|
||||
-- @function getPasswordFnc 获取mqtt,client,password的函数
|
||||
-- @return nil
|
||||
-- @usage
|
||||
-- 设置为MQTT-TCP直连:aLiYun.setConnectMode("direct")
|
||||
function setConnectMode(mode,host,port,getClientIdFnc,getUserNameFnc,getPasswordFnc)
|
||||
sConnectMode = mode
|
||||
sConnectHost = host
|
||||
sConnectPort = port or 1883
|
||||
sGetClientIdFnc = getClientIdFnc
|
||||
sGetUserNameFnc = getUserNameFnc
|
||||
sGetPasswordFnc = getPasswordFnc
|
||||
end
|
||||
|
||||
--- 订阅主题
|
||||
-- @param topic string或者table类型,一个主题时为string类型,多个主题时为table类型,主题内容为UTF8编码
|
||||
-- @param qos number或者nil,topic为一个主题时,qos为number类型(0/1,默认0);topic为多个主题时,qos为nil
|
||||
-- @return nil
|
||||
-- @usage
|
||||
-- aLiYun.subscribe("/b0FMK1Ga5cp/862991234567890/get", 0)
|
||||
-- aLiYun.subscribe({["/b0FMK1Ga5cp/862991234567890/get"] = 0, ["/b0FMK1Ga5cp/862991234567890/get"] = 1})
|
||||
function subscribe(topic,qos)
|
||||
insert("SUBSCRIBE",topic,qos)
|
||||
sys.publish("aliyun_publish_ind","send")
|
||||
end
|
||||
|
||||
--- 发布一条消息
|
||||
-- @string topic UTF8编码的主题
|
||||
-- @string payload 负载
|
||||
-- @number[opt=0] qos 质量等级,0/1,默认0
|
||||
-- @function[opt=nil] cbFnc 消息发布结果的回调函数
|
||||
-- 回调函数的调用形式为:cbFnc(result,cbPara)。result为true表示发布成功,false或者nil表示订阅失败;cbPara为本接口中的第5个参数
|
||||
-- @param[opt=nil] cbPara 消息发布结果回调函数的回调参数
|
||||
-- @return nil
|
||||
-- @usage
|
||||
-- aLiYun.publish("/b0FMK1Ga5cp/862991234567890/update","test",0)
|
||||
-- aLiYun.publish("/b0FMK1Ga5cp/862991234567890/update","test",1,cbFnc,"cbFncPara")
|
||||
function publish(topic,payload,qos,cbFnc,cbPara)
|
||||
insert("PUBLISH",topic,qos,payload,cbFnc,cbPara)
|
||||
sys.publish("aliyun_publish_ind","send")
|
||||
log.info("aliyun aliyun_publish_ind","publish")
|
||||
end
|
||||
|
||||
--- 注册事件的处理函数
|
||||
-- @string evt 事件
|
||||
-- "auth"表示鉴权服务器认证结果事件
|
||||
-- "connect"表示接入服务器连接结果事件
|
||||
-- "reconnect"表示重连事件
|
||||
-- "receive"表示接收到接入服务器的消息事件
|
||||
-- @function cbFnc 事件的处理函数
|
||||
-- 当evt为"auth"时,cbFnc的调用形式为:cbFnc(result),result为true表示认证成功,false或者nil表示认证失败
|
||||
-- 当evt为"connect"时,cbFnc的调用形式为:cbFnc(result),result为true表示连接成功,false或者nil表示连接失败
|
||||
-- 当evt为"receive"时,cbFnc的调用形式为:cbFnc(topic,qos,payload),topic为UTF8编码的主题(string类型),qos为质量等级(number类型),payload为原始编码的负载(string类型)
|
||||
-- 当evt为"reconnect"时,cbFnc的调用形式为:cbFnc(),表示lib中在自动重连阿里云服务器
|
||||
-- @return nil
|
||||
-- @usage
|
||||
-- aLiYun.on("connect",cbFnc)
|
||||
function on(evt,cbFnc)
|
||||
evtCb[evt] = cbFnc
|
||||
end
|
||||
|
||||
--- 设置阿里云task连续一段时间工作异常的处理程序
|
||||
-- @function cbFnc 异常处理函数,cbFnc的调用形式为:cbFnc()
|
||||
-- @number[opt=150] tmout 连续工作异常的时间,当连续异常到达这个时间之后,会调用cbFnc()
|
||||
-- @return nil
|
||||
-- @usage
|
||||
-- aLiYun.setErrHandle(function() sys.restart("ALIYUN_TASK_INACTIVE") end, 300)
|
||||
function setErrHandle(cbFnc,tmout)
|
||||
sErrHandleCb = cbFnc
|
||||
sErrHandleTmout = tmout or 150
|
||||
if not sErrHandleCo then
|
||||
sErrHandleCo = sys.taskInit(function()
|
||||
while true do
|
||||
if sys.wait(sErrHandleTmout*1000) == nil then
|
||||
if not isSleep then
|
||||
sErrHandleCb()
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
function rrpcUseCustomTopic(state)
|
||||
rrpc_state = state
|
||||
end
|
||||
255
4G/源代码/lib/aLiYunOta.lua
Normal file
255
4G/源代码/lib/aLiYunOta.lua
Normal file
@@ -0,0 +1,255 @@
|
||||
--- 模块功能:阿里云物联网套件客户端OTA功能.
|
||||
-- 目前固件签名算法仅支持MD5
|
||||
-- @module aLiYunOta
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2018.04.16
|
||||
|
||||
require"log"
|
||||
require"http"
|
||||
|
||||
module(..., package.seeall)
|
||||
|
||||
--gVersion:固件版本号字符串,如果用户没有调用本文件的setVer接口设置,则默认为_G.PROJECT.."_".._G.VERSION.."_"..sys.getcorever()
|
||||
--gName:阿里云iot网站上配置的新固件文件下载后,在模块中的保存路径,如果用户没有调用本文件的setName接口设置,则默认为/luazip/update.bin
|
||||
--gCb:新固件下载成功后,要执行的回调函数
|
||||
local gVersion,gName,gCb = _G.PROJECT.."_".._G.VERSION.."_"..rtos.get_version()
|
||||
local gFilePath,gFileSize
|
||||
local processed = 0
|
||||
local flowMd5
|
||||
--productKey:产品标识
|
||||
--deviceName:设备名称
|
||||
local productKey,deviceName
|
||||
|
||||
--verRpted:版本号是否已经上报
|
||||
local verRpted,sConnected
|
||||
--lastStep:最后一次上报的下载新固件的进度
|
||||
local lastStep
|
||||
--下载中标志
|
||||
local downloading
|
||||
|
||||
local function otaCb(result,filePath,md5,size)
|
||||
log.info("aLiYunOta.otaCb",gCb,result,filePath,size,(type(gName) =="string") and io.fileSize(filePath) or "function")
|
||||
downloading = false
|
||||
--校验MD5
|
||||
if result then
|
||||
if type(gName) == "string" then
|
||||
local calMD5 = crypto.md5(filePath,"file")
|
||||
result = (string.upper(calMD5) == string.upper(md5))
|
||||
log.info("aLiYunOta.otaCb cmp md5",result,calMD5,md5)
|
||||
else
|
||||
local calMD5 = flowMd5:hexdigest()
|
||||
result = (string.upper(calMD5) == string.upper(md5))
|
||||
log.info("aLiYunOta.otaCb cmp md5",result,calMD5,md5)
|
||||
end
|
||||
end
|
||||
|
||||
rtos.fota_end()
|
||||
if not result then
|
||||
rtos.fota_start()
|
||||
rtos.fota_end()
|
||||
end
|
||||
|
||||
if gCb then
|
||||
gCb(result,filePath)
|
||||
else
|
||||
if result then
|
||||
sys.restart("ALIYUN_OTA")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:upgradeStepRpt
|
||||
功能 :新固件文件下载进度上报
|
||||
参数 :
|
||||
step:1到100代表下载进度比;-2代表下载失败
|
||||
desc:描述信息,可为空或者nil
|
||||
返回值:无
|
||||
]]
|
||||
local function upgradeStepRpt(step,desc)
|
||||
log.info("aLiYunOta.upgradeStepRpt",step,desc,sConnected)
|
||||
if sConnected then
|
||||
if step<=0 or step==100 then sys.timerStop(getPercent) end
|
||||
lastStep = step
|
||||
aLiYun.publish("/ota/device/progress/"..productKey.."/"..deviceName,"{\"id\":1,\"params\":{\"step\":\""..step.."\",\"desc\":\""..(desc or "").."\"}}")
|
||||
end
|
||||
end
|
||||
|
||||
function getPercent()
|
||||
local step
|
||||
if type(gName) == "string" then
|
||||
step = io.fileSize(gName)*100/gFileSize
|
||||
else
|
||||
step = processed*100/gFileSize
|
||||
end
|
||||
log.info("aLiYunOta.getPercent",step)
|
||||
if step~=0 and step~=lastStep then
|
||||
upgradeStepRpt(step)
|
||||
end
|
||||
sys.timerStart(getPercent,5000)
|
||||
end
|
||||
|
||||
local function downloadCbFnc(result,prompt,head,filePath)
|
||||
log.info("aLiYunOta.downloadCbFnc",result,prompt,filePath)
|
||||
sys.publish("ALIYUN_OTA_DOWNLOAD_IND",result)
|
||||
end
|
||||
|
||||
local function saveUpdata(pdata,binlen,statusCode)
|
||||
log.info("saveUpdata",binlen,statusCode)
|
||||
if statusCode == "206" and pdata and binlen then
|
||||
if rtos.fota_process(pdata,binlen) ~=0 then --返回-94表示没有进行初始化,需要调用rtos.fota_start()进行更新初始化
|
||||
log.info("updata.fota_process","fail!!")
|
||||
return
|
||||
else
|
||||
flowMd5:update(pdata)
|
||||
--打印此升级包的长度跟总包长度
|
||||
processed = processed + pdata:len()
|
||||
log.info("updata.fota_process",processed,binlen)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function downloadTask(url,size,md5,ver)
|
||||
log.info("aLiYunOta.downloadTask1",downloading,url,size,md5)
|
||||
if not downloading then
|
||||
downloading = true
|
||||
gFileSize = size
|
||||
|
||||
local rangeBegin,retryCnt = 0,0
|
||||
sys.timerStart(getPercent,5000)
|
||||
sys.publish("LIB_ALIYUN_OTA_DOWNLOAD_BEGIN",ver)
|
||||
while true do
|
||||
if not gName then
|
||||
gName = saveUpdata
|
||||
end
|
||||
http.request("GET",url,nil,{["Range"]="bytes="..rangeBegin.."-"},"",20000,downloadCbFnc,gName)
|
||||
--if rangeBegin==0 then os.remove(gName) end
|
||||
local _,result = sys.waitUntil("ALIYUN_OTA_DOWNLOAD_IND")
|
||||
log.info("aLiYunOta.downloadTask2",result)
|
||||
if result then
|
||||
upgradeStepRpt(100,0)
|
||||
sys.timerStart(otaCb,5000,true,gName,md5,size)
|
||||
break
|
||||
else
|
||||
retryCnt = retryCnt+1
|
||||
if retryCnt>=30 then
|
||||
upgradeStepRpt(-2,"timeout")
|
||||
otaCb(false,gName)
|
||||
break
|
||||
end
|
||||
end
|
||||
if type(gName) == "string" then
|
||||
rangeBegin = io.fileSize(gName)
|
||||
else
|
||||
rangeBegin = processed
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--[[
|
||||
函数名:upgrade
|
||||
功能 :收到云端固件升级通知消息时的回调函数
|
||||
参数 :
|
||||
payload:消息负载(原始编码,收到的payload是什么内容,就是什么内容,没有做任何编码转换)
|
||||
返回值:无
|
||||
]]
|
||||
function upgrade(payload)
|
||||
local result
|
||||
local jsonData, result = json.decode(payload)
|
||||
log.info("aLiYunOta.upgrade", result, payload)
|
||||
if result and jsonData.data and jsonData.data.url then
|
||||
if rtos.fota_start() ~= 0 then
|
||||
log.info("fota_start fail")
|
||||
sys.timerStart(verRpt, 10000)
|
||||
return
|
||||
end
|
||||
flowMd5 = crypto.flow_md5()
|
||||
sys.taskInit(downloadTask, jsonData.data.url, jsonData.data.size, jsonData.data.md5, jsonData.data.version)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
--[[
|
||||
函数名:verRptCb
|
||||
功能 :上报固件版本号给云端后,收到PUBACK时的回调函数
|
||||
参数 :
|
||||
result:true表示上报成功,false或者nil表示失败
|
||||
返回值:无
|
||||
]]
|
||||
local function verRptCb(result)
|
||||
log.info("aLiYunOta.verRptCb",result)
|
||||
verRpted = result
|
||||
if not result then sys.timerStart(verRpt,20000) end
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:verRpt
|
||||
功能 :上报固件版本号给云端
|
||||
参数 :无
|
||||
返回值:无
|
||||
]]
|
||||
function verRpt()
|
||||
log.info("aLiYunOta.verRpt",sConnected,gVersion)
|
||||
if sConnected then
|
||||
aLiYun.publish("/ota/device/inform/"..productKey.."/"..deviceName,"{\"id\":1,\"params\":{\"version\":\""..gVersion.."\"}}",1,verRptCb)
|
||||
end
|
||||
end
|
||||
|
||||
function connectCb(result,key,name)
|
||||
sConnected = result
|
||||
if result then
|
||||
log.info("aLiYunOta.connectCb",verRpted)
|
||||
productKey,deviceName = key,name
|
||||
--订阅主题
|
||||
aLiYun.subscribe({["/ota/device/upgrade/"..key.."/"..name]=0, ["/ota/device/upgrade/"..key.."/"..name]=1})
|
||||
if not verRpted then
|
||||
--上报固件版本号给云端
|
||||
verRpt()
|
||||
end
|
||||
else
|
||||
sys.timerStop(verRpt)
|
||||
end
|
||||
end
|
||||
|
||||
function isDownloading()
|
||||
return downloading
|
||||
end
|
||||
|
||||
--- 设置当前的固件版本号
|
||||
-- @string version 当前固件版本号
|
||||
-- @return nil
|
||||
-- @usage
|
||||
-- aLiYunOta.setVer("MCU_VERSION_1.0.0")
|
||||
function setVer(version)
|
||||
local oldVer = gVersion
|
||||
gVersion = version
|
||||
if verRpted and version~=oldVer then
|
||||
verRpted = false
|
||||
verRpt()
|
||||
end
|
||||
end
|
||||
|
||||
--- 设置新固件保存的文件名
|
||||
-- @string name 新固件下载后保存的文件名;注意此文件名并不是保存的完整路径,完整路径通过setCb设置的回调函数去获取
|
||||
-- @return nil
|
||||
-- @usage
|
||||
-- aLiYunOta.setName("MCU_FIRMWARE.bin")
|
||||
function setName(name)
|
||||
gName = name
|
||||
end
|
||||
|
||||
--- 设置新固件下载后的回调函数
|
||||
-- @function cbFnc 新固件下载后的回调函数
|
||||
-- 回调函数的调用形式为:cbFnc(result,filePath),result为下载结果,true表示成功,false或者nil表示失败;filePath为新固件文件保存的完整路径
|
||||
-- @return nil
|
||||
-- @usage
|
||||
-- aLiYunOta.setCb(cbFnc)
|
||||
function setCb(cbFnc)
|
||||
gCb = cbFnc
|
||||
end
|
||||
232
4G/源代码/lib/agps.lua
Normal file
232
4G/源代码/lib/agps.lua
Normal file
@@ -0,0 +1,232 @@
|
||||
--- 模块功能:GPS辅助定位以及星历更新服务.
|
||||
-- 本功能模块只能配合Air800或者Air530使用;
|
||||
-- require"agps"后,会自动开启本功能模块的任务;
|
||||
-- 开机后,仅获取一次基站对应的经纬度位置和当前时间,把经纬度位置和时间写到GPS芯片中,可以加速GPS定位
|
||||
-- 会定期更新GPS星历,星历更新算法如下:
|
||||
-- 从最后一次GPS定位成功的时间算起,每隔4小时连接星历服务器下载一次星历数据(大概4K字节),写入GPS芯片。
|
||||
-- 例如01:00分开机后,更新了一次星历文件,截止到05:00,“一直没有开启过GPS”或者“开启过GPS,但是GPS从来没有定位成功”,在05:00就会下载星历数据然后写入GPS芯片;
|
||||
-- 05:00更新星历数据后,在06:00打开了GPS,并且GPS定位成功,然后在07:00关闭了GPS,关闭前GPS仍然处于定位成功状态;
|
||||
-- 截止到11:00,“一直没有开启过GPS”或者“开启过GPS,但是GPS从来没有定位成功”,在11:00就会下载星历数据然后写入GPS芯片;
|
||||
-- @module agps
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2018.03.26
|
||||
|
||||
require"http"
|
||||
require"lbsLoc"
|
||||
require"net"
|
||||
require"gps"
|
||||
module(..., package.seeall)
|
||||
|
||||
local EPH_TIME_FILE = "/ephTime.txt"
|
||||
local EPH_DATA_FILE = "/ephData.bin"
|
||||
local writeEphIdx,sEphData,writeEphSta = 0
|
||||
local EPH_UPDATE_INTERVAL = 4*3600
|
||||
local lastLbsLng,lastLbsLat = "",""
|
||||
local fneed_xingli=false
|
||||
|
||||
local function runTimer()
|
||||
sys.timerStart(updateEph,EPH_UPDATE_INTERVAL*1000)
|
||||
end
|
||||
|
||||
local function writeEphEnd()
|
||||
log.info("agps.writeEphEnd")
|
||||
local cmd,sum = (("AAF00E00950000"):fromHex())..pack.pack("<i",gps.uartBaudrate),0
|
||||
for i=3,cmd:len() do
|
||||
sum = bit.bxor(sum,cmd:byte(i))
|
||||
end
|
||||
gps.writeCmd(cmd..string.char(sum).."\r\n")
|
||||
sys.timerStart(gps.close,2000,gps.TIMER,{tag="lib.agps.lua.eph"})
|
||||
writeEphIdx,sEphData,writeEphSta = 0
|
||||
end
|
||||
|
||||
local function writeEph()
|
||||
log.info("agps.writeEph",writeEphSta)
|
||||
if writeEphSta=="IDLE" then
|
||||
gps.writeCmd("$PGKC149,1,"..gps.uartBaudrate.."*")
|
||||
writeEphSta = "WAIT_BINARY_CMD_ACK"
|
||||
elseif writeEphSta=="WAIT_BINARY_CMD_ACK" or writeEphSta=="WAIT_WRITE_EPH_CMD_ACK" then
|
||||
if sEphData and sEphData:len()>0 then
|
||||
local hexStr = sEphData:sub(1,1024)
|
||||
if hexStr:len()<1024 then hexStr = hexStr..("F"):rep(1024-hexStr:len()) end
|
||||
hexStr = "AAF00B026602"..string.format("%02X",writeEphIdx):upper().."00"..hexStr
|
||||
|
||||
local checkSum = 0
|
||||
local binStr = hexStr:fromHex()
|
||||
for i=3,binStr:len() do
|
||||
checkSum = bit.bxor(checkSum,binStr:byte(i))
|
||||
end
|
||||
string.format("%02X",checkSum):upper()
|
||||
|
||||
hexStr = hexStr..(string.format("%02X",checkSum):upper()).."0D0A"
|
||||
gps.writeCmd(hexStr:fromHex(),true)
|
||||
|
||||
sEphData = sEphData:sub(1025,-1)
|
||||
writeEphIdx = writeEphIdx+1
|
||||
writeEphSta = "WAIT_WRITE_EPH_CMD_ACK"
|
||||
else
|
||||
gps.writeCmd(("AAF00B006602FFFF6F0D0A"):fromHex(),true)
|
||||
writeEphSta = "WAIT_WRITE_EPH_END_CMD_ACK"
|
||||
end
|
||||
elseif writeEphSta=="WAIT_WRITE_EPH_END_CMD_ACK" then
|
||||
io.writeFile(EPH_TIME_FILE,tostring(os.time()))
|
||||
writeEphEnd()
|
||||
end
|
||||
end
|
||||
|
||||
local function writeEphBegin()
|
||||
writeEphSta = "IDLE"
|
||||
writeEph()
|
||||
end
|
||||
|
||||
local function downloadEphCb(result,prompt,head,body)
|
||||
log.info("agps.downloadEphCb",result,prompt)
|
||||
runTimer()
|
||||
if result and prompt=="200" and body then
|
||||
io.writeFile(EPH_DATA_FILE,body)
|
||||
if gps.isFix() then
|
||||
io.writeFile(EPH_TIME_FILE,tostring(os.time()))
|
||||
else
|
||||
fneed_xingli=false
|
||||
sEphData = body:toHex()
|
||||
gps.open(gps.TIMER,{tag="lib.agps.lua.eph",val=10,cb=writeEphEnd})
|
||||
sys.timerStart(writeEphBegin,2000)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--连接服务器下载星历
|
||||
function updateEph()
|
||||
if gps.isFix() then runTimer() return end
|
||||
http.request("GET","download.openluat.com/9501-xingli/brdcGPD.dat_rda",nil,nil,nil,20000,downloadEphCb)
|
||||
end
|
||||
|
||||
|
||||
|
||||
--JWL在星历的时间范围内,如果有星历文件,重启模块就直接把本地的星历文件写入GPS芯片
|
||||
--在AGPS_LOCATED 的订阅事件中,调用此接口。
|
||||
function upd_xingli()
|
||||
if not gps.isFix() then
|
||||
local lstm = io.readFile(EPH_TIME_FILE)
|
||||
if not lstm or lstm=="" then return end
|
||||
log.info("agps.upd_xingli lstm=",lstm)
|
||||
if os.time()-tonumber(lstm) < EPH_UPDATE_INTERVAL then
|
||||
local body = io.readFile(EPH_DATA_FILE)
|
||||
if body and #body >0 then
|
||||
log.info("agps.upd_xingli length=",#body)
|
||||
fneed_xingli=false
|
||||
sEphData = body:toHex()
|
||||
gps.open(gps.TIMER,{tag="lib.agps.lua.eph",val=10,cb=writeEphEnd})
|
||||
sys.timerStart(writeEphBegin,2000)
|
||||
else
|
||||
log.info("agps.upd_xingli wanto update xingli data")
|
||||
updateEph()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
sys.subscribe("AGPS_LOCATED", function()
|
||||
sys.timerStart(function()
|
||||
log.info("agps.want to upd_xingli",fneed_xingli)
|
||||
if fneed_xingli then
|
||||
upd_xingli()
|
||||
end
|
||||
end,5000)
|
||||
end)
|
||||
|
||||
--检查是否需要更新星历
|
||||
local function checkEph()
|
||||
local result
|
||||
if not gps.isFix() then
|
||||
lastTm = io.readFile(EPH_TIME_FILE)
|
||||
if not lastTm or lastTm=="" then return true end
|
||||
log.info("agps.checkEph",os.time(),tonumber(lastTm)," DELTA=",os.time()-tonumber(lastTm))
|
||||
result = (os.time()-tonumber(lastTm) >= EPH_UPDATE_INTERVAL)
|
||||
end
|
||||
if not result then runTimer() end
|
||||
return result
|
||||
end
|
||||
|
||||
local function setFastFix(lng,lat,tm)
|
||||
gps.setFastFix(lat,lng,tm)
|
||||
if checkEph() then updateEph() end
|
||||
end
|
||||
|
||||
local getloc = 0
|
||||
local lbsLocRequesting
|
||||
--获取到基站对应的经纬度,写到GPS芯片中
|
||||
local function getLocCb(result,lat,lng,addr,time)
|
||||
fneed_xingli=true
|
||||
log.info("agps.getLocCb",result,lat,lng,time and time:len() or 0)
|
||||
lbsLocRequesting = false
|
||||
if result==0 then
|
||||
lastLbsLng,lastLbsLat = lng,lat
|
||||
sys.publish("AGPS_LOCATED", lng,lat)
|
||||
if not gps.isFix() then
|
||||
local tm = {year=0,month=0,day=0,hour=0,min=0,sec=0}
|
||||
if time:len()==6 then
|
||||
tm = {year=time:byte(1)+2000,month=time:byte(2),day=time:byte(3),hour=time:byte(4),min=time:byte(5),sec=time:byte(6)}
|
||||
misc.setClock(tm)
|
||||
tm = common.timeZoneConvert(tm.year,tm.month,tm.day,tm.hour,tm.min,tm.sec,8,0)
|
||||
end
|
||||
gps.open(gps.TIMERORSUC,{tag="lib.agps.lua.fastFix",val=4})
|
||||
sys.timerStart(setFastFix,2000,lng,lat,tm)
|
||||
getloc = 1
|
||||
end
|
||||
end
|
||||
|
||||
if result~=0 or gps.isFix() then
|
||||
if checkEph() then updateEph() end
|
||||
end
|
||||
end
|
||||
|
||||
--是否获取到基站对应的经纬度
|
||||
function isgetloc()
|
||||
return getloc
|
||||
end
|
||||
|
||||
local function ipReady()
|
||||
if gps.isFix() then
|
||||
runTimer()
|
||||
else
|
||||
if not lbsLocRequesting then
|
||||
lbsLocRequesting = true
|
||||
lbsLoc.request(getLocCb,nil,30000,"0","bs.openluat.com","12411",true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function gpsState(evt,para)
|
||||
log.info("agps.GPS_STATE",evt,para)
|
||||
if evt=="LOCATION_SUCCESS" or (evt=="CLOSE" and para==true) then
|
||||
runTimer()
|
||||
elseif evt=="BINARY_CMD_ACK" or evt=="WRITE_EPH_ACK" or evt=="WRITE_EPH_END_ACK" then
|
||||
writeEph()
|
||||
elseif evt=="OPEN" then
|
||||
local lng,lat = gps.getLastLocation()
|
||||
if lng=="" or lat=="" then
|
||||
lng,lat = lastLbsLng,lastLbsLat
|
||||
end
|
||||
if lng~="" and lat~="" then
|
||||
gps.open(gps.TIMERORSUC,{tag="lib.agps.lua.fastFix",val=4})
|
||||
local tm = os.date("*t")
|
||||
sys.timerStart(gps.setFastFix,2000,lat,lng,common.timeZoneConvert(tm.year,tm.month,tm.day,tm.hour,tm.min,tm.sec,8,0))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function init()
|
||||
sys.subscribe("GPS_STATE",gpsState)
|
||||
sys.subscribe("IP_READY_IND",ipReady)
|
||||
log.info("agps.init")
|
||||
end
|
||||
|
||||
function unInit()
|
||||
sys.unsubscribe("GPS_STATE",gpsState)
|
||||
sys.unsubscribe("IP_READY_IND",ipReady)
|
||||
log.info("agps.unInit")
|
||||
end
|
||||
|
||||
init()
|
||||
292
4G/源代码/lib/agps9701.lua
Normal file
292
4G/源代码/lib/agps9701.lua
Normal file
@@ -0,0 +1,292 @@
|
||||
--- 模块功能:GPS辅助定位以及星历更新服务.
|
||||
-- 本功能模块只能配合Air512G、Air551G、Air552G使用;
|
||||
-- require"agps"后,会自动开启本功能模块的任务;
|
||||
-- 开机后,仅获取一次基站对应的经纬度位置和当前时间,把经纬度位置和时间写到GPS芯片中,可以加速GPS定位
|
||||
-- 会定期更新GPS星历,星历更新算法如下:
|
||||
-- 从最后一次GPS定位成功的时间算起,每隔4小时连接星历服务器下载一次星历数据(大概4K字节),写入GPS芯片。
|
||||
-- 例如01:00分开机后,更新了一次星历文件,截止到05:00,“一直没有开启过GPS”或者“开启过GPS,但是GPS从来没有定位成功”,在05:00就会下载星历数据然后写入GPS芯片;
|
||||
-- 05:00更新星历数据后,在06:00打开了GPS,并且GPS定位成功,然后在07:00关闭了GPS,关闭前GPS仍然处于定位成功状态;
|
||||
-- 截止到11:00,“一直没有开启过GPS”或者“开启过GPS,但是GPS从来没有定位成功”,在11:00就会下载星历数据然后写入GPS芯片;
|
||||
-- @module agps9701
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2018.03.26
|
||||
|
||||
require"http"
|
||||
require"lbsLoc"
|
||||
require"net"
|
||||
local gps = require"gps9701"
|
||||
module(..., package.seeall)
|
||||
|
||||
local EPH_TIME_FILE = "/ephTime.txt"
|
||||
local EPH_DATA_FILE = "/ephData.bin"
|
||||
local writeEphIdx,sEphData,writeEphSta = 0
|
||||
local EPH_UPDATE_INTERVAL = 4*3600
|
||||
local lastLbsLng,lastLbsLat = "",""
|
||||
local fneed_xingli=false
|
||||
local cachepack = ""
|
||||
local gpsresult = nil
|
||||
local agpsend = nil
|
||||
local tasklock = true
|
||||
|
||||
--重置写入agps星历包
|
||||
local function agpsreag()
|
||||
tasklock = false
|
||||
sys.taskInit(function()
|
||||
while true do
|
||||
if agpsend then
|
||||
log.info("agpsend")
|
||||
tasklock = true
|
||||
agpsend = false
|
||||
break
|
||||
end
|
||||
sys.wait(1000)
|
||||
if gps.checkdatapack then
|
||||
while true do
|
||||
if agpsend then
|
||||
break
|
||||
end
|
||||
gpsresult = sys.waitUntil("NOEMAL_RECE",1000)
|
||||
if (not gpsresult) and (not agpsend) then
|
||||
gps.writeCmd(cachepack:fromHex(),true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local function runTimer()
|
||||
sys.timerStart(updateEph,EPH_UPDATE_INTERVAL*1000)
|
||||
end
|
||||
|
||||
local function writeEphEnd()
|
||||
agpsend = true
|
||||
log.info("agps.writeEphEnd",pack.pack("<i",gps.uartBaudrate):toHex())
|
||||
local cmd,sum = (("AAF00F00950000"):fromHex())..pack.pack("<i",gps.uartBaudrate),0
|
||||
for i=3,cmd:len() do
|
||||
sum = bit.bxor(sum,cmd:byte(i))
|
||||
end
|
||||
local validate = "00AA0F"
|
||||
gps.writeCmd(cmd..string.char(sum)..validate:fromHex(),true)
|
||||
sys.timerStart(gps.close,2000,gps.TIMER,{tag="lib.agps.lua.eph"})
|
||||
writeEphIdx,sEphData,writeEphSta = 0
|
||||
end
|
||||
|
||||
local function writeEph()
|
||||
log.info("agps.writeEph",writeEphSta)
|
||||
if writeEphSta=="IDLE" then
|
||||
if tasklock then
|
||||
agpsreag()
|
||||
end
|
||||
gps.writeCmd("$PGKC149,1,"..gps.uartBaudrate.."*")
|
||||
writeEphSta = "WAIT_BINARY_CMD_ACK"
|
||||
elseif writeEphSta=="WAIT_BINARY_CMD_ACK" or writeEphSta=="WAIT_WRITE_EPH_CMD_ACK" then
|
||||
if sEphData and sEphData:len()>0 then
|
||||
local hexStr = sEphData:sub(1,1024)
|
||||
if hexStr:len()<1024 then hexStr = hexStr..("0"):rep(1024-hexStr:len()) end
|
||||
hexStr = "AAF00C026602"..string.format("%02X",writeEphIdx):upper().."00"..hexStr
|
||||
local checkSum = 0
|
||||
local binStr = hexStr:fromHex()
|
||||
for i=3,binStr:len() do
|
||||
checkSum = bit.bxor(checkSum,binStr:byte(i))
|
||||
end
|
||||
string.format("%02X",checkSum):upper()
|
||||
|
||||
hexStr = hexStr..(string.format("%02X",checkSum):upper()).."00".."AA0F"
|
||||
cachepack = hexStr
|
||||
gps.writeCmd(hexStr:fromHex(),true)
|
||||
|
||||
sEphData = sEphData:sub(1025,-1)
|
||||
writeEphIdx = writeEphIdx+1
|
||||
writeEphSta = "WAIT_WRITE_EPH_CMD_ACK"
|
||||
else
|
||||
gps.writeCmd(("AAF00C006602FFFF6800AA0F"):fromHex(),true)
|
||||
writeEphSta = "WAIT_WRITE_EPH_END_CMD_ACK"
|
||||
end
|
||||
elseif writeEphSta=="WAIT_WRITE_EPH_END_CMD_ACK" then
|
||||
io.writeFile(EPH_TIME_FILE,tostring(os.time()))
|
||||
writeEphEnd()
|
||||
end
|
||||
end
|
||||
|
||||
-- sys.taskInit(function()
|
||||
-- sys.waitUntil()
|
||||
-- while true do
|
||||
-- if agpsend then
|
||||
-- log.info("agpsend")
|
||||
-- break
|
||||
-- end
|
||||
-- sys.wait(1000)
|
||||
-- if gps.checkdatapack then
|
||||
-- while true do
|
||||
-- if agpsend then
|
||||
-- break
|
||||
-- end
|
||||
-- gpsresult = sys.waitUntil("NOEMAL_RECE",1000)
|
||||
-- if (not gpsresult) and (not agpsend) then
|
||||
-- gps.writeCmd(cachepack:fromHex(),true)
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
-- end)
|
||||
|
||||
local function writeEphBegin()
|
||||
writeEphSta = "IDLE"
|
||||
writeEph()
|
||||
end
|
||||
|
||||
local function downloadEphCb(result,prompt,head,body)
|
||||
log.info("agps.downloadEphCb",result,prompt,body)
|
||||
runTimer()
|
||||
if result and prompt=="200" and body then
|
||||
io.writeFile(EPH_DATA_FILE,body)
|
||||
if gps.isFix() then
|
||||
io.writeFile(EPH_TIME_FILE,tostring(os.time()))
|
||||
else
|
||||
fneed_xingli=false
|
||||
sEphData = body:toHex()
|
||||
gps.open(gps.TIMER,{tag="lib.agps.lua.eph",val=10,cb=writeEphEnd})
|
||||
sys.timerStart(writeEphBegin,2000)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--连接服务器下载星历
|
||||
function updateEph()
|
||||
if gps.isFix() then runTimer() return end
|
||||
http.request("GET","download.openluat.com/9501-xingli/brdcGPD.dat_rda",nil,nil,nil,20000,downloadEphCb)
|
||||
end
|
||||
|
||||
|
||||
|
||||
--JWL在星历的时间范围内,如果有星历文件,重启模块就直接把本地的星历文件写入GPS芯片
|
||||
--在AGPS_LOCATED 的订阅事件中,调用此接口。
|
||||
function upd_xingli()
|
||||
if not gps.isFix() then
|
||||
local lstm = io.readFile(EPH_TIME_FILE)
|
||||
if not lstm or lstm=="" then return end
|
||||
log.info("agps.upd_xingli lstm=",lstm)
|
||||
if os.time()-tonumber(lstm) < EPH_UPDATE_INTERVAL then
|
||||
local body = io.readFile(EPH_DATA_FILE)
|
||||
if body and #body >0 then
|
||||
log.info("agps.upd_xingli length=",#body)
|
||||
fneed_xingli=false
|
||||
sEphData = body:toHex()
|
||||
gps.open(gps.TIMER,{tag="lib.agps.lua.eph",val=10,cb=writeEphEnd})
|
||||
sys.timerStart(writeEphBegin,2000)
|
||||
else
|
||||
log.info("agps.upd_xingli wanto update xingli data")
|
||||
updateEph()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
sys.subscribe("AGPS_LOCATED", function()
|
||||
sys.timerStart(function()
|
||||
log.info("agps.want to upd_xingli",fneed_xingli)
|
||||
if fneed_xingli then
|
||||
upd_xingli()
|
||||
end
|
||||
end,7000)
|
||||
end)
|
||||
|
||||
--检查是否需要更新星历
|
||||
local function checkEph()
|
||||
local result
|
||||
if not gps.isFix() then
|
||||
lastTm = io.readFile(EPH_TIME_FILE)
|
||||
if not lastTm or lastTm=="" then return true end
|
||||
log.info("agps.checkEph",os.time(),tonumber(lastTm)," DELTA=",os.time()-tonumber(lastTm))
|
||||
result = (os.time()-tonumber(lastTm) >= EPH_UPDATE_INTERVAL)
|
||||
end
|
||||
if not result then runTimer() end
|
||||
return result
|
||||
end
|
||||
|
||||
local function setFastFix(lng,lat,tm)
|
||||
gps.setFastFix(lat,lng,tm)
|
||||
if checkEph() then
|
||||
updateEph()
|
||||
end
|
||||
end
|
||||
|
||||
local getloc = 0
|
||||
local lbsLocRequesting
|
||||
--获取到基站对应的经纬度,写到GPS芯片中
|
||||
local function getLocCb(result,lat,lng,addr,time)
|
||||
fneed_xingli=true
|
||||
log.info("agps.getLocCb",result,lat,lng,time and time:len() or 0)
|
||||
lbsLocRequesting = false
|
||||
if result==0 then
|
||||
lastLbsLng,lastLbsLat = lng,lat
|
||||
sys.publish("AGPS_LOCATED", lng,lat)
|
||||
if not gps.isFix() then
|
||||
local tm = {year=0,month=0,day=0,hour=0,min=0,sec=0}
|
||||
if time:len()==6 then
|
||||
tm = {year=time:byte(1)+2000,month=time:byte(2),day=time:byte(3),hour=time:byte(4),min=time:byte(5),sec=time:byte(6)}
|
||||
misc.setClock(tm)
|
||||
tm = common.timeZoneConvert(tm.year,tm.month,tm.day,tm.hour,tm.min,tm.sec,8,0)
|
||||
end
|
||||
gps.open(gps.TIMERORSUC,{tag="lib.agps.lua.fastFix",val=4})
|
||||
sys.timerStart(setFastFix,2000,lng,lat,tm)
|
||||
getloc = 1
|
||||
end
|
||||
end
|
||||
|
||||
if result~=0 or gps.isFix() then
|
||||
if checkEph() then updateEph() end
|
||||
end
|
||||
end
|
||||
|
||||
--是否获取到基站对应的经纬度
|
||||
function isgetloc()
|
||||
return getloc
|
||||
end
|
||||
|
||||
local function ipReady()
|
||||
if gps.isFix() then
|
||||
runTimer()
|
||||
else
|
||||
if not lbsLocRequesting then
|
||||
lbsLocRequesting = true
|
||||
lbsLoc.request(getLocCb,nil,30000,"0","bs.openluat.com","12411",true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function gpsState(evt,para)
|
||||
log.info("agps.GPS_STATE",evt,para)
|
||||
if evt=="LOCATION_SUCCESS" or (evt=="CLOSE" and para==true) then
|
||||
runTimer()
|
||||
elseif evt=="BINARY_CMD_ACK" or evt=="WRITE_EPH_ACK" or evt=="WRITE_EPH_END_ACK" then
|
||||
writeEph()
|
||||
elseif evt=="OPEN" then
|
||||
local lng,lat = gps.getLastLocation()
|
||||
if lng=="" or lat=="" then
|
||||
lng,lat = lastLbsLng,lastLbsLat
|
||||
end
|
||||
if lng~="" and lat~="" then
|
||||
gps.open(gps.TIMERORSUC,{tag="lib.agps.lua.fastFix",val=4})
|
||||
local tm = os.date("*t")
|
||||
sys.timerStart(gps.setFastFix,2000,lat,lng,common.timeZoneConvert(tm.year,tm.month,tm.day,tm.hour,tm.min,tm.sec,8,0))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function init()
|
||||
sys.subscribe("GPS_STATE",gpsState)
|
||||
sys.subscribe("IP_READY_IND",ipReady)
|
||||
log.info("agps.init")
|
||||
end
|
||||
|
||||
function unInit()
|
||||
sys.unsubscribe("GPS_STATE",gpsState)
|
||||
sys.unsubscribe("IP_READY_IND",ipReady)
|
||||
log.info("agps.unInit")
|
||||
end
|
||||
|
||||
init()
|
||||
194
4G/源代码/lib/agpsHxxt.lua
Normal file
194
4G/源代码/lib/agpsHxxt.lua
Normal file
@@ -0,0 +1,194 @@
|
||||
--- 模块功能:GPS辅助定位以及星历更新服务.
|
||||
-- 本功能模块只能配合Air820UX系列的模块以及Air530H模块,和芯星通GPS芯片使用;
|
||||
-- require"agpsHxxt"后,会自动开启本功能模块的任务;
|
||||
-- 会定期更新GPS星历,星历更新算法如下:
|
||||
-- 从最后一次GPS定位成功的时间算起,每隔4小时连接星历服务器下载一次星历数据(大概4K字节),写入GPS芯片。
|
||||
-- 例如01:00分开机后,更新了一次星历文件,截止到05:00,“一直没有开启过GPS”或者“开启过GPS,但是GPS从来没有定位成功”,在05:00就会下载星历数据然后写入GPS芯片;
|
||||
-- 05:00更新星历数据后,在06:00打开了GPS,并且GPS定位成功,然后在07:00关闭了GPS,关闭前GPS仍然处于定位成功状态;
|
||||
-- 截止到11:00,“一直没有开启过GPS”或者“开启过GPS,但是GPS从来没有定位成功”,在11:00就会下载星历数据然后写入GPS芯片;
|
||||
-- @module agpsHxxt
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2020.10.28
|
||||
|
||||
require"http"
|
||||
require"lbsLoc"
|
||||
require"net"
|
||||
local gps = require"gpsHxxt"
|
||||
module(..., package.seeall)
|
||||
|
||||
local EPH_TIME_FILE = "/ephTime.txt"
|
||||
local EPH_DATA_FILE = "/ephData.bin"
|
||||
local sEphData
|
||||
local EPH_UPDATE_INTERVAL = 3*3600
|
||||
local lastLbsLng,lastLbsLat = "",""
|
||||
|
||||
local function runTimer()
|
||||
sys.timerStart(updateEph,EPH_UPDATE_INTERVAL*1000)
|
||||
end
|
||||
|
||||
local function writeEphEnd()
|
||||
log.info("agpsHxxt.writeEphEnd")
|
||||
sys.timerStart(gps.close,3000,gps.TIMER,{tag="lib.agpsHxxt.lua.eph"})
|
||||
sEphData = nil
|
||||
end
|
||||
|
||||
local function writeEph()
|
||||
log.info("agpsHxxt.writeEph")
|
||||
gps.writeData(sEphData)
|
||||
--gps.writeData(io.readFile("/lua/aid_data.bin"))
|
||||
--uart.setup(2, 9600, 8, uart.PAR_NONE, uart.STOP_1)
|
||||
--uart.write(2,io.readFile("/lua/aid_data.bin"))
|
||||
writeEphEnd()
|
||||
end
|
||||
|
||||
local function downloadEphCb(result,prompt,head,body)
|
||||
log.info("agpsHxxt.downloadEphCb",result,prompt)
|
||||
runTimer()
|
||||
if result and prompt=="200" and body then
|
||||
io.writeFile(EPH_DATA_FILE,body)
|
||||
io.writeFile(EPH_TIME_FILE,tostring(os.time()))
|
||||
if gps.isFix() then
|
||||
-- io.writeFile(EPH_TIME_FILE,tostring(os.time()))
|
||||
else
|
||||
sEphData = body
|
||||
gps.open(gps.TIMER,{tag="lib.agpsHxxt.lua.eph",val=10,cb=writeEphEnd})
|
||||
sys.timerStart(writeEph,2000)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--连接服务器下载星历
|
||||
function updateEph()
|
||||
if gps.isFix() then runTimer() return end
|
||||
http.request("GET","download.openluat.com:80/9501-xingli/HXXT_GPS_BDS_AGNSS_DATA.dat",nil,nil,nil,20000,downloadEphCb)
|
||||
end
|
||||
|
||||
|
||||
|
||||
--JWL在星历的时间范围内,如果有星历文件,重启模块就直接把本地的星历文件写入GPS芯片
|
||||
--在AGPS_LOCATED 的订阅事件中,调用此接口。
|
||||
function upd_xingli()
|
||||
if not gps.isFix() then
|
||||
local lstm = io.readFile(EPH_TIME_FILE)
|
||||
if not lstm or lstm=="" then return end
|
||||
log.info("agpsHxxt.upd_xingli lstm=",lstm)
|
||||
if os.time()-tonumber(lstm) < EPH_UPDATE_INTERVAL then
|
||||
local body = io.readFile(EPH_DATA_FILE)
|
||||
if body and #body >0 then
|
||||
log.info("agpsHxxt.upd_xingli length=",#body)
|
||||
sEphData = body
|
||||
gps.open(gps.TIMER,{tag="lib.agpsHxxt.lua.eph",val=10,cb=writeEphEnd})
|
||||
sys.timerStart(writeEph,2000)
|
||||
else
|
||||
log.info("agpsHxxt.upd_xingli wanto update xingli data")
|
||||
updateEph()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--检查是否需要更新星历
|
||||
local function checkEph()
|
||||
local result
|
||||
if not gps.isFix() then
|
||||
lastTm = io.readFile(EPH_TIME_FILE)
|
||||
if not lastTm or lastTm=="" then return true end
|
||||
log.info("agpsHxxt.checkEph",os.time(),tonumber(lastTm)," DELTA=",os.time()-tonumber(lastTm))
|
||||
result = (os.time()-tonumber(lastTm) >= EPH_UPDATE_INTERVAL)
|
||||
end
|
||||
if not result then runTimer() end
|
||||
return result
|
||||
end
|
||||
|
||||
|
||||
|
||||
local function setFastFix(lng,lat,tm)
|
||||
gps.setFastFix(lat,lng,tm)
|
||||
if checkEph() then updateEph()end
|
||||
end
|
||||
|
||||
local getloc = 0
|
||||
local lbsLocRequesting
|
||||
--获取到基站对应的经纬度,写到GPS芯片中
|
||||
local function getLocCb(result,lat,lng,addr,time)
|
||||
log.info("agpsHxxt.getLocCb",result,lat,lng,time and time:len() or 0)
|
||||
lbsLocRequesting = false
|
||||
if result==0 then
|
||||
lastLbsLng,lastLbsLat = lng,lat
|
||||
if not gps.isFix() then
|
||||
local tm = {year=0,month=0,day=0,hour=0,min=0,sec=0}
|
||||
if time:len()==6 then
|
||||
tm = {year=time:byte(1)+2000,month=time:byte(2),day=time:byte(3),hour=time:byte(4),min=time:byte(5),sec=time:byte(6)}
|
||||
misc.setClock(tm)
|
||||
tm = common.timeZoneConvert(tm.year,tm.month,tm.day,tm.hour,tm.min,tm.sec,8,0)
|
||||
end
|
||||
gps.open(gps.TIMERORSUC,{tag="lib.agpsHxxt.lua.fastFix",val=4})
|
||||
sys.timerStart(setFastFix,2000,lng,lat,tm)
|
||||
getloc = 1
|
||||
end
|
||||
end
|
||||
|
||||
if result~=0 or gps.isFix() then
|
||||
if checkEph() then updateEph() end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
--是否获取到基站对应的经纬度
|
||||
function isgetloc()
|
||||
return getloc
|
||||
end
|
||||
|
||||
local function ipReady()
|
||||
if gps.isFix() then
|
||||
runTimer()
|
||||
else
|
||||
if not lbsLocRequesting then
|
||||
lbsLocRequesting = true
|
||||
lbsLoc.request(getLocCb,nil,30000,"0","bs.openluat.com","12411",true)
|
||||
end
|
||||
log.info("agpsHxxt.ipready to updateEph")
|
||||
if checkEph() then
|
||||
updateEph()
|
||||
else
|
||||
sys.timerStart(upd_xingli,3000)
|
||||
end
|
||||
end
|
||||
end
|
||||
local function gpsState(evt,para)
|
||||
log.info("agpsHxxt.GPS_STATE",evt,para)
|
||||
if evt=="LOCATION_SUCCESS" or (evt=="CLOSE" and para==true) then
|
||||
runTimer()
|
||||
elseif evt=="OPEN" then
|
||||
local lng,lat = gps.getLastLocation()
|
||||
if lng=="" or lat=="" then
|
||||
lng,lat = lastLbsLng,lastLbsLat
|
||||
end
|
||||
if lng~="" and lat~="" then
|
||||
gps.open(gps.TIMERORSUC,{tag="lib.agpsHxxt.lua.fastFix",val=4})
|
||||
local tm = os.date("*t")
|
||||
sys.timerStart(gps.setFastFix,2000,lat,lng,common.timeZoneConvert(tm.year,tm.month,tm.day,tm.hour,tm.min,tm.sec,8,0))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
function init()
|
||||
sys.subscribe("GPS_STATE",gpsState)
|
||||
sys.subscribe("IP_READY_IND",ipReady)
|
||||
log.info("agpsHxxt.init")
|
||||
end
|
||||
|
||||
function unInit()
|
||||
sys.unsubscribe("GPS_STATE",gpsState)
|
||||
sys.unsubscribe("IP_READY_IND",ipReady)
|
||||
log.info("agpsHxxt.unInit")
|
||||
end
|
||||
|
||||
init()
|
||||
193
4G/源代码/lib/agpsZkw.lua
Normal file
193
4G/源代码/lib/agpsZkw.lua
Normal file
@@ -0,0 +1,193 @@
|
||||
--- 模块功能:GPS辅助定位以及星历更新服务.
|
||||
-- 本功能模块只能配合Air820UX系列的模块以及Air530Z模块,中科微GPS芯片使用;
|
||||
-- require"agpsZkw"后,会自动开启本功能模块的任务;
|
||||
-- 会定期更新GPS星历,星历更新算法如下:
|
||||
-- 从最后一次GPS定位成功的时间算起,每隔4小时连接星历服务器下载一次星历数据(大概4K字节),写入GPS芯片。
|
||||
-- 例如01:00分开机后,更新了一次星历文件,截止到05:00,“一直没有开启过GPS”或者“开启过GPS,但是GPS从来没有定位成功”,在05:00就会下载星历数据然后写入GPS芯片;
|
||||
-- 05:00更新星历数据后,在06:00打开了GPS,并且GPS定位成功,然后在07:00关闭了GPS,关闭前GPS仍然处于定位成功状态;
|
||||
-- 截止到11:00,“一直没有开启过GPS”或者“开启过GPS,但是GPS从来没有定位成功”,在11:00就会下载星历数据然后写入GPS芯片;
|
||||
-- @module agpsZkw
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2020.10.28
|
||||
|
||||
require"http"
|
||||
require"lbsLoc"
|
||||
require"net"
|
||||
local gps = require"gpsZkw"
|
||||
module(..., package.seeall)
|
||||
|
||||
local EPH_TIME_FILE = "/ephTime.txt"
|
||||
local EPH_DATA_FILE = "/ephData.bin"
|
||||
local sEphData
|
||||
local EPH_UPDATE_INTERVAL = 4*3600
|
||||
local lastLbsLng,lastLbsLat = "",""
|
||||
|
||||
local function runTimer()
|
||||
sys.timerStart(updateEph,EPH_UPDATE_INTERVAL*1000)
|
||||
end
|
||||
|
||||
local function writeEphEnd()
|
||||
log.info("agpsZkw.writeEphEnd")
|
||||
sys.timerStart(gps.close,3000,gps.TIMER,{tag="lib.agpsZkw.lua.eph"})
|
||||
sEphData = nil
|
||||
end
|
||||
|
||||
local function writeEph()
|
||||
log.info("agpsZkw.writeEph")
|
||||
gps.writeData(sEphData)
|
||||
writeEphEnd()
|
||||
end
|
||||
|
||||
local function downloadEphCb(result,prompt,head,body)
|
||||
log.info("agpsZkw.downloadEphCb",result,prompt)
|
||||
runTimer()
|
||||
if result and prompt=="200" and body then
|
||||
io.writeFile(EPH_DATA_FILE,body)
|
||||
io.writeFile(EPH_TIME_FILE,tostring(os.time()))
|
||||
if gps.isFix() then
|
||||
|
||||
else
|
||||
sEphData = body
|
||||
gps.open(gps.TIMER,{tag="lib.agpsZkw.lua.eph",val=10,cb=writeEphEnd})
|
||||
sys.timerStart(writeEph,2000)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--连接服务器下载星历
|
||||
function updateEph()
|
||||
if gps.isFix() then runTimer() return end
|
||||
http.request("GET","http://download.openluat.com/9501-xingli/CASIC_data.dat",nil,nil,nil,20000,downloadEphCb)
|
||||
end
|
||||
|
||||
|
||||
|
||||
--JWL在星历的时间范围内,如果有星历文件,重启模块就直接把本地的星历文件写入GPS芯片
|
||||
--在AGPS_LOCATED 的订阅事件中,调用此接口。
|
||||
function upd_xingli()
|
||||
if not gps.isFix() then
|
||||
local lstm = io.readFile(EPH_TIME_FILE)
|
||||
if not lstm or lstm=="" then return end
|
||||
log.info("agpsZkw.upd_xingli lstm=",lstm)
|
||||
if os.time()-tonumber(lstm) < EPH_UPDATE_INTERVAL then
|
||||
local body = io.readFile(EPH_DATA_FILE)
|
||||
if body and #body >0 then
|
||||
log.info("agpsZkw.upd_xingli length=",#body)
|
||||
sEphData = body
|
||||
gps.open(gps.TIMER,{tag="lib.agpsZkw.lua.eph",val=10,cb=writeEphEnd})
|
||||
sys.timerStart(writeEph,2000)
|
||||
else
|
||||
log.info("agpsZkw.upd_xingli wanto update xingli data")
|
||||
updateEph()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--检查是否需要更新星历
|
||||
local function checkEph()
|
||||
local result
|
||||
if not gps.isFix() then
|
||||
lastTm = io.readFile(EPH_TIME_FILE)
|
||||
if not lastTm or lastTm=="" then return true end
|
||||
log.info("agpsZkw.checkEph",os.time(),tonumber(lastTm)," DELTA=",os.time()-tonumber(lastTm))
|
||||
result = (os.time()-tonumber(lastTm) >= EPH_UPDATE_INTERVAL)
|
||||
end
|
||||
if not result then runTimer() end
|
||||
return result
|
||||
end
|
||||
|
||||
|
||||
|
||||
local function setFastFix(lng,lat,tm)
|
||||
gps.setFastFix(lat,lng,tm)
|
||||
if checkEph() then updateEph() end
|
||||
end
|
||||
|
||||
local getloc = 0
|
||||
local lbsLocRequesting
|
||||
--获取到基站对应的经纬度,写到GPS芯片中
|
||||
local function getLocCb(result,lat,lng,addr,time)
|
||||
log.info("agpsZkw.getLocCb",result,lat,lng,time and time:len() or 0)
|
||||
lbsLocRequesting = false
|
||||
if result==0 then
|
||||
lastLbsLng,lastLbsLat = lng,lat
|
||||
if not gps.isFix() then
|
||||
local tm = {year=0,month=0,day=0,hour=0,min=0,sec=0}
|
||||
if time:len()==6 then
|
||||
tm = {year=time:byte(1)+2000,month=time:byte(2),day=time:byte(3),hour=time:byte(4),min=time:byte(5),sec=time:byte(6)}
|
||||
misc.setClock(tm)
|
||||
tm = common.timeZoneConvert(tm.year,tm.month,tm.day,tm.hour,tm.min,tm.sec,8,0)
|
||||
end
|
||||
gps.open(gps.TIMERORSUC,{tag="lib.agpsZkw.lua.fastFix",val=4})
|
||||
sys.timerStart(gps.setFastFix,2000,lng,lat,tm)
|
||||
getloc = 1
|
||||
end
|
||||
end
|
||||
|
||||
if result~=0 or gps.isFix() then
|
||||
if checkEph() then updateEph() end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
--是否获取到基站对应的经纬度
|
||||
function isgetloc()
|
||||
return getloc
|
||||
end
|
||||
|
||||
|
||||
local function ipReady()
|
||||
if gps.isFix() then
|
||||
runTimer()
|
||||
else
|
||||
|
||||
if not lbsLocRequesting then
|
||||
lbsLocRequesting = true
|
||||
lbsLoc.request(getLocCb,nil,30000,"0","bs.openluat.com","12411",true)
|
||||
end
|
||||
log.info("agpsZkw.ipready to updateEph")
|
||||
if checkEph() then
|
||||
updateEph()
|
||||
else
|
||||
sys.timerStart(upd_xingli,3000)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function gpsState(evt,para)
|
||||
log.info("agpsZkw.GPS_STATE",evt,para)
|
||||
if evt=="LOCATION_SUCCESS" or (evt=="CLOSE" and para==true) then
|
||||
runTimer()
|
||||
elseif evt=="OPEN" then
|
||||
local lng,lat = gps.getLastLocation()
|
||||
if lng=="" or lat=="" then
|
||||
lng,lat = lastLbsLng,lastLbsLat
|
||||
end
|
||||
if lng~="" and lat~="" then
|
||||
gps.open(gps.TIMERORSUC,{tag="lib.agpsZkw.lua.fastFix",val=4})
|
||||
local tm = os.date("*t")
|
||||
sys.timerStart(gps.setFastFix,2000,lat,lng,common.timeZoneConvert(tm.year,tm.month,tm.day,tm.hour,tm.min,tm.sec,8,0))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function init()
|
||||
sys.subscribe("GPS_STATE",gpsState)
|
||||
sys.subscribe("IP_READY_IND",ipReady)
|
||||
log.info("agpsZkw.unInit")
|
||||
end
|
||||
|
||||
|
||||
function unInit()
|
||||
sys.unsubscribe("GPS_STATE",gpsState)
|
||||
sys.unsubscribe("IP_READY_IND",ipReady)
|
||||
log.info("agpsZkw.unInit")
|
||||
end
|
||||
|
||||
init()
|
||||
483
4G/源代码/lib/audio.lua
Normal file
483
4G/源代码/lib/audio.lua
Normal file
@@ -0,0 +1,483 @@
|
||||
--- 模块功能:音频播放.
|
||||
-- 支持MP3、amr文件播放;
|
||||
-- 支持本地TTS播放、通话中TTS播放到对端(需要使用支持TTS功能的core软件)
|
||||
-- @module audio
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2018.3.19
|
||||
|
||||
require "common"
|
||||
require "misc"
|
||||
require "utils"
|
||||
module(..., package.seeall)
|
||||
|
||||
local req = ril.request
|
||||
local stopCbFnc
|
||||
--tts速度,默认50
|
||||
local ttsSpeed = 50
|
||||
--喇叭音量和mic音量等级
|
||||
local sVolume,sMicVolume = 4,1
|
||||
local sCallVolume = 4
|
||||
|
||||
|
||||
|
||||
--音频播放的协程ID
|
||||
local taskID
|
||||
|
||||
|
||||
--播放和停止请求队列,用于存储通过调用audio.play和audio.stop接口允许播放和停止播放的请求项
|
||||
|
||||
--每个播放请求项为table类型,数据结构如下(参考本文件中的play接口注释)
|
||||
--priority:播放优先级
|
||||
--type:播放类型
|
||||
--path:播放音频内容
|
||||
--vol:播放音量
|
||||
--cbFnc:播放结束后的回调函数
|
||||
--dup:是否重复播放
|
||||
--dupInterval:重复播放的间隔,单位毫秒
|
||||
|
||||
--每个停止请求项为table类型,数据结构如下(参考本文件中的stop接口注释)
|
||||
--type:固定为"STOP"
|
||||
--cbFnc:停止播放后的回调函数
|
||||
local audioQueue = {}
|
||||
|
||||
--sStrategy:优先级相同时的播放策略,0(表示继续播放正在播放的音频,忽略请求播放的新音频),1(表示停止正在播放的音频,播放请求播放的新音频)
|
||||
local sStrategy
|
||||
|
||||
local function isTtsStopResultValid()
|
||||
local version = string.match(rtos.get_version(),"(%d+)_RDA")
|
||||
if version then
|
||||
return tonumber(version)>=8
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
local function handleCb(item,result)
|
||||
log.info("audio.handleCb",item.cbFnc,result)
|
||||
if item.cbFnc then item.cbFnc(result) end
|
||||
table.remove(audioQueue,1)
|
||||
end
|
||||
|
||||
local function handlePlayInd(item,key,value)
|
||||
log.info("audio.handlePlayInd",key,value)
|
||||
--播放结束
|
||||
if key=="RESULT" then
|
||||
--播放成功
|
||||
if value then
|
||||
if item.dup then
|
||||
if item.dupInterval>0 then
|
||||
log.info("audio.handlePlayInd",item.type,"dup wait LIB_AUDIO_PLAY_IND or timeout",item.dupInterval)
|
||||
local result,reason = sys.waitUntil("LIB_AUDIO_PLAY_IND",item.dupInterval)
|
||||
log.info("audio.handlePlayInd",item.type,"dup wait",reason or "timeout")
|
||||
if result then
|
||||
log.warn("audio.handlePlayInd",item.type,"dup wait error",reason)
|
||||
handleCb(item,reason=="NEW" and 4 or 5)
|
||||
end
|
||||
end
|
||||
else
|
||||
handleCb(item,0)
|
||||
end
|
||||
--播放失败
|
||||
else
|
||||
log.warn("audio.handlePlayInd",item.type,"play cnf error")
|
||||
handleCb(item,1)
|
||||
end
|
||||
--新的优先级更高的播放请求
|
||||
elseif key=="NEW" then
|
||||
log.warn("audio.handlePlayInd",item.type,"priority error")
|
||||
handleCb(item,4)
|
||||
--主动调用audio.stop
|
||||
elseif key=="STOP" then
|
||||
log.warn("audio.handlePlayInd",item.type,"stop error",result)
|
||||
handleCb(item,5)
|
||||
end
|
||||
end
|
||||
|
||||
local ttsEngineInited
|
||||
local audioTaskWaitPlayEntry
|
||||
|
||||
local function audioTask()
|
||||
while true do
|
||||
if #audioQueue==0 then
|
||||
log.info("audioTask","wait LIB_AUDIO_PLAY_ENTRY")
|
||||
audioTaskWaitPlayEntry = true
|
||||
sys.waitUntil("LIB_AUDIO_PLAY_ENTRY")
|
||||
audioTaskWaitPlayEntry = false
|
||||
end
|
||||
|
||||
local item = audioQueue[1]
|
||||
|
||||
log.info("audioTask",item.type,"#audioQueue",#audioQueue)
|
||||
if item.type=="FILE" then
|
||||
--队列中有优先级高的请求等待处理
|
||||
if #audioQueue>1 then
|
||||
log.warn("audioTask",item.type,"priority low")
|
||||
local behind = audioQueue[2]
|
||||
handleCb(item,behind.type=="STOP" and 5 or 4)
|
||||
else
|
||||
setVolume(item.vol)
|
||||
local result
|
||||
if type(item.path)=="table" then
|
||||
if (item.path[1]):match("%.amr$") or (item.path[1]):match("%.AMR$") then
|
||||
local dataBuf = {}
|
||||
for i=1,#item.path do
|
||||
table.insert(dataBuf,(io.readFile(item.path[i])):sub(i==1 and 1 or 7,-1))
|
||||
end
|
||||
result = audiocore.playdata(table.concat(dataBuf),audiocore.AMR)
|
||||
elseif (item.path[1]):match("%.pcm$") or (item.path[1]):match("%.PCM$") then
|
||||
local dataBuf = {}
|
||||
for i=1,#item.path do
|
||||
table.insert(dataBuf,io.readFile(item.path[i]))
|
||||
end
|
||||
result = audiocore.playdata(table.concat(dataBuf),audiocore.PCM)
|
||||
elseif (item.path[1]):match("%.mp3$") or (item.path[1]):match("%.MP3$") then
|
||||
local dataBuf = {}
|
||||
for i=1,#item.path do
|
||||
table.insert(dataBuf,io.readFile(item.path[i]))
|
||||
end
|
||||
result = audiocore.playdata(table.concat(dataBuf),audiocore.MP3)
|
||||
elseif (item.path[1]):match("%.wav$") or (item.path[1]):match("%.WAV$") then
|
||||
local dataBuf = {}
|
||||
for i=1,#item.path do
|
||||
table.insert(dataBuf,io.readFile(item.path[i]))
|
||||
end
|
||||
result = audiocore.playdata(table.concat(dataBuf),audiocore.WAV)
|
||||
else
|
||||
result = false
|
||||
end
|
||||
--result = audiocore.play(unpack(item.path))
|
||||
else
|
||||
if item.path:match("%.wav$") or item.path:match("%.WAV$") then
|
||||
result = audiocore.playdata(io.readFile(item.path),audiocore.WAV)
|
||||
else
|
||||
result =audiocore.play(item.path)
|
||||
end
|
||||
end
|
||||
if result then
|
||||
--等待三种消息(播放结束、主动调用audio.stop、新的优先级更高的播放请求)
|
||||
log.info("audioTask",item.type,"wait LIB_AUDIO_PLAY_IND")
|
||||
local _,key,value = sys.waitUntil("LIB_AUDIO_PLAY_IND")
|
||||
log.info("audioTask",item.type,"recv LIB_AUDIO_PLAY_IND",key,value)
|
||||
|
||||
audiocore.stop()
|
||||
handlePlayInd(item,key,value)
|
||||
else
|
||||
log.warn("audioTask",item.type,"audiocore.play error")
|
||||
audiocore.stop()
|
||||
handleCb(item,1)
|
||||
end
|
||||
end
|
||||
elseif item.type=="TTS" or item.type=="TTSCC" then
|
||||
--队列中有优先级高的请求等待处理
|
||||
if #audioQueue>1 then
|
||||
log.warn("audioTask",item.type,"priority low")
|
||||
local behind = audioQueue[2]
|
||||
handleCb(item,behind.type=="STOP" and 5 or 4)
|
||||
else
|
||||
setVolume(item.vol)
|
||||
if item.type=="TTS" then
|
||||
if not ttsEngineInited then
|
||||
ttsply.initEngine()
|
||||
ttsEngineInited = true
|
||||
end
|
||||
ttsply.setParm(0,ttsSpeed)
|
||||
--队列中有优先级高的请求等待处理
|
||||
if #audioQueue>1 then
|
||||
log.warn("audioTask",item.type,"priority low1")
|
||||
if isTtsStopResultValid() then
|
||||
if ttsply.stop() then
|
||||
sys.waitUntil("LIB_AUDIO_PLAY_IND",2000)
|
||||
end
|
||||
else
|
||||
ttsply.stop()
|
||||
sys.waitUntil("LIB_AUDIO_PLAY_IND",500)
|
||||
end
|
||||
local behind = audioQueue[2]
|
||||
handleCb(item,behind.type=="STOP" and 5 or 4)
|
||||
else
|
||||
ttsply.play(common.utf8ToGb2312(item.path))
|
||||
|
||||
--等待三种消息(播放结束、主动调用audio.stop、新的优先级更高的播放请求)
|
||||
log.info("audioTask",item.type,"wait LIB_AUDIO_PLAY_IND")
|
||||
local _,key,value = sys.waitUntil("LIB_AUDIO_PLAY_IND")
|
||||
log.info("audioTask",item.type,"recv LIB_AUDIO_PLAY_IND",key,value)
|
||||
|
||||
if item.type=="TTS" then
|
||||
if isTtsStopResultValid() then
|
||||
--log.info("tts 1")
|
||||
if ttsply.stop() then
|
||||
--log.info("tts 2")
|
||||
sys.waitUntil("LIB_AUDIO_PLAY_IND",2000)
|
||||
end
|
||||
--log.info("tts 3")
|
||||
else
|
||||
ttsply.stop()
|
||||
sys.waitUntil("LIB_AUDIO_PLAY_IND",500)
|
||||
end
|
||||
else
|
||||
|
||||
end
|
||||
|
||||
handlePlayInd(item,key,value)
|
||||
end
|
||||
else
|
||||
|
||||
end
|
||||
end
|
||||
elseif item.type=="RECORD" then
|
||||
--队列中有优先级高的请求等待处理
|
||||
if #audioQueue>1 then
|
||||
log.warn("audioTask",item.type,"priority low")
|
||||
local behind = audioQueue[2]
|
||||
handleCb(item,behind.type=="STOP" and 5 or 4)
|
||||
else
|
||||
setVolume(item.vol)
|
||||
f,d=record.getSize()
|
||||
req("AT+AUDREC=1,0,2,"..item.path..","..d*1000)
|
||||
|
||||
--等待三种消息(播放结束、主动调用audio.stop、新的优先级更高的播放请求)
|
||||
log.info("audioTask",item.type,"wait LIB_AUDIO_PLAY_IND")
|
||||
local _,key,value = sys.waitUntil("LIB_AUDIO_PLAY_IND")
|
||||
log.info("audioTask",item.type,"recv LIB_AUDIO_PLAY_IND",key,value)
|
||||
|
||||
req("AT+AUDREC=1,0,3,"..item.path..","..d*1000)
|
||||
sys.waitUntil("LIB_AUDIO_RECORD_STOP_RESULT")
|
||||
|
||||
handlePlayInd(item,key,value)
|
||||
end
|
||||
elseif item.type=="STOP" then
|
||||
if item.cbFnc then item.cbFnc(0) end
|
||||
table.remove(audioQueue,1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- 播放音频
|
||||
-- @number priority 音频优先级,数值越大,优先级越高
|
||||
-- 优先级高的播放请求会终止优先级低的播放
|
||||
-- 相同优先级的播放请求,播放策略参考:audio.setStrategy接口
|
||||
-- @string type 音频类型,目前仅支持"FILE"、"TTS"
|
||||
-- @string path 音频文件路径,跟typ有关
|
||||
-- typ为"FILE"时:表示音频文件路径
|
||||
-- typ为"TTS"时:表示要播放的UTF8编码格式的数据
|
||||
-- @number[opt=4] vol 播放音量,取值范围0到7,0为静音
|
||||
-- @function[opt=nil] cbFnc 音频播放结束时的回调函数,回调函数的调用形式如下:
|
||||
-- cbFnc(result)
|
||||
-- result表示播放结果:
|
||||
-- 0-播放成功结束;
|
||||
-- 1-播放出错
|
||||
-- 2-播放优先级不够,没有播放
|
||||
-- 3-传入的参数出错,没有播放
|
||||
-- 4-被新的播放请求中止
|
||||
-- 5-调用audio.stop接口主动停止
|
||||
-- @bool[opt=nil] dup 是否循环播放,true循环,false或者nil不循环
|
||||
-- @number[opt=0] dupInterval 循环播放间隔(单位毫秒),dup为true时,此值才有意义
|
||||
-- @return result,bool或者nil类型,同步调用成功返回true,否则返回false
|
||||
-- @usage audio.play(0,"FILE","/lua/call.mp3")
|
||||
-- @usage audio.play(0,"FILE","/lua/call.mp3",7)
|
||||
-- @usage audio.play(0,"FILE","/lua/call.mp3",7,cbFnc)
|
||||
-- @usage 更多用法参考demo/audio/testAudio.lua
|
||||
function play(priority,type,path,vol,cbFnc,dup,dupInterval)
|
||||
log.info("audio.play",priority,type,path,vol,cbFnc,dup,dupInterval)
|
||||
if not taskID then
|
||||
taskID = sys.taskInit(audioTask)
|
||||
end
|
||||
|
||||
local item = {priority=priority,type=type,path=path,vol=vol or 4,cbFnc=cbFnc,dup=dup,dupInterval=dupInterval or 0}
|
||||
|
||||
if #audioQueue==0 then
|
||||
table.insert(audioQueue,item)
|
||||
sys.publish("LIB_AUDIO_PLAY_ENTRY")
|
||||
else
|
||||
local front = audioQueue[#audioQueue]
|
||||
if front.type=="STOP" then
|
||||
table.insert(audioQueue,item)
|
||||
else
|
||||
if priority>front.priority or (priority==front.priority and sStrategy==1) then
|
||||
table.insert(audioQueue,item)
|
||||
if not audioTaskWaitPlayEntry then
|
||||
sys.publish("LIB_AUDIO_PLAY_IND","NEW")
|
||||
end
|
||||
else
|
||||
log.warn("audio.play","priority error")
|
||||
if cbFnc then cbFnc(2) end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
--- 停止音频播放
|
||||
-- @function[opt=nil] cbFnc 停止音频播放的回调函数(停止结果通过此函数通知用户),回调函数的调用形式为:
|
||||
-- cbFnc(result)
|
||||
-- result:number类型
|
||||
-- 0表示停止成功
|
||||
-- @return nil
|
||||
-- @usage audio.stop()
|
||||
function stop(cbFnc)
|
||||
log.info("audio.stop",cbFnc)
|
||||
if #audioQueue==0 then
|
||||
if cbFnc then cbFnc(0) end
|
||||
else
|
||||
table.insert(audioQueue,{type="STOP",cbFnc=cbFnc})
|
||||
sys.publish("LIB_AUDIO_PLAY_IND","STOP")
|
||||
end
|
||||
end
|
||||
|
||||
local function audioMsg(msg)
|
||||
--log.info("audio.MSG_AUDIO",msg.play_end_ind,msg.play_error_ind)
|
||||
sys.publish("LIB_AUDIO_PLAY_IND","RESULT",msg.play_end_ind)
|
||||
end
|
||||
|
||||
--注册core上报的rtos.MSG_AUDIO消息的处理函数
|
||||
rtos.on(rtos.MSG_AUDIO,audioMsg)
|
||||
rtos.on(rtos.MSG_TTSPLY_STATUS, function() log.info("rtos.MSG_TTSPLY_STATUS") sys.publish("LIB_AUDIO_PLAY_IND","RESULT",true) end)
|
||||
rtos.on(rtos.MSG_TTSPLY_ERROR, function() log.info("rtos.MSG_TTSPLY_ERROR") sys.publish("LIB_AUDIO_PLAY_IND","RESULT",false) end)
|
||||
|
||||
--- 设置喇叭音量等级
|
||||
-- @number vol 音量值为0-7,0为静音
|
||||
-- @return bool result,设置成功返回true,失败返回false
|
||||
-- @usage audio.setVolume(7)
|
||||
function setVolume(vol)
|
||||
local result = audiocore.setvol(vol)
|
||||
if result == 1 then
|
||||
result = true
|
||||
elseif result == 0 then
|
||||
result = false
|
||||
end
|
||||
if result then sVolume = vol end
|
||||
return result
|
||||
end
|
||||
|
||||
--- 设置通话音量等级
|
||||
-- @number vol 音量值为0-7,0为静音
|
||||
-- @return bool result,设置成功返回true,失败返回false
|
||||
-- @usage audio.setCallVolume(7)
|
||||
function setCallVolume(vol)
|
||||
--local result = audiocore.setsphvol(vol)
|
||||
--if result then sCallVolume = vol end
|
||||
--return result
|
||||
audiocore.setsphvol(vol)
|
||||
sCallVolume = vol
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
-- 设置麦克音量等级
|
||||
-- @number vol,音量值为0-15,0为静音
|
||||
-- @return bool result,设置成功返回true,失败返回false
|
||||
-- @usage audio.setMicVolume(14)
|
||||
function setMicVolume(vol)
|
||||
ril.request("AT+CMIC="..audiocore.LOUDSPEAKER..","..vol)
|
||||
return true
|
||||
end
|
||||
|
||||
ril.regRsp("+CMIC",function(cmd,success)
|
||||
if success then
|
||||
sMicVolume = tonumber(cmd:match("CMIC=%d+,(%d+)"))
|
||||
end
|
||||
end)
|
||||
|
||||
--- 设置mic增益等级
|
||||
-- 通话时mic增益在通话建立成功之后设置才有效
|
||||
-- 录音mic增益设置后实时生效
|
||||
-- @string mode 增益类型
|
||||
-- "call"表示通话中mic增益
|
||||
-- "record"表示录音mic增益
|
||||
-- @number level 增益等级,取值为0-7
|
||||
-- @return bool result,设置成功返回true,失败返回false
|
||||
-- @usage audio.setMicGain("record",7),设置录音时mic增益为7级
|
||||
function setMicGain(mode, level)
|
||||
if (mode ~= "call" and mode ~= "record") or (level > 7 and level < 0) then
|
||||
return false
|
||||
else
|
||||
local gainHex
|
||||
if level == 7 then
|
||||
gainHex = string.format("%02X%02X%02X%02X", 7, 0, 15, 0)
|
||||
else
|
||||
gainHex = string.format("%02X%02X%02X%02X", level, 0, level * 2, 0)
|
||||
end
|
||||
|
||||
if mode == "call" then
|
||||
ril.request("AT+CACCP=5,1,0," .. gainHex)
|
||||
ril.request("AT+CACCP=0,1,0," .. gainHex)
|
||||
elseif mode == "record" then
|
||||
ril.request("AT+CACCP=2,1,6," .. gainHex)
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
--- 获取喇叭音量等级
|
||||
-- @return number vol,喇叭音量等级
|
||||
-- @usage audio.getVolume()
|
||||
function getVolume()
|
||||
return sVolume
|
||||
end
|
||||
|
||||
--- 获取通话音量等级
|
||||
-- @return number vol,通话音量等级
|
||||
-- @usage audio.getCallVolume()
|
||||
function getCallVolume()
|
||||
return sCallVolume
|
||||
end
|
||||
|
||||
-- 获取麦克音量等级
|
||||
-- @return number vol,麦克音量等级
|
||||
-- @usage audio.getMicVolume()
|
||||
function getMicVolume()
|
||||
return sMicVolume
|
||||
end
|
||||
|
||||
--- 设置优先级相同时的播放策略
|
||||
-- @number strategy 优先级相同时的播放策略;
|
||||
-- 0:表示继续播放正在播放的音频,忽略请求播放的新音频
|
||||
-- 1:表示停止正在播放的音频,播放请求播放的新音频
|
||||
-- @return nil
|
||||
-- @usage audio.setStrategy(0)
|
||||
-- @usage audio.setStrategy(1)
|
||||
function setStrategy(strategy)
|
||||
sStrategy=strategy
|
||||
end
|
||||
|
||||
--- 设置TTS朗读速度
|
||||
-- @number speed 速度范围为0-100,默认50
|
||||
-- @return bool result,设置成功返回true,失败返回false
|
||||
-- @usage audio.setTTSSpeed(70)
|
||||
function setTTSSpeed(speed)
|
||||
if type(speed) == "number" and speed >= 0 and speed <= 100 then
|
||||
ttsSpeed = speed
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
--- 设置音频输入、输出通道
|
||||
-- 设置后实时生效
|
||||
-- @number[opt=2] output 0:earphone听筒,1:headphone耳机,2:speaker喇叭
|
||||
-- @number[opt=0] input 0:主mic,3:耳机mic
|
||||
-- @return nil
|
||||
-- @usage
|
||||
-- 设置为听筒输出:audio.setChannel(0)
|
||||
-- 设置为耳机输出:audio.setChannel(1)
|
||||
-- 设置为喇叭输出:audio.setChannel(2)
|
||||
-- 设置为喇叭输出、耳机mic输入:audio.setChannel(2,3)
|
||||
function setChannel(output, input)
|
||||
local version = string.match(rtos.get_version(), "(%d+)_RDA")
|
||||
if not version or tonumber(version) >= 9 then --匹配不到,兼容其它版本 或者大于版本9
|
||||
audiocore.setchannel(output or 2, input or 0)
|
||||
else
|
||||
ril.request("AT+AUDCH="..(output==1 and 1 or 2))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--默认音频通道设置为LOUDSPEAKER,因为目前的模块只支持LOUDSPEAKER通道
|
||||
audiocore.setchannel(audiocore.LOUDSPEAKER)
|
||||
--默认音量等级设置为4级,4级是中间等级,最低为0级,最高为7级
|
||||
setVolume(sVolume)
|
||||
setCallVolume(sCallVolume)
|
||||
--默认MIC音量等级设置为1级,最低为0级,最高为15级
|
||||
setMicVolume(sMicVolume)
|
||||
247
4G/源代码/lib/cc.lua
Normal file
247
4G/源代码/lib/cc.lua
Normal file
@@ -0,0 +1,247 @@
|
||||
--- 模块功能:通话管理
|
||||
-- @module cc
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2017.11.2
|
||||
|
||||
module(..., package.seeall)
|
||||
require"ril"
|
||||
require"pm"
|
||||
|
||||
--- 通话中
|
||||
CONNECTED = 0
|
||||
--- 通话保持中
|
||||
HOLD = 1
|
||||
--- 正在呼出
|
||||
DIALING = 2
|
||||
ALERTING = 3
|
||||
--- 正在呼入
|
||||
INCOMING = 4
|
||||
WAITING = 5
|
||||
--- 正在挂断通话
|
||||
DISCONNECTING = 98
|
||||
--- 通话已挂断
|
||||
DISCONNECTED = 99
|
||||
|
||||
local req = ril.request
|
||||
local publish = sys.publish
|
||||
|
||||
--底层通话模块是否准备就绪,true就绪,false或者nil未就绪
|
||||
local ccready = false
|
||||
--通话列表
|
||||
local call_list = {n= 0}
|
||||
--通话断开原因
|
||||
local discReason
|
||||
|
||||
--- 是否存在通话
|
||||
-- @return bool result 存在通话返回true,否则返回false
|
||||
-- @usage result = cc.anyCallExist()
|
||||
function anyCallExist()
|
||||
return call_list.n ~= 0
|
||||
end
|
||||
|
||||
--- 查询某个号码的通话状态
|
||||
-- @string num 查询号码
|
||||
-- @return number state 通话状态,状态值参考本模块Fields定义
|
||||
-- @usage state = cc.getState('10086')
|
||||
function getState(num)
|
||||
return call_list[num] or DISCONNECTED
|
||||
end
|
||||
|
||||
--- 呼出电话
|
||||
-- @string num 呼出号码
|
||||
-- @number[opt=0] delay 延时delay毫秒后,才发起呼叫
|
||||
-- @return bool result,true表示允许发送at命令拨号并且发送at,false表示不允许at命令拨号
|
||||
-- @usage cc.dial('10086')
|
||||
function dial(num, delay)
|
||||
if num == "" or num == nil then return false end
|
||||
pm.wake("cc")
|
||||
req(string.format("%s%s;", "ATD", num), nil, nil, delay)
|
||||
call_list[num] = DIALING
|
||||
return true
|
||||
end
|
||||
|
||||
--- 挂断通话
|
||||
-- @string num 号码,若指定号码通话状态不对,则直接退出,不会执行挂断,若挂断时会挂断所有电话
|
||||
-- @return nil
|
||||
-- @usage cc.hangUp('10086')
|
||||
function hangUp(num)
|
||||
if call_list[num] == DISCONNECTING or call_list[num] == DISCONNECTED then return end
|
||||
if audio and type(audio.stop)=="function" then audio.stop() end
|
||||
req("AT+CHUP")
|
||||
call_list[num] = DISCONNECTING
|
||||
end
|
||||
|
||||
--- 接听电话
|
||||
-- @string num 号码,若指定号码通话状态不对,则直接退出,不会接通
|
||||
-- @return nil
|
||||
-- @usage cc.accept('10086')
|
||||
function accept(num)
|
||||
if call_list[num] ~= INCOMING then return end
|
||||
if audio and type(audio.stop)=="function" then audio.stop() end
|
||||
req("ATA")
|
||||
call_list[num] = CONNECTING
|
||||
end
|
||||
|
||||
--- 通话中发送声音到对端,必须是12.2K AMR格式
|
||||
-- @string data 12.2K,AMR格式的数据
|
||||
-- @bool[opt=nil] loop 是否循环发送,true为循环,其余为不循环
|
||||
-- @bool[opt=nil] downLinkPlay 声音是否在本端播放,true为播放,其余为不播放
|
||||
-- @return bool result true为成功,false为失败
|
||||
-- @usage
|
||||
-- cc.transVoice("#!AMR\010\060*********")
|
||||
-- cc.transVoice("#!AMR\010\060*********",true)
|
||||
-- cc.transVoice("#!AMR\010\060*********",true,true)
|
||||
function transVoice(data, loop, downLinkPlay)
|
||||
local f = io.open("/RecDir/rec000", "wb")
|
||||
|
||||
if f == nil then
|
||||
log.error("transVoice:open file error")
|
||||
return false
|
||||
end
|
||||
|
||||
-- 有文件头并且是12.2K帧
|
||||
if string.sub(data, 1, 7) == "#!AMR\010\060" then
|
||||
-- 无文件头且是12.2K帧
|
||||
elseif string.byte(data, 1) == 0x3C then
|
||||
f:write("#!AMR\010")
|
||||
else
|
||||
log.error('cc.transVoice', 'must be 12.2K AMR')
|
||||
return false
|
||||
end
|
||||
|
||||
f:write(data)
|
||||
f:close()
|
||||
|
||||
req(string.format("AT+AUDREC=%d,%d,2,0,50000", downLinkPlay == true and 1 or 0, loop == true and 1 or 0))
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
--- 设置dtmf检测是否使能以及灵敏度
|
||||
-- @bool[opt=nil] enable true使能,false或者nil为不使能
|
||||
-- @number[opt=3] sens 灵敏度,最灵敏为1
|
||||
-- @return nil
|
||||
-- @usage cc.dtmfDetect(true)
|
||||
function dtmfDetect(enable, sens)
|
||||
if enable == true then
|
||||
if sens then
|
||||
req("AT+DTMFDET=2,1," .. sens)
|
||||
else
|
||||
req("AT+DTMFDET=2,1,3")
|
||||
end
|
||||
end
|
||||
|
||||
req("AT+DTMFDET=" .. (enable and 1 or 0))
|
||||
end
|
||||
|
||||
--- 发送dtmf到对端
|
||||
-- @string str dtmf字符串,仅支持数字、ABCD*#
|
||||
-- @number[opt=100] playtime 每个dtmf播放时间,单位毫秒
|
||||
-- @number[opt=100] intvl 两个dtmf间隔,单位毫秒
|
||||
-- @return nil
|
||||
-- @usage cc.sendDtmf("123")
|
||||
function sendDtmf(str, playtime, intvl)
|
||||
if string.match(str, "([%dABCD%*#]+)") ~= str then
|
||||
log.error("sendDtmf: illegal string " .. str)
|
||||
return false
|
||||
end
|
||||
|
||||
playtime = playtime and playtime or 100
|
||||
intvl = intvl and intvl or 100
|
||||
|
||||
--req("AT+SENDSOUND=" .. string.format("\"%s\",%d,%d", str, playtime, intvl))
|
||||
req("AT+VTS=".. str)
|
||||
end
|
||||
|
||||
local dtmfnum = { [71] = "Hz1000", [69] = "Hz1400", [70] = "Hz2300" }
|
||||
local function parsedtmfnum(data)
|
||||
local n = tonumber(string.match(data, "(%d+)"))
|
||||
local dtmf
|
||||
|
||||
if (n >= 48 and n <= 57) or (n >= 65 and n <= 68) or n == 42 or n == 35 then
|
||||
dtmf = string.char(n)
|
||||
else
|
||||
dtmf = dtmfnum[n]
|
||||
end
|
||||
|
||||
if dtmf then
|
||||
publish("CALL_DTMF_DETECT", dtmf) -- 通话中dtmf解码会产生消息AUDIO_DTMF_DETECT,消息数据为DTMF字符
|
||||
end
|
||||
end
|
||||
|
||||
local function ccurc(data, prefix)
|
||||
if data == "CALL READY" then --底层通话模块准备就绪
|
||||
ccready = true
|
||||
publish("CALL_READY")
|
||||
req("AT+CCWA=1")
|
||||
elseif prefix == "+DTMFDET" then
|
||||
parsedtmfnum(data)
|
||||
else
|
||||
if data=="NO CARRIER" or data=="NO ANSWER" or data=="BUSY" then
|
||||
discReason = data
|
||||
end
|
||||
|
||||
req('AT+CLCC')
|
||||
if data == "CONNECT" and audio and type(audio.stop)=="function" then audio.stop() end --先停止音频播放
|
||||
end
|
||||
end
|
||||
|
||||
local function ccrsp(cmd, success, response, intermediate)
|
||||
if cmd=="AT+CHUP" then
|
||||
discReason = "CHUP"
|
||||
end
|
||||
|
||||
req('AT+CLCC')
|
||||
end
|
||||
|
||||
--注册以下通知的处理函数
|
||||
ril.regUrc("CALL READY", ccurc)
|
||||
ril.regUrc("CONNECT", ccurc)
|
||||
ril.regUrc("NO CARRIER", ccurc)
|
||||
ril.regUrc("NO ANSWER", ccurc)
|
||||
ril.regUrc("BUSY", ccurc)
|
||||
ril.regUrc("+CLIP", ccurc)
|
||||
ril.regUrc("+CCWA", ccurc)
|
||||
ril.regUrc("+DTMFDET", ccurc)
|
||||
--注册以下AT命令的应答处理函数
|
||||
ril.regRsp("D", ccrsp)
|
||||
ril.regRsp("A", ccrsp)
|
||||
ril.regRsp("+CHUP", ccrsp)
|
||||
ril.regRsp("+CHLD", ccrsp)
|
||||
ril.regRsp("+CLCC", function(cmd, success, response, intermediate)
|
||||
if success then
|
||||
local new = {n = 0 }
|
||||
if intermediate and intermediate:len() > 0 then
|
||||
for id, dir, stat, num in intermediate:gmatch('%+CLCC:%s*(%d+),(%d),(%d),%d,%d,"([^"]*)".-\r\n') do
|
||||
stat = tonumber(stat)
|
||||
if stat == WAITING then
|
||||
req('AT+CHLD=1' .. id)
|
||||
return
|
||||
end
|
||||
if call_list[num] ~= stat then
|
||||
if stat == INCOMING or stat == CONNECTED then
|
||||
pm.wake('cc')
|
||||
publish(stat == INCOMING and 'CALL_INCOMING' or 'CALL_CONNECTED', num)
|
||||
end
|
||||
end
|
||||
new[num] = stat
|
||||
new.n = new.n + 1
|
||||
end
|
||||
end
|
||||
call_list = new
|
||||
if new.n == 0 then
|
||||
publish('CALL_DISCONNECTED',discReason)
|
||||
discReason = nil
|
||||
pm.sleep('cc')
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
--开启拨号音,忙音检测
|
||||
req("ATX4")
|
||||
--开启来电urc上报
|
||||
req("AT+CLIP=1")
|
||||
req("ATS7=60")
|
||||
req("AT+SETVOLTE=1")
|
||||
38
4G/源代码/lib/clib.lua
Normal file
38
4G/源代码/lib/clib.lua
Normal file
@@ -0,0 +1,38 @@
|
||||
--- 模块功能:完善luat的c库接口
|
||||
-- @module clib
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2017.9.20
|
||||
local uartReceiveCallbacks = {}
|
||||
local uartSentCallbacks = {}
|
||||
|
||||
--- 注册串口事件的处理函数
|
||||
-- @number id 串口ID,1表示串口1,2表示串口2,uart.ATC表示虚拟AT口
|
||||
-- @string event 串口事件:
|
||||
-- "recieve"表示串口收到数据,注意:使用uart.setup配置串口时,第6个参数设置为nil或者0,收到数据时,才会产生"receive"事件
|
||||
-- "sent"表示串口数据发送完成,注意:使用uart.setup配置串口时,第7个参数设置为1,调用uart.write接口发送数据之后,才会产生"sent"事件
|
||||
-- @function[opt=nil] callback 串口事件的处理函数
|
||||
-- @return nil
|
||||
-- @usage
|
||||
-- uart.on(1,"receive",rcvFnc)
|
||||
-- uart.on(1,"sent",sentFnc)
|
||||
uart.on = function(id, event, callback)
|
||||
if event == "receive" then
|
||||
uartReceiveCallbacks[id] = callback
|
||||
elseif event == "sent" then
|
||||
uartSentCallbacks[id] = callback
|
||||
end
|
||||
end
|
||||
|
||||
rtos.on(rtos.MSG_UART_RXDATA, function(id, length)
|
||||
if uartReceiveCallbacks[id] then
|
||||
uartReceiveCallbacks[id](id, length)
|
||||
end
|
||||
end)
|
||||
|
||||
rtos.on(rtos.MSG_UART_TX_DONE, function(id)
|
||||
if uartSentCallbacks[id] then
|
||||
uartSentCallbacks[id](id)
|
||||
end
|
||||
end)
|
||||
308
4G/源代码/lib/common.lua
Normal file
308
4G/源代码/lib/common.lua
Normal file
@@ -0,0 +1,308 @@
|
||||
---模块功能:通用库函数、编码格式转换、时区时间转换
|
||||
-- @module common
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2017.02.20
|
||||
--定义模块,导入依赖库
|
||||
module(..., package.seeall)
|
||||
|
||||
--加载常用的全局函数至本地
|
||||
local tinsert, ssub, sbyte, schar, sformat, slen = table.insert, string.sub, string.byte, string.char, string.format, string.len
|
||||
|
||||
--- ascii字符串的unicode编码的16进制字符串 转化为 ascii字符串
|
||||
-- @string inNum 待转换字符串
|
||||
-- @return string data,转换后的字符串
|
||||
-- @usage
|
||||
-- local data = common.ucs2ToAscii("0031003200330034")
|
||||
-- data is "1234"
|
||||
function ucs2ToAscii(inNum)
|
||||
local tonum = {}
|
||||
for i = 1, slen(inNum), 4 do
|
||||
tinsert(tonum, tonumber(ssub(inNum, i, i + 3), 16) % 256)
|
||||
end
|
||||
return schar(unpack(tonum))
|
||||
end
|
||||
--- ascii字符串 转化为 ascii字符串的unicode编码的16进制字符串(仅支持数字和+)
|
||||
-- @string inNum 待转换字符串
|
||||
-- @return string data,转换后的字符串
|
||||
-- @usage
|
||||
-- local data = common.nstrToUcs2Hex("+1234")
|
||||
-- data is "002B0031003200330034"
|
||||
function nstrToUcs2Hex(inNum)
|
||||
local hexs = ""
|
||||
local elem = ""
|
||||
for i = 1, slen(inNum) do
|
||||
elem = ssub(inNum, i, i)
|
||||
if elem == "+" then
|
||||
hexs = hexs .. "002B"
|
||||
else
|
||||
hexs = hexs .. "003" .. elem
|
||||
end
|
||||
end
|
||||
return hexs
|
||||
end
|
||||
|
||||
--- ASCII字符串 转化为 BCD编码格式字符串(仅支持数字)
|
||||
-- @string inStr 待转换字符串
|
||||
-- @number destLen 转换后的字符串期望长度,如果实际不足,则填充F
|
||||
-- @return string data,转换后的字符串
|
||||
-- @usage
|
||||
-- local data = common.numToBcdNum("8618126324567")
|
||||
-- data is "688121364265f7" (表示第1个字节是0x68,第2个字节为0x81,......)
|
||||
function numToBcdNum(inStr,destLen)
|
||||
local l,t,num = string.len(inStr or ""),{}
|
||||
|
||||
destLen = destLen or (inStr:len()+1)/2
|
||||
|
||||
for i=1,l,2 do
|
||||
num = tonumber(inStr:sub(i,i+1),16)
|
||||
|
||||
if i==l then
|
||||
num = 0xf0+num
|
||||
else
|
||||
num = (num%0x10)*0x10 + (num-(num%0x10))/0x10
|
||||
end
|
||||
|
||||
table.insert(t,num)
|
||||
end
|
||||
|
||||
local s = string.char(unpack(t))
|
||||
|
||||
l = slen(s)
|
||||
if l < destLen then
|
||||
s = s .. string.rep("\255",destLen-l)
|
||||
elseif l > destLen then
|
||||
s = ssub(s,1,destLen)
|
||||
end
|
||||
|
||||
return s
|
||||
end
|
||||
|
||||
--- BCD编码格式字符串 转化为 号码ASCII字符串(仅支持数字)
|
||||
-- @string num 待转换字符串
|
||||
-- @return string data,转换后的字符串
|
||||
-- @usage
|
||||
-- local data = common.bcdNumToNum(common.fromHex("688121364265f7")) --表示第1个字节是0x68,第2个字节为0x81,......
|
||||
-- data is "8618126324567"
|
||||
function bcdNumToNum(num)
|
||||
local byte,v1,v2
|
||||
local t = {}
|
||||
|
||||
for i=1,num:len() do
|
||||
byte = num:byte(i)
|
||||
v1,v2 = bit.band(byte,0x0f),bit.band(bit.rshift(byte,4),0x0f)
|
||||
|
||||
if v1 == 0x0f then break end
|
||||
table.insert(t,v1)
|
||||
|
||||
if v2 == 0x0f then break end
|
||||
table.insert(t,v2)
|
||||
end
|
||||
|
||||
return table.concat(t)
|
||||
end
|
||||
|
||||
--- unicode小端编码 转化为 gb2312编码
|
||||
-- @string ucs2s unicode小端编码数据
|
||||
-- @return string data,gb2312编码数据
|
||||
-- @usage local data = common.ucs2ToGb2312(ucs2s)
|
||||
function ucs2ToGb2312(ucs2s)
|
||||
local cd = iconv.open("gb2312", "ucs2")
|
||||
return cd:iconv(ucs2s)
|
||||
end
|
||||
|
||||
--- gb2312编码 转化为 unicode小端编码
|
||||
-- @string gb2312s gb2312编码数据
|
||||
-- @return string data,unicode小端编码数据
|
||||
-- @usage local data = common.gb2312ToUcs2(gb2312s)
|
||||
function gb2312ToUcs2(gb2312s)
|
||||
local cd = iconv.open("ucs2", "gb2312")
|
||||
return cd:iconv(gb2312s)
|
||||
end
|
||||
|
||||
--- unicode大端编码 转化为 gb2312编码
|
||||
-- @string ucs2s unicode大端编码数据
|
||||
-- @return string data,gb2312编码数据
|
||||
-- @usage data = common.ucs2beToGb2312(ucs2s)
|
||||
function ucs2beToGb2312(ucs2s)
|
||||
local cd = iconv.open("gb2312", "ucs2be")
|
||||
return cd:iconv(ucs2s)
|
||||
end
|
||||
|
||||
--- gb2312编码 转化为 unicode大端编码
|
||||
-- @string gb2312s gb2312编码数据
|
||||
-- @return string data,unicode大端编码数据
|
||||
-- @usage local data = common.gb2312ToUcs2be(gb2312s)
|
||||
function gb2312ToUcs2be(gb2312s)
|
||||
local cd = iconv.open("ucs2be", "gb2312")
|
||||
return cd:iconv(gb2312s)
|
||||
end
|
||||
|
||||
--- unicode小端编码 转化为 utf8编码
|
||||
-- @string ucs2s unicode小端编码数据
|
||||
-- @return string data,utf8编码数据
|
||||
-- @usage data = common.ucs2ToUtf8(ucs2s)
|
||||
function ucs2ToUtf8(ucs2s)
|
||||
local cd = iconv.open("utf8", "ucs2")
|
||||
return cd:iconv(ucs2s)
|
||||
end
|
||||
|
||||
--- utf8编码 转化为 unicode小端编码
|
||||
-- @string utf8s utf8编码数据
|
||||
-- @return string data,unicode小端编码数据
|
||||
-- @usage local data = common.utf8ToUcs2(utf8s)
|
||||
function utf8ToUcs2(utf8s)
|
||||
local cd = iconv.open("ucs2", "utf8")
|
||||
return cd:iconv(utf8s)
|
||||
end
|
||||
|
||||
--- unicode大端编码 转化为 utf8编码
|
||||
-- @string ucs2s unicode大端编码数据
|
||||
-- @return string data,utf8编码数据
|
||||
-- @usage data = common.ucs2beToUtf8(ucs2s)
|
||||
function ucs2beToUtf8(ucs2s)
|
||||
local cd = iconv.open("utf8", "ucs2be")
|
||||
return cd:iconv(ucs2s)
|
||||
end
|
||||
|
||||
--- utf8编码 转化为 unicode大端编码
|
||||
-- @string utf8s utf8编码数据
|
||||
-- @return string data,unicode大端编码数据
|
||||
-- @usage local data = common.utf8ToUcs2be(utf8s)
|
||||
function utf8ToUcs2be(utf8s)
|
||||
local cd = iconv.open("ucs2be", "utf8")
|
||||
return cd:iconv(utf8s)
|
||||
end
|
||||
|
||||
--- utf8编码 转化为 gb2312编码
|
||||
-- @string utf8s utf8编码数据
|
||||
-- @return string data,gb2312编码数据
|
||||
-- @usage local data = common.utf8ToGb2312(utf8s)
|
||||
function utf8ToGb2312(utf8s)
|
||||
local cd = iconv.open("ucs2", "utf8")
|
||||
local ucs2s = cd:iconv(utf8s)
|
||||
cd = iconv.open("gb2312", "ucs2")
|
||||
return cd:iconv(ucs2s)
|
||||
end
|
||||
|
||||
--- gb2312编码 转化为 utf8编码
|
||||
-- @string gb2312s gb2312编码数据
|
||||
-- @return string data,utf8编码数据
|
||||
-- @usage local data = common.gb2312ToUtf8(gb2312s)
|
||||
function gb2312ToUtf8(gb2312s)
|
||||
local cd = iconv.open("ucs2", "gb2312")
|
||||
local ucs2s = cd:iconv(gb2312s)
|
||||
cd = iconv.open("utf8", "ucs2")
|
||||
return cd:iconv(ucs2s)
|
||||
end
|
||||
|
||||
local function timeAddzone(y, m, d, hh, mm, ss, zone)
|
||||
if not y or not m or not d or not hh or not mm or not ss then
|
||||
return
|
||||
end
|
||||
hh = hh + zone
|
||||
if hh >= 24 then
|
||||
hh = hh - 24
|
||||
d = d + 1
|
||||
if m == 4 or m == 6 or m == 9 or m == 11 then
|
||||
if d > 30 then
|
||||
d = 1
|
||||
m = m + 1
|
||||
end
|
||||
elseif m == 1 or m == 3 or m == 5 or m == 7 or m == 8 or m == 10 then
|
||||
if d > 31 then
|
||||
d = 1
|
||||
m = m + 1
|
||||
end
|
||||
elseif m == 12 then
|
||||
if d > 31 then
|
||||
d = 1
|
||||
m = 1
|
||||
y = y + 1
|
||||
end
|
||||
elseif m == 2 then
|
||||
if (((y + 2000) % 400) == 0) or (((y + 2000) % 4 == 0) and ((y + 2000) % 100 ~= 0)) then
|
||||
if d > 29 then
|
||||
d = 1
|
||||
m = 3
|
||||
end
|
||||
else
|
||||
if d > 28 then
|
||||
d = 1
|
||||
m = 3
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
local t = {}
|
||||
t.year, t.month, t.day, t.hour, t.min, t.sec = y, m, d, hh, mm, ss
|
||||
return t
|
||||
end
|
||||
local function timeSubZone(y, m, d, hh, mm, ss, zone)
|
||||
if not y or not m or not d or not hh or not mm or not ss then
|
||||
return
|
||||
end
|
||||
hh = hh + zone
|
||||
if hh < 0 then
|
||||
hh = hh + 24
|
||||
d = d - 1
|
||||
if m == 2 or m == 4 or m == 6 or m == 8 or m == 9 or m == 11 then
|
||||
if d < 1 then
|
||||
d = 31
|
||||
m = m - 1
|
||||
end
|
||||
elseif m == 5 or m == 7 or m == 10 or m == 12 then
|
||||
if d < 1 then
|
||||
d = 30
|
||||
m = m - 1
|
||||
end
|
||||
elseif m == 1 then
|
||||
if d < 1 then
|
||||
d = 31
|
||||
m = 12
|
||||
y = y - 1
|
||||
end
|
||||
elseif m == 3 then
|
||||
if (((y + 2000) % 400) == 0) or (((y + 2000) % 4 == 0) and ((y + 2000) % 100 ~= 0)) then
|
||||
if d < 1 then
|
||||
d = 29
|
||||
m = 2
|
||||
end
|
||||
else
|
||||
if d < 1 then
|
||||
d = 28
|
||||
m = 2
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
local t = {}
|
||||
t.year, t.month, t.day, t.hour, t.min, t.sec = y, m, d, hh, mm, ss
|
||||
return t
|
||||
end
|
||||
|
||||
--- 时区时间转换
|
||||
-- @number y 源时区年份
|
||||
-- @number m 源时区月份
|
||||
-- @number d 源时区天
|
||||
-- @number hh 源时区小时
|
||||
-- @number mm 源时区分
|
||||
-- @number ss 源时区秒
|
||||
-- @number srcTimeZone 源时区
|
||||
-- @number dstTimeZone 目的时区
|
||||
-- @return table dstZoneTime,返回目的时区对应的时间,{year,month,day,hour,min,sec}
|
||||
-- @usage
|
||||
-- local dstZoneTime = common.timeZoneConvert(2018,1,1,18,00,00,0,8)
|
||||
-- dstZoneTime为{year=2018,month=1,day=2,hour=2,min=0,sec=0}
|
||||
function timeZoneConvert(y, m, d, hh, mm, ss, srcTimeZone, dstTimeZone)
|
||||
local t = {}
|
||||
local zone = dstTimeZone-srcTimeZone
|
||||
|
||||
if zone >= 0 and zone < 23 then
|
||||
t = timeAddzone(y, m, d, hh, mm, ss, zone)
|
||||
elseif zone < 0 and zone >= -24 then
|
||||
t = timeSubZone(y, m, d, hh, mm, ss, zone)
|
||||
end
|
||||
return t
|
||||
end
|
||||
137
4G/源代码/lib/console.lua
Normal file
137
4G/源代码/lib/console.lua
Normal file
@@ -0,0 +1,137 @@
|
||||
--- 模块功能:Luat控制台.
|
||||
-- 使用说明参考demo/console下的《console功能使用说明.docx》
|
||||
-- @module console
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2017.9.15
|
||||
|
||||
require"ril"
|
||||
module(..., package.seeall)
|
||||
local uart_id
|
||||
local console_task
|
||||
|
||||
local function read_line()
|
||||
while true do
|
||||
local s = uart.read(uart_id, "*l")
|
||||
if s ~= "" then
|
||||
return s
|
||||
end
|
||||
coroutine.yield()
|
||||
end
|
||||
end
|
||||
|
||||
local function write(s)
|
||||
uart.write(uart_id, s)
|
||||
end
|
||||
|
||||
local function on_wait_event_timeout()
|
||||
coroutine.resume(console_task, "TIMEOUT")
|
||||
end
|
||||
|
||||
local function wait_event(event, timeout)
|
||||
if timeout then
|
||||
sys.timerStart(on_wait_event_timeout, timeout)
|
||||
end
|
||||
|
||||
while true do
|
||||
local receive_event = coroutine.yield()
|
||||
if receive_event == event then
|
||||
sys.timerStop(on_wait_event_timeout)
|
||||
return
|
||||
elseif receive_event == "TIMEOUT" then
|
||||
write("WAIT EVENT " .. event .. "TIMEOUT\r\n")
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function main_loop()
|
||||
local cache_data = ""
|
||||
local wait_event_flag
|
||||
|
||||
-- 定义执行环境,命令行下输入的脚本的print重写到命令行的write
|
||||
local execute_env = {
|
||||
print = function(...)
|
||||
local arg = { ... }
|
||||
for i, v in ipairs(arg) do
|
||||
arg[i] = type(v) == "nil" and "nil" or tostring(v)
|
||||
end
|
||||
write(table.concat(arg, "\t"))
|
||||
write("\r\n")
|
||||
end,
|
||||
sendat = function(cmd, data)
|
||||
ril.request(cmd, data, function(cmd, success, response, intermediate)
|
||||
if intermediate then
|
||||
write("\r\n" .. intermediate .. "\r\n")
|
||||
end
|
||||
if response then
|
||||
write("\r\n" .. response .. "\r\n")
|
||||
end
|
||||
coroutine.resume(console_task, "WAIT_AT_RESPONSE")
|
||||
end, nil)
|
||||
wait_event_flag = "WAIT_AT_RESPONSE"
|
||||
end,
|
||||
}
|
||||
setmetatable(execute_env, { __index = _G })
|
||||
|
||||
-- 输出提示语
|
||||
write("\r\nWelcome to Luat Console\r\n")
|
||||
write("\r\n> ")
|
||||
|
||||
while true do
|
||||
-- 读取输入
|
||||
local new_data = read_line("*l")
|
||||
-- 输出回显
|
||||
write(new_data)
|
||||
-- 拼接之前未成行的剩余数据
|
||||
cache_data = cache_data .. new_data
|
||||
-- 去掉回车换行
|
||||
local line = string.match(cache_data, "(.-)\r?\n")
|
||||
if line then
|
||||
-- 收到一整行的数据 清除缓冲数据
|
||||
cache_data = ""
|
||||
-- 输出新行
|
||||
write("\n")
|
||||
-- 用xpcall执行用户输入的脚本,可以捕捉脚本的错误
|
||||
xpcall(function()
|
||||
-- 执行用户输入的脚本
|
||||
local f = assert(loadstring(line.." "))
|
||||
setfenv(f, execute_env)
|
||||
f()
|
||||
end,
|
||||
function(err) -- 错误输出
|
||||
write(err .. '\r\n')
|
||||
write(debug.traceback())
|
||||
end)
|
||||
if wait_event_flag then
|
||||
wait_event(wait_event_flag, 3000)
|
||||
wait_event_flag = nil
|
||||
end
|
||||
-- 输出输入提示符
|
||||
write("\r\n> ")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- 配置控制台使用的串口参数,创建控制台协程
|
||||
-- @number id 控制台使用的串口ID:1表示串口1,2表示串口2
|
||||
-- @number[opt=115200] baudrate 控制台使用的串口波特率
|
||||
-- 支持1200,2400,4800,9600,10400,14400,19200,28800,38400,57600,76800,115200,230400,460800,576000,921600,1152000,4000000
|
||||
-- @return nil
|
||||
-- @usage console.setup(1, 115200)
|
||||
function setup(id, baudrate)
|
||||
-- 默认串口1
|
||||
uart_id = id or 1
|
||||
-- 默认波特率115200
|
||||
baudrate = baudrate or 115200
|
||||
-- 创建console处理的协程
|
||||
console_task = coroutine.create(main_loop)
|
||||
-- 初始化串口
|
||||
uart.setup(uart_id, baudrate, 8, uart.PAR_NONE, uart.STOP_1)
|
||||
-- 串口收到数据时唤醒console协程
|
||||
uart.on(uart_id, "receive", function()
|
||||
coroutine.resume(console_task)
|
||||
end)
|
||||
coroutine.resume(console_task)
|
||||
end
|
||||
347
4G/源代码/lib/errDump.lua
Normal file
347
4G/源代码/lib/errDump.lua
Normal file
@@ -0,0 +1,347 @@
|
||||
--- 模块功能:系统错误日志管理(强烈建议用户开启此模块的“错误日志上报调试服务器”功能).
|
||||
-- 错误日志包括四种:
|
||||
-- 1、系统主任务运行时的错误日志
|
||||
-- 此类错误会导致软件重启,错误日志保存在/luaerrinfo.txt文件中
|
||||
-- 2、调用sys.taskInit创建的协程运行过程中的错误日志
|
||||
-- 此类错误会终止当前协程的运行,但是不会导致软件重启,错误日志保存在/lib_err.txt中
|
||||
-- 3、调用errDump.appendErr或者sys.restart接口保存的错误日志
|
||||
-- 此类错误日志保存在/lib_err.txt中
|
||||
-- 4、调用errDump.setNetworkLog接口打开网络异常日志功能后,会自动保存最近几种网络异常日志
|
||||
-- 错误日志保存在/lib_network_err.txt中
|
||||
-- 5、底层固件的死机信息
|
||||
--
|
||||
-- 其中2和3保存的错误日志,最多支持5K字节
|
||||
-- 每次上报错误日志给调试服务器之后,会清空已保存的日志
|
||||
-- @module errDump
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2017.09.26
|
||||
require"socket"
|
||||
require"misc"
|
||||
module(..., package.seeall)
|
||||
|
||||
--错误信息文件以及错误信息内容
|
||||
local LIB_ERR_FILE,libErr,LIB_ERR_MAX_LEN = "/lib_err.txt","",5*1024
|
||||
local LUA_ERR_FILE,luaErr = "/luaerrinfo.txt",""
|
||||
local sReporting,sProtocol,switch
|
||||
local LIB_NETWORK_ERR_FILE,sNetworkLog,stNetworkLog,sNetworkLogFlag = "/lib_network_err.txt","",{}
|
||||
local firmwareAssertErr = ""
|
||||
|
||||
-- 初始化LIB_ERR_FILE文件中的错误信息(读取到内存中,并且打印出来)
|
||||
-- @return nil
|
||||
-- @usage readTxt.initErr()
|
||||
local function initErr()
|
||||
libErr = io.readFile(LIB_ERR_FILE) or ""
|
||||
if libErr~="" then
|
||||
log.error("errDump.libErr", libErr)
|
||||
end
|
||||
|
||||
luaErr = io.readFile(LUA_ERR_FILE) or ""
|
||||
if luaErr~="" then
|
||||
log.error("errDump.luaErr", luaErr)
|
||||
end
|
||||
|
||||
sNetworkLog = io.readFile(LIB_NETWORK_ERR_FILE) or ""
|
||||
if sNetworkLog~="" then
|
||||
log.error("errDump.libNetErr", sNetworkLog)
|
||||
end
|
||||
|
||||
if type(rtos.get_fatal_info)=="function" then
|
||||
firmwareAssertErr = rtos.get_fatal_info() or ""
|
||||
if firmwareAssertErr~="" then
|
||||
log.error("errDump.firmwareAssertErr", firmwareAssertErr)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- 追加错误信息到LIB_ERR_FILE文件中(文件最多允许存储5K字节的数据)
|
||||
-- @string s 用户自定义的错误信息,errDump功能模块会对此错误信息做如下处理:
|
||||
-- 1、重启后会通过Luat下载调试工具输出,在trace中搜索errDump.libErr,可以搜索到错误信息
|
||||
-- 2、如果用户调用errDump.request接口设置了错误信息要上报的调试服务器地址和端口,则每次重启会自动上报错误信息到调试服务器
|
||||
-- 3、如果用户调用errDump.request接口设置了定时上报,则定时上报时会上报错误信息到调试服务器
|
||||
-- 其中第2和第3种情况,上报成功后,会自动清除错误信息
|
||||
-- @return bool result,true表示成功,false或者nil表示失败
|
||||
-- @usage errDump.appendErr("net working timeout!")
|
||||
function appendErr(s)
|
||||
if s then
|
||||
s=s.."\r\n"
|
||||
log.error("errDump.appendErr",s)
|
||||
if (s:len()+libErr:len())<=LIB_ERR_MAX_LEN then
|
||||
libErr = libErr..s
|
||||
return io.writeFile(LIB_ERR_FILE, libErr)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function reportData()
|
||||
local s = _G.PROJECT.."_"..rtos.get_version()..",".._G.VERSION..","..misc.getImei()..","..misc.getSn()..","
|
||||
s = s.."\r\npoweron reason:"..rtos.poweron_reason().."\r\n"..luaErr..(luaErr:len()>0 and "\r\n" or "")..libErr..(libErr:len()>0 and "\r\n" or "")..sNetworkLog
|
||||
s = s..(firmwareAssertErr:len()>0 and "\r\n" or "")..firmwareAssertErr
|
||||
return s
|
||||
end
|
||||
|
||||
local function httpPostCbFnc(result,statusCode)
|
||||
log.info("errDump.httpPostCbFnc",result,statusCode)
|
||||
sys.publish("ERRDUMP_HTTP_POST",result,statusCode)
|
||||
end
|
||||
|
||||
local function checkSwitch(addr)
|
||||
local first = true
|
||||
while true do
|
||||
if not socket.isReady() then sys.waitUntil("IP_READY_IND") end
|
||||
--log.info("errDump.clientTask","err",luaErr~="" or libErr~="")
|
||||
local host,port = addr:match("://(.+):(%d+)$")
|
||||
if not host then log.error("errDump.request invalid host port") return end
|
||||
local result, data, time
|
||||
while true do
|
||||
local sck = socket.udp()
|
||||
data = string.char(0, 0) .. misc.getImei()
|
||||
if sck:connect(host, port) then
|
||||
if sck:send(data) then
|
||||
result, data = sck:recv(5000)
|
||||
if result then
|
||||
data, result = json.decode(data)
|
||||
if result then
|
||||
if data.r == 1 then
|
||||
switch = true
|
||||
time = tonumber(data.expire_at)
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
switch = false
|
||||
end
|
||||
else
|
||||
switch = false
|
||||
end
|
||||
sck:close()
|
||||
if time then
|
||||
local clk = time - os.time()
|
||||
if clk < 7200 then
|
||||
sys.timerStart(function() switch = false end, clk * 1000)
|
||||
end
|
||||
end
|
||||
break
|
||||
end
|
||||
if first then
|
||||
sys.publish("GET_SWITCH")
|
||||
first = nil
|
||||
end
|
||||
sys.wait(7200000)
|
||||
end
|
||||
end
|
||||
|
||||
function clientTask(protocol,addr,period,flag)
|
||||
sReporting = true
|
||||
if flag then
|
||||
sys.taskInit(checkSwitch, addr)
|
||||
sys.waitUntil("GET_SWITCH")
|
||||
end
|
||||
|
||||
while true do
|
||||
if not socket.isReady() then sys.waitUntil("IP_READY_IND") end
|
||||
--log.info("errDump.clientTask","err",luaErr~="" or libErr~="")
|
||||
if luaErr~="" or libErr~="" or sNetworkLog~="" or firmwareAssertErr~="" then
|
||||
local retryCnt,result,data = 0
|
||||
while true do
|
||||
if protocol=="http" or protocol=="https" then
|
||||
http.request("POST",addr,nil,nil,reportData(),20000,httpPostCbFnc)
|
||||
_,result = sys.waitUntil("ERRDUMP_HTTP_POST")
|
||||
else
|
||||
if flag and not switch then
|
||||
break
|
||||
end
|
||||
|
||||
local host,port = addr:match("://(.+):(%d+)$")
|
||||
if not host then log.error("errDump.request invalid host port") return end
|
||||
local sck = protocol=="udp" and socket.udp() or socket.tcp()
|
||||
if sck:connect(host,port) then
|
||||
result = sck:send(reportData())
|
||||
if result and protocol=="udp" then
|
||||
result,data = sck:recv(20000)
|
||||
if result then
|
||||
if not flag then
|
||||
result = data=="OK"
|
||||
else
|
||||
data, result = json.decode(data)
|
||||
if result then
|
||||
result = data.r == 1 and true or false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
sck:close()
|
||||
end
|
||||
|
||||
if result then
|
||||
libErr = ""
|
||||
os.remove(LIB_ERR_FILE)
|
||||
luaErr = ""
|
||||
os.remove(LUA_ERR_FILE)
|
||||
sNetworkLog = ""
|
||||
stNetworkLog = {}
|
||||
os.remove(LIB_NETWORK_ERR_FILE)
|
||||
firmwareAssertErr = ""
|
||||
if type(rtos.remove_fatal_info)=="function" then rtos.remove_fatal_info() end
|
||||
break
|
||||
else
|
||||
if flag then
|
||||
break
|
||||
end
|
||||
retryCnt = retryCnt+1
|
||||
if retryCnt==3 then
|
||||
break
|
||||
end
|
||||
sys.wait(5000)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if period then
|
||||
--log.info("errDump.clientTask","wait",period)
|
||||
sys.wait(period)
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
sReporting = false
|
||||
end
|
||||
|
||||
function updateNetworkLog()
|
||||
if sNetworkLogFlag then
|
||||
sNetworkLog = ""
|
||||
for k,v in pairs(stNetworkLog) do
|
||||
if v and v~="" then
|
||||
sNetworkLog = sNetworkLog.."\r\n"..k.."@"..v
|
||||
end
|
||||
end
|
||||
|
||||
if sNetworkLog~="" then
|
||||
io.writeFile(LIB_NETWORK_ERR_FILE,sNetworkLog)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local onceGsmRegistered,onceGprsAttached
|
||||
--- 配置网络错误日志开关
|
||||
-- @bool[opt=nil] flag 是否打开网络错误日志开关,true为打开,false或者nil为关闭
|
||||
-- @usage
|
||||
-- errDump.setNetworkLog(true)
|
||||
function setNetworkLog(flag)
|
||||
sNetworkLogFlag = flag
|
||||
local procer = flag and sys.subscribe or sys.unsubscribe
|
||||
if not flag then
|
||||
sNetworkLog,stNetworkLog = "",{}
|
||||
end
|
||||
|
||||
local function getTimeStr()
|
||||
local clk = os.date("*t")
|
||||
return string.format("%02d_%02d:%02d:%02d",clk.day,clk.hour,clk.min,clk.sec)
|
||||
end
|
||||
|
||||
procer("FLYMODE",function(value)
|
||||
if value then
|
||||
stNetworkLog["FLYMODE"] = getTimeStr()
|
||||
updateNetworkLog()
|
||||
end
|
||||
end)
|
||||
procer("SIM_IND",function(value)
|
||||
if value~="RDY" then
|
||||
stNetworkLog["SIM_IND"] = getTimeStr()..":"..value
|
||||
updateNetworkLog()
|
||||
end
|
||||
end)
|
||||
procer("NET_STATE_UNREGISTER",function()
|
||||
if onceGsmRegistered then
|
||||
stNetworkLog["NET_STATE_UNREGISTER"] = getTimeStr()
|
||||
updateNetworkLog()
|
||||
end
|
||||
end)
|
||||
procer("NET_STATE_REGISTERED",function() onceGsmRegistered=true end)
|
||||
procer("GPRS_ATTACH",function(value)
|
||||
if value then
|
||||
onceGprsAttached = true
|
||||
elseif onceGprsAttached then
|
||||
stNetworkLog["GPRS_ATTACH"] = getTimeStr()..":0"
|
||||
updateNetworkLog()
|
||||
end
|
||||
end)
|
||||
procer("LIB_SOCKET_CONNECT_FAIL_IND",function(ssl,prot,addr,port)
|
||||
stNetworkLog[(ssl and "ssl" or prot).."://"..addr..":"..port] = getTimeStr()..":connect fail"
|
||||
updateNetworkLog()
|
||||
end)
|
||||
procer("LIB_SOCKET_SEND_FAIL_IND",function(ssl,prot,addr,port)
|
||||
stNetworkLog[(ssl and "ssl" or prot).."://"..addr..":"..port] = getTimeStr()..":send fail"
|
||||
updateNetworkLog()
|
||||
end)
|
||||
procer("LIB_SOCKET_CLOSE_IND",function(ssl,prot,addr,port)
|
||||
stNetworkLog[(ssl and "ssl" or prot).."://"..addr..":"..port.." closed"] = getTimeStr()
|
||||
updateNetworkLog()
|
||||
end)
|
||||
procer("PDP_DEACT_IND",function()
|
||||
stNetworkLog["PDP_DEACT_IND"] = getTimeStr()
|
||||
updateNetworkLog()
|
||||
end)
|
||||
procer("IP_SHUT_IND",function()
|
||||
stNetworkLog["IP_SHUT_IND"] = getTimeStr()
|
||||
updateNetworkLog()
|
||||
end)
|
||||
end
|
||||
|
||||
--- 配置调试服务器地址,启动错误信息上报给调试服务器的功能,上报成功后,会清除错误信息
|
||||
-- @string addr 调试服务器地址信息,支持http,udp,tcp
|
||||
-- 1、如果调试服务器使用http协议,终端将采用POST命令,把错误信息上报到addr指定的URL中,addr的格式如下
|
||||
-- (除protocol和hostname外,其余字段可选;目前的实现不支持hash)
|
||||
-- |------------------------------------------------------------------------------|
|
||||
-- | protocol ||| auth | host | path | hash |
|
||||
-- |----------|||-----------|-----------------|---------------------------|-------|
|
||||
-- | ||| | hostname | port | pathname | search | |
|
||||
-- | ||| |----------|------|----------|----------------| |
|
||||
-- " http(s) :// user:pass @ host.com : 8080 /p/a/t/h ? query=string # hash "
|
||||
-- | ||| | | | | | |
|
||||
-- |------------------------------------------------------------------------------|
|
||||
-- 2、如果调试服务器使用udp协议,终端将错误信息,直接上报给调试服务器,调试服务器收到信息后,要回复大写的OK;addr格式如下:
|
||||
-- |----------|||----------|------|
|
||||
-- | protocol ||| hostname | port |
|
||||
-- | |||----------|------|
|
||||
-- " udp :// host.com : 8081 |
|
||||
-- | ||| | |
|
||||
-- |------------------------------|
|
||||
-- 3、如果调试服务器使用tcp协议,终端将错误信息,直接上报给调试服务器;addr格式如下:
|
||||
-- |----------|||----------|------|
|
||||
-- | protocol ||| hostname | port |
|
||||
-- | |||----------|------|
|
||||
-- " tcp :// host.com : 8082 |
|
||||
-- | ||| | |
|
||||
-- |------------------------------|
|
||||
-- @number[opt=600000] period 单位毫秒,定时检查错误信息并上报的间隔
|
||||
-- @bool flag 当使用合宙调试服务器时,此参数填为true;使用自定义服务器时,此参数可省略
|
||||
-- @return bool result,成功返回true,失败返回nil
|
||||
-- @usage
|
||||
-- errDump.request("http://www.user_server.com/errdump")
|
||||
-- errDump.request("udp://www.user_server.com:8081")
|
||||
-- errDump.request("tcp://www.user_server.com:8082")
|
||||
-- errDump.request("tcp://www.user_server.com:8082",6*3600*1000)
|
||||
-- errDump.request("udp://www.hezhou_server.com:8083",6*3600*1000,true)
|
||||
function request(addr,period,flag)
|
||||
local protocol = addr:match("(%a+)://")
|
||||
if protocol~="http" and protocol~="https" and protocol~="udp" and protocol~="tcp" then
|
||||
log.error("errDump.request invalid protocol",protocol)
|
||||
return
|
||||
end
|
||||
|
||||
if flag and protocol ~= "udp" then
|
||||
log.error("errDump.request invalid protocol",protocol)
|
||||
return
|
||||
end
|
||||
|
||||
if not sReporting then
|
||||
sys.taskInit(clientTask,protocol,addr,period or 600000, flag)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
initErr()
|
||||
344
4G/源代码/lib/ftp.lua
Normal file
344
4G/源代码/lib/ftp.lua
Normal file
@@ -0,0 +1,344 @@
|
||||
--- 模块功能:FTP客户端
|
||||
-- @module ftp
|
||||
-- @author Dozingfiretruck
|
||||
-- @license MIT
|
||||
-- @copyright OpenLuat.com
|
||||
-- @release 2020.12.08
|
||||
require "socket"
|
||||
require "utils"
|
||||
module(..., package.seeall)
|
||||
|
||||
local ftp_client -- ftp命令连接socket对象
|
||||
local ftp_data_client -- ftp数据连接socket对象
|
||||
local data_client_ip, data_client_port -- ftp数据连接地址
|
||||
|
||||
--- FTP客户端关闭
|
||||
function close()
|
||||
ftp_client:send("QUIT\r\n")
|
||||
ftp_client:close()
|
||||
log.info("ftp", "ftp close")
|
||||
end
|
||||
|
||||
--- FTP客户端命令
|
||||
-- @string command 命令,例如"PWD","HELP","SYST"
|
||||
-- @number[opt=0] timeout 接收超时时间,单位毫秒
|
||||
-- @return string,string,返回 response_code, response_message
|
||||
function command(command, timeout)
|
||||
if not ftp_client:send(command .. "\r\n") then
|
||||
close()
|
||||
return '426', 'SOCKET_SEND_ERROR'
|
||||
end
|
||||
local r, n = ftp_client:recv(timeout)
|
||||
if r == true and n:sub(1, 3) == "230" then
|
||||
r, n = ftp_client:recv(timeout)
|
||||
end
|
||||
log.info("ftp_command", n)
|
||||
if not r then
|
||||
close()
|
||||
return '503', 'SOCKET_RECV_TIMOUT'
|
||||
else
|
||||
return n:sub(1, 3), n:sub(4, #n)
|
||||
end
|
||||
end
|
||||
|
||||
--- 连接到PASV接口
|
||||
-- @number[opt=0] timeout 接收超时时间,单位毫秒
|
||||
-- @return string,string,返回 response_code, response_message
|
||||
local function pasv_connect(timeout)
|
||||
---被动模式
|
||||
if not ftp_client:send("PASV\r\n") then
|
||||
close()
|
||||
return '426', 'SOCKET_SEND_ERROR'
|
||||
end
|
||||
local r, n = ftp_client:recv(timeout)
|
||||
if r == true and n:sub(1, 3) == "230" then
|
||||
r, n = ftp_client:recv(timeout)
|
||||
end
|
||||
if not r then
|
||||
close()
|
||||
return '503', 'SOCKET_RECV_TIMOUT'
|
||||
end
|
||||
local h1, h2, h3, h4, p1, p2 =
|
||||
n:match("(%d+),(%d+),(%d+),(%d+),(%d+),(%d+)")
|
||||
data_client_ip, data_client_port =
|
||||
h1 .. '.' .. h2 .. '.' .. h3 .. '.' .. h4,
|
||||
string.format("%d", (p1 * 256 + p2))
|
||||
log.info("ftp ip", data_client_ip, "port", data_client_port)
|
||||
if not r then
|
||||
close()
|
||||
return '503', 'SOCKET_RECV_TIMOUT'
|
||||
end
|
||||
---创建ftp数据连接
|
||||
ftp_data_client = socket.tcp()
|
||||
log.info("ftp ip", data_client_ip, "port", data_client_port)
|
||||
if not ftp_data_client:connect(data_client_ip, data_client_port) then
|
||||
close()
|
||||
ftp_data_client:close()
|
||||
return '502', 'SOCKET_CONN_ERROR'
|
||||
end
|
||||
return '200', 'ftp pasv success'
|
||||
end
|
||||
|
||||
--- FTP客户端登录
|
||||
-- @string ftp_mode FTP模式"PASV"or"PORT",默认PASV:被动模式,PORT:主动模式(暂时仅支持被动模式)
|
||||
-- @string host ip地址
|
||||
-- @string port 端口,默认21
|
||||
-- @string username 用户名
|
||||
-- @string password 密码
|
||||
-- @number[opt=0] timeout 接收超时时间,单位毫秒
|
||||
-- @bool ssl 可选参数,默认为nil,ssl,是否为ssl连接,true表示是,其余表示否
|
||||
-- @table cert 可选参数,默认为nil,cert,ssl连接需要的证书配置,只有ssl参数为true时,才参数才有意义,cert格式如下:
|
||||
-- {
|
||||
-- caCert = "ca.crt", --CA证书文件(Base64编码 X.509格式),如果存在此参数,则表示客户端会对服务器的证书进行校验;不存在则不校验
|
||||
-- clientCert = "client.crt", --客户端证书文件(Base64编码 X.509格式),服务器对客户端的证书进行校验时会用到此参数
|
||||
-- clientKey = "client.key", --客户端私钥文件(Base64编码 X.509格式)
|
||||
-- clientPassword = "123456", --客户端证书文件密码[可选]
|
||||
-- }
|
||||
-- @return string,string,返回 response_code, response_message
|
||||
function login(ftp_mode, host, port, username, password, timeout, ssl, cert)
|
||||
if ftp_mode ~= "PASV" then
|
||||
log.error("暂不支持主动模式 ")
|
||||
return '-1', 'ftp ftp_mode error'
|
||||
end
|
||||
while not socket.isReady() do sys.wait(1000) end
|
||||
ftp_client = socket.tcp(ssl, cert)
|
||||
if not ftp_client:connect(host, port or 21) then
|
||||
close()
|
||||
return '502', 'SOCKET_CONN_ERROR'
|
||||
end
|
||||
local r, n = ftp_client:recv(timeout)
|
||||
if not r then
|
||||
close()
|
||||
return '503', 'SOCKET_RECV_TIMOUT'
|
||||
end
|
||||
if not ftp_client:send("USER " .. username .. "\r\n") then
|
||||
close()
|
||||
return '426', 'SOCKET_SEND_ERROR'
|
||||
end
|
||||
r = ftp_client:recv(timeout)
|
||||
if not r then
|
||||
close()
|
||||
return '503', 'SOCKET_RECV_TIMOUT'
|
||||
end
|
||||
-- 密码
|
||||
if not ftp_client:send("PASS " .. password .. "\r\n") then
|
||||
close()
|
||||
return '426', 'SOCKET_SEND_ERROR'
|
||||
end
|
||||
r, n = ftp_client:recv(timeout)
|
||||
if not r then
|
||||
close()
|
||||
return '503', 'SOCKET_RECV_TIMOUT'
|
||||
end
|
||||
if n:sub(1, 3) == '230' then
|
||||
log.info("ftp", n)
|
||||
elseif n:sub(1, 3) == '530' then
|
||||
log.error("ftp Password error ", n)
|
||||
close()
|
||||
return '530', n
|
||||
end
|
||||
log.info("ftp login success")
|
||||
return '200', 'ftp login success'
|
||||
end
|
||||
|
||||
--- FTP客户端文件上传
|
||||
-- @string remote_file 远程文件名
|
||||
-- @string local_file 本地文件名
|
||||
-- @number[opt=0] timeout 接收超时时间,单位毫秒
|
||||
-- @return string,string,返回 response_code, response_message
|
||||
function upload(remote_file, local_file, timeout)
|
||||
local com, msg = pasv_connect()
|
||||
if com ~= "200" then return com, msg end
|
||||
-- 文件上传
|
||||
if not ftp_client:send("APPE " .. remote_file .. "\r\n") then
|
||||
close()
|
||||
ftp_data_client:close()
|
||||
return '426', 'SOCKET_SEND_ERROR'
|
||||
end
|
||||
local r, n = ftp_client:recv(timeout)
|
||||
log.info("ftp upload", n)
|
||||
if not r then
|
||||
close()
|
||||
ftp_data_client:close()
|
||||
return '503', 'SOCKET_RECV_TIMOUT'
|
||||
end
|
||||
if n:sub(1, 3) == '553' then
|
||||
log.error("ftp STOR error ", n)
|
||||
close()
|
||||
ftp_data_client:close()
|
||||
return '553', n
|
||||
end
|
||||
local file = io.open(local_file, "r")
|
||||
if file then
|
||||
local file_size = io.fileSize(local_file)
|
||||
local file_cnt = 0
|
||||
log.info("ftp", "local_file_size", file_size)
|
||||
repeat
|
||||
log.info("ftp", "file_cnt", file_cnt, "file_size - file_cnt",
|
||||
file_size - file_cnt)
|
||||
local temp_data = io.readStream(local_file, file_cnt, 11200)
|
||||
if not ftp_data_client:send(temp_data) then
|
||||
close()
|
||||
ftp_data_client:close()
|
||||
return '426', 'SOCKET_SEND_ERROR'
|
||||
end
|
||||
file_cnt = file_cnt + 11200
|
||||
until (file_size - file_cnt < 11200)
|
||||
local temp_data = io.readStream(local_file, file_cnt,
|
||||
file_size - file_cnt)
|
||||
if not ftp_data_client:send(temp_data) then
|
||||
close()
|
||||
ftp_data_client:close()
|
||||
return '426', 'SOCKET_SEND_ERROR'
|
||||
end
|
||||
file:close()
|
||||
ftp_data_client:close()
|
||||
return '200', 'ftp success'
|
||||
else
|
||||
log.info("文件打开失败")
|
||||
ftp_data_client:close()
|
||||
return '503', 'file open error'
|
||||
end
|
||||
end
|
||||
|
||||
--- FTP客户端文件下载
|
||||
-- @string remote_file 远程文件名
|
||||
-- @string local_file 本地文件名
|
||||
-- @number[opt=0] timeout 接收超时时间,单位毫秒
|
||||
-- @return string,string,返回 response_code, response_message
|
||||
function download(remote_file, local_file, timeout)
|
||||
local com, msg = pasv_connect()
|
||||
if com ~= "200" then return com, msg end
|
||||
-- 文件大小
|
||||
if not ftp_client:send("SIZE " .. remote_file .. "\r\n") then
|
||||
close()
|
||||
ftp_data_client:close()
|
||||
return '426', 'SOCKET_SEND_ERROR'
|
||||
end
|
||||
local r, n = ftp_client:recv(timeout)
|
||||
if not r then
|
||||
close()
|
||||
ftp_data_client:close()
|
||||
return '503', 'SOCKET_RECV_TIMOUT'
|
||||
end
|
||||
if n:sub(1, 3) == '213' then
|
||||
log.info("ftp filename size", n:sub(5, #n))
|
||||
filename_size = tonumber(n:sub(5, #n))
|
||||
elseif n:sub(1, 3) == '550' then
|
||||
log.error("ftp filename error ", n)
|
||||
return
|
||||
end
|
||||
-- 文件下载
|
||||
if not ftp_client:send("RETR " .. remote_file .. "\r\n") then
|
||||
close()
|
||||
ftp_data_client:close()
|
||||
return '426', 'SOCKET_SEND_ERROR'
|
||||
end
|
||||
local r, n = ftp_client:recv(timeout)
|
||||
log.info("ftp download", n)
|
||||
if not r then
|
||||
close()
|
||||
ftp_data_client:close()
|
||||
return '503', 'SOCKET_RECV_TIMOUT'
|
||||
end
|
||||
if io.exists(local_file) then
|
||||
os.remove(local_file)
|
||||
log.info("删除文件", local_file)
|
||||
end
|
||||
local file = io.open(local_file, "ab")
|
||||
if file then
|
||||
while true do
|
||||
local r, file_data = ftp_data_client:recv(timeout)
|
||||
if r then
|
||||
if file_data:find("226 Successfully transferred ") == nil and
|
||||
file_data:find("226 Transfer OK") == nil then
|
||||
file:write(file_data)
|
||||
end
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
file:close()
|
||||
ftp_data_client:close()
|
||||
return '200', 'ftp success'
|
||||
else
|
||||
log.info("文件打开失败")
|
||||
ftp_data_client:close()
|
||||
return '503', 'file open error'
|
||||
end
|
||||
end
|
||||
|
||||
--- 设置FTP传输类型 A:ascii I:Binary
|
||||
-- @string mode A:ascii I:Binary
|
||||
-- @number[opt=0] timeout 接收超时时间,单位毫秒
|
||||
-- @return string,string,返回 response_code, response_message
|
||||
function checktype(mode, timeout) return command("TYPE " .. mode, timeout) end
|
||||
|
||||
--- 显示当前工作目录
|
||||
-- @number[opt=0] timeout 接收超时时间,单位毫秒
|
||||
-- @return string,string,返回 response_code, response_message
|
||||
function pwd(timeout) return command("PWD ", timeout) end
|
||||
|
||||
--- 更改工作目录
|
||||
-- @string path 工作目录
|
||||
-- @number[opt=0] timeout 接收超时时间,单位毫秒
|
||||
-- @return string,string,返回 response_code, response_message
|
||||
function cwd(path, timeout) return command("CWD " .. path, timeout) end
|
||||
|
||||
--- 回到上级目录
|
||||
-- @number[opt=0] timeout 接收超时时间,单位毫秒
|
||||
-- @return string,string,返回 response_code, response_message
|
||||
function cdup(timeout) return command("CDUP", timeout) end
|
||||
|
||||
--- 创建目录
|
||||
-- @string path 目录
|
||||
-- @number[opt=0] timeout 接收超时时间,单位毫秒
|
||||
-- @return string,string,返回 response_code, response_message
|
||||
function mkd(path, timeout) return command("MKD " .. path, timeout) end
|
||||
|
||||
--- 列出目录列表或文件信息
|
||||
-- @string file_irectory 目录或文件
|
||||
-- @number[opt=0] timeout 接收超时时间,单位毫秒
|
||||
-- @return string,string,返回 response_code, response_message
|
||||
function list(file_irectory, timeout)
|
||||
local com, msg = pasv_connect()
|
||||
if com ~= "200" then return com, msg end
|
||||
local r, n = command("LIST " .. file_irectory, timeout)
|
||||
if r == "503" then return r, n end
|
||||
|
||||
r, n = ftp_client:recv(timeout)
|
||||
log.info("ftp", r, n)
|
||||
if not r then
|
||||
close()
|
||||
ftp_data_client:close()
|
||||
return '503', 'SOCKET_RECV_TIMOUT'
|
||||
end
|
||||
local data = ""
|
||||
while true do
|
||||
local r, n = ftp_data_client:recv(timeout)
|
||||
if r then
|
||||
data = data .. n
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
ftp_data_client:close()
|
||||
return r, data
|
||||
end
|
||||
|
||||
--- 删除目录
|
||||
-- @string file_irectory 路径目录
|
||||
-- @number[opt=0] timeout 接收超时时间,单位毫秒
|
||||
-- @return string,string,返回 response_code, response_message
|
||||
function deletefolder(file_irectory, timeout)
|
||||
return command("RMD " .. file_irectory, timeout)
|
||||
end
|
||||
|
||||
--- 删除文件
|
||||
-- @string file_irectory 路径文件(相对/绝对)
|
||||
-- @number[opt=0] timeout 接收超时时间,单位毫秒
|
||||
-- @return string,string,返回 response_code, response_message
|
||||
function deletefile(file_irectory, timeout)
|
||||
return command("DELE " .. file_irectory, timeout)
|
||||
end
|
||||
|
||||
901
4G/源代码/lib/gps.lua
Normal file
901
4G/源代码/lib/gps.lua
Normal file
@@ -0,0 +1,901 @@
|
||||
--- 模块功能:GPS模块管理
|
||||
-- 注意:此功能模块中的功能接口可以分为四大类:
|
||||
-- 1、GPS开启
|
||||
-- 2、GPS关闭
|
||||
-- 3、GPS定位数据读取
|
||||
-- 4、GPS参数和功能设置
|
||||
-- 1、2、3是通用功能,除了支持合宙的Air530模块,理论上也支持其他厂家的串口GPS模块
|
||||
-- 4是专用功能,仅支持合宙的Air530模块
|
||||
-- @module gps
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2017.10.23
|
||||
require"pm"
|
||||
require"utils"
|
||||
module(..., package.seeall)
|
||||
|
||||
local smatch,sfind,slen,ssub,sbyte,sformat,srep = string.match,string.find,string.len,string.sub,string.byte,string.format,string.rep
|
||||
--GPS开启标志,true表示开启状态,false或者nil表示关闭状态
|
||||
local openFlag
|
||||
--GPS定位标志,"2D"表示2D定位,"3D"表示3D定位,其余表示未定位
|
||||
--GPS定位标志,true表示,其余表示未定位
|
||||
local fixFlag,fixOnece=nil,nil
|
||||
--GPS定位成功后,过滤掉前filterSeconds秒的经纬度信息
|
||||
--是否已经过滤完成
|
||||
local filterSeconds,filteredFlag = 0
|
||||
--从定位成功切换到定位失败,连续定位失败的次数
|
||||
local fixFailCnt = 0
|
||||
--经纬度类型和数据
|
||||
local latitudeType,latitude,longitudeType,longitude = "N","","E",""
|
||||
--海拔,速度,方向角
|
||||
local altitude,speed,course = "0","0","0"
|
||||
--参与定位的卫星个数,所有可见卫星的最大信号值,所有可见卫星的最大信号值中间缓存值
|
||||
local usedSateCnt,maxSignalStrength,maxSignalStrengthVar = "0",0,0
|
||||
--可见卫星个数
|
||||
local viewedGpsSateCnt,viewedBdSateCnt = "0","0"
|
||||
--可用卫星号,UTC时间,信噪比
|
||||
local SateSn,UtcTime,Gsv
|
||||
--大地高,度分经度,度分纬度
|
||||
local Sep,Ggalng,Ggalat
|
||||
--是否需要解析项
|
||||
local psUtcTime,psGsv,psSn
|
||||
|
||||
--GPS供电设置函数
|
||||
local powerCbFnc
|
||||
--串口配置
|
||||
uartBaudrate = 115200
|
||||
local uartID,uartDatabits,uartParity,uartStopbits = 2,8,uart.PAR_NONE,uart.STOP_1
|
||||
--搜星模式命令字符串,"$PGKC115," .. gps .. "," .. glonass .. "," .. beidou .. "," .. galieo .. "*"
|
||||
local aerialModeStr,aerialModeSetted = ""
|
||||
--运行模式命令字符串,"$PGKC105," .. mode .. "," .. rt .. "," .. st .. "*"
|
||||
local runModeStr,runModeSetted = ""
|
||||
--正常运行模式下NMEA数据上报间隔命令字符串,"$PGKC101," .. interval .. "*"
|
||||
local nmeaReportStr,nmeaReportSetted = ""
|
||||
--每种NEMA数据的输出频率命令字符串
|
||||
local nmeaReportFreqStr,nmeaReportFreqSetted = ""
|
||||
--NMEA数据处理模式,0表示仅gps.lua内部处理,1表示仅用户自己处理,2表示gps.lua和用户同时处理
|
||||
--用户处理一条NMEA数据的回调函数
|
||||
local nmeaMode,nmeaCbFnc = 0
|
||||
--NMEA数据输出间隔
|
||||
local nmeaInterval = 1000
|
||||
--运行模式
|
||||
--0,正常运行模式
|
||||
--1,周期超低功耗跟踪模式
|
||||
--2,周期低功耗模式
|
||||
--4,直接进入超低功耗跟踪模式
|
||||
--8,自动低功耗模式,可以通过串口唤醒
|
||||
--9, 自动超低功耗跟踪模式,需要force on来唤醒
|
||||
local runMode = 0
|
||||
|
||||
--gps 的串口线程是否在工作;
|
||||
local taskFlag=false
|
||||
--runMode为1或者2时,GPS运行状态和休眠状态的时长
|
||||
local runTime,sleepTime
|
||||
|
||||
--检测gps是否工作正常的定时器ID
|
||||
local workAbnormalTimerId
|
||||
|
||||
--[[
|
||||
函数名:getstrength
|
||||
功能 :解析GSV数据
|
||||
参数 :
|
||||
sg:NEMA中的一行GSV数据
|
||||
返回值:无
|
||||
]]
|
||||
local function getstrength(sg)
|
||||
sg = ssub(sg, 4, #sg)
|
||||
local d1,d2,curnum,lineno,total,sgv_str = sfind(sg,"GSV,(%d),(%d),(%d+),(.*)%*.*")
|
||||
|
||||
if not curnum or not lineno or not total or not sgv_str then
|
||||
return
|
||||
end
|
||||
if lineno == nil then
|
||||
maxSignalStrengthVar = 0
|
||||
maxSignalStrength = 0
|
||||
elseif tonumber(lineno) == 1 then
|
||||
maxSignalStrength = maxSignalStrengthVar
|
||||
maxSignalStrengthVar = 0
|
||||
end
|
||||
|
||||
local tmpstr,i = sgv_str
|
||||
for i=1,4 do
|
||||
local d1,d2,id,elevation,azimuth,strength = sfind(tmpstr,"(%d+),([%-]*%d*),(%d*),(%d*)")
|
||||
if id == nil then return end
|
||||
if strength == "" or not strength then
|
||||
strength = "00"
|
||||
end
|
||||
strength = tonumber(strength)
|
||||
if strength > maxSignalStrengthVar then
|
||||
maxSignalStrengthVar = strength
|
||||
end
|
||||
|
||||
local idx,cur,fnd,tmpid = 0,id..","..elevation..","..azimuth..","..strength..",",false
|
||||
for tmpid in string.gmatch(Gsv,"(%d+),%d*,%d*,%d*,") do
|
||||
idx = idx + 1
|
||||
if tmpid == id then fnd = true break end
|
||||
end
|
||||
if fnd then
|
||||
local pattern,i = ""
|
||||
for i=1,idx do
|
||||
pattern = pattern.."%d+,%d*,%d*,%d*,"
|
||||
end
|
||||
local m1,m2 = sfind(Gsv,"^"..pattern)
|
||||
if m1 and m2 then
|
||||
local front = ssub(Gsv,1,m2)
|
||||
local n1,n2 = sfind(front,"%d+,%d*,%d*,%d*,$")
|
||||
if n1 and n2 then
|
||||
Gsv = ssub(Gsv,1,n1-1)..cur..ssub(Gsv,n2+1,-1)
|
||||
end
|
||||
end
|
||||
else
|
||||
Gsv = Gsv..cur
|
||||
end
|
||||
|
||||
tmpstr = ssub(tmpstr,d2+1,-1)
|
||||
end
|
||||
end
|
||||
|
||||
local function filterTimerFnc()
|
||||
log.info("gps.filterTimerFnc end")
|
||||
filteredFlag = true
|
||||
end
|
||||
|
||||
local function stopWorkAbnormalTimer()
|
||||
if workAbnormalTimerId then
|
||||
sys.timerStop(workAbnormalTimerId)
|
||||
workAbnormalTimerId = nil
|
||||
end
|
||||
end
|
||||
|
||||
local function parseNmea(s)
|
||||
if not s or s=="" then return end
|
||||
local lat,lng,spd,cog,gpsFind,gpsTime,gpsDate,locSateCnt,hdp,latTyp,lngTyp,altd
|
||||
|
||||
local hexStr = s:toHex()
|
||||
if "AAF00C0001009500039B0D0A"==hexStr then
|
||||
sys.publish("GPS_STATE","BINARY_CMD_ACK")
|
||||
stopWorkAbnormalTimer()
|
||||
return
|
||||
elseif smatch(hexStr,"^AAF00C000300") then
|
||||
sys.publish("GPS_STATE",smatch(hexStr,"^AAF00C000300FFFF") and "WRITE_EPH_END_ACK" or "WRITE_EPH_ACK")
|
||||
stopWorkAbnormalTimer()
|
||||
return
|
||||
end
|
||||
|
||||
local fixed,workAbnormal
|
||||
if smatch(s,"GGA") then
|
||||
lat,latTyp,lng,lngTyp,gpsFind,locSateCnt,hdp,altd,sep = smatch(s,"GGA,%d+%.%d+,(%d+%.%d+),([NS]),(%d+%.%d+),([EW]),(%d),(%d+),([%d%.]*),(.*),M,(.*),M")
|
||||
if (gpsFind=="1" or gpsFind=="2" or gpsFind=="4") and altd then
|
||||
--fixed = true
|
||||
altitude = altd
|
||||
latitudeType,longitudeType,latitude,longitude = latTyp,lngTyp,lat,lng
|
||||
usedSateCnt = locSateCnt
|
||||
Ggalng,Ggalat = (lngTyp=="W" and "-" or "")..lng,(latTyp=="S" and "-" or "")..lat
|
||||
Sep = sep
|
||||
else
|
||||
fixed = false
|
||||
end
|
||||
elseif smatch(s,"RMC") then
|
||||
gpsTime,gpsFind,lat,latTyp,lng,lngTyp,spd,cog,gpsDate = smatch(s,"RMC,(%d%d%d%d%d%d)%.%d+,(%w),(%d*%.*%d*),([NS]*),(%d*%.*%d*),([EW]*),(.-),(.-),(%d%d%d%d%d%d),")
|
||||
if gpsFind=="A" and cog then
|
||||
fixed = true
|
||||
latitudeType,longitudeType,latitude,longitude = latTyp,lngTyp,lat,lng
|
||||
speed = spd
|
||||
course = cog
|
||||
else
|
||||
fixed = false
|
||||
end
|
||||
if psUtcTime and gpsFind == "A" and gpsTime and gpsDate and gpsTime ~= "" and gpsDate ~= "" then
|
||||
local yy,mm,dd,h,m,s = tonumber(ssub(gpsDate,5,6)),tonumber(ssub(gpsDate,3,4)),tonumber(ssub(gpsDate,1,2)),tonumber(ssub(gpsTime,1,2)),tonumber(ssub(gpsTime,3,4)),tonumber(ssub(gpsTime,5,6))
|
||||
UtcTime = {year=2000+yy,month=mm,day=dd,hour=h,min=m,sec=s}
|
||||
end
|
||||
elseif smatch(s,"GPGSV") then
|
||||
viewedGpsSateCnt = tonumber(smatch(s,"%d+,%d+,(%d+)") or "0")
|
||||
if psGsv then getstrength(s) end
|
||||
elseif smatch(s,"BDGSV") then
|
||||
viewedBdSateCnt = tonumber(smatch(s,"%d+,%d+,(%d+)") or "0")
|
||||
if psGsv then getstrength(s) end
|
||||
elseif smatch(s,"GSA") then
|
||||
if psSn then
|
||||
local satesn = smatch(s,"GSA,%w*,%d*,(%d*,%d*,%d*,%d*,%d*,%d*,%d*,%d*,%d*,%d*,%d*,%d*,)") or ""
|
||||
if slen(satesn) > 0 and smatch(satesn,"%d+,") then
|
||||
SateSn = satesn
|
||||
end
|
||||
end
|
||||
else
|
||||
workAbnormal = true
|
||||
end
|
||||
|
||||
if not workAbnormal then
|
||||
stopWorkAbnormalTimer()
|
||||
end
|
||||
|
||||
if filterSeconds>0 and fixed and not fixFlag and not filteredFlag then
|
||||
if not sys.timerIsActive(filterTimerFnc) then
|
||||
log.info("gps.filterTimerFnc begin")
|
||||
sys.publish("GPS_STATE","LOCATION_FILTER")
|
||||
sys.timerStart(filterTimerFnc,filterSeconds*1000)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
--定位成功
|
||||
if fixed then
|
||||
if not fixFlag then
|
||||
fixFlag,filteredFlag = true,true
|
||||
fixOnece=true
|
||||
fixFailCnt = 0
|
||||
sys.publish("GPS_STATE","LOCATION_SUCCESS")
|
||||
end
|
||||
elseif fixed==false then
|
||||
if fixFlag then
|
||||
fixFailCnt = fixFailCnt+1
|
||||
if fixFailCnt>=20 then
|
||||
fixFlag,filteredFlag = false
|
||||
sys.timerStop(filterTimerFnc)
|
||||
sys.publish("GPS_STATE","LOCATION_FAIL")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function taskRead()
|
||||
local cacheData = ""
|
||||
local co = coroutine.running()
|
||||
while true do
|
||||
local s =""
|
||||
if openFlag then
|
||||
s= uart.read(uartID, "*l")
|
||||
end
|
||||
if s == "" then
|
||||
uart.on(uartID,"receive",function() coroutine.resume(co) end)
|
||||
coroutine.yield()
|
||||
uart.on(uartID,"receive")
|
||||
else
|
||||
cacheData = cacheData..s
|
||||
local d1,d2,nemaStr = sfind(cacheData,"\r\n")
|
||||
while d1 do
|
||||
writePendingCmds()
|
||||
nemaStr = ssub(cacheData,1,d2)
|
||||
cacheData = ssub(cacheData,d2+1,-1)
|
||||
|
||||
if nmeaMode==0 or nmeaMode==2 then
|
||||
--解析一行NEMA数据
|
||||
parseNmea(nemaStr)
|
||||
end
|
||||
if (nmeaMode==1 or nmeaMode==2) and nmeaCbFnc then
|
||||
nmeaCbFnc(nemaStr)
|
||||
end
|
||||
d1,d2 = sfind(cacheData,"\r\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- GPS串口写命令操作
|
||||
-- @string cmd,GPS指令(cmd格式:"$PGKC149,1,115200*"或者"$PGKC149,1,115200*XX\r\n")
|
||||
-- @bool isFull,cmd是否为完整的指令格式,包括校验和以及\r\n;true表示完整,false或者nil为不完整
|
||||
-- @return nil
|
||||
-- @usage gps.writeCmd(cmd)
|
||||
function writeCmd(cmd,isFull)
|
||||
local tmp = cmd
|
||||
if not isFull then
|
||||
tmp = 0
|
||||
for i=2,cmd:len()-1 do
|
||||
tmp = bit.bxor(tmp,cmd:byte(i))
|
||||
end
|
||||
tmp = cmd..(string.format("%02X",tmp)):upper().."\r\n"
|
||||
end
|
||||
uart.write(uartID,tmp)
|
||||
log.info("gps.writecmd",tmp)
|
||||
log.info("gps.writecmd:toHex",tmp:toHex())
|
||||
end
|
||||
|
||||
function writePendingCmds()
|
||||
if not aerialModeSetted and aerialModeStr~="" then writeCmd(aerialModeStr) aerialModeSetted=true end
|
||||
if not runModeSetted and runModeStr~="" then writeCmd(runModeStr) runModeSetted=true end
|
||||
if not nmeaReportSetted and nmeaReportStr~="" then writeCmd(nmeaReportStr) nmeaReportSetted=true end
|
||||
if not nmeaReportFreqSetted and nmeaReportFreqStr~="" then writeCmd(nmeaReportFreqStr) nmeaReportFreqSetted=true end
|
||||
end
|
||||
|
||||
local function _open()
|
||||
if openFlag then return end
|
||||
pm.wake("gps.lua")
|
||||
uart.setup(uartID,uartBaudrate,uartDatabits,uartParity,uartStopbits)
|
||||
if not taskFlag then
|
||||
taskFlag =true
|
||||
sys.taskInit(taskRead)
|
||||
end
|
||||
if powerCbFnc then
|
||||
powerCbFnc(true)
|
||||
else
|
||||
pmd.ldoset(15, pmd.LDO_VMMC)
|
||||
rtos.sys32k_clk_out(1)
|
||||
end
|
||||
openFlag = true
|
||||
workAbnormalTimerId = sys.timerStart(sys.publish,8000,"GPS_WORK_ABNORMAL_IND")
|
||||
sys.publish("GPS_STATE","OPEN")
|
||||
fixFlag,filteredFlag = false
|
||||
Ggalng,Ggalat,Gsv,Sep = "","",""
|
||||
log.info("gps._open")
|
||||
end
|
||||
|
||||
local function _close()
|
||||
if not openFlag then return end
|
||||
if powerCbFnc then
|
||||
powerCbFnc(false)
|
||||
else
|
||||
pmd.ldoset(0,pmd.LDO_VMMC)
|
||||
rtos.sys32k_clk_out(0)
|
||||
end
|
||||
uart.close(uartID)
|
||||
pm.sleep("gps.lua")
|
||||
openFlag = false
|
||||
sys.publish("GPS_STATE","CLOSE",fixFlag)
|
||||
stopWorkAbnormalTimer()
|
||||
fixFlag,filteredFlag = false
|
||||
sys.timerStop(filterTimerFnc)
|
||||
Ggalng,Ggalat,Gsv,Sep = "","",""
|
||||
aerialModeSetted,runModeSetted,nmeaReportSetted,nmeaReportFreqSetted = nil
|
||||
log.info("gps._close")
|
||||
end
|
||||
|
||||
|
||||
--- GPS应用模式1.
|
||||
--
|
||||
-- 打开GPS后,GPS定位成功时,如果有回调函数,会调用回调函数
|
||||
--
|
||||
-- 使用此应用模式调用gps.open打开的“GPS应用”,必须主动调用gps.close或者gps.closeAll才能关闭此“GPS应用”,主动关闭时,即使有回调函数,也不会调用回调函数
|
||||
DEFAULT = 1
|
||||
--- GPS应用模式2.
|
||||
--
|
||||
-- 打开GPS后,如果在GPS开启最大时长到达时,没有定位成功,如果有回调函数,会调用回调函数,然后自动关闭此“GPS应用”
|
||||
--
|
||||
-- 打开GPS后,如果在GPS开启最大时长内,定位成功,如果有回调函数,会调用回调函数,然后自动关闭此“GPS应用”
|
||||
--
|
||||
-- 打开GPS后,在自动关闭此“GPS应用”前,可以调用gps.close或者gps.closeAll主动关闭此“GPS应用”,主动关闭时,即使有回调函数,也不会调用回调函数
|
||||
TIMERORSUC = 2
|
||||
--- GPS应用模式3.
|
||||
--
|
||||
-- 打开GPS后,在GPS开启最大时长时间到达时,无论是否定位成功,如果有回调函数,会调用回调函数,然后自动关闭此“GPS应用”
|
||||
--
|
||||
-- 打开GPS后,在自动关闭此“GPS应用”前,可以调用gps.close或者gps.closeAll主动关闭此“GPS应用”,主动关闭时,即使有回调函数,也不会调用回调函数
|
||||
TIMER = 3
|
||||
|
||||
--“GPS应用”表
|
||||
local tList = {}
|
||||
|
||||
--[[
|
||||
函数名:delItem
|
||||
功能 :从“GPS应用”表中删除一项“GPS应用”,并不是真正的删除,只是设置一个无效标志
|
||||
参数 :
|
||||
mode:GPS应用模式
|
||||
para:
|
||||
para.tag:“GPS应用”标记
|
||||
para.val:GPS开启最大时长
|
||||
para.cb:回调函数
|
||||
返回值:无
|
||||
]]
|
||||
local function delItem(mode,para)
|
||||
for i=1,#tList do
|
||||
--标志有效 并且 GPS应用模式相同 并且 “GPS应用”标记相同
|
||||
if tList[i].flag and tList[i].mode==mode and tList[i].para.tag==para.tag then
|
||||
--设置无效标志
|
||||
tList[i].flag,tList[i].delay = false
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:addItem
|
||||
功能 :新增一项“GPS应用”到“GPS应用”表
|
||||
参数 :
|
||||
mode:GPS应用模式
|
||||
para:
|
||||
para.tag:“GPS应用”标记
|
||||
para.val:GPS开启最大时长
|
||||
para.cb:回调函数
|
||||
返回值:无
|
||||
]]
|
||||
local function addItem(mode,para)
|
||||
--删除相同的“GPS应用”
|
||||
delItem(mode,para)
|
||||
local item,i,fnd = {flag=true, mode=mode, para=para}
|
||||
--如果是TIMERORSUC或者TIMER模式,初始化GPS工作剩余时间
|
||||
if mode==TIMERORSUC or mode==TIMER then item.para.remain = para.val end
|
||||
for i=1,#tList do
|
||||
--如果存在无效的“GPS应用”项,直接使用此位置
|
||||
if not tList[i].flag then
|
||||
tList[i] = item
|
||||
fnd = true
|
||||
break
|
||||
end
|
||||
end
|
||||
--新增一项
|
||||
if not fnd then table.insert(tList,item) end
|
||||
end
|
||||
|
||||
local function existTimerItem()
|
||||
for i=1,#tList do
|
||||
if tList[i].flag and (tList[i].mode==TIMERORSUC or tList[i].mode==TIMER or tList[i].para.delay) then return true end
|
||||
end
|
||||
end
|
||||
|
||||
local function timerFnc()
|
||||
for i=1,#tList do
|
||||
if tList[i].flag then
|
||||
log.info("gps.timerFnc@"..i,tList[i].mode,tList[i].para.tag,tList[i].para.val,tList[i].para.remain,tList[i].para.delay)
|
||||
local rmn,dly,md,cb = tList[i].para.remain,tList[i].para.delay,tList[i].mode,tList[i].para.cb
|
||||
|
||||
if rmn and rmn>0 then
|
||||
tList[i].para.remain = rmn-1
|
||||
end
|
||||
if dly and dly>0 then
|
||||
tList[i].para.delay = dly-1
|
||||
end
|
||||
rmn = tList[i].para.remain
|
||||
|
||||
if isFix() and md==TIMER and rmn==0 and not tList[i].para.delay then
|
||||
tList[i].para.delay = 1
|
||||
end
|
||||
|
||||
dly = tList[i].para.delay
|
||||
if isFix() then
|
||||
if dly and dly==0 then
|
||||
if cb then cb(tList[i].para.tag) end
|
||||
if md == DEFAULT then
|
||||
tList[i].para.delay = nil
|
||||
else
|
||||
close(md,tList[i].para)
|
||||
end
|
||||
end
|
||||
else
|
||||
if rmn and rmn == 0 then
|
||||
if cb then cb(tList[i].para.tag) end
|
||||
close(md,tList[i].para)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if existTimerItem() then sys.timerStart(timerFnc,1000) end
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:statInd
|
||||
功能 :处理GPS定位成功的消息
|
||||
参数 :
|
||||
evt:GPS消息类型
|
||||
返回值:无
|
||||
]]
|
||||
local function statInd(evt)
|
||||
--定位成功的消息
|
||||
if evt == "LOCATION_SUCCESS" then
|
||||
for i=1,#tList do
|
||||
log.info("gps.statInd@"..i,tList[i].flag,tList[i].mode,tList[i].para.tag,tList[i].para.val,tList[i].para.remain,tList[i].para.delay,tList[i].para.cb)
|
||||
if tList[i].flag then
|
||||
if tList[i].mode ~= TIMER then
|
||||
tList[i].para.delay = 1
|
||||
if tList[i].mode == DEFAULT then
|
||||
if existTimerItem() then sys.timerStart(timerFnc,1000) end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- 打开一个“GPS应用”
|
||||
-- “GPS应用”:指的是使用GPS功能的一个应用
|
||||
-- 例如,假设有如下3种需求,要打开GPS,则一共有3个“GPS应用”:
|
||||
-- “GPS应用1”:每隔1分钟打开一次GPS
|
||||
-- “GPS应用2”:设备发生震动时打开GPS
|
||||
-- “GPS应用3”:收到一条特殊短信时打开GPS
|
||||
-- 只有所有“GPS应用”都关闭了,才会去真正关闭GPS
|
||||
-- 每个“GPS应用”打开或者关闭GPS时,最多有4个参数,其中 GPS应用模式和GPS应用标记 共同决定了一个唯一的“GPS应用”:
|
||||
-- 1、GPS应用模式(必选)
|
||||
-- 2、GPS应用标记(必选)
|
||||
-- 3、GPS开启最大时长[可选]
|
||||
-- 4、回调函数[可选]
|
||||
-- 例如gps.open(gps.TIMERORSUC,{tag="TEST",val=120,cb=testGpsCb})
|
||||
-- gps.TIMERORSUC为GPS应用模式,"TEST"为GPS应用标记,120秒为GPS开启最大时长,testGpsCb为回调函数
|
||||
-- @number mode GPS应用模式,支持gps.DEFAULT,gps.TIMERORSUC,gps.TIMER三种
|
||||
-- @param para table类型,GPS应用参数
|
||||
-- para.tag:string类型,GPS应用标记
|
||||
-- para.val:number类型,GPS应用开启最大时长,mode参数为gps.TIMERORSUC或者gps.TIMER时,此值才有意义
|
||||
-- para.cb:GPS应用结束时的回调函数,回调函数的调用形式为para.cb(para.tag)
|
||||
-- @return nil
|
||||
-- @usage gps.open(gps.DEFAULT,{tag="TEST1",cb=test1Cb})
|
||||
-- @usage gps.open(gps.TIMERORSUC,{tag="TEST2",val=60,cb=test2Cb})
|
||||
-- @usage gps.open(gps.TIMER,{tag="TEST3",val=120,cb=test3Cb})
|
||||
-- @see DEFAULT,TIMERORSUC,TIMER
|
||||
function open(mode,para)
|
||||
assert((para and type(para) == "table" and para.tag and type(para.tag) == "string"),"gps.open para invalid")
|
||||
log.info("gps.open",mode,para.tag,para.val,para.cb)
|
||||
--如果GPS定位成功
|
||||
if isFix() then
|
||||
if mode~=TIMER then
|
||||
--执行回调函数
|
||||
if para.cb then para.cb(para.tag) end
|
||||
if mode==TIMERORSUC then return end
|
||||
end
|
||||
end
|
||||
addItem(mode,para)
|
||||
--真正去打开GPS
|
||||
_open()
|
||||
--启动1秒的定时器
|
||||
if existTimerItem() and not sys.timerIsActive(timerFnc) then
|
||||
sys.timerStart(timerFnc,1000)
|
||||
end
|
||||
end
|
||||
|
||||
--- 关闭一个“GPS应用”
|
||||
-- 只是从逻辑上关闭一个GPS应用,并不一定真正关闭GPS,是有所有的GPS应用都处于关闭状态,才会去真正关闭GPS
|
||||
-- @number mode GPS应用模式,支持gps.DEFAULT,gps.TIMERORSUC,gps.TIMER三种
|
||||
-- @param para table类型,GPS应用参数
|
||||
-- para.tag:string类型,GPS应用标记
|
||||
-- para.val:number类型,GPS应用开启最大时长,mode参数为gps.TIMERORSUC或者gps.TIMER时,此值才有意义;使用close接口时,不需要传入此参数
|
||||
-- para.cb:GPS应用结束时的回调函数,回调函数的调用形式为para.cb(para.tag);使用close接口时,不需要传入此参数
|
||||
-- @return nil
|
||||
-- @usage GPS应用模式和GPS应用标记唯一确定一个“GPS应用”,调用本接口关闭时,mode和para.tag要和gps.open打开一个“GPS应用”时传入的mode和para.tag保持一致
|
||||
-- @usage gps.close(gps.DEFAULT,{tag="TEST1"})
|
||||
-- @usage gps.close(gps.TIMERORSUC,{tag="TEST2"})
|
||||
-- @usage gps.close(gps.TIMER,{tag="TEST3"})
|
||||
-- @see open,DEFAULT,TIMERORSUC,TIMER
|
||||
function close(mode,para)
|
||||
assert((para and type(para)=="table" and para.tag and type(para.tag)=="string"),"gps.close para invalid")
|
||||
log.info("gps.close",mode,para.tag,para.val,para.cb)
|
||||
--删除此“GPS应用”
|
||||
delItem(mode,para)
|
||||
local valid,i
|
||||
for i=1,#tList do
|
||||
if tList[i].flag then
|
||||
valid = true
|
||||
end
|
||||
end
|
||||
--如果没有一个“GPS应用”有效,则关闭GPS
|
||||
if not valid then _close() end
|
||||
end
|
||||
|
||||
--- 关闭所有“GPS应用”
|
||||
-- @return nil
|
||||
-- @usage gps.closeAll()
|
||||
-- @see open,DEFAULT,TIMERORSUC,TIMER
|
||||
function closeAll()
|
||||
for i=1,#tList do
|
||||
if tList[i].flag and tList[i].para.cb then tList[i].para.cb(tList[i].para.tag) end
|
||||
close(tList[i].mode,tList[i].para)
|
||||
end
|
||||
end
|
||||
|
||||
--- 判断一个“GPS应用”是否处于激活状态
|
||||
-- @number mode GPS应用模式,支持gps.DEFAULT,gps.TIMERORSUC,gps.TIMER三种
|
||||
-- @param para table类型,GPS应用参数
|
||||
-- para.tag:string类型,GPS应用标记
|
||||
-- para.val:number类型,GPS应用开启最大时长,mode参数为gps.TIMERORSUC或者gps.TIMER时,此值才有意义;使用isActive接口时,不需要传入此参数
|
||||
-- para.cb:GPS应用结束时的回调函数,回调函数的调用形式为para.cb(para.tag);使用isActive接口时,不需要传入此参数
|
||||
-- @return bool result,处于激活状态返回true,否则返回nil
|
||||
-- @usage GPS应用模式和GPS应用标记唯一确定一个“GPS应用”,调用本接口查询状态时,mode和para.tag要和gps.open打开一个“GPS应用”时传入的mode和para.tag保持一致
|
||||
-- @usage gps.isActive(gps.DEFAULT,{tag="TEST1"})
|
||||
-- @usage gps.isActive(gps.TIMERORSUC,{tag="TEST2"})
|
||||
-- @usage gps.isActive(gps.TIMER,{tag="TEST3"})
|
||||
-- @see open,DEFAULT,TIMERORSUC,TIMER
|
||||
function isActive(mode,para)
|
||||
assert((para and type(para)=="table" and para.tag and type(para.tag)=="string"),"gps.isActive para invalid")
|
||||
for i=1,#tList do
|
||||
if tList[i].flag and tList[i].mode==mode and tList[i].para.tag==para.tag then return true end
|
||||
end
|
||||
end
|
||||
|
||||
--- 设置GPS模块供电控制的回调函数
|
||||
-- 如果使用的是Air800,或者供电控制使用的是LDO_VCAM,则打开GPS应用前不需要调用此接口进行设置
|
||||
-- 否则在调用gps.open前,使用此接口,传入自定义的供电控制函数cbFnc,GPS开启时,gps.lua自动执行cbFnc(true),GPS关闭时,gps.lua自动执行cbFnc(false)
|
||||
-- @param cbFnc function类型,用户自定义的GPS供电控制函数
|
||||
-- @return nil
|
||||
-- @usage gps.setPowerCbFnc(cbFnc)
|
||||
function setPowerCbFnc(cbFnc)
|
||||
powerCbFnc = cbFnc
|
||||
end
|
||||
|
||||
--- 设置GPS模块和GSM模块之间数据通信的串口参数
|
||||
-- 如果使用的是Air800,或者使用的UART2(波特率115200,数据位8,无检验位,停止位1),则打开GPS应用前不需要调用此接口进行设置
|
||||
-- 否则在调用gps.open前,使用此接口,传入UART参数
|
||||
-- @number id UART ID,支持1和2,1表示UART1,2表示UART2
|
||||
-- @number baudrate 波特率,支持1200,2400,4800,9600,10400,14400,19200,28800,38400,57600,76800,115200,230400,460800,576000,921600,1152000,4000000
|
||||
-- @number databits 数据位,支持8
|
||||
-- @number parity 校验位,支持uart.PAR_NONE,uart.PAR_EVEN,uart.PAR_ODD
|
||||
-- @number stopbits 停止位,支持uart.STOP_1,uart.STOP_2
|
||||
-- @return nil
|
||||
-- @usage gps.setUart(2,115200,8,uart.PAR_NONE,uart.STOP_1)
|
||||
function setUart(id,baudrate,databits,parity,stopbits)
|
||||
uartID,uartBaudrate,uartDatabits,uartParity,uartStopbits = id,baudrate,databits,parity,stopbits
|
||||
end
|
||||
|
||||
--- 设置GPS模块搜星模式.
|
||||
-- 如果使用的是Air800或者Air530,不调用此接口配置,则默认同时开启GPS和北斗定位
|
||||
-- @number gps GPS定位系统,1是打开,0是关闭
|
||||
-- @number beidou 中国北斗定位系统,1是打开,0是关闭
|
||||
-- @number glonass 俄罗斯Glonass定位系统,1是打开,0是关闭
|
||||
-- @number galieo 欧盟伽利略定位系统,1是打开,0是关闭
|
||||
-- @return nil
|
||||
-- @usage gps.setAeriaMode(1,1,0,0)
|
||||
function setAerialMode(gps,beidou,glonass,galieo)
|
||||
local gps = gps or 0
|
||||
local glonass = glonass or 0
|
||||
local beidou = beidou or 0
|
||||
local galieo = galieo or 0
|
||||
if gps+glonass+beidou+galieo == 0 then gps=1 beidou=1 end
|
||||
local tmpStr = "$PGKC115,"..gps..","..glonass..","..beidou..","..galieo.."*"
|
||||
if tmpStr~=aerialModeStr then
|
||||
aerialModeStr,aerialModeSetted = tmpStr
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- 设置NMEA数据处理模式.
|
||||
-- 如果不调用此接口配置,则默认仅gps.lua内部处理NMEA数据
|
||||
-- @number mode NMEA数据处理模式,0表示仅gps.lua内部处理,1表示仅用户自己处理,2表示gps.lua和用户同时处理
|
||||
-- @param cbFnc function类型,用户处理一条NMEA数据的回调函数,mode为1和2时,此值才有意义
|
||||
-- @return nil
|
||||
-- @usage gps.setNmeaMode(0)
|
||||
-- @usage gps.setNmeaMode(1,cbFnc)
|
||||
-- @usage gps.setNmeaMode(2,cbFnc)
|
||||
function setNmeaMode(mode,cbFnc)
|
||||
nmeaMode,nmeaCbFnc = mode,cbFnc
|
||||
end
|
||||
|
||||
-- 设置GPS模块的运行模式.
|
||||
-- 如果不调用此接口配置,则默认为正常运行模式
|
||||
-- @number mode 运行模式
|
||||
-- 0:正常运行模式
|
||||
-- 1:周期超低功耗跟踪模式
|
||||
-- 2:周期低功耗模式
|
||||
-- 4:直接进入超低功耗跟踪模式
|
||||
-- 8:自动低功耗模式,可以通过串口唤醒
|
||||
-- 9:自动超低功耗跟踪模式,需要force on来唤醒
|
||||
-- @number runTm 单位毫秒,mode为0时表示NEMA数据的上报间隔,mode为1或者2时表示运行时长,其余mode时此值无意义
|
||||
-- @number sleepTm 单位毫秒,mode为1或者2时表示运行时长,其余mode时此值无意义
|
||||
-- @return nil
|
||||
-- @usage gps.setRunMode(0,1000)
|
||||
-- @usage gps.setRunMode(1,5000,2000)
|
||||
function setRunMode(mode,runTm,sleepTm)
|
||||
local rt,st = runTm or "",sleepTm or ""
|
||||
if mode==0 and rt then
|
||||
if rt>10000 then rt=10000 end
|
||||
if rt<200 then rt=200 end
|
||||
nmeaReportStr = "$PGKC101,"..rt.."*"
|
||||
end
|
||||
|
||||
local tmpStr = "$PGKC105,"..mode..((mode==1 or mode==2) and (","..rt..","..st) or "").."*"
|
||||
if tmpStr~=runModeStr then
|
||||
runModeStr,runModeSetted = tmpStr
|
||||
end
|
||||
end
|
||||
|
||||
--- 设置NEMA语句的输出频率.
|
||||
-- @number[opt=1] rmc 单位秒,RMC语句输出频率,取值范围0到10之间的整数,0表示不输出
|
||||
-- @number[opt=1] gga 单位秒,GGA语句输出频率,取值范围0到10之间的整数,0表示不输出
|
||||
-- @number[opt=1] gsa 单位秒,GSA语句输出频率,取值范围0到10之间的整数,0表示不输出
|
||||
-- @number[opt=1] gsv 单位秒,GSV语句输出频率,取值范围0到10之间的整数,0表示不输出
|
||||
-- @number[opt=1] vtg 单位秒,VTG语句输出频率,取值范围0到10之间的整数,0表示不输出
|
||||
-- @number[opt=0] gll 单位秒,GLL语句输出频率,取值范围0到10之间的整数,0表示不输出
|
||||
-- @return nil
|
||||
-- @usage gps.setNemaReportFreq(5,0,0,0,0,0)
|
||||
function setNemaReportFreq(rmc,gga,gsa,gsv,vtg,gll)
|
||||
local tmpStr = "$PGKC242,"..(gll or 0)..","..(rmc or 1)..","..(vtg or 1)..","..(gga or 1)..","..(gsa or 1)..","..(gsv or 1)..",0,0,0,0,0,0,0,0,0,0,0,0,0".."*"
|
||||
if tmpStr~=nmeaReportFreqStr then
|
||||
nmeaReportFreqStr,nmeaReportFreqSetted = tmpStr
|
||||
end
|
||||
end
|
||||
|
||||
--- 设置GPS定位成功后经纬度的过滤时间.
|
||||
-- @number[opt=0] seconds 单位秒,GPS定位成功后,丢弃前seconds秒的位置信息
|
||||
-- @return nil
|
||||
-- @usage gps.setLocationFilter(2)
|
||||
function setLocationFilter(seconds)
|
||||
filterSeconds = seconds or 0
|
||||
end
|
||||
|
||||
function setFastFix(lat,lng,tm)
|
||||
local t = tm.year..","..tm.month..","..tm.day..","..tm.hour..","..tm.min..","..tm.sec.."*"
|
||||
log.info("gps.setFastFix",lat,lng,t)
|
||||
writeCmd("$PGKC634,"..t)
|
||||
writeCmd("$PGKC635,"..lat..","..lng..",0,"..t)
|
||||
end
|
||||
|
||||
--- 获取GPS模块是否处于开启状态
|
||||
-- @return bool result,true表示开启状态,false或者nil表示关闭状态
|
||||
-- @usage gps.isOpen()
|
||||
function isOpen()
|
||||
return openFlag
|
||||
end
|
||||
|
||||
--- 获取GPS模块是否定位成功
|
||||
-- @return bool result,true表示定位成功,false或者nil表示定位失败
|
||||
-- @usage gps.isFix()
|
||||
function isFix()
|
||||
return fixFlag
|
||||
end
|
||||
|
||||
--- 获取GPS模块是否首次定位成功过
|
||||
-- @return bool result,true表示曾经定位成功
|
||||
-- @usage gps.isOnece()
|
||||
function isOnece()
|
||||
return fixOnece
|
||||
end
|
||||
|
||||
-- 度分格式转换为度格式
|
||||
-- @string inStr 度分格式的位置
|
||||
-- @return string,度格式的位置
|
||||
-- @usage degreeMinuteToDegree("3114.50931")--->"31.2418218",31度14.50931分转换为31.2418218度
|
||||
-- @usage degreeMinuteToDegree("12128.44954")--->"121.4741590",121度28.44954分转换为121.4741590度
|
||||
local function degreeMinuteToDegree(inStr)
|
||||
local integer,fraction = smatch(inStr,"(%d+)%.(%d+)")
|
||||
if integer and fraction then
|
||||
local intLen = slen(integer)
|
||||
if intLen~=4 and intLen~=5 then log.error("gps.degreeMinuteToDegree integer error",inStr) return "" end
|
||||
if slen(fraction)<5 then fraction = fraction..srep("0",5-slen(fraction)) end
|
||||
fraction = ssub(fraction,1,5)
|
||||
local temp = tonumber(ssub(integer,intLen-1,intLen)..fraction)*10
|
||||
fraction = tostring((temp-(temp%6))/6)
|
||||
local fracLen = slen(fraction)
|
||||
if fracLen>7 then
|
||||
fraction = ssub(fraction,1,7)
|
||||
elseif fracLen<7 then
|
||||
fraction = srep("0",7-fracLen)..fraction
|
||||
end
|
||||
return ssub(integer,1,intLen-2).."."..fraction
|
||||
end
|
||||
|
||||
return ""
|
||||
end
|
||||
|
||||
--- 获取度格式的经纬度信息
|
||||
-- @string[opt=nil] typ 返回的经纬度格式,typ为"DEGREE_MINUTE"时表示返回度分格式,其余表示返回度格式
|
||||
-- @return table location
|
||||
-- 例如typ为"DEGREE_MINUTE"时返回{lngType="E",lng="12128.44954",latType="N",lat="3114.50931"}
|
||||
-- 例如typ不是"DEGREE_MINUTE"时返回{lngType="E",lng="121.123456",latType="N",lat="31.123456"}
|
||||
-- lngType:string类型,表示经度类型,取值"E","W"
|
||||
-- lng:string类型,表示度格式的经度值,无效时为""
|
||||
-- latType:string类型,表示纬度类型,取值"N","S"
|
||||
-- lat:string类型,表示度格式的纬度值,无效时为""
|
||||
-- @usage gps.getLocation()
|
||||
function getLocation(typ)
|
||||
return {
|
||||
lngType=longitudeType,
|
||||
lng=isFix() and (typ=="DEGREE_MINUTE" and longitude or degreeMinuteToDegree(longitude)) or "",
|
||||
latType=latitudeType,
|
||||
lat=isFix() and (typ=="DEGREE_MINUTE" and latitude or degreeMinuteToDegree(latitude)) or ""
|
||||
}
|
||||
end
|
||||
|
||||
function getLastLocation(typ)
|
||||
if typ=="DEGREE_MINUTE" then
|
||||
return {
|
||||
lngType=longitudeType,
|
||||
lng=longitude,
|
||||
latType=latitudeType,
|
||||
lat=latitude
|
||||
}
|
||||
else
|
||||
return (longitude and longitude~="") and degreeMinuteToDegree(longitude) or "", (latitude and latitude~="") and degreeMinuteToDegree(latitude) or ""
|
||||
end
|
||||
end
|
||||
|
||||
--- 获取海拔
|
||||
-- @return number altitude,海拔,单位米
|
||||
-- @usage gps.getAltitude()
|
||||
function getAltitude()
|
||||
return tonumber(smatch(altitude,"(%d+)") or "0")
|
||||
end
|
||||
|
||||
--- 获取速度
|
||||
-- @return number kmSpeed,第一个返回值为公里每小时的速度
|
||||
-- @return number nmSpeed,第二个返回值为海里每小时的速度
|
||||
-- @usage gps.getSpeed()
|
||||
function getSpeed()
|
||||
local integer = tonumber(smatch(speed,"(%d+)") or "0")
|
||||
return (integer*1852 - (integer*1852 %1000))/1000,integer
|
||||
end
|
||||
|
||||
--- 获取原始速度,字符串带浮点
|
||||
-- @return number speed 海里每小时的速度
|
||||
-- @usage gps.getOrgSpeed()
|
||||
function getOrgSpeed()
|
||||
return speed
|
||||
end
|
||||
|
||||
--- 获取方向角
|
||||
-- @return number course,方向角
|
||||
-- @usage gps.getCourse()
|
||||
function getCourse()
|
||||
return tonumber(smatch(course,"(%d+)") or "0")
|
||||
end
|
||||
|
||||
-- 获取所有可见卫星的最大信号强度
|
||||
-- @return number strength,最大信号强度
|
||||
-- @usage gps.getMaxSignalStrength()
|
||||
function getMaxSignalStrength()
|
||||
return maxSignalStrength
|
||||
end
|
||||
|
||||
--- 获取可见卫星的个数
|
||||
-- @return number count,可见卫星的个数
|
||||
-- @usage gps.getViewedSateCnt()
|
||||
function getViewedSateCnt()
|
||||
return tonumber(viewedGpsSateCnt)+tonumber(viewedBdSateCnt)
|
||||
end
|
||||
|
||||
--- 获取定位使用的卫星个数
|
||||
-- @return number count,定位使用的卫星个数
|
||||
-- @usage gps.getUsedSateCnt()
|
||||
function getUsedSateCnt()
|
||||
return tonumber(usedSateCnt)
|
||||
end
|
||||
|
||||
--- 获取GGA语句中度分格式的经纬度信息
|
||||
-- @return string lng,度分格式的经度值(dddmm.mmmm),西经会添加一个-前缀,无效时为"";例如"12112.3456"表示东经121度12.3456分,"-12112.3456"表示西经121度12.3456分
|
||||
-- @return string lat,度分格式的纬度值(ddmm.mmmm),南纬会添加一个-前缀,无效时为"";例如"3112.3456"表示北纬31度12.3456分,"-3112.3456"表示南纬31度12.3456分
|
||||
-- @usage gps.getGgaloc()
|
||||
function getGgaloc()
|
||||
return Ggalng or "",Ggalat or ""
|
||||
end
|
||||
|
||||
--- 获取RMC语句中的UTC时间
|
||||
-- 只有同时满足如下两个条件,返回值才有效
|
||||
-- 1、开启了GPS,并且定位成功
|
||||
-- 2、调用setParseItem接口,第一个参数设置为true
|
||||
-- @return table utcTime,UTC时间,nil表示无效,例如{year=2018,month=4,day=24,hour=11,min=52,sec=10}
|
||||
-- @usage gps.getUtcTime()
|
||||
function getUtcTime()
|
||||
return UtcTime
|
||||
end
|
||||
|
||||
--- 获取定位使用的大地高
|
||||
-- @return number sep,大地高
|
||||
-- @usage gps.getSep()
|
||||
function getSep()
|
||||
return tonumber(Sep or "0")
|
||||
end
|
||||
|
||||
--- 获取GSA语句中的可见卫星号
|
||||
-- 只有同时满足如下两个条件,返回值才有效
|
||||
-- 1、开启了GPS,并且定位成功
|
||||
-- 2、调用setParseItem接口,第三个参数设置为true
|
||||
-- @return string viewedSateId,可用卫星号,""表示无效
|
||||
-- @usage gps.getSateSn()
|
||||
function getSateSn()
|
||||
return SateSn or ""
|
||||
end
|
||||
|
||||
--- 获取GSV语句中的可见卫星的信噪比
|
||||
-- 只有同时满足如下两个条件,返回值才有效
|
||||
-- 1、开启了GPS,并且定位成功
|
||||
-- 2、调用setParseItem接口,第二个参数设置为true
|
||||
-- @return string gsv,信噪比
|
||||
-- @usage gps.getGsv()
|
||||
function getGsv()
|
||||
return Gsv or ""
|
||||
end
|
||||
|
||||
--- 设置是否需要解析的字段
|
||||
-- @bool[opt=nil] utcTime 是否解析RMC语句中的UTC时间,true表示解析,false或者nil不解析
|
||||
-- @bool[opt=nil] gsv 是否解析GSV语句,true表示解析,false或者nil不解析
|
||||
-- @bool[opt=nil] gsaId 是否解析GSA语句中的卫星ID,true表示解析,false或者nil不解析
|
||||
-- @usage gps.setParseItem(true,true,true)
|
||||
function setParseItem(utcTime,gsv,gsaId)
|
||||
psUtcTime,psGsv,psSn = utcTime,gsv,gsaId
|
||||
end
|
||||
|
||||
|
||||
function init()
|
||||
sys.subscribe("GPS_STATE",statInd)
|
||||
end
|
||||
|
||||
function unInit()
|
||||
sys.unsubscribe("GPS_STATE",statInd)
|
||||
closeAll()
|
||||
end
|
||||
|
||||
init()
|
||||
933
4G/源代码/lib/gps9701.lua
Normal file
933
4G/源代码/lib/gps9701.lua
Normal file
@@ -0,0 +1,933 @@
|
||||
--- 模块功能:GPS模块管理
|
||||
-- 注意:此功能模块中的功能接口可以分为四大类:
|
||||
-- 1、GPS开启
|
||||
-- 2、GPS关闭
|
||||
-- 3、GPS定位数据读取
|
||||
-- 4、GPS参数和功能设置
|
||||
-- 1、2、3是通用功能,除了支持合宙的Air512G、Air551G、Air552G模块,理论上也支持其他厂家的串口GPS模块
|
||||
-- 4是专用功能,仅支持合宙的Air512G、Air551G、Air552G模块
|
||||
-- @module gps9701
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2017.10.23
|
||||
require"pm"
|
||||
require"utils"
|
||||
module(..., package.seeall)
|
||||
|
||||
local smatch,sfind,slen,ssub,sbyte,sformat,srep = string.match,string.find,string.len,string.sub,string.byte,string.format,string.rep
|
||||
--GPS开启标志,true表示开启状态,false或者nil表示关闭状态
|
||||
local openFlag
|
||||
--GPS定位标志,"2D"表示2D定位,"3D"表示3D定位,其余表示未定位
|
||||
--GPS定位标志,true表示,其余表示未定位
|
||||
local fixFlag,fixOnece=nil,nil
|
||||
--GPS定位成功后,过滤掉前filterSeconds秒的经纬度信息
|
||||
--是否已经过滤完成
|
||||
local filterSeconds,filteredFlag = 0
|
||||
--从定位成功切换到定位失败,连续定位失败的次数
|
||||
local fixFailCnt = 0
|
||||
--经纬度类型和数据
|
||||
local latitudeType,latitude,longitudeType,longitude = "N","","E",""
|
||||
--海拔,速度,方向角
|
||||
local altitude,speed,course = "0","0","0"
|
||||
--参与定位的卫星个数,所有可见卫星的最大信号值,所有可见卫星的最大信号值中间缓存值
|
||||
local usedSateCnt,maxSignalStrength,maxSignalStrengthVar = "0",0,0
|
||||
--可见卫星个数
|
||||
local viewedGpsSateCnt,viewedBdSateCnt = "0","0"
|
||||
--可用卫星号,UTC时间,信噪比
|
||||
local SateSn,UtcTime,Gsv
|
||||
--大地高,度分经度,度分纬度
|
||||
local Sep,Ggalng,Ggalat
|
||||
--是否需要解析项
|
||||
local psUtcTime,psGsv,psSn
|
||||
|
||||
--GPS供电设置函数
|
||||
local powerCbFnc
|
||||
--串口配置
|
||||
uartBaudrate = 115200
|
||||
local uartID,uartDatabits,uartParity,uartStopbits = 2,8,uart.PAR_NONE,uart.STOP_1
|
||||
--搜星模式命令字符串,"$PGKC115," .. gps .. "," .. glonass .. "," .. beidou .. "," .. galieo .. "*"
|
||||
local aerialModeStr,aerialModeSetted = ""
|
||||
--运行模式命令字符串,"$PGKC105," .. mode .. "," .. rt .. "," .. st .. "*"
|
||||
local runModeStr,runModeSetted = ""
|
||||
--正常运行模式下NMEA数据上报间隔命令字符串,"$PGKC101," .. interval .. "*"
|
||||
local nmeaReportStr,nmeaReportSetted = ""
|
||||
--每种NEMA数据的输出频率命令字符串
|
||||
local nmeaReportFreqStr,nmeaReportFreqSetted = ""
|
||||
--NMEA数据处理模式,0表示仅gps.lua内部处理,1表示仅用户自己处理,2表示gps.lua和用户同时处理
|
||||
--用户处理一条NMEA数据的回调函数
|
||||
local nmeaMode,nmeaCbFnc = 0
|
||||
--NMEA数据输出间隔
|
||||
local nmeaInterval = 1000
|
||||
--运行模式
|
||||
--0,正常运行模式
|
||||
--1,周期超低功耗跟踪模式
|
||||
--2,周期低功耗模式
|
||||
--4,直接进入超低功耗跟踪模式
|
||||
--8,自动低功耗模式,可以通过串口唤醒
|
||||
--9, 自动超低功耗跟踪模式,需要force on来唤醒
|
||||
local runMode = 0
|
||||
|
||||
--gps 的串口线程是否在工作;
|
||||
local taskFlag=false
|
||||
--runMode为1或者2时,GPS运行状态和休眠状态的时长
|
||||
local runTime,sleepTime
|
||||
|
||||
--检测gps是否工作正常的定时器ID
|
||||
local workAbnormalTimerId
|
||||
|
||||
local agpsver = nil
|
||||
checkdatapack = false
|
||||
local testcoro = nil
|
||||
local i = 0
|
||||
|
||||
--[[
|
||||
函数名:getstrength
|
||||
功能 :解析GSV数据
|
||||
参数 :
|
||||
sg:NEMA中的一行GSV数据
|
||||
返回值:无
|
||||
]]
|
||||
local function getstrength(sg)
|
||||
sg = ssub(sg, 4, #sg)
|
||||
local d1,d2,curnum,lineno,total,sgv_str = sfind(sg,"GSV,(%d),(%d),(%d+),(.*)%*.*")
|
||||
|
||||
if not curnum or not lineno or not total or not sgv_str then
|
||||
return
|
||||
end
|
||||
if lineno == nil then
|
||||
maxSignalStrengthVar = 0
|
||||
maxSignalStrength = 0
|
||||
elseif tonumber(lineno) == 1 then
|
||||
maxSignalStrength = maxSignalStrengthVar
|
||||
maxSignalStrengthVar = 0
|
||||
end
|
||||
|
||||
local tmpstr,i = sgv_str
|
||||
for i=1,4 do
|
||||
local d1,d2,id,elevation,azimuth,strength = sfind(tmpstr,"(%d+),([%-]*%d*),(%d*),(%d*)")
|
||||
if id == nil then return end
|
||||
if strength == "" or not strength then
|
||||
strength = "00"
|
||||
end
|
||||
strength = tonumber(strength)
|
||||
if strength > maxSignalStrengthVar then
|
||||
maxSignalStrengthVar = strength
|
||||
end
|
||||
|
||||
local idx,cur,fnd,tmpid = 0,id..","..elevation..","..azimuth..","..strength..",",false
|
||||
for tmpid in string.gmatch(Gsv,"(%d+),%d*,%d*,%d*,") do
|
||||
idx = idx + 1
|
||||
if tmpid == id then fnd = true break end
|
||||
end
|
||||
if fnd then
|
||||
local pattern,i = ""
|
||||
for i=1,idx do
|
||||
pattern = pattern.."%d+,%d*,%d*,%d*,"
|
||||
end
|
||||
local m1,m2 = sfind(Gsv,"^"..pattern)
|
||||
if m1 and m2 then
|
||||
local front = ssub(Gsv,1,m2)
|
||||
local n1,n2 = sfind(front,"%d+,%d*,%d*,%d*,$")
|
||||
if n1 and n2 then
|
||||
Gsv = ssub(Gsv,1,n1-1)..cur..ssub(Gsv,n2+1,-1)
|
||||
end
|
||||
end
|
||||
else
|
||||
Gsv = Gsv..cur
|
||||
end
|
||||
|
||||
tmpstr = ssub(tmpstr,d2+1,-1)
|
||||
end
|
||||
end
|
||||
|
||||
local function filterTimerFnc()
|
||||
log.info("gps.filterTimerFnc end")
|
||||
filteredFlag = true
|
||||
end
|
||||
|
||||
local function stopWorkAbnormalTimer()
|
||||
if workAbnormalTimerId then
|
||||
sys.timerStop(workAbnormalTimerId)
|
||||
workAbnormalTimerId = nil
|
||||
end
|
||||
end
|
||||
|
||||
local function parseNmea(s)
|
||||
if not s or s=="" then return end
|
||||
local lat,lng,spd,cog,gpsFind,gpsTime,gpsDate,locSateCnt,hdp,latTyp,lngTyp,altd
|
||||
|
||||
local hexStr = s:toHex()
|
||||
if smatch(hexStr,"AAF00D0001009500039A00AA0F") then
|
||||
sys.publish("GPS_STATE","BINARY_CMD_ACK")
|
||||
agpsver = true
|
||||
stopWorkAbnormalTimer()
|
||||
i= i+1
|
||||
return
|
||||
elseif smatch(hexStr,"AAF00D000300") then
|
||||
sys.publish("GPS_STATE",smatch(hexStr,"^AAF00D000300FFFF") and "WRITE_EPH_END_ACK" or "WRITE_EPH_ACK")
|
||||
stopWorkAbnormalTimer()
|
||||
return
|
||||
end
|
||||
|
||||
local fixed,workAbnormal
|
||||
if smatch(s,"GGA") then
|
||||
lat,latTyp,lng,lngTyp,gpsFind,locSateCnt,hdp,altd,sep = smatch(s,"GGA,%d+%.%d+,(%d+%.%d+),([NS]),(%d+%.%d+),([EW]),(%d),(%d+),([%d%.]*),(.*),M,(.*),M")
|
||||
if (gpsFind=="1" or gpsFind=="2" or gpsFind=="4") and altd then
|
||||
--fixed = true
|
||||
altitude = altd
|
||||
latitudeType,longitudeType,latitude,longitude = latTyp,lngTyp,lat,lng
|
||||
usedSateCnt = locSateCnt
|
||||
Ggalng,Ggalat = (lngTyp=="W" and "-" or "")..lng,(latTyp=="S" and "-" or "")..lat
|
||||
Sep = sep
|
||||
else
|
||||
fixed = false
|
||||
end
|
||||
elseif smatch(s,"RMC") then
|
||||
gpsTime,gpsFind,lat,latTyp,lng,lngTyp,spd,cog,gpsDate = smatch(s,"RMC,(%d%d%d%d%d%d)%.%d+,(%w),(%d*%.*%d*),([NS]*),(%d*%.*%d*),([EW]*),(.-),(.-),(%d%d%d%d%d%d),")
|
||||
if gpsFind=="A" and cog then
|
||||
fixed = true
|
||||
latitudeType,longitudeType,latitude,longitude = latTyp,lngTyp,lat,lng
|
||||
speed = spd
|
||||
course = cog
|
||||
else
|
||||
fixed = false
|
||||
end
|
||||
if psUtcTime and gpsFind == "A" and gpsTime and gpsDate and gpsTime ~= "" and gpsDate ~= "" then
|
||||
local yy,mm,dd,h,m,s = tonumber(ssub(gpsDate,5,6)),tonumber(ssub(gpsDate,3,4)),tonumber(ssub(gpsDate,1,2)),tonumber(ssub(gpsTime,1,2)),tonumber(ssub(gpsTime,3,4)),tonumber(ssub(gpsTime,5,6))
|
||||
UtcTime = {year=2000+yy,month=mm,day=dd,hour=h,min=m,sec=s}
|
||||
end
|
||||
elseif smatch(s,"GPGSV") then
|
||||
viewedGpsSateCnt = tonumber(smatch(s,"%d+,%d+,(%d+)") or "0")
|
||||
if psGsv then getstrength(s) end
|
||||
elseif smatch(s,"BDGSV") then
|
||||
viewedBdSateCnt = tonumber(smatch(s,"%d+,%d+,(%d+)") or "0")
|
||||
if psGsv then getstrength(s) end
|
||||
elseif smatch(s,"GSA") then
|
||||
if psSn then
|
||||
local satesn = smatch(s,"GSA,%w*,%d*,(%d*,%d*,%d*,%d*,%d*,%d*,%d*,%d*,%d*,%d*,%d*,%d*,)") or ""
|
||||
if slen(satesn) > 0 and smatch(satesn,"%d+,") then
|
||||
SateSn = satesn
|
||||
end
|
||||
end
|
||||
else
|
||||
workAbnormal = true
|
||||
end
|
||||
|
||||
if not workAbnormal then
|
||||
stopWorkAbnormalTimer()
|
||||
end
|
||||
|
||||
if filterSeconds>0 and fixed and not fixFlag and not filteredFlag then
|
||||
if not sys.timerIsActive(filterTimerFnc) then
|
||||
log.info("gps.filterTimerFnc begin")
|
||||
sys.publish("GPS_STATE","LOCATION_FILTER")
|
||||
sys.timerStart(filterTimerFnc,filterSeconds*1000)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
--定位成功
|
||||
if fixed then
|
||||
if not fixFlag then
|
||||
fixFlag,filteredFlag = true,true
|
||||
fixOnece=true
|
||||
fixFailCnt = 0
|
||||
sys.publish("GPS_STATE","LOCATION_SUCCESS")
|
||||
end
|
||||
elseif fixed==false then
|
||||
if fixFlag then
|
||||
fixFailCnt = fixFailCnt+1
|
||||
if fixFailCnt>=20 then
|
||||
fixFlag,filteredFlag = false
|
||||
sys.timerStop(filterTimerFnc)
|
||||
sys.publish("GPS_STATE","LOCATION_FAIL")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function taskRead()
|
||||
local cacheData = ""
|
||||
local co = coroutine.running()
|
||||
while true do
|
||||
local s =""
|
||||
if openFlag then
|
||||
s= uart.read(uartID, "*l")
|
||||
end
|
||||
if i==2 then
|
||||
agpsver =false
|
||||
i = 0
|
||||
end
|
||||
if agpsver then
|
||||
|
||||
if s=="" then
|
||||
checkdatapack = true
|
||||
else
|
||||
sys.publish("NOEMAL_RECE")
|
||||
end
|
||||
end
|
||||
if s == "" then
|
||||
uart.on(uartID,"receive",function() coroutine.resume(co) end)
|
||||
coroutine.yield()
|
||||
uart.on(uartID,"receive")
|
||||
else
|
||||
cacheData = cacheData..s
|
||||
if cacheData:toHex()=="AAF00D0001009500039A00AA0F" or smatch(cacheData:toHex(),"AAF00D000300") then
|
||||
cacheData = cacheData.."\r\n"
|
||||
end
|
||||
local d1,d2,nemaStr = sfind(cacheData,"\r\n")--截取串口收到的数据
|
||||
while d1 do
|
||||
writePendingCmds()
|
||||
nemaStr = ssub(cacheData,1,d2)
|
||||
cacheData = ssub(cacheData,d2+1,-1)
|
||||
|
||||
if nmeaMode==0 or nmeaMode==2 then
|
||||
--解析一行NEMA数据
|
||||
parseNmea(nemaStr)
|
||||
end
|
||||
if (nmeaMode==1 or nmeaMode==2) and nmeaCbFnc then
|
||||
nmeaCbFnc(nemaStr)
|
||||
end
|
||||
d1,d2 = sfind(cacheData,"\r\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- GPS串口写命令操作
|
||||
-- @string cmd,GPS指令(cmd格式:"$PGKC149,1,115200*"或者"$PGKC149,1,115200*XX\r\n")
|
||||
-- @bool isFull,cmd是否为完整的指令格式,包括校验和以及\r\n;true表示完整,false或者nil为不完整
|
||||
-- @return nil
|
||||
-- @usage gps.writeCmd(cmd)
|
||||
function writeCmd(cmd,isFull)
|
||||
local tmp = cmd
|
||||
-- log.info("星历数据5",tmp:toHex())
|
||||
if not isFull then
|
||||
tmp = 0
|
||||
for i=2,cmd:len()-1 do
|
||||
tmp = bit.bxor(tmp,cmd:byte(i))
|
||||
end
|
||||
tmp = cmd..(string.format("%02X",tmp)):upper().."\r\n"
|
||||
end
|
||||
-- sys.timerStart(function() uart.write(uartID,tmp) end, 100)
|
||||
uart.write(uartID,tmp)
|
||||
-- log.info("gps.writecmd",tmp)
|
||||
log.info("gps.writecmd:toHex",uartID, tmp:toHex())
|
||||
end
|
||||
|
||||
function writePendingCmds()
|
||||
if not aerialModeSetted and aerialModeStr~="" then writeCmd(aerialModeStr) aerialModeSetted=true end
|
||||
if not runModeSetted and runModeStr~="" then writeCmd(runModeStr) runModeSetted=true end
|
||||
if not nmeaReportSetted and nmeaReportStr~="" then writeCmd(nmeaReportStr) nmeaReportSetted=true end
|
||||
if not nmeaReportFreqSetted and nmeaReportFreqStr~="" then writeCmd(nmeaReportFreqStr) nmeaReportFreqSetted=true end
|
||||
end
|
||||
|
||||
local function _open()
|
||||
if openFlag then return end--gps开启状态
|
||||
pm.wake("gps.lua")
|
||||
uart.setup(uartID,uartBaudrate,uartDatabits,uartParity,uartStopbits)
|
||||
if not taskFlag then --串口线程
|
||||
taskFlag =true
|
||||
sys.taskInit(taskRead)
|
||||
end
|
||||
if powerCbFnc then
|
||||
powerCbFnc(true)
|
||||
else
|
||||
pmd.ldoset(15, pmd.LDO_VMMC)
|
||||
rtos.sys32k_clk_out(1)
|
||||
end
|
||||
openFlag = true
|
||||
workAbnormalTimerId = sys.timerStart(sys.publish,8000,"GPS_WORK_ABNORMAL_IND")
|
||||
sys.publish("GPS_STATE","OPEN")
|
||||
fixFlag,filteredFlag = false
|
||||
Ggalng,Ggalat,Gsv,Sep = "","",""
|
||||
log.info("gps._open")
|
||||
end
|
||||
|
||||
|
||||
|
||||
local function _close()
|
||||
if not openFlag then return end
|
||||
if powerCbFnc then
|
||||
powerCbFnc(false)
|
||||
else
|
||||
pmd.ldoset(0,pmd.LDO_VMMC)
|
||||
rtos.sys32k_clk_out(0)
|
||||
end
|
||||
uart.close(uartID)
|
||||
pm.sleep("gps.lua")
|
||||
openFlag = false
|
||||
sys.publish("GPS_STATE","CLOSE",fixFlag)
|
||||
stopWorkAbnormalTimer()
|
||||
fixFlag,filteredFlag = false
|
||||
sys.timerStop(filterTimerFnc)
|
||||
Ggalng,Ggalat,Gsv,Sep = "","",""
|
||||
aerialModeSetted,runModeSetted,nmeaReportSetted,nmeaReportFreqSetted = nil
|
||||
log.info("gps._close")
|
||||
end
|
||||
|
||||
|
||||
--- GPS应用模式1.
|
||||
--
|
||||
-- 打开GPS后,GPS定位成功时,如果有回调函数,会调用回调函数
|
||||
--
|
||||
-- 使用此应用模式调用gps9701.open打开的“GPS应用”,必须主动调用gps9701.close或者gps9701.closeAll才能关闭此“GPS应用”,主动关闭时,即使有回调函数,也不会调用回调函数
|
||||
DEFAULT = 1
|
||||
--- GPS应用模式2.
|
||||
--
|
||||
-- 打开GPS后,如果在GPS开启最大时长到达时,没有定位成功,如果有回调函数,会调用回调函数,然后自动关闭此“GPS应用”
|
||||
--
|
||||
-- 打开GPS后,如果在GPS开启最大时长内,定位成功,如果有回调函数,会调用回调函数,然后自动关闭此“GPS应用”
|
||||
--
|
||||
-- 打开GPS后,在自动关闭此“GPS应用”前,可以调用gps9701.close或者gps9701.closeAll主动关闭此“GPS应用”,主动关闭时,即使有回调函数,也不会调用回调函数
|
||||
TIMERORSUC = 2
|
||||
--- GPS应用模式3.
|
||||
--
|
||||
-- 打开GPS后,在GPS开启最大时长时间到达时,无论是否定位成功,如果有回调函数,会调用回调函数,然后自动关闭此“GPS应用”
|
||||
--
|
||||
-- 打开GPS后,在自动关闭此“GPS应用”前,可以调用gps9701.close或者gps9701.closeAll主动关闭此“GPS应用”,主动关闭时,即使有回调函数,也不会调用回调函数
|
||||
TIMER = 3
|
||||
|
||||
--“GPS应用”表
|
||||
local tList = {}
|
||||
|
||||
--[[
|
||||
函数名:delItem
|
||||
功能 :从“GPS应用”表中删除一项“GPS应用”,并不是真正的删除,只是设置一个无效标志
|
||||
参数 :
|
||||
mode:GPS应用模式
|
||||
para:
|
||||
para.tag:“GPS应用”标记
|
||||
para.val:GPS开启最大时长
|
||||
para.cb:回调函数
|
||||
返回值:无
|
||||
]]
|
||||
local function delItem(mode,para)
|
||||
for i=1,#tList do
|
||||
--标志有效 并且 GPS应用模式相同 并且 “GPS应用”标记相同
|
||||
if tList[i].flag and tList[i].mode==mode and tList[i].para.tag==para.tag then
|
||||
--设置无效标志
|
||||
tList[i].flag,tList[i].delay = false
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:addItem
|
||||
功能 :新增一项“GPS应用”到“GPS应用”表
|
||||
参数 :
|
||||
mode:GPS应用模式
|
||||
para:
|
||||
para.tag:“GPS应用”标记
|
||||
para.val:GPS开启最大时长
|
||||
para.cb:回调函数
|
||||
返回值:无
|
||||
]]
|
||||
local function addItem(mode,para)
|
||||
--删除相同的“GPS应用”
|
||||
delItem(mode,para)
|
||||
local item,i,fnd = {flag=true, mode=mode, para=para}
|
||||
--如果是TIMERORSUC或者TIMER模式,初始化GPS工作剩余时间
|
||||
if mode==TIMERORSUC or mode==TIMER then item.para.remain = para.val end
|
||||
for i=1,#tList do
|
||||
--如果存在无效的“GPS应用”项,直接使用此位置
|
||||
if not tList[i].flag then
|
||||
tList[i] = item
|
||||
fnd = true
|
||||
break
|
||||
end
|
||||
end
|
||||
--新增一项
|
||||
if not fnd then table.insert(tList,item) end
|
||||
end
|
||||
|
||||
local function existTimerItem()
|
||||
for i=1,#tList do
|
||||
if tList[i].flag and (tList[i].mode==TIMERORSUC or tList[i].mode==TIMER or tList[i].para.delay) then return true end
|
||||
end
|
||||
end
|
||||
|
||||
local function timerFnc()
|
||||
for i=1,#tList do
|
||||
if tList[i].flag then
|
||||
log.info("gps.timerFnc@"..i,tList[i].mode,tList[i].para.tag,tList[i].para.val,tList[i].para.remain,tList[i].para.delay)
|
||||
local rmn,dly,md,cb = tList[i].para.remain,tList[i].para.delay,tList[i].mode,tList[i].para.cb
|
||||
|
||||
if rmn and rmn>0 then
|
||||
tList[i].para.remain = rmn-1
|
||||
end
|
||||
if dly and dly>0 then
|
||||
tList[i].para.delay = dly-1
|
||||
end
|
||||
rmn = tList[i].para.remain
|
||||
|
||||
if isFix() and md==TIMER and rmn==0 and not tList[i].para.delay then
|
||||
tList[i].para.delay = 1
|
||||
end
|
||||
|
||||
dly = tList[i].para.delay
|
||||
if isFix() then
|
||||
if dly and dly==0 then
|
||||
if cb then cb(tList[i].para.tag) end
|
||||
if md == DEFAULT then
|
||||
tList[i].para.delay = nil
|
||||
else
|
||||
close(md,tList[i].para)
|
||||
end
|
||||
end
|
||||
else
|
||||
if rmn and rmn == 0 then
|
||||
if cb then cb(tList[i].para.tag) end
|
||||
close(md,tList[i].para)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if existTimerItem() then sys.timerStart(timerFnc,1000) end
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:statInd
|
||||
功能 :处理GPS定位成功的消息
|
||||
参数 :
|
||||
evt:GPS消息类型
|
||||
返回值:无
|
||||
]]
|
||||
local function statInd(evt)
|
||||
--定位成功的消息
|
||||
if evt == "LOCATION_SUCCESS" then
|
||||
for i=1,#tList do
|
||||
log.info("gps.statInd@"..i,tList[i].flag,tList[i].mode,tList[i].para.tag,tList[i].para.val,tList[i].para.remain,tList[i].para.delay,tList[i].para.cb)
|
||||
if tList[i].flag then
|
||||
if tList[i].mode ~= TIMER then
|
||||
tList[i].para.delay = 1
|
||||
if tList[i].mode == DEFAULT then
|
||||
if existTimerItem() then sys.timerStart(timerFnc,1000) end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- 打开一个“GPS应用”
|
||||
-- “GPS应用”:指的是使用GPS功能的一个应用
|
||||
-- 例如,假设有如下3种需求,要打开GPS,则一共有3个“GPS应用”:
|
||||
-- “GPS应用1”:每隔1分钟打开一次GPS
|
||||
-- “GPS应用2”:设备发生震动时打开GPS
|
||||
-- “GPS应用3”:收到一条特殊短信时打开GPS
|
||||
-- 只有所有“GPS应用”都关闭了,才会去真正关闭GPS
|
||||
-- 每个“GPS应用”打开或者关闭GPS时,最多有4个参数,其中 GPS应用模式和GPS应用标记 共同决定了一个唯一的“GPS应用”:
|
||||
-- 1、GPS应用模式(必选)
|
||||
-- 2、GPS应用标记(必选)
|
||||
-- 3、GPS开启最大时长[可选]
|
||||
-- 4、回调函数[可选]
|
||||
-- 例如gps9701.open(gps9701.TIMERORSUC,{tag="TEST",val=120,cb=testGpsCb})
|
||||
-- gps9701.TIMERORSUC为GPS应用模式,"TEST"为GPS应用标记,120秒为GPS开启最大时长,testGpsCb为回调函数
|
||||
-- @number mode GPS应用模式,支持gps9701.DEFAULT,gps9701.TIMERORSUC,gps9701.TIMER三种
|
||||
-- @param para table类型,GPS应用参数
|
||||
-- para.tag:string类型,GPS应用标记
|
||||
-- para.val:number类型,GPS应用开启最大时长,mode参数为gps9701.TIMERORSUC或者gps9701.TIMER时,此值才有意义
|
||||
-- para.cb:GPS应用结束时的回调函数,回调函数的调用形式为para.cb(para.tag)
|
||||
-- @return nil
|
||||
-- @usage gps9701.open(gps9701.DEFAULT,{tag="TEST1",cb=test1Cb})
|
||||
-- @usage gps9701.open(gps9701.TIMERORSUC,{tag="TEST2",val=60,cb=test2Cb})
|
||||
-- @usage gps9701.open(gps9701.TIMER,{tag="TEST3",val=120,cb=test3Cb})
|
||||
-- @see DEFAULT,TIMERORSUC,TIMER
|
||||
function open(mode,para)
|
||||
assert((para and type(para) == "table" and para.tag and type(para.tag) == "string"),"gps.open para invalid")
|
||||
log.info("gps.open",mode,para.tag,para.val,para.cb)
|
||||
--如果GPS定位成功
|
||||
if isFix() then
|
||||
if mode~=TIMER then
|
||||
--执行回调函数
|
||||
if para.cb then para.cb(para.tag) end
|
||||
if mode==TIMERORSUC then return end
|
||||
end
|
||||
end
|
||||
addItem(mode,para)--gps应用
|
||||
--真正去打开GPS
|
||||
_open()
|
||||
--启动1秒的定时器
|
||||
if existTimerItem() and not sys.timerIsActive(timerFnc) then
|
||||
sys.timerStart(timerFnc,1000)
|
||||
end
|
||||
end
|
||||
|
||||
--- 关闭一个“GPS应用”
|
||||
-- 只是从逻辑上关闭一个GPS应用,并不一定真正关闭GPS,是有所有的GPS应用都处于关闭状态,才会去真正关闭GPS
|
||||
-- @number mode GPS应用模式,支持gps9701.DEFAULT,gps9701.TIMERORSUC,gps9701.TIMER三种
|
||||
-- @param para table类型,GPS应用参数
|
||||
-- para.tag:string类型,GPS应用标记
|
||||
-- para.val:number类型,GPS应用开启最大时长,mode参数为gps9701.TIMERORSUC或者gps9701.TIMER时,此值才有意义;使用close接口时,不需要传入此参数
|
||||
-- para.cb:GPS应用结束时的回调函数,回调函数的调用形式为para.cb(para.tag);使用close接口时,不需要传入此参数
|
||||
-- @return nil
|
||||
-- @usage GPS应用模式和GPS应用标记唯一确定一个“GPS应用”,调用本接口关闭时,mode和para.tag要和gps9701.open打开一个“GPS应用”时传入的mode和para.tag保持一致
|
||||
-- @usage gps9701.close(gps9701.DEFAULT,{tag="TEST1"})
|
||||
-- @usage gps9701.close(gps9701.TIMERORSUC,{tag="TEST2"})
|
||||
-- @usage gps9701.close(gps9701.TIMER,{tag="TEST3"})
|
||||
-- @see open,DEFAULT,TIMERORSUC,TIMER
|
||||
function close(mode,para)
|
||||
assert((para and type(para)=="table" and para.tag and type(para.tag)=="string"),"gps.close para invalid")
|
||||
log.info("gps.close",mode,para.tag,para.val,para.cb)
|
||||
--删除此“GPS应用”
|
||||
delItem(mode,para)
|
||||
local valid,i
|
||||
for i=1,#tList do
|
||||
if tList[i].flag then
|
||||
valid = true
|
||||
end
|
||||
end
|
||||
--如果没有一个“GPS应用”有效,则关闭GPS
|
||||
if not valid then _close() end
|
||||
end
|
||||
|
||||
--- 关闭所有“GPS应用”
|
||||
-- @return nil
|
||||
-- @usage gps9701.closeAll()
|
||||
-- @see open,DEFAULT,TIMERORSUC,TIMER
|
||||
function closeAll()
|
||||
for i=1,#tList do
|
||||
if tList[i].flag and tList[i].para.cb then tList[i].para.cb(tList[i].para.tag) end
|
||||
close(tList[i].mode,tList[i].para)
|
||||
end
|
||||
end
|
||||
|
||||
--- 判断一个“GPS应用”是否处于激活状态
|
||||
-- @number mode GPS应用模式,支持gps9701.DEFAULT,gps9701.TIMERORSUC,gps9701.TIMER三种
|
||||
-- @param para table类型,GPS应用参数
|
||||
-- para.tag:string类型,GPS应用标记
|
||||
-- para.val:number类型,GPS应用开启最大时长,mode参数为gps9701.TIMERORSUC或者gps9701.TIMER时,此值才有意义;使用isActive接口时,不需要传入此参数
|
||||
-- para.cb:GPS应用结束时的回调函数,回调函数的调用形式为para.cb(para.tag);使用isActive接口时,不需要传入此参数
|
||||
-- @return bool result,处于激活状态返回true,否则返回nil
|
||||
-- @usage GPS应用模式和GPS应用标记唯一确定一个“GPS应用”,调用本接口查询状态时,mode和para.tag要和gps9701.open打开一个“GPS应用”时传入的mode和para.tag保持一致
|
||||
-- @usage gps9701.isActive(gps9701.DEFAULT,{tag="TEST1"})
|
||||
-- @usage gps9701.isActive(gps9701.TIMERORSUC,{tag="TEST2"})
|
||||
-- @usage gps9701.isActive(gps9701.TIMER,{tag="TEST3"})
|
||||
-- @see open,DEFAULT,TIMERORSUC,TIMER
|
||||
function isActive(mode,para)
|
||||
assert((para and type(para)=="table" and para.tag and type(para.tag)=="string"),"gps.isActive para invalid")
|
||||
for i=1,#tList do
|
||||
if tList[i].flag and tList[i].mode==mode and tList[i].para.tag==para.tag then return true end
|
||||
end
|
||||
end
|
||||
|
||||
--- 设置GPS模块供电控制的回调函数
|
||||
-- 如果使用的是Air800,或者供电控制使用的是LDO_VCAM,则打开GPS应用前不需要调用此接口进行设置
|
||||
-- 否则在调用gps9701.open前,使用此接口,传入自定义的供电控制函数cbFnc,GPS开启时,gps9701.lua自动执行cbFnc(true),GPS关闭时,gps9701.lua自动执行cbFnc(false)
|
||||
-- @param cbFnc function类型,用户自定义的GPS供电控制函数
|
||||
-- @return nil
|
||||
-- @usage gps9701.setPowerCbFnc(cbFnc)
|
||||
function setPowerCbFnc(cbFnc)
|
||||
powerCbFnc = cbFnc
|
||||
end
|
||||
|
||||
--- 设置GPS模块和GSM模块之间数据通信的串口参数
|
||||
-- 如果使用的是Air800,或者使用的UART2(波特率115200,数据位8,无检验位,停止位1),则打开GPS应用前不需要调用此接口进行设置
|
||||
-- 否则在调用gps9701.open前,使用此接口,传入UART参数
|
||||
-- @number id UART ID,支持1和2,1表示UART1,2表示UART2
|
||||
-- @number baudrate 波特率,支持1200,2400,4800,9600,10400,14400,19200,28800,38400,57600,76800,115200,230400,460800,576000,921600,1152000,4000000
|
||||
-- @number databits 数据位,支持8
|
||||
-- @number parity 校验位,支持uart.PAR_NONE,uart.PAR_EVEN,uart.PAR_ODD
|
||||
-- @number stopbits 停止位,支持uart.STOP_1,uart.STOP_2
|
||||
-- @return nil
|
||||
-- @usage gps9701.setUart(2,115200,8,uart.PAR_NONE,uart.STOP_1)
|
||||
function setUart(id,baudrate,databits,parity,stopbits)
|
||||
uartID,uartBaudrate,uartDatabits,uartParity,uartStopbits = id,baudrate,databits,parity,stopbits
|
||||
end
|
||||
|
||||
--- 设置GPS模块搜星模式.
|
||||
-- 如果使用的是Air800或者Air530,不调用此接口配置,则默认同时开启GPS和北斗定位
|
||||
-- @number gps GPS定位系统,1是打开,0是关闭
|
||||
-- @number beidou 中国北斗定位系统,1是打开,0是关闭
|
||||
-- @number glonass 俄罗斯Glonass定位系统,1是打开,0是关闭
|
||||
-- @number galieo 欧盟伽利略定位系统,1是打开,0是关闭
|
||||
-- @return nil
|
||||
-- @usage gps9701.setAeriaMode(1,1,0,0)
|
||||
function setAerialMode(gps,beidou,glonass,galieo)
|
||||
local gps = gps or 0
|
||||
local glonass = glonass or 0
|
||||
local beidou = beidou or 0
|
||||
local galieo = galieo or 0
|
||||
if gps+glonass+beidou+galieo == 0 then gps=1 beidou=1 end
|
||||
if gps==1 then gps=1 end
|
||||
if glonass==1 then glonass=4 end
|
||||
if beidou==1 then beidou= 8 end
|
||||
if galieo==1 then galieo= 2 end
|
||||
local searchstar=gps+glonass+beidou+galieo
|
||||
searchstar = string.format("%X",searchstar)
|
||||
log.info("搜星",searchstar)
|
||||
--local tmpStr = "$PGKC115,"..gps..","..glonass..","..beidou..","..galieo.."*"
|
||||
local tmpStr = "$PGKC121,0,"..searchstar..",0*"
|
||||
if tmpStr~=aerialModeStr then
|
||||
aerialModeStr,aerialModeSetted = tmpStr
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- 设置NMEA数据处理模式.
|
||||
-- 如果不调用此接口配置,则默认仅gps.lua内部处理NMEA数据
|
||||
-- @number mode NMEA数据处理模式,0表示仅gps.lua内部处理,1表示仅用户自己处理,2表示gps.lua和用户同时处理
|
||||
-- @param cbFnc function类型,用户处理一条NMEA数据的回调函数,mode为1和2时,此值才有意义
|
||||
-- @return nil
|
||||
-- @usage gps9701.setNmeaMode(0)
|
||||
-- @usage gps9701.setNmeaMode(1,cbFnc)
|
||||
-- @usage gps9701.setNmeaMode(2,cbFnc)
|
||||
function setNmeaMode(mode,cbFnc)
|
||||
nmeaMode,nmeaCbFnc = mode,cbFnc
|
||||
end
|
||||
|
||||
--- 设置GPS模块的运行模式.
|
||||
-- 如果不调用此接口配置,则默认为正常运行模式
|
||||
-- @number mode 运行模式
|
||||
-- 0:正常运行模式
|
||||
-- 1:周期超低功耗跟踪模式
|
||||
-- 4:直接进入超低功耗跟踪模式
|
||||
-- 8:低功耗模式,可以通过串口发送命令唤醒
|
||||
-- @number runTm 单位毫秒,mode为0时表示NEMA数据的上报间隔,mode为1时表示运行时长,其余mode时此值无意义
|
||||
-- @number sleepTm 单位毫秒,mode为1时表示运行时长,其余mode时此值无意义
|
||||
-- @return nil
|
||||
-- @usage gps9701.setRunMode(0,1000)
|
||||
-- @usage gps9701.setRunMode(1,5000,2000)
|
||||
function setRunMode(mode,runTm,sleepTm)
|
||||
local rt,st = runTm or "",sleepTm or ""
|
||||
if mode==0 and rt then
|
||||
if rt>10000 then rt=10000 end
|
||||
if rt<100 then rt=100 end
|
||||
nmeaReportStr = "$PGKC101,"..rt.."*"
|
||||
end
|
||||
|
||||
local tmpStr = "$PGKC105,"..mode..((mode==1) and (","..rt..","..st) or "").."*"
|
||||
if tmpStr~=runModeStr then
|
||||
runModeStr,runModeSetted = tmpStr
|
||||
end
|
||||
end
|
||||
|
||||
--- 设置NEMA语句的输出频率.
|
||||
-- @number[opt=1] rmc 单位秒,RMC语句输出频率,取值范围0到10之间的整数,0表示不输出
|
||||
-- @number[opt=1] gga 单位秒,GGA语句输出频率,取值范围0到10之间的整数,0表示不输出
|
||||
-- @number[opt=1] gsa 单位秒,GSA语句输出频率,取值范围0到10之间的整数,0表示不输出
|
||||
-- @number[opt=1] gsv 单位秒,GSV语句输出频率,取值范围0到10之间的整数,0表示不输出
|
||||
-- @number[opt=1] vtg 单位秒,VTG语句输出频率,取值范围0到10之间的整数,0表示不输出
|
||||
-- @number[opt=0] gll 单位秒,GLL语句输出频率,取值范围0到10之间的整数,0表示不输出
|
||||
-- @return nil
|
||||
-- @usage gps9701.setNemaReportFreq(5,0,0,0,0,0)
|
||||
function setNemaReportFreq(rmc,gga,gsa,gsv,vtg,gll)
|
||||
local tmpStr = "$PGKC242,"..(gll or 0)..","..(rmc or 1)..","..(vtg or 1)..","..(gga or 1)..","..(gsa or 1)..","..(gsv or 1)..",0,0,0,0,0,0,0,0,0,0,0,0,0".."*"
|
||||
if tmpStr~=nmeaReportFreqStr then
|
||||
nmeaReportFreqStr,nmeaReportFreqSetted = tmpStr
|
||||
end
|
||||
end
|
||||
|
||||
--- 设置GPS定位成功后经纬度的过滤时间.
|
||||
-- @number[opt=0] seconds 单位秒,GPS定位成功后,丢弃前seconds秒的位置信息
|
||||
-- @return nil
|
||||
-- @usage gps9701.setLocationFilter(2)
|
||||
function setLocationFilter(seconds)
|
||||
filterSeconds = seconds or 0
|
||||
end
|
||||
|
||||
function setFastFix(lat,lng,tm)
|
||||
local t = tm.year..","..tm.month..","..tm.day..","..tm.hour..","..tm.min..","..tm.sec.."*"
|
||||
log.info("gps.setFastFix",lat,lng,t)
|
||||
-- writeCmd("$PGKC634,"..t)
|
||||
writeCmd("$PGKC639,"..lat..","..lng..",0,"..t)
|
||||
end
|
||||
|
||||
--- 获取GPS模块是否处于开启状态
|
||||
-- @return bool result,true表示开启状态,false或者nil表示关闭状态
|
||||
-- @usage gps9701.isOpen()
|
||||
function isOpen()
|
||||
return openFlag
|
||||
end
|
||||
|
||||
--- 获取GPS模块是否定位成功
|
||||
-- @return bool result,true表示定位成功,false或者nil表示定位失败
|
||||
-- @usage gps9701.isFix()
|
||||
function isFix()
|
||||
return fixFlag
|
||||
end
|
||||
|
||||
--- 获取GPS模块是否首次定位成功过
|
||||
-- @return bool result,true表示曾经定位成功
|
||||
-- @usage gps9701.isOnece()
|
||||
function isOnece()
|
||||
return fixOnece
|
||||
end
|
||||
|
||||
-- 度分格式转换为度格式
|
||||
-- @string inStr 度分格式的位置
|
||||
-- @return string,度格式的位置
|
||||
-- @usage degreeMinuteToDegree("3114.50931")--->"31.2418218",31度14.50931分转换为31.2418218度
|
||||
-- @usage degreeMinuteToDegree("12128.44954")--->"121.4741590",121度28.44954分转换为121.4741590度
|
||||
local function degreeMinuteToDegree(inStr)
|
||||
local integer,fraction = smatch(inStr,"(%d+)%.(%d+)")
|
||||
if integer and fraction then
|
||||
local intLen = slen(integer)
|
||||
if intLen~=4 and intLen~=5 then log.error("gps.degreeMinuteToDegree integer error",inStr) return "" end
|
||||
if slen(fraction)<5 then fraction = fraction..srep("0",5-slen(fraction)) end
|
||||
fraction = ssub(fraction,1,5)
|
||||
local temp = tonumber(ssub(integer,intLen-1,intLen)..fraction)*10
|
||||
fraction = tostring((temp-(temp%6))/6)
|
||||
local fracLen = slen(fraction)
|
||||
if fracLen>7 then
|
||||
fraction = ssub(fraction,1,7)
|
||||
elseif fracLen<7 then
|
||||
fraction = srep("0",7-fracLen)..fraction
|
||||
end
|
||||
return ssub(integer,1,intLen-2).."."..fraction
|
||||
end
|
||||
|
||||
return ""
|
||||
end
|
||||
|
||||
--- 获取度格式的经纬度信息
|
||||
-- @string[opt=nil] typ 返回的经纬度格式,typ为"DEGREE_MINUTE"时表示返回度分格式,其余表示返回度格式
|
||||
-- @return table location
|
||||
-- 例如typ为"DEGREE_MINUTE"时返回{lngType="E",lng="12128.44954",latType="N",lat="3114.50931"}
|
||||
-- 例如typ不是"DEGREE_MINUTE"时返回{lngType="E",lng="121.123456",latType="N",lat="31.123456"}
|
||||
-- lngType:string类型,表示经度类型,取值"E","W"
|
||||
-- lng:string类型,表示度格式的经度值,无效时为""
|
||||
-- latType:string类型,表示纬度类型,取值"N","S"
|
||||
-- lat:string类型,表示度格式的纬度值,无效时为""
|
||||
-- @usage gps9701.getLocation()
|
||||
function getLocation(typ)
|
||||
return {
|
||||
lngType=longitudeType,
|
||||
lng=isFix() and (typ=="DEGREE_MINUTE" and longitude or degreeMinuteToDegree(longitude)) or "",
|
||||
latType=latitudeType,
|
||||
lat=isFix() and (typ=="DEGREE_MINUTE" and latitude or degreeMinuteToDegree(latitude)) or ""
|
||||
}
|
||||
end
|
||||
|
||||
function getLastLocation(typ)
|
||||
if typ=="DEGREE_MINUTE" then
|
||||
return {
|
||||
lngType=longitudeType,
|
||||
lng=longitude,
|
||||
latType=latitudeType,
|
||||
lat=latitude
|
||||
}
|
||||
else
|
||||
return (longitude and longitude~="") and degreeMinuteToDegree(longitude) or "", (latitude and latitude~="") and degreeMinuteToDegree(latitude) or ""
|
||||
end
|
||||
end
|
||||
|
||||
--- 获取海拔
|
||||
-- @return number altitude,海拔,单位米
|
||||
-- @usage gps9701.getAltitude()
|
||||
function getAltitude()
|
||||
return tonumber(smatch(altitude,"(%d+)") or "0")
|
||||
end
|
||||
|
||||
--- 获取速度
|
||||
-- @return number kmSpeed,第一个返回值为公里每小时的速度
|
||||
-- @return number nmSpeed,第二个返回值为海里每小时的速度
|
||||
-- @usage gps9701.getSpeed()
|
||||
function getSpeed()
|
||||
local integer = tonumber(smatch(speed,"(%d+)") or "0")
|
||||
return (integer*1852 - (integer*1852 %1000))/1000,integer
|
||||
end
|
||||
|
||||
--- 获取原始速度,字符串带浮点
|
||||
-- @return number speed 海里每小时的速度
|
||||
-- @usage gps9701.getOrgSpeed()
|
||||
function getOrgSpeed()
|
||||
return speed
|
||||
end
|
||||
|
||||
--- 获取方向角
|
||||
-- @return number course,方向角
|
||||
-- @usage gps9701.getCourse()
|
||||
function getCourse()
|
||||
return tonumber(smatch(course,"(%d+)") or "0")
|
||||
end
|
||||
|
||||
-- 获取所有可见卫星的最大信号强度
|
||||
-- @return number strength,最大信号强度
|
||||
-- @usage gps9701.getMaxSignalStrength()
|
||||
function getMaxSignalStrength()
|
||||
return maxSignalStrength
|
||||
end
|
||||
|
||||
--- 获取可见卫星的个数
|
||||
-- @return number count,可见卫星的个数
|
||||
-- @usage gps9701.getViewedSateCnt()
|
||||
function getViewedSateCnt()
|
||||
return tonumber(viewedGpsSateCnt)+tonumber(viewedBdSateCnt)
|
||||
end
|
||||
|
||||
--- 获取定位使用的卫星个数
|
||||
-- @return number count,定位使用的卫星个数
|
||||
-- @usage gps9701.getUsedSateCnt()
|
||||
function getUsedSateCnt()
|
||||
return tonumber(usedSateCnt)
|
||||
end
|
||||
|
||||
--- 获取GGA语句中度分格式的经纬度信息
|
||||
-- @return string lng,度分格式的经度值(dddmm.mmmm),西经会添加一个-前缀,无效时为"";例如"12112.3456"表示东经121度12.3456分,"-12112.3456"表示西经121度12.3456分
|
||||
-- @return string lat,度分格式的纬度值(ddmm.mmmm),南纬会添加一个-前缀,无效时为"";例如"3112.3456"表示北纬31度12.3456分,"-3112.3456"表示南纬31度12.3456分
|
||||
-- @usage gps9701.getGgaloc()
|
||||
function getGgaloc()
|
||||
return Ggalng or "",Ggalat or ""
|
||||
end
|
||||
|
||||
--- 获取RMC语句中的UTC时间
|
||||
-- 只有同时满足如下两个条件,返回值才有效
|
||||
-- 1、开启了GPS,并且定位成功
|
||||
-- 2、调用setParseItem接口,第一个参数设置为true
|
||||
-- @return table utcTime,UTC时间,nil表示无效,例如{year=2018,month=4,day=24,hour=11,min=52,sec=10}
|
||||
-- @usage gps9701.getUtcTime()
|
||||
function getUtcTime()
|
||||
return UtcTime
|
||||
end
|
||||
|
||||
--- 获取定位使用的大地高
|
||||
-- @return number sep,大地高
|
||||
-- @usage gps9701.getSep()
|
||||
function getSep()
|
||||
return tonumber(Sep or "0")
|
||||
end
|
||||
|
||||
--- 获取GSA语句中的可见卫星号
|
||||
-- 只有同时满足如下两个条件,返回值才有效
|
||||
-- 1、开启了GPS,并且定位成功
|
||||
-- 2、调用setParseItem接口,第三个参数设置为true
|
||||
-- @return string viewedSateId,可用卫星号,""表示无效
|
||||
-- @usage gps9701.getSateSn()
|
||||
function getSateSn()
|
||||
return SateSn or ""
|
||||
end
|
||||
|
||||
--- 获取GSV语句中的可见卫星的信噪比
|
||||
-- 只有同时满足如下两个条件,返回值才有效
|
||||
-- 1、开启了GPS,并且定位成功
|
||||
-- 2、调用setParseItem接口,第二个参数设置为true
|
||||
-- @return string gsv,信噪比
|
||||
-- @usage gps9701.getGsv()
|
||||
function getGsv()
|
||||
return Gsv or ""
|
||||
end
|
||||
|
||||
--- 设置是否需要解析的字段
|
||||
-- @bool[opt=nil] utcTime 是否解析RMC语句中的UTC时间,true表示解析,false或者nil不解析
|
||||
-- @bool[opt=nil] gsv 是否解析GSV语句,true表示解析,false或者nil不解析
|
||||
-- @bool[opt=nil] gsaId 是否解析GSA语句中的卫星ID,true表示解析,false或者nil不解析
|
||||
-- @usage gps.setParseItem(true,true,true)
|
||||
function setParseItem(utcTime,gsv,gsaId)
|
||||
psUtcTime,psGsv,psSn = utcTime,gsv,gsaId
|
||||
end
|
||||
|
||||
|
||||
function init()
|
||||
sys.subscribe("GPS_STATE",statInd)
|
||||
end
|
||||
|
||||
function unInit()
|
||||
sys.unsubscribe("GPS_STATE",statInd)
|
||||
closeAll()
|
||||
end
|
||||
|
||||
init()
|
||||
927
4G/源代码/lib/gpsHxxt.lua
Normal file
927
4G/源代码/lib/gpsHxxt.lua
Normal file
@@ -0,0 +1,927 @@
|
||||
--- 模块功能:GPS模块管理
|
||||
-- 注意:此功能模块中的功能接口可以分为四大类:
|
||||
-- 1、GPS开启
|
||||
-- 2、GPS关闭
|
||||
-- 3、GPS定位数据读取
|
||||
-- 4、GPS参数和功能设置
|
||||
-- 1、2、3是通用功能,除了支持合宙的Air530H模块,理论上也支持其他厂家的串口GPS模块
|
||||
-- 4是专用功能,仅支持合宙的Air530H和Air82x系列模块
|
||||
-- @module gpsHxxt
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2021.6.19
|
||||
require"pm"
|
||||
require"utils"
|
||||
module(..., package.seeall)
|
||||
|
||||
local smatch,sfind,slen,ssub,sbyte,sformat,srep = string.match,string.find,string.len,string.sub,string.byte,string.format,string.rep
|
||||
--GPS开启标志,true表示开启状态,false或者nil表示关闭状态
|
||||
local openFlag
|
||||
--GPS定位标志,"2D"表示2D定位,"3D"表示3D定位,其余表示未定位
|
||||
--GPS定位标志,true表示,其余表示未定位
|
||||
local fixFlag,fixOnece=nil,nil
|
||||
--GPS定位成功后,过滤掉前filterSeconds秒的经纬度信息
|
||||
--是否已经过滤完成
|
||||
local filterSeconds,filteredFlag = 0
|
||||
--从定位成功切换到定位失败,连续定位失败的次数
|
||||
local fixFailCnt = 0
|
||||
--经纬度类型和数据
|
||||
local latitudeType,latitude,longitudeType,longitude = "N","","E",""
|
||||
--海拔,速度,方向角
|
||||
local altitude,speed,course = "0","0","0"
|
||||
--参与定位的卫星个数,所有可见卫星的最大信号值,所有可见卫星的最大信号值中间缓存值
|
||||
local usedSateCnt,maxSignalStrength,maxSignalStrengthVar = "0",0,0
|
||||
--可见卫星个数
|
||||
local viewedGpsSateCnt,viewedBdSateCnt = "0","0"
|
||||
--可用卫星号,UTC时间,信噪比
|
||||
local SateSn,UtcTime,Gsv
|
||||
--大地高,度分经度,度分纬度
|
||||
local Sep,Ggalng,Ggalat
|
||||
--是否需要解析项
|
||||
local psUtcTime,psGsv,psSn
|
||||
|
||||
--GPS供电设置函数
|
||||
local powerCbFnc
|
||||
--串口配置
|
||||
uartBaudrate = 115200
|
||||
local uartID,uartDatabits,uartParity,uartStopbits = 3,8,uart.PAR_NONE,uart.STOP_1
|
||||
--搜星模式命令字符串,"$PGKC115," .. gps .. "," .. glonass .. "," .. beidou .. "," .. galieo .. "*"
|
||||
local aerialModeStr,aerialModeSetted = ""
|
||||
--启动模式命令字符串
|
||||
local startModeStr,startModeSetted = "",""
|
||||
--运行模式命令字符串,"$PGKC105," .. mode .. "," .. rt .. "," .. st .. "*"
|
||||
local runModeStr,runModeSetted = "",""
|
||||
--正常运行模式下NMEA数据上报间隔命令字符串,"$PGKC101," .. interval .. "*"
|
||||
local nmeaReportStr,nmeaReportSetted = ""
|
||||
--每种NEMA数据的输出频率命令字符串
|
||||
local nmeaReportFreqStr,nmeaReportFreqSetted = ""
|
||||
--NMEA数据处理模式,0表示仅gpsHxxt.lua内部处理,1表示仅用户自己处理,2表示gpsHxxt.lua和用户同时处理
|
||||
--用户处理一条NMEA数据的回调函数
|
||||
local nmeaMode,nmeaCbFnc = 0
|
||||
--NMEA数据输出间隔
|
||||
local nmeaInterval = 1000
|
||||
--运行模式
|
||||
--0,正常运行模式
|
||||
--1,低功耗模式
|
||||
local runMode = 0
|
||||
--gps 的串口线程是否在工作;
|
||||
local taskFlag=false
|
||||
--runMode为1或者2时,GPS运行状态和休眠状态的时长
|
||||
local runTime,sleepTime
|
||||
|
||||
--检测gps是否工作正常的定时器ID
|
||||
local workAbnormalTimerId
|
||||
|
||||
--[[
|
||||
函数名:getstrength
|
||||
功能 :解析GSV数据
|
||||
参数 :
|
||||
sg:NEMA中的一行GSV数据
|
||||
返回值:无
|
||||
]]
|
||||
local function getstrength(sg)
|
||||
sg = ssub(sg, 4, #sg)
|
||||
local d1,d2,curnum,lineno,total,sgv_str = sfind(sg,"GSV,(%d),(%d),(%d+),(.*)%*.*")
|
||||
|
||||
if not curnum or not lineno or not total or not sgv_str then
|
||||
return
|
||||
end
|
||||
if lineno == nil then
|
||||
maxSignalStrengthVar = 0
|
||||
maxSignalStrength = 0
|
||||
elseif tonumber(lineno) == 1 then
|
||||
maxSignalStrength = maxSignalStrengthVar
|
||||
maxSignalStrengthVar = 0
|
||||
end
|
||||
|
||||
local tmpstr,i = sgv_str
|
||||
for i=1,4 do
|
||||
local d1,d2,id,elevation,azimuth,strength = sfind(tmpstr,"(%d+),([%-]*%d*),(%d*),(%d*)")
|
||||
if id == nil then return end
|
||||
if strength == "" or not strength then
|
||||
strength = "00"
|
||||
end
|
||||
strength = tonumber(strength)
|
||||
if strength > maxSignalStrengthVar then
|
||||
maxSignalStrengthVar = strength
|
||||
end
|
||||
|
||||
local idx,cur,fnd,tmpid = 0,id..","..elevation..","..azimuth..","..strength..",",false
|
||||
for tmpid in string.gmatch(Gsv,"(%d+),%d*,%d*,%d*,") do
|
||||
idx = idx + 1
|
||||
if tmpid == id then fnd = true break end
|
||||
end
|
||||
if fnd then
|
||||
local pattern,i = ""
|
||||
for i=1,idx do
|
||||
pattern = pattern.."%d+,%d*,%d*,%d*,"
|
||||
end
|
||||
local m1,m2 = sfind(Gsv,"^"..pattern)
|
||||
if m1 and m2 then
|
||||
local front = ssub(Gsv,1,m2)
|
||||
local n1,n2 = sfind(front,"%d+,%d*,%d*,%d*,$")
|
||||
if n1 and n2 then
|
||||
Gsv = ssub(Gsv,1,n1-1)..cur..ssub(Gsv,n2+1,-1)
|
||||
end
|
||||
end
|
||||
else
|
||||
Gsv = Gsv..cur
|
||||
end
|
||||
|
||||
tmpstr = ssub(tmpstr,d2+1,-1)
|
||||
end
|
||||
end
|
||||
|
||||
local function filterTimerFnc()
|
||||
log.info("gpsHxxt.filterTimerFnc end")
|
||||
filteredFlag = true
|
||||
end
|
||||
|
||||
local function stopWorkAbnormalTimer()
|
||||
if workAbnormalTimerId then
|
||||
sys.timerStop(workAbnormalTimerId)
|
||||
workAbnormalTimerId = nil
|
||||
end
|
||||
end
|
||||
|
||||
local function parseNmea(s)
|
||||
if not s or s=="" then return end
|
||||
local lat,lng,spd,cog,gpsFind,gpsTime,gpsDate,locSateCnt,hdp,latTyp,lngTyp,altd
|
||||
|
||||
local fixed,workAbnormal
|
||||
if smatch(s,"GGA") then
|
||||
lat,latTyp,lng,lngTyp,gpsFind,locSateCnt,hdp,altd,sep = smatch(s,"GGA,%d+%.%d+,(%d+%.%d+),([NS]),(%d+%.%d+),([EW]),(%d),(%d+),([%d%.]*),(.*),M,(.*),M")
|
||||
if (gpsFind=="1" or gpsFind=="2" or gpsFind=="4") and altd then
|
||||
--fixed = true
|
||||
altitude = altd
|
||||
latitudeType,longitudeType,latitude,longitude = latTyp,lngTyp,lat,lng
|
||||
usedSateCnt = locSateCnt
|
||||
Ggalng,Ggalat = (lngTyp=="W" and "-" or "")..lng,(latTyp=="S" and "-" or "")..lat
|
||||
Sep = sep
|
||||
else
|
||||
fixed = false
|
||||
end
|
||||
elseif smatch(s,"RMC") then
|
||||
gpsTime,gpsFind,lat,latTyp,lng,lngTyp,spd,cog,gpsDate = smatch(s,"RMC,(%d%d%d%d%d%d)%.%d+,(%w),(%d*%.*%d*),([NS]*),(%d*%.*%d*),([EW]*),(.-),(.-),(%d%d%d%d%d%d),")
|
||||
if gpsFind=="A" and cog then
|
||||
fixed = true
|
||||
latitudeType,longitudeType,latitude,longitude = latTyp,lngTyp,lat,lng
|
||||
speed = spd
|
||||
course = cog
|
||||
else
|
||||
fixed = false
|
||||
end
|
||||
if psUtcTime and gpsFind == "A" and gpsTime and gpsDate and gpsTime ~= "" and gpsDate ~= "" then
|
||||
local yy,mm,dd,h,m,s = tonumber(ssub(gpsDate,5,6)),tonumber(ssub(gpsDate,3,4)),tonumber(ssub(gpsDate,1,2)),tonumber(ssub(gpsTime,1,2)),tonumber(ssub(gpsTime,3,4)),tonumber(ssub(gpsTime,5,6))
|
||||
UtcTime = {year=2000+yy,month=mm,day=dd,hour=h,min=m,sec=s}
|
||||
end
|
||||
elseif smatch(s,"GPGSV") then
|
||||
viewedGpsSateCnt = tonumber(smatch(s,"%d+,%d+,(%d+)") or "0")
|
||||
if psGsv then getstrength(s) end
|
||||
elseif smatch(s,"BDGSV") then
|
||||
viewedBdSateCnt = tonumber(smatch(s,"%d+,%d+,(%d+)") or "0")
|
||||
if psGsv then getstrength(s) end
|
||||
elseif smatch(s,"GSA") then
|
||||
if psSn then
|
||||
local satesn = smatch(s,"GSA,%w*,%d*,(%d*,%d*,%d*,%d*,%d*,%d*,%d*,%d*,%d*,%d*,%d*,%d*,)") or ""
|
||||
if slen(satesn) > 0 and smatch(satesn,"%d+,") then
|
||||
SateSn = satesn
|
||||
end
|
||||
end
|
||||
else
|
||||
workAbnormal = true
|
||||
end
|
||||
|
||||
if not workAbnormal then
|
||||
stopWorkAbnormalTimer()
|
||||
end
|
||||
|
||||
if filterSeconds>0 and fixed and not fixFlag and not filteredFlag then
|
||||
if not sys.timerIsActive(filterTimerFnc) then
|
||||
log.info("gpsHxxt.filterTimerFnc begin")
|
||||
sys.publish("GPS_STATE","LOCATION_FILTER")
|
||||
sys.timerStart(filterTimerFnc,filterSeconds*1000)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
--定位成功
|
||||
if fixed then
|
||||
if not fixFlag then
|
||||
fixFlag,filteredFlag = true,true
|
||||
fixOnece=true
|
||||
fixFailCnt = 0
|
||||
sys.publish("GPS_STATE","LOCATION_SUCCESS")
|
||||
end
|
||||
elseif fixed==false then
|
||||
if fixFlag then
|
||||
fixFailCnt = fixFailCnt+1
|
||||
if fixFailCnt>=20 then
|
||||
fixFlag,filteredFlag = false
|
||||
sys.timerStop(filterTimerFnc)
|
||||
sys.publish("GPS_STATE","LOCATION_FAIL")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function taskRead()
|
||||
local cacheData = ""
|
||||
local co = coroutine.running()
|
||||
while true do
|
||||
local s =""
|
||||
if openFlag then
|
||||
s= uart.read(uartID, "*l")
|
||||
end
|
||||
if s == "" then
|
||||
uart.on(uartID,"receive",function() coroutine.resume(co) end)
|
||||
coroutine.yield()
|
||||
uart.on(uartID,"receive")
|
||||
else
|
||||
cacheData = cacheData..s
|
||||
local d1,d2,nemaStr = sfind(cacheData,"\r\n")
|
||||
while d1 do
|
||||
writePendingCmds()
|
||||
nemaStr = ssub(cacheData,1,d2)
|
||||
cacheData = ssub(cacheData,d2+1,-1)
|
||||
|
||||
if nmeaMode==0 or nmeaMode==2 then
|
||||
--解析一行NEMA数据
|
||||
parseNmea(nemaStr)
|
||||
end
|
||||
if (nmeaMode==1 or nmeaMode==2) and nmeaCbFnc then
|
||||
nmeaCbFnc(nemaStr)
|
||||
end
|
||||
d1,d2 = sfind(cacheData,"\r\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function writeData(data)
|
||||
uart.write(uartID,data)
|
||||
log.info("gpsHxxt.writeData",data:toHex())
|
||||
end
|
||||
|
||||
|
||||
-- GPS串口写命令操作
|
||||
-- @number class,消息类
|
||||
-- @number is,消息编号
|
||||
-- @string payload,有效载荷
|
||||
-- @return nil
|
||||
-- @usage gpsHxxt.writeCmd(cmd)
|
||||
function writeCasic(class,id,payload)
|
||||
local tmp = pack.pack("<bbHbbA",0xBA,0xCE,payload:len(),class,id,payload)
|
||||
|
||||
local checkSum = bit.lshift(id,24)+bit.lshift(class,16)+payload:len()
|
||||
for i=1,payload:len(),4 do
|
||||
checkSum = checkSum + payload:byte(i) + bit.lshift(payload:byte(i+1),8) + bit.lshift(payload:byte(i+2),16) + bit.lshift(payload:byte(i+3),24)
|
||||
end
|
||||
|
||||
tmp = tmp..pack.pack("<I",checkSum)
|
||||
|
||||
uart.write(uartID,tmp)
|
||||
--log.info("gpsZkw.writeCasic",tmp)
|
||||
log.info("gpsHxxt.writeCasic",tmp:toHex())
|
||||
end
|
||||
|
||||
-- GPS串口写命令操作
|
||||
-- @string cmd,GPS指令(cmd格式:"$PGKC149,1,115200*"或者"$PGKC149,1,115200*XX\r\n")
|
||||
-- @bool isFull,cmd是否为完整的指令格式,包括校验和以及\r\n;true表示完整,false或者nil为不完整
|
||||
-- @return nil
|
||||
-- @usage gpsHxxt.writeCmd(cmd)
|
||||
function writeCmd(cmd,isFull)
|
||||
local tmp = cmd
|
||||
if not isFull then
|
||||
tmp = 0
|
||||
for i=2,cmd:len()-1 do
|
||||
tmp = bit.bxor(tmp,cmd:byte(i))
|
||||
end
|
||||
--tmp = cmd..(string.format("%02X",tmp)):upper().."\r\n"
|
||||
tmp = cmd.."\r\n"
|
||||
end
|
||||
|
||||
uart.write(uartID,tmp)
|
||||
log.info("gpsHxxt.writecmd",tmp)
|
||||
--log.info("gpsHxxt.writecmd:toHex",tmp:toHex())
|
||||
end
|
||||
|
||||
function writePendingCmds()
|
||||
if not aerialModeSetted and aerialModeStr~="" then writeCmd(aerialModeStr) aerialModeSetted=true end
|
||||
--if not runModeSetted and runModeStr~="" then writeCmd(runModeStr) runModeSetted=true end
|
||||
if not nmeaReportSetted and nmeaReportStr~="" then writeCmd(nmeaReportStr) nmeaReportSetted=true end
|
||||
if not nmeaReportFreqSetted and nmeaReportFreqStr~="" then writeCmd(nmeaReportFreqStr) nmeaReportFreqSetted=true end
|
||||
end
|
||||
|
||||
local function _open()
|
||||
if openFlag then return end
|
||||
pm.wake("gpsHxxt.lua")
|
||||
uart.setup(uartID,uartBaudrate,uartDatabits,uartParity,uartStopbits)
|
||||
if not taskFlag then
|
||||
taskFlag =true
|
||||
sys.taskInit(taskRead)
|
||||
end
|
||||
if powerCbFnc then
|
||||
powerCbFnc(true)
|
||||
else
|
||||
pmd.ldoset(15,pmd.LDO_VIBR)
|
||||
rtos.sys32k_clk_out(1)
|
||||
end
|
||||
openFlag = true
|
||||
workAbnormalTimerId = sys.timerStart(sys.publish,8000,"GPS_WORK_ABNORMAL_IND")
|
||||
sys.publish("GPS_STATE","OPEN")
|
||||
fixFlag,filteredFlag = false
|
||||
Ggalng,Ggalat,Gsv,Sep = "","",""
|
||||
log.info("gpsHxxt._open")
|
||||
end
|
||||
|
||||
local function _close()
|
||||
if not openFlag then return end
|
||||
if powerCbFnc then
|
||||
powerCbFnc(false)
|
||||
else
|
||||
pmd.ldoset(0,pmd.LDO_VIBR)
|
||||
--rtos.sys32k_clk_out(0)
|
||||
end
|
||||
uart.close(uartID)
|
||||
pm.sleep("gpsHxxt.lua")
|
||||
openFlag = false
|
||||
sys.publish("GPS_STATE","CLOSE",fixFlag)
|
||||
stopWorkAbnormalTimer()
|
||||
fixFlag,filteredFlag = false
|
||||
sys.timerStop(filterTimerFnc)
|
||||
Ggalng,Ggalat,Gsv,Sep = "","",""
|
||||
aerialModeSetted,runModeSetted,nmeaReportSetted,nmeaReportFreqSetted = nil
|
||||
log.info("gpsHxxt._close")
|
||||
end
|
||||
|
||||
|
||||
--- GPS应用模式1.
|
||||
--
|
||||
-- 打开GPS后,GPS定位成功时,如果有回调函数,会调用回调函数
|
||||
--
|
||||
-- 使用此应用模式调用gpsHxxt.open打开的“GPS应用”,必须主动调用gpsHxxt.close或者gpsHxxt.closeAll才能关闭此“GPS应用”,主动关闭时,即使有回调函数,也不会调用回调函数
|
||||
DEFAULT = 1
|
||||
--- GPS应用模式2.
|
||||
--
|
||||
-- 打开GPS后,如果在GPS开启最大时长到达时,没有定位成功,如果有回调函数,会调用回调函数,然后自动关闭此“GPS应用”
|
||||
--
|
||||
-- 打开GPS后,如果在GPS开启最大时长内,定位成功,如果有回调函数,会调用回调函数,然后自动关闭此“GPS应用”
|
||||
--
|
||||
-- 打开GPS后,在自动关闭此“GPS应用”前,可以调用gpsHxxt.close或者gpsHxxt.closeAll主动关闭此“GPS应用”,主动关闭时,即使有回调函数,也不会调用回调函数
|
||||
TIMERORSUC = 2
|
||||
--- GPS应用模式3.
|
||||
--
|
||||
-- 打开GPS后,在GPS开启最大时长时间到达时,无论是否定位成功,如果有回调函数,会调用回调函数,然后自动关闭此“GPS应用”
|
||||
--
|
||||
-- 打开GPS后,在自动关闭此“GPS应用”前,可以调用gpsHxxt.close或者gpsHxxt.closeAll主动关闭此“GPS应用”,主动关闭时,即使有回调函数,也不会调用回调函数
|
||||
TIMER = 3
|
||||
|
||||
--“GPS应用”表
|
||||
local tList = {}
|
||||
|
||||
--[[
|
||||
函数名:delItem
|
||||
功能 :从“GPS应用”表中删除一项“GPS应用”,并不是真正的删除,只是设置一个无效标志
|
||||
参数 :
|
||||
mode:GPS应用模式
|
||||
para:
|
||||
para.tag:“GPS应用”标记
|
||||
para.val:GPS开启最大时长
|
||||
para.cb:回调函数
|
||||
返回值:无
|
||||
]]
|
||||
local function delItem(mode,para)
|
||||
for i=1,#tList do
|
||||
--标志有效 并且 GPS应用模式相同 并且 “GPS应用”标记相同
|
||||
if tList[i].flag and tList[i].mode==mode and tList[i].para.tag==para.tag then
|
||||
--设置无效标志
|
||||
tList[i].flag,tList[i].delay = false
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:addItem
|
||||
功能 :新增一项“GPS应用”到“GPS应用”表
|
||||
参数 :
|
||||
mode:GPS应用模式
|
||||
para:
|
||||
para.tag:“GPS应用”标记
|
||||
para.val:GPS开启最大时长
|
||||
para.cb:回调函数
|
||||
返回值:无
|
||||
]]
|
||||
local function addItem(mode,para)
|
||||
--删除相同的“GPS应用”
|
||||
delItem(mode,para)
|
||||
local item,i,fnd = {flag=true, mode=mode, para=para}
|
||||
--如果是TIMERORSUC或者TIMER模式,初始化GPS工作剩余时间
|
||||
if mode==TIMERORSUC or mode==TIMER then item.para.remain = para.val end
|
||||
for i=1,#tList do
|
||||
--如果存在无效的“GPS应用”项,直接使用此位置
|
||||
if not tList[i].flag then
|
||||
tList[i] = item
|
||||
fnd = true
|
||||
break
|
||||
end
|
||||
end
|
||||
--新增一项
|
||||
if not fnd then table.insert(tList,item) end
|
||||
end
|
||||
|
||||
local function existTimerItem()
|
||||
for i=1,#tList do
|
||||
if tList[i].flag and (tList[i].mode==TIMERORSUC or tList[i].mode==TIMER or tList[i].para.delay) then return true end
|
||||
end
|
||||
end
|
||||
|
||||
local function timerFnc()
|
||||
for i=1,#tList do
|
||||
if tList[i].flag then
|
||||
log.info("gpsHxxt.timerFnc@"..i,tList[i].mode,tList[i].para.tag,tList[i].para.val,tList[i].para.remain,tList[i].para.delay)
|
||||
local rmn,dly,md,cb = tList[i].para.remain,tList[i].para.delay,tList[i].mode,tList[i].para.cb
|
||||
|
||||
if rmn and rmn>0 then
|
||||
tList[i].para.remain = rmn-1
|
||||
end
|
||||
if dly and dly>0 then
|
||||
tList[i].para.delay = dly-1
|
||||
end
|
||||
rmn = tList[i].para.remain
|
||||
|
||||
if isFix() and md==TIMER and rmn==0 and not tList[i].para.delay then
|
||||
tList[i].para.delay = 1
|
||||
end
|
||||
|
||||
dly = tList[i].para.delay
|
||||
if isFix() then
|
||||
if dly and dly==0 then
|
||||
if cb then cb(tList[i].para.tag) end
|
||||
if md == DEFAULT then
|
||||
tList[i].para.delay = nil
|
||||
else
|
||||
close(md,tList[i].para)
|
||||
end
|
||||
end
|
||||
else
|
||||
if rmn and rmn == 0 then
|
||||
if cb then cb(tList[i].para.tag) end
|
||||
close(md,tList[i].para)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if existTimerItem() then sys.timerStart(timerFnc,1000) end
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:statInd
|
||||
功能 :处理GPS定位成功的消息
|
||||
参数 :
|
||||
evt:GPS消息类型
|
||||
返回值:无
|
||||
]]
|
||||
local function statInd(evt)
|
||||
--定位成功的消息
|
||||
if evt == "LOCATION_SUCCESS" then
|
||||
for i=1,#tList do
|
||||
log.info("gpsHxxt.statInd@"..i,tList[i].flag,tList[i].mode,tList[i].para.tag,tList[i].para.val,tList[i].para.remain,tList[i].para.delay,tList[i].para.cb)
|
||||
if tList[i].flag then
|
||||
if tList[i].mode ~= TIMER then
|
||||
tList[i].para.delay = 1
|
||||
if tList[i].mode == DEFAULT then
|
||||
if existTimerItem() then sys.timerStart(timerFnc,1000) end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- 打开一个“GPS应用”
|
||||
-- “GPS应用”:指的是使用GPS功能的一个应用
|
||||
-- 例如,假设有如下3种需求,要打开GPS,则一共有3个“GPS应用”:
|
||||
-- “GPS应用1”:每隔1分钟打开一次GPS
|
||||
-- “GPS应用2”:设备发生震动时打开GPS
|
||||
-- “GPS应用3”:收到一条特殊短信时打开GPS
|
||||
-- 只有所有“GPS应用”都关闭了,才会去真正关闭GPS
|
||||
-- 每个“GPS应用”打开或者关闭GPS时,最多有4个参数,其中 GPS应用模式和GPS应用标记 共同决定了一个唯一的“GPS应用”:
|
||||
-- 1、GPS应用模式(必选)
|
||||
-- 2、GPS应用标记(必选)
|
||||
-- 3、GPS开启最大时长[可选]
|
||||
-- 4、回调函数[可选]
|
||||
-- 例如gpsHxxt.open(gpsHxxt.TIMERORSUC,{tag="TEST",val=120,cb=testGpsCb})
|
||||
-- gpsHxxt.TIMERORSUC为GPS应用模式,"TEST"为GPS应用标记,120秒为GPS开启最大时长,testGpsCb为回调函数
|
||||
-- @number mode GPS应用模式,支持gpsHxxt.DEFAULT,gpsHxxt.TIMERORSUC,gpsHxxt.TIMER三种
|
||||
-- @param para table类型,GPS应用参数
|
||||
-- para.tag:string类型,GPS应用标记
|
||||
-- para.val:number类型,GPS应用开启最大时长,mode参数为gpsHxxt.TIMERORSUC或者gpsHxxt.TIMER时,此值才有意义
|
||||
-- para.cb:GPS应用结束时的回调函数,回调函数的调用形式为para.cb(para.tag)
|
||||
-- @return nil
|
||||
-- @usage gpsHxxt.open(gpsHxxt.DEFAULT,{tag="TEST1",cb=test1Cb})
|
||||
-- @usage gpsHxxt.open(gpsHxxt.TIMERORSUC,{tag="TEST2",val=60,cb=test2Cb})
|
||||
-- @usage gpsHxxt.open(gpsHxxt.TIMER,{tag="TEST3",val=120,cb=test3Cb})
|
||||
-- @see DEFAULT,TIMERORSUC,TIMER
|
||||
function open(mode,para)
|
||||
assert((para and type(para) == "table" and para.tag and type(para.tag) == "string"),"gpsHxxt.open para invalid")
|
||||
log.info("gpsHxxt.open",mode,para.tag,para.val,para.cb)
|
||||
--如果GPS定位成功
|
||||
if isFix() then
|
||||
if mode~=TIMER then
|
||||
--执行回调函数
|
||||
if para.cb then para.cb(para.tag) end
|
||||
if mode==TIMERORSUC then return end
|
||||
end
|
||||
end
|
||||
addItem(mode,para)
|
||||
--真正去打开GPS
|
||||
_open()
|
||||
--启动1秒的定时器
|
||||
if existTimerItem() and not sys.timerIsActive(timerFnc) then
|
||||
sys.timerStart(timerFnc,1000)
|
||||
end
|
||||
end
|
||||
|
||||
--- 关闭一个“GPS应用”
|
||||
-- 只是从逻辑上关闭一个GPS应用,并不一定真正关闭GPS,是有所有的GPS应用都处于关闭状态,才会去真正关闭GPS
|
||||
-- @number mode GPS应用模式,支持gpsHxxt.DEFAULT,gpsHxxt.TIMERORSUC,gpsHxxt.TIMER三种
|
||||
-- @param para table类型,GPS应用参数
|
||||
-- para.tag:string类型,GPS应用标记
|
||||
-- para.val:number类型,GPS应用开启最大时长,mode参数为gpsHxxt.TIMERORSUC或者gpsHxxt.TIMER时,此值才有意义;使用close接口时,不需要传入此参数
|
||||
-- para.cb:GPS应用结束时的回调函数,回调函数的调用形式为para.cb(para.tag);使用close接口时,不需要传入此参数
|
||||
-- @return nil
|
||||
-- @usage GPS应用模式和GPS应用标记唯一确定一个“GPS应用”,调用本接口关闭时,mode和para.tag要和gpsHxxt.open打开一个“GPS应用”时传入的mode和para.tag保持一致
|
||||
-- @usage gpsHxxt.close(gpsHxxt.DEFAULT,{tag="TEST1"})
|
||||
-- @usage gpsHxxt.close(gpsHxxt.TIMERORSUC,{tag="TEST2"})
|
||||
-- @usage gpsHxxt.close(gpsHxxt.TIMER,{tag="TEST3"})
|
||||
-- @see open,DEFAULT,TIMERORSUC,TIMER
|
||||
function close(mode,para)
|
||||
assert((para and type(para)=="table" and para.tag and type(para.tag)=="string"),"gpsHxxt.close para invalid")
|
||||
log.info("gpsHxxt.close",mode,para.tag,para.val,para.cb)
|
||||
--删除此“GPS应用”
|
||||
delItem(mode,para)
|
||||
local valid,i
|
||||
for i=1,#tList do
|
||||
if tList[i].flag then
|
||||
valid = true
|
||||
end
|
||||
end
|
||||
--如果没有一个“GPS应用”有效,则关闭GPS
|
||||
if not valid then _close() end
|
||||
end
|
||||
|
||||
--- 关闭所有“GPS应用”
|
||||
-- @return nil
|
||||
-- @usage gpsHxxt.closeAll()
|
||||
-- @see open,DEFAULT,TIMERORSUC,TIMER
|
||||
function closeAll()
|
||||
for i=1,#tList do
|
||||
if tList[i].flag and tList[i].para.cb then tList[i].para.cb(tList[i].para.tag) end
|
||||
close(tList[i].mode,tList[i].para)
|
||||
end
|
||||
end
|
||||
|
||||
--- 判断一个“GPS应用”是否处于激活状态
|
||||
-- @number mode GPS应用模式,支持gpsHxxt.DEFAULT,gpsHxxt.TIMERORSUC,gpsHxxt.TIMER三种
|
||||
-- @param para table类型,GPS应用参数
|
||||
-- para.tag:string类型,GPS应用标记
|
||||
-- para.val:number类型,GPS应用开启最大时长,mode参数为gpsHxxt.TIMERORSUC或者gpsHxxt.TIMER时,此值才有意义;使用isActive接口时,不需要传入此参数
|
||||
-- para.cb:GPS应用结束时的回调函数,回调函数的调用形式为para.cb(para.tag);使用isActive接口时,不需要传入此参数
|
||||
-- @return bool result,处于激活状态返回true,否则返回nil
|
||||
-- @usage GPS应用模式和GPS应用标记唯一确定一个“GPS应用”,调用本接口查询状态时,mode和para.tag要和gpsHxxt.open打开一个“GPS应用”时传入的mode和para.tag保持一致
|
||||
-- @usage gpsHxxt.isActive(gpsHxxt.DEFAULT,{tag="TEST1"})
|
||||
-- @usage gpsHxxt.isActive(gpsHxxt.TIMERORSUC,{tag="TEST2"})
|
||||
-- @usage gpsHxxt.isActive(gpsHxxt.TIMER,{tag="TEST3"})
|
||||
-- @see open,DEFAULT,TIMERORSUC,TIMER
|
||||
function isActive(mode,para)
|
||||
assert((para and type(para)=="table" and para.tag and type(para.tag)=="string"),"gpsHxxt.isActive para invalid")
|
||||
for i=1,#tList do
|
||||
if tList[i].flag and tList[i].mode==mode and tList[i].para.tag==para.tag then return true end
|
||||
end
|
||||
end
|
||||
|
||||
--- 设置GPS模块供电控制的回调函数
|
||||
-- 如果使用的是Air800,或者供电控制使用的是LDO_VCAM,则打开GPS应用前不需要调用此接口进行设置
|
||||
-- 否则在调用gpsHxxt.open前,使用此接口,传入自定义的供电控制函数cbFnc,GPS开启时,gpsHxxt.lua自动执行cbFnc(true),GPS关闭时,gpsHxxt.lua自动执行cbFnc(false)
|
||||
-- @param cbFnc function类型,用户自定义的GPS供电控制函数
|
||||
-- @return nil
|
||||
-- @usage gpsHxxt.setPowerCbFnc(cbFnc)
|
||||
function setPowerCbFnc(cbFnc)
|
||||
powerCbFnc = cbFnc
|
||||
end
|
||||
|
||||
--- 设置GPS模块和GSM模块之间数据通信的串口参数
|
||||
-- 如果使用的是Air800,或者使用的UART2(波特率115200,数据位8,无检验位,停止位1),则打开GPS应用前不需要调用此接口进行设置
|
||||
-- 否则在调用gpsHxxt.open前,使用此接口,传入UART参数
|
||||
-- @number id UART ID,支持1和2,1表示UART1,2表示UART2
|
||||
-- @number baudrate 波特率,支持1200,2400,4800,9600,10400,14400,19200,28800,38400,57600,76800,115200,230400,460800,576000,921600,1152000,4000000
|
||||
-- @number databits 数据位,支持8
|
||||
-- @number parity 校验位,支持uart.PAR_NONE,uart.PAR_EVEN,uart.PAR_ODD
|
||||
-- @number stopbits 停止位,支持uart.STOP_1,uart.STOP_2
|
||||
-- @return nil
|
||||
-- @usage gpsHxxt.setUart(2,115200,8,uart.PAR_NONE,uart.STOP_1)
|
||||
function setUart(id,baudrate,databits,parity,stopbits)
|
||||
uartID,uartBaudrate,uartDatabits,uartParity,uartStopbits = id,baudrate,databits,parity,stopbits
|
||||
end
|
||||
|
||||
--- 设置GPS模块搜星模式
|
||||
-- 同时仅支持配置为一种搜星模式
|
||||
-- @number H01 设置为GPS L1+SBAS+QZSS联合定位模式
|
||||
-- @number H10 设置为BDS B1定位
|
||||
-- @number H101 设置为GPS+GLONASS+GALILEO+SBAS+QZSS联合定位
|
||||
-- @number H11 设置为GPS+BDS+GALILEO+SBAS+QZSS联合定位
|
||||
-- @return nil
|
||||
-- @usage gpsHxxt.setAerialMode(nil,nil,1)
|
||||
function setAerialMode(H01,H10,H101,H11)
|
||||
local H01 = H01 or 0
|
||||
local H10 = H10 or 0
|
||||
local H101 = H101 or 0
|
||||
local H11 = H11 or 0
|
||||
|
||||
if H01~=0 then rt=H01 end
|
||||
if H10~=0 then rt=H10 end
|
||||
if H101~=0 then rt=H101 end
|
||||
if H11~=0 then rt=H11 end
|
||||
if H01+H10+H101+H11 == 0 then H11=1 end
|
||||
if H01+H10+H101+H11>1 then
|
||||
log.warn("gpsHxxt.setAerialMode"," function input err")
|
||||
return
|
||||
end
|
||||
local tmpStr = "$CFGSYS,"..rt
|
||||
if tmpStr~=aerialModeStr then
|
||||
aerialModeStr,aerialModeSetted = tmpStr
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- 设置NMEA数据处理模式.
|
||||
-- 如果不调用此接口配置,则默认仅gpsHxxt.lua内部处理NMEA数据
|
||||
-- @number mode NMEA数据处理模式,0表示仅gpsHxxt.lua内部处理,1表示仅用户自己处理,2表示gpsHxxt.lua和用户同时处理
|
||||
-- @param cbFnc function类型,用户处理一条NMEA数据的回调函数,mode为1和2时,此值才有意义
|
||||
-- @return nil
|
||||
-- @usage gpsHxxt.setNmeaMode(0)
|
||||
-- @usage gpsHxxt.setNmeaMode(1,cbFnc)
|
||||
-- @usage gpsHxxt.setNmeaMode(2,cbFnc)
|
||||
function setNmeaMode(mode,cbFnc)
|
||||
nmeaMode,nmeaCbFnc = mode,cbFnc
|
||||
end
|
||||
|
||||
--- 设置GPS模块的运行模式.
|
||||
-- 如果不调用此接口配置,则默认为正常运行模式
|
||||
-- @number mode 运行模式
|
||||
-- 0:正常运行模式
|
||||
-- 1:低功耗模式
|
||||
-- @return nil
|
||||
-- @usage gpsHxxt.setRunMode(0)
|
||||
-- @usage gpsHxxt.setRunMode(1)
|
||||
function setRunMode(mode)
|
||||
local tmpStr = "$CFGLOWPOWER,"..mode
|
||||
if tmpStr~=runModeStr then
|
||||
runModeStr,runModeSetted = tmpStr
|
||||
end
|
||||
end
|
||||
|
||||
--设置NEMA输出语句种类
|
||||
-- @string date NEMA语句名称
|
||||
-- @number flag 0关闭1打开
|
||||
-- @usage gpaHxxt.setNemaReportFreq("GSV",1)
|
||||
-- 开机后需等待250ms+后发送才有效
|
||||
function setNemaReportFreq(date,flag)
|
||||
local rt=""
|
||||
if date=="RMC" then
|
||||
writeCmd("$CFGMSG,0,4,"..flag)
|
||||
elseif date=="GGA" then
|
||||
writeCmd("$CFGMSG,0,0,"..flag)
|
||||
elseif date=="GSA" then
|
||||
writeCmd("$CFGMSG,0,2,"..flag)
|
||||
elseif date=="VTG" then
|
||||
writeCmd("$CFGMSG,0,5,"..flag)
|
||||
elseif date=="GLL" then
|
||||
writeCmd("$CFGMSG,0,1,"..flag)
|
||||
elseif date=="GSV" then
|
||||
writeCmd("$CFGMSG,0,3,"..flag)
|
||||
end
|
||||
end
|
||||
|
||||
--- 设置GPS定位成功后经纬度的过滤时间.
|
||||
-- @number[opt=0] seconds 单位秒,GPS定位成功后,丢弃前seconds秒的位置信息
|
||||
-- @return nil
|
||||
-- @usage gpsHxxt.setLocationFilter(2)
|
||||
function setLocationFilter(seconds)
|
||||
filterSeconds = seconds or 0
|
||||
end
|
||||
|
||||
function setFastFix(lat,lng,tm)
|
||||
local tLocation = getLocation()
|
||||
local Altitude = getAltitude()
|
||||
lngType=tLocation.lngType
|
||||
latType=tLocation.latType
|
||||
local tm = os.date("*t")
|
||||
local t = tm.year..","..tm.month..","..tm.day..","..tm.hour..","..tm.min..","..tm.sec..",000"
|
||||
local APos = string.format("%s,%f,%s,%f,%s,%f","$AIDPOS",lat,latType,lng,lngType,Altitude)
|
||||
log.info("gpsHxxt.setFastFix",lat,lng,t)
|
||||
writeCmd("$AIDTIME,"..t)
|
||||
writeCmd(APos)
|
||||
end
|
||||
|
||||
--- 获取GPS模块是否处于开启状态
|
||||
-- @return bool result,true表示开启状态,false或者nil表示关闭状态
|
||||
-- @usage gpsHxxt.isOpen()
|
||||
function isOpen()
|
||||
return openFlag
|
||||
end
|
||||
|
||||
--- 获取GPS模块是否定位成功
|
||||
-- @return bool result,true表示定位成功,false或者nil表示定位失败
|
||||
-- @usage gpsHxxt.isFix()
|
||||
function isFix()
|
||||
return fixFlag
|
||||
end
|
||||
|
||||
function issFix()
|
||||
fixFlag=false
|
||||
end
|
||||
--- 获取GPS模块是否首次定位成功过
|
||||
-- @return bool result,true表示曾经定位成功
|
||||
-- @usage gpsHxxt.isOnece()
|
||||
function isOnece()
|
||||
return fixOnece
|
||||
end
|
||||
|
||||
-- 度分格式转换为度格式
|
||||
-- @string inStr 度分格式的位置
|
||||
-- @return string,度格式的位置
|
||||
-- @usage degreeMinuteToDegree("3114.50931")--->"31.2418218",31度14.50931分转换为31.2418218度
|
||||
-- @usage degreeMinuteToDegree("12128.44954")--->"121.4741590",121度28.44954分转换为121.4741590度
|
||||
local function degreeMinuteToDegree(inStr)
|
||||
local integer,fraction = smatch(inStr,"(%d+)%.(%d+)")
|
||||
if integer and fraction then
|
||||
local intLen = slen(integer)
|
||||
if intLen~=4 and intLen~=5 then log.error("gpsHxxt.degreeMinuteToDegree integer error",inStr) return "" end
|
||||
if slen(fraction)<5 then fraction = fraction..srep("0",5-slen(fraction)) end
|
||||
fraction = ssub(fraction,1,5)
|
||||
local temp = tonumber(ssub(integer,intLen-1,intLen)..fraction)*10
|
||||
fraction = tostring((temp-(temp%6))/6)
|
||||
local fracLen = slen(fraction)
|
||||
if fracLen>7 then
|
||||
fraction = ssub(fraction,1,7)
|
||||
elseif fracLen<7 then
|
||||
fraction = srep("0",7-fracLen)..fraction
|
||||
end
|
||||
return ssub(integer,1,intLen-2).."."..fraction
|
||||
end
|
||||
|
||||
return ""
|
||||
end
|
||||
|
||||
--- 获取度格式的经纬度信息
|
||||
-- @string[opt=nil] typ 返回的经纬度格式,typ为"DEGREE_MINUTE"时表示返回度分格式,其余表示返回度格式
|
||||
-- @return table location
|
||||
-- 例如typ为"DEGREE_MINUTE"时返回{lngType="E",lng="12128.44954",latType="N",lat="3114.50931"}
|
||||
-- 例如typ不是"DEGREE_MINUTE"时返回{lngType="E",lng="121.123456",latType="N",lat="31.123456"}
|
||||
-- lngType:string类型,表示经度类型,取值"E","W"
|
||||
-- lng:string类型,表示度格式的经度值,无效时为""
|
||||
-- latType:string类型,表示纬度类型,取值"N","S"
|
||||
-- lat:string类型,表示度格式的纬度值,无效时为""
|
||||
-- @usage gpsHxxt.getLocation()
|
||||
function getLocation(typ)
|
||||
return {
|
||||
lngType=longitudeType,
|
||||
lng=isFix() and (typ=="DEGREE_MINUTE" and longitude or degreeMinuteToDegree(longitude)) or "",
|
||||
latType=latitudeType,
|
||||
lat=isFix() and (typ=="DEGREE_MINUTE" and latitude or degreeMinuteToDegree(latitude)) or ""
|
||||
}
|
||||
end
|
||||
|
||||
function getLastLocation(typ)
|
||||
if typ=="DEGREE_MINUTE" then
|
||||
return {
|
||||
lngType=longitudeType,
|
||||
lng=longitude,
|
||||
latType=latitudeType,
|
||||
lat=latitude
|
||||
}
|
||||
else
|
||||
return (longitude and longitude~="") and degreeMinuteToDegree(longitude) or "", (latitude and latitude~="") and degreeMinuteToDegree(latitude) or ""
|
||||
end
|
||||
end
|
||||
|
||||
--- 获取海拔
|
||||
-- @return number altitude,海拔,单位米
|
||||
-- @usage gpsHxxt.getAltitude()
|
||||
function getAltitude()
|
||||
return tonumber(smatch(altitude,"(%d+)") or "0")
|
||||
end
|
||||
|
||||
--- 获取速度
|
||||
-- @return number kmSpeed,第一个返回值为公里每小时的速度
|
||||
-- @return number nmSpeed,第二个返回值为海里每小时的速度
|
||||
-- @usage gpsHxxt.getSpeed()
|
||||
function getSpeed()
|
||||
local integer = tonumber(smatch(speed,"(%d+)") or "0")
|
||||
return (integer*1852 - (integer*1852 %1000))/1000,integer
|
||||
end
|
||||
|
||||
--- 获取原始速度,字符串带浮点
|
||||
-- @return number speed 海里每小时的速度
|
||||
-- @usage gpsHxxt.getOrgSpeed()
|
||||
function getOrgSpeed()
|
||||
return speed
|
||||
end
|
||||
|
||||
--- 获取方向角
|
||||
-- @return number course,方向角
|
||||
-- @usage gpsHxxt.getCourse()
|
||||
function getCourse()
|
||||
return tonumber(smatch(course,"(%d+)") or "0")
|
||||
end
|
||||
|
||||
-- 获取所有可见卫星的最大信号强度
|
||||
-- @return number strength,最大信号强度
|
||||
-- @usage gpsHxxt.getMaxSignalStrength()
|
||||
function getMaxSignalStrength()
|
||||
return maxSignalStrength
|
||||
end
|
||||
|
||||
--- 获取可见卫星的个数
|
||||
-- @return number count,可见卫星的个数
|
||||
-- @usage gpsHxxt.getViewedSateCnt()
|
||||
function getViewedSateCnt()
|
||||
return tonumber(viewedGpsSateCnt)+tonumber(viewedBdSateCnt)
|
||||
end
|
||||
|
||||
--- 获取定位使用的卫星个数
|
||||
-- @return number count,定位使用的卫星个数
|
||||
-- @usage gpsHxxt.getUsedSateCnt()
|
||||
function getUsedSateCnt()
|
||||
return tonumber(usedSateCnt)
|
||||
end
|
||||
|
||||
--- 获取GGA语句中度分格式的经纬度信息
|
||||
-- @return string lng,度分格式的经度值(dddmm.mmmm),西经会添加一个-前缀,无效时为"";例如"12112.3456"表示东经121度12.3456分,"-12112.3456"表示西经121度12.3456分
|
||||
-- @return string lat,度分格式的纬度值(ddmm.mmmm),南纬会添加一个-前缀,无效时为"";例如"3112.3456"表示北纬31度12.3456分,"-3112.3456"表示南纬31度12.3456分
|
||||
-- @usage gpsHxxt.getGgaloc()
|
||||
function getGgaloc()
|
||||
return Ggalng or "",Ggalat or ""
|
||||
end
|
||||
|
||||
--- 获取RMC语句中的UTC时间
|
||||
-- 只有同时满足如下两个条件,返回值才有效
|
||||
-- 1、开启了GPS,并且定位成功
|
||||
-- 2、调用setParseItem接口,第一个参数设置为true
|
||||
-- @return table utcTime,UTC时间,nil表示无效,例如{year=2018,month=4,day=24,hour=11,min=52,sec=10}
|
||||
-- @usage gpsHxxt.getUtcTime()
|
||||
function getUtcTime()
|
||||
return UtcTime
|
||||
end
|
||||
|
||||
--- 获取定位使用的大地高
|
||||
-- @return number sep,大地高
|
||||
-- @usage gpsHxxt.getSep()
|
||||
function getSep()
|
||||
return tonumber(Sep or "0")
|
||||
end
|
||||
|
||||
--- 获取GSA语句中的可见卫星号
|
||||
-- 只有同时满足如下两个条件,返回值才有效
|
||||
-- 1、开启了GPS,并且定位成功
|
||||
-- 2、调用setParseItem接口,第三个参数设置为true
|
||||
-- @return string viewedSateId,可用卫星号,""表示无效
|
||||
-- @usage gpsHxxt.getSateSn()
|
||||
function getSateSn()
|
||||
return SateSn or ""
|
||||
end
|
||||
|
||||
--- 获取GSV语句中的可见卫星的信噪比
|
||||
-- 只有同时满足如下两个条件,返回值才有效
|
||||
-- 1、开启了GPS,并且定位成功
|
||||
-- 2、调用setParseItem接口,第二个参数设置为true
|
||||
-- @return string gsv,信噪比
|
||||
-- @usage gpsHxxt.getGsv()
|
||||
function getGsv()
|
||||
return Gsv or ""
|
||||
end
|
||||
|
||||
--- 设置是否需要解析的字段
|
||||
-- @bool[opt=nil] utcTime 是否解析RMC语句中的UTC时间,true表示解析,false或者nil不解析
|
||||
-- @bool[opt=nil] gsv 是否解析GSV语句,true表示解析,false或者nil不解析
|
||||
-- @bool[opt=nil] gsaId 是否解析GSA语句中的卫星ID,true表示解析,false或者nil不解析
|
||||
-- @usage gpsHxxt.setParseItem(true,true,true)
|
||||
function setParseItem(utcTime,gsv,gsaId)
|
||||
psUtcTime,psGsv,psSn = utcTime,gsv,gsaId
|
||||
end
|
||||
|
||||
function init()
|
||||
sys.subscribe("GPS_STATE",statInd)
|
||||
end
|
||||
|
||||
function unInit()
|
||||
sys.unsubscribe("GPS_STATE",statInd)
|
||||
closeAll()
|
||||
end
|
||||
|
||||
init()
|
||||
|
||||
942
4G/源代码/lib/gpsZkw.lua
Normal file
942
4G/源代码/lib/gpsZkw.lua
Normal file
@@ -0,0 +1,942 @@
|
||||
--- 模块功能:GPS模块管理
|
||||
-- 注意:此功能模块中的功能接口可以分为四大类:
|
||||
-- 1、GPS开启
|
||||
-- 2、GPS关闭
|
||||
-- 3、GPS定位数据读取
|
||||
-- 4、GPS参数和功能设置
|
||||
-- 1、2、3是通用功能,除了支持合宙的Air530Z和Air82X系列模块,理论上也支持其他厂家的串口GPS模块
|
||||
-- 4是专用功能,仅支持合宙的Air530Z和Air82X系列模块
|
||||
-- @module gpsZkw
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2017.10.23
|
||||
require"pm"
|
||||
require"utils"
|
||||
module(..., package.seeall)
|
||||
|
||||
local smatch,sfind,slen,ssub,sbyte,sformat,srep = string.match,string.find,string.len,string.sub,string.byte,string.format,string.rep
|
||||
--GPS开启标志,true表示开启状态,false或者nil表示关闭状态
|
||||
local openFlag
|
||||
--GPS定位标志,"2D"表示2D定位,"3D"表示3D定位,其余表示未定位
|
||||
--GPS定位标志,true表示,其余表示未定位
|
||||
local fixFlag,fixOnece=nil,nil
|
||||
--GPS定位成功后,过滤掉前filterSeconds秒的经纬度信息
|
||||
--是否已经过滤完成
|
||||
local filterSeconds,filteredFlag = 0
|
||||
--从定位成功切换到定位失败,连续定位失败的次数
|
||||
local fixFailCnt = 0
|
||||
--经纬度类型和数据
|
||||
local latitudeType,latitude,longitudeType,longitude = "N","","E",""
|
||||
--海拔,速度,方向角
|
||||
local altitude,speed,course = "0","0","0"
|
||||
--参与定位的卫星个数,所有可见卫星的最大信号值,所有可见卫星的最大信号值中间缓存值
|
||||
local usedSateCnt,maxSignalStrength,maxSignalStrengthVar = "0",0,0
|
||||
--可见卫星个数
|
||||
local viewedGpsSateCnt,viewedBdSateCnt = "0","0"
|
||||
--可用卫星号,UTC时间,信噪比
|
||||
local SateSn,UtcTime,Gsv
|
||||
--大地高,度分经度,度分纬度
|
||||
local Sep,Ggalng,Ggalat
|
||||
--是否需要解析项
|
||||
local psUtcTime,psGsv,psSn
|
||||
|
||||
--GPS供电设置函数
|
||||
local powerCbFnc
|
||||
--串口配置
|
||||
uartBaudrate = 9600
|
||||
local uartID,uartDatabits,uartParity,uartStopbits = 3,8,uart.PAR_NONE,uart.STOP_1
|
||||
--改变GPS模块串口波特率命令字符串,"$PCAS01," .. br .. "*"
|
||||
local setBaudrateStr,setBaudrateSetted = ""
|
||||
--搜星模式命令字符串,"$PGKC115," .. gps .. "," .. glonass .. "," .. beidou .. "," .. galieo .. "*"
|
||||
local aerialModeStr,aerialModeSetted = ""
|
||||
--运行模式命令字符串,"$PGKC105," .. mode .. "," .. rt .. "," .. st .. "*"
|
||||
--local runModeStr,runModeSetted = ""
|
||||
--正常运行模式下NMEA数据上报间隔命令字符串,"$PGKC101," .. interval .. "*"
|
||||
local nmeaReportStr,nmeaReportSetted = ""
|
||||
--每种NEMA数据的输出频率命令字符串
|
||||
local nmeaReportFreqStr,nmeaReportFreqSetted = ""
|
||||
--NMEA数据处理模式,0表示仅gps.lua内部处理,1表示仅用户自己处理,2表示gps.lua和用户同时处理
|
||||
--用户处理一条NMEA数据的回调函数
|
||||
local nmeaMode,nmeaCbFnc = 0
|
||||
--NMEA数据输出间隔
|
||||
local nmeaInterval = 1000
|
||||
--运行模式
|
||||
--0,正常运行模式
|
||||
--1,周期超低功耗跟踪模式
|
||||
--2,周期低功耗模式
|
||||
--4,直接进入超低功耗跟踪模式
|
||||
--8,自动低功耗模式,可以通过串口唤醒
|
||||
--9, 自动超低功耗跟踪模式,需要force on来唤醒
|
||||
local runMode = 0
|
||||
--gps 的串口线程是否在工作;
|
||||
local taskFlag=false
|
||||
--runMode为1或者2时,GPS运行状态和休眠状态的时长
|
||||
local runTime,sleepTime
|
||||
|
||||
--检测gps是否工作正常的定时器ID
|
||||
local workAbnormalTimerId
|
||||
|
||||
--[[
|
||||
函数名:getstrength
|
||||
功能 :解析GSV数据
|
||||
参数 :
|
||||
sg:NEMA中的一行GSV数据
|
||||
返回值:无
|
||||
]]
|
||||
local function getstrength(sg)
|
||||
sg = ssub(sg, 4, #sg)
|
||||
local d1,d2,curnum,lineno,total,sgv_str = sfind(sg,"GSV,(%d),(%d),(%d+),(.*)%*.*")
|
||||
|
||||
if not curnum or not lineno or not total or not sgv_str then
|
||||
return
|
||||
end
|
||||
if lineno == nil then
|
||||
maxSignalStrengthVar = 0
|
||||
maxSignalStrength = 0
|
||||
elseif tonumber(lineno) == 1 then
|
||||
maxSignalStrength = maxSignalStrengthVar
|
||||
maxSignalStrengthVar = 0
|
||||
end
|
||||
|
||||
local tmpstr,i = sgv_str
|
||||
for i=1,4 do
|
||||
local d1,d2,id,elevation,azimuth,strength = sfind(tmpstr,"(%d+),([%-]*%d*),(%d*),(%d*)")
|
||||
if id == nil then return end
|
||||
if strength == "" or not strength then
|
||||
strength = "00"
|
||||
end
|
||||
strength = tonumber(strength)
|
||||
if strength > maxSignalStrengthVar then
|
||||
maxSignalStrengthVar = strength
|
||||
end
|
||||
|
||||
local idx,cur,fnd,tmpid = 0,id..","..elevation..","..azimuth..","..strength..",",false
|
||||
for tmpid in string.gmatch(Gsv,"(%d+),%d*,%d*,%d*,") do
|
||||
idx = idx + 1
|
||||
if tmpid == id then fnd = true break end
|
||||
end
|
||||
if fnd then
|
||||
local pattern,i = ""
|
||||
for i=1,idx do
|
||||
pattern = pattern.."%d+,%d*,%d*,%d*,"
|
||||
end
|
||||
local m1,m2 = sfind(Gsv,"^"..pattern)
|
||||
if m1 and m2 then
|
||||
local front = ssub(Gsv,1,m2)
|
||||
local n1,n2 = sfind(front,"%d+,%d*,%d*,%d*,$")
|
||||
if n1 and n2 then
|
||||
Gsv = ssub(Gsv,1,n1-1)..cur..ssub(Gsv,n2+1,-1)
|
||||
end
|
||||
end
|
||||
else
|
||||
Gsv = Gsv..cur
|
||||
end
|
||||
|
||||
tmpstr = ssub(tmpstr,d2+1,-1)
|
||||
end
|
||||
end
|
||||
|
||||
local function filterTimerFnc()
|
||||
log.info("gpsZkw.filterTimerFnc end")
|
||||
filteredFlag = true
|
||||
end
|
||||
|
||||
local function stopWorkAbnormalTimer()
|
||||
if workAbnormalTimerId then
|
||||
sys.timerStop(workAbnormalTimerId)
|
||||
workAbnormalTimerId = nil
|
||||
end
|
||||
end
|
||||
|
||||
local function parseNmea(s)
|
||||
if not s or s=="" then return end
|
||||
local lat,lng,spd,cog,gpsFind,gpsTime,gpsDate,locSateCnt,hdp,latTyp,lngTyp,altd
|
||||
|
||||
local fixed,workAbnormal
|
||||
if smatch(s,"GGA") then
|
||||
lat,latTyp,lng,lngTyp,gpsFind,locSateCnt,hdp,altd,sep = smatch(s,"GGA,%d+%.%d+,(%d+%.%d+),([NS]),(%d+%.%d+),([EW]),(%d),(%d+),([%d%.]*),(.*),M,(.*),M")
|
||||
if (gpsFind=="1" or gpsFind=="2" or gpsFind=="4") and altd then
|
||||
--fixed = true
|
||||
altitude = altd
|
||||
latitudeType,longitudeType,latitude,longitude = latTyp,lngTyp,lat,lng
|
||||
usedSateCnt = locSateCnt
|
||||
Ggalng,Ggalat = (lngTyp=="W" and "-" or "")..lng,(latTyp=="S" and "-" or "")..lat
|
||||
Sep = sep
|
||||
else
|
||||
fixed = false
|
||||
end
|
||||
elseif smatch(s,"RMC") then
|
||||
gpsTime,gpsFind,lat,latTyp,lng,lngTyp,spd,cog,gpsDate = smatch(s,"RMC,(%d%d%d%d%d%d)%.%d+,(%w),(%d*%.*%d*),([NS]*),(%d*%.*%d*),([EW]*),(.-),(.-),(%d%d%d%d%d%d),")
|
||||
if gpsFind=="A" and cog then
|
||||
fixed = true
|
||||
latitudeType,longitudeType,latitude,longitude = latTyp,lngTyp,lat,lng
|
||||
speed = spd
|
||||
course = cog
|
||||
else
|
||||
fixed = false
|
||||
end
|
||||
if psUtcTime and gpsFind == "A" and gpsTime and gpsDate and gpsTime ~= "" and gpsDate ~= "" then
|
||||
local yy,mm,dd,h,m,s = tonumber(ssub(gpsDate,5,6)),tonumber(ssub(gpsDate,3,4)),tonumber(ssub(gpsDate,1,2)),tonumber(ssub(gpsTime,1,2)),tonumber(ssub(gpsTime,3,4)),tonumber(ssub(gpsTime,5,6))
|
||||
UtcTime = {year=2000+yy,month=mm,day=dd,hour=h,min=m,sec=s}
|
||||
end
|
||||
elseif smatch(s,"GPGSV") then
|
||||
viewedGpsSateCnt = tonumber(smatch(s,"%d+,%d+,(%d+)") or "0")
|
||||
if psGsv then getstrength(s) end
|
||||
elseif smatch(s,"BDGSV") then
|
||||
viewedBdSateCnt = tonumber(smatch(s,"%d+,%d+,(%d+)") or "0")
|
||||
if psGsv then getstrength(s) end
|
||||
elseif smatch(s,"GSA") then
|
||||
if psSn then
|
||||
local satesn = smatch(s,"GSA,%w*,%d*,(%d*,%d*,%d*,%d*,%d*,%d*,%d*,%d*,%d*,%d*,%d*,%d*,)") or ""
|
||||
if slen(satesn) > 0 and smatch(satesn,"%d+,") then
|
||||
SateSn = satesn
|
||||
end
|
||||
end
|
||||
else
|
||||
workAbnormal = true
|
||||
end
|
||||
|
||||
if not workAbnormal then
|
||||
stopWorkAbnormalTimer()
|
||||
end
|
||||
|
||||
if filterSeconds>0 and fixed and not fixFlag and not filteredFlag then
|
||||
if not sys.timerIsActive(filterTimerFnc) then
|
||||
log.info("gpsZkw.filterTimerFnc begin")
|
||||
sys.publish("GPS_STATE","LOCATION_FILTER")
|
||||
sys.timerStart(filterTimerFnc,filterSeconds*1000)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
--定位成功
|
||||
if fixed then
|
||||
if not fixFlag then
|
||||
fixFlag,filteredFlag = true,true
|
||||
fixOnece=true
|
||||
fixFailCnt = 0
|
||||
sys.publish("GPS_STATE","LOCATION_SUCCESS")
|
||||
end
|
||||
elseif fixed==false then
|
||||
if fixFlag then
|
||||
fixFailCnt = fixFailCnt+1
|
||||
if fixFailCnt>=20 then
|
||||
fixFlag,filteredFlag = false
|
||||
sys.timerStop(filterTimerFnc)
|
||||
sys.publish("GPS_STATE","LOCATION_FAIL")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function taskRead()
|
||||
local cacheData = ""
|
||||
local co = coroutine.running()
|
||||
while true do
|
||||
local s =""
|
||||
if openFlag then
|
||||
s= uart.read(uartID, "*l")
|
||||
end
|
||||
if s == "" then
|
||||
uart.on(uartID,"receive",function() coroutine.resume(co) end)
|
||||
coroutine.yield()
|
||||
uart.on(uartID,"receive")
|
||||
else
|
||||
cacheData = cacheData..s
|
||||
local d1,d2,nemaStr = sfind(cacheData,"\r\n")
|
||||
while d1 do
|
||||
writePendingCmds()
|
||||
nemaStr = ssub(cacheData,1,d2)
|
||||
cacheData = ssub(cacheData,d2+1,-1)
|
||||
|
||||
if nmeaMode==0 or nmeaMode==2 then
|
||||
--解析一行NEMA数据
|
||||
parseNmea(nemaStr)
|
||||
end
|
||||
if (nmeaMode==1 or nmeaMode==2) and nmeaCbFnc then
|
||||
nmeaCbFnc(nemaStr)
|
||||
end
|
||||
d1,d2 = sfind(cacheData,"\r\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function writeData(data)
|
||||
uart.write(uartID,data)
|
||||
log.info("gpsZkw.writeData",data:toHex())
|
||||
end
|
||||
|
||||
|
||||
-- GPS串口写命令操作
|
||||
-- @number class,消息类
|
||||
-- @number is,消息编号
|
||||
-- @string payload,有效载荷
|
||||
-- @return nil
|
||||
-- @usage gps.writeCmd(cmd)
|
||||
function writeCasic(class,id,payload)
|
||||
local tmp = pack.pack("<bbHbbA",0xBA,0xCE,payload:len(),class,id,payload)
|
||||
|
||||
local checkSum = bit.lshift(id,24)+bit.lshift(class,16)+payload:len()
|
||||
for i=1,payload:len(),4 do
|
||||
checkSum = checkSum + payload:byte(i) + bit.lshift(payload:byte(i+1),8) + bit.lshift(payload:byte(i+2),16) + bit.lshift(payload:byte(i+3),24)
|
||||
end
|
||||
|
||||
tmp = tmp..pack.pack("<I",checkSum)
|
||||
|
||||
uart.write(uartID,tmp)
|
||||
--log.info("gpsZkw.writeCasic",tmp)
|
||||
log.info("gpsZkw.writeCasic",tmp:toHex())
|
||||
end
|
||||
|
||||
-- GPS串口写命令操作
|
||||
-- @string cmd,GPS指令(cmd格式:"$PCAS04,7*XX\r\n")
|
||||
-- @bool isFull,cmd是否为完整的指令格式,包括校验和以及\r\n;true表示完整,false或者nil为不完整
|
||||
-- @return nil
|
||||
-- @usage gps.writeCmd(cmd)
|
||||
function writeCmd(cmd,isFull)
|
||||
local tmp = cmd
|
||||
if not isFull then
|
||||
tmp = 0
|
||||
for i=2,cmd:len()-1 do
|
||||
tmp = bit.bxor(tmp,cmd:byte(i))
|
||||
end
|
||||
tmp = cmd..(string.format("%02X",tmp)):upper().."\r\n"
|
||||
end
|
||||
uart.write(uartID,tmp)
|
||||
log.info("gpsZkw.writecmd",tmp)
|
||||
--log.info("gpsZkw.writeCmd",tmp:toHex())
|
||||
end
|
||||
|
||||
function writePendingCmds()
|
||||
if not aerialModeSetted and aerialModeStr~="" then writeCmd(aerialModeStr) aerialModeSetted=true end
|
||||
--if not runModeSetted and runModeStr~="" then writeCmd(runModeStr) runModeSetted=true end
|
||||
if not nmeaReportSetted and nmeaReportStr~="" then writeCmd(nmeaReportStr) nmeaReportSetted=true end
|
||||
if not nmeaReportFreqSetted and nmeaReportFreqStr~="" then writeCmd(nmeaReportFreqStr) nmeaReportFreqSetted=true end
|
||||
if not setBaudrateSetted and setBaudrateStr~="" then writeCmd(setBaudrateStr) setBaudrateSetted=true end
|
||||
end
|
||||
|
||||
local function _open()
|
||||
if openFlag then return end
|
||||
pm.wake("gps.lua")
|
||||
uart.setup(uartID,uartBaudrate,uartDatabits,uartParity,uartStopbits)
|
||||
if not taskFlag then
|
||||
taskFlag =true
|
||||
sys.taskInit(taskRead)
|
||||
end
|
||||
if powerCbFnc then
|
||||
powerCbFnc(true)
|
||||
else
|
||||
pmd.ldoset(10,pmd.LDO_VIBR)
|
||||
rtos.sys32k_clk_out(1)
|
||||
end
|
||||
openFlag = true
|
||||
workAbnormalTimerId = sys.timerStart(sys.publish,8000,"GPS_WORK_ABNORMAL_IND")
|
||||
sys.publish("GPS_STATE","OPEN")
|
||||
fixFlag,filteredFlag = false
|
||||
Ggalng,Ggalat,Gsv,Sep = "","",""
|
||||
log.info("gpsZkw._open")
|
||||
end
|
||||
|
||||
local function _close()
|
||||
if not openFlag then return end
|
||||
if powerCbFnc then
|
||||
powerCbFnc(false)
|
||||
else
|
||||
pmd.ldoset(0,pmd.LDO_VIBR)
|
||||
rtos.sys32k_clk_out(0)
|
||||
end
|
||||
uart.close(uartID)
|
||||
pm.sleep("gps.lua")
|
||||
openFlag = false
|
||||
sys.publish("GPS_STATE","CLOSE",fixFlag)
|
||||
stopWorkAbnormalTimer()
|
||||
fixFlag,filteredFlag = false
|
||||
sys.timerStop(filterTimerFnc)
|
||||
Ggalng,Ggalat,Gsv,Sep = "","",""
|
||||
aerialModeSetted,runModeSetted,nmeaReportSetted,nmeaReportFreqSetted = nil
|
||||
log.info("gpsZkw._close")
|
||||
end
|
||||
|
||||
|
||||
--- GPS应用模式1.
|
||||
--
|
||||
-- 打开GPS后,GPS定位成功时,如果有回调函数,会调用回调函数
|
||||
--
|
||||
-- 使用此应用模式调用gpsZkw.open打开的“GPS应用”,必须主动调用gpsZkw.close或者gpsZkw.closeAll才能关闭此“GPS应用”,主动关闭时,即使有回调函数,也不会调用回调函数
|
||||
DEFAULT = 1
|
||||
--- GPS应用模式2.
|
||||
--
|
||||
-- 打开GPS后,如果在GPS开启最大时长到达时,没有定位成功,如果有回调函数,会调用回调函数,然后自动关闭此“GPS应用”
|
||||
--
|
||||
-- 打开GPS后,如果在GPS开启最大时长内,定位成功,如果有回调函数,会调用回调函数,然后自动关闭此“GPS应用”
|
||||
--
|
||||
-- 打开GPS后,在自动关闭此“GPS应用”前,可以调用gpsZkw.close或者gpsZkw.closeAll主动关闭此“GPS应用”,主动关闭时,即使有回调函数,也不会调用回调函数
|
||||
TIMERORSUC = 2
|
||||
--- GPS应用模式3.
|
||||
--
|
||||
-- 打开GPS后,在GPS开启最大时长时间到达时,无论是否定位成功,如果有回调函数,会调用回调函数,然后自动关闭此“GPS应用”
|
||||
--
|
||||
-- 打开GPS后,在自动关闭此“GPS应用”前,可以调用gpsZkw.close或者gpsZkw.closeAll主动关闭此“GPS应用”,主动关闭时,即使有回调函数,也不会调用回调函数
|
||||
TIMER = 3
|
||||
|
||||
--“GPS应用”表
|
||||
local tList = {}
|
||||
|
||||
--[[
|
||||
函数名:delItem
|
||||
功能 :从“GPS应用”表中删除一项“GPS应用”,并不是真正的删除,只是设置一个无效标志
|
||||
参数 :
|
||||
mode:GPS应用模式
|
||||
para:
|
||||
para.tag:“GPS应用”标记
|
||||
para.val:GPS开启最大时长
|
||||
para.cb:回调函数
|
||||
返回值:无
|
||||
]]
|
||||
local function delItem(mode,para)
|
||||
for i=1,#tList do
|
||||
--标志有效 并且 GPS应用模式相同 并且 “GPS应用”标记相同
|
||||
if tList[i].flag and tList[i].mode==mode and tList[i].para.tag==para.tag then
|
||||
--设置无效标志
|
||||
tList[i].flag,tList[i].delay = false
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:addItem
|
||||
功能 :新增一项“GPS应用”到“GPS应用”表
|
||||
参数 :
|
||||
mode:GPS应用模式
|
||||
para:
|
||||
para.tag:“GPS应用”标记
|
||||
para.val:GPS开启最大时长
|
||||
para.cb:回调函数
|
||||
返回值:无
|
||||
]]
|
||||
local function addItem(mode,para)
|
||||
--删除相同的“GPS应用”
|
||||
delItem(mode,para)
|
||||
local item,i,fnd = {flag=true, mode=mode, para=para}
|
||||
--如果是TIMERORSUC或者TIMER模式,初始化GPS工作剩余时间
|
||||
if mode==TIMERORSUC or mode==TIMER then item.para.remain = para.val end
|
||||
for i=1,#tList do
|
||||
--如果存在无效的“GPS应用”项,直接使用此位置
|
||||
if not tList[i].flag then
|
||||
tList[i] = item
|
||||
fnd = true
|
||||
break
|
||||
end
|
||||
end
|
||||
--新增一项
|
||||
if not fnd then table.insert(tList,item) end
|
||||
end
|
||||
|
||||
local function existTimerItem()
|
||||
for i=1,#tList do
|
||||
if tList[i].flag and (tList[i].mode==TIMERORSUC or tList[i].mode==TIMER or tList[i].para.delay) then return true end
|
||||
end
|
||||
end
|
||||
|
||||
local function timerFnc()
|
||||
for i=1,#tList do
|
||||
if tList[i].flag then
|
||||
log.info("gpsZkw.timerFnc@"..i,tList[i].mode,tList[i].para.tag,tList[i].para.val,tList[i].para.remain,tList[i].para.delay)
|
||||
local rmn,dly,md,cb = tList[i].para.remain,tList[i].para.delay,tList[i].mode,tList[i].para.cb
|
||||
|
||||
if rmn and rmn>0 then
|
||||
tList[i].para.remain = rmn-1
|
||||
end
|
||||
if dly and dly>0 then
|
||||
tList[i].para.delay = dly-1
|
||||
end
|
||||
rmn = tList[i].para.remain
|
||||
|
||||
if isFix() and md==TIMER and rmn==0 and not tList[i].para.delay then
|
||||
tList[i].para.delay = 1
|
||||
end
|
||||
|
||||
dly = tList[i].para.delay
|
||||
if isFix() then
|
||||
if dly and dly==0 then
|
||||
if cb then cb(tList[i].para.tag) end
|
||||
if md == DEFAULT then
|
||||
tList[i].para.delay = nil
|
||||
else
|
||||
close(md,tList[i].para)
|
||||
end
|
||||
end
|
||||
else
|
||||
if rmn and rmn == 0 then
|
||||
if cb then cb(tList[i].para.tag) end
|
||||
close(md,tList[i].para)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if existTimerItem() then sys.timerStart(timerFnc,1000) end
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:statInd
|
||||
功能 :处理GPS定位成功的消息
|
||||
参数 :
|
||||
evt:GPS消息类型
|
||||
返回值:无
|
||||
]]
|
||||
local function statInd(evt)
|
||||
--定位成功的消息
|
||||
if evt == "LOCATION_SUCCESS" then
|
||||
for i=1,#tList do
|
||||
log.info("gpsZkw.statInd@"..i,tList[i].flag,tList[i].mode,tList[i].para.tag,tList[i].para.val,tList[i].para.remain,tList[i].para.delay,tList[i].para.cb)
|
||||
if tList[i].flag then
|
||||
if tList[i].mode ~= TIMER then
|
||||
tList[i].para.delay = 1
|
||||
if tList[i].mode == DEFAULT then
|
||||
if existTimerItem() then sys.timerStart(timerFnc,1000) end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- 打开一个“GPS应用”
|
||||
-- “GPS应用”:指的是使用GPS功能的一个应用
|
||||
-- 例如,假设有如下3种需求,要打开GPS,则一共有3个“GPS应用”:
|
||||
-- “GPS应用1”:每隔1分钟打开一次GPS
|
||||
-- “GPS应用2”:设备发生震动时打开GPS
|
||||
-- “GPS应用3”:收到一条特殊短信时打开GPS
|
||||
-- 只有所有“GPS应用”都关闭了,才会去真正关闭GPS
|
||||
-- 每个“GPS应用”打开或者关闭GPS时,最多有4个参数,其中 GPS应用模式和GPS应用标记 共同决定了一个唯一的“GPS应用”:
|
||||
-- 1、GPS应用模式(必选)
|
||||
-- 2、GPS应用标记(必选)
|
||||
-- 3、GPS开启最大时长[可选]
|
||||
-- 4、回调函数[可选]
|
||||
-- 例如gpsZkw.open(gpsZkw.TIMERORSUC,{tag="TEST",val=120,cb=testGpsCb})
|
||||
-- gpsZkw.TIMERORSUC为GPS应用模式,"TEST"为GPS应用标记,120秒为GPS开启最大时长,testGpsCb为回调函数
|
||||
-- @number mode GPS应用模式,支持gpsZkw.DEFAULT,gpsZkw.TIMERORSUC,gpsZkw.TIMER三种
|
||||
-- @param para table类型,GPS应用参数
|
||||
-- para.tag:string类型,GPS应用标记
|
||||
-- para.val:number类型,GPS应用开启最大时长,mode参数为gpsZkw.TIMERORSUC或者gpsZkw.TIMER时,此值才有意义
|
||||
-- para.cb:GPS应用结束时的回调函数,回调函数的调用形式为para.cb(para.tag)
|
||||
-- @return nil
|
||||
-- @usage gpsZkw.open(gpsZkw.DEFAULT,{tag="TEST1",cb=test1Cb})
|
||||
-- @usage gpsZkw.open(gpsZkw.TIMERORSUC,{tag="TEST2",val=60,cb=test2Cb})
|
||||
-- @usage gpsZkw.open(gpsZkw.TIMER,{tag="TEST3",val=120,cb=test3Cb})
|
||||
-- @see DEFAULT,TIMERORSUC,TIMER
|
||||
function open(mode,para)
|
||||
assert((para and type(para) == "table" and para.tag and type(para.tag) == "string"),"gps.open para invalid")
|
||||
log.info("gpsZkw.open",mode,para.tag,para.val,para.cb)
|
||||
--如果GPS定位成功
|
||||
if isFix() then
|
||||
if mode~=TIMER then
|
||||
--执行回调函数
|
||||
if para.cb then para.cb(para.tag) end
|
||||
if mode==TIMERORSUC then return end
|
||||
end
|
||||
end
|
||||
addItem(mode,para)
|
||||
--真正去打开GPS
|
||||
_open()
|
||||
--启动1秒的定时器
|
||||
if existTimerItem() and not sys.timerIsActive(timerFnc) then
|
||||
sys.timerStart(timerFnc,1000)
|
||||
end
|
||||
end
|
||||
|
||||
--- 关闭一个“GPS应用”
|
||||
-- 只是从逻辑上关闭一个GPS应用,并不一定真正关闭GPS,是有所有的GPS应用都处于关闭状态,才会去真正关闭GPS
|
||||
-- @number mode GPS应用模式,支持gps.DEFAULT,gps.TIMERORSUC,gps.TIMER三种
|
||||
-- @param para table类型,GPS应用参数
|
||||
-- para.tag:string类型,GPS应用标记
|
||||
-- para.val:number类型,GPS应用开启最大时长,mode参数为gps.TIMERORSUC或者gps.TIMER时,此值才有意义;使用close接口时,不需要传入此参数
|
||||
-- para.cb:GPS应用结束时的回调函数,回调函数的调用形式为para.cb(para.tag);使用close接口时,不需要传入此参数
|
||||
-- @return nil
|
||||
-- @usage GPS应用模式和GPS应用标记唯一确定一个“GPS应用”,调用本接口关闭时,mode和para.tag要和gpsZkw.open打开一个“GPS应用”时传入的mode和para.tag保持一致
|
||||
-- @usage gpsZkw.close(gpsZkw.DEFAULT,{tag="TEST1"})
|
||||
-- @usage gpsZkw.close(gpsZkw.TIMERORSUC,{tag="TEST2"})
|
||||
-- @usage gpsZkw.close(gpsZkw.TIMER,{tag="TEST3"})
|
||||
-- @see open,DEFAULT,TIMERORSUC,TIMER
|
||||
function close(mode,para)
|
||||
assert((para and type(para)=="table" and para.tag and type(para.tag)=="string"),"gps.close para invalid")
|
||||
log.info("gpsZkw.close",mode,para.tag,para.val,para.cb)
|
||||
--删除此“GPS应用”
|
||||
delItem(mode,para)
|
||||
local valid,i
|
||||
for i=1,#tList do
|
||||
if tList[i].flag then
|
||||
valid = true
|
||||
end
|
||||
end
|
||||
--如果没有一个“GPS应用”有效,则关闭GPS
|
||||
if not valid then _close() end
|
||||
end
|
||||
|
||||
--- 关闭所有“GPS应用”
|
||||
-- @return nil
|
||||
-- @usage gpsZkw.closeAll()
|
||||
-- @see open,DEFAULT,TIMERORSUC,TIMER
|
||||
function closeAll()
|
||||
for i=1,#tList do
|
||||
if tList[i].flag and tList[i].para.cb then tList[i].para.cb(tList[i].para.tag) end
|
||||
close(tList[i].mode,tList[i].para)
|
||||
end
|
||||
end
|
||||
|
||||
--- 判断一个“GPS应用”是否处于激活状态
|
||||
-- @number mode GPS应用模式,支持gps.DEFAULT,gps.TIMERORSUC,gps.TIMER三种
|
||||
-- @param para table类型,GPS应用参数
|
||||
-- para.tag:string类型,GPS应用标记
|
||||
-- para.val:number类型,GPS应用开启最大时长,mode参数为gpsZkw.TIMERORSUC或者gpsZkw.TIMER时,此值才有意义;使用isActive接口时,不需要传入此参数
|
||||
-- para.cb:GPS应用结束时的回调函数,回调函数的调用形式为para.cb(para.tag);使用isActive接口时,不需要传入此参数
|
||||
-- @return bool result,处于激活状态返回true,否则返回nil
|
||||
-- @usage GPS应用模式和GPS应用标记唯一确定一个“GPS应用”,调用本接口查询状态时,mode和para.tag要和gpsZkw.open打开一个“GPS应用”时传入的mode和para.tag保持一致
|
||||
-- @usage gpsZkw.isActive(gpsZkw.DEFAULT,{tag="TEST1"})
|
||||
-- @usage gpsZkw.isActive(gpsZkw.TIMERORSUC,{tag="TEST2"})
|
||||
-- @usage gpsZkw.isActive(gpsZkw.TIMER,{tag="TEST3"})
|
||||
-- @see open,DEFAULT,TIMERORSUC,TIMER
|
||||
function isActive(mode,para)
|
||||
assert((para and type(para)=="table" and para.tag and type(para.tag)=="string"),"gps.isActive para invalid")
|
||||
for i=1,#tList do
|
||||
if tList[i].flag and tList[i].mode==mode and tList[i].para.tag==para.tag then return true end
|
||||
end
|
||||
end
|
||||
|
||||
--- 设置GPS模块供电控制的回调函数
|
||||
-- 如果使用的是Air800,或者供电控制使用的是LDO_VCAM,则打开GPS应用前不需要调用此接口进行设置
|
||||
-- 否则在调用gpsZkw.open前,使用此接口,传入自定义的供电控制函数cbFnc,GPS开启时,gpsZkw.lua自动执行cbFnc(true),GPS关闭时,gpsZkw.lua自动执行cbFnc(false)
|
||||
-- @param cbFnc function类型,用户自定义的GPS供电控制函数
|
||||
-- @return nil
|
||||
-- @usage gpsZkw.setPowerCbFnc(cbFnc)
|
||||
function setPowerCbFnc(cbFnc)
|
||||
powerCbFnc = cbFnc
|
||||
end
|
||||
|
||||
--- 设置GPS模块和GSM模块之间数据通信的串口参数
|
||||
-- 如果使用的是Air800,或者使用的UART2(波特率115200,数据位8,无检验位,停止位1),则打开GPS应用前不需要调用此接口进行设置
|
||||
-- 否则在调用gpsZkw.open前,使用此接口,传入UART参数
|
||||
-- @number id UART ID,支持1和2,1表示UART1,2表示UART2
|
||||
-- @number baudrate 波特率,支持1200,2400,4800,9600,10400,14400,19200,28800,38400,57600,76800,115200,230400,460800,576000,921600,1152000,4000000
|
||||
-- @number databits 数据位,支持7,8
|
||||
-- @number parity 校验位,支持uart.PAR_NONE,uart.PAR_EVEN,uart.PAR_ODD
|
||||
-- @number stopbits 停止位,支持uart.STOP_1,uart.STOP_2
|
||||
-- @return nil
|
||||
-- @usage gpsZkw.setUart(2,115200,8,uart.PAR_NONE,uart.STOP_1)
|
||||
function setUart(id,baudrate,databits,parity,stopbits)
|
||||
uartID,uartBaudrate,uartDatabits,uartParity,uartStopbits = id,baudrate,databits,parity,stopbits
|
||||
end
|
||||
|
||||
--- 设置GPS模块搜星模式.
|
||||
-- 如果使用的是Air820,不调用此接口配置,则默认同时开启GPS、北斗和glonass定位
|
||||
-- @number gps GPS定位系统,1是打开,0是关闭
|
||||
-- @number beidou 中国北斗定位系统,1是打开,0是关闭
|
||||
-- @number glonass 俄罗斯Glonass定位系统,1是打开,0是关闭
|
||||
-- @return nil
|
||||
-- @usage gpsZkw.setAeriaMode(1,1,0)
|
||||
function setAerialMode(gps,beidou,glonass)
|
||||
local gps = gps or 0
|
||||
local beidou = (beidou or 0)*2
|
||||
local glonass = (glonass or 0)*4
|
||||
|
||||
local tmpStr = "$PCAS04,"..(gps+beidou+glonass).."*"
|
||||
if tmpStr~=aerialModeStr then
|
||||
aerialModeStr,aerialModeSetted = tmpStr
|
||||
end
|
||||
end
|
||||
|
||||
-- 改变GPS模块串口波特率操作
|
||||
-- @string cmd,GPS指令(cmd格式:$PCAS01,br*CS<CR><LF>)
|
||||
-- @bumber br 波特率配置 0=4800bps 1=9600bps 2=19200bps 3=38400bps 4=57600bps 5=115200bps
|
||||
-- @return nil
|
||||
-- @usage gpsZkw.setBaudrate(br)
|
||||
function setBaudrate(br)
|
||||
local br = br or 0
|
||||
|
||||
local tmpStr = "$PCAS01,"..br.."*"
|
||||
if tmpStr~=setBaudrateStr then
|
||||
setBaudrateStr,setBaudrateSetted = tmpStr
|
||||
end
|
||||
end
|
||||
|
||||
--- 设置NMEA数据处理模式.
|
||||
-- 如果不调用此接口配置,则默认仅gpsZkw.lua内部处理NMEA数据
|
||||
-- @number mode NMEA数据处理模式,0表示仅gpsZkw.lua内部处理,1表示仅用户自己处理,2表示gpsZkw.lua和用户同时处理
|
||||
-- @param cbFnc function类型,用户处理一条NMEA数据的回调函数,mode为1和2时,此值才有意义
|
||||
-- @return nil
|
||||
-- @usage gpsZkw.setNmeaMode(0)
|
||||
-- @usage gpsZkw.setNmeaMode(1,cbFnc)
|
||||
-- @usage gpsZkw.setNmeaMode(2,cbFnc)
|
||||
function setNmeaMode(mode,cbFnc)
|
||||
nmeaMode,nmeaCbFnc = mode,cbFnc
|
||||
end
|
||||
|
||||
--[[
|
||||
-- 设置GPS模块的运行模式.
|
||||
-- 如果不调用此接口配置,则默认为正常运行模式
|
||||
-- @number mode,运行模式
|
||||
-- 0:正常运行模式
|
||||
-- 1:周期超低功耗跟踪模式
|
||||
-- 2:周期低功耗模式
|
||||
-- 4:直接进入超低功耗跟踪模式
|
||||
-- 8:自动低功耗模式,可以通过串口唤醒
|
||||
-- 9:自动超低功耗跟踪模式,需要force on来唤醒
|
||||
-- @number runTm,单位毫秒,mode为0时表示NEMA数据的上报间隔,mode为1或者2时表示运行时长,其余mode时此值无意义
|
||||
-- @number sleepTm,单位毫秒,mode为1或者2时表示运行时长,其余mode时此值无意义
|
||||
-- @return nil
|
||||
-- @usage gps.setRunMode(0,1000)
|
||||
-- @usage gps.setRunMode(1,5000,2000)
|
||||
function setRunMode(mode,runTm,sleepTm)
|
||||
local rt,st = runTm or "",sleepTm or ""
|
||||
if mode==0 and rt then
|
||||
if rt>10000 then rt=10000 end
|
||||
if rt<200 then rt=200 end
|
||||
nmeaReportStr = "$PGKC101,"..rt.."*"
|
||||
end
|
||||
|
||||
local tmpStr = "$PGKC105,"..mode..((mode==1 or mode==2) and (","..rt..","..st) or "").."*"
|
||||
if tmpStr~=runModeStr then
|
||||
runModeStr,runModeSetted = tmpStr
|
||||
end
|
||||
end
|
||||
]]
|
||||
|
||||
--- 设置NEMA语句的输出频率.
|
||||
-- @number[opt=1] rmc 单位秒,RMC语句输出频率,取值范围0到9之间的整数,0表示不输出
|
||||
-- @number[opt=1] gga 单位秒,GGA语句输出频率,取值范围0到9之间的整数,0表示不输出
|
||||
-- @number[opt=1] gsa 单位秒,GSA语句输出频率,取值范围0到9之间的整数,0表示不输出
|
||||
-- @number[opt=1] gsv 单位秒,GSV语句输出频率,取值范围0到9之间的整数,0表示不输出
|
||||
-- @number[opt=0] vtg 单位秒,VTG语句输出频率,取值范围0到9之间的整数,0表示不输出
|
||||
-- @number[opt=0] gll 单位秒,GLL语句输出频率,取值范围0到9之间的整数,0表示不输出
|
||||
-- @return nil
|
||||
-- @usage gpsZkw.setNemaReportFreq(5,0,0,0,0,0)
|
||||
function setNemaReportFreq(rmc,gga,gsa,gsv,vtg,gll)
|
||||
tmpStr = "$PCAS03,,,,,,,,,,,1*"
|
||||
if tmpStr~=nmeaReportFreqStr then
|
||||
nmeaReportFreqStr,nmeaReportFreqSetted = tmpStr
|
||||
end
|
||||
end
|
||||
|
||||
--- 设置GPS定位成功后经纬度的过滤时间.
|
||||
-- @number[opt=0] seconds 单位秒,GPS定位成功后,丢弃前seconds秒的位置信息
|
||||
-- @return nil
|
||||
-- @usage gpsZkw.setLocationFilter(2)
|
||||
function setLocationFilter(seconds)
|
||||
filterSeconds = seconds or 0
|
||||
end
|
||||
|
||||
function setFastFix(lat,lng,tm)
|
||||
local t = tm.year..","..tm.month..","..tm.day..","..tm.hour..","..tm.min..","..tm.sec.."*"
|
||||
log.info("gpsZkw.setFastFix",lat,lng,t)
|
||||
log.info("gpsZkw.setFastFix",lat,lng, json.encode(tm))
|
||||
if zkw530z and zkw530z.settime and zkw530z.setlocation and zkw530z.getaidini ~= nil then
|
||||
if lng~=nil and lat~=nil and lng~="" and lat ~="" then
|
||||
zkw530z.settime(tm.year,tm.month,tm.day,tm.hour,tm.min,tm.sec)
|
||||
zkw530z.setlocation(lat,lng)--参数:lat,lon
|
||||
local tmpbuf = zkw530z.getaidini()
|
||||
writeData(tmpbuf)
|
||||
log.info("gpsZkw.writecmd",tmpbuf:toHex())
|
||||
end
|
||||
else
|
||||
log.info("zkw530z.settime is not supported!")
|
||||
end
|
||||
end
|
||||
|
||||
--- 获取GPS模块是否处于开启状态
|
||||
-- @return bool result,true表示开启状态,false或者nil表示关闭状态
|
||||
-- @usage gpsZkw.isOpen()
|
||||
function isOpen()
|
||||
return openFlag
|
||||
end
|
||||
|
||||
--- 获取GPS模块是否定位成功
|
||||
-- @return bool result,true表示定位成功,false或者nil表示定位失败
|
||||
-- @usage gpsZkw.isFix()
|
||||
function isFix()
|
||||
return fixFlag
|
||||
end
|
||||
|
||||
--- 获取GPS模块是否首次定位成功过
|
||||
-- @return bool result,true表示曾经定位成功
|
||||
-- @usage gpsZkw.isOnece()
|
||||
function isOnece()
|
||||
return fixOnece
|
||||
end
|
||||
|
||||
-- 度分格式转换为度格式
|
||||
-- @string inStr 度分格式的位置
|
||||
-- @return string,度格式的位置
|
||||
-- @usage degreeMinuteToDegree("3114.50931")--->"31.2418218",31度14.50931分转换为31.2418218度
|
||||
-- @usage degreeMinuteToDegree("12128.44954")--->"121.4741590",121度28.44954分转换为121.4741590度
|
||||
local function degreeMinuteToDegree(inStr)
|
||||
local integer,fraction = smatch(inStr,"(%d+)%.(%d+)")
|
||||
if integer and fraction then
|
||||
local intLen = slen(integer)
|
||||
if intLen~=4 and intLen~=5 then log.error("gps.degreeMinuteToDegree integer error",inStr) return "" end
|
||||
if slen(fraction)<5 then fraction = fraction..srep("0",5-slen(fraction)) end
|
||||
fraction = ssub(fraction,1,5)
|
||||
local temp = tonumber(ssub(integer,intLen-1,intLen)..fraction)*10
|
||||
fraction = tostring((temp-(temp%6))/6)
|
||||
local fracLen = slen(fraction)
|
||||
if fracLen>7 then
|
||||
fraction = ssub(fraction,1,7)
|
||||
elseif fracLen<7 then
|
||||
fraction = srep("0",7-fracLen)..fraction
|
||||
end
|
||||
return ssub(integer,1,intLen-2).."."..fraction
|
||||
end
|
||||
|
||||
return ""
|
||||
end
|
||||
|
||||
--- 获取度格式的经纬度信息
|
||||
-- @string[opt=nil] typ 返回的经纬度格式,typ为"DEGREE_MINUTE"时表示返回度分格式,其余表示返回度格式
|
||||
-- @return table location
|
||||
-- 例如typ为"DEGREE_MINUTE"时返回{lngType="E",lng="12128.44954",latType="N",lat="3114.50931"}
|
||||
-- 例如typ不是"DEGREE_MINUTE"时返回{lngType="E",lng="121.123456",latType="N",lat="31.123456"}
|
||||
-- lngType:string类型,表示经度类型,取值"E","W"
|
||||
-- lng:string类型,表示度格式的经度值,无效时为""
|
||||
-- latType:string类型,表示纬度类型,取值"N","S"
|
||||
-- lat:string类型,表示度格式的纬度值,无效时为""
|
||||
-- @usage gpsZkw.getLocation()
|
||||
function getLocation(typ)
|
||||
return {
|
||||
lngType=longitudeType,
|
||||
lng=isFix() and (typ=="DEGREE_MINUTE" and longitude or degreeMinuteToDegree(longitude)) or "",
|
||||
latType=latitudeType,
|
||||
lat=isFix() and (typ=="DEGREE_MINUTE" and latitude or degreeMinuteToDegree(latitude)) or ""
|
||||
}
|
||||
end
|
||||
|
||||
function getLastLocation(typ)
|
||||
if typ=="DEGREE_MINUTE" then
|
||||
return {
|
||||
lngType=longitudeType,
|
||||
lng=longitude,
|
||||
latType=latitudeType,
|
||||
lat=latitude
|
||||
}
|
||||
else
|
||||
return (longitude and longitude~="") and degreeMinuteToDegree(longitude) or "", (latitude and latitude~="") and degreeMinuteToDegree(latitude) or ""
|
||||
end
|
||||
end
|
||||
|
||||
--- 获取海拔
|
||||
-- @return number altitude,海拔,单位米
|
||||
-- @usage gpsZkw.getAltitude()
|
||||
function getAltitude()
|
||||
return tonumber(smatch(altitude,"(%d+)") or "0")
|
||||
end
|
||||
|
||||
--- 获取速度
|
||||
-- @return number kmSpeed,第一个返回值为公里每小时的速度
|
||||
-- @return number nmSpeed,第二个返回值为海里每小时的速度
|
||||
-- @usage gpsZkw.getSpeed()
|
||||
function getSpeed()
|
||||
local integer = tonumber(smatch(speed,"(%d+)") or "0")
|
||||
return (integer*1852 - (integer*1852 %1000))/1000,integer
|
||||
end
|
||||
|
||||
--- 获取原始速度,字符串带浮点
|
||||
-- @return number speed 海里每小时的速度
|
||||
-- @usage gpsZkw.getOrgSpeed()
|
||||
function getOrgSpeed()
|
||||
return speed
|
||||
end
|
||||
|
||||
--- 获取方向角
|
||||
-- @return number course,方向角
|
||||
-- @usage gpsZkw.getCourse()
|
||||
function getCourse()
|
||||
return tonumber(smatch(course,"(%d+)") or "0")
|
||||
end
|
||||
|
||||
-- 获取所有可见卫星的最大信号强度
|
||||
-- @return number strength,最大信号强度
|
||||
-- @usage gpsZkw.getMaxSignalStrength()
|
||||
function getMaxSignalStrength()
|
||||
return maxSignalStrength
|
||||
end
|
||||
|
||||
--- 获取可见卫星的个数
|
||||
-- @return number count,可见卫星的个数
|
||||
-- @usage gpsZkw.getViewedSateCnt()
|
||||
function getViewedSateCnt()
|
||||
return tonumber(viewedGpsSateCnt)+tonumber(viewedBdSateCnt)
|
||||
end
|
||||
|
||||
--- 获取定位使用的卫星个数
|
||||
-- @return number count,定位使用的卫星个数
|
||||
-- @usage gpsZkw.getUsedSateCnt()
|
||||
function getUsedSateCnt()
|
||||
return tonumber(usedSateCnt)
|
||||
end
|
||||
|
||||
--- 获取GGA语句中度分格式的经纬度信息
|
||||
-- @return string lng,度分格式的经度值(dddmm.mmmm),西经会添加一个-前缀,无效时为"";例如"12112.3456"表示东经121度12.3456分,"-12112.3456"表示西经121度12.3456分
|
||||
-- @return string lat,度分格式的纬度值(ddmm.mmmm),南纬会添加一个-前缀,无效时为"";例如"3112.3456"表示北纬31度12.3456分,"-3112.3456"表示南纬31度12.3456分
|
||||
-- @usage gpsZkw.getGgaloc()
|
||||
function getGgaloc()
|
||||
return Ggalng or "",Ggalat or ""
|
||||
end
|
||||
|
||||
--- 获取RMC语句中的UTC时间
|
||||
-- 只有同时满足如下两个条件,返回值才有效
|
||||
-- 1、开启了GPS,并且定位成功
|
||||
-- 2、调用setParseItem接口,第一个参数设置为true
|
||||
-- @return table utcTime,UTC时间,nil表示无效,例如{year=2018,month=4,day=24,hour=11,min=52,sec=10}
|
||||
-- @usage gpsZkw.getUtcTime()
|
||||
function getUtcTime()
|
||||
return UtcTime
|
||||
end
|
||||
|
||||
--- 获取定位使用的大地高
|
||||
-- @return number sep,大地高
|
||||
-- @usage gpsZkw.getSep()
|
||||
function getSep()
|
||||
return tonumber(Sep or "0")
|
||||
end
|
||||
|
||||
--- 获取GSA语句中的可见卫星号
|
||||
-- 只有同时满足如下两个条件,返回值才有效
|
||||
-- 1、开启了GPS,并且定位成功
|
||||
-- 2、调用setParseItem接口,第三个参数设置为true
|
||||
-- @return string viewedSateId,可用卫星号,""表示无效
|
||||
-- @usage gpsZkw.getSateSn()
|
||||
function getSateSn()
|
||||
return SateSn or ""
|
||||
end
|
||||
|
||||
--- 获取GSV语句中的可见卫星的信噪比
|
||||
-- 只有同时满足如下两个条件,返回值才有效
|
||||
-- 1、开启了GPS,并且定位成功
|
||||
-- 2、调用setParseItem接口,第二个参数设置为true
|
||||
-- @return string gsv,信噪比
|
||||
-- @usage gpsZkw.getGsv()
|
||||
function getGsv()
|
||||
return Gsv or ""
|
||||
end
|
||||
|
||||
--- 设置是否需要解析的字段
|
||||
-- @bool[opt=nil] utcTime 是否解析RMC语句中的UTC时间,true表示解析,false或者nil不解析
|
||||
-- @bool[opt=nil] gsv 是否解析GSV语句,true表示解析,false或者nil不解析
|
||||
-- @bool[opt=nil] gsaId 是否解析GSA语句中的卫星ID,true表示解析,false或者nil不解析
|
||||
-- @usage gpsZkw.setParseItem(true,true,true)
|
||||
function setParseItem(utcTime,gsv,gsaId)
|
||||
psUtcTime,psGsv,psSn = utcTime,gsv,gsaId
|
||||
end
|
||||
|
||||
|
||||
function init()
|
||||
sys.subscribe("GPS_STATE",statInd)
|
||||
end
|
||||
|
||||
function unInit()
|
||||
sys.unsubscribe("GPS_STATE",statInd)
|
||||
closeAll()
|
||||
end
|
||||
|
||||
init()
|
||||
310
4G/源代码/lib/http.lua
Normal file
310
4G/源代码/lib/http.lua
Normal file
@@ -0,0 +1,310 @@
|
||||
--- 模块功能:HTTP客户端
|
||||
-- @module http
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2017.10.23
|
||||
require"socket"
|
||||
require"utils"
|
||||
module(..., package.seeall)
|
||||
|
||||
local function response(client,cbFnc,result,prompt,head,body)
|
||||
if not result then log.error("http.response",result,prompt) end
|
||||
if cbFnc then cbFnc(result,prompt,head,body) end
|
||||
if client then client:close() end
|
||||
end
|
||||
|
||||
local function receive(client,timeout,cbFnc,result,prompt,head,body)
|
||||
local res,data = client:recv(timeout)
|
||||
if not res then
|
||||
response(client,cbFnc,result,prompt or "receive timeout",head,body)
|
||||
end
|
||||
return res,data
|
||||
end
|
||||
|
||||
local function getFileBase64Len(s)
|
||||
if s then return (io.fileSize(s)+2)/3*4 end
|
||||
end
|
||||
|
||||
local function taskClient(method,protocal,auth,host,port,path,cert,head,body,timeout,cbFnc,rcvFilePath,tCoreExtPara)
|
||||
log.info("http path",path)
|
||||
while not socket.isReady() do
|
||||
if not sys.waitUntil("IP_READY_IND",timeout) then return response(nil,cbFnc,false,"network not ready") end
|
||||
end
|
||||
|
||||
--计算body长度
|
||||
local bodyLen = 0
|
||||
if body then
|
||||
if type(body)=="string" then
|
||||
bodyLen = body:len()
|
||||
elseif type(body)=="table" then
|
||||
for i=1,#body do
|
||||
bodyLen = bodyLen + (type(body[i])=="string" and string.len(body[i]) or getFileBase64Len(body[i].file_base64) or io.fileSize(body[i].file))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--重构head
|
||||
local heads = head or {}
|
||||
if not heads.Host then heads["Host"] = (port ~= 80 and port ~= 443) and (host..":"..port) or host end
|
||||
if not heads.Connection then heads["Connection"] = "short" end
|
||||
if bodyLen>0 and bodyLen~=tonumber(heads["Content-Length"] or "0") then heads["Content-Length"] = bodyLen end
|
||||
if auth~="" and not heads.Authorization then heads["Authorization"] = ("Basic "..crypto.base64_encode(auth,#auth)) end
|
||||
local headStr = ""
|
||||
for k,v in pairs(heads) do
|
||||
headStr = headStr..k..": "..v.."\r\n"
|
||||
end
|
||||
headStr = headStr.."\r\n"
|
||||
|
||||
local client = socket.tcp(protocal=="https",cert,tCoreExtPara)
|
||||
if not client then return response(nil,cbFnc,false,"create socket error") end
|
||||
if not client:connect(host,port,timeout/1000) then
|
||||
return response(client,cbFnc,false,"connect fail")
|
||||
end
|
||||
|
||||
--发送请求行+请求头+string类型的body
|
||||
if not client:send(method.." "..path.." HTTP/1.1".."\r\n"..headStr..(type(body)=="string" and body or "")) then
|
||||
return response(client,cbFnc,false,"send head fail")
|
||||
end
|
||||
|
||||
--发送table类型的body
|
||||
if type(body)=="table" then
|
||||
for i=1,#body do
|
||||
if type(body[i])=="string" then
|
||||
if not client:send(body[i]) then
|
||||
return response(client,cbFnc,false,"send body fail")
|
||||
end
|
||||
else
|
||||
local file = io.open(body[i].file or body[i].file_base64,"rb")
|
||||
if file then
|
||||
while true do
|
||||
local dat = file:read(body[i].file and 11200 or 8400)
|
||||
if not dat then
|
||||
io.close(file)
|
||||
break
|
||||
end
|
||||
if body[i].file_base64 then dat=crypto.base64_encode(dat,#dat) end
|
||||
if not client:send(dat) then
|
||||
io.close(file)
|
||||
return response(client,cbFnc,false,"send file fail")
|
||||
end
|
||||
end
|
||||
else
|
||||
return response(client,cbFnc,false,"send file open fail")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local rcvCache,rspHead,rspBody,d1,d2,result,data,statusCode,rcvChunked,contentLen = "",{},{}
|
||||
--接收数据,解析状态行和头
|
||||
while true do
|
||||
result,data = receive(client,timeout,cbFnc,false,nil,rspHead,rcvFilePath or table.concat(rspBody))
|
||||
if not result then return end
|
||||
rcvCache = rcvCache..data
|
||||
d1,d2 = rcvCache:find("\r\n\r\n")
|
||||
if d2 then
|
||||
--状态行
|
||||
_,d1,statusCode = rcvCache:find("%s(%d+)%s.-\r\n")
|
||||
if not statusCode then
|
||||
return response(client,cbFnc,false,"parse received status error",rspHead,rcvFilePath or table.concat(rspBody))
|
||||
end
|
||||
--应答头
|
||||
for k,v in string.gmatch(rcvCache:sub(d1+1,d2-2),"(.-):%s*(.-)\r\n") do
|
||||
rspHead[k] = v
|
||||
if (string.upper(k)==string.upper("Transfer-Encoding")) and (string.upper(v)==string.upper("chunked")) then rcvChunked = true end
|
||||
end
|
||||
if not rcvChunked then
|
||||
contentLen = tonumber(rspHead["Content-Length"] or "2147483647")
|
||||
end
|
||||
if method == "HEAD" then
|
||||
contentLen = 0
|
||||
end
|
||||
--未处理的body数据
|
||||
rcvCache = rcvCache:sub(d2+1,-1)
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
--解析body
|
||||
if rcvChunked then
|
||||
local chunkSize
|
||||
--循环处理每个chunk
|
||||
while true do
|
||||
--解析chunk size
|
||||
if not chunkSize then
|
||||
d1,d2,chunkSize = rcvCache:find("(%x+)\r\n")
|
||||
if chunkSize then
|
||||
chunkSize = tonumber(chunkSize,16)
|
||||
rcvCache = rcvCache:sub(d2+1,-1)
|
||||
else
|
||||
result,data = receive(client,timeout,cbFnc,false,nil,rspHead,rcvFilePath or table.concat(rspBody))
|
||||
if not result then return end
|
||||
rcvCache = rcvCache..data
|
||||
end
|
||||
end
|
||||
|
||||
--log.info("http.taskClient chunkSize",chunkSize)
|
||||
|
||||
--解析chunk data
|
||||
if chunkSize then
|
||||
if rcvCache:len()<chunkSize+2 then
|
||||
result,data = receive(client,timeout,cbFnc,false,nil,rspHead,rcvFilePath or table.concat(rspBody))
|
||||
if not result then return end
|
||||
rcvCache = rcvCache..data
|
||||
else
|
||||
if chunkSize>0 then
|
||||
local chunkData = rcvCache:sub(1,chunkSize)
|
||||
--保存到文件中
|
||||
if type(rcvFilePath)=="string" then
|
||||
local file = io.open(rcvFilePath,"a+")
|
||||
if not file then return response(client,cbFnc,false,"receive: open file error",rspHead,rcvFilePath or table.concat(rspBody)) end
|
||||
if not file:write(chunkData) then response(client,cbFnc,false,"receive: write file error",rspHead,rcvFilePath or table.concat(rspBody)) end
|
||||
file:close()
|
||||
elseif type(rcvFilePath)=="function" then --保存到缓冲区中
|
||||
local userResult = rcvFilePath(data,rspHead["Content-Range"] and tonumber((rspHead["Content-Range"]):match("/(%d+)")) or contentLen,statusCode)
|
||||
if userResult~=nil then
|
||||
return response(client,cbFnc,userResult,userResult and statusCode or "receive: user process error",rspHead)
|
||||
end
|
||||
else
|
||||
table.insert(rspBody,chunkData)
|
||||
end
|
||||
rcvCache = rcvCache:sub(chunkSize+3,-1)
|
||||
chunkSize = nil
|
||||
elseif chunkSize==0 then
|
||||
return response(client,cbFnc,true,statusCode,rspHead,rcvFilePath or table.concat(rspBody))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
local rmnLen = contentLen
|
||||
while true do
|
||||
data = rcvCache:len()<=rmnLen and rcvCache or rcvCache:sub(1,rmnLen)
|
||||
if type(rcvFilePath)=="string" then
|
||||
if data:len()>0 then
|
||||
local file = io.open(rcvFilePath,"a+")
|
||||
if not file then return response(client,cbFnc,false,"receive: open file error",rspHead,rcvFilePath or table.concat(rspBody)) end
|
||||
if not file:write(data) then response(client,cbFnc,false,"receive: write file error",rspHead,rcvFilePath or table.concat(rspBody)) end
|
||||
file:close()
|
||||
end
|
||||
elseif type(rcvFilePath)=="function" then
|
||||
local userResult = rcvFilePath(data,rspHead["Content-Range"] and tonumber((rspHead["Content-Range"]):match("/(%d+)")) or contentLen,statusCode)
|
||||
if userResult~=nil then
|
||||
return response(client,cbFnc,userResult,userResult and statusCode or "receive: user process error",rspHead)
|
||||
end
|
||||
else
|
||||
table.insert(rspBody,data)
|
||||
end
|
||||
rmnLen = rmnLen-data:len()
|
||||
if rmnLen==0 then break end
|
||||
result,rcvCache = receive(client,timeout,cbFnc,contentLen==0x7FFFFFFF,contentLen==0x7FFFFFFF and statusCode or nil,rspHead,rcvFilePath or table.concat(rspBody))
|
||||
if not result then return end
|
||||
end
|
||||
return response(client,cbFnc,true,statusCode,rspHead,rcvFilePath or table.concat(rspBody))
|
||||
end
|
||||
end
|
||||
|
||||
--- 发送HTTP请求
|
||||
-- @string method HTTP请求方法
|
||||
-- 支持"GET","HEAD","POST","OPTIONS","PUT","DELETE","TRACE","CONNECT"
|
||||
-- @string url HTTP请求url
|
||||
-- url格式(除hostname外,其余字段可选;目前的实现不支持hash),url中如果包含UTF8编码中文,则需要调用string.rawurlEncode转换成RFC3986编码。
|
||||
-- |------------------------------------------------------------------------------|
|
||||
-- | protocol ||| auth | host | path | hash |
|
||||
-- |----------|||-----------|-----------------|---------------------------|-------|
|
||||
-- | ||| | hostname | port | pathname | search | |
|
||||
-- | ||| |----------|------|----------|----------------| |
|
||||
-- " http[s] :// user:pass @ host.com : 8080 /p/a/t/h ? query=string # hash "
|
||||
-- | ||| | | | | | |
|
||||
-- |------------------------------------------------------------------------------|
|
||||
-- @table[opt=nil] cert table或者nil类型,ssl证书,当url为https类型时,此参数才有意义。cert格式如下:
|
||||
-- {
|
||||
-- caCert = "ca.crt", --CA证书文件(Base64编码 X.509格式),如果存在此参数,则表示客户端会对服务器的证书进行校验;不存在则不校验
|
||||
-- clientCert = "client.crt", --客户端证书文件(Base64编码 X.509格式),服务器对客户端的证书进行校验时会用到此参数
|
||||
-- clientKey = "client.key", --客户端私钥文件(Base64编码 X.509格式)
|
||||
-- clientPassword = "123456", --客户端证书文件密码[可选]
|
||||
-- }
|
||||
-- @table[opt=nil] head nil或者table类型,自定义请求头
|
||||
-- http.lua会自动添加Host: XXX、Connection: short、Content-Length: XXX三个请求头
|
||||
-- 如果这三个请求头满足不了需求,head参数传入自定义请求头,如果自定义请求头中存在Host、Connection、Content-Length三个请求头,将覆盖http.lua中自动添加的同名请求头
|
||||
-- head格式如下:
|
||||
-- 如果没有自定义请求头,传入nil或者{};否则传入{head1="value1", head2="value2", head3="value3"},value中不能有\r\n
|
||||
-- @param[opt=nil] body nil、string或者table类型,请求实体
|
||||
-- 如果body仅仅是一串数据,可以直接传入一个string类型的body即可
|
||||
--
|
||||
-- 如果body的数据比较复杂,包括字符串数据和文件,则传入table类型的数据,格式如下:
|
||||
-- {
|
||||
-- [1] = "string1",
|
||||
-- [2] = {file="/ldata/test.jpg"},
|
||||
-- [3] = "string2"
|
||||
-- }
|
||||
-- 例如上面的这个body,索引必须为连续的数字(从1开始),实际传输时,先发送字符串"string1",再发送文件/ldata/test.jpg的内容,最后发送字符串"string2"
|
||||
--
|
||||
-- 如果传输的文件内容需要进行base64编码再上传,请把file改成file_base64,格式如下:
|
||||
-- {
|
||||
-- [1] = "string1",
|
||||
-- [2] = {file_base64="/ldata/test.jpg"},
|
||||
-- [3] = "string2"
|
||||
-- }
|
||||
-- 例如上面的这个body,索引必须为连续的数字(从1开始),实际传输时,先发送字符串"string1",再发送文件/ldata/test.jpg经过base64编码后的内容,最后发送字符串"string2"
|
||||
-- @number[opt=30000] timeout http请求应答整个过程中,每个子过程的超时时间,单位毫秒,默认为30秒,子过程包括如下两种:
|
||||
-- 1、pdp数据网络激活的超时时间
|
||||
-- 2、http请求发送成功后,分段接收服务器的应答数据,每段数据接收的超时时间
|
||||
-- @function[opt=nil] cbFnc 执行HTTP请求的回调函数(请求发送结果以及应答数据接收结果都通过此函数通知用户),回调函数的调用形式为:
|
||||
-- cbFnc(result,prompt,head,body)
|
||||
-- result:true或者false,true表示成功收到了服务器的应答,false表示请求发送失败或者接收服务器应答失败
|
||||
-- prompt:string类型,result为true时,表示服务器的应答码;result为false时,表示错误信息
|
||||
-- head:table或者nil类型,表示服务器的应答头;result为true时,此参数为{head1="value1", head2="value2", head3="value3"},value中不包含\r\n;result为false时,此参数为nil
|
||||
-- body:string类型,如果调用request接口时传入了rcvFileName,此参数表示下载文件的完整路径;否则表示接收到的应答实体数据
|
||||
-- @string[opt=nil] rcvFileName string类型时,保存“服务器应答实体数据”的文件名,可以传入完整的文件路径,也可以传入单独的文件名,如果是文件名,http.lua会自动生成一个完整路径,通过cbFnc的参数body传出
|
||||
-- function类型时,rcvFileName(stepData,totalLen,statusCode)
|
||||
-- stepData: 本次服务器应答实体数据
|
||||
-- totalLen: 实体数据的总长度
|
||||
-- statusCode:服务器的应答码
|
||||
-- @table[opt=nil] tCoreExtPara table类型{rcvBufferSize=0}修改缓冲空间大小,解决窗口满连接超时问题,单位:字节
|
||||
-- @return string rcvFilePath,如果传入了rcvFileName,则返回对应的完整路径;其余情况都返回nil
|
||||
-- @usage
|
||||
-- http.request("GET","www.lua.org",nil,nil,nil,30000,cbFnc)
|
||||
-- http.request("GET","http://www.lua.org",nil,nil,nil,30000,cbFnc)
|
||||
-- http.request("GET","http://www.lua.org:80",nil,nil,nil,30000,cbFnc,"download.bin")
|
||||
-- http.request("GET","www.lua.org/about.html",nil,nil,nil,30000,cbFnc)
|
||||
-- http.request("GET","www.lua.org:80/about.html",nil,nil,nil,30000,cbFnc)
|
||||
-- http.request("GET","http://wiki.openluat.com/search.html?q=123",nil,nil,nil,30000,cbFnc)
|
||||
-- http.request("POST","www.test.com/report.html",nil,{Head1="ValueData1"},"BodyData",30000,cbFnc)
|
||||
-- http.request("POST","www.test.com/report.html",nil,{Head1="ValueData1",Head2="ValueData2"},{[1]="string1",[2] ={file="/ldata/test.jpg"},[3]="string2"},30000,cbFnc)
|
||||
-- http.request("GET","https://www.baidu.com",{caCert="ca.crt"})
|
||||
-- http.request("GET","https://www.baidu.com",{caCert="ca.crt",clientCert = "client.crt",clientKey = "client.key"})
|
||||
-- http.request("GET","https://www.baidu.com",{caCert="ca.crt",clientCert = "client.crt",clientKey = "client.key",clientPassword = "123456"})
|
||||
function request(method,url,cert,head,body,timeout,cbFnc,rcvFileName,tCoreExtPara)
|
||||
local protocal,auth,hostName,port,path,d1,d2,offset,rcvFilePath
|
||||
d1,d2,protocal = url:find("^(%a+)://")
|
||||
if not protocal then protocal = "http" end
|
||||
offset = d2 or 0
|
||||
|
||||
d1,d2,auth = url:find("(.-:.-)@",offset+1)
|
||||
offset = d2 or offset
|
||||
|
||||
if url:match("^[^/]+:(%d+)",offset+1) then
|
||||
d1,d2,hostName,port = url:find("^([^/]+):(%d+)",offset+1)
|
||||
else
|
||||
d1,d2,hostName = url:find("(.-)/",offset+1)
|
||||
if hostName then
|
||||
d2 = d2-1
|
||||
else
|
||||
hostName = url:sub(offset+1,-1)
|
||||
offset = url:len()
|
||||
end
|
||||
end
|
||||
|
||||
if not hostName then return response(nil,cbFnc,false,"Invalid url, can't get host") end
|
||||
if port=="" or not port then port = (protocal=="https" and 443 or 80) end
|
||||
offset = d2 or offset
|
||||
|
||||
path = url:sub(offset+1,-1)
|
||||
|
||||
sys.taskInit(taskClient,method,protocal,auth or "",hostName,port,path=="" and "/" or path,cert,head,body or "",timeout or 30000,cbFnc,rcvFileName,tCoreExtPara)
|
||||
if type(rcvFileName) == "string" then
|
||||
return rcvFileName
|
||||
end
|
||||
end
|
||||
181
4G/源代码/lib/lbsLoc.lua
Normal file
181
4G/源代码/lib/lbsLoc.lua
Normal file
@@ -0,0 +1,181 @@
|
||||
--- 模块功能:根据基站信息查询经纬度
|
||||
-- @module lbsLoc
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2018.03.25
|
||||
|
||||
require"socket"
|
||||
require"utils"
|
||||
require"common"
|
||||
require"misc"
|
||||
module(..., package.seeall)
|
||||
|
||||
local function enCellInfo(s)
|
||||
local ret,t,mcc,mnc,lac,ci,rssi,k,v,m,n,cntrssi = "",{}
|
||||
log.info("lbsLoc.enCellInfo",s)
|
||||
for mcc,mnc,lac,ci,rssi in string.gmatch(s,"(%d+)%.(%d+)%.(%d+)%.(%d+)%.(%d+);") do
|
||||
mcc,mnc,lac,ci,rssi = tonumber(mcc),tonumber(mnc),tonumber(lac),tonumber(ci),(tonumber(rssi) > 31) and 31 or tonumber(rssi)
|
||||
local handle = nil
|
||||
for k,v in pairs(t) do
|
||||
if v.lac == lac and v.mcc == mcc and v.mnc == mnc then
|
||||
if #v.rssici < 8 then
|
||||
table.insert(v.rssici,{rssi=rssi,ci=ci})
|
||||
end
|
||||
handle = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if not handle then
|
||||
table.insert(t,{mcc=mcc,mnc=mnc,lac=lac,rssici={{rssi=rssi,ci=ci}}})
|
||||
end
|
||||
end
|
||||
for k,v in pairs(t) do
|
||||
ret = ret .. pack.pack(">HHb",v.lac,v.mcc,v.mnc)
|
||||
for m,n in pairs(v.rssici) do
|
||||
cntrssi = bit.bor(bit.lshift(((m == 1) and (#v.rssici-1) or 0),5),n.rssi)
|
||||
ret = ret .. pack.pack(">bi",cntrssi,n.ci)
|
||||
end
|
||||
end
|
||||
|
||||
return string.char(#t)..ret
|
||||
end
|
||||
|
||||
local function enWifiInfo(tWifi)
|
||||
local ret,cnt,k,v = "",0
|
||||
if tWifi then
|
||||
for k,v in pairs(tWifi) do
|
||||
log.info("lbsLoc.enWifiInfo",k,v)
|
||||
ret = ret..pack.pack("Ab",(k:gsub(":","")):fromHex(),(v<0) and (v+255) or v)
|
||||
cnt = cnt+1
|
||||
end
|
||||
end
|
||||
return string.char(cnt)..ret
|
||||
end
|
||||
|
||||
local function enMuid()
|
||||
local muid = misc.getMuid()
|
||||
return string.char(muid:len())..muid
|
||||
end
|
||||
|
||||
local function trans(str)
|
||||
local s = str
|
||||
if str:len()<10 then
|
||||
s = str..string.rep("0",10-str:len())
|
||||
end
|
||||
|
||||
return s:sub(1,3).."."..s:sub(4,10)
|
||||
end
|
||||
|
||||
local function taskClient(cbFnc,reqAddr,timeout,productKey,host,port,reqTime,reqWifi)
|
||||
while not socket.isReady() do
|
||||
if not sys.waitUntil("IP_READY_IND",timeout) then return cbFnc(1) end
|
||||
end
|
||||
|
||||
local retryCnt,sck = 0
|
||||
local reqStr = pack.pack("bAbAAAAA",
|
||||
productKey:len(),
|
||||
productKey,
|
||||
(reqAddr and 2 or 0)+(reqTime and 4 or 0)+8+(reqWifi and 16 or 0)+32,
|
||||
"",
|
||||
common.numToBcdNum(misc.getImei()),
|
||||
enMuid(),
|
||||
enCellInfo(net.getCellInfoExt()),
|
||||
enWifiInfo(reqWifi))
|
||||
log.info("reqStr",reqStr:toHex())
|
||||
while true do
|
||||
sck = socket.udp()
|
||||
if not sck then cbFnc(6) return end
|
||||
if sck:connect(host,port) then
|
||||
while true do
|
||||
if sck:send(reqStr) then
|
||||
local result,data = sck:recv(timeout)
|
||||
if result then
|
||||
sck:close()
|
||||
log.info("lbsLoc receive",data:toHex())
|
||||
if data:len()>=11 and (data:byte(1)==0 or data:byte(1)==0xFF) then
|
||||
local locType = data:byte(1)
|
||||
cbFnc(0,
|
||||
trans(common.bcdNumToNum(data:sub(2,6))),
|
||||
trans(common.bcdNumToNum(data:sub(7,11))),
|
||||
reqAddr and data:sub(13,12+data:byte(12)) or nil,
|
||||
data:sub(reqAddr and (13+data:byte(12)) or 12,-1),
|
||||
locType)
|
||||
else
|
||||
log.warn("lbsLoc.query","根据基站查询经纬度失败")
|
||||
if data:byte(1)==2 then
|
||||
log.warn("lbsLoc.query","main.lua中的PRODUCT_KEY和此设备在iot.openluat.com中所属项目的ProductKey必须一致,请去检查")
|
||||
else
|
||||
log.warn("lbsLoc.query","基站数据库查询不到所有小区的位置信息")
|
||||
log.warn("lbsLoc.query","在trace中向上搜索encellinfo,然后在电脑浏览器中打开http://bs.openluat.com/,手动查找encellinfo后的所有小区位置")
|
||||
log.warn("lbsLoc.query","如果手动可以查到位置,则服务器存在BUG,直接向技术人员反映问题")
|
||||
log.warn("lbsLoc.query","如果手动无法查到位置,则基站数据库还没有收录当前设备的小区位置信息,向技术人员反馈,我们会尽快收录")
|
||||
end
|
||||
cbFnc(5)
|
||||
end
|
||||
return
|
||||
else
|
||||
sck:close()
|
||||
retryCnt = retryCnt+1
|
||||
if retryCnt>=3 then return cbFnc(4) end
|
||||
break
|
||||
end
|
||||
else
|
||||
sck:close()
|
||||
retryCnt = retryCnt+1
|
||||
if retryCnt>=3 then return cbFnc(3) end
|
||||
break
|
||||
end
|
||||
end
|
||||
else
|
||||
sck:close()
|
||||
retryCnt = retryCnt+1
|
||||
if retryCnt>=3 then return cbFnc(2) end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- 发送基站/WIFI定位请求(仅支持中国区域的位置查询)
|
||||
-- @function cbFnc 用户回调函数,回调函数的调用形式为:
|
||||
-- cbFnc(result,lat,lng,addr,time,locType)
|
||||
-- result:number类型
|
||||
-- 0表示成功
|
||||
-- 1表示网络环境尚未就绪
|
||||
-- 2表示连接服务器失败
|
||||
-- 3表示发送数据失败
|
||||
-- 4表示接收服务器应答超时
|
||||
-- 5表示服务器返回查询失败
|
||||
-- 6表示socket已满,创建socket失败
|
||||
-- 为0时,后面的5个参数才有意义
|
||||
-- lat:string类型或者nil,纬度,整数部分3位,小数部分7位,例如"031.2425864"
|
||||
-- lng:string类型或者nil,经度,整数部分3位,小数部分7位,例如"121.4736522"
|
||||
-- addr:目前无意义
|
||||
-- time:string类型或者nil,服务器返回的时间,6个字节,年月日时分秒,需要转为十六进制读取
|
||||
-- 第一个字节:年减去2000,例如2017年,则为0x11
|
||||
-- 第二个字节:月,例如7月则为0x07,12月则为0x0C
|
||||
-- 第三个字节:日,例如11日则为0x0B
|
||||
-- 第四个字节:时,例如18时则为0x12
|
||||
-- 第五个字节:分,例如59分则为0x3B
|
||||
-- 第六个字节:秒,例如48秒则为0x30
|
||||
-- locType:numble类型或者nil,定位类型,0表示基站定位成功,255表示WIFI定位成功
|
||||
-- @bool[opt=nil] reqAddr 是否请求服务器返回具体的位置字符串信息,目前此功能不完善
|
||||
-- @number[opt=20000] timeout 请求超时时间,单位毫秒,默认20000毫秒
|
||||
-- @string[opt=nil] productKey IOT网站上的产品证书,如果在main.lua中定义了PRODUCT_KEY变量,则此参数可以传nil
|
||||
-- @string[opt=nil] host 服务器域名,此参数可选,目前仅lib中agps.lua使用此参数。应用脚本可以直接传nil
|
||||
-- @string[opt=nil] port 服务器端口,此参数可选,目前仅lib中agps.lua使用此参数。应用脚本可以直接传nil
|
||||
-- @bool[opt=nil] reqTime 是否需要服务器返回时间信息,true返回,false或者nil不返回,此参数可选,目前仅lib中agps.lua使用此参数。应用脚本可以直接传nil
|
||||
-- @table[opt=nil] reqWifi 搜索到的WIFI热点信息(MAC地址和信号强度),如果传入了此参数,后台会查询WIFI热点对应的经纬度,此参数格式如下:
|
||||
-- {
|
||||
-- ["1a:fe:34:9e:a1:77"] = -63,
|
||||
-- ["8c:be:be:2d:cd:e9"] = -81,
|
||||
-- ["20:4e:7f:82:c2:c4"] = -70,
|
||||
-- }
|
||||
-- @return nil
|
||||
-- @usage lbsLoc.request(cbFnc)
|
||||
-- @usage lbsLoc.request(cbFnc,true)
|
||||
-- @usage lbsLoc.request(cbFnc,nil,20000)
|
||||
function request(cbFnc,reqAddr,timeout,productKey,host,port,reqTime,reqWifi)
|
||||
assert(_G.PRODUCT_KEY or productKey,"undefine PRODUCT_KEY in main.lua")
|
||||
sys.taskInit(taskClient,cbFnc,reqAddr,timeout or 20000,productKey or _G.PRODUCT_KEY,host or "bs.openluat.com",port or "12411",reqTime,reqWifi)
|
||||
end
|
||||
|
||||
70
4G/源代码/lib/led.lua
Normal file
70
4G/源代码/lib/led.lua
Normal file
@@ -0,0 +1,70 @@
|
||||
--- 模块功能:LED闪灯模块
|
||||
-- @module led
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2018.02.04
|
||||
module(..., package.seeall)
|
||||
|
||||
--- 闪烁指示灯
|
||||
-- @function ledPin ledPin(1)用pins.setup注册返回的方法
|
||||
-- @number light light-亮灯时间ms
|
||||
-- @number dark dark-灭灯时间ms
|
||||
-- @return nil
|
||||
-- @usage led.blinkPwm(lenPin,500,500)
|
||||
-- @usage 调用函数需要使用任务支持
|
||||
function blinkPwm(ledPin, light, dark)
|
||||
ledPin(1)
|
||||
sys.wait(light)
|
||||
ledPin(0)
|
||||
sys.wait(dark)
|
||||
end
|
||||
--- 等级指示灯
|
||||
-- @function ledPin ledPin(1)用pins.setup注册返回的方法
|
||||
-- @number bl 亮灯时间ms
|
||||
-- @number bd 灭灯时间ms
|
||||
-- @number cnt 重复次数 (等级的级别,亮灭1次算数字1)
|
||||
-- @number gap 间隔时间 (每次循环周期的间隔)
|
||||
-- @return nil
|
||||
-- @usage led.leveled(ledPin,200,200,4,1000)
|
||||
-- @usage 调用函数需要使用任务支持
|
||||
function levelLed(ledPin, bl, bd, cnt, gap)
|
||||
if not (ledPin and bl and bd and cnt and gap) then return end
|
||||
for i = 1, cnt do blinkPwm(ledPin, bl, bd) end
|
||||
sys.wait(gap)
|
||||
end
|
||||
|
||||
--- 呼吸灯
|
||||
-- @function ledPin 呼吸灯的ledPin(1)用pins.setup注册返回的方法
|
||||
-- @return nil
|
||||
-- @usage led.breateLed(ledPin)
|
||||
-- @usage 调用函数需要使用任务支持
|
||||
function breateLed(ledPin)
|
||||
-- 呼吸灯的状态、PWM周期
|
||||
local bLighting, bDarking, LED_PWM = false, true, 18
|
||||
if bLighting then
|
||||
for i = 1, LED_PWM - 1 do
|
||||
ledPin(0)
|
||||
sys.wait(i)
|
||||
ledPin(1)
|
||||
sys.wait(LED_PWM - i)
|
||||
end
|
||||
bLighting = false
|
||||
bDarking = true
|
||||
ledPin(0)
|
||||
sys.wait(700)
|
||||
end
|
||||
if bDarking then
|
||||
for i = 1, LED_PWM - 1 do
|
||||
ledPin(0)
|
||||
sys.wait(LED_PWM - i)
|
||||
ledPin(1)
|
||||
sys.wait(i)
|
||||
end
|
||||
bLighting = true
|
||||
bDarking = false
|
||||
ledPin(1)
|
||||
sys.wait(700)
|
||||
end
|
||||
end
|
||||
|
||||
461
4G/源代码/lib/link.lua
Normal file
461
4G/源代码/lib/link.lua
Normal file
@@ -0,0 +1,461 @@
|
||||
--- 模块功能:数据链路激活(创建、连接、状态维护)
|
||||
-- @module link
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2017.9.20
|
||||
-- 4G网络下不手动激活pdp,注册上网后发cgdcont?等默认承载激活后上报IP_READY_IND,
|
||||
-- 2G网络下,先cgact?查询有任一一路pdp激活,则直接上报IP_READY_IND,否则cgact激活cid_manual
|
||||
require "net"
|
||||
|
||||
module(..., package.seeall)
|
||||
|
||||
local publish = sys.publish
|
||||
local request = ril.request
|
||||
local ipAddr = ""
|
||||
local gprsAttached
|
||||
local cid_manual = 5
|
||||
local readyTable = {false, false, false}
|
||||
-- 链路层网络类型:
|
||||
-- 蜂窝模组数据网络
|
||||
CELLULAR = 1
|
||||
-- ch395以太网
|
||||
CH395 = 2
|
||||
-- w5500以太网
|
||||
W5500 = 3
|
||||
-- ESP8266WIFI网络
|
||||
ESP8266 = 4
|
||||
local network = CELLULAR
|
||||
|
||||
function setReady(mode, state)
|
||||
readyTable[mode] = state
|
||||
end
|
||||
function getIp()
|
||||
return ipAddr
|
||||
end
|
||||
function isReady()
|
||||
return readyTable[network]
|
||||
end
|
||||
|
||||
-- apn,用户名,密码
|
||||
local apnname, username, password
|
||||
local dnsIP
|
||||
local authProt, authApn, authUser, authPassword
|
||||
|
||||
function setAPN(apn, user, pwd)
|
||||
apnname, username, password = apn, user, pwd
|
||||
end
|
||||
|
||||
function setDnsIP(ip1, ip2)
|
||||
dnsIP = "\"" .. (ip1 or "") .. "\",\"" .. (ip2 or "") .. "\""
|
||||
end
|
||||
|
||||
local function setCgdf()
|
||||
request("AT+AUTOAPN=0")
|
||||
request('AT*CGDFLT=1,"IP","' .. authApn .. '",,,,,,,,,,,,,,,,,,1')
|
||||
request('AT*CGDFAUTH=1,' .. authProt .. ',"' .. authUser .. '","' .. authPassword .. '"', nil, function(cmd, result)
|
||||
if result then
|
||||
sys.restart("CGDFAUTH")
|
||||
else
|
||||
sys.timerStart(setCgdf, 5000)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
--- 设置专网卡APN(注意:在main.lua中,尽可能靠前的位置调用此接口)
|
||||
-- 第一次设置成功之后,软件会自动重启,因为重启后才能生效
|
||||
-- @number[opt=0] prot 加密方式,0:不加密,1:PAP,2:CHAP
|
||||
-- @string[opt=""] apn apn名称
|
||||
-- @string[opt=""] user apn用户名
|
||||
-- @string[opt=""] pwd apn密码
|
||||
-- @return nil
|
||||
-- @usage
|
||||
-- c = link.setAuthApn(2,"MYAPN","MYNAME","MYPASSWORD")
|
||||
function setAuthApn(prot, apn, user, pwd)
|
||||
--[[
|
||||
local coreVer = rtos.get_version()
|
||||
local verNo = coreVer:match("Luat_V(%d+)_ASR1802_")
|
||||
if verNo and tonumber(verNo)>=27 then
|
||||
request("AT+AUTOAPN=0")]]
|
||||
-- 0:保存并重启生效
|
||||
-- 1:不保存立即生效
|
||||
-- 2:保存并立即生效
|
||||
-- 3:删除保存的文件
|
||||
request('AT+CPNETAPN=2,"' .. apn .. '","' .. user .. '","' .. pwd .. '",' .. prot)
|
||||
--[[else
|
||||
authProt,authApn,authUser,authPassword = prot or 0,apn or "",user or "",pwd or ""
|
||||
request("AT*CGDFLT?")
|
||||
ril.regUrc("*CGDFLT", function(data)
|
||||
local dftApn = data:match("CGDFLT:%s*\"%w*\",\"(.-)\"")
|
||||
if dftApn~=authApn then
|
||||
setCgdf()
|
||||
end
|
||||
end)
|
||||
end]]
|
||||
end
|
||||
|
||||
local function Pdp_Act()
|
||||
log.info("link.Pdp_Act", readyTable[CELLULAR], net.getNetMode(), gprsAttached)
|
||||
if readyTable[CELLULAR] then
|
||||
request("AT+CGDCONT?", nil, cgdcontRsp)
|
||||
return
|
||||
end
|
||||
if net.getNetMode() == net.NetMode_LTE then
|
||||
if not gprsAttached then
|
||||
gprsAttached = true
|
||||
sys.publish("GPRS_ATTACH", true)
|
||||
end
|
||||
if not apnname then
|
||||
sys.timerStart(pdpCmdCnf, 1000, "SET_PDP_4G_WAITAPN", true)
|
||||
else
|
||||
request("AT+CGDCONT?", nil, cgdcontRsp)
|
||||
-- request(string.format('AT*CGDFLT=0,"IP","%s"', apnname), nil, pdpCmdCnf)
|
||||
end
|
||||
else
|
||||
request('AT+CGATT?')
|
||||
end
|
||||
end
|
||||
|
||||
local function procshut(curCmd, result, respdata, interdata)
|
||||
if network ~= CELLULAR then
|
||||
return
|
||||
end
|
||||
if IsCidActived(cid_manual, interdata) then
|
||||
ril.request(string.format('AT+CGACT=0,%d', cid_manual), nil, function(cmd, result)
|
||||
if result then
|
||||
readyTable[CELLULAR] = false
|
||||
sys.publish('IP_ERROR_IND')
|
||||
|
||||
if net.getState() ~= 'REGISTERED' then
|
||||
return
|
||||
end
|
||||
sys.timerStart(Pdp_Act, 2000)
|
||||
end
|
||||
end)
|
||||
else
|
||||
readyTable[CELLULAR] = false
|
||||
sys.publish('IP_ERROR_IND')
|
||||
|
||||
if net.getState() ~= 'REGISTERED' then
|
||||
return
|
||||
end
|
||||
sys.timerStart(Pdp_Act, 2000)
|
||||
end
|
||||
end
|
||||
--[[
|
||||
如果是默认承载,是去激活不了的,
|
||||
如果是手动激活的pdp,去激活cid_manual后也还是有默认承载存在,
|
||||
所以如果上层在去激活后要发起socket是能连上的,所以这里直接上报IP_ERROR_IND,由上层自己管理shut之后的逻辑
|
||||
]]
|
||||
function shut()
|
||||
if network ~= CELLULAR then
|
||||
return
|
||||
end
|
||||
-- ril.request("AT+CGACT?",nil,procshut)
|
||||
readyTable[CELLULAR] = false
|
||||
sys.publish('IP_ERROR_IND')
|
||||
|
||||
if net.getState() ~= 'REGISTERED' then
|
||||
return
|
||||
end
|
||||
sys.timerStart(Pdp_Act, 2000)
|
||||
end
|
||||
|
||||
function analysis_cgdcont(data)
|
||||
local tmp, loc, result
|
||||
while data do
|
||||
_, loc = string.find(data, "\r\n")
|
||||
if loc then
|
||||
tmp = string.sub(data, 1, loc)
|
||||
data = string.sub(data, loc + 1, -1)
|
||||
log.info("analysis_cgdcont ", tmp, loc, data)
|
||||
else
|
||||
tmp = data
|
||||
data = nil
|
||||
log.info("analysis_cgdcont end", tmp, loc, data)
|
||||
end
|
||||
if tmp then
|
||||
local cid, pdptyp, apn, addr = string.match(tmp, "(%d+),(.+),(.+),[\"\'](.+)[\"\']")
|
||||
if not cid or not pdptyp or not apn or not addr then
|
||||
log.info("analysis_cgdcont CGDCONT is empty")
|
||||
ipAddr = ""
|
||||
result = false
|
||||
else
|
||||
log.info("analysis_cgdcont ", cid, pdptyp, apn, addr)
|
||||
if addr:match("%d+%.%d+%.%d+%.%d") then
|
||||
ipAddr = addr
|
||||
return true
|
||||
else
|
||||
log.info("analysis_cgdcont CGDCONT is empty1")
|
||||
ipAddr = ""
|
||||
return false
|
||||
end
|
||||
end
|
||||
else
|
||||
ipAddr = ""
|
||||
log.info("analysis_cgdcont tmp is empty")
|
||||
end
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
function IsCidActived(cid, data)
|
||||
if not data then
|
||||
return
|
||||
end
|
||||
for k, v in string.gfind(data, "(%d+),%s*(%d)") do
|
||||
log.info("iscidactived ", k, v)
|
||||
if cid == tonumber(k) and v == '1' then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
function IsExistActivedCid(data)
|
||||
if not data then
|
||||
return
|
||||
end
|
||||
for k, v in string.gfind(data, "(%d+),%s*(%d)") do
|
||||
if v == '1' then
|
||||
log.info("ExistActivedCid ", k, v)
|
||||
return true
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
local cgdcontResult
|
||||
|
||||
function cgdcontRsp()
|
||||
if cgdcontResult then
|
||||
pdpCmdCnf("CONNECT_DELAY", true)
|
||||
end
|
||||
end
|
||||
|
||||
function pdpCmdCnf(curCmd, result, respdata, interdata)
|
||||
log.info("link.pdpCmdCnf", curCmd, result, respdata, interdata)
|
||||
if string.find(curCmd, "CGDCONT%?") then
|
||||
if result and interdata then
|
||||
result = analysis_cgdcont(interdata)
|
||||
else
|
||||
result = false
|
||||
end
|
||||
end
|
||||
|
||||
if result then
|
||||
cgdcontResult = false
|
||||
if string.find(curCmd, "CGDCONT=") then
|
||||
request(string.format('AT+CGACT=1,%d', cid_manual), nil, pdpCmdCnf)
|
||||
elseif string.find(curCmd, "CGDCONT%?") then
|
||||
-- sys.timerStart(pdpCmdCnf, 100, "CONNECT_DELAY",true)
|
||||
cgdcontResult = true
|
||||
elseif string.find(curCmd, "CONNECT_DELAY") and network == CELLULAR then
|
||||
log.info("publish IP_READY_IND")
|
||||
readyTable[CELLULAR] = true
|
||||
publish("IP_READY_IND")
|
||||
elseif string.find(curCmd, "CGACT=") then
|
||||
request("AT+CGDCONT?", nil, cgdcontRsp)
|
||||
elseif string.find(curCmd, "CGACT%?") then
|
||||
if IsExistActivedCid(interdata) then
|
||||
sys.timerStart(pdpCmdCnf, 100, "CONNECT_DELAY", true)
|
||||
else
|
||||
request(string.format('AT+CGDCONT=%d,"IP","%s"', cid_manual, authApn or apnname), nil, pdpCmdCnf)
|
||||
end
|
||||
elseif string.find(curCmd, "CGDFLT") then
|
||||
request("AT+CGDCONT?", nil, cgdcontRsp)
|
||||
elseif string.find(curCmd, "SET_PDP_4G_WAITAPN") then
|
||||
if not apnname then
|
||||
sys.timerStart(pdpCmdCnf, 100, "SET_PDP_4G_WAITAPN", true)
|
||||
else
|
||||
request("AT+CGDCONT?", nil, cgdcontRsp, 1000)
|
||||
-- request(string.format('AT*CGDFLT=0,"IP","%s"', apnname), nil, pdpCmdCnf)
|
||||
end
|
||||
end
|
||||
else
|
||||
if net.getState() ~= 'REGISTERED' then
|
||||
return
|
||||
end
|
||||
if net.getNetMode() == net.NetMode_LTE then
|
||||
request("AT+CGDCONT?", nil, cgdcontRsp, 1000)
|
||||
else
|
||||
request("AT+CGATT?", nil, nil, 1000)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- SIM卡 IMSI READY以后自动设置APN
|
||||
sys.subscribe("IMSI_READY", function()
|
||||
if not apnname then -- 如果未设置APN设置默认APN
|
||||
local mcc, mnc = tonumber(sim.getMcc(), 16), tonumber(sim.getMnc(), 16)
|
||||
apnname, username, password = apn and apn.get_default_apn(mcc, mnc) -- 如果存在APN库自动获取运营商的APN
|
||||
if not apnname or apnname == '' or apnname == "CMNET" then -- 默认情况,如果联通卡设置为联通APN 其他都默认为CMIOT
|
||||
apnname = (mcc == 0x460 and (mnc == 0x01 or mnc == 0x06)) and 'UNINET' or 'CMIOT'
|
||||
end
|
||||
end
|
||||
username = username or ''
|
||||
password = password or ''
|
||||
end)
|
||||
|
||||
ril.regRsp('+CGATT', function(a, b, c, intermediate)
|
||||
local attached = (intermediate == "+CGATT: 1")
|
||||
if gprsAttached ~= attached then
|
||||
gprsAttached = attached
|
||||
sys.publish("GPRS_ATTACH", attached)
|
||||
end
|
||||
|
||||
if readyTable[CELLULAR] then
|
||||
return
|
||||
end
|
||||
|
||||
if attached then
|
||||
log.info("pdp active", apnname, username, password)
|
||||
request("AT+CGACT?", nil, pdpCmdCnf, 1000)
|
||||
elseif net.getState() == 'REGISTERED' then
|
||||
sys.timerStart(request, 2000, "AT+CGATT=1")
|
||||
sys.timerStart(request, 2000, "AT+CGATT?")
|
||||
end
|
||||
end)
|
||||
|
||||
rtos.on(rtos.MSG_PDP_DEACT_IND, function()
|
||||
if network ~= CELLULAR then
|
||||
return
|
||||
end
|
||||
readyTable[CELLULAR] = false
|
||||
sys.publish('IP_ERROR_IND')
|
||||
|
||||
if net.getState() ~= 'REGISTERED' then
|
||||
return
|
||||
end
|
||||
sys.timerStart(Pdp_Act, 2000)
|
||||
end)
|
||||
|
||||
-- 网络注册成功 :AT+CGDCONT?查询默认承载是否激活
|
||||
-- 2/3G发起GPRS附着状态查询
|
||||
sys.subscribe("NET_STATE_REGISTERED", Pdp_Act)
|
||||
|
||||
local function cindCnf(cmd, result)
|
||||
if not result then
|
||||
request("AT+CIND=1", nil, cindCnf, 1000)
|
||||
end
|
||||
end
|
||||
|
||||
local function cgevurc(data)
|
||||
if network ~= CELLULAR then
|
||||
return
|
||||
end
|
||||
local cid = 0
|
||||
log.info("link.cgevurc", data)
|
||||
|
||||
if string.match(data, "DEACT") then
|
||||
cid = string.match(data, "DEACT,(%d)")
|
||||
cid = tonumber(cid)
|
||||
|
||||
if cid == cid_manual then
|
||||
request("AT+CFUN?")
|
||||
readyTable[CELLULAR] = false
|
||||
sys.publish('IP_ERROR_IND')
|
||||
sys.publish('PDP_DEACT_IND')
|
||||
if net.getState() ~= 'REGISTERED' then
|
||||
return
|
||||
end
|
||||
sys.timerStart(Pdp_Act, 2000)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
request("AT+CIND=1", nil, cindCnf)
|
||||
ril.regUrc("*CGEV", cgevurc)
|
||||
ril.regUrc("+CGDCONT", function(data)
|
||||
pdpCmdCnf("AT+CGDCONT?", true, "OK", data)
|
||||
end)
|
||||
|
||||
--- 打开链路层网络类型
|
||||
-- 注意:设置网络类型后,并不会关机保存,下次开机会自动恢复为默认的link.CELLULAR类型
|
||||
-- @number[opt=link.CELLULAR] mode 取值如下:
|
||||
-- link.CELLULAR:蜂窝模组数据网络
|
||||
-- link.CH395:CH395以太网络
|
||||
-- link.ESP8266:ESP8266WIFI网络
|
||||
-- @table[opt=nil] para 取值如下:
|
||||
-- 当mode为link.CELLULAR时,参数para无意义,可以直接传入nil
|
||||
-- 当mode为link.CH395,para为table类型,表示以太网的配置参数,参数结构如下:
|
||||
-- para= {
|
||||
-- mode = 1, --1表示客户端;2表示服务器;默认为1
|
||||
-- intPin = pio.P0_22, --以太网芯片中断通知引脚
|
||||
-- rstPin = pio.P0_23, --复位以太网芯片引脚
|
||||
-- spiCs = pio.P0_23, --spi片选
|
||||
-- serverAddr = "192.168.1.112", --做服务器应用时,本机的地址
|
||||
-- serverPort = 1888, --做服务器应用时,本机的端口
|
||||
-- serverGateway = "192.168.1.1", --做服务器应用时,本机的网关地址
|
||||
-- powerFunc=function(state) end --控制以太网模块的供电开关函数,ret为true开启供电,false关闭供电
|
||||
-- spi = {spi.SPI_1,0,0,8,800000}, --SPI通道参数,id,cpha,cpol,dataBits,clock,默认spi.SPI_1,0,0,8,800000
|
||||
-- }
|
||||
-- @return true/false,执行成功返回true,失败返回false。
|
||||
-- @usage
|
||||
-- 设置为蜂窝数据网络:
|
||||
-- c = link.setNetwork(link.CELLULAR, para)
|
||||
-- 设置为CH395以太网络:
|
||||
-- link.setNetwork(link.CH395, para)
|
||||
-- 设置为ESP8266WIFI网络:
|
||||
-- link.setNetwork(link.ESP8266, para)
|
||||
function openNetwork(mode, para)
|
||||
local tSocketModule = {
|
||||
[CH395] = socketCh395,
|
||||
[W5500] = socketW5500,
|
||||
[ESP8266] = socketESP8266
|
||||
}
|
||||
local md = mode or CELLULAR
|
||||
closeNetWork()
|
||||
network = md
|
||||
if network == CELLULAR then
|
||||
net.switchFly(false)
|
||||
return true
|
||||
else
|
||||
ipAddr = tSocketModule[network].open(para)
|
||||
if ipAddr ~= "" then
|
||||
return true
|
||||
else
|
||||
log.info('link', 'open CH395 err')
|
||||
return false
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
--- 关闭链路层网络类型
|
||||
-- 注意:关闭链路层网络类型,不会改变链路层网络类型,需要打开链路层网络类型配置才能切换。
|
||||
-- @return true/false,执行成功返回true,失败返回false。
|
||||
-- @usage
|
||||
-- 关闭链路层网络类型:
|
||||
-- link.closeNetWork()
|
||||
function closeNetWork()
|
||||
local tSocketModule = {
|
||||
[CH395] = socketCh395,
|
||||
[W5500] = socketW5500,
|
||||
[ESP8266] = socketESP8266
|
||||
}
|
||||
|
||||
if network == CELLULAR then
|
||||
-- 飞行模式
|
||||
net.switchFly(true)
|
||||
return true
|
||||
else
|
||||
return tSocketModule[network].close()
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
--- 获取链路层网络类型
|
||||
-- @return network,number类型,取值如下:
|
||||
-- link.CELLULAR:蜂窝模组数据网络
|
||||
-- link.CH395:CH395以太网络
|
||||
-- link.ESP8266:ESP8266WIFI网络
|
||||
-- @usage
|
||||
-- 获取数据网络类型:
|
||||
-- mode = link.getNetwork()
|
||||
function getNetwork()
|
||||
return network
|
||||
end
|
||||
123
4G/源代码/lib/log.lua
Normal file
123
4G/源代码/lib/log.lua
Normal file
@@ -0,0 +1,123 @@
|
||||
--- 模块功能:系统日志记录,分级别日志工具
|
||||
-- @module log
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2017.09.26
|
||||
|
||||
module(..., package.seeall)
|
||||
-- 定义日志级别常量,可在main入口全局指定
|
||||
-- 例如: LOG_LEVEL=log.LOGLEVEL_WARN
|
||||
LOG_SILENT = 0x00;
|
||||
LOGLEVEL_TRACE = 0x01;
|
||||
LOGLEVEL_DEBUG = 0x02;
|
||||
LOGLEVEL_INFO = 0x03;
|
||||
LOGLEVEL_WARN = 0x04;
|
||||
LOGLEVEL_ERROR = 0x05;
|
||||
LOGLEVEL_FATAL = 0x06;
|
||||
|
||||
-- 定义日志级别标签,分别对应日志级别的1-6
|
||||
local LEVEL_TAG = {'T', 'D', 'I', 'W', 'E', 'F'}
|
||||
local PREFIX_FMT = "[%s]-[%s]"
|
||||
|
||||
--- 内部函数,支持不同级别的log打印及判断
|
||||
-- @param level 日志级别,可选LOGLEVEL_TRACE,LOGLEVEL_DEBUG等
|
||||
-- @param tag 模块或功能名称(标签),作为日志前缀
|
||||
-- @param ... 日志内容,可变参数
|
||||
-- @return nil
|
||||
-- @usage _log(LOGLEVEL_TRACE,tag, 'log content')
|
||||
-- @usage _log(LOGLEVEL_DEBUG,tag, 'log content')
|
||||
local function _log(level, tag, ...)
|
||||
-- INFO 作为默认日志级别
|
||||
local OPENLEVEL = LOG_LEVEL and LOG_LEVEL or LOGLEVEL_INFO
|
||||
-- 如果日志级别为静默,或设定级别更高,则不输出日志
|
||||
if OPENLEVEL == LOG_SILENT or OPENLEVEL > level then return end
|
||||
-- 日志打印输出
|
||||
local prefix = string.format(PREFIX_FMT, LEVEL_TAG[level], type(tag)=="string" and tag or "")
|
||||
print(prefix, ...)
|
||||
|
||||
-- TODO,支持hookup,例如对某级别日志做额外处理
|
||||
-- TODO,支持标签过滤
|
||||
end
|
||||
|
||||
--- 输出trace级别的日志
|
||||
-- @param tag 模块或功能名称,作为日志前缀
|
||||
-- @param ... 日志内容,可变参数
|
||||
-- @return nil
|
||||
-- @usage log.trace('moduleA', 'log content')
|
||||
function trace(tag, ...)
|
||||
_log(LOGLEVEL_TRACE, tag, ...)
|
||||
end
|
||||
|
||||
--- 输出debug级别的日志
|
||||
-- @param tag 模块或功能名称,作为日志前缀
|
||||
-- @param ... 日志内容,可变参数
|
||||
-- @return nil
|
||||
-- @usage log.debug('moduleA', 'log content')
|
||||
function debug(tag, ...)
|
||||
_log(LOGLEVEL_DEBUG, tag, ...)
|
||||
end
|
||||
|
||||
--- 输出info级别的日志
|
||||
-- @param tag 模块或功能名称,作为日志前缀
|
||||
-- @param ... 日志内容,可变参数
|
||||
-- @return nil
|
||||
-- @usage log.info('moduleA', 'log content')
|
||||
function info(tag, ...)
|
||||
_log(LOGLEVEL_INFO, tag, ...)
|
||||
end
|
||||
|
||||
|
||||
--- 输出warn级别的日志
|
||||
-- @param tag 模块或功能名称,作为日志前缀
|
||||
-- @param ... 日志内容,可变参数
|
||||
-- @return nil
|
||||
-- @usage log.warn('moduleA', 'log content')
|
||||
function warn(tag, ...)
|
||||
_log(LOGLEVEL_WARN, tag, ...)
|
||||
end
|
||||
|
||||
--- 输出error级别的日志
|
||||
-- @param tag 模块或功能名称,作为日志前缀
|
||||
-- @param ... 日志内容,可变参数
|
||||
-- @return nil
|
||||
-- @usage log.error('moduleA', 'log content')
|
||||
function error(tag, ...)
|
||||
_log(LOGLEVEL_ERROR, tag, ...)
|
||||
end
|
||||
|
||||
--- 输出fatal级别的日志
|
||||
-- @param tag 模块或功能名称,作为日志前缀
|
||||
-- @param ... 日志内容,可变参数
|
||||
-- @return nil
|
||||
-- @usage log.fatal('moduleA', 'log content')
|
||||
function fatal(tag, ...)
|
||||
_log(LOGLEVEL_FATAL, tag, ...)
|
||||
end
|
||||
|
||||
|
||||
-- --- 开启或者关闭print的打印输出功能 --修复老版本波特率无法正常配置问题
|
||||
-- -- @bool v false或nil为关闭,其余为开启
|
||||
-- -- @param uartid 输出Luatrace的端口:1表示uart1,2表示uart2
|
||||
-- -- @return nil
|
||||
-- -- @usage log.openTrace(1,nil)
|
||||
-- function openTrace(v, uartid, baudrate)
|
||||
-- if uartid then
|
||||
-- if v then
|
||||
-- uart.setup(uartid, baudrate or 115200, 8, uart.PAR_NONE, uart.STOP_1)
|
||||
-- else
|
||||
-- uart.close(uartid)
|
||||
-- end
|
||||
-- end
|
||||
-- rtos.set_trace(v and 1 or 0, uartid)
|
||||
-- end
|
||||
|
||||
--- 开启或者关闭print的打印输出功能
|
||||
-- @bool v false或nil为关闭,其余为开启
|
||||
-- @param uartid 输出Luatrace的端口:1表示uart1,2表示uart2,3表示uart3
|
||||
-- @baudrate 可修改串口波特率,可选参数,默认115200
|
||||
-- @return nil
|
||||
-- @usage log.openTrace(1,nil)
|
||||
function openTrace(v, uartid, baudrate)
|
||||
rtos.set_trace(v and 1 or 0, uartid,baudrate)
|
||||
end
|
||||
316
4G/源代码/lib/misc.lua
Normal file
316
4G/源代码/lib/misc.lua
Normal file
@@ -0,0 +1,316 @@
|
||||
--- 模块功能:配置管理-序列号、IMEI、底层软件版本号、时钟、是否校准、飞行模式、查询电池电量等功能
|
||||
-- @module misc
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2017.10.20
|
||||
require "ril"
|
||||
local req = ril.request
|
||||
module(..., package.seeall)
|
||||
--sn:序列号
|
||||
--imei:IMEI
|
||||
--modeltype:模块型号,例如724,720,722等
|
||||
--calib: 校准标志
|
||||
--ant: 耦合测试标志位
|
||||
--temp:模块温度
|
||||
local sn, imei, calib, ver, muid, ant,modeltype,temp
|
||||
local setSnCbFnc,setImeiCbFnc,setClkCbFnc,getTemperatureCbFnc
|
||||
|
||||
local function timeReport()
|
||||
sys.publish("TIME_CLK_IND")
|
||||
sys.timerStart(setTimeReport,2000)
|
||||
end
|
||||
|
||||
function setTimeReport()
|
||||
sys.timerStart(timeReport,(os.time()%60==0) and 50 or (60-os.time()%60)*1000)
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:rsp
|
||||
功能 :本功能模块内“通过虚拟串口发送到底层core软件的AT命令”的应答处理
|
||||
参数 :
|
||||
cmd:此应答对应的AT命令
|
||||
success:AT命令执行结果,true或者false
|
||||
response:AT命令的应答中的执行结果字符串
|
||||
intermediate:AT命令的应答中的中间信息
|
||||
返回值:无
|
||||
]]
|
||||
local function rsp(cmd, success, response, intermediate)
|
||||
local prefix = string.match(cmd, "AT(%+%u+)")
|
||||
--查询序列号
|
||||
if cmd == "AT+WISN?" then
|
||||
result = (intermediate~="*CME ERROR: Missing SN")
|
||||
if result then
|
||||
sn = intermediate
|
||||
sys.publish('SN_READY_IND')
|
||||
end
|
||||
if setSnCbFnc then setSnCbFnc(result) end
|
||||
--查询IMEI
|
||||
elseif cmd == "AT+CGSN" then
|
||||
imei = intermediate
|
||||
if setImeiCbFnc then setImeiCbFnc(true) end
|
||||
sys.publish('IMEI_READY_IND')
|
||||
--查询模块温度
|
||||
elseif cmd =="AT+RFTEMPERATURE?" then
|
||||
temp = string.match(intermediate, ':(.+)')
|
||||
if getTemperatureCbFnc and type(getTemperatureCbFnc)=="function" then
|
||||
if success then
|
||||
getTemperatureCbFnc(temp)
|
||||
else
|
||||
getTemperatureCbFnc("")
|
||||
end
|
||||
end
|
||||
--查询模块型号
|
||||
elseif cmd == 'AT+CGMM' then
|
||||
modeltype = string.match(intermediate, '"(.+)"')
|
||||
if success then
|
||||
sys.publish('MODEL_NUMBER_READY_IND')
|
||||
end
|
||||
elseif cmd == 'AT+VER' then
|
||||
ver = intermediate
|
||||
elseif prefix == '+CCLK' then
|
||||
if success then
|
||||
sys.publish('TIME_UPDATE_IND')
|
||||
setTimeReport()
|
||||
end
|
||||
if setClkCbFnc then setClkCbFnc(getClock(),success) end
|
||||
elseif cmd:match("AT%+WISN=") then
|
||||
if success then
|
||||
req("AT+WISN?")
|
||||
else
|
||||
if setSnCbFnc then setSnCbFnc(false) end
|
||||
end
|
||||
elseif cmd:match("AT%+CALIBINFO%?") then
|
||||
if intermediate then
|
||||
local LTE_afc = intermediate:match("LTE_afc:(%d)")
|
||||
local LTE_TDD_agc = intermediate:match("LTE_TDD_agc:(%d)")
|
||||
local LTE_TDD_apc = intermediate:match("LTE_TDD_apc:(%d)")
|
||||
local LTE_FDD_agc = intermediate:match("LTE_FDD_agc:(%d)")
|
||||
local LTE_FDD_apc = intermediate:match("LTE_FDD_apc:(%d)")
|
||||
local ANT_LTE = intermediate:match("ANT_LTE:(%d)")
|
||||
|
||||
calib = (LTE_afc == "1" and LTE_TDD_agc == "1" and LTE_TDD_apc == "1" and LTE_FDD_agc == "1" and LTE_FDD_apc == "1")
|
||||
ant = (ANT_LTE == "1")
|
||||
end
|
||||
elseif cmd:match("AT%*CALINFO%=R") then
|
||||
if intermediate then
|
||||
local LteTest=intermediate:match("LteTest%,PASS")
|
||||
local LteCal=intermediate:match("LteCal%,PASS")
|
||||
if LteCal then
|
||||
calib=(LteCal=="LteCal,PASS")
|
||||
end
|
||||
if LteTest then
|
||||
ant=(LteTest=="LteTest,PASS")
|
||||
end
|
||||
end
|
||||
elseif cmd:match("AT%+WIMEI=") then
|
||||
if success then
|
||||
req("AT+CGSN")
|
||||
else
|
||||
if setImeiCbFnc then setImeiCbFnc(false) end
|
||||
end
|
||||
elseif cmd:match("AT%+MUID?") then
|
||||
if intermediate then muid = intermediate:match("+MUID:%s*(.+)") end
|
||||
end
|
||||
end
|
||||
|
||||
--- 获取core固件名
|
||||
-- @return string version,core固件名
|
||||
-- @usage
|
||||
-- local version = misc.getVersion()
|
||||
-- 如果core为Luat_V0026_RDA8910_TTS_FLOAT,则version为string类型的"Luat_V0026_RDA8910_TTS_FLOAT"
|
||||
function getVersion()
|
||||
return rtos.get_version()
|
||||
end
|
||||
|
||||
--- 设置系统时间
|
||||
-- @table t 系统时间,格式参考:{year=2017,month=2,day=14,hour=14,min=2,sec=58}
|
||||
-- @function[opt=nil] cbFnc 设置结果回调函数,回调函数的调用形式为:
|
||||
-- cbFnc(time,result)
|
||||
-- result为true表示成功,false或者nil为失败
|
||||
-- time表示设置之后的系统时间,table类型,例如{year=2017,month=2,day=14,hour=14,min=19,sec=23}
|
||||
-- @return nil
|
||||
-- @usage misc.setClock({year=2017,month=2,day=14,hour=14,min=2,sec=58})
|
||||
function setClock(t,cbFnc)
|
||||
if type(t) ~= "table" or (t.year-2000>38) then
|
||||
if cbFnc then cbFnc(getClock(),false) end
|
||||
return
|
||||
end
|
||||
setClkCbFnc = cbFnc
|
||||
req(string.format("AT+CCLK=\"%02d/%02d/%02d,%02d:%02d:%02d+32\"", string.sub(t.year, 3, 4), t.month, t.day, t.hour, t.min, t.sec), nil, rsp)
|
||||
end
|
||||
--- 获取系统时间
|
||||
-- @return table time,{year=2017,month=2,day=14,hour=14,min=19,sec=23}
|
||||
-- @usage time = getClock()
|
||||
function getClock()
|
||||
return os.date("*t")
|
||||
end
|
||||
--- 获取星期
|
||||
-- @return number week,1-7分别对应周一到周日
|
||||
-- @usage week = misc.getWeek()
|
||||
function getWeek()
|
||||
local clk = os.date("*t")
|
||||
return ((clk.wday == 1) and 7 or (clk.wday - 1))
|
||||
end
|
||||
--- 获取校准标志
|
||||
-- @return bool calib, true表示已校准,false或者nil表示未校准
|
||||
-- @usage calib = misc.getCalib()
|
||||
function getCalib()
|
||||
return calib
|
||||
end
|
||||
|
||||
--- 获取耦合测试标志
|
||||
-- @return bool ant, true表示已耦合测试,false或者nil表示未耦合测试
|
||||
-- @usage ant = misc.getAnt()
|
||||
function getAnt()
|
||||
return ant
|
||||
end
|
||||
--- 设置SN
|
||||
-- @string s 新sn的字符串
|
||||
-- @function[opt=nil] cbFnc 设置结果回调函数,回调函数的调用形式为:
|
||||
-- cnFnc(result),result为true表示成功,false或者nil为失败
|
||||
-- @return nil
|
||||
-- @usage
|
||||
-- misc.setSn("1234567890")
|
||||
-- misc.setSn("1234567890",cbFnc)
|
||||
function setSn(s, cbFnc)
|
||||
if s ~= sn then
|
||||
setSnCbFnc = cbFnc
|
||||
req("AT+WISN=\"" .. s .. "\"")
|
||||
else
|
||||
if cbFnc then cbFnc(true) end
|
||||
end
|
||||
end
|
||||
--- 获取模块序列号
|
||||
-- @return string sn,序列号,如果未获取到返回""
|
||||
-- 注意:开机lua脚本运行之后,会发送at命令去查询sn,所以需要一定时间才能获取到sn。开机后立即调用此接口,基本上返回""
|
||||
-- @usage sn = misc.getSn()
|
||||
function getSn()
|
||||
return sn or ""
|
||||
end
|
||||
--- 设置IMEI
|
||||
-- @string s 新IMEI字符串
|
||||
-- @function[opt=nil] cbFnc 设置结果回调函数,回调函数的调用形式为:
|
||||
-- cnFnc(result),result为true表示成功,false或者nil为失败
|
||||
-- @return nil
|
||||
-- @usage misc.setImei(”359759002514931”)
|
||||
function setImei(s, cbFnc)
|
||||
if s ~= imei then
|
||||
setImeiCbFnc = cbFnc
|
||||
req("AT+WIMEI=\"" .. s .. "\"")
|
||||
else
|
||||
if cbFnc then cbFnc(true) end
|
||||
end
|
||||
end
|
||||
--- 获取模块IMEI
|
||||
-- @return string,IMEI号,如果未获取到返回""
|
||||
-- 注意:开机lua脚本运行之后,会发送at命令去查询imei,所以需要一定时间才能获取到imei。开机后立即调用此接口,基本上返回""
|
||||
-- @usage imei = misc.getImei()
|
||||
function getImei()
|
||||
return imei or ""
|
||||
end
|
||||
--- 获取模块型号
|
||||
-- @return string,模块型号,如果未获取到返回""
|
||||
-- 例如:模块型号为724UG,则返回值为Air724UG;模块型号为722UG,则返回值为Air722UG;模块型号为820UG,则返回值为Air820UG
|
||||
-- 注意:开机lua脚本运行之后,会发送at命令去查询模块型号,所以需要一定时间才能获取到模块型号。开机后立即调用此接口,基本上返回""
|
||||
-- @usage modeltype = getModelType()
|
||||
function getModelType()
|
||||
return modeltype or ""
|
||||
end
|
||||
|
||||
-- 获取模块温度
|
||||
-- @return string,模块温度,如果要对该值进行运算,可以使用带float的固件将该值转为number
|
||||
-- 例如:模块温度为29.77摄氏度,则返回值为29.77
|
||||
function getTemperature(cb)
|
||||
getTemperatureCbFnc = cb
|
||||
ril.request("AT+RFTEMPERATURE?")
|
||||
end
|
||||
|
||||
--- 获取VBAT的电池电压
|
||||
-- @return number,电池电压,单位mv
|
||||
-- @usage vb = getVbatt()
|
||||
function getVbatt()
|
||||
if type(pmd.libScriptInit)=="function" then pmd.libScriptInit() end
|
||||
local v1, v2, v3, v4, v5 = pmd.param_get()
|
||||
return v2
|
||||
end
|
||||
|
||||
--- 获取VBUS连接状态
|
||||
-- @return boolean,true表示VBUS连接,false表示未连接
|
||||
-- @usage vbus = getVbus()
|
||||
function getVbus()
|
||||
local v1, v2, v3, v4, v5 = pmd.param_get()
|
||||
log.info("misc.getVbus",v1, v2, v3, v4, v5)
|
||||
return v4
|
||||
end
|
||||
|
||||
--- 获取模块MUID
|
||||
-- @return string,MUID号,如果未获取到返回""
|
||||
-- 注意:开机lua脚本运行之后,会发送at命令去查询muid,所以需要一定时间才能获取到muid。开机后立即调用此接口,基本上返回""
|
||||
-- @usage muid = misc.getMuid()
|
||||
function getMuid()
|
||||
return muid or ""
|
||||
end
|
||||
|
||||
--- 打开并且配置PWM(支持2路PWM,仅支持输出)
|
||||
-- @number id PWM输出通道,仅支持0和1
|
||||
-- 0使用MODULE_STATUS/GPIO_5引脚
|
||||
-- 1使用GPIO_13引脚,注意:上电的时候不要把 GPIO_13 拉高到V_GLOBAL_1V8,否则模块会进入校准模式,不正常开机
|
||||
-- @number para1 当id为0时,para1表示分频系数,最大值为2047;分频系数和频率的换算关系为:频率=25000000/para1(Hz);例如para1为500时,频率为50000Hz
|
||||
-- 分频系数和周期的换算关系为:周期=para1/25000000(s);例如para1为500时,周期为20us
|
||||
-- 当id为1时,para1表示时钟周期,取值范围为0-7,仅支持整数
|
||||
-- 0-7分别对应125、250、500、1000、1500、2000、2500、3000毫秒
|
||||
-- @number para2 当id为0时,para2表示占空比计算系数,最大值为1023;占空比计算系数和占空比的计算关系为:占空比=para2/para1
|
||||
-- 当id为1时,para2表示一个时钟周期内的高电平时间,取值范围为1-15,仅支持整数
|
||||
-- 1-15分别对应15.6、31.2、46.8、62、78、94、110、125、140、156、172、188、200、218、234毫秒
|
||||
-- @return nil
|
||||
-- @usage
|
||||
-- 通道0,频率为50000Hz,占空比为0.2:
|
||||
-- 频率为50000Hz,表示时钟周期为1/50000=0.00002秒=0.02毫秒=20微秒
|
||||
-- 占空比表示在一个时钟周期内,高电平的时长/时钟周期的时长,本例子中的0.2就表示,高电平时长为4微秒,低电平时长为16微秒
|
||||
-- misc.openPwm(0,500,100)
|
||||
--
|
||||
-- 通道1,时钟周期为500ms,高电平时间为125毫秒:
|
||||
-- misc.openPwm(1,2,8)
|
||||
function openPwm(id, para1, para2)
|
||||
pwm.open(id)
|
||||
pwm.set(id,para1,para2)
|
||||
end
|
||||
|
||||
--- 关闭PWM
|
||||
-- @number id PWM输出通道,仅支持0和1
|
||||
-- 0使用MODULE_STATUS/GPIO_5引脚
|
||||
-- 1使用GPIO_13引脚,注意:上电的时候不要把 GPIO_13 拉高到V_GLOBAL_1V8,否则模块会进入校准模式,不正常开机
|
||||
-- @return nil
|
||||
function closePwm(id)
|
||||
assert(id == 0 or id == 1, "closepwm id error: " .. id)
|
||||
pwm.close(id)
|
||||
end
|
||||
|
||||
--注册以下AT命令的应答处理函数
|
||||
ril.regRsp("+WISN", rsp)
|
||||
ril.regRsp("+CGSN", rsp)
|
||||
ril.regRsp("+RFTEMPERATURE",rsp)
|
||||
ril.regRsp("+CGMM", rsp)
|
||||
ril.regRsp("+MUID", rsp)
|
||||
ril.regRsp("+WIMEI", rsp)
|
||||
ril.regRsp("+AMFAC", rsp)
|
||||
--ril.regRsp('+VER', rsp, 4, '^[%w_]+$')
|
||||
ril.regRsp("+CALIBINFO",rsp)
|
||||
ril.regRsp("*CALINFO",rsp)
|
||||
--req('AT+VER')
|
||||
--查询序列号
|
||||
req("AT+WISN?")
|
||||
--查询IMEI
|
||||
req("AT+CGSN")
|
||||
req("AT+MUID?")
|
||||
req("AT*EXINFO?")
|
||||
--查询模块温度
|
||||
-- req("AT+RFTEMPERATURE?")
|
||||
--查询模块型号
|
||||
if string.match(rtos.get_version(),"ASR1603") then
|
||||
req("AT*CALINFO=R,LteCal")
|
||||
req("AT*CALINFO=R,LteTest")
|
||||
end
|
||||
req("AT+CGMM")
|
||||
setTimeReport()
|
||||
510
4G/源代码/lib/mqtt.lua
Normal file
510
4G/源代码/lib/mqtt.lua
Normal file
@@ -0,0 +1,510 @@
|
||||
--- 模块功能:MQTT客户端
|
||||
-- @module mqtt
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2017.10.24
|
||||
require "log"
|
||||
require "socket"
|
||||
require "utils"
|
||||
module(..., package.seeall)
|
||||
|
||||
-- MQTT 指令id
|
||||
local CONNECT, CONNACK, PUBLISH, PUBACK, PUBREC, PUBREL, PUBCOMP, SUBSCRIBE, SUBACK, UNSUBSCRIBE, UNSUBACK, PINGREQ, PINGRESP, DISCONNECT = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
|
||||
local CLIENT_COMMAND_TIMEOUT = 60000
|
||||
|
||||
local function encodeLen(len)
|
||||
local s = ""
|
||||
local digit
|
||||
repeat
|
||||
digit = len % 128
|
||||
len = (len - digit) / 128
|
||||
if len > 0 then
|
||||
digit = bit.bor(digit, 0x80)
|
||||
end
|
||||
s = s .. string.char(digit)
|
||||
until (len <= 0)
|
||||
return s
|
||||
end
|
||||
|
||||
local function encodeUTF8(s)
|
||||
if not s or #s == 0 then
|
||||
return ""
|
||||
else
|
||||
return pack.pack(">P", s)
|
||||
end
|
||||
end
|
||||
|
||||
local function packCONNECT(clientId, keepAlive, username, password, cleanSession, will, version)
|
||||
local content = pack.pack(">PbbHPAAAA",
|
||||
version == "3.1" and "MQIsdp" or "MQTT",
|
||||
version == "3.1" and 3 or 4,
|
||||
(#username == 0 and 0 or 1) * 128 + (#password == 0 and 0 or 1) * 64 + will.retain * 32 + will.qos * 8 + will.flag * 4 + cleanSession * 2,
|
||||
keepAlive,
|
||||
clientId,
|
||||
encodeUTF8(will.topic),
|
||||
encodeUTF8(will.payload),
|
||||
encodeUTF8(username),
|
||||
encodeUTF8(password))
|
||||
return pack.pack(">bAA",
|
||||
CONNECT * 16,
|
||||
encodeLen(string.len(content)),
|
||||
content)
|
||||
end
|
||||
|
||||
local function packSUBSCRIBE(dup, packetId, topics)
|
||||
local header = SUBSCRIBE * 16 + dup * 8 + 2
|
||||
local data = pack.pack(">H", packetId)
|
||||
for topic, qos in pairs(topics) do
|
||||
data = data .. pack.pack(">Pb", topic, qos)
|
||||
end
|
||||
return pack.pack(">bAA", header, encodeLen(#data), data)
|
||||
end
|
||||
|
||||
local function packUNSUBSCRIBE(dup, packetId, topics)
|
||||
local header = UNSUBSCRIBE * 16 + dup * 8 + 2
|
||||
local data = pack.pack(">H", packetId)
|
||||
for k, topic in pairs(topics) do
|
||||
data = data .. pack.pack(">P", topic)
|
||||
end
|
||||
return pack.pack(">bAA", header, encodeLen(#data), data)
|
||||
end
|
||||
|
||||
local function packPUBLISH(dup, qos, retain, packetId, topic, payload)
|
||||
local header = PUBLISH * 16 + dup * 8 + qos * 2 + retain
|
||||
local len = 2 + #topic + #payload
|
||||
if qos > 0 then
|
||||
return pack.pack(">bAPHA", header, encodeLen(len + 2), topic, packetId, payload)
|
||||
else
|
||||
return pack.pack(">bAPA", header, encodeLen(len), topic, payload)
|
||||
end
|
||||
end
|
||||
|
||||
local function packACK(id, dup, packetId)
|
||||
return pack.pack(">bbH", id * 16 + dup * 8 + (id == PUBREL and 1 or 0) * 2, 0x02, packetId)
|
||||
end
|
||||
|
||||
local function packZeroData(id, dup, qos, retain)
|
||||
dup = dup or 0
|
||||
qos = qos or 0
|
||||
retain = retain or 0
|
||||
return pack.pack(">bb", id * 16 + dup * 8 + qos * 2 + retain, 0)
|
||||
end
|
||||
|
||||
local function unpack(s)
|
||||
if #s < 2 then return end
|
||||
log.debug("mqtt.unpack", #s, string.toHex(string.sub(s, 1, 50)))
|
||||
|
||||
-- read remaining length
|
||||
local len = 0
|
||||
local multiplier = 1
|
||||
local pos = 2
|
||||
|
||||
repeat
|
||||
if pos > #s then return end
|
||||
local digit = string.byte(s, pos)
|
||||
len = len + ((digit % 128) * multiplier)
|
||||
multiplier = multiplier * 128
|
||||
pos = pos + 1
|
||||
until digit < 128
|
||||
|
||||
if #s < len + pos - 1 then return end
|
||||
|
||||
local header = string.byte(s, 1)
|
||||
|
||||
local packet = {id = (header - (header % 16)) / 16, dup = ((header % 16) - ((header % 16) % 8)) / 8, qos = bit.band(header, 0x06) / 2, retain = bit.band(header, 0x01)}
|
||||
local nextpos
|
||||
|
||||
if packet.id == CONNACK then
|
||||
nextpos, packet.ackFlag, packet.rc = pack.unpack(s, "bb", pos)
|
||||
elseif packet.id == SUBACK then
|
||||
nextpos, packet.ackFlag, packet.rc = pack.unpack(s, "bb", pos)
|
||||
if len >= 2 then
|
||||
nextpos, packet.packetId = pack.unpack(s, ">H", pos)
|
||||
packet.grantedQos = string.sub(s, nextpos, pos + len - 1)
|
||||
else
|
||||
packet.packetId = 0
|
||||
packet.grantedQos = ""
|
||||
end
|
||||
elseif packet.id == PUBLISH then
|
||||
nextpos, packet.topic = pack.unpack(s, ">P", pos)
|
||||
if packet.qos > 0 then
|
||||
nextpos, packet.packetId = pack.unpack(s, ">H", nextpos)
|
||||
end
|
||||
packet.payload = string.sub(s, nextpos, pos + len - 1)
|
||||
elseif packet.id ~= PINGRESP then
|
||||
if len >= 2 then
|
||||
nextpos, packet.packetId = pack.unpack(s, ">H", pos)
|
||||
else
|
||||
packet.packetId = 0
|
||||
end
|
||||
end
|
||||
|
||||
return packet, pos + len
|
||||
end
|
||||
|
||||
local mqttc = {}
|
||||
mqttc.__index = mqttc
|
||||
|
||||
--- 创建一个mqtt client实例
|
||||
-- @string clientId 确保设备唯一性
|
||||
-- @number[opt=300] keepAlive 心跳间隔(单位为秒),默认300秒
|
||||
-- @string[opt=""] username 用户名,用户名为空配置为""或者nil
|
||||
-- @string[opt=""] password 密码,密码为空配置为""或者nil
|
||||
-- @number[opt=1] cleanSession 1/0
|
||||
-- @table[opt=nil] will 遗嘱参数,格式为{qos=,retain=,topic=,payload=}
|
||||
-- @string[opt="3.1.1"] version MQTT版本号,仅支持"3.1"和"3.1.1"
|
||||
-- @return table mqttc client实例
|
||||
-- @usage
|
||||
-- mqttc = mqtt.client("clientid-123")
|
||||
-- mqttc = mqtt.client("clientid-123",200)
|
||||
-- mqttc = mqtt.client("clientid-123",nil,"user","password")
|
||||
-- mqttc = mqtt.client("clientid-123",nil,"user","password",nil,{qos=0,retain=0,topic="willTopic",payload="willTopic"},"3.1")
|
||||
function client(clientId, keepAlive, username, password, cleanSession, will, version)
|
||||
local o = {}
|
||||
local packetId = 1
|
||||
|
||||
if will then
|
||||
will.flag = 1
|
||||
else
|
||||
will = {flag = 0, qos = 0, retain = 0, topic = "", payload = ""}
|
||||
end
|
||||
|
||||
o.clientId = clientId
|
||||
o.keepAlive = keepAlive or 300
|
||||
o.username = username or ""
|
||||
o.password = password or ""
|
||||
o.cleanSession = cleanSession or 1
|
||||
o.version = version or "3.1.1"
|
||||
o.will = will
|
||||
o.commandTimeout = CLIENT_COMMAND_TIMEOUT
|
||||
o.cache = {}-- 接收到的mqtt数据包缓冲
|
||||
o.inbuf = "" -- 未完成的数据缓冲
|
||||
o.connected = false
|
||||
o.getNextPacketId = function()
|
||||
packetId = packetId == 65535 and 1 or (packetId + 1)
|
||||
return packetId
|
||||
end
|
||||
o.lastOTime = 0
|
||||
|
||||
setmetatable(o, mqttc)
|
||||
|
||||
return o
|
||||
end
|
||||
|
||||
-- 检测是否需要发送心跳包
|
||||
function mqttc:checkKeepAlive()
|
||||
if self.keepAlive == 0 then return true end
|
||||
if os.time() - self.lastOTime >= self.keepAlive then
|
||||
if not self:write(packZeroData(PINGREQ)) then
|
||||
log.info("mqtt.client:", "pingreq send fail")
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-- 发送mqtt数据
|
||||
function mqttc:write(data)
|
||||
log.debug("mqtt.client:write", string.toHex(string.sub(data, 1, 50)))
|
||||
local r = self.io:send(data)
|
||||
if r then self.lastOTime = os.time() end
|
||||
return r
|
||||
end
|
||||
|
||||
-- 接收mqtt数据包
|
||||
function mqttc:read(timeout, msg, msgNoResume)
|
||||
if not self:checkKeepAlive() then
|
||||
log.warn("mqtt.read checkKeepAlive fail")
|
||||
return false
|
||||
end
|
||||
|
||||
-- 处理之前缓冲的数据
|
||||
local packet, nextpos = unpack(self.inbuf)
|
||||
if packet then
|
||||
self.inbuf = string.sub(self.inbuf, nextpos)
|
||||
return true, packet
|
||||
end
|
||||
|
||||
while true do
|
||||
local recvTimeout
|
||||
|
||||
if self.keepAlive == 0 then
|
||||
recvTimeout = timeout
|
||||
else
|
||||
local kaTimeout = (self.keepAlive - (os.time() - self.lastOTime)) * 1000
|
||||
recvTimeout = kaTimeout > timeout and timeout or kaTimeout
|
||||
end
|
||||
|
||||
local r, s, p = self.io:recv(recvTimeout == 0 and 5 or recvTimeout, msg, msgNoResume)
|
||||
if r then
|
||||
self.inbuf = self.inbuf .. s
|
||||
elseif s == "timeout" then -- 超时,判断是否需要发送心跳包
|
||||
if not self:checkKeepAlive() then
|
||||
return false
|
||||
elseif timeout <= recvTimeout then
|
||||
return false, "timeout"
|
||||
else
|
||||
timeout = timeout - recvTimeout
|
||||
end
|
||||
else -- 其他错误直接返回
|
||||
return r, s, p
|
||||
end
|
||||
local packet, nextpos = unpack(self.inbuf)
|
||||
if packet then
|
||||
--self.lastIOTime = os.time()
|
||||
self.inbuf = string.sub(self.inbuf, nextpos)
|
||||
if packet.id ~= PINGRESP then
|
||||
return true, packet
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- 等待接收指定的mqtt消息
|
||||
function mqttc:waitfor(id, timeout, msg, msgNoResume)
|
||||
for index, packet in ipairs(self.cache) do
|
||||
if packet.id == id then
|
||||
return true, table.remove(self.cache, index)
|
||||
end
|
||||
end
|
||||
|
||||
while true do
|
||||
local insertCache = true
|
||||
local r, data, param = self:read(timeout, msg, msgNoResume)
|
||||
if r then
|
||||
if data.id == PUBLISH then
|
||||
if data.qos > 0 then
|
||||
if not self:write(packACK(data.qos == 1 and PUBACK or PUBREC, 0, data.packetId)) then
|
||||
log.info("mqtt.client:waitfor", "send publish ack failed", data.qos)
|
||||
return false
|
||||
end
|
||||
end
|
||||
elseif data.id == PUBREC or data.id == PUBREL then
|
||||
if not self:write(packACK(data.id == PUBREC and PUBREL or PUBCOMP, 0, data.packetId)) then
|
||||
log.info("mqtt.client:waitfor", "send ack fail", data.id == PUBREC and "PUBREC" or "PUBCOMP")
|
||||
return false
|
||||
end
|
||||
insertCache = false
|
||||
end
|
||||
|
||||
if data.id == id then
|
||||
return true, data
|
||||
end
|
||||
if insertCache then table.insert(self.cache, data) end
|
||||
else
|
||||
return false, data, param
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- 连接mqtt服务器
|
||||
-- @string host 服务器地址
|
||||
-- @param port string或者number类型,服务器端口
|
||||
-- @string[opt="tcp"] transport "tcp"或者"tcp_ssl"
|
||||
-- @table[opt=nil] cert table或者nil类型,ssl证书,当transport为"tcp_ssl"时,此参数才有意义。cert格式如下:
|
||||
-- {
|
||||
-- caCert = "ca.crt", --CA证书文件(Base64编码 X.509格式),如果存在此参数,则表示客户端会对服务器的证书进行校验;不存在则不校验
|
||||
-- clientCert = "client.crt", --客户端证书文件(Base64编码 X.509格式),服务器对客户端的证书进行校验时会用到此参数
|
||||
-- clientKey = "client.key", --客户端私钥文件(Base64编码 X.509格式)
|
||||
-- clientPassword = "123456", --客户端证书文件密码[可选]
|
||||
-- }
|
||||
-- @number[opt=120] timeout 可选参数,socket连接超时时间,单位秒
|
||||
-- @return result true表示成功,false或者nil表示失败
|
||||
-- @usage mqttc = mqtt.client("clientid-123", nil, nil, false); mqttc:connect("mqttserver.com", 1883, "tcp", 5)
|
||||
function mqttc:connect(host, port, transport, cert, timeout)
|
||||
if self.connected then
|
||||
log.info("mqtt.client:connect", "has connected")
|
||||
return false
|
||||
end
|
||||
|
||||
if self.io then
|
||||
self.io:close()
|
||||
self.io = nil
|
||||
end
|
||||
|
||||
if transport and transport ~= "tcp" and transport ~= "tcp_ssl" then
|
||||
log.info("mqtt.client:connect", "invalid transport", transport)
|
||||
return false
|
||||
end
|
||||
|
||||
self.io = socket.tcp(transport == "tcp_ssl" or type(cert) == "table", cert)
|
||||
|
||||
if not self.io:connect(host, port, timeout) then
|
||||
log.info("mqtt.client:connect", "connect host fail")
|
||||
return false
|
||||
end
|
||||
|
||||
if not self:write(packCONNECT(self.clientId, self.keepAlive, self.username, self.password, self.cleanSession, self.will, self.version)) then
|
||||
log.info("mqtt.client:connect", "send fail")
|
||||
return false
|
||||
end
|
||||
|
||||
local r, packet = self:waitfor(CONNACK, self.commandTimeout, nil, true)
|
||||
-- if not r or packet.rc ~= 0 then
|
||||
-- log.info("mqtt.client:connect", "connack error", r and packet.rc or -1)
|
||||
-- return false,packet.rc
|
||||
-- end
|
||||
if (not r) or (not packet) or packet.rc ~= 0 then
|
||||
log.info("mqtt.client:connect", "connack error", r and packet.rc or -1)
|
||||
return false, packet and packet.rc or -1
|
||||
end
|
||||
|
||||
self.connected = true
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
--- 订阅主题
|
||||
-- @param topic string或者table类型,一个主题时为string类型,多个主题时为table类型,主题内容为UTF8编码
|
||||
-- @param[opt=0] qos number或者nil,topic为一个主题时,qos为number类型(0/1/2,默认0);topic为多个主题时,qos为nil
|
||||
-- @return bool true表示成功,false或者nil表示失败
|
||||
-- @usage
|
||||
-- mqttc:subscribe("/abc", 0) -- subscribe topic "/abc" with qos = 0
|
||||
-- mqttc:subscribe({["/topic1"] = 0, ["/topic2"] = 1, ["/topic3"] = 2}) -- subscribe multi topic
|
||||
function mqttc:subscribe(topic, qos)
|
||||
if not self.connected then
|
||||
log.info("mqtt.client:subscribe", "not connected")
|
||||
return false
|
||||
end
|
||||
|
||||
local topics
|
||||
if type(topic) == "string" then
|
||||
topics = {[topic] = qos and qos or 0}
|
||||
else
|
||||
topics = topic
|
||||
end
|
||||
|
||||
if not self:write(packSUBSCRIBE(0, self.getNextPacketId(), topics)) then
|
||||
log.info("mqtt.client:subscribe", "send failed")
|
||||
return false
|
||||
end
|
||||
|
||||
local r, packet = self:waitfor(SUBACK, self.commandTimeout, nil, true)
|
||||
if not r then
|
||||
log.info("mqtt.client:subscribe", "wait ack failed")
|
||||
return false
|
||||
end
|
||||
|
||||
if not (packet.grantedQos and packet.grantedQos~="" and not packet.grantedQos:match(string.char(0x80))) then
|
||||
log.info("mqtt.client:subscribe", "suback grant qos error", packet.grantedQos)
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
--- 取消订阅主题
|
||||
-- @param topic string或者table类型,一个主题时为string类型,多个主题时为table类型,主题内容为UTF8编码
|
||||
-- @return bool true表示成功,false或者nil表示失败
|
||||
-- @usage
|
||||
-- mqttc:unsubscribe("/abc") -- unsubscribe topic "/abc"
|
||||
-- mqttc:unsubscribe({"/topic1", "/topic2", "/topic3"}) -- unsubscribe multi topic
|
||||
function mqttc:unsubscribe(topic)
|
||||
if not self.connected then
|
||||
log.info("mqtt.client:unsubscribe", "not connected")
|
||||
return false
|
||||
end
|
||||
|
||||
local topics
|
||||
if type(topic) == "string" then
|
||||
topics = {topic}
|
||||
else
|
||||
topics = topic
|
||||
end
|
||||
|
||||
if not self:write(packUNSUBSCRIBE(0, self.getNextPacketId(), topics)) then
|
||||
log.info("mqtt.client:unsubscribe", "send failed")
|
||||
return false
|
||||
end
|
||||
|
||||
if not self:waitfor(UNSUBACK, self.commandTimeout, nil, true) then
|
||||
log.info("mqtt.client:unsubscribe", "wait ack failed")
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
--- 发布一条消息
|
||||
-- @string topic UTF8编码的字符串
|
||||
-- @string payload 用户自己控制payload的编码,mqtt.lua不会对payload做任何编码转换
|
||||
-- @number[opt=0] qos 0/1/2, default 0
|
||||
-- @number[opt=0] retain 0或者1
|
||||
-- @return bool 发布成功返回true,失败返回false
|
||||
-- @usage
|
||||
-- mqttc = mqtt.client("clientid-123", nil, nil, false)
|
||||
-- mqttc:connect("mqttserver.com", 1883, "tcp")
|
||||
-- mqttc:publish("/topic", "publish from luat mqtt client", 0)
|
||||
function mqttc:publish(topic, payload, qos, retain)
|
||||
if not self.connected then
|
||||
log.info("mqtt.client:publish", "not connected")
|
||||
return false
|
||||
end
|
||||
|
||||
qos = qos or 0
|
||||
retain = retain or 0
|
||||
|
||||
if not self:write(packPUBLISH(0, qos, retain, qos > 0 and self.getNextPacketId() or 0, topic, payload)) then
|
||||
log.info("mqtt.client:publish", "socket send failed")
|
||||
return false
|
||||
end
|
||||
|
||||
if qos == 0 then return true end
|
||||
|
||||
if not self:waitfor(qos == 1 and PUBACK or PUBCOMP, self.commandTimeout, nil, true) then
|
||||
log.warn("mqtt.client:publish", "wait ack timeout")
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
--- 接收消息
|
||||
-- @number timeout 接收超时时间,单位毫秒
|
||||
-- @string[opt=nil] msg 可选参数,控制socket所在的线程退出recv阻塞状态
|
||||
-- @return result 数据接收结果,true表示成功,false表示失败
|
||||
-- @return data
|
||||
-- 如果result为true,表示服务器发过来的mqtt包
|
||||
--
|
||||
-- 如果result为false,超时失败,data为"timeout"
|
||||
-- 如果result为false,msg控制退出,data为msg的字符串
|
||||
-- 如果result为false,socket连接被动断开控制退出,data为"CLOSED"
|
||||
-- 如果result为false,PDP断开连接控制退出,data为"IP_ERROR_IND"
|
||||
--
|
||||
-- 如果result为false,mqtt不处于连接状态,data为nil
|
||||
-- 如果result为false,收到了PUBLISH报文,发送PUBACK或者PUBREC报文失败,data为nil
|
||||
-- 如果result为false,收到了PUBREC报文,发送PUBREL报文失败,data为nil
|
||||
-- 如果result为false,收到了PUBREL报文,发送PUBCOMP报文失败,data为nil
|
||||
-- 如果result为false,发送PINGREQ报文失败,data为nil
|
||||
-- @return param 如果是msg控制退出,param的值是msg的参数;其余情况无意义,为nil
|
||||
-- @usage
|
||||
-- true, packet = mqttc:receive(2000)
|
||||
-- false, error_message = mqttc:receive(2000)
|
||||
-- false, msg, para = mqttc:receive(2000,"APP_SEND_DATA")
|
||||
function mqttc:receive(timeout, msg)
|
||||
if not self.connected then
|
||||
log.info("mqtt.client:receive", "not connected")
|
||||
return false
|
||||
end
|
||||
|
||||
return self:waitfor(PUBLISH, timeout, msg)
|
||||
end
|
||||
|
||||
--- 断开与服务器的连接
|
||||
-- @return nil
|
||||
-- @usage
|
||||
-- mqttc = mqtt.client("clientid-123", nil, nil, false)
|
||||
-- mqttc:connect("mqttserver.com", 1883, "tcp")
|
||||
-- process data
|
||||
-- mqttc:disconnect()
|
||||
function mqttc:disconnect()
|
||||
if self.io then
|
||||
if self.connected then self:write(packZeroData(DISCONNECT)) end
|
||||
self.io:close()
|
||||
self.io = nil
|
||||
end
|
||||
self.cache = {}
|
||||
self.inbuf = ""
|
||||
self.connected = false
|
||||
end
|
||||
787
4G/源代码/lib/net.lua
Normal file
787
4G/源代码/lib/net.lua
Normal file
@@ -0,0 +1,787 @@
|
||||
---模块功能:网络管理、信号查询、GSM网络状态查询、网络指示灯控制、临近小区信息查询
|
||||
-- @module net
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2017.02.17
|
||||
|
||||
require "sys"
|
||||
require "ril"
|
||||
require "pio"
|
||||
require "sim"
|
||||
require "log"
|
||||
require "utils"
|
||||
module(..., package.seeall)
|
||||
|
||||
--加载常用的全局函数至本地
|
||||
local publish = sys.publish
|
||||
|
||||
--netmode define
|
||||
NetMode_noNet= 0
|
||||
NetMode_GSM= 1--2G
|
||||
NetMode_EDGE= 2--2.5G
|
||||
NetMode_TD= 3--3G
|
||||
NetMode_LTE= 4--4G
|
||||
NetMode_WCDMA= 5--3G
|
||||
local netMode = NetMode_noNet
|
||||
|
||||
--网络状态:
|
||||
--INIT:开机初始化中的状态
|
||||
--REGISTERED:注册上GSM网络
|
||||
--UNREGISTER:未注册上GSM网络
|
||||
local state = "INIT"
|
||||
--SIM卡状态:true为异常,false或者nil为正常
|
||||
local simerrsta
|
||||
-- 飞行模式状态
|
||||
flyMode = false
|
||||
|
||||
--lac:位置区ID
|
||||
--ci:小区ID
|
||||
--rssi:信号强度
|
||||
--rsrp:信号接收功率
|
||||
local lac, ci, rssi, rsrp, band = "", "", 0, 0, ""
|
||||
|
||||
--cellinfo:当前小区和临近小区信息表
|
||||
--multicellcb:获取多小区的回调函数
|
||||
local cellinfo, multicellcb = {}
|
||||
local curCellSeted
|
||||
|
||||
local function cops(data)
|
||||
--+COPS: 0,2,"46000",7
|
||||
local fmt,oper = data:match('COPS:%s*%d+%s*,(%d+)%s*,"(%d+)"')
|
||||
log.info("cops",fmt,oper,curCellSeted)
|
||||
if fmt=="2" and not curCellSeted then
|
||||
cellinfo[1].mcc = tonumber(oper:sub(1,3),16)
|
||||
cellinfo[1].mnc = tonumber(oper:sub(4,5),16)
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:creg
|
||||
功能 :解析CREG信息
|
||||
参数 :data:CREG信息字符串,例如+CREG: 2、+CREG: 1,"18be","93e1"、+CREG: 5,"18a7","cb51"
|
||||
返回值:无
|
||||
]]
|
||||
local function creg(data)
|
||||
local p1, s,act
|
||||
local prefix = (netMode == NetMode_LTE) and "+CEREG: " or (netMode == NetMode_noNet and "+CREG: " or "+CGREG: ")
|
||||
log.info("net.creg1",netMode,prefix)
|
||||
if not data:match(prefix) then
|
||||
--log.info("net.creg2",prefix)
|
||||
if prefix=="+CREG: " then
|
||||
--log.info("net.creg3")
|
||||
prefix = "+CGREG: "
|
||||
if not data:match("+CGREG: ") then
|
||||
log.warn("net.creg1","no match",data)
|
||||
return
|
||||
end
|
||||
elseif prefix=="+CGREG: " then
|
||||
--log.info("net.creg4")
|
||||
prefix = "+CREG: "
|
||||
if not data:match("+CREG: ") then
|
||||
log.warn("net.creg2","no match",data)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
--获取注册状态
|
||||
_, _, p1 = data:find(prefix .. "%d,(%d+)")
|
||||
--log.info("net.creg5",p1 == nil)
|
||||
if p1 == nil then
|
||||
_, _, p1 = data:find(prefix .. "(%d+)")
|
||||
--log.info("net.creg6",p1 == nil)
|
||||
if p1 == nil then return end
|
||||
act = data:match(prefix .. "%d+,.-,.-,(%d+)")
|
||||
else
|
||||
act = data:match(prefix .. "%d,%d+,.-,.-,(%d+)")
|
||||
end
|
||||
|
||||
log.info("net.creg7",p1,act)
|
||||
|
||||
--设置注册状态
|
||||
s = (p1=="1" or p1=="5") and "REGISTERED" or "UNREGISTER"
|
||||
|
||||
--log.info("net.creg8",s,state)
|
||||
if prefix=="+CGREG: " and s=="UNREGISTER" then
|
||||
log.info("net.creg9 ignore!!!")
|
||||
return
|
||||
end
|
||||
--注册状态发生了改变
|
||||
if s ~= state then
|
||||
--临近小区查询处理
|
||||
if s == "REGISTERED" then
|
||||
--产生一个内部消息NET_STATE_CHANGED,表示GSM网络注册状态发生变化
|
||||
publish("NET_STATE_REGISTERED")
|
||||
cengQueryPoll()
|
||||
end
|
||||
state = s
|
||||
end
|
||||
--已注册并且lac或ci发生了变化
|
||||
if state == "REGISTERED" then
|
||||
p2, p3 = data:match("\"(%x+)\",\"(%x+)\"")
|
||||
if p2 and p3 and (lac ~= p2 or ci ~= p3) then
|
||||
lac = p2
|
||||
ci = p3
|
||||
--产生一个内部消息NET_CELL_CHANGED,表示lac或ci发生了变化
|
||||
publish("NET_CELL_CHANGED")
|
||||
--cellinfo[1].mcc = tonumber(sim.getMcc(),16)
|
||||
--cellinfo[1].mnc = tonumber(sim.getMnc(),16)
|
||||
cellinfo[1].lac = tonumber(lac,16)
|
||||
cellinfo[1].ci = tonumber(ci,16)
|
||||
cellinfo[1].rssi = 28
|
||||
end
|
||||
|
||||
if act then
|
||||
if act=="0" then
|
||||
UpdNetMode("^MODE: 3,1")
|
||||
elseif act=="1" then
|
||||
UpdNetMode("^MODE: 3,2")
|
||||
elseif act=="3" then
|
||||
UpdNetMode("^MODE: 3,3")
|
||||
elseif act=="7" then
|
||||
UpdNetMode("^MODE: 17,17")
|
||||
else
|
||||
UpdNetMode("^MODE: 5,7")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:resetcellinfo
|
||||
功能 :重置当前小区和临近小区信息表
|
||||
参数 :无
|
||||
返回值:无
|
||||
]]
|
||||
local function resetCellInfo()
|
||||
local i
|
||||
cellinfo.cnt = 11 --最大个数
|
||||
for i = 1, cellinfo.cnt do
|
||||
cellinfo[i] = {}
|
||||
cellinfo[i].mcc, cellinfo[i].mnc = nil
|
||||
cellinfo[i].lac = 0
|
||||
cellinfo[i].ci = 0
|
||||
cellinfo[i].rssi = 0
|
||||
cellinfo[i].ta = 0
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:eemMgInfoSvc
|
||||
功能 :解析4G网络,当前小区和临近小区信息
|
||||
参数 :
|
||||
data:当前小区和临近小区信息字符串,例如下面中的每一行:
|
||||
+EEMLTESVC:xx,xx,...
|
||||
返回值:无
|
||||
]]
|
||||
local function eemLteSvc(data)
|
||||
local mcc,mnc,lac,ci,rssi,svcData
|
||||
if data:match("%+EEMLTESVC:%s*%d+,%s*%d+,%s*%d+,%s*.+") then
|
||||
svcData = string.match(data, "%+EEMLTESVC:(.+)")
|
||||
--log.info("eemLteSvc",svcData)
|
||||
if svcData then
|
||||
svcDataT = string.split(svcData, ', ')
|
||||
--log.info("eemLteSvc1",svcDataT[1],svcDataT[3],svcDataT[4],svcDataT[10],svcDataT[15])
|
||||
if not(svcDataT[1] and svcDataT[3] and svcDataT[4] and svcDataT[10] and svcDataT[15]) then
|
||||
svcDataT = string.split(svcData, ',')
|
||||
log.info("eemLteSvc2",svcDataT[1],svcDataT[3],svcDataT[4],svcDataT[10],svcDataT[15])
|
||||
end
|
||||
mcc = svcDataT[1]
|
||||
mnc = svcDataT[3]
|
||||
lac = svcDataT[4]
|
||||
ci = svcDataT[10]
|
||||
band = svcDataT[8]
|
||||
rssi = (tonumber(svcDataT[15])-(tonumber(svcDataT[15])%3))/3
|
||||
if rssi>31 then rssi=31 end
|
||||
if rssi<0 then rssi=0 end
|
||||
end
|
||||
log.info("eemLteSvc1",lac,ci,mcc,mnc)
|
||||
if lac and lac~="0" and ci and ci ~= "0" and mcc and mnc then
|
||||
--如果是第一条,清除信息表
|
||||
resetCellInfo()
|
||||
curCellSeted = true
|
||||
--保存mcc、mnc、lac、ci、rssi、ta
|
||||
cellinfo[1].mcc = mcc
|
||||
cellinfo[1].mnc = mnc
|
||||
cellinfo[1].lac = tonumber(lac)
|
||||
cellinfo[1].ci = tonumber(ci)
|
||||
cellinfo[1].rssi = tonumber(rssi)
|
||||
--cellinfo[id + 1].ta = tonumber(ta or "0")
|
||||
--产生一个内部消息CELL_INFO_IND,表示读取到了新的当前小区和临近小区信息
|
||||
if multicellcb then multicellcb(cellinfo) end
|
||||
publish("CELL_INFO_IND", cellinfo)
|
||||
end
|
||||
elseif data:match("%+EEMLTEINTER") or data:match("%+EEMLTEINTRA") or data:match("%+EEMLTEINTERRAT") then
|
||||
--data = "+EEMLTEINTRA: 0, 98, 39148, 51, 21, 1120, 0, 6311, 25418539"
|
||||
--data = "+EEMLTEINTERRAT:0,16,1120,0,6213,26862,627,1,-77"
|
||||
data = data:gsub(" ","")
|
||||
|
||||
if data:match("%+EEMLTEINTERRAT") then
|
||||
mcc,mnc,lac,ci,rssi = data:match("[-]*%d+,[-]*%d+,([-]*%d+),([-]*%d+),([-]*%d+),([-]*%d+),[-]*%d+,[-]*%d+,([-]*%d+)")
|
||||
else
|
||||
rssi,mcc,mnc,lac,ci = data:match("[-]*%d+,[-]*%d+,[-]*%d+,([-]*%d+),[-]*%d+,([-]*%d+),([-]*%d+),([-]*%d+),([-]*%d+)")
|
||||
end
|
||||
|
||||
--print(mcc,mnc,lac,ci,rssi)
|
||||
|
||||
if rssi then
|
||||
rssi = (rssi-(rssi%3))/3
|
||||
if rssi>31 then rssi=31 end
|
||||
if rssi<0 then rssi=0 end
|
||||
end
|
||||
if lac~="0" and lac~="-1" and ci~="0" and ci~="-1" then
|
||||
for i = 1, cellinfo.cnt do
|
||||
--print("cellinfo["..i.."].lac="..cellinfo[i].lac)
|
||||
if cellinfo[i].lac==0 then
|
||||
cellinfo[i] =
|
||||
{
|
||||
mcc = mcc,
|
||||
mnc = mnc,
|
||||
lac = tonumber(lac),
|
||||
ci = tonumber(ci),
|
||||
rssi = tonumber(rssi)
|
||||
}
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
--[[
|
||||
函数名:eemMgInfoSvc
|
||||
功能 :解析2G网络,当前小区
|
||||
参数 :
|
||||
data:当前小区信息字符串,例如下面中的每一行:
|
||||
+EEMGINFOSVC:xx,xx,...
|
||||
返回值:无
|
||||
]]
|
||||
local function eemGsmInfoSvc(data)
|
||||
--只处理有效的CENG信息
|
||||
if string.find(data, "%+EEMGINFOSVC:%s*%d+,%s*%d+,%s*%d+,%s*.+") then
|
||||
local mcc,mnc,lac,ci,ta,rssi
|
||||
local svcData = string.match(data, "%+EEMGINFOSVC:(.+)")
|
||||
if svcData then
|
||||
svcDataT = string.split(svcData, ', ')
|
||||
mcc = svcDataT[1]
|
||||
mnc = svcDataT[2]
|
||||
lac = svcDataT[3]
|
||||
ci = svcDataT[4]
|
||||
ta = svcDataT[10]
|
||||
rssi = svcDataT[12]
|
||||
if tonumber(rssi) >31
|
||||
then rssi = 31
|
||||
end
|
||||
if tonumber(rssi) < 0
|
||||
then rssi = 0
|
||||
end
|
||||
end
|
||||
if lac and lac~="0" and ci and ci ~= "0" and mcc and mnc then
|
||||
--如果是第一条,清除信息表
|
||||
resetCellInfo()
|
||||
curCellSeted = true
|
||||
--保存mcc、mnc、lac、ci、rssi、ta
|
||||
cellinfo[1].mcc = mcc
|
||||
cellinfo[1].mnc = mnc
|
||||
cellinfo[1].lac = tonumber(lac)
|
||||
cellinfo[1].ci = tonumber(ci)
|
||||
cellinfo[1].rssi = (tonumber(rssi) == 99) and 0 or tonumber(rssi)
|
||||
cellinfo[1].ta = tonumber(ta or "0")
|
||||
--产生一个内部消息CELL_INFO_IND,表示读取到了新的当前小区和临近小区信息
|
||||
if multicellcb then multicellcb(cellinfo) end
|
||||
publish("CELL_INFO_IND", cellinfo)
|
||||
end
|
||||
end
|
||||
end
|
||||
--[[
|
||||
函数名:eemMgInfoSvc
|
||||
功能 :解析2G网络,临近小区信息
|
||||
参数 :
|
||||
data:当前小区和临近小区信息字符串,例如下面中的每一行:
|
||||
+EEMGINFOSVC:xx,xx,...
|
||||
返回值:无
|
||||
]]
|
||||
local function eemGsmNCInfoSvc(data)
|
||||
if string.find(data, "%+EEMGINFONC: %d+, %d+, %d+, .+") then
|
||||
local mcc,mnc,lac,ci,ta,rssi,id
|
||||
local svcData = string.match(data, "%+EEMGINFONC:(.+)")
|
||||
if svcData then
|
||||
svcDataT = string.split(svcData, ', ')
|
||||
id = svcDataT[1]
|
||||
mcc = svcDataT[2]
|
||||
mnc = svcDataT[3]
|
||||
lac = svcDataT[4]
|
||||
ci = svcDataT[6]
|
||||
rssi = svcDataT[7]
|
||||
if tonumber(rssi) >31
|
||||
then rssi = 31
|
||||
end
|
||||
if tonumber(rssi) < 0
|
||||
then rssi = 0
|
||||
end
|
||||
end
|
||||
if lac and ci and mcc and mnc then
|
||||
--保存mcc、mnc、lac、ci、rssi、ta
|
||||
cellinfo[id + 2].mcc = mcc
|
||||
cellinfo[id + 2].mnc = mnc
|
||||
cellinfo[id + 2].lac = tonumber(lac)
|
||||
cellinfo[id + 2].ci = tonumber(ci)
|
||||
cellinfo[id + 2].rssi = (tonumber(rssi) == 99) and 0 or tonumber(rssi)
|
||||
--cellinfo[id + 1].ta = tonumber(ta or "0")
|
||||
end
|
||||
end
|
||||
end
|
||||
--[[
|
||||
函数名:eemMgInfoSvc
|
||||
功能 :解析3G网络,当前小区和临近小区信息
|
||||
参数 :
|
||||
data:当前小区和临近小区信息字符串,例如下面中的每一行:
|
||||
+EEMUMTSSVC:xx,xx,...
|
||||
返回值:无
|
||||
]]
|
||||
local function eemUMTSInfoSvc(data)
|
||||
--只处理有效的CENG信息
|
||||
if string.find(data, "%+EEMUMTSSVC: %d+, %d+, %d+, .+") then
|
||||
local mcc,mnc,lac,ci,rssi
|
||||
local svcData = string.match(data, "%+EEMUMTSSVC:(.+)")
|
||||
local cellMeasureFlag, cellParamFlag = string.match(data, "%+EEMUMTSSVC:%d+, (%d+), (%d+), .+")
|
||||
local svcDataT = string.split(svcData, ', ')
|
||||
local offset = 4
|
||||
if svcData and svcDataT then
|
||||
if tonumber(cellMeasureFlag) ~= 0 then
|
||||
offset = offset + 2
|
||||
rssi = svcDataT[offset]
|
||||
offset = offset + 4
|
||||
else
|
||||
offset = offset + 2
|
||||
rssi = svcDataT[offset]
|
||||
offset = offset + 2
|
||||
end
|
||||
|
||||
if tonumber(cellParamFlag) ~= 0 then
|
||||
offset = offset + 3
|
||||
mcc = svcDataT[offset]
|
||||
mnc = svcDataT[offset + 1]
|
||||
lac = svcDataT[offset + 2]
|
||||
ci = svcDataT[offset + 3]
|
||||
offset = offset + 3
|
||||
end
|
||||
end
|
||||
if lac and lac~="0" and ci and ci ~= "0" and mcc and mnc and rssi then
|
||||
--如果是第一条,清除信息表
|
||||
resetCellInfo()
|
||||
curCellSeted = true
|
||||
--保存mcc、mnc、lac、ci、rssi、ta
|
||||
cellinfo[1].mcc = mcc
|
||||
cellinfo[1].mnc = mnc
|
||||
cellinfo[1].lac = tonumber(lac)
|
||||
cellinfo[1].ci = tonumber(ci)
|
||||
cellinfo[1].rssi = tonumber(rssi)
|
||||
--产生一个内部消息CELL_INFO_IND,表示读取到了新的当前小区和临近小区信息
|
||||
if multicellcb then multicellcb(cellinfo) end
|
||||
publish("CELL_INFO_IND", cellinfo)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:UpdNetMode
|
||||
功能 :解析NetMode
|
||||
参数 :data:NetMode信息字符串,例如"^MODE: 17,17"
|
||||
返回值:无
|
||||
]]
|
||||
function UpdNetMode(data)
|
||||
local _, _, SysMainMode,SysMode = string.find(data, "(%d+),(%d+)")
|
||||
local netMode_cur
|
||||
log.info("net.UpdNetMode",netMode_cur,netMode, SysMainMode,SysMode)
|
||||
if SysMainMode and SysMode then
|
||||
if SysMainMode=="3" then
|
||||
netMode_cur = NetMode_GSM
|
||||
elseif SysMainMode=="5" then
|
||||
netMode_cur = NetMode_WCDMA
|
||||
elseif SysMainMode=="15" then
|
||||
netMode_cur = NetMode_TD
|
||||
elseif SysMainMode=="17" then
|
||||
netMode_cur = NetMode_LTE
|
||||
else
|
||||
netMode_cur = NetMode_noNet
|
||||
end
|
||||
|
||||
if SysMode=="3" then
|
||||
netMode_cur = NetMode_EDGE
|
||||
end
|
||||
end
|
||||
|
||||
if netMode ~= netMode_cur then
|
||||
netMode = netMode_cur
|
||||
publish("NET_UPD_NET_MODE",netMode)
|
||||
log.info("net.NET_UPD_NET_MODE",netMode)
|
||||
ril.request("AT+COPS?")
|
||||
if netMode == NetMode_LTE then
|
||||
ril.request("AT+CEREG?")
|
||||
elseif netMode == NetMode_noNet then
|
||||
ril.request("AT+CREG?")
|
||||
else
|
||||
ril.request("AT+CGREG?")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:neturc
|
||||
功能 :本功能模块内“注册的底层core通过虚拟串口主动上报的通知”的处理
|
||||
参数 :
|
||||
data:通知的完整字符串信息
|
||||
prefix:通知的前缀
|
||||
返回值:无
|
||||
]]
|
||||
local function neturc(data, prefix)
|
||||
if prefix=="+COPS" then
|
||||
cops(data)
|
||||
elseif prefix == "+CREG" or prefix == "+CGREG" or prefix == "+CEREG" then
|
||||
--收到网络状态变化时,更新一下信号值
|
||||
csqQueryPoll()
|
||||
--解析creg信息
|
||||
creg(data)
|
||||
elseif prefix == "+EEMLTESVC" or prefix == "+EEMLTEINTRA" or prefix == "+EEMLTEINTER" or prefix=="+EEMLTEINTERRAT" then
|
||||
eemLteSvc(data)
|
||||
elseif prefix == "+EEMUMTSSVC" then
|
||||
eemUMTSInfoSvc(data)
|
||||
elseif prefix == "+EEMGINFOSVC" then
|
||||
eemGsmInfoSvc(data)
|
||||
elseif prefix == "+EEMGINFONC" then
|
||||
eemGsmNCInfoSvc(data)
|
||||
elseif prefix == "^MODE" then
|
||||
UpdNetMode(data)
|
||||
end
|
||||
end
|
||||
|
||||
--- 设置飞行模式
|
||||
-- 注意:如果要测试飞行模式的功耗,开机后不要立即调用此接口进入飞行模式
|
||||
-- 在模块注册上网络之前,调用此接口进入飞行模式不仅无效,还会导致功耗数据异常
|
||||
-- 详情参考:http://doc.openluat.com/article/488/0
|
||||
-- @bool mode true:飞行模式开,false:飞行模式关
|
||||
-- @return nil
|
||||
-- @usage net.switchFly(mode)
|
||||
function switchFly(mode)
|
||||
if flyMode == mode then return end
|
||||
flyMode = mode
|
||||
-- 处理飞行模式
|
||||
if mode then
|
||||
ril.request("AT+CFUN=0")
|
||||
-- 处理退出飞行模式
|
||||
else
|
||||
ril.request("AT+CFUN=1")
|
||||
--处理查询定时器
|
||||
csqQueryPoll()
|
||||
cengQueryPoll()
|
||||
--复位GSM网络状态
|
||||
neturc("2", "+CREG")
|
||||
end
|
||||
end
|
||||
|
||||
--- 获取netmode
|
||||
-- @return number netMode,注册的网络类型
|
||||
-- 0:未注册
|
||||
-- 1:2G GSM网络
|
||||
-- 2:2.5G EDGE数据网络
|
||||
-- 3:3G TD网络
|
||||
-- 4:4G LTE网络
|
||||
-- 5:3G WCDMA网络
|
||||
-- @usage net.getNetMode()
|
||||
function getNetMode()
|
||||
return netMode
|
||||
end
|
||||
|
||||
--- 获取网络注册状态
|
||||
-- @return string state,GSM网络注册状态,
|
||||
-- "INIT"表示正在初始化
|
||||
-- "REGISTERED"表示已注册
|
||||
-- "UNREGISTER"表示未注册
|
||||
-- @usage net.getState()
|
||||
function getState()
|
||||
return state
|
||||
end
|
||||
|
||||
--- 获取当前小区的mcc
|
||||
-- @return string mcc,当前小区的mcc,如果还没有注册GSM网络,则返回sim卡的mcc
|
||||
-- @usage net.getMcc()
|
||||
function getMcc()
|
||||
return cellinfo[1].mcc and string.format("%x",cellinfo[1].mcc) or sim.getMcc()
|
||||
end
|
||||
|
||||
--- 获取当前小区的mnc
|
||||
-- @return string mcn,当前小区的mnc,如果还没有注册GSM网络,则返回sim卡的mnc
|
||||
-- @usage net.getMnc()
|
||||
function getMnc()
|
||||
return cellinfo[1].mnc and string.format("%x",cellinfo[1].mnc) or sim.getMnc()
|
||||
end
|
||||
|
||||
--- 获取当前位置区ID
|
||||
-- @return string lac,当前位置区ID(16进制字符串,例如"18be"),如果还没有注册GSM网络,则返回""
|
||||
-- @usage net.getLac()
|
||||
function getLac()
|
||||
return lac
|
||||
end
|
||||
|
||||
--- 获取当前注册的网络频段
|
||||
-- @return string band,当前注册的网络频段,如果还没有注册网络,则返回""
|
||||
-- @usage net.getBand()
|
||||
function getBand()
|
||||
return band
|
||||
end
|
||||
|
||||
--- 获取当前小区ID
|
||||
-- @return string ci,当前小区ID(16进制字符串,例如"93e1"),如果还没有注册GSM网络,则返回""
|
||||
-- @usage net.getCi()
|
||||
function getCi()
|
||||
return ci
|
||||
end
|
||||
|
||||
--- 获取信号强度
|
||||
-- 当前注册的是2G网络,就是2G网络的信号强度
|
||||
-- 当前注册的是4G网络,就是4G网络的信号强度
|
||||
-- @return number rssi,当前信号强度(取值范围0-31)
|
||||
-- @usage net.getRssi()
|
||||
function getRssi()
|
||||
return rssi
|
||||
end
|
||||
|
||||
--- 4G网络信号接收功率
|
||||
-- @return number rsrp,当前信号接收功率(取值范围-140 - -40)
|
||||
-- @usage net.getRsrp()
|
||||
function getRsrp()
|
||||
return rsrp
|
||||
end
|
||||
|
||||
function getCell()
|
||||
local i,ret = 1,""
|
||||
for i=1,cellinfo.cnt do
|
||||
if cellinfo[i] and cellinfo[i].lac and cellinfo[i].lac ~= 0 and cellinfo[i].ci and cellinfo[i].ci ~= 0 then
|
||||
ret = ret..cellinfo[i].ci.."."..cellinfo[i].rssi.."."
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
--- 获取当前和临近位置区、小区以及信号强度的拼接字符串
|
||||
-- @return string cellInfo,当前和临近位置区、小区以及信号强度的拼接字符串,例如:"6311.49234.30;6311.49233.23;6322.49232.18;"
|
||||
-- @usage net.getCellInfo()
|
||||
function getCellInfo()
|
||||
local i, ret = 1, ""
|
||||
for i = 1, cellinfo.cnt do
|
||||
if cellinfo[i] and cellinfo[i].lac and cellinfo[i].lac ~= 0 and cellinfo[i].ci and cellinfo[i].ci ~= 0 then
|
||||
ret = ret .. cellinfo[i].lac .. "." .. cellinfo[i].ci .. "." .. cellinfo[i].rssi .. ";"
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
--- 获取当前和临近位置区、小区、mcc、mnc、以及信号的拼接字符串
|
||||
-- @bool[opt=nil] rssi 信号是否拼接功率,true表示功率,false或者nil表示强度
|
||||
-- 表示强度时,信号的取值范围是0到31
|
||||
-- 表示功率时,信号的计算公式为 强度*2-113,取值范围为-113dB到-51dB
|
||||
-- @return string cellInfo,当前和临近位置区、小区、mcc、mnc、以及信号的拼接字符串,例如:
|
||||
-- 当rssi参数为true时,"460.01.6311.49234.-73;460.01.6311.49233.-67;460.02.6322.49232.-77;"
|
||||
-- 当rssi参数为false或者nil时,"460.01.6311.49234.30;460.01.6311.49233.23;460.02.6322.49232.18;"
|
||||
-- @usage net.getCellInfoExt()
|
||||
function getCellInfoExt(rssi)
|
||||
local i, ret = 1, ""
|
||||
for i = 1, cellinfo.cnt do
|
||||
if cellinfo[i] and cellinfo[i].mcc and cellinfo[i].mnc and cellinfo[i].lac and cellinfo[i].lac ~= 0 and cellinfo[i].ci and cellinfo[i].ci ~= 0 then
|
||||
ret = ret .. string.format("%x",cellinfo[i].mcc) .. "." .. string.format("%x",cellinfo[i].mnc) .. "." .. cellinfo[i].lac .. "." .. cellinfo[i].ci .. "." .. (rssi and (cellinfo[i].rssi*2-113) or cellinfo[i].rssi) .. ";"
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
--- 获取TA值
|
||||
-- @return number ta,TA值
|
||||
-- @usage net.getTa()
|
||||
function getTa()
|
||||
return cellinfo[1].ta
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:rsp
|
||||
功能 :本功能模块内“通过虚拟串口发送到底层core软件的AT命令”的应答处理
|
||||
参数 :
|
||||
cmd:此应答对应的AT命令
|
||||
success:AT命令执行结果,true或者false
|
||||
response:AT命令的应答中的执行结果字符串
|
||||
intermediate:AT命令的应答中的中间信息
|
||||
返回值:无
|
||||
]]
|
||||
local function rsp(cmd, success, response, intermediate)
|
||||
local prefix = string.match(cmd, "AT(%+%u+)")
|
||||
|
||||
if intermediate ~= nil then
|
||||
if prefix == "+CSQ" then
|
||||
local s = string.match(intermediate, "+CSQ:%s*(%d+)")
|
||||
if s ~= nil then
|
||||
rssi = tonumber(s)
|
||||
rssi = rssi == 99 and 0 or rssi
|
||||
--产生一个内部消息GSM_SIGNAL_REPORT_IND,表示读取到了信号强度
|
||||
publish("GSM_SIGNAL_REPORT_IND", success, rssi)
|
||||
end
|
||||
elseif prefix == "+CESQ" then
|
||||
local s = string.match(intermediate, "+CESQ: %d+,%d+,%d+,%d+,%d+,(%d+)")
|
||||
if s ~= nil then
|
||||
rsrp = tonumber(s)
|
||||
end
|
||||
elseif prefix == "+CENG" then end
|
||||
end
|
||||
if prefix == "+CFUN" then
|
||||
if success then publish("FLYMODE", flyMode) end
|
||||
end
|
||||
end
|
||||
|
||||
--- 实时读取“当前和临近小区信息”
|
||||
-- @function cbFnc 回调函数,当读取到小区信息后,会调用此回调函数,回调函数的调用形式为:
|
||||
-- cbFnc(cells),其中cells为string类型,格式为:当前和临近位置区、小区、mcc、mnc、以及信号强度的拼接字符串,例如:"460.01.6311.49234.30;460.01.6311.49233.23;460.02.6322.49232.18;"
|
||||
-- @return nil
|
||||
function getMultiCell(cbFnc)
|
||||
multicellcb = cbFnc
|
||||
--发送AT+CENG?查询
|
||||
ril.request("AT+EEMGINFO?")
|
||||
end
|
||||
|
||||
--- 发起查询基站信息(当前和临近小区信息)的请求
|
||||
-- @number period 查询间隔,单位毫秒
|
||||
-- @return bool result, true:查询成功,false:查询失败
|
||||
-- @usage net.cengQueryPoll() --查询1次
|
||||
-- @usage net.cengQueryPoll(60000) --每分钟查询1次
|
||||
function cengQueryPoll(period)
|
||||
-- 不是飞行模式 并且 工作模式为完整模式
|
||||
if not flyMode then
|
||||
--发送AT+CENG?查询
|
||||
ril.request("AT+EEMGINFO?")
|
||||
else
|
||||
log.warn("net.cengQueryPoll", "flymode:", flyMode)
|
||||
end
|
||||
if nil ~= period then
|
||||
--启动定时器
|
||||
sys.timerStopAll(cengQueryPoll)
|
||||
sys.timerStart(cengQueryPoll, period, period)
|
||||
end
|
||||
return not flyMode
|
||||
end
|
||||
|
||||
--- 发起查询信号强度的请求
|
||||
-- @number period 查询间隔,单位毫秒
|
||||
-- @return bool , true:查询成功,false:查询停止
|
||||
-- @usage net.csqQueryPoll() --查询1次
|
||||
-- @usage net.csqQueryPoll(60000) --每分钟查询1次
|
||||
function csqQueryPoll(period)
|
||||
--不是飞行模式 并且 工作模式为完整模式
|
||||
if not flyMode then
|
||||
--发送AT+CSQ查询
|
||||
ril.request("AT+CSQ")
|
||||
ril.request("AT+CESQ")
|
||||
else
|
||||
log.warn("net.csqQueryPoll", "flymode:", flyMode)
|
||||
end
|
||||
if nil ~= period then
|
||||
--启动定时器
|
||||
sys.timerStopAll(csqQueryPoll)
|
||||
sys.timerStart(csqQueryPoll, period, period)
|
||||
end
|
||||
return not flyMode
|
||||
end
|
||||
|
||||
|
||||
--- 设置查询信号强度和基站信息的间隔
|
||||
-- @number ... 查询周期,参数可变,参数为nil只查询1次,参数1是信号强度查询周期,参数2是基站查询周期
|
||||
-- @return bool ,true:设置成功,false:设置失败
|
||||
-- @usage net.startQueryAll()
|
||||
-- @usage net.startQueryAll(60000) -- 1分钟查询1次信号强度,只立即查询1次基站信息
|
||||
-- @usage net.startQueryAll(60000,600000) -- 1分钟查询1次信号强度,10分钟查询1次基站信息
|
||||
function startQueryAll(...)
|
||||
local arg = { ... }
|
||||
csqQueryPoll(arg[1])
|
||||
cengQueryPoll(arg[2])
|
||||
if flyMode then
|
||||
log.info("sim.startQuerAll", "flyMode:", flyMode)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- 停止查询信号强度和基站信息
|
||||
-- @return 无
|
||||
-- @usage net.stopQueryAll()
|
||||
function stopQueryAll()
|
||||
sys.timerStopAll(csqQueryPoll)
|
||||
sys.timerStopAll(cengQueryPoll)
|
||||
end
|
||||
|
||||
local sEngMode
|
||||
--- 设置工程模式
|
||||
-- @number[opt=1] mode 工程模式,目前仅支持0和1
|
||||
-- mode为0时,不支持临近小区查询,休眠时功耗较低
|
||||
-- mode为1时,支持临近小区查询,但是休眠时功耗较高
|
||||
-- @return nil
|
||||
-- @usage
|
||||
-- net.setEngMode(0)
|
||||
function setEngMode(mode)
|
||||
sEngMode = mode or 1
|
||||
ril.request("AT+EEMOPT="..sEngMode,nil,function(cmd,success)
|
||||
function retrySetEngMode()
|
||||
setEngMode(sEngMode)
|
||||
end
|
||||
if success then
|
||||
sys.timerStop(retrySetEngMode)
|
||||
else
|
||||
sys.timerStart(retrySetEngMode,3000)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- 处理SIM卡状态消息,SIM卡工作不正常时更新网络状态为未注册
|
||||
sys.subscribe("SIM_IND", function(para)
|
||||
log.info("SIM.subscribe", simerrsta, para)
|
||||
if simerrsta ~= (para ~= "RDY") then
|
||||
simerrsta = (para ~= "RDY")
|
||||
end
|
||||
--sim卡工作不正常
|
||||
if para ~= "RDY" then
|
||||
--更新GSM网络状态
|
||||
state = "UNREGISTER"
|
||||
--产生内部消息NET_STATE_CHANGED,表示网络状态发生变化
|
||||
publish("NET_STATE_UNREGISTER")
|
||||
else
|
||||
--state = "INIT"
|
||||
end
|
||||
end)
|
||||
|
||||
--注册+CREG和+CENG通知的处理函数
|
||||
ril.regUrc("+COPS", neturc)
|
||||
ril.regUrc("+CREG", neturc)
|
||||
ril.regUrc("+CGREG", neturc)
|
||||
ril.regUrc("+CEREG", neturc)
|
||||
--ril.regUrc("+CENG", neturc)
|
||||
ril.regUrc("+EEMLTESVC", neturc)
|
||||
ril.regUrc("+EEMLTEINTER", neturc)
|
||||
ril.regUrc("+EEMLTEINTRA", neturc)
|
||||
ril.regUrc("+EEMLTEINTERRAT", neturc)
|
||||
ril.regUrc("+EEMGINFOSVC", neturc)
|
||||
ril.regUrc("+EEMGINFONC", neturc)
|
||||
ril.regUrc("+EEMUMTSSVC", neturc)
|
||||
ril.regUrc("^MODE", neturc)
|
||||
--ril.regUrc("+CRSM", neturc)
|
||||
--注册AT+CCSQ和AT+CENG?命令的应答处理函数
|
||||
ril.regRsp("+CSQ", rsp)
|
||||
ril.regRsp("+CESQ",rsp)
|
||||
--ril.regRsp("+CENG", rsp)
|
||||
ril.regRsp("+CFUN", rsp)-- 飞行模式
|
||||
--发送AT命令
|
||||
ril.request("AT+COPS?")
|
||||
ril.request("AT+CREG=2")
|
||||
ril.request("AT+CGREG=2")
|
||||
ril.request("AT+CEREG=2")
|
||||
ril.request("AT+CREG?")
|
||||
ril.request("AT+CGREG?")
|
||||
ril.request("AT+CEREG?")
|
||||
ril.request("AT+CALIBINFO?")
|
||||
ril.request("AT*BAND?")
|
||||
setEngMode(1)
|
||||
--重置当前小区和临近小区信息表
|
||||
resetCellInfo()
|
||||
183
4G/源代码/lib/netLed.lua
Normal file
183
4G/源代码/lib/netLed.lua
Normal file
@@ -0,0 +1,183 @@
|
||||
--- 模块功能:网络指示灯模块
|
||||
-- @module netLed
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2018.03.14
|
||||
module(..., package.seeall)
|
||||
|
||||
require "pins"
|
||||
require "sim"
|
||||
|
||||
--SIM卡状态:true为异常,false或者nil为正常
|
||||
local simError
|
||||
--是否处于飞行模式:true为是,false或者nil为否
|
||||
local flyMode
|
||||
--是否注册上GSM网络,true为是,false或者nil为否
|
||||
local gsmRegistered
|
||||
--是否附着上GPRS网络,true为是,false或者nil为否
|
||||
local gprsAttached
|
||||
--是否有socket连接上后台,true为是,false或者nil为否
|
||||
local socketConnected
|
||||
|
||||
--网络指示灯表示的工作状态
|
||||
--NULL:功能关闭状态
|
||||
--FLYMODE:飞行模式
|
||||
--SIMERR:未检测到SIM卡或者SIM卡锁pin码等SIM卡异常
|
||||
--IDLE:未注册GSM网络
|
||||
--GSM:已注册GSM网络
|
||||
--GPRS:已附着GPRS数据网络
|
||||
--SCK:socket已连接上后台
|
||||
local ledState = "NULL"
|
||||
local ON,OFF = 1,2
|
||||
--各种工作状态下配置的点亮、熄灭时长(单位毫秒)
|
||||
local ledBlinkTime =
|
||||
{
|
||||
NULL = {0,0xFFFF}, --常灭
|
||||
FLYMODE = {0,0xFFFF}, --常灭
|
||||
SIMERR = {300,5700}, --亮300毫秒,灭5700毫秒
|
||||
IDLE = {300,3700}, --亮300毫秒,灭3700毫秒
|
||||
GSM = {300,1700}, --亮300毫秒,灭1700毫秒
|
||||
GPRS = {300,700}, --亮300毫秒,灭700毫秒
|
||||
SCK = {100,100}, --亮100毫秒,灭100毫秒
|
||||
}
|
||||
|
||||
--网络指示灯开关,true为打开,false或者nil为关闭
|
||||
local ledSwitch = false
|
||||
--网络指示灯默认PIN脚(GPIO64)
|
||||
local LEDPIN = pio.P2_0
|
||||
--LTE指示灯开关,true为打开,false或者nil为关闭
|
||||
local lteSwitch = false
|
||||
--LTE指示灯默认PIN脚(GPIO65)
|
||||
local LTEPIN = pio.P2_1
|
||||
|
||||
|
||||
--[[
|
||||
-- 模块功能:更新网络指示灯表示的工作状态
|
||||
-- 参数:无
|
||||
-- 返回值:无
|
||||
--]]
|
||||
local function updateState()
|
||||
--log.info("netLed.updateState",ledSwitch,ledState,flyMode,simError,gsmRegistered,gprsAttached,socketConnected)
|
||||
if ledSwitch then
|
||||
local newState = "IDLE"
|
||||
if flyMode then
|
||||
newState = "FLYMODE"
|
||||
elseif simError then
|
||||
newState = "SIMERR"
|
||||
elseif socketConnected then
|
||||
newState = "SCK"
|
||||
elseif gprsAttached then
|
||||
newState = "GPRS"
|
||||
elseif gsmRegistered then
|
||||
newState = "GSM"
|
||||
end
|
||||
--指示灯状态发生变化
|
||||
if newState~=ledState then
|
||||
ledState = newState
|
||||
sys.publish("NET_LED_UPDATE")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
-- 模块功能:网络指示灯模块的运行任务
|
||||
-- 参数:
|
||||
ledPinSetFunc:指示灯GPIO的设置函数
|
||||
-- 返回值:无
|
||||
--]]
|
||||
local function taskLed(ledPinSetFunc)
|
||||
while true do
|
||||
--log.info("netLed.taskLed",ledPinSetFunc,ledSwitch,ledState)
|
||||
if ledSwitch then
|
||||
local onTime,offTime = ledBlinkTime[ledState][ON],ledBlinkTime[ledState][OFF]
|
||||
if onTime>0 then
|
||||
ledPinSetFunc(1)
|
||||
if not sys.waitUntil("NET_LED_UPDATE", onTime) then
|
||||
if offTime>0 then
|
||||
ledPinSetFunc(0)
|
||||
sys.waitUntil("NET_LED_UPDATE", offTime)
|
||||
end
|
||||
end
|
||||
else if offTime>0 then
|
||||
ledPinSetFunc(0)
|
||||
sys.waitUntil("NET_LED_UPDATE", offTime)
|
||||
end
|
||||
end
|
||||
else
|
||||
ledPinSetFunc(0)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
-- 模块功能:LTE指示灯模块的运行任务
|
||||
-- 参数:
|
||||
ledPinSetFunc:指示灯GPIO的设置函数
|
||||
-- 返回值:无
|
||||
--]]
|
||||
local function taskLte(ledPinSetFunc)
|
||||
while true do
|
||||
local _,arg = sys.waitUntil("LTE_LED_UPDATE")
|
||||
if lteSwitch then
|
||||
ledPinSetFunc(arg and 1 or 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- 配置网络指示灯和LTE指示灯并且立即执行配置后的动作
|
||||
-- @bool flag 是否打开网络指示灯和LTE指示灯功能,true为打开,false为关闭
|
||||
-- @number ledPin 控制网络指示灯闪烁的GPIO引脚,例如pio.P0_1表示GPIO1
|
||||
-- @number ltePin 控制LTE指示灯闪烁的GPIO引脚,例如pio.P0_4表示GPIO4
|
||||
-- @return nil
|
||||
-- @usage setup(true,pio.P0_1,pio.P0_4)表示打开网络指示灯和LTE指示灯功能,GPIO1控制网络指示灯,GPIO4控制LTE指示灯
|
||||
-- @usage setup(false)表示关闭网络指示灯和LTE指示灯功能
|
||||
function setup(flag,ledPin,ltePin)
|
||||
--log.info("netLed.setup",flag,pin,ledSwitch)
|
||||
local oldSwitch = ledSwitch
|
||||
if flag~=ledSwitch then
|
||||
ledSwitch = flag
|
||||
sys.publish("NET_LED_UPDATE")
|
||||
end
|
||||
if flag and not oldSwitch then
|
||||
sys.taskInit(taskLed, pins.setup(ledPin or LEDPIN, 0))
|
||||
end
|
||||
if flag~=lteSwitch then
|
||||
lteSwitch = flag
|
||||
end
|
||||
if flag and ltePin and not oldSwitch then
|
||||
sys.taskInit(taskLte, pins.setup(ltePin, 0))
|
||||
end
|
||||
end
|
||||
|
||||
--- 配置某种工作状态下指示灯点亮和熄灭的时长(如果用户不配置,使用netLed.lua中ledBlinkTime配置的默认值)
|
||||
-- @string state 某种工作状态,仅支持"FLYMODE"、"SIMERR"、"IDLE"、"GSM"、"GPRS"、"SCK"
|
||||
-- @number on 指示灯点亮时长,单位毫秒,0xFFFF表示常亮,0表示常灭
|
||||
-- @number off 指示灯熄灭时长,单位毫秒,0xFFFF表示常灭,0表示常亮
|
||||
-- @return nil
|
||||
-- @usage updateBlinkTime("FLYMODE",1000,500)表示飞行模式工作状态下,指示灯闪烁规律为:亮1秒,灭0.5秒
|
||||
-- @usage updateBlinkTime("SCK",0xFFFF,0)表示有socket连接上后台的工作状态下,指示灯闪烁规律为:常亮
|
||||
-- @usage updateBlinkTime("SIMERR",0,0xFFFF)表示SIM卡异常状态下,指示灯闪烁规律为:常灭
|
||||
function updateBlinkTime(state,on,off)
|
||||
if not ledBlinkTime[state] then log.error("netLed.updateBlinkTime") return end
|
||||
local updated
|
||||
if on and ledBlinkTime[state][ON]~=on then
|
||||
ledBlinkTime[state][ON] = on
|
||||
updated = true
|
||||
end
|
||||
if off and ledBlinkTime[state][OFF]~=off then
|
||||
ledBlinkTime[state][OFF] = off
|
||||
updated = true
|
||||
end
|
||||
--log.info("netLed.updateBlinkTime",state,on,off,updated)
|
||||
if updated then sys.publish("NET_LED_UPDATE") end
|
||||
end
|
||||
|
||||
sys.subscribe("FLYMODE", function(mode) if flyMode~=mode then flyMode=mode updateState() end end)
|
||||
sys.subscribe("SIM_IND", function(para) if simError~=(para~="RDY") then simError=(para~="RDY") updateState() end end)
|
||||
sys.subscribe("NET_STATE_UNREGISTER", function() if gsmRegistered then gsmRegistered=false updateState() end end)
|
||||
sys.subscribe("NET_STATE_REGISTERED", function() if not gsmRegistered then gsmRegistered=true updateState() end end)
|
||||
sys.subscribe("GPRS_ATTACH", function(attach) if gprsAttached~=attach then gprsAttached=attach updateState() end end)
|
||||
sys.subscribe("SOCKET_ACTIVE", function(active) if socketConnected~=active then socketConnected=active updateState() end end)
|
||||
sys.subscribe("NET_UPD_NET_MODE", function() if lteSwitch then sys.publish("LTE_LED_UPDATE",net.getNetMode()==net.NetMode_LTE) end end)
|
||||
150
4G/源代码/lib/ntp.lua
Normal file
150
4G/源代码/lib/ntp.lua
Normal file
@@ -0,0 +1,150 @@
|
||||
--- 模块功能:网络授时.
|
||||
-- 重要提醒!!!!!!
|
||||
-- 本功能模块采用多个免费公共的NTP服务器来同步时间
|
||||
-- 并不能保证任何时间任何地点都能百分百同步到正确的时间
|
||||
-- 所以,如果用户项目中的业务逻辑严格依赖于时间同步功能
|
||||
-- 则不要使用使用本功能模块,建议使用自己的应用服务器来同步时间
|
||||
-- 参考 http://ask.openluat.com/article/912 加深对授时功能的理解
|
||||
-- @module ntp
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2017.10.21
|
||||
require "misc"
|
||||
require "socket"
|
||||
require "utils"
|
||||
require "log"
|
||||
local sbyte, ssub = string.byte, string.sub
|
||||
module(..., package.seeall)
|
||||
-- NTP服务器域名集合
|
||||
local timeServer = {
|
||||
"cn.pool.ntp.org",
|
||||
"edu.ntp.org.cn",
|
||||
"cn.ntp.org.cn",
|
||||
"s2c.time.edu.cn",
|
||||
"time1.aliyun.com",
|
||||
"tw.pool.ntp.org",
|
||||
"0.cn.pool.ntp.org",
|
||||
"0.tw.pool.ntp.org",
|
||||
"1.cn.pool.ntp.org",
|
||||
"1.tw.pool.ntp.org",
|
||||
"3.cn.pool.ntp.org",
|
||||
"3.tw.pool.ntp.org",
|
||||
}
|
||||
-- 同步超时等待时间
|
||||
local NTP_TIMEOUT = 8000
|
||||
-- 同步是否完成标记
|
||||
local ntpEnd = false
|
||||
|
||||
-- 获取NTP服务器地址列表
|
||||
-- @return table,服务器地址列表
|
||||
-- @usage local addtable = ntp.getServers()
|
||||
function getServers()
|
||||
return timeServer
|
||||
end
|
||||
|
||||
-- 设置NTP服务器地址列表
|
||||
-- @table st,tab类型,服务器地址列表
|
||||
-- @return 无
|
||||
-- @usage ntp.getServers({"1edu.ntp.org.cn","cn.ntp.org.cn"})
|
||||
function setServers(st)
|
||||
timeServer = st
|
||||
end
|
||||
|
||||
-- NTP同步标志
|
||||
-- @return bool,NTP的同步状态true为成功,fasle为失败
|
||||
-- @usage local sta = ntp.isEnd()
|
||||
function isEnd()
|
||||
return ntpEnd
|
||||
end
|
||||
|
||||
local sTs,sFnc,sFun
|
||||
|
||||
-- 同步时间,随机每个NTP服务器尝试1次,超时8秒,适用于被任务函数调用
|
||||
-- @number ts,每隔ts小时同步1次
|
||||
-- @function fnc,同步成功后回调函数
|
||||
-- @function fun,同步成功前回调函数
|
||||
-- @return nil
|
||||
-- @usage ntp.ntpTime() -- 只同步1次
|
||||
-- @usage ntp.ntpTime(1) -- 1小时同步1次
|
||||
-- @usage ntp.ntpTime(nil,fnc) -- 只同步1次,同步成功后执行fnc()
|
||||
-- @usage ntp.ntpTime(24,fnc) -- 24小时同步1次,同步成功后执行fnc()
|
||||
function ntpTime(ts, fnc, fun)
|
||||
local rc, data, ntim
|
||||
local sTs,sFnc,sFun = ts or sTs, fnc or sFnc, fun or sFun
|
||||
ntpEnd = false
|
||||
while true do
|
||||
local tUnusedSvr = {}
|
||||
for i = 1, #timeServer do
|
||||
tUnusedSvr[i] = timeServer[i]
|
||||
end
|
||||
for i = 1, #timeServer do
|
||||
while not socket.isReady() do sys.waitUntil('IP_READY_IND') end
|
||||
local c = socket.udp()
|
||||
local idx = rtos.tick() % #tUnusedSvr + 1
|
||||
if c:connect(tUnusedSvr[idx], "123") then
|
||||
if c:send(string.fromHex("E30006EC0000000000000000314E31340000000000000000000000000000000000000000000000000000000000000000")) then
|
||||
rc, data = c:recv(NTP_TIMEOUT)
|
||||
if rc and #data == 48 then
|
||||
ntim = os.date("*t", (sbyte(ssub(data, 41, 41)) - 0x83) * 2 ^ 24 + (sbyte(ssub(data, 42, 42)) - 0xAA) * 2 ^ 16 + (sbyte(ssub(data, 43, 43)) - 0x7E) * 2 ^ 8 + (sbyte(ssub(data, 44, 44)) - 0x80) + 1)
|
||||
if type(sFun) == "function" then sFun() end
|
||||
misc.setClock(ntim, sFnc)
|
||||
ntpEnd = true
|
||||
c:close()
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local cnt, n, m = #tUnusedSvr, 1
|
||||
for m = 1, cnt do
|
||||
if m ~= idx then
|
||||
tUnusedSvr[n] = tUnusedSvr[m]
|
||||
n = n + 1
|
||||
end
|
||||
end
|
||||
tUnusedSvr[cnt] = nil
|
||||
|
||||
c:close()
|
||||
sys.wait(1000)
|
||||
end
|
||||
if ntpEnd then
|
||||
sys.publish("NTP_SUCCEED")
|
||||
log.info("ntp.timeSync is date:", ntim.year .. "/" .. ntim.month .. "/" .. ntim.day .. "," .. ntim.hour .. ":" .. ntim.min .. ":" .. ntim.sec)
|
||||
else
|
||||
log.warn("ntp.timeSync is error!")
|
||||
end
|
||||
if sTs == nil or type(sTs) ~= "number" then break end
|
||||
sys.wait(sTs * 3600 * 1000)
|
||||
end
|
||||
end
|
||||
--- ntp同步时间任务.
|
||||
-- 重要提醒!!!!!!
|
||||
-- 本功能模块采用多个免费公共的NTP服务器来同步时间
|
||||
-- 并不能保证任何时间任何地点都能百分百同步到正确的时间
|
||||
-- 所以,如果用户项目中的业务逻辑严格依赖于时间同步功能
|
||||
-- 则不要使用使用本功能模块,建议使用自己的应用服务器来同步时间
|
||||
-- @number[opt=nil] period 调用本接口会立即同步一次;每隔period小时再自动同步1次,nil表示仅同步一次
|
||||
-- @function[opt=nil] fnc 同步结束,设置系统时间后的回调函数,回调函数的调用形式为:
|
||||
-- fnc(time,result)
|
||||
-- time表示设置之后的系统时间,table类型,例如{year=2017,month=2,day=14,hour=14,min=19,sec=23}
|
||||
-- result为true表示成功,false或者nil为失败
|
||||
-- @function[opt=nil] fun 同步结束,设置系统时间前的回调函数,回调函数的调用形式为:fun()
|
||||
-- @return nil
|
||||
--
|
||||
-- @usage
|
||||
-- 立即同步一次(仅同步这一次):
|
||||
-- ntp.timeSync()
|
||||
--
|
||||
-- 立即同步一次,之后每隔1小时自动同步一次:
|
||||
-- ntp.timeSync(1)
|
||||
--
|
||||
-- 立即同步一次(仅同步这一次),同步结束后执行fnc(time,result):
|
||||
-- ntp.timeSync(nil,fnc)
|
||||
--
|
||||
-- 立即同步一次,之后每隔24小时自动同步一次,每次同步结束后执行fnc(time,result):
|
||||
-- ntp.timeSync(24,fnc)
|
||||
function timeSync(period, fnc, fun)
|
||||
sTs,sFnc,sFun = period, fnc, fun
|
||||
sys.taskInit(ntpTime)
|
||||
end
|
||||
345
4G/源代码/lib/nvm.lua
Normal file
345
4G/源代码/lib/nvm.lua
Normal file
@@ -0,0 +1,345 @@
|
||||
--- 模块功能:参数管理
|
||||
-- @module nvm
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2017.11.9
|
||||
require "log"
|
||||
|
||||
module(..., package.seeall)
|
||||
|
||||
--实时参数配置存储在paraname文件中
|
||||
--默认参数配置存储在configname文件中
|
||||
--para:实时参数表
|
||||
--config:默认参数表
|
||||
paraname, paranamebak = "/nvm_para.lua", "/nvm_para_bak.lua"
|
||||
para = {}
|
||||
local libdftconfig, configname, cconfigname, econfigname
|
||||
local sBurnSave
|
||||
|
||||
--[[
|
||||
函数名:serialize
|
||||
功能 :根据不同的数据类型,按照不同的格式,写格式化后的数据到文件中
|
||||
参数 :
|
||||
pout:文件句柄
|
||||
o:数据
|
||||
返回值:无
|
||||
]]
|
||||
local function serialize(pout, o)
|
||||
if type(o) == "number" then
|
||||
--number类型,直接写原始数据
|
||||
pout:write(o)
|
||||
elseif type(o) == "string" then
|
||||
--string类型,原始数据左右各加上双引号写入
|
||||
pout:write(string.format("%q", o))
|
||||
elseif type(o) == "boolean" then
|
||||
--boolean类型,转化为string写入
|
||||
pout:write(tostring(o))
|
||||
elseif type(o) == "table" then
|
||||
--table类型,加换行,大括号,中括号,双引号写入
|
||||
pout:write("{\n")
|
||||
for k, v in pairs(o) do
|
||||
-- if type(k) == "number" then
|
||||
-- pout:write(" [" .. k .. "] = ")
|
||||
-- elseif type(k) == "string" then
|
||||
-- pout:write(" [\"" .. k .."\"] = ")
|
||||
-- else
|
||||
-- error("cannot serialize table key " .. type(o))
|
||||
-- end
|
||||
pout:write(" [")
|
||||
serialize(pout, k)
|
||||
pout:write("] = ")
|
||||
serialize(pout, v)
|
||||
pout:write(",\n")
|
||||
end
|
||||
pout:write("}\n")
|
||||
else
|
||||
error("cannot serialize a " .. type(o))
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:upd
|
||||
功能 :更新实时参数表
|
||||
参数 :
|
||||
overide:是否用默认参数强制更新实时参数
|
||||
返回值:无
|
||||
]]
|
||||
function upd(overide)
|
||||
for k, v in pairs(libdftconfig) do
|
||||
if k ~= "_M" and k ~= "_NAME" and k ~= "_PACKAGE" then
|
||||
if overide or para[k] == nil then
|
||||
para[k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function safePcall(file)
|
||||
local oldPath = package.path
|
||||
package.path = "/?.lua;".."/?.luae;".."/#lua_user#/?.lua;".."/#lua_user#/?.luae;"..package.path
|
||||
local result, para = pcall(require, file)
|
||||
package.path = oldPath
|
||||
return result, para
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:load
|
||||
功能 :初始化参数
|
||||
参数 :无
|
||||
返回值:如果参数文件异常,会恢复出厂设置,此时返回false;其余情况返回nil
|
||||
]]
|
||||
local function load()
|
||||
local f, fBak, fExist, fBakExist
|
||||
f = io.open(paraname, "rb")
|
||||
fBak = io.open(paranamebak, "rb")
|
||||
|
||||
if f then
|
||||
fExist = f:read("*a") ~= ""
|
||||
f:close()
|
||||
end
|
||||
if fBak then
|
||||
fBakExist = fBak:read("*a") ~= ""
|
||||
fBak:close()
|
||||
end
|
||||
|
||||
print("load fExist fBakExist", fExist, fBakExist)
|
||||
|
||||
local fResult, fBakResult
|
||||
if fExist then
|
||||
if sBurnSave then
|
||||
fResult, para = safePcall(paraname:match("/#lua_user#/(.+)%.lua"))
|
||||
else
|
||||
fResult, para = safePcall(paraname:match("/(.+)%.lua"))
|
||||
end
|
||||
end
|
||||
|
||||
print("load fResult,type(para)", fResult,type(para))
|
||||
|
||||
local fError,fBakError
|
||||
|
||||
if fResult then
|
||||
if type(para)=="table" then
|
||||
os.remove(paranamebak)
|
||||
upd()
|
||||
return
|
||||
else
|
||||
fError = true
|
||||
end
|
||||
end
|
||||
|
||||
if fBakExist then
|
||||
if sBurnSave then
|
||||
fBakResult, para = safePcall(paranamebak:match("/#lua_user#/(.+)%.lua"))
|
||||
else
|
||||
fBakResult, para = safePcall(paranamebak:match("/(.+)%.lua"))
|
||||
end
|
||||
end
|
||||
|
||||
print("load fBakResult,type(para)", fBakResult,type(para))
|
||||
|
||||
if fBakResult then
|
||||
if type(para)=="table" then
|
||||
os.remove(paraname)
|
||||
upd()
|
||||
return
|
||||
else
|
||||
fBakError = true
|
||||
end
|
||||
else
|
||||
para = {}
|
||||
restore()
|
||||
end
|
||||
|
||||
--两种参数文件都不存在,不算异常,以下算是异常情况:
|
||||
--1、参数文件存在,备份参数文件不存在
|
||||
-- (1)、参数文件require异常
|
||||
--2、参数文件不存在,备份参数文件存在
|
||||
-- (1)、备份参数文件require异常
|
||||
--3、参数文件存在,备份参数文件存在
|
||||
-- (1)、参数文件和备份参数文件require都异常
|
||||
if fExist and not fBakExist then
|
||||
if not fResult or fError then return false end
|
||||
end
|
||||
if not fExist and fBakExist then
|
||||
if not fBakResult or fBakError then return false end
|
||||
end
|
||||
if fExist and fBakExist then
|
||||
if (not fResult or fError) and (not fBakResult or fBakError) then return false end
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:save
|
||||
功能 :保存参数文件
|
||||
参数 :
|
||||
s:是否真正保存,true保存,false或者nil不保存
|
||||
返回值:无
|
||||
]]
|
||||
local function save(s)
|
||||
if not s then return end
|
||||
local f = {}
|
||||
f.write = function(self, s)table.insert(self, s) end
|
||||
|
||||
f:write("module(...)\n")
|
||||
|
||||
for k, v in pairs(para) do
|
||||
if k ~= "_M" and k ~= "_NAME" and k ~= "_PACKAGE" then
|
||||
f:write(k .. " = ")
|
||||
serialize(f, v)
|
||||
f:write("\n")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local fparabak = io.open(paranamebak, 'wb')
|
||||
fparabak:write(table.concat(f))
|
||||
fparabak:close()
|
||||
|
||||
os.remove(paraname)
|
||||
os.rename(paranamebak, paraname)
|
||||
end
|
||||
|
||||
--- 初始化参数存储管理模块
|
||||
-- @string defaultCfgFile 默认参数文件名
|
||||
-- @bool burnSave 本地烧录时是否保留已有参数,true为保留,false或者nil为清除
|
||||
-- 注意:在同一个项目,不同版本中,此参数必须保持前后版本一致
|
||||
-- @return boolean result,如果初始化异常,会恢复出厂设置,此时返回false;其余情况返回nil
|
||||
-- @usage
|
||||
-- 初始化参数存储管理模块,默认参数文件名为config.lua,本地烧录时清除已有的参数:
|
||||
-- nvm.init("config.lua")
|
||||
-- 初始化参数存储管理模块,默认参数文件名为config.lua,本地烧录时保留已有的参数:
|
||||
-- nvm.init("config.lua",true)
|
||||
function init(defaultCfgFile,burnSave)
|
||||
local f
|
||||
f, libdftconfig = safePcall(defaultCfgFile:match("(.+)%.lua"))
|
||||
configname, cconfigname, econfigname = "/lua/" .. defaultCfgFile, "/lua/" .. defaultCfgFile .. "c", "/lua/" .. defaultCfgFile .. "e"
|
||||
|
||||
sBurnSave = burnSave
|
||||
if burnSave then
|
||||
rtos.make_dir("/#lua_user#")
|
||||
paraname, paranamebak = "/#lua_user#/nvm_para.lua", "/#lua_user#/nvm_para_bak.lua"
|
||||
end
|
||||
|
||||
--初始化配置文件,从文件中把参数读取到内存中
|
||||
return load()
|
||||
end
|
||||
|
||||
--- 设置某个参数的值
|
||||
-- @string k 参数名
|
||||
-- @param v 参数的新值,仅支持number、string、boolean、table类型
|
||||
-- @param r 设置原因,如果传入了非nil的有效参数,并且v值和旧值相比发生了改变,
|
||||
-- 会产生一个PARA_CHANGED_IND内部消息,携带 k,v,r 3个参数
|
||||
-- @param s 是否立即写入到文件系统中,false不写入,其余的都写入
|
||||
-- @return bool或者nil,成功返回true,失败返回nil
|
||||
-- @usage
|
||||
-- 参数name赋值为Luat,立即写入文件系统:
|
||||
-- nvm.set("name","Luat")
|
||||
--
|
||||
-- 参数age赋值为12,立即写入文件系统:
|
||||
-- 如果旧值不是12,会产生一个PARA_CHANGED_IND消息,携带 "age",12,"SVR" 3个参数:
|
||||
-- nvm.set("age",12,"SVR")
|
||||
--
|
||||
-- 参数class赋值为Class2,不写入文件系统:
|
||||
-- nvm.set("class","Class2",nil,false)
|
||||
--
|
||||
-- 参数score赋值为{chinese=100,math=99,english=98},立即写入文件系统:
|
||||
-- nvm.set("score",{chinese=100,math=99,english=98})
|
||||
--
|
||||
-- 连续写入4个参数,前3个不保存到文件系统中,写第4个时,一次性全部保存到文件系统中:
|
||||
-- nvm.set("para1",1,nil,false)
|
||||
-- nvm.set("para2",2,nil,false)
|
||||
-- nvm.set("para3",3,nil,false)
|
||||
-- nvm.set("para4",4)
|
||||
function set(k, v, r, s)
|
||||
local bchg = true
|
||||
if type(v) ~= "table" then
|
||||
bchg = (para[k] ~= v)
|
||||
end
|
||||
log.info("nvm.set", bchg, k, v, r, s)
|
||||
if bchg then
|
||||
para[k] = v
|
||||
save(s or s == nil)
|
||||
if r then sys.publish("PARA_CHANGED_IND", k, v, r) end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- 设置某个table类型参数的某一个索引的值
|
||||
-- @string k table类型的参数名
|
||||
-- @param kk table类型参数的某一个索引名
|
||||
-- @param v table类型参数的某一个索引的新值
|
||||
-- @param r 设置原因,如果传入了非nil的有效参数,并且v值和旧值相比发生了改变,会产生一个TPARA_CHANGED_IND消息,携带k,kk,v,r4个参数
|
||||
-- @param s 是否立即写入到文件系统中,false不写入,其余的都写入
|
||||
-- @return bool或者nil,成功返回true,失败返回nil
|
||||
-- @usage nvm.sett("score","chinese",100),参数score["chinese"]赋值为100,立即写入文件系统
|
||||
-- @usage nvm.sett("score","chinese",100,"SVR"),参数score["chinese"]赋值为100,立即写入文件系统,
|
||||
-- 如果旧值不是100,会产生一个TPARA_CHANGED_IND消息,携带 "score","chinese",100,"SVR" 4个参数
|
||||
-- @usage nvm.sett("score","chinese",100,nil,false),参数class赋值为Class2,不写入文件系统
|
||||
function sett(k, kk, v, r, s)
|
||||
local bchg = true
|
||||
if type(v) ~= "table" then
|
||||
bchg = (para[k][kk] ~= v)
|
||||
end
|
||||
if bchg then
|
||||
para[k][kk] = v
|
||||
save(s or s == nil)
|
||||
if r then sys.publish("TPARA_CHANGED_IND", k, kk, v, r) end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- 所有参数立即写入文件系统
|
||||
-- @return nil
|
||||
-- @usage nvm.flush()
|
||||
function flush()
|
||||
save(true)
|
||||
end
|
||||
|
||||
--- 读取某个参数的值
|
||||
-- @string k 参数名
|
||||
-- @return 参数值
|
||||
-- @usage
|
||||
-- 读取参数名为name的参数值:
|
||||
-- nameValue = nvm.get("name")
|
||||
function get(k)
|
||||
return para[k]
|
||||
end
|
||||
|
||||
--- 读取某个table类型参数的键名对应的值
|
||||
-- @string k table类型的参数名
|
||||
-- @param kk table类型参数的键名
|
||||
-- @usage
|
||||
-- 有一个table参数为score,数据如下:
|
||||
-- score = {chinese=100, math=100, english=95}
|
||||
-- 读取score中chinese对应的值:
|
||||
-- nvm.gett("score","chinese")
|
||||
function gett(k, kk)
|
||||
return para[k][kk]
|
||||
end
|
||||
|
||||
--- 参数恢复出厂设置
|
||||
-- @return nil
|
||||
-- @usage nvm.restore()
|
||||
function restore()
|
||||
os.remove(paraname)
|
||||
os.remove(paranamebak)
|
||||
local fpara, fconfig = io.open(paraname, "wb"), io.open(configname, "rb")
|
||||
if not fconfig then fconfig = io.open(cconfigname, "rb") end
|
||||
if not fconfig then fconfig = io.open(econfigname, "rb") end
|
||||
fpara:write(fconfig:read("*a"))
|
||||
fpara:close()
|
||||
fconfig:close()
|
||||
upd(true)
|
||||
end
|
||||
|
||||
--- 请求删除参数文件.
|
||||
-- 此接口一般用在远程升级时,需要用新的config.lua覆盖原来的参数文件的场景,在此场景下,远程升级包下载成功后,在确定要重启前调用此接口
|
||||
-- 下次开机执行nvm.init("config.lua")时,会用新的config.lua文件自动覆盖参数文件;以后再开机就不会自动覆盖了
|
||||
-- 也就是说"nvm.remove()->重启->nvm.init("config.lua")"是一个仅执行一次的完整操作
|
||||
-- @return nil
|
||||
-- @usage nvm.remove()
|
||||
function remove()
|
||||
os.remove(paraname)
|
||||
os.remove(paranamebak)
|
||||
end
|
||||
184
4G/源代码/lib/patch.lua
Normal file
184
4G/源代码/lib/patch.lua
Normal file
@@ -0,0 +1,184 @@
|
||||
--- 模块功能:Lua补丁
|
||||
-- @module patch
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2017.10.21
|
||||
|
||||
require"pm"
|
||||
module(..., package.seeall)
|
||||
|
||||
--[[
|
||||
模块名称:Lua自带接口补丁
|
||||
模块功能:补丁某些Lua自带的接口,规避调用异常时死机
|
||||
模块最后修改时间:2017.02.14
|
||||
]]
|
||||
--保存Lua自带的os.time接口
|
||||
local oldostime = os.time
|
||||
|
||||
--[[
|
||||
函数名:safeostime
|
||||
功能 :封装自定义的os.time接口
|
||||
参数 :
|
||||
t:日期表,如果没有传入,使用系统当前时间
|
||||
返回值:t时间距离1970年1月1日0时0分0秒所经过的秒数
|
||||
]]
|
||||
function safeostime(t)
|
||||
return oldostime(t) or 0
|
||||
end
|
||||
|
||||
--Lua自带的os.time接口指向自定义的safeostime接口
|
||||
os.time = safeostime
|
||||
|
||||
--保存Lua自带的os.date接口
|
||||
local oldosdate = os.date
|
||||
|
||||
--[[
|
||||
函数名:safeosdate
|
||||
功能 :封装自定义的os.date接口
|
||||
参数 :
|
||||
s:输出格式
|
||||
t:距离1970年1月1日0时0分0秒所经过的秒数
|
||||
返回值:参考Lua自带的os.date接口说明
|
||||
]]
|
||||
function safeosdate(s, t)
|
||||
if s == "*t" then
|
||||
return oldosdate(s, t) or {year = 2012,
|
||||
month = 12,
|
||||
day = 11,
|
||||
hour = 10,
|
||||
min = 9,
|
||||
sec = 0}
|
||||
else
|
||||
return oldosdate(s, t)
|
||||
end
|
||||
end
|
||||
|
||||
--Lua自带的os.date接口指向自定义的safeosdate接口
|
||||
os.date = safeosdate
|
||||
|
||||
-- 对coroutine.resume加一个修饰器用于捕获协程错误
|
||||
local rawcoresume = coroutine.resume
|
||||
coroutine.resume = function(...)
|
||||
local arg = { ... }
|
||||
function wrapper(co,...)
|
||||
local arg = { ... }
|
||||
if not arg[1] then
|
||||
local traceBack = debug.traceback(co) or "empty"
|
||||
traceBack = (traceBack and traceBack~="") and ((arg[2] or "").."\r\n"..traceBack) or (arg[2] or "")
|
||||
log.error("coroutine.resume",traceBack)
|
||||
if errDump and type(errDump.appendErr)=="function" then
|
||||
errDump.appendErr(traceBack)
|
||||
end
|
||||
if _G.COROUTINE_ERROR_RESTART then rtos.restart() end
|
||||
end
|
||||
return unpack(arg)
|
||||
end
|
||||
return wrapper(arg[1],rawcoresume(...))
|
||||
end
|
||||
|
||||
os.clockms = function() return rtos.tick()/16 end
|
||||
|
||||
--保存Lua自带的json.decode接口
|
||||
if json and json.decode then oldjsondecode = json.decode end
|
||||
|
||||
--- 封装自定义的json.decode接口
|
||||
-- @string s json格式的字符串
|
||||
-- @return table,第一个返回值为解析json字符串后的table
|
||||
-- @return boole,第二个返回值为解析结果(true表示成功,false失败)
|
||||
-- @return string,第三个返回值可选(只有第二个返回值为false时,才有意义),表示出错信息
|
||||
local function safeJsonDecode(s)
|
||||
local result, info = pcall(oldjsondecode, s)
|
||||
if result then
|
||||
return info, true
|
||||
else
|
||||
return {}, false, info
|
||||
end
|
||||
end
|
||||
|
||||
--Lua自带的json.decode接口指向自定义的safeJsonDecode接口
|
||||
if json and json.decode then json.decode = safeJsonDecode end
|
||||
|
||||
local oldUartWrite = uart.write
|
||||
uart.write = function(...)
|
||||
pm.wake("lib.patch.uart.write")
|
||||
local result = oldUartWrite(...)
|
||||
pm.sleep("lib.patch.uart.write")
|
||||
return result
|
||||
end
|
||||
|
||||
if i2c and i2c.write then
|
||||
local oldI2cWrite = i2c.write
|
||||
i2c.write = function(...)
|
||||
pm.wake("lib.patch.i2c.write")
|
||||
local result = oldI2cWrite(...)
|
||||
pm.sleep("lib.patch.i2c.write")
|
||||
return result
|
||||
end
|
||||
end
|
||||
|
||||
if i2c and i2c.send then
|
||||
local oldI2cSend = i2c.send
|
||||
i2c.send = function(...)
|
||||
pm.wake("lib.patch.i2c.send")
|
||||
local result = oldI2cSend(...)
|
||||
pm.sleep("lib.patch.i2c.send")
|
||||
return result
|
||||
end
|
||||
end
|
||||
|
||||
if spi and spi.send then
|
||||
oldSpiSend = spi.send
|
||||
spi.send = function(...)
|
||||
pm.wake("lib.patch.spi.send")
|
||||
local result = oldSpiSend(...)
|
||||
pm.sleep("lib.patch.spi.send")
|
||||
return result
|
||||
end
|
||||
end
|
||||
|
||||
if spi and spi.send_recv then
|
||||
oldSpiSendRecv = spi.send_recv
|
||||
spi.send_recv = function(...)
|
||||
pm.wake("lib.patch.spi.send_recv")
|
||||
local result = oldSpiSendRecv(...)
|
||||
pm.sleep("lib.patch.spi.send_recv")
|
||||
return result
|
||||
end
|
||||
end
|
||||
|
||||
if disp and disp.sleep then
|
||||
oldDispSleep = disp.sleep
|
||||
disp.sleep = function(...)
|
||||
pm.wake("lib.patch.disp.sleep")
|
||||
oldDispSleep(...)
|
||||
pm.sleep("lib.patch.disp.sleep")
|
||||
end
|
||||
end
|
||||
|
||||
if io and io.mount then
|
||||
oldIoMount = io.mount
|
||||
io.mount = function (...)
|
||||
pm.wake("lib.patch.io.mount")
|
||||
local result = oldIoMount(...)
|
||||
pm.sleep("lib.patch.io.mount")
|
||||
return result
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
local pmdInited
|
||||
|
||||
if pmd and pmd.init then
|
||||
oldPmdInit = pmd.init
|
||||
pmd.init = function (...)
|
||||
if not pmdInited then pmdInited = true end
|
||||
local result = oldPmdInit(...)
|
||||
return result
|
||||
end
|
||||
end
|
||||
|
||||
pmd.libScriptInit = function()
|
||||
if not pmdInited then pmd.init({}) end
|
||||
end
|
||||
116
4G/源代码/lib/pb.lua
Normal file
116
4G/源代码/lib/pb.lua
Normal file
@@ -0,0 +1,116 @@
|
||||
--- 模块功能:电话簿管理
|
||||
-- @module pb
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2018.03.10
|
||||
|
||||
module(..., package.seeall)
|
||||
|
||||
require"ril"
|
||||
|
||||
local req = ril.request
|
||||
|
||||
local storagecb,readcb,writecb,deletecb
|
||||
|
||||
local curPb = "SM"
|
||||
|
||||
--- 设置电话本存储区域
|
||||
-- @string storage 存储区域字符串,仅支持"SM"
|
||||
-- @param cb 设置后的回调函数
|
||||
--
|
||||
-- 回调方式为cb(result),result为true表示成功,false或者nil表示失败
|
||||
-- @return 无
|
||||
-- @usage pb.setStorage(storage,cb)
|
||||
function setStorage(storage,cb)
|
||||
if storage=="SM" or storage=="FD" then
|
||||
storagecb = cb
|
||||
req("AT+CPBS=\"" .. storage .. "\"" )
|
||||
end
|
||||
end
|
||||
|
||||
--- 读取一条电话本记录
|
||||
-- @number index 电话本在存储区的位置
|
||||
-- @function cb function类型,读取后的回调函数
|
||||
--
|
||||
-- 回调方式为cb(result,name,number):result为true表示成功,false或者nil表示失败;name为姓名;number为号码
|
||||
-- @usage pb.read(1,cb)
|
||||
function read(index,cb)
|
||||
if index == "" or index == nil then
|
||||
return false
|
||||
end
|
||||
readcb = cb
|
||||
req("AT+CPBR=" .. index)
|
||||
end
|
||||
|
||||
--- 写入一条电话本记录
|
||||
-- @number index 电话本在存储区的位置
|
||||
-- @string name 姓名
|
||||
-- @string num 号码
|
||||
-- @function cb functionl类型,写入后的回调函数
|
||||
--
|
||||
-- 回调方式为cb(result):result为true表示成功,false或者nil表示失败
|
||||
-- @return 无
|
||||
-- @usage pb.write(1,"zhangsan","13233334444",cb)
|
||||
function write(index,name,num,cb)
|
||||
if num == nil or name == nil or index == nil then
|
||||
return false
|
||||
end
|
||||
writecb = cb
|
||||
req("AT+CPBW=" .. index .. ",\"" .. num .. "\"," .. "129" .. ",\"" .. name .. "\"" )
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
--- 删除一条电话本记录
|
||||
-- @number index 电话本在存储区的位置
|
||||
-- @function cb function类型,删除后的回调函数
|
||||
--
|
||||
-- 回调方式为cb(result):result为true表示成功,false或者nil表示失败
|
||||
-- @return 无
|
||||
-- @usage pb.delete(1,cb)
|
||||
function delete(index,cb)
|
||||
if index == "" or index == nil then
|
||||
return false
|
||||
end
|
||||
deletecb = cb
|
||||
req("AT+CPBW=" .. index)
|
||||
return true
|
||||
end
|
||||
|
||||
local function pbrsp(cmd,success,response,intermediate)
|
||||
local prefix = string.match(cmd,"AT(%+%u+%?*)")
|
||||
intermediate = intermediate or ""
|
||||
|
||||
if prefix == "+CPBR" then
|
||||
local index = string.match(cmd,"AT%+CPBR%s*=%s*(%d+)")
|
||||
local num,name = string.match(intermediate,"+CPBR:%s*%d+,\"([#%*%+%d]*)\",%d+,\"(%w*)\"")
|
||||
num,name = num or "",name or ""
|
||||
sys.publish("PB_READ_CNF",success,index,num,name)
|
||||
local cb = readcb
|
||||
readcb = nil
|
||||
if cb then cb(success,name,num) return end
|
||||
elseif prefix == "+CPBW" then
|
||||
sys.publish("PB_WRITE_CNF",success)
|
||||
local cb = writecb
|
||||
writecb = nil
|
||||
if cb then cb(success) return end
|
||||
cb = deletecb
|
||||
deletecb = nil
|
||||
if cb then cb(success) return end
|
||||
elseif prefix == "+CPBS?" then
|
||||
local storage,used,total = string.match(intermediate,"+CPBS:%s*\"(%u+)\",(%d+),(%d+)")
|
||||
used,total = tonumber(used),tonumber(total)
|
||||
sys.publish("CPBS_READ_CNF",success,storage,used,total)
|
||||
elseif prefix == "+CPBS" then
|
||||
local cb = storagecb
|
||||
storagecb = nil
|
||||
if cb then cb(success) return end
|
||||
end
|
||||
end
|
||||
|
||||
ril.regRsp("+CPBR",pbrsp)
|
||||
ril.regRsp("+CPBW",pbrsp)
|
||||
ril.regRsp("+CPBS",pbrsp)
|
||||
ril.regRsp("+CPBS?",pbrsp)
|
||||
req("AT+CPBS=\"SM\"")
|
||||
150
4G/源代码/lib/pins.lua
Normal file
150
4G/源代码/lib/pins.lua
Normal file
@@ -0,0 +1,150 @@
|
||||
--- 模块功能:GPIO 功能配置,包括输入输出IO和上升下降沿中断IO
|
||||
-- @module pins
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2017.09.23 11:34
|
||||
require "sys"
|
||||
module(..., package.seeall)
|
||||
local interruptCallbacks = {}
|
||||
local dirs = {}
|
||||
--- 配置GPIO模式
|
||||
-- @number pin GPIO,ID
|
||||
-- GPIO 0到GPIO 31表示为pio.P0_0到pio.P0_31
|
||||
-- GPIO 32到GPIO XX表示为pio.P1_0到pio.P1_(XX-32),例如GPIO33 表示为pio.P1_1
|
||||
-- GPIO 64到GPIO XX表示为pio.P2_0到pio.P2_(XX-64),例如GPIO65 表示为pio.P2_1
|
||||
-- @param val number、nil或者function类型
|
||||
-- 配置为输出模式时,为number类型,表示默认电平,0是低电平,1是高电平
|
||||
-- 配置为输入模式时,为nil
|
||||
-- 配置为中断模式时,为function类型,表示中断处理函数
|
||||
-- @param pull number,pio.PULLUP:上拉模式。pio.PULLDOWN:下拉模式。pio.NOPULL:高阻态
|
||||
-- 如果没有设置此参数,默认的上下拉参考模块的硬件设计说明书
|
||||
-- @return function
|
||||
-- 配置为输出模式时,返回的函数,可以设置IO的电平
|
||||
-- 配置为输入或者中断模式时,返回的函数,可以实时获取IO的电平
|
||||
-- @usage setOutputFnc = pins.setup(pio.P1_1,0),配置GPIO 33,输出模式,默认输出低电平;
|
||||
--执行setOutputFnc(0)可输出低电平,执行setOutputFnc(1)可输出高电平
|
||||
-- @usage getInputFnc = pins.setup(pio.P1_1,intFnc),配置GPIO33,中断模式
|
||||
-- 产生中断时自动调用intFnc(msg)函数:上升沿中断时:msg为cpu.INT_GPIO_POSEDGE;下降沿中断时:msg为cpu.INT_GPIO_NEGEDGE
|
||||
-- 执行getInputFnc()即可获得当前电平;如果是低电平,getInputFnc()返回0;如果是高电平,getInputFnc()返回1
|
||||
-- @usage getInputFnc = pins.setup(pio.P1_1),配置GPIO33,输入模式
|
||||
--执行getInputFnc()即可获得当前电平;如果是低电平,getInputFnc()返回0;如果是高电平,getInputFnc()返回1
|
||||
-- @usage
|
||||
--有些GPIO需要打开对应的ldo电压域之后,才能正常配置工作,电压域和对应的GPIO关系如下
|
||||
--pmd.ldoset(x,pmd.LDO_VSIM1) -- GPIO 29、30、31
|
||||
--pmd.ldoset(x,pmd.LDO_VLCD) -- GPIO 0、1、2、3、4
|
||||
--pmd.ldoset(x,pmd.LDO_VMMC) -- GPIO 24、25、26、27、28
|
||||
--x=0时:关闭LDO
|
||||
--x=1时:LDO输出1.716V
|
||||
--x=2时:LDO输出1.828V
|
||||
--x=3时:LDO输出1.939V
|
||||
--x=4时:LDO输出2.051V
|
||||
--x=5时:LDO输出2.162V
|
||||
--x=6时:LDO输出2.271V
|
||||
--x=7时:LDO输出2.375V
|
||||
--x=8时:LDO输出2.493V
|
||||
--x=9时:LDO输出2.607V
|
||||
--x=10时:LDO输出2.719V
|
||||
--x=11时:LDO输出2.831V
|
||||
--x=12时:LDO输出2.942V
|
||||
--x=13时:LDO输出3.054V
|
||||
--x=14时:LDO输出3.165V
|
||||
--x=15时:LDO输出3.177V
|
||||
--除了上面列举出的GPIO外,其余的GPIO不需要打开特定的电压域,可以直接配置工作
|
||||
function setup(pin, val, pull)
|
||||
-- 关闭该IO
|
||||
pio.pin.close(pin)
|
||||
-- 中断模式配置
|
||||
if type(val) == "function" then
|
||||
pio.pin.setdir(pio.INT, pin)
|
||||
if pull then pio.pin.setpull(pull or pio.PULLUP, pin) end
|
||||
--注册引脚中断的处理函数
|
||||
interruptCallbacks[pin] = val
|
||||
dirs[pin] = false
|
||||
return function()
|
||||
return pio.pin.getval(pin)
|
||||
end
|
||||
end
|
||||
-- 输出模式初始化默认配置
|
||||
if val ~= nil then
|
||||
dirs[pin] = true
|
||||
pio.pin.setdir(val == 1 and pio.OUTPUT1 or pio.OUTPUT, pin)
|
||||
else
|
||||
-- 输入模式初始化默认配置
|
||||
dirs[pin] = false
|
||||
pio.pin.setdir(pio.INPUT, pin)
|
||||
if pull then pio.pin.setpull(pull or pio.PULLUP, pin) end
|
||||
end
|
||||
-- 返回一个自动切换输入输出模式的函数
|
||||
return function(val)
|
||||
val = tonumber(val)
|
||||
if (not val and dirs[pin]) or (val and not dirs[pin]) then
|
||||
pio.pin.close(pin)
|
||||
pio.pin.setdir(val and (val == 1 and pio.OUTPUT1 or pio.OUTPUT) or pio.INPUT, pin)
|
||||
if not val and pull then pio.pin.setpull(pull or pio.PULLUP, pin) end
|
||||
dirs[pin] = val and true or false
|
||||
return val or pio.pin.getval(pin)
|
||||
end
|
||||
if val then
|
||||
pio.pin.setval(val, pin)
|
||||
return val
|
||||
else
|
||||
return pio.pin.getval(pin)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- 关闭GPIO模式
|
||||
-- @number pin GPIO,ID
|
||||
--
|
||||
-- GPIO 0到GPIO 31表示为pio.P0_0到pio.P0_31
|
||||
--
|
||||
-- GPIO 32到GPIO XX表示为pio.P1_0到pio.P1_(XX-32),例如GPIO33 表示为pio.P1_1
|
||||
-- @usage pins.close(pio.P1_1),关闭GPIO33
|
||||
function close(pin)
|
||||
pio.pin.close(pin)
|
||||
end
|
||||
|
||||
rtos.on(rtos.MSG_INT, function(msg)
|
||||
if interruptCallbacks[msg.int_resnum] == nil then
|
||||
log.warn('pins.rtos.on', 'warning:rtos.MSG_INT callback nil', msg.int_resnum)
|
||||
return
|
||||
end
|
||||
interruptCallbacks[msg.int_resnum](msg.int_id)
|
||||
end)
|
||||
|
||||
-- 795io引脚复用 宏
|
||||
IOMUX_GPIO0 = 0
|
||||
IOMUX_GPIO1 = 1
|
||||
IOMUX_GPIO2 = 2
|
||||
IOMUX_GPIO3 = 3
|
||||
IOMUX_GPIO4 = 4
|
||||
IOMUX_GPIO5 = 5
|
||||
|
||||
IOMUX_GPIO8 = 8
|
||||
IOMUX_GPIO9 = 9
|
||||
IOMUX_GPIO10 = 10
|
||||
|
||||
IOMUX_GPIO11 = 11
|
||||
IOMUX_GPIO12 = 12
|
||||
IOMUX_GPIO13 = 13
|
||||
IOMUX_GPIO14 = 14
|
||||
IOMUX_GPIO15 = 15
|
||||
IOMUX_GPIO18 = 18
|
||||
IOMUX_GPIO19 = 19
|
||||
IOMUX_GPIO20 = 20
|
||||
|
||||
IOMUX_GPIO21 = 21
|
||||
IOMUX_GPIO22 = 22
|
||||
IOMUX_GPIO23 = 23
|
||||
IOMUX_GPIO29 = 29
|
||||
IOMUX_GPIO30 = 30
|
||||
|
||||
IOMUX_GPIO31 = 31
|
||||
|
||||
IOMUX_USART_1 = 57
|
||||
IOMUX_USART_2 = 58
|
||||
IOMUX_USART_3 = 59
|
||||
|
||||
IOMUX_I2C_2 = 67
|
||||
IOMUX_I2C_3 = 68
|
||||
65
4G/源代码/lib/pm.lua
Normal file
65
4G/源代码/lib/pm.lua
Normal file
@@ -0,0 +1,65 @@
|
||||
--- 模块功能:休眠管理
|
||||
-- @module pm
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2017.10.22
|
||||
module(..., package.seeall)
|
||||
|
||||
--[[
|
||||
关于休眠这一部分的说明:
|
||||
目前的休眠处理有两种方式,
|
||||
一种是底层core内部,自动处理,例如tcp发送或者接收数据时,会自动唤醒,发送接收结束后,会自动休眠;这部分不用lua脚本控制
|
||||
另一种是lua脚本使用pm.sleep和pm.wake自行控制,例如,uart连接外围设备,uart接收数据前,要主动去pm.wake,这样才能保证前面接收的数据不出错,当不需要通信时,调用pm.sleep;如果有lcd的项目,也是同样道理
|
||||
不休眠时功耗至少30mA左右
|
||||
如果不是故意控制的不休眠,一定要保证pm.wake("A")了,有地方去调用pm.sleep("A")
|
||||
]]
|
||||
--唤醒标记表
|
||||
local tags = {}
|
||||
--lua应用是否休眠,true休眠,其余没休眠
|
||||
local flag = true
|
||||
|
||||
--- 某个Lua应用唤醒系统
|
||||
-- @param tag 一般string类型,某个Lua应用的唤醒标记,用户自定义
|
||||
-- @return 无
|
||||
-- @usage pm.wake(tag)
|
||||
function wake(tag)
|
||||
assert(tag and tag ~= nil, "pm.wake tag invalid")
|
||||
--唤醒表中此唤醒标记位置置1
|
||||
tags[tag] = 1
|
||||
--如果lua应用处于休眠状态
|
||||
if flag == true then
|
||||
--设置为唤醒状态
|
||||
flag = false
|
||||
--调用底层软件接口,真正唤醒系统
|
||||
pmd.sleep(0)
|
||||
end
|
||||
end
|
||||
--- 某个Lua应用休眠系统
|
||||
-- @param tag 一般string类型,某个Lua应用的唤醒标记,用户自定义,跟wake中的标记保持一致
|
||||
-- @return 无
|
||||
-- @usage pm.sleep(tag)
|
||||
function sleep(tag)
|
||||
assert(tag and tag ~= nil, "pm.sleep tag invalid")
|
||||
--唤醒表中此休眠标记位置置0
|
||||
tags[tag] = 0
|
||||
--只要存在任何一个标记唤醒,则不睡眠
|
||||
for k, v in pairs(tags) do
|
||||
if v > 0 then
|
||||
return
|
||||
end
|
||||
end
|
||||
flag = true
|
||||
--调用底层软件接口,真正休眠系统
|
||||
pmd.sleep(1)
|
||||
end
|
||||
|
||||
--- pm.isSleep([tag]) 读取某个Lua应用或者全局的休眠状态
|
||||
-- @param tag 可选参数,如果查询某个tag的休眠状态,则跟wake中的tag保持一致;如果查询全局休眠状态,则不需要这个参数
|
||||
-- @return true休眠,其余没休眠
|
||||
-- @usage
|
||||
-- pm.isSleep() -- 查询全局休眠状态
|
||||
-- pm.isSleep('lcd') -- 查询lcd的休眠状态
|
||||
function isSleep(tag)
|
||||
return tag and tags[tag] ~= 1 or flag
|
||||
end
|
||||
57
4G/源代码/lib/powerKey.lua
Normal file
57
4G/源代码/lib/powerKey.lua
Normal file
@@ -0,0 +1,57 @@
|
||||
--- 模块功能:开机键功能配置
|
||||
-- @module powerKey
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2018.06.13
|
||||
|
||||
require"sys"
|
||||
module(..., package.seeall)
|
||||
|
||||
--[[
|
||||
sta:按键状态,IDLE表示空闲状态,PRESSED表示已按下状态,LONGPRESSED表示已经长按下状态
|
||||
longprd:长按键判断时长,默认3秒;按下大于等于3秒再弹起判定为长按键;按下后,在3秒内弹起,判定为短按键
|
||||
longcb:长按键处理函数
|
||||
shortcb:短按键处理函数
|
||||
]]
|
||||
local sta,longprd,longcb,shortcb = "IDLE",3000
|
||||
|
||||
local function longtimercb()
|
||||
log.info("keypad.longtimercb")
|
||||
sta = "LONGPRESSED"
|
||||
end
|
||||
|
||||
local function keyMsg(msg)
|
||||
log.info("keyMsg",msg.key_matrix_row,msg.key_matrix_col,msg.pressed)
|
||||
if msg.pressed then
|
||||
sta = "PRESSED"
|
||||
sys.timerStart(longtimercb,longprd)
|
||||
else
|
||||
sys.timerStop(longtimercb)
|
||||
if sta=="PRESSED" then
|
||||
if shortcb then shortcb() end
|
||||
elseif sta=="LONGPRESSED" then
|
||||
(longcb or rtos.poweroff)()
|
||||
end
|
||||
sta = "IDLE"
|
||||
end
|
||||
end
|
||||
|
||||
--- 配置开机键长按弹起和短按弹起的功能.
|
||||
-- 如何定义长按键和短按键,例如长按键判断时长为3秒:
|
||||
-- 按下大于等于3秒再弹起判定为长按键;
|
||||
-- 按下后,在3秒内弹起,判定为短按键
|
||||
-- @number[opt=3000] longPrd 长按键判断时长,单位毫秒
|
||||
-- @function[opt=nil] longCb 长按弹起时的回调函数,如果为nil,使用默认的处理函数,会自动关机
|
||||
-- @function[opt=nil] shortCb 短按弹起时的回调函数
|
||||
-- @return nil
|
||||
-- @usage
|
||||
-- powerKey.setup(nil,longCb,shortCb)
|
||||
-- powerKey.setup(5000,longCb)
|
||||
-- powerKey.setup()
|
||||
function setup(longPrd,longCb,shortCb)
|
||||
longprd,longcb,shortcb = longPrd or 3000,longCb,shortCb
|
||||
end
|
||||
|
||||
rtos.on(rtos.MSG_KEYPAD,keyMsg)
|
||||
rtos.init_module(rtos.MOD_KEYPAD,0,0,0)
|
||||
183
4G/源代码/lib/record.lua
Normal file
183
4G/源代码/lib/record.lua
Normal file
@@ -0,0 +1,183 @@
|
||||
--- 模块功能:录音处理
|
||||
-- @module record
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2017.11.23
|
||||
|
||||
require "log"
|
||||
require "ril"
|
||||
module(..., package.seeall)
|
||||
|
||||
|
||||
local FILE = '/record.amr'
|
||||
local recordType = "FILE"
|
||||
local recording,stoping,recordCb,stopCbFnc
|
||||
|
||||
--- 开始录音
|
||||
-- @number seconds 录音时长,单位:秒
|
||||
-- 流录音模式下,如果想长时间录音,可以将此参数设置为0x7FFFFFFF,相当于录音2147483647秒=24855天
|
||||
-- @function[opt=nil] cbFnc 录音回调函数:
|
||||
-- 当type参数为"FILE"时,回调函数的调用形式为:
|
||||
-- cbFnc(result,size)
|
||||
-- result:录音结果,true表示成功,false或者nil表示失败
|
||||
-- size:number类型,录音文件的大小,单位是字节,在result为true时才有意义
|
||||
-- 当type参数为"STREAM"时,回调函数的调用形式为:
|
||||
-- cbFnc(result,size,tag)
|
||||
-- result:录音结果,true表示成功,false或者nil表示失败
|
||||
-- size:number类型,每次上报的录音数据流的大小,单位是字节,在result为true时才有意义
|
||||
-- tag:string类型,"STREAM"表示录音数据流通知,"END"表示录音结束
|
||||
-- @string[opt="FILE"] type 录音模式
|
||||
-- "FILE"表示文件录音模式,录音数据自动保存在文件中,录音结束后,执行一次cbFnc函数
|
||||
-- "STREAM"表示流录音模式,录音数据保存在内存中,每隔一段时间执行一次cbFnc函数去读取录音数据流,录音结束后再执行一次cbFnc函数
|
||||
-- @number[opt=1] quality 录音质量,0:一般质量,1:中等质量,2:高质量,3:无损质量
|
||||
-- @number[opt=2] rcdType 录音类型,n:1:mic(从麦克风录制),2:voice(录制语音通话,录制的流与上下行通道),3:voice_dual(在poc模式下从麦克风录制)
|
||||
-- @number[opt=3] format 录音格式,1:pcm,2:wav,3:amrnb,4:speex
|
||||
-- pcm格式:录音质量参数无效,采样率:8000,单声道,采样精度:16 bit,5秒钟录音80KB左右
|
||||
-- wav格式:录音质量参数无效,比特率:128kbps,5秒钟录音80KB左右
|
||||
-- amrnb格式:录音质量参数有效
|
||||
-- 录音质量为0时:比特率:5.15kbps,5秒钟录音3KB多
|
||||
-- 录音质量为1时:比特率:6.70kbps,5秒钟录音4KB多
|
||||
-- 录音质量为2时:比特率:7.95kbps,5秒钟录音4KB多
|
||||
-- 录音质量为3时:比特率:12.2kbps,5秒钟录音7KB多
|
||||
-- speex格式:录音质量参数无效,pcm格式128kbps后的压缩格式,5秒钟6KB左右
|
||||
-- @number[opt=nil] streamRptLen 流录音时,每次上报的字节阀值
|
||||
-- @usage
|
||||
-- 文件录音模式,录音5秒,一般质量,amrnb格式,录音结束后执行cbFnc函数:
|
||||
-- record.start(5,cbFnc)
|
||||
-- 流录音模式,录音5秒,一般质量,amrnb格式,每隔一段时间执行一次cbFnc函数,录音结束后再执行一次cbFnc函数:
|
||||
-- record.start(5,cbFnc,"STREAM")
|
||||
-- 流录音模式,录音5秒,一般质量,amrnb格式,每产生500字节的录音数据执行一次cbFnc函数,录音结束后再执行一次cbFnc函数:
|
||||
-- record.start(5,cbFnc,"STREAM",nil,nil,500)
|
||||
function start(seconds, cbFnc, type, quality, rcdType,format, streamRptLen)
|
||||
if recording or stoping or seconds <= 0 or ((type~="STREAM") and seconds>50) then
|
||||
log.error('record.start', recording, stoping, seconds)
|
||||
if cbFnc then cbFnc() end
|
||||
return
|
||||
end
|
||||
delete()
|
||||
|
||||
recordType = type or "FILE"
|
||||
if type=="STREAM" then
|
||||
--param1: 录音时长 n:单位秒
|
||||
--param2: 录音质量 n:0:一般质量 1:中等质量 2:高质量 3:无损质量
|
||||
--param3:录音类型 n:1:mic 2:voice 3:voice_dual
|
||||
--param4:录音文件类型 n: 1:pcm 2:wav 3:amrnb
|
||||
audiocore.streamrecord(seconds,quality or 1,rcdType or 1,format or 3,streamRptLen)
|
||||
else
|
||||
--param1: 录音保存文件
|
||||
--param2: 录音时长 n:单位秒
|
||||
--param3: 录音质量 n:0:一般质量 1:中等质量 2:高质量 3:无损质量
|
||||
--param4:录音类型 n:1:mic 2:voice 3:voice_dual
|
||||
--param5:录音文件类型 n: 1:pcm 2:wav 3:amrnb
|
||||
audiocore.record(FILE,seconds,quality or 1,rcdType or 1,format or 3)
|
||||
end
|
||||
log.info("record.start",seconds,recordType,format or 3)
|
||||
recording = true
|
||||
recordCb = cbFnc
|
||||
return true
|
||||
end
|
||||
|
||||
--- 停止录音
|
||||
-- @function[opt=nil] cbFnc 停止录音的回调函数(停止结果通过此函数通知用户),回调函数的调用形式为:
|
||||
-- cbFnc(result)
|
||||
-- result:number类型
|
||||
-- 0表示停止成功
|
||||
-- 1表示之前已经发送了停止动作,请耐心等待停止结果的回调
|
||||
-- @usage record.stop(cb)
|
||||
function stop(cbFnc)
|
||||
if not recording then
|
||||
if cbFnc then cbFnc(0) end
|
||||
return
|
||||
end
|
||||
if stoping then
|
||||
if cbFnc then cbFnc(1) end
|
||||
return
|
||||
end
|
||||
stopCbFnc = cbFnc
|
||||
log.info("record.stop")
|
||||
audiocore.stoprecord()
|
||||
stoping = true
|
||||
end
|
||||
|
||||
--- 读取录音文件的完整路径
|
||||
-- @return string 录音文件的完整路径
|
||||
-- @usage filePath = record.getFilePath()
|
||||
function getFilePath()
|
||||
return FILE
|
||||
end
|
||||
|
||||
--- 读取录音数据
|
||||
-- @param offset 偏移位置
|
||||
-- @param len 长度
|
||||
-- @return data 录音数据
|
||||
-- @usage data = record.getData(0, 1024)
|
||||
function getData(offset, len)
|
||||
local f = io.open(FILE, "rb")
|
||||
if not f then log.error('record.getData', 'open failed') return "" end
|
||||
if not f:seek("set", offset) then log.error('record.getData', 'seek failed') f:close() return "" end
|
||||
local data = f:read(len)
|
||||
f:close()
|
||||
log.info("record.getData", data and data:len() or 0)
|
||||
return data or ""
|
||||
end
|
||||
|
||||
--- 读取录音文件总长度,录音时长
|
||||
-- @return fileSize 录音文件大小
|
||||
-- @return duration 录音时长
|
||||
-- @usage fileSize, duration = record.getSize()
|
||||
function getSize()
|
||||
local size,duration = io.fileSize(FILE),0
|
||||
if size>6 then
|
||||
duration = ((size-6)-((size-6)%1600))/1600
|
||||
end
|
||||
return size, duration
|
||||
end
|
||||
|
||||
--- 删除录音
|
||||
-- @usage record.delete()
|
||||
function delete()
|
||||
log.info("record.delete")
|
||||
audiocore.deleterecord()
|
||||
os.remove(FILE)
|
||||
end
|
||||
|
||||
--- 判断是否存在录音
|
||||
-- @return result true - 有录音 false - 无录音
|
||||
-- @usage result = record.exists()
|
||||
function exists()
|
||||
return io.exists(FILE)
|
||||
end
|
||||
|
||||
--- 是否正在处理录音
|
||||
-- @return result true - 正在处理 false - 空闲
|
||||
-- @usage result = record.isBusy()
|
||||
function isBusy()
|
||||
return recording or stoping
|
||||
end
|
||||
|
||||
|
||||
rtos.on(rtos.MSG_RECORD,function(msg)
|
||||
log.info("record.MSG_RECORD",msg.record_end_ind,msg.record_error_ind,recordType)
|
||||
--文件录音,在回调时可以删除录音buf;但是流录音,一定要等buf读取完成后,再删除
|
||||
if recordType=="FILE" then audiocore.deleterecord() end
|
||||
if msg.record_error_ind then
|
||||
delete()
|
||||
if recordCb then recordCb(false,0,"END") recordCb = nil end
|
||||
recording = false
|
||||
stoping = false
|
||||
if stopCbFnc then stopCbFnc(0) stopCbFnc=nil end
|
||||
end
|
||||
if msg.record_end_ind then
|
||||
if recordCb then recordCb(true,recordType=="FILE" and io.fileSize(FILE) or 0,"END") recordCb = nil end
|
||||
recording = false
|
||||
stoping = false
|
||||
if stopCbFnc then stopCbFnc(0) stopCbFnc=nil end
|
||||
end
|
||||
end)
|
||||
|
||||
rtos.on(rtos.MSG_STREAM_RECORD,function(msg)
|
||||
log.info("record.MSG_STREAM_RECORD",msg.wait_read_len)
|
||||
if recordCb then recordCb(true,msg.wait_read_len,"STREAM") end
|
||||
end)
|
||||
|
||||
554
4G/源代码/lib/ril.lua
Normal file
554
4G/源代码/lib/ril.lua
Normal file
@@ -0,0 +1,554 @@
|
||||
--- 模块功能:虚拟串口AT命令交互管理
|
||||
-- @module ril
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2017.02.13
|
||||
|
||||
require "uart"
|
||||
require "rtos"
|
||||
require "sys"
|
||||
require "log"
|
||||
module(..., package.seeall)
|
||||
|
||||
--加载常用的全局函数至本地
|
||||
local vwrite = uart.write
|
||||
local vread = uart.read
|
||||
|
||||
--是否为透传模式,true为透传模式,false或者nil为非透传模式
|
||||
--默认非透传模式
|
||||
local transparentmode
|
||||
--透传模式下,虚拟串口数据接收的处理函数
|
||||
local rcvfunc
|
||||
|
||||
--执行AT命令后1分钟无反馈,判定at命令执行失败,则重启软件
|
||||
local TIMEOUT = 60000*3
|
||||
|
||||
--AT命令的应答类型
|
||||
--NORESULT:收到的应答数据当做urc通知处理,如果发送的AT命令不处理应答或者没有设置类型,默认为此类型
|
||||
--NUMBERIC:纯数字类型;例如发送AT+CGSN命令,应答的内容为:862991527986589\r\nOK,此类型指的是862991527986589这一部分为纯数字类型
|
||||
--SLINE:有前缀的单行字符串类型;例如发送AT+CSQ命令,应答的内容为:+CSQ: 23,99\r\nOK,此类型指的是+CSQ: 23,99这一部分为单行字符串类型
|
||||
--MLINE:有前缀的多行字符串类型;例如发送AT+CMGR=5命令,应答的内容为:+CMGR: 0,,84\r\n0891683108200105F76409A001560889F800087120315123842342050003590404590D003A59\r\nOK,此类型指的是OK之前为多行字符串类型
|
||||
--STRING:无前缀的字符串类型,例如发送AT+ATWMFT=99命令,应答的内容为:SUCC\r\nOK,此类型指的是SUCC
|
||||
--SPECIAL:特殊类型,需要针对AT命令做特殊处理,例如CIPSEND、CIPCLOSE、CIFSR
|
||||
local NORESULT, NUMBERIC, SLINE, MLINE, STRING, SPECIAL = 0, 1, 2, 3, 4, 10
|
||||
|
||||
--AT命令的应答类型表,预置了如下几项
|
||||
local RILCMD = {
|
||||
["+CSQ"] = 2,
|
||||
["+CESQ"] = 2,
|
||||
["+CGMM"] = 2,
|
||||
["+RFTEMPERATURE"] =2,
|
||||
["+MUID"] = 2,
|
||||
["+CGSN"] = 1,
|
||||
["+WISN"] = 4,
|
||||
["+CIMI"] = 1,
|
||||
["+ICCID"] = 2,
|
||||
["+SIMCROSS"] = 2,
|
||||
["+CGATT"] = 2,
|
||||
["+CCLK"] = 2,
|
||||
['+CNUM'] = 3,
|
||||
--["+ATWMFT"] = 4,
|
||||
["+CMGR"] = 3,
|
||||
["+CMGS"] = 2,
|
||||
["+CPBF"] = 3,
|
||||
["+CPBR"] = 3,
|
||||
['+CLCC'] = 3,
|
||||
["+CTFSGETID"] = 2,
|
||||
["+CTFSDECRYPT"] = 2,
|
||||
["+CTFSAUTH"] = 2,
|
||||
["+CGDATA"] = 10,
|
||||
["+CIND"] = 2,
|
||||
--["+CGDCONT"] = 3,
|
||||
["+CGACT"] = 3,
|
||||
["+CALIBINFO"] = 4,
|
||||
["*CALINFO"] = 3,
|
||||
}
|
||||
|
||||
--radioready:AT命令通道是否准备就绪
|
||||
--delaying:执行完某些AT命令前,需要延时一段时间,才允许执行这些AT命令;此标志表示是否在延时状态
|
||||
local radioready, delaying = false
|
||||
|
||||
--AT命令队列
|
||||
local cmdqueue = {
|
||||
"ATE0",
|
||||
"AT+CMEE=0",
|
||||
}
|
||||
--当前正在执行的AT命令,参数,反馈回调,延迟执行时间,命令头,类型,反馈格式
|
||||
local currcmd, currarg, currsp, curdelay, cmdhead, cmdtype, rspformt
|
||||
--反馈结果,中间信息,结果信息
|
||||
local result, interdata, respdata
|
||||
|
||||
--ril会出现三种情况:
|
||||
--发送AT命令,收到应答
|
||||
--发送AT命令,命令超时没有应答
|
||||
--底层软件主动上报的通知,下文我们简称为urc
|
||||
--[[
|
||||
函数名:atimeout
|
||||
功能 :发送AT命令,命令超时没有应答的处理
|
||||
参数 :无
|
||||
返回值:无
|
||||
]]
|
||||
local function atimeout()
|
||||
--重启软件
|
||||
sys.restart("ril.atimeout_" .. (currcmd or ""))
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:defrsp
|
||||
功能 :AT命令的默认应答处理。如果没有定义某个AT的应答处理函数,则会走到本函数
|
||||
参数 :
|
||||
cmd:此应答对应的AT命令
|
||||
success:AT命令执行结果,true或者false
|
||||
response:AT命令的应答中的执行结果字符串
|
||||
intermediate:AT命令的应答中的中间信息
|
||||
返回值:无
|
||||
]]
|
||||
local function defrsp(cmd, success, response, intermediate)
|
||||
log.info("ril.defrsp", cmd, success, response, intermediate)
|
||||
end
|
||||
|
||||
--AT命令的应答处理表
|
||||
local rsptable = {}
|
||||
setmetatable(rsptable, {__index = function() return defrsp end})
|
||||
|
||||
--自定义的AT命令应答格式表,当AT命令应答为STRING格式时,用户可以进一步定义这里面的格式
|
||||
local formtab = {}
|
||||
|
||||
---注册某个AT命令应答的处理函数
|
||||
-- @param head 此应答对应的AT命令头,去掉了最前面的AT两个字符
|
||||
-- @param fnc AT命令应答的处理函数
|
||||
-- @param typ AT命令的应答类型,取值范围NORESULT,NUMBERIC,SLINE,MLINE,STRING,SPECIAL
|
||||
-- @param formt typ为STRING时,进一步定义STRING中的详细格式
|
||||
-- @return bool ,成功返回true,失败false
|
||||
-- @usage ril.regRsp("+CSQ", rsp)
|
||||
function regRsp(head, fnc, typ, formt)
|
||||
--没有定义应答类型
|
||||
if typ == nil then
|
||||
rsptable[head] = fnc
|
||||
return true
|
||||
end
|
||||
--定义了合法应答类型
|
||||
if typ == 0 or typ == 1 or typ == 2 or typ == 3 or typ == 4 or typ == 10 then
|
||||
--如果AT命令的应答类型已存在,并且与新设置的不一致
|
||||
if RILCMD[head] and RILCMD[head] ~= typ then
|
||||
return false
|
||||
end
|
||||
--保存
|
||||
RILCMD[head] = typ
|
||||
rsptable[head] = fnc
|
||||
formtab[head] = formt
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
local app_rilcb=nil
|
||||
--[[
|
||||
函数名:setrilcb
|
||||
功能 :AT命令的应答处理(含请求结果码和非请求结果码,返回到应用层)
|
||||
参数 :无
|
||||
返回值:无
|
||||
]]
|
||||
function setrilcb(cb)
|
||||
app_rilcb =cb
|
||||
end
|
||||
|
||||
|
||||
--[[
|
||||
函数名:rsp
|
||||
功能 :AT命令的应答处理
|
||||
参数 :无
|
||||
返回值:无
|
||||
]]
|
||||
local function rsp()
|
||||
--停止应答超时定时器
|
||||
sys.timerStopAll(atimeout)
|
||||
--如果发送AT命令时已经同步指定了应答处理函数
|
||||
if currsp then
|
||||
currsp(currcmd, result, respdata, interdata)
|
||||
--用户注册的应答处理函数表中找到处理函数
|
||||
else
|
||||
rsptable[cmdhead](currcmd, result, respdata, interdata)
|
||||
end
|
||||
--重置全局变量
|
||||
currcmd, currarg, currsp, curdelay, cmdhead, cmdtype, rspformt = nil
|
||||
result, interdata, respdata = nil
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:defurc
|
||||
功能 :urc的默认处理。如果没有定义某个urc的应答处理函数,则会走到本函数
|
||||
参数 :
|
||||
data:urc内容
|
||||
返回值:无
|
||||
]]
|
||||
local function defurc(data)
|
||||
log.info("ril.defurc", data)
|
||||
end
|
||||
|
||||
--urc的处理表
|
||||
local urctable = {}
|
||||
setmetatable(urctable, {__index = function() return defurc end})
|
||||
|
||||
--- 注册某个urc的处理函数
|
||||
-- @param prefix urc前缀,最前面的连续字符串,包含+、大写字符、数字的组合
|
||||
-- @param handler urc的处理函数
|
||||
-- @return 无
|
||||
-- @usage ril.regUrc("+CREG", neturc)
|
||||
function regUrc(prefix, handler)
|
||||
urctable[prefix] = handler
|
||||
end
|
||||
|
||||
--- 解注册某个urc的处理函数
|
||||
-- @param prefix urc前缀,最前面的连续字符串,包含+、大写字符、数字的组合
|
||||
-- @return 无
|
||||
-- @usage deRegUrc("+CREG")
|
||||
function deRegUrc(prefix)
|
||||
urctable[prefix] = nil
|
||||
end
|
||||
|
||||
--“数据过滤器”,虚拟串口收到的数据时,首先需要调用此函数过滤处理一下
|
||||
local urcfilter
|
||||
|
||||
--[[
|
||||
函数名:urc
|
||||
功能 :urc处理
|
||||
参数 :
|
||||
data:urc数据
|
||||
返回值:无
|
||||
]]
|
||||
local function urc(data)
|
||||
--AT通道准备就绪
|
||||
if data == "RDY" then
|
||||
radioready = true
|
||||
else
|
||||
local prefix = string.match(data, "([%+%^%*]*[%u%d& ]+)")
|
||||
--执行prefix的urc处理函数,返回数据过滤器
|
||||
urcfilter = urctable[prefix](data, prefix)
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:procatc
|
||||
功能 :处理虚拟串口收到的数据
|
||||
参数 :
|
||||
data:收到的数据
|
||||
返回值:无
|
||||
]]
|
||||
local function procatc(data)
|
||||
--if data:match("^%+EEMLTEINTER") or data:match("^%+EEMLTEINTRA") or data:match("^%+EEMUMTSINTER") or data:match("^%+EEMUMTSINTRA") then return end
|
||||
-- log.info("ril.proatc", data)
|
||||
--如果命令的应答是多行字符串格式
|
||||
if interdata and cmdtype == MLINE then
|
||||
--不出现OK\r\n,则认为应答还未结束
|
||||
if data ~= "OK\r\n" then
|
||||
--去掉最后的\r\n
|
||||
if string.find(data, "\r\n", -2) then
|
||||
data = string.sub(data, 1, -3)
|
||||
end
|
||||
--拼接到中间数据
|
||||
interdata = interdata .. "\r\n" .. data
|
||||
return
|
||||
end
|
||||
end
|
||||
--如果存在“数据过滤器”
|
||||
if urcfilter then
|
||||
data, urcfilter = urcfilter(data)
|
||||
end
|
||||
--去掉最后的\r\n
|
||||
if string.find(data, "\r\n", -2) then
|
||||
data = string.sub(data, 1, -3)
|
||||
end
|
||||
--数据为空
|
||||
if data == "" then
|
||||
return
|
||||
end
|
||||
|
||||
if data:match("^%+EEMLTEINTER") or data:match("^%+EEMLTEINTRA") or data:match("^%+EEMUMTSINTER") or data:match("^%+EEMUMTSINTRA") then
|
||||
|
||||
else
|
||||
log.info("ril.proatc", data)
|
||||
end
|
||||
|
||||
--当前无命令在执行则判定为urc
|
||||
if currcmd == nil then
|
||||
urc(data)
|
||||
return
|
||||
end
|
||||
|
||||
local isurc = false
|
||||
|
||||
--一些特殊的错误信息,转化为ERROR统一处理
|
||||
if data:match("^%+CMS ERROR:") or data:match("^%+CME ERROR:") then
|
||||
data = "ERROR"
|
||||
end
|
||||
--执行成功的应答
|
||||
if data == "OK" or data == "SHUT OK" then
|
||||
result = true
|
||||
respdata = data
|
||||
--执行失败的应答
|
||||
elseif data == "ERROR" or data == "NO ANSWER" or data == "NO DIALTONE" then
|
||||
result = false
|
||||
respdata = data
|
||||
--需要继续输入参数的AT命令应答
|
||||
elseif data == "> " then
|
||||
--发送短信
|
||||
if cmdhead == "+CMGS" then
|
||||
log.info("ril.procatc.send", currarg)
|
||||
vwrite(uart.ATC, currarg, "\026")
|
||||
else
|
||||
log.error("error promot cmd:", currcmd)
|
||||
end
|
||||
else
|
||||
--无类型
|
||||
if cmdtype == NORESULT then
|
||||
isurc = true
|
||||
--全数字类型
|
||||
elseif cmdtype == NUMBERIC then
|
||||
local numstr = data:match("(%x+)")
|
||||
if numstr == data then
|
||||
interdata = data
|
||||
else
|
||||
isurc = true
|
||||
end
|
||||
--字符串类型
|
||||
elseif cmdtype == STRING then
|
||||
--进一步检查格式
|
||||
if data:match(rspformt or "^.+$") and not data:match("^%+CPIN:") then
|
||||
interdata = data
|
||||
else
|
||||
isurc = true
|
||||
end
|
||||
elseif cmdtype == SLINE or cmdtype == MLINE then
|
||||
if interdata == nil and string.find(data, cmdhead) == 1 then
|
||||
interdata = data
|
||||
else
|
||||
isurc = true
|
||||
end
|
||||
--CGDATA 返回CONNECT或者ERROR
|
||||
elseif cmdhead == "+CGDATA" then
|
||||
if string.find(data, "CONNECT") == 1 then
|
||||
result = true
|
||||
respdata = data
|
||||
else
|
||||
isurc = true
|
||||
end
|
||||
else
|
||||
isurc = true
|
||||
end
|
||||
end
|
||||
--urc处理
|
||||
if isurc then
|
||||
urc(data)
|
||||
--应答处理
|
||||
elseif result ~= nil then
|
||||
rsp()
|
||||
end
|
||||
end
|
||||
|
||||
--是否在读取虚拟串口数据
|
||||
local readat = false
|
||||
|
||||
--[[
|
||||
函数名:getcmd
|
||||
功能 :解析一条AT命令
|
||||
参数 :
|
||||
item:AT命令
|
||||
返回值:当前AT命令的内容
|
||||
]]
|
||||
local function getcmd(item)
|
||||
local cmd, arg, rsp, delay
|
||||
--命令是string类型
|
||||
if type(item) == "string" then
|
||||
--命令内容
|
||||
cmd = item
|
||||
--命令是table类型
|
||||
elseif type(item) == "table" then
|
||||
--命令内容
|
||||
cmd = item.cmd
|
||||
--命令参数
|
||||
arg = item.arg
|
||||
--命令应答处理函数
|
||||
rsp = item.rsp
|
||||
--命令延时执行时间
|
||||
delay = item.delay
|
||||
else
|
||||
log.info("ril.getcmd", "getpack unknown item")
|
||||
return
|
||||
end
|
||||
--命令前缀
|
||||
local head = string.match(cmd, "AT([%+%*%^]*%u+)")
|
||||
|
||||
if head == nil then
|
||||
log.error("ril.getcmd", "request error cmd:", cmd)
|
||||
return
|
||||
end
|
||||
--这两个命令必须有参数
|
||||
if head == "+CMGS" or head == "+CIPSEND" then -- 必须有参数
|
||||
if arg == nil or arg == "" then
|
||||
log.error("ril.getcmd", "request error no arg", head)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
--赋值全局变量
|
||||
currcmd = cmd
|
||||
currarg = arg
|
||||
currsp = rsp
|
||||
curdelay = delay
|
||||
cmdhead = head
|
||||
cmdtype = RILCMD[head] or NORESULT
|
||||
rspformt = formtab[head]
|
||||
|
||||
return currcmd
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:sendat
|
||||
功能 :发送AT命令
|
||||
参数 :无
|
||||
返回值:无
|
||||
]]
|
||||
local function sendat()
|
||||
--AT通道未准备就绪、正在读取虚拟串口数据、有AT命令在执行或者队列无命令、正延时发送某条AT
|
||||
if not radioready or readat or currcmd ~= nil or delaying then
|
||||
return
|
||||
end
|
||||
|
||||
local item
|
||||
|
||||
while true do
|
||||
--队列无AT命令
|
||||
if #cmdqueue == 0 then
|
||||
return
|
||||
end
|
||||
--读取第一条命令
|
||||
item = table.remove(cmdqueue, 1)
|
||||
--解析命令
|
||||
getcmd(item)
|
||||
--需要延迟发送
|
||||
if curdelay then
|
||||
--启动延迟发送定时器
|
||||
sys.timerStart(delayfunc, curdelay)
|
||||
--清除全局变量
|
||||
currcmd, currarg, currsp, curdelay, cmdhead, cmdtype, rspformt = nil
|
||||
item.delay = nil
|
||||
--设置延迟发送标志
|
||||
delaying = true
|
||||
--把命令重新插入命令队列的队首
|
||||
table.insert(cmdqueue, 1, item)
|
||||
return
|
||||
end
|
||||
|
||||
if currcmd ~= nil then
|
||||
break
|
||||
end
|
||||
end
|
||||
--启动AT命令应答超时定时器
|
||||
sys.timerStart(atimeout, TIMEOUT)
|
||||
|
||||
log.info("ril.sendat", currcmd)
|
||||
--向虚拟串口中发送AT命令
|
||||
if currcmd:match("^AT%+POC=") then
|
||||
vwrite(uart.ATC, currcmd .. "\r\n")
|
||||
else
|
||||
vwrite(uart.ATC, currcmd .. "\r")
|
||||
end
|
||||
end
|
||||
|
||||
-- 延时执行某条AT命令的定时器回调
|
||||
-- @return 无
|
||||
-- @usage ril.delayfunc()
|
||||
function delayfunc()
|
||||
--清除延时标志
|
||||
delaying = nil
|
||||
--执行AT命令发送
|
||||
sendat()
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:atcreader
|
||||
功能 :“AT命令的虚拟串口数据接收消息”的处理函数,当虚拟串口收到数据时,会走到此函数中
|
||||
参数 :无
|
||||
返回值:无
|
||||
]]
|
||||
local function atcreader()
|
||||
local s
|
||||
|
||||
if not transparentmode then readat = true end
|
||||
--循环读取虚拟串口收到的数据
|
||||
while true do
|
||||
--每次读取一行
|
||||
s = vread(uart.ATC, "*l", 0)
|
||||
if string.len(s) ~= 0 then
|
||||
if transparentmode then
|
||||
--透传模式下直接转发数据
|
||||
rcvfunc(s)
|
||||
else
|
||||
--非透传模式下处理收到的数据
|
||||
procatc(s)
|
||||
|
||||
if app_rilcb ~=nil then app_rilcb(s) end
|
||||
end
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
if not transparentmode then
|
||||
readat = false
|
||||
--数据处理完以后继续执行AT命令发送
|
||||
sendat()
|
||||
end
|
||||
end
|
||||
|
||||
--- 发送AT命令到底层软件
|
||||
-- @param cmd AT命令内容
|
||||
-- @param arg AT命令参数,例如AT+CMGS=12命令执行后,接下来会发送此参数;AT+CIPSEND=14命令执行后,接下来会发送此参数
|
||||
-- @param onrsp AT命令应答的处理函数,只是当前发送的AT命令应答有效,处理之后就失效了
|
||||
-- @param delay 延时delay毫秒后,才发送此AT命令
|
||||
-- @return 无
|
||||
-- @usage ril.request("AT+CENG=1,1")
|
||||
-- @usage ril.request("AT+CRSM=214,28539,0,0,12,\"64f01064f03064f002fffff\"", nil, crsmResponse)
|
||||
function request(cmd, arg, onrsp, delay)
|
||||
if transparentmode then return end
|
||||
--插入缓冲队列
|
||||
if arg or onrsp or delay or formt then
|
||||
table.insert(cmdqueue, {cmd = cmd, arg = arg, rsp = onrsp, delay = delay})
|
||||
else
|
||||
table.insert(cmdqueue, cmd)
|
||||
end
|
||||
--执行AT命令发送
|
||||
sendat()
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:setransparentmode
|
||||
功能 :AT命令通道设置为透传模式
|
||||
参数 :
|
||||
fnc:透传模式下,虚拟串口数据接收的处理函数
|
||||
返回值:无
|
||||
注意:透传模式和非透传模式,只支持开机的第一次设置,不支持中途切换
|
||||
]]
|
||||
function setransparentmode(fnc)
|
||||
transparentmode, rcvfunc = true, fnc
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:sendtransparentdata
|
||||
功能 :透传模式下发送数据
|
||||
参数 :
|
||||
data:数据
|
||||
返回值:成功返回true,失败返回nil
|
||||
]]
|
||||
function sendtransparentdata(data)
|
||||
if not transparentmode then return end
|
||||
vwrite(uart.ATC, data)
|
||||
return true
|
||||
end
|
||||
|
||||
--注册“AT命令的虚拟串口数据接收消息”的处理函数
|
||||
uart.on(uart.ATC, "receive", atcreader)
|
||||
38
4G/源代码/lib/scanCode.lua
Normal file
38
4G/源代码/lib/scanCode.lua
Normal file
@@ -0,0 +1,38 @@
|
||||
--- 模块功能:扫码.
|
||||
-- 支持二维码、条形码扫描
|
||||
-- @module scanCode
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2018.9.19
|
||||
|
||||
require"sys"
|
||||
module(..., package.seeall)
|
||||
|
||||
local sCbFnc
|
||||
|
||||
--- 设置扫码请求
|
||||
-- @function cbFnc 扫码返回或者超时未返回的回调函数,回调函数的调用形式为:
|
||||
-- cbFnc(result,type,str)
|
||||
-- result:true或者false,true表示扫码成功,false表示超时失败
|
||||
-- type:string或者nil类型,result为true时,表示扫码类型;result为false时,为nil;支持QR-Code和CODE-128
|
||||
-- str:string或者nil类型,result为true时,表示扫码结果的字符串;result为false时,为nil
|
||||
-- @number[opt=10000] timeout 设置请求后,等待扫码结果返回的超时时间,单位毫秒,默认为10秒
|
||||
-- @usage
|
||||
-- scanCode.request(cbFnc)
|
||||
-- scanCode.request(cbFnc,5000)
|
||||
function request(cbFnc,timeout)
|
||||
sCbFnc = cbFnc
|
||||
sys.timerStart(sCbFnc,timeout or 10000,false)
|
||||
end
|
||||
|
||||
local function zbarMsg(msg)
|
||||
--log.info("scanCode.zbarMsg",msg.result,sys.timerIsActive(sCbFnc,false))
|
||||
if msg.result and sys.timerIsActive(sCbFnc,false) then
|
||||
sys.timerStop(sCbFnc,false)
|
||||
sCbFnc(true,msg.type,msg.data)
|
||||
end
|
||||
end
|
||||
|
||||
--注册core上报的rtos.MSG_ZBAR消息的处理函数
|
||||
rtos.on(rtos.MSG_ZBAR,zbarMsg)
|
||||
184
4G/源代码/lib/sim.lua
Normal file
184
4G/源代码/lib/sim.lua
Normal file
@@ -0,0 +1,184 @@
|
||||
--- 模块功能:查询sim卡状态、iccid、imsi、mcc、mnc
|
||||
-- @module sim
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2017.02.13
|
||||
require "ril"
|
||||
require "sys"
|
||||
module(..., package.seeall)
|
||||
|
||||
local req = ril.request
|
||||
--sim卡的imsi、sim卡的iccid
|
||||
local imsi, iccid, status
|
||||
local sNumber,bQueryNumber = ""
|
||||
local simCross,setSimCrossCbFnc
|
||||
|
||||
--- 获取sim卡的iccid
|
||||
-- @return string ,返回iccid,如果还没有读取出来,则返回nil
|
||||
-- @usage 注意:开机lua脚本运行之后,会发送at命令去查询iccid,所以需要一定时间才能获取到iccid。开机后立即调用此接口,基本上返回nil
|
||||
-- @usage sim.getIccid()
|
||||
function getIccid()
|
||||
return iccid
|
||||
end
|
||||
|
||||
--- 获取sim卡的imsi
|
||||
-- @return string ,返回imsi,如果还没有读取出来,则返回nil
|
||||
-- @usage 开机lua脚本运行之后,会发送at命令去查询imsi,所以需要一定时间才能获取到imsi。开机后立即调用此接口,基本上返回nil
|
||||
-- @usage sim.getImsi()
|
||||
function getImsi()
|
||||
return imsi
|
||||
end
|
||||
|
||||
--- 获取sim卡的mcc
|
||||
-- @return string ,返回值:mcc,如果还没有读取出来,则返回""
|
||||
-- @usage 注意:开机lua脚本运行之后,会发送at命令去查询imsi,所以需要一定时间才能获取到imsi。开机后立即调用此接口,基本上返回""
|
||||
-- @usage sim.getMcc()
|
||||
function getMcc()
|
||||
return (imsi ~= nil and imsi ~= "") and string.sub(imsi, 1, 3) or ""
|
||||
end
|
||||
|
||||
--- 获取sim卡的getmnc
|
||||
-- @return string ,返回mnc,如果还没有读取出来,则返回""
|
||||
-- @usage 注意:开机lua脚本运行之后,会发送at命令去查询imsi,所以需要一定时间才能获取到imsi。开机后立即调用此接口,基本上返回""
|
||||
-- @usage sim.getMnc()
|
||||
function getMnc()
|
||||
return (imsi ~= nil and imsi ~= "") and string.sub(imsi, 4, 5) or ""
|
||||
end
|
||||
|
||||
--- 获取sim卡的状态
|
||||
-- @return bool ,true表示sim卡正常,false或者nil表示未检测到卡或者卡异常
|
||||
-- @usage 开机lua脚本运行之后,会发送at命令去查询状态,所以需要一定时间才能获取到状态。开机后立即调用此接口,基本上返回nil
|
||||
-- @usage sim.getStatus()
|
||||
function getStatus()
|
||||
return status
|
||||
end
|
||||
|
||||
--- 设置“是否打开查询本机号码”的功能
|
||||
-- @bool flag 开启或者关闭查询功能的标志,false或者nil为关闭,其余为开启
|
||||
-- @return nil
|
||||
-- @usage sim.setQueryNumber(true)
|
||||
function setQueryNumber(flag)
|
||||
bQueryNumber = flag
|
||||
end
|
||||
|
||||
--- 获取sim卡的本机号码
|
||||
-- @return string ,返回值:sNumber,如果还没有读取出来或者读取失败,则返回""
|
||||
-- @usage 注意:开机lua脚本运行之后,会发送at命令去查询本机号码,所以需要一定时间才能获取到本机号码。开机后立即调用此接口,基本上返回""
|
||||
-- @usage 注意:此功能需要卡商支持,卡商必须把卡写到sim卡中,模块才能从卡中读出号码;目前市场上的很多卡,没有写入号码,是无法读取得
|
||||
-- @usage sim.getNumber()
|
||||
function getNumber()
|
||||
return sNumber or ""
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:rsp
|
||||
功能 :本功能模块内“通过虚拟串口发送到底层core软件的AT命令”的应答处理
|
||||
参数 :
|
||||
cmd:此应答对应的AT命令
|
||||
success:AT命令执行结果,true或者false
|
||||
response:AT命令的应答中的执行结果字符串
|
||||
intermediate:AT命令的应答中的中间信息
|
||||
返回值:无
|
||||
]]
|
||||
local function rsp(cmd, success, response, intermediate)
|
||||
if cmd == "AT+ICCID" then
|
||||
if intermediate then
|
||||
iccid = string.match(intermediate, "%+ICCID: (.+)")
|
||||
end
|
||||
elseif cmd == "AT+SIMCROSS?" then
|
||||
if success then
|
||||
simCross = tonumber(intermediate:match("%+SIMCROSS:%s*(%d)"))
|
||||
end
|
||||
if setSimCrossCbFnc then setSimCrossCbFnc(success) end
|
||||
elseif cmd:match("AT%+SIMCROSS=") then
|
||||
if success then
|
||||
req("AT+SIMCROSS?")
|
||||
else
|
||||
if setSimCrossCbFnc then setSimCrossCbFnc(false) end
|
||||
end
|
||||
elseif cmd == "AT+CIMI" then
|
||||
imsi = intermediate
|
||||
--产生一个内部消息IMSI_READY,通知已经读取imsi
|
||||
sys.publish("IMSI_READY")
|
||||
elseif cmd == "AT+CNUM" then
|
||||
if success then
|
||||
if intermediate then sNumber = intermediate:match("%+CNUM:%s*\".-\",\"[%+]*(%d+)\",") end
|
||||
else
|
||||
sys.timerStart(ril.request,5000,"AT+CNUM")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:urc
|
||||
-- 功能 :本功能模块内“注册的底层core通过虚拟串口主动上报的通知”的处理
|
||||
参数 :
|
||||
data:通知的完整字符串信息
|
||||
prefix:通知的前缀
|
||||
返回值:无
|
||||
]]
|
||||
local function urc(data, prefix)
|
||||
--sim卡状态通知
|
||||
if prefix == "+CPIN" then
|
||||
status = false
|
||||
--sim卡正常
|
||||
if data == "+CPIN: READY" then
|
||||
status = true
|
||||
ril.request("AT+ICCID")
|
||||
ril.request("AT+CIMI")
|
||||
if bQueryNumber then ril.request("AT+CNUM") end
|
||||
sys.publish("SIM_IND", "RDY")
|
||||
--未检测到sim卡
|
||||
elseif data == "+CPIN: NOT INSERTED" then
|
||||
sys.publish("SIM_IND", "NIST")
|
||||
else
|
||||
--sim卡pin开启
|
||||
if data == "+CPIN: SIM PIN" then
|
||||
sys.publish("SIM_IND","SIM_PIN")
|
||||
end
|
||||
sys.publish("SIM_IND", "NORDY")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function set2gSim()
|
||||
ril.request("AT+MEDCR=0,8,1")
|
||||
ril.request("AT+MEDCR=0,17,240")
|
||||
ril.request("AT+MEDCR=0,19,1")
|
||||
end
|
||||
|
||||
--- 设置双卡单待sim id
|
||||
-- @number id 双卡单待的simid,仅支持0和1
|
||||
-- @function[opt=nil] cbFnc 设置结果回调函数,回调函数的调用形式为:
|
||||
-- cnFnc(result),result为true表示成功,false或者nil为失败
|
||||
-- @return nil
|
||||
-- @usage
|
||||
-- sim.setId(0)
|
||||
-- sim.setId(1,cbFnc)
|
||||
function setId(id,cbFnc)
|
||||
if id ~= simCross then
|
||||
setSimCrossCbFnc = cbFnc
|
||||
ril.request("AT+SIMCROSS="..id)
|
||||
else
|
||||
if cbFnc then cbFnc(true) end
|
||||
end
|
||||
end
|
||||
|
||||
--- 获取目前设置的双卡单待id
|
||||
-- @return number ,返回id(0或者1),如果还没有读取出来,则返回nil
|
||||
-- @usage 注意:开机lua脚本运行之后,会发送at命令去查询id,所以需要一定时间才能获取到id。开机后立即调用此接口,基本上返回nil
|
||||
-- @usage sim.getId()
|
||||
function getId()
|
||||
return simCross
|
||||
end
|
||||
|
||||
--注册AT+CCID命令的应答处理函数
|
||||
ril.regRsp("+ICCID", rsp)
|
||||
--注册AT+CIMI命令的应答处理函数
|
||||
ril.regRsp("+CIMI", rsp)
|
||||
ril.regRsp("+CNUM", rsp)
|
||||
ril.regRsp("+SIMCROSS", rsp)
|
||||
--注册+CPIN通知的处理函数
|
||||
ril.regUrc("+CPIN", urc)
|
||||
ril.request("AT+SIMCROSS?")
|
||||
713
4G/源代码/lib/sms.lua
Normal file
713
4G/源代码/lib/sms.lua
Normal file
@@ -0,0 +1,713 @@
|
||||
--- 模块功能:短信功能
|
||||
-- @module sms
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2018.03.25
|
||||
require "sys"
|
||||
require "ril"
|
||||
require "common"
|
||||
require "utils"
|
||||
module(..., package.seeall)
|
||||
|
||||
local publish = sys.publish
|
||||
local req = ril.request
|
||||
|
||||
--ready:底层短信功能是否准备就绪
|
||||
local smsReady, isn, tlongsms, netReady, cpinReady,smsUrcReady = false, 255, {}
|
||||
local ssub, slen, sformat, smatch = string.sub, string.len, string.format, string.match
|
||||
local tsend = {}
|
||||
|
||||
--[[
|
||||
函数名:numtobcdnum
|
||||
功能 :号码ASCII字符串 转化为 BCD编码格式字符串,仅支持数字和+,例如"+8618126324567" -> 91688121364265f7 (表示第1个字节是0x91,第2个字节为0x68,......)
|
||||
参数 :
|
||||
num:待转换字符串
|
||||
返回值:转换后的字符串
|
||||
]]
|
||||
local function numtobcdnum(num)
|
||||
local len, numfix, convnum = slen(num), "81", ""
|
||||
|
||||
if ssub(num, 1, 1) == "+" then
|
||||
numfix = "91"
|
||||
len = len - 1
|
||||
num = ssub(num, 2, -1)
|
||||
end
|
||||
|
||||
if len % 2 ~= 0 then --奇数位
|
||||
for i = 1, (len - (len % 2)) / 2 do
|
||||
convnum = convnum .. ssub(num, i * 2, i * 2) .. ssub(num, i * 2 - 1, i * 2 - 1)
|
||||
end
|
||||
convnum = convnum .. "F" .. ssub(num, len, len)
|
||||
else --偶数位
|
||||
for i = 1, (len - (len % 2)) / 2 do
|
||||
convnum = convnum .. ssub(num, i * 2, i * 2) .. ssub(num, i * 2 - 1, i * 2 - 1)
|
||||
end
|
||||
end
|
||||
|
||||
return numfix .. convnum
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:bcdnumtonum
|
||||
功能 :BCD编码格式字符串 转化为 号码ASCII字符串,仅支持数字和+,例如91688121364265f7 (表示第1个字节是0x91,第2个字节为0x68,......) -> "+8618126324567"
|
||||
参数 :
|
||||
num:待转换字符串
|
||||
返回值:转换后的字符串
|
||||
]]
|
||||
local function bcdnumtonum(num)
|
||||
local len, numfix, convnum = slen(num), "", ""
|
||||
|
||||
if len % 2 ~= 0 then
|
||||
print("your bcdnum is err " .. num)
|
||||
return
|
||||
end
|
||||
|
||||
if ssub(num, 1, 2) == "91" then
|
||||
numfix = "+"
|
||||
end
|
||||
|
||||
len, num = len - 2, ssub(num, 3, -1)
|
||||
|
||||
for i = 1, (len - (len % 2)) / 2 do
|
||||
convnum = convnum .. ssub(num, i * 2, i * 2) .. ssub(num, i * 2 - 1, i * 2 - 1)
|
||||
end
|
||||
|
||||
if ssub(convnum, len, len) == "f" or ssub(convnum, len, len) == "F" then
|
||||
convnum = ssub(convnum, 1, -2)
|
||||
end
|
||||
|
||||
return numfix .. convnum
|
||||
end
|
||||
|
||||
|
||||
--[[
|
||||
函数名:_send
|
||||
功能 :发送短信(内部接口)
|
||||
参数 :num,号码
|
||||
data:短信内容
|
||||
istp:发送方式选择
|
||||
返回值:true:发送成功,false发送失败
|
||||
]]
|
||||
local function _send(num, data,istp)
|
||||
|
||||
if istp == "TEXT" or istp == "text" then
|
||||
req("AT+CSCS=\"IRA\"")
|
||||
-- req("AT+CSMP?")
|
||||
-- req("AT+CSCS?")
|
||||
req("AT+CMGF=1")
|
||||
table.insert(tsend, {sval = 1, rval = 0, flg = true})--sval发送的包数,rval收到的包数
|
||||
req(string.format("%s%s", "AT+CMGS=",num), string.format("%s",data))
|
||||
req('AT+CSCS="UCS2"')
|
||||
req("AT+CMGF=0")
|
||||
return
|
||||
end
|
||||
|
||||
local numlen, datalen, pducnt, pdu, pdulen, udhi = sformat("%02X", slen(num)), slen(data) / 2, 1, "", "", ""
|
||||
if not smsReady or not netReady then return false end
|
||||
|
||||
--如果发送的数据大于140字节则为长短信
|
||||
if datalen > 140 then
|
||||
--计算出长短信拆分后的总条数,长短信的每包的数据实际只有134个实际要发送的短信内容,数据的前6字节为协议头
|
||||
pducnt = sformat("%d", (datalen + 133) / 134)
|
||||
pducnt = tonumber(pducnt)
|
||||
--分配一个序列号,范围为0-255
|
||||
isn = isn == 255 and 0 or isn + 1
|
||||
end
|
||||
|
||||
table.insert(tsend, {sval = pducnt, rval = 0, flg = true})--sval发送的包数,rval收到的包数
|
||||
|
||||
if ssub(num, 1, 1) == "+" then
|
||||
numlen = sformat("%02X", slen(num) - 1)
|
||||
end
|
||||
|
||||
for i = 1, pducnt do
|
||||
--如果是长短信
|
||||
if pducnt > 1 then
|
||||
local len_mul
|
||||
len_mul = (i == pducnt and sformat("%02X", datalen - (pducnt - 1) * 134 + 6) or "8C")
|
||||
--udhi:6位协议头格式
|
||||
udhi = "050003" .. sformat("%02X", isn) .. sformat("%02X", pducnt) .. sformat("%02X", i)
|
||||
log.info(datalen, udhi)
|
||||
pdu = "005110" .. numlen .. numtobcdnum(num) .. "000800" .. len_mul .. udhi .. ssub(data, (i - 1) * 134 * 2 + 1, i * 134 * 2)
|
||||
--发送短短信
|
||||
else
|
||||
datalen = sformat("%02X", datalen)
|
||||
pdu = "001110" .. numlen .. numtobcdnum(num) .. "000800" .. datalen .. data
|
||||
end
|
||||
pdulen = slen(pdu) / 2 - 1
|
||||
req(sformat("%s%s", "AT+CMGS=", pdulen), pdu)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:read
|
||||
功能 :读短信
|
||||
参数 :pos短信位置
|
||||
返回值:true:读成功,false读失败
|
||||
]]
|
||||
function read(pos)
|
||||
if not smsReady or pos == nil or pos == 0 then return false end
|
||||
|
||||
req("AT+CMGR=" .. pos)
|
||||
return true
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:delete
|
||||
功能 :删除短信
|
||||
参数 :pos短信位置
|
||||
返回值:true:删除成功,false删除失败
|
||||
]]
|
||||
function delete(pos)
|
||||
if not smsReady or pos == nil or pos == 0 then return false end
|
||||
req("AT+CMGD=" .. pos)
|
||||
return true
|
||||
end
|
||||
|
||||
Charmap = {[0] = 0x40, 0xa3, 0x24, 0xa5, 0xe8, 0xE9, 0xF9, 0xEC, 0xF2, 0xC7, 0x0A, 0xD8, 0xF8, 0x0D, 0xC5, 0xE5
|
||||
, 0x0394, 0x5F, 0x03A6, 0x0393, 0x039B, 0x03A9, 0x03A0, 0x03A8, 0x03A3, 0x0398, 0x039E, 0x1B, 0xC6, 0xE5, 0xDF, 0xA9
|
||||
, 0x20, 0x21, 0x22, 0x23, 0xA4, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F
|
||||
, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F
|
||||
, 0xA1, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F
|
||||
, 0X50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0xC4, 0xD6, 0xD1, 0xDC, 0xA7
|
||||
, 0xBF, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F
|
||||
, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0xE4, 0xF6, 0xF1, 0xFC, 0xE0}
|
||||
|
||||
Charmapctl = {[10] = 0x0C, [20] = 0x5E, [40] = 0x7B, [41] = 0x7D, [47] = 0x5C, [60] = 0x5B, [61] = 0x7E
|
||||
, [62] = 0x5D, [64] = 0x7C, [101] = 0xA4}
|
||||
|
||||
--[[
|
||||
函数名:gsm7bitdecode
|
||||
功能 :7位编码, 在PDU模式中,当使用7位编码时,最多可发160个字符
|
||||
参数 :data
|
||||
longsms
|
||||
返回值:
|
||||
]]
|
||||
function gsm7bitdecode(data, longsms)
|
||||
local ucsdata, lpcnt, tmpdata, resdata, nbyte, nleft, ucslen, olddat = "", slen(data) / 2, 0, 0, 0, 0, 0
|
||||
|
||||
if longsms then
|
||||
tmpdata = tonumber("0x" .. ssub(data, 1, 2))
|
||||
resdata = bit.rshift(tmpdata, 1)
|
||||
if olddat == 27 then
|
||||
if Charmapctl[resdata] then --特殊字符
|
||||
olddat, resdata = resdata, Charmapctl[resdata]
|
||||
ucsdata = ssub(ucsdata, 1, -5)
|
||||
else
|
||||
olddat, resdata = resdata, Charmap[resdata]
|
||||
end
|
||||
else
|
||||
olddat, resdata = resdata, Charmap[resdata]
|
||||
end
|
||||
ucsdata = ucsdata .. sformat("%04X", resdata)
|
||||
else
|
||||
tmpdata = tonumber("0x" .. ssub(data, 1, 2))
|
||||
resdata = bit.band(bit.bor(bit.lshift(tmpdata, nbyte), nleft), 0x7f)
|
||||
if olddat == 27 then
|
||||
if Charmapctl[resdata] then --特殊字符
|
||||
olddat, resdata = resdata, Charmapctl[resdata]
|
||||
ucsdata = ssub(ucsdata, 1, -5)
|
||||
else
|
||||
olddat, resdata = resdata, Charmap[resdata]
|
||||
end
|
||||
else
|
||||
olddat, resdata = resdata, Charmap[resdata]
|
||||
end
|
||||
ucsdata = ucsdata .. sformat("%04X", resdata)
|
||||
|
||||
nleft = bit.rshift(tmpdata, 7 - nbyte)
|
||||
nbyte = nbyte + 1
|
||||
ucslen = ucslen + 1
|
||||
end
|
||||
|
||||
for i = 2, lpcnt do
|
||||
tmpdata = tonumber("0x" .. ssub(data, (i - 1) * 2 + 1, i * 2))
|
||||
if tmpdata == nil then break end
|
||||
resdata = bit.band(bit.bor(bit.lshift(tmpdata, nbyte), nleft), 0x7f)
|
||||
if olddat == 27 then
|
||||
if Charmapctl[resdata] then --特殊字符
|
||||
olddat, resdata = resdata, Charmapctl[resdata]
|
||||
ucsdata = ssub(ucsdata, 1, -5)
|
||||
else
|
||||
olddat, resdata = resdata, Charmap[resdata]
|
||||
end
|
||||
else
|
||||
olddat, resdata = resdata, Charmap[resdata]
|
||||
end
|
||||
ucsdata = ucsdata .. sformat("%04X", resdata)
|
||||
|
||||
nleft = bit.rshift(tmpdata, 7 - nbyte)
|
||||
nbyte = nbyte + 1
|
||||
ucslen = ucslen + 1
|
||||
|
||||
if nbyte == 7 then
|
||||
if olddat == 27 then
|
||||
if Charmapctl[nleft] then --特殊字符
|
||||
olddat, nleft = nleft, Charmapctl[nleft]
|
||||
ucsdata = ssub(ucsdata, 1, -5)
|
||||
else
|
||||
olddat, nleft = nleft, Charmap[nleft]
|
||||
end
|
||||
else
|
||||
olddat, nleft = nleft, Charmap[nleft]
|
||||
end
|
||||
ucsdata = ucsdata .. sformat("%04X", nleft)
|
||||
nbyte, nleft = 0, 0
|
||||
ucslen = ucslen + 1
|
||||
end
|
||||
end
|
||||
|
||||
return ucsdata, ucslen
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:gsm8bitdecode
|
||||
功能 :8位编码
|
||||
参数 :data
|
||||
longsms
|
||||
返回值:
|
||||
]]
|
||||
function gsm8bitdecode(data)
|
||||
local ucsdata, lpcnt = "", slen(data) / 2
|
||||
|
||||
for i = 1, lpcnt do
|
||||
ucsdata = ucsdata .. "00" .. ssub(data, (i - 1) * 2 + 1, i * 2)
|
||||
end
|
||||
|
||||
return ucsdata, lpcnt
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:rsp
|
||||
功能 :AT应答
|
||||
参数 :cmd,success,response,intermediate
|
||||
返回值:无
|
||||
]]
|
||||
local function rsp(cmd, success, response, intermediate)
|
||||
local prefix = smatch(cmd, "AT(%+%u+)")
|
||||
log.info("lib_sms rsp", prefix, cmd, success, response, intermediate)
|
||||
|
||||
--读短信成功
|
||||
if prefix == "+CMGR" then
|
||||
if not success then publish("SMS_READ_CNF") return end
|
||||
local convnum, t, stat, alpha, len, pdu, data, longsms, total, isn, idx = "", ""
|
||||
if intermediate then
|
||||
stat, alpha, len, pdu = smatch(intermediate, "+CMGR:%s*(%d),(.*),%s*(%d+)\r\n(%x+)")
|
||||
len = tonumber(len)--PDU数据长度,不包括短信息中心号码
|
||||
end
|
||||
|
||||
--收到的PDU包不为空则解析PDU包
|
||||
if pdu and pdu ~= "" then
|
||||
local offset, addlen, addnum, flag, dcs, tz, txtlen, fo = 5
|
||||
pdu = ssub(pdu, (slen(pdu) / 2 - len) * 2 + 1, -1)--PDU数据,不包括短信息中心号码
|
||||
fo = tonumber("0x" .. ssub(pdu, 1, 1))--PDU短信首字节的高4位,第6位为数据报头标志位
|
||||
if bit.band(fo, 0x4) ~= 0 then
|
||||
longsms = true
|
||||
end
|
||||
addlen = tonumber(sformat("%d", "0x" .. ssub(pdu, 3, 4)))--回复地址数字个数
|
||||
|
||||
addlen = addlen % 2 == 0 and addlen + 2 or addlen + 3 --加上号码类型2位(5,6)or 加上号码类型2位(5,6)和1位F
|
||||
|
||||
offset = offset + addlen
|
||||
|
||||
addnum = ssub(pdu, 5, 5 + addlen - 1)
|
||||
convnum = bcdnumtonum(addnum)
|
||||
|
||||
flag = tonumber(sformat("%d", "0x" .. ssub(pdu, offset, offset + 1)))--协议标识 (TP-PID)
|
||||
offset = offset + 2
|
||||
dcs = tonumber(sformat("%d", "0x" .. ssub(pdu, offset, offset + 1)))--用户信息编码方式 Dcs=8,表示短信存放的格式为UCS2编码
|
||||
offset = offset + 2
|
||||
tz = ssub(pdu, offset, offset + 13)--时区7个字节
|
||||
offset = offset + 14
|
||||
txtlen = tonumber(sformat("%d", "0x" .. ssub(pdu, offset, offset + 1)))--短信文本长度
|
||||
offset = offset + 2
|
||||
data = ssub(pdu, offset, offset + txtlen * 2 - 1)--短信文本
|
||||
if longsms then
|
||||
if tonumber("0x" .. ssub(data, 5, 6)) == 3 then
|
||||
isn, total, idx = tonumber("0x" .. ssub(data, 7, 8)), tonumber("0x" .. ssub(data, 9, 10)), tonumber("0x" .. ssub(data, 11, 12))
|
||||
data = ssub(data, 13, -1)--去掉报头6个字节
|
||||
elseif tonumber("0x" .. ssub(data, 5, 6)) == 4 then
|
||||
isn, total, idx = tonumber("0x" .. ssub(data, 7, 10)), tonumber("0x" .. ssub(data, 11, 12)), tonumber("0x" .. ssub(data, 13, 14))
|
||||
data = ssub(data, 15, -1)--去掉报头7个字节
|
||||
end
|
||||
end
|
||||
|
||||
log.info("TP-PID : ", flag, "dcs: ", dcs, "tz: ", tz, "data: ", data, "txtlen", txtlen)
|
||||
|
||||
if dcs == 0x00 then --7bit encode
|
||||
local newlen
|
||||
data, newlen = gsm7bitdecode(data, longsms)
|
||||
if newlen > txtlen then
|
||||
data = ssub(data, 1, txtlen * 4)
|
||||
end
|
||||
log.info("7bit to ucs2 data: ", data, "txtlen", txtlen, "newlen", newlen)
|
||||
elseif dcs == 0x04 then --8bit encode
|
||||
data, txtlen = gsm8bitdecode(data)
|
||||
log.info("8bit to ucs2 data: ", data, "txtlen", txtlen)
|
||||
end
|
||||
|
||||
for i = 1, 7 do
|
||||
t = t .. ssub(tz, i * 2, i * 2) .. ssub(tz, i * 2 - 1, i * 2 - 1)
|
||||
|
||||
if i <= 3 then
|
||||
t = i < 3 and (t .. "/") or (t .. ",")
|
||||
elseif i <= 6 then
|
||||
t = i < 6 and (t .. ":") or (t .. "+")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local pos = smatch(cmd, "AT%+CMGR=(%d+)")
|
||||
data = data or ""
|
||||
alpha = alpha or ""
|
||||
publish("SMS_READ_CNF", success, convnum, data, pos, t, alpha, total, idx, isn)
|
||||
elseif prefix == "+CMGD" then
|
||||
publish("SMS_DELETE_CNF", success)
|
||||
elseif prefix == "+CMGS" then
|
||||
--如果是短短信,直接发送短信确认消息
|
||||
if tsend[1].sval == 1 then --{sval=pducnt,rval=0,flg=true}
|
||||
table.remove(tsend, 1)
|
||||
publish("SMS_SEND_CNF", success)
|
||||
--如果是长短信,所有cmgs之后,才抛出SMS_SEND_CNF,所有cmgs都成功,才true,其余都是false
|
||||
else
|
||||
tsend[1].rval = tsend[1].rval + 1
|
||||
--只要其中有发送失败的短信,则整个长短信将标记为发送失败
|
||||
if not success then tsend[1].flg = false end
|
||||
if tsend[1].sval == tsend[1].rval then
|
||||
publish("SMS_SEND_CNF", tsend[1].flg)
|
||||
table.remove(tsend, 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function init()
|
||||
if cpinReady and smsUrcReady and netReady then
|
||||
--使用PDU模式发送
|
||||
req("AT+CMGF=0")
|
||||
--设置短信TEXT 模式参数
|
||||
-- req("AT+CSMP=17,167,0,8")
|
||||
--设置AT命令的字符编码是UCS2
|
||||
req('AT+CSCS="UCS2"')
|
||||
--设置存储区为SM
|
||||
req('AT+CPMS="SM"')
|
||||
-- 上报消息CMTI
|
||||
req("AT+CNMI=2,1,0,0,0")
|
||||
--分发短信准备好消息
|
||||
publish("SMS_READY")
|
||||
--清空已读短信
|
||||
req("AT+CMGD=1,3")
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:urc
|
||||
功能 :主动上报消息处理函数
|
||||
参数 :data,prefix
|
||||
返回值:无
|
||||
]]
|
||||
local function urc(data, prefix)
|
||||
--短信准备好
|
||||
if data == "SMS READY" then
|
||||
smsUrcReady = true
|
||||
init()
|
||||
-- 存储短信
|
||||
elseif prefix == "+CMTI" then
|
||||
--分发收到新短信消息
|
||||
publish("SMS_NEW_MSG_IND", smatch(data, "(%d+)", slen(prefix) + 1))
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:getsmsstate
|
||||
功能 :获取短消息是否准备好的状态
|
||||
参数 :无
|
||||
返回值:true准备好,其他值:未准备好
|
||||
]]
|
||||
function getsmsstate()
|
||||
return smsReady
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:mergelongsms
|
||||
功能 :合并长短信
|
||||
参数 :
|
||||
tag:长短信标记(ISN和总条数的拼接字符串)
|
||||
返回值:无
|
||||
]]
|
||||
local function mergelongsms(tag)
|
||||
local data = ""
|
||||
--按表中的顺序,一次拼接短消息内容
|
||||
for i = 1, tlongsms[tag]["total"] do
|
||||
data = data .. (tlongsms[tag]["dat"][i] or "")
|
||||
end
|
||||
--分发长短信合并确认消息
|
||||
publish("LONG_SMS_MERGR_CNF", true, tlongsms[tag]["num"], data, tlongsms[tag]["t"], tlongsms[tag]["nam"])
|
||||
log.info("mergelongsms", "num:", tlongsms[tag]["num"], "data", data)
|
||||
tlongsms[tag]["dat"], tlongsms[tag] = nil
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:longsmsind
|
||||
功能 :长短信被拆解后的消息包上报
|
||||
参数 :id,num, data,datetime,name,total,idx,isn
|
||||
返回值:无
|
||||
]]
|
||||
local function longsmsind(num, data, datetime, name, total, idx, isn)
|
||||
log.info("longsmsind", "isn", isn, "total:", total, "idx:", idx, "data", data)
|
||||
|
||||
if tlongsms[isn .. total] then
|
||||
if not tlongsms[isn .. total]["dat"] then tlongsms[isn .. total]["dat"] = {} end
|
||||
tlongsms[isn .. total]["dat"][idx] = data
|
||||
else
|
||||
tlongsms[isn .. total] = {}
|
||||
tlongsms[isn .. total]["total"], tlongsms[isn .. total]["num"], tlongsms[isn .. total]["t"], tlongsms[isn .. total]["nam"] = total, num, datetime, name
|
||||
tlongsms[isn .. total]["dat"] = {}
|
||||
tlongsms[isn .. total]["dat"][idx] = data
|
||||
end
|
||||
|
||||
local totalrcv = 0
|
||||
for i = 1, tlongsms[isn .. total]["total"] do
|
||||
if tlongsms[isn .. total]["dat"][i] then totalrcv = totalrcv + 1 end
|
||||
end
|
||||
--长短信接收完整
|
||||
if tlongsms[isn .. total]["total"] == totalrcv then
|
||||
sys.timerStop(mergelongsms, isn .. total)
|
||||
mergelongsms(isn .. total)
|
||||
else
|
||||
--如果2分钟后长短信还没收完整,2分钟后将自动合并已收到的长短信
|
||||
sys.timerStart(mergelongsms, 120000, isn .. total)
|
||||
end
|
||||
end
|
||||
|
||||
--注册长短信合并处理函数
|
||||
sys.subscribe("LONG_SMS_MERGE", longsmsind)
|
||||
|
||||
ril.regUrc("SMS READY", urc)
|
||||
ril.regUrc("+CMT", urc)
|
||||
ril.regUrc("+CMTI", urc)
|
||||
|
||||
ril.regRsp("+CMGR", rsp)
|
||||
ril.regRsp("+CMGD", rsp)
|
||||
ril.regRsp("+CMGS", rsp)
|
||||
|
||||
|
||||
--短信发送缓冲表最大个数
|
||||
local SMS_SEND_BUF_MAX_CNT = 10
|
||||
--短信发送间隔,单位毫秒
|
||||
local SMS_SEND_INTERVAL = 3000
|
||||
--短信发送缓冲表
|
||||
local tsmsnd = {}
|
||||
|
||||
--[[
|
||||
函数名:sndnxt
|
||||
功能 :发送短信发送缓冲表中的第一条短信
|
||||
参数 :无
|
||||
返回值:无
|
||||
]]
|
||||
local function sndnxt()
|
||||
if #tsmsnd > 0 then
|
||||
_send(tsmsnd[1].num, tsmsnd[1].data)
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:sendcnf
|
||||
功能 :SMS_SEND_CNF消息的处理函数,异步通知短信发送结果
|
||||
参数 :
|
||||
result:短信发送结果,true为成功,false或者nil为失败
|
||||
返回值:无
|
||||
]]
|
||||
local function sendcnf(result)
|
||||
log.info("sendcnf", result)
|
||||
local num, data, cb = tsmsnd[1].num, tsmsnd[1].data, tsmsnd[1].cb
|
||||
--从短信发送缓冲表中移除当前短信
|
||||
table.remove(tsmsnd, 1)
|
||||
--如果有发送回调函数,执行回调
|
||||
if cb then cb(result, num, data) end
|
||||
--如果短信发送缓冲表中还有短信,则SMS_SEND_INTERVAL毫秒后,继续发送下条短信
|
||||
if #tsmsnd > 0 then sys.timerStart(sndnxt, SMS_SEND_INTERVAL) end
|
||||
end
|
||||
|
||||
--- 发送短信
|
||||
-- @string num 短信接收方号码,ASCII码字符串格式
|
||||
-- @string data 短信内容,GB2312编码的字符串
|
||||
-- 如果短信内容中只有ascii可见字符,则超过160个字符时,会被拆分为几条长级联短信进行发送
|
||||
-- 如果短信内容中包含除ascii可见字符外的其他字符,例如包含汉字,一个汉字算作一个字符,一个ascii可见字符也算作一个字符,超过70个字符时,会被拆分为几条长级联短信进行发送
|
||||
-- @function[opt=nil] cbFnc 短信发送结果异步返回时的用户回调函数,回调函数的调用形式为:
|
||||
-- cbFnc(result,num,data)
|
||||
-- num:短信接收方的号码,ASCII码字符串格式
|
||||
-- data:短信内容,unicode大端编码的HEX字符串
|
||||
-- @number[opt=nil] idx 插入短信发送缓冲表的位置,默认是插入末尾
|
||||
-- @string[opt=nil] istp 表示发送方式选择 "TEXT" or "text" 表示 text文本方式发送,nil或其它参数表示pdu方式发送,补充pdu方式采用 USC2 编码方式发送
|
||||
-- @return result,true表示调用接口成功(并不是短信发送成功,短信发送结果,通过sendcnf返回,如果有cbFnc,会通知cbFnc函数);返回false,表示调用接口失败
|
||||
-- @usage sms.send("10086","test",cbFnc,istp)
|
||||
function send(num, data, cbFnc, idx,istp)
|
||||
--号码或者内容非法
|
||||
if not num or num == "" or not data or data == "" then if cbFnc then cbFnc(false, num, data) end return end
|
||||
--短信发送缓冲表已满
|
||||
if #tsmsnd >= SMS_SEND_BUF_MAX_CNT then if cbFnc then cbFnc(false, num, data) end return end
|
||||
|
||||
if istp == "TEXT" or istp == "text" then
|
||||
|
||||
--如果指定了插入位置
|
||||
if idx then
|
||||
table.insert(tsmsnd, idx, {num = num, data = data, cb = cbFnc})
|
||||
--没有指定插入位置,插入到末尾
|
||||
else
|
||||
table.insert(tsmsnd, {num = num, data = data, cb = cbFnc})
|
||||
end
|
||||
|
||||
--如果短信发送缓冲表中只有一条短信,立即触发短信发送动作
|
||||
if #tsmsnd == 1 then _send(num, data,istp) return true end
|
||||
return
|
||||
end
|
||||
|
||||
local dat = string.toHex(common.gb2312ToUcs2be(data))
|
||||
--如果指定了插入位置
|
||||
if idx then
|
||||
table.insert(tsmsnd, idx, {num = num, data = dat, cb = cbFnc})
|
||||
--没有指定插入位置,插入到末尾
|
||||
else
|
||||
table.insert(tsmsnd, {num = num, data = dat, cb = cbFnc})
|
||||
end
|
||||
--如果短信发送缓冲表中只有一条短信,立即触发短信发送动作
|
||||
if #tsmsnd == 1 then _send(num, dat) return true end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
--短信接收位置表
|
||||
local tnewsms = {}
|
||||
|
||||
--[[
|
||||
函数名:readsms
|
||||
功能 :读取短信接收位置表中的第一条短信
|
||||
参数 :无
|
||||
返回值:无
|
||||
]]
|
||||
local function readsms()
|
||||
if #tnewsms ~= 0 then
|
||||
read(tnewsms[1])
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:newsms
|
||||
功能 :SMS_NEW_MSG_IND(未读短信或者新短信主动上报的消息)消息的处理函数
|
||||
参数 :
|
||||
pos:短信存储位置
|
||||
返回值:无
|
||||
]]
|
||||
local function newsms(pos)
|
||||
--存储位置插入到短信接收位置表中
|
||||
table.insert(tnewsms, pos)
|
||||
--如果只有一条短信,则立即读取
|
||||
if #tnewsms == 1 then
|
||||
readsms()
|
||||
end
|
||||
end
|
||||
|
||||
--新短信的用户处理函数
|
||||
local newsmscb
|
||||
|
||||
--- 设置新短信的用户处理函数
|
||||
-- @function cbFnc 新短信的用户处理函数
|
||||
-- @return nil
|
||||
-- @usage sms.setNewSmsCb(cbFnc)
|
||||
function setNewSmsCb(cbFnc)
|
||||
newsmscb = cbFnc
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:readcnf
|
||||
功能 :SMS_READ_CNF消息的处理函数,异步返回读取的短信内容
|
||||
参数 :
|
||||
result:短信读取结果,true为成功,false或者nil为失败
|
||||
num:短信号码,ASCII码字符串格式
|
||||
data:短信内容,UCS2大端格式的16进制字符串
|
||||
pos:短信的存储位置,暂时没用
|
||||
datetime:短信日期和时间,ASCII码字符串格式
|
||||
name:短信号码对应的联系人姓名,暂时没用
|
||||
返回值:无
|
||||
]]
|
||||
local function readcnf(result, num, data, pos, datetime, name, total, idx, isn)
|
||||
if result then
|
||||
--过滤号码中的86和+86
|
||||
local d1, d2 = string.find(num, "^([%+]*86)")
|
||||
if d1 and d2 then
|
||||
num = string.sub(num, d2 + 1, -1)
|
||||
end
|
||||
--删除短信
|
||||
delete(tnewsms[1])
|
||||
--从短信接收位置表中删除此短信的位置
|
||||
table.remove(tnewsms, 1)
|
||||
if total and total > 1 then
|
||||
publish("LONG_SMS_MERGE", num, data, datetime, name, total, idx, isn)
|
||||
readsms()--读取下一条新短信
|
||||
return
|
||||
end
|
||||
if data then
|
||||
--短信内容转换为GB2312字符串格式
|
||||
data = common.ucs2beToGb2312(data:fromHex())
|
||||
--用户应用程序处理短信
|
||||
if newsmscb then newsmscb(num, data, datetime) end
|
||||
end
|
||||
--继续读取下一条短信
|
||||
readsms()
|
||||
else
|
||||
--删除短信
|
||||
delete(tnewsms[1])
|
||||
--从短信接收位置表中删除此短信的位置
|
||||
table.remove(tnewsms, 1)
|
||||
--继续读取下一条短信
|
||||
readsms()
|
||||
end
|
||||
end
|
||||
|
||||
local function longsmsmergecnf(res, num, data, datetime)
|
||||
--log.info("longsmsmergecnf",num,data,datetime)
|
||||
if data then
|
||||
--短信内容转换为GB2312字符串格式
|
||||
data = common.ucs2beToGb2312(data:fromHex())
|
||||
--用户应用程序处理短信
|
||||
if newsmscb then newsmscb(num, data, datetime) end
|
||||
end
|
||||
end
|
||||
|
||||
sys.subscribe("SMS_NEW_MSG_IND", newsms)
|
||||
sys.subscribe("SMS_READ_CNF", readcnf)
|
||||
sys.subscribe("SMS_SEND_CNF", sendcnf)
|
||||
sys.subscribe("SMS_READY",
|
||||
function()
|
||||
if not smsReady then
|
||||
smsReady = true
|
||||
if netReady then sndnxt() end
|
||||
end
|
||||
end
|
||||
)
|
||||
sys.subscribe("NET_STATE_REGISTERED",
|
||||
function()
|
||||
if not netReady then
|
||||
netReady = true
|
||||
init()
|
||||
if smsReady then sndnxt() end
|
||||
end
|
||||
end
|
||||
)
|
||||
sys.subscribe("SIM_IND",
|
||||
function(para)
|
||||
if para=="RDY" and not cpinReady then
|
||||
cpinReady = true
|
||||
init()
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
sys.subscribe("LONG_SMS_MERGR_CNF", longsmsmergecnf)
|
||||
|
||||
-- 此处为临时AT+CNMI补丁
|
||||
sys.timerStart(req, 30000, "AT+CNMI=2,1,0,0,0")
|
||||
121
4G/源代码/lib/socket.lua
Normal file
121
4G/源代码/lib/socket.lua
Normal file
@@ -0,0 +1,121 @@
|
||||
--- 模块功能:数据链路激活、SOCKET管理(创建、连接、数据收发、状态维护)
|
||||
-- @module socket
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2017.9.25
|
||||
require "socket4G"
|
||||
module(..., package.seeall)
|
||||
|
||||
--- SOCKET 是否有可用
|
||||
-- @return 可用true,不可用false
|
||||
socket.isReady = link.isReady
|
||||
local tSocketModule = nil
|
||||
local function init()
|
||||
tSocketModule = tSocketModule or {
|
||||
[link.CELLULAR] = socket4G,
|
||||
[link.CH395] = socketCh395,
|
||||
[link.W5500] = socketW5500,
|
||||
[link.ESP8266] = socketESP8266
|
||||
}
|
||||
end
|
||||
--- 创建基于TCP的socket对象
|
||||
-- @bool[opt=nil] ssl 是否为ssl连接,true表示是,其余表示否
|
||||
-- @table[opt=nil] cert ssl连接需要的证书配置,只有ssl参数为true时,此参数才有意义,cert格式如下:
|
||||
-- {
|
||||
-- caCert = "ca.crt", --CA证书文件(Base64编码 X.509格式),如果存在此参数,则表示客户端会对服务器的证书进行校验;不存在则不校验
|
||||
-- clientCert = "client.crt", --客户端证书文件(Base64编码 X.509格式),服务器对客户端的证书进行校验时会用到此参数
|
||||
-- clientKey = "client.key", --客户端私钥文件(Base64编码 X.509格式)
|
||||
-- clientPassword = "123456", --客户端证书文件密码[可选]
|
||||
-- }
|
||||
-- @table[opt=nil] tCoreExtPara 建立链接扩展参数,4G链接和ch395链接所需扩展参数不一样
|
||||
-- @bool[ipv6=nil] ipv6 是否为ipv6连接,true表示是,其余表示否
|
||||
-- @return client,创建成功返回socket客户端对象;创建失败返回nil
|
||||
-- @usage
|
||||
-- c = socket.tcp()
|
||||
-- c = socket.tcp(true)
|
||||
-- c = socket.tcp(true, {caCert="ca.crt"})
|
||||
-- c = socket.tcp(true, {caCert="ca.crt", clientCert="client.crt", clientKey="client.key"})
|
||||
-- c = socket.tcp(true, {caCert="ca.crt", clientCert="client.crt", clientKey="client.key", clientPassword="123456"})
|
||||
|
||||
function tcp(ssl, cert, tCoreExtPara, ipv6)
|
||||
init()
|
||||
return tSocketModule[link.getNetwork()].tcp(ssl, cert, tCoreExtPara, ipv6)
|
||||
end
|
||||
--- 创建基于UDP的socket对象
|
||||
-- @bool[ipv6=nil] ipv6 是否为ipv6连接,true表示是,其余表示否
|
||||
-- @return client,创建成功返回socket客户端对象;创建失败返回nil
|
||||
-- @usage c = socket.udp()
|
||||
function udp(ipv6)
|
||||
init()
|
||||
return tSocketModule[link.getNetwork()].udp(ipv6)
|
||||
end
|
||||
--- 设置TCP层自动重传的参数
|
||||
-- @number[opt=4] retryCnt 重传次数;取值范围0到12
|
||||
-- @number[opt=16] retryMaxTimeout 限制每次重传允许的最大超时时间(单位秒),取值范围1到16
|
||||
-- @return nil
|
||||
-- @usage
|
||||
-- setTcpResendPara(3,8)
|
||||
-- setTcpResendPara(4,16)
|
||||
function setTcpResendPara(retryCnt, retryMaxTimeout)
|
||||
init()
|
||||
return tSocketModule[link.getNetwork()].setTcpResendPara(retryCnt, retryMaxTimeout)
|
||||
end
|
||||
|
||||
--- 设置域名解析参数
|
||||
-- 注意:0027以及之后的core版本才支持此功能
|
||||
-- @number[opt=4] retryCnt 重传次数;取值范围1到8
|
||||
-- @number[opt=4] retryTimeoutMulti 重传超时时间倍数,取值范围1到5
|
||||
-- 第n次重传超时时间的计算方式为:第n次的重传超时基数*retryTimeoutMulti,单位为秒
|
||||
-- 重传超时基数表为{1, 1, 2, 4, 4, 4, 4, 4}
|
||||
-- 第1次重传超时时间为:1*retryTimeoutMulti 秒
|
||||
-- 第2次重传超时时间为:1*retryTimeoutMulti 秒
|
||||
-- 第3次重传超时时间为:2*retryTimeoutMulti 秒
|
||||
-- ...........................................
|
||||
-- 第8次重传超时时间为:8*retryTimeoutMulti 秒
|
||||
-- @return nil
|
||||
-- @usage
|
||||
-- socket.setDnsParsePara(8,5)
|
||||
function setDnsParsePara(retryCnt, retryTimeoutMulti)
|
||||
init()
|
||||
return tSocketModule[link.getNetwork()].setDnsParsePara(retryCnt, retryTimeoutMulti)
|
||||
end
|
||||
|
||||
--- 打印所有socket的状态
|
||||
-- @return 无
|
||||
-- @usage socket.printStatus()
|
||||
function printStatus()
|
||||
init()
|
||||
return tSocketModule[link.getNetwork()].printStatus()
|
||||
end
|
||||
|
||||
--- 设置数据传输后,允许进入休眠状态的延时时长
|
||||
-- 3024版本以及之后的版本才支持此功能
|
||||
-- 此功能设置的参数,设置成功后,掉电会自动保存
|
||||
-- @number tm 数据传输后,允许进入休眠状态的延时时长,单位为秒,取值范围1到20
|
||||
-- 注意:此时间越短,允许进入休眠状态越快,功耗越低;但是在某些网络环境中,此时间越短,可能会造成数据传输不稳定
|
||||
-- 建议在可以接受的功耗范围内,此值设置的越大越好
|
||||
-- 如果没有设置此参数,此延时时长是和基站的配置有关,一般来说是10秒左右
|
||||
-- @return nil
|
||||
-- @usage
|
||||
-- socket.setLowPower(5)
|
||||
function setLowPower(tm)
|
||||
init()
|
||||
return tSocketModule[link.getNetwork()].setLowPower(tm)
|
||||
end
|
||||
|
||||
|
||||
--- 设置IP数据流量统计上报间隔
|
||||
-- 此功能设置的参数,设置成功后,掉电不会保存,每次开机,应用脚本需要重新设置
|
||||
-- @number interval IP数据流量统计上报间隔,单位为秒,建议取值不要小于60秒;0表示关闭此功能,默认为0
|
||||
-- @return nil
|
||||
-- @usage
|
||||
-- socket.setIpStatis(60)
|
||||
-- 每隔60秒会通过sys.publish("LIB_IP_STATIS_RPT", dataFlow)
|
||||
-- 应用脚本通过sys.subscribe("LIB_IP_STATIS_RPT", function(dataFlow)
|
||||
-- --自行处理interval间隔内,新增的数据流量dataFlow,单位字节
|
||||
-- end)
|
||||
function setIpStatis(interval)
|
||||
init()
|
||||
return tSocketModule[link.getNetwork()].setIpStatis(interval or 0)
|
||||
end
|
||||
680
4G/源代码/lib/socket4G.lua
Normal file
680
4G/源代码/lib/socket4G.lua
Normal file
@@ -0,0 +1,680 @@
|
||||
-- 模块功能:数据链路激活、socket4G管理(创建、连接、数据收发、状态维护)
|
||||
-- @module socket4G
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2017.9.25
|
||||
require "link"
|
||||
require "utils"
|
||||
module(..., package.seeall)
|
||||
|
||||
local sockets = {}
|
||||
-- 单次发送数据最大值
|
||||
local SENDSIZE = 11200
|
||||
-- 缓冲区最大下标
|
||||
local INDEX_MAX = 256
|
||||
-- 是否有socket正处在链接
|
||||
local socketsConnected = 0
|
||||
|
||||
-- ip流量统计间隔,每个统计间隔内新增ip流量
|
||||
local ipStatisInterval, ipDataFlow = 0,0
|
||||
|
||||
local function ipDataFlowAdd(flow)
|
||||
if ipStatisInterval~=0 then
|
||||
ipDataFlow = ipDataFlow+flow
|
||||
end
|
||||
end
|
||||
|
||||
-- SOCKET 是否有可用
|
||||
-- @return 可用true,不可用false
|
||||
-- socket4G.isReady = link.isReady
|
||||
|
||||
local function errorInd(error)
|
||||
local coSuspended = {}
|
||||
|
||||
for _, c in pairs(sockets) do -- IP状态出错时,通知所有已连接的socket
|
||||
c.error = error
|
||||
--不能打开如下3行代码,IP出错时,会通知每个socket,socket会主动close
|
||||
--如果设置了connected=false,则主动close时,直接退出,不会执行close动作,导致core中的socket资源没释放
|
||||
--会引发core中socket耗尽以及socket id重复的问题
|
||||
--c.connected = false
|
||||
--socketsConnected = c.connected or socketsConnected
|
||||
--if error == 'CLOSED' then sys.publish("SOCKET_ACTIVE", socketsConnected) end
|
||||
if c.co and coroutine.status(c.co) == "suspended" then
|
||||
--coroutine.resume(c.co, false)
|
||||
table.insert(coSuspended, c.co)
|
||||
end
|
||||
end
|
||||
|
||||
for k, v in pairs(coSuspended) do
|
||||
if v and coroutine.status(v) == "suspended" then
|
||||
coroutine.resume(v, false, error)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sys.subscribe("IP_ERROR_IND", function()errorInd('IP_ERROR_IND') end)
|
||||
--sys.subscribe('IP_SHUT_IND', function()errorInd('CLOSED') end)
|
||||
-- 创建socket函数
|
||||
local mt = {}
|
||||
mt.__index = mt
|
||||
local function socket(protocol, cert, tCoreExtPara,ipv6)
|
||||
local ssl = protocol:match("SSL")
|
||||
local co = coroutine.running()
|
||||
if not co then
|
||||
log.warn("socket.socket: socket must be called in coroutine")
|
||||
return nil
|
||||
end
|
||||
-- 实例的属性参数表
|
||||
local o = {
|
||||
id = nil,
|
||||
protocol = protocol,
|
||||
tCoreExtPara = tCoreExtPara,
|
||||
ssl = ssl,
|
||||
cert = cert,
|
||||
co = co,
|
||||
input = {},
|
||||
output = {},
|
||||
wait = "",
|
||||
connected = false,
|
||||
iSubscribe = false,
|
||||
subMessage = nil,
|
||||
isBlock = false,
|
||||
msg = nil,
|
||||
rcvProcFnc = nil,
|
||||
ipv6 = ipv6,
|
||||
}
|
||||
return setmetatable(o, mt)
|
||||
end
|
||||
|
||||
--- 创建基于TCP的socket对象
|
||||
-- @bool[opt=nil] ssl 是否为ssl连接,true表示是,其余表示否
|
||||
-- @table[opt=nil] cert ssl连接需要的证书配置,只有ssl参数为true时,此参数才有意义,cert格式如下:
|
||||
-- {
|
||||
-- caCert = "ca.crt", --CA证书文件(Base64编码 X.509格式),如果存在此参数,则表示客户端会对服务器的证书进行校验;不存在则不校验
|
||||
-- clientCert = "client.crt", --客户端证书文件(Base64编码 X.509格式),服务器对客户端的证书进行校验时会用到此参数
|
||||
-- clientKey = "client.key", --客户端私钥文件(Base64编码 X.509格式)
|
||||
-- clientPassword = "123456", --客户端证书文件密码[可选]
|
||||
-- insist = 1, --证书中的域名校验失败时,是否坚持连接,默认为1,坚持连接,0为不连接
|
||||
-- hostNameFlag = 0, --服务器域名是否上报,默认为0,不上报,1为上报
|
||||
-- }
|
||||
-- @number[opt=nil] tCoreExtPara 建立链接扩展参数
|
||||
-- @bool[ipv6=nil] ipv6 是否为ipv6连接,true表示是,其余表示否
|
||||
-- {
|
||||
-- rcvBufferSize = "num" --接收缓冲区大小,默认为0
|
||||
-- }
|
||||
-- @return client,创建成功返回socket客户端对象;创建失败返回nil
|
||||
-- @usage
|
||||
-- c = socket4G.tcp()
|
||||
-- c = socket4G.tcp(true)
|
||||
-- c = socket4G.tcp(true, {caCert="ca.crt"})
|
||||
-- c = socket4G.tcp(true, {caCert="ca.crt", clientCert="client.crt", clientKey="client.key"})
|
||||
-- c = socket4G.tcp(true, {caCert="ca.crt", clientCert="client.crt", clientKey="client.key", clientPassword="123456"})
|
||||
function tcp(ssl, cert, tCoreExtPara, ipv6)
|
||||
return socket("TCP" .. (ssl == true and "SSL" or ""), (ssl == true) and cert or nil, tCoreExtPara, ipv6)
|
||||
end
|
||||
|
||||
--- 创建基于UDP的socket对象
|
||||
-- @bool[ipv6=nil] ipv6 是否为ipv6连接,true表示是,其余表示否
|
||||
-- @return client,创建成功返回socket客户端对象;创建失败返回nil
|
||||
-- @usage c = socket4G.udp()
|
||||
function udp(ipv6)
|
||||
return socket("UDP", nil, nil, ipv6)
|
||||
end
|
||||
|
||||
--- 连接服务器
|
||||
-- @string address 服务器地址,支持ip和域名
|
||||
-- @param port string或者number类型,服务器端口
|
||||
-- @number[opt=120] timeout 可选参数,连接超时时间,单位秒
|
||||
-- @return bool result true - 成功,false - 失败
|
||||
-- @return string ,id '0' -- '8' ,返回通道ID编号
|
||||
-- @usage
|
||||
-- socketClient = socket4G.tcp()
|
||||
-- socketClient:connect("www.baidu.com","80")
|
||||
function mt:connect(address, port, timeout)
|
||||
assert(self.co == coroutine.running(), "socket:connect: coroutine mismatch")
|
||||
|
||||
if not link.isReady() then
|
||||
log.info("socket.connect: ip not ready")
|
||||
return false
|
||||
end
|
||||
|
||||
if self.protocol=="TCP" then
|
||||
ipDataFlowAdd(120)
|
||||
elseif self.protocol=="TCPSSL" then
|
||||
ipDataFlowAdd(800)
|
||||
end
|
||||
|
||||
self.address = address
|
||||
self.port = port
|
||||
local tCoreExtPara = self.tCoreExtPara or {}
|
||||
-- 默认缓冲区大小
|
||||
local rcvBufferSize = tCoreExtPara.rcvBufferSize or 0
|
||||
local socket_connect_fnc = nil
|
||||
if self.ipv6 == true then
|
||||
socket_connect_fnc = socketcore.ipv6_conn
|
||||
else
|
||||
socket_connect_fnc = (type(socketcore.sock_conn_ext)=="function") and socketcore.sock_conn_ext or socketcore.sock_conn
|
||||
end
|
||||
if self.protocol == 'TCP' then
|
||||
self.id = socket_connect_fnc(0, address, port, rcvBufferSize)
|
||||
elseif self.protocol == 'TCPSSL' then
|
||||
local cert = {hostName = address}
|
||||
local insist = 1
|
||||
local hostNameFlag = 0
|
||||
if self.cert then
|
||||
if self.cert.caCert then
|
||||
if self.cert.caCert:sub(1, 1) ~= "/" then self.cert.caCert = "/lua/" .. self.cert.caCert end
|
||||
cert.caCert = io.readFile(self.cert.caCert)
|
||||
end
|
||||
if self.cert.clientCert then
|
||||
if self.cert.clientCert:sub(1, 1) ~= "/" then self.cert.clientCert = "/lua/" .. self.cert.clientCert end
|
||||
cert.clientCert = io.readFile(self.cert.clientCert)
|
||||
end
|
||||
if self.cert.clientKey then
|
||||
if self.cert.clientKey:sub(1, 1) ~= "/" then self.cert.clientKey = "/lua/" .. self.cert.clientKey end
|
||||
cert.clientKey = io.readFile(self.cert.clientKey)
|
||||
end
|
||||
insist = self.cert.insist == 0 and 0 or 1
|
||||
hostNameFlag = self.cert.hostNameFlag == 1 and 1 or 0
|
||||
end
|
||||
self.id = socket_connect_fnc(2, address, port, cert, rcvBufferSize, insist, nil, hostNameFlag)
|
||||
else
|
||||
self.id = socket_connect_fnc(1, address, port, rcvBufferSize)
|
||||
end
|
||||
if self.ipv6 ~= true and type(socketcore.sock_conn_ext)=="function" then
|
||||
if not self.id or self.id<0 then
|
||||
if self.id==-2 then
|
||||
require "http"
|
||||
--请求腾讯云免费HttpDns解析
|
||||
http.request("GET", "119.29.29.29/d?dn=" .. address, nil, nil, nil, 40000,
|
||||
function(result, statusCode, head, body)
|
||||
log.info("socket.httpDnsCb", result, statusCode, head, body)
|
||||
sys.publish("SOCKET_HTTPDNS_RESULT_"..address.."_"..port, result, statusCode, head, body)
|
||||
end)
|
||||
local _, result, statusCode, head, body = sys.waitUntil("SOCKET_HTTPDNS_RESULT_"..address.."_"..port)
|
||||
|
||||
--DNS解析成功
|
||||
if result and statusCode == "200" and body and body:match("^[%d%.]+") then
|
||||
return self:connect(body:match("^([%d%.]+)"),port,timeout)
|
||||
end
|
||||
end
|
||||
self.id = nil
|
||||
end
|
||||
end
|
||||
if not self.id then
|
||||
log.info("socket:connect: core sock conn error", self.protocol, address, port, self.cert)
|
||||
return false
|
||||
end
|
||||
log.info("socket:connect-coreid,prot,addr,port,cert,timeout", self.id, self.protocol, address, port, self.cert, timeout or 120)
|
||||
sockets[self.id] = self
|
||||
self.wait = "SOCKET_CONNECT"
|
||||
self.timerId = sys.timerStart(coroutine.resume, (timeout or 120) * 1000, self.co, false, "TIMEOUT")
|
||||
local result, reason = coroutine.yield()
|
||||
if self.timerId and reason ~= "TIMEOUT" then sys.timerStop(self.timerId) end
|
||||
if not result then
|
||||
log.info("socket:connect: connect fail", reason)
|
||||
if reason == "RESPONSE" then
|
||||
sockets[self.id] = nil
|
||||
self.id = nil
|
||||
end
|
||||
sys.publish("LIB_SOCKET_CONNECT_FAIL_IND", self.ssl, self.protocol, address, port)
|
||||
return false
|
||||
end
|
||||
log.info("socket:connect: connect ok")
|
||||
|
||||
if not self.connected then
|
||||
self.connected = true
|
||||
socketsConnected = socketsConnected+1
|
||||
sys.publish("SOCKET_ACTIVE", socketsConnected>0)
|
||||
end
|
||||
|
||||
return true, self.id
|
||||
end
|
||||
|
||||
--- 异步发送数据
|
||||
-- @number[opt=nil] keepAlive 服务器和客户端最大通信间隔时间,也叫心跳包最大时间,单位秒
|
||||
-- @string[opt=nil] pingreq 心跳包的字符串
|
||||
-- @return boole,false 失败,true 表示成功
|
||||
-- @usage
|
||||
-- socketClient = socket4G.tcp()
|
||||
-- socketClient:connect("www.baidu.com","80")
|
||||
-- while socketClient:asyncSelect() do end
|
||||
function mt:asyncSelect(keepAlive, pingreq)
|
||||
assert(self.co == coroutine.running(), "socket:asyncSelect: coroutine mismatch")
|
||||
if self.error then
|
||||
log.warn('socket.client:asyncSelect', 'error', self.error)
|
||||
return false
|
||||
end
|
||||
|
||||
self.wait = "SOCKET_SEND"
|
||||
local dataLen = 0
|
||||
--log.info("socket.asyncSelect #self.output",#self.output)
|
||||
while #self.output ~= 0 do
|
||||
local data = table.concat(self.output)
|
||||
dataLen = string.len(data)
|
||||
self.output = {}
|
||||
local sendSize = self.protocol == "UDP" and 1472 or SENDSIZE
|
||||
for i = 1, dataLen, sendSize do
|
||||
-- 按最大MTU单元对data分包
|
||||
if self.ipv6 == true then
|
||||
socketcore.ipv6_send(self.id, data:sub(i, i + sendSize - 1))
|
||||
else
|
||||
socketcore.sock_send(self.id, data:sub(i, i + sendSize - 1))
|
||||
end
|
||||
ipDataFlowAdd((data:sub(i, i + sendSize - 1)):len()+(self.protocol == "UDP" and 40 or 80))
|
||||
if self.timeout then
|
||||
self.timerId = sys.timerStart(coroutine.resume, self.timeout * 1000, self.co, false, "TIMEOUT")
|
||||
end
|
||||
--log.info("socket.asyncSelect self.timeout",self.timeout)
|
||||
local result, reason = coroutine.yield()
|
||||
if self.timerId and reason ~= "TIMEOUT" then sys.timerStop(self.timerId) end
|
||||
sys.publish("SOCKET_ASYNC_SEND", result)
|
||||
if not result then
|
||||
sys.publish("LIB_SOCKET_SEND_FAIL_IND", self.ssl, self.protocol, self.address, self.port)
|
||||
--log.warn('socket.asyncSelect', 'send error')
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
self.wait = "SOCKET_WAIT"
|
||||
--log.info("socket.asyncSelect",dataLen,self.id)
|
||||
if dataLen>0 then sys.publish("SOCKET_SEND", self.id, true) end
|
||||
if keepAlive and keepAlive ~= 0 then
|
||||
if type(pingreq) == "function" then
|
||||
sys.timerStart(pingreq, keepAlive * 1000)
|
||||
else
|
||||
sys.timerStart(self.asyncSend, keepAlive * 1000, self, pingreq or "\0")
|
||||
end
|
||||
end
|
||||
return coroutine.yield()
|
||||
end
|
||||
|
||||
function mt:getAsyncSend()
|
||||
if self.error then return 0 end
|
||||
return #(self.output)
|
||||
end
|
||||
--- 异步缓存待发送的数据
|
||||
-- @string data 数据
|
||||
-- @number[opt=nil] timeout 可选参数,发送超时时间,单位秒;为nil时表示不支持timeout
|
||||
-- @return result true - 成功,false - 失败
|
||||
-- @usage
|
||||
-- socketClient = socket4G.tcp()
|
||||
-- socketClient:connect("www.baidu.com","80")
|
||||
-- socketClient:asyncSend("12345678")
|
||||
function mt:asyncSend(data, timeout)
|
||||
if self.error then
|
||||
log.warn('socket.client:asyncSend', 'error', self.error)
|
||||
return false
|
||||
end
|
||||
self.timeout = timeout
|
||||
table.insert(self.output, data or "")
|
||||
--log.info("socket.asyncSend",self.wait)
|
||||
if self.wait == "SOCKET_WAIT" then coroutine.resume(self.co, true) end
|
||||
return true
|
||||
end
|
||||
--- 异步接收数据
|
||||
-- @return data 表示接收到的数据(如果是UDP,返回最新的一包数据;如果是TCP,返回所有收到的数据)
|
||||
-- ""表示未收到数据
|
||||
-- @usage
|
||||
-- socketClient = socket4G.tcp()
|
||||
-- socketClient:connect("www.baidu.com","80")
|
||||
-- data = socketClient:asyncRecv()
|
||||
function mt:asyncRecv()
|
||||
if #self.input == 0 then return "" end
|
||||
if self.protocol == "UDP" then
|
||||
return table.remove(self.input)
|
||||
else
|
||||
local s = table.concat(self.input)
|
||||
self.input = {}
|
||||
if self.ipv6 == true then
|
||||
if self.isBlock then table.insert(self.input, socketcore.ipv6_recv(self.msg.socket_index, self.msg.recv_len)) end
|
||||
else
|
||||
if self.isBlock then table.insert(self.input, socketcore.sock_recv(self.msg.socket_index, self.msg.recv_len)) end
|
||||
end
|
||||
return s
|
||||
end
|
||||
end
|
||||
|
||||
--- 同步发送数据
|
||||
-- @string data 数据
|
||||
-- 此处传入的数据长度和剩余可用内存有关,只要内存够用,可以随便传入数据
|
||||
-- 虽然说此处的数据长度没有特别限制,但是调用core中的socket发送接口时,每次最多发送11200字节的数据
|
||||
-- 例如此处传入的data长度是112000字节,则在这个send接口中,会循环10次,每次发送11200字节的数据
|
||||
-- @number[opt=120] timeout 可选参数,发送超时时间,单位秒
|
||||
-- @return result true - 成功,false - 失败
|
||||
-- @usage
|
||||
-- socketClient = socket4G.tcp()
|
||||
-- socketClient:connect("www.baidu.com","80")
|
||||
-- socketClient:send("12345678")
|
||||
function mt:send(data, timeout)
|
||||
assert(self.co == coroutine.running(), "socket:send: coroutine mismatch")
|
||||
if self.error then
|
||||
log.warn('socket.client:send', 'error', self.error)
|
||||
return false
|
||||
end
|
||||
log.debug("socket.send", "total " .. string.len(data or "") .. " bytes", "first 30 bytes", (data or ""):sub(1, 30))
|
||||
local sendSize = self.protocol == "UDP" and 1472 or SENDSIZE
|
||||
for i = 1, string.len(data or ""), sendSize do
|
||||
-- 按最大MTU单元对data分包
|
||||
self.wait = "SOCKET_SEND"
|
||||
if self.ipv6 == true then
|
||||
socketcore.ipv6_send(self.id, data:sub(i, i + sendSize - 1))
|
||||
else
|
||||
socketcore.sock_send(self.id, data:sub(i, i + sendSize - 1))
|
||||
end
|
||||
ipDataFlowAdd((data:sub(i, i + sendSize - 1)):len()+(self.protocol == "UDP" and 40 or 80))
|
||||
self.timerId = sys.timerStart(coroutine.resume, (timeout or 120) * 1000, self.co, false, "TIMEOUT")
|
||||
local result, reason = coroutine.yield()
|
||||
if self.timerId and reason ~= "TIMEOUT" then sys.timerStop(self.timerId) end
|
||||
if not result then
|
||||
log.info("socket:send", "send fail", reason)
|
||||
sys.publish("LIB_SOCKET_SEND_FAIL_IND", self.ssl, self.protocol, self.address, self.port)
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- 同步接收数据
|
||||
-- @number[opt=0] timeout 可选参数,接收超时时间,单位毫秒
|
||||
-- @string[opt=nil] msg 可选参数,控制socket所在的线程退出recv阻塞状态
|
||||
-- @bool[opt=nil] msgNoResume 可选参数,控制socket所在的线程退出recv阻塞状态
|
||||
-- false或者nil表示“在recv阻塞状态,收到msg消息,可以退出阻塞状态”,true表示不退出
|
||||
-- 此参数仅lib内部使用,应用脚本不要使用此参数
|
||||
-- @return result 数据接收结果
|
||||
-- true表示成功(接收到了数据)
|
||||
-- false表示失败(没有接收到数据)
|
||||
-- @return data
|
||||
-- 如果result为true,data表示接收到的数据(如果是UDP,返回最新的一包数据;如果是TCP,返回所有收到的数据)
|
||||
-- 如果result为false,超时失败,data为"timeout"
|
||||
-- 如果result为false,msg控制退出,data为msg的字符串
|
||||
-- 如果result为false,socket连接被动断开控制退出,data为"CLOSED"
|
||||
-- 如果result为false,PDP断开连接控制退出,data为"IP_ERROR_IND"
|
||||
-- @return param 如果是msg控制退出,param的值是msg的参数
|
||||
-- @usage
|
||||
-- socketClient = socket4G.tcp()
|
||||
-- socketClient:connect("www.baidu.com","80")
|
||||
-- result,data = socketClient:recv(60000,"APP_SOCKET_SEND_DATA")
|
||||
function mt:recv(timeout, msg, msgNoResume)
|
||||
assert(self.co == coroutine.running(), "socket:recv: coroutine mismatch")
|
||||
if self.error then
|
||||
log.warn('socket.client:recv', 'error', self.error)
|
||||
return false
|
||||
end
|
||||
self.msgNoResume = msgNoResume
|
||||
if msg and not self.iSubscribe then
|
||||
self.iSubscribe = msg
|
||||
self.subMessage = function(data)
|
||||
--if data then table.insert(self.output, data) end
|
||||
if self.wait == "+RECEIVE" and not self.msgNoResume then
|
||||
if data then table.insert(self.output, data) end
|
||||
coroutine.resume(self.co, 0xAA)
|
||||
end
|
||||
end
|
||||
sys.subscribe(msg, self.subMessage)
|
||||
end
|
||||
if msg and #self.output > 0 then sys.publish(msg, false) end
|
||||
if #self.input == 0 then
|
||||
self.wait = "+RECEIVE"
|
||||
if timeout and timeout > 0 then
|
||||
local r, s = sys.wait(timeout)
|
||||
if r == nil then
|
||||
return false, "timeout"
|
||||
elseif r == 0xAA then
|
||||
local dat = table.concat(self.output)
|
||||
self.output = {}
|
||||
return false, msg, dat
|
||||
else
|
||||
return r, s
|
||||
end
|
||||
else
|
||||
local r, s = coroutine.yield()
|
||||
if r == 0xAA then
|
||||
local dat = table.concat(self.output)
|
||||
self.output = {}
|
||||
return false, msg, dat
|
||||
else
|
||||
return r, s
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if self.protocol == "UDP" then
|
||||
local s = table.remove(self.input)
|
||||
return true, s
|
||||
else
|
||||
log.warn("-------------------使用缓冲区---------------")
|
||||
local s = table.concat(self.input)
|
||||
self.input = {}
|
||||
if self.ipv6 == true then
|
||||
if self.isBlock then table.insert(self.input, socketcore.ipv6_recv(self.msg.socket_index, self.msg.recv_len)) end
|
||||
else
|
||||
if self.isBlock then table.insert(self.input, socketcore.sock_recv(self.msg.socket_index, self.msg.recv_len)) end
|
||||
end
|
||||
return true, s
|
||||
end
|
||||
end
|
||||
|
||||
--- 主动关闭并且销毁一个socket
|
||||
-- @return nil
|
||||
-- @usage
|
||||
-- socketClient = socket4G.tcp()
|
||||
-- socketClient:connect("www.baidu.com","80")
|
||||
-- socketClient:close()
|
||||
function mt:close()
|
||||
assert(self.co == coroutine.running(), "socket:close: coroutine mismatch")
|
||||
if self.iSubscribe then
|
||||
sys.unsubscribe(self.iSubscribe, self.subMessage)
|
||||
self.iSubscribe = false
|
||||
end
|
||||
--此处不要再判断状态,否则在连接超时失败时,conneted状态仍然是未连接,会导致无法close
|
||||
--if self.connected then
|
||||
log.info("socket:sock_close", self.id)
|
||||
local result, reason
|
||||
|
||||
if self.id then
|
||||
if self.ipv6 == true then
|
||||
socketcore.ipv6_close(self.id)
|
||||
else
|
||||
socketcore.sock_close(self.id)
|
||||
end
|
||||
if self.protocol~="UDP" then ipDataFlowAdd(120) end
|
||||
self.wait = "SOCKET_CLOSE"
|
||||
while true do
|
||||
result, reason = coroutine.yield()
|
||||
if reason == "RESPONSE" then break end
|
||||
end
|
||||
end
|
||||
if self.connected then
|
||||
self.connected = false
|
||||
if socketsConnected>0 then
|
||||
socketsConnected = socketsConnected-1
|
||||
end
|
||||
sys.publish("SOCKET_ACTIVE", socketsConnected>0)
|
||||
end
|
||||
if self.input then
|
||||
self.input = {}
|
||||
end
|
||||
--end
|
||||
if self.id ~= nil then
|
||||
sockets[self.id] = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- socket接收自定义控制处理
|
||||
-- @function[opt=nil] rcvCbFnc,socket接收到数据后,执行的回调函数,回调函数的调用形式为:
|
||||
-- rcvCbFnc(readFnc,socketIndex,rcvDataLen)
|
||||
-- rcvCbFnc内部,会判断是否读取数据,如果读取,执行readFnc(socketIndex,rcvDataLen),返回true;否则返回false或者nil
|
||||
function mt:setRcvProc(rcvCbFnc)
|
||||
assert(self.co == coroutine.running(), "socket:setRcvProc: coroutine mismatch")
|
||||
self.rcvProcFnc = rcvCbFnc
|
||||
end
|
||||
|
||||
local function on_response(msg)
|
||||
local t = {
|
||||
[rtos.MSG_SOCK_CLOSE_CNF] = 'SOCKET_CLOSE',
|
||||
[rtos.MSG_SOCK_SEND_CNF] = 'SOCKET_SEND',
|
||||
[rtos.MSG_SOCK_CONN_CNF] = 'SOCKET_CONNECT',
|
||||
}
|
||||
if not sockets[msg.socket_index] then
|
||||
log.warn('response on nil socket', msg.socket_index, t[msg.id], msg.result)
|
||||
return
|
||||
end
|
||||
if sockets[msg.socket_index].wait ~= t[msg.id] then
|
||||
log.warn('response on invalid wait', sockets[msg.socket_index].id, sockets[msg.socket_index].wait, t[msg.id], msg.socket_index)
|
||||
return
|
||||
end
|
||||
log.info("socket:on_response:", msg.socket_index, t[msg.id], msg.result)
|
||||
if type(socketcore.sock_destroy) == "function" then
|
||||
if (msg.id == rtos.MSG_SOCK_CONN_CNF and msg.result ~= 0) or msg.id == rtos.MSG_SOCK_CLOSE_CNF then
|
||||
socketcore.sock_destroy(msg.socket_index)
|
||||
end
|
||||
end
|
||||
coroutine.resume(sockets[msg.socket_index].co, msg.result == 0, "RESPONSE")
|
||||
end
|
||||
|
||||
rtos.on(rtos.MSG_SOCK_CLOSE_CNF, on_response)
|
||||
rtos.on(rtos.MSG_SOCK_CONN_CNF, on_response)
|
||||
rtos.on(rtos.MSG_SOCK_SEND_CNF, on_response)
|
||||
rtos.on(rtos.MSG_SOCK_CLOSE_IND, function(msg)
|
||||
log.info("socket.rtos.MSG_SOCK_CLOSE_IND")
|
||||
if not sockets[msg.socket_index] then
|
||||
log.warn('close ind on nil socket', msg.socket_index, msg.id)
|
||||
return
|
||||
end
|
||||
if sockets[msg.socket_index].connected then
|
||||
sockets[msg.socket_index].connected = false
|
||||
if socketsConnected>0 then
|
||||
socketsConnected = socketsConnected-1
|
||||
end
|
||||
sys.publish("SOCKET_ACTIVE", socketsConnected>0)
|
||||
end
|
||||
sockets[msg.socket_index].error = 'CLOSED'
|
||||
|
||||
--[[
|
||||
if type(socketcore.sock_destroy) == "function" then
|
||||
socketcore.sock_destroy(msg.socket_index)
|
||||
end]]
|
||||
sys.publish("LIB_SOCKET_CLOSE_IND", sockets[msg.socket_index].ssl, sockets[msg.socket_index].protocol, sockets[msg.socket_index].address, sockets[msg.socket_index].port)
|
||||
coroutine.resume(sockets[msg.socket_index].co, false, "CLOSED")
|
||||
end)
|
||||
rtos.on(rtos.MSG_SOCK_RECV_IND, function(msg)
|
||||
if not sockets[msg.socket_index] then
|
||||
log.warn('close ind on nil socket', msg.socket_index, msg.id)
|
||||
return
|
||||
end
|
||||
|
||||
-- local s = socketcore.sock_recv(msg.socket_index, msg.recv_len)
|
||||
-- log.debug("socket.recv", "total " .. msg.recv_len .. " bytes", "first " .. 30 .. " bytes", s:sub(1, 30))
|
||||
log.debug("socket.recv", msg.recv_len, sockets[msg.socket_index].rcvProcFnc)
|
||||
ipDataFlowAdd(msg.recv_len+(sockets[msg.socket_index].protocol=="UDP" and 40 or 80))
|
||||
if sockets[msg.socket_index].rcvProcFnc then
|
||||
if sockets[msg.socket_index].ipv6 == true then
|
||||
sockets[msg.socket_index].rcvProcFnc(socketcore.ipv6_recv, msg.socket_index, msg.recv_len)
|
||||
else
|
||||
sockets[msg.socket_index].rcvProcFnc(socketcore.sock_recv, msg.socket_index, msg.recv_len)
|
||||
end
|
||||
else
|
||||
if sockets[msg.socket_index].wait == "+RECEIVE" then
|
||||
if sockets[msg.socket_index].ipv6 == true then
|
||||
coroutine.resume(sockets[msg.socket_index].co, true, socketcore.ipv6_recv(msg.socket_index, msg.recv_len))
|
||||
else
|
||||
coroutine.resume(sockets[msg.socket_index].co, true, socketcore.sock_recv(msg.socket_index, msg.recv_len))
|
||||
end
|
||||
else -- 数据进缓冲区,缓冲区溢出采用覆盖模式
|
||||
if #sockets[msg.socket_index].input > INDEX_MAX then
|
||||
log.error("socket recv", "out of stack", "block")
|
||||
-- sockets[msg.socket_index].input = {}
|
||||
sockets[msg.socket_index].isBlock = true
|
||||
sockets[msg.socket_index].msg = msg
|
||||
else
|
||||
sockets[msg.socket_index].isBlock = false
|
||||
if sockets[msg.socket_index].ipv6 == true then
|
||||
table.insert(sockets[msg.socket_index].input, socketcore.ipv6_recv(msg.socket_index, msg.recv_len))
|
||||
else
|
||||
table.insert(sockets[msg.socket_index].input, socketcore.sock_recv(msg.socket_index, msg.recv_len))
|
||||
end
|
||||
end
|
||||
sys.publish("SOCKET_RECV", msg.socket_index)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
--- 设置TCP层自动重传的参数
|
||||
-- @number[opt=4] retryCnt 重传次数;取值范围0到12
|
||||
-- @number[opt=16] retryMaxTimeout 限制每次重传允许的最大超时时间(单位秒),取值范围1到16
|
||||
-- @return nil
|
||||
-- @usage
|
||||
-- setTcpResendPara(3,8)
|
||||
-- setTcpResendPara(4,16)
|
||||
function setTcpResendPara(retryCnt, retryMaxTimeout)
|
||||
ril.request("AT+TCPUSERPARAM=6," .. (retryCnt or 4) .. ",7200," .. (retryMaxTimeout or 16))
|
||||
end
|
||||
|
||||
--- 设置域名解析参数
|
||||
-- 注意:0027以及之后的core版本才支持此功能
|
||||
-- @number[opt=4] retryCnt 重传次数;取值范围1到8
|
||||
-- @number[opt=4] retryTimeoutMulti 重传超时时间倍数,取值范围1到5
|
||||
-- 第n次重传超时时间的计算方式为:第n次的重传超时基数*retryTimeoutMulti,单位为秒
|
||||
-- 重传超时基数表为{1, 1, 2, 4, 4, 4, 4, 4}
|
||||
-- 第1次重传超时时间为:1*retryTimeoutMulti 秒
|
||||
-- 第2次重传超时时间为:1*retryTimeoutMulti 秒
|
||||
-- 第3次重传超时时间为:2*retryTimeoutMulti 秒
|
||||
-- ...........................................
|
||||
-- 第8次重传超时时间为:8*retryTimeoutMulti 秒
|
||||
-- @return nil
|
||||
-- @usage
|
||||
-- socket4G.setDnsParsePara(8,5)
|
||||
function setDnsParsePara(retryCnt, retryTimeoutMulti)
|
||||
ril.request("AT*DNSTMOUT="..(retryCnt or 4)..","..(retryTimeoutMulti or 4))
|
||||
end
|
||||
|
||||
-- 打印所有socket的状态
|
||||
-- @return 无
|
||||
-- @usage socket4G.printStatus()
|
||||
function printStatus()
|
||||
for _, client in pairs(sockets) do
|
||||
for k, v in pairs(client) do
|
||||
log.info('socket.printStatus', 'client', client.id, k, v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- 设置数据传输后,允许进入休眠状态的延时时长
|
||||
-- 3024版本以及之后的版本才支持此功能
|
||||
-- 此功能设置的参数,设置成功后,掉电会自动保存
|
||||
-- @number tm 数据传输后,允许进入休眠状态的延时时长,单位为秒,取值范围1到20
|
||||
-- 注意:此时间越短,允许进入休眠状态越快,功耗越低;但是在某些网络环境中,此时间越短,可能会造成数据传输不稳定
|
||||
-- 建议在可以接受的功耗范围内,此值设置的越大越好
|
||||
-- 如果没有设置此参数,此延时时长是和基站的配置有关,一般来说是10秒左右
|
||||
-- @return nil
|
||||
-- @usage
|
||||
-- socket4G.setLowPower(5)
|
||||
function setLowPower(tm)
|
||||
ril.request("AT*RTIME="..tm)
|
||||
end
|
||||
|
||||
|
||||
local function ipStatisTimerCb()
|
||||
if ipDataFlow~=0 then
|
||||
sys.publish("LIB_IP_STATIS_RPT",ipDataFlow)
|
||||
ipDataFlow = 0
|
||||
end
|
||||
end
|
||||
|
||||
function setIpStatis(interval)
|
||||
if ipStatisInterval~=interval then
|
||||
ipStatisInterval = interval
|
||||
ipStatisTimerCb()
|
||||
if interval==0 then
|
||||
sys.timerStop(ipStatisTimerCb)
|
||||
else
|
||||
sys.timerLoopStart(ipStatisTimerCb,interval*1000)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--setDnsParsePara(4,4)
|
||||
--setTcpResendPara(1,16)
|
||||
|
||||
1569
4G/源代码/lib/socketCh395.lua
Normal file
1569
4G/源代码/lib/socketCh395.lua
Normal file
File diff suppressed because it is too large
Load Diff
943
4G/源代码/lib/socketESP8266.lua
Normal file
943
4G/源代码/lib/socketESP8266.lua
Normal file
@@ -0,0 +1,943 @@
|
||||
--- 模块功能:数据链路激活、socketESP8266管理(创建、连接、数据收发、状态维护)
|
||||
-- @module socketESP8266
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2017.9.25
|
||||
-- require "link_wifi"
|
||||
require "utils"
|
||||
module(..., package.seeall)
|
||||
local powerCbFnc = nil
|
||||
-- local ril = ril_wifi
|
||||
local req = wifiRil.request
|
||||
|
||||
local valid = {"3", "2", "1", "0"}
|
||||
local validSsl = {"3", "2", "1", "0"}
|
||||
local sockets = {}
|
||||
local socketsSsl = {}
|
||||
-- 单次发送数据最大值
|
||||
local SENDSIZE = 1460
|
||||
-- 缓冲区最大下标
|
||||
local INDEX_MAX = 256
|
||||
|
||||
-- 用户自定义的DNS解析器
|
||||
local dnsParser
|
||||
local dnsParserToken = 0
|
||||
|
||||
-- SOCKET 是否有可用
|
||||
-- @return 可用true,不可用false
|
||||
isReady = link.isReady
|
||||
|
||||
local function isSocketActive(ssl)
|
||||
for _, c in pairs(ssl and socketsSsl or sockets) do
|
||||
if c.connected then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function socketStatusNtfy()
|
||||
sys.publish("SOCKET_ACTIVE", isSocketActive() or isSocketActive(true))
|
||||
end
|
||||
|
||||
local function stopConnectTimer(tSocket, id)
|
||||
if id and tSocket[id] and tSocket[id].co and coroutine.status(tSocket[id].co) == "suspended" and (tSocket[id].wait == "+SSLCONNECT" or tSocket[id].wait == "+CIPSTART") then
|
||||
-- and (tSocket[id].wait == "+SSLCONNECT" or (tSocket[id].protocol == "UDP" and tSocket[id].wait == "+CIPSTART")) then
|
||||
sys.timerStop(coroutine.resume, tSocket[id].co, false, "TIMEOUT")
|
||||
end
|
||||
end
|
||||
|
||||
local function errorInd(error)
|
||||
local coSuspended = {}
|
||||
|
||||
for k, v in pairs({sockets, socketsSsl}) do
|
||||
-- if #v ~= 0 then
|
||||
for _, c in pairs(v) do -- IP状态出错时,通知所有已连接的socket
|
||||
-- if c.connected or c.created then
|
||||
if error == 'CLOSED' and not c.ssl then
|
||||
c.connected = false
|
||||
socketStatusNtfy()
|
||||
end
|
||||
c.error = error
|
||||
if c.co and coroutine.status(c.co) == "suspended" then
|
||||
stopConnectTimer(v, c.id)
|
||||
-- coroutine.resume(c.co, false)
|
||||
table.insert(coSuspended, c.co)
|
||||
end
|
||||
-- end
|
||||
end
|
||||
-- end
|
||||
end
|
||||
|
||||
for k, v in pairs(coSuspended) do
|
||||
if v and coroutine.status(v) == "suspended" then
|
||||
coroutine.resume(v, false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sys.subscribe("IP_ERROR_IND", function()
|
||||
errorInd('IP_ERROR_IND')
|
||||
end)
|
||||
sys.subscribe('IP_SHUT_IND', function()
|
||||
errorInd('CLOSED')
|
||||
end)
|
||||
|
||||
-- 订阅rsp返回的消息处理函数
|
||||
local function onSocketURC(data, prefix)
|
||||
local tag, id, result = string.match(data, "([SSL]*)[&]*(%d), *([%u :%d]+)")
|
||||
tSocket = (tag == "SSL" and socketsSsl or sockets)
|
||||
if not id or not tSocket[id] then
|
||||
log.error('socket: urc on nil socket', data, id, tSocket[id], socketsSsl[id])
|
||||
return
|
||||
end
|
||||
if result == "CONNECT" or result:match("CONNECT ERROR") or result:match("CONNECT FAIL") then
|
||||
if tSocket[id].wait == "+CIPSTART" or tSocket[id].wait == "+SSLCONNECT" then
|
||||
stopConnectTimer(tSocket, id)
|
||||
coroutine.resume(tSocket[id].co, result == "CONNECT")
|
||||
else
|
||||
log.error("socket: error urc", tSocket[id].wait)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if tag == "SSL" and string.find(result, "ERROR:") == 1 then
|
||||
return
|
||||
end
|
||||
|
||||
if string.find(result, "ERROR") or result == "CLOSED" then
|
||||
if result == 'CLOSED' and not tSocket[id].ssl then
|
||||
tSocket[id].connected = false
|
||||
socketStatusNtfy()
|
||||
end
|
||||
tSocket[id].error = result
|
||||
stopConnectTimer(tSocket, id)
|
||||
coroutine.resume(tSocket[id].co, false)
|
||||
end
|
||||
end
|
||||
-- 创建socket函数
|
||||
local mt = {}
|
||||
mt.__index = mt
|
||||
local function socket(protocol, cert)
|
||||
local ssl = nil-- protocol:match("SSL")
|
||||
local id = table.remove(ssl and validSsl or valid)
|
||||
if not id then
|
||||
log.warn("socket.socket: too many sockets")
|
||||
return nil
|
||||
end
|
||||
|
||||
local co = coroutine.running()
|
||||
if not co then
|
||||
log.warn("socket.socket: socket must be called in coroutine")
|
||||
return nil
|
||||
end
|
||||
-- 实例的属性参数表
|
||||
local o = {
|
||||
id = id,
|
||||
protocol = protocol,
|
||||
ssl = ssl,
|
||||
cert = cert,
|
||||
co = co,
|
||||
input = {},
|
||||
output = {},
|
||||
wait = "",
|
||||
connected = false,
|
||||
iSubscribe = false,
|
||||
subMessage = nil
|
||||
}
|
||||
|
||||
tSocket = (ssl and socketsSsl or sockets)
|
||||
tSocket[id] = o
|
||||
if cert then
|
||||
local tmpPath, result, caConf, certConf, keyConf
|
||||
if cert.caCert then
|
||||
result = true
|
||||
tmpPath = (cert.caCert:sub(1, 1) == "/") and cert.caCert or ("/lua/" .. cert.caCert)
|
||||
if not io.exists("/at_ca.bin") then
|
||||
result = false
|
||||
else
|
||||
if crypto.md5("/at_ca.bin", "file") ~= crypto.md5(tmpPath, "file") then
|
||||
result = false
|
||||
end
|
||||
end
|
||||
if not result then
|
||||
io.writeFile("/at_ca.bin", io.readFile(tmpPath))
|
||||
req("AT+SYSFLASH=0,\"" .. "client_ca" .. "\",0,8192", nil, nil, nil, {
|
||||
id = id,
|
||||
path32 = "client_ca",
|
||||
path8955 = tmpPath
|
||||
})
|
||||
coroutine.yield()
|
||||
end
|
||||
caConf = true
|
||||
end
|
||||
if cert.clientCert then
|
||||
result = true
|
||||
tmpPath = (cert.clientCert:sub(1, 1) == "/") and cert.clientCert or ("/lua/" .. cert.clientCert)
|
||||
if not io.exists("/at_cert.bin") then
|
||||
result = false
|
||||
else
|
||||
if crypto.md5("/at_cert.bin", "file") ~= crypto.md5(tmpPath, "file") then
|
||||
result = false
|
||||
end
|
||||
end
|
||||
if not result then
|
||||
io.writeFile("/at_cert.bin", io.readFile(tmpPath))
|
||||
req("AT+SYSFLASH=0,\"" .. "client_cert" .. "\",0,8192", nil, nil, nil, {
|
||||
id = id,
|
||||
path32 = "client_cert",
|
||||
path8955 = tmpPath
|
||||
})
|
||||
coroutine.yield()
|
||||
end
|
||||
certConf = true
|
||||
end
|
||||
if cert.clientKey then
|
||||
result = true
|
||||
tmpPath = (cert.clientKey:sub(1, 1) == "/") and cert.clientKey or ("/lua/" .. cert.clientKey)
|
||||
if not io.exists("/at_key.bin") then
|
||||
result = false
|
||||
else
|
||||
if crypto.md5("/at_key.bin", "file") ~= crypto.md5(tmpPath, "file") then
|
||||
result = false
|
||||
end
|
||||
end
|
||||
if not result then
|
||||
io.writeFile("/at_key.bin", io.readFile(tmpPath))
|
||||
req("AT+SYSFLASH=0,\"" .. "client_key" .. "\",0,8192", nil, nil, nil, {
|
||||
id = id,
|
||||
path32 = "client_key",
|
||||
path8955 = tmpPath
|
||||
})
|
||||
coroutine.yield()
|
||||
end
|
||||
keyConf = true
|
||||
end
|
||||
if caConf and not certConf and not keyConf then
|
||||
req(string.format("AT+CIPSSLCCONF=%d,%d,%d,%d", id, 2, 0, 0))
|
||||
elseif not caConf and certConf and not keyConf then
|
||||
req(string.format("AT+CIPSSLCCONF=%d,%d,%d,%d", id, 1, 0, 0))
|
||||
elseif caConf and certConf and keyConf then
|
||||
req(string.format("AT+CIPSSLCCONF=%d,%d,%d,%d", id, 3, 0, 0))
|
||||
end
|
||||
else
|
||||
req(string.format("AT+CIPSSLCCONF=%d,%d", id, 0))
|
||||
end
|
||||
|
||||
return setmetatable(o, mt)
|
||||
end
|
||||
--- 创建基于TCP的socket对象
|
||||
-- @bool[opt=nil] ssl 是否为ssl连接,true表示是,其余表示否
|
||||
-- @table[opt=nil] cert ssl连接需要的证书配置,只有ssl参数为true时,才参数才有意义,cert格式如下:
|
||||
-- {
|
||||
-- caCert = "ca.crt", --CA证书文件(Base64编码 X.509格式),如果存在此参数,则表示客户端会对服务器的证书进行校验;不存在则不校验
|
||||
-- clientCert = "client.crt", --客户端证书文件(Base64编码 X.509格式),服务器对客户端的证书进行校验时会用到此参数
|
||||
-- clientKey = "client.key", --客户端私钥文件(Base64编码 X.509格式)
|
||||
-- clientPassword = "123456", --客户端证书文件密码[可选]
|
||||
-- }
|
||||
-- @return client,创建成功返回socket客户端对象;创建失败返回nil
|
||||
-- @usage
|
||||
-- c = socketESP8266.tcp()
|
||||
-- c = socketESP8266.tcp(true)
|
||||
-- c = socketESP8266.tcp(true, {caCert="ca.crt"})
|
||||
-- c = socketESP8266.tcp(true, {caCert="ca.crt", clientCert="client.crt", clientKey="client.key"})
|
||||
-- c = socketESP8266.tcp(true, {caCert="ca.crt", clientCert="client.crt", clientKey="client.key", clientPassword="123456"})
|
||||
function tcp(ssl, cert)
|
||||
return socket((ssl == true and "SSL" or "TCP"), (ssl == true) and cert or nil)
|
||||
end
|
||||
--- 创建基于UDP的socket对象
|
||||
-- @return client,创建成功返回socket客户端对象;创建失败返回nil
|
||||
-- @usage c = socketESP8266.udp()
|
||||
function udp()
|
||||
return socket("UDP")
|
||||
end
|
||||
|
||||
local sslInited
|
||||
local tSslInputCert, sSslInputCert = {}, ""
|
||||
|
||||
local function sslInit()
|
||||
if not sslInited then
|
||||
sslInited = true
|
||||
req("AT+SSLINIT")
|
||||
end
|
||||
|
||||
local i, item
|
||||
for i = 1, #tSslInputCert do
|
||||
item = table.remove(tSslInputCert, 1)
|
||||
req(item.cmd, item.arg)
|
||||
end
|
||||
tSslInputCert = {}
|
||||
end
|
||||
|
||||
local function sslTerm()
|
||||
if sslInited then
|
||||
if not isSocketActive(true) then
|
||||
sSslInputCert, sslInited = ""
|
||||
req("AT+SSLTERM")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function sslInputCert(t, f)
|
||||
if sSslInputCert:match(t .. f .. "&") then
|
||||
return
|
||||
end
|
||||
if not tSslInputCert then
|
||||
tSslInputCert = {}
|
||||
end
|
||||
local s = io.readFile((f:sub(1, 1) == "/") and f or ("/lua/" .. f))
|
||||
if not s then
|
||||
log.error("inputcrt err open", path)
|
||||
return
|
||||
end
|
||||
-- table.insert(tSslInputCert, {cmd = "AT+SSLCERT=0,\"" .. t .. "\",\"" .. f .. "\",1," .. s:len(), arg = s or ""})
|
||||
table.insert(tSslInputCert, {
|
||||
cmd = "AT+SYSFLASH=0,\"" .. t .. "\",\"" .. f .. "\",1," .. s:len(),
|
||||
arg = s or ""
|
||||
})
|
||||
sSslInputCert = sSslInputCert .. t .. f .. "&"
|
||||
end
|
||||
|
||||
local path32, path8955
|
||||
--- 连接服务器
|
||||
-- @string address 服务器地址,支持ip和域名
|
||||
-- @param port string或者number类型,服务器端口
|
||||
-- @return bool result true - 成功,false - 失败
|
||||
-- @number timeout, 链接服务器最长超时时间
|
||||
-- @usage c = socketESP8266.tcp(); c:connect("www.baidu.com",80,5);
|
||||
function mt:connect(address, port, timeout)
|
||||
assert(self.co == coroutine.running(), "socket:connect: coroutine mismatch")
|
||||
|
||||
if not link.isReady() then
|
||||
log.info("socket.connect: ip not ready")
|
||||
return false
|
||||
end
|
||||
|
||||
if cc and cc.anyCallExist() then
|
||||
log.info("socket:connect: call exist, cannot connect")
|
||||
return false
|
||||
end
|
||||
self.address = address
|
||||
self.port = port
|
||||
if self.cert then
|
||||
local tConfigCert, i = {}
|
||||
-- if self.cert then
|
||||
-- if self.cert.caCert then
|
||||
-- sslInputCert("cacrt", self.cert.caCert)
|
||||
-- table.insert(tConfigCert, "AT+SSLCERT=1," .. self.id .. ",\"cacrt\",\"" .. self.cert.caCert .. "\"")
|
||||
-- end
|
||||
-- if self.cert.clientCert then
|
||||
-- sslInputCert("localcrt", self.cert.clientCert)
|
||||
-- table.insert(tConfigCert, "AT+SSLCERT=1," .. self.id .. ",\"localcrt\",\"" .. self.cert.clientCert .. "\",\"" .. (self.cert.clientPassword or "") .. "\"")
|
||||
-- end
|
||||
-- if self.cert.clientKey then
|
||||
-- sslInputCert("localprivatekey", self.cert.clientKey)
|
||||
-- table.insert(tConfigCert, "AT+SSLCERT=1," .. self.id .. ",\"localprivatekey\",\"" .. self.cert.clientKey .. "\"")
|
||||
-- end
|
||||
-- end
|
||||
|
||||
-- sslInit()
|
||||
-- req(string.format("AT+SSLCREATE=%d,\"%s\",%d", self.id, address .. ":" .. port, (self.cert and self.cert.caCert) and 0 or 1))
|
||||
-- self.created = true
|
||||
-- for i = 1, #tConfigCert do
|
||||
-- req(tConfigCert[i])
|
||||
-- end
|
||||
-- req("AT+SSLCONNECT=" .. self.id)
|
||||
--
|
||||
|
||||
req(string.format("AT+CIPSTART=%d,\"%s\",\"%s\",%s", self.id, "SSL", address, port))
|
||||
else
|
||||
req(string.format("AT+CIPSTART=%d,\"%s\",\"%s\",%s", self.id, self.protocol, address, port))
|
||||
end
|
||||
-- if self.ssl or self.protocol == "UDP" then sys.timerStart(coroutine.resume, 120000, self.co, false, "TIMEOUT") end
|
||||
sys.timerStart(coroutine.resume, (timeout or 120) * 1000, self.co, false, "TIMEOUT")
|
||||
|
||||
wifiRil.regUrc((self.ssl and "SSL&" or "") .. self.id, onSocketURC)
|
||||
self.wait = self.ssl and "+SSLCONNECT" or "+CIPSTART"
|
||||
|
||||
local r, s = coroutine.yield()
|
||||
|
||||
if r == false and s == "DNS" then
|
||||
if self.ssl then
|
||||
self:sslDestroy()
|
||||
self.error = nil
|
||||
end
|
||||
|
||||
require "http"
|
||||
-- 请求腾讯云免费HttpDns解析
|
||||
http.request("GET", "119.29.29.29/d?dn=" .. address, nil, nil, nil, 40000, function(result, statusCode, head, body)
|
||||
log.info("socket.httpDnsCb", result, statusCode, head, body)
|
||||
sys.publish("SOCKET_HTTPDNS_RESULT_" .. address .. "_" .. port, result, statusCode, head, body)
|
||||
end)
|
||||
local _, result, statusCode, head, body = sys.waitUntil("SOCKET_HTTPDNS_RESULT_" .. address .. "_" .. port)
|
||||
|
||||
-- DNS解析成功
|
||||
if result and statusCode == "200" and body and body:match("^[%d%.]+") then
|
||||
return self:connect(body:match("^([%d%.]+)"), port)
|
||||
-- DNS解析失败
|
||||
else
|
||||
if dnsParser then
|
||||
dnsParserToken = dnsParserToken + 1
|
||||
dnsParser(address, dnsParserToken)
|
||||
local result, ip = sys.waitUntil("USER_DNS_PARSE_RESULT_" .. dnsParserToken, 40000)
|
||||
if result and ip and ip:match("^[%d%.]+") then
|
||||
return self:connect(ip:match("^[%d%.]+"), port)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if r == false then
|
||||
if self.ssl then
|
||||
self:sslDestroy()
|
||||
end
|
||||
sys.publish("LIB_SOCKET_CONNECT_FAIL_IND", self.ssl, self.protocol, address, port)
|
||||
return false
|
||||
end
|
||||
self.connected = true
|
||||
socketStatusNtfy()
|
||||
return true
|
||||
end
|
||||
|
||||
--- 异步收发选择器
|
||||
-- @number keepAlive 服务器和客户端最大通信间隔时间,也叫心跳包最大时间,单位秒
|
||||
-- @string pingreq 心跳包的字符串
|
||||
-- @return boole,false 失败,true 表示成功
|
||||
function mt:asyncSelect(keepAlive, pingreq)
|
||||
assert(self.co == coroutine.running(), "socket:asyncSelect: coroutine mismatch")
|
||||
if self.error then
|
||||
log.warn('socket.client:asyncSelect', 'error', self.error)
|
||||
return false
|
||||
end
|
||||
|
||||
self.wait = "SOCKET_SEND"
|
||||
while #self.output ~= 0 do
|
||||
local data = table.concat(self.output)
|
||||
self.output = {}
|
||||
for i = 1, string.len(data), SENDSIZE do
|
||||
-- 按最大MTU单元对data分包
|
||||
local stepData = string.sub(data, i, i + SENDSIZE - 1)
|
||||
-- 发送AT命令执行数据发送
|
||||
req(string.format("AT+" .. (self.ssl and "SSL" or "CIP") .. "SEND=%d,%d", self.id, string.len(stepData)), stepData)
|
||||
self.wait = self.ssl and "+SSLSEND" or "+CIPSEND"
|
||||
if not coroutine.yield() then
|
||||
if self.ssl then
|
||||
self:sslDestroy()
|
||||
end
|
||||
sys.publish("LIB_SOCKET_SEND_FAIL_IND", self.ssl, self.protocol, self.address, self.port)
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
self.wait = "SOCKET_WAIT"
|
||||
sys.publish("SOCKET_SEND", self.id)
|
||||
if keepAlive and keepAlive ~= 0 then
|
||||
if type(pingreq) == "function" then
|
||||
sys.timerStart(pingreq, keepAlive * 1000)
|
||||
else
|
||||
sys.timerStart(self.asyncSend, keepAlive * 1000, self, pingreq or "\0")
|
||||
end
|
||||
end
|
||||
return coroutine.yield()
|
||||
end
|
||||
--- 异步发送数据
|
||||
-- @string data 数据
|
||||
-- @return result true - 成功,false - 失败
|
||||
-- @usage c = socketESP8266.tcp(); c:connect(); c:asyncSend("12345678");
|
||||
function mt:asyncSend(data)
|
||||
if self.error then
|
||||
log.warn('socket.client:asyncSend', 'error', self.error)
|
||||
return false
|
||||
end
|
||||
table.insert(self.output, data or "")
|
||||
if self.wait == "SOCKET_WAIT" then
|
||||
coroutine.resume(self.co, true)
|
||||
end
|
||||
return true
|
||||
end
|
||||
--- 异步接收数据
|
||||
-- @return nil, 表示没有收到数据
|
||||
-- @return data 如果是UDP协议,返回新的数据包,如果是TCP,返回所有收到的数据,没有数据返回长度为0的空串
|
||||
-- @usage c = socketESP8266.tcp(); c:connect()
|
||||
-- @usage data = c:asyncRecv()
|
||||
function mt:asyncRecv()
|
||||
if #self.input == 0 then
|
||||
return ""
|
||||
end
|
||||
if self.protocol == "UDP" then
|
||||
return table.remove(self.input)
|
||||
else
|
||||
local s = table.concat(self.input)
|
||||
self.input = {}
|
||||
return s
|
||||
end
|
||||
end
|
||||
|
||||
--- 发送数据
|
||||
-- @string data 数据
|
||||
-- @return result true - 成功,false - 失败
|
||||
-- @usage c = socketESP8266.tcp(); c:connect(); c:send("12345678");
|
||||
function mt:send(data)
|
||||
assert(self.co == coroutine.running(), "socket:send: coroutine mismatch")
|
||||
if self.error then
|
||||
log.warn('socket.client:send', 'error', self.error)
|
||||
return false
|
||||
end
|
||||
if self.id == nil then
|
||||
log.warn('socket.client:send', 'closed')
|
||||
return false
|
||||
end
|
||||
|
||||
for i = 1, string.len(data or ""), SENDSIZE do
|
||||
-- 按最大MTU单元对data分包
|
||||
local stepData = string.sub(data, i, i + SENDSIZE - 1)
|
||||
-- 发送AT命令执行数据发送
|
||||
req(string.format("AT+" .. (self.ssl and "SSL" or "CIP") .. "SEND=%d,%d", self.id, string.len(stepData)), stepData)
|
||||
self.wait = self.ssl and "+SSLSEND" or "+CIPSEND"
|
||||
if not coroutine.yield() then
|
||||
if self.ssl then
|
||||
self:sslDestroy()
|
||||
end
|
||||
sys.publish("LIB_SOCKET_SEND_FAIL_IND", self.ssl, self.protocol, self.address, self.port)
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
--- 接收数据
|
||||
-- @number[opt=0] timeout 可选参数,接收超时时间,单位毫秒
|
||||
-- @string[opt=nil] msg 可选参数,控制socket所在的线程退出recv阻塞状态
|
||||
-- @bool[opt=nil] msgNoResume 可选参数,控制socket所在的线程退出recv阻塞状态,false或者nil表示“在recv阻塞状态,收到msg消息,可以退出阻塞状态”,true表示不退出
|
||||
-- @return result 数据接收结果,true表示成功,false表示失败
|
||||
-- @return data 如果成功的话,返回接收到的数据;超时时返回错误为"timeout";msg控制退出时返回msg的字符串
|
||||
-- @return param 如果是msg返回的false,则data的值是msg,param的值是msg的参数
|
||||
-- @usage c = socketESP8266.tcp(); c:connect()
|
||||
-- @usage result, data = c:recv()
|
||||
-- @usage false,msg,param = c:recv(60000,"publish_msg")
|
||||
function mt:recv(timeout, msg, msgNoResume)
|
||||
assert(self.co == coroutine.running(), "socket:recv: coroutine mismatch")
|
||||
if self.error then
|
||||
log.warn('socket.client:recv', 'error', self.error)
|
||||
return false
|
||||
end
|
||||
self.msgNoResume = msgNoResume
|
||||
if msg and not self.iSubscribe then
|
||||
self.iSubscribe = msg
|
||||
self.subMessage = function(data)
|
||||
-- if data then table.insert(self.output, data) end
|
||||
if (self.wait == "+RECEIVE" or self.wait == "+SSL RECEIVE") and not self.msgNoResume then
|
||||
if data then
|
||||
table.insert(self.output, data)
|
||||
end
|
||||
coroutine.resume(self.co, 0xAA)
|
||||
end
|
||||
end
|
||||
sys.subscribe(msg, self.subMessage)
|
||||
end
|
||||
if msg and #self.output ~= 0 then
|
||||
sys.publish(msg, false)
|
||||
end
|
||||
if #self.input == 0 then
|
||||
self.wait = self.ssl and "+SSL RECEIVE" or "+RECEIVE"
|
||||
if timeout and timeout > 0 then
|
||||
local r, s = sys.wait(timeout)
|
||||
-- if not r then
|
||||
-- return false, "timeout"
|
||||
-- elseif r and r == msg then
|
||||
-- return false, r, s
|
||||
-- else
|
||||
-- if self.ssl and not r then self:sslDestroy() end
|
||||
-- return r, s
|
||||
-- end
|
||||
if r == nil then
|
||||
return false, "timeout"
|
||||
elseif r == 0xAA then
|
||||
local dat = table.concat(self.output)
|
||||
self.output = {}
|
||||
return false, msg, dat
|
||||
else
|
||||
if self.ssl and not r then
|
||||
self:sslDestroy()
|
||||
end
|
||||
return r, s
|
||||
end
|
||||
else
|
||||
local r, s = coroutine.yield()
|
||||
if r == 0xAA then
|
||||
local dat = table.concat(self.output)
|
||||
self.output = {}
|
||||
return false, msg, dat
|
||||
else
|
||||
return r, s
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if self.protocol == "UDP" then
|
||||
return true, table.remove(self.input)
|
||||
else
|
||||
local s = table.concat(self.input)
|
||||
self.input = {}
|
||||
return true, s
|
||||
end
|
||||
end
|
||||
|
||||
function mt:sslDestroy()
|
||||
assert(self.co == coroutine.running(), "socket:sslDestroy: coroutine mismatch")
|
||||
if self.ssl and (self.connected or self.created) then
|
||||
self.connected = false
|
||||
self.created = false
|
||||
req("AT+SSLDESTROY=" .. self.id)
|
||||
self.wait = "+SSLDESTROY"
|
||||
coroutine.yield()
|
||||
socketStatusNtfy()
|
||||
end
|
||||
end
|
||||
--- 销毁一个socket
|
||||
-- @return nil
|
||||
-- @usage c = socket.tcp(); c:connect(); c:send("123"); c:close()
|
||||
function mt:close(slow)
|
||||
assert(self.co == coroutine.running(), "socket:close: coroutine mismatch")
|
||||
if self.iSubscribe then
|
||||
sys.unsubscribe(self.iSubscribe, self.subMessage)
|
||||
self.iSubscribe = false
|
||||
end
|
||||
if self.connected or self.created then
|
||||
self.connected = false
|
||||
self.created = false
|
||||
req(self.ssl and ("AT+SSLDESTROY=" .. self.id) or ("AT+CIPCLOSE=" .. self.id .. (slow and ",0" or "")))
|
||||
self.wait = self.ssl and "+SSLDESTROY" or "+CIPCLOSE"
|
||||
coroutine.yield()
|
||||
socketStatusNtfy()
|
||||
end
|
||||
if self.id ~= nil then
|
||||
wifiRil.deRegUrc((self.ssl and "SSL&" or "") .. self.id, onSocketURC)
|
||||
table.insert((self.ssl and validSsl or valid), 1, self.id)
|
||||
if self.ssl then
|
||||
socketsSsl[self.id] = nil
|
||||
else
|
||||
sockets[self.id] = nil
|
||||
end
|
||||
self.id = nil
|
||||
end
|
||||
end
|
||||
local function onResponse(cmd, success, response, intermediate)
|
||||
local prefix = string.match(cmd, "AT(%+%u+)")
|
||||
local id = string.match(cmd, "AT%+%u+=(%d)")
|
||||
if response == '+PDP: DEACT' then
|
||||
sys.publish('PDP_DEACT_IND')
|
||||
end -- cipsend 如果正好pdp deact会返回+PDP: DEACT作为回应
|
||||
local tSocket = prefix:match("SSL") and socketsSsl or sockets
|
||||
if not tSocket[id] then
|
||||
log.warn('socket: response on nil socket', cmd, response)
|
||||
return
|
||||
end
|
||||
log.info("cmd",cmd, success, response, intermediate)
|
||||
if cmd:match("^AT%+SSLCREATE") then
|
||||
tSocket[id].createResp = response
|
||||
end
|
||||
if tSocket[id].wait == prefix then
|
||||
if (prefix == "+CIPSTART" or prefix == "+SSLCONNECT") and success then
|
||||
-- CIPSTART,SSLCONNECT 返回OK只是表示被接受
|
||||
return
|
||||
end
|
||||
|
||||
if prefix == '+CIPSEND' then
|
||||
if response == 'OK' then
|
||||
-- 返回OK只是表示被接受
|
||||
return
|
||||
end
|
||||
if response ~= 'SEND OK' and response ~= 'OK' then
|
||||
local acceptLen = response:match("Recv (%d+) bytes")
|
||||
log.info("testlen", acceptLen, cmd:match("AT%+%u+=%d,(%d+)"))
|
||||
if acceptLen then
|
||||
if acceptLen ~= cmd:match("AT%+%u+=%d,(%d+)") then
|
||||
success = false
|
||||
else
|
||||
return
|
||||
end
|
||||
else
|
||||
success = false
|
||||
end
|
||||
end
|
||||
elseif prefix == "+SSLSEND" then
|
||||
if response:match("%d, *([%u%d :]+)") ~= 'SEND OK' then
|
||||
success = false
|
||||
end
|
||||
end
|
||||
|
||||
local reason, address
|
||||
if not success then
|
||||
if prefix == "+CIPSTART" then
|
||||
address = cmd:match("AT%+CIPSTART=%d,\"%a+\",\"(.+)\",%d+")
|
||||
elseif prefix == "+SSLCONNECT" and (tSocket[id].createResp or ""):match("SSL&%d+,CREATE ERROR: 4") then
|
||||
address = tSocket[id].address or ""
|
||||
end
|
||||
if address and not address:match("^[%d%.]+$") then
|
||||
reason = "DNS"
|
||||
end
|
||||
end
|
||||
|
||||
if not reason and not success then
|
||||
tSocket[id].error = response
|
||||
end
|
||||
stopConnectTimer(tSocket, id)
|
||||
coroutine.resume(tSocket[id].co, success, reason)
|
||||
end
|
||||
end
|
||||
|
||||
local function onSocketReceiveUrc(urc, prefix, cmd)
|
||||
local len, datatest = string.match(urc, "+CIPRECVDATA:(%d+),(.+)")
|
||||
local id = string.match(cmd, "+CIPRECVDATA=(%d),")
|
||||
tSocket = (tag == "SSL" and socketsSsl or sockets)
|
||||
len = tonumber(len)
|
||||
if len == 0 then
|
||||
return urc
|
||||
end
|
||||
|
||||
if string.len(datatest) == len then
|
||||
sys.publish("SOCKET_RECV", id)
|
||||
if tSocket[id].wait == "+RECEIVE" then
|
||||
coroutine.resume(tSocket[id].co, true, datatest)
|
||||
else -- 数据进缓冲区,缓冲区溢出采用覆盖模式
|
||||
if #tSocket[id].input > INDEX_MAX then
|
||||
tSocket[id].input = {}
|
||||
end
|
||||
table.insert(tSocket[id].input, datatest)
|
||||
end
|
||||
elseif string.len(datatest) < len then
|
||||
local cache = {}
|
||||
table.insert(cache, datatest)
|
||||
len = len - string.len(datatest)
|
||||
local function filter(data)
|
||||
-- 剩余未收到的数据长度
|
||||
if string.len(data) >= len then -- at通道的内容比剩余未收到的数据多
|
||||
-- 截取网络发来的数据
|
||||
table.insert(cache, string.sub(data, 1, len))
|
||||
-- 剩下的数据扔给at进行后续处理
|
||||
data = string.sub(data, len + 1, -1)
|
||||
if not tSocket[id] then
|
||||
log.warn('socket: receive on nil socket', id)
|
||||
else
|
||||
sys.publish("SOCKET_RECV", id)
|
||||
local s = table.concat(cache)
|
||||
if tSocket[id].wait == "+RECEIVE" or tSocket[id].wait == "+SSL RECEIVE" then
|
||||
coroutine.resume(tSocket[id].co, true, s)
|
||||
else -- 数据进缓冲区,缓冲区溢出采用覆盖模式
|
||||
if #tSocket[id].input > INDEX_MAX then
|
||||
tSocket[id].input = {}
|
||||
end
|
||||
table.insert(tSocket[id].input, s)
|
||||
end
|
||||
end
|
||||
return data
|
||||
else
|
||||
table.insert(cache, data)
|
||||
len = len - string.len(data)
|
||||
return "", filter
|
||||
end
|
||||
end
|
||||
return filter
|
||||
end
|
||||
end
|
||||
|
||||
wifiRil.regUrc("+CIPCLOSE", onResponse)
|
||||
wifiRil.regRsp("+CIPSEND", onResponse)
|
||||
wifiRil.regRsp("+CIPSTART", onResponse)
|
||||
wifiRil.regRsp("+SSLDESTROY", onResponse)
|
||||
wifiRil.regRsp("+SSLCREATE", onResponse)
|
||||
wifiRil.regRsp("+SSLSEND", onResponse)
|
||||
wifiRil.regRsp("+SSLCONNECT", onResponse)
|
||||
wifiRil.regUrc("+CIPRECVDATA", onSocketReceiveUrc)
|
||||
wifiRil.regUrc("+SSL RECEIVE", onSocketReceiveUrc)
|
||||
|
||||
wifiRil.regRsp("+SYSFLASH", function(cmd, result, response, intermediate, param)
|
||||
if cmd:find("AT%+SYSFLASH=0") then
|
||||
req("AT+SYSFLASH=1,\"" .. param.path32 .. "\",0," .. io.fileSize(param.path8955), io.readFile(param.path8955), nil, nil, param.id)
|
||||
elseif cmd:find("AT%+SYSFLASH=1") then
|
||||
local tSocket = sockets
|
||||
coroutine.resume(tSocket[param].co)
|
||||
end
|
||||
end)
|
||||
local recvId
|
||||
getRecvId = function() return recvId end
|
||||
|
||||
local errorNum = 0
|
||||
local function wifiUrc(data, prefix)
|
||||
log.info("urc上报", data, prefix)
|
||||
if prefix == "STATUS" then
|
||||
if not connecting or not connected then
|
||||
local state = data:sub(8, -1)
|
||||
if state == "0" or state == "1" or state == "5" then
|
||||
errorNum = errorNum + 1
|
||||
if errorNum > 5 then
|
||||
wifiRil.request("AT+CWMODE=1")
|
||||
-- wifiRil.request("AT+CWSTARTSMART=3")
|
||||
wifiRil.request("AT+CWJAP=\"Luat_XIAOMIPRO\",\"Air123456\"")
|
||||
else
|
||||
wifiRil.request("AT+CIPSTATUS", nil, nil, 5000)
|
||||
end
|
||||
elseif state == "2" then
|
||||
errorNum = 0
|
||||
connected = true
|
||||
sys.publish("IP_READY_IND")
|
||||
return
|
||||
end
|
||||
end
|
||||
elseif prefix == "WIFI GOT IP" then
|
||||
connecting = false
|
||||
connected = true
|
||||
link.setReady(link.ESP8266, true)
|
||||
sys.publish("IP_READY_IND")
|
||||
return
|
||||
elseif prefix == "WIFI CONNECTED" then
|
||||
connecting = true
|
||||
return
|
||||
elseif prefix == "Smart get wifi info" then
|
||||
stopConfig = sys.timerStart(wifiRil.request, 20000, "AT+CWSTOPSMART")
|
||||
return
|
||||
elseif prefix == "smartconfig connected wifi" then
|
||||
connecting = true
|
||||
if sys.timerIsActive(stopConfig) then sys.timerStop(stopConfig) end
|
||||
wifiRil.request("AT+CWSTOPSMART", nil, nil, 6000)
|
||||
elseif prefix == "+IPD" then
|
||||
log.info("rcv data", data)
|
||||
local lid, dataLen = string.match(data, "%+IPD,(%d),(%d+)")
|
||||
recvId = lid
|
||||
wifiRil.request(string.format("AT+CIPRECVDATA=%d,%d", lid, dataLen))
|
||||
elseif prefix == "+CIPRECVLEN" then
|
||||
log.info("rcv test", prefix, data)
|
||||
local lid = {string.match(data, "%+CIPRECVLEN:(.+),(.+),(.+),(.+),(.+)")}
|
||||
for k, v in pairs(lid) do
|
||||
if v ~= "-1" and v ~= "0" then
|
||||
wifiRil.request(string.format("AT+CIPRECVDATA=%d,%d", k - 1, 2147483647))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function wifiRsp(cmd, success, response, intermediate)
|
||||
log.info("wifi", cmd, success, response, intermediate)
|
||||
if cmd == "AT+CWSTARTSMART=3" then
|
||||
if success then smartConfig = true end
|
||||
elseif cmd == "AT+CWSTOPSMART" then
|
||||
if connecting then
|
||||
connecting = false
|
||||
connected = true
|
||||
sys.publish("IP_READY_IND")
|
||||
else
|
||||
sys.timerStart(wifiRil.request, 2000, "AT+CWSTARTSMART=3")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
wifiRil.regUrc("+IPD", wifiUrc)
|
||||
wifiRil.regUrc("STATUS", wifiUrc)
|
||||
wifiRil.regUrc("WIFI GOT IP", wifiUrc)
|
||||
wifiRil.regUrc("WIFI CONNECTED", wifiUrc)
|
||||
wifiRil.regUrc("smartconfig connected wifi", wifiUrc)
|
||||
wifiRil.regUrc("Smart get wifi info", wifiUrc)
|
||||
wifiRil.regRsp("+CWSTARTSMART", wifiRsp)
|
||||
wifiRil.regRsp("+CWSTOPSMART", wifiRsp)
|
||||
wifiRil.regUrc("+CIPRECVLEN", wifiUrc)
|
||||
wifiRil.request("AT+CWMODE=1")
|
||||
wifiRil.request("AT+CIPRECVMODE=1")
|
||||
wifiRil.request("AT+CIPMODE=0")
|
||||
wifiRil.request("AT+CIPMUX=1")
|
||||
wifiRil.request("AT+CIPSTATUS")
|
||||
|
||||
|
||||
function printStatus()
|
||||
log.info('socket.printStatus', 'valid id', table.concat(valid), table.concat(validSsl))
|
||||
|
||||
for m, n in pairs({sockets, socketsSsl}) do
|
||||
for _, client in pairs(n) do
|
||||
for k, v in pairs(client) do
|
||||
log.info('socket.printStatus', 'client', client.id, k, v)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- 设置TCP层自动重传的参数
|
||||
-- @number[opt=4] retryCnt 重传次数;取值范围0到12
|
||||
-- @number[opt=16] retryMaxTimeout 限制每次重传允许的最大超时时间(单位秒),取值范围1到16
|
||||
-- @return nil
|
||||
-- @usage
|
||||
-- setTcpResendPara(3,8)
|
||||
-- setTcpResendPara(4,16)
|
||||
function setTcpResendPara(retryCnt, retryMaxTimeout)
|
||||
req("AT+TCPUSERPARAM=6," .. (retryCnt or 4) .. ",7200," .. (retryMaxTimeout or 16))
|
||||
wifiRil.setDataTimeout(((retryCnt or 4) * (retryMaxTimeout or 16) + 60) * 1000)
|
||||
end
|
||||
|
||||
function setIpStatis(interval)
|
||||
|
||||
end
|
||||
|
||||
-- 设置用户自定义的DNS解析器.
|
||||
-- 通过域名连接服务器时,DNS解析的过程如下:
|
||||
-- 1、使用core中提供的方式,连接运营商DNS服务器解析,如果解析成功,则结束;如果解析失败,走第2步
|
||||
-- 2、使用脚本lib中提供的免费腾讯云HttpDns解析,如果解析成功,则结束;如果解析失败,走第3步
|
||||
-- 3、如果存在用户自定义的DNS解析器,则使用此处用户自定义的DNS解析器去解析
|
||||
-- @function[opt=nil] parserFnc 用户自定义的DNS解析器函数,函数的调用形式为:
|
||||
-- parserFnc(domainName,token),调用接口后会等待解析结果的消息通知或者40秒超时失败
|
||||
-- domainName:string类型,表示域名,例如"www.baidu.com"
|
||||
-- token:string类型,此次DNS解析请求的token,例如"1"
|
||||
-- 解析结束后,要publish一个消息来通知解析结果,消息参数中的ip地址最多返回一个,sys.publish("USER_DNS_PARSE_RESULT_"..token,ip),例如:
|
||||
-- sys.publish("USER_DNS_PARSE_RESULT_1","115.239.211.112")
|
||||
-- 表示解析成功,解析到1个IP地址115.239.211.112
|
||||
-- sys.publish("USER_DNS_PARSE_RESULT_1")
|
||||
-- 表示解析失败
|
||||
-- @return nil
|
||||
-- @usage socket.setDnsParser(parserFnc)
|
||||
function setDnsParser(parserFnc)
|
||||
dnsParser = parserFnc
|
||||
end
|
||||
|
||||
-- 设置数据发送模式(在网络准备就绪之前调用此接口设置).
|
||||
-- 如果设置为快发模式,注意如下两点:
|
||||
-- 1、通过send接口发送的数据,如果成功发送到服务器,设备端无法获取到这个成功状态
|
||||
-- 2、通过send接口发送的数据,如果发送失败,设备端可以获取到这个失败状态
|
||||
-- 慢发模式可以获取到send接口发送的成功或者失败
|
||||
--
|
||||
-- ****************************************************************************************************************************************************************
|
||||
-- TCP协议发送数据时,数据发送出去之后,必须等到服务器返回TCP ACK包,才认为数据发送成功,在网络较差的情况下,这种ACK确认就会导致发送过程很慢。
|
||||
-- 从而导致用户程序后续的AT处理逻辑一直处于等待状态。例如执行AT+CIPSEND动作发送一包数据后,接下来要执行AT+QTTS播放TTS,但是CIPSEND一直等了1分钟才返回SEND OK,
|
||||
-- 这时AT+QTTS就会一直等待1分钟,可能不是程序中想看到的。
|
||||
-- 此时就可以设置为快发模式,AT+CIPSEND可以立即返回一个结果,此结果表示“数据是否被缓冲区所保存”,从而不影响后续其他AT指令的及时执行
|
||||
--
|
||||
-- AT版本可以通过AT+CIPQSEND指令、Luat版本可以通过socket.setSendMode接口设置发送模式为快发或者慢发
|
||||
--
|
||||
-- 快发模式下,在core中有一个1460*7=10220字节的缓冲区,要发送的数据首先存储到此缓冲区,然后在core中自动循环发送。
|
||||
-- 如果此缓冲区已满,则AT+CIPSEND会直接返回ERROR,socket:send接口也会直接返回失败
|
||||
--
|
||||
-- 同时满足如下几种条件,适合使用快发模式:
|
||||
-- 1. 发送的数据量小,并且发送频率低,数据发送速度远远不会超过core中的10220字节大小;
|
||||
-- 没有精确地判断标准,可以简单的按照3分钟不超过10220字节来判断;曾经有一个不适合快发模式的例子如下:
|
||||
-- 用户使用Luat版本的http上传一个几十K的文件,设置了快发模式,导致一直发送失败,因为循环的向core中的缓冲区插入数据,
|
||||
-- 插入数据的速度远远超过发送数据到服务器的速度,所以很快就导致缓冲区慢,再插入数据时,就直接返回失败
|
||||
-- 2. 对每次发送的数据,不需要确认发送结果
|
||||
-- 3. 数据发送功能不能影响其他功能的及时响应
|
||||
-- ****************************************************************************************************************************************************************
|
||||
--
|
||||
-- @number[opt=0] mode 数据发送模式,0表示慢发,1表示快发
|
||||
-- @return nil
|
||||
-- @usage socket.setSendMode(1)
|
||||
-- function setSendMode(mode)
|
||||
-- linkTest.setSendMode(mode)
|
||||
-- end
|
||||
|
||||
|
||||
function open(para)
|
||||
powerCbFnc = para["powerFunc"] or nil
|
||||
if powerCbFnc then
|
||||
powerCbFnc(true)
|
||||
end
|
||||
end
|
||||
|
||||
function close()
|
||||
if powerCbFnc then
|
||||
powerCbFnc(false)
|
||||
end
|
||||
end
|
||||
526
4G/源代码/lib/sys.lua
Normal file
526
4G/源代码/lib/sys.lua
Normal file
@@ -0,0 +1,526 @@
|
||||
--- 模块功能:Luat协程调度框架
|
||||
-- @module sys
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2017.9.13
|
||||
require "utils"
|
||||
require "log"
|
||||
require "patch"
|
||||
module(..., package.seeall)
|
||||
|
||||
-- lib脚本版本号,只要lib中的任何一个脚本做了修改,都需要更新此版本号
|
||||
SCRIPT_LIB_VER = "2.4.5"
|
||||
|
||||
-- TaskID最大值
|
||||
local TASK_TIMER_ID_MAX = 0x1FFFFFFF
|
||||
-- msgId 最大值(请勿修改否则会发生msgId碰撞的危险)
|
||||
local MSG_TIMER_ID_MAX = 0x7FFFFFFF
|
||||
|
||||
-- 任务定时器id
|
||||
local taskTimerId = 0
|
||||
-- 消息定时器id
|
||||
local msgId = TASK_TIMER_ID_MAX
|
||||
-- 定时器id表
|
||||
local timerPool = {}
|
||||
local taskTimerPool = {}
|
||||
--消息定时器参数表
|
||||
local para = {}
|
||||
--定时器是否循环表
|
||||
local loop = {}
|
||||
|
||||
|
||||
-- 启动GSM协议栈。例如在充电开机未启动GSM协议栈状态下,如果用户长按键正常开机,此时调用此接口启动GSM协议栈即可
|
||||
-- @return 无
|
||||
-- @usage sys.powerOn()
|
||||
function powerOn()
|
||||
rtos.poweron(1)
|
||||
end
|
||||
|
||||
--- 软件重启
|
||||
-- @string r 重启原因,用户自定义,一般是string类型,重启后的trace中会打印出此重启原因
|
||||
-- @return 无
|
||||
-- @usage sys.restart('程序超时软件重启')
|
||||
function restart(r)
|
||||
assert(r and r ~= "", "sys.restart cause null")
|
||||
if errDump and errDump.appendErr and type(errDump.appendErr) == "function" then errDump.appendErr("restart[" .. r .. "];") end
|
||||
log.warn("sys.restart", r)
|
||||
rtos.restart()
|
||||
end
|
||||
|
||||
|
||||
--- task任务延时函数
|
||||
-- 只能直接或者间接的被task任务主函数调用,如果定时器创建成功,则本task会挂起
|
||||
-- @number ms 延时时间,单位毫秒,最小1,最大0x7FFFFFFF
|
||||
-- 实际上支持的最小超时时间是5毫秒,小于5毫秒的时间都会被转化为5毫秒
|
||||
-- @return result,分为如下三种情况:
|
||||
-- 1、如果定时器创建失败,本task不会被挂起,直接返回nil
|
||||
-- 2、如果定时器创建成功,本task被挂起,超时时间到达后,会激活本task,返回nil
|
||||
-- 3、如果定时器创建成功,本task被挂起,在超时时间到达之前,其他业务逻辑主动激活本task,
|
||||
-- 返回激活时携带的可变参数(如果不是故意为之,可能是写bug了)
|
||||
-- @usage
|
||||
-- task延时5秒:
|
||||
-- sys.taskInit(function()
|
||||
-- sys.wait(5000)
|
||||
-- end)
|
||||
function wait(ms)
|
||||
-- 参数检测,参数不能为负值
|
||||
assert(ms > 0, "The wait time cannot be negative!")
|
||||
--4G底层不支持小于5ms的定时器
|
||||
if ms < 5 then ms = 5 end
|
||||
-- 选一个未使用的定时器ID给该任务线程
|
||||
if taskTimerId >= TASK_TIMER_ID_MAX then taskTimerId = 0 end
|
||||
taskTimerId = taskTimerId + 1
|
||||
local timerid = taskTimerId
|
||||
taskTimerPool[coroutine.running()] = timerid
|
||||
timerPool[timerid] = coroutine.running()
|
||||
-- 调用core的rtos定时器
|
||||
if 1 ~= rtos.timer_start(timerid, ms) then log.debug("rtos.timer_start error") return end
|
||||
-- 挂起调用的任务线程
|
||||
local message = {coroutine.yield()}
|
||||
if #message ~= 0 then
|
||||
rtos.timer_stop(timerid)
|
||||
taskTimerPool[coroutine.running()] = nil
|
||||
timerPool[timerid] = nil
|
||||
return unpack(message)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- task任务条件等待函数(支持事件消息和定时器消息)
|
||||
-- 只能直接或者间接的被task任务主函数调用,调用本接口的task会挂起
|
||||
-- @string id 消息ID,建议使用string类型
|
||||
-- @number[opt=nil] ms 延时时间,单位毫秒,最小1,最大0x7FFFFFFF
|
||||
-- 实际上支持的最小超时时间是5毫秒,小于5毫秒的时间都会被转化为5毫秒
|
||||
-- @return result,data,分为如下三种情况:
|
||||
-- 1、如果存在超时时间参数:
|
||||
-- (1)、在超时时间到达之前,如果收到了等待的消息ID,则result为true,data为消息ID携带的参数(可能是多个参数)
|
||||
-- (2)、在超时时间到达之前,如果没收到等待的消息ID,则result为false,data为nil
|
||||
-- 2、如果不存在超时时间参数:如果收到了等待的消息ID,则result为true,data为消息ID携带的参数(可能是多个参数)
|
||||
-- (1)、如果收到了等待的消息ID,则result为true,data为消息ID携带的参数(可能是多个参数)
|
||||
-- (2)、如果没收到等待的消息ID,则task一直挂起
|
||||
-- 3、还存在一种特殊情况,本task挂起时,可能被task的外部应用逻辑给主动激活(如果不是故意为之,可能是写bug了)
|
||||
-- @usage
|
||||
-- task延时120秒或者收到"SIM_IND"消息:
|
||||
-- sys.taskInit(function()
|
||||
-- local result, data = sys.waitUntil("SIM_IND",120000)
|
||||
-- end)
|
||||
function waitUntil(id, ms)
|
||||
subscribe(id, coroutine.running())
|
||||
local message = ms and {wait(ms)} or {coroutine.yield()}
|
||||
unsubscribe(id, coroutine.running())
|
||||
return message[1] ~= nil, unpack(message, 2, #message)
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- 同上,但不返回等待结果
|
||||
function sys.waitUntilMsg(id)
|
||||
local co = sys.check_task()
|
||||
sys.subscribe(id, co)
|
||||
local message = {coroutine.yield()}
|
||||
sys.unsubscribe(id, co)
|
||||
return unpack(message, 2, #message)
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Task任务的条件等待函数扩展(包括事件消息和定时器消息等条件),只能用于任务函数中。
|
||||
-- @param id 消息ID
|
||||
-- @number ms 等待超时时间,单位ms,最大等待126322567毫秒
|
||||
-- @return message 接收到消息返回message,超时返回false
|
||||
-- @return data 接收到消息返回消息参数
|
||||
-- @usage result, data = sys.waitUntilExt("SIM_IND", 120000)
|
||||
function waitUntilExt(id, ms)
|
||||
subscribe(id, coroutine.running())
|
||||
local message = ms and {wait(ms)} or {coroutine.yield()}
|
||||
unsubscribe(id, coroutine.running())
|
||||
if message[1] ~= nil then return unpack(message) end
|
||||
return false
|
||||
end
|
||||
|
||||
--- 创建一个任务并且运行该任务
|
||||
-- @param fun 任务主函数,激活task时使用
|
||||
-- @param ... 任务主函数fun的可变参数
|
||||
-- @return co 返回该任务的线程ID
|
||||
-- @usage sys.taskInit(task1,'a','b')
|
||||
function taskInit(fun, ...)
|
||||
local co = coroutine.create(fun)
|
||||
coroutine.resume(co, ...)
|
||||
return co
|
||||
end
|
||||
|
||||
--- Luat平台初始化
|
||||
-- @param mode 充电开机是否启动GSM协议栈,1不启动,否则启动
|
||||
-- @param lprfnc 用户应用脚本中定义的“低电关机处理函数”,如果有函数名,则低电时,本文件中的run接口不会执行任何动作,否则,会延时1分钟自动关机
|
||||
-- @return 无
|
||||
-- @usage sys.init(1,0)
|
||||
function init(mode, lprfnc)
|
||||
-- 用户应用脚本中必须定义PROJECT和VERSION两个全局变量,否则会死机重启,如何定义请参考各个demo中的main.lua
|
||||
assert(PROJECT and PROJECT ~= "" and VERSION and VERSION ~= "", "Undefine PROJECT or VERSION")
|
||||
collectgarbage("setpause", 80)
|
||||
|
||||
-- 设置AT命令的虚拟串口
|
||||
uart.setup(uart.ATC, 0, 0, uart.PAR_NONE, uart.STOP_1)
|
||||
log.info("poweron reason:", rtos.poweron_reason(), PROJECT, VERSION, SCRIPT_LIB_VER, rtos.get_version())
|
||||
pcall(rtos.set_lua_info,"\r\n"..rtos.get_version().."\r\n"..(_G.PROJECT or "NO PROJECT").."\r\n"..(_G.VERSION or "NO VERSION"))
|
||||
if type(rtos.get_build_time)=="function" then log.info("core build time", rtos.get_build_time()) end
|
||||
if mode == 1 then
|
||||
-- 充电开机
|
||||
if rtos.poweron_reason() == rtos.POWERON_CHARGER then
|
||||
-- 关闭GSM协议栈
|
||||
rtos.poweron(0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------ rtos消息回调处理部分 ------------------------------------------
|
||||
--[[
|
||||
函数名:cmpTable
|
||||
功能 :比较两个table的内容是否相同,注意:table中不能再包含table
|
||||
参数 :
|
||||
t1:第一个table
|
||||
t2:第二个table
|
||||
返回值:相同返回true,否则false
|
||||
]]
|
||||
local function cmpTable(t1, t2)
|
||||
if not t2 then return #t1 == 0 end
|
||||
if #t1 == #t2 then
|
||||
for i = 1, #t1 do
|
||||
if unpack(t1, i, i) ~= unpack(t2, i, i) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
--- 关闭sys.timerStart和sys.timerLoopStart创建的定时器
|
||||
-- 有两种方式可以唯一标识一个定时器:
|
||||
-- 1、定时器ID
|
||||
-- 2、定时器回调函数和可变参数
|
||||
-- @param val 有两种形式:
|
||||
-- 1、为number类型时,表示定时器ID
|
||||
-- 2、为function类型时,表示定时器回调函数
|
||||
-- @param ... 可变参数,当val为定时器回调函数时,此可变参数才有意义,表示定时器回调函数的可变回调参数
|
||||
-- @return nil
|
||||
-- @usage
|
||||
-- 通过定时器ID关闭一个定时器:
|
||||
-- local timerId = sys.timerStart(publicTimerCbFnc,8000,"second")
|
||||
-- sys.timerStop(timerId)
|
||||
-- 通过定时器回调函数和可变参数关闭一个定时器:
|
||||
-- sys.timerStart(publicTimerCbFnc,8000,"first")
|
||||
-- sys.timerStop(publicTimerCbFnc,"first")
|
||||
function timerStop(val, ...)
|
||||
-- val 为定时器ID
|
||||
local arg={ ... }
|
||||
if type(val) == 'number' then
|
||||
timerPool[val], para[val], loop[val] = nil
|
||||
rtos.timer_stop(val)
|
||||
else
|
||||
for k, v in pairs(timerPool) do
|
||||
-- 回调函数相同
|
||||
if type(v) == 'table' and v.cb == val or v == val then
|
||||
-- 可变参数相同
|
||||
if cmpTable(arg, para[k]) then
|
||||
rtos.timer_stop(k)
|
||||
timerPool[k], para[k], loop[val] = nil
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- 关闭sys.timerStart和sys.timerLoopStart创建的某个回调函数的所有定时器
|
||||
-- @function fnc 定时器回调函数
|
||||
-- @return nil
|
||||
-- @usage
|
||||
-- 关闭回调函数为publicTimerCbFnc的所有定时器
|
||||
-- local function publicTimerCbFnc(tag)
|
||||
-- log.info("publicTimerCbFnc",tag)
|
||||
-- end
|
||||
--
|
||||
-- sys.timerStart(publicTimerCbFnc,8000,"first")
|
||||
-- sys.timerStart(publicTimerCbFnc,8000,"second")
|
||||
-- sys.timerStart(publicTimerCbFnc,8000,"third")
|
||||
|
||||
-- sys.timerStopAll(publicTimerCbFnc)
|
||||
function timerStopAll(fnc)
|
||||
for k, v in pairs(timerPool) do
|
||||
if type(v) == "table" and v.cb == fnc or v == fnc then
|
||||
rtos.timer_stop(k)
|
||||
timerPool[k], para[k], loop[k] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- 创建并且启动一个单次定时器
|
||||
-- 有两种方式可以唯一标识一个定时器:
|
||||
-- 1、定时器ID
|
||||
-- 2、定时器回调函数和可变参数
|
||||
-- @param fnc 定时器回调函数,必须存在,不允许为nil
|
||||
-- 当定时器超时时间到达时,回调函数的调用形式为fnc(...),其中...为回调参数
|
||||
-- @number ms 定时器超时时间,单位毫秒,最小1,最大0x7FFFFFFF
|
||||
-- 实际上支持的最小超时时间是5毫秒,小于5毫秒的时间都会被转化为5毫秒
|
||||
-- @param ... 可变参数,回调函数fnc的回调参数
|
||||
-- @return number timerId,创建成功返回定时器ID;创建失败返回nil
|
||||
-- @usage
|
||||
-- 创建一个5秒的单次定时器,回调函数打印"timerCb",没有可变参数:
|
||||
-- sys.timerStart(function() log.info("timerCb") end, 5000)
|
||||
-- 创建一个5秒的单次定时器,回调函数打印"timerCb"和"test",可变参数为"test":
|
||||
-- sys.timerStart(function(tag) log.info("timerCb",tag) end, 5000, "test")
|
||||
function timerStart(fnc, ms, ...)
|
||||
--回调函数和时长检测
|
||||
local arg={ ... }
|
||||
local argcnt=0
|
||||
for i, v in pairs(arg) do
|
||||
argcnt = argcnt+1
|
||||
end
|
||||
assert(fnc ~= nil, "sys.timerStart(first param) is nil !")
|
||||
assert(ms > 0, "sys.timerStart(Second parameter) is <= zero !")
|
||||
--4G底层不支持小于5ms的定时器
|
||||
if ms < 5 then ms = 5 end
|
||||
-- 关闭完全相同的定时器
|
||||
if argcnt == 0 then
|
||||
timerStop(fnc)
|
||||
else
|
||||
timerStop(fnc, ...)
|
||||
end
|
||||
-- 为定时器申请ID,ID值 1-0X1FFFFFFF 留给任务,0X1FFFFFFF-0x7FFFFFFF留给消息专用定时器
|
||||
while true do
|
||||
if msgId >= MSG_TIMER_ID_MAX then msgId = TASK_TIMER_ID_MAX end
|
||||
msgId = msgId + 1
|
||||
if timerPool[msgId] == nil then
|
||||
timerPool[msgId] = fnc
|
||||
break
|
||||
end
|
||||
end
|
||||
--调用底层接口启动定时器
|
||||
if rtos.timer_start(msgId, ms) ~= 1 then log.debug("rtos.timer_start error") return end
|
||||
--如果存在可变参数,在定时器参数表中保存参数
|
||||
if argcnt ~= 0 then
|
||||
para[msgId] = arg
|
||||
end
|
||||
--返回定时器id
|
||||
return msgId
|
||||
end
|
||||
|
||||
--- 创建并且启动一个循环定时器
|
||||
-- 有两种方式可以唯一标识一个定时器:
|
||||
-- 1、定时器ID
|
||||
-- 2、定时器回调函数和可变参数
|
||||
-- @param fnc 定时器回调函数,必须存在,不允许为nil
|
||||
-- 当定时器超时时间到达时,回调函数的调用形式为fnc(...),其中...为回调参数
|
||||
-- @number ms 定时器超时时间,单位毫秒,最小1,最大0x7FFFFFFF
|
||||
-- 实际上支持的最小超时时间是5毫秒,小于5毫秒的时间都会被转化为5毫秒
|
||||
-- @param ... 可变参数,回调函数fnc的回调参数
|
||||
-- @return number timerId,创建成功返回定时器ID;创建失败返回nil
|
||||
-- @usage
|
||||
-- 创建一个5秒的循环定时器,回调函数打印"timerCb",没有可变参数:
|
||||
-- sys.timerLoopStart(function() log.info("timerCb") end, 5000)
|
||||
-- 创建一个5秒的循环定时器,回调函数打印"timerCb"和"test",可变参数为"test":
|
||||
-- sys.timerLoopStart(function(tag) log.info("timerCb",tag) end, 5000, "test")
|
||||
function timerLoopStart(fnc, ms, ...)
|
||||
local tid = timerStart(fnc, ms, ...)
|
||||
if tid then loop[tid] = (ms<5 and 5 or ms) end
|
||||
return tid
|
||||
end
|
||||
|
||||
--- 判断“通过timerStart或者timerLoopStart创建的定时器”是否处于激活状态
|
||||
-- @param val 定时器标识,有两种表示形式
|
||||
-- 1、number类型,通过timerStart或者timerLoopStart创建定时器时返回的定时器ID,此情况下,不需要传入回调参数...就能唯一标识一个定时器
|
||||
-- 2、function类型,通过timerStart或者timerLoopStart创建定时器时的回调函数,此情况下,如果存在回调参数,需要传入回调参数...才能唯一标识一个定时器
|
||||
-- @param ... 回调参数,和“通过timerStart或者timerLoopStart创建定时器”的回调参数保持一致
|
||||
-- @return status,定时器激活状态;根据val的表示形式,有不同的返回值:
|
||||
-- 1、val为number类型时:如果处于激活状态,则返回function类型的定时器回调函数;否则返回nil
|
||||
-- 2、val为function类型时:如果处于激活状态,则返回bool类型的true;否则返回nil
|
||||
-- @usage
|
||||
-- 定时器ID形式标识定时器的使用参考:
|
||||
-- local timerId1 = sys.timerStart(function() end,5000)
|
||||
--
|
||||
-- sys.taskInit(function()
|
||||
-- sys.wait(3000)
|
||||
-- log.info("after 3 senonds, timerId1 isActive?",sys.timerIsActive(timerId1))
|
||||
--
|
||||
-- sys.wait(3000)
|
||||
-- log.info("after 6 senonds, timerId1 isActive?",sys.timerIsActive(timerId1))
|
||||
-- end)
|
||||
--
|
||||
--
|
||||
-- 回调函数和回调参数标识定时器的使用参考:
|
||||
-- local function timerCbFnc2(tag)
|
||||
-- log.info("timerCbFnc2",tag)
|
||||
-- end
|
||||
--
|
||||
-- sys.timerStart(timerCbFnc2,5000,"test")
|
||||
--
|
||||
-- sys.taskInit(function()
|
||||
-- sys.wait(3000)
|
||||
-- log.info("after 3 senonds, timerCbFnc2 test isActive?",sys.timerIsActive(timerCbFnc2,"test"))
|
||||
--
|
||||
-- sys.wait(3000)
|
||||
-- log.info("after 6 senonds, timerCbFnc2 test isActive?",sys.timerIsActive(timerCbFnc2,"test"))
|
||||
-- end)
|
||||
function timerIsActive(val, ...)
|
||||
local arg={ ... }
|
||||
if type(val) == "number" then
|
||||
return timerPool[val]
|
||||
else
|
||||
for k, v in pairs(timerPool) do
|
||||
if v == val then
|
||||
if cmpTable(arg, para[k]) then return true end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
------------------------------------------ LUA应用消息订阅/发布接口 ------------------------------------------
|
||||
-- 订阅者列表
|
||||
local subscribers = {}
|
||||
--内部消息队列
|
||||
local messageQueue = {}
|
||||
|
||||
--- 订阅消息
|
||||
-- @param id 消息id
|
||||
-- @param callback 消息回调处理
|
||||
-- @usage subscribe("NET_STATUS_IND", callback)
|
||||
function subscribe(id, callback)
|
||||
if type(id) ~= "string" or (type(callback) ~= "function" and type(callback) ~= "thread") then
|
||||
log.warn("warning: sys.subscribe invalid parameter", id, callback)
|
||||
return
|
||||
end
|
||||
if not subscribers[id] then subscribers[id] = {count = 0} end
|
||||
if not subscribers[id][callback] then
|
||||
subscribers[id].count = subscribers[id].count + 1
|
||||
subscribers[id][callback] = true
|
||||
end
|
||||
end
|
||||
|
||||
--- 取消订阅消息
|
||||
-- @param id 消息id
|
||||
-- @param callback 消息回调处理
|
||||
-- @usage unsubscribe("NET_STATUS_IND", callback)
|
||||
function unsubscribe(id, callback)
|
||||
if type(id) ~= "string" or (type(callback) ~= "function" and type(callback) ~= "thread") then
|
||||
log.warn("warning: sys.unsubscribe invalid parameter", id, callback)
|
||||
return
|
||||
end
|
||||
-- 取消订阅时将对应取消的函数赋值为false,不能直接赋值为nil,否则可能触发lua invalid key to 'next'异常
|
||||
if subscribers[id] then
|
||||
if subscribers[id][callback] then
|
||||
subscribers[id].count = subscribers[id].count - 1
|
||||
subscribers[id][callback] = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- 发布内部消息,存储在内部消息队列中
|
||||
-- @param ... 可变参数,用户自定义
|
||||
-- @return 无
|
||||
-- @usage publish("NET_STATUS_IND")
|
||||
function publish(...)
|
||||
local arg = { ... }
|
||||
table.insert(messageQueue, arg)
|
||||
end
|
||||
|
||||
-- 分发消息
|
||||
local function dispatch()
|
||||
while true do
|
||||
if #messageQueue == 0 then
|
||||
-- 当在同一个task内sys.waitUntil()不同的消息,并且没有任何消息publish时,会造成内存泄漏。
|
||||
-- 例如:sys.waitUntil("1", 500)、sys.waitUntil("2", 500)、...、sys.waitUntil("n", 500)
|
||||
for k, v in pairs(subscribers) do
|
||||
if v.count == 0 then subscribers[k] = nil end
|
||||
end
|
||||
break
|
||||
end
|
||||
local message = table.remove(messageQueue, 1)
|
||||
if subscribers[message[1]] then
|
||||
for callback, flag in pairs(subscribers[message[1]]) do
|
||||
if flag then
|
||||
if type(callback) == "function" then
|
||||
callback(unpack(message, 2, #message))
|
||||
elseif type(callback) == "thread" then
|
||||
coroutine.resume(callback, unpack(message))
|
||||
end
|
||||
end
|
||||
end
|
||||
-- 当某个subscribe的消息的回调取消订阅时,在这里将它赋值为nil,回收内存
|
||||
if subscribers[message[1]] then
|
||||
for callback, flag in pairs(subscribers[message[1]]) do
|
||||
if not flag then
|
||||
subscribers[message[1]][callback] = nil
|
||||
end
|
||||
end
|
||||
--当所有subscribe消息的回调都取消订阅时,在这里清空对应消息的表,回收内存
|
||||
if subscribers[message[1]].count == 0 then
|
||||
subscribers[message[1]] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- rtos消息回调
|
||||
local handlers = {}
|
||||
setmetatable(handlers, {__index = function() return function() end end, })
|
||||
|
||||
--- 注册rtos消息回调处理函数
|
||||
-- @number id 消息类型id
|
||||
-- @param handler 消息处理函数
|
||||
-- @return 无
|
||||
-- @usage rtos.on(rtos.MSG_KEYPAD, function(param) handle keypad message end)
|
||||
rtos.on = function(id, handler)
|
||||
handlers[id] = handler
|
||||
end
|
||||
|
||||
------------------------------------------ Luat 主调度框架 ------------------------------------------
|
||||
--- run()从底层获取core消息并及时处理相关消息,查询定时器并调度各注册成功的任务线程运行和挂起
|
||||
-- @return 无
|
||||
-- @usage sys.run()
|
||||
function run()
|
||||
while true do
|
||||
-- 分发内部消息
|
||||
dispatch()
|
||||
-- 阻塞读取外部消息
|
||||
local msg, param = rtos.receive(rtos.INF_TIMEOUT)
|
||||
-- 判断是否为定时器消息,并且消息是否注册
|
||||
if msg == rtos.MSG_TIMER and timerPool[param] then
|
||||
if param <= TASK_TIMER_ID_MAX then
|
||||
local taskId = timerPool[param]
|
||||
timerPool[param] = nil
|
||||
if taskTimerPool[taskId] == param then
|
||||
taskTimerPool[taskId] = nil
|
||||
coroutine.resume(taskId)
|
||||
end
|
||||
else
|
||||
local cb = timerPool[param]
|
||||
--如果不是循环定时器,从定时器id表中删除此定时器
|
||||
if not loop[param] then timerPool[param] = nil end
|
||||
if para[param] ~= nil then
|
||||
cb(unpack(para[param]))
|
||||
if not loop[param] then para[param] = nil end
|
||||
else
|
||||
cb()
|
||||
end
|
||||
--如果是循环定时器,继续启动此定时器
|
||||
if loop[param] then rtos.timer_start(param, loop[param]) end
|
||||
end
|
||||
--其他消息(音频消息、充电管理消息、按键消息等)
|
||||
elseif type(msg) == "number" then
|
||||
handlers[msg](param)
|
||||
else
|
||||
handlers[msg.id](msg)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
require "clib"
|
||||
|
||||
if type(rtos.openSoftDog)=="function" then
|
||||
rtos.openSoftDog(60000)
|
||||
sys.timerLoopStart(rtos.eatSoftDog,20000)
|
||||
end
|
||||
185
4G/源代码/lib/sysplus.lua
Normal file
185
4G/源代码/lib/sysplus.lua
Normal file
@@ -0,0 +1,185 @@
|
||||
|
||||
--[[
|
||||
@module sysplus
|
||||
@summary LuaTask核心增强逻辑
|
||||
@version 1.0
|
||||
@date 2022.04.27
|
||||
@author 晨旭/刘清宇/李思琦
|
||||
@usage
|
||||
|
||||
]]
|
||||
|
||||
module(..., package.seeall)
|
||||
|
||||
local sys = require "sys"
|
||||
local sysplus = {}
|
||||
|
||||
----------------------------------------------
|
||||
-- 提供给异步c接口使用, by 晨旭
|
||||
sysplus.cwaitMt = {
|
||||
wait = function(t,r)
|
||||
return function()
|
||||
if r and type(r) == "table" then--新建等待失败的返回
|
||||
return table.unpack(r)
|
||||
end
|
||||
return sys.waitUntilMsg(t)
|
||||
end
|
||||
end,
|
||||
cb = function(t,r)
|
||||
return function(f)
|
||||
if type(f) ~= "function" then return end
|
||||
sys.taskInit(function ()
|
||||
if r and type(r) == "table" then
|
||||
--sys.wait(1)--如果回调里调用了sys.publish,直接调用回调,会触发不了下一行的吧。。。
|
||||
f(table.unpack(r))
|
||||
return
|
||||
end
|
||||
f(sys.waitUntilMsg(t))
|
||||
end)
|
||||
end
|
||||
end,
|
||||
}
|
||||
sysplus.cwaitMt.__index = function(t,i)
|
||||
if sysplus.cwaitMt[i] then
|
||||
return sysplus.cwaitMt[i](rawget(t,"w"),rawget(t,"r"))
|
||||
else
|
||||
rawget(t,i)
|
||||
end
|
||||
end
|
||||
_G.sys_cw = function (w,...)
|
||||
local r = {...}
|
||||
local t = {w=w,r=(#r > 0 and r or nil)}
|
||||
setmetatable(t,sysplus.cwaitMt)
|
||||
return t
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------
|
||||
------------- 基于任务的task扩展 by 李思琦---------------------------
|
||||
|
||||
--任务列表
|
||||
local taskList = {}
|
||||
|
||||
--- 创建一个任务线程,在模块最末行调用该函数并注册模块中的任务函数,main.lua导入该模块即可
|
||||
-- @param fun 任务函数名,用于resume唤醒时调用
|
||||
-- @param taskName 任务名称,用于唤醒任务的id
|
||||
-- @param cbFun 接收到非目标消息时的回调函数
|
||||
-- @param ... 任务函数fun的可变参数
|
||||
-- @return co 返回该任务的线程号
|
||||
-- @usage sysplus.taskInitEx(task1,'a',callback)
|
||||
function sysplus.taskInitEx(fun, taskName, cbFun, ...)
|
||||
taskList[taskName]={msgQueue={}, To=false, cb=cbFun}
|
||||
return sys.taskInit(fun, ...)
|
||||
end
|
||||
|
||||
--- 删除由taskInitEx创建的任务线程
|
||||
-- @param taskName 任务名称,用于唤醒任务的id
|
||||
-- @return 无
|
||||
-- @usage sysplus.taskDel('a')
|
||||
function sysplus.taskDel(taskName)
|
||||
taskList[taskName]=nil
|
||||
end
|
||||
|
||||
local function waitTo(taskName)
|
||||
taskList[taskName].To = true
|
||||
sys.publish(taskName)
|
||||
end
|
||||
|
||||
--- 等待接收一个目标消息
|
||||
-- @param taskName 任务名称,用于唤醒任务的id
|
||||
-- @param target 目标消息,如果为nil,则表示接收到任意消息都会退出
|
||||
-- @param ms 超时时间,如果为nil,则表示无超时,永远等待
|
||||
-- @return msg or false 成功返回table型的msg,超时返回false
|
||||
-- @usage sysplus.waitMsg('a', 'b', 1000)
|
||||
function sysplus.waitMsg(taskName, target, ms)
|
||||
if taskList[taskName] == nil then
|
||||
log.error("sysplus", "sys.taskInitEx启动的task才能使用waitMsg")
|
||||
return false
|
||||
end
|
||||
local msg = false
|
||||
local message = nil
|
||||
if #taskList[taskName].msgQueue > 0 then
|
||||
msg = table.remove(taskList[taskName].msgQueue, 1)
|
||||
if target == nil then
|
||||
return msg
|
||||
end
|
||||
if (msg[1] == target) then
|
||||
return msg
|
||||
elseif type(taskList[taskName].cb) == "function" then
|
||||
taskList[taskName].cb(msg)
|
||||
end
|
||||
end
|
||||
sys.subscribe(taskName, coroutine.running())
|
||||
sys.timerStop(waitTo, taskName)
|
||||
if ms and ms ~= 0 then
|
||||
sys.timerStart(waitTo, ms, taskName)
|
||||
end
|
||||
taskList[taskName].To = false
|
||||
local finish=false
|
||||
while not finish do
|
||||
message = coroutine.yield()
|
||||
if #taskList[taskName].msgQueue > 0 then
|
||||
msg = table.remove(taskList[taskName].msgQueue, 1)
|
||||
-- sys.info("check target", msg[1], target)
|
||||
if target == nil then
|
||||
finish = true
|
||||
else
|
||||
if (msg[1] == target) then
|
||||
finish = true
|
||||
elseif type(taskList[taskName].cb) == "function" then
|
||||
taskList[taskName].cb(msg)
|
||||
end
|
||||
end
|
||||
elseif taskList[taskName].To then
|
||||
-- sys.info(taskName, "wait message timeout")
|
||||
finish = true
|
||||
end
|
||||
end
|
||||
if taskList[taskName].To then
|
||||
msg = nil
|
||||
end
|
||||
taskList[taskName].To = false
|
||||
sys.timerStop(waitTo, taskName)
|
||||
sys.unsubscribe(taskName, coroutine.running())
|
||||
return msg
|
||||
end
|
||||
|
||||
--- 向目标任务发送一个消息
|
||||
-- @param taskName 任务名称,用于唤醒任务的id
|
||||
-- @param param1 消息中的参数1,同时也是waitMsg里的target
|
||||
-- @param param2 消息中的参数2
|
||||
-- @param param3 消息中的参数3
|
||||
-- @param param4 消息中的参数4
|
||||
-- @return true or false 成功返回true
|
||||
-- @usage sysplus.sendMsg('a', 'b')
|
||||
function sysplus.sendMsg(taskName, param1, param2, param3, param4)
|
||||
if taskList[taskName]~=nil then
|
||||
table.insert(taskList[taskName].msgQueue, {param1, param2, param3, param4})
|
||||
sys.publish(taskName)
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function sysplus.cleanMsg(taskName)
|
||||
if taskList[taskName]~=nil then
|
||||
taskList[taskName].msgQueue = {}
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function sysplus.taskCB(taskName, msg)
|
||||
if taskList[taskName]~=nil then
|
||||
if type(taskList[taskName].cb) == "function" then
|
||||
taskList[taskName].cb(msg)
|
||||
return
|
||||
end
|
||||
end
|
||||
log.error(taskName, "no cb fun")
|
||||
end
|
||||
|
||||
_G.sys_send = sysplus.sendMsg
|
||||
_G.sys_wait = sysplus.waitMsg
|
||||
|
||||
return sysplus
|
||||
----------------------------
|
||||
115
4G/源代码/lib/uiWin.lua
Normal file
115
4G/源代码/lib/uiWin.lua
Normal file
@@ -0,0 +1,115 @@
|
||||
--- 模块功能:UI窗口管理
|
||||
-- @module uiWin
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2018.03.25
|
||||
|
||||
module(...,package.seeall)
|
||||
|
||||
--窗口管理栈
|
||||
local stack = {}
|
||||
--当前分配的窗口ID
|
||||
local winid = 0
|
||||
|
||||
local function allocid()
|
||||
winid = winid + 1
|
||||
return winid
|
||||
end
|
||||
|
||||
local function loseFocus()
|
||||
if stack[#stack] and stack[#stack]["onLoseFocus"] then
|
||||
stack[#stack]["onLoseFocus"]()
|
||||
end
|
||||
end
|
||||
|
||||
--- 新增一个窗口
|
||||
-- @table wnd 窗口的元素以及消息处理函数表
|
||||
-- @return number,窗口ID
|
||||
-- @usage uiWin.add({onUpdate = refresh})
|
||||
function add(wnd)
|
||||
---必须注册更新接口
|
||||
assert(wnd.onUpdate)
|
||||
if type(wnd) ~= "table" then
|
||||
assert("unknown uiwin type "..type(wnd))
|
||||
end
|
||||
--上一个窗口执行失去焦点的处理函数
|
||||
loseFocus()
|
||||
--为新窗口分配窗口ID
|
||||
wnd.id = allocid()
|
||||
--新窗口请求入栈
|
||||
sys.publish("UIWND_ADD",wnd)
|
||||
return wnd.id
|
||||
end
|
||||
|
||||
--- 移除一个窗口
|
||||
-- @number winId 窗口ID
|
||||
-- @return nil
|
||||
-- @usage uiWin.remove(winId)
|
||||
function remove(winId)
|
||||
sys.publish("UIWND_REMOVE",winId)
|
||||
end
|
||||
|
||||
function removeAll()
|
||||
sys.publish("UIWND_REMOVEALL")
|
||||
end
|
||||
|
||||
function update()
|
||||
sys.publish("UIWND_UPDATE")
|
||||
end
|
||||
|
||||
local function onAdd(wnd)
|
||||
table.insert(stack,wnd)
|
||||
stack[#stack].onUpdate()
|
||||
end
|
||||
|
||||
local function onRemove(winid)
|
||||
local istop,k,v
|
||||
for k,v in ipairs(stack) do
|
||||
if v.id == winid then
|
||||
istop = (k==#stack)
|
||||
table.remove(stack,k)
|
||||
if #stack~=0 and istop then
|
||||
stack[#stack].onUpdate()
|
||||
end
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function onRemoveAll()
|
||||
local k,v
|
||||
for k,v in ipairs(stack) do
|
||||
table.remove(stack,k)
|
||||
end
|
||||
end
|
||||
|
||||
local function onUpdate()
|
||||
if stack[#stack] and stack[#stack].onUpdate then
|
||||
stack[#stack].onUpdate()
|
||||
end
|
||||
end
|
||||
|
||||
--key:自定义功能键
|
||||
--value:自定义功能键的状态
|
||||
local function onKey(key,value)
|
||||
if stack[#stack] and stack[#stack].onKey then
|
||||
stack[#stack].onKey(key,value)
|
||||
end
|
||||
end
|
||||
|
||||
--- 判断一个窗口是否处于最前显示
|
||||
-- @number winId 窗口ID
|
||||
-- @return bool,true表示最前显示,其余表示非最前显示
|
||||
-- @usage uiWin.isActive(winId)
|
||||
function isActive(winId)
|
||||
if stack[#stack] and stack[#stack].id then
|
||||
return stack[#stack].id==winId
|
||||
end
|
||||
end
|
||||
|
||||
sys.subscribe("UIWND_ADD",onAdd)
|
||||
sys.subscribe("UIWND_REMOVE",onRemove)
|
||||
sys.subscribe("UIWND_REMOVEALL",onRemoveAll)
|
||||
sys.subscribe("UIWND_UPDATE",onUpdate)
|
||||
sys.subscribe("UIWND_KEY",onKey)
|
||||
230
4G/源代码/lib/update.lua
Normal file
230
4G/源代码/lib/update.lua
Normal file
@@ -0,0 +1,230 @@
|
||||
--- 模块功能:远程升级.
|
||||
-- @module update
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2018.03.29
|
||||
|
||||
require "misc"
|
||||
require "http"
|
||||
require "log"
|
||||
require "common"
|
||||
|
||||
module(..., package.seeall)
|
||||
|
||||
local sUpdating,sCbFnc,sUrl,sPeriod,sRedir,sLocation,fotastart
|
||||
local sProcessedLen = 0
|
||||
--local sBraekTest = 0
|
||||
local httpRspCode
|
||||
local sGetImeiFnc,sDownloadProcessFnc
|
||||
|
||||
local updateMsg
|
||||
|
||||
local otaBegin
|
||||
|
||||
local function httpDownloadCbFnc(result,statusCode,head)
|
||||
log.info("update.httpDownloadCbFnc",result,statusCode,head,sCbFnc,sPeriod)
|
||||
sys.publish("UPDATE_DOWNLOAD",result,statusCode,head)
|
||||
end
|
||||
|
||||
local function processOta(stepData,totalLen,statusCode)
|
||||
|
||||
if stepData and totalLen then
|
||||
if statusCode=="200" or statusCode=="206" then
|
||||
if not otaBegin then sys.publish("LIB_UPDATE_OTA_DOWNLOAD_BEGIN") otaBegin=true end
|
||||
local fotaProcessStatus=rtos.fota_process((sProcessedLen+stepData:len()>totalLen) and stepData:sub(1,totalLen-sProcessedLen) or stepData,totalLen)
|
||||
if fotaProcessStatus~=0 then
|
||||
log.error("update.processOta","fail",fotaProcessStatus,"failFotaProcessStatus")
|
||||
log.error("update.processOta","get_fs_free_size: ",rtos.get_fs_free_size()," Bytes")
|
||||
sys.publish("LIB_UPDATE_OTA_DOWNLOAD_END",false)
|
||||
return false
|
||||
else
|
||||
sProcessedLen = sProcessedLen + stepData:len()
|
||||
if sDownloadProcessFnc then sDownloadProcessFnc(sProcessedLen*100/totalLen) end
|
||||
log.info("update.processOta",totalLen,sProcessedLen,(sProcessedLen*100/totalLen).."%")
|
||||
--if sProcessedLen*100/totalLen==sBraekTest then return false end
|
||||
if sProcessedLen*100/totalLen>=100 then sys.publish("LIB_UPDATE_OTA_DOWNLOAD_END",true) return true end
|
||||
end
|
||||
elseif statusCode:sub(1,1)~="3" and stepData:len()==totalLen and totalLen>0 then
|
||||
if totalLen<=200 then
|
||||
local msg = stepData:match("\"msg\":%s*\"(.-)\"")
|
||||
if msg and msg:len()<=200 then
|
||||
updateMsg = common.ucs2beToUtf8((msg:gsub("\\u","")):fromHex())
|
||||
log.warn("update.error",updateMsg)
|
||||
end
|
||||
end
|
||||
httpRspCode = stepData:match("\"code\":%s*(%d+)")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function clientTask()
|
||||
sUpdating = true
|
||||
--不要省略此处代码,否则下文中的misc.getImei有可能获取不到
|
||||
while not socket.isReady() do sys.waitUntil("IP_READY_IND") end
|
||||
|
||||
|
||||
|
||||
while true do
|
||||
local retryCnt = 0
|
||||
sProcessedLen = 0
|
||||
otaBegin = false
|
||||
updateMsg = nil
|
||||
while true do
|
||||
--sBraekTest = sBraekTest+30
|
||||
log.info("update.http.request",sLocation,sUrl,sProcessedLen,sBraekTest,fotastart)
|
||||
if not fotastart then break end
|
||||
local coreVer = rtos.get_version()
|
||||
local coreName1,coreName2 = coreVer:match("(.-)_V%d+(_.+)")
|
||||
local coreVersion = tonumber(coreVer:match(".-_V(%d+)"))
|
||||
httpRspCode = nil
|
||||
|
||||
-- 合宙云平台升级地址
|
||||
local iotURL="iot.openluat.com/api/site/firmware_upgrade"
|
||||
-- 模块信息
|
||||
local moduleInfo="?project_key=".._G.PRODUCT_KEY
|
||||
.."&imei="..(sGetImeiFnc and sGetImeiFnc() or misc.getImei())
|
||||
.."&firmware_name=".._G.PROJECT.."_"..coreName1..coreName2
|
||||
.."&core_version="..coreVersion
|
||||
.."&dfota=1&version=".._G.VERSION..(sRedir and "&need_oss_url=1" or "")
|
||||
|
||||
|
||||
-- 如果自定义升级地址前三位为“###”,则不拼接模块信息
|
||||
if sUrl and string.sub(sUrl,1,3)=="###" then
|
||||
log.info("1-3",string.sub(sUrl,1,3))
|
||||
log.info("3-0",string.sub(sUrl,4))
|
||||
customizeUrl=string.sub(sUrl,4)
|
||||
elseif sUrl then
|
||||
customizeUrl=sUrl..moduleInfo
|
||||
else
|
||||
-- 默认向合宙云平台地址拼接模块信息
|
||||
iotURL=iotURL..moduleInfo
|
||||
end
|
||||
|
||||
http.request("GET",
|
||||
sLocation or (customizeUrl or iotURL),
|
||||
nil,{["Range"]="bytes="..sProcessedLen.."-"},nil,60000,httpDownloadCbFnc,processOta)
|
||||
|
||||
|
||||
local _,result,statusCode,head = sys.waitUntil("UPDATE_DOWNLOAD")
|
||||
log.info("update.waitUntil UPDATE_DOWNLOAD",result,statusCode,httpRspCode)
|
||||
if result then
|
||||
local needBreak
|
||||
if statusCode=="200" or statusCode=="206" then
|
||||
needBreak = true
|
||||
local check = rtos.fota_end()
|
||||
log.info("update.rtos.fota_end", check)
|
||||
if sCbFnc then
|
||||
if check == 0 then
|
||||
sCbFnc(true)
|
||||
else
|
||||
sCbFnc(false)
|
||||
end
|
||||
else
|
||||
if check == 0 then
|
||||
sys.restart("UPDATE_DOWNLOAD_SUCCESS")
|
||||
end
|
||||
end
|
||||
elseif statusCode:sub(1,1)=="3" and head and head["Location"] then
|
||||
sLocation = head["Location"]
|
||||
sys.wait(2000)
|
||||
elseif httpRspCode=="43" then
|
||||
log.info("update.clientTask","wait server create fota")
|
||||
sys.wait(30000)
|
||||
else
|
||||
log.info("update.rtos.fota_end",rtos.fota_end())
|
||||
if sCbFnc then sCbFnc(false) end
|
||||
needBreak = true
|
||||
end
|
||||
|
||||
if needBreak then
|
||||
break
|
||||
end
|
||||
else
|
||||
retryCnt = retryCnt+1
|
||||
if retryCnt==30 then
|
||||
rtos.fota_end()
|
||||
if sCbFnc then sCbFnc(false) end
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sProcessedLen = 0
|
||||
|
||||
if sPeriod then
|
||||
sys.wait(sPeriod)
|
||||
if rtos.fota_start()~=0 then
|
||||
log.error("update.request","fota_start fail")
|
||||
fotastart = false
|
||||
else
|
||||
fotastart = true
|
||||
end
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
sUpdating = false
|
||||
end
|
||||
|
||||
--- 启动远程升级功能
|
||||
-- @function[opt=nil] cbFnc 每次执行远程升级功能后的回调函数,回调函数的调用形式为:
|
||||
-- cbFnc(result),result为true表示升级包下载成功,其余表示下载失败
|
||||
--如果没有设置此参数,则升级包下载成功后,会自动重启
|
||||
-- @string[opt=nil] url 使用http的get命令下载升级包的url,如果没有设置此参数,默认使用Luatiot平台的url
|
||||
-- 如果用户设置了url,注意:仅传入完整url的前半部分(如果有参数,即传入?前一部分),http.lua会自动添加?以及后面的参数,例如:
|
||||
-- 设置的url="www.userserver.com/api/site/firmware_upgrade",则http.lua会在此url后面补充下面的参数
|
||||
-- "?project_key=".._G.PRODUCT_KEY
|
||||
-- .."&imei="..misc.getimei()
|
||||
-- .."&device_key="..misc.getsn()
|
||||
-- .."&firmware_name=".._G.PROJECT.."_"..rtos.get_version().."&version=".._G.VERSION
|
||||
-- 如果用户设置了url,且url前面增加三个井号"###",http.lua会自动忽略"###"并以用户填入的url作为请求地址,不会自动添加模块信息,例如:
|
||||
-- 设置的url="###www.userserver.com"/api/site/firmware_upgrade?customparam=test",则http.lua会将此url开头的"###"忽略,并以此url为地址进行请求
|
||||
-- "www.userserver.com"/api/site/firmware_upgrade?customparam=test"
|
||||
-- 如果redir设置为true,还会补充.."&need_oss_url=1"
|
||||
-- @number[opt=nil] period 单位毫秒,定时启动远程升级功能的间隔,如果没有设置此参数,仅执行一次远程升级功能
|
||||
-- @bool[opt=nil] redir 是否访问重定向到阿里云的升级包,使用Luat提供的升级服务器时,此参数才有意义
|
||||
-- 为了缓解Luat的升级服务器压力,从2018年7月11日起,在iot.openluat.com新增或者修改升级包的升级配置时,升级文件会备份一份到阿里云服务器
|
||||
-- 如果此参数设置为true,会从阿里云服务器下载升级包;如果此参数设置为false或者nil,仍然从Luat的升级服务器下载升级包
|
||||
-- @return nil
|
||||
-- @usage
|
||||
-- update.request()
|
||||
-- update.request(cbFnc)
|
||||
-- update.request(cbFnc,"www.userserver.com/update")
|
||||
-- update.request(cbFnc,nil,4*3600*1000)
|
||||
-- update.request(cbFnc,nil,4*3600*1000,true)
|
||||
function request(cbFnc,url,period,redir)
|
||||
if rtos.fota_start()~=0 then
|
||||
log.error("update.request","fota_start fail")
|
||||
fotastart = false
|
||||
return
|
||||
else
|
||||
fotastart = true
|
||||
end
|
||||
sCbFnc,sUrl,sPeriod,sRedir = cbFnc or sCbFnc,url or sUrl,period or sPeriod,sRedir or redir
|
||||
log.info("update.request",sCbFnc,sUrl,sPeriod,sRedir)
|
||||
if not sUpdating then
|
||||
sys.taskInit(clientTask)
|
||||
end
|
||||
end
|
||||
|
||||
function setGetImeiCbFnc(cbFnc)
|
||||
sGetImeiFnc = cbFnc
|
||||
end
|
||||
|
||||
--- 设置升级包下载过程中的下载进度通知回调函数
|
||||
-- @function cbFnc 下载进度通知回调函数,回调函数的调用形式如下:
|
||||
-- cbFnc(step)
|
||||
-- step表示下载进度:取值范围是0到100,下载进度更新很快,建议在回调函数中,每隔5或者10执行一次实际动作
|
||||
-- @return nil
|
||||
-- @usage update.setDownloadProcessCbFnc(function(step) end)
|
||||
function setDownloadProcessCbFnc(cbFnc)
|
||||
sDownloadProcessFnc = cbFnc
|
||||
end
|
||||
|
||||
--- 获取请求升级包时服务器返回的信息
|
||||
-- @return updateMsg, 若没有请求升级或服务器未返回相关信息,则返回值为nil,否则返回服务器返回的相关信息
|
||||
-- @usage local msg = getUpdateMsg()
|
||||
function getUpdateMsg()
|
||||
return updateMsg
|
||||
end
|
||||
286
4G/源代码/lib/utils.lua
Normal file
286
4G/源代码/lib/utils.lua
Normal file
@@ -0,0 +1,286 @@
|
||||
--- 模块功能:常用工具类接口
|
||||
-- @module utils
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2019.01.05
|
||||
module(..., package.seeall)
|
||||
|
||||
--- 将Lua字符串转成HEX字符串,如"123abc"转为"313233616263"
|
||||
-- @string str 输入字符串
|
||||
-- @string[opt=""] separator 输出的16进制字符串分隔符
|
||||
-- @return hexstring 16进制组成的串
|
||||
-- @return len 输入的字符串长度
|
||||
-- @usage
|
||||
-- string.toHex("\1\2\3") -> "010203" 3
|
||||
-- string.toHex("123abc") -> "313233616263" 6
|
||||
-- string.toHex("123abc"," ") -> "31 32 33 61 62 63 " 6
|
||||
function string.toHex(str, separator)
|
||||
return str:gsub('.', function(c)
|
||||
return string.format("%02X" .. (separator or ""), string.byte(c))
|
||||
end)
|
||||
end
|
||||
--- 将HEX字符串转成Lua字符串,如"313233616263"转为"123abc", 函数里加入了过滤分隔符,可以过滤掉大部分分隔符(可参见正则表达式中\s和\p的范围)。
|
||||
-- @string hex 16进制组成的串
|
||||
-- @return charstring,字符组成的串
|
||||
-- @return len,输出字符串的长度
|
||||
-- @usage
|
||||
-- string.fromHex("010203") -> "\1\2\3"
|
||||
-- string.fromHex("313233616263:) -> "123abc"
|
||||
function string.fromHex(hex)
|
||||
--滤掉分隔符
|
||||
local hex = hex:gsub("[%s%p]", ""):upper()
|
||||
return hex:gsub("%x%x", function(c)
|
||||
return string.char(tonumber(c, 16))
|
||||
end)
|
||||
end
|
||||
|
||||
-- 返回字符串tonumber的转义字符串(用来支持超过31位整数的转换)
|
||||
-- @string str 输入字符串
|
||||
-- @return str 转换后的lua 二进制字符串
|
||||
-- @return len 转换了多少个字符
|
||||
-- @usage
|
||||
-- string.toValue("123456") -> "\1\2\3\4\5\6" 6
|
||||
-- string.toValue("123abc") -> "\1\2\3\a\b\c" 6
|
||||
function string.toValue(str)
|
||||
return string.fromHex(str:gsub("%x", "0%1"))
|
||||
end
|
||||
|
||||
--- 返回utf8编码字符串的长度
|
||||
-- @string str utf8编码的字符串,支持中文
|
||||
-- @return number,返回字符串长度
|
||||
-- @usage local cnt = string.utf8Len("中国a"),cnt == 3
|
||||
function string.utf8Len(str)
|
||||
local _, count = string.gsub(str, "[^\128-\193]", "")
|
||||
return count
|
||||
end
|
||||
|
||||
--- 返回utf8编码字符串的单个utf8字符的table
|
||||
-- @string str utf8编码的字符串,支持中文
|
||||
-- @return table,utf8字符串的table
|
||||
-- @usage local t = string.utf8ToTable("中国2018")
|
||||
function string.utf8ToTable(str)
|
||||
local tab = {}
|
||||
for uchar in string.gfind(str, "[%z\1-\127\194-\244][\128-\191]*") do
|
||||
tab[#tab + 1] = uchar
|
||||
end
|
||||
return tab
|
||||
end
|
||||
|
||||
--- 返回字符串的 RFC3986 编码
|
||||
-- @string str 要转换编码的字符串,支持UTF8编码中文
|
||||
-- @return str, RFC3986 编码的字符串
|
||||
-- @usage local str = string.rawurlEncode("####133") ,str == "%23%23%23%23133"
|
||||
-- @usage local str = string.rawurlEncode("中国2018") , str == "%e4%b8%ad%e5%9b%bd2018"
|
||||
function string.rawurlEncode(str)
|
||||
local t = str:utf8ToTable()
|
||||
for i = 1, #t do
|
||||
if #t[i] == 1 then
|
||||
t[i] = string.gsub(string.gsub(t[i], "([^%w_%~%.%- ])", function(c) return string.format("%%%02X", string.byte(c)) end), " ", "%%20")
|
||||
else
|
||||
t[i] = string.gsub(t[i], ".", function(c) return string.format("%%%02X", string.byte(c)) end)
|
||||
end
|
||||
end
|
||||
return table.concat(t)
|
||||
end
|
||||
|
||||
--- 返回字符串的urlEncode编码
|
||||
-- @string str 要转换编码的字符串,支持UTF8编码中文
|
||||
-- @return str,urlEncode编码的字符串
|
||||
-- @usage local str = string.urlEncode("####133") ,str == "%23%23%23%23133"
|
||||
-- @usage local str = string.urlEncode("中国2018") , str == "%e4%b8%ad%e5%9b%bd2018"
|
||||
function string.urlEncode(str)
|
||||
local t = str:utf8ToTable()
|
||||
for i = 1, #t do
|
||||
if #t[i] == 1 then
|
||||
t[i] = string.gsub(string.gsub(t[i], "([^%w_%*%.%- ])", function(c) return string.format("%%%02X", string.byte(c)) end), " ", "+")
|
||||
else
|
||||
t[i] = string.gsub(t[i], ".", function(c) return string.format("%%%02X", string.byte(c)) end)
|
||||
end
|
||||
end
|
||||
return table.concat(t)
|
||||
end
|
||||
|
||||
--- 返回一个迭代器函数,每次调用函数都会返回hash表的排序后的键值对
|
||||
-- @table t 要排序的hash表
|
||||
-- @param f 自定义排序函数
|
||||
-- @return function.
|
||||
-- @usage test = {a=1,f=9,d=2,c=8,b=5}
|
||||
-- @usage for name,line in pairsByKeys(test) do print(name,line) end
|
||||
function table.gsort(t, f)
|
||||
local a = {}
|
||||
for n in pairs(t) do a[#a + 1] = n end
|
||||
table.sort(a, f)
|
||||
local i = 0
|
||||
return function()
|
||||
i = i + 1
|
||||
return a[i], t[a[i]]
|
||||
end
|
||||
end
|
||||
|
||||
--- table.concat的增强版,支持嵌套字符串数组
|
||||
-- @table l 嵌套字符串数组
|
||||
-- @return string
|
||||
-- @usage print(table.rconcat({"a",{" nice "}," and ", {{" long "},{" list "}}}))
|
||||
function table.rconcat(l)
|
||||
if type(l) ~= "table" then return l end
|
||||
local res = {}
|
||||
for i = 1, #l do
|
||||
res[i] =table.rconcat(l[i])
|
||||
end
|
||||
return table.concat(res)
|
||||
end
|
||||
|
||||
--- 返回数字的千位符号格式
|
||||
-- @number num 数字
|
||||
-- @return string,千位符号的数字字符串
|
||||
-- @usage loca s = string.formatNumberThousands(1000) ,s = "1,000"
|
||||
function string.formatNumberThousands(num)
|
||||
local k, formatted
|
||||
formatted = tostring(tonumber(num))
|
||||
while true do
|
||||
formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
|
||||
if k == 0 then break end
|
||||
end
|
||||
return formatted
|
||||
end
|
||||
|
||||
--- 按照指定分隔符分割字符串
|
||||
-- @string str 输入字符串
|
||||
-- @string delimiter 分隔符
|
||||
-- @return 分割后的字符串列表
|
||||
-- @usage "123,456,789":split(',') -> {'123','456','789'}
|
||||
function string.split(str, delimiter)
|
||||
local strlist, tmp = {}, string.byte(delimiter)
|
||||
if delimiter == "" then
|
||||
for i = 1, #str do strlist[i] = str:sub(i, i) end
|
||||
else
|
||||
for substr in string.gmatch(str .. delimiter, "(.-)" .. (((tmp > 96 and tmp < 123) or (tmp > 64 and tmp < 91) or (tmp > 47 and tmp < 58)) and delimiter or "%" .. delimiter)) do
|
||||
table.insert(strlist, substr)
|
||||
end
|
||||
end
|
||||
return strlist
|
||||
end
|
||||
|
||||
-- 和校验
|
||||
-- @string str 需要校验的字符串
|
||||
-- @string number 1为返回1个字节,2为返回2个字节
|
||||
-- @retrun 返回和校验结果
|
||||
-- @usage string.checkSum("1234",1)
|
||||
function string.checkSum(str, num)
|
||||
assert(type(str) == "string", "The first argument is not a string!")
|
||||
local sum = 0
|
||||
for i = 1, #str do
|
||||
sum = sum + str:sub(i, i):byte()
|
||||
end
|
||||
if num == 2 then
|
||||
return sum % 0x10000
|
||||
else
|
||||
return sum % 0x100
|
||||
end
|
||||
end
|
||||
|
||||
--- 判断文件是否存在
|
||||
-- @string path 文件全名,例如:"/lua/call.mp3"
|
||||
-- @return bool,存在为true,不存在为false
|
||||
-- @usage local ex = io.exists("/lua/call.mp3")
|
||||
function io.exists(path)
|
||||
local file = io.open(path, "r")
|
||||
if file then
|
||||
io.close(file)
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
--- 读取文件中的所有内容
|
||||
-- @string path 文件全名,例如:"/lua/call.txt"
|
||||
-- @return string,文件的内容,文件不存在返回nil
|
||||
-- @usage local c = io.readFile("/lua/call.txt")
|
||||
function io.readFile(path)
|
||||
local file = io.open(path, "rb")
|
||||
if file then
|
||||
local content = file:read("*a")
|
||||
io.close(file)
|
||||
return content
|
||||
end
|
||||
end
|
||||
--- 写入文件指定的内容,默认为覆盖二进制模式
|
||||
-- @string path 文件全名,例如:"/lua/call.txt"
|
||||
-- @string content 文件内容
|
||||
-- @string mode 文件写入模式,支持如下几种(默认"w+b"):
|
||||
-- "w"或者"w+b":空文件写入模式,如果文件不存在,则新建文件,然后从起始位置开始写入;如果文件存在,则删除已有内容,然后从起始位置开始写入
|
||||
-- "a"或者"a+b":追加写入模式,如果文件不存在,则新建文件,然后从起始位置开始写入;如果文件存在,则从文件末尾开始追加写入
|
||||
-- @return boolean result,文件写入结果,true表示写入成功;false表示写入失败
|
||||
-- @usage local c = io.writeFile("/lua/call.txt","test")
|
||||
function io.writeFile(path, content, mode)
|
||||
local mode = mode or "w+b"
|
||||
local file = io.open(path, mode)
|
||||
if file then
|
||||
if file:write(content) == nil then return false end
|
||||
io.close(file)
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
--- 将文件路径分解为table信息
|
||||
-- @string path 文件路径全名,例如:"/lua/call.txt"
|
||||
-- @return table,{dirname="/lua/",filename="call.txt",basename="call",extname=".txt"}
|
||||
-- @usage loca p = io.pathInfo("/lua/call.txt")
|
||||
function io.pathInfo(path)
|
||||
local pos = string.len(path)
|
||||
local extpos = pos + 1
|
||||
while pos > 0 do
|
||||
local b = string.byte(path, pos)
|
||||
if b == 46 then -- 46 = char "."
|
||||
extpos = pos
|
||||
elseif b == 47 then -- 47 = char "/"
|
||||
break
|
||||
end
|
||||
pos = pos - 1
|
||||
end
|
||||
|
||||
local dirname = string.sub(path, 1, pos)
|
||||
local filename = string.sub(path, pos + 1)
|
||||
extpos = extpos - pos
|
||||
local basename = string.sub(filename, 1, extpos - 1)
|
||||
local extname = string.sub(filename, extpos)
|
||||
return {
|
||||
dirname = dirname,
|
||||
filename = filename,
|
||||
basename = basename,
|
||||
extname = extname
|
||||
}
|
||||
end
|
||||
--- 返回文件大小
|
||||
-- @string path 文件路径全名,例如:"/lua/call.txt"
|
||||
-- @return number ,文件大小
|
||||
-- @usage locan cnt = io.fileSize("/lua/call.txt")
|
||||
function io.fileSize(path)
|
||||
local size = 0
|
||||
local file = io.open(path, "r")
|
||||
if file then
|
||||
local current = file:seek()
|
||||
size = file:seek("end")
|
||||
file:seek("set", current)
|
||||
io.close(file)
|
||||
end
|
||||
return size
|
||||
end
|
||||
|
||||
--- 返回指定位置读取的字符串
|
||||
-- @string path 文件路径全名,例如:"/lua/call.txt"
|
||||
-- @number offset 要读取的指定位置,相对于文件开头的偏移位置
|
||||
-- @number len 要读取的字节数
|
||||
-- @return string,返回要读取的数据,读取失败返回nil
|
||||
function io.readStream(path, offset, len)
|
||||
local file, str = io.open(path, "r")
|
||||
if file then
|
||||
local current = file:seek()
|
||||
file:seek("set", offset)
|
||||
str = file:read(len)
|
||||
file:seek("set", current)
|
||||
io.close(file)
|
||||
end
|
||||
return str
|
||||
end
|
||||
1
4G/源代码/lib/wdt.lua
Normal file
1
4G/源代码/lib/wdt.lua
Normal file
@@ -0,0 +1 @@
|
||||
module(..., package.seeall)
|
||||
427
4G/源代码/lib/websocket.lua
Normal file
427
4G/源代码/lib/websocket.lua
Normal file
@@ -0,0 +1,427 @@
|
||||
--- 模块功能:websocket客户端
|
||||
-- @module websocket
|
||||
-- @author wendal
|
||||
-- @license MIT
|
||||
-- @copyright OpenLuat.com
|
||||
-- @release 2023.09.18
|
||||
require "utils"
|
||||
require "socket"
|
||||
module(..., package.seeall)
|
||||
|
||||
local magic = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
|
||||
|
||||
local ws = {}
|
||||
ws.__index = ws
|
||||
local function websocket(url, cert)
|
||||
return setmetatable({
|
||||
io = nil,
|
||||
url = url,
|
||||
key = "",
|
||||
wss = "",
|
||||
cert = cert,
|
||||
host = "",
|
||||
port = "",
|
||||
input = "",
|
||||
callbacks = {},
|
||||
send_data = {},
|
||||
send_text = nil,
|
||||
sendsize = 1460,
|
||||
open_callback = false,
|
||||
connected = false,
|
||||
terminated = false,
|
||||
readyState = "CONNECTING"
|
||||
}, ws)
|
||||
end
|
||||
--- 创建 websocket 对象
|
||||
-- @string url websocket服务器的连接地址,格式为ws(或wss)://xxx开头
|
||||
-- @table[opt=nil] cert ssl连接需要的证书配置,cert格式如下:
|
||||
-- {
|
||||
-- caCert = "ca.crt", --CA证书文件(Base64编码 X.509格式),如果存在此参数,则表示客户端会对服务器的证书进行校验;不存在则不校验
|
||||
-- clientCert = "client.crt", --客户端证书文件(Base64编码 X.509格式),服务器对客户端的证书进行校验时会用到此参数
|
||||
-- clientKey = "client.key", --客户端私钥文件(Base64编码 X.509格式)
|
||||
-- clientPassword = "123456", --客户端证书文件密码[可选]
|
||||
-- insist = 1, --证书中的域名校验失败时,是否坚持连接,默认为1,坚持连接,0为不连接
|
||||
-- }
|
||||
-- @return table 返回1个websocket对象
|
||||
-- @usage local ws = websocket.new("ws://121.40.165.18:8800")
|
||||
function new(url, cert)
|
||||
return websocket(url, cert)
|
||||
end
|
||||
|
||||
--- ws:on 注册函数
|
||||
-- @string event 事件,可选值"open","message","close","error","pong"
|
||||
-- @function callback 回调方法,message|error|pong形参是该方法需要的数据。
|
||||
-- @usage mt:on("message",function(message) local print(message)end)
|
||||
function ws:on(event, callback)
|
||||
self.callbacks[event] = callback
|
||||
end
|
||||
|
||||
--- websocket 与 websocket 服务器建立连接
|
||||
-- @number timeout 与websocket服务器建立连接最长超时
|
||||
-- @return bool,true,表示连接成功,false or nil 表示连接失败
|
||||
-- @usage while not ws:connect(20000) do sys.wait(2000) end
|
||||
function ws:connect(timeout)
|
||||
self.wss, self.host, self.port, self.path = self.url:match("(%a+)://([%w%.%-]+):?(%d*)(.*)")
|
||||
self.wss, self.host = self.wss:lower(), self.host:lower()
|
||||
self.port = self.port ~= "" and self.port or (self.wss == "wss" and 443 or 80)
|
||||
if self.wss == "wss" then
|
||||
self.io = socket.tcp(true, self.cert)
|
||||
else
|
||||
self.io = socket.tcp()
|
||||
end
|
||||
if not self.io then
|
||||
log.error("websocket:connect:", "没有可用的TCP通道!")
|
||||
return false
|
||||
end
|
||||
log.info("websocket url:", self.url)
|
||||
if not self.io:connect(self.host, self.port, timeout) then
|
||||
log.error("websocket:connect", "服务器连接失败!")
|
||||
return false
|
||||
end
|
||||
self.key = crypto.base64_encode(math.random(100000000000000, 999999999999999) .. 0, 16)
|
||||
local req = "GET " .. self.path .. " HTTP/1.1\r\nHost: " .. self.host .. ":" .. self.port .. "\r\nConnection: Upgrade\r\nUpgrade: websocket\r\n" .. "Origin: http://" .. self.host ..
|
||||
"\r\nSec-WebSocket-Version: 13\r\n" .. "Sec-WebSocket-Key: " .. self.key .. "\r\n\r\n"
|
||||
if self.io:send(req, tonumber(timeout) or 20000) then
|
||||
local r, s = self.io:recv(tonumber(timeout) or 5000)
|
||||
if not r then
|
||||
self.io:close()
|
||||
log.error("websocket:connect", "与 websocket server 握手超时!")
|
||||
return false
|
||||
end
|
||||
local _, idx, code = s:find("%s(%d+)%s.-\r\n")
|
||||
if code == "101" then
|
||||
local header, accept = {}, self.key .. magic
|
||||
accept = crypto.sha1(accept, #accept):fromHex()
|
||||
accept = crypto.base64_encode(accept, #accept)
|
||||
for k, v in string.gmatch(s:sub(idx + 1, -1), "(.-):%s*(.-)\r\n") do
|
||||
header[k:lower()] = v
|
||||
end
|
||||
if header["sec-websocket-accept"] and header["sec-websocket-accept"] == accept then
|
||||
log.info("websocket:connect", "与 websocket server 握手成功!")
|
||||
self.connected, self.readyState = true, "OPEN"
|
||||
if self.callbacks.open then
|
||||
self.open_callback = true
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
log.error("websocket:connect", "与 websocket server 握手失败!")
|
||||
return false
|
||||
end
|
||||
-- 掩码加密
|
||||
-- mask: 4位长度掩码字符串
|
||||
-- data: 待加密的字符串
|
||||
-- return: 掩码加密后的字符串
|
||||
local function wsmask(mask, data)
|
||||
local i = 0
|
||||
return data:gsub(".", function(c)
|
||||
i = i + 1
|
||||
return string.char(bit.bxor(data:byte(i), mask:byte((i - 1) % 4 + 1)))
|
||||
end)
|
||||
end
|
||||
--- websocket发送帧方法
|
||||
-- @bool fin true表示结束帧,false表示延续帧
|
||||
-- @number opcode 0x0--0xF,其他值非法,代码意义参考websocket手册
|
||||
-- @string data 用户要发送的数据
|
||||
-- @usage self:sendFrame(true, 0x1, "www.openluat.com")
|
||||
function ws:sendFrame(fin, opcode, data)
|
||||
if not self.connected then
|
||||
return
|
||||
end
|
||||
local finbit, maskbit, len = fin and 0x80 or 0, 0x80, #data
|
||||
local frame = pack.pack("b", bit.bor(finbit, opcode))
|
||||
if len < 126 then
|
||||
frame = frame .. pack.pack("b", bit.bor(len, maskbit))
|
||||
elseif len < 0xFFFF then
|
||||
frame = frame .. pack.pack(">bH", bit.bor(126, maskbit), len)
|
||||
else
|
||||
-- frame = frame .. pack.pack(">BL", bit.bor(127, maskbit), len)
|
||||
log.error("ws:sendFrame", "数据长度超过最大值!")
|
||||
return
|
||||
end
|
||||
local mask = pack.pack(">I", os.time())
|
||||
frame = frame .. mask .. wsmask(mask, data)
|
||||
for i = 1, #frame, self.sendsize do
|
||||
if not self.io:send(frame:sub(i, i + self.sendsize - 1)) then
|
||||
break
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
-- websocket 发送用户数据方法
|
||||
-- @string data: 用户要发送的字符串数据
|
||||
-- @bool text: true 数据为文本字符串,nil或false数据为二进制数据。
|
||||
-- @usage self:send("www.openluat.com")
|
||||
-- @usage self:send("www.openluat.com",true)
|
||||
-- @usage self:send(string.fromHex("www.openluat.com"))
|
||||
local function send(ws, data, text)
|
||||
if text then
|
||||
log.info("websocket cleint send:", data:sub(1, 100))
|
||||
ws:sendFrame(true, 0x1, data)
|
||||
else
|
||||
ws:sendFrame(true, 0x2, data)
|
||||
end
|
||||
if ws.callbacks.sent then
|
||||
ws.callbacks.sent()
|
||||
end
|
||||
end
|
||||
-- websocket发送数据包
|
||||
-- @usage self:send(data, is_text)
|
||||
function ws:send(data, text)
|
||||
table.insert(self.send_data, {data, text})
|
||||
sys.publish("WEBSOCKET_SEND_DATA", "send")
|
||||
end
|
||||
|
||||
local function ping(ws)
|
||||
ws:sendFrame(true, 0x9, "")
|
||||
end
|
||||
|
||||
local function pong(ws, data)
|
||||
ws:sendFrame(true, 0xA, data or "")
|
||||
end
|
||||
|
||||
-- websocket发送ping包(已废弃)
|
||||
-- @usage self:ping()
|
||||
function ws:ping() end
|
||||
|
||||
-- websocket发送pong包(已废弃)
|
||||
-- @usage self:pong()
|
||||
function ws:pong() end
|
||||
|
||||
local function uplink(ws)
|
||||
while #ws.send_data > 0 do
|
||||
local tmp = {}
|
||||
for _, v in ipairs(ws.send_data) do
|
||||
table.insert(tmp, v)
|
||||
end
|
||||
ws.send_data = {}
|
||||
for _, v in ipairs(tmp) do
|
||||
send(ws, v[1], v[2])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- 处理 websocket 发过来的数据并解析帧数据
|
||||
-- @return string : 返回解析后的单帧用户数据
|
||||
function ws:recvFrame()
|
||||
uplink(self) -- 有没有待上传数据, 有就全部上报
|
||||
local close_ctrl = "EXIT_TASK" .. self.io.id
|
||||
local r, s, p = self.io:recv(5000, "WEBSOCKET_SEND_DATA")
|
||||
if not r then
|
||||
if s == "timeout" then
|
||||
return false, nil, "WEBSOCKET_OK"
|
||||
elseif s == "WEBSOCKET_SEND_DATA" then
|
||||
if p == "send" then -- 主动上报
|
||||
uplink(self)
|
||||
elseif p == "ping" then -- 本地心跳上行
|
||||
ping(self, "")
|
||||
elseif p == "pong" then -- 服务器心跳下行,马上回应
|
||||
pong(self, "")
|
||||
elseif p == close_ctrl then
|
||||
return false, nil, close_ctrl
|
||||
end
|
||||
return false, nil, "WEBSOCKET_OK"
|
||||
else
|
||||
return false, nil, "Read byte error!"
|
||||
end
|
||||
end
|
||||
if #self.input ~= 0 then
|
||||
s = self.input .. s
|
||||
end
|
||||
local _, firstByte, secondByte = pack.unpack(s:sub(1, 2), "bb")
|
||||
local fin = bit.band(firstByte, 0x80) ~= 0
|
||||
local rsv = bit.band(firstByte, 0x70) ~= 0
|
||||
local opcode = bit.band(firstByte, 0x0f)
|
||||
local isControl = bit.band(opcode, 0x08) ~= 0
|
||||
-- 检查RSV1,RSV2,RSV3 是否为0,客户端不支持扩展
|
||||
if rsv then
|
||||
return false, nil, "服务器正在使用未定义的扩展!"
|
||||
end
|
||||
-- 检查数据是否存在掩码加密
|
||||
local maskbit = bit.band(secondByte, 0x80) ~= 0
|
||||
local length = bit.band(secondByte, 0x7f)
|
||||
if isControl and (length >= 126 or not fin) then
|
||||
return false, nil, "控制帧异常!"
|
||||
end
|
||||
if maskbit then
|
||||
return false, nil, "数据帧被掩码处理过!"
|
||||
end
|
||||
-- 获取载荷长度
|
||||
if length == 126 then
|
||||
-- if not r then return false, nil, "读取帧载荷长度失败!" end
|
||||
_, length = pack.unpack(s:sub(3, 4), ">H")
|
||||
elseif length == 127 then
|
||||
return false, nil, "数据帧长度超过支持范围!"
|
||||
end
|
||||
-- 获取有效载荷数据
|
||||
if length > 0 then
|
||||
-- TODO 支持大于64的包
|
||||
-- log.info("teste", #s, length)
|
||||
if length > 126 then
|
||||
-- r, s = self.io:recv()
|
||||
if #s < length + 4 then
|
||||
self.input = s
|
||||
return true, false, ""
|
||||
end
|
||||
s = s:sub(5, 5 + length - 1)
|
||||
else
|
||||
s = s:sub(3, 3 + length - 1)
|
||||
end
|
||||
-- log.info("s的长度", #s, length)
|
||||
-- if not r then return false, nil, "读取帧有效载荷数据失败!" end
|
||||
end
|
||||
-- 处理切片帧
|
||||
if not fin then -- 切片未完成
|
||||
return true, false, s
|
||||
else -- 未分片帧
|
||||
if opcode < 0x3 then -- 数据帧
|
||||
self.input = ""
|
||||
return true, true, s
|
||||
elseif opcode == 0x8 then -- close
|
||||
local code, reason
|
||||
if #s >= 2 then
|
||||
_, code = pack.unpack(s:sub(1, 2), ">H")
|
||||
end
|
||||
if #s > 2 then
|
||||
reason = s:sub(3)
|
||||
end
|
||||
self.terminated = true
|
||||
-- self:close(code, reason)
|
||||
self.input = ""
|
||||
return false, nil, reason
|
||||
elseif opcode == 0x9 then -- Ping
|
||||
pong(self, s or "")
|
||||
elseif opcode == 0xA then -- Pong
|
||||
if self.callbacks.pong then
|
||||
self.callbacks.pong(s)
|
||||
end
|
||||
end
|
||||
self.input = ""
|
||||
return true, true, nil
|
||||
end
|
||||
end
|
||||
--- 处理 websocket 发过来的数据并拼包
|
||||
-- @return result, boolean: 返回数据的状态 true 为正常, false 为失败
|
||||
-- @return data, string: result为true时为数据,false时为报错信息
|
||||
-- @usage local result, data = ws:recv()
|
||||
function ws:recv()
|
||||
local data = ""
|
||||
while true do
|
||||
local success, final, message = self:recvFrame()
|
||||
-- 数据帧解析错误
|
||||
if not success then
|
||||
return success, message
|
||||
end
|
||||
-- 数据帧分片处理
|
||||
if message then
|
||||
data = data .. message
|
||||
else
|
||||
data = "" -- 数据帧包含控制帧处理
|
||||
end
|
||||
-- 数据帧处理完成
|
||||
if final and message then
|
||||
break
|
||||
end
|
||||
end
|
||||
if self.callbacks.message then
|
||||
self.callbacks.message(data)
|
||||
end
|
||||
return true, data
|
||||
end
|
||||
|
||||
--- 关闭 websocket 与服务器的链接
|
||||
-- @number code 1000或1002等,请参考websocket标准
|
||||
-- @string reason 关闭原因
|
||||
-- @return nil
|
||||
-- @usage ws:close()
|
||||
-- @usage ws:close(1002,"协议错误")
|
||||
function ws:close(code, reason)
|
||||
-- 1000 "normal closure" status code
|
||||
self.readyState = "CLOSING"
|
||||
if self.terminated then
|
||||
log.error("ws:close server code:", code, reason)
|
||||
elseif self.io.connected then
|
||||
if code == nil and reason ~= nil then
|
||||
code = 1000
|
||||
end
|
||||
local data = ""
|
||||
if code ~= nil then
|
||||
data = pack.pack(">H", code)
|
||||
end
|
||||
if reason ~= nil then
|
||||
data = data .. reason
|
||||
end
|
||||
self.terminated = true
|
||||
self:sendFrame(true, 0x8, data)
|
||||
end
|
||||
self.io:close()
|
||||
self.readyState, self.connected = "CLOSED", false
|
||||
if self.callbacks.close then
|
||||
self.callbacks.close(code or 1001)
|
||||
end
|
||||
self.input = ""
|
||||
end
|
||||
--- 主动退出一个指定的websocket任务
|
||||
-- @传入一个websocket对象
|
||||
-- @return nil
|
||||
-- @usage wesocket.exit(ws)
|
||||
function exit(ws)
|
||||
sys.publish("WEBSOCKET_SEND_DATA", "EXIT_TASK" .. ws.io.id)
|
||||
end
|
||||
--- 获取websocket当前状态
|
||||
-- @return string,状态值("CONNECTING","OPEN","CLOSING","CLOSED")
|
||||
-- @usage ws:state()
|
||||
function ws:state()
|
||||
return self.readyState
|
||||
end
|
||||
--- 获取websocket与服务器连接状态
|
||||
-- @return boolean: true 连接成功,其他值连接失败
|
||||
-- @usage ws:online()
|
||||
function ws:online()
|
||||
return self.connected
|
||||
end
|
||||
--- websocket 需要在任务中启动,带自动重连,支持心跳协议
|
||||
-- @number[opt=nil] keepAlive websocket心跳包,建议30秒
|
||||
-- @function[opt=nil] proc 处理服务器下发消息的函数
|
||||
-- @number[opt=1000] reconnTime 断开链接后的重连时间
|
||||
-- @return nil
|
||||
-- @usage sys.taskInit(ws.start,ws,30)
|
||||
-- @usage sys.taskInit(ws.start,ws,30,function(msg)u1:send(msg) end)
|
||||
function ws:start(keepAlive, proc, reconnTime)
|
||||
reconnTime = tonumber(reconnTime) and reconnTime * 1000 or 1000
|
||||
keepAlivetimer = sys.timerLoopStart(self.ping, 30000, self)
|
||||
while true do
|
||||
while not socket.isReady() do
|
||||
sys.wait(1000)
|
||||
end
|
||||
if self:connect() then
|
||||
if self.open_callback == true then
|
||||
self.callbacks.open()
|
||||
self.open_callback = false
|
||||
end
|
||||
local close_ctrl = "EXIT_TASK" .. self.io.id
|
||||
repeat
|
||||
local r, message = self:recv()
|
||||
if r then
|
||||
if type(proc) == "function" then
|
||||
proc(message)
|
||||
end
|
||||
elseif message == close_ctrl then
|
||||
self:close()
|
||||
sys.timerStop(keepAlivetimer)
|
||||
if self.io.id ~= nil then
|
||||
self = nil
|
||||
end
|
||||
return true
|
||||
elseif not r and message ~= "WEBSOCKET_OK" then
|
||||
log.error('ws recv error', message)
|
||||
end
|
||||
until not r and message ~= "WEBSOCKET_OK"
|
||||
end
|
||||
self:close()
|
||||
log.info("websocket:Start", "与 websocket Server 的连接已断开!")
|
||||
sys.wait(reconnTime)
|
||||
end
|
||||
end
|
||||
|
||||
642
4G/源代码/lib/wifiRil.lua
Normal file
642
4G/源代码/lib/wifiRil.lua
Normal file
@@ -0,0 +1,642 @@
|
||||
--- 模块功能:esp8266 wifi模块AT命令交互管理
|
||||
-- @module wifiRil
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2017.02.13
|
||||
-- require "uart"
|
||||
-- require "rtos"
|
||||
require "sys"
|
||||
require "log"
|
||||
module(..., package.seeall)
|
||||
|
||||
local UART_ID = 1
|
||||
|
||||
--加载常用的全局函数至本地
|
||||
local vwrite = uart.write
|
||||
local vread = uart.read
|
||||
|
||||
--是否为透传模式,true为透传模式,false或者nil为非透传模式
|
||||
--默认非透传模式
|
||||
local transparentmode
|
||||
--透传模式下,虚拟串口数据接收的处理函数
|
||||
local rcvfunc
|
||||
|
||||
local uartswitch
|
||||
--cipsend执行完毕的flag
|
||||
local cipsendflag
|
||||
--执行AT命令后1分钟无反馈,判定at命令执行失败,则重启软件
|
||||
local TIMEOUT,DATA_TIMEOUT = 120000,120000
|
||||
|
||||
--AT命令的应答类型
|
||||
--NORESULT:收到的应答数据当做urc通知处理,如果发送的AT命令不处理应答或者没有设置类型,默认为此类型
|
||||
--NUMBERIC:纯数字类型;例如发送AT+CGSN命令,应答的内容为:862991527986589\r\nOK,此类型指的是862991527986589这一部分为纯数字类型
|
||||
--SLINE:有前缀的单行字符串类型;例如发送AT+CSQ命令,应答的内容为:+CSQ: 23,99\r\nOK,此类型指的是+CSQ: 23,99这一部分为单行字符串类型
|
||||
--MLINE:有前缀的多行字符串类型;例如发送AT+CMGR=5命令,应答的内容为:+CMGR: 0,,84\r\n0891683108200105F76409A001560889F800087120315123842342050003590404590D003A59\r\nOK,此类型指的是OK之前为多行字符串类型
|
||||
--STRING:无前缀的字符串类型,例如发送AT+ATWMFT=99命令,应答的内容为:SUCC\r\nOK,此类型指的是SUCC
|
||||
--SPECIAL:特殊类型,需要针对AT命令做特殊处理,例如CIPSEND、CIPCLOSE、CIFSR
|
||||
local NORESULT, NUMBERIC, SLINE, MLINE, STRING, SPECIAL = 0, 1, 2, 3, 4, 10
|
||||
|
||||
--AT命令的应答类型表,预置了如下几项
|
||||
local RILCMD = {
|
||||
["+CSQ"] = 2,
|
||||
["+MUID"] = 2,
|
||||
["+CGSN"] = 1,
|
||||
["+WISN"] = 4,
|
||||
["+CIMI"] = 1,
|
||||
["+CCID"] = 1,
|
||||
["+CGATT"] = 2,
|
||||
["+CCLK"] = 2,
|
||||
["+ATWMFT"] = 4,
|
||||
["+CMGR"] = 3,
|
||||
["+CMGS"] = 2,
|
||||
["+CPBF"] = 3,
|
||||
["+CPBR"] = 3,
|
||||
['+CLCC'] = 3,
|
||||
['+CNUM'] = 3,
|
||||
["+CIPSEND"] = 10,
|
||||
["+CIPCLOSE"] = 10,
|
||||
["+SSLINIT"] = 10,
|
||||
["+SSLCERT"] = 10,
|
||||
["+SSLCREATE"] = 10,
|
||||
["+SSLCONNECT"] = 10,
|
||||
["+SSLSEND"] = 10,
|
||||
["+SSLDESTROY"] = 10,
|
||||
["+SSLTERM"] = 10,
|
||||
["+CIFSR"] = 10,
|
||||
["+CTFSGETID"] = 2,
|
||||
["+CTFSDECRYPT"] = 2,
|
||||
["+CTFSAUTH"] = 2,
|
||||
["+ALIPAYOPEN"] = 2,
|
||||
["+ALIPAYREP"] = 2,
|
||||
["+ALIPAYPINFO"] = 2,
|
||||
["+ALIPAYACT"] = 2,
|
||||
["+ALIPAYDID"] = 2,
|
||||
["+ALIPAYSIGN"] = 2
|
||||
}
|
||||
|
||||
--radioready:AT命令通道是否准备就绪
|
||||
--delaying:执行完某些AT命令前,需要延时一段时间,才允许执行这些AT命令;此标志表示是否在延时状态
|
||||
local radioready, delaying = false
|
||||
|
||||
--AT命令队列
|
||||
local cmdqueue = {
|
||||
"ATE0",
|
||||
-- "AT+SYSLOG=0",
|
||||
'AT+CIPSNTPCFG=1,8,"cn.ntp.org.cn","ntp.sjtu.edu.cn"',
|
||||
}
|
||||
--当前正在执行的AT命令,参数,反馈回调,延迟执行时间,命令头,类型,反馈格式
|
||||
local currcmd, currarg, currsp, curdelay, cmdhead, cmdtype, rspformt, cmdRspParam
|
||||
--反馈结果,中间信息,结果信息
|
||||
local result, interdata, respdata
|
||||
|
||||
local sslCreating
|
||||
|
||||
--ril会出现三种情况:
|
||||
--发送AT命令,收到应答
|
||||
--发送AT命令,命令超时没有应答
|
||||
--底层软件主动上报的通知,下文我们简称为urc
|
||||
--[[
|
||||
函数名:atimeout
|
||||
功能 :发送AT命令,命令超时没有应答的处理
|
||||
参数 :无
|
||||
返回值:无
|
||||
]]
|
||||
local function atimeout()
|
||||
--重启软件
|
||||
sys.restart("wifiRil.atimeout_" .. (currcmd or ""))
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:defrsp
|
||||
功能 :AT命令的默认应答处理。如果没有定义某个AT的应答处理函数,则会走到本函数
|
||||
参数 :
|
||||
cmd:此应答对应的AT命令
|
||||
success:AT命令执行结果,true或者false
|
||||
response:AT命令的应答中的执行结果字符串
|
||||
intermediate:AT命令的应答中的中间信息
|
||||
返回值:无
|
||||
]]
|
||||
local function defrsp(cmd, success, response, intermediate)
|
||||
log.info("wifiRil.defrsp", cmd, success, response, intermediate)
|
||||
end
|
||||
|
||||
--AT命令的应答处理表
|
||||
local rsptable = {}
|
||||
setmetatable(rsptable, {
|
||||
__index = function()
|
||||
return defrsp
|
||||
end
|
||||
})
|
||||
|
||||
--自定义的AT命令应答格式表,当AT命令应答为STRING格式时,用户可以进一步定义这里面的格式
|
||||
local formtab = {}
|
||||
|
||||
---注册某个AT命令应答的处理函数
|
||||
-- @param head 此应答对应的AT命令头,去掉了最前面的AT两个字符
|
||||
-- @param fnc AT命令应答的处理函数
|
||||
-- @param typ AT命令的应答类型,取值范围NORESULT,NUMBERIC,SLINE,MLINE,STRING,SPECIAL
|
||||
-- @param formt typ为STRING时,进一步定义STRING中的详细格式
|
||||
-- @return bool ,成功返回true,失败false
|
||||
-- @usage wifiRil.regRsp("+CSQ", rsp)
|
||||
function regRsp(head, fnc, typ, formt)
|
||||
--没有定义应答类型
|
||||
if typ == nil then
|
||||
rsptable[head] = fnc
|
||||
return true
|
||||
end
|
||||
--定义了合法应答类型
|
||||
if typ == 0 or typ == 1 or typ == 2 or typ == 3 or typ == 4 or typ == 10 then
|
||||
--如果AT命令的应答类型已存在,并且与新设置的不一致
|
||||
if RILCMD[head] and RILCMD[head] ~= typ then
|
||||
return false
|
||||
end
|
||||
--保存
|
||||
RILCMD[head] = typ
|
||||
rsptable[head] = fnc
|
||||
formtab[head] = formt
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:rsp
|
||||
功能 :AT命令的应答处理
|
||||
参数 :无
|
||||
返回值:无
|
||||
]]
|
||||
local function rsp()
|
||||
--停止应答超时定时器
|
||||
sys.timerStopAll(atimeout)
|
||||
|
||||
--如果发送AT命令时已经同步指定了应答处理函数
|
||||
if currsp then
|
||||
currsp(currcmd, result, respdata, interdata)
|
||||
--用户注册的应答处理函数表中找到处理函数
|
||||
else
|
||||
rsptable[cmdhead](currcmd, result, respdata, interdata, cmdRspParam)
|
||||
end
|
||||
--重置全局变量
|
||||
if cmdhead == "+CIPSEND" and cipsendflag then
|
||||
return
|
||||
end
|
||||
currcmd, currarg, currsp, curdelay, cmdhead, cmdtype, rspformt = nil
|
||||
result, interdata, respdata = nil
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:defurc
|
||||
功能 :urc的默认处理。如果没有定义某个urc的应答处理函数,则会走到本函数
|
||||
参数 :
|
||||
data:urc内容
|
||||
返回值:无
|
||||
]]
|
||||
local function defurc(data)
|
||||
log.info("wifiRil.defurc", data)
|
||||
end
|
||||
|
||||
--urc的处理表
|
||||
local urctable = {}
|
||||
setmetatable(urctable, {
|
||||
__index = function()
|
||||
return defurc
|
||||
end
|
||||
})
|
||||
|
||||
--- 注册某个urc的处理函数
|
||||
-- @param prefix urc前缀,最前面的连续字符串,包含+、大写字符、数字的组合
|
||||
-- @param handler urc的处理函数
|
||||
-- @return 无
|
||||
-- @usage wifiRil.regUrc("+CREG", neturc)
|
||||
function regUrc(prefix, handler)
|
||||
urctable[prefix] = handler
|
||||
end
|
||||
|
||||
--- 解注册某个urc的处理函数
|
||||
-- @param prefix urc前缀,最前面的连续字符串,包含+、大写字符、数字的组合
|
||||
-- @return 无
|
||||
-- @usage deRegUrc("+CREG")
|
||||
function deRegUrc(prefix)
|
||||
urctable[prefix] = nil
|
||||
end
|
||||
|
||||
--“数据过滤器”,虚拟串口收到的数据时,首先需要调用此函数过滤处理一下
|
||||
local urcfilter
|
||||
|
||||
--[[
|
||||
函数名:urc
|
||||
功能 :urc处理
|
||||
参数 :
|
||||
data:urc数据
|
||||
返回值:无
|
||||
]]
|
||||
local function urc(data, cmd)
|
||||
--AT通道准备就绪
|
||||
if data == "ready" then
|
||||
radioready = true
|
||||
else
|
||||
local prefix = string.match(data, "([%+%*]*[%a%d& ]+)")
|
||||
-- 执行prefix的urc处理函数,返回数据过滤器
|
||||
urcfilter = urctable[prefix](data, prefix, cmd)
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:procatc
|
||||
功能 :处理虚拟串口收到的数据
|
||||
参数 :
|
||||
data:收到的数据
|
||||
返回值:无
|
||||
]]
|
||||
local function procatc(data)
|
||||
log.info("wifiRil.proatc", data)
|
||||
--如果命令的应答是多行字符串格式
|
||||
if interdata and cmdtype == MLINE then
|
||||
--不出现OK\r\n,则认为应答还未结束
|
||||
if data ~= "OK\r\n" then
|
||||
--去掉最后的\r\n
|
||||
if string.find(data, "\r\n", -2) then
|
||||
data = string.sub(data, 1, -3)
|
||||
end
|
||||
--拼接到中间数据
|
||||
interdata = interdata .. "\r\n" .. data
|
||||
return
|
||||
end
|
||||
end
|
||||
--如果存在“数据过滤器”
|
||||
if urcfilter then
|
||||
data, urcfilter = urcfilter(data)
|
||||
end
|
||||
--去掉最后的\r\n
|
||||
if not data:find("+CIPRECVDATA") then
|
||||
if string.find(data, "\r\n", -2) then
|
||||
data = string.sub(data, 1, -3)
|
||||
end
|
||||
else
|
||||
local len, tmp = data:match("%+CIPRECVDATA:(%d+),(.+)")
|
||||
if len + 2 == #tmp and string.find(data, "\r\n", -2) then
|
||||
data = string.sub(data, 1, -3)
|
||||
end
|
||||
end
|
||||
--数据为空
|
||||
if data == "" then
|
||||
return
|
||||
end
|
||||
--当前无命令在执行则判定为urc
|
||||
if currcmd == nil then
|
||||
urc(data)
|
||||
return
|
||||
end
|
||||
|
||||
local isurc = false
|
||||
|
||||
--一些特殊的错误信息,转化为ERROR统一处理
|
||||
if string.find(data, "^%+CMS ERROR:") or string.find(data, "^%+CME ERROR:") or (data == "CONNECT FAIL" and currcmd and string.match(currcmd, "CIPSTART")) then
|
||||
data = "ERROR"
|
||||
end
|
||||
if sslCreating and data=="+PDP: DEACT" and tonumber(string.match(rtos.get_version(),"Luat_V(%d+)_"))<31 then
|
||||
sys.publish("SSL_DNS_PARSE_PDP_DEACT")
|
||||
end
|
||||
|
||||
if cmdhead == "+CIPSEND" and data == "SEND OK" then
|
||||
result = true
|
||||
respdata = data
|
||||
cipsendflag = false
|
||||
elseif cmdhead == "+CIPSEND" and data == "OK" then
|
||||
result = true
|
||||
respdata = data
|
||||
cipsendflag = true
|
||||
--执行成功的应答
|
||||
elseif data == "OK" or data == "SHUT OK" then
|
||||
result = true
|
||||
respdata = data
|
||||
--执行失败的应答
|
||||
elseif data == "ERROR" or data == "NO ANSWER" or data == "NO DIALTONE" then
|
||||
result = false
|
||||
respdata = data
|
||||
--需要继续输入参数的AT命令应答
|
||||
elseif data == ">" then
|
||||
--发送短信
|
||||
if cmdhead == "+CMGS" then
|
||||
log.info("wifiRil.procatc.send", currarg)
|
||||
vwrite(UART_ID, currarg, "\026")
|
||||
--发送数据
|
||||
elseif cmdhead == "+CIPSEND" or cmdhead == "+SSLSEND" or cmdhead == "+SSLCERT" then
|
||||
log.info("wifiRil.procatc.send", "first 200 bytes", currarg:sub(1,200))
|
||||
vwrite(UART_ID, currarg)
|
||||
elseif cmdhead == "+SYSFLASH" then
|
||||
log.info("wifiRil.sysflash.send", "first 200 bytes", currarg:sub(1,200))
|
||||
vwrite(UART_ID, currarg)
|
||||
else
|
||||
log.error("error promot cmd:", currcmd)
|
||||
end
|
||||
else
|
||||
--无类型
|
||||
if cmdtype == NORESULT then
|
||||
isurc = true
|
||||
--全数字类型
|
||||
elseif cmdtype == NUMBERIC then
|
||||
local numstr = string.match(data, "(%x+)")
|
||||
if numstr == data then
|
||||
interdata = data
|
||||
else
|
||||
isurc = true
|
||||
end
|
||||
--字符串类型
|
||||
elseif cmdtype == STRING then
|
||||
--进一步检查格式
|
||||
if string.match(data, rspformt or "^.+$") then
|
||||
interdata = data
|
||||
else
|
||||
isurc = true
|
||||
end
|
||||
elseif cmdtype == SLINE or cmdtype == MLINE then
|
||||
if interdata == nil and string.find(data, cmdhead) == 1 then
|
||||
interdata = data
|
||||
else
|
||||
isurc = true
|
||||
end
|
||||
--特殊处理
|
||||
elseif cmdhead == "+CIFSR" then
|
||||
local s = string.match(data, "%d+%.%d+%.%d+%.%d+")
|
||||
if s ~= nil then
|
||||
interdata = s
|
||||
result = true
|
||||
else
|
||||
isurc = true
|
||||
end
|
||||
--特殊处理
|
||||
elseif cmdhead == "+CIPSEND" or cmdhead == "+CIPCLOSE" then
|
||||
local keystr = cmdhead == "+CIPSEND" and "SEND" or "CLOSE"
|
||||
local lid, res = string.match(data, "(%d), *([%u%d :]+)")
|
||||
|
||||
if data:match("^%d, *CLOSED$") then
|
||||
isurc = true
|
||||
elseif lid and res then
|
||||
if (string.find(res, keystr) == 1 or string.find(res, "TCP ERROR") == 1 or string.find(res, "UDP ERROR") == 1 or string.find(data, "DATA ACCEPT")) and (lid == string.match(currcmd, "=(%d)")) then
|
||||
result = data:match("ERROR") == nil
|
||||
respdata = data
|
||||
else
|
||||
isurc = true
|
||||
end
|
||||
elseif data == "+PDP: DEACT" then
|
||||
result = true
|
||||
respdata = data
|
||||
elseif data:match("^Recv %d+ bytes$") then
|
||||
result = true
|
||||
respdata = data
|
||||
else
|
||||
isurc = true
|
||||
end
|
||||
elseif cmdhead == "+SSLINIT" or cmdhead == "+SSLCERT" or cmdhead == "+SSLCREATE" or cmdhead == "+SSLCONNECT" or cmdhead == "+SSLSEND" or cmdhead == "+SSLDESTROY" or cmdhead == "+SSLTERM" then
|
||||
if string.match(data, "^SSL&%d, *CLOSED") or string.match(data, "^SSL&%d, *ERROR") or string.match(data, "SSL&%d,CONNECT ERROR") then
|
||||
isurc = true
|
||||
elseif string.match(data, "^SSL&%d,") then
|
||||
respdata = data
|
||||
if string.match(data, "ERROR") then
|
||||
result = false
|
||||
else
|
||||
result = true
|
||||
end
|
||||
if cmdhead == "+SSLCREATE" then
|
||||
sslCreating = false
|
||||
end
|
||||
else
|
||||
isurc = true
|
||||
end
|
||||
else
|
||||
isurc = true
|
||||
end
|
||||
end
|
||||
-- urc处理
|
||||
if isurc then
|
||||
urc(data, currcmd)
|
||||
--应答处理
|
||||
elseif result ~= nil then
|
||||
rsp()
|
||||
end
|
||||
end
|
||||
|
||||
--是否在读取虚拟串口数据
|
||||
local readat = false
|
||||
|
||||
--[[
|
||||
函数名:getcmd
|
||||
功能 :解析一条AT命令
|
||||
参数 :
|
||||
item:AT命令
|
||||
返回值:当前AT命令的内容
|
||||
]]
|
||||
local function getcmd(item)
|
||||
local cmd, arg, rsp, delay, rspParam
|
||||
--命令是string类型
|
||||
if type(item) == "string" then
|
||||
--命令内容
|
||||
cmd = item
|
||||
--命令是table类型
|
||||
elseif type(item) == "table" then
|
||||
--命令内容
|
||||
cmd = item.cmd
|
||||
--命令参数
|
||||
arg = item.arg
|
||||
--命令应答处理函数
|
||||
rsp = item.rsp
|
||||
--命令延时执行时间
|
||||
delay = item.delay
|
||||
--命令携带的参数,执行回调时传入此参数
|
||||
rspParam = item.rspParam
|
||||
else
|
||||
log.info("wifiRil.getcmd", "getpack unknown item")
|
||||
return
|
||||
end
|
||||
-- 命令前缀
|
||||
local head = string.match(cmd, "AT([%+%*]*%u+)")
|
||||
|
||||
if head == nil then
|
||||
log.error("wifiRil.getcmd", "request error cmd:", cmd)
|
||||
return
|
||||
end
|
||||
--这两个命令必须有参数
|
||||
if head == "+CMGS" or head == "+CIPSEND" then -- 必须有参数
|
||||
if arg == nil or arg == "" then
|
||||
log.error("wifiRil.getcmd", "request error no arg", head)
|
||||
return
|
||||
end
|
||||
end
|
||||
--赋值全局变量
|
||||
currcmd = cmd
|
||||
currarg = arg
|
||||
currsp = rsp
|
||||
curdelay = delay
|
||||
cmdhead = head
|
||||
cmdRspParam = rspParam
|
||||
cmdtype = RILCMD[head] or NORESULT
|
||||
rspformt = formtab[head]
|
||||
|
||||
return currcmd
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:sendat
|
||||
功能 :发送AT命令
|
||||
参数 :无
|
||||
返回值:无
|
||||
]]
|
||||
local function sendat()
|
||||
-- AT通道未准备就绪、正在读取虚拟串口数据、有AT命令在执行或者队列无命令、正延时发送某条AT
|
||||
if not radioready or readat or currcmd ~= nil or delaying then
|
||||
return
|
||||
end
|
||||
local item
|
||||
|
||||
while true do
|
||||
--队列无AT命令
|
||||
if #cmdqueue == 0 then
|
||||
return
|
||||
end
|
||||
--读取第一条命令
|
||||
item = table.remove(cmdqueue, 1)
|
||||
--解析命令
|
||||
getcmd(item)
|
||||
--需要延迟发送
|
||||
if curdelay then
|
||||
--启动延迟发送定时器
|
||||
sys.timerStart(delayfunc, curdelay)
|
||||
--清除全局变量
|
||||
currcmd, currarg, currsp, curdelay, cmdhead, cmdtype, rspformt, cmdRspParam = nil
|
||||
item.delay = nil
|
||||
--设置延迟发送标志
|
||||
delaying = true
|
||||
--把命令重新插入命令队列的队首
|
||||
table.insert(cmdqueue, 1, item)
|
||||
return
|
||||
end
|
||||
|
||||
if currcmd ~= nil then
|
||||
break
|
||||
end
|
||||
end
|
||||
--启动AT命令应答超时定时器
|
||||
if currcmd:match("^AT%+CIPSTART") or currcmd:match("^AT%+CIPSEND") or currcmd:match("^AT%+SSLCREATE") or currcmd:match("^AT%+SSLCONNECT") or currcmd:match("^AT%+SSLSEND") then
|
||||
sys.timerStart(atimeout,DATA_TIMEOUT)
|
||||
else
|
||||
sys.timerStart(atimeout, TIMEOUT)
|
||||
end
|
||||
|
||||
if currcmd:match("^AT%+SSLCREATE") then
|
||||
sslCreating = true
|
||||
end
|
||||
|
||||
log.info("wifiRil.sendat", currcmd)
|
||||
--向虚拟串口中发送AT命令
|
||||
vwrite(UART_ID, currcmd .. "\r\n")
|
||||
end
|
||||
|
||||
-- 延时执行某条AT命令的定时器回调
|
||||
-- @return 无
|
||||
-- @usage wifiRil.delayfunc()
|
||||
function delayfunc()
|
||||
--清除延时标志
|
||||
delaying = nil
|
||||
--执行AT命令发送
|
||||
sendat()
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:atcreader
|
||||
功能 :“AT命令的虚拟串口数据接收消息”的处理函数,当虚拟串口收到数据时,会走到此函数中
|
||||
参数 :无
|
||||
返回值:无
|
||||
]]
|
||||
local function atcreader()
|
||||
local s
|
||||
|
||||
if not transparentmode then
|
||||
readat = true
|
||||
end
|
||||
-- 循环读取虚拟串口收到的数据
|
||||
while true do
|
||||
--每次读取一行
|
||||
s = vread(UART_ID, "*l", 0)
|
||||
if string.len(s) ~= 0 then
|
||||
if transparentmode then
|
||||
--透传模式下直接转发数据
|
||||
rcvfunc(s)
|
||||
else
|
||||
--非透传模式下处理收到的数据
|
||||
procatc(s)
|
||||
end
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
if not transparentmode then
|
||||
readat = false
|
||||
--数据处理完以后继续执行AT命令发送
|
||||
sendat()
|
||||
end
|
||||
end
|
||||
|
||||
--- 发送AT命令到底层软件
|
||||
-- @param cmd AT命令内容
|
||||
-- @param arg AT命令参数,例如AT+CMGS=12命令执行后,接下来会发送此参数;AT+CIPSEND=14命令执行后,接下来会发送此参数
|
||||
-- @param onrsp AT命令应答的处理函数,只是当前发送的AT命令应答有效,处理之后就失效了
|
||||
-- @param delay 延时delay毫秒后,才发送此AT命令
|
||||
-- @return 无
|
||||
-- @usage wifiRil.request("AT+CENG=1,1")
|
||||
-- @usage wifiRil.request("AT+CRSM=214,28539,0,0,12,\"64f01064f03064f002fffff\"", nil, crsmResponse)
|
||||
function request(cmd, arg, onrsp, delay, param)
|
||||
if transparentmode then
|
||||
return
|
||||
end
|
||||
--插入缓冲队列
|
||||
if arg or onrsp or delay or formt or param then
|
||||
table.insert(cmdqueue, {
|
||||
cmd = cmd,
|
||||
arg = arg,
|
||||
rsp = onrsp,
|
||||
delay = delay,
|
||||
rspParam = param
|
||||
})
|
||||
else
|
||||
table.insert(cmdqueue, cmd)
|
||||
end
|
||||
--执行AT命令发送
|
||||
sendat()
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:setransparentmode
|
||||
功能 :AT命令通道设置为透传模式
|
||||
参数 :
|
||||
fnc:透传模式下,虚拟串口数据接收的处理函数
|
||||
返回值:无
|
||||
注意:透传模式和非透传模式,只支持开机的第一次设置,不支持中途切换
|
||||
]]
|
||||
function setransparentmode(fnc)
|
||||
transparentmode, rcvfunc = true, fnc
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:sendtransparentdata
|
||||
功能 :透传模式下发送数据
|
||||
参数 :
|
||||
data:数据
|
||||
返回值:成功返回true,失败返回nil
|
||||
]]
|
||||
function sendtransparentdata(data)
|
||||
if not transparentmode then
|
||||
return
|
||||
end
|
||||
vwrite(UART_ID, data)
|
||||
return true
|
||||
end
|
||||
|
||||
function setDataTimeout(tm)
|
||||
DATA_TIMEOUT = (tm<120000 and 120000 or tm)
|
||||
end
|
||||
uart.setup(UART_ID, 115200, 8, uart.PAR_NONE, uart.STOP_1)
|
||||
--注册“AT命令的虚拟串口数据接收消息”的处理函数
|
||||
uart.on(UART_ID, "receive", atcreader)
|
||||
57
4G/源代码/lib/wifiScan.lua
Normal file
57
4G/源代码/lib/wifiScan.lua
Normal file
@@ -0,0 +1,57 @@
|
||||
--- 模块功能:wifi扫描功能
|
||||
-- 支持wifi热点扫描
|
||||
-- @module wifiScan
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2020.5.21
|
||||
|
||||
require"sys"
|
||||
module(..., package.seeall)
|
||||
|
||||
local sCbFnc
|
||||
|
||||
--- wifi扫描热点请求
|
||||
-- @function cbFnc 扫描到热点返回或者超时未返回的回调函数,回调函数的调用形式为:
|
||||
-- cbFnc(result,cnt,info)
|
||||
-- result:true或者false,true表示扫描成功,false表示扫描失败或者超时失败
|
||||
-- cnt:number类型,表示扫描到的热点个数
|
||||
-- info:table或者nil类型;result为false时,为nil;result为true时,表示扫码到的热点mac和信号信息,table类型,例如:
|
||||
-- {
|
||||
-- ["1a:fe:34:9e:a1:77"] = -63,
|
||||
-- ["8c:be:be:2d:cd:e9"] = -81,
|
||||
-- ["20:4e:7f:82:c2:c4"] = -70,
|
||||
-- }
|
||||
-- @number[opt=10000] timeout 等待扫描热点返回的超时时间,单位毫秒,默认为10秒
|
||||
-- @usage
|
||||
-- wifiScan.request(cbFnc)
|
||||
-- wifiScan.request(cbFnc,5000)
|
||||
function request(cbFnc,timeout)
|
||||
sCbFnc = cbFnc
|
||||
sys.timerStart(sCbFnc,timeout or 10000,false)
|
||||
wifi.getinfo()
|
||||
end
|
||||
|
||||
local function wifiMsg(msg)
|
||||
log.info("wifiScan.wifiMsg",msg.cnt,msg.info,sys.timerIsActive(sCbFnc,false))
|
||||
if sys.timerIsActive(sCbFnc,false) then
|
||||
sys.timerStop(sCbFnc,false)
|
||||
local num,info = msg.cnt,msg.info
|
||||
if num==0 then
|
||||
sCbFnc(false,0)
|
||||
else
|
||||
--9a0074bdb0e8,183,2;40313cd7b4bb,185,4;828917c49d9a,173,2;8107999c460,175,8;c4b548f863e,160,7;
|
||||
log.info("wifi.getinfo",num,info)
|
||||
local tInfo,cnt = {},0
|
||||
for mac,rssi,channel in string.gmatch(info,"(.-),(.-),(.-);") do
|
||||
cnt = cnt+1
|
||||
if mac:len()<12 then mac=string.rep("0",12-mac:len())..mac end
|
||||
tInfo[mac:sub(1,2)..":"..mac:sub(3,4)..":"..mac:sub(5,6)..":"..mac:sub(7,8)..":"..mac:sub(9,10)..":"..mac:sub(11,12)] = tonumber(rssi)
|
||||
end
|
||||
sCbFnc(true,cnt,tInfo)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--注册core上报的rtos.MSG_WIFI消息的处理函数
|
||||
rtos.on(rtos.MSG_WIFI,wifiMsg)
|
||||
Reference in New Issue
Block a user