Files
JBao_Heat_Lamp_FW/apps/usr_app/app_th_scan.c
T

326 lines
9.0 KiB
C
Raw 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 app_th_scan.c
* @brief CBR0 断连温湿度扫描(规格书第 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-"+DP11 缸体号(uint16 十进制,无前导零)
* 且厂商段 0xFFFF 首字节 == DP11 配置版本 → 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_MS8.2)。
* 不去重:风扇同一地址先发回连再发 JBao-。单位 0.625ms → ms*8/5500ms→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/版本取自 DP11 联动配置)
* @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
}