Initial Commit

This commit is contained in:
2026-07-06 11:14:35 +09:00
parent e6de6e8cc3
commit e55b283b39
31 changed files with 1023 additions and 2123 deletions
+7 -27
View File
@@ -11,9 +11,7 @@
#include "debug_print.h"
#include "ble_service.h"
/*==============================================================================
* Command response state
*============================================================================*/
/* Command response state */
static uint8_t tx_u16_buf[8];
static uint8_t tx_imu_buf[18];
static uint8_t tx_echo_buf[4 + 2 + 2 + (PIEZO_MEASURE_MAX_SAMPLES * 2) + 2];
@@ -35,9 +33,7 @@ void cmd_reboot_after_response(void)
sys_reboot(SYS_REBOOT_COLD);
}
/*==============================================================================
* CRC16 (CRC-CCITT, Nordic SDK 호환)
*============================================================================*/
/* CRC16 (CRC-CCITT, Nordic SDK 호환) */
uint16_t cmd_crc16_compute(const uint8_t *p_data, uint32_t size)
{
uint16_t crc = 0xFFFF;
@@ -53,8 +49,7 @@ uint16_t cmd_crc16_compute(const uint8_t *p_data, uint32_t size)
return crc;
}
bool cmd_get_data_u16_be(const uint8_t *data, uint8_t data_len,
uint8_t word_index, uint16_t *out)
bool cmd_get_data_u16_be(const uint8_t *data, uint8_t data_len, uint8_t word_index, uint16_t *out)
{
uint8_t offset = (uint8_t)(word_index * 2U);
@@ -88,11 +83,7 @@ char cmd_ascii_to_lower(char ch)
return ch;
}
/*==============================================================================
* 응답 패킷 전송
*============================================================================*/
/* TAG(4B) + uint16 값(2B) + CRC16(2B) = 8바이트 전송 */
/* 응답 패킷 전송: TAG(4B) + uint16 값(2B) + CRC16(2B) = 8바이트 전송 */
int cmd_send_response_u16(const char *tag, uint16_t value)
{
uint8_t *buf = tx_u16_buf;
@@ -170,12 +161,7 @@ int cmd_send_response_tag_echo(const char *tag, const char *echo_tag)
return ble_data_send(buf, 10);
}
/*==============================================================================
* 응답 패킷 전송 (IMU)
*============================================================================*/
/* TAG(4B) + int16×6 빅엔디안(12B) + CRC16(2B) = 18바이트 전송
* 기존 format_data() + dr_binary_tx_safe(buf, 8) 방식과 동일한 레이아웃 */
/*응답 패킷 전송 (IMU): TAG(4B) + int16×6 빅엔디안(12B) + CRC16(2B) = 18바이트 전송 */
int cmd_send_response_imu(const int16_t accel[3], const int16_t gyro[3])
{
uint8_t *buf = tx_imu_buf;
@@ -201,7 +187,7 @@ int cmd_send_response_imu(const int16_t accel[3], const int16_t gyro[3])
void cmd_send_response_echo(uint8_t session, uint8_t channel, const uint16_t *samples, uint16_t num_samples)
{
/* 정적 버퍼 사용 (한 번에 한 명령만 처리) */
// 정적 버퍼 사용 (한 번에 한 명령만 처리)
uint8_t *buf = tx_echo_buf;
buf[0] = 'r'; buf[1] = 'e'; buf[2] = 'b'; buf[3] = ':';
@@ -302,10 +288,4 @@ void cmd_send_response_piezo_config(const char *tag, uint16_t freq, uint16_t cyc
buf[15] = (uint8_t)(crc >> 8);
ble_data_send(buf, sizeof(tx_cfg_buf));
}
/*==============================================================================
* 커맨드 핸들러
*============================================================================*/
/* msn? → 배터리 전압 측정 → rsn: + mV */
}
+1
View File
@@ -36,6 +36,7 @@ const cmd_entry_t cmd_table[] =
{ "mrs?", cmd_mrs },
{ "mpz?", cmd_mpz },
{ "mqz?", cmd_mqz },
{ "mim?", cmd_mim },
};
const int cmd_table_size = (int)(sizeof(cmd_table) / sizeof(cmd_table[0]));
+2 -6
View File
@@ -70,10 +70,6 @@ int cmd_msr(const uint8_t *data, uint8_t data_len)
return 1;
}
/*
* 현재 연결의 bond/security 정보를 먼저 지우면 같은 연결에서 rsr: 응답을 보낼 수 없음
* 응답 전송 완료 후 bond 삭제와 reboot 진행 필요
*/
k_msleep(100);
#if IS_ENABLED(CONFIG_BT_SMP)
@@ -112,7 +108,7 @@ int cmd_mdf(const uint8_t *data, uint8_t data_len)
uint16_t status = ble_dfu_advertising_is_enabled() ? 0xFFFF : 0x0000;
/* DFU advertising 전환 중 연결이 끊겨도 전원 래치가 유지되도록 먼저 ON 상태를 확정 */
// DFU advertising 전환 중 연결이 끊겨도 전원 래치가 유지되도록 먼저 ON 상태를 확정
device_power_keep_on();
int tx_err = cmd_send_response_u16("rdf:", status);
@@ -124,7 +120,7 @@ int cmd_mdf(const uint8_t *data, uint8_t data_len)
if (status == 0x0000)
{
/* rdf: 응답이 나간 뒤 연결 해제와 SMP 광고 전환 실행 */
// rdf: 응답이 나간 뒤 연결 해제와 SMP 광고 전환 실행
k_msleep(100);
int err = ble_dfu_advertising_enable();
if (err)
+28 -12
View File
@@ -11,6 +11,9 @@
#include "app_nvs.h"
#include "cmd_info.h"
/*
* mfv: firmware version read
*/
int cmd_mfv(const uint8_t *data, uint8_t data_len)
{
char fw_version[SERIAL_NO_LENGTH];
@@ -28,6 +31,9 @@ int cmd_mfv(const uint8_t *data, uint8_t data_len)
return 1;
}
/*
* mid: device information(hw version + fw version + serial number) read
*/
int cmd_mid(const uint8_t *data, uint8_t data_len)
{
int err;
@@ -43,6 +49,9 @@ int cmd_mid(const uint8_t *data, uint8_t data_len)
return 1;
}
/*
* mwh: hardware version write
*/
int cmd_mwh(const uint8_t *data, uint8_t data_len)
{
if (data_len < HW_NO_LENGTH)
@@ -64,6 +73,9 @@ int cmd_mwh(const uint8_t *data, uint8_t data_len)
return 1;
}
/*
* mrh: hardware version read
*/
int cmd_mrh(const uint8_t *data, uint8_t data_len)
{
int err;
@@ -79,6 +91,9 @@ int cmd_mrh(const uint8_t *data, uint8_t data_len)
return 1;
}
/*
* mws: serial number write
*/
int cmd_mws(const uint8_t *data, uint8_t data_len)
{
if (data_len < SERIAL_NO_LENGTH)
@@ -100,6 +115,9 @@ int cmd_mws(const uint8_t *data, uint8_t data_len)
return 1;
}
/*
* mrs: serial number read
*/
int cmd_mrs(const uint8_t *data, uint8_t data_len)
{
int err;
@@ -115,6 +133,9 @@ int cmd_mrs(const uint8_t *data, uint8_t data_len)
return 1;
}
/*
* mpz: passkey write
*/
int cmd_mpz(const uint8_t *data, uint8_t data_len)
{
if (data_len < PASSKEY_LENGTH)
@@ -125,13 +146,13 @@ int cmd_mpz(const uint8_t *data, uint8_t data_len)
if (m_passkey_changed != 0U)
{
/* 패스키가 변경된 경우 다시 바꿀 수 없음(FFFF 반환) */
// 패스키가 변경된 경우 다시 바꿀 수 없음(FFFF 반환)
cmd_send_response_u16("rpz:", 0xFFFF);
DBG_PRINTF("[CMD] mpz: passkey already changed\r\n");
return 1;
}
/* NVS 저장이 모두 성공한 뒤 RAM 값 확정 */
// NVS 저장이 모두 성공한 뒤 RAM 값 확정
char new_passkey[PASSKEY_BUF_SIZE];
memset(new_passkey, 0, sizeof(new_passkey));
memcpy(new_passkey, data, PASSKEY_LENGTH);
@@ -143,7 +164,7 @@ int cmd_mpz(const uint8_t *data, uint8_t data_len)
return 1;
}
/* 변경 완료 플래그 세트 : 다음 mpz? 명령 차단 */
// 변경 완료 플래그 세트 : 다음 mpz? 명령 차단
err = app_nvs_save_passkey_changed(1U);
if (err)
{
@@ -159,6 +180,9 @@ int cmd_mpz(const uint8_t *data, uint8_t data_len)
return 1;
}
/*
* mqz: passkey read (삭제 예정)
*/
int cmd_mqz(const uint8_t *data, uint8_t data_len)
{
ARG_UNUSED(data);
@@ -166,12 +190,4 @@ int cmd_mqz(const uint8_t *data, uint8_t data_len)
cmd_send_response_ascii("rqz:", m_static_passkey, PASSKEY_LENGTH);
return 1;
}
/*
* LED 상태 변경 명령
* mls?: 앱에서 전달한 LED 상태값을 적용하고 rls로 echo back
* - 파라미터 부족: 0xFFFF
* - 범위 초과: 0xFFFE
* - LED_STATE_OFF 진입 시 IMU FIFO 정지
*/
}
+284 -253
View File
@@ -17,8 +17,280 @@
static uint8_t tx_rim_samples[IMU_FIFO_RIM_TARGET_SAMPLES * IMU_FIFO_SAMPLE_BYTES];
/*
* 테스트용
* mpa?: piezo 전원 ON
* 주기 측정 명령
* mbb?: piezo echo sweep + 배터리 + IMU + IMU 온도 측정
* - 성공 시 rbb 1개, reb 6개, raa 상태값 전송
* - processing=true 구간에서는 내부 모니터의 I2C 접근을 막는 기준으로 사용
*
* - 순서:
* 1. battery / imu / temp 측정
* 2. rbb: 1개 전송
* 3. piezo / echo ADC 준비
* 4. piezo 6채널 burst
* 5. echo 샘플 채널별로 모으기
* 6. reb: 6개 전송
* 7. raa: 상태값 전송
*/
int cmd_mbb(const uint8_t *data, uint8_t data_len)
{
ARG_UNUSED(data);
ARG_UNUSED(data_len);
uint8_t session = cmd_next_echo_session();
int16_t accel[3];
int16_t gyro[3];
processing = true;
power_button_suspend(true);
DBG_PRINTF("[MBB] cmd start\r\n");
int batt_mv = -1;
int16_t temp_cdeg = INT16_MIN;
/* Read battery first (idle measurement) so mbb? result matches msn? */
DBG_PRINTF("[MBB] battery read (idle)\r\n");
batt_mv = battery_read_mv();
int status = piezo_measure_start_session();
if (status == ECHO_STATUS_OK) // Piezo GPIO, power, ADC initialization/wake succeeded
{
DBG_PRINTF("[MBB] imu/temp read\r\n");
imu_read_with_temperature(accel, gyro, &temp_cdeg);
battery_protection_record_continuous(batt_mv, temp_cdeg); // update protection counters for continuous monitoring
DBG_PRINTF("[MBB] response rbb: tx start\r\n");
cmd_send_response_bundle((uint16_t)batt_mv, accel, gyro, temp_cdeg);
DBG_PRINTF("[MBB] piezo sweep start\r\n");
status = piezo_measure_sweep();
if (status == ECHO_STATUS_OK)
{
for (uint8_t ch = 0; ch < PIEZO_NUM_CHANNELS; ch++)
{
DBG_PRINTF("[MBB] response reb: tx start ch=%d\r\n", ch);
cmd_send_response_echo(session, ch, piezo_measure_channel_buffer(ch), piezo_config_get()->samples);
}
}
}
piezo_power_off();
power_button_suspend(false);
DBG_PRINTF("[MBB] done\r\n");
cmd_send_response_u16("raa:", (uint16_t)status); // 최종 상태 raa: 전송
DBG_PRINTF("[CMD] mbb status=0x%04X\r\n", status);
processing = false;
return 1;
}
/*
* 정렬모드
* mtb?: IMU FIFO + piezo echo sweep
* - 순서:
* 1. IMU FIFO start
* 2. piezo 6채널 burst
* 3. echo 샘플 채널별로 모으기
* 4. reb: 패킷 6개 전송
* 5. raa: 상태값 전송
* 6. rim: IMU FIFO 15 샘플 전송
*/
int cmd_mtb(const uint8_t *data, uint8_t data_len)
{
ARG_UNUSED(data);
ARG_UNUSED(data_len);
uint8_t session = cmd_next_echo_session();
uint16_t rim_count = 0U;
bool fifo_started = false;
processing = true;
power_button_suspend(true);
DBG_PRINTF("[MTB] cmd start\r\n");
int imu_ret = imu_fifo_start();
if (imu_ret != 0)
{
DBG_PRINTF("[MTB] fifo start fail ret=%d\r\n", imu_ret);
}
else
{
fifo_started = true;
}
int status = piezo_measure_start_session();
if (status == ECHO_STATUS_OK) // Piezo GPIO, power, ADC initialization/wake succeeded
{
DBG_PRINTF("[MTB] piezo sweep start\r\n");
status = piezo_measure_sweep();
if (status == ECHO_STATUS_OK)
{
for (uint8_t ch = 0; ch < PIEZO_NUM_CHANNELS; ch++)
{
DBG_PRINTF("[MTB] response reb: tx start ch=%d\r\n", ch);
cmd_send_response_echo(session, ch, piezo_measure_channel_buffer(ch), piezo_config_get()->samples);
}
}
}
cmd_send_response_u16("raa:", (uint16_t)status); // 최종 상태 raa: 전송
if (status == ECHO_STATUS_OK)
{
if (fifo_started)
{
int imu_ret = imu_fifo_read_latest(tx_rim_samples, IMU_FIFO_RIM_TARGET_SAMPLES, &rim_count);
if (imu_ret != 0)
{
DBG_PRINTF("[MTB] fifo read fail ret=%d\r\n", imu_ret);
rim_count = 0U;
}
}
cmd_send_response_rim(tx_rim_samples, rim_count); // IMU FIFO 샘플 rim: 전송
}
DBG_PRINTF("[CMD] mtb status=0x%04X\r\n", status);
piezo_power_off();
power_button_suspend(false);
DBG_PRINTF("[CMD] mtb status=0x%04X rim=%u\r\n", status, rim_count);
processing = false;
return 1;
}
/*
* mcf?: piezo measurement parameter read
*/
int cmd_mcf(const uint8_t *data, uint8_t data_len)
{
ARG_UNUSED(data);
ARG_UNUSED(data_len);
const piezo_config_t *cfg = piezo_config_get();
cmd_send_response_piezo_config("rcf:", cfg->freq, cfg->cycles, cfg->avg, cfg->delay_us, cfg->samples);
return 1;
}
/*
* mcs?: piezo measurement parameter write
*/
int cmd_mcs(const uint8_t *data, uint8_t data_len)
{
piezo_config_t cfg;
if (data_len < 10U)
{
cmd_send_response_piezo_config("rcs:", 0xFFFF, 0U, 0U, 0U, 0U);
return 1;
}
cmd_get_data_u16_be(data, data_len, 0, &cfg.freq);
cmd_get_data_u16_be(data, data_len, 1, &cfg.cycles);
cmd_get_data_u16_be(data, data_len, 2, &cfg.avg);
cmd_get_data_u16_be(data, data_len, 3, &cfg.delay_us);
cmd_get_data_u16_be(data, data_len, 4, &cfg.samples);
if (!piezo_config_validate(&cfg))
{
cmd_send_response_piezo_config("rcs:", 0xFFFF, cfg.cycles, cfg.avg, cfg.delay_us, cfg.samples);
return 1;
}
int err = piezo_config_set(&cfg);
if (err)
{
cmd_send_response_piezo_config("rcs:", 0xFFFD, cfg.cycles, cfg.avg, cfg.delay_us, cfg.samples);
return 1;
}
const piezo_config_t *saved_cfg = piezo_config_get();
cmd_send_response_piezo_config("rcs:", saved_cfg->freq, saved_cfg->cycles, saved_cfg->avg, saved_cfg->delay_us, saved_cfg->samples);
return 1;
}
// =============================== Test Command ===============================
/* TEST
* mim? : IMU FIFO 15 samples
*/
int cmd_mim(const uint8_t *data, uint8_t data_len)
{
ARG_UNUSED(data);
ARG_UNUSED(data_len);
uint16_t rim_count = 0U;
int imu_ret = imu_fifo_start();
if (imu_ret != 0)
{
DBG_PRINTF("[MTB] fifo start fail ret=%d\r\n", imu_ret);
cmd_send_response_rim(tx_rim_samples, 0U);
return 1;
}
k_msleep(320); // IMU FIFO is 50Hz, 15 samples need about 300ms
imu_ret = imu_fifo_read_latest(tx_rim_samples, IMU_FIFO_RIM_TARGET_SAMPLES, &rim_count);
if (imu_ret != 0)
{
DBG_PRINTF("[MIM] fifo read fail ret=%d\r\n", imu_ret);
rim_count = 0U;
}
cmd_send_response_rim(tx_rim_samples, rim_count); // IMU FIFO 15 samples rim: 전송
imu_fifo_stop();
return 1;
}
/*
* TEST (이전 정렬모드)
* maa?: piezo echo sweep
* - 순서:
* 1. piezo 6채널 burst
* 2. echo 샘플 채널별로 모으기
* 3. reb: 패킷 6개 전송
* 4. raa: 상태값 전송
*/
int cmd_maa(const uint8_t *data, uint8_t data_len)
{
ARG_UNUSED(data);
ARG_UNUSED(data_len);
uint8_t session = cmd_next_echo_session();
processing = true;
power_button_suspend(true);
int status = piezo_measure_start_session();
if (status == ECHO_STATUS_OK) // Piezo GPIO, power, ADC initialization/wake succeeded
{
DBG_PRINTF("[MBB] piezo sweep start\r\n");
status = piezo_measure_sweep();
if (status == ECHO_STATUS_OK)
{
for (uint8_t ch = 0; ch < PIEZO_NUM_CHANNELS; ch++)
{
DBG_PRINTF("[MBB] response reb: tx start ch=%d\r\n", ch);
cmd_send_response_echo(session, ch, piezo_measure_channel_buffer(ch), piezo_config_get()->samples);
}
}
}
piezo_power_off();
power_button_suspend(false);
cmd_send_response_u16("raa:", (uint16_t)status); // 최종 상태 raa: 전송
DBG_PRINTF("[CMD] maa status=0x%04X\r\n", status);
processing = false;
return 1;
}
/*
* TEST
* mpa?: piezo power ON
*/
int cmd_mpa(const uint8_t *data, uint8_t data_len)
{
@@ -42,8 +314,8 @@ int cmd_mpa(const uint8_t *data, uint8_t data_len)
}
/*
* 테스트용
* mpb?: piezo 전원 OFF
* TEST
* mpb?: piezo power OFF
*/
int cmd_mpb(const uint8_t *data, uint8_t data_len)
{
@@ -67,9 +339,8 @@ int cmd_mpb(const uint8_t *data, uint8_t data_len)
}
/*
* 테스트용
* mpc?: burst만 한 번 발생시키는 테스트 명령
* echo를 읽지 않고 초음파가 나가는지만 볼 때 사용
* TEST
* mpc?: burst only
*/
int cmd_mpc(const uint8_t *data, uint8_t data_len)
{
@@ -109,10 +380,7 @@ int cmd_mpc(const uint8_t *data, uint8_t data_len)
return 1;
}
/*
* 현재 Zephyr 포팅본은 2.1MHz SW burst 하나만 구현되어 있음
* 레거시의 freq_option 값은 받아두되, 아직은 같은 burst 함수로 처리
*/
// 현재 2.1MHz 고정
piezo_burst_sw((uint8_t)cycles);
piezo_power_off();
power_button_suspend(false);
@@ -122,8 +390,8 @@ int cmd_mpc(const uint8_t *data, uint8_t data_len)
}
/*
* 테스트용
* mec?: 단일 채널 burst + echo capture
* TEST
* mec?: 1 channel burst + echo capture
*/
int cmd_mec(const uint8_t *data, uint8_t data_len)
{
@@ -180,11 +448,8 @@ int cmd_mec(const uint8_t *data, uint8_t data_len)
}
/*
* 테스트용
* mad?: 단일 채널 echo capture만 수행
* - piezo burst는 하지 않고 echo ADC만 수행
* - echo 샘플을 모은 뒤 reb: 패킷 1개 전송
* - raa: 상태값은 전체 작업 성공/실패 요약
* TEST
* mad?: 1 channel echo capture only
*/
int cmd_mad(const uint8_t *data, uint8_t data_len)
{
@@ -232,238 +497,4 @@ int cmd_mad(const uint8_t *data, uint8_t data_len)
processing = false;
return 1;
}
/*
* 테스트용(이전 정렬모드)
* maa?: piezo echo sweep
* - 순서:
* 1. piezo 6채널 burst
* 2. echo 샘플 채널별로 모으기
* 3. reb: 패킷 6개 전송
* 4. raa: 상태값은 전체 작업 성공/실패 요약
*/
int cmd_maa(const uint8_t *data, uint8_t data_len)
{
ARG_UNUSED(data);
ARG_UNUSED(data_len);
uint8_t session = cmd_next_echo_session();
processing = true;
power_button_suspend(true);
int status = piezo_measure_start_session();
if (status == ECHO_STATUS_OK) // Piezo GPIO, 전원, ADC 초기화/wake 성공
{
DBG_PRINTF("[MBB] piezo sweep start\r\n");
status = piezo_measure_sweep();
for (uint8_t ch = 0; ch < PIEZO_NUM_CHANNELS; ch++)
{
DBG_PRINTF("[MBB] response reb: tx start ch=%d\r\n", ch);
cmd_send_response_echo(session, ch, piezo_measure_channel_buffer(ch), piezo_config_get()->samples);
}
}
piezo_power_off();
power_button_suspend(false);
cmd_send_response_u16("raa:", (uint16_t)status); // 최종 상태값 raa: 전송
DBG_PRINTF("[CMD] maa status=0x%04X\r\n", status);
processing = false;
return 1;
}
/*
* 전체 측정 명령
* mbb?: piezo echo sweep + 배터리 + IMU + IMU 온도 측정
* - 성공 시 rbb 1개, reb 6개, raa 상태값 전송
* - processing=true 구간에서는 내부 모니터의 I2C 접근을 막는 기준으로 사용
*
* - 순서:
* 1. battery / imu / temp 측정
* 2. rbb: 1개 전송
* 3. piezo / echo ADC 준비
* 4. piezo 6채널 burst
* 5. echo 샘플 채널별로 모으기
* 6. reb: 6개 전송
* 7. raa: 최종 상태 전송
*/
int cmd_mbb(const uint8_t *data, uint8_t data_len)
{
ARG_UNUSED(data);
ARG_UNUSED(data_len);
uint8_t session = cmd_next_echo_session();
int16_t accel[3];
int16_t gyro[3];
processing = true;
power_button_suspend(true);
DBG_PRINTF("[MBB] cmd start\r\n");
int status = piezo_measure_start_session();
int batt_mv = -1;
int16_t temp_cdeg = INT16_MIN;
if (status == ECHO_STATUS_OK) // Piezo GPIO, 전원, ADC 초기화/wake 성공
{
DBG_PRINTF("[MBB] battery read\r\n");
batt_mv = battery_read_mv();
DBG_PRINTF("[MBB] imu/temp read\r\n");
imu_read_with_temperature(accel, gyro, &temp_cdeg);
DBG_PRINTF("[MBB] response rbb: tx start\r\n");
cmd_send_response_bundle((uint16_t)batt_mv, accel, gyro, temp_cdeg);
DBG_PRINTF("[MBB] piezo sweep start\r\n");
status = piezo_measure_sweep();
for (uint8_t ch = 0; ch < PIEZO_NUM_CHANNELS; ch++)
{
DBG_PRINTF("[MBB] response reb: tx start ch=%d\r\n", ch);
cmd_send_response_echo(session, ch, piezo_measure_channel_buffer(ch), piezo_config_get()->samples);
}
}
piezo_power_off();
power_button_suspend(false);
DBG_PRINTF("[MBB] done\r\n");
cmd_send_response_u16("raa:", (uint16_t)status); // 최종 상태값 raa: 전송
DBG_PRINTF("[CMD] mbb status=0x%04X\r\n", status);
processing = false;
return 1;
}
/*
* 정렬모드
* mtb?: IMU FIFO + piezo echo sweep
* - 순서:
* 1. IMU FIFO start
* 2. piezo 6채널 burst
* 3. echo 샘플 채널별로 모으기
* 4. reb: 패킷 6개 전송
* 5. raa: 상태값은 전체 작업 성공/실패 요약
* - IMU FIFO 샘플은 rim: 패킷으로 전송
*/
int cmd_mtb(const uint8_t *data, uint8_t data_len)
{
ARG_UNUSED(data);
ARG_UNUSED(data_len);
uint8_t session = cmd_next_echo_session();
uint16_t rim_count = 0U;
bool fifo_started = false;
processing = true;
power_button_suspend(true);
DBG_PRINTF("[MTB] cmd start\r\n");
int imu_ret = imu_fifo_start();
if (imu_ret != 0)
{
DBG_PRINTF("[MTB] fifo start fail ret=%d\r\n", imu_ret);
}
else
{
fifo_started = true;
}
int status = piezo_measure_start_session();
if (status == ECHO_STATUS_OK) // Piezo GPIO, 전원, ADC 초기화/wake 성공
{
DBG_PRINTF("[MTB] piezo sweep start\r\n");
status = piezo_measure_sweep();
for (uint8_t ch = 0; ch < PIEZO_NUM_CHANNELS; ch++)
{
DBG_PRINTF("[MTB] response reb: tx start ch=%d\r\n", ch);
cmd_send_response_echo(session, ch, piezo_measure_channel_buffer(ch), piezo_config_get()->samples);
}
}
cmd_send_response_u16("raa:", (uint16_t)status); // 최종 상태값 raa: 전송
if (fifo_started)
{
int imu_ret = imu_fifo_read_latest(tx_rim_samples, IMU_FIFO_RIM_TARGET_SAMPLES, &rim_count);
if (imu_ret != 0)
{
DBG_PRINTF("[MTB] fifo read fail ret=%d\r\n", imu_ret);
rim_count = 0U;
}
}
cmd_send_response_rim(tx_rim_samples, rim_count); // IMU FIFO 샘플 rim: 전송
DBG_PRINTF("[CMD] mtb status=0x%04X\r\n", status);
piezo_power_off();
power_button_suspend(false);
DBG_PRINTF("[CMD] mtb status=0x%04X rim=%u\r\n", status, rim_count);
processing = false;
return 1;
}
/*
* mcf?: piezo 측정 파라미터 읽기
* 응답 rcf: + 설정값 echo back
*/
int cmd_mcf(const uint8_t *data, uint8_t data_len)
{
ARG_UNUSED(data);
ARG_UNUSED(data_len);
const piezo_config_t *cfg = piezo_config_get();
cmd_send_response_piezo_config("rcf:", cfg->freq, cfg->cycles, cfg->avg, cfg->delay_us, cfg->samples);
return 1;
}
/*
* mcs?: piezo 측정 파라미터 쓰기
*/
int cmd_mcs(const uint8_t *data, uint8_t data_len)
{
piezo_config_t cfg;
if (data_len < 10U)
{
cmd_send_response_piezo_config("rcs:", 0xFFFF, 0U, 0U, 0U, 0U);
return 1;
}
cmd_get_data_u16_be(data, data_len, 0, &cfg.freq);
cmd_get_data_u16_be(data, data_len, 1, &cfg.cycles);
cmd_get_data_u16_be(data, data_len, 2, &cfg.avg);
cmd_get_data_u16_be(data, data_len, 3, &cfg.delay_us);
cmd_get_data_u16_be(data, data_len, 4, &cfg.samples);
if (!piezo_config_validate(&cfg))
{
cmd_send_response_piezo_config("rcs:", 0xFFFF, cfg.cycles, cfg.avg, cfg.delay_us, cfg.samples);
return 1;
}
int err = piezo_config_set(&cfg);
if (err)
{
cmd_send_response_piezo_config("rcs:", 0xFFFD, cfg.cycles, cfg.avg, cfg.delay_us, cfg.samples);
return 1;
}
const piezo_config_t *saved_cfg = piezo_config_get();
cmd_send_response_piezo_config("rcs:", saved_cfg->freq, saved_cfg->cycles, saved_cfg->avg, saved_cfg->delay_us, saved_cfg->samples);
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=범위 초과 */
}
+1
View File
@@ -17,5 +17,6 @@ int cmd_mbb(const uint8_t *data, uint8_t data_len);
int cmd_mtb(const uint8_t *data, uint8_t data_len);
int cmd_mcf(const uint8_t *data, uint8_t data_len);
int cmd_mcs(const uint8_t *data, uint8_t data_len);
int cmd_mim(const uint8_t *data, uint8_t data_len);
#endif /* CMD_PIEZO_H__ */
+19 -19
View File
@@ -13,6 +13,9 @@
#include "led_control.h"
#include "cmd_sensor.h"
/*
* msn: battery voltage read
*/
int cmd_msn(const uint8_t *data, uint8_t data_len)
{
ARG_UNUSED(data);
@@ -28,6 +31,9 @@ int cmd_msn(const uint8_t *data, uint8_t data_len)
return 1;
}
/*
* msp: IMU data direct read
*/
int cmd_msp(const uint8_t *data, uint8_t data_len)
{
ARG_UNUSED(data);
@@ -47,8 +53,9 @@ int cmd_msp(const uint8_t *data, uint8_t data_len)
return 1;
}
/* mst? -> IMU internal temperature -> rso: + temperature (degC x 100, BE)
* Error response: 0xFFFF = IMU temperature read failed. */
/*
* mst: IMU temperature direct read
*/
int cmd_mst(const uint8_t *data, uint8_t data_len)
{
ARG_UNUSED(data);
@@ -68,19 +75,18 @@ int cmd_mst(const uint8_t *data, uint8_t data_len)
return 1;
}
/* 음수 온도도 2's complement로 그대로 전송 (앱이 int16로 해석) */
// 음수 온도도 2's complement로 그대로 전송 (앱이 int16로 해석)
cmd_send_response_u16("rso:", (uint16_t)t_cdeg);
DBG_PRINTF("[CMD] mst -> %d.%02d C\r\n", t_cdeg / 100, (t_cdeg < 0 ? -t_cdeg : t_cdeg) % 100);
return 1;
}
/*
* 테스트용
* mpa?: piezo TX/RX 전원 레일만 켬
* mls: LED status set
*/
int cmd_mls(const uint8_t *data, uint8_t data_len)
{
/* 파라미터 부족: 에러 코드 0xFFFF 에코 */
// 파라미터 부족: 에러 코드 0xFFFF 에코
if (data_len < 2)
{
cmd_send_response_u16("rls:", 0xFFFF);
@@ -88,10 +94,10 @@ int cmd_mls(const uint8_t *data, uint8_t data_len)
return 1;
}
/* state 필드: 2바이트 big-endian */
// state 필드: 2바이트 big-endian
uint16_t state = ((uint16_t)data[0] << 8) | (uint16_t)data[1];
/* 범위 초과: 에러 코드 0xFFFE 에코 */
// 범위 초과: 에러 코드 0xFFFE 에코
if (state > LED_STATE_ERROR)
{
cmd_send_response_u16("rls:", 0xFFFE);
@@ -101,14 +107,12 @@ int cmd_mls(const uint8_t *data, uint8_t data_len)
led_set_state((led_state_t)state);
/* 정렬 LED 상태와 보호 판정 정책 연결
* - ALIGN_SEARCHING/ALIGN_COMPLETE 동안 보호 판정 차단
* - 다른 LED 상태에서는 기존 조건에 따라 보호 판정 재개
*/
battery_protection_set_alignment_mode((state == LED_STATE_ALIGN_SEARCHING) ||
(state == LED_STATE_ALIGN_COMPLETE));
// 정렬 LED 상태와 보호 판정 정책 연결
// - ALIGN_SEARCHING/ALIGN_COMPLETE 동안 보호 판정 차단
// - 다른 LED 상태에서는 기존 조건에 따라 보호 판정 재개
battery_protection_set_alignment_mode((state == LED_STATE_ALIGN_SEARCHING) || (state == LED_STATE_ALIGN_COMPLETE));
/* OFF 상태에서는 정렬/측정용 FIFO를 함께 정리 */
// OFF 상태에서는 정렬/측정용 FIFO를 함께 정리
if (state == LED_STATE_OFF)
{
(void)imu_fifo_stop();
@@ -117,7 +121,3 @@ int cmd_mls(const uint8_t *data, uint8_t data_len)
cmd_send_response_u16("rls:", state);
return 1;
}
/*==============================================================================
* 커맨드 테이블
*============================================================================*/
+1 -3
View File
@@ -12,9 +12,7 @@
#include "cmd_table.h"
#include "debug_print.h"
/*==============================================================================
* 파서 엔트리
*============================================================================*/
/* 파서 엔트리 */
int ble_cmd_dispatch(const uint8_t *buf, uint16_t len)
{
//DBG_CORE("[PARSER] enter len=%u\r\n", len);
+1 -1
View File
@@ -2,7 +2,7 @@
* @file parser.h
* @brief BLE command parser
*
* 4바이트 TAG + DATA + CRC16 패킷 파싱 및 디스패치
* Packet format : [TAG 4B] [DATA NB] [CRC16 2B]
******************************************************************************/
#ifndef CMD_PARSER_H__
#define CMD_PARSER_H__