ril.lua 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  1. --- 虚拟串口AT命令交互管理
  2. -- @module ril
  3. -- @author openLuat
  4. -- @license MIT
  5. -- @copyright openLuat
  6. -- @release 2017.02.13
  7. local base = _G
  8. local table = require "table"
  9. local string = require "string"
  10. local uart = require "uart"
  11. local rtos = require "rtos"
  12. local sys = require "sys"
  13. local log = require "log"
  14. module("ril")
  15. --加载常用的全局函数至本地
  16. local setmetatable = base.setmetatable
  17. local type = base.type
  18. local vwrite = uart.write
  19. local vread = uart.read
  20. --是否为透传模式,true为透传模式,false或者nil为非透传模式
  21. --默认非透传模式
  22. local transparentmode
  23. --透传模式下,虚拟串口数据接收的处理函数
  24. local rcvfunc
  25. --执行AT命令后1分钟无反馈,判定at命令执行失败,则重启软件
  26. local TIMEOUT = 60000
  27. --AT命令的应答类型
  28. --NORESULT:收到的应答数据当做urc通知处理,如果发送的AT命令不处理应答或者没有设置类型,默认为此类型
  29. --NUMBERIC:纯数字类型;例如发送AT+CGSN命令,应答的内容为:862991527986589\r\nOK,此类型指的是862991527986589这一部分为纯数字类型
  30. --SLINE:有前缀的单行字符串类型;例如发送AT+CSQ命令,应答的内容为:+CSQ: 23,99\r\nOK,此类型指的是+CSQ: 23,99这一部分为单行字符串类型
  31. --MLINE:有前缀的多行字符串类型;例如发送AT+CMGR=5命令,应答的内容为:+CMGR: 0,,84\r\n0891683108200105F76409A001560889F800087120315123842342050003590404590D003A59\r\nOK,此类型指的是OK之前为多行字符串类型
  32. --STRING:无前缀的字符串类型,例如发送AT+ATWMFT=99命令,应答的内容为:SUCC\r\nOK,此类型指的是SUCC
  33. --SPECIAL:特殊类型,需要针对AT命令做特殊处理,例如CIPSEND、CIPCLOSE、CIFSR
  34. local NORESULT, NUMBERIC, SLINE, MLINE, STRING, SPECIAL = 0, 1, 2, 3, 4, 10
  35. --AT命令的应答类型表,预置了如下几项
  36. local RILCMD = {
  37. ["+CSQ"] = 2,
  38. ["+CGSN"] = 1,
  39. ["+WISN"] = 4,
  40. ["+CIMI"] = 1,
  41. ["+CCID"] = 1,
  42. ["+CGATT"] = 2,
  43. ["+CCLK"] = 2,
  44. ["+ATWMFT"] = 4,
  45. ["+CMGR"] = 3,
  46. ["+CMGS"] = 2,
  47. ["+CPBF"] = 3,
  48. ["+CPBR"] = 3,
  49. ["+CIPSEND"] = 10,
  50. ["+CIPCLOSE"] = 10,
  51. ["+SSLINIT"] = 10,
  52. ["+SSLCERT"] = 10,
  53. ["+SSLCREATE"] = 10,
  54. ["+SSLCONNECT"] = 10,
  55. ["+SSLSEND"] = 10,
  56. ["+SSLDESTROY"] = 10,
  57. ["+SSLTERM"] = 10,
  58. ["+CIFSR"] = 10,
  59. }
  60. --radioready:AT命令通道是否准备就绪
  61. --delaying:执行完某些AT命令前,需要延时一段时间,才允许执行这些AT命令;此标志表示是否在延时状态
  62. local radioready, delaying = false
  63. --AT命令队列
  64. local cmdqueue = {
  65. "ATE0",
  66. "AT+CMEE=0",
  67. }
  68. --当前正在执行的AT命令,参数,反馈回调,延迟执行时间,命令头,类型,反馈格式
  69. local currcmd, currarg, currsp, curdelay, cmdhead, cmdtype, rspformt
  70. --反馈结果,中间信息,结果信息
  71. local result, interdata, respdata
  72. --ril会出现三种情况:
  73. --发送AT命令,收到应答
  74. --发送AT命令,命令超时没有应答
  75. --底层软件主动上报的通知,下文我们简称为urc
  76. --[[
  77. 函数名:atimeout
  78. 功能 :发送AT命令,命令超时没有应答的处理
  79. 参数 :无
  80. 返回值:无
  81. ]]
  82. local function atimeout()
  83. --重启软件
  84. sys.restart("ril.atimeout_" .. (currcmd or ""))
  85. end
  86. --[[
  87. 函数名:defrsp
  88. 功能 :AT命令的默认应答处理。如果没有定义某个AT的应答处理函数,则会走到本函数
  89. 参数 :
  90. cmd:此应答对应的AT命令
  91. success:AT命令执行结果,true或者false
  92. response:AT命令的应答中的执行结果字符串
  93. intermediate:AT命令的应答中的中间信息
  94. 返回值:无
  95. ]]
  96. local function defrsp(cmd, success, response, intermediate)
  97. log.info("ril.defrsp", cmd, success, response, intermediate)
  98. end
  99. --AT命令的应答处理表
  100. local rsptable = {}
  101. setmetatable(rsptable, {__index = function() return defrsp end})
  102. --自定义的AT命令应答格式表,当AT命令应答为STRING格式时,用户可以进一步定义这里面的格式
  103. local formtab = {}
  104. ---注册某个AT命令应答的处理函数
  105. -- @param head 此应答对应的AT命令头,去掉了最前面的AT两个字符
  106. -- @param fnc AT命令应答的处理函数
  107. -- @param typ AT命令的应答类型,取值范围NORESULT,NUMBERIC,SLINE,MLINE,STRING,SPECIAL
  108. -- @param formt typ为STRING时,进一步定义STRING中的详细格式
  109. -- @return bool ,成功返回true,失败false
  110. -- @usage ril.regrsp("+CSQ", rsp)
  111. function regrsp(head, fnc, typ, formt)
  112. --没有定义应答类型
  113. if typ == nil then
  114. rsptable[head] = fnc
  115. return true
  116. end
  117. --定义了合法应答类型
  118. if typ == 0 or typ == 1 or typ == 2 or typ == 3 or typ == 4 or typ == 10 then
  119. --如果AT命令的应答类型已存在,并且与新设置的不一致
  120. if RILCMD[head] and RILCMD[head] ~= typ then
  121. return false
  122. end
  123. --保存
  124. RILCMD[head] = typ
  125. rsptable[head] = fnc
  126. formtab[head] = formt
  127. return true
  128. else
  129. return false
  130. end
  131. end
  132. --[[
  133. 函数名:rsp
  134. 功能 :AT命令的应答处理
  135. 参数 :无
  136. 返回值:无
  137. ]]
  138. local function rsp()
  139. --停止应答超时定时器
  140. sys.timer_stop(atimeout)
  141. --如果发送AT命令时已经同步指定了应答处理函数
  142. if currsp then
  143. currsp(currcmd, result, respdata, interdata)
  144. --用户注册的应答处理函数表中找到处理函数
  145. else
  146. rsptable[cmdhead](currcmd, result, respdata, interdata)
  147. end
  148. --重置全局变量
  149. currcmd, currarg, currsp, curdelay, cmdhead, cmdtype, rspformt = nil
  150. result, interdata, respdata = nil
  151. end
  152. --[[
  153. 函数名:defurc
  154. 功能 :urc的默认处理。如果没有定义某个urc的应答处理函数,则会走到本函数
  155. 参数 :
  156. data:urc内容
  157. 返回值:无
  158. ]]
  159. local function defurc(data)
  160. log.info("ril.defurc", data)
  161. end
  162. --urc的处理表
  163. local urctable = {}
  164. setmetatable(urctable, {__index = function() return defurc end})
  165. --- 注册某个urc的处理函数
  166. -- @param prefix urc前缀,最前面的连续字符串,包含+、大写字符、数字的组合
  167. -- @param handler urc的处理函数
  168. -- @return 无
  169. -- @usage ril.regurc("+CREG", neturc)
  170. function regurc(prefix, handler)
  171. urctable[prefix] = handler
  172. end
  173. --- 解注册某个urc的处理函数
  174. -- @param prefix urc前缀,最前面的连续字符串,包含+、大写字符、数字的组合
  175. -- @return 无
  176. -- @usage deregurc("+CREG")
  177. function deregurc(prefix)
  178. urctable[prefix] = nil
  179. end
  180. --“数据过滤器”,虚拟串口收到的数据时,首先需要调用此函数过滤处理一下
  181. local urcfilter
  182. --[[
  183. 函数名:urc
  184. 功能 :urc处理
  185. 参数 :
  186. data:urc数据
  187. 返回值:无
  188. ]]
  189. local function urc(data)
  190. --AT通道准备就绪
  191. if data == "RDY" then
  192. radioready = true
  193. else
  194. local prefix = string.match(data, "(%+*[%u%d& ]+)")
  195. --执行prefix的urc处理函数,返回数据过滤器
  196. urcfilter = urctable[prefix](data, prefix)
  197. end
  198. end
  199. --[[
  200. 函数名:procatc
  201. 功能 :处理虚拟串口收到的数据
  202. 参数 :
  203. data:收到的数据
  204. 返回值:无
  205. ]]
  206. local function procatc(data)
  207. log.info("ril.proatc", data)
  208. --如果命令的应答是多行字符串格式
  209. if interdata and cmdtype == MLINE then
  210. --不出现OK\r\n,则认为应答还未结束
  211. if data ~= "OK\r\n" then
  212. --去掉最后的\r\n
  213. if string.find(data, "\r\n", -2) then
  214. data = string.sub(data, 1, -3)
  215. end
  216. --拼接到中间数据
  217. interdata = interdata .. "\r\n" .. data
  218. return
  219. end
  220. end
  221. --如果存在“数据过滤器”
  222. if urcfilter then
  223. data, urcfilter = urcfilter(data)
  224. end
  225. --去掉最后的\r\n
  226. if string.find(data, "\r\n", -2) then
  227. data = string.sub(data, 1, -3)
  228. end
  229. --数据为空
  230. if data == "" then
  231. return
  232. end
  233. --当前无命令在执行则判定为urc
  234. if currcmd == nil then
  235. urc(data)
  236. return
  237. end
  238. local isurc = false
  239. --一些特殊的错误信息,转化为ERROR统一处理
  240. if string.find(data, "^%+CMS ERROR:") or string.find(data, "^%+CME ERROR:") or (data == "CONNECT FAIL" and currcmd and string.match(currcmd, "CIPSTART")) then
  241. data = "ERROR"
  242. end
  243. --执行成功的应答
  244. if data == "OK" or data == "SHUT OK" then
  245. result = true
  246. respdata = data
  247. --执行失败的应答
  248. elseif data == "ERROR" or data == "NO ANSWER" or data == "NO DIALTONE" then
  249. result = false
  250. respdata = data
  251. --需要继续输入参数的AT命令应答
  252. elseif data == "> " then
  253. --发送短信
  254. if cmdhead == "+CMGS" then
  255. log.info("ril.procatc.send", currarg)
  256. vwrite(uart.ATC, currarg, "\026")
  257. --发送数据
  258. elseif cmdhead == "+CIPSEND" or cmdhead == "+SSLSEND" or cmdhead == "+SSLCERT" then
  259. log.info("ril.procatc.send", currarg)
  260. vwrite(uart.ATC, currarg)
  261. else
  262. log.error("error promot cmd:", currcmd)
  263. end
  264. else
  265. --无类型
  266. if cmdtype == NORESULT then
  267. isurc = true
  268. --全数字类型
  269. elseif cmdtype == NUMBERIC then
  270. local numstr = string.match(data, "(%x+)")
  271. if numstr == data then
  272. interdata = data
  273. else
  274. isurc = true
  275. end
  276. --字符串类型
  277. elseif cmdtype == STRING then
  278. --进一步检查格式
  279. if string.match(data, rspformt or "^%w+$") then
  280. interdata = data
  281. else
  282. isurc = true
  283. end
  284. elseif cmdtype == SLINE or cmdtype == MLINE then
  285. if interdata == nil and string.find(data, cmdhead) == 1 then
  286. interdata = data
  287. else
  288. isurc = true
  289. end
  290. --特殊处理
  291. elseif cmdhead == "+CIFSR" then
  292. local s = string.match(data, "%d+%.%d+%.%d+%.%d+")
  293. if s ~= nil then
  294. interdata = s
  295. result = true
  296. else
  297. isurc = true
  298. end
  299. --特殊处理
  300. elseif cmdhead == "+CIPSEND" or cmdhead == "+CIPCLOSE" then
  301. local keystr = cmdhead == "+CIPSEND" and "SEND" or "CLOSE"
  302. local lid, res = string.match(data, "(%d), *([%u%d :]+)")
  303. if lid and res then
  304. 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
  305. result = data:match("ERROR") == nil
  306. respdata = data
  307. else
  308. isurc = true
  309. end
  310. elseif data == "+PDP: DEACT" then
  311. result = true
  312. respdata = data
  313. else
  314. isurc = true
  315. end
  316. elseif cmdhead == "+SSLINIT" or cmdhead == "+SSLCERT" or cmdhead == "+SSLCREATE" or cmdhead == "+SSLCONNECT" or cmdhead == "+SSLSEND" or cmdhead == "+SSLDESTROY" or cmdhead == "+SSLTERM" then
  317. if string.match(data, "^SSL&%d,") then
  318. respdata = data
  319. if string.match(data, "ERROR") then
  320. result = false
  321. else
  322. result = true
  323. end
  324. else
  325. isurc = true
  326. end
  327. else
  328. isurc = true
  329. end
  330. end
  331. --urc处理
  332. if isurc then
  333. urc(data)
  334. --应答处理
  335. elseif result ~= nil then
  336. rsp()
  337. end
  338. end
  339. --是否在读取虚拟串口数据
  340. local readat = false
  341. --[[
  342. 函数名:getcmd
  343. 功能 :解析一条AT命令
  344. 参数 :
  345. item:AT命令
  346. 返回值:当前AT命令的内容
  347. ]]
  348. local function getcmd(item)
  349. local cmd, arg, rsp, delay
  350. --命令是string类型
  351. if type(item) == "string" then
  352. --命令内容
  353. cmd = item
  354. --命令是table类型
  355. elseif type(item) == "table" then
  356. --命令内容
  357. cmd = item.cmd
  358. --命令参数
  359. arg = item.arg
  360. --命令应答处理函数
  361. rsp = item.rsp
  362. --命令延时执行时间
  363. delay = item.delay
  364. else
  365. log.info("ril.getcmd", "getpack unknown item")
  366. return
  367. end
  368. --命令前缀
  369. head = string.match(cmd, "AT([%+%*]*%u+)")
  370. if head == nil then
  371. log.error("ril.getcmd", "request error cmd:", cmd)
  372. return
  373. end
  374. --这两个命令必须有参数
  375. if head == "+CMGS" or head == "+CIPSEND" then -- 必须有参数
  376. if arg == nil or arg == "" then
  377. log.error("ril.getcmd", "request error no arg", head)
  378. return
  379. end
  380. end
  381. --赋值全局变量
  382. currcmd = cmd
  383. currarg = arg
  384. currsp = rsp
  385. curdelay = delay
  386. cmdhead = head
  387. cmdtype = RILCMD[head] or NORESULT
  388. rspformt = formtab[head]
  389. return currcmd
  390. end
  391. --[[
  392. 函数名:sendat
  393. 功能 :发送AT命令
  394. 参数 :无
  395. 返回值:无
  396. ]]
  397. local function sendat()
  398. --AT通道未准备就绪、正在读取虚拟串口数据、有AT命令在执行或者队列无命令、正延时发送某条AT
  399. if not radioready or readat or currcmd ~= nil or delaying then
  400. return
  401. end
  402. local item
  403. while true do
  404. --队列无AT命令
  405. if #cmdqueue == 0 then
  406. return
  407. end
  408. --读取第一条命令
  409. item = table.remove(cmdqueue, 1)
  410. --解析命令
  411. getcmd(item)
  412. --需要延迟发送
  413. if curdelay then
  414. --启动延迟发送定时器
  415. sys.timer_start(delayfunc, curdelay)
  416. --清除全局变量
  417. currcmd, currarg, currsp, curdelay, cmdhead, cmdtype, rspformt = nil
  418. item.delay = nil
  419. --设置延迟发送标志
  420. delaying = true
  421. --把命令重新插入命令队列的队首
  422. table.insert(cmdqueue, 1, item)
  423. return
  424. end
  425. if currcmd ~= nil then
  426. break
  427. end
  428. end
  429. --启动AT命令应答超时定时器
  430. sys.timer_start(atimeout, TIMEOUT)
  431. log.info("ril.sendat", currcmd)
  432. --向虚拟串口中发送AT命令
  433. vwrite(uart.ATC, currcmd .. "\r")
  434. end
  435. --- 延时执行某条AT命令的定时器回调
  436. -- @return 无
  437. -- @usage ril.delayfunc()
  438. function delayfunc()
  439. --清除延时标志
  440. delaying = nil
  441. --执行AT命令发送
  442. sendat()
  443. end
  444. --[[
  445. 函数名:atcreader
  446. 功能 :“AT命令的虚拟串口数据接收消息”的处理函数,当虚拟串口收到数据时,会走到此函数中
  447. 参数 :无
  448. 返回值:无
  449. ]]
  450. local function atcreader()
  451. local s
  452. if not transparentmode then readat = true end
  453. --循环读取虚拟串口收到的数据
  454. while true do
  455. --每次读取一行
  456. s = vread(uart.ATC, "*l", 0)
  457. if string.len(s) ~= 0 then
  458. if transparentmode then
  459. --透传模式下直接转发数据
  460. rcvfunc(s)
  461. else
  462. --非透传模式下处理收到的数据
  463. procatc(s)
  464. end
  465. else
  466. break
  467. end
  468. end
  469. if not transparentmode then
  470. readat = false
  471. --数据处理完以后继续执行AT命令发送
  472. sendat()
  473. end
  474. end
  475. --- 发送AT命令到底层软件
  476. -- @param cmd AT命令内容
  477. -- @param arg AT命令参数,例如AT+CMGS=12命令执行后,接下来会发送此参数;AT+CIPSEND=14命令执行后,接下来会发送此参数
  478. -- @param onrsp AT命令应答的处理函数,只是当前发送的AT命令应答有效,处理之后就失效了
  479. -- @param delay 延时delay毫秒后,才发送此AT命令
  480. -- @return 无
  481. -- @usage ril.request("AT+CENG=1,1")
  482. -- @usage ril.request("AT+CRSM=214,28539,0,0,12,\"64f01064f03064f002fffff\"", nil, crsmResponse)
  483. function request(cmd, arg, onrsp, delay)
  484. if transparentmode then return end
  485. --插入缓冲队列
  486. if arg or onrsp or delay or formt then
  487. table.insert(cmdqueue, {cmd = cmd, arg = arg, rsp = onrsp, delay = delay})
  488. else
  489. table.insert(cmdqueue, cmd)
  490. end
  491. --执行AT命令发送
  492. sendat()
  493. end
  494. --[[
  495. 函数名:setransparentmode
  496. 功能 :AT命令通道设置为透传模式
  497. 参数 :
  498. fnc:透传模式下,虚拟串口数据接收的处理函数
  499. 返回值:无
  500. 注意:透传模式和非透传模式,只支持开机的第一次设置,不支持中途切换
  501. ]]
  502. function setransparentmode(fnc)
  503. transparentmode, rcvfunc = true, fnc
  504. end
  505. --[[
  506. 函数名:sendtransparentdata
  507. 功能 :透传模式下发送数据
  508. 参数 :
  509. data:数据
  510. 返回值:成功返回true,失败返回nil
  511. ]]
  512. function sendtransparentdata(data)
  513. if not transparentmode then return end
  514. vwrite(uart.ATC, data)
  515. return true
  516. end
  517. --注册“AT命令的虚拟串口数据接收消息”的处理函数
  518. uart.on(uart.ATC, "receive", atcreader)