use std::convert::TryInto; use std::sync::mpsc; use std::thread; use std::thread::sleep; use std::time::{Duration, Instant}; use embedded_nrf24l01::{Configuration, CrcMode, DataRate, RxMode, NRF24L01}; use linux_embedded_hal::spidev::{SpiModeFlags, Spidev, SpidevOptions}; use linux_embedded_hal::sysfs_gpio::Direction; use linux_embedded_hal::sysfs_gpio::Error as GpioError; use linux_embedded_hal::Pin; use log::{error, info}; use protocol::{Location, Packet, Value}; use rand::rngs::ThreadRng; use rand::Rng; use crate::spi::EmbeddedHalSpidev; use crate::Error; use crate::{SensorData, SensorUpdate}; const DISPLAY_ID: u8 = 0x20; const DISPLAY_KEY: [u8; 16] = include!("../../../common/display_key.txt"); const WEATHER_STATION_0_ID: u8 = 0x30; const WEATHER_STATION_0_KEY: [u8; 16] = include!("../../../common/weather_station_0_key.txt"); pub fn start(updates: mpsc::Sender) { // TODO: Channels for sensor readings. thread::spawn(move || { // Whenever a error occurs, reinitialize the radio module shortly later. loop { match radio_thread(&updates) { Ok(()) => {} Err(e) => { error!("Radio error: {:?}", e); } } thread::sleep(Duration::from_secs(3)); } }); } fn radio_thread(updates: &mpsc::Sender) -> Result<(), Error> { let mut radio = Radio::init(updates)?; radio.run_loop()?; Ok(()) } struct Radio<'a> { rx: Option>>, updates: &'a mpsc::Sender, rng: ThreadRng, } impl<'a> Radio<'a> { fn init(updates: &'a mpsc::Sender) -> Result, Error> { info!("Initializing radio..."); // The NRF module is connected as follows: // - CE: PA1 // - CS: PG8 // - IRQ: PG9 // - MOSI: PC0 // - MISO: PC1 // - SCK: PC2 // Configure SPI. let mut spi = Spidev::open("/dev/spidev0.0").unwrap(); let options = SpidevOptions::new() .bits_per_word(8) .max_speed_hz(500_000) .mode(SpiModeFlags::SPI_MODE_0) .build(); spi.configure(&options).unwrap(); let spi = EmbeddedHalSpidev::from(spi); // Configure the GPIOs. let ce_nr = get_pin_number('A', 1); let ce = Pin::new(ce_nr); ce.export().unwrap(); ce.set_direction(Direction::Out).unwrap(); let cs_nr = get_pin_number('G', 8); let cs = Pin::new(cs_nr); cs.export().unwrap(); cs.set_direction(Direction::Out).unwrap(); let irq_nr = get_pin_number('G', 9); let irq = Pin::new(irq_nr); irq.export().unwrap(); irq.set_direction(Direction::In).unwrap(); // Initialize the radio module. let mut nrf24 = NRF24L01::new(ce, cs, spi)?; nrf24.set_frequency(0x32)?; nrf24.set_rf(DataRate::R2Mbps, 3)?; nrf24.set_crc(Some(CrcMode::OneByte))?; nrf24.set_auto_retransmit(250, 3)?; nrf24.set_pipes_rx_enable(&[true, false, false, false, false, false])?; // TODO enable pipe 0 once the base station receives messages nrf24 .set_rx_addr(0, &[0xB3, 0xB3, 0xB3, 0xB3, 0x00]) .unwrap(); nrf24.flush_rx().unwrap(); nrf24.flush_tx().unwrap(); nrf24.set_auto_ack(&[true; 6]).unwrap(); info!("auto ack: {:?}", nrf24.get_auto_ack().unwrap()); nrf24.set_pipes_rx_lengths(&[Some(32); 6]).unwrap(); info!("width: {}", nrf24.get_address_width().unwrap()); let rx = nrf24.rx().unwrap(); Ok(Radio { rx: Some(rx), updates, rng: rand::thread_rng(), }) } fn run_loop(&mut self) -> Result<(), Error> { let mut start = Instant::now(); // Receive data. info!("Starting to receive:"); loop { sleep(Duration::from_millis(1)); let rx = self.rx.as_mut().unwrap(); if let Some(pipe) = rx.can_read().unwrap() { let payload = rx.read().unwrap(); info!( "packet received on pipe {}: {:x?}, {}", pipe, payload.as_ref(), payload.len() ); if payload.len() != 32 { continue; } let mut payload: [u8; 32] = payload.as_ref().try_into().unwrap(); self.handle_packet(&mut payload)?; let end = Instant::now(); let elapsed = end.duration_since(start); info!("Debug: {:?}", elapsed); start = end; } } } fn handle_packet(&mut self, payload: &mut [u8]) -> Result<(), Error> { // Get the key of the device. let device_id = payload[0]; let key = match get_key(device_id) { Some(k) => k, None => { info!("packet from unknown device {:02x}", device_id); return Ok(()); } }; // Decode the packet. let packet = match Packet::decrypt_and_decode(key, payload) { None => { info!("invalid packet from device {:02x}", device_id); return Ok(()); } Some(p) => p, }; // TODO: For all packets except for salt requests, check whether the // salt was incremented to prevent replay attacks. // TODO: Also check whether the device bit in the salt is 0. info!("packet from {}: {:?}", device_id, packet); match packet { Packet::GetSalt => { self.send_salt(device_id)?; } Packet::Salt(_) => { error!("received Salt packet from device."); } Packet::Report(payload) => { let location = get_location(device_id); let count = payload.count; for i in 0..count { match payload.values[i as usize] { Value::Invalid => {} Value::Time(_) => { error!("device tried to report a time"); } Value::Temperature(temperature) => { let temperature = temperature as f32 / 10.0; info!("{:?} temperature: {} °C", location, temperature); } Value::Pressure(pressure) => { let pressure = pressure as f32 / 100.0; info!("{:?} pressure: {} HPa", location, pressure); } Value::Humidity(humidity) => { let humidity = humidity as f32 / 100.0; info!("{:?} humidity: {}%", location, humidity); } } } // TODO: Send values via MQTT /* updates .send(SensorUpdate { location: 0, data: vec![ SensorData::Temperature(temperature), SensorData::Pressure(pressure), SensorData::Humidity(humidity), ], }) .unwrap(); */ } Packet::GetValues(_payload) => { error!("GetValues not yet implemented."); // TODO } Packet::Values(_payload) => { error!("received Values packet from device."); } } Ok(()) } fn send_salt(&mut self, device_id: u8) -> Result<(), Error> { let salt = (self.rng.gen::() & !0xff & !(1 << 63)) | device_id as u64; let packet = Packet::Salt(salt); self.send_packet(device_id, packet) } fn send_packet(&mut self, device_id: u8, packet: Packet) -> Result<(), Error> { /*let salt = self.rng.gen::(); let mut tx = self .rx .take() .unwrap() .standby() .tx() .map_err(|(_, e)| Error::Radio(e))?; let mut encoded = [0u8; 32]; let key = get_key(device_id); if packet.encode_and_encrypt(key.unwrap(), salt, &mut encoded) { tx.set_tx_addr(&[0xB3, 0xB3, 0xB3, 0xB3, device_id]) .unwrap(); tx.set_rx_addr(0, &[0xB3, 0xB3, 0xB3, 0xB3, device_id]) .unwrap(); tx.send(&encoded).unwrap(); // TODO: Check whether the packet arrived. } else { info!("could not encode packet {:?}", packet); } self.rx = Some( tx.standby() .map_err(Error::Radio)? .rx() .map_err(|(_, e)| Error::Radio(e))?, );*/ Ok(()) } } fn get_location(device_id: u8) -> Location { match device_id { DISPLAY_ID => Location::Bedroom, WEATHER_STATION_0_ID => Location::Livingroom, _ => Location::Livingroom, } } fn get_key(device_id: u8) -> Option<&'static [u8]> { match device_id { DISPLAY_ID => Some(&DISPLAY_KEY), WEATHER_STATION_0_ID => Some(&WEATHER_STATION_0_KEY), _ => None, } } fn get_pin_number(c: char, n: u64) -> u64 { (c as u64 - 'A' as u64) * 32 + n }