exfotawifi.lua 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. --[[
  2. @module exfotawifi
  3. @summary 用于Air8000/8000A/8000W型号模组自动升级WIFI
  4. @version 1.0.3
  5. @date 2025.9.23
  6. @author 拓毅恒
  7. @usage
  8. 注:使用时在创建的一个task处理函数中直接调用exfotawifi.request()即可开始执行WiFi升级任务
  9. 升级完毕后最好取消调用,防止后期版本升级过高导致程序使用不稳定
  10. -- 用法实例
  11. local exfotawifi = require("exfotawifi")
  12. local function fota_wifi_task()
  13. -- ...此处省略很多代码
  14. local result = exfotawifi.request()
  15. if result then
  16. log.info("exfotawifi", "升级任务执行成功")
  17. else
  18. log.info("exfotawifi", "升级任务执行失败")
  19. end
  20. -- ...此处省略很多代码
  21. end
  22. -- 启动WiFi自动更新任务
  23. sys.taskInit(fota_wifi_task)
  24. ]]
  25. local exfotawifi = {}
  26. local is_request = false -- 标记是否正在执行request任务
  27. local fota_result = false -- 记录fota任务的执行结果
  28. -- 判断是否为空
  29. local function is_nil(s)
  30. return s == nil or s == ""
  31. end
  32. -- 判断json是否合法
  33. local function is_json(str)
  34. local success, result = pcall(json.decode, str)
  35. return success and type(result) == "table"
  36. end
  37. -- 解析服务器响应的json数据
  38. local function parse_response(body)
  39. if not body or body == "" then
  40. log.error("exfotawifi", "返回的body为空")
  41. return nil
  42. end
  43. local success, json_body = pcall(json.decode, body)
  44. if success and type(json_body) == "table" then
  45. log.info("exfotawifi", "解析服务器响应成功")
  46. return json_body
  47. else
  48. log.error("exfotawifi", "解析服务器响应失败,body内容:", body)
  49. return nil
  50. end
  51. end
  52. -- 判断是否需要升级,返回true或false
  53. local function need_fota(version, server_version)
  54. local version_num = tonumber(version)
  55. local server_version_num = tonumber(server_version)
  56. if version_num < server_version_num then
  57. return true
  58. end
  59. return false
  60. end
  61. -- 下载升级文件,支持断点续传
  62. local function download_file(url)
  63. local download_dir = "/http_download/"
  64. local result, reason = io.mkdir(download_dir)
  65. if not result then
  66. log.error("download_file","io.mkdir error", reason)
  67. end
  68. local file_path = download_dir.."fotawifi.bin"
  69. local downloaded_size = 0
  70. -- 检查文件是否存在,获取已下载的大小
  71. if io.exists(file_path) then
  72. downloaded_size = io.fileSize(file_path)
  73. log.info("exfotawifi", "检测到未完成的下载,已下载大小:", downloaded_size)
  74. end
  75. -- 设置请求头,支持断点续传
  76. local headers = {}
  77. if downloaded_size > 0 then
  78. headers["Range"] = "bytes=" .. downloaded_size .. "-"
  79. end
  80. local code, headers, body = http.request("GET", url, headers, nil, nil).wait()
  81. if code == 200 or code == 206 then
  82. -- 开始写入文件
  83. local file_mode = downloaded_size > 0 and "a+" or "w+"
  84. local file = io.open(file_path, file_mode)
  85. if file then
  86. file:seek("end", downloaded_size)
  87. file:write(body)
  88. file:close()
  89. -- 判断文件是否下载完整
  90. local file_size = io.fileSize(file_path)
  91. local content_length = tonumber(headers["content-length"] or headers["Content-Length"])
  92. if file_size >= (content_length or file_size) then
  93. log.info("exfotawifi", "下载升级文件成功,文件路径:", file_path)
  94. return file_path
  95. else
  96. log.info("exfotawifi", "下载中...当前大小:", file_size, "目标大小:", content_length)
  97. end
  98. else
  99. log.error("exfotawifi", "无法创建文件")
  100. -- 删除不完整的文件
  101. os.remove(file_path)
  102. end
  103. else
  104. log.error("exfotawifi", "下载失败,状态码:", code)
  105. -- 删除不完整的文件
  106. if io.exists(file_path) then
  107. os.remove(file_path)
  108. end
  109. end
  110. return nil
  111. end
  112. -- 执行升级操作
  113. local function fota_start(file_path)
  114. -- 检查文件是否存在
  115. if not io.exists(file_path) then
  116. log.error("exfotawifi", "升级文件不存在")
  117. return false
  118. end
  119. -- 检查文件大小是否超过256K (256 * 1024 Bytes)
  120. local file_size = io.fileSize(file_path)
  121. if file_size < 256 * 1024 then
  122. log.error("exfotawifi", "升级文件大小不足256K,文件大小:", file_size)
  123. return false
  124. end
  125. -- 执行airlink.sfota操作
  126. local result = airlink.sfota(file_path)
  127. if result then
  128. log.info("exfotawifi", "升级成功")
  129. -- 释放文件占用的空间
  130. -- 因为sfota是异步执行的,所以这里不能用os.remove()删除文件
  131. file_path = nil
  132. return true
  133. else
  134. log.error("exfotawifi", "升级失败")
  135. os.remove(file_path)
  136. return false
  137. end
  138. end
  139. function exfotawifi.request()
  140. local result, ip, adapter = sys.waitUntil("IP_READY", 30000)
  141. if result then
  142. log.info("exfotawifi", "开始执行升级任务")
  143. if is_request then
  144. log.warn("exfotawifi", "升级任务正在执行中,请勿重复调用")
  145. return false
  146. end
  147. is_request = true
  148. fota_result = false
  149. -- 构建请求URL
  150. local url = "http://wififota.openluat.com/air8000/update.json"
  151. local imei = is_nil(mobile.imei()) and "未知imei" or mobile.imei()
  152. local version = is_nil(airlink.sver()) and "未知版本" or airlink.sver()
  153. local muid = is_nil(mobile.muid()) and "未知muid" or mobile.muid()
  154. local hw = is_nil(hmeta.hwver()) and "未知硬件版本" or hmeta.hwver()
  155. local coreversion = is_nil(rtos.version()) and "未知4G固件版本" or rtos.version()
  156. local model = is_nil(hmeta.model()) and "未知4G设备型号" or hmeta.model()
  157. local request_url = string.format("%s?imei=%s&version=%s&muid=%s&hw=%s&coreversion=%s&model=%s", url, imei, version, muid, hw, coreversion, model)
  158. log.info("exfotawifi", "正在请求升级信息, URL:", request_url)
  159. -- 发送HTTP请求获取服务器响应
  160. local code, headers, body = http.request("GET", request_url, {}, nil, {timeout = 30000}).wait()
  161. if code == 200 then
  162. log.info("exfotawifi", "获取服务器响应成功")
  163. -- 打印返回的body内容
  164. -- log.info("exfotawifi", "body:", body)
  165. -- 解析服务器响应的json数据
  166. local response = parse_response(body)
  167. if response then
  168. -- 获取服务器返回的版本号和下载链接
  169. local server_version = response.version
  170. local download_url = response.url
  171. -- 获取本地版本号
  172. local local_version = airlink.sver()
  173. -- 判断是否需要升级
  174. if need_fota(local_version, server_version) then
  175. log.info("exfotawifi", "需要升级, 本地版本:", local_version, "服务器版本:", server_version)
  176. -- 下载升级文件
  177. local file_path = download_file(download_url)
  178. if file_path then
  179. -- 开始升级
  180. fota_result = fota_start(file_path)
  181. end
  182. else
  183. log.info("exfotawifi", "当前已是最新WIFI固件")
  184. fota_result = true
  185. end
  186. else
  187. log.error("exfotawifi", "解析服务器响应失败")
  188. end
  189. else
  190. log.error("exfotawifi", "获取服务器响应失败,状态码:", code)
  191. end
  192. else
  193. log.error("当前正在升级WIFI&蓝牙固件,请插入可以上网的SIM卡并重新启动")
  194. end
  195. -- 释放请求标记
  196. is_request = false
  197. return fota_result
  198. end
  199. return exfotawifi