--[[ @module exfotawifi @summary 用于Air8000/8000A/8000W型号模组自动升级WIFI @version 1.0.3 @date 2025.9.23 @author 拓毅恒 @usage 注:使用时在创建的一个task处理函数中直接调用exfotawifi.request()即可开始执行WiFi升级任务 升级完毕后最好取消调用,防止后期版本升级过高导致程序使用不稳定 -- 用法实例 local exfotawifi = require("exfotawifi") local function fota_wifi_task() -- ...此处省略很多代码 local result = exfotawifi.request() if result then log.info("exfotawifi", "升级任务执行成功") else log.info("exfotawifi", "升级任务执行失败") end -- ...此处省略很多代码 end -- 启动WiFi自动更新任务 sys.taskInit(fota_wifi_task) ]] local exfotawifi = {} local is_request = false -- 标记是否正在执行request任务 local fota_result = false -- 记录fota任务的执行结果 -- 判断是否为空 local function is_nil(s) return s == nil or s == "" end -- 判断json是否合法 local function is_json(str) local success, result = pcall(json.decode, str) return success and type(result) == "table" end -- 解析服务器响应的json数据 local function parse_response(body) if not body or body == "" then log.error("exfotawifi", "返回的body为空") return nil end local success, json_body = pcall(json.decode, body) if success and type(json_body) == "table" then log.info("exfotawifi", "解析服务器响应成功") return json_body else log.error("exfotawifi", "解析服务器响应失败,body内容:", body) return nil end end -- 判断是否需要升级,返回true或false local function need_fota(version, server_version) local version_num = tonumber(version) local server_version_num = tonumber(server_version) if version_num < server_version_num then return true end return false end -- 下载升级文件,支持断点续传 local function download_file(url) local download_dir = "/http_download/" local result, reason = io.mkdir(download_dir) if not result then log.error("download_file","io.mkdir error", reason) end local file_path = download_dir.."fotawifi.bin" local downloaded_size = 0 -- 检查文件是否存在,获取已下载的大小 if io.exists(file_path) then downloaded_size = io.fileSize(file_path) log.info("exfotawifi", "检测到未完成的下载,已下载大小:", downloaded_size) end -- 设置请求头,支持断点续传 local headers = {} if downloaded_size > 0 then headers["Range"] = "bytes=" .. downloaded_size .. "-" end local code, headers, body = http.request("GET", url, headers, nil, nil).wait() if code == 200 or code == 206 then -- 开始写入文件 local file_mode = downloaded_size > 0 and "a+" or "w+" local file = io.open(file_path, file_mode) if file then file:seek("end", downloaded_size) file:write(body) file:close() -- 判断文件是否下载完整 local file_size = io.fileSize(file_path) local content_length = tonumber(headers["content-length"] or headers["Content-Length"]) if file_size >= (content_length or file_size) then log.info("exfotawifi", "下载升级文件成功,文件路径:", file_path) return file_path else log.info("exfotawifi", "下载中...当前大小:", file_size, "目标大小:", content_length) end else log.error("exfotawifi", "无法创建文件") -- 删除不完整的文件 os.remove(file_path) end else log.error("exfotawifi", "下载失败,状态码:", code) -- 删除不完整的文件 if io.exists(file_path) then os.remove(file_path) end end return nil end -- 执行升级操作 local function fota_start(file_path) -- 检查文件是否存在 if not io.exists(file_path) then log.error("exfotawifi", "升级文件不存在") return false end -- 检查文件大小是否超过256K (256 * 1024 Bytes) local file_size = io.fileSize(file_path) if file_size < 256 * 1024 then log.error("exfotawifi", "升级文件大小不足256K,文件大小:", file_size) return false end -- 执行airlink.sfota操作 local result = airlink.sfota(file_path) if result then log.info("exfotawifi", "升级成功") -- 释放文件占用的空间 -- 因为sfota是异步执行的,所以这里不能用os.remove()删除文件 file_path = nil return true else log.error("exfotawifi", "升级失败") os.remove(file_path) return false end end function exfotawifi.request() local result, ip, adapter = sys.waitUntil("IP_READY", 30000) if result then log.info("exfotawifi", "开始执行升级任务") if is_request then log.warn("exfotawifi", "升级任务正在执行中,请勿重复调用") return false end is_request = true fota_result = false -- 构建请求URL local url = "http://wififota.openluat.com/air8000/update.json" local imei = is_nil(mobile.imei()) and "未知imei" or mobile.imei() local version = is_nil(airlink.sver()) and "未知版本" or airlink.sver() local muid = is_nil(mobile.muid()) and "未知muid" or mobile.muid() local hw = is_nil(hmeta.hwver()) and "未知硬件版本" or hmeta.hwver() local coreversion = is_nil(rtos.version()) and "未知4G固件版本" or rtos.version() local model = is_nil(hmeta.model()) and "未知4G设备型号" or hmeta.model() 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) log.info("exfotawifi", "正在请求升级信息, URL:", request_url) -- 发送HTTP请求获取服务器响应 local code, headers, body = http.request("GET", request_url, {}, nil, {timeout = 30000}).wait() if code == 200 then log.info("exfotawifi", "获取服务器响应成功") -- 打印返回的body内容 -- log.info("exfotawifi", "body:", body) -- 解析服务器响应的json数据 local response = parse_response(body) if response then -- 获取服务器返回的版本号和下载链接 local server_version = response.version local download_url = response.url -- 获取本地版本号 local local_version = airlink.sver() -- 判断是否需要升级 if need_fota(local_version, server_version) then log.info("exfotawifi", "需要升级, 本地版本:", local_version, "服务器版本:", server_version) -- 下载升级文件 local file_path = download_file(download_url) if file_path then -- 开始升级 fota_result = fota_start(file_path) end else log.info("exfotawifi", "当前已是最新WIFI固件") fota_result = true end else log.error("exfotawifi", "解析服务器响应失败") end else log.error("exfotawifi", "获取服务器响应失败,状态码:", code) end else log.error("当前正在升级WIFI&蓝牙固件,请插入可以上网的SIM卡并重新启动") end -- 释放请求标记 is_request = false return fota_result end return exfotawifi