|
|
@@ -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
|
+}
|