diff options
| author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2022-10-11 08:48:55 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2022-10-11 08:48:55 +0000 |
| commit | 83fcc360fef662b1c9db121b9f9b73e0d5b8fde8 (patch) | |
| tree | 18da699ace96450f89ad15d15fdc8c2410f14fc5 /examples/nrf/src/bin | |
| parent | 71a56292d685891bcbec4b8fc076def9078f3a6a (diff) | |
| parent | 327d3cf0df7a1b116ea7ec44d36a569e6ba6ca16 (diff) | |
Merge #985
985: Create Sx126X LORA driver r=lulf a=ceekdee
Implementation features:
- update embassy-lora to support Semtech SX126X chips, specifically the RAK4631 chip (nrf52480 and sx1262).
- support additional SX126X packages by adding a feature (reference feature rak4631) and updating the board specific Rust file. To enable feature rak4631, settings.json must currently enable "rust-analyzer.linkedProjects" for "examples/nrf/Cargo.toml".
- provide tx/rx examples in examples/nrf to show compatibility with the interface provided by the SX127X LORA implementation.
Only LORA P2P communication has been tested. Implementation lines marked with ??? indicate areas for further investigation. Furthermore, I question whether the DIO1 handler is adequate for catching all interrupt sequences.
This implementation is patterned after the C/C++ implementation provided by Semtech, available through the RAK-nRF52-RUI developers platform.
Co-authored-by: ceekdee <[email protected]>
Co-authored-by: Chuck Davis <[email protected]>
Diffstat (limited to 'examples/nrf/src/bin')
| -rw-r--r-- | examples/nrf/src/bin/lora_p2p_report.rs | 78 | ||||
| -rw-r--r-- | examples/nrf/src/bin/lora_p2p_sense.rs | 125 |
2 files changed, 203 insertions, 0 deletions
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 | } | ||
