Skip to content

Arduino - 字符函数

您的 Arduino 经常需要处理基于文本的数据,无论是来自串口监视器的命令、SD 卡中的配置文件,还是网络请求中的参数。标准 C++ 库 <cctype>(默认已包含)提供了一组高效的函数,用于分类和转换单个字符(char)。

尽管 Arduino 的 String 对象提供了更强大的字符串操作功能,但这些底层函数速度极快且内存效率高,非常适合资源受限的微控制器。

这些函数接受一个字符(作为 int 类型)并返回 true(非零值)或 false(零)。

  • isDigit(c):它是一个数字字符(‘0’-‘9’)吗?
  • isAlpha(c):它是一个字母字符(‘a’-‘z’, ‘A’-‘Z’)吗?
  • isAlphaNumeric(c):它是一个数字或字母吗?
  • isSpace(c):它是一个空白字符(空格、制表符 \t、换行符 \n 等)吗?
  • isPunct(c):它是一个标点符号吗?
  • isUpperCase(c):它是一个大写字母吗?
  • isLowerCase(c):它是一个小写字母吗?
  • toUpperCase(c):将字符转换为其大写形式。
  • toLowerCase(c):将字符转换为其小写形式。

实践示例:一个简单的串口命令解析器

Section titled “实践示例:一个简单的串口命令解析器”

让我们构建一个健壮的命令解析器。我们的 Arduino 将通过串口监视器监听命令。这是一种常见的调试、配置和控制模式。

我们的命令将是:

  • L1:打开 LED
  • L0:关闭 LED
  • B<值>:设置闪烁延迟为 <值> 毫秒(例如 B500)
constexpr int LED_PIN = 13;
int blinkDelay = 500; // 默认闪烁延迟
void setup() {
pinMode(LED_PIN, OUTPUT);
Serial.begin(9600);
printInstructions();
}
void loop() {
// 检查是否收到任何数据
if (Serial.available() > 0) {
// 读取第一个字符(命令)
char command = Serial.read();
command = toUpperCase(command); // 使命令不区分大小写
if (command == 'L') {
parseLedCommand();
} else if (command == 'B') {
parseBlinkCommand();
} else {
// 清除缓冲区中所有剩余的垃圾数据
while(Serial.available() > 0) Serial.read();
Serial.println("错误:未知命令。");
printInstructions();
}
}
}
void parseLedCommand() {
// 稍等片刻,等待下一个字符到来
delay(5);
if (Serial.available() > 0) {
char value = Serial.read();
if (value == '1') {
digitalWrite(LED_PIN, HIGH);
Serial.println("OK: LED 已开启");
} else if (value == '0') {
digitalWrite(LED_PIN, LOW);
Serial.println("OK: LED 已关闭");
} else {
Serial.println("错误:L 的值无效。请使用 L1 或 L0。");
}
}
}
void parseBlinkCommand() {
delay(5);
// 为简单起见,使用 parseInt(),它内部会检查数字。
// 这是一个常见的 Arduino 辅助函数。
long value = Serial.parseInt();
if (value > 0) {
blinkDelay = value;
Serial.print("OK: 闪烁延迟设置为 ");
Serial.print(blinkDelay);
Serial.println(" 毫秒。");
// 使用 isDigit() 手动解析的示例
// String numberStr = "";
// while(Serial.available() > 0) {
// char c = Serial.peek(); // peek() 只是查看而不移除
// if (isDigit(c)) {
// numberStr += Serial.read();
// } else {
// break; // 如果不是数字则停止
// }
// }
// if (numberStr.length() > 0) {
// blinkDelay = numberStr.toInt();
// ...
// }
} else {
Serial.println("错误:闪烁延迟值无效或为零。");
}
}
void printInstructions() {
Serial.println("\n--- 命令菜单 ---");
Serial.println("L1 : 打开 LED");
Serial.println("L0 : 关闭 LED");
Serial.println("B<ms> : 设置闪烁延迟(例如 B250)");
Serial.println("--------------------\n");
}
  • 阻塞与非阻塞:Serial.read() 是非阻塞的,它会立即返回。Serial.parseInt() 是阻塞的,它会等待超时。在设计代码时请注意这种区别。
  • 缓冲区处理:串口缓冲区很小(大多数 Arduino 上为 64 字节)。即使接收到的数据无效,也要始终读取,以防止缓冲区溢出。
  • 命令结束符:在更复杂的协议中,您需要一种方法来判断命令何时结束。一种常见技术是读取直到接收到换行符('\n'),这在您在串口监视器中按下 Enter 键时发送。