瀏覽代碼

display: Add some radio code to fetch a salt and send the first report.

Mathias Gottschlag 5 年之前
父節點
當前提交
5a62bd1299

+ 2
- 1
display/firmware/Cargo.toml 查看文件

@@ -21,9 +21,10 @@ mkl25z4-hal = { git = "https://github.com/mgottschlag/mkl25z4-hal" }
21 21
 embedded-hal = "0.2.3"
22 22
 nb = "0.1.2"
23 23
 bme280 = { git = "https://github.com/mgottschlag/bme280-rs", branch = "destroy" }
24
-embedded-nrf24l01 = { git = "https://github.com/astro/embedded-nrf24l01" }
24
+embedded-nrf24l01 = { git = "https://github.com/mgottschlag/embedded-nrf24l01", branch = "wip" }
25 25
 epd-waveshare = { git = "https://github.com/mgottschlag/epd-waveshare.git", branch = "streaming" }
26 26
 tinygfx = { git = "https://github.com/mgottschlag/tinygfx" }
27
+protocol = { path = "../../common/rust-protocol" }
27 28
 
28 29
 [dependencies.void]
29 30
 default-features = false

+ 9
- 1
display/firmware/src/main.rs 查看文件

@@ -4,6 +4,7 @@
4 4
 mod display;
5 5
 mod information;
6 6
 mod pins;
7
+mod radio;
7 8
 mod sensors;
8 9
 
9 10
 // TODO: Enable warnings again.
@@ -19,6 +20,7 @@ use panic_semihosting as _;
19 20
 use display::{Display, DisplayState};
20 21
 use information::{Information, TemperatureHumidity};
21 22
 use pins::{BME280Pins, DisplayBmeSpi};
23
+use radio::Radio;
22 24
 
23 25
 #[rtfm::app(device = mkl25z4_hal::mkl25z4, peripherals = true)]
24 26
 const APP: () = {
@@ -28,6 +30,7 @@ const APP: () = {
28 30
         display: Display,
29 31
         display_bme_spi: Option<DisplayBmeSpi>,
30 32
         bme_pins: Option<BME280Pins>,
33
+        radio: Option<Radio>,
31 34
         time: CopyableMonoTimer,
32 35
     }
33 36
 
@@ -58,6 +61,8 @@ const APP: () = {
58 61
 
59 62
         let display = Display::init(pins.display, &mut pins.display_bme_spi, time);
60 63
 
64
+        let radio = Radio::init(pins.nrf, time);
65
+
61 66
         // Fetch the first update.
62 67
         ctx.spawn.fetch_update().ok();
63 68
 
@@ -76,6 +81,7 @@ const APP: () = {
76 81
             display,
77 82
             display_bme_spi: Some(pins.display_bme_spi),
78 83
             bme_pins: Some(pins.bme),
84
+            radio: Some(radio),
79 85
             time,
80 86
         }
81 87
     }
@@ -94,7 +100,7 @@ const APP: () = {
94 100
         // TODO
95 101
     }
96 102
 
97
-    #[task(priority = 1, spawn = [update_display], resources = [info, bme_pins, display_bme_spi, &time])]
103
+    #[task(priority = 1, spawn = [update_display], resources = [info, bme_pins, display_bme_spi, &time, radio])]
98 104
     fn fetch_update(mut ctx: fetch_update::Context) {
99 105
         // Fetch local updates.
100 106
         let time = ctx.resources.time.clone();
@@ -109,6 +115,8 @@ const APP: () = {
109 115
 
110 116
         // Send local measurements to the base station. This needs to be done before fetching data
111 117
         // from the base station as the base station might return the data to us.
118
+        let mut radio = ctx.resources.radio.as_mut().unwrap().enable();
119
+        radio.send_sensor_values(values);
112 120
         // TODO
113 121
 
114 122
         // Fetch updates via radio.

+ 34
- 22
display/firmware/src/pins.rs 查看文件

@@ -38,6 +38,30 @@ pub type DisplayBmeSpi = Spi<
38 38
     gpioc::PC5<gpio::Alternate2>,
39 39
 >;
40 40
 
41
+pub type RadioIrq = gpiob::PB0<gpio::Input<gpio::PullUp>>;
42
+pub type RadioCs = gpiob::PB1<gpio::Output<gpio::PushPull>>;
43
+pub type RadioCe = gpiob::PB2<gpio::Output<gpio::PushPull>>;
44
+pub type RadioPwr = gpiob::PB3<gpio::Output<gpio::PushPull>>;
45
+pub type RadioSpi = Spi<
46
+    SPI1,
47
+    gpiob::PB16<gpio::Alternate2>,
48
+    gpiob::PB17<gpio::Alternate2>,
49
+    gpiod::PD5<gpio::Alternate2>,
50
+>;
51
+
52
+pub struct RadioPins {
53
+    /// NRF IRQ line (active low).
54
+    pub irq: RadioIrq,
55
+    /// NRF chip select (active low).
56
+    pub cs: RadioCs,
57
+    /// NRF chip enable.
58
+    pub ce: RadioCe,
59
+    /// NRF supply voltage enable (active low).
60
+    pub pwr: RadioPwr,
61
+    /// SPI for the NRF module.
62
+    pub spi: RadioSpi,
63
+}
64
+
41 65
 pub struct Pins {
42 66
     /// 5V supply for the LED (active high).
43 67
     pub enable_5v: gpioa::PA1<gpio::Output<gpio::PushPull>>,
@@ -75,21 +99,7 @@ pub struct Pins {
75 99
     /// Buzzer (active high).
76 100
     pub buzzer: gpioc::PC3<gpio::Output<gpio::PushPull>>,
77 101
 
78
-    /// NRF IRQ line (active low).
79
-    pub nrf_irq: gpiob::PB0<gpio::Input<gpio::PullUp>>,
80
-    /// NRF chip select (active low).
81
-    pub nrf_cs: gpiob::PB1<gpio::Output<gpio::PushPull>>,
82
-    /// NRF chip enable.
83
-    pub nrf_ce: gpiob::PB2<gpio::Output<gpio::PushPull>>,
84
-    /// NRF supply voltage enable (active low).
85
-    pub nrf_pwr: gpiob::PB3<gpio::Output<gpio::PushPull>>,
86
-    /// SPI for the NRF module.
87
-    pub nrf_spi: Spi<
88
-        SPI1,
89
-        gpiob::PB16<gpio::Alternate2>,
90
-        gpiob::PB17<gpio::Alternate2>,
91
-        gpiod::PD5<gpio::Alternate2>,
92
-    >,
102
+    pub nrf: RadioPins,
93 103
 
94 104
     pub display: DisplayPins,
95 105
 
@@ -158,11 +168,13 @@ impl Pins {
158 168
             als_en: gpioe.pe20.into_push_pull_output(&mut gpioe.pddr),
159 169
             als: gpioe.pe21.into_floating_input(&mut gpioe.pddr),
160 170
             buzzer: gpioc.pc3.into_push_pull_output(&mut gpioc.pddr),
161
-            nrf_irq: gpiob.pb0.into_pull_up_input(&mut gpiob.pddr),
162
-            nrf_cs: gpiob.pb1.into_push_pull_output(&mut gpiob.pddr),
163
-            nrf_ce: gpiob.pb2.into_push_pull_output(&mut gpiob.pddr),
164
-            nrf_pwr: gpiob.pb3.into_push_pull_output(&mut gpiob.pddr),
165
-            nrf_spi: spi1,
171
+            nrf: RadioPins {
172
+                irq: gpiob.pb0.into_pull_up_input(&mut gpiob.pddr),
173
+                cs: gpiob.pb1.into_push_pull_output(&mut gpiob.pddr),
174
+                ce: gpiob.pb2.into_push_pull_output(&mut gpiob.pddr),
175
+                pwr: gpiob.pb3.into_push_pull_output(&mut gpiob.pddr),
176
+                spi: spi1,
177
+            },
166 178
 
167 179
             display: DisplayPins {
168 180
                 busy: gpioc.pc4.into_pull_up_input(&mut gpioc.pddr),
@@ -184,8 +196,8 @@ impl Pins {
184 196
         pins.enable_5v.set_low().ok();
185 197
         pins.als_en.set_low().ok();
186 198
         pins.buzzer.set_low().ok();
187
-        pins.nrf_cs.set_high().ok();
188
-        pins.nrf_pwr.set_high().ok();
199
+        pins.nrf.cs.set_high().ok();
200
+        pins.nrf.pwr.set_high().ok();
189 201
         pins.display.cs.set_high().ok();
190 202
         pins.display.pwr.set_high().ok();
191 203
         pins.bme.pwr.set_high().ok();

+ 175
- 0
display/firmware/src/radio.rs 查看文件

@@ -0,0 +1,175 @@
1
+use core::convert::TryInto;
2
+
3
+use embedded_hal::blocking::delay::DelayMs;
4
+use embedded_hal::digital::v2::OutputPin;
5
+use embedded_nrf24l01::{Configuration, CrcMode, DataRate, RxMode, StandbyMode, NRF24L01};
6
+use mkl25z4_hal::time::CopyableMonoTimer;
7
+use mkl25z4_hal::NoError;
8
+use protocol::{Packet, Report, Value};
9
+
10
+use super::pins::{RadioCe, RadioCs, RadioIrq, RadioPins, RadioPwr, RadioSpi};
11
+use super::sensors::BME280Data;
12
+
13
+const DEVICE_ID: u8 = 0x20;
14
+const KEY: [u8; 16] = include!("../../../common/display_key.txt");
15
+
16
+pub struct Radio {
17
+    nrf24: Option<NRF24L01<NoError, RadioCe, RadioCs, RadioSpi>>,
18
+    pwr: RadioPwr,
19
+    irq: RadioIrq,
20
+    salt: Option<u64>,
21
+    time: CopyableMonoTimer,
22
+}
23
+
24
+impl Radio {
25
+    pub fn init(mut pins: RadioPins, time: CopyableMonoTimer) -> Radio {
26
+        pins.pwr.set_low().ok();
27
+        let nrf24 = NRF24L01::new(pins.ce, pins.cs, pins.spi).unwrap();
28
+        let nrf24 = nrf24.power_down().unwrap();
29
+        pins.pwr.set_high().ok();
30
+        Radio {
31
+            nrf24: Some(nrf24),
32
+            pwr: pins.pwr,
33
+            irq: pins.irq,
34
+            salt: None,
35
+            time,
36
+        }
37
+    }
38
+
39
+    pub fn enable<'a>(&'a mut self) -> EnabledRadio<'a> {
40
+        // Enable supply voltage.
41
+        self.pwr.set_low().ok();
42
+        self.time.delay_ms(10u8);
43
+
44
+        // Configure the device.
45
+        let nrf24 = self.nrf24.take().unwrap();
46
+        let mut nrf24 = StandbyMode::power_up(nrf24).unwrap();
47
+        self.time.delay_ms(2u8);
48
+        nrf24.set_frequency(0x32).unwrap();
49
+        nrf24.set_rf(DataRate::R2Mbps, 3).unwrap();
50
+        nrf24.set_crc(Some(CrcMode::OneByte)).unwrap();
51
+        nrf24.set_auto_retransmit(250, 1).unwrap();
52
+        nrf24.set_auto_ack(&[true; 6]).unwrap();
53
+        nrf24
54
+            .set_pipes_rx_enable(&[true, true, false, false, false, false])
55
+            .unwrap();
56
+        nrf24.set_tx_addr(&[0xB3, 0xB3, 0xB3, 0xB3, 0x00]).unwrap();
57
+        nrf24
58
+            .set_rx_addr(0, &[0xB3, 0xB3, 0xB3, 0xB3, 0x00])
59
+            .unwrap();
60
+        nrf24
61
+            .set_rx_addr(1, &[0xB3, 0xB3, 0xB3, 0xB3, 0x20])
62
+            .unwrap();
63
+        nrf24.flush_rx().unwrap();
64
+        nrf24.flush_tx().unwrap();
65
+        nrf24.set_pipes_rx_lengths(&[Some(32); 6]).unwrap();
66
+
67
+        EnabledRadio {
68
+            radio: self,
69
+            nrf24: Some(nrf24),
70
+        }
71
+    }
72
+}
73
+
74
+pub struct EnabledRadio<'a> {
75
+    radio: &'a mut Radio,
76
+    nrf24: Option<StandbyMode<NRF24L01<NoError, RadioCe, RadioCs, RadioSpi>>>,
77
+}
78
+
79
+impl<'a> EnabledRadio<'a> {
80
+    // TODO: Use generic "SensorValues" instead of BME280Data, which includes ambient light
81
+    // readings
82
+    pub fn send_sensor_values(&mut self, values: BME280Data) -> bool {
83
+        if self.radio.salt.is_none() {
84
+            if !self.fetch_salt() {
85
+                return false;
86
+            }
87
+        }
88
+
89
+        let mut tx = self.nrf24.take().unwrap().tx().unwrap();
90
+        while !tx.can_send().unwrap() {}
91
+        let mut payload = [0u8; 32];
92
+        let mut report = Report {
93
+            count: 3,
94
+            values: [Value::Invalid; 8],
95
+        };
96
+        report.values[0] = Value::Temperature(values.temperature as i16);
97
+        report.values[1] = Value::Pressure(values.pressure);
98
+        report.values[2] = Value::Humidity(values.humidity as u16);
99
+        let packet = Packet::Report(report);
100
+        packet.encode_and_encrypt(&KEY, self.radio.salt.unwrap(), &mut payload);
101
+        tx.send(&payload).unwrap();
102
+        // TODO: Check whether the packet arrived.
103
+
104
+        self.nrf24 = Some(tx.standby().unwrap());
105
+        true
106
+    }
107
+
108
+    fn fetch_salt(&mut self) -> bool {
109
+        // Request a device ID.
110
+        let mut tx = self.nrf24.take().unwrap().tx().unwrap();
111
+        while !tx.can_send().unwrap() {}
112
+        let mut payload = [0u8; 32];
113
+        let packet = Packet::GetSalt;
114
+        packet.encode_and_encrypt(&KEY, DEVICE_ID as u64, &mut payload);
115
+        tx.send(&payload).unwrap();
116
+        // TODO: Check whether the packet arrived.
117
+
118
+        // Wait for the initial salt.
119
+        let mut salt_received = false;
120
+        let mut rx = tx.standby().unwrap().rx().unwrap();
121
+        loop {
122
+            let packet = match self.receive_packet(&mut rx) {
123
+                None => break,
124
+                Some(p) => p,
125
+            };
126
+
127
+            match packet {
128
+                Packet::Salt(salt) => {
129
+                    self.radio.salt = Some((salt & 0x7fffffffffffff00) | DEVICE_ID as u64);
130
+                    salt_received = true;
131
+                    break;
132
+                }
133
+                _ => continue,
134
+            }
135
+        }
136
+
137
+        self.nrf24 = Some(rx.standby());
138
+        salt_received
139
+    }
140
+
141
+    fn receive_packet(
142
+        &mut self,
143
+        rx: &mut RxMode<NRF24L01<NoError, RadioCe, RadioCs, RadioSpi>>,
144
+    ) -> Option<Packet> {
145
+        // TODO: Timeout.
146
+        loop {
147
+            if let Some(_pipe) = rx.can_read().unwrap() {
148
+                // Receive a packet.
149
+                // TODO: Check pipe?
150
+                let payload = rx.read().unwrap();
151
+                if payload.len() != 32 {
152
+                    continue;
153
+                }
154
+                let mut payload: [u8; 32] = payload[0..32].try_into().unwrap();
155
+
156
+                let packet = match Packet::decrypt_and_decode(&KEY, &mut payload) {
157
+                    Ok(p) => p,
158
+                    Err(_) => continue,
159
+                };
160
+
161
+                return Some(packet);
162
+            }
163
+        }
164
+    }
165
+}
166
+
167
+impl<'a> Drop for EnabledRadio<'a> {
168
+    fn drop(&mut self) {
169
+        let nrf24 = self.nrf24.take().unwrap();
170
+        // Disable the chip and remove the supply voltage.
171
+        let nrf24 = nrf24.power_down().unwrap();
172
+        self.radio.pwr.set_high().ok();
173
+        self.radio.nrf24 = Some(nrf24);
174
+    }
175
+}

+ 1
- 0
display/firmware/src/sensors.rs 查看文件

@@ -34,6 +34,7 @@ pub fn bme280(
34 34
     )
35 35
 }
36 36
 
37
+#[derive(Clone, Copy)]
37 38
 pub struct BME280Data {
38 39
     pub temperature: i32,
39 40
     pub humidity: u32,

Loading…
取消
儲存