코드 정리

This commit is contained in:
2026-06-26 17:20:45 +09:00
parent 6c7fa6d0e6
commit 1a78656c8f
14 changed files with 105 additions and 265 deletions
+22 -79
View File
@@ -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 <zephyr/kernel.h>
#include <zephyr/bluetooth/bluetooth.h>
@@ -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)
{
+3 -5
View File
@@ -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) */
+7 -9
View File
@@ -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 <zephyr/kernel.h>
#include <zephyr/drivers/adc.h>
@@ -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)
+1 -1
View File
@@ -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__
+1 -3
View File
@@ -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
-3
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
******************************************************************************/
+54 -101
View File
@@ -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):
* [OFFON] 2 P0.08 ON + LED
* 2 OFF ( )
* [ONOFF] 2 sleep_mode_enter()
* :
* - BOOT_THRESHOLD P0.08 ON
* - LED advertising , , BLE advertising
* - = POWER_ON_DELAY * BOOT_THRESHOLD
*
* /DFU :
* - advertising을
*
* :
* [OFFON] 2 ON ( 2, 1)
*
* [ONOFF] advertising + POWER_OFF_DELAY
*
******************************************************************************/
#include <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h>
@@ -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();
+3 -3
View File
@@ -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 <stdbool.h>
/*==============================================================================
* 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"
/*==============================================================================
+7 -54
View File
@@ -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);
}
}
+2 -2
View File
@@ -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__ */
+2 -2
View File
@@ -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 <zephyr/kernel.h>
#include "main.h"
+1 -1
View File
@@ -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__