Skip to content

Commit

Permalink
Update embedded-hal to 1.0.
Browse files Browse the repository at this point in the history
Add async methods.
Add async example (Embassy executor).
  • Loading branch information
qwerty19106 committed Jun 17, 2024
1 parent 96079f4 commit 0d86643
Show file tree
Hide file tree
Showing 7 changed files with 368 additions and 22 deletions.
6 changes: 3 additions & 3 deletions mavlink-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ rust-version = "1.65.0"
[dependencies]
crc-any = { workspace = true, default-features = false }
byteorder = { workspace = true, default-features = false }
embedded-hal = { version = "0.2", optional = true }
nb = { version = "1.0", optional = true }
embedded-io = { version = "0.6.1", optional = true }
embedded-io-async = { version = "0.6.1", optional = true }
serde = { version = "1.0.115", optional = true, features = ["derive"] }
serde_arrays = { version = "0.1.0", optional = true }
serial = { version = "0.4", optional = true }
Expand All @@ -30,6 +30,6 @@ serial = { version = "0.4", optional = true }
"udp" = []
"tcp" = []
"direct-serial" = ["serial"]
"embedded" = ["embedded-hal", "nb"]
"embedded" = ["dep:embedded-io", "dep:embedded-io-async"]
"serde" = ["dep:serde", "dep:serde_arrays"]
default = ["std", "tcp", "udp", "direct-serial", "serde"]
24 changes: 6 additions & 18 deletions mavlink-core/src/embedded.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,12 @@ use crate::error::*;

/// Replacement for std::io::Read + byteorder::ReadBytesExt in no_std envs
pub trait Read {
fn read_u8(&mut self) -> Result<u8, MessageReadError>;

fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), MessageReadError> {
for byte in buf {
*byte = self.read_u8()?;
}

Ok(())
}
fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), MessageReadError>;
}

impl<R: embedded_hal::serial::Read<u8>> Read for R {
fn read_u8(&mut self) -> Result<u8, MessageReadError> {
nb::block!(self.read()).map_err(|_| MessageReadError::Io)
impl<R: embedded_io::Read> Read for R {
fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), MessageReadError> {
embedded_io::Read::read_exact(self, buf).map_err(|_| MessageReadError::Io)
}
}

Expand All @@ -24,12 +16,8 @@ pub trait Write {
fn write_all(&mut self, buf: &[u8]) -> Result<(), MessageWriteError>;
}

impl<W: embedded_hal::serial::Write<u8>> Write for W {
impl<W: embedded_io::Write> Write for W {
fn write_all(&mut self, buf: &[u8]) -> Result<(), MessageWriteError> {
for byte in buf {
nb::block!(self.write(*byte)).map_err(|_| MessageWriteError::Io)?;
}

Ok(())
embedded_io::Write::write_all(self, buf).map_err(|_| MessageWriteError::Io)
}
}
205 changes: 204 additions & 1 deletion mavlink-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,49 @@ pub fn read_v1_raw_message<M: Message, R: Read>(
}
}

/// Read a MAVLink v1 message from a Read stream.
/// Async read a raw buffer with the mavlink message
/// V1 maximum size is 263 bytes: `<https://mavlink.io/en/guide/serialization.html>`
///
/// # Example
/// See mavlink/examples/embedded-async-read full example for details.
#[cfg(feature = "embedded")]
pub async fn read_v1_raw_message_async<M: Message>(
reader: &mut impl embedded_io_async::Read,
) -> Result<MAVLinkV1MessageRaw, error::MessageReadError> {
loop {
// search for the magic framing value indicating start of mavlink message
let mut byte = [0u8];
loop {
reader
.read_exact(&mut byte)
.await
.map_err(|_| error::MessageReadError::Io)?;
if byte[0] == MAV_STX {
break;
}
}

let mut message = MAVLinkV1MessageRaw::new();

message.0[0] = MAV_STX;
reader
.read_exact(message.mut_header())
.await
.map_err(|_| error::MessageReadError::Io)?;
reader
.read_exact(message.mut_payload_and_checksum())
.await
.map_err(|_| error::MessageReadError::Io)?;

// retry if CRC failed after previous STX
// (an STX byte may appear in the middle of a message)
if message.has_valid_crc::<M>() {
return Ok(message);
}
}
}

/// Read a MAVLink v1 message from a Read stream.
pub fn read_v1_msg<M: Message, R: Read>(
r: &mut PeekReader<R>,
) -> Result<(MavHeader, M), error::MessageReadError> {
Expand All @@ -419,6 +461,34 @@ pub fn read_v1_msg<M: Message, R: Read>(
.map_err(|err| err.into())
}

/// Async read a MAVLink v1 message from a Read stream.
///
/// NOTE: it will be add ~80KB to firmware flash size because all *_DATA::deser methods will be add to firmware.
/// Use `*_DATA::ser` methods manually to prevent it.
#[cfg(feature = "embedded")]
pub async fn read_v1_msg_async<M: Message>(
r: &mut impl embedded_io_async::Read,
) -> Result<(MavHeader, M), error::MessageReadError> {
let message = read_v1_raw_message_async::<M>(r).await?;

M::parse(
MavlinkVersion::V1,
u32::from(message.message_id()),
message.payload(),
)
.map(|msg| {
(
MavHeader {
sequence: message.sequence(),
system_id: message.system_id(),
component_id: message.component_id(),
},
msg,
)
})
.map_err(|err| err.into())
}

const MAVLINK_IFLAG_SIGNED: u8 = 0x01;

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
Expand Down Expand Up @@ -621,6 +691,48 @@ pub fn read_v2_raw_message<M: Message, R: Read>(
}
}

/// Async read a raw buffer with the mavlink message
/// V2 maximum size is 280 bytes: `<https://mavlink.io/en/guide/serialization.html>`
///
/// # Example
/// See mavlink/examples/embedded-async-read full example for details.
#[cfg(feature = "embedded")]
pub async fn read_v2_raw_message_async<M: Message>(
reader: &mut impl embedded_io_async::Read,
) -> Result<MAVLinkV2MessageRaw, error::MessageReadError> {
loop {
// search for the magic framing value indicating start of mavlink message
let mut byte = [0u8];
loop {
reader
.read_exact(&mut byte)
.await
.map_err(|_| error::MessageReadError::Io)?;
if byte[0] == MAV_STX_V2 {
break;
}
}

let mut message = MAVLinkV2MessageRaw::new();

message.0[0] = MAV_STX;
reader
.read_exact(message.mut_header())
.await
.map_err(|_| error::MessageReadError::Io)?;
reader
.read_exact(message.mut_payload_and_checksum_and_sign())
.await
.map_err(|_| error::MessageReadError::Io)?;

// retry if CRC failed after previous STX
// (an STX byte may appear in the middle of a message)
if message.has_valid_crc::<M>() {
return Ok(message);
}
}
}

/// Read a MAVLink v2 message from a Read stream.
pub fn read_v2_msg<M: Message, R: Read>(
read: &mut PeekReader<R>,
Expand All @@ -641,6 +753,34 @@ pub fn read_v2_msg<M: Message, R: Read>(
.map_err(|err| err.into())
}

/// Async read a MAVLink v2 message from a Read stream.
///
/// NOTE: it will be add ~80KB to firmware flash size because all *_DATA::deser methods will be add to firmware.
/// Use `*_DATA::deser` methods manually to prevent it.
#[cfg(feature = "embedded")]
pub async fn read_v2_msg_async<M: Message, R: embedded_io_async::Read>(
r: &mut R,
) -> Result<(MavHeader, M), error::MessageReadError> {
let message = read_v2_raw_message_async::<M>(r).await?;

M::parse(
MavlinkVersion::V2,
u32::from(message.message_id()),
message.payload(),
)
.map(|msg| {
(
MavHeader {
sequence: message.sequence(),
system_id: message.system_id(),
component_id: message.component_id(),
},
msg,
)
})
.map_err(|err| err.into())
}

/// Write a message using the given mavlink version
pub fn write_versioned_msg<M: Message, W: Write>(
w: &mut W,
Expand All @@ -654,6 +794,23 @@ pub fn write_versioned_msg<M: Message, W: Write>(
}
}

/// Async write a message using the given mavlink version
///
/// NOTE: it will be add ~70KB to firmware flash size because all *_DATA::ser methods will be add to firmware.
/// Use `*_DATA::ser` methods manually to prevent it.
#[cfg(feature = "embedded")]
pub async fn write_versioned_msg_async<M: Message>(
w: &mut impl embedded_io_async::Write,
version: MavlinkVersion,
header: MavHeader,
data: &M,
) -> Result<usize, error::MessageWriteError> {
match version {
MavlinkVersion::V2 => write_v2_msg_async(w, header, data).await,
MavlinkVersion::V1 => write_v1_msg_async(w, header, data).await,
}
}

/// Write a MAVLink v2 message to a Write stream.
pub fn write_v2_msg<M: Message, W: Write>(
w: &mut W,
Expand All @@ -671,6 +828,29 @@ pub fn write_v2_msg<M: Message, W: Write>(
Ok(len)
}

/// Async write a MAVLink v2 message to a Write stream.
///
/// NOTE: it will be add ~70KB to firmware flash size because all *_DATA::ser methods will be add to firmware.
/// Use `*_DATA::ser` methods manually to prevent it.
#[cfg(feature = "embedded")]
pub async fn write_v2_msg_async<M: Message>(
w: &mut impl embedded_io_async::Write,
header: MavHeader,
data: &M,
) -> Result<usize, error::MessageWriteError> {
let mut message_raw = MAVLinkV2MessageRaw::new();
message_raw.serialize_message(header, data);

let payload_length: usize = message_raw.payload_length().into();
let len = 1 + MAVLinkV2MessageRaw::HEADER_SIZE + payload_length + 2;

w.write_all(&message_raw.0[..len])
.await
.map_err(|_| error::MessageWriteError::Io)?;

Ok(len)
}

/// Write a MAVLink v1 message to a Write stream.
pub fn write_v1_msg<M: Message, W: Write>(
w: &mut W,
Expand All @@ -687,3 +867,26 @@ pub fn write_v1_msg<M: Message, W: Write>(

Ok(len)
}

/// Write a MAVLink v1 message to a Write stream.
///
/// NOTE: it will be add ~70KB to firmware flash size because all *_DATA::ser methods will be add to firmware.
/// Use `*_DATA::ser` methods manually to prevent it.
#[cfg(feature = "embedded")]
pub async fn write_v1_msg_async<M: Message>(
w: &mut impl embedded_io_async::Write,
header: MavHeader,
data: &M,
) -> Result<usize, error::MessageWriteError> {
let mut message_raw = MAVLinkV1MessageRaw::new();
message_raw.serialize_message(header, data);

let payload_length: usize = message_raw.payload_length().into();
let len = 1 + MAVLinkV1MessageRaw::HEADER_SIZE + payload_length + 2;

w.write_all(&message_raw.0[..len])
.await
.map_err(|_| error::MessageWriteError::Io)?;

Ok(len)
}
7 changes: 7 additions & 0 deletions mavlink/examples/embedded-async-read/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[target.thumbv7em-none-eabihf]
rustflags = [
"-C", "link-arg=-Tlink.x",
]

[build]
target = "thumbv7em-none-eabihf"
44 changes: 44 additions & 0 deletions mavlink/examples/embedded-async-read/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
[package]
name = "mavlink-embedded-async-read"
edition = "2021"
authors = ["Patrick José Pereira <patrickelectric@gmail.com>"]
version = "0.1.0"

[profile.release]
opt-level = 'z' # Optimize for binary size, but also turn off loop vectorization.
lto = true # Performs "fat" LTO which attempts to perform optimizations across all crates within the dependency graph

[dependencies]
cortex-m = { version = "0.7", features = [
"inline-asm",
"critical-section-single-core",
] } # Low level access to Cortex-M processors
cortex-m-rt = "0.7" # Startup code and minimal runtime for Cortex-M microcontrollers
rtt-target = "0.5"
panic-rtt-target = "0.1" # Panic handler
static_cell = "2.1"

embassy-time = { version = "0.3", features = ["tick-hz-32_768"] }
embassy-executor = { version = "0.5", features = [
"arch-cortex-m",
"executor-thread",
"executor-interrupt",
"integrated-timers",
] }
embassy-stm32 = { version = "0.1", features = [
"memory-x",
"stm32f446re",
"time-driver-any",
] }

[dependencies.mavlink] # MAVLink library (wait for 0.9.0 version)
path = "../../"
features = ["common", "embedded"]
default-features = false

[patch.crates-io]
embassy-time-driver = { git = "https://github.com/embassy-rs/embassy.git", rev = "86c48dde4192cabcad22faa10cabb4dc5f035c0a" }
embassy-time-queue-driver = { git = "https://github.com/embassy-rs/embassy.git", rev = "86c48dde4192cabcad22faa10cabb4dc5f035c0a" }
embassy-stm32 = { git = "https://github.com/embassy-rs/embassy.git", rev = "86c48dde4192cabcad22faa10cabb4dc5f035c0a" }

[workspace]
11 changes: 11 additions & 0 deletions mavlink/examples/embedded-async-read/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# rust-MAVLink Embedded async example (with reading loop)
### How to run:
- Install cargo flash:
- cargo install cargo-flash
- Install target
- rustup target add thumbv7em-none-eabihf
- Check if we can build the project
- cargo build
- Connect your STM32f446re board
- Flash it!
- cargo flash --chip stm32f446RETx --release --log info
Loading

0 comments on commit 0d86643

Please sign in to comment.