Skip to content

Commit 8a5f2d0

Browse files
committed
refactor!: replace open_tx_pipe() with tx_address
related to nRF24/RF24#1028 but takes a different approach from nRF24/RF24#1029 (more like nRF24/RF24#1030)
1 parent 39d8287 commit 8a5f2d0

File tree

7 files changed

+142
-71
lines changed

7 files changed

+142
-71
lines changed

bindings/node/src/radio/interface.rs

+18-9
Original file line numberDiff line numberDiff line change
@@ -761,20 +761,29 @@ impl RF24 {
761761
.map_err(|e| Error::new(Status::GenericFailure, format!("{e:?}")))
762762
}
763763

764-
/// Set the address used for transmitting on pipe 0.
764+
/// The TX address used on pipe 0 for outgoing transmissions.
765765
///
766-
/// Only pipe 0 can be used for transmitting. It is highly recommended to
767-
/// avoid using pipe 0 to receive because of this.
766+
/// Set this before calling {@link RF24::asTx}.
768767
///
769-
/// @param address - The address to receive data from.
768+
/// This is cached on the {@link RF24} instance, so {@link RF24::asTx} can
769+
/// use it to ensure proper auto-ack behavior in TX mode,
770+
/// if pipe 0 is also used for RX with a different address.
770771
///
771772
/// @group Basic
772-
#[napi]
773-
pub fn open_tx_pipe(&mut self, address: Buffer) -> Result<()> {
773+
#[napi(setter, js_name = "txAddress")]
774+
pub fn set_tx_address(&mut self, address: Buffer) {
774775
let address = address.to_vec();
775-
self.inner
776-
.open_tx_pipe(&address)
777-
.map_err(|e| Error::new(Status::GenericFailure, format!("{e:?}")))
776+
let addr_len = address.len().min(5);
777+
self.inner.tx_address[0..addr_len].copy_from_slice(&address[0..addr_len])
778+
}
779+
780+
/// @group Basic
781+
#[napi(getter, js_name = "txAddress")]
782+
pub fn get_tx_address(&mut self) -> Result<Buffer> {
783+
let mut address = [0; 5];
784+
let addr_len = self.get_address_length()? as usize;
785+
address[0..addr_len].copy_from_slice(&self.inner.tx_address[0..addr_len]);
786+
Ok(Buffer::from(&address[0..addr_len]))
778787
}
779788

780789
/// Close the specified pipe from receiving transmissions.

bindings/python/src/radio/interface.rs

+16-9
Original file line numberDiff line numberDiff line change
@@ -648,17 +648,24 @@ impl RF24 {
648648
.map_err(|e| PyRuntimeError::new_err(format!("{e:?}")))
649649
}
650650

651-
/// Set the address used for transmitting on pipe 0.
651+
/// The TX address used on pipe 0 for outgoing transmissions.
652652
///
653-
/// Only pipe 0 can be used for transmitting. It is highly recommended to
654-
/// avoid using pipe 0 to receive because of this.
653+
/// Set this before calling [`RF24.as_tx()`][rf24_py.RF24.as_tx].
655654
///
656-
/// Parameters:
657-
/// address: The address to receive data from.
658-
pub fn open_tx_pipe(&mut self, address: &[u8]) -> PyResult<()> {
659-
self.inner
660-
.open_tx_pipe(address)
661-
.map_err(|e| PyRuntimeError::new_err(format!("{e:?}")))
655+
/// This is cached on the [`RF24`][rf24_py.RF24] instance, so
656+
/// [`RF24.as_tx()`][rf24_py.RF24.as_tx] can
657+
/// use it to ensure proper auto-ack behavior in TX mode,
658+
/// if pipe 0 is also used for RX with a different address.
659+
#[setter]
660+
pub fn set_tx_address(&mut self, address: &[u8]) {
661+
let addr_len = address.len().min(5);
662+
self.inner.tx_address[0..addr_len].copy_from_slice(&address[0..addr_len])
663+
}
664+
665+
#[getter]
666+
pub fn get_tx_address(&mut self) -> PyResult<Cow<[u8]>> {
667+
let addr_len = self.get_address_length()? as usize;
668+
Ok(Cow::from(&self.inner.tx_address[0..addr_len]))
662669
}
663670

664671
/// Close the specified pipe from receiving transmissions.

crates/rf24-rs/src/radio/prelude.rs

+14-8
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ pub trait EsbPipe {
2121
///
2222
/// If the specified `pipe` is not in range [0, 5], then this function does nothing.
2323
///
24-
/// Up to 6 pipes can be open for reading at once. Open all the required
24+
/// Up to 6 pipes can be open for reading at once. Open all the required
2525
/// reading pipes, and then call [`EsbRadio::as_rx()`].
2626
///
2727
/// ### About pipe addresses
@@ -41,11 +41,14 @@ pub trait EsbPipe {
4141
///
4242
/// <div class="warning">
4343
///
44+
/// Only pipe 0 can be used for transmitting. It is highly recommended to
45+
/// avoid using pipe 0 to receive because of this.
46+
///
4447
/// If the pipe 0 is opened for receiving by this function, the `address`
4548
/// passed to this function (for pipe 0) will be restored at every call to
4649
/// [`EsbRadio::as_rx()`].
4750
/// This address restoration is implemented because of the underlying necessary
48-
/// functionality of [`EsbPipe::open_tx_pipe()`].
51+
/// functionality of [`EsbRadio::as_tx()`].
4952
///
5053
/// It is important that the `address` length for pipe 0
5154
/// is equal to the length configured by [`EsbPipe::set_address_length()`].
@@ -56,10 +59,6 @@ pub trait EsbPipe {
5659
/// to understand how to avoid using malformed addresses.
5760
fn open_rx_pipe(&mut self, pipe: u8, address: &[u8]) -> Result<(), Self::PipeErrorType>;
5861

59-
/// Set an address to pipe 0 for transmitting when radio is in TX mode.
60-
///
61-
fn open_tx_pipe(&mut self, address: &[u8]) -> Result<(), Self::PipeErrorType>;
62-
6362
/// Close a specified pipe from receiving data when radio is in RX mode.
6463
fn close_rx_pipe(&mut self, pipe: u8) -> Result<(), Self::PipeErrorType>;
6564

@@ -480,14 +479,21 @@ pub trait EsbRadio {
480479
///
481480
/// Conventionally, this should be called after setting the RX addresses via
482481
/// [`EsbPipe::open_rx_pipe()`]
482+
///
483+
/// This function will restore the cached RX address set to pipe 0.
484+
/// This is done because the [`EsbRadio::as_tx()`] will appropriate the
485+
/// RX address on pipe 0 for auto-ack purposes.
483486
fn as_rx(&mut self) -> Result<(), Self::RadioErrorType>;
484487

485488
/// Put the radio into inactive TX mode.
486489
///
487490
/// This must be called at least once before calling [`EsbRadio::send()`] or
488491
/// [`EsbRadio::write()`].
489-
/// Conventionally, this should be called after setting the TX address via
490-
/// [`EsbPipe::open_tx_pipe()`].
492+
/// Conventionally, this should be called after setting the
493+
/// [`tx_address`](value@crate::radio::rf24::RF24::tx_address).
494+
///
495+
/// For auto-ack purposes, this function will also restore the cached
496+
/// [`tx_address`](value@crate::radio::rf24::RF24::tx_address) to the RX pipe 0.
491497
fn as_tx(&mut self) -> Result<(), Self::RadioErrorType>;
492498

493499
/// Is the radio in RX mode?

crates/rf24-rs/src/radio/rf24/init.rs

+20-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use super::{data_rate::set_tx_delay, registers, Feature, Nrf24Error, RF24};
1+
use super::{commands, data_rate::set_tx_delay, registers, Feature, Nrf24Error, RF24};
22
use crate::{
33
radio::{
44
prelude::{EsbChannel, EsbFifo, EsbInit, EsbPayloadLength, EsbPipe, EsbPower, EsbStatus},
@@ -57,7 +57,8 @@ where
5757
self.flush_rx()?;
5858
self.flush_tx()?;
5959

60-
self.set_address_length(config.address_length())?;
60+
let addr_len = config.address_length();
61+
self.set_address_length(addr_len)?;
6162

6263
self.spi_write_byte(registers::SETUP_RETR, config.auto_retries.into_bits())?;
6364
self.spi_write_byte(registers::EN_AA, config.auto_ack())?;
@@ -85,8 +86,17 @@ where
8586
self.close_rx_pipe(pipe)?;
8687
}
8788
}
88-
config.tx_address(&mut address);
89-
self.open_tx_pipe(&address)?;
89+
config.tx_address(&mut self.tx_address);
90+
// use `spi_transfer()` to avoid multiple borrows of self (`spi_write_buf()` and `tx_address`)
91+
for reg in [registers::TX_ADDR, registers::RX_ADDR_P0] {
92+
self.buf[0] = reg | commands::W_REGISTER;
93+
self.buf[1..addr_len as usize + 1]
94+
.copy_from_slice(&self.tx_address[0..addr_len as usize]);
95+
self.spi_transfer(addr_len + 1)?;
96+
}
97+
// enable pipe 0 for TX mode
98+
self.spi_read(1, registers::EN_RXADDR)?;
99+
self.spi_write_byte(registers::EN_RXADDR, self.buf[1] | 1)?;
90100

91101
self.set_payload_length(config.payload_length())?;
92102

@@ -305,6 +315,12 @@ mod test {
305315
],
306316
vec![0xEu8, 0, 0, 0, 0, 0],
307317
),
318+
// open pipe 0 for TX (regardless of auto-ack)
319+
(vec![registers::EN_RXADDR, 0u8], vec![0xEu8, 0u8]),
320+
(
321+
vec![registers::EN_RXADDR | commands::W_REGISTER, 1u8],
322+
vec![0xEu8, 0u8],
323+
),
308324
// set payload length to 32 bytes on all pipes
309325
(
310326
vec![registers::RX_PW_P0 | commands::W_REGISTER, 32u8],

crates/rf24-rs/src/radio/rf24/mod.rs

+32
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,14 @@ pub struct RF24<SPI, DO, DELAY> {
8080
config_reg: ConfigReg,
8181
feature: Feature,
8282
pipe0_rx_addr: Option<[u8; 5]>,
83+
/// The TX address used on pipe 0 for outgoing transmissions.
84+
///
85+
/// Set this before calling [`RF24::as_tx()`].
86+
///
87+
/// This is cached on the [`RF24`] instance, so [`RF24::as_tx()`] can
88+
/// use it to ensure proper auto-ack behavior in TX mode,
89+
/// if pipe 0 is also used for RX with a different address.
90+
pub tx_address: [u8; 5],
8391
payload_length: u8,
8492
}
8593

@@ -104,6 +112,7 @@ where
104112
status: StatusFlags::from_bits(0),
105113
buf: [0u8; 33],
106114
pipe0_rx_addr: None,
115+
tx_address: [0xE7; 5],
107116
feature: Feature::from_bits(0)
108117
.with_address_length(5)
109118
.with_is_plus_variant(true),
@@ -284,6 +293,29 @@ mod test {
284293
vec![registers::CONFIG | commands::W_REGISTER, 0xCu8],
285294
vec![0xEu8, 0u8],
286295
),
296+
//set cached TX address
297+
(
298+
vec![
299+
registers::TX_ADDR | commands::W_REGISTER,
300+
0xE7,
301+
0xE7,
302+
0xE7,
303+
0xE7,
304+
0xE7
305+
],
306+
vec![0xE, 0, 0, 0, 0, 0]
307+
),
308+
(
309+
vec![
310+
registers::RX_ADDR_P0 | commands::W_REGISTER,
311+
0xE7,
312+
0xE7,
313+
0xE7,
314+
0xE7,
315+
0xE7
316+
],
317+
vec![0xE, 0, 0, 0, 0, 0]
318+
),
287319
// open pipe 0 for TX (regardless of auto-ack)
288320
(vec![registers::EN_RXADDR, 0u8], vec![0xEu8, 0u8]),
289321
(

crates/rf24-rs/src/radio/rf24/pipe.rs

-27
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,6 @@ where
4242
self.spi_write_byte(registers::EN_RXADDR, out)
4343
}
4444

45-
fn open_tx_pipe(&mut self, address: &[u8]) -> Result<(), Self::PipeErrorType> {
46-
self.spi_write_buf(registers::TX_ADDR, address)?;
47-
self.spi_write_buf(registers::RX_ADDR_P0, address)
48-
}
49-
5045
/// If the given `pipe` number is not in range [0, 5], then this function does nothing.
5146
fn close_rx_pipe(&mut self, pipe: u8) -> Result<(), Self::PipeErrorType> {
5247
if pipe > 5 {
@@ -106,28 +101,6 @@ mod test {
106101
let address = [0x55u8; 5];
107102
radio.open_rx_pipe(9, &address).unwrap();
108103
radio.open_rx_pipe(5, &address).unwrap();
109-
spi.done();
110-
ce_pin.done();
111-
}
112-
113-
#[test]
114-
pub fn open_tx_pipe() {
115-
let mut expected_buf = [0x55u8; 6];
116-
expected_buf[0] = registers::TX_ADDR | commands::W_REGISTER;
117-
let mut p0_buf = [0x55u8; 6];
118-
p0_buf[0] = registers::RX_ADDR_P0 | commands::W_REGISTER;
119-
let mut response = [0u8; 6];
120-
response[0] = 0xEu8;
121-
122-
let spi_expectations = spi_test_expects![
123-
// open_rx_pipe(5)
124-
(expected_buf.to_vec(), response.clone().to_vec()),
125-
(p0_buf.to_vec(), response.to_vec()),
126-
];
127-
let mocks = mk_radio(&[], &spi_expectations);
128-
let (mut radio, mut spi, mut ce_pin) = (mocks.0, mocks.1, mocks.2);
129-
let address = [0x55u8; 5];
130-
radio.open_tx_pipe(&address).unwrap();
131104
radio.close_rx_pipe(9).unwrap();
132105
spi.done();
133106
ce_pin.done();

crates/rf24-rs/src/radio/rf24/radio.rs

+42-14
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,17 @@ where
4242
self.config_reg = self.config_reg.as_tx();
4343
self.spi_write_byte(registers::CONFIG, self.config_reg.into_bits())?;
4444

45+
let addr_len = self.feature.address_length();
46+
// use `spi_transfer()` to avoid multiple borrows of self (`spi_write_buf()` and `tx_address`)
47+
for reg in [registers::TX_ADDR, registers::RX_ADDR_P0] {
48+
self.buf[0] = reg | commands::W_REGISTER;
49+
self.buf[1..addr_len as usize + 1]
50+
.copy_from_slice(&self.tx_address[0..addr_len as usize]);
51+
self.spi_transfer(addr_len + 1)?;
52+
}
53+
4554
self.spi_read(1, registers::EN_RXADDR)?;
46-
let out = self.buf[1] | 1;
47-
self.spi_write_byte(registers::EN_RXADDR, out)
55+
self.spi_write_byte(registers::EN_RXADDR, self.buf[1] | 1)
4856
}
4957

5058
fn is_rx(&self) -> bool {
@@ -98,23 +106,22 @@ where
98106
// TX FIFO is full already
99107
return Ok(false);
100108
}
101-
let mut buf_len = buf.len().min(32) as u8;
109+
let buf_len = buf.len().min(32);
102110
// to avoid resizing the given buf, we'll have to use self._buf directly
103111
self.buf[0] = if !ask_no_ack {
104112
commands::W_TX_PAYLOAD
105113
} else {
106114
commands::W_TX_PAYLOAD_NO_ACK
107115
};
108-
self.buf[1..(buf_len + 1) as usize].copy_from_slice(&buf[..buf_len as usize]);
116+
self.buf[1..buf_len + 1].copy_from_slice(&buf[..buf_len]);
109117
// ensure payload_length setting is respected
110-
if !self.feature.dynamic_payloads() && buf_len < self.payload_length {
118+
if !self.feature.dynamic_payloads() && (buf_len as u8) < self.payload_length {
111119
// pad buf with zeros
112-
for i in (buf_len + 1)..(self.payload_length + 1) {
113-
self.buf[i as usize] = 0;
114-
}
115-
buf_len = self.payload_length;
120+
self.buf[buf_len + 1..self.payload_length as usize + 1].fill(0);
121+
self.spi_transfer(self.payload_length + 1)?;
122+
} else {
123+
self.spi_transfer(buf_len as u8 + 1)?;
116124
}
117-
self.spi_transfer(buf_len + 1)?;
118125
if start_tx {
119126
self.ce_pin.set_high().map_err(Nrf24Error::Gpo)?;
120127
}
@@ -150,9 +157,7 @@ where
150157
return Ok(0);
151158
}
152159
self.spi_read(buf_len, commands::R_RX_PAYLOAD)?;
153-
for i in 0..buf_len {
154-
buf[i as usize] = self.buf[i as usize + 1];
155-
}
160+
buf[0..buf_len as usize].copy_from_slice(&self.buf[1..buf_len as usize + 1]);
156161
let flags = StatusFlags::from_bits(mnemonics::MASK_RX_DR);
157162
self.clear_status_flags(flags)?;
158163
Ok(buf_len)
@@ -256,7 +261,7 @@ mod test {
256261
vec![registers::STATUS | commands::W_REGISTER, 0x70u8],
257262
vec![0xEu8, 0u8],
258263
),
259-
// write cached _pipe0_rx_addr
264+
// write cached pipe0_rx_addr
260265
(buf_expected.to_vec(), vec![0xEu8, 0u8, 0u8, 0u8, 0u8, 0u8]),
261266
];
262267
let mocks = mk_radio(&ce_expectations, &spi_expectations);
@@ -279,6 +284,29 @@ mod test {
279284
vec![registers::CONFIG | commands::W_REGISTER, 0xCu8],
280285
vec![0xEu8, 0u8],
281286
),
287+
// set cached TX address to RX pipe 0
288+
(
289+
vec![
290+
registers::TX_ADDR | commands::W_REGISTER,
291+
0xE7,
292+
0xE7,
293+
0xE7,
294+
0xE7,
295+
0xE7
296+
],
297+
vec![0xE, 0, 0, 0, 0, 0]
298+
),
299+
(
300+
vec![
301+
registers::RX_ADDR_P0 | commands::W_REGISTER,
302+
0xE7,
303+
0xE7,
304+
0xE7,
305+
0xE7,
306+
0xE7
307+
],
308+
vec![0xE, 0, 0, 0, 0, 0]
309+
),
282310
// open pipe 0 for TX (regardless of auto-ack)
283311
(vec![registers::EN_RXADDR, 0u8], vec![0xEu8, 0u8]),
284312
(

0 commit comments

Comments
 (0)