Compare commits

...

7 Commits

Author SHA1 Message Date
jh.chun abb8d6078a 전원 버튼 ON/OFF 임계 카운트 분리
- 버튼 감지 타이밍이 초기화 등으로 런타임과 달라서 체감상 3초로 맞추기 위해 분리
2026-07-21 15:51:25 +09:00
jh.chun b6d79ad16a Kconfig 정리 2026-07-21 15:06:39 +09:00
jh.chun 0fc578fb50 주석 정리 2026-07-21 14:41:33 +09:00
jh.chun d3513e3985 CMakeLists 빌드 구성 업데이트 2026-07-21 14:41:15 +09:00
jh.chun 14fba03b62 NVS 모듈 경로 정리
- app_nvs.c/h를 src/nvs/로 이동
- 기기 정보, 패스키, piezo 측정 파라미터 저장/로드 담당
2026-07-21 14:31:35 +09:00
jh.chun e3225215c5 CMakeLists 빌드 구성 업데이트
- 분리된 전원/DFU/BLE RX 명령 큐 모듈 include 경로 및 source 파일 반영
2026-07-21 13:45:25 +09:00
jh.chun b086b0f366 BLE RX 명령 큐 모듈 분리 2026-07-21 13:44:11 +09:00
10 changed files with 163 additions and 152 deletions
+7 -2
View File
@@ -10,6 +10,9 @@ target_include_directories(app PRIVATE
src/command
src/command/handlers
src/ble
src/power
src/dfu
src/nvs
src/drivers/battery
src/drivers/echo_adc
src/drivers/led
@@ -20,15 +23,17 @@ target_include_directories(app PRIVATE
target_sources(app PRIVATE
src/main.c
src/app_nvs.c
src/nvs/app_nvs.c
src/command/parser.c
src/command/ble_cmd_queue.c
src/command/cmd_common.c
src/command/cmd_table.c
src/command/handlers/cmd_device.c
src/command/handlers/cmd_info.c
src/command/handlers/cmd_piezo.c
src/command/handlers/cmd_sensor.c
src/power_control.c
src/power/power_control.c
src/dfu/dfu_manager.c
src/measurement/piezo_measure.c
src/ble/ble_service.c
src/ble/ble_tx_power.c
+1 -6
View File
@@ -125,7 +125,7 @@ CONFIG_BOOTLOADER_MCUBOOT=y
# Enable zcbor library used by mcumgr/SMP/DFU CBOR payloads.
CONFIG_ZCBOR=y
# Version string used by MCUboot imgtool signing and versioned DFU zip naming.
CONFIG_MCUBOOT_IMGTOOL_SIGN_VERSION="0.0.2+26"
CONFIG_MCUBOOT_IMGTOOL_SIGN_VERSION="0.0.0+0"
# Internal flash
# Enable internal flash driver.
@@ -182,8 +182,3 @@ CONFIG_MCUMGR_GRP_OS=y
CONFIG_IMG_MANAGER=y
# Write uploaded image data to flash as a stream.
CONFIG_STREAM_FLASH=y
# Netcore HCI IPC
# nRF52840 build does not use network core HCI IPC.
#CONFIG_SB_CONFIG_NETCORE_HCI_IPC=y
#SB_CONFIG_NETCORE_HCI_IPC=y
+97
View File
@@ -0,0 +1,97 @@
/*******************************************************************************
* @file ble_cmd_queue.c
* @brief BLE RX command queue
******************************************************************************/
#include <stdint.h>
#include <string.h>
#include <zephyr/kernel.h>
#include "ble_cmd_queue.h"
#include "debug_print.h"
#include "parser.h"
#define BLE_CMD_MAX_LEN 256
#define BLE_CMD_QUEUE_DEPTH 8 // RX command ring buffer depth
#define BLE_CMD_WORKQ_STACK_SZ 8192
#define BLE_CMD_WORKQ_PRIORITY 10
static struct k_work_q ble_cmd_work_q;
K_THREAD_STACK_DEFINE(ble_cmd_workq_stack, BLE_CMD_WORKQ_STACK_SZ);
static struct k_work ble_cmd_work; // BLE command worker
static struct k_spinlock ble_cmd_lock; // RX queue lock
static uint8_t ble_cmd_q_buf[BLE_CMD_QUEUE_DEPTH][BLE_CMD_MAX_LEN]; // command data
static uint16_t ble_cmd_q_len[BLE_CMD_QUEUE_DEPTH]; // command length
static uint8_t ble_cmd_q_head; // next pop slot
static uint8_t ble_cmd_q_tail; // next push slot
static uint8_t ble_cmd_q_count; // queued command count
/* Queue에 쌓인 BLE 명령을 parser로 전달 */
static void ble_cmd_work_handler(struct k_work *work)
{
ARG_UNUSED(work);
uint8_t local_buf[BLE_CMD_MAX_LEN];
uint16_t local_len;
for (;;)
{
k_spinlock_key_t key = k_spin_lock(&ble_cmd_lock);
if (ble_cmd_q_count == 0U)
{
k_spin_unlock(&ble_cmd_lock, key);
break;
}
local_len = ble_cmd_q_len[ble_cmd_q_head];
memcpy(local_buf, ble_cmd_q_buf[ble_cmd_q_head], local_len);
ble_cmd_q_head = (uint8_t)((ble_cmd_q_head + 1U) % BLE_CMD_QUEUE_DEPTH);
ble_cmd_q_count--;
k_spin_unlock(&ble_cmd_lock, key);
if (local_len == 0U)
{
DBG_ERR("[BLE RX] worker: empty\r\n");
continue;
}
ble_cmd_dispatch(local_buf, local_len);
}
}
/* BLE RX 명령 처리 queue 초기화 */
void ble_cmd_queue_init(void)
{
k_work_init(&ble_cmd_work, ble_cmd_work_handler);
k_work_queue_start(&ble_cmd_work_q, ble_cmd_workq_stack, K_THREAD_STACK_SIZEOF(ble_cmd_workq_stack), BLE_CMD_WORKQ_PRIORITY, NULL);
}
/* BLE RX 콜백에서 호출되는 명령 enqueue 함수 */
void ble_cmd_queue_rx_handler(const uint8_t *data, uint16_t len)
{
if (len > BLE_CMD_MAX_LEN)
{
DBG_ERR("[BLE RX] drop: len=%u exceeds %d\r\n", len, BLE_CMD_MAX_LEN);
return;
}
k_spinlock_key_t key = k_spin_lock(&ble_cmd_lock);
if (ble_cmd_q_count >= BLE_CMD_QUEUE_DEPTH)
{
k_spin_unlock(&ble_cmd_lock, key);
DBG_ERR("[BLE RX] drop: queue full\r\n");
return;
}
memcpy(ble_cmd_q_buf[ble_cmd_q_tail], data, len);
ble_cmd_q_len[ble_cmd_q_tail] = len;
ble_cmd_q_tail = (uint8_t)((ble_cmd_q_tail + 1U) % BLE_CMD_QUEUE_DEPTH);
ble_cmd_q_count++;
k_spin_unlock(&ble_cmd_lock, key);
int err = k_work_submit_to_queue(&ble_cmd_work_q, &ble_cmd_work);
if (err < 0)
{
DBG_ERR("[BLE RX] queue submit fail err=%d\r\n", err);
}
}
+13
View File
@@ -0,0 +1,13 @@
/*******************************************************************************
* @file ble_cmd_queue.h
* @brief BLE RX command queue
******************************************************************************/
#ifndef BLE_CMD_QUEUE_H__
#define BLE_CMD_QUEUE_H__
#include <stdint.h>
void ble_cmd_queue_init(void);
void ble_cmd_queue_rx_handler(const uint8_t *data, uint16_t len);
#endif /* BLE_CMD_QUEUE_H__ */
+1 -1
View File
@@ -277,7 +277,7 @@ int cmd_maa(const uint8_t *data, uint8_t data_len)
if (status == ECHO_STATUS_OK) // Piezo GPIO, power, ADC initialization/wake succeeded
{
DBG_PRINTF("[MBB] piezo sweep start\r\n");
DBG_PRINTF("[MAA] piezo sweep start\r\n");
status = piezo_measure_sweep();
if (status == ECHO_STATUS_OK)
{
-1
View File
@@ -688,7 +688,6 @@ int imu_fifo_read_latest(uint8_t *sample_bytes,
{
record_count = imu_fifo_compact_records(record_count);
DBG_PRINTF("[IMU FIFO] compact %u -> %u\r\n", raw_count, record_count);
}
if (record_count > max_samples)
{
+34 -133
View File
@@ -1,6 +1,12 @@
/*******************************************************************************
* @file main.c
* @brief VesiScan-Basic
*
* -
* -
* - NVS
* -
* - main loop
******************************************************************************/
#include <zephyr/kernel.h>
@@ -14,24 +20,12 @@
#include "led_control.h"
#include "ble_service.h"
#include "battery_adc.h"
#include "parser.h"
#include "ble_cmd_queue.h"
#include "imu_i2c.h"
#include "piezo_measure.h"
LOG_MODULE_REGISTER(vesiscan, LOG_LEVEL_INF);
#define BLE_CMD_MAX_LEN 256
#define BLE_CMD_QUEUE_DEPTH 8 // RX 명령 링버퍼 깊이(버스트 흡수)
#define BLE_CMD_WORKQ_STACK_SZ 8192
/*
* BLE
* - piezo sweep
* - ADC capture
* - BLE
* -
*/
#define BLE_CMD_WORKQ_PRIORITY 10
volatile bool ble_connection_st = false; // BLE 연결 상태
volatile bool processing = false; // 센서 데이터 처리 중 플래그
bool bond_data_delete = true; // 본딩 데이터 삭제 요청
@@ -46,96 +40,19 @@ static bool resume_without_power_button; // 리셋 후 버튼 없이 복귀
static bool dfu_confirm_pending_boot; // DFU test 이미지 확인 전 자동 복귀
static bool dfu_reset_resume_request; // DFU 완료 리셋 후 자동 복귀
static uint32_t boot_reset_reason; // RESETREAS 원본 값
static struct k_work_q ble_cmd_work_q;
K_THREAD_STACK_DEFINE(ble_cmd_workq_stack, BLE_CMD_WORKQ_STACK_SZ);
static struct k_work ble_cmd_work; // BLE 명령 처리 work
static struct k_spinlock ble_cmd_lock; // RX 큐 보호
// RX 명령 링버퍼: 들어온 순서대로 쌓고 워커가 순차 처리 (head=꺼낼 위치, tail=넣을 위치)
static uint8_t ble_cmd_q_buf[BLE_CMD_QUEUE_DEPTH][BLE_CMD_MAX_LEN]; // 명령 데이터
static uint16_t ble_cmd_q_len[BLE_CMD_QUEUE_DEPTH]; // 각 명령 길이
static uint8_t ble_cmd_q_head; // 다음에 꺼낼 슬롯
static uint8_t ble_cmd_q_tail; // 다음에 넣을 슬롯
static uint8_t ble_cmd_q_count; // 큐에 쌓인 명령 수
static void ble_cmd_work_handler(struct k_work *work)
{
ARG_UNUSED(work);
// work queue 로컬 복사본
uint8_t local_buf[BLE_CMD_MAX_LEN];
uint16_t local_len;
// 큐가 빌 때까지 head에서 하나씩 꺼내 순차 처리 (dispatch는 락 밖에서)
for (;;)
{
k_spinlock_key_t key = k_spin_lock(&ble_cmd_lock);
if (ble_cmd_q_count == 0U)
{
k_spin_unlock(&ble_cmd_lock, key);
break;
}
local_len = ble_cmd_q_len[ble_cmd_q_head];
memcpy(local_buf, ble_cmd_q_buf[ble_cmd_q_head], local_len);
ble_cmd_q_head = (uint8_t)((ble_cmd_q_head + 1U) % BLE_CMD_QUEUE_DEPTH);
ble_cmd_q_count--;
k_spin_unlock(&ble_cmd_lock, key);
if (local_len == 0U)
{
DBG_ERR("[BLE RX] worker: empty\r\n");
continue;
}
// 일반 스레드 문맥 명령 처리
//DBG_CORE("[BLE RX] worker dispatch len=%u\r\n", local_len);
ble_cmd_dispatch(local_buf, local_len);
}
}
/* BLE RX 콜백 */
static void ble_rx_handler(const uint8_t *data, uint16_t len)
{
if (len > BLE_CMD_MAX_LEN)
{
DBG_ERR("[BLE RX] drop: len=%u exceeds %d\r\n", len, BLE_CMD_MAX_LEN);
return;
}
// 공유 RX 큐 보호: tail 슬롯에 명령 적재 (FIFO)
k_spinlock_key_t key = k_spin_lock(&ble_cmd_lock);
if (ble_cmd_q_count >= BLE_CMD_QUEUE_DEPTH)
{
k_spin_unlock(&ble_cmd_lock, key);
DBG_ERR("[BLE RX] drop: queue full\r\n");
return;
}
// 명령 버퍼 복사
memcpy(ble_cmd_q_buf[ble_cmd_q_tail], data, len);
ble_cmd_q_len[ble_cmd_q_tail] = len;
ble_cmd_q_tail = (uint8_t)((ble_cmd_q_tail + 1U) % BLE_CMD_QUEUE_DEPTH);
ble_cmd_q_count++;
k_spin_unlock(&ble_cmd_lock, key);
// 명령 처리 work 예약 (워커는 큐가 빌 때까지 처리하므로 중복 예약은 무해)
// 실패해도 항목은 큐에 유효하게 남아 있고, 다음 RX의 submit이 함께 처리하므로 롤백하지 않음
int err = k_work_submit_to_queue(&ble_cmd_work_q, &ble_cmd_work);
if (err < 0)
{
DBG_ERR("[BLE RX] queue submit fail err=%d\r\n", err);
}
}
/* 전원 유지 (POWER_HOLD) 제어 */
/* 부팅 후 전원 유지 판단 및 모듈 초기화 순서 조립 */
static void boot_context_detect(void)
{
// 부팅 원인 확인
boot_reset_reason = nrf_power_resetreas_get(NRF_POWER);
// DFU 완료 리셋인지 확인
// DFU rest (DFU 완료 후 재부팅)
// GPREGRET은 reset 후에도 유지되는 작은 상태 표시용 레지스터
#if NRF_POWER_HAS_GPREGRET
dfu_reset_resume_request = (nrf_power_gpregret_get(NRF_POWER, DFU_RESUME_GPREGRET_REG) == DFU_RESUME_MAGIC);
// DFU reset 표시 확인 후 다음 부팅 오판 방지를 위해 정리
if (dfu_reset_resume_request)
{
nrf_power_gpregret_set(NRF_POWER, DFU_RESUME_GPREGRET_REG, 0U);
@@ -144,23 +61,25 @@ static void boot_context_detect(void)
dfu_reset_resume_request = false;
#endif
// 소프트 리셋 계열 자동 복귀
// soft reset 후 복귀 (전원 버튼 새 부팅 X)
resume_without_power_button = (boot_reset_reason & (NRF_POWER_RESETREAS_SREQ_MASK | NRF_POWER_RESETREAS_DOG_MASK | NRF_POWER_RESETREAS_LOCKUP_MASK | NRF_POWER_RESETREAS_RESETPIN_MASK)) != 0U;
// 다음 부팅 오판 방지를 위해 RESETREAS 플래그 정리
if (boot_reset_reason != 0U)
{
// RESETREAS 플래그 정리
nrf_power_resetreas_clear(NRF_POWER, boot_reset_reason);
}
}
/* NVS 값이 없을 때 사용할 기본값 초기화 (앱 전역 상태 기본값) - NVS 값이 있는 경우 이후 NVS 값으로 덮어씀 */
static void load_default_config(void)
{
// 기본 문자열 길이 제한
// 기본 문자열 길이 계산
size_t serial_len = strlen(SERIAL_NUMBER);
size_t hw_len = strlen(HARDWARE_VERSION);
size_t passkey_len = strlen(DEFAULT_PASSKEY);
// 버퍼 오버플로우 방지 - 기본 문자열 길이 제한 (긴 경우 잘라내기)
if (serial_len > SERIAL_NO_LENGTH)
{
serial_len = SERIAL_NO_LENGTH;
@@ -176,6 +95,8 @@ static void load_default_config(void)
passkey_len = PASSKEY_LENGTH;
}
// 복사할 때 버퍼를 먼저 0으로 채우고 복사 -> 문자열 종료 NUL 보장
// 기본 Serial Number 복사
memset(SERIAL_NO, 0, sizeof(SERIAL_NO));
memcpy(SERIAL_NO, SERIAL_NUMBER, serial_len);
@@ -187,52 +108,35 @@ static void load_default_config(void)
memset(m_static_passkey, 0, sizeof(m_static_passkey));
memcpy(m_static_passkey, DEFAULT_PASSKEY, passkey_len);
// NVS 로드 전 기본값은 변경 가능 상태
m_passkey_changed = 0U;
m_passkey_changed = 0U; // 패스키 변경 여부 초기화
// 기본 상태값
m_reset_status = 1;
bond_data_delete = true;
m_reset_status = 1; // 기본 reset 상태 코드 설정
bond_data_delete = true; // 기본 상태는 bond 삭제 완료 삭제로 초기화
DBG_CORE("[INIT] Default (S/N=%s)\r\n", SERIAL_NO);
}
/* 타이머 초기화 */
static void timers_init(void)
{
// BLE 명령 처리 work queue
k_work_init(&ble_cmd_work, ble_cmd_work_handler);
k_work_queue_start(&ble_cmd_work_q, ble_cmd_workq_stack, K_THREAD_STACK_SIZEOF(ble_cmd_workq_stack), BLE_CMD_WORKQ_PRIORITY, NULL);
}
/* 메인 함수 */
int main(void)
{
// 리셋/복귀 컨텍스트
boot_context_detect();
// 하드웨어 기본 초기화
#if defined(CONFIG_BOOTLOADER_MCUBOOT)
dfu_confirm_pending_boot = dfu_is_confirm_pending_boot();
dfu_confirm_pending_boot = dfu_is_confirm_pending_boot(); // MCUboot test 이미지 상태인지 확인
#else
dfu_confirm_pending_boot = false;
#endif
power_control_latch_init(resume_without_power_button || dfu_confirm_pending_boot || dfu_reset_resume_request);
power_control_reset_state();
dfu_confirm_running_image();
DBG_CORE("\r\n========================================\r\n");
DBG_CORE(" TEST BUILD %s (Zephyr)\r\n", FIRMWARE_VERSION);
DBG_CORE(" BUILD TAG: %s\r\n", CONFIG_MCUBOOT_IMGTOOL_SIGN_VERSION);
DBG_CORE("========================================\r\n");
DBG_CORE("[INIT] HW Init\r\n");
// 기본 하드웨어/센서 초기화
// 각 모듈 초기화
power_control_init();
timers_init();
ble_cmd_queue_init();
dfu_manager_init();
dfu_log_mcuboot_state();
load_default_config();
@@ -241,20 +145,17 @@ int main(void)
battery_timer_init();
imu_init();
piezo_config_init();
DBG_CORE("[INIT] HW - gpio/timer/config/led/batt/imu/temp/piezo-cfg OK\r\n");
// BLE 스택 + NUS
DBG_CORE("[INIT] BLE Init\r\n");
if (ble_service_init(ble_rx_handler) == 0)
// BLE Stack/NUS 초기화 (0=성공)
if (ble_service_init(ble_cmd_queue_rx_handler) == 0) // BLE RX 콜백은 command queue로 연결
{
DBG_CORE("[INIT] BLE - ble/nus OK\r\n");
// 소프트 리셋 후 서비스 복구
// 버튼 없이 정상 ON 상태로 복구하는 경우
if (resume_without_power_button || dfu_confirm_pending_boot || dfu_reset_resume_request)
{
power_control_resume_after_reset(boot_reset_reason);
}
// BLE 준비까지 확인한 뒤 DFU test 이미지를 확정
dfu_confirm_running_image();
dfu_confirm_running_image(); // BLE 준비까지 확인한 뒤 DFU test 이미지 확정(confirm)
}
else
{
@@ -265,10 +166,10 @@ int main(void)
DBG_CORE(" READY [%s]\r\n", SERIAL_NO);
DBG_CORE("========================================\r\n");
// 전원 버튼 상태머신 시작 (부팅 시 버튼이 눌려있는 상태)
power_control_start();
power_control_start(); // 전원 버튼 상태머신 시작 (부팅 시 버튼이 눌려있는 상태)
// 메인 루프 - idle
// main loop - idle
// callback, work queue 기반 동작 유지
for (;;)
{
k_msleep(100);
View File
View File
+9 -8
View File
@@ -17,11 +17,12 @@
#include "ble_service.h"
#include "battery_adc.h"
#define POWER_HOLD_NODE DT_NODELABEL(pwr_hold) // 전원 래치
#define POWER_BTN_NODE DT_NODELABEL(button_check) // 전원 버튼
#define POWER_LOOP_INTERVAL 20 // 전원 시퀀스 타이머 간격(ms)
#define BOOT_THRESHOLD 350 // power button hold threshold
#define BOND_RESET_THRESHOLD (15000 / POWER_ON_DELAY) // 15s long press bond reset
#define POWER_HOLD_NODE DT_NODELABEL(pwr_hold) // 전원 래치
#define POWER_BTN_NODE DT_NODELABEL(button_check) // 전원 버튼
#define POWER_LOOP_INTERVAL 20 // 전원 시퀀스 타이머 간격(ms)
#define POWER_ON_THRESHOLD 350 // power button hold threshold(off -> on)
#define POWER_OFF_THRESHOLD 500 // power button hold threshold(on -> off)
#define BOND_RESET_THRESHOLD (15000 / POWER_ON_DELAY) // 15s long press bond reset
static const struct gpio_dt_spec power_hold = GPIO_DT_SPEC_GET(POWER_HOLD_NODE, gpios); // 전원 유지 출력
static const struct gpio_dt_spec power_btn = GPIO_DT_SPEC_GET(POWER_BTN_NODE, gpios); // 전원 버튼 입력
@@ -211,7 +212,7 @@ static void main_s(struct k_timer *timer)
{
cnt_s++;
if (cnt_s == BOOT_THRESHOLD) // 임계 시간 도달 시 전원 ON 확정
if (cnt_s == POWER_ON_THRESHOLD) // 임계 시간 도달 시 전원 ON 확정
{
device_on = true;
power_control_handler(ON, "button-2s-latch");
@@ -248,7 +249,7 @@ static void main_s(struct k_timer *timer)
{
cnt_s++;
if (cnt_s >= BOOT_THRESHOLD)
if (cnt_s >= POWER_OFF_THRESHOLD)
{
battery_timer_stop();
k_work_submit(&adv_stop_work);
@@ -364,7 +365,7 @@ void power_control_start(void)
k_timer_start(&m_power_on_delay_timer, K_MSEC(POWER_ON_DELAY), K_NO_WAIT);
}
/* 소프트 리셋/DFU 리셋 후 버튼 없이 ON 상태 복구 */
/* soft reset/DFU reset/test image 부팅 시 버튼 없이 정상 ON 상태 복구 */
void power_control_resume_after_reset(uint32_t reset_reason)
{
device_on = true;