diff --git a/components/dataLink/DataLinkFrames.cpp b/components/dataLink/DataLinkFrames.cpp index e6d88cd..ef36679 100644 --- a/components/dataLink/DataLinkFrames.cpp +++ b/components/dataLink/DataLinkFrames.cpp @@ -151,7 +151,14 @@ esp_err_t DataLinkManager::complete_fragment(uint16_t board_id, uint16_t sequenc if (metadata.num_fragments_rx != metadata.fragments.size()){ return ESP_ERR_INVALID_STATE; } - uint16_t total_data_len = metadata.num_fragments_rx * MAX_GENERIC_DATA_LEN; //max data size with n fragments + // Compute the exact total size from actual fragment data lengths, not the + // theoretical maximum (num_fragments * MAX_GENERIC_DATA_LEN). The last fragment + // is almost always shorter, so over-allocating would leave garbage bytes at the + // end that consumers reading combined_data->size() would see. + uint16_t total_data_len = 0; + for (size_t i = 0; i < metadata.num_fragments_rx; i++){ + total_data_len += metadata.fragments[i].data_len; + } xSemaphoreGive(rx_fragment_mutex[channel]); auto combined_data = std::make_unique>(); @@ -181,6 +188,7 @@ esp_err_t DataLinkManager::complete_fragment(uint16_t board_id, uint16_t sequenc memcpy(&combined_data->data()[prev_index], metadata.fragments[i].data, metadata.fragments[i].data_len); prev_index += metadata.fragments[i].data_len; } + combined_data->resize(prev_index); // ensure vector size == actual data (no garbage tail bytes) xSemaphoreGive(rx_fragment_mutex[channel]); diff --git a/components/dataLink/DataLinkRIP.cpp b/components/dataLink/DataLinkRIP.cpp index 95114e4..a0f8ee1 100644 --- a/components/dataLink/DataLinkRIP.cpp +++ b/components/dataLink/DataLinkRIP.cpp @@ -175,9 +175,10 @@ esp_err_t DataLinkManager::rip_find_entry(uint8_t board_id, RIPRow** entry, bool } /** - * @brief Returns the associated RIP Table row by row number. Information returned is read only. + * @brief Returns a snapshot of the RIP table row by row number. Information returned is a safe copy taken under the lock. * - * @param entry + * @param entry Pointer to a caller-owned RIPRow that will be filled with a snapshot of the table row. + * The caller must NOT hold or release the internal row semaphore; it is managed here. * @param row_num * @return esp_err_t */ @@ -207,7 +208,12 @@ esp_err_t DataLinkManager::rip_get_row(RIPRow** entry, uint8_t row_num){ } } - *entry = &rip_table[row_num]; + static RIPRow row_snapshots[RIP_MAX_ROUTES]; + row_snapshots[row_num].info = rip_table[row_num].info; + row_snapshots[row_num].channel = rip_table[row_num].channel; + row_snapshots[row_num].ttl = rip_table[row_num].ttl; + row_snapshots[row_num].valid = rip_table[row_num].valid; + *entry = &row_snapshots[row_num]; xSemaphoreGive(rip_table[row_num].row_sem); diff --git a/components/rmt/RMTManager.cpp b/components/rmt/RMTManager.cpp index a801ed7..63ece89 100644 --- a/components/rmt/RMTManager.cpp +++ b/components/rmt/RMTManager.cpp @@ -30,9 +30,6 @@ esp_err_t RMTManager::init_tx_channel(){ esp_err_t res_tx = ESP_FAIL; memory_to_free = xQueueCreate(15, sizeof(uint8_t*)); - xTaskCreate(RMTManager::freeMemory, "RIPFreeMem", 4096, static_cast(memory_to_free), 5, NULL); - memory_to_free = xQueueCreate(15, sizeof(uint8_t*)); - xTaskCreate(RMTManager::freeMemory, "RIPFreeMem", 4096, static_cast(memory_to_free), 5, NULL); for (uint8_t i = 0; i < num_channels; i++){ @@ -414,7 +411,10 @@ esp_err_t RMTManager::send(uint8_t* data, size_t size, rmt_transmit_config_t* co esp_err_t res = rmt_transmit(this->channels[channel_num].tx_rmt_handle, this->channels[channel_num].encoder, new_data_to_send_buf.data, new_data_to_send_buf.length, config); if (res != ESP_OK){ - // printf("Failed to send %s\n", data); + // rmt_transmit() failed: pull our item back out of the queue so the + // TX-done callback won't see it, then free the memory exactly once. + TxBuffer discarded = {}; + xQueueReceive(channels[channel_num].tx_queue, &discarded, 0); vPortFree((void*)new_data_to_send_buf.data); ESP_LOGE(DEBUG_TAG, "Failed to send %s", data); return ESP_FAIL; @@ -593,7 +593,7 @@ esp_err_t RMTManager::receive(uint8_t* recv_buf, size_t size, size_t* output_siz // printf("duration0 %d level0 %d duration1 %d level1 %d\n", rx_data.received_symbols[i].duration0, rx_data.received_symbols[i].level0, rx_data.received_symbols[i].duration1, rx_data.received_symbols[i].level1); // } - int num = this->decode_symbols(rx_data.received_symbols, rx_data.num_symbols, channels[channel_num].decoded_recv_symbols, sizeof(channels[channel_num].decoded_recv_symbols)); + int num = this->decode_symbols(rx_data.received_symbols, rx_data.num_symbols, channels[channel_num].decoded_recv_symbols, RECEIVE_BUFFER_SIZE); if (num < 0){ return ESP_FAIL; } diff --git a/main/control/DCMotorActuator.cpp b/main/control/DCMotorActuator.cpp index 9edc7fd..1057177 100644 --- a/main/control/DCMotorActuator.cpp +++ b/main/control/DCMotorActuator.cpp @@ -62,8 +62,10 @@ DCMotorActuator::DCMotorActuator() { this->m_integral = 0; this->m_last_error = 0; - xTaskCreate(reinterpret_cast(pid_task), "pid_task", 3072, this, 1, - &this->m_pid_task); + // Pin the PID task to Core 1 so it doesn't compete with the RMT driver, + // which runs its ISR and internal tasks on Core 0. + xTaskCreatePinnedToCore(reinterpret_cast(pid_task), "pid_task", 3072, this, 1, + &this->m_pid_task, 1); } DCMotorActuator::~DCMotorActuator() {