跳到主要内容

以太网通信

概述

YFAir8000 通过外挂 CH390H 以太网芯片实现百兆网口。本例程演示 CH390H 的 SPI 驱动初始化、DHCP 获取 IP,以及通过 MQTT 验证网络通信。

技术栈
  • 运行环境:LuatOS(合宙 Air8000W 模组)
  • 以太网芯片:CH390H(SPI1, CS=GPIO12, IRQ=GPIO21)
  • 验证方式:MQTT 连接阿里云 IoT 平台

程序流程

Main()

├─ Task1: 以太网初始化
│ ├─ gpio.setup(153, 0) → 初始化
│ ├─ sys.wait(6000) → 等待硬件就绪
│ ├─ spi.setup(1, ...) → SPI1 25600000
│ ├─ netdrv.setup(LWIP_ETH, CH390) → 挂载 CH390H
│ └─ netdrv.dhcp(LWIP_ETH, true) → DHCP 获取 IP

├─ Task2: MQTT 连接
│ ├─ sys.waitUntil("IP_READY") → 等待 IP 就绪
│ ├─ mqtt.create(LWIP_ETH, ...) → 绑定以太网
│ └─ 断线恢复(4阶段) → 重连→网络检查→DHCP→最终尝试

└─ Task3: 定时上报(每5秒)

项目结构

以太网上传阿里云/
└── main.lua # 以太网 + MQTT 上阿里云(单文件)

核心代码详解

1. CH390H SPI 初始化

-- SPI1 配置:模式0, 8bit, 25.6MHz
local result = spi.setup(1, nil, 0, 0, 8, 25600000)
if result ~= 0 then
log.info("main", "网口SPI打开错误", result)
return
end

-- 挂载 CH390H 以太网控制器
netdrv.setup(socket.LWIP_ETH, netdrv.CH390, {
spi = 1, -- SPI1
cs = 12, -- 片选 GPIO12
irq = 21 -- 中断 GPIO21
})

-- 启动 DHCP
netdrv.dhcp(socket.LWIP_ETH, true)

2. 等待网络就绪

sys.waitUntil("IP_READY")
log.info("以太网 IP:", netdrv.ipv4(socket.LWIP_ETH))

3. MQTT 绑定以太网适配器

mqttc = mqtt.create(socket.LWIP_ETH, mqtt_host, mqtt_port, false)
mqttc:auth(client_id, user_name, password)
mqttc:keepalive(120)
mqttc:autoreconn(true, 10000)
mqttc:connect()

4. 断线四阶段恢复

-- disconnect 事件处理
-- 阶段1: 直接重连
if mqtt_client:connect() then return end

-- 阶段2: 网络状态检查(3次 × 5秒)
while retry_count < 3 do
local ip = netdrv.ipv4(socket.LWIP_ETH)
if ip and ip ~= "0.0.0.0" then
mqtt_client:connect()
end
retry_count = retry_count + 1
sys.wait(5000)
end

-- 阶段3: 重启DHCP(最多30秒)
netdrv.dhcp(socket.LWIP_ETH, true)
sys.waitUntil("IP_READY", 30000)

-- 阶段4: 最终尝试
mqtt_client:connect()

全部代码

main.lua
PROJECT = "CH390H"
VERSION = "1.0.0"

sys = require("sys")
sysplus = require("sysplus")

local ProductKey = "YOUR_PRODUCT_KEY"
local DeviceName = "YOUR_DEVICE_NAME"
local mqtt_host = "YOUR_MQTT_HOST"
local mqtt_port = 1883
local client_id = ProductKey .. "." .. DeviceName .. "|securemode=2,signmethod=hmacsha256,timestamp=xxx|"
local user_name = DeviceName .. "&" .. ProductKey
local password = "YOUR_HMAC_SHA256_PASSWORD"

local pub_topic = "/sys/" .. ProductKey .. "/" .. DeviceName .. "/thing/event/property/post"
local sub_topic = "/sys/" .. ProductKey .. "/" .. DeviceName .. "/thing/event/property/post_reply"
local mqttc = nil

-- ============================================================
-- Task1: 以太网 CH390H 初始化
-- ============================================================
sys.taskInit(function()
gpio.setup(153, 0, gpio.PULLUP)
sys.wait(6000)

local result = spi.setup(1, nil, 0, 0, 8, 25600000)
log.info("main", "SPI open", result)
if result ~= 0 then
log.info("main", "网口SPI打开错误", result)
return
end

netdrv.setup(socket.LWIP_ETH, netdrv.CH390, {
spi = 1, cs = 12, irq = 21
})
netdrv.dhcp(socket.LWIP_ETH, true)
end)

-- ============================================================
-- Task2: MQTT 连接与事件处理
-- ============================================================
sys.taskInit(function()
sys.waitUntil("IP_READY")
sys.wait(6000)

log.info("MQTT", "开始连接MQTT服务器:" .. mqtt_host .. ":" .. mqtt_port)
mqttc = mqtt.create(socket.LWIP_ETH, mqtt_host, mqtt_port, false)
mqttc:auth(client_id, user_name, password)
mqttc:keepalive(120)
mqttc:autoreconn(true, 10000)

mqttc:on(function(mqtt_client, event, data, payload)
if event == "conack" then
log.info("服务器鉴权完成,mqtt连接已经建立")
sys.publish("mqtt_conack")
mqtt_client:subscribe({ [sub_topic] = 1 })
elseif event == "recv" then
if data == sub_topic then
local payload = json.decode(payload)
if payload.code == 200 then
log.info("MQTT", "云端确认数据发送成功")
else
log.warn("MQTT", "云端处理失败", payload.message)
end
end
elseif event == "disconnect" then
log.warn("MQTT", "断开连接,开始检测网络恢复...")

-- 阶段1: 直接重连
if mqtt_client:connect() then
log.info("MQTT", "直接重连成功")
return
end

-- 阶段2: 检查网络状态(3次,每次5秒)
local retry_count = 0
while retry_count < 3 do
local ip, mask, gw = netdrv.ipv4(socket.LWIP_ETH)
if ip and ip ~= "0.0.0.0" then
log.info("MQTT", "网络已恢复,重新连接MQTT...")
if mqtt_client:connect() then return end
end
retry_count = retry_count + 1
sys.wait(5000)
end

-- 阶段3: 重启DHCP(最多30秒)
log.error("MQTT", "网络未恢复,尝试重启DHCP...")
netdrv.dhcp(socket.LWIP_ETH, true)
local success = sys.waitUntil("IP_READY", 30000)
if not success then
log.error("MQTT", "DHCP 超时,放弃恢复")
return
end

-- 阶段4: 最终尝试
if mqtt_client:connect() then
log.info("MQTT", "网络恢复后MQTT连接成功")
else
log.error("MQTT", "最终MQTT连接失败")
end

elseif event == "puback" then
log.info("MQTT", "消息确认成功,包ID:" .. data)
end
end)

mqttc:connect()
sys.waitUntil("mqtt_conack")
end)

-- 生成上报数据
local function generateRandomData()
local CurrentTemperature = math.random(15, 35) + math.random(1, 9) / 10
local CurrentHumidity = math.random(30, 70) + math.random(1, 9) / 10
log.info("MQTT", "发布数据",
"温度:" .. string.format("%.1f", CurrentTemperature) .. "℃,湿度:" ..
string.format("%.1f", CurrentHumidity) .. "%RH")
return {
id = "123", version = "1.0",
method = "thing.event.property.post",
params = {
CurrentTemperature = CurrentTemperature,
CurrentHumidity = CurrentHumidity
}
}
end

-- ============================================================
-- Task3: 定时数据上报
-- ============================================================
sys.taskInit(function()
sys.waitUntil("mqtt_conack")
sys.wait(3000)
while true do
sys.wait(5000)
if mqttc and mqttc:ready() then
local payload = generateRandomData()
mqttc:publish(pub_topic, json.encode(payload), 1)
else
log.warn("MQTT", "当前MQTT连接不可用,跳过本次发送")
end
end
end)

sys.run()

关键技术点

技术点API说明
SPI 初始化spi.setup(1, nil, 0, 0, 8, 25600000)SPI1, 模式0, 25.6MHz
CH390H 挂载netdrv.setup(LWIP_ETH, netdrv.CH390, {...})片选 GPIO12, 中断 GPIO21
DHCPnetdrv.dhcp(LWIP_ETH, true)自动获取 IP
查询 IPnetdrv.ipv4(socket.LWIP_ETH)获取 IPv4 地址/掩码/网关
等待 IPsys.waitUntil("IP_READY")DHCP 成功获取 IP
MQTT 绑定mqtt.create(socket.LWIP_ETH, ...)MQTT 走以太网通道
断线恢复四阶段:重连→网络检查→DHCP→最终尝试逐级降级恢复策略