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:로 판단
This commit is contained in:
jhChun
2026-03-30 16:37:41 +09:00
parent 689ad29aa6
commit 5e27eb762d
3 changed files with 150 additions and 278 deletions

View File

@@ -517,17 +517,12 @@ extern void dr_sd_delay_ms(uint32_t ms);
extern void dr_piezo_burst_sw(uint8_t cycles); extern void dr_piezo_burst_sw(uint8_t cycles);
/* BLE packet constants */ /* BLE packet constants */
#define BLE_MTU_SIZE 240 #define BLE_MTU_SIZE 244 /* ATT MTU 247 - 3 (ATT header) = 244 */
#define BLE_FIRST_HEADER_LEN 14 /* "rec:" + header */ #define BLE_REB_HEADER_LEN 6 /* "reb:" tag(4) + num_samples(2) */
#define BLE_CONT_HEADER_LEN 6 /* "red:" + packet_index */ #define BLE_RED_HEADER_LEN 6 /* "red:" tag(4) + pkt_idx(2) */
#define BLE_FIRST_DATA_LEN (BLE_MTU_SIZE - BLE_FIRST_HEADER_LEN) #define BLE_REB_DATA_LEN (BLE_MTU_SIZE - BLE_REB_HEADER_LEN) /* 238 bytes = 119 samples */
#define BLE_CONT_DATA_LEN (BLE_MTU_SIZE - BLE_CONT_HEADER_LEN) #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_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 * 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) * @brief Integrated burst + capture + BLE transmit (16-bit raw data)
* *
* PROTOCOL v3: Small header packet + separate data packets with delays * reb+red merged protocol: header와 데이터를 첫 패킷에 합침
* (Large first packets were being dropped by BLE stack)
* *
* Response format: * Response format:
* Packet 1 (reb:): header only (14 bytes) * 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 234 bytes = 117 samples) * Packet 2~N (red:): pkt_idx(2) + raw_data(up to 238 bytes) — 119샘플 초과 시만
* Final (ree:): total_packets(2) * 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, dr_adc_err_t dr_adc_burst_capture_transmit(uint8_t freq_option, uint16_t delay_us,
uint16_t num_samples, uint8_t cycles, 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 (averaging > 1000) averaging = 1000; /* Maximum 1000 */
if (piezo_ch >= MAA_NUM_CHANNELS) piezo_ch = 0; /* 채널 범위 검증 */ if (piezo_ch >= MAA_NUM_CHANNELS) piezo_ch = 0; /* 채널 범위 검증 */
dr_adc_echo_t echo; /* echo 분석은 PC에서 raw 데이터로 직접 수행 */
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;
/* Accumulator buffer for averaging (32-bit to prevent overflow) */ /* Accumulator buffer for averaging (32-bit to prevent overflow) */
static uint32_t accum_buffer[DR_ADC_ECHO_SAMPLES_MAX]; 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); ADC_LOG("mec: averaged %u measurements", averaging);
} }
/*--- Step 6: Analyze averaged data ---*/ /*--- Step 6: Transmit via BLE - reb: + red: merged protocol ---*/
dr_adc_analyze_echo(m_echo_buffer, num_samples, &echo, 100); /* 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 ---*/ uint16_t total_data_bytes = num_samples * 2;
/* 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 src_idx = 0; uint16_t src_idx = 0;
for (uint16_t pkt = 0; pkt < data_packets; pkt++) {
ble_buffer[0] = 'r'; /* Packet 1: reb: header + data merged */
ble_buffer[1] = 'e'; ble_buffer[0] = 'r'; ble_buffer[1] = 'e'; ble_buffer[2] = 'b'; ble_buffer[3] = ':';
ble_buffer[2] = 'd'; ble_buffer[4] = (uint8_t)(num_samples & 0xFF);
ble_buffer[3] = ':'; 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[4] = (uint8_t)(pkt & 0xFF);
ble_buffer[5] = (uint8_t)(pkt >> 8); ble_buffer[5] = (uint8_t)(pkt >> 8);
uint16_t dst_idx = 6; dst_idx = BLE_RED_HEADER_LEN;
uint16_t bytes_this_pkt = 0; while (src_idx < num_samples && (dst_idx - BLE_RED_HEADER_LEN) < BLE_RED_DATA_LEN) {
while (src_idx < echo.num_samples && bytes_this_pkt < cont_pkt_data_cap) {
uint16_t sample = m_echo_buffer[src_idx++] & 0x0FFF; uint16_t sample = m_echo_buffer[src_idx++] & 0x0FFF;
ble_buffer[dst_idx++] = (uint8_t)(sample & 0xFF); ble_buffer[dst_idx++] = (uint8_t)(sample & 0xFF);
ble_buffer[dst_idx++] = (uint8_t)(sample >> 8); 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_binary_tx_safe(ble_buffer, dst_idx / 2);
dr_sd_delay_ms(BLE_PACKET_DELAY_MS); /* Inter-packet delay */ pkt++;
} }
/* vD: Send raa: only - ree: causes subsequent packet loss */ /* raa: completion — skip_raa: 0=send (mec), 1=skip (maa - caller sends final raa) */
/* DrBench now saves channel data when receiving raa: */
/* skip_raa: 0=send raa (mec), 1=skip raa (maa - caller sends final raa) */
if (!skip_raa) { if (!skip_raa) {
ble_buffer[0] = 'r'; dr_sd_delay_ms(BLE_PACKET_DELAY_MS);
ble_buffer[1] = 'a'; ble_buffer[0] = 'r'; ble_buffer[1] = 'a'; ble_buffer[2] = 'a'; ble_buffer[3] = ':';
ble_buffer[2] = 'a';
ble_buffer[3] = ':';
ble_buffer[4] = 0x00; ble_buffer[4] = 0x00;
ble_buffer[5] = 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; return DR_ADC_OK;
} }
@@ -876,16 +840,7 @@ dr_adc_err_t dr_adc_capture_channel_only(uint8_t freq_option, uint16_t delay_us,
} }
out_channel->num_samples = num_samples; out_channel->num_samples = num_samples;
/* peak/baseline 분석은 PC에서 raw 데이터로 직접 수행 */
/* 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);
return DR_ADC_OK; return DR_ADC_OK;
} }
@@ -894,87 +849,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, dr_adc_err_t dr_adc_transmit_channel(const dr_maa_channel_t *ch_data,
uint8_t *ble_buffer) 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) { 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; 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 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; uint16_t src_idx = 0;
for (uint16_t pkt = 0; pkt < data_packets; pkt++) {
ble_buffer[0] = 'r'; /* Packet 1: reb: header + data merged */
ble_buffer[1] = 'e'; ble_buffer[0] = 'r'; ble_buffer[1] = 'e'; ble_buffer[2] = 'b'; ble_buffer[3] = ':';
ble_buffer[2] = 'd'; ble_buffer[4] = (uint8_t)(ch_data->num_samples & 0xFF);
ble_buffer[3] = ':'; 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[4] = (uint8_t)(pkt & 0xFF);
ble_buffer[5] = (uint8_t)(pkt >> 8); ble_buffer[5] = (uint8_t)(pkt >> 8);
uint16_t dst_idx = 6; dst_idx = BLE_RED_HEADER_LEN;
uint16_t bytes_this_pkt = 0; while (src_idx < ch_data->num_samples && (dst_idx - BLE_RED_HEADER_LEN) < BLE_RED_DATA_LEN) {
while (src_idx < ch_data->num_samples && bytes_this_pkt < cont_pkt_data_cap) {
uint16_t sample = ch_data->samples[src_idx++] & 0x0FFF; uint16_t sample = ch_data->samples[src_idx++] & 0x0FFF;
ble_buffer[dst_idx++] = (uint8_t)(sample & 0xFF); ble_buffer[dst_idx++] = (uint8_t)(sample & 0xFF);
ble_buffer[dst_idx++] = (uint8_t)(sample >> 8); ble_buffer[dst_idx++] = (uint8_t)(sample >> 8);
bytes_this_pkt += 2;
} }
dr_binary_tx_safe(ble_buffer, dst_idx / 2); dr_binary_tx_safe(ble_buffer, dst_idx / 2);
dr_sd_delay_ms(BLE_PACKET_DELAY_MS); pkt++;
dr_ble_debug(0x0103, pkt); /* red: packet sent */
} }
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; return DR_ADC_OK;
} }
@@ -1036,73 +952,46 @@ dr_adc_err_t dr_adc_transmit_channel_delta(const dr_maa_channel_t *ch_data,
ch_data->num_samples, compressed_size, ch_data->num_samples, compressed_size,
100.0f * compressed_size / (ch_data->num_samples * 2)); 100.0f * compressed_size / (ch_data->num_samples * 2));
uint16_t cont_pkt_data_cap = BLE_MTU_SIZE - BLE_CONT_HEADER_LEN; /* 234 bytes */ /* Packet 1: rdb: header(8) + delta data merged */
uint16_t data_packets = (compressed_size + cont_pkt_data_cap - 1) / cont_pkt_data_cap; ble_buffer[0] = 'r'; ble_buffer[1] = 'd'; ble_buffer[2] = 'b'; ble_buffer[3] = ':';
uint16_t total_packets = 1 + data_packets; 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 */ #define BLE_RDB_HEADER_LEN 8 /* rdb: tag(4) + num_samples(2) + compressed_size(2) */
uint16_t pkt_with_ver = total_packets | 0xC000; /* v2 delta marker */ uint16_t rdb_data_cap = BLE_MTU_SIZE - BLE_RDB_HEADER_LEN; /* 236 bytes */
/* Packet 1: rdb: header (16 bytes) - includes compressed_size */ uint16_t dst_idx = BLE_RDB_HEADER_LEN;
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 src_idx = 0; uint16_t src_idx = 0;
for (uint16_t pkt = 0; pkt < data_packets; pkt++) uint16_t first_chunk = (compressed_size > rdb_data_cap) ? rdb_data_cap : compressed_size;
{ while (src_idx < first_chunk) {
ble_buffer[0] = 'r'; ble_buffer[dst_idx++] = delta_buffer[src_idx++];
ble_buffer[1] = 'd'; }
ble_buffer[2] = 'd'; /* Pad to word boundary if needed */
ble_buffer[3] = ':'; 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[4] = (uint8_t)(pkt & 0xFF);
ble_buffer[5] = (uint8_t)(pkt >> 8); ble_buffer[5] = (uint8_t)(pkt >> 8);
uint16_t dst_idx = 6; dst_idx = BLE_RED_HEADER_LEN;
uint16_t bytes_this_pkt = 0; while (src_idx < compressed_size && (dst_idx - BLE_RED_HEADER_LEN) < BLE_RED_DATA_LEN) {
while (src_idx < compressed_size && bytes_this_pkt < cont_pkt_data_cap)
{
ble_buffer[dst_idx++] = delta_buffer[src_idx++]; 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_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; return DR_ADC_OK;
} }
@@ -1117,8 +1006,7 @@ dr_adc_err_t dr_adc_transmit_channel_delta(const dr_maa_channel_t *ch_data,
/* Global async context */ /* Global async context */
static maa_async_ctx_t g_maa_ctx = { .state = MAA_ASYNC_IDLE }; static maa_async_ctx_t g_maa_ctx = { .state = MAA_ASYNC_IDLE };
/* Version marker for async MAA */ /* (version marker 제거 — reb+red merged protocol) */
#define MAA_ASYNC_VERSION_MARKER 0xC000 /* vC: async 8-channel + raa delay fix + cleanup */
/** /**
* @brief Capture one channel (internal helper) * @brief Capture one channel (internal helper)
@@ -1141,7 +1029,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) static void maa_async_send_header(void)
{ {
@@ -1149,45 +1040,33 @@ static void maa_async_send_header(void)
uint8_t *buf = g_maa_ctx.ble_buffer; uint8_t *buf = g_maa_ctx.ble_buffer;
uint16_t total_data_bytes = ch->num_samples * 2; 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 + data merged */
/* reb: header (14 bytes) */
buf[0] = 'r'; buf[1] = 'e'; buf[2] = 'b'; buf[3] = ':'; buf[0] = 'r'; buf[1] = 'e'; buf[2] = 'b'; buf[3] = ':';
buf[4] = (uint8_t)(pkts_with_ver & 0xFF); buf[4] = (uint8_t)(ch->num_samples & 0xFF);
buf[5] = (uint8_t)(pkts_with_ver >> 8); buf[5] = (uint8_t)(ch->num_samples >> 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);
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 덤프 로그 (캡처 시간에 영향 없음) dr_binary_tx_safe(buf, dst_idx / 2);
if (g_plat.log) dr_sd_delay_ms(5); /* dr_binary_tx_safe 내부 재시도(40ms)로 TX 완료 보장, 최소 딜레이만 */
{
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 */
g_maa_ctx.current_pkt = 0; g_maa_ctx.current_pkt = 0;
g_maa_ctx.data_offset = 0; g_maa_ctx.data_offset = src_idx * 2; /* 이미 전송한 바이트 수 */
g_maa_ctx.state = MAA_ASYNC_TX_DATA;
/* 데이터가 첫 패킷에 다 들어갔으면 바로 다음 채널로 */
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 +1086,7 @@ static bool maa_async_send_data_packet(void)
uint16_t pkt_idx = g_maa_ctx.current_pkt; uint16_t pkt_idx = g_maa_ctx.current_pkt;
uint16_t remaining = total_data_bytes - g_maa_ctx.data_offset; 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 */ /* red: packet header */
buf[0] = 'r'; buf[1] = 'e'; buf[2] = 'd'; buf[3] = ':'; buf[0] = 'r'; buf[1] = 'e'; buf[2] = 'd'; buf[3] = ':';

View File

@@ -311,7 +311,7 @@ void dr_adc_print_buffer(const uint16_t *buffer, uint16_t num_samples);
* @return dr_adc_err_t Error code * @return dr_adc_err_t Error code
* *
* @note Must call dr_adc_register_ble_tx() before using this function * @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) * @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, 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 { typedef struct {
uint16_t samples[MAA_SAMPLES_MAX]; /**< Raw sample data */ uint16_t samples[MAA_SAMPLES_MAX]; /**< Raw sample data */
uint16_t num_samples; /**< Actual sample count */ 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; } 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 * @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 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 * @return dr_adc_err_t Error code
*/ */
dr_adc_err_t dr_adc_transmit_channel(const dr_maa_channel_t *ch_data, 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 * @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 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 (>= 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 * Design: State machine driven by BLE TX complete events
* Flow: * Flow:
* maa? cmd -> maa_async_start() -> capture CH0 -> TX reb: -> TX red: ... * maa? cmd -> maa_async_start() -> capture all CH -> TX reb:(header+data) -> TX red: (if needed)
* BLE_NUS_EVT_TX_RDY -> maa_async_continue() -> TX next packet or next channel * BLE_NUS_EVT_TX_RDY -> maa_async_on_tx_ready() -> TX next packet or next channel
* All done -> TX raa: -> state=IDLE * 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 { typedef enum {
MAA_ASYNC_IDLE = 0, /**< Not active */ MAA_ASYNC_IDLE = 0, /**< Not active */
MAA_ASYNC_CAPTURING, /**< ADC capture in progress */ 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_TX_DATA, /**< Sending red: data packets */
MAA_ASYNC_NEXT_CHANNEL, /**< Preparing next channel */ MAA_ASYNC_NEXT_CHANNEL, /**< Preparing next channel */
MAA_ASYNC_COMPLETE /**< Sending raa: and finishing */ MAA_ASYNC_COMPLETE /**< Sending raa: and finishing */
@@ -452,8 +450,6 @@ typedef struct {
uint16_t averaging; /**< Averaging count */ uint16_t averaging; /**< Averaging count */
uint8_t *ble_buffer; /**< Working buffer for BLE packets */ uint8_t *ble_buffer; /**< Working buffer for BLE packets */
dr_maa_channel_t channels[MAA_NUM_CHANNELS]; /**< Captured data for each channel */ 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용) */ bool pre_capture_all; /**< true: 전채널 캡처 완료 후 일괄 전송 (mbb용) */
void (*on_complete_cb)(void); /**< 비동기 캡처 완료 후 호출될 콜백 (NULL이면 미사용) */ void (*on_complete_cb)(void); /**< 비동기 캡처 완료 후 호출될 콜백 (NULL이면 미사용) */
} maa_async_ctx_t; } maa_async_ctx_t;
@@ -469,7 +465,7 @@ typedef struct {
* @param num_samples Samples per channel (1~200) * @param num_samples Samples per channel (1~200)
* @param cycles Burst cycles (3~7) * @param cycles Burst cycles (3~7)
* @param averaging Averaging count (1~1000) * @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 * @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, dr_adc_err_t maa_async_start(uint8_t freq_option, uint16_t delay_us,

View File

@@ -356,9 +356,9 @@ static bool dr_parse_cmd(const uint8_t *buffer, uint8_t length, ParsedCmd *out)
* - TX/RX 전원 활성화 (mpa? / rpa:) - test용 * - TX/RX 전원 활성화 (mpa? / rpa:) - test용
* - TX/RX 전원 비활성화 (mpb? / rpb:) - test용 * - TX/RX 전원 비활성화 (mpb? / rpb:) - test용
* - 단일 채널 Burst (mpc? / rpc:) - test용 * - 단일 채널 Burst (mpc? / rpc:) - test용
* - 단일 채널 Burst + ADC -> echo capture (mec? / reb: -> red: -> ree:) : TX/RX Active -> 응답 -> TX/RX Sleep * - 단일 채널 Burst + ADC -> echo capture (mec? / reb: merged -> raa:) : TX/RX Active -> 응답 -> TX/RX Sleep
* - 모든 채널 Burst + ADC -> echo capture (maa? / reb: -> red: -> 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: -> red: -> raa:) + 각종 센서 측정(배터리, IMU, 온도) : TX/RX Active -> 응답 -> TX/RX Sleep (NEW!) * - 모든 채널 Burst + ADC -> echo capture (mbb? / reb: merged -> raa:) + 각종 센서 측정(배터리, IMU, 온도) : TX/RX Active -> 응답 -> TX/RX Sleep
* *
* 삭제 명령어 * 삭제 명령어
* - 디바이스 활성화/슬립 : TX 전원 활성화/비활성화와 동일 기능 * - 디바이스 활성화/슬립 : TX 전원 활성화/비활성화와 동일 기능
@@ -787,13 +787,12 @@ static int Cmd_mpc(const ParsedCmd *cmd)
* word 4: averaging (기본=1) - 평균화 횟수 (1~1000, 노이즈 저감용) * word 4: averaging (기본=1) - 평균화 횟수 (1~1000, 노이즈 저감용)
* word 5: piezo_ch (기본=0) - 피에조 채널 (0~7) * word 5: piezo_ch (기본=0) - 피에조 채널 (0~7)
* *
* 응답 멀티패킷 형식 (16비트 원시): * 응답 패킷 형식 (reb+red merged protocol):
* 헤더 패킷 ("reb:"): total_pkts, peak, idx, baseline, samples * 패킷 ("reb:"): num_samples(2) + raw_data(up to 238B = 119샘플)
* 데이터 패킷 ("red:"): pkt_idx + 16비트 ADC 원시 데이터 * 연속 패킷 ("red:"): pkt_idx(2) + raw_data(up to 238B) — 119샘플 초과 시만
* 료 패킷 ("ree:"): total_bytes_sent * 료 패킷 ("raa:"): status(2)
* *
* 16비트 포맷: 샘플당 2바이트 (Little-Endian) * 100샘플 기준: reb: 1패킷(206B)으로 완료
* 예: 140샘플(20cm) = 280바이트 = 약 2패킷
*/ */
static int Cmd_mec(const ParsedCmd *cmd) static int Cmd_mec(const ParsedCmd *cmd)
{ {
@@ -936,14 +935,12 @@ static int Cmd_cmd(const ParsedCmd *cmd)
* 주파수 = 1.8MHz, 딜레이 = 10us, 샘플 수 = 140 * 주파수 = 1.8MHz, 딜레이 = 10us, 샘플 수 = 140
* 사이클 = 7, 평균화 = 5회 * 사이클 = 7, 평균화 = 5회
* *
* 응답 형식: * 응답 형식 (reb+red merged protocol):
* 각 채널(CH0~CH3)마다: * 각 채널마다:
* reb: [총패킷수(2)] [피크(2)] [인덱스(2)] [기준선(2)] [샘플수(2)] * reb: [샘플수(2)] [raw_data...] — 119샘플 이하면 단일 패킷
* red: [패킷순번(2)] [데이터...] * red: [패킷순번(2)] [데이터...] — 119샘플 초과 시만
* 전체 완료: * 전체 완료:
* raa: [상태(2)] * raa: [상태(2)]
*
* 버전 마커: 0xA000 (vA) = 비동기 4채널
*/ */
/* 피에조 캡처 파라미터: FDS(m_config)에서 로드, 앱에서 변경 가능 */ /* 피에조 캡처 파라미터: FDS(m_config)에서 로드, 앱에서 변경 가능 */
@@ -1072,11 +1069,11 @@ static void all_sensors(void)
/** /**
* @brief mbb? - 6채널 전체 캡처 + 센서 측정 (배터리/온도/IMU) * @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)] * 1) 센서 측정: rbb: [배터리(2) + IMU 6축(12) + 온도(2)]
* 2) 각 채널(CH0~CH5): reb: [헤더] → red: [데이터...] * 2) 각 채널: reb: [샘플수(2) + raw_data] (100샘플이면 단일 패킷)
* 3) 캡처 완료: raa: [상태] * 3) 캡처 완료: raa: [상태]
*/ */