加上模组 OTA:APP 用 55AA 的 0x30~0x39 升级,MCU 和模组分开升。

- MCU 升级:模组只原样转发,不存 MCU 固件
- 模组自己升级:写到备用区,校验通过后再切过去,成功回 0x34,等 APP 回 0x35 再重启
- 升模组时先通知 MCU 先别上报;升的过程中不跑心跳、不处理普通控制
- 关掉原来的 JSON OTA,改走这条协议
This commit is contained in:
2026-08-20 16:12:08 +08:00
parent 7b7583668b
commit c0b5fcc4c5
23 changed files with 2700 additions and 446 deletions
-1
View File
@@ -1,4 +1,3 @@
PY32F040开发框架/*
.vscode/
模组功能分期清单.md
.clang-format
+466
View File
@@ -0,0 +1,466 @@
# OTA升级流程
# 修订记录
|文档版本|日期|编写人|修订类型|修订内容|
|---|---|---|---|---|
|V1\.0\.0|2026\-07\-09|麦田|新建|首次发布OTA升级流程文档|
# APP获取设备版本
设备连接服务器后,主动上报当前版本给服务器,服务器进行维护。
服务器维护:
|**字段**|**说明**|
|---|---|
|固件类型|BLE / MCU |
|固件版本|当前版本号|
APP进入设备页面后,找服务器获取当前固件版本。
服务器返回:
```Plain Text
MCU固件
当前版本:V1.0.0
BLE固件
当前版本:V2.0.0
```
---
# 服务器推送OTA版本给APP
用户在服务器上传了bin文件,并填写了版本号和固件类型,点击发布。APP进入设备页面之后,服务器推送OTA文件信息给APP。
服务器推送:
```Plain Text
MCU固件
OTA版本:V1.2.0
长度:128KB
CRC320x12345678
BLE固件
OTA版本:V2.0.1
长度:256KB
CRC320x87654321
```
# APP进行版本比较
APP比较:
```Plain Text
OTA版本 VS 设备当前版本
```
例如:
|**固件**|**当前版本**|**OTA版本**|**是否升级**|
|---|---|---|---|
|MCU|V1\.0\.0|V1\.2\.0|是|
|BLE|V2\.0\.0|V2\.0\.1|是|
如果两个固件都需要升级:
升级顺序:
```Plain Text
第一步:MCU升级
第二步:BLE升级
```
避免 BLE 升级过程中影响 MCU OTA 通讯。
---
# APP通知BLE准备OTA
APP发送**OTA升级请求**告诉 BLE 本次升级目标。
例如升级 MCU
```Plain Text
Target:MCU
Firmware Version:V1.2.0
Payload Length:128KB
Payload CRC32:xxxx
```
---
# BLE判断升级目标
BLE收到:**OTA升级请求**
进行固件类型判断:
## 情况1:升级BLE
流程:
```Plain Text
APP
|
| OTA升级请求
|
BLE
|
| 自身检查
|
BLE返回准备结果
```
检查:
- Flash空间
- OTA状态
- 固件信息
返回:OTA请求应答
成功:
```Plain Text
Result = 0x00
Max Data Length = xxx
```
然后APP开始发送BLE Bin。
---
## 情况2:升级MCU
流程:
```Plain Text
APP
BLE
MCU
```
BLE收到后:
提取:
- Firmware Version
- Payload Length
- Payload CRC32
发送:
```Plain Text
BLE
|
| OTA升级请求
MCU
```
---
# MCU检查OTA条件
MCU收到升级请求。
检查:
1. 固件版本 :新版本 \> 当前版本
2. Flash空间
3. OTA状态 返回:OTA请求应答
结果:
|**Result**|含义|
|---|---|
|0x00|允许升级|
|0x20|无需升级(版本一致)|
|0x21|OTA 版本低于当前版本|
|0x22|Flash 空间不足|
|0x23|Bootloader 不支持|
|0x24|OTA 状态错误|
|0x25|固件类型不支持|
同时返回:
```Plain Text
Max Data Length
```
例如:
```Plain Text
512 Byte
```
---
# APP发送OTA Bin数据
APP按照:
```Plain Text
Max Data Length
```
进行分包。
例如:
MCU允许:
```Plain Text
512 Byte
```
发送:
```Plain Text
当前分包序号
总分包数
OTA数据
```
流程:
```Plain Text
APP
OTA_DATA
BLE
OTA_DATA
MCU
```
---
# MCU接收OTA数据
MCU每收到一包:
1. 校验数据包
2. 写入OTA区域
3. 累计接收长度
例如:
第一次:
```Plain Text
received = 512
```
第二次:
```Plain Text
received = 1024
```
直到:
```Plain Text
received == Payload Length
```
表示:
数据接收完成。
---
# OTA数据包应答
每包数据:
MCU返回:OTA数据应答
例如
成功:
```Plain Text
Result=0x00
```
失败:
```Plain Text
Result=错误码
```
如果失败:
BLE停止发送。
---
# MCU完成OTA校验
全部数据接收完成。
MCU
计算:CRC32
比较:CRC32 VS OTA升级请求中的CRC32
---
## 校验成功
MCU:保存升级信息。
返回:OTA\_RESULT
然后:重启。
---
## 校验失败
返回:OTA\_RESULT
错误码:
例如:
```Plain Text
CRC错误
```
---
# MCU通知BLE升级结果
MCU升级完成后主动发送:MCU OTA完成通知
协议:0x34
内容:
```Plain Text
OTA Result
MCU Version
```
BLE收到:添加上类型(0x01 MCU
上传APP。
---
# BLE通知APP升级结果
BLE发送:OTA完成通知
告诉APP
```Plain Text
MCU升级完成
版本:V1.2.0
结果:成功
```
---
# 如果BLE也需要升级
MCU升级成功后:
APP继续:
```Plain Text
Target = BLE
```
流程重新开始:
```Plain Text
APP
BLE
BLE自身OTA
```
完成后:
BLE通知:
```Plain Text
OTA成功
```
APP更新最终状态。
---
# 完整流程图
```Plain Text
云服务器
|
|
获取最新固件信息
|
APP比较版本,决定升级顺序
|
┌───────────┴───────────┐
│ │
MCU升级 BLE升级
│ │
OTA升级请求 OTA升级请求
│ │
▼ ▼
BLE BLE
│ │
│ 解包 ▼
▼ 自身准备
OTA升级请求
MCU
OTA请求应答
OTA数据
OTA数据应答
CRC校验
OTA_RESULT
MCU通知BLE
BLE通知APP
```
+9
View File
@@ -177,6 +177,7 @@
<Add directory="apps/usr_mcu_bridge" />
<Add directory="apps/usr_mcu_bridge/protocol" />
<Add directory="apps/usr_mcu_bridge/business" />
<Add directory="apps/usr_ota" />
<Add directory="apps/usr_periph" />
</Compiler>
<Linker>
@@ -800,6 +801,14 @@
<Unit filename="apps/usr_mcu_bridge/business/mcu_mgmt.c">
<Option compilerVar="CC" />
</Unit>
<Unit filename="apps/usr_mcu_bridge/business/mcu_ota.c">
<Option compilerVar="CC" />
</Unit>
<Unit filename="apps/usr_mcu_bridge/business/mcu_ota.h" />
<Unit filename="apps/usr_ota/usr_ota.c">
<Option compilerVar="CC" />
</Unit>
<Unit filename="apps/usr_ota/usr_ota.h" />
<Unit filename="apps/usr_mcu_bridge/business/mcu_pairing.c">
<Option compilerVar="CC" />
</Unit>
+3
View File
@@ -251,6 +251,7 @@ INCLUDES := \
-Iapps/usr_mcu_bridge \
-Iapps/usr_mcu_bridge/protocol \
-Iapps/usr_mcu_bridge/business \
-Iapps/usr_ota \
-Iapps/usr_periph \
-I$(SYS_INC_DIR) \
@@ -628,6 +629,8 @@ c_SRC_FILES := \
apps/usr_mcu_bridge/business/mcu_mgmt.c \
apps/usr_mcu_bridge/business/mcu_time.c \
apps/usr_mcu_bridge/business/mcu_version.c \
apps/usr_mcu_bridge/business/mcu_ota.c \
apps/usr_ota/usr_ota.c \
apps/usr_periph/usr_rtc.c \
apps/usr_le_code/ble_proto.c \
apps/usr_le_code/bleproto.c \
@@ -53,10 +53,19 @@ extern u8 muti_adv_data_len;
extern bleproto_authsetup_req_t usr_auth_data;
extern u8 usr_get_full();
extern u16 usr_get_conn_service();
extern u16 usr_get_conn_service(void);
extern int muti_make_set_adv_data(u8* data,u8 data_len);
extern u8 usr_get_ldoin_all();
/**
* @brief BLE 是否已连接
* @return 1=已连接,0=未连接
*/
uint8_t usr_ble_is_connected(void)
{
return (usr_get_conn_service() != 0) ? 1u : 0u;
}
void usr_ble_adv_updata();
static int le_timer_handle=0;
@@ -231,7 +240,6 @@ void usr_reconn_adv()
muti_adv_data_len=bleproto_advdata_encode4reconn(muti_adv_data,txpower,gmac,manucode,pcode,prodcode);
usr_adv_flag=1;
}*/
extern u8 usr_get_ota_status();
/**
* @brief 周期处理广播刷新(由 500ms 定时器调用)
@@ -252,7 +260,7 @@ void usr_ble_adv_updata()
ble_trans_module_enable(1);
}
if(usr_get_conn_service()==0)
if (!usr_ble_is_connected())
{
/* 未连接:回连快广播(80)持续约 60s(500ms×120)后切慢广播 */
if(usr_get_adv_interval()==80)
@@ -284,13 +292,9 @@ void usr_ble_adv_updata()
}
if(usr_get_ota_status()==0)
if (!usr_ble_is_connected())
{
/* 不在 OTA:未连接则退出 OTA 残留(ota_succ 时不退) */
if(usr_get_conn_service()==0)
{
if(usr_var.usr_ota_succ_flag==0)usr_ota_exit();
}
mcu_bridge_on_ble_disconnected();
}
}
@@ -323,7 +327,7 @@ void usr_ota_process()
ble_multi_trans_disconnect(); /* 第一次:先断开,好换 OTA 广播地址 */
}
if (usr_get_conn_service() == 0)
if (!usr_ble_is_connected())
{
cnt++;
if (cnt == 2)
@@ -344,6 +348,7 @@ void usr_ota_process()
void usr_app_ota_init()
{
/* 旧版 RCSP/JSON OTA 入口;产品 55AA dual_bank 路径不要调用 */
usr_var.usr_goto_updata = 1;
if (usr_var.ota_timer == 0)
{
@@ -2,12 +2,13 @@
* @file usr_le_api.h
* @brief usr_le_code 对外 API(广播 / 收发 / 配网 / MCU 帧转发)
* @author cyWu <1917507415@qq.com>
* @date 2026.08.13
* @version V1.2.0
* @date 2026.08.15
* @version V1.3.0
* @history
* - V1.0.0, 2026.08.03, cyWu, 首次发布
* - V1.1.0, 2026.08.06, cyWu, 增加 MCU 帧 BLE 发送接口,解耦模组桥
* - V1.2.0, 2026.08.13, cyWu, P1:复位前断连停广播;0x41 走 cmd100 上报
* - V1.3.0, 2026.08.15, cyWu, 连接查询/写配对/BLE 发送收口到本头文件
******************************************************************************/
#ifndef __USR_LE_API_H__
@@ -79,7 +80,26 @@ uint8_t usr_get_pair_flag(void);
uint8_t usr_ble_is_notify_ready(void);
/**
* @brief 启动应用侧 OTA 流程
* @brief BLE 是否已建立连接
* @return 1=已连接,0=未连接
*/
uint8_t usr_ble_is_connected(void);
/**
* @brief 标记已配网并保存三元组到 VM
*/
void usr_write_pair_info(void);
/**
* @brief BLE 协议栈发送(实现于协议栈适配层)
* @param data 发送缓冲
* @param len 长度
*/
void usr_ble_send_data(uint8_t *data, uint16_t len);
/**
* @brief 旧版 RCSP/JSON OTA:改 MAC 发 OTA 广播
* @note 产品路径走 SOC 55AA 0x30~0x39 + dual_bank,不要调用本函数
*/
void usr_app_ota_init(void);
@@ -1,3 +1,14 @@
/******************************************************************************
* @file usr_le_recieve.c
* @brief BLE 收包入口:bleproto 解析后转交 mcu_bridge / 组应答
* @author cyWu <1917507415@qq.com>
* @date 2026.08.15
* @version V1.1.0
* @history
* - V1.0.0, 2026.08.03, cyWu, 首次发布
* - V1.1.0, 2026.08.15, cyWu, 只经 mcu_bridge.h 访问桥;JSON OTA 废弃
******************************************************************************/
#include "system/includes.h"
#include "app_config.h"
#include "asm/pwm_led.h"
@@ -11,67 +22,103 @@
#include "user_cfg.h"
#include "key_event_deal.h"
#include "audio.h"
#include "update.h"
#include "vm.h"
#include "bleproto.h"
#include "bleproto_api.h"
#include "ble_proto.h"
#include "mcu_frame.h"
#include "mcu_bridge.h"
#include "usr_le_api.h"
#define LOG_TAG_CONST APP
#define LOG_TAG "[APP]"
#define LOG_ERROR_ENABLE
#define LOG_DEBUG_ENABLE
#define LOG_INFO_ENABLE
/* #define LOG_DUMP_ENABLE */
#define LOG_CLI_ENABLE
#include "debug.h"
#define FIRM_PACK_LEN 2048 // 128
/* 拼 GATT 分包用:最大业务包 ≈ bleproto头8 + V2开销~24 + MCU 0x32(640) ≈ 672
* AUTHSETUP JSON 约数百字节。1024 覆盖产品路径并留余量(不再按 JSON OTADATA 的 2KB+ 预留)。 */
#define USR_LE_RX_BUF_LEN 1024
#define USR_LE_V2_WRAP_OVERHEAD 32u /* V2 头开销,MCU 帧最大 128 → 约 151 */
#define USR_LE_V2_CMD_REPORT 100u /* REPORTCMD_V2 内层 cmd */
#define USR_LE_V2_CMD_RUN 110u /* RUNCMD_V2 内层 cmd */
static u8 start_data[10] = {0x55, 0xAA, 0xFE, 0x61, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00};
static u8 end_data[2] = {0xaa, 0x55};
static int encode_len = 0x00;
static u8 txbuf[384] = {0};
static u8 rxbuf[FIRM_PACK_LEN + 1024] = {0};
static u8 ota_source_data[FIRM_PACK_LEN * 2] = {0};
extern u16 usr_get_conn_service();
extern void ble_multi_trans_disconnect(void);
extern void usr_ble_send_data(u8 *data, u16 len); /// BLE发送接口,MTU 244
extern void usr_write_pair_info();
extern void usr_clear_pair_info();
extern void app_audio_volume_set(u8 value);
extern int dual_ota_app_data_deal(u32 msg, u8 *buf, u32 len);
extern int uart_tr_send_data(u8 *data, u32 len);
extern void usr_intterupt_close();
extern void mcu_bridge_on_paired(void);
extern void mcu_bridge_on_pair_cleared(void);
extern void mcu_bridge_on_app_frame(const uint8_t *data, uint16_t len);
extern void mcu_bridge_on_gateway_report_ack(uint16_t cmd,
uint8_t gateway_int_cnt,
int32_t gateway_int0);
extern void mcu_bridge_on_network_time(uint64_t unix_s, int32_t gmtoff_s);
void usr_ota_deal_task();
void usr_data_ana(bleproto_appdata_desc_t *desc);
void usr_run_cmd_code_v2(ble_proto_packet_t *runcmd_par, u8 usr_msg_id);
static u8 rxbuf[USR_LE_RX_BUF_LEN] = {0};
static u8 s_last_runcmd_msgid = 0; /* RUNCMD_V2 应答须回填最近 msgid */
bleproto_authsetup_req_t usr_auth_data;
void usr_le_data_recieve(u8 *data, u16 len) /// BLE数据接收
static void usr_data_ana(bleproto_appdata_desc_t *desc);
static void usr_run_cmd_code_v2(ble_proto_packet_t *runcmd_par, u8 usr_msg_id);
/**
* @brief 填 bleproto 应答头
* @param out 输出 desc
* @param in 请求 desc(取 msgid
* @param serviceid 服务号
*/
static void usr_le_fill_rsp_header(bleproto_appdata_desc_t *out,
const bleproto_appdata_desc_t *in,
uint8_t serviceid)
{
out->header.version = 0;
out->header.datafmt = BLEPROTO_APPDATA_DATAFMT_JSON;
out->header.msgtype = BLEPROTO_APPDATA_MSGTYPE_RSP;
out->header.msgid = in->header.msgid;
out->header.encrypt = 0;
out->header.serviceid = serviceid;
}
/**
* @brief 编码并经 BLE 发出
* @param desc 已填好的应答
*/
static void usr_le_encode_and_send(bleproto_appdata_desc_t *desc)
{
encode_len = bleproto_appdata_encode(txbuf, sizeof(txbuf), sizeof(txbuf), desc);
if (encode_len > 0)
{
printf("encode_len = %d\n", encode_len);
usr_ble_send_data(txbuf, encode_len);
}
else
{
printf("encode_len fail %d\n", encode_len);
}
}
/**
* @brief 是否走网关主动上报(REPORTCMD_V2
* @param mcu_cmd 55AA 命令字
* @return 1=主动上报,0=RUNCMD 应答
*/
static uint8_t usr_le_is_gateway_report_cmd(uint8_t mcu_cmd)
{
return (mcu_cmd == MCU_CMD_REPORT ||
mcu_cmd == MCU_CMD_OTA_COMPLETE ||
mcu_cmd == MCU_CMD_OTA_STOP_MCU ||
mcu_cmd == MCU_CMD_GET_MCU_INFO_ACK)
? 1u
: 0u;
}
/**
* @brief BLE 收包入口
* @param data 收包数据
* @param len 数据长度
*/
void usr_le_data_recieve(u8 *data, u16 len)
{
// log_info("le recieve: %d",len);
// printf_buf(data, len);
static u16 usr_len = 0;
/* desc 含 ycmd 等 union,放 static,避免每次收包在栈上开大块 */
static bleproto_appdata_desc_t s_rx_desc;
int rc_result = 0;
if ((u32)usr_len + len > sizeof(rxbuf))
{
/* 累积超界:多半是丢包/半包导致 decode 一直返回 0,防止缓冲区溢出 */
printf("### rxbuf overflow, usr_len=%u len=%u, reset\n", usr_len, len);
usr_len = 0;
return;
@@ -80,97 +127,62 @@ void usr_le_data_recieve(u8 *data, u16 len) /// BLE数据接收
memcpy(&rxbuf[usr_len], data, len);
usr_len += len;
rc_result = bleproto_appdata_decode(rxbuf, usr_len, &s_rx_desc);
// printf("rc_result_le## = %d\n",rc_result);
if (rc_result < 0)
{
// 接收到了非法的数据包,无法解析
printf("### invild pack\n");
usr_len = 0;
}
else if (rc_result == 0)
{
// 接收的数据不完整,继续接收数据
printf("### keep recieving\n");
}
else //(rc_result > 0)
else
{
// 数据解析成功
printf("### pack recieved ok\n");
usr_len = 0;
usr_data_ana(&s_rx_desc);
}
}
void usr_data_ana(bleproto_appdata_desc_t *desc)
/**
* @brief 解析 bleproto 服务并分发
* @param desc 已 decode 的包
*/
static void usr_data_ana(bleproto_appdata_desc_t *desc)
{
/* 应答侧 desc 同样较大,用 static,避免 BLE 任务栈峰值 */
static bleproto_appdata_desc_t s_encode_desc;
memset(&s_encode_desc, 0, sizeof(s_encode_desc));
printf("#############334 desc->header.serviceid = %d\n", desc->header.serviceid);
if (desc->header.msgtype /*desc.header.msgtype*/ == BLEPROTO_APPDATA_MSGTYPE_REQ)
printf("desc serviceid=%d msgtype=%d\n",
desc->header.serviceid, desc->header.msgtype);
if (desc->header.msgtype == BLEPROTO_APPDATA_MSGTYPE_REQ)
{
switch (desc->header.serviceid)
{
case E_BLEPROTO_SERVICE_ID_DEVICEINFO:
{
case E_BLEPROTO_SERVICE_ID_DEVICEINFO: {
bleproto_deviceinfo_req_t *req = &desc->datast.deviceinfo_req;
/* 网关从子设备连上开始计时,每 12 小时经 DEVICEINFO 同步一次时间戳+时区 */
mcu_bridge_on_network_time(req->t, req->gmtoff);
}
s_encode_desc.header.version = 0;
s_encode_desc.header.datafmt = BLEPROTO_APPDATA_DATAFMT_JSON;
s_encode_desc.header.msgtype = BLEPROTO_APPDATA_MSGTYPE_RSP;
s_encode_desc.header.msgid = desc->header.msgid; // 这里的msg_id为上面解析到的包的msg_id;
s_encode_desc.header.encrypt = 0;
s_encode_desc.header.serviceid = E_BLEPROTO_SERVICE_ID_DEVICEINFO;
// deviceinfo rsp序列化
bleproto_deviceinfo_rsp_t *rsp = &s_encode_desc.datast.deviceinfo_rsp;
{
// 填写设备相关信息
/* 网关从子设备连上开始计时,每 12 小时经 DEVICEINFO 同步一次 */
mcu_bridge_on_network_time(req->t, req->gmtoff);
usr_le_fill_rsp_header(&s_encode_desc, desc, E_BLEPROTO_SERVICE_ID_DEVICEINFO);
rsp->hbcycle = 1000;
strcpy(rsp->swv, "V1.0.0.00");
strcpy(rsp->hwv, "V1.0.0.00");
rsp->longcon = 1;
rsp->prototype = 4;
}
encode_len = bleproto_appdata_encode(txbuf, sizeof(txbuf), sizeof(txbuf), &s_encode_desc);
if (encode_len > 0)
{
printf("encode_len = %d\n", encode_len);
// 调用蓝牙协议栈发送数据接口,将数据一包一包的发送出去
// 第一包:packetlen
// 第二包:packetlen
// ......
// 第N包: totallen - (packetlen * (N -1))
usr_ble_send_data(txbuf, encode_len);
}
else
{
printf("2encode_len = %d\n", encode_len);
}
// printf("E_BLEPROTO_SERVICE_ID_DEVICEINFO\n");
usr_le_encode_and_send(&s_encode_desc);
break;
case E_BLEPROTO_SERVICE_ID_AUTHSETUP:
// 设备处理,回复响应
printf("E_BLEPROTO_SERVICE_ID_AUTHSETUP\n");
s_encode_desc.header.version = 0;
s_encode_desc.header.datafmt = BLEPROTO_APPDATA_DATAFMT_JSON;
s_encode_desc.header.msgtype = BLEPROTO_APPDATA_MSGTYPE_RSP;
s_encode_desc.header.msgid = desc->header.msgid; // 这里的msg_id为上面解析到的包的msg_id;
s_encode_desc.header.encrypt = 0;
s_encode_desc.header.serviceid = E_BLEPROTO_SERVICE_ID_AUTHSETUP;
}
case E_BLEPROTO_SERVICE_ID_AUTHSETUP: {
bleproto_authsetup_rsp_t *au_rsp = &s_encode_desc.datast.authsetup_rsp;
au_rsp->errcode = 0;
printf("E_BLEPROTO_SERVICE_ID_AUTHSETUP\n");
usr_le_fill_rsp_header(&s_encode_desc, desc, E_BLEPROTO_SERVICE_ID_AUTHSETUP);
au_rsp->errcode = 0;
memcpy(&usr_auth_data, &desc->datast.authsetup_req, sizeof(usr_auth_data));
{
// 保存三元组
if (usr_auth_data.paired_flag == 0)
{
usr_write_pair_info();
@@ -178,30 +190,14 @@ void usr_data_ana(bleproto_appdata_desc_t *desc)
printf("usr_auth_data %s %s ", usr_auth_data.authcode, usr_auth_data.devid);
printf_buf(usr_auth_data.gwmac, 6);
}
}
encode_len = bleproto_appdata_encode(txbuf, sizeof(txbuf), sizeof(txbuf), &s_encode_desc);
if (encode_len > 0)
{
printf("encode_len = %d\n", encode_len);
usr_ble_send_data(txbuf, encode_len);
}
else
{
printf("2encode_len = %d\n", encode_len);
}
usr_le_encode_and_send(&s_encode_desc);
break;
case E_BLEPROTO_SERVICE_ID_AUTHDELETE:
// 设备处理,回复响应
printf("E_BLEPROTO_SERVICE_ID_AUTHDELETE\n");
s_encode_desc.header.version = 0;
s_encode_desc.header.datafmt = BLEPROTO_APPDATA_DATAFMT_JSON;
s_encode_desc.header.msgtype = BLEPROTO_APPDATA_MSGTYPE_RSP;
s_encode_desc.header.msgid = desc->header.msgid; // 这里的msg_id为上面解析到的包的msg_id;
s_encode_desc.header.encrypt = 0;
s_encode_desc.header.serviceid = E_BLEPROTO_SERVICE_ID_AUTHSETUP;
}
case E_BLEPROTO_SERVICE_ID_AUTHDELETE: {
bleproto_authdelete_rsp_t *aude_rsp = &s_encode_desc.datast.authdelete_rsp;
printf("E_BLEPROTO_SERVICE_ID_AUTHDELETE\n");
usr_le_fill_rsp_header(&s_encode_desc, desc, E_BLEPROTO_SERVICE_ID_AUTHDELETE);
memcpy(aude_rsp->gwmac, usr_auth_data.gwmac, 6);
if (!memcmp(usr_auth_data.authcode, desc->datast.authdelete_req.authcode, 32))
{
@@ -212,20 +208,10 @@ void usr_data_ana(bleproto_appdata_desc_t *desc)
{
aude_rsp->errcode = 501;
}
encode_len = bleproto_appdata_encode(txbuf, sizeof(txbuf), sizeof(txbuf), &s_encode_desc);
if (encode_len > 0)
{
printf("encode_len = %d\n", encode_len);
usr_ble_send_data(txbuf, encode_len);
}
else
{
printf("2encode_len = %d\n", encode_len);
}
usr_le_encode_and_send(&s_encode_desc);
break;
}
case E_BLEPROTO_SERVICE_ID_RUNCMD:
// 设备处理,回复响应
printf("E_BLEPROTO_SERVICE_ID_RUNCMD\n");
break;
case E_BLEPROTO_SERVICE_ID_RUNCMD_V2:
@@ -233,107 +219,28 @@ void usr_data_ana(bleproto_appdata_desc_t *desc)
usr_run_cmd_code_v2(&desc->datast.runcmd_v2_req, desc->header.msgid);
break;
case E_BLEPROTO_SERVICE_ID_OTANOTIFY:
printf("E_BLEPROTO_SERVICE_ID_OTANOTIFY\n");
s_encode_desc.header.version = 0;
s_encode_desc.header.datafmt = BLEPROTO_APPDATA_DATAFMT_JSON;
s_encode_desc.header.msgtype = BLEPROTO_APPDATA_MSGTYPE_RSP;
s_encode_desc.header.msgid = desc->header.msgid; // 这里的msg_id为上面解析到的包的msg_id;
s_encode_desc.header.encrypt = 0;
s_encode_desc.header.serviceid = E_BLEPROTO_SERVICE_ID_OTANOTIFY;
s_encode_desc.datast.doaction_rsp.errcode = 0;
bleproto_otanotify_rsp_t *in_ota_rsp = &s_encode_desc.datast.otanotify_rsp;
bleproto_otanotify_req_t *in_ota_data2 = &desc->datast.otanotify_req;
printf("in_ota_data id = %d\n", in_ota_data2->id);
printf("in_ota_data ver = %s\n", in_ota_data2->version);
printf("in_ota_data size= %d\n", in_ota_data2->size);
printf("in_ota_data md5 = %s\n", in_ota_data2->md5);
{
in_ota_rsp->errcode = 0;
// 执行命令
}
usr_var.usr_ota_data_id = in_ota_data2->id; // int
usr_var.usr_ota_data_size = in_ota_data2->size;
encode_len = bleproto_appdata_encode(txbuf, sizeof(txbuf), sizeof(txbuf), &s_encode_desc);
if (encode_len > 0)
{
printf("encode_len = %d\n", encode_len);
usr_ble_send_data(txbuf, encode_len);
}
else
{
printf("2encode_len = %d\n", encode_len);
}
usr_var.usr_allow_to_flash = 0;
usr_var.usr_data_index = 0;
start_data[2] = usr_var.usr_ota_data_size & 0x000000ff;
start_data[3] = (usr_var.usr_ota_data_size >> 8) & 0x000000ff;
start_data[4] = (usr_var.usr_ota_data_size >> 16) & 0x000000ff;
start_data[5] = (usr_var.usr_ota_data_size >> 24) & 0x000000ff;
dual_ota_app_data_deal(0, start_data, 10);
printf("#### buf total size = %d\n", usr_var.usr_ota_data_size);
printf_buf(start_data, 10);
s_encode_desc.header.version = 0;
s_encode_desc.header.datafmt = BLEPROTO_APPDATA_DATAFMT_JSON;
s_encode_desc.header.msgtype = BLEPROTO_APPDATA_MSGTYPE_REQ;
s_encode_desc.header.msgid = 0; // desc->header.msgid;
s_encode_desc.header.encrypt = 0;
s_encode_desc.header.serviceid = E_BLEPROTO_SERVICE_ID_OTADATA;
s_encode_desc.datast.doaction_rsp.errcode = 0;
s_encode_desc.datast.otadata_req.id = usr_var.usr_ota_data_id;
s_encode_desc.datast.otadata_req.offset = usr_var.usr_data_index;
s_encode_desc.datast.otadata_req.maxsize = FIRM_PACK_LEN;
encode_len = bleproto_appdata_encode(txbuf, sizeof(txbuf), sizeof(txbuf), &s_encode_desc);
if (encode_len > 0)
{
usr_ble_send_data(txbuf, encode_len);
}
else
{
printf("2encode_len err = %d\n", encode_len);
}
if (usr_var.usr_double_ota_timer_id == 0)
{
clk_set("sys", 48 * 1000000L);
usr_var.usr_double_ota_timer_id = 1;
// clk_set("sys", 120 * 1000000L);
os_task_create(usr_ota_deal_task, NULL, 31, FIRM_PACK_LEN, 0, "usr_ota");
}
break;
case E_BLEPROTO_SERVICE_ID_OTADATA:
printf("E_BLEPROTO_SERVICE_ID_OTADATA\n");
break;
case E_BLEPROTO_SERVICE_ID_OTADATA_V2:
printf("E_BLEPROTO_SERVICE_ID_OTADATA\n");
printf("JSON OTA deprecated, use SOC 0x30/0x32\n");
break;
default:
printf("err !!! default event desc->header.serviceid =%d\n", desc->header.serviceid);
break;
}
return;
}
else if (desc->header.msgtype == BLEPROTO_APPDATA_MSGTYPE_RSP)
if (desc->header.msgtype == BLEPROTO_APPDATA_MSGTYPE_RSP)
{
printf("BLEPROTO_APPDATA_MSGTYPE_RSP\n");
switch (desc->header.serviceid)
{
case E_BLEPROTO_SERVICE_ID_REPORTCMD:
// 主控回复的响应,设备处理
printf("E_BLEPROTO_SERVICE_ID_REPORTCMD\n");
printf("cmd id = %d", desc->datast.reportcmd_rsp.errcode);
printf("E_BLEPROTO_SERVICE_ID_REPORTCMD errcode=%d\n", desc->datast.reportcmd_rsp.errcode);
break;
case E_BLEPROTO_SERVICE_ID_REPORTCMD_V2:
{
/* 网关对 MCU 主动上报的应答:成功后由桥接层回 MCU 0x08 */
case E_BLEPROTO_SERVICE_ID_REPORTCMD_V2: {
ble_proto_packet_t *rpt_ack = &desc->datast.reportcmd_v2_rsp;
printf("E_BLEPROTO_SERVICE_ID_REPORTCMD_V2 cmd=%u cnt=%u int0=%ld\n",
printf("REPORTCMD_V2 cmd=%u cnt=%u int0=%ld\n",
(unsigned)rpt_ack->cmd,
(unsigned)rpt_ack->gateway_int_cnt,
(long)((rpt_ack->gateway_int_cnt > 0) ? rpt_ack->gateway_int[0] : 0));
@@ -344,121 +251,22 @@ void usr_data_ana(bleproto_appdata_desc_t *desc)
break;
}
case E_BLEPROTO_SERVICE_ID_OTADATA:
bleproto_otadata_rsp_t *ota_data_rsp = &desc->datast.otadata_rsp;
// printf("errcode = %d offset = %d len = %d\n",ota_data_rsp->errcode,ota_data_rsp->offset,ota_data_rsp->len);
uart_tr_send_data((u8 *)ota_data_rsp->data, ota_data_rsp->len);
memcpy(&ota_source_data[usr_var.usr_allow_to_flash], ota_data_rsp->data, ota_data_rsp->len);
usr_var.usr_allow_to_flash += ota_data_rsp->len;
usr_var.usr_start_write_flash = 0;
if (usr_var.usr_allow_to_flash >= (FIRM_PACK_LEN * 2)) // 0 - 1024
// 1024 - 2048
// 2048 - 3072
// 3072 - 4096
{
usr_var.usr_start_write_flash = 1;
}
else
{
usr_var.usr_data_index += FIRM_PACK_LEN;
if (usr_var.usr_data_index >= usr_var.usr_ota_data_size)
{
usr_var.usr_start_write_flash = 2;
break;
}
// bleproto_appdata_desc_t encode_desc = { 0 };
s_encode_desc.header.version = 0;
s_encode_desc.header.datafmt = BLEPROTO_APPDATA_DATAFMT_JSON;
s_encode_desc.header.msgtype = BLEPROTO_APPDATA_MSGTYPE_REQ;
s_encode_desc.header.msgid = 0; // desc->header.msgid;
s_encode_desc.header.encrypt = 0;
s_encode_desc.header.serviceid = E_BLEPROTO_SERVICE_ID_OTADATA;
s_encode_desc.datast.doaction_rsp.errcode = 0;
s_encode_desc.datast.otadata_req.id = usr_var.usr_ota_data_id;
s_encode_desc.datast.otadata_req.offset = usr_var.usr_data_index;
s_encode_desc.datast.otadata_req.maxsize = FIRM_PACK_LEN;
encode_len = bleproto_appdata_encode(txbuf, sizeof(txbuf), sizeof(txbuf), &s_encode_desc);
if (encode_len > 0)
{
usr_ble_send_data(txbuf, encode_len);
}
else
{
printf("2encode_len err = %d\n", encode_len);
}
}
printf("JSON OTA deprecated, use SOC 0x32\n");
break;
default:
printf("E_BLEPROTO_SERVICE_ID_REPORTCMD\n");
printf("unhandled RSP serviceid=%d\n", desc->header.serviceid);
break;
}
}
}
void usr_ota_deal_task()
/**
* @brief APP RUNCMD_V255AA 字节流转交桥接层
* @param runcmd_par 内层包
* @param usr_msg_id bleproto msgid
*/
static void usr_run_cmd_code_v2(ble_proto_packet_t *runcmd_par, u8 usr_msg_id)
{
os_time_dly(5);
static bleproto_appdata_desc_t s_ota_encode_desc;
while (1)
{
if (usr_var.usr_ota_succ_flag == 0)
{
if (usr_var.usr_start_write_flash == 1)
{
// OS_ENTER_CRITICAL();
usr_var.usr_data_index += FIRM_PACK_LEN;
usr_var.usr_start_write_flash = 0;
dual_ota_app_data_deal(0, ota_source_data, usr_var.usr_allow_to_flash);
os_time_dly(1);
// OS_EXIT_CRITICAL();
// printf("usr_var.usr_allow_to_flash = %d\n",usr_var.usr_allow_to_flash);
usr_var.usr_allow_to_flash = 0;
memset(&s_ota_encode_desc, 0, sizeof(s_ota_encode_desc));
s_ota_encode_desc.header.version = 0;
s_ota_encode_desc.header.datafmt = BLEPROTO_APPDATA_DATAFMT_JSON;
s_ota_encode_desc.header.msgtype = BLEPROTO_APPDATA_MSGTYPE_REQ;
s_ota_encode_desc.header.msgid = 0; // desc->header.msgid;
s_ota_encode_desc.header.encrypt = 0;
s_ota_encode_desc.header.serviceid = E_BLEPROTO_SERVICE_ID_OTADATA;
s_ota_encode_desc.datast.doaction_rsp.errcode = 0;
s_ota_encode_desc.datast.otadata_req.id = usr_var.usr_ota_data_id;
s_ota_encode_desc.datast.otadata_req.offset = usr_var.usr_data_index;
s_ota_encode_desc.datast.otadata_req.maxsize = FIRM_PACK_LEN;
encode_len = bleproto_appdata_encode(txbuf, sizeof(txbuf), sizeof(txbuf), &s_ota_encode_desc);
if (encode_len > 0)
{
usr_ble_send_data(txbuf, encode_len);
}
else
{
printf("2encode_len err = %d\n", encode_len);
}
}
else if (usr_var.usr_start_write_flash == 2)
{
printf("usr_start_write_flash = %d\n", usr_var.usr_start_write_flash);
usr_var.usr_start_write_flash = 0;
// OS_ENTER_CRITICAL();
dual_ota_app_data_deal(0, ota_source_data, usr_var.usr_allow_to_flash);
// OS_EXIT_CRITICAL();
printf("usr_var.usr_allow_to_flash = %d\n", usr_var.usr_allow_to_flash);
usr_var.usr_allow_to_flash = 0;
os_time_dly(30);
dual_ota_app_data_deal(0, end_data, 2);
}
}
os_time_dly(10);
// putchar('6');
}
}
/* 最近一次收到 RUNCMD_V2 请求的 msgid,应答时回填 */
static u8 s_last_runcmd_msgid = 0;
void usr_run_cmd_code_v2(ble_proto_packet_t *runcmd_par, u8 usr_msg_id)
{
u8 mcu_frame[MCU_FRAME_MAX_LEN];
u16 copy_len;
if (!runcmd_par || !runcmd_par->bytes_data || runcmd_par->device_bytes_len == 0)
@@ -472,15 +280,14 @@ void usr_run_cmd_code_v2(ble_proto_packet_t *runcmd_par, u8 usr_msg_id)
runcmd_par->cmd, runcmd_par->device_bytes_len);
copy_len = runcmd_par->device_bytes_len;
if (copy_len > sizeof(mcu_frame))
if (copy_len > MCU_OTA_FRAME_MAX_LEN)
{
printf("usr_run_cmd_code_v2 bytes too long %u\n", copy_len);
return;
}
memcpy(mcu_frame, runcmd_par->bytes_data, copy_len);
s_last_runcmd_msgid = usr_msg_id;
mcu_bridge_on_app_frame(mcu_frame, copy_len);
mcu_bridge_on_app_frame(runcmd_par->bytes_data, copy_len);
}
/**
@@ -488,22 +295,19 @@ void usr_run_cmd_code_v2(ble_proto_packet_t *runcmd_par, u8 usr_msg_id)
* @param data 55AA 完整帧
* @param len 帧长度
* @note 主动上报(0x07/0x34/0x38/0x41) → REPORTCMD_V2;其余 → RUNCMD_V2 应答
* @note 轻量路径:栈上仅 ble_proto_packet_t + 小 v2 缓冲,
* 不走 bleproto_appdata_desc_t / bleproto_appdata_encode(约 2.3KB×2
* @return 1=已交给协议栈,0=参数非法/超长/CCC 未就绪/组包失败
*/
uint8_t usr_le_send_mcu_frame(u8 *data, u16 len)
{
ble_proto_packet_t pkt;
bleproto_appdata_header_t hdr;
/* V2开销:Hdr6+gw_cnt1+gw_int8+dev6+CRC2=23MCU帧最大128 → 需约151 */
u8 v2_buf[MCU_FRAME_MAX_LEN + 32];
u8 v2_buf[MCU_FRAME_MAX_LEN + USR_LE_V2_WRAP_OVERHEAD];
u8 mcu_cmd;
u8 is_active_send;
int plen;
int elen;
if (!data || len < 5)
if (!data || len < MCU_FRAME_OVERHEAD)
{
printf("usr_le_send_mcu_frame invalid param, len=%d\n", len);
return 0;
@@ -513,45 +317,35 @@ uint8_t usr_le_send_mcu_frame(u8 *data, u16 len)
printf("usr_le_send_mcu_frame frame too long %u\n", len);
return 0;
}
/* 已配网/已连接不等于能发:主机写完 CCC 才允许 notify */
if (!usr_ble_is_notify_ready())
{
printf("usr_le_send_mcu_frame wait CCC cmd=0x%02X\n", data[4]);
printf("usr_le_send_mcu_frame wait CCC cmd=0x%02X\n", data[MCU_FRAME_OFF_CMD]);
return 0;
}
mcu_cmd = data[4];
/* 主动上报走 REPORTCMD_V2(cmd100),应答走 RUNCMD_V2(cmd110) */
is_active_send = (mcu_cmd == MCU_CMD_REPORT) ||
(mcu_cmd == MCU_CMD_OTA_COMPLETE) ||
(mcu_cmd == MCU_CMD_OTA_STOP_MCU) ||
(mcu_cmd == MCU_CMD_GET_MCU_INFO_ACK);
mcu_cmd = data[MCU_FRAME_OFF_CMD];
is_active_send = usr_le_is_gateway_report_cmd(mcu_cmd);
memset(&pkt, 0, sizeof(pkt));
memset(&hdr, 0, sizeof(hdr));
hdr.version = 0;
hdr.datafmt = BLEPROTO_APPDATA_DATAFMT_JSON;
hdr.encrypt = 0;
if (is_active_send)
{
/* 设备→网关主动上报,等网关 ACK,不带 runcmd msgid */
hdr.msgtype = BLEPROTO_APPDATA_MSGTYPE_REQ;
hdr.msgid = 0;
hdr.serviceid = E_BLEPROTO_SERVICE_ID_REPORTCMD_V2;
pkt.cmd = 100;
pkt.cmd = USR_LE_V2_CMD_REPORT;
pkt.gateway_int_cnt = 0;
}
else
{
/* APP 下发的应答:msgid 必须对上最近一次 RUNCMD,网关才认 */
hdr.msgtype = BLEPROTO_APPDATA_MSGTYPE_RSP;
hdr.msgid = s_last_runcmd_msgid;
hdr.serviceid = E_BLEPROTO_SERVICE_ID_RUNCMD_V2;
pkt.cmd = 110;
pkt.cmd = USR_LE_V2_CMD_RUN;
pkt.gateway_int_cnt = 2;
pkt.gateway_int[0] = 0;
pkt.gateway_int[1] = 0;
@@ -7,6 +7,7 @@
* @history
* - V1.0.0, 2026.08.12, cyWu, 取代原 mcu_link,编排三个业务子模块
* - V1.1.0, 2026.08.13, cyWu, 编排 P1 管理/对时/版本
* - V1.2.0, 2026.08.15, cyWu, 编排 P2 OTA;版本周期在 OTA 时让路
******************************************************************************/
#include "system/includes.h"
@@ -19,6 +20,7 @@
#include "mcu_pairing.h"
#include "mcu_time.h"
#include "mcu_version.h"
#include "mcu_ota.h"
#define LOG_TAG_CONST APP
#define LOG_TAG "[MCU_BIZ]"
@@ -39,6 +41,7 @@ void mcu_business_init(void)
mcu_mgmt_init();
mcu_time_init();
mcu_version_init();
mcu_ota_init();
usr_var.usr_goto_pair = 0;
usr_var.usr_adv_flag = 0;
@@ -54,8 +57,12 @@ void mcu_business_periodic(void)
mcu_pairing_periodic();
mcu_mgmt_periodic();
mcu_time_periodic();
mcu_ota_periodic();
/* OTA 独占 UART/BLE 时,版本拉取会抢 ble_pending,让路 */
if (!mcu_ota_is_active()) {
mcu_version_periodic();
}
}
/**
* @brief 是否身份就绪
@@ -188,3 +195,53 @@ void mcu_business_on_gateway_ack(uint8_t success, uint8_t mcu_report_consumed)
{
mcu_version_on_gateway_ack(success, mcu_report_consumed);
}
/**
* @brief APP OTA 帧
*/
void mcu_business_on_app_ota_frame(const uint8_t *frame, uint16_t len)
{
mcu_ota_on_app_frame(frame, len);
}
/**
* @brief MCU OTA 帧
*/
void mcu_business_on_mcu_ota_frame(const uint8_t *frame, uint16_t len)
{
mcu_ota_on_uart_frame(frame, len);
}
/**
* @brief 0x15
*/
void mcu_business_on_ota_ws_ack(void)
{
mcu_ota_on_work_status_ack();
}
/**
* @brief BLE 断连
*/
void mcu_business_on_ble_disconnected(void)
{
mcu_ota_on_disconnect();
}
/**
* @brief OTA 是否进行中
* @return 1/0
*/
uint8_t mcu_business_ota_is_active(void)
{
return mcu_ota_is_active();
}
/**
* @brief 是否模组 OTA
* @return 1/0
*/
uint8_t mcu_business_ota_is_ble_active(void)
{
return mcu_ota_is_ble_active();
}
@@ -3,11 +3,12 @@
* @brief 业务门面:协调身份 / 配网 / 连接 / 管理 / 对时 / 版本
* @author cyWu <1917507415@qq.com>
* @date 2026.08.13
* @version V1.1.0
* @version V1.2.0
* @history
* - V1.0.0, 2026.08.12, cyWu, 取代原 mcu_link,供协议层(mcu_protocol
* 单点调用;协议层不直接依赖 identity/pairing/link_status 三个子模块
* - V1.1.0, 2026.08.13, cyWu, 接入 P1 管理/对时/版本子模块
* - V1.2.0, 2026.08.15, cyWu, 接入 P2 OTA;协议层只经本门面访问 OTA
******************************************************************************/
#ifndef __MCU_BUSINESS_H__
@@ -24,6 +25,7 @@
/* mcu_mgmt.c —— 出厂复位 / 重启 / 通知 MCU 重启 */
/* mcu_time.c —— 对时(写 RTC / 0x1D 读回) */
/* mcu_version.c —— UART 0x40/0x41 与 SOC 0x41 上报 */
/* mcu_ota.c —— OTA Type 分流 / MCU 透传 / 模组 dual_bank */
/* 须在协议任务(mcu_protocol_task)单一上下文中调用。 */
/*============================================================================*/
@@ -119,4 +121,44 @@ void mcu_business_on_mcu_info_ack(const uint8_t *frame, uint16_t len);
*/
void mcu_business_on_gateway_ack(uint8_t success, uint8_t mcu_report_consumed);
/*============================================================================*/
/* OTA(协议层只调门面,不直接 include mcu_ota.h */
/*============================================================================*/
/**
* @brief APP 下发 OTA 帧(0x30/0x32/0x35/0x36
* @param frame 完整 55AA
* @param len 长度
*/
void mcu_business_on_app_ota_frame(const uint8_t *frame, uint16_t len);
/**
* @brief MCU 上报 OTA 帧(0x31/0x33/0x34/0x37/0x38
* @param frame 完整 55AA
* @param len 长度
*/
void mcu_business_on_mcu_ota_frame(const uint8_t *frame, uint16_t len);
/**
* @brief UART 0x15 到达(BLE OTA 置 Bit6 后)
*/
void mcu_business_on_ota_ws_ack(void);
/**
* @brief BLE 断连:中止 OTA 会话(不切 bank、不复位)
*/
void mcu_business_on_ble_disconnected(void);
/**
* @brief 是否有 OTA 会话(心跳/0x14/版本上报据此让路)
* @return 1=是
*/
uint8_t mcu_business_ota_is_active(void);
/**
* @brief 是否模组(BLEOTA(据此丢弃 MCU 0x07
* @return 1=是
*/
uint8_t mcu_business_ota_is_ble_active(void);
#endif /* __MCU_BUSINESS_H__ */
@@ -22,12 +22,6 @@
#define LOG_INFO_ENABLE
#include "debug.h"
/*============================================================================*/
/* 外部符号 */
/*============================================================================*/
extern u16 usr_get_conn_service(void);
/*============================================================================*/
/* 私有变量 */
/*============================================================================*/
@@ -106,7 +100,7 @@ void mcu_link_status_set_ota(uint8_t on)
*/
void mcu_link_status_periodic(void)
{
uint8_t conn = (usr_get_conn_service() != 0) ? 1 : 0; /* 1=BLE 已连接 */
uint8_t conn = usr_ble_is_connected(); /* 1=BLE 已连接 */
uint8_t paired = usr_get_pair_flag(); /* 1=VM 已绑定三元组 */
uint8_t link_up;
@@ -0,0 +1,942 @@
/******************************************************************************
* @file mcu_ota.c
* @brief OTA 协议适配实现
* @author cyWu <1917507415@qq.com>
* @date 2026.08.15
* @version V1.0.0
* @history
* - V1.0.0, 2026.08.15, cyWu, 首次发布
******************************************************************************/
#include "system/includes.h"
#include "mcu_ota.h"
#include "mcu_cmd.h"
#include "mcu_frame.h"
#include "mcu_protocol.h"
#include "mcu_link_status.h"
#include "mcu_util.h"
#include "mcu_version.h"
#include "usr_ota.h"
#include "usr_le_api.h"
#define LOG_TAG_CONST APP
#define LOG_TAG "[MCU_OTA]"
#define LOG_ERROR_ENABLE
#define LOG_DEBUG_ENABLE
#define LOG_INFO_ENABLE
#include "debug.h"
/*============================================================================*/
/* 私有宏 */
/*============================================================================*/
#define MCU_OTA_TYPE_MCU (1u) /* MCU 类型 */
#define MCU_OTA_TYPE_BLE (2u) /* BLE 类型 */
#define MCU_OTA_BLE_MAX_DATA (512u) /* BLE 最大数据 */
#define MCU_OTA_UART_ACK_MS (2000u) /* UART ACK 时间 */
#define MCU_OTA_IDLE_MS (30000u) /* 空闲时间 */
#define MCU_OTA_APP_ACK_MS (1000u) /* APP ACK 时间 */
#define MCU_OTA_WS_WAIT_MS (500u) /* Bit6 ACK 时间 */
/* SOC 0x30 载荷:Type(1)+Ver(3)+Len(4)+CRC32(4) */
#define MCU_OTA_SOC_NOTIFY_PLEN (12u) /* SOC 通知长度 */
#define MCU_OTA_UART_NOTIFY_PLEN (11u) /* 无 Type 通知长度 */
#define MCU_OTA_DATA_HDR_LEN (4u) /* frag(2)+total(2) 数据头长度 */
#define MCU_OTA_DONE_UART_PLEN (4u) /* Result+Ver3 完成长度 */
#define MCU_OTA_DONE_SOC_PLEN (5u) /* Type+Result+Ver3 完成长度 */
#define MCU_OTA_PL_TYPE (0u) /* 类型 */
#define MCU_OTA_PL_VER (1u) /* 版本 */
#define MCU_OTA_PL_SIZE (4u) /* 大小 */
#define MCU_OTA_PL_CRC32 (8u) /* CRC32 */
#define MCU_OTA_DATA_PL_FRAG (0u) /* 分片 */
#define MCU_OTA_DATA_PL_BODY (4u) /* 主体 */
typedef enum
{
MCU_OTA_ST_IDLE = 0, /* 空闲 */
MCU_OTA_ST_MCU_WAIT_NOTIFY, /* 已发 0x30 给 MCU,等 MCU 回 0x31 */
MCU_OTA_ST_MCU_WRITE, /* 可以收 APP 下一包 0x32 */
MCU_OTA_ST_MCU_WAIT_DATA, /* 已把 0x32 转给 MCU,等 MCU 回 0x33 */
MCU_OTA_ST_MCU_WAIT_APP_DONE, /* MCU 已报 0x34,等 APP 回 0x35 */
MCU_OTA_ST_BLE_WAIT_WS, /* BLE 等待 Bit6 ACK */
MCU_OTA_ST_BLE_WRITE, /* BLE 写入 */
MCU_OTA_ST_BLE_WAIT_FEED, /* BLE 等待喂饱 */
MCU_OTA_ST_BLE_WAIT_APP_DONE, /* BLE 等待 APP 完成 */
} McuOtaState_t;
/*============================================================================*/
/* 私有变量 */
/*============================================================================*/
typedef struct {
uint8_t active; /* 活动 */
uint8_t expect; /* 期望命令 */
uint8_t sn; /* SN */
uint32_t tx_ms; /* 发送时间 */
} McuOtaUartWait_t;
typedef struct {
McuOtaState_t state; /* 状态 */
uint8_t type; /* 固件类型 */
uint8_t ver[3]; /* 版本 */
uint8_t app_sn; /* APP SN */
uint16_t expect_frag; /* 期望分片 */
uint32_t file_size; /* 文件大小 */
uint32_t file_crc32; /* 文件 CRC32 */
uint32_t last_data_ms; /* 最后一次数据时间 */
McuOtaUartWait_t uart_wait; /* UART ACK 等待 */
uint8_t ws_wait; /* Bit6 等待 */
uint32_t ws_tx_ms; /* 进入 WAIT_WS 时刻:最多等 500ms 就给 APP 0x31 */
uint8_t app_done_wait; /* APP 完成等待 */
uint32_t app_done_ms; /* APP 完成时间 */
/* 写 Flash 的回调只把结果记在这里,协议任务回头再处理、再回 APP */
volatile uint8_t usr_evt; /* 待处理结果(UsrOtaEvt_t */
volatile uint8_t usr_evt_valid; /* 1=有待处理结果 */
} McuOta_t;
static McuOta_t s_ota;
/**
* @brief 开始等 MCU UART ACK
* @param expect 期望命令
* @param sn SN
*/
static void mcu_ota_arm_uart_wait(uint8_t expect, uint8_t sn)
{
s_ota.uart_wait.active = 1;
s_ota.uart_wait.expect = expect;
s_ota.uart_wait.sn = sn;
s_ota.uart_wait.tx_ms = mcu_util_get_ms();
}
/**
* @brief 清除 UART ACK 等待
*/
static void mcu_ota_clear_uart_wait(void)
{
s_ota.uart_wait.active = 0;
}
/**
* @brief 是否匹配当前 UART ACK 等待
* @param cmd 命令
* @param sn 序列号
* @return 1=匹配
*/
static uint8_t mcu_ota_uart_wait_match(uint8_t cmd, uint8_t sn)
{
return (s_ota.uart_wait.active &&
(cmd == s_ota.uart_wait.expect) &&
(sn == s_ota.uart_wait.sn))
? 1u
: 0u;
}
/*============================================================================*/
/* 私有函数 */
/*============================================================================*/
/**
* @brief 回 APP 一帧
* @param cmd 命令
* @param sn 序列号
* @param pl 载荷
* @param plen 长度
*/
static void mcu_ota_reply_app(uint8_t cmd, uint8_t sn,
const uint8_t *pl, uint16_t plen)
{
uint8_t buf[MCU_FRAME_MAX_LEN];
int32_t total;
total = mcu_frame_pack(buf, sizeof(buf), cmd, sn, pl, plen);
if (total > 0)
{
usr_le_send_mcu_frame(buf, (uint16_t)total);
}
}
/**
* @brief 回 APP 单字节 Result
* @param cmd 命令
* @param sn SN
* @param result 结果
*/
static void mcu_ota_reply_app_result(uint8_t cmd, uint8_t sn, uint8_t result)
{
mcu_ota_reply_app(cmd, sn, &result, 1);
}
/**
* @brief 结束会话并恢复后台 UART
* @param clear_bit6 1=清模组 OTA Bit6 并强制再推 0x14
*/
static void mcu_ota_session_end(uint8_t clear_bit6)
{
if (clear_bit6)
{
mcu_link_status_set_ota(0);
mcu_protocol_invalidate_work_status_push();
}
memset(&s_ota, 0, sizeof(s_ota));
s_ota.state = MCU_OTA_ST_IDLE;
log_info("session end");
}
/**
* @brief 失败停止:MCU 发 0x36BLE 发 0x38
*/
static void mcu_ota_fail_stop(uint8_t result)
{
uint8_t type = s_ota.type;
log_error("fail stop result=0x%02X type=%u", result, type);
if (type == MCU_OTA_TYPE_MCU)
{
mcu_protocol_send_cmd(MCU_CMD_OTA_STOP_BLE, NULL, 0);
}
else if (type == MCU_OTA_TYPE_BLE)
{
usr_ota_abort();
mcu_ota_reply_app(MCU_CMD_OTA_STOP_MCU, s_ota.app_sn, NULL, 0);
}
mcu_ota_session_end(type == MCU_OTA_TYPE_BLE ? 1 : 0);
}
/**
* @brief 比较 BLE 版本:1=低于当前
* @param ver 三字节版本
* @return 1=过低
*/
static uint8_t mcu_ota_ble_ver_low(const uint8_t ver[3])
{
uint32_t in;
uint32_t cur;
in = ((uint32_t)ver[0] << 16) | ((uint32_t)ver[1] << 8) | ver[2];
cur = ((uint32_t)MCU_BLE_FW_VER_MAJOR << 16) |
((uint32_t)MCU_BLE_FW_VER_MINOR << 8) |
MCU_BLE_FW_VER_PATCH;
return (in < cur) ? 1 : 0;
}
/**
* @brief 推 Bit6 的 0x14
* @return 1=已发出
*/
static uint8_t mcu_ota_push_bit6(void)
{
uint8_t payload[2];
if (mcu_protocol_is_ble_pending())
{
return 0;
}
mcu_link_status_set_ota(1);
payload[0] = mcu_link_status_get();
payload[1] = 0x00;
if (mcu_protocol_send_cmd_wait_ack(MCU_CMD_WORK_STATUS, payload, 2,
MCU_CMD_WORK_STATUS_ACK) <= 0)
{
return 0;
}
s_ota.ws_wait = 1;
log_info("push Bit6 work_status=0x%02X", payload[0]);
return 1;
}
/**
* @brief 回 APP BLE 0x31
* @param result 结果
*/
static void mcu_ota_reply_ble_notify(uint8_t result)
{
uint8_t pl[3];
pl[0] = result;
mcu_util_write_u16_be(&pl[1], MCU_OTA_BLE_MAX_DATA);
mcu_ota_reply_app(MCU_CMD_OTA_NOTIFY_ACK, s_ota.app_sn, pl, 3);
}
/**
* @brief BLE 0x31 成功,进入 WRITE
*/
static void mcu_ota_ble_enter_write(void)
{
s_ota.ws_wait = 0;
s_ota.state = MCU_OTA_ST_BLE_WRITE;
s_ota.expect_frag = 1;
s_ota.last_data_ms = mcu_util_get_ms();
mcu_ota_reply_ble_notify(MCU_RESULT_OK);
log_info("BLE OTA write start");
}
/**
* @brief 处理 APP 0x30
* @param frame 帧
* @param len 长度
*/
static void mcu_ota_handle_notify(const uint8_t *frame, uint16_t len)
{
uint8_t type;
uint8_t sn;
uint8_t mcu_pl[MCU_OTA_UART_NOTIFY_PLEN];
UsrOta_Ret_t ret;
if (len < (MCU_FRAME_OVERHEAD + MCU_OTA_SOC_NOTIFY_PLEN))
{
mcu_ota_reply_app_result(MCU_CMD_OTA_NOTIFY_ACK,
frame[MCU_FRAME_OFF_SN],
MCU_RESULT_PARAM_ERR);
return;
}
sn = frame[MCU_FRAME_OFF_SN];
type = frame[MCU_FRAME_OFF_PAYLOAD + MCU_OTA_PL_TYPE];
if (s_ota.state != MCU_OTA_ST_IDLE)
{
mcu_ota_reply_app_result(MCU_CMD_OTA_NOTIFY_ACK, sn,
MCU_RESULT_OTA_STATE_ERR);
return;
}
s_ota.app_sn = sn;
memcpy(s_ota.ver, &frame[MCU_FRAME_OFF_PAYLOAD + MCU_OTA_PL_VER], 3);
s_ota.file_size = mcu_util_read_u32_be(&frame[MCU_FRAME_OFF_PAYLOAD + MCU_OTA_PL_SIZE]);
s_ota.file_crc32 = mcu_util_read_u32_be(&frame[MCU_FRAME_OFF_PAYLOAD + MCU_OTA_PL_CRC32]);
if (type == MCU_OTA_FW_TYPE_MCU)
{
memcpy(mcu_pl, &frame[MCU_FRAME_OFF_PAYLOAD + MCU_OTA_PL_VER],
MCU_OTA_UART_NOTIFY_PLEN);
if (mcu_protocol_send_cmd_sn(MCU_CMD_OTA_NOTIFY, sn,
mcu_pl, MCU_OTA_UART_NOTIFY_PLEN) < 0)
{
mcu_ota_reply_app_result(MCU_CMD_OTA_NOTIFY_ACK, sn,
MCU_RESULT_FAIL);
return;
}
s_ota.type = MCU_OTA_TYPE_MCU;
s_ota.state = MCU_OTA_ST_MCU_WAIT_NOTIFY;
mcu_ota_arm_uart_wait(MCU_CMD_OTA_NOTIFY_ACK, sn);
log_info("MCU OTA notify size=%u", s_ota.file_size);
return;
}
if (type != MCU_OTA_FW_TYPE_BLE)
{
mcu_ota_reply_app_result(MCU_CMD_OTA_NOTIFY_ACK, sn,
MCU_RESULT_PARAM_ERR);
return;
}
if (mcu_ota_ble_ver_low(s_ota.ver))
{
mcu_ota_reply_app_result(MCU_CMD_OTA_NOTIFY_ACK, sn,
MCU_RESULT_OTA_VER_LOW);
return;
}
ret = usr_ota_start(s_ota.file_size, s_ota.file_crc32);
if (ret == USR_OTA_ERR_SPACE)
{
mcu_ota_reply_ble_notify(MCU_RESULT_OTA_SPACE_ERR);
return;
}
if (ret != USR_OTA_OK)
{
mcu_ota_reply_ble_notify(MCU_RESULT_OTA_FAIL);
return;
}
s_ota.type = MCU_OTA_TYPE_BLE;
s_ota.state = MCU_OTA_ST_BLE_WAIT_WS;
/* 从此刻起算 500ms0x14 没发出去时 push_bit6 不会写时间戳,避免 ws_tx_ms=0 立刻超时 */
s_ota.ws_tx_ms = mcu_util_get_ms();
if (!mcu_ota_push_bit6())
{
log_debug("Bit6 push busy, retry later");
}
}
/**
* @brief APP 0x32MCU 透传
* @param frame 帧
* @param len 长度
* @param sn SN
* @param frag 分片序号
*/
static void mcu_ota_handle_data_mcu(const uint8_t *frame, uint16_t len,
uint8_t sn, uint16_t frag)
{
if (s_ota.state != MCU_OTA_ST_MCU_WRITE)
{
mcu_ota_reply_app_result(MCU_CMD_OTA_DATA_ACK, sn,
MCU_RESULT_OTA_STATE_ERR);
return;
}
if (frag != s_ota.expect_frag)
{
log_error("MCU frag %u expect %u", frag, s_ota.expect_frag);
mcu_ota_reply_app_result(MCU_CMD_OTA_DATA_ACK, sn,
MCU_RESULT_OTA_FILE_ERR);
mcu_ota_fail_stop(MCU_RESULT_OTA_FILE_ERR);
return;
}
if (mcu_protocol_send_raw(frame, len) < 0)
{
mcu_ota_reply_app_result(MCU_CMD_OTA_DATA_ACK, sn, MCU_RESULT_FAIL);
mcu_ota_fail_stop(MCU_RESULT_FAIL);
return;
}
s_ota.state = MCU_OTA_ST_MCU_WAIT_DATA;
mcu_ota_arm_uart_wait(MCU_CMD_OTA_DATA_ACK, sn);
s_ota.last_data_ms = s_ota.uart_wait.tx_ms;
}
/**
* @brief APP 0x32:喂入模组 dual_bank
* @param frame 帧
* @param len 长度
* @param sn SN
* @param frag 分片序号
*/
static void mcu_ota_handle_data_ble(const uint8_t *frame, uint16_t len,
uint8_t sn, uint16_t frag)
{
uint16_t data_len;
UsrOta_Ret_t ret;
if (s_ota.state != MCU_OTA_ST_BLE_WRITE)
{
mcu_ota_reply_app_result(MCU_CMD_OTA_DATA_ACK, sn,
MCU_RESULT_OTA_STATE_ERR);
return;
}
if (frag != s_ota.expect_frag)
{
log_error("BLE frag %u expect %u", frag, s_ota.expect_frag);
mcu_ota_reply_app_result(MCU_CMD_OTA_DATA_ACK, sn,
MCU_RESULT_OTA_FILE_ERR);
mcu_ota_fail_stop(MCU_RESULT_OTA_FILE_ERR);
return;
}
data_len = MCU_FRAME_PAYLOAD_LEN(len);
if (data_len < MCU_OTA_DATA_HDR_LEN)
{
mcu_ota_reply_app_result(MCU_CMD_OTA_DATA_ACK, sn, MCU_RESULT_PARAM_ERR);
return;
}
data_len = (uint16_t)(data_len - MCU_OTA_DATA_HDR_LEN);
ret = usr_ota_feed(&frame[MCU_FRAME_OFF_PAYLOAD + MCU_OTA_DATA_PL_BODY],
data_len);
s_ota.last_data_ms = mcu_util_get_ms();
if (ret == USR_OTA_OK)
{
s_ota.expect_frag++;
mcu_ota_reply_app_result(MCU_CMD_OTA_DATA_ACK, sn, MCU_RESULT_OK);
return;
}
if (ret == USR_OTA_FEED_PENDING)
{
s_ota.state = MCU_OTA_ST_BLE_WAIT_FEED;
return;
}
log_error("usr_ota_feed ret=%d", ret);
mcu_ota_reply_app_result(MCU_CMD_OTA_DATA_ACK, sn, MCU_RESULT_OTA_FAIL);
mcu_ota_fail_stop(MCU_RESULT_OTA_FAIL);
}
/**
* @brief 处理 APP 0x32
* @param frame 帧
* @param len 长度
*/
static void mcu_ota_handle_data(const uint8_t *frame, uint16_t len)
{
uint8_t sn;
uint16_t frag;
if (len < (MCU_FRAME_OVERHEAD + MCU_OTA_DATA_HDR_LEN))
{
mcu_ota_reply_app_result(MCU_CMD_OTA_DATA_ACK, frame[MCU_FRAME_OFF_SN],
MCU_RESULT_PARAM_ERR);
return;
}
sn = frame[MCU_FRAME_OFF_SN];
frag = mcu_util_read_u16_be(&frame[MCU_FRAME_OFF_PAYLOAD + MCU_OTA_DATA_PL_FRAG]);
s_ota.app_sn = sn;
if (s_ota.type == MCU_OTA_TYPE_MCU)
{
mcu_ota_handle_data_mcu(frame, len, sn, frag);
return;
}
if (s_ota.type == MCU_OTA_TYPE_BLE)
{
mcu_ota_handle_data_ble(frame, len, sn, frag);
return;
}
mcu_ota_reply_app_result(MCU_CMD_OTA_DATA_ACK, sn, MCU_RESULT_OTA_STATE_ERR);
}
/**
* @brief 处理 APP 0x35
* @param frame 帧
* @param len 长度
*/
static void mcu_ota_handle_complete_ack(const uint8_t *frame, uint16_t len)
{
uint8_t sn = frame[MCU_FRAME_OFF_SN];
uint16_t plen = MCU_FRAME_PAYLOAD_LEN(len);
if (s_ota.state == MCU_OTA_ST_MCU_WAIT_APP_DONE)
{
mcu_protocol_send_cmd_sn(MCU_CMD_OTA_COMPLETE_ACK, sn,
&frame[MCU_FRAME_OFF_PAYLOAD], plen);
mcu_ota_session_end(0);
return;
}
if (s_ota.state == MCU_OTA_ST_BLE_WAIT_APP_DONE)
{
s_ota.app_done_wait = 0;
usr_ota_request_reboot();
mcu_ota_session_end(1);
return;
}
log_info("ignore 0x35, state=%d", s_ota.state);
}
/**
* @brief 处理 APP 0x36
* @param sn 序列号
*/
static void mcu_ota_handle_stop(uint8_t sn)
{
/* boot 已写入:0x36 也按成功收尾复位,禁止 abort 清 bank */
if (s_ota.state == MCU_OTA_ST_BLE_WAIT_APP_DONE)
{
mcu_ota_reply_app_result(MCU_CMD_OTA_STOP_BLE_ACK, sn, MCU_RESULT_OK);
s_ota.app_done_wait = 0;
usr_ota_request_reboot();
mcu_ota_session_end(1);
return;
}
if (s_ota.type == MCU_OTA_TYPE_MCU)
{
mcu_protocol_send_cmd_sn(MCU_CMD_OTA_STOP_BLE, sn, NULL, 0);
mcu_ota_arm_uart_wait(MCU_CMD_OTA_STOP_BLE_ACK, sn);
return;
}
if (s_ota.type == MCU_OTA_TYPE_BLE)
{
usr_ota_abort();
mcu_ota_reply_app_result(MCU_CMD_OTA_STOP_BLE_ACK, sn, MCU_RESULT_OK);
mcu_ota_session_end(1);
return;
}
mcu_ota_reply_app_result(MCU_CMD_OTA_STOP_BLE_ACK, sn, MCU_RESULT_OK);
}
/**
* @brief usr_ota 写 Flash 完成回调(跑在 OTA 任务,不是协议任务)
* @param evt 写 Flash / 校验 / 烧 boot 结果
* @note 这就是回调:Flash 做完会喊这一声。
* 但不能在这里发 BLE(上下文不对),只把结果记下来,
* 等协议任务 mcu_ota_handle_usr_evt() 再回 0x33/0x34。
*/
static void mcu_ota_on_usr_evt(UsrOtaEvt_t evt)
{
s_ota.usr_evt = (uint8_t)evt;
s_ota.usr_evt_valid = 1;
}
/**
* @brief 协议任务处理 usr_ota 记下的结果,并回 APP
*
* @note 谁调用:mcu_ota_periodic() 每次先跑本函数。
* 事件从哪来:OTA 任务写完 Flash / 校验 / 写启动信息后,
* 回调 mcu_ota_on_usr_evt() 只把结果记在 usr_evt
* 真正发 55AA 必须在本函数(协议任务)里做。
*
* 和 APP 的对应关系:
* WRITE_OK 一包 4096 已写入备用 bank → 回 0x33 成功,继续收 0x32
* WRITE_FAIL 这一包写入失败 → 回 0x33 失败,再发 0x38 结束
* VERIFY_OK 整份固件校验通过 → 不回 APP,usr_ota 接着写启动信息
* VERIFY_FAIL 整份固件校验失败 → 发 0x38 结束
* BURN_OK 启动信息已写入 → 发 0x34,等 APP 0x35 再复位
* BURN_FAIL 启动信息写入失败 → 发 0x38 结束
*/
static void mcu_ota_handle_usr_evt(void)
{
UsrOtaEvt_t evt;
if (!s_ota.usr_evt_valid)
{
return;
}
evt = (UsrOtaEvt_t)s_ota.usr_evt;
s_ota.usr_evt_valid = 0;
switch (evt)
{
case USR_OTA_EVT_WRITE_OK:
/* 凑满 4096(或最后一包)时,handle_data_ble 当时没回 0x33
* 卡在 BLE_WAIT_FEED 等写 Flash。现在这一包已经写进备用 bank,
* 回 0x33 成功,切回 BLE_WRITE 继续收下一包 0x32。
* 未满 4096 的包在 feed 时已经回过 0x33,不会进 WAIT_FEED。 */
if (s_ota.state == MCU_OTA_ST_BLE_WAIT_FEED)
{
s_ota.expect_frag++;
s_ota.state = MCU_OTA_ST_BLE_WRITE;
s_ota.last_data_ms = mcu_util_get_ms();
mcu_ota_reply_app_result(MCU_CMD_OTA_DATA_ACK, s_ota.app_sn,
MCU_RESULT_OK);
}
break;
case USR_OTA_EVT_WRITE_FAIL:
/* 这一包 dual_bank 写入失败:先回 0x33 失败(0x25),再发 0x38 停会话 */
mcu_ota_reply_app_result(MCU_CMD_OTA_DATA_ACK, s_ota.app_sn,
MCU_RESULT_OTA_FAIL);
mcu_ota_fail_stop(MCU_RESULT_OTA_FAIL);
break;
case USR_OTA_EVT_VERIFY_FAIL:
/* 整份固件已写完,但 CRC 或 dual_bank 校验失败:发 0x38,错误码 0x24 */
mcu_ota_fail_stop(MCU_RESULT_OTA_FILE_ERR);
break;
case USR_OTA_EVT_BURN_FAIL:
/* 固件校验已过,但启动信息写入失败,复位后仍会跑旧 bank:发 0x38 */
mcu_ota_fail_stop(MCU_RESULT_OTA_FILE_ERR);
break;
case USR_OTA_EVT_BURN_OK: {
uint8_t pl[5];
/* 走到这里三件事都已完成:
* 1) 新固件全部写入备用 bank
* 2) CRC / dual_bank 校验通过
* 3) 启动信息已写入(下次复位从新 bank 启动)
* 发 SOC 0x34Type=BLE),然后等 APP 0x35;超时也会复位。 */
pl[0] = MCU_OTA_FW_TYPE_BLE;
pl[1] = MCU_RESULT_OK;
pl[2] = MCU_BLE_FW_VER_MAJOR;
pl[3] = MCU_BLE_FW_VER_MINOR;
pl[4] = MCU_BLE_FW_VER_PATCH;
s_ota.state = MCU_OTA_ST_BLE_WAIT_APP_DONE;
s_ota.app_done_wait = 1;
s_ota.app_done_ms = mcu_util_get_ms();
mcu_ota_reply_app(MCU_CMD_OTA_COMPLETE, s_ota.app_sn, pl, 5);
break;
}
case USR_OTA_EVT_VERIFY_OK:
/* 整份固件校验通过。usr_ota 任务里已经接着调 burn_boot
* 成功会再来 BURN_OK 才发 0x34,这里不回 APP。 */
break;
default:
break;
}
}
/*============================================================================*/
/* 公有函数 */
/*============================================================================*/
/**
* @brief 初始化
*/
void mcu_ota_init(void)
{
memset(&s_ota, 0, sizeof(s_ota));
usr_ota_init();
usr_ota_set_evt_cb(mcu_ota_on_usr_evt); /* 注册:写 Flash 完成时回调过来 */
log_info("init ok");
}
/**
* @brief 周期处理
* @note 先看有没有写 Flash 的待处理结果,再跑超时
*/
void mcu_ota_periodic(void)
{
uint32_t now = mcu_util_get_ms();
mcu_ota_handle_usr_evt();
if (s_ota.state == MCU_OTA_ST_IDLE)
{
return;
}
if (s_ota.state == MCU_OTA_ST_BLE_WAIT_WS)
{
if (!s_ota.ws_wait)
{
(void)mcu_ota_push_bit6();
}
if ((now - s_ota.ws_tx_ms) > MCU_OTA_WS_WAIT_MS)
{
log_error("Bit6 0x15 timeout, continue BLE OTA");
mcu_ota_ble_enter_write();
}
return;
}
if (s_ota.uart_wait.active &&
(now - s_ota.uart_wait.tx_ms) > MCU_OTA_UART_ACK_MS)
{
log_error("UART OTA ack timeout expect=0x%02X", s_ota.uart_wait.expect);
if (s_ota.state == MCU_OTA_ST_MCU_WAIT_DATA)
{
mcu_ota_reply_app_result(MCU_CMD_OTA_DATA_ACK, s_ota.app_sn,
MCU_RESULT_FAIL);
}
else if (s_ota.state == MCU_OTA_ST_MCU_WAIT_NOTIFY)
{
mcu_ota_reply_app_result(MCU_CMD_OTA_NOTIFY_ACK, s_ota.app_sn,
MCU_RESULT_FAIL);
}
mcu_ota_fail_stop(MCU_RESULT_FAIL);
return;
}
if (s_ota.state == MCU_OTA_ST_MCU_WAIT_APP_DONE &&
(now - s_ota.app_done_ms) > MCU_OTA_APP_ACK_MS)
{
log_error("APP 0x35 timeout after MCU 0x34, end session");
mcu_ota_session_end(0);
return;
}
if (s_ota.app_done_wait &&
(now - s_ota.app_done_ms) > MCU_OTA_APP_ACK_MS)
{
log_error("APP 0x35 timeout, reboot anyway");
s_ota.app_done_wait = 0;
usr_ota_request_reboot();
mcu_ota_session_end(1);
return;
}
if ((s_ota.state == MCU_OTA_ST_MCU_WRITE ||
s_ota.state == MCU_OTA_ST_BLE_WRITE) &&
(now - s_ota.last_data_ms) > MCU_OTA_IDLE_MS)
{
log_error("OTA data idle timeout");
mcu_ota_fail_stop(MCU_RESULT_FAIL);
}
}
/**
* @brief APP OTA 帧
* @param frame 帧
* @param len 长度
*/
void mcu_ota_on_app_frame(const uint8_t *frame, uint16_t len)
{
uint8_t cmd;
if (!frame || len < MCU_FRAME_OVERHEAD)
{
return;
}
cmd = frame[MCU_FRAME_OFF_CMD];
log_info("APP OTA cmd=0x%02X len=%u", cmd, len);
switch (cmd)
{
case MCU_CMD_OTA_NOTIFY:
mcu_ota_handle_notify(frame, len);
break;
case MCU_CMD_OTA_DATA:
mcu_ota_handle_data(frame, len);
break;
case MCU_CMD_OTA_COMPLETE_ACK:
mcu_ota_handle_complete_ack(frame, len);
break;
case MCU_CMD_OTA_STOP_BLE:
mcu_ota_handle_stop(frame[MCU_FRAME_OFF_SN]);
break;
default:
log_error("unexpected APP OTA cmd=0x%02X", cmd);
break;
}
}
/**
* @brief MCU OTA 帧
* @param frame 帧
* @param len 长度
*/
void mcu_ota_on_uart_frame(const uint8_t *frame, uint16_t len)
{
uint8_t cmd;
uint8_t sn;
uint8_t result;
if (!frame || len < MCU_FRAME_OVERHEAD)
{
return;
}
cmd = frame[MCU_FRAME_OFF_CMD];
sn = frame[MCU_FRAME_OFF_SN];
log_info("MCU OTA cmd=0x%02X sn=%u", cmd, sn);
if (mcu_ota_uart_wait_match(cmd, sn))
{
mcu_ota_clear_uart_wait();
}
switch (cmd)
{
case MCU_CMD_OTA_NOTIFY_ACK:
usr_le_send_mcu_frame((u8 *)frame, len);
result = (MCU_FRAME_PAYLOAD_LEN(len) > 0) ? frame[MCU_FRAME_OFF_PAYLOAD] : MCU_RESULT_FAIL;
if (result == MCU_RESULT_OK)
{
s_ota.state = MCU_OTA_ST_MCU_WRITE;
s_ota.expect_frag = 1;
s_ota.last_data_ms = mcu_util_get_ms();
}
else
{
mcu_ota_session_end(0);
}
break;
case MCU_CMD_OTA_DATA_ACK:
usr_le_send_mcu_frame((u8 *)frame, len);
result = (MCU_FRAME_PAYLOAD_LEN(len) > 0) ? frame[MCU_FRAME_OFF_PAYLOAD] : MCU_RESULT_FAIL;
if (result == MCU_RESULT_OK)
{
s_ota.expect_frag++;
s_ota.state = MCU_OTA_ST_MCU_WRITE;
s_ota.last_data_ms = mcu_util_get_ms();
}
else
{
mcu_ota_fail_stop(result);
}
break;
case MCU_CMD_OTA_COMPLETE: {
uint8_t soc[MCU_FRAME_MAX_LEN];
uint8_t pl[MCU_OTA_DONE_SOC_PLEN];
int32_t total;
/* MCU: Result+Ver3 → SOC: Type+Result+Ver3 */
pl[0] = MCU_OTA_FW_TYPE_MCU;
if (MCU_FRAME_PAYLOAD_LEN(len) >= MCU_OTA_DONE_UART_PLEN)
{
memcpy(&pl[1], &frame[MCU_FRAME_OFF_PAYLOAD], MCU_OTA_DONE_UART_PLEN);
}
else
{
pl[1] = MCU_RESULT_FAIL;
memset(&pl[2], 0, 3);
}
total = mcu_frame_pack(soc, sizeof(soc), MCU_CMD_OTA_COMPLETE,
sn, pl, MCU_OTA_DONE_SOC_PLEN);
if (total > 0)
{
usr_le_send_mcu_frame(soc, (uint16_t)total);
}
s_ota.state = MCU_OTA_ST_MCU_WAIT_APP_DONE;
s_ota.app_done_ms = mcu_util_get_ms();
break;
}
case MCU_CMD_OTA_STOP_BLE_ACK:
usr_le_send_mcu_frame((u8 *)frame, len);
mcu_ota_session_end(0);
break;
case MCU_CMD_OTA_STOP_MCU:
usr_le_send_mcu_frame((u8 *)frame, len);
if (s_ota.type == MCU_OTA_TYPE_MCU)
{
mcu_ota_session_end(0);
}
break;
default:
log_error("unexpected MCU OTA cmd=0x%02X", cmd);
break;
}
}
/**
* @brief 0x15
*/
void mcu_ota_on_work_status_ack(void)
{
if (s_ota.state == MCU_OTA_ST_BLE_WAIT_WS && s_ota.ws_wait)
{
log_info("Bit6 ack");
mcu_ota_ble_enter_write();
}
}
/**
* @brief 断连中止
*/
void mcu_ota_on_disconnect(void)
{
uint8_t type = s_ota.type;
if (s_ota.state == MCU_OTA_ST_IDLE)
{
return;
}
log_info("disconnect abort type=%u state=%d", type, s_ota.state);
/* boot 已写入:断连也走复位,禁止 abort 清 bank */
if (s_ota.state == MCU_OTA_ST_BLE_WAIT_APP_DONE)
{
s_ota.app_done_wait = 0;
usr_ota_request_reboot();
mcu_ota_session_end(1);
return;
}
if (type == MCU_OTA_TYPE_MCU)
{
mcu_protocol_send_cmd(MCU_CMD_OTA_STOP_BLE, NULL, 0);
}
else if (type == MCU_OTA_TYPE_BLE)
{
usr_ota_abort();
}
mcu_ota_session_end(type == MCU_OTA_TYPE_BLE ? 1 : 0);
}
/**
* @brief 是否有会话
* @return 1/0
*/
uint8_t mcu_ota_is_active(void)
{
return (s_ota.state != MCU_OTA_ST_IDLE) ? 1 : 0;
}
/**
* @brief 是否 BLE OTA
* @return 1/0
*/
uint8_t mcu_ota_is_ble_active(void)
{
return (s_ota.type == MCU_OTA_TYPE_BLE &&
s_ota.state != MCU_OTA_ST_IDLE)
? 1
: 0;
}
@@ -0,0 +1,62 @@
/******************************************************************************
* @file mcu_ota.h
* @brief OTA 协议适配:Type 分流、MCU 透传、模组 dual_bank 衔接
* @author cyWu <1917507415@qq.com>
* @date 2026.08.15
* @version V1.0.0
* @history
* - V1.0.0, 2026.08.15, cyWu, 首次发布
******************************************************************************/
#ifndef __MCU_OTA_H__
#define __MCU_OTA_H__
#include <stdint.h>
/**
* @brief 初始化 OTA 业务(含 usr_ota 引擎)
*/
void mcu_ota_init(void);
/**
* @brief 周期:UART/空闲超时、0x14 重试、usr_ota 事件、0x35 超时复位
*/
void mcu_ota_periodic(void);
/**
* @brief 处理 APP 下发的 OTA 帧(0x30/0x32/0x35/0x36
* @param frame 完整 55AA
* @param len 长度
*/
void mcu_ota_on_app_frame(const uint8_t *frame, uint16_t len);
/**
* @brief 处理 MCU 上报的 OTA 帧(0x31/0x33/0x34/0x37/0x38
* @param frame 完整 55AA
* @param len 长度
*/
void mcu_ota_on_uart_frame(const uint8_t *frame, uint16_t len);
/**
* @brief 0x15 到达(BLE OTA 置 Bit6 后)
*/
void mcu_ota_on_work_status_ack(void);
/**
* @brief BLE 断连:中止会话,不切 bank、不复位
*/
void mcu_ota_on_disconnect(void);
/**
* @brief 是否有 OTA 会话
* @return 1=是
*/
uint8_t mcu_ota_is_active(void);
/**
* @brief 是否模组(BLEOTA
* @return 1=是
*/
uint8_t mcu_ota_is_ble_active(void);
#endif /* __MCU_OTA_H__ */
@@ -23,12 +23,6 @@
#define LOG_INFO_ENABLE
#include "debug.h"
/*============================================================================*/
/* 外部符号 */
/*============================================================================*/
extern u16 usr_get_conn_service(void);
/*============================================================================*/
/* 私有宏 */
/*============================================================================*/
@@ -188,7 +182,7 @@ void mcu_version_init(void)
*/
void mcu_version_periodic(void)
{
uint8_t conn = (usr_get_conn_service() != 0) ? 1 : 0; /* 1=BLE 已连接 */
uint8_t conn = usr_ble_is_connected(); /* 1=BLE 已连接 */
uint8_t paired = usr_get_pair_flag(); /* 1=已配网/已绑定 */
uint32_t now = mcu_util_get_ms();
@@ -93,3 +93,23 @@ void mcu_bridge_on_network_time(uint64_t unix_s, int32_t gmtoff_s)
{
mcu_protocol_notify_network_time(unix_s, gmtoff_s);
}
/**
* @brief BLE 断连:仅在 OTA 进行中入队,避免 adv 周期空转塞队列
*/
void mcu_bridge_on_ble_disconnected(void)
{
if (!mcu_business_ota_is_active()) {
return;
}
mcu_protocol_notify_disconnected();
}
/**
* @brief 是否有 OTA 会话
* @return 1/0
*/
uint8_t mcu_bridge_ota_is_active(void)
{
return mcu_business_ota_is_active();
}
@@ -12,6 +12,7 @@
* 本文件不再包含任何实现逻辑,只做“组合根”,对外 API 保持不变,
* 外部调用方(app_main.c / usr_le_adv.c / usr_le_recieve.c 等)无需改动
* - V2.1.0, 2026.08.13, cyWu, P1 增加网络时间入口;业务子模块扩到管理/对时/版本
* - V2.2.0, 2026.08.15, cyWu, P2 OTA 入口走本门面,usr_le_code 不再 include mcu_ota.h
******************************************************************************/
#ifndef __MCU_BRIDGE_H__
@@ -38,11 +39,16 @@
/* ├── mcu_mgmt.c/h 模组管理(0x16/0x18/0x1A */
/* ├── mcu_time.c/h 对时(0x1C/0x1D,写入 usr_rtc */
/* ├── mcu_version.c/h MCU 信息 + SOC 0x41 上报 */
/* ├── mcu_ota.c/h P2 OTAType 分流 / MCU 透传 / Bit6 */
/* └── mcu_business.c/h 业务门面:协调子模块,供协议层调用 */
/* */
/* 依赖方向(单向,禁止反向 include): */
/* mcu_bridge → protocol → business(子模块) → business(门面) */
/* business 子模块只经 mcu_protocol_send_cmd 等窄接口回调协议层发送 */
/* usr_le_code / app_main → mcu_bridge.h */
/* mcu_bridge → mcu_protocol + mcu_business(门面) */
/* mcu_protocol → mcu_business.h(不 include 业务子模块) */
/* mcu_business 门面 → identity/pairing/link/mgmt/time/version/ota */
/* 业务子模块互不 include;只经 mcu_protocol_send_* 回调协议层发送 */
/* mcu_ota → usr_ota(协议无关 dual_bank 引擎,不感知 55AA */
/*============================================================================*/
/*============================================================================*/
@@ -122,4 +128,16 @@ void mcu_bridge_on_gateway_report_ack(uint16_t cmd,
*/
void mcu_bridge_on_network_time(uint64_t unix_s, int32_t gmtoff_s);
/**
* @brief BLE 断连:入队后中止 OTA(不切 bank、不复位;烧 boot 成功后仍复位)
* @note 可从 adv 周期调用;无会话时立即返回,不会塞满事件队列
*/
void mcu_bridge_on_ble_disconnected(void);
/**
* @brief 是否有 OTA 会话
* @return 1=是
*/
uint8_t mcu_bridge_ota_is_active(void);
#endif /* __MCU_BRIDGE_H__ */
@@ -80,7 +80,7 @@ typedef enum {
MCU_CMD_AUDIO = 0x26,
MCU_CMD_AUDIO_ACK = 0x27,
/* OTA(后续扩展) */
/* OTA */
MCU_CMD_OTA_NOTIFY = 0x30,
MCU_CMD_OTA_NOTIFY_ACK = 0x31,
MCU_CMD_OTA_DATA = 0x32,
@@ -97,25 +97,32 @@ typedef enum {
MCU_CMD_GET_MCU_INFO_ACK = 0x41
} McuCmd_t;
/*============================================================================*/
/* OTA 固件类型(SOC 0x30/0x34 的 Type 字节;UART 侧 0x30 无此字段) */
/*============================================================================*/
#define MCU_OTA_FW_TYPE_MCU (0x01u)
#define MCU_OTA_FW_TYPE_BLE (0x02u)
/*============================================================================*/
/* 结果码 */
/*============================================================================*/
typedef enum {
MCU_RESULT_OK = 0x00,
MCU_RESULT_PARAM_ERR = 0x01,
MCU_RESULT_BUSY = 0x02,
MCU_RESULT_NOT_SUPPORT = 0x03,
MCU_RESULT_CRC_ERR = 0x04,
MCU_RESULT_FAIL = 0x05,
MCU_RESULT_OK = 0x00, /**< 成功 */
MCU_RESULT_PARAM_ERR = 0x01, /**< 参数错误 */
MCU_RESULT_BUSY = 0x02, /**< 忙 */
MCU_RESULT_NOT_SUPPORT = 0x03, /**< 不支持 */
MCU_RESULT_CRC_ERR = 0x04, /**< 校验错误 */
MCU_RESULT_FAIL = 0x05, /**< 失败 */
/* OTA 专用 */
MCU_RESULT_OTA_NO_NEED = 0x20,
MCU_RESULT_OTA_VER_LOW = 0x21,
MCU_RESULT_OTA_SPACE_ERR = 0x22,
MCU_RESULT_OTA_STATE_ERR = 0x23,
MCU_RESULT_OTA_FILE_ERR = 0x24,
MCU_RESULT_OTA_FAIL = 0x25
MCU_RESULT_OTA_NO_NEED = 0x20, /**< 不需要 */
MCU_RESULT_OTA_VER_LOW = 0x21, /**< 版本过低 */
MCU_RESULT_OTA_SPACE_ERR = 0x22, /**< 空间不足 */
MCU_RESULT_OTA_STATE_ERR = 0x23, /**< 状态错误 */
MCU_RESULT_OTA_FILE_ERR = 0x24, /**< 文件错误 */
MCU_RESULT_OTA_FAIL = 0x25, /**< 失败 */
} McuResult_t;
#endif /* __MCU_CMD_H__ */
@@ -138,3 +138,37 @@ int8_t mcu_frame_feed(uint8_t byte, uint8_t *out, uint16_t *out_len)
}
return 0;
}
/**
* @brief 校验一帧完整 55AA(非流式)
* @param data 完整帧
* @param len 实际长度
* @param max_len 允许的最大帧长
* @return 1=合法,0=非法
*/
uint8_t mcu_frame_check(const uint8_t *data, uint16_t len, uint16_t max_len)
{
uint16_t len_field;
uint16_t total;
if (!data || len < MCU_FRAME_OVERHEAD || max_len < MCU_FRAME_OVERHEAD) {
return 0;
}
if (data[0] != MCU_PKT_HEAD_0 || data[1] != MCU_PKT_HEAD_1) {
return 0;
}
len_field = mcu_util_read_u16_be(&data[2]);
/* LEN = CMD + SN + payload + CKSUM,至少 3 */
if (len_field < 3u) {
return 0;
}
total = (uint16_t)(len_field + 4u);
if (total != len || total > max_len) {
return 0;
}
if (mcu_util_checksum(data, total) != data[total - 1u]) {
return 0;
}
return 1;
}
@@ -6,6 +6,7 @@
* @version V1.0.0
* @history
* - V1.0.0, 2026.08.06, cyWu, 首次发布
* - V1.1.0, 2026.08.15, cyWu, 增加整包校验 mcu_frame_check,供 APP/BLE 完整帧使用
******************************************************************************/
#ifndef __MCU_FRAME_H__
@@ -21,6 +22,17 @@
#define MCU_PKT_HEAD_0 (0x55)
#define MCU_PKT_HEAD_1 (0xAA)
#define MCU_FRAME_MAX_LEN (128)
#define MCU_OTA_FRAME_MAX_LEN (640) /**< 仅 OTA 0x32512 载荷 + 帧头约 523 */
#define MCU_FRAME_OFF_CMD (4) /**< 命令字 */
#define MCU_FRAME_OFF_SN (5) /**< 序列号 */
#define MCU_FRAME_OFF_PAYLOAD (6) /**< 载荷起始 */
#define MCU_FRAME_OVERHEAD (7) /**< 55AA + Len2 + Cmd + Sn + Cks,无载荷时的最短帧 */
/** 由整帧长度得到载荷长度;帧不完整则返回 0 */
#define MCU_FRAME_PAYLOAD_LEN(total_len) \
(((total_len) >= MCU_FRAME_OVERHEAD) ? \
(uint16_t)((total_len) - MCU_FRAME_OVERHEAD) : 0u)
/*============================================================================*/
/* 外部接口 */
@@ -49,6 +61,17 @@ int32_t mcu_frame_pack(uint8_t *out, uint16_t out_max,
*/
int8_t mcu_frame_feed(uint8_t byte, uint8_t *out, uint16_t *out_len);
/**
* @brief 校验一帧完整 55AA(非流式)
* @param data 完整帧
* @param len 实际长度
* @param max_len 允许的最大帧长(普通帧 128OTA 0x32 为 640
* @return 1=合法(头/LEN 与 len 一致/校验和),0=非法
* @note UART 是字节流用 mcu_frame_feedAPP/BLE 已是整包,用本函数,
* 不要喂给 feed,以免和 UART 解析状态机抢同一份静态缓冲
*/
uint8_t mcu_frame_check(const uint8_t *data, uint16_t len, uint16_t max_len);
/**
* @brief 复位帧解析状态机
*/
@@ -62,6 +62,13 @@ typedef struct
uint16_t len;
} McuPending_t;
typedef struct
{
uint8_t buf[MCU_OTA_FRAME_MAX_LEN];
uint16_t len;
uint8_t busy;
} McuOtaBypass_t;
typedef enum
{
MCU_EVT_UART_FRAME = 0,
@@ -69,7 +76,9 @@ typedef enum
MCU_EVT_GATEWAY_ACK,
MCU_EVT_BLE_PAIRED,
MCU_EVT_BLE_PAIR_CLEARED,
MCU_EVT_NET_TIME
MCU_EVT_NET_TIME,
MCU_EVT_APP_OTA_DATA,
MCU_EVT_BLE_DISCONNECTED
} McuEvtType_t;
typedef struct
@@ -98,6 +107,10 @@ typedef struct
McuPending_t ble_pending; /* 模组主动 UART(心跳/0x14/0x40/0x1A),一次只能占一个 */
uint8_t report_waiting; /* 1=MCU 0x07 已转网关,等 ACK 后再回 0x08 */
uint8_t report_sn; /* 等待回 0x08 的那条 0x07 的 SN */
uint8_t parse_out[MCU_FRAME_MAX_LEN]; /* UART 组帧临时输出 */
McuOtaBypass_t ota_rx; /* APP 0x32 旁路,不进 128 事件槽 */
volatile uint8_t disc_posted; /* 断连事件已在队列中,避免 adv 周期重复入队 */
volatile uint8_t lp_busy; /* 协议任务忙,禁止 sleep */
} McuProtocol_t;
typedef struct
@@ -115,8 +128,6 @@ typedef struct
/*============================================================================*/
static McuProtocol_t s_proto;
static uint8_t s_parse_out[MCU_FRAME_MAX_LEN];
static volatile u8 s_protocol_busy = 0;
static McuEvtQueue_t s_evt_q;
/*============================================================================*/
@@ -226,6 +237,14 @@ static void mcu_protocol_dispatch_event(const McuEvt_t *evt)
case MCU_EVT_NET_TIME:
mcu_business_on_network_time(evt->unix_s, evt->gmtoff_s);
break;
case MCU_EVT_APP_OTA_DATA:
mcu_protocol_handle_app_frame(s_proto.ota_rx.buf, s_proto.ota_rx.len);
s_proto.ota_rx.busy = 0;
break;
case MCU_EVT_BLE_DISCONNECTED:
s_proto.disc_posted = 0;
mcu_business_on_ble_disconnected();
break;
default:
log_error("unknown evt type=%d", evt->type);
break;
@@ -247,13 +266,13 @@ static void mcu_protocol_task(void *arg)
{
os_sem_pend(&s_evt_q.sem, (int)MCU_PROTOCOL_TICK_OS);
s_protocol_busy = 1;
s_proto.lp_busy = 1;
while (mcu_protocol_pop_event(&evt))
{
mcu_protocol_dispatch_event(&evt);
}
mcu_protocol_periodic();
s_protocol_busy = 0;
s_proto.lp_busy = 0;
}
}
@@ -263,7 +282,7 @@ static void mcu_protocol_task(void *arg)
*/
static u8 mcu_protocol_lp_idle_query(void)
{
return s_protocol_busy ? 0 : 1;
return s_proto.lp_busy ? 0 : 1;
}
REGISTER_LP_TARGET(mcu_protocol_lp_target) = {
@@ -323,6 +342,37 @@ int32_t mcu_protocol_send_cmd(uint8_t cmd, const uint8_t *payload, uint16_t plen
return mcu_protocol_send_frame(cmd, mcu_protocol_next_sn(), payload, plen);
}
/**
* @brief 指定 SN 发送
* @param cmd 命令
* @param sn 序列号
* @param payload 载荷
* @param plen 长度
* @return 帧长;<0 失败
*/
int32_t mcu_protocol_send_cmd_sn(uint8_t cmd, uint8_t sn,
const uint8_t *payload, uint16_t plen)
{
return mcu_protocol_send_frame(cmd, sn, payload, plen);
}
/**
* @brief 原样转发完整帧
* @param data 帧
* @param len 长度
* @return 帧长;<0 失败
*/
int32_t mcu_protocol_send_raw(const uint8_t *data, uint16_t len)
{
if (!data || len == 0) {
return -1;
}
if (mcu_uart_send(data, len) != MCU_UART_OK) {
return -1;
}
return (int32_t)len;
}
/**
* @brief 发送并占用 ble_pending
* @param cmd 命令
@@ -387,6 +437,23 @@ static void mcu_protocol_send_ack(uint8_t cmd, uint8_t sn, uint8_t result)
mcu_protocol_send_frame(cmd, sn, &result, 1);
}
/**
* @brief 组一小帧回 APPOTA 独占时拒 0x03/0x05
* @param cmd ACK 命令
* @param sn SN
* @param result 结果
*/
static void mcu_protocol_reply_app_result(uint8_t cmd, uint8_t sn, uint8_t result)
{
uint8_t buf[MCU_FRAME_MAX_LEN];
int32_t total;
total = mcu_frame_pack(buf, sizeof(buf), cmd, sn, &result, 1);
if (total > 0) {
usr_le_send_mcu_frame(buf, (uint16_t)total);
}
}
/**
* @brief 强制重推工作状态
*/
@@ -410,13 +477,13 @@ static void mcu_protocol_handle_pair_cfg(const uint8_t *frame, uint16_t len)
uint8_t mode;
uint16_t timeout_s;
if (len < 10)
if (len < (MCU_FRAME_OVERHEAD + 3u))
{
return;
}
sn = frame[5];
mode = frame[6];
timeout_s = mcu_util_read_u16_be(&frame[7]);
sn = frame[MCU_FRAME_OFF_SN];
mode = frame[MCU_FRAME_OFF_PAYLOAD];
timeout_s = mcu_util_read_u16_be(&frame[MCU_FRAME_OFF_PAYLOAD + 1]);
/* 协议只认 mode=1/2,其它直接回参数错误 */
if (mode != 1 && mode != 2)
@@ -451,7 +518,7 @@ static void mcu_protocol_handle_get_time(const uint8_t *frame, uint16_t len)
{
return;
}
sn = frame[5];
sn = frame[MCU_FRAME_OFF_SN];
mcu_business_build_time_ack(payload, &plen);
if (plen == 0)
{
@@ -470,7 +537,7 @@ static void mcu_protocol_handle_get_time(const uint8_t *frame, uint16_t len)
*/
static void mcu_protocol_handle_report(const uint8_t *frame, uint16_t len)
{
uint8_t sn = frame[5];
uint8_t sn = frame[MCU_FRAME_OFF_SN];
/* 已配网也会在 CCC 之前连上;这时发出去会失败,不置 waiting,让 MCU 自己重发 0x07 */
if (!usr_ble_is_notify_ready())
@@ -499,7 +566,7 @@ static void mcu_protocol_handle_frame(const uint8_t *frame, uint16_t len)
uint8_t cmd;
uint8_t sn;
if (!frame || len < 6)
if (!frame || len < MCU_FRAME_OVERHEAD)
{
return;
}
@@ -510,8 +577,8 @@ static void mcu_protocol_handle_frame(const uint8_t *frame, uint16_t len)
s_proto.hb_waiting = 0;
s_proto.mcu_comm_alarm = 0;
cmd = frame[4];
sn = frame[5];
cmd = frame[MCU_FRAME_OFF_CMD];
sn = frame[MCU_FRAME_OFF_SN];
log_debug("MCU RX cmd=0x%02X sn=%u len=%u", cmd, sn, len);
/* APP 透传在等这条 ACK(命令+SN 都对上):转给 APP,不再往下分发 */
@@ -547,6 +614,10 @@ static void mcu_protocol_handle_frame(const uint8_t *frame, uint16_t len)
mcu_protocol_handle_pair_cfg(frame, len);
break;
case MCU_CMD_REPORT:
if (mcu_business_ota_is_ble_active()) {
log_info("drop MCU 0x07 during BLE OTA");
return;
}
mcu_protocol_handle_report(frame, len); /* 转网关,等 ACK 再回 0x08 */
break;
case MCU_CMD_CONTROL_ACK:
@@ -572,14 +643,30 @@ static void mcu_protocol_handle_frame(const uint8_t *frame, uint16_t len)
mcu_business_on_mcu_reboot_ack(frame, len);
break;
case MCU_CMD_HEARTBEAT_ACK:
break;
case MCU_CMD_WORK_STATUS_ACK:
break; /* pending 已在上面释放,无需再处理载荷 */
default:
/* 工厂/拍照/录音/音频:协议有命令字但模组未实现,回不支持 */
if (cmd == MCU_CMD_FACTORY || cmd == MCU_CMD_PHOTO ||
cmd == MCU_CMD_RECORD || cmd == MCU_CMD_AUDIO)
{
mcu_business_on_ota_ws_ack();
break;
case MCU_CMD_OTA_NOTIFY_ACK:
case MCU_CMD_OTA_DATA_ACK:
case MCU_CMD_OTA_COMPLETE:
case MCU_CMD_OTA_STOP_BLE_ACK:
case MCU_CMD_OTA_STOP_MCU:
mcu_business_on_mcu_ota_frame(frame, len);
break;
case MCU_CMD_FACTORY:
case MCU_CMD_PHOTO:
case MCU_CMD_RECORD:
case MCU_CMD_AUDIO:
if (mcu_business_ota_is_active()) {
log_info("drop MCU cmd=0x%02X during OTA", cmd);
break;
}
mcu_protocol_send_ack((uint8_t)(cmd + 1), sn, MCU_RESULT_NOT_SUPPORT);
break;
default:
if (mcu_business_ota_is_active()) {
log_info("drop MCU cmd=0x%02X during OTA", cmd);
}
break;
}
@@ -596,26 +683,55 @@ static void mcu_protocol_handle_app_frame(const uint8_t *data, uint16_t len)
uint8_t sn;
uint8_t expect_ack;
cmd = data[4];
sn = data[5];
cmd = data[MCU_FRAME_OFF_CMD];
sn = data[MCU_FRAME_OFF_SN];
/* 0x03/0x05 必须等 MCU ACK 再回 APP,所以进 pending 可超时重发。
* 其它 APP 帧直接透传到 UART,模组不管应答。 */
if (cmd == MCU_CMD_CONTROL)
switch (cmd)
{
case MCU_CMD_OTA_NOTIFY:
case MCU_CMD_OTA_DATA:
case MCU_CMD_OTA_COMPLETE_ACK:
case MCU_CMD_OTA_STOP_BLE:
mcu_business_on_app_ota_frame(data, len);
return;
/* 反向 OTA 命令:模组→APP,或会话已结束后的 0x39,禁止透传到 UART */
case MCU_CMD_OTA_NOTIFY_ACK:
case MCU_CMD_OTA_DATA_ACK:
case MCU_CMD_OTA_COMPLETE:
case MCU_CMD_OTA_STOP_BLE_ACK:
case MCU_CMD_OTA_STOP_MCU:
case MCU_CMD_OTA_STOP_MCU_ACK:
log_info("ignore APP OTA dir cmd=0x%02X", cmd);
return;
case MCU_CMD_CONTROL:
if (mcu_business_ota_is_active()) {
mcu_protocol_reply_app_result(MCU_CMD_CONTROL_ACK, sn, MCU_RESULT_BUSY);
return;
}
expect_ack = MCU_CMD_CONTROL_ACK;
break;
case MCU_CMD_READ:
if (mcu_business_ota_is_active()) {
mcu_protocol_reply_app_result(MCU_CMD_READ_ACK, sn, MCU_RESULT_BUSY);
return;
}
else if (cmd == MCU_CMD_READ)
{
expect_ack = MCU_CMD_READ_ACK;
break;
default:
if (mcu_business_ota_is_active()) {
log_info("drop APP cmd=0x%02X during OTA", cmd);
return;
}
else
{
/* 其它 APP 帧直接透传到 UART,模组不管应答 */
mcu_uart_send(data, len);
return;
}
/* 上一条 0x03/0x05 还没回,丢掉新的,避免 SN 对乱 */
/* 0x03/0x05:等 MCU ACK 再回 APP,进 pending 可超时重发 */
if (s_proto.app_pending.active)
{
log_error("app frame busy, drop cmd=0x%02X", cmd);
@@ -686,7 +802,7 @@ static void mcu_protocol_try_heartbeat(void)
int32_t total;
/* 身份都还没通,先别发心跳 */
if (!mcu_business_is_identity_ready())
if (!mcu_business_is_identity_ready() || mcu_business_ota_is_active())
{
return;
}
@@ -756,7 +872,7 @@ static void mcu_protocol_try_work_status(void)
uint32_t now;
uint8_t ws;
if (!mcu_business_is_identity_ready())
if (!mcu_business_is_identity_ready() || mcu_business_ota_is_active())
{
return;
}
@@ -942,12 +1058,12 @@ void mcu_protocol_on_uart_rx(const uint8_t *data, uint16_t len)
for (i = 0; i < len; i++)
{
ret = mcu_frame_feed(data[i], s_parse_out, &out_len);
ret = mcu_frame_feed(data[i], s_proto.parse_out, &out_len);
if (ret == 1)
{
evt.type = MCU_EVT_UART_FRAME;
evt.len = out_len;
memcpy(evt.buf, s_parse_out, out_len);
memcpy(evt.buf, s_proto.parse_out, out_len);
printf("UART valid data:");
printf_buf(evt.buf, evt.len);
mcu_protocol_post_event(&evt);
@@ -963,15 +1079,38 @@ void mcu_protocol_on_uart_rx(const uint8_t *data, uint16_t len)
void mcu_protocol_on_app_frame(const uint8_t *data, uint16_t len)
{
McuEvt_t evt;
uint8_t cmd;
uint16_t max_len;
if (!s_proto.initialized || !data || len < 6 || len > sizeof(evt.buf))
{
log_error("app frame invalid len=%u", len);
if (!s_proto.initialized || !data) {
log_error("app frame invalid");
return;
}
if (data[0] != MCU_PKT_HEAD_0 || data[1] != MCU_PKT_HEAD_1)
{
log_error("app frame head err");
/* 先看命令字,决定最大帧长;再做与 UART 同等的头/LEN/校验和检查 */
if (len < MCU_FRAME_OVERHEAD) {
log_error("app frame too short len=%u", len);
return;
}
cmd = data[MCU_FRAME_OFF_CMD];
max_len = (cmd == MCU_CMD_OTA_DATA) ? MCU_OTA_FRAME_MAX_LEN : MCU_FRAME_MAX_LEN;
if (!mcu_frame_check(data, len, max_len)) {
log_error("app frame check fail cmd=0x%02X len=%u", cmd, len);
return;
}
/* 0x32 可能超过 128,走独立缓冲,事件队列只带通知 */
if (cmd == MCU_CMD_OTA_DATA) {
if (s_proto.ota_rx.busy) {
log_error("OTA data buf busy, drop");
return;
}
memcpy(s_proto.ota_rx.buf, data, len);
s_proto.ota_rx.len = len;
s_proto.ota_rx.busy = 1;
memset(&evt, 0, sizeof(evt));
evt.type = MCU_EVT_APP_OTA_DATA;
mcu_protocol_post_event(&evt);
return;
}
@@ -1040,3 +1179,22 @@ void mcu_protocol_notify_network_time(uint64_t unix_s, int32_t gmtoff_s)
evt.gmtoff_s = gmtoff_s;
mcu_protocol_post_event(&evt);
}
/**
* @brief BLE 断连入队(adv 周期可调,内部合并重复)
*/
void mcu_protocol_notify_disconnected(void)
{
McuEvt_t evt;
if (s_proto.disc_posted) {
return;
}
s_proto.disc_posted = 1;
memset(&evt, 0, sizeof(evt));
evt.type = MCU_EVT_BLE_DISCONNECTED;
if (!mcu_protocol_post_event(&evt)) {
s_proto.disc_posted = 0;
log_error("disconnect evt queue full");
}
}
@@ -8,6 +8,7 @@
* - V1.0.0, 2026.08.12, cyWu, 从 mcu_bridge.c 拆出,仅保留协议机制,
* 不含任何业务判断(身份/配网/连接状态见 ../business
* - V1.1.0, 2026.08.13, cyWu, P1:管理命令分发、pending 发送、网关时间入队
* - V1.2.0, 2026.08.15, cyWu, P2OTA 大包旁路、按 SN 发送、原始帧转发
******************************************************************************/
#ifndef __MCU_PROTOCOL_H__
@@ -70,6 +71,11 @@ void mcu_protocol_notify_gateway_ack(uint16_t cmd,
*/
void mcu_protocol_notify_network_time(uint64_t unix_s, int32_t gmtoff_s);
/**
* @brief BLE 断连通知(入队,交由协议任务处理 OTA 中止;可从 adv 周期调用)
*/
void mcu_protocol_notify_disconnected(void);
/*============================================================================*/
/* 供 business 层调用的协议发送接口 */
/*============================================================================*/
@@ -83,6 +89,25 @@ void mcu_protocol_notify_network_time(uint64_t unix_s, int32_t gmtoff_s);
*/
int32_t mcu_protocol_send_cmd(uint8_t cmd, const uint8_t *payload, uint16_t plen);
/**
* @brief 使用指定 SN 组帧发送(OTA 转发需与 APP SN 一致)
* @param cmd 命令字
* @param sn 序列号
* @param payload 载荷,可为 NULL
* @param plen 载荷长度
* @return 帧长;<0 失败
*/
int32_t mcu_protocol_send_cmd_sn(uint8_t cmd, uint8_t sn,
const uint8_t *payload, uint16_t plen);
/**
* @brief 原样发送完整 55AA(用于 0x32 大包,不经 128 组帧缓冲)
* @param data 完整帧
* @param len 长度
* @return 帧长;<0 失败
*/
int32_t mcu_protocol_send_raw(const uint8_t *data, uint16_t len);
/**
* @brief 发送并占用 ble_pending,等待 expect_ack
* @param cmd 命令字
@@ -20,7 +20,7 @@
#include "asm/gpio.h"
#include "asm/power/power_api.h"
#include "mcu_uart.h"
#include "mcu_bridge.h"
#include "mcu_protocol.h"
#include "mcu_frame.h"
#define LOG_TAG_CONST APP
@@ -53,7 +53,7 @@ static volatile u8 s_usr_uart_recieve = 0;
/*============================================================================*/
/**
* @brief UART 接收任务:阻塞读串口,喂入协议
* @brief UART 接收任务:阻塞读串口,喂入协议
* @param arg 未使用
*/
static void mcu_uart_rx_task(void *arg)
@@ -75,7 +75,7 @@ static void mcu_uart_rx_task(void *arg)
log_debug("uart rx cnt=%d", rxcnt);
printf("uart rx data:");
printf_buf(s_rx_tmp, rxcnt);
mcu_bridge_on_uart_rx(s_rx_tmp, (uint16_t)rxcnt);
mcu_protocol_on_uart_rx(s_rx_tmp, (uint16_t)rxcnt);
}
}
}
@@ -146,7 +146,7 @@ McuUart_Ret_t mcu_uart_send(const uint8_t *data, uint16_t len)
if (!s_initialized || !s_uart_bus) {
return MCU_UART_ERR_NOT_INIT;
}
if (!data || len == 0 || len > MCU_FRAME_MAX_LEN) {
if (!data || len == 0 || len > MCU_OTA_FRAME_MAX_LEN) {
return MCU_UART_ERR_PARAM;
}
+481
View File
@@ -0,0 +1,481 @@
/******************************************************************************
* @file usr_ota.c
* @brief 模组双备份 OTA 引擎实现(独立任务调用 dual_bank API
* @author cyWu <1917507415@qq.com>
* @date 2026.08.15
* @version V1.1.0
* @history
* - V1.0.0, 2026.08.15, cyWu, 首次发布
* - V1.1.0, 2026.08.15, cyWu, 会话状态收拢到 UsrOta_t;失败路径共用退出
******************************************************************************/
#include "system/includes.h"
#include "asm/cpu.h"
#include "asm/clock.h"
#include "asm/power/power_api.h"
#include "dual_bank_updata_api.h"
#include "usr_ota.h"
#include <string.h>
#define LOG_TAG_CONST APP
#define LOG_TAG "[USR_OTA]"
#define LOG_ERROR_ENABLE
#define LOG_DEBUG_ENABLE
#define LOG_INFO_ENABLE
#include "debug.h"
/*============================================================================*/
/* 私有宏 */
/*============================================================================*/
#define USR_OTA_CRC32_POLY (0xEDB88320u)
#define USR_OTA_REBOOT_DELAY_MS (300u)
typedef enum
{
USR_OTA_ST_IDLE = 0, /**< 空闲 */
USR_OTA_ST_WRITE, /**< 写入 */
USR_OTA_ST_VERIFY, /**< 校验 */
USR_OTA_ST_BURN, /**< 烧 boot */
USR_OTA_ST_WAIT_REBOOT /**< 等待重启 */
} UsrOtaState_t;
extern u32 dual_bank_update_verify_without_crc(void);
/*============================================================================*/
/* 私有变量 */
/*============================================================================*/
typedef struct {
uint8_t initialized; /**< 初始化 */
uint8_t task_created; /**< 任务创建 */
volatile uint8_t lp_busy; /**< 低功耗忙 */
UsrOtaState_t state; /**< 状态 */
UsrOtaEvtCb_t evt_cb; /**< 事件回调 */
uint32_t file_size; /**< 文件大小 */
uint32_t file_crc32; /**< 文件 CRC32 */
uint32_t written; /**< 写入 */
uint32_t crc32; /**< CRC32 */
uint16_t pkt_len; /**< 包长度 */
volatile uint8_t write_req; /**< 写请求 */
volatile uint8_t abort_req; /**< 中止请求 */
} UsrOta_t;
static UsrOta_t s_ota;
static uint8_t s_pkt_buf[USR_OTA_WRITE_PKT_LEN] __attribute__((aligned(4)));
/*============================================================================*/
/* 私有函数 */
/*============================================================================*/
/**
* @brief IEEE 802.3 CRC32 复位
*/
static void usr_ota_crc32_reset(void)
{
s_ota.crc32 = 0xFFFFFFFFu;
}
/**
* @brief 累加 CRC32
* @param data 数据
* @param len 长度
*/
static void usr_ota_crc32_update(const uint8_t *data, uint32_t len)
{
uint32_t crc = s_ota.crc32;
uint32_t i;
while (len--)
{
crc ^= *data++;
for (i = 0; i < 8u; i++)
{
if (crc & 1u)
{
crc = (crc >> 1) ^ USR_OTA_CRC32_POLY;
}
else
{
crc >>= 1;
}
}
}
s_ota.crc32 = crc;
}
/**
* @brief 取最终 CRC32
* @return CRC32
*/
static uint32_t usr_ota_crc32_final(void)
{
return s_ota.crc32 ^ 0xFFFFFFFFu;
}
/**
* @brief 抛事件给协议适配层
* @param evt 事件
*/
static void usr_ota_post_evt(UsrOtaEvt_t evt)
{
if (s_ota.evt_cb)
{
s_ota.evt_cb(evt);
}
}
/**
* @brief 清 bank、退出 dual_bank、回到空闲
*/
static void usr_ota_hw_abort(void)
{
flash_update_clr_boot_info(CLEAR_APP_UPDATE_BANK);
dual_bank_passive_update_exit(NULL);
s_ota.pkt_len = 0;
s_ota.write_req = 0;
s_ota.abort_req = 0;
s_ota.state = USR_OTA_ST_IDLE;
s_ota.lp_busy = 0;
}
/**
* @brief 失败退出并抛事件
* @param evt 失败事件
*/
static void usr_ota_fail_exit(UsrOtaEvt_t evt)
{
usr_ota_hw_abort();
usr_ota_post_evt(evt);
}
/**
* @brief 低功耗查询:OTA 忙则不许睡
* @return 1=可睡,0=保持唤醒
*/
static u8 usr_ota_lp_idle_query(void)
{
return s_ota.lp_busy ? 0 : 1;
}
REGISTER_LP_TARGET(usr_ota_lp_target) = {
.name = "usr_ota",
.is_idle = usr_ota_lp_idle_query,
};
/**
* @brief 写 Flash 完成回调(dual_bank 任务上下文)
* @param priv 0=成功,非 0=失败
* @return 0
*/
static int usr_ota_write_cb(void *priv)
{
if (priv)
{
log_error("dual_bank write fail");
usr_ota_fail_exit(USR_OTA_EVT_WRITE_FAIL);
return 0;
}
s_ota.written += s_ota.pkt_len;
s_ota.pkt_len = 0;
log_info("write ok, written=%u/%u", s_ota.written, s_ota.file_size);
usr_ota_post_evt(USR_OTA_EVT_WRITE_OK);
if (s_ota.written >= s_ota.file_size)
{
s_ota.state = USR_OTA_ST_VERIFY;
}
return 0;
}
/**
* @brief 烧 boot 完成回调
* @param err 0=成功
* @return 0
*/
static int usr_ota_burn_cb(int err)
{
if (err != 0)
{
log_error("burn boot fail err=%d", err);
usr_ota_fail_exit(USR_OTA_EVT_BURN_FAIL);
return 0;
}
s_ota.state = USR_OTA_ST_WAIT_REBOOT;
s_ota.lp_busy = 0;
log_info("burn boot ok, wait reboot");
usr_ota_post_evt(USR_OTA_EVT_BURN_OK);
return 0;
}
/**
* @brief 校验并烧 bootOTA 任务)
*/
static void usr_ota_do_verify_burn(void)
{
uint32_t calc;
u32 ret;
calc = usr_ota_crc32_final();
if (calc != s_ota.file_crc32)
{
log_error("crc32 mismatch calc=0x%08X expect=0x%08X",
calc, s_ota.file_crc32);
usr_ota_fail_exit(USR_OTA_EVT_VERIFY_FAIL);
return;
}
ret = dual_bank_update_verify_without_crc();
if (ret != 0)
{
log_error("dual_bank verify fail ret=%u", ret);
usr_ota_fail_exit(USR_OTA_EVT_VERIFY_FAIL);
return;
}
log_info("verify ok, burn boot");
s_ota.state = USR_OTA_ST_BURN;
usr_ota_post_evt(USR_OTA_EVT_VERIFY_OK);
dual_bank_update_burn_boot_info(usr_ota_burn_cb);
}
/**
* @brief 处理中止请求
*/
static void usr_ota_do_abort(void)
{
if (s_ota.state == USR_OTA_ST_IDLE ||
s_ota.state == USR_OTA_ST_BURN ||
s_ota.state == USR_OTA_ST_WAIT_REBOOT)
{
s_ota.abort_req = 0;
return;
}
log_info("abort, state=%d", s_ota.state);
usr_ota_hw_abort();
}
/**
* @brief OTA 任务:写 Flash / 校验 / 烧 boot
* @param arg 未使用
*/
static void usr_ota_task(void *arg)
{
(void)arg;
log_info("ota task start");
while (1)
{
if (s_ota.abort_req)
{
usr_ota_do_abort();
}
if (s_ota.write_req && s_ota.state == USR_OTA_ST_WRITE)
{
s_ota.write_req = 0;
if (((uint32_t)s_pkt_buf & 0x3u) != 0)
{
log_error("pkt buf not 4-byte aligned");
usr_ota_write_cb((void *)1);
}
else
{
dual_bank_update_write(s_pkt_buf, s_ota.pkt_len, usr_ota_write_cb);
}
}
if (s_ota.state == USR_OTA_ST_VERIFY)
{
usr_ota_do_verify_burn();
}
os_time_dly(1);
}
}
/*============================================================================*/
/* 公有函数 */
/*============================================================================*/
/**
* @brief 初始化
* @return UsrOta_Ret_t
*/
UsrOta_Ret_t usr_ota_init(void)
{
if (s_ota.initialized)
{
return USR_OTA_OK;
}
memset(&s_ota, 0, sizeof(s_ota));
memset(s_pkt_buf, 0, sizeof(s_pkt_buf));
s_ota.state = USR_OTA_ST_IDLE;
s_ota.initialized = 1;
log_info("init ok, pkt_buf=%p", s_pkt_buf);
return USR_OTA_OK;
}
/**
* @brief 注册事件回调
* @param cb 回调
*/
void usr_ota_set_evt_cb(UsrOtaEvtCb_t cb)
{
s_ota.evt_cb = cb;
}
/**
* @brief 开始升级
* @param file_size 总长度
* @param file_crc32 CRC32
* @return UsrOta_Ret_t
*/
UsrOta_Ret_t usr_ota_start(uint32_t file_size, uint32_t file_crc32)
{
u32 ret;
if (!s_ota.initialized)
{
return USR_OTA_ERR_NOT_INIT;
}
if (file_size == 0)
{
return USR_OTA_ERR_PARAM;
}
if (s_ota.state != USR_OTA_ST_IDLE)
{
return USR_OTA_ERR_BUSY;
}
if (!s_ota.task_created)
{
os_task_create(usr_ota_task, NULL, USR_OTA_TASK_PRIO,
USR_OTA_TASK_STACK, 0, "usr_ota");
s_ota.task_created = 1;
}
clk_set("sys", 48 * 1000000L);
flash_update_clr_boot_info(CLEAR_APP_UPDATE_BANK);
ret = dual_bank_passive_update_init(0, file_size, USR_OTA_WRITE_PKT_LEN, NULL);
if (ret != 0)
{
log_error("passive init fail %u", ret);
dual_bank_passive_update_exit(NULL);
return USR_OTA_ERR_SPACE;
}
ret = dual_bank_update_allow_check(file_size);
if (ret != 0)
{
log_error("allow_check fail %u size=%u", ret, file_size);
dual_bank_passive_update_exit(NULL);
return USR_OTA_ERR_SPACE;
}
s_ota.file_size = file_size;
s_ota.file_crc32 = file_crc32;
s_ota.written = 0;
s_ota.pkt_len = 0;
s_ota.write_req = 0;
s_ota.abort_req = 0;
usr_ota_crc32_reset();
s_ota.lp_busy = 1;
s_ota.state = USR_OTA_ST_WRITE;
log_info("start size=%u crc32=0x%08X", file_size, file_crc32);
return USR_OTA_OK;
}
/**
* @brief 喂入固件分片
* @param buf 数据
* @param len 长度
* @return UsrOta_Ret_t
*/
UsrOta_Ret_t usr_ota_feed(const uint8_t *buf, uint16_t len)
{
uint32_t remain;
if (!buf || len == 0)
{
return USR_OTA_ERR_PARAM;
}
if (s_ota.state != USR_OTA_ST_WRITE)
{
return USR_OTA_ERR_STATE;
}
if (s_ota.write_req)
{
return USR_OTA_ERR_BUSY;
}
if ((uint32_t)s_ota.pkt_len + len > USR_OTA_WRITE_PKT_LEN)
{
return USR_OTA_ERR_PARAM;
}
memcpy(&s_pkt_buf[s_ota.pkt_len], buf, len);
s_ota.pkt_len = (uint16_t)(s_ota.pkt_len + len);
usr_ota_crc32_update(buf, len);
remain = (s_ota.written < s_ota.file_size)
? (s_ota.file_size - s_ota.written)
: 0u;
/* 凑满 4096,或已经拿到文件剩下的全部字节 → 交给 OTA 任务写 Flash */
if (s_ota.pkt_len >= USR_OTA_WRITE_PKT_LEN ||
(uint32_t)s_ota.pkt_len >= remain)
{
s_ota.write_req = 1;
return USR_OTA_FEED_PENDING;
}
return USR_OTA_OK;
}
/**
* @brief 中止升级
*/
void usr_ota_abort(void)
{
/* boot 已写入或正在烧:禁止清 bank */
if (s_ota.state == USR_OTA_ST_IDLE ||
s_ota.state == USR_OTA_ST_BURN ||
s_ota.state == USR_OTA_ST_WAIT_REBOOT)
{
return;
}
s_ota.abort_req = 1;
}
/**
* @brief 延时复位
*/
static void usr_ota_do_cpu_reset(void *priv)
{
(void)priv;
cpu_reset();
}
/**
* @brief 烧 boot 成功后 exit + 复位
*/
void usr_ota_request_reboot(void)
{
log_info("request reboot");
dual_bank_passive_update_exit(NULL);
s_ota.state = USR_OTA_ST_IDLE;
s_ota.lp_busy = 0;
sys_timeout_add(NULL, usr_ota_do_cpu_reset, USR_OTA_REBOOT_DELAY_MS);
}
/**
* @brief 是否忙
* @return 1/0
*/
uint8_t usr_ota_is_busy(void)
{
return (s_ota.state != USR_OTA_ST_IDLE) ? 1 : 0;
}
+101
View File
@@ -0,0 +1,101 @@
/******************************************************************************
* @file usr_ota.h
* @brief 模组双备份 OTA 引擎(协议无关):START/WRITE/VERIFY/BURN
* @author cyWu <1917507415@qq.com>
* @date 2026.08.15
* @version V1.0.0
* @history
* - V1.0.0, 2026.08.15, cyWu, 首次发布
******************************************************************************/
#ifndef __USR_OTA_H__
#define __USR_OTA_H__
#include <stdint.h>
/*============================================================================*/
/* 宏定义 */
/*============================================================================*/
#define USR_OTA_WRITE_PKT_LEN (4096u) /**< dual_bank 单次写入长度 */
#define USR_OTA_TASK_PRIO (31u)
#define USR_OTA_TASK_STACK (2048u)
/*============================================================================*/
/* 错误码 / 事件 */
/*============================================================================*/
typedef enum
{
USR_OTA_OK = 0, /**< 成功 */
USR_OTA_ERR_PARAM, /**< 参数错误 */
USR_OTA_ERR_BUSY, /**< 忙 */
USR_OTA_ERR_STATE, /**< 状态错误 */
USR_OTA_ERR_SPACE, /**< 空间不足 */
USR_OTA_ERR_NOT_INIT, /**< 未初始化 */
USR_OTA_FEED_PENDING, /**< 本包已缓存,等写 Flash 完成后再 ACK */
USR_OTA_UNKNOWN /**< 未知错误 */
} UsrOta_Ret_t;
typedef enum
{
USR_OTA_EVT_WRITE_OK = 0, /**< 一包写入成功,可回 0x33 */
USR_OTA_EVT_WRITE_FAIL, /**< 一包写入失败 */
USR_OTA_EVT_VERIFY_OK, /**< 校验成功 */
USR_OTA_EVT_VERIFY_FAIL, /**< 校验失败 */
USR_OTA_EVT_BURN_OK, /**< 烧 boot 成功 */
USR_OTA_EVT_BURN_FAIL, /**< 烧 boot 失败 */
} UsrOtaEvt_t;
typedef void (*UsrOtaEvtCb_t)(UsrOtaEvt_t evt);
/*============================================================================*/
/* 外部接口 */
/*============================================================================*/
/**
* @brief 初始化双备份 OTA 引擎(创建任务、注册 LP)
* @return UsrOta_Ret_t
*/
UsrOta_Ret_t usr_ota_init(void);
/**
* @brief 注册事件回调(写完/校验/烧 boot)
* @param cb 回调,可在 OTA 任务上下文触发
*/
void usr_ota_set_evt_cb(UsrOtaEvtCb_t cb);
/**
* @brief 开始模组 OTA(协议任务上下文调用)
* @param file_size 固件总长度
* @param file_crc32 协议 CRC32IEEE 802.3
* @return OK / BUSY / SPACE / PARAM
*/
UsrOta_Ret_t usr_ota_start(uint32_t file_size, uint32_t file_crc32);
/**
* @brief 喂入一包固件(只缓存;满 4096 或最后一包才写 Flash
* @param buf 数据
* @param len 长度
* @return OK=可立刻 ACKFEED_PENDING=等 WRITE_OK;其它=失败
*/
UsrOta_Ret_t usr_ota_feed(const uint8_t *buf, uint16_t len);
/**
* @brief 中止升级:清 UPDATE_BANK boot、exit,不复位
* @note 正在烧 boot 或已烧成功时为 no-op,避免把已提交的 bank 清掉
*/
void usr_ota_abort(void);
/**
* @brief 烧 boot 成功后收尾:exit + 延时复位
*/
void usr_ota_request_reboot(void);
/**
* @brief 是否正在模组 OTA
* @return 1=忙,0=空闲
*/
uint8_t usr_ota_is_busy(void);
#endif /* __USR_OTA_H__ */