0

TLDR: I am getting consistent freezes when printing floats to a String via arduinos String class. Previously I was getting the same freeze with sprintf and %f. I've solved the problem using the PString class, but I would like to understand the issue.

FULL STORY: I have a rather large c++ codebase for arduino SAMD architecture (a MKRZero), that started freezing recently on a line of code I hadn't touched in a long time. This was a call to sprintf with %f, which was oddly working as expected previously. After some research on SO and google, I realized that float formatting via sprintf isn't supported on Arduino, and after ruling out dtostr (due to not being on AVR), I tried with Arduinos String class. This temporarily solved the problem, but the freeze resurfaced after testing the system with some different external periphicals (I2C slaves that can be connected and disconnected). So the exact same code, but some differences in the parts of it being exersized due to the different periphical.

The codebase is quite large (several 1000s lines), and I haven't been able to reproduce with a simple example. So unfortunately without much context, these are the lines that fail:

for (int fieldIndex = 0; fieldIndex < totalFullDataPoints; fieldIndex++) {
      char buffer[14];
      Serial.println("foo");
      // String floatString = String((float)data[fieldIndex], 2); // causes system to freeze
      // String floatString = String((float)1024.46, 2); // causes system to freeze
      String floatString = String((float)1024.46); // causes system to freeze      
      // String floatString = String("1024.46"); // works
      Serial.println("bar"); // freezes before this
}

The bug is extremely unstable in that I can cause it to not trigger by modifying unrelated stuff other places in the code or disconnecting a sensor (I2C slave) from my arduino. But when it's present it's consistent in that it happens every run. I even had a version of my code that worked - but removing there three lines would cause it to freeze again:

String floatString = "14123.123";
Serial.println("Float String: ");
Serial.println(floatString);

I'm quite certain it's not a memory problem, and as far as I can tell it's not a case of pointers or non-terminated strings exploding.

I ended up using PStrings (https://github.com/boseji/PString-Arduino-lib) due to this post https://forum.arduino.cc/t/use-pstring-to-avoid-crashes-due-to-string-sprintf-or-dtostrf-float-issues/230946 but I'm frustrated and curious as to why it freezes in such a seemingly random fashion when creating floats via String is supposed to be supported.

kagama
  • 92
  • 1
  • 7
  • 2
    You have classic symptoms of a memory corruption problem, such as pointers or non-terminated strings exploding. – user253751 Apr 13 '21 at 12:49
  • What happens if you use `1024.46f` in place of a forced cast to a `float`? Or using the `double` value? – Adrian Mole Apr 13 '21 at 12:51
  • Are you sure you have sufficient stack space available? – tofro Apr 13 '21 at 12:52
  • @AdrianMole Tried both of these - still freezes – kagama Apr 13 '21 at 13:34
  • @tofro When building with PIO, I'm getting RAM: 16.9% and Flash: 36.2%, so I *think* I should have sufficient. Another indicator of this being unrelated to stack space is that it either triggers very early or not at all – kagama Apr 13 '21 at 13:34
  • @user253751 Cheers, I was thinking it might be a different cause since the freeze seem to consistently happen in relation to trying to print float to strings in a certain place in the code.. But if this a classic symptom of these problems, it seems likely... So I'll probably just have to go digging for where I've screwed things up then :) – kagama Apr 13 '21 at 13:36
  • @kagama indeed. Check all your code that uses pointers as it might be overwriting some random memory. On a PC there's a lot of memory that you aren't using and might not notice if you overwrite... microcontrollers have less memory so there's a higher chance of hitting something important. – user253751 Apr 13 '21 at 14:33
  • In about 15 years of embedded design with Atmel parts, I have never had intermittent problems that weren't due to board fab issues. Millions of line of code. Also, I have never used the crappy and bloated String library, nor any other library. Take from that what you will. Examine your assembly code. – TomServo Apr 14 '21 at 02:36
  • *The codebase is quite large (several 1000s lines), and I haven't been able to reproduce with a simple example*. I suspect something else in your code may be causing some corruption which causes the snippet of code you are showing, which is probably in and of itself proper logic, to faill – lurker Apr 14 '21 at 11:12

1 Answers1

0

After a lot of debugging, it seems that the issue indeed does have to do with ability of Arduino to print floats to string, and not with pointers or non-terminated string issues in my code.

It seems to be stalling in the dtostrf() call of the String constructor:

// ~/.platformio/packages/framework-arduino-samd/cores/arduino/api/String.cpp
String::String(float value, unsigned char decimalPlaces)
{
    static size_t const FLOAT_BUF_SIZE = FLT_MAX_10_EXP + FLT_MAX_DECIMAL_PLACES + 1 /* '-' */ + 1 /* '.' */ + 1 /* '\0' */;
    init();
    char buf[FLOAT_BUF_SIZE];
    decimalPlaces = min(decimalPlaces, FLT_MAX_DECIMAL_PLACES);
    *this = dtostrf(value, (decimalPlaces + 2), decimalPlaces, buf);     // <-- HERE
}

and it seems to be related to the assembler call to _print_float within the dtostrf() function:

// ~/.platformio/packages/framework-arduino-samd/cores/arduino/api/deprecated-avr-comp/avr/dtostrf.c.impl
char *dtostrf (double val, signed char width, unsigned char prec, char *sout) {
  asm(".global _printf_float");   // If this line is uncommented, the stall wont happen
  char fmt[20];
  sprintf(fmt, "%%%d.%df", width, prec);
  sprintf(sout, fmt, val);   // If the above line is not commented out, the system will freeze here
  return sout;
}

I realize this might be a pretty unsatisfactory answer for anyone coming across this thread... But for what it's worth, the solution for us will be to use PString (https://github.com/boseji/PString-Arduino-lib) as they at least seem stable so far.

I will follow up on this thread if the issue ever surfaces again despite using PStrings

kagama
  • 92
  • 1
  • 7