You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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客户端无此现象。

排查方法

  1. 缓冲区操作误区:client.flush()的作用是等待输出缓冲区的字节发送完成,而非清空输入缓冲区,这是你之前尝试无效的核心原因。
  2. Telnet连接握手数据:TeraTerm建立Telnet连接时,会发送终端协商的控制字符(如0xFF开头的Telnet命令),这些字符被程序当成命令的一部分,导致乱码。
  3. 连接状态未重置:当前代码未处理客户端断开连接的情况,alreadyConnected变量不会自动重置,导致重连时逻辑出错。
  4. 重复网络初始化:setup中两次调用Ethernet.begin(),第二次会触发DHCP申请,干扰静态IP配置,导致网络状态不稳定。

解决措施

  1. 正确清空输入缓冲区
    在连接建立时,循环读取并丢弃所有缓冲区残留字节,替换原来的client.flush():

    while (client.available() > 0) {
      client.read(); // 读取所有残留字节并丢弃
    }
    
  2. 过滤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;
      }
    }
    
  3. 重置连接状态
    在loop中检查客户端连接状态,断开时重置alreadyConnected:

    if (client) {
      if (!client.connected()) {
        alreadyConnected = false;
        client.stop();
      }
      // 原有连接处理逻辑...
    }
    
  4. 修正网络初始化
    删除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;
        }
      }
    }
  }
}

新手学习指南

  1. Arduino核心基础:优先掌握Arduino的串口通信、变量类型、循环逻辑,重点理解输入输出缓冲区的区别和操作方法。
  2. Ethernet库使用:仔细学习EthernetServer和EthernetClient的API,明确available()、read()、connected()、stop()等函数的作用,避免混淆输入输出操作。
  3. Telnet协议基础:了解Telnet连接时的握手流程,熟悉常见的Telnet控制字符(如IAC命令),知道哪些字节属于协议控制而非业务数据。
  4. 调试技巧:用串口监视器打印每个读取到的字节的十六进制值(Serial.print(newChar, HEX)),直观看到乱码对应的实际字节,快速定位问题。

内容的提问来源于stack exchange,提问作者user23736704

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 17:57:32