Files
BR_YKC/4G/源代码/lib/wifiRil.lua
2026-05-21 13:24:05 +08:00

642 lines
19 KiB
Lua
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
--- 模块功能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
}
--radioreadyAT命令通道是否准备就绪
--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命令
successAT命令执行结果true或者false
responseAT命令的应答中的执行结果字符串
intermediateAT命令的应答中的中间信息
返回值:无
]]
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的应答处理函数则会走到本函数
参数
dataurc内容
返回值:无
]]
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处理
参数
dataurc数据
返回值:无
]]
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命令
参数
itemAT命令
返回值当前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)