#![no_std]
use core::cmp::{max, min};
#[cfg(feature = "log")]
use log::{debug, trace, warn};
#[cfg(feature = "defmt")]
use defmt::{debug, trace, warn};
#[cfg(feature = "serde")]
extern crate serde;
use core::fmt::Debug;
use core::marker::PhantomData;
extern crate embedded_hal as hal;
use hal::blocking::spi::{Transfer, Write};
use hal::digital::v2::OutputPin;
extern crate radio;
use radio::{
Channel as _, Interrupts as _, Power as _, Register as _, Registers as _, Rssi as _, State as _,
};
pub mod prelude;
use prelude::*;
pub mod register;
use register::*;
pub mod config;
pub struct Sx1231<Spi, CsPin, SpiError, PinError> {
spi: Spi,
cs: CsPin,
rx_buf: [u8; 256],
rx_buf_len: usize,
_se: PhantomData<SpiError>,
_pe: PhantomData<PinError>,
config: Config,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error<SpiError, PinError> {
Spi(SpiError),
Pin(PinError),
InvalidConfiguration,
Aborted,
InvalidResponse,
Timeout,
Crc,
BufferSize(usize),
InvalidDevice(u8),
InvalidPacketSize(usize, usize),
UnexpectedValue(u8),
}
impl<Spi, CsPin, SpiError, PinError> Sx1231<Spi, CsPin, SpiError, PinError>
where
Spi: Transfer<u8, Error = SpiError> + Write<u8, Error = SpiError>,
CsPin: OutputPin<Error = PinError>,
SpiError: Debug,
PinError: Debug,
{
pub fn new(spi: Spi, cs: CsPin) -> Self {
Sx1231 {
spi,
cs,
rx_buf: [0u8; 256],
rx_buf_len: 0,
_se: PhantomData,
_pe: PhantomData,
config: Config::default(),
}
}
pub fn free(self) -> (Spi, CsPin) {
(self.spi, self.cs)
}
}
impl<Spi, CsPin, SpiError, PinError> Sx1231<Spi, CsPin, SpiError, PinError>
where
Spi: Transfer<u8, Error = SpiError> + Write<u8, Error = SpiError>,
CsPin: OutputPin<Error = PinError>,
SpiError: Debug,
PinError: Debug,
{
pub fn configure(&mut self, config: &Config) -> Result<(), Error<SpiError, PinError>> {
let version = self.silicon_version()?;
if version & 0xF0 != 0x20 {
return Err(Error::InvalidDevice(version));
}
self.set_channel(&config.channel)?;
self.write_register(
DataModulation::new()
.with_mod_type(config.modulation)
.with_mode(config.data_mode),
)?;
self.write_register(Preamble::new().with_length(config.preamble_len))?;
self.set_sync_word(config.sync_word)?;
let packet_format = match config.payload_mode {
PayloadMode::Constant(v) => {
self.write_register(PayloadLength::new().with_length(v as u8))?;
PacketFormat::Fixed
}
PayloadMode::Variable => {
self.write_register(PayloadLength::new().with_length(0xFF))?;
PacketFormat::Variable
}
};
self.write_register(
PacketConfig1::new()
.with_packet_format(packet_format)
.with_dc_free(config.dc_free)
.with_crc(config.crc)
.with_crc_auto_clear(config.crc_auto_clear)
.with_address_filter(config.address_filter),
)?;
self.write_register(
PacketConfig2::new()
.with_auto_rx_restart(config.auto_rx_restart)
.with_inter_packet_rx_delay(config.inter_packet_rx_delay),
)?;
self.write_register(
FifoThreshold::new()
.with_start_condition(config.tx_start_condition)
.with_threshold(config.fifo_threshold),
)?;
self.write_register(RssiThreshold::new().with_value(config.rssi_threshold))?;
self.write_register(AfcControl::new().with_low_beta_on(config.afc_low_beta))?;
if config.afc_low_beta {
self.write_register(
TestDagc::new().with_continous_dagc(ContinousDagc::ImprovedMarginLowBetaOn),
)?;
self.write_register(TestAfc::new().with_low_beta_offset(20))?;
}
self.write_register(AfcFei::new().with_afc_auto_on(true))?;
self.write_register(TimeoutRssiThresh::new().with_value(64))?;
self.write_register(
Lna::new()
.with_zin(LnaZin::Zin50Ohm)
.with_gain_select(LnaGainSelect::Agc),
)?;
self.write_register(
TestLna::new().with_sensitivity_boost(SensitivityBoost::HighSensitivity),
)?;
self.set_power(config.power)?;
self.config = config.clone();
Ok(())
}
fn set_sync_word(&mut self, sync_word: &[u8]) -> Result<(), Error<SpiError, PinError>> {
if !sync_word.is_empty() {
self.write_register(
SyncConfig::new()
.with_sync_on(true)
.with_size_minus_one(sync_word.len() as u8 - 1),
)?;
let mut sync_value = [0u8; 8];
sync_value[..sync_word.len()].copy_from_slice(sync_word);
self.write_register(SyncValue::new().with_value(u64::from_be_bytes(sync_value)))?;
} else {
self.write_register(SyncConfig::new().with_sync_on(false))?;
}
Ok(())
}
pub fn silicon_version(&mut self) -> Result<u8, Error<SpiError, PinError>> {
self.read_register::<Version>().map(|r| r.into())
}
pub(crate) fn freq_to_channel_index(&self, freq: u32) -> u32 {
let step = (self.config.xtal_freq as f32) / (2u32.pow(19) as f32);
let ch = (freq as f32) / step;
ch as u32
}
pub fn set_frequency(&mut self, freq: u32) -> Result<(), Error<SpiError, PinError>> {
let channel = self.freq_to_channel_index(freq);
debug!("Set channel to index: {:?} (freq: {:?})", channel, freq);
self.write_register(CarrierFreq::new().with_freq(channel))
}
fn write_fifo(&mut self, data: &[u8]) -> Result<(), Error<SpiError, PinError>> {
self.cs.set_low().map_err(|e| Error::Pin(e))?;
self.spi
.write(&[Fifo::ADDRESS | 0x80])
.map_err(|e| Error::Spi(e))?;
self.spi.write(data).map_err(|e| Error::Spi(e))?;
self.cs.set_high().map_err(|e| Error::Pin(e))?;
Ok(())
}
fn read_fifo(&mut self, len: usize) -> Result<(), Error<SpiError, PinError>> {
let rx_buf_len = self.rx_buf_len;
if rx_buf_len + len > self.rx_buf.len() {
return Err(Error::BufferSize(rx_buf_len + len));
} else if len > self.rx_buf.len() {
return Err(Error::BufferSize(len));
}
self.cs.set_low().map_err(|e| Error::Pin(e))?;
self.spi
.write(&[Fifo::ADDRESS])
.map_err(|e| Error::Spi(e))?;
self.spi
.transfer(&mut self.rx_buf[rx_buf_len..(rx_buf_len + len)])
.map_err(|e| Error::Spi(e))?;
self.rx_buf_len += len;
self.cs.set_high().map_err(|e| Error::Pin(e))?;
Ok(())
}
}
impl<Spi, CsPin, SpiError, PinError> Sx1231<Spi, CsPin, SpiError, PinError>
where
Spi: Transfer<u8, Error = SpiError> + Write<u8, Error = SpiError>,
CsPin: OutputPin<Error = PinError>,
SpiError: Debug,
PinError: Debug,
{
pub fn set_state_checked(&mut self, state: ModemMode) -> Result<(), Error<SpiError, PinError>> {
trace!("Set state to: {:?} (0x{:02x})", state, state as u8);
self.set_state(state)?;
let mut ticks = 0;
loop {
let s = self.get_state()?;
trace!("Received: {:?}", s);
if state == s {
break;
}
if ticks >= self.config.timeout_ticks {
warn!("Set state timeout: {:?}, {:?}", state, s);
return Err(Error::Timeout);
}
ticks += 1;
}
Ok(())
}
}
impl<Spi, CsPin, SpiError, PinError> radio::State for Sx1231<Spi, CsPin, SpiError, PinError>
where
Spi: Transfer<u8, Error = SpiError> + Write<u8, Error = SpiError>,
CsPin: OutputPin<Error = PinError>,
SpiError: Debug,
PinError: Debug,
{
type State = ModemMode;
type Error = Error<SpiError, PinError>;
fn get_state(&mut self) -> Result<Self::State, Self::Error> {
Ok(self.read_register::<OpMode>()?.modem_mode())
}
fn set_state(&mut self, state: Self::State) -> Result<(), Self::Error> {
let mut op_mode = self.read_register::<OpMode>()?;
op_mode.set_modem_mode(state);
self.write_register(op_mode)
}
}
impl<Spi, CsPin, SpiError, PinError> radio::Power for Sx1231<Spi, CsPin, SpiError, PinError>
where
Spi: Transfer<u8, Error = SpiError> + Write<u8, Error = SpiError>,
CsPin: OutputPin<Error = PinError>,
SpiError: Debug,
PinError: Debug,
{
type Error = Error<SpiError, PinError>;
fn set_power(&mut self, power: i8) -> Result<(), Error<SpiError, PinError>> {
let power = min(max(power, 0), 20) as u8;
let mut config = PaLevel::new();
if power < 14 {
config.set_pa1_on(true);
config.set_level(power + 18);
} else if power < 18 {
config.set_pa1_on(true);
config.set_pa2_on(true);
config.set_level(power + 14);
}
debug!("Updated PA config for {} dBm: {:?}", power, config);
self.write_register(config)?;
Ok(())
}
}
impl<Spi, CsPin, SpiError, PinError> radio::Interrupts for Sx1231<Spi, CsPin, SpiError, PinError>
where
Spi: Transfer<u8, Error = SpiError> + Write<u8, Error = SpiError>,
CsPin: OutputPin<Error = PinError>,
SpiError: Debug,
PinError: Debug,
{
type Irq = IrqFlags;
type Error = Error<SpiError, PinError>;
fn get_interrupts(&mut self, clear: bool) -> Result<Self::Irq, Self::Error> {
let irq: IrqFlags = self.read_register()?;
if clear {
self.write_register(irq.with_rssi(false).with_fifo_overrun(false))?;
}
Ok(irq)
}
}
impl<Spi, CsPin, SpiError, PinError> radio::Channel for Sx1231<Spi, CsPin, SpiError, PinError>
where
Spi: Transfer<u8, Error = SpiError> + Write<u8, Error = SpiError>,
CsPin: OutputPin<Error = PinError>,
SpiError: Debug,
PinError: Debug,
{
type Channel = Channel;
type Error = Error<SpiError, PinError>;
fn set_channel(&mut self, channel: &Channel) -> Result<(), Error<SpiError, PinError>> {
self.set_frequency(channel.freq)?;
let round = |x: f32| -> u32 {
let integer = x as u32;
if (x - (integer as f32)) < 0.5 {
integer
} else {
integer + 1
}
};
let fdev = round((channel.fdev as f32) / self.config.freq_step) as u16;
let datarate = round(self.config.xtal_freq as f32 / channel.br as f32) as u16;
trace!("fdev: {} bitrate: {}", fdev, datarate);
self.write_register(FreqDev::new().with_freq_dev(fdev))?;
self.write_register(Bitrate::new().with_bitrate(datarate))?;
self.write_register(RxBw::new().with_bw(channel.bw))?;
self.write_register(AfcBw::new().with_bw(channel.bw_afc))?;
Ok(())
}
}
impl<Spi, CsPin, SpiError, PinError> radio::Transmit for Sx1231<Spi, CsPin, SpiError, PinError>
where
Spi: Transfer<u8, Error = SpiError> + Write<u8, Error = SpiError>,
CsPin: OutputPin<Error = PinError>,
SpiError: Debug,
PinError: Debug,
{
type Error = Error<SpiError, PinError>;
fn start_transmit(&mut self, data: &[u8]) -> Result<(), Self::Error> {
#[cfg(feature = "log")]
debug!("Starting send (data: {:x?}, len: {})", data, data.len());
#[cfg(feature = "defmt")]
debug!("Starting send (data: {}, len: {})", data, data.len());
assert!(data.len() < 255);
self.set_state_checked(ModemMode::Standby)?;
let irq = self.get_interrupts(true)?;
trace!("clearing interrupts {:?}", irq);
match self.config.payload_mode {
PayloadMode::Constant(_) => {}
PayloadMode::Variable => {
self.write_fifo(&[data.len() as u8])?;
}
}
self.write_fifo(&data[..min(64, data.len())])?;
self.set_state(ModemMode::Transmitter)?;
if data.len() > 64 {
for chunk in data[64..].chunks(32) {
while self.get_interrupts(false)?.fifo_level() {}
self.write_fifo(chunk)?;
}
}
Ok(())
}
fn check_transmit(&mut self) -> Result<bool, Error<SpiError, PinError>> {
let irq = self.get_interrupts(true)?;
trace!("Check transmit IRQ: {:?}", irq);
if irq.packet_sent() {
self.set_state_checked(ModemMode::Sleep)?;
return Ok(true);
}
Ok(false)
}
}
impl<Spi, CsPin, SpiError, PinError> radio::Receive for Sx1231<Spi, CsPin, SpiError, PinError>
where
Spi: Transfer<u8, Error = SpiError> + Write<u8, Error = SpiError>,
CsPin: OutputPin<Error = PinError>,
SpiError: Debug,
PinError: Debug,
{
type Info = PacketInfo;
type Error = Error<SpiError, PinError>;
fn start_receive(&mut self) -> Result<(), Self::Error> {
trace!("Starting receive");
self.set_state_checked(ModemMode::Standby)?;
let irq = self.get_interrupts(true)?;
trace!("clearing interrupts {:?}", irq);
self.set_state(ModemMode::Receiver)?;
trace!("started receive");
self.rx_buf_len = 0;
Ok(())
}
fn check_receive(&mut self, restart: bool) -> Result<bool, Self::Error> {
let irq = self.get_interrupts(false)?;
let s = self.get_state()?;
let mut res = Ok(false);
trace!("Check Receive (State: {:?}, IRQ: {:?})", s, irq);
if irq.rssi() {
let rssi = self.poll_rssi()?;
trace!("Check Receive RSSI: {:?}", rssi);
}
if irq.payload_ready() {
self.set_state_checked(ModemMode::Standby)?;
if self.rx_buf_len == 0 {
self.read_fifo(1)?;
}
let packet_len = self.rx_buf[0] as usize;
if packet_len < self.rx_buf_len + 1 {
return Err(Error::InvalidPacketSize(packet_len, self.rx_buf_len + 1));
}
self.read_fifo(packet_len - self.rx_buf_len + 1)?;
debug!("RX complete! ({} bytes)", packet_len);
res = Ok(true)
} else if irq.fifo_level() {
self.read_fifo(32)?;
trace!(
"Received chunk: {:?}",
&self.rx_buf[(self.rx_buf_len - 32)..self.rx_buf_len]
);
} else if irq.timeout() {
trace!("RX timeout");
self.rx_buf_len = 0;
res = Err(Error::Timeout);
}
match (restart, res) {
(true, Err(_)) => {
trace!("RX restarting");
self.start_receive()?;
Ok(false)
}
(_, r) => r,
}
}
fn get_received(&mut self, data: &mut [u8]) -> Result<(usize, Self::Info), Self::Error> {
let mut info = PacketInfo::default();
info.rssi = self.poll_rssi()?;
let packet_data = &self.rx_buf[1..self.rx_buf_len];
data[..packet_data.len()].copy_from_slice(packet_data);
self.rx_buf_len = 0;
#[cfg(feature = "log")]
debug!("Received data: {:02x?} info: {:?}", packet_data, &info);
#[cfg(feature = "defmt")]
debug!("Received data: {} info: {:?}", packet_data, &info);
Ok((packet_data.len(), info))
}
}
impl<Spi, CsPin, SpiError, PinError> radio::Rssi for Sx1231<Spi, CsPin, SpiError, PinError>
where
Spi: Transfer<u8, Error = SpiError> + Write<u8, Error = SpiError>,
CsPin: OutputPin<Error = PinError>,
SpiError: Debug,
PinError: Debug,
{
type Error = Error<SpiError, PinError>;
fn poll_rssi(&mut self) -> Result<i16, Error<SpiError, PinError>> {
let reg: u8 = self.read_register::<RssiValue>()?.into();
let rssi = -(i16::from(reg)) / 2;
Ok(rssi)
}
}
pub trait RegisterWord {
type Bytes: Default + core::convert::AsMut<[u8]>;
fn to_bytes(self) -> Self::Bytes;
fn from_bytes(bytes: Self::Bytes) -> Self;
}
impl RegisterWord for u8 {
type Bytes = [u8; 1];
fn to_bytes(self) -> Self::Bytes {
self.to_be_bytes()
}
fn from_bytes(bytes: Self::Bytes) -> Self {
Self::from_be_bytes(bytes)
}
}
impl RegisterWord for u16 {
type Bytes = [u8; 2];
fn to_bytes(self) -> Self::Bytes {
self.to_be_bytes()
}
fn from_bytes(bytes: Self::Bytes) -> Self {
Self::from_be_bytes(bytes)
}
}
impl RegisterWord for [u8; 3] {
type Bytes = [u8; 3];
fn to_bytes(self) -> Self::Bytes {
self
}
fn from_bytes(bytes: Self::Bytes) -> Self {
bytes
}
}
impl RegisterWord for u64 {
type Bytes = [u8; 8];
fn to_bytes(self) -> Self::Bytes {
self.to_be_bytes()
}
fn from_bytes(bytes: Self::Bytes) -> Self {
Self::from_be_bytes(bytes)
}
}
impl<Spi, CsPin, SpiError, PinError, Word, Bytes> radio::Registers<Word>
for Sx1231<Spi, CsPin, SpiError, PinError>
where
Spi: Transfer<u8, Error = SpiError> + Write<u8, Error = SpiError>,
CsPin: OutputPin<Error = PinError>,
SpiError: Debug,
PinError: Debug,
Word: RegisterWord<Bytes = Bytes>,
Bytes: Default + core::convert::AsMut<[u8]>,
{
type Error = Error<SpiError, PinError>;
fn read_register<R: radio::Register<Word = Word>>(
&mut self,
) -> Result<R, Error<SpiError, PinError>> {
let mut bytes = Bytes::default();
self.cs.set_low().map_err(|e| Error::Pin(e))?;
self.spi.write(&[R::ADDRESS]).map_err(|e| Error::Spi(e))?;
self.spi
.transfer(bytes.as_mut())
.map_err(|e| Error::Spi(e))?;
self.cs.set_high().map_err(|e| Error::Pin(e))?;
let d = R::Word::from_bytes(bytes);
R::try_from(d).map_err(|_| Error::UnexpectedValue(R::ADDRESS))
}
fn write_register<R: radio::Register<Word = Word>>(
&mut self,
r: R,
) -> Result<(), Error<SpiError, PinError>> {
self.cs.set_low().map_err(|e| Error::Pin(e))?;
self.spi.write(&[R::ADDRESS]).map_err(|e| Error::Spi(e))?;
self.spi
.write(r.into().to_bytes().as_mut())
.map_err(|e| Error::Spi(e))?;
self.cs.set_high().map_err(|e| Error::Pin(e))?;
Ok(())
}
}
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}