Compare commits

...

16 Commits

Author SHA1 Message Date
jhChun 3ec6afec36 MUX 전환 후 ADC S/H 커패시터 안정화를 위한 dummy read 2026-04-07 14:17:06 +09:00
jhChun 6cb927e831 DEBUG 빌드 해제
- 에러 처리 시 먹통 현상 = DEBUG 빌드
2026-04-03 16:43:29 +09:00
jhChun 47a31c1eb4 SAADC 타이밍 충돌로 인한 FW RESET 방지: 배터리 저전압 측정 주기 및 init 실패시 처리 변경
- 배터리 저전압 측정 주기 5초 -> 60초로 변경(충돌 확률 감소)
- SAADC init 실패 시 APP_ERROR_CHECK -> 측정 스킵 처리

***** 현재 에러 처리는 APP_ERROR_CHECK로 되어있는데, 에러 발생 시 APP_ERROR_CHECK에 걸리면 HardFault 무한 루프에 빠져서 기기가 먹통이 됨. 런타임 중 에러가 발생해도 안전하게 복구되도록 에러 처리 전환 필요함. *****
2026-04-02 11:27:28 +09:00
jhChun 5ee39f834e Slave Latency 4 -> 0 변경 2026-04-02 11:17:06 +09:00
jhChun 5128c44912 DR_ADC_ECHO_SAMPLES_MAX 100 -> 119 2026-04-01 16:22:42 +09:00
jhChun 8fdc153ed5 블루투스 스캐닝 LED 1초 간격 점멸 -> 0.5초 간격 점멸 2026-03-31 17:58:49 +09:00
jhChun d8ef9bcb41 dev 모드 BLE 연결 수정
- dev 모드인 경우 보안 x
2026-03-31 17:58:33 +09:00
jhChun 007e0b5683 Ver VBTFW0102 2026-03-31 14:27:14 +09:00
jhChun d63a9cfa3c BLE 보안 실패 시 자동 재페어링 및 버그 3건 수정
- 앱과 기기 사이 키가 불일치하는 경우 새로운 키 생성 요청(재페어링 허용)
- main.c: PM_EVT_CONN_SEC_CONFIG_REQ 중복 핸들러 제거
- main.c: DEV 모드에서 pm_peers_delete() 이중 호출 방지
- main.c: 활성 연결 중 advertising 재시작 방지(m_conn_handle 체크)
- ble_quick_security: 보안 실패 시 bond 삭제 + 자동 재페어링 시도
- ble_quick_security: allow_repairing = true (항상 재페어링 허용)
2026-03-31 14:17:35 +09:00
jhChun 217d741d31 LED 상태 설정 명령 추가
- BLE 명령으로 LED 상태 설정
2026-03-31 14:01:02 +09:00
jhChun f464838ed4 Connection Interval 30ms로 변경
- 기존: MIN=20, MAX=75
- 변경: MIN=30, MAX=30
- GAP Event Length=24(30ms)에 맞춰 변경함
- Piezo 6채널 ADC 데이터 7패킷 송신 시 3~4패킷/CI로, ~60ms 소요
2026-03-31 09:55:47 +09:00
jhChun a150d1da96 LED 직접 제어 모듈 추가(BSP 대체)
- 전원 ON/OFF, 블루투스 스캐닝 초록 LED 적용 완료
2026-03-30 18:00:38 +09:00
jhChun 11a0b3f11a FW 버전 업데이트 2026-03-30 17:10:17 +09:00
jhChun 5e27eb762d BLE Piezo 6채널 데이터 패킷 병합 (reb+red -> reb 단일 패킷)
- BLE_MTU_SIZE 240 -> 244 (ATT MTU 247 - 3 = 244, 기존 4B 낭비 해소)
- reb: 헤더 축소(14B → 6B) 후 데이터 병합 -> 119샘플까지 단일 패킷(현재 100샘플)
- reb: 헤더에서 peak_raw/peak_index/baseline_raw/버전마커 제거
  → PC에서 raw 데이터로 직접 계산, 단일 패킷이므로 버전마커 불필요
- 채널 간 딜레이 50ms -> 5ms (dr_binary_tx_safe 내부 재시도로 TX 보장)
- delta 전송(rdb/rdd)도 동일 방식 적용, 종료 패킷(ree:/rde:) 제거
- 채널 완료 판단: 종료 패킷(ree:) 제거, reb: 수신 시 채널 완료 (100샘플 기준 단일 패킷)
- 전체 완료는 기존과 동일하게 raa:로 판단
2026-03-30 16:37:41 +09:00
jhChun 689ad29aa6 BLE 명령 수신 시 Connection interval 재협상 요청 추가
- Windows BLE 스택이 장시간 연결 시 CI를 수초까지 늘려 응답이 지연되는 문제 대응
- 30초에 1회 20~75ms로 돌려달라고 요청
2026-03-30 15:27:22 +09:00
jhChun aaf22d942a BLE 연결 실패 발생 시 추적을 위한 RTT 로그 추가
- 0: IMU 값 송신(msp)- 1: 배터리 SAADC 시작- 2: 배터리 콜백 완료(battery_event_handler)- 3: 배터리 대기 루프 탈출- 4: IMU 읽기 완료- 5: Piezo RX/TX Active- 6: 온도 SAADC 시작- 7: 온도 콜백 완료(tmp235_voltage_handler)- 8: 온도 대기 루프 탈출- 9: rbb: 패킷 전송- 10: Piezo 캡처 시작(maa_async_start)- 11: 전체 종료(mbb)
2026-03-30 15:22:59 +09:00
13 changed files with 805 additions and 460 deletions
+25 -3
View File
@@ -95,6 +95,14 @@ void ble_security_quick_pm_handler(pm_evt_t const *p_evt)
{
ret_code_t err_code;
// DEV 모드: 보안 실패 이벤트는 SDK 핸들러에 전달하지 않음 (disconnect 방지)
if (m_state.dev_mode && p_evt->evt_id == PM_EVT_CONN_SEC_FAILED) {
DBG_PRINTF("Security failed: error=%d\r\n",
p_evt->params.conn_sec_failed.error);
DBG_PRINTF("DEV: Ignoring sec failure, keeping connection\r\n");
return;
}
// Call standard handlers (required)
pm_handler_on_pm_evt(p_evt);
pm_handler_flash_clean(p_evt);
@@ -113,6 +121,12 @@ void ble_security_quick_pm_handler(pm_evt_t const *p_evt)
DBG_PRINTF("Security failed: error=%d\r\n",
p_evt->params.conn_sec_failed.error);
if (m_state.dev_mode) {
// DEV 모드: 보안 실패 무시 — 연결 유지
DBG_PRINTF("DEV: Ignoring sec failure, keeping connection\r\n");
break;
}
if (p_evt->params.conn_sec_failed.error == PM_CONN_SEC_ERROR_PIN_OR_KEY_MISSING) {
// Key missing: 재페어링 시도, 실패 시 disconnect로 폴백
err_code = pm_conn_secure(p_evt->conn_handle, true);
@@ -126,15 +140,23 @@ void ble_security_quick_pm_handler(pm_evt_t const *p_evt)
pm_handler_disconnect_on_sec_failure(p_evt);
}
} else {
// 기타 보안 실패 → disconnect
pm_handler_disconnect_on_sec_failure(p_evt);
// 기타 보안 실패 → bond 삭제 후 재페어링 시도
pm_peer_id_t peer_id;
if (pm_peer_id_get(p_evt->conn_handle, &peer_id) == NRF_SUCCESS
&& peer_id != PM_PEER_ID_INVALID) {
pm_peer_delete(peer_id);
}
err_code = pm_conn_secure(p_evt->conn_handle, true);
if (err_code != NRF_SUCCESS) {
pm_handler_disconnect_on_sec_failure(p_evt);
}
}
break;
case PM_EVT_CONN_SEC_CONFIG_REQ:
{
pm_conn_sec_config_t config = {
.allow_repairing = m_state.dev_mode
.allow_repairing = true
};
pm_conn_sec_config_reply(p_evt->conn_handle, &config);
}
+130 -248
View File
@@ -517,17 +517,12 @@ extern void dr_sd_delay_ms(uint32_t ms);
extern void dr_piezo_burst_sw(uint8_t cycles);
/* BLE packet constants */
#define BLE_MTU_SIZE 240
#define BLE_FIRST_HEADER_LEN 14 /* "rec:" + header */
#define BLE_CONT_HEADER_LEN 6 /* "red:" + packet_index */
#define BLE_FIRST_DATA_LEN (BLE_MTU_SIZE - BLE_FIRST_HEADER_LEN)
#define BLE_CONT_DATA_LEN (BLE_MTU_SIZE - BLE_CONT_HEADER_LEN)
#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 */
#define BLE_FIRST_PACKET_DELAY 5 /* Minimal delay */
/* Protocol version markers (OR'd with packet count in reb: header) */
#define MEC_VERSION_MARKER 0xD000 /* vD: raa only (no ree) - ree causes packet loss */
#define MAA_VERSION_MARKER 0xF000 /* vF: maa? hardcoded 8-channel debug */
/*==============================================================================
* PIEZO CHANNEL SELECTION
@@ -565,15 +560,14 @@ extern void dr_piezo_select_channel(uint8_t channel);
/**
* @brief Integrated burst + capture + BLE transmit (16-bit raw data)
*
* PROTOCOL v3: Small header packet + separate data packets with delays
* (Large first packets were being dropped by BLE stack)
* reb+red merged protocol: header와 데이터를 첫 패킷에 합침
*
* Response format:
* Packet 1 (reb:): header only (14 bytes)
* Packet 2~N (red:): pkt_idx(2) + raw_data(up to 234 bytes = 117 samples)
* Final (ree:): total_packets(2)
* Packet 1 (reb:): num_samples(2) + raw_data(up to 238 bytes = 119 samples)
* Packet 2~N (red:): pkt_idx(2) + raw_data(up to 238 bytes) — 119샘플 초과 시만
* Final (raa:): status(2) — skip_raa=0 일 때만
*
* With 140 samples: 280 bytes = reb:(14) + red:(6+234) + red:(6+46) + ree:(6)
* With 100 samples: 200 bytes = reb:(6+200) = 206 bytes (단일 패킷)
*/
dr_adc_err_t dr_adc_burst_capture_transmit(uint8_t freq_option, uint16_t delay_us,
uint16_t num_samples, uint8_t cycles,
@@ -589,13 +583,7 @@ dr_adc_err_t dr_adc_burst_capture_transmit(uint8_t freq_option, uint16_t delay_u
if (averaging > 1000) averaging = 1000; /* Maximum 1000 */
if (piezo_ch >= MAA_NUM_CHANNELS) piezo_ch = 0; /* 채널 범위 검증 */
dr_adc_echo_t echo;
echo.peak_raw = 0;
echo.peak_mv = 0;
echo.peak_index = 0;
echo.peak_time_us = 0;
echo.baseline_raw = 0;
echo.num_samples = 0;
/* echo 분석은 PC에서 raw 데이터로 직접 수행 */
/* Accumulator buffer for averaging (32-bit to prevent overflow) */
static uint32_t accum_buffer[DR_ADC_ECHO_SAMPLES_MAX];
@@ -717,82 +705,58 @@ dr_adc_err_t dr_adc_burst_capture_transmit(uint8_t freq_option, uint16_t delay_u
ADC_LOG("mec: averaged %u measurements", averaging);
}
/*--- Step 6: Analyze averaged data ---*/
dr_adc_analyze_echo(m_echo_buffer, num_samples, &echo, 100);
/*--- Step 6: Transmit via BLE - reb: + red: merged protocol ---*/
/* Packet 1 (reb:): tag(4) + num_samples(2) + data(up to 238 bytes = 119 samples) */
/* Packet 2~N (red:): tag(4) + pkt_idx(2) + data(up to 238 bytes) — only if > 119 samples */
/*--- Step 6: Transmit via BLE - PROTOCOL v3 ---*/
/* Packet 1: reb:(4) + header(10) = 14 bytes ONLY (no data) */
/* Packet 2~N: red:(4) + pkt_idx(2) + data(up to 234 bytes) */
/* Final: ree:(4) + total_pkts(2) = 6 bytes */
uint16_t total_data_bytes = echo.num_samples * 2;
uint16_t cont_pkt_data_cap = BLE_MTU_SIZE - BLE_CONT_HEADER_LEN; /* 234 bytes */
/* Calculate data packets needed (header packet doesn't carry data) */
uint16_t data_packets = (total_data_bytes + cont_pkt_data_cap - 1) / cont_pkt_data_cap;
/* Version marker: select based on skip_raa (0=mec, 1=maa) */
uint16_t version_marker = skip_raa ? MAA_VERSION_MARKER : MEC_VERSION_MARKER;
uint16_t pkts_with_ver = data_packets | version_marker;
ADC_LOG("mec v6: samples=%u data=%u bytes, packets=%u", echo.num_samples, total_data_bytes, data_packets);
/* Packet 1: reb: header ONLY (14 bytes) - small packet won't be dropped */
ble_buffer[0] = 'r';
ble_buffer[1] = 'e';
ble_buffer[2] = 'b';
ble_buffer[3] = ':';
ble_buffer[4] = (uint8_t)(pkts_with_ver & 0xFF);
ble_buffer[5] = (uint8_t)(pkts_with_ver >> 8);
ble_buffer[6] = (uint8_t)(echo.peak_raw & 0xFF);
ble_buffer[7] = (uint8_t)(echo.peak_raw >> 8);
ble_buffer[8] = (uint8_t)(echo.peak_index & 0xFF);
ble_buffer[9] = (uint8_t)(echo.peak_index >> 8);
ble_buffer[10] = (uint8_t)(echo.baseline_raw & 0xFF);
ble_buffer[11] = (uint8_t)(echo.baseline_raw >> 8);
ble_buffer[12] = (uint8_t)(echo.num_samples & 0xFF);
ble_buffer[13] = (uint8_t)(echo.num_samples >> 8);
dr_binary_tx_safe(ble_buffer, 7); /* Send header only: 14 bytes = 7 words */
dr_sd_delay_ms(BLE_PACKET_DELAY_MS); /* Wait for BLE stack */
/* Data packets (red:) - starting from pkt index 0 */
uint16_t total_data_bytes = num_samples * 2;
uint16_t src_idx = 0;
for (uint16_t pkt = 0; pkt < data_packets; pkt++) {
ble_buffer[0] = 'r';
ble_buffer[1] = 'e';
ble_buffer[2] = 'd';
ble_buffer[3] = ':';
/* Packet 1: reb: header + data merged */
ble_buffer[0] = 'r'; ble_buffer[1] = 'e'; ble_buffer[2] = 'b'; ble_buffer[3] = ':';
ble_buffer[4] = (uint8_t)(num_samples & 0xFF);
ble_buffer[5] = (uint8_t)(num_samples >> 8);
uint16_t dst_idx = BLE_REB_HEADER_LEN;
uint16_t first_chunk = (total_data_bytes > BLE_REB_DATA_LEN) ? BLE_REB_DATA_LEN : total_data_bytes;
while (src_idx < num_samples && (dst_idx - BLE_REB_HEADER_LEN) < first_chunk) {
uint16_t sample = m_echo_buffer[src_idx++] & 0x0FFF;
ble_buffer[dst_idx++] = (uint8_t)(sample & 0xFF);
ble_buffer[dst_idx++] = (uint8_t)(sample >> 8);
}
dr_binary_tx_safe(ble_buffer, dst_idx / 2);
/* Continuation packets (red:) — only if data didn't fit in reb: */
uint16_t pkt = 0;
while (src_idx < num_samples) {
dr_sd_delay_ms(BLE_PACKET_DELAY_MS);
ble_buffer[0] = 'r'; ble_buffer[1] = 'e'; ble_buffer[2] = 'd'; ble_buffer[3] = ':';
ble_buffer[4] = (uint8_t)(pkt & 0xFF);
ble_buffer[5] = (uint8_t)(pkt >> 8);
uint16_t dst_idx = 6;
uint16_t bytes_this_pkt = 0;
while (src_idx < echo.num_samples && bytes_this_pkt < cont_pkt_data_cap) {
dst_idx = BLE_RED_HEADER_LEN;
while (src_idx < num_samples && (dst_idx - BLE_RED_HEADER_LEN) < BLE_RED_DATA_LEN) {
uint16_t sample = m_echo_buffer[src_idx++] & 0x0FFF;
ble_buffer[dst_idx++] = (uint8_t)(sample & 0xFF);
ble_buffer[dst_idx++] = (uint8_t)(sample >> 8);
bytes_this_pkt += 2;
}
dr_binary_tx_safe(ble_buffer, dst_idx / 2); /* bytes to words */
dr_sd_delay_ms(BLE_PACKET_DELAY_MS); /* Inter-packet delay */
dr_binary_tx_safe(ble_buffer, dst_idx / 2);
pkt++;
}
/* vD: Send raa: only - ree: causes subsequent packet loss */
/* DrBench now saves channel data when receiving raa: */
/* skip_raa: 0=send raa (mec), 1=skip raa (maa - caller sends final raa) */
/* raa: completion — skip_raa: 0=send (mec), 1=skip (maa - caller sends final raa) */
if (!skip_raa) {
ble_buffer[0] = 'r';
ble_buffer[1] = 'a';
ble_buffer[2] = 'a';
ble_buffer[3] = ':';
dr_sd_delay_ms(BLE_PACKET_DELAY_MS);
ble_buffer[0] = 'r'; ble_buffer[1] = 'a'; ble_buffer[2] = 'a'; ble_buffer[3] = ':';
ble_buffer[4] = 0x00;
ble_buffer[5] = 0x00;
dr_binary_tx_safe(ble_buffer, 3); /* 6 bytes = 3 words */
dr_binary_tx_safe(ble_buffer, 3);
}
ADC_LOG("mec v6: complete - reb + red*%u + skip_raa=%u (%u samples)", data_packets, skip_raa, echo.num_samples);
ADC_LOG("mec: reb+data merged, skip_raa=%u (%u samples)", skip_raa, num_samples);
return DR_ADC_OK;
}
@@ -831,6 +795,9 @@ dr_adc_err_t dr_adc_capture_channel_only(uint8_t freq_option, uint16_t delay_us,
/* 채널 선택(MUX) : 1.3ms */
dr_piezo_select_channel(piezo_ch);
/* MUX 전환 후 S/H 커패시터 안정화를 위한 dummy read */
(void)spim_read_raw();
/* 채널당 반복 측정 횟수만큼 */
for (uint16_t avg_iter = 0; avg_iter < averaging; avg_iter++)
{
@@ -876,16 +843,7 @@ dr_adc_err_t dr_adc_capture_channel_only(uint8_t freq_option, uint16_t delay_us,
}
out_channel->num_samples = num_samples;
/* Analyze echo data */
dr_adc_echo_t echo;
dr_adc_analyze_echo(out_channel->samples, num_samples, &echo, 100);
out_channel->peak_raw = echo.peak_raw;
out_channel->peak_index = echo.peak_index;
out_channel->baseline_raw = echo.baseline_raw;
/* peak, index, baseline 확인용 로그 */
//if (g_plat.log) g_plat.log("CH%u: peak=%u index=%u baseline=%u\r\n", piezo_ch, out_channel->peak_raw, out_channel->peak_index, out_channel->baseline_raw);
/* peak/baseline 분석은 PC에서 raw 데이터로 직접 수행 */
return DR_ADC_OK;
}
@@ -894,87 +852,48 @@ dr_adc_err_t dr_adc_capture_channel_only(uint8_t freq_option, uint16_t delay_us,
dr_adc_err_t dr_adc_transmit_channel(const dr_maa_channel_t *ch_data,
uint8_t *ble_buffer)
{
/* Debug BEFORE null check to see if we even enter the function */
dr_ble_debug(0x00FF, 0xAAAA); /* Function called marker */
if (ch_data == NULL || ble_buffer == NULL) {
dr_ble_debug(0x00FE, (ch_data == NULL ? 0x0001 : 0) | (ble_buffer == NULL ? 0x0002 : 0));
return DR_ADC_ERR_INVALID_PARAM;
}
dr_ble_debug(0x0100, ch_data->num_samples); /* Function entry - params valid */
uint16_t total_data_bytes = ch_data->num_samples * 2;
uint16_t cont_pkt_data_cap = BLE_MTU_SIZE - BLE_CONT_HEADER_LEN; /* 234 bytes */
uint16_t data_packets = (total_data_bytes + cont_pkt_data_cap - 1) / cont_pkt_data_cap;
uint16_t total_packets = 1 + data_packets;
/* Version marker: MAA_VERSION_MARKER | total_packets */
uint16_t pkt_with_ver = total_packets | MAA_VERSION_MARKER;
dr_ble_debug(0x0101, total_packets); /* Before reb: */
/* Packet 1: reb: header (14 bytes) */
ble_buffer[0] = 'r';
ble_buffer[1] = 'e';
ble_buffer[2] = 'b';
ble_buffer[3] = ':';
ble_buffer[4] = (uint8_t)(pkt_with_ver & 0xFF);
ble_buffer[5] = (uint8_t)(pkt_with_ver >> 8);
ble_buffer[6] = (uint8_t)(ch_data->peak_raw & 0xFF);
ble_buffer[7] = (uint8_t)(ch_data->peak_raw >> 8);
ble_buffer[8] = (uint8_t)(ch_data->peak_index & 0xFF);
ble_buffer[9] = (uint8_t)(ch_data->peak_index >> 8);
ble_buffer[10] = (uint8_t)(ch_data->baseline_raw & 0xFF);
ble_buffer[11] = (uint8_t)(ch_data->baseline_raw >> 8);
ble_buffer[12] = (uint8_t)(ch_data->num_samples & 0xFF);
ble_buffer[13] = (uint8_t)(ch_data->num_samples >> 8);
/* Send reb: header */
/* Use dr_binary_tx_safe like mec? does, with dr_sd_delay_ms for SoftDevice */
dr_binary_tx_safe(ble_buffer, 7);
dr_sd_delay_ms(BLE_PACKET_DELAY_MS);
dr_ble_debug(0x0102, data_packets); /* After reb: */
/* Data packets (red:) */
uint16_t src_idx = 0;
for (uint16_t pkt = 0; pkt < data_packets; pkt++) {
ble_buffer[0] = 'r';
ble_buffer[1] = 'e';
ble_buffer[2] = 'd';
ble_buffer[3] = ':';
/* Packet 1: reb: header + data merged */
ble_buffer[0] = 'r'; ble_buffer[1] = 'e'; ble_buffer[2] = 'b'; ble_buffer[3] = ':';
ble_buffer[4] = (uint8_t)(ch_data->num_samples & 0xFF);
ble_buffer[5] = (uint8_t)(ch_data->num_samples >> 8);
uint16_t dst_idx = BLE_REB_HEADER_LEN;
uint16_t first_chunk = (total_data_bytes > BLE_REB_DATA_LEN) ? BLE_REB_DATA_LEN : total_data_bytes;
while (src_idx < ch_data->num_samples && (dst_idx - BLE_REB_HEADER_LEN) < first_chunk) {
uint16_t sample = ch_data->samples[src_idx++] & 0x0FFF;
ble_buffer[dst_idx++] = (uint8_t)(sample & 0xFF);
ble_buffer[dst_idx++] = (uint8_t)(sample >> 8);
}
dr_binary_tx_safe(ble_buffer, dst_idx / 2);
/* Continuation packets (red:) — only if data didn't fit in reb: */
uint16_t pkt = 0;
while (src_idx < ch_data->num_samples) {
dr_sd_delay_ms(BLE_PACKET_DELAY_MS);
ble_buffer[0] = 'r'; ble_buffer[1] = 'e'; ble_buffer[2] = 'd'; ble_buffer[3] = ':';
ble_buffer[4] = (uint8_t)(pkt & 0xFF);
ble_buffer[5] = (uint8_t)(pkt >> 8);
uint16_t dst_idx = 6;
uint16_t bytes_this_pkt = 0;
while (src_idx < ch_data->num_samples && bytes_this_pkt < cont_pkt_data_cap) {
dst_idx = BLE_RED_HEADER_LEN;
while (src_idx < ch_data->num_samples && (dst_idx - BLE_RED_HEADER_LEN) < BLE_RED_DATA_LEN) {
uint16_t sample = ch_data->samples[src_idx++] & 0x0FFF;
ble_buffer[dst_idx++] = (uint8_t)(sample & 0xFF);
ble_buffer[dst_idx++] = (uint8_t)(sample >> 8);
bytes_this_pkt += 2;
}
dr_binary_tx_safe(ble_buffer, dst_idx / 2);
dr_sd_delay_ms(BLE_PACKET_DELAY_MS);
dr_ble_debug(0x0103, pkt); /* red: packet sent */
pkt++;
}
dr_ble_debug(0x0104, 0); /* Before ree: */
/* Final packet: ree: */
ble_buffer[0] = 'r';
ble_buffer[1] = 'e';
ble_buffer[2] = 'e';
ble_buffer[3] = ':';
ble_buffer[4] = (uint8_t)(total_packets & 0xFF);
ble_buffer[5] = (uint8_t)(total_packets >> 8);
dr_binary_tx_safe(ble_buffer, 3);
dr_ble_debug(0x010F, 0); /* Function complete */
return DR_ADC_OK;
}
@@ -1036,73 +955,46 @@ dr_adc_err_t dr_adc_transmit_channel_delta(const dr_maa_channel_t *ch_data,
ch_data->num_samples, compressed_size,
100.0f * compressed_size / (ch_data->num_samples * 2));
uint16_t cont_pkt_data_cap = BLE_MTU_SIZE - BLE_CONT_HEADER_LEN; /* 234 bytes */
uint16_t data_packets = (compressed_size + cont_pkt_data_cap - 1) / cont_pkt_data_cap;
uint16_t total_packets = 1 + data_packets;
/* Packet 1: rdb: header(8) + delta data merged */
ble_buffer[0] = 'r'; ble_buffer[1] = 'd'; ble_buffer[2] = 'b'; ble_buffer[3] = ':';
ble_buffer[4] = (uint8_t)(ch_data->num_samples & 0xFF);
ble_buffer[5] = (uint8_t)(ch_data->num_samples >> 8);
ble_buffer[6] = (uint8_t)(compressed_size & 0xFF);
ble_buffer[7] = (uint8_t)(compressed_size >> 8);
/* Version marker for delta mode: 0xC000 | total_packets */
uint16_t pkt_with_ver = total_packets | 0xC000; /* v2 delta marker */
#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 */
/* Packet 1: rdb: header (16 bytes) - includes compressed_size */
ble_buffer[0] = 'r';
ble_buffer[1] = 'd';
ble_buffer[2] = 'b';
ble_buffer[3] = ':';
ble_buffer[4] = (uint8_t)(pkt_with_ver & 0xFF);
ble_buffer[5] = (uint8_t)(pkt_with_ver >> 8);
ble_buffer[6] = (uint8_t)(ch_data->peak_raw & 0xFF);
ble_buffer[7] = (uint8_t)(ch_data->peak_raw >> 8);
ble_buffer[8] = (uint8_t)(ch_data->peak_index & 0xFF);
ble_buffer[9] = (uint8_t)(ch_data->peak_index >> 8);
ble_buffer[10] = (uint8_t)(ch_data->baseline_raw & 0xFF);
ble_buffer[11] = (uint8_t)(ch_data->baseline_raw >> 8);
ble_buffer[12] = (uint8_t)(ch_data->num_samples & 0xFF);
ble_buffer[13] = (uint8_t)(ch_data->num_samples >> 8);
ble_buffer[14] = (uint8_t)(compressed_size & 0xFF);
ble_buffer[15] = (uint8_t)(compressed_size >> 8);
dr_binary_tx_safe(ble_buffer, 8); /* 16 bytes = 8 words */
dr_sd_delay_ms(BLE_PACKET_DELAY_MS);
/* Data packets (rdd:) */
uint16_t dst_idx = BLE_RDB_HEADER_LEN;
uint16_t src_idx = 0;
for (uint16_t pkt = 0; pkt < data_packets; pkt++)
{
ble_buffer[0] = 'r';
ble_buffer[1] = 'd';
ble_buffer[2] = 'd';
ble_buffer[3] = ':';
uint16_t first_chunk = (compressed_size > rdb_data_cap) ? rdb_data_cap : compressed_size;
while (src_idx < first_chunk) {
ble_buffer[dst_idx++] = delta_buffer[src_idx++];
}
/* Pad to word boundary if needed */
if (dst_idx & 1) ble_buffer[dst_idx++] = 0;
dr_binary_tx_safe(ble_buffer, dst_idx / 2);
/* Continuation packets (rdd:) — only if data didn't fit */
uint16_t pkt = 0;
while (src_idx < compressed_size) {
dr_sd_delay_ms(BLE_PACKET_DELAY_MS);
ble_buffer[0] = 'r'; ble_buffer[1] = 'd'; ble_buffer[2] = 'd'; ble_buffer[3] = ':';
ble_buffer[4] = (uint8_t)(pkt & 0xFF);
ble_buffer[5] = (uint8_t)(pkt >> 8);
uint16_t dst_idx = 6;
uint16_t bytes_this_pkt = 0;
while (src_idx < compressed_size && bytes_this_pkt < cont_pkt_data_cap)
{
dst_idx = BLE_RED_HEADER_LEN;
while (src_idx < compressed_size && (dst_idx - BLE_RED_HEADER_LEN) < BLE_RED_DATA_LEN) {
ble_buffer[dst_idx++] = delta_buffer[src_idx++];
bytes_this_pkt++;
}
/* Pad to word boundary if needed */
if (dst_idx & 1)
{
ble_buffer[dst_idx++] = 0;
}
if (dst_idx & 1) ble_buffer[dst_idx++] = 0;
dr_binary_tx_safe(ble_buffer, dst_idx / 2);
dr_sd_delay_ms(BLE_PACKET_DELAY_MS);
pkt++;
}
/* Final packet: rde: */
ble_buffer[0] = 'r';
ble_buffer[1] = 'd';
ble_buffer[2] = 'e';
ble_buffer[3] = ':';
ble_buffer[4] = (uint8_t)(total_packets & 0xFF);
ble_buffer[5] = (uint8_t)(total_packets >> 8);
dr_binary_tx_safe(ble_buffer, 3);
dr_sd_delay_ms(BLE_PACKET_DELAY_MS);
return DR_ADC_OK;
}
@@ -1117,8 +1009,7 @@ dr_adc_err_t dr_adc_transmit_channel_delta(const dr_maa_channel_t *ch_data,
/* Global async context */
static maa_async_ctx_t g_maa_ctx = { .state = MAA_ASYNC_IDLE };
/* Version marker for async MAA */
#define MAA_ASYNC_VERSION_MARKER 0xC000 /* vC: async 8-channel + raa delay fix + cleanup */
/* (version marker 제거 — reb+red merged protocol) */
/**
* @brief Capture one channel (internal helper)
@@ -1141,7 +1032,10 @@ static dr_adc_err_t maa_async_capture_channel(uint8_t ch)
}
/**
* @brief Send reb: header for current channel
* @brief Send reb: header + data merged for current channel
*
* reb: tag(4) + num_samples(2) + data(up to 238 bytes)
* 100샘플(200B) 이하면 이 패킷 하나로 채널 전송 완료
*/
static void maa_async_send_header(void)
{
@@ -1149,45 +1043,33 @@ static void maa_async_send_header(void)
uint8_t *buf = g_maa_ctx.ble_buffer;
uint16_t total_data_bytes = ch->num_samples * 2;
uint16_t cont_pkt_data_cap = BLE_MTU_SIZE - BLE_CONT_HEADER_LEN;
g_maa_ctx.data_packets = (total_data_bytes + cont_pkt_data_cap - 1) / cont_pkt_data_cap;
g_maa_ctx.total_packets = 1 + g_maa_ctx.data_packets;
uint16_t pkts_with_ver = g_maa_ctx.total_packets | MAA_ASYNC_VERSION_MARKER;
/* reb: header (14 bytes) */
/* reb: header + data merged */
buf[0] = 'r'; buf[1] = 'e'; buf[2] = 'b'; buf[3] = ':';
buf[4] = (uint8_t)(pkts_with_ver & 0xFF);
buf[5] = (uint8_t)(pkts_with_ver >> 8);
buf[6] = (uint8_t)(ch->peak_raw & 0xFF);
buf[7] = (uint8_t)(ch->peak_raw >> 8);
buf[8] = (uint8_t)(ch->peak_index & 0xFF);
buf[9] = (uint8_t)(ch->peak_index >> 8);
buf[10] = (uint8_t)(ch->baseline_raw & 0xFF);
buf[11] = (uint8_t)(ch->baseline_raw >> 8);
buf[12] = (uint8_t)(ch->num_samples & 0xFF);
buf[13] = (uint8_t)(ch->num_samples >> 8);
buf[4] = (uint8_t)(ch->num_samples & 0xFF);
buf[5] = (uint8_t)(ch->num_samples >> 8);
dr_binary_tx_safe(buf, 7); /* 14 bytes = 7 words */
uint16_t dst_idx = BLE_REB_HEADER_LEN;
uint16_t first_chunk = (total_data_bytes > BLE_REB_DATA_LEN) ? BLE_REB_DATA_LEN : total_data_bytes;
uint16_t src_idx = 0;
while (src_idx < ch->num_samples && (dst_idx - BLE_REB_HEADER_LEN) < first_chunk) {
uint16_t sample = ch->samples[src_idx++];
buf[dst_idx++] = (uint8_t)(sample & 0xFF);
buf[dst_idx++] = (uint8_t)(sample >> 8);
}
/* 50ms BLE 대기 시간을 활용하여 raw 덤프 로그 (캡처 시간에 영향 없음)
if (g_plat.log)
{
if (g_maa_ctx.current_ch > 0) g_plat.log("\r\n");
g_plat.log("CH%u raw (%u samples):\r\n", g_maa_ctx.current_ch, ch->num_samples);
for (uint16_t i = 0; i < ch->num_samples; i++)
{
g_plat.log("%u ", ch->samples[i]);
if ((i + 1) % 16 == 0) g_plat.log("\r\n");
}
if (ch->num_samples % 16 != 0) g_plat.log("\r\n");
}*/
dr_sd_delay_ms(50); /* Allow BLE stack to process TX */
dr_binary_tx_safe(buf, dst_idx / 2);
dr_sd_delay_ms(5); /* dr_binary_tx_safe 내부 재시도(40ms)로 TX 완료 보장, 최소 딜레이만 */
g_maa_ctx.current_pkt = 0;
g_maa_ctx.data_offset = 0;
g_maa_ctx.state = MAA_ASYNC_TX_DATA;
g_maa_ctx.data_offset = src_idx * 2; /* 이미 전송한 바이트 수 */
/* 데이터가 첫 패킷에 다 들어갔으면 바로 다음 채널로 */
if (g_maa_ctx.data_offset >= total_data_bytes) {
g_maa_ctx.state = MAA_ASYNC_TX_DATA; /* send_data_packet()에서 false 반환 → 다음 채널 */
} else {
g_maa_ctx.state = MAA_ASYNC_TX_DATA;
}
}
/**
@@ -1207,7 +1089,7 @@ static bool maa_async_send_data_packet(void)
uint16_t pkt_idx = g_maa_ctx.current_pkt;
uint16_t remaining = total_data_bytes - g_maa_ctx.data_offset;
uint16_t chunk_size = (remaining > BLE_CONT_DATA_LEN) ? BLE_CONT_DATA_LEN : remaining;
uint16_t chunk_size = (remaining > BLE_RED_DATA_LEN) ? BLE_RED_DATA_LEN : remaining;
/* red: packet header */
buf[0] = 'r'; buf[1] = 'e'; buf[2] = 'd'; buf[3] = ':';
+10 -14
View File
@@ -73,7 +73,7 @@
*============================================================================*/
#define DR_ADC_SCLK_MHZ 8.6f /**< SPI bit-bang SCLK frequency */
#define DR_ADC_CLOCKS_PER_SAMPLE 16 /**< ADC121S051: 16 SCLK per sample */
#define DR_ADC_ECHO_SAMPLES_MAX 100 /**< Maximum samples */
#define DR_ADC_ECHO_SAMPLES_MAX 119 /**< Maximum samples */
#define DR_ADC_ECHO_SAMPLES_DEFAULT 100 /**< Default samples */
#define DR_ADC_SAMPLE_INTERVAL_US 1.86f /**< 16 / 8.6MHz = 1.86us per sample */
#define DR_ADC_SOUND_SPEED_MM_US 1.54f /**< Sound speed in tissue (mm/us) */
@@ -311,7 +311,7 @@ void dr_adc_print_buffer(const uint16_t *buffer, uint16_t num_samples);
* @return dr_adc_err_t Error code
*
* @note Must call dr_adc_register_ble_tx() before using this function
* @note BLE packets: reb: (header), red: (data), ree: (end)
* @note BLE packets: reb: (header+data merged), red: (continuation, >119 samples only)
* @note Higher averaging reduces noise but increases measurement time (~0.3ms per avg)
*/
dr_adc_err_t dr_adc_burst_capture_transmit(uint8_t freq_option, uint16_t delay_us,
@@ -345,9 +345,6 @@ void dr_piezo_select_channel(uint8_t channel);
typedef struct {
uint16_t samples[MAA_SAMPLES_MAX]; /**< Raw sample data */
uint16_t num_samples; /**< Actual sample count */
uint16_t peak_raw; /**< Peak amplitude */
uint16_t peak_index; /**< Peak sample index */
uint16_t baseline_raw; /**< Baseline level */
} dr_maa_channel_t;
/**
@@ -373,10 +370,11 @@ dr_adc_err_t dr_adc_capture_channel_only(uint8_t freq_option, uint16_t delay_us,
/**
* @brief Transmit captured channel data via BLE
*
* Sends previously captured channel data using reb:/red:/ree: protocol.
* Sends previously captured channel data using reb+red merged protocol.
* reb: tag(4) + num_samples(2) + data(up to 238B). red: only if > 119 samples.
*
* @param ch_data Pointer to captured channel data
* @param ble_buffer Working buffer for BLE packets (>= 240 bytes)
* @param ble_buffer Working buffer for BLE packets (>= 244 bytes)
* @return dr_adc_err_t Error code
*/
dr_adc_err_t dr_adc_transmit_channel(const dr_maa_channel_t *ch_data,
@@ -410,7 +408,7 @@ dr_adc_err_t dr_adc_delta_compress(const uint16_t *samples, uint16_t num_samples
/**
* @brief Transmit captured channel data via BLE with delta compression
*
* Uses rdb:/rdd:/rde: protocol (delta variant of reb:/red:/ree:)
* Uses rdb+rdd merged protocol (delta variant of reb+red merged)
*
* @param ch_data Pointer to captured channel data
* @param ble_buffer Working buffer for BLE packets (>= 240 bytes)
@@ -424,8 +422,8 @@ dr_adc_err_t dr_adc_transmit_channel_delta(const dr_maa_channel_t *ch_data,
*
* Design: State machine driven by BLE TX complete events
* Flow:
* maa? cmd -> maa_async_start() -> capture CH0 -> TX reb: -> TX red: ...
* BLE_NUS_EVT_TX_RDY -> maa_async_continue() -> TX next packet or next channel
* maa? cmd -> maa_async_start() -> capture all CH -> TX reb:(header+data) -> TX red: (if needed)
* BLE_NUS_EVT_TX_RDY -> maa_async_on_tx_ready() -> TX next packet or next channel
* All done -> TX raa: -> state=IDLE
*============================================================================*/
@@ -433,7 +431,7 @@ dr_adc_err_t dr_adc_transmit_channel_delta(const dr_maa_channel_t *ch_data,
typedef enum {
MAA_ASYNC_IDLE = 0, /**< Not active */
MAA_ASYNC_CAPTURING, /**< ADC capture in progress */
MAA_ASYNC_TX_HEADER, /**< Sending reb: header */
MAA_ASYNC_TX_HEADER, /**< Sending reb: header+data merged */
MAA_ASYNC_TX_DATA, /**< Sending red: data packets */
MAA_ASYNC_NEXT_CHANNEL, /**< Preparing next channel */
MAA_ASYNC_COMPLETE /**< Sending raa: and finishing */
@@ -452,8 +450,6 @@ typedef struct {
uint16_t averaging; /**< Averaging count */
uint8_t *ble_buffer; /**< Working buffer for BLE packets */
dr_maa_channel_t channels[MAA_NUM_CHANNELS]; /**< Captured data for each channel */
uint16_t total_packets; /**< Total packets for current channel */
uint16_t data_packets; /**< Data packets for current channel */
bool pre_capture_all; /**< true: 전채널 캡처 완료 후 일괄 전송 (mbb용) */
void (*on_complete_cb)(void); /**< 비동기 캡처 완료 후 호출될 콜백 (NULL이면 미사용) */
} maa_async_ctx_t;
@@ -469,7 +465,7 @@ typedef struct {
* @param num_samples Samples per channel (1~200)
* @param cycles Burst cycles (3~7)
* @param averaging Averaging count (1~1000)
* @param ble_buffer Working buffer (>= 240 bytes)
* @param ble_buffer Working buffer (>= 244 bytes)
* @return dr_adc_err_t DR_ADC_OK if started successfully
*/
dr_adc_err_t maa_async_start(uint8_t freq_option, uint16_t delay_us,
+62 -19
View File
@@ -26,12 +26,14 @@
#include <string.h>
#include "nrf_gpio.h"
#include "debug_print.h"
#include "nrf_delay.h"
#include "dr_piezo.h"
#include "dr_util.h"
#include "main.h"
#include "dr_adc121s051.h"
#include "led_control.h"
#include "app_timer.h"
/*==============================================================================
* 외부 함수 선언부
@@ -355,9 +357,9 @@ static bool dr_parse_cmd(const uint8_t *buffer, uint8_t length, ParsedCmd *out)
* - TX/RX 전원 활성화 (mpa? / rpa:) - test용
* - TX/RX 전원 비활성화 (mpb? / rpb:) - test용
* - 단일 채널 Burst (mpc? / rpc:) - test용
* - 단일 채널 Burst + ADC -> echo capture (mec? / reb: -> red: -> ree:) : TX/RX Active -> 응답 -> TX/RX Sleep
* - 모든 채널 Burst + ADC -> echo capture (maa? / reb: -> red: -> raa:) : TX/RX Active -> 응답 -> TX/RX Sleep
* - 모든 채널 Burst + ADC -> echo capture (mbb? / reb: -> red: -> raa:) + 각종 센서 측정(배터리, IMU, 온도) : TX/RX Active -> 응답 -> TX/RX Sleep (NEW!)
* - 단일 채널 Burst + ADC -> echo capture (mec? / reb: merged -> raa:) : TX/RX Active -> 응답 -> TX/RX Sleep
* - 모든 채널 Burst + ADC -> echo capture (maa? / reb: merged -> raa:) : TX/RX Active -> 응답 -> TX/RX Sleep
* - 모든 채널 Burst + ADC -> echo capture (mbb? / reb: merged -> raa:) + 각종 센서 측정(배터리, IMU, 온도) : TX/RX Active -> 응답 -> TX/RX Sleep
*
* 삭제 명령어
* - 디바이스 활성화/슬립 : TX 전원 활성화/비활성화와 동일 기능
@@ -407,6 +409,9 @@ static int Cmd_mbb(const ParsedCmd *cmd); /* mbb? 6채널 캡처 + 센서 측
static int Cmd_mcf(const ParsedCmd *cmd); /* mcf? 피에조 파라미터 읽기 (FDS) */
static int Cmd_mcs(const ParsedCmd *cmd); /* mcs? 피에조 파라미터 쓰기 (FDS) */
/* E. LED 제어 */
static int Cmd_mls(const ParsedCmd *cmd); /* mls? LED 상태 설정 (앱 → 기기) */
/* ---- 명령 테이블 ---- */
@@ -451,6 +456,9 @@ static CmdEntry g_cmd_table[] = {
{ "mbb?", true, Cmd_mbb },
{ "mcf?", true, Cmd_mcf },
{ "mcs?", true, Cmd_mcs },
/* F. LED 제어 */
{ "mls?", true, Cmd_mls },
};
/* 명령 테이블 엔트리 수 (컴파일 타임 계산) */
@@ -786,13 +794,12 @@ static int Cmd_mpc(const ParsedCmd *cmd)
* word 4: averaging (기본=1) - 평균화 횟수 (1~1000, 노이즈 저감용)
* word 5: piezo_ch (기본=0) - 피에조 채널 (0~7)
*
* 응답 멀티패킷 형식 (16비트 원시):
* 헤더 패킷 ("reb:"): total_pkts, peak, idx, baseline, samples
* 데이터 패킷 ("red:"): pkt_idx + 16비트 ADC 원시 데이터
* 료 패킷 ("ree:"): total_bytes_sent
* 응답 패킷 형식 (reb+red merged protocol):
* 패킷 ("reb:"): num_samples(2) + raw_data(up to 238B = 119샘플)
* 연속 패킷 ("red:"): pkt_idx(2) + raw_data(up to 238B) — 119샘플 초과 시만
* 료 패킷 ("raa:"): status(2)
*
* 16비트 포맷: 샘플당 2바이트 (Little-Endian)
* 예: 140샘플(20cm) = 280바이트 = 약 2패킷
* 100샘플 기준: reb: 1패킷(206B)으로 완료
*/
static int Cmd_mec(const ParsedCmd *cmd)
{
@@ -935,14 +942,12 @@ static int Cmd_cmd(const ParsedCmd *cmd)
* 주파수 = 1.8MHz, 딜레이 = 10us, 샘플 수 = 140
* 사이클 = 7, 평균화 = 5회
*
* 응답 형식:
* 각 채널(CH0~CH3)마다:
* reb: [총패킷수(2)] [피크(2)] [인덱스(2)] [기준선(2)] [샘플수(2)]
* red: [패킷순번(2)] [데이터...]
* 응답 형식 (reb+red merged protocol):
* 각 채널마다:
* reb: [샘플수(2)] [raw_data...] — 119샘플 이하면 단일 패킷
* red: [패킷순번(2)] [데이터...] — 119샘플 초과 시만
* 전체 완료:
* raa: [상태(2)]
*
* 버전 마커: 0xA000 (vA) = 비동기 4채널
*/
/* 피에조 캡처 파라미터: FDS(m_config)에서 로드, 앱에서 변경 가능 */
@@ -1010,30 +1015,36 @@ static void all_sensors(void)
info4 = true; /* 센서값을 전역 변수에 저장 (BLE 전송 안 함) */
/* 1. 배터리 전압 측정 → info_batt에 저장 */
DBG_PRINTF("1");
battery_saadc_done = false;
battery_level_meas();
for (timeout_cnt = 0; !battery_saadc_done && timeout_cnt < 100; timeout_cnt++)
{
dr_sd_delay_ms(1); /* 콜백 오면 즉시 탈출, 최대 100ms */
}
DBG_PRINTF("3");
/* 2. IMU 6축 단발 읽기 → info_imu[6]에 저장 */
hw_i2c_init_once();
imu_read_direct();
DBG_PRINTF("4");
/* 3. 온도 측정 → info_temp에 저장 (TMP235는 Piezo TX/RX 전원 필요) */
if (!dr_piezo_is_power_on())
{
dr_piezo_power_on();
}
DBG_PRINTF("5");
DBG_PRINTF("6");
tmp235_saadc_done = false;
tmp235_voltage_level_meas();
for (timeout_cnt = 0; !tmp235_saadc_done && timeout_cnt < 100; timeout_cnt++)
{
dr_sd_delay_ms(1); /* 콜백 오면 즉시 탈출, 최대 100ms, 타임아웃 시 이전 값 또는 쓰레기값.. */
}
DBG_PRINTF("8");
info4 = false;
@@ -1054,6 +1065,7 @@ static void all_sensors(void)
buf[19] = (uint8_t)(info_temp >> 8);
dr_binary_tx_safe(buf, 10); /* 20바이트 = 10워드 */
DBG_PRINTF("9");
/* 배터리, IMU, 온도 확인용 로그 */
//if (g_plat.log) g_plat.log("-------------------------------------------------------------------------------------\r\n[Battery] %u\r\n[IMU] %d,%d,%d,%d,%d,%d\r\n[Temperature] %u\r\n\r\n", info_batt, (int16_t)info_imu[0], (int16_t)info_imu[1], (int16_t)info_imu[2],
@@ -1064,11 +1076,11 @@ static void all_sensors(void)
/**
* @brief mbb? - 6채널 전체 캡처 + 센서 측정 (배터리/온도/IMU)
*
* 센서 측정(rbb:) → 6채널 비동기 캡처(reb:/red:/raa:) → TX/RX OFF
* 센서 측정(rbb:) → 6채널 비동기 캡처(reb: merged) → raa: → TX/RX OFF
*
* 응답 흐름:
* 1) 센서 측정: rbb: [배터리(2) + IMU 6축(12) + 온도(2)]
* 2) 각 채널(CH0~CH5): reb: [헤더] → red: [데이터...]
* 2) 각 채널: reb: [샘플수(2) + raw_data] (100샘플이면 단일 패킷)
* 3) 캡처 완료: raa: [상태]
*/
@@ -1077,6 +1089,7 @@ static int Cmd_mbb(const ParsedCmd *cmd)
dr_adc_err_t err;
all_sensors(); /* 배터리, IMU, 온도 센서 먼저 측정 */
DBG_PRINTF("10");
if (maa_async_is_busy())
{
@@ -1111,6 +1124,7 @@ static int Cmd_mbb(const ParsedCmd *cmd)
dr_piezo_power_off();
return 1;
}
DBG_PRINTF("11\n");
return 1;
}
@@ -1187,6 +1201,35 @@ static int Cmd_mcs(const ParsedCmd *cmd)
}
/**
* @brief mls? - LED 상태 설정 (앱 → 기기)
*
* 파라미터: [state(2)] - led_state_t enum 값
* 0: OFF, 4: DETACH_WARNING, 5: ALIGN_SEARCHING, 6: ALIGN_COMPLETE
* 응답: rls: [state(2)] - 에코
*/
static int Cmd_mls(const ParsedCmd *cmd)
{
if (cmd->data_len < 2) {
dr_ble_return_1("rls:", 0xFFFF);
return 1;
}
uint16_t state;
dr_get_u16(cmd, 0, &state);
if (state > LED_STATE_ERROR) {
dr_ble_return_1("rls:", 0xFFFE);
return 1;
}
led_set_state((led_state_t)state);
dr_ble_return_1("rls:", state);
return 1;
}
/*==============================================================================
* 설정: HW/시리얼 넘버 FDS 읽기/쓰기
*
@@ -67,7 +67,7 @@ static nrf_saadc_value_t adc_buf;
APP_TIMER_DEF(m_battery_loop_timer_id);
/* 배터리 측정 주기: 5초 (밀리초 단위) */
#define BATTERY_LOOP_INTERVAL 5000
#define BATTERY_LOOP_INTERVAL 60000
/* 저전압 체크 플래그 — battery_loop에서 true로 설정, 핸들러에서 소비 */
bool low_battery_check = false;
@@ -143,6 +143,7 @@ void battery_event_handler( nrf_drv_saadc_evt_t const * p_event )
nrf_drv_saadc_uninit();
/* 콜백 완료 알림 (all_sensors 대기 해제용) */
if (info4) DBG_PRINTF("2");
battery_saadc_done = true;
/* ADC값 → mV 변환 (매크로: ADC x 600/1023 x 6) */
@@ -216,7 +217,9 @@ static void battery_configure(void)
saadc_config.resolution = NRF_SAADC_RESOLUTION_12BIT; // 10 -> 12bit
saadc_config.oversample = NRF_SAADC_OVERSAMPLE_4X;
ret_code_t err_code = nrf_drv_saadc_init(&saadc_config, battery_event_handler);
APP_ERROR_CHECK(err_code);
if (err_code != NRF_SUCCESS) {
return; /* SAADC 사용 중 → 이번 측정 스킵, 다음 주기에 재시도 */
}
/* AIN2 채널 설정: 싱글엔드 입력, 1/6 gain, burst + TACQ 20μs */
nrf_saadc_channel_config_t config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN2);
@@ -381,7 +381,7 @@ void imu_callback(inv_imu_sensor_event_t *event)
/* UART 모드: "Tp" 접두사로 6축 데이터를 텍스트 형식으로 출력 */
else if(cmd_type_t == CMD_UART) {
DBG_PRINTF("Tp%d,%d,%d,%d,%d,%d\r\n\r\n", accel[0], accel[1], accel[2], gyro[0], gyro[1], gyro[2]);
//DBG_PRINTF("Tp%d,%d,%d,%d,%d,%d\r\n\r\n", accel[0], accel[1], accel[2], gyro[0], gyro[1], gyro[2]);
}
/*
@@ -399,7 +399,7 @@ void imu_callback(inv_imu_sensor_event_t *event)
ssp_data[5] = (uint16_t)gyro[2];
format_data(imu_bin_buffer, "rsp:", ssp_data,12);
DBG_PRINTF("Tp%d,%d,%d,%d,%d,%d\r\n\r\n", accel[0], accel[1], accel[2], gyro[0], gyro[1], gyro[2]);
//DBG_PRINTF("Tp%d,%d,%d,%d,%d,%d\r\n\r\n", accel[0], accel[1], accel[2], gyro[0], gyro[1], gyro[2]);
dr_binary_tx_safe(imu_bin_buffer,8);
if(custom_add_data==true) {
@@ -569,6 +569,7 @@ int imu_read_direct(void)
/* 일반 모드: "rsp:" 태그로 BLE 즉시 전송 */
format_data(imu_bin_buffer, "rsp:", ssp_data, 12);
dr_binary_tx_safe(imu_bin_buffer, 8);
DBG_PRINTF("0");
}
/* IMU 슬립 모드: PWR_MGMT0 = 0x00 → 가속도/자이로 모두 OFF (전력 절감) */
@@ -0,0 +1,292 @@
/*******************************************************************************
* @file led_control.c
* @brief LED 직접 제어 드라이버 (BSP 미사용)
* @date 2026-03-30
*
* app_timer 1개로 2색 LED(녹색/주황)의 복합 blink 패턴 구현
* 단순 on/off 상태는 타이머 없이 즉시 GPIO 제어
* 복합 패턴(에러 등)은 phase 기반 state machine으로 처리
******************************************************************************/
#include "led_control.h"
#include "nrf_gpio.h"
#include "app_timer.h"
/*==============================================================================
* 내부 상수
*============================================================================*/
#define MS_TO_TICKS(ms) APP_TIMER_TICKS(ms)
/*==============================================================================
* 색상
*============================================================================*/
#define COLOR_NONE 0
#define COLOR_GREEN 1
#define COLOR_ORANGE 2
/*==============================================================================
* 에러 패턴 상수 (No.7)
* 3Hz 깜빡 3회 = 166ms on + 166ms off × 3 = ~1초, 이후 꺼짐 1초
*============================================================================*/
#define ERROR_BLINK_ON_MS 166
#define ERROR_BLINK_OFF_MS 166
#define ERROR_BLINK_COUNT 3
#define ERROR_PAUSE_MS 1000
/*==============================================================================
* 패턴 테이블 (단순 blink 용)
*============================================================================*/
typedef struct
{
uint32_t on_ms; /* LED 켜짐 시간 (ms) */
uint32_t off_ms; /* LED 꺼짐 시간 (ms) */
uint8_t color; /* COLOR_GREEN 또는 COLOR_ORANGE */
bool repeat; /* true: 무한 반복, false: 1회 후 OFF */
} led_pattern_t;
static const led_pattern_t m_patterns[LED_STATE_COUNT] =
{
[LED_STATE_OFF] = { 0, 0, COLOR_NONE, false },
[LED_STATE_POWER_ON] = { 2000, 0, COLOR_GREEN, false }, /* 초록 점등 2초 → 유지 */
[LED_STATE_POWER_OFF] = { 2000, 0, COLOR_GREEN, false }, /* 초록 점등 2초 → OFF */
[LED_STATE_ADVERTISING] = { 500, 500, COLOR_GREEN, true }, /* 초록 점멸 1초 */
[LED_STATE_DETACH_WARNING] = { 1000, 3000, COLOR_GREEN, true }, /* 초록 1초 on / 3초 off */
[LED_STATE_ALIGN_SEARCHING] = { 1000, 1000, COLOR_ORANGE, true }, /* 주황 점멸 1초 */
[LED_STATE_ALIGN_COMPLETE] = { 3000, 1000, COLOR_GREEN, true }, /* 초록 3초 on / 1초 off */
[LED_STATE_ERROR] = { 0, 0, COLOR_ORANGE, true } /* 별도 state machine */
};
/*==============================================================================
* 모듈 변수
*============================================================================*/
APP_TIMER_DEF(m_led_timer);
static led_state_t m_current_state = LED_STATE_OFF;
static bool m_phase_on; /* true: LED 켜진 구간, false: 꺼진 구간 */
/* 에러 패턴 전용 */
static uint8_t m_error_blink_cnt; /* 현재까지 깜빡인 횟수 */
static uint8_t m_error_phase; /* 0: blink-on, 1: blink-off, 2: pause */
/*==============================================================================
* GPIO 헬퍼
*============================================================================*/
static inline void led_green_on(void)
{
#if LED_ACTIVE_LOW
nrf_gpio_pin_clear(LED_PIN_GREEN);
#else
nrf_gpio_pin_set(LED_PIN_GREEN);
#endif
}
static inline void led_green_off(void)
{
#if LED_ACTIVE_LOW
nrf_gpio_pin_set(LED_PIN_GREEN);
#else
nrf_gpio_pin_clear(LED_PIN_GREEN);
#endif
}
static inline void led_orange_on(void)
{
#if LED_ACTIVE_LOW
nrf_gpio_pin_clear(LED_PIN_ORANGE);
#else
nrf_gpio_pin_set(LED_PIN_ORANGE);
#endif
}
static inline void led_orange_off(void)
{
#if LED_ACTIVE_LOW
nrf_gpio_pin_set(LED_PIN_ORANGE);
#else
nrf_gpio_pin_clear(LED_PIN_ORANGE);
#endif
}
static void led_all_off(void)
{
led_green_off();
led_orange_off();
}
static void led_color_on(uint8_t color)
{
led_all_off();
if (color == COLOR_GREEN) led_green_on();
else if (color == COLOR_ORANGE) led_orange_on();
}
/*==============================================================================
* 타이머 시작 헬퍼
*============================================================================*/
static void timer_start_ms(uint32_t ms)
{
if (ms == 0) return;
app_timer_start(m_led_timer, MS_TO_TICKS(ms), NULL);
}
/*==============================================================================
* 에러 패턴 state machine (No.7)
* phase 0: LED ON (166ms) → phase 1
* phase 1: LED OFF (166ms) → cnt++ → cnt<3 ? phase 0 : phase 2
* phase 2: PAUSE (1000ms) → cnt=0, phase 0
*============================================================================*/
static void error_pattern_start(void)
{
m_error_blink_cnt = 0;
m_error_phase = 0;
led_color_on(COLOR_ORANGE);
timer_start_ms(ERROR_BLINK_ON_MS);
}
static void error_pattern_tick(void)
{
switch (m_error_phase)
{
case 0: /* ON 구간 끝 → OFF */
led_all_off();
m_error_phase = 1;
timer_start_ms(ERROR_BLINK_OFF_MS);
break;
case 1: /* OFF 구간 끝 */
m_error_blink_cnt++;
if (m_error_blink_cnt < ERROR_BLINK_COUNT)
{
/* 다시 ON */
m_error_phase = 0;
led_color_on(COLOR_ORANGE);
timer_start_ms(ERROR_BLINK_ON_MS);
}
else
{
/* 3회 완료 → pause */
m_error_phase = 2;
timer_start_ms(ERROR_PAUSE_MS);
}
break;
case 2: /* pause 끝 → 처음부터 */
m_error_blink_cnt = 0;
m_error_phase = 0;
led_color_on(COLOR_ORANGE);
timer_start_ms(ERROR_BLINK_ON_MS);
break;
default:
break;
}
}
/*==============================================================================
* 타이머 콜백
*============================================================================*/
static void led_timer_handler(void * p_context)
{
(void)p_context;
/* 에러 상태는 별도 처리 */
if (m_current_state == LED_STATE_ERROR)
{
error_pattern_tick();
return;
}
const led_pattern_t * p = &m_patterns[m_current_state];
if (m_phase_on)
{
/* ON→OFF 전환 */
led_all_off();
m_phase_on = false;
if (p->off_ms > 0)
{
timer_start_ms(p->off_ms);
}
else if (!p->repeat)
{
/* 1회성: off_ms == 0 이면 그냥 유지 (POWER_ON) 또는 끄기 (POWER_OFF) */
if (m_current_state == LED_STATE_POWER_OFF)
{
led_all_off();
m_current_state = LED_STATE_OFF;
}
/* POWER_ON: 점등 유지 상태 → 타이머 x */
}
}
else
{
/* OFF → ON 전환 */
if (p->repeat)
{
led_color_on(p->color);
m_phase_on = true;
timer_start_ms(p->on_ms);
}
/* repeat == false && off_ms > 0 인 경우는 현재 없음 */
}
}
/*==============================================================================
* 공개 함수
*============================================================================*/
void led_init(void)
{
/* GPIO 출력 설정 */
nrf_gpio_cfg_output(LED_PIN_GREEN);
nrf_gpio_cfg_output(LED_PIN_ORANGE);
led_all_off();
/* 타이머 생성 (single-shot) */
app_timer_create(&m_led_timer, APP_TIMER_MODE_SINGLE_SHOT, led_timer_handler);
m_current_state = LED_STATE_OFF;
}
void led_set_state(led_state_t state)
{
if (state >= LED_STATE_COUNT) return;
/* 이전 패턴 중단 */
app_timer_stop(m_led_timer);
led_all_off();
m_current_state = state;
m_phase_on = false;
const led_pattern_t * p = &m_patterns[state];
switch (state)
{
case LED_STATE_OFF:
/* 이미 all off */
break;
/* 에러 패턴: 별도 state machine */
case LED_STATE_ERROR:
error_pattern_start();
break;
/* 그 외: on 구간부터 시작 */
default:
led_color_on(p->color);
m_phase_on = true;
if (p->on_ms > 0)
{
timer_start_ms(p->on_ms);
}
break;
}
}
led_state_t led_get_state(void)
{
return m_current_state;
}
@@ -0,0 +1,52 @@
/*******************************************************************************
* @file led_control.h
* @brief LED 직접 제어 드라이버 (BSP 미사용)
* @date 2026-03-30
*
* 녹색(P0.12) + 주황(P0.29) 2색 LED를 app_timer 기반으로 제어
* 각 상태별 on/off 시간, 색상, 반복 패턴을 테이블로 관리
******************************************************************************/
#ifndef LED_CONTROL_H__
#define LED_CONTROL_H__
#include <stdint.h>
#include <stdbool.h>
/*==============================================================================
* LED 핀 정의
*============================================================================*/
#define LED_PIN_GREEN NRF_GPIO_PIN_MAP(0, 12) /* 녹색 LED */
#define LED_PIN_ORANGE NRF_GPIO_PIN_MAP(0, 29) /* 주황 LED */
#define LED_ACTIVE_LOW 1 /* 1 = Active Low (GPIO LOW에서 LED 켜짐) */
/*==============================================================================
* LED 상태 열거형
*============================================================================*/
typedef enum
{
LED_STATE_OFF = 0, /* 모든 LED 소등 */
LED_STATE_POWER_ON, /* 1: 전원 ON - 초록 소등 → 녹색 점등 2초 */
LED_STATE_POWER_OFF, /* 2: 전원 OFF - 초록 점등 2초 → 소등 */
LED_STATE_ADVERTISING, /* 3: 블루투스 스캐닝 - 초록 점멸 1초 간격(반복) */
LED_STATE_DETACH_WARNING, /* 4: 정상 작동중(미부착 시) - 초록 점등 1초 / 소등 3초 (반복) */
LED_STATE_ALIGN_SEARCHING, /* 5: 정렬모드(탐지중) - 주황 점멸 1초 간격 (반복) */
LED_STATE_ALIGN_COMPLETE, /* 6: 정렬모드(탐지 완료) - 초록 점등 3초 / 소등 1초 */
LED_STATE_ERROR, /* 7: 오류 발생 - 주황 3Hz 깜빡 3회 / 꺼짐 1초 (반복) */
LED_STATE_COUNT /* 배열 크기 */
} led_state_t;
/*==============================================================================
* 공개 함수
*============================================================================*/
/** @brief LED GPIO 초기화 + 타이머 생성 - main()에서 1회 호출 */
void led_init(void);
/** @brief LED 상태 변경 - 이전 패턴을 즉시 중단하고 새 패턴 시작 */
void led_set_state(led_state_t state);
/** @brief 현재 LED 상태 조회 */
led_state_t led_get_state(void);
#endif /* LED_CONTROL_H__ */
@@ -114,6 +114,7 @@
#include "debug_print.h" /* 디버그 출력 매크로 (DBG_PRINTF) */
#include "fstorage.h" /* Flash Storage 래퍼 (FDS 초기화/저장/로드) */
#include "dr_piezo.h" /* 피에조 초음파 드라이버 */
#include "led_control.h" /* LED 직접 제어 드라이버 (BSP 미사용) */
@@ -143,9 +144,9 @@
#define APP_ADV_DURATION 18000 /* 광고 지속시간: 18000 x 10ms = 3분 → 타임아웃 시 슬립 */
#endif
#define MIN_CONN_INTERVAL MSEC_TO_UNITS(20, UNIT_1_25_MS) /* 최소 연결 간격: 20ms */
#define MAX_CONN_INTERVAL MSEC_TO_UNITS(75, UNIT_1_25_MS) /* 최대 연결 간격: 75ms */
#define SLAVE_LATENCY 4 /* 슬레이브 지연: 0 (매 연결 이벤트마다 응답) */
#define MIN_CONN_INTERVAL MSEC_TO_UNITS(30, UNIT_1_25_MS) /* 최소 연결 간격: 30ms */
#define MAX_CONN_INTERVAL MSEC_TO_UNITS(30, UNIT_1_25_MS) /* 최대 연결 간격: 30ms */
#define SLAVE_LATENCY 0 /* 슬레이브 지연: 0 (매 연결 이벤트마다 응답) */
#define CONN_SUP_TIMEOUT MSEC_TO_UNITS(4000, UNIT_10_MS) /* 연결 감독 타임아웃: 4초 */
#define FIRST_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(5000) /* 첫 파라미터 갱신 요청까지 5초 대기 */
#define NEXT_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(30000) /* 이후 갱신 요청 간격: 30초 */
@@ -439,7 +440,7 @@ static void t_power_off_timeout_handler(void * p_context)
UNUSED_PARAMETER(p_context);
APP_ERROR_CHECK(app_timer_stop(m_power_off_delay_timer_id));
DBG_PRINTF("[PWR] Off timeout\r\n");
bsp_indication_set(BSP_INDICATE_USER_STATE_OFF); /* LED OFF */
led_set_state(LED_STATE_OFF); /* LED OFF */
power_control_handler(OFF); /* P0.8 LOW → 전원 차단 */
}
@@ -609,7 +610,24 @@ static void nus_data_handler(ble_nus_evt_t * p_evt)
{
cmd_type_t = CMD_BLE;
ble_got_new_data = true;
/* Central이 연결 간격을 늘렸을 수 있으므로 빠른 간격 재요청 (30초에 1회) */
{
static uint32_t last_update_tick = 0;
uint32_t now_tick = app_timer_cnt_get();
if (last_update_tick == 0 ||
app_timer_cnt_diff_compute(now_tick, last_update_tick) >= APP_TIMER_TICKS(30000))
{
ble_gap_conn_params_t conn_params;
conn_params.min_conn_interval = MIN_CONN_INTERVAL;
conn_params.max_conn_interval = MAX_CONN_INTERVAL;
conn_params.slave_latency = SLAVE_LATENCY;
conn_params.conn_sup_timeout = CONN_SUP_TIMEOUT;
sd_ble_gap_conn_param_update(m_conn_handle, &conn_params);
last_update_tick = now_tick;
}
}
/* 콜백에서는 복사만 하고, 메인 루프에서 처리 */
if (p_evt->params.rx_data.length <= BLE_NUS_MAX_DATA_LEN)
{
@@ -781,7 +799,7 @@ static void on_adv_evt(ble_adv_evt_t ble_adv_evt)
switch (ble_adv_evt)
{
case BLE_ADV_EVT_FAST:
bsp_indication_set(BSP_INDICATE_ADVERTISING);
led_set_state(LED_STATE_ADVERTISING);
DBG_PRINTF("[ADV] Fast\r\n");
break;
case BLE_ADV_EVT_IDLE:
@@ -926,19 +944,15 @@ static void pm_evt_handler(pm_evt_t const * p_evt)
DBG_PRINTF("[PM] Sec failed\r\n");
break;
/* 모든 본딩 삭제 완료 → 광고 재시작 */
/* 모든 본딩 삭제 완료 → 활성 연결이 없을 때만 광고 재시작 */
case PM_EVT_PEERS_DELETE_SUCCEEDED:
DBG_PRINTF("[PM] Bonds erased\r\n");
advertising_start(false);
if (m_conn_handle == BLE_CONN_HANDLE_INVALID) {
advertising_start(false);
}
break;
/* 보안 설정 요청 → 재페어링 허용 */
case PM_EVT_CONN_SEC_CONFIG_REQ:
{
pm_conn_sec_config_t conn_sec_config = {.allow_repairing = true};
pm_conn_sec_config_reply(p_evt->conn_handle, &conn_sec_config);
}
break;
/* PM_EVT_CONN_SEC_CONFIG_REQ → ble_quick_security에서 처리 */
/* 피어 데이터(본딩 정보) 갱신 성공 → 피어 BLE 주소 저장 */
case PM_EVT_PEER_DATA_UPDATE_SUCCEEDED:
@@ -1002,14 +1016,17 @@ static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
#endif
#if FEATURE_SECURE_CONNECTION
pm_handler_secure_on_connection(p_ble_evt);
if (!ble_security_is_dev_mode()) {
pm_handler_secure_on_connection(p_ble_evt);
}
#endif
switch (p_ble_evt->header.evt_id)
{
/* BLE 연결이 끊어지는 경우 */
case BLE_GAP_EVT_DISCONNECTED:
DBG_PRINTF("[BLE] Disconnected\r\n");
DBG_PRINTF("[BLE] Disconnected (reason 0x%02X)\r\n",
p_ble_evt->evt.gap_evt.params.disconnected.reason);
ble_connection_st = 0;
m_conn_handle = BLE_CONN_HANDLE_INVALID;
m_tx_in_progress = false;
@@ -1050,8 +1067,7 @@ static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
APP_ERROR_CHECK(err_code);
}
err_code = bsp_indication_set(BSP_INDICATE_CONNECTED);
APP_ERROR_CHECK(err_code);
led_set_state(LED_STATE_OFF); /* 연결 완료 → LED OFF */
break;
@@ -1085,10 +1101,18 @@ static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
case BLE_GAP_EVT_SEC_PARAMS_REQUEST:
#if !FEATURE_SECURE_CONNECTION
err_code = sd_ble_gap_sec_params_reply(m_conn_handle,
BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP,
err_code = sd_ble_gap_sec_params_reply(m_conn_handle,
BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP,
NULL, NULL);
APP_ERROR_CHECK(err_code);
#else
if (ble_security_is_dev_mode()) {
err_code = sd_ble_gap_sec_params_reply(m_conn_handle,
BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP,
NULL, NULL);
APP_ERROR_CHECK(err_code);
DBG_PRINTF("DEV: Rejected security request\r\n");
}
#endif
break;
@@ -1174,7 +1198,7 @@ void bsp_event_handler(bsp_event_t event)
if (processing == false)
{
DBG_PRINTF("[BSP] Power\r\n");
bsp_indication_set(BSP_INDICATE_USER_STATE_ON);
led_set_state(LED_STATE_POWER_OFF);
go_device_power_off = true;
main_timer_start();
}
@@ -1189,14 +1213,16 @@ void bsp_event_handler(bsp_event_t event)
* 유틸리티 함수
*============================================================================*/
/** @brief BSP 버튼/LED 초기화 (부트 Phase 5) */
/** @brief BSP 버튼 초기화 + LED 직접 초기화 (부트 Phase 5) */
static void buttons_leds_init(bool * p_erase_bonds)
{
bsp_event_t startup_event;
uint32_t err_code = bsp_init(BSP_INIT_LEDS | BSP_INIT_BUTTONS, bsp_event_handler);
uint32_t err_code = bsp_init(BSP_INIT_BUTTONS, bsp_event_handler);
APP_ERROR_CHECK(err_code);
led_init();
err_code = bsp_btn_ble_init(NULL, &startup_event);
APP_ERROR_CHECK(err_code);
@@ -1325,7 +1351,7 @@ void assert_nrf_callback(uint16_t line_num, const uint8_t * p_file_name)
*/
void sleep_mode_enter(void)
{
bsp_indication_set(BSP_INDICATE_USER_STATE_ON);
led_set_state(LED_STATE_POWER_OFF);
DBG_PRINTF("[SYS] Sleep\r\n");
APP_ERROR_CHECK(app_timer_start(m_power_off_delay_timer_id, APP_TIMER_TICKS(POWER_OFF_DELAY), NULL));
}
@@ -1338,7 +1364,7 @@ void sleep_mode_enter(void)
*/
void device_power_off(void)
{
bsp_indication_set(BSP_INDICATE_USER_STATE_ON);
led_set_state(LED_STATE_POWER_OFF);
APP_ERROR_CHECK(app_timer_start(m_power_off_delay_timer_id, APP_TIMER_TICKS(POWER_OFF_DELAY), NULL));
}
@@ -1595,7 +1621,7 @@ static void main_s(void * p_context)
if ((cnt_s < 150) && (m_reset_status != 2))
{
DBG_PRINTF("[BTN] Short->OFF\r\n");
bsp_indication_set(BSP_INDICATE_USER_STATE_OFF);
led_set_state(LED_STATE_OFF);
power_control_handler(OFF);
cnt_s = 0;
}
@@ -1622,7 +1648,8 @@ static void main_s(void * p_context)
battery_timer_start();
#if FEATURE_SECURE_CONNECTION
advertising_start(erase_bonds || bond_data_delete);
/* DEV 모드: ble_security_quick_init()에서 이미 bond 삭제됨 → 중복 방지 */
advertising_start(!ble_security_is_dev_mode() && (erase_bonds || bond_data_delete));
#else
advertising_start();
#endif
@@ -1637,7 +1664,7 @@ static void main_s(void * p_context)
device_reset = false;
if (cnt_s == 150) {
bsp_indication_set(BSP_INDICATE_USER_STATE_ON);
led_set_state(LED_STATE_POWER_ON);
DBG_PRINTF("[BTN] 1.5s\r\n");
}
@@ -27,8 +27,12 @@
* Firmware 식별 코드
* - VBTFW0100 = 개발(시험)용 Ver 1.00
* - VB0FW0100 = 양산용 Ver 1.00
*
* Firmware Version Update History
* - VBTFW0101 : reb+red 패킷 병합 (채널당 단일 패킷), 260330 jhChun
* - VBTFW0102 : LED 상태 설정 명령(msl) 추가 및 재페어링 허용 등 260331 jhChun
------------------------------------------------------------------------- */
#define FIRMWARE_VERSION "VBTFW0100"
#define FIRMWARE_VERSION "VBTFW0102"
/*==============================================================================
* 데이터 길이 상수
File diff suppressed because one or more lines are too long
@@ -164,6 +164,7 @@ void tmp235_voltage_handler(nrf_drv_saadc_evt_t const * p_event) /* TMP325 Vout
// data_tx_handler(ble_tx_buffer);
}
DBG_PRINTF("7");
tmp235_saadc_done = true;
}