增加断网后重连广播和温湿度广播循环切换

This commit is contained in:
2026-09-03 14:57:03 +08:00
parent 5625362916
commit 9a2fdb1709
5 changed files with 312 additions and 6 deletions
+21
View File
@@ -282,3 +282,24 @@ uint8_t app_linkage_is_valid(void)
LinkageCfg_t cfg;
return lk_parse(gDevData.linkage_config, &cfg);
}
/**
* @brief 缸体编号(Byte0-1 大端)
* @note 只读,不做有效性判定,不受 BOARD_LINKAGE_ENABLE 影响——
* 引擎关掉时 DP6 依然由 APP 下发并存在 gDevData 里。
* @return 缸体编号,0=未配
*/
uint16_t app_linkage_get_tank_id(void)
{
return (uint16_t)(((uint16_t)gDevData.linkage_config[0] << 8) |
gDevData.linkage_config[1]);
}
/**
* @brief 联动配置版本(Byte6
* @return 配置版本
*/
uint8_t app_linkage_get_cfg_ver(void)
{
return gDevData.linkage_config[6];
}
+15
View File
@@ -81,4 +81,19 @@ void app_linkage_void(void);
*/
uint8_t app_linkage_is_valid(void);
/**
* @brief 缸体编号(DP6 Byte0-1 大端)
* @note 只读 DP6,不判有效性、不改任何开停状态。
* 0 = 未配缸体(此时不发自定义温湿度广播,一直发见宝回连包)。
* @return 缸体编号
*/
uint16_t app_linkage_get_tank_id(void);
/**
* @brief 联动配置版本(DP6 Byte6,只存不用)
* @note 温湿度广播 Manufacturer Data 的 ver 字段取这里。
* @return 配置版本
*/
uint8_t app_linkage_get_cfg_ver(void);
#endif /* __APP_LINKAGE_H__ */
+40
View File
@@ -41,6 +41,14 @@ typedef struct {
static AppThCtx_t s_th;
/**
* 「本周期采样成功」事件(生产者=20ms 中断,消费者=500ms 任务上下文)
* ⚠ 中断里只写这三个 volatile,绝不做开关广播 / 打日志 / 写 flash
*/
static volatile uint8_t s_th_pending;
static volatile int16_t s_th_pending_t;
static volatile uint8_t s_th_pending_h;
/**
* @brief 记一次采样失败(发令 nack / 读数失败共用)
* @return 无
@@ -73,6 +81,9 @@ void app_th_init(void)
s_th.valid = 0;
s_th.fail_cnt = 0;
s_th.last_ok = 0;
s_th_pending = 0;
s_th_pending_t = 0;
s_th_pending_h = 0;
s_th.inited = 1;
log_info("th init period=%ums\n", (unsigned)BOARD_TH_PERIOD_MS);
}
@@ -119,6 +130,10 @@ void app_th_poll(uint16_t dt)
s_th.fail_cnt = 0;
s_th.valid = 1;
s_th.last_ok = 1;
/* 先填值再置 pending:消费者关中断取,生产者是中断,顺序无歧义 */
s_th_pending_t = t;
s_th_pending_h = h;
s_th_pending = 1;
log_info("th %d.%dC %d%%\n",
(int)(t / 10), (int)((t < 0) ? (-t % 10) : (t % 10)), h);
} else {
@@ -181,3 +196,28 @@ uint8_t app_th_last_sample_ok(void)
{
return s_th.last_ok;
}
/**
* @brief 取走一次「本周期采样成功」事件(消费端,任务上下文)
* @param t_x10 出参:温度 ×10
* @param h 出参:湿度 %
* @return 1=取到新采样,0=无新采样
*/
uint8_t app_th_take_sample(int16_t *t_x10, uint8_t *h)
{
uint8_t got;
local_irq_disable();
got = s_th_pending;
if (got) {
s_th_pending = 0;
if (t_x10) {
*t_x10 = s_th_pending_t;
}
if (h) {
*h = s_th_pending_h;
}
}
local_irq_enable();
return got;
}
+14
View File
@@ -65,4 +65,18 @@ uint8_t app_th_is_fault(void);
*/
uint8_t app_th_last_sample_ok(void);
/**
* @brief 取走一次「本周期采样成功」事件(供 usr_le_adv 的 500ms 任务上下文)
* @note ⚠ 生产者是 app_th_poll(20ms 硬件定时器中断上下文),那里只置
* volatile pending,不开关广播、不打日志、不写 flash。
* 消费者(usr_ble_adv_updataBLE 线程任务上下文)调本函数取走,
* 内部 local_irq_disable 保护,取到即清。
* 只在真正采样成功(last_ok 那次)时产生,失败/fault 不产生,
* 取到的就是本次新值而不是历史有效值。
* @param t_x10 出参:温度 ×10(可为 NULL)
* @param h 出参:湿度 %(可为 NULL)
* @return 1=取到一次新采样,0=无新采样
*/
uint8_t app_th_take_sample(int16_t *t_x10, uint8_t *h);
#endif /* __APP_TH_H__ */
+222 -6
View File
@@ -1,12 +1,16 @@
/******************************************************************************
* @file usr_le_adv.c
* @brief BLE 广播 / 配对 / OTA 广播切换
* @brief BLE 广播 / 配对 / OTA 广播切换 + 自定义温湿度广播窗
* @author cyWu <1917507415@qq.com>
* @date 2026.08.19
* @version V1.1.0
* @date 2026.09.03
* @version V1.2.0,新增「已绑定未连接」时的 1s 温湿度广播窗
* @history
* - V1.0.0, 2026.08.03, cyWu, 首次发布
* - V1.1.0, 2026.08.19, cyWu, 对齐模组注释与助手;保留 SOC 上电绑广播策略
* - V1.2.0, 2026.09.03, cyWu, 已绑定+未连接+有缸体+本周期采样成功 → 发 1s
* 自定义温湿度广播(3 段 ADFlags / Complete Local Name / Manufacturer
* 0xFFFF),随后切回见宝回连包。配置中/未配网/tank=0/采样失败/OTA/已连接
* 不发。app_th 只置 pending,本文件(500ms 任务上下文)负责组包与切回。
******************************************************************************/
#include "system/includes.h"
@@ -16,6 +20,8 @@
#include "bleproto.h"
#include "bleproto_api.h"
#include "usr_le_product.h"
#include "app_th.h"
#include "app_linkage.h"
#define LOG_TAG_CONST APP
#define LOG_TAG "[LE_ADV]"
@@ -42,12 +48,46 @@
* SOC 决策表(与模组不同:未配网也发绑定广播,便于上电配网):
* usr_goto_pair==1 → 绑定广播
* paired_flag==0 → 绑定广播
* paired_flag==1 → 回连广播
* paired_flag==1 → 回连广播(可被下面的温湿度窗临时顶替 1s
*
* 自定义温湿度广播窗(V1.2.0):
* 同时满足「未连接 + paired_flag==1 + usr_goto_pair==0 + 非 OTA + tank_id!=0
* + 本周期 AHT20 采样成功」→ 发 1s(500ms × 2 拍)自定义包,之后 encode4reconn
* 切回见宝回连包。整包三段,按蓝牙 AD 结构 [Len][Type][Data…] 组:
* 02 01 06
* (1+n) 09 "JBao-<tank>"tank 十进制无前导零)
* 06 FF FF FF <ver> <temp int8> <humi uint8>
* 这不是见宝 TLV,不用 encode4heartbeat;见宝回连仍用 encode4reconn。
* 给 nRF 和以后自研扫描器看,GATT 0x2A00 / gap_device_name 不动。
******************************************************************************/
extern u8 muti_adv_data[31];
extern u8 muti_adv_data_len;
/** 广播数据最大长度(BLE 4.x 31 字节) */
#define USR_ADV_DATA_MAX 31
/** 温湿度窗持续拍数:500ms × 2 = 1s */
#define USR_TH_ADV_WINDOW_TICKS 2
/** 温湿度窗内的快广播间隔(窗内暂停「60s 变慢广播」) */
#define USR_TH_ADV_FAST_INTERVAL 80
/** AD TypeFlags(与见宝包一致) */
#define USR_AD_TYPE_FLAGS 0x01
/** AD TypeComplete Local Name */
#define USR_AD_TYPE_LOCAL_NAME 0x09
/** AD TypeManufacturer Specific Data(禁止用 0x16 Service Data,那是见宝在用的) */
#define USR_AD_TYPE_MANUFACTURER 0xFF
/** 温湿度窗状态:>0 = 窗进行中,值 = 剩余 500ms 拍数 */
static u8 s_th_win_ticks = 0;
/** 窗内提速前的广播间隔,0 = 进窗时本来就是快广播,出窗不还原 */
static u16 s_th_win_interval = 0;
static void usr_adv_encode_reconn(void);
static void usr_adv_encode_th(u16 tank, u8 ver, int16_t temp_x10, u8 humi);
static void usr_th_adv_stop(u8 back_to_reconn);
static void usr_th_adv_tick(void);
/* 定义在 usr_le_recieve.c,库内共用,不对外导出 */
extern bleproto_authsetup_req_t usr_auth_data;
@@ -166,6 +206,10 @@ void usr_ble_adv_init(void)
memcpy(prodcode, g_usr_le_prodcode, BLEPROTO_ADV_TLV_LENGTH_PRODCODE);
memcpy(pcode, g_usr_le_pcode, BLE_PROTO_ADV_LENGTH_PCODE);
/* 广播策略重置:清掉温湿度窗(若有),间隔由本函数统一置 80 */
s_th_win_ticks = 0;
s_th_win_interval = 0;
usr_set_adv_interval(80);
if (usr_var.usr_ota_succ_flag)
{
@@ -216,6 +260,174 @@ u8 usr_get_pair_flag(void)
return usr_auth_data.paired_flag;
}
/**
* @brief 组见宝回连广播包(与 usr_ble_adv_init 已配网分支一致)
* @note 只组包并置 usr_adv_flag=1,真正下发由 usr_ble_adv_updata 完成
* @return 无
*/
static void usr_adv_encode_reconn(void)
{
uint8_t manucode[BLEPROTO_ADV_TLV_LENGTH_MANUCODE] = {0};
uint8_t prodcode[BLEPROTO_ADV_TLV_LENGTH_PRODCODE];
uint8_t pcode[BLE_PROTO_ADV_LENGTH_PCODE];
uint8_t gmac[6];
memcpy(prodcode, g_usr_le_prodcode, BLEPROTO_ADV_TLV_LENGTH_PRODCODE);
memcpy(pcode, g_usr_le_pcode, BLE_PROTO_ADV_LENGTH_PCODE);
memcpy(gmac, usr_auth_data.gwmac, 6);
muti_adv_data_len = bleproto_advdata_encode4reconn(muti_adv_data, 0, gmac,
manucode, pcode, prodcode);
usr_var.usr_adv_flag = 1;
}
/**
* @brief 组自定义温湿度广播包(三段 AD,非见宝 TLV,给 nRF/自研扫描器)
* @note 整包:02 01 06 / (1+n) 09 "JBao-<tank>" / 06 FF FF FF ver T H
* Company ID 0xFFFF 小端;温度 int8 ℃(向 0 取整,钳位 [-128,127]);
* 湿度 uint8 %。不动 GATT 0x2A00 / gap_device_name。
* @param tank 缸体编号(非 0)
* @param ver 配置版本(linkage_config[6]
* @param temp_x10 温度 ×10
* @param humi 湿度 %
* @return 无
*/
static void usr_adv_encode_th(u16 tank, u8 ver, int16_t temp_x10, u8 humi)
{
u8 *p = muti_adv_data;
char dec[5];
u8 digits = 0;
u16 v = tank;
int32_t t_int;
u8 name_len;
/* 段1 Flags(与见宝一致) */
*p++ = 0x02;
*p++ = USR_AD_TYPE_FLAGS;
*p++ = 0x06;
/* 段2 Complete Local Name "JBao-<tank 十进制无前导零>" */
while (v) {
dec[digits++] = (char)('0' + (v % 10));
v /= 10;
}
name_len = (u8)(5 + digits); /* "JBao-" + tank 位数 */
*p++ = (u8)(1 + name_len); /* AD Length = Type(1) + Data */
*p++ = USR_AD_TYPE_LOCAL_NAME;
*p++ = 'J';
*p++ = 'B';
*p++ = 'a';
*p++ = 'o';
*p++ = '-';
while (digits) {
*p++ = (u8)dec[--digits]; /* 反序数组正序发 → 高位在前 */
}
/* 段3 Manufacturer SpecificCompany 0xFFFF(小端) + ver + T(int8) + H */
t_int = temp_x10 / 10; /* C 整除即向 0 取整 */
if (t_int < -128) {
t_int = -128;
}
if (t_int > 127) {
t_int = 127;
}
if (humi > 100) {
humi = 100;
}
*p++ = 0x06; /* Type(1) + Company(2) + ver/T/H(3) */
*p++ = USR_AD_TYPE_MANUFACTURER;
*p++ = 0xFF; /* Company ID 0xFFFF 低字节 */
*p++ = 0xFF; /* Company ID 0xFFFF 高字节 */
*p++ = ver;
*p++ = (u8)t_int;
*p++ = humi;
muti_adv_data_len = (u8)(p - muti_adv_data);
usr_var.usr_adv_flag = 1;
}
/**
* @brief 结束温湿度窗:还原广播间隔,可选切回见宝回连包
* @param back_to_reconn 1=组回连包(窗自然到期用);
* 0=只清窗(配对/OTA/连接切换时包由各自流程组)
* @return 无
*/
static void usr_th_adv_stop(u8 back_to_reconn)
{
if (s_th_win_ticks == 0) {
return;
}
s_th_win_ticks = 0;
if (s_th_win_interval != 0) {
/* 进窗前是慢广播 → 还原;进窗前本来就是快广播则不还原 */
usr_set_adv_interval(s_th_win_interval);
s_th_win_interval = 0;
}
if (back_to_reconn) {
usr_adv_encode_reconn();
log_info("th adv win end, back to reconn\n");
}
}
/**
* @brief 温湿度窗状态机(usr_ble_adv_updata 500ms 拍,任务上下文)
* @note 进窗条件同时满足:未连接 + paired_flag==1 + 非配对 + 非 OTA +
* tank_id!=0 + 本周期 AHT20 采样成功(take 到的才是本次新值)。
* 窗长 500ms × 2;窗内再来采样成功 → 刷新段 3 并重计 2 拍。
* ⚠ app_th 中断侧只置 pending,本函数是唯一消费者。
* @return 无
*/
static void usr_th_adv_tick(void)
{
int16_t t_x10 = 0;
u8 humi = 0;
u8 got;
u16 tank;
/* 窗进行中:先处理强制结束(配对 / OTA / 连接切换 / 清配网) */
if (s_th_win_ticks) {
if (usr_ble_is_connected() || usr_var.usr_goto_pair ||
(usr_auth_data.paired_flag == 0) || usr_var.usr_goto_updata ||
usr_var.usr_ota_succ_flag) {
usr_th_adv_stop(0);
} else if (s_th_win_ticks > 1) {
s_th_win_ticks--;
} else {
/* 最后一拍:必须先 stop(里头要求 ticks!=0),再组回连包。
* 若先减成 0 再 stop,会被 if (ticks==0) return 直接丢掉回连。 */
usr_th_adv_stop(1);
}
}
if (usr_ble_is_connected() || usr_var.usr_goto_pair ||
(usr_auth_data.paired_flag == 0) || usr_var.usr_goto_updata ||
usr_var.usr_ota_succ_flag) {
return;
}
got = app_th_take_sample(&t_x10, &humi);
if (!got) {
return;
}
tank = app_linkage_get_tank_id();
if (tank == 0) {
/* 未配缸体:一直回连,不发温湿度窗 */
return;
}
if (s_th_win_ticks == 0) {
/* 进窗:记下当前间隔并提速(窗内暂停「60s 变慢广播」) */
s_th_win_interval = (usr_get_adv_interval() != USR_TH_ADV_FAST_INTERVAL) ?
usr_get_adv_interval() : 0;
usr_set_adv_interval(USR_TH_ADV_FAST_INTERVAL);
}
/* 进窗 / 窗内刷新:组包(usr_adv_flag=1,下一行刷新块立即下发) */
usr_adv_encode_th(tank, app_linkage_get_cfg_ver(), t_x10, humi);
s_th_win_ticks = USR_TH_ADV_WINDOW_TICKS;
log_info("th adv: tank=%u ver=%u T=%dC H=%u%%\n",
tank, app_linkage_get_cfg_ver(), (int)(t_x10 / 10), humi);
}
/**
* @brief 周期处理广播刷新(由 500ms 定时器调用)
* @note 仅当 usr_adv_flag==1 时:停广播 → 写入 muti_adv_data → 再开广播
@@ -224,6 +436,9 @@ void usr_ble_adv_updata(void)
{
static u16 usr_goto_slow_adv = 0;
/* 温湿度窗状态机:先于广播刷新执行,进/出窗当拍立即生效 */
usr_th_adv_tick();
if (usr_var.usr_adv_flag)
{
ble_trans_module_enable(0);
@@ -236,12 +451,13 @@ void usr_ble_adv_updata(void)
if (!usr_ble_is_connected())
{
/* 未连接:回连快广播(80)持续约 60s(500ms×120)后切慢广播 */
/* 未连接:回连快广播(80)持续约 60s(500ms×120)后切慢广播
* 温湿度窗内暂停计数(窗内强制快广播,窗长 1s) */
if (usr_get_adv_interval() == 80)
{
if ((usr_var.usr_goto_pair == 0) && usr_auth_data.paired_flag)
{
if (usr_var.usr_ota_succ_flag == 0)
if ((usr_var.usr_ota_succ_flag == 0) && (s_th_win_ticks == 0))
{
usr_goto_slow_adv++;
printf("usr_goto_slow_adv = %d\n", usr_goto_slow_adv);