Crates.io | mavio |
lib.rs | mavio |
version | |
source | src |
created_at | 2023-12-30 11:54:14.42782+00 |
updated_at | 2025-03-15 21:07:02.23152+00 |
description | Minimalistic MAVLink client that supports `no-std` and `no-alloc` targets. |
homepage | https://mavka.gitlab.io/home/projects/mavio |
repository | https://gitlab.com/mavka/libs/mavio |
max_upload_size | |
id | 1084152 |
Cargo.toml error: | TOML parse error at line 22, column 1 | 22 | autolib = false | ^^^^^^^ unknown field `autolib`, expected one of `name`, `version`, `edition`, `authors`, `description`, `readme`, `license`, `repository`, `homepage`, `documentation`, `build`, `resolver`, `links`, `default-run`, `default_dash_run`, `rust-version`, `rust_dash_version`, `rust_version`, `license-file`, `license_dash_file`, `license_file`, `licenseFile`, `license_capital_file`, `forced-target`, `forced_dash_target`, `autobins`, `autotests`, `autoexamples`, `autobenches`, `publish`, `metadata`, `keywords`, `categories`, `exclude`, `include` |
size | 0 |
Minimalistic library for transport-agnostic MAVLink communication. It supports no-std
(and no-alloc
) targets.
Currently, we use GitLab as the main project repository and GitHub as official mirror.
We accept issues and pull-requests only at GitLab but will do our best to keep GitHub discussions as alive as possible.
The mirror will always contain latest release tags and is kept up to date automatically.
Mavio is a building block for more sophisticated tools. It is entirely focused on one thing: to include absolute minimum of functionality required for correct communication with everything that speaks MAVLink protocol.
MAVLink 1
and MAVLink 2
protocol versions.In other words, Mavio implements all stateless features of MAVLink protocol. Which means that it does not provide support for message sequencing, automatic heartbeats, etc. The client is responsible for implementing these parts of the protocol by their own or use a dedicated library. We've decided to keep Mavio as simple and catchy as an 8-bit melody.
At the same time, Mavio is flexible and tries to dictate as few as possible. In particular:
std::io::Read
and std::io::Write
traits.no_std
targets. For such cases the library provides simplified versions of Read
and Write
traits.This library is a part of Mavka toolchain. It is integrated with other projects such as:
Mavio
that
provides a high-level interface for MAVLink messaging and takes care about stateful features of the protocol:
sequencing, message time-stamping, automatic heartbeats, simplifies message signing, and so on.This project respects semantic versioning
. As allowed by specification, breaking changes may be
introduced in minor releases until version 1.0.0
is reached. However, we will keep unstable features under the
unstable
feature flag whenever possible.
Install Mavio with cargo:
cargo add mavio
For details, please check API section and API documentation.
Connect as TCP client, receive 10 frames, and decode any received
HEARTBEAT
message:
use std::net::TcpStream;
use mavio::{Frame, Receiver};
use mavio::dialects::minimal as dialect;
use mavio::io::StdReader;
use dialect::Minimal;
fn main() -> mavio::errors::Result<()> {
let mut receiver = Receiver::new(StdReader::new(TcpStream::connect("0.0.0.0:5600")?));
for i in 0..10 {
let frame = receiver.recv_frame()?;
if let Err(err) = frame.validate_checksum::<Minimal>() {
eprintln!("Invalid checksum: {:?}", err);
continue;
}
if let Ok(Minimal::Heartbeat(msg)) = frame::decode() {
println!(
"HEARTBEAT #{}: mavlink_version={:#?}",
frame.sequence(),
msg.mavlink_version,
);
}
}
Ok(())
}
A slightly more elaborated use-case can be found in tcp_client.rs
example.
Listen to TCP port as a server, send 10 HEARTBEAT
messages
to any connected client using MAVLink 2 protocol, then disconnect a client.
use std::net::TcpStream;
use mavio::dialects::minimal as dialect;
use mavio::io::StdWriter;
use dialect::enums::{MavAutopilot, MavModeFlag, MavState, MavType};
use mavio::prelude::*;
fn main() -> Result<()> {
// Create a TCP client sender
let mut sender = Sender::new(StdWriter::new(TcpStream::connect("0.0.0.0:5600")?));
// Create an endpoint that represents a MAVLink device speaking `MAVLink 2` protocol
let endpoint = Endpoint::v2(MavLinkId::new(15, 42));
// Create a message
let message = dialect::messages::Heartbeat {
type_: MavType::FixedWing,
autopilot: MavAutopilot::Generic,
base_mode: MavModeFlag::TEST_ENABLED & MavModeFlag::CUSTOM_MODE_ENABLED,
custom_mode: 0,
system_status: MavState::Active,
mavlink_version: 3,
};
println!("MESSAGE: {message:?}");
for i in 0..10 {
// Build the next frame for this endpoint.
// All required fields will be populated, including frame sequence counter.
let frame = endpoint.next_frame(&message)?;
sender.send_frame(&frame)?;
println!("FRAME #{} sent: {:#?}", i, frame);
}
Ok(())
}
Check tcp_server.rs
for a slightly more elaborated use-case.
This section provides a general API overview. For further details, please check API documentation.
Mavio provides two basic I/O primitives: Sender
/
Receiver
for synchronous and
AsyncSender
/
AsyncReceiver
for asynchronous I/O. These structs send
and receive instances of Frame
.
Sender
and Receiver
are generic over Write
and
std::io::Read
accordingly. While AsyncSender
and
AsyncReceiver
use AsyncWrite
and
std::io::AsyncRead
. A set of I/O
adapters are available for
std::io, Tokio, and
futures-rs
. That means you can communicate MAVLink messages over various transports
including UDP, TCP, Unix sockets, and files. It is also easy to implement custom transport.
For no-std
targets Mavio provides adapters for embedded HAL
I/O
(both synchronous and asynchronous).
Upon receiving, MAVLink Frame
s can be validated and decoded
into MAVLink messages. Frames can be routed, signed, or forwarded to another system/component ID without decoding.
Note!
MAVLink checksum validation requires
CRC_EXTRA
byte which in its turn depends on a dialect specification. That means, if you are performing dialect-agnostic routing from a noisy source or from devices which implement outdated message specifications, you may forward junk messages. In case of high-latency channels you might want to enforce compliance with a particular dialect to filter incompatible messages.
To decode a frame into a MAVLink message, you need to use a specific dialect. Standard MAVLink dialects are available
under mavio::dialects
and can be enabled by the corresponding
feature flags (dlct-<dialect name>
).
minimal
β minimal dialect required to expose your presence to
other MAVLink devices.standard
β a superset of minimal
dialect which expected to be
used by almost all flight stack.common
β minimum viable dialect with most of the features, a
building block for other future-rich dialects.ardupilotmega
β feature-full dialect used by
ArduPilot. In most cases this dialect is the go-to choice if you want to recognize almost
all MAVLink messages used by existing flight stacks.all
β meta-dialect which includes all other standard dialects
including these which were created for testing purposes. It is guaranteed that namespaces of the dialects in all
family do not collide.asluav
, avssuas
, csairlink
, cubepilot
, development
, icarous
, matrixpilot
, paparazzi
, ualberta
,
uavionix
. These do not include python_array_test
and test
dialects which should be either generated manually
or as a part of all
meta-dialect.For example:
use mavio::dialects::common as dialect;
use dialect::{Common, messages::Heartbeat};
use mavio::prelude::*;
fn main() -> mavio::error::Result<()> {
let frame: Frame<V2> = Frame::builder()
.version(V2).system_id(1).component_id(0).sequence(0)
.message(&Heartbeat::default())?
.build();
// Decode MavLink frame into a dialect message:
match frame.decode()? {
Common::Heartbeat(msg) => {
/* process heartbeat */
}
_ => { unreachable!(); }
};
Ok(())
}
For small applications that use only a small subset of messages, avoid using dialect enums as they contain all message variants. Instead, decode messages directly from frames:
use mavio::dialects::common as dialect;
use dialect::messages::Heartbeat;
use mavio::prelude::*;
fn main() -> mavio::error::Result<()> {
let frame: Frame<V2> = Frame::builder()
.version(V2).system_id(1).component_id(0).sequence(0)
.message(&Heartbeat::default())?
.build();
// Use only specific messages:
match frame.message_id() {
Heartbeat::ID => {
let msg = Heartbeat::try_from(frame.payload())?;
/* process heartbeat */
}
/* process other messages */
_ => { unreachable!(); }
};
Ok(())
}
Actual implementations of MAVLink dialects are generated by MAVSpec.
There are three ways to generate your own dialects: generate from XML definitions using build script, patch
mavlink-message-definitions
, and create your own ad-hoc
dialects. All these options are technically not related to Mavio and are part of MAVSpec. We suggest to check its
documentation for details.
You may also found useful to review build.rs
in one of the examples.
Mavio allows to generate additional structures tailored for MAVLink microservices.
Each microservice is a subdialect with only those messages and enums which are necessary. To generate microservice
subdialects use msrv-*
feature flags.
Mavio also provides additional utils to work with MAVLink microservices. These tools can be enabled by msrv-utils-*
feature flags and available in microservices
module.
Microservice utils feature flags:
msrv-utils-all
β all microservice utils.msrv-utils-mission
β MAVLink
mission protocol utils including support for unofficial
mission file format.It is possible to bundle message definitions generated by MAVInspect
into definitions
module. This can be useful for ground control stations that require to present the
user with the descriptions of MAVLink entities.
To enable definitions bundling use definitions
feature flag.
β οΈ Message definitions available only with
std
feature enabled. Otherwise, this will cause build to fail.
Examples for synchronous I/O in ./examples/sync/examples
:
tcp_server.rs
is a simple TCP server that awaits for connections, sends
and receives heartbeats:
cargo run --package mavio_examples_sync --example tcp_server
tcp_client.rs
is a TCP client which connects to server, sends and receives
heartbeats:
cargo run --package mavio_examples_sync --example tcp_client
tcp_ping_pong.rs
server and clients which communicate with each other
via TCP:
cargo run --package mavio_examples_sync --example tcp_ping_pong
Examples for asynchronous I/O in ./examples/async/examples
:
async_tcp_ping_pong.rs
server and clients which communicate with
each other via TCP:
cargo run --package mavio_examples_async --example async_tcp_ping_pong
Examples for custom dialect generation with filtered MAVLink entities can be found in
./examples/custom/examples
:
custom_dialects_usage.rs
a basic usage of
custom-generated dialect:
cargo run --package mavio_examples_custom --example mavio_examples_custom_usage
custom_message.rs
creating and using a custom message:
cargo run --package mavio_examples_custom --example custom_message
Here we simply comply with the suggested dual licensing according to Rust API Guidelines (C-PERMISSIVE).
Licensed under either of
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.