#![allow(dead_code)]
use embedded_hal_mock::eh1::delay::CheckedDelay;
use embedded_hal_mock::eh1::delay::Transaction as DelayTransaction;
use embedded_hal_mock::eh1::digital::Edge as PinEdge;
use embedded_hal_mock::eh1::digital::Mock as PinMock;
use embedded_hal_mock::eh1::digital::State as PinState;
use embedded_hal_mock::eh1::digital::Transaction as PinTransaction;
use embedded_hal_mock::eh1::spi::Mock as SpiMock;
use embedded_hal_mock::eh1::spi::Transaction as SpiTransaction;
use futures::executor::block_on;
use rfm69_async::registers::{
ContinuousDagc, DataMode, DccCutoff, FifoMode, InterPacketRxDelay, LnaConfig, LnaGain, LnaImpedance, Modulation,
ModulationShaping, ModulationType, OpMode, PacketConfig, PacketDc, PacketFiltering, PacketFormat, RxBw, RxBwFsk,
};
use rfm69_async::{Address, Error, Flags, Packet, Rfm69};
const REG_FIFO: u8 = 0x00;
const REG_OPMODE: u8 = 0x01;
const REG_DATA_MODUL: u8 = 0x02;
const REG_BITRATE_MSB: u8 = 0x03;
const REG_FDEV_MSB: u8 = 0x05;
const REG_FRF_MSB: u8 = 0x07;
const REG_VERSION: u8 = 0x10;
const REG_LNA: u8 = 0x18;
const REG_RX_BW: u8 = 0x19;
const REG_RSSI_VALUE: u8 = 0x24;
const REG_DIO_MAPPING1: u8 = 0x25;
const REG_IRQ_FLAGS1: u8 = 0x27;
const REG_IRQ_FLAGS2: u8 = 0x28;
const REG_RSSI_THRESH: u8 = 0x29;
const REG_PREAMBLE_MSB: u8 = 0x2C;
const REG_SYNC_CONFIG: u8 = 0x2E;
const REG_SYNC_VALUE1: u8 = 0x2F;
const REG_PACKET_CONFIG_1: u8 = 0x37;
const REG_FIFO_THRESH: u8 = 0x3C;
const REG_PACKET_CONFIG_2: u8 = 0x3D;
const REG_TEST_DAGC: u8 = 0x6F;
const OPMODE_SLEEP: u8 = 0x00;
const OPMODE_STANDBY: u8 = 0x04;
const OPMODE_TX: u8 = 0x0c;
const OPMODE_RX: u8 = 0x10;
const IRQ1_MODE_READY: u8 = 0x80;
const IRQ2_FIFO_OVERRUN: u8 = 0x10;
const IRQ2_PACKET_SENT: u8 = 0x08;
const IRQ2_PAYLOAD_READY: u8 = 0x04;
const VERSION_OK: u8 = 0x24;
const DIO0_PACKET_SENT: u8 = 0x00; const DIO0_PAYLOAD_READY: u8 = 0x40;
fn rd(addr: u8, ret: u8) -> Vec<SpiTransaction<u8>> {
vec![
SpiTransaction::transaction_start(),
SpiTransaction::transfer(vec![addr & 0x7f], vec![0, ret]),
SpiTransaction::transaction_end(),
]
}
fn wr(addr: u8, byte: u8) -> Vec<SpiTransaction<u8>> {
vec![
SpiTransaction::transaction_start(),
SpiTransaction::write_vec(vec![addr | 0x80, byte]),
SpiTransaction::transaction_end(),
]
}
fn wr_n(addr: u8, bytes: &[u8]) -> Vec<SpiTransaction<u8>> {
vec![
SpiTransaction::transaction_start(),
SpiTransaction::write_vec(vec![addr | 0x80]),
SpiTransaction::write_vec(bytes.to_vec()),
SpiTransaction::transaction_end(),
]
}
fn rd_n(addr: u8, returns: &[u8]) -> Vec<SpiTransaction<u8>> {
vec![
SpiTransaction::transaction_start(),
SpiTransaction::write_vec(vec![addr & 0x7f]),
SpiTransaction::read_vec(returns.to_vec()),
SpiTransaction::transaction_end(),
]
}
#[test]
fn reset_completes_with_known_version() {
let mut spi_x = Vec::new();
spi_x.extend(rd(REG_VERSION, VERSION_OK));
spi_x.extend(wr(REG_OPMODE, OPMODE_SLEEP));
let reset_x = vec![PinTransaction::set(PinState::High), PinTransaction::set(PinState::Low)];
let delay_x = vec![
DelayTransaction::async_delay_ms(10),
DelayTransaction::async_delay_ms(10),
];
let mut spi = SpiMock::new(&spi_x);
let mut reset = PinMock::new(&reset_x);
let mut delay = CheckedDelay::new(&delay_x);
let dio0: Option<PinMock> = None;
let mut rfm = Rfm69::new(spi.clone(), reset.clone(), dio0, delay.clone());
block_on(rfm.reset()).unwrap();
spi.done();
reset.done();
delay.done();
}
#[test]
fn reset_returns_version_mismatch_on_unknown_chip() {
let spi_x = rd(REG_VERSION, 0x12);
let reset_x = vec![PinTransaction::set(PinState::High), PinTransaction::set(PinState::Low)];
let delay_x = vec![
DelayTransaction::async_delay_ms(10),
DelayTransaction::async_delay_ms(10),
];
let mut spi = SpiMock::new(&spi_x);
let mut reset = PinMock::new(&reset_x);
let mut delay = CheckedDelay::new(&delay_x);
let dio0: Option<PinMock> = None;
let mut rfm = Rfm69::new(spi.clone(), reset.clone(), dio0, delay.clone());
let res = block_on(rfm.reset());
assert!(matches!(res, Err(Error::VersionMismatch(0x12))));
spi.done();
reset.done();
delay.done();
}
#[test]
fn read_register_uses_addr_with_high_bit_clear() {
let spi_x = rd(REG_IRQ_FLAGS2, IRQ2_PAYLOAD_READY);
let mut spi = SpiMock::new(&spi_x);
let mut reset = PinMock::new(&[]);
let mut delay = CheckedDelay::new(&[]);
let dio0: Option<PinMock> = None;
let mut rfm = Rfm69::new(spi.clone(), reset.clone(), dio0, delay.clone());
assert!(block_on(rfm.is_packet_ready()).unwrap());
spi.done();
reset.done();
delay.done();
}
#[test]
fn frequency_writes_three_scaled_bytes() {
let spi_x = wr_n(REG_FRF_MSB, &[0x6C, 0x40, 0x00]);
let mut spi = SpiMock::new(&spi_x);
let mut reset = PinMock::new(&[]);
let mut delay = CheckedDelay::new(&[]);
let dio0: Option<PinMock> = None;
let mut rfm = Rfm69::new(spi.clone(), reset.clone(), dio0, delay.clone());
block_on(rfm.frequency(433_000_000)).unwrap();
spi.done();
reset.done();
delay.done();
}
#[test]
fn send_with_dio0_drives_full_protocol() {
let pkt = Packet::new(Address::Unicast(1), Address::Unicast(2), Flags::None, &[0xAB, 0xCD]).unwrap();
let fifo_bytes: &[u8] = &[5, 1, 2, 0, 0xAB, 0xCD];
let mut spi_x = Vec::new();
spi_x.extend(wr(REG_DIO_MAPPING1, DIO0_PACKET_SENT));
spi_x.extend(rd(REG_OPMODE, OPMODE_STANDBY));
spi_x.extend(wr(REG_OPMODE, OPMODE_STANDBY));
spi_x.extend(rd(REG_IRQ_FLAGS1, IRQ1_MODE_READY));
spi_x.extend(wr(REG_IRQ_FLAGS2, IRQ2_FIFO_OVERRUN));
spi_x.extend(wr_n(REG_FIFO, fifo_bytes));
spi_x.extend(wr(REG_OPMODE, OPMODE_TX));
spi_x.extend(wr(REG_OPMODE, OPMODE_STANDBY));
let dio0_x = vec![PinTransaction::wait_for_state(PinState::High)];
let delay_x = vec![
DelayTransaction::async_delay_ms(1), DelayTransaction::async_delay_ms(1), ];
let mut spi = SpiMock::new(&spi_x);
let mut reset = PinMock::new(&[]);
let mut dio0 = PinMock::new(&dio0_x);
let mut delay = CheckedDelay::new(&delay_x);
let mut rfm = Rfm69::new(spi.clone(), reset.clone(), Some(dio0.clone()), delay.clone());
block_on(rfm.send(&pkt)).unwrap();
spi.done();
reset.done();
dio0.done();
delay.done();
}
#[test]
fn recv_with_dio0_reads_fifo_and_rssi() {
let fifo_len: u8 = 5;
let fifo_payload = [2u8, 1, 0, 0xDE, 0xAD];
let rssi_raw = 100u8;
let mut spi_x = Vec::new();
spi_x.extend(wr(REG_DIO_MAPPING1, DIO0_PAYLOAD_READY));
spi_x.extend(wr(REG_OPMODE, OPMODE_RX));
spi_x.extend(wr(REG_OPMODE, OPMODE_STANDBY));
spi_x.extend(rd(REG_FIFO, fifo_len));
spi_x.extend(rd_n(REG_FIFO, &fifo_payload));
spi_x.extend(rd(REG_RSSI_VALUE, rssi_raw));
let dio0_x = vec![PinTransaction::wait_for_edge(PinEdge::Rising)];
let mut spi = SpiMock::new(&spi_x);
let mut reset = PinMock::new(&[]);
let mut dio0 = PinMock::new(&dio0_x);
let mut delay = CheckedDelay::new(&[]);
let mut rfm = Rfm69::new(spi.clone(), reset.clone(), Some(dio0.clone()), delay.clone());
let pkt = block_on(rfm.recv()).unwrap();
assert_eq!(pkt.src, Address::Unicast(2));
assert_eq!(pkt.dst, Address::Unicast(1));
assert!(matches!(pkt.flags, Flags::None));
assert_eq!(pkt.data.as_slice(), &[0xDE, 0xAD]);
assert_eq!(pkt.rssi, Some(-(i16::from(rssi_raw)) >> 1));
spi.done();
reset.done();
dio0.done();
delay.done();
}
type SetterFixture = (
Rfm69<SpiMock<u8>, PinMock, PinMock, CheckedDelay>,
SpiMock<u8>,
PinMock,
CheckedDelay,
);
fn rfm_with_spi(spi_x: &[SpiTransaction<u8>]) -> SetterFixture {
let spi = SpiMock::new(spi_x);
let reset = PinMock::new(&[]);
let delay = CheckedDelay::new(&[]);
let dio0: Option<PinMock> = None;
let rfm = Rfm69::new(spi.clone(), reset.clone(), dio0, delay.clone());
(rfm, spi, reset, delay)
}
#[test]
fn set_mode_writes_opmode_register() {
let spi_x = wr(REG_OPMODE, 0x04);
let (mut rfm, mut spi, mut reset, mut delay) = rfm_with_spi(&spi_x);
block_on(rfm.set_mode(OpMode::Standby)).unwrap();
spi.done();
reset.done();
delay.done();
}
#[test]
fn modulation_composes_three_fields() {
let spi_x = wr(REG_DATA_MODUL, 0x02);
let (mut rfm, mut spi, mut reset, mut delay) = rfm_with_spi(&spi_x);
block_on(rfm.modulation(Modulation {
data_mode: DataMode::Packet,
modulation_type: ModulationType::Fsk,
shaping: ModulationShaping::Shaping10,
}))
.unwrap();
spi.done();
reset.done();
delay.done();
}
#[test]
fn bit_rate_writes_two_scaled_bytes() {
let spi_x = wr_n(REG_BITRATE_MSB, &[0x01, 0x40]);
let (mut rfm, mut spi, mut reset, mut delay) = rfm_with_spi(&spi_x);
block_on(rfm.bit_rate(100_000)).unwrap();
spi.done();
reset.done();
delay.done();
}
#[test]
fn fdev_writes_two_scaled_bytes() {
let spi_x = wr_n(REG_FDEV_MSB, &[0x03, 0x33]);
let (mut rfm, mut spi, mut reset, mut delay) = rfm_with_spi(&spi_x);
block_on(rfm.fdev(50_000)).unwrap();
spi.done();
reset.done();
delay.done();
}
#[test]
fn rx_bw_composes_dcc_cutoff_and_filter() {
let spi_x = wr(REG_RX_BW, 0x42);
let (mut rfm, mut spi, mut reset, mut delay) = rfm_with_spi(&spi_x);
block_on(rfm.rx_bw(RxBw {
dcc_cutoff: DccCutoff::Percent4,
rx_bw: RxBwFsk::Khz125dot0,
}))
.unwrap();
spi.done();
reset.done();
delay.done();
}
#[test]
fn preamble_length_writes_two_be_bytes() {
let spi_x = wr_n(REG_PREAMBLE_MSB, &[0x00, 0x03]);
let (mut rfm, mut spi, mut reset, mut delay) = rfm_with_spi(&spi_x);
block_on(rfm.preamble_length(3)).unwrap();
spi.done();
reset.done();
delay.done();
}
#[test]
fn sync_normal_writes_config_byte_plus_payload() {
let mut spi_x = Vec::new();
spi_x.extend(wr(REG_SYNC_CONFIG, 0x88));
spi_x.extend(wr_n(REG_SYNC_VALUE1, &[0x2D, 0x42]));
let (mut rfm, mut spi, mut reset, mut delay) = rfm_with_spi(&spi_x);
block_on(rfm.sync(&[0x2D, 0x42])).unwrap();
spi.done();
reset.done();
delay.done();
}
#[test]
fn sync_empty_clears_config_bit() {
let mut spi_x = Vec::new();
spi_x.extend(rd(REG_SYNC_CONFIG, 0xFF));
spi_x.extend(wr(REG_SYNC_CONFIG, 0x7F));
let (mut rfm, mut spi, mut reset, mut delay) = rfm_with_spi(&spi_x);
block_on(rfm.sync(&[])).unwrap();
spi.done();
reset.done();
delay.done();
}
#[test]
fn sync_too_long_returns_error_without_spi() {
let (mut rfm, mut spi, mut reset, mut delay) = rfm_with_spi(&[]);
let res = block_on(rfm.sync(&[0u8; 9]));
assert!(matches!(res, Err(Error::SyncSize)));
spi.done();
reset.done();
delay.done();
}
#[test]
fn packet_writes_config1_then_updates_config2() {
let mut spi_x = Vec::new();
spi_x.extend(wr_n(REG_PACKET_CONFIG_1, &[0x90, 66]));
spi_x.extend(rd(REG_PACKET_CONFIG_2, 0x00));
spi_x.extend(wr(REG_PACKET_CONFIG_2, 0x12));
let (mut rfm, mut spi, mut reset, mut delay) = rfm_with_spi(&spi_x);
block_on(rfm.packet(PacketConfig {
format: PacketFormat::Variable(66),
dc: PacketDc::None,
filtering: PacketFiltering::None,
crc: true,
interpacket_rx_delay: InterPacketRxDelay::Delay2Bits,
auto_rx_restart: true,
}))
.unwrap();
spi.done();
reset.done();
delay.done();
}
#[test]
fn fifo_mode_not_empty_sets_high_bit() {
let mut spi_x = Vec::new();
spi_x.extend(rd(REG_FIFO_THRESH, 0x0F));
spi_x.extend(wr(REG_FIFO_THRESH, 0x8F));
let (mut rfm, mut spi, mut reset, mut delay) = rfm_with_spi(&spi_x);
block_on(rfm.fifo_mode(FifoMode::NotEmpty)).unwrap();
spi.done();
reset.done();
delay.done();
}
#[test]
fn fifo_mode_level_writes_masked_threshold() {
let spi_x = wr(REG_FIFO_THRESH, 0x7F);
let (mut rfm, mut spi, mut reset, mut delay) = rfm_with_spi(&spi_x);
block_on(rfm.fifo_mode(FifoMode::Level(0xFF))).unwrap();
spi.done();
reset.done();
delay.done();
}
#[test]
fn lna_preserves_reserved_bits() {
let mut spi_x = Vec::new();
spi_x.extend(rd(REG_LNA, 0xFF));
spi_x.extend(wr(REG_LNA, 0xF8));
let (mut rfm, mut spi, mut reset, mut delay) = rfm_with_spi(&spi_x);
block_on(rfm.lna(LnaConfig {
zin: LnaImpedance::Ohm200,
gain_select: LnaGain::AgcLoop,
}))
.unwrap();
spi.done();
reset.done();
delay.done();
}
#[test]
fn rssi_threshold_writes_value_directly() {
let spi_x = wr(REG_RSSI_THRESH, 0xDC);
let (mut rfm, mut spi, mut reset, mut delay) = rfm_with_spi(&spi_x);
block_on(rfm.rssi_threshold(220)).unwrap();
spi.done();
reset.done();
delay.done();
}
#[test]
fn continuous_dagc_writes_test_dagc_register() {
let spi_x = wr(REG_TEST_DAGC, 0x30);
let (mut rfm, mut spi, mut reset, mut delay) = rfm_with_spi(&spi_x);
block_on(rfm.continuous_dagc(ContinuousDagc::ImprovedMarginAfcLowBetaOn0)).unwrap();
spi.done();
reset.done();
delay.done();
}
#[test]
fn is_mode_ready_reads_irq_flags1_mode_ready_bit() {
let spi_x = rd(REG_IRQ_FLAGS1, IRQ1_MODE_READY);
let (mut rfm, mut spi, mut reset, mut delay) = rfm_with_spi(&spi_x);
assert!(block_on(rfm.is_mode_ready()).unwrap());
spi.done();
reset.done();
delay.done();
}
#[test]
fn is_packet_sent_reads_irq_flags2_packet_sent_bit() {
let spi_x = rd(REG_IRQ_FLAGS2, IRQ2_PACKET_SENT);
let (mut rfm, mut spi, mut reset, mut delay) = rfm_with_spi(&spi_x);
assert!(block_on(rfm.is_packet_sent()).unwrap());
spi.done();
reset.done();
delay.done();
}
#[test]
fn recv_without_dio0_polls_irq_flags2() {
let fifo_len: u8 = 3;
let fifo_payload = [3u8, 1, 0]; let rssi_raw = 60u8;
let mut spi_x = Vec::new();
spi_x.extend(wr(REG_OPMODE, OPMODE_RX));
spi_x.extend(rd(REG_IRQ_FLAGS2, IRQ2_PAYLOAD_READY));
spi_x.extend(wr(REG_OPMODE, OPMODE_STANDBY));
spi_x.extend(rd(REG_FIFO, fifo_len));
spi_x.extend(rd_n(REG_FIFO, &fifo_payload));
spi_x.extend(rd(REG_RSSI_VALUE, rssi_raw));
let mut spi = SpiMock::new(&spi_x);
let mut reset = PinMock::new(&[]);
let mut delay = CheckedDelay::new(&[]);
let dio0: Option<PinMock> = None;
let mut rfm = Rfm69::new(spi.clone(), reset.clone(), dio0, delay.clone());
let pkt = block_on(rfm.recv()).unwrap();
assert_eq!(pkt.src, Address::Unicast(3));
assert_eq!(pkt.dst, Address::Unicast(1));
assert_eq!(pkt.data.len(), 0);
assert_eq!(pkt.rssi, Some(-(i16::from(rssi_raw)) >> 1));
spi.done();
reset.done();
delay.done();
}