ril.lua 16 KB

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