pamoja for TypeScript - v0.1.17
    Preparing search index...

    Module @pamoja/mavlink

    Ergonomic facade over the generated MAVLink binding.

    MAVLink is the language drones speak: PX4 and ArduPilot autopilots and MAVSDK ground stations all exchange MAVLink frames, so talking to a vehicle means putting exactly the right bytes on the wire and trusting the bytes that come back. This is that byte layer: v1 and v2 frames, the CRC-16/MCRF4XX checksum every frame carries, the per-message CRC_EXTRA seed that catches a frame whose shape does not match, and MAVLink 2 signing.

    Nothing here is limited to the messages this build happens to know. The common dialect's seeds are built in, and Dialect carries any others, derived from a message definition the way the specification does.

    Above the bytes sits the shape: MessageSchema names a message's fields, so a MavlinkMessage is filled in and read back by name rather than by byte offset, and MessageSchemaBuilder describes a message this build has never heard of.

    Above the messages sit the exchanges: MissionSender and MissionReceiver carry a plan between a station and a vehicle, CommandProtocol matches a command to its acknowledgement and counts retries, and offboard builds setpoints. Each takes a frame off the link and hands back the frame to send, with no IO or timers of its own.

    @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

    npm install @pamoja/mavlink
    

    This pulls in @pamoja/native, the compiled engine. npm install pamoja is the whole framework in one package.

    The test that runs in CI, spliced here as it ran.

    From bindings/node/guides/mavlink.ts:

    import {
    CommandProtocol,
    type MavlinkFrame,
    MavlinkMessage,
    MavlinkParser,
    fromObject,
    message,
    schemaFor,
    } from '@pamoja/mavlink'

    const VEHICLE = 1
    const AUTOPILOT = 1
    const STATION = 255

    // The values the MAVLink common dialect gives these fields.
    const MAV_TYPE_GCS = 6
    const MAV_TYPE_QUADROTOR = 2
    const MAV_AUTOPILOT_INVALID = 8
    const MAV_AUTOPILOT_ARDUPILOTMEGA = 3
    const MAV_STATE_ACTIVE = 4
    const MAV_STATE_STANDBY = 3
    const MAV_CMD_COMPONENT_ARM_DISARM = 400
    const MAV_CMD_NAV_TAKEOFF = 22
    const 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.
    const 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)
    const sent = announce.toFrame({ systemId: STATION, componentId: 190, sequence: 0 })
    console.log(`sent HEARTBEAT in ${sent.bytes.length} 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.
    const heartbeatShape = schemaFor('HEARTBEAT')
    const vehicle = fromObject(heartbeatShape, {
    type: MAV_TYPE_QUADROTOR,
    autopilot: MAV_AUTOPILOT_ARDUPILOTMEGA,
    system_status: MAV_STATE_STANDBY,
    mavlink_version: 3,
    })
    const good = vehicle.toFrame({ systemId: VEHICLE, componentId: AUTOPILOT, sequence: 0 })
    const garbled = Buffer.from(good.bytes)
    garbled[garbled.length - 1] ^= 0xff
    const delivered = Buffer.concat([Buffer.from('???'), 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.
    const parser = new MavlinkParser()
    const received = parser.push(delivered)[0]!
    const heard = MavlinkMessage.decode(heartbeatShape, received.payload)
    console.log(`heard a type-${heard.get('type')} vehicle in state ${heard.get('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.
    const arming = new CommandProtocol(MAV_CMD_COMPONENT_ARM_DISARM, 3)
    const commandShape = schemaFor('COMMAND_LONG')
    const arm = fromObject(commandShape, {
    param1: 1, // 1 arms, 0 disarms
    target_system: VEHICLE,
    target_component: AUTOPILOT,
    command: arming.command,
    confirmation: arming.confirmation,
    })
    arm.toFrame({ systemId: STATION, componentId: 190, sequence: 1 })
    console.log(`sent arm request, confirmation ${arming.confirmation}`)

    // Nothing comes back in time, so it goes again with the next confirmation number.
    const resend = arming.onTimeout()
    console.log(`silence, resending with confirmation ${resend}`)

    // An acknowledgement names the command it answers, so one for a different command is not
    // this exchange finishing.
    const ackShape = schemaFor('COMMAND_ACK')
    const acknowledgement = (command: number): MavlinkFrame =>
    fromObject(ackShape, { command, result: MAV_RESULT_ACCEPTED }).toFrame({
    systemId: VEHICLE,
    componentId: AUTOPILOT,
    sequence: 0,
    })

    const stray = arming.onFrame(acknowledgement(MAV_CMD_NAV_TAKEOFF))
    console.log(`an ack for another command: ${stray?.kind}`)

    const outcome = arming.onFrame(acknowledgement(MAV_CMD_COMPONENT_ARM_DISARM))
    if (outcome?.kind === 'final' && outcome.value === MAV_RESULT_ACCEPTED) {
    console.log('armed the vehicle is ready')
    } else {
    console.log(`the vehicle answered ${outcome?.kind} ${outcome?.value}`)
    }
    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

    MIT

    CommandProtocol
    Dialect
    MavlinkFrame
    MavlinkMessage
    MavlinkParser
    MavlinkSigner
    MavlinkVerifier
    MessageSchema
    MessageSchemaBuilder
    MissionReceiver
    MissionSender
    ReceiverStep
    SenderStep
    AckOutcome
    MavlinkField
    MavlinkFieldInfo
    MavlinkHeader
    MavlinkFields
    MavlinkFieldTypeValue
    MavlinkFieldValue
    MavlinkVersion
    DEFAULT_TIMESTAMP_WINDOW
    KEY_LEN
    MavlinkFieldType
    MavlinkTypeMask
    MavlinkVersion
    MAX_FRAME
    MAX_PAYLOAD
    MAX_RETRIES
    offboard
    SIGNATURE_LEN
    crc16
    frame
    fromObject
    knownCrcExtra
    knownMessages
    message
    messageCrcExtra
    schemaFor
    timestampFromUnixMicros
    timestampNow
    toObject