aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2023-04-26 20:10:40 +0000
committerGitHub <[email protected]>2023-04-26 20:10:40 +0000
commit759d911b5046b1f6201ea9728f1b42e4a0153659 (patch)
tree2b8305193406caa6d79eed56a453a5447256dadc
parentcb00fb18cbb8dcc83133fffeba42108fee932b38 (diff)
parenta277deeaa563eb1ec78856a3f6866e73ed206c6d (diff)
Merge #1396
1396: Add an external LoRa physical layer feature r=Dirbaio a=ceekdee The original LoRa drivers have been deprecated and examples associated with them deleted; however, the original LoRa drivers are still available to allow a gentle transition to the external lora-phy crate. Co-authored-by: ceekdee <[email protected]> Co-authored-by: Chuck Davis <[email protected]> Co-authored-by: Ulf Lilleengen <[email protected]>
-rw-r--r--embassy-lora/Cargo.toml6
-rw-r--r--embassy-lora/src/iv.rs325
-rw-r--r--embassy-lora/src/lib.rs6
-rw-r--r--embassy-lora/src/stm32wl/mod.rs1
-rw-r--r--embassy-stm32/src/lib.rs1
-rw-r--r--embassy-stm32/src/spi/mod.rs2
-rw-r--r--embassy-stm32/src/subghz/mod.rs2
-rw-r--r--examples/nrf52840/Cargo.toml12
-rw-r--r--examples/nrf52840/src/bin/lora_cad.rs99
-rw-r--r--examples/nrf52840/src/bin/lora_lorawan.rs83
-rw-r--r--examples/nrf52840/src/bin/lora_p2p_receive.rs121
-rw-r--r--examples/nrf52840/src/bin/lora_p2p_receive_duty_cycle.rs131
-rw-r--r--examples/nrf52840/src/bin/lora_p2p_report.rs81
-rw-r--r--examples/nrf52840/src/bin/lora_p2p_send.rs104
-rw-r--r--examples/nrf52840/src/bin/lora_p2p_sense.rs128
-rw-r--r--examples/rp/Cargo.toml6
-rw-r--r--examples/rp/src/bin/lora_lorawan.rs80
-rw-r--r--examples/rp/src/bin/lora_p2p_receive.rs115
-rw-r--r--examples/rp/src/bin/lora_p2p_send.rs103
-rw-r--r--examples/rp/src/bin/lora_p2p_send_multicore.rs139
-rw-r--r--examples/stm32l0/Cargo.toml11
-rw-r--r--examples/stm32l0/src/bin/lora_cad.rs105
-rw-r--r--examples/stm32l0/src/bin/lora_lorawan.rs88
-rw-r--r--examples/stm32l0/src/bin/lora_p2p_receive.rs127
-rw-r--r--examples/stm32l0/src/bin/lora_p2p_send.rs110
-rw-r--r--examples/stm32l0/src/bin/lorawan.rs74
-rw-r--r--examples/stm32wl/Cargo.toml13
-rw-r--r--examples/stm32wl/src/bin/lora_lorawan.rs90
-rw-r--r--examples/stm32wl/src/bin/lora_p2p_receive.rs127
-rw-r--r--examples/stm32wl/src/bin/lora_p2p_send.rs110
-rw-r--r--examples/stm32wl/src/bin/lorawan.rs104
-rw-r--r--examples/stm32wl/src/bin/subghz.rs119
32 files changed, 2095 insertions, 528 deletions
diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml
index 50449dd46..692a82040 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
@@ -38,5 +39,6 @@ futures = { version = "0.3.17", default-features = false, features = [ "async-aw
38embedded-hal = { version = "0.2", features = ["unproven"] } 39embedded-hal = { version = "0.2", features = ["unproven"] }
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", optional = true }
42lorawan = { version = "0.7.2", default-features = false } 43lorawan-device = { version = "0.10.0", default-features = false, features = ["async"] }
44lorawan = { version = "0.7.3", 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..c01d3f71a 100644
--- a/embassy-lora/src/lib.rs
+++ b/embassy-lora/src/lib.rs
@@ -5,12 +5,18 @@
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")]
13#[deprecated(note = "use the external LoRa physical layer crate - https://crates.io/crates/lora-phy")]
10pub mod stm32wl; 14pub mod stm32wl;
11#[cfg(feature = "sx126x")] 15#[cfg(feature = "sx126x")]
16#[deprecated(note = "use the external LoRa physical layer crate - https://crates.io/crates/lora-phy")]
12pub mod sx126x; 17pub mod sx126x;
13#[cfg(feature = "sx127x")] 18#[cfg(feature = "sx127x")]
19#[deprecated(note = "use the external LoRa physical layer crate - https://crates.io/crates/lora-phy")]
14pub mod sx127x; 20pub mod sx127x;
15 21
16#[cfg(feature = "time")] 22#[cfg(feature = "time")]
diff --git a/embassy-lora/src/stm32wl/mod.rs b/embassy-lora/src/stm32wl/mod.rs
index d76e8c43b..dae9a195c 100644
--- a/embassy-lora/src/stm32wl/mod.rs
+++ b/embassy-lora/src/stm32wl/mod.rs
@@ -1,4 +1,5 @@
1//! A radio driver integration for the radio found on STM32WL family devices. 1//! A radio driver integration for the radio found on STM32WL family devices.
2#![allow(deprecated)]
2use core::future::poll_fn; 3use core::future::poll_fn;
3use core::task::Poll; 4use core::task::Poll;
4 5
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index bbde2da57..f0fc152ce 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -56,6 +56,7 @@ pub mod sdmmc;
56#[cfg(spi)] 56#[cfg(spi)]
57pub mod spi; 57pub mod spi;
58#[cfg(stm32wl)] 58#[cfg(stm32wl)]
59#[deprecated(note = "use the external LoRa physical layer crate - https://crates.io/crates/lora-phy")]
59pub mod subghz; 60pub mod subghz;
60#[cfg(usart)] 61#[cfg(usart)]
61pub mod usart; 62pub mod usart;
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..59f30a9be 100644
--- a/examples/nrf52840/Cargo.toml
+++ b/examples/nrf52840/Cargo.toml
@@ -6,8 +6,8 @@ license = "MIT OR Apache-2.0"
6 6
7[features] 7[features]
8default = ["nightly"] 8default = ["nightly"]
9nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embedded-io/async", "embassy-net", 9nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-time/nightly", "embassy-time/unstable-traits",
10 "embassy-lora", "lorawan-device", "lorawan"] 10 "embassy-usb", "embedded-io/async", "embassy-net", "embassy-lora", "lora-phy", "lorawan-device", "lorawan"]
11 11
12[dependencies] 12[dependencies]
13embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 13embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
@@ -18,10 +18,10 @@ embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defm
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", optional = true }
23lorawan-device = { version = "0.9.0", default-features = false, features = ["async"], optional = true } 23lorawan-device = { version = "0.10.0", 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.3", 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..beca061ed
--- /dev/null
+++ b/examples/nrf52840/src/bin/lora_cad.rs
@@ -0,0 +1,99 @@
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 demonstrates 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
20const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region
21
22bind_interrupts!(struct Irqs {
23 SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 => spim::InterruptHandler<peripherals::TWISPI1>;
24});
25
26#[embassy_executor::main]
27async fn main(_spawner: Spawner) {
28 let p = embassy_nrf::init(Default::default());
29 let mut spi_config = spim::Config::default();
30 spi_config.frequency = spim::Frequency::M16;
31
32 let spim = spim::Spim::new(p.TWISPI1, Irqs, p.P1_11, p.P1_13, p.P1_12, spi_config);
33
34 let nss = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard);
35 let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard);
36 let dio1 = Input::new(p.P1_15.degrade(), Pull::Down);
37 let busy = Input::new(p.P1_14.degrade(), Pull::Down);
38 let rf_switch_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard);
39 let rf_switch_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard);
40
41 let iv =
42 GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, Some(rf_switch_rx), Some(rf_switch_tx)).unwrap();
43
44 let mut delay = Delay;
45
46 let mut lora = {
47 match LoRa::new(SX1261_2::new(BoardType::Rak4631Sx1262, spim, iv), false, &mut delay).await {
48 Ok(l) => l,
49 Err(err) => {
50 info!("Radio error = {}", err);
51 return;
52 }
53 }
54 };
55
56 let mut debug_indicator = Output::new(p.P1_03, Level::Low, OutputDrive::Standard);
57 let mut start_indicator = Output::new(p.P1_04, Level::Low, OutputDrive::Standard);
58
59 start_indicator.set_high();
60 Timer::after(Duration::from_secs(5)).await;
61 start_indicator.set_low();
62
63 let mdltn_params = {
64 match lora.create_modulation_params(
65 SpreadingFactor::_10,
66 Bandwidth::_250KHz,
67 CodingRate::_4_8,
68 LORA_FREQUENCY_IN_HZ,
69 ) {
70 Ok(mp) => mp,
71 Err(err) => {
72 info!("Radio error = {}", err);
73 return;
74 }
75 }
76 };
77
78 match lora.prepare_for_cad(&mdltn_params, true).await {
79 Ok(()) => {}
80 Err(err) => {
81 info!("Radio error = {}", err);
82 return;
83 }
84 };
85
86 match lora.cad().await {
87 Ok(cad_activity_detected) => {
88 if cad_activity_detected {
89 info!("cad successful with activity detected")
90 } else {
91 info!("cad successful without activity detected")
92 }
93 debug_indicator.set_high();
94 Timer::after(Duration::from_secs(5)).await;
95 debug_indicator.set_low();
96 }
97 Err(err) => info!("cad unsuccessful = {}", err),
98 }
99}
diff --git a/examples/nrf52840/src/bin/lora_lorawan.rs b/examples/nrf52840/src/bin/lora_lorawan.rs
new file mode 100644
index 000000000..c953680c6
--- /dev/null
+++ b/examples/nrf52840/src/bin/lora_lorawan.rs
@@ -0,0 +1,83 @@
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 demonstrates LoRaWAN join 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_lora::LoraTimer;
13use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull};
14use embassy_nrf::rng::Rng;
15use embassy_nrf::{bind_interrupts, peripherals, rng, spim};
16use embassy_time::Delay;
17use lora_phy::mod_params::*;
18use lora_phy::sx1261_2::SX1261_2;
19use lora_phy::LoRa;
20use lorawan::default_crypto::DefaultFactory as Crypto;
21use lorawan_device::async_device::lora_radio::LoRaRadio;
22use lorawan_device::async_device::{region, Device, JoinMode};
23use {defmt_rtt as _, panic_probe as _};
24
25const LORAWAN_REGION: region::Region = region::Region::EU868; // warning: set this appropriately for the region
26
27bind_interrupts!(struct Irqs {
28 SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 => spim::InterruptHandler<peripherals::TWISPI1>;
29 RNG => rng::InterruptHandler<peripherals::RNG>;
30});
31
32#[embassy_executor::main]
33async fn main(_spawner: Spawner) {
34 let p = embassy_nrf::init(Default::default());
35 let mut spi_config = spim::Config::default();
36 spi_config.frequency = spim::Frequency::M16;
37
38 let spim = spim::Spim::new(p.TWISPI1, Irqs, p.P1_11, p.P1_13, p.P1_12, spi_config);
39
40 let nss = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard);
41 let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard);
42 let dio1 = Input::new(p.P1_15.degrade(), Pull::Down);
43 let busy = Input::new(p.P1_14.degrade(), Pull::Down);
44 let rf_switch_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard);
45 let rf_switch_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard);
46
47 let iv =
48 GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, Some(rf_switch_rx), Some(rf_switch_tx)).unwrap();
49
50 let mut delay = Delay;
51
52 let lora = {
53 match LoRa::new(SX1261_2::new(BoardType::Rak4631Sx1262, spim, iv), true, &mut delay).await {
54 Ok(l) => l,
55 Err(err) => {
56 info!("Radio error = {}", err);
57 return;
58 }
59 }
60 };
61
62 let radio = LoRaRadio::new(lora);
63 let region: region::Configuration = region::Configuration::new(LORAWAN_REGION);
64 let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), Rng::new(p.RNG, Irqs));
65
66 defmt::info!("Joining LoRaWAN network");
67
68 // TODO: Adjust the EUI and Keys according to your network credentials
69 match device
70 .join(&JoinMode::OTAA {
71 deveui: [0, 0, 0, 0, 0, 0, 0, 0],
72 appeui: [0, 0, 0, 0, 0, 0, 0, 0],
73 appkey: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
74 })
75 .await
76 {
77 Ok(()) => defmt::info!("LoRaWAN network joined"),
78 Err(err) => {
79 info!("Radio error = {}", err);
80 return;
81 }
82 };
83}
diff --git a/examples/nrf52840/src/bin/lora_p2p_receive.rs b/examples/nrf52840/src/bin/lora_p2p_receive.rs
new file mode 100644
index 000000000..563fe42ec
--- /dev/null
+++ b/examples/nrf52840/src/bin/lora_p2p_receive.rs
@@ -0,0 +1,121 @@
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 demonstrates LORA P2P receive functionality in conjunction with the lora_p2p_send example.
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
20const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region
21
22bind_interrupts!(struct Irqs {
23 SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 => spim::InterruptHandler<peripherals::TWISPI1>;
24});
25
26#[embassy_executor::main]
27async fn main(_spawner: Spawner) {
28 let p = embassy_nrf::init(Default::default());
29 let mut spi_config = spim::Config::default();
30 spi_config.frequency = spim::Frequency::M16;
31
32 let spim = spim::Spim::new(p.TWISPI1, Irqs, p.P1_11, p.P1_13, p.P1_12, spi_config);
33
34 let nss = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard);
35 let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard);
36 let dio1 = Input::new(p.P1_15.degrade(), Pull::Down);
37 let busy = Input::new(p.P1_14.degrade(), Pull::Down);
38 let rf_switch_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard);
39 let rf_switch_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard);
40
41 let iv =
42 GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, Some(rf_switch_rx), Some(rf_switch_tx)).unwrap();
43
44 let mut delay = Delay;
45
46 let mut lora = {
47 match LoRa::new(SX1261_2::new(BoardType::Rak4631Sx1262, spim, iv), false, &mut delay).await {
48 Ok(l) => l,
49 Err(err) => {
50 info!("Radio error = {}", err);
51 return;
52 }
53 }
54 };
55
56 let mut debug_indicator = Output::new(p.P1_03, Level::Low, OutputDrive::Standard);
57 let mut start_indicator = Output::new(p.P1_04, Level::Low, OutputDrive::Standard);
58
59 start_indicator.set_high();
60 Timer::after(Duration::from_secs(5)).await;
61 start_indicator.set_low();
62
63 let mut receiving_buffer = [00u8; 100];
64
65 let mdltn_params = {
66 match lora.create_modulation_params(
67 SpreadingFactor::_10,
68 Bandwidth::_250KHz,
69 CodingRate::_4_8,
70 LORA_FREQUENCY_IN_HZ,
71 ) {
72 Ok(mp) => mp,
73 Err(err) => {
74 info!("Radio error = {}", err);
75 return;
76 }
77 }
78 };
79
80 let rx_pkt_params = {
81 match lora.create_rx_packet_params(4, false, receiving_buffer.len() as u8, true, false, &mdltn_params) {
82 Ok(pp) => pp,
83 Err(err) => {
84 info!("Radio error = {}", err);
85 return;
86 }
87 }
88 };
89
90 match lora
91 .prepare_for_rx(&mdltn_params, &rx_pkt_params, None, true, false, 0, 0x00ffffffu32)
92 .await
93 {
94 Ok(()) => {}
95 Err(err) => {
96 info!("Radio error = {}", err);
97 return;
98 }
99 };
100
101 loop {
102 receiving_buffer = [00u8; 100];
103 match lora.rx(&rx_pkt_params, &mut receiving_buffer).await {
104 Ok((received_len, _rx_pkt_status)) => {
105 if (received_len == 3)
106 && (receiving_buffer[0] == 0x01u8)
107 && (receiving_buffer[1] == 0x02u8)
108 && (receiving_buffer[2] == 0x03u8)
109 {
110 info!("rx successful");
111 debug_indicator.set_high();
112 Timer::after(Duration::from_secs(5)).await;
113 debug_indicator.set_low();
114 } else {
115 info!("rx unknown packet");
116 }
117 }
118 Err(err) => info!("rx unsuccessful = {}", err),
119 }
120 }
121}
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..1fd8f61a2
--- /dev/null
+++ b/examples/nrf52840/src/bin/lora_p2p_receive_duty_cycle.rs
@@ -0,0 +1,131 @@
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 demonstrates LoRa Rx duty cycle functionality in conjunction with the lora_p2p_send example.
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
20const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region
21
22bind_interrupts!(struct Irqs {
23 SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 => spim::InterruptHandler<peripherals::TWISPI1>;
24});
25
26#[embassy_executor::main]
27async fn main(_spawner: Spawner) {
28 let p = embassy_nrf::init(Default::default());
29 let mut spi_config = spim::Config::default();
30 spi_config.frequency = spim::Frequency::M16;
31
32 let spim = spim::Spim::new(p.TWISPI1, Irqs, p.P1_11, p.P1_13, p.P1_12, spi_config);
33
34 let nss = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard);
35 let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard);
36 let dio1 = Input::new(p.P1_15.degrade(), Pull::Down);
37 let busy = Input::new(p.P1_14.degrade(), Pull::Down);
38 let rf_switch_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard);
39 let rf_switch_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard);
40
41 let iv =
42 GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, Some(rf_switch_rx), Some(rf_switch_tx)).unwrap();
43
44 let mut delay = Delay;
45
46 let mut lora = {
47 match LoRa::new(SX1261_2::new(BoardType::Rak4631Sx1262, spim, iv), false, &mut delay).await {
48 Ok(l) => l,
49 Err(err) => {
50 info!("Radio error = {}", err);
51 return;
52 }
53 }
54 };
55
56 let mut debug_indicator = Output::new(p.P1_03, Level::Low, OutputDrive::Standard);
57 let mut start_indicator = Output::new(p.P1_04, Level::Low, OutputDrive::Standard);
58
59 start_indicator.set_high();
60 Timer::after(Duration::from_secs(5)).await;
61 start_indicator.set_low();
62
63 let mut receiving_buffer = [00u8; 100];
64
65 let mdltn_params = {
66 match lora.create_modulation_params(
67 SpreadingFactor::_10,
68 Bandwidth::_250KHz,
69 CodingRate::_4_8,
70 LORA_FREQUENCY_IN_HZ,
71 ) {
72 Ok(mp) => mp,
73 Err(err) => {
74 info!("Radio error = {}", err);
75 return;
76 }
77 }
78 };
79
80 let rx_pkt_params = {
81 match lora.create_rx_packet_params(4, false, receiving_buffer.len() as u8, true, false, &mdltn_params) {
82 Ok(pp) => pp,
83 Err(err) => {
84 info!("Radio error = {}", err);
85 return;
86 }
87 }
88 };
89
90 // 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.
91 match lora
92 .prepare_for_rx(
93 &mdltn_params,
94 &rx_pkt_params,
95 Some(&DutyCycleParams {
96 rx_time: 300_000, // 300_000 units * 15.625 us/unit = 4.69 s
97 sleep_time: 200_000, // 200_000 units * 15.625 us/unit = 3.13 s
98 }),
99 false,
100 false,
101 0,
102 0,
103 )
104 .await
105 {
106 Ok(()) => {}
107 Err(err) => {
108 info!("Radio error = {}", err);
109 return;
110 }
111 };
112
113 receiving_buffer = [00u8; 100];
114 match lora.rx(&rx_pkt_params, &mut receiving_buffer).await {
115 Ok((received_len, _rx_pkt_status)) => {
116 if (received_len == 3)
117 && (receiving_buffer[0] == 0x01u8)
118 && (receiving_buffer[1] == 0x02u8)
119 && (receiving_buffer[2] == 0x03u8)
120 {
121 info!("rx successful");
122 debug_indicator.set_high();
123 Timer::after(Duration::from_secs(5)).await;
124 debug_indicator.set_low();
125 } else {
126 info!("rx unknown packet")
127 }
128 }
129 Err(err) => info!("rx unsuccessful = {}", err),
130 }
131}
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/nrf52840/src/bin/lora_p2p_send.rs b/examples/nrf52840/src/bin/lora_p2p_send.rs
new file mode 100644
index 000000000..1c8bbc27a
--- /dev/null
+++ b/examples/nrf52840/src/bin/lora_p2p_send.rs
@@ -0,0 +1,104 @@
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 demonstrates LORA P2P send 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;
15use lora_phy::mod_params::*;
16use lora_phy::sx1261_2::SX1261_2;
17use lora_phy::LoRa;
18use {defmt_rtt as _, panic_probe as _};
19
20const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region
21
22bind_interrupts!(struct Irqs {
23 SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 => spim::InterruptHandler<peripherals::TWISPI1>;
24});
25
26#[embassy_executor::main]
27async fn main(_spawner: Spawner) {
28 let p = embassy_nrf::init(Default::default());
29 let mut spi_config = spim::Config::default();
30 spi_config.frequency = spim::Frequency::M16;
31
32 let spim = spim::Spim::new(p.TWISPI1, Irqs, p.P1_11, p.P1_13, p.P1_12, spi_config);
33
34 let nss = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard);
35 let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard);
36 let dio1 = Input::new(p.P1_15.degrade(), Pull::Down);
37 let busy = Input::new(p.P1_14.degrade(), Pull::Down);
38 let rf_switch_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard);
39 let rf_switch_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard);
40
41 let iv =
42 GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, Some(rf_switch_rx), Some(rf_switch_tx)).unwrap();
43
44 let mut delay = Delay;
45
46 let mut lora = {
47 match LoRa::new(SX1261_2::new(BoardType::Rak4631Sx1262, spim, iv), false, &mut delay).await {
48 Ok(l) => l,
49 Err(err) => {
50 info!("Radio error = {}", err);
51 return;
52 }
53 }
54 };
55
56 let mdltn_params = {
57 match lora.create_modulation_params(
58 SpreadingFactor::_10,
59 Bandwidth::_250KHz,
60 CodingRate::_4_8,
61 LORA_FREQUENCY_IN_HZ,
62 ) {
63 Ok(mp) => mp,
64 Err(err) => {
65 info!("Radio error = {}", err);
66 return;
67 }
68 }
69 };
70
71 let mut tx_pkt_params = {
72 match lora.create_tx_packet_params(4, false, true, false, &mdltn_params) {
73 Ok(pp) => pp,
74 Err(err) => {
75 info!("Radio error = {}", err);
76 return;
77 }
78 }
79 };
80
81 match lora.prepare_for_tx(&mdltn_params, 20, false).await {
82 Ok(()) => {}
83 Err(err) => {
84 info!("Radio error = {}", err);
85 return;
86 }
87 };
88
89 let buffer = [0x01u8, 0x02u8, 0x03u8];
90 match lora.tx(&mdltn_params, &mut tx_pkt_params, &buffer, 0xffffff).await {
91 Ok(()) => {
92 info!("TX DONE");
93 }
94 Err(err) => {
95 info!("Radio error = {}", err);
96 return;
97 }
98 };
99
100 match lora.sleep(&mut delay).await {
101 Ok(()) => info!("Sleep successful"),
102 Err(err) => info!("Sleep unsuccessful = {}", err),
103 }
104}
diff --git a/examples/nrf52840/src/bin/lora_p2p_sense.rs b/examples/nrf52840/src/bin/lora_p2p_sense.rs
deleted file mode 100644
index b6f41ffcc..000000000
--- a/examples/nrf52840/src/bin/lora_p2p_sense.rs
+++ /dev/null
@@ -1,128 +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_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
11use defmt::*;
12use embassy_executor::Spawner;
13use embassy_lora::sx126x::*;
14use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull};
15use embassy_nrf::{bind_interrupts, peripherals, spim};
16use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
17use embassy_sync::pubsub::{PubSubChannel, Publisher};
18use embassy_time::{Duration, Timer};
19use lorawan_device::async_device::radio::{Bandwidth, CodingRate, PhyRxTx, RfConfig, SpreadingFactor, TxConfig};
20use {defmt_rtt as _, panic_probe as _, panic_probe as _};
21
22bind_interrupts!(struct Irqs {
23 SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 => spim::InterruptHandler<peripherals::TWISPI1>;
24});
25
26// Message bus: queue of 2, 1 subscriber (Lora P2P), 2 publishers (temperature, motion detection)
27static MESSAGE_BUS: PubSubChannel<CriticalSectionRawMutex, Message, 2, 1, 2> = PubSubChannel::new();
28
29#[derive(Clone, defmt::Format)]
30enum Message {
31 Temperature(i32),
32 MotionDetected,
33}
34
35#[embassy_executor::task]
36async fn temperature_task(publisher: Publisher<'static, CriticalSectionRawMutex, Message, 2, 1, 2>) {
37 // Publish a fake temperature every 43 seconds, minimizing LORA traffic.
38 loop {
39 Timer::after(Duration::from_secs(43)).await;
40 publisher.publish(Message::Temperature(9)).await;
41 }
42}
43
44#[embassy_executor::task]
45async fn motion_detection_task(publisher: Publisher<'static, CriticalSectionRawMutex, Message, 2, 1, 2>) {
46 // Publish a fake motion detection every 79 seconds, minimizing LORA traffic.
47 loop {
48 Timer::after(Duration::from_secs(79)).await;
49 publisher.publish(Message::MotionDetected).await;
50 }
51}
52
53#[embassy_executor::main]
54async fn main(spawner: Spawner) {
55 let p = embassy_nrf::init(Default::default());
56 // set up to funnel temperature and motion detection events to the Lora Tx task
57 let mut lora_tx_subscriber = unwrap!(MESSAGE_BUS.subscriber());
58 let temperature_publisher = unwrap!(MESSAGE_BUS.publisher());
59 let motion_detection_publisher = unwrap!(MESSAGE_BUS.publisher());
60
61 let mut spi_config = spim::Config::default();
62 spi_config.frequency = spim::Frequency::M16;
63
64 let mut radio = {
65 let spim = spim::Spim::new(p.TWISPI1, Irqs, p.P1_11, p.P1_13, p.P1_12, spi_config);
66
67 let cs = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard);
68 let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard);
69 let dio1 = Input::new(p.P1_15.degrade(), Pull::Down);
70 let busy = Input::new(p.P1_14.degrade(), Pull::Down);
71 let antenna_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard);
72 let antenna_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard);
73
74 match Sx126xRadio::new(spim, cs, reset, antenna_rx, antenna_tx, dio1, busy, false).await {
75 Ok(r) => r,
76 Err(err) => {
77 info!("Sx126xRadio error = {}", err);
78 return;
79 }
80 }
81 };
82
83 let mut start_indicator = Output::new(p.P1_04, Level::Low, OutputDrive::Standard);
84
85 start_indicator.set_high();
86 Timer::after(Duration::from_secs(5)).await;
87 start_indicator.set_low();
88
89 match radio.lora.sleep().await {
90 Ok(()) => info!("Sleep successful"),
91 Err(err) => info!("Sleep unsuccessful = {}", err),
92 }
93
94 unwrap!(spawner.spawn(temperature_task(temperature_publisher)));
95 unwrap!(spawner.spawn(motion_detection_task(motion_detection_publisher)));
96
97 loop {
98 let message = lora_tx_subscriber.next_message_pure().await;
99
100 let tx_config = TxConfig {
101 // 11 byte maximum payload for Bandwidth 125 and SF 10
102 pw: 10, // up to 20
103 rf: RfConfig {
104 frequency: 903900000, // channel in Hz, not MHz
105 bandwidth: Bandwidth::_250KHz,
106 spreading_factor: SpreadingFactor::_10,
107 coding_rate: CodingRate::_4_8,
108 },
109 };
110
111 let mut buffer = [0x00u8];
112 match message {
113 Message::Temperature(temperature) => buffer[0] = temperature as u8,
114 Message::MotionDetected => buffer[0] = 0x01u8,
115 };
116
117 // unencrypted
118 match radio.tx(tx_config, &buffer).await {
119 Ok(ret_val) => info!("TX ret_val = {}", ret_val),
120 Err(err) => info!("TX error = {}", err),
121 }
122
123 match radio.lora.sleep().await {
124 Ok(()) => info!("Sleep successful"),
125 Err(err) => info!("Sleep unsuccessful = {}", err),
126 }
127 }
128}
diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml
index 8067f7ba5..45af8762e 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" }
20lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"] }
21lorawan = { version = "0.7.3", 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/rp/src/bin/lora_lorawan.rs b/examples/rp/src/bin/lora_lorawan.rs
new file mode 100644
index 000000000..a9c84bf95
--- /dev/null
+++ b/examples/rp/src/bin/lora_lorawan.rs
@@ -0,0 +1,80 @@
1//! This example runs on the Raspberry Pi Pico with a Waveshare board containing a Semtech Sx1262 radio.
2//! It demonstrates LoRaWAN join 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::GenericSx126xInterfaceVariant;
11use embassy_lora::LoraTimer;
12use embassy_rp::gpio::{Input, Level, Output, Pin, Pull};
13use embassy_rp::spi::{Config, Spi};
14use embassy_time::Delay;
15use lora_phy::mod_params::*;
16use lora_phy::sx1261_2::SX1261_2;
17use lora_phy::LoRa;
18use lorawan::default_crypto::DefaultFactory as Crypto;
19use lorawan_device::async_device::lora_radio::LoRaRadio;
20use lorawan_device::async_device::{region, Device, JoinMode};
21use {defmt_rtt as _, panic_probe as _};
22
23const LORAWAN_REGION: region::Region = region::Region::EU868; // warning: set this appropriately for the region
24
25#[embassy_executor::main]
26async fn main(_spawner: Spawner) {
27 let p = embassy_rp::init(Default::default());
28
29 let miso = p.PIN_12;
30 let mosi = p.PIN_11;
31 let clk = p.PIN_10;
32 let spi = Spi::new(p.SPI1, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default());
33
34 let nss = Output::new(p.PIN_3.degrade(), Level::High);
35 let reset = Output::new(p.PIN_15.degrade(), Level::High);
36 let dio1 = Input::new(p.PIN_20.degrade(), Pull::None);
37 let busy = Input::new(p.PIN_2.degrade(), Pull::None);
38
39 let iv = GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, None, None).unwrap();
40
41 let mut delay = Delay;
42
43 let lora = {
44 match LoRa::new(
45 SX1261_2::new(BoardType::RpPicoWaveshareSx1262, spi, iv),
46 true,
47 &mut delay,
48 )
49 .await
50 {
51 Ok(l) => l,
52 Err(err) => {
53 info!("Radio error = {}", err);
54 return;
55 }
56 }
57 };
58
59 let radio = LoRaRadio::new(lora);
60 let region: region::Configuration = region::Configuration::new(LORAWAN_REGION);
61 let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), embassy_rp::clocks::RoscRng);
62
63 defmt::info!("Joining LoRaWAN network");
64
65 // TODO: Adjust the EUI and Keys according to your network credentials
66 match device
67 .join(&JoinMode::OTAA {
68 deveui: [0, 0, 0, 0, 0, 0, 0, 0],
69 appeui: [0, 0, 0, 0, 0, 0, 0, 0],
70 appkey: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
71 })
72 .await
73 {
74 Ok(()) => defmt::info!("LoRaWAN network joined"),
75 Err(err) => {
76 info!("Radio error = {}", err);
77 return;
78 }
79 };
80}
diff --git a/examples/rp/src/bin/lora_p2p_receive.rs b/examples/rp/src/bin/lora_p2p_receive.rs
new file mode 100644
index 000000000..250419202
--- /dev/null
+++ b/examples/rp/src/bin/lora_p2p_receive.rs
@@ -0,0 +1,115 @@
1//! This example runs on the Raspberry Pi Pico with a Waveshare board containing a Semtech Sx1262 radio.
2//! It demonstrates LORA P2P receive functionality in conjunction with the lora_p2p_send example.
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::GenericSx126xInterfaceVariant;
11use embassy_rp::gpio::{Input, Level, Output, Pin, Pull};
12use embassy_rp::spi::{Config, Spi};
13use embassy_time::{Delay, Duration, Timer};
14use lora_phy::mod_params::*;
15use lora_phy::sx1261_2::SX1261_2;
16use lora_phy::LoRa;
17use {defmt_rtt as _, panic_probe as _};
18
19const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region
20
21#[embassy_executor::main]
22async fn main(_spawner: Spawner) {
23 let p = embassy_rp::init(Default::default());
24
25 let miso = p.PIN_12;
26 let mosi = p.PIN_11;
27 let clk = p.PIN_10;
28 let spi = Spi::new(p.SPI1, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default());
29
30 let nss = Output::new(p.PIN_3.degrade(), Level::High);
31 let reset = Output::new(p.PIN_15.degrade(), Level::High);
32 let dio1 = Input::new(p.PIN_20.degrade(), Pull::None);
33 let busy = Input::new(p.PIN_2.degrade(), Pull::None);
34
35 let iv = GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, None, None).unwrap();
36
37 let mut delay = Delay;
38
39 let mut lora = {
40 match LoRa::new(
41 SX1261_2::new(BoardType::RpPicoWaveshareSx1262, spi, iv),
42 false,
43 &mut delay,
44 )
45 .await
46 {
47 Ok(l) => l,
48 Err(err) => {
49 info!("Radio error = {}", err);
50 return;
51 }
52 }
53 };
54
55 let mut debug_indicator = Output::new(p.PIN_25, Level::Low);
56
57 let mut receiving_buffer = [00u8; 100];
58
59 let mdltn_params = {
60 match lora.create_modulation_params(
61 SpreadingFactor::_10,
62 Bandwidth::_250KHz,
63 CodingRate::_4_8,
64 LORA_FREQUENCY_IN_HZ,
65 ) {
66 Ok(mp) => mp,
67 Err(err) => {
68 info!("Radio error = {}", err);
69 return;
70 }
71 }
72 };
73
74 let rx_pkt_params = {
75 match lora.create_rx_packet_params(4, false, receiving_buffer.len() as u8, true, false, &mdltn_params) {
76 Ok(pp) => pp,
77 Err(err) => {
78 info!("Radio error = {}", err);
79 return;
80 }
81 }
82 };
83
84 match lora
85 .prepare_for_rx(&mdltn_params, &rx_pkt_params, None, true, false, 0, 0x00ffffffu32)
86 .await
87 {
88 Ok(()) => {}
89 Err(err) => {
90 info!("Radio error = {}", err);
91 return;
92 }
93 };
94
95 loop {
96 receiving_buffer = [00u8; 100];
97 match lora.rx(&rx_pkt_params, &mut receiving_buffer).await {
98 Ok((received_len, _rx_pkt_status)) => {
99 if (received_len == 3)
100 && (receiving_buffer[0] == 0x01u8)
101 && (receiving_buffer[1] == 0x02u8)
102 && (receiving_buffer[2] == 0x03u8)
103 {
104 info!("rx successful");
105 debug_indicator.set_high();
106 Timer::after(Duration::from_secs(5)).await;
107 debug_indicator.set_low();
108 } else {
109 info!("rx unknown packet");
110 }
111 }
112 Err(err) => info!("rx unsuccessful = {}", err),
113 }
114 }
115}
diff --git a/examples/rp/src/bin/lora_p2p_send.rs b/examples/rp/src/bin/lora_p2p_send.rs
new file mode 100644
index 000000000..3a0544b17
--- /dev/null
+++ b/examples/rp/src/bin/lora_p2p_send.rs
@@ -0,0 +1,103 @@
1//! This example runs on the Raspberry Pi Pico with a Waveshare board containing a 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)]
7
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_lora::iv::GenericSx126xInterfaceVariant;
11use embassy_rp::gpio::{Input, Level, Output, Pin, Pull};
12use embassy_rp::spi::{Config, Spi};
13use embassy_time::Delay;
14use lora_phy::mod_params::*;
15use lora_phy::sx1261_2::SX1261_2;
16use lora_phy::LoRa;
17use {defmt_rtt as _, panic_probe as _};
18
19const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region
20
21#[embassy_executor::main]
22async fn main(_spawner: Spawner) {
23 let p = embassy_rp::init(Default::default());
24
25 let miso = p.PIN_12;
26 let mosi = p.PIN_11;
27 let clk = p.PIN_10;
28 let spi = Spi::new(p.SPI1, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default());
29
30 let nss = Output::new(p.PIN_3.degrade(), Level::High);
31 let reset = Output::new(p.PIN_15.degrade(), Level::High);
32 let dio1 = Input::new(p.PIN_20.degrade(), Pull::None);
33 let busy = Input::new(p.PIN_2.degrade(), Pull::None);
34
35 let iv = GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, None, None).unwrap();
36
37 let mut delay = Delay;
38
39 let mut lora = {
40 match LoRa::new(
41 SX1261_2::new(BoardType::RpPicoWaveshareSx1262, spi, iv),
42 false,
43 &mut delay,
44 )
45 .await
46 {
47 Ok(l) => l,
48 Err(err) => {
49 info!("Radio error = {}", err);
50 return;
51 }
52 }
53 };
54
55 let mdltn_params = {
56 match lora.create_modulation_params(
57 SpreadingFactor::_10,
58 Bandwidth::_250KHz,
59 CodingRate::_4_8,
60 LORA_FREQUENCY_IN_HZ,
61 ) {
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}
diff --git a/examples/rp/src/bin/lora_p2p_send_multicore.rs b/examples/rp/src/bin/lora_p2p_send_multicore.rs
new file mode 100644
index 000000000..5585606d8
--- /dev/null
+++ b/examples/rp/src/bin/lora_p2p_send_multicore.rs
@@ -0,0 +1,139 @@
1//! This example runs on the Raspberry Pi Pico with a Waveshare board containing a Semtech Sx1262 radio.
2//! It demonstrates LORA P2P send functionality using the second core, with data provided by the first core.
3#![no_std]
4#![no_main]
5#![macro_use]
6#![feature(type_alias_impl_trait)]
7
8use defmt::*;
9use embassy_executor::Executor;
10use embassy_executor::_export::StaticCell;
11use embassy_lora::iv::GenericSx126xInterfaceVariant;
12use embassy_rp::gpio::{AnyPin, Input, Level, Output, Pin, Pull};
13use embassy_rp::multicore::{spawn_core1, Stack};
14use embassy_rp::peripherals::SPI1;
15use embassy_rp::spi::{Async, Config, Spi};
16use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
17use embassy_sync::channel::Channel;
18use embassy_time::{Delay, Duration, Timer};
19use lora_phy::mod_params::*;
20use lora_phy::sx1261_2::SX1261_2;
21use lora_phy::LoRa;
22use {defmt_rtt as _, panic_probe as _};
23
24static mut CORE1_STACK: Stack<4096> = Stack::new();
25static EXECUTOR0: StaticCell<Executor> = StaticCell::new();
26static EXECUTOR1: StaticCell<Executor> = StaticCell::new();
27static CHANNEL: Channel<CriticalSectionRawMutex, [u8; 3], 1> = Channel::new();
28
29const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region
30
31#[cortex_m_rt::entry]
32fn main() -> ! {
33 let p = embassy_rp::init(Default::default());
34
35 let miso = p.PIN_12;
36 let mosi = p.PIN_11;
37 let clk = p.PIN_10;
38 let spi = Spi::new(p.SPI1, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default());
39
40 let nss = Output::new(p.PIN_3.degrade(), Level::High);
41 let reset = Output::new(p.PIN_15.degrade(), Level::High);
42 let dio1 = Input::new(p.PIN_20.degrade(), Pull::None);
43 let busy = Input::new(p.PIN_2.degrade(), Pull::None);
44
45 let iv = GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, None, None).unwrap();
46
47 spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || {
48 let executor1 = EXECUTOR1.init(Executor::new());
49 executor1.run(|spawner| unwrap!(spawner.spawn(core1_task(spi, iv))));
50 });
51
52 let executor0 = EXECUTOR0.init(Executor::new());
53 executor0.run(|spawner| unwrap!(spawner.spawn(core0_task())));
54}
55
56#[embassy_executor::task]
57async fn core0_task() {
58 info!("Hello from core 0");
59 loop {
60 CHANNEL.send([0x01u8, 0x02u8, 0x03u8]).await;
61 Timer::after(Duration::from_millis(60 * 1000)).await;
62 }
63}
64
65#[embassy_executor::task]
66async fn core1_task(
67 spi: Spi<'static, SPI1, Async>,
68 iv: GenericSx126xInterfaceVariant<Output<'static, AnyPin>, Input<'static, AnyPin>>,
69) {
70 info!("Hello from core 1");
71 let mut delay = Delay;
72
73 let mut lora = {
74 match LoRa::new(
75 SX1261_2::new(BoardType::RpPicoWaveshareSx1262, spi, iv),
76 false,
77 &mut delay,
78 )
79 .await
80 {
81 Ok(l) => l,
82 Err(err) => {
83 info!("Radio error = {}", err);
84 return;
85 }
86 }
87 };
88
89 let mdltn_params = {
90 match lora.create_modulation_params(
91 SpreadingFactor::_10,
92 Bandwidth::_250KHz,
93 CodingRate::_4_8,
94 LORA_FREQUENCY_IN_HZ,
95 ) {
96 Ok(mp) => mp,
97 Err(err) => {
98 info!("Radio error = {}", err);
99 return;
100 }
101 }
102 };
103
104 let mut tx_pkt_params = {
105 match lora.create_tx_packet_params(4, false, true, false, &mdltn_params) {
106 Ok(pp) => pp,
107 Err(err) => {
108 info!("Radio error = {}", err);
109 return;
110 }
111 }
112 };
113
114 loop {
115 let buffer: [u8; 3] = CHANNEL.recv().await;
116 match lora.prepare_for_tx(&mdltn_params, 20, false).await {
117 Ok(()) => {}
118 Err(err) => {
119 info!("Radio error = {}", err);
120 return;
121 }
122 };
123
124 match lora.tx(&mdltn_params, &mut tx_pkt_params, &buffer, 0xffffff).await {
125 Ok(()) => {
126 info!("TX DONE");
127 }
128 Err(err) => {
129 info!("Radio error = {}", err);
130 return;
131 }
132 };
133
134 match lora.sleep(&mut delay).await {
135 Ok(()) => info!("Sleep successful"),
136 Err(err) => info!("Sleep unsuccessful = {}", err),
137 }
138 }
139}
diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml
index d08e2b61a..ca022e254 100644
--- a/examples/stm32l0/Cargo.toml
+++ b/examples/stm32l0/Cargo.toml
@@ -6,17 +6,18 @@ license = "MIT OR Apache-2.0"
6 6
7[features] 7[features]
8default = ["nightly"] 8default = ["nightly"]
9nightly = ["embassy-stm32/nightly", "embassy-lora", "lorawan-device", "lorawan", "embedded-io/async"] 9nightly = ["embassy-stm32/nightly", "embassy-time/nightly", "embassy-time/unstable-traits",
10 "embassy-lora", "lora-phy", "lorawan-device", "lorawan", "embedded-io/async"]
10 11
11[dependencies] 12[dependencies]
12embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } 13embassy-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"] } 14embassy-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"] } 15embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["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"] } 16embassy-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} 17embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt", "external-lora-phy"], optional = true }
17 18lora-phy = { version = "1", optional = true }
18lorawan-device = { version = "0.9.0", default-features = false, features = ["async"], optional = true } 19lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"], optional = true }
19lorawan = { version = "0.7.2", default-features = false, features = ["default-crypto"], optional = true } 20lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"], optional = true }
20 21
21defmt = "0.3" 22defmt = "0.3"
22defmt-rtt = "0.4" 23defmt-rtt = "0.4"
diff --git a/examples/stm32l0/src/bin/lora_cad.rs b/examples/stm32l0/src/bin/lora_cad.rs
new file mode 100644
index 000000000..588cea1e5
--- /dev/null
+++ b/examples/stm32l0/src/bin/lora_cad.rs
@@ -0,0 +1,105 @@
1//! This example runs on the STM32 LoRa Discovery board, which has a builtin Semtech Sx1276 radio.
2//! It demonstrates LORA P2P CAD 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
21const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region
22
23#[embassy_executor::main]
24async fn main(_spawner: Spawner) {
25 let mut config = embassy_stm32::Config::default();
26 config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI16;
27 config.rcc.enable_hsi48 = true;
28 let p = embassy_stm32::init(config);
29
30 // SPI for sx1276
31 let spi = spi::Spi::new(
32 p.SPI1,
33 p.PB3,
34 p.PA7,
35 p.PA6,
36 p.DMA1_CH3,
37 p.DMA1_CH2,
38 khz(200),
39 spi::Config::default(),
40 );
41
42 let nss = Output::new(p.PA15.degrade(), Level::High, Speed::Low);
43 let reset = Output::new(p.PC0.degrade(), Level::High, Speed::Low);
44
45 let irq_pin = Input::new(p.PB4.degrade(), Pull::Up);
46 let irq = ExtiInput::new(irq_pin, p.EXTI4.degrade());
47
48 let iv = Stm32l0InterfaceVariant::new(nss, reset, irq, None, None).unwrap();
49
50 let mut delay = Delay;
51
52 let mut lora = {
53 match LoRa::new(SX1276_7_8_9::new(BoardType::Stm32l0Sx1276, spi, iv), false, &mut delay).await {
54 Ok(l) => l,
55 Err(err) => {
56 info!("Radio error = {}", err);
57 return;
58 }
59 }
60 };
61
62 let mut debug_indicator = Output::new(p.PB5, Level::Low, Speed::Low);
63 let mut start_indicator = Output::new(p.PB6, Level::Low, Speed::Low);
64
65 start_indicator.set_high();
66 Timer::after(Duration::from_secs(5)).await;
67 start_indicator.set_low();
68
69 let mdltn_params = {
70 match lora.create_modulation_params(
71 SpreadingFactor::_10,
72 Bandwidth::_250KHz,
73 CodingRate::_4_8,
74 LORA_FREQUENCY_IN_HZ,
75 ) {
76 Ok(mp) => mp,
77 Err(err) => {
78 info!("Radio error = {}", err);
79 return;
80 }
81 }
82 };
83
84 match lora.prepare_for_cad(&mdltn_params, true).await {
85 Ok(()) => {}
86 Err(err) => {
87 info!("Radio error = {}", err);
88 return;
89 }
90 };
91
92 match lora.cad().await {
93 Ok(cad_activity_detected) => {
94 if cad_activity_detected {
95 info!("cad successful with activity detected")
96 } else {
97 info!("cad successful without activity detected")
98 }
99 debug_indicator.set_high();
100 Timer::after(Duration::from_secs(5)).await;
101 debug_indicator.set_low();
102 }
103 Err(err) => info!("cad unsuccessful = {}", err),
104 }
105}
diff --git a/examples/stm32l0/src/bin/lora_lorawan.rs b/examples/stm32l0/src/bin/lora_lorawan.rs
new file mode 100644
index 000000000..c397edd58
--- /dev/null
+++ b/examples/stm32l0/src/bin/lora_lorawan.rs
@@ -0,0 +1,88 @@
1//! This example runs on the STM32 LoRa Discovery board, which has a builtin Semtech Sx1276 radio.
2//! It demonstrates LoRaWAN join 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_lora::LoraTimer;
12use embassy_stm32::exti::{Channel, ExtiInput};
13use embassy_stm32::gpio::{Input, Level, Output, Pin, Pull, Speed};
14use embassy_stm32::rng::Rng;
15use embassy_stm32::spi;
16use embassy_stm32::time::khz;
17use embassy_time::Delay;
18use lora_phy::mod_params::*;
19use lora_phy::sx1276_7_8_9::SX1276_7_8_9;
20use lora_phy::LoRa;
21use lorawan::default_crypto::DefaultFactory as Crypto;
22use lorawan_device::async_device::lora_radio::LoRaRadio;
23use lorawan_device::async_device::{region, Device, JoinMode};
24use {defmt_rtt as _, panic_probe as _};
25
26const LORAWAN_REGION: region::Region = region::Region::EU868; // warning: set this appropriately for the region
27
28#[embassy_executor::main]
29async fn main(_spawner: Spawner) {
30 let mut config = embassy_stm32::Config::default();
31 config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI16;
32 config.rcc.enable_hsi48 = true;
33 let p = embassy_stm32::init(config);
34
35 // SPI for sx1276
36 let spi = spi::Spi::new(
37 p.SPI1,
38 p.PB3,
39 p.PA7,
40 p.PA6,
41 p.DMA1_CH3,
42 p.DMA1_CH2,
43 khz(200),
44 spi::Config::default(),
45 );
46
47 let nss = Output::new(p.PA15.degrade(), Level::High, Speed::Low);
48 let reset = Output::new(p.PC0.degrade(), Level::High, Speed::Low);
49
50 let irq_pin = Input::new(p.PB4.degrade(), Pull::Up);
51 let irq = ExtiInput::new(irq_pin, p.EXTI4.degrade());
52
53 let iv = Stm32l0InterfaceVariant::new(nss, reset, irq, None, None).unwrap();
54
55 let mut delay = Delay;
56
57 let lora = {
58 match LoRa::new(SX1276_7_8_9::new(BoardType::Stm32l0Sx1276, spi, iv), true, &mut delay).await {
59 Ok(l) => l,
60 Err(err) => {
61 info!("Radio error = {}", err);
62 return;
63 }
64 }
65 };
66
67 let radio = LoRaRadio::new(lora);
68 let region: region::Configuration = region::Configuration::new(LORAWAN_REGION);
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/stm32l0/src/bin/lora_p2p_receive.rs b/examples/stm32l0/src/bin/lora_p2p_receive.rs
new file mode 100644
index 000000000..bb7509509
--- /dev/null
+++ b/examples/stm32l0/src/bin/lora_p2p_receive.rs
@@ -0,0 +1,127 @@
1//! This example runs on the STM32 LoRa Discovery board, which has a builtin Semtech Sx1276 radio.
2//! It demonstrates LORA P2P receive functionality in conjunction with the lora_p2p_send example.
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
21const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region
22
23#[embassy_executor::main]
24async fn main(_spawner: Spawner) {
25 let mut config = embassy_stm32::Config::default();
26 config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI16;
27 config.rcc.enable_hsi48 = true;
28 let p = embassy_stm32::init(config);
29
30 // SPI for sx1276
31 let spi = spi::Spi::new(
32 p.SPI1,
33 p.PB3,
34 p.PA7,
35 p.PA6,
36 p.DMA1_CH3,
37 p.DMA1_CH2,
38 khz(200),
39 spi::Config::default(),
40 );
41
42 let nss = Output::new(p.PA15.degrade(), Level::High, Speed::Low);
43 let reset = Output::new(p.PC0.degrade(), Level::High, Speed::Low);
44
45 let irq_pin = Input::new(p.PB4.degrade(), Pull::Up);
46 let irq = ExtiInput::new(irq_pin, p.EXTI4.degrade());
47
48 let iv = Stm32l0InterfaceVariant::new(nss, reset, irq, None, None).unwrap();
49
50 let mut delay = Delay;
51
52 let mut lora = {
53 match LoRa::new(SX1276_7_8_9::new(BoardType::Stm32l0Sx1276, spi, iv), false, &mut delay).await {
54 Ok(l) => l,
55 Err(err) => {
56 info!("Radio error = {}", err);
57 return;
58 }
59 }
60 };
61
62 let mut debug_indicator = Output::new(p.PB5, Level::Low, Speed::Low);
63 let mut start_indicator = Output::new(p.PB6, Level::Low, Speed::Low);
64
65 start_indicator.set_high();
66 Timer::after(Duration::from_secs(5)).await;
67 start_indicator.set_low();
68
69 let mut receiving_buffer = [00u8; 100];
70
71 let mdltn_params = {
72 match lora.create_modulation_params(
73 SpreadingFactor::_10,
74 Bandwidth::_250KHz,
75 CodingRate::_4_8,
76 LORA_FREQUENCY_IN_HZ,
77 ) {
78 Ok(mp) => mp,
79 Err(err) => {
80 info!("Radio error = {}", err);
81 return;
82 }
83 }
84 };
85
86 let rx_pkt_params = {
87 match lora.create_rx_packet_params(4, false, receiving_buffer.len() as u8, true, false, &mdltn_params) {
88 Ok(pp) => pp,
89 Err(err) => {
90 info!("Radio error = {}", err);
91 return;
92 }
93 }
94 };
95
96 match lora
97 .prepare_for_rx(&mdltn_params, &rx_pkt_params, None, true, false, 0, 0x00ffffffu32)
98 .await
99 {
100 Ok(()) => {}
101 Err(err) => {
102 info!("Radio error = {}", err);
103 return;
104 }
105 };
106
107 loop {
108 receiving_buffer = [00u8; 100];
109 match lora.rx(&rx_pkt_params, &mut receiving_buffer).await {
110 Ok((received_len, _rx_pkt_status)) => {
111 if (received_len == 3)
112 && (receiving_buffer[0] == 0x01u8)
113 && (receiving_buffer[1] == 0x02u8)
114 && (receiving_buffer[2] == 0x03u8)
115 {
116 info!("rx successful");
117 debug_indicator.set_high();
118 Timer::after(Duration::from_secs(5)).await;
119 debug_indicator.set_low();
120 } else {
121 info!("rx unknown packet");
122 }
123 }
124 Err(err) => info!("rx unsuccessful = {}", err),
125 }
126 }
127}
diff --git a/examples/stm32l0/src/bin/lora_p2p_send.rs b/examples/stm32l0/src/bin/lora_p2p_send.rs
new file mode 100644
index 000000000..e6fadc01d
--- /dev/null
+++ b/examples/stm32l0/src/bin/lora_p2p_send.rs
@@ -0,0 +1,110 @@
1//! This example runs on the STM32 LoRa Discovery board, which has a builtin Semtech Sx1276 radio.
2//! It demonstrates LORA P2P send 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;
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
21const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region
22
23#[embassy_executor::main]
24async fn main(_spawner: Spawner) {
25 let mut config = embassy_stm32::Config::default();
26 config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI16;
27 config.rcc.enable_hsi48 = true;
28 let p = embassy_stm32::init(config);
29
30 // SPI for sx1276
31 let spi = spi::Spi::new(
32 p.SPI1,
33 p.PB3,
34 p.PA7,
35 p.PA6,
36 p.DMA1_CH3,
37 p.DMA1_CH2,
38 khz(200),
39 spi::Config::default(),
40 );
41
42 let nss = Output::new(p.PA15.degrade(), Level::High, Speed::Low);
43 let reset = Output::new(p.PC0.degrade(), Level::High, Speed::Low);
44
45 let irq_pin = Input::new(p.PB4.degrade(), Pull::Up);
46 let irq = ExtiInput::new(irq_pin, p.EXTI4.degrade());
47
48 let iv = Stm32l0InterfaceVariant::new(nss, reset, irq, None, None).unwrap();
49
50 let mut delay = Delay;
51
52 let mut lora = {
53 match LoRa::new(SX1276_7_8_9::new(BoardType::Stm32l0Sx1276, spi, iv), false, &mut delay).await {
54 Ok(l) => l,
55 Err(err) => {
56 info!("Radio error = {}", err);
57 return;
58 }
59 }
60 };
61
62 let mdltn_params = {
63 match lora.create_modulation_params(
64 SpreadingFactor::_10,
65 Bandwidth::_250KHz,
66 CodingRate::_4_8,
67 LORA_FREQUENCY_IN_HZ,
68 ) {
69 Ok(mp) => mp,
70 Err(err) => {
71 info!("Radio error = {}", err);
72 return;
73 }
74 }
75 };
76
77 let mut tx_pkt_params = {
78 match lora.create_tx_packet_params(4, false, true, false, &mdltn_params) {
79 Ok(pp) => pp,
80 Err(err) => {
81 info!("Radio error = {}", err);
82 return;
83 }
84 }
85 };
86
87 match lora.prepare_for_tx(&mdltn_params, 17, true).await {
88 Ok(()) => {}
89 Err(err) => {
90 info!("Radio error = {}", err);
91 return;
92 }
93 };
94
95 let buffer = [0x01u8, 0x02u8, 0x03u8];
96 match lora.tx(&mdltn_params, &mut tx_pkt_params, &buffer, 0xffffff).await {
97 Ok(()) => {
98 info!("TX DONE");
99 }
100 Err(err) => {
101 info!("Radio error = {}", err);
102 return;
103 }
104 };
105
106 match lora.sleep(&mut delay).await {
107 Ok(()) => info!("Sleep successful"),
108 Err(err) => info!("Sleep unsuccessful = {}", err),
109 }
110}
diff --git a/examples/stm32l0/src/bin/lorawan.rs b/examples/stm32l0/src/bin/lorawan.rs
deleted file mode 100644
index ea01f610c..000000000
--- a/examples/stm32l0/src/bin/lorawan.rs
+++ /dev/null
@@ -1,74 +0,0 @@
1//! This example runs on the STM32 LoRa Discovery board which has a builtin Semtech Sx127x radio
2#![no_std]
3#![no_main]
4#![macro_use]
5#![allow(dead_code)]
6#![feature(type_alias_impl_trait)]
7
8use embassy_executor::Spawner;
9use embassy_lora::sx127x::*;
10use embassy_lora::LoraTimer;
11use embassy_stm32::exti::ExtiInput;
12use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
13use embassy_stm32::rng::Rng;
14use embassy_stm32::spi;
15use embassy_stm32::time::khz;
16use lorawan::default_crypto::DefaultFactory as Crypto;
17use lorawan_device::async_device::{region, Device, JoinMode};
18use {defmt_rtt as _, panic_probe as _};
19
20#[embassy_executor::main]
21async fn main(_spawner: Spawner) {
22 let mut config = embassy_stm32::Config::default();
23 config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI16;
24 config.rcc.enable_hsi48 = true;
25 let p = embassy_stm32::init(config);
26
27 // SPI for sx127x
28 let spi = spi::Spi::new(
29 p.SPI1,
30 p.PB3,
31 p.PA7,
32 p.PA6,
33 p.DMA1_CH3,
34 p.DMA1_CH2,
35 khz(200),
36 spi::Config::default(),
37 );
38
39 let cs = Output::new(p.PA15, Level::High, Speed::Low);
40 let reset = Output::new(p.PC0, Level::High, Speed::Low);
41 let _ = Input::new(p.PB1, Pull::None);
42
43 let ready = Input::new(p.PB4, Pull::Up);
44 let ready_pin = ExtiInput::new(ready, p.EXTI4);
45
46 let radio = Sx127xRadio::new(spi, cs, reset, ready_pin, DummySwitch).await.unwrap();
47
48 let region = region::Configuration::new(region::Region::EU868);
49 let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), Rng::new(p.RNG));
50
51 defmt::info!("Joining LoRaWAN network");
52
53 // TODO: Adjust the EUI and Keys according to your network credentials
54 device
55 .join(&JoinMode::OTAA {
56 deveui: [0, 0, 0, 0, 0, 0, 0, 0],
57 appeui: [0, 0, 0, 0, 0, 0, 0, 0],
58 appkey: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
59 })
60 .await
61 .ok()
62 .unwrap();
63 defmt::info!("LoRaWAN network joined");
64
65 defmt::info!("Sending 'PING'");
66 device.send(b"PING", 1, false).await.ok().unwrap();
67 defmt::info!("Message sent!");
68}
69
70pub struct DummySwitch;
71impl RadioSwitch for DummySwitch {
72 fn set_rx(&mut self) {}
73 fn set_tx(&mut self) {}
74}
diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml
index 07f136b40..0eb24bc44 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" }
15lorawan = { version = "0.7.2", default-features = false, features = ["default-crypto"] } 15lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"] }
16lorawan = { version = "0.7.3", 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..4bcc5420e
--- /dev/null
+++ b/examples/stm32wl/src/bin/lora_lorawan.rs
@@ -0,0 +1,90 @@
1//! This example runs on a STM32WL board, which has a builtin Semtech Sx1262 radio.
2//! It demonstrates LoRaWAN join 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
31const LORAWAN_REGION: region::Region = region::Region::EU868; // warning: set this appropriately for the region
32
33#[embassy_executor::main]
34async fn main(_spawner: Spawner) {
35 let mut config = embassy_stm32::Config::default();
36 config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI16;
37 config.rcc.enable_lsi = true;
38 let p = embassy_stm32::init(config);
39
40 unsafe { pac::RCC.ccipr().modify(|w| w.set_rngsel(0b01)) }
41
42 let clk = Hertz(core::cmp::min(SUBGHZSPI::frequency().0 / 2, 16_000_000));
43 let mut spi_config = SpiConfig::default();
44 spi_config.mode = MODE_0;
45 spi_config.bit_order = BitOrder::MsbFirst;
46 let spi = Spi::new_subghz(p.SUBGHZSPI, NoDma, NoDma, clk, spi_config);
47
48 let spi = BlockingAsync::new(spi);
49
50 let irq = interrupt::take!(SUBGHZ_RADIO);
51 into_ref!(irq);
52 // Set CTRL1 and CTRL3 for high-power transmission, while CTRL2 acts as an RF switch between tx and rx
53 let _ctrl1 = Output::new(p.PC4.degrade(), Level::Low, Speed::High);
54 let ctrl2 = Output::new(p.PC5.degrade(), Level::High, Speed::High);
55 let _ctrl3 = Output::new(p.PC3.degrade(), Level::High, Speed::High);
56 let iv = Stm32wlInterfaceVariant::new(irq, None, Some(ctrl2)).unwrap();
57
58 let mut delay = Delay;
59
60 let lora = {
61 match LoRa::new(SX1261_2::new(BoardType::Stm32wlSx1262, spi, iv), true, &mut delay).await {
62 Ok(l) => l,
63 Err(err) => {
64 info!("Radio error = {}", err);
65 return;
66 }
67 }
68 };
69 let radio = LoRaRadio::new(lora);
70 let region: region::Configuration = region::Configuration::new(LORAWAN_REGION);
71 let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), Rng::new(p.RNG));
72
73 defmt::info!("Joining LoRaWAN network");
74
75 // TODO: Adjust the EUI and Keys according to your network credentials
76 match device
77 .join(&JoinMode::OTAA {
78 deveui: [0, 0, 0, 0, 0, 0, 0, 0],
79 appeui: [0, 0, 0, 0, 0, 0, 0, 0],
80 appkey: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
81 })
82 .await
83 {
84 Ok(()) => defmt::info!("LoRaWAN network joined"),
85 Err(err) => {
86 info!("Radio error = {}", err);
87 return;
88 }
89 };
90}
diff --git a/examples/stm32wl/src/bin/lora_p2p_receive.rs b/examples/stm32wl/src/bin/lora_p2p_receive.rs
new file mode 100644
index 000000000..bb31518c4
--- /dev/null
+++ b/examples/stm32wl/src/bin/lora_p2p_receive.rs
@@ -0,0 +1,127 @@
1//! This example runs on the STM32WL board, which has a builtin Semtech Sx1262 radio.
2//! It demonstrates LORA P2P receive functionality in conjunction with the lora_p2p_send example.
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, Duration, Timer};
21use lora_phy::mod_params::*;
22use lora_phy::sx1261_2::SX1261_2;
23use lora_phy::LoRa;
24use {defmt_rtt as _, panic_probe as _};
25
26const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region
27
28#[embassy_executor::main]
29async fn main(_spawner: Spawner) {
30 let mut config = embassy_stm32::Config::default();
31 config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSE32;
32 let p = embassy_stm32::init(config);
33
34 let clk = Hertz(core::cmp::min(SUBGHZSPI::frequency().0 / 2, 16_000_000));
35 let mut spi_config = SpiConfig::default();
36 spi_config.mode = MODE_0;
37 spi_config.bit_order = BitOrder::MsbFirst;
38 let spi = Spi::new_subghz(p.SUBGHZSPI, NoDma, NoDma, clk, spi_config);
39
40 let spi = BlockingAsync::new(spi);
41
42 let irq = interrupt::take!(SUBGHZ_RADIO);
43 into_ref!(irq);
44 // Set CTRL1 and CTRL3 for high-power transmission, while CTRL2 acts as an RF switch between tx and rx
45 let _ctrl1 = Output::new(p.PC4.degrade(), Level::Low, Speed::High);
46 let ctrl2 = Output::new(p.PC5.degrade(), Level::High, Speed::High);
47 let _ctrl3 = Output::new(p.PC3.degrade(), Level::High, Speed::High);
48 let iv = Stm32wlInterfaceVariant::new(irq, None, Some(ctrl2)).unwrap();
49
50 let mut delay = Delay;
51
52 let mut lora = {
53 match LoRa::new(SX1261_2::new(BoardType::Stm32wlSx1262, spi, iv), false, &mut delay).await {
54 Ok(l) => l,
55 Err(err) => {
56 info!("Radio error = {}", err);
57 return;
58 }
59 }
60 };
61
62 let mut debug_indicator = Output::new(p.PB9, Level::Low, Speed::Low);
63 let mut start_indicator = Output::new(p.PB15, Level::Low, Speed::Low);
64
65 start_indicator.set_high();
66 Timer::after(Duration::from_secs(5)).await;
67 start_indicator.set_low();
68
69 let mut receiving_buffer = [00u8; 100];
70
71 let mdltn_params = {
72 match lora.create_modulation_params(
73 SpreadingFactor::_10,
74 Bandwidth::_250KHz,
75 CodingRate::_4_8,
76 LORA_FREQUENCY_IN_HZ,
77 ) {
78 Ok(mp) => mp,
79 Err(err) => {
80 info!("Radio error = {}", err);
81 return;
82 }
83 }
84 };
85
86 let rx_pkt_params = {
87 match lora.create_rx_packet_params(4, false, receiving_buffer.len() as u8, true, false, &mdltn_params) {
88 Ok(pp) => pp,
89 Err(err) => {
90 info!("Radio error = {}", err);
91 return;
92 }
93 }
94 };
95
96 match lora
97 .prepare_for_rx(&mdltn_params, &rx_pkt_params, None, true, false, 0, 0x00ffffffu32)
98 .await
99 {
100 Ok(()) => {}
101 Err(err) => {
102 info!("Radio error = {}", err);
103 return;
104 }
105 };
106
107 loop {
108 receiving_buffer = [00u8; 100];
109 match lora.rx(&rx_pkt_params, &mut receiving_buffer).await {
110 Ok((received_len, _rx_pkt_status)) => {
111 if (received_len == 3)
112 && (receiving_buffer[0] == 0x01u8)
113 && (receiving_buffer[1] == 0x02u8)
114 && (receiving_buffer[2] == 0x03u8)
115 {
116 info!("rx successful");
117 debug_indicator.set_high();
118 Timer::after(Duration::from_secs(5)).await;
119 debug_indicator.set_low();
120 } else {
121 info!("rx unknown packet");
122 }
123 }
124 Err(err) => info!("rx unsuccessful = {}", err),
125 }
126 }
127}
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..8a38402fa
--- /dev/null
+++ b/examples/stm32wl/src/bin/lora_p2p_send.rs
@@ -0,0 +1,110 @@
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
26const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region
27
28#[embassy_executor::main]
29async fn main(_spawner: Spawner) {
30 let mut config = embassy_stm32::Config::default();
31 config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSE32;
32 let p = embassy_stm32::init(config);
33
34 let clk = Hertz(core::cmp::min(SUBGHZSPI::frequency().0 / 2, 16_000_000));
35 let mut spi_config = SpiConfig::default();
36 spi_config.mode = MODE_0;
37 spi_config.bit_order = BitOrder::MsbFirst;
38 let spi = Spi::new_subghz(p.SUBGHZSPI, NoDma, NoDma, clk, spi_config);
39
40 let spi = BlockingAsync::new(spi);
41
42 let irq = interrupt::take!(SUBGHZ_RADIO);
43 into_ref!(irq);
44 // Set CTRL1 and CTRL3 for high-power transmission, while CTRL2 acts as an RF switch between tx and rx
45 let _ctrl1 = Output::new(p.PC4.degrade(), Level::Low, Speed::High);
46 let ctrl2 = Output::new(p.PC5.degrade(), Level::High, Speed::High);
47 let _ctrl3 = Output::new(p.PC3.degrade(), Level::High, Speed::High);
48 let iv = Stm32wlInterfaceVariant::new(irq, None, Some(ctrl2)).unwrap();
49
50 let mut delay = Delay;
51
52 let mut lora = {
53 match LoRa::new(SX1261_2::new(BoardType::Stm32wlSx1262, spi, iv), false, &mut delay).await {
54 Ok(l) => l,
55 Err(err) => {
56 info!("Radio error = {}", err);
57 return;
58 }
59 }
60 };
61
62 let mdltn_params = {
63 match lora.create_modulation_params(
64 SpreadingFactor::_10,
65 Bandwidth::_250KHz,
66 CodingRate::_4_8,
67 LORA_FREQUENCY_IN_HZ,
68 ) {
69 Ok(mp) => mp,
70 Err(err) => {
71 info!("Radio error = {}", err);
72 return;
73 }
74 }
75 };
76
77 let mut tx_pkt_params = {
78 match lora.create_tx_packet_params(4, false, true, false, &mdltn_params) {
79 Ok(pp) => pp,
80 Err(err) => {
81 info!("Radio error = {}", err);
82 return;
83 }
84 }
85 };
86
87 match lora.prepare_for_tx(&mdltn_params, 20, false).await {
88 Ok(()) => {}
89 Err(err) => {
90 info!("Radio error = {}", err);
91 return;
92 }
93 };
94
95 let buffer = [0x01u8, 0x02u8, 0x03u8];
96 match lora.tx(&mdltn_params, &mut tx_pkt_params, &buffer, 0xffffff).await {
97 Ok(()) => {
98 info!("TX DONE");
99 }
100 Err(err) => {
101 info!("Radio error = {}", err);
102 return;
103 }
104 };
105
106 match lora.sleep(&mut delay).await {
107 Ok(()) => info!("Sleep successful"),
108 Err(err) => info!("Sleep unsuccessful = {}", err),
109 }
110}
diff --git a/examples/stm32wl/src/bin/lorawan.rs b/examples/stm32wl/src/bin/lorawan.rs
deleted file mode 100644
index 32f29cc5d..000000000
--- a/examples/stm32wl/src/bin/lorawan.rs
+++ /dev/null
@@ -1,104 +0,0 @@
1#![no_std]
2#![no_main]
3#![macro_use]
4#![allow(dead_code)]
5#![feature(type_alias_impl_trait)]
6
7use embassy_executor::Spawner;
8use embassy_lora::stm32wl::*;
9use embassy_lora::LoraTimer;
10use embassy_stm32::dma::NoDma;
11use embassy_stm32::gpio::{AnyPin, Level, Output, Pin, Speed};
12use embassy_stm32::rng::Rng;
13use embassy_stm32::subghz::*;
14use embassy_stm32::{interrupt, pac};
15use lorawan::default_crypto::DefaultFactory as Crypto;
16use lorawan_device::async_device::{region, Device, JoinMode};
17use {defmt_rtt as _, panic_probe as _};
18
19struct RadioSwitch<'a> {
20 ctrl1: Output<'a, AnyPin>,
21 ctrl2: Output<'a, AnyPin>,
22 ctrl3: Output<'a, AnyPin>,
23}
24
25impl<'a> RadioSwitch<'a> {
26 fn new(ctrl1: Output<'a, AnyPin>, ctrl2: Output<'a, AnyPin>, ctrl3: Output<'a, AnyPin>) -> Self {
27 Self { ctrl1, ctrl2, ctrl3 }
28 }
29}
30
31impl<'a> embassy_lora::stm32wl::RadioSwitch for RadioSwitch<'a> {
32 fn set_rx(&mut self) {
33 self.ctrl1.set_high();
34 self.ctrl2.set_low();
35 self.ctrl3.set_high();
36 }
37
38 fn set_tx(&mut self) {
39 self.ctrl1.set_high();
40 self.ctrl2.set_high();
41 self.ctrl3.set_high();
42 }
43}
44
45#[embassy_executor::main]
46async fn main(_spawner: Spawner) {
47 let mut config = embassy_stm32::Config::default();
48 config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI16;
49 config.rcc.enable_lsi = true;
50 let p = embassy_stm32::init(config);
51
52 unsafe { pac::RCC.ccipr().modify(|w| w.set_rngsel(0b01)) }
53
54 let ctrl1 = Output::new(p.PC3.degrade(), Level::High, Speed::High);
55 let ctrl2 = Output::new(p.PC4.degrade(), Level::High, Speed::High);
56 let ctrl3 = Output::new(p.PC5.degrade(), Level::High, Speed::High);
57 let rfs = RadioSwitch::new(ctrl1, ctrl2, ctrl3);
58
59 let radio = SubGhz::new(p.SUBGHZSPI, NoDma, NoDma);
60 let irq = interrupt::take!(SUBGHZ_RADIO);
61
62 let mut radio_config = SubGhzRadioConfig::default();
63 radio_config.calibrate_image = CalibrateImage::ISM_863_870;
64 let radio = SubGhzRadio::new(radio, rfs, irq, radio_config).unwrap();
65
66 let mut region = region::Configuration::new(region::Region::EU868);
67
68 // NOTE: This is specific for TTN, as they have a special RX1 delay
69 region.set_receive_delay1(5000);
70
71 let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), Rng::new(p.RNG));
72
73 // Depending on network, this might be part of JOIN
74 device.set_datarate(region::DR::_0); // SF12
75
76 // device.set_datarate(region::DR::_1); // SF11
77 // device.set_datarate(region::DR::_2); // SF10
78 // device.set_datarate(region::DR::_3); // SF9
79 // device.set_datarate(region::DR::_4); // SF8
80 // device.set_datarate(region::DR::_5); // SF7
81
82 defmt::info!("Joining LoRaWAN network");
83
84 // TODO: Adjust the EUI and Keys according to your network credentials
85 device
86 .join(&JoinMode::OTAA {
87 deveui: [0, 0, 0, 0, 0, 0, 0, 0],
88 appeui: [0, 0, 0, 0, 0, 0, 0, 0],
89 appkey: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
90 })
91 .await
92 .ok()
93 .unwrap();
94 defmt::info!("LoRaWAN network joined");
95
96 let mut rx: [u8; 255] = [0; 255];
97 defmt::info!("Sending 'PING'");
98 let len = device.send_recv(b"PING", &mut rx[..], 1, true).await.ok().unwrap();
99 if len > 0 {
100 defmt::info!("Message sent, received downlink: {:?}", &rx[..len]);
101 } else {
102 defmt::info!("Message sent!");
103 }
104}
diff --git a/examples/stm32wl/src/bin/subghz.rs b/examples/stm32wl/src/bin/subghz.rs
deleted file mode 100644
index 32c8b5515..000000000
--- a/examples/stm32wl/src/bin/subghz.rs
+++ /dev/null
@@ -1,119 +0,0 @@
1#![no_std]
2#![no_main]
3#![macro_use]
4#![allow(dead_code)]
5#![feature(type_alias_impl_trait)]
6
7use defmt::*;
8use embassy_executor::Spawner;
9use embassy_stm32::dma::NoDma;
10use embassy_stm32::exti::ExtiInput;
11use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
12use embassy_stm32::interrupt;
13use embassy_stm32::interrupt::{Interrupt, InterruptExt};
14use embassy_stm32::subghz::*;
15use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
16use embassy_sync::signal::Signal;
17use {defmt_rtt as _, panic_probe as _};
18
19const PING_DATA: &str = "PING";
20const DATA_LEN: u8 = PING_DATA.len() as u8;
21const PING_DATA_BYTES: &[u8] = PING_DATA.as_bytes();
22const PREAMBLE_LEN: u16 = 5 * 8;
23
24const RF_FREQ: RfFreq = RfFreq::from_frequency(867_500_000);
25
26const SYNC_WORD: [u8; 8] = [0x79, 0x80, 0x0C, 0xC0, 0x29, 0x95, 0xF8, 0x4A];
27const SYNC_WORD_LEN: u8 = SYNC_WORD.len() as u8;
28const SYNC_WORD_LEN_BITS: u8 = SYNC_WORD_LEN * 8;
29
30const TX_BUF_OFFSET: u8 = 128;
31const RX_BUF_OFFSET: u8 = 0;
32const LORA_PACKET_PARAMS: LoRaPacketParams = LoRaPacketParams::new()
33 .set_crc_en(true)
34 .set_preamble_len(PREAMBLE_LEN)
35 .set_payload_len(DATA_LEN)
36 .set_invert_iq(false)
37 .set_header_type(HeaderType::Fixed);
38
39const LORA_MOD_PARAMS: LoRaModParams = LoRaModParams::new()
40 .set_bw(LoRaBandwidth::Bw125)
41 .set_cr(CodingRate::Cr45)
42 .set_ldro_en(true)
43 .set_sf(SpreadingFactor::Sf7);
44
45// configuration for +10 dBm output power
46// see table 35 "PA optimal setting and operating modes"
47const PA_CONFIG: PaConfig = PaConfig::new().set_pa_duty_cycle(0x1).set_hp_max(0x0).set_pa(PaSel::Lp);
48
49const TCXO_MODE: TcxoMode = TcxoMode::new()
50 .set_txco_trim(TcxoTrim::Volts1pt7)
51 .set_timeout(Timeout::from_duration_sat(core::time::Duration::from_millis(10)));
52
53const TX_PARAMS: TxParams = TxParams::new().set_power(0x0D).set_ramp_time(RampTime::Micros40);
54
55#[embassy_executor::main]
56async fn main(_spawner: Spawner) {
57 let mut config = embassy_stm32::Config::default();
58 config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSE32;
59 let p = embassy_stm32::init(config);
60
61 let mut led1 = Output::new(p.PB15, Level::High, Speed::Low);
62 let mut led2 = Output::new(p.PB9, Level::Low, Speed::Low);
63 let mut led3 = Output::new(p.PB11, Level::Low, Speed::Low);
64
65 let button = Input::new(p.PA0, Pull::Up);
66 let mut pin = ExtiInput::new(button, p.EXTI0);
67
68 static IRQ_SIGNAL: Signal<CriticalSectionRawMutex, ()> = Signal::new();
69 let radio_irq = interrupt::take!(SUBGHZ_RADIO);
70 radio_irq.set_handler(|_| {
71 IRQ_SIGNAL.signal(());
72 unsafe { interrupt::SUBGHZ_RADIO::steal() }.disable();
73 });
74
75 let mut radio = SubGhz::new(p.SUBGHZSPI, NoDma, NoDma);
76
77 defmt::info!("Radio ready for use");
78
79 led1.set_low();
80
81 led2.set_high();
82
83 unwrap!(radio.set_standby(StandbyClk::Rc));
84 unwrap!(radio.set_tcxo_mode(&TCXO_MODE));
85 unwrap!(radio.set_standby(StandbyClk::Hse));
86 unwrap!(radio.set_regulator_mode(RegMode::Ldo));
87 unwrap!(radio.set_buffer_base_address(TX_BUF_OFFSET, RX_BUF_OFFSET));
88 unwrap!(radio.set_pa_config(&PA_CONFIG));
89 unwrap!(radio.set_pa_ocp(Ocp::Max60m));
90 unwrap!(radio.set_tx_params(&TX_PARAMS));
91 unwrap!(radio.set_packet_type(PacketType::LoRa));
92 unwrap!(radio.set_lora_sync_word(LoRaSyncWord::Public));
93 unwrap!(radio.set_lora_mod_params(&LORA_MOD_PARAMS));
94 unwrap!(radio.set_lora_packet_params(&LORA_PACKET_PARAMS));
95 unwrap!(radio.calibrate_image(CalibrateImage::ISM_863_870));
96 unwrap!(radio.set_rf_frequency(&RF_FREQ));
97
98 defmt::info!("Status: {:?}", unwrap!(radio.status()));
99
100 led2.set_low();
101
102 loop {
103 pin.wait_for_rising_edge().await;
104 led3.set_high();
105 unwrap!(radio.set_irq_cfg(&CfgIrq::new().irq_enable_all(Irq::TxDone)));
106 unwrap!(radio.write_buffer(TX_BUF_OFFSET, PING_DATA_BYTES));
107 unwrap!(radio.set_tx(Timeout::DISABLED));
108
109 radio_irq.enable();
110 IRQ_SIGNAL.wait().await;
111
112 let (_, irq_status) = unwrap!(radio.irq_status());
113 if irq_status & Irq::TxDone.mask() != 0 {
114 defmt::info!("TX done");
115 }
116 unwrap!(radio.clear_irq_status(irq_status));
117 led3.set_low();
118 }
119}