TeraTerm发送至Arduino的初始命令含乱码,如何排查解决?
Arduino Telnet命令乱码问题排查与解决
问题描述
我编写了Arduino Uno程序,通过Telnet接收TeraTerm控制台发送的字符串命令,用于设置I/O扩展器,但遇到以下问题:
- 首次发送的4-5条命令,有效字符串前总会出现乱码,导致命令无法执行,后续发送的命令恢复正常;
- 设备闲置约20分钟后,发送的第一条命令同样会出现乱码,后续命令正常。
程序最终需对接LabVIEW,目前无法测试相关环节。我是Arduino及编程新手,希望了解问题排查方法、解决措施,以及相关学习资源。
曾尝试使用client.flush()但无效果,以下是出现问题的精简版代码:
#include <SPI.h> #include <Ethernet.h> // Arduino的MAC地址 byte mac[] = { 0xA8, 0x61, 0x0A, 0xAE, 0x6D, 0x65 }; IPAddress ip(192,168,206,225); IPAddress gateway(192,168,204,1); IPAddress subnet(255, 255, 205, 20); // Telnet标准端口23 EthernetServer server(23); boolean alreadyConnected = false; String commandString = ""; void setup() { delay(200); Serial.begin(9600); // 服务器初始化 Ethernet.begin(mac, ip, gateway, subnet); server.begin(); while (!Serial){;} Serial.print("- Arduino's IP address : "); Serial.println(Ethernet.localIP()); Serial.print("- Gateway's IP address : "); Serial.println(Ethernet.gatewayIP()); Serial.print("- Network's subnet mask : "); Serial.println(Ethernet.subnetMask()); Serial.print("- DNS server's IP address: "); Serial.println(Ethernet.dnsServerIP()); // 重复初始化(问题点) if (Ethernet.begin(mac) == 0) { Serial.println("Keine IP-Adresse erhalten."); // 检查以太网硬件 if (Ethernet.hardwareStatus() == EthernetNoHardware) Serial.println("Ethernet Schild nicht gefunden."); // 检查以太网电缆 if (Ethernet.linkStatus() == LinkOFF) Serial.println("Ethernet Kabel nicht verbunden."); } Serial.print("- Arduino's IP address : "); Serial.println(Ethernet.localIP()); Serial.print("- Gateway's IP address : "); Serial.println(Ethernet.gatewayIP()); Serial.print("- Network's subnet mask : "); Serial.println(Ethernet.subnetMask()); Serial.print("- DNS server's IP address: "); Serial.println(Ethernet.dnsServerIP()); } void loop() { EthernetClient client = server.available(); if (client) { if (!alreadyConnected) { // 尝试清空缓冲区(方法错误) client.flush(); commandString = ""; // 清空命令字符串 server.print("--> Please type your command and hit Return..."); alreadyConnected = true; } if(client.available()) { char newChar = client.read(); // 读取客户端传入的字节 if (newChar == '\r') // 收到回车时处理命令 { server.print("Received this command: "); server.println(commandString); Serial.print("Recieved this command:"); Serial.println(commandString); commandString += '\r'; commandString = ""; } else { commandString += newChar; } } } }
注:串口监视器输出显示乱码,但TeraTerm客户端无此现象。
排查方法
- 缓冲区操作误区:
client.flush()的作用是等待输出缓冲区的字节发送完成,而非清空输入缓冲区,这是你之前尝试无效的核心原因。 - Telnet连接握手数据:TeraTerm建立Telnet连接时,会发送终端协商的控制字符(如0xFF开头的Telnet命令),这些字符被程序当成命令的一部分,导致乱码。
- 连接状态未重置:当前代码未处理客户端断开连接的情况,
alreadyConnected变量不会自动重置,导致重连时逻辑出错。 - 重复网络初始化:setup中两次调用
Ethernet.begin(),第二次会触发DHCP申请,干扰静态IP配置,导致网络状态不稳定。
解决措施
正确清空输入缓冲区
在连接建立时,循环读取并丢弃所有缓冲区残留字节,替换原来的client.flush():while (client.available() > 0) { client.read(); // 读取所有残留字节并丢弃 }过滤Telnet控制字符
读取字符时只保留ASCII可打印字符(0x20-0x7E)和回车/换行,过滤控制码:char newChar = client.read(); // 只保留有效字符 if ((newChar >= 0x20 && newChar <= 0x7E) || newChar == '\r' || newChar == '\n') { if (newChar == '\r') { // 命令处理逻辑 server.print("Received this command: "); server.println(commandString); Serial.print("Recieved this command:"); Serial.println(commandString); commandString = ""; // 直接清空,无需添加'\r' } else { commandString += newChar; } }重置连接状态
在loop中检查客户端连接状态,断开时重置alreadyConnected:if (client) { if (!client.connected()) { alreadyConnected = false; client.stop(); } // 原有连接处理逻辑... }修正网络初始化
删除setup中第二次Ethernet.begin(mac)调用,只保留静态IP初始化:// 保留这一行即可 Ethernet.begin(mac, ip, gateway, subnet); server.begin(); // 移除后面的if (Ethernet.begin(mac) == 0) 代码块
修改后的完整代码示例
#include <SPI.h> #include <Ethernet.h> // Arduino的MAC地址 byte mac[] = { 0xA8, 0x61, 0x0A, 0xAE, 0x6D, 0x65 }; IPAddress ip(192,168,206,225); IPAddress gateway(192,168,204,1); IPAddress subnet(255, 255, 205, 20); // Telnet标准端口23 EthernetServer server(23); boolean alreadyConnected = false; String commandString = ""; void setup() { delay(200); Serial.begin(9600); // 服务器初始化(仅一次静态IP配置) Ethernet.begin(mac, ip, gateway, subnet); server.begin(); while (!Serial){;} Serial.print("- Arduino's IP address : "); Serial.println(Ethernet.localIP()); Serial.print("- Gateway's IP address : "); Serial.println(Ethernet.gatewayIP()); Serial.print("- Network's subnet mask : "); Serial.println(Ethernet.subnetMask()); Serial.print("- DNS server's IP address: "); Serial.println(Ethernet.dnsServerIP()); // 检查硬件与连接状态 if (Ethernet.hardwareStatus() == EthernetNoHardware) Serial.println("Ethernet Schild nicht gefunden."); if (Ethernet.linkStatus() == LinkOFF) Serial.println("Ethernet Kabel nicht verbunden."); } void loop() { EthernetClient client = server.available(); if (client) { // 处理断开连接的情况 if (!client.connected()) { alreadyConnected = false; client.stop(); return; } if (!alreadyConnected) { // 清空输入缓冲区所有残留字节 while (client.available() > 0) { client.read(); } commandString = ""; server.print("--> Please type your command and hit Return..."); alreadyConnected = true; } if(client.available()) { char newChar = client.read(); // 过滤无效控制字符 if ((newChar >= 0x20 && newChar <= 0x7E) || newChar == '\r' || newChar == '\n') { if (newChar == '\r') { server.print("Received this command: "); server.println(commandString); Serial.print("Recieved this command:"); Serial.println(commandString); commandString = ""; } else { commandString += newChar; } } } } }
新手学习指南
- Arduino核心基础:优先掌握Arduino的串口通信、变量类型、循环逻辑,重点理解输入输出缓冲区的区别和操作方法。
- Ethernet库使用:仔细学习EthernetServer和EthernetClient的API,明确
available()、read()、connected()、stop()等函数的作用,避免混淆输入输出操作。 - Telnet协议基础:了解Telnet连接时的握手流程,熟悉常见的Telnet控制字符(如IAC命令),知道哪些字节属于协议控制而非业务数据。
- 调试技巧:用串口监视器打印每个读取到的字节的十六进制值(
Serial.print(newChar, HEX)),直观看到乱码对应的实际字节,快速定位问题。
内容的提问来源于stack exchange,提问作者user23736704
相关产品推荐
相关产品推荐

