2 Commity 7add150e6a ... 418616a577

Autor SHA1 Wiadomość Data
  zouping 418616a577 Merge branch 'master' of http://192.168.0.100:9001/P_9102/9102_LUA 1 miesiąc temu
  zouping 7ab994a609 调试宝3.0 LUA 1 miesiąc temu
85 zmienionych plików z 11650 dodań i 0 usunięć
  1. 89 0
      hw_drv/lcd_drv.lua
  2. BIN
      images/1.png
  3. BIN
      images/232.png
  4. BIN
      images/4851.png
  5. BIN
      images/4g00.png
  6. BIN
      images/4g1.png
  7. BIN
      images/4g2.png
  8. BIN
      images/4g3.png
  9. BIN
      images/4g4.png
  10. BIN
      images/LoRa.png
  11. BIN
      images/MQTT.png
  12. BIN
      images/back1.png
  13. BIN
      images/bjrz.png
  14. BIN
      images/bjrz1.png
  15. BIN
      images/bjxx2.png
  16. BIN
      images/bmq.png
  17. BIN
      images/bsk2.png
  18. BIN
      images/dian1.png
  19. BIN
      images/dian2.png
  20. BIN
      images/dian3.png
  21. BIN
      images/dian4.png
  22. BIN
      images/gjb.png
  23. BIN
      images/hardware.png
  24. BIN
      images/help.png
  25. BIN
      images/home2.png
  26. BIN
      images/light.png
  27. BIN
      images/person3.png
  28. BIN
      images/rzjc.png
  29. BIN
      images/sbq.png
  30. BIN
      images/shutdown.png
  31. BIN
      images/sj.png
  32. BIN
      images/test1.png
  33. BIN
      images/test2.png
  34. BIN
      images/tq.png
  35. BIN
      images/up.png
  36. BIN
      images/up1.png
  37. BIN
      images/upgrade.png
  38. BIN
      images/wbjw.png
  39. BIN
      images/wifi.png
  40. BIN
      images/wifi0.png
  41. BIN
      images/wifi1.png
  42. BIN
      images/wifi2.png
  43. BIN
      images/wifi3.png
  44. BIN
      images/wifi4.png
  45. BIN
      images/wlan3.png
  46. BIN
      images/xd4.png
  47. BIN
      images/ywy.png
  48. 70 0
      main.lua
  49. 289 0
      net_manager.lua
  50. 187 0
      power_init.lua
  51. 40 0
      pwrkey.lua
  52. 75 0
      tp_key_drv/tp_drv.lua
  53. 178 0
      tsb_ui/tsb_232log_page.lua
  54. 178 0
      tsb_ui/tsb_485log_page.lua
  55. 384 0
      tsb_ui/tsb_bmq_page.lua
  56. 563 0
      tsb_ui/tsb_bsk_page.lua
  57. 285 0
      tsb_ui/tsb_channel_page.lua
  58. 377 0
      tsb_ui/tsb_common_page.lua
  59. 214 0
      tsb_ui/tsb_devinfo_page.lua
  60. 143 0
      tsb_ui/tsb_devlog_page.lua
  61. 335 0
      tsb_ui/tsb_firm_page.lua
  62. 511 0
      tsb_ui/tsb_game_page.lua
  63. 93 0
      tsb_ui/tsb_help_page.lua
  64. 308 0
      tsb_ui/tsb_home_page.lua
  65. 148 0
      tsb_ui/tsb_light_page.lua
  66. 143 0
      tsb_ui/tsb_log_page.lua
  67. 243 0
      tsb_ui/tsb_login_page.lua
  68. 291 0
      tsb_ui/tsb_lora_page.lua
  69. 444 0
      tsb_ui/tsb_mqtt_page.lua
  70. 235 0
      tsb_ui/tsb_reflash_page.lua
  71. 565 0
      tsb_ui/tsb_test_page.lua
  72. 271 0
      tsb_ui/tsb_tq_page.lua
  73. 186 0
      tsb_ui/tsb_ui_main.lua
  74. 94 0
      tsb_ui/tsb_update_page.lua
  75. 431 0
      tsb_ui/tsb_upgrade_page.lua
  76. 143 0
      tsb_ui/tsb_waveform_page.lua
  77. 1802 0
      tsb_ui/tsb_wavein_page.lua
  78. 383 0
      tsb_ui/tsb_waveout_page.lua
  79. 464 0
      tsb_ui/tsb_wlan_page.lua
  80. 340 0
      tsb_ui/tsb_ywy_1_page.lua
  81. 163 0
      tsb_ui/tsb_ywy_2_page.lua
  82. 146 0
      tsb_ui/tsb_ywy_page.lua
  83. 416 0
      uart1_msg.lua
  84. 213 0
      uart485_test.lua
  85. 210 0
      wifi_sta.lua

+ 89 - 0
hw_drv/lcd_drv.lua

@@ -0,0 +1,89 @@
+--[[
+@module  lcd_drv
+@summary LCD显示驱动模块,基于lcd核心库
+@version 1.0
+@date    2026.02.05
+@author  江访
+@usage
+本模块为LCD显示驱动功能模块,主要功能包括:
+1、初始化 LCD屏幕;
+2、配置LCD显示参数和显示缓冲区;
+3、初始化AirUI;
+4、支持多种屏幕方向和分辨率设置;
+
+对外接口:
+1、lcd_drv.init():初始化LCD显示驱动
+]]
+
+
+local lcd_drv = {}
+
+--[[
+初始化LCD显示驱动;
+
+@api lcd_drv.init()
+@summary 配置并初始化LCD屏幕
+@return boolean 初始化成功返回true,失败返回false
+
+@usage
+-- 初始化LCD显示
+local result = lcd_drv.init()
+if result then
+    log.info("LCD初始化成功")
+else
+    log.error("LCD初始化失败")
+end
+]]
+
+function lcd_drv.init()
+    -- 开启屏幕供电
+    -- gpio.setup(141, 1)
+    local result = lcd.init("st7796",
+        {
+            pin_pwr = 2,       -- 背光控制引脚GPIO端口号
+            port = lcd.HWID_0, -- 驱动端口
+            pin_rst = 36,      -- lcd复位引脚
+            -- direction = 0,     -- lcd屏幕方向 0:0° 1:90° 2:180° 3:270°,屏幕方向和分辨率保存一致
+            -- w = 320,           -- lcd 水平分辨率
+            -- h = 480,           -- lcd 竖直分辨率
+            direction = 3,     -- lcd屏幕方向 0:0° 1:90° 2:180° 3:270°,屏幕方向和分辨率保存一致
+            w = 480,           -- lcd 水平分辨率
+            h = 320,           -- lcd 竖直分辨率
+            xoffset = 0,       -- x偏移(不同屏幕ic 不同屏幕方向会有差异)
+            yoffset = 0,       -- y偏移(不同屏幕ic 不同屏幕方向会有差异)
+        })
+
+    log.info("lcd.init", result)
+
+    if result then
+        -- 开启缓冲区, 刷屏速度会加快, 但也消耗2倍屏幕分辨率的内存
+        -- lcd.setupBuff(nil, true)
+        -- lcd.autoFlush(false)
+
+        -- 初始化AirUI
+        local width, height = lcd.getSize()
+        local result = airui.init(width, height)
+        if not result then
+            log.error("airui", "init failed")
+            return result
+        end
+
+        -- 加载字体
+        airui.font_load({
+            type = "hzfont",  -- 字体类型,hzfont
+            path = nil,       -- 字体路径,Air8000固件内置,无需填写
+            size = 14,        -- 默认字体大写
+            cache_size = 512, --
+            antialias = 4,    -- 字体抗锯齿等级,1-4级,级别越高抗锯齿效果越好,加载时间越长
+        })
+
+        -- 开启背光引脚供电
+        -- gpio.setup(1, 1)
+
+        return result
+    end
+
+    return result
+end
+
+return lcd_drv

BIN
images/1.png


BIN
images/232.png


BIN
images/4851.png


BIN
images/4g00.png


BIN
images/4g1.png


BIN
images/4g2.png


BIN
images/4g3.png


BIN
images/4g4.png


BIN
images/LoRa.png


BIN
images/MQTT.png


BIN
images/back1.png


BIN
images/bjrz.png


BIN
images/bjrz1.png


BIN
images/bjxx2.png


BIN
images/bmq.png


BIN
images/bsk2.png


BIN
images/dian1.png


BIN
images/dian2.png


BIN
images/dian3.png


BIN
images/dian4.png


BIN
images/gjb.png


BIN
images/hardware.png


BIN
images/help.png


BIN
images/home2.png


BIN
images/light.png


BIN
images/person3.png


BIN
images/rzjc.png


BIN
images/sbq.png


BIN
images/shutdown.png


BIN
images/sj.png


BIN
images/test1.png


BIN
images/test2.png


BIN
images/tq.png


BIN
images/up.png


BIN
images/up1.png


BIN
images/upgrade.png


BIN
images/wbjw.png


BIN
images/wifi.png


BIN
images/wifi0.png


BIN
images/wifi1.png


BIN
images/wifi2.png


BIN
images/wifi3.png


BIN
images/wifi4.png


BIN
images/wlan3.png


BIN
images/xd4.png


BIN
images/ywy.png


+ 70 - 0
main.lua

@@ -0,0 +1,70 @@
+--[[
+@module  main
+@summary 调试宝3.0主程序入口
+@version 1.0.0
+@date    2026.05.09
+@author  李一玮
+@usage
+本文件是调试宝3.0主入口
+]]
+
+-- 项目名称和版本定义
+PROJECT = "TSB_V30" -- 项目名称,用于标识当前工程
+VERSION = "9102.0.001"      -- 项目版本号
+
+LUA_VERSION = 0x91020001
+
+PRODUCT_KEY = "iMzhfzjQXrqszIeMHcUOl48mdbF9lP9D"
+
+-- 在日志中打印项目名和项目版本号
+log.info("ui_demo", PROJECT, VERSION)
+
+-- 设置日志输出风格为样式2(建议调试时开启)
+-- log.style(2)
+
+-- 如果内核固件支持errDump功能,此处进行配置,【强烈建议打开此处的注释】
+-- 因为此功能模块可以记录并且上传脚本在运行过程中出现的语法错误或者其他自定义的错误信息,可以初步分析一些设备运行异常的问题
+-- 以下代码是最基本的用法,更复杂的用法可以详细阅读API说明文档
+-- 启动errDump日志存储并且上传功能,600秒上传一次
+-- if errDump then
+--     errDump.config(true, 600)
+-- end
+
+-- 启动一个循环定时器
+-- 每隔3秒钟打印一次总内存,实时的已使用内存,历史最高的已使用内存情况
+-- 方便分析内存使用是否有异常
+-- sys.timerLoopStart(function()
+--     log.info("mem.lua", rtos.meminfo())
+--     log.info("mem.sys", rtos.meminfo("sys"))
+-- end, 3000)
+
+-- 加载显示驱动
+lcd_drv = require("lcd_drv")
+-- 加载触摸驱动
+tp_drv = require("tp_drv")
+
+require ("power_init")
+
+require("tsb_ui_main")
+
+--require("uart1_msg")
+--require("multiple_uart")
+--require("uart485_test")
+--require("net_manager")
+--require("wifi_sta")
+--require("mobile_sta")
+--require("uart_test")
+--require("test_adc")
+--require("multiple_uart")
+--require("uart485_test")
+--require("bmq_pluse_test")
+--require("pwrkey")
+
+-- 查询当前固件内AirUI核心库版本
+-- local version_result = airui.version()
+-- log.info("airui", "version -> " .. version_result)
+
+-- 用户代码已结束
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后不要加任何语句!!!!!因为添加的任何语句都不会被执行

+ 289 - 0
net_manager.lua

@@ -0,0 +1,289 @@
+--[[
+@module  net_manager
+@summary 网络连接管理器 
+@version 1.0
+@date    2026.05.07
+@author  Wei Jianqiang
+@usage 管理WiFi和4G网络切换
+功能说明:
+1. 默认状态下自动使用4G联网
+2. WiFi连接成功后自动断开4G(进入飞行模式)
+3. 关闭WiFi时自动恢复4G(退出飞行模式)
+]]
+
+-- 网络状态枚举
+local NET_STATE = {
+    DISCONNECTED = "disconnected",  -- 未连接
+    MOBILE_CONNECTED = "mobile_connected",  -- 4G连接
+    WIFI_CONNECTED = "wifi_connected"  -- WiFi连接
+}
+
+-- 当前网络状态
+local current_state = NET_STATE.DISCONNECTED
+
+-- WiFi连接信息
+local wifi_info = {
+    ssid = "",
+    password = ""
+}
+
+-- 网络状态回调函数(供UI注册)
+local status_callback = nil
+
+-- 设置状态回调
+local function set_status_callback(callback)
+    status_callback = callback
+end
+
+-- 通知状态变化
+local function notify_status_change(new_state)
+    current_state = new_state
+    log.info("net_manager", "网络状态变化:", new_state)
+    
+    if status_callback then
+        status_callback(new_state)
+    end
+    
+    -- 发布系统事件
+    sys.publish("NET_STATE_CHANGED", new_state)
+end
+
+-- 初始化网络(默认4G)
+local function init_network()
+    log.info("net_manager", "初始化网络,默认使用4G")
+    
+    -- 确保4G处于非飞行模式
+    if mobile then
+        local result = mobile.flymode(0, false)  -- 退出飞行模式
+        log.info("net_manager", "4G退出飞行模式,原状态:", result)
+    end
+    
+    -- 等待4G网络就绪
+    sys.taskInit(function()
+        log.info("net_manager", "等待4G网络就绪...")
+        local ready = sys.waitUntil("IP_READY", 30000)
+        if ready then
+            notify_status_change(NET_STATE.MOBILE_CONNECTED)
+            log.info("net_manager", "4G网络连接成功")
+        else
+            log.info("net_manager", "4G网络就绪超时")
+        end
+    end)
+end
+
+-- 连接WiFi
+local function connect_wifi(ssid, password)
+    if not ssid or password == "" then
+        log.info("net_manager", "WiFi名称为空")
+        return false
+    end
+    
+    log.info("net_manager", "准备连接WiFi:", ssid)
+    
+    -- 保存WiFi信息
+    wifi_info.ssid = ssid
+    wifi_info.password = password or ""
+    
+    -- 启动连接任务
+    sys.taskInit(function()
+        -- 连接WiFi
+        wlan.connect(ssid, password)
+        
+        -- 等待连接成功
+        local success = sys.waitUntil("IP_READY", 15000)
+        if success then
+            log.info("net_manager", "WiFi连接成功:", ssid)
+            
+            -- WiFi连接成功,断开4G(进入飞行模式)
+            if mobile then
+                local result = mobile.flymode(0, true)
+                log.info("net_manager", "4G进入飞行模式,原状态:", result)
+            end
+            
+            notify_status_change(NET_STATE.WIFI_CONNECTED)
+        else
+            log.info("net_manager", "WiFi连接失败或超时")
+            notify_status_change(NET_STATE.DISCONNECTED)
+        end
+    end)
+    
+    return true
+end
+
+-- 断开WiFi
+local function disconnect_wifi()
+    log.info("net_manager", "断开WiFi")
+    
+    -- 断开WiFi连接
+    wlan.disconnect()
+    
+    -- 恢复4G(退出飞行模式)
+    if mobile then
+        local result = mobile.flymode(0, false)
+        log.info("net_manager", "4G退出飞行模式,原状态:", result)
+    end
+    
+    -- 等待4G恢复
+    sys.taskInit(function()
+        local ready = sys.waitUntil("IP_READY", 30000)
+        if ready then
+            notify_status_change(NET_STATE.MOBILE_CONNECTED)
+            log.info("net_manager", "4G网络恢复成功")
+        else
+            log.info("net_manager", "4G网络恢复超时")
+            notify_status_change(NET_STATE.DISCONNECTED)
+        end
+    end)
+end
+
+-- 开启4G(如果WiFi正在连接,先断开WiFi)
+local function enable_mobile()
+    log.info("net_manager", "开启4G")
+    
+    -- 如果当前是WiFi连接状态,先断开WiFi
+    if current_state == NET_STATE.WIFI_CONNECTED then
+        log.info("net_manager", "当前为WiFi模式,先断开WiFi")
+    end
+    
+    -- 退出4G飞行模式
+    if mobile then
+        local result = mobile.flymode(0, false)
+        log.info("net_manager", "4G退出飞行模式,原状态:", result)
+    end
+    
+    -- 等待4G连接
+    sys.taskInit(function()
+        local ready = sys.waitUntil("IP_READY", 30000)
+        if ready then
+            log.info("net_manager", "4G连接成功")
+            notify_status_change(NET_STATE.MOBILE_CONNECTED)
+            
+            -- 4G连接成功后,确保WiFi已断开
+            local wlan_info = wlan.getInfo()
+            if wlan_info and wlan_info.ssid then
+                log.info("net_manager", "4G连接成功,断开WiFi")
+                wlan.disconnect()
+            end
+        else
+            log.info("net_manager", "4G连接失败")
+            -- 4G连接失败,保持原有状态
+            if current_state == NET_STATE.WIFI_CONNECTED then
+                log.info("net_manager", "保持WiFi连接状态")
+            end
+        end
+    end)
+    
+    return true
+end
+
+-- 关闭4G(进入飞行模式)
+local function disable_mobile()
+    log.info("net_manager", "关闭4G")
+    
+    if mobile then
+        local result = mobile.flymode(0, true)
+        log.info("net_manager", "4G进入飞行模式,原状态:", result)
+    end
+    
+    notify_status_change(NET_STATE.DISCONNECTED)
+    return true
+end
+
+-- 获取当前网络状态
+local function get_status()
+    -- 状态文字映射表
+    local state_map = {
+        [NET_STATE.DISCONNECTED] = "未连接",
+        [NET_STATE.MOBILE_CONNECTED] = "4G连接",
+        [NET_STATE.WIFI_CONNECTED] = "WiFi连接"
+    }
+    
+    -- 创建状态表
+    local status = {}
+    status.state = current_state
+    status.state_text = state_map[current_state] or "未知"
+    status.wifi_ssid = wifi_info.ssid
+    status.is_wifi_connected = (current_state == NET_STATE.WIFI_CONNECTED)
+    status.is_mobile_connected = (current_state == NET_STATE.MOBILE_CONNECTED)
+    
+    -- 添加4G信号信息
+    if mobile then
+        status.mobile_info = {}
+        status.mobile_info.csq = mobile.csq()
+        status.mobile_info.rssi = mobile.rssi()
+        status.mobile_info.imei = mobile.imei()
+    end
+    
+    -- 添加WiFi信息
+    local wlan_info = wlan.getInfo()
+    if wlan_info then
+        status.wifi_info = wlan_info
+    end
+    
+    return status
+end
+
+-- WiFi状态事件监听
+sys.subscribe("WLAN_STA_INC", function(evt, data)
+    log.info("net_manager", "WiFi事件:", evt, data)
+    
+    if evt == "DISCONNECTED" then
+        -- WiFi断开,自动恢复4G
+        if current_state == NET_STATE.WIFI_CONNECTED then
+            log.info("net_manager", "WiFi意外断开,恢复4G")
+            disconnect_wifi()
+        end
+    end
+end)
+
+-- 对外接口
+local exports = {
+    -- 网络状态常量
+    STATE = NET_STATE,
+    
+    -- 初始化网络(默认4G)
+    init = init_network,
+    
+    -- WiFi操作
+    connect_wifi = connect_wifi,
+    disconnect_wifi = disconnect_wifi,
+    
+    -- 4G操作
+    enable_mobile = enable_mobile,
+    disable_mobile = disable_mobile,
+    
+    -- 获取状态
+    get_status = get_status,
+    
+    -- 设置状态回调
+    set_status_callback = set_status_callback
+}
+
+-- 模块加载时自动初始化网络(上电即执行)
+sys.taskInit(function()
+    log.info("net_manager", "模块加载,自动初始化网络")
+    
+    -- 等待IP就绪(可能已经就绪)
+    local ready = sys.waitUntil("IP_READY", 30000)
+    if ready then
+        log.info("net_manager", "IP已就绪,当前为4G模式")
+        current_state = NET_STATE.MOBILE_CONNECTED
+        sys.publish("NET_STATE_CHANGED", current_state)
+    else
+        log.info("net_manager", "等待IP就绪超时")
+    end
+    
+    -- 启动定时检查网络状态
+    sys.timerLoopStart(function()
+        local status = get_status()
+        if status.is_mobile_connected and current_state ~= NET_STATE.MOBILE_CONNECTED then
+            current_state = NET_STATE.MOBILE_CONNECTED
+            sys.publish("NET_STATE_CHANGED", current_state)
+        elseif status.is_wifi_connected and current_state ~= NET_STATE.WIFI_CONNECTED then
+            current_state = NET_STATE.WIFI_CONNECTED
+            sys.publish("NET_STATE_CHANGED", current_state)
+        end
+    end, 30000)  -- 每30秒检查一次
+end)
+
+return exports

+ 187 - 0
power_init.lua

@@ -0,0 +1,187 @@
+local i2c_id = 1
+local ch224q_i2c_addr = 0x22  -- 受电分析芯片I2C地址
+local ch224q_i2c_reg1 = 0x09  -- 快充协议激活情况(只读)
+local ch224q_i2c_reg2 = 0x0A  -- 电压请求(只写,0:5V 1:9V 2:12V 3:15V 4:20V 5:28V)
+local ch224q_i2c_reg3 = 0x50  -- 最大电流参考值(只写,该寄存器仅PD协议有效)
+
+local bq25895_addr = 0x6A   -- 充电芯片I2C地址
+local bq25895_reg1 = 0x02   -- 控制ADC检测开关
+local bq25895_reg2 = 0x09   -- bit5位可以控制VSYS到VBAT的通路开关(默认0导通,1关闭)
+
+local reg_addr_ADCCTL = 0x02
+local reg_addr_BATV   = 0x0E
+local reg_addr_SYSV   = 0x0F
+local reg_addr_TSPCT  = 0x10
+local reg_addr_VBUSV  = 0x11
+local reg_addr_ICHGR  = 0x12
+local reg_addr_ICO    = 0x13
+
+local function get_value()
+    local ret_val_BATV = i2c.readReg(i2c_id, bq25895_addr, reg_addr_BATV, 1)
+    local ret_val_SYSV = i2c.readReg(i2c_id, bq25895_addr, reg_addr_SYSV, 1)
+    local ret_val_TSPCT = i2c.readReg(i2c_id, bq25895_addr, reg_addr_TSPCT, 1)
+    local ret_val_VBUSV = i2c.readReg(i2c_id, bq25895_addr, reg_addr_VBUSV, 1)
+    local ret_val_ICHGR = i2c.readReg(i2c_id, bq25895_addr, reg_addr_ICHGR, 1)
+    local ret_val_ICO = i2c.readReg(i2c_id, bq25895_addr, reg_addr_ICO, 1)
+
+    local byte_BATV = (ret_val_BATV == "") and 0x00 or string.byte(ret_val_BATV)
+    local byte_SYSV = (ret_val_SYSV == "") and 0x00 or string.byte(ret_val_SYSV)
+    local byte_TSPCT = (ret_val_TSPCT == "") and 0x00 or string.byte(ret_val_TSPCT)
+    local byte_VBUSV = (ret_val_VBUSV == "") and 0x00 or string.byte(ret_val_VBUSV)
+    local byte_ICHGR = (ret_val_ICHGR == "") and 0x00 or string.byte(ret_val_ICHGR)
+    local byte_ICO = (ret_val_ICO == "") and 0x00 or string.byte(ret_val_ICO)
+
+    local s_BATV = (ret_val_BATV == "") and "nil" or string.format("%02X", byte_BATV)
+    local s_SYSV = (ret_val_SYSV == "") and "nil" or string.format("%02X", byte_SYSV)
+    local s_TSPCT = (ret_val_TSPCT == "") and "nil" or string.format("%02X", byte_TSPCT)
+    local s_VBUSV = (ret_val_VBUSV == "") and "nil" or string.format("%02X", byte_VBUSV)
+    local s_ICHGR = (ret_val_ICHGR == "") and "nil" or string.format("%02X", byte_ICHGR)
+    local s_ICO = (ret_val_ICO == "") and "nil" or string.format("%02X", byte_ICO)
+
+
+    local value_BATV = (byte_BATV == 0x00) and 0x00 or ((byte_BATV & 0x7F) * 20 + 2304)
+    local value_SYSV = (byte_SYSV == 0x00) and 0x00 or ((byte_SYSV & 0x7F) * 20 + 2304)
+    local value_TSPCT = (byte_TSPCT == 0x00) and 0x00 or ((byte_TSPCT & 0x7F) * 0.465 + 21)
+    local value_VBUSV = (byte_VBUSV == 0x00) and 0x00 or ((byte_VBUSV & 0x7F) * 100 + 2600)
+    local value_ICHGR = (byte_ICHGR == 0x00) and 0x00 or ((byte_ICHGR & 0x7F) * 50)
+    local value_ICO = (byte_ICO == 0x00) and 0x00 or ((byte_ICO & 0x3F) * 50 + 100)
+
+    local value_Pin = 0
+    local s_Pin = "nil"
+    if (ret_val_VBUSV ~= "") and (ret_val_ICO ~= "") then
+        value_Pin = value_VBUSV * value_ICO / 1000
+        s_Pin = string.format("%.1fmW", value_Pin)
+    else
+        value_Pin = 0
+        s_Pin = "nil"
+    end
+
+    local value_Pchgr = 0
+    local s_Pchgr = "nil"
+    if (ret_val_BATV ~= "") and (ret_val_ICHGR ~= "") then
+        value_Pchgr = value_BATV * value_ICHGR / 1000
+        
+        s_Pchgr = string.format("%.1fmW", value_Pchgr)
+    else
+        value_Pchgr = 0
+        s_Pchgr = "nil"
+    end
+
+    local dbg_info_s1 = ""
+    dbg_info_s1 = string.format("%02X=%s, %02X=%s, %02X=%s, %02X=%s, %02X=%s, %02X=%s=%dmA\n", 
+                reg_addr_BATV, s_BATV,
+                reg_addr_SYSV, s_SYSV,
+                reg_addr_TSPCT, s_TSPCT,
+                reg_addr_VBUSV, s_VBUSV,
+                reg_addr_ICHGR, s_ICHGR,
+                reg_addr_ICO, s_ICO,value_ICO
+                )
+
+    dbg_info_s1 = dbg_info_s1 .."\n"
+
+    dbg_info_s1 = dbg_info_s1..string.format("iBat=%dmV;", value_BATV)
+    dbg_info_s1 = dbg_info_s1..string.format("Sys=%dmV;", value_SYSV)
+    dbg_info_s1 = dbg_info_s1..string.format("TS=%.2f%%;", value_TSPCT)
+    dbg_info_s1 = dbg_info_s1..string.format("Bus=%dmV;", value_VBUSV)
+    dbg_info_s1 = dbg_info_s1..string.format("Chg=%dmA;", value_ICHGR)
+
+    dbg_info_s1 = dbg_info_s1 .."\n"
+    -- dbg_info_s1 = dbg_info_s1.."iccid:"..(mobile.iccid() or "N/A")..";"
+    -- dbg_info_s1 = dbg_info_s1.."imei:"..(mobile.imei() or "N/A")..";\n"
+    -- dbg_info_s1 = dbg_info_s1.."csq:"..(mobile.csq() or "N/A")
+    -- dbg_info_s1 = dbg_info_s1..";rssi:"..(mobile.rssi() or "N/A")
+    -- dbg_info_s1 = dbg_info_s1..";rsrq:"..(mobile.rsrq() or "N/A")
+    -- dbg_info_s1 = dbg_info_s1..";rsrp:"..(mobile.rsrp() or "N/A")
+    -- dbg_info_s1 = dbg_info_s1..";snr:"..(mobile.snr() or "N/A")
+
+    uart.write(1, dbg_info_s1)
+    log.info("充电信息:", dbg_info_s1)
+end
+
+
+local function init_set()
+    -- 3V3使能
+    gpio.setup(140,0)
+    gpio.set(140, gpio.HIGH)
+
+    -- 10V使能
+    gpio.setup(30,0)
+    gpio.set(30, gpio.HIGH)
+
+    -- BQ_CE电池充电使能
+    gpio.setup(141,0)
+
+    -- 显示模块使能
+    gpio.setup(32,0)
+
+    -- 运行灯闪烁控制
+    gpio.setup(156,0)
+    sys.timerLoopStart(function()
+        gpio.toggle(156)
+    end, 500)
+
+end
+                              
+sys.taskInit(function()
+    init_set()
+    sys.wait(200)
+    local result = i2c.setup(i2c_id,i2c.SLOW)
+    log.info("i2c 1", "setup", result)
+
+    local result_soft_rst = i2c.writeReg(i2c_id, bq25895_addr, 0x14, string.char(0x80))
+
+    -- local data1 = i2c.readReg(i2c_id, ch224q_i2c_addr, ch224q_i2c_reg1, 1)
+    -- local data2 = i2c.readReg(i2c_id, ch224q_i2c_addr, ch224q_i2c_reg3, 1)
+    -- local result1 = i2c.writeReg(i2c_id, ch224q_i2c_addr, ch224q_i2c_reg2, string.char(0x01))
+    -- if result1 then
+    --     uart.write(1, "成功写入数据")
+    -- else
+    --     uart.write(1, "写入数据失败")
+    -- end
+    -- log.info("i2c 请求电压",result1)
+
+    -- uart.write(1, "充电协议:"..data1:toHex())
+    -- uart.write(1, "可用最大电流:"..data2:toHex())
+
+    -- local reg2_data = i2c.readReg(i2c_id, bq25895_addr, bq25895_reg1, 1)
+    -- local reg2_temp = (reg2_data == "") and 0x00 or string.byte(reg2_data)
+    -- local reg2_data_result = reg2_temp | 0xC0
+    -- local reg2_result = i2c.writeReg(i2c_id, bq25895_addr, bq25895_reg1, string.char(reg2_data_result))
+
+    -- -- local reg02_data = i2c.readReg(i2c_id, bq25895_addr, bq25895_reg1, 1)
+    -- -- uart.write(1, "\nreg02:"..reg02_data:toHex().."\n")
+
+    -- local reg09_result = i2c.writeReg(i2c_id, bq25895_addr, bq25895_reg2, string.char(0x44))
+
+    -- while true do
+    --     sys.wait(3000)
+    --     for i = 0, 20 do
+    --     --     if i == 14 then -- 读取VBAT电压值
+    --     --         -- local reg09_data = i2c.readReg(i2c_id, bq25895_addr, bq25895_reg2, 1)
+    --     --         -- local reg09_data_temp = string.byte(reg2_data)
+    --     --         -- local reg09_data_result = 0x64
+    --     --         local reg09_result = i2c.writeReg(i2c_id, bq25895_addr, bq25895_reg2, string.char(0x64))
+    --     --         sys.wait(500)
+    --     --         local data = i2c.readReg(i2c_id, bq25895_addr, i, 1)
+    --     --         local voltage = string.toValue(data)
+    --     --         uart.write(1, "寄存器14状态,"..i..":"..data:toHex().." "..voltage.."\n")
+    --     --         local reg09_result = i2c.writeReg(i2c_id, bq25895_addr, bq25895_reg2, string.char(0x44))
+    --     --         sys.wait(500)
+    --     --     end
+    --         -- local data = i2c.readReg(i2c_id, bq25895_addr, i, 1)
+    --         -- uart.write(1, "寄存器状态,"..i..":"..data:toHex().."\n")
+    --     end
+    --     uart.write(1, "电池管理:\n")
+    --     log.info("电池管理:")
+    --     get_value()
+    --     -- sys.wait(1000)
+    --     -- local reg09_result = i2c.writeReg(i2c_id, bq25895_addr, bq25895_reg2, string.char(0x64))
+    --     -- sys.wait(5000)
+    --     -- uart.write(1, "断开状态下:\n")
+    --     -- get_value()
+    --     -- sys.wait(1000)
+    --     -- local reg09_result = i2c.writeReg(i2c_id, bq25895_addr, bq25895_reg2, string.char(0x44))
+        
+    -- end
+end
+)

+ 40 - 0
pwrkey.lua

@@ -0,0 +1,40 @@
+local poweron_pin = 46    -- 赋值poweron引脚编号
+
+local power_off_flag = false  -- 是否正在关机
+
+local count=0    -- 五秒内短按三次关机
+
+local function pwrkeycb() 
+    log.info("poweron_key", gpio.get(poweron_pin))
+    if gpio.get(poweron_pin) == 0 then
+        count=count+1
+        sys.timerStart(function()
+            log.info("计数归零")
+            count=0
+        end, 5000)
+        if count>=3 then
+            power_off_flag = true
+            -- pm.shutdown() 
+        end
+    end
+end
+
+
+if poweron_pin ~= 255 then
+    gpio.setup(poweron_pin, pwrkeycb, gpio.PULLUP,gpio.BOTH)
+else
+    log.info("bsp not support")
+end
+
+
+
+sys.taskInit(function()
+    while true do
+        if power_off_flag then
+          sys.wait(1000)
+          pm.shutdown() 
+        end
+
+        sys.wait(100)
+    end
+end)

+ 75 - 0
tp_key_drv/tp_drv.lua

@@ -0,0 +1,75 @@
+--[[
+@module  tp_drv
+@summary 触摸面板驱动模块,基于tp核心库
+@version 1.0
+@date    2026.02.05
+@author  江访
+@usage
+本模块为触摸面板驱动功能模块,主要功能包括:
+1、初始化GT911触摸控制器;
+2、配置I2C通信接口和触摸回调函数;
+3、发布触摸事件消息供UI系统处理;
+
+对外接口:
+1、tp_drv.init():初始化触摸面板驱动
+]]
+
+local tp_drv = {}
+
+
+--[[
+初始化触摸面板驱动;
+
+@api tp_drv.init()
+@summary 配置并初始化GT911触摸控制器
+@return boolean 初始化成功返回true,失败返回false
+
+@usage
+-- 初始化触摸面板
+local result = tp_drv.init()
+if result then
+    log.info("触摸面板初始化成功")
+else
+    log.error("触摸面板初始化失败")
+end
+]]
+
+local function tp_callback(tp_device, tp_data)
+    --log.info("tp_drv tp_callback", tp_data[1].event, tp_data[1].x, tp_data[1].y)
+    -- 发布触摸事件供其他模块订阅
+    if(tp_data[1].event == tp.EVENT_UP) then
+        sys.publish("TP_TOUCH", tp_data[1].x, tp_data[1].y)
+    end
+end
+
+function tp_drv.init()
+    -- 开机I2C供电,触摸、摄像头和音频都是使用I2C0
+    -- gpio.setup(147, 1)
+    -- gpio.setup(164, 1)
+    -- 等待供电稳定
+    sys.wait(100)
+    -- 初始化硬件I2C
+    i2c.setup(0, i2c.SLOW) -- 初始化I2C 0,设置为低速模式
+
+    -- 此处触摸IC数据读取使用的是软件I2C接口
+    -- 参数说明:
+    -- "gt911": 触摸控制器型号
+    -- port: I2C接口对象
+    -- pin_rst: 复位引脚编号
+    -- pin_int: 中断引脚编号
+    -- w: 触摸面板宽度
+    -- h: 触摸面板高度
+    local result = tp.init("gt911", { port = 0, pin_rst = 0xff, pin_int = 21},tp_callback)
+
+    log.info("tp.init", result)
+
+    if not result then
+        log.error("ui_main", "触摸初始化失败")
+        return result
+    else
+        -- 绑定触摸设备到AirUI输入设备
+        return airui.device_bind_touch(result)
+    end
+end
+
+return tp_drv

+ 178 - 0
tsb_ui/tsb_232log_page.lua

@@ -0,0 +1,178 @@
+--[[
+@module  tsb_232log_page        
+@summary 232日志页面
+@version 1.0
+@date    2026.03.17
+@author  李一玮
+@usage
+本文件是232日志页面,展示232日志的各种用法。
+]]
+
+local tsb_232log_page = {}
+
+-- 页面UI元素
+local main_container = nil
+local common_ui = require("tsb_common_page")
+
+-- 创建UI
+function tsb_232log_page.create_ui()
+    main_container = airui.container({
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 320,
+        color = 0xF5F5F5,
+    })
+
+    --------------------- 标题栏 ------------------------
+    local title_bar = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 30,
+        color = 0xFFCCBC,
+    })
+
+    airui.label({
+        parent = title_bar,
+        text = "232监测界面",
+        x = 190,
+        y = 8,
+        w = 200,
+        h = 20,
+        font_size = 16,
+        color = 0xFFFFFF,
+    })
+
+    -- 标题栏公共信息展示
+    common_ui.add_battery_display(title_bar)
+    common_ui.create_back_button(title_bar, tsb_232log_page.cleanup)
+
+    -- 滚动容器
+    local scroll_container = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 30, 
+        w = 480,
+        h = 270,
+        color = 0xFFFFFF,
+    })
+
+    --------------------- 控制区 ------------------------
+    -- 波特率标签
+    airui.label({
+        parent = scroll_container,
+        text = "波特率:",
+        x = 5,
+        y = 16,
+        w = 60,
+        h = 30,
+        font_size = 15,
+    })
+
+    -- 波特率下拉框
+    local baudrate_dropdown = airui.dropdown({
+        parent = scroll_container,
+        x = 65,
+        y = 7,
+        w = 100,
+        h = 35,
+        options = {"2400", "4800", "9600", "19200", "38400", "57600", "115200"},
+        default_index = 2, -- 默认选择9600
+        on_change = function(self,idx)
+            log.info("485_baudrate", "选择了波特率:" .. self.options[idx + 1])
+        end
+    })
+
+
+    airui.label({
+        parent = scroll_container,
+        text = "推测:",
+        x = 180,
+        y = 16,
+        w = 40,
+        h = 30,
+        font_size = 15,
+    })
+
+    local baud_predict = airui.label({
+        parent = scroll_container,
+        text = "2400",
+        x = 220,
+        y = 15,
+        w = 50,
+        h = 20,
+        font_size = 15,
+    })
+
+
+    --------------------- 485日志显示 ------------------------
+    local log_container = airui.container({
+        parent = scroll_container,
+        x = 0,
+        y = 45,
+        w = 480,
+        h = 222,
+        color = 0xFFFFFF,
+    })
+
+    -- 日志内容显示
+    local log_content = airui.textarea({
+        parent = log_container,
+        x = 5,
+        y = 5,
+        w = 470,
+        h = 212,
+        text = "00 01 02 03 04 05 06 07\n" ..
+               "08 09 0A 0B 0C 0D 0E 0F\n" 
+    })
+
+    -- 开始监测按钮
+    local start_btn = airui.button({
+        parent = scroll_container,
+        x = 285,
+        y = 5,
+        w = 100,
+        h = 35,
+        text = "开始监测",
+        style = { bg_color = 0x2B6FF1,border_color = 0x2B6FF1, text_color = 0xFFFFFF, radius = 8 },
+        on_click = function(self)
+            log.info("485_log", "开始监测按钮被点击")
+        end
+    })
+
+    -- 清空按钮
+    local clear_btn = airui.button({
+        parent = scroll_container,
+        x = 395,
+        y = 5,
+        w = 80,
+        h = 35,
+        text = "清空",
+        on_click = function(self)
+            log.info("485_log", "清空按钮被点击")
+            -- 清空日志内容
+            log_content:set_text("")
+        end
+    })
+    ---------------------------------------------------
+
+    -- 底部信息
+    common_ui.create_status_bar(main_container)
+end
+
+-- 初始化页面
+function tsb_232log_page.init(params)
+    tsb_232log_page.create_ui()
+end
+
+-- 清理页面
+function tsb_232log_page.cleanup()
+    if main_container then
+        main_container:destroy()
+        main_container = nil
+    end
+end
+
+return tsb_232log_page

+ 178 - 0
tsb_ui/tsb_485log_page.lua

@@ -0,0 +1,178 @@
+--[[
+@module  tsb_485log_page
+@summary 485日志页面
+@version 1.0
+@date    2026.03.04
+@author  李一玮
+@usage
+本文件是485日志页面,展示485日志的各种用法。
+]]
+
+local tsb_485log_page = {}
+
+-- 页面UI元素
+local main_container = nil
+local common_ui = require("tsb_common_page")
+
+-- 创建UI
+function tsb_485log_page.create_ui()
+    main_container = airui.container({
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 320,
+        color = 0xF5F5F5,
+    })
+
+    --------------------- 标题栏 ------------------------
+    local title_bar = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 30,
+        color = 0xA5D6A7,
+    })
+
+    airui.label({
+        parent = title_bar,
+        text = "485监测界面",
+        x = 190,
+        y = 8,
+        w = 200,
+        h = 20,
+        font_size = 16,
+        color = 0xFFFFFF,
+    })
+
+    -- 标题栏公共信息展示
+    common_ui.add_battery_display(title_bar)
+    common_ui.create_back_button(title_bar, tsb_485log_page.cleanup)
+
+    -- 滚动容器
+    local scroll_container = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 30,
+        w = 480,
+        h = 270,
+        color = 0xFFFFFF,
+    })
+
+    --------------------- 控制区 ------------------------
+    -- 波特率标签
+    airui.label({
+        parent = scroll_container,
+        text = "波特率:",
+        x = 5,
+        y = 16,
+        w = 60,
+        h = 30,
+        font_size = 15,
+    })
+
+    -- 波特率下拉框
+    local baudrate_dropdown = airui.dropdown({
+        parent = scroll_container,
+        x = 65,
+        y = 7,
+        w = 100,
+        h = 35,
+        options = {"2400", "4800", "9600", "19200", "38400", "57600", "115200"},
+        default_index = 2, -- 默认选择9600
+        on_change = function(self,idx)
+            log.info("485_baudrate", "选择了波特率:" .. self.options[idx + 1])
+        end
+    })
+
+
+    airui.label({
+        parent = scroll_container,
+        text = "推测:",
+        x = 180,
+        y = 16,
+        w = 40,
+        h = 30,
+        font_size = 15,
+    })
+
+    local baud_predict = airui.label({
+        parent = scroll_container,
+        text = "2400",
+        x = 220,
+        y = 15,
+        w = 50,
+        h = 20,
+        font_size = 15,
+    })
+
+
+    --------------------- 485日志显示 ------------------------
+    local log_container = airui.container({
+        parent = scroll_container,
+        x = 0,
+        y = 45,
+        w = 480,
+        h = 222,
+        color = 0xFFFFFF,
+    })
+
+    -- 日志内容显示
+    local log_content = airui.textarea({
+        parent = log_container,
+        x = 5,
+        y = 5,
+        w = 470,
+        h = 212,
+        text = "00 01 02 03 04 05 06 07\n" ..
+               "08 09 0A 0B 0C 0D 0E 0F\n"
+    })
+
+    -- 开始监测按钮
+    local start_btn = airui.button({
+        parent = scroll_container,
+        x = 285,
+        y = 5,
+        w = 100,
+        h = 35,
+        text = "开始监测",
+        style = { bg_color = 0x2B6FF1,border_color = 0x2B6FF1, text_color = 0xFFFFFF, radius = 8 },
+        on_click = function(self)
+            log.info("485_log", "开始监测按钮被点击")
+        end
+    })
+
+    -- 清空按钮
+    local clear_btn = airui.button({
+        parent = scroll_container,
+        x = 395,
+        y = 5,
+        w = 80,
+        h = 35,
+        text = "清空",
+        on_click = function(self)
+            log.info("485_log", "清空按钮被点击")
+            -- 清空日志内容
+            log_content:set_text("")
+        end
+    })
+    ---------------------------------------------------
+
+    -- 底部信息
+    common_ui.create_status_bar(main_container)
+end
+
+-- 初始化页面
+function tsb_485log_page.init(params)
+    tsb_485log_page.create_ui()
+end
+
+-- 清理页面
+function tsb_485log_page.cleanup()
+    if main_container then
+        main_container:destroy()
+        main_container = nil
+    end
+end
+
+return tsb_485log_page

+ 384 - 0
tsb_ui/tsb_bmq_page.lua

@@ -0,0 +1,384 @@
+--[[
+@module  tsb_bmq_page
+@summary 编码器演示页面
+@version 1.0
+@date    2026.03.04
+@author  李一玮
+@usage
+本文件是编码器演示页面,展示编码器的各种用法。
+]]
+
+local tsb_bmq_page = {}
+
+-- 页面UI元素
+local main_container = nil
+local common_ui = require("tsb_common_page")
+
+local label_cur_vol = nil
+local label_pulse_count = nil
+
+-- GPIO检测配置
+local DETECT_PIN = 31  -- 检测引脚:GPIO31
+local pulse_count = 0  -- 脉冲计数
+local level_check_timer = nil  -- 电平检查定时器
+
+-- 电平中断回调函数(检测到高→低跳变时执行)
+local function pin_interrupt_callback()
+    -- 低电平时计数(下降沿)
+    if label_pulse_count then
+        pulse_count = pulse_count + 1
+        label_pulse_count:set_text(tostring(pulse_count))
+        --log.info("bmq", "检测到下降沿,脉冲数:", pulse_count)
+    end
+end
+
+-- 周期性检查电平状态(用于更新显示)
+local function check_level_status()
+    if label_cur_vol then
+        local level = gpio.get(DETECT_PIN)
+        label_cur_vol:set_text(level == 1 and "高" or "低")
+    end
+end
+
+-- 创建UI
+function tsb_bmq_page.create_ui()
+    main_container = airui.container({
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 320,
+        color = 0xF5F5F5,
+    })
+
+    --------------------- 标题栏 ------------------------
+    local title_bar = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 30,
+        color = 0xFF9800,
+    })
+
+    airui.label({
+        parent = title_bar,
+        text = "编码器",
+        x = 210,
+        y = 8,
+        w = 80,
+        h = 20,
+        font_size = 16,
+        color = 0xFFFFFF,
+    })
+    
+    -- 标题栏公共信息展示
+    common_ui.add_battery_display(title_bar)
+    common_ui.create_back_button(title_bar, tsb_bmq_page.cleanup)
+    ---------------------------------------------------
+
+    -- 滚动容器
+    local scroll_container = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 30,
+        w = 480,
+        h = 270,
+        color = 0xF5F5F5,
+    })
+
+    local keyboard1 = airui.keyboard({
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 160,                        -- x, y, 键盘默认打开ALIGN_BOTTOM_MID,位置从中下方开始计算
+        mode = "numeric",               -- 键盘模式,可选 "text"/"upper"/"lower"/"numeric"
+        preview = true,          
+        preview_height = 35,
+        auto_hide = true,               -- 自动隐藏键盘
+        bg_color = 0xf1f1f1,            -- 键盘背景颜色为灰色,可选,不设置则透明
+        on_commit = function()          -- 确认事件回调,只有在按下确认键时才会触发
+            log.info("keyboard", "commit")
+        end
+    })
+
+    ------------------- 模拟编码器输入 ------------------
+    local bmq_input_container = airui.container({
+        parent = scroll_container,
+        x = 10,
+        y = 5,
+        w = 200,
+        h = 260,
+        color = 0xFFFFFF,
+        radius = 5,
+    })
+
+    airui.label({
+        parent = bmq_input_container,
+        text = "编码器信号输入",
+        x = 35,
+        y = 10,
+        w = 140,
+        h = 30,
+        font_size = 18,
+    })
+
+    airui.label({
+        parent = bmq_input_container,
+        text = "实时电平:",
+        x = 20,
+        y = 80,
+        w = 80,
+        h = 30,
+        font_size = 16,
+    })
+
+    label_cur_vol = airui.label({
+        parent = bmq_input_container,
+        text = "高",
+        x = 100,
+        y = 80,
+        w = 80,
+        h = 30,
+        font_size = 16,
+    })
+
+    airui.label({
+        parent = bmq_input_container,
+        text = "检测脉冲数:",
+        x = 20,
+        y = 140,
+        w = 100,
+        h = 30,
+        font_size = 16,
+    })
+
+    label_pulse_count = airui.label({
+        parent = bmq_input_container,
+        text = "12345",
+        x = 120,
+        y = 140,
+        w = 70,
+        h = 30,
+        font_size = 16,
+    })
+
+    local tax_serial_btn = airui.button({
+        parent = bmq_input_container,
+        x = 65,
+        y = 215,
+        w = 80,
+        h = 35,
+        text = "清零",
+        on_click = function(self)
+            log.info("button", "清零按钮被点击")
+            pulse_count = 0
+            label_pulse_count:set_text("0")
+        end
+    })
+
+    ----------------------------------------------------
+
+    ------------------- 模拟编码器输出 ------------------
+    local bmq_output_container = airui.container({
+        parent = scroll_container,
+        x = 220,
+        y = 5,
+        w = 250,
+        h = 260,
+        color = 0xFFFFFF,
+        radius = 5,
+    })
+
+    airui.label({
+        parent = bmq_output_container,
+        text = "编码器信号输出",
+        x = 60,
+        y = 10,
+        w = 140,
+        h = 30,
+        font_size = 18,
+    })
+
+    -- 脉冲周期
+    airui.label({
+        parent = bmq_output_container,
+        text = "脉冲周期:",
+        x = 20,
+        y = 50,
+        w = 80,
+        h = 30,
+        font_size = 16,
+    })
+
+    local cycle_input = airui.textarea({
+        parent = bmq_output_container,
+        x = 100,
+        y = 40,
+        w = 60,
+        h = 35,
+        text = "5",
+        max_len = 5,
+        keyboard = keyboard1
+    })
+
+    airui.label({
+        parent = bmq_output_container,
+        text = "ms",
+        x = 165,
+        y = 50,
+        w = 30,
+        h = 30,
+        font_size = 16,
+    })
+
+    -- 电压幅度
+    airui.label({
+        parent = bmq_output_container,
+        text = "电压幅度:",
+        x = 20,
+        y = 90,
+        w = 80,
+        h = 30,
+        font_size = 16,
+    })
+
+    local voltage_input = airui.textarea({
+        parent = bmq_output_container,
+        x = 100,
+        y = 80,
+        w = 60,
+        h = 35,
+        text = "5",
+        max_len = 2,
+        keyboard = keyboard1
+    })
+
+    airui.label({
+        parent = bmq_output_container,
+        text = "V",
+        x = 170,
+        y = 90,
+        w = 30,
+        h = 30,
+        font_size = 16,
+    })
+
+    -- 当前电平
+    airui.label({
+        parent = bmq_output_container,
+        text = "当前电平:",
+        x = 20,
+        y = 130,
+        w = 80,
+        h = 30,
+        font_size = 16,
+    })
+
+    local label_output_level = airui.label({
+        parent = bmq_output_container,
+        text = "高",
+        x = 100,
+        y = 130,
+        w = 80,
+        h = 30,
+        font_size = 16,
+    })
+
+    -- 输出脉冲数
+    airui.label({
+        parent = bmq_output_container,
+        text = "输出脉冲数:",
+        x = 20,
+        y = 170,
+        w = 100,
+        h = 30,
+        font_size = 16,
+    })
+
+    local label_output_pulse = airui.label({
+        parent = bmq_output_container,
+        text = "0",
+        x = 120,
+        y = 170,
+        w = 80,
+        h = 30,
+        font_size = 16,
+    })
+
+    -- 按钮
+    local start_btn = airui.button({
+        parent = bmq_output_container,
+        x = 40,
+        y = 215,
+        w = 80,
+        h = 35,
+        text = "开始",
+        style = { bg_color = 0x2B6FF1,border_color = 0x2B6FF1, text_color = 0xFFFFFF, radius = 8 },
+        on_click = function(self)
+            log.info("button", "开始按钮被点击")
+        end
+    })
+
+    local clear_btn = airui.button({
+        parent = bmq_output_container,
+        x = 130,
+        y = 215,
+        w = 80,
+        h = 35,
+        text = "清零",
+        on_click = function(self)
+            log.info("button", "清零按钮被点击")
+        end
+    })
+    ----------------------------------------------------
+    -- 底部信息
+    common_ui.create_status_bar(main_container)
+end
+
+-- 初始化GPIO检测
+local function init_gpio_detect()
+    -- 配置为下降沿中断检测
+    gpio.setup(DETECT_PIN, pin_interrupt_callback, gpio.PULLDOWN, gpio.FALLING)
+    log.info("bmq", "GPIO初始化完成,引脚:", DETECT_PIN)
+    
+    -- 初始化显示当前电平
+    local level = gpio.get(DETECT_PIN)
+    if label_cur_vol then
+        label_cur_vol:set_text(level == 1 and "高" or "低")
+    end
+    if label_pulse_count then
+        label_pulse_count:set_text("0")
+    end
+    
+    -- 启动周期性电平检查定时器(每50ms检查一次)
+    level_check_timer = sys.timerLoopStart(check_level_status, 2)
+    log.info("bmq", "电平检查定时器已启动")
+end
+
+-- 初始化页面
+function tsb_bmq_page.init(params)
+    tsb_bmq_page.create_ui()
+    -- 初始化GPIO检测
+    init_gpio_detect()
+end
+
+-- 清理页面
+function tsb_bmq_page.cleanup()
+    -- 停止电平检查定时器
+    if level_check_timer then
+        sys.timerStop(level_check_timer)
+        level_check_timer = nil
+        log.info("bmq", "电平检查定时器已停止")
+    end
+    
+    -- 释放GPIO资源
+    gpio.setup(DETECT_PIN, nil)
+    log.info("bmq", "GPIO资源已释放")
+    
+    if main_container then
+        main_container:destroy()
+        main_container = nil
+    end
+end
+
+return tsb_bmq_page

+ 563 - 0
tsb_ui/tsb_bsk_page.lua

@@ -0,0 +1,563 @@
+--[[
+@module  tsb_bsk_page
+@summary 报税口演示页面
+@version 1.0
+@date    2026.03.04
+@author  李一玮
+@usage
+本文件是报税口演示页面,展示报税口的各种用法。
+]]
+
+local tsb_bsk_page = {}
+
+-- 页面UI元素
+local main_container = nil
+local common_ui = require("tsb_common_page")
+
+-- 创建UI
+function tsb_bsk_page.create_ui()
+    main_container = airui.container({
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 320,
+        color = 0xF5F5F5,
+    })
+    --------------------- 标题栏 ------------------------
+    local title_bar = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 30,
+        color = 0x007AFF,
+    })
+
+    airui.label({
+        parent = title_bar,
+        text = "报税口",
+        x = 210,
+        y = 8,
+        w = 80,
+        h = 20,
+        font_size = 16,
+        color = 0xFFFFFF,
+    })
+    
+    -- 标题栏公共信息展示
+    common_ui.add_battery_display(title_bar)
+    common_ui.create_back_button(title_bar, tsb_bsk_page.cleanup)
+
+    ---------------------------------------------------
+
+    -- 滚动容器
+    local scroll_container = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 30,
+        w = 480,
+        h = 270,
+        color = 0xF5F5F5,
+    })
+
+    local keyboard1 = airui.keyboard({
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 160,                        -- x, y, 键盘默认打开ALIGN_BOTTOM_MID,位置从中下方开始计算
+        mode = "numeric",               -- 键盘模式,可选 "text"/"upper"/"lower"/"numeric"
+        preview = true,          
+        preview_height = 35,
+        auto_hide = true,               -- 自动隐藏键盘
+        bg_color = 0xf1f1f1,            -- 键盘背景颜色为灰色,可选,不设置则透明
+        on_commit = function()          -- 确认事件回调,只有在按下确认键时才会触发
+            log.info("keyboard", "commit")
+        end
+    })
+
+    --------------------- 接口号 ------------------------
+    airui.label({
+        parent = scroll_container,
+        text = "接口号:",
+        x = 5,
+        y = 17,
+        w = 55,
+        h = 20,
+        font_size = 15,
+    })
+
+    local tax_dropdown = airui.dropdown({
+        parent = scroll_container,
+        x = 60,
+        y = 7,
+        w = 55,
+        h = 35,
+        options = {"A", "B"},
+        default_index = 0,
+        on_change = function(self,idx)
+            -- local texts = {"选项1", "选项2", "选项3", "选项4", "选项5"}
+            -- selected_label1:set_text("当前选中: " .. texts[idx + 1])
+        end
+    })
+    ---------------------------------------------------
+
+    --------------------- 枪号 ------------------------
+    airui.label({
+        parent = scroll_container,
+        text = "枪号:",
+        x = 120,
+        y = 17,
+        w = 40,
+        h = 20,
+        font_size = 15,
+    })
+
+    local gunnum_input = airui.textarea({
+        parent = scroll_container,
+        x = 160,
+        y = 7,
+        w = 55,
+        h = 35,
+        text = "1",
+        -- placeholder = "...",
+        max_len = 1,
+        keyboard = keyboard1
+    })
+    ---------------------------------------------------
+
+    
+    --------------------- 新国标开关 -------------------
+    airui.label({
+        parent = scroll_container,
+        text = "新国标",
+        x = 217,
+        y = 17,
+        w = 50,
+        h = 20,
+        font_size = 15,
+    })
+
+    local switch_new_tax = airui.switch({
+        parent = scroll_container,
+        x = 267,
+        y = 12,
+        w = 50,
+        h = 25,
+        checked = false,
+        on_change = function(self)
+            log.info("NEW_TAX", self:get_state() and "开启" or "关闭")
+        end
+    })
+    ---------------------------------------------------
+
+    -------------------- 新国标报税口 ------------------
+    airui.label({
+        parent = scroll_container,
+        text = "新国标报税口",
+        x = 320,
+        y = 17,
+        w = 95,
+        h = 40,
+        font_size = 15,
+    })
+
+    local new_tax_num_input = airui.textarea({
+        parent = scroll_container,
+        x = 415,
+        y = 7,
+        w = 55,
+        h = 35,
+        text = "1",
+        -- placeholder = "...",
+        max_len = 1,
+        keyboard = keyboard1
+    })
+    ---------------------------------------------------
+
+    ----------------- 查询税控序列号区域 ---------------
+    local tax_serial_container = airui.container({
+        parent = scroll_container,
+        x = 10,
+        y = 50,
+        w = 160,
+        h = 210,
+        color = 0xFFFFFF,
+        radius = 5,
+    })
+
+    local tax_serial_btn = airui.button({
+        parent = tax_serial_container,
+        x = 20,
+        y = 12,
+        w = 120,
+        h = 35,
+        text = "查询税控序列号",
+        style = { bg_color = 0x2B6FF1,border_color = 0x2B6FF1, text_color = 0xFFFFFF, radius = 8 },
+        on_click = function(self)
+            log.info("button", "基本按钮被点击")
+        end
+    })
+
+    -- 查询结果显示
+    airui.label({
+        parent = tax_serial_container,
+        text = "税控序列号:",
+        x = 10,
+        y = 60,
+        w = 120,
+        h = 20,
+        font_size = 14,
+    })
+
+    local label_tax_serial = airui.label({
+        parent = tax_serial_container,
+        text = "0123456789",
+        x = 10,
+        y = 80,
+        w = 150,
+        h = 20,
+        font_size = 14,
+    })
+
+    airui.label({
+        parent = tax_serial_container,
+        text = "厂家:",
+        x = 10,
+        y = 110,
+        w = 40,
+        h = 20,
+        font_size = 14,
+    })
+
+    local label_company = airui.label({
+        parent = tax_serial_container,
+        text = "拓盛",
+        x = 50,
+        y = 110,
+        w = 90,
+        h = 20,
+        font_size = 14,
+    })
+
+    airui.label({
+        parent = tax_serial_container,
+        text = "枪个数:",
+        x = 10,
+        y = 150,
+        w = 60,
+        h = 20,
+        font_size = 14,
+    })
+
+    local label_gunnum = airui.label({
+        parent = tax_serial_container,
+        text = "2",
+        x = 70,
+        y = 150,
+        w = 80,
+        h = 20,
+        font_size = 14,
+    })
+
+    airui.label({
+        parent = tax_serial_container,
+        text = "执行结果:",
+        x = 10,
+        y = 190,
+        w = 70,
+        h = 20,
+        font_size = 14,
+    })
+
+    local label_tax_serial_result = airui.label({
+        parent = tax_serial_container,
+        text = "执行成功",
+        x = 80,
+        y = 190,
+        w = 80,
+        h = 20,
+        font_size = 14,
+    })
+    ---------------------------------------------------
+
+    ----------------- 日累月累总累 ------------------
+    local tax_msg_container = airui.container({
+        parent = scroll_container,
+        x = 180,
+        y = 50,
+        w = 290,
+        h = 210,
+        color = 0xFFFFFF,
+        radius = 5,
+    })
+
+    local year_input = airui.textarea({
+        parent = tax_msg_container,
+        x = 10,
+        y = 7,
+        w = 70,
+        h = 35,
+        text = os.date('%Y'),
+        -- placeholder = "...",
+        max_len = 4,
+        keyboard = keyboard1
+    })
+
+    airui.label({
+        parent = tax_msg_container,
+        text = "年",
+        x = 85,
+        y = 18,
+        w = 20,
+        h = 20,
+        font_size = 15,
+    })
+
+    local month_dropdown = airui.dropdown({
+        parent = tax_msg_container,
+        x = 105,
+        y = 7,
+        w = 65,
+        h = 35,
+        options = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"},
+        default_index = 0,
+        on_change = function(self,idx)
+            -- local texts = {"选项1", "选项2", "选项3", "选项4", "选项5"}
+            -- selected_label1:set_text("当前选中: " .. texts[idx + 1])
+        end
+    })
+
+    airui.label({
+        parent = tax_msg_container,
+        text = "月",
+        x = 175,
+        y = 18,
+        w = 20,
+        h = 20,
+        font_size = 15,
+    })
+
+    local day_dropdown = airui.dropdown({
+        parent = tax_msg_container,
+        x = 195,
+        y = 7,
+        w = 65,
+        h = 35,
+        options = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13","14","15","16","17","18","19","20","21","22","23","24","25","26","27","28","29","30","31"},
+        default_index = 0,
+        on_change = function(self,idx)
+            -- local texts = {"选项1", "选项2", "选项3", "选项4", "选项5"}
+            -- selected_label1:set_text("当前选中: " .. texts[idx + 1])
+        end
+    })
+
+    airui.label({
+        parent = tax_msg_container,
+        text = "日",
+        x = 265,
+        y = 18,
+        w = 20,
+        h = 20,
+        font_size = 15,
+    })
+
+    
+
+    local check_total_btn = airui.button({
+        parent = tax_msg_container,
+        x = 10,
+        y = 50,
+        w = 120,
+        h = 35,
+        text = "查当次及总累",
+        style = { bg_color = 0x2B6FF1,border_color = 0x2B6FF1, text_color = 0xFFFFFF, radius = 8 },
+        on_click = function(self)
+            log.info("button", "基本按钮被点击")
+        end
+    })
+
+    local check_day_btn = airui.button({
+        parent = tax_msg_container,
+        x = 140,
+        y = 50,
+        w = 60,
+        h = 35,
+        text = "查日累",
+        style = { bg_color = 0x2B6FF1,border_color = 0x2B6FF1, text_color = 0xFFFFFF, radius = 8 },
+        on_click = function(self)
+            log.info("button", "基本按钮被点击")
+        end
+    })
+
+    local check_month_btn = airui.button({
+        parent = tax_msg_container,
+        x = 210,
+        y = 50,
+        w = 60,
+        h = 35,
+        text = "查月累",
+        style = { bg_color = 0x2B6FF1,border_color = 0x2B6FF1, text_color = 0xFFFFFF, radius = 8 },
+        on_click = function(self)
+            log.info("button", "基本按钮被点击")
+        end
+    })
+
+
+    airui.label({
+        parent = tax_msg_container,
+        text = "金额:",
+        x = 10,
+        y = 100,
+        w = 50,
+        h = 20,
+        font_size = 14,
+    })
+
+    local label_money = airui.label({
+        parent = tax_msg_container,
+        text = "9999.99",
+        x = 50,
+        y = 100,
+        w = 70,
+        h = 20,
+        font_size = 14,
+    })
+
+    airui.label({
+        parent = tax_msg_container,
+        text = "油量:",
+        x = 10,
+        y = 120,
+        w = 50,
+        h = 20,
+        font_size = 14,
+    })
+
+    local label_oil = airui.label({
+        parent = tax_msg_container,
+        text = "9999.99",
+        x = 50,
+        y = 120,
+        w = 70,
+        h = 20,
+        font_size = 14,
+    })
+
+    airui.label({
+        parent = tax_msg_container,
+        text = "单价:",
+        x = 10,
+        y = 140,
+        w = 50,
+        h = 20,
+        font_size = 14,
+    })
+
+    local label_price = airui.label({
+        parent = tax_msg_container,
+        text = "99.99",
+        x = 50,
+        y = 140,
+        w = 50,
+        h = 20,
+        font_size = 14,
+    })
+
+    airui.label({
+        parent = tax_msg_container,
+        text = "是否密文:",
+        x = 10,
+        y = 160,
+        w = 70,
+        h = 20,
+        font_size = 14,
+    })
+
+    local label_encryption = airui.label({
+        parent = tax_msg_container,
+        text = "是",
+        x = 80,
+        y = 160,
+        w = 30,
+        h = 20,
+        font_size = 14,
+    })
+
+    airui.label({
+        parent = tax_msg_container,
+        text = "总金额",
+        x = 185,
+        y = 100,
+        w = 70,
+        h = 20,
+        font_size = 14,
+    })
+
+    local label_total_money = airui.label({
+        parent = tax_msg_container,
+        text = "1234567891234.99",
+        x = 140,
+        y = 120,
+        w = 150,
+        h = 30,
+        font_size = 14,
+    })
+
+    airui.label({
+        parent = tax_msg_container,
+        text = "总油量",
+        x = 185,
+        y = 140,
+        w = 70,
+        h = 20,
+        font_size = 14,
+    })
+
+    local label_total_oil = airui.label({
+        parent = tax_msg_container,
+        text = "1234567891234.99",
+        x = 140,
+        y = 160,
+        w = 150,
+        h = 30,
+        font_size = 14,
+    })
+
+    airui.label({
+        parent = tax_msg_container,
+        text = "执行结果:",
+        x = 10,
+        y = 190,
+        w = 70,
+        h = 20,
+        font_size = 14,
+    })
+
+    local label_check_result = airui.label({
+        parent = tax_msg_container,
+        text = "执行成功",
+        x = 80,
+        y = 190,
+        w = 120,
+        h = 20,
+        font_size = 14,
+    })
+
+    -- 底部信息栏
+    common_ui.create_status_bar(main_container)
+end
+
+-- 初始化页面
+function tsb_bsk_page.init(params)
+    tsb_bsk_page.create_ui()
+end
+
+-- 清理页面
+function tsb_bsk_page.cleanup()
+    if main_container then
+        main_container:destroy()
+        main_container = nil
+    end
+end
+
+return tsb_bsk_page

+ 285 - 0
tsb_ui/tsb_channel_page.lua

@@ -0,0 +1,285 @@
+--[[
+@module  tsb_channel_page
+@summary 信道切换页面
+@version 1.0
+@date    2026.03.05
+@author  李一玮
+@usage
+本文件是信道切换页面,展示信道切换的各种用法。
+]]
+
+local tsb_channel_page = {}
+
+-- 页面UI元素
+local main_container = nil
+local common_ui = require("tsb_common_page")
+
+-- 创建UI
+function tsb_channel_page.create_ui()
+    main_container = airui.container({
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 320,
+        color = 0xF5F5F5,
+        color_opacity = 0
+    })
+
+    --------------------- 标题栏 ------------------------
+    local title_bar = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 30,
+        color = 0x7E57C2,
+    })
+
+    airui.label({
+        parent = title_bar,
+        text = "信道切换",
+        x = 190,
+        y = 8,
+        w = 100,
+        h = 20,
+        font_size = 16,
+        color = 0xFFFFFF,
+    })
+
+    
+    -- 标题栏公共信息展示
+    common_ui.add_battery_display(title_bar)
+    common_ui.create_back_button(title_bar, tsb_channel_page.cleanup)
+    ---------------------------------------------------
+
+    -- 滚动容器
+    local scroll_container = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 30,
+        w = 480,
+        h = 270,
+        color = 0xFFFFFF,
+        
+    })
+
+    local keyboard1 = airui.keyboard({
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 160,                        -- x, y, 键盘默认打开ALIGN_BOTTOM_MID,位置从中下方开始计算
+        mode = "numeric",               -- 键盘模式,可选 "text"/"upper"/"lower"/"numeric"
+        auto_hide = true,               -- 自动隐藏键盘
+        preview = true,          
+        preview_height = 35,
+        bg_color = 0xf1f1f1,            -- 键盘背景颜色为灰色,可选,不设置则透明
+        on_commit = function()          -- 确认事件回调,只有在按下确认键时才会触发
+            log.info("keyboard", "commit")
+        end
+    })
+
+    local keyboard2 = airui.keyboard({
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 160,                        -- x, y, 键盘默认打开ALIGN_BOTTOM_MID,位置从中下方开始计算
+        mode = "lower",               -- 键盘模式,可选 "text"/"upper"/"lower"/"numeric"
+        auto_hide = true,               -- 自动隐藏键盘
+        preview = true,          
+        preview_height = 35,
+        bg_color = 0xf1f1f1,            -- 键盘背景颜色为灰色,可选,不设置则透明
+        on_commit = function()          -- 确认事件回调,只有在按下确认键时才会触发
+            log.info("keyboard", "commit")
+        end
+    })
+
+    --------------------- 第一行控制区 ------------------------
+    -- 当前lora信道标签
+    airui.label({
+        parent = scroll_container,
+        text = "当前调试宝信道:",
+        x = 10,
+        y = 15,
+        w = 120,
+        h = 30,
+        font_size = 15,
+    })
+
+    -- 当前lora信道值
+    local current_channel_label = airui.label({
+        parent = scroll_container,
+        text = "1",
+        x = 135,
+        y = 15,
+        w = 30,
+        h = 30,
+        font_size = 15,
+    })
+
+    -- 设置lora信道标签
+    airui.label({
+        parent = scroll_container,
+        text = "设置调试宝信道:",
+        x = 180,
+        y = 15,
+        w = 130,
+        h = 30,
+        font_size = 15,
+    })
+
+    -- 设置lora信道下拉框
+    local channel_dropdown = airui.dropdown({
+        parent = scroll_container,
+        x = 310,
+        y = 8,
+        w = 70,
+        h = 30,
+        options = {"1", "2", "3", "4", "5", "6"},
+        default_index = 0, -- 默认选择1
+        on_change = function(self,idx)
+            log.info("mqtt_channel", "选择了信道:" .. self.options[idx + 1])
+        end
+    })
+
+
+    -- 目标设备类型标签
+    airui.label({
+        parent = scroll_container,
+        text = "目标设备类型:",
+        x = 10,
+        y = 60,
+        w = 100,
+        h = 30,
+        font_size = 15,
+    })
+
+    -- 目标设备类型下拉框
+    local device_type_dropdown = airui.dropdown({
+        parent = scroll_container,
+        x = 120,
+        y = 50,
+        w = 100,
+        h = 35,
+        options = {"路由器", "连接器"},
+        default_index = 0, -- 默认选择路由器
+        on_change = function(self,idx)
+            log.info("device_type", "选择了设备类型:" .. self.options[idx + 1])
+        end
+    })
+
+    airui.label({
+        parent = scroll_container,
+        text = "目标设备SN:",
+        x = 10,
+        y = 110,
+        w = 100,
+        h = 30,
+        font_size = 15,
+    })
+
+    -- 连接器SN输入框
+    local sn_input = airui.textarea({
+        parent = scroll_container,
+        x = 120,
+        y = 100,
+        w = 100,
+        h = 35,
+        text = "",
+        max_length = 5,
+        keyboard = keyboard1
+    })
+
+
+    airui.label({
+        parent = scroll_container,
+        text = "切换到信道:",
+        x = 10,
+        y = 160,
+        w = 100,
+        h = 20,
+        font_size = 15,
+    })
+
+    local channel1_dropdown = airui.dropdown({
+        parent = scroll_container,
+        x = 120,
+        y = 150,
+        w = 70,
+        h = 35,
+        options = {"1", "2", "3", "4", "5", "6"},
+        default_index = 0, -- 默认选择1
+        on_change = function(self,idx)
+            log.info("mqtt_channel", "选择了信道:" .. self.options[idx + 1])
+        end
+    })
+    ---------------------------------------------------
+
+
+    -- 新配置下发按钮
+    local new_config_btn = airui.button({
+        parent = scroll_container,
+        x = 30,
+        y = 200,
+        w = 100,
+        h = 35,
+        text = "指令下发",
+        --style = { bg_color = 0x2B6FF1,border_color = 0x2B6FF1, text_color = 0xFFFFFF, radius = 8 },
+        on_click = function(self)
+            log.info("mqtt_config", "新配置下发按钮被点击")
+        end
+    })
+
+    -- 恢复默认配置按钮
+    -- local default_config_btn = airui.button({
+    --     parent = scroll_container,
+    --     x = 120,
+    --     y = 230,
+    --     w = 120,
+    --     h = 35,
+    --     text = "恢复默认配置",
+    --     on_click = function(self)
+    --         log.info("mqtt_config", "恢复默认配置按钮被点击")
+    --     end
+    -- })
+
+    -- 执行结果
+    airui.label({
+        parent = scroll_container,
+        text = "执行结果:",
+        x = 270,
+        y = 210,
+        w = 80,
+        h = 30,
+        font_size = 16,
+    })
+
+    -- 执行结果值
+    local result_label = airui.label({
+        parent = scroll_container,
+        text = "成功",
+        x = 350,
+        y = 210,    
+        w = 100,
+        h = 30,
+        font_size = 16,
+    })
+    ---------------------------------------------------
+
+    -- 底部信息
+    common_ui.create_status_bar(main_container)
+end
+
+-- 初始化页面
+function tsb_channel_page.init(params)
+    tsb_channel_page.create_ui()
+end
+
+-- 清理页面
+function tsb_channel_page.cleanup()
+    if main_container then
+        main_container:destroy()
+        main_container = nil
+    end
+end
+
+return tsb_channel_page

+ 377 - 0
tsb_ui/tsb_common_page.lua

@@ -0,0 +1,377 @@
+--[[
+@module  common_ui
+@summary 公共UI组件
+@version 1.0
+@date    2026.03.05
+@author  李一玮
+@usage
+本文件提供公共UI组件功能,如电量显示等。
+]]
+
+local common_ui = {}
+local time1_id = nil
+local show_charge_info = false  -- 控制显示哪个文本
+local switch_counter = 0  -- 计数器,用于控制文本切换频率
+local shutdown_msgbox = nil  -- 跟踪关机弹窗状态
+local bq25895_iic_id = 1
+local bq25895 = nil
+local bq25895_addr = 0x6A   -- 充电芯片I2C地址
+--local bq25895_reg2 = 0x02   -- 控制ADC检测开关
+local bq25895_reg9 = 0x09   -- bit5位可以控制VSYS到VBAT的通路开关(默认0导通,1关闭)
+
+-- 在现有标题栏添加电量显示
+-- @param title_bar 标题栏容器对象
+-- @return battery_label 电量标签对象,用于后续更新
+function common_ui.add_battery_display(parent)
+    -- 电量图标
+    airui.image({
+        parent = parent,
+        x = 410,
+        y = 5,
+        w = 40,
+        h = 20,
+        src = "/luadb/dian3.png",
+        zoom = 200,
+    })
+
+    -- 电量值标签
+    local battery_label = airui.label({
+        parent = parent,
+        text = "70%",
+        x = 445,
+        y = 8,
+        w = 35,
+        h = 20,
+        font_size = 14,
+        color = 0xFFFFFF,
+    })
+
+    -- 信号图标
+    airui.image({
+        parent = parent,
+        x = 355,
+        y = 5,
+        w = 40,
+        h = 20,
+        src = "/luadb/4g3.png",
+        zoom = 160,
+    })
+
+    -- 信号值标签
+    local battery_label = airui.label({
+        parent = parent,
+        text = "4G",
+        x = 390,
+        y = 8,
+        w = 45,
+        h = 20,
+        font_size = 14,
+        color = 0xFFFFFF,
+    })
+
+    -- WIFI图标
+    airui.image({
+        parent = parent,
+        x = 330,
+        y = 5,
+        w = 40,
+        h = 20,
+        src = "/luadb/wifi3.png",
+        zoom = 150,
+    })
+
+    return battery_label
+end
+
+function common_ui.create_back_button(parent, cleanup_func)
+    -- local back_btn = airui.button({
+    --     parent = parent,
+    --     x = 10,
+    --     y = 3,
+    --     w = 45,
+    --     h = 25,
+    --     text = "返回",
+    --     on_click = function()
+    --         -- 调用当前页面的清理函数
+    --         if cleanup_func then
+    --             cleanup_func()
+    --         end
+    --         -- 返回上一页
+    --         go_back()
+    --     end
+    -- })
+
+    local back_img = airui.image({
+        parent = parent,
+        x = 5,
+        y = 4,
+        w = 20,
+        h = 25,                    
+        src = "/luadb/back1.png",
+        zoom = 256,
+        opacity = 255,
+        on_click = function()
+            -- 调用当前页面的清理函数
+            if cleanup_func then
+                cleanup_func()
+            end
+            -- 返回上一页
+            go_back()
+        end
+    })
+
+    local back_label = airui.label({
+        parent = parent,
+        text = "返回",
+        x = 22,
+        y = 9,
+        w = 45,
+        h = 20,
+        font_size = 15,
+        color = 0xFFFFFF,
+        on_click = function()
+            -- 调用当前页面的清理函数
+            if cleanup_func then
+                cleanup_func()
+            end
+            -- 返回上一页
+            go_back()
+        end
+    })
+
+    -- local home_btn = airui.button({
+    --     parent = parent,
+    --     x = 60,
+    --     y = 3,
+    --     w = 45,
+    --     h = 25,
+    --     text = "首页",
+    --     on_click = function()
+    --         if cleanup_func then
+    --             cleanup_func()
+    --         end
+    --         go_home()
+    --     end
+    -- })
+
+    local home_img = airui.image({
+        parent = parent,
+        x = 65,
+        y = 4,
+        w = 20,
+        h = 25,                    
+        src = "/luadb/home2.png",
+        zoom = 140,
+        opacity = 255,
+        on_click = function()
+            if cleanup_func then
+                cleanup_func()
+            end
+            go_home()
+        end
+    })
+
+    local home_label = airui.label({
+        parent = parent,
+        text = "首页",
+        x = 87,
+        y = 9,
+        w = 45,
+        h = 20,
+        font_size = 15,
+        color = 0xFFFFFF,
+        on_click = function()
+            if cleanup_func then
+                cleanup_func()
+            end
+            go_home()
+        end
+    })
+
+
+    return back_img
+
+    
+end
+
+function common_ui.add_background_png(parent)
+    local background_img = airui.image({
+        parent = parent,
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 270,                    
+        src = "/luadb/1.png",
+        zoom = 256,
+        opacity = 255
+    })
+
+    return background_img
+end
+
+-- 创建底部信息栏
+-- @param main_container 主容器对象
+-- @return status_bar 底部信息栏容器对象
+function common_ui.create_status_bar(parent)
+    -- 底部信息
+    local status_bar = airui.container({
+        parent = parent,
+        x = 0,
+        y = 300,
+        w = 480,
+        h = 20,
+        color = 0xDFEAFF,
+    })
+
+    local version_charge_msg = airui.label({
+        parent = status_bar,
+        text = "TSB-3.0 v1.0.0",
+        x = 0,
+        y = 0,
+        w = 180,
+        h = 20,
+        font_size = 14,
+    })
+
+    airui.label({
+        parent = status_bar,
+        text = "SN:",
+        x = 350,
+        y = 0,
+        w = 40,
+        h = 18,
+        font_size = 14,
+    })
+
+    local sn_label = airui.label({
+        parent = status_bar,
+        text = "55",
+        x = 380,
+        y = 0,
+        w = 40,
+        h = 18,
+        font_size = 14,
+    })
+
+    
+
+    local shutdown_image = airui.image({
+        parent = status_bar,
+        x = 422,
+        y = 0,
+        w = 30,
+        h = 20,                    
+        src = "/luadb/shutdown.png",
+        zoom = 200,
+        opacity = 255,
+        on_click = function(self)
+            common_ui.show_shutdown_confirm(status_bar)
+        end
+    })
+
+    local shutdwon_label = airui.label({
+        parent = status_bar,
+        text = "关机",
+        x = 445,
+        y = 2,
+        w = 30,
+        h = 18,
+        font_size = 14,
+        on_click = function(self)
+            common_ui.show_shutdown_confirm(status_bar)
+        end
+    })
+
+    local dynamic_label = airui.label({
+        parent = status_bar,
+        text = os.date("%Y-%m-%d %H:%M:%S"),
+        x = 170,
+        y = 0,
+        w = 180,
+        h = 20,
+        font_size = 14,
+    })
+
+    -- 先停止已存在的定时器,避免多个定时器同时运行
+    if time1_id then
+        sys.timerStop(time1_id)
+        time1_id = nil
+    end
+
+    -- 启动新的定时器
+    time1_id = sys.timerLoopStart(function()
+        local current_time = os.date("%Y-%m-%d %H:%M:%S")
+        -- 检查dynamic_label是否仍然存在
+        if dynamic_label then
+            dynamic_label:set_text(current_time)
+        else
+            -- 如果标签不存在,停止定时器
+            if time1_id then
+                sys.timerStop(time1_id)
+                time1_id = nil
+            end
+        end
+        
+        -- 每两次定时切换一次文本(即1800ms)
+        if version_charge_msg then
+            switch_counter = switch_counter + 1
+            if switch_counter >= 2 then
+                show_charge_info = not show_charge_info  -- 切换状态
+                if show_charge_info then
+                    version_charge_msg:set_text("充电中。。充电功率:7.5W")
+                else
+                    version_charge_msg:set_text("TSB-3.0 v1.0.0")
+                end
+                switch_counter = 0  -- 重置计数器
+            end
+        end
+    end, 900)  -- 每900ms更新一次时间
+    
+    return status_bar
+end
+
+-- 显示关机确认弹窗
+-- @param parent 父容器对象
+function common_ui.show_shutdown_confirm(parent)
+    -- 检查是否已经存在关机弹窗
+    if shutdown_msgbox then
+        log.info("shutdown", "关机弹窗已存在,不重复创建")
+        return
+    end
+    
+    shutdown_msgbox = airui.msgbox({
+        title = "系统提示",
+        text = "是否确认关机?",
+        buttons = { "否", "是" },
+        on_action = function(self, label)
+            if label == "是" then
+                -- 执行关机操作
+                log.info("shutdown", "用户确认关机")
+                i2c.setup(bq25895_iic_id,i2c.SLOW)
+
+                local reg9_data = i2c.readReg(bq25895_iic_id, bq25895_addr, bq25895_reg9, 1)
+                local reg9_temp = (reg9_data == "") and 0x00 or string.byte(reg9_data)
+                local reg9_data_result = reg9_temp | 0x20
+                local reg9_result = i2c.writeReg(bq25895_iic_id, bq25895_addr, bq25895_reg9, string.char(reg9_data_result))
+                pm.shutdown()
+            else
+                -- 取消关机操作
+                log.info("shutdown", "用户取消关机")
+            end
+            self:hide()
+            -- 重置弹窗状态
+            shutdown_msgbox = nil
+        end
+    })
+    shutdown_msgbox:show()
+end
+
+-- 清理函数,用于停止定时器
+function common_ui.cleanup()
+    if time1_id then
+        sys.timerStop(time1_id)
+        time1_id = nil
+    end
+end
+
+return common_ui

+ 214 - 0
tsb_ui/tsb_devinfo_page.lua

@@ -0,0 +1,214 @@
+--[[
+@module  tsb_devinfo_page
+@summary 设备信息页面
+@version 1.0
+@date    2026.03.04
+@author  李一玮
+@usage
+本文件是设备信息页面,展示设备的各种信息。
+]]
+
+local tsb_devinfo_page = {}
+
+-- 页面UI元素
+local main_container = nil
+local common_ui = require("tsb_common_page")
+
+-- 创建UI
+function tsb_devinfo_page.create_ui()
+    main_container = airui.container({
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 320,
+        color = 0xF5F5F5,
+    })
+
+    --------------------- 标题栏 ------------------------
+    local title_bar = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 30,
+        color = 0xB3E5FC,
+    })
+
+    airui.label({
+        parent = title_bar,
+        text = "本机信息",
+        x = 200,
+        y = 8,
+        w = 80,
+        h = 20,
+        font_size = 16,
+        color = 0xFFFFFF,
+    })
+
+    -- 标题栏公共信息展示
+    common_ui.add_battery_display(title_bar)
+    common_ui.create_back_button(title_bar, tsb_devinfo_page.cleanup)
+    ---------------------------------------------------
+
+    -- 滚动容器
+    local scroll_container = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 30,
+        w = 480,
+        h = 270,
+        color = 0xFFFFFF,
+    })
+
+    --------------------- 设备信息展示 ------------------------
+    -- 程序版本
+    airui.label({
+        parent = scroll_container,
+        text = "程序版本:",
+        x = 20,
+        y = 20,
+        w = 100,
+        h = 30,
+        font_size = 16,
+    })
+
+    local version_label = airui.label({
+        parent = scroll_container,
+        text = "V1.0.0",
+        x = 120,
+        y = 20,
+        w = 200,
+        h = 30,
+        font_size = 16,
+        color = 0x666666,
+    })
+
+    -- 运行时间
+    airui.label({
+        parent = scroll_container,
+        text = "运行时间:",
+        x = 20,
+        y = 60,
+        w = 100,
+        h = 30,
+        font_size = 16,
+    })
+
+    local runtime_label = airui.label({
+        parent = scroll_container,
+        text = "00:00:00",
+        x = 120,
+        y = 60,
+        w = 200,
+        h = 30,
+        font_size = 16,
+        color = 0x666666,
+    })
+
+    -- 设备复位次数
+    airui.label({
+        parent = scroll_container,
+        text = "设备复位次数:",
+        x = 20,
+        y = 100,
+        w = 120,
+        h = 30,
+        font_size = 16,
+    })
+
+    local reset_count_label = airui.label({
+        parent = scroll_container,
+        text = "0",
+        x = 140,
+        y = 100,
+        w = 200,
+        h = 30,
+        font_size = 16,
+        color = 0x666666,
+    })
+
+    -- 设备SN
+    airui.label({
+        parent = scroll_container,
+        text = "设备SN:",
+        x = 20,
+        y = 140,
+        w = 100,
+        h = 30,
+        font_size = 16,
+    })
+
+    local device_sn_label = airui.label({
+        parent = scroll_container,
+        text = "SN001",
+        x = 120,
+        y = 140,
+        w = 200,
+        h = 30,
+        font_size = 16,
+        color = 0x666666,
+    })
+
+    -- IMEI
+    airui.label({
+        parent = scroll_container,
+        text = "IMEI:",
+        x = 20,
+        y = 180,
+        w = 100,
+        h = 30,
+        font_size = 16,
+    })
+
+    local imei_label = airui.label({
+        parent = scroll_container,
+        text = "123456789012345",
+        x = 120,
+        y = 180,
+        w = 300,
+        h = 30,
+        font_size = 16,
+        color = 0x666666,
+    })
+
+    -- ICCID
+    airui.label({
+        parent = scroll_container,
+        text = "ICCID:",
+        x = 20,
+        y = 220,
+        w = 100,
+        h = 30,
+        font_size = 16,
+    })
+
+    local iccid_label = airui.label({
+        parent = scroll_container,
+        text = "8986012345678901234",
+        x = 120,
+        y = 220,
+        w = 300,
+        h = 30,
+        font_size = 16,
+        color = 0x666666,
+    })
+    ---------------------------------------------------
+
+    -- 底部信息
+    common_ui.create_status_bar(main_container)
+end
+
+-- 初始化页面
+function tsb_devinfo_page.init(params)
+    tsb_devinfo_page.create_ui()
+end
+
+-- 清理页面
+function tsb_devinfo_page.cleanup()
+    if main_container then
+        main_container:destroy()
+        main_container = nil
+    end
+end
+
+return tsb_devinfo_page

+ 143 - 0
tsb_ui/tsb_devlog_page.lua

@@ -0,0 +1,143 @@
+--[[
+@module  tsb_devlog_page
+@summary 设备日志页面
+@version 1.0
+@date    2026.03.04
+@author  李一玮
+@usage
+本文件是设备日志页面,展示设备的日志信息。
+]]
+
+local tsb_devlog_page = {}
+
+-- 页面UI元素
+local main_container = nil
+local common_ui = require("tsb_common_page")
+
+-- 创建UI
+function tsb_devlog_page.create_ui()
+    main_container = airui.container({
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 320,
+        color = 0xF5F5F5,
+    })
+
+    --------------------- 标题栏 ------------------------
+    local title_bar = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 30,
+        color = 0x82B1FF,
+    })
+
+    airui.label({
+        parent = title_bar,
+        text = "日志监测界面",
+        x = 190,
+        y = 8,
+        w = 200,
+        h = 20,
+        font_size = 16,
+        color = 0xFFFFFF,
+    })
+
+    -- 标题栏公共信息展示
+    common_ui.add_battery_display(title_bar)
+    common_ui.create_back_button(title_bar, tsb_devlog_page.cleanup)
+    ---------------------------------------------------
+
+    -- 滚动容器
+    local scroll_container = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 30,
+        w = 480,
+        h = 270,
+        color = 0xFFFFFF,
+    })
+
+    --------------------- 设备日志显示 ------------------------
+    local log_container = airui.container({
+        parent = scroll_container,
+        x = 0,
+        y = 50,
+        w = 480,
+        h = 215,
+        color = 0xFFFFFF,
+    })
+
+    -- 日志内容显示
+    local log_content = airui.textarea({
+        parent = log_container,
+        x = 5,
+        y = 5,
+        w = 470,
+        h = 205,
+        max_len = 10000,
+        text = "[2024-07-02 09:30:46] app start\n" ..
+               "[2024-07-02 09:30:47] init done\n" ..
+               "[2024-07-02 09:30:48] connect success\n" ..
+               "[2024-07-02 09:30:50] start monitor\n" ..
+               "[2024-07-02 09:31:00] detect device 1\n" ..
+               "[2024-07-02 09:31:10] detect device 2\n" ..
+               "[2024-07-02 09:31:20] upload success\n" ..
+               "[2024-07-02 09:31:30] system running\n" ..
+               "[2024-07-02 09:31:30] system status: normal\n",
+    })
+
+    --------------------- 控制按钮 ------------------------
+    -- 开始检测按钮
+    local start_btn = airui.button({
+        parent = scroll_container,
+        x = 10,
+        y = 10,
+        w = 100,
+        h = 35,
+        text = "开始检测",
+        style = { bg_color = 0x2B6FF1,border_color = 0x2B6FF1, text_color = 0xFFFFFF, radius = 8 },
+        on_click = function(self)
+            log.info("devlog", "开始检测按钮被点击")
+        end
+    })
+
+    -- 清空按钮
+    local clear_btn = airui.button({
+        parent = scroll_container,
+        x = 120,
+        y = 10,
+        w = 80,
+        h = 35,
+        text = "清空",
+        on_click = function(self)
+            log.info("devlog", "清空按钮被点击")
+            -- 清空日志内容
+            log_content:set_text(" ")
+        end
+    })
+    ---------------------------------------------------
+
+    
+    ---------------------------------------------------
+
+    -- 底部信息
+    common_ui.create_status_bar(main_container)
+end
+
+-- 初始化页面
+function tsb_devlog_page.init(params)
+    tsb_devlog_page.create_ui()
+end
+
+-- 清理页面
+function tsb_devlog_page.cleanup()
+    if main_container then
+        main_container:destroy()
+        main_container = nil
+    end
+end
+
+return tsb_devlog_page

+ 335 - 0
tsb_ui/tsb_firm_page.lua

@@ -0,0 +1,335 @@
+--[[
+@module  tsb_firm_page
+@summary 固件下载页面
+@version 1.0
+@date    2026.03.05
+@author  李一玮
+@usage
+本文件是固件下载页面,展示固件下载的各种用法。
+]]
+
+local tsb_firm_page = {}
+
+-- 页面UI元素
+local main_container = nil
+local download_progress = 0
+local download_bar = nil
+local download_timer = nil
+local common_ui = require("tsb_common_page")
+
+
+-- 创建UI
+function tsb_firm_page.create_ui()
+    main_container = airui.container({
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 320,
+        color = 0xF5F5F5,
+    })
+
+    --------------------- 标题栏 ------------------------
+    local title_bar = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 30,
+        color = 0x2196F3,
+    })
+
+    airui.label({
+        parent = title_bar,
+        text = "固件包下载",
+        x = 190,
+        y = 8,
+        w = 120,
+        h = 20,
+        font_size = 16,
+        color = 0xFFFFFF,
+    })
+
+
+    -- 标题栏公共信息展示
+    common_ui.add_battery_display(title_bar)
+    common_ui.create_back_button(title_bar, tsb_firm_page.cleanup)
+    ---------------------------------------------------
+
+    -- 滚动容器
+    local scroll_container = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 30,
+        w = 480,
+        h = 270,
+        color = 0xFFFFFF,
+    })
+
+    common_ui.add_battery_display(scroll_container)
+
+    --------------------- 第一行控制区 ------------------------
+    -- 设备类型标签
+    airui.label({
+        parent = scroll_container,
+        text = "设备类型:",
+        x = 10,
+        y = 18,
+        w = 80,
+        h = 30,
+        font_size = 16,
+    })
+
+    -- 设备类型下拉框
+    local device_type_dropdown = airui.dropdown({
+        parent = scroll_container,
+        x = 90,
+        y = 10,
+        w = 100,
+        h = 30,
+        options = {"0101", "0102", "0201", "0202", "0301", "0302", "0103", "0104", "0902", "0904"},
+        default_index = 0,
+        on_change = function(self,idx)
+            log.info("firm_device_type", "选择了设备类型:" .. self.options[idx + 1])
+        end
+    })
+
+    -- 固件类型标签
+    airui.label({
+        parent = scroll_container,
+        text = "固件类型:",
+        x = 230,
+        y = 18,
+        w = 80,
+        h = 30,
+        font_size = 16,
+    })
+
+    -- 固件类型下拉框
+    local firmware_type_dropdown = airui.dropdown({
+        parent = scroll_container,
+        x = 310,
+        y = 10,
+        w = 100,
+        h = 30,
+        options = {"产测", "app", "boot"},
+        default_index = 1, -- 默认选择app
+        on_change = function(self,idx)
+            log.info("firm_type", "选择了固件类型:" .. self.options[idx + 1])
+        end
+    })
+    ---------------------------------------------------
+
+    --------------------- 第二行控制区 ------------------------
+    -- 先声明开关变量
+    local flash_package_switch
+    local upgrade_package_switch
+
+    -- 刷机包标签
+    airui.label({
+        parent = scroll_container,
+        text = "刷机包",
+        x = 10,
+        y = 57,
+        w = 60,
+        h = 30,
+        font_size = 16,
+    })
+
+    -- 刷机包开关
+    flash_package_switch = airui.switch({
+        parent = scroll_container,
+        x = 70,
+        y = 55,
+        w = 50,
+        h = 20,
+        checked = true,
+        on_change = function(self)
+            local checked = self:get_state()
+            log.info("刷机包开关", checked and "开启" or "关闭")
+            -- 互斥逻辑:如果刷机包开启,则关闭升级包
+            if checked and upgrade_package_switch then
+                upgrade_package_switch:set_state(false)
+            end
+        end
+    })
+
+    -- 升级包标签
+    airui.label({
+        parent = scroll_container,
+        text = "升级包",
+        x = 140,
+        y = 57,
+        w = 60,
+        h = 30,
+        font_size = 16,
+    })
+
+    -- 升级包开关
+    upgrade_package_switch = airui.switch({
+        parent = scroll_container,
+        x = 200,
+        y = 55,
+        w = 50,
+        h = 20,
+        checked = false,
+        on_change = function(self)
+            local checked = self:get_state()
+            log.info("升级包开关", checked and "开启" or "关闭")
+            -- 互斥逻辑:如果升级包开启,则关闭刷机包
+            if checked and flash_package_switch then
+                flash_package_switch:set_state(false)
+            end
+        end
+    })
+    ---------------------------------------------------
+
+    --------------------- 第三行控制区 ------------------------
+    -- 固件版本标签
+    airui.label({
+        parent = scroll_container,
+        text = "固件版本",
+        x = 10,
+        y = 97,
+        w = 80,
+        h = 30,
+        font_size = 16,
+    })
+
+    -- 固件版本下拉框
+    local firmware_version_dropdown = airui.dropdown({
+        parent = scroll_container,
+        x = 90,
+        y = 90,
+        w = 150,
+        h = 30,
+        options = {"V1.0.0", "V1.1.0", "V2.0.0"}, -- 示例版本,实际可根据需要修改
+        default_index = 0,
+        on_change = function(self,idx)
+            log.info("firm_version", "选择了固件版本:" .. self.options[idx + 1])
+        end
+    })
+    ---------------------------------------------------
+
+    --------------------- 操作按钮区 -----------------------
+    -- 下载进度条
+    download_bar = airui.bar({
+        parent = scroll_container,
+        x = 5,
+        y = 240,
+        w = 470,
+        h = 20,
+        value = 0,
+        min = 0,
+        max = 100,
+        radius = 5,
+        indicator_color = 0x2196F3,
+        show_progress_text = true,
+        progress_text_format = "%d%%",
+    })
+
+    -- 下载固件包按钮
+    local download_btn = airui.button({
+        parent = scroll_container,
+        x = 170,
+        y = 140,
+        w = 150,
+        h = 40,
+        text = "下载固件包",
+        style = { bg_color = 0x2B6FF1,border_color = 0x2B6FF1, text_color = 0xFFFFFF, radius = 8 },
+        on_click = function(self)
+            log.info("firm_download", "下载固件包按钮被点击")
+            -- 重置进度
+            download_bar:set_value(0, true)
+            
+            -- 开始下载动画
+            if download_timer then
+                sys.timerStop(download_timer)
+                download_timer = nil
+            end
+            
+            download_timer = sys.timerLoopStart(function()
+                local current = download_bar:get_value()
+                if current >= 100 then
+                    current = 0
+                else
+                    current = current + 5
+                end
+                download_bar:set_value(current, true) -- 启用动画
+                
+                if current >= 100 then
+                    sys.timerStop(download_timer)
+                    download_timer = nil
+                    -- 下载完成,显示消息框
+                    local msg = airui.msgbox({
+                        title = "系统提示",
+                        text = "下载完成",
+                        buttons = { "确定" },
+                        on_action = function(self, label)
+                            log.info("firm_download", "下载完成消息框被点击: " .. label)
+                            self:hide()
+                            download_bar:set_value(0, true)
+                        end
+                    })
+                    msg:show()
+                end
+            end, 200)
+        end
+    })
+
+    -- 去刷机按钮
+    local flash_btn = airui.button({
+        parent = scroll_container,
+        x = 30,
+        y = 190,
+        w = 150,
+        h = 40,
+        text = "去刷机",
+        on_click = function(self)
+            log.info("firm_flash_btn", "去刷机按钮被点击")
+            -- 跳转到刷机页面
+            --tsb_firm_page.cleanup()
+            show_page("tsb_reflash_page")
+        end
+    })
+
+    -- 去升级按钮
+    local upgrade_btn = airui.button({
+        parent = scroll_container,
+        x = 300,
+        y = 190,
+        w = 150,
+        h = 40,
+        text = "去升级",
+        on_click = function(self)
+            log.info("firm_upgrade_btn", "去升级按钮被点击")
+            -- 跳转到升级页面
+            --tsb_firm_page.cleanup()
+            show_page("tsb_upgrade_page")
+        end
+    })
+    ---------------------------------------------------
+
+    
+
+    -- 底部信息
+    common_ui.create_status_bar(main_container)
+end
+
+-- 初始化页面
+function tsb_firm_page.init(params)
+    tsb_firm_page.create_ui()
+end
+
+-- 清理页面
+function tsb_firm_page.cleanup()
+    if download_timer then
+        sys.timerStop(download_timer)
+        download_timer = nil
+    end
+    if main_container then
+        main_container:destroy()
+        main_container = nil
+    end
+end
+
+return tsb_firm_page

+ 511 - 0
tsb_ui/tsb_game_page.lua

@@ -0,0 +1,511 @@
+--[[
+@module  game_page
+@summary 俄罗斯方块游戏演示页面
+@version 1.0
+@date    2026.02.05
+@author  江访
+@usage
+本文件是俄罗斯方块游戏的演示页面。
+]]
+
+local game_page = {}
+
+-- 屏幕与棋盘参数
+local SCREEN_W, SCREEN_H = 480, 320 -- 横屏:480宽,320高
+local common_ui = require("tsb_common_page")
+local GRID_W, GRID_H = 15, 10   
+local CELL_SIZE = 20                -- 每格像素大小(20px)
+local BOARD_W = GRID_W * CELL_SIZE
+local BOARD_H = GRID_H * CELL_SIZE
+local BOARD_X = (SCREEN_W - BOARD_W) // 2 -- 水平居中
+local BOARD_Y = 100                        -- 棋盘左上角Y坐标
+
+-- 7 种俄罗斯方块颜色(RGB565格式)
+local COLORS = {
+    0x07FF, -- I (青色)
+    0x001F, -- J (蓝色)
+    0xFD20, -- L (橙色)
+    0xFFE0, -- O (黄色)
+    0x07E0, -- S (绿色)
+    0x8010, -- T (紫色)
+    0xF800, -- Z (红色)
+}
+
+-- 7 种形状(1 表示方块)
+local SHAPES = {
+    { { 1, 1, 1, 1 } },           -- I
+    { { 1, 0, 0 },   { 1, 1, 1 } }, -- J
+    { { 0, 0, 1 },   { 1, 1, 1 } }, -- L
+    { { 1, 1 },      { 1, 1 } },  -- O
+    { { 0, 1, 1 },   { 1, 1, 0 } }, -- S
+    { { 0, 1, 0 },   { 1, 1, 1 } }, -- T
+    { { 1, 1, 0 },   { 0, 1, 1 } }, -- Z
+}
+
+-- 游戏状态
+local grid     -- grid[y][x] = nil 或 颜色下标
+local curPiece -- {x, y, shape, color}
+local score = 0
+local gameOver = false
+local gameTimer = nil
+
+-- UI 控件
+local main_container
+local scoreLabel
+local statusLabel
+local leftBtn, rightBtn, rotateBtn, downBtn, restartBtn
+
+----------------------------------------------------------------
+-- 工具函数:网格与方块
+----------------------------------------------------------------
+local function initGrid()
+    grid = {}
+    for y = 1, GRID_H do
+        grid[y] = {}
+        for x = 1, GRID_W do
+            grid[y][x] = nil
+        end
+    end
+end
+
+local function rotateShape(shape)
+    local h, w = #shape, #shape[1]
+    local res = {}
+    for x = 1, w do
+        res[x] = {}
+        for y = 1, h do
+            res[x][y] = shape[h - y + 1][x]
+        end
+    end
+    return res
+end
+
+local function newPiece()
+    local idx = math.random(1, #SHAPES)
+    local shp = SHAPES[idx]
+    local pw  = #shp[1]
+    local px  = math.floor(GRID_W / 2) - math.floor(pw / 2) + 1
+    curPiece  = {
+        x = px,
+        y = 1,
+        shape = shp,
+        color = idx,
+    }
+end
+
+local function eachBlock(piece, cb)
+    local shape = piece.shape
+    for j = 1, #shape do
+        for i = 1, #shape[j] do
+            if shape[j][i] == 1 then
+                cb(piece.x + i - 1, piece.y + j - 1)
+            end
+        end
+    end
+end
+
+local function validPosition(piece)
+    local ok = true
+    eachBlock(piece, function(x, y)
+        if x < 1 or x > GRID_W or y < 1 or y > GRID_H then
+            ok = false
+            return
+        end
+        if grid[y][x] ~= nil then
+            ok = false
+            return
+        end
+    end)
+    return ok
+end
+
+local function mergePiece()
+    if not curPiece then return end
+    eachBlock(curPiece, function(x, y)
+        if y >= 1 and y <= GRID_H and x >= 1 and x <= GRID_W then
+            grid[y][x] = curPiece.color
+        end
+    end)
+end
+
+-- 返回本次消掉的行数
+local function clearLines()
+    local cleared = 0
+    for y = GRID_H, 1, -1 do
+        local full = true
+        for x = 1, GRID_W do
+            if grid[y][x] == nil then
+                full = false
+                break
+            end
+        end
+        if full then
+            cleared = cleared + 1
+            -- 下移
+            for yy = y, 2, -1 do
+                grid[yy] = grid[yy - 1]
+            end
+            local row = {}
+            for x = 1, GRID_W do row[x] = nil end
+            grid[1] = row
+            y = y + 1
+        end
+    end
+    if cleared > 0 then
+        score = score + cleared * 100
+    end
+    return cleared
+end
+
+----------------------------------------------------------------
+-- 绘制函数:使用lcd.fill绘制棋盘
+----------------------------------------------------------------
+local function drawCell(x, y, color)
+    local cx = BOARD_X + (x - 1) * CELL_SIZE
+    local cy = BOARD_Y + (y - 1) * CELL_SIZE
+    lcd.fill(cx, cy, cx + CELL_SIZE - 2, cy + CELL_SIZE - 2, color)
+end
+
+local function drawBoard()
+    -- 绘制棋盘背景
+    lcd.fill(BOARD_X, BOARD_Y, BOARD_X + BOARD_W - 1, BOARD_Y + BOARD_H - 1, 0x1082) -- 深灰色背景
+
+    -- 绘制固定的方块
+    for y = 1, GRID_H do
+        for x = 1, GRID_W do
+            local fixed = grid[y][x]
+            if fixed then
+                drawCell(x, y, COLORS[fixed])
+            else
+                -- 绘制空单元格(带边框效果)
+                local cx = BOARD_X + (x - 1) * CELL_SIZE
+                local cy = BOARD_Y + (y - 1) * CELL_SIZE
+                lcd.fill(cx, cy, cx + CELL_SIZE - 2, cy + CELL_SIZE - 2, 0x0841)         -- 更深的灰色
+                lcd.fill(cx + 1, cy + 1, cx + CELL_SIZE - 3, cy + CELL_SIZE - 3, 0x18C3) -- 浅灰色内框
+            end
+        end
+    end
+
+    -- 绘制当前方块
+    if curPiece then
+        eachBlock(curPiece, function(x, y)
+            if y >= 1 and y <= GRID_H and x >= 1 and x <= GRID_W then
+                drawCell(x, y, COLORS[curPiece.color])
+            end
+        end)
+    end
+
+    -- 绘制棋盘边框
+    lcd.fill(BOARD_X - 2, BOARD_Y - 2, BOARD_X + BOARD_W + 1, BOARD_Y - 1, 0xFFFF)                 -- 上边框
+    lcd.fill(BOARD_X - 2, BOARD_Y + BOARD_H, BOARD_X + BOARD_W + 1, BOARD_Y + BOARD_H + 1, 0xFFFF) -- 下边框
+    lcd.fill(BOARD_X - 2, BOARD_Y - 2, BOARD_X - 1, BOARD_Y + BOARD_H + 1, 0xFFFF)                 -- 左边框
+    lcd.fill(BOARD_X + BOARD_W, BOARD_Y - 2, BOARD_X + BOARD_W + 1, BOARD_Y + BOARD_H + 1, 0xFFFF) -- 右边框
+end
+
+----------------------------------------------------------------
+-- UI更新函数
+----------------------------------------------------------------
+local function updateScoreLabel()
+    if scoreLabel then
+        scoreLabel:set_text("分数: " .. tostring(score))
+    end
+end
+
+local function updateStatusLabel(extra)
+    if not statusLabel then return end
+    if gameOver then
+        statusLabel:set_text("游戏结束! 点击重新开始")
+    else
+        if extra and extra ~= "" then
+            statusLabel:set_text("俄罗斯方块 | " .. extra)
+        else
+            statusLabel:set_text("俄罗斯方块 | 使用按钮游玩")
+        end
+    end
+end
+
+local function redrawAll(extraStatus)
+    updateScoreLabel()
+    updateStatusLabel(extraStatus)
+    drawBoard()
+end
+
+----------------------------------------------------------------
+-- 控制逻辑
+----------------------------------------------------------------
+local function stepDown()
+    if gameOver or not curPiece then return end
+    local test = {
+        x = curPiece.x,
+        y = curPiece.y + 1,
+        shape = curPiece.shape,
+        color = curPiece.color,
+    }
+    if validPosition(test) then
+        curPiece = test
+        redrawAll()
+        return
+    end
+
+    -- 碰到底或碰到已固定方块
+    mergePiece()
+    local cleared = clearLines()
+    newPiece()
+    if not validPosition(curPiece) then
+        gameOver = true
+        redrawAll("游戏结束!")
+        if gameTimer then
+            sys.timerStop(gameTimer)
+            gameTimer = nil
+        end
+    else
+        if cleared > 0 then
+            redrawAll("消除了 " .. cleared .. " 行!")
+        else
+            redrawAll()
+        end
+    end
+end
+
+local function moveLeft()
+    if gameOver or not curPiece then return end
+    local test = {
+        x = curPiece.x - 1,
+        y = curPiece.y,
+        shape = curPiece.shape,
+        color = curPiece.color,
+    }
+    if validPosition(test) then
+        curPiece = test
+        redrawAll()
+    end
+end
+
+local function moveRight()
+    if gameOver or not curPiece then return end
+    local test = {
+        x = curPiece.x + 1,
+        y = curPiece.y,
+        shape = curPiece.shape,
+        color = curPiece.color,
+    }
+    if validPosition(test) then
+        curPiece = test
+        redrawAll()
+    end
+end
+
+local function softDrop()
+    if gameOver or not curPiece then return end
+    local test = {
+        x = curPiece.x,
+        y = curPiece.y + 1,
+        shape = curPiece.shape,
+        color = curPiece.color,
+    }
+    if validPosition(test) then
+        curPiece = test
+        redrawAll()
+    end
+end
+
+local function rotatePiece()
+    if gameOver or not curPiece then return end
+    local newShape = rotateShape(curPiece.shape)
+    local test = {
+        x = curPiece.x,
+        y = curPiece.y,
+        shape = newShape,
+        color = curPiece.color,
+    }
+    if validPosition(test) then
+        curPiece = test
+        redrawAll("旋转")
+    end
+end
+
+local function restartGame()
+    if gameTimer then
+        sys.timerStop(gameTimer)
+    end
+    
+    score = 0
+    gameOver = false
+    initGrid()
+    newPiece()
+    redrawAll("重新开始,分数: 0")
+    
+    -- 重新启动定时器
+    gameTimer = sys.timerLoopStart(stepDown, 400)
+end
+
+----------------------------------------------------------------
+-- 创建游戏UI
+----------------------------------------------------------------
+function game_page.create_ui()
+    -- 创建主容器
+    main_container = airui.container({
+        x = 0,
+        y = 0,
+        w = SCREEN_W,
+        h = SCREEN_H,
+        color = 0x0000, -- 黑色背景
+    })
+
+    -- 标题栏
+    local title_bar = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 0,
+        w = SCREEN_W,
+        h = 50,
+        color = 0x1E3A8A, -- 深蓝色
+    })
+
+    -- 游戏标题
+    airui.label({
+        parent = title_bar,
+        text = "俄罗斯方块",
+        x = 80,
+        y = 15,
+        w = 160,
+        h = 20,
+    })
+    
+    local battery_label = common_ui.add_battery_display(title_bar)
+    
+    -- 返回按钮
+    common_ui.create_back_button(title_bar, game_page.cleanup)
+
+    -- 分数标签
+    scoreLabel = airui.label({
+        parent = main_container,
+        text = "分数: 0",
+        x = 220,
+        y = 70,
+        w = 150,
+        h = 20,
+    })
+
+    -- 状态标签
+    statusLabel = airui.label({
+        parent = main_container,
+        text = "俄罗斯方块 | 使用按钮游玩",
+        x = 10,
+        y = 70,
+        w = SCREEN_W - 20,
+        h = 20,
+    })
+
+    -- 控制按钮区域
+    local btnY = BOARD_Y + BOARD_H + 10
+    local btnW, btnH, gap = 70, 30, 10
+
+    -- 计算按钮起始X坐标(居中显示)
+    local totalBtnWidth = btnW * 3 + gap * 2 -- 第一行3个按钮
+    local btnStartX = (SCREEN_W - totalBtnWidth) // 2
+
+    -- 第一行按钮
+    leftBtn = airui.button({
+        parent   = main_container,
+        x        = btnStartX,
+        y        = btnY,
+        w        = btnW,
+        h        = btnH,
+        text     = "左移",
+        on_click = function() moveLeft() end,
+    })
+
+    rightBtn = airui.button({
+        parent   = main_container,
+        x        = btnStartX + btnW + gap,
+        y        = btnY,
+        w        = btnW,
+        h        = btnH,
+        text     = "右移",
+        on_click = function() moveRight() end,
+    })
+
+    rotateBtn = airui.button({
+        parent   = main_container,
+        x        = btnStartX + 2 * (btnW + gap),
+        y        = btnY,
+        w        = btnW,
+        h        = btnH,
+        text     = "旋转",
+        on_click = function() rotatePiece() end,
+    })
+
+    -- 第二行按钮
+    btnY = btnY + btnH + gap
+
+    downBtn = airui.button({
+        parent   = main_container,
+        x        = btnStartX,
+        y        = btnY,
+        w        = btnW * 2 + gap, -- 稍宽一点
+        h        = btnH,
+        text     = "下落",
+        on_click = function() softDrop() end,
+    })
+
+    restartBtn = airui.button({
+        parent   = main_container,
+        x        = btnStartX + btnW * 2 + gap * 2,
+        y        = btnY,
+        w        = btnW,
+        h        = btnH,
+        text     = "重新开始",
+        on_click = function() restartGame() end,
+    })
+end
+
+----------------------------------------------------------------
+-- 页面生命周期函数
+----------------------------------------------------------------
+function game_page.init(params)
+    math.randomseed(os.time())
+    
+    -- 初始化游戏状态
+    initGrid()
+    newPiece()
+    
+    -- 创建UI
+    game_page.create_ui()
+    
+    -- 初始绘制
+    redrawAll("准备开始!")
+    
+    -- 启动定时器
+    gameTimer = sys.timerLoopStart(stepDown, 400)
+end
+
+function game_page.cleanup()
+    -- 停止定时器
+    if gameTimer then
+        sys.timerStop(gameTimer)
+        gameTimer = nil
+    end
+    
+    -- 清理UI
+    if main_container then
+        main_container:destroy()
+        main_container = nil
+    end
+    
+    -- 重置游戏状态
+    grid = nil
+    curPiece = nil
+    score = 0
+    gameOver = false
+    
+    leftBtn = nil
+    rightBtn = nil
+    rotateBtn = nil
+    downBtn = nil
+    restartBtn = nil
+    scoreLabel = nil
+    statusLabel = nil
+end
+
+return game_page

+ 93 - 0
tsb_ui/tsb_help_page.lua

@@ -0,0 +1,93 @@
+--[[
+@module  tsb_help_page         
+@summary 帮助页面
+@version 1.0
+@date    2026.03.17
+@author  李一玮
+@usage
+本文件是帮助页面,展示帮助的各种用法。
+]]
+
+local tsb_help_page  = {}
+
+-- 页面UI元素
+local main_container = nil
+local common_ui = require("tsb_common_page")
+
+-- 创建UI
+function tsb_help_page .create_ui()
+    main_container = airui.container({
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 320,
+        color = 0xF5F5F5,
+    })
+
+    --------------------- 标题栏 ------------------------
+    local title_bar = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 30,
+        color = 0xCE93D8,
+    })
+
+    airui.label({
+        parent = title_bar,
+        text = "帮助界面",
+        x = 190,
+        y = 8,
+        w = 200,
+        h = 20,
+        font_size = 16,
+        color = 0xFFFFFF,
+    })
+
+    -- 标题栏公共信息展示
+    common_ui.add_battery_display(title_bar)
+    common_ui.create_back_button(title_bar, tsb_help_page .cleanup)
+
+    -- 滚动容器
+    local scroll_container = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 30,
+        w = 480,
+        h = 290,
+        color = 0xFFFFFF,
+    })
+
+    local hardware_img = airui.image({
+        parent = scroll_container,
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 290,                    
+        src = "/luadb/hardware.png",
+        zoom = 100,
+        opacity = 255,
+    })
+
+    ---------------------------------------------------
+
+    -- 底部信息
+    --common_ui.create_status_bar(main_container)
+end
+
+-- 初始化页面
+function tsb_help_page .init(params)
+    tsb_help_page .create_ui()
+end
+
+-- 清理页面
+function tsb_help_page .cleanup()
+    common_ui.cleanup()
+    if main_container then
+        main_container:destroy()
+        main_container = nil
+    end
+end
+
+return tsb_help_page 

+ 308 - 0
tsb_ui/tsb_home_page.lua

@@ -0,0 +1,308 @@
+--[[
+@module  tsb_home_page
+@summary 调试宝主页
+@version 1.0
+@date    2026.03.04
+@author  李一玮
+@usage
+本文件是调试宝的主页,提供所有功能演示的入口。
+]]
+
+local tsb_home_page = {}
+
+-- 页面UI元素
+local main_container = nil
+local scroll_container = nil
+local common_ui = require("tsb_common_page")
+
+_G.current_tab = _G.current_tab or 0
+
+-- 全局变量:存储当前是否为游客模式
+local is_visitor_mode = false
+
+-- 选项卡分类
+local tab_names = {"常用功能", "目标设备监测", "目标设备管理", "本机设备管理"}
+
+-- 演示模块列表(按选项卡分类)
+local demos_by_tab = {
+    -- 常用功能
+    {
+        {name = "报税口", src = "/luadb/bsk2.png", page = "tsb_bsk_page", color = 0x007AFF},
+        {name = "液位仪", src = "/luadb/ywy.png", page = "tsb_ywy_page", color = 0x4CAF50},
+        {name = "提枪信号", src = "/luadb/tq.png", page = "tsb_tq_page", color = 0xF44336},
+        {name = "编码器", src = "/luadb/bmq.png", page = "tsb_bmq_page", color = 0xFF9800},
+        {name = "简易示波器", src = "/luadb/sbq.png", page = "tsb_waveform_page", color = 0xFFAB91},
+        {name = "帮助", src = "/luadb/help.png", page = "tsb_help_page", color = 0xCE93D8},
+        --{name = "测试", src = "/luadb/bsk2.png", page = "tsb_test_page", color = 0x007AFF},
+    },
+    -- 目标设备监测
+    {
+        {name = "日志监测", src = "/luadb/rzjc.png", page = "tsb_devlog_page", color = 0x82B1FF},
+        {name = "485监测", src = "/luadb/4851.png", page = "tsb_485log_page", color = 0xA5D6A7},
+        {name = "232监测", src = "/luadb/232.png", page = "tsb_232log_page", color = 0xFFCCBC},
+        {name = "射频局域网监测", src = "/luadb/LoRa.png", page = "tsb_lora_page", color = 0xFFE082},
+        
+    },
+    -- 目标设备管理
+    {
+        {name = "固件包下载", src = "/luadb/gjb.png", page = "tsb_firm_page", color = 0x2196F3},
+        {name = "升级", src = "/luadb/upgrade.png", page = "tsb_upgrade_page", color = 0x66BB6A},
+        {name = "刷机", src = "/luadb/sj.png", page = "tsb_reflash_page", color = 0xF4511E},
+        {name = "MQTT配置", src = "/luadb/MQTT.png", page = "tsb_mqtt_page", color = 0xFFB74D},
+        {name = "信道切换", src = "/luadb/xd4.png", page = "tsb_channel_page", color = 0x7E57C2},
+    },
+    -- 本机设备管理
+    {
+        {name = "本机信息", src = "/luadb/bjxx2.png", page = "tsb_devinfo_page", color = 0xB3E5FC},
+        {name = "本机升级", src = "/luadb/up1.png", page = "tsb_update_page", color = 0xA5D6A7},
+        {name = "本机日志", src = "/luadb/bjrz1.png", page = "tsb_log_page", color = 0xFFCCBC},
+        {name = "亮度管理", src = "/luadb/light.png", page = "tsb_light_page", color = 0xF2E2C5},
+        {name = "网络设置", src = "/luadb/wlan3.png", page = "tsb_wlan_page", color = 0xCE93D8},
+    }
+}
+
+-- 游客模式演示模块列表(按选项卡分类)
+local demos_by_tab_visitor = {
+    -- 常用功能(全部保留)
+    {
+        {name = "报税口", src = "/luadb/bsk2.png", page = "tsb_bsk_page", color = 0x007AFF},
+        {name = "液位仪", src = "/luadb/ywy.png", page = "tsb_ywy_page", color = 0x4CAF50},
+        {name = "提枪信号", src = "/luadb/tq.png", page = "tsb_tq_page", color = 0xF44336},
+        {name = "编码器", src = "/luadb/bmq.png", page = "tsb_bmq_page", color = 0xFF9800},
+        {name = "简易示波器", src = "/luadb/sbq.png", page = "tsb_waveform_page", color = 0xFFAB91},
+        {name = "帮助", src = "/luadb/help.png", page = "tsb_help_page", color = 0xCE93D8},
+    },
+    -- 目标设备监测(只保留485监测)
+    {
+        {name = "485监测", src = "/luadb/4851.png", page = "tsb_485log_page", color = 0xA5D6A7},
+    },
+    -- 目标设备管理(全部隐藏)
+    {},
+    -- 本机设备管理(只保留本机信息和亮度管理)
+    {
+        {name = "本机信息", src = "/luadb/bjxx2.png", page = "tsb_devinfo_page", color = 0xB3E5FC},
+        {name = "亮度管理", src = "/luadb/light.png", page = "tsb_light_page", color = 0xF2E2C5},
+        {name = "网络设置", src = "/luadb/wlan3.png", page = "tsb_wlan_page", color = 0xCE93D8},
+    }
+}
+
+-- 创建主页UI
+function tsb_home_page.create_ui()
+    -- 创建主容器
+    main_container = airui.container({
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 320,
+        color = 0xF8F9FA,
+    })
+
+    -- 标题栏
+    local title_bar = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 30,
+        color = 0x2E6EF4,
+    })
+
+    local battery_label = common_ui.add_battery_display(title_bar)
+
+    airui.label({
+        parent = title_bar,
+        text = "主界面",
+        x = 210,
+        y = 8,
+        w = 80,
+        h = 20,
+        font_size = 16,
+        color = 0xFFFFFF,
+    })
+
+    -- 返回按钮(仅首页的返回按钮没有调用common_page中的函数)
+    -- local back_btn = airui.button({
+    --     parent = title_bar,
+    --     x = 10,
+    --     y = 3,
+    --     w = 45,
+    --     h = 25,
+    --     text = "返回",
+        -- on_click = function()
+        --     tsb_home_page.cleanup()
+        --     go_back()
+        -- end
+    -- })
+
+    local back_img = airui.image({
+        parent = title_bar,
+        x = 5,
+        y = 4,
+        w = 20,
+        h = 25,                    
+        src = "/luadb/back1.png",
+        zoom = 256,
+        opacity = 255,
+        on_click = function()
+            tsb_home_page.cleanup()
+            go_back()
+        end
+    })
+
+    local back_label = airui.label({
+        parent = title_bar,
+        text = "返回",
+        x = 22,
+        y = 9,
+        w = 45,
+        h = 20,
+        font_size = 15,
+        color = 0xFFFFFF,
+        on_click = function()
+            tsb_home_page.cleanup()
+            go_back()
+        end
+    })
+
+    local person_img = airui.image({
+        parent = title_bar,
+        x = 70,
+        y = 3,
+        w = 20,
+        h = 25,                    
+        src = "/luadb/person3.png",
+        zoom = 170,
+        opacity = 255,
+    })
+
+    local person_msg = airui.label({
+        parent = title_bar,
+        text = is_visitor_mode and "游客模式" or "lyw...(管理员)",
+        x = 95,
+        y = 8,
+        w = 100,
+        h = 20,
+        font_size = 14,
+        color = 0xFFFFFF,
+    })
+
+    -- 滚动容器
+    scroll_container = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 30,
+        w = 480,
+        h = 270,
+        color = 0xF8F9FA,
+    })
+
+    -- 创建选项卡组件
+    local tabview = airui.tabview({
+        parent = scroll_container,
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 270,
+        tabs = tab_names,
+        active = _G.current_tab,
+        page_style = {
+            tabbar_size = 65,
+            pad = { method = airui.TABVIEW_PAD_ALL, value = 5 },
+            bg_opa = 200, -- 设置背景透明度,0-255
+        },
+    })
+
+    -- 为每个选项卡添加内容
+    local demos_list = is_visitor_mode and demos_by_tab_visitor or demos_by_tab
+    for i, demos in ipairs(demos_list) do
+        local tab_content = tabview:get_content(i - 1)
+        if tab_content then
+            -- 创建网格布局的演示按钮
+            local button_width = 150
+            local button_height = 80
+            local columns = 3
+            local padding = 5
+            local y_offset = 10
+            
+            for j, demo in ipairs(demos) do
+                local col = (j - 1) % columns
+                local row = math.floor((j - 1) / columns)
+                
+                local x = padding + col * (button_width + padding)
+                local y = y_offset + row * (button_height + padding)
+                
+                -- 创建按钮容器(卡片样式)
+                local card = airui.container({
+                    parent = tab_content,
+                    x = x,
+                    y = y,
+                    w = button_width,
+                    h = button_height,
+                    color = demo.color,
+                    radius = 8,
+                })
+                
+                -- 图标或图片
+                if demo.src then
+                    -- 使用图片
+                    airui.image({
+                        parent = card,
+                        x = 5,
+                        y = 7,
+                        w = 50,
+                        h = 50,          
+                        src = demo.src,
+                        zoom = 200,
+                        opacity = 255,
+                    })
+                elseif demo.icon then
+                    -- 使用图标
+                    airui.label({
+                        parent = card,
+                        text = demo.icon,
+                        x = 15,
+                        y = 20,
+                        w = 40,
+                        h = 40,
+                        font_size = 24, -- 增大图标大小
+                    })
+                end
+                
+                -- 演示名称标签
+                airui.label({
+                    parent = card,
+                    text = demo.name,
+                    x = 50,
+                    y = 25,
+                    w = button_width - 60,
+                    h = 40,
+                    font_size = 14, -- 调整字体大小
+                    on_click = function()
+                        _G.current_tab = i - 1
+                        _G.show_page(demo.page)
+                    end
+                })
+            end
+        end
+    end
+
+    -- 底部状态栏
+    common_ui.create_status_bar(main_container)
+end
+
+-- 初始化页面
+function tsb_home_page.init(params)
+    -- 检查是否为游客模式
+    is_visitor_mode = params and params.visitor_mode or false
+    log.info("tsb_home_page", "初始化页面,游客模式:", is_visitor_mode)
+    tsb_home_page.create_ui()
+end
+
+-- 清理页面
+function tsb_home_page.cleanup()
+    -- 清理UI元素
+    main_container = nil
+    scroll_container = nil
+end
+
+return tsb_home_page

+ 148 - 0
tsb_ui/tsb_light_page.lua

@@ -0,0 +1,148 @@
+--[[
+@module  tsb_light_page
+@summary 亮度演示页面
+@version 1.0
+@date    2026.03.04
+@author  李一玮
+@usage
+本文件是亮度演示页面,展示亮度的各种用法。
+]]
+
+local tsb_light_page = {}
+
+-- 页面UI元素
+local main_container = nil
+local common_ui = require("tsb_common_page")
+
+-- 创建UI
+function tsb_light_page.create_ui()
+    main_container = airui.container({
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 320,
+        color = 0xF5F5F5,
+    })
+
+    --------------------- 标题栏 ------------------------
+    local title_bar = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 30,
+        color = 0xF2E2C5,
+    })
+
+    airui.label({
+        parent = title_bar,
+        text = "亮度管理",
+        x = 190,
+        y = 8,
+        w = 100,
+        h = 20,
+        font_size = 16,
+        color = 0xFFFFFF,
+    })
+
+    -- 标题栏公共信息展示
+    common_ui.add_battery_display(title_bar)
+    common_ui.create_back_button(title_bar, tsb_light_page.cleanup)
+    ---------------------------------------------------
+
+    -- 滚动容器
+    local scroll_container = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 30,
+        w = 480,
+        h = 270,
+        color = 0xFFFFFF,
+    })
+
+    --------------------- 屏幕亮度控制 ------------------------
+    -- 亮度标签
+    airui.label({
+        parent = scroll_container,
+        text = "屏幕亮度控制",
+        x = 180,
+        y = 50,
+        w = 100,
+        h = 30,
+        font_size = 16,
+    })
+
+    -- 亮度进度条
+    local brightness_bar = airui.bar({
+        parent = scroll_container,
+        x = 20,
+        y = 90,
+        w = 440,
+        h = 20,
+        value = 50,
+        min = 10,
+        max = 100,
+        radius = 5,
+        show_progress_text = true,
+        indicator_color = 0x2196F3,
+        progress_text_format = "%d%%",
+    })
+
+    -- 亮度控制按钮
+    -- 降低亮度按钮
+    local decrease_brightness_btn = airui.button({
+        parent = scroll_container,
+        x = 100,
+        y = 140,
+        w = 110,
+        h = 40,
+        text = "降低亮度",
+        on_click = function(self)
+            local current = brightness_bar:get_value()
+            if current > 10 then
+                current = current - 10
+                brightness_bar:set_value(current, true)
+                brightness_value_label:set_text(current .. "%")
+                log.info("set_brightness", "亮度降低到:" .. current .. "%")
+            end
+        end
+    })
+
+    -- 提升亮度按钮
+    local increase_brightness_btn = airui.button({
+        parent = scroll_container,
+        x = 280,
+        y = 140,
+        w = 110,
+        h = 40,
+        text = "提升亮度",
+        on_click = function(self)
+            local current = brightness_bar:get_value()
+            if current < 100 then
+                current = current + 10
+                brightness_bar:set_value(current, true)
+                brightness_value_label:set_text(current .. "%")
+                log.info("set_brightness", "亮度提升到:" .. current .. "%")
+            end
+        end
+    })
+    ---------------------------------------------------
+
+    -- 底部信息
+    common_ui.create_status_bar(main_container)
+end
+
+-- 初始化页面
+function tsb_light_page.init(params)
+    tsb_light_page.create_ui()
+end
+
+-- 清理页面
+function tsb_light_page.cleanup()
+    if main_container then
+        main_container:destroy()
+        main_container = nil
+    end
+end
+
+return tsb_light_page

+ 143 - 0
tsb_ui/tsb_log_page.lua

@@ -0,0 +1,143 @@
+--[[
+@module  tsb_log_page
+@summary 本机日志页面
+@version 1.0
+@date    2026.03.04
+@author  李一玮
+@usage
+本文件是设备日志页面,展示设备的日志信息。
+]]
+
+local tsb_log_page = {}
+
+-- 页面UI元素
+local main_container = nil
+local common_ui = require("tsb_common_page")
+
+-- 创建UI
+function tsb_log_page.create_ui()
+    main_container = airui.container({
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 320,
+        color = 0xF5F5F5,
+    })
+
+    --------------------- 标题栏 ------------------------
+    local title_bar = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 30,
+        color = 0xFFCCBC,
+    })
+
+    airui.label({
+        parent = title_bar,
+        text = "本机日志",
+        x = 190,
+        y = 8,
+        w = 200,
+        h = 20,
+        font_size = 16,
+        color = 0xFFFFFF,
+    })
+
+    -- 标题栏公共信息展示
+    common_ui.add_battery_display(title_bar)
+    common_ui.create_back_button(title_bar, tsb_log_page.cleanup)
+    ---------------------------------------------------
+
+    -- 滚动容器
+    local scroll_container = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 30,
+        w = 480,
+        h = 270,
+        color = 0xFFFFFF,
+    })
+
+    --------------------- 设备日志显示 ------------------------
+    local log_container = airui.container({
+        parent = scroll_container,
+        x = 0,
+        y = 50,
+        w = 480,
+        h = 215,
+        color = 0xFFFFFF,
+    })
+
+    -- 日志内容显示
+    local log_content = airui.textarea({
+        parent = log_container,
+        x = 5,
+        y = 5,
+        w = 470,
+        h = 205,
+        max_len = 10000,
+        text = "[2024-07-02 09:30:46] app start\n" ..
+               "[2024-07-02 09:30:47] init done\n" ..
+               "[2024-07-02 09:30:48] connect success\n" ..
+               "[2024-07-02 09:30:50] start monitor\n" ..
+               "[2024-07-02 09:31:00] detect device 1\n" ..
+               "[2024-07-02 09:31:10] detect device 2\n" ..
+               "[2024-07-02 09:31:20] upload success\n" ..
+               "[2024-07-02 09:31:30] system running\n" ..
+               "[2024-07-02 09:31:30] system status: normal\n",
+    })
+
+    --------------------- 控制按钮 ------------------------
+    -- 开始检测按钮
+    local start_btn = airui.button({
+        parent = scroll_container,
+        x = 10,
+        y = 10,
+        w = 120,
+        h = 35,
+        text = "查看本机日志",
+        --style = { bg_color = 0x2B6FF1,border_color = 0x2B6FF1, text_color = 0xFFFFFF, radius = 8 },
+        on_click = function(self)
+            log.info("devlog", "开始检测按钮被点击")
+        end
+    })
+
+    -- 清空按钮
+    -- local clear_btn = airui.button({
+    --     parent = scroll_container,
+    --     x = 120,
+    --     y = 10,
+    --     w = 80,
+    --     h = 35,
+    --     text = "清空",
+    --     on_click = function(self)
+    --         log.info("devlog", "清空按钮被点击")
+    --         -- 清空日志内容
+    --         log_content:set_text(" ")
+    --     end
+    -- })
+    ---------------------------------------------------
+
+    
+    ---------------------------------------------------
+
+    -- 底部信息
+    common_ui.create_status_bar(main_container)
+end
+
+-- 初始化页面
+function tsb_log_page.init(params)
+    tsb_log_page.create_ui()
+end
+
+-- 清理页面
+function tsb_log_page.cleanup()
+    if main_container then
+        main_container:destroy()
+        main_container = nil
+    end
+end
+
+return tsb_log_page

+ 243 - 0
tsb_ui/tsb_login_page.lua

@@ -0,0 +1,243 @@
+--[[
+@module     tsb_login_page
+@summary    调试宝登录页面
+@version    1.0
+@date       2026.03.04
+@author     李一玮
+@usage      本文件是调试宝登录页面,展示调试宝登录的各种用法。
+]]
+
+local tsb_login_page = {}
+
+----------------------------------------------------------------
+-- 页面UI元素
+----------------------------------------------------------------
+local main_container   = nil
+local scroll_container = nil
+local common_ui = require("tsb_common_page")
+
+----------------------------------------------------------------
+-- 创建UI
+----------------------------------------------------------------
+function tsb_login_page.create_ui()
+    main_container = airui.container({
+        parent = airui.screen,
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 320,
+        color = 0xFFFFFF,
+    })
+ 
+    -- 标题栏
+    local title_bar = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 30,
+        color = 0x007AFF,
+    })
+
+
+    common_ui.add_battery_display(title_bar)
+
+    -- 滚动容器
+    scroll_container = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 30,
+        w = 480,
+        h = 270,
+        color = 0xFFFFFF,
+    })
+
+    common_ui.add_background_png(scroll_container)
+
+    local welcome_msg = airui.label({
+        parent = scroll_container,
+        text = "欢迎登录调试宝55号",
+        x = 170,
+        y = 70,
+        w = 180,
+        h = 30,
+        font_size = 16,
+        color = 0x000000
+    })
+
+    -- 注册虚拟键盘,先创建再在 textarea 配置里复用
+    local keyboard1 = airui.keyboard({
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 160,                        -- x, y, 键盘默认打开ALIGN_BOTTOM_MID,位置从中下方开始计算
+        mode = "numeric",               -- 键盘模式,可选 "text"/"upper"/"lower"/"numeric"
+        auto_hide = true,               -- 自动隐藏键盘
+        preview = true,          
+        preview_height = 35,
+        bg_color = 0xf1f1f1,            -- 键盘背景颜色为灰色,可选,不设置则透明
+        on_commit = function()          -- 确认事件回调,只有在按下确认键时才会触发
+            log.info("keyboard", "commit")
+        end
+    })
+
+    local form_container = airui.container({
+        parent = scroll_container,
+        x = 135,
+        y = 100,
+        w = 220,
+        h = 140,
+        color = 0xFFFFFF,
+        color_opacity = 0,
+    })
+
+    -- 账号输入
+    airui.label({
+        parent = form_container,
+        text = "账号",
+        x = 0,
+        y = 7,
+        w = 60,
+        h = 30,
+        font_size = 14,
+        color = 0x6D7278
+    })
+
+    local name_input = airui.textarea({
+        parent = form_container,
+        x = 33,
+        y = 0,
+        w = 180,
+        h = 30,
+        text = "",
+        placeholder = "请输入账号",
+        max_len = 20,
+        keyboard = keyboard1
+    })
+
+    -- 密码输入
+    airui.label({
+        parent = form_container,
+        text = "密码",
+        x = 0,
+        y = 49,
+        w = 60,
+        h = 30,
+        font_size = 14,
+        color = 0x6D7278
+    })
+
+    local email_input = airui.textarea({
+        parent = form_container,
+        x = 33,
+        y = 42,
+        w = 180,
+        h = 30,
+        text = "",
+        placeholder = "请输入密码",
+        max_len = 50,
+        keyboard = keyboard1
+    })
+
+    -- 登录按钮
+    local submit_btn = airui.button({
+        parent = form_container,
+        x = 3,
+        y = 90,
+        w = 100,
+        h = 35,
+        text = "登录",
+        style = { bg_color = 0x2B6FF1,border_color = 0x2B6FF1, text_color = 0xFFFFFF, radius = 8 },
+        on_click = function(self)
+            local name = name_input:get_text()
+            local email = email_input:get_text()
+            
+            _G.show_page("tsb_home_page")
+
+            -- if name == "" or email == "" then
+            --     local msg = airui.msgbox({
+            --         text = "请填写完整信息",
+            --         buttons = { "确定" },
+            --         on_action = function(self, label)
+            --             if label == "确定" then
+            --                 self:hide()
+            --             end
+            --         end
+            --     })
+            --     msg:show()
+            -- else
+            --     local msg = airui.msgbox({
+            --         text = "登录成功!\n姓名: " .. name .. "\n邮箱: " .. email,
+            --         buttons = { "确定" },
+            --         on_action = function(self, label)
+            --             if label == "确定" then
+            --                 self:hide()
+            --             end
+            --         end
+            --     })
+            --     msg:show()
+            -- end
+        end
+    })
+
+    local visitor_btn = airui.button({
+        parent = form_container,
+        x = 112,
+        y = 90,
+        w = 100,
+        h = 35,
+        text = "游客模式",
+        on_click = function(self)
+            log.info("button", "游客模式按钮被点击")
+            _G.show_page("tsb_home_page", { visitor_mode = true })
+        end
+    })
+
+    local basic_image = airui.image({
+        parent = scroll_container,
+        x = 11,
+        y = 6,
+        w = 120,
+        h = 40,                    
+        src = "/luadb/wbjw.png",
+        zoom = 100,
+        -- on_click = function(self)
+        --     log.info("image", "基本图片被点击")
+        --     local msg = airui.msgbox({
+        --         text = "基本图片被点击",
+        --         buttons = { "确定" },
+        --         timeout = 1500,
+        --         on_action = function(self, label)
+        --             self:hide()
+        --         end
+        --     })
+        --     msg:show()
+        -- end
+    })
+
+    _G.current_tab = 0
+
+    -- 底部信息栏
+    common_ui.create_status_bar(main_container)
+end
+
+----------------------------------------------------------------
+-- 初始化页面
+----------------------------------------------------------------
+function tsb_login_page.init(params)
+    tsb_login_page.create_ui()
+end
+
+----------------------------------------------------------------
+-- 清理页面
+----------------------------------------------------------------
+function tsb_login_page.cleanup()
+    if main_container then
+        main_container:destroy()
+        main_container   = nil
+        scroll_container = nil
+        --sys.timerStop(time1_id)
+    end
+end
+
+return tsb_login_page

+ 291 - 0
tsb_ui/tsb_lora_page.lua

@@ -0,0 +1,291 @@
+--[[
+@module  tsb_lora_page
+@summary 无线局域网监测演示页面
+@version 1.0
+@date    2026.03.04
+@author  李一玮
+@usage
+本文件是LORA信号检测演示页面,展示LORA信号的各种用法。
+]]
+
+local tsb_lora_page = {}
+
+-- 页面UI元素
+local main_container = nil
+local common_ui = require("tsb_common_page")
+
+-- 创建UI
+function tsb_lora_page.create_ui()
+    main_container = airui.container({
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 320,
+        color = 0xF5F5F5,
+    })
+
+    --------------------- 标题栏 ------------------------
+    local title_bar = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 30,
+        color = 0xFFE082,
+    })
+
+    airui.label({
+        parent = title_bar,
+        text = "射频局域网监测",
+        x = 180,
+        y = 8,
+        w = 200,
+        h = 20,
+        font_size = 16,
+        color = 0xFFFFFF,
+    })
+
+    -- 标题栏公共信息展示
+    common_ui.add_battery_display(title_bar)
+    common_ui.create_back_button(title_bar, tsb_lora_page.cleanup)
+    ---------------------------------------------------
+
+
+
+    -- 滚动容器
+    local scroll_container = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 30,
+        w = 480,
+        h = 290,
+        color = 0xFFFFFF,
+    })
+
+    local keyboard1 = airui.keyboard({
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 160,                        -- x, y, 键盘默认打开ALIGN_BOTTOM_MID,位置从中下方开始计算
+        mode = "numeric",               -- 键盘模式,可选 "text"/"upper"/"lower"/"numeric"
+        auto_hide = true,               -- 自动隐藏键盘
+        preview = true,          
+        preview_height = 35,
+        bg_color = 0xf1f1f1,            -- 键盘背景颜色为灰色,可选,不设置则透明
+        on_commit = function()          -- 确认事件回调,只有在按下确认键时才会触发
+            log.info("keyboard", "commit")
+        end
+    })
+
+    --------------------- 第一行控制区 ------------------------
+    -- 当前lora信道标签
+    airui.label({
+        parent = scroll_container,
+        text = "当前lora信道",
+        x = 10,
+        y = 12,
+        w = 100,
+        h = 30,
+        font_size = 14,
+    })
+
+    -- 当前lora信道值
+    local current_channel_label = airui.label({
+        parent = scroll_container,
+        text = "1",
+        x = 110,
+        y = 12,
+        w = 30,
+        h = 30,
+        font_size = 14,
+    })
+
+    -- 设置lora信道标签
+    airui.label({
+        parent = scroll_container,
+        text = "设置lora信道",
+        x = 130,
+        y = 12,
+        w = 100,
+        h = 30,
+        font_size = 14,
+    })
+
+    -- 设置lora信道下拉框
+    local channel_dropdown = airui.dropdown({
+        parent = scroll_container,
+        x = 230,
+        y = 7,
+        w = 80,
+        h = 30,
+        options = {"1", "2", "3", "4", "5", "6"},
+        default_index = 0, -- 默认选择1
+        on_change = function(self,idx)
+            log.info("lora_channel", "选择了信道:" .. self.options[idx + 1])
+        end
+    })
+
+    -- 开始监测按钮
+    local start_btn = airui.button({
+        parent = scroll_container,
+        x = 320,
+        y = 5,
+        w = 80,
+        h = 30,
+        text = "开始监测",
+        style = { bg_color = 0x2B6FF1,border_color = 0x2B6FF1, text_color = 0xFFFFFF, radius = 8 },
+        on_click = function(self)
+            log.info("lora_monitor", "开始监测按钮被点击")
+        end
+    })
+
+    -- 重置按钮
+    local reset_btn = airui.button({
+        parent = scroll_container,
+        x = 405,
+        y = 5,
+        w = 60,
+        h = 30,
+        text = "重置",
+        on_click = function(self)
+            log.info("lora_monitor", "重置按钮被点击")
+        end
+    })
+    ---------------------------------------------------
+
+    --------------------- 第二行控制区 ------------------------
+    -- 监测设备类型标签
+    airui.label({
+        parent = scroll_container,
+        text = "监测设备类型",
+        x = 10,
+        y = 53,
+        w = 100,
+        h = 30,
+        font_size = 14,
+    })
+
+    -- 监测设备类型下拉框
+    local device_type_dropdown = airui.dropdown({
+        parent = scroll_container,
+        x = 110,
+        y = 45,
+        w = 100,
+        h = 30,
+        options = {"路由器", "连接器", "全部设备"},
+        default_index = 0, -- 默认选择路由器
+        on_change = function(self,idx)
+            log.info("device_type", "选择了设备类型:" .. self.options[idx + 1])
+        end
+    })
+
+    -- 连接器SN标签
+    airui.label({
+        parent = scroll_container,
+        text = "连接器SN",
+        x = 220,
+        y = 53,
+        w = 80,
+        h = 30,
+        font_size = 14,
+    })
+
+    -- 连接器SN输入框
+    local sn_input = airui.textarea({
+        parent = scroll_container,
+        x = 290,
+        y = 45,
+        w = 80,
+        h = 30,
+        text = "",
+        max_length = 5,
+        keyboard = keyboard1
+    })
+
+    -- 上传服务器标签
+    airui.label({
+        parent = scroll_container,
+        text = "上传",
+        x = 380,
+        y = 50,
+        w = 60,
+        h = 30,
+        font_size = 14,
+    })
+
+    -- 上传服务器开关
+    local upload_switch = airui.switch({
+        parent = scroll_container,
+        x = 415,
+        y = 45,
+        w = 50,
+        h = 25,
+        checked = true,
+        on_change = function(self, checked)
+            log.info("upload_switch", "上传服务器:" .. (checked and "开启" or "关闭"))
+        end
+    })
+    ---------------------------------------------------
+
+    --------------------- 表格区域 ------------------------
+    -- 创建表格
+    local lora_table = airui.table({
+        parent = scroll_container,
+        x = 0,
+        y = 80,
+        w = 480,
+        h = 210,
+        rows = 11, -- 10行数据 + 表头
+        cols = 9,
+        col_width = {70, 80, 90, 70, 70, 100, 100, 70, 150}, -- 调整列宽以适应内容
+        border_color = 0xFFE082,
+    })
+
+    -- 设置表头
+    local headers = {"序号", "发送方", "连接器SN", "信号", "版本", "一级类型", "二级类型", "数据", "时间"}
+    for i, header in ipairs(headers) do
+        lora_table:set_cell_text(0, i-1, header)
+    end
+
+    -- 示例数据
+    local lora_data = {
+        {"1", "设备1", "12345", "-65", "v1.0", "路由器", "A型", "00 01 02", "2024/07/02 09:30:46"},
+        {"2", "设备2", "67890", "-70", "v1.1", "连接器", "B型", "03 04 05", "2024/07/02 09:31:00"},
+        {"3", "设备3", "54321", "-60", "v1.0", "路由器", "A型", "06 07 08", "2024/07/02 09:32:15"},
+        {"4", "设备4", "09876", "-75", "v1.1", "连接器", "C型", "09 0A 0B", "2024/07/02 09:33:30"},
+        {"5", "设备5", "13579", "-68", "v1.0", "路由器", "B型", "0C 0D 0E", "2024/07/02 09:34:45"},
+        {"6", "设备6", "24680", "-72", "v1.1", "连接器", "A型", "0F 10 11", "2024/07/02 09:35:00"},
+        {"7", "设备7", "97531", "-63", "v1.0", "路由器", "C型", "12 13 14", "2024/07/02 09:36:15"},
+        {"8", "设备8", "86420", "-69", "v1.1", "连接器", "B型", "15 16 17", "2024/07/02 09:37:30"},
+        {"9", "设备9", "12457", "-66", "v1.0", "路由器", "A型", "18 19 1A", "2024/07/02 09:38:45"},
+        {"10", "设备10", "36925", "-71", "v1.1", "连接器", "C型", "1B 1C 1D", "2024/07/02 09:39:00"},
+    }
+
+    -- 填充表格数据
+    for i, data in ipairs(lora_data) do
+        for j, value in ipairs(data) do
+            lora_table:set_cell_text(i, j-1, value)
+        end
+    end
+    ---------------------------------------------------
+
+    -- 底部信息
+    --common_ui.create_status_bar(main_container)
+end
+
+-- 初始化页面
+function tsb_lora_page.init(params)
+    tsb_lora_page.create_ui()
+end
+
+-- 清理页面
+function tsb_lora_page.cleanup()
+    -- 停止定时器
+    common_ui.cleanup()
+    if main_container then
+        main_container:destroy()
+        main_container = nil
+    end
+end
+
+return tsb_lora_page

+ 444 - 0
tsb_ui/tsb_mqtt_page.lua

@@ -0,0 +1,444 @@
+--[[
+@module  tsb_mqtt_page
+@summary MQTT配置页面
+@version 1.0
+@date    2026.03.05
+@author  李一玮
+@usage
+本文件是MQTT配置页面,展示MQTT配置的各种用法。
+]]
+
+local tsb_mqtt_page = {}
+
+-- 页面UI元素
+local main_container = nil
+local common_ui = require("tsb_common_page")
+
+-- 创建UI
+function tsb_mqtt_page.create_ui()
+    main_container = airui.container({
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 320,
+        color = 0xFFFFFF,
+        color_opacity = 0
+    })
+
+    --------------------- 标题栏 ------------------------
+    local title_bar = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 30,
+        color = 0xFFB74D,
+    })
+
+    airui.label({
+        parent = title_bar,
+        text = "MQTT配置",
+        x = 195,
+        y = 8,
+        w = 100,
+        h = 20,
+        font_size = 16,
+        color = 0xFFFFFF,
+    })
+
+    
+    -- 标题栏公共信息展示
+    common_ui.add_battery_display(title_bar)
+    common_ui.create_back_button(title_bar, tsb_mqtt_page.cleanup)
+    ---------------------------------------------------
+
+    -- 滚动容器
+    local scroll_container = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 30,
+        w = 480,
+        h = 260,
+        color = 0xFFFFFF,
+        
+    })
+
+    local keyboard1 = airui.keyboard({
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 160,                        -- x, y, 键盘默认打开ALIGN_BOTTOM_MID,位置从中下方开始计算
+        mode = "numeric",               -- 键盘模式,可选 "text"/"upper"/"lower"/"numeric"
+        auto_hide = true,               -- 自动隐藏键盘
+        preview = true,          
+        preview_height = 35,
+        bg_color = 0xf1f1f1,            -- 键盘背景颜色为灰色,可选,不设置则透明
+        on_commit = function()          -- 确认事件回调,只有在按下确认键时才会触发
+            log.info("keyboard", "commit")
+        end
+    })
+
+    local keyboard2 = airui.keyboard({
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 160,                        -- x, y, 键盘默认打开ALIGN_BOTTOM_MID,位置从中下方开始计算
+        mode = "lower",               -- 键盘模式,可选 "text"/"upper"/"lower"/"numeric"
+        auto_hide = true,               -- 自动隐藏键盘
+        preview = true,
+        preview_height = 35,
+        bg_color = 0xf1f1f1,            -- 键盘背景颜色为灰色,可选,不设置则透明
+        on_commit = function()          -- 确认事件回调,只有在按下确认键时才会触发
+            log.info("keyboard", "commit")
+        end
+    })
+
+    --------------------- 第一行控制区 ------------------------
+    -- 当前lora信道标签
+    airui.label({
+        parent = scroll_container,
+        text = "当前lora信道",
+        x = 10,
+        y = 10,
+        w = 100,
+        h = 30,
+        font_size = 14,
+    })
+
+    -- 当前lora信道值
+    local current_channel_label = airui.label({
+        parent = scroll_container,
+        text = "1",
+        x = 110,
+        y = 10,
+        w = 30,
+        h = 30,
+        font_size = 14,
+    })
+
+    -- 设置lora信道标签
+    airui.label({
+        parent = scroll_container,
+        text = "设置lora信道",
+        x = 150,
+        y = 10,
+        w = 100,
+        h = 30,
+        font_size = 14,
+    })
+
+    -- 设置lora信道下拉框
+    local channel_dropdown = airui.dropdown({
+        parent = scroll_container,
+        x = 250,
+        y = 3,
+        w = 70,
+        h = 30,
+        options = {"1", "2", "3", "4", "5", "6"},
+        default_index = 0, -- 默认选择1
+        on_change = function(self,idx)
+            log.info("mqtt_channel", "选择了信道:" .. self.options[idx + 1])
+        end
+    })
+    ---------------------------------------------------
+
+    --------------------- 输入框区域 ------------------------
+    -- 左侧输入框容器
+    local left_container = airui.container({
+        parent = scroll_container,
+        x = 10,
+        y = 40,
+        w = 230,
+        h = 230,
+        color = 0xFFFFFF,
+    })
+
+    -- 左侧第一组:一级设备类型
+    airui.label({
+        parent = left_container,
+        text = "一级设备类型:",
+        x = 0,
+        y = 0,
+        w = 100,
+        h = 20,
+        font_size = 14,
+    })
+    local device_type_input = airui.textarea({
+        parent = left_container,
+        x = 0,
+        y = 20,
+        w = 150,
+        h = 30,
+        text = "",
+        keyboard = keyboard1
+    })
+
+    -- 左侧第二组:一级设备SN
+    airui.label({
+        parent = left_container,
+        text = "一级设备SN:",
+        x = 0,
+        y = 55,
+        w = 100,
+        h = 20,
+        font_size = 14,
+    })
+    local device_sn_input = airui.textarea({
+        parent = left_container,
+        x = 0,
+        y = 75,
+        w = 150,
+        h = 30,
+        text = "",
+        keyboard = keyboard1,
+    })
+
+    -- 左侧第三组:目标设备类型
+    airui.label({
+        parent = left_container,
+        text = "目标设备类型:",
+        x = 0,
+        y = 110,
+        w = 100,
+        h = 20,
+        font_size = 14,
+    })
+    local target_type_input = airui.textarea({
+        parent = left_container,
+        x = 0,
+        y = 130,
+        w = 150,
+        h = 30,
+        text = "",
+        keyboard = keyboard1,
+    })
+
+    -- 左侧第四组:目标设备SN
+    airui.label({
+        parent = left_container,
+        text = "目标设备SN",
+        x = 0,
+        y = 165,
+        w = 100,
+        h = 20,
+        font_size = 14,
+    })
+    local target_sn_input = airui.textarea({
+        parent = left_container,
+        x = 0,
+        y = 185,
+        w = 150,
+        h = 30,
+        text = "",
+        keyboard = keyboard1,
+    })
+
+    -- 右侧输入框容器
+    local right_container = airui.container({
+        parent = scroll_container,
+        x = 240,
+        y = 40,
+        w = 230,
+        h = 230,
+        color = 0xFFFFFF,
+    })
+
+    -- 右侧第一组:服务器地址
+    airui.label({
+        parent = right_container,
+        text = "服务器地址:",
+        x = 0,
+        y = 0,
+        w = 100,
+        h = 20,
+        font_size = 14,
+    })
+    local server_addr_input = airui.textarea({
+        parent = right_container,
+        x = 0,
+        y = 20,
+        w = 220,
+        h = 30,
+        text = "",
+        keyboard = keyboard2,
+    })
+
+    -- 右侧第二组:服务器端口
+    airui.label({
+        parent = right_container,
+        text = "服务器端口:",
+        x = 0,
+        y = 55,
+        w = 100,
+        h = 20,
+        font_size = 14,
+    })
+    local server_port_input = airui.textarea({
+        parent = right_container,
+        x = 0,
+        y = 75,
+        w = 220,
+        h = 30,
+        text = "",
+        keyboard = keyboard1,
+    })
+
+    -- 右侧第三组:服务器用户名
+    airui.label({
+        parent = right_container,
+        text = "服务器用户名:",
+        x = 0,
+        y = 110,
+        w = 100,
+        h = 20,
+        font_size = 14,
+    })
+    local server_user_input = airui.textarea({
+        parent = right_container,
+        x = 0,
+        y = 130,
+        w = 220,
+        h = 30,
+        text = "",
+        keyboard = keyboard2,
+    })
+
+    -- 右侧第四组:服务器密码
+    airui.label({
+        parent = right_container,
+        text = "服务器密码:",
+        x = 0,
+        y = 165,
+        w = 100,
+        h = 20,
+        font_size = 14,
+    })
+    local server_pass_input = airui.textarea({
+        parent = right_container,
+        x = 0,
+        y = 185,
+        w = 220,
+        h = 30,
+        text = "",
+        keyboard = keyboard2,
+    })
+    ---------------------------------------------------
+
+    --------------------- 提示信息 ------------------------
+    airui.label({
+        parent = scroll_container,
+        text = "一级设备SN和目标设备SN同时写1代表全量配置(仅LORA模式)",
+        x = 10,
+        y = 280,
+        w = 460,
+        h = 20,
+        font_size = 12,
+        color = 0x757575,
+    })
+    ---------------------------------------------------
+
+    --------------------- 控制按钮区 ------------------------
+    -- 串口发送标签
+    airui.label({
+        parent = scroll_container,
+        text = "串口发送",
+        x = 15,
+        y = 312,
+        w = 80,
+        h = 30,
+        font_size = 14,
+    })
+
+    -- 串口发送开关
+    local serial_switch = airui.switch({
+        parent = scroll_container,
+        x = 80,
+        y = 310,
+        w = 50,
+        h = 20,
+        checked = false,
+        on_change = function(self)
+            log.info("UART SEND", self:get_state() and "开启" or "关闭")
+        end
+    })
+
+    -- 串口发送提示信息
+    airui.label({
+        parent = scroll_container,
+        text = "默认为LORA模式,可选串口发送",
+        x = 140,
+        y = 312,
+        w = 200,
+        h = 20,
+        font_size = 12,
+        color = 0x757575,
+    })
+
+    -- 新配置下发按钮
+    local new_config_btn = airui.button({
+        parent = scroll_container,
+        x = 10,
+        y = 340,
+        w = 100,
+        h = 35,
+        text = "新配置下发",
+        style = { bg_color = 0x2B6FF1,border_color = 0x2B6FF1, text_color = 0xFFFFFF, radius = 8 },
+        on_click = function(self)
+            log.info("mqtt_config", "新配置下发按钮被点击")
+        end
+    })
+
+    -- 恢复默认配置按钮
+    local default_config_btn = airui.button({
+        parent = scroll_container,
+        x = 120,
+        y = 340,
+        w = 120,
+        h = 35,
+        text = "恢复默认配置",
+        on_click = function(self)
+            log.info("mqtt_config", "恢复默认配置按钮被点击")
+        end
+    })
+
+    -- 执行结果
+    airui.label({
+        parent = scroll_container,
+        text = "执行结果:",
+        x = 300,
+        y = 347,
+        w = 80,
+        h = 30,
+        font_size = 16,
+    })
+
+    -- 执行结果值
+    local result_label = airui.label({
+        parent = scroll_container,
+        text = "成功",
+        x = 380,
+        y = 347,
+        w = 100,
+        h = 30,
+        font_size = 16,
+    })
+    ---------------------------------------------------
+
+    -- 底部信息
+    common_ui.create_status_bar(main_container)
+end
+
+-- 初始化页面
+function tsb_mqtt_page.init(params)
+    tsb_mqtt_page.create_ui()
+end
+
+-- 清理页面
+function tsb_mqtt_page.cleanup()
+    if main_container then
+        main_container:destroy()
+        main_container = nil
+    end
+end
+
+return tsb_mqtt_page

+ 235 - 0
tsb_ui/tsb_reflash_page.lua

@@ -0,0 +1,235 @@
+--[[
+@module  tsb_reflash_page
+@summary 刷机页面
+@version 1.0
+@date    2026.03.05
+@author  李一玮
+@usage
+本文件是刷机页面,展示刷机的各种用法。
+]]
+
+local tsb_reflash_page = {}
+
+-- 页面UI元素
+local main_container = nil
+local common_ui = require("tsb_common_page")
+
+-- 创建UI
+function tsb_reflash_page.create_ui()
+    main_container = airui.container({
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 320,
+        color = 0xF5F5F5,
+    })
+
+    --------------------- 标题栏 ------------------------
+    local title_bar = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 30,
+        color = 0xF4511E,
+    })
+
+    airui.label({
+        parent = title_bar,
+        text = "刷机界面",
+        x = 200,
+        y = 8,
+        w = 80,
+        h = 20,
+        font_size = 16,
+        color = 0xFFFFFF,
+    })
+
+    -- 标题栏公共信息展示
+    common_ui.add_battery_display(title_bar)
+    common_ui.create_back_button(title_bar, tsb_reflash_page.cleanup)
+    ---------------------------------------------------
+
+    -- 滚动容器
+    local scroll_container = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 30,
+        w = 480,
+        h = 270,
+        color = 0xFFFFFF,
+    })
+
+    --------------------- 第一行控制区 ------------------------
+    -- 设备类型标签
+    airui.label({
+        parent = scroll_container,
+        text = "设备类型:",
+        x = 30,
+        y = 32,
+        w = 80,
+        h = 30,
+        font_size = 16,
+    })
+
+    -- 设备类型下拉框
+    local device_type_dropdown = airui.dropdown({
+        parent = scroll_container,
+        x = 108,
+        y = 25,
+        w = 100,
+        h = 30,
+        options = {"0101", "0102", "0201", "0202", "0301", "0302", "0103", "0104", "0902", "0904"},
+        default_index = 0,
+        on_change = function(self,idx)
+            log.info("reflash_device_type", "选择了设备类型:" .. self.options[idx + 1])
+        end
+    })
+
+    -- 固件类型标签
+    airui.label({
+        parent = scroll_container,
+        text = "固件类型:",
+        x = 255,
+        y = 32,
+        w = 80,
+        h = 30,
+        font_size = 16,
+    })
+
+    -- 固件类型下拉框
+    local firmware_type_dropdown = airui.dropdown({
+        parent = scroll_container,
+        x = 330,
+        y = 25,
+        w = 100,
+        h = 30,
+        options = {"产测", "app", "boot"},
+        default_index = 1, -- 默认选择app
+        on_change = function(self,idx)
+            log.info("reflash_firm_type", "选择了固件类型:" .. self.options[idx + 1])
+        end
+    })
+    ---------------------------------------------------
+
+    --------------------- 第二行控制区 ------------------------
+    -- 提示信息容器
+    local info_container = airui.container({
+        parent = scroll_container,
+        x = 10,
+        y = 90,
+        w = 300,
+        h = 150,
+        color = 0xF5F5F5,
+        radius = 5,
+    })
+
+    -- 提示信息标题
+    airui.label({
+        parent = info_container,
+        text = "固件信息:",
+        x = 10,
+        y = 10,
+        w = 280,
+        h = 20,
+        font_size = 15,
+        color = 0xF4511E,
+    })
+
+    -- 设备类型信息
+    airui.label({
+        parent = info_container,
+        text = "设备类型:0102",
+        x = 10,
+        y = 40,
+        w = 280,
+        h = 20,
+        font_size = 14,
+        color = 0x666666,
+    })
+
+    -- 固件类型信息
+    airui.label({
+        parent = info_container,
+        text = "固件类型:app",
+        x = 10,
+        y = 60,
+        w = 280,
+        h = 20,
+        font_size = 14,
+        color = 0x666666,
+    })
+
+    -- 包版本信息
+    airui.label({
+        parent = info_container,
+        text = "包版本:01023049",
+        x = 10,
+        y = 80,
+        w = 280,
+        h = 20,
+        font_size = 14,
+        color = 0x666666,
+    })
+
+    -- 包大小信息
+    airui.label({
+        parent = info_container,
+        text = "包大小:150084字节",
+        x = 150,
+        y = 80,
+        w = 140,
+        h = 20,
+        font_size = 14,
+        color = 0x666666,
+    })
+
+    -- 操作按钮区
+    -- 开始刷机按钮
+    local start_reflash_btn = airui.button({
+        parent = scroll_container,
+        x = 335,
+        y = 110,
+        w = 120,
+        h = 35,
+        text = "开始刷机",
+        style = { bg_color = 0x2B6FF1,border_color = 0x2B6FF1, text_color = 0xFFFFFF, radius = 8 },
+        on_click = function(self)
+            log.info("reflash_start", "开始刷机按钮被点击")
+        end
+    })
+
+    -- 停止按钮
+    local stop_btn = airui.button({
+        parent = scroll_container,
+        x = 335,
+        y = 180,
+        w = 120,
+        h = 35,
+        text = "停止",
+        on_click = function(self)
+            log.info("reflash_stop", "停止按钮被点击")
+        end
+    })
+    ---------------------------------------------------
+
+    -- 底部信息
+    common_ui.create_status_bar(main_container)
+end
+
+-- 初始化页面
+function tsb_reflash_page.init(params)
+    tsb_reflash_page.create_ui()
+end
+
+-- 清理页面
+function tsb_reflash_page.cleanup()
+    -- 停止定时器
+    common_ui.cleanup()
+    if main_container then
+        main_container:destroy()
+        main_container = nil
+    end
+end
+
+return tsb_reflash_page

+ 565 - 0
tsb_ui/tsb_test_page.lua

@@ -0,0 +1,565 @@
+--[[
+@module  tsb_test_page
+@summary 报税口演示页面
+@version 1.0
+@date    2026.03.04
+@author  李一玮
+@usage
+本文件是报税口演示页面,展示报税口的各种用法。
+]]
+
+local tsb_test_page = {}
+
+-- 页面UI元素
+local main_container = nil
+local common_ui = require("tsb_common_page")
+
+-- 创建UI
+function tsb_test_page.create_ui()
+    main_container = airui.container({
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 320,
+        color = 0xF5F5F5,
+    })
+    --------------------- 标题栏 ------------------------
+    local title_bar = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 30,
+        color = 0x007AFF,
+    })
+
+    airui.label({
+        parent = title_bar,
+        text = "报税口",
+        x = 210,
+        y = 8,
+        w = 80,
+        h = 20,
+        font_size = 16,
+        color = 0xFFFFFF,
+    })
+    
+    -- 标题栏公共信息展示
+    common_ui.add_battery_display(title_bar)
+    common_ui.create_back_button(title_bar, tsb_test_page.cleanup)
+
+    ---------------------------------------------------
+
+    -- 滚动容器
+    local scroll_container = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 30,
+        w = 480,
+        h = 270,
+        color = 0xF5F5F5,
+    })
+
+    --common_ui.add_background_png(scroll_container)
+
+    local keyboard1 = airui.keyboard({
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 160,                        -- x, y, 键盘默认打开ALIGN_BOTTOM_MID,位置从中下方开始计算
+        mode = "numeric",               -- 键盘模式,可选 "text"/"upper"/"lower"/"numeric"
+        auto_hide = true,               -- 自动隐藏键盘
+        preview = true,          
+        preview_height = 35,
+        bg_color = 0xf1f1f1,            -- 键盘背景颜色为灰色,可选,不设置则透明
+        on_commit = function()          -- 确认事件回调,只有在按下确认键时才会触发
+            log.info("keyboard", "commit")
+        end
+    })
+
+    --------------------- 接口号 ------------------------
+    airui.label({
+        parent = scroll_container,
+        text = "接口号:",
+        x = 5,
+        y = 17,
+        w = 55,
+        h = 20,
+        font_size = 15,
+    })
+
+    local tax_dropdown = airui.dropdown({
+        parent = scroll_container,
+        x = 60,
+        y = 7,
+        w = 55,
+        h = 35,
+        options = {"A", "B"},
+        default_index = 0,
+        on_change = function(self,idx)
+            -- local texts = {"选项1", "选项2", "选项3", "选项4", "选项5"}
+            -- selected_label1:set_text("当前选中: " .. texts[idx + 1])
+        end
+    })
+    ---------------------------------------------------
+
+    --------------------- 枪号 ------------------------
+    airui.label({
+        parent = scroll_container,
+        text = "枪号:",
+        x = 120,
+        y = 17,
+        w = 40,
+        h = 20,
+        font_size = 15,
+    })
+
+    local gunnum_input = airui.textarea({
+        parent = scroll_container,
+        x = 160,
+        y = 7,
+        w = 55,
+        h = 35,
+        text = "1",
+        -- placeholder = "...",
+        max_len = 1,
+        keyboard = keyboard1
+    })
+    ---------------------------------------------------
+
+    
+    --------------------- 新国标开关 -------------------
+    airui.label({
+        parent = scroll_container,
+        text = "新国标",
+        x = 217,
+        y = 17,
+        w = 50,
+        h = 20,
+        font_size = 15,
+    })
+
+    local switch_new_tax = airui.switch({
+        parent = scroll_container,
+        x = 267,
+        y = 12,
+        w = 50,
+        h = 25,
+        checked = false,
+        on_change = function(self)
+            log.info("NEW_TAX", self:get_state() and "开启" or "关闭")
+        end
+    })
+    ---------------------------------------------------
+
+    -------------------- 新国标报税口 ------------------
+    airui.label({
+        parent = scroll_container,
+        text = "新国标报税口",
+        x = 320,
+        y = 17,
+        w = 95,
+        h = 40,
+        font_size = 15,
+    })
+
+    local new_tax_num_input = airui.textarea({
+        parent = scroll_container,
+        x = 415,
+        y = 7,
+        w = 55,
+        h = 35,
+        text = "1",
+        -- placeholder = "...",
+        max_len = 1,
+        keyboard = keyboard1
+    })
+    ---------------------------------------------------
+
+    ----------------- 查询税控序列号区域 ---------------
+    local tax_serial_container = airui.container({
+        parent = scroll_container,
+        x = 10,
+        y = 50,
+        w = 160,
+        h = 210,
+        color = 0xFFFFFF,
+        radius = 5,
+    })
+
+    local tax_serial_btn = airui.button({
+        parent = tax_serial_container,
+        x = 20,
+        y = 12,
+        w = 120,
+        h = 35,
+        text = "查询税控序列号",
+        style = { bg_color = 0x2B6FF1,border_color = 0x2B6FF1, text_color = 0xFFFFFF, radius = 8 },
+        on_click = function(self)
+            log.info("button", "基本按钮被点击")
+        end
+    })
+
+    -- 查询结果显示
+    airui.label({
+        parent = tax_serial_container,
+        text = "税控序列号:",
+        x = 10,
+        y = 60,
+        w = 120,
+        h = 20,
+        font_size = 14,
+    })
+
+    local label_tax_serial = airui.label({
+        parent = tax_serial_container,
+        text = "0123456789",
+        x = 10,
+        y = 80,
+        w = 150,
+        h = 20,
+        font_size = 14,
+    })
+
+    airui.label({
+        parent = tax_serial_container,
+        text = "厂家:",
+        x = 10,
+        y = 110,
+        w = 40,
+        h = 20,
+        font_size = 14,
+    })
+
+    local label_company = airui.label({
+        parent = tax_serial_container,
+        text = "拓盛",
+        x = 50,
+        y = 110,
+        w = 90,
+        h = 20,
+        font_size = 14,
+    })
+
+    airui.label({
+        parent = tax_serial_container,
+        text = "枪个数:",
+        x = 10,
+        y = 150,
+        w = 60,
+        h = 20,
+        font_size = 14,
+    })
+
+    local label_gunnum = airui.label({
+        parent = tax_serial_container,
+        text = "2",
+        x = 70,
+        y = 150,
+        w = 80,
+        h = 20,
+        font_size = 14,
+    })
+
+    airui.label({
+        parent = tax_serial_container,
+        text = "执行结果:",
+        x = 10,
+        y = 190,
+        w = 70,
+        h = 20,
+        font_size = 14,
+    })
+
+    local label_tax_serial_result = airui.label({
+        parent = tax_serial_container,
+        text = "执行成功",
+        x = 80,
+        y = 190,
+        w = 80,
+        h = 20,
+        font_size = 14,
+    })
+    ---------------------------------------------------
+
+    ----------------- 日累月累总累 ------------------
+    local tax_msg_container = airui.container({
+        parent = scroll_container,
+        x = 180,
+        y = 50,
+        w = 290,
+        h = 210,
+        color = 0xFFFFFF,
+        radius = 5,
+    })
+
+    local year_input = airui.textarea({
+        parent = tax_msg_container,
+        x = 10,
+        y = 7,
+        w = 70,
+        h = 35,
+        text = os.date('%Y'),
+        -- placeholder = "...",
+        max_len = 4,
+        keyboard = keyboard1
+    })
+
+    airui.label({
+        parent = tax_msg_container,
+        text = "年",
+        x = 85,
+        y = 18,
+        w = 20,
+        h = 20,
+        font_size = 15,
+    })
+
+    local month_dropdown = airui.dropdown({
+        parent = tax_msg_container,
+        x = 105,
+        y = 7,
+        w = 65,
+        h = 35,
+        options = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"},
+        default_index = 0,
+        on_change = function(self,idx)
+            -- local texts = {"选项1", "选项2", "选项3", "选项4", "选项5"}
+            -- selected_label1:set_text("当前选中: " .. texts[idx + 1])
+        end
+    })
+
+    airui.label({
+        parent = tax_msg_container,
+        text = "月",
+        x = 175,
+        y = 18,
+        w = 20,
+        h = 20,
+        font_size = 15,
+    })
+
+    local day_dropdown = airui.dropdown({
+        parent = tax_msg_container,
+        x = 195,
+        y = 7,
+        w = 65,
+        h = 35,
+        options = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13","14","15","16","17","18","19","20","21","22","23","24","25","26","27","28","29","30","31"},
+        default_index = 0,
+        on_change = function(self,idx)
+            -- local texts = {"选项1", "选项2", "选项3", "选项4", "选项5"}
+            -- selected_label1:set_text("当前选中: " .. texts[idx + 1])
+        end
+    })
+
+    airui.label({
+        parent = tax_msg_container,
+        text = "日",
+        x = 265,
+        y = 18,
+        w = 20,
+        h = 20,
+        font_size = 15,
+    })
+
+    
+
+    local check_total_btn = airui.button({
+        parent = tax_msg_container,
+        x = 10,
+        y = 50,
+        w = 120,
+        h = 35,
+        text = "查当次及总累",
+        style = { bg_color = 0x2B6FF1,border_color = 0x2B6FF1, text_color = 0xFFFFFF, radius = 8 },
+        on_click = function(self)
+            log.info("button", "基本按钮被点击")
+        end
+    })
+
+    local check_day_btn = airui.button({
+        parent = tax_msg_container,
+        x = 140,
+        y = 50,
+        w = 60,
+        h = 35,
+        text = "查日累",
+        style = { bg_color = 0x2B6FF1,border_color = 0x2B6FF1, text_color = 0xFFFFFF, radius = 8 },
+        on_click = function(self)
+            log.info("button", "基本按钮被点击")
+        end
+    })
+
+    local check_month_btn = airui.button({
+        parent = tax_msg_container,
+        x = 210,
+        y = 50,
+        w = 60,
+        h = 35,
+        text = "查月累",
+        style = { bg_color = 0x2B6FF1,border_color = 0x2B6FF1, text_color = 0xFFFFFF, radius = 8 },
+        on_click = function(self)
+            log.info("button", "基本按钮被点击")
+        end
+    })
+
+
+    airui.label({
+        parent = tax_msg_container,
+        text = "金额:",
+        x = 10,
+        y = 100,
+        w = 50,
+        h = 20,
+        font_size = 14,
+    })
+
+    local label_money = airui.label({
+        parent = tax_msg_container,
+        text = "9999.99",
+        x = 50,
+        y = 100,
+        w = 70,
+        h = 20,
+        font_size = 14,
+    })
+
+    airui.label({
+        parent = tax_msg_container,
+        text = "油量:",
+        x = 10,
+        y = 120,
+        w = 50,
+        h = 20,
+        font_size = 14,
+    })
+
+    local label_oil = airui.label({
+        parent = tax_msg_container,
+        text = "9999.99",
+        x = 50,
+        y = 120,
+        w = 70,
+        h = 20,
+        font_size = 14,
+    })
+
+    airui.label({
+        parent = tax_msg_container,
+        text = "单价:",
+        x = 10,
+        y = 140,
+        w = 50,
+        h = 20,
+        font_size = 14,
+    })
+
+    local label_price = airui.label({
+        parent = tax_msg_container,
+        text = "99.99",
+        x = 50,
+        y = 140,
+        w = 50,
+        h = 20,
+        font_size = 14,
+    })
+
+    airui.label({
+        parent = tax_msg_container,
+        text = "是否密文:",
+        x = 10,
+        y = 160,
+        w = 70,
+        h = 20,
+        font_size = 14,
+    })
+
+    local label_encryption = airui.label({
+        parent = tax_msg_container,
+        text = "是",
+        x = 80,
+        y = 160,
+        w = 30,
+        h = 20,
+        font_size = 14,
+    })
+
+    airui.label({
+        parent = tax_msg_container,
+        text = "总金额",
+        x = 185,
+        y = 100,
+        w = 70,
+        h = 20,
+        font_size = 14,
+    })
+
+    local label_total_money = airui.label({
+        parent = tax_msg_container,
+        text = "1234567891234.99",
+        x = 140,
+        y = 120,
+        w = 150,
+        h = 30,
+        font_size = 14,
+    })
+
+    airui.label({
+        parent = tax_msg_container,
+        text = "总油量",
+        x = 185,
+        y = 140,
+        w = 70,
+        h = 20,
+        font_size = 14,
+    })
+
+    local label_total_oil = airui.label({
+        parent = tax_msg_container,
+        text = "1234567891234.99",
+        x = 140,
+        y = 160,
+        w = 150,
+        h = 30,
+        font_size = 14,
+    })
+
+    airui.label({
+        parent = tax_msg_container,
+        text = "执行结果:",
+        x = 10,
+        y = 190,
+        w = 70,
+        h = 20,
+        font_size = 14,
+    })
+
+    local label_check_result = airui.label({
+        parent = tax_msg_container,
+        text = "执行成功",
+        x = 80,
+        y = 190,
+        w = 120,
+        h = 20,
+        font_size = 14,
+    })
+
+    -- 底部信息栏
+    common_ui.create_status_bar(main_container)
+end
+
+-- 初始化页面
+function tsb_test_page.init(params)
+    tsb_test_page.create_ui()
+end
+
+-- 清理页面
+function tsb_test_page.cleanup()
+    if main_container then
+        main_container:destroy()
+        main_container = nil
+    end
+end
+
+return tsb_test_page

+ 271 - 0
tsb_ui/tsb_tq_page.lua

@@ -0,0 +1,271 @@
+--[[
+@module  tsb_tq_page
+@summary 提枪信号演示页面
+@version 1.0
+@date    2026.03.04
+@author  李一玮
+@usage
+本文件是提枪信号演示页面,展示提枪信号的各种用法。
+]]
+
+local tsb_tq_page = {}
+
+-- 页面UI元素
+local main_container = nil
+local common_ui = require("tsb_common_page")
+
+-- 创建UI
+function tsb_tq_page.create_ui()
+    main_container = airui.container({
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 320,
+        color = 0xF5F5F5,
+    })
+
+    --------------------- 标题栏 ------------------------
+    local title_bar = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 30,
+        color = 0xF44336,
+    })
+
+    airui.label({
+        parent = title_bar,
+        text = "提枪信号",
+        x = 200,
+        y = 8,
+        w = 80,
+        h = 20,
+        font_size = 16,
+        color = 0xFFFFFF,
+    })
+
+    -- 标题栏公共信息展示
+    common_ui.add_battery_display(title_bar)
+    common_ui.create_back_button(title_bar, tsb_tq_page.cleanup)
+    ---------------------------------------------------
+
+    -- 滚动容器
+    local scroll_container = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 30,
+        w = 480,
+        h = 270,
+        color = 0xF5F5F5,
+    })
+
+    local keyboard1 = airui.keyboard({
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 160,                        -- x, y, 键盘默认打开ALIGN_BOTTOM_MID,位置从中下方开始计算
+        mode = "numeric",               -- 键盘模式,可选 "text"/"upper"/"lower"/"numeric"
+        auto_hide = true,               -- 自动隐藏键盘
+        preview = true,          
+        preview_height = 35,
+        bg_color = 0xf1f1f1,            -- 键盘背景颜色为灰色,可选,不设置则透明
+        on_commit = function()          -- 确认事件回调,只有在按下确认键时才会触发
+            log.info("keyboard", "commit")
+        end
+    })
+
+    ------------------- 提枪信号输入 ------------------
+    local tq_input_container = airui.container({
+        parent = scroll_container,
+        x = 10,
+        y = 5,
+        w = 200,
+        h = 260,
+        color = 0xFFFFFF,
+        radius = 5,
+    })
+
+    airui.label({
+        parent = tq_input_container,
+        text = "提枪信号输入",
+        x = 35,
+        y = 10,
+        w = 140,
+        h = 30,
+        font_size = 18,
+    })
+
+    airui.label({
+        parent = tq_input_container,
+        text = "油枪状态:",
+        x = 20,
+        y = 80,
+        w = 80,
+        h = 30,
+        font_size = 16,
+    })
+
+    local label_cur_vol = airui.label({
+        parent = tq_input_container,
+        text = "抬枪",
+        x = 100,
+        y = 80,
+        w = 60,
+        h = 30,
+        font_size = 16,
+    })
+
+    airui.label({
+        parent = tq_input_container,
+        text = "实时电压:",
+        x = 20,
+        y = 140,
+        w = 100,
+        h = 30,
+        font_size = 16,
+    })
+
+    local label_pulse_count = airui.label({
+        parent = tq_input_container,
+        text = "5.0V",
+        x = 100,
+        y = 140,
+        w = 70,
+        h = 30,
+        font_size = 16,
+    })
+    ----------------------------------------------------
+
+    ------------------- 提枪信号输出 ------------------
+    local tq_output_container = airui.container({
+        parent = scroll_container,
+        x = 220,
+        y = 5,
+        w = 250,
+        h = 260,
+        color = 0xFFFFFF,
+        radius = 5,
+    })
+
+    airui.label({
+        parent = tq_output_container,
+        text = "提枪信号输出",
+        x = 60,
+        y = 10,
+        w = 140,
+        h = 30,
+        font_size = 18,
+    })
+
+    -- 输出电压
+    airui.label({
+        parent = tq_output_container,
+        text = "输出电压:",
+        x = 20,
+        y = 80,
+        w = 80,
+        h = 30,
+        font_size = 16,
+    })
+
+    local voltage_output = airui.textarea({
+        parent = tq_output_container,
+        x = 100,
+        y = 72,
+        w = 60,
+        h = 35,
+        text = "5",
+        max_len = 2,
+        keyboard = keyboard1
+    })
+
+    airui.label({
+        parent = tq_output_container,
+        text = "V",
+        x = 165,
+        y = 80,
+        w = 30,
+        h = 30,
+        font_size = 16,
+    })
+
+    --------------------- 新国标开关 -------------------
+    airui.label({
+        parent = tq_output_container,
+        text = "周期输出:",
+        x = 20,
+        y = 140,
+        w = 80,
+        h = 20,
+        font_size = 16,
+    })
+
+    local switch_tq = airui.switch({
+        parent = tq_output_container,
+        x = 100,
+        y = 133,
+        w = 55,
+        h = 25,
+        checked = false,
+        on_change = function(self)
+            log.info("NEW_TAX", self:get_state() and "开启" or "关闭")
+        end
+    })
+
+    airui.label({
+        parent = tq_output_container,
+        text = "开启后,提枪信号将按照3s的周期切换输出上面设置的电压和0V。",
+        x = 20,
+        y = 170,
+        w = 200,
+        h = 40,
+        font_size = 12,
+        color = 0x757575,
+    })
+
+    -- 按钮
+    local start_btn = airui.button({
+        parent = tq_output_container,   
+        x = 40,
+        y = 215,    
+        w = 80,
+        h = 35,
+        text = "开始",
+        style = { bg_color = 0x2B6FF1,border_color = 0x2B6FF1, text_color = 0xFFFFFF, radius = 8 },
+        on_click = function(self)
+            log.info("button", "开始按钮被点击")
+        end
+    })
+
+    local stop_btn = airui.button({
+        parent = tq_output_container,   
+        x = 130,
+        y = 215,
+        w = 80,
+        h = 35,
+        text = "停止",
+        on_click = function(self)
+            log.info("button", "停止按钮被点击")
+        end
+    })
+    ----------------------------------------------------
+
+    -- 底部信息
+    common_ui.create_status_bar(main_container)
+end
+
+-- 初始化页面
+function tsb_tq_page.init(params)
+    tsb_tq_page.create_ui()
+end
+
+-- 清理页面
+function tsb_tq_page.cleanup()
+    if main_container then
+        main_container:destroy()
+        main_container = nil
+    end
+end
+
+return tsb_tq_page

+ 186 - 0
tsb_ui/tsb_ui_main.lua

@@ -0,0 +1,186 @@
+--[[
+@module  ui_main
+@summary 调试宝UI主程序,负责页面管理和主循环
+@version 1.0
+@date    2026.03.04
+@author  李一玮
+@usage
+本文件管理所有调试宝UI页面,包括主页和各个演示页面。
+]]
+
+require("tsb_login_page")
+require("tsb_home_page")
+require("tsb_bsk_page")
+require("tsb_tq_page")
+require("tsb_bmq_page")
+require("tsb_ywy_page")
+require("tsb_lora_page")
+require("tsb_devlog_page")
+require("tsb_485log_page")
+require("tsb_mqtt_page")
+require("tsb_firm_page")
+require("tsb_upgrade_page")
+require("tsb_reflash_page")
+require("tsb_light_page")
+require("tsb_devinfo_page")
+require("tsb_update_page")
+require("tsb_wlan_page")
+require("tsb_game_page")
+require("tsb_wavein_page")
+require("tsb_waveout_page")
+require("tsb_ywy_1_page")
+require("tsb_ywy_2_page")
+require("tsb_232log_page")
+require("tsb_channel_page")
+require("tsb_log_page")
+require("tsb_help_page")
+require("tsb_waveform_page")
+require("tsb_test_page")
+
+
+
+-- 当前显示的页面
+local current_page = nil
+local page_stack = {} -- 页面,用于返回功能
+-- local frame_time = 20 -- 主循环刷新间隔,单位ms
+
+
+-- 页面定义
+local pages = {
+    tsb_login_page  = "tsb_login_page",                 -- 调试宝登录页面
+    tsb_home_page   = "tsb_home_page",                  -- 调试宝主界面
+    tsb_bsk_page    = "tsb_bsk_page",                   -- 报税口页面
+    tsb_tq_page     = "tsb_tq_page",                    -- 提枪信号页面
+    tsb_bmq_page    = "tsb_bmq_page",                   -- 编码器页面
+    tsb_ywy_page    = "tsb_ywy_page",                   -- 液位仪页面
+    tsb_lora_page   = "tsb_lora_page",                  -- 无线局域网监测页面
+    tsb_devlog_page = "tsb_devlog_page",                -- 设备日志监测页面
+    tsb_485log_page = "tsb_485log_page",                -- 485日志监测页面
+    tsb_mqtt_page   = "tsb_mqtt_page",                  -- MQTT配置页面
+    tsb_firm_page   = "tsb_firm_page",                  -- 固件包下载页面
+    tsb_upgrade_page = "tsb_upgrade_page",              -- 升级演示页面
+    tsb_reflash_page = "tsb_reflash_page",              -- 刷机页面
+    tsb_light_page   = "tsb_light_page",                -- 亮度页面   
+    tsb_devinfo_page = "tsb_devinfo_page",              -- 设备信息页面 
+    tsb_update_page  = "tsb_update_page",               -- 更新页面
+    tsb_wlan_page    = "tsb_wlan_page",                 -- 网络配置页面
+    tsb_wavein_page  = "tsb_wavein_page",               -- 波形输入页面
+    tsb_waveout_page = "tsb_waveout_page",             -- 波形输出页面
+    tsb_ywy_1_page   = "tsb_ywy_1_page",                -- 液位仪页面1
+    tsb_ywy_2_page   = "tsb_ywy_2_page",                -- 液位仪页面2
+    tsb_232log_page  = "tsb_232log_page",               -- 232日志监测页面
+    tsb_channel_page = "tsb_channel_page",              -- 信道切换页面
+    tsb_log_page     = "tsb_log_page",                  -- 本机日志页面
+    tsb_help_page    = "tsb_help_page",                 -- 帮助页面
+    tsb_waveform_page = "tsb_waveform_page",            -- 波形页面
+    tsb_test_page    = "tsb_test_page",                 -- 测试页面
+    -- tsb_game_page    = "tsb_game_page",                   -- 游戏页面
+}
+
+-- 显示指定页面
+local function show_page(page_name, params)
+    -- 保存当前页面
+    if not params or not (params.from_back or params.from_home) then
+        if current_page then
+            table.insert(page_stack, {
+                name = current_page.name,
+                instance = current_page.instance
+            })
+        end
+    end
+
+    -- 清理当前页面
+    if current_page and current_page.instance.cleanup then
+        current_page.instance.cleanup()
+    end
+
+    -- 加载新页面
+    local module_name = pages[page_name]
+    if not module_name then
+        log.error("tsb_ui_main", "页面不存在:", page_name)
+        return false
+    end
+
+    local page_module = require(module_name)
+
+    -- 初始化页面
+    if page_module.init then
+        page_module.init(params)
+    end
+
+    -- 更新当前页面信息
+    current_page = {
+        name = page_name,
+        module = module_name,
+        instance = page_module
+    }
+
+    log.info("tsb_ui_main", "切换到页面:", page_name)
+    return true
+end
+
+-- 返回上一个页面
+local function go_back()
+    if #page_stack > 0 then
+        local prev_page = table.remove(page_stack)
+        if prev_page.instance and prev_page.instance.show then
+            prev_page.instance.show()
+            current_page = prev_page
+            log.info("tsb_ui_main", "返回页面:", prev_page.name)
+            return true
+        else
+            return show_page(prev_page.name, { from_back = true })
+        end
+    else
+        return show_page("tsb_login_page", { from_back = true })
+    end
+end
+
+-- 返回到首页面
+local function go_home()
+    -- 先判断是否已在首页
+    if current_page and current_page.name == "tsb_home_page" then
+        log.info("tsb_ui_main", "当前已在首页,无需跳转")
+        return true
+    end
+
+    -- 清空页面栈
+    page_stack = {}
+
+    -- 调用show_page,用from_home标识
+    return show_page("tsb_home_page", { from_home = true })
+end
+
+-- 主任务函数
+local function tsb_ui_main_task()
+
+    -- 3V3供电使能
+    gpio.setup(153,1, gpio.PULLUP)
+    -- 10V供电使能
+    gpio.setup(147,1, gpio.PULLUP)
+
+    -- gpio.setup(140,1, gpio.PULLUP)
+
+    -- gpio.setup(30,0)
+    -- gpio.set(30, gpio.HIGH)
+
+    -- -- 开启屏幕供电
+    -- gpio.setup(32,0) 
+    -- gpio.set(32,0)
+
+    -- 初始化硬件
+    lcd_drv.init()
+    tp_drv.init()
+
+    -- 显示主页
+    show_page("tsb_login_page")
+    
+end
+
+-- 全局函数,方便页面调用
+_G.show_page = show_page
+_G.go_back = go_back
+_G.go_home = go_home
+
+-- 启动UI主任务
+sys.taskInit(tsb_ui_main_task)

+ 94 - 0
tsb_ui/tsb_update_page.lua

@@ -0,0 +1,94 @@
+--[[
+@module  tsb_update_page
+@summary 更新页面
+@version 1.0
+@date    2026.03.04
+@author  李一玮
+@usage
+本文件是更新页面,展示更新的各种用法。
+]]
+
+local tsb_update_page = {}
+
+-- 页面UI元素
+local main_container = nil
+local common_ui = require("tsb_common_page")
+
+-- 创建UI
+function tsb_update_page.create_ui()
+    main_container = airui.container({
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 320,
+        color = 0xF5F5F5,
+    })
+
+    --------------------- 标题栏 ------------------------
+    local title_bar = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 30,
+        color = 0xA5D6A7,
+    })
+
+    airui.label({
+        parent = title_bar,
+        text = "本机升级",
+        x = 200,
+        y = 8,
+        w = 80,
+        h = 20,
+        font_size = 16,
+        color = 0xFFFFFF,
+    })
+
+    -- 标题栏公共信息展示
+    common_ui.add_battery_display(title_bar)
+    common_ui.create_back_button(title_bar, tsb_update_page.cleanup)
+    ---------------------------------------------------
+
+    -- 滚动容器
+    local scroll_container = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 30,
+        w = 480,
+        h = 270,
+        color = 0xFFFFFF,
+    })
+
+    --------------------- 检查更新 -----------------------
+    local update_btn = airui.button({
+        parent = scroll_container,
+        x = 15,
+        y = 15,
+        w = 120,
+        h = 40,
+        text = "检查设备更新",
+        on_click = function()
+            log.info("检查设备更新")
+        end
+    })
+
+
+    -- 底部信息
+    common_ui.create_status_bar(main_container)
+end
+
+-- 初始化页面
+function tsb_update_page.init(params)
+    tsb_update_page.create_ui()
+end
+
+-- 清理页面
+function tsb_update_page.cleanup()
+    if main_container then
+        main_container:destroy()
+        main_container = nil
+    end
+end
+
+return tsb_update_page

+ 431 - 0
tsb_ui/tsb_upgrade_page.lua

@@ -0,0 +1,431 @@
+--[[
+@module  tsb_upgrade_page
+@summary 升级演示页面
+@version 1.0
+@date    2026.03.05
+@author  李一玮
+@usage
+本文件是升级演示页面,展示升级演示的各种用法。
+]]
+
+local tsb_upgrade_page = {}
+
+-- 页面UI元素
+local main_container = nil
+local common_ui = require("tsb_common_page")
+
+-- 创建UI
+function tsb_upgrade_page.create_ui()
+    main_container = airui.container({
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 320,
+        color = 0xF5F5F5,
+    })
+
+    --------------------- 标题栏 ------------------------
+    local title_bar = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 30,
+        color = 0x66BB6A,
+    })
+
+    airui.label({
+        parent = title_bar,
+        text = "升级界面",
+        x = 200,
+        y = 8,
+        w = 80,
+        h = 20,
+        font_size = 16,
+        color = 0xFFFFFF,
+    })
+
+    -- 标题栏公共信息展示
+    common_ui.add_battery_display(title_bar)
+    common_ui.create_back_button(title_bar, tsb_upgrade_page.cleanup)
+    ---------------------------------------------------
+
+    -- 滚动容器
+    local scroll_container = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 30,
+        w = 480,
+        h = 270,
+        color = 0xFFFFFF,
+    })
+
+    local keyboard1 = airui.keyboard({
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 160,                        -- x, y, 键盘默认打开ALIGN_BOTTOM_MID,位置从中下方开始计算
+        mode = "numeric",               -- 键盘模式,可选 "text"/"upper"/"lower"/"numeric"
+        auto_hide = true,               -- 自动隐藏键盘
+        preview = true,          
+        preview_height = 35,
+        bg_color = 0xf1f1f1,            -- 键盘背景颜色为灰色,可选,不设置则透明
+        on_commit = function()          -- 确认事件回调,只有在按下确认键时才会触发
+            log.info("keyboard", "commit")
+        end
+    })
+
+    -- 设备类型标签
+    airui.label({
+        parent = scroll_container,
+        text = "设备类型:",
+        x = 10,
+        y = 12,
+        w = 80,
+        h = 30,
+        font_size = 16,
+    })
+
+    -- 设备类型下拉框
+    local device_type_dropdown = airui.dropdown({
+        parent = scroll_container,
+        x = 90,
+        y = 5,
+        w = 100,
+        h = 30,
+        options = {"0101", "0102", "0201", "0202", "0301", "0302", "0103", "0104", "0902", "0904"},
+        default_index = 0,
+        on_change = function(self,idx)
+            log.info("upgrade_device_type", "选择了设备类型:" .. self.options[idx + 1])
+        end
+    })
+
+    -- 当前lora信道标签
+    airui.label({
+        parent = scroll_container,
+        text = "当前lora信道:",
+        x = 255,
+        y = 12,
+        w = 120,
+        h = 30,
+        font_size = 16,
+    })
+
+    -- 当前lora信道值
+    local current_channel_label = airui.label({
+        parent = scroll_container,
+        text = "1",
+        x = 400,
+        y = 12,
+        w = 30,
+        h = 30,
+        font_size = 16,
+    })
+
+    airui.label({
+        parent = scroll_container,
+        text = "设置lora信道:",
+        x = 255,
+        y = 50,
+        w = 120,
+        h = 30,
+        font_size = 16,
+    })
+
+    -- 设置lora信道下拉框
+    local channel_dropdown = airui.dropdown({
+        parent = scroll_container,
+        x = 380,
+        y = 45,
+        w = 70,
+        h = 30,
+        options = {"1", "2", "3", "4", "5", "6"},
+        default_index = 0, -- 默认选择1
+        on_change = function(self,idx)
+            log.info("mqtt_channel", "选择了信道:" .. self.options[idx + 1])
+        end
+    })
+
+    -- 设备SN标签
+    airui.label({
+        parent = scroll_container,
+        text = "设备SN:",
+        x = 10,
+        y = 50,
+        w = 70,
+        h = 30,
+        font_size = 16,
+    })
+
+    -- 设备SN输入框
+    local device_sn_input = airui.textarea({
+        parent = scroll_container,
+        x = 90,
+        y = 40,
+        w = 100,
+        h = 35,
+        text = "",
+        keyboard = keyboard1
+    })
+    ---------------------------------------------------
+
+    -- 先声明开关变量
+    local target_upgrade_switch
+    local broadcast_upgrade_switch
+
+    -- 定向升级标签
+    airui.label({
+        parent = scroll_container,
+        text = "定向升级",
+        x = 10,
+        y = 92,
+        w = 70,
+        h = 30,
+        font_size = 16,
+    })
+
+    -- 定向升级开关
+    target_upgrade_switch = airui.switch({
+        parent = scroll_container,
+        x = 80,
+        y = 87,
+        w = 55,
+        h = 25,
+        checked = true, -- 默认定向升级开
+        on_change = function(self)
+            local checked = self:get_state()
+            log.info("定向升级开关", checked and "开启" or "关闭")
+            -- 互斥逻辑:如果定向升级开启,则关闭广播升级
+            if checked and broadcast_upgrade_switch then
+                broadcast_upgrade_switch:set_state(false)
+            end
+        end
+    })
+
+    -- 广播升级标签
+    airui.label({
+        parent = scroll_container,
+        text = "广播升级",
+        x = 160,
+        y = 92,
+        w = 70,
+        h = 30,
+        font_size = 16,
+    })
+
+    -- 广播升级开关
+    broadcast_upgrade_switch = airui.switch({
+        parent = scroll_container,
+        x = 230,
+        y = 87,
+        w = 55,
+        h = 25,
+        checked = false, -- 默认广播升级关
+        on_change = function(self)
+            local checked = self:get_state()
+            log.info("广播升级开关", checked and "开启" or "关闭")
+            -- 互斥逻辑:如果广播升级开启,则关闭定向升级
+            if checked and target_upgrade_switch then
+                target_upgrade_switch:set_state(false)
+            end
+        end
+    })
+    ---------------------------------------------------
+
+    --------------------- 第四行控制区 ------------------------
+    -- 提示信息容器
+    local info_container = airui.container({
+        parent = scroll_container,
+        x = 10,
+        y = 130,
+        w = 250,
+        h = 130,
+        color = 0xF5F5F5,
+        radius = 5,
+    })
+
+    -- 提示信息
+    airui.label({
+        parent = info_container,
+        text = "提示信息:",
+        x = 10,
+        y = 10,
+        w = 230,
+        h = 20,
+        font_size = 15,
+        color = 0x66BB6A,
+    })
+
+    airui.label({
+        parent = info_container,
+        text = "1. 确保设备已连接到网络",
+        x = 10,
+        y = 40,
+        w = 230,
+        h = 20,
+        font_size = 12,
+        color = 0x666666,
+    })
+
+    airui.label({
+        parent = info_container,
+        text = "2. 选择正确的设备类型和SN",
+        x = 10,
+        y = 60,
+        w = 230,
+        h = 20,
+        font_size = 12,
+        color = 0x666666,
+    })
+
+    airui.label({
+        parent = info_container,
+        text = "3. 选择升级方式(定向或广播)",
+        x = 10,
+        y = 80,
+        w = 230,
+        h = 20,
+        font_size = 12,
+        color = 0x666666,
+    })
+
+    airui.label({
+        parent = info_container,
+        text = "4. ......",
+        x = 10,
+        y = 100,
+        w = 230,
+        h = 20,
+        font_size = 12,
+        color = 0x666666,
+    })
+
+    -- 操作按钮区
+    -- 开始升级按钮
+    local start_upgrade_btn = airui.button({
+        parent = scroll_container,
+        x = 300,
+        y = 140,
+        w = 150,
+        h = 30,
+        text = "开始升级",
+        style = { bg_color = 0x2B6FF1,border_color = 0x2B6FF1, text_color = 0xFFFFFF, radius = 8 },
+        on_click = function(self)
+            log.info("upgrade_start", "开始升级按钮被点击")
+        end
+    })
+
+    -- 停止升级按钮
+    local stop_upgrade_btn = airui.button({
+        parent = scroll_container,
+        x = 300,
+        y = 180,
+        w = 150,
+        h = 30,
+        text = "停止升级",
+        on_click = function(self)
+            log.info("upgrade_stop", "停止升级按钮被点击")
+        end
+    })
+
+    -- 查看升级状态按钮
+    local check_status_btn = airui.button({
+        parent = scroll_container,
+        x = 300,
+        y = 220,
+        w = 150,
+        h = 30,
+        text = "查看升级状态",
+        on_click = function(self)
+            log.info("upgrade_check", "查看升级状态按钮被点击")
+            -- 弹出新窗口显示升级状态表格
+            local status_window = airui.container({
+                x = 20,
+                y = 35,
+                w = 440,
+                h = 255,
+                color = 0xFFFFFF,
+                radius = 5,
+                border_width = 2,
+                border_color = 0x66BB6A,
+            })
+
+            -- 窗口标题
+            airui.label({
+                parent = status_window,
+                text = "升级状态",
+                x = 190,
+                y = 15,
+                w = 80,
+                h = 30,
+                font_size = 16,
+                color = 0x66BB6A,--0xE91E63
+            })
+
+            -- 创建表格
+            local status_table = airui.table({
+                parent = status_window,
+                x = 10,
+                y = 40,
+                w = 420,
+                h = 200,
+                row_count = 6,
+                col_count = 5,
+                col_width = {70, 120, 120, 120, 120},
+            })
+
+            -- 设置表头
+            local headers = {"序号", "设备类型", "设备SN", "软件版本", "升级状态"}
+            for i, header in ipairs(headers) do
+                status_table:set_cell_text(0, i-1, header)
+            end
+
+            -- 示例数据
+            local status_data = {
+                {"1", "0101", "SN001", "V1.0.0", "升级中"},
+                {"2", "0202", "SN002", "V1.1.0", "已完成"},
+                {"3", "0201", "SN003", "V1.0.0", "失败"},
+            }
+
+            -- 填充表格数据
+            for i, data in ipairs(status_data) do
+                for j, value in ipairs(data) do
+                    status_table:set_cell_text(i, j-1, value)
+                end
+            end
+
+
+            -- 关闭按钮
+            local close_btn = airui.button({
+                parent = status_window,
+                x = 390,
+                y = 5,
+                w = 40,
+                h = 25,
+                text = "关闭",
+                on_click = function(self)
+                    status_window:destroy()
+                end
+            })
+        end
+    })
+    ---------------------------------------------------
+
+    -- 底部信息
+    common_ui.create_status_bar(main_container)
+end
+
+-- 初始化页面
+function tsb_upgrade_page.init(params)
+    tsb_upgrade_page.create_ui()
+end
+
+-- 清理页面
+function tsb_upgrade_page.cleanup()
+    if main_container then
+        main_container:destroy()
+        main_container = nil
+    end
+end
+
+return tsb_upgrade_page

+ 143 - 0
tsb_ui/tsb_waveform_page.lua

@@ -0,0 +1,143 @@
+--[[
+@module  tsb_waveform_page        
+@summary 简易示波器页面
+@version 1.0
+@date    2026.03.17
+@author  李一玮
+@usage
+本文件是简易示波器页面,展示232日志的各种用法。
+]]
+
+local tsb_waveform_page = {}
+
+-- 页面UI元素
+local main_container = nil
+local common_ui = require("tsb_common_page")
+
+-- 创建UI
+function tsb_waveform_page.create_ui()
+    main_container = airui.container({
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 320,
+        color = 0xF5F5F5,
+    })
+
+    --------------------- 标题栏 ------------------------
+    local title_bar = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 30,
+        color = 0xFFAB91,
+    })
+
+    airui.label({
+        parent = title_bar,
+        text = "简易示波器",
+        x = 190,
+        y = 8,
+        w = 200,
+        h = 20,
+        font_size = 16,
+        color = 0xFFFFFF,
+    })
+
+    -- 标题栏公共信息展示
+    common_ui.add_battery_display(title_bar)
+    common_ui.create_back_button(title_bar, tsb_waveform_page.cleanup)
+
+    -- 滚动容器
+    local scroll_container = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 30, 
+        w = 480,
+        h = 270,
+        color = 0xFFFFFF,
+    })
+
+    -- 创建两个容器卡片
+    local card_width = 140
+    local card_height = 80
+    local padding = 20
+    local y_offset = 20
+
+    -- 示波器输入卡片
+    local input_card = airui.container({
+        parent = scroll_container,
+        x = padding,
+        y = y_offset,
+        w = card_width,
+        h = card_height,
+        color = 0xFFD93D,
+        radius = 8,
+    })
+
+    -- 示波器输入标题
+    airui.label({
+        parent = input_card,
+        text = "波形输入",
+        x = 10,
+        y = 10,
+        w = card_width - 20,
+        h = 30,
+        font_size = 16,
+        color = 0x000000,
+    })
+
+    -- 点击事件
+    input_card:set_on_click(function()
+        log.info("waveform_page", "点击示波器输入卡片")
+        _G.show_page("tsb_wavein_page")
+    end)
+
+    -- 示波器输出卡片
+    local output_card = airui.container({
+        parent = scroll_container,
+        x = padding * 2 + card_width,
+        y = y_offset,
+        w = card_width,
+        h = card_height,
+        color = 0x72DDF7,
+        radius = 8,
+    })
+
+    -- 示波器输出标题
+    airui.label({
+        parent = output_card,
+        text = "波形输出",
+        x = 10,
+        y = 10,
+        w = card_width - 20,
+        h = 30,
+        font_size = 16,
+        color = 0x000000,
+    })
+
+    -- 点击事件
+    output_card:set_on_click(function()
+        log.info("waveform_page", "点击示波器输出卡片")
+        _G.show_page("tsb_waveout_page")
+    end)
+
+    -- 底部信息
+    common_ui.create_status_bar(main_container)
+end
+
+-- 初始化页面
+function tsb_waveform_page.init(params)
+    tsb_waveform_page.create_ui()
+end
+
+-- 清理页面
+function tsb_waveform_page.cleanup()
+    if main_container then
+        main_container:destroy()
+        main_container = nil
+    end
+end
+
+return tsb_waveform_page

+ 1802 - 0
tsb_ui/tsb_wavein_page.lua

@@ -0,0 +1,1802 @@
+--[[
+@module  tsb_wavein_page
+@summary 波形输入页面
+@version 1.0
+@date    2026.03.18
+@author  李一玮
+@usage
+本文件是波形输入页面,展示波形数据和控制功能。
+]]
+
+local tsb_wavein_page = {}
+
+-- 页面UI元素
+local main_container = nil
+local scroll_container = nil
+local chart_timer = nil          -- 数据推送定时器ID
+local page_active = false        -- 页面是否活跃,用于定时器安全
+local common_ui = require("tsb_common_page")
+
+local chart = nil              -- 图表对象
+local freq_label = nil         -- 频率显示标签
+local vmax_label = nil         -- 电压最大值标签
+local vmin_label = nil         -- 电压最小值标签
+local trigger_threshold_input = nil -- 触发阈值输入框
+local visible_btn = nil         -- 按钮可见性按钮
+local set_btn = nil             -- 设置按钮
+local reset_btn = nil           -- 重置按钮
+local label_btn = nil           -- 标签按钮
+local trigger_type_btn = nil    -- 触发类型按钮
+local trigger_mode_btn = nil    -- 触发方式按钮
+local pulse_width_input = nil   -- 脉宽触发输入框
+local time_scale_dropdown = nil -- 时间档位下拉框
+local run_control_btn = nil     -- 运行控制按钮
+
+-- 控制参数
+local time_scale = "2ms"        -- 时间档位
+local vertical_scale = "5V"     -- 垂直档位
+local trigger_threshold = "0"   -- 触发阈值
+local trigger_type = "上升沿"    -- 触发类型:上升沿、下降沿、双边沿、脉宽触发
+local trigger_mode = "自动"     -- 触发方式:自动、普通、单次
+local show_menu = false
+local vertical_scale_dropdown = nil
+local is_running = true         -- 运行状态
+local last_x_offset = 0         -- 上一次的水平偏移值
+local last_t_offset = 0         -- 上一次的触发位置
+local label_x_offset = nil      -- 水平偏移标签
+local tp_touch_subid = nil      -- 触摸事件订阅ID
+
+
+-- 示波器可移动标签相关参数
+local show_labels = false       -- 是否显示标签
+local step_size   = 1           -- 标签移动步长
+local choose_step_btn = nil     -- 步长选择按钮
+local trigger_line = nil        -- 触发位置竖线
+local trigger_label = nil       -- 触发位置标签"T"
+local trigger_line_color = 0xA8A8A8  -- 灰色
+local label_overlay = nil       -- 标签覆盖层
+
+-- 更新触发位置竖线
+local function update_trigger_line(x_offset)
+    -- 500个数据点映射到440像素宽度
+    -- x_offset范围: 0-500,默认250(中间位置)
+    -- 计算屏幕X坐标
+    local screen_x = x_offset / 500 * 440
+    
+    -- 销毁旧的触发线
+    if trigger_line then
+        trigger_line:destroy()
+        trigger_line = nil
+    end
+    
+    -- 创建新的触发线(红色竖线)
+    trigger_line = airui.shape({
+        parent = chart,
+        x = 0,
+        y = 0,
+        w = 430,
+        h = 220,
+        items = {
+            {
+                type = "line",
+                x1 = screen_x, y1 = 0,
+                x2 = screen_x, y2 = 230,
+                color = trigger_line_color,
+                width = 2,
+                opacity = 80
+            }
+        }
+    })
+    
+    -- 销毁旧的标签"T"
+    if trigger_label then
+        trigger_label:destroy()
+        trigger_label = nil
+    end
+    
+    -- 创建标签"T"
+    trigger_label = airui.label({
+        parent = main_container,
+        text = "T",
+        x = 10 + screen_x + 7,  -- 居中显示
+        y = 35,
+        w = 14,
+        h = 16,
+        color = trigger_line_color,
+        font_size = 14,
+    })
+    
+    log.info("chart", "触发位置更新", "x_offset:", x_offset, "screen_x:", screen_x)
+end
+local label_msg_container = nil -- 标签消息容器
+local label_btn_container = nil -- 标签操作按钮容器
+local label_a_title = nil       -- 标签A标题
+local label_b_title = nil       -- 标签B标题
+local label_a_x = nil           -- 标签A位置
+local label_b_x = nil           -- 标签B位置
+local label_a_msg = nil         -- 标签A消息
+local label_b_msg = nil         -- 标签B消息
+local label_ab_msg = nil        -- 标签AB消息
+local label_btn_a_left = nil    -- 标签A左移按钮
+local label_btn_a_right = nil   -- 标签A右移按钮
+local label_btn_b_left = nil    -- 标签B左移按钮
+local label_btn_b_right = nil   -- 标签B右移按钮
+
+local label_c_title = nil       -- 标签C标题
+local label_d_title = nil       -- 标签D标题
+local label_c_y = nil           -- 标签C位置
+local label_d_y = nil           -- 标签D位置
+local label_c_msg = nil         -- 标签C消息
+local label_d_msg = nil         -- 标签D消息
+local label_cd_msg = nil        -- 标签CD消息
+local label_btn_c_up = nil      -- 标签C上移按钮
+local label_btn_c_down = nil    -- 标签C下移按钮
+local label_btn_d_up = nil      -- 标签D上移按钮
+local label_btn_d_down = nil    -- 标签D下移按钮
+
+local label_a_color = 0xD32F2F  -- 复古红
+local label_b_color = 0x0277BD  -- 湖蓝
+local label_c_color = 0x2E7D32  -- 墨绿
+local label_d_color = 0x7B1FA2  -- 深紫
+
+-- 引入uart1_msg模块
+local uart_send = require("uart1_msg")
+
+-- 发送ADC配置指令
+local function send_adc_config()
+    -- 收集参数
+    local config = {}
+    
+    -- 垂直档位(单位:mV)
+    local vertical_gear_map = {
+        ["1.5V"] = 15000,
+        ["1V"] = 10000,
+        ["500mV"] = 5000,
+        ["200mV"] = 2000,
+        ["100mV"] = 1000,
+        ["50mv"] = 500,
+        ["20mv"] = 200
+    }
+    config.vertical_gear = vertical_gear_map[vertical_scale] or 5000
+    
+    -- 时间档位(单位:us)
+    local time_gear_map = {
+        ["1s"] = 1000000,
+        ["500ms"] = 500000,
+        ["200ms"] = 200000,
+        ["100ms"] = 100000,
+        ["50ms"] = 50000,
+        ["20ms"] = 20000,
+        ["10ms"] = 10000,
+        ["5ms"] = 5000,
+        ["2ms"] = 2000,
+        ["1ms"] = 1000,
+        ["500us"] = 500,
+        ["200us"] = 200,
+        ["100us"] = 100,
+        ["50us"] = 50
+    }
+    config.time_gear = time_gear_map[time_scale] or 1000
+    
+    -- 水平偏移
+    if _G.x_offset == 0 then
+        config.x_offset = 0  -- 回归原始
+    elseif _G.x_offset > last_x_offset then
+        config.x_offset = 2  -- 右移
+    elseif _G.x_offset < last_x_offset then
+        config.x_offset = 1  -- 左移
+    else
+        config.x_offset = 0
+    end
+    -- 更新上一次的x_offset值
+    last_x_offset = _G.x_offset
+    
+    -- 垂直偏移
+    config.y_offset = _G.y_offset
+    
+    -- 触发阈值(单位:mV)
+    local threshold = tonumber(trigger_threshold_input:get_text()) or 2
+    config.trigger_threshold = threshold * 1000
+    
+    -- 触发类型
+    local trigger_type_map = {
+        ["上升沿"] = 0,
+        ["下降沿"] = 1,
+        ["双边沿"] = 2,
+        ["脉宽触发"] = 3
+    }
+    config.trigger_type = trigger_type_map[trigger_type] or 0
+    
+    -- 触发脉宽(单位:us)
+    local pulse_width = tonumber(pulse_width_input:get_text()) or 200
+    config.trigger_width = pulse_width
+    
+    -- 触发方式
+    local trigger_method_map = {
+        ["自动"] = 0,
+        ["普通"] = 1,
+        ["单次"] = 2
+    }
+    config.trigger_method = trigger_method_map[trigger_mode] or 0
+    
+    -- 运行控制
+    config.run_control = is_running and 1 or 0
+    
+    -- 复位
+    config.reset = 0
+    
+    -- 发送指令
+    uart_send.send_adc_config(config)
+end
+
+
+-- 发送ADC动态操作指令
+local function send_adc_operate()
+    -- 收集参数
+    local config = {}
+
+    -- 触发方式
+    local trigger_method_map = {
+        ["自动"] = 0,
+        ["普通"] = 1,
+        ["单次"] = 2
+    }
+    config.trigger_method = trigger_method_map[trigger_mode] or 0
+    
+    -- 运行控制
+    config.run_control = is_running and 1 or 0
+    
+    -- 复位
+    config.reset = 0
+    
+    -- 发送指令
+    uart_send.send_adc_operate(config)
+end
+
+-- 更新水平偏移标签显示
+local function update_x_offset_label()
+    local offset_value = _G.x_offset or 0
+    local percent = math.abs(offset_value) * 10  -- 将 -10~10 转换为 0~100%
+    
+    local text
+    if offset_value == 0 then
+        text = "偏移 0%"
+    elseif offset_value < 0 then
+        text = string.format("左移 %d%%", percent)
+    else
+        text = string.format("右移 %d%%", percent)
+    end
+    
+    if label_x_offset then
+        label_x_offset:set_text(text)
+    end
+end
+
+local function update_vertical_scale_dropdown()
+    if not vertical_scale_dropdown then
+        return
+    end
+    -- 反向映射:Y_FULL_SCALE 值到档位选项
+    local scale_map = {
+        [15] = 0,    -- "15V"
+        [10] = 1,      -- "10V"     
+        [5] = 2,    -- "5V"
+        [2] = 3,    -- "2V"
+        [1] = 4,    -- "1V"
+        [0.5] = 5,     -- "500mv"
+        [0.2] = 6      -- "200mv"
+    }
+    
+    local index = scale_map[_G.Y_FULL_SCALE] or 2 -- 默认5V
+    vertical_scale_dropdown:set_selected(index)
+    
+    -- 同时更新 vertical_scale 变量
+    local options = {"1.5V","1V", "500mV", "200mV", "100mV", "50mv", "20mv"}  
+    vertical_scale = options[index + 1]
+end
+
+local function update_time_scale_dropdown()
+    if not time_scale_dropdown then
+        return
+    end
+    -- 设置时间档位为2ms(索引8)
+    local default_index = 8 -- 2ms
+    time_scale_dropdown:set_selected(default_index)
+    
+    -- 同时更新 time_scale 变量
+    local options = {"1s", "500ms", "200ms", "100ms", "50ms", "20ms", "10ms", "5ms", "2ms", "1ms", "500us", "200us", "100us", "50us"}
+    time_scale = options[default_index + 1]
+end
+
+local function reset_all_settings()
+    -- 重置全局变量
+    _G.x_offset = 0
+    _G.y_offset = 0
+    _G.Y_FULL_SCALE = 5
+    
+    -- 重置垂直档位
+    update_vertical_scale_dropdown()
+    
+    -- 重置时间档位
+    update_time_scale_dropdown()
+
+    -- 初始化时更新标签显示
+    update_x_offset_label()
+    
+    -- 重置触发阈值输入框
+    if trigger_threshold_input then
+        trigger_threshold_input:set_text("2")
+    end
+    
+    -- 重置脉宽触发输入框
+    if pulse_width_input then
+        pulse_width_input:set_text("200")
+    end
+    
+    -- 重置触发类型
+    trigger_type = "上升沿"
+    if trigger_type_btn then
+        trigger_type_btn:set_text("上升沿")
+    end
+    
+    -- 重置触发方式
+    trigger_mode = "自动"
+    if trigger_mode_btn then
+        trigger_mode_btn:set_text("自动")
+    end
+    
+    -- 重置运行状态
+    is_running = true
+    if run_control_btn then
+        run_control_btn:set_text("暂停")
+    end
+
+
+    
+    -- 发送配置指令
+    send_adc_config()
+    send_adc_operate()
+end
+
+
+-- 创建UI
+function tsb_wavein_page.create_ui()
+    main_container = airui.container({
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 320,
+        color = 0xFFFFFF
+    })
+
+    --------------------- 标题栏 ------------------------
+    local title_bar = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 30,
+        color = 0xFFD93D
+    })
+
+    airui.label({
+        parent = title_bar,
+        text = "波形输入",
+        x = 200,
+        y = 8,
+        w = 80,
+        h = 20,
+        font_size = 16,
+        color = 0xFFFFFF,
+    })
+    
+    -- 标题栏公共信息展示
+    common_ui.add_battery_display(title_bar)
+    common_ui.create_back_button(title_bar, tsb_wavein_page.cleanup)
+    ---------------------------------------------------
+
+    local keyboard1 = airui.keyboard({
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 160,                        -- x, y, 键盘默认打开ALIGN_BOTTOM_MID,位置从中下方开始计算
+        mode = "numeric",               -- 键盘模式,可选 "text"/"upper"/"lower"/"numeric"
+        auto_hide = true,               -- 自动隐藏键盘
+        preview = true,          
+        preview_height = 35,
+        bg_color = 0xf1f1f1,            -- 键盘背景颜色为灰色,可选,不设置则透明
+        on_commit = function()          -- 确认事件回调,只有在按下确认键时才会触发
+            log.info("keyboard", "commit")
+            send_adc_config()
+        end
+    })
+
+    chart = airui.chart({
+        parent = main_container,
+        x = 10,
+        y = 40,
+        w = 460,
+        h = 250,
+        y_min = 0,
+        y_max = 100,
+        point_count = 500,          
+        update_mode = "shift",
+        line_color = 0x00b4ff,
+        line_width = 2,
+        hdiv = 11,
+        vdiv = 11,
+        bg_opa = 0
+    })
+
+    -- 控制区域(右侧)
+    local control_container = airui.container({
+        parent = main_container,
+        x = 210,
+        y = 40,
+        w = 210,
+        h = 250,
+        color = 0xFFFFFF,
+        radius = 5,
+        border_width = 1,
+        border_color = 0xDDDDDD,
+        color_opacity = 255
+    })
+
+    control_container:hide()
+
+
+    -- 订阅触摸事件,点击左侧区域关闭设置面板
+    tp_touch_subid = sys.subscribe("TP_TOUCH", function(x, y)
+         -- 只响应按下事件,避免重复触发
+         if x >= 20 and x <= 200 and y >= 30 and y <= 280 then
+             if show_menu then
+                 control_container:hide()
+                 show_menu = false
+                 log.info("chart", "点击左侧区域关闭设置面板")
+             end
+         end
+     end)
+
+    -- 隐藏/显示按钮(放在最顶层)
+    visible_btn = airui.button({
+        parent = main_container,
+        text = "隐藏",
+        x = 420,
+        y = 40,
+        w = 50,
+        h = 30,
+        style = {bg_opa = 0},
+        on_click = function(self)
+            if self:get_text() == "隐藏" then
+                self:set_text("显示")
+                scroll_container:hide()
+            else
+                self:set_text("隐藏")
+                scroll_container:open()
+            end
+        end
+    })
+
+    -- 按钮容器(放在chart之后,确保在chart之上)
+    scroll_container = airui.container({
+        parent = main_container,
+        x = 410,
+        y = 70,
+        w = 65,
+        h = 190,
+        color = 0xFFFFFFF,
+        color_opacity =0
+    })
+
+    set_btn = airui.button({
+        parent = scroll_container,
+        text = "设置",
+        x = 10,
+        y = 5,
+        w = 50,
+        h = 30,
+        --style = {bg_opa = 0},
+        on_click = function()
+            if show_menu then
+                control_container:hide()
+                show_menu = false
+            else
+                control_container:open()
+                show_menu = true
+            end
+        end
+    })
+
+    reset_btn = airui.button({
+        parent = scroll_container,
+        text = "复位",
+        x = 10,
+        y = 40,
+        w = 50,
+        h = 30,
+        --style = {bg_opa = 0},
+        on_click = function()
+            reset_all_settings()
+        end
+    })
+
+    trigger_mode_btn = airui.button({
+        parent = scroll_container,
+        text = "自动",
+        x = 10,
+        y = 75,
+        w = 50,
+        h = 30,
+        font_size = 14,
+        --style = {bg_opa = 0},
+        on_click = function(self)
+            if trigger_mode == "自动" then
+                trigger_mode = "普通"
+            elseif trigger_mode == "普通" then
+                trigger_mode = "单次"
+            else
+                trigger_mode = "自动"
+            end
+            self:set_text(trigger_mode)
+            log.info("chart", "触发方式: " .. trigger_mode)
+            send_adc_operate()
+        end
+    })
+
+    -- 开始/暂停按钮
+    run_control_btn = airui.button({
+        parent = scroll_container,
+        text = "暂停",
+        x = 10,
+        y = 110,
+        w = 50,
+        h = 30,
+        font_size = 14,
+        --style = {bg_opa = 0},
+        on_click = function(self)
+            if is_running then
+                self:set_text("开始")
+                is_running = false
+                log.info("chart", "暂停")
+            else
+                self:set_text("暂停")
+                is_running = true
+                log.info("chart", "开始")
+            end
+            send_adc_operate()
+        end
+    })
+
+
+    label_msg_container = airui.container({
+        parent = main_container,
+        x = 180,
+        y = 40,
+        w = 240,
+        h = 80,
+        color = 0xFFFFFF,
+        radius = 8,
+        border_width = 0,
+        color_opacity = 0
+    })
+
+    label_a_msg = airui.label({
+        parent = label_msg_container,
+        text = "Xa: ms",
+        x = 5,
+        y = 10,
+        w = 120,
+        h = 20,
+        font_size = 13,
+        color = 0x000000,
+    })
+
+    label_b_msg = airui.label({
+        parent = label_msg_container,
+        text = "Xb: ms",
+        x = 5,
+        y = 30,
+        w = 120,
+        h = 20,
+        font_size = 13,
+        color = 0x000000,
+    })
+
+    label_ab_msg = airui.label({
+        parent = label_msg_container,
+        text = "Xab: ms",
+        x = 5,
+        y = 50,
+        w = 120,
+        h = 20,
+        font_size = 13,
+        color = 0x000000,
+    })
+
+    label_c_msg = airui.label({
+        parent = label_msg_container,
+        text = "Yc: mV",
+        x = 130,
+        y = 10,
+        w = 110,
+        h = 20,
+        font_size = 13,
+        color = 0x000000,
+    })
+    label_d_msg = airui.label({
+        parent = label_msg_container,
+        text = "Yd: mV",
+        x = 130,
+        y = 30,
+        w = 110,
+        h = 20,
+        font_size = 13,
+        color = 0x000000,
+    })
+    label_cd_msg = airui.label({
+        parent = label_msg_container,
+        text = "Ycd: mV",
+        x = 130,
+        y = 50,
+        w = 110,
+        h = 20,
+        font_size = 13,
+        color = 0x000000,
+    })
+
+
+    label_msg_container:hide()
+
+
+    label_btn_container = airui.container({
+        parent = main_container,
+        x = 10,
+        y = 130,
+        w = 210,
+        h = 150,
+        color = 0xF111FF,
+        radius = 8,
+        border_width = 0,
+        color_opacity = 0
+    })
+
+-- 时间档位映射(单位:ms)
+    local time_gear_map = {
+        ["1s"] = 1000,
+        ["500ms"] = 500,
+        ["200ms"] = 200,
+        ["100ms"] = 100,
+        ["50ms"] = 50,
+        ["20ms"] = 20,
+        ["10ms"] = 10,
+        ["5ms"] = 5,
+        ["2ms"] = 2,
+        ["1ms"] = 1,
+        ["500us"] = 0.5,
+        ["200us"] = 0.2,
+        ["100us"] = 0.1,
+        ["50us"] = 0.05
+    }
+
+    -- 垂直档位映射(单位:V)
+    local vertical_gear_map = {
+        ["50V"] = 50,
+        ["20V"] = 20,
+        ["10V"] = 10,
+        ["5V"] = 5,
+        ["2V"] = 2,
+        ["1V"] = 1,
+        ["500mV"] = 0.5,
+        ["200mV"] = 0.2,
+        ["100mV"] = 0.1,
+        ["50mV"] = 0.05,
+        ["20mV"] = 0.02,
+        ["10mV"] = 0.01,
+        ["5mV"] = 0.005
+    }
+
+    -- 更新标签位置函数
+    local function update_label_positions()
+        if not label_overlay then return end
+        
+        -- 销毁旧的overlay并重建
+        label_overlay:destroy()
+        
+        -- 创建新的overlay
+        label_overlay = airui.shape({
+            parent = chart,
+            x = 0,
+            y = 0,
+            w = 430,
+            h = 220,
+            items = {
+                -- 标签A竖线
+                {
+                    type = "line",
+                    x1 = label_a_x, y1 = 0,
+                    x2 = label_a_x, y2 = 250,
+                    color = label_a_color,
+                    width = 2,
+                    opacity = 255,
+                    dash_width = 2,
+                    dash_gap = 2,
+                },
+                -- 标签B竖线
+                {
+                    type = "line",
+                    x1 = label_b_x, y1 = 0,
+                    x2 = label_b_x, y2 = 250,
+                    color = label_b_color,
+                    width = 2,
+                    opacity = 255,
+                    dash_width = 2,
+                    dash_gap = 2,
+                },
+
+                -- 标签C竖线
+                {
+                    type = "line",
+                    x1 = 0, y1 = label_c_y,
+                    x2 = 430, y2 = label_c_y,
+                    color = label_c_color,
+                    width = 2,
+                    opacity = 255,
+                    dash_width = 2,
+                    dash_gap = 2,
+                },
+                -- 标签D竖线
+                {
+                    type = "line",
+                    x1 = 0, y1 = label_d_y,
+                    x2 = 430, y2 = label_d_y,
+                    color = label_d_color,
+                    width = 2,
+                    opacity = 255,
+                    dash_width = 2,
+                    dash_gap = 2,
+                }
+            }
+        })
+        
+        -- 更新标签A文字位置
+        if label_a_title then
+            label_a_title:destroy()
+        end
+        label_a_title = airui.label({
+            parent = main_container,
+            text = "A",
+            x = 10 + label_a_x + 6,
+            y = 30,
+            w = 14,
+            h = 16,
+            color = label_a_color,
+            font_size = 14,
+        })
+        
+        -- 更新标签B文字位置
+        if label_b_title then
+            label_b_title:destroy()
+        end
+        label_b_title = airui.label({
+            parent = main_container,
+            text = "B",
+            x = 10 + label_b_x + 6,
+            y = 30,
+            w = 14,
+            h = 16,
+            color = label_b_color,
+            font_size = 14,
+        })
+        
+        -- 更新标签C文字位置
+        if label_c_title then
+            label_c_title:destroy()
+        end
+        label_c_title = airui.label({
+            parent = main_container,
+            text = "C",
+            x = 5,
+            y = 40 + label_c_y,
+            w = 14,
+            h = 16,
+            color = label_c_color,
+            font_size = 14,
+        })
+        
+        -- 更新标签D文字位置
+        if label_d_title then
+            label_d_title:destroy()
+        end
+        label_d_title = airui.label({
+            parent = main_container,
+            text = "D",
+            x = 5,
+            y = 40 + label_d_y,
+            w = 14,
+            h = 16,
+            color = label_d_color,
+            font_size = 14,
+        })
+        
+        -- 计算并更新时间显示(标签A和B)
+        local div_time = time_gear_map[time_scale] or 2  -- 每格时间(ms)
+        local px_per_div_x = 44  -- 横向每格像素数
+        local px_per_ms = px_per_div_x / div_time  -- 每ms对应的像素数
+        
+        -- 计算Xa(以最左边为起点)
+        local xa_ms = label_a_x / px_per_ms
+        
+        -- 计算Xb(以最左边为起点)
+        local xb_ms = label_b_x / px_per_ms
+        
+        -- 计算Xab
+        local xab_ms = math.abs(xb_ms - xa_ms)
+        
+        -- 获取最大绝对值用于确定统一单位
+        local max_val_x = math.max(math.abs(xa_ms), math.abs(xb_ms), xab_ms)
+        
+        -- 根据最大值确定统一单位
+        local unit_x
+        if max_val_x >= 1000 then
+            unit_x = "s"
+            xa_ms = xa_ms / 1000
+            xb_ms = xb_ms / 1000
+            xab_ms = xab_ms / 1000
+        elseif max_val_x >= 1 then
+            unit_x = "ms"
+        else
+            unit_x = "us"
+            xa_ms = xa_ms * 1000
+            xb_ms = xb_ms * 1000
+            xab_ms = xab_ms * 1000
+        end
+        
+        -- 更新时间标签显示
+        if unit_x == "s" then
+            label_a_msg:set_text(string.format("Xa: %.3fs", xa_ms))
+            label_b_msg:set_text(string.format("Xb: %.3fs", xb_ms))
+            label_ab_msg:set_text(string.format("Xab: %.3fs", xab_ms))
+        elseif unit_x == "ms" then
+            label_a_msg:set_text(string.format("Xa: %.3fms", xa_ms))
+            label_b_msg:set_text(string.format("Xb: %.3fms", xb_ms))
+            label_ab_msg:set_text(string.format("Xab: %.3fms", xab_ms))
+        else
+            label_a_msg:set_text(string.format("Xa: %.0fus", xa_ms))
+            label_b_msg:set_text(string.format("Xb: %.0fus", xb_ms))
+            label_ab_msg:set_text(string.format("Xab: %.0fus", xab_ms))
+        end
+        
+        
+        
+        -- 计算并更新电压显示(标签C和D)
+        local div_voltage = vertical_gear_map[vertical_scale] or 1  -- 每格电压(V)
+        local px_per_div_y = 23  -- 纵向每格像素数
+        local px_per_v = px_per_div_y / div_voltage  -- 每V对应的像素数
+        
+        -- 计算Yc(以最上方为起点,y=0时为最大电压)
+        local yc_v = (230 - label_c_y) / px_per_v  -- 230为最大y位置(10格 × 23px/格)
+        
+        -- 计算Yd
+        local yd_v = (230 - label_d_y) / px_per_v
+        
+        -- 计算Ycd
+        local ycd_v = math.abs(yd_v - yc_v)
+        
+        -- 获取最大绝对值用于确定统一单位
+        local max_val_y = math.max(math.abs(yc_v), math.abs(yd_v), ycd_v)
+        
+        -- 根据最大值确定统一单位
+        local unit_y
+        if max_val_y >= 1 then
+            unit_y = "V"
+        elseif max_val_y >= 0.001 then
+            unit_y = "mV"
+            yc_v = yc_v * 1000
+            yd_v = yd_v * 1000
+            ycd_v = ycd_v * 1000
+        else
+            unit_y = "uV"
+            yc_v = yc_v * 1000000
+            yd_v = yd_v * 1000000
+            ycd_v = ycd_v * 1000000
+        end
+        
+        -- 更新电压标签显示(保留2位小数)
+        if unit_y == "V" then
+            label_c_msg:set_text(string.format("Yc: %.2fV", yc_v))
+            label_d_msg:set_text(string.format("Yd: %.2fV", yd_v))
+            label_cd_msg:set_text(string.format("Ycd: %.2fV", ycd_v))
+        elseif unit_y == "mV" then
+            label_c_msg:set_text(string.format("Yc: %.2fmV", yc_v))
+            label_d_msg:set_text(string.format("Yd: %.2fmV", yd_v))
+            label_cd_msg:set_text(string.format("Ycd: %.2fmV", ycd_v))
+        else
+            label_c_msg:set_text(string.format("Yc: %.0fuV", yc_v))
+            label_d_msg:set_text(string.format("Yd: %.0fuV", yd_v))
+            label_cd_msg:set_text(string.format("Ycd: %.0fuV", ycd_v))
+        end
+    end
+
+    label_btn_a_left = airui.button({
+        parent = label_btn_container,
+        text = "A左移",
+        x = 5,
+        y = 5,
+        w = 60,
+        h = 30,
+        --style = {bg_opa = 0},
+        on_click = function()
+            if label_a_x > 0 then
+                label_a_x = label_a_x - step_size
+                update_label_positions()
+                log.info("chart", "标签A左移", "位置:", label_a_x)
+            end
+        end
+    })
+
+    label_btn_a_right = airui.button({
+        parent = label_btn_container,
+        text = "A右移",
+        x = 70,
+        y = 5,
+        w = 60,
+        h = 30,
+        --style = {bg_opa = 0},
+        on_click = function()
+            if label_a_x < 430 then
+                label_a_x = label_a_x + step_size
+                update_label_positions()
+                log.info("chart", "标签A右移", "位置:", label_a_x)
+            end
+        end
+    })
+
+    label_btn_b_left = airui.button({
+        parent = label_btn_container,
+        text = "B左移",
+        x = 5,
+        y = 40,
+        w = 60,
+        h = 30,
+        --style = {bg_opa = 0},
+        on_click = function()
+            if label_b_x > 0 then
+                label_b_x = label_b_x - step_size
+                update_label_positions()
+                log.info("chart", "标签B左移", "位置:", label_b_x)
+            end
+        end
+    })
+
+    label_btn_b_right = airui.button({
+        parent = label_btn_container,
+        text = "B右移",
+        x = 70,
+        y = 40,
+        w = 60,
+        h = 30,
+        --style = {bg_opa = 0},
+        on_click = function()
+            if label_b_x < 430 then
+                label_b_x = label_b_x + step_size
+                update_label_positions()
+                log.info("chart", "标签B右移", "位置:", label_b_x)
+            end
+        end
+    })
+
+    label_btn_c_up = airui.button({
+        parent = label_btn_container,
+        text = "C上移",
+        x = 5,
+        y = 75,
+        w = 60,
+        h = 30,
+        --style = {bg_opa = 0},
+        on_click = function()
+            if label_c_y > 0 then
+                label_c_y = label_c_y - step_size
+                update_label_positions()
+                log.info("chart", "标签C上移", "位置:", label_c_y)
+            end
+        end
+    })
+
+    label_btn_c_down = airui.button({
+        parent = label_btn_container,
+        text = "C下移",
+        x = 70,
+        y = 75,
+        w = 60,
+        h = 30,
+        --style = {bg_opa = 0},
+        on_click = function()
+            if label_c_y < 224 then
+                label_c_y = label_c_y + step_size
+                update_label_positions()
+                log.info("chart", "标签C下移", "位置:", label_c_y)
+            end
+        end
+    })
+
+    label_btn_d_up = airui.button({
+        parent = label_btn_container,
+        text = "D上移",
+        x = 5,
+        y = 110,
+        w = 60,
+        h = 30,
+        --style = {bg_opa = 0},
+        on_click = function()
+            if label_d_y > 0 then
+                label_d_y = label_d_y - step_size
+                update_label_positions()
+                log.info("chart", "标签D上移", "位置:", label_d_y)
+            end
+        end
+    })
+
+    label_btn_d_down = airui.button({
+        parent = label_btn_container,
+        text = "D下移",
+        x = 70,
+        y = 110,
+        w = 60,
+        h = 30,
+        --style = {bg_opa = 0},
+        on_click = function()
+            if label_d_y < 224 then
+                label_d_y = label_d_y + step_size
+                update_label_positions()
+                log.info("chart", "标签D下移", "位置:", label_d_y)
+            end
+        end
+    })
+
+
+    choose_step_btn = airui.button({
+        parent = label_btn_container,
+        text = "步长:1",
+        x = 135,
+        y = 110,
+        w = 70,
+        h = 30,
+        --style = {bg_opa = 0},
+        on_click = function(self)
+            -- 步长档位列表
+            local step_sizes = {1, 5, 20, 50}
+            
+            -- 找到当前步长的索引
+            local current_index = 1
+            for i, size in ipairs(step_sizes) do
+                if size == step_size then
+                    current_index = i
+                    break
+                end
+            end
+            
+            -- 切换到下一个步长(循环)
+            local next_index = current_index % #step_sizes + 1
+            step_size = step_sizes[next_index]
+            
+            -- 更新按钮显示文字
+            self:set_text(string.format("步长:%d", step_size))
+            
+            log.info("chart", "步长切换", "当前步长:", step_size)
+        end
+    })
+
+
+    label_btn_container:hide()
+
+    label_btn = airui.button({
+        parent = scroll_container,
+        text = "标签",
+        x = 10,
+        y = 145,
+        w = 50,
+        h = 30,
+        --style = {bg_opa = 0},
+        on_click = function(self)
+            if show_labels then
+                self:set_text("标签")
+                show_labels = false
+                log.info("chart", "关闭标签")
+                label_msg_container:hide()
+                label_btn_container:hide()
+                -- 销毁overlay
+                if label_overlay then
+                    label_overlay:destroy()
+                    label_overlay = nil
+                end
+                -- 销毁标签文字
+                if label_a_title then
+                    label_a_title:destroy()
+                    label_a_title = nil
+                end
+                if label_b_title then
+                    label_b_title:destroy()
+                    label_b_title = nil
+                end
+                if label_c_title then
+                    label_c_title:destroy()
+                    label_c_title = nil
+                end
+                if label_d_title then
+                    label_d_title:destroy()
+                    label_d_title = nil
+                end
+            else
+                self:set_text("关标")
+                show_labels = true
+                log.info("chart", "开启标签")
+                label_msg_container:open()
+                label_btn_container:open()
+                
+                -- 标签位置变量(可配置)
+                label_a_x = 200  -- 标签A的X位置
+                label_b_x = 300  -- 标签B的X位置
+
+                label_c_y = 100
+                label_d_y = 150
+                
+                -- 创建overlay,包含A、B两根竖线(父容器为chart,避免覆盖按钮)
+                label_overlay = airui.shape({
+                    parent = chart,
+                    x = 0,
+                    y = 0,
+                    w = 430,
+                    h = 220,
+                    items = {
+                        -- 标签A竖线
+                        {
+                            type = "line",
+                            x1 = label_a_x, y1 = 0,
+                            x2 = label_a_x, y2 = 250,
+                            color = label_a_color,
+                            width = 2,
+                            opacity = 255,
+                            dash_width = 2,
+                            dash_gap = 2,
+                        },
+                        -- 标签B竖线
+                        {
+                            type = "line",
+                            x1 = label_b_x, y1 = 0,
+                            x2 = label_b_x, y2 = 250,
+                            color = label_b_color,
+                            width = 2,
+                            opacity = 255,
+                            dash_width = 2,
+                            dash_gap = 2,
+                        },
+
+                        -- 标签C横线
+                        {
+                            type = "line",
+                            x1 = 0, y1 = label_c_y,
+                            x2 = 430, y2 = label_c_y,
+                            color = label_c_color,
+                            width = 2,
+                            opacity = 255,
+                            dash_width = 2,
+                            dash_gap = 2,
+                        },
+                        -- 标签D横线
+                        {
+                            type = "line",
+                            x1 = 0, y1 = label_d_y,
+                            x2 = 430, y2 = label_d_y,
+                            color = label_d_color,
+                            width = 2,
+                            opacity = 255,
+                            dash_width = 2,
+                            dash_gap = 2,
+                        }
+                    }
+                })
+                
+                -- 创建标签A文字
+                label_a_title = airui.label({
+                    parent = main_container,
+                    text = "A",
+                    x = 10 + label_a_x + 6,
+                    y = 35,
+                    w = 14,
+                    h = 16,
+                    color = label_a_color,
+                    font_size = 14,
+                })
+                
+                -- 创建标签B文字
+                label_b_title = airui.label({
+                    parent = main_container,
+                    text = "B",
+                    x = 10 + label_b_x + 6, 
+                    y = 35,
+                    w = 14,
+                    h = 16,
+                    color = label_b_color,
+                    font_size = 14,
+                })
+                
+                -- 创建标签C文字
+                label_c_title = airui.label({
+                    parent = main_container,
+                    text = "C",
+                    x = 10,  
+                    y = label_c_y + 40,
+                    w = 14,
+                    h = 16,
+                    color = label_c_color,
+                    font_size = 14,
+                })
+                
+                -- 创建标签D文字
+                label_d_title = airui.label({
+                    parent = main_container,
+                    text = "D",
+                    x = 10,  
+                    y = label_d_y + 40,
+                    w = 14,
+                    h = 16,
+                    color = label_d_color,
+                    font_size = 14,
+                })
+            end
+        end
+    })
+
+    -- 添加选项卡
+    local tabview = airui.tabview({
+        parent = control_container,
+        x = 2,
+        y = 2,
+        w = 205,
+        h = 245,
+        tabs = {"基础设置", "触发设置"},
+        active = 0,
+        page_style = {
+            tabbar_size = 40,
+            pad = { method = airui.TABVIEW_PAD_ALL, value = 0 },
+            bg_opa = 255,
+        },
+    })
+
+    -- 第一页:基础设置
+    local tab1 = tabview:get_content(0)
+    local control_y1 = 10
+
+    -- 时间档位
+    airui.label({
+        parent = tab1,
+        text = "时间档位",
+        x = 10,
+        y = control_y1 + 5,
+        w = 70,
+        h = 20,
+        font_size = 12,
+        color = 0x000000,
+    })
+    time_scale_dropdown = airui.dropdown({
+        parent = tab1,
+        x = 80,
+        y = control_y1,
+        w = 110,
+        h = 25,
+        options = {"1s", "500ms", "200ms", "100ms", "50ms", "20ms", "10ms", "5ms", "2ms", "1ms", "500us", "200us", "100us", "50us"},
+        default_index = 8, -- 默认2ms
+        on_change = function(self, idx)
+            local options = {"1s", "500ms", "200ms", "100ms", "50ms", "20ms", "10ms", "5ms", "2ms", "1ms", "500us", "200us", "100us", "50us"}
+            time_scale = options[idx + 1]
+            log.info("chart", "时间档位: " .. time_scale)
+            send_adc_config()
+        end
+    })
+    control_y1 = control_y1 + 40
+
+    -- 垂直档位
+    airui.label({
+        parent = tab1,
+        text = "垂直档位",
+        x = 10,
+        y = control_y1 + 5,
+        w = 90,
+        h = 20,
+        font_size = 12,
+        color = 0x000000,
+    })
+    vertical_scale_dropdown = airui.dropdown({
+        parent = tab1,
+        x = 80,
+        y = control_y1,
+        w = 110,
+        h = 25,
+        options = {"1.5V","1V", "500mV", "200mV", "100mV", "50mv", "20mv"},
+        default_index = 2, -- 默认5V
+        on_change = function(self, idx)
+            local options = {"1.5V","1V","500mV", "200mV", "100mV", "50mv", "20mv"}
+            vertical_scale = options[idx + 1]
+            -- 根据选择的档位更新全局变量
+            local scale_map = {
+                ["1.5V"] = 15,
+                ["1V"] = 10,
+                ["500mV"] = 5,
+                ["200mV"] = 2,
+                ["100mV"] = 1,
+                ["50mv"] = 0.5,
+                ["20mv"] = 0.2
+            }
+            _G.Y_FULL_SCALE = scale_map[vertical_scale] or 5
+            log.info("chart", "垂直档位: " .. vertical_scale, "Y_FULL_SCALE: " .. _G.Y_FULL_SCALE)
+            send_adc_config()
+        end
+    })
+    control_y1 = control_y1 + 40
+
+    -- 水平偏移
+    airui.label({
+        parent = tab1,
+        text = "水平偏移",
+        x = 10,
+        y = control_y1,
+        w = 110,
+        h = 20,
+        font_size = 12,
+        color = 0x000000,
+    })
+    local x_offset_container = airui.container({
+        parent = tab1,
+        x = 10,
+        y = control_y1 + 20,
+        w = 190,
+        h = 30,
+        color = 0xFFFFFF,
+        radius = 4,
+    })
+    
+
+    airui.button({
+        parent = x_offset_container,
+        text = "←",
+        x = 10,
+        y = 2,
+        w = 75,
+        h = 25,
+        on_click = function()
+            if _G.x_offset > -10 then
+                _G.x_offset = _G.x_offset - 1  -- 左移
+                log.info("chart", "水平偏移左", "x_offset: " .. _G.x_offset)
+                update_x_offset_label()  -- 更新标签显示
+                send_adc_config()
+            else
+                log.info("chart", "水平偏移已达到左极限")
+            end
+        end
+    })
+    airui.button({
+        parent = x_offset_container,
+        text = "→",
+        x = 110,
+        y = 2,
+        w = 75,
+        h = 25,
+        on_click = function()
+            if _G.x_offset < 10 then
+                _G.x_offset = _G.x_offset + 1  -- 右移
+                log.info("chart", "水平偏移右", "x_offset: " .. _G.x_offset)
+                update_x_offset_label()  -- 更新标签显示
+                send_adc_config()
+            else
+                log.info("chart", "水平偏移已达到右极限")
+            end
+        end
+    })
+
+    label_x_offset = airui.label({
+        parent = tab1,
+        text = "偏移 0%",
+        x = 130,
+        y = control_y1,
+        w = 70,
+        h = 20,
+        font_size = 12,
+        color = 0x000000,
+    })
+    
+    
+
+    control_y1 = control_y1 + 60
+
+    -- 垂直偏移
+    airui.label({
+        parent = tab1,
+        text = "垂直偏移",
+        x = 10,
+        y = control_y1,
+        w = 110,
+        h = 20,
+        font_size = 12,
+        color = 0x000000,
+    })
+    local y_offset_container = airui.container({
+        parent = tab1,
+        x = 10,
+        y = control_y1 + 20,
+        w = 190,
+        h = 30,
+        color = 0xFFFFFF,
+        radius = 4,
+    })
+    airui.button({
+        parent = y_offset_container,
+        text = "↑",
+        x = 10,
+        y = 2,
+        w = 75,
+        h = 25,
+        on_click = function()
+            _G.y_offset = _G.y_offset + 5  -- 上移,值增大
+            log.info("chart", "垂直偏移上", "y_offset: " .. _G.y_offset)
+            send_adc_config()
+        end
+    })
+    airui.button({
+        parent = y_offset_container,
+        text = "↓",
+        x = 110,
+        y = 2,
+        w = 75,
+        h = 25,
+        on_click = function()
+            _G.y_offset = _G.y_offset - 5  -- 下移,值减小
+            log.info("chart", "垂直偏移下", "y_offset: " .. _G.y_offset)
+            send_adc_config()
+        end
+    })
+    -- 第二页:触发设置
+    local tab2 = tabview:get_content(1)
+    local control_y2 = 25
+
+    -- 触发阈值
+    airui.label({
+        parent = tab2,
+        text = "触发阈值",
+        x = 10,
+        y = control_y2 + 10,
+        w = 60,
+        h = 20,
+        font_size = 14,
+        color = 0x000000,
+    })
+    trigger_threshold_input = airui.textarea({
+        parent = tab2,
+        x = 80,
+        y = control_y2 + 5,
+        w = 80,
+        h = 25,
+        text = "2",
+        max_len = 5,
+        keyboard = keyboard1,
+        on_text_change = function(txt) 
+            log.info("text changed:", txt)
+        end
+    })
+    airui.label({
+        parent = tab2,
+        text = "V",
+        x = 165,
+        y = control_y2 + 10,
+        w = 30,
+        h = 20,
+        font_size = 14,
+        color = 0x000000,
+    })
+    control_y2 = control_y2 + 40
+
+    -- 触发类型
+    airui.label({
+        parent = tab2,
+        text = "触发类型",
+        x = 10,
+        y = control_y2 + 30,
+        w = 60,
+        h = 20,
+        font_size = 14,
+        color = 0x000000,
+    })
+    trigger_type_btn = airui.button({
+        parent = tab2,
+        text = "上升沿",
+        x = 80,
+        y = control_y2 + 25,
+        w = 80,
+        h = 25,
+        font_size = 14,
+        on_click = function(self)
+            if trigger_type == "上升沿" then
+                trigger_type = "下降沿"
+            elseif trigger_type == "下降沿" then
+                trigger_type = "双边沿"
+            elseif trigger_type == "双边沿" then
+                trigger_type = "脉宽触发"
+            elseif trigger_type == "脉宽触发" then
+                trigger_type = "上升沿"
+            end
+            self:set_text(trigger_type)
+            log.info("chart", "触发类型: " .. trigger_type)
+            send_adc_config()
+            
+        end
+    })
+    control_y2 = control_y2 + 80
+
+    -- 脉宽触发
+    airui.label({
+        parent = tab2,
+        text = "脉宽触发",
+        x = 10,
+        y = control_y2 + 10,
+        w = 60,
+        h = 20,
+        font_size = 14,
+        color = 0x000000,
+    })
+    pulse_width_input = airui.textarea({
+        parent = tab2,
+        x = 80,
+        y = control_y2 + 5,
+        w = 80,
+        h = 25,
+        text = "200",
+        max_len = 5,
+        keyboard = keyboard1,
+    })
+    airui.label({
+        parent = tab2,
+        text = "us",
+        x = 165,
+        y = control_y2 + 10,
+        w = 30,
+        h = 20,
+        font_size = 14,
+        color = 0x000000,
+    })
+
+    -- 信息显示区域(图表下方)
+    local info_container = airui.container({
+        parent = main_container,
+        x = 10,
+        y = 40,
+        w = 160,
+        h = 80,
+        color = 0xFFFFFF,
+        radius = 8,
+        border_width = 0,
+        color_opacity = 0
+    })
+
+    airui.label({
+        parent = info_container,
+        text = "频率:",
+        x = 5,
+        y = 10,
+        w = 40,
+        h = 20,
+        font_size = 13,
+        color = 0x000000,
+    })
+    freq_label = airui.label({
+        parent = info_container,
+        text = "0 Hz",
+        x = 60,
+        y = 10,
+        w = 100,
+        h = 20,
+        font_size = 13,
+        color = 0x000000,
+    })
+
+    airui.label({
+        parent = info_container,
+        text = "Vmax:",
+        x = 5,
+        y = 30,
+        w = 60,
+        h = 20,
+        font_size = 13,
+        color = 0x000000,
+    })
+    vmax_label = airui.label({
+        parent = info_container,
+        text = "0 mV",
+        x = 60,
+        y = 30,
+        w = 65,
+        h = 20,
+        font_size = 13,
+        color = 0x000000,
+    })
+
+    airui.label({
+        parent = info_container,
+        text = "Vmin:",
+        x = 5,
+        y = 50,
+        w = 60,
+        h = 20,
+        font_size = 13,
+        color = 0x000000,
+    })
+    vmin_label = airui.label({
+        parent = info_container,
+        text = "0 mV",
+        x = 60,
+        y = 50,
+        w = 65, 
+        h = 20,
+        font_size = 13,
+        color = 0x000000,
+    })
+
+    -- 底部信息
+    common_ui.create_status_bar(main_container)
+end
+
+
+
+local adc_wave_subid = nil
+local function subscribe_adc_wave()
+    -- 先注销旧订阅,防止重复
+    if adc_wave_subid then
+        sys.unsubscribe(adc_wave_subid)
+    end
+
+    -- 绑定订阅
+    adc_wave_subid = sys.subscribe("ADC_WAVE_DATA", function(wave_data)
+        -- 只有页面激活 + 图表存在 才更新
+        if not page_active or not chart then
+            return
+        end
+
+        -- 校验数据
+        if not wave_data or #wave_data ~= 500 then
+            log.error("chart","波形长度错误!期望500,实际:", #wave_data)
+            return
+        end
+
+        chart:set_values(1, wave_data)
+        
+        --log.info("chart","500点波形更新成功!")
+    end)
+    log.info("chart","ADC波形订阅已启动")
+end
+
+local adc_msg_subid = nil
+local function subscribe_adc_msg()
+    -- 先注销旧订阅,防止重复
+    if adc_msg_subid then
+        sys.unsubscribe(adc_msg_subid)
+    end
+
+    -- 绑定订阅
+    adc_msg_subid = sys.subscribe("ADC_DATA_UPDATE", function(data)
+        if not data then return end
+        
+        -- 更新频率标签
+        if freq_label then
+            local freq = data.frequency
+            local freq_str, unit
+            if freq >= 1000 then
+                freq_str = string.format("%.2f", freq / 1000)
+                unit = "kHz"
+            else
+                freq_str = tostring(freq)
+                unit = "Hz"
+            end
+            freq_label:set_text(freq_str .. " " .. unit)
+        end
+        
+        -- 更新电压最大值标签
+        if vmax_label then
+            local max_vol = data.max_vol
+            local vol_str, unit
+            if max_vol >= 1000 then
+                vol_str = string.format("%.2f", max_vol / 1000)
+                unit = "V"
+            else
+                vol_str = tostring(max_vol)
+                unit = "mV"
+            end
+            vmax_label:set_text(vol_str .. " " .. unit)
+        end
+        
+        -- 更新电压最小值标签
+        if vmin_label then
+            local min_vol = data.min_vol
+            local vol_str, unit
+            if min_vol >= 1000 then
+                vol_str = string.format("%.2f", min_vol / 1000)
+                unit = "V"
+            else
+                vol_str = tostring(min_vol)
+                unit = "mV"
+            end
+            vmin_label:set_text(vol_str .. " " .. unit)
+        end
+        
+        -- 更新触发位置竖线
+        if data.x_offset ~= nil and page_active and data.x_offset ~= last_t_offset then
+            last_t_offset = data.x_offset
+            update_trigger_line(data.x_offset)
+        end
+    end)
+end
+
+
+
+
+-- 初始化页面
+function tsb_wavein_page.init(params)
+    tsb_wavein_page.create_ui()
+    page_active = true
+    subscribe_adc_wave()
+    
+    -- 创建初始触发线(默认位置250,即中间位置)
+    update_trigger_line(250)
+    subscribe_adc_msg()    
+    
+    -- 首次初始化时发送一次配置指令
+    -- 使用定时器延迟发送
+    sys.timerStart(function()
+        -- 先发送一个同步字节,确保串口完全就绪
+        -- 短暂延时后发送实际配置
+       reset_all_settings()
+    end, 100)
+
+end
+
+-- 清理页面
+function tsb_wavein_page.cleanup()
+    page_active = false
+
+     -- 停止ADC运行
+    is_running = false
+    send_adc_operate()  -- 发送停止指令
+    log.info("chart", "ADC已停止运行")
+    
+    if chart_timer then
+        sys.timerStop(chart_timer)
+        chart_timer = nil
+    end
+
+    if adc_msg_subid then
+        sys.unsubscribe(adc_msg_subid)
+        adc_msg_subid = nil
+    end
+    if adc_wave_subid then
+        sys.unsubscribe(adc_wave_subid)
+        adc_wave_subid = nil
+    end
+    if tp_touch_subid then
+        sys.unsubscribe(tp_touch_subid)
+        tp_touch_subid = nil
+    end
+
+   
+
+    if main_container then
+        main_container:destroy()
+        main_container = nil
+        scroll_container = nil
+        chart = nil
+        freq_label = nil
+        vmax_label = nil
+        vmin_label = nil
+        trigger_threshold_input = nil
+        trigger_type_btn = nil
+        trigger_mode_btn = nil
+    end
+end
+
+return tsb_wavein_page

+ 383 - 0
tsb_ui/tsb_waveout_page.lua

@@ -0,0 +1,383 @@
+--[[
+@module  tsb_waveout_page
+@summary 波形输出页面
+@version 1.0
+@date    2026.03.18
+@author  李一玮
+@usage
+本文件是波形输出页面,用于配置和控制波形输出。
+]]
+
+local tsb_waveout_page = {}
+
+-- 页面UI元素
+local main_container = nil
+local max_amplitude_input = nil
+local min_amplitude_input = nil
+local frequency_input = nil
+local duty_cycle_input = nil
+local common_ui = require("tsb_common_page")
+local uart1_msg = require("uart1_msg")
+
+-- 配置参数
+local wave_type = "方波"      -- 波形类型
+local max_vol = "5.0"         -- 最大电压(V)
+local min_vol = "0.0"         -- 最小电压(V)
+local frequency = "1000"        -- 频率(Hz)
+local duty_cycle = "50"         -- 占空比(%)
+local is_running = false         -- 是否正在运行
+
+
+-- 发送DAC配置
+local function send_dac_config()
+    -- 收集配置参数
+    local config = {}
+    
+    -- 运行控制
+    config.run_control = is_running and 1 or 0
+    
+    -- 波形类型
+    local wave_type_map = {
+        ["方波"] = 0,
+        ["正弦波"] = 1,
+        ["三角波"] = 2,
+        ["锯齿波"] = 3
+    }
+    config.wave_type = wave_type_map[wave_type] or 0
+    
+    -- 最大电压和最小电压(转换为mV)
+    local max_vol = max_amplitude_input and tonumber(max_amplitude_input:get_text()) or 5
+    local min_vol = min_amplitude_input and tonumber(min_amplitude_input:get_text()) or 0
+    config.max_vol = math.floor(max_vol * 1000)
+    config.min_vol = math.floor(min_vol * 1000)
+    
+    -- 频率(Hz)
+    config.frequency = frequency_input and tonumber(frequency_input:get_text()) or 1000
+    
+    -- 占空比(%)
+    config.duty_cycle = duty_cycle_input and tonumber(duty_cycle_input:get_text()) or 50
+    
+    -- 发送配置
+    uart1_msg.send_dac_config(config)
+end
+
+
+-- 创建UI
+function tsb_waveout_page.create_ui()
+    main_container = airui.container({
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 320,
+        color = 0xF5F5F5,
+    })
+
+    --------------------- 标题栏 ------------------------
+    local title_bar = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 30,
+        color = 0x72DDF7,
+    })
+
+    airui.label({
+        parent = title_bar,
+        text = "波形输出",
+        x = 200,
+        y = 8,
+        w = 80,
+        h = 20,
+        font_size = 16,
+        color = 0xFFFFFF,
+    })
+
+    -- 标题栏公共信息展示
+    common_ui.add_battery_display(title_bar)
+    common_ui.create_back_button(title_bar, tsb_waveout_page.cleanup)
+    ---------------------------------------------------
+
+    -- 主容器
+    local scroll_container = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 30,
+        w = 480,
+        h = 270,
+        color = 0xF5F5F5,
+    })
+
+    local keyboard1 = airui.keyboard({
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 160,                        -- x, y, 键盘默认打开ALIGN_BOTTOM_MID,位置从中下方开始计算
+        mode = "numeric",               -- 键盘模式,可选 "text"/"upper"/"lower"/"numeric"
+        auto_hide = true,               -- 自动隐藏键盘
+        preview = true,          
+        preview_height = 35,
+        bg_color = 0xf1f1f1,            -- 键盘背景颜色为灰色,可选,不设置则透明
+        on_commit = function()          -- 确认事件回调,只有在按下确认键时才会触发
+            log.info("keyboard", "commit")
+            send_dac_config()
+        end
+    })
+
+    -- 配置容器
+    local config_container = airui.container({
+        parent = scroll_container,
+        x = 20,
+        y = 20,
+        w = 440,
+        h = 180,
+        color = 0xFFFFFF,
+        radius = 8,
+        border_width = 1,
+        border_color = 0xDDDDDD,
+    })
+
+    local config_y = 20
+
+    -- 波形类型
+    airui.label({
+        parent = config_container,
+        text = "波形类型",
+        x = 20,
+        y = config_y,
+        w = 80,
+        h = 25,
+        font_size = 14,
+        color = 0x333333,
+    })
+    local wave_type_dropdown = airui.dropdown({
+        parent = config_container,
+        x = 120,
+        y = config_y-8,
+        w = 100,
+        h = 35,
+        options = {"方波", "正弦波", "三角波", "锯齿波"},
+        default_index = 0,
+        on_change = function(self, idx)
+            local options = {"方波", "正弦波", "三角波", "锯齿波"}
+            wave_type = options[idx + 1]
+            log.info("waveout", "波形类型: " .. wave_type)
+            send_dac_config()
+        end
+    })
+    config_y = config_y + 40
+
+    -- 幅度(峰峰值)
+    airui.label({
+        parent = config_container,
+        text = "最大电压 (V)",
+        x = 20,
+        y = config_y,
+        w = 100,
+        h = 35,
+        font_size = 14,
+        color = 0x333333,
+    })
+    max_amplitude_input = airui.textarea({
+        parent = config_container,
+        x = 120,
+        y = config_y-8,
+        w = 100,
+        h = 35,
+        text = "5",
+        max_len = 5,
+        keyboard = keyboard1
+    })
+
+     airui.label({
+        parent = config_container,
+        text = "最小电压 (V)",
+        x = 230,
+        y = config_y,
+        w = 100,
+        h = 35,
+        font_size = 14,
+        color = 0x333333,
+    })
+    min_amplitude_input = airui.textarea({
+        parent = config_container,
+        x = 330,
+        y = config_y-8,
+        w = 100,
+        h = 35,
+        text = "0",
+        max_len = 5,
+        keyboard = keyboard1
+    })
+    config_y = config_y + 40
+
+    -- 频率
+    airui.label({
+        parent = config_container,
+        text = "频率 (Hz)",
+        x = 20,
+        y = config_y,
+        w = 80,
+        h = 25,
+        font_size = 14,
+        color = 0x333333,
+    })
+    frequency_input = airui.textarea({
+        parent = config_container,
+        x = 120,
+        y = config_y-8,
+        w = 100,
+        h = 35,
+        text = "1000",
+        max_len = 6,
+        keyboard = keyboard1
+    })
+    config_y = config_y + 40
+
+    -- 占空比(仅方波可用)
+    airui.label({
+        parent = config_container,
+        text = "占空比 (%)",
+        x = 20,
+        y = config_y,
+        w = 80,
+        h = 25,
+        font_size = 14,
+        color = 0x333333,
+    })
+    duty_cycle_input = airui.textarea({
+        parent = config_container,
+        x = 120,
+        y = config_y-8,
+        w = 100,
+        h = 35,
+        text = "50",
+        max_len = 3,
+        keyboard = keyboard1
+    })
+
+    -- 控制按钮区域
+    local button_container = airui.container({
+        parent = scroll_container,
+        x = 20,
+        y = 210,
+        w = 440,
+        h = 40,
+        color = 0xF5F5F5,
+    })
+
+    -- 开始按钮
+    local start_btn = airui.button({
+        parent = button_container,
+        text = "开始",
+        x = 100,
+        y = 0,
+        w = 100,
+        h = 35,
+        color = 0x4CAF50,
+        text_color = 0xFFFFFF,
+        style = { bg_color = 0x2B6FF1,border_color = 0x2B6FF1, text_color = 0xFFFFFF, radius = 8 },
+        on_click = function(self)
+            if not is_running then
+                is_running = true
+                self:set_text("运行中")
+                -- 获取配置参数
+                max_vol = max_amplitude_input:get_text()
+                min_vol = min_amplitude_input:get_text()
+                frequency = frequency_input:get_text()
+                duty_cycle = duty_cycle_input:get_text()
+                log.info("waveout", "开始输出波形", wave_type, max_vol, min_vol, frequency, duty_cycle)
+                -- 发送配置指令
+                send_dac_config()
+            end
+        end
+    })
+
+    -- 停止按钮
+    local stop_btn = airui.button({
+        parent = button_container,
+        text = "停止",
+        x = 240,
+        y = 0,
+        w = 100,
+        h = 35,
+        color = 0xF44336,
+        text_color = 0xFFFFFF,
+        on_click = function(self)
+            if is_running then
+                is_running = false
+                start_btn:set_text("开始")
+                log.info("waveout", "停止输出波形")
+                -- 发送停止指令
+                send_dac_config()
+            end
+        end
+    })
+
+    -- 底部信息
+    common_ui.create_status_bar(main_container)
+end
+
+-- 初始化页面
+function tsb_waveout_page.init(params)
+    tsb_waveout_page.create_ui()
+end
+
+
+-- 直接使用全局变量发送配置(不依赖UI元素)
+local function send_dac_config_direct()
+    local config = {}
+    
+    -- 运行控制
+    config.run_control = is_running and 1 or 0
+    
+    -- 波形类型
+    local wave_type_map = {
+        ["方波"] = 0,
+        ["正弦波"] = 1,
+        ["三角波"] = 2,
+        ["锯齿波"] = 3
+    }
+    config.wave_type = wave_type_map[wave_type] or 0
+    
+    -- 最大电压和最小电压(转换为mV)
+    config.max_vol = math.floor((tonumber(max_vol) or 5) * 1000)
+    config.min_vol = math.floor((tonumber(min_vol) or 0) * 1000)
+    
+    -- 频率(Hz)
+    config.frequency = tonumber(frequency) or 1000
+    
+    -- 占空比(%)
+    config.duty_cycle = tonumber(duty_cycle) or 50
+    
+    -- 发送配置
+    uart1_msg.send_dac_config(config)
+end
+
+-- 恢复默认配置并下发
+local function restore_default_config()
+    -- 恢复默认配置参数
+    wave_type = "方波"
+    max_vol = "5.0"
+    min_vol = "0.0"
+    frequency = "1000"
+    duty_cycle = "50"
+    is_running = false
+    
+    -- 使用不依赖UI的方式下发配置
+    send_dac_config_direct()
+    log.info("waveout", "已恢复默认配置并下发")
+end
+
+-- 清理页面
+function tsb_waveout_page.cleanup()
+    -- 恢复默认配置并下发(在销毁UI之前执行)
+    restore_default_config()
+    
+    if main_container then
+        main_container:destroy()
+        main_container = nil
+    end
+end
+
+return tsb_waveout_page

+ 464 - 0
tsb_ui/tsb_wlan_page.lua

@@ -0,0 +1,464 @@
+--[[
+@module  tsb_wlan_page
+@summary 网络配置页面
+@version 1.0
+@date    2026.03.17
+@author  李一玮
+@usage
+本文件是网络配置页面,展示网络配置的各种用法。
+]]
+
+local tsb_wlan_page = {}
+
+-- 页面UI元素
+local main_container = nil
+local common_ui = require("tsb_common_page")
+local net_manager = require("net_manager")  -- 引入网络管理器
+
+-- 网络状态UI元素(需要在事件回调中访问)
+local network4g_status = nil
+local network4g_switch = nil
+local wifi_status = nil
+local wifi_switch = nil          -- WiFi开关按钮
+local ip_address_label = nil     -- IP地址标签
+local signal_strength_label = nil -- 信号强度标签
+
+-- 创建UI
+function tsb_wlan_page.create_ui()
+    main_container = airui.container({
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 320,
+        color = 0xFFFFFF,
+    })
+
+    --------------------- 标题栏 ------------------------
+    local title_bar = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 30,
+        color = 0xCE93D8,
+    })
+
+    airui.label({
+        parent = title_bar,
+        text = "网络设置",
+        x = 200,
+        y = 10,
+        w = 80,
+        h = 20,
+        font_size = 16,
+        color = 0xFFFFFF,
+    })
+
+    -- 标题栏公共信息展示
+    common_ui.add_battery_display(title_bar)
+    common_ui.create_back_button(title_bar, tsb_wlan_page.cleanup)
+    ---------------------------------------------------
+
+    -- 滚动容器
+    local scroll_container = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 30,
+        w = 480,
+        h = 260,
+        color = 0xFFFFFF,
+    })
+
+    local keyboard1 = airui.keyboard({
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 160,                        -- x, y, 键盘默认打开ALIGN_BOTTOM_MID,位置从中下方开始计算
+        mode = "lower",               -- 键盘模式,可选 "text"/"upper"/"lower"/"numeric"
+        auto_hide = true,               -- 自动隐藏键盘
+        preview = true,          
+        preview_height = 35,
+        bg_color = 0xf1f1f1,            -- 键盘背景颜色为灰色,可选,不设置则透明
+        on_commit = function()          -- 确认事件回调,只有在按下确认键时才会触发
+            log.info("keyboard", "commit")
+        end
+    })
+
+    --------------------- 4G网络配置 ------------------------
+    -- 4G网络容器
+    local network4g_container = airui.container({
+        parent = scroll_container,
+        x = 20,
+        y = 10,
+        w = 440,
+        h = 80,
+        color = 0xF5F5F5,
+        radius = 8,
+    })
+
+    -- 4G网络标题
+    airui.label({
+        parent = network4g_container,
+        text = "4G网络配置",
+        x = 10,
+        y = 10,
+        w = 120,
+        h = 30,
+        font_size = 16,
+        color = 0xCE93D8,
+    })
+
+    -- 4G开关标签
+    airui.label({
+        parent = network4g_container,
+        text = "4G网络:",
+        x = 10,
+        y = 40,
+        w = 80,
+        h = 30,
+        font_size = 14,
+    })
+
+    -- 4G开关状态
+    network4g_status = airui.label({
+        parent = network4g_container,
+        text = "开启",
+        x = 80,
+        y = 40,
+        w = 60,
+        h = 30,
+        font_size = 14,
+        color = 0x4CAF50,
+    })
+
+    -- 4G开关按钮
+    network4g_switch = airui.button({
+        parent = network4g_container,
+        x = 350,
+        y = 40,
+        w = 80,
+        h = 30,
+        text = "关闭",
+        on_click = function(self)
+            local current_status = network4g_status:get_text()
+            if current_status == "开启" then
+                network4g_status:set_text("关闭")
+                network4g_status:set_color(0xF44336)
+                self:set_text("开启")
+                log.info("set_4g", "关闭4G网络")
+                net_manager.disable_mobile()  -- 调用net_manager关闭4G
+            else
+                network4g_status:set_text("开启")
+                network4g_status:set_color(0x4CAF50)
+                self:set_text("关闭")
+                log.info("set_4g", "开启4G网络")
+                net_manager.enable_mobile()  -- 调用net_manager开启4G
+            end
+        end
+    })
+    ---------------------------------------------------
+
+    --------------------- WIFI配置 ------------------------
+    -- WIFI配置容器
+    local wifi_container = airui.container({
+        parent = scroll_container,
+        x = 20,
+        y = 100,
+        w = 440,
+        h = 160,
+        color = 0xF5F5F5,
+        radius = 8,
+    })
+
+    -- WIFI配置标题
+    airui.label({
+        parent = wifi_container,
+        text = "WIFI配置",
+        x = 10,
+        y = 10,
+        w = 100,
+        h = 30,
+        font_size = 16,
+        color = 0xCE93D8,
+    })
+
+    -- WIFI开关标签
+    airui.label({
+        parent = wifi_container,
+        text = "WIFI:",
+        x = 10,
+        y = 40,
+        w = 60,
+        h = 30,
+        font_size = 14,
+    })
+
+    -- WIFI开关状态
+    wifi_status = airui.label({
+        parent = wifi_container,
+        text = "关闭",
+        x = 80,
+        y = 40,
+        w = 60,
+        h = 30,
+        font_size = 14,
+        color = 0xF44336,
+    })
+
+    -- WIFI开关按钮
+    wifi_switch = airui.button({
+        parent = wifi_container,
+        x = 350,
+        y = 40,
+        w = 80,
+        h = 30,
+        text = "开启",
+        on_click = function(self)
+            local current_status = wifi_status:get_text()
+            if current_status == "开启" then
+                wifi_status:set_text("关闭")
+                wifi_status:set_color(0xF44336)
+                self:set_text("开启")
+                log.info("set_wifi", "关闭WIFI")
+                net_manager.disconnect_wifi()  -- 调用net_manager断开WiFi
+            else
+                wifi_status:set_text("开启")
+                wifi_status:set_color(0x4CAF50)
+                self:set_text("关闭")
+                log.info("set_wifi", "开启WIFI")
+                -- 这里可以添加开启WIFI的逻辑
+            end
+        end
+    })
+
+    -- WIFI名称标签
+    airui.label({
+        parent = wifi_container,
+        text = "WIFI名称:",
+        x = 10,
+        y = 80,
+        w = 100,
+        h = 30,
+        font_size = 14,
+    })
+
+    -- WIFI名称输入框
+    local wifi_name_input = airui.textarea({
+        parent = wifi_container,
+        x = 110,
+        y = 80,
+        w = 200,
+        h = 30,
+        text = "",
+        keyboard = keyboard1
+    })
+
+    -- WIFI密码标签
+    airui.label({
+        parent = wifi_container,
+        text = "WIFI密码:",
+        x = 10,
+        y = 120,
+        w = 100,
+        h = 30,
+        font_size = 14,
+    })
+
+    -- WIFI密码输入框
+    local wifi_password_input = airui.textarea({
+        parent = wifi_container,
+        x = 110,
+        y = 120,
+        w = 200,
+        h = 30,
+        text = "",
+        keyboard = keyboard1
+    })
+
+    -- 连接按钮
+    local connect_wifi_btn = airui.button({
+        parent = wifi_container,
+        x = 320,
+        y = 90,
+        w = 100,
+        h = 40,
+        text = "连接",
+        on_click = function(self)
+            local current_wifi_status = wifi_status:get_text()
+            if current_wifi_status ~= "开启" then
+                log.info("set_wifi", "请先开启WiFi")
+                return
+            end
+            
+            local wifi_name = wifi_name_input:get_text()
+            local wifi_password = wifi_password_input:get_text()
+            
+            if not wifi_name or wifi_name == "" then
+                log.info("set_wifi", "请输入WiFi名称")
+                return
+            end
+            
+            log.info("set_wifi", "连接WIFI:" .. wifi_name)
+            net_manager.connect_wifi(wifi_name, wifi_password)  -- 调用net_manager连接WiFi
+        end
+    })
+    ---------------------------------------------------
+
+    --------------------- 网络状态 ------------------------
+    -- 网络状态容器
+    local network_status_container = airui.container({
+        parent = scroll_container,
+        x = 20,
+        y = 280,
+        w = 440,
+        h = 100,
+        color = 0xF5F5F5,
+        radius = 8,
+    })
+
+    -- 网络状态标题
+    airui.label({
+        parent = network_status_container,
+        text = "网络状态",
+        x = 10,
+        y = 10,
+        w = 100,
+        h = 30,
+        font_size = 16,
+        color = 0xCE93D8,
+    })
+
+    -- IP地址标签
+    airui.label({
+        parent = network_status_container,
+        text = "IP地址:",
+        x = 10,
+        y = 40,
+        w = 80,
+        h = 30,
+        font_size = 14,
+    })
+
+    -- IP地址值
+    ip_address_label = airui.label({
+        parent = network_status_container,
+        text = "0.0.0.0",
+        x = 100,
+        y = 40,
+        w = 200,
+        h = 30,
+        font_size = 14,
+    })
+
+    -- 信号强度标签
+    airui.label({
+        parent = network_status_container,
+        text = "信号强度:",
+        x = 10,
+        y = 70,
+        w = 80,
+        h = 30,
+        font_size = 14,
+    })
+
+    -- 信号强度值
+    signal_strength_label = airui.label({
+        parent = network_status_container,
+        text = "0",
+        x = 100,
+        y = 70,
+        w = 50,
+        h = 30,
+        font_size = 14,
+    })
+    ---------------------------------------------------
+
+    -- 底部信息
+    common_ui.create_status_bar(main_container)
+end
+
+----- 初始化页面
+function tsb_wlan_page.init(params)
+    tsb_wlan_page.create_ui()
+    
+    -- 初始化网络管理器(默认启动4G)
+    net_manager.init()
+    
+    -- 更新WiFi信息显示
+    local function update_wifi_info()
+        local wlan_info = wlan.getInfo()
+        if wlan_info then
+            -- 更新IP地址(使用网关作为IP地址显示)
+            if ip_address_label then
+                ip_address_label:set_text(wlan_info.gw or "0.0.0.0")
+            end
+            
+            -- 更新信号强度
+            if signal_strength_label then
+                local rssi = wlan_info.rssi
+                if rssi then
+                    signal_strength_label:set_text(rssi .. " dBm")
+                    -- 根据信号强度设置颜色
+                    if rssi > -60 then
+                        signal_strength_label:set_color(0x4CAF50)  -- 绿色,信号强
+                    elseif rssi >= -80 then
+                        signal_strength_label:set_color(0xFF9800)  -- 橙色,信号中等
+                    else
+                        signal_strength_label:set_color(0xF44336)  -- 红色,信号差
+                    end
+                else
+                    signal_strength_label:set_text("0")
+                end
+            end
+            
+            log.info("wlan_page", "WiFi信息更新", json.encode(wlan_info))
+        end
+    end
+    
+    -- 监听网络状态变化,更新UI
+    sys.subscribe("NET_STATE_CHANGED", function(state)
+        log.info("wlan_page", "网络状态变化:", state)
+        
+        if state == net_manager.STATE.WIFI_CONNECTED then
+            -- WiFi连接成功,更新4G状态显示为关闭
+            network4g_status:set_text("关闭")
+            network4g_status:set_color(0xF44336)
+            network4g_switch:set_text("开启")
+            -- 更新WiFi信息
+            update_wifi_info()
+        elseif state == net_manager.STATE.MOBILE_CONNECTED then
+            -- 4G连接成功
+            network4g_status:set_text("开启")
+            network4g_status:set_color(0x4CAF50)
+            network4g_switch:set_text("关闭")
+            
+            -- 关闭WiFi UI状态
+            if wifi_status then
+                wifi_status:set_text("关闭")
+                wifi_status:set_color(0xF44336)
+            end
+            if wifi_switch then
+                wifi_switch:set_text("开启")
+            end
+            
+            -- 重置WiFi信息显示
+            if ip_address_label then
+                ip_address_label:set_text("0.0.0.0")
+            end
+            if signal_strength_label then
+                signal_strength_label:set_text("0")
+                signal_strength_label:set_color(0x000000)
+            end
+        end
+    end)
+end
+
+-- 清理页面
+function tsb_wlan_page.cleanup()
+    if main_container then
+        main_container:destroy()
+        main_container = nil
+    end
+end
+
+return tsb_wlan_page

+ 340 - 0
tsb_ui/tsb_ywy_1_page.lua

@@ -0,0 +1,340 @@
+--[[
+@module  tsb_ywy_1_page
+@summary 液位仪查询演示页面
+@version 1.0
+@date    2026.03.16
+@author  李一玮
+@usage
+    本文件是液位仪查询演示页面,展示液位仪查询的各种用法。
+]]
+
+local tsb_ywy_1_page = {}
+local common_ui = require("tsb_common_page")
+
+-- 页面UI元素
+local main_container = nil
+
+-- 创建UI
+function tsb_ywy_1_page.create_ui()
+    main_container = airui.container({
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 320,
+        color = 0xF5F5F5,
+    })
+
+    --------------------- 标题栏 ------------------------
+    local title_bar = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 30,
+        color = 0x4CAF50,
+    })
+
+    airui.label({
+        parent = title_bar,
+        text = "液位仪查询",
+        x = 200,
+        y = 8,
+        w = 120,
+        h = 20,
+        font_size = 16,
+        color = 0xFFFFFF,
+    })
+
+    -- 标题栏公共信息展示
+    common_ui.add_battery_display(title_bar)
+    common_ui.create_back_button(title_bar, tsb_ywy_1_page.cleanup)
+    ---------------------------------------------------
+
+    -- 滚动容器
+    local scroll_container = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 30,
+        w = 480,
+        h = 290,
+        color = 0xFFFFFF,
+    })
+
+    ------------------- 分页内容区 ------------------------
+    --液位仪查询页面
+    local query_page = airui.container({
+        parent = scroll_container,
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 290,
+        color = 0xFFFFFF,--0xE8F5E9
+    })
+
+    --------------------- 总罐数和查询按钮 ------------------------
+    local top_container = airui.container({
+        parent = query_page,
+        x = 0,
+        y = 5,
+        w = 480,
+        h = 40,
+        color = 0xFFFFFF,
+    })
+
+    -- 总罐数标签
+    airui.label({
+        parent = top_container,
+        text = "总罐数",
+        x = 10,
+        y = 10,
+        w = 60,
+        h = 30,
+        font_size = 15,
+    })
+
+    -- 总罐数值
+    local total_tanks_label = airui.label({
+        parent = top_container,
+        text = "12",
+        x = 70,
+        y = 10,
+        w = 40,
+        h = 30,
+        font_size = 15,
+    })
+
+    -- 单位标签
+    airui.label({
+        parent = top_container,
+        text = "个",
+        x = 100,
+        y = 10,
+        w = 20,
+        h = 30,
+        font_size = 15,
+    })
+
+    -- A口查询按钮
+    local a_query_btn = airui.button({
+        parent = top_container,
+        x = 210,
+        y = 1,
+        w = 80,
+        h = 30,
+        text = "A口查询",
+        on_click = function(self)
+            log.info("ywy_query", "A口查询按钮被点击")
+        end
+    })
+
+    -- B口查询按钮
+    local b_query_btn = airui.button({
+        parent = top_container,
+        x = 300,
+        y = 1,
+        w = 80,
+        h = 30,
+        text = "B口查询",
+        on_click = function(self)
+            log.info("ywy_query", "B口查询按钮被点击")
+        end
+    })
+
+    -- 485查询按钮
+    local rs485_query_btn = airui.button({
+        parent = top_container,
+        x = 390,
+        y = 1,
+        w = 80,
+        h = 30,
+        text = "485查询",
+        on_click = function(self)
+            log.info("ywy_query", "485查询按钮被点击")
+        end
+    })
+    -------------------------------------------------
+
+    ------------------- 表格区域 ------------------------
+    local table_container = airui.container({
+        parent = query_page,
+        x = 0,
+        y = 40,
+        w = 480,
+        h = 220,
+        color = 0xE8F5E9,
+    })
+
+    -- 创建表格
+    local tank_table = airui.table({
+        parent = table_container,
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 220,
+        rows = 11, -- 10个罐 + 表头
+        cols = 10,
+        col_width = {70, 100, 100, 100, 70, 70, 70, 80, 70, 150}, -- 调整列宽以适应内容,确保表头横向显示
+        border_color = 0x4CAF50,
+    })
+
+    -- 设置表头
+    local headers = {"罐号", "油水体积1", "油水体积2", "剩余体积", "油高", "水高", "温度", "水体积", "串口", "读取时间"}
+    for i, header in ipairs(headers) do
+        tank_table:set_cell_text(0, i-1, header)
+    end
+
+    -- 示例数据
+    local tank_data = {
+        {"1", "14919.52", "14767.34", "12080.47", "16577.25", "0.00", "28.50", "0.00", "A口", "2024/07/02 09:30:46"},
+        {"2", "20292.46", "20085.48", "6707.53", "22547.17", "0.00", "28.50", "0.00", "A口", "2024/07/02 09:30:46"},
+        {"3", "10936.19", "10824.64", "16063.80", "12151.32", "0.00", "28.50", "0.00", "A口", "2024/07/02 09:30:46"},
+        {"4", "22748.73", "22516.69", "4251.26", "25276.37", "0.00", "28.50", "0.00", "A口", "2024/07/02 09:30:46"},
+        {"5", "7282.67", "7208.38", "19717.32", "8091.85", "0.00", "28.50", "0.00", "A口", "2024/07/02 09:30:46"},
+        {"6", "14497.98", "14350.10", "12502.01", "16108.87", "0.00", "28.50", "0.00", "A口", "2024/07/02 09:30:46"},
+        {"7", "1984.31", "1964.07", "25015.68", "2204.79", "0.00", "28.50", "0.00", "A口", "2024/07/02 09:30:46"},
+        {"8", "9565.87", "9468.30", "17434.12", "10628.75", "0.00", "28.50", "0.00", "A口", "2024/07/02 09:30:46"},
+        {"9", "15678.90", "15432.10", "11234.56", "17890.12", "0.00", "28.50", "0.00", "A口", "2024/07/02 09:30:46"},
+        {"10", "8765.43", "8654.32", "18976.54", "9876.54", "0.00", "28.50", "0.00", "A口", "2024/07/02 09:30:46"},
+    }
+
+    -- 填充表格数据
+    for i, data in ipairs(tank_data) do
+        for j, value in ipairs(data) do
+            tank_table:set_cell_text(i, j-1, value)
+        end
+    end
+    -------------------------------------------------
+
+    ------------------- 执行结果和日志按钮 ------------------------
+    local bottom_container = airui.container({
+        parent = query_page,
+        x = 0,
+        y = 260,
+        w = 480,
+        h = 30,
+        color = 0xFFFFFF,
+    })
+
+    -- 执行结果标签
+    airui.label({
+        parent = bottom_container,
+        text = "执行结果:",
+        x = 10,
+        y = 10,
+        w = 80,
+        h = 20,
+        font_size = 15,
+    })
+
+    -- 执行结果值
+    local result_label = airui.label({
+        parent = bottom_container,
+        text = "执行成功",
+        x = 90,
+        y = 10,
+        w = 100,
+        h = 20,
+        font_size = 15,
+    })
+
+
+    -- 日志窗口
+    local log_window = airui.container({
+        parent = scroll_container,
+        x = 20,
+        y = 5,
+        w = 440,
+        h = 250,
+        color = 0xF1F8E9,
+        radius = 0,
+    })
+
+    -- 日志窗口标题栏
+    local log_title_bar = airui.container({
+        parent = log_window,
+        x = 0,
+        y = 0,
+        w = 440,
+        h = 30,
+        color = 0x4CAF50,
+    })
+
+    airui.label({
+        parent = log_title_bar,
+        text = "原始日志",
+        x = 10,
+        y = 8,
+        w = 150,
+        h = 20,
+        font_size = 15,
+        color = 0xFFFFFF,
+    })
+
+    -- 关闭按钮
+    local close_btn = airui.button({
+        parent = log_title_bar,
+        x = 408,
+        y = 2,
+        w = 30,
+        h = 28,
+        text = "X",
+        on_click = function(self)
+            log_window:hide()
+        end
+    })
+
+    -- 日志内容区域
+    local log_content = airui.textarea({
+        parent = log_window,
+        x = 10,
+        y = 40,
+        w = 420,
+        h = 200,
+        text = "00 01 02 03 04 05 06 07\n" ..
+               "08 09 0A 0B 0C 0D 0E 0F\n" ..
+               "10 11 12 13 14 15 16 17\n" ..
+               "18 19 1A 1B 1C 1D 1E 1F\n" ..
+               "20 21 22 23 24 25 26 27\n" ..
+               "28 29 2A 2B 2C 2D 2E 2F\n" ..
+               "30 31 32 33 34 35 36 37\n" ..
+               "38 39 3A 3B 3C 3D 3E 3F",
+    })
+
+    log_window:hide()
+
+    -- 查看原始日志按钮
+    local log_btn = airui.button({
+        parent = bottom_container,
+        x = 375,
+        y = 1,
+        w = 100,
+        h = 27,
+        text = "查看原始日志",
+        on_click = function(self)
+            log.info("ywy_query", "查看原始日志按钮被点击")
+            -- 显示日志窗口
+            log_window:open()
+        end
+    })
+    ---------------------------------------------------
+
+    -- 底部信息
+    --common_ui.create_status_bar(main_container)
+end
+
+-- 初始化页面
+function tsb_ywy_1_page.init(params)
+    tsb_ywy_1_page.create_ui()
+end
+
+-- 清理页面
+function tsb_ywy_1_page.cleanup()
+    -- 停止定时器
+    common_ui.cleanup()
+    if main_container then
+        main_container:destroy()
+        main_container = nil
+    end
+end
+
+return tsb_ywy_1_page

+ 163 - 0
tsb_ui/tsb_ywy_2_page.lua

@@ -0,0 +1,163 @@
+--[[
+@module  tsb_ywy_2_page
+@summary 液位仪透传页面
+@version 1.0
+@date    2026.03.16
+@author  李一玮
+@usage
+本文件是液位仪透传页面,展示液位仪的透传功能。
+]]
+
+local tsb_ywy_2_page = {}
+local common_ui = require("tsb_common_page")
+
+-- 页面UI元素
+local main_container = nil
+
+-- 创建UI
+function tsb_ywy_2_page.create_ui()
+    main_container = airui.container({
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 320,
+        color = 0xF5F5F5,
+    })
+
+    --------------------- 标题栏 ------------------------
+    local title_bar = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 30,
+        color = 0x4CAF50,
+    })
+
+    airui.label({
+        parent = title_bar,
+        text = "液位仪透传",
+        x = 200,
+        y = 8,
+        w = 120,
+        h = 20,
+        font_size = 16,
+        color = 0xFFFFFF,
+    })
+
+    -- 标题栏公共信息展示
+    common_ui.add_battery_display(title_bar)
+    common_ui.create_back_button(title_bar, tsb_ywy_2_page.cleanup)
+    ---------------------------------------------------
+
+    -- 滚动容器
+    local scroll_container = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 30,
+        w = 480,
+        h = 290,
+        color = 0xFFFFFF,--0xF5F5F5
+    })
+    ---------------------------------------------------
+
+    --------------------- 分页内容区 ------------------------
+    -- 液位仪透传页面
+    local passthrough_page = airui.container({
+        parent = scroll_container,
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 290,
+        color = 0xF0F7E8,--0xF1F8E9
+    })
+
+    --------------------- A口接收数据 ------------------------
+    -- A口标签
+    airui.label({
+        parent = passthrough_page,
+        text = "A口接收数据",
+        x = 65,
+        y = 10,
+        w = 120,
+        h = 20,
+        font_size = 16,
+    })
+
+    -- A口数据容器
+    local a_port_container = airui.container({
+        parent = passthrough_page,
+        x = 2,
+        y = 30,
+        w = 240,
+        h = 250,
+        color = 0xFFFFFF,
+    })
+
+    -- A口数据显示
+    local a_port_data = airui.textarea({
+        parent = a_port_container,
+        x = 5,
+        y = 5,
+        w = 230,
+        h = 245,
+        text = "00 01 02 03 04 05 06 07\n" ..
+               "08 09 0A 0B 0C 0D 0E 0F\n" 
+    })
+    ---------------------------------------------------
+
+    --------------------- B口接收数据 ------------------------
+    -- B口标签
+    airui.label({
+        parent = passthrough_page,
+        text = "B口接收数据",
+        x = 310,
+        y = 10,
+        w = 120,
+        h = 20,
+        font_size = 16,
+    })
+
+    -- B口数据容器
+    local b_port_container = airui.container({
+        parent = passthrough_page,
+        x = 238,
+        y = 30,
+        w = 240,
+        h = 250,
+        color = 0xFFFFFF,
+    })
+
+    -- B口数据显示
+    local b_port_data = airui.textarea({
+        parent = b_port_container,
+        x = 5,
+        y = 5,
+        w = 230,
+        h = 245,
+        text = "40 41 42 43 44 45 46 47\n" ..
+               "48 49 4A 4B 4C 4D 4E 4F\n"
+    })
+    ---------------------------------------------------
+
+    -- 底部信息
+    --common_ui.create_status_bar(main_container)
+end
+
+-- 初始化页面
+function tsb_ywy_2_page.init(params)
+    tsb_ywy_2_page.create_ui()
+end
+
+-- 清理页面
+function tsb_ywy_2_page.cleanup()
+    -- 停止定时器
+    common_ui.cleanup()
+    if main_container then
+        main_container:destroy()
+        main_container = nil
+    end
+end
+
+
+return tsb_ywy_2_page

+ 146 - 0
tsb_ui/tsb_ywy_page.lua

@@ -0,0 +1,146 @@
+--[[
+@module  tsb_ywy_page
+@summary 液位仪演示页面
+@version 1.0
+@date    2026.02.05
+@author  江访
+@usage
+本文件是液位仪演示页面,展示液位仪的各种用法。
+]]
+
+local tsb_ywy_page = {}
+local common_ui = require("tsb_common_page")
+
+-- 页面UI元素
+local main_container = nil
+
+-- 创建UI
+function tsb_ywy_page.create_ui()
+    main_container = airui.container({
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 320,
+        color = 0xF5F5F5,
+    })
+
+    --------------------- 标题栏 ------------------------
+    local title_bar = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 30,
+        color = 0x4CAF50,
+    })
+
+    airui.label({
+        parent = title_bar,
+        text = "液位仪",
+        x = 210,
+        y = 8,
+        w = 80,
+        h = 20,
+        font_size = 16,
+        color = 0xFFFFFF,
+    })
+
+    -- 标题栏公共信息展示
+    common_ui.add_battery_display(title_bar)
+    common_ui.create_back_button(title_bar, tsb_ywy_page.cleanup)
+    ---------------------------------------------------
+
+    -- 滚动容器
+    local scroll_container = airui.container({
+        parent = main_container,
+        x = 0,
+        y = 30,
+        w = 480,
+        h = 270,
+        color = 0xFFFFFF,
+    })
+
+    --------------------- 第一排控制区 ------------------------
+    local control_container = airui.container({
+        parent = scroll_container,
+        x = 0,
+        y = 0,
+        w = 480,
+        h = 80,
+        color = 0xFFFFFF,
+    })
+
+    -- 波特率标签
+    airui.label({
+        parent = control_container,
+        text = "波特率:",
+        x = 10,
+        y = 28,
+        w = 60,
+        h = 30,
+        font_size = 15,
+    })
+
+    -- 波特率下拉框
+    local baudrate_dropdown = airui.dropdown({
+        parent = control_container,
+        x = 70,
+        y = 20,
+        w = 120,
+        h = 35,
+        options = {"2400", "4800", "9600", "19200", "38400", "57600", "115200"},
+        default_index = 2, -- 默认选择9600
+        on_change = function(self,idx)
+            log.info("baudrate", "选择了波特率:" .. self.options[idx + 1])
+        end
+    })
+
+    -- 液位仪查询按钮
+    local query_btn = airui.button({
+        parent = control_container,
+        x = 220,
+        y = 20,
+        w = 100,
+        h = 35,
+        text = "液位仪查询",
+        on_click = function(self)
+            log.info("ywy_page", "点击液位仪查询按钮")
+            _G.show_page("tsb_ywy_1_page")
+        end
+    })
+
+    -- 液位仪透传按钮
+    local passthrough_btn = airui.button({
+        parent = control_container,
+        x = 340,
+        y = 20,
+        w = 100,
+        h = 35,
+        text = "液位仪透传",
+        on_click = function(self)
+            log.info("ywy_page", "点击液位仪透传按钮")
+            _G.show_page("tsb_ywy_2_page")
+        end
+    })
+    ---------------------------------------------------
+
+    -- 底部信息
+    common_ui.create_status_bar(main_container)
+end
+
+-- 初始化页面
+function tsb_ywy_page.init(params)
+    tsb_ywy_page.create_ui()
+end
+
+-- 清理页面
+function tsb_ywy_page.cleanup()
+    -- 停止定时器
+    common_ui.cleanup()
+    if main_container then
+        main_container:destroy()
+        main_container = nil
+    end
+end
+
+return tsb_ywy_page

+ 416 - 0
uart1_msg.lua

@@ -0,0 +1,416 @@
+--[[
+@module  uart1_adc
+@summary UART1接收MCU发送的ADC数据(对齐新C结构体协议)
+@usage  处理粘包分包、协议解析、CRC校验
+]]
+local uartid = 1
+local uart_send = {}
+local rdbuf = ""
+
+-- ===================== 【新版协议常量】 =====================
+local FRAME_HEAD = "\xFE\xFE"
+local FRAME_HEAD_LEN = 12              -- 帧头长度不变
+local CHECK_FIELD_LEN = 2              -- CRC 2字节
+local DEVICE_TYPE = 0x9102
+
+-- 【关键】ADC 数据长度:500点 uint16 = 1000字节
+local ADC_POINT_CNT = 500
+local ADC_DATA_LEN = ADC_POINT_CNT * 2 -- 1000 字节
+
+-- 【关键】完整 adc_data_up_t 结构体总长度(必须和C语言一致)
+local ADC_STRUCT_LEN = 2 + 4 + 4 + 4 + 2 + 2 + 2 + 1 + 2 + (ADC_POINT_CNT * 2)
+-- 计算结果 = 1023 字节
+
+-- ===================== 帧头解析(完全不变) =====================
+local function head_paser(data)
+    local netHeader = {pro_ver=0,msg_id=0,msg_type1=0,msg_type2=0,msg_len=0}
+    local nextpos = 1
+
+    if #data < FRAME_HEAD_LEN then
+        return false,nil
+    end
+
+    nextpos, netHeader.head = pack.unpack(data, "<H", nextpos)
+    if netHeader.head ~= 0xFEFE then
+        log.warn("uart1","帧头错误")
+        return false,nil
+    end
+
+    nextpos, netHeader.pro_ver   = pack.unpack(data, "b", nextpos)
+    nextpos, netHeader.msg_id    = pack.unpack(data, "<I", nextpos)
+    nextpos, netHeader.msg_type1 = pack.unpack(data, "b", nextpos)
+    nextpos, netHeader.msg_type2 = pack.unpack(data, "<H", nextpos)
+    nextpos, netHeader.msg_len   = pack.unpack(data, "<H", nextpos)
+
+    return true, netHeader
+end
+
+-- ===================== 【核心】完整帧解析 =====================
+local function parse(data)
+    if not data or #data == 0 then
+        return false, data, nil
+    end
+
+    local head_pos = string.find(data, FRAME_HEAD, 1, true)
+    if not head_pos then
+        return false, data, nil
+    end
+
+    local remain_buf = string.sub(data, head_pos)
+
+    if #remain_buf < FRAME_HEAD_LEN then
+        return false, remain_buf, nil
+    end
+
+    local head_ok, netHeader = head_paser(remain_buf)
+    if not head_ok then
+        return true, string.sub(remain_buf, 2), nil
+    end
+
+    -- 【关键】msg_len = adc结构体长度 + 2字节CRC
+    local frame_total_len = FRAME_HEAD_LEN + netHeader.msg_len
+
+    -- 长度校验:必须等于 ADC结构体长度 + 2
+    if netHeader.msg_len ~= (ADC_STRUCT_LEN + CHECK_FIELD_LEN) then
+        log.error("uart1","长度错误,期望:", ADC_STRUCT_LEN + CHECK_FIELD_LEN, "实际:", netHeader.msg_len)
+        return true, string.sub(remain_buf, 2), nil
+    end
+
+    if #remain_buf < frame_total_len then
+        return false, remain_buf, nil
+    end
+
+    local full_frame = string.sub(remain_buf, 1, frame_total_len)
+    local unproc_buf = string.sub(remain_buf, frame_total_len + 1)
+
+    return true, unproc_buf, full_frame
+end
+
+-- ===================== 接收拼接(不变) =====================
+local function proc(data)
+    if not data or #data == 0 then return end
+    rdbuf = rdbuf .. data
+    local result, unproc_buf, full_frame
+    unproc_buf = rdbuf
+
+    while true do
+        result, unproc_buf, full_frame = parse(unproc_buf)
+        if full_frame then
+            sys.publish("UART1_ADC_RECIVE", full_frame)
+        end
+        if not result or not unproc_buf or unproc_buf == "" then
+            break
+        end
+    end
+    rdbuf = unproc_buf or ""
+end
+
+-- ===================== 串口回调(不变) =====================
+local function uart_cb(id, len)
+    local data = ""
+    repeat
+        data = uart.read(id, 1050)
+        if not data or #data == 0 then break end
+        proc(data)
+        --log.info("uart", "receive", id, #data)
+    until data == ""
+end
+
+-- local rxbuff = zbuff.create(1050)
+-- local function uart_cb(id, len)
+--     local rx_len = uart.rx(id, rxbuff)  -- 只读一次
+--     if rx_len > 0 then
+--         local data_str = rxbuff:toStr(1, rx_len)
+--         proc(data_str)
+--         log.info("uart", "receive", rx_len)
+--     end
+-- end
+
+-- 全局偏移量(不变)
+_G.Y_FULL_SCALE = 5
+_G.x_offset = 0
+_G.y_offset = 0
+
+-- ===================== 【核心:新版结构体数据解析】 =====================
+sys.subscribe("UART1_ADC_RECIVE", function(full_frame)
+    if not full_frame or #full_frame == 0 then return end
+
+    local head_ok, netHeader = head_paser(full_frame)
+    if not head_ok then return end
+
+    -- 1. 拆分:帧头 + 结构体数据(info) + CRC
+    local total_frame_len = FRAME_HEAD_LEN + netHeader.msg_len
+    local struct_data = string.sub(full_frame, FRAME_HEAD_LEN + 1, FRAME_HEAD_LEN + ADC_STRUCT_LEN)
+    local recv_crc_str  = string.sub(full_frame, total_frame_len - 1, total_frame_len)
+    local crc_calc_data  = string.sub(full_frame, 1, total_frame_len - CHECK_FIELD_LEN)
+
+    -- 2. CRC 校验
+    local _, recv_crc = pack.unpack(recv_crc_str, "<H", 1)
+    local calc_crc = crypto.crc16("IBM", crc_calc_data)
+
+    --log.info("uart1","CRC校验:接收=",string.format("%04X",recv_crc),"计算=",string.format("%04X",calc_crc))
+
+    if recv_crc ~= calc_crc then
+        log.error("uart1","CRC校验失败")
+        return
+    end
+    --log.info("uart1","CRC校验成功")
+
+    -- ===================== 【关键】解析 adc_data_up_t 结构体 =====================
+    local pos = 1
+    local adc_struct = {}
+
+    _, adc_struct.device_type     = pack.unpack(struct_data, "<H", pos)  pos = pos + 2
+    _, adc_struct.device_sn       = pack.unpack(struct_data, "<I", pos)  pos = pos + 4
+    _, adc_struct.reserve         = pack.unpack(struct_data, "<I", pos)  pos = pos + 4
+    _, adc_struct.frequency       = pack.unpack(struct_data, "<I", pos)  pos = pos + 4
+    _, adc_struct.max_vol         = pack.unpack(struct_data, "<H", pos)  pos = pos + 2
+    _, adc_struct.min_vol         = pack.unpack(struct_data, "<H", pos)  pos = pos + 2
+    _, adc_struct.x_offset        = pack.unpack(struct_data, "<H", pos)  pos = pos + 2
+    _, adc_struct.compress_type   = pack.unpack(struct_data, "b", pos)   pos = pos + 1
+    _, adc_struct.data_len        = pack.unpack(struct_data, "<H", pos)  pos = pos + 2
+
+    -- 提取真正的 500 点 ADC 数据
+    local adc_raw_data = string.sub(struct_data, pos, pos + ADC_DATA_LEN - 1)
+
+    log.info("uart1","触发位置:", adc_struct.x_offset, "ADC点数:", adc_struct.data_len)
+
+    -- 打印频率、电压最大值和电压最小值
+    -- log.info("uart1", "频率: " .. adc_struct.frequency .. " Hz")
+    -- log.info("uart1", "电压最大值: " .. adc_struct.max_vol .. " mV")
+    -- log.info("uart1", "电压最小值: " .. adc_struct.min_vol .. " mV")
+    
+    -- 发布事件,通知UI更新
+    sys.publish("ADC_DATA_UPDATE", {
+        frequency = adc_struct.frequency,
+        max_vol = adc_struct.max_vol,
+        min_vol = adc_struct.min_vol,
+        x_offset = adc_struct.x_offset
+    })
+
+    -- ===================== 500点波形转换 =====================
+    local wave_data = {}
+    local raw_wave  = {}
+    pos = 1
+
+    for i = 1, ADC_POINT_CNT do
+        local adc_val = 0
+        if pos <= #adc_raw_data then
+            _, adc_val = pack.unpack(adc_raw_data, "<H", pos)
+            pos = pos + 2
+        end
+
+        -- 电压计算(和你原来一样)
+        local V_adc = adc_val * 3.3 / 4095.0
+        -- if V_adc < 0.2 then V_adc = 0.2 end
+        -- local V_real = (V_adc - 0.2) * 10.0
+        local V_real = V_adc * 5.0
+        local display_val = V_real / _G.Y_FULL_SCALE * 100.0
+        display_val = math.floor(display_val + 0.5)
+        display_val = math.max(0, display_val)
+        raw_wave[i] = display_val
+    end
+
+    -- 偏移处理
+    for i = 1, ADC_POINT_CNT do
+        local val = raw_wave[i]
+        val = val + _G.y_offset
+        val = math.max(0, val)
+        table.insert(wave_data, val)
+    end
+
+    -- 发布 500 点波形
+    sys.publish("ADC_WAVE_DATA", wave_data)
+    --log.info("uart1","500点波形解析完成")
+end)
+
+-- ===================== 串口初始化(不变) =====================
+uart.setup(uartid, 921600,8,1,uart.NONE,uart.LSB)
+uart.on(uartid, "receive", uart_cb)
+
+local function uart_send_cb(id)
+    log.info("uart", id , "发送完成")
+end
+uart.on(uartid, "sent", uart_send_cb)
+
+local msg_id = 0
+
+-- ADC配置参数下发
+function uart_send.send_adc_config(config)
+    if not config then
+        log.error("uart1", "发送ADC配置失败:参数为空")
+        return
+    end
+    
+    log.info("uart1", "发送ADC配置数据:")
+    log.info("uart1", "垂直档位: " .. config.vertical_gear .. " mV")
+    log.info("uart1", "时间档位: " .. config.time_gear .. " us")
+    log.info("uart1", "水平偏移: " .. config.x_offset)
+    log.info("uart1", "垂直偏移: " .. config.y_offset)
+    log.info("uart1", "触发阈值: " .. config.trigger_threshold .. " mV")
+    log.info("uart1", "触发类型: " .. config.trigger_type)
+    log.info("uart1", "触发脉宽: " .. config.trigger_width .. " us")
+   
+    
+    local ADC_CONFIG_SIZE = 25
+    local MSG_LEN = ADC_CONFIG_SIZE + CHECK_FIELD_LEN
+    
+    local frame_header = pack.pack("<H", 0xFEFE)
+    local proto_ver = pack.pack("b", 0x03)
+    local msg_id_pack = pack.pack("<I", msg_id)
+    local first_type = pack.pack("b", 0x01)
+    local second_type = pack.pack("<H", 0x1001)
+    local msg_len_pack = pack.pack("<H", MSG_LEN)
+    
+    local device_type = pack.pack("<H", DEVICE_TYPE)
+    local device_sn = pack.pack("<I", 0x00000001)
+    local reserve = pack.pack("<I", 0)
+    local vertical_gear = pack.pack("<H", config.vertical_gear)
+    local time_gear = pack.pack("<I", config.time_gear)
+    local x_offset = pack.pack("b", config.x_offset)
+    local y_offset = pack.pack("b", config.y_offset)
+    local trigger_threshold = pack.pack("<H", config.trigger_threshold)
+    local trigger_type = pack.pack("b", config.trigger_type)
+    local trigger_width = pack.pack("<I", config.trigger_width)
+    
+    local frame_data = frame_header .. proto_ver .. msg_id_pack .. first_type .. second_type .. msg_len_pack ..
+                      device_type .. device_sn .. reserve ..
+                      vertical_gear .. time_gear .. x_offset .. y_offset ..
+                      trigger_threshold .. trigger_type .. trigger_width
+    
+    local crc = crypto.crc16("IBM", frame_data)
+    local crc_pack = pack.pack("<H", crc)
+    
+    local full_frame = frame_data .. crc_pack
+    local hex_str = string.toHex(full_frame, " ")
+    log.info("uart1", "完整帧 Hex: ", hex_str)
+    log.info("uart1", "完整帧长度: ", #full_frame)
+
+    uart.write(uartid, full_frame, #full_frame)
+    log.info("uart1", "发送ADC配置指令成功,消息ID:", msg_id)
+    
+    msg_id = (msg_id + 1) & 0xFFFFFFFF
+end
+
+
+-- ADC动态操作下发
+function uart_send.send_adc_operate(config)
+    if not config then
+        log.error("uart1", "发送ADC动态操作失败:参数为空")
+        return
+    end
+
+    log.info("uart1", "发送ADC动态操作数据:")  
+    log.info("uart1", "触发方式: " .. config.trigger_method)
+    log.info("uart1", "运行控制: " .. config.run_control)
+    log.info("uart1", "复位: " .. config.reset)
+    
+    local ADC_CONFIG_SIZE = 13
+    local MSG_LEN = ADC_CONFIG_SIZE + CHECK_FIELD_LEN
+    
+    local frame_header = pack.pack("<H", 0xFEFE)
+    local proto_ver = pack.pack("b", 0x03)
+    local msg_id_pack = pack.pack("<I", msg_id)
+    local first_type = pack.pack("b", 0x01)
+    local second_type = pack.pack("<H", 0x1002)
+    local msg_len_pack = pack.pack("<H", MSG_LEN)
+
+    local device_type = pack.pack("<H", DEVICE_TYPE)
+    local device_sn = pack.pack("<I", 0x00000001)
+    local reserve = pack.pack("<I", 0)
+    local reset = pack.pack("b", config.reset)
+    local run_control = pack.pack("b", config.run_control)
+    local trigger_method = pack.pack("b", config.trigger_method)
+    
+    local frame_data = frame_header .. proto_ver .. msg_id_pack .. first_type .. second_type .. msg_len_pack ..
+                      device_type .. device_sn .. reserve ..
+                      reset .. run_control .. trigger_method
+    
+    local crc = crypto.crc16("IBM", frame_data)
+    local crc_pack = pack.pack("<H", crc)
+    
+    local full_frame = frame_data .. crc_pack
+    local hex_str = string.toHex(full_frame, " ")
+    log.info("uart1", "完整帧 Hex: ", hex_str)
+    log.info("uart1", "完整帧长度: ", #full_frame)
+
+    uart.write(uartid, full_frame, #full_frame)
+    log.info("uart1", "发送ADC配置指令成功,消息ID:", msg_id)
+    
+    msg_id = (msg_id + 1) & 0xFFFFFFFF
+end
+
+-- DAC配置参数下发
+function uart_send.send_dac_config(config)
+    if not config then
+        log.error("uart1", "发送DAC配置失败:参数为空")
+        return
+    end
+
+    log.info("uart1", "发送DAC配置数据:")  
+    log.info("uart1", "运行控制: " .. config.run_control)
+    log.info("uart1", "波形类型: " .. config.wave_type)
+    log.info("uart1", "最大电压: " .. config.max_vol)
+    log.info("uart1", "最小电压: " .. config.min_vol)
+    log.info("uart1", "频率: " .. config.frequency)
+    log.info("uart1", "占空比: " .. config.duty_cycle)
+    
+    local DAC_CONFIG_SIZE = 21
+    local MSG_LEN = DAC_CONFIG_SIZE + CHECK_FIELD_LEN   
+    
+    local frame_header = pack.pack("<H", 0xFEFE)
+    local proto_ver = pack.pack("b", 0x03)
+    local msg_id_pack = pack.pack("<I", msg_id)
+    local first_type = pack.pack("b", 0x01)
+    local second_type = pack.pack("<H", 0x1003)
+    local msg_len_pack = pack.pack("<H", MSG_LEN)
+
+    local device_type = pack.pack("<H", DEVICE_TYPE)
+    local device_sn = pack.pack("<I", 0x00000001)
+    local reserve = pack.pack("<I", 0)
+    local run_control = pack.pack("b", config.run_control)
+    local wave_type = pack.pack("b", config.wave_type)
+    local max_vol = pack.pack("<H", config.max_vol)
+    local min_vol = pack.pack("<H", config.min_vol)
+    local frequency = pack.pack("<I", config.frequency)
+    local duty_cycle = pack.pack("<b", config.duty_cycle)
+    
+    local frame_data = frame_header .. proto_ver .. msg_id_pack .. first_type .. second_type .. msg_len_pack ..
+                      device_type .. device_sn .. reserve ..
+                      run_control .. wave_type .. max_vol .. min_vol .. frequency .. duty_cycle
+    
+    local crc = crypto.crc16("IBM", frame_data)
+    local crc_pack = pack.pack("<H", crc)
+    
+    local full_frame = frame_data .. crc_pack
+    local hex_str = string.toHex(full_frame, " ")
+    log.info("uart1", "完整帧 Hex: ", hex_str)
+    log.info("uart1", "完整帧长度: ", #full_frame)
+
+    uart.write(uartid, full_frame, #full_frame)
+    log.info("uart1", "发送DAC配置指令成功,消息ID:", msg_id)
+    
+    msg_id = (msg_id + 1) & 0xFFFFFFFF
+end
+
+-- 上电初始化发送函数(发送20个 55 AA 组合)
+local function send_init_data()
+    -- 构建20个 55 AA 的数据
+    local init_data = ""
+    for i = 1, 20 do
+        init_data = init_data .. "\x55\xAA"
+    end
+    
+    -- 发送数据
+    uart.write(uartid, init_data, #init_data)
+    log.info("uart1", "上电初始化数据发送完成,长度:", #init_data)
+    
+    -- 打印发送的数据(十六进制)
+    local hex_str = string.toHex(init_data, " ")
+    log.info("uart1", "初始化数据 Hex:", hex_str)
+end
+
+-- 一次性定时器:上电后1秒发送初始化数据
+sys.timerStart(send_init_data, 1000)
+
+return uart_send

+ 213 - 0
uart485_test.lua

@@ -0,0 +1,213 @@
+--[[
+@module  485_uart
+@summary 485串口功能模块
+@version 1.0
+@date    2025.09.23
+@author  lyw
+@usage
+本demo演示的核心功能为:
+1.开启串口,配置波特率等参数;
+2.设置接收回调函数
+3.定时向串口发送数据
+]]
+local uartid = 12        -- 根据实际设备选取不同的uartid
+local uart485Pin = 153    -- 用于控制485接收和发送的使能引脚(根据实际设备选取不同引脚)
+
+local rdbuf = ""      -- 全局接收缓存,用于拼接分包数据
+
+-- 协议格式
+local FRAME_HEAD = "\xFE\xFE"  -- 帧头(2字节)
+local FRAME_HEAD_LEN = 12      --- 帧头长度12字节
+local CHECK_FIELD_LEN = 2      -- CRC校验位占2字节
+
+
+local function head_paser(data)
+    local netHeader = {pro_ver=0,msg_id=0,msg_type1=0,msg_type2=0,msg_len=0}
+    local nextpos = 1
+
+    if #data <FRAME_HEAD_LEN then return false,nil end
+
+
+    nextpos, netHeader.head = pack.unpack(data, "<H", nextpos)      -- 校验帧头
+    if netHeader.head ~= 0xFEFE then
+        log.info("net","error","帧头错误,应为:FEFE,实际为:", string.format("0x%04X",netHeader.head))
+        return false,nil
+    end
+
+    nextpos, netHeader.pro_ver   = pack.unpack(data, "b", nextpos)    -- 获取协议版本
+    nextpos, netHeader.msg_id    = pack.unpack(data, "<I", nextpos)    -- 获取消息序列号
+    nextpos, netHeader.msg_type1 = pack.unpack(data, "b", nextpos)    -- 获取一级消息类型
+    nextpos, netHeader.msg_type2 = pack.unpack(data, "<H", nextpos)    -- 获取二级消息类型
+    nextpos, netHeader.msg_len   = pack.unpack(data, "<H", nextpos)    -- 校验帧长
+
+
+    -- log.info("head_paser", "解析帧头成功", 
+    --     "协议版本", netHeader.pro_ver,
+    --     "消息ID", netHeader.msg_id,
+    --     "数据长度N", netHeader.msg_len)
+
+    return true, netHeader
+end
+
+local function parse(data)
+    if not data or #data == 0 then
+        return false, data, nil
+    end
+
+    log.info("parse", "开始解析数据", data:toHex())
+    -- 步骤1:查找帧头FE FE的位置
+    local head_pos = string.find(data, FRAME_HEAD, 1, true)
+    if not head_pos then
+        log.warn("parse", "未找到帧头FE FE,缓存长度:", #data)
+        return false, data, nil  -- 无帧头,返回原缓存
+    end
+
+    -- 步骤2:清理帧头前的无效数据(比如乱码、残留字节)
+    local remain_buf = string.sub(data, head_pos)
+    log.info("parse", "找到帧头,清理无效数据后缓存长度:", #remain_buf)
+
+    -- 步骤3:判断是否够帧头长度(12字节),不够则返回等待后续数据
+    if #remain_buf < FRAME_HEAD_LEN then
+        log.warn("parse", "缓存仅"..#remain_buf.."字节,不足帧头12字节,等待分包")
+        return false, remain_buf, nil
+    end
+
+    -- 步骤4:解析帧头,获取数据长度N
+    local head_ok, netHeader = head_paser(remain_buf)
+    if not head_ok then
+        log.error("parse", "帧头解析失败,丢弃当前帧头,继续查找下一个")
+        -- 跳过当前错误帧头,从下一个字节继续解析(避免死循环)
+        return true, string.sub(remain_buf, 2), nil
+    end
+
+    -- 步骤5:计算完整帧总长度 = 帧头(12) + 数据长度(N)
+    local frame_total_len = FRAME_HEAD_LEN + netHeader.msg_len
+    log.info("parse", "计算完整帧长度:", frame_total_len, "(帧头12 + 数据"..netHeader.msg_len.." + CRC2)")
+
+    -- 步骤6:判断缓存是否够完整帧长度,不够则返回等待分包
+    if #remain_buf < frame_total_len then
+        log.warn("parse", "缓存仅"..#remain_buf.."字节,不足完整帧"..frame_total_len.."字节,等待分包")
+        return false, remain_buf, nil
+    end
+
+    -- 步骤7:提取完整帧,剩余数据留待下次解析
+    local full_frame = string.sub(remain_buf, 1, frame_total_len)  -- 完整帧
+    local unproc_buf = string.sub(remain_buf, frame_total_len + 1) -- 剩余未解析数据
+
+    log.info("parse", "提取完整帧,长度:", #full_frame, "十六进制:", full_frame:toHex())
+    return true, unproc_buf, full_frame
+end
+
+local function proc(data)
+    if not data or #data == 0 then return end
+    
+    -- 步骤1:追加新数据到全局缓存
+    rdbuf = rdbuf .. data
+    log.info("proc", "全局缓存总长度:", #rdbuf)
+
+    local result, unproc_buf, full_frame
+    unproc_buf = rdbuf
+
+    -- 步骤2:循环解析所有完整帧(处理黏包)
+    while true do
+        -- 调用parse解析,返回:是否继续、剩余缓存、完整帧
+        result, unproc_buf, full_frame = parse(unproc_buf)
+        
+        -- 解析出完整帧则发布事件
+        if full_frame then
+            sys.publish("UART_485_RECIVE", full_frame)
+        end
+
+        -- 终止条件:无剩余数据/解析失败/无完整帧
+        if not result or not unproc_buf or unproc_buf == "" then
+            break
+        end
+    end
+
+    -- 步骤3:更新全局缓存为未解析的剩余数据
+    rdbuf = unproc_buf or ""
+    log.info("proc", "解析完成,剩余缓存长度:", #rdbuf)
+end
+
+
+local function uart_cb(id, len)
+    local data = ""
+    repeat
+        data = uart.read(id, 1024)  -- 一次读取最多1024字节
+        if not data or #data == 0 then break end
+        
+        -- 打印十六进制(二进制数据必须看十六进制,字符打印会乱码)
+        log.info("uart_cb", "接收数据长度:", #data)
+        log.info("uart_cb", "接收数据,长度:", #data, "十六进制:", data:toHex())
+        
+        -- 处理数据(拼接+解析)
+        proc(data)
+    until data == ""
+end
+
+
+sys.subscribe("UART_485_RECIVE", function(full_frame)
+    if not full_frame or #full_frame == 0 then return end
+
+    -- 解析帧头(再次确认,可选)
+    local head_ok, netHeader = head_paser(full_frame)
+    if not head_ok then
+        log.error("业务解析", "帧头解析失败")
+        return
+    end
+
+    local data_part = string.sub(full_frame, FRAME_HEAD_LEN + 1, #full_frame - CHECK_FIELD_LEN)
+    
+    local crc_data_part = string.sub(full_frame, 1, #full_frame - CHECK_FIELD_LEN)
+
+    local crc_str = string.sub(full_frame, #full_frame - CHECK_FIELD_LEN + 1, #full_frame)
+
+    local _, recv_crc = pack.unpack(crc_str, "<H", 1)  -- 第一个返回值是nextpos,无用;第二个是CRC数值
+
+    local calc_crc = crypto.crc16("IBM", crc_data_part)
+
+    log.info("业务解析", 
+        "接收CRC数值:", recv_crc, "十六进制:", string.format("0x%04X", recv_crc),
+        "计算CRC数值:", calc_crc, "十六进制:", string.format("0x%04X", calc_crc))
+
+    if recv_crc ~= calc_crc then
+        log.error("业务解析", "CRC校验失败!")
+        return  -- 校验失败,放弃处理该帧
+    end
+    log.info("业务解析", "CRC校验通过")
+
+    -- 7. 校验通过后处理业务逻辑(示例:根据消息类型分支处理)
+    log.info("业务解析完成", 
+        "一级消息类型:", netHeader.msg_type1,
+        "二级消息类型:", netHeader.msg_type2,
+        "消息ID:", netHeader.msg_id)
+    
+    -- 示例:根据不同消息类型处理数据
+    if netHeader.msg_type1 == 0x02 and netHeader.msg_type2 == 0x2002 then
+        log.info("业务解析", "处理控制类指令,数据:", data_part:toHex())
+        sys.publish("UART_485_RECIVE_CONTROL", data_part)
+    -- elseif netHeader.msg_type1 == 0x02 then
+    --     log.info("业务解析", "处理数据上报指令,数据:", data_part:toHex())
+
+    end
+end)
+
+local function uart_send()
+    -- 循环两秒向串口发一次数据
+    while true do
+        sys.wait(2000)
+        uart.write(uartid, "test data aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.")
+    end
+end
+
+--初始化
+--gpio.setup(3,0)
+uart.setup(uartid, 115200, 8, 1, uart.NONE, uart.LSB, 1024, uart485Pin, 0, 20000)
+
+-- 收取数据会触发回调, 这里的"receive" 是固定值
+uart.on(uartid, "receive", uart_cb)
+
+-- 发送数据完成会触发回调, 这里的"sent" 是固定值
+--uart.on(uartid, "sent", uart_send_cb)
+
+sys.taskInit(uart_send)

+ 210 - 0
wifi_sta.lua

@@ -0,0 +1,210 @@
+--[[
+@module  wifi_sta
+@summary wifi_sta模块 
+@version 1.0
+@date    2025.10.20
+@author  魏健强
+@usage 本文为wifi_sta功能模块,核心逻辑为
+1、模块连接wifi;
+2、连接MQTT服务器并发送消息;
+本文件没有对外接口,直接在其他功能模块中require "wifi_sta"就可以加载运行;
+]] 
+
+-- 必须加载sysplus库以支持MQTT
+_G.sysplus = require("sysplus")
+
+-- 测试环境MQTT配置
+local dflt_svr_cfg = {
+    ["svr_ip"] = "test-mqtt.cpyypt.cn",
+    ["svr_port"] = 9000,
+    ["svr_usr_name"] = "admin",
+    ["svr_usr_pwd"] = "houjianwei"
+}
+
+-- MQTT客户端实例
+local mqttc = nil
+local pub_topic = "cpyypt/up/test"
+local sub_topic = {["cpyypt/down/test"] = 0}
+
+-- 从fskv读取配置,如果不存在则使用默认配置
+local function load_svr_cfg()
+    local cfg = fskv.get("svr_cfg")
+    if not cfg then
+        cfg = dflt_svr_cfg
+        fskv.set("svr_cfg", cfg)
+        log.info("wifi_sta", "使用默认MQTT配置")
+    else
+        log.info("wifi_sta", "从fskv读取MQTT配置")
+    end
+    return cfg
+end
+
+-- wifi的STA相关事件
+sys.subscribe("WLAN_STA_INC", function(evt, data)
+    -- evt 可能的值有: "CONNECTED", "DISCONNECTED"
+    -- 当evt=CONNECTED, data是连接的AP的ssid, 字符串类型
+    -- 当evt=DISCONNECTED, data断开的原因, 整数类型
+    log.info("收到STA事件", evt, data)
+end)
+
+-- MQTT消息处理函数
+local function mqtt_event_handler(mqtt_client, event, data, payload)
+    log.info("mqtt", "event", event, data, payload)
+    if event == "conack" then
+        -- MQTT连接成功
+        log.info("mqtt", "连接成功")
+        sys.publish("mqtt_conack")
+        -- 订阅主题
+        mqtt_client:subscribe(sub_topic)
+        -- 发送测试消息
+        local test_msg = json.encode({
+            type = "test",
+            timestamp = os.time(),
+            message = "WiFi连接成功,MQTT测试消息"
+        })
+        mqtt_client:publish(pub_topic, test_msg, 1)
+        log.info("mqtt", "发送测试消息", test_msg)
+    elseif event == "recv" then
+        -- 收到下行消息
+        log.info("mqtt", "收到消息", "topic", data, "payload", payload)
+        sys.publish("mqtt_payload", data, payload)
+    elseif event == "sent" then
+        log.info("mqtt", "消息发送成功", "pkgid", data)
+    elseif event == "disconnect" then
+        log.info("mqtt", "断开连接")
+    end
+end
+
+-- 连接MQTT服务器
+local function connect_mqtt(svr_cfg)
+    log.info("mqtt", "准备连接", svr_cfg["svr_ip"], svr_cfg["svr_port"])
+    
+    -- 等待WiFi网卡就绪
+    local is_ready, index = socket.adapter(socket.LWIP_STA)
+    if not is_ready then
+        log.info("mqtt", "WiFi网卡未就绪,等待...")
+        while not socket.adapter(socket.LWIP_STA) do
+            sys.wait(500)
+        end
+    end
+    log.info("mqtt", "WiFi网卡已就绪")
+    
+    -- 创建MQTT客户端
+    mqttc = mqtt.create(nil, svr_cfg["svr_ip"], svr_cfg["svr_port"], false)
+    
+    -- 设置认证信息
+    mqttc:auth("wifi_test_client", svr_cfg["svr_usr_name"], svr_cfg["svr_usr_pwd"])
+    
+    -- 启用自动重连
+    mqttc:autoreconn(true, 3000)
+    
+    -- 设置事件回调
+    mqttc:on(mqtt_event_handler)
+    
+    -- 连接服务器
+    mqttc:connect()
+    
+    -- 等待连接成功
+    local success = sys.waitUntil("mqtt_conack", 10000)
+    if success then
+        log.info("mqtt", "连接成功")
+        return true
+    else
+        log.info("mqtt", "连接超时")
+        return false
+    end
+end
+
+function test_sta()
+    log.info("执行STA连接操作")
+    
+    -- 连接WiFi
+    wlan.connect("Xiaomi_0D49", "Wbjw2025")
+    
+    -- 等待wifi_sta网络连接成功
+    while not socket.adapter(socket.LWIP_STA) do
+        sys.waitUntil("IP_READY", 1000)
+    end
+    
+    log.info("wifi", "连接成功", json.encode(wlan.getInfo()))
+    
+    -- 查看当前默认网卡
+    local current_dft, last_id = socket.dft()
+    log.info("socket", "当前默认网卡:", current_dft, "最后注册网卡:", last_id)
+    log.info("socket", "LWIP_GP(4G)=", socket.LWIP_GP, "LWIP_STA(WiFi)=", socket.LWIP_STA)
+    
+    -- 设置默认网卡为WiFi
+    -- socket.dft(socket.LWIP_STA)
+    -- log.info("socket", "已设置默认网卡为WiFi")
+    
+    -- 再次查看确认
+    current_dft = socket.dft()
+    log.info("socket", "设置后默认网卡:", current_dft)
+    
+    -- 禁用4G(进入飞行模式),确保socket只能使用WiFi
+    -- local result = mobile.flymode(0, true)
+    -- log.info("wifi", "4G进入飞行模式,确保使用WiFi连接")
+    
+    -- 获取MQTT配置
+    local svr_cfg = load_svr_cfg()
+    log.info("mqtt", "配置", json.encode(svr_cfg))
+    
+    -- 同步时间(MQTT需要正确的时间)
+    -- 指定使用WiFi网卡(socket.LWIP_STA)进行NTP同步
+    log.info("sntp", "开始时间同步,使用WiFi网卡")
+    socket.sntp(nil, socket.LWIP_STA)
+    
+    -- 等待NTP同步完成(最多等待10秒)
+    local ntp_ok = sys.waitUntil("NTP_UPDATE", 10000)
+    if ntp_ok then
+        log.info("sntp", "时间同步成功", os.date())
+    else
+        log.info("sntp", "时间同步失败或超时")
+        -- 可以选择继续或退出
+    end
+    
+    -- 连接MQTT服务器
+    local mqtt_ok = connect_mqtt(svr_cfg)
+    if not mqtt_ok then
+        log.info("mqtt", "连接失败,重试")
+        return
+    end
+    
+    -- 主循环:定时发送心跳消息
+    while true do
+        local heartbeat_msg = json.encode({
+            type = "heartbeat",
+            timestamp = os.time(),
+            status = "online"
+        })
+        if mqttc then
+            mqttc:publish(pub_topic, heartbeat_msg, 1)
+            log.info("mqtt", "发送心跳", heartbeat_msg)
+        end
+        sys.wait(2000)  -- 每2秒发送一次心跳
+    end
+end
+
+function ip_ready_handle(ip, adapter)
+    log.info("ip_ready_handle", ip, adapter)
+    if adapter == socket.LWIP_STA then
+        log.info("wifi sta 链接成功")
+    end
+end
+
+sys.taskInit(test_sta)
+sys.subscribe("IP_READY", ip_ready_handle)
+
+-- 对外接口:发送MQTT消息
+function send_mqtt_message(topic, data, qos)
+    if mqttc then
+        return mqttc:publish(topic or pub_topic, data, qos or 1)
+    else
+        log.info("mqtt", "客户端未连接")
+        return nil
+    end
+end
+
+return {
+    send_mqtt_message = send_mqtt_message
+}