From 4bfe10d728da9e7249eea0528101d9cc403998f2 Mon Sep 17 00:00:00 2001
From: Wuchuyuan <1917507415@qq.com>
Date: Mon, 10 Aug 2026 16:03:55 +0800
Subject: [PATCH] =?UTF-8?q?1=E3=80=81=E5=B0=86=E5=9B=BA=E4=BB=B6=E6=94=B9?=
=?UTF-8?q?=E9=80=A0=E4=B8=BA=20BLE=20=E4=B8=B2=E5=8F=A3=E6=A8=A1=E7=BB=84?=
=?UTF-8?q?=EF=BC=9A=E6=96=B0=E5=A2=9E=20usr=5Fmcu=5Fbridge=EF=BC=8C?=
=?UTF-8?q?=E7=A7=BB=E9=99=A4=E6=97=A7=20jb=5Fproto?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
2、通过 UART 55AA 对接外部 MCU,完成身份/透传/配对/心跳;桥接忙时注册 LP target 拦住休眠,空闲可进 SLEEP,兼顾串口可靠与低功耗。
---
JBao_JL7016_MOD_FW/AC701N.cbp | 23 +-
JBao_JL7016_MOD_FW/Makefile | 12 +-
JBao_JL7016_MOD_FW/apps/earphone/app_main.c | 14 +-
.../board/br28/board_jl701n_demo_cfg.h | 3 +-
.../apps/earphone/include/app_main.h | 12 +
.../apps/earphone/log_config/app_config.c | 2 +-
.../apps/usr_jb_proto/Protocol/jb_product.c | 249 ----
.../apps/usr_jb_proto/Protocol/jb_product.h | 55 -
.../apps/usr_jb_proto/Protocol/jb_protocol.c | 749 -----------
.../apps/usr_jb_proto/Protocol/jb_protocol.h | 266 ----
.../apps/usr_jb_proto/Utils/jb_common.c | 69 --
.../apps/usr_jb_proto/Utils/jb_common.h | 37 -
.../apps/usr_jb_proto/Utils/jb_ringbuffer.c | 75 --
.../apps/usr_jb_proto/Utils/jb_ringbuffer.h | 30 -
.../apps/usr_jb_proto/usr_jb_main.c | 49 -
.../apps/usr_le_code/bleproto_packer.h | 5 +
.../apps/usr_le_code/usr_le_adv.c | 123 +-
.../apps/usr_le_code/usr_le_api.h | 33 +-
.../apps/usr_le_code/usr_le_product.h | 13 +-
.../apps/usr_le_code/usr_le_recieve.c | 63 +-
.../apps/usr_mcu_bridge/mcu_bridge.c | 1090 +++++++++++++++++
.../apps/usr_mcu_bridge/mcu_bridge.h | 100 ++
.../apps/usr_mcu_bridge/mcu_cmd.h | 110 ++
.../apps/usr_mcu_bridge/mcu_frame.c | 140 +++
.../apps/usr_mcu_bridge/mcu_frame.h | 57 +
.../apps/usr_mcu_bridge/mcu_uart.c | 141 +++
.../apps/usr_mcu_bridge/mcu_uart.h | 62 +
.../apps/usr_mcu_bridge/mcu_util.c | 108 ++
.../apps/usr_mcu_bridge/mcu_util.h | 65 +
.../download/earphone/download_app_ota.bat | 4 +-
30 files changed, 2078 insertions(+), 1681 deletions(-)
delete mode 100644 JBao_JL7016_MOD_FW/apps/usr_jb_proto/Protocol/jb_product.c
delete mode 100644 JBao_JL7016_MOD_FW/apps/usr_jb_proto/Protocol/jb_product.h
delete mode 100644 JBao_JL7016_MOD_FW/apps/usr_jb_proto/Protocol/jb_protocol.c
delete mode 100644 JBao_JL7016_MOD_FW/apps/usr_jb_proto/Protocol/jb_protocol.h
delete mode 100644 JBao_JL7016_MOD_FW/apps/usr_jb_proto/Utils/jb_common.c
delete mode 100644 JBao_JL7016_MOD_FW/apps/usr_jb_proto/Utils/jb_common.h
delete mode 100644 JBao_JL7016_MOD_FW/apps/usr_jb_proto/Utils/jb_ringbuffer.c
delete mode 100644 JBao_JL7016_MOD_FW/apps/usr_jb_proto/Utils/jb_ringbuffer.h
delete mode 100644 JBao_JL7016_MOD_FW/apps/usr_jb_proto/usr_jb_main.c
create mode 100644 JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_bridge.c
create mode 100644 JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_bridge.h
create mode 100644 JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_cmd.h
create mode 100644 JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_frame.c
create mode 100644 JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_frame.h
create mode 100644 JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_uart.c
create mode 100644 JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_uart.h
create mode 100644 JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_util.c
create mode 100644 JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_util.h
diff --git a/JBao_JL7016_MOD_FW/AC701N.cbp b/JBao_JL7016_MOD_FW/AC701N.cbp
index ce04d70..0cbc6c7 100644
--- a/JBao_JL7016_MOD_FW/AC701N.cbp
+++ b/JBao_JL7016_MOD_FW/AC701N.cbp
@@ -174,8 +174,7 @@
-
-
+
@@ -758,25 +757,23 @@
-
+
-
-
+
+
+
-
-
+
+
-
-
-
-
-
-
+
+
+
diff --git a/JBao_JL7016_MOD_FW/Makefile b/JBao_JL7016_MOD_FW/Makefile
index 6819daf..80160c8 100644
--- a/JBao_JL7016_MOD_FW/Makefile
+++ b/JBao_JL7016_MOD_FW/Makefile
@@ -248,8 +248,7 @@ INCLUDES := \
-Iapps/usr_le_code \
-Iapps/usr_le_code/thirdpart \
-Iapps/usr_le_code/thirdpart/mjson \
- -Iapps/usr_jb_proto/Protocol \
- -Iapps/usr_jb_proto/Utils \
+ -Iapps/usr_mcu_bridge \
-I$(SYS_INC_DIR) \
@@ -614,11 +613,10 @@ c_SRC_FILES := \
cpu/br28/tws_audio.c \
cpu/br28/uart_dev.c \
cpu/br28/umidigi_chargestore.c \
- apps/usr_jb_proto/Protocol/jb_product.c \
- apps/usr_jb_proto/Protocol/jb_protocol.c \
- apps/usr_jb_proto/Utils/jb_common.c \
- apps/usr_jb_proto/Utils/jb_ringbuffer.c \
- apps/usr_jb_proto/usr_jb_main.c \
+ apps/usr_mcu_bridge/mcu_bridge.c \
+ apps/usr_mcu_bridge/mcu_frame.c \
+ apps/usr_mcu_bridge/mcu_uart.c \
+ apps/usr_mcu_bridge/mcu_util.c \
apps/usr_le_code/ble_proto.c \
apps/usr_le_code/bleproto.c \
apps/usr_le_code/bleproto_packer.c \
diff --git a/JBao_JL7016_MOD_FW/apps/earphone/app_main.c b/JBao_JL7016_MOD_FW/apps/earphone/app_main.c
index c09a1e1..5bbca73 100644
--- a/JBao_JL7016_MOD_FW/apps/earphone/app_main.c
+++ b/JBao_JL7016_MOD_FW/apps/earphone/app_main.c
@@ -16,6 +16,7 @@
#include "bt_profile_cfg.h"
#include "dev_manager/dev_manager.h"
#include "update_loader_download.h"
+#include "mcu_bridge.h"
#ifndef CONFIG_MEDIA_NEW_ENABLE
#ifndef CONFIG_MEDIA_DEVELOP_ENABLE
@@ -254,7 +255,6 @@ const struct rtc_dev_platform_data rtc_init_data=
};
#endif // 0
extern void bt_osc_offset_ext_updata(s32 offset);
-extern void usr_jb_init();
void app_main()
{
int update = 0;
@@ -292,7 +292,7 @@ void app_main()
}
app_var_init();
- usr_jb_init();
+ mcu_bridge_init();
#if TCFG_MC_BIAS_AUTO_ADJUST
mc_trim_init(update);
@@ -325,13 +325,11 @@ void app_main()
}
/*
- * 上电强制进入配对模式:
- * 1) usr_goto_pair=1:后续 usr_ble_adv_init() 走绑定广播(encode4bind),不走重连广播
- * 2) usr_clear_pair_info_noreset():清 VM 中三元组,保证 paired_flag=0
- * 注意:须在 BLE 协议栈调用 usr_ble_adv_init() 之前设置;此处不清复位,避免上电死循环复位
+ * BLE+MCU:上电不强制配网。
+ * - 未配网:等 MCU 0x10 后再绑定广播
+ * - 已配网:身份就绪后由 usr_ble_adv_init() 发回连广播
*/
- usr_var.usr_goto_pair = 1;
- usr_clear_pair_info_noreset();
+ usr_var.usr_goto_pair = 0;
ui_manage_init();
ui_update_status(STATUS_POWERON);
diff --git a/JBao_JL7016_MOD_FW/apps/earphone/board/br28/board_jl701n_demo_cfg.h b/JBao_JL7016_MOD_FW/apps/earphone/board/br28/board_jl701n_demo_cfg.h
index 3b89093..61391bc 100644
--- a/JBao_JL7016_MOD_FW/apps/earphone/board/br28/board_jl701n_demo_cfg.h
+++ b/JBao_JL7016_MOD_FW/apps/earphone/board/br28/board_jl701n_demo_cfg.h
@@ -23,7 +23,7 @@
//*********************************************************************************//
#define TCFG_UART0_ENABLE ENABLE_THIS_MOUDLE //串口打印模块使能
#define TCFG_UART0_RX_PORT NO_CONFIG_PORT //串口接收脚配置(用于打印可以选择NO_CONFIG_PORT)
-#define TCFG_UART0_TX_PORT IO_PORTC_05 //串口发送脚配置
+#define TCFG_UART0_TX_PORT IO_PORTC_08 //串口发送脚配置
#define TCFG_UART0_BAUDRATE 1000000 //串口波特率配置
//*********************************************************************************//
@@ -733,6 +733,7 @@ DAC硬件上的连接方式,可选的配置:
//*********************************************************************************//
#define TCFG_LOWPOWER_POWER_SEL PWR_LDO15//PWR_LDO15 //电源模式设置,可选DCDC和LDO
#define TCFG_LOWPOWER_BTOSC_DISABLE 0 //低功耗模式下BTOSC是否保持
+/* MCU 串口方案:配合 mcu_bridge LP target,忙时保持唤醒、空闲可进低功耗 */
#define TCFG_LOWPOWER_LOWPOWER_SEL SLEEP_EN//DEEP_SLEEP_EN//SLEEP_EN//DEEP_SLEEP_EN //低功耗使能,蓝牙&&系统空闲可进入低功耗
#define TCFG_LOWPOWER_VDDIOM_LEVEL VDDIOM_VOL_28V //vddiom等级
#define TCFG_LOWPOWER_OSC_TYPE OSC_TYPE_LRC //低功耗晶振类型,btosc/lrc
diff --git a/JBao_JL7016_MOD_FW/apps/earphone/include/app_main.h b/JBao_JL7016_MOD_FW/apps/earphone/include/app_main.h
index c7e3c30..f7998ef 100644
--- a/JBao_JL7016_MOD_FW/apps/earphone/include/app_main.h
+++ b/JBao_JL7016_MOD_FW/apps/earphone/include/app_main.h
@@ -6,9 +6,21 @@ typedef struct _USR_VAR {
u8 usr_plan_cnt ;
u8 usr_intterupt_reinit_falg ;
u8 usr_goto_updata ;
+ /**
+ * 是否处于“主动配网”流程(RAM 运行态,掉电丢失)
+ * 0:正常态。未配网则不广播;已配网则发回连广播
+ * 1:MCU 下发 0x10 / 调用 usr_goto_pair_mode() 后置位,强制发绑定广播
+ * 配网成功(AUTHSETUP 写对)或配网超时后应清 0
+ */
u8 usr_goto_pair ;
u8 usr_can_pair ;
u8 usr_ota_succ_flag ;
+ /**
+ * 广播数据是否需要刷新到协议栈(一次性触发位)
+ * 1:usr_ble_adv_updata() 会把 muti_adv_data 写入栈并 enable 广播,然后清 0
+ * 0:本周期不更新广播数据
+ * 注意:置 1 不等于“一直在广播”,只是请求“应用一次新的广播包”
+ */
u8 usr_adv_flag ;
u8 usr_low_power_flag ;
u8 usr_allow_to_sleep_cnt;
diff --git a/JBao_JL7016_MOD_FW/apps/earphone/log_config/app_config.c b/JBao_JL7016_MOD_FW/apps/earphone/log_config/app_config.c
index a307a06..6686694 100644
--- a/JBao_JL7016_MOD_FW/apps/earphone/log_config/app_config.c
+++ b/JBao_JL7016_MOD_FW/apps/earphone/log_config/app_config.c
@@ -97,7 +97,7 @@ const char log_tag_const_e_APP_POWER AT(.LOG_TAG_CONST) = TRUE;
const char log_tag_const_v_APP AT(.LOG_TAG_CONST) = FALSE;
const char log_tag_const_i_APP AT(.LOG_TAG_CONST) = TRUE;
-const char log_tag_const_d_APP AT(.LOG_TAG_CONST) = FALSE;
+const char log_tag_const_d_APP AT(.LOG_TAG_CONST) = TRUE; /* MCU_BR 等 APP 标签的 log_debug 开关 */
const char log_tag_const_w_APP AT(.LOG_TAG_CONST) = TRUE;
const char log_tag_const_e_APP AT(.LOG_TAG_CONST) = TRUE;
diff --git a/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Protocol/jb_product.c b/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Protocol/jb_product.c
deleted file mode 100644
index 1afb4a6..0000000
--- a/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Protocol/jb_product.c
+++ /dev/null
@@ -1,249 +0,0 @@
-/* ================================================================
- * jb_product.c — 用户层实现(请填补空白处)
- * 设备: 侠客猫激光 | 生成时间: 2026-07-31 17:08:51
- *
- * ══ 开发流程 ══
- * 1. 实现硬件抽象: jbGetTimerCount(定时器由 SOC SDK 提供)
- * 发送由 SOC SDK 的 ble_send_data 承担,协议层已直接调用,无需 uartWrite
- * 2. userInit() 设置 DP 默认值与外设初始化
- * 3. userHandle() 用户数据采集
- * 4. jbEventProcess() 处理 APP 下发的控制命令
- * 5. 主动上报 / OTA 的网关应答在收到 REPORTCMD_V2 后调用 jbReportAckCheck 清除等待
- * 6. main.c / 调用方需周期性调用 jbReportAckCheck(cmd, cnt, int0)
- * ================================================================
- */
-
-#include "jb_product.h"
-
-/* ════════════════════════════════════════════════════════════════
- * 全局变量
- * ════════════════════════════════════════════════════════════════ */
-
-jb_data_point_t gDevData;
-static volatile uint32_t timerMs;
-
-/* ════════════════════════════════════════════════════════════════
- * userInit — DP 默认值
- * ════════════════════════════════════════════════════════════════ */
-void userInit(void)
-{
- memset(&gDevData, 0, sizeof(jb_data_point_t));
-
- gDevData.power = 0; /* DP0 开关 0=关 1=开 */
- gDevData.play_mode = 0; /* PLAY_MODE_0 (关) */
- gDevData.hand_mode = 0; /* HAND_MODE_0 (关) */
- gDevData.alm_low_battery = 0; /* DP3 低电量报警 0=关 1=开 */
- gDevData.beep_trig = 0; /* DP4 声音开关 0=关 1=开 */
- gDevData.battery = 0; /* DP5 电量上报 */
- gDevData.shake_wake = 0; /* DP6 震动触发开机 0=关 1=开 */
- memset(gDevData.no_disturb_enable, 0, 13); /* DP7 勿扰模式开关 */
- gDevData.reserva_dp1 = 0; /* DP8 预留DP1 */
- gDevData.reserva_dp2 = 0; /* DP9 预留DP2 */
- gDevData.reserva_dp3 = 0; /* DP10 预留DP3 */
-}
-
-/* ════════════════════════════════════════════════════════════════
- * userHandle — 用户数据采集
- * 协议层自动检测变化并上报(注意:此处是上报,不是下发控制)
- * ════════════════════════════════════════════════════════════════ */
-void userHandle(void)
-{
- /* ── 可上报 DP (RO / RW): 用户数据采集 ── */
- // gDevData.power = read_sensor(); /* DP0 开关 */
- // gDevData.play_mode = read_sensor(); /* DP1 自动模式 */
- // gDevData.alm_low_battery = read_sensor(); /* DP3 低电量报警 */
- // gDevData.beep_trig = read_sensor(); /* DP4 声音开关 */
- // gDevData.battery = read_sensor(); /* DP5 电量上报 */
- // gDevData.shake_wake = read_sensor(); /* DP6 震动触发开机 */
- // memcpy(gDevData.no_disturb_enable, sensor_read(), 13); /* DP7 勿扰模式开关 */
- // gDevData.reserva_dp1 = read_sensor(); /* DP8 预留DP1 */
- // gDevData.reserva_dp2 = read_sensor(); /* DP9 预留DP2 */
- // gDevData.reserva_dp3 = read_sensor(); /* DP10 预留DP3 */
-}
-
-/* ════════════════════════════════════════════════════════════════
- * jbEventProcess — 0x03 APP 控制事件处理(下行 / DP 赋值方向)
- *
- * 协议层自动解包 0x03 帧后调用本函数。
- * info->dp_ids[] = 被控制的 DP ID 列表
- * ctrl 已填入下发的值;复制到 gDevData 并执行动作
- * ════════════════════════════════════════════════════════════════ */
-int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *ctrl)
-{
- if (!info || !ctrl)
- {
- return -1;
- }
-
- for (uint8_t i = 0; i < info->count; i++)
- {
- switch (info->dp_ids[i])
- {
-
- /* ── DP0 开关 (bool) ── */
- case DP_ID_POWER:
- if (ctrl->power)
- {
- gDevData.power = 1;
- // TODO: 开 — 执行动作
- }
- else
- {
- gDevData.power = 0;
- // TODO: 关 — 执行动作
- }
- break;
-
- /* ── DP1 自动模式 (enum) ── */
- case DP_ID_PLAY_MODE:
- gDevData.play_mode = ctrl->play_mode;
- switch (ctrl->play_mode)
- {
- case PLAY_MODE_0: /* 关 */
- // TODO: 处理 关
- break;
- case PLAY_MODE_1: /* mode1 */
- // TODO: 处理 mode1
- break;
- case PLAY_MODE_2: /* mode2 */
- // TODO: 处理 mode2
- break;
- case PLAY_MODE_3: /* mode3 */
- // TODO: 处理 mode3
- break;
- case PLAY_MODE_4: /* mode4 */
- // TODO: 处理 mode4
- break;
- default:
- break;
- }
- break;
-
- /* ── DP2 手动模式 (enum) ── */
- case DP_ID_HAND_MODE:
- gDevData.hand_mode = ctrl->hand_mode;
- switch (ctrl->hand_mode)
- {
- case HAND_MODE_0: /* 关 */
- // TODO: 处理 关
- break;
- case HAND_MODE_1: /* 顺时针 */
- // TODO: 处理 顺时针
- break;
- case HAND_MODE_2: /* 逆时针 */
- // TODO: 处理 逆时针
- break;
- default:
- break;
- }
- break;
-
- /* ── DP4 声音开关 (bool) ── */
- case DP_ID_BEEP_TRIG:
- if (ctrl->beep_trig)
- {
- gDevData.beep_trig = 1;
- // TODO: 开 — 执行动作
- }
- else
- {
- gDevData.beep_trig = 0;
- // TODO: 关 — 执行动作
- }
- break;
-
- /* ── DP6 震动触发开机 (bool) ── */
- case DP_ID_SHAKE_WAKE:
- if (ctrl->shake_wake)
- {
- gDevData.shake_wake = 1;
- // TODO: 开 — 执行动作
- }
- else
- {
- gDevData.shake_wake = 0;
- // TODO: 关 — 执行动作
- }
- break;
-
- /* ── DP7 勿扰模式开关 (raw) ── */
- case DP_ID_NO_DISTURB_ENABLE:
- memcpy(gDevData.no_disturb_enable, ctrl->no_disturb_enable, 13);
- // TODO: 根据 gDevData.no_disturb_enable 执行动作 (长度 13 字节)
- break;
-
- /* ── DP8 预留DP1 (uint32) ── */
- case DP_ID_RESERVA_DP1:
- gDevData.reserva_dp1 = ctrl->reserva_dp1;
- // TODO: 根据 ctrl->reserva_dp1 执行动作
- break;
-
- /* ── DP9 预留DP2 (uint32) ── */
- case DP_ID_RESERVA_DP2:
- gDevData.reserva_dp2 = ctrl->reserva_dp2;
- // TODO: 根据 ctrl->reserva_dp2 执行动作
- break;
-
- /* ── DP10 预留DP3 (uint32) ── */
- case DP_ID_RESERVA_DP3:
- gDevData.reserva_dp3 = ctrl->reserva_dp3;
- // TODO: 根据 ctrl->reserva_dp3 执行动作
- break;
-
- default:
- break;
- }
- }
- return 0;
-}
-
-/* 0x30 OTA 升级请求 */
-void jbOnOtaNotify(uint32_t fwVersion, uint32_t fwLength, uint32_t fwCrc32)
-{
- (void)fwVersion; (void)fwLength; (void)fwCrc32;
- // TODO: 检查 Flash 空间与版本,决定是否接收升级
- // 允许: 直接返回(协议层默认应答 JB_OK + MaxDataLen=128)
- // 拒绝: 在 jbHandleOtaNotify 中改为 jbSendFrame(CMD_OTA_NOTIFY_ACK, sn, 错误码, 3)
-}
-
-/* 0x32 OTA 数据(含分片序号)*/
-void jbOnOtaData(const uint8_t *data, uint16_t len,
- uint16_t shardIdx, uint16_t totalShards)
-{
- (void)data; (void)len; (void)shardIdx; (void)totalShards;
- // TODO: 写入 Flash,累计 CRC32
- // 全部数据接收完成后调用 jbSendOtaResult(JB_OK, mcuVersion)
-}
-
-/* 0x36 / 0x38 停止 OTA */
-void jbOnOtaStop(void)
-{
- // TODO: 终止 OTA,释放资源
-}
-
-/* 任意 MCU→BLE 请求的 ACK 回调(cmd 标识是哪条请求的应答,result 为结果码)
- - SOC 仅保留 OTA 主动请求应答: OTA完成 0x35 / 停止 0x39
- - 可在本回调中设置"XX 完成/失败"标志,供主循环判断"确认成功才继续" */
-void jbOnAck(uint8_t cmd, uint8_t result)
-{
- (void)cmd; (void)result;
- // TODO: switch(cmd){ case CMD_OTA_COMPLETE_ACK: gOtaDone = (result==JB_OK); break; case CMD_OTA_STOP_MCU_ACK: ... }
-}
-
-/* ════════════════════════════════════════════════════════════════
- * 硬件抽象(按实际平台实现)
- * ════════════════════════════════════════════════════════════════ */
-
-/* ── 定时器 ── */
-void jbTimerIrq(void)
-{
- timerMs++;
-} /* 在 1ms 中断中调用本函数 */
-
-uint32_t jbGetTimerCount(void)
-{
- return timerMs;
-}
-
-/* SOC: 发送由 SOC SDK 的 ble_send_data 承担,无需 uartInit / uartWrite;
- * 定时器由 SOC SDK 提供,仅保留 jbTimerIrq / jbGetTimerCount 接入即可。 */
diff --git a/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Protocol/jb_product.h b/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Protocol/jb_product.h
deleted file mode 100644
index af034db..0000000
--- a/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Protocol/jb_product.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/* ================================================================
- * jb_product.h — 用户层头文件(自动生成)
- * 设备: 侠客猫激光 | 生成时间: 2026-07-31 17:08:51
- *
- * 声明用户需实现的函数。数据结构定义见 jb_protocol.h
- * ================================================================
- */
-
-#ifndef _JB_PRODUCT_H
-#define _JB_PRODUCT_H
-
-#include "jb_protocol.h"
-
-/* ════════════════════════════════════════════════════════════════
- * 硬件配置(SOC SDK 已提供定时器,无需 UART 波特率配置)
- * ════════════════════════════════════════════════════════════════ */
-#define TIMER_PERIOD_MS 1
-
-/* ════════════════════════════════════════════════════════════════
- * 版本管理
- * ════════════════════════════════════════════════════════════════ */
-/* MCU software version: Vmajor.minor.patch */
-#define JB_MCU_SW_VERSION_MAJOR 1
-#define JB_MCU_SW_VERSION_MINOR 0
-#define JB_MCU_SW_VERSION_PATCH 0
-
-/* MCU hardware version: Vmajor.minor.patch */
-#define JB_MCU_HW_VERSION_MAJOR 1
-#define JB_MCU_HW_VERSION_MINOR 0
-#define JB_MCU_HW_VERSION_PATCH 0
-
-/* ════════════════════════════════════════════════════════════════
- * 用户 API
- * ════════════════════════════════════════════════════════════════ */
-
-/* 全局设备数据 — 协议层直接读写该变量 */
-extern jb_data_point_t gDevData;
-
-/* ==================== 用户必须实现 ============================== */
-void userInit(void); /* DP 默认值与外设初始化 */
-void userHandle(void); /* 用户数据采集 */
-void jbTimerIrq(void); /* Call from 1ms timer ISR */
-
-/* =========== 协议回调(声明见 jb_protocol.h)================== */
-/* int8_t jbEventProcess(...); // 0x03 控制事件 */
-/* void jbOnOtaNotify(...); // 0x30 OTA 请求 */
-/* void jbOnOtaData(...); // 0x32 OTA 数据 */
-/* void jbOnOtaStop(); // 0x36/0x38 停止 OTA */
-/* void jbOnAck(...); // 任意请求 ACK(结果) */
-
-/* ============= 主动请求(声明见 jb_protocol.h)================ */
-/* int32_t jbNotifyOtaStop(); // 0x38 通知 BLE 停止 OTA */
-/* int32_t jbSendOtaResult(...); // 0x34 OTA 结果 */
-
-#endif /* _JB_PRODUCT_H */
diff --git a/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Protocol/jb_protocol.c b/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Protocol/jb_protocol.c
deleted file mode 100644
index 4fc8ec2..0000000
--- a/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Protocol/jb_protocol.c
+++ /dev/null
@@ -1,749 +0,0 @@
-/* ================================================================
- * jb_protocol.c — 见宝 IOT 协议实现(自动生成)
- * 设备: 侠客猫激光 | 生成时间: 2026-07-31 17:08:51
- * ================================================================
- */
-
-#include "jb_protocol.h"
-#include "jb_product.h"
-#include "jb_ringbuffer.h"
-#include "jb_common.h"
-
-/* ════════════════════════════════════════════════════════════════
- * 全局实例
- * ════════════════════════════════════════════════════════════════ */
-jb_protocol_t jbProtocol;
-static jb_ringbuffer_t rb; /* UART 接收缓冲区 */
-
-/* ════════════════════════════════════════════════════════════════
- * jbInit — 协议层初始化
- * ════════════════════════════════════════════════════════════════ */
-void jbInit(void)
-{
- memset(&jbProtocol, 0, sizeof(jb_protocol_t));
- jbRbInit(&rb);
-}
-
-/* ════════════════════════════════════════════════════════════════
- * jbPutData — UART 中断将数据压入环形缓冲区
- * ════════════════════════════════════════════════════════════════ */
-int32_t jbPutData(uint8_t *buf, uint32_t len)
-{
- if (!buf)
-{
- return -1;
- }
- for (uint32_t i = 0; i < len; i++)
- {
- jbRbPush(&rb, buf[i]);
- }
- return (int32_t)len;
-}
-
-/* ════════════════════════════════════════════════════════════════
- * jbSendFrame — 组装协议帧并经 ble_send_data 发送(SOC 无串口)
- * 返回帧总长度,< 0 表示出错
- * ════════════════════════════════════════════════════════════════ */
-static int32_t jbSendFrame(uint8_t cmd, uint8_t sn,
- const uint8_t *payload, uint16_t pLen)
-{
- uint8_t buf[MAX_PACKAGE_LEN];
- uint16_t lenField = pLen + 3; /* CMD + SN + payload + CKSUM */
- uint16_t total = lenField + 4;
-
- if (total > MAX_PACKAGE_LEN)
- {
- return -1;
- }
-
- /* 55 AA | LEN_H LEN_L | CMD | SN | payload | CKSUM */
- buf[0] = PKT_HEAD_0;
- buf[1] = PKT_HEAD_1;
- jbWriteU16BE(&buf[2], lenField);
- buf[4] = cmd;
- buf[5] = sn;
- if (payload && pLen > 0)
- {
- memcpy(&buf[6], payload, pLen);
- }
- buf[total - 1] = jbChecksum(buf, total);
-
- ble_send_data(buf, total);
- return (int32_t)total;
-}
-
-/* 发送单字节结果应答 */
-static void jbSendAck(uint8_t cmd, uint8_t sn, uint8_t result)
-{
- jbSendFrame(cmd, sn, &result, 1);
-}
-
-/* ════════════════════════════════════════════════════════════════
- * jbGetOnePacket — 从环形缓冲区提取完整帧
- * 返回: 0 = 成功, 1 = 需要更多数据, -2 = 校验和错误
- * ════════════════════════════════════════════════════════════════ */
-static int8_t jbGetOnePacket(uint8_t *out, uint16_t *outLen)
-{
- static uint8_t state = 0;
- static uint16_t expect = 0;
- static uint16_t idx = 0;
- static uint8_t prev = 0;
-
- while (jbRbAvailable(&rb))
- {
- uint8_t b = jbRbPop(&rb);
-
- if (state == 0)
- {
- /* 搜索同步头 55 AA */
- if (prev == PKT_HEAD_0 && b == PKT_HEAD_1)
- {
- out[0] = PKT_HEAD_0;
- out[1] = PKT_HEAD_1;
- idx = 2;
- state = 1;
- }
- }
- else if (state == 1)
- {
- /* 接收 LEN(2B) + CMD + SN */
- out[idx++] = b;
- if (idx == 6)
- {
- expect = 4 + jbReadU16BE(&out[2]);
- if (expect > MAX_PACKAGE_LEN || expect < 7)
- {
- state = 0;
- }
- else
- {
- state = 2;
- }
- }
- }
- else
- {
- /* 接收剩余数据 */
- out[idx++] = b;
- if (idx >= expect)
- {
- state = 0;
- if (out[expect - 1] == jbChecksum(out, expect))
- {
- *outLen = expect;
- return 0;
- }
- return -2;
- }
- }
- prev = b;
- }
- return 1;
-}
-
-/* ════════════════════════════════════════════════════════════════
- * jbPackDps — dataPoint → outBuf[bitmap | values]
- * mask 指定要打包的 DP(bit 序号 = DP ID)
- * 返回 outBuf 总字节数(bitmap + values)
- * 仅打包 RO / RW 方向的 DP
- * ════════════════════════════════════════════════════════════════ */
-static uint16_t jbPackDps(const jb_data_point_t *dp,
- const uint8_t *mask, uint8_t *outBuf)
-{
- uint8_t *bitmap = outBuf;
- uint8_t *out = outBuf + DP_BITMAP_SIZE;
- uint16_t valIdx = 0;
- memset(bitmap, 0, DP_BITMAP_SIZE);
-
- /* DP0 开关 (bool, 1B, RW) */
- if (mask[DP_ID_POWER / 8] & (1 << (DP_ID_POWER % 8)))
- {
- bitmap[DP_ID_POWER / 8] |= (1 << (DP_ID_POWER % 8));
- out[valIdx++] = dp->power ? 1 : 0;
- }
- /* DP1 自动模式 (enum, 1B, RW) */
- if (mask[DP_ID_PLAY_MODE / 8] & (1 << (DP_ID_PLAY_MODE % 8)))
- {
- bitmap[DP_ID_PLAY_MODE / 8] |= (1 << (DP_ID_PLAY_MODE % 8));
- out[valIdx++] = (uint8_t)dp->play_mode;
- }
- /* DP3 低电量报警 (bool, 1B, RO) */
- if (mask[DP_ID_ALM_LOW_BATTERY / 8] & (1 << (DP_ID_ALM_LOW_BATTERY % 8)))
- {
- bitmap[DP_ID_ALM_LOW_BATTERY / 8] |= (1 << (DP_ID_ALM_LOW_BATTERY % 8));
- out[valIdx++] = dp->alm_low_battery ? 1 : 0;
- }
- /* DP4 声音开关 (bool, 1B, RW) */
- if (mask[DP_ID_BEEP_TRIG / 8] & (1 << (DP_ID_BEEP_TRIG % 8)))
- {
- bitmap[DP_ID_BEEP_TRIG / 8] |= (1 << (DP_ID_BEEP_TRIG % 8));
- out[valIdx++] = dp->beep_trig ? 1 : 0;
- }
- /* DP5 电量上报 (uint8, 1B, RO) */
- if (mask[DP_ID_BATTERY / 8] & (1 << (DP_ID_BATTERY % 8)))
- {
- bitmap[DP_ID_BATTERY / 8] |= (1 << (DP_ID_BATTERY % 8));
- out[valIdx++] = (uint8_t)dp->battery;
- }
- /* DP6 震动触发开机 (bool, 1B, RW) */
- if (mask[DP_ID_SHAKE_WAKE / 8] & (1 << (DP_ID_SHAKE_WAKE % 8)))
- {
- bitmap[DP_ID_SHAKE_WAKE / 8] |= (1 << (DP_ID_SHAKE_WAKE % 8));
- out[valIdx++] = dp->shake_wake ? 1 : 0;
- }
- /* DP7 勿扰模式开关 (raw, 13B, RW) */
- if (mask[DP_ID_NO_DISTURB_ENABLE / 8] & (1 << (DP_ID_NO_DISTURB_ENABLE % 8)))
- {
- bitmap[DP_ID_NO_DISTURB_ENABLE / 8] |= (1 << (DP_ID_NO_DISTURB_ENABLE % 8));
- memcpy(&out[valIdx], dp->no_disturb_enable, 13);
- valIdx += 13;
- }
- /* DP8 预留DP1 (uint32, 4B, RW) */
- if (mask[DP_ID_RESERVA_DP1 / 8] & (1 << (DP_ID_RESERVA_DP1 % 8)))
- {
- bitmap[DP_ID_RESERVA_DP1 / 8] |= (1 << (DP_ID_RESERVA_DP1 % 8));
- jbWriteU32BE(&out[valIdx], (uint32_t)dp->reserva_dp1);
- valIdx += 4;
- }
- /* DP9 预留DP2 (uint32, 4B, RW) */
- if (mask[DP_ID_RESERVA_DP2 / 8] & (1 << (DP_ID_RESERVA_DP2 % 8)))
- {
- bitmap[DP_ID_RESERVA_DP2 / 8] |= (1 << (DP_ID_RESERVA_DP2 % 8));
- jbWriteU32BE(&out[valIdx], (uint32_t)dp->reserva_dp2);
- valIdx += 4;
- }
- /* DP10 预留DP3 (uint32, 4B, RW) */
- if (mask[DP_ID_RESERVA_DP3 / 8] & (1 << (DP_ID_RESERVA_DP3 % 8)))
- {
- bitmap[DP_ID_RESERVA_DP3 / 8] |= (1 << (DP_ID_RESERVA_DP3 % 8));
- jbWriteU32BE(&out[valIdx], (uint32_t)dp->reserva_dp3);
- valIdx += 4;
- }
-
- return DP_BITMAP_SIZE + valIdx;
-}
-
-/* ════════════════════════════════════════════════════════════════
- * jbUnpackDps — bitmap+values → dataPoint + event_info
- * 仅解包 WO / RW 方向的 DP
- * ════════════════════════════════════════════════════════════════ */
-static void jbUnpackDps(const uint8_t *bitmap, const uint8_t *values,
- uint16_t valLen, jb_data_point_t *dp,
- jb_event_info_t *info)
-{
- uint16_t valIdx = 0;
- if (info)
- {
- info->count = 0;
- }
-
- /* DP0 开关 (bool, 1B, RW) */
- if (bitmap[DP_ID_POWER / 8] & (1 << (DP_ID_POWER % 8)))
- {
- if (valIdx < valLen)
- {
- dp->power = (values[valIdx++] != 0);
- }
- if (info && info->count < DP_ID_MAX)
- {
- info->dp_ids[info->count++] = DP_ID_POWER;
- }
- }
- /* DP1 自动模式 (enum, 1B, RW) */
- if (bitmap[DP_ID_PLAY_MODE / 8] & (1 << (DP_ID_PLAY_MODE % 8)))
- {
- if (valIdx < valLen)
- {
- dp->play_mode = values[valIdx++];
- }
- if (info && info->count < DP_ID_MAX)
- {
- info->dp_ids[info->count++] = DP_ID_PLAY_MODE;
- }
- }
- /* DP2 手动模式 (enum, 1B, WO) */
- if (bitmap[DP_ID_HAND_MODE / 8] & (1 << (DP_ID_HAND_MODE % 8)))
- {
- if (valIdx < valLen)
- {
- dp->hand_mode = values[valIdx++];
- }
- if (info && info->count < DP_ID_MAX)
- {
- info->dp_ids[info->count++] = DP_ID_HAND_MODE;
- }
- }
- /* DP4 声音开关 (bool, 1B, RW) */
- if (bitmap[DP_ID_BEEP_TRIG / 8] & (1 << (DP_ID_BEEP_TRIG % 8)))
- {
- if (valIdx < valLen)
- {
- dp->beep_trig = (values[valIdx++] != 0);
- }
- if (info && info->count < DP_ID_MAX)
- {
- info->dp_ids[info->count++] = DP_ID_BEEP_TRIG;
- }
- }
- /* DP6 震动触发开机 (bool, 1B, RW) */
- if (bitmap[DP_ID_SHAKE_WAKE / 8] & (1 << (DP_ID_SHAKE_WAKE % 8)))
- {
- if (valIdx < valLen)
- {
- dp->shake_wake = (values[valIdx++] != 0);
- }
- if (info && info->count < DP_ID_MAX)
- {
- info->dp_ids[info->count++] = DP_ID_SHAKE_WAKE;
- }
- }
- /* DP7 勿扰模式开关 (raw, 13B, RW) */
- if (bitmap[DP_ID_NO_DISTURB_ENABLE / 8] & (1 << (DP_ID_NO_DISTURB_ENABLE % 8)))
- {
- if (valIdx + 13 <= valLen)
- {
- memcpy(dp->no_disturb_enable, &values[valIdx], 13);
- valIdx += 13;
- }
- if (info && info->count < DP_ID_MAX)
- {
- info->dp_ids[info->count++] = DP_ID_NO_DISTURB_ENABLE;
- }
- }
- /* DP8 预留DP1 (uint32, 4B, RW) */
- if (bitmap[DP_ID_RESERVA_DP1 / 8] & (1 << (DP_ID_RESERVA_DP1 % 8)))
- {
- if (valIdx + 4 <= valLen)
- {
- dp->reserva_dp1 = (uint32_t)jbReadU32BE(&values[valIdx]);
- valIdx += 4;
- }
- if (info && info->count < DP_ID_MAX)
- {
- info->dp_ids[info->count++] = DP_ID_RESERVA_DP1;
- }
- }
- /* DP9 预留DP2 (uint32, 4B, RW) */
- if (bitmap[DP_ID_RESERVA_DP2 / 8] & (1 << (DP_ID_RESERVA_DP2 % 8)))
- {
- if (valIdx + 4 <= valLen)
- {
- dp->reserva_dp2 = (uint32_t)jbReadU32BE(&values[valIdx]);
- valIdx += 4;
- }
- if (info && info->count < DP_ID_MAX)
- {
- info->dp_ids[info->count++] = DP_ID_RESERVA_DP2;
- }
- }
- /* DP10 预留DP3 (uint32, 4B, RW) */
- if (bitmap[DP_ID_RESERVA_DP3 / 8] & (1 << (DP_ID_RESERVA_DP3 % 8)))
- {
- if (valIdx + 4 <= valLen)
- {
- dp->reserva_dp3 = (uint32_t)jbReadU32BE(&values[valIdx]);
- valIdx += 4;
- }
- if (info && info->count < DP_ID_MAX)
- {
- info->dp_ids[info->count++] = DP_ID_RESERVA_DP3;
- }
- }
-}
-
-/* ════════════════════════════════════════════════════════════════
- * jbBuildDiffMask — 比较上次与当前值,为变化的可上报 DP 置 mask 位
- * 返回: 1 = 有 DP 发生变化, 0 = 全部未变化
- * ════════════════════════════════════════════════════════════════ */
-static uint8_t jbBuildDiffMask(const jb_data_point_t *last,
- const jb_data_point_t *cur,
- uint8_t *mask)
-{
- uint8_t hasChange = 0;
- memset(mask, 0, DP_BITMAP_SIZE);
-
- /* DP0 开关 */
- if (last->power != cur->power)
- {
- mask[DP_ID_POWER / 8] |= (1 << (DP_ID_POWER % 8));
- hasChange = 1;
- }
- /* DP1 自动模式 */
- if (last->play_mode != cur->play_mode)
- {
- mask[DP_ID_PLAY_MODE / 8] |= (1 << (DP_ID_PLAY_MODE % 8));
- hasChange = 1;
- }
- /* DP3 低电量报警 */
- if (last->alm_low_battery != cur->alm_low_battery)
- {
- mask[DP_ID_ALM_LOW_BATTERY / 8] |= (1 << (DP_ID_ALM_LOW_BATTERY % 8));
- hasChange = 1;
- }
- /* DP4 声音开关 */
- if (last->beep_trig != cur->beep_trig)
- {
- mask[DP_ID_BEEP_TRIG / 8] |= (1 << (DP_ID_BEEP_TRIG % 8));
- hasChange = 1;
- }
- /* DP5 电量上报 */
- if (last->battery != cur->battery)
- {
- mask[DP_ID_BATTERY / 8] |= (1 << (DP_ID_BATTERY % 8));
- hasChange = 1;
- }
- /* DP6 震动触发开机 */
- if (last->shake_wake != cur->shake_wake)
- {
- mask[DP_ID_SHAKE_WAKE / 8] |= (1 << (DP_ID_SHAKE_WAKE % 8));
- hasChange = 1;
- }
- /* DP7 勿扰模式开关 */
- if (memcmp(last->no_disturb_enable, cur->no_disturb_enable, 13))
- {
- mask[DP_ID_NO_DISTURB_ENABLE / 8] |= (1 << (DP_ID_NO_DISTURB_ENABLE % 8));
- hasChange = 1;
- }
- /* DP8 预留DP1 */
- if (last->reserva_dp1 != cur->reserva_dp1)
- {
- mask[DP_ID_RESERVA_DP1 / 8] |= (1 << (DP_ID_RESERVA_DP1 % 8));
- hasChange = 1;
- }
- /* DP9 预留DP2 */
- if (last->reserva_dp2 != cur->reserva_dp2)
- {
- mask[DP_ID_RESERVA_DP2 / 8] |= (1 << (DP_ID_RESERVA_DP2 % 8));
- hasChange = 1;
- }
- /* DP10 预留DP3 */
- if (last->reserva_dp3 != cur->reserva_dp3)
- {
- mask[DP_ID_RESERVA_DP3 / 8] |= (1 << (DP_ID_RESERVA_DP3 % 8));
- hasChange = 1;
- }
-
- return hasChange;
-}
-
-/* 置全量掩码(所有可上报 DP)*/
-static void jbSetFullMask(uint8_t *mask)
-{
- memset(mask, 0xFF, DP_BITMAP_SIZE);
- if (DP_COUNT % 8)
- {
- mask[DP_BITMAP_SIZE - 1] &= (1 << (DP_COUNT % 8)) - 1;
- }
-}
-
-/* ════════════════════════════════════════════════════════════════
- * jbSendReqWithAck — 发送请求帧并登记 ACK 重传
- *
- * 与 jbSendFrame 不同,本函数把完整帧缓存到 waitAck.buf,
- * 并启动 ACK 等待。主动上报 / OTA 等网关应答均走 REPORTCMD_V2,
- * 由 jbReportAckCheck() 清除等待;若 SEND_MAX_TIME 内未收到,
- * jbAckRetry() 自动重传,最多 SEND_MAX_NUM 次。
- * 返回 ble_send_data 结果(<0 出错)。
- * ════════════════════════════════════════════════════════════════ */
-static int32_t jbSendReqWithAck(uint8_t cmd, uint8_t sn,
- const uint8_t *payload, uint16_t pLen)
-{
- uint16_t lenField = pLen + 3; /* CMD + SN + payload + CKSUM */
- uint16_t total = lenField + 4; /* + 55 AA + LEN(2) */
-
- if (total > MAX_PACKAGE_LEN)
- {
- return -1;
- }
-
- uint8_t buf[MAX_PACKAGE_LEN];
- buf[0] = PKT_HEAD_0;
- buf[1] = PKT_HEAD_1;
- jbWriteU16BE(&buf[2], lenField);
- buf[4] = cmd;
- buf[5] = sn;
- if (payload && pLen > 0)
- {
- memcpy(&buf[6], payload, pLen);
- }
- buf[total - 1] = jbChecksum(buf, total);
-
- /* 缓存整帧,供超时重传 */
- memcpy(jbProtocol.waitAck.buf, buf, total);
- jbProtocol.waitAck.len = total;
- jbProtocol.waitAck.active = 1;
- jbProtocol.waitAck.sn = sn;
- jbProtocol.waitAck.retry = 0;
- jbProtocol.waitAck.sendTime = jbGetTimerCount();
-
- ble_send_data(buf, total);
- return (int32_t)total;
-}
-
-/* ════════════════════════════════════════════════════════════════
- * jbAckRetry — ACK 超时重传
- * ════════════════════════════════════════════════════════════════ */
-static void jbAckRetry(void)
-{
- if (!jbProtocol.waitAck.active)
- {
- return;
- }
- if (jbProtocol.waitAck.retry >= SEND_MAX_NUM)
- {
- memset(&jbProtocol.waitAck, 0, sizeof(jb_wait_ack_t));
- return;
- }
- if (jbGetTimerCount() - jbProtocol.waitAck.sendTime > SEND_MAX_TIME)
- {
- ble_send_data(jbProtocol.waitAck.buf, jbProtocol.waitAck.len);
- jbProtocol.waitAck.retry++;
- jbProtocol.waitAck.sendTime = jbGetTimerCount();
- }
-}
-
-/* ════════════════════════════════════════════════════════════════
- * jbReportAckCheck — 网关应答确认(主动上报 / OTA 共用)
- *
- * 网关通过 REPORTCMD_V2 回传(非 55AA)。成功条件:
- * cmd == 0
- * gateway_int_cnt == 1
- * gateway_int0 == 0
- * 满足则清除 waitAck,停止重传,并回调 jbOnAck(wait_cmd, 0)。
- *
- * 返回: 1=成功并清除等待; 0=未命中/条件不符。
- * ════════════════════════════════════════════════════════════════ */
-int8_t jbReportAckCheck(uint16_t cmd, uint8_t gateway_int_cnt, int32_t gateway_int0)
-{
- if (!jbProtocol.waitAck.active)
- {
- return 0;
- }
- if (cmd == 0 && gateway_int_cnt == 1 && gateway_int0 == 0)
- {
- uint8_t wait_cmd = jbProtocol.waitAck.buf[4];
- memset(&jbProtocol.waitAck, 0, sizeof(jb_wait_ack_t));
- jbOnAck(wait_cmd, 0); /* result=0 表示应答成功 */
- return 1;
- }
- return 0;
-}
-
-/* ════════════════════════════════════════════════════════════════
- * jbSendReport — 发送主动上报 (0x07)
- *
- * mask 指定本次上报包含哪些 DP:
- * - 差分掩码(来自 jbBuildDiffMask) → 仅上报变化的 DP
- * - 全量掩码(来自 jbSetFullMask) → 定时全量同步
- * ════════════════════════════════════════════════════════════════ */
-static void jbSendReport(jb_data_point_t *data, const uint8_t *mask)
-{
- uint8_t payload[MAX_PACKAGE_LEN];
- uint16_t plen = jbPackDps(data, mask, payload);
- uint8_t sn = jbProtocol.sn++;
- jbSendReqWithAck(CMD_REPORT, sn, payload, plen);
-}
-
-/* ════════════════════════════════════════════════════════════════
- * jbReportPolicy — 变化上报(差分) + 定时上报(全量)
- *
- * 策略:
- * 1. DP 发生变化 → 仅上报变化的 DP(差分掩码)
- * 2. 每隔 REPORT_PERIOD ms → 上报全部可上报 DP(全量同步)
- * ════════════════════════════════════════════════════════════════ */
-static void jbReportPolicy(jb_data_point_t *data)
-{
- uint32_t now = jbGetTimerCount();
- uint8_t diffMask[DP_BITMAP_SIZE];
-
- /* ── 1. 变化上报(差分) ── */
- uint8_t hasChange = jbBuildDiffMask(&jbProtocol.lastData, data, diffMask);
- if (hasChange && !jbProtocol.waitAck.active &&
- (now - jbProtocol.lastChangeTime > REPORT_DEBOUNCE))
- {
- jbSendReport(data, diffMask);
- memcpy(&jbProtocol.lastData, data, sizeof(jb_data_point_t));
- jbProtocol.lastChangeTime = now;
- jbProtocol.lastReportTime = now;
- return;
- }
-
- /* ── 2. 定时全量上报 ── */
- if (!jbProtocol.waitAck.active &&
- (now - jbProtocol.lastReportTime > REPORT_PERIOD))
- {
- uint8_t fullMask[DP_BITMAP_SIZE];
- jbSetFullMask(fullMask);
- jbSendReport(data, fullMask);
- memcpy(&jbProtocol.lastData, data, sizeof(jb_data_point_t));
- jbProtocol.lastReportTime = now;
- }
-}
-
-/* ════════════════════════════════════════════════════════════════
- * 命令处理函数(BLE → MCU 下行帧)
- * ════════════════════════════════════════════════════════════════ */
-
-/* ── 0x03 APP 下发控制 ── */
-static void jbHandleControl(uint8_t *frame, uint16_t len)
-{
- uint8_t sn = frame[5];
- uint8_t *bitmap = &frame[6];
- uint8_t *values = &frame[6 + DP_BITMAP_SIZE];
- uint16_t valLen = len - 7 - DP_BITMAP_SIZE;
-
- jb_event_info_t info;
- jb_data_point_t ctrlData;
- memset(&ctrlData, 0, sizeof(ctrlData));
- jbUnpackDps(bitmap, values, valLen, &ctrlData, &info);
-
- /* 分发给用户层;用户在回调中更新 gDevData */
- jbEventProcess(&info, &ctrlData);
- jbSendAck(CMD_CONTROL_ACK, sn, JB_OK);
-}
-
-/* ── 0x05 APP 读取请求 ── */
-static void jbHandleRead(uint8_t *frame, uint16_t len,
- jb_data_point_t *data)
-{
- uint8_t sn = frame[5];
- uint8_t *reqBitmap = &frame[6];
-
- uint8_t mask[DP_BITMAP_SIZE];
- memcpy(mask, reqBitmap, DP_BITMAP_SIZE);
-
- /* bitmap 全 1(=0xFF...) 表示读取全部 DP */
- uint8_t allOnes = 1;
- for (int i = 0; i < DP_BITMAP_SIZE; i++)
- {
- if (mask[i] != 0xFF)
- {
- allOnes = 0;
- break;
- }
- }
- if (allOnes)
- {
- jbSetFullMask(mask);
- }
-
- uint8_t payload[MAX_PACKAGE_LEN];
- uint16_t plen = jbPackDps(data, mask, payload);
- jbSendFrame(CMD_READ_ACK, sn, payload, plen);
-}
-
-/* ── 0x30 OTA 升级请求 ── */
-static void jbHandleOtaNotify(uint8_t *frame, uint16_t len)
-{
- uint8_t sn = frame[5];
- uint32_t fwVersion = ((uint32_t)frame[6] << 16)
- | ((uint32_t)frame[7] << 8)
- | frame[8];
- uint32_t fwLength = jbReadU32BE(&frame[9]);
- uint32_t fwCrc32 = jbReadU32BE(&frame[13]);
-
- jbOnOtaNotify(fwVersion, fwLength, fwCrc32);
-
- /* 默认应答: 结果码 + 最大单包长度(2B) */
- /* maxDataLen 由 MCU 自身能力决定,填入 0x31 应答,BLE 据此分片 */
- uint8_t ack[3];
- ack[0] = JB_OK;
- ack[1] = (uint8_t)(JB_OTA_MAX_PAYLOAD >> 8);
- ack[2] = (uint8_t)(JB_OTA_MAX_PAYLOAD & 0xFF);
- jbSendFrame(CMD_OTA_NOTIFY_ACK, sn, ack, 3);
-}
-
-/* ── 0x32 OTA 数据(含分片序号)── */
-static void jbHandleOtaData(uint8_t *frame, uint16_t len)
-{
- uint8_t sn = frame[5];
- uint16_t shardIdx = jbReadU16BE(&frame[6]);
- uint16_t totalShards = jbReadU16BE(&frame[8]);
- const uint8_t *pData = &frame[10];
- uint16_t dataLen = (len > 11) ? (len - 11) : 0;
- jbOnOtaData(pData, dataLen, shardIdx, totalShards);
- jbSendAck(CMD_OTA_DATA_ACK, sn, JB_OK);
-}
-
-/* ── 0x36 BLE 通知停止 OTA ── */
-static void jbHandleOtaStop(uint8_t *frame, uint16_t len)
-{
- uint8_t sn = frame[5];
- jbOnOtaStop();
- jbSendAck(CMD_OTA_STOP_BLE_ACK, sn, JB_OK);
-}
-
-/* 0x38 通知 BLE 停止 OTA(MCU 主动,等待 ACK 重传)*/
-int32_t jbNotifyOtaStop(void)
-{
- uint8_t sn = jbProtocol.sn++;
- return jbSendReqWithAck(CMD_OTA_STOP_MCU, sn, NULL, 0);
-}
-
-/* 0x34 OTA 升级完成(result + MCU 版本 3B,等待 ACK 重传)*/
-int32_t jbSendOtaResult(uint8_t result, const uint8_t *mcuVersion)
-{
- uint8_t sn = jbProtocol.sn++;
- uint8_t payload[4];
- payload[0] = result;
- if (mcuVersion)
- {
- memcpy(&payload[1], mcuVersion, 3);
- }
- else
- {
- memset(&payload[1], 0, 3);
- }
- return jbSendReqWithAck(CMD_OTA_COMPLETE, sn, payload, 4);
-}
-
-/* ════════════════════════════════════════════════════════════════
- * jbProtocolHandle — 协议主循环(在 while(1) 中调用)
- * ════════════════════════════════════════════════════════════════ */
-int32_t jbProtocolHandle(jb_data_point_t *data)
-{
- if (!data)
- {
- return -1;
- }
-
- /* ACK 超时重传 */
- jbAckRetry();
-
- /* 提取一帧 */
- uint16_t pktLen = 0;
- int8_t ret = jbGetOnePacket(jbProtocol.rxBuf, &pktLen);
- uint8_t cmd;
- if (ret == 0)
- {
- cmd = jbProtocol.rxBuf[4];
-
- /* 按命令码统一分发 BLE → MCU 的所有帧(含 MCU 主动请求的应答)*/
- switch (cmd)
- {
- /* ── APP-BLE 数据 ── */
- case CMD_CONTROL:
- jbHandleControl(jbProtocol.rxBuf, pktLen);
- break;
- case CMD_READ:
- jbHandleRead(jbProtocol.rxBuf, pktLen, data);
- break;
- /* ── BLE-MCU OTA ── */
- case CMD_OTA_NOTIFY:
- jbHandleOtaNotify(jbProtocol.rxBuf, pktLen);
- break;
- case CMD_OTA_DATA:
- jbHandleOtaData(jbProtocol.rxBuf, pktLen);
- break;
- case CMD_OTA_STOP_BLE:
- jbHandleOtaStop(jbProtocol.rxBuf, pktLen);
- break;
- /* 主动上报 / OTA 完成&停止 的网关应答均走 REPORTCMD_V2 → jbReportAckCheck */
- default:
- break;
- }
- }
-
- jbReportPolicy(data);
- return 0;
-}
diff --git a/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Protocol/jb_protocol.h b/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Protocol/jb_protocol.h
deleted file mode 100644
index e009008..0000000
--- a/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Protocol/jb_protocol.h
+++ /dev/null
@@ -1,266 +0,0 @@
-/* ================================================================
- * jb_protocol.h — 见宝 IOT 协议层(自动生成)
- * 设备: 侠客猫激光 | DP 数: 11 | Bitmap: 2B
- * 协议版本: V1.0 | 生成时间: 2026-07-31 17:08:51
- *
- * 帧格式: [55 AA] [LEN_H LEN_L] [CMD] [SN] [payload...] [CKSUM]
- * LEN = CMD(1) + SN(1) + payload + CKSUM(1) = payload + 3
- * CKSUM = sum(LEN_H .. payload_last) & 0xFF
- * Bitmap = 每个 DP 占 1 bit,bit 序号 = DP ID(0 起始)
- * Values = 按 bitmap 置位顺序排列,每个 DP 占完整 byte_length
- * ================================================================
- */
-
-#ifndef _JB_PROTOCOL_H
-#define _JB_PROTOCOL_H
-
-#include
-#include
-#include
-
-/* ════════════════════════════════════════════════════════════════
- * 协议常量
- * ════════════════════════════════════════════════════════════════ */
-#define PKT_HEAD_0 0x55
-#define PKT_HEAD_1 0xAA
-#define MAX_PACKAGE_LEN 128
-#define JB_OTA_MAX_PAYLOAD 96 /* OTA 单包 Payload 上限(Byte); 须 <= MAX_PACKAGE_LEN - 11(0x32 帧开销) */
-
-#define DP_COUNT 11
-#define DP_BITMAP_SIZE 2
-
-#define SEND_MAX_TIME 2500 /* ACK 重传超时(ms);BLE Indication往返常需数百ms~1s,过短会误重传导致双应答 */
-#define SEND_MAX_NUM 3 /* 最大重传次数 */
-#define REPORT_DEBOUNCE 2000 /* 变化上报防抖时间(ms) */
-#define REPORT_PERIOD 600000 /* 定时全量上报周期(ms) */
-
-/* Device identity: change these values for the target product.
- * JB_CATEGORY_CODE: 必须 4 字符 ASCII,供 BLE 广播 prodcode 使用
- * JB_PRODUCT_CODE : 必须 6 字符 ASCII,供 BLE 广播 pcode 使用
- */
-#define JB_PROTOCOL_VERSION 0x0100 /* V1.0 */
-#define JB_CATEGORY_CODE "CBC0"
-#define JB_PRODUCT_CODE "PICBC0"
-#define JB_DEVICE_SN 0x00
-#define JB_DEVICE_CAPABILITY 0ULL
-
-/* ════════════════════════════════════════════════════════════════
- * 命令码(见《MCU方案接入协议通讯文档》)
- * ════════════════════════════════════════════════════════════════ */
-typedef enum
-{
- /* ── APP-BLE 数据(6) ── */
- CMD_CONTROL = 0x03, /* APP/BLE → MCU: 下发控制 */
- CMD_CONTROL_ACK = 0x04, /* MCU → APP/BLE: 控制应答 */
- CMD_READ = 0x05, /* APP/BLE → MCU: 读取请求 */
- CMD_READ_ACK = 0x06, /* MCU → APP/BLE: 读取应答 */
- CMD_REPORT = 0x07, /* MCU → APP/BLE: 主动上报 */
- CMD_REPORT_ACK = 0x08, /* APP/BLE → MCU: 上报应答 */
- /* ── OTA(10) ── */
- CMD_OTA_NOTIFY = 0x30, /* BLE → MCU: OTA 升级请求 */
- CMD_OTA_NOTIFY_ACK = 0x31, /* MCU → BLE: OTA 应答(含MaxLen) */
- CMD_OTA_DATA = 0x32, /* BLE → MCU: OTA 数据 */
- CMD_OTA_DATA_ACK = 0x33, /* MCU → BLE: OTA 数据应答 */
- CMD_OTA_COMPLETE = 0x34, /* MCU → BLE: OTA 完成 */
- CMD_OTA_COMPLETE_ACK = 0x35, /* BLE → MCU: OTA 完成应答 */
- CMD_OTA_STOP_BLE = 0x36, /* BLE → MCU: 停止 OTA */
- CMD_OTA_STOP_BLE_ACK = 0x37, /* MCU → BLE: 停止应答 */
- CMD_OTA_STOP_MCU = 0x38, /* MCU → BLE: 通知停止 OTA */
- CMD_OTA_STOP_MCU_ACK = 0x39, /* BLE → MCU: 停止应答 */
-} jb_cmd_t;
-
-/* ════════════════════════════════════════════════════════════════
- * 结果码
- * ════════════════════════════════════════════════════════════════ */
-typedef enum
-{
- JB_OK = 0x00,
- JB_PARAM_ERR = 0x01,
- JB_BUSY = 0x02,
- JB_NOT_SUPPORT = 0x03,
- JB_CRC_ERR = 0x04,
- JB_FAIL = 0x05,
- /* ── OTA 专用错误码 ── */
- JB_OTA_NO_NEED = 0x20, /* 无需升级(版本一致) */
- JB_OTA_VER_LOW = 0x21, /* OTA 版本低于当前版本 */
- JB_OTA_SPACE_ERR = 0x22, /* Flash 空间不足 */
- JB_OTA_STATE_ERR = 0x23, /* OTA 状态错误 */
- JB_OTA_FILE_ERR = 0x24, /* OTA 文件检验失败 */
- JB_OTA_FAIL = 0x25, /* 升级失败 */
-} jb_result_t;
-
-/* ════════════════════════════════════════════════════════════════
- * BLE 工作状态位 (0x14)
- * ════════════════════════════════════════════════════════════════ */
-#define WS_CONFIGURED (1 << 0) /* 已配网 */
-#define WS_INTERNET (1 << 1) /* 已连外网 */
-#define WS_APP_ONLINE (1 << 2) /* App 在线 */
-#define WS_BLE_CONNECTED (1 << 3) /* 手机 BLE 已连接 */
-#define WS_OTA_UPGRADING (1 << 4) /* OTA 升级中 */
-#define WS_PAIRING_MODE (1 << 5) /* 配对模式激活 */
-
-/* ════════════════════════════════════════════════════════════════
- * 设备能力位图 (0x02 Capability Bitmap, 8B 大端)
- * ════════════════════════════════════════════════════════════════ */
-#define CAP_BLE_OTA (1ULL << 0) /* BLE OTA */
-#define CAP_MCU_OTA (1ULL << 1) /* MCU OTA */
-#define CAP_APP_DIRECT (1ULL << 2) /* APP 直连 BLE */
-#define CAP_IPC_GATEWAY (1ULL << 3) /* IPC 网关连接 */
-#define CAP_PRODUCTION (1ULL << 4) /* 产测模式 */
-#define CAP_PARAM_CFG (1ULL << 5) /* 参数配置 */
-#define CAP_LOG (1ULL << 6) /* 日志功能 */
-
-/* ════════════════════════════════════════════════════════════════
- * 身份信息 (0x02) / MCU 信息 (0x41)
- * ════════════════════════════════════════════════════════════════ */
-/* ════════════════════════════════════════════════════════════════
- * DP ID 枚举(从 Excel 自动生成)
- * ════════════════════════════════════════════════════════════════ */
-typedef enum
-{
- DP_ID_POWER = 0, /* 开关 (bool, RW) */
- DP_ID_PLAY_MODE = 1, /* 自动模式 (enum, RW) */
- DP_ID_HAND_MODE = 2, /* 手动模式 (enum, WO) */
- DP_ID_ALM_LOW_BATTERY = 3, /* 低电量报警 (bool, RO) */
- DP_ID_BEEP_TRIG = 4, /* 声音开关 (bool, RW) */
- DP_ID_BATTERY = 5, /* 电量上报 (uint8, RO) */
- DP_ID_SHAKE_WAKE = 6, /* 震动触发开机 (bool, RW) */
- DP_ID_NO_DISTURB_ENABLE = 7, /* 勿扰模式开关 (raw, RW) */
- DP_ID_RESERVA_DP1 = 8, /* 预留DP1 (uint32, RW) */
- DP_ID_RESERVA_DP2 = 9, /* 预留DP2 (uint32, RW) */
- DP_ID_RESERVA_DP3 = 10, /* 预留DP3 (uint32, RW) */
- DP_ID_MAX
-} dp_id_t;
-
-/* DP 数据长度(字节) */
-#define DP_LEN_POWER 1
-#define DP_LEN_PLAY_MODE 1
-#define DP_LEN_HAND_MODE 1
-#define DP_LEN_ALM_LOW_BATTERY 1
-#define DP_LEN_BEEP_TRIG 1
-#define DP_LEN_BATTERY 1
-#define DP_LEN_SHAKE_WAKE 1
-#define DP_LEN_NO_DISTURB_ENABLE 13
-#define DP_LEN_RESERVA_DP1 4
-#define DP_LEN_RESERVA_DP2 4
-#define DP_LEN_RESERVA_DP3 4
-
-/* ════════════════════════════════════════════════════════════════
- * DP 枚举值定义
- * ════════════════════════════════════════════════════════════════ */
-/* 自动模式 */
-typedef enum
-{
- PLAY_MODE_0 = 0, /* 关 */
- PLAY_MODE_1 = 1, /* mode1 */
- PLAY_MODE_2 = 2, /* mode2 */
- PLAY_MODE_3 = 3, /* mode3 */
- PLAY_MODE_4 = 4, /* mode4 */
-} PLAY_MODE_t;
-
-/* 手动模式 */
-typedef enum
-{
- HAND_MODE_0 = 0, /* 关 */
- HAND_MODE_1 = 1, /* 顺时针 */
- HAND_MODE_2 = 2, /* 逆时针 */
-} HAND_MODE_t;
-
-/* ════════════════════════════════════════════════════════════════
- * 数据结构
- * ════════════════════════════════════════════════════════════════ */
-
-/* 数据点 — 所有 DP 的当前值 */
-typedef struct
-{
- uint8_t power; /* DP0 开关 */
- uint8_t play_mode; /* DP1 自动模式 */
- uint8_t hand_mode; /* DP2 手动模式 */
- uint8_t alm_low_battery;/* DP3 低电量报警 */
- uint8_t beep_trig; /* DP4 声音开关 */
- uint8_t battery; /* DP5 电量上报 */
- uint8_t shake_wake; /* DP6 震动触发开机 */
- uint8_t no_disturb_enable[13];/* DP7 勿扰模式开关 */
- uint32_t reserva_dp1; /* DP8 预留DP1 */
- uint32_t reserva_dp2; /* DP9 预留DP2 */
- uint32_t reserva_dp3; /* DP10 预留DP3 */
-} jb_data_point_t;
-
-/* 控制事件信息 */
-typedef struct
-{
- uint8_t count;
- uint8_t dp_ids[DP_ID_MAX];
-} jb_event_info_t;
-
-/* ACK 等待 / 重传管理器 */
-typedef struct
-{
- uint8_t active; /* 是否有等待中的请求 */
- uint8_t sn; /* 帧序列号 */
- uint8_t retry;
- uint8_t buf[MAX_PACKAGE_LEN];
- uint16_t len;
- uint32_t sendTime;
-} jb_wait_ack_t;
-
-/* 协议全局状态 */
-typedef struct
-{
- uint8_t sn; /* 设备 SN 计数器 */
- uint8_t rxBuf[MAX_PACKAGE_LEN];
- jb_wait_ack_t waitAck; /* ACK 重传管理器 */
- jb_data_point_t lastData; /* 上次上报快照 */
- uint32_t lastReportTime; /* 上次上报时刻 */
- uint32_t lastChangeTime; /* 上次变化上报时刻 */
-} jb_protocol_t;
-
-/* ════════════════════════════════════════════════════════════════
- * 全局实例
- * ════════════════════════════════════════════════════════════════ */
-extern jb_protocol_t jbProtocol;
-
-/* ════════════════════════════════════════════════════════════════
- * 协议层 API(自动生成)
- * ════════════════════════════════════════════════════════════════ */
-void jbInit(void);
-int32_t jbPutData(uint8_t *buf, uint32_t len);
-
-int32_t jbNotifyOtaStop(void);
-int32_t jbSendOtaResult(uint8_t result, const uint8_t *mcuVersion);
-
-int32_t jbProtocolHandle(jb_data_point_t *data);
-
-/*
- * 网关应答确认(主动上报 / OTA 共用,REPORTCMD_V2,非 55AA)
- * cmd: ble_proto 包 cmd
- * gateway_int_cnt: gateway int 个数
- * gateway_int0: gateway_int[0]
- * 成功条件: cmd==0 && gateway_int_cnt==1 && gateway_int0==0
- * 返回: 1=成功并清除等待; 0=未命中/条件不符
- */
-int8_t jbReportAckCheck(uint16_t cmd, uint8_t gateway_int_cnt, int32_t gateway_int0);
-
-/* ════════════════════════════════════════════════════════════════
- * 硬件抽象(用户实现)
- * ════════════════════════════════════════════════════════════════ */
-uint32_t jbGetTimerCount(void); /* ms 计数器(定时器由 SOC SDK 提供) */
-
-/* 发送接口:经 SOC SDK 提供的 BLE 发送封装(由用户/SDK 实现,非本生成代码范围)*/
-extern void ble_send_data(uint8_t *data, uint16_t len);
-
-/* ════════════════════════════════════════════════════════════════
- * 用户回调(用户实现)
- * ════════════════════════════════════════════════════════════════ */
-/* 0x03 APP 下发控制事件 */
-int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *data);
-
-/* BLE → MCU 事件回调 */
-void jbOnOtaNotify(uint32_t fwVersion, uint32_t fwLength, uint32_t fwCrc32); /* 0x30 */
-void jbOnOtaData(const uint8_t *data, uint16_t len,uint16_t shardIdx, uint16_t totalShards); /* 0x32 */
-void jbOnOtaStop(void); /* 0x36 / 0x38 停止 */
-
-/* MCU → BLE 应答回调(收到 BLE 应答时触发)*/
-void jbOnAck(uint8_t cmd, uint8_t result); /* 任意请求 ACK(结果) */
-
-#endif /* _JB_PROTOCOL_H */
diff --git a/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Utils/jb_common.c b/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Utils/jb_common.c
deleted file mode 100644
index a631a1b..0000000
--- a/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Utils/jb_common.c
+++ /dev/null
@@ -1,69 +0,0 @@
-/* ================================================================
- * jb_common.c — 工具函数实现
- * ================================================================
- */
-#include "jb_common.h"
-
-/* ════════════════════════════════════════════════════════════════
- * 校验和
- * ════════════════════════════════════════════════════════════════ */
-uint8_t jbChecksum(const uint8_t *frame, uint16_t totalLen)
-{
- if (!frame || totalLen < 4)
- {
- return 0;
- }
- uint16_t sum = 0;
- /* 从 LEN_H(buf[2]) 累加到最后一个 payload 字节(buf[totalLen-2]) */
- for (uint16_t i = 2; i < totalLen - 1; i++)
- {
- sum += frame[i];
- }
- return (uint8_t)(sum & 0xFF);
-}
-
-/* ════════════════════════════════════════════════════════════════
- * 大端序读写
- * ════════════════════════════════════════════════════════════════ */
-uint16_t jbReadU16BE(const uint8_t *buf)
-{
- if (!buf)
- {
- return 0;
- }
- return ((uint16_t)buf[0] << 8) | buf[1];
-}
-
-uint32_t jbReadU32BE(const uint8_t *buf)
-{
- if (!buf)
- {
- return 0;
- }
- return ((uint32_t)buf[0] << 24)
- | ((uint32_t)buf[1] << 16)
- | ((uint32_t)buf[2] << 8)
- | buf[3];
-}
-
-void jbWriteU16BE(uint8_t *buf, uint16_t val)
-{
- if (!buf)
- {
- return;
- }
- buf[0] = (uint8_t)(val >> 8);
- buf[1] = (uint8_t)(val & 0xFF);
-}
-
-void jbWriteU32BE(uint8_t *buf, uint32_t val)
-{
- if (!buf)
- {
- return;
- }
- buf[0] = (uint8_t)(val >> 24);
- buf[1] = (uint8_t)(val >> 16);
- buf[2] = (uint8_t)(val >> 8);
- buf[3] = (uint8_t)(val & 0xFF);
-}
diff --git a/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Utils/jb_common.h b/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Utils/jb_common.h
deleted file mode 100644
index c9face2..0000000
--- a/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Utils/jb_common.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/* ================================================================
- * jb_common.h — 工具函数(校验和、大端序转换)
- *
- * 说明: 见宝协议采用大端序(Big-Endian)。
- * 多字节值(uint16/uint32)按高位在前传输。
- * ================================================================
- */
-#ifndef _JB_COMMON_H
-#define _JB_COMMON_H
-
-#include
-#include
-
-/* ════════════════════════════════════════════════════════════════
- * 校验和
- * sum(LEN_H, LEN_L, CMD, SN, ...payload_last) & 0xFF
- * buf[0]=55, buf[1]=AA,累加范围从 buf[2] 到 buf[totalLen-2]
- * ════════════════════════════════════════════════════════════════ */
-uint8_t jbChecksum(const uint8_t *frame, uint16_t totalLen);
-
-/* ════════════════════════════════════════════════════════════════
- * 大端序转换
- * ════════════════════════════════════════════════════════════════ */
-
-/* 从字节数组读取 uint16(大端序)*/
-uint16_t jbReadU16BE(const uint8_t *buf);
-
-/* 从字节数组读取 uint32(大端序)*/
-uint32_t jbReadU32BE(const uint8_t *buf);
-
-/* 将 uint16 写入字节数组(大端序)*/
-void jbWriteU16BE(uint8_t *buf, uint16_t val);
-
-/* 将 uint32 写入字节数组(大端序)*/
-void jbWriteU32BE(uint8_t *buf, uint32_t val);
-
-#endif /* _JB_COMMON_H */
diff --git a/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Utils/jb_ringbuffer.c b/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Utils/jb_ringbuffer.c
deleted file mode 100644
index b9c2e28..0000000
--- a/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Utils/jb_ringbuffer.c
+++ /dev/null
@@ -1,75 +0,0 @@
-/* ================================================================
- * jb_ringbuffer.c — 环形缓冲区实现
- * ================================================================
- */
-#include "jb_ringbuffer.h"
-
-/* ── 初始化 ── */
-void jbRbInit(jb_ringbuffer_t *rb)
-{
- if (!rb)
- {
- return;
- }
- rb->write = rb->read = rb->count = 0;
-}
-
-/* ── 压入一个字节 ── */
-void jbRbPush(jb_ringbuffer_t *rb, uint8_t byte)
-{
- if (!rb)
- {
- return;
- }
- if (rb->count >= JB_RB_CAPACITY)
- {
- return; /* 已满 — 丢弃 */
- }
- rb->buf[rb->write] = byte;
- rb->write = (rb->write + 1) % JB_RB_CAPACITY;
- rb->count++;
-}
-
-/* ── 弹出一个字节 ── */
-uint8_t jbRbPop(jb_ringbuffer_t *rb)
-{
- if (!rb || rb->count == 0)
- {
- return 0;
- }
- uint8_t byte = rb->buf[rb->read];
- rb->read = (rb->read + 1) % JB_RB_CAPACITY;
- rb->count--;
- return byte;
-}
-
-/* ── 查看指定偏移字节(不消费)── */
-uint8_t jbRbPeek(const jb_ringbuffer_t *rb, uint16_t offset)
-{
- if (!rb || offset >= rb->count)
- {
- return 0;
- }
- uint16_t idx = (rb->read + offset) % JB_RB_CAPACITY;
- return rb->buf[idx];
-}
-
-/* ── 可读字节数 ── */
-uint16_t jbRbAvailable(const jb_ringbuffer_t *rb)
-{
- if (!rb)
- {
- return 0;
- }
- return rb->count;
-}
-
-/* ── 清空缓冲区 ── */
-void jbRbClear(jb_ringbuffer_t *rb)
-{
- if (!rb)
- {
- return;
- }
- rb->write = rb->read = rb->count = 0;
-}
diff --git a/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Utils/jb_ringbuffer.h b/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Utils/jb_ringbuffer.h
deleted file mode 100644
index 190a82b..0000000
--- a/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Utils/jb_ringbuffer.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/* ================================================================
- * jb_ringbuffer.h — 环形缓冲区(UART 接收)
- * ================================================================
- */
-#ifndef _JB_RINGBUFFER_H
-#define _JB_RINGBUFFER_H
-
-#include
-#include
-
-/* 缓冲区配置 */
-#define JB_RB_CAPACITY 256 /* 缓冲区容量(2 的幂可优化取模运算)*/
-
-/* 环形缓冲区实例 */
-typedef struct
-{
- uint8_t buf[JB_RB_CAPACITY];
- uint16_t write; /* 写索引 */
- uint16_t read; /* 读索引 */
- uint16_t count; /* 已用字节数 */
-} jb_ringbuffer_t;
-
-void jbRbInit(jb_ringbuffer_t *rb);
-void jbRbPush(jb_ringbuffer_t *rb, uint8_t byte);
-uint8_t jbRbPop(jb_ringbuffer_t *rb);
-uint8_t jbRbPeek(const jb_ringbuffer_t *rb, uint16_t offset);
-uint16_t jbRbAvailable(const jb_ringbuffer_t *rb);
-void jbRbClear(jb_ringbuffer_t *rb);
-
-#endif /* _JB_RINGBUFFER_H */
diff --git a/JBao_JL7016_MOD_FW/apps/usr_jb_proto/usr_jb_main.c b/JBao_JL7016_MOD_FW/apps/usr_jb_proto/usr_jb_main.c
deleted file mode 100644
index 80b0657..0000000
--- a/JBao_JL7016_MOD_FW/apps/usr_jb_proto/usr_jb_main.c
+++ /dev/null
@@ -1,49 +0,0 @@
-#include "system/includes.h"
-#include "app_config.h"
-#include "app_main.h"
-#include "app_task.h"
-#include "user_cfg.h"
-#include "vm.h"
-#include "sys_time.h"
-
-#include "jb_protocol.h"
-#include "jb_product.h"
-#include "jb_ringbuffer.h"
-#include "jb_common.h"
-#include "usr_le_product.h"
-
-/* 向 usr_le_code 提供产品身份(换产品只改 jb_protocol.h 宏即可) */
-const uint8_t g_usr_le_prodcode[USR_LE_PRODCODE_LEN] = {
- JB_CATEGORY_CODE[0], JB_CATEGORY_CODE[1],
- JB_CATEGORY_CODE[2], JB_CATEGORY_CODE[3]
-};
-const uint8_t g_usr_le_pcode[USR_LE_PCODE_LEN] = {
- JB_PRODUCT_CODE[0], JB_PRODUCT_CODE[1], JB_PRODUCT_CODE[2],
- JB_PRODUCT_CODE[3], JB_PRODUCT_CODE[4], JB_PRODUCT_CODE[5]
-};
-
-void usr_timer_1_ms();
-static int jb_timer_id=0;
-
-void usr_jb_init()
-{
- jbInit();
- userInit();
- if(jb_timer_id==0)jb_timer_id = sys_s_hi_timer_add(NULL, usr_timer_1_ms, TIMER_PERIOD_MS);
-}
-
-void usr_timer_1_ms()
-{
- jbTimerIrq();
-}
-
-void usr_jb_le_recieve_data(u8* data,u16 len)
-{
- jbPutData(data,len);
-}
-
-void usr_timer_loop()///500ms
-{
- userHandle();
- jbProtocolHandle(&gDevData);
-}
diff --git a/JBao_JL7016_MOD_FW/apps/usr_le_code/bleproto_packer.h b/JBao_JL7016_MOD_FW/apps/usr_le_code/bleproto_packer.h
index 9b5661f..01bb9b0 100644
--- a/JBao_JL7016_MOD_FW/apps/usr_le_code/bleproto_packer.h
+++ b/JBao_JL7016_MOD_FW/apps/usr_le_code/bleproto_packer.h
@@ -403,6 +403,11 @@ typedef struct bleproto_authsetup_req {
uint8_t gwmac[6]; ///< 主控mac地址, 设备需要保存
char devid[36 + 1]; ///< 主控分配的子设备id, 设备需要保存
char authcode[32 + 1]; ///< 主控分配的子设备认证码, 设备需要保存
+ /**
+ * paired_flag:设备是否已完成配网(持久化在 VM)
+ * 0 = 未配网:默认不广播,除非 usr_goto_pair=1(绑定广播)
+ * 1 = 已配网:断连后发回连广播;AUTHSETUP 写三元组成功后置 1
+ */
uint8_t paired_flag;
// clang-format on
} bleproto_authsetup_req_t;
diff --git a/JBao_JL7016_MOD_FW/apps/usr_le_code/usr_le_adv.c b/JBao_JL7016_MOD_FW/apps/usr_le_code/usr_le_adv.c
index befd16e..fc54e89 100644
--- a/JBao_JL7016_MOD_FW/apps/usr_le_code/usr_le_adv.c
+++ b/JBao_JL7016_MOD_FW/apps/usr_le_code/usr_le_adv.c
@@ -15,8 +15,9 @@
#include "bleproto.h"
#include "bleproto_api.h"
#include "usr_le_product.h"
+#include "mcu_bridge.h"
#define LOG_TAG_CONST APP
-#define LOG_TAG "[APP]"
+#define LOG_TAG "[LE_ADV]"
#define LOG_ERROR_ENABLE
#define LOG_DEBUG_ENABLE
#define LOG_INFO_ENABLE
@@ -24,6 +25,27 @@
#define LOG_CLI_ENABLE
#include "debug.h"
+/******************************************************************************
+ * 广播相关三个标志(务必区分):
+ *
+ * 1) usr_auth_data.paired_flag —— 配网结果(存 VM,掉电保持)
+ * - 0:未配网
+ * - 1:已配网(三元组已保存)
+ *
+ * 2) usr_var.usr_goto_pair —— 是否主动进入配网流程(RAM,掉电丢失)
+ * - 0:非配网流程
+ * - 1:MCU 0x10 / usr_goto_pair_mode() 触发,发【绑定广播】
+ *
+ * 3) usr_var.usr_adv_flag —— 广播内容是否需要刷新到协议栈(一次性)
+ * - 1:usr_ble_adv_updata() 会重新 set adv data 并 enable
+ * - 0:本周期不刷新
+ *
+ * 决策表(身份就绪后):
+ * usr_goto_pair==1 → 绑定广播(重新配网)
+ * paired_flag==0 且未配网 → 不广播
+ * paired_flag==1 → 回连广播
+ ******************************************************************************/
+
extern u8 muti_adv_data[31];//={0};
extern u8 muti_adv_data_len;
@@ -46,6 +68,10 @@ extern u8 usr_get_work_status();
extern void usr_printf_sta();
extern int muti_make_set_adv_data_updata();
+/**
+ * @brief 从 VM 读取配对信息到 usr_auth_data
+ * @note 失败则清零,并将 paired_flag 置 0 写回
+ */
void usr_get_pair_info()
{
int ret = 0xffff;
@@ -53,46 +79,69 @@ void usr_get_pair_info()
if(ret<=0)
{
memset(&usr_auth_data,0x00,sizeof(usr_auth_data));
- usr_auth_data.paired_flag=0;
+ usr_auth_data.paired_flag=0; /* 未配网 */
syscfg_write(CFG_USER_PAIR_INFO, (u8 *)&usr_auth_data, sizeof(usr_auth_data));
}
}
+
+/**
+ * @brief 标记已配网并保存三元组到 VM
+ * @note AUTHSETUP 成功后调用,paired_flag = 1
+ */
void usr_write_pair_info()
{
- usr_auth_data.paired_flag=1;
+ usr_auth_data.paired_flag=1; /* 已配网 */
syscfg_write(CFG_USER_PAIR_INFO, (u8 *)&usr_auth_data, sizeof(usr_auth_data));
}
+/**
+ * @brief 清除配对信息并延时复位
+ */
void usr_clear_pair_info()
{
printf("usr_clear_pair_info\n");
memset(&usr_auth_data,0x00,sizeof(usr_auth_data));
- usr_auth_data.paired_flag=0;
+ usr_auth_data.paired_flag=0; /* 回到未配网 */
syscfg_write(CFG_USER_PAIR_INFO, (u8 *)&usr_auth_data, sizeof(usr_auth_data));
+ mcu_bridge_on_pair_cleared();
sys_timeout_add(NULL,cpu_reset,600);
}
+
+/**
+ * @brief 清除配对信息但不复位(用于重新配网)
+ */
void usr_clear_pair_info_noreset()
{
printf("usr_clear_pair_info2\n");
memset(&usr_auth_data,0x00,sizeof(usr_auth_data));
- usr_auth_data.paired_flag=0;
+ usr_auth_data.paired_flag=0; /* 回到未配网,后续由 usr_goto_pair 决定是否绑广播 */
syscfg_write(CFG_USER_PAIR_INFO, (u8 *)&usr_auth_data, sizeof(usr_auth_data));
+ mcu_bridge_on_pair_cleared();
}
extern void usr_set_adv_interval(u16 val);
extern u16 usr_get_adv_interval();
extern void ble_multi_trans_disconnect(void);
+extern void ble_trans_module_enable(u8 flag);
void usr_ble_adv_init();
+
+/**
+ * @brief 进入配网模式:清配对 + 置 usr_goto_pair + 发绑定广播
+ * @note MCU 0x10 配网请求最终会走到这里
+ */
void usr_goto_pair_mode()
{
ble_multi_trans_disconnect();
usr_clear_pair_info_noreset();
- usr_var.usr_goto_pair=1;
+ usr_var.usr_goto_pair=1; /* 进入主动配网流程 → 绑定广播 */
os_time_dly(5);
usr_ble_adv_init();
}
-
+/**
+ * @brief 按当前标志刷新广播策略(不一定开广播)
+ * @note 见文件头「三个标志」说明;未拿身份 / 未配网时保持不广播
+ */
void usr_ble_adv_init()
{
int8_t txpower = 0;
@@ -101,36 +150,48 @@ void usr_ble_adv_init()
uint8_t gmac[6] = {0};
uint8_t pcode[BLE_PROTO_ADV_LENGTH_PCODE];
- /* 产品身份由应用层 g_usr_le_* 提供(见 usr_jb_main.c) */
+ /* 产品身份由 MCU 桥写入 g_usr_le_* */
memcpy(prodcode, g_usr_le_prodcode, BLEPROTO_ADV_TLV_LENGTH_PRODCODE);
memcpy(pcode, g_usr_le_pcode, BLE_PROTO_ADV_LENGTH_PCODE);
usr_set_adv_interval(80);
if(usr_var.usr_ota_succ_flag)return;
- //muti_adv_data
- if(usr_var.usr_goto_pair)
- {
- /* app_main 上电已置 usr_goto_pair=1 并清配对信息,此处发绑定广播供网关配网 */
- usr_clear_pair_info_noreset();
- printf("power on force to par flag= %d\n",usr_auth_data.paired_flag);
- {
- muti_adv_data_len=bleproto_advdata_encode4bind(muti_adv_data,txpower,manucode,pcode,prodcode);
+
+ /* 未拿到 MCU 身份:完全不广播 */
+ if (!mcu_bridge_is_identity_ready()) {
+ printf("usr_ble_adv_init: wait MCU identity, no adv\n");
+ usr_var.usr_adv_flag = 0; /* 不请求刷新广播 */
+ ble_trans_module_enable(0);
+ if (le_timer_handle == 0) {
+ le_timer_handle = sys_timer_add(NULL, usr_ble_adv_updata, 500);
}
- usr_var.usr_adv_flag=1;
+ return;
+ }
+
+ if(usr_var.usr_goto_pair)
+ {
+ /* 主动配网中:发绑定广播,请求刷新到协议栈 */
+ usr_clear_pair_info_noreset();
+ printf("goto pair bind adv, paired_flag= %d\n",usr_auth_data.paired_flag);
+ muti_adv_data_len=bleproto_advdata_encode4bind(muti_adv_data,txpower,manucode,pcode,prodcode);
+ usr_var.usr_adv_flag=1; /* 请求:写入绑定广播并开启 */
}
else
{
usr_get_pair_info();
if(usr_auth_data.paired_flag==0)
{
- muti_adv_data_len=bleproto_advdata_encode4bind(muti_adv_data,txpower,manucode,pcode,prodcode);
+ /* 未配网且未进入配网流程:完全不广播 */
+ printf("usr_ble_adv_init: not paired, no adv\n");
usr_var.usr_adv_flag=0;
+ ble_trans_module_enable(0);
}
else
{
+ /* 已配网:回连广播 */
memcpy(gmac,usr_auth_data.gwmac,6);
muti_adv_data_len=bleproto_advdata_encode4reconn(muti_adv_data,txpower,gmac,manucode,pcode,prodcode);
- usr_var.usr_adv_flag=1;
+ usr_var.usr_adv_flag=1; /* 请求:写入回连广播并开启 */
}
}
@@ -138,6 +199,18 @@ void usr_ble_adv_init()
if(le_timer_handle==0)le_timer_handle = sys_timer_add(NULL, usr_ble_adv_updata, 500);
}
+/**
+ * @brief 获取是否已配网
+ * @return 1=已配网(paired_flag),0=未配网
+ */
+/**
+ * @brief 获取是否已配网
+ * @return 1=已配网(paired_flag),0=未配网
+ */
+/**
+ * @brief 获取是否已配网
+ * @return paired_flag:1=已配网,0=未配网
+ */
u8 usr_get_pair_flag()
{
return usr_auth_data.paired_flag;
@@ -154,9 +227,12 @@ void usr_reconn_adv()
muti_adv_data_len=bleproto_advdata_encode4reconn(muti_adv_data,txpower,gmac,manucode,pcode,prodcode);
usr_adv_flag=1;
}*/
-extern void ble_trans_module_enable(u8 flag);
-extern void usr_timer_loop();
extern u8 usr_get_ota_status();
+
+/**
+ * @brief 周期处理广播刷新(由 500ms 定时器调用)
+ * @note 仅当 usr_adv_flag==1 时:停广播 → 写入 muti_adv_data → 再开广播,并清 usr_adv_flag
+ */
void usr_ble_adv_updata()
{
//printf("usr_ble_adv_updata\n");
@@ -164,7 +240,7 @@ void usr_ble_adv_updata()
if(usr_var.usr_adv_flag)
{
ble_trans_module_enable(0);
- usr_var.usr_adv_flag=0;
+ usr_var.usr_adv_flag=0; /* 一次性触发,用完即清 */
muti_make_set_adv_data(muti_adv_data,muti_adv_data_len);
printf("###muti_adv_data = ");
printf_buf(muti_adv_data,muti_adv_data_len);
@@ -208,9 +284,6 @@ void usr_ble_adv_updata()
if(usr_var.usr_ota_succ_flag==0)usr_ota_exit();
}
}
-
- /* 协议层周期处理(上报/应答) */
- usr_timer_loop();
}
void usr_soft_off()
diff --git a/JBao_JL7016_MOD_FW/apps/usr_le_code/usr_le_api.h b/JBao_JL7016_MOD_FW/apps/usr_le_code/usr_le_api.h
index 69ef648..71e3b9c 100644
--- a/JBao_JL7016_MOD_FW/apps/usr_le_code/usr_le_api.h
+++ b/JBao_JL7016_MOD_FW/apps/usr_le_code/usr_le_api.h
@@ -1,11 +1,12 @@
/******************************************************************************
* @file usr_le_api.h
- * @brief usr_le_code 对外 API(广播 / 收发 / 配网)
+ * @brief usr_le_code 对外 API(广播 / 收发 / 配网 / MCU 帧转发)
* @author cyWu <1917507415@qq.com>
- * @date 2026.08.03
- * @version V1.0.0
+ * @date 2026.08.06
+ * @version V1.1.0
* @history
* - V1.0.0, 2026.08.03, cyWu, 首次发布
+ * - V1.1.0, 2026.08.06, cyWu, 增加 MCU 帧 BLE 发送接口,解耦模组桥
******************************************************************************/
#ifndef __USR_LE_API_H__
@@ -18,8 +19,12 @@ extern "C" {
#endif
/**
- * @brief 初始化 BLE 广播数据(绑定广播或重连广播)
- * @note 根据配对状态与 usr_goto_pair 选择 bind/reconn 广播,并启动 500ms 周期更新
+ * @brief 初始化/刷新 BLE 广播策略(不一定开启广播)
+ * @note 决策依赖三个标志:
+ * - paired_flag:是否已配网(VM)
+ * - usr_goto_pair:是否主动配网中(发绑定广播)
+ * - usr_adv_flag:是否请求把新广播包刷到协议栈
+ * 详见 usr_le_adv.c 文件头注释
*/
void usr_ble_adv_init(void);
@@ -27,18 +32,25 @@ void usr_ble_adv_init(void);
* @brief BLE 协议栈接收数据入口
* @param data 接收缓冲区
* @param len 本次接收长度
- * @note 组包后解析 bleproto,并分发 DEVICEINFO/AUTH/RUNCMD/OTA 等服务
*/
void usr_le_data_recieve(uint8_t *data, uint16_t len);
+/**
+ * @brief 将 MCU 协议 55AA 帧封装为 bleproto 后经 GATT 发送
+ * @param data 完整 55AA 帧
+ * @param len 帧长度
+ * @note 0x07/0x34/0x38 走 REPORTCMD_V2 主动上报;其余走 RUNCMD_V2 应答
+ */
+void usr_le_send_mcu_frame(uint8_t *data, uint16_t len);
+
/**
* @brief 进入配对/配网模式
- * @note 断开当前连接、清除配对信息(不复位),重新发绑定广播
+ * @note 清配对信息,置 usr_goto_pair=1,发绑定广播(非回连)
*/
void usr_goto_pair_mode(void);
/**
- * @brief 清除配对信息但不复位
+ * @brief 清除配对信息但不复位(paired_flag→0)
*/
void usr_clear_pair_info_noreset(void);
@@ -48,14 +60,13 @@ void usr_clear_pair_info_noreset(void);
void usr_clear_pair_info(void);
/**
- * @brief 获取当前是否已配对
- * @return 1=已配对,0=未配对
+ * @brief 获取是否已配网
+ * @return paired_flag:1=已配网,0=未配网
*/
uint8_t usr_get_pair_flag(void);
/**
* @brief 启动应用侧 OTA 流程
- * @note 置位 usr_goto_updata,并创建 1s 周期定时器驱动断连与 OTA 广播切换
*/
void usr_app_ota_init(void);
diff --git a/JBao_JL7016_MOD_FW/apps/usr_le_code/usr_le_product.h b/JBao_JL7016_MOD_FW/apps/usr_le_code/usr_le_product.h
index f4bb613..58cae62 100644
--- a/JBao_JL7016_MOD_FW/apps/usr_le_code/usr_le_product.h
+++ b/JBao_JL7016_MOD_FW/apps/usr_le_code/usr_le_product.h
@@ -1,11 +1,12 @@
/******************************************************************************
* @file usr_le_product.h
- * @brief usr_le_code 产品身份配置接口(由应用层提供符号,模块内不写死)
+ * @brief usr_le_code 产品身份配置接口(由 MCU 桥运行时填充)
* @author cyWu <1917507415@qq.com>
* @date 2026.08.03
- * @version V1.0.0
+ * @version V1.1.0
* @history
* - V1.0.0, 2026.08.03, cyWu, 首次发布
+ * - V1.1.0, 2026.08.06, cyWu, 改为非 const,由 mcu_bridge 从 MCU 0x02 写入
******************************************************************************/
#ifndef __USR_LE_PRODUCT_H__
@@ -29,15 +30,15 @@ extern "C" {
/**
* @brief 产品类别码,对应 BLE 广播 prodcode(固定 4 字节 ASCII)
- * @note 应用层用 JB_CATEGORY_CODE 填充,见 usr_jb_main.c
+ * @note 由 mcu_bridge 从 MCU 身份应答取前 4 字节写入
*/
-extern const uint8_t g_usr_le_prodcode[USR_LE_PRODCODE_LEN];
+extern uint8_t g_usr_le_prodcode[USR_LE_PRODCODE_LEN];
/**
* @brief 平台产品码,对应 BLE 广播 pcode(固定 6 字节 ASCII)
- * @note 应用层用 JB_PRODUCT_CODE 填充,见 usr_jb_main.c
+ * @note 由 mcu_bridge 从 MCU 身份应答取前 6 字节写入
*/
-extern const uint8_t g_usr_le_pcode[USR_LE_PCODE_LEN];
+extern uint8_t g_usr_le_pcode[USR_LE_PCODE_LEN];
#ifdef __cplusplus
}
diff --git a/JBao_JL7016_MOD_FW/apps/usr_le_code/usr_le_recieve.c b/JBao_JL7016_MOD_FW/apps/usr_le_code/usr_le_recieve.c
index 5a28e16..81245f9 100644
--- a/JBao_JL7016_MOD_FW/apps/usr_le_code/usr_le_recieve.c
+++ b/JBao_JL7016_MOD_FW/apps/usr_le_code/usr_le_recieve.c
@@ -16,6 +16,7 @@
#include "bleproto.h"
#include "bleproto_api.h"
#include "ble_proto.h"
+#include "mcu_frame.h"
#define LOG_TAG_CONST APP
#define LOG_TAG "[APP]"
#define LOG_ERROR_ENABLE
@@ -43,8 +44,12 @@ 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 usr_jb_le_recieve_data(u8 *data, u16 len);
-extern int8_t jbReportAckCheck(u16 cmd, u8 gateway_int_cnt, int32_t gateway_int0);
+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);
void usr_ota_deal_task();
void usr_data_ana(bleproto_appdata_desc_t *desc);
@@ -61,6 +66,14 @@ void usr_le_data_recieve(u8 *data, u16 len) /// BLE数据接收
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;
+ }
+
memcpy(&rxbuf[usr_len], data, len);
usr_len += len;
rc_result = bleproto_appdata_decode(rxbuf, usr_len, &desc);
@@ -149,6 +162,7 @@ void usr_data_ana(bleproto_appdata_desc_t *desc)
if (usr_auth_data.paired_flag == 0)
{
usr_write_pair_info();
+ mcu_bridge_on_paired();
printf("usr_auth_data %s %s ", usr_auth_data.authcode, usr_auth_data.devid);
printf_buf(usr_auth_data.gwmac, 6);
}
@@ -305,16 +319,16 @@ void usr_data_ana(bleproto_appdata_desc_t *desc)
break;
case E_BLEPROTO_SERVICE_ID_REPORTCMD_V2:
{
- /* 网关对主动上报 / OTA 的应答(非 55AA):
- * cmd==0 && gateway_int_cnt==1 && gateway_int[0]==0 → 应答成功 */
+ /* 网关对 MCU 主动上报的应答:成功后由桥接层回 MCU 0x08 */
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",
(unsigned)rpt_ack->cmd,
(unsigned)rpt_ack->gateway_int_cnt,
(long)((rpt_ack->gateway_int_cnt > 0) ? rpt_ack->gateway_int[0] : 0));
- jbReportAckCheck(rpt_ack->cmd,
- rpt_ack->gateway_int_cnt,
- (rpt_ack->gateway_int_cnt > 0) ? rpt_ack->gateway_int[0] : -1);
+ mcu_bridge_on_gateway_report_ack(
+ rpt_ack->cmd,
+ rpt_ack->gateway_int_cnt,
+ (rpt_ack->gateway_int_cnt > 0) ? rpt_ack->gateway_int[0] : -1);
break;
}
case E_BLEPROTO_SERVICE_ID_OTADATA:
@@ -431,7 +445,7 @@ void usr_ota_deal_task()
static u8 s_last_runcmd_msgid = 0;
void usr_run_cmd_code_v2(ble_proto_packet_t *runcmd_par, u8 usr_msg_id)
{
- u8 jb_frame[128]; /* 与 jb_protocol.h MAX_PACKAGE_LEN 一致 */
+ u8 mcu_frame[MCU_FRAME_MAX_LEN];
u16 copy_len;
if (!runcmd_par || !runcmd_par->bytes_data || runcmd_par->device_bytes_len == 0)
@@ -444,45 +458,40 @@ void usr_run_cmd_code_v2(ble_proto_packet_t *runcmd_par, u8 usr_msg_id)
printf("runcmd_par id = %d, bytes_len = %u\n",
runcmd_par->cmd, runcmd_par->device_bytes_len);
- /* 先拷到本地再入 JB 环缓,避免零拷贝指针被后续逻辑覆盖 */
copy_len = runcmd_par->device_bytes_len;
- if (copy_len > sizeof(jb_frame))
+ if (copy_len > sizeof(mcu_frame))
{
printf("usr_run_cmd_code_v2 bytes too long %u\n", copy_len);
return;
}
- memcpy(jb_frame, runcmd_par->bytes_data, copy_len);
+ memcpy(mcu_frame, runcmd_par->bytes_data, copy_len);
s_last_runcmd_msgid = usr_msg_id;
- usr_jb_le_recieve_data(jb_frame, copy_len);
+ mcu_bridge_on_app_frame(mcu_frame, copy_len);
}
/**
- * @brief 将 JB 协议帧封装为 bleproto V2 后通过 BLE 发送
- * @param data JB 协议原始帧(55 AA | LEN | CMD | ...)
+ * @brief 将 MCU 协议帧封装为 bleproto V2 后通过 BLE 发送
+ * @param data 55AA 完整帧
* @param len 帧长度
- * @note 根据 JB CMD 区分主动上报 / 应答:
- * - 主动(0x07/0x34/0x38): REPORTCMD_V2 + REQ + cmd=100
- * - 应答(其余): RUNCMD_V2 + RSP + cmd=110
+ * @note 主动上报(0x07/0x34/0x38) → REPORTCMD_V2;其余 → RUNCMD_V2 应答
*/
-void ble_send_data(u8 *data, u16 len)
+void usr_le_send_mcu_frame(u8 *data, u16 len)
{
bleproto_appdata_desc_t encode_desc = {0};
ble_proto_packet_t *pkt = NULL;
- u8 jb_cmd;
+ u8 mcu_cmd;
u8 is_active_send;
int elen;
if (!data || len < 5)
{
- printf("ble_send_data invalid param, len=%d\n", len);
+ printf("usr_le_send_mcu_frame invalid param, len=%d\n", len);
return;
}
- /* JB帧: 55 AA | LEN_H LEN_L | CMD | SN | ...,CMD 在偏移 4 */
- jb_cmd = data[4];
- /* 主动上报 0x07;MCU 主动 OTA 完成/停止 0x34/0x38;其余按应答 */
- is_active_send = (jb_cmd == 0x07) || (jb_cmd == 0x34) || (jb_cmd == 0x38);
+ mcu_cmd = data[4];
+ is_active_send = (mcu_cmd == 0x07) || (mcu_cmd == 0x34) || (mcu_cmd == 0x38);
encode_desc.header.version = 0;
encode_desc.header.datafmt = BLEPROTO_APPDATA_DATAFMT_JSON;
@@ -490,7 +499,6 @@ void ble_send_data(u8 *data, u16 len)
if (is_active_send)
{
- /* 主动发送:REPORTCMD_V2 / REQ / cmd=100 */
encode_desc.header.msgtype = BLEPROTO_APPDATA_MSGTYPE_REQ;
encode_desc.header.msgid = 0;
encode_desc.header.serviceid = E_BLEPROTO_SERVICE_ID_REPORTCMD_V2;
@@ -502,7 +510,6 @@ void ble_send_data(u8 *data, u16 len)
}
else
{
- /* 应答命令:RUNCMD_V2 / RSP / cmd=110,回填最新 msgid */
encode_desc.header.msgtype = BLEPROTO_APPDATA_MSGTYPE_RSP;
encode_desc.header.msgid = s_last_runcmd_msgid;
encode_desc.header.serviceid = E_BLEPROTO_SERVICE_ID_RUNCMD_V2;
@@ -521,8 +528,8 @@ void ble_send_data(u8 *data, u16 len)
pkt->bytes_data = data;
elen = bleproto_appdata_encode(txbuf, sizeof(txbuf), sizeof(txbuf), &encode_desc);
- printf("ble_send_data jb_cmd=0x%02X active=%d encode_len=%d msgid=%d\n",
- jb_cmd, is_active_send, elen, encode_desc.header.msgid);
+ printf("usr_le_send_mcu_frame cmd=0x%02X active=%d encode_len=%d msgid=%d\n",
+ mcu_cmd, is_active_send, elen, encode_desc.header.msgid);
if (elen > 0)
{
usr_ble_send_data(txbuf, elen);
diff --git a/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_bridge.c b/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_bridge.c
new file mode 100644
index 0000000..709581d
--- /dev/null
+++ b/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_bridge.c
@@ -0,0 +1,1090 @@
+/******************************************************************************
+ * @file mcu_bridge.c
+ * @brief BLE+MCU 协议桥 P0 实现
+ * @author cyWu <1917507415@qq.com>
+ * @date 2026.08.06
+ * @version V1.2.0
+ * @history
+ * - V1.0.0, 2026.08.06, cyWu, 首次发布
+ * - V1.1.0, 2026.08.06, cyWu, 改为事件队列 + 独立任务驱动,
+ * 彻底消除跨任务共享状态的竞态(UART 接收任务 / BLE 协议栈任务 /
+ * 原 sys_timer 回调三方并发访问 s_br 的问题)
+ * - V1.2.0, 2026.08.10, cyWu, 注册 LP target:桥接忙时禁止进低功耗,
+ * 空闲可睡,配合板级 SLEEP_EN 恢复串口方案下的低功耗
+ ******************************************************************************/
+
+#include "system/includes.h"
+#include "asm/power/power_api.h"
+#include "app_main.h"
+#include "mcu_bridge.h"
+#include "mcu_uart.h"
+#include "mcu_frame.h"
+#include "usr_le_api.h"
+#include "usr_le_product.h"
+#include "mcu_util.h"
+
+#define LOG_TAG_CONST APP
+#define LOG_TAG "[MCU_BR]"
+#define LOG_ERROR_ENABLE
+#define LOG_DEBUG_ENABLE
+#define LOG_INFO_ENABLE
+#define LOG_DUMP_ENABLE
+#define LOG_CLI_ENABLE
+#include "debug.h"
+
+/*============================================================================*/
+/* 私有宏 */
+/*============================================================================*/
+
+#define MCU_IDENTITY_RETRY_MS (1000u) /* 0x01 轮询间隔 */
+#define MCU_HEARTBEAT_IDLE_MS (55000u) /* 无 MCU 数据超时发心跳 */
+#define MCU_HEARTBEAT_WAIT_MS (2500u) /* 心跳应答等待 */
+#define MCU_HEARTBEAT_MAX_RETRY (3u)
+#define MCU_APP_ACK_WAIT_MS (2500u) /* APP 透传等 MCU ACK */
+#define MCU_APP_ACK_MAX_RETRY (3u)
+#define MCU_REPORT_GW_WAIT_MS (3000u) /* MCU 上报等网关 ACK */
+#define MCU_REPORT_GW_MAX_RETRY (3u)
+#define MCU_IDENTITY_PAYLOAD_MIN (2 + 8 + 8 + 16 + 8) /* Ver+Cat+Prod+SN+Cap */
+
+#define MCU_BRIDGE_TICK_MS (200u) /* 期望的兜底轮询周期(毫秒) */
+/* 杰理 os_sem_pend 超时单位是 OS tick,OS_TICKS_PER_SEC=100 → 1 tick = 10ms */
+#define MCU_BRIDGE_TICK_OS ((MCU_BRIDGE_TICK_MS + 9u) / 10u)
+#define MCU_EVT_QUEUE_DEPTH (8u) /* 事件队列深度 */
+#define MCU_BRIDGE_TASK_PRIO (31u)
+#define MCU_BRIDGE_TASK_STACK (1024u)
+
+/*============================================================================*/
+/* 外部符号 */
+/*============================================================================*/
+
+extern void ble_trans_module_enable(u8 flag);
+extern u16 usr_get_conn_service(void);
+
+/* 运行时可改写的产品身份(供广播使用) */
+uint8_t g_usr_le_prodcode[USR_LE_PRODCODE_LEN];
+uint8_t g_usr_le_pcode[USR_LE_PCODE_LEN];
+
+/*============================================================================*/
+/* 私有类型 */
+/*============================================================================*/
+
+typedef struct {
+ uint8_t active;
+ uint8_t sn;
+ uint8_t expect_ack; /* 期望的 ACK 命令 */
+ uint8_t retry;
+ uint32_t send_time;
+ uint8_t buf[MCU_FRAME_MAX_LEN];
+ uint16_t len;
+} McuPending_t;
+
+/**
+ * @brief 桥接层内部事件类型
+ * @note 所有跨任务(UART 接收任务 / BLE 协议栈任务)产生的输入,
+ * 都封装为事件投递到队列,只在 mcu_bridge_task 单一上下文中
+ * 消费处理,从而避免对 s_br 的并发访问。
+ */
+typedef enum {
+ MCU_EVT_UART_FRAME = 0, /* MCU 经 UART 送来的完整合法帧 */
+ MCU_EVT_APP_FRAME, /* APP/网关经 BLE 下发的 55AA 帧 */
+ MCU_EVT_GATEWAY_ACK, /* 网关对 MCU 上报(0x07)的应答 */
+ MCU_EVT_BLE_PAIRED, /* AUTHSETUP 成功(配网完成) */
+ MCU_EVT_BLE_PAIR_CLEARED /* 配对信息被清除 */
+} McuEvtType_t;
+
+typedef struct {
+ McuEvtType_t type;
+ uint16_t len;
+ uint8_t buf[MCU_FRAME_MAX_LEN];
+ uint16_t gw_cmd;
+ uint8_t gw_int_cnt;
+ int32_t gw_int0;
+} McuEvt_t;
+
+typedef struct {
+ uint8_t initialized;
+ uint8_t identity_ready;
+ uint8_t identity_adv_pending; /* 身份刚就绪,待刷新广播 */
+ uint8_t sn;
+ uint8_t work_status;
+ uint8_t last_pushed_status;
+ uint8_t hb_fail_cnt;
+ uint8_t hb_waiting;
+ uint8_t mcu_comm_alarm;
+ uint8_t last_ble_conn;
+ uint16_t pair_timeout_s;
+ uint32_t pair_start_ms;
+ uint32_t last_mcu_rx_ms;
+ uint32_t last_identity_tx_ms;
+ uint32_t last_hb_tx_ms;
+ McuPending_t app_pending; /* APP→MCU 透传等待 */
+ McuPending_t ble_pending; /* BLE 主动请求等待(心跳/状态等) */
+ McuPending_t report_pending; /* MCU 0x07 上报,等网关 ACK 后再回 0x08 */
+} McuBridge_t;
+
+/*============================================================================*/
+/* 私有变量 */
+/*============================================================================*/
+
+/* 业务状态:仅由 mcu_bridge_task 单一任务上下文读写,天然无竞态 */
+static McuBridge_t s_br;
+static uint8_t s_parse_out[MCU_FRAME_MAX_LEN];
+
+/*
+ * 低功耗空闲查询:桥接任务处理事件/周期逻辑时置 1,禁止进 sleep;
+ * pend 等待期间置 0,允许系统进入低功耗。由 idle 任务跨上下文读取,故用 volatile。
+ */
+static volatile u8 s_bridge_busy = 0;
+
+/**
+ * @brief 事件队列:环形缓冲 + 同步原语的集合
+ * @note 生产者可来自任意任务(UART 接收任务 / BLE 协议栈任务),
+ * 消费者固定为 mcu_bridge_task。lock 只保护本结构体自身的
+ * 入队/出队操作,不涉及业务逻辑,因此临界区极短。
+ */
+typedef struct {
+ McuEvt_t queue[MCU_EVT_QUEUE_DEPTH];
+ uint8_t head; /* 下一个可取出的位置 */
+ uint8_t tail; /* 下一个可写入的位置 */
+ uint8_t count; /* 当前事件个数 */
+ OS_MUTEX lock; /* 保护 queue/head/tail/count 四个字段 */
+ OS_SEM sem; /* 有新事件入队时唤醒 mcu_bridge_task */
+} McuEvtQueue_t;
+
+static McuEvtQueue_t s_evt_q;
+
+/*============================================================================*/
+/* 私有函数声明 */
+/*============================================================================*/
+
+static uint8_t mcu_bridge_next_sn(void);
+static int32_t mcu_bridge_send_frame(uint8_t cmd, uint8_t sn,
+ const uint8_t *payload, uint16_t plen);
+static void mcu_bridge_send_ack(uint8_t cmd, uint8_t sn, uint8_t result);
+static void mcu_bridge_handle_frame(const uint8_t *frame, uint16_t len);
+static void mcu_bridge_handle_identity_ack(const uint8_t *frame, uint16_t len);
+static void mcu_bridge_handle_pair_cfg(const uint8_t *frame, uint16_t len);
+static void mcu_bridge_handle_report(const uint8_t *frame, uint16_t len);
+static void mcu_bridge_handle_app_frame(const uint8_t *data, uint16_t len);
+static void mcu_bridge_handle_gateway_ack(uint16_t cmd, uint8_t gateway_int_cnt,
+ int32_t gateway_int0);
+static void mcu_bridge_handle_paired(void);
+static void mcu_bridge_handle_pair_cleared(void);
+static void mcu_bridge_try_identity(void);
+static void mcu_bridge_try_heartbeat(void);
+static void mcu_bridge_try_work_status(void);
+static void mcu_bridge_update_conn_bits(void);
+static void mcu_bridge_check_pair_timeout(void);
+static void mcu_bridge_check_app_pending(void);
+static void mcu_bridge_check_ble_pending(void);
+static void mcu_bridge_check_report_pending(void);
+static void mcu_bridge_set_ws_bit(uint8_t bit, uint8_t on);
+static void mcu_bridge_periodic(void);
+static uint8_t mcu_bridge_post_event(const McuEvt_t *evt);
+static uint8_t mcu_bridge_pop_event(McuEvt_t *evt);
+static void mcu_bridge_dispatch_event(const McuEvt_t *evt);
+static void mcu_bridge_task(void *arg);
+
+/*============================================================================*/
+/* 私有函数 */
+/*============================================================================*/
+
+/**
+ * @brief 投递一个事件到队列并唤醒 mcu_bridge_task
+ * @param evt 事件内容(按值拷贝入队)
+ * @return 1=成功,0=队列已满被丢弃
+ * @note 可从任意任务上下文调用(UART 接收任务 / BLE 协议栈任务等)
+ */
+static uint8_t mcu_bridge_post_event(const McuEvt_t *evt)
+{
+ uint8_t ok = 0;
+
+ os_mutex_pend(&s_evt_q.lock, 0);
+ if (s_evt_q.count < MCU_EVT_QUEUE_DEPTH) {
+ s_evt_q.queue[s_evt_q.tail] = *evt;
+ s_evt_q.tail = (uint8_t)((s_evt_q.tail + 1) % MCU_EVT_QUEUE_DEPTH);
+ s_evt_q.count++;
+ ok = 1;
+ }
+ os_mutex_post(&s_evt_q.lock);
+
+ if (ok) {
+ os_sem_post(&s_evt_q.sem);
+ } else {
+ log_error("event queue full, drop type=%d", evt->type);
+ }
+ return ok;
+}
+
+/**
+ * @brief 从队列取出一个事件
+ * @param evt 输出事件
+ * @return 1=取到,0=队列为空
+ * @note 仅由 mcu_bridge_task 调用
+ */
+static uint8_t mcu_bridge_pop_event(McuEvt_t *evt)
+{
+ uint8_t ok = 0;
+
+ os_mutex_pend(&s_evt_q.lock, 0);
+ if (s_evt_q.count > 0) {
+ *evt = s_evt_q.queue[s_evt_q.head];
+ s_evt_q.head = (uint8_t)((s_evt_q.head + 1) % MCU_EVT_QUEUE_DEPTH);
+ s_evt_q.count--;
+ ok = 1;
+ }
+ os_mutex_post(&s_evt_q.lock);
+ return ok;
+}
+
+/**
+ * @brief 事件分发:将队列事件转交给对应业务处理函数
+ * @param evt 事件
+ * @note 仅在 mcu_bridge_task 上下文执行,s_br 在此单一上下文内读写
+ */
+static void mcu_bridge_dispatch_event(const McuEvt_t *evt)
+{
+ switch (evt->type) {
+ case MCU_EVT_UART_FRAME:
+ mcu_bridge_handle_frame(evt->buf, evt->len);
+ break;
+ case MCU_EVT_APP_FRAME:
+ mcu_bridge_handle_app_frame(evt->buf, evt->len);
+ break;
+ case MCU_EVT_GATEWAY_ACK:
+ mcu_bridge_handle_gateway_ack(evt->gw_cmd, evt->gw_int_cnt, evt->gw_int0);
+ break;
+ case MCU_EVT_BLE_PAIRED:
+ mcu_bridge_handle_paired();
+ break;
+ case MCU_EVT_BLE_PAIR_CLEARED:
+ mcu_bridge_handle_pair_cleared();
+ break;
+ default:
+ log_error("unknown evt type=%d", evt->type);
+ break;
+ }
+}
+
+/**
+ * @brief 桥接层主任务:事件驱动 + 兜底轮询
+ * @param arg 未使用
+ * @note 唯一读写 s_br 的任务上下文;醒来时机 = 有新事件 或 每
+ * MCU_BRIDGE_TICK_MS 兜底一次,用于身份轮询/心跳/超时重传等。
+ * 处理期间通过 LP target 挡住系统休眠,避免 UART TX/DMA 异常。
+ */
+static void mcu_bridge_task(void *arg)
+{
+ McuEvt_t evt;
+
+ (void)arg;
+ log_info("bridge task start");
+
+ while (1) {
+ /* 有事件立刻醒;空闲时约每 MCU_BRIDGE_TICK_MS 醒一次做周期检查 */
+ os_sem_pend(&s_evt_q.sem, (int)MCU_BRIDGE_TICK_OS);
+
+ s_bridge_busy = 1;
+ while (mcu_bridge_pop_event(&evt)) {
+ mcu_bridge_dispatch_event(&evt);
+ }
+ mcu_bridge_periodic();
+ s_bridge_busy = 0;
+ }
+}
+
+/**
+ * @brief 低功耗空闲查询:桥接忙则不允许进 sleep
+ * @return 1=可进低功耗,0=保持唤醒
+ */
+static u8 mcu_bridge_lp_idle_query(void)
+{
+ return s_bridge_busy ? 0 : 1;
+}
+
+REGISTER_LP_TARGET(mcu_bridge_lp_target) = {
+ .name = "mcu_bridge",
+ .is_idle = mcu_bridge_lp_idle_query,
+};
+
+/**
+ * @brief 取下一个序列号
+ * @return SN
+ */
+static uint8_t mcu_bridge_next_sn(void)
+{
+ return s_br.sn++;
+}
+
+/**
+ * @brief 组帧并通过 UART 发给 MCU
+ * @param cmd 命令
+ * @param sn 序列号
+ * @param payload 数据
+ * @param plen 长度
+ * @return 帧长;<0 失败
+ */
+static int32_t mcu_bridge_send_frame(uint8_t cmd, uint8_t sn,
+ const uint8_t *payload, uint16_t plen)
+{
+ uint8_t buf[MCU_FRAME_MAX_LEN];
+ int32_t total;
+
+ total = mcu_frame_pack(buf, sizeof(buf), cmd, sn, payload, plen);
+ if (total <= 0) {
+ return -1;
+ }
+ if (mcu_uart_send(buf, (uint16_t)total) != MCU_UART_OK) {
+ return -1;
+ }
+ log_debug("MCU TX cmd=0x%02X sn=%u len=%d", cmd, sn, (int)total);
+ return total;
+}
+
+/**
+ * @brief 发送仅含 Result 的 ACK 帧给 MCU
+ * @param cmd ACK 命令字
+ * @param sn 序列号
+ * @param result 结果码
+ */
+static void mcu_bridge_send_ack(uint8_t cmd, uint8_t sn, uint8_t result)
+{
+ mcu_bridge_send_frame(cmd, sn, &result, 1);
+}
+
+/**
+ * @brief 设置/清除工作状态位
+ * @param bit 位掩码
+ * @param on 1=置位,0=清位
+ */
+static void mcu_bridge_set_ws_bit(uint8_t bit, uint8_t on)
+{
+ if (on) {
+ s_br.work_status |= bit;
+ } else {
+ s_br.work_status &= (uint8_t)~bit;
+ }
+}
+
+/**
+ * @brief 处理 0x02 身份应答
+ * @param frame 完整帧
+ * @param len 长度
+ * @note 载荷布局(Result 之后):Ver(2)+Category(8)+Product(8)+SN(16)+Cap(8)
+ */
+static void mcu_bridge_handle_identity_ack(const uint8_t *frame, uint16_t len)
+{
+ if (len < 7) {
+ return;
+ }
+ if (frame[6] != MCU_RESULT_OK) {
+ log_error("identity ack result=0x%02X", frame[6]);
+ return;
+ }
+ if (len < (7 + MCU_IDENTITY_PAYLOAD_MIN + 1)) {
+ log_error("identity ack too short %u", len);
+ return;
+ }
+
+ /* Category Code 在 Result(frame[6]) 后偏移 Ver(2),取前 4;
+ * Product Code 再后 Category(8),取前 6 */
+ memcpy(g_usr_le_prodcode, &frame[7 + 2], USR_LE_PRODCODE_LEN);
+ memcpy(g_usr_le_pcode, &frame[7 + 2 + 8], USR_LE_PCODE_LEN);
+
+ s_br.identity_ready = 1;
+ s_br.identity_adv_pending = 1;
+ log_info("identity ok prodcode:");
+ log_info_hexdump(g_usr_le_prodcode, USR_LE_PRODCODE_LEN);
+ log_info("identity ok pcode:");
+ log_info_hexdump(g_usr_le_pcode, USR_LE_PCODE_LEN);
+
+ /* 置位后由 mcu_bridge_periodic 刷新广播 */
+ s_br.last_pushed_status = 0xFF;
+}
+
+/**
+ * @brief 处理 0x10 配网请求
+ * @param frame 完整帧
+ * @param len 长度
+ */
+static void mcu_bridge_handle_pair_cfg(const uint8_t *frame, uint16_t len)
+{
+ uint8_t sn;
+ uint8_t mode;
+ uint16_t timeout_s;
+
+ if (len < 10) {
+ return;
+ }
+ sn = frame[5];
+ mode = frame[6];
+ timeout_s = mcu_util_read_u16_be(&frame[7]);
+
+ if (mode != 1 && mode != 2) {
+ mcu_bridge_send_ack(MCU_CMD_PAIR_CFG_ACK, sn, MCU_RESULT_PARAM_ERR);
+ return;
+ }
+ if (!s_br.identity_ready) {
+ mcu_bridge_send_ack(MCU_CMD_PAIR_CFG_ACK, sn, MCU_RESULT_BUSY);
+ return;
+ }
+
+ s_br.pair_timeout_s = timeout_s;
+ s_br.pair_start_ms = mcu_util_get_ms();
+ mcu_bridge_set_ws_bit(MCU_WS_PAIRING_MODE, 1);
+ mcu_bridge_set_ws_bit(MCU_WS_CONFIGURED, 0);
+
+ mcu_bridge_send_ack(MCU_CMD_PAIR_CFG_ACK, sn, MCU_RESULT_OK);
+
+ /* 重新配网:清配对并开绑定广播(非回连) */
+ log_info("MCU pair cfg mode=%u timeout=%u", mode, timeout_s);
+ usr_goto_pair_mode();
+}
+
+/**
+ * @brief 处理 MCU 主动上报 0x07
+ * @param frame 完整帧
+ * @param len 长度
+ * @note 先转发网关;收到 REPORTCMD_V2 成功应答后再回 MCU 0x08
+ */
+static void mcu_bridge_handle_report(const uint8_t *frame, uint16_t len)
+{
+ uint8_t sn = frame[5];
+
+ if (s_br.report_pending.active) {
+ log_error("report busy, drop sn=%u", sn);
+ mcu_bridge_send_ack(MCU_CMD_REPORT_ACK, sn, MCU_RESULT_BUSY);
+ return;
+ }
+ if (len > sizeof(s_br.report_pending.buf)) {
+ mcu_bridge_send_ack(MCU_CMD_REPORT_ACK, sn, MCU_RESULT_PARAM_ERR);
+ return;
+ }
+
+ memcpy(s_br.report_pending.buf, frame, len);
+ s_br.report_pending.len = len;
+ s_br.report_pending.sn = sn;
+ s_br.report_pending.expect_ack = MCU_CMD_REPORT_ACK; /* 最终要回给 MCU 的命令 */
+ s_br.report_pending.retry = 0;
+ s_br.report_pending.send_time = mcu_util_get_ms();
+ s_br.report_pending.active = 1;
+
+ /* 先上报网关,暂不回 MCU 0x08 */
+ usr_le_send_mcu_frame(s_br.report_pending.buf, s_br.report_pending.len);
+ log_debug("MCU report sn=%u -> gateway, wait ACK", sn);
+}
+
+/**
+ * @brief 分发一帧完整 MCU 协议
+ * @param frame 完整帧
+ * @param len 长度
+ */
+static void mcu_bridge_handle_frame(const uint8_t *frame, uint16_t len)
+{
+ uint8_t cmd;
+ uint8_t sn;
+
+ if (!frame || len < 6) {
+ return;
+ }
+
+ s_br.last_mcu_rx_ms = mcu_util_get_ms();
+ s_br.hb_fail_cnt = 0;
+ s_br.hb_waiting = 0;
+ s_br.mcu_comm_alarm = 0;
+
+ cmd = frame[4];
+ sn = frame[5];
+ log_debug("MCU RX cmd=0x%02X sn=%u len=%u", cmd, sn, len);
+
+ /* APP 透传等待的 ACK */
+ if (s_br.app_pending.active &&
+ (cmd == s_br.app_pending.expect_ack) &&
+ (sn == s_br.app_pending.sn)) {
+ s_br.app_pending.active = 0;
+ usr_le_send_mcu_frame((u8 *)frame, len);
+ return;
+ }
+
+ /* BLE 主动请求的 ACK(心跳/状态) */
+ if (s_br.ble_pending.active &&
+ (cmd == s_br.ble_pending.expect_ack) &&
+ (sn == s_br.ble_pending.sn)) {
+ s_br.ble_pending.active = 0;
+ if (cmd == MCU_CMD_HEARTBEAT_ACK) {
+ s_br.hb_waiting = 0;
+ s_br.hb_fail_cnt = 0;
+ }
+ return;
+ }
+
+ switch (cmd) {
+ case MCU_CMD_GET_IDENTITY_ACK:
+ mcu_bridge_handle_identity_ack(frame, len);
+ break;
+ case MCU_CMD_PAIR_CFG:
+ mcu_bridge_handle_pair_cfg(frame, len);
+ break;
+ case MCU_CMD_REPORT:
+ mcu_bridge_handle_report(frame, len);
+ break;
+ case MCU_CMD_CONTROL_ACK:
+ case MCU_CMD_READ_ACK:
+ /* 无匹配 pending 时仍尝试转发给 APP(容错) */
+ usr_le_send_mcu_frame((u8 *)frame, len);
+ break;
+ case MCU_CMD_HEARTBEAT_ACK:
+ case MCU_CMD_WORK_STATUS_ACK:
+ break;
+ default:
+ /* P0 未实现:回不支持(仅对 MCU→BLE 请求类) */
+ if (cmd == MCU_CMD_RESET || cmd == MCU_CMD_REBOOT ||
+ cmd == MCU_CMD_GET_TIME || cmd == MCU_CMD_FACTORY ||
+ cmd == MCU_CMD_PHOTO || cmd == MCU_CMD_RECORD ||
+ cmd == MCU_CMD_AUDIO) {
+ mcu_bridge_send_ack((uint8_t)(cmd + 1), sn, MCU_RESULT_NOT_SUPPORT);
+ }
+ break;
+ }
+}
+
+/**
+ * @brief 处理 APP/网关经 BLE 下发的 55AA 帧
+ * @param data 帧数据
+ * @param len 长度
+ */
+static void mcu_bridge_handle_app_frame(const uint8_t *data, uint16_t len)
+{
+ uint8_t cmd;
+ uint8_t sn;
+ uint8_t expect_ack;
+
+ cmd = data[4];
+ sn = data[5];
+
+ if (cmd == MCU_CMD_CONTROL) {
+ expect_ack = MCU_CMD_CONTROL_ACK;
+ } else if (cmd == MCU_CMD_READ) {
+ expect_ack = MCU_CMD_READ_ACK;
+ } else {
+ /* 其它命令 P0:原样转发,不等待(后续 OTA 等再扩展) */
+ mcu_uart_send(data, len);
+ return;
+ }
+
+ if (s_br.app_pending.active) {
+ log_error("app frame busy, drop cmd=0x%02X", cmd);
+ return;
+ }
+
+ memcpy(s_br.app_pending.buf, data, len);
+ s_br.app_pending.len = len;
+ s_br.app_pending.sn = sn;
+ s_br.app_pending.expect_ack = expect_ack;
+ s_br.app_pending.retry = 0;
+ s_br.app_pending.send_time = mcu_util_get_ms();
+ s_br.app_pending.active = 1;
+
+ mcu_uart_send(data, len);
+ log_debug("app->mcu forward cmd=0x%02X sn=%u wait 0x%02X",
+ cmd, sn, expect_ack);
+}
+
+/**
+ * @brief 处理网关对 REPORTCMD_V2 主动上报的应答
+ * @param cmd bleproto 包 cmd
+ * @param gateway_int_cnt gateway_int 个数
+ * @param gateway_int0 gateway_int[0]
+ * @note 成功条件:cmd==0 && cnt==1 && int0==0;成功后才向 MCU 回 0x08
+ */
+static void mcu_bridge_handle_gateway_ack(uint16_t cmd,
+ uint8_t gateway_int_cnt,
+ int32_t gateway_int0)
+{
+ uint8_t sn;
+
+ if (!s_br.report_pending.active) {
+ return;
+ }
+
+ /* 与旧 SOC jbReportAckCheck 成功条件一致 */
+ if (cmd == 0 && gateway_int_cnt == 1 && gateway_int0 == 0) {
+ sn = s_br.report_pending.sn;
+ mcu_bridge_send_ack(MCU_CMD_REPORT_ACK, sn, MCU_RESULT_OK);
+ s_br.report_pending.active = 0;
+ log_info("gateway report ack ok, MCU 0x08 sn=%u", sn);
+ return;
+ }
+
+ log_error("gateway report ack fail cmd=%u cnt=%u int0=%ld",
+ (unsigned)cmd, (unsigned)gateway_int_cnt, (long)gateway_int0);
+ /* 失败暂不立刻回 MCU,留给超时重试 */
+}
+
+/**
+ * @brief 处理配网成功通知
+ */
+static void mcu_bridge_handle_paired(void)
+{
+ usr_var.usr_goto_pair = 0;
+ mcu_bridge_set_ws_bit(MCU_WS_CONFIGURED, 1);
+ mcu_bridge_set_ws_bit(MCU_WS_PAIRING_MODE, 0);
+ /* Bit1/2/3 预留:INTERNET / APP_ONLINE / BLE_CONNECTED 暂固定为 0 */
+ log_info("mcu_bridge_handle_paired");
+}
+
+/**
+ * @brief 处理配对信息被清除通知
+ */
+static void mcu_bridge_handle_pair_cleared(void)
+{
+ mcu_bridge_set_ws_bit(MCU_WS_CONFIGURED, 0);
+}
+
+/**
+ * @brief 身份未就绪时每 1s 发 0x01
+ */
+static void mcu_bridge_try_identity(void)
+{
+ uint32_t now;
+ uint8_t sn;
+ int32_t total;
+
+ if (s_br.identity_ready) {
+ return;
+ }
+
+ now = mcu_util_get_ms();
+ if ((now - s_br.last_identity_tx_ms) < MCU_IDENTITY_RETRY_MS) {
+ return;
+ }
+ s_br.last_identity_tx_ms = now;
+
+ sn = mcu_bridge_next_sn();
+ total = mcu_bridge_send_frame(MCU_CMD_GET_IDENTITY, sn, NULL, 0);
+ if (total > 0) {
+ log_debug("identity req sn=%u", sn);
+ }
+}
+
+/**
+ * @brief 心跳检测与发送
+ */
+static void mcu_bridge_try_heartbeat(void)
+{
+ uint32_t now = mcu_util_get_ms();
+ uint8_t sn;
+ uint8_t buf[MCU_FRAME_MAX_LEN];
+ int32_t total;
+
+ if (!s_br.identity_ready) {
+ return;
+ }
+
+ if (s_br.hb_waiting) {
+ if ((now - s_br.last_hb_tx_ms) <= MCU_HEARTBEAT_WAIT_MS) {
+ return;
+ }
+ s_br.hb_waiting = 0;
+ s_br.ble_pending.active = 0;
+ s_br.hb_fail_cnt++;
+ log_error("heartbeat timeout fail=%u", s_br.hb_fail_cnt);
+ if (s_br.hb_fail_cnt >= MCU_HEARTBEAT_MAX_RETRY) {
+ s_br.mcu_comm_alarm = 1;
+ log_error("MCU comm alarm");
+ /* 告警后重新计时,避免狂打心跳 */
+ s_br.last_mcu_rx_ms = now;
+ return;
+ }
+ /* 未达 3 次:立即再发一帧心跳 */
+ } else if ((now - s_br.last_mcu_rx_ms) < MCU_HEARTBEAT_IDLE_MS) {
+ return;
+ }
+
+ sn = mcu_bridge_next_sn();
+ total = mcu_frame_pack(buf, sizeof(buf), MCU_CMD_HEARTBEAT, sn, NULL, 0);
+ if (total <= 0) {
+ return;
+ }
+ if (mcu_uart_send(buf, (uint16_t)total) != MCU_UART_OK) {
+ return;
+ }
+
+ s_br.ble_pending.active = 1;
+ s_br.ble_pending.sn = sn;
+ s_br.ble_pending.expect_ack = MCU_CMD_HEARTBEAT_ACK;
+ s_br.ble_pending.send_time = now;
+ s_br.ble_pending.retry = 0;
+ s_br.last_hb_tx_ms = now;
+ s_br.hb_waiting = 1;
+ log_debug("heartbeat tx sn=%u", sn);
+}
+
+/**
+ * @brief 工作状态变化时推送 0x14
+ */
+static void mcu_bridge_try_work_status(void)
+{
+ uint8_t payload[2];
+ uint8_t sn;
+ uint8_t buf[MCU_FRAME_MAX_LEN];
+ int32_t total;
+ uint32_t now;
+
+ if (!s_br.identity_ready) {
+ return;
+ }
+ if (s_br.work_status == s_br.last_pushed_status) {
+ return;
+ }
+ if (s_br.ble_pending.active) {
+ return; /* 避免与心跳等抢占 */
+ }
+
+ payload[0] = s_br.work_status;
+ payload[1] = 0x00; /* RSSI 占位,后续可接真实信号强度 */
+
+ sn = mcu_bridge_next_sn();
+ total = mcu_frame_pack(buf, sizeof(buf), MCU_CMD_WORK_STATUS, sn, payload, 2);
+ if (total <= 0) {
+ return;
+ }
+ if (mcu_uart_send(buf, (uint16_t)total) != MCU_UART_OK) {
+ return;
+ }
+
+ now = mcu_util_get_ms();
+ s_br.ble_pending.active = 1;
+ s_br.ble_pending.sn = sn;
+ s_br.ble_pending.expect_ack = MCU_CMD_WORK_STATUS_ACK;
+ s_br.ble_pending.send_time = now;
+ s_br.last_pushed_status = s_br.work_status;
+ log_info("work_status push 0x%02X", s_br.work_status);
+}
+
+/**
+ * @brief 轮询 BLE 连接,维护 Bit5,并在已配网断连时开回连广播
+ * @note - Bit1/2/3(INTERNET / APP_ONLINE / BLE_CONNECTED)预留,固定为 0,此处不更新
+ * - 已配网(Bit0)时:断连置 Bit5=1,连上后清 Bit5
+ * - 未配网时 Bit5 只由 0x10 / 配网超时 / 配网成功路径维护
+ */
+static void mcu_bridge_update_conn_bits(void)
+{
+ uint8_t conn = (usr_get_conn_service() != 0) ? 1 : 0;
+ uint8_t paired = usr_get_pair_flag();
+
+ if (conn != s_br.last_ble_conn) {
+ s_br.last_ble_conn = conn;
+
+ if (!conn && paired) {
+ /* 已配网断连:发回连广播(非绑定广播) */
+ usr_var.usr_goto_pair = 0;
+ if (s_br.identity_ready) {
+ usr_ble_adv_init();
+ }
+ }
+ }
+
+ /* 已配网:未连接显示「配网中」,已连接则退出「配网中」 */
+ if (paired) {
+ mcu_bridge_set_ws_bit(MCU_WS_PAIRING_MODE, conn ? 0 : 1);
+ }
+}
+
+/**
+ * @brief 配网超时退出
+ */
+static void mcu_bridge_check_pair_timeout(void)
+{
+ uint32_t now;
+ uint32_t timeout_ms;
+
+ if (!(s_br.work_status & MCU_WS_PAIRING_MODE)) {
+ return;
+ }
+ if (s_br.pair_timeout_s == 0) {
+ return;
+ }
+ if (usr_get_pair_flag()) {
+ return;
+ }
+
+ now = mcu_util_get_ms();
+ timeout_ms = (uint32_t)s_br.pair_timeout_s * 1000u;
+ if ((now - s_br.pair_start_ms) < timeout_ms) {
+ return;
+ }
+
+ log_info("pair timeout, stop adv");
+ usr_var.usr_goto_pair = 0;
+ mcu_bridge_set_ws_bit(MCU_WS_PAIRING_MODE, 0);
+ ble_trans_module_enable(0);
+ usr_var.usr_adv_flag = 0;
+}
+
+/**
+ * @brief APP 透传等待超时重发
+ */
+static void mcu_bridge_check_app_pending(void)
+{
+ uint32_t now;
+
+ if (!s_br.app_pending.active) {
+ return;
+ }
+
+ now = mcu_util_get_ms();
+ if ((now - s_br.app_pending.send_time) <= MCU_APP_ACK_WAIT_MS) {
+ return;
+ }
+
+ if (s_br.app_pending.retry >= MCU_APP_ACK_MAX_RETRY) {
+ log_error("app pending give up cmd expect=0x%02X",
+ s_br.app_pending.expect_ack);
+ s_br.app_pending.active = 0;
+ return;
+ }
+
+ mcu_uart_send(s_br.app_pending.buf, s_br.app_pending.len);
+ s_br.app_pending.retry++;
+ s_br.app_pending.send_time = now;
+ log_debug("app pending retry %u", s_br.app_pending.retry);
+}
+
+/**
+ * @brief MCU 上报等网关 ACK:超时则重发网关,超限则回 MCU 失败
+ */
+static void mcu_bridge_check_report_pending(void)
+{
+ uint32_t now;
+
+ if (!s_br.report_pending.active) {
+ return;
+ }
+
+ now = mcu_util_get_ms();
+ if ((now - s_br.report_pending.send_time) <= MCU_REPORT_GW_WAIT_MS) {
+ return;
+ }
+
+ if (s_br.report_pending.retry >= MCU_REPORT_GW_MAX_RETRY) {
+ log_error("gateway report ack timeout, sn=%u", s_br.report_pending.sn);
+ mcu_bridge_send_ack(MCU_CMD_REPORT_ACK, s_br.report_pending.sn, MCU_RESULT_FAIL);
+ s_br.report_pending.active = 0;
+ return;
+ }
+
+ usr_le_send_mcu_frame(s_br.report_pending.buf, s_br.report_pending.len);
+ s_br.report_pending.retry++;
+ s_br.report_pending.send_time = now;
+ log_debug("gateway report retry %u sn=%u",
+ s_br.report_pending.retry, s_br.report_pending.sn);
+}
+
+/**
+ * @brief BLE→MCU 主动帧等待超时清理
+ * @note 工作状态 ACK 超时后会重置 last_pushed_status,
+ * 使 mcu_bridge_try_work_status 在下个周期自动重推,避免状态永久失步
+ */
+static void mcu_bridge_check_ble_pending(void)
+{
+ uint32_t now;
+
+ if (!s_br.ble_pending.active) {
+ return;
+ }
+ /* 心跳有独立超时逻辑 */
+ if (s_br.ble_pending.expect_ack == MCU_CMD_HEARTBEAT_ACK) {
+ return;
+ }
+
+ now = mcu_util_get_ms();
+ if ((now - s_br.ble_pending.send_time) > MCU_APP_ACK_WAIT_MS) {
+ if (s_br.ble_pending.expect_ack == MCU_CMD_WORK_STATUS_ACK) {
+ log_error("work_status ack timeout, will retry");
+ s_br.last_pushed_status = 0xFF;
+ }
+ s_br.ble_pending.active = 0;
+ }
+}
+
+/**
+ * @brief 周期处理:身份轮询 / 心跳 / 重传 / 状态同步
+ * @note 仅在 mcu_bridge_task 上下文调用
+ */
+static void mcu_bridge_periodic(void)
+{
+ mcu_bridge_try_identity();
+
+ if (s_br.identity_adv_pending) {
+ s_br.identity_adv_pending = 0;
+ usr_ble_adv_init();
+ }
+
+ mcu_bridge_update_conn_bits();
+ mcu_bridge_check_pair_timeout();
+ mcu_bridge_check_app_pending();
+ mcu_bridge_check_ble_pending();
+ mcu_bridge_check_report_pending();
+ mcu_bridge_try_heartbeat();
+ mcu_bridge_try_work_status();
+}
+
+/*============================================================================*/
+/* 公有函数 */
+/*============================================================================*/
+
+/**
+ * @brief 初始化 MCU 协议桥(含 UART + 事件队列 + 独立任务)
+ * @return McuBridge_Ret_t
+ */
+McuBridge_Ret_t mcu_bridge_init(void)
+{
+ if (s_br.initialized) {
+ return MCU_BRIDGE_OK;
+ }
+
+ memset(&s_br, 0, sizeof(s_br));
+ memset(g_usr_le_prodcode, 0, sizeof(g_usr_le_prodcode));
+ memset(g_usr_le_pcode, 0, sizeof(g_usr_le_pcode));
+ mcu_frame_parser_reset();
+
+ if (mcu_uart_init() != MCU_UART_OK) {
+ return MCU_BRIDGE_ERR_UNKNOWN;
+ }
+
+ memset(&s_evt_q, 0, sizeof(s_evt_q));
+ os_mutex_create(&s_evt_q.lock);
+ os_sem_create(&s_evt_q.sem, 0);
+
+ s_br.last_mcu_rx_ms = mcu_util_get_ms();
+ s_br.last_identity_tx_ms = 0; /* 立即允许发第一帧 0x01 */
+ s_br.last_pushed_status = 0xFF; /* 强制首轮差异 */
+ s_br.initialized = 1;
+
+ /* 上电:不强制配网;广播由 usr_ble_adv_init 在身份就绪后按配对状态决定 */
+ usr_var.usr_goto_pair = 0;
+ usr_var.usr_adv_flag = 0;
+
+ os_task_create(mcu_bridge_task, NULL, MCU_BRIDGE_TASK_PRIO,
+ MCU_BRIDGE_TASK_STACK, 0, "mcu_bridge");
+
+ log_info("mcu_bridge_init ok");
+ return MCU_BRIDGE_OK;
+}
+
+/**
+ * @brief UART 收到原始数据(生产者,可在 UART 接收任务上下文调用)
+ * @param data 数据
+ * @param len 长度
+ * @note 仅做逐字节组帧,帧就绪后投递事件,不直接触碰业务状态 s_br
+ */
+void mcu_bridge_on_uart_rx(const uint8_t *data, uint16_t len)
+{
+ uint16_t i;
+ uint16_t out_len;
+ int8_t ret;
+ McuEvt_t evt;
+
+ if (!s_br.initialized || !data || len == 0) {
+ return;
+ }
+
+ for (i = 0; i < len; i++) {
+ ret = mcu_frame_feed(data[i], s_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);
+ mcu_bridge_post_event(&evt);
+ }
+ }
+}
+
+/**
+ * @brief APP/网关经 BLE 下发的 55AA 帧(生产者,可在 BLE 协议栈任务调用)
+ * @param data 55AA 帧
+ * @param len 长度
+ */
+void mcu_bridge_on_app_frame(const uint8_t *data, uint16_t len)
+{
+ McuEvt_t evt;
+
+ if (!s_br.initialized || !data || len < 6 || len > sizeof(evt.buf)) {
+ log_error("app frame invalid len=%u", len);
+ return;
+ }
+ if (data[0] != MCU_PKT_HEAD_0 || data[1] != MCU_PKT_HEAD_1) {
+ log_error("app frame head err");
+ return;
+ }
+
+ evt.type = MCU_EVT_APP_FRAME;
+ evt.len = len;
+ memcpy(evt.buf, data, len);
+ mcu_bridge_post_event(&evt);
+}
+
+/**
+ * @brief 是否已从 MCU 获取到设备身份
+ * @return 1=已就绪,0=未就绪
+ * @note 单字节只读快照,可在任意任务直接调用(无需加锁)
+ */
+uint8_t mcu_bridge_is_identity_ready(void)
+{
+ return s_br.identity_ready;
+}
+
+/**
+ * @brief 配网成功通知(生产者,可在 BLE 协议栈任务调用)
+ */
+void mcu_bridge_on_paired(void)
+{
+ McuEvt_t evt;
+
+ memset(&evt, 0, sizeof(evt));
+ evt.type = MCU_EVT_BLE_PAIRED;
+ mcu_bridge_post_event(&evt);
+}
+
+/**
+ * @brief 配对信息被清除通知(生产者,可在 BLE 协议栈任务调用)
+ */
+void mcu_bridge_on_pair_cleared(void)
+{
+ McuEvt_t evt;
+
+ memset(&evt, 0, sizeof(evt));
+ evt.type = MCU_EVT_BLE_PAIR_CLEARED;
+ mcu_bridge_post_event(&evt);
+}
+
+/**
+ * @brief 获取当前工作状态字节
+ * @return 工作状态 bitmap
+ * @note 单字节只读快照,可在任意任务直接调用(无需加锁)
+ */
+uint8_t mcu_bridge_get_work_status(void)
+{
+ return s_br.work_status;
+}
+
+/**
+ * @brief 网关对 REPORTCMD_V2 主动上报的应答(生产者,可在 BLE 协议栈任务调用)
+ * @param cmd bleproto 包 cmd
+ * @param gateway_int_cnt gateway_int 个数
+ * @param gateway_int0 gateway_int[0]
+ */
+void mcu_bridge_on_gateway_report_ack(uint16_t cmd,
+ uint8_t gateway_int_cnt,
+ int32_t gateway_int0)
+{
+ McuEvt_t evt;
+
+ memset(&evt, 0, sizeof(evt));
+ evt.type = MCU_EVT_GATEWAY_ACK;
+ evt.gw_cmd = cmd;
+ evt.gw_int_cnt = gateway_int_cnt;
+ evt.gw_int0 = gateway_int0;
+ mcu_bridge_post_event(&evt);
+}
diff --git a/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_bridge.h b/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_bridge.h
new file mode 100644
index 0000000..ff20ee2
--- /dev/null
+++ b/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_bridge.h
@@ -0,0 +1,100 @@
+/******************************************************************************
+ * @file mcu_bridge.h
+ * @brief BLE+MCU 协议桥对外 API(模组侧唯一业务入口)
+ * @author cyWu <1917507415@qq.com>
+ * @date 2026.08.06
+ * @version V1.1.0
+ * @history
+ * - V1.0.0, 2026.08.06, cyWu, 首次发布
+ * - V1.1.0, 2026.08.06, cyWu, 内部改为事件队列 + 独立任务驱动;
+ * 所有 on_xxx 接口均可在任意任务上下文安全调用(生产者-消费者模型)
+ ******************************************************************************/
+
+#ifndef __MCU_BRIDGE_H__
+#define __MCU_BRIDGE_H__
+
+#include
+#include "mcu_cmd.h"
+
+/*============================================================================*/
+/* 错误码 */
+/*============================================================================*/
+
+typedef enum {
+ MCU_BRIDGE_OK = 0,
+ MCU_BRIDGE_ERR_PARAM,
+ MCU_BRIDGE_ERR_BUSY,
+ MCU_BRIDGE_ERR_TIMEOUT,
+ MCU_BRIDGE_ERR_NOT_INIT,
+ MCU_BRIDGE_ERR_UNKNOWN
+} McuBridge_Ret_t;
+
+/*============================================================================*/
+/* 外部接口 */
+/*============================================================================*/
+
+/**
+ * @brief 初始化 MCU 协议桥(UART + 事件队列 + 独立任务)
+ * @return McuBridge_Ret_t
+ * @note 在 app_main 中调用一次即可;内部会创建 "mcu_bridge" 任务,
+ * 所有身份/心跳/透传/状态机逻辑均在该任务单一上下文中处理
+ */
+McuBridge_Ret_t mcu_bridge_init(void);
+
+/**
+ * @brief UART 收到原始数据(由 mcu_uart 接收任务调用)
+ * @param data 数据
+ * @param len 长度
+ * @note 仅做逐字节组帧;帧就绪后投递事件到 mcu_bridge 任务处理,
+ * 可安全在 UART 接收任务上下文调用
+ */
+void mcu_bridge_on_uart_rx(const uint8_t *data, uint16_t len);
+
+/**
+ * @brief APP/网关经 BLE 下发的 55AA 帧
+ * @param data 帧数据
+ * @param len 长度
+ * @note 对 0x03/0x05:转发 MCU,等 0x04/0x06 后再回传 BLE;
+ * 可安全在 BLE 协议栈任务上下文调用(内部投递事件异步处理)
+ */
+void mcu_bridge_on_app_frame(const uint8_t *data, uint16_t len);
+
+/**
+ * @brief 是否已从 MCU 获取到设备身份
+ * @return 1=已就绪,0=未就绪
+ * @note 单字节只读快照,可在任意任务直接调用
+ */
+uint8_t mcu_bridge_is_identity_ready(void);
+
+/**
+ * @brief 配网成功通知(AUTHSETUP 写对后由 BLE 层调用)
+ * @note 可安全在 BLE 协议栈任务上下文调用(内部投递事件异步处理)
+ */
+void mcu_bridge_on_paired(void);
+
+/**
+ * @brief 配对信息被清除通知
+ * @note 可安全在 BLE 协议栈任务上下文调用(内部投递事件异步处理)
+ */
+void mcu_bridge_on_pair_cleared(void);
+
+/**
+ * @brief 获取当前工作状态字节
+ * @return 工作状态 bitmap
+ * @note 单字节只读快照,可在任意任务直接调用
+ */
+uint8_t mcu_bridge_get_work_status(void);
+
+/**
+ * @brief 网关对 REPORTCMD_V2 主动上报的应答
+ * @param cmd bleproto 包 cmd
+ * @param gateway_int_cnt gateway_int 个数
+ * @param gateway_int0 gateway_int[0]
+ * @note 成功条件:cmd==0 && cnt==1 && int0==0;
+ * 成功后才向 MCU 回 0x08;可安全在 BLE 协议栈任务上下文调用
+ */
+void mcu_bridge_on_gateway_report_ack(uint16_t cmd,
+ uint8_t gateway_int_cnt,
+ int32_t gateway_int0);
+
+#endif /* __MCU_BRIDGE_H__ */
diff --git a/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_cmd.h b/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_cmd.h
new file mode 100644
index 0000000..b3b6806
--- /dev/null
+++ b/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_cmd.h
@@ -0,0 +1,110 @@
+/******************************************************************************
+ * @file mcu_cmd.h
+ * @brief MCU 接入协议命令字 / 结果码 / 工作状态位定义
+ * @author cyWu <1917507415@qq.com>
+ * @date 2026.08.06
+ * @version V1.0.0
+ * @history
+ * - V1.0.0, 2026.08.06, cyWu, 首次发布
+ ******************************************************************************/
+
+#ifndef __MCU_CMD_H__
+#define __MCU_CMD_H__
+
+#include
+
+/*============================================================================*/
+/* 工作状态 Bit(命令 0x14) */
+/*============================================================================*/
+
+#define MCU_WS_CONFIGURED (1u << 0) /**< 已完成配网 */
+#define MCU_WS_INTERNET (1u << 1) /**< 已连互联网(云),预留:暂无法获取,固定为 0 */
+#define MCU_WS_APP_ONLINE (1u << 2) /**< APP 在线,预留:暂无法获取,固定为 0 */
+#define MCU_WS_BLE_CONNECTED (1u << 3) /**< 手机已连接 BLE,预留:暂无法获取,固定为 0 */
+#define MCU_WS_OTA_UPGRADING (1u << 4) /**< OTA 升级中 */
+#define MCU_WS_PAIRING_MODE (1u << 5) /**< 配网模式中 */
+
+/*============================================================================*/
+/* 命令字 */
+/*============================================================================*/
+
+typedef enum {
+ /* 身份 */
+ MCU_CMD_GET_IDENTITY = 0x01,
+ MCU_CMD_GET_IDENTITY_ACK = 0x02,
+
+ /* 数据透传 */
+ MCU_CMD_CONTROL = 0x03,
+ MCU_CMD_CONTROL_ACK = 0x04,
+ MCU_CMD_READ = 0x05,
+ MCU_CMD_READ_ACK = 0x06,
+ MCU_CMD_REPORT = 0x07,
+ MCU_CMD_REPORT_ACK = 0x08,
+
+ /* 配网 / 心跳 / 状态 */
+ MCU_CMD_PAIR_CFG = 0x10,
+ MCU_CMD_PAIR_CFG_ACK = 0x11,
+ MCU_CMD_HEARTBEAT = 0x12,
+ MCU_CMD_HEARTBEAT_ACK = 0x13,
+ MCU_CMD_WORK_STATUS = 0x14,
+ MCU_CMD_WORK_STATUS_ACK = 0x15,
+
+ /* 模组管理(后续扩展) */
+ MCU_CMD_RESET = 0x16,
+ MCU_CMD_RESET_ACK = 0x17,
+ MCU_CMD_REBOOT = 0x18,
+ MCU_CMD_REBOOT_ACK = 0x19,
+ MCU_CMD_MCU_REBOOT = 0x1A,
+ MCU_CMD_MCU_REBOOT_ACK = 0x1B,
+ MCU_CMD_GET_TIME = 0x1C,
+ MCU_CMD_GET_TIME_ACK = 0x1D,
+
+ /* 扩展功能(后续扩展) */
+ MCU_CMD_FACTORY = 0x20,
+ MCU_CMD_FACTORY_ACK = 0x21,
+ MCU_CMD_PHOTO = 0x22,
+ MCU_CMD_PHOTO_ACK = 0x23,
+ MCU_CMD_RECORD = 0x24,
+ MCU_CMD_RECORD_ACK = 0x25,
+ MCU_CMD_AUDIO = 0x26,
+ MCU_CMD_AUDIO_ACK = 0x27,
+
+ /* OTA(后续扩展) */
+ MCU_CMD_OTA_NOTIFY = 0x30,
+ MCU_CMD_OTA_NOTIFY_ACK = 0x31,
+ MCU_CMD_OTA_DATA = 0x32,
+ MCU_CMD_OTA_DATA_ACK = 0x33,
+ MCU_CMD_OTA_COMPLETE = 0x34,
+ MCU_CMD_OTA_COMPLETE_ACK = 0x35,
+ MCU_CMD_OTA_STOP_BLE = 0x36,
+ MCU_CMD_OTA_STOP_BLE_ACK = 0x37,
+ MCU_CMD_OTA_STOP_MCU = 0x38,
+ MCU_CMD_OTA_STOP_MCU_ACK = 0x39,
+
+ /* MCU 信息 */
+ MCU_CMD_GET_MCU_INFO = 0x40,
+ MCU_CMD_GET_MCU_INFO_ACK = 0x41
+} McuCmd_t;
+
+/*============================================================================*/
+/* 结果码 */
+/*============================================================================*/
+
+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,
+
+ /* 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
+} McuResult_t;
+
+#endif /* __MCU_CMD_H__ */
diff --git a/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_frame.c b/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_frame.c
new file mode 100644
index 0000000..cc1f171
--- /dev/null
+++ b/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_frame.c
@@ -0,0 +1,140 @@
+/******************************************************************************
+ * @file mcu_frame.c
+ * @brief MCU 接入协议 55AA 帧组包/解包
+ * @author cyWu <1917507415@qq.com>
+ * @date 2026.08.06
+ * @version V1.0.0
+ * @history
+ * - V1.0.0, 2026.08.06, cyWu, 首次发布
+ ******************************************************************************/
+
+#include "mcu_frame.h"
+#include "mcu_util.h"
+#include
+
+/*============================================================================*/
+/* 私有变量 */
+/*============================================================================*/
+
+static uint8_t s_state = 0; /* 0=寻头 1=收长度高 2=收长度低 3=收载荷 */
+static uint8_t s_prev = 0;
+static uint16_t s_expect = 0;
+static uint16_t s_idx = 0;
+static uint8_t s_buf[MCU_FRAME_MAX_LEN];
+
+/*============================================================================*/
+/* 公有函数 */
+/*============================================================================*/
+
+/**
+ * @brief 复位帧解析状态机
+ */
+void mcu_frame_parser_reset(void)
+{
+ s_state = 0;
+ s_prev = 0;
+ s_expect = 0;
+ s_idx = 0;
+}
+
+/**
+ * @brief 组帧并写入 out(含包头与校验)
+ * @param out 输出缓冲
+ * @param out_max 缓冲最大长度
+ * @param cmd 命令字
+ * @param sn 序列号
+ * @param payload 数据区,可为 NULL
+ * @param plen 数据长度
+ * @return 帧总长度;<0 失败
+ */
+int32_t mcu_frame_pack(uint8_t *out, uint16_t out_max,
+ uint8_t cmd, uint8_t sn,
+ const uint8_t *payload, uint16_t plen)
+{
+ uint16_t len_field;
+ uint16_t total;
+
+ if (!out) {
+ return -1;
+ }
+
+ /* LEN = CMD + SN + payload + CKSUM */
+ len_field = (uint16_t)(plen + 3);
+ total = (uint16_t)(len_field + 4);
+
+ if (total > out_max || total > MCU_FRAME_MAX_LEN) {
+ return -1;
+ }
+
+ out[0] = MCU_PKT_HEAD_0;
+ out[1] = MCU_PKT_HEAD_1;
+ mcu_util_write_u16_be(&out[2], len_field);
+ out[4] = cmd;
+ out[5] = sn;
+ if (payload && plen > 0) {
+ memcpy(&out[6], payload, plen);
+ }
+ out[total - 1] = mcu_util_checksum(out, total);
+ return (int32_t)total;
+}
+
+/**
+ * @brief 向解析器喂入原始字节,尝试提取完整帧
+ * @param byte 输入字节
+ * @param out 输出完整帧缓冲
+ * @param out_len 输出帧长度
+ * @return 1=得到完整帧,0=继续,-1=校验错误丢弃
+ */
+int8_t mcu_frame_feed(uint8_t byte, uint8_t *out, uint16_t *out_len)
+{
+ if (!out || !out_len) {
+ return 0;
+ }
+
+ if (s_state == 0) {
+ if (s_prev == MCU_PKT_HEAD_0 && byte == MCU_PKT_HEAD_1) {
+ s_buf[0] = MCU_PKT_HEAD_0;
+ s_buf[1] = MCU_PKT_HEAD_1;
+ s_idx = 2;
+ s_state = 1;
+ }
+ s_prev = byte;
+ return 0;
+ }
+
+ if (s_state == 1) {
+ s_buf[s_idx++] = byte;
+ s_state = 2;
+ return 0;
+ }
+
+ if (s_state == 2) {
+ s_buf[s_idx++] = byte;
+ s_expect = mcu_util_read_u16_be(&s_buf[2]);
+ if (s_expect < 3 || (s_expect + 4) > MCU_FRAME_MAX_LEN) {
+ mcu_frame_parser_reset();
+ s_prev = byte;
+ return -1;
+ }
+ s_state = 3;
+ return 0;
+ }
+
+ s_buf[s_idx++] = byte;
+ if (s_idx >= (uint16_t)(s_expect + 4)) {
+ uint16_t total = (uint16_t)(s_expect + 4);
+ uint8_t ck = mcu_util_checksum(s_buf, total);
+ int8_t ret;
+
+ if (ck == s_buf[total - 1]) {
+ memcpy(out, s_buf, total);
+ *out_len = total;
+ ret = 1;
+ } else {
+ ret = -1;
+ }
+ mcu_frame_parser_reset();
+ return ret;
+ }
+ return 0;
+}
diff --git a/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_frame.h b/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_frame.h
new file mode 100644
index 0000000..09139d0
--- /dev/null
+++ b/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_frame.h
@@ -0,0 +1,57 @@
+/******************************************************************************
+ * @file mcu_frame.h
+ * @brief MCU 接入协议 55AA 帧组包/解包
+ * @author cyWu <1917507415@qq.com>
+ * @date 2026.08.06
+ * @version V1.0.0
+ * @history
+ * - V1.0.0, 2026.08.06, cyWu, 首次发布
+ ******************************************************************************/
+
+#ifndef __MCU_FRAME_H__
+#define __MCU_FRAME_H__
+
+#include
+#include "mcu_cmd.h"
+
+/*============================================================================*/
+/* 宏定义 */
+/*============================================================================*/
+
+#define MCU_PKT_HEAD_0 (0x55)
+#define MCU_PKT_HEAD_1 (0xAA)
+#define MCU_FRAME_MAX_LEN (128)
+
+/*============================================================================*/
+/* 外部接口 */
+/*============================================================================*/
+
+/**
+ * @brief 组帧并写入 out(含包头与校验)
+ * @param out 输出缓冲
+ * @param out_max 缓冲最大长度
+ * @param cmd 命令字
+ * @param sn 序列号
+ * @param payload 数据区,可为 NULL
+ * @param plen 数据长度
+ * @return 帧总长度;<0 失败
+ */
+int32_t mcu_frame_pack(uint8_t *out, uint16_t out_max,
+ uint8_t cmd, uint8_t sn,
+ const uint8_t *payload, uint16_t plen);
+
+/**
+ * @brief 向解析器喂入原始字节,尝试提取完整帧
+ * @param byte 输入字节
+ * @param out 输出完整帧缓冲
+ * @param out_len 输出帧长度
+ * @return 1=得到完整帧,0=继续,-1=校验错误丢弃
+ */
+int8_t mcu_frame_feed(uint8_t byte, uint8_t *out, uint16_t *out_len);
+
+/**
+ * @brief 复位帧解析状态机
+ */
+void mcu_frame_parser_reset(void);
+
+#endif /* __MCU_FRAME_H__ */
diff --git a/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_uart.c b/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_uart.c
new file mode 100644
index 0000000..bddf1bc
--- /dev/null
+++ b/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_uart.c
@@ -0,0 +1,141 @@
+/******************************************************************************
+ * @file mcu_uart.c
+ * @brief BLE 模组与外部 MCU 的 UART 驱动(PA7 TX / PA8 RX, 9600)
+ * @author cyWu <1917507415@qq.com>
+ * @date 2026.08.06
+ * @version V1.3.0
+ * @history
+ * - V1.0.0, 2026.08.06, cyWu, 首次发布
+ * - V1.1.0, 2026.08.07, cyWu, TX 使用静态对齐缓冲 + write()
+ * - V1.2.0, 2026.08.07, cyWu, 对齐供应商 demo:关闭低功耗
+ * - V1.3.0, 2026.08.07, cyWu, 发送与厂家一致,直接 write(data),去掉 TX 拷贝
+ *
+ * @note 串口收发期间由 mcu_bridge 的 LP target 挡住休眠;
+ * 板级可开 SLEEP_EN,勿在无 LP 保护时直接开 DEEP_SLEEP。
+ ******************************************************************************/
+
+#include "system/includes.h"
+#include "asm/uart_dev.h"
+#include "asm/gpio.h"
+#include "mcu_uart.h"
+#include "mcu_bridge.h"
+#include "mcu_frame.h"
+
+#define LOG_TAG_CONST APP
+#define LOG_TAG "[MCU_UART]"
+#define LOG_ERROR_ENABLE
+#define LOG_DEBUG_ENABLE
+#define LOG_INFO_ENABLE
+#define LOG_DUMP_ENABLE
+#define LOG_CLI_ENABLE
+#include "debug.h"
+
+/*============================================================================*/
+/* 私有变量 */
+/*============================================================================*/
+
+static uint8_t s_initialized = 0;
+static const uart_bus_t *s_uart_bus = NULL;
+static u8 s_rx_cbuf[MCU_UART_RX_CBUF_SIZE] __attribute__((aligned(4)));
+static u8 s_rx_tmp[MCU_UART_RX_CBUF_SIZE];
+static OS_MUTEX s_tx_lock;
+
+/*============================================================================*/
+/* 私有函数 */
+/*============================================================================*/
+
+/**
+ * @brief UART 接收任务:阻塞读串口,喂入协议桥
+ * @param arg 未使用
+ */
+static void mcu_uart_rx_task(void *arg)
+{
+ u32 rxcnt;
+
+ (void)arg;
+ log_info("rx task start");
+
+ while (1) {
+ if (!s_uart_bus) {
+ os_time_dly(10);
+ continue;
+ }
+ /* timeout=0:无数据时 pend,有 RX/OT 中断后唤醒 */
+ rxcnt = s_uart_bus->read(s_rx_tmp, sizeof(s_rx_tmp), 0);
+ if (rxcnt > 0) {
+ mcu_bridge_on_uart_rx(s_rx_tmp, (uint16_t)rxcnt);
+ }
+ }
+}
+
+/*============================================================================*/
+/* 公有函数 */
+/*============================================================================*/
+
+/**
+ * @brief 打开 MCU 串口并创建接收任务
+ * @return McuUart_Ret_t
+ */
+McuUart_Ret_t mcu_uart_init(void)
+{
+ struct uart_platform_data_t u_arg = {0};
+
+ if (s_initialized) {
+ return MCU_UART_OK;
+ }
+
+ u_arg.tx_pin = IO_PORTA_07;
+ u_arg.rx_pin = IO_PORTA_08;
+ u_arg.rx_cbuf = s_rx_cbuf;
+ u_arg.rx_cbuf_size = MCU_UART_RX_CBUF_SIZE;
+ u_arg.frame_length = MCU_UART_RX_CBUF_SIZE;
+ u_arg.rx_timeout = 10; /* ms,帧间隙产生 OT */
+ u_arg.isr_cbfun = NULL;
+ u_arg.baud = MCU_UART_BAUD;
+ u_arg.is_9bit = 0;
+
+ s_uart_bus = uart_dev_open(&u_arg);
+ if (s_uart_bus == NULL) {
+ log_error("init open fail");
+ return MCU_UART_ERR_OPEN;
+ }
+
+ os_mutex_create(&s_tx_lock);
+ os_task_create(mcu_uart_rx_task, NULL, 31, 1024, 0, "mcu_uart");
+
+ s_initialized = 1;
+ log_info("init ok, PA7/PA8 @ %d", MCU_UART_BAUD);
+ return MCU_UART_OK;
+}
+
+/**
+ * @brief 向 MCU 发送数据
+ * @param data 发送缓冲
+ * @param len 长度
+ * @return McuUart_Ret_t
+ * @note 与 usr_hw_uart.c 一致:uart_bus->write(data, len)
+ */
+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) {
+ return MCU_UART_ERR_PARAM;
+ }
+
+ os_mutex_pend(&s_tx_lock, 0);
+ s_uart_bus->write((void *)data, len);
+ os_mutex_post(&s_tx_lock);
+
+ return MCU_UART_OK;
+}
+
+/**
+ * @brief 获取串口是否已初始化
+ * @return 1=已初始化,0=未初始化
+ */
+uint8_t mcu_uart_is_ready(void)
+{
+ return s_initialized;
+}
diff --git a/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_uart.h b/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_uart.h
new file mode 100644
index 0000000..226fef8
--- /dev/null
+++ b/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_uart.h
@@ -0,0 +1,62 @@
+/******************************************************************************
+ * @file mcu_uart.h
+ * @brief BLE 模组与外部 MCU 的 UART 驱动接口(PA7/PA8, 9600)
+ * @author cyWu <1917507415@qq.com>
+ * @date 2026.08.06
+ * @version V1.0.0
+ * @history
+ * - V1.0.0, 2026.08.06, cyWu, 首次发布
+ ******************************************************************************/
+
+#ifndef __MCU_UART_H__
+#define __MCU_UART_H__
+
+#include
+
+/*============================================================================*/
+/* 宏定义 */
+/*============================================================================*/
+
+#define MCU_UART_BAUD (9600) /**< 协议默认波特率 */
+#define MCU_UART_RX_CBUF_SIZE (512) /**< DMA 循环缓冲,须为 2 的幂 */
+/* 引脚:TX=PA7(IO_PORTA_07),RX=PA8(IO_PORTA_08),在 mcu_uart.c 中配置 */
+
+/*============================================================================*/
+/* 错误码 */
+/*============================================================================*/
+
+typedef enum {
+ MCU_UART_OK = 0,
+ MCU_UART_ERR_PARAM,
+ MCU_UART_ERR_BUSY,
+ MCU_UART_ERR_TIMEOUT,
+ MCU_UART_ERR_NOT_INIT,
+ MCU_UART_ERR_OPEN,
+ MCU_UART_ERR_UNKNOWN
+} McuUart_Ret_t;
+
+/*============================================================================*/
+/* 外部接口 */
+/*============================================================================*/
+
+/**
+ * @brief 打开 MCU 串口并创建接收任务
+ * @return McuUart_Ret_t
+ */
+McuUart_Ret_t mcu_uart_init(void);
+
+/**
+ * @brief 向 MCU 发送数据
+ * @param data 发送缓冲
+ * @param len 长度
+ * @return McuUart_Ret_t
+ */
+McuUart_Ret_t mcu_uart_send(const uint8_t *data, uint16_t len);
+
+/**
+ * @brief 获取串口是否已初始化
+ * @return 1=已初始化,0=未初始化
+ */
+uint8_t mcu_uart_is_ready(void);
+
+#endif /* __MCU_UART_H__ */
diff --git a/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_util.c b/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_util.c
new file mode 100644
index 0000000..63d27cf
--- /dev/null
+++ b/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_util.c
@@ -0,0 +1,108 @@
+/******************************************************************************
+ * @file mcu_util.c
+ * @brief MCU 桥通用工具实现
+ * @author cyWu <1917507415@qq.com>
+ * @date 2026.08.06
+ * @version V1.2.0
+ * @history
+ * - V1.0.0, 2026.08.06, cyWu, 首次发布
+ * - V1.1.0, 2026.08.07, cyWu, get_ms 改用 timer_get_ms
+ * - V1.2.0, 2026.08.10, cyWu, 删除空壳 timer_init
+ ******************************************************************************/
+
+#include "system/includes.h"
+#include "asm/timer.h"
+#include "mcu_util.h"
+
+/*============================================================================*/
+/* 公有函数 */
+/*============================================================================*/
+
+/**
+ * @brief 获取开机以来的毫秒计数
+ * @return 毫秒计数
+ * @note 直接使用系统 tick 换算的毫秒,不受低功耗拉长 usr_timer 影响
+ */
+uint32_t mcu_util_get_ms(void)
+{
+ return timer_get_ms();
+}
+
+/**
+ * @brief 协议累加和校验
+ * @param frame 完整帧(含 55 AA)
+ * @param total_len 帧总长度
+ * @return 校验字节
+ */
+uint8_t mcu_util_checksum(const uint8_t *frame, uint16_t total_len)
+{
+ uint16_t sum = 0;
+ uint16_t i;
+
+ if (!frame || total_len < 4) {
+ return 0;
+ }
+ for (i = 2; i < (uint16_t)(total_len - 1); i++) {
+ sum = (uint16_t)(sum + frame[i]);
+ }
+ return (uint8_t)(sum & 0xFF);
+}
+
+/**
+ * @brief 大端读取 uint16
+ * @param buf 字节缓冲
+ * @return 数值
+ */
+uint16_t mcu_util_read_u16_be(const uint8_t *buf)
+{
+ if (!buf) {
+ return 0;
+ }
+ return (uint16_t)(((uint16_t)buf[0] << 8) | buf[1]);
+}
+
+/**
+ * @brief 大端读取 uint32
+ * @param buf 字节缓冲
+ * @return 数值
+ */
+uint32_t mcu_util_read_u32_be(const uint8_t *buf)
+{
+ if (!buf) {
+ return 0;
+ }
+ return ((uint32_t)buf[0] << 24)
+ | ((uint32_t)buf[1] << 16)
+ | ((uint32_t)buf[2] << 8)
+ | (uint32_t)buf[3];
+}
+
+/**
+ * @brief 大端写入 uint16
+ * @param buf 字节缓冲
+ * @param val 数值
+ */
+void mcu_util_write_u16_be(uint8_t *buf, uint16_t val)
+{
+ if (!buf) {
+ return;
+ }
+ buf[0] = (uint8_t)(val >> 8);
+ buf[1] = (uint8_t)(val & 0xFF);
+}
+
+/**
+ * @brief 大端写入 uint32
+ * @param buf 字节缓冲
+ * @param val 数值
+ */
+void mcu_util_write_u32_be(uint8_t *buf, uint32_t val)
+{
+ if (!buf) {
+ return;
+ }
+ buf[0] = (uint8_t)(val >> 24);
+ buf[1] = (uint8_t)(val >> 16);
+ buf[2] = (uint8_t)(val >> 8);
+ buf[3] = (uint8_t)(val & 0xFF);
+}
diff --git a/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_util.h b/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_util.h
new file mode 100644
index 0000000..78f964c
--- /dev/null
+++ b/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_util.h
@@ -0,0 +1,65 @@
+/******************************************************************************
+ * @file mcu_util.h
+ * @brief MCU 桥通用工具:校验和、大端序、毫秒节拍
+ * @author cyWu <1917507415@qq.com>
+ * @date 2026.08.06
+ * @version V1.1.0
+ * @history
+ * - V1.0.0, 2026.08.06, cyWu, 首次发布
+ * - V1.1.0, 2026.08.10, cyWu, 删除空壳 timer_init
+ ******************************************************************************/
+
+#ifndef __MCU_UTIL_H__
+#define __MCU_UTIL_H__
+
+#include
+
+/*============================================================================*/
+/* 外部接口 */
+/*============================================================================*/
+
+/**
+ * @brief 获取开机以来的毫秒计数
+ * @return 毫秒计数
+ * @note 基于系统 timer_get_ms,不受低功耗拉长 usr_timer 影响
+ */
+uint32_t mcu_util_get_ms(void);
+
+/**
+ * @brief 协议累加和校验
+ * @param frame 完整帧(含 55 AA)
+ * @param total_len 帧总长度
+ * @return 校验字节
+ * @note 累加范围:LEN 字段起至校验和前一字节
+ */
+uint8_t mcu_util_checksum(const uint8_t *frame, uint16_t total_len);
+
+/**
+ * @brief 大端读取 uint16
+ * @param buf 字节缓冲
+ * @return 数值
+ */
+uint16_t mcu_util_read_u16_be(const uint8_t *buf);
+
+/**
+ * @brief 大端读取 uint32
+ * @param buf 字节缓冲
+ * @return 数值
+ */
+uint32_t mcu_util_read_u32_be(const uint8_t *buf);
+
+/**
+ * @brief 大端写入 uint16
+ * @param buf 字节缓冲
+ * @param val 数值
+ */
+void mcu_util_write_u16_be(uint8_t *buf, uint16_t val);
+
+/**
+ * @brief 大端写入 uint32
+ * @param buf 字节缓冲
+ * @param val 数值
+ */
+void mcu_util_write_u32_be(uint8_t *buf, uint32_t val);
+
+#endif /* __MCU_UTIL_H__ */
diff --git a/JBao_JL7016_MOD_FW/cpu/br28/tools/download/earphone/download_app_ota.bat b/JBao_JL7016_MOD_FW/cpu/br28/tools/download/earphone/download_app_ota.bat
index 1e15e05..a585ec3 100644
--- a/JBao_JL7016_MOD_FW/cpu/br28/tools/download/earphone/download_app_ota.bat
+++ b/JBao_JL7016_MOD_FW/cpu/br28/tools/download/earphone/download_app_ota.bat
@@ -10,10 +10,10 @@ copy ..\..\ota.bin .
copy ..\..\anc_coeff.bin .
copy ..\..\anc_gains.bin .
-..\..\isd_download.exe ..\..\isd_config.ini -tonorflash -dev br28 -boot 0x120000 -div8 -wait 300 -uboot ..\..\uboot.boot -app ..\..\app.bin -res ..\..\cfg_tool.bin tone.cfg p11_code.bin -uboot_compress -key RS-5B88.key
+..\..\isd_download.exe ..\..\isd_config.ini -tonorflash -dev br28 -boot 0x120000 -div8 -wait 300 -uboot ..\..\uboot.boot -app ..\..\app.bin -res ..\..\cfg_tool.bin tone.cfg p11_code.bin -uboot_compress
-:: -format all
+:: -format all -key RS-5B88.key
::-reboot 2500
@rem ɾʱļ-format all