xmodem.lua 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. --[[
  2. @module xmodem
  3. @summary xmodem 协议
  4. @version 1.0
  5. @date 2025.10.17
  6. @author Dozingfiretruck
  7. @usage
  8. --加载xmodem模块
  9. xmodem=require ("xmodem")
  10. --设置默认filepath为脚本区的send.bin文件
  11. local filepath="/luadb/send.bin"
  12. local taskName = "xmodem_run"
  13. local uart_id = 1 --串口号
  14. local baudrate = 115200 --波特率
  15. local file_path=filepath --文件路径
  16. local send_type=true --true表示单次发送128字节,false表示单次发送1024字节
  17. local inform_data="wait C" --发送前提示信息,告知对方要发送C字符来接收文件
  18. -- 处理未识别的消息
  19. local function xmodem_run_cb(msg)
  20. log.info("xmodem_run_cb", msg[1], msg[2], msg[3], msg[4])
  21. end
  22. --http获取文件函数
  23. local function http_recived_cb()
  24. while not socket.adapter(socket.dft()) do
  25. log.warn("httpplus_app_task_func", "wait IP_READY", socket.dft())
  26. -- 在此处阻塞等待默认网卡连接成功的消息"IP_READY"
  27. -- 或者等待1秒超时退出阻塞等待状态;
  28. -- 注意:此处的1000毫秒超时不要修改的更长;
  29. -- 因为当使用exnetif.set_priority_order配置多个网卡连接外网的优先级时,会隐式的修改默认使用的网卡
  30. -- 当exnetif.set_priority_order的调用时序和此处的socket.adapter(socket.dft())判断时序有可能不匹配
  31. -- 此处的1秒,能够保证,即使时序不匹配,也能1秒钟退出阻塞状态,再去判断socket.adapter(socket.dft())
  32. sys.waitUntil("IP_READY", 1000)
  33. end
  34. local path = "/send.bin"
  35. -- 以下链接仅用于测试,禁止用于生产环境
  36. local code, headers, body_size = http.request("GET", "http://airtest.openluat.com:2900/download/send.bin", nil, nil, {dst=path}).wait()
  37. log.info("http", code==200 and "success" or "error", code)
  38. if code==200 then
  39. log.info("HTTP receive ok",body_size)
  40. file = io.open(path, "rb")
  41. if file then
  42. content = file:read("*a")
  43. log.info("文件读取", "路径:" .. path, "内容:" .. content)
  44. file:close()
  45. else
  46. log.error("文件操作", "无法打开文件读取内容", "路径:" .. path)
  47. end
  48. file_path=path
  49. end
  50. end
  51. -- 定义一个xmodem_run函数,用于用xmodem发送文件
  52. local function xmodem_run()
  53. --如果需要http下载文件,然后发送下载的文件,可以打开下面的http_recived_cb()函数
  54. -- http_recived_cb()
  55. --启动xmodem发送
  56. local result=xmodem.send(uart_id,baudrate,file_path,send_type,inform_data)
  57. --等待时间12秒,等待接收方发送C字符启动发送,发送结束后接收端发送ACK:0x06表示接收完成,文件全部传输完成之后模块发送EOT​:0x04表示传输结束,接收端返回0x06表示确认结束
  58. log.info("Xmodem", "start")
  59. log.info("Xmodem", "send result", result)
  60. --判断是否传输成功,传输是否成功,都需要关闭xmodem
  61. if result then
  62. log.info("Xmodem", "send success")
  63. xmodem.close(uart_id)
  64. else
  65. log.info("Xmodem", "send failed")
  66. xmodem.close(uart_id)
  67. end
  68. end
  69. --创建并且启动一个task
  70. --运行这个task的主函数xmodem_run
  71. sys.taskInit(xmodem_run, taskName,xmodem_run_cb)
  72. ]]
  73. local xmodem = {}
  74. local sys = require "sys"
  75. local HEAD
  76. local DATA_SIZE
  77. local SOH = 0x01 -- Modem数据头 128
  78. local STX = 0x02 -- Modem数据头 1K
  79. local EOT = 0x04 -- 发送结束
  80. local ACK = 0x06 -- 应答
  81. local NAK = 0x15 -- 非应答
  82. local CAN = 0x18 -- 取消发送
  83. local CTRLZ = 0x1A -- 填充
  84. local CRC_CHR = 0x43 -- C: ASCII字符C
  85. local CRC_SIZE = 2
  86. local FRAME_ID_SIZE = 2
  87. local DATA_SIZE_SOH = 128
  88. local DATA_SIZE_STX = 1024
  89. local function uart_cb(id, len)
  90. local data = uart.read(id, 1024)
  91. if #data == 0 then
  92. return
  93. end
  94. log.info("xmodem", "uart读取到数据:", data:toHex())
  95. data = data:byte(1)
  96. sys.publish("xmodem", data)
  97. end
  98. --[[
  99. xmodem 发送文件
  100. @api xmodem.send(uart_id,baudrate,type,inform_data)
  101. @number uart_id uart端口号
  102. @number uart_br uart波特率
  103. @string file_path 文件路径
  104. @bool type 1k/128 默认1k
  105. @return bool 发送结果
  106. @usage
  107. xmodem.send(1, 115200, "/luadb/send.bin",true)
  108. ]]
  109. function xmodem.send(uart_id,baudrate,file_path,type,inform_data)
  110. local ret, flen, cnt, crc
  111. if type then
  112. HEAD = SOH
  113. DATA_SIZE = DATA_SIZE_SOH
  114. else
  115. HEAD = STX
  116. DATA_SIZE = DATA_SIZE_STX
  117. end
  118. local XMODEM_SIZE = 1+FRAME_ID_SIZE+DATA_SIZE+CRC_SIZE
  119. local packsn = 0
  120. local xmodem_buff = zbuff.create(XMODEM_SIZE)
  121. local data_buff = zbuff.create(DATA_SIZE)
  122. local fd = io.open(file_path, "rb")
  123. if fd then
  124. uart.setup(uart_id,baudrate)
  125. uart.on(uart_id, "receive", uart_cb)
  126. if inform_data and inform_data~="" then
  127. uart.write(uart_id,inform_data)
  128. end
  129. local result, data = sys.waitUntil("xmodem", 12000)
  130. if result and (data == CRC_CHR or data == NAK) then
  131. cnt = 1
  132. while true do
  133. data_buff:set(0, CTRLZ)
  134. ret, flen = fd:fill(data_buff,0,DATA_SIZE)
  135. log.info("xmodem", "发送第", cnt, "包")
  136. if flen > 0 then
  137. data_buff:seek(0)
  138. crc = crypto.crc16("XMODEM",data_buff)
  139. packsn = (packsn+1) & 0xff
  140. xmodem_buff[0] = 0x02
  141. xmodem_buff[1] = packsn
  142. xmodem_buff[2] = 0xff-xmodem_buff[1]
  143. data_buff:seek(DATA_SIZE)
  144. xmodem_buff:copy(3, data_buff)
  145. xmodem_buff[1027] = crc>>8
  146. xmodem_buff[1028] = crc&0xff
  147. xmodem_buff:seek(XMODEM_SIZE)
  148. -- log.info(xmodem_buff:used())
  149. :: RESEND ::
  150. uart.tx(uart_id, xmodem_buff)
  151. result, data = sys.waitUntil("xmodem", 10000)
  152. if result and data == ACK then
  153. cnt = cnt + 1
  154. elseif result and data == NAK then
  155. goto RESEND
  156. else
  157. uart.write(uart_id, string.char(EOT))
  158. log.info("xmodem", "发送失败")
  159. return false
  160. end
  161. if flen ~= DATA_SIZE then
  162. log.info("xmodem", "文件到头了")
  163. break
  164. end
  165. else
  166. log.info("xmodem", "文件到头了")
  167. break
  168. end
  169. end
  170. uart.write(uart_id, string.char(EOT))
  171. fd:close()
  172. return true
  173. else
  174. log.info("xmodem", "不支持的起始数据包",data)
  175. return false
  176. end
  177. else
  178. log.info("xmodem", "待传输的文件不存在")
  179. return false
  180. end
  181. end
  182. --[[
  183. 关闭xmodem
  184. @api xmodem.close(uart_id)
  185. @number uart_id uart端口号
  186. @usage
  187. -- 执行xmodem传输后, 无论是否传输成功, 都建议关闭xmodem上下文, 也会关闭uart
  188. xmodem.close(2)
  189. ]]
  190. function xmodem.close(uart_id)
  191. uart.on(uart_id, "receive")
  192. uart.close(uart_id)
  193. end
  194. return xmodem