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

PlatformIO下ESP32与ESP8266自定义引脚I2C通信故障排查

ESP32与ESP8266 I2C通信故障排查

问题场景

使用板卡

ESP-WROOM-32 38PIN
ESP8266MOD

接线方式

ESP32 G21 -> ESP8266 D2 (and 5 volts with 2k resistor from ESP8266)
ESP32 G22 -> ESP8266 D1 (and 5 volts with 2k resistor from ESP8266)
ESP32 GND -> ESP8266 G 

开发环境配置

从站(ESP8266端)PlatformIO配置(platformio.ini)

[platformio]
default_envs = esp8266dev
data_dir = src/data

[env:esp8266dev]
platform = espressif8266
framework = arduino
board = nodemcuv2
board_build.mcu = esp8266
board_build.partitions = huge_app.csv
upload_protocol = esptool
lib_deps = 
    bblanchon/ArduinoJson @ ^6.19.4
upload_speed = 115200
monitor_speed = 9600
upload_port = COM14
monitor_port = COM14

主站(ESP32端)PlatformIO配置(platformio.ini)

[env:fm-devkit]
platform = espressif32
board = fm-devkit
framework = arduino
; upload_speed = 115200
; monitor_speed = 115200
monitor_speed = 9600
upload_port = COM12
monitor_port = COM12

测试代码

ESP32主站测试代码

#include <Arduino.h>
#include "Wire.h"

// GPIO21 SDA
#define SDA 33

// GPIO22 SLC
#define SCL 36

int x = 0x1234;

void setup()
{
  Serial.begin(9600);

  // Wire.begin(SDA, SCL);
  Wire.begin();
  Wire.setClock(100000);

  // Wire.beginTransmission(0x21); // roll calling for the Slave
  // byte busStatus = Wire.endTransmission();
  // if (busStatus != 0x00)
  // {
  //   Serial.print("I2C Bus communication problem...!" + String(busStatus));
  //   while (1)
  //     ; // wait for ever
  // }
  // Serial.println("Slave found!");
}

void loop()
{
  Serial.print(".");
  // Wire.requestFrom(0x21, 1, true);
  // int a = Wire.read();
  // Serial.println("Send " + String(a));
  Wire.beginTransmission(0x21); // address byte in write mode=0100001+0=01000010= 0x42 (queued)
  // Wire.write(highByte(x));      // 0x12 is queued
  // Wire.write(lowByte(x));       // 0x34 is queued
  Wire.write("HOLA");
  Wire.endTransmission(); // all queued data bytes are sent on ACK
  //-------------------
  delay(1000); // test interval
}

ESP8266从站测试代码

#include <Arduino.h>
#include <ArduinoJson.h>
#include <Wire.h>

#include "functions.hpp"


void setup()
{
    Serial.begin(SPEED);

    pinMode(RELAY1, OUTPUT); // Mode OUTPUT
    pinMode(RELAY2, OUTPUT); // Mode OUTPUT
    // pinMode(TX, FUNCTION_3); // Mode GPIO
    // pinMode(RX, FUNCTION_3); // Mode GPIO

    Wire.begin(SDA_PIN,SCL_PIN, I2C_SLAVE_ADDR);
    // Wire.begin(RX, TX, I2C_SLAVE_ADDR);
    Wire.setClock(CLOCK);
    Wire.onRequest(eventoSolicitud); // registrar evento de solicitud de datos
    Wire.onReceive(eventoRecepcion); // registrar evento de recepcion de datos

    digitalWrite(RELAY2, HIGH);
}

void loop()
{

    delay((tBucle * 1000));

}

void eventoRecepcion(int howMany)
{

  Serial.print("datos i2c");

  while (1 < Wire.available()) // loop through all but the last byte
  {
    char c = Wire.read(); // receive byte as a character
    Serial.print(c);      // print the character
  }

  int x = Wire.read(); // receive byte as an integer
  Serial.println(x);   // print the integer
}

故障现象

  • 使用默认D1/D2作为I2C引脚时,代码可运行但从站始终无法接收到主站发送的数据;
  • 若取消代码中TX/RX引脚pinMode配置的注释、调用Wire.begin(RX, TX, I2C_SLAVE_ADDR)配置自定义I2C引脚时,ESP8266从站完全无响应。

故障根因及修复方案

通用硬件问题

  1. I2C上拉电平错误:ESP32、ESP8266均为3.3V器件,引脚最高耐受电压不超过3.6V,当前接线将I2C引脚通过2k电阻上拉到5V,会导致电平阈值不匹配、从站无法正常应答ACK,长期通电还有烧坏引脚的风险。
    • 修复:将两个上拉电阻(建议更换为4.7kΩ)的上拉端从5V改接3.3V,确保两个板卡GND可靠连接。
  2. 主站引脚配置错误:代码中注释的SDA=33/SCL=36配置不可用,ESP32的GPIO36是仅输入引脚,没有输出驱动能力,完全无法满足I2C开漏输出的要求。
    • 修复:使用默认的GPIO21(SDA)/GPIO22(SCL)即可,若要更换引脚,避开GPIO34/35/36/39这类仅输入的引脚。

默认D1/D2引脚通信失败的代码问题

  1. 未做总线地址校验:主站代码将总线扫描逻辑注释,无法确认从站地址是否匹配、物理接线是否正常。I2C地址容易出现7位/8位混淆,比如代码注释里提到的0x42是8位写地址,对应的7位从站地址是0x21,要确认从站I2C_SLAVE_ADDR宏定义确实为0x21。
    • 修复:放开主站总线扫描逻辑,上电先确认能扫描到地址为0x21的从站设备,再做数据收发测试。
  2. 从站接收回调逻辑错误:
    • Wire.onReceive注册的回调是在I2C中断上下文执行的,回调里直接调用Serial.print这类耗时操作(9600波特率下打印1个字符需要1ms以上),会打断I2C硬实时时序,直接导致丢数据、总线锁死。
    • 接收判断逻辑while (1 < Wire.available())存在边界问题,当接收字节数小于2时会直接读空值,多字节接收时也容易出现残留数据。
    • 修复:在回调里只做数据读取、标志位置位的操作,将收到的数据存入全局缓冲区,打印、数据解析这类耗时逻辑全部放到loop()循环中执行;接收循环改为while(Wire.available() > 0)逐字节读完所有缓冲区数据。

切换RX/TX引脚后从站无响应的问题

  1. 引脚功能冲突:ESP8266的RX(GPIO3)/TX(GPIO1)上电默认是UART0的功能引脚,也就是Serial绑定的硬件串口。如果仅配置引脚为GPIO模式、但没有关闭/重映射Serial,会出现I2C外设和UART外设抢占引脚控制权的问题,导致I2C完全无法工作。
    • 修复:如果必须用RX/TX做I2C引脚,需要将调试串口重映射到UART1(仅TX可用,接GPIO2做日志输出),或者临时关闭Serial打印,避免引脚冲突。
  2. 启动电平干扰:ESP8266上电时会检测GPIO0/GPIO2/GPIO15的电平确定启动模式,其中GPIO1(TX)如果在上电瞬间被拉低,会导致模块进入下载模式,无法正常运行用户代码。
    • 修复:确保I2C接线不会在上电阶段拉低TX引脚电平,必要时可以在TX引脚加10k上拉电阻到3.3V,保证模块正常启动。
  3. 引脚复用配置顺序错误:必须在调用Wire.begin()之前,先执行pinMode(TX, FUNCTION_3)、pinMode(RX, FUNCTION_3)将两个引脚从UART功能切换为GPIO功能,同时确认Wire.begin()传入的SDA/SCL引脚顺序和实际物理接线完全对应,不要接反SDA和SCL。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 03:45:47