Compare commits

...
5 Commits
Author SHA1 Message Date
wuchuyuan d6c2c6dd52 调整自动模式顺序,调整版本号为1.0.1 2026-09-10 18:19:56 +08:00
wuchuyuanandCursor c7adda3711 修复切模式闪完多一闪,以及配对失败灯因休眠又慢又少。
切模式/配对快闪用优先级拦住待机绿,闪完同一拍衔上常亮;短灯效播完前禁睡,避免节拍被拉长。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-10 14:16:42 +08:00
wuchuyuan c141c7e0aa 修复了配网时进入休眠导致配网时间变成的问题,同时增加配网时间到了之后停止广播 2026-09-10 12:14:29 +08:00
wuchuyuan 5875d2a339 修复一下问题:
A用户绑定IPC和门夹弹力绳,分享给B用户;
A用户先进入门夹弹力绳页面,已唤醒玩具,B用户再进入页面
A用户退出页面下发休眠指令,B用户操作玩具,未提示操作成功/失败
2026-09-10 11:43:33 +08:00
wuchuyuan 026d6d5da7 1、开机按满 5s 绿灯先切配对快闪提示,松手才清绑定并进配网,继续按到 12s 仍可改道 OTA
2、app_led_init 提前到按键判定前,3s 常亮 / 5s 快闪 / 拒绝开机低电闪统一走灯效事件表,去掉开机阶段手搓 GPIO 闪烁
2026-08-27 20:19:57 +08:00
18 changed files with 381 additions and 129 deletions
+34 -21
View File
@@ -2,11 +2,19 @@
* @file app_boot.c
* @brief 深睡唤醒 / 上电按键判定实现
* @author cyWu <1917507415@qq.com>
* @date 2026.08.26
* @version V1.1.0
* @date 2026.08.27
* @version V1.4.0
* @history
* - V1.0.0, 2026.08.25, cyWu, 首次发布,实现深睡唤醒按键判定(3s 开机 / 5s 配对)
* - V1.1.0, 2026.08.26, cyWu, 新增 12s 长按进 OTA、开机前低电量拦截
* - V1.2.0, 2026.08.27, cyWu, 按满 5s 绿灯即按配对节奏快闪,松手才真正提交配网
* - V1.3.0, 2026.08.27, cyWu, 3s/5s 提示灯改直接复用 LED 事件库
* LED_EVENT_STANDBY/LED_EVENT_PAIRING),
* 不再手写一份闪烁定时逻辑
* - V1.4.0, 2026.08.27, cyWu, LED 事件库初始化提前到 usr_jb_init(调用本文件
* 前完成),本文件不再自己调 user_led_init
* 拒绝开机低电闪也改用新增的 LED_EVENT_BOOT_REJECT
* 事件,去掉手写的闪烁计次循环
******************************************************************************/
#include "app_boot.h"
@@ -15,6 +23,7 @@
#include "board_pin.h"
#include "app_main.h"
#include "usr_le_api.h"
#include "led_manager.h"
#include "asm/power/power_api.h"
#include "asm/power/power_reset.h"
@@ -62,18 +71,17 @@ static void boot_sleep(void)
}
/**
* @brief 拒绝开机前的低电提示:红灯闪 BOARD_BOOT_LOWBAT_BLINK_N 下
* @brief 拒绝开机前的低电提示:红灯闪 BOARD_BOOT_LOWBAT_BLINK_N 下
* @note 直接挂 LED_EVENT_BOOT_REJECT,闪烁节奏/次数由 led_manager 的事件表
* 统一管理,这里只管等它闪完(事件闪完会自己删掉)
* @return 无
*/
static void boot_blink_low_battery(void)
{
uint8_t i;
for (i = 0; i < BOARD_BOOT_LOWBAT_BLINK_N; i++) {
bsp_led_red_set(1);
boot_delay_ms(BOARD_BOOT_LOWBAT_ON_MS);
bsp_led_red_set(0);
boot_delay_ms(BOARD_BOOT_LOWBAT_OFF_MS);
LED_EventAdd(LED_EVENT_BOOT_REJECT);
while (LED_IsEventExist(LED_EVENT_BOOT_REJECT)) {
clr_wdt();
user_led_handle();
}
}
@@ -134,15 +142,19 @@ static uint8_t boot_skip_key_wait(void)
/**
* @brief 等待并判定开机按键
* @note 配对(5s)不再一到点就立即提交:需要继续按住观察是否会按到 12s
* 触发 OTA,真正的配对提交放在松手那一刻做,行为上仍是“按满 5s
* 再松手即进配对”,只是多给了继续按到 12s 改道 OTA 的机会。
* 触发 OTA,真正的配对提交放在松手那一刻做。按满 5s 绿灯先切到配对
* 快闪提示,行为上仍是“按满 5s 再松手即进配对”,只是多给了继续按到
* 12s 改道 OTA 的机会。
* 3s 常亮 / 5s 快闪直接复用 led_manager 的 LED_EVENT_STANDBY /
* LED_EVENT_PAIRING 事件(和正常运行时配对灯效完全一致的节奏),
* 不再手写一份闪烁定时。LED 事件库由调用方(usr_jb_init)在本函数
* 之前完成初始化,这里直接假定可用,跟 bsp_hw_init() 一个道理。
* @return 无(深睡分支不返回)
*/
void app_boot_wait_key(void)
{
uint32_t t0;
uint32_t elapsed;
uint8_t green_on = 0;
uint8_t pair_armed = 0;
usr_var.usr_goto_pair = 0;
@@ -165,7 +177,6 @@ void app_boot_wait_key(void)
}
t0 = timer_get_ms();
bsp_led_all_off();
log_info("boot key hold, wait 3s on / 5s pair / 12s ota\n");
while (bsp_key_is_pressed()) {
@@ -179,19 +190,21 @@ void app_boot_wait_key(void)
return;
}
/* 5s 只武装配对,真正提交放到松手那一刻,给继续按到 12s 改道 OTA 的机会 */
/* 5s 只武装配对并切到快闪,真正提交放到松手那一刻,给继续按到 12s 改道 OTA 的机会 */
if (!pair_armed && (elapsed >= BOARD_KEY_PAIR_MS)) {
pair_armed = 1;
log_info("boot hold %ums, pair armed (release now for pair, "
LED_EventDelete(LED_EVENT_STANDBY);
LED_EventAdd(LED_EVENT_PAIRING);
log_info("boot hold %ums, pair hint blink (release now for pair, "
"keep holding to %ums for ota)\n",
(unsigned)elapsed, (unsigned)BOARD_KEY_OTA_MS);
}
if (!green_on && (elapsed >= BOARD_KEY_POWER_MS)) {
green_on = 1;
bsp_led_green_set(1);
} else if (!pair_armed && !LED_IsEventExist(LED_EVENT_STANDBY)
&& (elapsed >= BOARD_KEY_POWER_MS)) {
LED_EventAdd(LED_EVENT_STANDBY);
log_info("boot hold 3s, will power on\n");
}
user_led_handle(); /* 按事件表当前状态刷新一次灯,闪烁节奏由事件配置自己算 */
}
elapsed = timer_get_ms() - t0;
+7 -6
View File
@@ -3,13 +3,14 @@
* @brief 深睡唤醒 / 上电按键判定:3s 开机,5s 配对,12s OTA,不足 3s 松手再睡;
* 开机前电量低于约 5% 且未充电时拒绝开机
* @author cyWu <1917507415@qq.com>
* @date 2026.08.26
* @version V1.1.0
* @date 2026.08.27
* @version V1.2.0
* @history
* - V1.0.0, 2026.08.25, cyWu, 首次发布,实现深睡唤醒按键判定(3s 开机 / 5s 配对)
* - V1.1.0, 2026.08.26, cyWu, 新增 12s 长按进 OTA;新增开机前低电量
* BOARD_VBAT_SHUTDOWN_MV)拦截,仅闪 5 下红灯
* 提示后继续深睡,不接受非充电状态下的开机
* - V1.2.0, 2026.08.27, cyWu, 按满 5s 绿灯即快闪提示,松手才真正提交配网
*
* @note 必须在 BLE 协议栈起来、1ms 定时器启动之前调用。杰理 jiffies 此时
* 往往 10ms 才跳一次,所以内部一律用 timer_get_ms 直接量时间,
@@ -21,10 +22,10 @@
/**
* @brief 等待并判定开机按键:不足 3s 松手、或全程未按键,直接原地深睡(不返回);
* 满 3s 视为正常开机;松手时已过 5s 视为开机并进入配对(内部会调用
* app_pair_start);持续按满 12s 直接开机并进入 OTA(调用
* usr_app_ota_init)。若未充电且电量低于约 5%,则闪 5 下红灯后
* 继续深睡,拒绝开机。
* 满 3s 视为正常开机(绿灯常亮);按满 5s 绿灯改快闪提示“松手即配网”,
* 真正清绑定 / 开绑定广播仍等松手(内部调用 app_pair_start);持续按满
* 12s 直接开机并进入 OTA(调用 usr_app_ota_init)。若未充电且电量低于
* 约 5%,则闪 5 下红灯后继续深睡,拒绝开机。
* @return 无(深睡分支通过 while(1) 喂狗不返回;其余情况正常返回)
*/
void app_boot_wait_key(void);
+65 -11
View File
@@ -2,8 +2,8 @@
* @file app_function.c
* @brief 设备功能开关 + 自动玩法 + 手动点动 + 按键动作实现
* @author cyWu <1917507415@qq.com>
* @date 2026.08.26
* @version V1.1.0
* @date 2026.09.10
* @version V1.3.0
* @history
* - V1.0.0, 2026.08.25, cyWu, 首次发布,实现 DP0 开关、自动玩法、手动点动与按键动作
* - V1.1.0, 2026.08.26, cyWu, 切模式闪灯改到 function_play_mode_apply 统一
@@ -11,6 +11,9 @@
* 启动也补上闪灯;DP2 停转(cmd=3)改边沿触发,
* 避免残留的 03 一直把刚启动的自动模式打断;
* 自动模式新增单次最长运行时长,到点回待机
* - V1.2.0, 2026.09.10, cyWu, 软关后 APP 下发其它控制指令也可唤醒,
* 唤醒后本拍继续执行该指令(玩法/点动)
* - V1.3.0, 2026.09.10, cyWu, 短按循环改为 0→4→2→6→1→3→5,App DP1 编号不变
******************************************************************************/
#include "app_function.h"
@@ -39,17 +42,41 @@ typedef struct {
uint16_t hand_ms;
volatile uint8_t hand_write_pending; /**< DP2 刚写入(03 停转要靠事件) */
volatile uint8_t hand_write_cmd;
volatile uint8_t app_cmd_wake; /**< 软关后收到其它 APP 控制 DP */
} AppFunctionCtx_t;
static AppFunctionCtx_t s_func;
static void function_hand_stop(void);
static void function_app_power_on(const char *reason);
static void function_play_mode_apply(uint8_t mode);
static void function_play_mode_poll(void);
static void function_play_mode_timeout(uint16_t dt);
static void function_hand_poll(void);
static void function_on_click(void);
/* 短按循环顺序(协议玩法号):停 → s_mode4 → s_mode2 → s_mode6 → s_mode1 → s_mode3 → s_mode5 → 停 */
static const uint8_t s_key_play_seq[7] = {
0, 4, 2, 6, 1, 3, 5
};
/**
* @brief 按短按顺序取下一档玩法
* @param cur 当前 DP1 玩法号 0~6
* @return 下一档玩法号
*/
static uint8_t function_key_play_next(uint8_t cur)
{
uint8_t i;
for (i = 0; i < 7; i++) {
if (s_key_play_seq[i] == cur) {
return s_key_play_seq[(i + 1) % 7];
}
}
return 0;
}
/**
* @brief 初始化功能模块状态
* @return 无
@@ -70,18 +97,45 @@ void app_function_dp_hand_write(uint8_t cmd)
s_func.hand_write_pending = 1;
}
/**
* @brief APP 下发了非关机控制 DP,软关时下一拍唤醒
* @return 无
*/
void app_function_on_app_cmd(void)
{
s_func.app_cmd_wake = 1;
}
/**
* @brief APP 侧开机:功能开、同步 DP0、挂开机绿灯
* @param reason 日志原因(英文)
* @return 无
*/
static void function_app_power_on(const char *reason)
{
s_func.app_power = 1;
gDevData.power = 1;
app_led_hint_power_on();
log_info("%s\n", reason);
}
/**
* @brief 跟随 APP 下发的 DP0/DP1,并处理 DP2 手动点动
* @return 无
*/
void app_function_poll(void)
{
/* DP0:APP 下发开关。充电中 / 正在关机时不跟,避免和 USB 软关抢状态 */
uint8_t cmd_wake = s_func.app_cmd_wake;
s_func.app_cmd_wake = 0;
/* DP0:APP 下发开关。充电中 / 正在关机时不跟,避免和 USB 软关抢状态。
* 已软关时,后续其它控制 DP 也当开机,本拍接着跑玩法/点动。 */
if (!app_power_usb_online() && !app_power_poweroff_pending()) {
if (gDevData.power && !s_func.app_power) {
s_func.app_power = 1;
app_led_hint_power_on();
log_info("APP power on\n");
if (!s_func.app_power && cmd_wake) {
function_app_power_on("APP cmd wake from soft off");
} else if (gDevData.power && !s_func.app_power) {
function_app_power_on("APP power on");
} else if (!gDevData.power && s_func.app_power) {
app_function_soft_off();
}
@@ -133,7 +187,7 @@ void app_function_stop_all(void)
}
/**
* @brief 软关:电机停、DP0=0,等 APP/短按再开。不断 BLE
* @brief 软关:电机停、DP0=0,等 APP 开机/其它指令/短按再开。不断 BLE
* @return 无
*/
void app_function_soft_off(void)
@@ -145,7 +199,7 @@ void app_function_soft_off(void)
gDevData.play_mode = 0;
app_mode_stop();
function_hand_stop();
log_info("soft off, wait APP power on\n");
log_info("soft off, wait APP cmd or key\n");
}
/**
@@ -335,7 +389,7 @@ static void function_hand_poll(void)
}
/**
* @brief 短按:自动模式 0→1→…→6→0(闪灯提示交给 function_play_mode_apply
* @brief 短按:按 s_key_play_seq 切自动模式(闪灯提示交给 function_play_mode_apply
* @return 无
*/
static void function_on_click(void)
@@ -344,7 +398,7 @@ static void function_on_click(void)
log_info("key click ignored, hand busy\n");
return;
}
function_play_mode_apply((uint8_t)((s_func.play_mode + 1) % 7));
function_play_mode_apply(function_key_play_next(s_func.play_mode));
log_info("key click play_mode=%d\n", s_func.play_mode);
}
+11 -3
View File
@@ -2,10 +2,11 @@
* @file app_function.h
* @brief 设备功能开关(DP0)+ 自动玩法(DP1)+ 手动点动(DP2)+ 按键动作
* @author cyWu <1917507415@qq.com>
* @date 2026.08.25
* @version V1.0.0
* @date 2026.09.10
* @version V1.2.0
* @history
* - V1.0.0, 2026.08.25, cyWu, 首次发布,实现 DP0 开关、自动玩法、手动点动与按键动作
* - V1.2.0, 2026.09.10, cyWu, 软关后 APP 下发其它控制 DP 也可唤醒功能
*
* @note 本模块决定“电机现在该不该转、转哪种”,会读 app_power/app_pair 的
* 状态作为阻塞条件(充电中/深睡中/配对中都不能转),也会触发
@@ -30,6 +31,13 @@ void app_function_init(void);
*/
void app_function_dp_hand_write(uint8_t cmd);
/**
* @brief APP 下发了非关机控制 DP(玩法/点动等),软关时下一拍唤醒功能
* @note 与关机 DP 同一帧到达时不要调用,避免关完立刻被同包其它 DP 拉开
* @return 无
*/
void app_function_on_app_cmd(void);
/**
* @brief 跟随 APP 下发的 DP0/DP1,并处理 DP2 手动点动,每个业务 tick 调用一次
* @return 无
@@ -50,7 +58,7 @@ void app_function_tick_1ms(uint16_t dt);
void app_function_stop_all(void);
/**
* @brief 软关:电机停、DP0=0,等 APP/短按再开(USB 拔出 / APP 关时用)
* @brief 软关:电机停、DP0=0,等 APP 开机/其它指令/短按再开(USB 拔出 / APP 关时用)
* @return 无
*/
void app_function_soft_off(void);
+23 -15
View File
@@ -2,8 +2,8 @@
* @file app_led.c
* @brief CBC2 灯效适配:充电红与待机绿可同时亮,不再 if-else 互斥
* @author cyWu <1917507415@qq.com>
* @date 2026.08.25
* @version V2.0.0
* @date 2026.09.10
* @version V2.5.0
* @history
* - V1.0.0, 2026.08.21, cyWu, 首次发布
* - V1.1.0, 2026.08.25, cyWu, 开机绿灯只提示几秒
@@ -12,6 +12,7 @@
* - V2.2.0, 2026.08.25, cyWu, 重启/清场逻辑各提一个小函数,把意图写进注释
* - V2.3.0, 2026.08.27, cyWu, 新增 OTA 红绿交替灯效
* - V2.4.0, 2026.08.27, cyWu, 新增空电关机红闪 5 下灯效
* - V2.5.0, 2026.09.10, cyWu, 切模式时保留待机绿,闪完不再多一闪
******************************************************************************/
#include "app_led.h"
@@ -53,15 +54,6 @@ static uint8_t green_pattern_busy(void)
|| LED_IsEventExist(LED_EVENT_PAIR_FAIL));
}
/**
* @brief 绿灯正被配对/切模式/结果占用时,不要再挂待机绿
* @return 1=绿灯忙
*/
static uint8_t green_busy(void)
{
return (uint8_t)(LED_IsEventExist(LED_EVENT_PAIRING) || green_pattern_busy());
}
/**
* @brief 灯效初始化
* @return APP_LED_OK=成功
@@ -157,6 +149,20 @@ uint8_t app_led_is_empty_off(void)
return s_empty_off;
}
/**
* @brief 短灯效还在播时不要进休眠
* @return 1=应禁睡,0=可以睡
*/
uint8_t app_led_need_nosleep(void)
{
if (s_ota || s_empty_off) {
return 1;
}
return (uint8_t)(LED_IsEventExist(LED_EVENT_PAIR_FAIL)
|| LED_IsEventExist(LED_EVENT_PAIR_OK)
|| LED_IsEventExist(LED_EVENT_MODE));
}
/**
* @brief 播放一次性配对结果灯
* @param ok 1=成功常绿,0=失败红绿交替
@@ -263,11 +269,13 @@ void app_led_run(uint16_t dt, uint8_t app_power)
led_clear_function_events();
} else {
#if BOARD_LED_STANDBY_ON
/* 正在充电(红)或已充满(绿)时都不叠加待机绿,只有完全没接电才显示待机 */
if ((charge_led == 0) && !pairing && !green_busy()) {
LED_EventAdd(LED_EVENT_STANDBY);
} else {
/* 配对/切模式/配对失败用 return 1 拦住待机,这里不用删。
* 充电红不占用绿灯:拦截只是不跑待机 handler,绿灯会停在上次的亮。
* 所以充电时要删待机,把绿灯掐灭。 */
if (charge_led == 1) {
LED_EventDelete(LED_EVENT_STANDBY);
} else {
LED_EventAdd(LED_EVENT_STANDBY);
}
#endif
}
+7
View File
@@ -75,6 +75,13 @@ void app_led_request_empty_off(void);
*/
uint8_t app_led_is_empty_off(void);
/**
* @brief 短灯效还在播时不要进休眠(配对失败/成功、切模式闪)
* @note 灯刷新跟业务定时器走;休眠后节拍被拉长,交替闪会又慢又少。
* @return 1=应禁睡,0=可以睡
*/
uint8_t app_led_need_nosleep(void);
/**
* @brief 清空全部灯事件并灭灯(关机前调用)
* @return 无
+1
View File
@@ -86,6 +86,7 @@ static const AppModeStep_t s_mode6[] = { /* 闪电快闪:两下轻、两下重
STEP_END
};
/* 下标 = 协议玩法编号 DP11~6 对应 s_mode1~s_mode6。短按循环顺序在 app_function.c */
static const AppModeStep_t *const s_tables[7] = {
0, s_mode1, s_mode2, s_mode3, s_mode4, s_mode5, s_mode6
};
+18 -3
View File
@@ -2,10 +2,11 @@
* @file app_pair.c
* @brief 配对会话计时实现
* @author cyWu <1917507415@qq.com>
* @date 2026.08.25
* @version V1.0.0
* @date 2026.09.10
* @version V1.1.0
* @history
* - V1.0.0, 2026.08.25, cyWu, 首次发布,实现配对会话计时与成功/超时判定
* - V1.1.0, 2026.09.10, cyWu, 窗口超时停止绑定广播
******************************************************************************/
#include "app_pair.h"
@@ -26,6 +27,19 @@ typedef struct {
static AppPairCtx_t s_pair;
/**
* @brief 配对超时回调:从任务上下文停绑定广播
* @note app_pair_tick 跑在 usr_timer 硬件时基里,不断开/不停广播,
* 把 BLE 动作丢到 sys_timeout,避免占中断时间。
* @param priv 未使用
* @return 无
*/
static void pair_timeout_stop_adv(void *priv)
{
(void)priv;
usr_stop_bind_adv();
}
/**
* @brief 开始一次配对会话(计时清零)
* @return 无
@@ -59,7 +73,8 @@ AppPairEvt_t app_pair_tick_1ms(uint16_t dt)
if (s_pair.elapsed_ms >= BOARD_PAIR_TIMEOUT_MS) {
s_pair.active = 0;
s_pair.elapsed_ms = 0;
log_info("pair timeout 120s, stay on\n");
log_info("pair timeout 120s, stop bind adv\n");
sys_timeout_add(NULL, pair_timeout_stop_adv, 10);
return APP_PAIR_EVT_FAIL;
}
+6 -5
View File
@@ -1,14 +1,15 @@
/******************************************************************************
* @file app_pair.h
* @brief 配对会话计时:开机配对窗口是否成功/超时,与 BLE 配对本身解耦
* @brief 配对会话计时:开机配对窗口是否成功/超时
* @author cyWu <1917507415@qq.com>
* @date 2026.08.25
* @version V1.0.0
* @date 2026.09.10
* @version V1.1.0
* @history
* - V1.0.0, 2026.08.25, cyWu, 首次发布,实现配对会话计时与成功/超时判定
* - V1.1.0, 2026.09.10, cyWu, 窗口超时停止绑定广播
*
* @note 真正的 BLE 广播/绑定逻辑在 usr_le_code;本模块负责进入配对后
* 60s 内 usr_get_pair_flag() 是否变 1”这件事,纯状态,不碰灯/按键
* @note 真正的 BLE 广播/绑定逻辑在 usr_le_code;本模块负责进入配对后
* 120s 内 usr_get_pair_flag() 是否变 1,超时则停绑定广播
******************************************************************************/
#ifndef __APP_PAIR_H__
+4 -1
View File
@@ -3,9 +3,11 @@
* @brief LED 事件框架:按通道掩码独立开关,红绿可同时亮
* @author cyWu <1917507415@qq.com>
* @date 2026.08.25
* @version V1.0.0
* @version V1.1.0
* @history
* - V1.0.0, 2026.08.25, cyWu, 首次发布,实现按通道掩码独立开关的 LED 事件框架
* - V1.1.0, 2026.08.27, cyWu, 新增 LED_EVENT_BOOT_REJECT(拒绝开机低电闪),
* 与运行时 LED_EVENT_EMPTY 参数解耦,各自独立可调
******************************************************************************/
#ifndef __LED_H__
@@ -53,6 +55,7 @@ typedef enum {
LED_EVENT_PAIR_FAIL, /**< 配对失败红绿交替 */
LED_EVENT_OTA, /**< OTA:红绿交替闪,直到升级结束/复位 */
LED_EVENT_EMPTY, /**< 电量 5% 关机:关绿灯,红灯闪 5 下 */
LED_EVENT_BOOT_REJECT, /**< 拒绝开机:电量过低,红灯闪 N 下后继续深睡 */
LED_EVENT_MAX
} LED_Event_t;
+40 -8
View File
@@ -2,10 +2,13 @@
* @file led_manager.c
* @brief CBC2 灯事件:充电红与待机绿走不同通道,可同时亮
* @author cyWu <1917507415@qq.com>
* @date 2026.08.25
* @version V1.0.0
* @date 2026.09.10
* @version V1.2.0
* @history
* - V1.0.0, 2026.08.25, cyWu, 首次发布,实现红绿独立通道灯事件表
* - V1.1.0, 2026.08.27, cyWu, 新增 LED_EVENT_BOOT_REJECT:拒绝开机低电闪,
* 供 app_boot 在按键判定阶段直接复用事件库
* - V1.2.0, 2026.09.10, cyWu, 切模式闪时待机绿不写灯,闪完同一拍衔上常亮
******************************************************************************/
#include "led_manager.h"
@@ -21,6 +24,7 @@ static uint8_t led_pair_ok_handler(LED_Config_t *led_config);
static uint8_t led_pair_fail_handler(LED_Config_t *led_config);
static uint8_t led_ota_handler(LED_Config_t *led_config);
static uint8_t led_empty_handler(LED_Config_t *led_config);
static uint8_t led_boot_reject_handler(LED_Config_t *led_config);
/**
* @brief 只改掩码里的通道,其它灯保持原样
@@ -64,26 +68,29 @@ static uint8_t led_standby_handler(LED_Config_t *led_config)
/**
* @brief 配对绿快闪(无限)
* @note 返回 1 拦住待机绿,否则待机会把快闪盖成常亮
* @param led_config 配置
* @return 0
* @return 1
*/
static uint8_t led_pairing_handler(LED_Config_t *led_config)
{
(void)LED_Update(led_config);
return 0;
return 1;
}
/**
* @brief 切模式闪完两下后删事件
* @note 还在闪时返回 1,拦住待机绿,避免把闪烁盖成常亮
* @param led_config 配置
* @return 0
* @return 1=还在闪,0=闪完已删
*/
static uint8_t led_mode_handler(LED_Config_t *led_config)
{
if (LED_Update(led_config) == 0) {
LED_EventDelete(LED_EVENT_MODE); /* 闪完两下后删,绿灯还给待机/配对 */
return 0;
}
return 0;
return 1;
}
/**
@@ -139,6 +146,20 @@ static uint8_t led_empty_handler(LED_Config_t *led_config)
return 1;
}
/**
* @brief 拒绝开机低电闪:闪完 N 下后删事件,随后调用方直接回深睡
* @param led_config 配置
* @return 1=还在闪,0=闪完已删事件
*/
static uint8_t led_boot_reject_handler(LED_Config_t *led_config)
{
if (LED_Update(led_config) == 0) {
LED_EventDelete(LED_EVENT_BOOT_REJECT);
return 0;
}
return 1;
}
/**
* @brief 注册回调并装载 CBC2 事件表(GPIO 已在 bsp_hw_init
* @return 无
@@ -231,8 +252,7 @@ void user_led_init(void)
.config.alternatingBlinkParams.Group[0] = LED_CHANNEL(LED_CH_RED),
.config.alternatingBlinkParams.Group[1] = LED_CHANNEL(LED_CH_GREEN),
.config.alternatingBlinkParams.Cycle = (uint32_t)(BOARD_LED_ALT_MS * 2u),
.config.runNum = (uint16_t)((BOARD_LED_PAIR_FAIL_MS + (BOARD_LED_ALT_MS * 2u) - 1u)
/ (BOARD_LED_ALT_MS * 2u)),
.config.runNum = (uint16_t)((BOARD_LED_PAIR_FAIL_MS + (BOARD_LED_ALT_MS * 2u) - 1u)/ (BOARD_LED_ALT_MS * 2u)),
.ledEventHandler = led_pair_fail_handler,
},
[LED_EVENT_OTA] = {
@@ -259,6 +279,18 @@ void user_led_init(void)
.config.runNum = BOARD_LED_EMPTY_BLINK_N,
.ledEventHandler = led_empty_handler,
},
[LED_EVENT_BOOT_REJECT] = {
/* 拒绝开机:电量过低,红灯闪 N 下,参数与运行时 EMPTY 独立解耦 */
.event = LED_EVENT_BOOT_REJECT,
.priority = PRIORITY_4,
.config.ledMask = LED_CHANNEL(LED_CH_RED),
.config.state = LED_BLINKING,
.config.blinkParams.orDer = 0, /* 先亮后灭 */
.config.blinkParams.onTime = BOARD_BOOT_LOWBAT_ON_MS,
.config.blinkParams.offTime = BOARD_BOOT_LOWBAT_OFF_MS,
.config.runNum = BOARD_BOOT_LOWBAT_BLINK_N,
.ledEventHandler = led_boot_reject_handler,
},
};
LED_SetStateInit(set_led_multi);
+19
View File
@@ -69,6 +69,9 @@ void userHandle(void)
* ════════════════════════════════════════════════════════════════ */
int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *ctrl)
{
uint8_t other_cmd = 0;
uint8_t got_power_off = 0;
if (!info || !ctrl)
{
return -1;
@@ -89,6 +92,7 @@ int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *ctrl)
else
{
gDevData.power = 0;
got_power_off = 1;
// TODO: 关 — 执行动作
}
break;
@@ -96,6 +100,7 @@ int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *ctrl)
/* ── DP1 自动模式 (enum) ── */
case DP_ID_PLAY_MODE:
gDevData.play_mode = ctrl->play_mode;
other_cmd = 1;
switch (ctrl->play_mode)
{
case PLAY_MODE_0: /* 停止复位 */
@@ -127,6 +132,7 @@ int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *ctrl)
/* ── DP2 手动模式 (raw) ── */
case DP_ID_HAND_MODE:
memcpy(gDevData.hand_mode, ctrl->hand_mode, 2);
other_cmd = 1;
/* 薄适配:通知业务层;03=停,00=空闲 */
{
extern void usr_dp_hand_write(uint8_t cmd);
@@ -136,6 +142,7 @@ int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *ctrl)
/* ── DP3 绳索校准 (bool) ── */
case DP_ID_CALIBRATION:
other_cmd = 1;
if (ctrl->calibration)
{
gDevData.calibration = 1;
@@ -151,30 +158,35 @@ int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *ctrl)
/* ── DP4 绳索圈数 (uint8) ── */
case DP_ID_NUMBERTURNS:
gDevData.numberturns = ctrl->numberturns;
other_cmd = 1;
// TODO: 根据 ctrl->numberturns 执行动作
break;
/* ── DP8 勿扰模式开关 (raw) ── */
case DP_ID_NO_DISTURB_ENABLE:
memcpy(gDevData.no_disturb_enable, ctrl->no_disturb_enable, 13);
other_cmd = 1;
// TODO: 根据 gDevData.no_disturb_enable 执行动作 (长度 13 字节)
break;
/* ── DP9 预留DP1 (uint32) ── */
case DP_ID_RESERVA_DP1:
gDevData.reserva_dp1 = ctrl->reserva_dp1;
other_cmd = 1;
// TODO: 根据 ctrl->reserva_dp1 执行动作
break;
/* ── DP10 预留DP2 (uint32) ── */
case DP_ID_RESERVA_DP2:
gDevData.reserva_dp2 = ctrl->reserva_dp2;
other_cmd = 1;
// TODO: 根据 ctrl->reserva_dp2 执行动作
break;
/* ── DP11 预留DP3 (uint32) ── */
case DP_ID_RESERVA_DP3:
gDevData.reserva_dp3 = ctrl->reserva_dp3;
other_cmd = 1;
// TODO: 根据 ctrl->reserva_dp3 执行动作
break;
@@ -182,6 +194,13 @@ int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *ctrl)
break;
}
}
/* 软关后其它控制 DP 可唤醒;与关机 DP 同帧则关机优先,避免关完立刻被拉开 */
if (other_cmd && !got_power_off)
{
extern void usr_dp_app_cmd(void);
usr_dp_app_cmd();
}
return 0;
}
+1 -1
View File
@@ -22,7 +22,7 @@
/* MCU software version: Vmajor.minor.patch */
#define JB_MCU_SW_VERSION_MAJOR 1
#define JB_MCU_SW_VERSION_MINOR 0
#define JB_MCU_SW_VERSION_PATCH 0
#define JB_MCU_SW_VERSION_PATCH 1
/* MCU hardware version: Vmajor.minor.patch */
#define JB_MCU_HW_VERSION_MAJOR 1
+37 -13
View File
@@ -2,8 +2,8 @@
* @file usr_jb_main.c
* @brief CBC2 业务组合根:只做“先后调用谁、谁通知谁”,不含具体业务算法
* @author cyWu <1917507415@qq.com>
* @date 2026.08.25
* @version V2.1.0
* @date 2026.09.10
* @version V2.3.0
* @history
* - V1.0.0, 2026.08.21, cyWu, 首次发布
* - V1.1.0, 2026.08.24, cyWu, 收口未接线模块;DP2 仅第 1 字节点动 1s
@@ -18,6 +18,12 @@
* 换向死区判定改用阈值检查而非精确计时,
* 取消专门的 1ms 高频档,全系统统一 20ms 节拍,
* 只保留"能不能休眠"这一个维度
* - V2.2.0, 2026.08.27, cyWu, app_led_init() 提前到 app_boot_wait_key() 之前,
* 开机 3s/5s/拒绝开机低电闪直接用灯效事件表,
* 不用再各自摸裸 GPIO 手搓一份闪烁定时
* - V2.3.0, 2026.09.10, cyWu, 配网期间禁睡:priority=0 时低功耗会把 20ms
* 节拍拉长,绑定广播和 AUTHSETUP 握手变慢,
* 实测配网可拖到数分钟
*
* @note 协议生成文件 jb_product.c 不要填业务。APP 下行写 gDevData。
* usr_timer_loop 由 BLE 回调,勿再注册第二份。
@@ -81,7 +87,7 @@ const uint8_t g_usr_le_pcode[USR_LE_PCODE_LEN] = {
*/
typedef enum {
USR_TICK_IDLE = 0, /**< 可休眠 */
USR_TICK_BLOCK_SLEEP /**< 禁止休眠(按键按住/玩法中/关机等待/电机运转,含换向死区)*/
USR_TICK_BLOCK_SLEEP /**< 禁止休眠(按键按住/玩法中/关机等待/电机运转/配网,含换向死区)*/
} UsrTickMode_t;
/** 调度器状态,替代原来 2 个散落的 static */
@@ -152,15 +158,19 @@ static void usr_poweroff_finalize_hook(void)
/**
* @brief 按当前业务状态判断能不能休眠
* @note 换向死区(bsp_motor_is_busy 已把它算进"忙")、点动、玩法、按键按住、
* 关机等待,统一都是"不能睡",节拍周期都一样用 USR_TIMER_IDLE_MS
* 不再区分要不要 1ms
* 关机等待、配网窗口,统一都是"不能睡",节拍周期都一样用 USR_TIMER_IDLE_MS
* 配网只跟 120s 配对窗口(app_pair_is_active):窗口内禁睡。
* 超时后配对失败灯还要再闪约 3s,这段也禁睡,否则节拍被拉长,
* 红绿交替会又慢又少。
* @return 本次应使用的档位
*/
static UsrTickMode_t usr_timer_pick_mode(void)
{
if (bsp_key_is_pressed() || app_power_poweroff_pending()
|| app_mode_get_active() || bsp_motor_is_busy()
|| app_function_is_hand_busy()) {
|| app_function_is_hand_busy()
|| app_pair_is_active()
|| app_led_need_nosleep()) {
return USR_TICK_BLOCK_SLEEP;
}
return USR_TICK_IDLE;
@@ -256,9 +266,12 @@ static void usr_log_boot_version(void)
/**
* @brief 业务初始化(在 BLE 栈起来之前由 app_main 调用)
* @note 内部会等按键 3s/5s;不足 3s 直接深睡,不会启动业务定时器。
* 按键判定只摸 bsp_hw 的裸 GPIO,不经过协议栈/灯效表/电机状态机,
* 所以只有 bsp_hw_init() 需要排在它前面——一旦判定要睡回去
* boot_sleep() 原地喂狗不再返回,后面这些初始化不会被浪费。
* 按键判定只摸 bsp_hw 的裸 GPIO 和 led_manager 的事件表(3s 常亮 /
* 5s 配对快闪 / 拒绝开机低电闪都要用灯),不经过协议栈/电机状态机
* 所以 app_led_init() 要排在它前面(跟 bsp_hw_init() 一样)——一旦
* 判定要睡回去,boot_sleep() 原地喂狗不再返回,后面这些初始化不会
* 被浪费。app_led_init() 内部有已初始化判断,这里提前调不会导致
* 后面重复初始化出问题。
* @return 无
*/
void usr_jb_init(void)
@@ -266,14 +279,14 @@ void usr_jb_init(void)
usr_log_boot_version(); /* UART 已在 app_main 起来,按键判定前就能看到版本 */
bsp_hw_init();
app_led_init(); /* 提前到按键判定之前:3s/5s/拒绝开机低电闪都要直接用灯效事件表 */
app_boot_wait_key(); /* 深睡分支不会返回;返回即代表确定要开机 */
jbInit();
userInit();
app_led_init();
if (usr_var.usr_goto_updata) {
/* 开机 12s 已进 OTA灯表刚起来,立刻挂红绿交替 */
/* 开机 12s 已进 OTA:立刻挂红绿交替 */
app_led_request_ota();
}
app_mode_init();
@@ -286,8 +299,10 @@ void usr_jb_init(void)
user_key_lock_if_held();
app_function_power_on_silent();
usr_timer_rearm(USR_TICK_IDLE);
log_info("timer idle %ums, sleep ok\n", (unsigned)USR_TIMER_IDLE_MS);
/* 开机若已进配对,第一拍就禁睡,不要先挂可休眠档再等 20ms 才切 */
usr_timer_rearm(usr_timer_pick_mode());
log_info("timer %ums, mode=%d\n", (unsigned)USR_TIMER_IDLE_MS,
(int)s_sched.mode);
}
/**
@@ -300,6 +315,15 @@ void usr_dp_hand_write(uint8_t cmd)
app_function_dp_hand_write(cmd);
}
/**
* @brief APP 下发了非关机控制 DPjb_product 薄适配)
* @return 无
*/
void usr_dp_app_cmd(void)
{
app_function_on_app_cmd();
}
/**
* @brief BLE 收到见宝载荷,送入协议环形缓冲
* @param data 载荷指针
+19 -6
View File
@@ -2,11 +2,12 @@
* @file usr_le_adv.c
* @brief BLE 广播 / 配对 / OTA 广播切换
* @author cyWu <1917507415@qq.com>
* @date 2026.08.19
* @version V1.1.0
* @date 2026.09.10
* @version V1.2.0
* @history
* - V1.0.0, 2026.08.03, cyWu, 首次发布
* - V1.1.0, 2026.08.19, cyWu, 对齐模组注释与助手;保留 SOC 上电绑广播策略
* - V1.2.0, 2026.09.10, cyWu, 配对窗口超时停止绑定广播
******************************************************************************/
#include "system/includes.h"
@@ -138,16 +139,28 @@ void usr_goto_pair_mode(void)
usr_ble_adv_init();
}
/**
* @brief 停止绑定广播:清配网标志、关掉广播;未完成的连接一并断开
* @note 只关广播,500ms 协议定时器还在,避免把 usr_timer_loop 停掉。
* usr_adv_flag 清 0 后周期刷新不会再 enable,广播不会自己开回来。
* @return 无
*/
void usr_stop_bind_adv(void)
{
printf("usr_stop_bind_adv\n");
ble_multi_trans_disconnect();
usr_var.usr_goto_pair = 0;
usr_var.usr_adv_flag = 0;
ble_trans_module_enable(0);
}
/**
* @brief 断开 BLE 并停止广播(复位/重启前)
*/
void usr_ble_disconnect_and_stop_adv(void)
{
printf("usr_ble_disconnect_and_stop_adv\n");
ble_multi_trans_disconnect();
usr_var.usr_goto_pair = 0;
usr_var.usr_adv_flag = 0;
ble_trans_module_enable(0);
usr_stop_bind_adv();
}
/**
+11 -2
View File
@@ -2,12 +2,13 @@
* @file usr_le_api.h
* @brief usr_le_code 对外应用 API(配网 / 配对 / OTA
* @author cyWu <1917507415@qq.com>
* @date 2026.08.03
* @version V1.0.2
* @date 2026.09.10
* @version V1.0.3
* @history
* - V1.0.0, 2026.08.03, cyWu, 首次发布
* - V1.0.1, 2026.08.19, cyWu, 收敛公开 API,移除 BLE 栈钩子声明
* - V1.0.2, 2026.08.25, cyWu, 改为源码编译,去掉静态库
* - V1.0.3, 2026.09.10, cyWu, 增加配对窗口超时停绑定广播
*
* @note usr_ble_adv_init / usr_le_data_recieve 为 BLE/RCSP 协议栈链接钩子,
* 由 usr_le_code 内部实现并导出符号,不作为应用层公开接口。
@@ -47,6 +48,14 @@ void usr_clear_pair_info(void);
*/
uint8_t usr_get_pair_flag(void);
/**
* @brief 停止绑定广播(配对窗口超时)
* @note 清 usr_goto_pair,关掉广播;若绑定未完成则断开连接。
* 已配网的回连广播不走本接口。
* @return 无
*/
void usr_stop_bind_adv(void);
/**
* @brief 启动应用侧 OTA 流程
* @note 置位 usr_goto_updata,并创建 1s 周期定时器驱动断连与 OTA 广播切换
+77 -33
View File
@@ -1,11 +1,12 @@
# 门夹弹力绳 需求文档(V1.1
# 门夹弹力绳 需求文档(V1.5
| 项目 | 内容 |
| --- | --- |
| 产品名称 | 门夹弹力绳(智能逗宠玩具) |
| 型号 | PICBC2 / CBC2 |
| 主控 | 杰理 AC7016C |
| 日期 | 2026-08-25 |
| 日期 | 2026-09-10 |
| 修订 | V1.5:配对窗口超时关闭绑定广播 |
---
@@ -13,26 +14,39 @@
门夹弹力绳是一款智能逗宠玩具。设备夹在门板上,内部电机带动弹力绳收放,让绳端玩具按不同"玩法"动起来逗宠物玩。支持手机 App 通过远程控制,也支持按键本地操作。
硬件:红绿双指示灯(低电平亮)+ 单按键(低电平按下)。
---
## 2. 核心功能
### 2.1 开关机
| 操作 | 行为 |
| --- | --- |
| 深睡状态下按 3 秒 | 开机 |
| 深睡状态下按 5 秒 | 开机并进入配对模式 |
| 开机状态下按 3 秒 | 关机(深睡省电) |
| 插着充电线时上电 | 自动开机,不用按键 |
| 刚上电没按按键 | 保持深睡,不误开机 |
| 操作 | 行为 | 灯效 |
| --- | --- | --- |
| 刚上电没按按键、也没插充电线 | 保持深睡,不误开机 | 灭 |
| 插着充电线时上电 | 自动开机,跳过按键判定 | 进正常灯效 |
| 深睡状态下按**3 秒** 后松手 | 开机 | 满 3 秒时绿灯常亮 |
| 深睡状态下按住 **5 秒** 后松手 | 开机并进入配对(清配对信息) | 满 5 秒切成绿灯快闪 |
| 深睡状态下按住 **12 秒** | 开机并进入 OTA(不等松手) | 随后红绿交替闪 |
| 深睡状态下电量过低(约 ≤5%)且未充电 | 拒绝开机,红闪 5 下后继续深睡 | 红 200ms 亮/灭 ×5 |
| 开机状态下按住 **3 秒** | 关机(深睡省电) | 立刻灭灯 |
- 深睡状态下,按按键(3 秒/5 秒)或插充电线均可唤醒开机。
补充说明:
- 深睡唤醒:按按键(3 秒开机 / 5 秒配对 / 12 秒 OTA)或插充电线均可开机。
- 满 5 秒只是「武装配对 + 快闪提示」,真正提交配对在 **松手** 那一刻。如果继续按到 12 秒,改道进 OTA,不再进配对。
- 开机判定时如果手还按着,会锁住按键,松开后才认短按/长按,避免刚开机立刻关机。
### 2.2 开关(App 控制)
- App 可开/关设备功能;关闭后电机停转,蓝牙不断开,可随时再开。
- 功能关闭后,以下操作都会退出软关并恢复功能:
- App 下发开机 DP
- App 再下发其它控制指令(自动玩法、手动点动等),并按该指令继续执行
- 短按按键:只唤醒功能,**不切玩法**
- 拔掉充电线会自动进入低功耗状态(电机停转、LED 关闭、蓝牙保持连接)。
- 充电中、正在深睡关机:其它指令不会把功能拉开。
### 2.3 自动玩法(6 种,App 或按键选择)
@@ -48,46 +62,75 @@
- 玩法会循环播放,直到切换或停止。
- 运行中可随时切换到其他玩法或停止。
- 单次最长运行 **10 分钟**,到点自动停转回待机(保护性超时,不闪灯,避免误以为被按到)。
- 充电中、配对中、正在深睡关机:不允许跑自动玩法,强制回到停转。
- 功能关闭时若 App 下发玩法/点动:先退出软关,再按该指令执行。
### 2.4 手动控制(App 控制)
- App 可手动点动:控制电机**收绳**或**放绳**,松手/点停止后电机停止。
- 手动控制进行中,自动玩法暂停。
- 从自动/空闲切到手动时,绿灯闪两下提示。
- 手动点动进行中,短按按键无效,避免打断收放绳。
- 选中自动玩法会先停手动,再进入自动模式。
### 2.5 按键控制
| 操作 | 行为 |
| --- | --- |
| 短按(<1 秒) | 切换玩法:停 → 玩法1 → 玩法2 → … → 玩法6 → 停,循环 |
| 长按 3 秒 | 关机 |
| 充电中 / 配对中 | 按键无效 |
开机后只有一颗按键,短按/长按含义如下:
| 操作 | 条件 | 行为 |
| --- | --- | --- |
| 短按(松开且未到 3 秒) | 功能开、空闲 | 切换玩法:停 → 起伏攻防战 → 飞鸟俯冲 → 闪电快闪 → 弹力伪装 → 云阶小陀螺 → 微风拂动 → 停,循环;绿灯闪两下。App 仍按 DP1 原编号选玩法 |
| 短按 | 功能已关(App 软关 / 拔充电线后) | 只唤醒功能,不切玩法;挂待机绿灯 |
| 短按 | 手动点动进行中 | 无效 |
| 长按 3 秒 | 功能开 | 关机进深睡 |
| 短按 / 长按 | 充电中 / 配对中 / 正在关机 | 无效 |
### 2.6 电量与充电
- 电量按电压换算:满电 4.2V = 100%3.2V = 0%,电量自动上报 App 显示。
- **低电量**(电池电压低于 **3.7V**):App 上报警告,指示灯红灯慢闪;持续 1 分钟未充电则自动关机保护电池
- **电量极低**(低于 3.2V):直接自动关机
- **插入充电线**:电机立即停止、进入充电状态,只有红灯常亮,表示充电中,充满转绿灯常亮;充电期间不执行玩法,按键无效
- **充电中拔掉充电线**:设备自动进入低功耗状态(电机停转、LED关闭、蓝牙保持连接),需按短按按键或 App 重新开启后才能使用
- 电池:锂电池,USB 5V 充电,充满自动转灯
- 电量按电压换算:满电 4.12V = 100%3.2V = 0%,电量自动上报 App 显示。充电内阻压降下,充满基准按 4.2V 计。
- **低电量**(电池电压低于 **3.7V**):App 上报警告,指示灯红灯慢闪。只提示、不关机;电压回升超过 3.75V 后退出低电
- **空电关机**(显示电量 ≤ **5%**):立刻停电机,红灯闪 5 下后自动关机保护电池。充电中不判定
- **开机电量过低**(未充电且裸电压约 ≤ 3.25V,对应约 5%):拒绝开机,红闪 5 下后继续深睡
- 开机后约 3 秒内不因低电/空电关机,避免上电瞬间误判
- **插入充电线**:电机立即停止、进入充电状态,红灯常亮表示充电,充满转绿灯常亮;充电期间不执行玩法,按键无效。功能关掉时充电灯仍继续亮
- **充电中拔掉充电线**:设备自动进入低功耗状态(电机停转、LED 关闭、蓝牙保持连接),需短按按键或 App 重新开启后才能使用。
- 电池:锂电池,USB 5V 充电,充满自动转灯;硬件判定充满后电量强制收尾到 100%。
### 2.7 指示灯
| 状态 | 灯效 |
| --- | --- |
| 正常待机 | 绿灯常亮 |
| 充电中 | 红灯常亮 |
| 充满 | 绿灯常亮 |
| 低电量 | 红灯慢闪 |
| 配对中 | 绿灯快闪 |
| 配对成功 | 绿灯常亮 3 秒 |
| 配对失败 | 红绿交替闪 3 秒 |
| 按键切玩法 | 绿灯闪两下 |
红绿双灯,通道独立,可以同时亮。充电灯和功能开关无关。
| 状态 | 灯效 | 节奏 |
| --- | --- | --- |
| 正常待机(功能开、没充电) | 绿灯常亮 | 一直亮 |
| 充电中 | 红灯常亮 | 一直亮 |
| 充满 | 绿灯常亮 | 一直亮 |
| 低电量(功能开、没接电) | 红灯慢闪 | 亮 200ms / 周期 2s |
| 配对 | 绿灯快闪 | 100ms 亮 / 100ms 灭 |
| 配对成功 | 绿灯常亮 3 秒 | 一次性 |
| 配对失败 | 红绿交替 3 秒 | 200ms 半周期 |
| 切玩法 / 切手动 | 绿灯闪两下 | 150ms 灭 + 150ms 亮 ×2 |
| OTA | 红绿交替,独占全部灯 | 200ms 半周期,一直闪到升级结束/复位 |
| 运行中空电关机 | 关绿灯,红灯闪 5 下再掉电 | 200ms 亮/灭 ×5 |
| 开机电量过低被拒 | 红灯闪 5 下后继续深睡 | 同上 |
优先级(高的盖住低的):
1. OTA / 空电关机红闪 / 切模式绿闪
2. 配对快闪、配对成功、配对失败
3. 充电红 / 充满绿 / 低电红闪
4. 待机绿灯(最低)
OTA 和空电关机会清掉其它灯,只跑自己那一种。功能关掉时,待机/配对/切模式/低电灯会清掉,**充电灯保留**。
### 2.8 配对
- 首次使用:深睡状态长按 5 秒开机并进入配对,App 扫码连接即可。
- 配对窗口 120:期间连接成功绿灯 3 秒;超时未连接保持开机,可重新配对。
- 首次使用:深睡状态长按 5 秒后松手,开机并进入配对,App 扫码连接即可。
- 按满 5 秒绿灯先切到配对快闪;若继续按到 12 秒则改道 OTA,不再提交配对。
- 配对窗口 120 秒:期间连接成功绿灯常亮 3 秒;超时未连接红绿交替 3 秒,关闭绑定广播,设备保持开机。要再配网:关机后长按 5 秒重新进入配对。
- 配对窗口 120 秒内系统不进入休眠,避免广播/握手被低功耗拉慢;窗口结束(成功或超时)后恢复可休眠。
- 配对期间不跑玩法、按键无效。
- App 可解绑设备,解绑后设备清除配对信息,重新配对即可再次使用。
### 2.9 时间同步
@@ -97,6 +140,7 @@
### 2.10 固件升级(OTA
- 支持 App 远程升级固件,升级协议已打通。
- 本地入口:深睡状态按住 12 秒,开机并进入 OTA 广播,红绿交替闪。
- 当前版本:固件 V1.0.0、硬件 V1.0.0。
- 实际升级流程(写入芯片、校验、版本判断)待开发。
+1 -1
View File
@@ -9,7 +9,7 @@
[CmdletBinding()]
param(
[string]$ProjectName = "PICBC2",
[string]$ProjectName = "JBao_Door_Clamp",
[string]$SdkRoot = ""
)