Browse Source

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

Mathias Gottschlag 4 years ago
parent
commit
6b434499a1

+ 277
- 4
base-station/software/Cargo.lock View File

1
 # This file is automatically @generated by Cargo.
1
 # This file is automatically @generated by Cargo.
2
 # It is not intended for manual editing.
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
 [[package]]
24
 [[package]]
4
 name = "aho-corasick"
25
 name = "aho-corasick"
5
 version = "0.7.10"
26
 version = "0.7.10"
15
 source = "registry+https://github.com/rust-lang/crates.io-index"
36
 source = "registry+https://github.com/rust-lang/crates.io-index"
16
 checksum = "b585a98a234c46fc563103e9278c9391fde1f4e6850334da895d27edb9580f62"
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
 [[package]]
51
 [[package]]
19
 name = "atty"
52
 name = "atty"
20
 version = "0.2.14"
53
 version = "0.2.14"
47
  "log",
80
  "log",
48
  "mqtt-protocol",
81
  "mqtt-protocol",
49
  "nb",
82
  "nb",
83
+ "nrf24l01-stick-driver",
50
  "protocol",
84
  "protocol",
51
  "rand",
85
  "rand",
52
  "sysfs_gpio",
86
  "sysfs_gpio",
266
  "slab",
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
 [[package]]
331
 [[package]]
270
 name = "getrandom"
332
 name = "getrandom"
271
 version = "0.1.14"
333
 version = "0.1.14"
286
  "futures",
348
  "futures",
287
  "libc",
349
  "libc",
288
  "mio",
350
  "mio",
289
- "nix",
351
+ "nix 0.14.1",
290
  "tokio",
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
 [[package]]
377
 [[package]]
294
 name = "hermit-abi"
378
 name = "hermit-abi"
295
 version = "0.1.11"
379
 version = "0.1.11"
317
  "bitflags",
401
  "bitflags",
318
  "byteorder",
402
  "byteorder",
319
  "libc",
403
  "libc",
320
- "nix",
404
+ "nix 0.14.1",
321
 ]
405
 ]
322
 
406
 
323
 [[package]]
407
 [[package]]
360
 source = "registry+https://github.com/rust-lang/crates.io-index"
444
 source = "registry+https://github.com/rust-lang/crates.io-index"
361
 checksum = "3baa92041a6fec78c687fa0cc2b3fae8884f743d672cf551bed1d6dac6988d0f"
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
 [[package]]
467
 [[package]]
364
 name = "linux-embedded-hal"
468
 name = "linux-embedded-hal"
365
 version = "0.3.0"
469
 version = "0.3.0"
385
  "cfg-if",
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
 [[package]]
510
 [[package]]
389
 name = "memchr"
511
 name = "memchr"
390
 version = "2.3.3"
512
 version = "2.3.3"
422
  "winapi 0.3.8",
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
 [[package]]
560
 [[package]]
426
 name = "mio-uds"
561
 name = "mio-uds"
427
 version = "0.6.7"
562
 version = "0.6.7"
497
  "void",
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
 [[package]]
671
 [[package]]
501
 name = "num-integer"
672
 name = "num-integer"
502
 version = "0.1.42"
673
 version = "0.1.42"
564
 source = "registry+https://github.com/rust-lang/crates.io-index"
735
 source = "registry+https://github.com/rust-lang/crates.io-index"
565
 checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
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
 [[package]]
761
 [[package]]
568
 name = "ppv-lite86"
762
 name = "ppv-lite86"
569
 version = "0.2.6"
763
 version = "0.2.6"
703
 source = "registry+https://github.com/rust-lang/crates.io-index"
897
 source = "registry+https://github.com/rust-lang/crates.io-index"
704
 checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
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
 [[package]]
920
 [[package]]
707
 name = "serial-core"
921
 name = "serial-core"
708
 version = "0.4.0"
922
 version = "0.4.0"
724
  "termios",
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
 [[package]]
958
 [[package]]
728
 name = "signal-hook-registry"
959
 name = "signal-hook-registry"
729
 version = "1.2.0"
960
 version = "1.2.0"
760
 dependencies = [
991
 dependencies = [
761
  "bitflags",
992
  "bitflags",
762
  "libc",
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
 [[package]]
1003
 [[package]]
767
 name = "syn"
1004
 name = "syn"
768
 version = "1.0.17"
1005
 version = "1.0.17"
781
 dependencies = [
1018
 dependencies = [
782
  "futures",
1019
  "futures",
783
  "mio",
1020
  "mio",
784
- "nix",
1021
+ "nix 0.14.1",
785
  "pin-project-lite",
1022
  "pin-project-lite",
786
  "tokio",
1023
  "tokio",
787
 ]
1024
 ]
878
  "syn",
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
 [[package]]
1148
 [[package]]
882
 name = "unicode-xid"
1149
 name = "unicode-xid"
883
 version = "0.2.0"
1150
 version = "0.2.0"
884
 source = "registry+https://github.com/rust-lang/crates.io-index"
1151
 source = "registry+https://github.com/rust-lang/crates.io-index"
885
 checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
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
 [[package]]
1160
 [[package]]
888
 name = "void"
1161
 name = "void"
889
 version = "1.0.2"
1162
 version = "1.0.2"

+ 9
- 3
base-station/software/Cargo.toml View File

4
 authors = ["Mathias Gottschlag <mgottschlag@gmail.com>"]
4
 authors = ["Mathias Gottschlag <mgottschlag@gmail.com>"]
5
 edition = "2018"
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
 [dependencies]
14
 [dependencies]
10
 linux-embedded-hal = "0.3"
15
 linux-embedded-hal = "0.3"
17
 protocol = { path = "../../common/rust-protocol" }
22
 protocol = { path = "../../common/rust-protocol" }
18
 rand = "0.7.3"
23
 rand = "0.7.3"
19
 tokio = { version = "0.2", features = [ "full" ] }
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
 futures = "0.3.5"
26
 futures = "0.3.5"
22
 futures-util = "0.3.5"
27
 futures-util = "0.3.5"
23
 nb = "0.1.2"
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
 chrono = "0.4.11"
30
 chrono = "0.4.11"
31
+nrf24l01-stick-driver = { git = "https://github.com/mgottschlag/nrf24l01-stick", optional = true }
26
 
32
 
27
 [patch.crates-io]
33
 [patch.crates-io]
28
 sysfs_gpio = { git = "https://github.com/mgottschlag/rust-sysfs-gpio", branch = "new-futures" }
34
 sysfs_gpio = { git = "https://github.com/mgottschlag/rust-sysfs-gpio", branch = "new-futures" }

+ 4
- 0
base-station/software/src/main.rs View File

2
 //!
2
 //!
3
 //! TODO: Document configuration and hardware setup.
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
 use std::env;
8
 use std::env;
6
 use std::sync::Arc;
9
 use std::sync::Arc;
7
 use std::time::{Duration, Instant};
10
 use std::time::{Duration, Instant};
21
 mod current;
24
 mod current;
22
 mod publish;
25
 mod publish;
23
 mod radio;
26
 mod radio;
27
+#[cfg(any(feature = "nanopineo", feature = "rpi4"))]
24
 mod spi;
28
 mod spi;
25
 mod tsdb;
29
 mod tsdb;
26
 
30
 

base-station/software/src/radio.rs → base-station/software/src/radio/gpio.rs View File

1
-//! Wrapper around the NRF24 driver which provides tokio-compatible asynchronous interfaces.
2
-
3
 use std::convert::TryInto;
1
 use std::convert::TryInto;
4
 use std::io;
2
 use std::io;
5
 use std::time::Duration;
3
 use std::time::Duration;
13
 use linux_embedded_hal::sysfs_gpio::{Direction, Error as GpioError};
11
 use linux_embedded_hal::sysfs_gpio::{Direction, Error as GpioError};
14
 use linux_embedded_hal::Pin;
12
 use linux_embedded_hal::Pin;
15
 use log::{error, info};
13
 use log::{error, info};
16
-use protocol::{Location, Packet};
14
+use protocol::Packet;
17
 use rand::Rng;
15
 use rand::Rng;
18
 use tokio::select;
16
 use tokio::select;
19
-use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};
17
+use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
20
 use tokio::time::delay_for;
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
         config: RadioConfig,
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
             config,
38
             config,
51
             nrf24: None,
39
             nrf24: None,
52
             irq: None,
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
         // - CE: PA1
48
         // - CE: PA1
92
         // - CS: PG8
49
         // - CS: PG8
93
         // - IRQ: PG9
50
         // - IRQ: PG9
96
         // - SCK: PC2
53
         // - SCK: PC2
97
         // - Power (active low): PG11
54
         // - Power (active low): PG11
98
 
55
 
56
+        // TODO: Other pins.
57
+
99
         // Configure the GPIOs.
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
         let mut pwr = Pin::new(pwr_nr);
72
         let mut pwr = Pin::new(pwr_nr);
102
         pwr.export().unwrap();
73
         pwr.export().unwrap();
103
         pwr.set_direction(Direction::Out).unwrap();
74
         pwr.set_direction(Direction::Out).unwrap();
104
 
75
 
105
-        let ce_nr = get_pin_number('A', 1);
106
         let mut ce = Pin::new(ce_nr);
76
         let mut ce = Pin::new(ce_nr);
107
         ce.export().unwrap();
77
         ce.export().unwrap();
108
         ce.set_direction(Direction::Out).unwrap();
78
         ce.set_direction(Direction::Out).unwrap();
109
         ce.set_high().unwrap();
79
         ce.set_high().unwrap();
110
-        let cs_nr = get_pin_number('G', 8);
111
         let mut cs = Pin::new(cs_nr);
80
         let mut cs = Pin::new(cs_nr);
112
         cs.export().unwrap();
81
         cs.export().unwrap();
113
         cs.set_direction(Direction::Out).unwrap();
82
         cs.set_direction(Direction::Out).unwrap();
114
         cs.set_high().unwrap();
83
         cs.set_high().unwrap();
115
         let mut chip = Chip::new("/dev/gpiochip0")?;
84
         let mut chip = Chip::new("/dev/gpiochip0")?;
116
-        let irq_nr = get_pin_number('G', 9);
117
         let line = chip.get_line(irq_nr as u32)?;
85
         let line = chip.get_line(irq_nr as u32)?;
118
         let irq = AsyncLineEventHandle::new(line.events(
86
         let irq = AsyncLineEventHandle::new(line.events(
119
             LineRequestFlags::INPUT,
87
             LineRequestFlags::INPUT,
172
         Ok(())
140
         Ok(())
173
     }
141
     }
174
 
142
 
175
-    async fn run(&mut self) {
143
+    pub async fn run(&mut self) {
176
         loop {
144
         loop {
177
             let e = self.run_with_initialized_radio().await;
145
             let e = self.run_with_initialized_radio().await;
178
             error!("Error in radio task: {:?}", e);
146
             error!("Error in radio task: {:?}", e);
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
 fn get_pin_number(c: char, n: u64) -> u64 {
300
 fn get_pin_number(c: char, n: u64) -> u64 {
351
     (c as u64 - 'A' as u64) * 32 + n
301
     (c as u64 - 'A' as u64) * 32 + n
352
 }
302
 }

+ 88
- 0
base-station/software/src/radio/mod.rs View File

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 View File

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

Loading…
Cancel
Save