Toggle navigation
首页
(current)
问答
文章
话题
商城
登录
注册
Luat全系列模块支持免费OTA远程升级,并提供例程将该功能部署到自己服务器上
升级服务
详解luat升级服务器所需要流程,服务器要求,参数传递,示例代码
## 准备姿势, update升级流程 先看一眼链接哦, 对升级过程有个认识 http://oldask.openluat.com/article/93 **升级流程 > 模块读取自身信息--拼接字符串--get请求服务器--服务器收到请求--处理请求--如果版本号低于最新版--返回200--模块下载升级包--下载成功自动重启--升级成功 如果版本号大于等于最新版--返回404错误码--模块停止升级检查。 ## 再次强调重点 1. 服务器返回200, 设备将读取响应,作为升级文件 2. 服务器返回3XX, 重定向到新的地址下载 3. 服务器返回4XX, 设备无需升级 ## 设备端代码 ``` require "update" update.request(nil,"iot.nutz.cn/api/site/firmware_upgrade") // 域名 + 升级URI, 用ip也是可以的 // 仅支持http协议,除非自行扩展update.lua ``` 4G模块的升级报错代码,请查阅 http://oldask.openluat.com/article/90 划重点: **要用外网服务器!!! 局域网可访问不了!!!** ## 服务器端 设备会发什么上来: 1. URI: /luat/update 自定义即可,服务器与设备要一致 2. 参数5个,均通过URL传递 * project_key 产品密钥,自行定义,可以不管 * imei 设备识别号 * firmware_name 当前固件名称,相当于Lod版本 * version 用户自定义版本号 * need_oss_url 是否需要oss路径(仅供实现了CDN之后使用),可以无视 服务器端接收到这些参数后,根据业务逻辑决定返回的内容 例如, 代码这样写,用的lod是Luat_V0017_ASR1802 ``` PRODUCT_KEY = "sadfsaqwerOKMGUFI" PROJECT = "ALIYUN" VERSION = "2.0.0" ``` 那么, URL参数会是这样 ``` project_key=sadfsaqwerOKMGUFI imei=86902342332452 firmware_name=ALIYUN_Luat_V0007_ASR1802 version=2.0.0 need_oss_url=0 ``` 另有**完整**服务器端实现,请查阅 http://oldask.openluat.com/article/878 ## 代码示例 ### 伪代码版 ``` // 根据imei查出设备 var dev = sql("select * from t_dev where imie=" + imei); if (dev == null) { // 没有这个设备,不准升级, or 自动插入记录 return resp(403); } // 根据设备查出升级计划 var updatePlan = sql("select * from t_update_plan where dev_id=" + dev.id) if (updatePlan == null) { // 没有对应的升级计划,不准升级 return resp(403); } // 固件版本是否对应 if (firmware_name != updatePlan.firmware_name) { // 固件版本不对应,无法升级 return resp(404); // 不是返回200就行 } // 软件版本号是否一致 if (version == updatePlan.version) { // 软件版本一样,无需升级 return resp(404); } // 需要升级,写入升级文件 resp.write(updatePlan.file) // 结束. ``` ### java版(使用nutz实现) ``` @IocBean @At("/luat") public class LuatUpdateModule { @Fail("http:500") @Ok("void") @At("/update") public void update(String project_key, String imei, String firmware_name, String version, int need_oss_url, HttpServletResponse resp) throws FileNotFoundException, IOException { // TODO 根据 imei 查出设备 // TODO 根据设备查出升级计划 String expectVersion = "2.0.1"; // 判断版本号 if (expectVersion.equalsIgnoreCase(version)) { resp.setStatus(404); // 不需要升级 return; } try (FileInputStream ins = new FileInputStream("/data/luat/update/" + expectVersion + "/update.bin")) { Streams.writeAndClose(resp.getOutputStream(), ins); } } } ``` ### php例子,由[罗耀锋](http://oldask.openluat.com/people/60)提供 ```php public function actionUpgrade() { $imei = UtilsApp::getParamterByParamterName("imei"); $ver = UtilsApp::getParamterByParamterName("version"); $updates = CardUpgrade::find()->all(); foreach ( $updates as $update) { $verInt = intval($ver); $updateVer = intval($update->ver); if ($updateVer>$verInt) { Yii::$app->getResponse()->sendFile(Yii::$app->basePath . '/web/' . $update->filepath, 'update.bin'); return; } } Yii::$app->response->statusCode=500; } ``` ### 基于UDP的升级检查服务 python版 ```python #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ device_upgrade_server.py python3, ubuntu16.04, mysql. 简单的升级服务,无数据校验等,仅供参考。 升级的配置存在mysql,是由另外的网页程序实现的。 需要安装sqlalchemy和gevent pip3 install sqlalchemy pip3 install gevent mysql的python接口 apt-get install python3-mysql.connector """ from gevent.server import DatagramServer # 使用gevent的udp服务器。也可以直接用socket收。 from gevent import monkey monkey.patch_all() import gevent import os import sys import time import datetime import re import traceback import logging logging.basicConfig(level = logging.DEBUG) dbg = logging.debug def print_exception_info(): # 打印trace信息 #dbg(type(sys.exc_info()[1])) #dbg(sys.exc_info()[1]) traceback.print_exc() #exc_type, exc_obj, exc_tb = sys.exc_info() #fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] #dbg(exc_type, fname, exc_tb.tb_lineno) import sqlalchemy from sqlalchemy import * from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker # mysql数据库配置 DB = 'mysql+mysqlconnector://name:password@127.0.0.1:3306/device_framework_upgrade' dbengine = create_engine(DB, echo = False, pool_size = 512, max_overflow = 0, pool_timeout = 0, pool_recycle = 5) Session = sessionmaker(bind = dbengine) ''' device's data look like "862991443753386,A9335_V1723_B3633_WD1,1.1.8" and "Get6,12" 数据格式 1.recv [imei,project_name,version] 2.send [LUAUPDATE,upgrade_id,package_count,last_package_size] 3.recv [Getn,upgrade_id] 4.send [data] ... ''' PACKAGE_SIZE = 1022 start_cmd_pattern = re.compile("^(\d{15}),(\w+),(\d+\.\d+\.\d+)$") get_cmd_pattern = re.compile("^Get(\d+),(\d+)$") class SingleUpgradeServer(DatagramServer): def __init__(self, *args, **kwargs): DatagramServer.__init__(self, *args, **kwargs) def handle(self, data_org, address): # 接收数据 try: dbg('=================================================') dbg(('=== %s %s: got data_org') % (time.ctime(), address[0])) dbg('=================================================') # 连接数据库 dbsession = Session() data = data_org.decode('ascii') # ascii解码 dbg(data) function = None reply = bytearray() # 匹配命令 match = start_cmd_pattern.match(data) if match: function = 0 else: match = get_cmd_pattern.match(data) if match: function = 1 dbg('function = {}'.format(function)) if function == 0: # match start_cmd_pattern imei, project_name, version = match.groups() # 在数据库中根据项目名查找固件 upgrade = dbsession.execute(text("select * from device_framework_upgrade.single_upgrade where project_name = :project_name and status = 1"), {"project_name":project_name}).fetchone() if upgrade is None: reply = "no upgrade".encode('ascii') dbg("no upgrade") # 检查imei是否在升级范围内 in_range = False imei_ranges = dbsession.execute(text("select * from device_framework_upgrade.single_upgrade_imei_range where upgrade_id = :upgrade_id"), {"upgrade_id":upgrade.id}).fetchall() for imei_range in imei_ranges: if imei_range.starting_imei <= imei and imei <= imei_range.ending_imei: in_range = True break if not in_range: reply = "imei range error".encode('ascii') dbg("range error") # 检查设备版本号是否最新 v1, v2, v3 = upgrade["version"].split('.') server_version = (int(v1) << 16) + (int(v2) << 8) + int(v3) v1, v2, v3 = version.split('.') device_version = (int(v1) << 16) + (int(v2) << 8) + int(v3) if device_version >= server_version: reply = "version error".encode('ascii') dbg("version error") else: # 读取文件信息 file_path = upgrade['file_path'] dbg(file_path) file_size = os.path.getsize(file_path) last_package_size = file_size % PACKAGE_SIZE package_count = int(file_size / PACKAGE_SIZE) if last_package_size != 0: package_count += 1 dbg((file_size, package_count, last_package_size)) reply = ("LUAUPDATE,%d,%d,%d" % (upgrade["id"], package_count, last_package_size)).encode('ascii') elif function == 1: # match get_cmd_pattern index, upgrade_id = match.groups() dbg("get") # 查找文件 upgrade = dbsession.execute(text("select * from device_framework_upgrade.single_upgrade where id = :id and status = 1"), {"id":int(upgrade_id)}).fetchone() file_path = upgrade['file_path'] dbg("file is %s" % file_path) # 读取文件数据 fd = open(file_path, 'rb') fd.seek((int(index) - 1) * PACKAGE_SIZE) reply = bytearray([int(int(index) / 256), int(index) % 256]) + fd.read(PACKAGE_SIZE) else: reply = "unknown function".encode('ascii') except: dbg("error") print_exception_info() reply = "error all".encode('ascii') finally: # 回复数据给设备 self.socket.sendto(reply, address) if dbsession: dbsession.close() if __name__ == '__main__': dbg('device_upgrade_server.py receiving datagrams on %s:%d' % ('', 2234)) try: # 启动udp服务器 SingleUpgradeServer('%s:%d' % ('', 2234)).serve_forever() except: print_exception_info() ```
发表于 2018-10-15 17:20
阅读 ( 8977 )
分类:
默认分类
9 推荐
打赏
收藏
你可能感兴趣的文章
非官方的自建OTA升级平台(Java版),免费,开源
7799 浏览
详解Air202/Air80x的Luat升级流程
6506 浏览
相关问题
远程升级文件下载成功,但是重启后报错,远程升级 失败,依旧运行老固件(新老固件只差个版本号)
1 回答
1 条评论
请先
登录
后评论
技术销售Wendal
软件工程师
15 篇文章
作家榜
»
技术销售Delectate
43 文章
陈夏
26 文章
国梁
24 文章
miuser
21 文章
晨旭
20 文章
朱天华
19 文章
金艺
19 文章
杨奉武
18 文章
×
发送私信
发给:
内容:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!