VisualHMI - OTA升级

1.概述

在产品全生命周期维护过程中,固件及人机界面(HMI)工程的远程升级与迭代是保障系统持续可用性与功能演进的关键环节。

本方案中,HMI 屏幕提供标准化的本地升级 API 接口,其核心特性在于: 升级逻辑与通信协议及数据来源完全解耦

  • 无论升级数据来自主板串口、以太网、Wi-Fi 还是 4G 网络
  • 无论上位机采用自定义协议、Modbus、HTTP 或其他传输方式

HMI 仅需通过标准 API 将接收到的升级数据写入指定 Flash 区域,校验解压成功后,即可触发后续升级流程。 该设计使升级功能不依赖特定通信链路或协议栈,极大提升了系统在多场景部署下的灵活性与可维护性,同时便于通过 Lua 脚本实现差异化升级策略。

使用范围:VisualHMI - HMI&M系列&Dx系列

应用下载:VisualHMI - OTA升级(点击下载)

1.1 OTA 升级 Flash 使用规范

以 HMI80480M070 为例

  1. Flash 总容量

    • 标准品 Flash 大小:128 Mbit = 16 MB(字节)。
  2. 系统固件占用

    • M 系列系统文件(底层 OS + 驱动)占用:1 MB(DH 系列为 2 MB,本例为 M 系列,按 1 MB 计)。
  3. 当前工程文件大小

    • 下载文件 private 大小:4.38 MB
  4. 已用 Flash 空间总计

    • 已用 = 系统(1 MB) + 工程(4.38 MB) = 5.38 MB
    • 剩余可用空间 = 16 MB − 5.38 MB = ≈10.62 MB
  5. OTA 升级起始地址要求

    • 必须从 Flash 的后半部分开始,即使剩余可用空间 =10.62MB,也要从≥ 8 MB 地址处开始(16 MB ÷ 2 = 8 MB)。
    • 目的:避免新旧工程在 Flash 中交叉覆盖,确保升级过程中当前系统仍可正常运行。
  6. OTA 区域需连续且空闲

    • OTA 写入区域必须是连续的未使用地址段
    • 若用户已通过“块地址”功能占用了部分高地址区(如 10~12 MB)
  7. OTA 文件大小限制

    • OTA 升级包(新工程)大小 Flash 一半,某升级包大小为 ≈10.62 MB,不可升级,;
    • OTA 升级包(新工程)大小=5MB,块地址占用8M~12M, 不可升级

2.Lua API说明

2.1.ota_init(md5, filesize, addr)

OTA 升级初始化接口,ota_init(md5, filesize, addr) 是 HMI 或嵌入式系统提供的 OTA升级初始化函数,用于配置本次升级的目标参数,包括固件大小、写入地址。调用成功后,系统将准备接收新固件数据并写入指定 Flash 区域,为后续的 ota_write()ota_finish() 流程奠定基础。

📊 参数说明

参数 类型 说明
md5 string MD5 校验字符串(固定值) "0123456789abcdef"
filesize number 待升级固件文件大小(字节)
• 单位:Byte
• 必须与实际传输的 .bin 文件大小严格一致
• 系统据此预分配空间并验证最终写入长度
addr number OTA 固件写入起始地址(Flash 物理地址)
• 必须 ≥ Flash 总容量的一半(例如:2MB Flash → addr ≥ 0x100000

2.2.ota_write(writeTb)

OTA 固件数据写入接口, ota_write(writeTb) 是 HMI 或嵌入式系统提供的 OTA升级数据写入函数,用于将分块的固件数据写入预先通过 ota_init() 配置的 Flash 地址区域

📊 参数说明

参数 类型 说明
writeTb table 待写入的字节数据块
• Lua 表形式,下标从 1 开始
• 有效数据长度 ≤ 2048 字节 • 若 < 2048,系统自动在末尾补 0x00 至 2048 字节

2.3.ota_check_upgrade(state)

OTA 升级校验与解压执行接口,ta_check_upgrade(state) 是 HMI 或嵌入式系统提供的 OTA 升级最终确认与执行函数。在用户通过 ota_init()ota_write() 完整传输新固件(ota.bin)后,调用此函数将触发系统对已写入 Flash 的固件数据进行完整性校验、解压缩,并完成升级流程

📊 参数说明

参数 类型 说明
state number 升级控制状态码
• 必须传入 1 , 表示“开始校验并执行升级”

2.4.ota_destory()

清除 OTA 升级残留数据,ota_destroy() 是 HMI 或嵌入式系统提供的 OTA升级清理接口,用于擦除已写入 Flash 的 OTA 固件数据

2.5.on_ota_progress(status, value)

OTA 升级过程状态回调函数,on_ota_progress(status, value) 是 HMI 系统在执行 ota_check_upgrade(1)自动触发的全局回调函数,用于向应用层实时反馈 OTA 固件的校验与解压进度及结果。开发者可通过实现此函数,在 UI 上显示进度条、提示信息或处理升级失败逻辑

📊 参数说明

status 含义 value 说明
1 校验过程开始 固定为 0,表示校验阶段已启动
2 校验结果 0:校验失败(固件损坏、MD5/CRC 不匹配等)
1:校验成功
3 解压过程 解压进度百分比
范围 0 ~ 100(如 value=50 表示解压完成 50%)
4 解压结果 0:解压失败(数据格式错误、空间不足等)
1:解压成功 → 即将重启

3.应用

本文的应用是以SD卡进行OTA升级,作为OTA功能演示,仅做参考使用。

3.1.ota文件生成

工程在编译后,选择量产下载,生成的文件中有用于OTA升级的ota.bin文件,本例程使用时需要把 打包生成的ota.bin文件改名为a.bin,如下所示:

3.2.Lua脚本

SD卡插入回调、U盘插入回调检测屏是否有SD卡、U盘插入,回调检测到插入,记录盘符,点击按键对地址LW1001,检索盘符是否有a.bin文件,有则进行OTA升级,没有则进行提示,代码如下所示:

--main.lua

_ENCRYPT_=0    --LUA脚本加密

--数据类型定义
VT_LW = 1    --变量地址
VT_RW = 2    --FLASH存储

useraddr = {
    progress = 0x1000,  --进度条
    ota_swich = 0x1001, --OTA开启升级按键
    ota_data = 0x1002,  --OTA升级包大小
    sd = 0x1005,        --SD指示灯
    usb = 0x1006,       --USB指示灯
    text = 0x1020,      --提示文本
}

sd_dir = 0
usb_dir = 0

function on_init()
    dofile('ota.lua')
end

function on_run(screen)
end

function on_update(slave,vtype,addr)

    if vtype == VT_LW
    then
        if addr == useraddr.ota_swich
        then
            if sd_dir ~= 0
            then
                list_dir(sd_dir)
            elseif usb_dir ~= 0
            then
                list_dir(usb_dir)
            else
                set_string(VT_LW, useraddr.text, '检测不到SD卡和USB,无法OTA升级!!!')
            end
        end
    end
end


function on_list_dir(path,filename,type,fsize)

    if type == 1
    then
        if filename == ota_filename
        then
            set_string(VT_LW, useraddr.text, '文件:'..ota_filename..' 已检测到')
            print('56-->filename = '..filename)
            if (fsize + ota_addr_start) > flashsize
            then
                set_string(VT_LW, useraddr.text, 'ota.bin 文件大小超过,无法升级!!!')
            else
                set_string(VT_LW, useraddr.text, '开始升级!!!')
                set_uint32(VT_LW, useraddr.ota_data, fsize)
                ota.set_upgrade(path..'/'..ota_filename)
            end
        end
    end
end

function on_usb_inserted(driver)
    usb_dir = driver
    set_uint16(VT_LW, useraddr.usb, 1)
end

function on_usb_removed()
    usb_dir = 0
    set_uint16(VT_LW, useraddr.usb, 0)
end

function on_sd_inserted(dir)
    sd_dir = dir
    set_uint16(VT_LW, useraddr.sd, 1)
end

function on_sd_removed()
    sd_dir = 0
    set_uint16(VT_LW, useraddr.sd, 0)
end

OTA升级,先进行OTA初始化,在通过读取文件,将内容写入OTA地址,写入结束后进行校验、解压,完成后会自动重启升级。OTA升级部分代码如下所示:

--ota.lua

ota = {}

ota_filename   = 'a.bin'
ota_addr_start = 8*1024*1024   --OTA升级写入起始地址(至少从flashsize一半开始)
flashsize      = 16*1024*1024   --FLASH总大小


function ota.set_upgrade(path)   -- OTA升级

    local TransferSize = 0
    local size = 0

    if file_open(path, 0) == true 
    then
        size = file_size()  --获取ota.bin文件大小
        print(path..'--> size = '..size)

        if size <  (flashsize - ota_addr_start)
        then
            ota_destroy()   --消除OTA数据

            local sdota_addr = ota_init('0123456789abcdef', size, ota_addr_start)     --OTA初始化,返回值为0
            print('> sdota_addr = '..sdota_addr)

            local size_complement = 0   --计算尾包数据长度
            if size % 2048 ~= 0
            then
                size_complement = 2048 - (size % 2048)
            end

            while(true)
            do
                local rddata = file_read(2048)

                print('> #rddata = '..(#(rddata)))

                if #(rddata) < 2048
                then
                    for i = (#(rddata) + 1), 2048
                    do
                        rddata[i] = 0x00
                    end
                end

                TransferSize = #(rddata) + TransferSize     --已读取a.bin文件长度
                local prg = string.format ('%.1f', ((TransferSize * 100) / (size + size_complement)))    --计算百分比
                set_uint16(VT_LW, useraddr.progress, math.modf((TransferSize * 1000) / size))   --取整数,写入进度条
                set_string(VT_LW, useraddr.text, '下载进度 : '..prg..' %   [ '..math.ceil(TransferSize)..' byte / '..math.ceil(size)..' byte ]')
                refresh_screen()

                ota_write(rddata)   --OTA写入,写入大小为2048byte,不足补零

                if size <= TransferSize then break end
            end

            file_close()
            ota_check_upgrade(1)    --OTA校验、解压
        end
    end
end


function on_ota_progress(status,value)  --OTA校验、解压回调

    if status == 1 and value == 0  --校验过程,固定为0
    then
        set_string(VT_LW, useraddr.text, '校验开始')

    elseif status == 2      --校验结果:0:失败;1:成功
    then
        if value == 0
        then
            set_string(VT_LW, useraddr.text, '校验失败')
        elseif value == 1
        then
            set_string(VT_LW, useraddr.text, '校验成功')
        end

    elseif status == 3      --解压进度:0~100
    then
        set_uint16(VT_LW, useraddr.progress, value*10)
        set_string(VT_LW, useraddr.text, '解压进度 : '..value..' %')
        refresh_screen()

    elseif status == 4      --0:解压失败;1:解压成功
    then
        if value == 0
        then
            set_string(VT_LW, useraddr.text, '解压失败')
        elseif value == 1
        then
            set_string(VT_LW, useraddr.text, '解压成功')
        end
    end
end
Copyright ©Dacai all right reserved,powered by Gitbook该文件修订时间: 2026-02-05 15:35:32

results matching ""

    No results matching ""