diff options
Diffstat (limited to 'examples')
| -rw-r--r-- | examples/nrf/Cargo.toml | 7 | ||||
| -rw-r--r-- | examples/nrf/src/bin/lora_p2p_report.rs | 78 | ||||
| -rw-r--r-- | examples/nrf/src/bin/lora_p2p_sense.rs | 125 |
3 files changed, 209 insertions, 1 deletions
diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index 9ebd04845..6949042e2 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml | |||
| @@ -6,7 +6,8 @@ license = "MIT OR Apache-2.0" | |||
| 6 | 6 | ||
| 7 | [features] | 7 | [features] |
| 8 | default = ["nightly"] | 8 | default = ["nightly"] |
| 9 | nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embedded-io/async", "embassy-net"] | 9 | nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embedded-io/async", "embassy-net", |
| 10 | "embassy-lora", "lorawan-device", "lorawan"] | ||
| 10 | 11 | ||
| 11 | [dependencies] | 12 | [dependencies] |
| 12 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 13 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| @@ -17,6 +18,10 @@ embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defm | |||
| 17 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"], optional = true } | 18 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"], optional = true } |
| 18 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true } | 19 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true } |
| 19 | embedded-io = "0.3.0" | 20 | embedded-io = "0.3.0" |
| 21 | embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt"], optional = true } | ||
| 22 | |||
| 23 | lorawan-device = { version = "0.8.0", default-features = false, features = ["async"], optional = true } | ||
| 24 | lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"], optional = true } | ||
| 20 | 25 | ||
| 21 | defmt = "0.3" | 26 | defmt = "0.3" |
| 22 | defmt-rtt = "0.3" | 27 | defmt-rtt = "0.3" |
diff --git a/examples/nrf/src/bin/lora_p2p_report.rs b/examples/nrf/src/bin/lora_p2p_report.rs new file mode 100644 index 000000000..d512b83f6 --- /dev/null +++ b/examples/nrf/src/bin/lora_p2p_report.rs | |||
| @@ -0,0 +1,78 @@ | |||
| 1 | //! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio. | ||
| 2 | //! Other nrf/sx126x combinations may work with appropriate pin modifications. | ||
| 3 | //! It demonstates LORA P2P functionality in conjunction with example lora_p2p_sense.rs. | ||
| 4 | #![no_std] | ||
| 5 | #![no_main] | ||
| 6 | #![macro_use] | ||
| 7 | #![allow(dead_code)] | ||
| 8 | #![feature(type_alias_impl_trait)] | ||
| 9 | |||
| 10 | use defmt::*; | ||
| 11 | use embassy_executor::Spawner; | ||
| 12 | use embassy_lora::sx126x::*; | ||
| 13 | use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull}; | ||
| 14 | use embassy_nrf::{interrupt, spim}; | ||
| 15 | use embassy_time::{Duration, Timer}; | ||
| 16 | use lorawan_device::async_device::radio::{Bandwidth, CodingRate, PhyRxTx, RfConfig, SpreadingFactor}; | ||
| 17 | use {defmt_rtt as _, panic_probe as _}; | ||
| 18 | |||
| 19 | #[embassy_executor::main] | ||
| 20 | async fn main(_spawner: Spawner) { | ||
| 21 | let p = embassy_nrf::init(Default::default()); | ||
| 22 | let mut spi_config = spim::Config::default(); | ||
| 23 | spi_config.frequency = spim::Frequency::M16; | ||
| 24 | |||
| 25 | let mut radio = { | ||
| 26 | let irq = interrupt::take!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); | ||
| 27 | let spim = spim::Spim::new(p.TWISPI1, irq, p.P1_11, p.P1_13, p.P1_12, spi_config); | ||
| 28 | |||
| 29 | let cs = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard); | ||
| 30 | let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard); | ||
| 31 | let dio1 = Input::new(p.P1_15.degrade(), Pull::Down); | ||
| 32 | let busy = Input::new(p.P1_14.degrade(), Pull::Down); | ||
| 33 | let antenna_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard); | ||
| 34 | let antenna_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard); | ||
| 35 | |||
| 36 | match Sx126xRadio::new(spim, cs, reset, antenna_rx, antenna_tx, dio1, busy, false).await { | ||
| 37 | Ok(r) => r, | ||
| 38 | Err(err) => { | ||
| 39 | info!("Sx126xRadio error = {}", err); | ||
| 40 | return; | ||
| 41 | } | ||
| 42 | } | ||
| 43 | }; | ||
| 44 | |||
| 45 | let mut debug_indicator = Output::new(p.P1_03, Level::Low, OutputDrive::Standard); | ||
| 46 | let mut start_indicator = Output::new(p.P1_04, Level::Low, OutputDrive::Standard); | ||
| 47 | |||
| 48 | start_indicator.set_high(); | ||
| 49 | Timer::after(Duration::from_secs(5)).await; | ||
| 50 | start_indicator.set_low(); | ||
| 51 | |||
| 52 | loop { | ||
| 53 | let rf_config = RfConfig { | ||
| 54 | frequency: 903900000, // channel in Hz | ||
| 55 | bandwidth: Bandwidth::_250KHz, | ||
| 56 | spreading_factor: SpreadingFactor::_10, | ||
| 57 | coding_rate: CodingRate::_4_8, | ||
| 58 | }; | ||
| 59 | |||
| 60 | let mut buffer = [00u8; 100]; | ||
| 61 | |||
| 62 | // P2P receive | ||
| 63 | match radio.rx(rf_config, &mut buffer).await { | ||
| 64 | Ok((buffer_len, rx_quality)) => info!( | ||
| 65 | "RX received = {:?} with length = {} rssi = {} snr = {}", | ||
| 66 | &buffer[0..buffer_len], | ||
| 67 | buffer_len, | ||
| 68 | rx_quality.rssi(), | ||
| 69 | rx_quality.snr() | ||
| 70 | ), | ||
| 71 | Err(err) => info!("RX error = {}", err), | ||
| 72 | } | ||
| 73 | |||
| 74 | debug_indicator.set_high(); | ||
| 75 | Timer::after(Duration::from_secs(2)).await; | ||
| 76 | debug_indicator.set_low(); | ||
| 77 | } | ||
| 78 | } | ||
diff --git a/examples/nrf/src/bin/lora_p2p_sense.rs b/examples/nrf/src/bin/lora_p2p_sense.rs new file mode 100644 index 000000000..b9768874b --- /dev/null +++ b/examples/nrf/src/bin/lora_p2p_sense.rs | |||
| @@ -0,0 +1,125 @@ | |||
| 1 | //! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio. | ||
| 2 | //! Other nrf/sx126x combinations may work with appropriate pin modifications. | ||
| 3 | //! It demonstates LORA P2P functionality in conjunction with example lora_p2p_report.rs. | ||
| 4 | #![no_std] | ||
| 5 | #![no_main] | ||
| 6 | #![macro_use] | ||
| 7 | #![feature(type_alias_impl_trait)] | ||
| 8 | #![feature(alloc_error_handler)] | ||
| 9 | #![allow(incomplete_features)] | ||
| 10 | |||
| 11 | use defmt::*; | ||
| 12 | use embassy_executor::Spawner; | ||
| 13 | use embassy_lora::sx126x::*; | ||
| 14 | use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull}; | ||
| 15 | use embassy_nrf::{interrupt, spim}; | ||
| 16 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||
| 17 | use embassy_sync::pubsub::{PubSubChannel, Publisher}; | ||
| 18 | use embassy_time::{Duration, Timer}; | ||
| 19 | use lorawan_device::async_device::radio::{Bandwidth, CodingRate, PhyRxTx, RfConfig, SpreadingFactor, TxConfig}; | ||
| 20 | use {defmt_rtt as _, panic_probe as _, panic_probe as _}; | ||
| 21 | |||
| 22 | // Message bus: queue of 2, 1 subscriber (Lora P2P), 2 publishers (temperature, motion detection) | ||
| 23 | static MESSAGE_BUS: PubSubChannel<CriticalSectionRawMutex, Message, 2, 1, 2> = PubSubChannel::new(); | ||
| 24 | |||
| 25 | #[derive(Clone, defmt::Format)] | ||
| 26 | enum Message { | ||
| 27 | Temperature(i32), | ||
| 28 | MotionDetected, | ||
| 29 | } | ||
| 30 | |||
| 31 | #[embassy_executor::task] | ||
| 32 | async fn temperature_task(publisher: Publisher<'static, CriticalSectionRawMutex, Message, 2, 1, 2>) { | ||
| 33 | // Publish a fake temperature every 43 seconds, minimizing LORA traffic. | ||
| 34 | loop { | ||
| 35 | Timer::after(Duration::from_secs(43)).await; | ||
| 36 | publisher.publish(Message::Temperature(9)).await; | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | #[embassy_executor::task] | ||
| 41 | async fn motion_detection_task(publisher: Publisher<'static, CriticalSectionRawMutex, Message, 2, 1, 2>) { | ||
| 42 | // Publish a fake motion detection every 79 seconds, minimizing LORA traffic. | ||
| 43 | loop { | ||
| 44 | Timer::after(Duration::from_secs(79)).await; | ||
| 45 | publisher.publish(Message::MotionDetected).await; | ||
| 46 | } | ||
| 47 | } | ||
| 48 | |||
| 49 | #[embassy_executor::main] | ||
| 50 | async fn main(spawner: Spawner) { | ||
| 51 | let p = embassy_nrf::init(Default::default()); | ||
| 52 | // set up to funnel temperature and motion detection events to the Lora Tx task | ||
| 53 | let mut lora_tx_subscriber = unwrap!(MESSAGE_BUS.subscriber()); | ||
| 54 | let temperature_publisher = unwrap!(MESSAGE_BUS.publisher()); | ||
| 55 | let motion_detection_publisher = unwrap!(MESSAGE_BUS.publisher()); | ||
| 56 | |||
| 57 | let mut spi_config = spim::Config::default(); | ||
| 58 | spi_config.frequency = spim::Frequency::M16; | ||
| 59 | |||
| 60 | let mut radio = { | ||
| 61 | let irq = interrupt::take!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); | ||
| 62 | let spim = spim::Spim::new(p.TWISPI1, irq, p.P1_11, p.P1_13, p.P1_12, spi_config); | ||
| 63 | |||
| 64 | let cs = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard); | ||
| 65 | let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard); | ||
| 66 | let dio1 = Input::new(p.P1_15.degrade(), Pull::Down); | ||
| 67 | let busy = Input::new(p.P1_14.degrade(), Pull::Down); | ||
| 68 | let antenna_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard); | ||
| 69 | let antenna_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard); | ||
| 70 | |||
| 71 | match Sx126xRadio::new(spim, cs, reset, antenna_rx, antenna_tx, dio1, busy, false).await { | ||
| 72 | Ok(r) => r, | ||
| 73 | Err(err) => { | ||
| 74 | info!("Sx126xRadio error = {}", err); | ||
| 75 | return; | ||
| 76 | } | ||
| 77 | } | ||
| 78 | }; | ||
| 79 | |||
| 80 | let mut start_indicator = Output::new(p.P1_04, Level::Low, OutputDrive::Standard); | ||
| 81 | |||
| 82 | start_indicator.set_high(); | ||
| 83 | Timer::after(Duration::from_secs(5)).await; | ||
| 84 | start_indicator.set_low(); | ||
| 85 | |||
| 86 | match radio.lora.sleep().await { | ||
| 87 | Ok(()) => info!("Sleep successful"), | ||
| 88 | Err(err) => info!("Sleep unsuccessful = {}", err), | ||
| 89 | } | ||
| 90 | |||
| 91 | unwrap!(spawner.spawn(temperature_task(temperature_publisher))); | ||
| 92 | unwrap!(spawner.spawn(motion_detection_task(motion_detection_publisher))); | ||
| 93 | |||
| 94 | loop { | ||
| 95 | let message = lora_tx_subscriber.next_message_pure().await; | ||
| 96 | |||
| 97 | let tx_config = TxConfig { | ||
| 98 | // 11 byte maximum payload for Bandwidth 125 and SF 10 | ||
| 99 | pw: 10, // up to 20 | ||
| 100 | rf: RfConfig { | ||
| 101 | frequency: 903900000, // channel in Hz, not MHz | ||
| 102 | bandwidth: Bandwidth::_250KHz, | ||
| 103 | spreading_factor: SpreadingFactor::_10, | ||
| 104 | coding_rate: CodingRate::_4_8, | ||
| 105 | }, | ||
| 106 | }; | ||
| 107 | |||
| 108 | let mut buffer = [0x00u8]; | ||
| 109 | match message { | ||
| 110 | Message::Temperature(temperature) => buffer[0] = temperature as u8, | ||
| 111 | Message::MotionDetected => buffer[0] = 0x01u8, | ||
| 112 | }; | ||
| 113 | |||
| 114 | // unencrypted | ||
| 115 | match radio.tx(tx_config, &buffer).await { | ||
| 116 | Ok(ret_val) => info!("TX ret_val = {}", ret_val), | ||
| 117 | Err(err) => info!("TX error = {}", err), | ||
| 118 | } | ||
| 119 | |||
| 120 | match radio.lora.sleep().await { | ||
| 121 | Ok(()) => info!("Sleep successful"), | ||
| 122 | Err(err) => info!("Sleep unsuccessful = {}", err), | ||
| 123 | } | ||
| 124 | } | ||
| 125 | } | ||
