link.lua 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. --- 模块功能:数据链路激活(创建、连接、状态维护)
  2. -- @module link
  3. -- @author openLuat
  4. -- @license MIT
  5. -- @copyright openLuat
  6. -- @release 2017.9.20
  7. -- 4G网络下不手动激活pdp,注册上网后发cgdcont?等默认承载激活后上报IP_READY_IND,
  8. -- 2G网络下,先cgact?查询有任一一路pdp激活,则直接上报IP_READY_IND,否则cgact激活cid_manual
  9. require "net"
  10. module(..., package.seeall)
  11. local publish = sys.publish
  12. local request = ril.request
  13. local ipAddr = ""
  14. local gprsAttached
  15. local cid_manual = 5
  16. local readyTable = {false, false, false}
  17. -- 链路层网络类型:
  18. -- 蜂窝模组数据网络
  19. CELLULAR = 1
  20. -- ch395以太网
  21. CH395 = 2
  22. -- w5500以太网
  23. W5500 = 3
  24. -- ESP8266WIFI网络
  25. ESP8266 = 4
  26. local network = CELLULAR
  27. function setReady(mode, state)
  28. readyTable[mode] = state
  29. end
  30. function getIp()
  31. return ipAddr
  32. end
  33. function isReady()
  34. return readyTable[network]
  35. end
  36. -- apn,用户名,密码
  37. local apnname, username, password
  38. local dnsIP
  39. local authProt, authApn, authUser, authPassword
  40. function setAPN(apn, user, pwd)
  41. apnname, username, password = apn, user, pwd
  42. end
  43. function setDnsIP(ip1, ip2)
  44. dnsIP = "\"" .. (ip1 or "") .. "\",\"" .. (ip2 or "") .. "\""
  45. end
  46. local function setCgdf()
  47. request("AT+AUTOAPN=0")
  48. request('AT*CGDFLT=1,"IP","' .. authApn .. '",,,,,,,,,,,,,,,,,,1')
  49. request('AT*CGDFAUTH=1,' .. authProt .. ',"' .. authUser .. '","' .. authPassword .. '"', nil, function(cmd, result)
  50. if result then
  51. sys.restart("CGDFAUTH")
  52. else
  53. sys.timerStart(setCgdf, 5000)
  54. end
  55. end)
  56. end
  57. --- 设置专网卡APN(注意:在main.lua中,尽可能靠前的位置调用此接口)
  58. -- 第一次设置成功之后,软件会自动重启,因为重启后才能生效
  59. -- @number[opt=0] prot 加密方式,0:不加密,1:PAP,2:CHAP
  60. -- @string[opt=""] apn apn名称
  61. -- @string[opt=""] user apn用户名
  62. -- @string[opt=""] pwd apn密码
  63. -- @return nil
  64. -- @usage
  65. -- c = link.setAuthApn(2,"MYAPN","MYNAME","MYPASSWORD")
  66. function setAuthApn(prot, apn, user, pwd)
  67. --[[
  68. local coreVer = rtos.get_version()
  69. local verNo = coreVer:match("Luat_V(%d+)_ASR1802_")
  70. if verNo and tonumber(verNo)>=27 then
  71. request("AT+AUTOAPN=0")]]
  72. -- 0:保存并重启生效
  73. -- 1:不保存立即生效
  74. -- 2:保存并立即生效
  75. -- 3:删除保存的文件
  76. request('AT+CPNETAPN=2,"' .. apn .. '","' .. user .. '","' .. pwd .. '",' .. prot)
  77. --[[else
  78. authProt,authApn,authUser,authPassword = prot or 0,apn or "",user or "",pwd or ""
  79. request("AT*CGDFLT?")
  80. ril.regUrc("*CGDFLT", function(data)
  81. local dftApn = data:match("CGDFLT:%s*\"%w*\",\"(.-)\"")
  82. if dftApn~=authApn then
  83. setCgdf()
  84. end
  85. end)
  86. end]]
  87. end
  88. local function Pdp_Act()
  89. log.info("link.Pdp_Act", readyTable[CELLULAR], net.getNetMode(), gprsAttached)
  90. if readyTable[CELLULAR] then
  91. request("AT+CGDCONT?", nil, cgdcontRsp)
  92. return
  93. end
  94. if net.getNetMode() == net.NetMode_LTE then
  95. if not gprsAttached then
  96. gprsAttached = true
  97. sys.publish("GPRS_ATTACH", true)
  98. end
  99. if not apnname then
  100. sys.timerStart(pdpCmdCnf, 1000, "SET_PDP_4G_WAITAPN", true)
  101. else
  102. request("AT+CGDCONT?", nil, cgdcontRsp)
  103. -- request(string.format('AT*CGDFLT=0,"IP","%s"', apnname), nil, pdpCmdCnf)
  104. end
  105. else
  106. request('AT+CGATT?')
  107. end
  108. end
  109. local function procshut(curCmd, result, respdata, interdata)
  110. if network ~= CELLULAR then
  111. return
  112. end
  113. if IsCidActived(cid_manual, interdata) then
  114. ril.request(string.format('AT+CGACT=0,%d', cid_manual), nil, function(cmd, result)
  115. if result then
  116. readyTable[CELLULAR] = false
  117. sys.publish('IP_ERROR_IND')
  118. if net.getState() ~= 'REGISTERED' then
  119. return
  120. end
  121. sys.timerStart(Pdp_Act, 2000)
  122. end
  123. end)
  124. else
  125. readyTable[CELLULAR] = false
  126. sys.publish('IP_ERROR_IND')
  127. if net.getState() ~= 'REGISTERED' then
  128. return
  129. end
  130. sys.timerStart(Pdp_Act, 2000)
  131. end
  132. end
  133. --[[
  134. 如果是默认承载,是去激活不了的,
  135. 如果是手动激活的pdp,去激活cid_manual后也还是有默认承载存在,
  136. 所以如果上层在去激活后要发起socket是能连上的,所以这里直接上报IP_ERROR_IND,由上层自己管理shut之后的逻辑
  137. ]]
  138. function shut()
  139. if network ~= CELLULAR then
  140. return
  141. end
  142. -- ril.request("AT+CGACT?",nil,procshut)
  143. readyTable[CELLULAR] = false
  144. sys.publish('IP_ERROR_IND')
  145. if net.getState() ~= 'REGISTERED' then
  146. return
  147. end
  148. sys.timerStart(Pdp_Act, 2000)
  149. end
  150. function analysis_cgdcont(data)
  151. local tmp, loc, result
  152. while data do
  153. _, loc = string.find(data, "\r\n")
  154. if loc then
  155. tmp = string.sub(data, 1, loc)
  156. data = string.sub(data, loc + 1, -1)
  157. log.info("analysis_cgdcont ", tmp, loc, data)
  158. else
  159. tmp = data
  160. data = nil
  161. log.info("analysis_cgdcont end", tmp, loc, data)
  162. end
  163. if tmp then
  164. local cid, pdptyp, apn, addr = string.match(tmp, "(%d+),(.+),(.+),[\"\'](.+)[\"\']")
  165. if not cid or not pdptyp or not apn or not addr then
  166. log.info("analysis_cgdcont CGDCONT is empty")
  167. ipAddr = ""
  168. result = false
  169. else
  170. log.info("analysis_cgdcont ", cid, pdptyp, apn, addr)
  171. if addr:match("%d+%.%d+%.%d+%.%d") then
  172. ipAddr = addr
  173. return true
  174. else
  175. log.info("analysis_cgdcont CGDCONT is empty1")
  176. ipAddr = ""
  177. return false
  178. end
  179. end
  180. else
  181. ipAddr = ""
  182. log.info("analysis_cgdcont tmp is empty")
  183. end
  184. end
  185. return result
  186. end
  187. function IsCidActived(cid, data)
  188. if not data then
  189. return
  190. end
  191. for k, v in string.gfind(data, "(%d+),%s*(%d)") do
  192. log.info("iscidactived ", k, v)
  193. if cid == tonumber(k) and v == '1' then
  194. return true
  195. end
  196. end
  197. return
  198. end
  199. function IsExistActivedCid(data)
  200. if not data then
  201. return
  202. end
  203. for k, v in string.gfind(data, "(%d+),%s*(%d)") do
  204. if v == '1' then
  205. log.info("ExistActivedCid ", k, v)
  206. return true
  207. end
  208. end
  209. return
  210. end
  211. local cgdcontResult
  212. function cgdcontRsp()
  213. if cgdcontResult then
  214. pdpCmdCnf("CONNECT_DELAY", true)
  215. end
  216. end
  217. function pdpCmdCnf(curCmd, result, respdata, interdata)
  218. log.info("link.pdpCmdCnf", curCmd, result, respdata, interdata)
  219. if string.find(curCmd, "CGDCONT%?") then
  220. if result and interdata then
  221. result = analysis_cgdcont(interdata)
  222. else
  223. result = false
  224. end
  225. end
  226. if result then
  227. cgdcontResult = false
  228. if string.find(curCmd, "CGDCONT=") then
  229. request(string.format('AT+CGACT=1,%d', cid_manual), nil, pdpCmdCnf)
  230. elseif string.find(curCmd, "CGDCONT%?") then
  231. -- sys.timerStart(pdpCmdCnf, 100, "CONNECT_DELAY",true)
  232. cgdcontResult = true
  233. elseif string.find(curCmd, "CONNECT_DELAY") and network == CELLULAR then
  234. log.info("publish IP_READY_IND")
  235. readyTable[CELLULAR] = true
  236. publish("IP_READY_IND")
  237. elseif string.find(curCmd, "CGACT=") then
  238. request("AT+CGDCONT?", nil, cgdcontRsp)
  239. elseif string.find(curCmd, "CGACT%?") then
  240. if IsExistActivedCid(interdata) then
  241. sys.timerStart(pdpCmdCnf, 100, "CONNECT_DELAY", true)
  242. else
  243. request(string.format('AT+CGDCONT=%d,"IP","%s"', cid_manual, authApn or apnname), nil, pdpCmdCnf)
  244. end
  245. elseif string.find(curCmd, "CGDFLT") then
  246. request("AT+CGDCONT?", nil, cgdcontRsp)
  247. elseif string.find(curCmd, "SET_PDP_4G_WAITAPN") then
  248. if not apnname then
  249. sys.timerStart(pdpCmdCnf, 100, "SET_PDP_4G_WAITAPN", true)
  250. else
  251. request("AT+CGDCONT?", nil, cgdcontRsp, 1000)
  252. -- request(string.format('AT*CGDFLT=0,"IP","%s"', apnname), nil, pdpCmdCnf)
  253. end
  254. end
  255. else
  256. if net.getState() ~= 'REGISTERED' then
  257. return
  258. end
  259. if net.getNetMode() == net.NetMode_LTE then
  260. request("AT+CGDCONT?", nil, cgdcontRsp, 1000)
  261. else
  262. request("AT+CGATT?", nil, nil, 1000)
  263. end
  264. end
  265. end
  266. -- SIM卡 IMSI READY以后自动设置APN
  267. sys.subscribe("IMSI_READY", function()
  268. if not apnname then -- 如果未设置APN设置默认APN
  269. local mcc, mnc = tonumber(sim.getMcc(), 16), tonumber(sim.getMnc(), 16)
  270. apnname, username, password = apn and apn.get_default_apn(mcc, mnc) -- 如果存在APN库自动获取运营商的APN
  271. if not apnname or apnname == '' or apnname == "CMNET" then -- 默认情况,如果联通卡设置为联通APN 其他都默认为CMIOT
  272. apnname = (mcc == 0x460 and (mnc == 0x01 or mnc == 0x06)) and 'UNINET' or 'CMIOT'
  273. end
  274. end
  275. username = username or ''
  276. password = password or ''
  277. end)
  278. ril.regRsp('+CGATT', function(a, b, c, intermediate)
  279. local attached = (intermediate == "+CGATT: 1")
  280. if gprsAttached ~= attached then
  281. gprsAttached = attached
  282. sys.publish("GPRS_ATTACH", attached)
  283. end
  284. if readyTable[CELLULAR] then
  285. return
  286. end
  287. if attached then
  288. log.info("pdp active", apnname, username, password)
  289. request("AT+CGACT?", nil, pdpCmdCnf, 1000)
  290. elseif net.getState() == 'REGISTERED' then
  291. sys.timerStart(request, 2000, "AT+CGATT=1")
  292. sys.timerStart(request, 2000, "AT+CGATT?")
  293. end
  294. end)
  295. rtos.on(rtos.MSG_PDP_DEACT_IND, function()
  296. if network ~= CELLULAR then
  297. return
  298. end
  299. readyTable[CELLULAR] = false
  300. sys.publish('IP_ERROR_IND')
  301. if net.getState() ~= 'REGISTERED' then
  302. return
  303. end
  304. sys.timerStart(Pdp_Act, 2000)
  305. end)
  306. -- 网络注册成功 :AT+CGDCONT?查询默认承载是否激活
  307. -- 2/3G发起GPRS附着状态查询
  308. sys.subscribe("NET_STATE_REGISTERED", Pdp_Act)
  309. local function cindCnf(cmd, result)
  310. if not result then
  311. request("AT+CIND=1", nil, cindCnf, 1000)
  312. end
  313. end
  314. local function cgevurc(data)
  315. if network ~= CELLULAR then
  316. return
  317. end
  318. local cid = 0
  319. log.info("link.cgevurc", data)
  320. if string.match(data, "DEACT") then
  321. cid = string.match(data, "DEACT,(%d)")
  322. cid = tonumber(cid)
  323. if cid == cid_manual then
  324. request("AT+CFUN?")
  325. readyTable[CELLULAR] = false
  326. sys.publish('IP_ERROR_IND')
  327. sys.publish('PDP_DEACT_IND')
  328. if net.getState() ~= 'REGISTERED' then
  329. return
  330. end
  331. sys.timerStart(Pdp_Act, 2000)
  332. end
  333. end
  334. end
  335. request("AT+CIND=1", nil, cindCnf)
  336. ril.regUrc("*CGEV", cgevurc)
  337. ril.regUrc("+CGDCONT", function(data)
  338. pdpCmdCnf("AT+CGDCONT?", true, "OK", data)
  339. end)
  340. --- 打开链路层网络类型
  341. -- 注意:设置网络类型后,并不会关机保存,下次开机会自动恢复为默认的link.CELLULAR类型
  342. -- @number[opt=link.CELLULAR] mode 取值如下:
  343. -- link.CELLULAR:蜂窝模组数据网络
  344. -- link.CH395:CH395以太网络
  345. -- link.ESP8266:ESP8266WIFI网络
  346. -- @table[opt=nil] para 取值如下:
  347. -- 当mode为link.CELLULAR时,参数para无意义,可以直接传入nil
  348. -- 当mode为link.CH395,para为table类型,表示以太网的配置参数,参数结构如下:
  349. -- para= {
  350. -- mode = 1, --1表示客户端;2表示服务器;默认为1
  351. -- intPin = pio.P0_22, --以太网芯片中断通知引脚
  352. -- rstPin = pio.P0_23, --复位以太网芯片引脚
  353. -- spiCs = pio.P0_23, --spi片选
  354. -- serverAddr = "192.168.1.112", --做服务器应用时,本机的地址
  355. -- serverPort = 1888, --做服务器应用时,本机的端口
  356. -- serverGateway = "192.168.1.1", --做服务器应用时,本机的网关地址
  357. -- powerFunc=function(state) end --控制以太网模块的供电开关函数,ret为true开启供电,false关闭供电
  358. -- spi = {spi.SPI_1,0,0,8,800000}, --SPI通道参数,id,cpha,cpol,dataBits,clock,默认spi.SPI_1,0,0,8,800000
  359. -- }
  360. -- @return true/false,执行成功返回true,失败返回false。
  361. -- @usage
  362. -- 设置为蜂窝数据网络:
  363. -- c = link.setNetwork(link.CELLULAR, para)
  364. -- 设置为CH395以太网络:
  365. -- link.setNetwork(link.CH395, para)
  366. -- 设置为ESP8266WIFI网络:
  367. -- link.setNetwork(link.ESP8266, para)
  368. function openNetwork(mode, para)
  369. local tSocketModule = {
  370. [CH395] = socketCh395,
  371. [W5500] = socketW5500,
  372. [ESP8266] = socketESP8266
  373. }
  374. local md = mode or CELLULAR
  375. closeNetWork()
  376. network = md
  377. if network == CELLULAR then
  378. net.switchFly(false)
  379. return true
  380. else
  381. ipAddr = tSocketModule[network].open(para)
  382. if ipAddr ~= "" then
  383. return true
  384. else
  385. log.info('link', 'open CH395 err')
  386. return false
  387. end
  388. end
  389. return false
  390. end
  391. --- 关闭链路层网络类型
  392. -- 注意:关闭链路层网络类型,不会改变链路层网络类型,需要打开链路层网络类型配置才能切换。
  393. -- @return true/false,执行成功返回true,失败返回false。
  394. -- @usage
  395. -- 关闭链路层网络类型:
  396. -- link.closeNetWork()
  397. function closeNetWork()
  398. local tSocketModule = {
  399. [CH395] = socketCh395,
  400. [W5500] = socketW5500,
  401. [ESP8266] = socketESP8266
  402. }
  403. if network == CELLULAR then
  404. -- 飞行模式
  405. net.switchFly(true)
  406. return true
  407. else
  408. return tSocketModule[network].close()
  409. end
  410. return false
  411. end
  412. --- 获取链路层网络类型
  413. -- @return network,number类型,取值如下:
  414. -- link.CELLULAR:蜂窝模组数据网络
  415. -- link.CH395:CH395以太网络
  416. -- link.ESP8266:ESP8266WIFI网络
  417. -- @usage
  418. -- 获取数据网络类型:
  419. -- mode = link.getNetwork()
  420. function getNetwork()
  421. return network
  422. end