{"id":19195400,"url":"https://github.com/ardupilot/node-mavlink","last_synced_at":"2025-10-25T05:50:01.654Z","repository":{"id":37771259,"uuid":"363728993","full_name":"ArduPilot/node-mavlink","owner":"ArduPilot","description":"This project is providing native TypeScript bindings and tools for sending and receiving MavLink messages over a verity of medium ","archived":false,"fork":false,"pushed_at":"2025-08-26T19:17:09.000Z","size":1219,"stargazers_count":88,"open_issues_count":0,"forks_count":32,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-09-28T06:01:50.190Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ArduPilot.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2021-05-02T19:05:05.000Z","updated_at":"2025-09-15T02:30:22.000Z","dependencies_parsed_at":"2024-01-31T17:04:35.980Z","dependency_job_id":"3449e25c-7a35-44d7-ab78-6157fa273d84","html_url":"https://github.com/ArduPilot/node-mavlink","commit_stats":null,"previous_names":["padcom/node-mavlink"],"tags_count":60,"template":false,"template_full_name":null,"purl":"pkg:github/ArduPilot/node-mavlink","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ArduPilot%2Fnode-mavlink","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ArduPilot%2Fnode-mavlink/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ArduPilot%2Fnode-mavlink/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ArduPilot%2Fnode-mavlink/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ArduPilot","download_url":"https://codeload.github.com/ArduPilot/node-mavlink/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ArduPilot%2Fnode-mavlink/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":280911393,"owners_count":26412209,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-25T02:00:06.499Z","response_time":81,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-09T12:09:49.673Z","updated_at":"2025-10-25T05:50:01.637Z","avatar_url":"https://github.com/ArduPilot.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003csup\u003eIf you're looking for the officially supported bindings for JavaScript see the [pymavlink](https://github.com/ArduPilot/pymavlink/tree/master/generator/javascript) project.\u003c/sup\u003e\n\n# Node.js MavLink library\n\nThis package is the implementation of serialization and parsing of MavLink messages for v1 and v2 protocols.\n\nIt consists partially of code generated from the XML documents in the original MavLink repository and a few pieces that define the parser and serializer.\n\n## Getting started\n\nIt is extremely easy to get started using this library. First you need to install the required packages. For demonstration purposes we'll be using the `serialport` package to read the data from serial port.\n\n### Reading messages\n\n```\n$ npm install --save node-mavlink serialport\n```\n\nOnce you've done it you can start using it. First you'll need a serial port that can parse messages one by one. Please note that since we're using ECMAScript modules the file name should end with `.mjs` extension (e.g. `test.mjs`)\n\n```javascript\nimport { SerialPort } from 'serialport'\nimport { MavLinkPacketSplitter, MavLinkPacketParser } from 'node-mavlink'\n\n// substitute /dev/ttyACM0 with your serial port!\nconst port = new SerialPort({ path: '/dev/ttyACM0', baudRate: 115200 })\n\n// constructing a reader that will emit each packet separately\nconst reader = port\n  .pipe(new MavLinkPacketSplitter())\n  .pipe(new MavLinkPacketParser())\n\nreader.on('data', packet =\u003e {\n  console.log(packet)\n})\n```\n\nThat's it! That is all it takes to read the raw data. But it doesn't end there - in fact this is only the beginning of what this library can do for you.\n\nEach message consists of multiple fields that contain specific data. Parsing the data is also very easy.\n\n```javascript\nimport {\n  MavLinkPacketRegistry,\n  minimal, common, ardupilotmega\n} from 'node-mavlink'\n\n// create a registry of mappings between a message id and a data class\nconst REGISTRY: MavLinkPacketRegistry = {\n  ...minimal.REGISTRY,\n  ...common.REGISTRY,\n  ...ardupilotmega.REGISTRY,\n}\n\nreader.on('data', packet =\u003e {\n  const clazz = REGISTRY[packet.header.msgid]\n  if (clazz) {\n    const data = packet.protocol.data(packet.payload, clazz)\n    console.log('Received packet:', data)\n  }\n})\n```\n\n### Sending messages\n\nSending messages is also very easy. One example that is very useful is to send the `REQUEST_PROTOCOL_VERSION` to switch to protocol version 2.\n\n```javascript\nimport { MavLinkProtocolV2, send } from 'node-mavlink'\n\n// Create an instance of of the `RequestProtocolVersionCommand`\n// class that will be the vessel for containing the command data.\n// Underneath the cover it uses CommandLong to convert the data.\n//\n// By convention the intermediate fields that are then serialized\n// are named with `_` (underscore) prefix and should not be used\n// directly. That doesn't mean you can't use them, but if there\n// is an equivalent Command class it is just a lot easier and every\n// parameter not only has a more descriptive names but also in-line\n// documentation.\nconst command = new common.RequestProtocolVersionCommand()\ncommand.confirmation = 1\n\nport.on('open', async () =\u003e {\n  // the port is open - we're ready to send data\n  await send(port, command, new MavLinkProtocolV2())\n})\n```\n\n## Interacting with other communication mediums\n\nThe splitter and parser work with generic streams. Of course the obvious choice for many use cases will be a serial port but the support doesn't end there.\n\nThere are options for streams working over network (TCP or UDP), GSM network - pretty much anything that sends and receives data over Node.js `Stream`s.\n\nHere's an example for connecting to telemetry via TCP (for example using [esp-link](https://github.com/jeelabs/esp-link) and a cheap ESP8266 module)\n\n```javascript\nimport { connect } from 'net'\n\n// substitute 192.168.4.1 with the IP address of your module\nconst port = connect({ host: '192.168.4.1', port: 2323 })\n\nport.on('connect', () =\u003e {\n  console.log('Connected!')\n  // here you can start sending commands\n})\n```\n\nThe rest is exactly the same. The TCP connection also is a stream so piping the data through the `MavLinkPacketSplitter` and `MavLinkPacketParser` works as expected.\n\n### A short note to my future self and others about baudrates\n\nThe default serial port speed for telemetry in Ardupilot is 57600 bauds. This means that in the user interface of the esp-link (accessible via a web page under the same IP address) you need to make sure the speed is properly set on the _µC Console_ tab. Just select the proper baudrate from the dropdown at the top of the page and you'll be all set. No reboot of the module required!\n\n### Using MAVESP8266 in UDP mode\n\nThe _official_ firmware for setting up a UDP telemetry using ESP8266 is [MAVESP8266](https://ardupilot.org/copter/docs/common-esp8266-telemetry.html). This firmware exposes messages over UDP rather than TCP but has other advantages (see the documentation).\n\nTo setup a stream that reads from a UDP socket isn't as easy as with TCP sockets (which are in a sense streams on their own) but is not hard at all because the library exposes the `MavEsp8266` class that encapsulates all of the hard work for you:\n\n```javascript\nimport { MavEsp8266, common } from 'node-mavlink'\n\nasync function main() {\n  const port = new MavEsp8266()\n\n  // start the communication\n  await port.start()\n\n  // log incoming packets\n  port.on('data', packet =\u003e {\n    console.log(packet.debug())\n  })\n\n  // You're now ready to send messages to the controller using the socket\n  // let's request the list of parameters\n  const message = new common.ParamRequestList()\n  message.targetSystem = 1\n  message.targetComponent = 1\n\n  // The `send` method is another utility method, very handy to have it provided\n  // by the library. It takes care of the sequence number and data serialization.\n  await port.send(message)\n}\n\nmain()\n```\n\nThat's it! Easy as a lion :)\n\n## Signed packages\n\nMavLink v2 introduces package signing. The way it currently works with Mission planner is you give it a pass phrase, Mission Planner encodes it using sha256 hashing algorithm and uses it as part of the signature calculation. Therefore if someone does not know the secret passphrase they won't be able to create packets that would seem to be coming from a source. It's a kind of security thing.\n\n### Reading signature\n\nThe `node-mavlink` library introduced signature parsing in version 0.0.1-beta.10. The way to verify if a package can be trusted is as follows:\n\n```javascript\nimport { MavLinkPacketSignature } from 'node-mavlink'\n\n// calculate secret key (change 'qwerty' to your secret phrase)\nconst key = MavLinkPacketSignature.key('qwerty')\n\n// log incoming messages\nport.on('data', packet =\u003e {\n  console.log(packet.debug())\n  if (packet.signature) {\n    if (packet.signature.matches(key)) {\n      // signature valid\n    } else {\n      // signature not valid - possible fraud package detected\n    }\n  } else {\n    // packet is not signed\n  }\n})\n```\n\nWhat you do with that information is up to you. You can continue to process that package or you can drop it. The library imposes no restriction on packets with invalid signatures.\n\n### Sending signed packages\n\nFirst we need to learn how to create a secure key. As mentioned before the key in Mission Planner is created by calculating an SHA256 checksum over a secret phrase that you can specify and then taking the first 6 bytes of it.\n\nTo do the same using this library:\n\n```javascript\nimport { MavLinkPacketSignature } from 'node-mavlink'\n\nconst key = MavLinkPacketSignature.key('your very secret passphrase')\n```\n\nNow that we have the key ready we can send signed packages. Let's use the `ParamRequestList` as an example:\n\n```javascript\nimport { common, sendSigned } from 'node-mavlink'\n\nasync function requestParameterList() {\n  const message = new common.ParamRequestList()\n  message.targetSystem = 1\n  message.targetComponent = 1\n\n  await sendSigned(port, message, key)\n}\n```\n\nIf you're using the `MavEsp8266` class for communicating over UDP it also exposes the `sendSigned()` method with the same signature.\n\n## Utility functions\n\nThe library exposes a few utility functions that make the life easier when writing application code\n\n#### `async waitFor(cb: Function, timeout: number, interval: number)`\n\nThis function calls the `cb` callback periodically at the `interval` (default: 100ms) and if it returns a `truthy` value it will stop polling. If, however, the value is `falsy` for a longer period of time than the `timeout` (default: 10000ms) then it will throw a `Timeout` error.\n\n#### `async send(stream: Writable, msg: MavLinkData, protocol: MavLinkProtocol)`\n\nThis function serializes the `msg` message using the provided `protocol` (default: `MavLinkProtocolV1`) and sends it to the `stream`. If the process is successful the method returns with the length of written data denoting that no error occurred. However, if the process was not successful it will error out with the underlying error object returned on by the stream.\n\n#### `async sendSigned(stream: Writable, msg: MavLinkData, key: Buffer, linkId: uint8_t, sysid: uint8_t, compid: uint8_t)`\n\nThis is a similar function to `send` but does so using MavLink v2 protocol and signs the message.\n\nThe default values for some parameters are as follows:\n- `linkId` = 1\n- `sysid` = 254\n- `compid` = 1\n\n#### `async sleep(ms: number)`\n\nThis is a very handy utility function that asynchronously pauses for a given time (ms).\n\n## Running sim_vehicle.py\n\nThe easiest way to start playing around with this package is to use `sim_vehicle.py`. You can use the default parameters for the MavEsp8266 if you'll make the simulator compatible with it:\n\n```\n$ Tools/autotest/sim_vehicle.py -v ArduCopter -f quad --console --map --out udpin:127.0.0.1:14555\n```\n\nThat last parameter (`--out udpin:127.0.0.1:14555`) opens up for incoming messages in port 14555, which is the default send port for MavEsp8266 and its default firmware.\n\n## Registering custom messages\n\nThere are times when you want to have custom messages, for example when you're building a rocket and there is no target you can use out of the box. There are actually two scenarios:\n\n1. You have a few custom messages, but generally you're happy with the original set of messages\n2. You don't care about the original messages, maybe you do about the heartbeat, but nothing else\n\n### Registering a single command\n\nThere are 3 steps to register a custom command:\n\na) create a class that defines your custom command\nb) add it to your `REGISTRY`\nc) register your custom command's magic number\n\nThe first two steps are pretty self explanatory and there is a plethora of examples in the mavlink-mappings project - use those to learn how to create your own message definitions.\n\nThe last step is quite easy:\n\n```javascript\nimport { registerCustomMessageMagicNumber } from 'node-mavlink'\n\nregisterCustomMessageMagicNumber('999999', 42)\n```\n\nFrom now on the splitter will know how to properly calculate CRC for your packages and you're all good.\n\n### Replacing magic numbers registry all together\n\nWell, if all you care about is the ping, why parse anything else, right? And if on top of the ping command you've got a number of custom messages - all the better to not parse even the messages!\n\n```javascript\nimport { MavLinkSplitter, MavLinkParser } from 'node-mavlink'\n\nconst MY_MAGIC_NUMBERS = {\n  '0': 50, // Heartbeat\n  // ...other magic number definitions go here\n}\n\nconst source = ... // obtain source stream\nconst reader = source\n  .pipe(new MavLinkPacketSplitter({}, { magicNumbers: MY_MAGIC_NUMBERS }))\n  .pipe(new MavLinkPacketParser())\n```\n\n## Closing thoughts\n\nThe original generated sources lack one very important aspect of a reusable library: documentation. Also, most of the time the names are more C-like than JavaScript-like.\n\nWhen generating sources for data classes a number of things happen:\n\n- `enum` values are trimmed from common prefix; duplicating enum name in its value when its value cannot be spelled without the enum name is pointless and leads to unnecessarily verbose code\n- enum values, whenever available, contain JSDoc describing their purpose\n- data classes are properly named (PascalCase)\n- data classes fields are properly named (camelCase)\n- both data classes and their fields whenever available also contain JSDoc\n- if a particular data class is deprecated that information is also available in the JSDoc\n- some class names are modified compared to their original values (e.g. `Statustext` =\u003e `StatusText`)\n\nThis leads to generated code that contains not only raw types but also documentation where it is mostly useful: right at your fingertips.\n\nI hope you'll enjoy using this library! If you have any comments, find a bug or just generally want to share your thoughts you can reach me via email: padcom@gmail.com\n\nPeace!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fardupilot%2Fnode-mavlink","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fardupilot%2Fnode-mavlink","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fardupilot%2Fnode-mavlink/lists"}