mod config;
mod fragment;
mod sx127x;
pub use config::{Bandwidth, CodingRate, LoRaConfig, LoRaProfile, SpreadingFactor};
pub use fragment::{FragmentAssembler, FragmentHeader};
pub use sx127x::{DioPin, SpiDevice, Sx127x};
use crate::error::{Error, Result};
use crate::rtps::Locator;
use crate::transport::Transport;
pub const LORA_MAX_PACKET: usize = 255;
pub struct LoRaTransport<SPI: SpiDevice, DIO0: DioPin> {
radio: Sx127x<SPI, DIO0>,
config: LoRaConfig,
assembler: FragmentAssembler,
tx_seq: u8,
node_id: u8,
}
impl<SPI: SpiDevice, DIO0: DioPin> LoRaTransport<SPI, DIO0> {
pub fn new(spi: SPI, dio0: DIO0, config: LoRaConfig, node_id: u8) -> Result<Self> {
let mut radio = Sx127x::new(spi, dio0);
radio.reset()?;
radio.set_mode_sleep()?;
radio.set_lora_mode()?;
radio.set_frequency(config.frequency_mhz)?;
radio.set_spreading_factor(config.spreading_factor)?;
radio.set_bandwidth(config.bandwidth)?;
radio.set_coding_rate(config.coding_rate)?;
radio.set_tx_power(config.tx_power_dbm)?;
radio.set_mode_standby()?;
Ok(Self {
radio,
config,
assembler: FragmentAssembler::new(),
tx_seq: 0,
node_id,
})
}
pub fn rssi(&mut self) -> Result<i16> {
self.radio.read_rssi()
}
pub fn snr(&mut self) -> Result<i8> {
self.radio.read_snr()
}
pub const fn node_id(&self) -> u8 {
self.node_id
}
fn send_raw(&mut self, data: &[u8]) -> Result<usize> {
if data.len() > LORA_MAX_PACKET {
return Err(Error::BufferTooSmall);
}
self.radio.send(data)?;
Ok(data.len())
}
fn recv_raw(&mut self, buf: &mut [u8]) -> Result<usize> {
self.radio.recv(buf, self.config.rx_timeout_ms)
}
fn next_seq(&mut self) -> u8 {
let seq = self.tx_seq;
self.tx_seq = self.tx_seq.wrapping_add(1);
seq
}
}
impl<SPI: SpiDevice, DIO0: DioPin> Transport for LoRaTransport<SPI, DIO0> {
fn init(&mut self) -> Result<()> {
self.radio.set_mode_standby()
}
fn send(&mut self, data: &[u8], _dest: &Locator) -> Result<usize> {
if data.len() <= LORA_MAX_PACKET - FragmentHeader::SIZE {
let mut buf = [0u8; LORA_MAX_PACKET];
let header = FragmentHeader::single(self.node_id, self.next_seq());
let header_len = header.encode(&mut buf)?;
buf[header_len..header_len + data.len()].copy_from_slice(data);
self.send_raw(&buf[..header_len + data.len()])
} else {
let max_payload = LORA_MAX_PACKET - FragmentHeader::SIZE;
let num_fragments = data.len().div_ceil(max_payload);
if num_fragments > 255 {
return Err(Error::BufferTooSmall);
}
let msg_seq = self.next_seq();
let mut total_sent = 0;
for (i, chunk) in data.chunks(max_payload).enumerate() {
let mut buf = [0u8; LORA_MAX_PACKET];
let header =
FragmentHeader::fragment(self.node_id, msg_seq, i as u8, num_fragments as u8);
let header_len = header.encode(&mut buf)?;
buf[header_len..header_len + chunk.len()].copy_from_slice(chunk);
self.send_raw(&buf[..header_len + chunk.len()])?;
total_sent += chunk.len();
}
Ok(total_sent)
}
}
fn recv(&mut self, buf: &mut [u8]) -> Result<(usize, Locator)> {
let mut rx_buf = [0u8; LORA_MAX_PACKET];
loop {
let len = self.recv_raw(&mut rx_buf)?;
if len < FragmentHeader::SIZE {
continue; }
let header = FragmentHeader::decode(&rx_buf[..len])?;
let payload = &rx_buf[FragmentHeader::SIZE..len];
if header.is_single() {
if payload.len() > buf.len() {
return Err(Error::BufferTooSmall);
}
buf[..payload.len()].copy_from_slice(payload);
let locator = Locator::udpv4([0, 0, 0, header.src_node], 0);
return Ok((payload.len(), locator));
}
if let Some(complete) = self.assembler.add_fragment(&header, payload)? {
if complete.len() > buf.len() {
return Err(Error::BufferTooSmall);
}
buf[..complete.len()].copy_from_slice(complete);
let locator = Locator::udpv4([0, 0, 0, header.src_node], 0);
return Ok((complete.len(), locator));
}
}
}
fn try_recv(&mut self, buf: &mut [u8]) -> Result<(usize, Locator)> {
if !self.radio.is_rx_done()? {
return Err(Error::ResourceExhausted);
}
self.recv(buf)
}
fn local_locator(&self) -> Locator {
Locator::udpv4([0, 0, 0, self.node_id], 0)
}
fn mtu(&self) -> usize {
LORA_MAX_PACKET - FragmentHeader::SIZE
}
fn shutdown(&mut self) -> Result<()> {
self.radio.set_mode_sleep()
}
}