전원 + BLE + 배터리

This commit is contained in:
jhChun
2026-04-10 17:57:09 +09:00
parent 750f2d139e
commit 8dcf4adf31
22 changed files with 1791 additions and 175 deletions
+171
View File
@@ -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;
}