跳转至

ymodem - ymodem协议

作者:李源龙

一、概述

Ymodem协议是一种在串行通信(如串口)中使用的​​文件传输协议​​,它由Chuck Forsberg在Xmodem协议的基础上改进而来,核心特点是​​支持批量文件传输​​和​​每帧最多1024字节的数据块​​,相比前身Xmodem的128字节帧,传输效率显著提升。

该协议通过​​CRC-16循环冗余校验​​来检测传输错误,并利用​​ACK(确认)/NAK(否认)握手信号​​实现自动重传机制,从而保证数据传输的可靠性。

Ymodem协议在传输开始时会将​​文件名和文件大小作为元数据​​在起始帧中发送,使得接收方能够预先知晓文件信息,因此尤其适用于嵌入式系统中的​​固件升级(OTA)和Bootloader引导程序​​等场景

二、核心示例

1、核心示例是指:使用本库文件提供的核心API,开发的基础业务逻辑的演示代码;

2、核心示例的作用是:帮助开发者快速理解如何使用本库,所以核心示例的逻辑都比较简单;

3、更加完整和详细的demo,请参考 LuatOS仓库 中各个产品目录下的demo/ymodem

-- 根据实际设备选取不同的uartid
local uartid = 1 

--初始化
local result = uart.setup(
    uartid,--串口id
    115200,--波特率
    8,--数据位
    1--停止位
)
local taskName = "ymodem_run"

-- 处理未识别的消息
local function ymodem_run_cb(msg)
    log.info("ymodem_run_cb", msg[1], msg[2], msg[3], msg[4])
end

--  定义一个局部变量,用于表示Ymodem协议是否正在运行
local ymodem_running = false 

--  创建一个缓冲区,大小为1024 + 32
local rxbuff = zbuff.create(1024 + 32) 

--  创建一个ymodem处理程序,保存路径为"/",文件名为"save.bin"
local ymodem_handler = ymodem.create("/","save.bin")

-- --  定义一个ymodem_run函数,用于发送C字符,并重置ymodem处理程序
local function ymodem_run() 
    while true do
        --  如果ymodem协议没有在运行,则发送请求;并重置ymodem处理程序
        if not ymodem_running then 
            --YMODEM 传输文件时,接收方会先发送一个字符 'C'来启动传输过程
            uart.write(uartid, "C")
            --发送完之后重置恢复到初始状态
            ymodem.reset(ymodem_handler) 
        end
        sys.wait(500)
    end
end

--  定义一个ymodem_rx函数,用于接收数据
local function ymodem_rx(id,len) 
    --  从uart接收数据到缓冲区
    while 1 do
        log.info("uart", "缓冲区", uart.rxSize(id)) -- 缓冲区中的数据数量
        local len = uart.rx(id, rxbuff)
        if len <= 0 then
            break
        end
        log.info("uart", "receive", id, rxbuff:used(), rxbuff:toStr())
    end
    --  打印缓冲区已使用的大小
    log.info(rxbuff:used()) 
    --  调用ymodem.receive函数,接收数据
    local result,ack,flag,file_done,all_done = ymodem.receive(ymodem_handler,rxbuff) 
    ymodem_running = result
    log.info("result:",ymodem_running,ack,flag,file_done,all_done)
    rxbuff:del()
    --成功就发送ack和flag
    if result then
        rxbuff:copy(0, ack,flag)
        uart.tx(id, rxbuff)
    end

    --  所有数据都接收完毕
    if all_done then 
        -- 判断/save.bin文件是否存在
        local exists=io.exists("/save.bin") 

        -- 判断/save.bin文件是否存在,存在则打印日志,显示/save.bin文件大小;不存在则打印日志,显示/save.bin文件不存在
        if exists then
            log.info("io", "save.bin file exists:", exists) 
            --打印文件大小
            log.info("io", "save.bin file size:", io.fileSize("/save.bin")) 
        else
            log.info("io", "save.bin file not exists") 
        end

        --ymodem_running置为false,再次开始接收
        ymodem_running = false 
    end
    rxbuff:del()
end

--  监听串口接收事件
uart.on(uartid, "receive", ymodem_rx) 

local function uart_sent_cb(id)
    log.info("uart", "sent", id) 
end
--  监听串口发送事件
uart.on(uartid, "sent", uart_sent_cb)

--创建并且启动一个task
--运行这个task的主函数ymodem_run
sys.taskInit(ymodem_run, taskName,ymodem_run_cb)

三、常量详解

核心库常量,顾名思义是由合宙LuatOS内核固件中定义的、不可重新赋值或修改的固定值,在脚本代码中不需要声明,可直接调用;

ymodem核心库没有常量。

四、函数详解

ymodem.create(dir_path,file_path)

功能

创建一个ymodem处理句柄

注意事项

暂无

参数

dir_path

含义说明:保存的文件夹路径,默认是"/"路径;
数据类型:string
取值范围:无特别限制;
是否必选:否;
注意事项:默认是"/"路径;
参数示例:ymodem.create("/")

file_path

含义说明:强制保存的文件名,默认是空,如果设置了,就会直接保存在该文件中;
数据类型:string
取值范围:无特别限制;
是否必选:否;
注意事项:如果不填该参数,文件名默认对端发过来的文件名;
参数示例:ymodem.create("/","save.bin")

返回值

local ymodem_handler = ymodem.create("/","save.bin")

ymodem_handler

含义说明:ymodem创建成功返回的对象
数据类型:userdata
取值范围:无特别限制;
注意事项:无;

例子

local ymodem_handler = ymodem.create("/","save.bin")

ymodem.receive(handler, data)

功能

ymodem接收文件数据并保存

注意事项

暂无

参数

handler

含义说明:ymodem创建成功的对象
数据类型:userdata
取值范围:无特别限制;
是否必选:必须传入此参数;
注意事项:作为必需参数,若不提供会导致错误;
参数示例:local handler = ymodem.create("/","save.bin")
        ymodem.receive(handler, data)

data

含义说明:串口接收的数据;
数据类型:string/zbuff
取值范围:无特别限制;
是否必选:必须传入此参数;
注意事项:作为必需参数,若不提供会导致错误;
参数示例:local handler = ymodem.create("/","save.bin")
        ymodem.receive(handler, data)

返回值

local result,ack,flag,file_done,all_done = ymodem.receive(handler, data)

result

含义说明:保存成功/失败;
数据类型:boolean
取值范围:true/false
注意事项:无;

ack

含义说明:ack值,需要通过串口/网络等途径返回发送方;
数据类型:number
取值范围:0-255
注意事项:无;

flag

含义说明:flag值,需要通过串口/网络等途径返回发送方,如果有ack值则不发送flag
数据类型:number
取值范围:0-255
注意事项:无;

file_done

含义说明:文件是否接收完成;
数据类型:boolean
取值范围:true/false 接收完成true,传输中false
注意事项:无;

all_done

含义说明:整个传输是否完成;
数据类型:boolean
取值范围:true/false 传输完成true,未完成false
注意事项:无;

例子

-- 注意, 数据来源不限, 通常是uart.read得到data
local handler = ymodem.create("/","save.bin")
local result,ack,flag,file_done,all_done = ymodem.receive(handler, data)

ymodem.reset(handler)

功能

重置ymodem处理过程

注意事项

这个操作主要是把状态复位,之前传输过来的数据不会清空,下次传输不需要重新create。

参数 handler

含义说明:ymodem创建成功的对象
数据类型:userdata
取值范围:无特别限制;
是否必选:必须传入此参数;
注意事项:作为必需参数,若不提供会导致错误;
参数示例:local handler = ymodem.create("/","save.bin")
        ymodem.reset(handler)

返回值

例子

-- 恢复到初始状态,一般用于接收出错后重置,从而进行下一次接收
local handler = ymodem.create("/","save.bin")
ymodem.reset(handler)

ymodem.release(handler)

功能

释放ymodem处理句柄

注意事项

暂无

参数 handler

含义说明:ymodem创建成功的对象
数据类型:userdata
取值范围:无特别限制;
是否必选:必须传入此参数;
注意事项:作为必需参数,若不提供会导致错误;
参数示例:local handler = ymodem.create("/","save.bin")
        ymodem.release(handler)

返回值

例子

ymodem.release(handler)

五、函数详解

支持 LuatOS 开发的8000系列和780EHM、780EGH、780EHV的产品都支持 ymodem 核心库。