Skip to content

MAVLink Heartbeat

Goal

Send a MAVLink HEARTBEAT from a companion computer connected by serial to a Betaflight flight controller.

The heartbeat is not a command. It is a broadcast status message that says:

  • this MAVLink component exists
  • this is my system id and component id
  • this is what kind of component I am
  • this is my current high-level state

For a companion computer, the heartbeat should describe the companion computer, not the Betaflight flight controller. The flight controller should send its own heartbeat if it supports MAVLink on that port.

Topology

flowchart LR
    CC["Companion computer"]
    FCU["Betaflight FCU"]
    GCS["GCS / router / logger"]

    CC <-->|Serial MAVLink| FCU
    FCU <-->|Telemetry link| GCS

Use the same sysid as the vehicle if the companion computer is part of that drone, but use a different compid so it is a separate component inside the same system.

Example:

  • Betaflight FCU: sysid = 1, compid = 1
  • Companion computer: sysid = 1, compid = 191

191 is MAV_COMP_ID_ONBOARD_COMPUTER. It identifies a generic onboard computer component. Do not use compid = 1, because that is normally the autopilot component.

HEARTBEAT fields

The HEARTBEAT message id is 0. It has no target_system or target_component field, so it is broadcast on the link.

For a companion computer connected to a Betaflight FCU, use:

Field Value Why
type MAV_TYPE_ONBOARD_CONTROLLER (18) The sender is the companion computer, not the multirotor airframe.
autopilot MAV_AUTOPILOT_INVALID (8) The companion computer is not the flight controller/autopilot.
base_mode 0 The companion is not reporting vehicle flight mode, arming, manual, guided, or auto state.
custom_mode 0 No companion-specific custom mode is being reported.
system_status MAV_STATE_STANDBY (3) The companion process is running and available. Use a fault state if the process is not healthy.
mavlink_version auto-filled by the MAVLink library This field is generated by the protocol implementation.

If the companion computer is actively controlling something important, use MAV_STATE_ACTIVE (4) while it is active. If the companion process is broken, do not keep sending a normal heartbeat from an unrelated background thread; report a fault state or stop sending so other MAVLink peers can detect the failure.

Why not MAV_TYPE_QUADROTOR

MAV_TYPE_QUADROTOR describes a flight controller mounted on a quadrotor. The companion computer is a separate component, so it should identify itself as an onboard controller. This lets a GCS, router, or logger distinguish:

  • the FCU/autopilot component
  • the companion computer component
  • other possible components such as cameras, gimbals, GPS, or OSD
send_heartbeat.py
import time
from pymavlink import mavutil


SERIAL_DEVICE = "/dev/ttyUSB0"
BAUD = 115200

master = mavutil.mavlink_connection(
    SERIAL_DEVICE,
    baud=BAUD,
    source_system=1,
    source_component=mavutil.mavlink.MAV_COMP_ID_ONBOARD_COMPUTER,
)

while True:
    master.mav.heartbeat_send(
        mavutil.mavlink.MAV_TYPE_ONBOARD_CONTROLLER,
        mavutil.mavlink.MAV_AUTOPILOT_INVALID,
        0,
        0,
        mavutil.mavlink.MAV_STATE_STANDBY,
    )
    time.sleep(1.0)

Send it at about 1 Hz. MAVLink does not define one universal timeout for every link, but telemetry systems commonly consider a component disconnected after several missed heartbeats.

Betaflight note

Betaflight is usually configured through MSP, and its MAVLink support depends on the firmware version, target, serial-port function, and telemetry setup. The heartbeat above is still a valid MAVLink heartbeat from the companion computer, but the FCU must actually have a MAVLink-capable serial port if you expect it to parse or forward the message.

References