fota_uart.lua 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. --[[
  2. @module fota_uart
  3. @summary 串口FOTA升级功能模块
  4. @version 1.0
  5. @date 2025.10.24
  6. @author 孟伟
  7. @usage
  8. -- 串口FOTA升级功能
  9. -- 提供通过串口分段接收升级包数据进行固件升级的功能
  10. 用法:
  11. 1. 先把脚本和固件烧录到模块里, 并确认开机
  12. 2. 按下Power键启动串口升级模式(设备会等待升级数据)
  13. 3. 在电脑端操作:进入命令行程序,执行 `python main.py` 进行升级,需要保证升级文件名字为 `fota_uart.bin`,并且和 `main.py` 在同一目录下
  14. 注意:运行`python main.py`需要确保电脑安装了Python环境。
  15. 4. 观察luatools的输出和main.py的输出
  16. 5. 模块接收正确的升级数据后,会提示1秒后重启
  17. 6. 本demo自带的脚本升级包,仅加了一条打印和修改版本号
  18. 串口通讯过程说明
  19. 串口升级采用简单的文本协议进行握手和数据传输控制:
  20. 协议流程:
  21. 1. 上位机发送:#FOTA\n
  22. 2. 设备回复:#FOTA RDY\n
  23. 3. 上位机发送:256字节数据包
  24. 4. 设备回复:#FOTA NEXT\n(请求下一包)
  25. 5. 重复步骤3-4直到所有数据发送完成
  26. 6. 设备回复:#FOTA OK\n(升级成功)
  27. 7. 设备自动重启
  28. 注意:
  29. - 本demo默认是走虚拟串口进行交互, 如需改成物理串口, 修改uart_id和main.py
  30. - 升级过程中如果发生错误,串口会自动关闭,需要重新按Power键开启
  31. - 升级成功设备会自动重启
  32. 本文件没有对外接口,直接在main.lua中require "fota_uart"就可以加载运行;
  33. ]]
  34. -- 定义所需要的UART编号
  35. -- uart_id = 1 -- UART1, 通常也是MAIN_UART
  36. local uart_id = uart.VUART_0 -- 虚拟USB串口
  37. -- 全局变量
  38. local uart_zbuff = nil
  39. local uart_fota_state = 0
  40. local uart_rx_counter = 0
  41. local uart_fota_writed = 0
  42. local upgrade_active = false -- 升级是否激活标志
  43. -- 按键回调函数 - Power键
  44. local function power_key_callback()
  45. if not upgrade_active then
  46. log.info("FOTA_UART", "Power键按下,启动串口升级模式")
  47. -- 初始化串口和缓冲区
  48. uart_zbuff = zbuff.create(1024)
  49. uart.setup(uart_id, 115200)
  50. uart.on(uart_id, "receive", uart_cbfun)
  51. upgrade_active = true
  52. uart_fota_state = 0
  53. uart_rx_counter = 0
  54. uart_fota_writed = 0
  55. -- 发布事件,唤醒升级任务
  56. sys.publish("UART_UPGRADE_START")
  57. else
  58. log.info("FOTA_UART", "升级模式已激活,请等待当前升级完成")
  59. end
  60. end
  61. -- 配置Power键
  62. gpio.setup(gpio.PWR_KEY, power_key_callback, gpio.PULLUP, gpio.FALLING)
  63. gpio.debounce(gpio.PWR_KEY, 200, 1) -- 200ms去抖
  64. -- 清理资源的函数
  65. local function cleanup_resources()
  66. if uart_zbuff then
  67. uart_zbuff:del()
  68. uart_zbuff = nil
  69. end
  70. uart.close(uart_id) -- 关闭串口
  71. upgrade_active = false
  72. uart_fota_state = 0
  73. log.info("FOTA_UART", "资源已清理,串口已关闭")
  74. end
  75. -- 串口接收回调函数
  76. function uart_cbfun(id, len)
  77. if not upgrade_active or not uart_zbuff then
  78. return
  79. end
  80. -- 防御缓冲区超标的情况
  81. if uart_zbuff:used() > 8192 then
  82. log.warn("fota", "uart_zbuff待处理的数据太多了,强制清空")
  83. uart_zbuff:del()
  84. end
  85. while 1 do
  86. local len = uart.rx(id, uart_zbuff)
  87. if len <= 0 then
  88. break
  89. end
  90. uart_rx_counter = uart_rx_counter + len
  91. log.info("uart", "收到数据", len, "累计", uart_rx_counter)
  92. -- 首次收到数据即发布事件,唤醒升级任务
  93. if uart_fota_state == 0 then
  94. sys.publish("UART_FOTA")
  95. end
  96. end
  97. end
  98. -- 串口升级任务
  99. local function uartUpgradeTask()
  100. local fota_state = 0 -- 0还没开始, 1进行中
  101. while 1 do
  102. -- 等待升级启动信号
  103. sys.waitUntil("UART_UPGRADE_START")
  104. log.info("FOTA_UART", "升级任务已启动,等待数据...")
  105. while upgrade_active do
  106. -- 等待升级数据到来
  107. sys.waitUntil("UART_FOTA", 1000)
  108. if not upgrade_active then break end
  109. local used = uart_zbuff and uart_zbuff:used() or 0
  110. if used > 0 then
  111. if fota_state == 0 then
  112. -- 等待FOTA的状态
  113. if used > 5 then
  114. local data = uart_zbuff:query()
  115. uart_zbuff:del()
  116. -- 如果接受到 #FOTA\n 代表数据要来了
  117. if data:startsWith("#FOTA") and data:endsWith("\n") then
  118. fota_state = 1
  119. log.info("fota", "检测到fota起始标记,进入FOTA状态", data)
  120. if fota.init() then
  121. -- 固件数据发送端应该在收到#FOTA RDY\n之后才开始发送数据
  122. uart.write(uart_id, "#FOTA RDY\n")
  123. else
  124. log.error("FOTA_UART", "FOTA初始化失败")
  125. cleanup_resources()
  126. break
  127. end
  128. end
  129. end
  130. else
  131. -- 已进入升级状态:把收到的数据喂给fota.run
  132. uart_fota_writed = uart_fota_writed + used
  133. log.info("准备写入fota包", used, "累计写入", uart_fota_writed)
  134. local result, isDone, cache = fota.run(uart_zbuff)
  135. log.debug("fota.run", result, isDone, cache)
  136. uart_zbuff:del() -- 清空缓冲区
  137. if not result then
  138. -- 写入失败,退出升级状态并通知上位机
  139. log.error("fota", "出错了", result, isDone, cache)
  140. uart.write(uart_id, "#FOTA ERR\n")
  141. -- 调用fota.finish(false)结束升级流程,参数false表示升级流程失败。
  142. fota.finish(false)
  143. cleanup_resources()
  144. fota_state = 0
  145. break
  146. elseif isDone then
  147. -- 全部数据写入完成,等待底层校验结束
  148. local success = false
  149. for i = 1, 30 do -- 最多等待3秒
  150. sys.wait(100)
  151. local succ, fotaDone = fota.isDone()
  152. if not succ then
  153. log.error("fota", "校验过程出错")
  154. uart.write(uart_id, "#FOTA ERR\n")
  155. fota.finish(false)
  156. cleanup_resources()
  157. fota_state = 0
  158. break
  159. end
  160. if fotaDone then
  161. uart_fota_state = 1
  162. -- 升级文件成功写入flash中的fota分区,准备重启设备;
  163. log.info("fota", "已完成,1s后重启")
  164. -- 调用fota.finish(true)结束升级流程,参数true表示正确走完流程。
  165. fota.finish(true)
  166. -- 反馈给上位机
  167. uart.write(uart_id, "#FOTA OK\n")
  168. sys.wait(1000)
  169. success = true
  170. rtos.reboot()
  171. break
  172. end
  173. end
  174. if not success then
  175. log.error("fota", "校验超时")
  176. uart.write(uart_id, "#FOTA ERR\n")
  177. fota.finish(false)
  178. cleanup_resources()
  179. fota_state = 0
  180. end
  181. break
  182. else
  183. -- 单包写入成功,通知上位机继续下发
  184. log.info("fota", "单包写入完成", used, "等待下一个包")
  185. uart.write(uart_id, "#FOTA NEXT\n")
  186. end
  187. end
  188. end
  189. end
  190. -- 重置状态,等待下次升级
  191. fota_state = 0
  192. uart_fota_state = 0
  193. uart_rx_counter = 0
  194. uart_fota_writed = 0
  195. end
  196. end
  197. -- 启动串口升级任务
  198. sys.taskInit(uartUpgradeTask)