Files

1799 lines
51 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @file fc41d_ble.c
* @brief FC41D 蓝牙调试完整实现:AT 收发、模组电源、BLE 状态机、DIP 选路、comm_link 后端
*
* 模块内部分为四段(均为 static 或本文件内可见):
* 1. AT 命令层 — UART7 与 FC41D 对话,解析 +QBLESTAT URC
* 2. 硬件控制 — PE6(FC41D_RST) 复位脉冲与释放,非电源使能
* 3. BLE 状态机 — 外设初始化、等待连接、QBLETRANMODE 透传数据交互
* 4. 对外接口 — ble_link_* / comm_link_ble_* 供 tcp_ser_task、comm_link 调用
*/
#include "publicdata/public_define.h"
#if BLE_DEBUG_EN
#include "fc41d_ble.h"
#include "fc41d_ble_priv.h"
#include "debug_link.h"
#if TCP_DEBUG_EN
#include "debug_files.h"
#include "tcp_protocol.h"
#endif
#include "gpio.h"
#include "usart.h"
#include "sys_drv_init.h"
#include "publicdata/publicdata.h"
#include "app_init/app_init.h"
#include "sub_comm/sub_comm_task.h"
#include "wdt_task/wdt_task.h"
#include "FreeRTOS.h"
#include "gd32h7xx.h"
#include <string.h>
#include <stdio.h>
/* UART7FC41D AT)专用:不扩展公共 usart 驱动,逻辑限定在本文件 */
static void s_fc41d_uart_init(void)
{
rcu_periph_clock_enable(RCU_GPIOE);
rcu_periph_clock_enable(RCU_UART7);
gpio_af_set(GPIOE, GPIO_AF_8, GPIO_PIN_1);
gpio_mode_set(GPIOE, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_1);
gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_1);
gpio_af_set(GPIOE, GPIO_AF_8, GPIO_PIN_0);
gpio_mode_set(GPIOE, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_0);
gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_0);
usart_deinit(UART7);
usart_word_length_set(UART7, USART_WL_8BIT);
usart_stop_bit_set(UART7, USART_STB_1BIT);
usart_parity_config(UART7, USART_PM_NONE);
usart_baudrate_set(UART7, FC41D_UART_BAUD);
usart_hardware_flow_rts_config(UART7, USART_RTS_DISABLE);
usart_hardware_flow_cts_config(UART7, USART_CTS_DISABLE);
usart_receive_config(UART7, USART_RECEIVE_ENABLE);
usart_transmit_config(UART7, USART_TRANSMIT_ENABLE);
usart_interrupt_enable(UART7, USART_INT_RBNE);
usart_enable(UART7);
}
static void s_fc41d_uart_discard(void)
{
uint8_t scrap[64];
while (SET == usart_flag_get(UART7, USART_FLAG_RBNE)) {
(void)usart_data_receive(UART7);
}
for (;;) {
int n = v_usart_read_rx_buffer(FC41D_UART_HW_NUM, scrap, (uint16_t)sizeof(scrap));
if (n <= 0) {
break;
}
}
}
static uint16_t s_fc41d_uart_drain_rx(uint8_t *buf, uint16_t cap)
{
uint16_t total = 0U;
if (buf == NULL || cap == 0U) {
return 0U;
}
while (total < cap && (SET == usart_flag_get(UART7, USART_FLAG_RBNE))) {
buf[total++] = (uint8_t)usart_data_receive(UART7);
}
{
int got = v_usart_read_rx_buffer(FC41D_UART_HW_NUM, &buf[total], (uint16_t)(cap - total));
if (got > 0) {
total += (uint16_t)got;
}
}
return total;
}
#define FC41D_LINK_CAP() (BLE->io.link_rx_acc_cap)
#define FC41D_APP_CAP() (BLE->io.app_rx_cap)
/* =============================================================================
* 模块常量与上下文宏(状态封装在 comm_link_ctx.backend.ble / session
* ============================================================================= */
/** 单次 QBLEGATTSNTFY 最大载荷(字节) */
#define FC41D_GATT_TX_CHUNK 400U
#define SESS (comm_link_session())
#define BLE (&(comm_link_ctx_get()->backend.ble))
/* =============================================================================
* AT 命令层 — 静态函数
* ============================================================================= */
static void v_fc41d_parse_qble_recv(uint8_t *buf, uint16_t *p_len);
static void v_at_uart_read_all(void);
static void v_at_uart_discard(void);
static void v_at_uart_drain_payload(void);
static int s_fc41d_resp_has_ok(const uint8_t *buf, uint16_t len);
static int s_fc41d_link_or_at_has_ok(void);
static void s_ble_drain_uart_before_gatt(void);
#if TCP_DEBUG_EN
static void s_ble_log_json_io(const char *dir, const uint8_t *data, uint32_t len);
#endif
static void v_at_set_transparent(uint8_t on);
static uint8_t u8_at_is_transparent(void);
static int s_ble_enter_transparent_blocking(void);
static int s_ble_exit_transparent_blocking(void);
/**
* @brief 将 UART 读到的原始字节追加到 BLE->at.rx_acc
* @param data 数据指针
* @param len 字节数;为 0 或 data 为 NULL 时不操作
* @note 缓冲满时从头覆盖,避免长时间运行撑爆内存
*/
static void v_at_append_rx(const uint8_t *data, uint16_t len)
{
uint16_t i;
if ((data == NULL) || (len == 0U)) {
return;
}
for (i = 0U; i < len; i++) {
if (BLE->at.rx_acc_len >= (FC41D_AT_RX_ACC_SIZE - 1U)) {
BLE->at.rx_acc_len = 0U;
}
BLE->at.rx_acc[BLE->at.rx_acc_len++] = (char)data[i];
BLE->at.rx_acc[BLE->at.rx_acc_len] = '\0';
}
}
static void v_fc41d_shift_rx(uint8_t *buf, uint16_t *p_len, uint16_t consume)
{
uint16_t len = *p_len;
if (consume >= len) {
*p_len = 0U;
return;
}
memmove(buf, &buf[consume], (size_t)(len - consume));
*p_len = (uint16_t)(len - consume);
}
static void s_app_rx_clear(void)
{
BLE->io.app_rx_wr = 0U;
BLE->io.app_rx_rd = 0U;
BLE->io.qble_payload_need = 0U;
BLE->io.link_rx_acc_len = 0U;
}
static void s_ble_io_free(void)
{
if (BLE->io.link_rx_acc != NULL) {
vPortFree(BLE->io.link_rx_acc);
BLE->io.link_rx_acc = NULL;
}
if (BLE->io.app_rx != NULL) {
vPortFree(BLE->io.app_rx);
BLE->io.app_rx = NULL;
}
BLE->io.link_rx_acc_cap = 0U;
BLE->io.app_rx_cap = 0U;
s_app_rx_clear();
}
static int s_ble_io_set_caps(uint16_t link_cap, uint16_t app_cap)
{
if ((link_cap == 0U) || (app_cap == 0U)) {
return -1;
}
if ((BLE->io.link_rx_acc != NULL) && (BLE->io.link_rx_acc_cap == link_cap) &&
(BLE->io.app_rx != NULL) && (BLE->io.app_rx_cap == app_cap)) {
s_app_rx_clear();
return 0;
}
s_ble_io_free();
BLE->io.link_rx_acc = (uint8_t *)pvPortMalloc((size_t)link_cap);
BLE->io.app_rx = (uint8_t *)pvPortMalloc((size_t)app_cap);
if ((BLE->io.link_rx_acc == NULL) || (BLE->io.app_rx == NULL)) {
s_ble_io_free();
BT_PRINT("BLE RX buf alloc fail link=%u app=%u\r\n",
(unsigned)link_cap, (unsigned)app_cap);
return -1;
}
BLE->io.link_rx_acc_cap = link_cap;
BLE->io.app_rx_cap = app_cap;
s_app_rx_clear();
return 0;
}
#if TCP_DEBUG_EN
static int s_ble_diag_trace(void)
{
return (system_filetransfer_is_active() != 0) ? 1 : 0;
}
int comm_link_ble_diag_trace(void)
{
return s_ble_diag_trace();
}
static void s_ble_log_json_io(const char *dir, const uint8_t *data, uint32_t len)
{
uint32_t off = 0U;
enum { kChunk = 256U };
if ((dir == NULL) || (data == NULL) || (len == 0U)) {
return;
}
printf("[BLE-JSON] %s (%u): ", dir, (unsigned)len);
if (len <= kChunk) {
printf("%.*s\r\n", (int)len, (const char *)data);
return;
}
while (off < len) {
uint32_t n = len - off;
if (n > kChunk) {
n = kChunk;
}
printf("%.*s", (int)n, (const char *)(data + off));
off += n;
}
printf("\r\n");
}
static uint16_t s_app_rx_pending(void)
{
uint16_t cap = FC41D_APP_CAP();
uint16_t rd = BLE->io.app_rx_rd;
uint16_t wr = BLE->io.app_rx_wr;
if (cap == 0U) {
return 0U;
}
if (wr >= rd) {
return (uint16_t)(wr - rd);
}
return (uint16_t)(cap - rd + wr);
}
static void s_fw_log_link_cache(const char *when)
{
uint16_t len;
uint16_t app_pending;
if (s_ble_diag_trace() == 0) {
return;
}
if (BLE->io.link_rx_acc == NULL) {
return;
}
len = BLE->io.link_rx_acc_len;
app_pending = s_app_rx_pending();
if ((len == 0U) && (app_pending == 0U) && (BLE->io.qble_payload_need == 0U)) {
return;
}
printf("[FW_UPGRADE] LINK-CACHE@%s link_len=%u need=%u link_cap=%u app_pending=%u\r\n",
when,
(unsigned)len,
(unsigned)BLE->io.qble_payload_need,
(unsigned)FC41D_LINK_CAP(),
(unsigned)app_pending);
if (len > 0U) {
tcp_firmware_upgrade_log_buf("LINK-CACHE-DATA", BLE->io.link_rx_acc, (uint32_t)len);
}
}
#endif
static int s_app_rx_push(const uint8_t *data, uint16_t len)
{
uint16_t i;
uint16_t cap = FC41D_APP_CAP();
if ((data == NULL) || (len == 0U) || (BLE->io.app_rx == NULL) || (cap == 0U)) {
return 0;
}
for (i = 0U; i < len; i++) {
uint16_t next = (uint16_t)((BLE->io.app_rx_wr + 1U) % cap);
if (next == BLE->io.app_rx_rd) {
BT_PRINT("app_rx overflow, lost %u bytes\r\n",
(unsigned)(len - i));
break;
}
BLE->io.app_rx[BLE->io.app_rx_wr] = data[i];
BLE->io.app_rx_wr = next;
}
#if TCP_DEBUG_EN
if ((s_ble_diag_trace() != 0) && (i > 0U)) {
tcp_firmware_upgrade_log_buf("APP-PUSH", data, (uint32_t)i);
s_fw_log_link_cache("after-app-push");
}
#endif
return (int)i;
}
static int s_app_rx_pop(uint8_t *buf, uint32_t cap_out)
{
uint32_t n = 0U;
uint16_t cap = FC41D_APP_CAP();
if ((buf == NULL) || (cap_out == 0U) || (BLE->io.app_rx == NULL) || (cap == 0U)) {
return 0;
}
while ((n < cap_out) && (BLE->io.app_rx_rd != BLE->io.app_rx_wr)) {
buf[n++] = BLE->io.app_rx[BLE->io.app_rx_rd];
BLE->io.app_rx_rd = (uint16_t)((BLE->io.app_rx_rd + 1U) % cap);
}
return (int)n;
}
static void v_at_set_transparent(uint8_t on)
{
BLE->sm.transparent = (on != 0U) ? 1U : 0U;
if (on != 0U) {
v_at_uart_discard();
s_app_rx_clear();
BT_PRINT("Enter QBLETRANMODE uart passthrough\r\n");
} else {
BT_PRINT("Leave uart passthrough\r\n");
}
}
static uint8_t u8_at_is_transparent(void)
{
return BLE->sm.transparent;
}
/** 透传模式:UART 载荷直接写入 app_rx 环 */
static void v_at_uart_drain_payload(void)
{
uint8_t chunk[128];
uint16_t n;
if (u8_at_is_transparent() == 0U) {
return;
}
do {
n = s_fc41d_uart_drain_rx(chunk, sizeof(chunk));
if (n > 0U) {
#if TCP_DEBUG_EN
if (s_ble_diag_trace() != 0) {
tcp_firmware_upgrade_log_buf("UART-RX", chunk, (uint32_t)n);
}
#endif
(void)s_app_rx_push(chunk, n);
}
} while (n == (uint16_t)sizeof(chunk));
}
/**
* @brief 排空 UART 到 link 解析器(保留 +QBLERECV 应用数据,不清硬件 FIFO 中业务载荷)
*/
static void v_at_uart_drain_link(void)
{
uint8_t round;
for (round = 0U; round < 4U; round++) {
v_at_uart_read_all();
}
}
/**
* @brief 在 link 缓冲内对齐到下一帧 +QBLERECV(仅帧头对齐,禁止 strstr 全缓冲搜索)
*/
static void v_fc41d_qble_resync(uint8_t *buf, uint16_t *p_len)
{
uint16_t len = *p_len;
uint16_t i;
if (len < 10U) {
return;
}
if (strncmp((char *)buf, "+QBLERECV:", 10) == 0) {
return;
}
for (i = 1U; i + 10U <= len; i++) {
if (strncmp((char *)&buf[i], "+QBLERECV:", 10) == 0) {
v_fc41d_shift_rx(buf, p_len, i);
return;
}
}
if (len > (FC41D_LINK_CAP() / 2U)) {
v_fc41d_shift_rx(buf, p_len, (uint16_t)(len - 64U));
}
}
/**
* @brief 在缓冲区内查找 +QBLERECV 帧头(续接载荷时避免吞掉下一帧)
* @return 帧头偏移,未找到 -1
*/
static int s_fc41d_qble_header_at(const uint8_t *buf, uint16_t len)
{
uint16_t i;
if ((buf == NULL) || (len < 10U)) {
return -1;
}
for (i = 0U; i + 10U <= len; i++) {
if (strncmp((char *)&buf[i], "+QBLERECV:", 10) == 0) {
return (int)i;
}
}
return -1;
}
/** @return 1 缓冲开头为 +QBLERECV: 的前缀(UART 分片头行) */
static int s_fc41d_is_qble_header_prefix(const uint8_t *buf, uint16_t len)
{
static const char k_hdr[] = "+QBLERECV:";
uint16_t i;
if ((buf == NULL) || (len == 0U)) {
return 0;
}
for (i = 0U; i < len; i++) {
if (i >= (sizeof(k_hdr) - 1U)) {
return 0;
}
if (buf[i] != (uint8_t)k_hdr[i]) {
return 0;
}
}
return 1;
}
/** @return 1 缓冲区内疑似 +QBLERECV AT 头行(含分片,禁止写入 app_rx) */
static int s_fc41d_looks_like_qble_at_text(const uint8_t *buf, uint16_t len)
{
uint16_t scan;
if ((buf == NULL) || (len == 0U)) {
return 0;
}
if (s_fc41d_is_qble_header_prefix(buf, len)) {
return 1;
}
if (len >= 7U && memcmp(buf, "BLERECV", 7) == 0) {
return 1;
}
if (len >= 6U && memcmp(buf, "LERECV", 6) == 0) {
return 1;
}
if (len >= 5U && memcmp(buf, "RECV:", 5) == 0) {
return 1;
}
if (len >= 4U && memcmp(buf, "ECV:", 4) == 0) {
return 1;
}
if (len >= 2U && buf[0] == (uint8_t)'V' && buf[1] == (uint8_t)':') {
return 1;
}
for (scan = 0U; (scan + 7U) <= len && scan < 64U; scan++) {
if (memcmp(&buf[scan], "BLERECV", 7) == 0) {
return 1;
}
if ((scan + 5U) <= len && memcmp(&buf[scan], "FFF2,", 5) == 0) {
return 1;
}
}
return 0;
}
/** @return 1 缓冲含 AT 头行 CRLFGATT JSON 载荷内不应出现裸 CRLF */
static int s_fc41d_buf_has_at_crlf(const uint8_t *buf, uint16_t len)
{
uint16_t i;
if ((buf == NULL) || (len < 2U)) {
return 0;
}
for (i = 0U; i + 1U < len && i < 80U; i++) {
if (buf[i] == (uint8_t)'\r' && buf[i + 1U] == (uint8_t)'\n') {
return 1;
}
}
return 0;
}
/** @return 1 无 +QBLERECV 包裹的 JSON/Base64 续接块(UART 拆包遗留) */
static int s_fc41d_is_orphan_app_payload(const uint8_t *buf, uint16_t len)
{
uint8_t c;
if ((buf == NULL) || (len == 0U)) {
return 0;
}
c = buf[0];
if (c == (uint8_t)'{') {
return 1;
}
if ((c >= (uint8_t)'A' && c <= (uint8_t)'Z') ||
(c >= (uint8_t)'a' && c <= (uint8_t)'z') ||
(c >= (uint8_t)'0' && c <= (uint8_t)'9') ||
(c == (uint8_t)'=') || (c == (uint8_t)'+') || (c == (uint8_t)'/')) {
return (s_fc41d_looks_like_qble_at_text(buf, len) == 0) ? 1 : 0;
}
return 0;
}
static void v_fc41d_drain_orphan_app_payload(uint8_t *buf, uint16_t *p_len)
{
uint16_t len = *p_len;
if (s_ble_diag_trace() == 0) {
return;
}
if ((len == 0U) || (BLE->io.qble_payload_need > 0U)) {
return;
}
if (buf[0] == (uint8_t)'+') {
return;
}
if (s_fc41d_looks_like_qble_at_text(buf, len)) {
return;
}
if (s_fc41d_qble_header_at(buf, len) >= 0) {
return;
}
if (s_fc41d_is_orphan_app_payload(buf, len) == 0) {
return;
}
(void)s_app_rx_push(buf, len);
v_fc41d_shift_rx(buf, p_len, len);
}
/**
* @brief 续接上一帧未收齐的 +QBLERECV 二进制载荷
*/
static void v_fc41d_qble_payload_continue(uint8_t *buf, uint16_t *p_len)
{
uint16_t len = *p_len;
uint16_t take;
int hdr;
if (BLE->io.qble_payload_need == 0U || len == 0U) {
return;
}
hdr = s_fc41d_qble_header_at(buf, len);
if (hdr == 0) {
/* 下一帧 +QBLERECV 已到(多次 Write 或 UART 对齐),放弃残缺续接 */
BLE->io.qble_payload_need = 0U;
return;
}
if (hdr < 0) {
/* UART 分片到达 +QBLERECV 头行时勿当作 GATT 载荷 */
if (s_fc41d_looks_like_qble_at_text(buf, len) ||
s_fc41d_buf_has_at_crlf(buf, len)) {
BLE->io.qble_payload_need = 0U;
return;
}
}
take = len;
if (hdr > 0) {
take = (uint16_t)hdr;
}
if (take > BLE->io.qble_payload_need) {
take = BLE->io.qble_payload_need;
}
if (take == 0U) {
return;
}
(void)s_app_rx_push(buf, take);
BLE->io.qble_payload_need = (uint16_t)(BLE->io.qble_payload_need - take);
v_fc41d_shift_rx(buf, p_len, take);
}
/**
* @brief 解析缓冲开头的 +QBLERECV 帧(头行 + 定长载荷);禁止在 Base64 载荷中误匹配
* @return 1 消费了一帧或坏头行,0 等待更多数据
*/
static int v_fc41d_try_parse_qble_recv_at_start(uint8_t *buf, uint16_t *p_len)
{
char *base = (char *)buf;
char *eol = NULL;
int role = 0;
int data_len = 0;
char uuid[12] = {0};
uint16_t len = *p_len;
uint16_t payload_off;
uint16_t payload_in_buf;
if (len < 12U || strncmp(base, "+QBLERECV:", 10) != 0) {
return 0;
}
for (uint16_t i = 10U; i + 1U < len; i++) {
if (base[i] == '\r' && base[i + 1U] == '\n') {
eol = &base[i];
break;
}
}
if (eol == NULL) {
return 0;
}
if (sscanf(base, "+QBLERECV:%d,%11[^,],%d", &role, uuid, &data_len) < 3 ||
data_len <= 0) {
v_fc41d_shift_rx(buf, p_len, (uint16_t)((eol + 2) - base));
return 1;
}
payload_off = (uint16_t)((eol + 2) - base);
payload_in_buf = (len > payload_off) ? (uint16_t)(len - payload_off) : 0U;
if (payload_in_buf >= (uint16_t)data_len) {
(void)s_app_rx_push((const uint8_t *)&buf[payload_off], (uint16_t)data_len);
v_fc41d_shift_rx(buf, p_len, (uint16_t)(payload_off + (uint16_t)data_len));
BLE->io.qble_payload_need = 0U;
#if TCP_DEBUG_EN
if (s_ble_diag_trace() != 0) {
printf("[FW_UPGRADE] QBLE full uuid=%s data_len=%d pushed=%d\r\n",
uuid, data_len, data_len);
}
#endif
} else {
if (payload_in_buf > 0U) {
(void)s_app_rx_push((const uint8_t *)&buf[payload_off], payload_in_buf);
}
if ((payload_off + payload_in_buf) < len &&
s_fc41d_qble_header_at((const uint8_t *)&buf[payload_off + payload_in_buf],
(uint16_t)(len - payload_off - payload_in_buf)) >= 0) {
/* 下一 +QBLERECV 已到达:按多帧拼接 JSON,勿等待本帧 data_len 填满 */
BLE->io.qble_payload_need = 0U;
v_fc41d_shift_rx(buf, p_len, (uint16_t)(payload_off + payload_in_buf));
} else if (payload_in_buf == 0U) {
/* 仅收到头行,等待后续纯二进制载荷 */
BLE->io.qble_payload_need = (uint16_t)data_len;
v_fc41d_shift_rx(buf, p_len, payload_off);
#if TCP_DEBUG_EN
if (s_ble_diag_trace() != 0) {
printf("[FW_UPGRADE] QBLE hdr-only uuid=%s data_len=%d need=%u\r\n",
uuid, data_len, (unsigned)BLE->io.qble_payload_need);
}
#endif
} else {
/* 同一 +QBLERECV 帧 UART 分片,续接剩余 payload */
BLE->io.qble_payload_need = (uint16_t)((uint16_t)data_len - payload_in_buf);
v_fc41d_shift_rx(buf, p_len, len);
#if TCP_DEBUG_EN
if (s_ble_diag_trace() != 0) {
printf("[FW_UPGRADE] QBLE uart-split uuid=%s data_len=%d in_buf=%u need=%u\r\n",
uuid, data_len, (unsigned)payload_in_buf,
(unsigned)BLE->io.qble_payload_need);
}
#endif
}
}
(void)role;
return 1;
}
static void v_fc41d_parse_qble_recv(uint8_t *buf, uint16_t *p_len)
{
while ((BLE->io.qble_payload_need == 0U) &&
(*p_len > 0U) &&
(v_fc41d_try_parse_qble_recv_at_start(buf, p_len) != 0)) {
}
}
static void v_fc41d_parse_link_rx(void)
{
uint16_t len;
uint16_t pos = 0U;
if ((BLE->io.link_rx_acc == NULL) || (FC41D_LINK_CAP() == 0U)) {
return;
}
if (BLE->io.qble_payload_need == 0U) {
v_fc41d_qble_resync(BLE->io.link_rx_acc, &BLE->io.link_rx_acc_len);
} else if (s_fc41d_looks_like_qble_at_text(BLE->io.link_rx_acc, BLE->io.link_rx_acc_len)) {
BLE->io.qble_payload_need = 0U;
}
v_fc41d_parse_qble_recv(BLE->io.link_rx_acc, &BLE->io.link_rx_acc_len);
v_fc41d_qble_payload_continue(BLE->io.link_rx_acc, &BLE->io.link_rx_acc_len);
if (BLE->io.qble_payload_need > 0U) {
if (s_fc41d_looks_like_qble_at_text(BLE->io.link_rx_acc, BLE->io.link_rx_acc_len)) {
BLE->io.qble_payload_need = 0U;
v_fc41d_parse_qble_recv(BLE->io.link_rx_acc, &BLE->io.link_rx_acc_len);
} else {
#if TCP_DEBUG_EN
if (s_ble_diag_trace() != 0) {
printf("[FW_UPGRADE] LINK-WAIT need=%u len=%u\r\n",
(unsigned)BLE->io.qble_payload_need,
(unsigned)BLE->io.link_rx_acc_len);
s_fw_log_link_cache("link-wait");
}
#endif
return;
}
}
len = BLE->io.link_rx_acc_len;
pos = 0U;
while (pos < len) {
uint16_t line_end = pos;
int found_eol = 0;
while (line_end + 1U < len) {
if ((BLE->io.link_rx_acc[line_end] == '\r') &&
(BLE->io.link_rx_acc[line_end + 1U] == '\n')) {
found_eol = 1;
break;
}
line_end++;
}
if (found_eol == 0) {
break;
}
{
char line[192];
uint16_t line_len = (uint16_t)(line_end - pos);
if (line_len >= (uint16_t)sizeof(line)) {
line_len = (uint16_t)(sizeof(line) - 1U);
}
memcpy(line, &BLE->io.link_rx_acc[pos], line_len);
line[line_len] = '\0';
if (strncmp(line, "+QBLERECV:", 10) == 0) {
break;
}
if (strncmp(line, "+QBLESTAT", 9) == 0) {
if (strstr(line, "DISCONNECTED") != NULL) {
BLE->at.pending_urc = FC41D_BLE_URC_DISCONNECTED;
BT_PRINT("URC: BLE DISCONNECTED (%s)\r\n", line);
} else if (strstr(line, "CONNECTED") != NULL) {
BLE->at.pending_urc = FC41D_BLE_URC_CONNECTED;
BT_PRINT("URC: BLE CONNECTED (%s)\r\n", line);
}
} else if (strncmp(line, "+QBLEPEER", 9) == 0) {
BLE->at.pending_urc = FC41D_BLE_URC_CONNECTED;
BT_PRINT("URC: BLE CONNECTED (%s)\r\n", line);
}
v_at_append_rx((const uint8_t *)line, line_len);
v_at_append_rx((const uint8_t *)"\r\n", 2U);
}
pos = (uint16_t)(line_end + 2U);
}
if (pos > 0U) {
v_fc41d_shift_rx(BLE->io.link_rx_acc, &BLE->io.link_rx_acc_len, pos);
}
if (system_filetransfer_is_active() != 0) {
v_fc41d_drain_orphan_app_payload(BLE->io.link_rx_acc, &BLE->io.link_rx_acc_len);
}
v_fc41d_parse_qble_recv(BLE->io.link_rx_acc, &BLE->io.link_rx_acc_len);
#if TCP_DEBUG_EN
if (s_ble_diag_trace() != 0) {
s_fw_log_link_cache("parse-done");
}
#endif
}
static void v_fc41d_link_rx_feed(const uint8_t *data, uint16_t len)
{
uint16_t i;
uint16_t link_cap = FC41D_LINK_CAP();
if ((data == NULL) || (len == 0U) || (BLE->io.link_rx_acc == NULL) || (link_cap == 0U)) {
return;
}
for (i = 0U; i < len; i++) {
if (BLE->io.link_rx_acc_len >= link_cap) {
v_fc41d_parse_link_rx();
link_cap = FC41D_LINK_CAP();
if (BLE->io.link_rx_acc_len >= link_cap) {
if (BLE->io.qble_payload_need > 0U) {
BT_PRINT("link_rx full, reset qble_payload_need\r\n");
BLE->io.qble_payload_need = 0U;
v_fc41d_qble_resync(BLE->io.link_rx_acc, &BLE->io.link_rx_acc_len);
} else {
v_fc41d_shift_rx(BLE->io.link_rx_acc, &BLE->io.link_rx_acc_len,
(uint16_t)(link_cap / 2U));
v_fc41d_qble_resync(BLE->io.link_rx_acc, &BLE->io.link_rx_acc_len);
}
link_cap = FC41D_LINK_CAP();
}
}
BLE->io.link_rx_acc[BLE->io.link_rx_acc_len++] = data[i];
}
v_fc41d_parse_link_rx();
}
static void v_at_uart_drain_link(void)
{
if (u8_at_is_transparent() != 0U) {
v_at_uart_drain_payload();
return;
}
v_at_uart_read_all();
}
/**
* @brief 排空 UART7 接收(AT 阶段解析 URC;透传阶段写入 app_rx)
*/
static void v_at_uart_read_all(void)
{
uint8_t chunk[128];
uint16_t n;
if (u8_at_is_transparent() != 0U) {
v_at_uart_drain_payload();
return;
}
do {
n = s_fc41d_uart_drain_rx(chunk, sizeof(chunk));
if (n > 0U) {
#if TCP_DEBUG_EN
if (s_ble_diag_trace() != 0) {
tcp_firmware_upgrade_log_buf("UART-RX", chunk, (uint32_t)n);
}
#endif
v_fc41d_link_rx_feed(chunk, n);
}
} while (n == (uint16_t)sizeof(chunk));
}
/**
* @brief 复位 AT 层接收状态与 URC 队列(模组重启或停止 BLE 时调用)
*/
static void v_at_init(void)
{
BLE->at.rx_acc_len = 0U;
BLE->at.rx_acc[0] = '\0';
BLE->io.link_rx_acc_len = 0U;
BLE->io.qble_payload_need = 0U;
BLE->at.pending_urc = FC41D_BLE_URC_NONE;
BLE->sm.transparent = 0U;
BLE->sm.gatt_fallback = 0U;
s_app_rx_clear();
}
/**
* @brief 清空 UART 硬件 FIFO 及 BLE->at.rx_acc,避免旧应答干扰下一条 AT
* @note 最多丢弃 4096 字节,防止死循环
*/
static void v_at_uart_discard(void)
{
uint8_t scrap[64];
uint32_t total = 0U;
s_fc41d_uart_discard();
while (total < 4096U) {
uint16_t n = s_fc41d_uart_drain_rx(scrap, sizeof(scrap));
if (n == 0U) {
break;
}
total += n;
}
BLE->at.rx_acc_len = 0U;
BLE->at.rx_acc[0] = '\0';
BLE->io.link_rx_acc_len = 0U;
}
/**
* @brief 将 AT 命令行格式化为完整发送串(含 AT 前缀与 \\r\\n)
* @return 格式化长度,失败返回 <=0
*/
static int s_at_format_cmd(const char *at_line, char *out, size_t out_sz)
{
int n;
if ((at_line == NULL) || (out == NULL) || (out_sz == 0U)) {
return -1;
}
if (strncmp(at_line, "AT", 2) == 0) {
n = snprintf(out, out_sz, "%s\r\n", at_line);
} else if (at_line[0] == '+') {
n = snprintf(out, out_sz, "AT%s\r\n", at_line);
} else {
n = snprintf(out, out_sz, "AT+%s\r\n", at_line);
}
if ((n <= 0) || ((size_t)n >= out_sz)) {
return -1;
}
return n;
}
/**
* @brief 阻塞等待当前 AT 应答 OK(同时解析 +QBLERECV 写入应用缓冲)
*/
static int s_at_blocking_wait_ok(uint32_t wait_ms)
{
uint64_t deadline_ms = u64_get_current_millis() + (uint64_t)wait_ms;
uint32_t poll_ms = 20U;
#if TCP_DEBUG_EN
if (system_filetransfer_is_active() != 0) {
poll_ms = 5U;
}
#endif
while (u64_get_current_millis() < deadline_ms) {
uint8_t chunk[128];
uint16_t got = s_fc41d_uart_drain_rx(chunk, sizeof(chunk));
if (got > 0U) {
#if TCP_DEBUG_EN
if (s_ble_diag_trace() != 0) {
tcp_firmware_upgrade_log_buf("UART-RX-AT", chunk, (uint32_t)got);
}
#endif
v_fc41d_link_rx_feed(chunk, got);
}
if (s_fc41d_link_or_at_has_ok() != 0) {
return 0;
}
if (strstr(BLE->at.rx_acc, "+QBLEMTU:") != NULL) {
return 0;
}
if (strstr(BLE->at.rx_acc, "ERROR") != NULL) {
return -1;
}
mSleep(poll_ms);
v_wdt_setFlag(TASK_ID_TcpSer);
}
return -1;
}
/**
* @brief 阻塞执行一条 AT 命令
* @return 0 成功,-1 失败
*/
static int s_at_exec_blocking_line(const char *at_line, uint32_t wait_ms)
{
char cmd[FC41D_AT_LINE_MAX];
int cmd_len;
if (at_line == NULL) {
return -1;
}
cmd_len = s_at_format_cmd(at_line, cmd, sizeof(cmd));
if (cmd_len <= 0) {
return -1;
}
BLE->at.rx_acc_len = 0U;
BLE->at.rx_acc[0] = '\0';
v_at_uart_drain_link();
BT_PRINT("AT>> %s", cmd);
if (u16_usart_send(FC41D_USART_ID, (uint8_t *)cmd, (uint16_t)cmd_len) == 0U) {
BT_PRINT("AT send failed\r\n");
return -1;
}
if (s_at_blocking_wait_ok(wait_ms) != 0) {
BT_PRINT("AT timeout blocking (rx_len=%u)\r\n", (unsigned)BLE->at.rx_acc_len);
if (BLE->at.rx_acc_len > 0U) {
BT_PRINT("AT rx: %s\r\n", BLE->at.rx_acc);
}
return -1;
}
return 0;
}
/**
* @brief 取走并清零一条待处理的 BLE URCCONNECTED/DISCONNECTED
* @note 顺带读 UART 并解析 URC / +QBLERECV
*/
static fc41d_ble_urc_t s_at_poll_urc(void)
{
fc41d_ble_urc_t ev;
if (u8_at_is_transparent() == 0U) {
v_at_uart_read_all();
}
ev = BLE->at.pending_urc;
BLE->at.pending_urc = FC41D_BLE_URC_NONE;
return ev;
}
/**
* @brief 阻塞执行一条较长 ATGATT notify 含 hex 载荷)
*/
static int s_at_exec_blocking_gatt(const char *at_line, uint32_t wait_ms)
{
int cmd_len;
if (at_line == NULL) {
return -1;
}
cmd_len = s_at_format_cmd(at_line, BLE->at.gatt_at_cmd, sizeof(BLE->at.gatt_at_cmd));
if (cmd_len <= 0) {
return -1;
}
BLE->at.rx_acc_len = 0U;
BLE->at.rx_acc[0] = '\0';
s_ble_drain_uart_before_gatt();
if (u16_usart_send(FC41D_USART_ID, (uint8_t *)BLE->at.gatt_at_cmd, (uint16_t)cmd_len) == 0U) {
BT_PRINT("AT send failed\r\n");
return -1;
}
if (s_at_blocking_wait_ok(wait_ms) != 0) {
s_ble_drain_uart_before_gatt();
if (s_fc41d_link_or_at_has_ok() != 0) {
return 0;
}
if (s_at_blocking_wait_ok(400U) != 0) {
BT_PRINT("AT GATT timeout (rx_len=%u)\r\n", (unsigned)BLE->at.rx_acc_len);
return -1;
}
return 0;
}
return 0;
}
/**
* @brief 经 QBLEGATTSNTFY 向手机 Notify 特征发送一段载荷(hex 编码)
*/
static int s_ble_gatt_notify_chunk(const uint8_t *data, uint16_t len)
{
static const char k_hex[] = "0123456789ABCDEF";
char line[FC41D_AT_CMD_MAX];
char *hex_out;
uint16_t i;
int n;
if ((data == NULL) || (len == 0U)) {
return -1;
}
if ((size_t)len * 2U + 48U >= sizeof(line)) {
return -1;
}
n = snprintf(line, sizeof(line), "QBLEGATTSNTFY=%s,%u,",
FC41D_CHAR_NOTIFY_UUID, (unsigned)len);
if (n <= 0) {
return -1;
}
hex_out = &line[n];
for (i = 0U; i < len; i++) {
hex_out[i * 2U] = k_hex[(data[i] >> 4) & 0x0FU];
hex_out[i * 2U + 1U] = k_hex[data[i] & 0x0FU];
}
hex_out[len * 2U] = '\0';
return s_at_exec_blocking_gatt(line, FC41D_GATT_AT_WAIT_MS);
}
/**
* @brief GATT 非透传发送(自动分片),与 WiFi/TCP 上层载荷格式一致
* @return 实际发送字节数,失败 -1
*/
static int s_ble_gatt_notify_send(const uint8_t *data, uint16_t len)
{
uint16_t off = 0U;
int total = 0;
if ((data == NULL) || (len == 0U)) {
return -1;
}
while (off < len) {
uint16_t chunk = (uint16_t)(len - off);
if (chunk > FC41D_GATT_TX_CHUNK) {
chunk = FC41D_GATT_TX_CHUNK;
}
if (s_ble_gatt_notify_chunk(&data[off], chunk) != 0) {
return (total > 0) ? total : -1;
}
off = (uint16_t)(off + chunk);
total += (int)chunk;
if (off < len) {
mSleep(30U);
}
}
return total;
}
/**
* @brief 判断 FC41D 应答中是否含独立 OK(排除 +Qxxx 字段内的 ok
*/
static int s_fc41d_resp_has_ok(const uint8_t *buf, uint16_t len)
{
uint16_t i;
if (buf == NULL || len < 2U) {
return 0;
}
for (i = 0U; i + 1U < len; ++i) {
if ((buf[i] == 'O' || buf[i] == 'o') && (buf[i + 1U] == 'K' || buf[i + 1U] == 'k')) {
if (i > 0U && buf[i - 1U] == '+') {
continue;
}
return 1;
}
}
return 0;
}
/**
* @brief 在 AT 累积区或 link 解析缓冲中查找模组 OK(升级时 OK 常混在 +QBLERECV 流里)
*/
static int s_fc41d_link_or_at_has_ok(void)
{
uint16_t i;
uint16_t n;
if (s_fc41d_resp_has_ok((const uint8_t *)BLE->at.rx_acc, BLE->at.rx_acc_len) != 0) {
return 1;
}
n = BLE->io.link_rx_acc_len;
if (n < 4U) {
return 0;
}
for (i = 0U; i + 3U < n; i++) {
if ((BLE->io.link_rx_acc[i] == '\r') && (BLE->io.link_rx_acc[i + 1U] == '\n') &&
(BLE->io.link_rx_acc[i + 2U] == 'O') && (BLE->io.link_rx_acc[i + 3U] == 'K')) {
return 1;
}
if ((i == 0U) && (BLE->io.link_rx_acc[0] == 'O') && (BLE->io.link_rx_acc[1] == 'K') &&
(BLE->io.link_rx_acc[2] == '\r') && (BLE->io.link_rx_acc[3] == '\n')) {
return 1;
}
}
return 0;
}
/**
* @brief GATT 发送前尽量排空入站数据,降低与 +QBLERECV 并发冲突
*/
static void s_ble_drain_uart_before_gatt(void)
{
#if TCP_DEBUG_EN
if (system_filetransfer_is_active() != 0) {
uint8_t round;
for (round = 0U; round < 6U; round++) {
v_at_uart_read_all();
}
return;
}
#endif
v_at_uart_drain_link();
}
static int s_ble_is_periodic_report_json(const uint8_t *data, uint32_t len)
{
if ((data == NULL) || (len < 16U)) {
return 0;
}
if (strstr((const char *)data, "\"command\":\"SystemInfo\"") != NULL) {
return 1;
}
if (strstr((const char *)data, "\"command\":\"GunInfo\"") != NULL) {
return 1;
}
return 0;
}
static void s_ble_set_peer_info(void)
{
(void)snprintf(SESS->peer_info, sizeof(SESS->peer_info), "BLE:%s", FC41D_BLE_NAME);
}
/* =============================================================================
* 硬件控制 — 静态函数(PE6 = FC41D_RST,高电平复位,常态低)
* ============================================================================= */
/**
* @brief FC41D 硬件复位脉冲:高 200ms 后释放为低,并清空 UART7 接收
*/
static void v_hw_fc41d_reset_pulse(void)
{
FC41D_RST(1);
mSleep(FC41D_RST_PULSE_MS);
FC41D_RST(0);
s_fc41d_uart_init();
s_fc41d_uart_discard();
BT_PRINT("FC41D reset pulse done (PE6 RST)\r\n");
}
/**
* @brief 拉高 PE6 使模组进入复位
*/
static void v_hw_fc41d_hold_reset(void)
{
FC41D_RST(1);
BT_PRINT("FC41D held in reset\r\n");
}
/* =============================================================================
* BLE 状态机 — 静态函数
* ============================================================================= */
/**
* @brief 阻塞执行 BLE 外设初始化 AT 序列
* @return 0 成功,-1 失败
*/
static int s_ble_setup_sequence_blocking(void)
{
if (s_at_exec_blocking_line("QBLEINIT=2", FC41D_AT_INIT_WAIT_MS) != 0) {
return -1;
}
(void)snprintf(BLE->at.ble_cmd_buf, sizeof(BLE->at.ble_cmd_buf), "QBLENAME=%s", FC41D_BLE_NAME);
if (s_at_exec_blocking_line(BLE->at.ble_cmd_buf, FC41D_AT_CMD_WAIT_MS) != 0) {
return -1;
}
if (s_at_exec_blocking_line("QBLEURC=2", FC41D_AT_CMD_WAIT_MS) != 0) {
return -1;
}
(void)snprintf(BLE->at.ble_cmd_buf, sizeof(BLE->at.ble_cmd_buf), "QBLEGATTSSRV=%s", FC41D_SRV_UUID);
if (s_at_exec_blocking_line(BLE->at.ble_cmd_buf, FC41D_AT_CMD_WAIT_MS) != 0) {
return -1;
}
(void)snprintf(BLE->at.ble_cmd_buf, sizeof(BLE->at.ble_cmd_buf), "QBLEGATTSCHAR=%s", FC41D_CHAR_WRITE_UUID);
if (s_at_exec_blocking_line(BLE->at.ble_cmd_buf, FC41D_AT_CMD_WAIT_MS) != 0) {
return -1;
}
(void)snprintf(BLE->at.ble_cmd_buf, sizeof(BLE->at.ble_cmd_buf), "QBLEGATTSCHAR=%s", FC41D_CHAR_NOTIFY_UUID);
if (s_at_exec_blocking_line(BLE->at.ble_cmd_buf, FC41D_AT_CMD_WAIT_MS) != 0) {
return -1;
}
if (s_at_exec_blocking_line("QBLEADVPARAM=150,150", FC41D_AT_CMD_WAIT_MS) != 0) {
return -1;
}
if (s_at_exec_blocking_line("QBLEADVSTART", FC41D_AT_CMD_WAIT_MS) != 0) {
return -1;
}
BT_PRINT("BLE advertising started, name=%s\r\n", FC41D_BLE_NAME);
return 0;
}
/**
* @brief 阻塞查询 QBLESTAT,判断是否已连接(10s 兜底,非每周期轮询)
* @return 1 已连接,0 未连接或查询失败
*/
static int s_fc41d_stat_query_connected(void)
{
static const char k_cmd[] = "AT+QBLESTAT\r\n";
uint8_t rx[320];
uint64_t deadline_ms;
uint16_t rx_len;
s_fc41d_uart_discard();
if (u16_usart_send(FC41D_USART_ID, (uint8_t *)k_cmd,
(uint16_t)(sizeof(k_cmd) - 1U)) == 0U) {
return 0;
}
rx_len = 0U;
rx[0] = '\0';
deadline_ms = u64_get_current_millis() + (uint64_t)FC41D_AT_CMD_WAIT_MS;
while (u64_get_current_millis() < deadline_ms) {
uint16_t got;
got = s_fc41d_uart_drain_rx(&rx[rx_len],
(uint16_t)(sizeof(rx) - rx_len));
if (got > 0U) {
rx_len += got;
rx[rx_len] = '\0';
}
if (strstr((char *)rx, "ERROR") != NULL) {
return 0;
}
if (s_fc41d_resp_has_ok(rx, rx_len) != 0) {
if (strstr((char *)rx, "CONNECTED") != NULL &&
strstr((char *)rx, "DISCONNECTED") == NULL) {
return 1;
}
return 0;
}
mSleep(20U);
v_wdt_setFlag(TASK_ID_TcpSer);
}
return 0;
}
/**
* @brief 连接后进入 QBLETRANMODE 透传(手册 AT+QBLETRANMODE
*/
static int s_ble_enter_transparent_blocking(void)
{
#if !FC41D_BLE_USE_TRANSPARENT
if (s_at_exec_blocking_line("QBLECFGMTU=512", FC41D_AT_CMD_WAIT_MS) != 0) {
BT_PRINT("QBLECFGMTU failed (continue)\r\n");
}
BLE->sm.gatt_fallback = 1U;
return 0;
#else
static const char *const k_tran_cmds[] = {
"QBLETRANMODE=" FC41D_TRAN_UUID ",1",
"QBLETRANMODE=" FC41D_CHAR_NOTIFY_UUID ",1",
"QBLETRANMODE=" FC41D_CHAR_WRITE_UUID ",1",
"QBLETRANMODE=" FC41D_TRAN_UUID ",0",
"QBLETRANMODE=" FC41D_CHAR_NOTIFY_UUID ",0",
};
size_t i;
if (s_at_exec_blocking_line("QBLECFGMTU=512", FC41D_AT_CMD_WAIT_MS) != 0) {
BT_PRINT("QBLECFGMTU failed (continue)\r\n");
}
for (i = 0U; i < (sizeof(k_tran_cmds) / sizeof(k_tran_cmds[0])); i++) {
if (s_at_exec_blocking_line(k_tran_cmds[i], FC41D_AT_CMD_WAIT_MS) == 0) {
v_at_set_transparent(1U);
BLE->sm.gatt_fallback = 0U;
BT_PRINT("Transparent mode OK (%s)\r\n", k_tran_cmds[i]);
return 0;
}
BT_PRINT("QBLETRANMODE failed: %s\r\n", k_tran_cmds[i]);
}
BLE->sm.gatt_fallback = 1U;
v_at_set_transparent(0U);
BT_PRINT("QBLETRANMODE all failed, GATT fallback (+QBLERECV)\r\n");
return 0;
#endif
}
static int s_ble_exit_transparent_blocking(void)
{
#if FC41D_BLE_USE_TRANSPARENT
if (u8_at_is_transparent() == 0U) {
return 0;
}
v_at_set_transparent(0U);
(void)snprintf(BLE->at.ble_cmd_buf, sizeof(BLE->at.ble_cmd_buf),
"QBLETRANMODE=%s,0", FC41D_CHAR_NOTIFY_UUID);
if (s_at_exec_blocking_line(BLE->at.ble_cmd_buf, FC41D_AT_CMD_WAIT_MS) != 0) {
BT_PRINT("QBLETRANMODE exit failed\r\n");
return -1;
}
#endif
return 0;
}
/**
* @brief 丢弃 UART 残留并清空 AT 接收缓冲(断连/停止前调用)
*/
static void v_at_tx_reset(void)
{
v_at_uart_discard();
}
/**
* @brief 复位 BLE 会话全部软件状态(不断电)
*/
static void v_ble_session_reset(void)
{
BLE->sm.state = FC41D_BLE_ST_IDLE;
SESS->client_connected = 0U;
BLE->sm.connect_deadline_ms = 0U;
BLE->sm.power_on_deadline_ms = 0U;
BLE->sm.last_stat_poll_ms = 0U;
BLE->sm.gatt_fallback = 0U;
SESS->peer_info[0] = '\0';
v_at_init();
s_ble_io_free();
v_at_tx_reset();
}
/**
* @brief 上电并开始 BLE 初始化状态机(异步,不阻塞 tcp_ser 任务)
* @return 0 成功
*/
static int s_ble_session_start(void)
{
v_ble_session_reset();
if (s_ble_io_set_caps(FC41D_LINK_RX_NORMAL, FC41D_APP_RX_NORMAL) != 0) {
BT_PRINT("BLE RX normal buf alloc failed\r\n");
return -1;
}
v_hw_fc41d_reset_pulse();
BLE->sm.power_on_deadline_ms = u64_get_current_millis() + (uint64_t)FC41D_POWER_ON_DELAY_MS;
BLE->sm.state = FC41D_BLE_ST_POWER_ON;
BT_PRINT("BLE init started (DIP1=%u)\r\n", (unsigned)u8_sub_comm_yx_get(E_SUB_DIP_1));
return 0;
}
/**
* @brief 驱动 BLE 状态机(需在 comm_link_ble_process / 任务循环中周期调用)
*/
static void v_ble_session_process(void)
{
fc41d_ble_urc_t urc;
uint64_t now_ms;
if (BLE->sm.state == FC41D_BLE_ST_IDLE || BLE->sm.state == FC41D_BLE_ST_ERROR) {
return;
}
now_ms = u64_get_current_millis();
urc = s_at_poll_urc();
if (BLE->sm.state == FC41D_BLE_ST_POWER_ON) {
if (u64_get_current_millis() < BLE->sm.power_on_deadline_ms) {
return;
}
if (s_ble_setup_sequence_blocking() != 0) {
BT_PRINT("BLE setup failed\r\n");
BLE->sm.state = FC41D_BLE_ST_ERROR;
return;
}
BLE->sm.state = FC41D_BLE_ST_WAIT_CONNECT;
BLE->sm.connect_deadline_ms = u64_get_current_millis() + (uint64_t)FC41D_CONNECT_WAIT_MS;
BLE->sm.last_stat_poll_ms = u64_get_current_millis();
BT_PRINT("Waiting for BLE connection...\r\n");
return;
}
if (BLE->sm.state == FC41D_BLE_ST_WAIT_CONNECT) {
if (urc == FC41D_BLE_URC_CONNECTED) {
BT_PRINT("BLE link connected (URC)\r\n");
SESS->client_connected = 1U;
s_ble_set_peer_info();
(void)s_ble_enter_transparent_blocking();
BLE->sm.state = FC41D_BLE_ST_CONNECTED;
if (u8_at_is_transparent() != 0U) {
BT_PRINT("BLE connected, name=%s\r\n", FC41D_BLE_NAME);
BT_PRINT("BLE transparent ready for JSON\r\n");
} else {
BT_PRINT("BLE connected (GATT), name=%s\r\n", FC41D_BLE_NAME);
}
return;
}
if ((now_ms - BLE->sm.last_stat_poll_ms) >= (uint64_t)FC41D_STAT_POLL_MS) {
BLE->sm.last_stat_poll_ms = now_ms;
if (s_fc41d_stat_query_connected() != 0) {
BT_PRINT("BLE link connected (QBLESTAT)\r\n");
SESS->client_connected = 1U;
s_ble_set_peer_info();
(void)s_ble_enter_transparent_blocking();
BLE->sm.state = FC41D_BLE_ST_CONNECTED;
if (u8_at_is_transparent() != 0U) {
BT_PRINT("BLE connected, name=%s\r\n", FC41D_BLE_NAME);
BT_PRINT("BLE transparent ready for JSON\r\n");
} else {
BT_PRINT("BLE connected (GATT), name=%s\r\n", FC41D_BLE_NAME);
}
return;
}
}
if (now_ms >= BLE->sm.connect_deadline_ms) {
/* 已在广播中,勿重复 QBLEADVSTART(模组会回 ERROR */
BT_PRINT("BLE still advertising, waiting for phone connect...\r\n");
BLE->sm.connect_deadline_ms = now_ms + (uint64_t)FC41D_CONNECT_WAIT_MS;
BLE->sm.last_stat_poll_ms = now_ms;
}
return;
}
if (BLE->sm.state == FC41D_BLE_ST_CONNECTED) {
if (u8_at_is_transparent() != 0U) {
v_at_uart_drain_payload();
} else {
#if TCP_DEBUG_EN
if (system_filetransfer_is_active() != 0) {
uint8_t drain;
for (drain = 0U; drain < 16U; drain++) {
v_at_uart_drain_link();
}
} else
#endif
{
v_at_uart_drain_link();
}
}
if (urc == FC41D_BLE_URC_DISCONNECTED) {
SESS->client_connected = 0U;
SESS->peer_info[0] = '\0';
(void)s_ble_exit_transparent_blocking();
BLE->sm.gatt_fallback = 0U;
s_app_rx_clear();
BLE->sm.state = FC41D_BLE_ST_WAIT_CONNECT;
BLE->sm.connect_deadline_ms = now_ms + (uint64_t)FC41D_CONNECT_WAIT_MS;
BLE->sm.last_stat_poll_ms = now_ms;
(void)s_at_exec_blocking_line("QBLEADVSTOP", FC41D_AT_CMD_WAIT_MS);
if (s_at_exec_blocking_line("QBLEADVSTART", FC41D_AT_CMD_WAIT_MS) != 0) {
BT_PRINT("BLE re-advertise failed\r\n");
} else {
BT_PRINT("BLE disconnected, re-advertising\r\n");
}
}
}
}
/**
* @brief 是否已有 BLE 逻辑连接(不含透传是否就绪)
*/
static uint8_t u8_ble_is_connected(void)
{
return SESS->client_connected;
}
/* =============================================================================
* 对外接口 — DIP 拨码
* ============================================================================= */
/**
* @brief 上电后等待一段时间,确保副板 sub_comm 已上报 DIP 状态
* @param timeout_ms 阻塞等待毫秒数(当前实现为固定 delay,便于日志打印 DIP1)
* @return 固定 0
*/
int ble_link_wait_dip_ready(uint32_t timeout_ms)
{
uint64_t deadline = u64_get_current_millis() + (uint64_t)timeout_ms;
while (u64_get_current_millis() < deadline) {
mSleep(50U);
}
BT_PRINT("DIP wait %ums, DIP1=%u\r\n",
(unsigned)timeout_ms, (unsigned)u8_sub_comm_yx_get(E_SUB_DIP_1));
return 0;
}
/**
* @brief 根据 DIP1 判断当前是否应选择蓝牙调试链路
* @return 1 DIP1=1(蓝牙),0 DIP1=0(以太网 TCP
*/
uint8_t ble_link_is_bt_mode(void)
{
return (u8_sub_comm_yx_get(E_SUB_DIP_1) != 0U) ? 1U : 0U;
}
/* =============================================================================
* 对外接口 — comm_link 后端(与 tcp_server JSON 协议对接)
* ============================================================================= */
/**
* @brief 启动 BLE 后端:模组上电并开始 AT 初始化序列
* @return 0 成功
*/
int comm_link_ble_start(void)
{
return s_ble_session_start();
}
/**
* @brief 停止 BLE:复位状态机、模组下电
*/
void comm_link_ble_stop(void)
{
(void)s_ble_exit_transparent_blocking();
v_at_tx_reset();
v_ble_session_reset();
v_hw_fc41d_hold_reset();
}
/**
* @brief 周期处理:推进 BLE 状态机(广播、连接、断线重广播)
*/
void comm_link_ble_process(void)
{
v_ble_session_process();
}
/**
* @brief 是否有上位机连接且已进入透传(tcp_server 可收发 JSON
* @return 1 已连接且透传就绪,0 否
*/
int comm_link_ble_is_client_connected(void)
{
if (u8_ble_is_connected() == 0U) {
return 0;
}
if (BLE->sm.state != FC41D_BLE_ST_CONNECTED) {
return 0;
}
#if FC41D_BLE_USE_TRANSPARENT
return (u8_at_is_transparent() != 0U || BLE->sm.gatt_fallback != 0U) ? 1 : 0;
#else
return 1;
#endif
}
/**
* @brief 主动断开当前 BLE 连接(发送 QBLEDISCONN
*/
void comm_link_ble_close_client(void)
{
(void)s_ble_exit_transparent_blocking();
BLE->sm.gatt_fallback = 0U;
v_at_tx_reset();
s_app_rx_clear();
(void)s_at_exec_blocking_line("QBLEDISCONN", FC41D_AT_CMD_WAIT_MS);
}
/**
* @brief 从 BLE 透传通道读取数据(QBLETRANMODE 后 UART 直传 JSON
* @return >0 字节数,0 无数据,-1 错误
*/
int comm_link_ble_receive_data(uint8_t *buffer, uint32_t buffer_size)
{
if ((buffer == NULL) || (buffer_size == 0U)) {
return -1;
}
if (BLE->sm.state != FC41D_BLE_ST_CONNECTED) {
return 0;
}
#if FC41D_BLE_USE_TRANSPARENT
if ((u8_at_is_transparent() == 0U) && (BLE->sm.gatt_fallback == 0U)) {
return 0;
}
#endif
#if TCP_DEBUG_EN
if (system_filetransfer_is_active() != 0) {
uint8_t pass;
for (pass = 0U; pass < 16U; pass++) {
v_at_uart_read_all();
}
} else
#endif
{
v_at_uart_read_all();
}
{
int n = s_app_rx_pop(buffer, buffer_size);
#if TCP_DEBUG_EN
if (n > 0) {
s_ble_log_json_io("RX", buffer, (uint32_t)n);
}
if ((s_ble_diag_trace() != 0) && (n > 0)) {
tcp_firmware_upgrade_log_buf("APP-POP", buffer, (uint32_t)n);
printf("[FW_UPGRADE] APP-POP done n=%d app_remain=%u\r\n",
n, (unsigned)s_app_rx_pending());
} else if ((s_ble_diag_trace() != 0) && (s_app_rx_pending() > 0U)) {
printf("[FW_UPGRADE] APP-POP blocked pending=%u cap_out=%u\r\n",
(unsigned)s_app_rx_pending(), (unsigned)buffer_size);
}
#endif
return n;
}
}
int comm_link_ble_rx_upgrade_begin(void)
{
BT_PRINT("BLE RX upgrade buf (link=%u app=%u)\r\n",
(unsigned)FC41D_LINK_RX_UPGRADE, (unsigned)FC41D_APP_RX_UPGRADE);
if (s_ble_io_set_caps(FC41D_LINK_RX_UPGRADE, FC41D_APP_RX_UPGRADE) != 0) {
return -1;
}
s_app_rx_clear();
return 0;
}
void comm_link_ble_rx_flush_pending(void)
{
s_app_rx_clear();
if (BLE->io.link_rx_acc != NULL) {
BLE->io.link_rx_acc_len = 0U;
BLE->io.qble_payload_need = 0U;
}
}
int comm_link_ble_rx_upgrade_end(void)
{
BT_PRINT("BLE RX restore normal buf (link=%u app=%u)\r\n",
(unsigned)FC41D_LINK_RX_NORMAL, (unsigned)FC41D_APP_RX_NORMAL);
return s_ble_io_set_caps(FC41D_LINK_RX_NORMAL, FC41D_APP_RX_NORMAL);
}
/**
* @brief 经 BLE 透传发送数据(tcp_server 应答 JSON
* @return 发送字节数,失败 -1
*/
int comm_link_ble_send_data(const uint8_t *data, uint32_t len)
{
uint32_t off = 0U;
int total = 0;
if (BLE->sm.state != FC41D_BLE_ST_CONNECTED) {
return -1;
}
if ((data == NULL) || (len == 0U)) {
return -1;
}
if (len > 0xFFFFU) {
len = 0xFFFFU;
}
#if TCP_DEBUG_EN
if ((system_filetransfer_is_active() != 0) &&
(s_ble_is_periodic_report_json(data, len) != 0)) {
return (int)len;
}
#endif
#if FC41D_BLE_USE_TRANSPARENT
if (u8_at_is_transparent() != 0U) {
while (off < len) {
uint16_t chunk = (uint16_t)(len - off);
if (chunk > 512U) {
chunk = 512U;
}
if (u16_usart_send(FC41D_USART_ID, (uint8_t *)(data + off), chunk) != chunk) {
return (total > 0) ? total : -1;
}
total += (int)chunk;
off += (uint32_t)chunk;
}
#if TCP_DEBUG_EN
s_ble_log_json_io("TX", data, len);
#endif
return total;
}
if (BLE->sm.gatt_fallback == 0U) {
return -1;
}
#endif
{
int sent = s_ble_gatt_notify_send(data, (uint16_t)len);
#if TCP_DEBUG_EN
if (sent > 0) {
s_ble_log_json_io("TX", data, (uint32_t)sent);
}
#endif
return (sent > 0) ? sent : -1;
}
}
/**
* @brief 填充对端描述字符串(BLE 无 IP,固定为 "BLE"
* @return 0 成功,-1 参数无效
*/
int comm_link_ble_get_peer_info(char *buf, uint32_t buf_size)
{
if ((buf == NULL) || (buf_size == 0U)) {
return -1;
}
if (SESS->peer_info[0] != '\0') {
(void)snprintf(buf, buf_size, "%s", SESS->peer_info);
} else {
(void)snprintf(buf, buf_size, "BLE");
}
return 0;
}
#if !TCP_DEBUG_EN
int comm_link_ble_diag_trace(void)
{
return 0;
}
#endif
#endif /* BLE_DEBUG_EN */