Skip to content

Comments

mavlink: unified TX architecture - move all service sends to TX thread#26512

Draft
dakejahl wants to merge 2 commits intomainfrom
jake/mavlink_rework
Draft

mavlink: unified TX architecture - move all service sends to TX thread#26512
dakejahl wants to merge 2 commits intomainfrom
jake/mavlink_rework

Conversation

@dakejahl
Copy link
Contributor

Summary

  • All MAVLink service responses (parameters, FTP, missions, log handler, timesync) now send through a TX queue instead of directly from the RX thread
  • The RX thread only receives and dispatches; all sends go through the TX thread with unified bandwidth management
  • Services and streams share the MAV_RATE budget — streams automatically back off when service traffic is active

Problem

The RX thread (MavlinkReceiver) both received messages AND sent service responses, bypassing MAV_RATE bandwidth management entirely. This caused uncontrolled link bursting that breaks low-bandwidth links with hard throughput limits (e.g. telemetry radios).

Changes

New: MavlinkTxQueue — thread-safe queue wrapping VariableLengthRingbuffer. Services encode messages via mavlink_msg_*_encode_chan() + enqueue_tx() instead of calling mavlink_msg_*_send_struct() directly. The TX thread drains the queue under a per-cycle byte budget.

Converted services:

  • MavlinkParametersManagersend_param(), send_error(), hash PARAM_VALUE, UAVCAN forwarding
  • MavlinkFTP_reply(), resend path, burst reads
  • MavlinkMissionManager — all 6 send helpers (ack, current, count, item, request, item_reached)
  • MavlinkLogHandlersend_log_entry(), state_sending_data()
  • MavlinkReceiver — PING response, MESSAGE_INTERVAL
  • MavlinkTimesync — TIMESYNC response

Thread safety: Per-service pthread_mutex_t protects shared state between handle_message() (RX thread) and send() (TX thread).

Bandwidth accounting: update_rate_mult() now subtracts measured service traffic (EMA of bytes/sec) from available stream bandwidth, replacing the old sending_parameters() hack.

Test plan

  • SITL: connect QGC, verify streams, parameter download, mission upload/download, log list/download all work
  • Set MAV_1_RATE low (e.g. 1200 B/s), trigger param download — streams should back off, heartbeat continues, no TX overruns
  • Verify mavlink status shows reasonable rate_mult values during service activity

@github-actions
Copy link

github-actions bot commented Feb 17, 2026

🔎 FLASH Analysis

px4_fmu-v5x [Total VM Diff: 1488 byte (0.07 %)]
    FILE SIZE        VM SIZE    
--------------  -------------- 
+0.1% +1.41Ki  +0.1% +1.41Ki    .text
 -99.7%    +450 -99.7%    +450    [57 Others]
   +13%    +352   +13%    +352    Mavlink::task_main()
  [NEW]    +352  [NEW]    +352    mavlink_finalize_message_chan.isra.0
  [NEW]    +272  [NEW]    +272    MavlinkTxQueue::push()
   +52%    +260   +52%    +260    MavlinkMissionManager::send_mission_item()
  [NEW]    +100  [NEW]    +100    mavlink_msg_file_transfer_protocol_encode_chan.isra.0
  [NEW]     +92  [NEW]     +92    MavlinkTxQueue::drain()
   +51%     +86   +51%     +86    MavlinkLogHandler::state_sending_data()
  [NEW]     +66  [NEW]     +66    MavlinkReceiver::service_send_cycle()
  +4.9%     +64  +4.9%     +64    MavlinkParametersManager::handle_message()
  [NEW]     +60  [NEW]     +60    CSWTCH.1938
   +14%     +52   +14%     +52    MavlinkReceiver::handle_message_ping()
   +18%     +52   +18%     +52    MavlinkTimesync::handle_message()
   +93%     +50   +93%     +50    MavlinkLogHandler::send_log_entry()
   +60%     +48   +60%     +48    MavlinkReceiver::get_message_interval()
  [DEL]     -44  [DEL]     -44    CSWTCH.2711
  [DEL]     -60  [DEL]     -60    CSWTCH.1931
 -29.5%     -82 -29.5%     -82    MavlinkParametersManager::send_error()
  -6.0%    -144  -6.0%    -144    MavlinkReceiver::run()
  [DEL]    -242  [DEL]    -242    _mavlink_resend_uart
  [DEL]    -344  [DEL]    -344    mavlink_finalize_message_buffer.isra.0
+0.3%     +48  +0.3%     +48    .ramfunc
   +13%     +60   +13%     +60    Mavlink::update_rate_mult()
  -1.2%      -4  -1.2%      -4    Ekf::measurementUpdate()
  [DEL]      -8  [DEL]      -8    ___ZNK3px46atomicIbE4loadEv_veneer
+0.0%    +127  [ = ]       0    .debug_abbrev
+0.0%     +32  [ = ]       0    .debug_aranges
+0.0%    +164  [ = ]       0    .debug_frame
+0.0% +6.94Ki  [ = ]       0    .debug_info
+0.0% +1.54Ki  [ = ]       0    .debug_line
  +500%      +5  [ = ]       0    [Unmapped]
  +0.0% +1.54Ki  [ = ]       0    [section .debug_line]
+0.0% +1.77Ki  [ = ]       0    .debug_loclists
+0.0%    +173  [ = ]       0    .debug_rnglists
 -66.7%      -2  [ = ]       0    [Unmapped]
  +0.0%    +175  [ = ]       0    [section .debug_rnglists]
+0.0%    +887  [ = ]       0    .debug_str
+0.0%    +108  [ = ]       0    .strtab
  [DEL]     -12  [ = ]       0    CSWTCH.1931
  [NEW]     +12  [ = ]       0    CSWTCH.1938
  [DEL]     -12  [ = ]       0    CSWTCH.2711
  [NEW]     +12  [ = ]       0    CSWTCH.2738
  [DEL]     -24  [ = ]       0    CSWTCH.3459
  [DEL]     -24  [ = ]       0    CSWTCH.3460
  [NEW]     +24  [ = ]       0    CSWTCH.3485
  [NEW]     +24  [ = ]       0    CSWTCH.3486
  [NEW]     +53  [ = ]       0    Mavlink::enqueue_tx()
 -52.5%     -74  [ = ]       0    MavlinkParametersManager::send_error()
  [NEW]     +43  [ = ]       0    MavlinkReceiver::service_send_cycle()
  [NEW]     +37  [ = ]       0    MavlinkTxQueue::drain()
  [NEW]     +54  [ = ]       0    MavlinkTxQueue::push()
   +53%     +55  [ = ]       0    [3 Others]
  [DEL]     -35  [ = ]       0    ___ZNK3px46atomicIbE4loadEv_veneer
 -35.6%     -16  [ = ]       0    __nxsched_add_blocked_veneer
   +50%     +16  [ = ]       0    __nxsched_add_readytorun_veneer
  [NEW]     +18  [ = ]       0    _mav_trim_payload
  [DEL]     -21  [ = ]       0    _mavlink_resend_uart
  [NEW]     +17  [ = ]       0    mav_array_memcpy
  [DEL]     -39  [ = ]       0    mavlink_finalize_message_buffer.isra.0
+0.0%    +224  [ = ]       0    .symtab
  [DEL]     -32  [ = ]       0    CSWTCH.1931
  [NEW]     +32  [ = ]       0    CSWTCH.1938
  [DEL]     -32  [ = ]       0    CSWTCH.2711
  [NEW]     +32  [ = ]       0    CSWTCH.2738
  [DEL]     -48  [ = ]       0    CSWTCH.3459
  [DEL]     -48  [ = ]       0    CSWTCH.3460
  [NEW]     +48  [ = ]       0    CSWTCH.3485
  [NEW]     +48  [ = ]       0    CSWTCH.3486
   +50%     +32  [ = ]       0    Mavlink::Mavlink()
  [NEW]     +32  [ = ]       0    Mavlink::enqueue_tx()
  +100%     +16  [ = ]       0    MavlinkFTP::_ensure_buffers_exist()
   +50%     +16  [ = ]       0    MavlinkFTP::_reply()
 -25.0%     -16  [ = ]       0    MavlinkFTP::~MavlinkFTP()
 -25.0%     -16  [ = ]       0    MavlinkLogHandler::create_log_list_file()
  +100%     +16  [ = ]       0    MavlinkLogHandler::handle_log_request_list()
  +200%     +32  [ = ]       0    MavlinkLogHandler::send_log_entry()
   +50%     +16  [ = ]       0    MavlinkMissionManager::send_mission_ack()
   +50%     +16  [ = ]       0    MavlinkMissionManager::send_mission_count()
   +50%     +16  [ = ]       0    MavlinkMissionManager::send_mission_item_reached()
 -50.0%     -16  [ = ]       0    MavlinkParametersManager::enque_uavcan_request()
 -91.1%     +80  [ = ]       0    [28 Others]
-14.4% -1.45Ki  [ = ]       0    [Unmapped]
+0.0% +11.9Ki  +0.1% +1.45Ki    TOTAL

px4_fmu-v6x [Total VM Diff: 1520 byte (0.08 %)]
    FILE SIZE        VM SIZE    
--------------  -------------- 
+0.1% +1.48Ki  +0.1% +1.48Ki    .text
 -99.8%    +426 -99.8%    +426    [58 Others]
   +13%    +352   +13%    +352    Mavlink::task_main()
  [NEW]    +352  [NEW]    +352    mavlink_finalize_message_chan.isra.0
  [NEW]    +272  [NEW]    +272    MavlinkTxQueue::push()
   +52%    +260   +52%    +260    MavlinkMissionManager::send_mission_item()
  [NEW]    +100  [NEW]    +100    mavlink_msg_file_transfer_protocol_encode_chan.isra.0
  [NEW]     +92  [NEW]     +92    MavlinkTxQueue::drain()
   +51%     +86   +51%     +86    MavlinkLogHandler::state_sending_data()
  [NEW]     +66  [NEW]     +66    MavlinkReceiver::service_send_cycle()
  +4.9%     +64  +4.9%     +64    MavlinkParametersManager::handle_message()
  [NEW]     +60  [NEW]     +60    CSWTCH.1938
   +13%     +60   +13%     +60    Mavlink::update_rate_mult()
   +14%     +52   +14%     +52    MavlinkReceiver::handle_message_ping()
   +18%     +52   +18%     +52    MavlinkTimesync::handle_message()
   +93%     +50   +93%     +50    MavlinkLogHandler::send_log_entry()
   +60%     +48   +60%     +48    MavlinkReceiver::get_message_interval()
  [DEL]     -60  [DEL]     -60    CSWTCH.1931
 -29.5%     -82 -29.5%     -82    MavlinkParametersManager::send_error()
  -6.0%    -144  -6.0%    -144    MavlinkReceiver::run()
  [DEL]    -242  [DEL]    -242    _mavlink_resend_uart
  [DEL]    -344  [DEL]    -344    mavlink_finalize_message_buffer.isra.0
+0.0%    +127  [ = ]       0    .debug_abbrev
+0.0%     +32  [ = ]       0    .debug_aranges
+0.0%    +164  [ = ]       0    .debug_frame
+0.0% +6.94Ki  [ = ]       0    .debug_info
+0.0% +1.53Ki  [ = ]       0    .debug_line
  [DEL]      -3  [ = ]       0    [Unmapped]
  +0.0% +1.54Ki  [ = ]       0    [section .debug_line]
+0.1% +1.77Ki  [ = ]       0    .debug_loclists
+0.0%    +177  [ = ]       0    .debug_rnglists
  [NEW]      +2  [ = ]       0    [Unmapped]
  +0.0%    +175  [ = ]       0    [section .debug_rnglists]
+0.0%    +887  [ = ]       0    .debug_str
+0.4%      +1  [ = ]       0    .shstrtab
+0.0%    +143  [ = ]       0    .strtab
  [DEL]     -12  [ = ]       0    CSWTCH.1931
  [NEW]     +12  [ = ]       0    CSWTCH.1938
  [DEL]     -12  [ = ]       0    CSWTCH.2711
  [NEW]     +12  [ = ]       0    CSWTCH.2738
  [DEL]     -24  [ = ]       0    CSWTCH.3459
  [DEL]     -24  [ = ]       0    CSWTCH.3460
  [NEW]     +24  [ = ]       0    CSWTCH.3485
  [NEW]     +24  [ = ]       0    CSWTCH.3486
  [NEW]     +53  [ = ]       0    Mavlink::enqueue_tx()
 -52.5%     -74  [ = ]       0    MavlinkParametersManager::send_error()
  [NEW]     +43  [ = ]       0    MavlinkReceiver::service_send_cycle()
  [NEW]     +37  [ = ]       0    MavlinkTxQueue::drain()
  [NEW]     +54  [ = ]       0    MavlinkTxQueue::push()
  [NEW]     +18  [ = ]       0    _mav_trim_payload
  [DEL]     -21  [ = ]       0    _mavlink_resend_uart
  [NEW]     +17  [ = ]       0    mav_array_memcpy
  [DEL]     -39  [ = ]       0    mavlink_finalize_message_buffer.isra.0
  [NEW]     +37  [ = ]       0    mavlink_finalize_message_chan.isra.0
  [NEW]     +54  [ = ]       0    mavlink_msg_file_transfer_protocol_encode_chan.isra.0
  [DEL]     -36  [ = ]       0    mavlink_msg_param_value_send_struct
+0.0%    +272  [ = ]       0    .symtab
  [DEL]     -32  [ = ]       0    CSWTCH.1931
  [NEW]     +32  [ = ]       0    CSWTCH.1938
  [DEL]     -32  [ = ]       0    CSWTCH.2711
  [NEW]     +32  [ = ]       0    CSWTCH.2738
  [DEL]     -48  [ = ]       0    CSWTCH.3459
  [DEL]     -48  [ = ]       0    CSWTCH.3460
  [NEW]     +48  [ = ]       0    CSWTCH.3485
  [NEW]     +48  [ = ]       0    CSWTCH.3486
   +50%     +32  [ = ]       0    Mavlink::Mavlink()
  [NEW]     +32  [ = ]       0    Mavlink::enqueue_tx()
  +100%     +16  [ = ]       0    MavlinkFTP::_ensure_buffers_exist()
   +50%     +16  [ = ]       0    MavlinkFTP::_reply()
 -25.0%     -16  [ = ]       0    MavlinkFTP::~MavlinkFTP()
 -25.0%     -16  [ = ]       0    MavlinkLogHandler::create_log_list_file()
  +100%     +16  [ = ]       0    MavlinkLogHandler::handle_log_request_list()
  +200%     +32  [ = ]       0    MavlinkLogHandler::send_log_entry()
   +50%     +16  [ = ]       0    MavlinkMissionManager::send_mission_ack()
   +50%     +16  [ = ]       0    MavlinkMissionManager::send_mission_count()
   +50%     +16  [ = ]       0    MavlinkMissionManager::send_mission_item_reached()
 -50.0%     -16  [ = ]       0    MavlinkParametersManager::enque_uavcan_request()
 -82.2%    +128  [ = ]       0    [25 Others]
 +65% +2.52Ki  [ = ]       0    [Unmapped]
+0.0% +16.0Ki  +0.1% +1.48Ki    TOTAL

Updated: 2026-02-17T23:15:35

@hamishwillee
Copy link
Contributor

Docs should be added somewhere in https://docs.px4.io/main/en/mavlink/ explaining the pattern. So obviously in https://docs.px4.io/main/en/mavlink/streaming_messages#define-the-streaming-class but also "How to TX a mavlink message" section.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants