Best way to assign multiple Rx addresses dynamically

Hi !

I'm communicating in RF24 from one Tx to multiple Rx, back and forth.

Today I've 10 Rx, but probably more in the future, so to avoid to hardcode each Rx number I've placed a DIP switch on my circuit to define the Rx number.

Actually I use byte[] array to store the addresses, but I would like to know if someone here know a much better and efficient way to assign addresses.

Thanks for suggestions

In my model railway project I create an address as

byte slaveAddress[6] = "Loco0";

and when I want to talk to the different locomotives the code changes the last character with

slaveAddress[4] = locoData.locoID;

where the locoID is one of C, D E or F

You could use a DIP switch to select the character to be inserted into the address

...R

Hi Robin,

In fact it's more or less what I'm doing already.

Rx side, I read from DIP switch and store in byte[] :

byte thisButtonAddress[6] = { 'B','T','N','0','0' };

//Read the button number from the DIP switch
void getButtonNr() {

	uint8_t const pinNrs[] = { 6, 7, 8, 9 };

	for (int i = 0; i < sizeof(pinNrs); ++i) {
		// Setup pin
		pinMode(pinNrs[i], INPUT_PULLUP);

		// Read value
		uint8_t curState = digitalRead(pinNrs[i]) ? 0 : 1;
		buttonNumber += (curState << i);
	}

	byte str[2] = { '0','0' };

	sprintf(str, "%d", buttonNumber);

	if (buttonNumber > 9) {
		thisButtonAddress[3] = str[0];
		thisButtonAddress[4] = str[1];
	}
	else {
		thisButtonAddress[4] = str[0];
	}
}

Tx side, I modify the button address to send the msg to :

byte buttonAddress[5] = { 'B','T','N','0','0' };

//Return the button number address
byte * getButtonAddress(int btn) {

	byte str[2] = { '0','0' };

	sprintf(str, "%d", btn);

	if (btn > 9) {
		buttonAddress[3] = str[0];
		buttonAddress[4] = str[1];
	}
	else {
		buttonAddress[3] = '0';
		buttonAddress[4] = str[0];
	}

	return buttonAddress;
}

What I would like to know is if it exists a better, simplest way to do that.

byte str[2] = { '0','0' };
	sprintf(str, "%d", btn);

The array is too short for any value above 9.

Whandall:

byte str[2] = { '0','0' };
sprintf(str, "%d", btn);


The array is too short for any value above 9.

Hi!

I've tried to set DIP switch to 12 and the number is assigned successfully.

Another approach to assign addresses than using chars ?

Regards

Alkerion:
I've tried to set DIP switch to 12 and the number is assigned successfully.

You are writing beyond the array, that invokes undefined behaviour.

Whandall:
You are writing beyond the array, that invokes undefined behaviour.

This is a good example of where we need to see the complete program.

It may be that the OP is using the poorly named str[] array as holding 2 bytes rather than as a cstring that would require space for a terminating null byte.

...R

Regardless how the generated cstring (by sprintf with closing zero) is used,
the closing zero still overwrites memory, which is rarely a good idea.

Whandall:
You are writing beyond the array, that invokes undefined behaviour.

OK, there is a few things I don't understand here, could you please be a bit more explicit.

How can I write beyound the array ?

For me I've a two elements array so I should be able to store two digits numbers up to 99.

If the function parameter is equals to 12 the str[] should look like that after sprintf() :

byte str[2] = { '1','2' };

No?

Whandall:
Regardless how the generated cstring (by sprintf with closing zero) is used,
the closing zero still overwrites memory, which is rarely a good idea.

What does it mean ?

where is the cstring ?

what's the "closing zero" ?

How should I correctly convert an int to a byte[] ?

Alkerion:
OK, there is a few things I don't understand here, could you please be a bit more explicit.

How can I write beyound the array ?

For me I've a two elements array so I should be able to store two digits numbers up to 99.

If the function parameter is equals to 12 the str[] should look like that after sprintf() :

byte str[2] = { '1','2' };

No?

No, str will contain '1', '2' and '\0' with the zero overwriting whatever there was.
You may not even notice any effect, but it's waiting for something critical getting directly behind it.

And all that is well documented, how C(++) uses char arrays as strings, how sprintf behaves.

function
int sprintf ( char * str, const char * format, ... );

Write formatted data to string
Composes a string with the same text that would be printed if format was used on printf, but instead of being printed, the content is stored as a C string in the buffer pointed by str.

The size of the buffer should be large enough to contain the entire resulting string (see snprintf for a safer version).

A terminating null character is automatically appended after the content.

http://www.cplusplus.com/reference/cstdio/sprintf/

OK,

so a byte array behave the same way than a cstring with the '\0'.

so I should declare my str variable like :

A) byte str[3] = { '0','0' }

or

byte str[3] = { '0','0','\0'}

or

byte str[3] = "0"

I'new to C (you should have noticed), it's quite an interesting, but quite strange language

Alkerion:
byte str[3] = { '0','0' }
or
byte str[3] = { '0','0','\0'}
or
byte str[3] = "0"

The first two create the exact same content, the third differs in the second byte,
but the content of the buffer does not matter, it will be overwitten by sprintf anyway.

You could use
byte str[3] = "15";so you would get a warning if str is not long enough.

Whandall:
No, str will contain '1', '2' and '\0' with the zero overwriting whatever there was.

I remain to be convinced.

I suspect you would not be saying this if I posted this line of code

byte myArray[2] = { '1','2' };

which is the same as

byte myArray[2] = { 49,50 };

You said in Reply #7

Regardless how the generated cstring (by sprintf with closing zero) is used,
the closing zero still overwrites memory, which is rarely a good idea.

I agree that sprintf() expects a closing zero, but AFAIK it does not create one so it won't trash memory.

...R

Robin2:
You said in Reply #7I agree that sprintf() expects a closing zero, but AFAIK it does not create one so it won't trash memory.

I quoted part of the description and a link to the documentation of sprintf in #9. :wink:

A terminating null character is automatically appended after the content.

Whandall:
I quoted part of the description and a link to the documentation of sprintf in #9. :wink:

You did, and I missed it - my apologies. (I foolishly thought sprintf() just sent stuff to be printed).

Now that I have read it I think the real issue with the OP's code is his decision to use sprintf(). Without that there would be no problem.

...R

OK,

Thanks for the explanations, not so simple to code in C when you come from the Java world, quite a lot of differences between these two languages.

I changed (again) my coding on Tx side to prevent memory from playing tricks :

const uint8_t thisMasterAddress[] = "SRV00";
const uint8_t buttonAddress[][6] = { "BTN01","BTN02","BTN03","BTN04","BTN05","BTN06","BTN07","BTN08","BTN09","BTN10" };

void sendToBtn(int id, int c) {

	Data_Sent.msg = c;
	Data_Sent.rnd++;

	bool rslt;

	radio.stopListening();
	radio.flush_tx();

	radio.openWritingPipe(buttonAddress[id-1]);

	rslt = radio.write(&Data_Sent, sizeof(Data_Sent));

	if (rslt) {
		btnConnected[id] = true;
		if (radio.isAckPayloadAvailable()) {
			radio.read(&ackData, sizeof(ackData));
			newData = true;
		}
		else {
			Serial.println("  Acknowledge but no data ");
		}
	}
	else {
		btnConnected[id] = false;
		Serial.println("  Tx failed");
	}

	radio.startListening();
}

I've also remarked that these two functions have been depracated :

void openReadingPipe (uint8_t number, uint64_t address)

void openWritingPipe (uint64_t address)

Replaced by :

void openWritingPipe (const uint8_t *address)

void openReadingPipe (uint8_t number, const uint8_t *address)

On Rx side, is it now OK like that now ?

//Read the button number from the DIP switch
void getButtonNr() {

	for (int i = 0; i < sizeof(DIPSwitchPins); ++i) {
		pinMode(DIPSwitchPins[i], INPUT_PULLUP);
		uint8_t curState = digitalRead(DIPSwitchPins[i]) ? 0 : 1;
		buttonNumber += (curState << i);
	}

	byte str[5];

	sprintf(str, "%d", buttonNumber);

	if (buttonNumber > 9) {
		thisButtonAddress[3] = str[0];
		thisButtonAddress[4] = str[1];
	}
	else {
		thisButtonAddress[4] = str[0];
	}
}

Regards

If you want to use sprintf you can just use one statement.

char butAddr[6];

    sprintf(butAddr, "BTN%02d", i);

the above could still be problematic for values above 99, which can be fixed with

char butAddr[6];

    snprintf(butAddr, 6, "BTN%02d", i);
char butAddr[6];

void setup() {
  Serial.begin(250000);
  for (byte i = 0; i < 16; i++) {
    snprintf(butAddr, 6, "BTN%02d", i);
    Serial.print(F("'"));
    Serial.print(butAddr);
    Serial.println(F("'"));
  }
}
void loop() {}
'BTN00'
'BTN01'
'BTN02'
'BTN03'
'BTN04'
'BTN05'
'BTN06'
'BTN07'
'BTN08'
'BTN09'
'BTN10'
'BTN11'
'BTN12'
'BTN13'
'BTN14'
'BTN15'

I have to say I don't understand why there is any need to use sprintf() or its cousins. To my little brain the system I posted in Reply #1 seems a lot simpler. If there is a need for more than 10 addresses then continue with the printable characters from ASCII 58 to ASCII 126.

...R

Thanks for the advices, my code as now less bugs and is more readable.