strlen and sprintf behavior - AVR vs ARM Moteinos

Started by alexelite, January 10, 2021, 04:59:17 PM

alexelite

Hello to all and a Happy New Year,

I started testing a Moteino M0 for the gateway and I noticed it behaved differently than the classical Moteino, and I am now trying to understand why. I posted in Moteino section because on this board I don't get it why it works the way it does. Later edit, you can skip to the Later info for a summary of the problem.

I use the default PiGateway sketch from github and only change the Radio settings, no other change in code.
After the Moteino Gateway finishes booting, I send "UPTIME" and get a correct response "UPTIME:1234"
On the Moteino M0 Gateway the same "UPTIME" command returns the "UPTIME:1234" and a new line with an error " [ 0 ]  ⸮*:INV:ID-OUT-OF-RANGE".

My understanding of the processCommand function is this:
Radio data is tokenized with strtok
ptr = strtok(data, ":");

and because data does not contain ':' it "returns" the whole data
Then the function checks if data is equal to a bunch of command, it finds it as UPTIME and prints the time.
After this it checks
  if(ptr != NULL) {                  // delimiter found, valid command
    sprintf(dataPart, "%s", ptr);

The comment says "delimiter found, valid command" , but ptr is not null even if token was not found, because strtok returns null only if the data is null, in the first run. And data is not null.
sprintf returns what should be now the first token, but it is the actual message.
    targetId = atoi(dataPart);       // attempt to extract nodeID part
    ptr = strtok(NULL, "");          // get command part to the end of the string
    sprintf(dataPart, "%s", ptr);
    if (strlen(dataPart) == 0) {
     return;
    }

targetId becomes 0 because there is no int to get
strtok is run again on the rest of 'data', which is null, and ptr is null now.
sprintf then buffers to dataPart a string that is null actualy
Now comes the part where diferences appear.
On Moteino strlen(dataPart) is 0 and function returns, no error info.
On Moteino M0 strlen(dataPart) is 2 and function keeps going, end the error info is printed.
My dilemma increases now, because if I try to Serial.print before sprintf, strlen(dataPart) becomes 1 on Moteino.

Now I try:
sprintf(dataPart, "%s", NULL);

strlen(dataPart) still 0, but with any Serial.print before it becomes 1,
And on Moteino M0 the above function always returns 2.
I imagine the difference in architectures is responsible for 1 vs 2 value, but why the inconsistency on AVR.

Later info:
After finishing writing this post I simplified the code even more and came up with this:
void setup() {
  char dataPart[20];
  Serial.begin(115200);
  delay(2000);
  sprintf(dataPart, "%s", NULL);
  Serial.println(strlen(dataPart));
  sprintf(dataPart, "%s", NULL);
  Serial.println(strlen(dataPart));
}

void loop() {
}

On Moteino the output is
0
1

On Moteino M0 the output is
2
2

Same dilemma as above: I imagine the difference in architectures is responsible for 1 vs 2 value, but why the inconsistency on AVR.
Please, can somebody enlighten me? Thanks

Felix

I suspect a compiler difference perhaps, and having to do with the specification of the sprintf and strlen functions:

Quotesprintf Return Value:
On success, the total number of characters written is returned. This count does not include the additional null-character automatically appended at the end of the string.
On failure, a negative number is returned.

Here's my experience: I use these functions extensively in parsing and manipulating buffers on AVRs in various applications. They behave as expected and I don't have any issues. I suggest a careful reading of their specification, and especially pay attention to things like initialization and function behavior in edge cases/undefined behavior. C++ is powerful but the say is "with power (of pointers and raw memory access) comes responsibility".

alexelite

Hello Felix,

Thanks for the fast reply.
I read more about sprintf and having a NULL parameter seams to be an undefined area, and should be avoided.
Testing the code and variations on different online c compilers and vscode, yields different results. Some work with own implementation(return different str length), others return segmentation fail.

You use a M0 as gateway? Do you get the same undesired message with the PiGateway sketch? If not that would be even weirder.

Thanks again.
Alex


Felix

I am actually using AVRs and this new M4 based RFGateway board in photo below, not M0 or I don't recall.
I did not see the INV message before.
Note that the gcc compilers between Arduino versions are also changing. I am still on IDE 1.8.6 for various reasons for my main development.
So when comparing apples to apples, they have to also be of the same exact type of apple ;)
I may need to look into this further, I can try latest IDE if I do.


alexelite

#4
Nice looking board.

I am on 1.8.13, but I will try to install 1.8.6 for a test.
Thank you again.

Later edit:
same result with 1.8.6 from adruino.cc
UPTIME:45085
[0] ⸮*:INV:ID-OUT-OF-RANGE
FREERAM:27947:16384
[0] ⸮*:INV:ID-OUT-OF-RANGE
SYSFREQ:868000000
[0] ⸮*:INV:ID-OUT-OF-RANGE


Later later edit:
I made an error earlier, used only the old IDE, but gcc compiler remained the same  :(.
I switched to platformio in vscode and, lo and behold, worked like a charm with your original code.

Felix

Quote from: alexelite on January 12, 2021, 03:37:14 AM
I made an error earlier, used only the old IDE, but gcc compiler remained the same  :(.
I switched to platformio in vscode and, lo and behold, worked like a charm with your original code.
Interesting so it must be the compiler then, or difference in compiler optimizations and build flags, which could also play a part in this.

cc

I recently purchased an RFGateway (M4) also and am unable to use it with Arduino IDE 1.8.13. I also tried Arduino IDE 1.8.6 but it didn't work either. Long ago I ran into a problem that was solved by reverting to a previous version of the Arduino Board definition in the Boards Manager. I tried that but it did not change the outcome. Are there any plans on addressing this issue?

Felix

cc - have you installed the Moteino SAMD boards definition (v1.6.2 latest at this moment) as explained on this page?
You also need Arduino SAMD package (1.8.11 latest at this time).