一、user_svc2_wr_ind_handler函數
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)的寫請求時被觸發。
它會解析命令數據并執行以下操作:
1. 打印接收到的原始數據(HEX 格式)
2. 根據命令類型(首字節)執行不同邏輯
? 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() ?
潛在問題與優化建議
1. ?命令格式檢查:
? 對于 0x101 和 0xE2 命令,未檢查 param->length 是否足夠,可能導致越界訪問。 ?
2. ?定時器精度: ? time_offset * 100 可能是筆誤,應為 time_offset * 1000(毫秒轉換)。 ?
3. ?狀態管理: ? is_part 和 step 作為全局變量,多命令并發時可能引發狀態混亂。 ?
4. ?錯誤處理:
? 未處理未知命令(首字節非 0xDD/0xAA/0x101/0xE2)的情況。 ? ?
應用場景 該處理函數常見于藍牙低功耗(BLE)設備中,用于接收手機 APP 發送的控制命令,實現:
? 時間同步(通過 NTP 或手機時間)
? 遠程重啟設備 ? 界面顯示切換(時鐘 / 日歷) ?通過藍牙協議棧的 GATT 服務,客戶端可以向設備寫入特定命令,觸發相應功能。
二、app_easy_timer_cancel函數
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 queueke_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 andfree the respective position in the timers callback array.The app_easy_timer_cancel() function cannot guarantee if a timer has enteredthe message queue or not. Therefore a message must be sent to the kernel andinform 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);}
}