many Tx, one Rx

Thinking about developing my temperature monitoring system. I have in mind making a number of Nano/nRF transmitters, with addresses selected by dil switches or jumpers. I think I'd start with perhaps two or three units, to a a maximum of say 15 such units. For the receiver, a similar unit. I do not want alter software as I add in transmitters. My initial thought was to send a message from the receiver (switched to transmit) and then send each receiver data back, separated by say 500mS. This would merely involve a bit of wasted time, having to wait for transmissions that were not coming from transmitters not yet installed, if you get my meaning. Alternatively, I could poll each transmitter, but what happens if not all are present? As you can tell, I know little about this stuff, but is it necessary to even switch the receiver to transmit? Can it be done by sending an ack, which all units can recognise as a start sequence?
If you have any ideas, I may end up begging for code snippets...

Have a look at this Simple nRF24L01+ Tutorial.

It includes an example for a master and 2 slaves that can easily be extended to a larger number of slaves.

Wireless problems can be very difficult to debug so get the wireless part working on its own before you start adding any other features.

The examples are as simple as I could make them and they have worked for other Forum members. If you get stuck it will be easier to help with code that I am familiar with. Start by getting the first example to work

...R

Hi Robin,

I have used your tutorial, made a couple of boards, Tx and Rx, worked fine. Thanks for providing that. I added in the Dallas temperature code, etc. I had a quick look at your code for master and two slaves, but I think I need it for one receiver and two or more transmitters, or have I got it aaf.

(As an aside, having added in the Dallas code, it seems that alternate Tx's fail. I'm thinking it's possibly power supply problem, or can the radio code get interrupted or something?) (Just another dry joint, I think - working fine until next time..)

raymw:
I had a quick look at your code for master and two slaves, but I think I need it for one receiver and two or more transmitters, or have I got it aaf.

My suggested code uses the master to poll the slaves in turn to get data from them. That system avoids the risk of two slaves sending at the same time and causing data corruption. However it is not appropriate for every situation - especially if the slaves are intended to be asleep most of the time to save energy.

If you want the slaves to send the data without being polled then all that is necessary is for each of them to send the data to the master's address with the master listening all the time. Each slave should include a character in its message that identifies the sender so the master can identify where the message came from.

Note that if the slaves are in charge of sending there will be occasions when two (or more) slaves transmit at the same time and both (all) messages get corrupted. You will need to write code to deal with this. One way is to get a slave to wait a short length of time chosen at random on each occasion before trying to re-send a failed message. Choosing the wait at random should ensure that two competing slaves don't try again at the same instant.

...R

Thanks for the explanation, I'll look at it again, in detail this time. (I thought I'd replied earlier, dunno where that went.)

raymw:
I have in mind making a number of Nano/nRF transmitters.............................two or three units, to a a maximum of say 15

I think this might be the sort of thing for the ESP-01 or the like, rather than buying, and powering, all those Nanos. The closer you get to 15, the more likely it is to be a better idea. Depending on what you want to do with the data, you might even find the one Rx is redundant.

My sensors would regularily send the measurements via multicast (without ack) to a predefined pipe.
If a packet gets lost, who cares, it will be repeated.

So anybody can listen and build a list of talking sensors, no polling required.

For simple information distribution like "living room: 22C" I see no benefit in a guaranteed delivery.

Example, same sketch on all nodes, changing the message would allow to distinguish two senders,
but for the simple 2 node test case that is not necessary, as nodes don't see their own packets.

#include <RF24.h>
const byte pinCE = 9;
const byte pinCSN = 10;
const byte functionalAddress[] = "Temps";
const char demoMessage[] = "demo: 22.2C";

RF24 radio(pinCE, pinCSN);

char incomingData[32];

void setup() {
  Serial.begin(250000);
  radio.begin();
  radio.setDataRate(RF24_250KBPS);
  radio.setPALevel(RF24_PA_MAX);
  radio.setAutoAck(true);
  radio.enableDynamicPayloads();
  radio.setRetries(3, 15);
  radio.openWritingPipe(functionalAddress);
  radio.openReadingPipe(1, functionalAddress);
  radio.setAutoAck(1, false);
  radio.startListening();
}

void loop() {
  if (radio.available()) {
    byte len = radio.getDynamicPayloadSize();
    radio.read(incomingData, len);
    Serial.print(F("rx: '"));
    Serial.print(incomingData);
    Serial.println(F("'"));
  }
  static uint32_t lastAnnounce;
  uint32_t now = millis();
  if (now - lastAnnounce >= 500) {
    radio.stopListening();
    radio.write(demoMessage, sizeof(demoMessage), true);
    radio.startListening();
    lastAnnounce = now;
  }
}
rx: 'demo: 22.2C'
rx: 'demo: 22.2C'
rx: 'demo: 22.2C'
rx: 'demo: 22.2C'
rx: 'demo: 22.2C'

There are even some not really necessary configuration calls in this quite minimalistic
two-roles-at-a-time example, they are relicts (or foundation) of parallel ack usage (active and passive).

I've been giving myself a headache in mental arithmetic thinking of multicast. If they send messages at prime number intervals, how often will signals clash? e.g. assuming seconds for point of view of explanation, and low prime numbers, 3 Tx's start at same time, with intervals of 7, 11, 13 the first clash will be at 77 seconds, then 101 seconds then 143 seconds, next at 154 and so on, and at least 5 clean signals between. If we had periods of say 8, 12 and 14, , we'd get a clash at 48 seconds, with 4 clean signals between. Random time periods would be, well, random. Join me down the rabbit hole :wink:

I doubt you will see many destructive collisions with a moderate packet rate and number of nodes.

The sender waits for a free medium before starting to transmit,
the packets are short and don't take much time to transmit.
if you don't need the extra range of the 256KBPS, you can lower the transmit time
by a factor 8 for the data transmission by using 2MBPS.

Test it. You could use a counter in the messages to detect missing packets.
IMHO there are too many unknown or unmeasurable variables in such a complex system
to predict the probability of collisions exactly,
tolerances, environment, drift, temperature, you name it.

raymw:
I've been giving myself a headache in mental arithmetic thinking of multicast.

I don't think there is any value in getting a headache because I don't think the complex maths will answer the question as the individual Arduinos will all run at slightly different speeds.

I agree with @Whandall that collisions will probably be rare. But the question for you is whether a collision matters, and how to deal with it if it does.

...R

But I thought this was 'digital'. It should be precise ;D ;D ;D

raymw:
But I thought this was 'digital'. It should be precise ;D ;D ;D

IT will be.

THEY won't be

:slight_smile:

...R

referring to Robins post #3, all worked well. I'm now modifying the code to allow hardware setting of the addresses (save specifically programming each RX, and sending temperatures whatever. Having fun with characters, strings, etc, getting mixed up with other languages I've used in the past...

raymw:
I'm now modifying the code to allow hardware setting of the addresses

In a slightly different context I am doing that with a series jumpers that can connect any or all of 3 I/O pins to GND (for 8 different addresses). A DIP switch is another alternative

And just remembered that I have another box with a 12 position rotary switch that selects resistors to change the voltage read by an analog input. This one does select different addresses for the nRF24.

...R

On the Nano, the dallas temp sensor uses D2, so I'm using d4,5,6 with input_pullups to read in the ground links. Pity, it could be easier to ground them if d2,d3 not needed. I think i'll hardwire them, not use switches, although i have a few 8 pin dil switches.

What I am trying to do, is send back two 'strings' to the master. the first being the address of the receiver, the second temperature. The maximum length of the strings will be 6 characters. That will leave space for a few more 6 character strings. I've got into a real tangle with characters, strings, and float conversions. As far as I can tell the writeAckPayload needs a single array of characters. I'll put my Rx code below, hopefully, if someone can pick through it, if you can correct the assignments/types whatever, I'd be pleased. Much of the code is related to the Dallas temp sensor, and there is still a number of my serialprint debugs there. Currently line 60 shows where I think I'm stuck at the moment, but most likely other parts concerning the data/rx address is messed up to. The addressing works OK with my also modified Master code (Heavily based on Robin2's example, which originally worked sending two integers, but it does not transfer my ascii data in any recognisable form for me, as i can't even sort out constructing the strings.. I'm not sure if more or less coffee will help, but it is something that is so obvious, as usual, I expect.

// SimpleRxAckPayload- the slave or the receiver

#include <Arduino.h>

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <Wire.h>

#define CE_PIN   9
#define CSN_PIN 10
//define address pins
#define p1 3
#define p2 4
#define p3 5

static byte thisSlaveAddress[5] = {'R', 'x', 'A', 'A', 'B'};

RF24 radio(CE_PIN, CSN_PIN);

// Data wire is plugged into port 2 on the Arduino
//labeled d2 on nano, with 5k pullup Resistor
#define ONE_WIRE_BUS 2
#define led 8 // for nano
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);

char dataReceived[10]; // this must match dataToSend in the TX
static char* ackData[2][6] = {"B", "-888.00"}; // the two values to be sent to the master
bool newData = false;
float tempc = 0;
static char outstr[10];
static char addr ; // for address from reading pins p1,p2,p3
static int adip;
unsigned long now;
unsigned long b4;
unsigned long period = 200;

//==============

void setup() {

  Serial.begin(9600);
  // get address from reading pins
  pinMode(p1, INPUT_PULLUP);
  pinMode(p2, INPUT_PULLUP);
  pinMode(p3, INPUT_PULLUP);
  adip = 0;
  if (p1 == LOW)adip = adip + 1;
  if (p2 == LOW)adip = adip + 2;
  if (p3 == LOW)adip = adip + 4;
  addr = char(adip + 65);
  thisSlaveAddress[4] = addr; //will be A,B,C,D,E,F,G,H
 // ackData[0,1]=thisSlaveAddress[4];
// I'd like to get ackdata[0] to be thisSlaveAddress
  Serial.println("SimpleRxAckPayload Starting");
  Serial.println(thisSlaveAddress[3]);
  Serial.println(thisSlaveAddress[4]);
  radio.begin();
  radio.setDataRate( RF24_250KBPS );
  radio.openReadingPipe(1, thisSlaveAddress);

  radio.enableAckPayload();

  radio.startListening();

  radio.writeAckPayload(1, &ackData, sizeof(ackData)); // pre-load data
}

//==========

void loop() {
  getData();
  showData();
}

//============

void getData() {
  if ( radio.available() ) {
    radio.read( &dataReceived, sizeof(dataReceived) );
    updateReplyData();
    newData = true;

    //turn on led
    digitalWrite(led, HIGH);
    b4 = millis();
    now = millis();
    while (period > (now - b4))
    {
      now = millis();
    }
    digitalWrite(led, LOW);
  }
}

//================

void showData() {
  if (newData == true) {
    Serial.print("Data received ");
    Serial.println(dataReceived);
    Serial.print(" ackPayload sent ");
    Serial.print(ackData[0]);
    Serial.print(", ");
    Serial.println(ackData[1]);
    newData = false;
  }
}

//================

void updateReplyData() {
  sensors.requestTemperatures(); // Send the command to get temperatures
  tempc = sensors.getTempCByIndex(0);
  dtostrf(tempc, 6, 2, outstr); //get temperature to character 6 chars long
  // ackData[1]=outstr;
  Serial.println(outstr);
  for (int j = 0; j < 10; j++) {
    ackData[1, j] = outstr[j];
  }
  radio.writeAckPayload(1, &ackData, sizeof(ackData)); // load the payload for the next time
  Serial.print (outstr);
  Serial.print (ackData[0, 4]);
}

If I understand that correctly you want to send the float tempc back to the master. You can do that simply with

radio.writeAckPayload(1, &tempc, sizeof(tempc));

Of course the master must be expecting to receive a float.

I reckon the nRF24 is easier to use than Serial.

...R

I want to send a couple of fixed length strings or character equivalent, but I'm stuck in converting the values that I have in their different types, and then if the writeAckPayload can send two or mor dimensioned arrays.

I think you need to learn about structs.

You probably know that an array is collection of the same type of items - for example a char array or an int array.

A struct allows you to create a collection of different type of items and treat them separately or as a block.

For example

struct TrainReplyStruct {
	char radioID;
	int battVolts;
	char moving; // M or S  in Auto mode S signals that the move is complete
	byte curLdr;
	char spacer1;
};

then you can create an instance of it with

TrainReplyStruct myStruct;

add values to it like this

myStruct.battVolts = analogRead(batteryPin);

and send it with the nRF24 like this

radio.writeAckPayload(1, &myStruct, sizeof(myStruct));

...R

Thanks Robin. Looks promising. All the examples I found for writeAckPayload, were for sending int's or a single char array, and it took me a while to find a reference to the first digit, usually a 1, and I am quite capable of making the wrong assumptions.. There seem to be very few examples of more intricate code with adequate explanation, and those I find, later on are revised. I was getting a bit fed up with it, almost thinking of doing gardening, but it's getting dark now.