전원 + BLE + 배터리
This commit is contained in:
+171
@@ -0,0 +1,171 @@
|
||||
/*******************************************************************************
|
||||
* @file parser.c
|
||||
* @brief BLE command parser (Zephyr port)
|
||||
*
|
||||
* 원본: pc_firm/parser.c
|
||||
* 패킷 포맷: [TAG 4B] [DATA NB] [CRC16 2B]
|
||||
*
|
||||
* 현재 구현된 커맨드: msn? (배터리 측정), mls? (LED 상태 설정)
|
||||
******************************************************************************/
|
||||
#include <zephyr/kernel.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "parser.h"
|
||||
#include "main.h"
|
||||
#include "debug_print.h"
|
||||
#include "ble_service.h"
|
||||
#include "battery_adc.h"
|
||||
#include "led_control.h"
|
||||
|
||||
/*==============================================================================
|
||||
* CRC16 (CRC-CCITT, Nordic SDK 호환)
|
||||
*============================================================================*/
|
||||
static uint16_t dr_crc16_compute(const uint8_t *p_data, uint32_t size)
|
||||
{
|
||||
uint16_t crc = 0xFFFF;
|
||||
|
||||
for (uint32_t i = 0; i < size; i++)
|
||||
{
|
||||
crc = (uint8_t)(crc >> 8) | (crc << 8);
|
||||
crc ^= p_data[i];
|
||||
crc ^= (uint8_t)(crc & 0xFF) >> 4;
|
||||
crc ^= (crc << 8) << 4;
|
||||
crc ^= ((crc & 0xFF) << 4) << 1;
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* 응답 패킷 전송
|
||||
*============================================================================*/
|
||||
|
||||
/* TAG(4B) + uint16 값(2B) + CRC16(2B) = 8바이트 전송 */
|
||||
static void send_response_u16(const char *tag, uint16_t value)
|
||||
{
|
||||
uint8_t buf[8];
|
||||
|
||||
buf[0] = tag[0];
|
||||
buf[1] = tag[1];
|
||||
buf[2] = tag[2];
|
||||
buf[3] = tag[3];
|
||||
buf[4] = (uint8_t)(value >> 8);
|
||||
buf[5] = (uint8_t)(value & 0xFF);
|
||||
|
||||
uint16_t crc = dr_crc16_compute(buf, 6);
|
||||
buf[6] = (uint8_t)(crc & 0xFF);
|
||||
buf[7] = (uint8_t)(crc >> 8);
|
||||
|
||||
ble_data_send(buf, 8);
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* 커맨드 핸들러
|
||||
*============================================================================*/
|
||||
|
||||
/* msn? → 배터리 전압 측정 → rsn: + mV */
|
||||
|
||||
static int cmd_msn(const uint8_t *data, uint8_t data_len)
|
||||
{
|
||||
int mv = battery_read_mv();
|
||||
if (mv < 0)
|
||||
{
|
||||
mv = 0;
|
||||
}
|
||||
|
||||
send_response_u16("rsn:", (uint16_t)mv);
|
||||
DBG_PRINTF("[CMD] msn -> %d mV\r\n", mv);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* mls? → LED 상태 변경 → rls: + state echo back
|
||||
* 파라미터: [state(2B LE)] — led_state_t enum 값
|
||||
* 0=OFF, 4=DETACH_WARNING, 5=ALIGN_SEARCHING, 6=ALIGN_COMPLETE, 7=ERROR
|
||||
* 에러 응답: 0xFFFF=파라미터 없음, 0xFFFE=범위 초과 */
|
||||
static int cmd_mls(const uint8_t *data, uint8_t data_len)
|
||||
{
|
||||
/* 파라미터 부족 → 에러 코드 0xFFFF 에코 */
|
||||
if (data_len < 2)
|
||||
{
|
||||
send_response_u16("rls:", 0xFFFF);
|
||||
DBG_PRINTF("[CMD] mls: no data\r\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 리틀엔디안으로 읽기 (앱이 LE로 전송) */
|
||||
uint16_t state = (uint16_t)(data[0]) | ((uint16_t)(data[1]) << 8);
|
||||
|
||||
/* 범위 초과 → 에러 코드 0xFFFE 에코 */
|
||||
if (state > LED_STATE_ERROR)
|
||||
{
|
||||
send_response_u16("rls:", 0xFFFE);
|
||||
DBG_PRINTF("[CMD] mls: invalid state %d\r\n", state);
|
||||
return 1;
|
||||
}
|
||||
|
||||
led_set_state((led_state_t)state);
|
||||
send_response_u16("rls:", state);
|
||||
DBG_PRINTF("[CMD] mls -> LED state=%d\r\n", state);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* 커맨드 테이블
|
||||
*============================================================================*/
|
||||
typedef struct
|
||||
{
|
||||
char tag[5];
|
||||
int (*handler)(const uint8_t *data, uint8_t data_len);
|
||||
} cmd_entry_t;
|
||||
|
||||
static const cmd_entry_t cmd_table[] =
|
||||
{
|
||||
{ "msn?", cmd_msn },
|
||||
{ "mls?", cmd_mls },
|
||||
};
|
||||
|
||||
#define CMD_TABLE_SIZE (sizeof(cmd_table) / sizeof(cmd_table[0]))
|
||||
|
||||
/*==============================================================================
|
||||
* 파서 엔트리
|
||||
*============================================================================*/
|
||||
int dr_parser(const uint8_t *buf, uint16_t len)
|
||||
{
|
||||
/* 최소 4바이트 TAG 필요 */
|
||||
if (len < 4)
|
||||
{
|
||||
DBG_PRINTF("[CMD] Too short (%d)\r\n", len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* CRC16 검증 (6바이트 이상이면 마지막 2바이트가 CRC) */
|
||||
if (len >= 6)
|
||||
{
|
||||
uint16_t calc_crc = dr_crc16_compute(buf, len - 2);
|
||||
uint16_t recv_crc = (uint16_t)(buf[len - 2]) | ((uint16_t)(buf[len - 1]) << 8);
|
||||
|
||||
if (calc_crc != recv_crc)
|
||||
{
|
||||
DBG_PRINTF("[CMD] CRC fail (calc=0x%04X recv=0x%04X)\r\n", calc_crc, recv_crc);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* TAG 추출 (4바이트) */
|
||||
char tag[5] = { buf[0], buf[1], buf[2], buf[3], '\0' };
|
||||
|
||||
/* 데이터 부분 (TAG 이후, CRC 이전) */
|
||||
const uint8_t *data = buf + 4;
|
||||
uint8_t data_len = (len >= 6) ? (len - 4 - 2) : (len - 4);
|
||||
|
||||
/* 테이블 검색 + 디스패치 */
|
||||
for (int i = 0; i < CMD_TABLE_SIZE; i++)
|
||||
{
|
||||
if (memcmp(tag, cmd_table[i].tag, 4) == 0)
|
||||
{
|
||||
return cmd_table[i].handler(data, data_len);
|
||||
}
|
||||
}
|
||||
|
||||
DBG_PRINTF("[CMD] Unknown: %s\r\n", tag);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user