rf24/radio/config.rs
1use crate::radio::rf24::bit_fields::{Config, Feature, SetupRetry, SetupRfAw};
2use crate::{CrcLength, DataRate, PaLevel};
3
4/// A struct to contain configuration about pipe addresses.
5#[derive(Debug, Clone, Copy)]
6pub struct EsbPipeConfig {
7 pub(super) tx_address: [u8; 5],
8 pipe0: [u8; 5],
9 pipe1: [u8; 5],
10 pipe2: u8,
11 pipe3: u8,
12 pipe4: u8,
13 pipe5: u8,
14 pipe6: u8,
15 pipe7: u8,
16 pub(super) rx_pipes_enabled: u8,
17}
18
19impl Default for EsbPipeConfig {
20 fn default() -> Self {
21 Self {
22 tx_address: [0xE7; 5],
23 pipe0: [0xE7; 5],
24 pipe1: [0xC2; 5],
25 pipe2: 0xC3,
26 pipe3: 0xC4,
27 pipe4: 0xC5,
28 pipe5: 0xC6,
29 pipe6: 0xC7,
30 pipe7: 0xC8,
31 rx_pipes_enabled: 2,
32 }
33 }
34}
35
36impl EsbPipeConfig {
37 pub fn set_tx_address(&mut self, address: &[u8]) {
38 let len = address.len().min(5);
39 self.tx_address[..len].copy_from_slice(&address[..len]);
40 }
41
42 pub fn set_rx_address(&mut self, pipe: u8, address: &[u8]) {
43 let len = address.len().min(5);
44 if len == 0 {
45 return;
46 }
47 if pipe < 8 {
48 self.rx_pipes_enabled |= 1 << pipe;
49 }
50 match pipe {
51 0 => self.pipe0[..len].copy_from_slice(&address[..len]),
52 1 => self.pipe1[..len].copy_from_slice(&address[..len]),
53 2 => self.pipe2 = address[0],
54 3 => self.pipe3 = address[0],
55 4 => self.pipe4 = address[0],
56 5 => self.pipe5 = address[0],
57 6 => self.pipe6 = address[0],
58 7 => self.pipe7 = address[0],
59 _ => (),
60 }
61 }
62
63 pub fn close_rx_pipe(&mut self, pipe: u8) {
64 if pipe < 8 {
65 self.rx_pipes_enabled &= !(1 << pipe);
66 }
67 }
68
69 pub(super) fn get_rx_address(&self, pipe: u8, address: &mut [u8]) {
70 let len = address.len().min(5);
71 match pipe {
72 0 => address[..len].copy_from_slice(&self.pipe0[..len]),
73 1 => address[..len].copy_from_slice(&self.pipe1[..len]),
74 2 => address[0] = self.pipe2,
75 3 => address[0] = self.pipe3,
76 4 => address[0] = self.pipe4,
77 5 => address[0] = self.pipe5,
78 6 => address[0] = self.pipe6,
79 7 => address[0] = self.pipe7,
80 _ => (),
81 }
82 if pipe > 1 && len > 1 {
83 address[1..(len - 1)].copy_from_slice(&self.pipe1[1..(len - 1)]);
84 }
85 }
86}
87
88/// An object to configure the radio.
89///
90/// This struct follows a builder pattern. Since all fields are private, users should
91/// start with the [`EsbConfig::default`] constructor, then mutate the object accordingly.
92/// ```
93/// let mut config = Config::default();
94/// config = config.with_channel(42);
95/// ```
96#[derive(Debug, Clone, Copy)]
97pub struct EsbConfig {
98 pub(crate) config_reg: Config,
99 pub(crate) auto_retries: SetupRetry,
100 pub(crate) setup_rf_aw: SetupRfAw,
101 pub(crate) feature: Feature,
102 channel: u8,
103 payload_length: u8,
104 auto_ack: u8,
105 pipes: EsbPipeConfig,
106}
107
108impl Default for EsbConfig {
109 /// Instantiate a [`EsbConfig`] object with library defaults.
110 ///
111 /// | feature | default value |
112 /// |--------:|:--------------|
113 /// | [`EsbConfig::channel()`] | `76` |
114 /// | [`EsbConfig::address_length()`] | `5` |
115 /// | [`EsbConfig::pa_level()`] | [`PaLevel::Max`] |
116 /// | [`EsbConfig::lna_enable()`] | `true` |
117 /// | [`EsbConfig::crc_length()`] | [`CrcLength::Bit16`] |
118 /// | [`EsbConfig::data_rate()`] | [`DataRate::Mbps1`] |
119 /// | [`EsbConfig::payload_length()`] | `32` |
120 /// | [`EsbConfig::dynamic_payloads()`] | `false` |
121 /// | [`EsbConfig::auto_ack()`] | `0x3F` (enabled for pipes 0 - 5) |
122 /// | [`EsbConfig::ack_payloads()`] | `false` |
123 /// | [`EsbConfig::ask_no_ack()`] | `false` |
124 /// | [`EsbConfig::auto_retry_delay()`] | `5` |
125 /// | [`EsbConfig::auto_retry_count()`] | `15` |
126 /// | [`EsbConfig::tx_address()`] | `[0xE7; 5]` |
127 /// | [`EsbConfig::rx_address()`] | See below table about [Default RX addresses](#default-rx-pipes-configuration) |
128 /// | [`EsbConfig::rx_dr()`] | `true` |
129 /// | [`EsbConfig::tx_ds()`] | `true` |
130 /// | [`EsbConfig::tx_df()`] | `true` |
131 ///
132 /// ## Default RX pipes' configuration
133 ///
134 /// | pipe number | state | address |
135 /// |-------------|--------|-------------|
136 /// | 0[^2] | closed | `[0xE7; 5]` |
137 /// | 1 | open | `[0xC2; 5]` |
138 /// | 2[^1] | closed | `0xC3` |
139 /// | 3[^1] | closed | `0xC4` |
140 /// | 4[^1] | closed | `0xC5` |
141 /// | 5[^1] | closed | `0xC6` |
142 ///
143 /// [^1]: Remember, pipes 2 - 5 share the same 4 LSBytes as the address on pipe 1.
144 /// [^2]: The RX address default value is the same as pipe 0 default RX address.
145 fn default() -> Self {
146 Self {
147 /*
148 - all events enabled for IRQ pin
149 - 8 bit CRC
150 - powered down
151 - inactive TX (StandBy-I) mode
152 */
153 config_reg: Config::default(),
154 /*
155 - 5 * 250 + 250 = 1500 us delay between attempts
156 - 15 max attempts
157 */
158 auto_retries: SetupRetry::default(),
159 /*
160 - 5 byte address length
161 - 1 Mbps data rate
162 - Max PA level
163 - LNA enabled
164 */
165 setup_rf_aw: SetupRfAw::default(),
166 /*
167 - disabled dynamic payloads
168 - disabled ACK payloads
169 - disabled ask_no_ack param
170 */
171 feature: Feature::default(),
172 channel: 76,
173 payload_length: 32,
174 // enable auto-ACK for pipes 0 - 5
175 auto_ack: 0x3F,
176 pipes: EsbPipeConfig::default(),
177 }
178 }
179}
180
181impl EsbConfig {
182 /// Returns the value set by [`EsbConfig::with_crc_length()`].
183 pub const fn crc_length(&self) -> CrcLength {
184 self.config_reg.crc_length()
185 }
186
187 /// The Cyclical Redundancy Checksum (CRC) length.
188 ///
189 /// See [`EsbCrcLength::set_crc_length()`](fn@crate::radio::prelude::EsbCrcLength::set_crc_length).
190 pub fn with_crc_length(self, length: CrcLength) -> Self {
191 let new_config = self.config_reg.with_crc_length(length);
192 Self {
193 config_reg: new_config,
194 ..self
195 }
196 }
197
198 /// Returns the value set by [`EsbConfig::with_data_rate()`].
199 pub const fn data_rate(&self) -> DataRate {
200 self.setup_rf_aw.data_rate()
201 }
202
203 /// The Data Rate (over the air).
204 ///
205 /// See [`EsbDataRate::set_data_rate()`](fn@crate::radio::prelude::EsbDataRate::set_data_rate).
206 pub fn with_data_rate(self, data_rate: DataRate) -> Self {
207 let new_config = self.setup_rf_aw.with_data_rate(data_rate);
208 Self {
209 setup_rf_aw: new_config,
210 ..self
211 }
212 }
213
214 /// Returns the value set by [`EsbConfig::with_pa_level()`].
215 pub const fn pa_level(&self) -> PaLevel {
216 self.setup_rf_aw.pa_level()
217 }
218
219 /// The Power Amplitude (PA) level.
220 ///
221 /// See [`EsbPaLevel::set_pa_level()`](fn@crate::radio::prelude::EsbPaLevel::set_pa_level).
222 pub fn with_pa_level(self, level: PaLevel) -> Self {
223 let new_config = self.setup_rf_aw.with_pa_level(level);
224 Self {
225 setup_rf_aw: new_config,
226 ..self
227 }
228 }
229
230 /// Returns the value set by [`EsbConfig::with_lna_enable()`].
231 pub const fn lna_enable(&self) -> bool {
232 self.setup_rf_aw.lna_enable()
233 }
234
235 /// Enable or disable the chip's Low Noise Amplifier (LNA) feature.
236 ///
237 /// This value may not be respected depending on the radio module used.
238 /// Consult the radio's manufacturer for accurate details.
239 pub fn with_lna_enable(self, enable: bool) -> Self {
240 let new_config = self.setup_rf_aw.with_lna_enable(enable);
241 Self {
242 setup_rf_aw: new_config,
243 ..self
244 }
245 }
246
247 /// Returns the value set by [`EsbConfig::with_address_length()`].
248 pub const fn address_length(&self) -> u8 {
249 self.setup_rf_aw.address_length()
250 }
251
252 /// The address length.
253 ///
254 /// This value is clamped to range [2, 5].
255 pub fn with_address_length(self, value: u8) -> Self {
256 let new_config = self.setup_rf_aw.with_address_length(value);
257 Self {
258 setup_rf_aw: new_config,
259 ..self
260 }
261 }
262
263 /// Returns the value set by [`EsbConfig::with_channel()`].
264 pub const fn channel(&self) -> u8 {
265 self.channel
266 }
267
268 /// Set the channel (over the air frequency).
269 ///
270 /// This value is clamped to range [0, 125].
271 /// The radio's frequency can be determined by the following equation:
272 /// ```text
273 /// frequency (in Hz) = channel + 2400
274 /// ```
275 pub fn with_channel(self, value: u8) -> Self {
276 Self {
277 channel: value.min(125),
278 ..self
279 }
280 }
281
282 /// The auto-retry feature's `delay` (set via [`EsbConfig::with_auto_retries()`])
283 pub const fn auto_retry_delay(&self) -> u8 {
284 self.auto_retries.ard()
285 }
286
287 /// The auto-retry feature's `count` (set via [`EsbConfig::with_auto_retries()`])
288 pub const fn auto_retry_count(&self) -> u8 {
289 self.auto_retries.arc()
290 }
291
292 /// Set the auto-retry feature's `delay` and `count` parameters.
293 ///
294 /// See [`EsbAutoAck::set_auto_retries()`](fn@crate::radio::prelude::EsbAutoAck::set_auto_retries).
295 pub fn with_auto_retries(self, delay: u8, count: u8) -> Self {
296 let new_config = self
297 .auto_retries
298 .with_ard(delay.min(15))
299 .with_arc(count.min(15));
300 Self {
301 auto_retries: new_config,
302 ..self
303 }
304 }
305
306 /// Get the value set by [`EsbConfig::rx_dr()`].
307 pub const fn rx_dr(&self) -> bool {
308 self.config_reg.rx_dr()
309 }
310
311 /// Enable or disable the "RX Data Ready" event triggering the radio's IRQ.
312 ///
313 /// See [`StatusFlags::rx_dr()`](fn@crate::StatusFlags::rx_dr).
314 pub fn with_rx_dr(self, enable: bool) -> Self {
315 let new_config = self.config_reg.with_rx_dr(enable);
316 Self {
317 config_reg: new_config,
318 ..self
319 }
320 }
321
322 /// Get the value set by [`EsbConfig::tx_ds()`].
323 pub const fn tx_ds(&self) -> bool {
324 self.config_reg.tx_ds()
325 }
326
327 /// Enable or disable the "TX Data Sent" event triggering the radio's IRQ.
328 ///
329 /// See [`StatusFlags::tx_ds()`](fn@crate::StatusFlags::tx_ds).
330 pub fn with_tx_ds(self, enable: bool) -> Self {
331 let new_config = self.config_reg.with_tx_ds(enable);
332 Self {
333 config_reg: new_config,
334 ..self
335 }
336 }
337
338 /// Get the value set by [`EsbConfig::tx_df()`].
339 pub const fn tx_df(&self) -> bool {
340 self.config_reg.tx_df()
341 }
342
343 /// Enable or disable the "TX Data Failed" event triggering the radio's IRQ.
344 ///
345 /// See [`StatusFlags::tx_df()`](fn@crate::StatusFlags::tx_df).
346 pub fn with_tx_df(self, enable: bool) -> Self {
347 let new_config = self.config_reg.with_tx_df(enable);
348 Self {
349 config_reg: new_config,
350 ..self
351 }
352 }
353
354 /// Return the value set by [`EsbConfig::with_ask_no_ack()`].
355 pub const fn ask_no_ack(&self) -> bool {
356 self.feature.ask_no_ack()
357 }
358
359 /// Allow disabling auto-ack per payload.
360 ///
361 /// See `ask_no_ack` parameter for
362 /// [`EsbRadio::send()`](fn@crate::radio::prelude::EsbRadio::send) and
363 /// [`EsbRadio::write()`](fn@crate::radio::prelude::EsbRadio::write).
364 pub fn with_ask_no_ack(self, enable: bool) -> Self {
365 let new_config = self.feature.with_ask_no_ack(enable);
366 Self {
367 feature: new_config,
368 ..self
369 }
370 }
371
372 /// Return the value set by [`EsbConfig::with_dynamic_payloads()`].
373 ///
374 /// This feature is enabled automatically when enabling ACK payloads
375 /// via [`EsbConfig::with_ack_payloads()`].
376 pub const fn dynamic_payloads(&self) -> bool {
377 self.feature.dynamic_payloads()
378 }
379
380 /// Enable or disable dynamically sized payloads.
381 ///
382 /// Enabling this feature nullifies the utility of [`EsbConfig::payload_length()`].
383 pub fn with_dynamic_payloads(self, enable: bool) -> Self {
384 let new_config = self.feature.with_dynamic_payloads(enable);
385 Self {
386 feature: new_config,
387 ..self
388 }
389 }
390
391 /// Return the value set by [`EsbConfig::with_auto_ack()`].
392 pub const fn auto_ack(&self) -> u8 {
393 self.auto_ack
394 }
395
396 /// Enable or disable auto-ACK feature.
397 ///
398 /// The given value (in binary form) is used to control the auto-ack feature for each pipe.
399 /// Bit 0 controls the feature for pipe 0. Bit 1 controls the feature for pipe 1. And so on.
400 ///
401 /// To enable the feature for pipes 0, 1 and 4:
402 /// ```
403 /// let config = EsbConfig::default().with_auto_ack(0b010011);
404 /// ```
405 /// If enabling the feature for any pipe other than 0, then the pipe 0 should also have the
406 /// feature enabled because pipe 0 is used to transmit automatic ACK packets in RX mode.
407 pub fn with_auto_ack(self, enable: u8) -> Self {
408 Self {
409 auto_ack: enable,
410 ..self
411 }
412 }
413
414 /// Return the value set by [`EsbConfig::with_ack_payloads()`].
415 pub const fn ack_payloads(&self) -> bool {
416 self.feature.ack_payloads()
417 }
418
419 /// Enable or disable custom ACK payloads for auto-ACK packets.
420 ///
421 /// ACK payloads require the [`EsbConfig::auto_ack`] and [`EsbConfig::dynamic_payloads`]
422 /// to be enabled. If ACK payloads are enabled, then this function also enables those
423 /// features (for all pipes).
424 pub fn with_ack_payloads(self, enable: bool) -> Self {
425 let auto_ack = if enable { 0xFF } else { self.auto_ack };
426 let new_config = self.feature.with_ack_payloads(enable);
427 Self {
428 auto_ack,
429 feature: new_config,
430 ..self
431 }
432 }
433
434 /// Return the value set by [`EsbConfig::with_payload_length()`].
435 ///
436 /// The hardware's maximum payload length is enforced by the hardware specific
437 /// implementations of [`EsbPayloadLength::set_payload_length()`](fn@crate::radio::prelude::EsbPayloadLength::set_payload_length).
438 pub const fn payload_length(&self) -> u8 {
439 self.payload_length
440 }
441
442 /// The payload length for statically sized payloads.
443 ///
444 /// See [`EsbPayloadLength::set_payload_length()`](fn@crate::radio::prelude::EsbPayloadLength::set_payload_length).
445 pub fn with_payload_length(self, value: u8) -> Self {
446 // NOTE: max payload length is enforced in hardware-specific implementations
447 Self {
448 payload_length: value,
449 ..self
450 }
451 }
452
453 // Close a RX pipe from receiving data.
454 //
455 // This is only useful if pipe 1 should be closed instead of open (after [`EsbConfig::default()`]).
456 pub fn close_rx_pipe(self, pipe: u8) -> Self {
457 let mut pipes = self.pipes;
458 pipes.close_rx_pipe(pipe);
459 Self { pipes, ..self }
460 }
461
462 /// Is a specified RX pipe open (`true`) or closed (`false`)?
463 ///
464 /// The value returned here is controlled by
465 /// [`EsbConfig::with_rx_address()`] (to open a pipe) and [`EsbConfig::close_rx_pipe()`].
466 pub fn is_rx_pipe_enabled(&self, pipe: u8) -> bool {
467 self.pipes.rx_pipes_enabled & (1u8 << pipe.min(8)) > 0
468 }
469
470 /// Get the address for a specified `pipe` set by [`EsbConfig::with_rx_address()`]
471 pub fn rx_address(&self, pipe: u8, address: &mut [u8]) {
472 self.pipes.get_rx_address(pipe, address);
473 }
474
475 /// Set the address of a specified RX `pipe` for receiving data.
476 ///
477 /// This does nothing if the given `pipe` is greater than `8`.
478 /// For pipes 2 - 5, the 4 LSBytes are used from address set to pipe 1 with the
479 /// MSByte from the given `address`.
480 ///
481 /// See also [`EsbConfig::with_tx_address()`].
482 pub fn with_rx_address(self, pipe: u8, address: &[u8]) -> Self {
483 let mut pipes = self.pipes;
484 pipes.set_rx_address(pipe, address);
485 Self { pipes, ..self }
486 }
487
488 /// Get the address set by [`EsbConfig::with_tx_address()`]
489 pub fn tx_address(&self, address: &mut [u8]) {
490 let len = address.len().min(5);
491 address[..len].copy_from_slice(&self.pipes.tx_address[..len]);
492 }
493
494 /// Set the TX address.
495 ///
496 /// Only pipe 0 can be used for TX operations (including auto-ACK packets during RX operations).
497 pub fn with_tx_address(self, address: &[u8]) -> Self {
498 let mut pipes = self.pipes;
499 pipes.set_tx_address(address);
500 Self { pipes, ..self }
501 }
502}
503
504#[cfg(test)]
505mod test {
506 use super::EsbConfig;
507 use crate::{CrcLength, DataRate, PaLevel};
508
509 #[test]
510 fn crc_length() {
511 let mut config = EsbConfig::default();
512 for len in [CrcLength::Disabled, CrcLength::Bit16, CrcLength::Bit8] {
513 config = config.with_crc_length(len);
514 assert_eq!(len, config.crc_length());
515 }
516 }
517
518 #[test]
519 fn config_irq_flags() {
520 let mut config = EsbConfig::default();
521 assert!(config.rx_dr());
522 assert!(config.tx_ds());
523 assert!(config.tx_df());
524 config = config.with_rx_dr(false).with_tx_ds(false).with_tx_df(false);
525 assert!(!config.rx_dr());
526 assert!(!config.tx_ds());
527 assert!(!config.tx_df());
528 }
529
530 #[test]
531 fn address_length() {
532 let mut config = EsbConfig::default();
533 for len in 0..10 {
534 config = config.with_address_length(len);
535 assert_eq!(config.address_length(), len.clamp(2, 5));
536 }
537 }
538
539 #[test]
540 fn pa_level() {
541 let mut config = EsbConfig::default();
542 for level in [PaLevel::Max, PaLevel::High, PaLevel::Low, PaLevel::Min] {
543 config = config.with_pa_level(level);
544 assert_eq!(config.pa_level(), level);
545 }
546 assert!(config.lna_enable());
547 config = config.with_lna_enable(false);
548 assert!(!config.lna_enable());
549 }
550
551 #[test]
552 fn data_rate() {
553 let mut config = EsbConfig::default();
554 for rate in [DataRate::Kbps250, DataRate::Mbps1, DataRate::Mbps2] {
555 config = config.with_data_rate(rate);
556 assert_eq!(config.data_rate(), rate);
557 }
558 }
559
560 #[test]
561 fn feature_register() {
562 let mut config = EsbConfig::default();
563 assert_eq!(config.auto_ack(), 0x3F);
564 assert!(!config.ack_payloads());
565 assert!(!config.dynamic_payloads());
566 assert!(!config.ask_no_ack());
567
568 config = config.with_ack_payloads(true);
569 assert_eq!(config.auto_ack(), 0xFF);
570 assert!(config.ack_payloads());
571 assert!(config.dynamic_payloads());
572 assert!(!config.ask_no_ack());
573
574 config = config.with_ask_no_ack(true).with_ack_payloads(false);
575 assert!(!config.ack_payloads());
576 assert!(config.dynamic_payloads());
577 assert!(config.ask_no_ack());
578
579 config = config.with_dynamic_payloads(false);
580 assert!(!config.dynamic_payloads());
581 assert!(!config.ack_payloads());
582 assert!(config.ask_no_ack());
583
584 config = config.with_auto_ack(3);
585 assert_eq!(config.auto_ack(), 3);
586 assert!(!config.dynamic_payloads());
587 }
588
589 #[test]
590 fn payload_length() {
591 let config = EsbConfig::default().with_payload_length(255);
592 assert_eq!(config.payload_length(), 255);
593 }
594
595 #[test]
596 fn channel() {
597 let config = EsbConfig::default().with_channel(255);
598 assert_eq!(config.channel(), 125);
599 }
600 #[test]
601 fn auto_retries() {
602 let mut config = EsbConfig::default();
603 assert_eq!(config.auto_retry_count(), 15);
604 assert_eq!(config.auto_retry_delay(), 5);
605 config = config.with_auto_retries(20, 3);
606 assert_eq!(config.auto_retry_count(), 3);
607 assert_eq!(config.auto_retry_delay(), 15);
608 }
609
610 #[test]
611 fn pipe_addresses() {
612 let mut config = EsbConfig::default();
613 let mut address = [0xB0; 5];
614 config = config.with_tx_address(&address);
615 let mut result = [0; 3];
616 config.tx_address(&mut result);
617 assert!(address.starts_with(&result));
618 config = config.close_rx_pipe(1).close_rx_pipe(10);
619 // just for coverage, pass a empty byte array as RX address
620 config = config.with_rx_address(0, &[]);
621 assert!(!config.is_rx_pipe_enabled(1));
622 for pipe in 0..=8 {
623 address.copy_from_slice(&[0xB0 + pipe; 5]);
624 config = config.with_rx_address(pipe, &address);
625 config.rx_address(pipe, &mut result);
626 if pipe < 2 {
627 assert!(address.starts_with(&result));
628 } else if pipe < 8 {
629 assert_eq!(address[0], result[0]);
630 // check base from pipe 1 is used for LSBs
631 assert!(result[1..].starts_with(&[0xB1, 0xB1]));
632 } else {
633 // pipe > 8 result in non-op mutations
634 assert_ne!(address[0], result[0]);
635 // check base from pipe 1 is still used for LSBs
636 assert!(result[1..].starts_with(&[0xB1, 0xB1]));
637 }
638
639 if pipe < 8 {
640 assert!(config.is_rx_pipe_enabled(pipe));
641 }
642 }
643 }
644}