HOWTO · Arduino

Arduino printf 函数

通过使用 sprintf()函数和 Serial.print()函数,可以获得与 printf()相同的输出。

本页内容

标准 Arduino 编程环境并未提供可将内容写入串口监视器的通用 printf() 调用。某些开发板核心会加入自己的 Serial.printf() 方法,因此请查阅所选开发板的文档。若要编写可移植的草图,可以先用 sprintf() 将内容格式化到字符缓冲区,再通过 Serial.print() 或 Serial.println() 发送。如果不需要格式字符串,也可以直接用多次串口调用拼出相同的消息。

Arduino 是否提供 printf()?

Arduino 各开发板并没有统一保证提供 printf() 接口。C 库的 printf() 通常会将内容写入标准输出流,而微控制器环境未必会把该流连接到串口。某些核心可能提供专属的 Serial.printf(),但是否可用以及支持哪些格式说明符取决于目标平台。下面的草图使用 setup() 和 loop(),并通过 Serial 写出内容;一个简单的主机端串口替身可让相同示例在没有 Arduino 开发板的情况下运行。在开发板上,Arduino 核心会提供 Serial 和草图的生命周期。

使用 sprintf() 格式化文本并通过串口发送

当消息需要占位符或填充时,可以使用 sprintf()。它会将完整文本写入字符数组,然后由 Serial.println() 发送文本并换行。此草图使用 snprintf() 将数组容量传给格式化函数,并在发送前检查完整消息是否放得下:

#include <cstdio>

#ifdef ARDUINO
#include <Arduino.h>
#else
class HostSerial {
 public:
  void begin(unsigned long) {}
  void println(const char* text) { std::puts(text); }
};
HostSerial Serial;
#endif

void setup() {
  Serial.begin(9600);
  char buffer[32];
  const int value = 42;
  const int length = std::snprintf(buffer, sizeof(buffer), "The answer is: %d", value);
  if (length >= 0 && static_cast<unsigned int>(length) < sizeof(buffer)) {
    Serial.println(buffer);
  }
}

void loop() {}

#ifndef ARDUINO
int main() {
  setup();
  return 0;
}
#endif
The answer is: 42

snprintf() 返回原本会写入的字符数,不包括结尾的空字符。如果该数量大于或等于数组容量,草图就不会发送不完整的消息。请确认所选开发板的 C 库按预期提供 snprintf();不同开发板和核心所提供的格式化函数及其行为可能有所不同。

使用 Serial.print() 拼出输出

对于简单的标签和值,直接调用串口方法可以省去临时格式化缓冲区。Serial.print() 会继续在同一行输出;Serial.println() 则会结束当前行。此草图通过多次串口调用拼出相同的一行内容:

#include <cstdio>

#ifdef ARDUINO
#include <Arduino.h>
#else
class HostSerial {
 public:
  void begin(unsigned long) {}
  void print(const char* text) { std::printf("%s", text); }
  void print(int value) { std::printf("%d", value); }
  void print(double value, int digits) { std::printf("%.*f", digits, value); }
  void println(const char* text) { std::printf("%s\n", text); }
};
HostSerial Serial;
#endif

void setup() {
  Serial.begin(9600);
  const int temperature = 25;
  const double humidity = 60.5;
  Serial.print("Temperature: ");
  Serial.print(temperature);
  Serial.print(" C, Humidity: ");
  Serial.print(humidity, 1);
  Serial.println("%");
}

void loop() {}

#ifndef ARDUINO
int main() {
  setup();
  return 0;
}
#endif
Temperature: 25 C, Humidity: 60.5%

主机端替身会模拟这里用到的调用,因此无需硬件也能展示草图生成的文本行;它并不是 Arduino 核心。在开发板上,Serial.print(humidity, 1) 会要求小数点后显示一位数字,而具体支持的格式取决于开发板的 Serial 实现。

处理浮点数值和缓冲区限制

Arduino 各核心通过 sprintf() 格式化浮点数的行为并不一致:有些嵌入式 C 库会省略格式化输入输出中的浮点转换,以节省程序存储空间。依赖 %f 之前,请先确认目标核心是否支持。若核心采用常见的 Arduino Print 实现,也可以使用 Serial.print(value, digits) 这种形式。

格式化后的内容必须能放入字符数组,并为结尾的 \0 留出空间。在目标环境提供 snprintf() 时,请传入数组的实际容量,并在使用结果前检查返回值。如果格式化后的文本长于数组容量,请扩大缓冲区或缩短消息。较大的缓冲区会占用更多 RAM,而许多开发板的 RAM 都有限。

选择合适的方法

如果使用单个格式字符串能让结构化消息更易读,可以选择 sprintf() 或 snprintf(),再用 Serial.println() 发送结果。如果只需拼接少量标签和值,则可以直接调用 Serial.print()。这两种方法都会通过串口发送文本,但不能保证所有开发板都支持相同的格式。请根据要编译的开发板,确认核心专属方法和浮点数行为。