Bot TutorialsGrok BotChatGPTClaudeGeminiCopilotPerplexityMore botsGuidesArticlesPapers
Topics
CookingHome automationFitnessCreative writingMusic productionCodingIndustrialPros (Professional track)
Search

Latest › Build a Drone with a Bot: Software and Hardware Series

Your First Bot-Written MAVLink Script: Reading Telemetry with pymavlink

Deep diveBy Bot Tutorials · Published Nov 4, 2026 · Facts last verified Nov 1, 2026

Next in this series: Tomorrow: Power and Battery Sizing Sanity Checks, with a Bot Doing the Arithmetic · The day after: Reviewing Bot-Written Flight Code: A Cheat Sheet with a MAVSDK Example · Series hub

Prompt a bot for a read-only pymavlink script, review every line and run it against ArduPilot SITL. Includes the prompt and a syntax-checked script.

Every ArduPilot and PX4 aircraft speaks MAVLink, a compact message protocol for telemetry and commands. Ground stations use it, companion computers use it, and so will your scripts. In this deep dive we'll ask a bot to write a first MAVLink script, then review it the way you should review all bot-written flight code: line by line, against the docs, in simulation.

We start read-only on purpose. A script that only listens can't arm motors or change modes, so mistakes cost nothing but a confusing printout.

The library: pymavlink

The MAVLink developer guide documents pymavlink, the Python library generated from MAVLink's message definitions. Its mavutil module handles connections, and mavutil.mavlink_connection() accepts strings like udpin:127.0.0.1:14550, which is where ArduPilot SITL's MAVProxy forwards telemetry, as we set up in Part 1.

The prompt

Here's the prompt we used. Notice how much of it is constraints:

Write a Python script using pymavlink that connects to ArduPilot SITL at udpin:127.0.0.1:14550, waits for a heartbeat, then prints the flight mode, altitude above home in meters, and battery voltage for about 20 messages. Requirements: read-only (send no commands, no arming, no mode changes); handle timeouts; comment every unit conversion and cite which MAVLink message field it comes from; use only fields that exist in the MAVLink common or ardupilotmega message definitions. If you're unsure a field exists, say so instead of guessing.

The constraints do three jobs. Read-only removes risk. Cite the field makes the bot show its working. Say so if unsure gives it permission to admit gaps instead of inventing a field name, which is one of the most common bot mistakes in MAVLink code.

The script

This is a cleaned-up version of what came back. Testing status: we syntax-checked it with Python 3.13 and confirmed that relative_alt and voltage_battery exist in pymavlink 2.4.50's ardupilotmega dialect. We have not run it against SITL for this article, so run it in your simulator first.

"""Read-only telemetry monitor for ArduPilot SITL (does not arm or move anything)."""
from pymavlink import mavutil

# SITL + MAVProxy forwards MAVLink to UDP 14550 on the same machine by default.
master = mavutil.mavlink_connection("udpin:127.0.0.1:14550")
master.wait_heartbeat(timeout=30)
print(f"Heartbeat from system {master.target_system}, component {master.target_component}")

wanted = ["HEARTBEAT", "GLOBAL_POSITION_INT", "SYS_STATUS"]
for _ in range(20):
    msg = master.recv_match(type=wanted, blocking=True, timeout=5)
    if msg is None:
        print("No message in 5 s - check the connection string and that SITL is running")
        break
    if msg.get_type() == "GLOBAL_POSITION_INT":
        print(f"alt above home: {msg.relative_alt / 1000:.1f} m")
    elif msg.get_type() == "SYS_STATUS":
        print(f"battery: {msg.voltage_battery / 1000:.2f} V, remaining {msg.battery_remaining}%")
    else:
        print("mode:", mavutil.mode_string_v10(msg))

Line-by-line review

Here's how to review this, and any bot-written MAVLink code.

The connection string. udpin: means listen on that port, which matches SITL's MAVProxy sending to 14550. If your ground station is already bound to 14550 on the same machine, the two will clash. Ask MAVProxy to add a second output, such as 14551 (the docs show listing outputs), and point the script there.

wait_heartbeat(timeout=30). Nothing works until a heartbeat arrives, because it tells pymavlink which system and component it's talking to. A timeout stops the script hanging forever. Bots often leave the timeout out.

recv_match(type=[...], blocking=True, timeout=5). This filters the stream to the messages we care about. Without a type filter you'd get dozens of message types per second.

Unit conversions. These are where bots slip most often, so check each one against the MAVLink message definitions:

mode_string_v10(msg). This turns a heartbeat's mode fields into a readable name like GUIDED or LOITER for ArduPilot vehicles.

Run it in SITL

  1. Start SITL as in Part 1.
  2. Run the script. You should see the mode and an altitude near 0 m while the drone is on the ground.
  3. In MAVProxy, take off in GUIDED mode by hand, and watch the altitude climb in your script's output.
  4. Land. Now break things on purpose: stop SITL mid-run and confirm the timeout message prints instead of the script hanging.

Breaking things on purpose is the most useful habit in flight software. If you can't trigger a failure in simulation, you don't know how your code behaves when it happens for real.

Asking the bot to extend it, safely

Once the read-only version works, natural next steps are logging to CSV or showing GPS satellite counts. Keep the same discipline:

Common bot mistakes we've seen in MAVLink code

Each one is easy to catch if you ask, for every line, "Which doc says this?"

Safety and legal: read this before anything leaves the ground

This series is about hobby, camera and learning drones only. A bot is a helper, not an authority, and everything it says about flight has to be checked.

Outside the US, check your national aviation authority's rules. They may be different.

FAQ

What is pymavlink?

It's the Python implementation of the MAVLink protocol, generated by the mavgen tool and documented in the MAVLink developer guide. It lets a script send and receive MAVLink messages.

Why start with a read-only script?

A script that only listens can't arm or move anything, so it's the safest way to learn the message flow before writing anything that sends commands.

Was the example tested?

It was syntax-checked and its message field names were checked against pymavlink 2.4.50's ArduPilot dialect. We haven't run it against SITL for this article, so run it in your simulator before relying on it.

Sources

  1. MAVLink Guide: Using pymavlink (mavgen Python)
  2. ArduPilot Dev Docs: Using SITL for ArduPilot Testing
  3. ArduPilot Dev Docs: MAVLink Commands
  4. ArduPilot Copter: Complete Parameter List

Video outline (coming)

  1. What MAVLink is and why scripts talk to it
  2. The prompt: constraints that make bot code safer
  3. Reading the script line by line
  4. Running it against SITL
  5. Unit conversions bots often get wrong
  6. Extending it safely
  7. Recap

The narrated video for this piece is in production; this outline is what it will cover.

Next in this series: Tomorrow: Power and Battery Sizing Sanity Checks, with a Bot Doing the Arithmetic · The day after: Reviewing Bot-Written Flight Code: A Cheat Sheet with a MAVSDK Example · Series hub

More fresh reading

Bot advice can be wrong. We cite official docs; check them yourself before acting, especially for anything involving money, safety or flight.