Przeglądaj źródła

base-station: Add support for the nrf24l01-stick USB adapter and the RPi 4.

Mathias Gottschlag 4 lat temu
rodzic
commit
6b434499a1

+ 277
- 4
base-station/software/Cargo.lock Wyświetl plik

@@ -1,5 +1,26 @@
1 1
 # This file is automatically @generated by Cargo.
2 2
 # It is not intended for manual editing.
3
+[[package]]
4
+name = "CoreFoundation-sys"
5
+version = "0.1.4"
6
+source = "registry+https://github.com/rust-lang/crates.io-index"
7
+checksum = "d0e9889e6db118d49d88d84728d0e964d973a5680befb5f85f55141beea5c20b"
8
+dependencies = [
9
+ "libc",
10
+ "mach 0.1.2",
11
+]
12
+
13
+[[package]]
14
+name = "IOKit-sys"
15
+version = "0.1.5"
16
+source = "registry+https://github.com/rust-lang/crates.io-index"
17
+checksum = "99696c398cbaf669d2368076bdb3d627fb0ce51a26899d7c61228c5c0af3bf4a"
18
+dependencies = [
19
+ "CoreFoundation-sys",
20
+ "libc",
21
+ "mach 0.1.2",
22
+]
23
+
3 24
 [[package]]
4 25
 name = "aho-corasick"
5 26
 version = "0.7.10"
@@ -15,6 +36,18 @@ version = "0.4.6"
15 36
 source = "registry+https://github.com/rust-lang/crates.io-index"
16 37
 checksum = "b585a98a234c46fc563103e9278c9391fde1f4e6850334da895d27edb9580f62"
17 38
 
39
+[[package]]
40
+name = "as-slice"
41
+version = "0.1.4"
42
+source = "registry+https://github.com/rust-lang/crates.io-index"
43
+checksum = "bb4d1c23475b74e3672afa8c2be22040b8b7783ad9b461021144ed10a46bb0e6"
44
+dependencies = [
45
+ "generic-array 0.12.3",
46
+ "generic-array 0.13.2",
47
+ "generic-array 0.14.4",
48
+ "stable_deref_trait",
49
+]
50
+
18 51
 [[package]]
19 52
 name = "atty"
20 53
 version = "0.2.14"
@@ -47,6 +80,7 @@ dependencies = [
47 80
  "log",
48 81
  "mqtt-protocol",
49 82
  "nb",
83
+ "nrf24l01-stick-driver",
50 84
  "protocol",
51 85
  "rand",
52 86
  "sysfs_gpio",
@@ -266,6 +300,34 @@ dependencies = [
266 300
  "slab",
267 301
 ]
268 302
 
303
+[[package]]
304
+name = "generic-array"
305
+version = "0.12.3"
306
+source = "registry+https://github.com/rust-lang/crates.io-index"
307
+checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
308
+dependencies = [
309
+ "typenum",
310
+]
311
+
312
+[[package]]
313
+name = "generic-array"
314
+version = "0.13.2"
315
+source = "registry+https://github.com/rust-lang/crates.io-index"
316
+checksum = "0ed1e761351b56f54eb9dcd0cfaca9fd0daecf93918e1cfc01c8a3d26ee7adcd"
317
+dependencies = [
318
+ "typenum",
319
+]
320
+
321
+[[package]]
322
+name = "generic-array"
323
+version = "0.14.4"
324
+source = "registry+https://github.com/rust-lang/crates.io-index"
325
+checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817"
326
+dependencies = [
327
+ "typenum",
328
+ "version_check",
329
+]
330
+
269 331
 [[package]]
270 332
 name = "getrandom"
271 333
 version = "0.1.14"
@@ -286,10 +348,32 @@ dependencies = [
286 348
  "futures",
287 349
  "libc",
288 350
  "mio",
289
- "nix",
351
+ "nix 0.14.1",
290 352
  "tokio",
291 353
 ]
292 354
 
355
+[[package]]
356
+name = "hash32"
357
+version = "0.1.1"
358
+source = "registry+https://github.com/rust-lang/crates.io-index"
359
+checksum = "d4041af86e63ac4298ce40e5cca669066e75b6f1aa3390fe2561ffa5e1d9f4cc"
360
+dependencies = [
361
+ "byteorder",
362
+]
363
+
364
+[[package]]
365
+name = "heapless"
366
+version = "0.5.6"
367
+source = "registry+https://github.com/rust-lang/crates.io-index"
368
+checksum = "74911a68a1658cfcfb61bc0ccfbd536e3b6e906f8c2f7883ee50157e3e2184f1"
369
+dependencies = [
370
+ "as-slice",
371
+ "generic-array 0.13.2",
372
+ "hash32",
373
+ "serde",
374
+ "stable_deref_trait",
375
+]
376
+
293 377
 [[package]]
294 378
 name = "hermit-abi"
295 379
 version = "0.1.11"
@@ -317,7 +401,7 @@ dependencies = [
317 401
  "bitflags",
318 402
  "byteorder",
319 403
  "libc",
320
- "nix",
404
+ "nix 0.14.1",
321 405
 ]
322 406
 
323 407
 [[package]]
@@ -360,6 +444,26 @@ version = "0.2.70"
360 444
 source = "registry+https://github.com/rust-lang/crates.io-index"
361 445
 checksum = "3baa92041a6fec78c687fa0cc2b3fae8884f743d672cf551bed1d6dac6988d0f"
362 446
 
447
+[[package]]
448
+name = "libudev"
449
+version = "0.2.0"
450
+source = "registry+https://github.com/rust-lang/crates.io-index"
451
+checksum = "ea626d3bdf40a1c5aee3bcd4f40826970cae8d80a8fec934c82a63840094dcfe"
452
+dependencies = [
453
+ "libc",
454
+ "libudev-sys",
455
+]
456
+
457
+[[package]]
458
+name = "libudev-sys"
459
+version = "0.1.4"
460
+source = "registry+https://github.com/rust-lang/crates.io-index"
461
+checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324"
462
+dependencies = [
463
+ "libc",
464
+ "pkg-config",
465
+]
466
+
363 467
 [[package]]
364 468
 name = "linux-embedded-hal"
365 469
 version = "0.3.0"
@@ -385,6 +489,24 @@ dependencies = [
385 489
  "cfg-if",
386 490
 ]
387 491
 
492
+[[package]]
493
+name = "mach"
494
+version = "0.1.2"
495
+source = "registry+https://github.com/rust-lang/crates.io-index"
496
+checksum = "2fd13ee2dd61cc82833ba05ade5a30bb3d63f7ced605ef827063c63078302de9"
497
+dependencies = [
498
+ "libc",
499
+]
500
+
501
+[[package]]
502
+name = "mach"
503
+version = "0.2.3"
504
+source = "registry+https://github.com/rust-lang/crates.io-index"
505
+checksum = "86dd2487cdfea56def77b88438a2c915fb45113c5319bfe7e14306ca4cd0b0e1"
506
+dependencies = [
507
+ "libc",
508
+]
509
+
388 510
 [[package]]
389 511
 name = "memchr"
390 512
 version = "2.3.3"
@@ -422,6 +544,19 @@ dependencies = [
422 544
  "winapi 0.3.8",
423 545
 ]
424 546
 
547
+[[package]]
548
+name = "mio-serial"
549
+version = "3.3.1"
550
+source = "registry+https://github.com/rust-lang/crates.io-index"
551
+checksum = "a0f0c240805cd4c65aa97da44ad99df8bad922cb999d0e451b51fdec13a661fd"
552
+dependencies = [
553
+ "mio",
554
+ "mio-named-pipes",
555
+ "nix 0.17.0",
556
+ "serialport",
557
+ "winapi 0.3.8",
558
+]
559
+
425 560
 [[package]]
426 561
 name = "mio-uds"
427 562
 version = "0.6.7"
@@ -497,6 +632,42 @@ dependencies = [
497 632
  "void",
498 633
 ]
499 634
 
635
+[[package]]
636
+name = "nix"
637
+version = "0.17.0"
638
+source = "registry+https://github.com/rust-lang/crates.io-index"
639
+checksum = "50e4785f2c3b7589a0d0c1dd60285e1188adac4006e8abd6dd578e1567027363"
640
+dependencies = [
641
+ "bitflags",
642
+ "cc",
643
+ "cfg-if",
644
+ "libc",
645
+ "void",
646
+]
647
+
648
+[[package]]
649
+name = "nrf24l01-stick-driver"
650
+version = "0.1.0"
651
+source = "git+https://github.com/mgottschlag/nrf24l01-stick#fe8085d89823ed30fa54715f4d891609b0b239d7"
652
+dependencies = [
653
+ "bytes",
654
+ "futures",
655
+ "nrf24l01-stick-protocol",
656
+ "thiserror",
657
+ "tokio",
658
+ "tokio-serial",
659
+ "tokio-util",
660
+]
661
+
662
+[[package]]
663
+name = "nrf24l01-stick-protocol"
664
+version = "0.1.0"
665
+source = "git+https://github.com/mgottschlag/nrf24l01-stick#fe8085d89823ed30fa54715f4d891609b0b239d7"
666
+dependencies = [
667
+ "postcard",
668
+ "serde",
669
+]
670
+
500 671
 [[package]]
501 672
 name = "num-integer"
502 673
 version = "0.1.42"
@@ -564,6 +735,29 @@ version = "0.1.0"
564 735
 source = "registry+https://github.com/rust-lang/crates.io-index"
565 736
 checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
566 737
 
738
+[[package]]
739
+name = "pkg-config"
740
+version = "0.3.19"
741
+source = "registry+https://github.com/rust-lang/crates.io-index"
742
+checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
743
+
744
+[[package]]
745
+name = "postcard"
746
+version = "0.5.1"
747
+source = "registry+https://github.com/rust-lang/crates.io-index"
748
+checksum = "b3e3f5c2e9a91383c6594ec68aa2dfdfe19a3c86f34b088ba7203f2483d2682f"
749
+dependencies = [
750
+ "heapless",
751
+ "postcard-cobs",
752
+ "serde",
753
+]
754
+
755
+[[package]]
756
+name = "postcard-cobs"
757
+version = "0.1.5-pre"
758
+source = "registry+https://github.com/rust-lang/crates.io-index"
759
+checksum = "7c68cb38ed13fd7bc9dd5db8f165b7c8d9c1a315104083a2b10f11354c2af97f"
760
+
567 761
 [[package]]
568 762
 name = "ppv-lite86"
569 763
 version = "0.2.6"
@@ -703,6 +897,26 @@ version = "0.7.0"
703 897
 source = "registry+https://github.com/rust-lang/crates.io-index"
704 898
 checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
705 899
 
900
+[[package]]
901
+name = "serde"
902
+version = "1.0.113"
903
+source = "registry+https://github.com/rust-lang/crates.io-index"
904
+checksum = "6135c78461981c79497158ef777264c51d9d0f4f3fc3a4d22b915900e42dac6a"
905
+dependencies = [
906
+ "serde_derive",
907
+]
908
+
909
+[[package]]
910
+name = "serde_derive"
911
+version = "1.0.113"
912
+source = "registry+https://github.com/rust-lang/crates.io-index"
913
+checksum = "93c5eaa17d0954cb481cdcfffe9d84fcfa7a1a9f2349271e678677be4c26ae31"
914
+dependencies = [
915
+ "proc-macro2",
916
+ "quote",
917
+ "syn",
918
+]
919
+
706 920
 [[package]]
707 921
 name = "serial-core"
708 922
 version = "0.4.0"
@@ -724,6 +938,23 @@ dependencies = [
724 938
  "termios",
725 939
 ]
726 940
 
941
+[[package]]
942
+name = "serialport"
943
+version = "3.3.0"
944
+source = "registry+https://github.com/rust-lang/crates.io-index"
945
+checksum = "5b8d3ecaf58010bedccae17be55d4ed6f2ecde5646fc48ce8c66ea2d35a1419c"
946
+dependencies = [
947
+ "CoreFoundation-sys",
948
+ "IOKit-sys",
949
+ "bitflags",
950
+ "cfg-if",
951
+ "libudev",
952
+ "mach 0.2.3",
953
+ "nix 0.14.1",
954
+ "regex",
955
+ "winapi 0.3.8",
956
+]
957
+
727 958
 [[package]]
728 959
 name = "signal-hook-registry"
729 960
 version = "1.2.0"
@@ -760,9 +991,15 @@ checksum = "aa5aa93a87c20f4efdf494917ef8fb475522601256ba6bb00ad1e6101f779fe9"
760 991
 dependencies = [
761 992
  "bitflags",
762 993
  "libc",
763
- "nix",
994
+ "nix 0.14.1",
764 995
 ]
765 996
 
997
+[[package]]
998
+name = "stable_deref_trait"
999
+version = "1.2.0"
1000
+source = "registry+https://github.com/rust-lang/crates.io-index"
1001
+checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
1002
+
766 1003
 [[package]]
767 1004
 name = "syn"
768 1005
 version = "1.0.17"
@@ -781,7 +1018,7 @@ source = "git+https://github.com/mgottschlag/rust-sysfs-gpio?branch=new-futures#
781 1018
 dependencies = [
782 1019
  "futures",
783 1020
  "mio",
784
- "nix",
1021
+ "nix 0.14.1",
785 1022
  "pin-project-lite",
786 1023
  "tokio",
787 1024
 ]
@@ -878,12 +1115,48 @@ dependencies = [
878 1115
  "syn",
879 1116
 ]
880 1117
 
1118
+[[package]]
1119
+name = "tokio-serial"
1120
+version = "4.3.3"
1121
+source = "registry+https://github.com/rust-lang/crates.io-index"
1122
+checksum = "0ad6436458f961b1345f55f2a771325f8dbdd9b5908285059d34a31e86779e38"
1123
+dependencies = [
1124
+ "mio-serial",
1125
+ "tokio",
1126
+]
1127
+
1128
+[[package]]
1129
+name = "tokio-util"
1130
+version = "0.3.1"
1131
+source = "registry+https://github.com/rust-lang/crates.io-index"
1132
+checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499"
1133
+dependencies = [
1134
+ "bytes",
1135
+ "futures-core",
1136
+ "futures-sink",
1137
+ "log",
1138
+ "pin-project-lite",
1139
+ "tokio",
1140
+]
1141
+
1142
+[[package]]
1143
+name = "typenum"
1144
+version = "1.12.0"
1145
+source = "registry+https://github.com/rust-lang/crates.io-index"
1146
+checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33"
1147
+
881 1148
 [[package]]
882 1149
 name = "unicode-xid"
883 1150
 version = "0.2.0"
884 1151
 source = "registry+https://github.com/rust-lang/crates.io-index"
885 1152
 checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
886 1153
 
1154
+[[package]]
1155
+name = "version_check"
1156
+version = "0.9.2"
1157
+source = "registry+https://github.com/rust-lang/crates.io-index"
1158
+checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
1159
+
887 1160
 [[package]]
888 1161
 name = "void"
889 1162
 version = "1.0.2"

+ 9
- 3
base-station/software/Cargo.toml Wyświetl plik

@@ -4,7 +4,12 @@ version = "0.1.0"
4 4
 authors = ["Mathias Gottschlag <mgottschlag@gmail.com>"]
5 5
 edition = "2018"
6 6
 
7
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7
+
8
+[features]
9
+default = []
10
+rpi4 = [ "sysfs_gpio", "gpio-cdev" ]
11
+nanopineo = [ "sysfs_gpio", "gpio-cdev" ]
12
+nrf24l01-stick = [ "nrf24l01-stick-driver" ]
8 13
 
9 14
 [dependencies]
10 15
 linux-embedded-hal = "0.3"
@@ -17,12 +22,13 @@ thiserror = "1.0"
17 22
 protocol = { path = "../../common/rust-protocol" }
18 23
 rand = "0.7.3"
19 24
 tokio = { version = "0.2", features = [ "full" ] }
20
-sysfs_gpio = { version = "0.5.4", features = [ "use_tokio" ] }
25
+sysfs_gpio = { version = "0.5.4", features = [ "use_tokio" ], optional = true }
21 26
 futures = "0.3.5"
22 27
 futures-util = "0.3.5"
23 28
 nb = "0.1.2"
24
-gpio-cdev = { git = "https://github.com/mgottschlag/gpio-cdev", branch = "tokio", features = [ "async-tokio" ] }
29
+gpio-cdev = { git = "https://github.com/mgottschlag/gpio-cdev", branch = "tokio", features = [ "async-tokio" ], optional = true }
25 30
 chrono = "0.4.11"
31
+nrf24l01-stick-driver = { git = "https://github.com/mgottschlag/nrf24l01-stick", optional = true }
26 32
 
27 33
 [patch.crates-io]
28 34
 sysfs_gpio = { git = "https://github.com/mgottschlag/rust-sysfs-gpio", branch = "new-futures" }

+ 4
- 0
base-station/software/src/main.rs Wyświetl plik

@@ -2,6 +2,9 @@
2 2
 //!
3 3
 //! TODO: Document configuration and hardware setup.
4 4
 
5
+#[cfg(not(any(feature = "rpi4", feature = "nanopineo", feature = "nrf24l01-stick")))]
6
+compile_error!("Select an SPI implementation via the corresponding cargo feature!");
7
+
5 8
 use std::env;
6 9
 use std::sync::Arc;
7 10
 use std::time::{Duration, Instant};
@@ -21,6 +24,7 @@ use tsdb::TimeSeriesDatabase;
21 24
 mod current;
22 25
 mod publish;
23 26
 mod radio;
27
+#[cfg(any(feature = "nanopineo", feature = "rpi4"))]
24 28
 mod spi;
25 29
 mod tsdb;
26 30
 

base-station/software/src/radio.rs → base-station/software/src/radio/gpio.rs Wyświetl plik

@@ -1,5 +1,3 @@
1
-//! Wrapper around the NRF24 driver which provides tokio-compatible asynchronous interfaces.
2
-
3 1
 use std::convert::TryInto;
4 2
 use std::io;
5 3
 use std::time::Duration;
@@ -13,81 +11,40 @@ use linux_embedded_hal::spidev::{SpiModeFlags, Spidev, SpidevOptions};
13 11
 use linux_embedded_hal::sysfs_gpio::{Direction, Error as GpioError};
14 12
 use linux_embedded_hal::Pin;
15 13
 use log::{error, info};
16
-use protocol::{Location, Packet};
14
+use protocol::Packet;
17 15
 use rand::Rng;
18 16
 use tokio::select;
19
-use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};
17
+use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
20 18
 use tokio::time::delay_for;
21 19
 
22
-use super::spi::EmbeddedHalSpidev;
23
-
24
-const DISPLAY_ID: u8 = 0x20;
25
-const DISPLAY_KEY: [u8; 16] = include!("../../../common/display_key.txt");
26
-const WEATHER_STATION_0_ID: u8 = 0x30;
27
-const WEATHER_STATION_0_KEY: [u8; 16] = include!("../../../common/weather_station_0_key.txt");
28
-const WEATHER_STATION_1_ID: u8 = 0x31;
29
-const WEATHER_STATION_1_KEY: [u8; 16] = include!("../../../common/weather_station_1_key.txt");
20
+use super::super::spi::EmbeddedHalSpidev;
21
+use super::{get_key, RadioConfig};
30 22
 
31
-/// Hardware configuration.
32
-pub struct RadioConfig {
33
-    // TODO
34
-}
35
-
36
-/// Wrapper around the NRF24 radio driver.
37
-pub struct Radio {
38
-    tx: UnboundedSender<(u8, Packet)>,
23
+pub struct RadioTask {
24
+    config: super::RadioConfig,
25
+    nrf24: Option<RxMode<NRF24L01<GpioError, Pin, Pin, EmbeddedHalSpidev>>>,
26
+    irq: Option<AsyncLineEventHandle>,
27
+    tx: UnboundedReceiver<(u8, Packet)>,
28
+    rx: UnboundedSender<(u8, Packet)>,
39 29
 }
40 30
 
41
-impl Radio {
42
-    /// Initializes the radio and returns both radio and the stream of incoming packets.
43
-    pub async fn init(
31
+impl RadioTask {
32
+    pub fn new(
44 33
         config: RadioConfig,
45
-    ) -> Result<(Radio, UnboundedReceiver<(u8, Packet)>), RadioError> {
46
-        info!("Initializing radio...");
47
-        let (tx_send, tx_recv) = mpsc::unbounded_channel();
48
-        let (rx_send, rx_recv) = mpsc::unbounded_channel();
49
-        let mut task = RadioTask {
34
+        tx: UnboundedReceiver<(u8, Packet)>,
35
+        rx: UnboundedSender<(u8, Packet)>,
36
+    ) -> RadioTask {
37
+        RadioTask {
50 38
             config,
51 39
             nrf24: None,
52 40
             irq: None,
53
-            tx: tx_recv,
54
-            rx: rx_send,
55
-        };
56
-        task.init().await?;
57
-        info!("radio initialized.");
58
-
59
-        // Start a task for packet handling.
60
-        tokio::spawn(async move {
61
-            task.run().await;
62
-        });
63
-
64
-        Ok((Radio { tx: tx_send }, rx_recv))
65
-    }
66
-
67
-    /// Sends a packet to the specified device.
68
-    ///
69
-    /// The function returns immediately, and the packet might or might not be sent successfully.
70
-    ///
71
-    /// # Arguments
72
-    ///
73
-    /// * `device_id` - the ID of the targeted device
74
-    /// * `packet` - the packet to send
75
-    pub fn send_packet_async(&mut self, device_id: u8, packet: Packet) {
76
-        self.tx.send((device_id, packet)).unwrap();
41
+            tx,
42
+            rx,
43
+        }
77 44
     }
78
-}
79 45
 
80
-struct RadioTask {
81
-    config: RadioConfig,
82
-    nrf24: Option<RxMode<NRF24L01<GpioError, Pin, Pin, EmbeddedHalSpidev>>>,
83
-    irq: Option<AsyncLineEventHandle>,
84
-    tx: UnboundedReceiver<(u8, Packet)>,
85
-    rx: UnboundedSender<(u8, Packet)>,
86
-}
87
-
88
-impl RadioTask {
89
-    async fn init(&mut self) -> Result<(), RadioError> {
90
-        // The NRF module is connected as follows:
46
+    pub async fn init(&mut self) -> Result<(), RadioError> {
47
+        // On the NanoPi Neo, the NRF module is connected as follows:
91 48
         // - CE: PA1
92 49
         // - CS: PG8
93 50
         // - IRQ: PG9
@@ -96,24 +53,35 @@ impl RadioTask {
96 53
         // - SCK: PC2
97 54
         // - Power (active low): PG11
98 55
 
56
+        // TODO: Other pins.
57
+
99 58
         // Configure the GPIOs.
100
-        let pwr_nr = get_pin_number('G', 11);
59
+        let (pwr_nr, ce_nr, cs_nr, irq_nr) = if cfg!(feature = "nanopineo") {
60
+            (
61
+                get_pin_number('G', 11),
62
+                get_pin_number('A', 1),
63
+                get_pin_number('G', 8),
64
+                get_pin_number('G', 9),
65
+            )
66
+        } else if cfg!(feature = "rpi4") {
67
+            (20, 25, 7, 12)
68
+        } else {
69
+            panic!("no system selected");
70
+        };
71
+
101 72
         let mut pwr = Pin::new(pwr_nr);
102 73
         pwr.export().unwrap();
103 74
         pwr.set_direction(Direction::Out).unwrap();
104 75
 
105
-        let ce_nr = get_pin_number('A', 1);
106 76
         let mut ce = Pin::new(ce_nr);
107 77
         ce.export().unwrap();
108 78
         ce.set_direction(Direction::Out).unwrap();
109 79
         ce.set_high().unwrap();
110
-        let cs_nr = get_pin_number('G', 8);
111 80
         let mut cs = Pin::new(cs_nr);
112 81
         cs.export().unwrap();
113 82
         cs.set_direction(Direction::Out).unwrap();
114 83
         cs.set_high().unwrap();
115 84
         let mut chip = Chip::new("/dev/gpiochip0")?;
116
-        let irq_nr = get_pin_number('G', 9);
117 85
         let line = chip.get_line(irq_nr as u32)?;
118 86
         let irq = AsyncLineEventHandle::new(line.events(
119 87
             LineRequestFlags::INPUT,
@@ -172,7 +140,7 @@ impl RadioTask {
172 140
         Ok(())
173 141
     }
174 142
 
175
-    async fn run(&mut self) {
143
+    pub async fn run(&mut self) {
176 144
         loop {
177 145
             let e = self.run_with_initialized_radio().await;
178 146
             error!("Error in radio task: {:?}", e);
@@ -329,24 +297,6 @@ impl From<embedded_nrf24l01::Error<std::io::Error>> for RadioError {
329 297
     }
330 298
 }
331 299
 
332
-pub fn get_device_location(device_id: u8) -> Location {
333
-    match device_id {
334
-        DISPLAY_ID => Location::Bedroom,
335
-        WEATHER_STATION_0_ID => Location::Livingroom,
336
-        WEATHER_STATION_1_ID => Location::Bathroom,
337
-        _ => Location::Livingroom,
338
-    }
339
-}
340
-
341
-fn get_key(device_id: u8) -> Option<&'static [u8]> {
342
-    match device_id {
343
-        DISPLAY_ID => Some(&DISPLAY_KEY),
344
-        WEATHER_STATION_0_ID => Some(&WEATHER_STATION_0_KEY),
345
-        WEATHER_STATION_1_ID => Some(&WEATHER_STATION_1_KEY),
346
-        _ => None,
347
-    }
348
-}
349
-
350 300
 fn get_pin_number(c: char, n: u64) -> u64 {
351 301
     (c as u64 - 'A' as u64) * 32 + n
352 302
 }

+ 88
- 0
base-station/software/src/radio/mod.rs Wyświetl plik

@@ -0,0 +1,88 @@
1
+//! Wrapper around the NRF24 driver which provides tokio-compatible asynchronous interfaces.
2
+
3
+use log::info;
4
+use protocol::{Location, Packet};
5
+use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};
6
+
7
+#[cfg(feature = "nrf24l01-stick")]
8
+mod nrfstick;
9
+#[cfg(feature = "nrf24l01-stick")]
10
+pub use nrfstick::RadioError;
11
+#[cfg(feature = "nrf24l01-stick")]
12
+pub use nrfstick::RadioTask;
13
+
14
+#[cfg(any(feature = "nanopineo", feature = "rpi4"))]
15
+mod gpio;
16
+#[cfg(any(feature = "nanopineo", feature = "rpi4"))]
17
+pub use gpio::RadioError;
18
+#[cfg(any(feature = "nanopineo", feature = "rpi4"))]
19
+pub use gpio::RadioTask;
20
+
21
+const DISPLAY_ID: u8 = 0x20;
22
+const DISPLAY_KEY: [u8; 16] = include!("../../../../common/display_key.txt");
23
+const WEATHER_STATION_0_ID: u8 = 0x30;
24
+const WEATHER_STATION_0_KEY: [u8; 16] = include!("../../../../common/weather_station_0_key.txt");
25
+const WEATHER_STATION_1_ID: u8 = 0x31;
26
+const WEATHER_STATION_1_KEY: [u8; 16] = include!("../../../../common/weather_station_1_key.txt");
27
+
28
+/// Hardware configuration.
29
+pub struct RadioConfig {
30
+    // TODO
31
+}
32
+
33
+/// Wrapper around the NRF24 radio driver.
34
+pub struct Radio {
35
+    tx: UnboundedSender<(u8, Packet)>,
36
+}
37
+
38
+impl Radio {
39
+    /// Initializes the radio and returns both radio and the stream of incoming packets.
40
+    pub async fn init(
41
+        config: RadioConfig,
42
+    ) -> Result<(Radio, UnboundedReceiver<(u8, Packet)>), RadioError> {
43
+        info!("Initializing radio...");
44
+        let (tx_send, tx_recv) = mpsc::unbounded_channel();
45
+        let (rx_send, rx_recv) = mpsc::unbounded_channel();
46
+        let mut task = RadioTask::new(config, tx_recv, rx_send);
47
+        task.init().await?;
48
+        info!("radio initialized.");
49
+
50
+        // Start a task for packet handling.
51
+        tokio::spawn(async move {
52
+            task.run().await;
53
+        });
54
+
55
+        Ok((Radio { tx: tx_send }, rx_recv))
56
+    }
57
+
58
+    /// Sends a packet to the specified device.
59
+    ///
60
+    /// The function returns immediately, and the packet might or might not be sent successfully.
61
+    ///
62
+    /// # Arguments
63
+    ///
64
+    /// * `device_id` - the ID of the targeted device
65
+    /// * `packet` - the packet to send
66
+    pub fn send_packet_async(&mut self, device_id: u8, packet: Packet) {
67
+        // TODO: Can we encrypt the packet here (and deduplicate the code)?
68
+        self.tx.send((device_id, packet)).unwrap();
69
+    }
70
+}
71
+
72
+pub fn get_device_location(device_id: u8) -> Location {
73
+    match device_id {
74
+        DISPLAY_ID => Location::Bedroom,
75
+        WEATHER_STATION_0_ID => Location::Livingroom,
76
+        WEATHER_STATION_1_ID => Location::Bathroom,
77
+        _ => Location::Livingroom,
78
+    }
79
+}
80
+
81
+fn get_key(device_id: u8) -> Option<&'static [u8]> {
82
+    match device_id {
83
+        DISPLAY_ID => Some(&DISPLAY_KEY),
84
+        WEATHER_STATION_0_ID => Some(&WEATHER_STATION_0_KEY),
85
+        WEATHER_STATION_1_ID => Some(&WEATHER_STATION_1_KEY),
86
+        _ => None,
87
+    }
88
+}

+ 177
- 0
base-station/software/src/radio/nrfstick.rs Wyświetl plik

@@ -0,0 +1,177 @@
1
+use std::convert::TryInto;
2
+use std::io;
3
+use std::time::Duration;
4
+
5
+use futures_util::stream::StreamExt;
6
+use log::{error, info};
7
+use nrf24l01_stick_driver::{
8
+    Configuration, CrcMode, DataRate, Receiver, MAX_PAYLOAD_LEN, NRF24L01,
9
+};
10
+use protocol::Packet;
11
+use rand::Rng;
12
+use tokio::select;
13
+use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
14
+use tokio::time::delay_for;
15
+
16
+use super::{get_key, RadioConfig};
17
+
18
+pub struct RadioTask {
19
+    config: super::RadioConfig,
20
+    nrf: Option<Receiver>,
21
+
22
+    tx: UnboundedReceiver<(u8, Packet)>,
23
+    rx: UnboundedSender<(u8, Packet)>,
24
+}
25
+
26
+impl RadioTask {
27
+    pub fn new(
28
+        config: RadioConfig,
29
+        tx: UnboundedReceiver<(u8, Packet)>,
30
+        rx: UnboundedSender<(u8, Packet)>,
31
+    ) -> RadioTask {
32
+        RadioTask {
33
+            config,
34
+            nrf: None,
35
+            tx,
36
+            rx,
37
+        }
38
+    }
39
+
40
+    pub async fn init(&mut self) -> Result<(), RadioError> {
41
+        let mut config = Configuration::default();
42
+        config.channel = 0x32;
43
+        config.rate = DataRate::R2Mbps;
44
+        config.power = 3;
45
+        config.crc = Some(CrcMode::OneByte);
46
+        config.auto_retransmit_delay_count = Some((250, 3));
47
+        // TODO: Auto ACK
48
+
49
+        let mut nrf24l01 = NRF24L01::open_default(config)
50
+            .await
51
+            .expect("could not open device");
52
+        nrf24l01
53
+            .set_receive_addr(
54
+                Some((&[0xb3u8, 0xb3u8, 0xb3u8, 0xb3u8, 0x00u8] as &[u8]).into()),
55
+                None,
56
+                None,
57
+                None,
58
+                None,
59
+            )
60
+            .await
61
+            .expect("could not set receive address");
62
+        self.nrf = Some(nrf24l01.receive().await.expect("could not start receiving"));
63
+
64
+        Ok(())
65
+    }
66
+
67
+    pub async fn run(&mut self) {
68
+        loop {
69
+            let e = self.run_with_initialized_radio().await;
70
+            error!("Error in radio task: {:?}", e);
71
+            self.nrf = None;
72
+            // Try to reinitialize the radio.
73
+            loop {
74
+                info!("Trying to reinitialize radio: {:?}", e);
75
+                match self.init().await {
76
+                    Ok(_) => break,
77
+                    Err(e) => error!("Radio reinitialization failed: {:?}", e),
78
+                }
79
+                delay_for(Duration::from_secs(3)).await;
80
+            }
81
+        }
82
+    }
83
+
84
+    async fn run_with_initialized_radio(&mut self) -> Result<(), RadioError> {
85
+        // Wait for an RX interrupt of until we have to send a packet.
86
+        loop {
87
+            select! {
88
+                packet = self.nrf.as_mut().unwrap().receive() => {
89
+                    let packet = packet?;
90
+                    info!(
91
+                        "packet received on pipe {}: {:x?}, {}",
92
+                        packet.pipe,
93
+                        packet.payload,
94
+                        packet.payload.len()
95
+                    );
96
+                    if packet.payload.len() != 32 {
97
+                        continue;
98
+                    }
99
+
100
+                    let mut payload: [u8; 32] = packet.payload[0..32].try_into().unwrap();
101
+                    self.handle_packet(&mut payload).await;
102
+                },
103
+                packet = self.tx.next() => {
104
+                    let (device_id, packet) = packet.unwrap();
105
+                    // Switch to TX mode and send the packet.
106
+                    self.send_packet(device_id, packet).await?;
107
+                },
108
+            }
109
+        }
110
+    }
111
+
112
+    async fn handle_packet(&mut self, packet: &mut [u8]) {
113
+        // Get the key of the device.
114
+        let device_id = packet[0];
115
+        let key = match get_key(device_id) {
116
+            Some(k) => k,
117
+            None => {
118
+                info!("packet from unknown device {:02x}", device_id);
119
+                return;
120
+            }
121
+        };
122
+
123
+        // Decode the packet.
124
+        let packet = match Packet::decrypt_and_decode(key, packet) {
125
+            Err(e) => {
126
+                info!(
127
+                    "invalid packet from device {:02x}, error {:?}",
128
+                    device_id, e
129
+                );
130
+                return;
131
+            }
132
+            Ok(p) => p,
133
+        };
134
+        // TODO: For all packets except for salt requests, check whether the
135
+        // salt was incremented to prevent replay attacks.
136
+        // TODO: Also check whether the device bit in the salt is 0.
137
+
138
+        info!("packet from {}: {:?}", device_id, packet);
139
+
140
+        self.rx.send((device_id, packet)).unwrap();
141
+    }
142
+
143
+    async fn send_packet(&mut self, device_id: u8, packet: Packet) -> Result<(), RadioError> {
144
+        let salt = rand::thread_rng().gen::<u64>();
145
+        // TODO: Fix salt (device bit, device id).
146
+        let mut encoded = [0u8; 32];
147
+        let key = get_key(device_id);
148
+
149
+        info!("sending packet: {:?}", packet);
150
+        if packet.encode_and_encrypt(key.unwrap(), salt, &mut encoded) {
151
+            match self
152
+                .nrf
153
+                .as_mut()
154
+                .unwrap()
155
+                .send(
156
+                    (&[0xb3u8, 0xb3u8, 0xb3u8, 0xb3u8, device_id][..]).into(),
157
+                    &encoded,
158
+                )
159
+                .await
160
+            {
161
+                Ok(()) => {}
162
+                Err(e) => println!("could not send: {:?}", e), // TODO: Distinguish between packet loss and other errors!
163
+            }
164
+        } else {
165
+            info!("could not encode packet {:?}", packet);
166
+        }
167
+        Ok(())
168
+    }
169
+}
170
+
171
+#[derive(thiserror::Error, Debug)]
172
+pub enum RadioError {
173
+    #[error("I/O error")]
174
+    Io(#[from] io::Error),
175
+    #[error("radio error: {0:?}")]
176
+    Radio(#[from] nrf24l01_stick_driver::Error),
177
+}

Ładowanie…
Anuluj
Zapisz