ril.lua 18 KB

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