exnetif.lua 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983
  1. --[[
  2. @module exnetif
  3. @summary exnetif 控制网络优先级(以太网->WIFI->4G)根据优先级选择上网的网卡。简化开启多网融合的操作,4G作为数据出口给WIFI,以太网设备上网,以太网作为数据出口给WIFI,Air8000上网,WIFI作为数据出口给Air8000,以太网上网。
  4. @version 1.0
  5. @date 2025.06.26
  6. @author wjq
  7. @usage
  8. 本文件的对外接口有4个:
  9. 1、exnetif.set_priority_order(networkConfigs):设置网络优先级顺序并初始化对应网络(需要在task中调用)
  10. 2、exnetif.notify_status(cb_fnc):设置网络状态变化回调函数
  11. 3、exnetif.setproxy(adapter, main_adapter,other_configs):配置网络代理实现多网融合(需要在task中调用)
  12. 4、exnetif.check_network_status(interval),检测间隔时间ms(选填),不填时只检测一次,填写后将根据间隔时间循环检测,会提高模块功耗
  13. ]]
  14. local exnetif = {}
  15. dnsproxy = require("dnsproxy")
  16. dhcpsrv = require("dhcpsrv")
  17. httpdns = require("httpdns")
  18. -- 设置pingip
  19. local wifi_ping_ip
  20. local eth_ping_ip
  21. local local_network_mode
  22. local need_ping = true
  23. local single_network_mode = false
  24. local auto_socket_switch = true
  25. local ping_time = 10000
  26. -- 连接状态
  27. local connection_states = {
  28. DISCONNECTED = 0,
  29. CONNECTING = 1,
  30. CONNECTED = 2,
  31. OPENED = 3,
  32. SINGLE_NETWORK=4
  33. }
  34. -- 状态回调函数
  35. local states_cbfnc = function(net_type) end
  36. -- 当前优先级
  37. local current_priority = { socket.LWIP_ETH, socket.LWIP_STA, socket.LWIP_GP }
  38. -- 连接状态
  39. local available = {
  40. [socket.LWIP_STA] = connection_states.DISCONNECTED,
  41. [socket.LWIP_ETH] = connection_states.DISCONNECTED,
  42. [socket.LWIP_GP] = connection_states.DISCONNECTED,
  43. [socket.LWIP_USER1] = connection_states.DISCONNECTED
  44. }
  45. -- 当前使用的网卡
  46. local current_active = nil
  47. -- 网络类型转字符串
  48. local function type_to_string(net_type)
  49. local type_map = {
  50. [socket.LWIP_STA] = "WiFi",
  51. [socket.LWIP_ETH] = "Ethernet",
  52. [socket.LWIP_GP] = "4G",
  53. [socket.LWIP_USER1] = "8101SPIETH"
  54. }
  55. return type_map[net_type] or "Unknown"
  56. end
  57. local function socket_state_detection(adapter)
  58. if netdrv.on then
  59. log.info("netdrv", "订阅socket连接状态变化事件", type_to_string(adapter))
  60. -- 订阅socket连接状态变化事件
  61. netdrv.on(adapter, netdrv.EVT_SOCKET, function(id, event, params)
  62. if event == "timeout" or event == "error" then
  63. if available[adapter] == connection_states.CONNECTED then
  64. available[adapter] = connection_states.CONNECTING
  65. end
  66. end
  67. -- log.info("netdrv", "socket event", id, event, json.encode(params or {}))
  68. -- if params then
  69. -- -- params里会有remote_ip, remote_port等信息, 可按需获取
  70. -- local remote_ip = params.remote_ip
  71. -- local remote_port = params.remote_port
  72. -- local domain_name = params.domain_name
  73. -- log.info("netdrv", "socket event", "remote_ip", remote_ip, "remote_port", remote_port, "domain_name", domain_name)
  74. -- end
  75. end)
  76. end
  77. end
  78. -- 状态更改后重新设置默认网卡
  79. local function apply_priority()
  80. local usable = false
  81. -- 查找优先级最高的可用网络
  82. for _, net_type in ipairs(current_priority) do
  83. -- log.info("网卡顺序",type_to_string(net_type),available[net_type])
  84. if available[net_type] == connection_states.CONNECTED then
  85. usable = true
  86. -- 设置优先级高的网卡
  87. if current_active ~= net_type then
  88. log.info("设置网卡", type_to_string(net_type))
  89. states_cbfnc(type_to_string(net_type), net_type) -- 默认网卡改变的回调函数
  90. socket.dft(net_type)
  91. if auto_socket_switch and socket.close_all then
  92. socket.close_all(current_active)
  93. end
  94. current_active = net_type
  95. end
  96. break
  97. end
  98. end
  99. -- 从存在可用网卡到没有可用网卡,才通知回调
  100. if usable == false and current_active ~= nil then
  101. --避免重复通知
  102. current_active = nil
  103. states_cbfnc(nil, -1)
  104. end
  105. end
  106. --httpdns域名解析测试
  107. local function http_dnstest(adaptertest)
  108. local ip = httpdns.ali("baidu.com", { adapter = adaptertest, timeout = 3000 })
  109. if ip ~= nil then
  110. available[adaptertest] = connection_states.CONNECTED
  111. log.info(type_to_string(adaptertest) .. "网卡httpdns域名解析成功")
  112. else
  113. log.info(type_to_string(adaptertest) .. "网卡httpdns域名解析失败")
  114. end
  115. log.info("httpdns", "baidu.com", ip)
  116. end
  117. -- ping操作
  118. local function ping_request(adaptertest)
  119. log.info("dns_request",type_to_string(adaptertest),need_ping)
  120. if need_ping then
  121. if adaptertest == socket.LWIP_ETH or adaptertest == socket.LWIP_USER1 then
  122. if eth_ping_ip == nil then
  123. http_dnstest(adaptertest)
  124. else
  125. icmp.setup(adaptertest)
  126. icmp.ping(adaptertest, eth_ping_ip)
  127. end
  128. end
  129. if adaptertest == socket.LWIP_STA then
  130. if wifi_ping_ip == nil then
  131. http_dnstest(adaptertest)
  132. else
  133. icmp.setup(adaptertest)
  134. icmp.ping(adaptertest, wifi_ping_ip)
  135. end
  136. end
  137. if adaptertest == socket.LWIP_GP then
  138. if eth_ping_ip ~= nil then
  139. icmp.setup(adaptertest)
  140. icmp.ping(adaptertest, eth_ping_ip)
  141. elseif wifi_ping_ip ~= nil then
  142. icmp.setup(adaptertest)
  143. icmp.ping(adaptertest, wifi_ping_ip)
  144. else
  145. http_dnstest(adaptertest)
  146. end
  147. end
  148. else
  149. log.info(type_to_string(adaptertest) .. "配置了不需要ping,直接切换为CONNECTED状态")
  150. available[adaptertest] = connection_states.CONNECTED
  151. end
  152. apply_priority()
  153. end
  154. -- 网卡上线回调函数
  155. local function ip_ready_handle(ip, adapter)
  156. local _, _, gw = socket.localIP(adapter)
  157. log.info("ip_ready_handle", ip, type_to_string(adapter), "state", available[adapter], "gw", gw)
  158. if local_network_mode then
  159. if adapter == socket.LWIP_ETH or adapter == socket.LWIP_USER1 then
  160. eth_ping_ip = gw
  161. elseif adapter == socket.LWIP_STA then
  162. wifi_ping_ip = gw
  163. end
  164. end
  165. log.info("eth_ping_ip", eth_ping_ip, "wifi_ping_ip", wifi_ping_ip)
  166. -- 需要ping操作,ping通后认为网络可用
  167. if available[adapter] == connection_states.OPENED then
  168. available[adapter] = connection_states.CONNECTING
  169. end
  170. -- ping_request(adapter)
  171. end
  172. -- 网卡下线回调函数
  173. local function ip_lose_handle(adapter)
  174. log.info("ip_lose_handle", type_to_string(adapter))
  175. if available[adapter] == connection_states.CONNECTING or available[adapter] == connection_states.CONNECTED then
  176. available[adapter] = connection_states.OPENED
  177. end
  178. if current_active == adapter then
  179. log.info(type_to_string(adapter) .. " 失效,切换到其他网络")
  180. apply_priority()
  181. end
  182. end
  183. local interval_time = nil
  184. --[[
  185. 对正常状态的网卡进行ping测试
  186. @api exnetif.check_network_status(interval),
  187. @int 检测间隔时间ms(选填),不填时只检测一次,填写后将根据间隔时间循环检测,会提高模块功耗
  188. ]]
  189. function exnetif.check_network_status(interval)
  190. if interval ~= nil then
  191. interval_time = interval
  192. end
  193. for _, net_type in ipairs(current_priority) do
  194. if available[net_type] == connection_states.CONNECTED then
  195. available[net_type] = connection_states.CONNECTING
  196. end
  197. end
  198. end
  199. --打开以太网Wan功能
  200. local function setup_eth(config)
  201. if config.local_network_mode then
  202. local_network_mode = true
  203. end
  204. if config.need_ping~=nil then
  205. need_ping = config.need_ping
  206. end
  207. if config.auto_socket_switch ~=nil then
  208. auto_socket_switch = config.auto_socket_switch
  209. -- log.info("设置自动关闭非当前网卡socket连接", auto_socket_switch)
  210. end
  211. eth_ping_ip = config.ping_ip
  212. if type(config.ping_time) == "number" then
  213. ping_time = config.ping_time
  214. end
  215. log.info("初始化以太网")
  216. if not single_network_mode then
  217. available[socket.LWIP_ETH] = connection_states.OPENED
  218. else
  219. available[socket.LWIP_ETH] = connection_states.SINGLE_NETWORK
  220. end
  221. -- 打开CH390供电
  222. if config.pwrpin then
  223. gpio.setup(config.pwrpin, 1, gpio.PULLUP)
  224. end
  225. -- sys.wait(100) -- 等待以太网模块上电稳定
  226. if config.tp == nil then
  227. log.info("8101以太网")
  228. if netdrv.setup(socket.LWIP_ETH) == false then
  229. log.error("以太网初始化失败")
  230. if config.pwrpin then
  231. gpio.close(config.pwrpin)
  232. end
  233. return false
  234. end
  235. else
  236. log.info("config.opts.spi",config.opts.spi,",config.type",config.tp)
  237. -- 配置SPI和初始化网络驱动
  238. local result = spi.setup(config.opts.spi, -- spi id
  239. nil, 0, -- CPHA
  240. 0, -- CPOL
  241. 8, -- 数据宽度
  242. 25600000 -- ,--波特率
  243. )
  244. log.info("main", "open spi", result)
  245. if result ~= 0 then -- 返回值为0,表示打开成功
  246. log.info("main", "spi open error", result)
  247. if config.pwrpin then
  248. gpio.close(config.pwrpin)
  249. end
  250. return false
  251. end
  252. -- 初始化指定netdrv设备,
  253. -- socket.LWIP_ETH 网络适配器编号
  254. -- netdrv.CH390外挂CH390
  255. -- SPI ID 1, 片选 GPIO12
  256. if netdrv.setup(socket.LWIP_ETH, config.tp, config.opts) == false then
  257. log.error("以太网初始化失败")
  258. if config.pwrpin then
  259. gpio.close(config.pwrpin)
  260. end
  261. return false
  262. end
  263. end
  264. if config.static_ip then
  265. sys.wait(1000) -- 等待以太网模块初始化完成,去掉会导致以太网初始化失败
  266. log.info("netdrv", "自定义以太网IP地址", config.static_ip.ipv4)
  267. log.info("静态ip",netdrv.ipv4(socket.LWIP_ETH, config.static_ip.ipv4,config.static_ip.mark,config.static_ip.gw))
  268. else
  269. netdrv.dhcp(socket.LWIP_ETH, true)
  270. end
  271. log.info("以太网初始化完成")
  272. socket_state_detection(socket.LWIP_ETH)
  273. return true
  274. end
  275. --打开8101spi以太网Wan功能
  276. local function setup_eth_user1(config)
  277. if config.local_network_mode then
  278. local_network_mode = true
  279. end
  280. if config.need_ping~=nil then
  281. need_ping = config.need_ping
  282. end
  283. if config.auto_socket_switch ~=nil then
  284. auto_socket_switch = config.auto_socket_switch
  285. -- log.info("设置自动关闭非当前网卡socket连接", auto_socket_switch)
  286. end
  287. eth_ping_ip = config.ping_ip
  288. if type(config.ping_time) == "number" then
  289. ping_time = config.ping_time
  290. end
  291. log.info("初始化以太网")
  292. if not single_network_mode then
  293. available[socket.LWIP_USER1] = connection_states.OPENED
  294. else
  295. available[socket.LWIP_USER1] = connection_states.SINGLE_NETWORK
  296. end
  297. -- 打开CH390供电
  298. if config.pwrpin then
  299. gpio.setup(config.pwrpin, 1, gpio.PULLUP)
  300. end
  301. -- sys.wait(100)-- 等待以太网模块上电稳定
  302. log.info("config.opts.spi", config.opts.spi, ",config.type", config.tp)
  303. -- 配置SPI和初始化网络驱动
  304. local result = spi.setup(config.opts.spi, -- spi id
  305. nil, 0, -- CPHA
  306. 0, -- CPOL
  307. 8, -- 数据宽度
  308. 25600000 -- ,--波特率
  309. )
  310. log.info("main", "open spi", result)
  311. if result ~= 0 then -- 返回值为0,表示打开成功
  312. log.info("main", "spi open error", result)
  313. if config.pwrpin then
  314. gpio.close(config.pwrpin)
  315. end
  316. return false
  317. end
  318. -- 初始化指定netdrv设备,
  319. -- socket.LWIP_ETH 网络适配器编号
  320. -- netdrv.CH390外挂CH390
  321. -- SPI ID 1, 片选 GPIO12
  322. if netdrv.setup(socket.LWIP_USER1, config.tp, config.opts) == false then
  323. log.error("以太网初始化失败")
  324. if config.pwrpin then
  325. gpio.close(config.pwrpin)
  326. end
  327. return false
  328. end
  329. if config.static_ip then
  330. sys.wait(1000) -- 等待以太网模块初始化完成,去掉会导致以太网初始化失败
  331. log.info("netdrv", "自定义以太网IP地址", config.static_ip.ipv4)
  332. log.info("静态ip",netdrv.ipv4(socket.LWIP_USER1, config.static_ip.ipv4, config.static_ip.mark, config.static_ip.gw))
  333. else
  334. netdrv.dhcp(socket.LWIP_USER1, true)
  335. end
  336. log.info("以太网初始化完成")
  337. socket_state_detection(socket.LWIP_USER1)
  338. return true
  339. end
  340. --连接wifi(STA模式)
  341. local function set_wifi_info(config)
  342. if config.local_network_mode then
  343. local_network_mode = true
  344. end
  345. if config.need_ping~=nil then
  346. need_ping = config.need_ping
  347. end
  348. if config.auto_socket_switch ~=nil then
  349. auto_socket_switch = config.auto_socket_switch
  350. -- log.info("设置自动关闭非当前网卡socket连接", auto_socket_switch)
  351. end
  352. wifi_ping_ip = config.ping_ip
  353. if type(config.ping_time) == "number" then
  354. ping_time = config.ping_time
  355. end
  356. log.info("WiFi名称:", config.ssid)
  357. log.info("密码 :", config.password)
  358. log.info("ping_ip :", config.ping_ip)
  359. wlan.init()
  360. if not single_network_mode then
  361. available[socket.LWIP_STA] = connection_states.OPENED
  362. else
  363. available[socket.LWIP_STA] = connection_states.SINGLE_NETWORK
  364. end
  365. -- 尝试连接Wi-Fi,并处理可能出现的错误
  366. local success = wlan.connect(config.ssid, config.password)
  367. if not success then
  368. log.error("WiFi连接失败")
  369. return false
  370. end
  371. log.info("WiFi STA初始化完成")
  372. socket_state_detection(socket.LWIP_STA)
  373. return true
  374. end
  375. --[[
  376. 设置网络优先级,相应网卡获取到ip且网络正常视为网卡可用,丢失ip视为网卡不可用.(需要在task中调用)
  377. @api exnetif.set_priority_order(new_priority)
  378. @table 网络优先级列表,优先级从高到低对应table中的第一个参数到最后一个参数
  379. @return boolean 成功返回true,失败返回false
  380. @usage
  381. 多网优先级模式:
  382. exnetif.set_priority_order({
  383. { -- 最高优先级网络
  384. WIFI = { -- WiFi配置
  385. ssid = "your_ssid", -- WiFi名称(string)
  386. password = "your_pwd", -- WiFi密码(string)
  387. need_ping = true, -- 是否需要通过ping来测试网络的连通性
  388. -- 在没有ping测试环境的项目中,需要将这个参数设置为false,表示不需要ping测试网络连通,
  389. -- 仅根据IP READY消息(即获取到了ip地址)来判断网络环境准备就绪,是否网络连通性则无法保证
  390. -- 如果没有设置此参数,默认为true
  391. -- 在有ping测试环境的项目中,建议不要将这个参数设置为true
  392. local_network_mode = true,-- 局域网模式(选填参数),设置为true时,exnetif会自动将ping_ip设置为网卡的网关ip。
  393. -- 用户不需要传入ping_ip参数,即使传入了,也无效。
  394. -- 这个模式的使用场景,仅适用于局域网环境;可以访问外网时,不要使用
  395. ping_ip = "112.125.89.8", -- 连通性检测IP(选填参数),默认使用httpdns获取baidu.com的ip作为判断条件,
  396. -- 注:如果填写ip,则ping通作为判断网络是否可用的条件,
  397. -- 所以需要根据网络环境填写内网或者外网ip,
  398. -- 填写外网ip的话要保证外网ip始终可用,
  399. -- 填写局域网ip的话要确保相应ip固定且能够被ping通
  400. ping_time = 10000 -- 填写ping_ip且未ping通时的检测间隔(ms, 可选,默认为10秒)
  401. -- 定时ping将会影响模块功耗,使用低功耗模式的话可以适当延迟间隔时间
  402. }
  403. },
  404. { -- 次优先级网络
  405. ETHERNET = { -- 以太网配置
  406. pwrpin = 140, -- 供电使能引脚(number)
  407. need_ping = true, -- 是否需要通过ping来测试网络的连通性
  408. -- 在没有ping测试环境的项目中,需要将这个参数设置为false,表示不需要ping测试网络连通,
  409. -- 仅根据IP READY消息(即获取到了ip地址)来判断网络环境准备就绪,是否网络连通性则无法保证
  410. -- 如果没有设置此参数,默认为true
  411. -- 在有ping测试环境的项目中,建议不要将这个参数设置为true
  412. local_network_mode = true, -- 局域网模式(选填参数),设置为true时,exnetif会自动将ping_ip设置为网卡的网关ip。
  413. -- 用户不需要传入ping_ip参数,即使传入了,也无效。
  414. -- 这个模式的使用场景,仅适用于局域网环境;可以访问外网时,不要使用
  415. ping_ip = "112.125.89.8", -- 连通性检测IP(选填参数),默认使用httpdns获取baidu.com的ip作为判断条件,
  416. -- 注:如果填写ip,则ping通作为判断网络是否可用的条件,
  417. -- 所以需要根据网络环境填写内网或者外网ip,
  418. -- 填写外网ip的话要保证外网ip始终可用,
  419. -- 填写局域网ip的话要确保相应ip固定且能够被ping通
  420. ping_time = 10000, -- 填写ping_ip且未ping通时的检测间隔(ms, 可选,默认为10秒)
  421. -- 定时ping将会影响模块功耗,使用低功耗模式的话可以适当延迟间隔时间
  422. tp = netdrv.CH390, -- 网卡芯片型号(选填参数),仅spi方式外挂以太网时需要填写。
  423. opts = { spi = 1, cs = 12 }, -- 外挂方式,需要额外的参数(选填参数),仅spi方式外挂以太网时需要填写。
  424. static_ip = { -- 静态ip配置(选填参数),不填写则使用dhcp获取ip
  425. ipv4 = "192.168.5.100", -- ip地址(string)
  426. mark = "255.255.255.0", -- 子网掩码(string)
  427. gw = "192.168.5.1" -- 网关地址(string)
  428. }
  429. }
  430. },
  431. { -- 最低优先级网络
  432. LWIP_GP = true -- 启用4G网络
  433. }
  434. })
  435. 单网络模式:
  436. -- 单网络模式下只使用WIFI网络
  437. exnetif.set_priority_order({
  438. { -- 单网络,打开wifi
  439. WIFI = { -- WiFi配置
  440. ssid = "test", -- WiFi名称(string)
  441. password = "HZ88888888", -- WiFi密码(string)
  442. }
  443. }
  444. })
  445. -- Air8000系列和780EXX系列单网络模式下只使用SPI以太网网络
  446. exnetif.set_priority_order({
  447. {
  448. ETHERNET = { -- 以太网配置
  449. pwrpin = 140, -- 供电使能引脚(number)
  450. tp = netdrv.CH390, -- 网卡芯片型号(选填参数),仅spi方式外挂以太网时需要填写。
  451. opts = {spi = 1, cs = 12}, -- 外挂方式,需要额外的参数(选填参数),仅spi方式外挂以太网时需要填写。
  452. }
  453. }
  454. })
  455. -- Air8101单网络模式下只使用SPI以太网网络
  456. exnetif.set_priority_order({
  457. {
  458. ETHUSER1 = { -- 以太网配置
  459. pwrpin = 13, -- 供电使能引脚(number)
  460. tp = netdrv.CH390, -- 网卡芯片型号(选填参数),仅spi方式外挂以太网时需要填写。
  461. opts = {spi = 0, cs = 15}, -- 外挂方式,需要额外的参数(选填参数),仅spi方式外挂以太网时需要填写。
  462. static_ip = { -- 静态ip配置(选填参数),不填写则使用dhcp获取ip
  463. ipv4 = "192.168.5.100", -- ip地址(string)
  464. mark = "255.255.255.0", -- 子网掩码(string)
  465. gw = "192.168.5.1" -- 网关地址(string)
  466. }
  467. }
  468. }
  469. })
  470. -- 单网络模式下只使用RMII以太网网络
  471. exnetif.set_priority_order({
  472. {
  473. ETHERNET = { -- 以太网配置
  474. pwrpin = 13, -- 供电使能引脚(number)
  475. }
  476. }
  477. })
  478. -- 4G单网模式下,不需要require "exnetif",减少不必要的功能模块加载
  479. ]]
  480. function exnetif.set_priority_order(networkConfigs)
  481. -- 判断表中数据个数
  482. if #networkConfigs < 1 then
  483. log.error("网络配置为空")
  484. return false
  485. end
  486. if #networkConfigs == 1 then
  487. single_network_mode = true
  488. end
  489. if not single_network_mode then
  490. -- CONNECTING的网卡需要定时ping
  491. sys.taskInit(function()
  492. while true do
  493. for _, net_type in ipairs(current_priority) do
  494. -- log.info("网卡顺序",type_to_string(net_type),available[net_type])
  495. if available[net_type] == connection_states.CONNECTING then
  496. log.info(type_to_string(net_type) .. "网卡开始PING")
  497. ping_request(net_type)
  498. sys.wait(ping_time)
  499. end
  500. end
  501. sys.wait(1000) -- 避免死循环
  502. end
  503. end)
  504. -- 循环ping检测任务,默认不启用
  505. sys.taskInit(function()
  506. while true do
  507. if interval_time ~= nil then
  508. sys.wait(interval_time)
  509. exnetif.check_network_status()
  510. end
  511. sys.wait(1000) -- 避免死循环
  512. end
  513. end)
  514. sys.subscribe("PING_RESULT", function(id, time, dst)
  515. log.info("ping", id, time, dst)
  516. log.info(type_to_string(id) .. "网卡PING测试成功")
  517. available[id] = connection_states.CONNECTED
  518. apply_priority()
  519. end)
  520. -- 订阅网络状态变化的消息
  521. sys.subscribe("IP_READY", ip_ready_handle)
  522. sys.subscribe("IP_LOSE", ip_lose_handle)
  523. end
  524. local new_priority = {}
  525. for _, config in ipairs(networkConfigs) do
  526. if type(config.WIFI) == "table" then
  527. -- 开启wifi
  528. local res = set_wifi_info(config.WIFI)
  529. if res == false then
  530. log.error("wifi连接失败")
  531. return false
  532. end
  533. table.insert(new_priority, socket.LWIP_STA)
  534. end
  535. if type(config.ETHUSER1) == "table" then
  536. -- 开启以太网
  537. local res = setup_eth_user1(config.ETHUSER1)
  538. if res == false then
  539. log.error("以太网打开失败")
  540. return false
  541. end
  542. table.insert(new_priority, socket.LWIP_USER1)
  543. end
  544. if type(config.ETHERNET) == "table" then
  545. -- 开启以太网
  546. local res = setup_eth(config.ETHERNET)
  547. if res == false then
  548. log.error("以太网打开失败")
  549. return false
  550. end
  551. table.insert(new_priority, socket.LWIP_ETH)
  552. end
  553. if config.LWIP_GP then
  554. -- 开启4G
  555. table.insert(new_priority, socket.LWIP_GP)
  556. available[socket.LWIP_GP] = connection_states.CONNECTING
  557. if config.auto_socket_switch ~=nil then
  558. auto_socket_switch = config.auto_socket_switch
  559. -- log.info("设置自动关闭非当前网卡socket连接", auto_socket_switch)
  560. end
  561. end
  562. end
  563. -- 设置新优先级
  564. current_priority = new_priority
  565. -- 此处按照用户期望的配置,先设置优先级最高的默认网卡
  566. -- 防止出现以下问题:
  567. -- 例如Air8000内核固件运行起来之后,默认网卡是socket.LWIP_GP,如果用户调用exnetif.set_priority_order接口配置最高优先级网卡为socket.LWIP_ETH
  568. -- 在socket.LWIP_ETH网卡准备就绪之前,socket.LWIP_GP可能已经准备就绪,此时默认网卡仍然是socket.LWIP_GP;
  569. -- 而网络应用层(例如socket,mqtt等)有关的demo,我们编写时,不关心具体网卡,直接使用默认网卡(这样符合正常逻辑);
  570. -- 就可能会出现“网络应用在这段时间内直接使用socket.LWIP_GP,而不是用户期望的网卡socket.LWIP_ETH来上网”的问题;
  571. socket.dft(new_priority[1])
  572. apply_priority()
  573. return true
  574. end
  575. --[[
  576. 设置网络状态变化回调函数。触发条件:网卡切换或者所有网卡都断网。回调函数的输入参数: 1. 当有可用网络的时候,返回当前使用网卡、网卡id;2. 当没有可用网络的时候,返回 nil、-1 。
  577. @api exnetif.notify_status(cb_fnc)
  578. @function 回调函数
  579. @usage
  580. exnetif.notify_status(function(net_type,adapter)
  581. log.info("可以使用优先级更高的网络:", net_type,adapter)
  582. end)
  583. ]]
  584. function exnetif.notify_status(cb_fnc)
  585. log.info("notify_status", type(cb_fnc))
  586. if type(cb_fnc) ~= "function" then
  587. log.error("notify_status设置错误,请传入一个函数")
  588. return
  589. end
  590. states_cbfnc = cb_fnc
  591. end
  592. --[[
  593. 设置多网融合模式,例如4G作为数据出口给WIFI或以太网设备上网(需要在task中调用)
  594. @api exnetif.setproxy(adapter, main_adapter,other_configs)
  595. @adapter 需要使用网络的网卡,例如socket.LWIP_ETH
  596. @adapter 提供网络的网卡,例如socket.LWIP_GP
  597. @table 其他设置参数(选填参数),
  598. @usage
  599. 典型应用:
  600. -- 以太网WAN提供网络其他设备连接以太网LAN口上网
  601. exnetif.setproxy(socket.LWIP_ETH, socket.LWIP_USER1, {
  602. ethpower_en = 20,-- 以太网模块的pwrpin引脚(gpio编号)
  603. tp = netdrv.CH390, -- 网卡芯片型号(选填参数),仅spi方式外挂以太网时需要填写。
  604. opts = {spi = 0, cs = 8}, -- 外挂方式,需要额外的参数(选填参数),仅spi方式外挂以太网时需要填写。
  605. main_adapter = {
  606. ethpower_en = 21,-- 以太网模块的pwrpin引脚(gpio编号)
  607. tp = netdrv.CH390, -- 网卡芯片型号(选填参数),仅spi方式外挂以太网时需要填写。
  608. opts = {spi = 1, cs = 12}
  609. }
  610. }) then
  611. -- wifi_sta提供网络开启wifi_ap热点供设备上网
  612. exnetif.setproxy(socket.LWIP_AP, socket.LWIP_STA, {
  613. ssid = "test2", -- AP热点名称(string),网卡包含wifi时填写
  614. password = "HZ88888888", -- AP热点密码(string),网卡包含wifi时填写
  615. ap_opts = { -- AP模式下配置项(选填参数)
  616. hidden = false, -- 是否隐藏SSID, 默认false,不隐藏
  617. max_conn = 4 }, -- 最大客户端数量, 默认4
  618. channel = 6, -- AP建立的通道, 默认6
  619. main_adapter = {
  620. ssid = "test", -- 提供网络的网卡开启参数
  621. password = "HZ88888888"
  622. }
  623. })
  624. -- 4G提供网络开启wifi_ap热点供设备上网,其他设备连接以太网LAN口上网
  625. exnetif.setproxy(socket.LWIP_AP, socket.LWIP_GP, {
  626. ssid = "Hotspot", -- WiFi名称(string),网卡包含wifi时填写
  627. password = "password123", -- WiFi密码(string),网卡包含wifi时填写
  628. adapter_addr = "192.168.5.1", -- adapter网卡的ip地址(选填),需要自定义ip和网关ip时填写
  629. adapter_gw= { 192, 168, 5, 1 }, -- adapter网卡的网关地址(选填),需要自定义ip和网关ip时填写
  630. ap_opts={ -- AP模式下配置项(选填参数)
  631. hidden = false, -- 是否隐藏SSID, 默认false,不隐藏
  632. max_conn = 4 }, -- 最大客户端数量, 默认4
  633. channel=6 -- AP建立的通道, 默认6
  634. })
  635. exnetif.setproxy(socket.LWIP_ETH, socket.LWIP_GP, {
  636. tp = netdrv.CH390, -- 网卡芯片型号(选填参数),仅spi方式外挂以太网时需要填写。
  637. opts = { spi = 1, cs = 12}, -- 外挂方式,需要额外的参数(选填参数),仅spi方式外挂以太网时需要填写。
  638. ethpower_en = 140, -- 以太网模块的pwrpin引脚(gpio编号)
  639. adapter_addr = "192.168.5.1", -- adapter网卡的ip地址(选填),需要自定义ip和网关ip时填写
  640. adapter_gw= { 192, 168, 5, 1 }, -- adapter网卡的网关地址(选填),需要自定义ip和网关ip时填写
  641. })
  642. -- 以太网提供网络供WiFi设备上网
  643. exnetif.setproxy(socket.LWIP_AP, socket.LWIP_ETH, {
  644. ssid = "Hotspot", -- WiFi名称(string),网卡包含wifi时填写
  645. password = "password123", -- WiFi密码(string),网卡包含wifi时填写
  646. main_adapter={
  647. tp = netdrv.CH390, -- 网卡芯片型号(选填参数),仅spi方式外挂以太网时需要填写。
  648. opts = { spi = 1, cs = 12}, -- 外挂方式,需要额外的参数(选填参数),仅spi方式外挂以太网时需要填写。
  649. ethpower_en = 140, -- 以太网模块的pwrpin引脚(gpio编号)
  650. }
  651. })
  652. -- WIFI提供网络供以太网设备上网
  653. exnetif.setproxy(socket.LWIP_ETH, socket.LWIP_STA, {
  654. tp = netdrv.CH390, -- 网卡芯片型号(选填参数),仅spi方式外挂以太网时需要填写。
  655. opts = { spi = 1, cs = 12}, -- 外挂方式,需要额外的参数(选填参数),仅spi方式外挂以太网时需要填写。
  656. ethpower_en = 140, -- 以太网模块的pwrpin引脚(gpio编号)
  657. main_adapter = {
  658. ssid = "test", -- 提供网络的网卡开启参数
  659. password = "HZ88888888"
  660. }
  661. })
  662. ]]
  663. function exnetif.setproxy(adapter, main_adapter, other_configs)
  664. if main_adapter == socket.LWIP_ETH and available[socket.LWIP_ETH] == connection_states.DISCONNECTED then
  665. -- 打开WAN功能
  666. log.info("ch390", "打开LDO供电", other_configs.main_adapter.ethpower_en)
  667. available[socket.LWIP_ETH] = connection_states.OPENED
  668. -- 打开CH390供电
  669. if other_configs.main_adapter.ethpower_en then
  670. gpio.setup(other_configs.main_adapter.ethpower_en, 1, gpio.PULLUP)
  671. end
  672. -- sys.wait(100) -- 等待以太网模块上电稳定
  673. if other_configs.main_adapter.tp == nil then
  674. log.info("8101以太网")
  675. if netdrv.setup(socket.LWIP_ETH) == false then
  676. log.error("以太网初始化失败")
  677. if other_configs.main_adapter.ethpower_en then
  678. gpio.close(other_configs.main_adapter.ethpower_en)
  679. end
  680. return false
  681. end
  682. else
  683. log.info("config.opts.spi", other_configs.main_adapter.opts.spi, ",config.type", other_configs.main_adapter.tp)
  684. -- 配置SPI和初始化网络驱动
  685. local result = spi.setup(other_configs.main_adapter.opts.spi, -- spi id
  686. nil, 0, -- CPHA
  687. 0, -- CPOL
  688. 8, -- 数据宽度
  689. 25600000 -- ,--波特率
  690. )
  691. log.info("main", "open spi", result)
  692. if result ~= 0 then -- 返回值为0,表示打开成功
  693. log.info("main", "spi open error", result)
  694. if other_configs.main_adapter.ethpower_en then
  695. gpio.close(other_configs.main_adapter.ethpower_en)
  696. end
  697. return false
  698. end
  699. -- 初始化指定netdrv设备,
  700. local success = netdrv.setup(socket.LWIP_ETH, other_configs.main_adapter.tp, other_configs.main_adapter.opts)
  701. if not success then
  702. log.error("以太网初始化失败")
  703. if other_configs.main_adapter.ethpower_en then
  704. gpio.close(other_configs.main_adapter.ethpower_en)
  705. end
  706. return false
  707. end
  708. end
  709. netdrv.dhcp(socket.LWIP_ETH, true)
  710. local count = 1
  711. while 1 do
  712. local ip = netdrv.ipv4(socket.LWIP_ETH)
  713. if ip and ip ~= "0.0.0.0" then break end
  714. if count > 600 then
  715. log.error("以太网连接超时,请检查配置")
  716. if other_configs.main_adapter.ethpower_en then
  717. gpio.close(other_configs.main_adapter.ethpower_en)
  718. end
  719. return false
  720. end
  721. sys.wait(100)
  722. count = count + 1
  723. end
  724. elseif main_adapter == socket.LWIP_USER1 and available[socket.LWIP_USER1] == connection_states.DISCONNECTED then
  725. log.info("初始化以太网")
  726. -- 打开CH390供电
  727. if other_configs.main_adapter.ethpower_en then
  728. gpio.setup(other_configs.main_adapter.ethpower_en, 1, gpio.PULLUP)
  729. end
  730. -- sys.wait(100) -- 等待以太网模块上电稳定
  731. log.info("config.opts.spi", other_configs.main_adapter.opts.spi, ",config.type", other_configs.main_adapter.tp)
  732. available[socket.LWIP_USER1] = connection_states.OPENED
  733. -- 配置SPI和初始化网络驱动
  734. local result = spi.setup(other_configs.main_adapter.opts.spi, -- spi id
  735. nil, 0, -- CPHA
  736. 0, -- CPOL
  737. 8, -- 数据宽度
  738. 25600000 -- ,--波特率
  739. )
  740. log.info("main", "open spi", result)
  741. if result ~= 0 then -- 返回值为0,表示打开成功
  742. log.info("main", "spi open error", result)
  743. if other_configs.main_adapter.ethpower_en then
  744. gpio.close(other_configs.main_adapter.ethpower_en)
  745. end
  746. return false
  747. end
  748. -- 初始化指定netdrv设备,
  749. -- socket.LWIP_ETH 网络适配器编号
  750. -- netdrv.CH390外挂CH390
  751. -- SPI ID 1, 片选 GPIO12
  752. if netdrv.setup(socket.LWIP_USER1, other_configs.main_adapter.tp, other_configs.main_adapter.opts) == false then
  753. log.error("以太网初始化失败")
  754. if other_configs.main_adapter.ethpower_en then
  755. gpio.close(other_configs.main_adapter.ethpower_en)
  756. end
  757. return false
  758. end
  759. netdrv.dhcp(socket.LWIP_USER1, true)
  760. log.info("以太网初始化完成")
  761. local count = 1
  762. while 1 do
  763. local ip = netdrv.ipv4(socket.LWIP_USER1)
  764. if ip and ip ~= "0.0.0.0" then break end
  765. if count > 600 then
  766. log.error("以太网连接超时,请检查配置")
  767. if other_configs.main_adapter.ethpower_en then
  768. gpio.close(other_configs.main_adapter.ethpower_en)
  769. end
  770. return false
  771. end
  772. sys.wait(100)
  773. count = count + 1
  774. end
  775. elseif main_adapter == socket.LWIP_STA and available[socket.LWIP_STA] == connection_states.DISCONNECTED then
  776. -- 打开STA功能,设置混合模式
  777. wlan.init()
  778. wlan.setMode(wlan.APSTA)
  779. available[socket.LWIP_STA] = connection_states.OPENED
  780. -- 尝试连接Wi-Fi,并处理可能出现的错误
  781. wlan.connect(other_configs.main_adapter.ssid, other_configs.main_adapter.password)
  782. -- 等待获取IP地址
  783. local count = 1
  784. while 1 do
  785. local ip = netdrv.ipv4(socket.LWIP_STA)
  786. if ip and ip ~= "0.0.0.0" then
  787. log.info("WiFi STA已连接,IP:", ip)
  788. break
  789. end
  790. if count > 600 then
  791. log.error("WiFi STA连接超时,请检查配置")
  792. return false
  793. end
  794. sys.wait(100)
  795. count = count + 1
  796. end
  797. log.info("WiFi STA初始化完成")
  798. end
  799. if adapter == socket.LWIP_ETH then
  800. log.info("ch390", "打开LDO供电", other_configs.ethpower_en)
  801. if other_configs.ethpower_en then
  802. gpio.setup(other_configs.ethpower_en, 1, gpio.PULLUP)
  803. end
  804. -- 打开LAN功能
  805. -- 配置 SPI 参数,Air8000 使用 SPI 接口与以太网模块进行通信。
  806. if other_configs.tp then
  807. log.info("netdrv spi挂载以太网", "初始化LAN功能")
  808. local result = spi.setup(
  809. other_configs.opts.spi, -- spi id
  810. nil, 0, -- CPHA
  811. 0, -- CPOL
  812. 8, -- 数据宽度
  813. 25600000 -- ,--波特率
  814. )
  815. log.info("main", "open spi", result)
  816. if result ~= 0 then -- 返回值为 0,表示打开成功
  817. log.error("main", "spi open error", result)
  818. if other_configs.ethpower_en then
  819. gpio.close(other_configs.ethpower_en)
  820. end
  821. return false
  822. end
  823. end
  824. -- 初始化以太网,Air8000 指定使用 CH390 芯片。
  825. log.info("netdrv", "初始化以太网", other_configs.tp, other_configs.opts)
  826. if netdrv.setup(socket.LWIP_ETH, other_configs.tp, other_configs.opts) == false then
  827. log.error("初始化以太网失败")
  828. if other_configs.ethpower_en then
  829. gpio.close(other_configs.ethpower_en)
  830. end
  831. return false
  832. end
  833. sys.wait(1000) -- 等待以太网模块初始化完成,去掉会导致以太网初始化失败
  834. -- 设置以太网的 IP 地址、子网掩码、网关地址
  835. log.info("netdrv", "自定义以太网IP地址", other_configs.adapter_addr, "网关地址", other_configs.adapter_gw)
  836. netdrv.ipv4(socket.LWIP_ETH, other_configs.adapter_addr or "192.168.5.1", "255.255.255.0", "0.0.0.0")
  837. -- 获取以太网网络状态,连接后返回 true,否则返回 false,如果不存在就返回 nil。
  838. local count = 1
  839. while netdrv.ready(socket.LWIP_ETH) ~= true do
  840. if count > 600 then
  841. log.error("以太网连接超时,请检查配置")
  842. if other_configs.ethpower_en then
  843. gpio.close(other_configs.ethpower_en)
  844. end
  845. return false
  846. end
  847. count = count + 1
  848. -- log.info("netdrv", "等待以太网就绪") -- 若以太网设备没有连上,可打开此处注释排查。
  849. sys.wait(100)
  850. end
  851. log.info("netdrv", "以太网就绪")
  852. -- 创建 DHCP 服务器,为连接到以太网的设备分配 IP 地址。
  853. log.info("netdrv", "创建dhcp服务器, 供以太网使用")
  854. if other_configs.adapter_gw then
  855. dhcpsrv.create({ adapter = socket.LWIP_ETH, gw = other_configs.adapter_gw })
  856. else
  857. dhcpsrv.create({ adapter = socket.LWIP_ETH, gw = { 192, 168, 5, 1 } })
  858. end
  859. -- 创建 DNS 代理服务,使得以太网接口上的设备可以通过 4G 网络访问互联网。
  860. log.info("netdrv", "创建dns代理服务, 供以太网使用")
  861. elseif adapter == socket.LWIP_AP then
  862. wlan.setMode(wlan.APSTA)
  863. -- 打开AP功能,设置混合模式
  864. log.info("执行AP创建操作", airlink.ready(), "正常吗?")
  865. wlan.createAP(other_configs.ssid, other_configs.password, other_configs.adapter_addr or "192.168.4.1",
  866. "255.255.255.0",
  867. other_configs.channel, other_configs.ap_opts)
  868. -- 设置 AP 的 IP 地址、子网掩码、网关地址
  869. netdrv.ipv4(socket.LWIP_AP, other_configs.adapter_addr or "192.168.4.1", "255.255.255.0", "0.0.0.0")
  870. -- 获取 WiFi AP 网络状态,连接后返回 true,否则返回 false,如果不存在就返回 nil。
  871. log.info("netdrv", "等待AP就绪")
  872. local count = 1
  873. while netdrv.ready(socket.LWIP_AP) ~= true do
  874. -- log.info("netdrv", "等待AP就绪")
  875. if count > 600 then
  876. log.error("AP创建超时,请检查配置")
  877. return false
  878. end
  879. sys.wait(100)
  880. count = count + 1
  881. end
  882. -- 创建 DHCP 服务器,为连接到 WiFi AP 的设备分配 IP 地址。
  883. log.info("netdrv", "创建dhcp服务器, 供AP使用")
  884. if other_configs.adapter_gw then
  885. dhcpsrv.create({ adapter = socket.LWIP_AP, gw = other_configs.adapter_gw })
  886. else
  887. dhcpsrv.create({ adapter = socket.LWIP_AP })
  888. end
  889. elseif adapter == socket.LWIP_USER1 then
  890. log.info("ch390", "打开LDO供电", other_configs.ethpower_en)
  891. if other_configs.ethpower_en then
  892. gpio.setup(other_configs.ethpower_en, 1, gpio.PULLUP)
  893. end
  894. -- 打开LAN功能
  895. -- 配置 SPI 参数,Air8101 使用 SPI 接口与以太网模块进行通信。
  896. log.info("netdrv spi挂载以太网", "初始化LAN功能")
  897. local result = spi.setup(
  898. other_configs.opts.spi, -- spi id
  899. nil, 0, -- CPHA
  900. 0, -- CPOL
  901. 8, -- 数据宽度
  902. 25600000 -- ,--波特率
  903. )
  904. log.info("main", "open spi", result)
  905. if result ~= 0 then -- 返回值为 0,表示打开成功
  906. log.error("main", "spi open error", result)
  907. if other_configs.ethpower_en then
  908. gpio.close(other_configs.ethpower_en)
  909. end
  910. return false
  911. end
  912. -- 初始化以太网,Air8000 指定使用 CH390 芯片。
  913. log.info("netdrv", "初始化以太网", other_configs.tp, other_configs.opts)
  914. if netdrv.setup(socket.LWIP_USER1, other_configs.tp, other_configs.opts) == false then
  915. log.error("初始化以太网失败")
  916. if other_configs.ethpower_en then
  917. gpio.close(other_configs.ethpower_en)
  918. end
  919. return false
  920. end
  921. sys.wait(1000) -- 等待以太网模块初始化完成,去掉会导致以太网初始化失败
  922. -- 设置以太网的 IP 地址、子网掩码、网关地址
  923. log.info("netdrv", "自定义以太网IP地址", other_configs.adapter_addr, "网关地址", other_configs.adapter_gw)
  924. netdrv.ipv4(socket.LWIP_USER1, other_configs.adapter_addr or "192.168.5.1", "255.255.255.0", "0.0.0.0")
  925. -- 获取以太网网络状态,连接后返回 true,否则返回 false,如果不存在就返回 nil。
  926. local count = 1
  927. while netdrv.ready(socket.LWIP_USER1) ~= true do
  928. if count > 600 then
  929. log.error("以太网连接超时,请检查配置")
  930. if other_configs.ethpower_en then
  931. gpio.close(other_configs.ethpower_en)
  932. end
  933. return false
  934. end
  935. count = count + 1
  936. -- log.info("netdrv", "等待以太网就绪") -- 若以太网设备没有连上,可打开此处注释排查。
  937. sys.wait(100)
  938. end
  939. log.info("netdrv", "以太网就绪")
  940. -- 创建 DHCP 服务器,为连接到以太网的设备分配 IP 地址。
  941. log.info("netdrv", "创建dhcp服务器, 供以太网使用")
  942. if other_configs.adapter_gw then
  943. dhcpsrv.create({ adapter = socket.LWIP_USER1, gw = other_configs.adapter_gw })
  944. else
  945. dhcpsrv.create({ adapter = socket.LWIP_USER1, gw = { 192, 168, 5, 1 } })
  946. end
  947. -- 创建 DNS 代理服务,使得以太网接口上的设备可以通过 4G 网络访问互联网。
  948. log.info("netdrv", "创建dns代理服务, 供以太网使用")
  949. end
  950. dnsproxy.setup(adapter, main_adapter)
  951. netdrv.napt(main_adapter)
  952. return true
  953. end
  954. return exnetif