MTU에 따른 BLE 패킷 분할 - Piezo echo ADC 응답 패킷

- 협상된 MTU 기준으로 reb: 응답 패킷 크기 판단
- 기존 reb:가 MTU에 들어가지 않으면 rec: chunk 분할 전송
This commit is contained in:
2026-07-20 17:09:59 +09:00
parent e691b4f2b6
commit 540c17e890
3 changed files with 146 additions and 1 deletions
+109 -1
View File
@@ -4,6 +4,7 @@
******************************************************************************/
#include <zephyr/kernel.h>
#include <zephyr/sys/reboot.h>
#include <zephyr/sys/util.h>
#include <string.h>
#include "cmd_common.h"
@@ -22,6 +23,12 @@ static uint8_t tx_id_buf[4 + HW_NO_LENGTH + SERIAL_NO_LENGTH + SERIAL_NO_LENGTH
static uint8_t tx_rim_buf[4 + 2 + (IMU_FIFO_RIM_TARGET_SAMPLES * IMU_FIFO_SAMPLE_BYTES) + 2];
static uint8_t g_echo_session;
/* ATT MTU에 따른 패킷 분할 */
#define ATT_NOTIFY_HEADER_SIZE 3U
#define REB_OVERHEAD_SIZE 10U
#define REC_OVERHEAD_SIZE 14U
#define ECHO_SAMPLE_SIZE 2U
uint8_t cmd_next_echo_session(void)
{
return g_echo_session++;
@@ -185,7 +192,27 @@ int cmd_send_response_imu(const int16_t accel[3], const int16_t gyro[3])
return ble_data_send(buf, 18);
}
void cmd_send_response_echo(uint8_t session, uint8_t channel, const uint16_t *samples, uint16_t num_samples)
/* ATT MTU에 따라 패킷 분할이 필요한지 계산 */
static uint16_t calc_rec_samples_per_packet(uint16_t notify_payload_max)
{
uint16_t samples_per_pkt = 1U;
// rec:는 고정 오버헤드가 14B, chunk 크기 다시 계산
if (notify_payload_max > REC_OVERHEAD_SIZE)
{
samples_per_pkt = (uint16_t)((notify_payload_max - REC_OVERHEAD_SIZE) / ECHO_SAMPLE_SIZE);
}
// 방어코드
if (samples_per_pkt == 0U)
{
samples_per_pkt = 1U;
}
return samples_per_pkt;
}
static void cmd_send_response_echo_reb(uint8_t session, uint8_t channel, const uint16_t *samples, uint16_t num_samples)
{
// 정적 버퍼 사용 (한 번에 한 명령만 처리)
uint8_t *buf = tx_echo_buf;
@@ -210,6 +237,87 @@ void cmd_send_response_echo(uint8_t session, uint8_t channel, const uint16_t *sa
ble_data_send(buf, payload_len + 2);
}
/*
* rec: packet layout:
* tag (4B): "rec:"
* session (1B): 측정 세션 번호
* channel (1B): piezo 채널 번호
* offset (2B): 전체 샘플 배열에서 시작 인덱스
* total_samples (2B): 전체 샘플 수
* chunk_samples (2B): 현재 패킷에 들어간 샘플 수
* sample data (N): Piezo ADC samples
* crc (2B): tag부터 sample data까지 CRC16
*/
static void cmd_send_response_echo_rec(uint8_t session, uint8_t channel, const uint16_t *samples, uint16_t num_samples, uint16_t samples_per_pkt)
{
uint8_t *buf = tx_echo_buf;
for (uint16_t off = 0U; off < num_samples; off = (uint16_t)(off + samples_per_pkt))
{
uint16_t remain = (uint16_t)(num_samples - off);
uint16_t chunk_samples = MIN(samples_per_pkt, remain);
buf[0] = 'r'; buf[1] = 'e'; buf[2] = 'c'; buf[3] = ':';
buf[4] = session;
buf[5] = channel;
buf[6] = (uint8_t)(off >> 8);
buf[7] = (uint8_t)(off & 0xFF);
buf[8] = (uint8_t)(num_samples >> 8);
buf[9] = (uint8_t)(num_samples & 0xFF);
buf[10] = (uint8_t)(chunk_samples >> 8);
buf[11] = (uint8_t)(chunk_samples & 0xFF);
// 마지막 chunk는 samples_per_pkt보다 작을 수 있음
for (uint16_t i = 0U; i < chunk_samples; i++)
{
buf[12 + i * 2] = (uint8_t)(samples[off + i] >> 8);
buf[13 + i * 2] = (uint8_t)(samples[off + i] & 0xFF);
}
uint16_t payload_len = (uint16_t)(REC_OVERHEAD_SIZE - 2U + (chunk_samples * ECHO_SAMPLE_SIZE));
uint16_t crc = cmd_crc16_compute(buf, payload_len);
DBG_PRINTF("[CMD] rec tx ch=%u off=%u total=%u chunk=%u len=%u\r\n",
channel, off, num_samples, chunk_samples, (uint16_t)(payload_len + 2U));
buf[payload_len] = (uint8_t)(crc & 0xFF);
buf[payload_len + 1U] = (uint8_t)(crc >> 8);
// ble_data_send()는 NUS sent 콜백을 기다리고 다음 chunk 전송
if (ble_data_send(buf, (uint16_t)(payload_len + 2U)) != 0)
{
break;
}
}
}
/* Piezo echo ADC 응답 패킷 */
void cmd_send_response_echo(uint8_t session, uint8_t channel, const uint16_t *samples, uint16_t num_samples)
{
uint16_t notify_payload_max = 0U;
uint16_t mtu = ble_current_mtu();
uint16_t reb_len = (uint16_t)(REB_OVERHEAD_SIZE + (num_samples * ECHO_SAMPLE_SIZE));
// 협상된 ATT MTU에서 최대 payload
if (mtu > ATT_NOTIFY_HEADER_SIZE)
{
notify_payload_max = (uint16_t)(mtu - ATT_NOTIFY_HEADER_SIZE);
}
// 분할이 필요 없는 경우(기존 reb: 패킷이 MTU 안에 들어가는 경우) reb: 응답
if ((notify_payload_max == 0U) || (reb_len <= notify_payload_max))
{
cmd_send_response_echo_reb(session, channel, samples, num_samples);
return;
}
// 기존 reb: 패킷이 현재 MTU 보다 큰 경우 rec:로 전환
uint16_t samples_per_pkt = calc_rec_samples_per_packet(notify_payload_max);
DBG_PRINTF("[CMD] rec split mtu=%u samples=%u chunk=%u\r\n", mtu, num_samples, samples_per_pkt);
cmd_send_response_echo_rec(session, channel, samples, num_samples, samples_per_pkt);
}
void cmd_send_response_bundle(uint16_t batt_mv, const int16_t accel[3], const int16_t gyro[3], int16_t temp_cdeg)
{
uint8_t *buf = tx_bundle_buf;