用Flutter实现GaiaControl BLE OTA升级功能,支持Android/IOS

news2024/11/15 9:48:44

代码基本移植官方GaiaControl Demo。
支持RWCP 断点续传 设置蓝牙mtu.协议。这里主要分析GAIA CSR ble ota的过程,协议等等,希望对你有所帮助。这里对蓝牙服务特性订阅都不谈。读者自行了解。

Gaia 是CSR 制定的一个上层使用协议,其在BR/EDR 基于RFCOMM,可以理解为一个特别UUID的SPP;经典蓝牙使用的UUID 0X1107。 ios只支持BLE所以重点关注SPP升级UUID 0X1101
Gaia 的UUID是00001107-D102-11E1-9B23-00025B00A5A5;
SPP 的UUID是00001101-0000-1000-8000-00805F9B34FB;
主要UUID如下
在这里插入图片描述

//升级服务UUID
Uuid otaUUID = Uuid.parse("00001100-d102-11e1-9b23-00025b00a5a5");
//升级服务订阅的特性
Uuid notifyUUID = Uuid.parse("00001102-d102-11e1-9b23-00025b00a5a5");
//升级服务写入特性
Uuid writeUUID = Uuid.parse("00001101-d102-11e1-9b23-00025b00a5a5");
//RWCP 更快的传输特性 不需要回包
Uuid writeNoResUUID = Uuid.parse("00001103-d102-11e1-9b23-00025b00a5a5");

GAIA协议流程非常繁琐,我们从头开始分析。
首先订阅升级通知通道 00001102-d102-11e1-9b23-00025b00a5a5
向00001101-d102-11e1-9b23-00025b00a5a5写入指令 0x000A400112 表示订阅升级通知
这里的000A400112组包格式如下

000A 4001 12

其中0x000A为固定字符vendorId 0x4001两个字节表示commandId 10x2表示订阅升级通知
订阅成功UUID 0x1102返回

000AC0010012

此时就可以开始发送升级请求

  void sendUpgradeConnect() async {
    GaiaPacketBLE packet = GaiaPacketBLE(GAIA.COMMAND_VM_UPGRADE_CONNECT);
    writeMsg(packet.getBytes());
  }

即发送0x0640 指令

000A0640

收到回包000A864000表示请求成功此时开始准备文件MD5信息用于校验 取文件MD5末尾4个字节。命令如下

000A0642130004F891C66F

这里的组包格式 vendorId + commandId + 操作符+数据 其中数据包含 长度+真正数据

000A 0642 13 0004 F891C66F

F891C66F为文件MD5;

此时会收到两条数据包

000A0642010000 //升级开始
000A40031214000600F891C66F03 //MD5接收完成 并携带上次传输步骤信息 其中0x03 表示正在升级并且上次传输步骤为0x00 
 void receiveSyncCFM(VMUPacket? packet) {
    List<int> data = packet?.mData ?? [];
    if (data.length >= 6) {
      int step = data[0];
      addLog("上次传输步骤 step $step");
      if (step == ResumePoints.IN_PROGRESS) {
        setResumePoint(step);
      } else {
        mResumePoint = step;
      }
    } else {
      mResumePoint = ResumePoints.DATA_TRANSFER;
    }
    sendStartReq();
  }
 void sendStartReq() {
    VMUPacket packet = VMUPacket.get(OpCodes.UPGRADE_START_REQ);
    sendVMUPacket(packet, false);
  }

接下来就是组包发送数据

  void sendStartDataReq() {
    setResumePoint(ResumePoints.DATA_TRANSFER);
    VMUPacket packet = VMUPacket.get(OpCodes.UPGRADE_START_DATA_REQ);
    sendVMUPacket(packet, false);
  }
   000A0642150000
void receiveDataBytesREQ(VMUPacket? packet) {
    List<int> data = packet?.mData ?? [];

    // Checking the data has the good length
    if (data.length == OpCodes.DATA_LENGTH) {
      // retrieving information from the received packet
      //REC 120300080000002400000000
      //SEND 000A064204000D0000030000FFFF0001FFFF0002
      var lengthByte = [data[0], data[1], data[2], data[3]];
      var fileByte = [data[4], data[5], data[6], data[7]];
      mBytesToSend = int.parse(StringUtils.byteToHexString(lengthByte), radix: 16);
      int fileOffset = int.parse(StringUtils.byteToHexString(fileByte), radix: 16);

      addLog(StringUtils.byteToHexString(data) + "本次发包: $fileOffset $mBytesToSend");
      // we check the value for the offset
      mStartOffset += (fileOffset > 0 && fileOffset + mStartOffset < (mBytesFile?.length ?? 0)) ? fileOffset : 0;

      // if the asked length doesn't fit with possibilities we use the maximum length we can use.
      mBytesToSend = (mBytesToSend > 0) ? mBytesToSend : 0;
      // if the requested length will look for bytes out of the array we reduce it to the remaining length.
      int remainingLength = mBytesFile?.length ?? 0 - mStartOffset;
      mBytesToSend = (mBytesToSend < remainingLength) ? mBytesToSend : remainingLength;
      if (mIsRWCPEnabled.value) {
        while (mBytesToSend > 0) {
          sendNextDataPacket();
        }
      } else {
        addLog("receiveDataBytesREQ: sendNextDataPacket");
        sendNextDataPacket();
      }
    } else {
      addLog("UpgradeError 数据传输失败");
      abortUpgrade();
    }
  }

断点续传的逻辑也在这。读取到fileOffset 进行文件偏移

最后一包数据格式0x01 + 数据 发送完毕

void sendData(bool lastPacket, List<int> data) {
    List<int> dataToSend = [];
    dataToSend.add(lastPacket ? 0x01 : 0x00);
    dataToSend.addAll(data);
    sendPkgCount++;
    VMUPacket packet = VMUPacket.get(OpCodes.UPGRADE_DATA, data: dataToSend);
    sendVMUPacket(packet, true);
  }

收到是否提交 我这里直接不询问提交

 static const UPGRADE_COMMIT_REQ = 0x0F;
 void askForConfirmation(int type) {
    int code = -1;
    switch (type) {
      case ConfirmationType.COMMIT:
        {
          code = OpCodes.UPGRADE_COMMIT_CFM;
        }
        break;
      case ConfirmationType.IN_PROGRESS:
        {
          code = OpCodes.UPGRADE_IN_PROGRESS_RES;
        }
        break;
      case ConfirmationType.TRANSFER_COMPLETE:
        {
          code = OpCodes.UPGRADE_TRANSFER_COMPLETE_RES;
        }
        break;
      case ConfirmationType.BATTERY_LOW_ON_DEVICE:
        {
          sendSyncReq();
        }
        return;
      case ConfirmationType.WARNING_FILE_IS_DIFFERENT:
        {
          stopUpgrade();
        }
        return;
    }
    addLog("askForConfirmation ConfirmationType type $type $code");
    VMUPacket packet = VMUPacket.get(code, data: [0]);
    sendVMUPacket(packet, false);
  }

提交完成蓝牙会自动断开 ,间隔1S后重连 重新测试通知 再次发送升级请求跟文件md5 。如果数据正常 会收到是否安装的指令

 static const int COMMIT = 0x04

关于RWCP传输是基于以往0x1103中写入数据不等待返回通知,能大幅提高传输速率。

其他读者看代码理解吧

在这里插入图片描述
在这里插入图片描述

不懂得欢迎交流探讨
https://github.com/Liberations/Flutter-GAIAControl

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/188657.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Swagger的使用

一、概述 RestFul Api文档在线自动生成工具 >Api文档与API定义同步更新直接运行&#xff0c;可以在线测试API接口支持多种语言:&#xff08;Java,Php&#xff09; 官网&#xff1a;https://swagger.io/ 二、使用 在项目中使用Swagger需要springfox&#xff1b; Swagger…

Axure 原型中的迭代设计

​“老师&#xff0c;什么是产品迭代&#xff1f;” “老师。产品迭代和平常的产品设计有什么不同吗&#xff1f;” “老师&#xff0c;产品迭代原型可以怎么做&#xff1f;需要全部重新绘制吗&#xff1f;” 在和小伙伴们的日常交流中&#xff0c;不乏对产品迭代存在疑惑的朋…

2023年,数据人谨记把握好这“四不要”和“四要”

2023年已经开启&#xff0c;这一年对任何组织数据治理的工作都是非常重要的&#xff0c;那么我们如何更好的掌握数据治理和应对数据治理项目就成为重中之重&#xff0c;下面就和大家谈谈数据治理学习和项目的一些心得体会&#xff0c;供大家参考。 不要相互割裂&#xff0c;要融…

VS Code配置snippets代码片段快速生成html模板,提高前端编写效率

先看下示例&#xff0c;在输入 ! 号回车后自动生成一段代码片段。 这样我们就可以更便捷的进行代码编写了。 配置方法如下&#xff1a; 然后找到对应的文件进行配置&#xff0c;例如 html.json&#xff0c;编写 .html 扩展名文件时就能触发。 我这选的 html.json 进行的配置…

CSS选择器整理学习(下)

书接上回&#xff0c;在前端项目开发中&#xff0c;有时候需要对特殊的元素进行特殊的处理&#xff0c;但有时候元素的位置不确定、层级不确定、数量不确定等问题&#xff0c;导致我们没办法进行元素的选择&#xff0c;这个时候我们就需要用到元素选择器了。 一、css选择器 1…

ESP-IDF:懒汉式和饿汉式单例模式测试代码

ESP-IDF:懒汉式和饿汉式单例模式测试代码 /单例模式测试/ /懒汉式/ class singleton_lazy { private: singleton_lazy(){}; public: static singleton_lazy *getinstance(){ if (pSingleton NULL){ pSingleton new singleton_lazy; } return pSingleton; } private: stati…

美摄云剪辑3.0:融合AIGC,实现效率提升

云剪辑3.0亮点速览 数字人视频一键生成本地素材剪辑无需等待上传支持Lottie动画导入01 模板化包装&#xff0c;一键生成数字人视频 伴随元宇宙技术的发展和产业数字化升级&#xff0c;数字人正在成为下一代互联网的基础设施。与此同时&#xff0c;AI的应用场景也愈加丰富。人…

网站被劫持的解决方案

网站被劫持怎么解决?用户打开网站就发现网站被篡改不是自己原来的页面&#xff0c;而是被变成被劫持的页面&#xff0c;以下几种情况以及对应的处理方法&#xff0c;一起来看看吧。 一、DNS劫持 DNS劫持是一种恶意攻击&#xff0c;其中&#xff0c;个人通过覆盖计算机的传输控…

TCP/IP详解与实例分析

TCP/IP详解 TCP/IP并不是一个具体的协议&#xff0c;而是指一个由FTP、SMTP、TCP、UDP、IP等协议构成的协议簇&#xff0c;只是因为在TCP/IP协议中TCP协议和IP协议最具代表性&#xff0c;所以被称为TCP/IP协议。 TCP/IP协议在一定程度上参考了OSI的体系结构&#xff0c;在TCP/…

前端面试题2023含答案 前端必备知识点 混淆 刷题 查漏补缺【持续更新中】

目录1. vue双向数据绑定&#xff08;响应式&#xff09;原理2. HTML 语义化&#xff08;语义化标签&#xff09;3. 标签上 title 与 alt 属性4. CSS单位&#xff1a;1px、1em、1rem、1vh / 1vw 的含义5. 网页前端性能优化的方式6. HTTP常见的状态码7. Vuex是什么&#xff08;有…

基于卡尔曼滤波器的PID控制-2

采用Simulink进行仿真&#xff01;&#xff01;Kalman算法由M函数实现。控制干扰信号wk)和测量噪声信号v(k)幅值均为0.10的白噪声信号&#xff0c;输入信号幅值为1.0、频率为0.5Hz 的正弦信号。采用卡尔曼滤波器实现信号的滤波,取O1&#xff0c;R1。仿真结果如图1和2所示。图1 …

PyQt5编程基础 2.1 GUI程序的基本框架

文章目录 创建纯代码GUI程序 创建目录 新建程序 创建GUI程序的基本过程(代码分析) 导入模块 创建应用程序 创建窗体 使用窗体类的GUI程序框架 创建项目目录 窗体设计 修改窗体的windowTitle 放一个label 放一个Push Button 保存窗体 代码设计 将QtApp中的ui文…

【自学Docker】Docker update命令

Docker update命令 大纲 docker update命令教程 docker update 命令可以用于更新一个或多个 Docker容器 的配置。该命令后面的 CONTAINER 可以是容器Id&#xff0c;或者是容器名。 docker update命令语法 haicoder(www.haicoder.net)# docker update [OPTIONS] CONTAINER […

网络编程套接字之UDP实现回显服务器及客户端

目录 前言&#xff1a; 基础理解 传输层协议 UDP TCP Socket API DatagramSocket API DatagramPacket API UDP实现回显服务器 完整代码展现&#xff08;有详细注释&#xff09; UDP实现回显客户端 完整代码展现&#xff08;有详细注释&#xff09; 小结&#xff1…

使用OpenCV对点集从左上到右下排序

本文实现如何使用OpenCV对点集从左上到右下排序。本文使用的案例图片如下&#xff1a; 需要实现的效果如下&#xff1a; 1.分阶段实现 让我们看看考虑4行的工作流。我考虑的是沿着y轴将图像分成4段&#xff0c;形成4行。对于图像的每一段&#xff0c;找出每一个以该段为中心…

虚拟主机怎么用?香港虚拟主机搭建网站教程

在了解使用方法之前要先明白什么是虚拟主机、它的用处以及服务器、VPS和虚拟主机之间有什么区别。香港虚拟主机也称为网站空间&#xff0c;顾名思义就是存放网站文件的空间。主要用于搭建网站、提供程序运行。下面就介绍一下香港虚拟主机搭建网站的使用方法以及教程。 一、香港…

职场新人应该如何培养项目管理的能力?

李强在一家企业工作&#xff0c;前阵子升任项目经理。但他这两天却一副愁眉不展的样子&#xff0c;像霜打的茄子。原来是因为他缺乏管理经验&#xff0c;设定的工作任务不合理&#xff0c;项目成员职责不清&#xff0c;导致项目混乱&#xff0c;项目进度不清晰。 项目管理涉及…

Redis搭建基于docker跨服务器的一主两从三哨兵集群模式

文章目录1 整体拓扑图2 redis与哨兵配置文件2.1 主节点配置文件2.1.1 主节点redis.conf配置文件2.1.2 主节点哨兵配置文件2.2 从节点配置文件2.1.1 从节点redis.conf配置文件2.1.2 从节点哨兵配置文件3 docke-compose编排文件4 启动并测试查看哨兵日志查看集群状态测试集群是否…

在 Visual Studio 中更好地进行搜索

当在 IDE 中查找特定的代码文件、类、方法或者功能、选项&#xff0c;找到它们并不总是那么容易。有多种搜索方式&#xff0c;到底该选择哪一种才最便捷呢&#xff1f;新的“一站式”搜索&#xff08;All-In-One Search&#xff09;将代码和功能搜索合并到同一个 UI 中&#xf…

从GPT到chatGPT(三):GPT3(一)

#GPT3 文章目录前言正文摘要介绍方法模型结构训练数据集训练过程评估小结前言 OpenAI在放出GPT2后&#xff0c;并没有引起业界太大的影响和关注&#xff0c;究其原因&#xff0c;并不是zero-shot这种想法不够吸引人&#xff0c;而是GPT2表现出来的效果依然差强人意&#xff0c…