From 76830e2ed0258f6729a327b6107f013b645f83ca Mon Sep 17 00:00:00 2001 From: Keziah Biermann Date: Mon, 17 Mar 2025 21:35:39 +0100 Subject: [PATCH 1/5] Better error API --- .appveyor.yml | 4 ++ rfm95/Cargo.toml | 3 +- rfm95/README.md | 5 +++ rfm95/src/error.rs | 61 +++++++++++++++++++++++++++++ rfm95/src/lib.rs | 1 + rfm95/src/lora/types.rs | 27 +++++++------ rfm95/src/rfm95/connection.rs | 14 ++++--- rfm95/src/rfm95/driver.rs | 72 ++++++++++++++++++----------------- 8 files changed, 133 insertions(+), 54 deletions(-) create mode 100644 rfm95/src/error.rs diff --git a/.appveyor.yml b/.appveyor.yml index c958ef0..d91d824 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -7,7 +7,11 @@ configuration: - --features= - --features=debug - --features=fugit + - --features=backtrace - --features=debug,fugit + - --features=fugit,backtrace + - --features=backtrace,debug + - --features=debug,fugit,backtrace # General environment vars diff --git a/rfm95/Cargo.toml b/rfm95/Cargo.toml index 19c6e57..60cab57 100644 --- a/rfm95/Cargo.toml +++ b/rfm95/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embedded-lora-rfm95" -version = "0.1.3" +version = "0.2.0" edition = "2021" authors = ["KizzyCode Software Labs./Keziah Biermann "] keywords = [] @@ -17,6 +17,7 @@ readme = "README.md" [features] default = [] debug = [] +backtrace = [] fugit = ["dep:fugit"] diff --git a/rfm95/README.md b/rfm95/README.md index b66ce94..6cb6933 100644 --- a/rfm95/README.md +++ b/rfm95/README.md @@ -18,6 +18,11 @@ The `fugit`-feature implements simple `From`/`Into`-conversions between the buil [`fugit`'s](https://crates.io/crates/fugit) [`HertzU32` type](https://docs.rs/fugit/latest/fugit/type.HertzU32.html). This is a comfort-feature only, and does not enable additional functionality. +### `backtrace` (disabled by default) +The `backtrace`-feature can be used to get more verbose errors. If this feature is enabled, errors will contain a human +readable description as well as file and line information about where the error occurred. This is useful for debugging +or better logging, but can be disabled if library size matters. + ### `debug` (disabled by default) The `debug` feature enables some debug functionality, namely an SPI debug callback which can be used to log all SPI transactions with the RFM95 modem, and provides some helper functions to dump the register state and FIFO contents. The diff --git a/rfm95/src/error.rs b/rfm95/src/error.rs new file mode 100644 index 0000000..27b5bab --- /dev/null +++ b/rfm95/src/error.rs @@ -0,0 +1,61 @@ +//! The crate's error type + +/// Creates an error +#[macro_export] +macro_rules! err { + ($kind:expr, $desc:expr) => {{ + #[cfg(feature = "backtrace")] + { + // Create an error with rich backtrace information + $crate::error::Error { kind: $kind, file: file!(), line: line!(), description: $desc } + } + + #[cfg(not(feature = "backtrace"))] + { + // Create a size-optimized error + $crate::error::Error { kind: $kind } + } + }}; + (eio: $desc:expr) => {{ + err!($crate::error::ErrorKind::IoError, $desc) + }}; + (etimedout: $desc:expr) => {{ + err!($crate::error::ErrorKind::Timeout, $desc) + }}; + (ebadmsg: $desc:expr) => {{ + err!($crate::error::ErrorKind::InvalidMessage, $desc) + }}; + (einval: $desc:expr) => {{ + err!($crate::error::ErrorKind::InvalidValue, $desc) + }}; +} + +/// The error kind +#[derive(Debug, PartialEq, Eq)] +#[repr(u8)] +pub enum ErrorKind { + /// An SPI or reset-related I/O error + IoError, + /// A timeout occurred + Timeout, + /// A CRC validation failed + InvalidMessage, + /// A function argument is invalid + InvalidValue, +} + +/// The crate's error type +#[derive(Debug)] +pub struct Error { + /// The error kind + pub kind: ErrorKind, + /// The file where the error was created + #[cfg(feature = "backtrace")] + pub file: &'static str, + /// The line at which the error was created + #[cfg(feature = "backtrace")] + pub line: u32, + /// A human readable error description + #[cfg(feature = "backtrace")] + pub description: &'static str, +} diff --git a/rfm95/src/lib.rs b/rfm95/src/lib.rs index 313d0e3..603e294 100644 --- a/rfm95/src/lib.rs +++ b/rfm95/src/lib.rs @@ -15,5 +15,6 @@ #![warn(clippy::allow_attributes_without_reason)] #![warn(clippy::cognitive_complexity)] +pub mod error; pub mod lora; pub mod rfm95; diff --git a/rfm95/src/lora/types.rs b/rfm95/src/lora/types.rs index a48c9e2..703e5d1 100644 --- a/rfm95/src/lora/types.rs +++ b/rfm95/src/lora/types.rs @@ -1,5 +1,8 @@ //! Small wrappers for type safety +use crate::err; +use crate::error::Error; + /// A LoRa spreading factor /// /// # Implementation Note @@ -26,7 +29,7 @@ pub enum SpreadingFactor { S12 = 12, } impl TryFrom for SpreadingFactor { - type Error = &'static str; + type Error = Error; fn try_from(value: u8) -> Result { match value { @@ -36,7 +39,7 @@ impl TryFrom for SpreadingFactor { sf if sf == Self::S10 as u8 => Ok(Self::S10), sf if sf == Self::S11 as u8 => Ok(Self::S11), sf if sf == Self::S12 as u8 => Ok(Self::S12), - _ => Err("Invalid or unsupported spreading factor"), + _ => Err(err!(einval: "Invalid or unsupported spreading factor")), } } } @@ -71,7 +74,7 @@ pub enum Bandwidth { B7_8 = 0b0000, } impl TryFrom for Bandwidth { - type Error = &'static str; + type Error = Error; fn try_from(value: u8) -> Result { match value { @@ -85,7 +88,7 @@ impl TryFrom for Bandwidth { bw if bw == Self::B15_6 as u8 => Ok(Self::B15_6), bw if bw == Self::B10_4 as u8 => Ok(Self::B10_4), bw if bw == Self::B7_8 as u8 => Ok(Self::B7_8), - _ => Err("Invalid or unsupported bandwidth"), + _ => Err(err!(einval: "Invalid or unsupported bandwidth")), } } } @@ -108,7 +111,7 @@ pub enum CodingRate { C4_8 = 0b100, } impl TryFrom for CodingRate { - type Error = &'static str; + type Error = Error; fn try_from(value: u8) -> Result { match value { @@ -116,7 +119,7 @@ impl TryFrom for CodingRate { cr if cr == Self::C4_6 as u8 => Ok(Self::C4_6), cr if cr == Self::C4_7 as u8 => Ok(Self::C4_7), cr if cr == Self::C4_8 as u8 => Ok(Self::C4_8), - _ => Err("Invalid coding rate"), + _ => Err(err!(einval: "Invalid coding rate")), } } } @@ -135,13 +138,13 @@ pub enum Polarity { Inverted = 1, } impl TryFrom for Polarity { - type Error = &'static str; + type Error = Error; fn try_from(value: u8) -> Result { match value { polarity if polarity == Self::Normal as u8 => Ok(Self::Normal), polarity if polarity == Self::Inverted as u8 => Ok(Self::Inverted), - _ => Err("Invalid IQ polarity value"), + _ => Err(err!(einval: "Invalid IQ polarity value")), } } } @@ -160,13 +163,13 @@ pub enum HeaderMode { Implicit = 1, } impl TryFrom for HeaderMode { - type Error = &'static str; + type Error = Error; fn try_from(value: u8) -> Result { match value { mode if mode == Self::Explicit as u8 => Ok(Self::Explicit), mode if mode == Self::Implicit as u8 => Ok(Self::Implicit), - _ => Err("Invalid header mode"), + _ => Err(err!(einval: "Invalid header mode")), } } } @@ -185,13 +188,13 @@ pub enum CrcMode { Enabled = 1, } impl TryFrom for CrcMode { - type Error = &'static str; + type Error = Error; fn try_from(value: u8) -> Result { match value { mode if mode == Self::Disabled as u8 => Ok(Self::Disabled), mode if mode == Self::Enabled as u8 => Ok(Self::Enabled), - _ => Err("Invalid CRC mode"), + _ => Err(err!(einval: "Invalid CRC mode")), } } } diff --git a/rfm95/src/rfm95/connection.rs b/rfm95/src/rfm95/connection.rs index 3caf970..6aec7d5 100644 --- a/rfm95/src/rfm95/connection.rs +++ b/rfm95/src/rfm95/connection.rs @@ -1,5 +1,7 @@ //! RFM95 SPI connection +use crate::err; +use crate::error::Error; use crate::rfm95::registers::Register; use core::fmt::{Debug, Formatter}; use embedded_hal::digital::OutputPin; @@ -32,7 +34,7 @@ where } /// Reads a RFM95 register via SPI - pub fn read(&mut self, register: T) -> Result + pub fn read(&mut self, register: T) -> Result where T: Register, { @@ -41,7 +43,7 @@ where Ok((register_value & register.mask()) >> register.offset()) } /// Updates a RFM95 register via SPI - pub fn write(&mut self, register: T, value: u8) -> Result<(), &'static str> + pub fn write(&mut self, register: T, value: u8) -> Result<(), Error> where T: Register, { @@ -61,15 +63,15 @@ where } /// Performs RFM95-specific SPI register access - fn register(&mut self, operation: u8, address: u8, payload: u8) -> Result { + fn register(&mut self, operation: u8, address: u8, payload: u8) -> Result { // Build command let address = address & 0b0111_1111; let mut command = [operation | address, payload]; // Do transaction - self.select.set_low().map_err(|_| "Failed to pull chip-select line to low")?; - self.bus.transfer_in_place(&mut command).map_err(|_| "Failed to do SPI transaction")?; - self.select.set_high().map_err(|_| "Failed to pull chip-select line to high")?; + self.select.set_low().map_err(|_| err!(eio: "Failed to pull chip-select line to low"))?; + self.bus.transfer_in_place(&mut command).map_err(|_| err!(eio: "Failed to do SPI transaction"))?; + self.select.set_high().map_err(|_| err!(eio: "Failed to pull chip-select line to high"))?; // SPI debug callback #[cfg(feature = "debug")] diff --git a/rfm95/src/rfm95/driver.rs b/rfm95/src/rfm95/driver.rs index b5e4db2..deb2e76 100644 --- a/rfm95/src/rfm95/driver.rs +++ b/rfm95/src/rfm95/driver.rs @@ -1,5 +1,7 @@ //! RFM95 driver for LoRa operations +use crate::err; +use crate::error::Error; use crate::lora::airtime; use crate::lora::config::Config; use crate::lora::types::*; @@ -57,17 +59,17 @@ where /// # Important /// The RFM95 modem is initialized to LoRa-mode and put to standby. All other configurations are left untouched, so /// you probably want to configure the modem initially (also see [`Self::set_config`]). - pub fn new(bus: Bus, select: Select, mut reset: R, mut timer: T) -> Result + pub fn new(bus: Bus, select: Select, mut reset: R, mut timer: T) -> Result where R: OutputPin, T: DelayNs, { // Pull reset to low and wait until the reset is triggered - reset.set_low().map_err(|_| "Failed to pull reset line to low")?; + reset.set_low().map_err(|_| err!(eio: "Failed to pull reset line to low"))?; timer.delay_ms(1); // Pull reset to high again and give the chip some time to boot - reset.set_high().map_err(|_| "Failed to pull reset line to high")?; + reset.set_high().map_err(|_| err!(eio: "Failed to pull reset line to high"))?; timer.delay_ms(10); // Validate chip revision to assure the protocol matches @@ -78,7 +80,7 @@ where let silicon_revision = wire.read(RegVersion)?; let true = Self::SUPPORTED_SILICON_REVISIONS.contains(&silicon_revision) else { // Raise an error here since other revisions may be incompatible - return Err("Unsupported silicon revision"); + return Err(err!(einval: "Unsupported silicon revision")); }; } @@ -98,7 +100,7 @@ where } /// Applies the given config (useful for initialization) - pub fn set_config(&mut self, config: &Config) -> Result<(), &'static str> { + pub fn set_config(&mut self, config: &Config) -> Result<(), Error> { self.set_spreading_factor(config.spreading_factor())?; self.set_bandwidth(config.bandwidth())?; self.set_coding_rate(config.coding_rate())?; @@ -112,12 +114,12 @@ where } /// The current spreading factor - pub fn spreading_factor(&mut self) -> Result { + pub fn spreading_factor(&mut self) -> Result { let spreading_factor = self.spi.read(RegModemConfig2SpreadingFactor)?; SpreadingFactor::try_from(spreading_factor) } /// Set the spreading factor - pub fn set_spreading_factor(&mut self, spreading_factor: T) -> Result<(), &'static str> + pub fn set_spreading_factor(&mut self, spreading_factor: T) -> Result<(), Error> where T: Into, { @@ -133,12 +135,12 @@ where } /// The current bandwidth - pub fn bandwidth(&mut self) -> Result { + pub fn bandwidth(&mut self) -> Result { let bandwidth = self.spi.read(RegModemConfig1Bw)?; Bandwidth::try_from(bandwidth) } /// Sets the bandwidth - pub fn set_bandwidth(&mut self, bandwidth: T) -> Result<(), &'static str> + pub fn set_bandwidth(&mut self, bandwidth: T) -> Result<(), Error> where T: Into, { @@ -154,12 +156,12 @@ where } /// The current coding rate - pub fn coding_rate(&mut self) -> Result { + pub fn coding_rate(&mut self) -> Result { let coding_rate = self.spi.read(RegModemConfig1CodingRate)?; CodingRate::try_from(coding_rate) } /// Sets the coding rate - pub fn set_coding_rate(&mut self, coding_rate: T) -> Result<(), &'static str> + pub fn set_coding_rate(&mut self, coding_rate: T) -> Result<(), Error> where T: Into, { @@ -168,12 +170,12 @@ where } /// The current IQ polarity - pub fn polarity(&mut self) -> Result { + pub fn polarity(&mut self) -> Result { let polarity = self.spi.read(RegInvertIQ)?; Polarity::try_from(polarity) } /// Sets the IQ polarity - pub fn set_polarity(&mut self, polarity: T) -> Result<(), &'static str> + pub fn set_polarity(&mut self, polarity: T) -> Result<(), Error> where T: Into, { @@ -182,12 +184,12 @@ where } /// The current header mode - pub fn header_mode(&mut self) -> Result { + pub fn header_mode(&mut self) -> Result { let header_mode = self.spi.read(RegModemConfig1ImplicitHeaderModeOn)?; HeaderMode::try_from(header_mode) } /// Sets the header mode - pub fn set_header_mode(&mut self, header_mode: T) -> Result<(), &'static str> + pub fn set_header_mode(&mut self, header_mode: T) -> Result<(), Error> where T: Into, { @@ -196,12 +198,12 @@ where } /// The current CRC mode - pub fn crc_mode(&mut self) -> Result { + pub fn crc_mode(&mut self) -> Result { let crc_mode = self.spi.read(RegModemConfig2RxPayloadCrcOn)?; CrcMode::try_from(crc_mode) } /// Sets the CRC mode - pub fn set_crc_mode(&mut self, crc: T) -> Result<(), &'static str> + pub fn set_crc_mode(&mut self, crc: T) -> Result<(), Error> where T: Into, { @@ -210,12 +212,12 @@ where } /// The current sync word - pub fn sync_word(&mut self) -> Result { + pub fn sync_word(&mut self) -> Result { let sync_word = self.spi.read(RegSyncWord)?; Ok(SyncWord::new(sync_word)) } /// Sets the sync word - pub fn set_sync_word(&mut self, sync_word: T) -> Result<(), &'static str> + pub fn set_sync_word(&mut self, sync_word: T) -> Result<(), Error> where T: Into, { @@ -224,7 +226,7 @@ where } /// The current preamble length - pub fn preamble_len(&mut self) -> Result { + pub fn preamble_len(&mut self) -> Result { // Read registers let preamble_len_msb = self.spi.read(RegPreambleMsb)?; let preamble_len_lsb = self.spi.read(RegPreambleLsb)?; @@ -234,7 +236,7 @@ where Ok(PreambleLength::new(preamble_len)) } /// Sets the preamble length - pub fn set_preamble_len(&mut self, len: T) -> Result<(), &'static str> + pub fn set_preamble_len(&mut self, len: T) -> Result<(), Error> where T: Into, { @@ -244,7 +246,7 @@ where } /// The current frequency - pub fn frequency(&mut self) -> Result { + pub fn frequency(&mut self) -> Result { // Read frequency from registers let frequency_msb = self.spi.read(RegFrMsb)?; let frequency_mid = self.spi.read(RegFrMid)?; @@ -258,7 +260,7 @@ where Ok(Frequency::hz(frequency)) } /// Sets the frequency - pub fn set_frequency(&mut self, frequency: T) -> Result<(), &'static str> + pub fn set_frequency(&mut self, frequency: T) -> Result<(), Error> where T: Into, { @@ -286,11 +288,11 @@ where /// # Non-Blocking /// This functions schedules the TX operation and returns immediately. To check if the TX operation is done, use /// [`Self::complete_tx`]. - pub fn start_tx(&mut self, data: &[u8]) -> Result<(), &'static str> { + pub fn start_tx(&mut self, data: &[u8]) -> Result<(), Error> { // Validate input length let 1..=RFM95_FIFO_SIZE = data.len() else { // The message is empty or too long - return Err("Invalid TX data length"); + return Err(err!(einval: "Invalid TX data length")); }; // Copy packet into FIFO... @@ -314,7 +316,7 @@ where /// /// # Non-Blocking /// This function is non-blocking. If the TX operation is not done yet, it returns `Ok(None)`. - pub fn complete_tx(&mut self) -> Result, &'static str> { + pub fn complete_tx(&mut self) -> Result, Error> { // Check for TX done let 0b1 = self.spi.read(RegIrqFlagsTxDone)? else { // The TX operation has not been completed yet @@ -338,7 +340,7 @@ where /// the maximum timeout, we take the configured [`Self::spreading_factor`] and [`Self::bandwidth`], and get the /// duration of a single symbol via [`crate::lora::airtime::symbol_airtime`]. The maximum timeout is the duration of /// a single symbol, multiplied with `1023`. - pub fn rx_timeout_max(&mut self) -> Result { + pub fn rx_timeout_max(&mut self) -> Result { // Get current config let spreading_factor = self.spreading_factor()?; let bandwidth = self.bandwidth()?; @@ -356,7 +358,7 @@ where /// # Maximum Timeout /// The RFM95 timeout counter works by counting symbols, and is thus dependent on the configured spreading factor /// and bandwidth. See also [`Self::rx_timeout_max`]. - pub fn start_rx(&mut self, timeout: Duration) -> Result<(), &'static str> { + pub fn start_rx(&mut self, timeout: Duration) -> Result<(), Error> { // Get the current symbol airtime in microseconds let spreading_factor = self.spreading_factor()?; let bandwidth = self.bandwidth()?; @@ -364,10 +366,10 @@ where let symbol_airtime_micros = symbol_airtime.as_micros() as i32; // Compute the raw timeout - let timeout_micros = i32::try_from(timeout.as_micros()).map_err(|_| "Timeout is too long")?; + let timeout_micros = i32::try_from(timeout.as_micros()).map_err(|_| err!(einval: "Timeout is too long"))?; let timeout_symbols @ 0..1024 = airtime::ceildiv(timeout_micros, symbol_airtime_micros) as u32 else { // This timeout is too large to be configured - return Err("Effective timeout is too large"); + return Err(err!(einval: "Effective timeout is too large")); }; // Configure the timeout and reset the address pointer @@ -398,15 +400,15 @@ where /// # Timeout or CRC errors /// If the receive operation times out or the received message is corrupt, #[allow(clippy::missing_panics_doc, reason = "The panic should never occur during regular operation")] - pub fn complete_rx(&mut self, buf: &mut [u8]) -> Result, &'static str> { + pub fn complete_rx(&mut self, buf: &mut [u8]) -> Result, Error> { // Check for errors let 0b0 = self.spi.read(RegIrqFlagsRxTimeout)? else { // The RX operation has timeouted - return Err("RX timeout"); + return Err(err!(etimedout: "RX timeout")); }; let 0b0 = self.spi.read(RegIrqFlagsPayloadCrcError)? else { // The RX operation has failed - return Err("RX CRC error"); + return Err(err!(ebadmsg: "RX CRC error")); }; // Check for RX done @@ -437,7 +439,7 @@ where /// Dumps all used registers; usefule for debugging purposes #[cfg(feature = "debug")] - pub fn dump_registers(&mut self) -> Result<[u8; REGISTER_MAX as usize + 1], &'static str> { + pub fn dump_registers(&mut self) -> Result<[u8; REGISTER_MAX as usize + 1], Error> { // A dynamic register for dumping purposes struct DynamicRegister(u8); impl Register for DynamicRegister { @@ -457,7 +459,7 @@ where } /// Dumps the entire FIFO contents #[cfg(feature = "debug")] - pub fn dump_fifo(&mut self) -> Result<[u8; RFM95_FIFO_SIZE], &'static str> { + pub fn dump_fifo(&mut self) -> Result<[u8; RFM95_FIFO_SIZE], Error> { // Save FIFO position let fifo_position = self.spi.read(RegFifoAddrPtr)?; From 0ccedeac03b37ecde5fd565880825bc744fc406d Mon Sep 17 00:00:00 2001 From: Keziah Biermann Date: Tue, 18 Mar 2025 18:22:57 +0100 Subject: [PATCH 2/5] More distinct error types --- rfm95/src/error.rs | 138 ++++++++++++++++++++++++---------- rfm95/src/lora/types.rs | 56 ++++++-------- rfm95/src/rfm95/connection.rs | 14 ++-- rfm95/src/rfm95/driver.rs | 88 +++++++++++----------- 4 files changed, 174 insertions(+), 122 deletions(-) diff --git a/rfm95/src/error.rs b/rfm95/src/error.rs index 27b5bab..04858c9 100644 --- a/rfm95/src/error.rs +++ b/rfm95/src/error.rs @@ -1,54 +1,65 @@ -//! The crate's error type +//! The crate's error types /// Creates an error #[macro_export] macro_rules! err { - ($kind:expr, $desc:expr) => {{ - #[cfg(feature = "backtrace")] - { - // Create an error with rich backtrace information - $crate::error::Error { kind: $kind, file: file!(), line: line!(), description: $desc } + ($kind:tt, $desc:expr) => {{ + $kind { + #[cfg(feature = "backtrace")] + file: file!(), + #[cfg(feature = "backtrace")] + line: line!(), + #[cfg(feature = "backtrace")] + description: $desc, } - - #[cfg(not(feature = "backtrace"))] - { - // Create a size-optimized error - $crate::error::Error { kind: $kind } - } - }}; - (eio: $desc:expr) => {{ - err!($crate::error::ErrorKind::IoError, $desc) - }}; - (etimedout: $desc:expr) => {{ - err!($crate::error::ErrorKind::Timeout, $desc) - }}; - (ebadmsg: $desc:expr) => {{ - err!($crate::error::ErrorKind::InvalidMessage, $desc) - }}; - (einval: $desc:expr) => {{ - err!($crate::error::ErrorKind::InvalidValue, $desc) }}; } -/// The error kind -#[derive(Debug, PartialEq, Eq)] -#[repr(u8)] -pub enum ErrorKind { - /// An SPI or reset-related I/O error - IoError, - /// A timeout occurred - Timeout, - /// A CRC validation failed - InvalidMessage, - /// A function argument is invalid - InvalidValue, +/// An I/O error +#[derive(Debug, Clone, Copy)] +pub struct IoError { + /// The file where the error was created + #[cfg(feature = "backtrace")] + pub file: &'static str, + /// The line at which the error was created + #[cfg(feature = "backtrace")] + pub line: u32, + /// A human readable error description + #[cfg(feature = "backtrace")] + pub description: &'static str, +} + +/// A timeout error +#[derive(Debug, Clone, Copy)] +pub struct TimeoutError { + /// The file where the error was created + #[cfg(feature = "backtrace")] + pub file: &'static str, + /// The line at which the error was created + #[cfg(feature = "backtrace")] + pub line: u32, + /// A human readable error description + #[cfg(feature = "backtrace")] + pub description: &'static str, +} + +/// A CRC-validation or format error +#[derive(Debug, Clone, Copy)] +pub struct InvalidMessageError { + /// The file where the error was created + #[cfg(feature = "backtrace")] + pub file: &'static str, + /// The line at which the error was created + #[cfg(feature = "backtrace")] + pub line: u32, + /// A human readable error description + #[cfg(feature = "backtrace")] + pub description: &'static str, } -/// The crate's error type -#[derive(Debug)] -pub struct Error { - /// The error kind - pub kind: ErrorKind, +/// An invalid-argument error +#[derive(Debug, Clone, Copy)] +pub struct InvalidArgumentError { /// The file where the error was created #[cfg(feature = "backtrace")] pub file: &'static str, @@ -59,3 +70,48 @@ pub struct Error { #[cfg(feature = "backtrace")] pub description: &'static str, } + +/// An RX- or TX-start error +#[derive(Debug, Clone, Copy)] +pub enum RxTxStartError { + /// A low-level SPI or reset-related I/O error + WireIoError(IoError), + /// An invalid-argument error + InvalidArgumentError(InvalidArgumentError), +} +impl From for RxTxStartError { + fn from(error: IoError) -> Self { + Self::WireIoError(error) + } +} +impl From for RxTxStartError { + fn from(error: InvalidArgumentError) -> Self { + Self::InvalidArgumentError(error) + } +} + +/// An RX-completion specific error +#[derive(Debug, Clone, Copy)] +pub enum RxCompleteError { + /// A low-level SPI or reset-related I/O error + WireIoError(IoError), + /// A timeout error + TimeoutError(TimeoutError), + /// A CRC-validation or format error + InvalidMessageError(InvalidMessageError), +} +impl From for RxCompleteError { + fn from(error: IoError) -> Self { + Self::WireIoError(error) + } +} +impl From for RxCompleteError { + fn from(error: TimeoutError) -> Self { + Self::TimeoutError(error) + } +} +impl From for RxCompleteError { + fn from(error: InvalidMessageError) -> Self { + Self::InvalidMessageError(error) + } +} diff --git a/rfm95/src/lora/types.rs b/rfm95/src/lora/types.rs index 703e5d1..b541e06 100644 --- a/rfm95/src/lora/types.rs +++ b/rfm95/src/lora/types.rs @@ -1,7 +1,7 @@ //! Small wrappers for type safety use crate::err; -use crate::error::Error; +use crate::error::IoError; /// A LoRa spreading factor /// @@ -28,10 +28,9 @@ pub enum SpreadingFactor { /// Spreading factor 12 aka 4096 chirps per symbol S12 = 12, } -impl TryFrom for SpreadingFactor { - type Error = Error; - - fn try_from(value: u8) -> Result { +impl SpreadingFactor { + /// Parses `self` from a register value + pub(crate) fn parse(value: u8) -> Result { match value { sf if sf == Self::S7 as u8 => Ok(Self::S7), sf if sf == Self::S8 as u8 => Ok(Self::S8), @@ -39,7 +38,7 @@ impl TryFrom for SpreadingFactor { sf if sf == Self::S10 as u8 => Ok(Self::S10), sf if sf == Self::S11 as u8 => Ok(Self::S11), sf if sf == Self::S12 as u8 => Ok(Self::S12), - _ => Err(err!(einval: "Invalid or unsupported spreading factor")), + _ => Err(err!(IoError, "Invalid or unsupported spreading factor")), } } } @@ -73,10 +72,9 @@ pub enum Bandwidth { /// 7.8 kHz bandwidth B7_8 = 0b0000, } -impl TryFrom for Bandwidth { - type Error = Error; - - fn try_from(value: u8) -> Result { +impl Bandwidth { + /// Parses `self` from a register value + pub(crate) fn parse(value: u8) -> Result { match value { bw if bw == Self::B500 as u8 => Ok(Self::B500), bw if bw == Self::B250 as u8 => Ok(Self::B250), @@ -88,7 +86,7 @@ impl TryFrom for Bandwidth { bw if bw == Self::B15_6 as u8 => Ok(Self::B15_6), bw if bw == Self::B10_4 as u8 => Ok(Self::B10_4), bw if bw == Self::B7_8 as u8 => Ok(Self::B7_8), - _ => Err(err!(einval: "Invalid or unsupported bandwidth")), + _ => Err(err!(IoError, "Invalid or unsupported bandwidth")), } } } @@ -110,16 +108,15 @@ pub enum CodingRate { /// Coding rate 4/8 aka 2x overhead C4_8 = 0b100, } -impl TryFrom for CodingRate { - type Error = Error; - - fn try_from(value: u8) -> Result { +impl CodingRate { + /// Parses `self` from a register value + pub(crate) fn parse(value: u8) -> Result { match value { cr if cr == Self::C4_5 as u8 => Ok(Self::C4_5), cr if cr == Self::C4_6 as u8 => Ok(Self::C4_6), cr if cr == Self::C4_7 as u8 => Ok(Self::C4_7), cr if cr == Self::C4_8 as u8 => Ok(Self::C4_8), - _ => Err(err!(einval: "Invalid coding rate")), + _ => Err(err!(IoError, "Invalid coding rate")), } } } @@ -137,14 +134,13 @@ pub enum Polarity { /// Inverted polarity, usually used for downlinks Inverted = 1, } -impl TryFrom for Polarity { - type Error = Error; - - fn try_from(value: u8) -> Result { +impl Polarity { + /// Parses `self` from a register value + pub(crate) fn parse(value: u8) -> Result { match value { polarity if polarity == Self::Normal as u8 => Ok(Self::Normal), polarity if polarity == Self::Inverted as u8 => Ok(Self::Inverted), - _ => Err(err!(einval: "Invalid IQ polarity value")), + _ => Err(err!(IoError, "Invalid IQ polarity value")), } } } @@ -162,14 +158,13 @@ pub enum HeaderMode { /// Implicit header mode to omit the header if decoding parameters are known Implicit = 1, } -impl TryFrom for HeaderMode { - type Error = Error; - - fn try_from(value: u8) -> Result { +impl HeaderMode { + /// Parses `self` from a register value + pub(crate) fn parse(value: u8) -> Result { match value { mode if mode == Self::Explicit as u8 => Ok(Self::Explicit), mode if mode == Self::Implicit as u8 => Ok(Self::Implicit), - _ => Err(err!(einval: "Invalid header mode")), + _ => Err(err!(IoError, "Invalid header mode")), } } } @@ -187,14 +182,13 @@ pub enum CrcMode { /// CRC enabled Enabled = 1, } -impl TryFrom for CrcMode { - type Error = Error; - - fn try_from(value: u8) -> Result { +impl CrcMode { + /// Parses `self` from a register value + pub(crate) fn parse(value: u8) -> Result { match value { mode if mode == Self::Disabled as u8 => Ok(Self::Disabled), mode if mode == Self::Enabled as u8 => Ok(Self::Enabled), - _ => Err(err!(einval: "Invalid CRC mode")), + _ => Err(err!(IoError, "Invalid CRC mode")), } } } diff --git a/rfm95/src/rfm95/connection.rs b/rfm95/src/rfm95/connection.rs index 6aec7d5..9ec9947 100644 --- a/rfm95/src/rfm95/connection.rs +++ b/rfm95/src/rfm95/connection.rs @@ -1,7 +1,7 @@ //! RFM95 SPI connection use crate::err; -use crate::error::Error; +use crate::error::IoError; use crate::rfm95::registers::Register; use core::fmt::{Debug, Formatter}; use embedded_hal::digital::OutputPin; @@ -34,7 +34,7 @@ where } /// Reads a RFM95 register via SPI - pub fn read(&mut self, register: T) -> Result + pub fn read(&mut self, register: T) -> Result where T: Register, { @@ -43,7 +43,7 @@ where Ok((register_value & register.mask()) >> register.offset()) } /// Updates a RFM95 register via SPI - pub fn write(&mut self, register: T, value: u8) -> Result<(), Error> + pub fn write(&mut self, register: T, value: u8) -> Result<(), IoError> where T: Register, { @@ -63,15 +63,15 @@ where } /// Performs RFM95-specific SPI register access - fn register(&mut self, operation: u8, address: u8, payload: u8) -> Result { + fn register(&mut self, operation: u8, address: u8, payload: u8) -> Result { // Build command let address = address & 0b0111_1111; let mut command = [operation | address, payload]; // Do transaction - self.select.set_low().map_err(|_| err!(eio: "Failed to pull chip-select line to low"))?; - self.bus.transfer_in_place(&mut command).map_err(|_| err!(eio: "Failed to do SPI transaction"))?; - self.select.set_high().map_err(|_| err!(eio: "Failed to pull chip-select line to high"))?; + self.select.set_low().map_err(|_| err!(IoError, "Failed to pull chip-select line to low"))?; + self.bus.transfer_in_place(&mut command).map_err(|_| err!(IoError, "Failed to do SPI transaction"))?; + self.select.set_high().map_err(|_| err!(IoError, "Failed to pull chip-select line to high"))?; // SPI debug callback #[cfg(feature = "debug")] diff --git a/rfm95/src/rfm95/driver.rs b/rfm95/src/rfm95/driver.rs index deb2e76..f737255 100644 --- a/rfm95/src/rfm95/driver.rs +++ b/rfm95/src/rfm95/driver.rs @@ -1,7 +1,7 @@ //! RFM95 driver for LoRa operations use crate::err; -use crate::error::Error; +use crate::error::{InvalidArgumentError, InvalidMessageError, IoError, RxCompleteError, RxTxStartError, TimeoutError}; use crate::lora::airtime; use crate::lora::config::Config; use crate::lora::types::*; @@ -59,17 +59,17 @@ where /// # Important /// The RFM95 modem is initialized to LoRa-mode and put to standby. All other configurations are left untouched, so /// you probably want to configure the modem initially (also see [`Self::set_config`]). - pub fn new(bus: Bus, select: Select, mut reset: R, mut timer: T) -> Result + pub fn new(bus: Bus, select: Select, mut reset: R, mut timer: T) -> Result where R: OutputPin, T: DelayNs, { // Pull reset to low and wait until the reset is triggered - reset.set_low().map_err(|_| err!(eio: "Failed to pull reset line to low"))?; + reset.set_low().map_err(|_| err!(IoError, "Failed to pull reset line to low"))?; timer.delay_ms(1); // Pull reset to high again and give the chip some time to boot - reset.set_high().map_err(|_| err!(eio: "Failed to pull reset line to high"))?; + reset.set_high().map_err(|_| err!(IoError, "Failed to pull reset line to high"))?; timer.delay_ms(10); // Validate chip revision to assure the protocol matches @@ -80,7 +80,7 @@ where let silicon_revision = wire.read(RegVersion)?; let true = Self::SUPPORTED_SILICON_REVISIONS.contains(&silicon_revision) else { // Raise an error here since other revisions may be incompatible - return Err(err!(einval: "Unsupported silicon revision")); + return Err(err!(IoError, "Unsupported silicon revision")); }; } @@ -100,7 +100,7 @@ where } /// Applies the given config (useful for initialization) - pub fn set_config(&mut self, config: &Config) -> Result<(), Error> { + pub fn set_config(&mut self, config: &Config) -> Result<(), IoError> { self.set_spreading_factor(config.spreading_factor())?; self.set_bandwidth(config.bandwidth())?; self.set_coding_rate(config.coding_rate())?; @@ -114,12 +114,13 @@ where } /// The current spreading factor - pub fn spreading_factor(&mut self) -> Result { - let spreading_factor = self.spi.read(RegModemConfig2SpreadingFactor)?; - SpreadingFactor::try_from(spreading_factor) + pub fn spreading_factor(&mut self) -> Result { + let spreading_factor_raw = self.spi.read(RegModemConfig2SpreadingFactor)?; + let spreading_factor = SpreadingFactor::parse(spreading_factor_raw)?; + Ok(spreading_factor) } /// Set the spreading factor - pub fn set_spreading_factor(&mut self, spreading_factor: T) -> Result<(), Error> + pub fn set_spreading_factor(&mut self, spreading_factor: T) -> Result<(), IoError> where T: Into, { @@ -135,12 +136,12 @@ where } /// The current bandwidth - pub fn bandwidth(&mut self) -> Result { + pub fn bandwidth(&mut self) -> Result { let bandwidth = self.spi.read(RegModemConfig1Bw)?; - Bandwidth::try_from(bandwidth) + Bandwidth::parse(bandwidth) } /// Sets the bandwidth - pub fn set_bandwidth(&mut self, bandwidth: T) -> Result<(), Error> + pub fn set_bandwidth(&mut self, bandwidth: T) -> Result<(), IoError> where T: Into, { @@ -156,12 +157,12 @@ where } /// The current coding rate - pub fn coding_rate(&mut self) -> Result { + pub fn coding_rate(&mut self) -> Result { let coding_rate = self.spi.read(RegModemConfig1CodingRate)?; - CodingRate::try_from(coding_rate) + CodingRate::parse(coding_rate) } /// Sets the coding rate - pub fn set_coding_rate(&mut self, coding_rate: T) -> Result<(), Error> + pub fn set_coding_rate(&mut self, coding_rate: T) -> Result<(), IoError> where T: Into, { @@ -170,12 +171,12 @@ where } /// The current IQ polarity - pub fn polarity(&mut self) -> Result { + pub fn polarity(&mut self) -> Result { let polarity = self.spi.read(RegInvertIQ)?; - Polarity::try_from(polarity) + Polarity::parse(polarity) } /// Sets the IQ polarity - pub fn set_polarity(&mut self, polarity: T) -> Result<(), Error> + pub fn set_polarity(&mut self, polarity: T) -> Result<(), IoError> where T: Into, { @@ -184,12 +185,12 @@ where } /// The current header mode - pub fn header_mode(&mut self) -> Result { + pub fn header_mode(&mut self) -> Result { let header_mode = self.spi.read(RegModemConfig1ImplicitHeaderModeOn)?; - HeaderMode::try_from(header_mode) + HeaderMode::parse(header_mode) } /// Sets the header mode - pub fn set_header_mode(&mut self, header_mode: T) -> Result<(), Error> + pub fn set_header_mode(&mut self, header_mode: T) -> Result<(), IoError> where T: Into, { @@ -198,12 +199,12 @@ where } /// The current CRC mode - pub fn crc_mode(&mut self) -> Result { + pub fn crc_mode(&mut self) -> Result { let crc_mode = self.spi.read(RegModemConfig2RxPayloadCrcOn)?; - CrcMode::try_from(crc_mode) + CrcMode::parse(crc_mode) } /// Sets the CRC mode - pub fn set_crc_mode(&mut self, crc: T) -> Result<(), Error> + pub fn set_crc_mode(&mut self, crc: T) -> Result<(), IoError> where T: Into, { @@ -212,12 +213,12 @@ where } /// The current sync word - pub fn sync_word(&mut self) -> Result { + pub fn sync_word(&mut self) -> Result { let sync_word = self.spi.read(RegSyncWord)?; Ok(SyncWord::new(sync_word)) } /// Sets the sync word - pub fn set_sync_word(&mut self, sync_word: T) -> Result<(), Error> + pub fn set_sync_word(&mut self, sync_word: T) -> Result<(), IoError> where T: Into, { @@ -226,7 +227,7 @@ where } /// The current preamble length - pub fn preamble_len(&mut self) -> Result { + pub fn preamble_len(&mut self) -> Result { // Read registers let preamble_len_msb = self.spi.read(RegPreambleMsb)?; let preamble_len_lsb = self.spi.read(RegPreambleLsb)?; @@ -236,7 +237,7 @@ where Ok(PreambleLength::new(preamble_len)) } /// Sets the preamble length - pub fn set_preamble_len(&mut self, len: T) -> Result<(), Error> + pub fn set_preamble_len(&mut self, len: T) -> Result<(), IoError> where T: Into, { @@ -246,7 +247,7 @@ where } /// The current frequency - pub fn frequency(&mut self) -> Result { + pub fn frequency(&mut self) -> Result { // Read frequency from registers let frequency_msb = self.spi.read(RegFrMsb)?; let frequency_mid = self.spi.read(RegFrMid)?; @@ -260,7 +261,7 @@ where Ok(Frequency::hz(frequency)) } /// Sets the frequency - pub fn set_frequency(&mut self, frequency: T) -> Result<(), Error> + pub fn set_frequency(&mut self, frequency: T) -> Result<(), IoError> where T: Into, { @@ -288,11 +289,11 @@ where /// # Non-Blocking /// This functions schedules the TX operation and returns immediately. To check if the TX operation is done, use /// [`Self::complete_tx`]. - pub fn start_tx(&mut self, data: &[u8]) -> Result<(), Error> { + pub fn start_tx(&mut self, data: &[u8]) -> Result<(), RxTxStartError> { // Validate input length let 1..=RFM95_FIFO_SIZE = data.len() else { // The message is empty or too long - return Err(err!(einval: "Invalid TX data length")); + return Err(err!(InvalidArgumentError, "Invalid TX data length"))?; }; // Copy packet into FIFO... @@ -316,7 +317,7 @@ where /// /// # Non-Blocking /// This function is non-blocking. If the TX operation is not done yet, it returns `Ok(None)`. - pub fn complete_tx(&mut self) -> Result, Error> { + pub fn complete_tx(&mut self) -> Result, IoError> { // Check for TX done let 0b1 = self.spi.read(RegIrqFlagsTxDone)? else { // The TX operation has not been completed yet @@ -340,7 +341,7 @@ where /// the maximum timeout, we take the configured [`Self::spreading_factor`] and [`Self::bandwidth`], and get the /// duration of a single symbol via [`crate::lora::airtime::symbol_airtime`]. The maximum timeout is the duration of /// a single symbol, multiplied with `1023`. - pub fn rx_timeout_max(&mut self) -> Result { + pub fn rx_timeout_max(&mut self) -> Result { // Get current config let spreading_factor = self.spreading_factor()?; let bandwidth = self.bandwidth()?; @@ -358,7 +359,7 @@ where /// # Maximum Timeout /// The RFM95 timeout counter works by counting symbols, and is thus dependent on the configured spreading factor /// and bandwidth. See also [`Self::rx_timeout_max`]. - pub fn start_rx(&mut self, timeout: Duration) -> Result<(), Error> { + pub fn start_rx(&mut self, timeout: Duration) -> Result<(), RxTxStartError> { // Get the current symbol airtime in microseconds let spreading_factor = self.spreading_factor()?; let bandwidth = self.bandwidth()?; @@ -366,10 +367,11 @@ where let symbol_airtime_micros = symbol_airtime.as_micros() as i32; // Compute the raw timeout - let timeout_micros = i32::try_from(timeout.as_micros()).map_err(|_| err!(einval: "Timeout is too long"))?; + let timeout_micros = + i32::try_from(timeout.as_micros()).map_err(|_| err!(InvalidArgumentError, "Timeout is too long"))?; let timeout_symbols @ 0..1024 = airtime::ceildiv(timeout_micros, symbol_airtime_micros) as u32 else { // This timeout is too large to be configured - return Err(err!(einval: "Effective timeout is too large")); + return Err(err!(InvalidArgumentError, "Effective timeout is too large"))?; }; // Configure the timeout and reset the address pointer @@ -400,15 +402,15 @@ where /// # Timeout or CRC errors /// If the receive operation times out or the received message is corrupt, #[allow(clippy::missing_panics_doc, reason = "The panic should never occur during regular operation")] - pub fn complete_rx(&mut self, buf: &mut [u8]) -> Result, Error> { + pub fn complete_rx(&mut self, buf: &mut [u8]) -> Result, RxCompleteError> { // Check for errors let 0b0 = self.spi.read(RegIrqFlagsRxTimeout)? else { // The RX operation has timeouted - return Err(err!(etimedout: "RX timeout")); + return Err(err!(TimeoutError, "RX timeout"))?; }; let 0b0 = self.spi.read(RegIrqFlagsPayloadCrcError)? else { // The RX operation has failed - return Err(err!(ebadmsg: "RX CRC error")); + return Err(err!(InvalidMessageError, "RX CRC error"))?; }; // Check for RX done @@ -439,7 +441,7 @@ where /// Dumps all used registers; usefule for debugging purposes #[cfg(feature = "debug")] - pub fn dump_registers(&mut self) -> Result<[u8; REGISTER_MAX as usize + 1], Error> { + pub fn dump_registers(&mut self) -> Result<[u8; REGISTER_MAX as usize + 1], IoError> { // A dynamic register for dumping purposes struct DynamicRegister(u8); impl Register for DynamicRegister { @@ -459,7 +461,7 @@ where } /// Dumps the entire FIFO contents #[cfg(feature = "debug")] - pub fn dump_fifo(&mut self) -> Result<[u8; RFM95_FIFO_SIZE], Error> { + pub fn dump_fifo(&mut self) -> Result<[u8; RFM95_FIFO_SIZE], IoError> { // Save FIFO position let fifo_position = self.spi.read(RegFifoAddrPtr)?; From 0e4682a031ec3cc8b3216fe415e0408ce50150dc Mon Sep 17 00:00:00 2001 From: Keziah Biermann Date: Tue, 18 Mar 2025 18:25:08 +0100 Subject: [PATCH 3/5] Fix variant naming --- rfm95/src/error.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/rfm95/src/error.rs b/rfm95/src/error.rs index 04858c9..bf04a6b 100644 --- a/rfm95/src/error.rs +++ b/rfm95/src/error.rs @@ -74,14 +74,14 @@ pub struct InvalidArgumentError { /// An RX- or TX-start error #[derive(Debug, Clone, Copy)] pub enum RxTxStartError { - /// A low-level SPI or reset-related I/O error - WireIoError(IoError), + /// An I/O error + IoError(IoError), /// An invalid-argument error InvalidArgumentError(InvalidArgumentError), } impl From for RxTxStartError { fn from(error: IoError) -> Self { - Self::WireIoError(error) + Self::IoError(error) } } impl From for RxTxStartError { @@ -93,8 +93,8 @@ impl From for RxTxStartError { /// An RX-completion specific error #[derive(Debug, Clone, Copy)] pub enum RxCompleteError { - /// A low-level SPI or reset-related I/O error - WireIoError(IoError), + /// An I/O error + IoError(IoError), /// A timeout error TimeoutError(TimeoutError), /// A CRC-validation or format error @@ -102,7 +102,7 @@ pub enum RxCompleteError { } impl From for RxCompleteError { fn from(error: IoError) -> Self { - Self::WireIoError(error) + Self::IoError(error) } } impl From for RxCompleteError { From 2be4d3ae87f6eaa067f36e89e89b94b29c592356 Mon Sep 17 00:00:00 2001 From: Keziah Biermann Date: Tue, 18 Mar 2025 18:29:38 +0100 Subject: [PATCH 4/5] Don't bundle RX- and TX-start errors --- rfm95/src/error.rs | 27 +++++++++++++++++++++++---- rfm95/src/rfm95/driver.rs | 8 +++++--- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/rfm95/src/error.rs b/rfm95/src/error.rs index bf04a6b..5fdc855 100644 --- a/rfm95/src/error.rs +++ b/rfm95/src/error.rs @@ -71,20 +71,39 @@ pub struct InvalidArgumentError { pub description: &'static str, } -/// An RX- or TX-start error +/// An TX-start error #[derive(Debug, Clone, Copy)] -pub enum RxTxStartError { +pub enum TxStartError { /// An I/O error IoError(IoError), /// An invalid-argument error InvalidArgumentError(InvalidArgumentError), } -impl From for RxTxStartError { +impl From for TxStartError { fn from(error: IoError) -> Self { Self::IoError(error) } } -impl From for RxTxStartError { +impl From for TxStartError { + fn from(error: InvalidArgumentError) -> Self { + Self::InvalidArgumentError(error) + } +} + +/// An RX-start error +#[derive(Debug, Clone, Copy)] +pub enum RxStartError { + /// An I/O error + IoError(IoError), + /// An invalid-argument error + InvalidArgumentError(InvalidArgumentError), +} +impl From for RxStartError { + fn from(error: IoError) -> Self { + Self::IoError(error) + } +} +impl From for RxStartError { fn from(error: InvalidArgumentError) -> Self { Self::InvalidArgumentError(error) } diff --git a/rfm95/src/rfm95/driver.rs b/rfm95/src/rfm95/driver.rs index f737255..9d2c8a5 100644 --- a/rfm95/src/rfm95/driver.rs +++ b/rfm95/src/rfm95/driver.rs @@ -1,7 +1,9 @@ //! RFM95 driver for LoRa operations use crate::err; -use crate::error::{InvalidArgumentError, InvalidMessageError, IoError, RxCompleteError, RxTxStartError, TimeoutError}; +use crate::error::{ + InvalidArgumentError, InvalidMessageError, IoError, RxCompleteError, RxStartError, TimeoutError, TxStartError, +}; use crate::lora::airtime; use crate::lora::config::Config; use crate::lora::types::*; @@ -289,7 +291,7 @@ where /// # Non-Blocking /// This functions schedules the TX operation and returns immediately. To check if the TX operation is done, use /// [`Self::complete_tx`]. - pub fn start_tx(&mut self, data: &[u8]) -> Result<(), RxTxStartError> { + pub fn start_tx(&mut self, data: &[u8]) -> Result<(), TxStartError> { // Validate input length let 1..=RFM95_FIFO_SIZE = data.len() else { // The message is empty or too long @@ -359,7 +361,7 @@ where /// # Maximum Timeout /// The RFM95 timeout counter works by counting symbols, and is thus dependent on the configured spreading factor /// and bandwidth. See also [`Self::rx_timeout_max`]. - pub fn start_rx(&mut self, timeout: Duration) -> Result<(), RxTxStartError> { + pub fn start_rx(&mut self, timeout: Duration) -> Result<(), RxStartError> { // Get the current symbol airtime in microseconds let spreading_factor = self.spreading_factor()?; let bandwidth = self.bandwidth()?; From 7c9214053bf13b757069f4e3f6ff540f15533ee5 Mon Sep 17 00:00:00 2001 From: Keziah Biermann Date: Tue, 18 Mar 2025 18:35:56 +0100 Subject: [PATCH 5/5] Add categories and keywords --- rfm95/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rfm95/Cargo.toml b/rfm95/Cargo.toml index 60cab57..a6e7cab 100644 --- a/rfm95/Cargo.toml +++ b/rfm95/Cargo.toml @@ -3,8 +3,8 @@ name = "embedded-lora-rfm95" version = "0.2.0" edition = "2021" authors = ["KizzyCode Software Labs./Keziah Biermann "] -keywords = [] -categories = [] +keywords = ["no-std", "embedded", "hardware-support", "lora", "rfm95"] +categories = ["no-std", "embedded", "hardware-support"] description = "A `no-std`-compatible, opinionated driver for the RFM95 LoRa modem" license = "BSD-2-Clause OR MIT" repository = "https://github.com/KizzyCode/embedded-lora-rust"