Compare commits
18 Commits
9e48c084b4
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 3ec6afec36 | |||
| 6cb927e831 | |||
| 47a31c1eb4 | |||
| 5ee39f834e | |||
| 5128c44912 | |||
| 8fdc153ed5 | |||
| d8ef9bcb41 | |||
| 007e0b5683 | |||
| d63a9cfa3c | |||
| 217d741d31 | |||
| f464838ed4 | |||
| a150d1da96 | |||
| 11a0b3f11a | |||
| 5e27eb762d | |||
| 689ad29aa6 | |||
| aaf22d942a | |||
| 91e294da5a | |||
| 856cf084b2 |
@@ -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
|
||||
// 기타 보안 실패 → 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);
|
||||
}
|
||||
|
||||
@@ -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] = ':';
|
||||
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) {
|
||||
/* 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);
|
||||
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);
|
||||
|
||||
/* 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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/* 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) */
|
||||
dr_binary_tx_safe(ble_buffer, dst_idx / 2);
|
||||
pkt++;
|
||||
}
|
||||
|
||||
/* 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,86 +852,47 @@ 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] = ':';
|
||||
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) {
|
||||
/* 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);
|
||||
bytes_this_pkt += 2;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
dr_ble_debug(0x0103, pkt); /* red: packet sent */
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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 */
|
||||
dr_binary_tx_safe(ble_buffer, dst_idx / 2);
|
||||
pkt++;
|
||||
}
|
||||
|
||||
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);
|
||||
uint16_t dst_idx = BLE_RDB_HEADER_LEN;
|
||||
uint16_t src_idx = 0;
|
||||
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, 8); /* 16 bytes = 8 words */
|
||||
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);
|
||||
|
||||
/* Data packets (rdd:) */
|
||||
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] = ':';
|
||||
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 */
|
||||
|
||||
/* 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");
|
||||
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);
|
||||
}
|
||||
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.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] = ':';
|
||||
|
||||
@@ -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,
|
||||
|
||||
+61
-18
@@ -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,23 +1015,28 @@ 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();
|
||||
|
||||
@@ -1034,6 +1044,7 @@ static void all_sensors(void)
|
||||
{
|
||||
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 미사용) */
|
||||
|
||||
|
||||
|
||||
@@ -121,7 +122,7 @@
|
||||
* 빌드 설정
|
||||
* DEBUG_MINIMAL_BOOT 삭제 26.03.26 jhChun
|
||||
*============================================================================*/
|
||||
#define BLE_DEV_MODE 0 /* 1: 개발 모드 (보안 없음), 0: 양산 모드 (패스키 필수) */
|
||||
#define BLE_DEV_MODE 1 /* 1: 개발 모드 (보안 없음), 0: 양산 모드 (패스키 필수) */
|
||||
|
||||
/*==============================================================================
|
||||
* 하드웨어 핀 정의
|
||||
@@ -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 → 전원 차단 */
|
||||
}
|
||||
|
||||
@@ -610,6 +611,23 @@ 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,20 +944,16 @@ 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");
|
||||
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
|
||||
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;
|
||||
@@ -1040,8 +1057,17 @@ static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
|
||||
|
||||
sd_ble_gap_tx_power_set(BLE_GAP_TX_POWER_ROLE_CONN, m_conn_handle, 8);
|
||||
|
||||
err_code = bsp_indication_set(BSP_INDICATE_CONNECTED);
|
||||
// 2M PHY 요청 (지원 안 되면 자동으로 1M 유지)
|
||||
{
|
||||
ble_gap_phys_t const phys = {
|
||||
.rx_phys = BLE_GAP_PHY_2MBPS,
|
||||
.tx_phys = BLE_GAP_PHY_2MBPS,
|
||||
};
|
||||
err_code = sd_ble_gap_phy_update(m_conn_handle, &phys);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
|
||||
led_set_state(LED_STATE_OFF); /* 연결 완료 → LED OFF */
|
||||
|
||||
break;
|
||||
|
||||
@@ -1056,6 +1082,15 @@ static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
|
||||
}
|
||||
break;
|
||||
|
||||
case BLE_GAP_EVT_PHY_UPDATE:
|
||||
{
|
||||
ble_gap_evt_phy_update_t const * p_phy = &p_ble_evt->evt.gap_evt.params.phy_update;
|
||||
DBG_PRINTF("[BLE] PHY updated: TX=%s, RX=%s\r\n",
|
||||
p_phy->tx_phy == BLE_GAP_PHY_2MBPS ? "2M" : "1M",
|
||||
p_phy->rx_phy == BLE_GAP_PHY_2MBPS ? "2M" : "1M");
|
||||
}
|
||||
break;
|
||||
|
||||
case BLE_GATTC_EVT_TIMEOUT:
|
||||
case BLE_GATTS_EVT_TIMEOUT:
|
||||
DBG_PRINTF("[BLE] Timeout\r\n");
|
||||
@@ -1070,6 +1105,14 @@ static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
|
||||
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;
|
||||
|
||||
@@ -1155,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();
|
||||
}
|
||||
@@ -1170,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);
|
||||
|
||||
@@ -1306,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));
|
||||
}
|
||||
@@ -1319,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));
|
||||
}
|
||||
|
||||
@@ -1576,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;
|
||||
}
|
||||
@@ -1603,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
|
||||
@@ -1618,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"
|
||||
|
||||
/*==============================================================================
|
||||
* 데이터 길이 상수
|
||||
|
||||
+153
-141
File diff suppressed because it is too large
Load Diff
+15
-5
File diff suppressed because one or more lines are too long
@@ -11555,7 +11555,7 @@
|
||||
// <i> The time set aside for this connection on every connection interval in 1.25 ms units.
|
||||
|
||||
#ifndef NRF_SDH_BLE_GAP_EVENT_LENGTH
|
||||
#define NRF_SDH_BLE_GAP_EVENT_LENGTH 6
|
||||
#define NRF_SDH_BLE_GAP_EVENT_LENGTH 24
|
||||
#endif
|
||||
|
||||
// <o> NRF_SDH_BLE_GATT_MAX_MTU_SIZE - Static maximum MTU size.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user