一、user_svc2_wr_ind_handler函数
cpp
void user_svc2_wr_ind_handler(ke_msg_id_t const msgid,
struct custs1_val_write_ind const *param,
ke_task_id_t const dest_id,
ke_task_id_t const src_id)
{
// sprintf(buf2,"HEX %d :",param->length);
arch_printf("cmd HEX %d:", param->length);
for (int i = 0; i < param->length; i++)
{
arch_printf("%02X", param->value[i]);
}
arch_printf("\r\n");
if ((param->value[0] == 0xDD) && (param->length >= 5))
{
current_unix_time = (param->value[1] << 24) + (param->value[2] << 16) + (param->value[3] << 8) + (param->value[4] & 0xff);
tm_t tm = {0};
transformTime(current_unix_time, &tm);
app_easy_timer_cancel(timer_used_min);
time_offset = 60 - tm.tm_sec;
timer_used_min = app_easy_timer(time_offset * 100, do_min_work);
arch_printf("%d-%02d-%02d %02d:%02d:%02d %d\n", tm.tm_year + YEAR0,
tm.tm_mon + 1,
tm.tm_mday,
tm.tm_hour,
tm.tm_min,
tm.tm_sec,
tm.tm_wday);
}
else if (param->value[0] == 0xAA)
{
platform_reset(RESET_NO_ERROR);
}
else if (param->value[0] == 0x101)
{
do_rili_show();
is_part = 0;
step = 1;
display();
}
else if (param->value[0] == 0xE2)
{
do_time_show();
is_part = 0;
step = 1;
display();
}
}
这段代码实现了一个蓝牙服务写入指示处理函数 user_svc2_wr_ind_handler,用于解析来自客户端的命令并执行相应操作。以下是对代码的详细解释:
函数概述
该函数是一个事件处理回调,当接收到自定义蓝牙服务(custs1)的写请求时被触发。
它会解析命令数据并执行以下操作:
-
打印接收到的原始数据(HEX 格式)
-
根据命令类型(首字节)执行不同逻辑
◦ 0xDD:设置系统时间
◦ 0xAA:重启设备
◦ 0x101:显示日历
◦ 0xE2:显示时间
核心逻辑解析
1. 数据接收与打印
arch_printf("cmd HEX %d:", param->length);
for (int i = 0; i < param->length; i++)
{
arch_printf("%02X", param->value[i]);
}
arch_printf("\r\n");
• 功能:将接收到的字节流以 HEX 格式打印输出
• 参数:
◦ param->length:数据长度
◦ param->value[]:数据内容
2. 时间设置命令(0xDD)
if ((param->value[0] == 0xDD) && (param->length >= 5))
{
// 从字节流中解析Unix时间戳(大端格式)
current_unix_time = (param->value[1] << 24) + (param->value[2] << 16) +
(param->value[3] << 8) + (param->value[4] & 0xff);
// 转换为本地时间结构
tm_t tm = {0};
transformTime(current_unix_time, &tm);
// 重新同步定时器,确保整分钟触发
app_easy_timer_cancel(timer_used_min);
time_offset = 60 - tm.tm_sec;
timer_used_min = app_easy_timer(time_offset * 100, do_min_work);
// 打印格式化时间
arch_printf("%d-%02d-%02d %02d:%02d:%02d %d\n",
tm.tm_year + YEAR0, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_wday);
}
• 功能:通过蓝牙设置系统时间
• 数据格式:
◦ value[0]:命令头(0xDD)
◦ value[1-4]:Unix 时间戳(4 字节大端整数)
• 时间同步机制:
◦ 取消当前分钟定时器
◦ 计算距离下一个整分钟的剩余秒数(time_offset)
◦ 重新设置定时器,确保 do_min_work 在整分钟触发
3. 系统重启命令(0xAA)
else if (param->value[0] == 0xAA)
{
platform_reset(RESET_NO_ERROR);
}
• 功能:触发系统软复位
• 参数:RESET_NO_ERROR 表示正常重启(无错误)
4. 日历显示命令(0x101)
else if (param->value[0] == 0x101)
{
do_rili_show(); // 显示日历内容
is_part = 0;
step = 1;
display(); // 更新屏幕显示
}
• 功能:切换到日历显示模式
• 状态变量:
◦ is_part:可能表示部分更新标志
◦ step:状态机控制变量
5. 时间显示命令(0xE2)
else if (param->value[0] == 0xE2)
{
do_time_show(); // 显示时间内容
is_part = 0;
step = 1;
display(); // 更新屏幕显示
}
• 功能:切换到时间显示模式
• 与日历命令类似,但调用 do_time_show() 而非 do_rili_show()
潜在问题与优化建议
- 命令格式检查:
◦ 对于 0x101 和 0xE2 命令,未检查 param->length 是否足够,可能导致越界访问。
-
定时器精度: ◦ time_offset * 100 可能是笔误,应为 time_offset * 1000(毫秒转换)。
-
状态管理: ◦ is_part 和 step 作为全局变量,多命令并发时可能引发状态混乱。
-
错误处理:
◦ 未处理未知命令(首字节非 0xDD/0xAA/0x101/0xE2)的情况。
应用场景 该处理函数常见于蓝牙低功耗(BLE)设备中,用于接收手机 APP 发送的控制命令,实现:
• 时间同步(通过 NTP 或手机时间)
• 远程重启设备 • 界面显示切换(时钟 / 日历) 通过蓝牙协议栈的 GATT 服务,客户端可以向设备写入特定命令,触发相应功能。
二、app_easy_timer_cancel函数
cpp
void app_easy_timer_cancel(const timer_hnd timer_id)
{
if APP_EASY_TIMER_HND_IS_VALID(timer_id)
{
if ((timer_callbacks[APP_EASY_TIMER_HND_TO_IDX(timer_id)] != NULL) &&
(timer_callbacks[APP_EASY_TIMER_HND_TO_IDX(timer_id)] != timer_canceled_handler))
{
// Remove the timer from the timer queue
ke_timer_clear(APP_EASY_TIMER_HND_TO_MSG_ID(timer_id), TASK_APP);
timer_callbacks[APP_EASY_TIMER_HND_TO_IDX(timer_id)] = timer_canceled_handler;
/*
Send a message to the kernel in order to clear the timer callback function and
free the respective position in the timers callback array.
The app_easy_timer_cancel() function cannot guarantee if a timer has entered
the message queue or not. Therefore a message must be sent to the kernel and
inform it about the requested cancel operation.
*/
struct cancel_timer_struct *req = KE_MSG_ALLOC(APP_CANCEL_TIMER, TASK_APP, TASK_APP,
cancel_timer_struct);
req->timer_id = timer_id;
ke_msg_send(req);
}
else
{
ASSERT_WARNING(0);
}
}
else
{
ASSERT_WARNING(0);
}
}