前言
- 程序分为上位机部分、BootLoader、App程序
- 上位机程序使用的是C#进行开发,目前只做成控制台部分
- 开发环境依然选择Clion
- 芯片采用的是stm32f103vet6
- 升级模块已和驱动层逻辑进行分离
BootLoader程序
Flash分区定义
头文件
#ifndef STM32F103VET6_PROJECT_APP_FLASH_CONF_H
#define STM32F103VET6_PROJECT_APP_FLASH_CONF_H
#include "stm32f1xx_hal.h"
#define SIZE_8B (8)
#define SIZE_256B (256)
#define SIZE_512B (512)
#define SIZE_1K (1024)
#define SIZE_2K (2048)
#define SIZE_3K (3072)
#define SIZE_4K (4092)
#define SYS_CONVERT(type, val) ((type)(val))
// 引导程序大小: 10KB
#define BOOT_AREA_SIZE (SIZE_1K*10)
#define BOOT_START_ADDR FLASH_BASE
// APP: 150KB (应用程序)
#define APP_AREA_SIZE (SIZE_1K*150)
#define APP_START_ADDR SYS_CONVERT(uint32_t,BOOT_START_ADDR+BOOT_AREA_SIZE)
// APP UPGRADE: 150KB (应用升级)
#define APP_UPGRADE_AREA_SIZE (SIZE_1K*150)
#define APP_UPGRADE_START_ADDR SYS_CONVERT(uint32_t,APP_START_ADDR+APP_AREA_SIZE)
// 配置区域: 50KB
#define CONF_AREA_SIZE (SIZE_1K*50)
#define CONF_START_ADDR SYS_CONVERT(uint32_t,APP_UPGRADE_START_ADDR+APP_UPGRADE_AREA_SIZE)
// 数据区域: 130KB
#define DATA_AREA_SIZE (SIZE_1K*130)
#define DATA_START_ADDR SYS_CONVERT(uint32_t,CONF_START_ADDR+CONF_AREA_SIZE)
// 引导数据配置信息:
#define BOOT_INF_ADDR SYS_CONVERT(uint32_t,DATA_START_ADDR+DATA_AREA_SIZE)
#endif //STM32F103VET6_PROJECT_APP_FLASH_CONF_H
固件信息读取
注意: 此处操作Flash驱动参考
头文件
#ifndef STM32F103VET6_PROJECT_OTA_BOOT_INFO_H
#define STM32F103VET6_PROJECT_OTA_BOOT_INFO_H
#include "stm32f1xx_hal.h"
// 定义升级标志位,确保唯一性
#define OTA_UPGRADE_FLAG (0x1389)
typedef struct {
uint8_t app_version[6]; // 应用版本
uint32_t app_new_version_flag; // 应用新版本标志位
uint32_t app_upgrade_data_size; // 应用升级数据大小
} APP_Info_t;
void Load_App_Info(APP_Info_t *info);
void Update_App_Info(APP_Info_t *info);
void Jump_To_App(uint32_t run_addr);
void Jump_To_BootLoader(void);
#endif //STM32F103VET6_PROJECT_OTA_BOOT_INFO_H
源文件
#include "ota_boot_info.h"
#include "bsp_flash.h"
#include "app_flash_conf.h"
typedef void (*pFunction)(void);
/**
* 加载APP信息
* @param dst
*/
void Load_App_Info(APP_Info_t *info) {
STMFLASH_Read_Base(BOOT_INF_ADDR, info, sizeof(APP_Info_t));
}
/**
* 更新APP信息
* @param dst
*/
void Update_App_Info(APP_Info_t *info) {
STMFLASH_Write_Base(BOOT_INF_ADDR, info, sizeof(APP_Info_t));
}
void Jump_To_BootLoader(void) {
__disable_irq();
uint32_t JumpAddress;
pFunction Jump_To_Application;
JumpAddress = *(volatile uint32_t *) (0x00000004);
Jump_To_Application = (pFunction) JumpAddress;
__set_MSP(*(volatile uint32_t *) JumpAddress);
__set_PSP(*(volatile uint32_t *) JumpAddress);
Jump_To_Application();
}
void Jump_To_App(uint32_t run_addr) {
__disable_irq();
pFunction jump_app;
jump_app = (pFunction) (*(volatile uint32_t *) (run_addr + 0x04));
__set_MSP(*(volatile uint32_t *) run_addr);
jump_app();
}
主入口方法逻辑
int main(void) {
/* USER CODE BEGIN 1 */
APP_Info_t app_info;
Load_App_Info(&app_info);
if (app_info.app_new_version_flag == OTA_UPGRADE_FLAG) {
app_info.app_new_version_flag = 0x0;
STMFLASH_Data_Copy(APP_UPGRADE_START_ADDR, APP_START_ADDR, app_info.app_upgrade_data_size);
Update_App_Info(&app_info);
}
Jump_To_App(APP_START_ADDR);
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1) {
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
链接文件中对FLASH 限制大小(10KB)
App程序
远程升级协议
头文件(upgrade_core.h)
#ifndef STM32_LIB_UPGRADE_CORE_H
#define STM32_LIB_UPGRADE_CORE_H
#include "sys_core.h"
#define OTA_UPGRADE_VERSION_LIN (6) /** @brief 升级版本长度*/
typedef struct {
uint8_t app_version[OTA_UPGRADE_VERSION_LIN]; // 应用版本
uint32_t app_new_version_flag; // 应用新版本标志位
uint32_t app_upgrade_data_size; // 应用升级数据大小
} app_info_t;
/**
* @brief 升级数据处理
* @param data 接收到的数据
* @param len 数据长度
*/
void device_upgrade_handle(u8 *data, u16 len);
extern void upgrade_resp(u8 status_code);
/**
* 获取当前设备类型
* @return 设备类型
*/
extern u8 get_cur_device_type(void);
/**
* 获取当前版本(长度固定为6)
* @return
*/
extern u8 *get_cur_version(void);
/**
* 获取升级写入地址
* @return
*/
extern u32 get_upgrade_write_addr(void);
/**
* 写入bin 数据
* @param addr
* @param data
* @param len
*/
extern void upgrade_write_bin(u32 addr, u8 *data, u16 len);
/**
* 写入app 更新信息
* @param data
*/
extern void upgrade_write_app_info(app_info_t *data);
/**
*
* @return 获取升级数据帧大小
*/
extern u32 get_upgrade_data_size(void);
#endif //STM32_LIB_UPGRADE_CORE_H
源文件(upgrade_core.c)
#include "upgrade_core.h"
#define upgrade_assert(condition, dst, resp_code) \
do{\
if (!(condition)) {\
dst = resp_code;\
goto end;\
}}while(0)
#define upgrade_assert_return(condition, resp_code)\
do{ \
if(!(condition)){ \
return resp_code; \
} \
}while(0)
typedef enum {
UPGRADE_START_PACKET, /**@brief 开始包*/
UPGRADE_DATA_PACKET, /**@brief 数据包*/
UPGRADE_END_PACKET, /**@brief 结束包*/
} packet_type;
typedef enum {
status_ok,/**@brief 正确*/
packet_type_error,/**@brief 包类型错误*/
param_error,/**@brief 参数错误*/
data_len_error,/**@brief 数据长度错误*/
device_type_error,/**@brief 设备类型错误*/
upgrade_version_low, /**@brief 升级版本过低*/
upgrade_packet_no_error,/**@brief 包号错误*/
upgrade_data_not_complete, /** 升级数据不完整*/
start_request_not_send, /**@brief 未发送开始请求*/
} resp_status_code;
typedef struct {
u8 device_type;/** @brief 设备类型*/
u8 upgrade_version[OTA_UPGRADE_VERSION_LIN]; /** @brief 升级版本*/
u32 upgrade_file_size; /** @brief 升级文件大小 */
} upgrade_info_t; /** @brief 升级帧信息*/
typedef struct {
u8 current_packet_no; /** @brief 当前包号*/
u8 total_packet_num; /** @brief 总包数*/
u8 *upgrade_data; /** @brief 升级数据*/
u16 upgrade_data_size; /** @brief 升级数据大小(字节数)*/
} upgrade_frame_t; /** @brief 升级帧结构体*/
typedef struct {
u8 start_upgrade_flag; /** @brief 开始升级标志位*/
packet_type pkt_type;/** @brief 包类型*/
upgrade_info_t base_info; /** @brief 基础信息*/
upgrade_frame_t upgrade_info; /** @brief 升级信息*/
} upgrade_context_t;
static u8 cur_packet_no = 1; // 当前升级包号
static __IO u32 rec_upgrade_data_size = 0;
static app_info_t app_info;
static upgrade_context_t context; /** @brief 升级上下文*/
/**
* 数据解析
* @param data
* @param len
* @return
*/
static resp_status_code upgrade_data_parse_handle(u8 *data, u16 len);
/**
* 数据处理
* @param ctx
* @return
*/
static resp_status_code upgrade_core_exec(upgrade_context_t *ctx);
// 版本比较
static int version_cmp(const u8 *v1, const u8 *v2);
// 数据转换
static inline u32 u8_to_u32(u8 *data);
void device_upgrade_handle(u8 *data, u16 len) {
resp_status_code tmp_status;
resp_status_code resp_status = status_ok;
// 验证参数是否为空
upgrade_assert(data != NULL, resp_status, param_error);
// 数据解析处理
tmp_status = upgrade_data_parse_handle(data, len);
// 验证数据解析
upgrade_assert(resp_status == tmp_status, resp_status, tmp_status);
// 数据解析处理
resp_status = upgrade_core_exec(&context);
end:
upgrade_resp(resp_status);
}
static resp_status_code upgrade_data_parse_handle(u8 *data, u16 len) {
uint16_t idx = 0;
switch (data[idx]) {
case UPGRADE_START_PACKET: {
upgrade_assert_return(len == 12, data_len_error); // 验证数据长度
// 数据解析
context.pkt_type = data[idx++]; // 包类型
context.base_info.device_type = data[idx++];// 设备类型
context.base_info.upgrade_file_size = u8_to_u32(data + idx); // 升级文件大小
idx += 4;
memcpy(context.base_info.upgrade_version, data + idx, OTA_UPGRADE_VERSION_LIN);
break;
}
case UPGRADE_DATA_PACKET: {
upgrade_assert_return(context.start_upgrade_flag, start_request_not_send); // 是否已经初始化
upgrade_assert_return(len == get_upgrade_data_size() + 3, data_len_error); // 验证数据长度
context.pkt_type = data[idx++]; // 包类型
context.upgrade_info.total_packet_num = data[idx++]; // 总包数
context.upgrade_info.current_packet_no = data[idx++]; // 当前包号
context.upgrade_info.upgrade_data = data + idx; // 升级数据
context.upgrade_info.upgrade_data_size = len - idx; // 升级数据大小(数据长度-前缀长度)
break;
}
case UPGRADE_END_PACKET: {
upgrade_assert_return(context.start_upgrade_flag, start_request_not_send); // 是否已经初始化
upgrade_assert_return(len == 6, data_len_error); // 验证数据长度
context.pkt_type = data[idx++]; // 包类型
context.base_info.device_type = data[idx++];// 设备类型
context.base_info.upgrade_file_size = u8_to_u32(data + idx); // 升级文件大小
break;
}
default: {
return packet_type_error;
}
}
return status_ok;
}
/**
* 核心逻辑执行
* @param ctx
* @return
*/
static resp_status_code upgrade_core_exec(upgrade_context_t *ctx) {
switch (ctx->pkt_type) {
case UPGRADE_START_PACKET: {
// 解析之后校验
// 设备类型校验
upgrade_assert_return(get_cur_device_type() == ctx->base_info.device_type, device_type_error);
// 版本校验
upgrade_assert_return(version_cmp(ctx->base_info.upgrade_version, get_cur_version()) > 0,
upgrade_version_low);
// 开始更新标志位置位
ctx->start_upgrade_flag = true;
memset(&app_info, 0, sizeof(app_info_t));
app_info.app_upgrade_data_size = ctx->base_info.upgrade_file_size;
cur_packet_no = 1;
rec_upgrade_data_size = 0;
break;
}
case UPGRADE_DATA_PACKET: {
// 升级包序号与当前需要的包序号是否对应
upgrade_assert_return(cur_packet_no == ctx->upgrade_info.current_packet_no, upgrade_packet_no_error);
// 获取升级数据地址
u32 upgrade_data_addr = get_upgrade_write_addr();
upgrade_write_bin(rec_upgrade_data_size + upgrade_data_addr,
ctx->upgrade_info.upgrade_data,
ctx->upgrade_info.upgrade_data_size);
rec_upgrade_data_size = rec_upgrade_data_size + ctx->upgrade_info.upgrade_data_size;
cur_packet_no += 1;
break;
}
case UPGRADE_END_PACKET:
upgrade_assert_return(app_info.app_upgrade_data_size == context.base_info.upgrade_file_size,
upgrade_data_not_complete);
memcpy(app_info.app_version, ctx->base_info.upgrade_version, OTA_UPGRADE_VERSION_LIN);
app_info.app_new_version_flag = true;
app_info.app_upgrade_data_size = rec_upgrade_data_size;
upgrade_write_app_info(&app_info);
rec_upgrade_data_size = 0;
cur_packet_no = 1;
break;
}
return status_ok;
}
/**
*
* @param v1
* @param v2
* @return 0 v1=v2 1 v1>v2 -1 v1<v2
*/
static int version_cmp(const u8 *v1, const u8 *v2) {
for (int i = 0; i < OTA_UPGRADE_VERSION_LIN; ++i) {
if (v1[i] > v2[i]) {
return 1;
} else if (v1[i] < v2[i]) {
return -1;
}
}
return 0;
}
static inline u32 u8_to_u32(u8 *data) {
return (uint32_t) (data[0] << 24) + (uint32_t) (data[1] << 16) + (uint32_t) (data[2] << 8) + data[3];
}
升级协议接口实现
/*
© Copyright (c) [scl]。保留所有权利。
本文仅供个人学习和研究使用,禁止用于商业用途。
*/
#include "upgrade_core.h"
#include "app_gb_conf.h"
#include "bsp_flash.h"
#define OTA_UPGRADE_FLAG (0x1389)
u8 cur_version[] = {20, 23, 01, 01, 01, 01};
void upgrade_resp(u8 status_code) {
os_log(&status_code, 1);
}
/**
* 获取当前设备类型
* @return 设备类型
*/
u8 get_cur_device_type(void) {
return 1;
}
/**
* 获取当前版本(长度固定为6)
* @return
*/
u8 *get_cur_version(void) {
return cur_version;
}
/**
* 获取升级写入地址
* @return
*/
u32 get_upgrade_write_addr(void) {
return APP_UPGRADE_START_ADDR;
}
/**
* 写入bin 数据
* @param addr
* @param data
* @param len
*/
void upgrade_write_bin(u32 addr, u8 *data, u16 len) {
STMFLASH_Write_Base(addr, data, len);
}
/**
* 写入app 更新信息
* @param data
*/
void upgrade_write_app_info(app_info_t *data) {
data->app_new_version_flag = OTA_UPGRADE_FLAG;
STMFLASH_Write_Base(BOOT_INF_ADDR, data, sizeof(app_info_t));
}
u32 get_upgrade_data_size(void) {
return SIZE_1K;
}
测试(此处是模仿RT-Thread的写法)
/*
© Copyright (c) [scl]。保留所有权利。
本文仅供个人学习和研究使用,禁止用于商业用途。
*/
#include "app_gb_conf.h"
#include "upgrade_core.h"
static u8 dma_buf[SIZE_2K];
// 执行之前
static void invoke_before(void) {
// todo 网络配置加载
}
// 执行
static void invoke(void) {
com_init_set_baud(com1_cnf, 115200);
// dma_init_set_direction(com1_rx_dma_cnf, DMA_PERIPH_TO_MEMORY);
COM1_OpenIRQ(0, 0);
com1_init();
}
// 执行之后
static void invoke_after(void) {
HAL_UARTEx_ReceiveToIdle_IT(&com1_handle, dma_buf, SIZE_2K);
os_ps("app upgrade demo123231123\r\n");
}
void RxEventCallback_USART1(uint16_t size) {
device_upgrade_handle(dma_buf, size);
HAL_UARTEx_ReceiveToIdle_IT(&com1_handle, dma_buf, SIZE_2K);
}
void ErrorCallback_USART1() {
os_ps("ErrorCallback_USART1\r\n");
HAL_UARTEx_ReceiveToIdle_DMA(&com1_handle, dma_buf, SIZE_2K);
}
CMD_EXPORT(app_upgrade, invoke_before, invoke, invoke_after);
上位机程序(采用的C#)
OtaPacketHelper帮助类
using CommonLib.BaseConvert;
using CommonLib.Basic;
namespace ModbusConsole.Upgrade;
/// <summary>
/// 包帮助类
/// </summary>
public static class OtaPacketHelper
{
/// <summary>
/// 构建起始包请求
/// </summary>
/// <param name="deviceType"></param>
/// <param name="upgradePacketSize"></param>
/// <param name="version"></param>
/// <returns></returns>
public static byte[] BuildOtaStartPacketRequest(byte deviceType, uint upgradePacketSize, string version)
{
var versionData = CommonSoftBasicHelper.HexStringToBytes(version);
var upgradeByteNum = ByteTransConvertHelper.ConvertToByte(upgradePacketSize);
var request = new OtaPacket.OtaStartPacketRequest
{
DeviceType = deviceType,
PacketType = (byte)OtaPacket.OtaPacketTypeEnum.StartPacket,
};
unsafe
{
CommonUnsafeBasicHelper.ArrayToPointer(request.DeviceVersion, versionData,
OtaPacket.Define.UpgradePacketVersionNum);
CommonUnsafeBasicHelper.ArrayToPointer(request.UpgradeDataPacketSize, upgradeByteNum,
upgradeByteNum.Length);
var pktLen = sizeof(OtaPacket.OtaStartPacketRequest);
var dstBuf = new byte[pktLen];
if (CommonUnsafeBasicHelper.PointerToArray(request.buf, dstBuf, dstBuf.Length))
{
return dstBuf;
}
}
return Array.Empty<byte>();
}
/// <summary>
/// 构建结束包请求
/// </summary>
/// <param name="deviceType"></param>
/// <param name="upgradePacketSize"></param>
/// <returns></returns>
public static byte[] BuildOtaEndPacketRequest(byte deviceType, uint upgradePacketSize)
{
var upgradeByteNum = ByteTransConvertHelper.ConvertToByte(upgradePacketSize);
var request = new OtaPacket.OtaEndPacketRequest
{
DeviceType = deviceType,
PacketType = (byte)OtaPacket.OtaPacketTypeEnum.EndPacket,
};
unsafe
{
CommonUnsafeBasicHelper.ArrayToPointer(request.UpgradeDataPacketSize, upgradeByteNum,upgradeByteNum.Length);
var pktLen = sizeof(OtaPacket.OtaEndPacketRequest);
var dstBuf = new byte[pktLen];
if (CommonUnsafeBasicHelper.PointerToArray(request.buf, dstBuf, dstBuf.Length))
{
return dstBuf;
}
}
return Array.Empty<byte>();
}
/// <summary>
/// 构建数据包
/// </summary>
/// <param name="curPacketNo"></param>
/// <param name="totalPacketNo"></param>
/// <param name="data"></param>
/// <returns></returns>
public static byte[] BuildOtaDataPacketRequest(byte curPacketNo, byte totalPacketNo, byte[] data)
{
var request = new OtaPacket.OtaDataPacketRequest()
{
PacketType = (byte)OtaPacket.OtaPacketTypeEnum.DataPacket,
NowPackNum = curPacketNo,
TotalPackNum = totalPacketNo
};
unsafe
{
CommonUnsafeBasicHelper.ArrayToPointer(request.UpgradeData, data,
Math.Min(data.Length, OtaPacket.Define.UpgradeDataPacketNum));
var pktLen = sizeof(OtaPacket.OtaDataPacketRequest);
var dstBuf = new byte[pktLen];
if (CommonUnsafeBasicHelper.PointerToArray(request.buf, dstBuf, dstBuf.Length))
{
return dstBuf;
}
}
return Array.Empty<byte>();
}
}
OtaPacket包类
using System.Runtime.InteropServices;
namespace ModbusConsole.Upgrade;
/// <summary>
/// 远程升级包
/// </summary>
public class OtaPacket
{
public class Define
{
public const int UpgradeDataPacketNum = 1024;
public const int UpgradePacketVersionNum = 6;
public const int UpgradePacketSize = 4;
}
public enum OtaPacketTypeEnum : byte
{
StartPacket = 0x00,
DataPacket = 0x01,
EndPacket = 0x02,
}
/// <summary>
/// OTA 升级信息请求包
/// </summary>
[StructLayout(LayoutKind.Explicit, Size = Define.UpgradePacketVersionNum + Define.UpgradePacketSize + 2)]
public unsafe struct OtaStartPacketRequest
{
[FieldOffset(0)] public fixed byte buf[Define.UpgradePacketVersionNum + Define.UpgradePacketSize + 2];
/// <summary>
/// 包类型
/// </summary>
[FieldOffset(0)] public byte PacketType;
/// <summary>
/// 设备类型
/// </summary>
[FieldOffset(1)] public byte DeviceType;
/// <summary>
/// 升级数据包大小
/// </summary>
[FieldOffset(2)] public fixed byte UpgradeDataPacketSize[Define.UpgradePacketSize];
/// <summary>
/// 设备版本号
/// </summary>
[FieldOffset(2 + Define.UpgradePacketSize)]
public fixed byte DeviceVersion[Define.UpgradePacketVersionNum];
}
/// <summary>
/// OTA 升级结束包
/// </summary>
[StructLayout(LayoutKind.Explicit, Size = Define.UpgradePacketSize + 2)]
public unsafe struct OtaEndPacketRequest
{
[FieldOffset(0)] public fixed byte buf[ Define.UpgradePacketSize + 2];
/// <summary>
/// 包类型
/// </summary>
[FieldOffset(0)] public byte PacketType;
/// <summary>
/// 设备类型
/// </summary>
[FieldOffset(1)] public byte DeviceType;
/// <summary>
/// 升级数据包大小
/// </summary>
[FieldOffset(2)] public fixed byte UpgradeDataPacketSize[Define.UpgradePacketSize];
}
/// <summary>
/// OTA 数据请求包
/// </summary>
[StructLayout(LayoutKind.Explicit, Size = Define.UpgradeDataPacketNum + 3)]
public unsafe struct OtaDataPacketRequest
{
[FieldOffset(0)] public fixed byte buf[Define.UpgradeDataPacketNum + 3];
/// <summary>
/// 包类型
/// </summary>
[FieldOffset(0)] public byte PacketType;
[FieldOffset(1)] public byte TotalPackNum;
[FieldOffset(2)] public byte NowPackNum;
[FieldOffset(3)] public fixed byte UpgradeData[Define.UpgradeDataPacketNum];
}
[StructLayout(LayoutKind.Explicit, Size = 1)]
public unsafe struct OtaPackResponse
{
[FieldOffset(0)] public fixed byte buf[1];
/// <summary>
/// 执行结果 0xFE时执行开机成功 0xFD时开启失败 0xFC表示当前已处于开启状态
/// </summary>
[FieldOffset(0)] public byte resp_code;
}
}
主入口函数
using System.IO.Ports;
using CommonLib.Basic;
using Modbus.Device;
using ModbusConsole.Helper;
using ModbusConsole.Upgrade;
namespace ModbusConsole;
public static class Program
{
public static void Main(string[] args)
{
var binFile =
@"E:\scl\gitee\stm32f103vet6-project\cmake-build-debug\stm32f103vet6-project.bin";
var binData = CommonFileBasicHelper.GetFileSize(binFile);
Console.WriteLine($"bin data size: {binData}");
var packetData = OtaPacketHelper.BuildOtaStartPacketRequest(0x01, (uint)binData, "202301020101");
Console.WriteLine(packetData.ToHexString());
var binDataList = CommonFileBasicHelper.LoadBinFile(binFile,1024);
var client = new SerialPort("COM17",115200);
client.Open();
client.Write(packetData, 0, packetData.Length);
Thread.Sleep(500);
var status = client.ReadByte();
Console.WriteLine(status);
for (var i = 0; i < binDataList.Count; i++)
{
var upgradeData =
OtaPacketHelper.BuildOtaDataPacketRequest((byte)(i + 1), (byte)binDataList.Count, binDataList[i]);
client.Write(upgradeData, 0, upgradeData.Length);
var result = client.ReadByte();
Console.WriteLine($"result: {result:X2}");
Thread.Sleep(50);
}
packetData = OtaPacketHelper.BuildOtaEndPacketRequest(0x1, (uint)binData);
client.Write(packetData, 0, packetData.Length);
status = client.ReadByte();
Console.WriteLine($"result: {status:X2}");
Console.Read();
}
}