Kaynağa Gözat

display: First part of fetching values from the base station.

Mathias Gottschlag 5 yıl önce
ebeveyn
işleme
5fdb6c2b02

+ 19
- 0
common/rust-protocol/src/lib.rs Dosyayı Görüntüle

@@ -84,6 +84,25 @@ impl Packet {
84 84
             }
85 85
         }
86 86
     }
87
+
88
+    pub fn get_type(&self) -> PacketType {
89
+        match self {
90
+            Self::GetSalt => PacketType::GetSalt,
91
+            Self::Salt(_) => PacketType::Salt,
92
+            Self::Report(_) => PacketType::Report,
93
+            Self::GetValues(_) => PacketType::GetValues,
94
+            Self::Values(_) => PacketType::Values,
95
+        }
96
+    }
97
+}
98
+
99
+#[derive(Debug, Clone, PartialEq, Eq)]
100
+pub enum PacketType {
101
+    GetSalt,
102
+    Salt,
103
+    Report,
104
+    GetValues,
105
+    Values,
87 106
 }
88 107
 
89 108
 #[derive(Debug, Clone, PartialEq, Eq)]

+ 4
- 0
display/firmware/src/information.rs Dosyayı Görüntüle

@@ -1,5 +1,7 @@
1 1
 //! Sensor information or information fetched from the internet.
2 2
 
3
+use super::sensors::BME280Data;
4
+
3 5
 pub struct Information {
4 6
     pub time: u32,
5 7
     pub valid: bool,
@@ -9,6 +11,8 @@ pub struct Information {
9 11
     pub bathroom: Option<TemperatureHumidity>,
10 12
 
11 13
     pub pressure: Option<u32>,
14
+
15
+    pub local: Option<BME280Data>,
12 16
 }
13 17
 
14 18
 #[derive(Copy, Clone)]

+ 16
- 1
display/firmware/src/main.rs Dosyayı Görüntüle

@@ -21,6 +21,7 @@ use mkl25z4_hal::time::{CopyableMonoTimer, NonCopyableMonoTimer, U32Ext};
21 21
 use mkl25z4_hal::timer::Timer;
22 22
 use mkl25z4_hal::timer::TimerInterrupt;
23 23
 use panic_semihosting as _;
24
+use protocol::{Location, ValueType};
24 25
 
25 26
 use display::{Display, DisplayState};
26 27
 use information::{Information, TemperatureHumidity};
@@ -84,6 +85,7 @@ const APP: () = {
84 85
                 outside: None,
85 86
                 bathroom: None,
86 87
                 pressure: None,
88
+                local: None,
87 89
             },
88 90
             display_state: DisplayState::new(),
89 91
             display,
@@ -127,16 +129,29 @@ const APP: () = {
127 129
         // from the base station as the base station might return the data to us.
128 130
         let mut radio = ctx.resources.radio.as_mut().unwrap().enable();
129 131
         radio.send_sensor_values(values);
130
-        // TODO
131 132
 
132 133
         // Fetch updates via radio.
134
+        let mut livingroom = [None; 2];
135
+        radio.request_values(
136
+            Location::Livingroom,
137
+            &[ValueType::Temperature, ValueType::Humidity],
138
+            &mut livingroom,
139
+        );
140
+        let mut balcony = [None; 2];
141
+        radio.request_values(
142
+            Location::Balcony,
143
+            &[ValueType::Temperature, ValueType::Humidity],
144
+            &mut balcony,
145
+        );
133 146
         // TODO
147
+
134 148
         ctx.resources.info.lock(|info| {
135 149
             info.inside = Some(TemperatureHumidity {
136 150
                 temperature: values.temperature,
137 151
                 humidity: values.humidity,
138 152
             });
139 153
             info.pressure = Some(values.pressure);
154
+            info.local = Some(values);
140 155
         });
141 156
 
142 157
         // If the update failed, simply increment the time and place a hint that

+ 123
- 40
display/firmware/src/radio.rs Dosyayı Görüntüle

@@ -1,11 +1,12 @@
1 1
 use core::convert::TryInto;
2 2
 
3 3
 use embedded_hal::blocking::delay::DelayMs;
4
+use embedded_hal::digital::v2::InputPin;
4 5
 use embedded_hal::digital::v2::OutputPin;
5
-use embedded_nrf24l01::{Configuration, CrcMode, DataRate, RxMode, StandbyMode, NRF24L01};
6
-use mkl25z4_hal::time::CopyableMonoTimer;
6
+use embedded_nrf24l01::{Configuration, CrcMode, DataRate, StandbyMode, NRF24L01};
7
+use mkl25z4_hal::time::{CopyableMonoTimer, Instant, MilliSeconds, MonoTimer, U32Ext};
7 8
 use mkl25z4_hal::NoError;
8
-use protocol::{Packet, Report, Value};
9
+use protocol::{GetValues, Location, Packet, PacketType, Report, Value, ValueType};
9 10
 
10 11
 use super::pins::{RadioCe, RadioCs, RadioIrq, RadioPins, RadioPwr, RadioSpi};
11 12
 use super::sensors::BME280Data;
@@ -80,6 +81,62 @@ impl<'a> EnabledRadio<'a> {
80 81
     // TODO: Use generic "SensorValues" instead of BME280Data, which includes ambient light
81 82
     // readings
82 83
     pub fn send_sensor_values(&mut self, values: BME280Data) -> bool {
84
+        let mut report = Report {
85
+            count: 3,
86
+            values: [Value::Invalid; 8],
87
+        };
88
+        report.values[0] = Value::Temperature(values.temperature as i16);
89
+        report.values[1] = Value::Pressure(values.pressure);
90
+        report.values[2] = Value::Humidity(values.humidity as u16);
91
+        self.send_packet(Packet::Report(report));
92
+
93
+        true
94
+    }
95
+
96
+    pub fn request_values(
97
+        &mut self,
98
+        location: Location,
99
+        value_types: &[ValueType],
100
+        values: &mut [Option<i64>],
101
+    ) -> bool {
102
+        // We always clear the value array so that the caller can ignore the return value.
103
+        for i in 0..values.len() {
104
+            values[i] = None;
105
+        }
106
+
107
+        // Request the values.
108
+        let mut get_values = GetValues {
109
+            count: value_types.len() as u8,
110
+            location,
111
+            types_: [ValueType::Time; 8],
112
+        };
113
+        get_values.types_[0..value_types.len()].copy_from_slice(value_types);
114
+        self.send_packet(Packet::GetValues(get_values));
115
+
116
+        // Receive the response.
117
+        let response = match self.receive_packet(PacketType::Values, 100.ms()) {
118
+            None => return false,
119
+            Some(Packet::Values(values)) => values,
120
+            _ => return false,
121
+        };
122
+
123
+        for i in 0..response.count {
124
+            let (type_, value) = match response.values[i as usize].split() {
125
+                Some(value) => value,
126
+                None => return false,
127
+            };
128
+
129
+            for j in 0..values.len() {
130
+                if value_types[j] == type_ {
131
+                    values[j] = Some(value);
132
+                }
133
+            }
134
+        }
135
+
136
+        true
137
+    }
138
+
139
+    fn send_packet(&mut self, packet: Packet) -> bool {
83 140
         if self.radio.salt.is_none() {
84 141
             if !self.fetch_salt() {
85 142
                 return false;
@@ -89,14 +146,6 @@ impl<'a> EnabledRadio<'a> {
89 146
         let mut tx = self.nrf24.take().unwrap().tx().unwrap();
90 147
         while !tx.can_send().unwrap() {}
91 148
         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 149
         packet.encode_and_encrypt(&KEY, self.radio.salt.unwrap(), &mut payload);
101 150
         tx.send(&payload).unwrap();
102 151
         // TODO: Check whether the packet arrived.
@@ -106,47 +155,52 @@ impl<'a> EnabledRadio<'a> {
106 155
     }
107 156
 
108 157
     fn fetch_salt(&mut self) -> bool {
109
-        // Request a device ID.
158
+        self.send_packet_raw(Packet::GetSalt);
159
+
160
+        // Wait for the initial salt.
161
+        match self.receive_packet(PacketType::Salt, 100.ms()) {
162
+            None => false,
163
+            Some(Packet::Salt(salt)) => {
164
+                self.radio.salt = Some((salt & 0x7fffffffffffff00) | DEVICE_ID as u64);
165
+                true
166
+            }
167
+            _ => false,
168
+        }
169
+    }
170
+
171
+    fn send_packet_raw(&mut self, packet: Packet) -> bool {
110 172
         let mut tx = self.nrf24.take().unwrap().tx().unwrap();
111 173
         while !tx.can_send().unwrap() {}
112 174
         let mut payload = [0u8; 32];
113
-        let packet = Packet::GetSalt;
114
-        packet.encode_and_encrypt(&KEY, DEVICE_ID as u64, &mut payload);
175
+        packet.encode_and_encrypt(
176
+            &KEY,
177
+            self.radio.salt.unwrap_or(DEVICE_ID as u64),
178
+            &mut payload,
179
+        );
115 180
         tx.send(&payload).unwrap();
116 181
         // TODO: Check whether the packet arrived.
117 182
 
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
183
+        self.nrf24 = Some(tx.standby().unwrap());
184
+        true
139 185
     }
140 186
 
141
-    fn receive_packet(
187
+    fn receive_packet<T: Into<MilliSeconds>>(
142 188
         &mut self,
143
-        rx: &mut RxMode<NRF24L01<NoError, RadioCe, RadioCs, RadioSpi>>,
189
+        type_: PacketType,
190
+        timeout: T,
144 191
     ) -> Option<Packet> {
145
-        // TODO: Timeout.
146
-        loop {
192
+        // TODO: Perhaps the radio should stay longer in RX mode?
193
+        let mut rx = self.nrf24.take().unwrap().rx().unwrap();
194
+        let timeout = Timeout::new(&self.radio.time, timeout);
195
+        while !timeout.expired() {
196
+            // Reduce bus activity as it can negatively affect the radio.
197
+            // TODO: Wait for proper interrupt to reduce power consumption?
198
+            if self.radio.irq.is_high().unwrap() {
199
+                continue;
200
+            }
201
+
147 202
             if let Some(_pipe) = rx.can_read().unwrap() {
148 203
                 // Receive a packet.
149
-                // TODO: Check pipe?
150 204
                 let payload = rx.read().unwrap();
151 205
                 if payload.len() != 32 {
152 206
                     continue;
@@ -157,10 +211,15 @@ impl<'a> EnabledRadio<'a> {
157 211
                     Ok(p) => p,
158 212
                     Err(_) => continue,
159 213
                 };
214
+                if packet.get_type() != type_ {
215
+                    continue;
216
+                }
160 217
 
161 218
                 return Some(packet);
162 219
             }
163 220
         }
221
+        self.nrf24 = Some(rx.standby());
222
+        None
164 223
     }
165 224
 }
166 225
 
@@ -173,3 +232,27 @@ impl<'a> Drop for EnabledRadio<'a> {
173 232
         self.radio.nrf24 = Some(nrf24);
174 233
     }
175 234
 }
235
+
236
+struct Timeout<'a, Timer> {
237
+    timer: &'a Timer,
238
+    start: Instant,
239
+    timeout: u32,
240
+}
241
+
242
+impl<'a, Timer> Timeout<'a, Timer>
243
+where
244
+    Timer: MonoTimer,
245
+{
246
+    fn new<T: Into<MilliSeconds>>(timer: &'a Timer, timeout: T) -> Self {
247
+        let ticks = (timer.frequency().0 as u64 * timeout.into().0 as u64 / 1000) as u32;
248
+        Self {
249
+            timer,
250
+            start: timer.now(),
251
+            timeout: ticks,
252
+        }
253
+    }
254
+
255
+    fn expired(&self) -> bool {
256
+        self.timer.now().elapsed(self.start) > self.timeout
257
+    }
258
+}

Loading…
İptal
Kaydet