--[[ @module xmodem @summary xmodem 协议 @version 1.0 @date 2025.10.17 @author Dozingfiretruck @usage --加载xmodem模块 xmodem=require ("xmodem") --设置默认filepath为脚本区的send.bin文件 local filepath="/luadb/send.bin" local taskName = "xmodem_run" local uart_id = 1 --串口号 local baudrate = 115200 --波特率 local file_path=filepath --文件路径 local send_type=true --true表示单次发送128字节,false表示单次发送1024字节 local inform_data="wait C" --发送前提示信息,告知对方要发送C字符来接收文件 -- 处理未识别的消息 local function xmodem_run_cb(msg) log.info("xmodem_run_cb", msg[1], msg[2], msg[3], msg[4]) end --http获取文件函数 local function http_recived_cb() while not socket.adapter(socket.dft()) do log.warn("httpplus_app_task_func", "wait IP_READY", socket.dft()) -- 在此处阻塞等待默认网卡连接成功的消息"IP_READY" -- 或者等待1秒超时退出阻塞等待状态; -- 注意:此处的1000毫秒超时不要修改的更长; -- 因为当使用exnetif.set_priority_order配置多个网卡连接外网的优先级时,会隐式的修改默认使用的网卡 -- 当exnetif.set_priority_order的调用时序和此处的socket.adapter(socket.dft())判断时序有可能不匹配 -- 此处的1秒,能够保证,即使时序不匹配,也能1秒钟退出阻塞状态,再去判断socket.adapter(socket.dft()) sys.waitUntil("IP_READY", 1000) end local path = "/send.bin" -- 以下链接仅用于测试,禁止用于生产环境 local code, headers, body_size = http.request("GET", "http://airtest.openluat.com:2900/download/send.bin", nil, nil, {dst=path}).wait() log.info("http", code==200 and "success" or "error", code) if code==200 then log.info("HTTP receive ok",body_size) file = io.open(path, "rb") if file then content = file:read("*a") log.info("文件读取", "路径:" .. path, "内容:" .. content) file:close() else log.error("文件操作", "无法打开文件读取内容", "路径:" .. path) end file_path=path end end -- 定义一个xmodem_run函数,用于用xmodem发送文件 local function xmodem_run() --如果需要http下载文件,然后发送下载的文件,可以打开下面的http_recived_cb()函数 -- http_recived_cb() --启动xmodem发送 local result=xmodem.send(uart_id,baudrate,file_path,send_type,inform_data) --等待时间12秒,等待接收方发送C字符启动发送,发送结束后接收端发送ACK:0x06表示接收完成,文件全部传输完成之后模块发送EOT​:0x04表示传输结束,接收端返回0x06表示确认结束 log.info("Xmodem", "start") log.info("Xmodem", "send result", result) --判断是否传输成功,传输是否成功,都需要关闭xmodem if result then log.info("Xmodem", "send success") xmodem.close(uart_id) else log.info("Xmodem", "send failed") xmodem.close(uart_id) end end --创建并且启动一个task --运行这个task的主函数xmodem_run sys.taskInit(xmodem_run, taskName,xmodem_run_cb) ]] local xmodem = {} local sys = require "sys" local HEAD local DATA_SIZE local SOH = 0x01 -- Modem数据头 128 local STX = 0x02 -- Modem数据头 1K local EOT = 0x04 -- 发送结束 local ACK = 0x06 -- 应答 local NAK = 0x15 -- 非应答 local CAN = 0x18 -- 取消发送 local CTRLZ = 0x1A -- 填充 local CRC_CHR = 0x43 -- C: ASCII字符C local CRC_SIZE = 2 local FRAME_ID_SIZE = 2 local DATA_SIZE_SOH = 128 local DATA_SIZE_STX = 1024 local function uart_cb(id, len) local data = uart.read(id, 1024) if #data == 0 then return end log.info("xmodem", "uart读取到数据:", data:toHex()) data = data:byte(1) sys.publish("xmodem", data) end --[[ xmodem 发送文件 @api xmodem.send(uart_id,baudrate,type,inform_data) @number uart_id uart端口号 @number uart_br uart波特率 @string file_path 文件路径 @bool type 1k/128 默认1k @return bool 发送结果 @usage xmodem.send(1, 115200, "/luadb/send.bin",true) ]] function xmodem.send(uart_id,baudrate,file_path,type,inform_data) local ret, flen, cnt, crc if type then HEAD = SOH DATA_SIZE = DATA_SIZE_SOH else HEAD = STX DATA_SIZE = DATA_SIZE_STX end local XMODEM_SIZE = 1+FRAME_ID_SIZE+DATA_SIZE+CRC_SIZE local packsn = 0 local xmodem_buff = zbuff.create(XMODEM_SIZE) local data_buff = zbuff.create(DATA_SIZE) local fd = io.open(file_path, "rb") if fd then uart.setup(uart_id,baudrate) uart.on(uart_id, "receive", uart_cb) if inform_data and inform_data~="" then uart.write(uart_id,inform_data) end local result, data = sys.waitUntil("xmodem", 12000) if result and (data == CRC_CHR or data == NAK) then cnt = 1 while true do data_buff:set(0, CTRLZ) ret, flen = fd:fill(data_buff,0,DATA_SIZE) log.info("xmodem", "发送第", cnt, "包") if flen > 0 then data_buff:seek(0) crc = crypto.crc16("XMODEM",data_buff) packsn = (packsn+1) & 0xff xmodem_buff[0] = 0x02 xmodem_buff[1] = packsn xmodem_buff[2] = 0xff-xmodem_buff[1] data_buff:seek(DATA_SIZE) xmodem_buff:copy(3, data_buff) xmodem_buff[1027] = crc>>8 xmodem_buff[1028] = crc&0xff xmodem_buff:seek(XMODEM_SIZE) -- log.info(xmodem_buff:used()) :: RESEND :: uart.tx(uart_id, xmodem_buff) result, data = sys.waitUntil("xmodem", 10000) if result and data == ACK then cnt = cnt + 1 elseif result and data == NAK then goto RESEND else uart.write(uart_id, string.char(EOT)) log.info("xmodem", "发送失败") return false end if flen ~= DATA_SIZE then log.info("xmodem", "文件到头了") break end else log.info("xmodem", "文件到头了") break end end uart.write(uart_id, string.char(EOT)) fd:close() return true else log.info("xmodem", "不支持的起始数据包",data) return false end else log.info("xmodem", "待传输的文件不存在") return false end end --[[ 关闭xmodem @api xmodem.close(uart_id) @number uart_id uart端口号 @usage -- 执行xmodem传输后, 无论是否传输成功, 都建议关闭xmodem上下文, 也会关闭uart xmodem.close(2) ]] function xmodem.close(uart_id) uart.on(uart_id, "receive") uart.close(uart_id) end return xmodem