326 lines
9.0 KiB
C
326 lines
9.0 KiB
C
/******************************************************************************
|
||
* @file app_th_scan.c
|
||
* @brief CBR1 断连温湿度扫描(规格书第 8 章):500ms 重连广播 ↔ 500ms 扫描
|
||
* @author cyWu <1917507415@qq.com>
|
||
* @date 2026.09.07
|
||
* @version V1.1.6
|
||
* @history
|
||
* - V1.1.6, 2026.09.07, cyWu, 扫描上报只留 GAP 0xE2
|
||
* - V1.0.0, 2026.09.04, cyWu, P2 完整实现(P0/P1 为骨架)
|
||
*
|
||
* @note 门控(8 章表格):已连接 / 未配对 / 配对中(usr_goto_pair) /
|
||
* OTA 中(usr_goto_updata) / 联动配置无效(缸体 0)→ 不扫描,纯广播。
|
||
* 进入交替后偶拍重连广播、奇拍扫描(8.2),一拍 = BOARD_ADV_SCAN_SLOT_MS。
|
||
*
|
||
* 匹配(8.1):名字 == "JBao-"+DP8 缸体号(uint16 十进制,无前导零)
|
||
* 且厂商段 0xFFFF 首字节 == DP8 配置版本 → temp_x10=int8℃×10 喂联动;
|
||
* 湿度只打日志。名字/版本/解析任何一项不符 → 当没收到,不更新温度、
|
||
* 不关加热(保持上一态)。低开销:先滤名字,名字对了才找厂商段。
|
||
*
|
||
* ⚠ 上报回调跑在 BLE 栈线程;扫描只允许断连态,占用射频期间广播已
|
||
* 暂停(usr_le_set_adv_pause),不允许「边广播边扫」。
|
||
******************************************************************************/
|
||
|
||
#include <string.h>
|
||
|
||
#include "system/includes.h"
|
||
#include "app_config.h"
|
||
#include "app_main.h" /* usr_var:配对/OTA 流程标志 */
|
||
#include "app_th_scan.h"
|
||
#include "app_linkage.h"
|
||
#include "usr_le_api.h" /* usr_ble_is_connected / usr_get_pair_flag / pause */
|
||
#include "board_pin.h"
|
||
#include "btstack/le/ble_api.h" /* ble_op_set_scan_param / ble_op_scan_enable2 */
|
||
|
||
#define LOG_TAG_CONST APP
|
||
#define LOG_TAG "[THSCAN]"
|
||
#define LOG_INFO_ENABLE
|
||
#include "debug.h"
|
||
|
||
/* adv AD type */
|
||
#define TH_SCAN_AD_NAME_SHORT 0x08u /* Shortened Local Name */
|
||
#define TH_SCAN_AD_NAME_COMPLETE 0x09u /* Complete Local Name */
|
||
#define TH_SCAN_AD_MFR 0xFFu /* Manufacturer Specific Data */
|
||
|
||
/* 厂商 0xFFFF(小端)后 3B:配置版本 / 温度 int8 / 湿度(8.1) */
|
||
#define TH_SCAN_MFR_PAYLOAD_LEN 3u
|
||
|
||
/* 期望名 "JBao-"(5) + uint16 十进制(<=5) + '\0' */
|
||
#define TH_SCAN_EXPECT_MAX 11u
|
||
|
||
/* 扫描参数:passive;一拍连续听 BOARD_ADV_SCAN_SLOT_MS(8.2)。
|
||
* 不去重:风扇同一地址先发回连再发 JBao-。单位 0.625ms → ms*8/5(500ms→800) */
|
||
#define TH_SCAN_SCAN_TYPE 0u
|
||
#define TH_SCAN_INTERVAL_UNIT (uint16_t)((BOARD_ADV_SCAN_SLOT_MS * 8u) / 5u)
|
||
#define TH_SCAN_WINDOW_UNIT TH_SCAN_INTERVAL_UNIT
|
||
|
||
/** 扫描模块运行时(全部状态集中在此) */
|
||
typedef struct {
|
||
uint8_t active; /**< 交替运行中(满足门控后置 1) */
|
||
uint8_t scan_on; /**< 控制器扫描已使能(上报过滤用) */
|
||
uint32_t tick; /**< 交替拍计数:偶=重连广播,奇=扫描(8.2) */
|
||
uint16_t tank_id; /**< 期望缸体号(配置变化时重建 expect) */
|
||
uint8_t cfg_ver; /**< 期望配置版本 */
|
||
uint8_t expect_len;
|
||
char expect[TH_SCAN_EXPECT_MAX]; /**< "JBao-"+十进制缸体号 */
|
||
} ThScanCtx_t;
|
||
|
||
static ThScanCtx_t s_scan;
|
||
|
||
static void th_scan_disabled_once(void)
|
||
{
|
||
static uint8_t s_logged;
|
||
|
||
if (!s_logged) {
|
||
s_logged = 1;
|
||
log_info("app_th_scan disabled\n");
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 门控:只有断连已配对、非配对/OTA 流程、联动配置有效才扫描
|
||
* @return 1=进入交替;0=纯广播(找回 APP 优先)
|
||
*/
|
||
static uint8_t th_scan_should_run(void)
|
||
{
|
||
if (usr_ble_is_connected()) {
|
||
return 0;
|
||
}
|
||
if (!usr_get_pair_flag()) {
|
||
return 0;
|
||
}
|
||
if (usr_var.usr_goto_pair) {
|
||
return 0;
|
||
}
|
||
if (usr_var.usr_goto_updata) {
|
||
return 0;
|
||
}
|
||
if (!app_linkage_is_valid()) { /* 缸体 0 / Tmin>Tmax:无联动可跑,不扫 */
|
||
return 0;
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
/**
|
||
* @brief 配置变化时重建期望名(tank/版本取自 DP8 联动配置)
|
||
* @return 无
|
||
*/
|
||
static void th_scan_sync_cfg(void)
|
||
{
|
||
uint16_t tank = app_linkage_get_tank_id();
|
||
uint8_t ver = app_linkage_get_cfg_ver();
|
||
char num[6];
|
||
uint16_t t;
|
||
int n = 0;
|
||
|
||
if (s_scan.tank_id == tank && s_scan.cfg_ver == ver) {
|
||
return;
|
||
}
|
||
s_scan.tank_id = tank;
|
||
s_scan.cfg_ver = ver;
|
||
|
||
memcpy(s_scan.expect, "JBao-", 5);
|
||
s_scan.expect_len = 5;
|
||
t = tank;
|
||
do { /* 十进制,无前导零 */
|
||
num[n++] = (char)('0' + (t % 10u));
|
||
t /= 10u;
|
||
} while (t);
|
||
while (n > 0 && s_scan.expect_len < sizeof(s_scan.expect)) {
|
||
s_scan.expect[s_scan.expect_len++] = num[--n];
|
||
}
|
||
s_scan.expect[s_scan.expect_len] = '\0';
|
||
}
|
||
|
||
/**
|
||
* @brief 开扫描(幂等;先由调用方暂停广播,再使能扫描)
|
||
* @return 无
|
||
*/
|
||
static void th_scan_start(void)
|
||
{
|
||
ble_cmd_ret_e ret;
|
||
|
||
if (s_scan.scan_on) {
|
||
return;
|
||
}
|
||
ble_op_set_scan_param(TH_SCAN_SCAN_TYPE, TH_SCAN_INTERVAL_UNIT, TH_SCAN_WINDOW_UNIT);
|
||
ret = ble_op_scan_enable2(1, 0);
|
||
if (ret != BLE_CMD_RET_SUCESS) {
|
||
log_info("scan enable fail %d\n", (int)ret);
|
||
return;
|
||
}
|
||
s_scan.scan_on = 1;
|
||
}
|
||
|
||
/**
|
||
* @brief 关扫描(幂等)
|
||
* @return 无
|
||
*/
|
||
static void th_scan_stop(void)
|
||
{
|
||
if (!s_scan.scan_on) {
|
||
return;
|
||
}
|
||
ble_op_scan_enable2(0, 0);
|
||
s_scan.scan_on = 0;
|
||
}
|
||
|
||
/**
|
||
* @brief 退出交替:停扫、恢复广播,回到纯 adv 策略
|
||
* @return 无
|
||
*/
|
||
static void th_scan_leave(void)
|
||
{
|
||
th_scan_stop();
|
||
usr_le_set_adv_pause(0);
|
||
s_scan.active = 0;
|
||
s_scan.tick = 0;
|
||
}
|
||
|
||
/**
|
||
* @brief 名字是否等于期望 "JBao-{tank}"
|
||
* @param data adv 数据
|
||
* @param dlen adv 数据长度
|
||
* @return 1=匹配
|
||
*/
|
||
static uint8_t th_scan_name_ok(const uint8_t *data, int dlen)
|
||
{
|
||
int i = 0;
|
||
|
||
while (i + 1 < dlen) {
|
||
uint8_t alen = data[i];
|
||
|
||
if (alen == 0 || i + 1 + alen > dlen) {
|
||
break;
|
||
}
|
||
if ((data[i + 1] == TH_SCAN_AD_NAME_COMPLETE ||
|
||
data[i + 1] == TH_SCAN_AD_NAME_SHORT) &&
|
||
(uint8_t)(alen - 1u) == s_scan.expect_len &&
|
||
memcmp(&data[i + 2], s_scan.expect, s_scan.expect_len) == 0) {
|
||
return 1;
|
||
}
|
||
i += 1 + alen;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* @brief 名字已匹配前提下取厂商段;版本相等才更新联动温度
|
||
* @note 版本不等 / 厂商段缺失 → 当没收到(保持上一态)
|
||
* @param data adv 数据
|
||
* @param dlen adv 数据长度
|
||
* @return 无
|
||
*/
|
||
static void th_scan_try_feed(const uint8_t *data, int dlen)
|
||
{
|
||
int i = 0;
|
||
|
||
while (i + 1 < dlen) {
|
||
uint8_t alen = data[i];
|
||
|
||
if (alen == 0 || i + 1 + alen > dlen) {
|
||
break;
|
||
}
|
||
if (data[i + 1] == TH_SCAN_AD_MFR && alen - 1u >= 2u + TH_SCAN_MFR_PAYLOAD_LEN) {
|
||
const uint8_t *pl = &data[i + 4]; /* 跳过 len/type/2B 厂商号 */
|
||
|
||
if (data[i + 2] == 0xFFu && data[i + 3] == 0xFFu &&
|
||
pl[0] == s_scan.cfg_ver) {
|
||
app_linkage_set_scan_temp((int8_t)pl[1]);
|
||
}
|
||
return;
|
||
}
|
||
i += 1 + alen;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 一包 adv payload 的匹配入口
|
||
* @param data AD 结构
|
||
* @param dlen 长度
|
||
* @return 无
|
||
*/
|
||
static void th_scan_on_adv(const uint8_t *data, int dlen)
|
||
{
|
||
if (data == NULL || dlen <= 0) {
|
||
return;
|
||
}
|
||
if (th_scan_name_ok(data, dlen)) {
|
||
th_scan_try_feed(data, dlen);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 初始化扫描模块
|
||
* @return 无
|
||
*/
|
||
void app_th_scan_init(void)
|
||
{
|
||
#if BOARD_TH_SCAN_ENABLE
|
||
memset(&s_scan, 0, sizeof(s_scan));
|
||
log_info("app_th_scan init\n");
|
||
#else
|
||
th_scan_disabled_once();
|
||
#endif
|
||
}
|
||
|
||
/**
|
||
* @brief 500ms 任务上下文(usr_timer_loop 内):偶拍广播 / 奇拍扫描
|
||
* @return 无
|
||
*/
|
||
void app_th_scan_tick(void)
|
||
{
|
||
#if BOARD_TH_SCAN_ENABLE
|
||
if (!th_scan_should_run()) {
|
||
th_scan_leave();
|
||
return;
|
||
}
|
||
|
||
th_scan_sync_cfg();
|
||
if (!s_scan.active) {
|
||
s_scan.active = 1;
|
||
s_scan.tick = 0; /* 8.2:进入先广播一拍,让 APP 优先找回 */
|
||
}
|
||
|
||
if ((s_scan.tick & 1u) == 0u) {
|
||
th_scan_stop(); /* 偶:重连广播 */
|
||
usr_le_set_adv_pause(0);
|
||
} else {
|
||
usr_le_set_adv_pause(1); /* 奇:关广播、开扫描 ≤500ms */
|
||
th_scan_start();
|
||
}
|
||
s_scan.tick++;
|
||
#else
|
||
th_scan_disabled_once();
|
||
#endif
|
||
}
|
||
|
||
/**
|
||
* @brief GAP_EVENT_ADVERTISING_REPORT(栈线程)
|
||
* @note 杰理布局与 wireless_mic 相同:packet[0]=0xE2 [1]=len [2]=adv_report_t
|
||
* @param packet HCI 事件包
|
||
* @param size 事件包长度
|
||
* @return 无
|
||
*/
|
||
void app_th_scan_report_event(uint8_t *packet, uint16_t size)
|
||
{
|
||
#if BOARD_TH_SCAN_ENABLE
|
||
adv_report_t *rpt;
|
||
|
||
if (!s_scan.active || !s_scan.scan_on || packet == NULL) {
|
||
return;
|
||
}
|
||
if (size < 12) {
|
||
return;
|
||
}
|
||
rpt = (adv_report_t *)&packet[2];
|
||
if (rpt->length == 0 || rpt->length > 31) {
|
||
return;
|
||
}
|
||
th_scan_on_adv(rpt->data, rpt->length);
|
||
#else
|
||
(void)packet;
|
||
(void)size;
|
||
#endif
|
||
}
|