#![no_std]
extern crate libc;
#[macro_use]
extern crate bitflags;
#[macro_use]
extern crate log;
#[macro_use]
extern crate serde;
use core::convert::TryFrom;
use core::marker::PhantomData;
use core::fmt::Debug;
extern crate embedded_hal as hal;
use hal::blocking::delay;
use hal::blocking::spi::{Transfer, Write};
use hal::digital::v2::{InputPin, OutputPin};
use hal::spi::{Mode as SpiMode, Phase, Polarity};
extern crate embedded_spi;
use embedded_spi::{wrapper::Wrapper as SpiWrapper, Error as WrapError};
extern crate radio;
use radio::{Power as _, State as _};
pub mod base;
pub mod device;
use device::{regs, Channel, Interrupts, Config, Modem, ModemMode, PaConfig, PacketInfo, State};
pub mod fsk;
pub mod lora;
pub mod prelude;
pub const SPI_MODE: SpiMode = SpiMode {
polarity: Polarity::IdleLow,
phase: Phase::CaptureOnFirstTransition,
};
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum Mode {
Unconfigured,
LoRa,
FskOok,
}
pub struct Sx127x<Base, CommsError, PinError> {
hal: Base,
_ce: PhantomData<CommsError>,
_pe: PhantomData<PinError>,
mode: Mode,
config: Config,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Error<CommsError, PinError> {
Comms(CommsError),
Pin(PinError),
InvalidConfiguration,
Aborted,
InvalidResponse,
Timeout,
Crc,
BufferSize,
InvalidDevice(u8),
}
impl<CommsError, PinError> From<WrapError<CommsError, PinError>> for Error<CommsError, PinError> {
fn from(e: WrapError<CommsError, PinError>) -> Self {
match e {
WrapError::Spi(e) => Error::Comms(e),
WrapError::Pin(e) => Error::Pin(e),
WrapError::Aborted => Error::Aborted,
}
}
}
#[derive(Clone, PartialEq, Debug)]
pub struct Settings {
pub xtal_freq: u32,
}
impl Default for Settings {
fn default() -> Self {
Self {
xtal_freq: 32000000,
}
}
}
impl<Spi, CommsError, Output, Input, PinError, Delay>
Sx127x<SpiWrapper<Spi, CommsError, Output, Input, PinError, Delay>, CommsError, PinError>
where
Spi: Transfer<u8, Error = CommsError> + Write<u8, Error = CommsError>,
Output: OutputPin<Error = PinError>,
Input: InputPin<Error = PinError>,
Delay: delay::DelayMs<u32>,
{
pub fn spi(
spi: Spi,
cs: Output,
busy: Input,
sdn: Output,
delay: Delay,
config: &Config,
) -> Result<Self, Error<CommsError, PinError>> {
let mut hal = SpiWrapper::new(spi, cs, delay);
hal.with_busy(busy);
hal.with_reset(sdn);
Self::new(hal, config)
}
}
impl<Base, CommsError, PinError> Sx127x<Base, CommsError, PinError>
where
Base: base::Base<CommsError, PinError>,
{
pub fn new(hal: Base, config: &Config) -> Result<Self, Error<CommsError, PinError>> {
let mut sx127x = Self::build(hal, config.clone());
sx127x.hal.reset()?;
let version = sx127x.silicon_version()?;
if version != 0x12 {
return Err(Error::InvalidDevice(version));
}
sx127x.rf_chain_calibration()?;
sx127x.set_state(State::Sleep)?;
for (modem, reg, val) in device::REGISTERS_INIT {
sx127x.set_modem(*modem)?;
sx127x.write_reg(*reg, *val)?;
}
sx127x.configure(config)?;
Ok(sx127x)
}
pub fn configure(&mut self, config: &Config) -> Result<(), Error<CommsError, PinError>> {
match (&config.modem, &config.channel) {
(Modem::LoRa(lora_modem), Channel::LoRa(lora_channel)) => {
self.lora_configure(lora_modem, lora_channel)?;
}
(Modem::FskOok(fsk_modem), Channel::FskOok(fsk_channel)) => {
self.fsk_configure(fsk_modem, fsk_channel)?;
}
_ => panic!("Invalid configuration, mismatch between Modem ({:?}) and Channel ({:?} modes", &config.modem, &config.channel),
}
self.configure_pa(&config.pa_config)?;
self.config = config.clone();
Ok(())
}
pub fn silicon_version(&mut self) -> Result<u8, Error<CommsError, PinError>> {
self.read_reg(regs::Common::VERSION)
}
pub(crate) fn configure_pa(
&mut self,
pa_config: &PaConfig,
) -> Result<(), Error<CommsError, PinError>> {
use device::*;
match pa_config.output {
PaSelect::Rfo(max) => {
self.update_reg(
regs::Common::PACONFIG,
PASELECT_MASK | MAXPOWER_MASK,
PASELECT_RFO | ((max << MAXPOWER_SHIFT) & MAXPOWER_MASK),
)?;
}
PaSelect::Boost => {
self.update_reg(regs::Common::PACONFIG, PASELECT_MASK, PASELECT_PA_BOOST)?;
}
}
self.set_power(pa_config.power)?;
Ok(())
}
pub(crate) fn set_state_checked(
&mut self,
state: State,
) -> Result<(), Error<CommsError, PinError>> {
trace!("Set state to: {:?} (0x{:02x})", state, state as u8);
self.set_state(state)?;
loop {
let s = self.get_state()?;
trace!("Received: {:?}", s);
if state == s {
break;
}
self.hal.delay_ms(1);
}
Ok(())
}
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(crate) fn channel_index_to_freq(&self, ch: u32) -> u32 {
let step = (self.config.xtal_freq as f32) / (2u32.pow(19) as f32);
let freq = (ch as f32) * step;
freq as u32
}
pub(crate) fn set_modem(
&mut self,
modem: ModemMode,
) -> Result<(), Error<CommsError, PinError>> {
match modem {
ModemMode::Standard => {
self.set_state(State::Sleep)?;
self.update_reg(
regs::Common::OPMODE,
device::OPMODE_LONGRANGEMODE_MASK | device::OPMODE_MODULATION_MASK,
device::LongRangeMode::Off as u8 | device::ModulationType::Fsk as u8,
)?;
}
ModemMode::LoRa => {
self.set_state(State::Sleep)?;
self.update_reg(
regs::Common::OPMODE,
device::OPMODE_LONGRANGEMODE_MASK,
device::LongRangeMode::On as u8,
)?;
}
}
Ok(())
}
pub(crate) fn set_frequency(&mut self, freq: u32) -> Result<(), Error<CommsError, PinError>> {
let channel = self.freq_to_channel_index(freq);
let outgoing = [
(channel >> 16) as u8,
(channel >> 8) as u8,
(channel >> 0) as u8,
];
debug!("Set channel to index: {:?} (freq: {:?})", channel, freq);
self.hal.write_regs(regs::Common::FRFMSB as u8, &outgoing)?;
Ok(())
}
pub(crate) fn get_frequency(&mut self) -> Result<u32, Error<CommsError, PinError>> {
let mut incoming = [0u8; 3];
self.hal
.read_regs(regs::Common::FRFMSB as u8, &mut incoming)?;
let ch = (incoming[0] as u32) << 16 | (incoming[1] as u32) << 8 | (incoming[2] as u32) << 0;
let freq = self.channel_index_to_freq(ch);
Ok(freq)
}
pub(crate) fn rf_chain_calibration(&mut self) -> Result<(), Error<CommsError, PinError>> {
debug!("Running calibration");
let frequency = self.get_frequency()?;
let pa_config = self.read_reg(regs::Common::PACONFIG)?;
self.write_reg(regs::Common::PACONFIG, 0x00)?;
self.update_reg(
regs::Fsk::IMAGECAL,
regs::RF_IMAGECAL_IMAGECAL_MASK as u8,
regs::RF_IMAGECAL_IMAGECAL_START as u8,
)?;
while self.read_reg(regs::Fsk::IMAGECAL)? & (regs::RF_IMAGECAL_IMAGECAL_RUNNING as u8) != 0
{
}
self.set_frequency(868e6 as u32)?;
self.update_reg(
regs::Fsk::IMAGECAL,
regs::RF_IMAGECAL_IMAGECAL_MASK as u8,
regs::RF_IMAGECAL_IMAGECAL_START as u8,
)?;
while self.read_reg(regs::Fsk::IMAGECAL)? & (regs::RF_IMAGECAL_IMAGECAL_RUNNING as u8) != 0
{
}
self.set_frequency(frequency)?;
self.write_reg(regs::Common::PACONFIG, pa_config)?;
debug!("Calibration done");
Ok(())
}
}
impl<Base, CommsError, PinError> Sx127x<Base, CommsError, PinError>
where
Base: base::Base<CommsError, PinError>,
{
pub(crate) fn build(hal: Base, config: Config) -> Self {
Sx127x {
hal,
config,
mode: Mode::Unconfigured,
_ce: PhantomData,
_pe: PhantomData,
}
}
}
impl<Base, CommsError, PinError> delay::DelayMs<u32> for Sx127x<Base, CommsError, PinError>
where
Base: base::Base<CommsError, PinError>,
{
fn delay_ms(&mut self, t: u32) {
self.hal.delay_ms(t)
}
}
impl<Base, CommsError, PinError> Sx127x<Base, CommsError, PinError>
where
Base: base::Base<CommsError, PinError>,
{
pub fn read_reg<R>(&mut self, reg: R) -> Result<u8, Error<CommsError, PinError>>
where
R: Copy + Clone + Debug + Into<u8>,
{
let value = self.hal.read_reg(reg.into())?;
trace!("Read reg: {:?} (0x{:02x}): 0x{:02x}", reg, reg.into(), value);
Ok(value)
}
pub fn write_reg<R>(&mut self, reg: R, value: u8) -> Result<(), Error<CommsError, PinError>>
where
R: Copy + Clone + Debug + Into<u8>,
{
trace!("Write reg: {:?} (0x{:02x}): 0x{:02x}", reg, reg.into(), value);
self.hal.write_reg(reg.into(), value)
}
pub fn update_reg<R>(
&mut self,
reg: R,
mask: u8,
value: u8,
) -> Result<u8, Error<CommsError, PinError>>
where
R: Copy + Clone + Debug + Into<u8>,
{
trace!("Update reg: {:?} (0x{:02x}): 0x{:02x} (0x{:02x})", reg, reg.into(), value, mask);
self.hal.update_reg(reg.into(), mask, value)
}
}
impl<Base, CommsError, PinError> radio::State for Sx127x<Base, CommsError, PinError>
where
Base: base::Base<CommsError, PinError>,
{
type State = State;
type Error = Error<CommsError, PinError>;
fn get_state(&mut self) -> Result<Self::State, Self::Error> {
let state = self.read_reg(regs::Common::OPMODE)?;
let state = State::try_from(state & device::OPMODE_STATE_MASK)
.map_err(|_| Error::InvalidResponse)?;
Ok(state)
}
fn set_state(&mut self, state: Self::State) -> Result<(), Self::Error> {
self.update_reg(regs::Common::OPMODE, device::OPMODE_STATE_MASK, state as u8)?;
Ok(())
}
}
impl<Base, CommsError, PinError> radio::Power for Sx127x<Base, CommsError, PinError>
where
Base: base::Base<CommsError, PinError>,
{
type Error = Error<CommsError, PinError>;
fn set_power(&mut self, power: i8) -> Result<(), Error<CommsError, PinError>> {
use device::*;
let power = core::cmp::max(power, 0);
let config = self.read_reg(regs::Common::PACONFIG)?;
match config & PASELECT_MASK {
PASELECT_RFO => {
let max = ((config & MAXPOWER_MASK) >> MAXPOWER_SHIFT) as i8;
let power = core::cmp::min(power, 17i8);
let value = power - max + 15;
let v = self.update_reg(regs::Common::PACONFIG, OUTPUTPOWER_MASK, value as u8)?;
debug!("Updated RFO PA_CONFIG for: {} dBm to: {:b}", power, v);
}
PASELECT_PA_BOOST => {
let power = core::cmp::min(power, 20i8);
let value = power - 17 + 15;
self.update_reg(regs::Common::PACONFIG, OUTPUTPOWER_MASK, value as u8)?;
let pa_dac_enable = if power > 17 {
PADAC_20DBM_ON
} else {
PADAC_20DBM_OFF
};
let v = self.update_reg(regs::Common::PADAC, PADAC_MASK, pa_dac_enable)?;
debug!("Updated BOOST PA_CONFIG for: {} dBm to: {:b}", power, v);
}
_ => (),
}
Ok(())
}
}
impl<Base, CommsError, PinError> radio::Interrupts for Sx127x<Base, CommsError, PinError>
where
Base: base::Base<CommsError, PinError>,
{
type Irq = Interrupts;
type Error = Error<CommsError, PinError>;
fn get_interrupts(&mut self, clear: bool) -> Result<Self::Irq, Self::Error> {
match self.mode {
Mode::LoRa => Ok(Interrupts::LoRa(self.lora_get_interrupts(clear)?)),
Mode::FskOok => Ok(Interrupts::FskOok(self.fsk_get_interrupts(clear)?)),
_ => Err(Error::InvalidConfiguration),
}
}
}
impl<Base, CommsError, PinError> radio::Channel for Sx127x<Base, CommsError, PinError>
where
Base: base::Base<CommsError, PinError>,
{
type Channel = Channel;
type Error = Error<CommsError, PinError>;
fn set_channel(&mut self, channel: &Self::Channel) -> Result<(), Error<CommsError, PinError>> {
match (self.mode, channel) {
(Mode::LoRa, Channel::LoRa(channel)) => self.lora_set_channel(channel),
(Mode::FskOok, Channel::FskOok(channel)) => self.fsk_set_channel(channel),
_ => Err(Error::InvalidConfiguration),
}
}
}
impl<Base, CommsError, PinError> radio::Transmit for Sx127x<Base, CommsError, PinError>
where
Base: base::Base<CommsError, PinError>,
{
type Error = Error<CommsError, PinError>;
fn start_transmit(&mut self, data: &[u8]) -> Result<(), Self::Error> {
match self.mode {
Mode::LoRa => self.lora_start_transmit(data),
Mode::FskOok => self.fsk_start_transmit(data),
_ => Err(Error::InvalidConfiguration),
}
}
fn check_transmit(&mut self) -> Result<bool, Error<CommsError, PinError>> {
match self.mode {
Mode::LoRa => self.lora_check_transmit(),
Mode::FskOok => self.fsk_check_transmit(),
_ => Err(Error::InvalidConfiguration),
}
}
}
impl<Base, CommsError, PinError> radio::Receive for Sx127x<Base, CommsError, PinError>
where
Base: base::Base<CommsError, PinError>,
{
type Info = PacketInfo;
type Error = Error<CommsError, PinError>;
fn start_receive(&mut self) -> Result<(), Self::Error> {
match self.mode {
Mode::LoRa => self.lora_start_receive(),
Mode::FskOok => self.fsk_start_receive(),
_ => Err(Error::InvalidConfiguration),
}
}
fn check_receive(&mut self, restart: bool) -> Result<bool, Self::Error> {
match self.mode {
Mode::LoRa => self.lora_check_receive(restart),
Mode::FskOok => self.fsk_check_receive(restart),
_ => Err(Error::InvalidConfiguration),
}
}
fn get_received(
&mut self,
info: &mut Self::Info,
data: &mut [u8],
) -> Result<usize, Self::Error> {
match self.mode {
Mode::LoRa => self.lora_get_received(info, data),
Mode::FskOok => self.fsk_get_received(info, data),
_ => Err(Error::InvalidConfiguration),
}
}
}
impl<Base, CommsError, PinError> radio::Rssi for Sx127x<Base, CommsError, PinError>
where
Base: base::Base<CommsError, PinError>,
{
type Error = Error<CommsError, PinError>;
fn poll_rssi(&mut self) -> Result<i16, Error<CommsError, PinError>> {
match self.mode {
Mode::LoRa => self.lora_poll_rssi(),
Mode::FskOok => self.fsk_poll_rssi(),
_ => Err(Error::InvalidConfiguration),
}
}
}
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}