// Taken from rfm95_rust_pi, which is // SPDX-License-Identifier: MIT use std; use sysfs_gpio::{Direction, Edge, Pin}; use spidev::{Spidev, SpidevTransfer, SpidevOptions, SPI_MODE_0}; use std::io; use std::thread; use std::time::Duration; use std::io::{Read, Write, ErrorKind}; const RST_BCM_PIN : u64 = 22; const DIO_BCM_PIN : u64 = 25; const CS_BCM_PIN : u64 = 8; pub const MAX_MTU: usize = 251; const SPI_MAX_SPEED_HZ : u32 = 5_000_000; #[derive(Copy, Clone)] pub enum LoraRegister { RegFifo = 0x00, RegOpMode = 0x01, // reserved : 0x02 - 0x05 RegFrfMsb = 0x06, RegFrfMid = 0x07, RegFrfLsb = 0x08, RegPaConfig = 0x09, RegPaRamp = 0x0A, RegOcp = 0x0B, RegLna = 0x0C, RegFifoAddrPtr = 0x0D, RegFifoTxBaseAddr = 0x0E, RegFifoRxBaseAddr = 0x0F, RegFifoRxCurrentAddr = 0x10, RegIrqFlagsMask = 0x11, RegIrqFlags = 0x12, RegRxNbBytes = 0x13, RegRxHeaderCntValueMsb = 0x14, RegRxHeaderCntValueLsb = 0x15, RegRxPacketCntValueMsb = 0x16, RegRxPacketCntValueLsb = 0x17, RegModemStat = 0x18, RegPktSnrValue = 0x19, RegPktRssiValue = 0x1A, RegRssiValue = 0x1B, RegHopChannel = 0x1C, RegModemConfig1 = 0x1D, RegModemConfig2 = 0x1E, RegSymbTimeoutLsb = 0x1F, RegPreambleMsb = 0x20, RegPreambleLsb = 0x21, RegPayloadLength = 0x22, RegMaxPayloadLength = 0x23, RegHopPeriod = 0x24, RegFifoRxByteAddr = 0x25, RegModemConfig3 = 0x26, // reserved : 0x27-0x3F RegDioMapping1 = 0x40, RegDioMapping2 = 0x41, RegVersion = 0x42, // unused 0x44 RegTcxo = 0x4B, RegPaDac = 0x4D, RegFormerTemp = 0x5B, RegAgcRef = 0x61, RegAgcThresh1 = 0x62, RegAgcThresh2 = 0x63, RegAgcThresh3 = 0x64, } impl LoraRegister { pub fn as_u8(&self) -> u8 { *self as u8 } } #[derive(Copy, Clone)] pub enum FskOokRegister { RegFifo = 0x00, RegOpMode = 0x01, RegBitRateMsb = 0x02, RegBitRateLsb = 0x03, RegFdevMsb = 0x04, RegFdevLsb = 0x05, RegFrfMsb = 0x06, RegFrfMid = 0x07, RegFrfLsb = 0x08, RegPaConfig = 0x09, RegPaRamp = 0x0A, RegOcp = 0x0B, RegLna = 0x0C, RegRxConfig = 0x0D, RegRssiConfig = 0x0E, RegRssiCollision = 0x0F, RegRssiThresh = 0x10, RegRssiValue = 0x11, RegRxBw = 0x12, RegAfcBw = 0x13, RegOokPeak = 0x14, RegOokFix = 0x15, RegOokAvg = 0x16, RegAfcFei = 0x1A, RegAfcMsb = 0x1B, RegAfcLsb = 0x1C, RegFeiMsb = 0x1D, RegFeiLsb = 0x1E, RegPreambleDetect = 0x1F, RegRxTimeout1 = 0x20, RegRxTimeout2 = 0x21, RegRxTimeout3 = 0x22, RegRxDelay = 0x23, RegOsc = 0x24, RegPreambleMsb = 0x25, RegPreambleLsb = 0x26, RegSyncConfig = 0x27, RegSyncValue1 = 0x28, RegSyncValue2 = 0x29, RegSyncValue3 = 0x2A, RegSyncValue4 = 0x2B, RegSyncValue5 = 0x2C, RegSyncValue6 = 0x2D, RegSyncValue7 = 0x2E, RegSyncValue8 = 0x2F, RegPacketconfig1 = 0x30, RegPacketConfig2 = 0x31, RegPayloadLength = 0x32, RegNodeAdrs = 0x33, RegBroadcastAdrs = 0x34, RegFifoThresh = 0x35, RegSeqConfig1 = 0x36, RegSeqConfig2 = 0x37, RegTimerResol = 0x38, RegTimer1Coef = 0x39, RegTimer2Coef = 0x3A, RegImageCal = 0x3B, RegTemp = 0x3C, RegLowBat = 0x3D, RegIrqFlags1 = 0x3E, RegIrqFlags2 = 0x3F, RegDioMapping1 = 0x40, RegDioMapping2 = 0x41, RegVersion = 0x42, RegPllHop = 0x44, RegTxco = 0x4B, RegPaDac = 0x4D, RegFormerTemp = 0x5B, RegBitRateFrac = 0x5D, RegAgcRef = 0x61, RegAgcThresh1 = 0x62, RegAgcThresh2 = 0x63, RegAgcThresh3 = 0x64, } impl FskOokRegister { pub fn as_u8(&self) -> u8 { *self as u8 } } #[derive(Copy, Clone)] pub enum Channel { Ch10 = 0xD84CCC, Ch11 = 0xD86000, Ch12 = 0xD87333, Ch13 = 0xD88666, Ch14 = 0xD89999, Ch15 = 0xD8ACCC, Ch16 = 0xD8C000, Ch17 = 0xD90000, } impl Channel { pub fn msb(&self) -> u8 { let chan_as_u8 = *self as u32; ((chan_as_u8 >> 16) & 0x000000FF) as u8 } pub fn mid(&self) -> u8 { let chan_as_u8 = *self as u32; ((chan_as_u8 >> 8) & 0x000000FF) as u8 } pub fn lsb(&self) -> u8 { let chan_as_u8 = *self as u32; (chan_as_u8 & 0x000000FF) as u8 } } #[derive(Copy, Clone)] pub enum Bandwidth { Bw125 = 0x07, Bw250 = 0x08, Bw500 = 0x09, } impl Bandwidth { pub fn as_u8(&self) -> u8 { *self as u8 } } #[derive(Copy, Clone)] pub enum CodingRate { Cr5 = 0x01, Cr6 = 0x02, Cr7 = 0x03, Cr8 = 0x04, } impl CodingRate { pub fn as_u8(&self) -> u8 { *self as u8 } } #[derive(Copy, Clone)] pub enum SpreadingFactor { Sf7 = 0x07, Sf8 = 0x08, Sf9 = 0x09, Sf10 = 0x0A, Sf11 = 0x0B, Sf12 = 0x0C, } impl SpreadingFactor { pub fn as_u8(&self) -> u8 { *self as u8 } } #[derive(Copy, Clone)] pub enum LoraMode { Sleep = 0x80, Standby = 0x81, Tx = 0x83, Rx = 0x85, RxSingle = 0x86, CadDetection = 0x87, StandbyOokFsk = 0xC1, } impl LoraMode { pub fn as_u8(&self) -> u8 { *self as u8 } } #[derive(Copy, Clone)] pub enum DioFunction { RxDone = 0x00, TxDone = 0x01, } impl DioFunction { pub fn as_u8(&self) -> u8 { *self as u8 } } trait ToFlag { fn flag_enabled(&self, f : IrqFlagMasks) -> bool; } #[derive(Copy, Clone)] enum IrqFlagMasks { CadDetected = 0x01, FhssChangeChannel = 0x02, CadDone = 0x04, TxDone = 0x08, ValidHeader = 0x10, PayloadCrcError = 0x20, RxDone = 0x40, RxTimeout = 0x80, } impl IrqFlagMasks { pub fn as_u8(&self) -> u8 { *self as u8 } } impl ToFlag for u8 { fn flag_enabled(&self, f : IrqFlagMasks) -> bool { self & f.as_u8() != 0 } } pub enum RF95EventType { None, DataReceived(Vec), DataSent, ErrorPinConfig, ErrorTimedOut, ErrorWrongCrc, ErrorCommBus, } pub struct RF95 { ch : Channel, bw : Bandwidth, cr : CodingRate, sf : SpreadingFactor, crc_check_enabled : bool, implicit_header_enabled : bool, pwr_db : u8, rst_pin : Pin, cs_pin : Pin, dio_pin : Pin, spi : Spidev, } impl RF95 { pub fn new(bw : Bandwidth, cr : CodingRate, sf : SpreadingFactor) -> io::Result { let tmp_rst_pin = Pin::new(RST_BCM_PIN); tmp_rst_pin.export().map_err(|e| io::Error::new(ErrorKind::Other, format!("Cannot export pin {}", e)))?; tmp_rst_pin.set_direction(Direction::Low) .map_err(|e| io::Error::new(ErrorKind::Other, format!("Cannot set direction for pin {}", e)))?; thread::sleep(Duration::from_millis(2)); tmp_rst_pin.set_value(1).map_err(|e| io::Error::new(ErrorKind::Other, format!("Cannot set value for pin {}", e)))?; thread::sleep(Duration::from_millis(5)); let mut spi = Spidev::open("/dev/spidev0.0")?; let spi_options = SpidevOptions::new() .bits_per_word(8) .max_speed_hz(SPI_MAX_SPEED_HZ) .mode(SPI_MODE_0) .build(); spi.configure(&spi_options)?; let dio_pin = Pin::new(DIO_BCM_PIN); dio_pin.export() .map_err(|e| io::Error::new(ErrorKind::Other, format!("Cannot export pin {}", e)))?; dio_pin.set_direction(Direction::In) .map_err(|e| io::Error::new(ErrorKind::Other, format!("Cannot set direction for pin {}", e)))?; dio_pin.set_edge(Edge::RisingEdge) .map_err(|e| io::Error::new(ErrorKind::Other, format!("Cannot set edge for pin {}", e)))?; let cs_pin = Pin::new(CS_BCM_PIN); cs_pin.export() .map_err(|e| io::Error::new(ErrorKind::Other, format!("Cannot export pin {}", e)))?; let mut rf = RF95 { ch : Channel::Ch10, bw, cr, sf, crc_check_enabled : false, implicit_header_enabled : false, pwr_db : 0, rst_pin : tmp_rst_pin, cs_pin : cs_pin, dio_pin : dio_pin, spi : spi, }; let ver = rf.get_version()?; if ver == 0 || ver == 0xff { return Err(io::Error::new(ErrorKind::Other, format!("Invalid chip version {}", ver))); } // We configure so that we can use the entire 256 byte FIFO for either // receive or transmit, but not both at the same time. rf.write_register(LoraRegister::RegFifoRxBaseAddr, 0)?; rf.write_register(LoraRegister::RegFifoTxBaseAddr, 0)?; Ok(rf) } pub fn get_version(&mut self) -> io::Result { let device_version = self.read_register(LoraRegister::RegVersion)?; Ok(device_version) } pub fn set_main_parameters(&mut self, ch : Channel, bw : Bandwidth, cr : CodingRate, sf : SpreadingFactor) -> io::Result<()> { self.set_channel(ch)?; self.set_bandwidth(bw)?; self.set_coding_rate(cr)?; self.set_spreading_factor(sf) } pub fn set_channel(&mut self, ch : Channel) -> io::Result<()> { self.set_mode(LoraMode::Sleep)?; self.ch = ch; self.write_register(LoraRegister::RegFrfMsb, ch.msb())?; self.write_register(LoraRegister::RegFrfMid, ch.mid())?; self.write_register(LoraRegister::RegFrfLsb, ch.lsb()) } pub fn set_spreading_factor(&mut self, sf : SpreadingFactor) -> io::Result<()> { self.set_mode(LoraMode::Sleep)?; self.sf = sf; let mut tmp = self.read_register(LoraRegister::RegModemConfig2)?; tmp &= 0x0F; tmp |= sf.as_u8() << 4; self.write_register(LoraRegister::RegModemConfig2, tmp) } pub fn set_bandwidth(&mut self, bw : Bandwidth) -> io::Result<()> { self.set_mode(LoraMode::Sleep)?; self.bw = bw; let mut tmp = self.read_register(LoraRegister::RegModemConfig1)?; tmp &= 0x0F; tmp |= bw.as_u8() << 4; self.write_register(LoraRegister::RegModemConfig1, tmp)?; Ok(()) } pub fn set_coding_rate(&mut self, cr : CodingRate) -> io::Result<()> { self.set_mode(LoraMode::Sleep)?; self.cr = cr; let mut tmp = self.read_register(LoraRegister::RegModemConfig1)?; tmp &= 0xF1; tmp |= cr.as_u8() << 1; self.write_register(LoraRegister::RegModemConfig1, tmp) } pub fn enable_crc_check(&mut self, en : bool) -> io::Result<()> { let mut tmp = self.read_register(LoraRegister::RegModemConfig2)?; if en { tmp |= 1 << 2; } else { tmp &= !(1 << 2); } self.crc_check_enabled = en; self.write_register(LoraRegister::RegModemConfig2, tmp) } pub fn enable_implicit_header(&mut self, en : bool) -> io::Result<()> { let mut tmp = self.read_register(LoraRegister::RegModemConfig1)?; if en { tmp |= 0x01; } else { tmp &= !0x01; } self.implicit_header_enabled = en; self.write_register(LoraRegister::RegModemConfig1, tmp) } pub fn set_output_power(&mut self, pwr : u8) -> io::Result<()> { let mut pwr = pwr; if pwr > 20 { pwr = 20; } self.pwr_db = pwr; let out = (pwr - 2) & 0x0F; let mut tmp = self.read_register(LoraRegister::RegPaConfig)?; tmp |= 0x80; tmp &= 0xF0; tmp |= out & 0x0F; self.write_register(LoraRegister::RegPaConfig, tmp) } pub fn set_mode(&mut self, m : LoraMode) -> io::Result<()> { self.write_register(LoraRegister::RegOpMode, m.as_u8()) } fn check_pending_events(&mut self) -> io::Result> { // TODO atomic read and clear of bits! let mut regv = self.read_register(LoraRegister::RegIrqFlags)?; if regv.flag_enabled(IrqFlagMasks::CadDetected) { regv = regv & !IrqFlagMasks::CadDetected.as_u8(); } if regv.flag_enabled(IrqFlagMasks::CadDone) { regv = regv & !IrqFlagMasks::CadDone.as_u8(); } if regv.flag_enabled(IrqFlagMasks::FhssChangeChannel) { regv = regv & !IrqFlagMasks::FhssChangeChannel.as_u8(); } if regv.flag_enabled(IrqFlagMasks::PayloadCrcError) { regv = regv & !IrqFlagMasks::PayloadCrcError.as_u8(); self.write_register(LoraRegister::RegIrqFlags, regv)?; return Ok(Some(RF95EventType::ErrorWrongCrc)); } if regv.flag_enabled(IrqFlagMasks::RxDone) { let len = self.read_register(LoraRegister::RegRxNbBytes)?; let curr_addr = self.read_register(LoraRegister::RegFifoRxCurrentAddr)?; self.write_register(LoraRegister::RegFifoAddrPtr, curr_addr)?; let mut buf = Vec::new(); self.read_buffer(LoraRegister::RegFifo, &mut buf, len)?; regv = regv & !IrqFlagMasks::RxDone.as_u8(); self.write_register(LoraRegister::RegIrqFlags, regv)?; return Ok(Some(RF95EventType::DataReceived(buf))); } if regv.flag_enabled(IrqFlagMasks::TxDone) { regv = regv & !IrqFlagMasks::TxDone.as_u8(); self.write_register(LoraRegister::RegIrqFlags, regv)?; return Ok(Some(RF95EventType::DataSent)); } if regv.flag_enabled(IrqFlagMasks::RxTimeout) { regv = regv & !IrqFlagMasks::RxTimeout.as_u8(); self.write_register(LoraRegister::RegIrqFlags, regv)?; return Ok(Some(RF95EventType::ErrorTimedOut)); } if regv.flag_enabled(IrqFlagMasks::ValidHeader) { regv = regv & !IrqFlagMasks::ValidHeader.as_u8(); } self.write_register(LoraRegister::RegIrqFlags, regv)?; Ok(None) } pub fn wait_for_event(&mut self, timeout : std::time::Duration) -> io::Result { let start = std::time::Instant::now(); let mut poller = self.dio_pin.get_poller() .map_err(|e| io::Error::new(ErrorKind::Other, format!("Cannot get poller for pin {}", e)))?; loop { match poller.poll(10).unwrap() { Some(_) => { if let Some(ev) = self.check_pending_events()? { return Ok(ev); } } None => (), }; if timeout.as_secs() > 0 && std::time::Instant::now().duration_since(start) > timeout { break; } } Ok(RF95EventType::ErrorTimedOut) } pub fn get_snr(&mut self) -> io::Result { Ok((self.read_register(LoraRegister::RegPktSnrValue)? as i16) / 4) } pub fn get_packet_rssi(&mut self) -> io::Result { Ok((self.read_register(LoraRegister::RegRssiValue)? as i16) - 137) } pub fn get_rssi(&mut self) -> io::Result { Ok((self.read_register(LoraRegister::RegRssiValue)? as i16) - 137) } pub fn reset(&mut self) -> io::Result<()> { self.rst_pin.set_direction(Direction::Low).map_err(|e| io::Error::new(ErrorKind::Other, format!("Cannot set direction for pin {}", e)))?; thread::sleep(Duration::from_millis(2)); self.rst_pin.set_value(1).map_err(|e| io::Error::new(ErrorKind::Other, format!("Cannot set value for pin {}", e)))?; thread::sleep(Duration::from_millis(5)); Ok(()) } pub fn set_dio_mapping(&mut self, df : DioFunction) -> io::Result<()> { let prev_mode = self.read_register(LoraRegister::RegOpMode)?; self.set_mode(LoraMode::StandbyOokFsk)?; let mut tmp = self.read_register(LoraRegister::RegDioMapping1)?; tmp &= 0x3F; if df.as_u8() == DioFunction::TxDone.as_u8() { tmp |= 0x40; } self.write_register(LoraRegister::RegDioMapping1, tmp)?; self.write_register(LoraRegister::RegOpMode, prev_mode) } fn write_register(&mut self, reg : LoraRegister, data : u8) -> io::Result<()> { self.cs_pin.set_direction(Direction::Low) .map_err(|e| io::Error::new(ErrorKind::Other, format!("Cannot set direction for pin {}", e)))?; self.spi.write(&[reg.as_u8(), data])?; self.cs_pin.set_value(1) .map_err(|e| io::Error::new(ErrorKind::Other, format!("Cannot set value for pin {}", e))) } pub fn send(&mut self, buffer : Vec) -> io::Result<()> { if buffer.len() > MAX_MTU { return Err(io::Error::new(ErrorKind::Other, "Message larger than MTU")); } // TODO waitpacketsent self.set_mode(LoraMode::Standby)?; // TODO waitCAD self.write_register(LoraRegister::RegFifoAddrPtr, 0)?; // TODO RadioHead header? let len = buffer.len() as u8; self.write_buffer(LoraRegister::RegFifo, buffer)?; self.write_register(LoraRegister::RegPayloadLength, len)?; self.set_mode(LoraMode::Tx)?; // Wait for TX to complete loop { // TODO protect IRQ clearing against concurrent access let mut regv = self.read_register(LoraRegister::RegIrqFlags)?; if regv.flag_enabled(IrqFlagMasks::TxDone) { regv = regv & !IrqFlagMasks::TxDone.as_u8(); self.write_register(LoraRegister::RegIrqFlags, regv)?; break; } thread::sleep(Duration::from_millis(2)); } Ok(()) } fn write_buffer(&mut self, reg : LoraRegister, buffer : Vec) -> io::Result<()> { self.cs_pin.set_direction(Direction::Low) .map_err(|e| io::Error::new(ErrorKind::Other, format!("Cannot set direction for pin {}", e)))?; let mut v2 = buffer; v2.insert(0, reg.as_u8()); self.spi.write(&v2)?; self.cs_pin.set_value(1) .map_err(|e| io::Error::new(ErrorKind::Other, format!("Cannot set value for pin {}", e))) } fn read_register(&mut self, reg : LoraRegister) -> io::Result { self.cs_pin.set_direction(Direction::Low) .map_err(|e| io::Error::new(ErrorKind::Other, format!("Cannot set direction for pin {}", e)))?; let mut rx_buf = [0; 2]; let tx_buf = vec![reg as u8, 0]; { let mut transfer = SpidevTransfer::read_write(&tx_buf, &mut rx_buf); self.spi.transfer(&mut transfer).unwrap(); } self.cs_pin.set_value(1) .map_err(|e| io::Error::new(ErrorKind::Other, format!("Cannot set value for pin {}", e)))?; Ok(rx_buf[1]) } fn read_buffer(&mut self, reg : LoraRegister, buffer : &mut Vec, length : u8) -> io::Result<()> { self.cs_pin.set_direction(Direction::Low) .map_err(|e| io::Error::new(ErrorKind::Other, format!("Cannot set direction for pin {}", e)))?; let mut tmp : Vec = Vec::with_capacity(length as usize); self.spi.write(&[reg.as_u8()])?; self.spi.read(tmp.as_mut_slice())?; buffer.clear(); buffer.write(&tmp)?; self.cs_pin.set_value(1) .map_err(|e| io::Error::new(ErrorKind::Other, format!("Cannot set value for pin {}", e)))?; Ok(()) } }