单片机GD32F303RCT6 (Macos环境)开发 (二十九)—— GD32通过蓝牙透传模块 IAP升级

news2025/4/16 8:17:56

GD32通过蓝牙透传模块 IAP升级

1、思路

上一节手机App可以通过HC-08模块控制mcu的开锁,关锁的动作,那么我们是不是可以将mcu的升级文件通过hc-08模块发送给gd32,完成gd32程序的自升级呢?

2、命令协议

蓝牙透传模块每次只能发20字节的数据,去掉头跟checksum四个字节,实际上每包的数据为16个字节。

/*串口升级协议如下:
    cmd + data length + data0 + ....+ data_n + checksum
    1、获取版本号             0x01 0x02 0x00 0x00 checksum
    2、升级
        1、进入升级模式        0x02 0x02 0x00 0x00 checksum
        2、升级文件大小        0x03 0x04 0x00 0x00 0x00 0x00 checksum
        3、数据包发送          0x04 0x10 0x00 0x00 0x00 0x00 .....  checksum
        4、数据包发送完成       0x05 0x02 0x00 0x00 checksum
    3、开锁
        0x06 0x02 0x00 0x00 checksum
    4、关门
        0x07 0x02 0x00 0x00 checksum
*/

3、App的开发以及gd32代码添加

1、在上一节的基础上增加UpdateCommand.java文件,
对所有发送命令进行一个封装。

2、重新封装开锁函数
App代码:

public void openLockCmd(BluetoothLeService bluetoothLeService){
    byte [] lockOn = {0x06,0x02,0x00,0x00,0x00,0x00};
    int crc = UpdateCommand.getInstance().getCRC(lockOn);
    lockOn[lockOn.length-1] = (byte)(crc >> 8);
    lockOn[lockOn.length-2] = (byte)(crc & (0xff));
    bluetoothLeService.writeSpecialCharacteristic(lockOn);
}

GD32代码:
在这里插入图片描述
3、重新封装关门代码:
App代码

public void closeLockCmd(BluetoothLeService bluetoothLeService){
    byte [] lockOff = {0x07,0x02,0x00,0x00,0x00,0x00};
    int crc = UpdateCommand.getInstance().getCRC(lockOff);
    lockOff[lockOff.length-1] = (byte)(crc >> 8);
    lockOff[lockOff.length-2] = (byte)(crc & (0xff));
    bluetoothLeService.writeSpecialCharacteristic(lockOff);
}

GD32:
在这里插入图片描述
4、App代码升级文件发送封装:
a、根据协议,先发送进入升级模式命令:
代码如下:

public void uartSendUpgradeCmd(BluetoothLeService bluetoothLeService){

    byte[] upgradeCmd = new byte[]{0x02,0x02,0x00,0x00,(byte)0xa0,0x5c};
    bluetoothLeService.writeSpecialCharacteristic(upgradeCmd);
}

b、获取文件大小以及发送文件大小命令:
我们将gd32的升级文件打包内apk内,直接二进制读取文件,按协议分发数据。实际这里可换成ota服务器,从ota服务器上抓取升级文件,然后分包发给gd32。
获取文件大小函数如下:

private int getUpgradeFileLength(Context context)
{
    InputStream inputStream;
    int fileSize;
    AssetManager assetManager = context.getAssets();
    try {
        inputStream = assetManager.open("Application.bin");
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
    try {
        fileSize = inputStream.available();
        inputStream.close();
        return fileSize;
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

public void uartSendFileSize(Context context,BluetoothLeService bluetoothLeService){

    int fileSize = UpdateCommand.getInstance().getUpgradeFileLength(context);
    byte [] fileSizeCmd = new byte[]{0x03,0x04,0x00,0x00,0x00,0x00,(byte)0xc2,0x42};

    fileSizeCmd[2] = (byte)((fileSize >> 24) & 0xff);
    fileSizeCmd[3] = (byte)((fileSize >> 16) & 0xff);
    fileSizeCmd[4] = (byte)((fileSize >> 8) & 0xff);
    fileSizeCmd[5] = (byte)((fileSize) & 0xff);

    int crcSize = UpdateCommand.getInstance().getCRC(fileSizeCmd);
    fileSizeCmd[fileSizeCmd.length-1] = (byte)(crcSize >> 8);
    fileSizeCmd[fileSizeCmd.length-2] = (byte)(crcSize & (0xff));

    bluetoothLeService.writeSpecialCharacteristic(fileSizeCmd);
}

c、分包发送数据函数:

public void uartSendFile(Context context,BluetoothLeService bluetoothLeService){

    int fileSize = UpdateCommand.getInstance().getUpgradeFileLength(context);
    wTimer = 0;
    AssetManager assetManager = context.getAssets();
    if (fileSize % EVERY_PACKAGE_DATA_LENGTH == 0){

        filePackages = fileSize / EVERY_PACKAGE_DATA_LENGTH;
    }else{
        filePackages = fileSize / EVERY_PACKAGE_DATA_LENGTH + 1;
    }
    InputStream inputStream = null;
    try {
        inputStream = assetManager.open("Application.bin");
        byte buffer[] = new byte[EVERY_PACKAGE_DATA_LENGTH];
        byte buffer2[] = new byte[EVERY_PACKAGE_DATA_LENGTH + 4];

        int len = 0;
        while ((len = inputStream.read(buffer,0,buffer.length))>0) {
            //buffer为读出来的二进制数据,长度1024,最后一段数据小于1024
            if (len != EVERY_PACKAGE_DATA_LENGTH){
                for (int i=len;i<EVERY_PACKAGE_DATA_LENGTH;i++){
                    buffer[i] = (byte) 0xff;
                }
            }

            System.arraycopy(buffer,0,buffer2,2,buffer.length);
            buffer2[0] = 0x04;
            buffer2[1] = (byte)0x80;
            int crc = UpdateCommand.getInstance().getCRC(buffer2);
            buffer2[buffer2.length-1] = (byte)(crc >> 8);
            buffer2[buffer2.length-2] = (byte)(crc & (0xff));
            LogUtil.d(TAG,"need send data " + ChangeTool.getInstance().byte2HexStr(buffer2));
            bluetoothLeService.writeSpecialCharacteristic(buffer2);
            Thread.sleep(20);
            wTimer++;
            if(this.mOnReceiveUartUpgradeListener != null)
                mOnReceiveUartUpgradeListener.OnReceiveUartUpgradeProcess((int)((wTimer * 100)/filePackages));
        }
    } catch (IOException e) {
        e.printStackTrace();
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    } finally {
        if (inputStream!=null) {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

d、发送升级完成命令:

public void uartUpgradeCompleted(BluetoothLeService bluetoothLeService) {
    byte[] complete_cmd = new byte[]{0x05,0x02,0x00,0x00,(byte)0xa0,0x5c};
    int crc = UpdateCommand.getInstance().getCRC(complete_cmd);
    complete_cmd[complete_cmd.length-1] = (byte)(crc >> 8);
    complete_cmd[complete_cmd.length-2] = (byte)(crc & (0xff));
    bluetoothLeService.writeSpecialCharacteristic(complete_cmd);
}

e、获取版本号函数:

public void uartGetMcuVersion(BluetoothLeService bluetoothLeService)
{
    byte[] versionCmd = new byte[]{0x01,0x02,0x00,0x00,(byte)0xa0,0x18};
    bluetoothLeService.writeSpecialCharacteristic(versionCmd);
}

Gd32相关函数添加:

a、获取版本号:

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

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

相关文章

Selenium的使用

一、基础 1、特点 selenium 是web中基于UI的自动化测试工具&#xff0c;它支持多平台、多语言、多浏览器&#xff0c;还有丰富的API。 2、原理 自动化脚本代码会创建一个http请求发送给浏览器驱动进行解析&#xff0c;浏览器驱动会操控浏览器执行测试&#xff0c;浏览器接着…

AirServer电脑通用版下载及使用教程

AirServer 是一款功能十分强大的投屏软件&#xff0c;支持并适用于 Windows和Mac。AirServer 是接收方&#xff0c;而不是发送方。 AirServer 只允许您接收镜像或流媒体内容&#xff0c;反之则不行。AirServer虽然功能十分强大&#xff0c;但是整体操作和使用都十分简单&#x…

如何在华为OD机试中获得满分?Java实现【知识图谱新词挖掘1】一文详解!

✅创作者&#xff1a;陈书予 &#x1f389;个人主页&#xff1a;陈书予的个人主页 &#x1f341;陈书予的个人社区&#xff0c;欢迎你的加入: 陈书予的社区 &#x1f31f;专栏地址: Java华为OD机试真题&#xff08;2022&2023) 文章目录 1. 题目描述2. 输入描述3. 输出描述…

【STL】list的模拟实现

目录 前言 结构解析 默认成员函数 构造函数 拷贝构造 赋值重载 析构函数 迭代器 const迭代器 数据修改 insert erase 尾插尾删头插头删 容量查询 源码 前言 &#x1f349;list之所以摆脱了单链表尾插麻烦&#xff0c;只能单向访问等缺点&#xff0c;正是因为其…

日常 - HttpURLConnection 网络请求 TLS 1.2

文章目录 环境前言HTTPS 请求流程服务端支持JDK 验证资源 环境 JDK 8 Hutool 4.5.1 前言 应供应商 DD 的 TLS 版本升级通知&#xff0c;企业版接口升级后 TLS 1.0 及 1.1 版本请求将无法连接&#xff0c;仅支持 TLS 1.2 及以上版本的客户端发起请求。 当前项目使用 Hutool …

有序表2:跳表

跳表是一个随机化的数据结构&#xff0c;可以被看做二叉树的一个变种&#xff0c;它在性能上和红黑树&#xff0c;AVL树不相上下&#xff0c;但是跳表的原理非常简单&#xff0c;目前在Redis和LeveIDB中都有用到。 它采用随机技术决定链表中哪些节点应增加向前指针以及在该节点…

找不到“SqlServer”模块-- 在此计算机上找不到任何 SQL Server cmdlet。

https://github.com/PowerShell/PowerShell/releases/tag/v7.2.2SQL Server Management Studio 18 启动触发器报错 标题: 找不到“SqlServer”模块 --------------- 在此计算机上找不到任何 SQL Server cmdlet。 在 https://powershellgallery.com/packages/SqlServer 上获取“…

PyTorch深度学习实战(1)——神经网络与模型训练过程详解

PyTorch深度学习实战&#xff08;1&#xff09;——神经网络与模型训练过程详解 0. 前言1. 传统机器学习与人工智能2. 人工神经网络基础2.1 人工神经网络组成2.2 神经网络的训练 3. 前向传播3.1 计算隐藏层值3.2 执行非线性激活3.3 计算输出层值3.4 计算损失值3.5 实现前向传播…

Linux——应用层之序列号与反序列化

TCP协议通讯流程 tcp是面向连接的通信协议,在通信之前,需要进行3次握手,来进行连接的建立。 当tcp在断开连接的时候,需要释放连接,4次挥手 服务器初始化: 调用socket, 创建文件描述符; 调用bind, 将当前的文件描述符和ip/port绑定在一起; 如果这个端口已经被其他进程占用了…

【机器学习】9种回归算法及实例总结,建议学习收藏

我相信很多人跟我一样&#xff0c;学习机器学习和数据科学的第一个算法是线性回归&#xff0c;它简单易懂。由于其功能有限&#xff0c;它不太可能成为工作中的最佳选择。大多数情况下&#xff0c;线性回归被用作基线模型来评估和比较研究中的新方法。 在处理实际问题时&#…

VirtualBox安装增强功能

在刚安装完的VisualBox中&#xff0c;默认屏幕是固定设置的&#xff0c;不会根据实际的窗口大小做自适应&#xff0c;这时候我们需要【安装增强功能】&#xff0c;然后打开【自动调整显示大小】&#xff0c;就可以实现虚拟机中屏幕自适应。 本教程的软件环境如下&#xff1a; 宿…

数据结构: 第四章 串

文章目录 一、串的定义和实现1.1串的定义和基本操作1.1.1串的定义1.1.2串的基本操作1.1.3小结 1.2串的存储结构1.2.1顺序存储1.2.2链式存储1.2.3基于顺序存储实现基本操作1.2.4小结 二、串的模式匹配2.1什么是字符串的模式匹配2.2朴素模式匹配算法2.3KMP算法2.4求next数组2.5KM…

python+django协同过滤算法的美食O2O外卖点餐系统vue

当然使用的数据库是mysql。尽管没有面向对象的数据库的作用强大&#xff0c;但是在Python开发上还是比较的灵活和方便的。系统功能主要介绍以下几点&#xff1a; 本外卖点餐系统主要包括二大功能模块&#xff0c;即用户功能模块和管理员功能模块。 &#xff08;1&#xff09;管…

Linux上安装jdk Tomcat mysql redis

1.安装JDk 1.1这里使用xshell中xfxp进行文件的上传&#xff0c;将jdk二进制包上传到Linux服务器上 下载地址&#xff1a;Java Downloads | Oracle 或者这里有下载好的安装包&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1ZSJxBDzDaTwCH2IG-d2Gig 提取码&#xff1a;…

dubbo 3.2.0 consumer bean初始化及服务发现简记

consumer bean初始化 以spring 如下配置<dubbo:reference id"versionConsumerBean" interface"org.apache.dubbo.samples.version.api.VersionService" version"*"/>为例&#xff0c;先使用spring 的初始化&#xff0c;核心代码 try {fin…

EDR(端点、端点检测与响应中心、可视化展现)

EDR基本原理与框架 EDR定义 端点检测和响应是一种主动式端点安全解决方案&#xff0c;通过记录终端与网络事件&#xff08;例如用户&#xff0c;文件&#xff0c;进程&#xff0c;注册表&#xff0c;内存和网络事件&#xff09;&#xff0c;并将这些信息本地存储在端点或集中数…

C#,码海拾贝(26)——求解“一般带状线性方程组banded linear equations”之C#源代码,《C#数值计算算法编程》源代码升级改进版

using System; namespace Zhou.CSharp.Algorithm { /// <summary> /// 求解线性方程组的类 LEquations /// 原作 周长发 /// 改编 深度混淆 /// </summary> public static partial class LEquations { /// <summary> /…

Redis五大基本数据结构(原理)

一、 Redis数据结构-String String是Redis中最常见的数据存储类型&#xff1a; 其基本编码方式是RAW&#xff0c;基于简单动态字符串&#xff08;SDS&#xff09;实现&#xff0c;存储上限为512mb。 如果存储的SDS长度小于44字节&#xff0c;则会采用EMBSTR编码&#xff0c;…

c++ 11标准模板(STL) std::map(六)

定义于头文件<map> template< class Key, class T, class Compare std::less<Key>, class Allocator std::allocator<std::pair<const Key, T> > > class map;(1)namespace pmr { template <class Key, class T, clas…

优化器| SGD/SGD-m/SGD-NAG/Adagrad/Adadelta/RMSProp/Adam/Nadam/Adamax

前言&#xff1a;最近准备复习一下深度学习的基础知识&#xff0c;开个专栏记录自己的学习笔记 各种SGD和Adam优化器整理 基本概念 优化&#xff1a;最大化或最小化目标函数&#xff0c;具体指最小化代价函数或损失函数 损失函数 J(θ)f(hθ(x)&#xff0c;y)&#xff0c;h…