diff --git a/.config/nextest.toml b/.config/nextest.toml index 25c130f..9d29e50 100644 --- a/.config/nextest.toml +++ b/.config/nextest.toml @@ -15,7 +15,7 @@ slow-timeout = "10s" # This is all tests in tests/ folder + unit test for --extra-args. default-filter = "all()" -# show wich tests were skipped +# show which tests were skipped status-level = "skip" # show log output from each test diff --git a/bindings/node/src/radio/config.rs b/bindings/node/src/radio/config.rs index 04031c6..f0bed74 100644 --- a/bindings/node/src/radio/config.rs +++ b/bindings/node/src/radio/config.rs @@ -8,7 +8,7 @@ use napi::{bindgen_prelude::Buffer, JsNumber, Result}; #[derive(Debug, Clone, Copy)] pub struct RadioConfig { inner: rf24::radio::RadioConfig, - _addr_buf: [u8; 5], + addr_buf: [u8; 5], } #[napi] @@ -53,7 +53,7 @@ impl RadioConfig { pub fn new() -> Self { Self { inner: rf24::radio::RadioConfig::default(), - _addr_buf: [0u8; 5], + addr_buf: [0u8; 5], } } @@ -344,8 +344,8 @@ impl RadioConfig { /// Get the address for a specified `pipe` set by {@link RadioConfig.setRxAddress}. #[napi] pub fn get_rx_address(&mut self, pipe: u8) -> Buffer { - self.inner.rx_address(pipe, &mut self._addr_buf); - Buffer::from(self._addr_buf.to_vec()) + self.inner.rx_address(pipe, &mut self.addr_buf); + Buffer::from(self.addr_buf.to_vec()) } /// Set the TX address. @@ -359,8 +359,8 @@ impl RadioConfig { #[napi(getter, js_name = "txAddress")] pub fn get_tx_address(&mut self) -> Buffer { - self.inner.tx_address(&mut self._addr_buf); - Buffer::from(self._addr_buf.to_vec()) + self.inner.tx_address(&mut self.addr_buf); + Buffer::from(self.addr_buf.to_vec()) } /// Close a RX pipe from receiving data. @@ -381,7 +381,7 @@ impl RadioConfig { pub fn from_inner(config: rf24::radio::RadioConfig) -> Self { Self { inner: config, - _addr_buf: [0u8; 5], + addr_buf: [0u8; 5], } } } diff --git a/bindings/node/src/radio/interface.rs b/bindings/node/src/radio/interface.rs index 8fc281c..45d725d 100644 --- a/bindings/node/src/radio/interface.rs +++ b/bindings/node/src/radio/interface.rs @@ -196,6 +196,10 @@ impl RF24 { /// Put the radio into active RX mode. /// + /// This function will restore the cached RX address set to pipe 0. + /// This is done because the {@link RF24.asTx} will + /// appropriate the RX address on pipe 0 for auto-ack purposes. + /// /// @group Basic #[napi] pub fn as_rx(&mut self) -> Result<()> { @@ -212,6 +216,15 @@ impl RF24 { /// > This function will also flush the TX FIFO when ACK payloads are enabled /// > (via {@link RF24.ackPayloads}). /// + /// This must be called at least once before calling + /// {@link RF24.send} or {@link RF24.write}. + /// Conventionally, this should be called before setting the TX address via + /// {@link RF24.openTxPipe}. + /// + /// For auto-ack purposes, this function will also restore the cached + /// TX address (passed to {@link RF24.openTxPipe}) + /// to the RX pipe 0. + /// /// @group Basic #[napi] pub fn as_tx(&mut self) -> Result<()> { @@ -766,7 +779,7 @@ impl RF24 { /// Only pipe 0 can be used for transmitting. It is highly recommended to /// avoid using pipe 0 to receive because of this. /// - /// @param address - The address to receive data from. + /// @param address - The address to transmit data to. /// /// @group Basic #[napi] diff --git a/bindings/python/src/radio/config.rs b/bindings/python/src/radio/config.rs index feec128..6068825 100644 --- a/bindings/python/src/radio/config.rs +++ b/bindings/python/src/radio/config.rs @@ -44,7 +44,7 @@ use std::borrow::Cow; #[derive(Debug, Clone, Copy)] pub struct RadioConfig { inner: rf24::radio::RadioConfig, - _addr_buf: [u8; 5], + addr_buf: [u8; 5], } #[pymethods] @@ -53,7 +53,7 @@ impl RadioConfig { pub fn new() -> Self { Self { inner: rf24::radio::RadioConfig::default(), - _addr_buf: [0u8; 5], + addr_buf: [0u8; 5], } } @@ -305,8 +305,8 @@ impl RadioConfig { /// Get the address for a specified `pipe` set by [`RadioConfig.set_rx_address()`][rf24_py.RadioConfig.set_rx_address]. pub fn get_rx_address(&mut self, pipe: u8) -> Cow<[u8]> { - self.inner.rx_address(pipe, &mut self._addr_buf); - Cow::from(&self._addr_buf) + self.inner.rx_address(pipe, &mut self.addr_buf); + Cow::from(&self.addr_buf) } /// Set the TX address. @@ -314,8 +314,8 @@ impl RadioConfig { /// Only pipe 0 can be used for TX operations (including auto-ACK packets during RX operations). #[getter] pub fn get_tx_address(&mut self) -> Cow<[u8]> { - self.inner.tx_address(&mut self._addr_buf); - Cow::from(&self._addr_buf) + self.inner.tx_address(&mut self.addr_buf); + Cow::from(&self.addr_buf) } #[setter] @@ -339,7 +339,7 @@ impl RadioConfig { pub fn from_inner(config: rf24::radio::RadioConfig) -> Self { Self { inner: config, - _addr_buf: [0u8; 5], + addr_buf: [0u8; 5], } } } diff --git a/bindings/python/src/radio/interface.rs b/bindings/python/src/radio/interface.rs index e8bca4a..37d583d 100644 --- a/bindings/python/src/radio/interface.rs +++ b/bindings/python/src/radio/interface.rs @@ -173,6 +173,10 @@ impl RF24 { } /// Put the radio into active RX mode. + /// + /// This function will restore the cached RX address set to pipe 0. + /// This is done because the [`RF24.as_tx()`][rf24_py.RF24.as_tx] will + /// appropriate the RX address on pipe 0 for auto-ack purposes. pub fn as_rx(&mut self) -> PyResult<()> { self.inner .as_rx() @@ -186,6 +190,16 @@ impl RF24 { /// Note: /// This function will also flush the TX FIFO when ACK payloads are enabled /// (via [`RF24.ack_payloads`][rf24_py.RF24.ack_payloads]). + /// + /// This must be called at least once before calling + /// [`RF24.send()`][rf24_py.RF24.send] or + /// [`RF24.write()`][rf24_py.RF24.write]. + /// Conventionally, this should be called before setting the TX address via + /// [`RF24.open_tx_pipe()`][rf24_py.RF24.open_tx_pipe]. + /// + /// For auto-ack purposes, this function will also restore the cached + /// TX address (passed to [`RF24.open_tx_pipe()`][rf24_py.RF24.open_tx_pipe]) + /// to the RX pipe 0. pub fn as_tx(&mut self) -> PyResult<()> { self.inner .as_tx() @@ -654,7 +668,7 @@ impl RF24 { /// avoid using pipe 0 to receive because of this. /// /// Parameters: - /// address: The address to receive data from. + /// address: The address to transmit data to. pub fn open_tx_pipe(&mut self, address: &[u8]) -> PyResult<()> { self.inner .open_tx_pipe(address) diff --git a/crates/rf24-rs/src/radio/prelude.rs b/crates/rf24-rs/src/radio/prelude.rs index 6745aa2..a180e22 100644 --- a/crates/rf24-rs/src/radio/prelude.rs +++ b/crates/rf24-rs/src/radio/prelude.rs @@ -12,16 +12,19 @@ use crate::types::{CrcLength, DataRate, FifoState, PaLevel, StatusFlags}; use super::RadioConfig; +/// A trait to alias hardware related errors about the radio. +pub trait RadioErrorType { + type Error; +} + /// A trait to represent manipulation of data pipes /// for an ESB capable transceiver. -pub trait EsbPipe { - type PipeErrorType; - +pub trait EsbPipe: RadioErrorType { /// Open a specified `pipe` for receiving data when radio is in RX mode. /// /// If the specified `pipe` is not in range [0, 5], then this function does nothing. /// - /// Up to 6 pipes can be open for reading at once. Open all the required + /// Up to 6 pipes can be open for reading at once. Open all the required /// reading pipes, and then call [`EsbRadio::as_rx()`]. /// /// ### About pipe addresses @@ -54,45 +57,43 @@ pub trait EsbPipe { /// /// Read [maniacBug's blog post](http://maniacalbits.blogspot.com/2013/04/rf24-addressing-nrf24l01-radios-require.html) /// to understand how to avoid using malformed addresses. - fn open_rx_pipe(&mut self, pipe: u8, address: &[u8]) -> Result<(), Self::PipeErrorType>; + fn open_rx_pipe(&mut self, pipe: u8, address: &[u8]) -> Result<(), Self::Error>; /// Set an address to pipe 0 for transmitting when radio is in TX mode. /// - fn open_tx_pipe(&mut self, address: &[u8]) -> Result<(), Self::PipeErrorType>; + /// Only pipe 0 can be used for transmitting. It is highly recommended to + /// avoid using pipe 0 to receive because of this. + fn open_tx_pipe(&mut self, address: &[u8]) -> Result<(), Self::Error>; /// Close a specified pipe from receiving data when radio is in RX mode. - fn close_rx_pipe(&mut self, pipe: u8) -> Result<(), Self::PipeErrorType>; + fn close_rx_pipe(&mut self, pipe: u8) -> Result<(), Self::Error>; /// Set the address length (applies to all pipes). /// /// If the specified length is clamped to the range [2, 5]. /// Any value outside that range defaults to 5. - fn set_address_length(&mut self, length: u8) -> Result<(), Self::PipeErrorType>; + fn set_address_length(&mut self, length: u8) -> Result<(), Self::Error>; /// Get the currently configured address length (applied to all pipes). - fn get_address_length(&mut self) -> Result; + fn get_address_length(&mut self) -> Result; } /// A trait to represent manipulation of a channel (aka frequency) /// for an ESB capable transceiver. -pub trait EsbChannel { - type ChannelErrorType; - +pub trait EsbChannel: RadioErrorType { /// Set the radio's currently selected channel. /// /// These channels translate to the RF frequency as an offset of Hz from 2400 MHz. /// The default channel is 76 (2400 + 76 = 2.476 GHz). - fn set_channel(&mut self, channel: u8) -> Result<(), Self::ChannelErrorType>; + fn set_channel(&mut self, channel: u8) -> Result<(), Self::Error>; /// Get the radio's currently selected channel. - fn get_channel(&mut self) -> Result; + fn get_channel(&mut self) -> Result; } /// A trait to represent manipulation of [`StatusFlags`] /// for an ESB capable transceiver. -pub trait EsbStatus { - type StatusErrorType; - +pub trait EsbStatus: RadioErrorType { /// Get the [`StatusFlags`] state that was cached from the latest SPI transaction. fn get_status_flags(&self, flags: &mut StatusFlags); @@ -102,7 +103,7 @@ pub trait EsbStatus { /// IRQ pin ignore the corresponding event. /// By default, all events are enabled and will trigger the IRQ pin, /// a behavior equivalent to `set_status_flags(None)`. - fn set_status_flags(&mut self, flags: StatusFlags) -> Result<(), Self::StatusErrorType>; + fn set_status_flags(&mut self, flags: StatusFlags) -> Result<(), Self::Error>; /// Clear the radio's IRQ status flags /// @@ -112,40 +113,38 @@ pub trait EsbStatus { /// interrupt event. Setting any member of [`StatusFlags`] to `false` will leave /// the corresponding status flag untouched. This means that the IRQ pin can remain /// active (LOW) when multiple events occurred but only flag was cleared. - fn clear_status_flags(&mut self, flags: StatusFlags) -> Result<(), Self::StatusErrorType>; + fn clear_status_flags(&mut self, flags: StatusFlags) -> Result<(), Self::Error>; /// Refresh the internal cache of status byte /// (which is also saved from every SPI transaction). /// /// Use [`EsbStatus::get_status_flags()`] to get the updated status flags. - fn update(&mut self) -> Result<(), Self::StatusErrorType>; + fn update(&mut self) -> Result<(), Self::Error>; } /// A trait to represent manipulation of RX and TX FIFOs /// for an ESB capable transceiver. -pub trait EsbFifo { - type FifoErrorType; - +pub trait EsbFifo: RadioErrorType { /// Flush the radio's RX FIFO. - fn flush_rx(&mut self) -> Result<(), Self::FifoErrorType>; + fn flush_rx(&mut self) -> Result<(), Self::Error>; /// Flush the radio's TX FIFO. /// /// This function is automatically called by [`EsbRadio::as_tx()`] /// if ACK payloads are enabled. - fn flush_tx(&mut self) -> Result<(), Self::FifoErrorType>; + fn flush_tx(&mut self) -> Result<(), Self::Error>; /// Get the state of the specified FIFO. /// /// - Pass `true` to `about_tx` parameter to get the state of the TX FIFO. /// - Pass `false` to `about_tx` parameter to get the state of the RX FIFO. - fn get_fifo_state(&mut self, about_tx: bool) -> Result; + fn get_fifo_state(&mut self, about_tx: bool) -> Result; /// Is there a payload available in the radio's RX FIFO? /// /// This function simply returns true if there is data to [`EsbRadio::read()`] from the RX FIFO. /// Use [`EsbFifo::available_pipe()`] to get information about the pipe that received the data. - fn available(&mut self) -> Result; + fn available(&mut self) -> Result; /// This is similar to [`EsbFifo::available()`] except the `pipe` parameter is given /// a mutable [`u8`] value, and the pipe number that received the data is stored to it. @@ -171,23 +170,21 @@ pub trait EsbFifo { /// before calling this function. /// /// - fn available_pipe(&mut self, pipe: &mut u8) -> Result; + fn available_pipe(&mut self, pipe: &mut u8) -> Result; } /// A trait to represent manipulation of payload lengths (static or dynamic) /// for an ESB capable transceiver. -pub trait EsbPayloadLength { - type PayloadLengthErrorType; - +pub trait EsbPayloadLength: RadioErrorType { /// Set the radio's static payload length. /// /// Note, this has no effect when dynamic payloads are enabled. - fn set_payload_length(&mut self, length: u8) -> Result<(), Self::PayloadLengthErrorType>; + fn set_payload_length(&mut self, length: u8) -> Result<(), Self::Error>; /// Get the currently configured static payload length used on pipe 0 /// /// Use [`EsbPayloadLength::get_dynamic_payload_length()`] instead when dynamic payloads are enabled. - fn get_payload_length(&mut self) -> Result; + fn get_payload_length(&mut self) -> Result; /// Set the dynamic payloads feature for all pipes. /// @@ -212,7 +209,7 @@ pub trait EsbPayloadLength { /// } /// } /// ``` - fn set_dynamic_payloads(&mut self, enable: bool) -> Result<(), Self::PayloadLengthErrorType>; + fn set_dynamic_payloads(&mut self, enable: bool) -> Result<(), Self::Error>; /// Get the current setting of the dynamic payloads feature. /// @@ -224,14 +221,12 @@ pub trait EsbPayloadLength { /// When dynamic payloads are disabled (via [`EsbPayloadLength::set_dynamic_payloads()`]) /// or there is no [`EsbFifo::available()`] payload in the RX FIFO, this function's /// returned value shall be considered invalid. - fn get_dynamic_payload_length(&mut self) -> Result; + fn get_dynamic_payload_length(&mut self) -> Result; } /// A trait to represent manipulation of the automatic acknowledgement feature /// for an ESB capable transceiver. -pub trait EsbAutoAck: EsbPayloadLength { - type AutoAckErrorType; - +pub trait EsbAutoAck: EsbPayloadLength + RadioErrorType { /// Enable or disable the custom ACK (acknowledgement) payloads attached to auto-ack packets. /// /// By default this feature is disabled. @@ -243,7 +238,7 @@ pub trait EsbAutoAck: EsbPayloadLength { /// Use [`EsbRadio::read()`] to fetch the payloads from the RX FIFO. /// /// To append a payload to an auto ack packet, use [`EsbAutoAck::write_ack_payload()`]. - fn set_ack_payloads(&mut self, enable: bool) -> Result<(), Self::AutoAckErrorType>; + fn set_ack_payloads(&mut self, enable: bool) -> Result<(), Self::Error>; /// Get the current setting of the ACK payloads feature. /// @@ -282,7 +277,7 @@ pub trait EsbAutoAck: EsbPayloadLength { /// /// See also [`EsbAutoAck::set_ack_payloads()`], /// [`EsbPayloadLength::set_dynamic_payloads`], and [`EsbAutoAck::set_auto_ack()`]. - fn write_ack_payload(&mut self, pipe: u8, buf: &[u8]) -> Result; + fn write_ack_payload(&mut self, pipe: u8, buf: &[u8]) -> Result; /// Enable or disable the auto-ack (automatic acknowledgement) feature for all /// pipes. @@ -304,7 +299,7 @@ pub trait EsbAutoAck: EsbPayloadLength { /// If disabling auto-acknowledgment packets, the ACK payloads /// feature is also disabled as this feature is required to send ACK /// payloads. - fn set_auto_ack(&mut self, enable: bool) -> Result<(), Self::AutoAckErrorType>; + fn set_auto_ack(&mut self, enable: bool) -> Result<(), Self::Error>; /// Set the auto-ack feature for an individual `pipe`. /// @@ -324,7 +319,7 @@ pub trait EsbAutoAck: EsbPayloadLength { /// If disabling auto-acknowledgment packets on pipe 0, the ACK /// payloads feature is also disabled as this feature is required on pipe 0 /// to send ACK payloads. - fn set_auto_ack_pipe(&mut self, enable: bool, pipe: u8) -> Result<(), Self::AutoAckErrorType>; + fn set_auto_ack_pipe(&mut self, enable: bool, pipe: u8) -> Result<(), Self::Error>; /// Set the number of retry attempts and delay between retry attempts when /// transmitting a payload. @@ -344,7 +339,7 @@ pub trait EsbAutoAck: EsbPayloadLength { /// Disabling the auto-retry feature on a transmitter still uses the /// auto-ack feature (if enabled), except it will not retry to transmit if /// the payload was not acknowledged on the first attempt. - fn set_auto_retries(&mut self, delay: u8, count: u8) -> Result<(), Self::AutoAckErrorType>; + fn set_auto_retries(&mut self, delay: u8, count: u8) -> Result<(), Self::Error>; /// Allow the functionality of the `ask_no_ack` parameter in [`EsbRadio::send()`] and /// [`EsbRadio::write()`]. @@ -354,26 +349,22 @@ pub trait EsbAutoAck: EsbPayloadLength { /// allow disabling the auto-ack feature on a per-payload basis. Such behavior would be /// desirable when transmitting to multiple radios that are setup to receive data from the /// same address. - fn allow_ask_no_ack(&mut self, enable: bool) -> Result<(), Self::AutoAckErrorType>; + fn allow_ask_no_ack(&mut self, enable: bool) -> Result<(), Self::Error>; } /// A trait to represent manipulation of the power amplitude level /// for an ESB capable transceiver. -pub trait EsbPaLevel { - type PaLevelErrorType; - +pub trait EsbPaLevel: RadioErrorType { /// Get the currently configured Power Amplitude Level (PA Level) - fn get_pa_level(&mut self) -> Result; + fn get_pa_level(&mut self) -> Result; /// Set the radio's Power Amplitude Level (PA Level) - fn set_pa_level(&mut self, pa_level: PaLevel) -> Result<(), Self::PaLevelErrorType>; + fn set_pa_level(&mut self, pa_level: PaLevel) -> Result<(), Self::Error>; } /// A trait to represent manipulation of the state of power /// for an ESB capable transceiver. -pub trait EsbPower { - type PowerErrorType; - +pub trait EsbPower: RadioErrorType { /// Power down the radio. /// ///
@@ -381,7 +372,7 @@ pub trait EsbPower { /// The nRF24L01 cannot receive nor transmit data when powered down. /// ///
- fn power_down(&mut self) -> Result<(), Self::PowerErrorType>; + fn power_down(&mut self) -> Result<(), Self::Error>; /// Power up the radio. /// @@ -399,7 +390,7 @@ pub trait EsbPower { /// // ... do something else for 5 milliseconds /// radio.as_rx().unwrap(); /// ``` - fn power_up(&mut self, delay: Option) -> Result<(), Self::PowerErrorType>; + fn power_up(&mut self, delay: Option) -> Result<(), Self::Error>; /// Get the current (cached) state of the radio's power. /// @@ -409,44 +400,36 @@ pub trait EsbPower { /// A trait to represent manipulation of Cyclical Redundancy Checksums /// for an ESB capable transceiver. -pub trait EsbCrcLength { - type CrcLengthErrorType; - +pub trait EsbCrcLength: RadioErrorType { /// Get the currently configured CRC (Cyclical Redundancy Checksum) length - fn get_crc_length(&mut self) -> Result; + fn get_crc_length(&mut self) -> Result; /// Set the radio's CRC (Cyclical Redundancy Checksum) length - fn set_crc_length(&mut self, crc_length: CrcLength) -> Result<(), Self::CrcLengthErrorType>; + fn set_crc_length(&mut self, crc_length: CrcLength) -> Result<(), Self::Error>; } /// A trait to represent manipulation of the Data Rate /// for an ESB capable transceiver. -pub trait EsbDataRate { - type DataRateErrorType; - +pub trait EsbDataRate: RadioErrorType { /// Get the currently configured Data Rate - fn get_data_rate(&mut self) -> Result; + fn get_data_rate(&mut self) -> Result; /// Set the radio's Data Rate - fn set_data_rate(&mut self, data_rate: DataRate) -> Result<(), Self::DataRateErrorType>; + fn set_data_rate(&mut self, data_rate: DataRate) -> Result<(), Self::Error>; } /// A trait to represent debug output /// for an ESB capable transceiver. -pub trait EsbDetails { - type DetailsErrorType; - +pub trait EsbDetails: RadioErrorType { /// Print details about radio's current configuration. /// /// This should only be used for debugging development. /// Using this in production should be limited due to a significant increase in /// compile size. - fn print_details(&mut self) -> Result<(), Self::DetailsErrorType>; + fn print_details(&mut self) -> Result<(), Self::Error>; } -pub trait EsbInit { - type ConfigErrorType; - +pub trait EsbInit: RadioErrorType { /// Initialize the radio's hardware. /// /// This is similar to [`EsbInit::with_config()`] (with [`RadioConfig::default()`]), @@ -459,36 +442,41 @@ pub trait EsbInit { /// This function should only be called once after instantiating the radio object. /// Afterward, it is quicker to use [`EsbInit::with_config()`] to reconfigure the /// radio for different network requirements. - fn init(&mut self) -> Result<(), Self::ConfigErrorType>; + fn init(&mut self) -> Result<(), Self::Error>; /// Reconfigure the radio using the given `config` object. /// /// See [`RadioConfig`] for more detail. /// This function is a convenience where calling multiple configuration functions may /// be cumbersome. - fn with_config(&mut self, config: &RadioConfig) -> Result<(), Self::ConfigErrorType>; + fn with_config(&mut self, config: &RadioConfig) -> Result<(), Self::Error>; } /// A trait to represent manipulation of an ESB capable transceiver. /// /// Although the name is rather generic, this trait describes the /// behavior of a radio's rudimentary modes (RX and TX). -pub trait EsbRadio { - type RadioErrorType; - +pub trait EsbRadio: RadioErrorType { /// Put the radio into active RX mode. /// /// Conventionally, this should be called after setting the RX addresses via /// [`EsbPipe::open_rx_pipe()`] - fn as_rx(&mut self) -> Result<(), Self::RadioErrorType>; + /// + /// This function will restore the cached RX address set to pipe 0. + /// This is done because the [`EsbRadio::as_tx()`] will appropriate the + /// RX address on pipe 0 for auto-ack purposes. + fn as_rx(&mut self) -> Result<(), Self::Error>; /// Put the radio into inactive TX mode. /// /// This must be called at least once before calling [`EsbRadio::send()`] or /// [`EsbRadio::write()`]. - /// Conventionally, this should be called after setting the TX address via + /// Conventionally, this should be called before setting the TX address via /// [`EsbPipe::open_tx_pipe()`]. - fn as_tx(&mut self) -> Result<(), Self::RadioErrorType>; + /// + /// For auto-ack purposes, this function will also restore the cached + /// TX address (passed to [`EsbPipe::open_tx_pipe()`]) to the RX pipe 0. + fn as_tx(&mut self) -> Result<(), Self::Error>; /// Is the radio in RX mode? fn is_rx(&self) -> bool; @@ -500,7 +488,7 @@ pub trait EsbRadio { /// /// See [`EsbRadio::write()`] for description of `ask_no_ack` parameter and more /// detail about how the radio processes data in the TX FIFO. - fn send(&mut self, buf: &[u8], ask_no_ack: bool) -> Result; + fn send(&mut self, buf: &[u8], ask_no_ack: bool) -> Result; /// Non-blocking function to prepare radio for transmitting payload(s). /// @@ -526,12 +514,7 @@ pub trait EsbRadio { /// Set the `start_tx` parameter `false` to prevent entering active TX mode. If the radio /// is already in active TX mode (because it is processing payloads in the TX FIFO), then /// this parameter has no effect. - fn write( - &mut self, - buf: &[u8], - ask_no_ack: bool, - start_tx: bool, - ) -> Result; + fn write(&mut self, buf: &[u8], ask_no_ack: bool, start_tx: bool) -> Result; /// Similar to [`EsbRadio::send()`] but specifically for failed transmissions. /// @@ -542,7 +525,7 @@ pub trait EsbRadio { /// /// Unlike [`EsbRadio::rewrite()`], this function will only make one attempt to /// resend the failed payload. - fn resend(&mut self) -> Result; + fn resend(&mut self) -> Result; /// Similar to [`EsbRadio::write()`] but specifically for failed transmissions. /// @@ -561,14 +544,14 @@ pub trait EsbRadio { /// - The radio's TX FIFO is flushed (via [`EsbFifo::flush_tx()`]). /// - The radio's CE pin is set to inactive LOW. This can be done directly on the pin or by calling /// [`EsbRadio::as_tx()`]. - fn rewrite(&mut self) -> Result<(), Self::RadioErrorType>; + fn rewrite(&mut self) -> Result<(), Self::Error>; /// Get the Auto-Retry Count (ARC) about the previous transmission. /// /// This data is reset for every payload attempted to transmit. /// It cannot exceed 15 per the `count` parameter in [`EsbAutoAck::set_auto_retries()`]. /// If auto-ack feature is disabled, then this function provides no useful data. - fn get_last_arc(&mut self) -> Result; + fn get_last_arc(&mut self) -> Result; /// Read data from the radio's RX FIFO into the specified `buf`. /// @@ -581,5 +564,5 @@ pub trait EsbRadio { /// (set by [`EsbPayloadLength::set_payload_length()`]) or the dynamic payload length /// (fetched internally using [`EsbPayloadLength::get_dynamic_payload_length()`]) if /// dynamic payload lengths are enable (see [`EsbPayloadLength::set_dynamic_payloads()`]). - fn read(&mut self, buf: &mut [u8], len: Option) -> Result; + fn read(&mut self, buf: &mut [u8], len: Option) -> Result; } diff --git a/crates/rf24-rs/src/radio/rf24/auto_ack.rs b/crates/rf24-rs/src/radio/rf24/auto_ack.rs index 1776659..335c6a8 100644 --- a/crates/rf24-rs/src/radio/rf24/auto_ack.rs +++ b/crates/rf24-rs/src/radio/rf24/auto_ack.rs @@ -1,6 +1,6 @@ use embedded_hal::{delay::DelayNs, digital::OutputPin, spi::SpiDevice}; -use crate::radio::{prelude::EsbAutoAck, Nrf24Error, RF24}; +use crate::radio::{prelude::EsbAutoAck, RF24}; use super::{commands, registers, Feature}; @@ -10,17 +10,15 @@ where DO: OutputPin, DELAY: DelayNs, { - type AutoAckErrorType = Nrf24Error; - - fn set_ack_payloads(&mut self, enable: bool) -> Result<(), Self::AutoAckErrorType> { - if self._feature.ack_payloads() != enable { + fn set_ack_payloads(&mut self, enable: bool) -> Result<(), Self::Error> { + if self.feature.ack_payloads() != enable { self.spi_read(1, registers::FEATURE)?; - self._feature = - Feature::from_bits(self._feature.into_bits() & !Feature::REG_MASK | self._buf[1]) + self.feature = + Feature::from_bits(self.feature.into_bits() & !Feature::REG_MASK | self.buf[1]) .with_ack_payloads(enable); self.spi_write_byte( registers::FEATURE, - self._feature.into_bits() & Feature::REG_MASK, + self.feature.into_bits() & Feature::REG_MASK, )?; if enable { @@ -33,46 +31,46 @@ where } fn get_ack_payloads(&self) -> bool { - self._feature.ack_payloads() + self.feature.ack_payloads() } - fn set_auto_ack(&mut self, enable: bool) -> Result<(), Self::AutoAckErrorType> { + fn set_auto_ack(&mut self, enable: bool) -> Result<(), Self::Error> { self.spi_write_byte(registers::EN_AA, 0x3F * enable as u8)?; // accommodate ACK payloads feature - if !enable && self._feature.ack_payloads() { + if !enable && self.feature.ack_payloads() { self.set_ack_payloads(false)?; } Ok(()) } - fn set_auto_ack_pipe(&mut self, enable: bool, pipe: u8) -> Result<(), Self::AutoAckErrorType> { + fn set_auto_ack_pipe(&mut self, enable: bool, pipe: u8) -> Result<(), Self::Error> { if pipe > 5 { return Ok(()); } self.spi_read(1, registers::EN_AA)?; let mask = 1 << pipe; - let reg_val = self._buf[1]; - if !enable && self._feature.ack_payloads() && pipe == 0 { + let reg_val = self.buf[1]; + if !enable && self.feature.ack_payloads() && pipe == 0 { self.set_ack_payloads(enable)?; } self.spi_write_byte(registers::EN_AA, reg_val & !mask | (mask * enable as u8)) } - fn allow_ask_no_ack(&mut self, enable: bool) -> Result<(), Self::AutoAckErrorType> { + fn allow_ask_no_ack(&mut self, enable: bool) -> Result<(), Self::Error> { self.spi_read(1, registers::FEATURE)?; - self.spi_write_byte(registers::FEATURE, self._buf[1] & !1 | enable as u8) + self.spi_write_byte(registers::FEATURE, self.buf[1] & !1 | enable as u8) } - fn write_ack_payload(&mut self, pipe: u8, buf: &[u8]) -> Result { - if self._feature.ack_payloads() && pipe <= 5 { + fn write_ack_payload(&mut self, pipe: u8, buf: &[u8]) -> Result { + if self.feature.ack_payloads() && pipe <= 5 { let len = buf.len().min(32); self.spi_write_buf(commands::W_ACK_PAYLOAD | pipe, &buf[..len])?; - return Ok(!self._status.tx_full()); + return Ok(!self.status.tx_full()); } Ok(false) } - fn set_auto_retries(&mut self, delay: u8, count: u8) -> Result<(), Self::AutoAckErrorType> { + fn set_auto_retries(&mut self, delay: u8, count: u8) -> Result<(), Self::Error> { self.spi_write_byte(registers::SETUP_RETR, count.min(15) | (delay.min(15) << 4)) } } diff --git a/crates/rf24-rs/src/radio/rf24/channel.rs b/crates/rf24-rs/src/radio/rf24/channel.rs index e3364d6..94dd58a 100644 --- a/crates/rf24-rs/src/radio/rf24/channel.rs +++ b/crates/rf24-rs/src/radio/rf24/channel.rs @@ -1,5 +1,5 @@ use super::registers; -use crate::radio::{prelude::EsbChannel, Nrf24Error, RF24}; +use crate::radio::{prelude::EsbChannel, RF24}; use embedded_hal::{delay::DelayNs, digital::OutputPin, spi::SpiDevice}; impl EsbChannel for RF24 @@ -8,18 +8,16 @@ where DO: OutputPin, DELAY: DelayNs, { - type ChannelErrorType = Nrf24Error; - /// The nRF24L01 support 126 channels. The specified `channel` is /// clamped to the range [0, 125]. - fn set_channel(&mut self, channel: u8) -> Result<(), Self::ChannelErrorType> { + fn set_channel(&mut self, channel: u8) -> Result<(), Self::Error> { self.spi_write_byte(registers::RF_CH, channel.min(125)) } /// See also [`RF24::set_channel()`]. - fn get_channel(&mut self) -> Result { + fn get_channel(&mut self) -> Result { self.spi_read(1, registers::RF_CH)?; - Ok(self._buf[1]) + Ok(self.buf[1]) } } diff --git a/crates/rf24-rs/src/radio/rf24/crc_length.rs b/crates/rf24-rs/src/radio/rf24/crc_length.rs index 1052f78..1916391 100644 --- a/crates/rf24-rs/src/radio/rf24/crc_length.rs +++ b/crates/rf24-rs/src/radio/rf24/crc_length.rs @@ -10,21 +10,19 @@ where DO: OutputPin, DELAY: DelayNs, { - type CrcLengthErrorType = Nrf24Error; - - fn get_crc_length(&mut self) -> Result { + fn get_crc_length(&mut self) -> Result { self.spi_read(1, registers::CONFIG)?; - if self._buf[1] & Config::CRC_MASK == 4 { + if self.buf[1] & Config::CRC_MASK == 4 { return Err(Nrf24Error::BinaryCorruption); } - self._config_reg = Config::from_bits(self._buf[1]); - Ok(self._config_reg.crc_length()) + self.config_reg = Config::from_bits(self.buf[1]); + Ok(self.config_reg.crc_length()) } - fn set_crc_length(&mut self, crc_length: CrcLength) -> Result<(), Self::CrcLengthErrorType> { + fn set_crc_length(&mut self, crc_length: CrcLength) -> Result<(), Self::Error> { self.spi_read(1, registers::CONFIG)?; - self._config_reg = self._config_reg.with_crc_length(crc_length); - self.spi_write_byte(registers::CONFIG, self._config_reg.into_bits()) + self.config_reg = self.config_reg.with_crc_length(crc_length); + self.spi_write_byte(registers::CONFIG, self.config_reg.into_bits()) } } diff --git a/crates/rf24-rs/src/radio/rf24/data_rate.rs b/crates/rf24-rs/src/radio/rf24/data_rate.rs index d7e946f..a91c485 100644 --- a/crates/rf24-rs/src/radio/rf24/data_rate.rs +++ b/crates/rf24-rs/src/radio/rf24/data_rate.rs @@ -21,22 +21,20 @@ where DO: OutputPin, DELAY: DelayNs, { - type DataRateErrorType = Nrf24Error; - - fn get_data_rate(&mut self) -> Result { + fn get_data_rate(&mut self) -> Result { self.spi_read(1, registers::RF_SETUP)?; - let da_bin = self._buf[1] & DataRate::MASK; + let da_bin = self.buf[1] & DataRate::MASK; if da_bin == DataRate::MASK { return Err(Nrf24Error::BinaryCorruption); } Ok(DataRate::from_bits(da_bin)) } - fn set_data_rate(&mut self, data_rate: DataRate) -> Result<(), Self::DataRateErrorType> { + fn set_data_rate(&mut self, data_rate: DataRate) -> Result<(), Self::Error> { self.tx_delay = set_tx_delay(data_rate); self.spi_read(1, registers::RF_SETUP)?; let da_bin = data_rate.into_bits(); - let out = self._buf[1] & !DataRate::MASK | da_bin; + let out = self.buf[1] & !DataRate::MASK | da_bin; self.spi_write_byte(registers::RF_SETUP, out) } } diff --git a/crates/rf24-rs/src/radio/rf24/details.rs b/crates/rf24-rs/src/radio/rf24/details.rs index a3b57eb..24488b0 100644 --- a/crates/rf24-rs/src/radio/rf24/details.rs +++ b/crates/rf24-rs/src/radio/rf24/details.rs @@ -1,4 +1,4 @@ -use super::{Nrf24Error, RF24}; +use super::RF24; use crate::radio::prelude::EsbDetails; use embedded_hal::{delay::DelayNs, digital::OutputPin, spi::SpiDevice}; @@ -18,11 +18,9 @@ where DO: OutputPin, DELAY: DelayNs, { - type DetailsErrorType = Nrf24Error; - #[cfg(feature = "defmt")] #[cfg(target_os = "none")] - fn print_details(&mut self) -> Result<(), Self::DetailsErrorType> { + fn print_details(&mut self) -> Result<(), Self::Error> { defmt::println!("Is a plus variant_________{=bool}", self.is_plus_variant()); let channel = self.get_channel()?; @@ -157,13 +155,13 @@ where } #[cfg(not(any(feature = "defmt", feature = "std")))] - fn print_details(&mut self) -> Result<(), Self::DetailsErrorType> { + fn print_details(&mut self) -> Result<(), Self::Error> { Ok(()) } #[cfg(not(target_os = "none"))] #[cfg(feature = "std")] - fn print_details(&mut self) -> Result<(), Self::DetailsErrorType> { + fn print_details(&mut self) -> Result<(), Self::Error> { use crate::radio::rf24::Config; std::println!("Is a plus variant_________{}", self.is_plus_variant()); @@ -178,7 +176,7 @@ where std::println!("RF Power Amplifier________{}", self.get_pa_level()?); self.spi_read(1, registers::RF_SETUP)?; - let rf_setup = self._buf[1]; + let rf_setup = self.buf[1]; std::println!("RF LNA enabled____________{}", rf_setup & 1 > 0); std::println!("CRC Length________________{}", self.get_crc_length()?); @@ -194,7 +192,7 @@ where ); self.spi_read(1, registers::SETUP_RETR)?; - let retry_setup = self._buf[1]; + let retry_setup = self.buf[1]; std::println!( "Auto retry delay__________{} microseconds", (retry_setup >> 4) as u16 * 250 + 250 @@ -202,10 +200,10 @@ where std::println!("Auto retry attempts_______{} maximum", retry_setup & 0x0F); self.spi_read(1, registers::FIFO_STATUS)?; - std::println!("Re-use TX FIFO____________{}", (self._buf[1] & 0x80) > 0); + std::println!("Re-use TX FIFO____________{}", (self.buf[1] & 0x80) > 0); self.spi_read(1, registers::OBSERVE_TX)?; - let observer = self._buf[1]; + let observer = self.buf[1]; std::println!("Packets lost\n on current channel____{}", observer >> 4); std::println!( "Retry attempts made\n for last transmission_{}", @@ -213,13 +211,13 @@ where ); self.spi_read(1, registers::CONFIG)?; - self._config_reg = Config::from_bits(self._buf[1]); - std::println!("IRQ on Data Ready_________{}", self._config_reg.rx_dr()); - std::println!(" Data Ready triggered__{}", self._status.rx_dr()); - std::println!("IRQ on Data Sent__________{}", self._config_reg.tx_ds()); - std::println!(" Data Sent triggered___{}", self._status.tx_ds()); - std::println!("IRQ on Data Fail__________{}", self._config_reg.tx_df()); - std::println!(" Data Fail triggered___{}", self._status.tx_df()); + self.config_reg = Config::from_bits(self.buf[1]); + std::println!("IRQ on Data Ready_________{}", self.config_reg.rx_dr()); + std::println!(" Data Ready triggered__{}", self.status.rx_dr()); + std::println!("IRQ on Data Sent__________{}", self.config_reg.tx_ds()); + std::println!(" Data Sent triggered___{}", self.status.tx_ds()); + std::println!("IRQ on Data Fail__________{}", self.config_reg.tx_df()); + std::println!(" Data Fail triggered___{}", self.status.tx_df()); let fifo = self.get_fifo_state(true)?; std::println!("TX FIFO___________________{}", fifo); @@ -227,37 +225,37 @@ where std::println!("RX FIFO___________________{}", fifo); self.spi_read(1, registers::FEATURE)?; - let features = self._buf[1]; + let features = self.buf[1]; std::println!("Ask no ACK allowed________{}", features & 1 > 0); std::println!("ACK Payload enabled_______{}", features & 2 > 0); self.spi_read(1, registers::DYNPD)?; - std::println!("Dynamic Payloads__________{:#010b}", self._buf[1]); + std::println!("Dynamic Payloads__________{:#010b}", self.buf[1]); self.spi_read(1, registers::EN_AA)?; - std::println!("Auto Acknowledgment_______{:#010b}", self._buf[1]); + std::println!("Auto Acknowledgment_______{:#010b}", self.buf[1]); std::println!( "Primary Mode______________{}X", - if self._config_reg.is_rx() { "R" } else { "T" } + if self.config_reg.is_rx() { "R" } else { "T" } ); std::println!("Powered Up________________{}", self.is_powered()); // print pipe addresses self.spi_read(5, registers::TX_ADDR)?; let mut address = [0u8; 4]; - address.copy_from_slice(&self._buf[2..6]); + address.copy_from_slice(&self.buf[2..6]); std::println!( "TX address_______________{:#08X}{:02X}", u32::from_le_bytes(address), - self._buf[1] + self.buf[1] ); self.spi_read(1, registers::EN_RXADDR)?; - let open_pipes = self._buf[1]; + let open_pipes = self.buf[1]; for pipe in 0..=5 { self.spi_read(if pipe < 2 { 5 } else { 1 }, registers::RX_ADDR_P0 + pipe)?; if pipe < 2 { - address.copy_from_slice(&self._buf[2..6]); + address.copy_from_slice(&self.buf[2..6]); } std::println!( "Pipe {pipe} ({}) bound to {:#08X}{:02X}", @@ -268,7 +266,7 @@ where }, // reverse the bytes read to represent how memory is stored u32::from_le_bytes(address), - self._buf[1], + self.buf[1], ); } Ok(()) diff --git a/crates/rf24-rs/src/radio/rf24/fifo.rs b/crates/rf24-rs/src/radio/rf24/fifo.rs index 2bf1345..e4fc959 100644 --- a/crates/rf24-rs/src/radio/rf24/fifo.rs +++ b/crates/rf24-rs/src/radio/rf24/fifo.rs @@ -1,9 +1,9 @@ use embedded_hal::{delay::DelayNs, digital::OutputPin, spi::SpiDevice}; -use crate::radio::{prelude::EsbFifo, Nrf24Error, RF24}; +use crate::radio::{prelude::EsbFifo, RF24}; use crate::FifoState; -use super::{commands, registers}; +use super::{commands, registers, Nrf24Error}; impl EsbFifo for RF24 where @@ -11,42 +11,41 @@ where DO: OutputPin, DELAY: DelayNs, { - type FifoErrorType = Nrf24Error; - - fn available(&mut self) -> Result { + fn available(&mut self) -> Result { self.spi_read(1, registers::FIFO_STATUS)?; - Ok(self._buf[1] & 1 == 0) + Ok(self.buf[1] & 1 == 0) } - fn available_pipe(&mut self, pipe: &mut u8) -> Result { + fn available_pipe(&mut self, pipe: &mut u8) -> Result { if self.available()? { // RX FIFO is not empty // get last used pipe self.spi_read(0, commands::NOP)?; - *pipe = self._status.rx_pipe(); + *pipe = self.status.rx_pipe(); return Ok(true); } Ok(false) } /// Use this to discard all 3 layers in the radio's RX FIFO. - fn flush_rx(&mut self) -> Result<(), Self::FifoErrorType> { + fn flush_rx(&mut self) -> Result<(), Self::Error> { self.spi_read(0, commands::FLUSH_RX) } /// Use this to discard all 3 layers in the radio's TX FIFO. - fn flush_tx(&mut self) -> Result<(), Self::FifoErrorType> { + fn flush_tx(&mut self) -> Result<(), Self::Error> { self.spi_read(0, commands::FLUSH_TX) } - fn get_fifo_state(&mut self, about_tx: bool) -> Result { + fn get_fifo_state(&mut self, about_tx: bool) -> Result { self.spi_read(1, registers::FIFO_STATUS)?; let offset = about_tx as u8 * 4; - let status = (self._buf[1] & (3 << offset)) >> offset; + let status = (self.buf[1] & (3 << offset)) >> offset; match status { + 0 => Ok(FifoState::Occupied), 1 => Ok(FifoState::Empty), 2 => Ok(FifoState::Full), - _ => Ok(FifoState::Occupied), + _ => Err(Nrf24Error::BinaryCorruption), } } } @@ -56,7 +55,7 @@ where #[cfg(test)] mod test { extern crate std; - use super::{commands, registers, EsbFifo, FifoState}; + use super::{commands, registers, EsbFifo, FifoState, Nrf24Error}; use crate::{spi_test_expects, test::mk_radio}; use embedded_hal_mock::eh1::spi::Transaction as SpiTransaction; use std::vec; @@ -113,6 +112,8 @@ mod test { (vec![registers::FIFO_STATUS, 1u8], vec![0xEu8, 2u8]), // read FIFO register value with occupied RX FIFO_STATUS (vec![registers::FIFO_STATUS, 2u8], vec![0xEu8, 0u8]), + // read FIFO register value with binary corruption over MISO + (vec![registers::FIFO_STATUS, 0], vec![0xEu8, 3]), ]; let mocks = mk_radio(&[], &spi_expectations); let (mut radio, mut spi, mut ce_pin) = (mocks.0, mocks.1, mocks.2); @@ -122,6 +123,10 @@ mod test { assert_eq!(radio.get_fifo_state(false), Ok(FifoState::Empty)); assert_eq!(radio.get_fifo_state(false), Ok(FifoState::Full)); assert_eq!(radio.get_fifo_state(false), Ok(FifoState::Occupied)); + assert_eq!( + radio.get_fifo_state(false), + Err(Nrf24Error::BinaryCorruption) + ); spi.done(); ce_pin.done(); } diff --git a/crates/rf24-rs/src/radio/rf24/init.rs b/crates/rf24-rs/src/radio/rf24/init.rs index b1b65ac..a7fbed6 100644 --- a/crates/rf24-rs/src/radio/rf24/init.rs +++ b/crates/rf24-rs/src/radio/rf24/init.rs @@ -14,32 +14,30 @@ where DO: OutputPin, DELAY: DelayNs, { - type ConfigErrorType = Nrf24Error; - /// Initialize the radio's hardware using the [`SpiDevice`] and [`OutputPin`] given /// to [`RF24::new()`]. - fn init(&mut self) -> Result<(), Self::ConfigErrorType> { + fn init(&mut self) -> Result<(), Self::Error> { // Must allow the radio time to settle else configuration bits will not necessarily stick. // This is actually only required following power up but some settling time also appears to // be required after resets too. For full coverage, we'll always assume the worst. // Enabling 16b CRC is by far the most obvious case if the wrong timing is used - or skipped. // Technically we require 4.5ms + 14us as a worst case. We'll just call it 5ms for good measure. // WARNING: Delay is based on P-variant whereby non-P *may* require different timing. - self._delay_impl.delay_ns(5000000); + self.delay_impl.delay_ns(5000000); self.power_down()?; self.spi_read(1, registers::CONFIG)?; - if self._buf[1] != self._config_reg.into_bits() { + if self.buf[1] != self.config_reg.into_bits() { return Err(Nrf24Error::BinaryCorruption); } // detect if is a plus variant & use old toggle features command accordingly self.spi_read(1, registers::FEATURE)?; - let before_toggle = self._buf[1]; + let before_toggle = self.buf[1]; self.toggle_features()?; self.spi_read(1, registers::FEATURE)?; - let after_toggle = self._buf[1]; - self._feature + let after_toggle = self.buf[1]; + self.feature .set_is_plus_variant(before_toggle == after_toggle); if after_toggle < before_toggle { // FEATURE register is disabled on non-plus variants until `toggle_features()` is used. @@ -49,7 +47,7 @@ where self.with_config(&RadioConfig::default()) } - fn with_config(&mut self, config: &RadioConfig) -> Result<(), Self::ConfigErrorType> { + fn with_config(&mut self, config: &RadioConfig) -> Result<(), Self::Error> { self.clear_status_flags(StatusFlags::new())?; self.power_down()?; @@ -61,14 +59,14 @@ where self.spi_write_byte(registers::SETUP_RETR, config.auto_retries.into_bits())?; self.spi_write_byte(registers::EN_AA, config.auto_ack())?; - self._feature = Feature::from_bits( - self._feature.into_bits() & !Feature::REG_MASK + self.feature = Feature::from_bits( + self.feature.into_bits() & !Feature::REG_MASK | (config.feature.into_bits() & Feature::REG_MASK), ); self.spi_write_byte(registers::DYNPD, 0x3F * (config.dynamic_payloads() as u8))?; self.spi_write_byte( registers::FEATURE, - self._feature.into_bits() & Feature::REG_MASK, + self.feature.into_bits() & Feature::REG_MASK, )?; let setup_rf_reg_val = config.setup_rf_aw.into_bits() & 0x27u8; @@ -99,8 +97,8 @@ where // Enable PTX // Do not write CE high so radio will remain in standby-I mode. // PTX should use only 22uA of power in standby-I mode. - self._config_reg = config.config_reg.with_power(true); - self.spi_write_byte(registers::CONFIG, self._config_reg.into_bits()) + self.config_reg = config.config_reg.with_power(true); + self.spi_write_byte(registers::CONFIG, self.config_reg.into_bits()) } } diff --git a/crates/rf24-rs/src/radio/rf24/mod.rs b/crates/rf24-rs/src/radio/rf24/mod.rs index 2148b04..9024d43 100644 --- a/crates/rf24-rs/src/radio/rf24/mod.rs +++ b/crates/rf24-rs/src/radio/rf24/mod.rs @@ -1,4 +1,8 @@ -use embedded_hal::{delay::DelayNs, digital::OutputPin, spi::SpiDevice}; +use embedded_hal::{ + delay::DelayNs, + digital::{Error as _, ErrorKind as OutputPinError, OutputPin}, + spi::{Error as _, ErrorKind as SpiError, SpiDevice}, +}; mod auto_ack; pub(crate) mod bit_fields; mod channel; @@ -17,7 +21,7 @@ pub use constants::{commands, mnemonics, registers}; mod details; mod status; use super::prelude::{ - EsbAutoAck, EsbChannel, EsbCrcLength, EsbFifo, EsbPaLevel, EsbPower, EsbRadio, + EsbAutoAck, EsbChannel, EsbCrcLength, EsbFifo, EsbPaLevel, EsbPower, EsbRadio, RadioErrorType, }; use crate::{ types::{CrcLength, PaLevel}, @@ -40,6 +44,27 @@ pub enum Nrf24Error { NotAsTxError, } +impl From for Nrf24Error { + fn from(value: SpiError) -> Self { + Nrf24Error::Spi(value) + } +} + +impl From for Nrf24Error { + fn from(value: OutputPinError) -> Self { + Nrf24Error::Gpo(value) + } +} + +impl RadioErrorType for RF24 +where + SPI: SpiDevice, + DO: OutputPin, + DELAY: DelayNs, +{ + type Error = Nrf24Error; +} + /// This struct implements the [`Esb*` traits](mod@crate::radio::prelude) /// for the nRF24L01 transceiver. /// @@ -67,20 +92,21 @@ pub struct RF24 { /// /// pub tx_delay: u32, - _spi: SPI, + spi: SPI, /// The CE pin for the radio. /// /// This really only exposed for advanced manipulation of active TX mode. /// It is strongly recommended to enter RX or TX mode using [`RF24::as_rx()`] and /// [`RF24::as_tx()`] because those methods guarantee proper radio usage. pub ce_pin: DO, - _delay_impl: DELAY, - _buf: [u8; 33], - _status: StatusFlags, - _config_reg: Config, - _feature: Feature, - _pipe0_rx_addr: Option<[u8; 5]>, - _payload_length: u8, + delay_impl: DELAY, + buf: [u8; 33], + status: StatusFlags, + config_reg: Config, + feature: Feature, + pipe0_rx_addr: Option<[u8; 5]>, + tx_addr: [u8; 5], + payload_length: u8, } impl RF24 @@ -99,25 +125,26 @@ where RF24 { tx_delay: 250, ce_pin, - _spi: spi, - _delay_impl: delay_impl, - _status: StatusFlags::from_bits(0), - _buf: [0u8; 33], - _pipe0_rx_addr: None, - _feature: Feature::from_bits(0) + spi, + delay_impl, + status: StatusFlags::from_bits(0), + buf: [0u8; 33], + pipe0_rx_addr: None, + tx_addr: [0xE7; 5], + feature: Feature::from_bits(0) .with_address_length(5) .with_is_plus_variant(true), // 16 bit CRC, enable all IRQ, and power down as TX - _config_reg: Config::from_bits(0xC), - _payload_length: 32, + config_reg: Config::from_bits(0xC), + payload_length: 32, } } - fn spi_transfer(&mut self, len: u8) -> Result<(), Nrf24Error> { - self._spi - .transfer_in_place(&mut self._buf[..len as usize]) - .map_err(Nrf24Error::Spi)?; - self._status = StatusFlags::from_bits(self._buf[0]); + fn spi_transfer(&mut self, len: u8) -> Result<(), Nrf24Error> { + self.spi + .transfer_in_place(&mut self.buf[..len as usize]) + .map_err(|e| e.kind())?; + self.status = StatusFlags::from_bits(self.buf[0]); Ok(()) } @@ -126,8 +153,12 @@ where /// self.spi_read(0, commands::NOP)?; /// // STATUS register is now stored in self._status /// ``` - fn spi_read(&mut self, len: u8, command: u8) -> Result<(), Nrf24Error> { - self._buf[0] = command; + fn spi_read( + &mut self, + len: u8, + command: u8, + ) -> Result<(), Nrf24Error> { + self.buf[0] = command; self.spi_transfer(len + 1) } @@ -135,9 +166,9 @@ where &mut self, command: u8, byte: u8, - ) -> Result<(), Nrf24Error> { - self._buf[0] = command | commands::W_REGISTER; - self._buf[1] = byte; + ) -> Result<(), Nrf24Error> { + self.buf[0] = command | commands::W_REGISTER; + self.buf[1] = byte; self.spi_transfer(2) } @@ -145,18 +176,18 @@ where &mut self, command: u8, buf: &[u8], - ) -> Result<(), Nrf24Error> { - self._buf[0] = command | commands::W_REGISTER; + ) -> Result<(), Nrf24Error> { + self.buf[0] = command | commands::W_REGISTER; let buf_len = buf.len(); - self._buf[1..(buf_len + 1)].copy_from_slice(&buf[..buf_len]); + self.buf[1..(buf_len + 1)].copy_from_slice(&buf[..buf_len]); self.spi_transfer(buf_len as u8 + 1) } /// A private function to write a special SPI command specific to older /// non-plus variants of the nRF24L01 radio module. It has no effect on plus variants. - fn toggle_features(&mut self) -> Result<(), Nrf24Error> { - self._buf[0] = commands::ACTIVATE; - self._buf[1] = 0x73; + fn toggle_features(&mut self) -> Result<(), Nrf24Error> { + self.buf[0] = commands::ACTIVATE; + self.buf[1] = 0x73; self.spi_transfer(2) } @@ -165,23 +196,23 @@ where /// The bool that this function returns is only valid _after_ calling /// [`init()`](fn@crate::radio::prelude::EsbInit::init). pub fn is_plus_variant(&self) -> bool { - self._feature.is_plus_variant() + self.feature.is_plus_variant() } - pub fn rpd(&mut self) -> Result> { + pub fn rpd(&mut self) -> Result> { self.spi_read(1, registers::RPD)?; - Ok(self._buf[1] & 1 == 1) + Ok(self.buf[1] & 1 == 1) } pub fn start_carrier_wave( &mut self, level: PaLevel, channel: u8, - ) -> Result<(), Nrf24Error> { + ) -> Result<(), Nrf24Error> { self.as_tx()?; self.spi_read(1, registers::RF_SETUP)?; - self.spi_write_byte(registers::RF_SETUP, self._buf[1] | 0x90)?; - if self._feature.is_plus_variant() { + self.spi_write_byte(registers::RF_SETUP, self.buf[1] | 0x90)?; + if self.feature.is_plus_variant() { self.set_auto_ack(false)?; self.set_auto_retries(0, 0)?; let buf = [0xFF; 32]; @@ -197,15 +228,15 @@ where } self.set_pa_level(level)?; self.set_channel(channel)?; - self.ce_pin.set_high().map_err(Nrf24Error::Gpo)?; - if self._feature.is_plus_variant() { - self._delay_impl.delay_ns(1000000); // datasheet says 1 ms is ok in this instance + self.ce_pin.set_high().map_err(|e| e.kind())?; + if self.feature.is_plus_variant() { + self.delay_impl.delay_ns(1000000); // datasheet says 1 ms is ok in this instance self.rewrite()?; } Ok(()) } - pub fn stop_carrier_wave(&mut self) -> Result<(), Nrf24Error> { + pub fn stop_carrier_wave(&mut self) -> Result<(), Nrf24Error> { /* * A note from the datasheet: * Do not use REUSE_TX_PL together with CONT_WAVE=1. When both these @@ -214,8 +245,9 @@ where */ self.power_down()?; // per datasheet recommendation (just to be safe) self.spi_read(1, registers::RF_SETUP)?; - self.spi_write_byte(registers::RF_SETUP, self._buf[1] & !0x90)?; - self.ce_pin.set_low().map_err(Nrf24Error::Gpo) + self.spi_write_byte(registers::RF_SETUP, self.buf[1] & !0x90)?; + self.ce_pin.set_low().map_err(|e| e.kind())?; + Ok(()) } /// Control the builtin LNA feature on nRF24L01 (older non-plus variants) and Si24R1 @@ -226,9 +258,9 @@ where /// /// This function has no effect on nRF24L01+ modules and PA/LNA variants because /// the LNA feature is always enabled. - pub fn set_lna(&mut self, enable: bool) -> Result<(), Nrf24Error> { + pub fn set_lna(&mut self, enable: bool) -> Result<(), Nrf24Error> { self.spi_read(1, registers::RF_SETUP)?; - let out = self._buf[1] & !1 | enable as u8; + let out = self.buf[1] & !1 | enable as u8; self.spi_write_byte(registers::RF_SETUP, out) } } @@ -284,6 +316,18 @@ mod test { vec![registers::CONFIG | commands::W_REGISTER, 0xCu8], vec![0xEu8, 0u8], ), + //set cached TX address + ( + vec![ + registers::RX_ADDR_P0 | commands::W_REGISTER, + 0xE7, + 0xE7, + 0xE7, + 0xE7, + 0xE7 + ], + vec![0xE, 0, 0, 0, 0, 0] + ), // open pipe 0 for TX (regardless of auto-ack) (vec![registers::EN_RXADDR, 0u8], vec![0xEu8, 0u8]), ( @@ -355,7 +399,7 @@ mod test { let mocks = mk_radio(&ce_expectations, &spi_expectations); let (mut radio, mut spi, mut ce_pin) = (mocks.0, mocks.1, mocks.2); - radio._feature = radio._feature.with_is_plus_variant(is_plus_variant); + radio.feature = radio.feature.with_is_plus_variant(is_plus_variant); radio.start_carrier_wave(crate::PaLevel::Max, 0xFF).unwrap(); spi.done(); ce_pin.done(); diff --git a/crates/rf24-rs/src/radio/rf24/pa_level.rs b/crates/rf24-rs/src/radio/rf24/pa_level.rs index d810784..d29ab08 100644 --- a/crates/rf24-rs/src/radio/rf24/pa_level.rs +++ b/crates/rf24-rs/src/radio/rf24/pa_level.rs @@ -1,7 +1,7 @@ use embedded_hal::{delay::DelayNs, digital::OutputPin, spi::SpiDevice}; use super::registers; -use crate::radio::{prelude::EsbPaLevel, Nrf24Error, RF24}; +use crate::radio::{prelude::EsbPaLevel, RF24}; use crate::PaLevel; impl EsbPaLevel for RF24 @@ -10,16 +10,14 @@ where DO: OutputPin, DELAY: DelayNs, { - type PaLevelErrorType = Nrf24Error; - - fn get_pa_level(&mut self) -> Result { + fn get_pa_level(&mut self) -> Result { self.spi_read(1, registers::RF_SETUP)?; - Ok(PaLevel::from_bits(self._buf[1] & PaLevel::MASK)) + Ok(PaLevel::from_bits(self.buf[1] & PaLevel::MASK)) } - fn set_pa_level(&mut self, pa_level: PaLevel) -> Result<(), Self::PaLevelErrorType> { + fn set_pa_level(&mut self, pa_level: PaLevel) -> Result<(), Self::Error> { self.spi_read(1, registers::RF_SETUP)?; - let out = self._buf[1] & !PaLevel::MASK | pa_level.into_bits(); + let out = self.buf[1] & !PaLevel::MASK | pa_level.into_bits(); self.spi_write_byte(registers::RF_SETUP, out) } } diff --git a/crates/rf24-rs/src/radio/rf24/payload_length.rs b/crates/rf24-rs/src/radio/rf24/payload_length.rs index bb8fadc..de61a97 100644 --- a/crates/rf24-rs/src/radio/rf24/payload_length.rs +++ b/crates/rf24-rs/src/radio/rf24/payload_length.rs @@ -9,45 +9,43 @@ where DO: OutputPin, DELAY: DelayNs, { - type PayloadLengthErrorType = Nrf24Error; - - fn set_payload_length(&mut self, length: u8) -> Result<(), Self::PayloadLengthErrorType> { + fn set_payload_length(&mut self, length: u8) -> Result<(), Self::Error> { let len = length.clamp(1, 32); for i in 0..6 { self.spi_write_byte(registers::RX_PW_P0 + i, len)?; } - self._payload_length = len; + self.payload_length = len; Ok(()) } - fn get_payload_length(&mut self) -> Result { + fn get_payload_length(&mut self) -> Result { self.spi_read(1, registers::RX_PW_P0)?; - Ok(self._buf[1]) + Ok(self.buf[1]) } - fn set_dynamic_payloads(&mut self, enable: bool) -> Result<(), Self::PayloadLengthErrorType> { + fn set_dynamic_payloads(&mut self, enable: bool) -> Result<(), Self::Error> { self.spi_read(1, registers::FEATURE)?; - self._feature = - Feature::from_bits(self._feature.into_bits() & !Feature::REG_MASK | self._buf[1]) + self.feature = + Feature::from_bits(self.feature.into_bits() & !Feature::REG_MASK | self.buf[1]) .with_dynamic_payloads(enable); self.spi_write_byte( registers::FEATURE, - self._feature.into_bits() & Feature::REG_MASK, + self.feature.into_bits() & Feature::REG_MASK, )?; self.spi_write_byte(registers::DYNPD, 0x3F * enable as u8)?; Ok(()) } fn get_dynamic_payloads(&self) -> bool { - self._feature.dynamic_payloads() + self.feature.dynamic_payloads() } - fn get_dynamic_payload_length(&mut self) -> Result { + fn get_dynamic_payload_length(&mut self) -> Result { self.spi_read(1, commands::R_RX_PL_WID)?; - if self._buf[1] > 32 { + if self.buf[1] > 32 { return Err(Nrf24Error::BinaryCorruption); } - Ok(self._buf[1]) + Ok(self.buf[1]) } } diff --git a/crates/rf24-rs/src/radio/rf24/pipe.rs b/crates/rf24-rs/src/radio/rf24/pipe.rs index 84c290f..cc9fb9a 100644 --- a/crates/rf24-rs/src/radio/rf24/pipe.rs +++ b/crates/rf24-rs/src/radio/rf24/pipe.rs @@ -1,6 +1,6 @@ use embedded_hal::{delay::DelayNs, digital::OutputPin, spi::SpiDevice}; -use crate::radio::{prelude::EsbPipe, Nrf24Error, RF24}; +use crate::radio::{prelude::EsbPipe, RF24}; use super::registers; @@ -10,9 +10,7 @@ where DO: OutputPin, DELAY: DelayNs, { - type PipeErrorType = Nrf24Error; - - fn open_rx_pipe(&mut self, pipe: u8, address: &[u8]) -> Result<(), Self::PipeErrorType> { + fn open_rx_pipe(&mut self, pipe: u8, address: &[u8]) -> Result<(), Self::Error> { if pipe > 5 { return Ok(()); } @@ -20,15 +18,15 @@ where if pipe < 2 { // Clamp the address length used: min(self._address_length, address.len()); // This means that we only write the bytes that were passed - let width = address.len().min(self._feature.address_length() as usize); + let width = address.len().min(self.feature.address_length() as usize); // If this is pipe 0, cache the address. This is needed because // open_tx_pipe() will overwrite the pipe 0 address, so // as_rx() will have to restore it. if pipe == 0 { - let mut cached_addr = self._pipe0_rx_addr.unwrap_or_default(); + let mut cached_addr = self.pipe0_rx_addr.unwrap_or_default(); cached_addr[..width].copy_from_slice(&address[..width]); - self._pipe0_rx_addr = Some(cached_addr); + self.pipe0_rx_addr = Some(cached_addr); } self.spi_write_buf(registers::RX_ADDR_P0 + pipe, &address[..width])?; } @@ -38,40 +36,42 @@ where } self.spi_read(1, registers::EN_RXADDR)?; - let out = self._buf[1] | (1 << pipe); + let out = self.buf[1] | (1 << pipe); self.spi_write_byte(registers::EN_RXADDR, out) } - fn open_tx_pipe(&mut self, address: &[u8]) -> Result<(), Self::PipeErrorType> { + fn open_tx_pipe(&mut self, address: &[u8]) -> Result<(), Self::Error> { + let len = address.len().min(self.feature.address_length() as usize); + self.tx_addr[0..len].copy_from_slice(&address[0..len]); self.spi_write_buf(registers::TX_ADDR, address)?; self.spi_write_buf(registers::RX_ADDR_P0, address) } /// If the given `pipe` number is not in range [0, 5], then this function does nothing. - fn close_rx_pipe(&mut self, pipe: u8) -> Result<(), Self::PipeErrorType> { + fn close_rx_pipe(&mut self, pipe: u8) -> Result<(), Self::Error> { if pipe > 5 { return Ok(()); } self.spi_read(1, registers::EN_RXADDR)?; - let out = self._buf[1] & !(1 << pipe); + let out = self.buf[1] & !(1 << pipe); self.spi_write_byte(registers::EN_RXADDR, out)?; if pipe == 0 { - self._pipe0_rx_addr = None; + self.pipe0_rx_addr = None; } Ok(()) } - fn set_address_length(&mut self, length: u8) -> Result<(), Self::PipeErrorType> { + fn set_address_length(&mut self, length: u8) -> Result<(), Self::Error> { let width = length.clamp(2, 5); self.spi_write_byte(registers::SETUP_AW, width - 2)?; - self._feature.set_address_length(width); + self.feature.set_address_length(width); Ok(()) } - fn get_address_length(&mut self) -> Result { + fn get_address_length(&mut self) -> Result { self.spi_read(1, registers::SETUP_AW)?; - let addr_length = self._buf[1].min(0xFD) + 2; - self._feature.set_address_length(addr_length); + let addr_length = self.buf[1].min(0xFD) + 2; + self.feature.set_address_length(addr_length); Ok(addr_length) } } diff --git a/crates/rf24-rs/src/radio/rf24/power.rs b/crates/rf24-rs/src/radio/rf24/power.rs index b0e9037..b57ac50 100644 --- a/crates/rf24-rs/src/radio/rf24/power.rs +++ b/crates/rf24-rs/src/radio/rf24/power.rs @@ -1,6 +1,10 @@ -use embedded_hal::{delay::DelayNs, digital::OutputPin, spi::SpiDevice}; +use embedded_hal::{ + delay::DelayNs, + digital::{Error, OutputPin}, + spi::SpiDevice, +}; -use crate::radio::{prelude::EsbPower, Nrf24Error, RF24}; +use crate::radio::{prelude::EsbPower, RF24}; use super::registers; @@ -10,8 +14,6 @@ where DO: OutputPin, DELAY: DelayNs, { - type PowerErrorType = Nrf24Error; - /// After calling [`EsbRadio::as_rx()`](fn@crate::radio::prelude::EsbRadio::as_rx), /// a non-PA/LNA radio will consume about /// 13.5mA at [`PaLevel::MAX`](type@crate::types::PaLevel::Max). @@ -21,20 +23,20 @@ where /// will consume about 26uA (.026mA). /// In full power down mode (a sleep state), the radio will consume approximately /// 900nA (.0009mA). - fn power_down(&mut self) -> Result<(), Self::PowerErrorType> { - self.ce_pin.set_low().map_err(Nrf24Error::Gpo)?; // Guarantee CE is low on powerDown - self._config_reg = self._config_reg.with_power(false); - self.spi_write_byte(registers::CONFIG, self._config_reg.into_bits())?; + fn power_down(&mut self) -> Result<(), Self::Error> { + self.ce_pin.set_low().map_err(|e| e.kind())?; // Guarantee CE is low on powerDown + self.config_reg = self.config_reg.with_power(false); + self.spi_write_byte(registers::CONFIG, self.config_reg.into_bits())?; Ok(()) } - fn power_up(&mut self, delay: Option) -> Result<(), Self::PowerErrorType> { + fn power_up(&mut self, delay: Option) -> Result<(), Self::Error> { // if not powered up then power up and wait for the radio to initialize - if self._config_reg.power() { + if self.config_reg.power() { return Ok(()); } - self._config_reg = self._config_reg.with_power(true); - self.spi_write_byte(registers::CONFIG, self._config_reg.into_bits())?; + self.config_reg = self.config_reg.with_power(true); + self.spi_write_byte(registers::CONFIG, self.config_reg.into_bits())?; // For nRF24L01+ to go from power down mode to TX or RX mode it must first pass through stand-by mode. // There must be a delay of Tpd2standby (see Table 16.) after the nRF24L01+ leaves power down mode before @@ -42,17 +44,17 @@ where match delay { Some(d) => { if d > 0 { - self._delay_impl.delay_us(d); + self.delay_impl.delay_us(d); } } - None => self._delay_impl.delay_us(5000), + None => self.delay_impl.delay_us(5000), } Ok(()) } /// Is the radio powered up? fn is_powered(&self) -> bool { - self._config_reg.power() + self.config_reg.power() } } diff --git a/crates/rf24-rs/src/radio/rf24/radio.rs b/crates/rf24-rs/src/radio/rf24/radio.rs index b7c6196..4647d63 100644 --- a/crates/rf24-rs/src/radio/rf24/radio.rs +++ b/crates/rf24-rs/src/radio/rf24/radio.rs @@ -1,9 +1,13 @@ -use super::{commands, mnemonics, registers, Nrf24Error, RF24}; +use super::{commands, mnemonics, registers, RF24}; use crate::{ radio::prelude::{EsbFifo, EsbPayloadLength, EsbPipe, EsbRadio, EsbStatus}, StatusFlags, }; -use embedded_hal::{delay::DelayNs, digital::OutputPin, spi::SpiDevice}; +use embedded_hal::{ + delay::DelayNs, + digital::{Error, OutputPin}, + spi::SpiDevice, +}; impl EsbRadio for RF24 where @@ -11,19 +15,17 @@ where DO: OutputPin, DELAY: DelayNs, { - type RadioErrorType = Nrf24Error; - - fn as_rx(&mut self) -> Result<(), Self::RadioErrorType> { - self._config_reg = self._config_reg.as_rx(); - self.spi_write_byte(registers::CONFIG, self._config_reg.into_bits())?; + fn as_rx(&mut self) -> Result<(), Self::Error> { + self.config_reg = self.config_reg.as_rx(); + self.spi_write_byte(registers::CONFIG, self.config_reg.into_bits())?; self.clear_status_flags(StatusFlags::new())?; - self.ce_pin.set_high().map_err(Nrf24Error::Gpo)?; + self.ce_pin.set_high().map_err(|e| e.kind())?; // Restore the pipe0 address, if exists - if let Some(addr) = self._pipe0_rx_addr { + if let Some(addr) = self.pipe0_rx_addr { self.spi_write_buf( registers::RX_ADDR_P0, - &addr[..self._feature.address_length() as usize], + &addr[..self.feature.address_length() as usize], )?; } else { self.close_rx_pipe(0)?; @@ -31,44 +33,50 @@ where Ok(()) } - fn as_tx(&mut self) -> Result<(), Self::RadioErrorType> { - self.ce_pin.set_low().map_err(Nrf24Error::Gpo)?; + fn as_tx(&mut self) -> Result<(), Self::Error> { + self.ce_pin.set_low().map_err(|e| e.kind())?; - self._delay_impl.delay_ns(self.tx_delay * 1000); - if self._feature.ack_payloads() { + self.delay_impl.delay_ns(self.tx_delay * 1000); + if self.feature.ack_payloads() { self.flush_tx()?; } - self._config_reg = self._config_reg.as_tx(); - self.spi_write_byte(registers::CONFIG, self._config_reg.into_bits())?; + self.config_reg = self.config_reg.as_tx(); + self.spi_write_byte(registers::CONFIG, self.config_reg.into_bits())?; + + // use `spi_transfer()` to avoid multiple borrows of self (`spi_write_buf()` and `tx_buf`) + let addr_len = self.feature.address_length(); + self.buf[0] = registers::RX_ADDR_P0 | commands::W_REGISTER; + self.buf[1..addr_len as usize + 1].copy_from_slice(&self.tx_addr[0..addr_len as usize]); + self.spi_transfer(addr_len + 1)?; self.spi_read(1, registers::EN_RXADDR)?; - let out = self._buf[1] | 1; + let out = self.buf[1] | 1; self.spi_write_byte(registers::EN_RXADDR, out) } fn is_rx(&self) -> bool { - self._config_reg.is_rx() + self.config_reg.is_rx() } /// See [`EsbRadio::send()`] for implementation-agnostic detail. /// /// This function calls [`RF24::flush_tx()`] upon entry, but it does not /// deactivate the radio's CE pin upon exit. - fn send(&mut self, buf: &[u8], ask_no_ack: bool) -> Result { - self.ce_pin.set_low().map_err(Nrf24Error::Gpo)?; + fn send(&mut self, buf: &[u8], ask_no_ack: bool) -> Result { + self.ce_pin.set_low().map_err(|e| e.kind())?; // this function only handles 1 payload at a time self.flush_tx()?; // flush the TX FIFO to ensure we are sending the given buf if !self.write(buf, ask_no_ack, true)? { // write() also clears the status flags and asserts the CE pin return Ok(false); } - self._delay_impl.delay_us(10); + self.delay_impl.delay_us(10); // now block until we get a tx_ds or tx_df event - while self._status.into_bits() & (mnemonics::MASK_MAX_RT | mnemonics::MASK_TX_DS) == 0 { + while self.status.into_bits() & (mnemonics::MASK_MAX_RT | mnemonics::MASK_TX_DS) == 0 { self.spi_read(0, commands::NOP)?; } - Ok(self._status.tx_ds()) + Ok(self.status.tx_ds()) } /// See [`EsbRadio::write()`] for implementation-agnostic detail. @@ -81,42 +89,36 @@ where /// microseconds when using this function, thus non-blocking behavior. /// /// - fn write( - &mut self, - buf: &[u8], - ask_no_ack: bool, - start_tx: bool, - ) -> Result { + fn write(&mut self, buf: &[u8], ask_no_ack: bool, start_tx: bool) -> Result { if self.is_rx() { // check if in RX mode to prevent improper radio usage - return Err(Self::RadioErrorType::NotAsTxError); + return Err(Self::Error::NotAsTxError); } self.clear_status_flags(StatusFlags::from_bits( mnemonics::MASK_MAX_RT | mnemonics::MASK_TX_DS, ))?; - if self._status.tx_full() { + if self.status.tx_full() { // TX FIFO is full already return Ok(false); } - let mut buf_len = buf.len().min(32) as u8; + let buf_len = buf.len().min(32); // to avoid resizing the given buf, we'll have to use self._buf directly - self._buf[0] = if !ask_no_ack { + self.buf[0] = if !ask_no_ack { commands::W_TX_PAYLOAD } else { commands::W_TX_PAYLOAD_NO_ACK }; - self._buf[1..(buf_len + 1) as usize].copy_from_slice(&buf[..buf_len as usize]); + self.buf[1..buf_len + 1].copy_from_slice(&buf[..buf_len]); // ensure payload_length setting is respected - if !self._feature.dynamic_payloads() && buf_len < self._payload_length { + if !self.feature.dynamic_payloads() && (buf_len as u8) < self.payload_length { // pad buf with zeros - for i in (buf_len + 1)..(self._payload_length + 1) { - self._buf[i as usize] = 0; - } - buf_len = self._payload_length; + self.buf[buf_len + 1..self.payload_length as usize + 1].fill(0); + self.spi_transfer(self.payload_length + 1)?; + } else { + self.spi_transfer(buf_len as u8 + 1)?; } - self.spi_transfer(buf_len + 1)?; if start_tx { - self.ce_pin.set_high().map_err(Nrf24Error::Gpo)?; + self.ce_pin.set_high().map_err(|e| e.kind())?; } Ok(true) } @@ -139,50 +141,49 @@ where /// padding for the data saved to the `buf` parameter's object. /// The nRF24L01 will repeatedly use the last byte from the last /// payload even when [`RF24::read()`] is called with an empty RX FIFO. - fn read(&mut self, buf: &mut [u8], len: Option) -> Result { + fn read(&mut self, buf: &mut [u8], len: Option) -> Result { let buf_len = - (buf.len().min(32) as u8).min(len.unwrap_or(if self._feature.dynamic_payloads() { + (buf.len().min(32) as u8).min(len.unwrap_or(if self.feature.dynamic_payloads() { self.get_dynamic_payload_length()? } else { - self._payload_length + self.payload_length })); if buf_len == 0 { return Ok(0); } self.spi_read(buf_len, commands::R_RX_PAYLOAD)?; - for i in 0..buf_len { - buf[i as usize] = self._buf[i as usize + 1]; - } + buf[0..buf_len as usize].copy_from_slice(&self.buf[1..buf_len as usize + 1]); let flags = StatusFlags::from_bits(mnemonics::MASK_RX_DR); self.clear_status_flags(flags)?; Ok(buf_len) } - fn resend(&mut self) -> Result { + fn resend(&mut self) -> Result { if self.is_rx() { // if in RX mode, prevent infinite loop below return Ok(false); } self.rewrite()?; - self._delay_impl.delay_us(10); + self.delay_impl.delay_us(10); // now block until a tx_ds or tx_df event occurs - while self._status.into_bits() & 0x30 == 0 { + while self.status.into_bits() & 0x30 == 0 { self.spi_read(0, commands::NOP)?; } - Ok(self._status.tx_ds()) + Ok(self.status.tx_ds()) } - fn rewrite(&mut self) -> Result<(), Self::RadioErrorType> { - self.ce_pin.set_low().map_err(Nrf24Error::Gpo)?; + fn rewrite(&mut self) -> Result<(), Self::Error> { + self.ce_pin.set_low().map_err(|e| e.kind())?; let flags = StatusFlags::from_bits(mnemonics::MASK_TX_DS | mnemonics::MASK_MAX_RT); self.clear_status_flags(flags)?; self.spi_read(0, commands::REUSE_TX_PL)?; - self.ce_pin.set_high().map_err(Nrf24Error::Gpo) + self.ce_pin.set_high().map_err(|e| e.kind())?; + Ok(()) } - fn get_last_arc(&mut self) -> Result { + fn get_last_arc(&mut self) -> Result { self.spi_read(1, registers::OBSERVE_TX)?; - Ok(self._buf[1] & 0xF) + Ok(self.buf[1] & 0xF) } } @@ -256,7 +257,7 @@ mod test { vec![registers::STATUS | commands::W_REGISTER, 0x70u8], vec![0xEu8, 0u8], ), - // write cached _pipe0_rx_addr + // write cached pipe0_rx_addr (buf_expected.to_vec(), vec![0xEu8, 0u8, 0u8, 0u8, 0u8, 0u8]), ]; let mocks = mk_radio(&ce_expectations, &spi_expectations); @@ -279,6 +280,18 @@ mod test { vec![registers::CONFIG | commands::W_REGISTER, 0xCu8], vec![0xEu8, 0u8], ), + // set cached TX address to RX pipe 0 + ( + vec![ + registers::RX_ADDR_P0 | commands::W_REGISTER, + 0xE7, + 0xE7, + 0xE7, + 0xE7, + 0xE7 + ], + vec![0xE, 0, 0, 0, 0, 0] + ), // open pipe 0 for TX (regardless of auto-ack) (vec![registers::EN_RXADDR, 0u8], vec![0xEu8, 0u8]), ( @@ -288,7 +301,7 @@ mod test { ]; let mocks = mk_radio(&ce_expectations, &spi_expectations); let (mut radio, mut spi, mut ce_pin) = (mocks.0, mocks.1, mocks.2); - radio._feature = radio._feature.with_ack_payloads(true); + radio.feature = radio.feature.with_ack_payloads(true); radio.as_tx().unwrap(); spi.done(); ce_pin.done(); @@ -343,7 +356,7 @@ mod test { assert!(radio.send(&payload, false).unwrap()); // again using simulated full TX FIFO assert!(!radio.send(&payload, false).unwrap()); - radio._config_reg = radio._config_reg.as_rx(); // simulate RX mode + radio.config_reg = radio.config_reg.as_rx(); // simulate RX mode assert!(radio.send(&payload, false).is_err()); spi.done(); ce_pin.done(); @@ -384,7 +397,7 @@ mod test { let (mut radio, mut spi, mut ce_pin) = (mocks.0, mocks.1, mocks.2); assert!(radio.write(&payload, true, false).unwrap()); // upload a dynamically sized payload - radio._feature = radio._feature.with_dynamic_payloads(true); + radio.feature = radio.feature.with_dynamic_payloads(true); assert!(radio.write(&payload, true, false).unwrap()); spi.done(); ce_pin.done(); @@ -428,7 +441,7 @@ mod test { assert_eq!(32u8, radio.read(&mut payload, None).unwrap()); assert_eq!(payload, [0x55u8; 32]); assert_eq!(0u8, radio.read(&mut payload, Some(0)).unwrap()); - radio._feature = radio._feature.with_dynamic_payloads(true); + radio.feature = radio.feature.with_dynamic_payloads(true); assert_eq!(32u8, radio.read(&mut payload, None).unwrap()); assert_eq!(payload, [0xAA; 32]); spi.done(); @@ -458,7 +471,7 @@ mod test { let mocks = mk_radio(&ce_expectations, &spi_expectations); let (mut radio, mut spi, mut ce_pin) = (mocks.0, mocks.1, mocks.2); assert!(radio.resend().unwrap()); - radio._config_reg = radio._config_reg.as_rx(); // simulate RX mode + radio.config_reg = radio.config_reg.as_rx(); // simulate RX mode assert!(!radio.resend().unwrap()); spi.done(); ce_pin.done(); diff --git a/crates/rf24-rs/src/radio/rf24/status.rs b/crates/rf24-rs/src/radio/rf24/status.rs index d3cb331..06c9b29 100644 --- a/crates/rf24-rs/src/radio/rf24/status.rs +++ b/crates/rf24-rs/src/radio/rf24/status.rs @@ -1,7 +1,7 @@ use embedded_hal::{delay::DelayNs, digital::OutputPin, spi::SpiDevice}; use crate::{ - radio::{prelude::EsbStatus, Nrf24Error, RF24}, + radio::{prelude::EsbStatus, RF24}, types::StatusFlags, }; @@ -13,26 +13,24 @@ where DO: OutputPin, DELAY: DelayNs, { - type StatusErrorType = Nrf24Error; - - fn set_status_flags(&mut self, flags: StatusFlags) -> Result<(), Self::StatusErrorType> { + fn set_status_flags(&mut self, flags: StatusFlags) -> Result<(), Self::Error> { self.spi_read(1, registers::CONFIG)?; - self._config_reg = Config::from_bits( - self._buf[1] & !StatusFlags::IRQ_MASK | (!flags.into_bits() & StatusFlags::IRQ_MASK), + self.config_reg = Config::from_bits( + self.buf[1] & !StatusFlags::IRQ_MASK | (!flags.into_bits() & StatusFlags::IRQ_MASK), ); - self.spi_write_byte(registers::CONFIG, self._config_reg.into_bits()) + self.spi_write_byte(registers::CONFIG, self.config_reg.into_bits()) } - fn clear_status_flags(&mut self, flags: StatusFlags) -> Result<(), Self::StatusErrorType> { + fn clear_status_flags(&mut self, flags: StatusFlags) -> Result<(), Self::Error> { self.spi_write_byte(registers::STATUS, flags.into_bits() & StatusFlags::IRQ_MASK) } - fn update(&mut self) -> Result<(), Self::StatusErrorType> { + fn update(&mut self) -> Result<(), Self::Error> { self.spi_read(0, commands::NOP) } fn get_status_flags(&self, flags: &mut StatusFlags) { - *flags = self._status; + *flags = self.status; } } diff --git a/crates/rf24ble-rs/src/radio.rs b/crates/rf24ble-rs/src/radio.rs index 9380753..919b64e 100644 --- a/crates/rf24ble-rs/src/radio.rs +++ b/crates/rf24ble-rs/src/radio.rs @@ -2,7 +2,11 @@ use crate::{ data_manipulation::{crc24_ble, reverse_bits, whiten}, services::BlePayload, }; -use embedded_hal::{delay::DelayNs, digital::OutputPin, spi::SpiDevice}; +use embedded_hal::{ + delay::DelayNs, + digital::{ErrorKind as OutputPinError, OutputPin}, + spi::{ErrorKind as SpiError, SpiDevice}, +}; use rf24::{ radio::{ prelude::{EsbChannel, EsbPaLevel, EsbRadio}, @@ -183,7 +187,7 @@ impl FakeBle { pub fn hop_channel( &self, radio: &mut RF24, - ) -> Result<(), Nrf24Error> + ) -> Result<(), Nrf24Error> where SPI: SpiDevice, DO: OutputPin, @@ -284,7 +288,7 @@ impl FakeBle { &self, radio: &mut RF24, buf: &[u8], - ) -> Result> + ) -> Result> where SPI: SpiDevice, DO: OutputPin, @@ -325,7 +329,7 @@ impl FakeBle { pub fn read( &self, radio: &mut RF24, - ) -> Result, Nrf24Error> + ) -> Result, Nrf24Error> where SPI: SpiDevice, DO: OutputPin,