Compare commits
No commits in common. "1a7f8c4ab18a49bb2808f8700db5b594daa65c0b" and "36bdf9dd16e19bb4a4349689d34e6995f4fafd73" have entirely different histories.
1a7f8c4ab1
...
36bdf9dd16
27
CHANGELOG.md
27
CHANGELOG.md
|
@ -5,38 +5,17 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## Unreleased - v0.3.0
|
||||
### Added
|
||||
## Unreleased - v0.2.0
|
||||
|
||||
### Changed
|
||||
|
||||
### Deprecated
|
||||
|
||||
### Removed
|
||||
|
||||
### Fixed
|
||||
|
||||
### Security
|
||||
|
||||
## v0.2.0
|
||||
### Added
|
||||
- `Rn2903::system_version_bytes()`
|
||||
- `Rn2903::system_factory_reset()`
|
||||
- `Rn2903::system_module_reset()`
|
||||
- `Rn2903::mac_pause()` and `::mac_resume()`
|
||||
- `Rn2903::system_{get, set}_nvm()`
|
||||
- `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
|
||||
- `BadResponse` and `CannotPause` error variants
|
||||
- Examples directory with LED blinky and LoRa packet RX examples
|
||||
|
||||
### Changed
|
||||
- README.md example (now showing packet RX)
|
||||
|
||||
### Deprecated
|
||||
|
||||
|
|
|
@ -131,7 +131,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "rn2903"
|
||||
version = "0.2.0"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serialport 3.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "rn2903"
|
||||
version = "0.2.0"
|
||||
version = "0.1.0"
|
||||
authors = ["Leonora Tindall <nora@nora.codes>"]
|
||||
edition = "2018"
|
||||
license = "GPL-3.0-only"
|
||||
|
|
16
README.md
16
README.md
|
@ -13,20 +13,22 @@ not directly depend on unstable crates.
|
|||
|
||||
## Example
|
||||
|
||||
For instance, here is a simple program which dumps all LoRa packets received.
|
||||
For instance, here is a simple program which blinks the LoStik's LED using the RN2903's
|
||||
GPIO functionality.
|
||||
|
||||
```rust
|
||||
use rn2903::{Rn2903, ModulationMode};
|
||||
use rn2903::Rn2903;
|
||||
use std::time::Duration;
|
||||
use std::thread;
|
||||
|
||||
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 {
|
||||
if let Some(packet) = txvr.radio_rx(65535).unwrap() {
|
||||
println!("{:?}", packet);
|
||||
}
|
||||
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));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use rn2903::{bytes_to_string, Rn2903};
|
||||
use rn2903::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 {
|
||||
if let Some(packet) = txvr.radio_rx(65535).unwrap() {
|
||||
println!("{:?}", packet);
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
use rn2903::{NvmAddress, Rn2903};
|
||||
use std::env::args;
|
||||
use std::process::exit;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
fn main() {
|
||||
let args: Vec<_> = args().collect();
|
||||
if args.len() <= 1 {
|
||||
eprintln!("rn2903_nvm_set_get <serial port>");
|
||||
eprintln!("\tGet, modify, check, and restore the contents of NVM::0x300.");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
let mut txvr = Rn2903::new_at(&args[1]).expect("Could not open device. Error");
|
||||
println!(
|
||||
"Successfully connected. Version: {}",
|
||||
txvr.system_version()
|
||||
.expect("Could not read from device. Error:")
|
||||
);
|
||||
|
||||
txvr.system_module_reset().unwrap();
|
||||
txvr.transact(b"mac pause").unwrap();
|
||||
let addr = NvmAddress::new(0x300);
|
||||
|
||||
txvr.transact(b"sys set pindig GPIO10 1").unwrap();
|
||||
let prev = txvr.system_get_nvm(addr).unwrap();
|
||||
println!("Previous value: {:#x}", prev);
|
||||
txvr.transact(b"sys set pindig GPIO10 0").unwrap();
|
||||
|
||||
txvr.transact(b"sys set pindig GPIO11 1").unwrap();
|
||||
txvr.system_set_nvm(addr, 0xAB).unwrap();
|
||||
println!("Wrote new value");
|
||||
txvr.transact(b"sys set pindig GPIO11 0").unwrap();
|
||||
|
||||
txvr.transact(b"sys set pindig GPIO10 1").unwrap();
|
||||
let new = txvr.system_get_nvm(addr).unwrap();
|
||||
println!("New value: {:#x}", new);
|
||||
txvr.transact(b"sys set pindig GPIO10 0").unwrap();
|
||||
|
||||
txvr.transact(b"sys set pindig GPIO11 1").unwrap();
|
||||
txvr.system_set_nvm(addr, prev).unwrap();
|
||||
println!("Restored old value");
|
||||
txvr.transact(b"sys set pindig GPIO11 0").unwrap();
|
||||
}
|
125
src/lib.rs
125
src/lib.rs
|
@ -8,23 +8,6 @@
|
|||
//! 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
|
||||
|
@ -66,12 +49,6 @@ 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.
|
||||
|
@ -331,34 +308,6 @@ impl Rn2903 {
|
|||
}
|
||||
}
|
||||
|
||||
/// An address in user-accessible nonvolatile memory. Guaranteed to be between 0x300 and
|
||||
/// 0x3FF.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct NvmAddress(u16);
|
||||
|
||||
impl NvmAddress {
|
||||
/// Create a new `NvmAddress` from a `u16`. The given value must be between 0x300 and
|
||||
/// 0x3FF.
|
||||
///
|
||||
/// # Panics
|
||||
/// Panics if the given value is not between 0x300 and 0x3FF.
|
||||
pub fn new(value: u16) -> NvmAddress {
|
||||
if value < 0x300 {
|
||||
panic!("Attempted to construct NvmAddress less than 0x300.");
|
||||
}
|
||||
if value > 0x3FF {
|
||||
panic!("Attempted to construct NvmAddress more than 0x3FF.");
|
||||
}
|
||||
NvmAddress(value)
|
||||
}
|
||||
|
||||
/// Return the address to which this NvmAddress refers. Guaranteed to be between
|
||||
/// 0x300 and 0x3FF.
|
||||
pub fn inner(self) -> u16 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// # System API Functions
|
||||
impl Rn2903 {
|
||||
/// Queries the module for its firmware version information.
|
||||
|
@ -391,80 +340,6 @@ impl Rn2903 {
|
|||
pub fn system_factory_reset(&mut self) -> Result<Vec<u8>> {
|
||||
self.transact(b"sys factoryRESET")
|
||||
}
|
||||
|
||||
/// Set the value of the on-MCU nonvolatile memory at the given address to the given
|
||||
/// value.
|
||||
pub fn system_set_nvm(&mut self, address: NvmAddress, value: u8) -> Result<()> {
|
||||
self.transact_expecting(
|
||||
&format!("sys set nvm {:x} {:x}", address.inner(), value).into_bytes(),
|
||||
b"ok",
|
||||
)
|
||||
}
|
||||
|
||||
/// Get the value of the on-MCU nonvolatile memory at the given address.
|
||||
pub fn system_get_nvm(&mut self, address: NvmAddress) -> Result<u8> {
|
||||
let response = bytes_to_string(
|
||||
&self.transact(&format!("sys get nvm {:x}", address.inner()).into_bytes())?,
|
||||
);
|
||||
match u8::from_str_radix(&response, 16) {
|
||||
Ok(v) => Ok(v),
|
||||
Err(_) => Err(Error::bad_response("<integer>", response)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 <value>
|
||||
}
|
||||
|
||||
/// # 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<Option<Vec<u8>>> {
|
||||
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<Vec<u8>, _> = 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>",
|
||||
bytes_to_string(&response),
|
||||
)),
|
||||
}
|
||||
}
|
||||
_ => Err(Error::bad_response(
|
||||
"radio_err | radio_rx <bytes>",
|
||||
bytes_to_string(&response),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// # MAC API Functions
|
||||
|
|
Loading…
Reference in New Issue