retain telemetry across transport outages

This commit is contained in:
Jay
2026-08-17 14:00:14 -04:00
parent bc4a2856e1
commit 291c6b54e6
11 changed files with 222 additions and 37 deletions
+37 -12
View File
@@ -24,6 +24,7 @@
#define TRIKKE_SAMPLE_TICKS pdMS_TO_TICKS(1000 / TRIKKE_SAMPLE_RATE_HZ)
#define TRIKKE_SAMPLE_QUEUE_DEPTH 512
#define TRIKKE_METADATA_INTERVAL_PACKETS 64
#define TRIKKE_TRANSPORT_RETRY_DELAY_MS 10
// Software calibration from the 2026-08-17 enclosure six-face capture.
// Accelerometer coefficients are measured. Gyroscope scale is nominal; its
@@ -158,14 +159,24 @@ static void acquisition_task(void *argument)
static bool write_binary_packet(const uint8_t *packet, size_t packet_size)
{
const bool complete = fwrite(packet, 1, packet_size, stdout) == packet_size;
fflush(stdout);
const size_t written = fwrite(packet, 1, packet_size, stdout);
const int flush_result = fflush(stdout);
const bool complete = written == packet_size && flush_result == 0;
if (!complete) {
clearerr(stdout);
}
return complete;
}
static void write_binary_packet_until_sent(
const uint8_t *packet,
size_t packet_size)
{
while (!write_binary_packet(packet, packet_size)) {
vTaskDelay(pdMS_TO_TICKS(TRIKKE_TRANSPORT_RETRY_DELAY_MS));
}
}
static void output_task(void *argument)
{
trikke_context_t *context = argument;
@@ -179,7 +190,7 @@ static void output_task(void *argument)
packet, sizeof(packet), packet_sequence++, esp_timer_get_time(),
atomic_load(&context->dropped_sample_count),
atomic_load(&context->loop_overrun_count), &TRIKKE_METADATA);
write_binary_packet(packet, packet_size);
write_binary_packet_until_sent(packet, packet_size);
while (true) {
trikke_wire_sample_t samples[TRIKKE_WIRE_MAX_RECORDS] = {0};
@@ -190,9 +201,21 @@ static void output_task(void *argument)
}
++sample_count;
while (sample_count < TRIKKE_WIRE_MAX_RECORDS &&
xQueueReceive(context->sample_queue, &samples[sample_count],
pdMS_TO_TICKS(15)) == pdPASS) {
while (sample_count < TRIKKE_WIRE_MAX_RECORDS) {
trikke_wire_sample_t next_sample = {0};
if (xQueuePeek(context->sample_queue, &next_sample,
pdMS_TO_TICKS(15)) != pdPASS) {
break;
}
if (!trikke_wire_timestamp_delta_fits(
samples[sample_count - 1].timestamp_us,
next_sample.timestamp_us)) {
break;
}
if (xQueueReceive(context->sample_queue, &samples[sample_count], 0) !=
pdPASS) {
break;
}
++sample_count;
}
@@ -202,16 +225,14 @@ static void output_task(void *argument)
packet, sizeof(packet), packet_sequence++, esp_timer_get_time(),
atomic_load(&context->dropped_sample_count),
atomic_load(&context->loop_overrun_count), &TRIKKE_METADATA);
write_binary_packet(packet, packet_size);
write_binary_packet_until_sent(packet, packet_size);
}
packet_size = trikke_encode_sample_packet(
packet, sizeof(packet), packet_sequence++,
atomic_load(&context->dropped_sample_count),
atomic_load(&context->loop_overrun_count), samples, sample_count);
if (!write_binary_packet(packet, packet_size)) {
atomic_fetch_add(&context->dropped_sample_count, sample_count);
}
write_binary_packet_until_sent(packet, packet_size);
++sample_packet_count;
}
}
@@ -232,8 +253,12 @@ static esp_err_t init_i2c(i2c_master_bus_handle_t *bus)
void app_main(void)
{
// Flush startup text by line; binary frames are flushed explicitly.
setvbuf(stdout, NULL, _IOLBF, 0);
// Keep the byte stream unbuffered so a write result describes the complete
// packet rather than bytes still retained inside stdio.
if (setvbuf(stdout, NULL, _IONBF, 0) != 0) {
ESP_LOGE(TAG, "failed to configure unbuffered telemetry output");
return;
}
ESP_LOGI(TAG, "Trikke motion telemetry prototype v0");
ESP_LOGI(TAG, "I2C: SDA=GPIO%d, SCL=GPIO%d, clock=%d Hz",