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

ESP32与Arduino非UART引脚实现小车机器人指令无损通信问询

自定义脉冲通信问题排查与替代方案求助

问题背景

我开发了一台搭载Arduino和电机驱动盾的小车机器人,ESP32接收网络指令后需转发给Arduino,指令包含forward、backward、right、left、autonomous、manual六种。因Arduino的RX/TX引脚被电机驱动盾占用,尝试通过自定义脉冲时长的通信方式(使用cmd_pin与data_validator引脚)实现数据传输:ESP32发送不同时长的高低脉冲,Arduino通过pulseIn检测脉冲时长判断指令。但当前方案无法正常工作,现需排查问题原因,并寻求基于Arduino闲置引脚(2、13、A3、A4、A5)的无损数据传输最优方案。

现有代码

Arduino代码

// 原代码未声明变量,需补充以下内容
#define cmd_pin 2
#define data_validator 13
bool automatic = false;
String command = "";

void setup() {
  Serial.begin(9600);
  Serial.println("started");
  pinMode(cmd_pin, INPUT);
  pinMode(data_validator, OUTPUT);
  digitalWrite(data_validator, HIGH);
}

String check(){
  int serial_data = digitalRead(cmd_pin);
  if(serial_data==LOW){
    delay(10);
    digitalWrite(data_validator, LOW);
    unsigned long high_dur = pulseIn(cmd_pin, HIGH);
    unsigned long low_dur = pulseIn(cmd_pin, LOW);
    Serial.println("HIGH ");
    Serial.println(high_dur);
    Serial.println("low ");
    Serial.println(low_dur);
    if(high_dur<=5000 && low_dur>24000){
      automatic = false;
      return "Forward";
    }
    else if(high_dur<=10000 && low_dur>19000){
      automatic = false;
      return "Backward";
    }
    else if(high_dur<=15000 && low_dur>14000){
      automatic = false;
      return "Turing right";
    }
    else if(high_dur<=20000 && low_dur>9000){
      automatic = false;
      return "Turing left";
    }
    else if(high_dur<=25000 && low_dur>4000){
      automatic = true;
      return "autonomous";
    }
    else if(high_dur<=30000 && low_dur>1900){
      automatic = false;
      return "manual";
    }
    digitalWrite(data_validator, HIGH);
  }
  return ""; // 无匹配指令时返回空字符串
}

void loop(){
  command = check(); 
  Serial.println(command);
  // 以下为指令执行逻辑(原代码注释)
//  if (command=="autonomous")
//    auton();
//  else if (command=="manual")
//    moveStop();
//  else if (command=="Turing left")
//    turnLeft();
//  else if (command=="Turing right")
//    turnRight();
//  else if (command=="Forward")
//    moveForward();
//  else if (command=="Backward")
//    moveBackward();
//  else{
//    if (automatic)
//      auton();
//  }
}

ESP32代码

// 原代码未声明变量,需补充以下内容
#define cmd_pin 2
#define data_validator 13
bool serial_busy = false;

void manual(){
  serial_busy = true;
  digitalWrite(cmd_pin, LOW);
  while (digitalRead(data_validator)!=LOW);
  delay(1);
  digitalWrite(cmd_pin,HIGH);
  delay(30);
  digitalWrite(cmd_pin, LOW);
  delay(1);
  digitalWrite(cmd_pin,HIGH);
  delay(1);
  digitalWrite(cmd_pin,LOW);
  delay(2);
  digitalWrite(cmd_pin,HIGH);
  
  serial_busy = false;
  delay(10);
}

void forward(){
  
  serial_busy = true;
  Serial.println("Forward");
  digitalWrite(cmd_pin, LOW);
  while (digitalRead(data_validator)!=LOW);
  
  delay(1);
  digitalWrite(cmd_pin,HIGH);
  delay(5);
  digitalWrite(cmd_pin, LOW);
  delay(1);
  digitalWrite(cmd_pin,HIGH);
  delay(1);
  digitalWrite(cmd_pin,LOW);
  delay(25);
  digitalWrite(cmd_pin,HIGH);
  
  serial_busy = false;
  delay(10);
}

void backward(){
  
  serial_busy = true;
  Serial.println("Backward");
  digitalWrite(cmd_pin, LOW);
  while (digitalRead(data_validator)!=LOW);
  
  delay(1);
  digitalWrite(cmd_pin,HIGH);
  delay(10);
  digitalWrite(cmd_pin, LOW);
  delay(1);
  digitalWrite(cmd_pin,HIGH);
  delay(1);
  digitalWrite(cmd_pin,LOW);
  delay(20);
  digitalWrite(cmd_pin,HIGH);
  
  serial_busy = false;
  delay(10);
}

void right(){
  
  serial_busy = true;
  Serial.println("Right");
  digitalWrite(cmd_pin, LOW);
  while (digitalRead(data_validator)!=LOW);
  
  delay(1);
  digitalWrite(cmd_pin,HIGH);
  delay(15);
  digitalWrite(cmd_pin, LOW);
  delay(1);
  digitalWrite(cmd_pin,HIGH);
  delay(1);
  digitalWrite(cmd_pin,LOW);
  delay(15);
  digitalWrite(cmd_pin,HIGH);
  
  serial_busy = false;
  delay(10);
}

void left(){
  
  serial_busy = true;
  Serial.println("Left");
  digitalWrite(cmd_pin, LOW);
  while (digitalRead(data_validator)!=LOW);
  
  delay(1);
  digitalWrite(cmd_pin,HIGH);
  delay(20);
  digitalWrite(cmd_pin, LOW);
  delay(1);
  digitalWrite(cmd_pin,HIGH);
  delay(1);
  digitalWrite(cmd_pin,LOW);
  delay(10);
  digitalWrite(cmd_pin,HIGH);
  
  serial_busy = false;
  delay(10);
}
void auton(){
  serial_busy = true;
  Serial.println("going autonomusly");
  digitalWrite(cmd_pin, LOW);
  while (digitalRead(data_validator)!=LOW);
  
  delay(1);
  digitalWrite(cmd_pin,HIGH);
  delay(25);
  digitalWrite(cmd_pin, LOW);
  delay(1);
  digitalWrite(cmd_pin,HIGH);
  delay(1);
  digitalWrite(cmd_pin,LOW);
  delay(5);
  digitalWrite(cmd_pin,HIGH);
  
  serial_busy = false;
  delay(10);
}

void setup(){
  Serial.begin(115200);
  pinMode(cmd_pin, OUTPUT);
  pinMode(data_validator, INPUT);
  digitalWrite(cmd_pin, HIGH);
}
void loop(){
  // 调用指令函数,比如forward()、left()等
}

问题排查

1. 核心时序不匹配

Arduino代码预期的是单一高脉冲+单一低脉冲的组合(比如Forward对应高脉冲≤5000ms、低脉冲>24000ms),但ESP32的指令函数发送的是多段脉冲序列(比如forward函数发送HIGH 5ms→LOW 1ms→HIGH 1ms→LOW 25ms→HIGH),两者的脉冲格式完全不匹配。Arduino的pulseIn会读取第一个高脉冲的时长(5ms),但后续的低脉冲仅1ms,远不满足判断条件中的>24000ms。

2. pulseIn超时问题

pulseIn默认超时时间为1秒(1000000微秒),而Arduino判断逻辑中要求低脉冲时长>24000ms(24秒),远超默认超时时间,pulseIn会直接返回0,导致判断逻辑失效。

3. 同步逻辑漏洞

Arduino的check()函数在匹配到指令后会直接return,跳过末尾的digitalWrite(data_validator, HIGH)操作,导致data_validator一直处于LOW状态,后续ESP32的while (digitalRead(data_validator)!=LOW)会直接通过,无法实现有效同步。

4. 未声明变量

原代码中cmd_pin、data_validator、automatic、command(Arduino端)和cmd_pin、data_validator、serial_busy(ESP32端)均未声明,编译会直接报错。

最优传输方案推荐

结合闲置引脚(2、13、A3、A4、A5),推荐以下两种成熟可靠的方案:

方案1:软件串口(SoftwareSerial)

适用引脚:数字引脚2和13

利用Arduino的SoftwareSerial库模拟串口通信,仅需2根引脚(RX/TX),无需自定义协议,稳定性高。

  • Arduino端实现:
    #include <SoftwareSerial.h>
    SoftwareSerial espSerial(2, 13); // 2=RX, 13=TX
    String command = "";
    
    void setup() {
      Serial.begin(9600);
      espSerial.begin(9600);
    }
    
    void loop() {
      if (espSerial.available()) {
        command = espSerial.readStringUntil('\n');
        // 执行指令逻辑
        Serial.println(command);
      }
    }
    
  • ESP32端实现:
    #define tx_pin 13 // 对应Arduino的RX引脚2
    #define rx_pin 2  // 对应Arduino的TX引脚13
    HardwareSerial espSerial(1); // 用ESP32的硬件串口1,避免占用默认串口
    
    void setup() {
      Serial.begin(115200);
      espSerial.begin(9600, SERIAL_8N1, rx_pin, tx_pin);
    }
    
    void forward() {
      espSerial.println("Forward");
    }
    

方案2:I2C通信

适用引脚:A4(SDA)和A5(SCL)

Arduino的A4/A5是默认I2C引脚,适合主从通信(ESP32为主设备,Arduino为从设备),仅需2根引脚,支持多设备扩展。

  • Arduino端(从设备):
    #include <Wire.h>
    #define SLAVE_ADDRESS 0x08
    String command = "";
    
    void receiveEvent(int howMany) {
      while (Wire.available()) {
        command += (char)Wire.read();
      }
    }
    
    void setup() {
      Wire.begin(SLAVE_ADDRESS);
      Wire.onReceive(receiveEvent);
      Serial.begin(9600);
    }
    
    void loop() {
      if (command.length() > 0) {
        Serial.println(command);
        command = "";
      }
    }
    
  • ESP32端(主设备):
    #include <Wire.h>
    #define SLAVE_ADDRESS 0x08
    
    void setup() {
      Serial.begin(115200);
      Wire.begin(); // ESP32默认用SDA=21, SCL=22,若需改引脚可传参:Wire.begin(sda_pin, scl_pin)
    }
    
    void sendCommand(String cmd) {
      Wire.beginTransmission(SLAVE_ADDRESS);
      Wire.print(cmd);
      Wire.endTransmission();
    }
    
    void forward() {
      sendCommand("Forward");
    }
    

修复现有脉冲方案的建议(可选)

如果坚持使用自定义脉冲,需修正以下几点:

  1. 统一脉冲格式:ESP32每个指令仅发送一段高脉冲+一段低脉冲,比如Forward发送HIGH 5000ms→LOW 25000ms。
  2. 调整pulseIn超时:修改pulseIn调用为pulseIn(cmd_pin, HIGH, 35000000)(35秒超时),确保能读取长脉冲。
  3. 修复同步逻辑:在check()函数的return前添加digitalWrite(data_validator, HIGH);,或者将该语句放在函数末尾,确保无论是否匹配指令都恢复引脚状态。
  4. 补全变量声明:添加所有未定义的引脚宏和变量。

内容的提问来源于stack exchange,提问作者Deekshith Ranga Babu Tirumala

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 15:55:18