Skip to main content

pamoja-mavlink

MAVLink v1 and v2 framing, signing, named message fields, and the mission, command, and offboard protocols. One capability of pamoja, one memory-safe Rust core with bindings for TypeScript, Python, and C#.

API reference read the guide documentation

Install

pip install pamoja-mavlink
from pamoja import mavlink

This pulls in pamoja-native, the compiled engine. pip install pamoja is the whole framework in one package.

Example

The script the test suite runs, spliced here as it ran.

From bindings/python/guides/mavlink.py:

from pamoja.mavlink import (
    CommandProtocol,
    MavlinkHeader,
    MavlinkMessage,
    MavlinkParser,
    from_dict,
    message,
    schema_for,
)

VEHICLE = 1
AUTOPILOT = 1
STATION = 255

# The values the MAVLink common dialect gives these fields.
MAV_TYPE_GCS = 6
MAV_TYPE_QUADROTOR = 2
MAV_AUTOPILOT_INVALID = 8
MAV_AUTOPILOT_ARDUPILOTMEGA = 3
MAV_STATE_ACTIVE = 4
MAV_STATE_STANDBY = 3
MAV_CMD_COMPONENT_ARM_DISARM = 400
MAV_CMD_NAV_TAKEOFF = 22
MAV_RESULT_ACCEPTED = 0

# Every MAVLink node broadcasts a heartbeat to say what it is and that it is alive. The
# fields are set by name rather than by writing the payload out byte by byte.
announce = message("HEARTBEAT")
announce.set("type", MAV_TYPE_GCS)
announce.set("autopilot", MAV_AUTOPILOT_INVALID)
announce.set("system_status", MAV_STATE_ACTIVE)
announce.set("mavlink_version", 3)
sent = announce.to_frame(MavlinkHeader(STATION, 190, 0))
print(f"sent      HEARTBEAT in {len(sent.bytes)} bytes")

# The vehicle answers with its own heartbeat. This copy arrives after some bytes that were
# already on the wire, and after a copy with one bit flipped in flight.
vehicle_shape = schema_for("HEARTBEAT")
vehicle = from_dict(
    vehicle_shape,
    {
        "type": MAV_TYPE_QUADROTOR,
        "autopilot": MAV_AUTOPILOT_ARDUPILOTMEGA,
        "system_status": MAV_STATE_STANDBY,
        "mavlink_version": 3,
    },
)
good = vehicle.to_frame(MavlinkHeader(VEHICLE, AUTOPILOT, 0))
garbled = bytearray(good.bytes)
garbled[-1] ^= 0xFF
delivered = b"???" + bytes(garbled) + good.bytes

# The parser skips whatever does not start a frame and drops one whose checksum fails, so
# the frame it hands back is the good copy rather than the garbled one.
parser = MavlinkParser()
received = parser.push(delivered)[0]
heard = MavlinkMessage.decode(vehicle_shape, received.payload)
print(f"heard     a type-{heard.get_int('type')} vehicle in state {heard.get_int('system_status')}")

# Arming it is a command, not a message a sender fires and forgets: the vehicle has to
# answer, and the sender keeps asking until it does. The protocol numbers each resend,
# which is how a vehicle tells a retry from a second, deliberate command.
arming = CommandProtocol(MAV_CMD_COMPONENT_ARM_DISARM, 3)
command_shape = schema_for("COMMAND_LONG")
arm = from_dict(
    command_shape,
    {
        "param1": 1.0,  # 1 arms, 0 disarms
        "target_system": VEHICLE,
        "target_component": AUTOPILOT,
        "command": arming.command,
        "confirmation": arming.confirmation,
    },
)
arm.to_frame(MavlinkHeader(STATION, 190, 1))
print(f"sent      arm request, confirmation {arming.confirmation}")

# Nothing comes back in time, so it goes again with the next confirmation number.
resend = arming.on_timeout()
print(f"silence, resending with confirmation {resend}")

# An acknowledgement names the command it answers, so one for a different command is not
# this exchange finishing.
ack_shape = schema_for("COMMAND_ACK")


def acknowledgement(command: int) -> object:
    built = from_dict(ack_shape, {"command": command, "result": MAV_RESULT_ACCEPTED})
    return built.to_frame(MavlinkHeader(VEHICLE, AUTOPILOT, 0))


stray = arming.on_frame(acknowledgement(MAV_CMD_NAV_TAKEOFF))
print(f"an ack for another command: {stray.kind}")

outcome = arming.on_frame(acknowledgement(MAV_CMD_COMPONENT_ARM_DISARM))
if outcome.kind == "final" and outcome.value == MAV_RESULT_ACCEPTED:
    print("armed     the vehicle is ready")
else:
    print(f"the vehicle answered {outcome.kind} {outcome.value}")

The same capability in every language

Language Package Reference
Rust pamoja-mavlink reference, docs.rs, install
TypeScript @pamoja/mavlink reference, install
Python pamoja-mavlink reference, install
C# Pamoja.Mavlink reference, install

Documentation

License

MIT

Release files for pamoja-mavlink 0.1.17

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pamoja-mavlink 0.1.17
File Size Uploaded
pamoja_mavlink-0.1.17.tar.gz 8.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pamoja-mavlink 0.1.17
File Interpreter ABI Platform
pamoja_mavlink-0.1.17-py3-none-any.whl Python 3 none any Details

Total release size: 17.2 kB

Release history Release notifications | RSS feed

0.2.0

2 release files

0.1.18

2 release files

This release

0.1.17 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page