Feed on
Posts
Comments

Following on from seeing nearby nRF24’s on the nRF24 Multi-Network, we’ll now be adding the functionality of forwarding data to other nRFs so they can forward it on to the intended nRF if you can’t reach the other nRF directly. It would be interesting to have a couple nRF’s in a line so that each nRF can only see two others to see how it all performs. I’ve decided to increase the clock speed of the ATtiny to 8MHz to speed things up.

https://www.youtube.com/watch?v=Ht2Y5NRWQmQ
(The left nRF sends a packet to the middle nRF which forwards it to the right nRF and then it goes in the opposite direction)

For the code below – as part of reading packets and processing them, we check if the type is a forward or forward response and if the to address matches our address and it’s a forward, then we send an forward response back. Otherwise if we received the forward response and it was to our address then we’re done. Lastly if the from address doesn’t match our address (i.e we won’t respond to nRFs forwarding our packet back to us) and the TTL doesn’t equal 0, we decrement the TTL and re-forward the packet.

Since other nRFs will be transmitting too in our forwarding scenario, it’s best to wait a little while before re-transmitting so that we are less likely to interfere with other nRFs transmitting the same forward packet.

uint8_t read_packet_and_process(void) {
  ...
  // Request for forwarding request or a forwarding response
  else if (data_in[TYPE] == FWD || data_in[TYPE] == FWDRESPOND) {
    // Was the forward request intended for us, then send a forward ACK reply
    if (data_in[TO] == MYADDR && data_in[TYPE] == FWD) {
      // Clear data out
      for (int x = 0; x < 32; x++) {
        data_out[x] = 0;
      }

      data_out[FROM] = MYADDR; // From our address
      data_out[TO] = data_in[FROM]; // Send back to the address that we received this packet from
      data_out[TYPE] = FWDRESPOND; // Ack respond

      // Send the ACK to the forward with random delay in-between
      mirf_transmit_data(RANDOMDELAY);
      return RECEIVED_DATA_FROM_FORWARD;
  }
  // Was the forward ACK response intended for us
  else if (data_in[TO] == MYADDR && data_in[TYPE] == FWDRESPOND) {
    return RECEIVED_DATA_FORWARD_ACK;
  }
  // Forward to any nRF if the from address isn't ours and TTL isn't 0 (i.e we didn't send the original forward packet)
  else if (data_in[FROM] != MYADDR && data_in[TTL] != 0) {
    // Copy data in to data out
    for (int x = 0; x < 32; x++) {
      data_out[x] = data_in[x];
    }
    // Deincrement the TTL
    data_out[TTL]--;
    // Send with random delay in-between
    mirf_transmit_data(RANDOMDELAY);
    return RECEIVED_DATA_TO_FORWARD;
  }
...

For testing, the 3rd nRF sends out the forwarding packet which gets received by the 2nd nRF, is forwarded to the 1st nRF which sends the forward ACK back to the 2nd nRF and then sends that back to the 3rd nRF. The 3rd nRF transmits and waits with the code below. It’s really up to the user as to how long you would like to wait for the forward ACK back.

data_out[FROM] = MYADDR;
data_out[TO] = 1;
data_out[TYPE] = FWD;
data_out[TTL] = 1;
data_out[DATA] = 0;
mirf_transmit_data(NODELAY);

// Wait for replies for 100 ms
for (int x = 0; x < 50; x++) {
  if (mirf_listen_5ms_timeout()) {
    if (read_packet_and_process() == RECEIVED_DATA_FORWARD_ACK) {
      PORTB |= (1<<PB2);
      _delay_ms(500);
      PORTB &= ~(1<<PB2);
      _delay_ms(500);
      break;
...

When testing, since all nRFs are close to each other, we need to modify the code so they only listen and reply to specific packets as shown below.

// Addr 3
if (data_in[TO] == MYADDR && data_in[TYPE] == FWDRESPOND && data_in[TTL] == 0) {
  return RECEIVED_DATA_FORWARD_ACK;
}

// Addr 2
/*if (data_in[TTL] == 1) {
  // Copy data in to data out
  for (int x = 0; x < 32; x++) {
    data_out[x] = data_in[x];
  }

  // Deincrement the TTL
  data_out[TTL]--;

  // Send with random delay in-between
  mirf_transmit_data(RANDOMDELAY);

  return RECEIVED_DATA_TO_FORWARD;
}*/

// Addr 1
/*if (data_in[TO] == MYADDR && data_in[TYPE] == FWD && data_in[TTL] == 0) {
  // Clear data out
  for (int x = 0; x < 32; x++) {
    data_out[x] = 0;
  }

  data_out[FROM] = MYADDR; // From our address
  data_out[TO] = data_in[FROM]; // Send back to the address that we received this packet from
  data_out[TYPE] = FWDRESPOND; // Ack respond
  data_out[TTL] = 1;

  // Send the ACK to the forward with random delay in-between
  mirf_transmit_data(RANDOMDELAY);

  return RECEIVED_DATA_FROM_FORWARD;
}*/

I’ve also tested without the code below and situating them far enough/in secluded areas so that each nRF can only see another nRF except for the 2nd one which sees both and it works fine.

Some other changes I’ve made were to update the transmit and check ack function so now we check for an ack right after we send the packet so we don’t have to send a few packets and then check for acks afterwards.

uint8_t mirf_transmit_data_check_ack(void) {
  TX_POWERUP; // Power up to transmitter mode
  ...
  // Reuse TX Payload, re-send the packet a few times to ensure they get it
  for (int x = 0; x < 4; x++) {
    TX_POWERUP; // Power up to transmitter mode
    _delay_us(500); // Wait until packet is transmitted
    ...
    RX_POWERUP;
    mirf_CE_hi; // Start listening
    ...
    // Check the ACK matches our address, who it came from and the ack type
    if (data_in[TO] == MYADDR && data_in[FROM] == data_out[TO] && data_in[TYPE] == ACKRESPOND) {
      return 1;
   }
...

The checking for neighbouring nRFs was also updated to now read the FIFO RX so if multiple nRFs send one after the other, we won’t t flush the TX/RX so we read all packets.

uint8_t check_neighbouring_nrf(void) {
  ...
  RX_POWERUP; // Power up to receiver mode
  mirf_flush_rx_tx(); // Flush RX and TX FIFO
  mirf_CE_hi; // Start listening
  ...
  // Check all FIFO RX
  while (!(mirf_fifostatus() & (1<<RX_EMPTY))) {
    ...
  }
}

Download nRF24_Multi-Network_TX-RX_v0.3 which includes the 3 nRF example.

Leave a Reply