NRF24 Identity crisis!

Hi,
I’m attempting to run a system with many Uno based stations being controlled by a Mega over a NRF24 L01 mesh, and I’m having issues with the correct identification of each of the stations.
I have numbered the Uno stations with #define nodeID xx and my understanding is that the control station is supposed to read the nodeID value in a package it receives when it sets up the mesh and make the allocation per that value.
I cannot get it to happen as I (probably naively) expect, and I’m wondering if anyone else has ever managed to get their heads around this (thorny?) issue.

What appears to actually happen is that the mesh seems to get set up on a first-served, first named basis - quite random really - and I cannot figure out how to overcome this.
It is quite important for me to have the ability to correctly sequence the stations from nodeID to nodeID - and as an option to deliberately randomise - but only when I so choose.
A further wrinkle is that there could be some numbers missing, but the mesh should still cope eg: 1,2,4,5,6,8,9.

I have based my code on the excellent work here by tmrh20, but feel as though I’m either mis-understanding or just missing something…: http://tmrh20.github.io/RF24Mesh/RF24Mesh_Example_8ino-example.html

/**
 * Main console, keeping track, commanding and receiving returns from Stations
 **/
 
 
#include "RF24Network.h" //..........

RF24 radio(48,49);
RF24Network network(radio); 
RF24Mesh mesh(radio,network);

void setup() {
  Serial.begin(115200);

  mesh.setNodeID(0);                  // Set the nodeID to 0 for the master node
  
  mesh.begin();                       // Connect to the mesh
  for (int i = 0; i < sizeof(maxStations); i++) maxStations[i] = i;  // populate the array fully
}
 
void loop() {    
  mesh.update();                      // Call mesh.update to keep the network updated
  mesh.DHCP();                        // In addition, keep the 'DHCP service' running on the
                                            // master node so addresses will be assigned to the sensor nodes
 
  uint8_t stationCount = 0;           // Start the sequence at 0
  stations = listCurrent;                // Read the node-count and proceed with what we've got
  bool hit = false; 
  
  while (proceed == true){            // Are we Good to GO? 
    if(digitalRead(2)== LOW) {
      randomiseStations();             // Jumble the list
      String mode = "RANDOM MODE";
      printStationsLCD(mode);
    } 
    else  {
      String mode = "SEQUENTIAL MODE";
      printStationsLCD(mode);
    }

/*****  This is the main 'Loop' for gameplay*****/
    payload.trigStation = 'u';
    while (stationCount < stations) {  
      mesh.update(); 
      if(next == true && (EEPROM.read((maxStations[stationCount]*2))*1000 < millis() - timer)) { 
        Serial.print("Addressing Station: ");
        Serial.print(maxStations[stationCount+1]);
        payload.maxUp = EEPROM.read(maxStations[stationCount]*2);
        RF24NetworkHeader header(mesh.addrList[stationCount].address, OCT); //Constructing a header
        if(network.write(header, &payload, sizeof(payload)) == 1){
          Serial.println(F("  Send OK"));
          timer = millis();
          hit = false;
          next = false;
        } else { 
          Serial.println(F("  Send Fail"));
          }
        }
        while (network.available()){                      // Check for incoming data from the sensors
          RF24NetworkHeader header;
          network.peek(header);
  
          switch(header.type){              
          case 'M':                                       // Display the incoming values from the sensor nodes
            float xPlier;                                 // Score multiplier from when shot taken after UP
            network.read(header,&scorePackage,sizeof(scorePackage));
            Serial.print(F("\tPlayer# "));
            Serial.print(playerCount-1);
            if (scorePackage.timeout == 1){
              Serial.print(" - timed out on target# "); Serial.println(scorePackage.NodeIdent);
            }else{
              Serial.print(F("; scored "));
              Serial.print(scorePackage.ScoreVal);
              //code
              } else {
                Serial.print(F(" for a multiplier of "));
                xPlier = ((10*(1.0+((float)(payload.maxUp - scorePackage.UptoHit) / (float)payload.maxUp)))+0.5);
                Serial.println(xPlier/10);
              }
              scoreboard[scorePackage.NodeIdent][playerCount].score = scorePackage.ScoreVal;
              //code
            }
            next = true;                            // good to go to next station
            hit = true;                             // we have a hit
            stationCount ++ ;
            timer = millis();
            break;
            
          default: 
            network.read(header,0,0);
            Serial.print(F("Something wrong - here: switch(header.type) case default; Header type - ")); 
            Serial.println(header.type);
            break;
          }
       }
    }
    proceed = false;
    stationCount = 0;
    Serial.print(F("All stations completed for Player ID# "));
    //code
  }
  if (mesh.addrListTop != listCurrent){
    Serial.print(F("Station Count: "));
    Serial.println(mesh.addrListTop);
    listCurrent = mesh.addrListTop;
  }
/*   
 *   When at startup or end of cycle, get Serial input to continue
 */
  if ( Serial.available() && (proceed == false) ) {
    char c = toupper(Serial.read());
    if ( c == 'G' ){
      getPlayerID();      
      Serial.print(F("Beginning sequence for Player# "));
      Serial.print(playerCount);
      Serial.print(F(":\t ID-"));
      Serial.println(payload.playerIdent);
      
      playerCount++ ;
      timer = millis();
      Serial.println();
      proceed = true; 
      scorePackage.timeout = false;
      next = true;
      c = "";                 
    } else if ( c == 'Z' ){
        //code
    }
  }
}

//code...

/*****  Reset and re-initialise the stations *****/
bool resetStations(void) {
  mesh.update();
  payload.maxUp = 0;            
  payload.playerIdent = 0;      
  payload.trigStation = 'r';
  Serial.println(F("Resetting: "));
  Serial.print(F("\tStation: "));
  for (uint8_t i = 0; i < 24; i++){
    RF24NetworkHeader header(mesh.addrList[i].address, OCT); //Constructing a header
    if(network.write(header, &payload, sizeof(payload)) == 1){
      Serial.print(F("OK :"));
      Serial.print(i+1);
      Serial.print(F(", "));
    }
  }
  mesh.update();
  mesh.DHCP();
  Serial.println(F(" -Done resetting"));
  return true;
}

And the relevant Station code follows:

#include <SPI.h>
#include "RF24.h"
#include "RF24Network.h"
#include "RF24Mesh.h"
#include <EEPROM.h>

/*
 * The individual main parameters are set here.
 * Name the Station node, from 1 to 24
 */
#define nodeID 1                      // Identify the Station
 

RF24 radio(9,10);
RF24Network network(radio);
RF24Mesh mesh(radio, network);


struct payload_t {
  byte    trigStation;      // What to trigger the Station with: up or reset etc     
  uint8_t maxUp;            // Duration of the Station up-time before Timeout
  uint8_t playerIdent;      // Identity of the current scoring player
};
struct payload_t payload;

struct scorePackage_t {
  uint8_t UptoHit;          // Time recorded by the Station from UP to the Hit
  uint8_t scoreVal;         // 
  uint8_t nodeIdent;        // Station identity returned by Station
  uint8_t playerID;         // Identity of the scoring player returned by Station
  uint8_t timeout;          // Only TRUE if station times out
};
struct scorePackage_t scorePackage;
 
uint8_t retryCounter = 0;                     //used to count the packets sent
bool done = false;                            //used to know when to stop sending packets
bool up  = false;                             // State of the target
long timer;
uint32_t runTimer = 0;
uint16_t upPadding = 2000;



void setup() {

  pinMode(A0,INPUT_PULLUP);
  pinMode(A1,INPUT_PULLUP);
  pinMode(A2,INPUT_PULLUP);
  pinMode(8,OUTPUT);
  pinMode(7,OUTPUT);
  
  Serial.begin(115200);             
  mesh.setNodeID(nodeID);                               // Set the nodeID manually
  Serial.println(F("Connecting to the mesh..."));       // Connect to the mesh
  mesh.begin();
  if (mesh.begin()) Serial.println(" Success ");
}

void loop()  
{
  mesh.update();

  scorePackage.nodeIdent = nodeID;    // Returning confirmation of the identity of this station
  if(done == true) {                                              //true once you have a score 
    if (!mesh.write(&scorePackage, 'M', sizeof(scorePackage))) {  // Send an 'M' type message containing the score data    
      if ( !mesh.checkConnection() ) {                            // If it fails, check the mesh network
        Serial.println(F("Renewing Address"));                    // refresh the network address
        mesh.renewAddress();
        Serial.println("Done with renewal");
      } else {
        Serial.println(F("Send fail, Test OK"));
      }
    } else {
      Serial.print(("Send OK. Score: ")); Serial.print(scorePackage.scoreVal);
      Serial.print(F(" in ")); 
      Serial.print(scorePackage.UptoHit); Serial.print(F( "s of ")); Serial.print(payload.maxUp);
      Serial.print(F("s from Player#: ")); Serial.println(scorePackage.playerID);
      done = false;
      timer = millis();
      //mesh.update();
      
    }
  }

/*****
 * This is the parameter Rx and the scoring engine 
 * it works fine, and returns all the data I need, afaik
 *****/
  while(network.available()) {
    Serial.print(".");
    RF24NetworkHeader header;
    network.read(header, &payload, sizeof(payload));
    if (!(payload.trigStation == 'u') && !(payload.trigStation == 'r' )) {
      Serial.print(F("Received Data from transmitter: ")); 
      Serial.print(F("  Received DATA:  "));
      Serial.println(payload.trigStation);            //print payload 
    }
    if (payload.trigStation == 'r' ) {
      softReset();
    } 
    else if (payload.trigStation == 'u' && !done /*&& (payload.nodeIdent == nodeID)*/) {
      Serial.print("!");
      Serial.print(F("Received Data from transmitter: "));
      Serial.print(F("Player# "));
      Serial.print(payload.playerIdent);  
      Serial.print(F("; UP duration: "));
      Serial.println(payload.maxUp); 
      digitalWrite(8,HIGH);      // Target UP
      Serial.println(F("Station UP"));
      up = true;
      done = false;
      timer = millis();
      scorePackage.timeout = 0;
      scorePackage.playerID = payload.playerIdent;
    }
  }

  
  while(up == true && (millis() - timer) > upPadding)  {   // Allow for the tasrget to settle
    digitalWrite(7,HIGH);                                  // Indicator ON
    if(analogRead(A0) < 900) {
      scorePackage.UptoHit = ((millis() - timer)/1000);
      digitalWrite(8,LOW);     // Output indicator OFF 
      digitalWrite(7,LOW);     // un-flip target
      scorePackage.scoreVal = 10;
      done = true;
      up = false;
    }
    else if(analogRead(A1) < 900) {
      scorePackage.UptoHit = ((millis() - timer)/1000);
      digitalWrite(8,LOW);     // Output indicator OFF 
      digitalWrite(7,LOW);     // un-flip target
      scorePackage.scoreVal = 5;
      done = true;
      up = false;
    }
    else if(analogRead(A2) < 900) {
      scorePackage.UptoHit = ((millis() - timer)/1000);
      digitalWrite(8,LOW);     // Output indicator OFF 
      digitalWrite(7,LOW);     // un-flip target
      scorePackage.scoreVal = 2;
      done = true;
      up = false;
    }
    if(millis() - timer >  (payload.maxUp * 1000) && up == true) {
      scorePackage.UptoHit = ((millis() - timer)/1000);
      digitalWrite(8,LOW);     // Output indicator OFF
      digitalWrite(7,LOW);     // un-flip target
      scorePackage.scoreVal = 0;
      done = true;
      scorePackage.timeout = 1;
      Serial.println(F("STATION TIMEOUT"));
      up = false;
    }
  }

  if ((millis() - timer > 10000)){
    timer = millis();
    if ( !mesh.checkConnection() ) {                            // If it fails, check the mesh network
        Serial.print(F("Renewing Address ..."));                    //refresh the network address
        mesh.renewAddress();
        Serial.println("Done with renewal");
      }
  }
}

void softReset(void){
asm volatile ("  jmp 0");
}

Your code seems to have a lot of undefined variables. Does it compile?

Rather than making folks here wade through your entire application code, you might be better off posting the shortest possible example that:

  1. Correctly compiles.

  2. Demonstrates the problem.

See Short, Self-Contained, Correct (Compilable), Example.

Hi

gfvalvo:
Your code seems to have a lot of undefined variables. Does it compile?

Rather than making folks here wade through your entire application code, you might be better off posting the shortest possible example that:

  1. Correctly compiles.

  2. Demonstrates the problem.

See Short, Self-Contained, Correct (Compilable), Example.

Oh yes - but to include the whole of the code exceeded the limit!

It all works - except for the fact the nodeID doesn't relate as I want it.
I thought to keep the un-related code out would be enough - but i see what you are asking for: something that compiles.
That would take me a while as it is, of course, all related...

OldManNoob:
Hi
Oh yes - but to include the whole of the code exceeded the limit!

It all works - except for the fact the nodeID doesn't relate as I want it.

That's my point. Post a short, complete, example that compiles and demonstrates the problem. The rest is unnecessary clutter. Leave it out.

gfvalvo:
That's my point. Post a short, complete, example that compiles and demonstrates the problem. The rest is unnecessary clutter. Leave it out.

Ok,
After a short but unavoidable interlude while my mother was hospitalised, I'm back and I've re-written the code to be short, to compile and to work.

OldManNoob:
Ok,
After a short but unavoidable interlude while my mother was hospitalised, I'm back and I've re-written the code to be short, to compile and to work.

But you did not bother to post it?

I do hope you did not overwrite the code in an earlier Post?

It would be a big help if you describe the overall system you are trying to implement. Do you really need to use the Mesh system?

...R
Simple nRF24L01+ Tutorial

Robin2:
But you did not bother to post it?

I do hope you did not overwrite the code in an earlier Post?

I did overwrite - yes. It seemed to be the best way.

The code now presented, along with the description of the issue I'm having with identifying the stations correctly, and which I posted originally, are above.

Mesh is required afaik because I need to be able to deploy the stations at (sometimes) quite a distance from the base unit controller. I wish to be able to task the stations regardless of their physical proximity to the master controller.

OldManNoob:
The code now presented, along with the description of the issue I'm having with identifying the stations correctly, and which I posted originally, are above.

My brain hurts, trying to follow this thread I guess.

You did say 'above';

I'm back and I've re-written the code to be short, to compile and to work.

So the code now works is the implication, no more problems ?

Robin2:
It would be a big help if you describe the overall system you are trying to implement.

Command unit “Main Console” (Mega) deals with user interface and input of variables and names.
The variables are related to the timing setup of the “Stations”, of which there are up to 24, and the name/id of the current user. It also handles the acquisition of the returned data from the Stations and the eventual posting of the returned data to a remote database.

The Stations are numbered in the code from 1 - 24.

The idea is that the variables sent by the parameters set in the Main Console (MC)affect the behaviour of the stations. Without going into too much detail (for the sake of clarity) it is sufficient to describe the operation as follows:

MC queries S1 - S24
MC saves result as an array: Array contains the ID numbers of the Stations on the Mesh. eg 1, 2, 3, 11, 14, 17. In this example the are 6 Stations replying, and numbers them 1 - 6

MC “Resets” the Stations in turn.

MC needs to “see” the replying Stations (here as 1-6), First to Last, and keep them in that order.
*** Here is my main issue: MC sees and enumerates the Stations in the order they contact it, and NOT in the numerical hierarchy set in Station software, info which is sent by the Stations when they call in as part of the Header data.

Main parameters for the Stations listed are recorded to EEPROM via input to MC:
Course is now set up.

MC receives input for User 1:
Sequence of Stations Sequential or Random? :S
User ability? Level 1-5 :3
and so on.

MC told to commence for User 1

MC → S1: Go with parameters A, B, C, D for User 1, L3.

S1 commences as instructed, and receives input from User; S1 → MC: results of interaction.

MC saves results from S1 and iterates Station number

MC → S2: rinse, repeat until S[end] for User 1

Go to User 2.
MC receives input for User 2:
ETC. ETC.

A further wrinkle is that the order of the Stations (in the above example strictly sequential) can be addressed in a random fashion, but the Station known as S1 is still ID1, S4 (in our example) is still ID11.

I hope this is a clear representation of my objective and my problem.

srnet:
My brain hurts, trying to follow this thread I guess.

You did say ‘above’;

So the code now works is the implication, no more problems ?

I am really sorry your brain is hurting. Imagine how mine is feeling!
Yes, I did say above: Above being the code posted above.
And, yes the code is as much of a “short, complete, example that compiles and demonstrates the problem” as I could make it and still get it to demonstrate the problem by doing what I don’t want it to do.

srnet:
So the code now works is the implication, no more problems ?

Not at all - the issue I'm looking for help with still remains to be addressed.

What I'm hoping for is that the Main Console (MC) finds all of the Stations on the net as they respond and join the mesh, and identifies them using their hard coded ID.
The idea is that when MC calls upon S2, only S2 responds.
What appears to actually happen is that the mesh seems to get set up on a first-served, first named basis, allocating an identifier to each Station as they appear to it on the mesh in sequence. So S3 may be allocated as Station 1 and S1 as Station 2 and so on.

Is there a way to cause the MC to id the Stations correctly according to their ID numbers?

OldManNoob:

It would be a big help if you describe the overall system you are trying to implement.

Command unit "Main Console" (Mega) deals with user interface and input of variables and names.

I was hoping for a description like "I'm trying to manage a boarding kennel for dogs" or "I'm trying to keep track of space debris for Nasa" - something that provides a real context for the project.

Rather than expect each node to exist on the Mesh with a hard-coded address, why not leave the mesh to do its own thing and include an identifier in the message so that the receiving node knows which message is intended for it.

Or maybe use an identifier in the message to enable the Master to figure out which mesh address belongs to which physical node.

...R

Hi Robin,
In brief, I’m designing a physical game to try to keep inner-city kids off the streets, with an interactive component.

Robin2:
Or maybe use an identifier in the message to enable the Master to figure out which mesh address belongs to which physical node.

…R

Ok, so the identifier in the message from Sx: uint8_t nodeIdent; // Station identity returned by Station named x where 0< x <25
The actual message returned by the Sx: scorePackage.nodeIdent = nodeID; // Returning confirmation of the identity of this station

In the MC: network.read(header,&scorePackage,sizeof(scorePackage));
Here the value of nodeIdent is correctly read.

My problem is in the setting up of the Station array at boot.
How do I get the MC to read the Stations into the array as S.low to S.high as it populates the array so that

Serial.print("Addressing Station: ");
        Serial.print(maxStations[stationCount+1]);
        payload.maxUp = EEPROM.read(maxStations[stationCount]*2);
        RF24NetworkHeader header(mesh.addrList[stationCount].address, OCT); //Constructing a header
        if(network.write(header, &payload, sizeof(payload)) == 1){
          Serial.println(F("  Send OK"));
          timer = millis();
          hit = false;
          next = false;
        }

writes to the proper Station identified in maxStations[stationCount+1], instead of a randomly(?) selected Station based, I think, upon order of appearance on the mesh?

OldManNoob:
In brief, I’m designing a physical game to try to keep inner-city kids off the streets, with an interactive component.

Could you be a little less brief?

Ok, so the identifier in the message from Sx: uint8_t nodeIdent; // Station identity returned by Station named x where 0< x <25
The actual message returned by the Sx: scorePackage.nodeIdent = nodeID; // Returning confirmation of the identity of this station

That is too cryptic for me. Can you write it in the sort of English that my granny would have understood. I often find with my own projects that taking the trouble to write clearly helps me to figure out a solution.

…R