Skip to content

Fix for large payloads with NRF52#244

Merged
TMRh20 merged 23 commits intomasterfrom
FixMaxFrameSize
Feb 27, 2025
Merged

Fix for large payloads with NRF52#244
TMRh20 merged 23 commits intomasterfrom
FixMaxFrameSize

Conversation

@TMRh20
Copy link
Member

@TMRh20 TMRh20 commented Feb 22, 2025

It seems we missed configuring the MAX_FRAME_SIZE with NRF52 devices in V2.0.

This fix clears that issue up.

@TMRh20 TMRh20 requested a review from 2bndy5 February 22, 2025 17:29
@TMRh20 TMRh20 marked this pull request as draft February 22, 2025 20:05
@TMRh20
Copy link
Member Author

TMRh20 commented Feb 22, 2025

So in reviewing this, additional changes are required to support large payloads. The write() function needed to be modified to take into account the max dynamic payload size set in nrf_to_nrf so it knows when to fragment payloads.

Additionally on line 705:

https://github.com/nRF24/RF24Network/pull/244/files#diff-8a8080552c7f675fa680e629d5c5baa2c875d8334cbd57d38b0e44bfc328b751R705-R707

we need to check if encryption is enabled to fragment payloads at the proper sizes. This seems to require adding a new variable to RF24, bool enableEncryption since I am getting errors trying to use this variable without having it defined in RF24. Any idea how to work around adding a variable to RF24 @2bndy5 ?

This is still a bit messy, I'm thinking I can clean it up a whole bunch still by defining variables for MAX_FRAME_SIZE and max_frame_payload_size instead of a mess of #defines.

@2bndy5

This comment has been minimized.

@2bndy5
Copy link
Member

2bndy5 commented Feb 22, 2025

Can you simply guard the encryption stuff with #ifdef RF52 or something similar?

@TMRh20
Copy link
Member Author

TMRh20 commented Feb 22, 2025

Can you simply guard the encryption stuff with #ifdef RF52 or something similar?

I tried that here:

#if defined(NRF52_RADIO_LIBRARY)
    uint8_t max_frame_size = NRF_RADIO->PCNF1;
    if(radio.enableEncryption == true){
       max_frame_size -= CCM_IV_SIZE + CCM_COUNTER_SIZE + CCM_MIC_SIZE;
    }
    uint8_t max_frame_payload_size2 = max_frame_size - sizeof(RF24NetworkHeader);
#endif

and get:

RF24Network.cpp:705:14: error: 'class RF24' has no member named 'enableEncryption'

I think because RF24 is included by default, the compiler expects it to have the same variables as nrf_to_nrf

@2bndy5
Copy link
Member

2bndy5 commented Feb 23, 2025

I think we'd need to go by the template typename (radio_t), but I haven't looked into this too deeply.

#if defined(ARDUINO_ARCH_NRF52) || defined(ARDUINO_ARCH_NRF52840) || defined(ARDUINO_ARCH_NRF52833)
// nrf_to_nrf is defined, now compare template radio type
if (radio_t == nrf_to_nrf) {
 // do nRF5x specific stuff
}
#endif

- Specify nrf_to_nrf or RF24 behavior in the overloaded beginWrite() functions
@TMRh20 TMRh20 marked this pull request as ready for review February 23, 2025 12:05
github-actions[bot]

This comment was marked as resolved.

@github-actions github-actions bot dismissed their stale review February 23, 2025 19:46

outdated suggestion

github-actions[bot]

This comment was marked as resolved.

@github-actions github-actions bot dismissed their stale review February 23, 2025 19:53

outdated suggestion

@2bndy5
Copy link
Member

2bndy5 commented Feb 23, 2025

I'm going to setup a dedicated CI workflow for nrf_to_nrf, so we can try to detect build failures before merging without local having to locally build first.

I'll have to build an example from nrf_to_nrf lib...

@github-actions
Copy link
Contributor

github-actions bot commented Feb 24, 2025

Memory usage change @ 8d4ecc8

Board flash % RAM for global variables %
arduino:avr:nano 🔺 +12 - +354 +0.04 - +1.15 🔺 +2 - +2 +0.1 - +0.1
arduino:samd:mkrzero 🔺 +4 - +8 0.0 - 0.0 🔺 +8 - +8 +0.02 - +0.02
Click for full report table
Board examples/helloworld_rx
flash
% examples/helloworld_rx
RAM for global variables
% examples/helloworld_rx_advanced
flash
% examples/helloworld_rx_advanced
RAM for global variables
% examples/helloworld_tx_advanced
flash
% examples/helloworld_tx_advanced
RAM for global variables
% examples/helloworld_tx
flash
% examples/helloworld_tx
RAM for global variables
% examples/Network_Priority_RX
flash
% examples/Network_Priority_RX
RAM for global variables
% examples/Network_Priority_TX
flash
% examples/Network_Priority_TX
RAM for global variables
%
arduino:avr:nano 12 0.04 2 0.1 12 0.04 2 0.1 22 0.07 2 0.1 354 1.15 2 0.1 12 0.04 2 0.1 230 0.75 2 0.1
arduino:samd:mkrzero 4 0.0 8 0.02 4 0.0 8 0.02 8 0.0 8 0.02 8 0.0 8 0.02 4 0.0 8 0.02 8 0.0 8 0.02
Click for full report CSV
Board,examples/helloworld_rx<br>flash,%,examples/helloworld_rx<br>RAM for global variables,%,examples/helloworld_rx_advanced<br>flash,%,examples/helloworld_rx_advanced<br>RAM for global variables,%,examples/helloworld_tx_advanced<br>flash,%,examples/helloworld_tx_advanced<br>RAM for global variables,%,examples/helloworld_tx<br>flash,%,examples/helloworld_tx<br>RAM for global variables,%,examples/Network_Priority_RX<br>flash,%,examples/Network_Priority_RX<br>RAM for global variables,%,examples/Network_Priority_TX<br>flash,%,examples/Network_Priority_TX<br>RAM for global variables,%
arduino:avr:nano,12,0.04,2,0.1,12,0.04,2,0.1,22,0.07,2,0.1,354,1.15,2,0.1,12,0.04,2,0.1,230,0.75,2,0.1
arduino:samd:mkrzero,4,0.0,8,0.02,4,0.0,8,0.02,8,0.0,8,0.02,8,0.0,8,0.02,4,0.0,8,0.02,8,0.0,8,0.02

@2bndy5
Copy link
Member

2bndy5 commented Feb 24, 2025

ok, I tested this on linux and found no disruption: examples work as expected.

I added copies of RF24Network examples from nrf2nrf library and used them in CI to test build the lib for adafruit feather nrf52840 (using PlatformIO). I'm not proud of the keeping duplicate examples in this lib and nrf2nrf lib, but it was path of least resistance that allowed using the reusable workflow in nRF24/.github repo.

@2bndy5
Copy link
Member

2bndy5 commented Feb 24, 2025

There's a not insignificant increase in compile size on Arduino Nano, So I'm hoping we can do better with the new specialized functions (maybe mark them as inline).

@2bndy5
Copy link
Member

2bndy5 commented Feb 24, 2025

@TMRh20 I noticed a growing list of stale branches in the nrf2nrf repo (most of which have been merged to main). I would suggest allowing github to auto-delete merged branches and delete the 1 branch that was abandoned for a different approach (which was merged to main).

@2bndy5
Copy link
Member

2bndy5 commented Feb 24, 2025

Hmm, adding inline didn't seem to reduce the compile size. I'm not sure if we can avoid this. I suppose the deciding factor would be: Will it still fit on ATTiny85?

@TMRh20
Copy link
Member Author

TMRh20 commented Feb 24, 2025

The following code still compiles on an ATtiny 84:

Sketch uses 7194 bytes (95%) of program storage space. Maximum is 7552 bytes.
Global variables use 204 bytes (39%) of dynamic memory, leaving 308 bytes for local variables. Maximum is 512 bytes.

#include "RF24.h"
#include "RF24Network.h"
#include "RF24Mesh.h"


/**** Configure the nrf24l01 CE and CS pins ****/
RF24 radio(7, 8);
RF24Network network(radio);
RF24Mesh mesh(radio, network);

/*
 * User Configuration: nodeID - A unique identifier for each radio. Allows addressing
 * to change dynamically with physical changes to the mesh.
 *
 * In this example, configuration takes place below, prior to uploading the sketch to the device
 * A unique value from 1-255 must be configured for each node.
 */
#define nodeID 1


uint32_t displayTimer = 0;

struct payload_t {
  unsigned long ms;
  unsigned long counter;
};

void setup() {

  mesh.setNodeID(nodeID);
  mesh.begin();
}



void loop() {

  mesh.update();

  // Send to the master node every second
  if (millis() - displayTimer >= 1000) {
    displayTimer = millis();

    // Send an 'M' type message containing the current millis()
    if (!mesh.write(&displayTimer, 'M', sizeof(displayTimer))) {
      // If a write fails, check connectivity to the mesh network
      if (!mesh.checkConnection()) {
        //refresh the network address
        if (mesh.renewAddress() == MESH_DEFAULT_ADDRESS) {
          //If address renewal fails, reconfigure the radio and restart the mesh
          //This allows recovery from most if not all radio errors
          mesh.begin();
        }
      }
    }

    while (network.available()) {
      RF24NetworkHeader header;
      payload_t payload;
      network.read(header, &payload, sizeof(payload));
    }
  }
}

@TMRh20
Copy link
Member Author

TMRh20 commented Feb 24, 2025

I just realized this changes the behavior of the bool ESBNetwork<radio_t>::write(RF24NetworkHeader& header, const void* message, uint16_t len, uint16_t writeDirect) function, which is typically only used in certain situations, but it IS a public function.

For now, users would need to call the standard write bool ESBNetwork<RF24>::write(RF24NetworkHeader& header, const void* message, uint16_t len) function at least once before calling the other write function, in order to set the correct max_payload_size.

I think I'm good with that, since most users probably don't use this function, a comment in the docs is probably good.

- Need to put the max_frame_size calcs in the final write function, or it will not be set properly if using Multicast or WriteDirect functions
@TMRh20
Copy link
Member Author

TMRh20 commented Feb 24, 2025

Re-thinking it, I'm not good with the previously noted behavior, its like adding a bug for no reason, so I put the max_payload_size calculations in the final bool ESBNetwork<RF24>::write(RF24NetworkHeader& header, const void* message, uint16_t len, uint16_t writeDirect) function

@2bndy5
Copy link
Member

2bndy5 commented Feb 24, 2025

I guess my concerns about compile size are negligible. Especially since NO_MASTER is still an option.

I also agree that behavior is important to remain consistent. Further changes to v2 might be complicated to cherry pick into v1 branch. But I think we can still make it work.

@TMRh20
Copy link
Member Author

TMRh20 commented Feb 25, 2025

Yeah, for the v1 branch I think it will have to be left up to the user to set the correct MAX_FRAME_SIZE in RF24Network.h

- Add intermediate function write() and change the old write() function to main_write()
@TMRh20
Copy link
Member Author

TMRh20 commented Feb 25, 2025

I ended up re-adding the intermediate function, it seemed a better option than recreating the entire write((RF24NetworkHeader& header, const void* message, uint16_t len, uint16_t writeDirect)) function for each radio.

Now the main user write(RF24NetworkHeader& header, const void* message, uint16_t len) function calls write(RF24NetworkHeader& header, const void* message, uint16_t len, uint16_t writeDirect) which sets the max_frame_size then calls main_write()

What a mess of commits lol. Can you tell I've been feeling under the weather lately?

@2bndy5
Copy link
Member

2bndy5 commented Feb 25, 2025

I just went through a week of the flu myself. Still coughing up phlegm...

Co-authored-by: Brendan <2bndy5@gmail.com>
@TMRh20
Copy link
Member Author

TMRh20 commented Feb 27, 2025

Ok, we've let it stew a bit here, and I don't see any more changes required. You figure this one is ready to merge?

@TMRh20 TMRh20 requested a review from 2bndy5 February 27, 2025 10:49
Copy link
Member

@2bndy5 2bndy5 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good.

After eae94fb, the compile size slightly decreased for priority_tx example, but it dramatically increased for holloworld_tx example. I'll have to look into this later...

@TMRh20 TMRh20 merged commit 32e78d2 into master Feb 27, 2025
101 checks passed
@TMRh20 TMRh20 deleted the FixMaxFrameSize branch February 27, 2025 10:56
2bndy5 added a commit that referenced this pull request May 4, 2025
* Fix for large payloads with NRF52

* Specify nrf_to_nrf or RF24 behavior in the overloaded write() functions

* modify doc comment

* add CI for nrf_to_nrf

* disambiguate examples in docs

- Add intermediate function write() and change the old write() function to main_write()

* Make main_write() function inline

Co-authored-by: Brendan <2bndy5@gmail.com>

* Remove max_frame_size var

Per @2bndy5 in #244

* Remove setting max_frame_payload_size in RF24 write function

* Missing bracket...

---------

Co-authored-by: Brendan <2bndy5@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants