0

I'm making a function to handle a message, then print the message using Serial.println(). I have it working, but ran into an issue I can't explain. The first sample code below works, the second (swapping the order of my function declaration) will compile and load, but causes the Teensy 4.1 to crash. I'm using PlatformIO on VSCode.

Can anyone tell me what is wrong with the second code, and why it will compile without error, but not run?

This works:

#include <Arduino.h>

void LogMsg(const char *msg){
  Serial.println(msg);
}
void LogMsg(String s){ LogMsg(s.c_str()); }

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  Serial.println("reset");
}

void loop() {
  // put your main code here, to run repeatedly:

  String str3 = "testing string cat ";
  uint32_t var = 12345;
  LogMsg(str3 + var);

  delay(500);
}

This compiles, loads, but crashes, causing continuous resets:

#include <Arduino.h>

void LogMsg(String s){ LogMsg(s.c_str()); }   // <-- swapped order
void LogMsg(const char *msg){                 // <-- swapped order
  Serial.println(msg);
}

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  Serial.println("reset");
}

void loop() {
  // put your main code here, to run repeatedly:

  String str3 = "testing string cat ";
  uint32_t var = 12345;
  LogMsg(str3 + var);

  delay(500);
}

Edit: The definition of void LogMsg(String s) was changed to reflect error in original and the simplification suggested by @hcheung. Behavior remains the same. The first instance works, the second crashes.

  • You don't really need two functions, you can call LogMsg() with `LogMsg(String.c_str());`. Or if you really need to have two overloaded functions, it will be simpler and make less error with `void LogMsg(String s) { LogMsg(s.c_str(); }`. – hcheung Nov 02 '21 at 01:55
  • BTW, the reason it crash because you allocation for `buf[n]` is less than the String object you trying to copy into. – hcheung Nov 02 '21 at 01:59
  • @hcheung - thanks for the simplification. However, the problem persists with your change. When ```void LogMsg(String s){ LogMsg(s.c_str()); }``` is defined first, the program crashes, when defined second, it works. Any thoughts why? – jmkdouglas Nov 02 '21 at 21:21
  • Hmm, this seems to be something to do with platformIO, it does not crash and perform correctly on Arduino IDE, but not work correctly when I run it on platformIO (with Atom). I don't know the reason, but you could make it functions correctly by changing the function to `LogMsg(String s) { Serial.println(s.c_str()); }` (i.e. not calling the other overloaded function). – hcheung Nov 03 '21 at 02:58
  • @Juraj, `String.toCharArray()` has the classic "short-by-1" problem, if the `n = String.length()`, it requires to pass-in n+1 in order to get the full string `String.toCharArray(buf, String.length()+1)`, otherwise it will be shorten by one character, what this means is that the buf needs to be `char buf[n+2]`. – hcheung Nov 03 '21 at 03:18

1 Answers1

0

C strings are terminated with '\0'. So toCharArray() will append a null character to your Ardunio String. Otherwise you would have to provide a length with the char pointer everytime you want to use that string.

Your char array must be big enough to fit this extra character or you will cause an access violation if toCharArray does not throw an exception first.

Piglet
  • 27,501
  • 3
  • 20
  • 43
  • Thanks for the correction. However, fixing char buf[n] does not change the behavior. In the first code, everything works, in the second it crashes, with the only change being the order of defining LogMsg(). Same behavior with char buf[n+1]. – jmkdouglas Nov 02 '21 at 21:16