diff --git a/src/ble/ble_service.c b/src/ble/ble_service.c index c091461..8f1bccf 100644 --- a/src/ble/ble_service.c +++ b/src/ble/ble_service.c @@ -1,13 +1,13 @@ /******************************************************************************* * @file ble_service.c - * @brief BLE NUS service module (Zephyr port) + * @brief BLE NUS service module * * Original VesiScan-Basic BLE implementation ported to Zephyr/NCS: * - NUS (Nordic UART Service) for data exchange * - Advertising: 40ms interval, 10-min timeout * - Connection: 15ms interval, 0 latency, 10s supervision timeout * - TX power +8 dBm, 2M PHY preferred - * - Dev mode: no security / Production mode: bonding + passkey (TODO) + * - Dev mode: no security / Production mode: bonding + passkey ******************************************************************************/ #include #include @@ -45,18 +45,8 @@ static uint32_t pairing_passkey = BT_PASSKEY_RAND; #endif /* - * NUS 전송은 "보냈다" 호출만 한다고 바로 끝나는 게 아니다. - * 내부 TX 버퍼에 들어간 뒤 실제로 무선으로 나가고, - * Zephyr가 sent 콜백을 줄 때 비로소 한 건이 끝났다고 볼 수 있다. - * - * 그래서 아래 2개를 같이 쓴다. * - mutex: 여러 곳에서 동시에 NUS 전송을 시작하지 못하게 막음 * - sem : "직전 전송이 정말 끝났는지" 기다리는 신호 - * - * mbb?처럼 큰 패킷을 여러 개 연속으로 보내는 경우 이 보호가 없으면 - * - 중간에 -ENOMEM이 뜨거나 - * - 몇 개는 나가고 몇 개는 실패하거나 - * - 앱 쪽에서 응답이 꼬여 보일 수 있다. */ static struct k_mutex nus_tx_lock; static struct k_sem nus_tx_done_sem; @@ -65,27 +55,20 @@ static struct k_sem nus_tx_done_sem; static struct k_work adv_restart_work; static struct k_work_delayable adv_timeout_work; static struct k_work_delayable conn_param_update_work; -static const bt_addr_le_t fixed_identity_addr = { - .type = BT_ADDR_LE_RANDOM, - .a = { - .val = { 0xF1, 0x8E, 0x81, 0xFA, 0x87, 0xC5 }, - }, -}; - static const char *ble_addr_type_str(uint8_t type) { switch (type) { - case BT_ADDR_LE_PUBLIC: - return "public"; - case BT_ADDR_LE_RANDOM: - return "random"; - case BT_ADDR_LE_PUBLIC_ID: - return "public-id"; - case BT_ADDR_LE_RANDOM_ID: - return "random-id"; - default: - return "unknown"; + case BT_ADDR_LE_PUBLIC: + return "public"; + case BT_ADDR_LE_RANDOM: + return "random"; + case BT_ADDR_LE_PUBLIC_ID: + return "public-id"; + case BT_ADDR_LE_RANDOM_ID: + return "random-id"; + default: + return "unknown"; } } @@ -98,39 +81,12 @@ static void ble_log_local_identity(void) bt_id_get(&addr, &count); if (count == 0U) { - DBG_ERR("[BLE] No local identity address\r\n"); + DBG_ERR("[BLE] No local identity (MAC address) configured\r\n"); return; } bt_addr_le_to_str(&addr, addr_str, sizeof(addr_str)); - DBG_CORE("[BLE] Local identity: %s (%s)\r\n", - addr_str, - ble_addr_type_str(addr.type)); -} - -static int ble_configure_fixed_identity(void) -{ - bt_addr_le_t addr = fixed_identity_addr; - char addr_str[BT_ADDR_LE_STR_LEN]; - int id; - - bt_addr_le_to_str(&addr, addr_str, sizeof(addr_str)); - DBG_CORE("[BLE] Request fixed identity: %s\r\n", addr_str); - - id = bt_id_create(&addr, NULL); - if (id < 0) - { - DBG_ERR("[BLE] bt_id_create failed (err %d)\r\n", id); - return id; - } - - if (id != BT_ID_DEFAULT) - { - DBG_ERR("[BLE] Unexpected identity slot %d\r\n", id); - return -EINVAL; - } - - return 0; + DBG_CORE("[BLE] Local identity (MAC address): %s\r\n", addr_str); } #if IS_ENABLED(CONFIG_BT_SMP) @@ -551,9 +507,9 @@ static void nus_received(struct bt_conn *conn, const uint8_t *data, uint16_t len static void nus_sent(struct bt_conn *conn) { ARG_UNUSED(conn); - /* 이 시점이 "직전 NUS 패킷 전송이 끝났다"는 완료 신호다. */ + /* 직전 NUS 패킷 전송 완료 */ k_sem_give(&nus_tx_done_sem); - DBG_CORE("[NUS TX] complete\r\n"); + //DBG_CORE("[NUS TX] complete\r\n"); } static struct bt_nus_cb nus_cb = @@ -577,12 +533,6 @@ int ble_service_init(ble_data_rx_cb_t rx_cb) k_work_init_delayable(&adv_timeout_work, adv_timeout_handler); k_work_init_delayable(&conn_param_update_work, conn_param_update_handler); - err = ble_configure_fixed_identity(); - if (err) - { - return err; - } - /* Enable BLE stack */ err = bt_enable(NULL); if (err) @@ -731,18 +681,14 @@ int ble_data_send(const uint8_t *data, uint16_t len) } #endif - /* - * NUS는 한 번에 한 패킷씩 질서 있게 보내는 편이 안전하다. - * 특히 mbb?는 rbb + reb x6 + raa처럼 여러 응답을 연속 전송하므로 - * 먼저 들어온 전송이 끝나기 전 다음 전송이 겹치지 않게 잠근다. - */ + /* 먼저 들어온 전송이 끝나기 전 다음 전송이 겹치지 않게 잠금 (NUS는 한 번에 한 패킷씩 보내는 것이 안전) */ k_mutex_lock(&nus_tx_lock, K_FOREVER); - /* 혹시 남아 있는 완료 신호가 있으면 비워서 "이번 전송 전용" 상태로 만든다. */ + /* 혹시 남아 있는 완료 신호가 있으면 비우고 이번 전송 전용 상태로 만듦 */ while (k_sem_take(&nus_tx_done_sem, K_NO_WAIT) == 0) { } - DBG_CORE("[NUS TX] send len=%u\r\n", len); + //DBG_CORE("[NUS TX] send len=%u\r\n", len); for (int retry = 0; ; retry++) { err = bt_nus_send(current_conn, data, len); @@ -751,8 +697,8 @@ int ble_data_send(const uint8_t *data, uint16_t len) } /* - * -ENOMEM은 지금 당장 보낼 자리(TX 버퍼)가 없다는 뜻이다. - * 바로 포기하지 않고 짧게 쉬었다가 다시 시도한다. + * -ENOMEM : BLE TX 버퍼가 가득참 + * 바로 포기하지 않고 짧게 쉬었다가 다시 시도 */ DBG_ERR("[NUS TX] busy, retry=%d\r\n", retry + 1); k_msleep(5); @@ -765,10 +711,7 @@ int ble_data_send(const uint8_t *data, uint16_t len) return err; } - /* - * bt_nus_send()가 성공했다고 해서 공중으로 다 나간 것은 아니다. - * sent 콜백이 올 때까지 조금 기다려서 "다음 패킷을 보내도 되는 시점"을 맞춘다. - */ + /* bt_nus_send() 성공 후 sent 콜백이 올 때까지 기다림 (다음 패킷을 보내도 되는 시점) */ err = k_sem_take(&nus_tx_done_sem, K_MSEC(1000)); if (err) { diff --git a/src/ble/ble_service.h b/src/ble/ble_service.h index a11dee3..1be2649 100644 --- a/src/ble/ble_service.h +++ b/src/ble/ble_service.h @@ -1,10 +1,10 @@ /******************************************************************************* * @file ble_service.h - * @brief BLE NUS service module (Zephyr port) + * @brief BLE NUS service module * - * Nordic UART Service (NUS) based BLE communication. + * Nordic UART Service (NUS) based BLE communication * Original: SoftDevice S140 + ble_nus SDK module - * Ported: Zephyr BLE + NCS bt_nus + * Ported: Zephyr BLE + NCS bt_nus ******************************************************************************/ #ifndef BLE_SERVICE_H__ #define BLE_SERVICE_H__ @@ -15,8 +15,6 @@ /*============================================================================== * BLE configuration *============================================================================*/ -#define BLE_DEV_MODE 0 /* 1: Dev (no security), 0: Production */ - #define APP_ADV_INTERVAL 64 /* 64 x 0.625ms = 40ms */ #define APP_ADV_DURATION 60000 /* 60000 x 10ms = 600s (10 min) */ diff --git a/src/drivers/battery/battery_adc.c b/src/drivers/battery/battery_adc.c index acf36aa..43c49d1 100644 --- a/src/drivers/battery/battery_adc.c +++ b/src/drivers/battery/battery_adc.c @@ -1,6 +1,6 @@ /******************************************************************************* * @file battery_adc.c - * @brief Battery voltage ADC measurement (Zephyr devicetree 기반) + * @brief Battery voltage ADC measurement * * ADC 채널 설정은 디바이스트리 overlay에서 관리: * - AIN2 (P0.04), Single-ended @@ -11,8 +11,9 @@ * * 동작 모드: * - 단독 측정 (msn? 커맨드) → battery_read_mv() 호출 - * - 주기 모니터링 (60초) → 저전압 10회 연속 시 자동 전원 OFF - * - info4 모드 (mbb?) → info_batt에 저장 + * - 주기 모니터링 (60초) → 저전압 또는 고온 5회 연속 시 자동 전원 OFF + * - 측정 중(processing) 또는 IMU FIFO 동작 중에는 주기 모니터링 스킵 + * - mbb? 측정 응답 -> parser에서 battery_read_mv() 호출 후 패킷에 포함 ******************************************************************************/ #include #include @@ -51,13 +52,10 @@ static uint8_t high_temperature_cnt = 0; #define BATTERY_MONITOR_INTERVAL_MS 60000 /* 60초 주기 */ /*============================================================================== - * 전압 변환 - *============================================================================*/ - -/** @brief ADC raw → mV 변환 + * 전압 변환 (ADC raw → mV 변환) * 공식: (raw × 600 / 4095) × 6 × 1.42 - * 정수 연산: raw × 600 × 6 × 142 / (4095 × 100) */ - + * 정수 연산: raw × 600 × 6 × 142 / (4095 × 100) + *============================================================================*/ static int adc_raw_to_mv(int16_t raw) { if (raw < 0) diff --git a/src/drivers/battery/battery_adc.h b/src/drivers/battery/battery_adc.h index 79ba97d..400264b 100644 --- a/src/drivers/battery/battery_adc.h +++ b/src/drivers/battery/battery_adc.h @@ -3,7 +3,7 @@ * @brief Battery voltage ADC measurement (Zephyr port) * * AIN2 채널, 12-bit, 1/6 gain, 4X oversample - * 저전압(3500mV) 10회 연속 감지 시 자동 전원 OFF + * 저전압(3500mV) 5회 연속 감지 시 자동 전원 OFF ******************************************************************************/ #ifndef BATTERY_ADC_H__ #define BATTERY_ADC_H__ diff --git a/src/drivers/imu/imu_i2c.c b/src/drivers/imu/imu_i2c.c index cb3c949..2d44013 100644 --- a/src/drivers/imu/imu_i2c.c +++ b/src/drivers/imu/imu_i2c.c @@ -1,8 +1,6 @@ /******************************************************************************* * @file imu_i2c.c - * @brief ICM42670P IMU 드라이버 (Zephyr I2C API) - * - * 기존 VesiScan-Basic(nRF5 SDK)의 imu_read_direct()를 Zephyr로 포팅. + * @brief ICM42670P IMU Driver (Zephyr I2C API) * * 동작 방식 (매 측정 시): * 1) GYRO_CONFIG0 = 0x09 → ±2000dps, 100Hz ODR diff --git a/src/drivers/imu/imu_i2c.h b/src/drivers/imu/imu_i2c.h index 7fa02e3..7a2e7c6 100644 --- a/src/drivers/imu/imu_i2c.h +++ b/src/drivers/imu/imu_i2c.h @@ -2,9 +2,6 @@ * @file imu_i2c.h * @brief ICM42670P IMU 드라이버 (Zephyr I2C API) * - * 기존 VesiScan-Basic(nRF5 SDK)의 imu_read_direct() 방식을 Zephyr로 포팅. - * 드라이버 API 없이 직접 I2C 레지스터 읽기/쓰기 방식 사용. - * * 핀 배치: * SCL: P1.14 * SDA: P1.15 diff --git a/src/drivers/led/led_control.c b/src/drivers/led/led_control.c index e2ea12b..c5d34fa 100644 --- a/src/drivers/led/led_control.c +++ b/src/drivers/led/led_control.c @@ -1,6 +1,6 @@ /******************************************************************************* * @file led_control.c - * @brief LED direct control driver (Zephyr port) + * @brief LED direct control driver * * k_timer based 2-color LED (green/orange) pattern control * Simple on/off states use immediate GPIO, complex patterns use state machine diff --git a/src/drivers/led/led_control.h b/src/drivers/led/led_control.h index 20090cb..5cf5561 100644 --- a/src/drivers/led/led_control.h +++ b/src/drivers/led/led_control.h @@ -1,6 +1,6 @@ /******************************************************************************* * @file led_control.h - * @brief LED direct control driver (Zephyr port) + * @brief LED direct control driver * * Green (P0.12) + Orange (P0.29) 2-color LED with k_timer based patterns ******************************************************************************/ diff --git a/src/main.c b/src/main.c index 4498407..e353bb0 100644 --- a/src/main.c +++ b/src/main.c @@ -1,16 +1,26 @@ /******************************************************************************* * @file main.c - * @brief VesiScan BASIC - Zephyr 포팅 (Stage 1: GPIO + 전원 + LED + BLE) + * @brief VesiScan-Basic * - * 부팅 시퀀스: - * Phase 1: 전원 유지 핀, GPIO, 타이머, LED 초기화 - * Phase 2: BLE 스택 + NUS 초기화, advertising 시작 - * Phase 3+: (이후 단계: 센서 등) + * 부팅 흐름: + * 1. 리셋/DFU 복구 상태 확인, 전원 유지 핀 초기 설정 + * 2. GPIO, 타이머, workqueue, LED, 배터리 ADC, IMU, piezo 설정 초기화 + * 3. BLE 스택 + NUS 초기화 + * 4. 전원 버튼 상태머신 시작 * - * 전원 버튼 상태머신 (5ms 폴링, k_timer): - * [OFF→ON] 버튼 2초 이상 유지 → P0.08 래치 ON + 녹색 LED 깜빡임 - * 2초 미만 놓으면 전원 OFF (래치 안 됨) - * [ON→OFF] 버튼 2초 이상 유지 → sleep_mode_enter() → 전원 차단 + * 일반 버튼 부팅: + * - 버튼을 BOOT_THRESHOLD 동안 유지하면 P0.08 전원 래치 ON + * - 래치 후 LED advertising 패턴, 배터리 모니터, BLE advertising 시작 + * - 기준 시간 = POWER_ON_DELAY * BOOT_THRESHOLD + * + * 리셋/DFU 복구: + * - 버튼 없이 전원 래치와 advertising을 자동 복구할 수 있음 + * + * 전원 버튼 상태머신: + * [OFF→ON] 2초 이상 버튼 유지 → 전원 래치 ON (초기화 시간 고려 체감상 2초, 실제 코드상 1초) + * 임계 시간 전에 놓으면 전원 래치 해제 + * [ON→OFF] 버튼 유지 → advertising 중지 + POWER_OFF_DELAY 후 전원 차단 + * ******************************************************************************/ #include #include @@ -81,13 +91,7 @@ static bool power_btn_suspended; /* 측정 중 전원 버튼 상태 /* * BLE advertising 제어용 워크 아이템 - * - * BLE HCI 명령(bt_le_adv_start/stop 등)은 내부적으로 세마포어 대기가 필요하므로 - * 인터럽트 컨텍스트(타이머 콜백 등)에서 직접 호출하면 커널 oops가 발생한다. - * (증상: "Controller unresponsive, command opcode 0x2006 timeout") - * - * k_work를 사용하면 시스템 워크큐 스레드에서 실행되므로 안전하게 BLE API 호출 가능. - * main_s() 타이머 콜백에서 k_work_submit()으로 예약하여 사용한다. + * - main_s()에서 k_work_submit()으로 예약하여 사용 */ static bool resume_without_power_button; /* 리셋 후 버튼 없이 복귀 */ static bool dfu_confirm_pending_boot; /* DFU test 이미지 확인 전 자동 복귀 */ @@ -95,7 +99,7 @@ static bool dfu_reset_resume_request; /* DFU 완료 리셋 후 자동 복 static uint32_t boot_reset_reason; /* RESETREAS 원본 값 */ static struct k_work adv_start_work; /* advertising 시작 work */ static struct k_work adv_stop_work; /* advertising 중지 work */ -static bool dfu_led_active; /* DFU 업로드 중 초록 LED 고정 */ +static bool dfu_led_active; /* DFU 업로드 중 초록 LED ON */ static const char *power_off_pending_reason = "none"; static enum mgmt_cb_return dfu_status_cb(uint32_t event, @@ -128,16 +132,8 @@ static struct mgmt_callback smp_cmd_status_callback = { }; /* - * BLE RX 콜백 안에서 무거운 일을 바로 처리하지 않기 위해 별도 work queue를 둔다. - * - * 이유: - * - BLE 콜백은 "일단 빨리 빠져나오는 것"이 중요하다. - * - 그 안에서 mbb? 같은 긴 측정을 돌리면 BLE 스택 타이밍을 망칠 수 있다. - * - 그래서 수신 데이터는 일단 복사만 하고, - * 실제 파싱/측정은 아래 work queue 스레드에서 천천히 처리한다. - * - * 현재 구조는 "동시에 여러 명령 처리"가 아니라 "한 번에 하나만 처리" 모델이다. - * 즉, 긴 명령 하나가 끝날 때까지 다음 명령은 drop된다. + * BLE RX 콜백 안에서 무거운 일을 바로 처리하지 않기 위해 별도 work queue를 둠 + * - 명령 하나가 끝날 때까지 다음 명령 drop : 확인 필요 */ static struct k_work_q ble_cmd_work_q; K_THREAD_STACK_DEFINE(ble_cmd_workq_stack, BLE_CMD_WORKQ_STACK_SZ); @@ -155,7 +151,7 @@ static void ble_cmd_work_handler(struct k_work *work) uint8_t local_buf[BLE_CMD_MAX_LEN]; uint16_t local_len = 0U; - /* 공유 RX 버퍼 짧게 잠금 */ + /* 공유 RX 명령 슬롯 보호: 현재 구조는 한 번에 한 명령만 보관 */ k_spinlock_key_t key = k_spin_lock(&ble_cmd_lock); if (ble_cmd_pending) { @@ -171,8 +167,8 @@ static void ble_cmd_work_handler(struct k_work *work) } /* 일반 스레드 문맥 명령 처리 */ - DBG_CORE("[BLE RX] worker dispatch len=%u\r\n", local_len); - dr_parser(local_buf, local_len); + //DBG_CORE("[BLE RX] worker dispatch len=%u\r\n", local_len); + ble_cmd_dispatch(local_buf, local_len); /* 명령 처리 완료 표시 */ key = k_spin_lock(&ble_cmd_lock); @@ -200,28 +196,13 @@ static void adv_stop_work_handler(struct k_work *work) *============================================================================*/ static void ble_rx_handler(const uint8_t *data, uint16_t len) { - /* RX 데이터 앞부분 로그 */ - DBG_CORE("[BLE RX] %u bytes:", len); - for (uint16_t i = 0; i < len && i < 32; i++) - { - DBG_CORE(" %02X", data[i]); - } - if (len > 32) - { - DBG_CORE(" ..."); - } - DBG_CORE("\r\n"); - if (len > BLE_CMD_MAX_LEN) { DBG_ERR("[BLE RX] drop: len=%u exceeds %d\r\n", len, BLE_CMD_MAX_LEN); return; } - /* - * 이미 긴 명령 하나를 처리 중이면 새 명령은 받지 않는다. - * 지금은 안정성 우선이라 "큐에 여러 개 쌓기"보다 "한 번에 하나"가 더 안전하다. - */ + /* 공유 RX 명령 슬롯 보호: 현재 구조는 한 번에 한 명령만 보관 */ k_spinlock_key_t key = k_spin_lock(&ble_cmd_lock); if (ble_cmd_pending) { @@ -237,7 +218,6 @@ static void ble_rx_handler(const uint8_t *data, uint16_t len) k_spin_unlock(&ble_cmd_lock, key); /* 명령 처리 work 예약 */ - DBG_CORE("[BLE RX] queued len=%u\r\n", len); int err = k_work_submit_to_queue(&ble_cmd_work_q, &ble_cmd_work); if (err < 0) { @@ -253,10 +233,6 @@ static void ble_rx_handler(const uint8_t *data, uint16_t len) /*============================================================================== * 전원 유지 (POWER_HOLD) 제어 *============================================================================*/ - -/* 전원 유지 핀(P0.08) 초기화 - 아직 래치하지 않음 - * 버튼을 물리적으로 누르고 있는 동안만 전원 유지됨 - * 2초 후 main_s()에서 래치(HIGH) 설정 */ static void boot_context_detect(void) { /* 부팅 원인 확인 */ @@ -264,8 +240,7 @@ static void boot_context_detect(void) /* DFU 완료 직후 리셋인지 확인 */ #if NRF_POWER_HAS_GPREGRET - dfu_reset_resume_request = - (nrf_power_gpregret_get(NRF_POWER, DFU_RESUME_GPREGRET_REG) == DFU_RESUME_MAGIC); + dfu_reset_resume_request = (nrf_power_gpregret_get(NRF_POWER, DFU_RESUME_GPREGRET_REG) == DFU_RESUME_MAGIC); if (dfu_reset_resume_request) { nrf_power_gpregret_set(NRF_POWER, DFU_RESUME_GPREGRET_REG, 0U); @@ -325,7 +300,6 @@ static void power_control_handler(on_off_cont_t device_power_st, const char *rea else { gpio_pin_set_dt(&power_hold, 1); /* P0.08 HIGH → 전원 유지 */ - DBG_PRINTF("[PWR] ON reason=%s\r\n", reason ? reason : "unknown"); } } @@ -335,7 +309,7 @@ static void power_control_handler(on_off_cont_t device_power_st, const char *rea static void gpio_init(void) { gpio_pin_configure_dt(&power_btn, GPIO_INPUT); /* 전원 버튼(P1.08) 입력 설정 */ - DBG_PRINTF("[GPIO] OK (BTN=%d)\r\n", gpio_pin_get_dt(&power_btn)); + DBG_PRINTF("[INIT] HW - GPIO OK (POWER BTN=%d)\r\n", gpio_pin_get_dt(&power_btn)); } @@ -380,7 +354,7 @@ static void load_default_config(void) m_reset_status = 1; bond_data_delete = true; - DBG_CORE("[CFG] Default (S/N=%s)\r\n", SERIAL_NO); + DBG_CORE("[INIT] Default (S/N=%s)\r\n", SERIAL_NO); } /*============================================================================== @@ -390,7 +364,7 @@ static void load_default_config(void) static void t_power_off_timeout_handler(struct k_timer *timer) { ARG_UNUSED(timer); - DBG_ERR("[PWR] Off timeout reason=%s\r\n", power_off_pending_reason); + DBG_ERR("[PWR] OFF timeout reason=%s\r\n", power_off_pending_reason); led_set_state(LED_STATE_OFF); power_control_handler(OFF, "off-timeout"); } @@ -402,14 +376,7 @@ static void t_power_off_timeout_handler(struct k_timer *timer) static void power_off_schedule(const char *reason, led_state_t led_state) { power_off_pending_reason = reason ? reason : "unknown"; - DBG_ERR("[PWR] OFF scheduled reason=%s delay_ms=%u device_on=%u btn=%d cnt=%u suspended=%u dfu_adv=%u\r\n", - power_off_pending_reason, - POWER_OFF_DELAY, - device_on ? 1U : 0U, - gpio_pin_get_dt(&power_btn), - cnt_s, - power_btn_suspended ? 1U : 0U, - ble_dfu_advertising_is_enabled() ? 1U : 0U); + DBG_ERR("[PWR] OFF scheduled reason = %s\r\n", power_off_pending_reason); led_set_state(led_state); k_timer_start(&m_power_off_delay_timer, K_MSEC(POWER_OFF_DELAY), K_NO_WAIT); } @@ -445,12 +412,7 @@ void device_power_keep_on(void) void power_button_suspend(bool suspend) { - /* - * mbb?처럼 측정 시간이 길 때 전원 버튼 상태머신이 끼어들지 않게 잠깐 멈춘다. - * - * 이 함수는 "부팅을 막는 함수"가 아니다. - * 이미 켜진 뒤, 특정 커맨드 처리 중에만 버튼 폴링 판단을 잠깐 쉬게 하는 역할이다. - */ + /* 측정 시간이 긴 커맨드 처리 중에만 버튼 폴링 판단 잠시 멈춤 */ power_btn_suspended = suspend; cnt_s = 0; } @@ -460,15 +422,15 @@ void power_button_suspend(bool suspend) * * [부팅 시퀀스] (device_on == false) * 1. 버튼 누름 → 전원 공급 → MCU 부팅 → 5ms마다 폴링 시작 - * 2. cnt_s < 400 (2초 미만)에서 놓으면 → 전원 OFF (래치 안 됨) - * 3. cnt_s == 400 (2초) 도달 → P0.08 래치 ON + 녹색 LED 깜빡임 + * 2. cnt_s < 200 (2초 미만)에서 놓으면 → 전원 OFF (래치 안 됨) + * 3. cnt_s == 200 (2초) 도달 → P0.08 래치 ON + 녹색 LED 깜빡임 * 4. 버튼 놓으면 → 깜빡임 유지, 폴링 계속 * * [전원 OFF 시퀀스] (device_on == true) * 1. 버튼 2초 이상 누름 → sleep_mode_enter() → 전원 차단 * 2. 버튼 2초 미만 놓으면 → 카운터 리셋, 무시 *============================================================================*/ -#define BOOT_THRESHOLD 200 /* 5ms x 400 = 2초 */ +#define BOOT_THRESHOLD 200 /* 5ms x 400 = 2초 -> 초기화 시간 고려 체감상 2초에 맞춤 */ static void main_s(struct k_timer *timer) { @@ -498,37 +460,31 @@ static void main_s(struct k_timer *timer) if (cnt_s == BOOT_THRESHOLD) /* 2초 도달: 래치 + 부팅 완료 */ { device_on = true; - cnt_s = 0; /* 카운터 리셋: 안 하면 다음 틱에서 ON→OFF 분기가 - * cnt_s >= 400 조건을 즉시 만족하여 전원 OFF됨 */ + cnt_s = 0; /* 카운터 리셋: 안 하면 다음 틱에서 ON→OFF 분기가 cnt_s >= 200 조건을 즉시 만족하여 전원 OFF됨 */ power_control_handler(ON, "button-2s-latch"); led_set_state(LED_STATE_ADVERTISING); k_work_submit(&adv_start_work); battery_timer_start(); m_reset_status = 1; - DBG_PRINTF("[BTN] 2s -> Power latched, LED blink\r\n"); - DBG_PRINTF("[BOOT] Complete, device ON\r\n"); - DBG_PRINTF("[DEV] device_on=%d\r\n", device_on); } } } /* 전원 OFF 시퀀스 (ON → OFF) */ else { - if (!boot_btn_released) /* 부팅 시 눌렀던 버튼을 아직 안 놓음 → 대기 */ + if (!boot_btn_released) /* 부팅 시 눌렀던 버튼을 아직 안 놓음 → 대기 */ { if (!button_pressed) { boot_btn_released = true; - DBG_PRINTF("[BTN] Boot button released\r\n"); } } - else if (button_pressed) /* 버튼 새로 누르고 있음 */ + else if (button_pressed) /* 버튼 새로 누르고 있음 */ { cnt_s++; - if (cnt_s >= BOOT_THRESHOLD) /* 2초 이상 → 전원 OFF */ + if (cnt_s >= BOOT_THRESHOLD) /* 2초 이상 → 전원 OFF */ { - DBG_PRINTF("[BTN] 2s long press -> Power OFF\r\n"); battery_timer_stop(); k_work_submit(&adv_stop_work); device_on = false; @@ -538,7 +494,7 @@ static void main_s(struct k_timer *timer) return; } } - else /* 버튼 놓음 → 카운터 리셋 */ + else /* 버튼 놓음 → 카운터 리셋 */ { if (cnt_s > 0) { @@ -603,17 +559,17 @@ static enum mgmt_cb_return dfu_status_cb(uint32_t event, { case MGMT_EVT_OP_IMG_MGMT_DFU_STARTED: case MGMT_EVT_OP_IMG_MGMT_DFU_CHUNK: - /* DFU 업로드 중에는 초록 LED를 계속 켬 */ + /* DFU 업로드 중에는 초록 LED ON */ dfu_led_active = true; led_ble_solid(); break; case MGMT_EVT_OP_IMG_MGMT_DFU_PENDING: - /* 업로드 완료 후 리셋 전까지 초록 LED 유지 */ + /* 업로드 완료 후 리셋 전까지 초록 LED ON */ dfu_led_active = true; led_ble_solid(); - /* DFU 리셋 후 버튼 없이 전원 복구 */ + /* DFU 리셋 후 버튼 없이 전원 ON */ #if NRF_POWER_HAS_GPREGRET nrf_power_gpregret_set(NRF_POWER, DFU_RESUME_GPREGRET_REG, DFU_RESUME_MAGIC); #endif @@ -785,7 +741,7 @@ int main(void) /* 리셋/복귀 컨텍스트 */ boot_context_detect(); - /*── Phase 1: 하드웨어 기본 초기화 ──*/ + /* 하드웨어 기본 초기화 */ power_hold_init(); cnt_s = 0; @@ -796,10 +752,10 @@ int main(void) DBG_CORE("\r\n========================================\r\n"); DBG_CORE(" TEST BUILD %s (Zephyr)\r\n", FIRMWARE_VERSION); - DBG_CORE(" BUILD TAG: TEST-ADV-UNIT-001\r\n"); + DBG_CORE(" BUILD TAG: TEST-ADV-UNIT-002\r\n"); DBG_CORE("========================================\r\n"); - DBG_CORE("[1] HW Init\r\n"); + DBG_CORE("[INIT] HW Init\r\n"); /* 기본 하드웨어/센서 초기화 */ gpio_init(); timers_init(); @@ -810,13 +766,13 @@ int main(void) battery_timer_init(); imu_init(); piezo_config_init(); - DBG_CORE(" gpio/timer/config/led/batt/imu/temp/piezo-cfg OK\r\n"); + DBG_CORE("[INIT] HW - gpio/timer/config/led/batt/imu/temp/piezo-cfg OK\r\n"); - /*── Phase 2: BLE 스택 + NUS ──*/ - DBG_CORE("[2] BLE Init\r\n"); + /* BLE 스택 + NUS */ + DBG_CORE("[INIT] BLE Init\r\n"); if (ble_service_init(ble_rx_handler) == 0) { - DBG_CORE(" ble/nus OK\r\n"); + DBG_CORE("[INIT] BLE - ble/nus OK\r\n"); /* 소프트 리셋 후 서비스 복구 */ resume_device_after_soft_reset(); /* BLE 준비까지 확인한 뒤 DFU test 이미지를 확정 */ @@ -824,15 +780,12 @@ int main(void) } else { - DBG_ERR(" ble FAIL\r\n"); + DBG_ERR("[INIT] BLE - ble/nus FAIL\r\n"); } - /*── Phase 3: FDS/NVS ──*/ - /*── Phase 4: 애플리케이션 (TODO) ──*/ - - DBG_CORE("\r\n========================================\r\n"); + DBG_CORE("========================================\r\n"); DBG_CORE(" READY [%s]\r\n", SERIAL_NO); - DBG_CORE("========================================\r\n\r\n"); + DBG_CORE("========================================\r\n"); /* 전원 버튼 상태머신 시작 (부팅 시 버튼이 눌려있는 상태) */ timers_start(); diff --git a/src/main.h b/src/main.h index a0eb889..e459985 100644 --- a/src/main.h +++ b/src/main.h @@ -1,6 +1,6 @@ /******************************************************************************* * @file main.h - * @brief VesiScan BASIC - Zephyr port main header + * @brief VesiScan-Basic ******************************************************************************/ #ifndef MAIN_H__ @@ -13,11 +13,11 @@ #include /*============================================================================== - * Firmware identification + * Firmware identification : Default values, can be overridden by NVS *============================================================================*/ #define FIRMWARE_VERSION "TSTFW042" #define HARDWARE_VERSION "VB0HW0000" -#define SERIAL_NUMBER "VB0260300ZZ" +#define SERIAL_NUMBER "VBT260300ZZ" #define DEFAULT_PASSKEY "123456" /*============================================================================== diff --git a/src/parser.c b/src/parser.c index fc1d1dc..bb6736e 100644 --- a/src/parser.c +++ b/src/parser.c @@ -109,17 +109,6 @@ static bool piezo_config_validate(const piezo_config_t *cfg) return true; } -static void piezo_config_log(const char *prefix, const piezo_config_t *cfg) -{ - DBG_PRINTF("%s freq=%u cycles=%u avg=%u delay=%u samples=%u\r\n", - prefix, - cfg->freq, - cfg->cycles, - cfg->avg, - cfg->delay_us, - cfg->samples); -} - const piezo_config_t *piezo_config_get(void) { return &g_piezo_config; @@ -127,13 +116,12 @@ const piezo_config_t *piezo_config_get(void) int piezo_config_init(void) { - int err = app_nvs_init(&g_piezo_config, piezo_config_validate, piezo_config_log); + int err = app_nvs_init(&g_piezo_config, piezo_config_validate, NULL); if (err) { return err; } - piezo_config_log("[CFG] piezo active", &g_piezo_config); return 0; } @@ -309,11 +297,7 @@ static int send_response_imu(const int16_t accel[3], const int16_t gyro[3]) static void send_response_echo(uint8_t session, uint8_t channel, const uint16_t *samples, uint16_t num_samples) { - /* - * reb: 패킷은 최대 210바이트라서 스택 지역변수로 두면 - * mbb?처럼 반복 호출할 때 워커 스택을 꽤 먹는다. - * 현재는 한 번에 하나의 명령만 처리하므로 정적 버퍼를 재사용한다. - */ + /* 정적 버퍼 사용 (한 번에 한 명령만 처리) */ uint8_t *buf = tx_echo_buf; buf[0] = 'r'; buf[1] = 'e'; buf[2] = 'b'; buf[3] = ':'; @@ -336,10 +320,7 @@ static void send_response_echo(uint8_t session, uint8_t channel, const uint16_t ble_data_send(buf, payload_len + 2); } -static void send_response_bundle(uint16_t batt_mv, - const int16_t accel[3], - const int16_t gyro[3], - int16_t temp_cdeg) +static void send_response_bundle(uint16_t batt_mv, const int16_t accel[3], const int16_t gyro[3], int16_t temp_cdeg) { uint8_t *buf = tx_bundle_buf; @@ -389,18 +370,11 @@ static void send_response_rim(const uint8_t *sample_bytes, uint16_t sample_count buf[payload_len] = (uint8_t)(crc & 0xFF); buf[payload_len + 1U] = (uint8_t)(crc >> 8); - DBG_PRINTF("[MTB] tx rim samples=%u len=%u\r\n", - sample_count, - (uint16_t)(payload_len + 2U)); + DBG_PRINTF("[MTB] tx rim samples=%u len=%u\r\n", sample_count, (uint16_t)(payload_len + 2U)); ble_data_send(buf, (uint16_t)(payload_len + 2U)); } -static void send_response_piezo_config(const char *tag, - uint16_t freq, - uint16_t cycles, - uint16_t avg, - uint16_t delay_us, - uint16_t samples) +static void send_response_piezo_config(const char *tag, uint16_t freq, uint16_t cycles, uint16_t avg, uint16_t delay_us, uint16_t samples) { uint8_t *buf = tx_cfg_buf; @@ -705,7 +679,6 @@ static int cmd_msn(const uint8_t *data, uint8_t data_len) } send_response_u16("rsn:", (uint16_t)mv); - DBG_PRINTF("[CMD] msn -> %d mV\r\n", mv); return 1; } @@ -1283,7 +1256,6 @@ static int cmd_mcf(const uint8_t *data, uint8_t data_len) const piezo_config_t *cfg = piezo_config_get(); send_response_piezo_config("rcf:", cfg->freq, cfg->cycles, cfg->avg, cfg->delay_us, cfg->samples); - piezo_config_log("[CMD] mcf ->", cfg); return 1; } @@ -1297,7 +1269,6 @@ static int cmd_mcs(const uint8_t *data, uint8_t data_len) if (data_len < 10U) { send_response_piezo_config("rcs:", 0xFFFF, 0U, 0U, 0U, 0U); - DBG_PRINTF("[CMD] mcs: insufficient data len=%u\r\n", data_len); return 1; } @@ -1310,7 +1281,6 @@ static int cmd_mcs(const uint8_t *data, uint8_t data_len) if (!piezo_config_validate(&cfg)) { send_response_piezo_config("rcs:", 0xFFFF, cfg.cycles, cfg.avg, cfg.delay_us, cfg.samples); - piezo_config_log("[CMD] mcs invalid", &cfg); return 1; } @@ -1319,12 +1289,10 @@ static int cmd_mcs(const uint8_t *data, uint8_t data_len) if (err) { send_response_piezo_config("rcs:", 0xFFFD, cfg.cycles, cfg.avg, cfg.delay_us, cfg.samples); - piezo_config_log("[CMD] mcs save fail", &g_piezo_config); return 1; } send_response_piezo_config("rcs:", g_piezo_config.freq, g_piezo_config.cycles, g_piezo_config.avg, g_piezo_config.delay_us, g_piezo_config.samples); - piezo_config_log("[CMD] mcs saved", &g_piezo_config); return 1; } @@ -1346,7 +1314,6 @@ static int cmd_mfv(const uint8_t *data, uint8_t data_len) { DBG_ERR("[CMD] mfv tx failed err=%d\r\n", err); } - DBG_PRINTF("[CMD] mfv read\r\n"); return 1; } @@ -1362,7 +1329,6 @@ static int cmd_mid(const uint8_t *data, uint8_t data_len) { DBG_ERR("[CMD] mid tx failed err=%d\r\n", err); } - DBG_PRINTF("[CMD] mid read\r\n"); return 1; } @@ -1371,7 +1337,6 @@ static int cmd_mwh(const uint8_t *data, uint8_t data_len) if (data_len < HW_NO_LENGTH) { send_response_u16("rwh:", 0xFFFF); - DBG_PRINTF("[CMD] mwh: insufficient data len=%u\r\n", data_len); return 1; } @@ -1385,7 +1350,6 @@ static int cmd_mwh(const uint8_t *data, uint8_t data_len) } send_response_ascii("rwh:", HW_NO, HW_NO_LENGTH); - DBG_PRINTF("[CMD] mwh updated\r\n"); return 1; } @@ -1401,7 +1365,6 @@ static int cmd_mrh(const uint8_t *data, uint8_t data_len) { DBG_ERR("[CMD] mrh tx failed err=%d\r\n", err); } - DBG_PRINTF("[CMD] mrh read\r\n"); return 1; } @@ -1410,7 +1373,6 @@ static int cmd_mws(const uint8_t *data, uint8_t data_len) if (data_len < SERIAL_NO_LENGTH) { send_response_u16("rws:", 0xFFFF); - DBG_PRINTF("[CMD] mws: insufficient data len=%u\r\n", data_len); return 1; } @@ -1424,7 +1386,6 @@ static int cmd_mws(const uint8_t *data, uint8_t data_len) } send_response_ascii("rws:", SERIAL_NO, SERIAL_NO_LENGTH); - DBG_PRINTF("[CMD] mws updated\r\n"); return 1; } @@ -1440,7 +1401,6 @@ static int cmd_mrs(const uint8_t *data, uint8_t data_len) { DBG_ERR("[CMD] mrs tx failed err=%d\r\n", err); } - DBG_PRINTF("[CMD] mrs read\r\n"); return 1; } @@ -1449,7 +1409,6 @@ static int cmd_mpz(const uint8_t *data, uint8_t data_len) if (data_len < PASSKEY_LENGTH) { send_response_u16("rpz:", 0xFFFF); - DBG_PRINTF("[CMD] mpz: insufficient data len=%u\r\n", data_len); return 1; } @@ -1486,7 +1445,6 @@ static int cmd_mpz(const uint8_t *data, uint8_t data_len) m_passkey_changed = 1U; send_response_ascii("rpz:", m_static_passkey, PASSKEY_LENGTH); - DBG_PRINTF("[CMD] mpz updated\r\n"); return 1; } @@ -1496,7 +1454,6 @@ static int cmd_mqz(const uint8_t *data, uint8_t data_len) ARG_UNUSED(data_len); send_response_ascii("rqz:", m_static_passkey, PASSKEY_LENGTH); - DBG_PRINTF("[CMD] mqz read\r\n"); return 1; } @@ -1524,12 +1481,10 @@ static int cmd_mls(const uint8_t *data, uint8_t data_len) led_set_state((led_state_t)state); if (state == LED_STATE_OFF) { - int imu_ret = imu_fifo_stop(); - DBG_PRINTF("[CMD] mls: fifo stop ret=%d\r\n", imu_ret); + (void)imu_fifo_stop(); } send_response_u16("rls:", state); - DBG_PRINTF("[CMD] mls -> LED state=%d\r\n", state); return 1; } @@ -1577,7 +1532,7 @@ static const cmd_entry_t cmd_table[] = /*============================================================================== * 파서 엔트리 *============================================================================*/ -int dr_parser(const uint8_t *buf, uint16_t len) +int ble_cmd_dispatch(const uint8_t *buf, uint16_t len) { DBG_CORE("[PARSER] enter len=%u\r\n", len); DBG_CORE("[CMD] RX len=%u\r\n", len); @@ -1619,7 +1574,6 @@ int dr_parser(const uint8_t *buf, uint16_t len) ascii_to_lower((char)buf[3]), '\0' }; - DBG_CORE("[CMD] tag=%s\r\n", tag); const uint8_t *data = buf + 4; uint8_t data_len = (uint8_t)(len - 4U - 2U); @@ -1638,7 +1592,6 @@ int dr_parser(const uint8_t *buf, uint16_t len) return -1; } - DBG_CORE("[CMD] dispatch -> %s\r\n", cmd_table[i].tag); return cmd_table[i].handler(data, data_len); } } diff --git a/src/parser.h b/src/parser.h index 41ed78d..c547ab2 100644 --- a/src/parser.h +++ b/src/parser.h @@ -1,6 +1,6 @@ /******************************************************************************* * @file parser.h - * @brief BLE command parser (Zephyr port) + * @brief BLE command parser * * 4바이트 TAG + DATA + CRC16 패킷 파싱 및 디스패치 ******************************************************************************/ @@ -20,6 +20,6 @@ typedef struct int piezo_config_init(void); const piezo_config_t *piezo_config_get(void); -int dr_parser(const uint8_t *buf, uint16_t len); +int ble_cmd_dispatch(const uint8_t *buf, uint16_t len); #endif /* CMD_PARSER_H__ */ diff --git a/src/power_control.c b/src/power_control.c index c8ea3a4..834f94a 100644 --- a/src/power_control.c +++ b/src/power_control.c @@ -1,8 +1,8 @@ /******************************************************************************* * @file power_control.c - * @brief Device power sequence control (Zephyr port) + * @brief Device power sequence control * - * Power-up sequence state machine with k_timer (single-shot 20ms intervals). + * Power-up sequence state machine with k_timer (single-shot 20ms intervals) ******************************************************************************/ #include #include "main.h" diff --git a/src/power_control.h b/src/power_control.h index 616f9c4..2f95a69 100644 --- a/src/power_control.h +++ b/src/power_control.h @@ -1,6 +1,6 @@ /******************************************************************************* * @file power_control.h - * @brief Device power sequence control (Zephyr port) + * @brief Device power sequence control ******************************************************************************/ #ifndef POWER_CONTROL_H__ #define POWER_CONTROL_H__