修复一下问题:
A用户绑定IPC和门夹弹力绳,分享给B用户; A用户先进入门夹弹力绳页面,已唤醒玩具,B用户再进入页面 A用户退出页面下发休眠指令,B用户操作玩具,未提示操作成功/失败
This commit is contained in:
@@ -2,8 +2,8 @@
|
|||||||
* @file app_function.c
|
* @file app_function.c
|
||||||
* @brief 设备功能开关 + 自动玩法 + 手动点动 + 按键动作实现
|
* @brief 设备功能开关 + 自动玩法 + 手动点动 + 按键动作实现
|
||||||
* @author cyWu <1917507415@qq.com>
|
* @author cyWu <1917507415@qq.com>
|
||||||
* @date 2026.08.26
|
* @date 2026.09.10
|
||||||
* @version V1.1.0
|
* @version V1.2.0
|
||||||
* @history
|
* @history
|
||||||
* - V1.0.0, 2026.08.25, cyWu, 首次发布,实现 DP0 开关、自动玩法、手动点动与按键动作
|
* - V1.0.0, 2026.08.25, cyWu, 首次发布,实现 DP0 开关、自动玩法、手动点动与按键动作
|
||||||
* - V1.1.0, 2026.08.26, cyWu, 切模式闪灯改到 function_play_mode_apply 统一
|
* - V1.1.0, 2026.08.26, cyWu, 切模式闪灯改到 function_play_mode_apply 统一
|
||||||
@@ -11,6 +11,8 @@
|
|||||||
* 启动也补上闪灯;DP2 停转(cmd=3)改边沿触发,
|
* 启动也补上闪灯;DP2 停转(cmd=3)改边沿触发,
|
||||||
* 避免残留的 03 一直把刚启动的自动模式打断;
|
* 避免残留的 03 一直把刚启动的自动模式打断;
|
||||||
* 自动模式新增单次最长运行时长,到点回待机
|
* 自动模式新增单次最长运行时长,到点回待机
|
||||||
|
* - V1.2.0, 2026.09.10, cyWu, 软关后 APP 下发其它控制指令也可唤醒,
|
||||||
|
* 唤醒后本拍继续执行该指令(玩法/点动)
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
#include "app_function.h"
|
#include "app_function.h"
|
||||||
@@ -39,11 +41,13 @@ typedef struct {
|
|||||||
uint16_t hand_ms;
|
uint16_t hand_ms;
|
||||||
volatile uint8_t hand_write_pending; /**< DP2 刚写入(03 停转要靠事件) */
|
volatile uint8_t hand_write_pending; /**< DP2 刚写入(03 停转要靠事件) */
|
||||||
volatile uint8_t hand_write_cmd;
|
volatile uint8_t hand_write_cmd;
|
||||||
|
volatile uint8_t app_cmd_wake; /**< 软关后收到其它 APP 控制 DP */
|
||||||
} AppFunctionCtx_t;
|
} AppFunctionCtx_t;
|
||||||
|
|
||||||
static AppFunctionCtx_t s_func;
|
static AppFunctionCtx_t s_func;
|
||||||
|
|
||||||
static void function_hand_stop(void);
|
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_apply(uint8_t mode);
|
||||||
static void function_play_mode_poll(void);
|
static void function_play_mode_poll(void);
|
||||||
static void function_play_mode_timeout(uint16_t dt);
|
static void function_play_mode_timeout(uint16_t dt);
|
||||||
@@ -70,18 +74,45 @@ void app_function_dp_hand_write(uint8_t cmd)
|
|||||||
s_func.hand_write_pending = 1;
|
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 手动点动
|
* @brief 跟随 APP 下发的 DP0/DP1,并处理 DP2 手动点动
|
||||||
* @return 无
|
* @return 无
|
||||||
*/
|
*/
|
||||||
void app_function_poll(void)
|
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 (!app_power_usb_online() && !app_power_poweroff_pending()) {
|
||||||
if (gDevData.power && !s_func.app_power) {
|
if (!s_func.app_power && cmd_wake) {
|
||||||
s_func.app_power = 1;
|
function_app_power_on("APP cmd wake from soft off");
|
||||||
app_led_hint_power_on();
|
} else if (gDevData.power && !s_func.app_power) {
|
||||||
log_info("APP power on\n");
|
function_app_power_on("APP power on");
|
||||||
} else if (!gDevData.power && s_func.app_power) {
|
} else if (!gDevData.power && s_func.app_power) {
|
||||||
app_function_soft_off();
|
app_function_soft_off();
|
||||||
}
|
}
|
||||||
@@ -133,7 +164,7 @@ void app_function_stop_all(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 软关:电机停、DP0=0,等 APP/短按再开。不断 BLE
|
* @brief 软关:电机停、DP0=0,等 APP 开机/其它指令/短按再开。不断 BLE
|
||||||
* @return 无
|
* @return 无
|
||||||
*/
|
*/
|
||||||
void app_function_soft_off(void)
|
void app_function_soft_off(void)
|
||||||
@@ -145,7 +176,7 @@ void app_function_soft_off(void)
|
|||||||
gDevData.play_mode = 0;
|
gDevData.play_mode = 0;
|
||||||
app_mode_stop();
|
app_mode_stop();
|
||||||
function_hand_stop();
|
function_hand_stop();
|
||||||
log_info("soft off, wait APP power on\n");
|
log_info("soft off, wait APP cmd or key\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2,10 +2,11 @@
|
|||||||
* @file app_function.h
|
* @file app_function.h
|
||||||
* @brief 设备功能开关(DP0)+ 自动玩法(DP1)+ 手动点动(DP2)+ 按键动作
|
* @brief 设备功能开关(DP0)+ 自动玩法(DP1)+ 手动点动(DP2)+ 按键动作
|
||||||
* @author cyWu <1917507415@qq.com>
|
* @author cyWu <1917507415@qq.com>
|
||||||
* @date 2026.08.25
|
* @date 2026.09.10
|
||||||
* @version V1.0.0
|
* @version V1.2.0
|
||||||
* @history
|
* @history
|
||||||
* - V1.0.0, 2026.08.25, cyWu, 首次发布,实现 DP0 开关、自动玩法、手动点动与按键动作
|
* - V1.0.0, 2026.08.25, cyWu, 首次发布,实现 DP0 开关、自动玩法、手动点动与按键动作
|
||||||
|
* - V1.2.0, 2026.09.10, cyWu, 软关后 APP 下发其它控制 DP 也可唤醒功能
|
||||||
*
|
*
|
||||||
* @note 本模块决定“电机现在该不该转、转哪种”,会读 app_power/app_pair 的
|
* @note 本模块决定“电机现在该不该转、转哪种”,会读 app_power/app_pair 的
|
||||||
* 状态作为阻塞条件(充电中/深睡中/配对中都不能转),也会触发
|
* 状态作为阻塞条件(充电中/深睡中/配对中都不能转),也会触发
|
||||||
@@ -30,6 +31,13 @@ void app_function_init(void);
|
|||||||
*/
|
*/
|
||||||
void app_function_dp_hand_write(uint8_t cmd);
|
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 调用一次
|
* @brief 跟随 APP 下发的 DP0/DP1,并处理 DP2 手动点动,每个业务 tick 调用一次
|
||||||
* @return 无
|
* @return 无
|
||||||
@@ -50,7 +58,7 @@ void app_function_tick_1ms(uint16_t dt);
|
|||||||
void app_function_stop_all(void);
|
void app_function_stop_all(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 软关:电机停、DP0=0,等 APP/短按再开(USB 拔出 / APP 关时用)
|
* @brief 软关:电机停、DP0=0,等 APP 开机/其它指令/短按再开(USB 拔出 / APP 关时用)
|
||||||
* @return 无
|
* @return 无
|
||||||
*/
|
*/
|
||||||
void app_function_soft_off(void);
|
void app_function_soft_off(void);
|
||||||
|
|||||||
@@ -69,6 +69,9 @@ void userHandle(void)
|
|||||||
* ════════════════════════════════════════════════════════════════ */
|
* ════════════════════════════════════════════════════════════════ */
|
||||||
int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *ctrl)
|
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)
|
if (!info || !ctrl)
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
@@ -89,6 +92,7 @@ int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *ctrl)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
gDevData.power = 0;
|
gDevData.power = 0;
|
||||||
|
got_power_off = 1;
|
||||||
// TODO: 关 — 执行动作
|
// TODO: 关 — 执行动作
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -96,6 +100,7 @@ int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *ctrl)
|
|||||||
/* ── DP1 自动模式 (enum) ── */
|
/* ── DP1 自动模式 (enum) ── */
|
||||||
case DP_ID_PLAY_MODE:
|
case DP_ID_PLAY_MODE:
|
||||||
gDevData.play_mode = ctrl->play_mode;
|
gDevData.play_mode = ctrl->play_mode;
|
||||||
|
other_cmd = 1;
|
||||||
switch (ctrl->play_mode)
|
switch (ctrl->play_mode)
|
||||||
{
|
{
|
||||||
case PLAY_MODE_0: /* 停止复位 */
|
case PLAY_MODE_0: /* 停止复位 */
|
||||||
@@ -127,6 +132,7 @@ int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *ctrl)
|
|||||||
/* ── DP2 手动模式 (raw) ── */
|
/* ── DP2 手动模式 (raw) ── */
|
||||||
case DP_ID_HAND_MODE:
|
case DP_ID_HAND_MODE:
|
||||||
memcpy(gDevData.hand_mode, ctrl->hand_mode, 2);
|
memcpy(gDevData.hand_mode, ctrl->hand_mode, 2);
|
||||||
|
other_cmd = 1;
|
||||||
/* 薄适配:通知业务层;03=停,00=空闲 */
|
/* 薄适配:通知业务层;03=停,00=空闲 */
|
||||||
{
|
{
|
||||||
extern void usr_dp_hand_write(uint8_t cmd);
|
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) ── */
|
/* ── DP3 绳索校准 (bool) ── */
|
||||||
case DP_ID_CALIBRATION:
|
case DP_ID_CALIBRATION:
|
||||||
|
other_cmd = 1;
|
||||||
if (ctrl->calibration)
|
if (ctrl->calibration)
|
||||||
{
|
{
|
||||||
gDevData.calibration = 1;
|
gDevData.calibration = 1;
|
||||||
@@ -151,30 +158,35 @@ int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *ctrl)
|
|||||||
/* ── DP4 绳索圈数 (uint8) ── */
|
/* ── DP4 绳索圈数 (uint8) ── */
|
||||||
case DP_ID_NUMBERTURNS:
|
case DP_ID_NUMBERTURNS:
|
||||||
gDevData.numberturns = ctrl->numberturns;
|
gDevData.numberturns = ctrl->numberturns;
|
||||||
|
other_cmd = 1;
|
||||||
// TODO: 根据 ctrl->numberturns 执行动作
|
// TODO: 根据 ctrl->numberturns 执行动作
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* ── DP8 勿扰模式开关 (raw) ── */
|
/* ── DP8 勿扰模式开关 (raw) ── */
|
||||||
case DP_ID_NO_DISTURB_ENABLE:
|
case DP_ID_NO_DISTURB_ENABLE:
|
||||||
memcpy(gDevData.no_disturb_enable, ctrl->no_disturb_enable, 13);
|
memcpy(gDevData.no_disturb_enable, ctrl->no_disturb_enable, 13);
|
||||||
|
other_cmd = 1;
|
||||||
// TODO: 根据 gDevData.no_disturb_enable 执行动作 (长度 13 字节)
|
// TODO: 根据 gDevData.no_disturb_enable 执行动作 (长度 13 字节)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* ── DP9 预留DP1 (uint32) ── */
|
/* ── DP9 预留DP1 (uint32) ── */
|
||||||
case DP_ID_RESERVA_DP1:
|
case DP_ID_RESERVA_DP1:
|
||||||
gDevData.reserva_dp1 = ctrl->reserva_dp1;
|
gDevData.reserva_dp1 = ctrl->reserva_dp1;
|
||||||
|
other_cmd = 1;
|
||||||
// TODO: 根据 ctrl->reserva_dp1 执行动作
|
// TODO: 根据 ctrl->reserva_dp1 执行动作
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* ── DP10 预留DP2 (uint32) ── */
|
/* ── DP10 预留DP2 (uint32) ── */
|
||||||
case DP_ID_RESERVA_DP2:
|
case DP_ID_RESERVA_DP2:
|
||||||
gDevData.reserva_dp2 = ctrl->reserva_dp2;
|
gDevData.reserva_dp2 = ctrl->reserva_dp2;
|
||||||
|
other_cmd = 1;
|
||||||
// TODO: 根据 ctrl->reserva_dp2 执行动作
|
// TODO: 根据 ctrl->reserva_dp2 执行动作
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* ── DP11 预留DP3 (uint32) ── */
|
/* ── DP11 预留DP3 (uint32) ── */
|
||||||
case DP_ID_RESERVA_DP3:
|
case DP_ID_RESERVA_DP3:
|
||||||
gDevData.reserva_dp3 = ctrl->reserva_dp3;
|
gDevData.reserva_dp3 = ctrl->reserva_dp3;
|
||||||
|
other_cmd = 1;
|
||||||
// TODO: 根据 ctrl->reserva_dp3 执行动作
|
// TODO: 根据 ctrl->reserva_dp3 执行动作
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -182,6 +194,13 @@ int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *ctrl)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 软关后其它控制 DP 可唤醒;与关机 DP 同帧则关机优先,避免关完立刻被拉开 */
|
||||||
|
if (other_cmd && !got_power_off)
|
||||||
|
{
|
||||||
|
extern void usr_dp_app_cmd(void);
|
||||||
|
usr_dp_app_cmd();
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -306,6 +306,15 @@ void usr_dp_hand_write(uint8_t cmd)
|
|||||||
app_function_dp_hand_write(cmd);
|
app_function_dp_hand_write(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief APP 下发了非关机控制 DP(jb_product 薄适配)
|
||||||
|
* @return 无
|
||||||
|
*/
|
||||||
|
void usr_dp_app_cmd(void)
|
||||||
|
{
|
||||||
|
app_function_on_app_cmd();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief BLE 收到见宝载荷,送入协议环形缓冲
|
* @brief BLE 收到见宝载荷,送入协议环形缓冲
|
||||||
* @param data 载荷指针
|
* @param data 载荷指针
|
||||||
|
|||||||
+76
-33
@@ -1,11 +1,12 @@
|
|||||||
# 门夹弹力绳 需求文档(V1.1)
|
# 门夹弹力绳 需求文档(V1.3)
|
||||||
|
|
||||||
| 项目 | 内容 |
|
| 项目 | 内容 |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| 产品名称 | 门夹弹力绳(智能逗宠玩具) |
|
| 产品名称 | 门夹弹力绳(智能逗宠玩具) |
|
||||||
| 型号 | PICBC2 / CBC2 |
|
| 型号 | PICBC2 / CBC2 |
|
||||||
| 主控 | 杰理 AC7016C |
|
| 主控 | 杰理 AC7016C |
|
||||||
| 日期 | 2026-08-25 |
|
| 日期 | 2026-09-10 |
|
||||||
|
| 修订 | V1.3:App 软关后,再下发其它控制指令也可退出关机 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -13,26 +14,39 @@
|
|||||||
|
|
||||||
门夹弹力绳是一款智能逗宠玩具。设备夹在门板上,内部电机带动弹力绳收放,让绳端玩具按不同"玩法"动起来逗宠物玩。支持手机 App 通过远程控制,也支持按键本地操作。
|
门夹弹力绳是一款智能逗宠玩具。设备夹在门板上,内部电机带动弹力绳收放,让绳端玩具按不同"玩法"动起来逗宠物玩。支持手机 App 通过远程控制,也支持按键本地操作。
|
||||||
|
|
||||||
|
硬件:红绿双指示灯(低电平亮)+ 单按键(低电平按下)。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2. 核心功能
|
## 2. 核心功能
|
||||||
|
|
||||||
### 2.1 开关机
|
### 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 控制)
|
### 2.2 开关(App 控制)
|
||||||
|
|
||||||
- App 可开/关设备功能;关闭后电机停转,蓝牙不断开,可随时再开。
|
- App 可开/关设备功能;关闭后电机停转,蓝牙不断开,可随时再开。
|
||||||
|
- 功能关闭后,以下操作都会退出软关并恢复功能:
|
||||||
|
- App 下发开机 DP
|
||||||
|
- App 再下发其它控制指令(自动玩法、手动点动等),并按该指令继续执行
|
||||||
|
- 短按按键:只唤醒功能,**不切玩法**
|
||||||
- 拔掉充电线会自动进入低功耗状态(电机停转、LED 关闭、蓝牙保持连接)。
|
- 拔掉充电线会自动进入低功耗状态(电机停转、LED 关闭、蓝牙保持连接)。
|
||||||
|
- 充电中、正在深睡关机:其它指令不会把功能拉开。
|
||||||
|
|
||||||
### 2.3 自动玩法(6 种,App 或按键选择)
|
### 2.3 自动玩法(6 种,App 或按键选择)
|
||||||
|
|
||||||
@@ -48,46 +62,74 @@
|
|||||||
|
|
||||||
- 玩法会循环播放,直到切换或停止。
|
- 玩法会循环播放,直到切换或停止。
|
||||||
- 运行中可随时切换到其他玩法或停止。
|
- 运行中可随时切换到其他玩法或停止。
|
||||||
|
- 单次最长运行 **10 分钟**,到点自动停转回待机(保护性超时,不闪灯,避免误以为被按到)。
|
||||||
|
- 充电中、配对中、正在深睡关机:不允许跑自动玩法,强制回到停转。
|
||||||
|
- 功能关闭时若 App 下发玩法/点动:先退出软关,再按该指令执行。
|
||||||
|
|
||||||
### 2.4 手动控制(App 控制)
|
### 2.4 手动控制(App 控制)
|
||||||
|
|
||||||
- App 可手动点动:控制电机**收绳**或**放绳**,松手/点停止后电机停止。
|
- App 可手动点动:控制电机**收绳**或**放绳**,松手/点停止后电机停止。
|
||||||
- 手动控制进行中,自动玩法暂停。
|
- 手动控制进行中,自动玩法暂停。
|
||||||
|
- 从自动/空闲切到手动时,绿灯闪两下提示。
|
||||||
|
- 手动点动进行中,短按按键无效,避免打断收放绳。
|
||||||
|
- 选中自动玩法会先停手动,再进入自动模式。
|
||||||
|
|
||||||
### 2.5 按键控制
|
### 2.5 按键控制
|
||||||
|
|
||||||
| 操作 | 行为 |
|
开机后只有一颗按键,短按/长按含义如下:
|
||||||
| --- | --- |
|
|
||||||
| 短按(<1 秒) | 切换玩法:停 → 玩法1 → 玩法2 → … → 玩法6 → 停,循环 |
|
| 操作 | 条件 | 行为 |
|
||||||
| 长按 3 秒 | 关机 |
|
| --- | --- | --- |
|
||||||
| 充电中 / 配对中 | 按键无效 |
|
| 短按(松开且未到 3 秒) | 功能开、空闲 | 切换玩法:停 → 玩法1 → … → 玩法6 → 停,循环;绿灯闪两下 |
|
||||||
|
| 短按 | 功能已关(App 软关 / 拔充电线后) | 只唤醒功能,不切玩法;挂待机绿灯 |
|
||||||
|
| 短按 | 手动点动进行中 | 无效 |
|
||||||
|
| 长按 3 秒 | 功能开 | 关机进深睡 |
|
||||||
|
| 短按 / 长按 | 充电中 / 配对中 / 正在关机 | 无效 |
|
||||||
|
|
||||||
### 2.6 电量与充电
|
### 2.6 电量与充电
|
||||||
|
|
||||||
- 电量按电压换算:满电 4.2V = 100%,3.2V = 0%,电量自动上报 App 显示。
|
- 电量按电压换算:满电约 4.12V = 100%,3.2V = 0%,电量自动上报 App 显示。充电内阻压降下,充满基准按 4.2V 计。
|
||||||
- **低电量**(电池电压低于 **3.7V**):App 上报警告,指示灯红灯慢闪;持续 1 分钟未充电则自动关机保护电池。
|
- **低电量**(电池电压低于 **3.7V**):App 上报警告,指示灯红灯慢闪。只提示、不关机;电压回升超过 3.75V 后退出低电。
|
||||||
- **电量极低**(低于 3.2V):直接自动关机。
|
- **空电关机**(显示电量 ≤ **5%**):立刻停电机,红灯闪 5 下后自动关机保护电池。充电中不判定。
|
||||||
- **插入充电线**:电机立即停止、进入充电状态,只有红灯常亮,表示充电中,充满转绿灯常亮;充电期间不执行玩法,按键无效。
|
- **开机电量过低**(未充电且裸电压约 ≤ 3.25V,对应约 5%):拒绝开机,红闪 5 下后继续深睡。
|
||||||
- **充电中拔掉充电线**:设备自动进入低功耗状态(电机停转、LED关闭、蓝牙保持连接),需按短按按键或 App 重新开启后才能使用。
|
- 开机后约 3 秒内不因低电/空电关机,避免上电瞬间误判。
|
||||||
- 电池:锂电池,USB 5V 充电,充满自动转灯。
|
- **插入充电线**:电机立即停止、进入充电状态,红灯常亮表示充电中,充满转绿灯常亮;充电期间不执行玩法,按键无效。功能关掉时充电灯仍继续亮。
|
||||||
|
- **充电中拔掉充电线**:设备自动进入低功耗状态(电机停转、LED 关闭、蓝牙保持连接),需短按按键或 App 重新开启后才能使用。
|
||||||
|
- 电池:锂电池,USB 5V 充电,充满自动转灯;硬件判定充满后电量强制收尾到 100%。
|
||||||
|
|
||||||
### 2.7 指示灯
|
### 2.7 指示灯
|
||||||
|
|
||||||
| 状态 | 灯效 |
|
红绿双灯,通道独立,可以同时亮。充电灯和功能开关无关。
|
||||||
| --- | --- |
|
|
||||||
| 正常待机 | 绿灯常亮 |
|
| 状态 | 灯效 | 节奏 |
|
||||||
| 充电中 | 红灯常亮 |
|
| --- | --- | --- |
|
||||||
| 充满 | 绿灯常亮 |
|
| 正常待机(功能开、没充电) | 绿灯常亮 | 一直亮 |
|
||||||
| 低电量 | 红灯慢闪 |
|
| 充电中 | 红灯常亮 | 一直亮 |
|
||||||
| 配对中 | 绿灯快闪 |
|
| 充满 | 绿灯常亮 | 一直亮 |
|
||||||
| 配对成功 | 绿灯常亮 3 秒 |
|
| 低电量(功能开、没接电) | 红灯慢闪 | 亮 200ms / 周期 2s |
|
||||||
| 配对失败 | 红绿交替闪 3 秒 |
|
| 配对中 | 绿灯快闪 | 100ms 亮 / 100ms 灭 |
|
||||||
| 按键切玩法 | 绿灯闪两下 |
|
| 配对成功 | 绿灯常亮 3 秒 | 一次性 |
|
||||||
|
| 配对失败 | 红绿交替 3 秒 | 200ms 半周期 |
|
||||||
|
| 切玩法 / 切手动 | 绿灯闪两下 | 150ms 灭 + 150ms 亮 ×2 |
|
||||||
|
| OTA | 红绿交替,独占全部灯 | 200ms 半周期,一直闪到升级结束/复位 |
|
||||||
|
| 运行中空电关机 | 关绿灯,红灯闪 5 下再掉电 | 200ms 亮/灭 ×5 |
|
||||||
|
| 开机电量过低被拒 | 红灯闪 5 下后继续深睡 | 同上 |
|
||||||
|
|
||||||
|
优先级(高的盖住低的):
|
||||||
|
|
||||||
|
1. OTA / 空电关机红闪 / 切模式绿闪
|
||||||
|
2. 配对快闪、配对成功、配对失败
|
||||||
|
3. 充电红 / 充满绿 / 低电红闪
|
||||||
|
4. 待机绿灯(最低)
|
||||||
|
|
||||||
|
OTA 和空电关机会清掉其它灯,只跑自己那一种。功能关掉时,待机/配对/切模式/低电灯会清掉,**充电灯保留**。
|
||||||
|
|
||||||
### 2.8 配对
|
### 2.8 配对
|
||||||
|
|
||||||
- 首次使用:深睡状态长按 5 秒开机并进入配对,App 扫码连接即可。
|
- 首次使用:深睡状态长按 5 秒后松手,开机并进入配对,App 扫码连接即可。
|
||||||
- 配对窗口 120 秒:期间连接成功绿灯 3 秒;超时未连接保持开机,可重新配对。
|
- 按满 5 秒绿灯先切到配对快闪;若继续按到 12 秒则改道 OTA,不再提交配对。
|
||||||
|
- 配对窗口 120 秒:期间连接成功绿灯常亮 3 秒;超时未连接红绿交替 3 秒,之后保持开机,可重新配对。
|
||||||
|
- 配对期间不跑玩法、按键无效。
|
||||||
- App 可解绑设备,解绑后设备清除配对信息,重新配对即可再次使用。
|
- App 可解绑设备,解绑后设备清除配对信息,重新配对即可再次使用。
|
||||||
|
|
||||||
### 2.9 时间同步
|
### 2.9 时间同步
|
||||||
@@ -97,6 +139,7 @@
|
|||||||
### 2.10 固件升级(OTA)
|
### 2.10 固件升级(OTA)
|
||||||
|
|
||||||
- 支持 App 远程升级固件,升级协议已打通。
|
- 支持 App 远程升级固件,升级协议已打通。
|
||||||
|
- 本地入口:深睡状态按住 12 秒,开机并进入 OTA 广播,红绿交替闪。
|
||||||
- 当前版本:固件 V1.0.0、硬件 V1.0.0。
|
- 当前版本:固件 V1.0.0、硬件 V1.0.0。
|
||||||
- 实际升级流程(写入芯片、校验、版本判断)待开发。
|
- 实际升级流程(写入芯片、校验、版本判断)待开发。
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user