Skip to main content

pamoja_radios/sx127x/
driver.rs

1//! The SX127x driven over embedded-hal in LoRa mode: reset, calibrate, configure, transmit,
2//! and receive.
3//!
4//! [`Sx127x`] owns the SPI device, the NRESET line, and a delay. It reads and writes the
5//! registers of [`register`](super::register), reads RegIrqFlags to learn when a frame has
6//! gone out or come in, and follows the transmit and receive sequences of the SX1276/77/78/79
7//! datasheet (Rev 7), with the errata applied as Semtech's LoRaMac-node applies them.
8
9use embedded_hal::delay::DelayNs;
10use embedded_hal::digital::{self, OutputPin};
11use embedded_hal::spi::{Operation, SpiDevice};
12use pamoja_lora::budget::Decibels;
13use pamoja_lora::LinkSettings;
14
15use super::config::{
16    self, automatic_if, frequency_bytes, high_bw_optimize, image_cal_start, invert_iq, invert_iq_2,
17    preamble_bytes, spurious_reception, LoraModulation, ModulationError, PaOutput, SyncWord,
18    TxPower,
19};
20use super::irq::IrqFlags;
21use super::register::{self, fsk_op_mode, lora_op_mode, read_address, write_address, Mode};
22use super::status::{rssi_dbm, ModemStatus, PacketStatus, Port};
23
24/// How long NRESET is held low, in microseconds. The datasheet's Manual Reset section asks for
25/// a hundred microseconds; this is the 1 ms Semtech's LoRaMac-node holds it for.
26pub const RESET_HOLD_US: u32 = 1_000;
27
28/// How long to wait after releasing NRESET, in microseconds. The datasheet asks for 5 ms;
29/// this is the 6 ms LoRaMac-node waits.
30pub const RESET_SETTLE_US: u32 = 6_000;
31
32/// How often the interrupt flags are read while a frame goes out or comes in, in
33/// microseconds.
34pub const IRQ_POLL_US: u32 = 1_000;
35
36/// How much longer than a frame's airtime a transmission may take before the driver gives
37/// up on it, in microseconds.
38pub const TIMEOUT_MARGIN_US: u64 = 1_000_000;
39
40/// How often RegImageCal is read while a calibration runs, in microseconds.
41pub const CALIBRATION_POLL_US: u32 = 1_000;
42
43/// How long a calibration may run before the driver gives up, in microseconds. The datasheet
44/// says it takes about 10 ms.
45pub const CALIBRATION_LIMIT_US: u32 = 100_000;
46
47/// How a module wires its SX127x: the amplifier output on its antenna and its clock.
48///
49/// The SPI interface cannot see which output a module uses, so it comes from the module's
50/// schematic.
51///
52/// # Examples
53///
54/// ```
55/// use pamoja_radios::sx127x::config::PaOutput;
56/// use pamoja_radios::sx127x::Board;
57///
58/// // An RFM95W: the antenna on PA_BOOST and a crystal.
59/// let board = Board::new(PaOutput::PaBoost);
60/// assert!(!board.tcxo);
61/// ```
62#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
63pub struct Board {
64    /// The amplifier output the antenna is on.
65    pub output: PaOutput,
66    /// Whether a TCXO drives the XTA pin in place of a crystal.
67    pub tcxo: bool,
68}
69
70impl Board {
71    /// A module with a crystal.
72    ///
73    /// # Arguments
74    ///
75    /// * `output` - the amplifier output the antenna is on.
76    ///
77    /// # Returns
78    ///
79    /// The board.
80    pub const fn new(output: PaOutput) -> Board {
81        Board {
82            output,
83            tcxo: false,
84        }
85    }
86
87    /// Returns the board clocked by a TCXO on XTA.
88    ///
89    /// # Returns
90    ///
91    /// The board.
92    pub const fn with_tcxo(mut self) -> Board {
93        self.tcxo = true;
94        self
95    }
96}
97
98/// What a radio sends and listens with: the carrier, the LoRa link, the power, and the sync
99/// word and IQ polarity that keep one network apart from another.
100///
101/// # Examples
102///
103/// ```
104/// use pamoja_lora::LinkSettings;
105/// use pamoja_radios::sx127x::config::{PaOutput, SyncWord, TxPower};
106/// use pamoja_radios::sx127x::RadioConfig;
107///
108/// // A LoRaWAN device on 868.1 MHz at SF9 with 14 dBm on PA_BOOST.
109/// let config = RadioConfig::new(
110///     868_100_000,
111///     LinkSettings::new(9, 125_000),
112///     TxPower::for_output(PaOutput::PaBoost, 14),
113/// )
114/// .lorawan_device();
115/// assert_eq!(config.sync_word, SyncWord::Public);
116/// assert!(config.invert_iq_receive && !config.invert_iq_transmit);
117/// ```
118#[derive(Clone, Copy, Debug, PartialEq, Eq)]
119pub struct RadioConfig {
120    /// The carrier frequency in hertz.
121    pub frequency_hz: u32,
122    /// The LoRa link settings frames are sent and received with.
123    pub link: LinkSettings,
124    /// The amplifier settings.
125    pub power: TxPower,
126    /// The sync word.
127    pub sync_word: SyncWord,
128    /// Whether transmitted frames have inverted IQ.
129    pub invert_iq_transmit: bool,
130    /// Whether received frames are expected with inverted IQ.
131    pub invert_iq_receive: bool,
132}
133
134impl RadioConfig {
135    /// A configuration with the private sync word and standard IQ both ways.
136    ///
137    /// # Arguments
138    ///
139    /// * `frequency_hz` - the carrier frequency in hertz.
140    /// * `link` - the LoRa link settings.
141    /// * `power` - the amplifier settings.
142    ///
143    /// # Returns
144    ///
145    /// The configuration.
146    pub const fn new(frequency_hz: u32, link: LinkSettings, power: TxPower) -> RadioConfig {
147        RadioConfig {
148            frequency_hz,
149            link,
150            power,
151            sync_word: SyncWord::Private,
152            invert_iq_transmit: false,
153            invert_iq_receive: false,
154        }
155    }
156
157    /// Returns the configuration with another sync word.
158    ///
159    /// # Arguments
160    ///
161    /// * `sync_word` - the sync word.
162    ///
163    /// # Returns
164    ///
165    /// The configuration.
166    pub const fn with_sync_word(mut self, sync_word: SyncWord) -> RadioConfig {
167        self.sync_word = sync_word;
168        self
169    }
170
171    /// Returns the configuration with the IQ polarity of each direction set.
172    ///
173    /// # Arguments
174    ///
175    /// * `transmit` - whether transmitted frames have inverted IQ.
176    /// * `receive` - whether received frames have inverted IQ.
177    ///
178    /// # Returns
179    ///
180    /// The configuration.
181    pub const fn with_inverted_iq(mut self, transmit: bool, receive: bool) -> RadioConfig {
182        self.invert_iq_transmit = transmit;
183        self.invert_iq_receive = receive;
184        self
185    }
186
187    /// Returns the configuration for a LoRaWAN end device: the public sync word, standard IQ
188    /// on uplinks, and inverted IQ on downlinks.
189    ///
190    /// # Returns
191    ///
192    /// The configuration.
193    pub const fn lorawan_device(self) -> RadioConfig {
194        self.with_sync_word(SyncWord::Public)
195            .with_inverted_iq(false, true)
196    }
197}
198
199/// How a reception ended.
200#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
201pub enum Reception {
202    /// A frame whose CRC checked, or that carried none.
203    Frame {
204        /// The payload length; the payload is at the start of the buffer.
205        len: usize,
206        /// The signal levels the frame arrived with.
207        status: PacketStatus,
208    },
209    /// No preamble arrived before the timeout.
210    Timeout,
211    /// A frame arrived whose payload CRC failed, and was dropped.
212    Corrupt,
213}
214
215/// What can go wrong driving an SX127x.
216#[derive(Clone, Copy, Debug, PartialEq, Eq)]
217pub enum RadioError<E> {
218    /// The SPI device failed.
219    Spi(E),
220    /// The NRESET line could not be driven.
221    Pin(digital::ErrorKind),
222    /// RegVersion held this instead of 0x12, so no SX1276, SX1277, SX1278, or SX1279, or a
223    /// miswired one, is on the bus.
224    Absent(u8),
225    /// The link settings are not ones the SX127x can use at the carrier.
226    Modulation(ModulationError),
227    /// The power settings are for the other amplifier output than the board wires.
228    Output,
229    /// A payload the chip cannot send: longer than 255 bytes, or empty, with its length.
230    PayloadLength(usize),
231    /// A received payload of this many bytes does not fit the buffer given.
232    BufferTooSmall(usize),
233    /// A transmission or a reception was asked for before [`Sx127x::configure`].
234    NotConfigured,
235    /// The image calibration was still running after [`CALIBRATION_LIMIT_US`].
236    Calibration,
237    /// The expected interrupt did not arrive in the time the driver allows.
238    NoInterrupt,
239}
240
241impl<E: core::fmt::Debug> core::fmt::Display for RadioError<E> {
242    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
243        match self {
244            RadioError::Spi(error) => write!(f, "SPI error: {error:?}"),
245            RadioError::Pin(kind) => write!(f, "NRESET line error: {kind:?}"),
246            RadioError::Absent(version) => {
247                write!(f, "no SX127x answered: RegVersion read {version:#04x}")
248            }
249            RadioError::Modulation(ModulationError::Bandwidth(hz)) => {
250                write!(
251                    f,
252                    "the SX127x has no {hz} Hz LoRa bandwidth at this carrier"
253                )
254            }
255            RadioError::Modulation(ModulationError::SpreadingFactor(sf)) => {
256                write!(f, "the SX127x has no SF{sf}")
257            }
258            RadioError::Modulation(ModulationError::ExplicitHeaderAtSf6) => {
259                write!(f, "SF6 needs an implicit header")
260            }
261            RadioError::Output => {
262                write!(f, "the power settings are for the other amplifier output")
263            }
264            RadioError::PayloadLength(len) => {
265                write!(f, "a LoRa frame carries 1 to 255 bytes, not {len}")
266            }
267            RadioError::BufferTooSmall(len) => {
268                write!(f, "a {len} byte payload does not fit the buffer")
269            }
270            RadioError::NotConfigured => write!(f, "the radio has not been configured"),
271            RadioError::Calibration => write!(f, "the image calibration did not finish"),
272            RadioError::NoInterrupt => write!(f, "the radio raised no interrupt in time"),
273        }
274    }
275}
276
277impl<E: core::fmt::Debug> core::error::Error for RadioError<E> {}
278
279fn pin<E, P: digital::Error>(error: P) -> RadioError<E> {
280    RadioError::Pin(error.kind())
281}
282
283/// A Semtech SX1276, SX1277, SX1278, or SX1279 on an SPI bus, with its NRESET line.
284///
285/// [`init`](Sx127x::init) resets the chip and puts it in LoRa mode,
286/// [`configure`](Sx127x::configure) calibrates its receiver and tunes it to a
287/// [`RadioConfig`], and [`transmit`](Sx127x::transmit) and [`receive`](Sx127x::receive)
288/// send and wait for one frame each. For anything those do not cover, the register and data
289/// buffer methods reach the chip directly.
290///
291/// # Examples
292///
293/// The chip's side of initialization, scripted from the same registers the driver writes:
294/// an RFM95W that answers RegVersion as an SX1276, sleeps in FSK mode so it may switch to
295/// LoRa, stands by in LoRa mode, and has its low-noise amplifier boosted.
296///
297/// ```
298/// use pamoja_hal::digital::PinState;
299/// use pamoja_hal::script::{DelayLog, PinScript, SpiScript, SpiStep};
300/// use pamoja_radios::sx127x::config::{PaOutput, LNA_BOOSTED};
301/// use pamoja_radios::sx127x::register::{self, Mode};
302/// use pamoja_radios::sx127x::{Board, Sx127x};
303///
304/// let write = |address, value| {
305///     [
306///         SpiStep::write([register::write_address(address)]),
307///         SpiStep::write([value]),
308///     ]
309/// };
310/// let mut steps = vec![
311///     SpiStep::write([register::read_address(register::VERSION)]),
312///     SpiStep::read([register::VERSION_SX1276]),
313/// ];
314/// steps.extend(write(register::OP_MODE, register::fsk_op_mode(Mode::Sleep)));
315/// steps.extend(write(register::OP_MODE, register::lora_op_mode(Mode::Sleep)));
316/// steps.extend(write(register::OP_MODE, register::lora_op_mode(Mode::Standby)));
317/// steps.extend(write(register::LNA, LNA_BOOSTED));
318/// let spi = SpiScript::new(steps);
319/// let board = Board::new(PaOutput::PaBoost);
320///
321/// let mut radio = Sx127x::new(spi, PinScript::new([]), DelayLog::new(), board);
322/// radio.init().expect("the scripted RFM95W answers");
323///
324/// let (spi, reset, _) = radio.release();
325/// assert!(spi.done());
326/// assert_eq!(reset.driven(), [PinState::Low, PinState::High]);
327/// ```
328pub struct Sx127x<SPI, RESET, D> {
329    spi: SPI,
330    reset: RESET,
331    delay: D,
332    board: Board,
333    config: Option<RadioConfig>,
334    tuned_hz: Option<u32>,
335    calibrated_high: bool,
336    calibrated_low: bool,
337}
338
339impl<SPI, RESET, D> Sx127x<SPI, RESET, D> {
340    /// Wraps a chip's SPI device and NRESET line. Nothing is sent until
341    /// [`init`](Sx127x::init).
342    ///
343    /// # Arguments
344    ///
345    /// * `spi` - the SPI device, with NSS as its chip select.
346    /// * `reset` - the NRESET line, as an output.
347    /// * `delay` - a delay for the reset pulse and the polling.
348    /// * `board` - how the module wires the chip.
349    ///
350    /// # Returns
351    ///
352    /// The driver.
353    pub fn new(spi: SPI, reset: RESET, delay: D, board: Board) -> Self {
354        Sx127x {
355            spi,
356            reset,
357            delay,
358            board,
359            config: None,
360            tuned_hz: None,
361            calibrated_high: false,
362            calibrated_low: false,
363        }
364    }
365
366    /// Returns how the module wires the chip.
367    ///
368    /// # Returns
369    ///
370    /// The board.
371    pub fn board(&self) -> Board {
372        self.board
373    }
374
375    /// Returns the configuration the chip was last tuned to.
376    ///
377    /// # Returns
378    ///
379    /// The configuration, or `None` before [`configure`](Sx127x::configure) or after a
380    /// reset.
381    pub fn config(&self) -> Option<&RadioConfig> {
382        self.config.as_ref()
383    }
384
385    /// Returns the power settings for an output power on this board's amplifier output.
386    ///
387    /// # Arguments
388    ///
389    /// * `output_dbm` - the output power wanted at the antenna port.
390    ///
391    /// # Returns
392    ///
393    /// The amplifier settings.
394    pub fn tx_power(&self, output_dbm: i8) -> TxPower {
395        TxPower::for_output(self.board.output, output_dbm)
396    }
397
398    /// Gives back the SPI device, the line, and the delay.
399    ///
400    /// # Returns
401    ///
402    /// The SPI device, NRESET, and the delay.
403    pub fn release(self) -> (SPI, RESET, D) {
404        (self.spi, self.reset, self.delay)
405    }
406}
407
408impl<SPI, RESET, D> Sx127x<SPI, RESET, D>
409where
410    SPI: SpiDevice,
411    RESET: OutputPin,
412    D: DelayNs,
413{
414    /// Resets the chip and puts it in LoRa mode.
415    ///
416    /// NRESET is pulsed low and RegVersion read. The chip, which comes out of reset as an
417    /// FSK radio in standby, is put to sleep, clocked from a TCXO if the board has one, and
418    /// switched to LoRa, which it only allows in sleep. It then goes to standby with the LNA
419    /// at LoRaMac-node's setting.
420    ///
421    /// # Errors
422    ///
423    /// Returns [`RadioError::Absent`] if RegVersion is not 0x12, and [`RadioError::Spi`] or
424    /// [`RadioError::Pin`] if the bus or the line fails.
425    pub fn init(&mut self) -> Result<(), RadioError<SPI::Error>> {
426        self.config = None;
427        self.tuned_hz = None;
428        self.calibrated_high = false;
429        self.calibrated_low = false;
430        self.reset.set_low().map_err(pin)?;
431        self.delay.delay_us(RESET_HOLD_US);
432        self.reset.set_high().map_err(pin)?;
433        self.delay.delay_us(RESET_SETTLE_US);
434
435        let version = self.version()?;
436        if version != register::VERSION_SX1276 {
437            return Err(RadioError::Absent(version));
438        }
439        self.write_register(register::OP_MODE, fsk_op_mode(Mode::Sleep))?;
440        if self.board.tcxo {
441            self.write_register(register::TCXO, config::TCXO_INPUT_ON)?;
442        }
443        self.write_register(register::OP_MODE, lora_op_mode(Mode::Sleep))?;
444        self.write_register(register::OP_MODE, lora_op_mode(Mode::Standby))?;
445        self.write_register(register::LNA, config::LNA_BOOSTED)
446    }
447
448    /// Tunes the chip to a configuration.
449    ///
450    /// The first time a carrier on each RF port is configured, the receiver's image and RSSI
451    /// calibration runs there, as the datasheet's Image and RSSI Calibration section advises,
452    /// since the calibration at reset only covers the low frequency port at 434 MHz. The chip
453    /// then takes the frequency, the amplifier, the modem settings, the preamble, the SF6
454    /// detection settings, the 500 kHz erratum, the longest payload, and the sync word.
455    ///
456    /// # Arguments
457    ///
458    /// * `config` - the configuration.
459    ///
460    /// # Errors
461    ///
462    /// Returns [`RadioError::Modulation`] if the link does not fit the chip at the carrier,
463    /// [`RadioError::Output`] if the power settings are for the other output,
464    /// [`RadioError::Calibration`] if the calibration does not finish, and the bus errors of
465    /// [`write_register`](Sx127x::write_register).
466    pub fn configure(&mut self, config: RadioConfig) -> Result<(), RadioError<SPI::Error>> {
467        let modulation = LoraModulation::from_link(&config.link).map_err(RadioError::Modulation)?;
468        if !modulation.bandwidth.in_band(config.frequency_hz) {
469            return Err(RadioError::Modulation(ModulationError::Bandwidth(
470                config.link.bandwidth_hz(),
471            )));
472        }
473        if config.power.output() != self.board.output {
474            return Err(RadioError::Output);
475        }
476        let calibrated = match Port::for_frequency(config.frequency_hz) {
477            Port::High => self.calibrated_high,
478            Port::Low => self.calibrated_low,
479        };
480        if calibrated {
481            self.standby()?;
482        } else {
483            self.calibrate(config.frequency_hz)?;
484        }
485        self.tune(config.frequency_hz)?;
486        self.write_register(register::PA_CONFIG, config.power.pa_config)?;
487        self.write_register(register::PA_DAC, config.power.pa_dac)?;
488        self.write_register(register::OCP, config.power.ocp)?;
489        self.write_register(register::MODEM_CONFIG_1, modulation.modem_config_1())?;
490        self.write_register(register::MODEM_CONFIG_2, modulation.modem_config_2(0))?;
491        self.write_register(register::MODEM_CONFIG_3, modulation.modem_config_3())?;
492        self.write_registers(register::PREAMBLE_MSB, &preamble_bytes(&config.link))?;
493        self.update_register(register::DETECT_OPTIMIZE, |value| {
494            modulation.detect_optimize(value)
495        })?;
496        self.write_register(
497            register::DETECTION_THRESHOLD,
498            modulation.detection_threshold(),
499        )?;
500        let (optimize_1, optimize_2) = high_bw_optimize(modulation.bandwidth, config.frequency_hz);
501        self.write_register(register::HIGH_BW_OPTIMIZE_1, optimize_1)?;
502        if let Some(optimize_2) = optimize_2 {
503            self.write_register(register::HIGH_BW_OPTIMIZE_2, optimize_2)?;
504        }
505        self.write_register(register::MAX_PAYLOAD_LENGTH, 0xFF)?;
506        self.write_register(register::SYNC_WORD, config.sync_word.to_byte())?;
507        self.config = Some(config);
508        Ok(())
509    }
510
511    /// Sends one frame and waits for it to leave.
512    ///
513    /// This is [`start_transmit`](Sx127x::start_transmit), a wait for the frame's airtime,
514    /// and [`finish_transmit`](Sx127x::finish_transmit) read every [`IRQ_POLL_US`] until the
515    /// chip reports the frame sent.
516    ///
517    /// # Arguments
518    ///
519    /// * `payload` - the frame's payload, 1 to 255 bytes.
520    ///
521    /// # Returns
522    ///
523    /// The frame's airtime in microseconds, for a [`DutyCycle`](crate::duty::DutyCycle) to
524    /// count.
525    ///
526    /// # Errors
527    ///
528    /// Returns the errors of [`start_transmit`](Sx127x::start_transmit), and
529    /// [`RadioError::NoInterrupt`] if TxDone does not arrive within the airtime and
530    /// [`TIMEOUT_MARGIN_US`] twice over.
531    pub fn transmit(&mut self, payload: &[u8]) -> Result<u64, RadioError<SPI::Error>> {
532        let airtime_us = self.start_transmit(payload)?;
533        let limit_us = airtime_us.saturating_add(2 * TIMEOUT_MARGIN_US);
534        let mut waited_us = airtime_us;
535        self.pause_us(airtime_us);
536        while !self.finish_transmit()? {
537            if waited_us >= limit_us {
538                return Err(RadioError::NoInterrupt);
539            }
540            self.delay.delay_us(IRQ_POLL_US);
541            waited_us = waited_us.saturating_add(u64::from(IRQ_POLL_US));
542        }
543        Ok(airtime_us)
544    }
545
546    /// Starts sending one frame and returns once the chip is transmitting.
547    ///
548    /// The steps are the datasheet's transmit sequence: standby, the IQ polarity, the payload
549    /// length, the data buffer pointer at the transmit base, the payload into RegFifo, TxDone
550    /// on DIO0, the interrupt flags cleared, and TX mode, after which the chip returns to
551    /// standby by itself.
552    ///
553    /// # Arguments
554    ///
555    /// * `payload` - the frame's payload, 1 to 255 bytes.
556    ///
557    /// # Returns
558    ///
559    /// The frame's airtime in microseconds.
560    ///
561    /// # Errors
562    ///
563    /// Returns [`RadioError::NotConfigured`] before [`configure`](Sx127x::configure),
564    /// [`RadioError::PayloadLength`] for an empty payload or one past 255 bytes, and the bus
565    /// errors of [`write_register`](Sx127x::write_register).
566    pub fn start_transmit(&mut self, payload: &[u8]) -> Result<u64, RadioError<SPI::Error>> {
567        let settings = self.config.ok_or(RadioError::NotConfigured)?;
568        let len = u8::try_from(payload.len())
569            .ok()
570            .filter(|len| *len > 0)
571            .ok_or(RadioError::PayloadLength(payload.len()))?;
572        let invert = settings.invert_iq_transmit;
573
574        self.standby()?;
575        self.write_register(register::INVERT_IQ, invert_iq(false, invert))?;
576        self.write_register(register::INVERT_IQ_2, invert_iq_2(invert))?;
577        self.tune(settings.frequency_hz)?;
578        self.write_register(register::PAYLOAD_LENGTH, len)?;
579        self.write_register(register::FIFO_TX_BASE_ADDR, 0)?;
580        self.write_register(register::FIFO_ADDR_PTR, 0)?;
581        self.write_registers(register::FIFO, payload)?;
582        self.write_register(register::DIO_MAPPING_1, config::DIO0_TX_DONE)?;
583        self.write_register(register::IRQ_FLAGS, IrqFlags::ALL.bits())?;
584        self.write_register(register::OP_MODE, lora_op_mode(Mode::Tx))?;
585        Ok(settings.link.airtime_us(payload.len()))
586    }
587
588    /// Reports whether the frame [`start_transmit`](Sx127x::start_transmit) began has left.
589    ///
590    /// RegIrqFlags is read once, and on TxDone that flag is cleared.
591    ///
592    /// # Returns
593    ///
594    /// `true` once the frame has been sent, `false` while it is still going out.
595    ///
596    /// # Errors
597    ///
598    /// Returns the bus errors of [`write_register`](Sx127x::write_register).
599    pub fn finish_transmit(&mut self) -> Result<bool, RadioError<SPI::Error>> {
600        let flags = self.irq_flags()?;
601        if !flags.contains(IrqFlags::TX_DONE) {
602            return Ok(false);
603        }
604        self.write_register(register::IRQ_FLAGS, IrqFlags::TX_DONE.bits())?;
605        Ok(true)
606    }
607
608    /// Listens for one frame in RXSINGLE mode.
609    ///
610    /// The receiver is prepared as for [`listen`](Sx127x::listen), the symbol timeout is set
611    /// from `timeout_us`, and RegIrqFlags is read until RxDone or RxTimeout. The chip returns
612    /// to standby by itself either way.
613    ///
614    /// # Arguments
615    ///
616    /// * `buffer` - where the payload goes.
617    /// * `timeout_us` - how long to listen for a preamble, in microseconds, which the chip
618    ///   counts in symbols from 4 to 1023; a longer timeout ends at 1023 symbols.
619    ///
620    /// # Returns
621    ///
622    /// The frame's length and levels, or that the timeout passed or the frame was corrupt.
623    ///
624    /// # Errors
625    ///
626    /// Returns [`RadioError::NotConfigured`] before [`configure`](Sx127x::configure),
627    /// [`RadioError::BufferTooSmall`] if the payload does not fit, [`RadioError::NoInterrupt`]
628    /// if the chip never answers, and the bus errors of
629    /// [`write_register`](Sx127x::write_register).
630    pub fn receive(
631        &mut self,
632        buffer: &mut [u8],
633        timeout_us: u64,
634    ) -> Result<Reception, RadioError<SPI::Error>> {
635        let settings = self.config.ok_or(RadioError::NotConfigured)?;
636        let modulation =
637            LoraModulation::from_link(&settings.link).map_err(RadioError::Modulation)?;
638        self.prepare_reception(&settings, &modulation)?;
639        let symbols = config::symbol_timeout(&settings.link, timeout_us);
640        self.write_register(register::MODEM_CONFIG_2, modulation.modem_config_2(symbols))?;
641        self.write_register(register::SYMB_TIMEOUT_LSB, symbols as u8)?;
642        self.write_register(register::OP_MODE, lora_op_mode(Mode::RxSingle))?;
643
644        let window_us = u64::from(symbols) * settings.link.symbol_time_us();
645        let limit_us = window_us
646            .saturating_add(settings.link.airtime_us(usize::from(u8::MAX)))
647            .saturating_add(TIMEOUT_MARGIN_US);
648        let flags = self.wait_for(IrqFlags::RX_DONE | IrqFlags::RX_TIMEOUT, limit_us)?;
649        self.write_register(register::IRQ_FLAGS, flags.bits())?;
650        if !flags.contains(IrqFlags::RX_DONE) {
651            return Ok(Reception::Timeout);
652        }
653        if flags.contains(IrqFlags::PAYLOAD_CRC_ERROR) {
654            return Ok(Reception::Corrupt);
655        }
656        self.read_frame(buffer, &settings)
657    }
658
659    /// Listens for a preamble and reports whether one is there: the channel activity
660    /// detection of section 4.1.5.
661    ///
662    /// The chip listens over one symbol, reports CadDone, and says with CadDetected whether
663    /// a LoRa preamble was in it. It returns to standby either way. A relay scanning for
664    /// wake-on-radio frames calls this once a scan period, and listens only when it says
665    /// something is there.
666    ///
667    /// # Returns
668    ///
669    /// `true` when the chip detected a preamble.
670    ///
671    /// # Errors
672    ///
673    /// Returns [`RadioError::NotConfigured`] before [`configure`](Sx127x::configure),
674    /// [`RadioError::Modulation`] for settings the chip cannot take,
675    /// [`RadioError::NoInterrupt`] if it never reports the detection done, and the bus
676    /// errors of [`write_register`](Sx127x::write_register).
677    pub fn detect(&mut self) -> Result<bool, RadioError<SPI::Error>> {
678        let settings = self.config.ok_or(RadioError::NotConfigured)?;
679        let modulation =
680            LoraModulation::from_link(&settings.link).map_err(RadioError::Modulation)?;
681        self.prepare_reception(&settings, &modulation)?;
682        self.write_register(register::DIO_MAPPING_1, config::DIO0_CAD_DONE)?;
683        self.write_register(register::OP_MODE, lora_op_mode(Mode::Cad))?;
684
685        // The detection takes a symbol and the processing after it; the margin covers a bus
686        // that answers slowly.
687        let limit_us = settings
688            .link
689            .symbol_time_us()
690            .saturating_mul(4)
691            .saturating_add(TIMEOUT_MARGIN_US);
692        let flags = self.wait_for(IrqFlags::CAD_DONE, limit_us)?;
693        self.write_register(register::IRQ_FLAGS, IrqFlags::ALL.bits())?;
694        Ok(flags.contains(IrqFlags::CAD_DETECTED))
695    }
696
697    /// Starts listening in RXCONTINUOUS mode, so the chip receives frame after frame until
698    /// another mode is set.
699    ///
700    /// The steps are the datasheet's receive sequence with erratum 2.3 applied: standby, the
701    /// IQ polarity, the IF and the carrier offset of the erratum, the data buffer pointer at
702    /// the receive base, RxDone on DIO0, the interrupt flags cleared, and RXCONTINUOUS. Each
703    /// frame is read with [`take_frame`](Sx127x::take_frame).
704    ///
705    /// # Errors
706    ///
707    /// Returns [`RadioError::NotConfigured`] before [`configure`](Sx127x::configure), and the
708    /// bus errors of [`write_register`](Sx127x::write_register).
709    pub fn listen(&mut self) -> Result<(), RadioError<SPI::Error>> {
710        let settings = self.config.ok_or(RadioError::NotConfigured)?;
711        let modulation =
712            LoraModulation::from_link(&settings.link).map_err(RadioError::Modulation)?;
713        self.prepare_reception(&settings, &modulation)?;
714        self.write_register(register::OP_MODE, lora_op_mode(Mode::RxContinuous))
715    }
716
717    /// Takes the frame a [`listen`](Sx127x::listen) has received, if one has arrived.
718    ///
719    /// RegIrqFlags is read once. On RxDone the reception flags are cleared, and a frame whose
720    /// CRC checked is copied out of the data buffer from its start address with its signal
721    /// levels while the chip goes on listening.
722    ///
723    /// # Arguments
724    ///
725    /// * `buffer` - where the payload goes.
726    ///
727    /// # Returns
728    ///
729    /// The frame, a corrupt frame, or `None` when nothing has arrived.
730    ///
731    /// # Errors
732    ///
733    /// Returns [`RadioError::NotConfigured`] before [`configure`](Sx127x::configure),
734    /// [`RadioError::BufferTooSmall`] if the payload does not fit, and the bus errors of
735    /// [`write_register`](Sx127x::write_register).
736    pub fn take_frame(
737        &mut self,
738        buffer: &mut [u8],
739    ) -> Result<Option<Reception>, RadioError<SPI::Error>> {
740        let settings = self.config.ok_or(RadioError::NotConfigured)?;
741        let flags = self.irq_flags()?;
742        if !flags.contains(IrqFlags::RX_DONE) {
743            return Ok(None);
744        }
745        let received = IrqFlags::RX_DONE | IrqFlags::PAYLOAD_CRC_ERROR | IrqFlags::VALID_HEADER;
746        self.write_register(register::IRQ_FLAGS, (flags & received).bits())?;
747        if flags.contains(IrqFlags::PAYLOAD_CRC_ERROR) {
748            return Ok(Some(Reception::Corrupt));
749        }
750        self.read_frame(buffer, &settings).map(Some)
751    }
752
753    /// Puts the chip in standby, which stops a transmission or a reception.
754    ///
755    /// # Errors
756    ///
757    /// Returns the bus errors of [`write_register`](Sx127x::write_register).
758    pub fn standby(&mut self) -> Result<(), RadioError<SPI::Error>> {
759        self.write_register(register::OP_MODE, lora_op_mode(Mode::Standby))
760    }
761
762    /// Draws a random number from the noise the receiver hears.
763    ///
764    /// RegRssiWideband holds a wideband RSSI sample, which the datasheet notes is used to
765    /// generate a random number locally. The chip listens continuously with every interrupt
766    /// masked, and the lowest bit of a sample a millisecond is taken 32 times, which is the
767    /// procedure of Semtech's LoRaMac-node reference driver. The interrupt mask is put back
768    /// and the chip left in standby, so the next frame goes out as configured.
769    ///
770    /// This is the source LoRaWAN 1.0.3 section 6.2.4 suggests for a join nonce on a device
771    /// with no other: "a sequence of RSSI measurements".
772    ///
773    /// # Returns
774    ///
775    /// Thirty-two bits of noise.
776    ///
777    /// # Errors
778    ///
779    /// Returns the bus errors of [`write_register`](Sx127x::write_register).
780    pub fn random(&mut self) -> Result<u32, RadioError<SPI::Error>> {
781        let mask = self.read_register(register::IRQ_FLAGS_MASK)?;
782        self.write_register(register::IRQ_FLAGS_MASK, 0xFF)?;
783        self.write_register(register::OP_MODE, lora_op_mode(Mode::RxContinuous))?;
784        let mut number = 0u32;
785        for bit in 0..32 {
786            self.delay.delay_ms(1);
787            let sample = self.read_register(register::RSSI_WIDEBAND)?;
788            number |= u32::from(sample & 0x01) << bit;
789        }
790        self.write_register(register::OP_MODE, lora_op_mode(Mode::Standby))?;
791        self.write_register(register::IRQ_FLAGS_MASK, mask)?;
792        Ok(number)
793    }
794
795    /// Puts the chip to sleep, where it keeps its registers but loses the data buffer.
796    ///
797    /// The configuration survives sleep, so the next transmission or reception wakes the chip
798    /// by setting its mode.
799    ///
800    /// # Errors
801    ///
802    /// Returns the bus errors of [`write_register`](Sx127x::write_register).
803    pub fn sleep(&mut self) -> Result<(), RadioError<SPI::Error>> {
804        self.write_register(register::OP_MODE, lora_op_mode(Mode::Sleep))
805    }
806
807    /// Reads RegVersion.
808    ///
809    /// # Returns
810    ///
811    /// The silicon revision, 0x12 for the SX1276 family.
812    ///
813    /// # Errors
814    ///
815    /// Returns [`RadioError::Spi`] if the SPI device fails.
816    pub fn version(&mut self) -> Result<u8, RadioError<SPI::Error>> {
817        self.read_register(register::VERSION)
818    }
819
820    /// Reads the raised interrupts.
821    ///
822    /// # Returns
823    ///
824    /// RegIrqFlags.
825    ///
826    /// # Errors
827    ///
828    /// Returns [`RadioError::Spi`] if the SPI device fails.
829    pub fn irq_flags(&mut self) -> Result<IrqFlags, RadioError<SPI::Error>> {
830        self.read_register(register::IRQ_FLAGS)
831            .map(IrqFlags::from_bits)
832    }
833
834    /// Reads the live state of the LoRa modem.
835    ///
836    /// # Returns
837    ///
838    /// RegModemStat, decoded.
839    ///
840    /// # Errors
841    ///
842    /// Returns [`RadioError::Spi`] if the SPI device fails.
843    pub fn modem_status(&mut self) -> Result<ModemStatus, RadioError<SPI::Error>> {
844        self.read_register(register::MODEM_STAT)
845            .map(ModemStatus::from_byte)
846    }
847
848    /// Reads the signal power the receiver hears right now, while it listens.
849    ///
850    /// # Returns
851    ///
852    /// The RSSI in dBm, with the offset of the port the configured carrier uses.
853    ///
854    /// # Errors
855    ///
856    /// Returns [`RadioError::NotConfigured`] before [`configure`](Sx127x::configure), and
857    /// [`RadioError::Spi`] if the SPI device fails.
858    pub fn rssi(&mut self) -> Result<Decibels, RadioError<SPI::Error>> {
859        let settings = self.config.ok_or(RadioError::NotConfigured)?;
860        let byte = self.read_register(register::RSSI_VALUE)?;
861        Ok(rssi_dbm(byte, Port::for_frequency(settings.frequency_hz)))
862    }
863
864    /// Reads one register.
865    ///
866    /// # Arguments
867    ///
868    /// * `address` - the register address.
869    ///
870    /// # Returns
871    ///
872    /// The register value.
873    ///
874    /// # Errors
875    ///
876    /// Returns [`RadioError::Spi`] if the SPI device fails.
877    pub fn read_register(&mut self, address: u8) -> Result<u8, RadioError<SPI::Error>> {
878        let mut value = [0u8; 1];
879        self.read_registers(address, &mut value)?;
880        Ok(value[0])
881    }
882
883    /// Writes one register.
884    ///
885    /// # Arguments
886    ///
887    /// * `address` - the register address.
888    /// * `value` - the value.
889    ///
890    /// # Errors
891    ///
892    /// Returns [`RadioError::Spi`] if the SPI device fails.
893    pub fn write_register(&mut self, address: u8, value: u8) -> Result<(), RadioError<SPI::Error>> {
894        self.write_registers(address, &[value])
895    }
896
897    /// Reads consecutive registers in one transaction, or bytes out of the data buffer when
898    /// `address` is [`register::FIFO`].
899    ///
900    /// # Arguments
901    ///
902    /// * `address` - the first register's address.
903    /// * `values` - where the values go.
904    ///
905    /// # Errors
906    ///
907    /// Returns [`RadioError::Spi`] if the SPI device fails.
908    pub fn read_registers(
909        &mut self,
910        address: u8,
911        values: &mut [u8],
912    ) -> Result<(), RadioError<SPI::Error>> {
913        self.spi
914            .transaction(&mut [
915                Operation::Write(&[read_address(address)]),
916                Operation::Read(values),
917            ])
918            .map_err(RadioError::Spi)
919    }
920
921    /// Writes consecutive registers in one transaction, or bytes into the data buffer when
922    /// `address` is [`register::FIFO`].
923    ///
924    /// # Arguments
925    ///
926    /// * `address` - the first register's address.
927    /// * `values` - the values.
928    ///
929    /// # Errors
930    ///
931    /// Returns [`RadioError::Spi`] if the SPI device fails.
932    pub fn write_registers(
933        &mut self,
934        address: u8,
935        values: &[u8],
936    ) -> Result<(), RadioError<SPI::Error>> {
937        self.spi
938            .transaction(&mut [
939                Operation::Write(&[write_address(address)]),
940                Operation::Write(values),
941            ])
942            .map_err(RadioError::Spi)
943    }
944
945    fn calibrate(&mut self, frequency_hz: u32) -> Result<(), RadioError<SPI::Error>> {
946        self.write_register(register::OP_MODE, lora_op_mode(Mode::Sleep))?;
947        self.write_register(register::OP_MODE, fsk_op_mode(Mode::Sleep))?;
948        self.write_register(register::OP_MODE, fsk_op_mode(Mode::Standby))?;
949        self.write_register(register::PA_CONFIG, 0x00)?;
950        self.write_registers(register::FRF_MSB, &frequency_bytes(frequency_hz))?;
951        self.tuned_hz = Some(frequency_hz);
952        self.update_register(register::IMAGE_CAL, image_cal_start)?;
953        let mut waited_us = 0u32;
954        while self.read_register(register::IMAGE_CAL)? & config::IMAGE_CAL_RUNNING != 0 {
955            if waited_us >= CALIBRATION_LIMIT_US {
956                return Err(RadioError::Calibration);
957            }
958            self.delay.delay_us(CALIBRATION_POLL_US);
959            waited_us = waited_us.saturating_add(CALIBRATION_POLL_US);
960        }
961        self.write_register(register::OP_MODE, fsk_op_mode(Mode::Sleep))?;
962        self.write_register(register::OP_MODE, lora_op_mode(Mode::Sleep))?;
963        self.standby()?;
964        match Port::for_frequency(frequency_hz) {
965            Port::High => self.calibrated_high = true,
966            Port::Low => self.calibrated_low = true,
967        }
968        Ok(())
969    }
970
971    fn tune(&mut self, frequency_hz: u32) -> Result<(), RadioError<SPI::Error>> {
972        if self.tuned_hz != Some(frequency_hz) {
973            self.write_registers(register::FRF_MSB, &frequency_bytes(frequency_hz))?;
974            self.tuned_hz = Some(frequency_hz);
975        }
976        Ok(())
977    }
978
979    fn prepare_reception(
980        &mut self,
981        settings: &RadioConfig,
982        modulation: &LoraModulation,
983    ) -> Result<(), RadioError<SPI::Error>> {
984        let invert = settings.invert_iq_receive;
985        self.standby()?;
986        self.write_register(register::INVERT_IQ, invert_iq(invert, false))?;
987        self.write_register(register::INVERT_IQ_2, invert_iq_2(invert))?;
988        let erratum = spurious_reception(modulation.bandwidth);
989        self.update_register(register::DETECT_OPTIMIZE, |value| {
990            automatic_if(value, erratum.automatic_if)
991        })?;
992        if let Some(if_freq_2) = erratum.if_freq_2 {
993            self.write_register(register::IF_FREQ_1, 0x00)?;
994            self.write_register(register::IF_FREQ_2, if_freq_2)?;
995        }
996        self.tune(settings.frequency_hz.saturating_add(erratum.offset_hz))?;
997        self.write_register(register::FIFO_RX_BASE_ADDR, 0)?;
998        self.write_register(register::FIFO_ADDR_PTR, 0)?;
999        self.write_register(register::DIO_MAPPING_1, config::DIO0_RX_DONE)?;
1000        self.write_register(register::IRQ_FLAGS, IrqFlags::ALL.bits())
1001    }
1002
1003    fn read_frame(
1004        &mut self,
1005        buffer: &mut [u8],
1006        settings: &RadioConfig,
1007    ) -> Result<Reception, RadioError<SPI::Error>> {
1008        let len = usize::from(self.read_register(register::RX_NB_BYTES)?);
1009        let frame = buffer
1010            .get_mut(..len)
1011            .ok_or(RadioError::BufferTooSmall(len))?;
1012        let start = self.read_register(register::FIFO_RX_CURRENT_ADDR)?;
1013        self.write_register(register::FIFO_ADDR_PTR, start)?;
1014        if len > 0 {
1015            self.read_registers(register::FIFO, frame)?;
1016        }
1017        let mut levels = [0u8; 2];
1018        self.read_registers(register::PKT_SNR_VALUE, &mut levels)?;
1019        Ok(Reception::Frame {
1020            len,
1021            status: PacketStatus::from_bytes(levels, Port::for_frequency(settings.frequency_hz)),
1022        })
1023    }
1024
1025    fn update_register(
1026        &mut self,
1027        address: u8,
1028        change: impl FnOnce(u8) -> u8,
1029    ) -> Result<(), RadioError<SPI::Error>> {
1030        let value = self.read_register(address)?;
1031        self.write_register(address, change(value))
1032    }
1033
1034    fn wait_for(
1035        &mut self,
1036        events: IrqFlags,
1037        limit_us: u64,
1038    ) -> Result<IrqFlags, RadioError<SPI::Error>> {
1039        let mut waited_us = 0u64;
1040        loop {
1041            let flags = self.irq_flags()?;
1042            if flags.intersects(events) {
1043                return Ok(flags);
1044            }
1045            if waited_us >= limit_us {
1046                return Err(RadioError::NoInterrupt);
1047            }
1048            self.delay.delay_us(IRQ_POLL_US);
1049            waited_us = waited_us.saturating_add(u64::from(IRQ_POLL_US));
1050        }
1051    }
1052
1053    fn pause_us(&mut self, micros: u64) {
1054        let mut left = micros;
1055        while left > 0 {
1056            let step = u32::try_from(left).unwrap_or(u32::MAX);
1057            self.delay.delay_us(step);
1058            left -= u64::from(step);
1059        }
1060    }
1061}
1062
1063#[cfg(test)]
1064mod tests {
1065    use super::*;
1066    use pamoja_hal::digital::PinState;
1067    use pamoja_hal::script::{DelayLog, PinScript, SpiScript, SpiStep};
1068
1069    type Radio = Sx127x<SpiScript, PinScript, DelayLog>;
1070
1071    fn radio(steps: Vec<SpiStep>) -> Radio {
1072        Sx127x::new(
1073            SpiScript::new(steps),
1074            PinScript::new([]),
1075            DelayLog::new(),
1076            Board::new(PaOutput::PaBoost),
1077        )
1078    }
1079
1080    fn wrote(address: u8, values: &[u8]) -> [SpiStep; 2] {
1081        [
1082            SpiStep::write([address | 0x80]),
1083            SpiStep::write(values.to_vec()),
1084        ]
1085    }
1086
1087    fn read(address: u8, values: &[u8]) -> [SpiStep; 2] {
1088        [SpiStep::write([address]), SpiStep::read(values.to_vec())]
1089    }
1090
1091    #[test]
1092    fn a_random_number_is_the_low_bit_of_32_wideband_samples() {
1093        let mut steps = Vec::new();
1094        steps.extend(read(0x11, &[0x00]));
1095        steps.extend(wrote(0x11, &[0xFF]));
1096        steps.extend(wrote(0x01, &[0x8D]));
1097        let samples: [u8; 32] = core::array::from_fn(|bit| if bit % 3 == 0 { 0x6B } else { 0x6A });
1098        for sample in samples {
1099            steps.extend(read(0x2C, &[sample]));
1100        }
1101        steps.extend(wrote(0x01, &[0x89]));
1102        steps.extend(wrote(0x11, &[0x00]));
1103
1104        let mut radio = radio(steps);
1105        let expected = (0..32).fold(0u32, |number, bit| {
1106            number | (u32::from(bit % 3 == 0) << bit)
1107        });
1108        assert_eq!(radio.random(), Ok(expected));
1109        let (spi, _, delay) = radio.release();
1110        assert!(spi.done(), "{} steps left", spi.remaining());
1111        assert_eq!(
1112            delay.total_ns(),
1113            32 * 1_000_000,
1114            "a millisecond before each sample"
1115        );
1116    }
1117
1118    fn eu868() -> RadioConfig {
1119        RadioConfig::new(
1120            868_100_000,
1121            LinkSettings::new(7, 125_000),
1122            TxPower::for_output(PaOutput::PaBoost, 14),
1123        )
1124        .with_sync_word(SyncWord::Public)
1125    }
1126
1127    fn calibration(frequency: [u8; 3]) -> Vec<SpiStep> {
1128        let mut steps = Vec::new();
1129        steps.extend(wrote(0x01, &[0x88]));
1130        steps.extend(wrote(0x01, &[0x08]));
1131        steps.extend(wrote(0x01, &[0x09]));
1132        steps.extend(wrote(0x09, &[0x00]));
1133        steps.extend(wrote(0x06, &frequency));
1134        steps.extend(read(0x3B, &[0x82]));
1135        steps.extend(wrote(0x3B, &[0x42]));
1136        steps.extend(read(0x3B, &[0x22]));
1137        steps.extend(read(0x3B, &[0x02]));
1138        steps.extend(wrote(0x01, &[0x08]));
1139        steps.extend(wrote(0x01, &[0x88]));
1140        steps.extend(wrote(0x01, &[0x89]));
1141        steps
1142    }
1143
1144    fn configuration() -> Vec<SpiStep> {
1145        let mut steps = calibration([0xD9, 0x06, 0x66]);
1146        steps.extend(wrote(0x09, &[0xFC]));
1147        steps.extend(wrote(0x4D, &[0x84]));
1148        steps.extend(wrote(0x0B, &[0x2B]));
1149        steps.extend(wrote(0x1D, &[0x72]));
1150        steps.extend(wrote(0x1E, &[0x74]));
1151        steps.extend(wrote(0x26, &[0x04]));
1152        steps.extend(wrote(0x20, &[0x00, 0x08]));
1153        steps.extend(read(0x31, &[0xC3]));
1154        steps.extend(wrote(0x31, &[0xC3]));
1155        steps.extend(wrote(0x37, &[0x0A]));
1156        steps.extend(wrote(0x36, &[0x03]));
1157        steps.extend(wrote(0x23, &[0xFF]));
1158        steps.extend(wrote(0x39, &[0x34]));
1159        steps
1160    }
1161
1162    fn configured(more: Vec<SpiStep>) -> Radio {
1163        let mut steps = configuration();
1164        steps.extend(more);
1165        let mut radio = radio(steps);
1166        radio.configure(eu868()).expect("configures");
1167        radio
1168    }
1169
1170    fn reception_setup() -> Vec<SpiStep> {
1171        let mut steps = Vec::new();
1172        steps.extend(wrote(0x01, &[0x89]));
1173        steps.extend(wrote(0x33, &[0x27]));
1174        steps.extend(wrote(0x3B, &[0x1D]));
1175        steps.extend(read(0x31, &[0xC3]));
1176        steps.extend(wrote(0x31, &[0x43]));
1177        steps.extend(wrote(0x30, &[0x00]));
1178        steps.extend(wrote(0x2F, &[0x40]));
1179        steps.extend(wrote(0x0F, &[0x00]));
1180        steps.extend(wrote(0x0D, &[0x00]));
1181        steps.extend(wrote(0x40, &[0x00]));
1182        steps.extend(wrote(0x12, &[0xFF]));
1183        steps
1184    }
1185
1186    #[test]
1187    fn init_resets_checks_the_version_and_enters_lora_mode_with_a_tcxo() {
1188        let mut steps = Vec::new();
1189        steps.extend(read(0x42, &[0x12]));
1190        steps.extend(wrote(0x01, &[0x08]));
1191        steps.extend(wrote(0x4B, &[0x19]));
1192        steps.extend(wrote(0x01, &[0x88]));
1193        steps.extend(wrote(0x01, &[0x89]));
1194        steps.extend(wrote(0x0C, &[0x23]));
1195        let board = Board::new(PaOutput::PaBoost).with_tcxo();
1196        let mut radio = Sx127x::new(
1197            SpiScript::new(steps),
1198            PinScript::new([]),
1199            DelayLog::new(),
1200            board,
1201        );
1202
1203        radio.init().expect("initializes");
1204
1205        let (spi, reset, delay) = radio.release();
1206        assert!(spi.done(), "{} steps left", spi.remaining());
1207        assert_eq!(reset.driven(), [PinState::Low, PinState::High]);
1208        assert_eq!(delay.waits_ns(), [1_000_000, 6_000_000]);
1209    }
1210
1211    #[test]
1212    fn init_refuses_a_chip_that_is_not_an_sx1276() {
1213        let mut radio = radio(read(0x42, &[0x22]).to_vec());
1214        assert_eq!(radio.init(), Err(RadioError::Absent(0x22)));
1215    }
1216
1217    #[test]
1218    fn configure_calibrates_the_port_once_then_writes_the_modem() {
1219        let mut steps = configuration();
1220        steps.extend(wrote(0x01, &[0x89]));
1221        steps.extend(wrote(0x06, &[0xD9, 0x13, 0x33]));
1222        steps.extend(wrote(0x09, &[0xFC]));
1223        steps.extend(wrote(0x4D, &[0x84]));
1224        steps.extend(wrote(0x0B, &[0x2B]));
1225        steps.extend(wrote(0x1D, &[0x72]));
1226        steps.extend(wrote(0x1E, &[0x74]));
1227        steps.extend(wrote(0x26, &[0x04]));
1228        steps.extend(wrote(0x20, &[0x00, 0x08]));
1229        steps.extend(read(0x31, &[0xC3]));
1230        steps.extend(wrote(0x31, &[0xC3]));
1231        steps.extend(wrote(0x37, &[0x0A]));
1232        steps.extend(wrote(0x36, &[0x03]));
1233        steps.extend(wrote(0x23, &[0xFF]));
1234        steps.extend(wrote(0x39, &[0x34]));
1235        let mut radio = radio(steps);
1236
1237        radio.configure(eu868()).expect("configures on 868.1 MHz");
1238        let mut next = eu868();
1239        next.frequency_hz = 868_300_000;
1240        radio.configure(next).expect("configures on 868.3 MHz");
1241
1242        let (spi, _, delay) = radio.release();
1243        assert!(spi.done(), "{} steps left", spi.remaining());
1244        assert_eq!(delay.waits_ns(), [1_000_000], "one poll while calibrating");
1245    }
1246
1247    #[test]
1248    fn configure_refuses_settings_the_chip_cannot_use() {
1249        let mut radio = radio(Vec::new());
1250        let sf5 = RadioConfig::new(
1251            868_100_000,
1252            LinkSettings::new(5, 125_000),
1253            TxPower::for_output(PaOutput::PaBoost, 14),
1254        );
1255        assert_eq!(
1256            radio.configure(sf5),
1257            Err(RadioError::Modulation(ModulationError::SpreadingFactor(5)))
1258        );
1259        let wide_at_169 = RadioConfig::new(
1260            169_400_000,
1261            LinkSettings::new(7, 500_000),
1262            TxPower::for_output(PaOutput::PaBoost, 14),
1263        );
1264        assert_eq!(
1265            radio.configure(wide_at_169),
1266            Err(RadioError::Modulation(ModulationError::Bandwidth(500_000)))
1267        );
1268        let rfo = RadioConfig::new(
1269            868_100_000,
1270            LinkSettings::new(7, 125_000),
1271            TxPower::for_output(PaOutput::Rfo, 14),
1272        );
1273        assert_eq!(radio.configure(rfo), Err(RadioError::Output));
1274        let (spi, _, _) = radio.release();
1275        assert_eq!(spi.consumed(), 0, "nothing reaches the bus");
1276    }
1277
1278    #[test]
1279    fn a_frame_goes_out_through_the_fifo_and_tx_mode() {
1280        let mut steps = Vec::new();
1281        steps.extend(wrote(0x01, &[0x89]));
1282        steps.extend(wrote(0x33, &[0x27]));
1283        steps.extend(wrote(0x3B, &[0x1D]));
1284        steps.extend(wrote(0x22, &[0x05]));
1285        steps.extend(wrote(0x0E, &[0x00]));
1286        steps.extend(wrote(0x0D, &[0x00]));
1287        steps.extend(wrote(0x00, b"level"));
1288        steps.extend(wrote(0x40, &[0x40]));
1289        steps.extend(wrote(0x12, &[0xFF]));
1290        steps.extend(wrote(0x01, &[0x8B]));
1291        steps.extend(read(0x12, &[0x00]));
1292        steps.extend(read(0x12, &[0x08]));
1293        steps.extend(wrote(0x12, &[0x08]));
1294        let mut radio = configured(steps);
1295
1296        let airtime = radio.transmit(b"level").expect("sends");
1297
1298        assert_eq!(airtime, LinkSettings::new(7, 125_000).airtime_us(5));
1299        let (spi, _, _) = radio.release();
1300        assert!(spi.done(), "{} steps left", spi.remaining());
1301    }
1302
1303    #[test]
1304    fn a_transmission_needs_a_configuration_and_a_payload_that_fits() {
1305        let mut unconfigured = radio(Vec::new());
1306        assert_eq!(
1307            unconfigured.start_transmit(b"x"),
1308            Err(RadioError::NotConfigured)
1309        );
1310        let mut radio = configured(Vec::new());
1311        assert_eq!(radio.start_transmit(&[]), Err(RadioError::PayloadLength(0)));
1312        assert_eq!(
1313            radio.start_transmit(&[0; 256]),
1314            Err(RadioError::PayloadLength(256))
1315        );
1316    }
1317
1318    #[test]
1319    fn detection_reports_whether_a_preamble_was_in_the_symbol_it_listened_over() {
1320        let scan = |flags: u8| {
1321            let mut steps = reception_setup();
1322            steps.extend(wrote(0x40, &[0x80]));
1323            steps.extend(wrote(0x01, &[0x8F]));
1324            steps.extend(read(0x12, &[flags]));
1325            steps.extend(wrote(0x12, &[0xFF]));
1326            steps
1327        };
1328
1329        let mut heard = configured(scan(0x05));
1330        assert_eq!(heard.detect(), Ok(true), "CadDone with CadDetected");
1331        assert!(heard.release().0.done());
1332
1333        let mut quiet = configured(scan(0x04));
1334        assert_eq!(quiet.detect(), Ok(false), "CadDone alone");
1335        assert!(quiet.release().0.done());
1336
1337        let mut bare = radio(Vec::new());
1338        assert_eq!(bare.detect(), Err(RadioError::NotConfigured));
1339    }
1340
1341    #[test]
1342    fn listening_applies_the_spurious_reception_erratum_and_takes_frames() {
1343        let mut steps = reception_setup();
1344        steps.extend(wrote(0x01, &[0x8D]));
1345        steps.extend(read(0x12, &[0x00]));
1346        steps.extend(read(0x12, &[0x50]));
1347        steps.extend(wrote(0x12, &[0x50]));
1348        steps.extend(read(0x13, &[0x02]));
1349        steps.extend(read(0x10, &[0x00]));
1350        steps.extend(wrote(0x0D, &[0x00]));
1351        steps.extend(read(0x00, b"hi"));
1352        steps.extend(read(0x19, &[0xF6, 0x30]));
1353        steps.extend(read(0x12, &[0x60]));
1354        steps.extend(wrote(0x12, &[0x60]));
1355        let mut radio = configured(steps);
1356        let mut buffer = [0u8; 255];
1357
1358        radio.listen().expect("listens");
1359        assert_eq!(radio.take_frame(&mut buffer), Ok(None));
1360        let frame = radio.take_frame(&mut buffer).expect("reads the frame");
1361        assert_eq!(
1362            frame,
1363            Some(Reception::Frame {
1364                len: 2,
1365                status: PacketStatus::from_bytes([0xF6, 0x30], Port::High),
1366            })
1367        );
1368        assert_eq!(&buffer[..2], b"hi");
1369        assert_eq!(radio.take_frame(&mut buffer), Ok(Some(Reception::Corrupt)));
1370
1371        let (spi, _, _) = radio.release();
1372        assert!(spi.done(), "{} steps left", spi.remaining());
1373    }
1374
1375    #[test]
1376    fn a_narrow_bandwidth_listens_one_bandwidth_above_the_carrier() {
1377        let narrow = RadioConfig::new(
1378            433_175_000,
1379            LinkSettings::new(9, 20_833),
1380            TxPower::for_output(PaOutput::PaBoost, 10),
1381        );
1382        let mut steps = calibration([0x6C, 0x4B, 0x33]);
1383        steps.extend(wrote(0x09, &[0xF8]));
1384        steps.extend(wrote(0x4D, &[0x84]));
1385        steps.extend(wrote(0x0B, &[0x2B]));
1386        steps.extend(wrote(0x1D, &[0x32]));
1387        steps.extend(wrote(0x1E, &[0x94]));
1388        steps.extend(wrote(0x26, &[0x0C]));
1389        steps.extend(wrote(0x20, &[0x00, 0x08]));
1390        steps.extend(read(0x31, &[0xC3]));
1391        steps.extend(wrote(0x31, &[0xC3]));
1392        steps.extend(wrote(0x37, &[0x0A]));
1393        steps.extend(wrote(0x36, &[0x03]));
1394        steps.extend(wrote(0x23, &[0xFF]));
1395        steps.extend(wrote(0x39, &[0x12]));
1396        steps.extend(wrote(0x01, &[0x89]));
1397        steps.extend(wrote(0x33, &[0x27]));
1398        steps.extend(wrote(0x3B, &[0x1D]));
1399        steps.extend(read(0x31, &[0xC3]));
1400        steps.extend(wrote(0x31, &[0x43]));
1401        steps.extend(wrote(0x30, &[0x00]));
1402        steps.extend(wrote(0x2F, &[0x44]));
1403        steps.extend(wrote(0x06, &frequency_bytes(433_175_000 + 20_830)));
1404        steps.extend(wrote(0x0F, &[0x00]));
1405        steps.extend(wrote(0x0D, &[0x00]));
1406        steps.extend(wrote(0x40, &[0x00]));
1407        steps.extend(wrote(0x12, &[0xFF]));
1408        steps.extend(wrote(0x01, &[0x8D]));
1409        let mut radio = radio(steps);
1410
1411        radio.configure(narrow).expect("configures");
1412        radio.listen().expect("listens");
1413
1414        let (spi, _, _) = radio.release();
1415        assert!(spi.done(), "{} steps left", spi.remaining());
1416    }
1417
1418    #[test]
1419    fn a_single_reception_counts_its_timeout_in_symbols_and_reports_it() {
1420        let mut steps = reception_setup();
1421        steps.extend(wrote(0x1E, &[0x74]));
1422        steps.extend(wrote(0x1F, &[0x62]));
1423        steps.extend(wrote(0x01, &[0x8E]));
1424        steps.extend(read(0x12, &[0x80]));
1425        steps.extend(wrote(0x12, &[0x80]));
1426        let mut radio = configured(steps);
1427        let mut buffer = [0u8; 64];
1428
1429        let outcome = radio.receive(&mut buffer, 100_000).expect("listens");
1430
1431        assert_eq!(outcome, Reception::Timeout);
1432        let (spi, _, _) = radio.release();
1433        assert!(spi.done(), "{} steps left", spi.remaining());
1434    }
1435
1436    #[test]
1437    fn a_frame_longer_than_the_buffer_is_refused() {
1438        let mut steps = reception_setup();
1439        steps.extend(wrote(0x01, &[0x8D]));
1440        steps.extend(read(0x12, &[0x40]));
1441        steps.extend(wrote(0x12, &[0x40]));
1442        steps.extend(read(0x13, &[0x10]));
1443        let mut radio = configured(steps);
1444        let mut buffer = [0u8; 8];
1445
1446        radio.listen().expect("listens");
1447        assert_eq!(
1448            radio.take_frame(&mut buffer),
1449            Err(RadioError::BufferTooSmall(16))
1450        );
1451    }
1452
1453    #[test]
1454    fn a_calibration_that_never_finishes_is_an_error() {
1455        let mut steps = Vec::new();
1456        steps.extend(wrote(0x01, &[0x88]));
1457        steps.extend(wrote(0x01, &[0x08]));
1458        steps.extend(wrote(0x01, &[0x09]));
1459        steps.extend(wrote(0x09, &[0x00]));
1460        steps.extend(wrote(0x06, &[0xD9, 0x06, 0x66]));
1461        steps.extend(read(0x3B, &[0x82]));
1462        steps.extend(wrote(0x3B, &[0x42]));
1463        for _ in 0..=CALIBRATION_LIMIT_US / CALIBRATION_POLL_US {
1464            steps.extend(read(0x3B, &[0x22]));
1465        }
1466        let mut radio = radio(steps);
1467
1468        assert_eq!(radio.configure(eu868()), Err(RadioError::Calibration));
1469    }
1470
1471    #[test]
1472    fn the_rssi_uses_the_offset_of_the_configured_port() {
1473        let mut radio = configured(read(0x1B, &[0x30]).to_vec());
1474        assert_eq!(radio.rssi(), Ok(Decibels::from_db(-109)));
1475    }
1476}