重构 usr_mcu_bridge:拆成 protocol/business 分层,精简上报与 UART 超时逻辑

1、协议层迁入 protocol/,业务拆为 identity/pairing/link_status,mcu_bridge 收敛为薄门面
2、0x07 上报不再缓存重发,由 MCU 侧超时重发;模组只记 SN,网关 ACK 后回 0x08
3、APP 透传重试用尽后主动回 FAIL;UART ACK 等待统一为 MCU_UART_ACK_WAIT_MS=500ms
This commit is contained in:
2026-08-13 12:23:41 +08:00
parent 95973ec52d
commit c681db0590
23 changed files with 1782 additions and 1082 deletions
+2 -1
View File
@@ -1 +1,2 @@
PY32F040开发框架/* PY32F040开发框架/*
.vscode/
+29 -7
View File
@@ -175,6 +175,8 @@
<Add directory="apps/usr_le_code/thirdpart/mjson" /> <Add directory="apps/usr_le_code/thirdpart/mjson" />
<Add directory="apps/usr_le_code/thirdpart" /> <Add directory="apps/usr_le_code/thirdpart" />
<Add directory="apps/usr_mcu_bridge" /> <Add directory="apps/usr_mcu_bridge" />
<Add directory="apps/usr_mcu_bridge/protocol" />
<Add directory="apps/usr_mcu_bridge/business" />
</Compiler> </Compiler>
<Linker> <Linker>
<Add option="-flto" /> <Add option="-flto" />
@@ -761,19 +763,39 @@
<Option compilerVar="CC" /> <Option compilerVar="CC" />
</Unit> </Unit>
<Unit filename="apps/usr_mcu_bridge/mcu_bridge.h" /> <Unit filename="apps/usr_mcu_bridge/mcu_bridge.h" />
<Unit filename="apps/usr_mcu_bridge/mcu_cmd.h" /> <Unit filename="apps/usr_mcu_bridge/protocol/mcu_cmd.h" />
<Unit filename="apps/usr_mcu_bridge/mcu_frame.c"> <Unit filename="apps/usr_mcu_bridge/protocol/mcu_frame.c">
<Option compilerVar="CC" /> <Option compilerVar="CC" />
</Unit> </Unit>
<Unit filename="apps/usr_mcu_bridge/mcu_frame.h" /> <Unit filename="apps/usr_mcu_bridge/protocol/mcu_frame.h" />
<Unit filename="apps/usr_mcu_bridge/mcu_uart.c"> <Unit filename="apps/usr_mcu_bridge/protocol/mcu_uart.c">
<Option compilerVar="CC" /> <Option compilerVar="CC" />
</Unit> </Unit>
<Unit filename="apps/usr_mcu_bridge/mcu_uart.h" /> <Unit filename="apps/usr_mcu_bridge/protocol/mcu_uart.h" />
<Unit filename="apps/usr_mcu_bridge/mcu_util.c"> <Unit filename="apps/usr_mcu_bridge/protocol/mcu_util.c">
<Option compilerVar="CC" /> <Option compilerVar="CC" />
</Unit> </Unit>
<Unit filename="apps/usr_mcu_bridge/mcu_util.h" /> <Unit filename="apps/usr_mcu_bridge/protocol/mcu_util.h" />
<Unit filename="apps/usr_mcu_bridge/protocol/mcu_protocol.c">
<Option compilerVar="CC" />
</Unit>
<Unit filename="apps/usr_mcu_bridge/protocol/mcu_protocol.h" />
<Unit filename="apps/usr_mcu_bridge/business/mcu_identity.c">
<Option compilerVar="CC" />
</Unit>
<Unit filename="apps/usr_mcu_bridge/business/mcu_identity.h" />
<Unit filename="apps/usr_mcu_bridge/business/mcu_pairing.c">
<Option compilerVar="CC" />
</Unit>
<Unit filename="apps/usr_mcu_bridge/business/mcu_pairing.h" />
<Unit filename="apps/usr_mcu_bridge/business/mcu_link_status.c">
<Option compilerVar="CC" />
</Unit>
<Unit filename="apps/usr_mcu_bridge/business/mcu_link_status.h" />
<Unit filename="apps/usr_mcu_bridge/business/mcu_business.c">
<Option compilerVar="CC" />
</Unit>
<Unit filename="apps/usr_mcu_bridge/business/mcu_business.h" />
<Unit filename="apps/usr_le_code/ble_proto.c"> <Unit filename="apps/usr_le_code/ble_proto.c">
<Option compilerVar="CC" /> <Option compilerVar="CC" />
</Unit> </Unit>
+10 -3
View File
@@ -249,6 +249,8 @@ INCLUDES := \
-Iapps/usr_le_code/thirdpart \ -Iapps/usr_le_code/thirdpart \
-Iapps/usr_le_code/thirdpart/mjson \ -Iapps/usr_le_code/thirdpart/mjson \
-Iapps/usr_mcu_bridge \ -Iapps/usr_mcu_bridge \
-Iapps/usr_mcu_bridge/protocol \
-Iapps/usr_mcu_bridge/business \
-I$(SYS_INC_DIR) \ -I$(SYS_INC_DIR) \
@@ -614,9 +616,14 @@ c_SRC_FILES := \
cpu/br28/uart_dev.c \ cpu/br28/uart_dev.c \
cpu/br28/umidigi_chargestore.c \ cpu/br28/umidigi_chargestore.c \
apps/usr_mcu_bridge/mcu_bridge.c \ apps/usr_mcu_bridge/mcu_bridge.c \
apps/usr_mcu_bridge/mcu_frame.c \ apps/usr_mcu_bridge/protocol/mcu_frame.c \
apps/usr_mcu_bridge/mcu_uart.c \ apps/usr_mcu_bridge/protocol/mcu_uart.c \
apps/usr_mcu_bridge/mcu_util.c \ apps/usr_mcu_bridge/protocol/mcu_util.c \
apps/usr_mcu_bridge/protocol/mcu_protocol.c \
apps/usr_mcu_bridge/business/mcu_identity.c \
apps/usr_mcu_bridge/business/mcu_pairing.c \
apps/usr_mcu_bridge/business/mcu_link_status.c \
apps/usr_mcu_bridge/business/mcu_business.c \
apps/usr_le_code/ble_proto.c \ apps/usr_le_code/ble_proto.c \
apps/usr_le_code/bleproto.c \ apps/usr_le_code/bleproto.c \
apps/usr_le_code/bleproto_packer.c \ apps/usr_le_code/bleproto_packer.c \
@@ -3,10 +3,12 @@
* @brief usr_le_code 产品身份配置接口(由 MCU 桥运行时填充) * @brief usr_le_code 产品身份配置接口(由 MCU 桥运行时填充)
* @author cyWu <1917507415@qq.com> * @author cyWu <1917507415@qq.com>
* @date 2026.08.03 * @date 2026.08.03
* @version V1.1.0 * @version V1.2.0
* @history * @history
* - V1.0.0, 2026.08.03, cyWu, 首次发布 * - V1.0.0, 2026.08.03, cyWu, 首次发布
* - V1.1.0, 2026.08.06, cyWu, 改为非 const,由 mcu_bridge 从 MCU 0x02 写入 * - V1.1.0, 2026.08.06, cyWu, 改为非 const,由 mcu_bridge 从 MCU 0x02 写入
* - V1.2.0, 2026.08.12, cyWu, 改由 mcu_link 从 MCU 0x02 写入
* - V1.3.0, 2026.08.12, cyWu, 改由 business/mcu_identity 从 MCU 0x02 写入
******************************************************************************/ ******************************************************************************/
#ifndef __USR_LE_PRODUCT_H__ #ifndef __USR_LE_PRODUCT_H__
@@ -30,13 +32,13 @@ extern "C" {
/** /**
* @brief 产品类别码,对应 BLE 广播 prodcode(固定 4 字节 ASCII * @brief 产品类别码,对应 BLE 广播 prodcode(固定 4 字节 ASCII
* @note 由 mcu_bridge 从 MCU 身份应答取前 4 字节写入 * @note 由 business/mcu_identity 从 MCU 身份应答取前 4 字节写入
*/ */
extern 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 * @brief 平台产品码,对应 BLE 广播 pcode(固定 6 字节 ASCII
* @note 由 mcu_bridge 从 MCU 身份应答取前 6 字节写入 * @note 由 business/mcu_identity 从 MCU 身份应答取前 6 字节写入
*/ */
extern uint8_t g_usr_le_pcode[USR_LE_PCODE_LEN]; extern uint8_t g_usr_le_pcode[USR_LE_PCODE_LEN];
@@ -0,0 +1,109 @@
/******************************************************************************
* @file mcu_business.c
* @brief 业务门面实现
* @author cyWu <1917507415@qq.com>
* @date 2026.08.12
* @version V1.0.0
* @history
* - V1.0.0, 2026.08.12, cyWu, 取代原 mcu_link,编排三个业务子模块
******************************************************************************/
#include "system/includes.h"
#include "app_main.h"
#include "mcu_business.h"
#include "mcu_identity.h"
#include "mcu_pairing.h"
#include "mcu_link_status.h"
#include "mcu_cmd.h"
#define LOG_TAG_CONST APP
#define LOG_TAG "[MCU_BIZ]"
#define LOG_ERROR_ENABLE
#define LOG_INFO_ENABLE
#include "debug.h"
/**
* @brief 初始化业务层
* @note 上电不强制配网;广播由 mcu_identity_on_ack 在身份就绪后按
* 配对状态触发(usr_ble_adv_init 内部决策)
*/
void mcu_business_init(void)
{
mcu_identity_init();
mcu_pairing_init();
mcu_link_status_init();
usr_var.usr_goto_pair = 0;
usr_var.usr_adv_flag = 0;
}
/**
* @brief 周期处理
*/
void mcu_business_periodic(void)
{
mcu_identity_periodic();
mcu_link_status_periodic();
mcu_pairing_periodic();
}
/**
* @brief 是否身份就绪
* @return 1/0
*/
uint8_t mcu_business_is_identity_ready(void)
{
return mcu_identity_is_ready();
}
/**
* @brief 获取工作状态
* @return work_status
*/
uint8_t mcu_business_get_work_status(void)
{
return mcu_link_status_get();
}
/**
* @brief 处理身份应答
* @param frame 完整帧
* @param len 长度
*/
void mcu_business_on_identity_ack(const uint8_t *frame, uint16_t len)
{
mcu_identity_on_ack(frame, len);
}
/**
* @brief 处理配网请求
* @param mode 模式
* @param timeout_s 超时秒
*/
void mcu_business_on_pair_cfg(uint8_t mode, uint16_t timeout_s)
{
mcu_pairing_start(mode, timeout_s);
}
/**
* @brief 配网成功
*/
void mcu_business_on_paired(void)
{
usr_var.usr_goto_pair = 0;
mcu_pairing_stop();
mcu_link_status_set_phase(MCU_WS_CONFIGURED);
log_info("paired -> CONFIGURED");
}
/**
* @brief 配对清除
* @note 若正处于配网流程(phase==PAIRING),保持不变;
* 否则(例如单纯清空配对表)回到未配网空闲
*/
void mcu_business_on_pair_cleared(void)
{
if (mcu_link_status_get_phase() != MCU_WS_PAIRING) {
mcu_link_status_set_phase(0);
}
}
@@ -0,0 +1,72 @@
/******************************************************************************
* @file mcu_business.h
* @brief 业务门面:协调身份 / 配网 / 连接状态三个业务子模块
* @author cyWu <1917507415@qq.com>
* @date 2026.08.12
* @version V1.0.0
* @history
* - V1.0.0, 2026.08.12, cyWu, 取代原 mcu_link,供协议层(mcu_protocol
* 单点调用;协议层不直接依赖 identity/pairing/link_status 三个子模块
******************************************************************************/
#ifndef __MCU_BUSINESS_H__
#define __MCU_BUSINESS_H__
#include <stdint.h>
/*============================================================================*/
/* 说明 */
/* 本模块只做业务编排,不含具体状态;具体逻辑见: */
/* mcu_identity.c —— 身份获取 */
/* mcu_pairing.c —— 配网请求 / 超时 */
/* mcu_link_status.c —— 连接状态位图(Bit0/1/2) */
/* 须在协议任务(mcu_protocol_task)单一上下文中调用。 */
/*============================================================================*/
/**
* @brief 初始化业务层(身份 + 配网 + 连接状态)
*/
void mcu_business_init(void);
/**
* @brief 周期处理:依次驱动身份 / 连接状态 / 配网三个子业务
*/
void mcu_business_periodic(void);
/**
* @brief 是否已从 MCU 获取到设备身份
* @return 1=已就绪,0=未就绪
*/
uint8_t mcu_business_is_identity_ready(void);
/**
* @brief 获取当前工作状态字节(命令 0x14 载荷)
* @return 工作状态 bitmap
*/
uint8_t mcu_business_get_work_status(void);
/**
* @brief 处理 0x02 身份应答
* @param frame 完整帧
* @param len 长度
*/
void mcu_business_on_identity_ack(const uint8_t *frame, uint16_t len);
/**
* @brief 处理已通过协议校验的 0x10 配网请求
* @param mode 1 或 2
* @param timeout_s 超时秒数,0 表示不超时
*/
void mcu_business_on_pair_cfg(uint8_t mode, uint16_t timeout_s);
/**
* @brief 配网成功(AUTHSETUP 写对)
*/
void mcu_business_on_paired(void);
/**
* @brief 配对信息被清除
*/
void mcu_business_on_pair_cleared(void);
#endif /* __MCU_BUSINESS_H__ */
@@ -0,0 +1,139 @@
/******************************************************************************
* @file mcu_identity.c
* @brief 身份业务实现
* @author cyWu <1917507415@qq.com>
* @date 2026.08.12
* @version V1.0.0
* @history
* - V1.0.0, 2026.08.12, cyWu, 从 mcu_link 拆出
******************************************************************************/
#include "system/includes.h"
#include "mcu_identity.h"
#include "mcu_protocol.h"
#include "mcu_cmd.h"
#include "mcu_util.h"
#include "usr_le_api.h"
#include "usr_le_product.h"
#define LOG_TAG_CONST APP
#define LOG_TAG "[MCU_ID]"
#define LOG_ERROR_ENABLE
#define LOG_DEBUG_ENABLE
#define LOG_INFO_ENABLE
#include "debug.h"
/*============================================================================*/
/* 私有宏 */
/*============================================================================*/
#define MCU_IDENTITY_RETRY_MS (1000u)
#define MCU_IDENTITY_PAYLOAD_MIN (2 + 8 + 8 + 16 + 8) /* Ver+Cat+Prod+SN+Cap */
/*============================================================================*/
/* 公有变量:运行时可改写的产品身份(供 BLE 广播使用) */
/*============================================================================*/
uint8_t g_usr_le_prodcode[USR_LE_PRODCODE_LEN];
uint8_t g_usr_le_pcode[USR_LE_PCODE_LEN];
/*============================================================================*/
/* 私有类型/变量 */
/*============================================================================*/
typedef struct {
uint8_t ready;
uint32_t last_tx_ms;
} McuIdentity_t;
static McuIdentity_t s_identity;
/*============================================================================*/
/* 公有函数 */
/*============================================================================*/
/**
* @brief 初始化身份业务状态
*/
void mcu_identity_init(void)
{
memset(&s_identity, 0, sizeof(s_identity));
memset(g_usr_le_prodcode, 0, sizeof(g_usr_le_prodcode));
memset(g_usr_le_pcode, 0, sizeof(g_usr_le_pcode));
}
/**
* @brief 身份未就绪时每 1s 发 0x01
*/
void mcu_identity_periodic(void)
{
uint32_t now;
if (s_identity.ready) {
return;
}
now = mcu_util_get_ms();
if ((now - s_identity.last_tx_ms) < MCU_IDENTITY_RETRY_MS) {
return;
}
s_identity.last_tx_ms = now;
if (mcu_protocol_send_cmd(MCU_CMD_GET_IDENTITY, NULL, 0) > 0) {
log_debug("identity req");
}
}
/**
* @brief 是否身份就绪
* @return 1/0
*/
uint8_t mcu_identity_is_ready(void)
{
return s_identity.ready;
}
/**
* @brief 处理 0x02 身份应答
* @param frame 完整帧
* @param len 长度
* @note 载荷布局(Result 之后):Ver(2)+Category(8)+Product(8)+SN(16)+Cap(8)
*/
void mcu_identity_on_ack(const uint8_t *frame, uint16_t len)
{
int i;
if (!frame || 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_identity.ready = 1;
printf("identity ok prodcode:");
for (i = 0; i < USR_LE_PRODCODE_LEN; i++) {
printf("%c", g_usr_le_prodcode[i]);
}
printf("\n");
printf("identity ok pcode:");
for (i = 0; i < USR_LE_PCODE_LEN; i++) {
printf("%c", g_usr_le_pcode[i]);
}
printf("\n");
/* 身份就绪:强制协议层重推 0x14,并刷新广播(决策见 usr_ble_adv_init */
mcu_protocol_invalidate_work_status_push();
usr_ble_adv_init();
}
@@ -0,0 +1,41 @@
/******************************************************************************
* @file mcu_identity.h
* @brief 身份业务:获取并持有 MCU 产品身份(prodcode/pcode
* @author cyWu <1917507415@qq.com>
* @date 2026.08.12
* @version V1.0.0
* @history
* - V1.0.0, 2026.08.12, cyWu, 从 mcu_link 拆出,独立为身份业务模块
******************************************************************************/
#ifndef __MCU_IDENTITY_H__
#define __MCU_IDENTITY_H__
#include <stdint.h>
/**
* @brief 初始化身份业务状态
*/
void mcu_identity_init(void);
/**
* @brief 周期处理:未就绪时每 1s 重发 0x01 身份请求
* @note 仅在协议任务上下文调用
*/
void mcu_identity_periodic(void);
/**
* @brief 是否已从 MCU 获取到设备身份
* @return 1=已就绪,0=未就绪
*/
uint8_t mcu_identity_is_ready(void);
/**
* @brief 处理 0x02 身份应答
* @param frame 完整帧
* @param len 长度
* @note 成功后写入 g_usr_le_prodcode/g_usr_le_pcode,并刷新 BLE 广播
*/
void mcu_identity_on_ack(const uint8_t *frame, uint16_t len);
#endif /* __MCU_IDENTITY_H__ */
@@ -0,0 +1,129 @@
/******************************************************************************
* @file mcu_link_status.c
* @brief 连接状态业务实现
* @author cyWu <1917507415@qq.com>
* @date 2026.08.12
* @version V1.0.0
* @history
* - V1.0.0, 2026.08.12, cyWu, 从 mcu_link 拆出
******************************************************************************/
#include "system/includes.h"
#include "app_main.h"
#include "mcu_link_status.h"
#include "mcu_identity.h"
#include "mcu_cmd.h"
#include "usr_le_api.h"
#define LOG_TAG_CONST APP
#define LOG_TAG "[MCU_LNK]"
#define LOG_ERROR_ENABLE
#define LOG_DEBUG_ENABLE
#define LOG_INFO_ENABLE
#include "debug.h"
/*============================================================================*/
/* 外部符号 */
/*============================================================================*/
extern u16 usr_get_conn_service(void);
/*============================================================================*/
/* 私有变量 */
/*============================================================================*/
typedef struct {
uint8_t work_status;
uint8_t last_ble_conn;
} McuLinkStatus_t;
static McuLinkStatus_t s_status;
/*============================================================================*/
/* 公有函数 */
/*============================================================================*/
/**
* @brief 初始化连接状态
*/
void mcu_link_status_init(void)
{
memset(&s_status, 0, sizeof(s_status));
}
/**
* @brief 获取完整工作状态字节
* @return work_status
*/
uint8_t mcu_link_status_get(void)
{
return s_status.work_status;
}
/**
* @brief 获取当前 Bit0/1/2 互斥过程态
* @return phase
*/
uint8_t mcu_link_status_get_phase(void)
{
return (uint8_t)(s_status.work_status & MCU_WS_LINK_MASK);
}
/**
* @brief 设置 Bit0/1/2 互斥过程态
* @param phase 目标过程态
*/
void mcu_link_status_set_phase(uint8_t phase)
{
s_status.work_status &= (uint8_t)~MCU_WS_LINK_MASK;
if (phase & MCU_WS_LINK_MASK) {
s_status.work_status |= (uint8_t)(phase & MCU_WS_LINK_MASK);
}
}
/**
* @brief 设置/清除 Bit6 OTA
* @param on 1/0
*/
void mcu_link_status_set_ota(uint8_t on)
{
if (on) {
s_status.work_status |= MCU_WS_OTA_UPGRADING;
} else {
s_status.work_status &= (uint8_t)~MCU_WS_OTA_UPGRADING;
}
}
/**
* @brief 周期处理:轮询 BLE 连接,维护 Bit0/1/2,已配网断连时开回连广播
* @note - 配网中(phase==PAIRING)由配网业务维护,此处不覆盖
* - 已配网:已连接→CONFIGURED,未连接→RECONNECTING
* - Bit3~5 预留固定 0,不在此处更新
*/
void mcu_link_status_periodic(void)
{
uint8_t conn = (usr_get_conn_service() != 0) ? 1 : 0;
uint8_t paired = usr_get_pair_flag();
if (conn != s_status.last_ble_conn) {
s_status.last_ble_conn = conn;
if (!conn && paired) {
/* 已配网断连:发回连广播(非绑定广播) */
usr_var.usr_goto_pair = 0;
if (mcu_identity_is_ready()) {
usr_ble_adv_init();
}
}
}
if (mcu_link_status_get_phase() == MCU_WS_PAIRING) {
return; /* 配网流程中不切换到回连/已配网 */
}
if (paired) {
mcu_link_status_set_phase(conn ? MCU_WS_CONFIGURED : MCU_WS_RECONNECTING);
} else {
mcu_link_status_set_phase(0); /* 未配网空闲 */
}
}
@@ -0,0 +1,53 @@
/******************************************************************************
* @file mcu_link_status.h
* @brief 连接状态业务:工作状态位图(Bit0/1/2 互斥过程态 + OTA 位)
* @author cyWu <1917507415@qq.com>
* @date 2026.08.12
* @version V1.0.0
* @history
* - V1.0.0, 2026.08.12, cyWu, 从 mcu_link 拆出,独立为连接状态业务模块
******************************************************************************/
#ifndef __MCU_LINK_STATUS_H__
#define __MCU_LINK_STATUS_H__
#include <stdint.h>
/**
* @brief 初始化连接状态
*/
void mcu_link_status_init(void);
/**
* @brief 周期处理:轮询 BLE 连接,维护 Bit0/1/2,已配网断连时触发回连广播
* @note 配网中(phase==PAIRING)时不会被本函数覆盖
*/
void mcu_link_status_periodic(void);
/**
* @brief 获取完整工作状态字节(命令 0x14 载荷)
* @return work_status
*/
uint8_t mcu_link_status_get(void);
/**
* @brief 获取当前 Bit0/1/2 互斥过程态
* @return MCU_WS_PAIRING / MCU_WS_CONFIGURED / MCU_WS_RECONNECTING / 0
*/
uint8_t mcu_link_status_get_phase(void);
/**
* @brief 设置 Bit0/1/2 互斥过程态
* @param phase MCU_WS_PAIRING / MCU_WS_CONFIGURED / MCU_WS_RECONNECTING / 0
* @note 只改 Bit0/1/2,不影响 Bit6 OTA 等独立位
*/
void mcu_link_status_set_phase(uint8_t phase);
/**
* @brief 设置/清除 Bit6 OTA 升级中
* @param on 1=置位,0=清除
* @note 预留给后续 OTA 模块调用
*/
void mcu_link_status_set_ota(uint8_t on);
#endif /* __MCU_LINK_STATUS_H__ */
@@ -0,0 +1,108 @@
/******************************************************************************
* @file mcu_pairing.c
* @brief 配网业务实现
* @author cyWu <1917507415@qq.com>
* @date 2026.08.12
* @version V1.0.0
* @history
* - V1.0.0, 2026.08.12, cyWu, 从 mcu_link 拆出
******************************************************************************/
#include "system/includes.h"
#include "app_main.h"
#include "mcu_pairing.h"
#include "mcu_link_status.h"
#include "mcu_cmd.h"
#include "mcu_util.h"
#include "usr_le_api.h"
#define LOG_TAG_CONST APP
#define LOG_TAG "[MCU_PAIR]"
#define LOG_ERROR_ENABLE
#define LOG_DEBUG_ENABLE
#define LOG_INFO_ENABLE
#include "debug.h"
/*============================================================================*/
/* 外部符号 */
/*============================================================================*/
extern void ble_trans_module_enable(u8 flag);
/*============================================================================*/
/* 私有变量 */
/*============================================================================*/
typedef struct {
uint16_t timeout_s;
uint32_t start_ms;
} McuPairing_t;
static McuPairing_t s_pairing;
/*============================================================================*/
/* 公有函数 */
/*============================================================================*/
/**
* @brief 初始化配网业务状态
*/
void mcu_pairing_init(void)
{
memset(&s_pairing, 0, sizeof(s_pairing));
}
/**
* @brief 开始配网
* @param mode 模式(预留)
* @param timeout_s 超时秒数
*/
void mcu_pairing_start(uint8_t mode, uint16_t timeout_s)
{
s_pairing.timeout_s = timeout_s;
s_pairing.start_ms = mcu_util_get_ms();
mcu_link_status_set_phase(MCU_WS_PAIRING);
log_info("pairing start mode=%u timeout=%u", mode, timeout_s);
usr_goto_pair_mode(); /* 清配对 + 置 usr_goto_pair + 发绑定广播 */
}
/**
* @brief 停止配网计时
*/
void mcu_pairing_stop(void)
{
s_pairing.timeout_s = 0;
}
/**
* @brief 配网超时检测
*/
void mcu_pairing_periodic(void)
{
uint32_t now;
uint32_t timeout_ms;
if (mcu_link_status_get_phase() != MCU_WS_PAIRING) {
return;
}
if (s_pairing.timeout_s == 0) {
return;
}
if (usr_get_pair_flag()) {
return; /* 已配网成功,交由 mcu_business_on_paired 处理切态 */
}
now = mcu_util_get_ms();
timeout_ms = (uint32_t)s_pairing.timeout_s * 1000u;
if ((now - s_pairing.start_ms) < timeout_ms) {
return;
}
log_info("pairing timeout, stop adv");
usr_var.usr_goto_pair = 0;
s_pairing.timeout_s = 0;
mcu_link_status_set_phase(0);
ble_trans_module_enable(0);
usr_var.usr_adv_flag = 0;
}
@@ -0,0 +1,40 @@
/******************************************************************************
* @file mcu_pairing.h
* @brief 配网业务:0x10 配网请求处理、配网超时退出
* @author cyWu <1917507415@qq.com>
* @date 2026.08.12
* @version V1.0.0
* @history
* - V1.0.0, 2026.08.12, cyWu, 从 mcu_link 拆出,独立为配网业务模块
******************************************************************************/
#ifndef __MCU_PAIRING_H__
#define __MCU_PAIRING_H__
#include <stdint.h>
/**
* @brief 初始化配网业务状态
*/
void mcu_pairing_init(void);
/**
* @brief 周期处理:配网超时检测
* @note 仅在 mcu_link_status_get_phase()==MCU_WS_PAIRING 时生效
*/
void mcu_pairing_periodic(void);
/**
* @brief 开始配网(协议层已完成参数校验与 ACK)
* @param mode 1 或 2(预留,当前两种模式行为一致)
* @param timeout_s 超时秒数,0 表示不超时
* @note 置 Bit0=配网中,并触发绑定广播(usr_goto_pair_mode
*/
void mcu_pairing_start(uint8_t mode, uint16_t timeout_s);
/**
* @brief 停止配网计时(配网成功后调用)
*/
void mcu_pairing_stop(void);
#endif /* __MCU_PAIRING_H__ */
File diff suppressed because it is too large Load Diff
@@ -1,13 +1,16 @@
/****************************************************************************** /******************************************************************************
* @file mcu_bridge.h * @file mcu_bridge.h
* @brief BLE+MCU 协议桥对外 API(模组侧唯一业务入口 * @brief BLE+MCU 接入模块对外总入口(组合门面
* @author cyWu <1917507415@qq.com> * @author cyWu <1917507415@qq.com>
* @date 2026.08.06 * @date 2026.08.12
* @version V1.1.0 * @version V2.0.0
* @history * @history
* - V1.0.0, 2026.08.06, cyWu, 首次发布 * - V1.0.0, 2026.08.06, cyWu, 首次发布
* - V1.1.0, 2026.08.06, cyWu, 内部改为事件队列 + 独立任务驱动 * - V1.1.0, 2026.08.06, cyWu, 内部改为事件队列 + 独立任务驱动
* 所有 on_xxx 接口均可在任意任务上下文安全调用(生产者-消费者模型) * - V1.2.0, 2026.08.12, cyWu, 与 mcu_link 解耦
* - V2.0.0, 2026.08.12, cyWu, 整体重构为 protocol/ + business/ 分层:
* 本文件不再包含任何实现逻辑,只做“组合根”,对外 API 保持不变,
* 外部调用方(app_main.c / usr_le_adv.c / usr_le_recieve.c 等)无需改动
******************************************************************************/ ******************************************************************************/
#ifndef __MCU_BRIDGE_H__ #ifndef __MCU_BRIDGE_H__
@@ -16,6 +19,28 @@
#include <stdint.h> #include <stdint.h>
#include "mcu_cmd.h" #include "mcu_cmd.h"
/*============================================================================*/
/* 说明:整体分层 */
/* */
/* apps/usr_mcu_bridge/ */
/* ├── mcu_bridge.c/h —— 本文件:组合根,稳定对外 API */
/* ├── protocol/ —— 协议层:只做协议机制,不含业务判断 */
/* │ ├── mcu_cmd.h 命令字 / 结果码 / work_status 位定义 */
/* │ ├── mcu_frame.c/h 55AA 组帧/解帧 */
/* │ ├── mcu_uart.c/h UART 收发驱动 */
/* │ ├── mcu_util.c/h 校验和/大端序/毫秒工具 */
/* │ └── mcu_protocol.c/h 事件队列/任务/分发/ACK/透传重传/心跳 */
/* └── business/ —— 业务层:按业务拆分,互不直接依赖对方实现 */
/* ├── mcu_identity.c/h 身份业务(0x01/0x02 */
/* ├── mcu_pairing.c/h 配网业务(0x10、超时) */
/* ├── mcu_link_status.c/h 连接状态业务(Bit0/1/2/6 位图) */
/* └── mcu_business.c/h 业务门面:协调以上三者,供协议层调用 */
/* */
/* 依赖方向(单向,禁止反向 include): */
/* mcu_bridge → protocol → business(子模块) → business(门面) */
/* business 子模块只经 mcu_protocol_send_cmd 等窄接口回调协议层发送 */
/*============================================================================*/
/*============================================================================*/ /*============================================================================*/
/* 错误码 */ /* 错误码 */
/*============================================================================*/ /*============================================================================*/
@@ -30,14 +55,12 @@ typedef enum {
} McuBridge_Ret_t; } McuBridge_Ret_t;
/*============================================================================*/ /*============================================================================*/
/* 外部接口 */ /* 外部接口(稳定 API,供 app_main / usr_le_code 等外部模块调用) */
/*============================================================================*/ /*============================================================================*/
/** /**
* @brief 初始化 MCU 协议桥(UART + 事件队列 + 独立任务 * @brief 初始化 MCU 接入模块(组合 protocol + business
* @return McuBridge_Ret_t * @return McuBridge_Ret_t
* @note 在 app_main 中调用一次即可;内部会创建 "mcu_bridge" 任务,
* 所有身份/心跳/透传/状态机逻辑均在该任务单一上下文中处理
*/ */
McuBridge_Ret_t mcu_bridge_init(void); McuBridge_Ret_t mcu_bridge_init(void);
@@ -45,8 +68,6 @@ McuBridge_Ret_t mcu_bridge_init(void);
* @brief UART 收到原始数据(由 mcu_uart 接收任务调用) * @brief UART 收到原始数据(由 mcu_uart 接收任务调用)
* @param data 数据 * @param data 数据
* @param len 长度 * @param len 长度
* @note 仅做逐字节组帧;帧就绪后投递事件到 mcu_bridge 任务处理,
* 可安全在 UART 接收任务上下文调用
*/ */
void mcu_bridge_on_uart_rx(const uint8_t *data, uint16_t len); void mcu_bridge_on_uart_rx(const uint8_t *data, uint16_t len);
@@ -54,34 +75,28 @@ void mcu_bridge_on_uart_rx(const uint8_t *data, uint16_t len);
* @brief APP/网关经 BLE 下发的 55AA 帧 * @brief APP/网关经 BLE 下发的 55AA 帧
* @param data 帧数据 * @param data 帧数据
* @param len 长度 * @param len 长度
* @note 对 0x03/0x05:转发 MCU,等 0x04/0x06 后再回传 BLE
* 可安全在 BLE 协议栈任务上下文调用(内部投递事件异步处理)
*/ */
void mcu_bridge_on_app_frame(const uint8_t *data, uint16_t len); void mcu_bridge_on_app_frame(const uint8_t *data, uint16_t len);
/** /**
* @brief 是否已从 MCU 获取到设备身份 * @brief 是否已从 MCU 获取到设备身份
* @return 1=已就绪,0=未就绪 * @return 1=已就绪,0=未就绪
* @note 单字节只读快照,可在任意任务直接调用
*/ */
uint8_t mcu_bridge_is_identity_ready(void); uint8_t mcu_bridge_is_identity_ready(void);
/** /**
* @brief 配网成功通知(AUTHSETUP 写对后由 BLE 层调用 * @brief 配网成功通知(BLE 协议栈任一上下文调用均可,内部会入队串行处理
* @note 可安全在 BLE 协议栈任务上下文调用(内部投递事件异步处理)
*/ */
void mcu_bridge_on_paired(void); void mcu_bridge_on_paired(void);
/** /**
* @brief 配对信息被清除通知 * @brief 配对信息被清除通知
* @note 可安全在 BLE 协议栈任务上下文调用(内部投递事件异步处理)
*/ */
void mcu_bridge_on_pair_cleared(void); void mcu_bridge_on_pair_cleared(void);
/** /**
* @brief 获取当前工作状态字节 * @brief 获取当前工作状态字节(命令 0x14 载荷)
* @return 工作状态 bitmap * @return 工作状态 bitmap
* @note 单字节只读快照,可在任意任务直接调用
*/ */
uint8_t mcu_bridge_get_work_status(void); uint8_t mcu_bridge_get_work_status(void);
@@ -90,8 +105,6 @@ uint8_t mcu_bridge_get_work_status(void);
* @param cmd bleproto 包 cmd * @param cmd bleproto 包 cmd
* @param gateway_int_cnt gateway_int 个数 * @param gateway_int_cnt gateway_int 个数
* @param gateway_int0 gateway_int[0] * @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, void mcu_bridge_on_gateway_report_ack(uint16_t cmd,
uint8_t gateway_int_cnt, uint8_t gateway_int_cnt,
@@ -2,10 +2,12 @@
* @file mcu_cmd.h * @file mcu_cmd.h
* @brief MCU / / * @brief MCU / /
* @author cyWu <1917507415@qq.com> * @author cyWu <1917507415@qq.com>
* @date 2026.08.06 * @date 2026.08.12
* @version V1.0.0 * @version V1.1.0
* @history * @history
* - V1.0.0, 2026.08.06, cyWu, * - V1.0.0, 2026.08.06, cyWu,
* - V1.1.0, 2026.08.12, cyWu, Bit0/1/2
* protocol/
******************************************************************************/ ******************************************************************************/
#ifndef __MCU_CMD_H__ #ifndef __MCU_CMD_H__
@@ -15,14 +17,23 @@
/*============================================================================*/ /*============================================================================*/
/* 工作状态 Bit(命令 0x14 */ /* 工作状态 Bit(命令 0x14 */
/* */
/* Bit0/1/2 互斥(至多一个为 1;可全 0=未配网空闲): */
/* Bit0 配网中 / Bit1 已配网正常 / Bit2 断线重连中 */
/* Bit3~5、Bit7 预留,固定 0Bit6 为 OTA,可与过程态独立叠加 */
/*============================================================================*/ /*============================================================================*/
#define MCU_WS_CONFIGURED (1u << 0) /**< 已完成配网 */ #define MCU_WS_PAIRING (1u << 0) /**< 配网中(绑定广播) */
#define MCU_WS_INTERNET (1u << 1) /**< 已连互联网(云),预留:暂无法获取,固定为 0 */ #define MCU_WS_CONFIGURED (1u << 1) /**< 已配网正常态(已连接/稳定工作) */
#define MCU_WS_APP_ONLINE (1u << 2) /**< APP 在线,预留:暂无法获取,固定为 0 */ #define MCU_WS_RECONNECTING (1u << 2) /**< 断线重连中(回连广播) */
#define MCU_WS_BLE_CONNECTED (1u << 3) /**< 手机已连接 BLE,预留:暂无法获取,固定 0 */ #define MCU_WS_CLOUD_ONLINE (1u << 3) /**< 已连接到云,预留:暂固定 0 */
#define MCU_WS_OTA_UPGRADING (1u << 4) /**< OTA 升级中 */ #define MCU_WS_APP_ONLINE (1u << 4) /**< APP 在线,预留:暂固定 0 */
#define MCU_WS_PAIRING_MODE (1u << 5) /**< 配网模式中 */ #define MCU_WS_APP_DIRECT (1u << 5) /**< APP 直连,预留:暂固定 0 */
#define MCU_WS_OTA_UPGRADING (1u << 6) /**< 模组 OTA 升级中 */
/* Bit7 预留 */
/** Bit0/1/2 过程态掩码(互斥组) */
#define MCU_WS_LINK_MASK (MCU_WS_PAIRING | MCU_WS_CONFIGURED | MCU_WS_RECONNECTING)
/*============================================================================*/ /*============================================================================*/
/* 命令字 */ /* 命令字 */
@@ -0,0 +1,881 @@
/******************************************************************************
* @file mcu_protocol.c
* @brief MCU 接入协议引擎实现
* @author cyWu <1917507415@qq.com>
* @date 2026.08.12
* @version V1.1.0
* @history
* - V1.0.0, 2026.08.12, cyWu, 从 mcu_bridge.c 拆出(历史版本见原文件):
* 事件队列 + 独立任务驱动、LP target、心跳/透传重传均原样保留
* - V1.1.0, 2026.08.12, cyWu, 0x07 上报改为不缓存、不超时重发:
* MCU 自身已有超时重发机制,模组侧只记 SN 转发+等 ACK 回 0x08
* 去掉 report_pending 整包缓存与 mcu_protocol_check_report_pending()
******************************************************************************/
#include "system/includes.h"
#include "asm/power/power_api.h"
#include "mcu_protocol.h"
#include "mcu_uart.h"
#include "mcu_frame.h"
#include "mcu_util.h"
#include "usr_le_api.h"
#include "mcu_business.h"
#define LOG_TAG_CONST APP
#define LOG_TAG "[MCU_PROTO]"
#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_HEARTBEAT_IDLE_MS (55000u)
#define MCU_HEARTBEAT_MAX_RETRY (3u)
#define MCU_UART_ACK_WAIT_MS (500u) /* 等 MCU UART ACK 超时(心跳/透传/0x14 等) */
#define MCU_APP_ACK_MAX_RETRY (3u)
#define MCU_PROTOCOL_TICK_MS (200u)
/* 杰理 os_sem_pend 超时单位是 OS tickOS_TICKS_PER_SEC=100 → 1 tick = 10ms */
#define MCU_PROTOCOL_TICK_OS ((MCU_PROTOCOL_TICK_MS + 9u) / 10u)
#define MCU_EVT_QUEUE_DEPTH (8u)
#define MCU_PROTOCOL_TASK_PRIO (31u)
#define MCU_PROTOCOL_TASK_STACK (2048u)
/*============================================================================*/
/* 私有类型 */
/*============================================================================*/
typedef struct
{
uint8_t active;
uint8_t sn;
uint8_t expect_ack;
uint8_t retry;
uint32_t send_time;
uint8_t buf[MCU_FRAME_MAX_LEN];
uint16_t len;
} McuPending_t;
typedef enum
{
MCU_EVT_UART_FRAME = 0,
MCU_EVT_APP_FRAME,
MCU_EVT_GATEWAY_ACK,
MCU_EVT_BLE_PAIRED,
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 sn;
uint8_t last_pushed_status;
uint8_t hb_fail_cnt;
uint8_t hb_waiting;
uint8_t mcu_comm_alarm;
uint32_t last_mcu_rx_ms;
uint32_t last_hb_tx_ms;
McuPending_t app_pending; /* APP→MCU 透传等待 */
McuPending_t ble_pending; /* BLE 主动请求等待(心跳/状态等) */
uint8_t report_waiting; /* 1=已转网关,等 ACK 后再回 MCU 0x08 */
uint8_t report_sn; /* 等待回 0x08 的 SN */
} McuProtocol_t;
typedef struct
{
McuEvt_t queue[MCU_EVT_QUEUE_DEPTH];
uint8_t head;
uint8_t tail;
uint8_t count;
OS_MUTEX lock;
OS_SEM sem;
} McuEvtQueue_t;
/*============================================================================*/
/* 私有变量 */
/*============================================================================*/
static McuProtocol_t s_proto;
static uint8_t s_parse_out[MCU_FRAME_MAX_LEN];
static volatile u8 s_protocol_busy = 0;
static McuEvtQueue_t s_evt_q;
/*============================================================================*/
/* 私有函数声明 */
/*============================================================================*/
static uint8_t mcu_protocol_next_sn(void);
static int32_t mcu_protocol_send_frame(uint8_t cmd, uint8_t sn,
const uint8_t *payload, uint16_t plen);
static void mcu_protocol_send_ack(uint8_t cmd, uint8_t sn, uint8_t result);
static void mcu_protocol_handle_frame(const uint8_t *frame, uint16_t len);
static void mcu_protocol_handle_pair_cfg(const uint8_t *frame, uint16_t len);
static void mcu_protocol_handle_report(const uint8_t *frame, uint16_t len);
static void mcu_protocol_handle_app_frame(const uint8_t *data, uint16_t len);
static void mcu_protocol_handle_gateway_ack(uint16_t cmd, uint8_t gateway_int_cnt,
int32_t gateway_int0);
static void mcu_protocol_try_heartbeat(void);
static void mcu_protocol_try_work_status(void);
static void mcu_protocol_check_app_pending(void);
static void mcu_protocol_check_ble_pending(void);
static void mcu_protocol_periodic(void);
static uint8_t mcu_protocol_post_event(const McuEvt_t *evt);
static uint8_t mcu_protocol_pop_event(McuEvt_t *evt);
static void mcu_protocol_dispatch_event(const McuEvt_t *evt);
static void mcu_protocol_task(void *arg);
/*============================================================================*/
/* 事件队列 */
/*============================================================================*/
/**
* @brief 投递事件并唤醒协议任务
* @param evt 事件
* @return 1=成功,0=队列满
*/
static uint8_t mcu_protocol_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=空
*/
static uint8_t mcu_protocol_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_protocol_task 上下文执行,s_proto 在此单一上下文内读写
*/
static void mcu_protocol_dispatch_event(const McuEvt_t *evt)
{
switch (evt->type)
{
case MCU_EVT_UART_FRAME:
mcu_protocol_handle_frame(evt->buf, evt->len);
break;
case MCU_EVT_APP_FRAME:
mcu_protocol_handle_app_frame(evt->buf, evt->len);
break;
case MCU_EVT_GATEWAY_ACK:
mcu_protocol_handle_gateway_ack(evt->gw_cmd, evt->gw_int_cnt, evt->gw_int0);
break;
case MCU_EVT_BLE_PAIRED:
mcu_business_on_paired();
break;
case MCU_EVT_BLE_PAIR_CLEARED:
mcu_business_on_pair_cleared();
break;
default:
log_error("unknown evt type=%d", evt->type);
break;
}
}
/**
* @brief 协议引擎主任务:事件驱动 + 兜底轮询
* @param arg 未使用
*/
static void mcu_protocol_task(void *arg)
{
McuEvt_t evt;
(void)arg;
log_info("protocol task start");
while (1)
{
os_sem_pend(&s_evt_q.sem, (int)MCU_PROTOCOL_TICK_OS);
s_protocol_busy = 1;
while (mcu_protocol_pop_event(&evt))
{
mcu_protocol_dispatch_event(&evt);
}
mcu_protocol_periodic();
s_protocol_busy = 0;
}
}
/**
* @brief 低功耗空闲查询:协议任务忙则不允许进 sleep
* @return 1=可睡,0=保持唤醒
*/
static u8 mcu_protocol_lp_idle_query(void)
{
return s_protocol_busy ? 0 : 1;
}
REGISTER_LP_TARGET(mcu_protocol_lp_target) = {
.name = "mcu_protocol",
.is_idle = mcu_protocol_lp_idle_query,
};
/*============================================================================*/
/* 发送 */
/*============================================================================*/
/**
* @brief 取下一个 SN
* @return SN
*/
static uint8_t mcu_protocol_next_sn(void)
{
return s_proto.sn++;
}
/**
* @brief 组帧发送
* @param cmd 命令
* @param sn 序列号
* @param payload 载荷
* @param plen 长度
* @return 帧长;<0 失败
*/
static int32_t mcu_protocol_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 自动 SN 发送(供 business 层调用)
* @param cmd 命令
* @param payload 载荷
* @param plen 长度
* @return 帧长;<0 失败
*/
int32_t mcu_protocol_send_cmd(uint8_t cmd, const uint8_t *payload, uint16_t plen)
{
return mcu_protocol_send_frame(cmd, mcu_protocol_next_sn(), payload, plen);
}
/**
* @brief 发送仅含 Result 的 ACK 帧
* @param cmd ACK 命令
* @param sn 序列号
* @param result 结果码
*/
static void mcu_protocol_send_ack(uint8_t cmd, uint8_t sn, uint8_t result)
{
mcu_protocol_send_frame(cmd, sn, &result, 1);
}
/**
* @brief 强制重推工作状态
*/
void mcu_protocol_invalidate_work_status_push(void)
{
s_proto.last_pushed_status = 0xFF;
}
/*============================================================================*/
/* 命令处理 */
/*============================================================================*/
/**
* @brief 处理 0x10:协议校验 + ACK,业务交给 business 层
* @param frame 完整帧
* @param len 长度
*/
static void mcu_protocol_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_protocol_send_ack(MCU_CMD_PAIR_CFG_ACK, sn, MCU_RESULT_PARAM_ERR);
return;
}
if (!mcu_business_is_identity_ready())
{
mcu_protocol_send_ack(MCU_CMD_PAIR_CFG_ACK, sn, MCU_RESULT_BUSY);
return;
}
mcu_protocol_send_ack(MCU_CMD_PAIR_CFG_ACK, sn, MCU_RESULT_OK);
mcu_business_on_pair_cfg(mode, timeout_s);
}
/**
* @brief 处理 MCU 上报 0x07:转网关,等网关 ACK 后再回 0x08
* @param frame 完整帧
* @param len 长度
* @note 不缓存重发、不超时回失败;MCU 自己会超时重发 0x07,
* 这里收到新的 0x07 会直接覆盖上一次等待中的 SN
*/
static void mcu_protocol_handle_report(const uint8_t *frame, uint16_t len)
{
uint8_t sn = frame[5];
s_proto.report_sn = sn;
s_proto.report_waiting = 1;
usr_le_send_mcu_frame((u8 *)frame, len);
log_debug("MCU report sn=%u -> gateway, wait ACK", sn);
}
/**
* @brief 分发 MCU 完整帧
* @param frame 完整帧
* @param len 长度
*/
static void mcu_protocol_handle_frame(const uint8_t *frame, uint16_t len)
{
uint8_t cmd;
uint8_t sn;
if (!frame || len < 6)
{
return;
}
s_proto.last_mcu_rx_ms = mcu_util_get_ms();
s_proto.hb_fail_cnt = 0;
s_proto.hb_waiting = 0;
s_proto.mcu_comm_alarm = 0;
cmd = frame[4];
sn = frame[5];
log_debug("MCU RX cmd=0x%02X sn=%u len=%u", cmd, sn, len);
if (s_proto.app_pending.active &&
(cmd == s_proto.app_pending.expect_ack) &&
(sn == s_proto.app_pending.sn))
{
s_proto.app_pending.active = 0;
usr_le_send_mcu_frame((u8 *)frame, len);
return;
}
if (s_proto.ble_pending.active &&
(cmd == s_proto.ble_pending.expect_ack) &&
(sn == s_proto.ble_pending.sn))
{
s_proto.ble_pending.active = 0;
if (cmd == MCU_CMD_HEARTBEAT_ACK)
{
s_proto.hb_waiting = 0;
s_proto.hb_fail_cnt = 0;
}
return;
}
switch (cmd)
{
case MCU_CMD_GET_IDENTITY_ACK:
mcu_business_on_identity_ack(frame, len);
break;
case MCU_CMD_PAIR_CFG:
mcu_protocol_handle_pair_cfg(frame, len);
break;
case MCU_CMD_REPORT:
mcu_protocol_handle_report(frame, len);
break;
case MCU_CMD_CONTROL_ACK:
case MCU_CMD_READ_ACK:
usr_le_send_mcu_frame((u8 *)frame, len);
break;
case MCU_CMD_HEARTBEAT_ACK:
case MCU_CMD_WORK_STATUS_ACK:
break;
default:
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_protocol_send_ack((uint8_t)(cmd + 1), sn, MCU_RESULT_NOT_SUPPORT);
}
break;
}
}
/**
* @brief 处理 APP 下发 55AA
* @param data 帧
* @param len 长度
*/
static void mcu_protocol_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
{
mcu_uart_send(data, len);
return;
}
if (s_proto.app_pending.active)
{
log_error("app frame busy, drop cmd=0x%02X", cmd);
return;
}
memcpy(s_proto.app_pending.buf, data, len);
s_proto.app_pending.len = len;
s_proto.app_pending.sn = sn;
s_proto.app_pending.expect_ack = expect_ack;
s_proto.app_pending.retry = 0;
s_proto.app_pending.send_time = mcu_util_get_ms();
s_proto.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 处理网关对 0x07 的应答
* @param cmd bleproto cmd
* @param gateway_int_cnt int 个数
* @param gateway_int0 int0
*/
static void mcu_protocol_handle_gateway_ack(uint16_t cmd,
uint8_t gateway_int_cnt,
int32_t gateway_int0)
{
uint8_t sn;
if (!s_proto.report_waiting)
{
return;
}
if (cmd == 0 && gateway_int_cnt == 1 && gateway_int0 == 0)
{
sn = s_proto.report_sn;
mcu_protocol_send_ack(MCU_CMD_REPORT_ACK, sn, MCU_RESULT_OK);
s_proto.report_waiting = 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);
}
/*============================================================================*/
/* 周期:心跳 / 状态推送 / pending 重试 */
/*============================================================================*/
/**
* @brief 心跳
*/
static void mcu_protocol_try_heartbeat(void)
{
uint32_t now = mcu_util_get_ms();
uint8_t sn;
uint8_t buf[MCU_FRAME_MAX_LEN];
int32_t total;
if (!mcu_business_is_identity_ready())
{
return;
}
if (s_proto.hb_waiting)
{
if ((now - s_proto.last_hb_tx_ms) <= MCU_UART_ACK_WAIT_MS)
{
return;
}
s_proto.hb_waiting = 0;
s_proto.ble_pending.active = 0;
s_proto.hb_fail_cnt++;
log_error("heartbeat timeout fail=%u", s_proto.hb_fail_cnt);
if (s_proto.hb_fail_cnt >= MCU_HEARTBEAT_MAX_RETRY)
{
s_proto.mcu_comm_alarm = 1;
log_error("MCU comm alarm");
s_proto.last_mcu_rx_ms = now;
return;
}
}
else if ((now - s_proto.last_mcu_rx_ms) < MCU_HEARTBEAT_IDLE_MS)
{
return;
}
sn = mcu_protocol_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_proto.ble_pending.active = 1;
s_proto.ble_pending.sn = sn;
s_proto.ble_pending.expect_ack = MCU_CMD_HEARTBEAT_ACK;
s_proto.ble_pending.send_time = now;
s_proto.ble_pending.retry = 0;
s_proto.last_hb_tx_ms = now;
s_proto.hb_waiting = 1;
log_debug("heartbeat tx sn=%u", sn);
}
/**
* @brief 工作状态变化时推送 0x14(状态值来自 business 层)
*/
static void mcu_protocol_try_work_status(void)
{
uint8_t payload[2];
uint8_t sn;
uint8_t buf[MCU_FRAME_MAX_LEN];
int32_t total;
uint32_t now;
uint8_t ws;
if (!mcu_business_is_identity_ready())
{
return;
}
ws = mcu_business_get_work_status();
if (ws == s_proto.last_pushed_status)
{
return;
}
if (s_proto.ble_pending.active)
{
return;
}
payload[0] = ws;
payload[1] = 0x00;
sn = mcu_protocol_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_proto.ble_pending.active = 1;
s_proto.ble_pending.sn = sn;
s_proto.ble_pending.expect_ack = MCU_CMD_WORK_STATUS_ACK;
s_proto.ble_pending.send_time = now;
s_proto.last_pushed_status = ws;
log_info("work_status push 0x%02X", ws);
}
/**
* @brief APP 透传超时重发;重试用尽后主动回 FAIL,不让 APP 干等
*/
static void mcu_protocol_check_app_pending(void)
{
uint32_t now;
uint8_t buf[MCU_FRAME_MAX_LEN];
uint8_t result;
int32_t total;
if (!s_proto.app_pending.active)
{
return;
}
now = mcu_util_get_ms();
if ((now - s_proto.app_pending.send_time) <= MCU_UART_ACK_WAIT_MS)
{
return;
}
if (s_proto.app_pending.retry >= MCU_APP_ACK_MAX_RETRY)
{
log_error("app pending give up cmd expect=0x%02X, notify APP FAIL",
s_proto.app_pending.expect_ack);
s_proto.app_pending.active = 0;
result = MCU_RESULT_FAIL;
total = mcu_frame_pack(buf, sizeof(buf), s_proto.app_pending.expect_ack,
s_proto.app_pending.sn, &result, 1);
if (total > 0)
{
usr_le_send_mcu_frame(buf, (uint16_t)total);
}
return;
}
mcu_uart_send(s_proto.app_pending.buf, s_proto.app_pending.len);
s_proto.app_pending.retry++;
s_proto.app_pending.send_time = now;
log_debug("app pending retry %u", s_proto.app_pending.retry);
}
/**
* @brief BLE 主动帧 pending 超时清理
*/
static void mcu_protocol_check_ble_pending(void)
{
uint32_t now;
if (!s_proto.ble_pending.active)
{
return;
}
if (s_proto.ble_pending.expect_ack == MCU_CMD_HEARTBEAT_ACK)
{
return;
}
now = mcu_util_get_ms();
if ((now - s_proto.ble_pending.send_time) > MCU_UART_ACK_WAIT_MS)
{
if (s_proto.ble_pending.expect_ack == MCU_CMD_WORK_STATUS_ACK)
{
log_error("work_status ack timeout, will retry");
s_proto.last_pushed_status = 0xFF;
}
s_proto.ble_pending.active = 0;
}
}
/**
* @brief 周期处理:串口防睡 / 业务周期 / pending 重试 / 心跳 / 状态推送
*/
static void mcu_protocol_periodic(void)
{
mcu_uart_rx_keep_awake_tick(); /* 串口防睡 */
mcu_business_periodic(); /* 业务周期 */
mcu_protocol_check_app_pending(); /* APP 透传超时重发;重试用尽后主动回 FAIL,不让 APP 干等 */
mcu_protocol_check_ble_pending(); /* BLE 主动帧 pending 超时清理 */
mcu_protocol_try_heartbeat(); /* 心跳 */
mcu_protocol_try_work_status(); /* 工作状态变化时推送 0x14(状态值来自 business 层) */
}
/*============================================================================*/
/* 公有入口 */
/*============================================================================*/
/**
* @brief 初始化协议引擎
* @return 1=成功,0=失败
*/
uint8_t mcu_protocol_init(void)
{
if (s_proto.initialized)
{
return 1;
}
memset(&s_proto, 0, sizeof(s_proto));
mcu_frame_parser_reset();
mcu_business_init();
if (mcu_uart_init() != MCU_UART_OK)
{
return 0;
}
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_proto.last_mcu_rx_ms = mcu_util_get_ms();
s_proto.last_pushed_status = 0xFF;
s_proto.initialized = 1;
os_task_create(mcu_protocol_task, NULL, MCU_PROTOCOL_TASK_PRIO,
MCU_PROTOCOL_TASK_STACK, 0, "mcu_bridge");
log_info("mcu_protocol_init ok");
return 1;
}
/**
* @brief UART RX 生产者
* @param data 数据
* @param len 长度
*/
void mcu_protocol_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_proto.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);
printf("UART valid data:");
printf_buf(evt.buf, evt.len);
mcu_protocol_post_event(&evt);
}
}
}
/**
* @brief APP 帧生产者
* @param data 帧
* @param len 长度
*/
void mcu_protocol_on_app_frame(const uint8_t *data, uint16_t len)
{
McuEvt_t evt;
if (!s_proto.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_protocol_post_event(&evt);
}
/**
* @brief 配网成功入队
*/
void mcu_protocol_notify_paired(void)
{
McuEvt_t evt;
memset(&evt, 0, sizeof(evt));
evt.type = MCU_EVT_BLE_PAIRED;
mcu_protocol_post_event(&evt);
}
/**
* @brief 配对清除入队
*/
void mcu_protocol_notify_pair_cleared(void)
{
McuEvt_t evt;
memset(&evt, 0, sizeof(evt));
evt.type = MCU_EVT_BLE_PAIR_CLEARED;
mcu_protocol_post_event(&evt);
}
/**
* @brief 网关上报应答入队
* @param cmd cmd
* @param gateway_int_cnt cnt
* @param gateway_int0 int0
*/
void mcu_protocol_notify_gateway_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_protocol_post_event(&evt);
}
@@ -0,0 +1,83 @@
/******************************************************************************
* @file mcu_protocol.h
* @brief MCU 接入协议引擎:事件队列 / 任务 / 命令分发 / ACK / 透传重传 / 心跳
* @author cyWu <1917507415@qq.com>
* @date 2026.08.12
* @version V1.0.0
* @history
* - V1.0.0, 2026.08.12, cyWu, 从 mcu_bridge.c 拆出,仅保留协议机制,
* 不含任何业务判断(身份/配网/连接状态见 ../business
******************************************************************************/
#ifndef __MCU_PROTOCOL_H__
#define __MCU_PROTOCOL_H__
#include <stdint.h>
#include "mcu_cmd.h"
/*============================================================================*/
/* 说明 */
/* 本模块只做协议机制:组帧收发、事件队列驱动的单一任务、命令分发、 */
/* ACK 与超时重传、心跳保活。所有业务判断(该不该配网/该发什么广播/ */
/* work_status 具体取值)一律转交 business 层,本模块不感知业务细节。 */
/*============================================================================*/
/**
* @brief 初始化协议引擎(UART + 事件队列 + 独立任务 + business 层)
* @return 1=成功,0=失败
*/
uint8_t mcu_protocol_init(void);
/**
* @brief UART 收到原始数据(由 mcu_uart 接收任务调用)
* @param data 数据
* @param len 长度
*/
void mcu_protocol_on_uart_rx(const uint8_t *data, uint16_t len);
/**
* @brief APP/网关经 BLE 下发的 55AA 帧
* @param data 帧数据
* @param len 长度
*/
void mcu_protocol_on_app_frame(const uint8_t *data, uint16_t len);
/**
* @brief 配网成功通知(入队,交由协议任务在自身上下文调用 business 层)
*/
void mcu_protocol_notify_paired(void);
/**
* @brief 配对信息被清除通知(入队)
*/
void mcu_protocol_notify_pair_cleared(void);
/**
* @brief 网关对 REPORTCMD_V2 主动上报的应答(入队)
* @param cmd bleproto 包 cmd
* @param gateway_int_cnt gateway_int 个数
* @param gateway_int0 gateway_int[0]
*/
void mcu_protocol_notify_gateway_ack(uint16_t cmd,
uint8_t gateway_int_cnt,
int32_t gateway_int0);
/*============================================================================*/
/* 供 business 层调用的协议发送接口 */
/*============================================================================*/
/**
* @brief 自动分配 SN 并发送一帧到 MCU
* @param cmd 命令字
* @param payload 载荷,可为 NULL
* @param plen 载荷长度
* @return 帧长;<0 失败
*/
int32_t mcu_protocol_send_cmd(uint8_t cmd, const uint8_t *payload, uint16_t plen);
/**
* @brief 强制下一轮重新推送 0x14(业务状态变化后调用)
*/
void mcu_protocol_invalidate_work_status_push(void);
#endif /* __MCU_PROTOCOL_H__ */
@@ -44,7 +44,7 @@ static OS_MUTEX s_tx_lock;
/* /*
* UART MCU_UART_RX_KEEP_AWAKE_TICKS * UART MCU_UART_RX_KEEP_AWAKE_TICKS
* mcu_bridge 0 LP target sleep * mcu_protocol 0 LP target sleep
*/ */
static volatile u8 s_usr_uart_recieve = 0; static volatile u8 s_usr_uart_recieve = 0;
@@ -25,7 +25,7 @@
#define MCU_UART_RX_PIN IO_PORTA_08 /**< MCU 通信 UART RXboard 唤醒口须一致) */ #define MCU_UART_RX_PIN IO_PORTA_08 /**< MCU 通信 UART RXboard 唤醒口须一致) */
/* /*
* sleep tick mcu_bridge 200ms * sleep tick mcu_protocol 200ms
* 3s * 3s
*/ */
#define MCU_UART_RX_KEEP_AWAKE_TICKS (15u) #define MCU_UART_RX_KEEP_AWAKE_TICKS (15u)