aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorceekdee <[email protected]>2023-04-21 01:20:46 -0500
committerceekdee <[email protected]>2023-04-21 01:20:46 -0500
commit02c86bca5275328a15376176ff44487ab7655866 (patch)
tree4f4c3c76261e95e389219b7732cfe6da5777de98
parentfb27594b2eb2cca2aea25dd92a7b730c185b6ecc (diff)
Add external LoRa physical layer functionality.
-rw-r--r--embassy-lora/Cargo.toml8
-rw-r--r--embassy-lora/src/iv.rs325
-rw-r--r--embassy-lora/src/lib.rs3
-rw-r--r--embassy-stm32/src/spi/mod.rs2
-rw-r--r--embassy-stm32/src/subghz/mod.rs2
-rw-r--r--examples/nrf52840/Cargo.toml10
-rw-r--r--examples/nrf52840/src/bin/lora_cad.rs92
-rw-r--r--examples/nrf52840/src/bin/lora_p2p_receive_duty_cycle.rs124
-rw-r--r--examples/nrf52840/src/bin/lora_p2p_report.rs81
-rw-r--r--examples/rp/Cargo.toml6
-rw-r--r--examples/stm32l0/Cargo.toml10
-rw-r--r--examples/stm32l0/src/bin/lora_p2p_receive.rs120
-rw-r--r--examples/stm32wl/Cargo.toml13
-rw-r--r--examples/stm32wl/src/bin/lora_lorawan.rs88
-rw-r--r--examples/stm32wl/src/bin/lora_p2p_send.rs103
15 files changed, 884 insertions, 103 deletions
diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml
index 50449dd46..20cfaf1bd 100644
--- a/embassy-lora/Cargo.toml
+++ b/embassy-lora/Cargo.toml
@@ -22,6 +22,7 @@ sx127x = []
22stm32wl = ["dep:embassy-stm32"] 22stm32wl = ["dep:embassy-stm32"]
23time = [] 23time = []
24defmt = ["dep:defmt", "lorawan/defmt", "lorawan-device/defmt"] 24defmt = ["dep:defmt", "lorawan/defmt", "lorawan-device/defmt"]
25external-lora-phy = ["dep:lora-phy"]
25 26
26[dependencies] 27[dependencies]
27 28
@@ -35,8 +36,9 @@ embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" }
35embedded-hal-async = { version = "=0.2.0-alpha.1" } 36embedded-hal-async = { version = "=0.2.0-alpha.1" }
36embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common", default-features = false } 37embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common", default-features = false }
37futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] } 38futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] }
38embedded-hal = { version = "0.2", features = ["unproven"] } 39embedded-hal = { version = "0.2.6" }
39bit_field = { version = "0.10" } 40bit_field = { version = "0.10" }
40 41
41lorawan-device = { version = "0.9.0", default-features = false, features = ["async"] } 42lora-phy = { version = "1", path = "../../lora-phy", optional = true }
42lorawan = { version = "0.7.2", default-features = false } 43lorawan-device = { version = "0.9.0", path = "../../rust-lorawan/device", default-features = false, features = ["async"] }
44lorawan = { version = "0.7.2", path = "../../rust-lorawan/encoding", default-features = false }
diff --git a/embassy-lora/src/iv.rs b/embassy-lora/src/iv.rs
new file mode 100644
index 000000000..f81134405
--- /dev/null
+++ b/embassy-lora/src/iv.rs
@@ -0,0 +1,325 @@
1#[cfg(feature = "stm32wl")]
2use embassy_stm32::interrupt::*;
3#[cfg(feature = "stm32wl")]
4use embassy_stm32::{pac, PeripheralRef};
5#[cfg(feature = "stm32wl")]
6use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
7#[cfg(feature = "stm32wl")]
8use embassy_sync::signal::Signal;
9use embedded_hal::digital::v2::OutputPin;
10use embedded_hal_async::delay::DelayUs;
11use embedded_hal_async::digital::Wait;
12use lora_phy::mod_params::RadioError::*;
13use lora_phy::mod_params::{BoardType, RadioError};
14use lora_phy::mod_traits::InterfaceVariant;
15
16#[cfg(feature = "stm32wl")]
17static IRQ_SIGNAL: Signal<CriticalSectionRawMutex, ()> = Signal::new();
18
19#[cfg(feature = "stm32wl")]
20/// Base for the InterfaceVariant implementation for an stm32wl/sx1262 combination
21pub struct Stm32wlInterfaceVariant<'a, CTRL> {
22 board_type: BoardType,
23 irq: PeripheralRef<'a, SUBGHZ_RADIO>,
24 rf_switch_rx: Option<CTRL>,
25 rf_switch_tx: Option<CTRL>,
26}
27
28#[cfg(feature = "stm32wl")]
29impl<'a, CTRL> Stm32wlInterfaceVariant<'a, CTRL>
30where
31 CTRL: OutputPin,
32{
33 /// Create an InterfaceVariant instance for an stm32wl/sx1262 combination
34 pub fn new(
35 irq: PeripheralRef<'a, SUBGHZ_RADIO>,
36 rf_switch_rx: Option<CTRL>,
37 rf_switch_tx: Option<CTRL>,
38 ) -> Result<Self, RadioError> {
39 irq.disable();
40 irq.set_handler(Self::on_interrupt);
41 Ok(Self {
42 board_type: BoardType::Stm32wlSx1262, // updated when associated with a specific LoRa board
43 irq,
44 rf_switch_rx,
45 rf_switch_tx,
46 })
47 }
48
49 fn on_interrupt(_: *mut ()) {
50 unsafe { SUBGHZ_RADIO::steal() }.disable();
51 IRQ_SIGNAL.signal(());
52 }
53}
54
55#[cfg(feature = "stm32wl")]
56impl<CTRL> InterfaceVariant for Stm32wlInterfaceVariant<'_, CTRL>
57where
58 CTRL: OutputPin,
59{
60 fn set_board_type(&mut self, board_type: BoardType) {
61 self.board_type = board_type;
62 }
63 async fn set_nss_low(&mut self) -> Result<(), RadioError> {
64 let pwr = pac::PWR;
65 unsafe {
66 pwr.subghzspicr().modify(|w| w.set_nss(pac::pwr::vals::Nss::LOW));
67 }
68 Ok(())
69 }
70 async fn set_nss_high(&mut self) -> Result<(), RadioError> {
71 let pwr = pac::PWR;
72 unsafe {
73 pwr.subghzspicr().modify(|w| w.set_nss(pac::pwr::vals::Nss::HIGH));
74 }
75 Ok(())
76 }
77 async fn reset(&mut self, _delay: &mut impl DelayUs) -> Result<(), RadioError> {
78 let rcc = pac::RCC;
79 unsafe {
80 rcc.csr().modify(|w| w.set_rfrst(true));
81 rcc.csr().modify(|w| w.set_rfrst(false));
82 }
83 Ok(())
84 }
85 async fn wait_on_busy(&mut self) -> Result<(), RadioError> {
86 let pwr = pac::PWR;
87 while unsafe { pwr.sr2().read().rfbusys() == pac::pwr::vals::Rfbusys::BUSY } {}
88 Ok(())
89 }
90
91 async fn await_irq(&mut self) -> Result<(), RadioError> {
92 self.irq.enable();
93 IRQ_SIGNAL.wait().await;
94 Ok(())
95 }
96
97 async fn enable_rf_switch_rx(&mut self) -> Result<(), RadioError> {
98 match &mut self.rf_switch_tx {
99 Some(pin) => pin.set_low().map_err(|_| RfSwitchTx)?,
100 None => (),
101 };
102 match &mut self.rf_switch_rx {
103 Some(pin) => pin.set_high().map_err(|_| RfSwitchRx),
104 None => Ok(()),
105 }
106 }
107 async fn enable_rf_switch_tx(&mut self) -> Result<(), RadioError> {
108 match &mut self.rf_switch_rx {
109 Some(pin) => pin.set_low().map_err(|_| RfSwitchRx)?,
110 None => (),
111 };
112 match &mut self.rf_switch_tx {
113 Some(pin) => pin.set_high().map_err(|_| RfSwitchTx),
114 None => Ok(()),
115 }
116 }
117 async fn disable_rf_switch(&mut self) -> Result<(), RadioError> {
118 match &mut self.rf_switch_rx {
119 Some(pin) => pin.set_low().map_err(|_| RfSwitchRx)?,
120 None => (),
121 };
122 match &mut self.rf_switch_tx {
123 Some(pin) => pin.set_low().map_err(|_| RfSwitchTx),
124 None => Ok(()),
125 }
126 }
127}
128
129/// Base for the InterfaceVariant implementation for an stm32l0/sx1276 combination
130pub struct Stm32l0InterfaceVariant<CTRL, WAIT> {
131 board_type: BoardType,
132 nss: CTRL,
133 reset: CTRL,
134 irq: WAIT,
135 rf_switch_rx: Option<CTRL>,
136 rf_switch_tx: Option<CTRL>,
137}
138
139impl<CTRL, WAIT> Stm32l0InterfaceVariant<CTRL, WAIT>
140where
141 CTRL: OutputPin,
142 WAIT: Wait,
143{
144 /// Create an InterfaceVariant instance for an stm32l0/sx1276 combination
145 pub fn new(
146 nss: CTRL,
147 reset: CTRL,
148 irq: WAIT,
149 rf_switch_rx: Option<CTRL>,
150 rf_switch_tx: Option<CTRL>,
151 ) -> Result<Self, RadioError> {
152 Ok(Self {
153 board_type: BoardType::Stm32l0Sx1276, // updated when associated with a specific LoRa board
154 nss,
155 reset,
156 irq,
157 rf_switch_rx,
158 rf_switch_tx,
159 })
160 }
161}
162
163impl<CTRL, WAIT> InterfaceVariant for Stm32l0InterfaceVariant<CTRL, WAIT>
164where
165 CTRL: OutputPin,
166 WAIT: Wait,
167{
168 fn set_board_type(&mut self, board_type: BoardType) {
169 self.board_type = board_type;
170 }
171 async fn set_nss_low(&mut self) -> Result<(), RadioError> {
172 self.nss.set_low().map_err(|_| NSS)
173 }
174 async fn set_nss_high(&mut self) -> Result<(), RadioError> {
175 self.nss.set_high().map_err(|_| NSS)
176 }
177 async fn reset(&mut self, delay: &mut impl DelayUs) -> Result<(), RadioError> {
178 delay.delay_ms(10).await;
179 self.reset.set_low().map_err(|_| Reset)?;
180 delay.delay_ms(10).await;
181 self.reset.set_high().map_err(|_| Reset)?;
182 delay.delay_ms(10).await;
183 Ok(())
184 }
185 async fn wait_on_busy(&mut self) -> Result<(), RadioError> {
186 Ok(())
187 }
188 async fn await_irq(&mut self) -> Result<(), RadioError> {
189 self.irq.wait_for_high().await.map_err(|_| Irq)
190 }
191
192 async fn enable_rf_switch_rx(&mut self) -> Result<(), RadioError> {
193 match &mut self.rf_switch_tx {
194 Some(pin) => pin.set_low().map_err(|_| RfSwitchTx)?,
195 None => (),
196 };
197 match &mut self.rf_switch_rx {
198 Some(pin) => pin.set_high().map_err(|_| RfSwitchRx),
199 None => Ok(()),
200 }
201 }
202 async fn enable_rf_switch_tx(&mut self) -> Result<(), RadioError> {
203 match &mut self.rf_switch_rx {
204 Some(pin) => pin.set_low().map_err(|_| RfSwitchRx)?,
205 None => (),
206 };
207 match &mut self.rf_switch_tx {
208 Some(pin) => pin.set_high().map_err(|_| RfSwitchTx),
209 None => Ok(()),
210 }
211 }
212 async fn disable_rf_switch(&mut self) -> Result<(), RadioError> {
213 match &mut self.rf_switch_rx {
214 Some(pin) => pin.set_low().map_err(|_| RfSwitchRx)?,
215 None => (),
216 };
217 match &mut self.rf_switch_tx {
218 Some(pin) => pin.set_low().map_err(|_| RfSwitchTx),
219 None => Ok(()),
220 }
221 }
222}
223
224/// Base for the InterfaceVariant implementation for a generic Sx126x LoRa board
225pub struct GenericSx126xInterfaceVariant<CTRL, WAIT> {
226 board_type: BoardType,
227 nss: CTRL,
228 reset: CTRL,
229 dio1: WAIT,
230 busy: WAIT,
231 rf_switch_rx: Option<CTRL>,
232 rf_switch_tx: Option<CTRL>,
233}
234
235impl<CTRL, WAIT> GenericSx126xInterfaceVariant<CTRL, WAIT>
236where
237 CTRL: OutputPin,
238 WAIT: Wait,
239{
240 /// Create an InterfaceVariant instance for an nrf52840/sx1262 combination
241 pub fn new(
242 nss: CTRL,
243 reset: CTRL,
244 dio1: WAIT,
245 busy: WAIT,
246 rf_switch_rx: Option<CTRL>,
247 rf_switch_tx: Option<CTRL>,
248 ) -> Result<Self, RadioError> {
249 Ok(Self {
250 board_type: BoardType::Rak4631Sx1262, // updated when associated with a specific LoRa board
251 nss,
252 reset,
253 dio1,
254 busy,
255 rf_switch_rx,
256 rf_switch_tx,
257 })
258 }
259}
260
261impl<CTRL, WAIT> InterfaceVariant for GenericSx126xInterfaceVariant<CTRL, WAIT>
262where
263 CTRL: OutputPin,
264 WAIT: Wait,
265{
266 fn set_board_type(&mut self, board_type: BoardType) {
267 self.board_type = board_type;
268 }
269 async fn set_nss_low(&mut self) -> Result<(), RadioError> {
270 self.nss.set_low().map_err(|_| NSS)
271 }
272 async fn set_nss_high(&mut self) -> Result<(), RadioError> {
273 self.nss.set_high().map_err(|_| NSS)
274 }
275 async fn reset(&mut self, delay: &mut impl DelayUs) -> Result<(), RadioError> {
276 delay.delay_ms(10).await;
277 self.reset.set_low().map_err(|_| Reset)?;
278 delay.delay_ms(20).await;
279 self.reset.set_high().map_err(|_| Reset)?;
280 delay.delay_ms(10).await;
281 Ok(())
282 }
283 async fn wait_on_busy(&mut self) -> Result<(), RadioError> {
284 self.busy.wait_for_low().await.map_err(|_| Busy)
285 }
286 async fn await_irq(&mut self) -> Result<(), RadioError> {
287 if self.board_type != BoardType::RpPicoWaveshareSx1262 {
288 self.dio1.wait_for_high().await.map_err(|_| DIO1)?;
289 } else {
290 self.dio1.wait_for_rising_edge().await.map_err(|_| DIO1)?;
291 }
292 Ok(())
293 }
294
295 async fn enable_rf_switch_rx(&mut self) -> Result<(), RadioError> {
296 match &mut self.rf_switch_tx {
297 Some(pin) => pin.set_low().map_err(|_| RfSwitchTx)?,
298 None => (),
299 };
300 match &mut self.rf_switch_rx {
301 Some(pin) => pin.set_high().map_err(|_| RfSwitchRx),
302 None => Ok(()),
303 }
304 }
305 async fn enable_rf_switch_tx(&mut self) -> Result<(), RadioError> {
306 match &mut self.rf_switch_rx {
307 Some(pin) => pin.set_low().map_err(|_| RfSwitchRx)?,
308 None => (),
309 };
310 match &mut self.rf_switch_tx {
311 Some(pin) => pin.set_high().map_err(|_| RfSwitchTx),
312 None => Ok(()),
313 }
314 }
315 async fn disable_rf_switch(&mut self) -> Result<(), RadioError> {
316 match &mut self.rf_switch_rx {
317 Some(pin) => pin.set_low().map_err(|_| RfSwitchRx)?,
318 None => (),
319 };
320 match &mut self.rf_switch_tx {
321 Some(pin) => pin.set_low().map_err(|_| RfSwitchTx),
322 None => Ok(()),
323 }
324 }
325}
diff --git a/embassy-lora/src/lib.rs b/embassy-lora/src/lib.rs
index 5c919cbb6..d8bc3cd98 100644
--- a/embassy-lora/src/lib.rs
+++ b/embassy-lora/src/lib.rs
@@ -5,6 +5,9 @@
5//! crate's async LoRaWAN MAC implementation. 5//! crate's async LoRaWAN MAC implementation.
6 6
7pub(crate) mod fmt; 7pub(crate) mod fmt;
8#[cfg(feature = "external-lora-phy")]
9/// interface variants required by the external lora crate
10pub mod iv;
8 11
9#[cfg(feature = "stm32wl")] 12#[cfg(feature = "stm32wl")]
10pub mod stm32wl; 13pub mod stm32wl;
diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs
index 492d0649a..aefa42435 100644
--- a/embassy-stm32/src/spi/mod.rs
+++ b/embassy-stm32/src/spi/mod.rs
@@ -197,7 +197,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
197 /// Useful for on chip peripherals like SUBGHZ which are hardwired. 197 /// Useful for on chip peripherals like SUBGHZ which are hardwired.
198 /// The bus can optionally be exposed externally with `Spi::new()` still. 198 /// The bus can optionally be exposed externally with `Spi::new()` still.
199 #[allow(dead_code)] 199 #[allow(dead_code)]
200 pub(crate) fn new_internal( 200 pub fn new_subghz(
201 peri: impl Peripheral<P = T> + 'd, 201 peri: impl Peripheral<P = T> + 'd,
202 txdma: impl Peripheral<P = Tx> + 'd, 202 txdma: impl Peripheral<P = Tx> + 'd,
203 rxdma: impl Peripheral<P = Rx> + 'd, 203 rxdma: impl Peripheral<P = Rx> + 'd,
diff --git a/embassy-stm32/src/subghz/mod.rs b/embassy-stm32/src/subghz/mod.rs
index 33398fa1d..cd566ba24 100644
--- a/embassy-stm32/src/subghz/mod.rs
+++ b/embassy-stm32/src/subghz/mod.rs
@@ -224,7 +224,7 @@ impl<'d, Tx, Rx> SubGhz<'d, Tx, Rx> {
224 let mut config = SpiConfig::default(); 224 let mut config = SpiConfig::default();
225 config.mode = MODE_0; 225 config.mode = MODE_0;
226 config.bit_order = BitOrder::MsbFirst; 226 config.bit_order = BitOrder::MsbFirst;
227 let spi = Spi::new_internal(peri, txdma, rxdma, clk, config); 227 let spi = Spi::new_subghz(peri, txdma, rxdma, clk, config);
228 228
229 unsafe { wakeup() }; 229 unsafe { wakeup() };
230 230
diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml
index 10c269a76..7a1fe281a 100644
--- a/examples/nrf52840/Cargo.toml
+++ b/examples/nrf52840/Cargo.toml
@@ -13,15 +13,15 @@ nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/night
13embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 13embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
14embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } 14embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] }
15embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } 15embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
16embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } 16embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime"] }
17embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } 17embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] }
18embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true } 18embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true }
19embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "msos-descriptor",], optional = true } 19embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "msos-descriptor",], optional = true }
20embedded-io = "0.4.0" 20embedded-io = "0.4.0"
21embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt"], optional = true } 21embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt", "external-lora-phy"], optional = true }
22 22lora-phy = { version = "1", path = "../../../lora-phy" }
23lorawan-device = { version = "0.9.0", default-features = false, features = ["async"], optional = true } 23lorawan-device = { version = "0.9.0", path = "../../../rust-lorawan/device", default-features = false, features = ["async", "external-lora-phy"], optional = true }
24lorawan = { version = "0.7.2", default-features = false, features = ["default-crypto"], optional = true } 24lorawan = { version = "0.7.2", path = "../../../rust-lorawan/encoding", default-features = false, features = ["default-crypto"], optional = true }
25 25
26defmt = "0.3" 26defmt = "0.3"
27defmt-rtt = "0.4" 27defmt-rtt = "0.4"
diff --git a/examples/nrf52840/src/bin/lora_cad.rs b/examples/nrf52840/src/bin/lora_cad.rs
new file mode 100644
index 000000000..8899c1b23
--- /dev/null
+++ b/examples/nrf52840/src/bin/lora_cad.rs
@@ -0,0 +1,92 @@
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 CAD functionality.
4#![no_std]
5#![no_main]
6#![macro_use]
7#![feature(type_alias_impl_trait)]
8
9use defmt::*;
10use embassy_executor::Spawner;
11use embassy_lora::iv::GenericSx126xInterfaceVariant;
12use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull};
13use embassy_nrf::{bind_interrupts, peripherals, spim};
14use embassy_time::{Delay, Duration, Timer};
15use lora_phy::mod_params::*;
16use lora_phy::sx1261_2::SX1261_2;
17use lora_phy::LoRa;
18use {defmt_rtt as _, panic_probe as _};
19
20bind_interrupts!(struct Irqs {
21 SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 => spim::InterruptHandler<peripherals::TWISPI1>;
22});
23
24#[embassy_executor::main]
25async fn main(_spawner: Spawner) {
26 let p = embassy_nrf::init(Default::default());
27 let mut spi_config = spim::Config::default();
28 spi_config.frequency = spim::Frequency::M16;
29
30 let spim = spim::Spim::new(p.TWISPI1, Irqs, p.P1_11, p.P1_13, p.P1_12, spi_config);
31
32 let nss = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard);
33 let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard);
34 let dio1 = Input::new(p.P1_15.degrade(), Pull::Down);
35 let busy = Input::new(p.P1_14.degrade(), Pull::Down);
36 let rf_switch_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard);
37 let rf_switch_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard);
38
39 let iv =
40 GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, Some(rf_switch_rx), Some(rf_switch_tx)).unwrap();
41
42 let mut delay = Delay;
43
44 let mut lora = {
45 match LoRa::new(SX1261_2::new(BoardType::Rak4631Sx1262, spim, iv), false, &mut delay).await {
46 Ok(l) => l,
47 Err(err) => {
48 info!("Radio error = {}", err);
49 return;
50 }
51 }
52 };
53
54 let mut debug_indicator = Output::new(p.P1_03, Level::Low, OutputDrive::Standard);
55 let mut start_indicator = Output::new(p.P1_04, Level::Low, OutputDrive::Standard);
56
57 start_indicator.set_high();
58 Timer::after(Duration::from_secs(5)).await;
59 start_indicator.set_low();
60
61 let mdltn_params = {
62 match lora.create_modulation_params(SpreadingFactor::_10, Bandwidth::_250KHz, CodingRate::_4_8, 903900000) {
63 Ok(mp) => mp,
64 Err(err) => {
65 info!("Radio error = {}", err);
66 return;
67 }
68 }
69 };
70
71 match lora.prepare_for_cad(&mdltn_params, true).await {
72 Ok(()) => {}
73 Err(err) => {
74 info!("Radio error = {}", err);
75 return;
76 }
77 };
78
79 match lora.cad().await {
80 Ok(cad_activity_detected) => {
81 if cad_activity_detected {
82 info!("cad successful with activity detected")
83 } else {
84 info!("cad successful without activity detected")
85 }
86 debug_indicator.set_high();
87 Timer::after(Duration::from_secs(15)).await;
88 debug_indicator.set_low();
89 }
90 Err(err) => info!("cad unsuccessful = {}", err),
91 }
92}
diff --git a/examples/nrf52840/src/bin/lora_p2p_receive_duty_cycle.rs b/examples/nrf52840/src/bin/lora_p2p_receive_duty_cycle.rs
new file mode 100644
index 000000000..d84701742
--- /dev/null
+++ b/examples/nrf52840/src/bin/lora_p2p_receive_duty_cycle.rs
@@ -0,0 +1,124 @@
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 Rx duty cycle functionality.
4#![no_std]
5#![no_main]
6#![macro_use]
7#![feature(type_alias_impl_trait)]
8
9use defmt::*;
10use embassy_executor::Spawner;
11use embassy_lora::iv::GenericSx126xInterfaceVariant;
12use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull};
13use embassy_nrf::{bind_interrupts, peripherals, spim};
14use embassy_time::{Delay, Duration, Timer};
15use lora_phy::mod_params::*;
16use lora_phy::sx1261_2::SX1261_2;
17use lora_phy::LoRa;
18use {defmt_rtt as _, panic_probe as _};
19
20bind_interrupts!(struct Irqs {
21 SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 => spim::InterruptHandler<peripherals::TWISPI1>;
22});
23
24#[embassy_executor::main]
25async fn main(_spawner: Spawner) {
26 let p = embassy_nrf::init(Default::default());
27 let mut spi_config = spim::Config::default();
28 spi_config.frequency = spim::Frequency::M16;
29
30 let spim = spim::Spim::new(p.TWISPI1, Irqs, p.P1_11, p.P1_13, p.P1_12, spi_config);
31
32 let nss = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard);
33 let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard);
34 let dio1 = Input::new(p.P1_15.degrade(), Pull::Down);
35 let busy = Input::new(p.P1_14.degrade(), Pull::Down);
36 let rf_switch_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard);
37 let rf_switch_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard);
38
39 let iv =
40 GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, Some(rf_switch_rx), Some(rf_switch_tx)).unwrap();
41
42 let mut delay = Delay;
43
44 let mut lora = {
45 match LoRa::new(SX1261_2::new(BoardType::Rak4631Sx1262, spim, iv), false, &mut delay).await {
46 Ok(l) => l,
47 Err(err) => {
48 info!("Radio error = {}", err);
49 return;
50 }
51 }
52 };
53
54 let mut debug_indicator = Output::new(p.P1_03, Level::Low, OutputDrive::Standard);
55 let mut start_indicator = Output::new(p.P1_04, Level::Low, OutputDrive::Standard);
56
57 start_indicator.set_high();
58 Timer::after(Duration::from_secs(5)).await;
59 start_indicator.set_low();
60
61 let mut receiving_buffer = [00u8; 100];
62
63 let mdltn_params = {
64 match lora.create_modulation_params(SpreadingFactor::_10, Bandwidth::_250KHz, CodingRate::_4_8, 903900000) {
65 Ok(mp) => mp,
66 Err(err) => {
67 info!("Radio error = {}", err);
68 return;
69 }
70 }
71 };
72
73 let rx_pkt_params = {
74 match lora.create_rx_packet_params(4, false, receiving_buffer.len() as u8, true, false, &mdltn_params) {
75 Ok(pp) => pp,
76 Err(err) => {
77 info!("Radio error = {}", err);
78 return;
79 }
80 }
81 };
82
83 // See "RM0453 Reference manual STM32WL5x advanced Arm®-based 32-bit MCUs with sub-GHz radio solution" for the best explanation of Rx duty cycle processing.
84 match lora
85 .prepare_for_rx(
86 &mdltn_params,
87 &rx_pkt_params,
88 Some(&DutyCycleParams {
89 rx_time: 300_000, // 300_000 units * 15.625 us/unit = 4.69 s
90 sleep_time: 200_000, // 200_000 units * 15.625 us/unit = 3.13 s
91 }),
92 false,
93 false,
94 0,
95 0,
96 )
97 .await
98 {
99 Ok(()) => {}
100 Err(err) => {
101 info!("Radio error = {}", err);
102 return;
103 }
104 };
105
106 receiving_buffer = [00u8; 100];
107 match lora.rx(&rx_pkt_params, &mut receiving_buffer).await {
108 Ok((received_len, _rx_pkt_status)) => {
109 if (received_len == 3)
110 && (receiving_buffer[0] == 0x01u8)
111 && (receiving_buffer[1] == 0x02u8)
112 && (receiving_buffer[2] == 0x03u8)
113 {
114 info!("rx successful");
115 debug_indicator.set_high();
116 Timer::after(Duration::from_secs(5)).await;
117 debug_indicator.set_low();
118 } else {
119 info!("rx unknown packet")
120 }
121 }
122 Err(err) => info!("rx unsuccessful = {}", err),
123 }
124}
diff --git a/examples/nrf52840/src/bin/lora_p2p_report.rs b/examples/nrf52840/src/bin/lora_p2p_report.rs
deleted file mode 100644
index e24f0db03..000000000
--- a/examples/nrf52840/src/bin/lora_p2p_report.rs
+++ /dev/null
@@ -1,81 +0,0 @@
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
10use defmt::*;
11use embassy_executor::Spawner;
12use embassy_lora::sx126x::*;
13use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull};
14use embassy_nrf::{bind_interrupts, peripherals, spim};
15use embassy_time::{Duration, Timer};
16use lorawan_device::async_device::radio::{Bandwidth, CodingRate, PhyRxTx, RfConfig, SpreadingFactor};
17use {defmt_rtt as _, panic_probe as _};
18
19bind_interrupts!(struct Irqs {
20 SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 => spim::InterruptHandler<peripherals::TWISPI1>;
21});
22
23#[embassy_executor::main]
24async fn main(_spawner: Spawner) {
25 let p = embassy_nrf::init(Default::default());
26 let mut spi_config = spim::Config::default();
27 spi_config.frequency = spim::Frequency::M16;
28
29 let mut radio = {
30 let spim = spim::Spim::new(p.TWISPI1, Irqs, p.P1_11, p.P1_13, p.P1_12, spi_config);
31
32 let cs = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard);
33 let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard);
34 let dio1 = Input::new(p.P1_15.degrade(), Pull::Down);
35 let busy = Input::new(p.P1_14.degrade(), Pull::Down);
36 let antenna_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard);
37 let antenna_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard);
38
39 match Sx126xRadio::new(spim, cs, reset, antenna_rx, antenna_tx, dio1, busy, false).await {
40 Ok(r) => r,
41 Err(err) => {
42 info!("Sx126xRadio error = {}", err);
43 return;
44 }
45 }
46 };
47
48 let mut debug_indicator = Output::new(p.P1_03, Level::Low, OutputDrive::Standard);
49 let mut start_indicator = Output::new(p.P1_04, Level::Low, OutputDrive::Standard);
50
51 start_indicator.set_high();
52 Timer::after(Duration::from_secs(5)).await;
53 start_indicator.set_low();
54
55 loop {
56 let rf_config = RfConfig {
57 frequency: 903900000, // channel in Hz
58 bandwidth: Bandwidth::_250KHz,
59 spreading_factor: SpreadingFactor::_10,
60 coding_rate: CodingRate::_4_8,
61 };
62
63 let mut buffer = [00u8; 100];
64
65 // P2P receive
66 match radio.rx(rf_config, &mut buffer).await {
67 Ok((buffer_len, rx_quality)) => info!(
68 "RX received = {:?} with length = {} rssi = {} snr = {}",
69 &buffer[0..buffer_len],
70 buffer_len,
71 rx_quality.rssi(),
72 rx_quality.snr()
73 ),
74 Err(err) => info!("RX error = {}", err),
75 }
76
77 debug_indicator.set_high();
78 Timer::after(Duration::from_secs(2)).await;
79 debug_indicator.set_low();
80 }
81}
diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml
index 8067f7ba5..1787e4f8a 100644
--- a/examples/rp/Cargo.toml
+++ b/examples/rp/Cargo.toml
@@ -9,12 +9,16 @@ license = "MIT OR Apache-2.0"
9embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal", features = ["defmt"] } 9embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal", features = ["defmt"] }
10embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 11embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
12embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } 12embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime"] }
13embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "pio", "critical-section-impl"] } 13embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "pio", "critical-section-impl"] }
14embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } 14embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
15embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } 15embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] }
16embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 16embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
17embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" } 17embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" }
18embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt", "external-lora-phy"] }
19lora-phy = { version = "1", path = "../../../lora-phy" }
20lorawan-device = { version = "0.9.0", path = "../../../rust-lorawan/device", default-features = false, features = ["async", "external-lora-phy"] }
21lorawan = { version = "0.7.2", path = "../../../rust-lorawan/encoding", default-features = false, features = ["default-crypto"] }
18 22
19defmt = "0.3" 23defmt = "0.3"
20defmt-rtt = "0.4" 24defmt-rtt = "0.4"
diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml
index d08e2b61a..7e9827a84 100644
--- a/examples/stm32l0/Cargo.toml
+++ b/examples/stm32l0/Cargo.toml
@@ -11,12 +11,12 @@ nightly = ["embassy-stm32/nightly", "embassy-lora", "lorawan-device", "lorawan",
11[dependencies] 11[dependencies]
12embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } 12embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] }
13embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 13embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
14embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 14embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
15embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] } 15embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] }
16embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt"], optional = true} 16embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt", "external-lora-phy"], optional = true }
17 17lora-phy = { version = "1", path = "../../../lora-phy" }
18lorawan-device = { version = "0.9.0", default-features = false, features = ["async"], optional = true } 18lorawan-device = { version = "0.9.0", path = "../../../rust-lorawan/device", default-features = false, features = ["async", "external-lora-phy"], optional = true }
19lorawan = { version = "0.7.2", default-features = false, features = ["default-crypto"], optional = true } 19lorawan = { version = "0.7.2", path = "../../../rust-lorawan/encoding", default-features = false, features = ["default-crypto"], optional = true }
20 20
21defmt = "0.3" 21defmt = "0.3"
22defmt-rtt = "0.4" 22defmt-rtt = "0.4"
diff --git a/examples/stm32l0/src/bin/lora_p2p_receive.rs b/examples/stm32l0/src/bin/lora_p2p_receive.rs
new file mode 100644
index 000000000..a5c5f75ea
--- /dev/null
+++ b/examples/stm32l0/src/bin/lora_p2p_receive.rs
@@ -0,0 +1,120 @@
1//! This example runs on the STM32 LoRa Discovery board, which has a builtin Semtech Sx1276 radio.
2//! It demonstrates LORA P2P receive functionality.
3#![no_std]
4#![no_main]
5#![macro_use]
6#![feature(type_alias_impl_trait)]
7
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_lora::iv::Stm32l0InterfaceVariant;
11use embassy_stm32::exti::{Channel, ExtiInput};
12use embassy_stm32::gpio::{Input, Level, Output, Pin, Pull, Speed};
13use embassy_stm32::spi;
14use embassy_stm32::time::khz;
15use embassy_time::{Delay, Duration, Timer};
16use lora_phy::mod_params::*;
17use lora_phy::sx1276_7_8_9::SX1276_7_8_9;
18use lora_phy::LoRa;
19use {defmt_rtt as _, panic_probe as _};
20
21#[embassy_executor::main]
22async fn main(_spawner: Spawner) {
23 let mut config = embassy_stm32::Config::default();
24 config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI16;
25 config.rcc.enable_hsi48 = true;
26 let p = embassy_stm32::init(config);
27
28 // SPI for sx1276
29 let spi = spi::Spi::new(
30 p.SPI1,
31 p.PB3,
32 p.PA7,
33 p.PA6,
34 p.DMA1_CH3,
35 p.DMA1_CH2,
36 khz(200),
37 spi::Config::default(),
38 );
39
40 let nss = Output::new(p.PA15.degrade(), Level::High, Speed::Low);
41 let reset = Output::new(p.PC0.degrade(), Level::High, Speed::Low);
42
43 let irq_pin = Input::new(p.PB4.degrade(), Pull::Up);
44 let irq = ExtiInput::new(irq_pin, p.EXTI4.degrade());
45
46 let iv = Stm32l0InterfaceVariant::new(nss, reset, irq, None, None).unwrap();
47
48 let mut delay = Delay;
49
50 let mut lora = {
51 match LoRa::new(SX1276_7_8_9::new(BoardType::Stm32l0Sx1276, spi, iv), false, &mut delay).await {
52 Ok(l) => l,
53 Err(err) => {
54 info!("Radio error = {}", err);
55 return;
56 }
57 }
58 };
59
60 let mut debug_indicator = Output::new(p.PB5, Level::Low, Speed::Low);
61 let mut start_indicator = Output::new(p.PB6, Level::Low, Speed::Low);
62
63 start_indicator.set_high();
64 Timer::after(Duration::from_secs(5)).await;
65 start_indicator.set_low();
66
67 let mut receiving_buffer = [00u8; 100];
68
69 let mdltn_params = {
70 match lora.create_modulation_params(SpreadingFactor::_10, Bandwidth::_250KHz, CodingRate::_4_8, 903900000) {
71 Ok(mp) => mp,
72 Err(err) => {
73 info!("Radio error = {}", err);
74 return;
75 }
76 }
77 };
78
79 let rx_pkt_params = {
80 match lora.create_rx_packet_params(4, false, receiving_buffer.len() as u8, true, false, &mdltn_params) {
81 Ok(pp) => pp,
82 Err(err) => {
83 info!("Radio error = {}", err);
84 return;
85 }
86 }
87 };
88
89 match lora
90 .prepare_for_rx(&mdltn_params, &rx_pkt_params, None, true, false, 0, 0x00ffffffu32)
91 .await
92 {
93 Ok(()) => {}
94 Err(err) => {
95 info!("Radio error = {}", err);
96 return;
97 }
98 };
99
100 loop {
101 receiving_buffer = [00u8; 100];
102 match lora.rx(&rx_pkt_params, &mut receiving_buffer).await {
103 Ok((received_len, _rx_pkt_status)) => {
104 if (received_len == 3)
105 && (receiving_buffer[0] == 0x01u8)
106 && (receiving_buffer[1] == 0x02u8)
107 && (receiving_buffer[2] == 0x03u8)
108 {
109 info!("rx successful");
110 debug_indicator.set_high();
111 Timer::after(Duration::from_secs(5)).await;
112 debug_indicator.set_low();
113 } else {
114 info!("rx unknown packet");
115 }
116 }
117 Err(err) => info!("rx unsuccessful = {}", err),
118 }
119 }
120}
diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml
index 07f136b40..80c7bccd4 100644
--- a/examples/stm32wl/Cargo.toml
+++ b/examples/stm32wl/Cargo.toml
@@ -7,12 +7,13 @@ license = "MIT OR Apache-2.0"
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "unstable-pac", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "unstable-pac", "exti"] }
12embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt"] } 12embassy-embedded-hal = {version = "0.1.0", path = "../../embassy-embedded-hal" }
13 13embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt", "external-lora-phy"] }
14lorawan-device = { version = "0.9.0", default-features = false, features = ["async"] } 14lora-phy = { version = "1", path = "../../../lora-phy" }
15lorawan = { version = "0.7.2", default-features = false, features = ["default-crypto"] } 15lorawan-device = { version = "0.9.0", path = "../../../rust-lorawan/device", default-features = false, features = ["async", "external-lora-phy"] }
16lorawan = { version = "0.7.2", path = "../../../rust-lorawan/encoding", default-features = false, features = ["default-crypto"] }
16 17
17defmt = "0.3" 18defmt = "0.3"
18defmt-rtt = "0.4" 19defmt-rtt = "0.4"
diff --git a/examples/stm32wl/src/bin/lora_lorawan.rs b/examples/stm32wl/src/bin/lora_lorawan.rs
new file mode 100644
index 000000000..f7cf03595
--- /dev/null
+++ b/examples/stm32wl/src/bin/lora_lorawan.rs
@@ -0,0 +1,88 @@
1//! This example runs on a STM32WL board, which has a builtin Semtech Sx1262 radio.
2//! It demonstrates LoRaWAN functionality.
3#![no_std]
4#![no_main]
5#![macro_use]
6#![feature(type_alias_impl_trait, async_fn_in_trait)]
7#![allow(incomplete_features)]
8
9use defmt::info;
10use embassy_embedded_hal::adapter::BlockingAsync;
11use embassy_executor::Spawner;
12use embassy_lora::iv::Stm32wlInterfaceVariant;
13use embassy_lora::LoraTimer;
14use embassy_stm32::dma::NoDma;
15use embassy_stm32::gpio::{Level, Output, Pin, Speed};
16use embassy_stm32::peripherals::SUBGHZSPI;
17use embassy_stm32::rcc::low_level::RccPeripheral;
18use embassy_stm32::rng::Rng;
19use embassy_stm32::spi::{BitOrder, Config as SpiConfig, Spi, MODE_0};
20use embassy_stm32::time::Hertz;
21use embassy_stm32::{interrupt, into_ref, pac, Peripheral};
22use embassy_time::Delay;
23use lora_phy::mod_params::*;
24use lora_phy::sx1261_2::SX1261_2;
25use lora_phy::LoRa;
26use lorawan::default_crypto::DefaultFactory as Crypto;
27use lorawan_device::async_device::lora_radio::LoRaRadio;
28use lorawan_device::async_device::{region, Device, JoinMode};
29use {defmt_rtt as _, panic_probe as _};
30
31#[embassy_executor::main]
32async fn main(_spawner: Spawner) {
33 let mut config = embassy_stm32::Config::default();
34 config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI16;
35 config.rcc.enable_lsi = true;
36 let p = embassy_stm32::init(config);
37
38 unsafe { pac::RCC.ccipr().modify(|w| w.set_rngsel(0b01)) }
39
40 let clk = Hertz(core::cmp::min(SUBGHZSPI::frequency().0 / 2, 16_000_000));
41 let mut spi_config = SpiConfig::default();
42 spi_config.mode = MODE_0;
43 spi_config.bit_order = BitOrder::MsbFirst;
44 let spi = Spi::new_subghz(p.SUBGHZSPI, NoDma, NoDma, clk, spi_config);
45
46 let spi = BlockingAsync::new(spi);
47
48 let irq = interrupt::take!(SUBGHZ_RADIO);
49 into_ref!(irq);
50 // Set CTRL1 and CTRL3 for high-power transmission, while CTRL2 acts as an RF switch between tx and rx
51 let _ctrl1 = Output::new(p.PC4.degrade(), Level::Low, Speed::High);
52 let ctrl2 = Output::new(p.PC5.degrade(), Level::High, Speed::High);
53 let _ctrl3 = Output::new(p.PC3.degrade(), Level::High, Speed::High);
54 let iv = Stm32wlInterfaceVariant::new(irq, None, Some(ctrl2)).unwrap();
55
56 let mut delay = Delay;
57
58 let lora = {
59 match LoRa::new(SX1261_2::new(BoardType::Stm32wlSx1262, spi, iv), true, &mut delay).await {
60 Ok(l) => l,
61 Err(err) => {
62 info!("Radio error = {}", err);
63 return;
64 }
65 }
66 };
67 let radio = LoRaRadio::new(lora);
68 let region: region::Configuration = region::Configuration::new(region::Region::EU868);
69 let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), Rng::new(p.RNG));
70
71 defmt::info!("Joining LoRaWAN network");
72
73 // TODO: Adjust the EUI and Keys according to your network credentials
74 match device
75 .join(&JoinMode::OTAA {
76 deveui: [0, 0, 0, 0, 0, 0, 0, 0],
77 appeui: [0, 0, 0, 0, 0, 0, 0, 0],
78 appkey: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
79 })
80 .await
81 {
82 Ok(()) => defmt::info!("LoRaWAN network joined"),
83 Err(err) => {
84 info!("Radio error = {}", err);
85 return;
86 }
87 };
88}
diff --git a/examples/stm32wl/src/bin/lora_p2p_send.rs b/examples/stm32wl/src/bin/lora_p2p_send.rs
new file mode 100644
index 000000000..11c35a8fe
--- /dev/null
+++ b/examples/stm32wl/src/bin/lora_p2p_send.rs
@@ -0,0 +1,103 @@
1//! This example runs on a STM32WL board, which has a builtin Semtech Sx1262 radio.
2//! It demonstrates LORA P2P send functionality.
3#![no_std]
4#![no_main]
5#![macro_use]
6#![feature(type_alias_impl_trait, async_fn_in_trait)]
7#![allow(incomplete_features)]
8
9use defmt::info;
10use embassy_embedded_hal::adapter::BlockingAsync;
11use embassy_executor::Spawner;
12use embassy_lora::iv::Stm32wlInterfaceVariant;
13use embassy_stm32::dma::NoDma;
14use embassy_stm32::gpio::{Level, Output, Pin, Speed};
15use embassy_stm32::peripherals::SUBGHZSPI;
16use embassy_stm32::rcc::low_level::RccPeripheral;
17use embassy_stm32::spi::{BitOrder, Config as SpiConfig, Spi, MODE_0};
18use embassy_stm32::time::Hertz;
19use embassy_stm32::{interrupt, into_ref, Peripheral};
20use embassy_time::Delay;
21use lora_phy::mod_params::*;
22use lora_phy::sx1261_2::SX1261_2;
23use lora_phy::LoRa;
24use {defmt_rtt as _, panic_probe as _};
25
26#[embassy_executor::main]
27async fn main(_spawner: Spawner) {
28 let mut config = embassy_stm32::Config::default();
29 config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSE32;
30 let p = embassy_stm32::init(config);
31
32 let clk = Hertz(core::cmp::min(SUBGHZSPI::frequency().0 / 2, 16_000_000));
33 let mut spi_config = SpiConfig::default();
34 spi_config.mode = MODE_0;
35 spi_config.bit_order = BitOrder::MsbFirst;
36 let spi = Spi::new_subghz(p.SUBGHZSPI, NoDma, NoDma, clk, spi_config);
37
38 let spi = BlockingAsync::new(spi);
39
40 let irq = interrupt::take!(SUBGHZ_RADIO);
41 into_ref!(irq);
42 // Set CTRL1 and CTRL3 for high-power transmission, while CTRL2 acts as an RF switch between tx and rx
43 let _ctrl1 = Output::new(p.PC4.degrade(), Level::Low, Speed::High);
44 let ctrl2 = Output::new(p.PC5.degrade(), Level::High, Speed::High);
45 let _ctrl3 = Output::new(p.PC3.degrade(), Level::High, Speed::High);
46 let iv = Stm32wlInterfaceVariant::new(irq, None, Some(ctrl2)).unwrap();
47
48 let mut delay = Delay;
49
50 let mut lora = {
51 match LoRa::new(SX1261_2::new(BoardType::Stm32wlSx1262, spi, iv), false, &mut delay).await {
52 Ok(l) => l,
53 Err(err) => {
54 info!("Radio error = {}", err);
55 return;
56 }
57 }
58 };
59
60 let mdltn_params = {
61 match lora.create_modulation_params(SpreadingFactor::_10, Bandwidth::_250KHz, CodingRate::_4_8, 903900000) {
62 Ok(mp) => mp,
63 Err(err) => {
64 info!("Radio error = {}", err);
65 return;
66 }
67 }
68 };
69
70 let mut tx_pkt_params = {
71 match lora.create_tx_packet_params(4, false, true, false, &mdltn_params) {
72 Ok(pp) => pp,
73 Err(err) => {
74 info!("Radio error = {}", err);
75 return;
76 }
77 }
78 };
79
80 match lora.prepare_for_tx(&mdltn_params, 20, false).await {
81 Ok(()) => {}
82 Err(err) => {
83 info!("Radio error = {}", err);
84 return;
85 }
86 };
87
88 let buffer = [0x01u8, 0x02u8, 0x03u8];
89 match lora.tx(&mdltn_params, &mut tx_pkt_params, &buffer, 0xffffff).await {
90 Ok(()) => {
91 info!("TX DONE");
92 }
93 Err(err) => {
94 info!("Radio error = {}", err);
95 return;
96 }
97 };
98
99 match lora.sleep(&mut delay).await {
100 Ok(()) => info!("Sleep successful"),
101 Err(err) => info!("Sleep unsuccessful = {}", err),
102 }
103}