ble_packet_fota.lua 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. --[[
  2. @module ble_packet_fota
  3. @summary 蓝牙FOTA升级功能模块(分段写入方式)
  4. @version 1.0
  5. @date 2025.12.08
  6. @author 孟伟
  7. @usage
  8. -- 蓝牙FOTA升级功能(分段写入方式)
  9. -- 提供通过蓝牙低功耗(BLE)接收升级包数据进行固件升级的功能
  10. 本文件为FOTA业务逻辑处理模块,核心业务逻辑为:
  11. 1. 处理接收到的BLE写入请求数据
  12. 2. 实现FOTA升级流程的控制(分段写入方式)
  13. 3. 管理升级状态和分段数据操作
  14. 本文件的对外接口有1个:
  15. 1. ble_packet_fota.proc(service_uuid, char_uuid, data): 处理接收到的BLE写入请求数据
  16. 依赖模块:
  17. - ble_main: 用于提供BLE服务和事件处理
  18. ]]
  19. local ble_packet_fota = {}
  20. -- 升级状态管理
  21. local upgrade_state = {
  22. is_upgrading = false, -- 是否正在升级
  23. total_size = 0, -- 总文件大小(字节)
  24. received_size = 0, -- 已接收大小(字节)
  25. upgrade_packet = 0 -- 升级包计数器
  26. }
  27. -- 配置参数
  28. local config = {
  29. service_uuid = "F000", -- FOTA服务UUID(短格式)
  30. char_uuid_cmd = "F001", -- 命令特征值UUID
  31. char_uuid_data = "F002", -- 数据特征值UUID
  32. max_packet_size = 200 -- BLE数据包最大长度(字节)
  33. }
  34. local function ble_reboot()
  35. -- 完成FOTA流程并重启
  36. fota.finish(true)
  37. log.info("FOTA_CMD", "正在重启设备...")
  38. rtos.reboot()
  39. end
  40. -- 处理FOTA命令
  41. -- @param cmd_data 命令数据,格式:[命令码(1字节)] 或 [命令码(1字节) + 文件大小(4字节)]
  42. local function handle_command(cmd_data)
  43. log.info("FOTA_CMD", "收到命令数据:", cmd_data:toHex(), "长度:", #cmd_data)
  44. -- 检查命令数据是否有效
  45. if #cmd_data < 1 then
  46. log.error("FOTA_CMD", "命令数据为空")
  47. return
  48. end
  49. -- 解析命令码(第一个字节)
  50. local cmd = cmd_data:byte(1)
  51. log.info("FOTA_CMD", "解析命令码:", cmd, string.format("(0x%02X)", cmd))
  52. -- 命令0x01:开始升级
  53. if cmd == 0x01 then
  54. log.info("FOTA_CMD", "处理开始升级命令")
  55. -- 检查命令格式:需要至少5字节(1字节命令码 + 4字节文件大小)
  56. if #cmd_data >= 5 then
  57. -- 解析文件大小(小端序,从第2字节开始)
  58. local total_size = string.unpack("<I4", cmd_data, 2)
  59. log.info("FOTA_CMD", "文件总大小:", total_size, "字节")
  60. -- 初始化FOTA子系统
  61. log.info("FOTA_CMD", "初始化FOTA子系统...")
  62. if fota.init() then
  63. log.info("FOTA_CMD", "FOTA初始化成功")
  64. -- 等待FOTA底层准备就绪
  65. log.info("FOTA_CMD", "等待FOTA底层准备...")
  66. -- 等待FOTA底层准备就绪,最多等待10秒
  67. local wait_count = 0
  68. local wait_ok = false
  69. while wait_count < 100 do -- 最多轮询100次,每次100ms,共10秒
  70. if fota.wait() then
  71. wait_ok = true
  72. break
  73. end
  74. sys.wait(100)
  75. wait_count = wait_count + 1
  76. end
  77. if wait_ok then
  78. log.info("FOTA_CMD", "FOTA底层准备就绪")
  79. -- 更新升级状态
  80. upgrade_state.is_upgrading = true
  81. upgrade_state.total_size = total_size
  82. upgrade_state.received_size = 0
  83. upgrade_state.upgrade_packet = 0
  84. log.info("FOTA_CMD", "升级状态已设置",
  85. "总大小:", upgrade_state.total_size)
  86. log.info("FOTA_CMD", "准备接收固件数据...")
  87. else
  88. log.error("FOTA_CMD", "FOTA底层准备超时")
  89. fota.finish(false)
  90. upgrade_state.is_upgrading = false
  91. end
  92. else
  93. log.error("FOTA_CMD", "FOTA初始化失败")
  94. end
  95. else
  96. log.error("FOTA_CMD", "开始命令格式错误,长度不足")
  97. end
  98. -- 命令0x02:结束升级(通知升级包发完)
  99. elseif cmd == 0x02 then
  100. log.info("FOTA_CMD", "处理结束升级命令")
  101. -- 检查是否处于升级状态
  102. if not upgrade_state.is_upgrading then
  103. log.warn("FOTA_CMD", "未处于升级状态,忽略结束命令")
  104. return
  105. end
  106. -- 验证文件完整性
  107. log.info("FOTA_CMD", "验证文件完整性...")
  108. log.info("FOTA_CMD", "已接收:", upgrade_state.received_size, "字节")
  109. log.info("FOTA_CMD", "应接收:", upgrade_state.total_size, "字节")
  110. if upgrade_state.received_size == upgrade_state.total_size then
  111. log.info("FOTA_CMD", "文件完整性验证通过")
  112. log.info("FOTA_CMD", "升级数据已全部接收,等待升级完成...")
  113. -- 等待底层校验结束
  114. local success = false
  115. for i = 1, 30 do -- 最多等待3秒
  116. sys.wait(100)
  117. local succ, fotaDone = fota.isDone()
  118. if not succ then
  119. log.error("FOTA_CMD", "校验过程出错")
  120. fota.finish(false)
  121. upgrade_state.is_upgrading = false
  122. break
  123. end
  124. if fotaDone then
  125. log.info("FOTA_CMD", "FOTA升级成功!")
  126. -- 延迟重启,给用户一些反应时间
  127. log.info("FOTA_CMD", "2秒后设备将自动重启...,重启后通过日志判断最终是否升级成功")
  128. -- 延迟2秒后重启设备
  129. sys.timerStart(ble_reboot, 2000)
  130. success = true
  131. break
  132. end
  133. end
  134. if not success then
  135. log.error("FOTA_CMD", "校验超时")
  136. fota.finish(false)
  137. upgrade_state.is_upgrading = false
  138. end
  139. else
  140. log.error("FOTA_CMD", "文件不完整,升级失败")
  141. -- 清理升级状态
  142. upgrade_state.is_upgrading = false
  143. fota.finish(false)
  144. end
  145. log.info("FOTA_CMD", "结束升级命令处理完成")
  146. else
  147. log.warn("FOTA_CMD", "未知命令码:", cmd, string.format("(0x%02X)", cmd))
  148. end
  149. end
  150. -- 处理FOTA数据
  151. -- @param data 固件数据块
  152. local function handle_data(data)
  153. log.info("FOTA_DATA", "收到数据包,长度:", #data, "字节")
  154. -- 检查是否处于升级状态
  155. if not upgrade_state.is_upgrading then
  156. log.warn("FOTA_DATA", "未处于升级状态,忽略数据")
  157. return
  158. end
  159. -- 直接使用fota.run()处理分段数据,不写入文件
  160. log.info("FOTA_DATA", "处理分段数据,包序号:", upgrade_state.upgrade_packet)
  161. local result, isDone = fota.run(data)
  162. log.info("FOTA_DATA", "分段写入结果:", "result:", result, "isDone:", isDone)
  163. if result then
  164. -- 更新接收状态
  165. upgrade_state.received_size = upgrade_state.received_size + #data
  166. upgrade_state.upgrade_packet = upgrade_state.upgrade_packet + 1
  167. -- 计算并显示进度
  168. local progress = math.floor((upgrade_state.received_size / upgrade_state.total_size) * 100)
  169. -- 每50个数据包或完成时打印进度
  170. if upgrade_state.received_size % (config.max_packet_size * 50) == 0 or
  171. upgrade_state.received_size >= upgrade_state.total_size then
  172. log.info("FOTA_DATA", "升级进度:", progress, "%",
  173. "(", upgrade_state.received_size, "/", upgrade_state.total_size, ")")
  174. end
  175. log.info("FOTA_DATA", "数据写入成功,当前总计:", upgrade_state.received_size, "字节")
  176. -- 如果所有数据都已接收,检查升级是否完成
  177. if upgrade_state.received_size >= upgrade_state.total_size then
  178. log.info("FOTA_DATA", "所有数据已接收,等待升级完成...")
  179. end
  180. else
  181. log.error("FOTA_DATA", "分段写入失败")
  182. -- 分段写入失败,终止升级
  183. upgrade_state.is_upgrading = false
  184. fota.finish(false)
  185. end
  186. end
  187. -- 处理接收到的BLE写入请求数据
  188. -- @param service_uuid 服务UUID
  189. -- @param char_uuid 特征值UUID
  190. -- @param data 写入的数据
  191. function ble_packet_fota.proc(service_uuid, char_uuid, data)
  192. log.info("ble_packet_fota", "处理写入数据", service_uuid, char_uuid, data:toHex())
  193. -- 简化的UUID匹配逻辑:检查UUID是否包含我们的短UUID
  194. local is_service_match = string.find(service_uuid:lower(), config.service_uuid:lower())
  195. local is_cmd_match = string.find(char_uuid:lower(), config.char_uuid_cmd:lower())
  196. local is_data_match = string.find(char_uuid:lower(), config.char_uuid_data:lower())
  197. log.info("ble_packet_fota", "UUID匹配结果:",
  198. "服务匹配:", is_service_match,
  199. "命令匹配:", is_cmd_match,
  200. "数据匹配:", is_data_match)
  201. if is_service_match then
  202. if is_cmd_match then
  203. -- 命令特征值:处理FOTA命令
  204. log.info("ble_packet_fota", "命令特征值匹配,处理命令")
  205. handle_command(data)
  206. elseif is_data_match then
  207. -- 数据特征值:处理FOTA数据
  208. log.info("ble_packet_fota", "数据特征值匹配,处理数据")
  209. handle_data(data)
  210. else
  211. log.warn("ble_packet_fota", "未知的特征值UUID:", char_uuid)
  212. end
  213. else
  214. log.warn("ble_packet_fota", "未知的服务UUID:", service_uuid)
  215. end
  216. end
  217. -- 处理BLE连接断开事件
  218. -- @return nil
  219. function ble_packet_fota.proc_disconnect()
  220. log.info("ble_packet_fota", "处理连接断开事件")
  221. -- 如果正在升级,连接断开则终止升级
  222. if upgrade_state.is_upgrading then
  223. log.error("ble_packet_fota", "升级过程中连接断开,终止升级")
  224. upgrade_state.is_upgrading = false
  225. -- 结束FOTA流程
  226. fota.finish(false)
  227. end
  228. end
  229. return ble_packet_fota