diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f59291..b76961b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,14 +13,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `Rn2903::system_module_reset()` - `Rn2903::mac_pause()` and `::mac_resume()` - `Rn2903::system_{get, set}_nvm()` -- `BadResponse` and `CannotPause` error variants +- `Rn2903::radio_set_modulation_mode()` +- `Rn2903::radio_rx()` +- `BadResponse`, `TransceiverBusy`, and `CannotPause` error variants - `NvmAddress` newtype for representing values that can be passed to NVM functions +- `ModulationMode` enum listing available modulation modes - Examples directory: - LED blink - NVM read/write - LoRa packet RX ### Changed +- README.md example (now showing packet RX) ### Deprecated diff --git a/README.md b/README.md index 100c1ce..88fe258 100644 --- a/README.md +++ b/README.md @@ -13,22 +13,20 @@ not directly depend on unstable crates. ## Example -For instance, here is a simple program which blinks the LoStik's LED using the RN2903's -GPIO functionality. +For instance, here is a simple program which dumps all LoRa packets received. ```rust -use rn2903::Rn2903; -use std::time::Duration; -use std::thread; +use rn2903::{Rn2903, ModulationMode}; fn main() { let mut txvr = Rn2903::new_at("/dev/ttyUSB0") .expect("Could not open device. Error"); + txvr.mac_pause().unwrap(); + txvr.radio_set_modulation_mode(ModulationMode::LoRa).unwrap(); loop { - txvr.transact(b"sys set pindig GPIO10 0").unwrap(); - thread::sleep(Duration::from_millis(1000)); - txvr.transact(b"sys set pindig GPIO10 1").unwrap(); - thread::sleep(Duration::from_millis(1000)); + if let Some(packet) = txvr.radio_rx(65535).unwrap() { + println!("{:?}", packet); + } } } ``` diff --git a/examples/lora_packet_rx.rs b/examples/lora_packet_rx.rs index 58736bb..4e7a0be 100644 --- a/examples/lora_packet_rx.rs +++ b/examples/lora_packet_rx.rs @@ -1,4 +1,4 @@ -use rn2903::Rn2903; +use rn2903::{bytes_to_string, Rn2903}; use std::env::args; use std::process::exit; use std::thread; @@ -21,12 +21,12 @@ fn main() { txvr.mac_pause().unwrap(); txvr.transact(b"sys set pindig GPIO10 0").unwrap(); - txvr.transact(b"radio rx 0").unwrap(); loop { - println!("{:?}", txvr.read_line().unwrap()); - txvr.transact(b"sys set pindig GPIO10 1").unwrap(); - thread::sleep(Duration::from_millis(100)); - txvr.transact(b"sys set pindig GPIO10 0").unwrap(); - txvr.transact(b"radio rx 0").unwrap(); + if let Some(packet) = txvr.radio_rx(65535).unwrap() { + println!("{:?}", packet); + txvr.transact(b"sys set pindig GPIO10 1").unwrap(); + thread::sleep(Duration::from_millis(100)); + txvr.transact(b"sys set pindig GPIO10 0").unwrap(); + } } } diff --git a/src/lib.rs b/src/lib.rs index 4153461..3c9c2ad 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,23 @@ //! TTY or virtual COM port, or a RN2903 connected via a TTL serial interface. //! //! See the [`Rn2903` struct](struct.Rn2903.html) for the bulk of the crate's functionality. +//! +//! # Examples +//! +//! Receiving and printing valid LoRa payloads. +//! +//! ```no_run +//! # use rn2903::{Rn2903, ModulationMode}; +//! let mut txvr = Rn2903::new_at("/dev/ttyUSB0") +//! .expect("Could not open device. Error"); +//! txvr.mac_pause().unwrap(); +//! txvr.radio_set_modulation_mode(ModulationMode::LoRa).unwrap(); +//! loop { +//! if let Some(packet) = txvr.radio_rx(65535).unwrap() { +//! println!("{:?}", packet); +//! } +//! } +//! ``` // One of the critical aspects of this library is error handling. Because it is intended // to communicate with an external device, any operation could discover a disconnection @@ -49,6 +66,12 @@ quick_error! { description("the LoRaWAN MAC cannot be paused") display("The LoRaWAN MAC cannot be paused right now, but a pause was requested.") } + /// The transceiver is busy with another operation, or is under the control of + /// the MAC, and cannot be used to perform the requested operation. + TransceiverBusy { + description("the radio transceiver hardware is in use") + display("The LoRa/FSK radio transceiver hardware is in use by another operation or the MAC layer and cannot be used to perform the requested operation.") + } /// The program has become disconnected from the RN2903 module due to an I/O /// error. It is possible the device was physically disconnected, or that the /// host operating system closed the serial port for some reason. @@ -390,6 +413,60 @@ impl Rn2903 { } } +/// Types of modulation available for transmitting and receiving packets. +#[derive(Debug, PartialEq, Eq)] +pub enum ModulationMode { + /// Regular digital frequency shift keying mode + Fsk, + /// LoRa chirp spread spectrum mode + LoRa, // TODO: GFSK with radio set bt +} + +/// # Radio API Functions +impl Rn2903 { + /// Set the modulation mode used by the radio for transmission and reception. + pub fn radio_set_modulation_mode(&mut self, mode: ModulationMode) -> Result<()> { + match mode { + ModulationMode::Fsk => self.transact_expecting(b"radio set mod fsk", b"ok"), + ModulationMode::LoRa => self.transact_expecting(b"radio set mod lora", b"ok"), + } + } + + /// Open the receiver for the given timeout in symbols (for LoRa) or milliseconds + /// (for FSK), returning `Ok(Some(_))` if a valid packet is received or `Ok(None)` if + /// no packet is received before the timeout. + pub fn radio_rx(&mut self, timeout: u16) -> Result>> { + let result = self.transact(&format!("radio rx {}", timeout).into_bytes())?; + match &result[..] { + b"ok" => (), + b"busy" => return Err(Error::TransceiverBusy), + v => return Err(Error::bad_response("ok | busy", bytes_to_string(v))), + }; + let response = self.read_line()?; + match &response[0..9] { + b"radio_err" => Ok(None), + b"radio_rx " => { + let response_bytes: std::result::Result, _> = response[10..] + .chunks(2) + .map(bytes_to_string) + .map(|b| u8::from_str_radix(&b, 16)) + .collect(); + match response_bytes { + Ok(v) => Ok(Some(v)), + Err(_) => Err(Error::bad_response( + "radio_rx ", + bytes_to_string(&response), + )), + } + } + _ => Err(Error::bad_response( + "radio_err | radio_rx ", + bytes_to_string(&response), + )), + } + } +} + /// # MAC API Functions impl Rn2903 { /// Pauses the LoRaWAN MAC functionality on the device, returning the number of