aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorceekdee <[email protected]>2022-09-27 21:55:41 -0500
committerceekdee <[email protected]>2022-09-27 21:55:41 -0500
commita89a0c2f122759a7e743e75152f7af7f67b4387b (patch)
tree52ea198c2a28cf405c651b9a73a3a87eccab1e37
parent9bb43ffe9adddb3497bb08d0730635fbd66cff94 (diff)
Initial add for sx126x
-rw-r--r--README.md2
-rw-r--r--embassy-lora/Cargo.toml2
-rw-r--r--embassy-lora/src/lib.rs2
-rw-r--r--embassy-lora/src/sx126x/mod.rs171
-rw-r--r--embassy-lora/src/sx126x/sx126x_lora/board_specific.rs251
-rw-r--r--embassy-lora/src/sx126x/sx126x_lora/mod.rs735
-rw-r--r--embassy-lora/src/sx126x/sx126x_lora/mod_params.rs469
-rw-r--r--embassy-lora/src/sx126x/sx126x_lora/subroutine.rs688
-rw-r--r--examples/nrf/Cargo.toml7
-rw-r--r--examples/nrf/src/bin/lora_p2p_report.rs84
-rw-r--r--examples/nrf/src/bin/lora_p2p_sense.rs173
11 files changed, 2582 insertions, 2 deletions
diff --git a/README.md b/README.md
index 9f08bf676..eaa91012c 100644
--- a/README.md
+++ b/README.md
@@ -31,7 +31,7 @@ The <a href="https://docs.embassy.dev/embassy-net/">embassy-net</a> network stac
31The <a href="https://github.com/embassy-rs/nrf-softdevice">nrf-softdevice</a> crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers. 31The <a href="https://github.com/embassy-rs/nrf-softdevice">nrf-softdevice</a> crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers.
32 32
33- **LoRa** - 33- **LoRa** -
34<a href="https://docs.embassy.dev/embassy-lora/">embassy-lora</a> supports LoRa networking on STM32WL wireless microcontrollers and Semtech SX127x transceivers. 34<a href="https://docs.embassy.dev/embassy-lora/">embassy-lora</a> supports LoRa networking on STM32WL wireless microcontrollers and Semtech SX126x and SX127x transceivers.
35 35
36- **USB** - 36- **USB** -
37<a href="https://docs.embassy.dev/embassy-usb/">embassy-usb</a> implements a device-side USB stack. Implementations for common classes such as USB serial (CDC ACM) and USB HID are available, and a rich builder API allows building your own. 37<a href="https://docs.embassy.dev/embassy-usb/">embassy-usb</a> implements a device-side USB stack. Implementations for common classes such as USB serial (CDC ACM) and USB HID are available, and a rich builder API allows building your own.
diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml
index 1757efa8b..0c2f983f6 100644
--- a/embassy-lora/Cargo.toml
+++ b/embassy-lora/Cargo.toml
@@ -8,6 +8,7 @@ src_base = "https://github.com/embassy-rs/embassy/blob/embassy-lora-v$VERSION/em
8src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-lora/src/" 8src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-lora/src/"
9features = ["time", "defmt"] 9features = ["time", "defmt"]
10flavors = [ 10flavors = [
11 { name = "rak4631", target = "thumbv7em-none-eabihf", features = ["rak4631"] },
11 { name = "sx127x", target = "thumbv7em-none-eabihf", features = ["sx127x", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any"] }, 12 { name = "sx127x", target = "thumbv7em-none-eabihf", features = ["sx127x", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any"] },
12 { name = "stm32wl", target = "thumbv7em-none-eabihf", features = ["stm32wl", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any"] }, 13 { name = "stm32wl", target = "thumbv7em-none-eabihf", features = ["stm32wl", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any"] },
13] 14]
@@ -15,6 +16,7 @@ flavors = [
15[lib] 16[lib]
16 17
17[features] 18[features]
19rak4631 = []
18sx127x = [] 20sx127x = []
19stm32wl = ["embassy-stm32", "embassy-stm32/subghz"] 21stm32wl = ["embassy-stm32", "embassy-stm32/subghz"]
20time = [] 22time = []
diff --git a/embassy-lora/src/lib.rs b/embassy-lora/src/lib.rs
index 90ba0d1d4..342f66b2a 100644
--- a/embassy-lora/src/lib.rs
+++ b/embassy-lora/src/lib.rs
@@ -7,6 +7,8 @@ pub(crate) mod fmt;
7 7
8#[cfg(feature = "stm32wl")] 8#[cfg(feature = "stm32wl")]
9pub mod stm32wl; 9pub mod stm32wl;
10#[cfg(feature = "rak4631")]
11pub mod sx126x;
10#[cfg(feature = "sx127x")] 12#[cfg(feature = "sx127x")]
11pub mod sx127x; 13pub mod sx127x;
12 14
diff --git a/embassy-lora/src/sx126x/mod.rs b/embassy-lora/src/sx126x/mod.rs
new file mode 100644
index 000000000..d67c7b109
--- /dev/null
+++ b/embassy-lora/src/sx126x/mod.rs
@@ -0,0 +1,171 @@
1use core::future::Future;
2
3use defmt::Format;
4use embedded_hal::digital::v2::OutputPin;
5use embedded_hal_async::digital::Wait;
6use embedded_hal_async::spi::*;
7use lorawan_device::async_device::radio::{PhyRxTx, RfConfig, RxQuality, TxConfig};
8use lorawan_device::async_device::Timings;
9
10mod sx126x_lora;
11use sx126x_lora::LoRa;
12
13use self::sx126x_lora::mod_params::RadioError;
14
15/// Semtech Sx126x LoRa peripheral
16pub struct Sx126xRadio<SPI, CS, RESET, ANTRX, ANTTX, WAIT, BUS>
17where
18 SPI: SpiBus<u8, Error = BUS> + 'static,
19 CS: OutputPin + 'static,
20 RESET: OutputPin + 'static,
21 ANTRX: OutputPin + 'static,
22 ANTTX: OutputPin + 'static,
23 WAIT: Wait + 'static,
24 BUS: Error + Format + 'static,
25{
26 pub lora: LoRa<SPI, CS, RESET, ANTRX, ANTTX, WAIT>,
27}
28
29impl<SPI, CS, RESET, ANTRX, ANTTX, WAIT, BUS> Sx126xRadio<SPI, CS, RESET, ANTRX, ANTTX, WAIT, BUS>
30where
31 SPI: SpiBus<u8, Error = BUS> + 'static,
32 CS: OutputPin + 'static,
33 RESET: OutputPin + 'static,
34 ANTRX: OutputPin + 'static,
35 ANTTX: OutputPin + 'static,
36 WAIT: Wait + 'static,
37 BUS: Error + Format + 'static,
38{
39 pub async fn new(
40 spi: SPI,
41 cs: CS,
42 reset: RESET,
43 antenna_rx: ANTRX,
44 antenna_tx: ANTTX,
45 dio1: WAIT,
46 busy: WAIT,
47 enable_public_network: bool,
48 ) -> Result<Self, RadioError<BUS>> {
49 let mut lora = LoRa::new(spi, cs, reset, antenna_rx, antenna_tx, dio1, busy);
50 lora.init().await?;
51 lora.set_lora_modem(enable_public_network).await?;
52 Ok(Self { lora })
53 }
54}
55
56impl<SPI, CS, RESET, ANTRX, ANTTX, WAIT, BUS> Timings for Sx126xRadio<SPI, CS, RESET, ANTRX, ANTTX, WAIT, BUS>
57where
58 SPI: SpiBus<u8, Error = BUS> + 'static,
59 CS: OutputPin + 'static,
60 RESET: OutputPin + 'static,
61 ANTRX: OutputPin + 'static,
62 ANTTX: OutputPin + 'static,
63 WAIT: Wait + 'static,
64 BUS: Error + Format + 'static,
65{
66 fn get_rx_window_offset_ms(&self) -> i32 {
67 -500
68 }
69 fn get_rx_window_duration_ms(&self) -> u32 {
70 800
71 }
72}
73
74impl<SPI, CS, RESET, ANTRX, ANTTX, WAIT, BUS> PhyRxTx for Sx126xRadio<SPI, CS, RESET, ANTRX, ANTTX, WAIT, BUS>
75where
76 SPI: SpiBus<u8, Error = BUS> + 'static,
77 CS: OutputPin + 'static,
78 RESET: OutputPin + 'static,
79 ANTRX: OutputPin + 'static,
80 ANTTX: OutputPin + 'static,
81 WAIT: Wait + 'static,
82 BUS: Error + Format + 'static,
83{
84 type PhyError = RadioError<BUS>;
85
86 type TxFuture<'m> = impl Future<Output = Result<u32, Self::PhyError>> + 'm
87 where
88 SPI: 'm,
89 CS: 'm,
90 RESET: 'm,
91 ANTRX: 'm,
92 ANTTX: 'm,
93 WAIT: 'm,
94 BUS: 'm;
95
96 fn tx<'m>(&'m mut self, config: TxConfig, buffer: &'m [u8]) -> Self::TxFuture<'m> {
97 trace!("TX START");
98 async move {
99 self.lora
100 .set_tx_config(
101 config.pw,
102 config.rf.spreading_factor.into(),
103 config.rf.bandwidth.into(),
104 config.rf.coding_rate.into(),
105 4,
106 false,
107 true,
108 false,
109 0,
110 false,
111 )
112 .await?;
113 self.lora.set_max_payload_length(buffer.len() as u8).await?;
114 self.lora.set_channel(config.rf.frequency).await?;
115 self.lora.send(buffer, 0xffffff).await?;
116 self.lora.process_irq(None, None, None).await?;
117 trace!("TX DONE");
118 return Ok(0);
119 }
120 }
121
122 type RxFuture<'m> = impl Future<Output = Result<(usize, RxQuality), Self::PhyError>> + 'm
123 where
124 SPI: 'm,
125 CS: 'm,
126 RESET: 'm,
127 ANTRX: 'm,
128 ANTTX: 'm,
129 WAIT: 'm,
130 BUS: 'm;
131
132 fn rx<'m>(&'m mut self, config: RfConfig, receiving_buffer: &'m mut [u8]) -> Self::RxFuture<'m> {
133 trace!("RX START");
134 async move {
135 self.lora
136 .set_rx_config(
137 config.spreading_factor.into(),
138 config.bandwidth.into(),
139 config.coding_rate.into(),
140 4,
141 4,
142 false,
143 0u8,
144 true,
145 false,
146 0,
147 false,
148 true,
149 )
150 .await?;
151 self.lora.set_max_payload_length(receiving_buffer.len() as u8).await?;
152 self.lora.set_channel(config.frequency).await?;
153 self.lora.rx(90 * 1000).await?;
154 let mut received_len = 0u8;
155 self.lora
156 .process_irq(Some(receiving_buffer), Some(&mut received_len), None)
157 .await?;
158 trace!("RX DONE");
159
160 let packet_status = self.lora.get_latest_packet_status();
161 let mut rssi = 0i16;
162 let mut snr = 0i8;
163 if packet_status.is_some() {
164 rssi = packet_status.unwrap().rssi as i16;
165 snr = packet_status.unwrap().snr;
166 }
167
168 Ok((received_len as usize, RxQuality::new(rssi, snr)))
169 }
170 }
171}
diff --git a/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs b/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs
new file mode 100644
index 000000000..5b4891c04
--- /dev/null
+++ b/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs
@@ -0,0 +1,251 @@
1use embassy_time::{Duration, Timer};
2use embedded_hal::digital::v2::OutputPin;
3use embedded_hal_async::digital::Wait;
4use embedded_hal_async::spi::SpiBus;
5
6use super::mod_params::RadioError::*;
7use super::mod_params::*;
8use super::LoRa;
9
10// Defines the time required for the TCXO to wakeup [ms].
11const BRD_TCXO_WAKEUP_TIME: u32 = 10;
12
13// Provides board-specific functionality for Semtech SX126x-based boards. Use #[cfg(feature = "board_type")] to specify unique board functionality.
14// The base implementation supports the RAK4631 board.
15
16impl<SPI, CS, RESET, ANTRX, ANTTX, WAIT, BUS> LoRa<SPI, CS, RESET, ANTRX, ANTTX, WAIT>
17where
18 SPI: SpiBus<u8, Error = BUS>,
19 CS: OutputPin,
20 RESET: OutputPin,
21 ANTRX: OutputPin,
22 ANTTX: OutputPin,
23 WAIT: Wait,
24{
25 // De-initialize the radio I/Os pins interface. Useful when going into MCU low power modes.
26 pub(super) async fn brd_io_deinit(&mut self) -> Result<(), RadioError<BUS>> {
27 Ok(()) // no operation currently
28 }
29
30 // Initialize the TCXO power pin
31 pub(super) async fn brd_io_tcxo_init(&mut self) -> Result<(), RadioError<BUS>> {
32 let timeout = self.brd_get_board_tcxo_wakeup_time() << 6;
33 self.sub_set_dio3_as_tcxo_ctrl(TcxoCtrlVoltage::Ctrl1V7, timeout)
34 .await?;
35 Ok(())
36 }
37
38 // Initialize RF switch control pins
39 pub(super) async fn brd_io_rf_switch_init(&mut self) -> Result<(), RadioError<BUS>> {
40 self.sub_set_dio2_as_rf_switch_ctrl(true).await?;
41 Ok(())
42 }
43
44 // Initialize the radio debug pins
45 pub(super) async fn brd_io_dbg_init(&mut self) -> Result<(), RadioError<BUS>> {
46 Ok(()) // no operation currently
47 }
48
49 // Hardware reset of the radio
50 pub(super) async fn brd_reset(&mut self) -> Result<(), RadioError<BUS>> {
51 Timer::after(Duration::from_millis(10)).await;
52 self.reset.set_low().map_err(|_| Reset)?;
53 Timer::after(Duration::from_millis(20)).await;
54 self.reset.set_high().map_err(|_| Reset)?;
55 Timer::after(Duration::from_millis(10)).await;
56 Ok(())
57 }
58
59 // Wait while the busy pin is high
60 pub(super) async fn brd_wait_on_busy(&mut self) -> Result<(), RadioError<BUS>> {
61 self.busy.wait_for_low().await.map_err(|_| Busy)?;
62 Ok(())
63 }
64
65 // Wake up the radio
66 pub(super) async fn brd_wakeup(&mut self) -> Result<(), RadioError<BUS>> {
67 self.cs.set_low().map_err(|_| CS)?;
68 self.spi.write(&[OpCode::GetStatus.value()]).await.map_err(SPI)?;
69 self.spi.write(&[0x00]).await.map_err(SPI)?;
70 self.cs.set_high().map_err(|_| CS)?;
71
72 self.brd_wait_on_busy().await?;
73 self.brd_set_operating_mode(RadioMode::StandbyRC);
74 Ok(())
75 }
76
77 // Send a command that writes data to the radio
78 pub(super) async fn brd_write_command(&mut self, op_code: OpCode, buffer: &[u8]) -> Result<(), RadioError<BUS>> {
79 self.sub_check_device_ready().await?;
80
81 self.cs.set_low().map_err(|_| CS)?;
82 self.spi.write(&[op_code.value()]).await.map_err(SPI)?;
83 self.spi.write(buffer).await.map_err(SPI)?;
84 self.cs.set_high().map_err(|_| CS)?;
85
86 if op_code != OpCode::SetSleep {
87 self.brd_wait_on_busy().await?;
88 }
89 Ok(())
90 }
91
92 // Send a command that reads data from the radio, filling the provided buffer and returning a status
93 pub(super) async fn brd_read_command(&mut self, op_code: OpCode, buffer: &mut [u8]) -> Result<u8, RadioError<BUS>> {
94 let mut status = [0u8];
95 let mut input = [0u8];
96
97 self.sub_check_device_ready().await?;
98
99 self.cs.set_low().map_err(|_| CS)?;
100 self.spi.write(&[op_code.value()]).await.map_err(SPI)?;
101 self.spi.transfer(&mut status, &[0x00]).await.map_err(SPI)?;
102 for i in 0..buffer.len() {
103 self.spi.transfer(&mut input, &[0x00]).await.map_err(SPI)?;
104 buffer[i] = input[0];
105 }
106 self.cs.set_high().map_err(|_| CS)?;
107
108 self.brd_wait_on_busy().await?;
109
110 Ok(status[0])
111 }
112
113 // Write one or more bytes of data to the radio memory
114 pub(super) async fn brd_write_registers(
115 &mut self,
116 start_register: Register,
117 buffer: &[u8],
118 ) -> Result<(), RadioError<BUS>> {
119 self.sub_check_device_ready().await?;
120
121 self.cs.set_low().map_err(|_| CS)?;
122 self.spi.write(&[OpCode::WriteRegister.value()]).await.map_err(SPI)?;
123 self.spi
124 .write(&[
125 ((start_register.addr() & 0xFF00) >> 8) as u8,
126 (start_register.addr() & 0x00FF) as u8,
127 ])
128 .await
129 .map_err(SPI)?;
130 self.spi.write(buffer).await.map_err(SPI)?;
131 self.cs.set_high().map_err(|_| CS)?;
132
133 self.brd_wait_on_busy().await?;
134 Ok(())
135 }
136
137 // Read one or more bytes of data from the radio memory
138 pub(super) async fn brd_read_registers(
139 &mut self,
140 start_register: Register,
141 buffer: &mut [u8],
142 ) -> Result<(), RadioError<BUS>> {
143 let mut input = [0u8];
144
145 self.sub_check_device_ready().await?;
146
147 self.cs.set_low().map_err(|_| CS)?;
148 self.spi.write(&[OpCode::ReadRegister.value()]).await.map_err(SPI)?;
149 self.spi
150 .write(&[
151 ((start_register.addr() & 0xFF00) >> 8) as u8,
152 (start_register.addr() & 0x00FF) as u8,
153 0x00u8,
154 ])
155 .await
156 .map_err(SPI)?;
157 for i in 0..buffer.len() {
158 self.spi.transfer(&mut input, &[0x00]).await.map_err(SPI)?;
159 buffer[i] = input[0];
160 }
161 self.cs.set_high().map_err(|_| CS)?;
162
163 self.brd_wait_on_busy().await?;
164 Ok(())
165 }
166
167 // Write data to the buffer holding the payload in the radio
168 pub(super) async fn brd_write_buffer(&mut self, offset: u8, buffer: &[u8]) -> Result<(), RadioError<BUS>> {
169 self.sub_check_device_ready().await?;
170
171 self.cs.set_low().map_err(|_| CS)?;
172 self.spi.write(&[OpCode::WriteBuffer.value()]).await.map_err(SPI)?;
173 self.spi.write(&[offset]).await.map_err(SPI)?;
174 self.spi.write(buffer).await.map_err(SPI)?;
175 self.cs.set_high().map_err(|_| CS)?;
176
177 self.brd_wait_on_busy().await?;
178 Ok(())
179 }
180
181 // Read data from the buffer holding the payload in the radio
182 pub(super) async fn brd_read_buffer(&mut self, offset: u8, buffer: &mut [u8]) -> Result<(), RadioError<BUS>> {
183 let mut input = [0u8];
184
185 self.sub_check_device_ready().await?;
186
187 self.cs.set_low().map_err(|_| CS)?;
188 self.spi.write(&[OpCode::ReadBuffer.value()]).await.map_err(SPI)?;
189 self.spi.write(&[offset]).await.map_err(SPI)?;
190 self.spi.write(&[0x00]).await.map_err(SPI)?;
191 for i in 0..buffer.len() {
192 self.spi.transfer(&mut input, &[0x00]).await.map_err(SPI)?;
193 buffer[i] = input[0];
194 }
195 self.cs.set_high().map_err(|_| CS)?;
196
197 self.brd_wait_on_busy().await?;
198 Ok(())
199 }
200
201 // Set the radio output power
202 pub(super) async fn brd_set_rf_tx_power(&mut self, power: i8) -> Result<(), RadioError<BUS>> {
203 self.sub_set_tx_params(power, RampTime::Ramp40Us).await?;
204 Ok(())
205 }
206
207 // Get the radio type
208 pub(super) fn brd_get_radio_type(&mut self) -> RadioType {
209 #[cfg(feature = "rak4631")]
210 RadioType::SX1262
211 }
212
213 // Initialize the RF switch I/O pins interface
214 pub(super) async fn brd_ant_sw_on(&mut self) -> Result<(), RadioError<BUS>> {
215 Ok(()) // no operation currently
216 }
217
218 // De-initialize the RF switch I/O pins interface for MCU low power modes
219 pub(super) async fn brd_ant_sw_off(&mut self) -> Result<(), RadioError<BUS>> {
220 Ok(()) // no operation currently
221 }
222
223 // Check if the given RF frequency is supported by the hardware
224 pub(super) async fn brd_check_rf_frequency(&mut self, _frequency: u32) -> Result<bool, RadioError<BUS>> {
225 #[cfg(feature = "rak4631")]
226 Ok(true) // all frequencies currently supported for the SX1262 within a rak4631
227 }
228
229 // Get the duration required for the TCXO to wakeup [ms].
230 pub(super) fn brd_get_board_tcxo_wakeup_time(&mut self) -> u32 {
231 BRD_TCXO_WAKEUP_TIME
232 }
233
234 /* Get current state of the DIO1 pin - not currently needed if waiting on DIO1 instead of using an IRQ process
235 pub(super) async fn brd_get_dio1_pin_state(
236 &mut self,
237 ) -> Result<u32, RadioError<BUS>> {
238 Ok(0)
239 }
240 */
241
242 // Get the current radio operatiing mode
243 pub(super) fn brd_get_operating_mode(&mut self) -> RadioMode {
244 self.operating_mode
245 }
246
247 // Set/Update the current radio operating mode This function is only required to reflect the current radio operating mode when processing interrupts.
248 pub(super) fn brd_set_operating_mode(&mut self, mode: RadioMode) {
249 self.operating_mode = mode;
250 }
251}
diff --git a/embassy-lora/src/sx126x/sx126x_lora/mod.rs b/embassy-lora/src/sx126x/sx126x_lora/mod.rs
new file mode 100644
index 000000000..02f5f83a2
--- /dev/null
+++ b/embassy-lora/src/sx126x/sx126x_lora/mod.rs
@@ -0,0 +1,735 @@
1#![allow(dead_code)]
2
3use embassy_time::{Duration, Timer};
4use embedded_hal::digital::v2::OutputPin;
5use embedded_hal_async::digital::Wait;
6use embedded_hal_async::spi::SpiBus;
7
8mod board_specific;
9pub mod mod_params;
10mod subroutine;
11
12use mod_params::RadioError::*;
13use mod_params::*;
14
15// Syncwords for public and private networks
16const LORA_MAC_PUBLIC_SYNCWORD: u16 = 0x3444;
17const LORA_MAC_PRIVATE_SYNCWORD: u16 = 0x1424;
18
19// Maximum number of registers that can be added to the retention list
20const MAX_NUMBER_REGS_IN_RETENTION: u8 = 4;
21
22// Possible LoRa bandwidths
23const LORA_BANDWIDTHS: [Bandwidth; 3] = [Bandwidth::_125KHz, Bandwidth::_250KHz, Bandwidth::_500KHz];
24
25// Radio complete wakeup time with margin for temperature compensation [ms]
26const RADIO_WAKEUP_TIME: u32 = 3;
27
28/// Provides high-level access to Semtech SX126x-based boards
29pub struct LoRa<SPI, CS, RESET, ANTRX, ANTTX, WAIT> {
30 spi: SPI,
31 cs: CS,
32 reset: RESET,
33 antenna_rx: ANTRX,
34 antenna_tx: ANTTX,
35 dio1: WAIT,
36 busy: WAIT,
37 operating_mode: RadioMode,
38 rx_continuous: bool,
39 max_payload_length: u8,
40 modulation_params: Option<ModulationParams>,
41 packet_type: PacketType,
42 packet_params: Option<PacketParams>,
43 packet_status: Option<PacketStatus>,
44 image_calibrated: bool,
45 frequency_error: u32,
46}
47
48impl<SPI, CS, RESET, ANTRX, ANTTX, WAIT, BUS> LoRa<SPI, CS, RESET, ANTRX, ANTTX, WAIT>
49where
50 SPI: SpiBus<u8, Error = BUS>,
51 CS: OutputPin,
52 RESET: OutputPin,
53 ANTRX: OutputPin,
54 ANTTX: OutputPin,
55 WAIT: Wait,
56{
57 /// Builds and returns a new instance of the radio. Only one instance of the radio should exist at a time ()
58 pub fn new(spi: SPI, cs: CS, reset: RESET, antenna_rx: ANTRX, antenna_tx: ANTTX, dio1: WAIT, busy: WAIT) -> Self {
59 Self {
60 spi,
61 cs,
62 reset,
63 antenna_rx,
64 antenna_tx,
65 dio1,
66 busy,
67 operating_mode: RadioMode::Sleep,
68 rx_continuous: false,
69 max_payload_length: 0xFFu8,
70 modulation_params: None,
71 packet_type: PacketType::LoRa,
72 packet_params: None,
73 packet_status: None,
74 image_calibrated: false,
75 frequency_error: 0u32, // where is volatile FrequencyError modified ???
76 }
77 }
78
79 /// Initialize the radio
80 pub async fn init(&mut self) -> Result<(), RadioError<BUS>> {
81 self.sub_init().await?;
82 self.sub_set_standby(StandbyMode::RC).await?;
83 self.sub_set_regulator_mode(RegulatorMode::UseDCDC).await?;
84 self.sub_set_buffer_base_address(0x00u8, 0x00u8).await?;
85 self.sub_set_tx_params(0i8, RampTime::Ramp200Us).await?;
86 self.sub_set_dio_irq_params(
87 IrqMask::All.value(),
88 IrqMask::All.value(),
89 IrqMask::None.value(),
90 IrqMask::None.value(),
91 )
92 .await?;
93 self.add_register_to_retention_list(Register::RxGain.addr()).await?;
94 self.add_register_to_retention_list(Register::TxModulation.addr())
95 .await?;
96 Ok(())
97 }
98
99 /// Return current radio state
100 pub fn get_status(&mut self) -> RadioState {
101 match self.brd_get_operating_mode() {
102 RadioMode::Transmit => RadioState::TxRunning,
103 RadioMode::Receive => RadioState::RxRunning,
104 RadioMode::ChannelActivityDetection => RadioState::ChannelActivityDetecting,
105 _ => RadioState::Idle,
106 }
107 }
108
109 /// Configure the radio for LoRa (FSK support should be provided in a separate driver, if desired)
110 pub async fn set_lora_modem(&mut self, enable_public_network: bool) -> Result<(), RadioError<BUS>> {
111 self.sub_set_packet_type(PacketType::LoRa).await?;
112 if enable_public_network {
113 self.brd_write_registers(
114 Register::LoRaSyncword,
115 &[
116 ((LORA_MAC_PUBLIC_SYNCWORD >> 8) & 0xFF) as u8,
117 (LORA_MAC_PUBLIC_SYNCWORD & 0xFF) as u8,
118 ],
119 )
120 .await?;
121 } else {
122 self.brd_write_registers(
123 Register::LoRaSyncword,
124 &[
125 ((LORA_MAC_PRIVATE_SYNCWORD >> 8) & 0xFF) as u8,
126 (LORA_MAC_PRIVATE_SYNCWORD & 0xFF) as u8,
127 ],
128 )
129 .await?;
130 }
131
132 Ok(())
133 }
134
135 /// Sets the channel frequency
136 pub async fn set_channel(&mut self, frequency: u32) -> Result<(), RadioError<BUS>> {
137 self.sub_set_rf_frequency(frequency).await?;
138 Ok(())
139 }
140
141 /* Checks if the channel is free for the given time. This is currently not implemented until a substitute
142 for switching to the FSK modem is found.
143
144 pub async fn is_channel_free(&mut self, frequency: u32, rxBandwidth: u32, rssiThresh: i16, maxCarrierSenseTime: u32) -> bool;
145 */
146
147 /// Generate a 32 bit random value based on the RSSI readings, after disabling all interrupts. Ensure set_lora_modem() is called befrorehand.
148 /// After calling this function either set_rx_config() or set_tx_config() must be called.
149 pub async fn get_random_value(&mut self) -> Result<u32, RadioError<BUS>> {
150 self.sub_set_dio_irq_params(
151 IrqMask::None.value(),
152 IrqMask::None.value(),
153 IrqMask::None.value(),
154 IrqMask::None.value(),
155 )
156 .await?;
157
158 let result = self.sub_get_random().await?;
159 Ok(result)
160 }
161
162 /// Set the reception parameters for the LoRa modem (only). Ensure set_lora_modem() is called befrorehand.
163 /// spreading_factor [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096 chips/symbol]
164 /// bandwidth [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved]
165 /// coding_rate [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8]
166 /// preamble_length length in symbols (the hardware adds 4 more symbols)
167 /// symb_timeout RxSingle timeout value in symbols
168 /// fixed_len fixed length packets [0: variable, 1: fixed]
169 /// payload_len payload length when fixed length is used
170 /// crc_on [0: OFF, 1: ON]
171 /// freq_hop_on intra-packet frequency hopping [0: OFF, 1: ON]
172 /// hop_period number of symbols between each hop
173 /// iq_inverted invert IQ signals [0: not inverted, 1: inverted]
174 /// rx_continuous reception mode [false: single mode, true: continuous mode]
175 pub async fn set_rx_config(
176 &mut self,
177 spreading_factor: SpreadingFactor,
178 bandwidth: Bandwidth,
179 coding_rate: CodingRate,
180 preamble_length: u16,
181 symb_timeout: u16,
182 fixed_len: bool,
183 payload_len: u8,
184 crc_on: bool,
185 _freq_hop_on: bool,
186 _hop_period: u8,
187 iq_inverted: bool,
188 rx_continuous: bool,
189 ) -> Result<(), RadioError<BUS>> {
190 let mut symb_timeout_final = symb_timeout;
191
192 self.rx_continuous = rx_continuous;
193 if self.rx_continuous {
194 symb_timeout_final = 0;
195 }
196 if fixed_len {
197 self.max_payload_length = payload_len;
198 } else {
199 self.max_payload_length = 0xFFu8;
200 }
201
202 self.sub_set_stop_rx_timer_on_preamble_detect(false).await?;
203
204 let mut low_data_rate_optimize = 0x00u8;
205 if (((spreading_factor == SpreadingFactor::_11) || (spreading_factor == SpreadingFactor::_12))
206 && (bandwidth == Bandwidth::_125KHz))
207 || ((spreading_factor == SpreadingFactor::_12) && (bandwidth == Bandwidth::_250KHz))
208 {
209 low_data_rate_optimize = 0x01u8;
210 }
211
212 let modulation_params = ModulationParams {
213 spreading_factor: spreading_factor,
214 bandwidth: bandwidth,
215 coding_rate: coding_rate,
216 low_data_rate_optimize: low_data_rate_optimize,
217 };
218
219 let mut preamble_length_final = preamble_length;
220 if ((spreading_factor == SpreadingFactor::_5) || (spreading_factor == SpreadingFactor::_6))
221 && (preamble_length < 12)
222 {
223 preamble_length_final = 12;
224 }
225
226 let packet_params = PacketParams {
227 preamble_length: preamble_length_final,
228 implicit_header: fixed_len,
229 payload_length: self.max_payload_length,
230 crc_on: crc_on,
231 iq_inverted: iq_inverted,
232 };
233
234 self.modulation_params = Some(modulation_params);
235 self.packet_params = Some(packet_params);
236
237 self.standby().await?;
238 self.sub_set_modulation_params().await?;
239 self.sub_set_packet_params().await?;
240 self.sub_set_lora_symb_num_timeout(symb_timeout_final).await?;
241
242 // Optimize the Inverted IQ Operation (see DS_SX1261-2_V1.2 datasheet chapter 15.4)
243 let mut iq_polarity = [0x00u8];
244 self.brd_read_registers(Register::IQPolarity, &mut iq_polarity).await?;
245 if iq_inverted {
246 self.brd_write_registers(Register::IQPolarity, &[iq_polarity[0] & (!(1 << 2))])
247 .await?;
248 } else {
249 self.brd_write_registers(Register::IQPolarity, &[iq_polarity[0] | (1 << 2)])
250 .await?;
251 }
252 Ok(())
253 }
254
255 /// Set the transmission parameters for the LoRa modem (only).
256 /// power output power [dBm]
257 /// spreading_factor [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096 chips/symbol]
258 /// bandwidth [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved]
259 /// coding_rate [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8]
260 /// preamble_length length in symbols (the hardware adds 4 more symbols)
261 /// fixed_len fixed length packets [0: variable, 1: fixed]
262 /// crc_on [0: OFF, 1: ON]
263 /// freq_hop_on intra-packet frequency hopping [0: OFF, 1: ON]
264 /// hop_period number of symbols between each hop
265 /// iq_inverted invert IQ signals [0: not inverted, 1: inverted]
266 pub async fn set_tx_config(
267 &mut self,
268 power: i8,
269 spreading_factor: SpreadingFactor,
270 bandwidth: Bandwidth,
271 coding_rate: CodingRate,
272 preamble_length: u16,
273 fixed_len: bool,
274 crc_on: bool,
275 _freq_hop_on: bool,
276 _hop_period: u8,
277 iq_inverted: bool,
278 ) -> Result<(), RadioError<BUS>> {
279 let mut low_data_rate_optimize = 0x00u8;
280 if (((spreading_factor == SpreadingFactor::_11) || (spreading_factor == SpreadingFactor::_12))
281 && (bandwidth == Bandwidth::_125KHz))
282 || ((spreading_factor == SpreadingFactor::_12) && (bandwidth == Bandwidth::_250KHz))
283 {
284 low_data_rate_optimize = 0x01u8;
285 }
286
287 let modulation_params = ModulationParams {
288 spreading_factor: spreading_factor,
289 bandwidth: bandwidth,
290 coding_rate: coding_rate,
291 low_data_rate_optimize: low_data_rate_optimize,
292 };
293
294 let mut preamble_length_final = preamble_length;
295 if ((spreading_factor == SpreadingFactor::_5) || (spreading_factor == SpreadingFactor::_6))
296 && (preamble_length < 12)
297 {
298 preamble_length_final = 12;
299 }
300
301 let packet_params = PacketParams {
302 preamble_length: preamble_length_final,
303 implicit_header: fixed_len,
304 payload_length: self.max_payload_length,
305 crc_on: crc_on,
306 iq_inverted: iq_inverted,
307 };
308
309 self.modulation_params = Some(modulation_params);
310 self.packet_params = Some(packet_params);
311
312 self.standby().await?;
313 self.sub_set_modulation_params().await?;
314 self.sub_set_packet_params().await?;
315
316 // Handle modulation quality with the 500 kHz LoRa bandwidth (see DS_SX1261-2_V1.2 datasheet chapter 15.1)
317
318 let mut tx_modulation = [0x00u8];
319 self.brd_read_registers(Register::TxModulation, &mut tx_modulation)
320 .await?;
321 if bandwidth == Bandwidth::_500KHz {
322 self.brd_write_registers(Register::TxModulation, &[tx_modulation[0] & (!(1 << 2))])
323 .await?;
324 } else {
325 self.brd_write_registers(Register::TxModulation, &[tx_modulation[0] | (1 << 2)])
326 .await?;
327 }
328
329 self.brd_set_rf_tx_power(power).await?;
330 Ok(())
331 }
332
333 /// Check if the given RF frequency is supported by the hardware [true: supported, false: unsupported]
334 pub async fn check_rf_frequency(&mut self, frequency: u32) -> Result<bool, RadioError<BUS>> {
335 Ok(self.brd_check_rf_frequency(frequency).await?)
336 }
337
338 /// Computes the packet time on air in ms for the given payload for a LoRa modem (can only be called once set_rx_config or set_tx_config have been called)
339 /// spreading_factor [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096 chips/symbol]
340 /// bandwidth [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved]
341 /// coding_rate [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8]
342 /// preamble_length length in symbols (the hardware adds 4 more symbols)
343 /// fixed_len fixed length packets [0: variable, 1: fixed]
344 /// payload_len sets payload length when fixed length is used
345 /// crc_on [0: OFF, 1: ON]
346 pub fn get_time_on_air(
347 &mut self,
348 spreading_factor: SpreadingFactor,
349 bandwidth: Bandwidth,
350 coding_rate: CodingRate,
351 preamble_length: u16,
352 fixed_len: bool,
353 payload_len: u8,
354 crc_on: bool,
355 ) -> Result<u32, RadioError<BUS>> {
356 let numerator = 1000
357 * Self::get_lora_time_on_air_numerator(
358 spreading_factor,
359 bandwidth,
360 coding_rate,
361 preamble_length,
362 fixed_len,
363 payload_len,
364 crc_on,
365 );
366 let denominator = bandwidth.value_in_hz();
367 if denominator == 0 {
368 Err(RadioError::InvalidBandwidth)
369 } else {
370 Ok((numerator + denominator - 1) / denominator)
371 }
372 }
373
374 /// Send the buffer of the given size. Prepares the packet to be sent and sets the radio in transmission [timeout in ms]
375 pub async fn send(&mut self, buffer: &[u8], timeout: u32) -> Result<(), RadioError<BUS>> {
376 if self.packet_params.is_some() {
377 self.sub_set_dio_irq_params(
378 IrqMask::TxDone.value() | IrqMask::RxTxTimeout.value(),
379 IrqMask::TxDone.value() | IrqMask::RxTxTimeout.value(),
380 IrqMask::None.value(),
381 IrqMask::None.value(),
382 )
383 .await?;
384
385 let mut packet_params = self.packet_params.as_mut().unwrap();
386 packet_params.payload_length = buffer.len() as u8;
387 self.sub_set_packet_params().await?;
388 self.sub_send_payload(buffer, timeout).await?;
389 Ok(())
390 } else {
391 Err(RadioError::PacketParamsMissing)
392 }
393 }
394
395 /// Set the radio in sleep mode
396 pub async fn sleep(&mut self) -> Result<(), RadioError<BUS>> {
397 self.sub_set_sleep(SleepParams {
398 wakeup_rtc: false,
399 reset: false,
400 warm_start: true,
401 })
402 .await?;
403 Timer::after(Duration::from_millis(2)).await;
404 Ok(())
405 }
406
407 /// Set the radio in standby mode
408 pub async fn standby(&mut self) -> Result<(), RadioError<BUS>> {
409 self.sub_set_standby(StandbyMode::RC).await?;
410 Ok(())
411 }
412
413 /// Set the radio in reception mode for the given duration [0: continuous, others: timeout (ms)]
414 pub async fn rx(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> {
415 self.sub_set_dio_irq_params(
416 IrqMask::All.value(),
417 IrqMask::All.value(),
418 IrqMask::None.value(),
419 IrqMask::None.value(),
420 )
421 .await?;
422
423 if self.rx_continuous {
424 self.sub_set_rx(0xFFFFFF).await?;
425 } else {
426 self.sub_set_rx(timeout << 6).await?;
427 }
428
429 Ok(())
430 }
431
432 /// Start a Channel Activity Detection
433 pub async fn start_cad(&mut self) -> Result<(), RadioError<BUS>> {
434 self.sub_set_dio_irq_params(
435 IrqMask::CADDone.value() | IrqMask::CADActivityDetected.value(),
436 IrqMask::CADDone.value() | IrqMask::CADActivityDetected.value(),
437 IrqMask::None.value(),
438 IrqMask::None.value(),
439 )
440 .await?;
441 self.sub_set_cad().await?;
442 Ok(())
443 }
444
445 /// Sets the radio in continuous wave transmission mode
446 /// frequency channel RF frequency
447 /// power output power [dBm]
448 /// timeout transmission mode timeout [s]
449 pub async fn set_tx_continuous_wave(
450 &mut self,
451 frequency: u32,
452 power: i8,
453 _timeout: u16,
454 ) -> Result<(), RadioError<BUS>> {
455 self.sub_set_rf_frequency(frequency).await?;
456 self.brd_set_rf_tx_power(power).await?;
457 self.sub_set_tx_continuous_wave().await?;
458
459 Ok(())
460 }
461
462 /// Read the current RSSI value for the LoRa modem (only) [dBm]
463 pub async fn get_rssi(&mut self) -> Result<i16, RadioError<BUS>> {
464 let value = self.sub_get_rssi_inst().await?;
465 Ok(value as i16)
466 }
467
468 /// Write one or more radio registers with a buffer of a given size, starting at the first register address
469 pub async fn write_registers_from_buffer(
470 &mut self,
471 start_register: Register,
472 buffer: &[u8],
473 ) -> Result<(), RadioError<BUS>> {
474 self.brd_write_registers(start_register, buffer).await?;
475 Ok(())
476 }
477
478 /// Read one or more radio registers into a buffer of a given size, starting at the first register address
479 pub async fn read_registers_into_buffer(
480 &mut self,
481 start_register: Register,
482 buffer: &mut [u8],
483 ) -> Result<(), RadioError<BUS>> {
484 self.brd_read_registers(start_register, buffer).await?;
485 Ok(())
486 }
487
488 /// Set the maximum payload length (in bytes) for a LoRa modem (only).
489 pub async fn set_max_payload_length(&mut self, max: u8) -> Result<(), RadioError<BUS>> {
490 if self.packet_params.is_some() {
491 let packet_params = self.packet_params.as_mut().unwrap();
492 self.max_payload_length = max;
493 packet_params.payload_length = max;
494 self.sub_set_packet_params().await?;
495 Ok(())
496 } else {
497 Err(RadioError::PacketParamsMissing)
498 }
499 }
500
501 /// Get the time required for the board plus radio to get out of sleep [ms]
502 pub fn get_wakeup_time(&mut self) -> u32 {
503 self.brd_get_board_tcxo_wakeup_time() + RADIO_WAKEUP_TIME
504 }
505
506 /// Process the radio irq
507 pub async fn process_irq(
508 &mut self,
509 receiving_buffer: Option<&mut [u8]>,
510 received_len: Option<&mut u8>,
511 cad_activity_detected: Option<&mut bool>,
512 ) -> Result<(), RadioError<BUS>> {
513 loop {
514 info!("process_irq loop entered"); // debug ???
515
516 let de = self.sub_get_device_errors().await?;
517 info!("device_errors: rc_64khz_calibration = {}, rc_13mhz_calibration = {}, pll_calibration = {}, adc_calibration = {}, image_calibration = {}, xosc_start = {}, pll_lock = {}, pa_ramp = {}",
518 de.rc_64khz_calibration, de.rc_13mhz_calibration, de.pll_calibration, de.adc_calibration, de.image_calibration, de.xosc_start, de.pll_lock, de.pa_ramp);
519 let st = self.sub_get_status().await?;
520 info!(
521 "radio status: cmd_status: {:x}, chip_mode: {:x}",
522 st.cmd_status, st.chip_mode
523 );
524
525 self.dio1.wait_for_high().await.map_err(|_| DIO1)?;
526 let operating_mode = self.brd_get_operating_mode();
527 let irq_flags = self.sub_get_irq_status().await?;
528 self.sub_clear_irq_status(irq_flags).await?;
529 info!("process_irq DIO1 satisfied: irq_flags = {:x}", irq_flags); // debug ???
530
531 // check for errors and unexpected interrupt masks (based on operation mode)
532 if (irq_flags & IrqMask::HeaderError.value()) == IrqMask::HeaderError.value() {
533 if !self.rx_continuous {
534 self.brd_set_operating_mode(RadioMode::StandbyRC);
535 }
536 return Err(RadioError::HeaderError);
537 } else if (irq_flags & IrqMask::CRCError.value()) == IrqMask::CRCError.value() {
538 if operating_mode == RadioMode::Receive {
539 if !self.rx_continuous {
540 self.brd_set_operating_mode(RadioMode::StandbyRC);
541 }
542 return Err(RadioError::CRCErrorOnReceive);
543 } else {
544 return Err(RadioError::CRCErrorUnexpected);
545 }
546 } else if (irq_flags & IrqMask::RxTxTimeout.value()) == IrqMask::RxTxTimeout.value() {
547 if operating_mode == RadioMode::Transmit {
548 self.brd_set_operating_mode(RadioMode::StandbyRC);
549 return Err(RadioError::TransmitTimeout);
550 } else if operating_mode == RadioMode::Receive {
551 self.brd_set_operating_mode(RadioMode::StandbyRC);
552 return Err(RadioError::ReceiveTimeout);
553 } else {
554 return Err(RadioError::TimeoutUnexpected);
555 }
556 } else if ((irq_flags & IrqMask::TxDone.value()) == IrqMask::TxDone.value())
557 && (operating_mode != RadioMode::Transmit)
558 {
559 return Err(RadioError::TransmitDoneUnexpected);
560 } else if ((irq_flags & IrqMask::RxDone.value()) == IrqMask::RxDone.value())
561 && (operating_mode != RadioMode::Receive)
562 {
563 return Err(RadioError::ReceiveDoneUnexpected);
564 } else if (((irq_flags & IrqMask::CADActivityDetected.value()) == IrqMask::CADActivityDetected.value())
565 || ((irq_flags & IrqMask::CADDone.value()) == IrqMask::CADDone.value()))
566 && (operating_mode != RadioMode::ChannelActivityDetection)
567 {
568 return Err(RadioError::CADUnexpected);
569 }
570
571 // debug ???
572 if (irq_flags & IrqMask::HeaderValid.value()) == IrqMask::HeaderValid.value() {
573 info!("HeaderValid");
574 } else if (irq_flags & IrqMask::PreambleDetected.value()) == IrqMask::PreambleDetected.value() {
575 info!("PreambleDetected");
576 } else if (irq_flags & IrqMask::SyncwordValid.value()) == IrqMask::SyncwordValid.value() {
577 info!("SyncwordValid");
578 }
579
580 // handle completions
581 if (irq_flags & IrqMask::TxDone.value()) == IrqMask::TxDone.value() {
582 self.brd_set_operating_mode(RadioMode::StandbyRC);
583 return Ok(());
584 } else if (irq_flags & IrqMask::RxDone.value()) == IrqMask::RxDone.value() {
585 if !self.rx_continuous {
586 self.brd_set_operating_mode(RadioMode::StandbyRC);
587
588 // implicit header mode timeout behavior (see DS_SX1261-2_V1.2 datasheet chapter 15.3)
589 self.brd_write_registers(Register::RTCCtrl, &[0x00]).await?;
590 let mut evt_clr = [0x00u8];
591 self.brd_read_registers(Register::EvtClr, &mut evt_clr).await?;
592 evt_clr[0] |= 1 << 1;
593 self.brd_write_registers(Register::EvtClr, &evt_clr).await?;
594 }
595
596 if receiving_buffer.is_some() && received_len.is_some() {
597 *(received_len.unwrap()) = self.sub_get_payload(receiving_buffer.unwrap()).await?;
598 }
599 self.packet_status = self.sub_get_packet_status().await?.into();
600 return Ok(());
601 } else if (irq_flags & IrqMask::CADDone.value()) == IrqMask::CADDone.value() {
602 if cad_activity_detected.is_some() {
603 *(cad_activity_detected.unwrap()) =
604 (irq_flags & IrqMask::CADActivityDetected.value()) == IrqMask::CADActivityDetected.value();
605 }
606 self.brd_set_operating_mode(RadioMode::StandbyRC);
607 return Ok(());
608 }
609
610 // if DIO1 was driven high for reasons other than an error or operation completion (currently, PreambleDetected, SyncwordValid, and HeaderValid
611 // are in that category), loop to wait again
612 }
613 }
614
615 // SX126x-specific functions
616
617 /// Set the radio in reception mode with Max LNA gain for the given time (SX126x radios only) [0: continuous, others timeout in ms]
618 pub async fn set_rx_boosted(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> {
619 self.sub_set_dio_irq_params(
620 IrqMask::All.value(),
621 IrqMask::All.value(),
622 IrqMask::None.value(),
623 IrqMask::None.value(),
624 )
625 .await?;
626
627 if self.rx_continuous {
628 self.sub_set_rx_boosted(0xFFFFFF).await?; // Rx continuous
629 } else {
630 self.sub_set_rx_boosted(timeout << 6).await?;
631 }
632
633 Ok(())
634 }
635
636 /// Set the Rx duty cycle management parameters (SX126x radios only)
637 /// rx_time structure describing reception timeout value
638 /// sleep_time structure describing sleep timeout value
639 pub async fn set_rx_duty_cycle(&mut self, rx_time: u32, sleep_time: u32) -> Result<(), RadioError<BUS>> {
640 self.sub_set_rx_duty_cycle(rx_time, sleep_time).await?;
641 Ok(())
642 }
643
644 pub fn get_latest_packet_status(&mut self) -> Option<PacketStatus> {
645 self.packet_status
646 }
647
648 // Utilities
649
650 async fn add_register_to_retention_list(&mut self, register_address: u16) -> Result<(), RadioError<BUS>> {
651 let mut buffer = [0x00u8; (1 + (2 * MAX_NUMBER_REGS_IN_RETENTION)) as usize];
652
653 // Read the address and registers already added to the list
654 self.brd_read_registers(Register::RetentionList, &mut buffer).await?;
655
656 let number_of_registers = buffer[0];
657 for i in 0..number_of_registers {
658 if register_address
659 == ((buffer[(1 + (2 * i)) as usize] as u16) << 8) | (buffer[(2 + (2 * i)) as usize] as u16)
660 {
661 return Ok(()); // register already in list
662 }
663 }
664
665 if number_of_registers < MAX_NUMBER_REGS_IN_RETENTION {
666 buffer[0] += 1; // increment number of registers
667
668 buffer[(1 + (2 * number_of_registers)) as usize] = ((register_address >> 8) & 0xFF) as u8;
669 buffer[(2 + (2 * number_of_registers)) as usize] = (register_address & 0xFF) as u8;
670 self.brd_write_registers(Register::RetentionList, &buffer).await?;
671
672 Ok(())
673 } else {
674 Err(RadioError::RetentionListExceeded)
675 }
676 }
677
678 fn get_lora_time_on_air_numerator(
679 spreading_factor: SpreadingFactor,
680 bandwidth: Bandwidth,
681 coding_rate: CodingRate,
682 preamble_length: u16,
683 fixed_len: bool,
684 payload_len: u8,
685 crc_on: bool,
686 ) -> u32 {
687 let cell_denominator;
688 let cr_denominator = (coding_rate.value() as i32) + 4;
689
690 // Ensure that the preamble length is at least 12 symbols when using SF5 or SF6
691 let mut preamble_length_final = preamble_length;
692 if ((spreading_factor == SpreadingFactor::_5) || (spreading_factor == SpreadingFactor::_6))
693 && (preamble_length < 12)
694 {
695 preamble_length_final = 12;
696 }
697
698 let mut low_data_rate_optimize = false;
699 if (((spreading_factor == SpreadingFactor::_11) || (spreading_factor == SpreadingFactor::_12))
700 && (bandwidth == Bandwidth::_125KHz))
701 || ((spreading_factor == SpreadingFactor::_12) && (bandwidth == Bandwidth::_250KHz))
702 {
703 low_data_rate_optimize = true;
704 }
705
706 let mut cell_numerator = ((payload_len as i32) << 3) + (if crc_on { 16 } else { 0 })
707 - (4 * spreading_factor.value() as i32)
708 + (if fixed_len { 0 } else { 20 });
709
710 if spreading_factor.value() <= 6 {
711 cell_denominator = 4 * (spreading_factor.value() as i32);
712 } else {
713 cell_numerator += 8;
714 if low_data_rate_optimize {
715 cell_denominator = 4 * ((spreading_factor.value() as i32) - 2);
716 } else {
717 cell_denominator = 4 * (spreading_factor.value() as i32);
718 }
719 }
720
721 if cell_numerator < 0 {
722 cell_numerator = 0;
723 }
724
725 let mut intermediate: i32 = (((cell_numerator + cell_denominator - 1) / cell_denominator) * cr_denominator)
726 + (preamble_length_final as i32)
727 + 12;
728
729 if spreading_factor.value() <= 6 {
730 intermediate = intermediate + 2;
731 }
732
733 (((4 * intermediate) + 1) * (1 << (spreading_factor.value() - 2))) as u32
734 }
735}
diff --git a/embassy-lora/src/sx126x/sx126x_lora/mod_params.rs b/embassy-lora/src/sx126x/sx126x_lora/mod_params.rs
new file mode 100644
index 000000000..e270b2a09
--- /dev/null
+++ b/embassy-lora/src/sx126x/sx126x_lora/mod_params.rs
@@ -0,0 +1,469 @@
1use core::fmt::Debug;
2
3use lorawan_device::async_device::radio as device;
4
5#[allow(clippy::upper_case_acronyms)]
6#[derive(Debug)]
7#[cfg_attr(feature = "defmt", derive(defmt::Format))]
8pub enum RadioError<BUS> {
9 SPI(BUS),
10 CS,
11 Reset,
12 AntRx,
13 AntTx,
14 Busy,
15 DIO1,
16 PayloadSizeMismatch(usize, usize),
17 RetentionListExceeded,
18 InvalidBandwidth,
19 ModulationParamsMissing,
20 PacketParamsMissing,
21 HeaderError,
22 CRCErrorUnexpected,
23 CRCErrorOnReceive,
24 TransmitTimeout,
25 ReceiveTimeout,
26 TimeoutUnexpected,
27 TransmitDoneUnexpected,
28 ReceiveDoneUnexpected,
29 CADUnexpected,
30}
31
32pub struct RadioSystemError {
33 pub rc_64khz_calibration: bool,
34 pub rc_13mhz_calibration: bool,
35 pub pll_calibration: bool,
36 pub adc_calibration: bool,
37 pub image_calibration: bool,
38 pub xosc_start: bool,
39 pub pll_lock: bool,
40 pub pa_ramp: bool,
41}
42
43#[derive(Clone, Copy, PartialEq)]
44pub enum PacketType {
45 GFSK = 0x00,
46 LoRa = 0x01,
47 None = 0x0F,
48}
49
50impl PacketType {
51 pub const fn value(self) -> u8 {
52 self as u8
53 }
54 pub fn to_enum(value: u8) -> Self {
55 if value == 0x00 {
56 PacketType::GFSK
57 } else if value == 0x01 {
58 PacketType::LoRa
59 } else {
60 PacketType::None
61 }
62 }
63}
64
65#[derive(Clone, Copy)]
66pub struct PacketStatus {
67 pub rssi: i8,
68 pub snr: i8,
69 pub signal_rssi: i8,
70 pub freq_error: u32,
71}
72
73#[derive(Clone, Copy, PartialEq)]
74pub enum RadioType {
75 SX1261,
76 SX1262,
77}
78
79#[derive(Clone, Copy, PartialEq)]
80pub enum RadioMode {
81 Sleep = 0x00, // sleep mode
82 StandbyRC = 0x01, // standby mode with RC oscillator
83 StandbyXOSC = 0x02, // standby mode with XOSC oscillator
84 FrequencySynthesis = 0x03, // frequency synthesis mode
85 Transmit = 0x04, // transmit mode
86 Receive = 0x05, // receive mode
87 ReceiveDutyCycle = 0x06, // receive duty cycle mode
88 ChannelActivityDetection = 0x07, // channel activity detection mode
89}
90
91impl RadioMode {
92 /// Returns the value of the mode.
93 pub const fn value(self) -> u8 {
94 self as u8
95 }
96 pub fn to_enum(value: u8) -> Self {
97 if value == 0x00 {
98 RadioMode::Sleep
99 } else if value == 0x01 {
100 RadioMode::StandbyRC
101 } else if value == 0x02 {
102 RadioMode::StandbyXOSC
103 } else if value == 0x03 {
104 RadioMode::FrequencySynthesis
105 } else if value == 0x04 {
106 RadioMode::Transmit
107 } else if value == 0x05 {
108 RadioMode::Receive
109 } else if value == 0x06 {
110 RadioMode::ReceiveDutyCycle
111 } else if value == 0x07 {
112 RadioMode::ChannelActivityDetection
113 } else {
114 RadioMode::Sleep
115 }
116 }
117}
118
119pub enum RadioState {
120 Idle = 0x00,
121 RxRunning = 0x01,
122 TxRunning = 0x02,
123 ChannelActivityDetecting = 0x03,
124}
125
126impl RadioState {
127 /// Returns the value of the state.
128 pub fn value(self) -> u8 {
129 self as u8
130 }
131}
132
133pub struct RadioStatus {
134 pub cmd_status: u8,
135 pub chip_mode: u8,
136}
137
138impl RadioStatus {
139 pub fn value(self) -> u8 {
140 (self.chip_mode << 4) | (self.cmd_status << 1)
141 }
142}
143
144#[derive(Clone, Copy)]
145pub enum IrqMask {
146 None = 0x0000,
147 TxDone = 0x0001,
148 RxDone = 0x0002,
149 PreambleDetected = 0x0004,
150 SyncwordValid = 0x0008,
151 HeaderValid = 0x0010,
152 HeaderError = 0x0020,
153 CRCError = 0x0040,
154 CADDone = 0x0080,
155 CADActivityDetected = 0x0100,
156 RxTxTimeout = 0x0200,
157 All = 0xFFFF,
158}
159
160impl IrqMask {
161 pub fn value(self) -> u16 {
162 self as u16
163 }
164}
165
166#[derive(Clone, Copy)]
167pub enum Register {
168 PacketParams = 0x0704, // packet configuration
169 PayloadLength = 0x0702, // payload size
170 SynchTimeout = 0x0706, // recalculated number of symbols
171 Syncword = 0x06C0, // Syncword values
172 LoRaSyncword = 0x0740, // LoRa Syncword value
173 GeneratedRandomNumber = 0x0819, //32-bit generated random number
174 AnaLNA = 0x08E2, // disable the LNA
175 AnaMixer = 0x08E5, // disable the mixer
176 RxGain = 0x08AC, // RX gain (0x94: power saving, 0x96: rx boosted)
177 XTATrim = 0x0911, // device internal trimming capacitor
178 OCP = 0x08E7, // over current protection max value
179 RetentionList = 0x029F, // retention list
180 IQPolarity = 0x0736, // optimize the inverted IQ operation (see DS_SX1261-2_V1.2 datasheet chapter 15.4)
181 TxModulation = 0x0889, // modulation quality with 500 kHz LoRa Bandwidth (see DS_SX1261-2_V1.2 datasheet chapter 15.1)
182 TxClampCfg = 0x08D8, // better resistance to antenna mismatch (see DS_SX1261-2_V1.2 datasheet chapter 15.2)
183 RTCCtrl = 0x0902, // RTC control
184 EvtClr = 0x0944, // event clear
185}
186
187impl Register {
188 pub fn addr(self) -> u16 {
189 self as u16
190 }
191}
192
193#[derive(Clone, Copy, PartialEq)]
194pub enum OpCode {
195 GetStatus = 0xC0,
196 WriteRegister = 0x0D,
197 ReadRegister = 0x1D,
198 WriteBuffer = 0x0E,
199 ReadBuffer = 0x1E,
200 SetSleep = 0x84,
201 SetStandby = 0x80,
202 SetFS = 0xC1,
203 SetTx = 0x83,
204 SetRx = 0x82,
205 SetRxDutyCycle = 0x94,
206 SetCAD = 0xC5,
207 SetTxContinuousWave = 0xD1,
208 SetTxContinuousPremable = 0xD2,
209 SetPacketType = 0x8A,
210 GetPacketType = 0x11,
211 SetRFFrequency = 0x86,
212 SetTxParams = 0x8E,
213 SetPAConfig = 0x95,
214 SetCADParams = 0x88,
215 SetBufferBaseAddress = 0x8F,
216 SetModulationParams = 0x8B,
217 SetPacketParams = 0x8C,
218 GetRxBufferStatus = 0x13,
219 GetPacketStatus = 0x14,
220 GetRSSIInst = 0x15,
221 GetStats = 0x10,
222 ResetStats = 0x00,
223 CfgDIOIrq = 0x08,
224 GetIrqStatus = 0x12,
225 ClrIrqStatus = 0x02,
226 Calibrate = 0x89,
227 CalibrateImage = 0x98,
228 SetRegulatorMode = 0x96,
229 GetErrors = 0x17,
230 ClrErrors = 0x07,
231 SetTCXOMode = 0x97,
232 SetTxFallbackMode = 0x93,
233 SetRFSwitchMode = 0x9D,
234 SetStopRxTimerOnPreamble = 0x9F,
235 SetLoRaSymbTimeout = 0xA0,
236}
237
238impl OpCode {
239 pub fn value(self) -> u8 {
240 self as u8
241 }
242}
243
244pub struct SleepParams {
245 pub wakeup_rtc: bool, // get out of sleep mode if wakeup signal received from RTC
246 pub reset: bool,
247 pub warm_start: bool,
248}
249
250impl SleepParams {
251 pub fn value(self) -> u8 {
252 ((self.warm_start as u8) << 2) | ((self.reset as u8) << 1) | (self.wakeup_rtc as u8)
253 }
254}
255
256#[derive(Clone, Copy, PartialEq)]
257pub enum StandbyMode {
258 RC = 0x00,
259 XOSC = 0x01,
260}
261
262impl StandbyMode {
263 pub fn value(self) -> u8 {
264 self as u8
265 }
266}
267
268#[derive(Clone, Copy)]
269pub enum RegulatorMode {
270 UseLDO = 0x00,
271 UseDCDC = 0x01,
272}
273
274impl RegulatorMode {
275 pub fn value(self) -> u8 {
276 self as u8
277 }
278}
279
280#[derive(Clone, Copy)]
281pub struct CalibrationParams {
282 pub rc64k_enable: bool, // calibrate RC64K clock
283 pub rc13m_enable: bool, // calibrate RC13M clock
284 pub pll_enable: bool, // calibrate PLL
285 pub adc_pulse_enable: bool, // calibrate ADC Pulse
286 pub adc_bulkn_enable: bool, // calibrate ADC bulkN
287 pub adc_bulkp_enable: bool, // calibrate ADC bulkP
288 pub img_enable: bool,
289}
290
291impl CalibrationParams {
292 pub fn value(self) -> u8 {
293 ((self.img_enable as u8) << 6)
294 | ((self.adc_bulkp_enable as u8) << 5)
295 | ((self.adc_bulkn_enable as u8) << 4)
296 | ((self.adc_pulse_enable as u8) << 3)
297 | ((self.pll_enable as u8) << 2)
298 | ((self.rc13m_enable as u8) << 1)
299 | ((self.rc64k_enable as u8) << 0)
300 }
301}
302
303#[derive(Clone, Copy)]
304pub enum TcxoCtrlVoltage {
305 Ctrl1V6 = 0x00,
306 Ctrl1V7 = 0x01,
307 Ctrl1V8 = 0x02,
308 Ctrl2V2 = 0x03,
309 Ctrl2V4 = 0x04,
310 Ctrl2V7 = 0x05,
311 Ctrl3V0 = 0x06,
312 Ctrl3V3 = 0x07,
313}
314
315impl TcxoCtrlVoltage {
316 pub fn value(self) -> u8 {
317 self as u8
318 }
319}
320
321#[derive(Clone, Copy)]
322pub enum RampTime {
323 Ramp10Us = 0x00,
324 Ramp20Us = 0x01,
325 Ramp40Us = 0x02,
326 Ramp80Us = 0x03,
327 Ramp200Us = 0x04,
328 Ramp800Us = 0x05,
329 Ramp1700Us = 0x06,
330 Ramp3400Us = 0x07,
331}
332
333impl RampTime {
334 pub fn value(self) -> u8 {
335 self as u8
336 }
337}
338
339#[derive(Clone, Copy, PartialEq)]
340pub enum SpreadingFactor {
341 _5 = 0x05,
342 _6 = 0x06,
343 _7 = 0x07,
344 _8 = 0x08,
345 _9 = 0x09,
346 _10 = 0x0A,
347 _11 = 0x0B,
348 _12 = 0x0C,
349}
350
351impl SpreadingFactor {
352 pub fn value(self) -> u8 {
353 self as u8
354 }
355}
356
357impl From<device::SpreadingFactor> for SpreadingFactor {
358 fn from(sf: device::SpreadingFactor) -> Self {
359 match sf {
360 device::SpreadingFactor::_7 => SpreadingFactor::_7,
361 device::SpreadingFactor::_8 => SpreadingFactor::_8,
362 device::SpreadingFactor::_9 => SpreadingFactor::_9,
363 device::SpreadingFactor::_10 => SpreadingFactor::_10,
364 device::SpreadingFactor::_11 => SpreadingFactor::_11,
365 device::SpreadingFactor::_12 => SpreadingFactor::_12,
366 }
367 }
368}
369
370#[derive(Clone, Copy, PartialEq)]
371pub enum Bandwidth {
372 _500KHz = 0x06,
373 _250KHz = 0x05,
374 _125KHz = 0x04,
375}
376
377impl Bandwidth {
378 pub fn value(self) -> u8 {
379 self as u8
380 }
381
382 pub fn value_in_hz(self) -> u32 {
383 match self {
384 Bandwidth::_125KHz => 125000u32,
385 Bandwidth::_250KHz => 250000u32,
386 Bandwidth::_500KHz => 500000u32,
387 }
388 }
389}
390
391impl From<device::Bandwidth> for Bandwidth {
392 fn from(bw: device::Bandwidth) -> Self {
393 match bw {
394 device::Bandwidth::_500KHz => Bandwidth::_500KHz,
395 device::Bandwidth::_250KHz => Bandwidth::_250KHz,
396 device::Bandwidth::_125KHz => Bandwidth::_125KHz,
397 }
398 }
399}
400
401#[derive(Clone, Copy)]
402pub enum CodingRate {
403 _4_5 = 0x01,
404 _4_6 = 0x02,
405 _4_7 = 0x03,
406 _4_8 = 0x04,
407}
408
409impl CodingRate {
410 pub fn value(self) -> u8 {
411 self as u8
412 }
413}
414
415impl From<device::CodingRate> for CodingRate {
416 fn from(cr: device::CodingRate) -> Self {
417 match cr {
418 device::CodingRate::_4_5 => CodingRate::_4_5,
419 device::CodingRate::_4_6 => CodingRate::_4_6,
420 device::CodingRate::_4_7 => CodingRate::_4_7,
421 device::CodingRate::_4_8 => CodingRate::_4_8,
422 }
423 }
424}
425
426#[derive(Clone, Copy)]
427pub struct ModulationParams {
428 pub spreading_factor: SpreadingFactor,
429 pub bandwidth: Bandwidth,
430 pub coding_rate: CodingRate,
431 pub low_data_rate_optimize: u8,
432}
433
434#[derive(Clone, Copy)]
435pub struct PacketParams {
436 pub preamble_length: u16, // number of LoRa symbols in the preamble
437 pub implicit_header: bool, // if the header is explicit, it will be transmitted in the LoRa packet, but is not transmitted if the header is implicit (known fixed length)
438 pub payload_length: u8,
439 pub crc_on: bool,
440 pub iq_inverted: bool,
441}
442
443#[derive(Clone, Copy)]
444pub enum CADSymbols {
445 _1 = 0x00,
446 _2 = 0x01,
447 _4 = 0x02,
448 _8 = 0x03,
449 _16 = 0x04,
450}
451
452impl CADSymbols {
453 pub fn value(self) -> u8 {
454 self as u8
455 }
456}
457
458#[derive(Clone, Copy)]
459pub enum CADExitMode {
460 CADOnly = 0x00,
461 CADRx = 0x01,
462 CADLBT = 0x10,
463}
464
465impl CADExitMode {
466 pub fn value(self) -> u8 {
467 self as u8
468 }
469}
diff --git a/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs b/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs
new file mode 100644
index 000000000..02a0a72ec
--- /dev/null
+++ b/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs
@@ -0,0 +1,688 @@
1use embedded_hal::digital::v2::OutputPin;
2use embedded_hal_async::digital::Wait;
3use embedded_hal_async::spi::SpiBus;
4
5use super::mod_params::*;
6use super::LoRa;
7
8// Internal frequency of the radio
9const SX126X_XTAL_FREQ: u32 = 32000000;
10
11// Scaling factor used to perform fixed-point operations
12const SX126X_PLL_STEP_SHIFT_AMOUNT: u32 = 14;
13
14// PLL step - scaled with SX126X_PLL_STEP_SHIFT_AMOUNT
15const SX126X_PLL_STEP_SCALED: u32 = SX126X_XTAL_FREQ >> (25 - SX126X_PLL_STEP_SHIFT_AMOUNT);
16
17// Maximum value for parameter symbNum
18const SX126X_MAX_LORA_SYMB_NUM_TIMEOUT: u8 = 248;
19
20// Provides board-specific functionality for Semtech SX126x-based boards
21
22impl<SPI, CS, RESET, ANTRX, ANTTX, WAIT, BUS> LoRa<SPI, CS, RESET, ANTRX, ANTTX, WAIT>
23where
24 SPI: SpiBus<u8, Error = BUS>,
25 CS: OutputPin,
26 RESET: OutputPin,
27 ANTRX: OutputPin,
28 ANTTX: OutputPin,
29 WAIT: Wait,
30{
31 // Initialize the radio driver
32 pub(super) async fn sub_init(&mut self) -> Result<(), RadioError<BUS>> {
33 self.brd_reset().await?;
34 self.brd_wakeup().await?;
35 self.sub_set_standby(StandbyMode::RC).await?;
36 self.brd_io_tcxo_init().await?;
37 self.brd_io_rf_switch_init().await?;
38 self.image_calibrated = false;
39 Ok(())
40 }
41
42 // Wakeup the radio if it is in Sleep mode and check that Busy is low
43 pub(super) async fn sub_check_device_ready(&mut self) -> Result<(), RadioError<BUS>> {
44 let operating_mode = self.brd_get_operating_mode();
45 if operating_mode == RadioMode::Sleep || operating_mode == RadioMode::ReceiveDutyCycle {
46 self.brd_wakeup().await?;
47 self.brd_ant_sw_on().await?;
48 }
49 self.brd_wait_on_busy().await?;
50 Ok(())
51 }
52
53 // Save the payload to be sent in the radio buffer
54 pub(super) async fn sub_set_payload(&mut self, payload: &[u8]) -> Result<(), RadioError<BUS>> {
55 self.brd_write_buffer(0x00, payload).await?;
56 Ok(())
57 }
58
59 // Read the payload received.
60 pub(super) async fn sub_get_payload(&mut self, buffer: &mut [u8]) -> Result<u8, RadioError<BUS>> {
61 let (size, offset) = self.sub_get_rx_buffer_status().await?;
62 if (size as usize) > buffer.len() {
63 Err(RadioError::PayloadSizeMismatch(size as usize, buffer.len()))
64 } else {
65 self.brd_read_buffer(offset, buffer).await?;
66 Ok(size)
67 }
68 }
69
70 // Send a payload
71 pub(super) async fn sub_send_payload(&mut self, payload: &[u8], timeout: u32) -> Result<(), RadioError<BUS>> {
72 self.sub_set_payload(payload).await?;
73 self.sub_set_tx(timeout).await?;
74 Ok(())
75 }
76
77 // Get a 32-bit random value generated by the radio. A valid packet type must have been configured before using this command.
78 //
79 // The radio must be in reception mode before executing this function. This code can potentially result in interrupt generation. It is the responsibility of
80 // the calling code to disable radio interrupts before calling this function, and re-enable them afterwards if necessary, or be certain that any interrupts
81 // generated during this process will not cause undesired side-effects in the software.
82 //
83 // The random numbers produced by the generator do not have a uniform or Gaussian distribution. If uniformity is needed, perform appropriate software post-processing.
84 pub(super) async fn sub_get_random(&mut self) -> Result<u32, RadioError<BUS>> {
85 let mut reg_ana_lna_buffer_original = [0x00u8];
86 let mut reg_ana_mixer_buffer_original = [0x00u8];
87 let mut reg_ana_lna_buffer = [0x00u8];
88 let mut reg_ana_mixer_buffer = [0x00u8];
89 let mut number_buffer = [0x00u8, 0x00u8, 0x00u8, 0x00u8];
90
91 self.brd_read_registers(Register::AnaLNA, &mut reg_ana_lna_buffer_original)
92 .await?;
93 reg_ana_lna_buffer[0] = reg_ana_lna_buffer_original[0] & (!(1 << 0));
94 self.brd_write_registers(Register::AnaLNA, &reg_ana_lna_buffer).await?;
95
96 self.brd_read_registers(Register::AnaMixer, &mut reg_ana_mixer_buffer_original)
97 .await?;
98 reg_ana_mixer_buffer[0] = reg_ana_mixer_buffer_original[0] & (!(1 << 7));
99 self.brd_write_registers(Register::AnaMixer, &reg_ana_mixer_buffer)
100 .await?;
101
102 // Set radio in continuous reception
103 self.sub_set_rx(0xFFFFFFu32).await?;
104
105 self.brd_read_registers(Register::GeneratedRandomNumber, &mut number_buffer)
106 .await?;
107
108 self.sub_set_standby(StandbyMode::RC).await?;
109
110 self.brd_write_registers(Register::AnaLNA, &reg_ana_lna_buffer_original)
111 .await?;
112 self.brd_write_registers(Register::AnaMixer, &reg_ana_mixer_buffer_original)
113 .await?;
114
115 Ok(Self::convert_u8_buffer_to_u32(&number_buffer))
116 }
117
118 // Set the radio in sleep mode
119 pub(super) async fn sub_set_sleep(&mut self, sleep_config: SleepParams) -> Result<(), RadioError<BUS>> {
120 self.brd_ant_sw_off().await?;
121
122 let _fix_rx = self.antenna_rx.set_low();
123 let _fix_tx = self.antenna_tx.set_low();
124
125 if !sleep_config.warm_start {
126 self.image_calibrated = false;
127 }
128
129 self.brd_write_command(OpCode::SetSleep, &[sleep_config.value()])
130 .await?;
131 self.brd_set_operating_mode(RadioMode::Sleep);
132 Ok(())
133 }
134
135 // Set the radio in configuration mode
136 pub(super) async fn sub_set_standby(&mut self, mode: StandbyMode) -> Result<(), RadioError<BUS>> {
137 self.brd_write_command(OpCode::SetStandby, &[mode.value()]).await?;
138 if mode == StandbyMode::RC {
139 self.brd_set_operating_mode(RadioMode::StandbyRC);
140 } else {
141 self.brd_set_operating_mode(RadioMode::StandbyXOSC);
142 }
143
144 let _fix_rx = self.antenna_rx.set_low();
145 let _fix_tx = self.antenna_tx.set_low();
146 Ok(())
147 }
148
149 // Set the radio in FS mode
150 pub(super) async fn sub_set_fs(&mut self) -> Result<(), RadioError<BUS>> {
151 // antenna settings ???
152 self.brd_write_command(OpCode::SetFS, &[]).await?;
153 self.brd_set_operating_mode(RadioMode::FrequencySynthesis);
154 Ok(())
155 }
156
157 // Set the radio in transmission mode with timeout specified
158 pub(super) async fn sub_set_tx(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> {
159 let buffer = [
160 Self::timeout_1(timeout),
161 Self::timeout_2(timeout),
162 Self::timeout_3(timeout),
163 ];
164
165 let _fix_rx = self.antenna_rx.set_low();
166 let _fix_tx = self.antenna_tx.set_high();
167
168 self.brd_set_operating_mode(RadioMode::Transmit);
169 self.brd_write_command(OpCode::SetTx, &buffer).await?;
170 Ok(())
171 }
172
173 // Set the radio in reception mode with timeout specified
174 pub(super) async fn sub_set_rx(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> {
175 let buffer = [
176 Self::timeout_1(timeout),
177 Self::timeout_2(timeout),
178 Self::timeout_3(timeout),
179 ];
180
181 let _fix_rx = self.antenna_rx.set_high();
182 let _fix_tx = self.antenna_tx.set_low();
183
184 self.brd_set_operating_mode(RadioMode::Receive);
185 self.brd_write_registers(Register::RxGain, &[0x94u8]).await?;
186 self.brd_write_command(OpCode::SetRx, &buffer).await?;
187 Ok(())
188 }
189
190 // Set the radio in reception mode with Boosted LNA gain and timeout specified
191 pub(super) async fn sub_set_rx_boosted(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> {
192 let buffer = [
193 Self::timeout_1(timeout),
194 Self::timeout_2(timeout),
195 Self::timeout_3(timeout),
196 ];
197
198 let _fix_rx = self.antenna_rx.set_high();
199 let _fix_tx = self.antenna_tx.set_low();
200
201 self.brd_set_operating_mode(RadioMode::Receive);
202 // set max LNA gain, increase current by ~2mA for around ~3dB in sensitivity
203 self.brd_write_registers(Register::RxGain, &[0x96u8]).await?;
204 self.brd_write_command(OpCode::SetRx, &buffer).await?;
205 Ok(())
206 }
207
208 // Set the Rx duty cycle management parameters
209 pub(super) async fn sub_set_rx_duty_cycle(&mut self, rx_time: u32, sleep_time: u32) -> Result<(), RadioError<BUS>> {
210 let buffer = [
211 ((rx_time >> 16) & 0xFF) as u8,
212 ((rx_time >> 8) & 0xFF) as u8,
213 (rx_time & 0xFF) as u8,
214 ((sleep_time >> 16) & 0xFF) as u8,
215 ((sleep_time >> 8) & 0xFF) as u8,
216 (sleep_time & 0xFF) as u8,
217 ];
218
219 // antenna settings ???
220
221 self.brd_write_command(OpCode::SetRxDutyCycle, &buffer).await?;
222 self.brd_set_operating_mode(RadioMode::ReceiveDutyCycle);
223 Ok(())
224 }
225
226 // Set the radio in CAD mode
227 pub(super) async fn sub_set_cad(&mut self) -> Result<(), RadioError<BUS>> {
228 let _fix_rx = self.antenna_rx.set_high();
229 let _fix_tx = self.antenna_tx.set_low();
230
231 self.brd_write_command(OpCode::SetCAD, &[]).await?;
232 self.brd_set_operating_mode(RadioMode::ChannelActivityDetection);
233 Ok(())
234 }
235
236 // Set the radio in continuous wave transmission mode
237 pub(super) async fn sub_set_tx_continuous_wave(&mut self) -> Result<(), RadioError<BUS>> {
238 let _fix_rx = self.antenna_rx.set_low();
239 let _fix_tx = self.antenna_tx.set_high();
240
241 self.brd_write_command(OpCode::SetTxContinuousWave, &[]).await?;
242 self.brd_set_operating_mode(RadioMode::Transmit);
243 Ok(())
244 }
245
246 // Set the radio in continuous preamble transmission mode
247 pub(super) async fn sub_set_tx_infinite_preamble(&mut self) -> Result<(), RadioError<BUS>> {
248 let _fix_rx = self.antenna_rx.set_low();
249 let _fix_tx = self.antenna_tx.set_high();
250
251 self.brd_write_command(OpCode::SetTxContinuousPremable, &[]).await?;
252 self.brd_set_operating_mode(RadioMode::Transmit);
253 Ok(())
254 }
255
256 // Decide which interrupt will stop the internal radio rx timer.
257 // false timer stop after header/syncword detection
258 // true timer stop after preamble detection
259 pub(super) async fn sub_set_stop_rx_timer_on_preamble_detect(
260 &mut self,
261 enable: bool,
262 ) -> Result<(), RadioError<BUS>> {
263 self.brd_write_command(OpCode::SetStopRxTimerOnPreamble, &[enable as u8])
264 .await?;
265 Ok(())
266 }
267
268 // Set the number of symbols the radio will wait to validate a reception
269 pub(super) async fn sub_set_lora_symb_num_timeout(&mut self, symb_num: u16) -> Result<(), RadioError<BUS>> {
270 let mut exp = 0u8;
271 let mut reg;
272 let mut mant = ((core::cmp::min(symb_num, SX126X_MAX_LORA_SYMB_NUM_TIMEOUT as u16) as u8) + 1) >> 1;
273 while mant > 31 {
274 mant = (mant + 3) >> 2;
275 exp += 1;
276 }
277 reg = mant << ((2 * exp) + 1);
278
279 self.brd_write_command(OpCode::SetLoRaSymbTimeout, &[reg]).await?;
280
281 if symb_num != 0 {
282 reg = exp + (mant << 3);
283 self.brd_write_registers(Register::SynchTimeout, &[reg]).await?;
284 }
285
286 Ok(())
287 }
288
289 // Set the power regulators operating mode (LDO or DC_DC). Using only LDO implies that the Rx or Tx current is doubled
290 pub(super) async fn sub_set_regulator_mode(&mut self, mode: RegulatorMode) -> Result<(), RadioError<BUS>> {
291 self.brd_write_command(OpCode::SetRegulatorMode, &[mode.value()])
292 .await?;
293 Ok(())
294 }
295
296 // Calibrate the given radio block
297 pub(super) async fn sub_calibrate(&mut self, calibrate_params: CalibrationParams) -> Result<(), RadioError<BUS>> {
298 self.brd_write_command(OpCode::Calibrate, &[calibrate_params.value()])
299 .await?;
300 Ok(())
301 }
302
303 // Calibrate the image rejection based on the given frequency
304 pub(super) async fn sub_calibrate_image(&mut self, freq: u32) -> Result<(), RadioError<BUS>> {
305 let mut cal_freq = [0x00u8, 0x00u8];
306
307 if freq > 900000000 {
308 cal_freq[0] = 0xE1;
309 cal_freq[1] = 0xE9;
310 } else if freq > 850000000 {
311 cal_freq[0] = 0xD7;
312 cal_freq[1] = 0xDB;
313 } else if freq > 770000000 {
314 cal_freq[0] = 0xC1;
315 cal_freq[1] = 0xC5;
316 } else if freq > 460000000 {
317 cal_freq[0] = 0x75;
318 cal_freq[1] = 0x81;
319 } else if freq > 425000000 {
320 cal_freq[0] = 0x6B;
321 cal_freq[1] = 0x6F;
322 }
323 self.brd_write_command(OpCode::CalibrateImage, &cal_freq).await?;
324 Ok(())
325 }
326
327 // Activate the extention of the timeout when a long preamble is used
328 pub(super) async fn sub_set_long_preamble(&mut self, _enable: u8) -> Result<(), RadioError<BUS>> {
329 Ok(()) // no operation currently
330 }
331
332 // Set the transmission parameters
333 // hp_max 0 for sx1261, 7 for sx1262
334 // device_sel 1 for sx1261, 0 for sx1262
335 // pa_lut 0 for 14dBm LUT, 1 for 22dBm LUT
336 pub(super) async fn sub_set_pa_config(
337 &mut self,
338 pa_duty_cycle: u8,
339 hp_max: u8,
340 device_sel: u8,
341 pa_lut: u8,
342 ) -> Result<(), RadioError<BUS>> {
343 self.brd_write_command(OpCode::SetPAConfig, &[pa_duty_cycle, hp_max, device_sel, pa_lut])
344 .await?;
345 Ok(())
346 }
347
348 // Define into which mode the chip goes after a TX / RX done
349 pub(super) async fn sub_set_rx_tx_fallback_mode(&mut self, fallback_mode: u8) -> Result<(), RadioError<BUS>> {
350 self.brd_write_command(OpCode::SetTxFallbackMode, &[fallback_mode])
351 .await?;
352 Ok(())
353 }
354
355 // Set the IRQ mask and DIO masks
356 pub(super) async fn sub_set_dio_irq_params(
357 &mut self,
358 irq_mask: u16,
359 dio1_mask: u16,
360 dio2_mask: u16,
361 dio3_mask: u16,
362 ) -> Result<(), RadioError<BUS>> {
363 let mut buffer = [0x00u8; 8];
364
365 buffer[0] = ((irq_mask >> 8) & 0x00FF) as u8;
366 buffer[1] = (irq_mask & 0x00FF) as u8;
367 buffer[2] = ((dio1_mask >> 8) & 0x00FF) as u8;
368 buffer[3] = (dio1_mask & 0x00FF) as u8;
369 buffer[4] = ((dio2_mask >> 8) & 0x00FF) as u8;
370 buffer[5] = (dio2_mask & 0x00FF) as u8;
371 buffer[6] = ((dio3_mask >> 8) & 0x00FF) as u8;
372 buffer[7] = (dio3_mask & 0x00FF) as u8;
373 self.brd_write_command(OpCode::CfgDIOIrq, &buffer).await?;
374 Ok(())
375 }
376
377 // Return the current IRQ status
378 pub(super) async fn sub_get_irq_status(&mut self) -> Result<u16, RadioError<BUS>> {
379 let mut irq_status = [0x00u8, 0x00u8];
380 self.brd_read_command(OpCode::GetIrqStatus, &mut irq_status).await?;
381 Ok(((irq_status[0] as u16) << 8) | (irq_status[1] as u16))
382 }
383
384 // Indicate if DIO2 is used to control an RF Switch
385 pub(super) async fn sub_set_dio2_as_rf_switch_ctrl(&mut self, enable: bool) -> Result<(), RadioError<BUS>> {
386 self.brd_write_command(OpCode::SetRFSwitchMode, &[enable as u8]).await?;
387 Ok(())
388 }
389
390 // Indicate if the radio main clock is supplied from a TCXO
391 // tcxo_voltage voltage used to control the TCXO on/off from DIO3
392 // timeout duration given to the TCXO to go to 32MHz
393 pub(super) async fn sub_set_dio3_as_tcxo_ctrl(
394 &mut self,
395 tcxo_voltage: TcxoCtrlVoltage,
396 timeout: u32,
397 ) -> Result<(), RadioError<BUS>> {
398 let buffer = [
399 tcxo_voltage.value() & 0x07,
400 Self::timeout_1(timeout),
401 Self::timeout_2(timeout),
402 Self::timeout_3(timeout),
403 ];
404 self.brd_write_command(OpCode::SetTCXOMode, &buffer).await?;
405
406 Ok(())
407 }
408
409 // Set the RF frequency (Hz)
410 pub(super) async fn sub_set_rf_frequency(&mut self, frequency: u32) -> Result<(), RadioError<BUS>> {
411 let mut buffer = [0x00u8; 4];
412
413 if !self.image_calibrated {
414 self.sub_calibrate_image(frequency).await?;
415 self.image_calibrated = true;
416 }
417
418 let freq_in_pll_steps = Self::convert_freq_in_hz_to_pll_step(frequency);
419
420 buffer[0] = ((freq_in_pll_steps >> 24) & 0xFF) as u8;
421 buffer[1] = ((freq_in_pll_steps >> 16) & 0xFF) as u8;
422 buffer[2] = ((freq_in_pll_steps >> 8) & 0xFF) as u8;
423 buffer[3] = (freq_in_pll_steps & 0xFF) as u8;
424 self.brd_write_command(OpCode::SetRFFrequency, &buffer).await?;
425 Ok(())
426 }
427
428 // Set the radio for the given protocol (LoRa or GFSK). This method has to be called before setting RF frequency, modulation paramaters, and packet paramaters.
429 pub(super) async fn sub_set_packet_type(&mut self, packet_type: PacketType) -> Result<(), RadioError<BUS>> {
430 self.packet_type = packet_type;
431 self.brd_write_command(OpCode::SetPacketType, &[packet_type.value()])
432 .await?;
433 Ok(())
434 }
435
436 // Get the current radio protocol (LoRa or GFSK)
437 pub(super) fn sub_get_packet_type(&mut self) -> PacketType {
438 self.packet_type
439 }
440
441 // Set the transmission parameters
442 // power RF output power [-18..13] dBm
443 // ramp_time transmission ramp up time
444 pub(super) async fn sub_set_tx_params(
445 &mut self,
446 mut power: i8,
447 ramp_time: RampTime,
448 ) -> Result<(), RadioError<BUS>> {
449 if self.brd_get_radio_type() == RadioType::SX1261 {
450 if power == 15 {
451 self.sub_set_pa_config(0x06, 0x00, 0x01, 0x01).await?;
452 } else {
453 self.sub_set_pa_config(0x04, 0x00, 0x01, 0x01).await?;
454 }
455
456 if power >= 14 {
457 power = 14;
458 } else if power < -17 {
459 power = -17;
460 }
461 } else {
462 // Provide better resistance of the SX1262 Tx to antenna mismatch (see DS_SX1261-2_V1.2 datasheet chapter 15.2)
463 let mut tx_clamp_cfg = [0x00u8];
464 self.brd_read_registers(Register::TxClampCfg, &mut tx_clamp_cfg).await?;
465 tx_clamp_cfg[0] = tx_clamp_cfg[0] | (0x0F << 1);
466 self.brd_write_registers(Register::TxClampCfg, &tx_clamp_cfg).await?;
467
468 self.sub_set_pa_config(0x04, 0x07, 0x00, 0x01).await?;
469
470 if power > 22 {
471 power = 22;
472 } else if power < -9 {
473 power = -9;
474 }
475 }
476
477 // power conversion of negative number from i8 to u8 ???
478 self.brd_write_command(OpCode::SetTxParams, &[power as u8, ramp_time.value()])
479 .await?;
480 Ok(())
481 }
482
483 // Set the modulation parameters
484 pub(super) async fn sub_set_modulation_params(&mut self) -> Result<(), RadioError<BUS>> {
485 if self.modulation_params.is_some() {
486 let mut buffer = [0x00u8; 4];
487
488 // Since this driver only supports LoRa, ensure the packet type is set accordingly
489 self.sub_set_packet_type(PacketType::LoRa).await?;
490
491 let modulation_params = self.modulation_params.unwrap();
492 buffer[0] = modulation_params.spreading_factor.value();
493 buffer[1] = modulation_params.bandwidth.value();
494 buffer[2] = modulation_params.coding_rate.value();
495 buffer[3] = modulation_params.low_data_rate_optimize;
496
497 self.brd_write_command(OpCode::SetModulationParams, &buffer).await?;
498 Ok(())
499 } else {
500 Err(RadioError::ModulationParamsMissing)
501 }
502 }
503
504 // Set the packet parameters
505 pub(super) async fn sub_set_packet_params(&mut self) -> Result<(), RadioError<BUS>> {
506 if self.packet_params.is_some() {
507 let mut buffer = [0x00u8; 6];
508
509 // Since this driver only supports LoRa, ensure the packet type is set accordingly
510 self.sub_set_packet_type(PacketType::LoRa).await?;
511
512 let packet_params = self.packet_params.unwrap();
513 buffer[0] = ((packet_params.preamble_length >> 8) & 0xFF) as u8;
514 buffer[1] = (packet_params.preamble_length & 0xFF) as u8;
515 buffer[2] = packet_params.implicit_header as u8;
516 buffer[3] = packet_params.payload_length;
517 buffer[4] = packet_params.crc_on as u8;
518 buffer[5] = packet_params.iq_inverted as u8;
519
520 self.brd_write_command(OpCode::SetPacketParams, &buffer).await?;
521 Ok(())
522 } else {
523 Err(RadioError::PacketParamsMissing)
524 }
525 }
526
527 // Set the channel activity detection (CAD) parameters
528 // symbols number of symbols to use for CAD operations
529 // det_peak limit for detection of SNR peak used in the CAD
530 // det_min minimum symbol recognition for CAD
531 // exit_mode operation to be done at the end of CAD action
532 // timeout timeout value to abort the CAD activity
533
534 pub(super) async fn sub_set_cad_params(
535 &mut self,
536 symbols: CADSymbols,
537 det_peak: u8,
538 det_min: u8,
539 exit_mode: CADExitMode,
540 timeout: u32,
541 ) -> Result<(), RadioError<BUS>> {
542 let mut buffer = [0x00u8; 7];
543
544 buffer[0] = symbols.value();
545 buffer[1] = det_peak;
546 buffer[2] = det_min;
547 buffer[3] = exit_mode.value();
548 buffer[4] = Self::timeout_1(timeout);
549 buffer[5] = Self::timeout_2(timeout);
550 buffer[6] = Self::timeout_3(timeout);
551
552 self.brd_write_command(OpCode::SetCADParams, &buffer).await?;
553 self.brd_set_operating_mode(RadioMode::ChannelActivityDetection);
554 Ok(())
555 }
556
557 // Set the data buffer base address for transmission and reception
558 pub(super) async fn sub_set_buffer_base_address(
559 &mut self,
560 tx_base_address: u8,
561 rx_base_address: u8,
562 ) -> Result<(), RadioError<BUS>> {
563 self.brd_write_command(OpCode::SetBufferBaseAddress, &[tx_base_address, rx_base_address])
564 .await?;
565 Ok(())
566 }
567
568 // Get the current radio status
569 pub(super) async fn sub_get_status(&mut self) -> Result<RadioStatus, RadioError<BUS>> {
570 let status = self.brd_read_command(OpCode::GetStatus, &mut []).await?;
571 Ok(RadioStatus {
572 cmd_status: (status & (0x07 << 1)) >> 1,
573 chip_mode: (status & (0x07 << 4)) >> 4,
574 })
575 }
576
577 // Get the instantaneous RSSI value for the last packet received
578 pub(super) async fn sub_get_rssi_inst(&mut self) -> Result<i8, RadioError<BUS>> {
579 let mut buffer = [0x00u8];
580 self.brd_read_command(OpCode::GetRSSIInst, &mut buffer).await?;
581 let rssi: i8 = (-(buffer[0] as i8)) >> 1; // check this ???
582 Ok(rssi)
583 }
584
585 // Get the last received packet buffer status
586 pub(super) async fn sub_get_rx_buffer_status(&mut self) -> Result<(u8, u8), RadioError<BUS>> {
587 if self.packet_params.is_some() {
588 let mut status = [0x00u8; 2];
589 let mut payload_length_buffer = [0x00u8];
590
591 self.brd_read_command(OpCode::GetRxBufferStatus, &mut status).await?;
592 if (self.sub_get_packet_type() == PacketType::LoRa) && self.packet_params.unwrap().implicit_header {
593 self.brd_read_registers(Register::PayloadLength, &mut payload_length_buffer)
594 .await?;
595 } else {
596 payload_length_buffer[0] = status[0];
597 }
598
599 let payload_length = payload_length_buffer[0];
600 let offset = status[1];
601
602 Ok((payload_length, offset))
603 } else {
604 Err(RadioError::PacketParamsMissing)
605 }
606 }
607
608 // Get the last received packet payload status
609 pub(super) async fn sub_get_packet_status(&mut self) -> Result<PacketStatus, RadioError<BUS>> {
610 let mut status = [0x00u8; 3];
611 self.brd_read_command(OpCode::GetPacketStatus, &mut status).await?;
612
613 // check this ???
614 let rssi = (-(status[0] as i8)) >> 1;
615 let snr = ((status[1] as i8) + 2) >> 2;
616 let signal_rssi = (-(status[2] as i8)) >> 1;
617 let freq_error = self.frequency_error;
618
619 Ok(PacketStatus {
620 rssi,
621 snr,
622 signal_rssi,
623 freq_error,
624 })
625 }
626
627 // Get the possible system errors
628 pub(super) async fn sub_get_device_errors(&mut self) -> Result<RadioSystemError, RadioError<BUS>> {
629 let mut errors = [0x00u8; 2];
630 self.brd_read_command(OpCode::GetErrors, &mut errors).await?;
631
632 Ok(RadioSystemError {
633 rc_64khz_calibration: (errors[1] & (1 << 0)) != 0,
634 rc_13mhz_calibration: (errors[1] & (1 << 1)) != 0,
635 pll_calibration: (errors[1] & (1 << 2)) != 0,
636 adc_calibration: (errors[1] & (1 << 3)) != 0,
637 image_calibration: (errors[1] & (1 << 4)) != 0,
638 xosc_start: (errors[1] & (1 << 5)) != 0,
639 pll_lock: (errors[1] & (1 << 6)) != 0,
640 pa_ramp: (errors[0] & (1 << 0)) != 0,
641 })
642 }
643
644 // Clear all the errors in the device
645 pub(super) async fn sub_clear_device_errors(&mut self) -> Result<(), RadioError<BUS>> {
646 self.brd_write_command(OpCode::ClrErrors, &[0x00u8, 0x00u8]).await?;
647 Ok(())
648 }
649
650 // Clear the IRQs
651 pub(super) async fn sub_clear_irq_status(&mut self, irq: u16) -> Result<(), RadioError<BUS>> {
652 let mut buffer = [0x00u8, 0x00u8];
653 buffer[0] = ((irq >> 8) & 0xFF) as u8;
654 buffer[1] = (irq & 0xFF) as u8;
655 self.brd_write_command(OpCode::ClrIrqStatus, &buffer).await?;
656 Ok(())
657 }
658
659 // Utility functions
660
661 fn timeout_1(timeout: u32) -> u8 {
662 ((timeout >> 16) & 0xFF) as u8
663 }
664 fn timeout_2(timeout: u32) -> u8 {
665 ((timeout >> 8) & 0xFF) as u8
666 }
667 fn timeout_3(timeout: u32) -> u8 {
668 (timeout & 0xFF) as u8
669 }
670
671 // check this ???
672 fn convert_u8_buffer_to_u32(buffer: &[u8; 4]) -> u32 {
673 let b0 = buffer[0] as u32;
674 let b1 = buffer[1] as u32;
675 let b2 = buffer[2] as u32;
676 let b3 = buffer[3] as u32;
677 (b0 << 24) | (b1 << 16) | (b2 << 8) | b3
678 }
679
680 fn convert_freq_in_hz_to_pll_step(freq_in_hz: u32) -> u32 {
681 // Get integer and fractional parts of the frequency computed with a PLL step scaled value
682 let steps_int = freq_in_hz / SX126X_PLL_STEP_SCALED;
683 let steps_frac = freq_in_hz - (steps_int * SX126X_PLL_STEP_SCALED);
684
685 (steps_int << SX126X_PLL_STEP_SHIFT_AMOUNT)
686 + (((steps_frac << SX126X_PLL_STEP_SHIFT_AMOUNT) + (SX126X_PLL_STEP_SCALED >> 1)) / SX126X_PLL_STEP_SCALED)
687 }
688}
diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml
index a5d340c69..54df16ba0 100644
--- a/examples/nrf/Cargo.toml
+++ b/examples/nrf/Cargo.toml
@@ -5,7 +5,8 @@ version = "0.1.0"
5 5
6[features] 6[features]
7default = ["nightly"] 7default = ["nightly"]
8nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embedded-io/async", "embassy-net"] 8nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embedded-io/async", "embassy-net",
9 "embassy-lora", "lorawan-device", "lorawan"]
9 10
10[dependencies] 11[dependencies]
11embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 12embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
@@ -16,6 +17,10 @@ embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defm
16embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"], optional = true } 17embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"], optional = true }
17embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true } 18embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true }
18embedded-io = "0.3.0" 19embedded-io = "0.3.0"
20embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["rak4631", "time", "defmt"], optional = true }
21
22lorawan-device = { version = "0.8.0", default-features = false, features = ["async"], optional = true }
23lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"], optional = true }
19 24
20defmt = "0.3" 25defmt = "0.3"
21defmt-rtt = "0.3" 26defmt-rtt = "0.3"
diff --git a/examples/nrf/src/bin/lora_p2p_report.rs b/examples/nrf/src/bin/lora_p2p_report.rs
new file mode 100644
index 000000000..46cb848b1
--- /dev/null
+++ b/examples/nrf/src/bin/lora_p2p_report.rs
@@ -0,0 +1,84 @@
1//! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio.
2#![no_std]
3#![no_main]
4#![macro_use]
5#![allow(dead_code)]
6#![feature(type_alias_impl_trait)]
7
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_lora::sx126x::*;
11use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull};
12use embassy_nrf::{interrupt, spim};
13use embassy_time::{Duration, Timer};
14use lorawan_device::async_device::radio::{Bandwidth, CodingRate, PhyRxTx, RfConfig, SpreadingFactor};
15use {defmt_rtt as _, panic_probe as _};
16
17#[embassy_executor::main]
18async fn main(_spawner: Spawner) {
19 let p = embassy_nrf::init(Default::default());
20 let mut spi_config = spim::Config::default();
21 spi_config.frequency = spim::Frequency::M1; // M16 ???
22
23 let mut radio = {
24 let irq = interrupt::take!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
25 let spim = spim::Spim::new(p.TWISPI1, irq, p.P1_11, p.P1_13, p.P1_12, spi_config);
26
27 let cs = Output::new(p.P1_10, Level::High, OutputDrive::Standard);
28 let reset = Output::new(p.P1_06, Level::High, OutputDrive::Standard);
29 let dio1 = Input::new(p.P1_15.degrade(), Pull::Down);
30 let busy = Input::new(p.P1_14.degrade(), Pull::Down);
31 let antenna_rx = Output::new(p.P1_05, Level::Low, OutputDrive::Standard);
32 let antenna_tx = Output::new(p.P1_07, Level::Low, OutputDrive::Standard);
33
34 match Sx126xRadio::new(spim, cs, reset, antenna_rx, antenna_tx, dio1, busy, false).await {
35 Ok(r) => r,
36 Err(err) => {
37 info!("Sx126xRadio error = {}", err);
38 return;
39 }
40 }
41 };
42
43 let mut debug_indicator = Output::new(p.P1_03, Level::Low, OutputDrive::Standard);
44 let mut start_indicator = Output::new(p.P1_04, Level::Low, OutputDrive::Standard);
45
46 start_indicator.set_high();
47 Timer::after(Duration::from_secs(5)).await;
48 start_indicator.set_low();
49
50 match radio.lora.sleep().await {
51 Ok(()) => info!("Sleep successful"),
52 Err(err) => info!("Sleep unsuccessful = {}", err),
53 }
54
55 let rf_config = RfConfig {
56 frequency: 903900000, // channel in Hz
57 bandwidth: Bandwidth::_250KHz,
58 spreading_factor: SpreadingFactor::_10,
59 coding_rate: CodingRate::_4_8,
60 };
61
62 let mut buffer = [00u8; 100];
63
64 // P2P receive
65 match radio.rx(rf_config, &mut buffer).await {
66 Ok((buffer_len, rx_quality)) => info!(
67 "RX received = {:?} with length = {} rssi = {} snr = {}",
68 &buffer[0..buffer_len],
69 buffer_len,
70 rx_quality.rssi(),
71 rx_quality.snr()
72 ),
73 Err(err) => info!("RX error = {}", err),
74 }
75
76 match radio.lora.sleep().await {
77 Ok(()) => info!("Sleep successful"),
78 Err(err) => info!("Sleep unsuccessful = {}", err),
79 }
80
81 debug_indicator.set_high();
82 Timer::after(Duration::from_secs(5)).await;
83 debug_indicator.set_low();
84}
diff --git a/examples/nrf/src/bin/lora_p2p_sense.rs b/examples/nrf/src/bin/lora_p2p_sense.rs
new file mode 100644
index 000000000..57aaea665
--- /dev/null
+++ b/examples/nrf/src/bin/lora_p2p_sense.rs
@@ -0,0 +1,173 @@
1//! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio.
2#![no_std]
3#![no_main]
4#![macro_use]
5#![feature(type_alias_impl_trait)]
6#![feature(alloc_error_handler)]
7#![allow(incomplete_features)]
8
9use defmt::*;
10use embassy_executor::Spawner;
11use embassy_lora::sx126x::*;
12use embassy_nrf::gpio::{AnyPin, Input, Level, Output, OutputDrive, Pin as _, Pull};
13use embassy_nrf::temp::Temp;
14use embassy_nrf::{interrupt, spim};
15use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
16use embassy_sync::pubsub::{PubSubChannel, Publisher};
17use embassy_time::{Duration, Timer};
18use lorawan_device::async_device::radio::{Bandwidth, CodingRate, PhyRxTx, RfConfig, SpreadingFactor, TxConfig};
19use {defmt_rtt as _, panic_probe as _, panic_probe as _};
20
21// Sensor packet constants
22const TEMPERATURE_UID: u8 = 0x01;
23const MOTION_UID: u8 = 0x02;
24
25// Message bus: queue of 2, 1 subscriber (Lora P2P), 2 publishers (temperature, motion detection)
26static MESSAGE_BUS: PubSubChannel<CriticalSectionRawMutex, Message, 2, 1, 2> = PubSubChannel::new();
27
28#[derive(Clone, defmt::Format)]
29enum Message {
30 Temperature(i32),
31 MotionDetected,
32}
33
34#[embassy_executor::task]
35async fn temperature_task(
36 mut temperature: Temp<'static>,
37 publisher: Publisher<'static, CriticalSectionRawMutex, Message, 2, 1, 2>,
38) {
39 Timer::after(Duration::from_secs(45)).await; // stabilize for 45 seconds
40
41 let mut temperature_reporting_threshhold = 10;
42
43 loop {
44 let value = temperature.read().await;
45 let mut temperature_val = value.to_num::<i32>();
46
47 info!("Temperature: {}", temperature_val); // debug ???
48
49 // only report every 2 degree Celsius drops, from 9 through 5, but starting at 3 always report
50
51 if temperature_val == 8 || temperature_val == 6 || temperature_val == 4 {
52 temperature_val += 1;
53 }
54
55 if temperature_reporting_threshhold > temperature_val
56 && (temperature_val == 9 || temperature_val == 7 || temperature_val == 5)
57 {
58 temperature_reporting_threshhold = temperature_val;
59 publisher.publish(Message::Temperature(temperature_val)).await;
60 } else if temperature_val <= 3 {
61 publisher.publish(Message::Temperature(temperature_val)).await;
62 }
63
64 Timer::after(Duration::from_secs(20 * 60)).await;
65 }
66}
67
68#[embassy_executor::task]
69async fn motion_detection_task(
70 mut pir_pin: Input<'static, AnyPin>,
71 publisher: Publisher<'static, CriticalSectionRawMutex, Message, 2, 1, 2>,
72) {
73 Timer::after(Duration::from_secs(30)).await; // stabilize for 30 seconds
74
75 loop {
76 // wait for motion detection
77 pir_pin.wait_for_low().await;
78 publisher.publish(Message::MotionDetected).await;
79
80 // wait a minute before setting up for more motion detection
81 Timer::after(Duration::from_secs(60)).await;
82 }
83}
84
85#[embassy_executor::main]
86async fn main(spawner: Spawner) {
87 let p = embassy_nrf::init(Default::default());
88 // set up to funnel temperature and motion detection events to the Lora Tx task
89 let mut lora_tx_subscriber = unwrap!(MESSAGE_BUS.subscriber());
90 let temperature_publisher = unwrap!(MESSAGE_BUS.publisher());
91 let motion_detection_publisher = unwrap!(MESSAGE_BUS.publisher());
92
93 let mut spi_config = spim::Config::default();
94 spi_config.frequency = spim::Frequency::M1; // M16 ???
95
96 let mut radio = {
97 let irq = interrupt::take!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
98 let spim = spim::Spim::new(p.TWISPI1, irq, p.P1_11, p.P1_13, p.P1_12, spi_config);
99
100 let cs = Output::new(p.P1_10, Level::High, OutputDrive::Standard);
101 let reset = Output::new(p.P1_06, Level::High, OutputDrive::Standard);
102 let dio1 = Input::new(p.P1_15.degrade(), Pull::Down);
103 let busy = Input::new(p.P1_14.degrade(), Pull::Down);
104 let antenna_rx = Output::new(p.P1_05, Level::Low, OutputDrive::Standard);
105 let antenna_tx = Output::new(p.P1_07, Level::Low, OutputDrive::Standard);
106
107 match Sx126xRadio::new(spim, cs, reset, antenna_rx, antenna_tx, dio1, busy, false).await {
108 Ok(r) => r,
109 Err(err) => {
110 info!("Sx126xRadio error = {}", err);
111 return;
112 }
113 }
114 };
115
116 // set up for the temperature task
117 let temperature_irq = interrupt::take!(TEMP);
118 let temperature = Temp::new(p.TEMP, temperature_irq);
119
120 // set the motion detection pin
121 let pir_pin = Input::new(p.P0_10.degrade(), Pull::Up);
122
123 let mut start_indicator = Output::new(p.P1_04, Level::Low, OutputDrive::Standard);
124 let mut debug_indicator = Output::new(p.P1_03, Level::Low, OutputDrive::Standard);
125
126 start_indicator.set_high();
127 Timer::after(Duration::from_secs(5)).await;
128 start_indicator.set_low();
129
130 match radio.lora.sleep().await {
131 Ok(()) => info!("Sleep successful"),
132 Err(err) => info!("Sleep unsuccessful = {}", err),
133 }
134
135 unwrap!(spawner.spawn(temperature_task(temperature, temperature_publisher)));
136 unwrap!(spawner.spawn(motion_detection_task(pir_pin, motion_detection_publisher)));
137
138 loop {
139 let message = lora_tx_subscriber.next_message_pure().await;
140
141 let tx_config = TxConfig {
142 // 11 byte maximum payload for Bandwidth 125 and SF 10
143 pw: 20, // up to 20 // 5 ???
144 rf: RfConfig {
145 frequency: 903900000, // channel in Hz, not MHz
146 bandwidth: Bandwidth::_250KHz,
147 spreading_factor: SpreadingFactor::_10,
148 coding_rate: CodingRate::_4_8,
149 },
150 };
151
152 let mut buffer = [TEMPERATURE_UID, 0xffu8, MOTION_UID, 0x00u8];
153 match message {
154 Message::Temperature(temperature) => buffer[1] = temperature as u8,
155 Message::MotionDetected => buffer[3] = 0x01u8,
156 };
157
158 // crypto for text ???
159 match radio.tx(tx_config, &buffer).await {
160 Ok(ret_val) => info!("TX ret_val = {}", ret_val),
161 Err(err) => info!("TX error = {}", err),
162 }
163
164 match radio.lora.sleep().await {
165 Ok(()) => info!("Sleep successful"),
166 Err(err) => info!("Sleep unsuccessful = {}", err),
167 }
168
169 debug_indicator.set_high();
170 Timer::after(Duration::from_secs(5)).await;
171 debug_indicator.set_low();
172 }
173}