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
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:
GLOBAL_POSITION_INT.relative_altis in millimeters, so dividing by 1000 gives meters.SYS_STATUS.voltage_batteryis in millivolts, so dividing by 1000 gives volts.battery_remainingis a percentage, and the MAVLink definition uses -1 when it's unknown. Handle that case if you use it for anything.
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
- Start SITL as in Part 1.
- Run the script. You should see the mode and an altitude near 0 m while the drone is on the ground.
- In MAVProxy, take off in GUIDED mode by hand, and watch the altitude climb in your script's output.
- 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:
- One change per prompt. Ask for a diff, not a full rewrite, so you can review what changed.
- Ask for the source of each new field, with its units.
- Keep sending commands separate. When you're ready to send commands such as arming or mode changes, put them in a separate script that only ever points at SITL, with a big comment at the top. We cover that with MAVSDK and a review checklist in the code-review piece.
Common bot mistakes we've seen in MAVLink code
- Made-up message field names that look plausible.
- Mixing up
udpinandudpout(orudpandudpoutin older examples). - Forgetting the millimeter and millivolt scaling.
- Using deprecated commands. Some older examples use commands that the current MAVLink and ArduPilot docs have replaced, so check ArduPilot's MAVLink Commands page.
- No timeouts, so the script hangs silently.
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.
- Simulate before you fly. Run every script and every parameter change in a simulator (ArduPilot SITL, or PX4 SITL with Gazebo) before it goes near a real aircraft. Bot-written code that has never run in simulation does not go on a vehicle.
- Check bot advice against official sources. Compare every answer with the ArduPilot, PX4, MAVLink or Betaflight documentation and with the manufacturer's spec sheet for each part. If the bot and the docs disagree, the docs win.
- Props off on the bench. Take the propellers off whenever the battery is connected for setup, motor tests or firmware work.
- Keep failsafes and geofences on. Don't let a bot talk you into disabling radio, battery or GCS failsafes, or into getting around a geofence or airspace restriction.
- Follow the FAA rules in the US. The FAA requires registration for drones of 250 grams (0.55 lb) or more, and for any drone flown under Part 107. Recreational flyers must pass the free TRUST test and carry proof of it. Registered drones need Remote ID, either built in, through a broadcast module, or by flying at an FAA-Recognized Identification Area. Commercial flights need a Part 107 certificate. Stay away from airports and controlled airspace unless you have authorization, keep the drone in sight, and don't fly over people.
- Treat LiPo batteries with respect. Charge on a fireproof surface with a balance charger, never leave a charging pack unattended, and retire swollen or damaged packs following the manufacturer's instructions.
- Never fly untested code near people. Test first flights in an open, legal area with nobody nearby.
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
- MAVLink Guide: Using pymavlink (mavgen Python)
- ArduPilot Dev Docs: Using SITL for ArduPilot Testing
- ArduPilot Dev Docs: MAVLink Commands
- ArduPilot Copter: Complete Parameter List
Video outline (coming)
- What MAVLink is and why scripts talk to it
- The prompt: constraints that make bot code safer
- Reading the script line by line
- Running it against SITL
- Unit conversions bots often get wrong
- Extending it safely
- Recap
The narrated video for this piece is in production; this outline is what it will cover.
More fresh reading
- Bring Back RSS: Build a Calm News Feed With a Bot's Help (Guide, Nov 4, 2026)
- Review Your Training Code with a Bot: Checklist (Checklist, Nov 3, 2026)
- Read ML Error Messages and Profile Code: Cheat Sheet (Cheat sheet, Nov 3, 2026)
- Export a Model to ONNX and Run It: A Deep Dive (Deep dive, Nov 3, 2026)
Bot advice can be wrong. We cite official docs; check them yourself before acting, especially for anything involving money, safety or flight.