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),它最近开始冻结在我很长时间没有接触过的一行代码上。这是使用 %f
对 sprintf
的调用,奇怪的是它按预期正常工作。在对 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);
我很确定这不是内存问题,据我所知这不是指针或未终止字符串爆炸的情况。
我最终使用了 PString
s(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) 作为他们至少到目前为止看起来很稳定。
尽管使用 PString
s
,如果问题再次出现,我将跟进此线程
TLDR:当通过 arduinos String
class 将浮点数打印到字符串时,我一直卡住。以前我在使用 sprintf 和 %f 时遇到了同样的问题。我已经使用 PString class 解决了这个问题,但我想了解这个问题。
完整故事:我有一个相当大的 arduino SAMD 架构的 c++ 代码库(一个 MKRZero),它最近开始冻结在我很长时间没有接触过的一行代码上。这是使用 %f
对 sprintf
的调用,奇怪的是它按预期正常工作。在对 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);
我很确定这不是内存问题,据我所知这不是指针或未终止字符串爆炸的情况。
我最终使用了 PString
s(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) 作为他们至少到目前为止看起来很稳定。
尽管使用 PString
s