跳转至

03 内部硬件看门狗(wdt)

作者:马梦阳 | 最后修改:2026-07-27

一、看门狗电路概述

看门狗(Watch Dog Timer,WDT)是一种用于监控嵌入式系统状态的电路,旨在提高系统的可靠性和稳定性。在看门狗电路的帮助下,当系统出现异常,如程序跑飞或死循环时,能够自动复位并重新启动系统。

1.1 看门狗的工作原理

看门狗电路通常包括一个定时器、一个喂狗信号和一个复位(Reset)输出。在正常的系统运行过程中,主控制器(MCU)会定期向喂狗信号发送一个脉冲,以“喂”看门狗,防止其计时到达预设值。如果 MCU 由于某种原因未能在规定时间内喂狗,看门狗定时器将会计满并输出一个复位信号,将 MCU 复位。

1.2 看门狗的应用架构

在实际应用中,看门狗芯片通常连接到 MCU 的一个 GPIO 端口。MCU 在其正常运行期间会定期向该端口写入一个高电平或低电平信号,以此告诉看门狗它仍然正常工作。如果 MCU 未能按时发出信号,看门狗将触发复位操作。

1.3 看门狗的类型

看门狗可以分为独立看门狗和窗口看门狗。独立看门狗的时间精度要求较低,而窗口看门狗则用于时间精度要求较高的场合。

1.4 软硬件看门狗的区别

1.4.1 内部硬件看门狗(Always-On Watchdog Timer)

  • 定义:AON WDT,位于芯片"Always-On"电源域的硬件看门狗,即使主芯片休眠仍能工作
  • 特点:开机后硬件层面自动开始计数,独立于软件系统运行
  • 控制方式:完全由内核固件自动控制,不需要脚本干预
  • 工作原理:硬件定时器持续计数,喂狗操作复位计数,超时(计数溢出)触发系统复位
  • 可靠性:理论上能复位大部分系统异常,但在极端硬件故障(如 PSRAM 完全死锁)时可能无法正确重启

1.4.2 软件看门狗

  • 定义:基于操作系统任务调度机制的软件层面监控
  • 特点:能够检测任务阻塞和软件死锁,依赖操作系统正常运行
  • 控制方式:通过 wdt 库函数控制
  • 实现原理:基于操作系统定时器机制,FreeRTOS 按 2ms 间隔调用检测函数
  • 特殊说明:在 Air8101 中,软件看门狗检测函数同时也会喂硬件看门狗
  • 定义:基于合宙 AirLink 通讯协议实现主/从机双向互看门狗操作
  • 特点:通过合宙 AirLink 通讯实现,AirLink 通讯协议参考:AirLink 通讯协议
  • 控制方式:通过 exairlinkwdt 扩展库实现喂狗,待喂狗超时时通过配置的 TO_RESET 管脚复位对端
  • 实现原理:exairlinkwdt 扩展库内部通过向对端发送 AirLink 数据实现启动、喂狗等操作
  • 特别说明:需要主/从机支持 AirLink 核心库

1.4.4 外部硬件看门狗

  • 定义:独立的看门狗芯片,通过 GPIO/I2C 等接口通信
  • 特点:完全独立于主系统,具有独立的电源和时钟,可靠性最高
  • 控制方式:不通过 wdt 库,需使用 i2c、gpio 等外设驱动库实现喂狗操作

硬件看门狗利用独立的定时器电路来实现监控功能,具有较高的可靠性。软件看门狗则使用处理器内部的定时器或其他机制来实现,虽然在一定程度上可以简化硬件设计,但在可靠性方面通常不如硬件看门狗。

总结而言,看门狗是一种重要的系统保护机制,通过定期“喂狗”来确保系统的正常运行。当系统出现异常时,看门狗能够及时复位系统,防止死循环和程序跑飞等情况的发生,从而提高系统的可靠性和稳定性。在实际应用中,开发者需要根据具体需求选择合适的看门狗解决方案,并设置适当的时间参数,以实现最佳的保护效果。

二、演示功能概述

本文演示主要通过 Air8101 和 LuatOS 系统,介绍内部硬件看门狗、外部 Air153C 硬件看门狗、AirLink 看门狗三种看门狗在正常与异常情况时的表现。

内部硬件看门狗:

演示分为两个部分:正常运行情况和故障场景模拟。

在正常运行情况下,示例代码会初始化看门狗核心库。然后,创建一个任务,该任务会定期喂狗,以防止看门狗定时器超时。如果看门狗定时器超时,系统将自动重启。

在故障场景模拟部分,我们添加了一个新的任务 fault_task,该任务进入一个死循环,不进行喂狗操作。这样,当看门狗定时器超时后,系统将自动重启,模拟了设备在故障场景下的自动恢复能力。

Air153C 外部硬件看门狗(待补充):

演示分为两个部分:正常运行情况和故障场景模拟。

在正常运行情况下,示例代码内部配置为每 10 秒执行一次喂狗操作;

在故障场景模拟部分,示例代码内部配置为先每 10 秒执行一次喂狗操作,共执行三次后停止喂狗,等达到 Air153C 看门狗芯片等待喂狗超时时间后,自动硬件复位 Air8101。

AirLink 看门狗:

演示分为两个部分:正常运行情况和故障场景模拟。

在正常运行情况下,示例代码内部配置为默认每 150 秒执行一次喂狗操作;

在故障场景模拟部分,通过断开 Air8101 的发送端,当从机未收到 Air8101 发来的喂狗数据一段时间后(等待喂狗超时定时器默认为 240 秒),自动硬件复位 Air8101。

通过这个演示,我们可以看到看门狗定时器在嵌入式系统中的重要作用,它能够有效防止系统死机,提高系统的稳定性和可靠性。

三、准备硬件环境

LzuBbS3NxoVu34x4dj7c3d04nDb

1、Air8101核心板一块

2、Air780ER2、Air780ER3 核心板各一块(板子图片待补充)

  • Air780ER2/ER3 模组专门用于为主控提供 4G 上网能力;
  • 该模组不需要用户进行二次开发,收到后其模组内部已经烧录好底层固件和脚本,上手即用;
  • 模组详细资料请参考:Air780ER2/ER3 简介

3、TYPE-C USB数据线一根

4、Air8101核心板和数据线的硬件接线方式为

  • Air8101核心板通过TYPE-C USB口连接TYPE-C USB 数据线,数据线的另外一端连接电脑的USB口;

5、Air8101 核心板与 Air780ER2 核心板硬件接线方式如下,用于演示 AirLink UART 看门狗

在进行接线测试时需要注意:

Air8101 和 Air780ER2 的 airlink 看门狗代码中,GPIO 空闲电平为 低,喂狗电平为 高。

因此需要中间外加 三极管 进行间接控制,如果直接连接,会导致对端被误复位。

双向互看门狗电路参考:Air1601 开发板设计文件

说明 Air8101 核心板(排针丝印) Air780ER2 核心板(排针丝印)
主机接收,从机发送 11/U1RX 18/U1_TXD
主机发送,从机接收 12/U1TX 17/U1_RXD
共地 GND GND
主机控制复位从机 9/GPIO6 15/RESET_N
从机控制复位主机 23/CEN 16/GPIO27

6、Air8101 板与 Air780ER3 核心板硬件接线方式如下,用于演示 AirLink SPI 看门狗

在进行接线测试时需要注意:

Air8101 和 Air780ER3 的 airlink 看门狗代码中,GPIO 空闲电平为 低,喂狗电平为 高。

因此需要中间外加 三极管 进行间接控制,如果直接连接,会导致对端被误复位。

双向互看门狗电路参考:Air1601 开发板设计文件

说明 Air8101 核心板(排针丝印) Air780ER3 核心板(排针丝印)
SPI_CS 54/DISP 30/SPI0_CS
SPI_CLK 28/DCLK 33/SPI0_CLK
SPI_MOSI 57/DE 32/SPI0_MOSI
SPI_MISO 55/HSYN 31/SPI0_MISO
RDY 43/R2 26/GPIO25
GND GND GND
主机控制复位从机 9/GPIO6 15/RESET_N
从机控制复位主机 23/CEN 16/GPIO27

Air8101 购买链接:Air8101 核心板 WiFi 4G 以太网 蓝牙 720P显示屏 200万拍照

Air780ER2 购买链接:Air780ER2 USB/RNDIS通用协议 合宙AirLink协议搭配Air1601/8101

Air780ER3 购买链接:Air780ER3 USB/RNDIS通用协议 合宙AirLink协议搭配Air1601/8101

四、准备软件环境

在开始实践本示例之前,先筹备一下软件环境:

1.Luatools 工具

2.Air8101 内核固件文件(底层 core 固件文件):

本demo开发测试时使用的固件为LuatOS-SoC_V2018_Air8101_101.soc,本demo对固件版本没有什么特殊要求,所以你如果要测试本demo时,可以直接使用最新版本的内核固件;如果发现最新版本的内核固件测试有问题,可以使用我们开发本demo时使用的内核固件版本来对比测试。

3.Air780ER2/ER3 量产固件文件:

Air780ER2/ER3 不需要用户再进行二次开发,在收到模组后其模组内部已经烧录好内核固件和脚本。

如果需要查看固件更新说明请参考:Air780ER2/ER3 固件版本索引

4.luatos 需要的脚本和资源文件

  • Air8101 脚本和资源文件点击此处查看与下载
  • lib 脚本文件:使用 Luatools 烧录时,勾选 添加默认 lib 选项,使用默认 lib 脚本文件;

5.准备好软件环境之后,接下来查看如何烧录项目文件到 Air8101 核心板中,将本篇文章中演示使用的项目文件烧录到 Air8101核心板 中。

五、wdt 核心库介绍

wdt 核心库提供了一系列用于管理和操作看门狗定时器的 API 函数,使得开发者可以方便地在 LuatOS 系统中管理看门狗定时器,从而提高设备的稳定性。

API 接口参考:wdt 核心库

wdt 核心库 专门用于控制芯片内部的看门狗

  • 内部硬件看门狗:芯片内部集成的硬件看门狗单元,如 AON WDT(Always-On Watchdog Timer)。

  • 软件看门狗:基于操作系统任务调度机制的软件层面看门狗。

六、air153C_wtd 扩展库介绍

air153C_wtd 库是用于控制外部硬件看门狗的 Lua 库,通过 GPIO 引脚控制外部看门狗芯片,旨在提高系统的可靠性和稳定性。在 air153C_wtd 库的帮助下,当系统出现异常时,能够通过外部看门狗芯片自动复位并重新启动系统。

API 接口参考:air153C_wtd 扩展库

air153C_wtd 库专门用于控制芯片外部的看门狗:

  • 外部硬件看门狗:通过 GPIO 引脚控制的外部看门狗芯片
  • 脉冲喂狗机制:通过产生特定宽度的脉冲信号来喂狗

七、exairlinkwdt 扩展库介绍

exairlinkwdt 扩展库基于 AirLink 通讯协议实现,用于实现主机与从机之间互看门狗功能。

库代码中默认空闲电平为 低电平,用于主/从机 GPIO 与 WTDOG 管脚使用 三极管 连接时的双向互看门狗电路,电路设计参考:Air1601 开发板设计文件

如果打算将 GPIO 与 WTDOG 直接连接,可以使用配置接口将 GPIO 空闲电平配置为对应电平即可。

八、功能验证

8.1 内部看门狗示例代码介绍

8.1.1 简要说明

本模块演示了内部看门狗的两种工作场景:正常喂狗模式和异常故障模式。

  • 通过修改 DEMO_MODE 变量,来选择演示模式:

  • "normal": 正常喂狗模式

  • "fault": 异常故障模式

  • 正常场景:定期喂狗,系统正常运行

  • 系统启动后,通过 sys.timerLoopStart 创建一个循环定时器

  • 定时器每 3 秒执行一次 feed_watchdog 函数

  • 由于定期喂狗,看门狗不会超时,系统保持稳定运行

  • 异常场景:模拟故障导致无法喂狗,触发看门狗复位

  • 系统启动后,正常喂狗约 5 秒钟

  • 随后进入模拟故障状态,创建死循环阻塞系统

  • 死循环导致喂狗定时器无法执行

  • 看门狗计数器持续累加,最终超时触发系统复位

8.1.2 完整代码展示

-- internal_wdt.lua
--[[
@summary 内部看门狗演示模块
@version 1.0
@date    2025.10.25
@author  陈媛媛
@usage
本模块演示内部看门狗的正常和异常场景:
1、正常场景:定期喂狗,系统正常运行
2、异常场景:模拟故障导致无法喂狗,触发看门狗复位
3、通过修改 DEMO_MODE 变量来选择演示模式:
- "normal": 正常喂狗模式
- "fault": 异常故障模式

注意:在异常模式下,设备会在运行一段时间后重启
]]

-- 演示模式选择: "normal" 或 "fault"
local DEMO_MODE = "normal"  -- 修改这个变量来切换演示模式

-- 喂狗函数
function feed_watchdog()
    wdt.feed()
    log.info("wdt", "喂狗完成")
end

-- 故障模拟函数
function simulate_fault()
    sys.wait(5000) -- 等待5秒,让系统先正常运行一会

    -- 在进入死循环前尝试喂狗一次,并检查返回值
    local success = wdt.feed()
    log.info("wdt", "故障前最后一次喂狗,成功 =", success)

    log.info("fault_task", "进入死循环模拟故障")
    log.info("fault_task", "看门狗喂狗任务被阻塞,系统将在约20秒后重启")

    while true do
        -- 模拟故障场景,真的进入死循环
        -- 这将导致无法喂狗,最终触发系统重启
    end
end

-- 内部看门狗演示函数
function internal_wdt_demo()
    -- 检查wdt库是否存在
    if wdt == nil then
        log.error("wdt", "wdt库不存在")
        return
    end

    log.info("wdt", "硬件看门狗已由底层固件启用")

    -- 检查开机原因
    local reason1, reason2, reason3 = pm.lastReson()
    log.info("reset_reason", "重启原因1:", reason1, "原因2:", reason2, "原因3:", reason3)

    -- 注意, 大部分芯片/模块是 2 倍超时时间后才会重启
    -- 以下是常规配置, 9秒超时, 3秒喂一次狗
    -- 若软件崩溃,死循环,硬件死机,那么 最多 18 秒后,自动复位
    -- 注意: 软件bug导致业务失败, 并不能通过wdt解决
    wdt.init(9000)

    -- 定期喂狗,防止系统重启
    -- 设置喂狗间隔为3秒,确保在20秒超时前完成喂狗
    sys.timerLoopStart(feed_watchdog, 3000) -- 每3秒喂一次狗
end

if DEMO_MODE == "fault" then
    -- 创建一个新的任务来模拟故障场景
    sys.taskInit(simulate_fault)
end

-- 启动演示
sys.taskInit(internal_wdt_demo)

return internal_wdt_demo

8.1.3 运行结果展示

通过 Luatools 工具查看日志 正常场景

641e75c0-40d7-4e5e-82c7-43b8ea29d9f2

添加死循环模拟故障场景

06db6fbc-e217-49cd-9bc9-c72e45bb68fe

8.2 Air153C 外部硬件看门狗(待补充)

8.3.1 简要说明

AirLink 看门狗使用 AirLink 通讯协议,可以使用 UART 或者 SPI 接口方式两种,由于看门狗表现是一样的,所以放在一起进行介绍。

正常场景:

Air8101 定期向从机执行喂狗操作,默认喂狗周期为 150 秒。

异常场景:

当从机收不到 Air8101 发来的喂狗信号时,等待喂狗超时定时器超时后便会自动硬件复位 Air8101,默认超时时间为 240 秒。

8.3.2 完整代码展示

AirLink UART模式看门狗演示模块:

--[[
@module  airlink_uart_wdt
@summary airlink UART多网融合模块
@version 1.0
@date    2026.07.21
@author  马梦阳
@usage
本demo演示的核心功能为:
1. 初始化4G网络连接。
2. Air8101与对端设备进行数据交互。
3. 自动切换网络连接模式。
4. 通过HTTP GET请求测试网络连接情况。
5. 启动看门狗功能。

使用示例:
require("airlink_uart_wdt")
]]

--============================================================
-- 全局配置参数
--============================================================

-- AirLink UART 配置
local AIRLINK_UART_ID = 1    -- UART ID
local AIRLINK_BAUD = 2000000 -- 波特率:2M

-- 网络配置
local NET_PEER_IP = "192.168.111.2" -- 对端IP

-- 测试配置
local HTTP_URL = "https://httpbin.luatos.com/bytes/2048"
local HTTP_INTERVAL = 10000 -- HTTP请求间隔:10秒
local HTTP_TIMEOUT = 9000   -- HTTP超时:9秒
local SDATA_INTERVAL = 1000 -- 数据发送间隔:1秒

--============================================================
-- 模块加载
--============================================================

local exnetif = require "exnetif"
local exairlinkwdt = require("exairlinkwdt")

--============================================================
-- 看门狗初始化
--============================================================

local function init_airlink_wdt()
    sys.wait(5000)

    -- 初始化看门狗,TO_RESET 使用 GPIO6,默认空闲电平为 低电平
    -- 在设计双向互看门狗时,需要使用 三极管 进行连接
    -- 禁止直连,否则会导致看门狗功能异常,造成对端被误复位
    local success = exairlinkwdt.open({ reset_pin = 6 , reset_idle_level = 0 })
    if success then
        log.info("main", "看门狗启动成功")
    else
        log.error("main", "看门狗启动失败")
    end
end

--============================================================
-- 网络初始化
--============================================================

-- 初始化网络,使得Air8101可以外挂Air780ER2模块实现4G联网功能。
local function init_airlink_net()
    exnetif.set_priority_order({
        {                                           -- 开启4G虚拟网卡
            airlink_4G = {
                auto_socket_switch = false,         -- 切换网卡时是否断开之前网卡的所有socket连接并用新的网卡重新建立连接
                airlink_type = airlink.MODE_UART,   -- airlink工作模式:UART模式
                airlink_uart_id = AIRLINK_UART_ID,  -- airlink使用的UART接口ID
                airlink_uart_baud = AIRLINK_BAUD,   -- airlink使用的UART波特率
                airlink_adapter = socket.LWIP_GP_GW -- Air8101使用socket.LWIP_GP_GW网卡标识
            }
        }
    })
end

--============================================================
-- 数据收发
--============================================================

-- Air8101发送数据信息给Air780ER2模块。
local function airlink_sdata_Air780ER2()
    -- 设置网络时间同步。
    -- socket.sntp()
    while 1 do
        -- rtos.bsp():设备硬件bsp型号;os.date():本地时间。
        local data = rtos.bsp() .. " " .. os.date()
        log.info("发送数据给对端设备", data, "当前airlink状态", airlink.ready())
        airlink.sdata(data)
        sys.wait(SDATA_INTERVAL)
        log.info("ticks", mcu.ticks(), hmeta.chip(), hmeta.model(), hmeta.hwver())
        airlink.statistics()
    end
end

--============================================================
-- 获取并打印 airlink 从机的网络信息
--============================================================

local function log_mobile_sync_info()
    while true do
        sys.wait(15000)
        if mobile then
            log.info("存在 mobile 底层库")
        else
            log.info("不存在 mobile 底层库,跳过获取 airlink 从机端网络信息")
            return
        end
        local imei = "unknown"
        local imsi = "unknown"
        local iccid = "unknown"
        local csq = -1
        local status = -1
        local rsrp = 0
        local rsrq = 0
        local snr = 0
        local rssi = 0
        if mobile and mobile.imei then
            local ok, val = pcall(mobile.imei)
            if ok and val then imei = val end
        end
        if mobile and mobile.imsi then
            local ok, val = pcall(mobile.imsi)
            if ok and val then imsi = val end
        end
        if mobile and mobile.iccid then
            local ok, val = pcall(mobile.iccid)
            if ok and val then iccid = val end
        end
        if mobile and mobile.csq then
            local ok, val = pcall(mobile.csq)
            if ok and val then csq = val end
        end
        log.info("airlink 从机网络信息", "imei", imei, "imsi", imsi, "iccid", iccid, "csq", csq, "airlink_ready", airlink.ready())

        -- 以下网络信息暂时不打印
        -- if mobile and mobile.status then
        --     local ok, val = pcall(mobile.status)
        --     if ok and val then status = val end
        -- end
        -- if mobile and mobile.rsrp then
        --     local ok, val = pcall(mobile.rsrp)
        --     if ok and val then rsrp = val end
        -- end
        -- if mobile and mobile.rsrq then
        --     local ok, val = pcall(mobile.rsrq)
        --     if ok and val then rsrq = val end
        -- end
        -- if mobile and mobile.snr then
        --     local ok, val = pcall(mobile.snr)
        --     if ok and val then snr = val end
        -- end
        -- if mobile and mobile.rssi then
        --     local ok, val = pcall(mobile.rssi)
        --     if ok and val then rssi = val end
        -- end
        -- log.info("mobile-rpc-sync", "imei", imei, "imsi", imsi, "iccid", iccid, "csq", csq, "status", status, "rsrp", rsrp, "rsrq", rsrq, "snr", snr, "rssi", rssi, "airlink_ready", airlink.ready())
    end
end

--============================================================
-- 测试函数
--============================================================

-- Ping测试(调试用)
local function ping_test()
    while true do
        -- 必须指定使用哪个网卡
        netdrv.ping(socket.LWIP_GP_GW, NET_PEER_IP)
        -- local res = sys.waitUntil("PING_RESULT", 3000)
        -- if not res then
        --     log.info("ping超时")
        -- end
        sys.wait(3000)
    end
end

-- Ping响应结果回调
local function ping_res(id, time, dst)
    log.info("ping", id, time, dst) --获取到响应结果,id为网卡id,也就是调用netdrv.ping时填写的网卡的常量值,time为请求到响应的时间,dst为目标IP也就是调用netdrv.ping时填写的ip
end


-- HTTP GET请求测试,用于判断Air8101的网络连接情况。
local function http_get_test()
    while true do
        sys.wait(HTTP_INTERVAL)
        -- 本功能在2025.9.3
        log.info("网卡状态", netdrv.ready(socket.LWIP_GP_GW))
        -- 发起一个HTTP GET请求。
        log.info("发起HTTP GET请求", HTTP_URL)
        local code, headers, body = http.request("GET", HTTP_URL, nil, nil, {
            timeout = HTTP_TIMEOUT,
            adapter = socket.LWIP_GP_GW
        }).wait()

        -- 打印HTTP请求的结果,包括响应码code和响应体长度#body。
        if code == 200 then
            log.info("HTTP请求成功", "响应码", code, "响应体长度", body and #body)
            sys.publish("打印网卡信息", "succeeded")
        else
            log.error("HTTP请求失败", "错误码", code)
            sys.publish("打印网卡信息", "failed")
        end
    end
end

--============================================================
-- 事件订阅
--============================================================

-- 订阅airlink的SDATA事件,打印收到的信息。
local function airlink_sdata(data)
    -- 打印收到的信息。
    log.info("收到AIRLINK_SDATA!!", data)
end

--============================================================
-- 启动入口
--============================================================

-- 订阅PING_RESULT事件
sys.subscribe("PING_RESULT", ping_res)

-- 订阅airlink的SDATA事件,打印收到的信息。
sys.subscribe("AIRLINK_SDATA", airlink_sdata)

-- 开启airlink
sys.taskInit(init_airlink_net)

-- 启动Ping测试
-- sys.taskInit(ping_test)

-- HTTP GET请求测试
sys.taskInit(http_get_test)

-- Air8101发送数据信息给Air780ER2模块。
sys.taskInit(airlink_sdata_Air780ER2)

-- 获取并打印 airlink 从机的网络信息
sys.taskInit(log_mobile_sync_info)

-- 初始化看门狗
sys.taskInit(init_airlink_wdt)

AirLink SPI模式看门狗演示模块:

--[[
@module  airlink_spi_wdt
@summary airlink SPI多网融合模块
@version 1.0
@date    2026.07.21
@author  马梦阳
@usage
本demo演示的核心功能为:
1. 初始化4G网络连接。
2. Air8101与对端设备进行数据交互。
3. 自动切换网络连接模式。
4. 通过HTTP GET请求测试网络连接情况。
5. 启动看门狗功能。

使用示例:
require("airlink_spi_wdt")
]]

--============================================================
-- 全局配置参数
--============================================================

-- AirLink SPI 配置
local AIRLINK_SPI_ID = 0    -- SPI ID
local AIRLINK_SPI_CS_PIN = 15    -- CS引脚gpio号
local AIRLINK_SPI_RDY_PIN = 48    -- RDY引脚gpio号
local AIRLINK_SPI_SPEED = 8 * 1000000 -- SPI速度:8M

-- 网络配置
local NET_PEER_IP = "192.168.111.2" -- 对端IP

-- 测试配置
local HTTP_URL = "https://httpbin.luatos.com/bytes/2048"
local HTTP_INTERVAL = 10000 -- HTTP请求间隔:10秒
local HTTP_TIMEOUT = 9000   -- HTTP超时:9秒
local SDATA_INTERVAL = 1000 -- 数据发送间隔:1秒

--============================================================
-- 模块加载
--============================================================

local exnetif = require "exnetif"
local exairlinkwdt = require("exairlinkwdt")

--============================================================
-- 看门狗初始化
--============================================================

local function init_airlink_wdt()
    sys.wait(5000)

    -- 初始化看门狗,TO_RESET 使用 GPIO6,默认空闲电平为 低电平
    -- 在设计双向互看门狗时,需要使用 三极管 进行连接
    -- 禁止直连,否则会导致看门狗功能异常,造成对端被误复位
    local success = exairlinkwdt.open({ reset_pin = 6 , reset_idle_level = 0 })
    if success then
        log.info("main", "看门狗启动成功")
    else
        log.error("main", "看门狗启动失败")
    end
end

--============================================================
-- 网络初始化
--============================================================

-- 初始化网络,使得Air8101可以外挂Air780ER3模块实现4G联网功能。
local function init_airlink_net()
    exnetif.set_priority_order({
        {                                               -- 开启4G虚拟网卡
            airlink_4G = {
                auto_socket_switch = false,             -- 切换网卡时是否断开之前网卡的所有socket连接并用新的网卡重新建立连接
                airlink_type = airlink.MODE_SPI_MASTER, -- airlink工作模式
                airlink_spi_id = AIRLINK_SPI_ID,        -- airlink使用的SPI接口ID,选填参数
                airlink_cs_pin = AIRLINK_SPI_CS_PIN,    -- airlink使用的片选引脚gpio号,选填参数
                airlink_rdy_pin = AIRLINK_SPI_RDY_PIN,  -- airlink使用的rdy引脚gpio号,选填参数
                airlink_spi_speed = AIRLINK_SPI_SPEED,  -- airlink使用的SPI速度,选填参数
            }
        }
    })
end

--============================================================
-- 数据收发
--============================================================

-- Air8101发送数据信息给Air780ER3模块。
local function airlink_sdata_Air780ER3()
    -- 设置网络时间同步。
    -- socket.sntp()
    while 1 do
        -- rtos.bsp():设备硬件bsp型号;os.date():本地时间。
        local data = rtos.bsp() .. " " .. os.date()
        log.info("发送数据给对端设备", data, "当前airlink状态", airlink.ready())
        airlink.sdata(data)
        sys.wait(SDATA_INTERVAL)
        log.info("ticks", mcu.ticks(), hmeta.chip(), hmeta.model(), hmeta.hwver())
        airlink.statistics()
    end
end

--============================================================
-- 获取并打印 airlink 从机的网络信息
--============================================================

local function log_mobile_sync_info()
    while true do
        sys.wait(15000)
        if mobile then
            log.info("存在 mobile 底层库")
        else
            log.info("不存在 mobile 底层库,跳过获取 airlink 从机端网络信息")
            return
        end
        local imei = "unknown"
        local imsi = "unknown"
        local iccid = "unknown"
        local csq = -1
        local status = -1
        local rsrp = 0
        local rsrq = 0
        local snr = 0
        local rssi = 0
        if mobile and mobile.imei then
            local ok, val = pcall(mobile.imei)
            if ok and val then imei = val end
        end
        if mobile and mobile.imsi then
            local ok, val = pcall(mobile.imsi)
            if ok and val then imsi = val end
        end
        if mobile and mobile.iccid then
            local ok, val = pcall(mobile.iccid)
            if ok and val then iccid = val end
        end
        if mobile and mobile.csq then
            local ok, val = pcall(mobile.csq)
            if ok and val then csq = val end
        end
        log.info("airlink 从机网络信息", "imei", imei, "imsi", imsi, "iccid", iccid, "csq", csq, "airlink_ready", airlink.ready())

        -- 以下网络信息暂时不打印
        -- if mobile and mobile.status then
        --     local ok, val = pcall(mobile.status)
        --     if ok and val then status = val end
        -- end
        -- if mobile and mobile.rsrp then
        --     local ok, val = pcall(mobile.rsrp)
        --     if ok and val then rsrp = val end
        -- end
        -- if mobile and mobile.rsrq then
        --     local ok, val = pcall(mobile.rsrq)
        --     if ok and val then rsrq = val end
        -- end
        -- if mobile and mobile.snr then
        --     local ok, val = pcall(mobile.snr)
        --     if ok and val then snr = val end
        -- end
        -- if mobile and mobile.rssi then
        --     local ok, val = pcall(mobile.rssi)
        --     if ok and val then rssi = val end
        -- end
        -- log.info("mobile-rpc-sync", "imei", imei, "imsi", imsi, "iccid", iccid, "csq", csq, "status", status, "rsrp", rsrp, "rsrq", rsrq, "snr", snr, "rssi", rssi, "airlink_ready", airlink.ready())
    end
end

--============================================================
-- 测试函数
--============================================================

-- Ping测试(调试用)
local function ping_test()
    while true do
        -- 必须指定使用哪个网卡
        netdrv.ping(socket.LWIP_GP_GW, NET_PEER_IP)
        -- local res = sys.waitUntil("PING_RESULT", 3000)
        -- if not res then
        --     log.info("ping超时")
        -- end
        sys.wait(3000)
    end
end

-- Ping响应结果回调
local function ping_res(id, time, dst)
    log.info("ping", id, time, dst) --获取到响应结果,id为网卡id,也就是调用netdrv.ping时填写的网卡的常量值,time为请求到响应的时间,dst为目标IP也就是调用netdrv.ping时填写的ip
end


-- HTTP GET请求测试,用于判断Air8101的网络连接情况。
local function http_get_test()
    while true do
        sys.wait(HTTP_INTERVAL)
        -- 本功能在2025.9.3
        log.info("网卡状态", netdrv.ready(socket.LWIP_GP_GW))
        -- 发起一个HTTP GET请求。
        log.info("发起HTTP GET请求", HTTP_URL)
        local code, headers, body = http.request("GET", HTTP_URL, nil, nil, {
            timeout = HTTP_TIMEOUT,
            adapter = socket.LWIP_GP_GW
        }).wait()

        -- 打印HTTP请求的结果,包括响应码code和响应体长度#body。
        if code == 200 then
            log.info("HTTP请求成功", "响应码", code, "响应体长度", body and #body)
            sys.publish("打印网卡信息", "succeeded")
        else
            log.error("HTTP请求失败", "错误码", code)
            sys.publish("打印网卡信息", "failed")
        end
    end
end

--============================================================
-- 事件订阅
--============================================================

-- 订阅airlink的SDATA事件,打印收到的信息。
local function airlink_sdata(data)
    -- 打印收到的信息。
    log.info("收到AIRLINK_SDATA!!", data)
end

--============================================================
-- 启动入口
--============================================================

-- 订阅PING_RESULT事件
sys.subscribe("PING_RESULT", ping_res)

-- 订阅airlink的SDATA事件,打印收到的信息。
sys.subscribe("AIRLINK_SDATA", airlink_sdata)

-- 开启airlink
sys.taskInit(init_airlink_net)

-- 启动Ping测试
-- sys.taskInit(ping_test)

-- HTTP GET请求测试
sys.taskInit(http_get_test)

-- Air8101发送数据信息给Air780ER3模块。
sys.taskInit(airlink_sdata_Air780ER3)

-- 获取并打印 airlink 从机的网络信息
sys.taskInit(log_mobile_sync_info)

-- 初始化看门狗
sys.taskInit(init_airlink_wdt)

8.1.3 运行结果展示

开启喂狗,并进行第一次喂狗操作(默认每 150 秒喂狗一次)

[2026-07-23 01:25:51.970][CPU1][LTOS/N][000000005.864]:I/user.exairlinkwdt 命令发送成功: WDT:OPEN
[2026-07-23 01:25:51.975][CPU1][LTOS/N][000000005.865]:I/user.exairlinkwdt 命令发送成功: WDT:FEED
[2026-07-23 01:25:52.001][CPU1][LTOS/N][000000005.866]:I/user.exairlinkwdt 喂狗定时器启动,周期: 150000 ms
[2026-07-23 01:25:52.005][CPU1][LTOS/N][000000005.866]:I/user.exairlinkwdt 看门狗初始化成功,管脚: 6 常态电平: 0
[2026-07-23 01:25:52.008][CPU1][LTOS/N][000000005.866]:I/user.main 看门狗启动成功

从机收到开启命令后开启/重置等待喂狗超时定时器

[2026-07-23 01:25:51.986][000000008.197] I/user.exairlinkwdt TO_RESET 管脚配置成功,管脚: 27 常态电平: 0
[2026-07-23 01:25:51.988][000000008.198] I/user.exairlinkwdt 等待喂狗超时定时器启动,超时时长: 240000 ms
[2026-07-23 01:25:51.993][000000008.199] I/user.收到AIRLINK_SDATA!! WDT:OPEN
[2026-07-23 01:25:51.995][000000008.200] I/user.exairlinkwdt 收到喂狗命令,重置等待喂狗超时定时器
[2026-07-23 01:25:52.000][000000008.200] I/user.exairlinkwdt 等待喂狗超时定时器已停止
[2026-07-23 01:25:52.011][000000008.201] I/user.exairlinkwdt 等待喂狗超时定时器启动,超时时长: 240000 ms
[2026-07-23 01:25:52.024][000000008.201] I/user.收到AIRLINK_SDATA!! WDT:FEED
[2026-07-23 01:25:52.028][000000008.205] I/user.收到AIRLINK_SDATA!! Air8101 Thu Jan  1 08:00:05 1970

从机的等待喂狗超时定时器超时时间达到后,会复位 8101

[2026-07-23 01:29:51.978][000000248.196] W/user.exairlinkwdt 等待喂狗超时,触发硬件复位
[2026-07-23 01:29:51.985][000000248.197] W/user.exairlinkwdt TO_RESET 管脚输出复位电平: 1
[2026-07-23 01:29:52.087][000000248.297] I/user.exairlinkwdt TO_RESET 管脚恢复常态电平: 0

8101 被从机硬件复位

[2026-07-23 01:29:51.421][CPU1][LTOS/N][000000245.312]:I/user.ticks 245196 BK7258 Air8101 A10
[2026-07-23 01:29:51.424][CPU1][LTOS/N][000000245.312]:统计信息 收发总包 278 278 0 0
[2026-07-23 01:29:51.428][CPU1][LTOS/N][000000245.312]:统计信息 发送IP包 104 104 0 0
[2026-07-23 01:29:51.431][CPU1][LTOS/N][000000245.312]:统计信息 发送IP字节 7262 7262 0 0
[2026-07-23 01:29:51.434][CPU1][LTOS/N][000000245.312]:统计信息 接收IP包 31 31 0 0
[2026-07-23 01:29:51.438][CPU1][LTOS/N][000000245.312]:统计信息 接收IP字节 16350 16350 0 0
[2026-07-23 01:29:51.443][CPU1][LTOS/N][000000245.312]:统计信息 等待从机 0
[2026-07-23 01:29:51.447][CPU1][LTOS/N][000000245.312]:统计信息 Task超时事件 0
[2026-07-23 01:29:51.451][CPU1][LTOS/N][000000245.312]:统计信息 Task新数据事件 352
[2026-07-23 01:29:51.454][CPU1][LTOS/N][000000245.313]:I/user.发送数据给对端设备 Air8101 Thu Jan  1 08:04:05 1970 当前airlink状态 true
[2026-07-23 01:29:51.963][CPU2][LTOS/N][000000245.855]:dns all done ,now stop
[2026-07-23 01:29:51.967][CPU2][LTOS/N][000000245.855]:adatper 15 dns server 0.0.0.0
[2026-07-23 01:29:52.134][CPU0][CAPP/N][000000000.000]:ENV start address is 0x007FA000, size is 8192 bytes.
[2026-07-23 01:29:52.138][CPU0][CAPP/N][000000000.000]:EasyFlash V4.1.0 is initialize success.
[2026-07-23 01:29:52.141][CPU0][CAPP/N][000000000.002]:driver_init: psram init ok
[2026-07-23 01:29:52.144][CPU0][CAPP/N][000000000.002]:uncali sdmadc value:[0 0]
[2026-07-23 01:29:52.147][CPU0][LTOS/N][000000000.004]:rwnx_cal_set_rfconfig(0x102) phy on; rf off
[2026-07-23 01:29:52.152][CPU0][CAPP/N][000000000.010]:temp in otp is:582
[2026-07-23 01:29:52.243][CPU0][CAPP/N][000000000.108]:load polar tab magic code error 0xffffffff
[2026-07-23 01:29:52.247][CPU0][LTOS/N][000000000.108]:xtal_cali:76
[2026-07-23 01:29:52.251][CPU0][CAPP/N][000000000.108]:idx:40=40+(0),r:54,xtal:76,pwr_gain:a8ab7128
[2026-07-23 01:29:52.253][CPU0][CAPP/N][000000000.110]:wifi inited(1) ret(0)
[2026-07-23 01:29:52.257][CPU0][CAPP/N][000000000.111]:sync_base_mac_record, saved records is more than 1, free index(3).
[2026-07-23 01:29:52.604][CPU2][CAPP/N][000000000.472]:ENV start address is 0x007FC000, size is 8192 bytes.
[2026-07-23 01:29:52.696][CPU1][CAPP/N][000000000.564]:EasyFlash V4.1.0 is initialize success.
[2026-07-23 01:29:52.701][CPU2][CAPP/N][000000000.567]:wdrv_tx_cmd_buffer_init, addr[0]=0x28012e14, pattern_addr=0x28012e10
[2026-07-23 01:29:52.704][CPU2][CAPP/N][000000000.567]:wdrv_tx_cmd_buffer_init, addr[1]=0x28013134, pattern_addr=0x28013130
[2026-07-23 01:29:52.707][CPU2][CAPP/N][000000000.568]:wdrv_tx_cmd_buffer_init, addr[2]=0x28013454, pattern_addr=0x28013450
[2026-07-23 01:29:52.710][CPU0][CAPP/N][000000000.569]:cif_handle_bk_cmd_lwipmem_addr_req,665,addr:0x2806c400
[2026-07-23 01:29:52.728][CPU1][LTOS/N][000000000.595]:reset native reason: 0
[2026-07-23 01:29:52.731][CPU1][LTOS/N][000000000.596]:boot up by power on
[2026-07-23 01:29:52.734][CPU1][LTOS/N][000000000.596]:poweron reason 0
[2026-07-23 01:29:52.763][CPU2][LTOS/N][000000000.597]:STA MAC: C8C2C68CD586
[2026-07-23 01:29:52.766][CPU2][LTOS/N][000000000.598]:AP  MAC: C8C2C68CD587
[2026-07-23 01:29:52.770][CPU1][CAPP/N][000000000.598]:sync_base_mac_record, saved records is more than 1, free index(3).
[2026-07-23 01:29:52.773][CPU1][LTOS/N][000000000.599]:BLE MAC: C8C2C68CD588
[2026-07-23 01:29:52.778][CPU1][LTOS/N][000000000.599]:UID: 54540D2043
[2026-07-23 01:29:52.783][CPU2][LTOS/N][000000000.601]:LuatOS@Air8101 base 26.04 bsp V2018 64bit
[2026-07-23 01:29:52.786][CPU2][LTOS/N][000000000.601]:ROM Build: Jul  7 2026 14:14:30
[2026-07-23 01:29:52.900][CPU1][LTOS/N][000000000.759]:/luadb/pins_air8101.json not exist!!
[2026-07-23 01:29:52.907][CPU1][LTOS/N][000000000.762]:loadlibs luavm 2097144 19136 19216
[2026-07-23 01:29:52.911][CPU1][LTOS/N][000000000.762]:loadlibs sys   224464 29736 29736
[2026-07-23 01:29:52.916][CPU1][LTOS/N][000000000.762]:loadlibs psram 6291456 133904 151792
[2026-07-23 01:29:52.919][CPU1][LTOS/N][000000000.778]:I/user.main wdt_DEMO 001.999.000
[2026-07-23 01:29:52.946][CPU1][LTOS/N][000000000.805]:D/user.dnsproxy version -> 202607100900
[2026-07-23 01:29:52.950][CPU1][LTOS/N][000000000.823]:D/user.udpsrv version -> 202607021200
[2026-07-23 01:29:52.953][CPU1][LTOS/N][000000000.826]:D/user.dhcpsrv version -> 202607021200
[2026-07-23 01:29:52.978][CPU1][LTOS/N][000000000.836]:D/user.httpdns version -> 202607021200
[2026-07-23 01:29:52.981][CPU1][LTOS/N][000000000.840]:D/user.exnetif version -> 202607161200
[2026-07-23 01:29:52.984][CPU1][LTOS/N][000000000.852]:D/user.exairlinkwdt version -> 202607190000

九、总结

本次演示通过 Air8101 核心板和 LuatOS 系统,详细介绍了如何使用内部看门狗、外部硬件看门狗、AirLink 看门狗来防止嵌入式设备死机。

通过本次演示,我们深入理解了看门狗定时器在嵌入式系统中的工作原理和应用价值,以及在实际项目中如何有效地利用看门狗库来提高系统的稳定性和可靠性。这对于嵌入式系统开发者来说,是一个具有重要意义的技能。

搜索
AirMaster 实时解答