Compare commits

..

19 Commits

Author SHA1 Message Date
jh.chun 9f252c7bef BLE 연결 해제 헬퍼 함수 및 에러별 분기 처리 2026-04-21 16:54:51 +09:00
jh.chun be1ca58bd6 버전 업데이트 2026-04-21 16:52:21 +09:00
jh.chun e20e8f5665 전원 버튼 타이머 조건 변경 2026-04-21 16:51:54 +09:00
jh.chun 2bff259fa3 코드 정리 2026-04-21 14:59:47 +09:00
jh.chun c72349cf13 NUS characteristic: MITM 인증 완료된 연결만 접근 가능하도록 수정
- SEC_OPEN -> SEC_MITM
- 기존에는 SoftDevice 레벨에서 인증 전 write를 차단하지 않음
2026-04-21 14:08:57 +09:00
jh.chun a0aa180ef1 DFU characteristic: MITM 인증 완료된 연결만 접근 가능하도록 수정
- SEC_OPEN -> SEC_MITM
- 기존에는 페어링 완료 전에도 DFU characteristic write 가능, BLE SMP 타임아웃(30초) 전에 wirte하면 부트로더로 재부팅되어 인증 없이 DFU 모드 진입 가능했음
2026-04-21 14:08:02 +09:00
jh.chun e9eb89f203 DFU 진입 시 주변 장치 및 타이머 정리 추가
- PREPARE 이벤트 시 타이머(메인, 배터리/온도) 중지 및 비동기 측정 중단 및 상태 리셋, Piezo 전원 차단
- 정리하지 않는 경우 Piezo TX 비정상 신호 출력/Flash 데이터 깨질 가능성 존재
2026-04-21 10:51:02 +09:00
jh.chun 790e3a1b82 미사용 변수 삭제 2026-04-21 09:56:24 +09:00
jh.chun 50a8e68cf4 BLE 인증 전 NUS 명령 실행 방지
- GAP 연결 시 PROD 모드에서 ble_connection_st 조기 활성화 제거(보안 인증 후 활성화)
- nus_data_handler()에 인증 상태 검증 가드 추가(ble_connection_st가 0인 경우 받지 않고 즉시 리턴)
2026-04-21 09:41:12 +09:00
jh.chun 1fa0d8b30b FW 버전 업데이트 2026-04-20 14:23:19 +09:00
jh.chun 36cd84e49c 테스트용 함수 삭제 2026-04-20 14:22:55 +09:00
jh.chun 163593c0cd 코드 정리 2026-04-20 14:22:43 +09:00
jh.chun 009897a7d8 Piezo TX/RX Active 전 딜레이 3ms -> 10ms
- ch0 데이터 안정화를 위함
2026-04-20 14:22:13 +09:00
jh.chun 93a11b7519 전원 버튼 부팅 판정 시간을 실측 기준으로 보정
- 버튼 카운터가 초기화 후부터 증가하기 때문에 체감 2초 부팅에 맞게 부팅 판정 tick을 400(2s) -> 250(1.25s)로 변경
2026-04-20 12:12:51 +09:00
jh.chun 2db6e36c08 MUX 채널 수정
ch0: A0
ch1: A1
ch2: A2
ch3: A3
ch4: B3
ch5: B2
2026-04-20 09:19:10 +09:00
jh.chun 231a000849 delta_buffer 크기 400 -> 420
- num_samples=140일 때 압축 worst case = 419바이트
2026-04-17 14:20:28 +09:00
jh.chun 0294cdb428 사용하지 않는 매크로 삭제: LESC 및 Static Passkey 관련
- ble 관련이 ble_quick_security 파일로 가면서 정의만 남은 매크로
2026-04-17 13:48:02 +09:00
jh.chun 80c846fd25 미사용 AES 버퍼 삭제 2026-04-17 12:22:28 +09:00
jh.chun 2daa8b3690 passkey 보안: 런타임 플래그
- 런타임 플래그 factory_provisioned=0인 경우에만 passkey 쓰기 가능
2026-04-17 12:15:39 +09:00
14 changed files with 189 additions and 254 deletions
@@ -216,9 +216,9 @@ uint32_t ble_dfu_buttonless_char_add(ble_dfu_buttonless_t * p_dfu)
add_char_params.is_var_len = true; add_char_params.is_var_len = true;
add_char_params.max_len = BLE_GATT_ATT_MTU_DEFAULT; add_char_params.max_len = BLE_GATT_ATT_MTU_DEFAULT;
add_char_params.cccd_write_access = SEC_OPEN; add_char_params.cccd_write_access = SEC_MITM;
add_char_params.write_access = SEC_OPEN; add_char_params.write_access = SEC_MITM;
add_char_params.read_access = SEC_OPEN; add_char_params.read_access = SEC_MITM;
return characteristic_add(p_dfu->service_handle, &add_char_params, &p_dfu->control_point_char); return characteristic_add(p_dfu->service_handle, &add_char_params, &p_dfu->control_point_char);
} }
@@ -283,8 +283,8 @@ uint32_t ble_nus_init(ble_nus_t * p_nus, ble_nus_init_t const * p_nus_init)
add_char_params.char_props.write = 1; add_char_params.char_props.write = 1;
add_char_params.char_props.write_wo_resp = 1; add_char_params.char_props.write_wo_resp = 1;
add_char_params.read_access = SEC_OPEN; add_char_params.read_access = SEC_MITM;
add_char_params.write_access = SEC_OPEN; add_char_params.write_access = SEC_MITM;
err_code = characteristic_add(p_nus->service_handle, &add_char_params, &p_nus->rx_handles); err_code = characteristic_add(p_nus->service_handle, &add_char_params, &p_nus->rx_handles);
if (err_code != NRF_SUCCESS) if (err_code != NRF_SUCCESS)
@@ -302,9 +302,9 @@ uint32_t ble_nus_init(ble_nus_t * p_nus, ble_nus_init_t const * p_nus_init)
add_char_params.is_var_len = true; add_char_params.is_var_len = true;
add_char_params.char_props.notify = 1; add_char_params.char_props.notify = 1;
add_char_params.read_access = SEC_OPEN; add_char_params.read_access = SEC_MITM;
add_char_params.write_access = SEC_OPEN; add_char_params.write_access = SEC_MITM;
add_char_params.cccd_write_access = SEC_OPEN; add_char_params.cccd_write_access = SEC_MITM;
return characteristic_add(p_nus->service_handle, &add_char_params, &p_nus->tx_handles); return characteristic_add(p_nus->service_handle, &add_char_params, &p_nus->tx_handles);
/**@snippet [Adding proprietary characteristic to the SoftDevice] */ /**@snippet [Adding proprietary characteristic to the SoftDevice] */
@@ -138,11 +138,18 @@ int Cmd_mrs(const ParsedCmd *cmd)
*============================================================================*/ *============================================================================*/
int Cmd_mpz(const ParsedCmd *cmd) int Cmd_mpz(const ParsedCmd *cmd)
{ {
if (m_config.factory_provisioned != 0)
{
dr_ble_return_1("rpz:", 0xFFFF);
return 1;
}
char passkey[7] = {0}; char passkey[7] = {0};
dr_get_ascii(cmd, 0, passkey, 6); dr_get_ascii(cmd, 0, passkey, 6);
memcpy(m_static_passkey, passkey, 6); memcpy(m_static_passkey, passkey, 6);
memcpy(m_config.static_passkey, m_static_passkey, 6); memcpy(m_config.static_passkey, m_static_passkey, 6);
m_config.factory_provisioned = 1;
config_save(); config_save();
ascii_format_data(ble_bin_buffer, "rpz:", passkey, 6); ascii_format_data(ble_bin_buffer, "rpz:", passkey, 6);
@@ -112,6 +112,9 @@ void fds_default_value_set(void)
m_config.piezo_num_samples = 100; /* 100 samples */ m_config.piezo_num_samples = 100; /* 100 samples */
m_config.piezo_cycles = 7; /* 7 cycles */ m_config.piezo_cycles = 7; /* 7 cycles */
m_config.piezo_averaging = 3; /* 3x averaging */ m_config.piezo_averaging = 3; /* 3x averaging */
/* Factory provisioning — default: not provisioned */
m_config.factory_provisioned = 0;
} }
@@ -130,39 +133,35 @@ static void fds_evt_handler( fds_evt_t const *p_evt )
{ {
fds_last_evt = p_evt->id; fds_last_evt = p_evt->id;
switch( p_evt->id ) switch (p_evt->id)
{ {
case FDS_EVT_INIT: case FDS_EVT_INIT:
if( p_evt->result == NRF_SUCCESS ) if (p_evt->result == NRF_SUCCESS)
{ {
m_fds_initialized = true; m_fds_initialized = true;
} }
break; break;
case FDS_EVT_WRITE: case FDS_EVT_WRITE:
{
fds_flag_write = false; fds_flag_write = false;
}
break; break;
case FDS_EVT_UPDATE: case FDS_EVT_UPDATE:
{
fds_flag_write = false; fds_flag_write = false;
if(go_device_power_off == true) if (go_device_power_off == true)
{ {
device_power_off(); device_power_off();
} }
if(go_sleep_mode_enter == true) if (go_sleep_mode_enter == true)
{ {
sleep_mode_enter(); sleep_mode_enter();
} }
if(go_NVIC_SystemReset == true) if (go_NVIC_SystemReset == true)
{ {
DBG_PRINTF("Off FDS_EVENT\r\n"); DBG_PRINTF("Off FDS_EVENT\r\n");
NVIC_SystemReset(); NVIC_SystemReset();
} }
}
break; break;
case FDS_EVT_DEL_RECORD: case FDS_EVT_DEL_RECORD:
@@ -188,7 +187,7 @@ static void fds_evt_handler( fds_evt_t const *p_evt )
static void wait_for_fds_ready( void ) static void wait_for_fds_ready( void )
{ {
uint32_t timeout = 0; uint32_t timeout = 0;
while( !m_fds_initialized ) while(!m_fds_initialized)
{ {
nrf_pwr_mgmt_run(); nrf_pwr_mgmt_run();
nrf_delay_ms(1); nrf_delay_ms(1);
@@ -237,7 +236,7 @@ void config_load( void )
goto cfg_load_start; goto cfg_load_start;
} }
if( rc == NRF_SUCCESS ) if (rc == NRF_SUCCESS)
{ {
fds_flash_record_t config = { 0 }; fds_flash_record_t config = { 0 };
@@ -259,7 +258,7 @@ void config_load( void )
DBG_PRINTF("[FDS] magic=0x%08X (expect 0x%08X)\r\n", m_config.magic_number, CONFIG_MAGIC_NUMBER_VALUE); DBG_PRINTF("[FDS] magic=0x%08X (expect 0x%08X)\r\n", m_config.magic_number, CONFIG_MAGIC_NUMBER_VALUE);
if( m_config.magic_number != (uint32_t)CONFIG_MAGIC_NUMBER_VALUE ) if (m_config.magic_number != (uint32_t)CONFIG_MAGIC_NUMBER_VALUE)
{ {
DBG_PRINTF("[FDS] FORMAT! overwriting with defaults\r\n"); DBG_PRINTF("[FDS] FORMAT! overwriting with defaults\r\n");
rc = fds_record_delete(&desc); rc = fds_record_delete(&desc);
@@ -269,7 +268,7 @@ void config_load( void )
fds_default_value_set(); fds_default_value_set();
rc = fds_record_update(&desc, &m_dummy_record); rc = fds_record_update(&desc, &m_dummy_record);
if( (rc != NRF_SUCCESS) && (rc == FDS_ERR_NO_SPACE_IN_FLASH) ) if ((rc != NRF_SUCCESS) && (rc == FDS_ERR_NO_SPACE_IN_FLASH))
{ {
rc = fds_gc(); rc = fds_gc();
APP_ERROR_CHECK(rc); APP_ERROR_CHECK(rc);
@@ -301,20 +300,20 @@ void config_load( void )
fds_wait_cnt = 0; fds_wait_cnt = 0;
while(fds_flag_write && fds_wait_cnt < 3000) /* 3 second timeout */ while (fds_flag_write && fds_wait_cnt < 3000) /* 3 second timeout */
{ {
nrf_pwr_mgmt_run(); nrf_pwr_mgmt_run();
nrf_delay_ms(1); nrf_delay_ms(1);
fds_wait_cnt++; fds_wait_cnt++;
} }
if(fds_flag_write) if (fds_flag_write)
{ {
DBG_PRINTF("[FDS] write TIMEOUT! forcing flag clear\r\n"); DBG_PRINTF("[FDS] write TIMEOUT! forcing flag clear\r\n");
fds_flag_write = false; fds_flag_write = false;
} }
if( (rc != NRF_SUCCESS) && (rc == FDS_ERR_NO_SPACE_IN_FLASH) ) if ((rc != NRF_SUCCESS) && (rc == FDS_ERR_NO_SPACE_IN_FLASH))
{ {
rc = fds_gc(); rc = fds_gc();
APP_ERROR_CHECK(rc); APP_ERROR_CHECK(rc);
@@ -370,7 +369,7 @@ void config_save( void )
} }
} }
if( m_config.magic_number != (uint32_t)CONFIG_MAGIC_NUMBER_VALUE ) if (m_config.magic_number != (uint32_t)CONFIG_MAGIC_NUMBER_VALUE)
{ {
m_config.magic_number = CONFIG_MAGIC_NUMBER_VALUE; m_config.magic_number = CONFIG_MAGIC_NUMBER_VALUE;
} }
@@ -380,13 +379,13 @@ void config_save( void )
rc = fds_record_find(CONFIG_FILE, CONFIG_REC_KEY, &desc, &tok); rc = fds_record_find(CONFIG_FILE, CONFIG_REC_KEY, &desc, &tok);
DBG_PRINTF("[CFG_SAVE] find rc=%u\r\n", rc); DBG_PRINTF("[CFG_SAVE] find rc=%u\r\n", rc);
if( rc == NRF_SUCCESS ) if (rc == NRF_SUCCESS)
{ {
fds_flag_write = true; fds_flag_write = true;
rc = fds_record_update(&desc, &m_dummy_record); rc = fds_record_update(&desc, &m_dummy_record);
DBG_PRINTF("[CFG_SAVE] update rc=%u\r\n", rc); DBG_PRINTF("[CFG_SAVE] update rc=%u\r\n", rc);
if( rc == FDS_ERR_NO_SPACE_IN_FLASH ) if (rc == FDS_ERR_NO_SPACE_IN_FLASH)
{ {
fds_flag_write = false; fds_flag_write = false;
rc = fds_gc(); rc = fds_gc();
@@ -396,7 +395,7 @@ void config_save( void )
DBG_PRINTF("[CFG_SAVE] retry rc=%u\r\n", rc); DBG_PRINTF("[CFG_SAVE] retry rc=%u\r\n", rc);
} }
if( rc != NRF_SUCCESS ) if (rc != NRF_SUCCESS)
{ {
DBG_PRINTF("[CFG_SAVE] FAIL rc=%u\r\n", rc); DBG_PRINTF("[CFG_SAVE] FAIL rc=%u\r\n", rc);
fds_flag_write = false; fds_flag_write = false;
@@ -409,7 +408,7 @@ void config_save( void )
rc = fds_record_write(&desc, &m_dummy_record); rc = fds_record_write(&desc, &m_dummy_record);
DBG_PRINTF("[CFG_SAVE] write rc=%u\r\n", rc); DBG_PRINTF("[CFG_SAVE] write rc=%u\r\n", rc);
if( rc != NRF_SUCCESS ) if ( rc != NRF_SUCCESS )
{ {
DBG_PRINTF("[CFG_SAVE] FAIL rc=%u\r\n", rc); DBG_PRINTF("[CFG_SAVE] FAIL rc=%u\r\n", rc);
fds_flag_write = false; fds_flag_write = false;
@@ -5,7 +5,7 @@
* via the Nordic FDS library. Replaces external EEPROM and coexists safely * via the Nordic FDS library. Replaces external EEPROM and coexists safely
* with the SoftDevice. * with the SoftDevice.
* *
* config_data_t (48 bytes, packed): * config_data_t (49 bytes, packed):
* magic_number (4B) : format validation (0x20231226) * magic_number (4B) : format validation (0x20231226)
* hw_no (12B): hardware version string * hw_no (12B): hardware version string
* serial_no (12B): serial number (also used as BLE device name) * serial_no (12B): serial number (also used as BLE device name)
@@ -14,6 +14,7 @@
* reset_status (1B) : reset cause code * reset_status (1B) : reset cause code
* life_cycle (4B) : device usage count * life_cycle (4B) : device usage count
* piezo_* (8B) : piezo measurement parameters * piezo_* (8B) : piezo measurement parameters
* factory_provisioned(1B): passkey provisioning lock flag
* *
* API: * API:
* fs_storage_init() : initialise FDS (once at boot) * fs_storage_init() : initialise FDS (once at boot)
@@ -65,7 +66,10 @@ typedef struct
uint16_t piezo_averaging; /* 2B - averages per channel (1..10) */ uint16_t piezo_averaging; /* 2B - averages per channel (1..10) */
uint16_t piezo_delay_us; /* 2B - delay from TX pulse to ADC start (us) (0..30) */ uint16_t piezo_delay_us; /* 2B - delay from TX pulse to ADC start (us) (0..30) */
uint16_t piezo_num_samples; /* 2B - ADC sample count (80..140) */ uint16_t piezo_num_samples; /* 2B - ADC sample count (80..140) */
} config_data_t; /* Total: 48 bytes */
/* Factory provisioning lock */
uint8_t factory_provisioned; /* 1B - 0=passkey not set, 1=passkey set (locked) */
} config_data_t; /* Total: 49 bytes */
extern config_data_t m_config; extern config_data_t m_config;
@@ -39,10 +39,12 @@ static void twi_uninitialize(void)
} }
/* Initialise the TWI peripheral (SCL/SDA pins, 400 kHz, blocking mode). */ /* Initialise the TWI peripheral (SCL/SDA pins, 400 kHz, blocking mode). */
static void twi_initialize(void){ static void twi_initialize(void)
{
ret_code_t err_code; ret_code_t err_code;
const nrfx_twi_config_t twi_config = { const nrfx_twi_config_t twi_config =
{
.scl = ICM42670_I2C_SCL_PIN, .scl = ICM42670_I2C_SCL_PIN,
.sda = ICM42670_I2C_SDA_PIN, .sda = ICM42670_I2C_SDA_PIN,
.frequency = NRF_TWI_FREQ_400K, .frequency = NRF_TWI_FREQ_400K,
@@ -108,7 +108,6 @@
#endif #endif
/* -- Crypto / command parser / debug -- */ /* -- Crypto / command parser / debug -- */
#include "nrf_crypto.h" /* nRF crypto library (AES, etc.) */
#include "parser.h" /* Binary command parser (dr_cmd_parser) */ #include "parser.h" /* Binary command parser (dr_cmd_parser) */
#include "cmd_table.h" /* Command table registration (cmd_table_init) */ #include "cmd_table.h" /* Command table registration (cmd_table_init) */
#include "debug_print.h" /* Debug output macro (DBG_PRINTF) */ #include "debug_print.h" /* Debug output macro (DBG_PRINTF) */
@@ -116,8 +115,6 @@
#include "dr_piezo.h" /* Piezo ultrasound driver */ #include "dr_piezo.h" /* Piezo ultrasound driver */
#include "led_control.h" /* LED direct control driver */ #include "led_control.h" /* LED direct control driver */
/*============================================================================== /*==============================================================================
* Build Configuration * Build Configuration
*============================================================================*/ *============================================================================*/
@@ -149,29 +146,6 @@
#define NEXT_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(30000) /* Subsequent param update interval: 30s */ #define NEXT_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(30000) /* Subsequent param update interval: 30s */
#define MAX_CONN_PARAMS_UPDATE_COUNT 3 /* Max param update attempts */ #define MAX_CONN_PARAMS_UPDATE_COUNT 3 /* Max param update attempts */
/* -- BLE security parameters (when FEATURE_SECURE_CONNECTION enabled) --
* Bonding (BOND=1): store pairing info in flash for reconnection
* MITM protection (MITM=1): prevent man-in-the-middle attacks (passkey auth required)
* LESC: disabled(0) in static passkey mode, enabled(1) in dynamic passkey mode
* IO capabilities: DISPLAY_ONLY -> device displays passkey, user enters in app
*/
#if FEATURE_SECURE_CONNECTION
#define LESC_DEBUG_MODE 0 /* LESC debug mode disabled */
#define SEC_PARAM_BOND 1 /* Bonding enabled (store pairing info) */
#define SEC_PARAM_MITM 1 /* MITM protection enabled */
#if FEATURE_STATIC_PASSKEY
#define SEC_PARAM_LESC 0 /* Static passkey mode: LESC disabled */
#else
#define SEC_PARAM_LESC 1 /* Dynamic passkey mode: LESC enabled */
#endif
#define SEC_PARAM_KEYPRESS 0 /* Keypress notification disabled */
#define SEC_PARAM_IO_CAPABILITIES BLE_GAP_IO_CAPS_DISPLAY_ONLY /* Display only */
#define SEC_PARAM_OOB 0 /* No OOB (Out-of-Band) data */
#define SEC_PARAM_MIN_KEY_SIZE 7 /* Min encryption key size (bytes) */
#define SEC_PARAM_MAX_KEY_SIZE 16 /* Max encryption key size (bytes) */
#define PASSKEY_TXT_LENGTH 8 /* Passkey text buffer length */
#endif
/*============================================================================== /*==============================================================================
* System Constants * System Constants
*============================================================================*/ *============================================================================*/
@@ -181,9 +155,6 @@
#define POWER_ON_DELAY 5 /* Power button polling interval (5ms) */ #define POWER_ON_DELAY 5 /* Power button polling interval (5ms) */
#define POWER_OFF_DELAY 3000 /* Power-off delay: 3s after LED indication */ #define POWER_OFF_DELAY 3000 /* Power-off delay: 3s after LED indication */
#define POWER_RESET_DELAY 2000 /* Reset delay: 2s */ #define POWER_RESET_DELAY 2000 /* Reset delay: 2s */
#define LED_NUM 24 /* LED pin number */
#define AES_KEY_SIZE 16 /* AES encryption key size (128-bit) */
#define AES_BLOCK_SIZE 16 /* AES block size (128-bit) */
/*============================================================================== /*==============================================================================
* BLE Instances (statically allocated via SoftDevice macros) * BLE Instances (statically allocated via SoftDevice macros)
@@ -219,18 +190,11 @@ static uint8_t c_addr[6] = {0}; /* Connected peer BLE a
static char * roles_str[] = {"INVALID_ROLE", "CENTRAL", "PERIPHERAL"}; static char * roles_str[] = {"INVALID_ROLE", "CENTRAL", "PERIPHERAL"};
/*==============================================================================
* Global Variables
* IEC 62304 §5.5.3: all global buffers zero-initialized for deterministic startup
*============================================================================*/
uint8_t m_encrypted_text[AES_BLOCK_SIZE] = {0}; /* AES encryption result buffer */
uint8_t m_encrypted_text2[AES_BLOCK_SIZE] = {0}; /* AES encryption result buffer 2 */
uint8_t m_decrypted_text[AES_BLOCK_SIZE] = {0}; /* AES decryption result buffer */
volatile uint8_t Sj_type; /* Command type identifier */
bool power_off_duble_prohibit = false; /* Power-off double prevention flag */ bool power_off_duble_prohibit = false; /* Power-off double prevention flag */
volatile bool power_state = false; /* Power state tracking */
/*==============================================================================
* Extern Variables and Functions
*============================================================================*/
/* -- External module flags (defined in main_timer/power_control) -- */ /* -- External module flags (defined in main_timer/power_control) -- */
extern bool go_device_power_off; /* Power-off request flag */ extern bool go_device_power_off; /* Power-off request flag */
extern bool go_sleep_mode_enter; /* Sleep mode entry request flag */ extern bool go_sleep_mode_enter; /* Sleep mode entry request flag */
@@ -242,6 +206,14 @@ extern bool info4; /* Device info transmission complete flag
extern uint8_t add_cycle; /* Additional measurement cycle counter */ extern uint8_t add_cycle; /* Additional measurement cycle counter */
extern bool motion_raw_data_enabled; /* Motion raw data streaming enabled */ extern bool motion_raw_data_enabled; /* Motion raw data streaming enabled */
/* -- External module functions (used in DFU prepare / disconnect cleanup) -- */
extern void maa_async_abort(void);
extern bool maa_async_on_tx_ready(void);
extern bool maa_async_is_busy(void);
extern void dr_piezo_power_off(void);
extern void battery_timer_stop(void);
extern void main_timer_stop(void);
uint16_t cnt_s; /* Power button polling counter (5ms units, 150=0.75s) */ uint16_t cnt_s; /* Power button polling counter (5ms units, 150=0.75s) */
char ble_tx_buffer[BLE_NUS_MAX_DATA_LEN] = {0}; /* BLE text transmission buffer */ char ble_tx_buffer[BLE_NUS_MAX_DATA_LEN] = {0}; /* BLE text transmission buffer */
uint8_t ble_bin_buffer[BLE_NUS_MAX_DATA_LEN] = {0}; /* BLE binary response buffer */ uint8_t ble_bin_buffer[BLE_NUS_MAX_DATA_LEN] = {0}; /* BLE binary response buffer */
@@ -263,7 +235,6 @@ static volatile bool s_tx_pending = false; /* TX re
static uint8_t s_tx_pending_buf[BLE_NUS_MAX_DATA_LEN] = {0}; /* Pending packet (with CRC) */ static uint8_t s_tx_pending_buf[BLE_NUS_MAX_DATA_LEN] = {0}; /* Pending packet (with CRC) */
static uint16_t s_tx_pending_len = 0; /* Pending packet length */ static uint16_t s_tx_pending_len = 0; /* Pending packet length */
char m_static_passkey[PASSKEY_LENGTH] = DEFAULT_PASSKEY; /* Static passkey (6 digits, loaded from FDS) */ char m_static_passkey[PASSKEY_LENGTH] = DEFAULT_PASSKEY; /* Static passkey (6 digits, loaded from FDS) */
char SERIAL_NO[SERIAL_NO_LENGTH] = {0}; /* Serial number (used as BLE device name) */ char SERIAL_NO[SERIAL_NO_LENGTH] = {0}; /* Serial number (used as BLE device name) */
char HW_NO[HW_NO_LENGTH] = {0}; /* Hardware number (FDS stored/read) */ char HW_NO[HW_NO_LENGTH] = {0}; /* Hardware number (FDS stored/read) */
@@ -516,6 +487,10 @@ static void ble_dfu_evt_handler(ble_dfu_buttonless_evt_type_t event)
{ {
case BLE_DFU_EVT_BOOTLOADER_ENTER_PREPARE: case BLE_DFU_EVT_BOOTLOADER_ENTER_PREPARE:
DBG_PRINTF("[DFU] Prepare\r\n"); DBG_PRINTF("[DFU] Prepare\r\n");
maa_async_abort();
dr_piezo_power_off();
battery_timer_stop();
main_timer_stop();
break; break;
case BLE_DFU_EVT_BOOTLOADER_ENTER: case BLE_DFU_EVT_BOOTLOADER_ENTER:
DBG_PRINTF("[DFU] Enter\r\n"); DBG_PRINTF("[DFU] Enter\r\n");
@@ -525,7 +500,6 @@ static void ble_dfu_evt_handler(ble_dfu_buttonless_evt_type_t event)
break; break;
case BLE_DFU_EVT_RESPONSE_SEND_ERROR: case BLE_DFU_EVT_RESPONSE_SEND_ERROR:
DBG_PRINTF("[DFU] Error\r\n"); DBG_PRINTF("[DFU] Error\r\n");
APP_ERROR_CHECK(false);
break; break;
default: default:
break; break;
@@ -584,10 +558,6 @@ static void nrf_qwr_error_handler(uint32_t nrf_error)
APP_ERROR_HANDLER(nrf_error); APP_ERROR_HANDLER(nrf_error);
} }
/* Async MAA TX complete handler (forward declarations) */
extern bool maa_async_on_tx_ready(void);
extern bool maa_async_is_busy(void);
extern void maa_async_abort(void);
/* 2026-03-17: Prevent blocking in BLE callback -- buffer command for main loop processing */ /* 2026-03-17: Prevent blocking in BLE callback -- buffer command for main loop processing */
static volatile uint8_t pending_cmd_buf[BLE_NUS_MAX_DATA_LEN] = {0}; static volatile uint8_t pending_cmd_buf[BLE_NUS_MAX_DATA_LEN] = {0};
@@ -604,6 +574,11 @@ static void nus_data_handler(ble_nus_evt_t * p_evt)
{ {
if (p_evt->type == BLE_NUS_EVT_RX_DATA) if (p_evt->type == BLE_NUS_EVT_RX_DATA)
{ {
if (!ble_connection_st)
{
return; /* Reject command before security is established */
}
cmd_type_t = CMD_BLE; cmd_type_t = CMD_BLE;
ble_got_new_data = true; ble_got_new_data = true;
@@ -1079,11 +1054,14 @@ static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
break; break;
case BLE_GAP_EVT_CONNECTED: case BLE_GAP_EVT_CONNECTED:
DBG_PRINTF("[BLE] Connected\r\n"); DBG_PRINTF("[BLE] GAP Connected\r\n");
#if FEATURE_SECURE_CONNECTION #if FEATURE_SECURE_CONNECTION
if (BLE_DEV_MODE)
{
ble_connection_st = 1; ble_connection_st = 1;
battery_timer_start(); battery_timer_start();
}
#endif #endif
m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle; m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
@@ -1637,13 +1615,13 @@ static void main_s(void * p_context)
/* ---- Boot phase ---- */ /* ---- Boot phase ---- */
if (button_released) if (button_released)
{ {
if ((cnt_s < 400) && (m_reset_status != 2)) if ((cnt_s < 250) && (m_reset_status != 2))
{ {
led_set_state(LED_STATE_OFF); led_set_state(LED_STATE_OFF);
power_control_handler(OFF); power_control_handler(OFF);
cnt_s = 0; cnt_s = 0;
} }
else if (cnt_s > 3000) /* 3000 x 5ms = 15s */ else if (cnt_s >= 3000) /* 3000 x 5ms = 15s */
{ {
DBG_PRINTF("[BTN] Bond Delete\r\n"); DBG_PRINTF("[BTN] Bond Delete\r\n");
power_control_handler(ON); power_control_handler(ON);
@@ -1655,7 +1633,7 @@ static void main_s(void * p_context)
go_device_power_off = true; go_device_power_off = true;
main_timer_start(); main_timer_start();
} }
else if (cnt_s > 400 || (m_reset_status == 2)) /* 400 x 5ms = 2s */ else if (cnt_s >= 250 || (m_reset_status == 2)) /* 250 x 5ms = 1.25s + α */
{ {
DBG_PRINTF("[BTN] Boot (cnt=%d)\r\n", cnt_s); DBG_PRINTF("[BTN] Boot (cnt=%d)\r\n", cnt_s);
device_reset = false; device_reset = false;
@@ -1684,7 +1662,7 @@ static void main_s(void * p_context)
cnt_s++; cnt_s++;
device_reset = false; device_reset = false;
if (cnt_s == 400) /* 400 x 5ms = 2s */ if (cnt_s == 250) /* 250 x 5ms = 1.25s + α */
{ {
led_set_state(LED_STATE_POWER_ON); led_set_state(LED_STATE_POWER_ON);
DBG_PRINTF("[BTN] 2.0s\r\n"); DBG_PRINTF("[BTN] 2.0s\r\n");
@@ -31,8 +31,14 @@
* Firmware Version Update History * Firmware Version Update History
* - VBTFW0101 : Merged reb+red packets (single packet per channel), 260330 jhChun * - VBTFW0101 : Merged reb+red packets (single packet per channel), 260330 jhChun
* - VBTFW0102 : Added LED state command (msl) and re-pairing support, 260331 jhChun * - VBTFW0102 : Added LED state command (msl) and re-pairing support, 260331 jhChun
* - VBTFW0103 260416 jhChun
* : Stabilized ADC measurement by clearing buffers before sampling.
* : Improved low-battery detection and automatic power-off handling.
* : Updated BLE security, bonding, and advertising timeout behavior.
* : Cleaned up command parsing and removed unused project files.
* - VBTFW0111 260421 jhChun
------------------------------------------------------------------------- */ ------------------------------------------------------------------------- */
#define FIRMWARE_VERSION "VBTFW0102" #define FIRMWARE_VERSION "VBTFW0111"
/*============================================================================== /*==============================================================================
* Data Length Constants * Data Length Constants
@@ -71,11 +71,25 @@ extern void dr_piezo_power_off(void);
/*============================================================================== /*==============================================================================
* PRIVATE DEFINES * PRIVATE DEFINES
*============================================================================*/ *============================================================================*/
/* Extract pin number from NRF_GPIO_PIN_MAP (lower 5 bits) */ /* Extract pin number from NRF_GPIO_PIN_MAP (lower 5 bits) */
#define DR_PIN_NUM(pin) ((pin) & 0x1F) #define DR_PIN_NUM(pin) ((pin) & 0x1F)
#define DR_PIN_PORT(pin) (((pin) >> 5) & 0x01) #define DR_PIN_PORT(pin) (((pin) >> 5) & 0x01)
/* BLE packet constants */
#define BLE_MTU_SIZE 244 /* ATT MTU 247 - 3 (ATT header) = 244 */
#define BLE_REB_HEADER_LEN 6 /* "reb:" tag(4) + num_samples(2) */
#define BLE_RED_HEADER_LEN 6 /* "red:" tag(4) + pkt_idx(2) */
#define BLE_RDB_HEADER_LEN 8 /* "rdb:" tag(4) + num_samples(2) + compressed_size(2) */
#define BLE_REB_DATA_LEN (BLE_MTU_SIZE - BLE_REB_HEADER_LEN) /* 238 bytes = 119 samples */
#define BLE_RED_DATA_LEN (BLE_MTU_SIZE - BLE_RED_HEADER_LEN) /* 238 bytes = 119 samples */
#define BLE_PACKET_DELAY_MS 100 /* Inter-packet delay - allow BLE TX buffer to drain */
/* Piezo MUX pins (8ch) */
#define DR_PIEZO_EN_MUXA NRF_GPIO_PIN_MAP(0, 21) /**< MUXA Enable */
#define DR_PIEZO_EN_MUXB NRF_GPIO_PIN_MAP(0, 23) /**< MUXB Enable */
#define DR_PIEZO_MUX_SEL1 NRF_GPIO_PIN_MAP(0, 28) /**< MUX Select 1 */
#define DR_PIEZO_MUX_SEL0 NRF_GPIO_PIN_MAP(1, 10) /**< MUX Select 0 */
/*============================================================================== /*==============================================================================
* PRIVATE VARIABLES * PRIVATE VARIABLES
*============================================================================*/ *============================================================================*/
@@ -133,8 +147,6 @@ dr_adc_err_t dr_adc_init(void)
{ {
nrfx_err_t err; nrfx_err_t err;
ADC_LOG("ADC121S051 init (HW SPI, SPIM3)...");
nrfx_spim_config_t config = NRFX_SPIM_DEFAULT_CONFIG; nrfx_spim_config_t config = NRFX_SPIM_DEFAULT_CONFIG;
config.sck_pin = DR_ADC_PIN_SCLK; /* P0.14 */ config.sck_pin = DR_ADC_PIN_SCLK; /* P0.14 */
config.mosi_pin = NRFX_SPIM_PIN_NOT_USED;/* not used (read-only ADC) */ config.mosi_pin = NRFX_SPIM_PIN_NOT_USED;/* not used (read-only ADC) */
@@ -146,7 +158,7 @@ dr_adc_err_t dr_adc_init(void)
config.ss_active_high = false; /* CS active LOW */ config.ss_active_high = false; /* CS active LOW */
err = nrfx_spim_init(&m_spim, &config, NULL, NULL); err = nrfx_spim_init(&m_spim, &config, NULL, NULL);
ADC_LOG("SPIM3 init result: 0x%08X (%s)", err, (err == NRFX_SUCCESS) ? "OK" : "FAIL");
if (err != NRFX_SUCCESS) if (err != NRFX_SUCCESS)
{ {
return DR_ADC_ERR_NOT_INIT; return DR_ADC_ERR_NOT_INIT;
@@ -160,8 +172,6 @@ dr_adc_err_t dr_adc_init(void)
m_initialized = true; m_initialized = true;
ADC_LOG("ADC121S051 ready (VREF=%dmV, HW SPI)", m_vref_mv);
return DR_ADC_OK; return DR_ADC_OK;
} }
@@ -173,10 +183,7 @@ void dr_adc_uninit(void)
} }
nrfx_spim_uninit(&m_spim); nrfx_spim_uninit(&m_spim);
m_initialized = false; m_initialized = false;
ADC_LOG("ADC121S051 uninitialized");
} }
bool dr_adc_is_initialized(void) bool dr_adc_is_initialized(void)
@@ -350,8 +357,7 @@ dr_adc_err_t dr_adc_measure_echo(dr_adc_echo_t *echo, const dr_adc_echo_config_t
return dr_adc_analyze_echo(m_echo_buffer, cfg.num_samples, echo, cfg.threshold_raw); return dr_adc_analyze_echo(m_echo_buffer, cfg.num_samples, echo, cfg.threshold_raw);
} }
dr_adc_err_t dr_adc_burst_and_capture(uint8_t cycles, uint16_t delay_us, dr_adc_err_t dr_adc_burst_and_capture(uint8_t cycles, uint16_t delay_us, uint16_t num_samples, dr_adc_echo_t *echo)
uint16_t num_samples, dr_adc_echo_t *echo)
{ {
(void)cycles; /* Not used - ADC test only */ (void)cycles; /* Not used - ADC test only */
@@ -407,8 +413,7 @@ const uint16_t* dr_adc_get_echo_buffer(void)
return m_echo_buffer; return m_echo_buffer;
} }
dr_adc_err_t dr_adc_analyze_echo(const uint16_t *buffer, uint16_t num_samples, dr_adc_err_t dr_adc_analyze_echo(const uint16_t *buffer, uint16_t num_samples, dr_adc_echo_t *echo, uint16_t threshold)
dr_adc_echo_t *echo, uint16_t threshold)
{ {
if (buffer == NULL || echo == NULL) if (buffer == NULL || echo == NULL)
{ {
@@ -522,57 +527,6 @@ uint32_t dr_adc_get_vref(void)
return m_vref_mv; return m_vref_mv;
} }
/*==============================================================================
* PUBLIC FUNCTIONS - DEBUG
*============================================================================*/
bool dr_adc_test(void)
{
if (!m_initialized)
{
ADC_LOG("Test FAIL: not initialized");
return false;
}
uint16_t raw;
dr_adc_err_t err = dr_adc_read_raw(&raw);
if (err != DR_ADC_OK)
{
ADC_LOG("Test FAIL: read error %d", err);
return false;
}
if (raw > DR_ADC_MAX_VALUE)
{
ADC_LOG("Test FAIL: invalid value %d", raw);
return false;
}
ADC_LOG("Test PASS: raw=%d (%dmV)", raw, dr_adc_raw_to_mv(raw));
return true;
}
void dr_adc_print_buffer(const uint16_t *buffer, uint16_t num_samples)
{
#ifdef DEBUG_ADC
if (buffer == NULL || num_samples == 0)
{
return;
}
ADC_LOG("Echo buffer (%d samples):", num_samples);
for (uint16_t i = 0; i < num_samples; i++)
{
ADC_LOG("[%3d] %4d (%4dmV)", i, buffer[i], dr_adc_raw_to_mv(buffer[i]));
}
#else
(void)buffer;
(void)num_samples;
#endif
}
/*============================================================================== /*==============================================================================
* BLE TRANSMISSION * BLE TRANSMISSION
*============================================================================*/ *============================================================================*/
@@ -591,26 +545,11 @@ extern void dr_sd_delay_ms(uint32_t ms);
/* External piezo burst function */ /* External piezo burst function */
extern void dr_piezo_burst_sw(uint8_t cycles); extern void dr_piezo_burst_sw(uint8_t cycles);
/* BLE packet constants */
#define BLE_MTU_SIZE 244 /* ATT MTU 247 - 3 (ATT header) = 244 */
#define BLE_REB_HEADER_LEN 6 /* "reb:" tag(4) + num_samples(2) */
#define BLE_RED_HEADER_LEN 6 /* "red:" tag(4) + pkt_idx(2) */
#define BLE_REB_DATA_LEN (BLE_MTU_SIZE - BLE_REB_HEADER_LEN) /* 238 bytes = 119 samples */
#define BLE_RED_DATA_LEN (BLE_MTU_SIZE - BLE_RED_HEADER_LEN) /* 238 bytes = 119 samples */
#define BLE_PACKET_DELAY_MS 100 /* Inter-packet delay - allow BLE TX buffer to drain */
/*============================================================================== /*==============================================================================
* PIEZO CHANNEL SELECTION * PIEZO CHANNEL SELECTION
*============================================================================*/ *============================================================================*/
/* Piezo MUX pins (8ch) */
#define DR_PIEZO_EN_MUXA NRF_GPIO_PIN_MAP(0, 21) /**< MUXA Enable */
#define DR_PIEZO_EN_MUXB NRF_GPIO_PIN_MAP(0, 23) /**< MUXB Enable */
#define DR_PIEZO_MUX_SEL1 NRF_GPIO_PIN_MAP(0, 28) /**< MUX Select 1 */
#define DR_PIEZO_MUX_SEL0 NRF_GPIO_PIN_MAP(1, 10) /**< MUX Select 0 */
/* /*
* Channel mapping (8ch) jhChun 26.01.29 * Channel mapping (8ch)
* | EN MUXA | EN MUXB | SEL 0 | SEL 1 * | EN MUXA | EN MUXB | SEL 0 | SEL 1
* ---------------------------------------------- * ----------------------------------------------
@@ -1095,8 +1034,7 @@ dr_adc_err_t dr_adc_delta_compress(const uint16_t *samples, uint16_t num_samples
} }
dr_adc_err_t dr_adc_transmit_channel_delta(const dr_maa_channel_t *ch_data, dr_adc_err_t dr_adc_transmit_channel_delta(const dr_maa_channel_t *ch_data, uint8_t *ble_buffer)
uint8_t *ble_buffer)
{ {
if (ch_data == NULL || ble_buffer == NULL) if (ch_data == NULL || ble_buffer == NULL)
{ {
@@ -1104,7 +1042,7 @@ dr_adc_err_t dr_adc_transmit_channel_delta(const dr_maa_channel_t *ch_data,
} }
/* Compress data first */ /* Compress data first */
static uint8_t delta_buffer[400] = {0}; /* Worst case: 140*3 = 420 bytes */ static uint8_t delta_buffer[420] = {0}; /* Worst case when num_samples=140: 140 * 3 = 420 bytes */
uint16_t compressed_size = 0; uint16_t compressed_size = 0;
dr_adc_err_t err = dr_adc_delta_compress(ch_data->samples, ch_data->num_samples, dr_adc_err_t err = dr_adc_delta_compress(ch_data->samples, ch_data->num_samples,
@@ -1125,7 +1063,6 @@ dr_adc_err_t dr_adc_transmit_channel_delta(const dr_maa_channel_t *ch_data,
ble_buffer[6] = (uint8_t)(compressed_size >> 8); ble_buffer[6] = (uint8_t)(compressed_size >> 8);
ble_buffer[7] = (uint8_t)(compressed_size & 0xFF); ble_buffer[7] = (uint8_t)(compressed_size & 0xFF);
#define BLE_RDB_HEADER_LEN 8 /* rdb: tag(4) + num_samples(2) + compressed_size(2) */
uint16_t rdb_data_cap = BLE_MTU_SIZE - BLE_RDB_HEADER_LEN; /* 236 bytes */ uint16_t rdb_data_cap = BLE_MTU_SIZE - BLE_RDB_HEADER_LEN; /* 236 bytes */
uint16_t dst_idx = BLE_RDB_HEADER_LEN; uint16_t dst_idx = BLE_RDB_HEADER_LEN;
@@ -263,19 +263,6 @@ uint32_t dr_adc_get_vref(void);
* DEBUG FUNCTIONS * DEBUG FUNCTIONS
*============================================================================*/ *============================================================================*/
/**
* @brief Test ADC communication
* @return true if OK
*/
bool dr_adc_test(void);
/**
* @brief Print echo buffer to debug output
* @param buffer Sample buffer
* @param num_samples Number of samples
*/
void dr_adc_print_buffer(const uint16_t *buffer, uint16_t num_samples);
/*============================================================================== /*==============================================================================
* POWER CONTROL * POWER CONTROL
*============================================================================*/ *============================================================================*/
@@ -99,8 +99,8 @@ extern bool motion_data_once;
*============================================================================*/ *============================================================================*/
void safety_check_complete(float temp_c) void safety_check_complete(float temp_c)
{ {
DBG_PRINTF("[SAFETY] Batt=%d mV, Temp=%d.%d C\r\n", //DBG_PRINTF("[SAFETY] Batt=%d mV, Temp=%d.%d C\r\n",
(int)safety_batt_mv, (int)temp_c, ((int)(temp_c * 10)) % 10); // (int)safety_batt_mv, (int)temp_c, ((int)(temp_c * 10)) % 10);
/* Battery check */ /* Battery check */
if (safety_batt_mv <= LOW_BATTERY_VOLTAGE) if (safety_batt_mv <= LOW_BATTERY_VOLTAGE)
@@ -101,22 +101,22 @@
#define HALF_PERIOD_TICKS 4 /* half period: 250 ns / 62.5 ns = 4 ticks */ #define HALF_PERIOD_TICKS 4 /* half period: 250 ns / 62.5 ns = 4 ticks */
/*============================================================================== /*==============================================================================
* Piezo operating frequency * SW burst port register bitmasks
* *
* Target: 2.1 MHz (HW burst mode). * Pre-calculated from pin definitions in dr_piezo.h.
* SW burst timing is hard-coded per-frequency via NOP count. * Used for direct writes to NRF_P1->OUT, enabling simultaneous multi-pin control.
* * PE is on P0 port, controlled separately via OUTSET/OUTCLR.
* NOP timing (CPU 64 MHz, 1 NOP = 15.625 ns):
* 1.7 MHz: half 294 ns -> 1st half 18 NOP, 2nd half 10 NOP + loop overhead
* 1.8 MHz: half 278 ns -> 1st half 17 NOP, 2nd half 11 NOP + loop overhead
* 1.9 MHz: half 263 ns -> 1st half 15 NOP, 2nd half 9 NOP + loop overhead
* 2.0 MHz: half 250 ns -> 1st half 15 NOP, 2nd half 10 NOP + loop overhead
* 2.1 MHz: half 238 ns -> 1st half 14 NOP, 2nd half 9 NOP + loop overhead
* 2.2 MHz: half 227 ns -> 1st half 13 NOP, 2nd half 8 NOP + loop overhead
*
* To change frequency, modify NOP count in dr_piezo_burst_sw_XXmhz().
*============================================================================*/ *============================================================================*/
#define PIEZO_FREQ_MHZ 2.1f /* default operating frequency (MHz) */ /* Extract bit position within port from pin number (lower 5 bits = 0~31) */
#define PIN_NUM(pin) ((pin) & 0x1F)
#define P_OUT_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_P_OUT)) /* P1.07 positive output */
#define N_OUT_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_N_OUT)) /* P1.06 negative output */
#define PE_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_PE)) /* P0.25 pulse enable */
#define DMP_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_DMP)) /* P1.00 dump control */
/* Combined mask of piezo control pins on P1 port (excludes channel select pins) */
#define P1_CTRL_MASK (P_OUT_MASK | N_OUT_MASK | DMP_MASK)
/*============================================================================== /*==============================================================================
* Static variables * Static variables
@@ -194,7 +194,7 @@ void dr_piezo_power_on(void)
nrf_gpio_pin_set(DR_PIEZO_PWR_EN); nrf_gpio_pin_set(DR_PIEZO_PWR_EN);
/* Wait for power stabilization */ /* Wait for power stabilization */
nrf_delay_ms(3); nrf_delay_ms(10);
m_power_enabled = true; m_power_enabled = true;
@@ -544,6 +544,7 @@ void dr_piezo_mux_init(void)
NRF_GPIO_PIN_H0H1, /* High drive */ NRF_GPIO_PIN_H0H1, /* High drive */
NRF_GPIO_PIN_NOSENSE NRF_GPIO_PIN_NOSENSE
); );
nrf_gpio_cfg( nrf_gpio_cfg(
DR_PIEZO_MUX_SEL1, DR_PIEZO_MUX_SEL1,
NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_DIR_OUTPUT,
@@ -553,7 +554,7 @@ void dr_piezo_mux_init(void)
NRF_GPIO_PIN_NOSENSE NRF_GPIO_PIN_NOSENSE
); );
/* GPIO PIN Setting jhChun 0129 */ /* GPIO PIN Setting */
nrf_gpio_cfg( nrf_gpio_cfg(
DR_PIEZO_EN_MUXA, // PIN DR_PIEZO_EN_MUXA, // PIN
NRF_GPIO_PIN_DIR_OUTPUT, // DIR : OUTPUT NRF_GPIO_PIN_DIR_OUTPUT, // DIR : OUTPUT
@@ -592,37 +593,37 @@ void dr_piezo_select_channel(uint8_t channel)
{ {
channel = channel & 0x07; /* Mask to 0~7 range */ channel = channel & 0x07; /* Mask to 0~7 range */
switch (channel) { switch (channel)
// EN_A EN_B SEL0 SEL1 {
case 0: // A0: 1 0 0 0 case 0: // A0: EN_MUXA=1, EN_MUXB=0, SEL0=0, SEL1=0
nrf_gpio_pin_clear(DR_PIEZO_EN_MUXB); nrf_gpio_pin_set(DR_PIEZO_EN_MUXA); nrf_gpio_pin_clear(DR_PIEZO_EN_MUXB); nrf_gpio_pin_set(DR_PIEZO_EN_MUXA);
nrf_gpio_pin_clear(DR_PIEZO_MUX_SEL0); nrf_gpio_pin_clear(DR_PIEZO_MUX_SEL1); nrf_gpio_pin_clear(DR_PIEZO_MUX_SEL0); nrf_gpio_pin_clear(DR_PIEZO_MUX_SEL1);
break; break;
case 1: // A2: 1 0 1 0 case 1: // A1: EN_MUXA=1, EN_MUXB=0, SEL0=1, SEL1=0
nrf_gpio_pin_clear(DR_PIEZO_EN_MUXB); nrf_gpio_pin_set(DR_PIEZO_EN_MUXA); nrf_gpio_pin_clear(DR_PIEZO_EN_MUXB); nrf_gpio_pin_set(DR_PIEZO_EN_MUXA);
nrf_gpio_pin_set(DR_PIEZO_MUX_SEL0); nrf_gpio_pin_clear(DR_PIEZO_MUX_SEL1); nrf_gpio_pin_set(DR_PIEZO_MUX_SEL0); nrf_gpio_pin_clear(DR_PIEZO_MUX_SEL1);
break; break;
case 2: // A1: 1 0 0 1 case 2: // A2: EN_MUXA=1, EN_MUXB=0, SEL0=0, SEL1=1
nrf_gpio_pin_clear(DR_PIEZO_EN_MUXB); nrf_gpio_pin_set(DR_PIEZO_EN_MUXA); nrf_gpio_pin_clear(DR_PIEZO_EN_MUXB); nrf_gpio_pin_set(DR_PIEZO_EN_MUXA);
nrf_gpio_pin_clear(DR_PIEZO_MUX_SEL0); nrf_gpio_pin_set(DR_PIEZO_MUX_SEL1); nrf_gpio_pin_clear(DR_PIEZO_MUX_SEL0); nrf_gpio_pin_set(DR_PIEZO_MUX_SEL1);
break; break;
case 3: // A3: 1 0 1 1 case 3: // A3: EN_MUXA=1, EN_MUXB=0, SEL0=1, SEL1=1
nrf_gpio_pin_clear(DR_PIEZO_EN_MUXB); nrf_gpio_pin_set(DR_PIEZO_EN_MUXA); nrf_gpio_pin_clear(DR_PIEZO_EN_MUXB); nrf_gpio_pin_set(DR_PIEZO_EN_MUXA);
nrf_gpio_pin_set(DR_PIEZO_MUX_SEL0); nrf_gpio_pin_set(DR_PIEZO_MUX_SEL1); nrf_gpio_pin_set(DR_PIEZO_MUX_SEL0); nrf_gpio_pin_set(DR_PIEZO_MUX_SEL1);
break; break;
case 4: // B0: 0 1 1 1 /*case 4: // B0: EN_MUXA=0, EN_MUXB=1, SEL0=1, SEL1=1
nrf_gpio_pin_clear(DR_PIEZO_EN_MUXA); nrf_gpio_pin_set(DR_PIEZO_EN_MUXB); nrf_gpio_pin_clear(DR_PIEZO_EN_MUXA); nrf_gpio_pin_set(DR_PIEZO_EN_MUXB);
nrf_gpio_pin_set(DR_PIEZO_MUX_SEL0); nrf_gpio_pin_set(DR_PIEZO_MUX_SEL1); nrf_gpio_pin_set(DR_PIEZO_MUX_SEL0); nrf_gpio_pin_set(DR_PIEZO_MUX_SEL1);
break; break;
case 5: // B1: 0 1 1 0 case 5: // B1: EN_MUXA=0, EN_MUXB=1, SEL0=0, SEL1=1
nrf_gpio_pin_clear(DR_PIEZO_EN_MUXA); nrf_gpio_pin_set(DR_PIEZO_EN_MUXB); nrf_gpio_pin_clear(DR_PIEZO_EN_MUXA); nrf_gpio_pin_set(DR_PIEZO_EN_MUXB);
nrf_gpio_pin_clear(DR_PIEZO_MUX_SEL0); nrf_gpio_pin_set(DR_PIEZO_MUX_SEL1); nrf_gpio_pin_clear(DR_PIEZO_MUX_SEL0); nrf_gpio_pin_set(DR_PIEZO_MUX_SEL1);
break; break;*/
case 6: // B2: 0 1 0 1 case 5: // B2: EN_MUXA=0, EN_MUXB=1, SEL0=1, SEL1=0
nrf_gpio_pin_clear(DR_PIEZO_EN_MUXA); nrf_gpio_pin_set(DR_PIEZO_EN_MUXB); nrf_gpio_pin_clear(DR_PIEZO_EN_MUXA); nrf_gpio_pin_set(DR_PIEZO_EN_MUXB);
nrf_gpio_pin_set(DR_PIEZO_MUX_SEL0); nrf_gpio_pin_clear(DR_PIEZO_MUX_SEL1); nrf_gpio_pin_set(DR_PIEZO_MUX_SEL0); nrf_gpio_pin_clear(DR_PIEZO_MUX_SEL1);
break; break;
case 7: // B3: 0 1 0 0 case 4: // B3: EN_MUXA=0, EN_MUXB=1, SEL0=0, SEL1=0
nrf_gpio_pin_clear(DR_PIEZO_EN_MUXA); nrf_gpio_pin_set(DR_PIEZO_EN_MUXB); nrf_gpio_pin_clear(DR_PIEZO_EN_MUXA); nrf_gpio_pin_set(DR_PIEZO_EN_MUXB);
nrf_gpio_pin_clear(DR_PIEZO_MUX_SEL0); nrf_gpio_pin_clear(DR_PIEZO_MUX_SEL1); nrf_gpio_pin_clear(DR_PIEZO_MUX_SEL0); nrf_gpio_pin_clear(DR_PIEZO_MUX_SEL1);
break; break;
@@ -741,29 +742,6 @@ void dr_piezo_transmit(uint8_t cycles)
* channel select pins (MUX SEL) via P1_CTRL_MASK (only control pins change). * channel select pins (MUX SEL) via P1_CTRL_MASK (only control pins change).
* PE is on P0 port, so it is controlled separately via OUTSET/OUTCLR registers. * PE is on P0 port, so it is controlled separately via OUTSET/OUTCLR registers.
*============================================================================*/ *============================================================================*/
/* Extract bit position within port from pin number (lower 5 bits = 0~31) */
#define PIN_NUM(pin) ((pin) & 0x1F)
/* P1 port pin bitmasks - auto-generated from pin definitions in dr_piezo.h
*
* WARNING: Never hardcode pin numbers!
* Hardcoding may save a developer's time temporarily,
* but it will also shorten that developer's lifespan.
* - Charles KWON
*
* Each mask represents the bit position within the port register.
* Used for direct writes to NRF_P1->OUT, enabling simultaneous multi-pin control.
*/
#define P_OUT_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_P_OUT)) /* P1.07 positive output */
#define N_OUT_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_N_OUT)) /* P1.06 negative output */
#define PE_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_PE)) /* P0.25 pulse enable */
#define DMP_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_DMP)) /* P1.00 dump control */
/* Combined mask of piezo control pins on P1 port (excludes channel select pins) */
#define P1_CTRL_MASK (P_OUT_MASK | N_OUT_MASK | DMP_MASK)
/* /*
* Software burst - default frequency 2.1MHz * Software burst - default frequency 2.1MHz
* *
@@ -74,7 +74,8 @@ int device_sleep_mode(void)
* *
* @return 0 (always succeeds) * @return 0 (always succeeds)
*/ */
int device_activated(void){ int device_activated(void)
{
int rc = 0; int rc = 0;
p_order = 0; /* State machine start step (Step 0: I2C init) */ p_order = 0; /* State machine start step (Step 0: I2C init) */
lock_check =true; /* Lock: power sequence in progress */ lock_check =true; /* Lock: power sequence in progress */
@@ -117,10 +118,13 @@ void power_loop(void *p_context)
/* Advance to next step or finish */ /* Advance to next step or finish */
/* Advance to next step or finish sequence */ /* Advance to next step or finish sequence */
if (p_order < 2) { if (p_order < 2)
{
p_order++; /* Move to next step */ p_order++; /* Move to next step */
power_timer_start(); /* Execute next step after 20ms */ power_timer_start(); /* Execute next step after 20ms */
} else { }
else
{
/* Power sequence fully complete */ /* Power sequence fully complete */
DBG_PRINTF("[PWR] Device Activated OK!\r\n"); DBG_PRINTF("[PWR] Device Activated OK!\r\n");
} }
@@ -135,7 +139,8 @@ void power_loop(void *p_context)
* *
* @return 0 (always succeeds) * @return 0 (always succeeds)
*/ */
int device_reactivated(void){ int device_reactivated(void)
{
int rc = 0; int rc = 0;
sw_i2c_init_once(); /* Re-initialize I2C bus */ sw_i2c_init_once(); /* Re-initialize I2C bus */
nrf_delay_ms(10); /* Stabilization delay */ nrf_delay_ms(10); /* Stabilization delay */
@@ -20,6 +20,22 @@ static struct {
bool bonds_delete_pending; bool bonds_delete_pending;
} m_state = {0}; } m_state = {0};
static const char * sec_error_name(pm_sec_error_code_t error)
{
switch (error) {
case PM_CONN_SEC_ERROR_PIN_OR_KEY_MISSING:
return "PIN_OR_KEY_MISSING";
case PM_CONN_SEC_ERROR_MIC_FAILURE:
return "MIC_FAILURE";
case PM_CONN_SEC_ERROR_DISCONNECT:
return "DISCONNECT";
case PM_CONN_SEC_ERROR_SMP_TIMEOUT:
return "SMP_TIMEOUT";
default:
return "UNKNOWN";
}
}
/** /**
* @brief Initialize BLE security * @brief Initialize BLE security
*/ */
@@ -102,8 +118,9 @@ void ble_security_quick_pm_handler(pm_evt_t const *p_evt)
// DEV mode: do not forward security failure events to SDK handler (prevent disconnect) // DEV mode: do not forward security failure events to SDK handler (prevent disconnect)
if (m_state.dev_mode && p_evt->evt_id == PM_EVT_CONN_SEC_FAILED) { if (m_state.dev_mode && p_evt->evt_id == PM_EVT_CONN_SEC_FAILED) {
DBG_PRINTF("Security failed: error=%d\r\n", DBG_PRINTF("Security failed: error=%d (%s)\r\n",
p_evt->params.conn_sec_failed.error); p_evt->params.conn_sec_failed.error,
sec_error_name(p_evt->params.conn_sec_failed.error));
DBG_PRINTF("DEV: Ignoring sec failure, keeping connection\r\n"); DBG_PRINTF("DEV: Ignoring sec failure, keeping connection\r\n");
return; return;
} }
@@ -129,8 +146,9 @@ void ble_security_quick_pm_handler(pm_evt_t const *p_evt)
break; break;
case PM_EVT_CONN_SEC_FAILED: case PM_EVT_CONN_SEC_FAILED:
DBG_PRINTF("Security failed: error=%d\r\n", DBG_PRINTF("Security failed: error=%d (%s)\r\n",
p_evt->params.conn_sec_failed.error); p_evt->params.conn_sec_failed.error,
sec_error_name(p_evt->params.conn_sec_failed.error));
if (m_state.dev_mode) { if (m_state.dev_mode) {
// DEV mode: ignore security failure, keep connection // DEV mode: ignore security failure, keep connection
@@ -138,6 +156,20 @@ void ble_security_quick_pm_handler(pm_evt_t const *p_evt)
break; break;
} }
if (p_evt->params.conn_sec_failed.error == PM_CONN_SEC_ERROR_DISCONNECT) {
// The peer/link already disconnected before security finished.
// There is no live connection to repair; BLE_GAP_EVT_DISCONNECTED
// will restart advertising.
DBG_PRINTF("Security ended by disconnect; waiting for reconnect\r\n");
break;
}
if (p_evt->params.conn_sec_failed.error == PM_CONN_SEC_ERROR_SMP_TIMEOUT) {
// The SDK cannot start another SMP procedure on this link.
pm_handler_disconnect_on_sec_failure(p_evt);
break;
}
if (p_evt->params.conn_sec_failed.error == PM_CONN_SEC_ERROR_PIN_OR_KEY_MISSING) { if (p_evt->params.conn_sec_failed.error == PM_CONN_SEC_ERROR_PIN_OR_KEY_MISSING) {
// Key missing: attempt re-pairing, fall back to disconnect on failure // Key missing: attempt re-pairing, fall back to disconnect on failure
err_code = pm_conn_secure(p_evt->conn_handle, true); err_code = pm_conn_secure(p_evt->conn_handle, true);