#![no_std]
#![feature(async_fn_in_trait)]
#![allow(incomplete_features)]
#![warn(missing_docs)]
#![doc = include_str!("../README.md")]
pub(crate) mod interface;
pub mod mod_params;
pub mod mod_traits;
pub mod sx1261_2;
pub mod sx1276_7_8_9;
use embedded_hal_async::delay::DelayUs;
use interface::*;
use mod_params::*;
use mod_traits::*;
const MAX_LORA_SYMB_NUM_TIMEOUT: u32 = 248;
pub struct LoRa<RK, DLY>
where
RK: RadioKind,
DLY: DelayUs,
{
radio_kind: RK,
delay: DLY,
radio_mode: RadioMode,
enable_public_network: bool,
rx_continuous: bool,
polling_timeout_in_ms: Option<u32>,
cold_start: bool,
calibrate_image: bool,
}
impl<RK, DLY> LoRa<RK, DLY>
where
RK: RadioKind,
DLY: DelayUs,
{
pub async fn new(radio_kind: RK, enable_public_network: bool, delay: DLY) -> Result<Self, RadioError> {
let mut lora = Self {
radio_kind,
delay,
radio_mode: RadioMode::Sleep,
enable_public_network,
rx_continuous: false,
polling_timeout_in_ms: None,
cold_start: true,
calibrate_image: true,
};
lora.init().await?;
Ok(lora)
}
pub fn get_board_type(&self) -> BoardType {
self.radio_kind.get_board_type()
}
pub fn create_modulation_params(
&mut self,
spreading_factor: SpreadingFactor,
bandwidth: Bandwidth,
coding_rate: CodingRate,
frequency_in_hz: u32,
) -> Result<ModulationParams, RadioError> {
self.radio_kind
.create_modulation_params(spreading_factor, bandwidth, coding_rate, frequency_in_hz)
}
pub fn create_tx_packet_params(
&mut self,
preamble_length: u16,
implicit_header: bool,
crc_on: bool,
iq_inverted: bool,
modulation_params: &ModulationParams,
) -> Result<PacketParams, RadioError> {
self.radio_kind.create_packet_params(
preamble_length,
implicit_header,
0,
crc_on,
iq_inverted,
modulation_params,
)
}
pub fn create_rx_packet_params(
&mut self,
preamble_length: u16,
implicit_header: bool,
max_payload_length: u8,
crc_on: bool,
iq_inverted: bool,
modulation_params: &ModulationParams,
) -> Result<PacketParams, RadioError> {
self.radio_kind.create_packet_params(
preamble_length,
implicit_header,
max_payload_length,
crc_on,
iq_inverted,
modulation_params,
)
}
pub async fn init(&mut self) -> Result<(), RadioError> {
self.cold_start = true;
self.radio_kind.reset(&mut self.delay).await?;
self.radio_kind.ensure_ready(self.radio_mode).await?;
self.radio_kind.set_standby().await?;
self.radio_mode = RadioMode::Standby;
self.rx_continuous = false;
self.do_cold_start().await
}
async fn do_cold_start(&mut self) -> Result<(), RadioError> {
self.radio_kind.init_rf_switch().await?;
self.radio_kind.set_lora_modem(self.enable_public_network).await?;
self.radio_kind.set_oscillator().await?;
self.radio_kind.set_regulator_mode().await?;
self.radio_kind.set_tx_rx_buffer_base_address(0, 0).await?;
self.radio_kind
.set_tx_power_and_ramp_time(0, None, false, false)
.await?;
self.radio_kind.set_irq_params(Some(self.radio_mode)).await?;
self.radio_kind.update_retention_list().await?;
self.cold_start = false;
self.calibrate_image = true;
Ok(())
}
pub async fn sleep(&mut self, warm_start_if_possible: bool) -> Result<(), RadioError> {
if self.radio_mode != RadioMode::Sleep {
self.radio_kind.ensure_ready(self.radio_mode).await?;
self.radio_kind
.set_sleep(warm_start_if_possible, &mut self.delay)
.await?;
if !warm_start_if_possible {
self.cold_start = true;
}
self.radio_mode = RadioMode::Sleep;
}
Ok(())
}
pub async fn prepare_for_tx(
&mut self,
mdltn_params: &ModulationParams,
output_power: i32,
tx_boosted_if_possible: bool,
) -> Result<(), RadioError> {
self.rx_continuous = false;
self.radio_kind.ensure_ready(self.radio_mode).await?;
if self.radio_mode != RadioMode::Standby {
self.radio_kind.set_standby().await?;
self.radio_mode = RadioMode::Standby;
}
if self.cold_start {
self.do_cold_start().await?;
}
if self.calibrate_image {
self.radio_kind.calibrate_image(mdltn_params.frequency_in_hz).await?;
self.calibrate_image = false;
}
self.radio_kind.set_modulation_params(mdltn_params).await?;
self.radio_kind
.set_tx_power_and_ramp_time(output_power, Some(mdltn_params), tx_boosted_if_possible, true)
.await
}
pub async fn tx(
&mut self,
mdltn_params: &ModulationParams,
tx_pkt_params: &mut PacketParams,
buffer: &[u8],
timeout_in_ms: u32,
) -> Result<(), RadioError> {
self.rx_continuous = false;
self.radio_kind.ensure_ready(self.radio_mode).await?;
if self.radio_mode != RadioMode::Standby {
self.radio_kind.set_standby().await?;
self.radio_mode = RadioMode::Standby;
}
tx_pkt_params.set_payload_length(buffer.len())?;
self.radio_kind.set_packet_params(tx_pkt_params).await?;
self.radio_kind.set_channel(mdltn_params.frequency_in_hz).await?;
self.radio_kind.set_payload(buffer).await?;
self.radio_mode = RadioMode::Transmit;
self.radio_kind.set_irq_params(Some(self.radio_mode)).await?;
self.radio_kind.do_tx(timeout_in_ms).await?;
match self
.radio_kind
.process_irq(self.radio_mode, self.rx_continuous, &mut self.delay, None, None)
.await
{
Ok(()) => Ok(()),
Err(err) => {
self.radio_kind.ensure_ready(self.radio_mode).await?;
self.radio_kind.set_standby().await?;
self.radio_mode = RadioMode::Standby;
Err(err)
}
}
}
#[allow(clippy::too_many_arguments)]
pub async fn prepare_for_rx(
&mut self,
mdltn_params: &ModulationParams,
rx_pkt_params: &PacketParams,
window_in_secs: Option<u8>, duty_cycle_params: Option<&DutyCycleParams>,
rx_boosted_if_supported: bool,
) -> Result<(), RadioError> {
let mut symbol_timeout: u32 = 0;
match window_in_secs {
Some(window) => {
let sf = mdltn_params.spreading_factor.value();
let bw = mdltn_params.bandwidth.value_in_hz();
let symbols_per_sec = bw / (0x01u32 << sf); let window_in_ms: u32 = (window as u32).checked_mul(1000).unwrap();
symbol_timeout = (window_in_ms - 200).checked_mul(symbols_per_sec).unwrap() / 1000; if symbol_timeout > MAX_LORA_SYMB_NUM_TIMEOUT {
symbol_timeout = MAX_LORA_SYMB_NUM_TIMEOUT;
}
self.rx_continuous = false;
self.polling_timeout_in_ms = Some(window_in_ms.checked_mul(5).unwrap());
}
None => {
self.rx_continuous = true;
self.polling_timeout_in_ms = None;
}
}
self.radio_kind.ensure_ready(self.radio_mode).await?;
if self.radio_mode != RadioMode::Standby {
self.radio_kind.set_standby().await?;
self.radio_mode = RadioMode::Standby;
}
if self.cold_start {
self.do_cold_start().await?;
}
if self.calibrate_image {
self.radio_kind.calibrate_image(mdltn_params.frequency_in_hz).await?;
self.calibrate_image = false;
}
self.radio_kind.set_modulation_params(mdltn_params).await?;
self.radio_kind.set_packet_params(rx_pkt_params).await?;
self.radio_kind.set_channel(mdltn_params.frequency_in_hz).await?;
self.radio_mode = match duty_cycle_params {
Some(&_duty_cycle) => RadioMode::ReceiveDutyCycle,
None => RadioMode::Receive,
};
self.radio_kind.set_irq_params(Some(self.radio_mode)).await?;
self.radio_kind
.do_rx(
rx_pkt_params,
duty_cycle_params,
self.rx_continuous,
rx_boosted_if_supported,
symbol_timeout as u16,
)
.await
}
pub async fn rx(
&mut self,
rx_pkt_params: &PacketParams,
receiving_buffer: &mut [u8],
) -> Result<(u8, PacketStatus), RadioError> {
match self
.radio_kind
.process_irq(
self.radio_mode,
self.rx_continuous,
&mut self.delay,
self.polling_timeout_in_ms,
None,
)
.await
{
Ok(()) => {
let received_len = self.radio_kind.get_rx_payload(rx_pkt_params, receiving_buffer).await?;
let rx_pkt_status = self.radio_kind.get_rx_packet_status().await?;
Ok((received_len, rx_pkt_status))
}
Err(err) => {
if !self.rx_continuous {
self.radio_kind.ensure_ready(self.radio_mode).await?;
self.radio_kind.set_standby().await?;
self.radio_mode = RadioMode::Standby;
}
Err(err)
}
}
}
pub async fn prepare_for_cad(
&mut self,
mdltn_params: &ModulationParams,
rx_boosted_if_supported: bool,
) -> Result<(), RadioError> {
self.rx_continuous = false;
self.radio_kind.ensure_ready(self.radio_mode).await?;
if self.radio_mode != RadioMode::Standby {
self.radio_kind.set_standby().await?;
self.radio_mode = RadioMode::Standby;
}
if self.cold_start {
self.do_cold_start().await?;
}
if self.calibrate_image {
self.radio_kind.calibrate_image(mdltn_params.frequency_in_hz).await?;
self.calibrate_image = false;
}
self.radio_kind.set_modulation_params(mdltn_params).await?;
self.radio_kind.set_channel(mdltn_params.frequency_in_hz).await?;
self.radio_mode = RadioMode::ChannelActivityDetection;
self.radio_kind.set_irq_params(Some(self.radio_mode)).await?;
self.radio_kind.do_cad(mdltn_params, rx_boosted_if_supported).await
}
pub async fn cad(&mut self) -> Result<bool, RadioError> {
let mut cad_activity_detected = false;
match self
.radio_kind
.process_irq(
self.radio_mode,
self.rx_continuous,
&mut self.delay,
None,
Some(&mut cad_activity_detected),
)
.await
{
Ok(()) => Ok(cad_activity_detected),
Err(err) => {
self.radio_kind.ensure_ready(self.radio_mode).await?;
self.radio_kind.set_standby().await?;
self.radio_mode = RadioMode::Standby;
Err(err)
}
}
}
}
impl<RK, DLY> AsyncRng for LoRa<RK, DLY>
where
RK: RngRadio,
DLY: DelayUs,
{
async fn get_random_number(&mut self) -> Result<u32, RadioError> {
self.rx_continuous = false;
self.radio_kind.ensure_ready(self.radio_mode).await?;
if self.radio_mode != RadioMode::Standby {
self.radio_kind.set_standby().await?;
self.radio_mode = RadioMode::Standby;
}
if self.cold_start {
self.do_cold_start().await?;
}
let random_number = self.radio_kind.get_random_number().await?;
self.radio_kind.set_standby().await?;
self.radio_mode = RadioMode::Standby;
Ok(random_number)
}
}