Files
BR_YKC/4G/tools/resource/soc_script/v2025.12.31.22/lib/exnetif.lua
2026-03-31 15:46:04 +08:00

1136 lines
49 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
--[[
@module exnetif
@summary exnetif 控制网络优先级(以太网->WIFI->4G根据优先级选择上网的网卡。简化开启多网融合的操作4G作为数据出口给WIFI,以太网设备上网以太网作为数据出口给WIFI,Air8000上网WIFI作为数据出口给Air8000,以太网上网。
@version 1.0
@date 2025.06.26
@author wjq
@usage
本文件的对外接口有5个
1、exnetif.set_priority_order(networkConfigs):设置网络优先级顺序并初始化对应网络(需要在task中调用)
2、exnetif.notify_status(cb_fnc):设置网络状态变化回调函数
3、exnetif.setproxy(adapter, main_adapter,other_configs):配置网络代理实现多网融合(需要在task中调用)
4、exnetif.check_network_status(interval),检测间隔时间ms(选填),不填时只检测一次,填写后将根据间隔时间循环检测,会提高模块功耗
5、exnetif.close(type, adapter):关闭指定网卡,内核固件版本号需>=2020
]]
local exnetif = {}
dnsproxy = require("dnsproxy")
dhcpsrv = require("dhcpsrv")
httpdns = require("httpdns")
-- 设置pingip
local wifi_ping_ip
local eth_ping_ip
-- 局域网模式
local local_network_mode
-- 是否需要通过ping来测试网络的连通性
local need_ping = true
-- 使用单网卡模式
local single_network_mode = false
-- 是否自动关闭非当前网卡socket连接
local auto_socket_switch = true
-- ping检测间隔时间
local ping_time = 10000
-- 连接状态
local connection_states = {
DISCONNECTED = 0,
CONNECTING = 1,
CONNECTED = 2,
OPENED = 3,
SINGLE_NETWORK = 4,
STOPED = 5
}
-- 状态回调函数
local states_cbfnc = function(net_type)
end
-- 当前优先级
local current_priority = {socket.LWIP_ETH, socket.LWIP_STA, socket.LWIP_GP}
-- 连接状态
local available = {
[socket.LWIP_STA] = connection_states.DISCONNECTED,
[socket.LWIP_ETH] = connection_states.DISCONNECTED,
[socket.LWIP_GP] = connection_states.DISCONNECTED,
[socket.LWIP_USER1] = connection_states.DISCONNECTED
}
-- 当前使用的网卡
local current_active = nil
-- 网络类型转字符串
local function type_to_string(net_type)
local type_map = {
[socket.LWIP_STA] = "WiFi",
[socket.LWIP_ETH] = "Ethernet",
[socket.LWIP_GP] = "4G",
[socket.LWIP_USER1] = "8101SPIETH"
}
return type_map[net_type] or "Unknown"
end
-- 保存以太网网卡参数用于关闭spi和供电使能
local eth_cfg = {
[socket.LWIP_ETH] = {},
[socket.LWIP_USER1] = {}
}
-- 订阅socket连接状态变化事件socket出现异常时修改网卡状态
local function socket_state_detection(adapter)
if netdrv.on then
log.info("netdrv", "订阅socket连接状态变化事件", type_to_string(adapter))
-- 订阅socket连接状态变化事件
netdrv.on(adapter, netdrv.EVT_SOCKET, function(id, event, params)
if event == "timeout" or event == "error" then
if available[adapter] == connection_states.CONNECTED then
available[adapter] = connection_states.CONNECTING
end
end
-- log.info("netdrv", "socket event", id, event, json.encode(params or {}))
-- if params then
-- -- params里会有remote_ip, remote_port等信息, 可按需获取
-- local remote_ip = params.remote_ip
-- local remote_port = params.remote_port
-- local domain_name = params.domain_name
-- log.info("netdrv", "socket event", "remote_ip", remote_ip, "remote_port", remote_port, "domain_name", domain_name)
-- end
end)
end
end
-- 状态更改后重新设置默认网卡
local function apply_priority()
local usable = false
-- 查找优先级最高的可用网络
for _, net_type in ipairs(current_priority) do
-- log.info("网卡顺序",type_to_string(net_type),available[net_type])
if available[net_type] == connection_states.CONNECTED then
usable = true
-- 设置优先级高的网卡
if current_active ~= net_type then
log.info("设置网卡", type_to_string(net_type))
states_cbfnc(type_to_string(net_type), net_type) -- 默认网卡改变的回调函数
socket.dft(net_type)
if auto_socket_switch and socket.close_all then
socket.close_all(current_active)
end
current_active = net_type
end
break
end
end
-- 从存在可用网卡到没有可用网卡,才通知回调
if usable == false and current_active ~= nil then
-- 避免重复通知
current_active = nil
states_cbfnc(nil, -1)
end
end
-- httpdns域名解析测试
local function http_dnstest(adaptertest)
local ip = httpdns.ali("baidu.com", {
adapter = adaptertest,
timeout = 3000
})
if ip ~= nil then
available[adaptertest] = connection_states.CONNECTED
log.info(type_to_string(adaptertest) .. "网卡httpdns域名解析成功")
else
log.info(type_to_string(adaptertest) .. "网卡httpdns域名解析失败")
end
log.info("httpdns", "baidu.com", ip)
end
-- ping操作
local function ping_request(adaptertest)
log.info("dns_request", type_to_string(adaptertest), need_ping)
if need_ping then
if adaptertest == socket.LWIP_ETH or adaptertest == socket.LWIP_USER1 then
if eth_ping_ip == nil then
http_dnstest(adaptertest)
else
icmp.setup(adaptertest)
icmp.ping(adaptertest, eth_ping_ip)
end
end
if adaptertest == socket.LWIP_STA then
if wifi_ping_ip == nil then
http_dnstest(adaptertest)
else
icmp.setup(adaptertest)
icmp.ping(adaptertest, wifi_ping_ip)
end
end
if adaptertest == socket.LWIP_GP then
if eth_ping_ip ~= nil then
icmp.setup(adaptertest)
icmp.ping(adaptertest, eth_ping_ip)
elseif wifi_ping_ip ~= nil then
icmp.setup(adaptertest)
icmp.ping(adaptertest, wifi_ping_ip)
else
http_dnstest(adaptertest)
end
end
else
log.info(type_to_string(adaptertest) .. "配置了不需要ping直接切换为CONNECTED状态")
available[adaptertest] = connection_states.CONNECTED
end
apply_priority()
end
-- 网卡上线回调函数
local function ip_ready_handle(ip, adapter)
local _, _, gw = socket.localIP(adapter)
log.info("ip_ready_handle", ip, type_to_string(adapter), "state", available[adapter], "gw", gw)
if local_network_mode then
if adapter == socket.LWIP_ETH or adapter == socket.LWIP_USER1 then
eth_ping_ip = gw
elseif adapter == socket.LWIP_STA then
wifi_ping_ip = gw
end
end
log.info("eth_ping_ip", eth_ping_ip, "wifi_ping_ip", wifi_ping_ip)
-- 需要ping操作ping通后认为网络可用
if available[adapter] == connection_states.OPENED then
available[adapter] = connection_states.CONNECTING
end
-- ping_request(adapter)
end
-- 网卡下线回调函数
local function ip_lose_handle(adapter)
log.info("ip_lose_handle", type_to_string(adapter))
if available[adapter] == connection_states.CONNECTING or available[adapter] == connection_states.CONNECTED then
available[adapter] = connection_states.OPENED
end
if current_active == adapter then
log.info(type_to_string(adapter) .. " 失效,切换到其他网络")
apply_priority()
end
end
local interval_time = nil
--[[
对正常状态的网卡进行ping测试
@api exnetif.check_network_status(interval),
@int 检测间隔时间ms(选填),不填时只检测一次,填写后将根据间隔时间循环检测,会提高模块功耗
]]
function exnetif.check_network_status(interval)
if interval ~= nil then
interval_time = interval
end
for _, net_type in ipairs(current_priority) do
if available[net_type] == connection_states.CONNECTED then
available[net_type] = connection_states.CONNECTING
end
end
end
-- 打开以太网Wan功能
local function setup_eth(config)
if config.local_network_mode then
local_network_mode = true
end
if config.need_ping ~= nil then
need_ping = config.need_ping
end
if config.auto_socket_switch ~= nil then
auto_socket_switch = config.auto_socket_switch
-- log.info("设置自动关闭非当前网卡socket连接", auto_socket_switch)
end
eth_ping_ip = config.ping_ip
if type(config.ping_time) == "number" then
ping_time = config.ping_time
end
log.info("初始化以太网")
if not single_network_mode then
available[socket.LWIP_ETH] = connection_states.OPENED
else
available[socket.LWIP_ETH] = connection_states.SINGLE_NETWORK
end
-- 打开CH390供电
if config.pwrpin then
gpio.setup(config.pwrpin, 1, gpio.PULLUP)
end
-- sys.wait(100) -- 等待以太网模块上电稳定
if config.tp == nil then
log.info("8101以太网")
if netdrv.setup(socket.LWIP_ETH) == false then
log.error("以太网初始化失败")
if config.pwrpin then
gpio.close(config.pwrpin)
end
return false
end
else
log.info("config.opts.spi", config.opts.spi, ",config.type", config.tp)
-- 配置SPI和初始化网络驱动
local result = spi.setup(config.opts.spi, -- spi id
nil, 0, -- CPHA
0, -- CPOL
8, -- 数据宽度
25600000 -- ,--波特率
)
log.info("main", "open spi", result)
if result ~= 0 then -- 返回值为0表示打开成功
log.info("main", "spi open error", result)
if config.pwrpin then
gpio.close(config.pwrpin)
end
return false
end
-- 初始化指定netdrv设备,
-- socket.LWIP_ETH 网络适配器编号
-- netdrv.CH390外挂CH390
-- SPI ID 1, 片选 GPIO12
if netdrv.setup(socket.LWIP_ETH, config.tp, config.opts) == false then
log.error("以太网初始化失败")
if config.pwrpin then
gpio.close(config.pwrpin)
end
return false
end
end
if config.static_ip then
sys.wait(1000) -- 等待以太网模块初始化完成,去掉会导致以太网初始化失败
log.info("netdrv", "自定义以太网IP地址", config.static_ip.ipv4)
log.info("静态ip",
netdrv.ipv4(socket.LWIP_ETH, config.static_ip.ipv4, config.static_ip.mark, config.static_ip.gw))
else
netdrv.dhcp(socket.LWIP_ETH, true)
end
log.info("以太网初始化完成")
socket_state_detection(socket.LWIP_ETH)
return true
end
-- 打开8101spi以太网Wan功能
local function setup_eth_user1(config)
if config.local_network_mode then
local_network_mode = true
end
if config.need_ping ~= nil then
need_ping = config.need_ping
end
if config.auto_socket_switch ~= nil then
auto_socket_switch = config.auto_socket_switch
-- log.info("设置自动关闭非当前网卡socket连接", auto_socket_switch)
end
eth_ping_ip = config.ping_ip
if type(config.ping_time) == "number" then
ping_time = config.ping_time
end
log.info("初始化以太网")
if not single_network_mode then
available[socket.LWIP_USER1] = connection_states.OPENED
else
available[socket.LWIP_USER1] = connection_states.SINGLE_NETWORK
end
-- 打开CH390供电
if config.pwrpin then
gpio.setup(config.pwrpin, 1, gpio.PULLUP)
end
-- sys.wait(100)-- 等待以太网模块上电稳定
log.info("config.opts.spi", config.opts.spi, ",config.type", config.tp)
-- 配置SPI和初始化网络驱动
local result = spi.setup(config.opts.spi, -- spi id
nil, 0, -- CPHA
0, -- CPOL
8, -- 数据宽度
25600000 -- ,--波特率
)
log.info("main", "open spi", result)
if result ~= 0 then -- 返回值为0表示打开成功
log.info("main", "spi open error", result)
if config.pwrpin then
gpio.close(config.pwrpin)
end
return false
end
-- 初始化指定netdrv设备,
-- socket.LWIP_ETH 网络适配器编号
-- netdrv.CH390外挂CH390
-- SPI ID 1, 片选 GPIO12
if netdrv.setup(socket.LWIP_USER1, config.tp, config.opts) == false then
log.error("以太网初始化失败")
if config.pwrpin then
gpio.close(config.pwrpin)
end
return false
end
if config.static_ip then
sys.wait(1000) -- 等待以太网模块初始化完成,去掉会导致以太网初始化失败
log.info("netdrv", "自定义以太网IP地址", config.static_ip.ipv4)
log.info("静态ip",
netdrv.ipv4(socket.LWIP_USER1, config.static_ip.ipv4, config.static_ip.mark, config.static_ip.gw))
else
netdrv.dhcp(socket.LWIP_USER1, true)
end
log.info("以太网初始化完成")
socket_state_detection(socket.LWIP_USER1)
return true
end
-- 连接wifi(STA模式)
local function set_wifi_info(config)
if config.local_network_mode then
local_network_mode = true
end
if config.need_ping ~= nil then
need_ping = config.need_ping
end
if config.auto_socket_switch ~= nil then
auto_socket_switch = config.auto_socket_switch
-- log.info("设置自动关闭非当前网卡socket连接", auto_socket_switch)
end
wifi_ping_ip = config.ping_ip
if type(config.ping_time) == "number" then
ping_time = config.ping_time
end
log.info("WiFi名称:", config.ssid)
log.info("密码 :", config.password)
log.info("ping_ip :", config.ping_ip)
wlan.init()
if not single_network_mode then
available[socket.LWIP_STA] = connection_states.OPENED
else
available[socket.LWIP_STA] = connection_states.SINGLE_NETWORK
end
-- 尝试连接Wi-Fi并处理可能出现的错误
local success = wlan.connect(config.ssid, config.password)
if not success then
log.error("WiFi连接失败")
return false
end
log.info("WiFi STA初始化完成")
socket_state_detection(socket.LWIP_STA)
return true
end
-- 以太网处于stop状态重启以太网
local function eth_net_restart(adapter)
log.info("以太网处于stop状态重启下")
if not single_network_mode then
available[adapter] = connection_states.OPENED
else
available[adapter] = connection_states.SINGLE_NETWORK
end
-- 打开CH390供电
if eth_cfg[adapter].pwrpin then
log.info("打开以太网供电引脚:", eth_cfg[adapter].pwrpin)
gpio.setup(eth_cfg[adapter].pwrpin, 1, gpio.PULLUP)
end
-- sys.wait(100) -- 等待以太网模块上电稳定
if eth_cfg[adapter].tp == nil then
log.info("8101以太网")
else
log.info("config.opts.spi", eth_cfg[adapter].opts.spi, ",config.type", eth_cfg[adapter].tp)
-- 配置SPI和初始化网络驱动
local result = spi.setup(eth_cfg[adapter].opts.spi, -- spi id
nil, 0, -- CPHA
0, -- CPOL
8, -- 数据宽度
25600000 -- ,--波特率
)
log.info("main", "open spi", result)
if result ~= 0 then -- 返回值为0表示打开成功
log.info("main", "spi open error", result)
if eth_cfg[adapter].pwrpin then
gpio.close(eth_cfg[adapter].pwrpin)
end
return false
end
end
netdrv.ctrl(adapter, netdrv.CTRL_UPDOWN, 1)
end
-- ping测试循环
local function ping_test_loop()
while true do
for _, net_type in ipairs(current_priority) do
-- log.info("网卡顺序",type_to_string(net_type),available[net_type])
if available[net_type] == connection_states.CONNECTING then
log.info(type_to_string(net_type) .. "网卡开始PING")
ping_request(net_type)
sys.wait(ping_time)
end
end
sys.wait(1000) -- 避免死循环
end
end
-- 正常状态网卡的检测任务
local function normal_network_test()
while true do
if interval_time ~= nil then
sys.wait(interval_time)
exnetif.check_network_status()
end
sys.wait(1000) -- 避免死循环
end
end
-- PING结果处理函数
local function ping_res(id, time, dst)
log.info("ping", id, time, dst)
log.info(type_to_string(id) .. "网卡PING测试成功")
available[id] = connection_states.CONNECTED
apply_priority()
end
--[[
设置网络优先级相应网卡获取到ip且网络正常视为网卡可用丢失ip视为网卡不可用.(需要在task中调用)
@api exnetif.set_priority_order(new_priority)
@table 网络优先级列表,优先级从高到低对应table中的第一个参数到最后一个参数
@return boolean 成功返回true失败返回false
@usage
多网优先级模式:
exnetif.set_priority_order({
{ -- 最高优先级网络
WIFI = { -- WiFi配置
ssid = "your_ssid", -- WiFi名称(string)
password = "your_pwd", -- WiFi密码(string)
need_ping = true, -- 是否需要通过ping来测试网络的连通性
-- 在没有ping测试环境的项目中需要将这个参数设置为false表示不需要ping测试网络连通
-- 仅根据IP READY消息即获取到了ip地址来判断网络环境准备就绪是否网络连通性则无法保证
-- 如果没有设置此参数默认为true
-- 在有ping测试环境的项目中建议不要将这个参数设置为true
local_network_mode = true,-- 局域网模式(选填参数)设置为true时exnetif会自动将ping_ip设置为网卡的网关ip。
-- 用户不需要传入ping_ip参数即使传入了也无效。
-- 这个模式的使用场景,仅适用于局域网环境;可以访问外网时,不要使用
ping_ip = "112.125.89.8", -- 连通性检测IP(选填参数),默认使用httpdns获取baidu.com的ip作为判断条件
-- 注如果填写ip则ping通作为判断网络是否可用的条件
-- 所以需要根据网络环境填写内网或者外网ip,
-- 填写外网ip的话要保证外网ip始终可用
-- 填写局域网ip的话要确保相应ip固定且能够被ping通
ping_time = 10000 -- 填写ping_ip且未ping通时的检测间隔(ms, 可选默认为10秒)
-- 定时ping将会影响模块功耗使用低功耗模式的话可以适当延迟间隔时间
}
},
{ -- 次优先级网络
ETHERNET = { -- 以太网配置
pwrpin = 140, -- 供电使能引脚(number)
need_ping = true, -- 是否需要通过ping来测试网络的连通性
-- 在没有ping测试环境的项目中需要将这个参数设置为false表示不需要ping测试网络连通
-- 仅根据IP READY消息即获取到了ip地址来判断网络环境准备就绪是否网络连通性则无法保证
-- 如果没有设置此参数默认为true
-- 在有ping测试环境的项目中建议不要将这个参数设置为true
local_network_mode = true, -- 局域网模式(选填参数)设置为true时exnetif会自动将ping_ip设置为网卡的网关ip。
-- 用户不需要传入ping_ip参数即使传入了也无效。
-- 这个模式的使用场景,仅适用于局域网环境;可以访问外网时,不要使用
ping_ip = "112.125.89.8", -- 连通性检测IP(选填参数),默认使用httpdns获取baidu.com的ip作为判断条件
-- 注如果填写ip则ping通作为判断网络是否可用的条件
-- 所以需要根据网络环境填写内网或者外网ip,
-- 填写外网ip的话要保证外网ip始终可用
-- 填写局域网ip的话要确保相应ip固定且能够被ping通
ping_time = 10000, -- 填写ping_ip且未ping通时的检测间隔(ms, 可选,默认为10秒)
-- 定时ping将会影响模块功耗使用低功耗模式的话可以适当延迟间隔时间
tp = netdrv.CH390, -- 网卡芯片型号(选填参数)仅spi方式外挂以太网时需要填写。
opts = { spi = 1, cs = 12 }, -- 外挂方式,需要额外的参数(选填参数)仅spi方式外挂以太网时需要填写。
static_ip = { -- 静态ip配置(选填参数)不填写则使用dhcp获取ip
ipv4 = "192.168.5.100", -- ip地址(string)
mark = "255.255.255.0", -- 子网掩码(string)
gw = "192.168.5.1" -- 网关地址(string)
}
}
},
{ -- 最低优先级网络
LWIP_GP = true -- 启用4G网络
}
})
单网络模式:
-- 单网络模式下只使用WIFI网络
exnetif.set_priority_order({
{ -- 单网络打开wifi
WIFI = { -- WiFi配置
ssid = "test", -- WiFi名称(string)
password = "HZ88888888", -- WiFi密码(string)
}
}
})
-- Air8000系列和780EXX系列单网络模式下只使用SPI以太网网络
exnetif.set_priority_order({
{
ETHERNET = { -- 以太网配置
pwrpin = 140, -- 供电使能引脚(number)
tp = netdrv.CH390, -- 网卡芯片型号(选填参数)仅spi方式外挂以太网时需要填写。
opts = {spi = 1, cs = 12}, -- 外挂方式,需要额外的参数(选填参数)仅spi方式外挂以太网时需要填写。
}
}
})
-- Air8101单网络模式下只使用SPI以太网网络
exnetif.set_priority_order({
{
ETHUSER1 = { -- 以太网配置
pwrpin = 13, -- 供电使能引脚(number)
tp = netdrv.CH390, -- 网卡芯片型号(选填参数)仅spi方式外挂以太网时需要填写。
opts = {spi = 0, cs = 15}, -- 外挂方式,需要额外的参数(选填参数)仅spi方式外挂以太网时需要填写。
static_ip = { -- 静态ip配置(选填参数)不填写则使用dhcp获取ip
ipv4 = "192.168.5.100", -- ip地址(string)
mark = "255.255.255.0", -- 子网掩码(string)
gw = "192.168.5.1" -- 网关地址(string)
}
}
}
})
-- 单网络模式下只使用RMII以太网网络
exnetif.set_priority_order({
{
ETHERNET = { -- 以太网配置
pwrpin = 13, -- 供电使能引脚(number)
}
}
})
-- 4G单网模式下不需要require "exnetif",减少不必要的功能模块加载
]]
function exnetif.set_priority_order(networkConfigs)
-- 判断表中数据个数
if #networkConfigs < 1 then
log.error("网络配置为空")
return false
end
if #networkConfigs == 1 then
single_network_mode = true
end
if not single_network_mode then
-- CONNECTING的网卡需要定时ping
sys.taskInit(ping_test_loop)
-- 循环ping检测正常网卡的任务默认不启用
sys.taskInit(normal_network_test)
sys.subscribe("PING_RESULT", ping_res)
-- 订阅网络状态变化的消息
sys.subscribe("IP_READY", ip_ready_handle)
sys.subscribe("IP_LOSE", ip_lose_handle)
end
local new_priority = {}
-- 只有关闭状态的网卡需要初始化
for _, config in ipairs(networkConfigs) do
if type(config.WIFI) == "table" then
if available[socket.LWIP_STA] == connection_states.DISCONNECTED then
-- 开启wifi
local res = set_wifi_info(config.WIFI)
if res == false then
log.error("wifi连接失败")
return false
end
else
log.info("wifi不是关闭状态跳过初始化")
end
table.insert(new_priority, socket.LWIP_STA)
end
if type(config.ETHUSER1) == "table" then
eth_cfg[socket.LWIP_USER1] = config.ETHUSER1
if available[socket.LWIP_USER1] == connection_states.DISCONNECTED then
-- 开启以太网
local res = setup_eth_user1(config.ETHUSER1)
if res == false then
log.error("以太网打开失败")
return false
end
elseif available[socket.LWIP_USER1] == connection_states.STOPED then
eth_net_restart(socket.LWIP_USER1)
else
log.info("8101spi以太网不是关闭状态跳过初始化")
end
table.insert(new_priority, socket.LWIP_USER1)
end
if type(config.ETHERNET) == "table" then
eth_cfg[socket.LWIP_ETH] = config.ETHERNET
if available[socket.LWIP_ETH] == connection_states.DISCONNECTED then
-- 开启以太网
local res = setup_eth(config.ETHERNET)
if res == false then
log.error("以太网打开失败")
return false
end
elseif available[socket.LWIP_ETH] == connection_states.STOPED then
eth_net_restart(socket.LWIP_ETH)
else
log.info("以太网不是关闭状态,跳过初始化")
end
table.insert(new_priority, socket.LWIP_ETH)
end
if config.LWIP_GP then
-- 退出飞行模式
mobile.flymode(nil, false)
-- 开启4G
table.insert(new_priority, socket.LWIP_GP)
available[socket.LWIP_GP] = connection_states.CONNECTING
if config.auto_socket_switch ~= nil then
auto_socket_switch = config.auto_socket_switch
-- log.info("设置自动关闭非当前网卡socket连接", auto_socket_switch)
end
end
end
-- 设置新优先级
current_priority = new_priority
-- 此处按照用户期望的配置,先设置优先级最高的默认网卡
-- 防止出现以下问题:
-- 例如Air8000内核固件运行起来之后默认网卡是socket.LWIP_GP如果用户调用exnetif.set_priority_order接口配置最高优先级网卡为socket.LWIP_ETH
-- 在socket.LWIP_ETH网卡准备就绪之前socket.LWIP_GP可能已经准备就绪此时默认网卡仍然是socket.LWIP_GP
-- 而网络应用层例如socketmqtt等有关的demo我们编写时不关心具体网卡直接使用默认网卡这样符合正常逻辑
-- 就可能会出现“网络应用在这段时间内直接使用socket.LWIP_GP而不是用户期望的网卡socket.LWIP_ETH来上网”的问题
socket.dft(new_priority[1])
apply_priority()
return true
end
--[[
设置网络状态变化回调函数。触发条件:网卡切换或者所有网卡都断网。回调函数的输入参数: 1. 当有可用网络的时候返回当前使用网卡、网卡id2. 当没有可用网络的时候,返回 nil、-1 。
@api exnetif.notify_status(cb_fnc)
@function 回调函数
@usage
exnetif.notify_status(function(net_type,adapter)
log.info("可以使用优先级更高的网络:", net_type,adapter)
end)
]]
function exnetif.notify_status(cb_fnc)
log.info("notify_status", type(cb_fnc))
if type(cb_fnc) ~= "function" then
log.error("notify_status设置错误请传入一个函数")
return
end
states_cbfnc = cb_fnc
end
--[[
设置多网融合模式例如4G作为数据出口给WIFI或以太网设备上网(需要在task中调用)
@api exnetif.setproxy(adapter, main_adapter,other_configs)
@adapter 需要使用网络的网卡例如socket.LWIP_ETH
@adapter 提供网络的网卡例如socket.LWIP_GP
@table 其他设置参数(选填参数)
@usage
典型应用:
-- 以太网WAN提供网络其他设备连接以太网LAN口上网
exnetif.setproxy(socket.LWIP_ETH, socket.LWIP_USER1, {
ethpower_en = 20,-- 以太网模块的pwrpin引脚(gpio编号)
tp = netdrv.CH390, -- 网卡芯片型号(选填参数)仅spi方式外挂以太网时需要填写。
opts = {spi = 0, cs = 8}, -- 外挂方式,需要额外的参数(选填参数)仅spi方式外挂以太网时需要填写。
main_adapter = {
ethpower_en = 21,-- 以太网模块的pwrpin引脚(gpio编号)
tp = netdrv.CH390, -- 网卡芯片型号(选填参数)仅spi方式外挂以太网时需要填写。
opts = {spi = 1, cs = 12}
}
}) then
-- wifi_sta提供网络开启wifi_ap热点供设备上网
exnetif.setproxy(socket.LWIP_AP, socket.LWIP_STA, {
ssid = "test2", -- AP热点名称(string)网卡包含wifi时填写
password = "HZ88888888", -- AP热点密码(string)网卡包含wifi时填写
ap_opts = { -- AP模式下配置项(选填参数)
hidden = false, -- 是否隐藏SSID, 默认false,不隐藏
max_conn = 4 }, -- 最大客户端数量, 默认4
channel = 6, -- AP建立的通道, 默认6
main_adapter = {
ssid = "test", -- 提供网络的网卡开启参数
password = "HZ88888888"
}
})
-- 4G提供网络开启wifi_ap热点供设备上网,其他设备连接以太网LAN口上网
exnetif.setproxy(socket.LWIP_AP, socket.LWIP_GP, {
ssid = "Hotspot", -- WiFi名称(string)网卡包含wifi时填写
password = "password123", -- WiFi密码(string)网卡包含wifi时填写
adapter_addr = "192.168.5.1", -- adapter网卡的ip地址(选填),需要自定义ip和网关ip时填写
adapter_gw= { 192, 168, 5, 1 }, -- adapter网卡的网关地址(选填),需要自定义ip和网关ip时填写
ap_opts={ -- AP模式下配置项(选填参数)
hidden = false, -- 是否隐藏SSID, 默认false,不隐藏
max_conn = 4 }, -- 最大客户端数量, 默认4
channel=6 -- AP建立的通道, 默认6
})
exnetif.setproxy(socket.LWIP_ETH, socket.LWIP_GP, {
tp = netdrv.CH390, -- 网卡芯片型号(选填参数)仅spi方式外挂以太网时需要填写。
opts = { spi = 1, cs = 12}, -- 外挂方式,需要额外的参数(选填参数)仅spi方式外挂以太网时需要填写。
ethpower_en = 140, -- 以太网模块的pwrpin引脚(gpio编号)
adapter_addr = "192.168.5.1", -- adapter网卡的ip地址(选填),需要自定义ip和网关ip时填写
adapter_gw= { 192, 168, 5, 1 }, -- adapter网卡的网关地址(选填),需要自定义ip和网关ip时填写
})
-- 以太网提供网络供WiFi设备上网
exnetif.setproxy(socket.LWIP_AP, socket.LWIP_ETH, {
ssid = "Hotspot", -- WiFi名称(string)网卡包含wifi时填写
password = "password123", -- WiFi密码(string)网卡包含wifi时填写
main_adapter={
tp = netdrv.CH390, -- 网卡芯片型号(选填参数)仅spi方式外挂以太网时需要填写。
opts = { spi = 1, cs = 12}, -- 外挂方式,需要额外的参数(选填参数)仅spi方式外挂以太网时需要填写。
ethpower_en = 140, -- 以太网模块的pwrpin引脚(gpio编号)
}
})
-- WIFI提供网络供以太网设备上网
exnetif.setproxy(socket.LWIP_ETH, socket.LWIP_STA, {
tp = netdrv.CH390, -- 网卡芯片型号(选填参数)仅spi方式外挂以太网时需要填写。
opts = { spi = 1, cs = 12}, -- 外挂方式,需要额外的参数(选填参数)仅spi方式外挂以太网时需要填写。
ethpower_en = 140, -- 以太网模块的pwrpin引脚(gpio编号)
main_adapter = {
ssid = "test", -- 提供网络的网卡开启参数
password = "HZ88888888"
}
})
]]
function exnetif.setproxy(adapter, main_adapter, other_configs)
if main_adapter == socket.LWIP_ETH and available[socket.LWIP_ETH] == connection_states.DISCONNECTED then
-- 打开WAN功能
log.info("ch390", "打开LDO供电", other_configs.main_adapter.ethpower_en)
available[socket.LWIP_ETH] = connection_states.OPENED
-- 打开CH390供电
if other_configs.main_adapter.ethpower_en then
gpio.setup(other_configs.main_adapter.ethpower_en, 1, gpio.PULLUP)
end
-- sys.wait(100) -- 等待以太网模块上电稳定
if other_configs.main_adapter.tp == nil then
log.info("8101以太网")
if netdrv.setup(socket.LWIP_ETH) == false then
log.error("以太网初始化失败")
if other_configs.main_adapter.ethpower_en then
gpio.close(other_configs.main_adapter.ethpower_en)
end
return false
end
else
log.info("config.opts.spi", other_configs.main_adapter.opts.spi, ",config.type",
other_configs.main_adapter.tp)
-- 配置SPI和初始化网络驱动
local result = spi.setup(other_configs.main_adapter.opts.spi, -- spi id
nil, 0, -- CPHA
0, -- CPOL
8, -- 数据宽度
25600000 -- ,--波特率
)
log.info("main", "open spi", result)
if result ~= 0 then -- 返回值为0表示打开成功
log.info("main", "spi open error", result)
if other_configs.main_adapter.ethpower_en then
gpio.close(other_configs.main_adapter.ethpower_en)
end
return false
end
-- 初始化指定netdrv设备,
local success =
netdrv.setup(socket.LWIP_ETH, other_configs.main_adapter.tp, other_configs.main_adapter.opts)
if not success then
log.error("以太网初始化失败")
if other_configs.main_adapter.ethpower_en then
gpio.close(other_configs.main_adapter.ethpower_en)
end
return false
end
end
netdrv.dhcp(socket.LWIP_ETH, true)
local count = 1
while 1 do
local ip = netdrv.ipv4(socket.LWIP_ETH)
if ip and ip ~= "0.0.0.0" then
break
end
if count > 600 then
log.error("以太网连接超时,请检查配置")
if other_configs.main_adapter.ethpower_en then
gpio.close(other_configs.main_adapter.ethpower_en)
end
return false
end
sys.wait(100)
count = count + 1
end
elseif main_adapter == socket.LWIP_USER1 and available[socket.LWIP_USER1] == connection_states.DISCONNECTED then
log.info("初始化以太网")
-- 打开CH390供电
if other_configs.main_adapter.ethpower_en then
gpio.setup(other_configs.main_adapter.ethpower_en, 1, gpio.PULLUP)
end
-- sys.wait(100) -- 等待以太网模块上电稳定
log.info("config.opts.spi", other_configs.main_adapter.opts.spi, ",config.type", other_configs.main_adapter.tp)
available[socket.LWIP_USER1] = connection_states.OPENED
-- 配置SPI和初始化网络驱动
local result = spi.setup(other_configs.main_adapter.opts.spi, -- spi id
nil, 0, -- CPHA
0, -- CPOL
8, -- 数据宽度
25600000 -- ,--波特率
)
log.info("main", "open spi", result)
if result ~= 0 then -- 返回值为0表示打开成功
log.info("main", "spi open error", result)
if other_configs.main_adapter.ethpower_en then
gpio.close(other_configs.main_adapter.ethpower_en)
end
return false
end
-- 初始化指定netdrv设备,
-- socket.LWIP_ETH 网络适配器编号
-- netdrv.CH390外挂CH390
-- SPI ID 1, 片选 GPIO12
if netdrv.setup(socket.LWIP_USER1, other_configs.main_adapter.tp, other_configs.main_adapter.opts) == false then
log.error("以太网初始化失败")
if other_configs.main_adapter.ethpower_en then
gpio.close(other_configs.main_adapter.ethpower_en)
end
return false
end
netdrv.dhcp(socket.LWIP_USER1, true)
log.info("以太网初始化完成")
local count = 1
while 1 do
local ip = netdrv.ipv4(socket.LWIP_USER1)
if ip and ip ~= "0.0.0.0" then
break
end
if count > 600 then
log.error("以太网连接超时,请检查配置")
if other_configs.main_adapter.ethpower_en then
gpio.close(other_configs.main_adapter.ethpower_en)
end
return false
end
sys.wait(100)
count = count + 1
end
elseif main_adapter == socket.LWIP_STA and available[socket.LWIP_STA] == connection_states.DISCONNECTED then
-- 打开STA功能设置混合模式
wlan.init()
wlan.setMode(wlan.APSTA)
available[socket.LWIP_STA] = connection_states.OPENED
-- 尝试连接Wi-Fi并处理可能出现的错误
wlan.connect(other_configs.main_adapter.ssid, other_configs.main_adapter.password)
-- 等待获取IP地址
local count = 1
while 1 do
local ip = netdrv.ipv4(socket.LWIP_STA)
if ip and ip ~= "0.0.0.0" then
log.info("WiFi STA已连接IP:", ip)
break
end
if count > 600 then
log.error("WiFi STA连接超时请检查配置")
return false
end
sys.wait(100)
count = count + 1
end
log.info("WiFi STA初始化完成")
end
if adapter == socket.LWIP_ETH then
log.info("ch390", "打开LDO供电", other_configs.ethpower_en)
if other_configs.ethpower_en then
gpio.setup(other_configs.ethpower_en, 1, gpio.PULLUP)
end
-- 打开LAN功能
-- 配置 SPI 参数Air8000 使用 SPI 接口与以太网模块进行通信。
if other_configs.tp then
log.info("netdrv spi挂载以太网", "初始化LAN功能")
local result = spi.setup(other_configs.opts.spi, -- spi id
nil, 0, -- CPHA
0, -- CPOL
8, -- 数据宽度
25600000 -- ,--波特率
)
log.info("main", "open spi", result)
if result ~= 0 then -- 返回值为 0表示打开成功
log.error("main", "spi open error", result)
if other_configs.ethpower_en then
gpio.close(other_configs.ethpower_en)
end
return false
end
end
-- 初始化以太网Air8000 指定使用 CH390 芯片。
log.info("netdrv", "初始化以太网", other_configs.tp, other_configs.opts)
if netdrv.setup(socket.LWIP_ETH, other_configs.tp, other_configs.opts) == false then
log.error("初始化以太网失败")
if other_configs.ethpower_en then
gpio.close(other_configs.ethpower_en)
end
return false
end
sys.wait(1000) -- 等待以太网模块初始化完成,去掉会导致以太网初始化失败
-- 设置以太网的 IP 地址、子网掩码、网关地址
log.info("netdrv", "自定义以太网IP地址", other_configs.adapter_addr, "网关地址",
other_configs.adapter_gw)
netdrv.ipv4(socket.LWIP_ETH, other_configs.adapter_addr or "192.168.5.1", "255.255.255.0", "0.0.0.0")
-- 获取以太网网络状态,连接后返回 true否则返回 false如果不存在就返回 nil。
local count = 1
while netdrv.ready(socket.LWIP_ETH) ~= true do
if count > 600 then
log.error("以太网连接超时,请检查配置")
if other_configs.ethpower_en then
gpio.close(other_configs.ethpower_en)
end
return false
end
count = count + 1
-- log.info("netdrv", "等待以太网就绪") -- 若以太网设备没有连上,可打开此处注释排查。
sys.wait(100)
end
log.info("netdrv", "以太网就绪")
-- 创建 DHCP 服务器,为连接到以太网的设备分配 IP 地址。
log.info("netdrv", "创建dhcp服务器, 供以太网使用")
if other_configs.adapter_gw then
dhcpsrv.create({
adapter = socket.LWIP_ETH,
gw = other_configs.adapter_gw
})
else
dhcpsrv.create({
adapter = socket.LWIP_ETH,
gw = {192, 168, 5, 1}
})
end
-- 创建 DNS 代理服务,使得以太网接口上的设备可以通过 4G 网络访问互联网。
log.info("netdrv", "创建dns代理服务, 供以太网使用")
elseif adapter == socket.LWIP_AP then
wlan.setMode(wlan.APSTA)
-- 打开AP功能设置混合模式
log.info("执行AP创建操作", airlink.ready(), "正常吗?")
wlan.createAP(other_configs.ssid, other_configs.password, other_configs.adapter_addr or "192.168.4.1",
"255.255.255.0", other_configs.channel, other_configs.ap_opts)
-- 设置 AP 的 IP 地址、子网掩码、网关地址
netdrv.ipv4(socket.LWIP_AP, other_configs.adapter_addr or "192.168.4.1", "255.255.255.0", "0.0.0.0")
-- 获取 WiFi AP 网络状态,连接后返回 true否则返回 false如果不存在就返回 nil。
log.info("netdrv", "等待AP就绪")
local count = 1
while netdrv.ready(socket.LWIP_AP) ~= true do
-- log.info("netdrv", "等待AP就绪")
if count > 600 then
log.error("AP创建超时请检查配置")
return false
end
sys.wait(100)
count = count + 1
end
-- 创建 DHCP 服务器,为连接到 WiFi AP 的设备分配 IP 地址。
log.info("netdrv", "创建dhcp服务器, 供AP使用")
if other_configs.adapter_gw then
dhcpsrv.create({
adapter = socket.LWIP_AP,
gw = other_configs.adapter_gw
})
else
dhcpsrv.create({
adapter = socket.LWIP_AP
})
end
elseif adapter == socket.LWIP_USER1 then
log.info("ch390", "打开LDO供电", other_configs.ethpower_en)
if other_configs.ethpower_en then
gpio.setup(other_configs.ethpower_en, 1, gpio.PULLUP)
end
-- 打开LAN功能
-- 配置 SPI 参数Air8101 使用 SPI 接口与以太网模块进行通信。
log.info("netdrv spi挂载以太网", "初始化LAN功能")
local result = spi.setup(other_configs.opts.spi, -- spi id
nil, 0, -- CPHA
0, -- CPOL
8, -- 数据宽度
25600000 -- ,--波特率
)
log.info("main", "open spi", result)
if result ~= 0 then -- 返回值为 0表示打开成功
log.error("main", "spi open error", result)
if other_configs.ethpower_en then
gpio.close(other_configs.ethpower_en)
end
return false
end
-- 初始化以太网Air8000 指定使用 CH390 芯片。
log.info("netdrv", "初始化以太网", other_configs.tp, other_configs.opts)
if netdrv.setup(socket.LWIP_USER1, other_configs.tp, other_configs.opts) == false then
log.error("初始化以太网失败")
if other_configs.ethpower_en then
gpio.close(other_configs.ethpower_en)
end
return false
end
sys.wait(1000) -- 等待以太网模块初始化完成,去掉会导致以太网初始化失败
-- 设置以太网的 IP 地址、子网掩码、网关地址
log.info("netdrv", "自定义以太网IP地址", other_configs.adapter_addr, "网关地址",
other_configs.adapter_gw)
netdrv.ipv4(socket.LWIP_USER1, other_configs.adapter_addr or "192.168.5.1", "255.255.255.0", "0.0.0.0")
-- 获取以太网网络状态,连接后返回 true否则返回 false如果不存在就返回 nil。
local count = 1
while netdrv.ready(socket.LWIP_USER1) ~= true do
if count > 600 then
log.error("以太网连接超时,请检查配置")
if other_configs.ethpower_en then
gpio.close(other_configs.ethpower_en)
end
return false
end
count = count + 1
-- log.info("netdrv", "等待以太网就绪") -- 若以太网设备没有连上,可打开此处注释排查。
sys.wait(100)
end
log.info("netdrv", "以太网就绪")
-- 创建 DHCP 服务器,为连接到以太网的设备分配 IP 地址。
log.info("netdrv", "创建dhcp服务器, 供以太网使用")
if other_configs.adapter_gw then
dhcpsrv.create({
adapter = socket.LWIP_USER1,
gw = other_configs.adapter_gw
})
else
dhcpsrv.create({
adapter = socket.LWIP_USER1,
gw = {192, 168, 5, 1}
})
end
-- 创建 DNS 代理服务,使得以太网接口上的设备可以通过 4G 网络访问互联网。
log.info("netdrv", "创建dns代理服务, 供以太网使用")
end
dnsproxy.setup(adapter, main_adapter)
netdrv.napt(main_adapter)
return true
end
--[[
关闭网卡功能。(内核固件版本号需>=2020)
@api exnetif.close(type,adapter)
@param type boolean 是否为多网融合
@param adapter number 需要关闭的网卡
@return boolean 操作结果
@usage
exnetif.close(true) --关闭多网融合功能
exnetif.close(false,socket.LWIP_ETH) --关闭优先级中的以太网网卡
]]
function exnetif.close(type, adapter)
if type == true then
-- TODO: 目前dhcpsrv扩展库dnsproxy扩展库和napt没有关闭接口
else
if adapter == nil then
log.error("请指定需要关闭的网卡")
return false
end
-- 处理关闭操作以太网关闭spi和供电使能。WLAN部分AP可以关闭STA可以断开连接。4G的话就是进出飞行模式。并设置网卡为关闭状态。
available[adapter] = connection_states.DISCONNECTED
if adapter == socket.LWIP_AP then
wlan.stopAP()
elseif adapter == socket.LWIP_ETH or adapter == socket.LWIP_USER1 then
if netdrv.CTRL_UPDOWN == nil then
log.error("当前固件不支持关闭接口,请使用最新固件测试")
return false
end
if eth_cfg[adapter] and eth_cfg[adapter].pwrpin then
gpio.set(eth_cfg[adapter].pwrpin, 0)
gpio.close(eth_cfg[adapter].pwrpin)
end
-- 关闭spi
spi.close(eth_cfg[adapter].opts.spi)
netdrv.ctrl(adapter, netdrv.CTRL_UPDOWN, 0)
available[adapter] = connection_states.STOPED
elseif adapter == socket.LWIP_STA then
wlan.disconnect()
elseif adapter == socket.LWIP_GP then
mobile.flymode(nil, true)
end
log.info("exnetif", "关闭网卡功能", type_to_string(adapter))
apply_priority()
return true
end
end
return exnetif