Arduino 非常随机地冻结通过 String 和 sprintf 将浮点数打印为字符串

Arduino very randomly freezes on printing float to string via String and sprintf

TLDR:当通过 arduinos String class 将浮点数打印到字符串时,我一直卡住。以前我在使用 sprintf 和 %f 时遇到了同样的问题。我已经使用 PString class 解决了这个问题,但我想了解这个问题。

完整故事:我有一个相当大的 arduino SAMD 架构的 c++ 代码库(一个 MKRZero),它最近开始冻结在我很长时间没有接触过的一行代码上。这是使用 %fsprintf 的调用,奇怪的是它按预期正常工作。在对 SO 和 google 进行一些研究后,我意识到 Arduino 不支持通过 sprintf 进行浮点格式化,并且在排除 dtostr 之后(由于不在 AVR 上),我尝试使用 Arduinos String class。这暂时解决了问题,但是在使用一些不同的外部外围设备(可以连接和断开连接的 I2C 从属设备)测试系统后,冻结重新浮出水面。所以完全相同的代码,但由于不同的外围设备,它被执行的部分有所不同。

代码库非常大(几千行),我无法用一个简单的例子重现。不幸的是,没有太多上下文,这些是失败的行:

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
}

这个 bug 非常不稳定,因为我可以通过修改代码中其他地方不相关的东西或从我的 arduino 断开传感器(I2C 从属)来使其不触发。但是当它出现时,它是一致的,因为它每隔 运行 发生一次。我什至有一个可以运行的代码版本 - 但是 删除 三行会导致它再次冻结:

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

我很确定这不是内存问题,据我所知这不是指针或未终止字符串爆炸的情况。

我最终使用了 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 但我很沮丧和好奇为什么它在通过 String 创建浮动时以这种看似随机的方式冻结待支持。

经过大量调试,问题似乎确实与 Arduino 将浮点数打印为字符串的能力有关,而不是我的代码中的指针或未终止字符串问题。

它似乎在 String 构造函数的 dtostrf() 调用中停滞不前:

// ~/.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 /* '[=10=]' */;
    init();
    char buf[FLOAT_BUF_SIZE];
    decimalPlaces = min(decimalPlaces, FLT_MAX_DECIMAL_PLACES);
    *this = dtostrf(value, (decimalPlaces + 2), decimalPlaces, buf);     // <-- HERE
}

这似乎与 dtostrf() 函数中对 _print_float 的汇编程序调用有关:

// ~/.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;
}

我意识到对于遇到此线程的任何人来说,这可能是一个非常不令人满意的答案...但是对于它的价值,我们的解决方案是使用 PString (https://github.com/boseji/PString-Arduino-lib) 作为他们至少到目前为止看起来很稳定。

尽管使用 PStrings

,如果问题再次出现,我将跟进此线程