El dilema del bus de colector abierto: Por qué unos diodos necesarios pueden bloquear tu Modbus


Si estás leyendo esto, es probable que hayas montado un bus con varios dispositivos, como los populares medidores PZEM, y te hayas encontrado con un comportamiento extraño: todo funciona a medias. Lees datos sin problema, pero en el momento de intentar algo más complejo, como cambiar la dirección de un dispositivo (con el comando setAddress), todo falla.

Lo más frustrante es que, a menudo, el culpable no es un componente defectuoso, sino un pequeño detalle de diseño que, aunque necesario, introduce un efecto secundario inesperado. Este es el caso de los diodos en un bus de colector abierto con múltiples dispositivos.Analicemos por qué ocurre esto y cómo diagnosticar el problema.

pzem004 1712339109

La Necesidad de los Diodos: Protegiendo el Bus

En un bus donde varios dispositivos comparten la misma línea de comunicación y usan salidas de colector abierto (o drenaje abierto), los diodos son esenciales. Su función es evitar que un dispositivo que está en reposo (con su salida en alta impedancia) bloquee la línea de pull-up cuando otro dispositivo intenta comunicarse.

La orientación correcta, y la que seguramente tienes, es con el cátodo hacia el pin TX del PZEM y el ánodo conectado al bus común. Esta es la configuración estándar para que un bus de colector abierto funcione correctamente. Sin estos diodos, el bus quedaría permanentemente bloqueado.

Por lo tanto, el problema no es la presencia de los diodos en sí, sino un efecto secundario de los mismos que interfiere con la comunicación bidireccional completa que exige el protocolo.

El Diagnóstico: Por qué la Lectura es Posible y la Escritura No

Para entenderlo, veamos qué sucede en los dos escenarios típicos con un bus que tiene esta topología.

1. Lectura por Broadcast (Funciona sin problemas)

Imagina que tu ESP32 envía una solicitud de lectura a todos los dispositivos (broadcast).

  1. La orden sale: La señal de tu ESP32 viaja directamente por la línea TX hacia los pines RX de todos los PZEM. No hay ningún obstáculo.
  2. La respuesta llega (a medias): El PZEM que tiene la dirección solicitada procesa el comando y debe responder por su línea TX.
    • Para enviar un ‘0’ lógico (nivel bajo), el pin TX del PZEM se conecta a tierra. El diodo conduce y la señal llega limpiamente al bus y al ESP32. Perfecto.
    • Para enviar un ‘1’ lógico (nivel alto), el pin TX del PZEM se pone en alta impedancia (desconectado). Aquí es donde el diodo de ese PZEM se polariza inversamente, actuando como un interruptor abierto y bloqueando la señal.

En este modo de broadcast, el ESP32 no espera una respuesta directa de un único dispositivo. El sistema puede funcionar porque el flanco de bajada (‘0’) es lo suficientemente claro para que el maestro interprete la comunicación, aunque la señal esté degradada. Por eso no se detecta un fallo inmediato.

2. Escritura con Confirmación (setAddress) (Falla estrepitosamente)

Aquí es donde se revela el verdadero problema. El comando setAddress no es una simple orden que se envía y se olvida. El protocolo Modbus-RTU, que utilizan los PZEM, exige una confirmación.

  1. El ESP32 ordena: Envía el comando para cambiar la dirección al PZEM M3.
  2. El PZEM M3 confirma: Para que el comando sea exitoso, el PZEM M3 debe enviar una trama de confirmación (ACK) de vuelta al ESP32.
    • El ‘0’ sí pasa: Para enviar un ‘0’ lógico, el TX del M3 se pone a tierra, el diodo conduce y la señal llega al ESP32. Perfecto.
    • El ‘1’ es bloqueado (¡problema!): Para enviar un ‘1’ lógico, el TX del M3 se pone en alta impedancia. En ese instante, el diodo del M3 se polariza inversamente y bloquea completamente la señal. La línea de datos se queda «flotando», sin que el pull-up pueda subirla a nivel alto.

El resultado es que el ESP32 nunca recibe los bits en ‘1’ que forman parte de la trama de confirmación. La comunicación se corrompe, el checksum no coincide y el comando setAddress falla.

En Resumen

El diodo actúa como una válvula unidireccional desde la perspectiva del PZEM hacia el bus. Permite que el PZEM tire de la línea a tierra (para enviar un ‘0’), pero le impide soltarla para que la resistencia de pull-up la lleve a estado alto (para enviar un ‘1’). Este comportamiento, que es inherente a la forma en que están colocados los diodos, es letal para un protocolo bidireccional y basado en flancos como Modbus.

Ahora que sabes que los diodos son la causa del bloqueo, el siguiente paso es buscar una solución. ¿La más común? Implementar un puente removible de modo que cuando vayamos a programar la dirección de un modulo PZEM podemos cortocircuitar el diodo de modo que una vez programado podamos liberar ese cortocircuito en su operación normal. Algo mucho mas avanzado es montar un circuito que convierta las señales unidireccionales en verdaderamente bidireccionales, como un adaptador de nivel lógico o un circuito discreto con transistores. Pero esa, como suele decirse, es una historia para otro post.

Conexión rápida de una pantalla OLED a un ESP32


Conectar una pantalla TFT pequeña a un ESP32 parece, sobre el papel, una tarea sencilla: cuatro cables SPI, una librería y listo. La realidad, como suele pasar en electrónica, es bastante distinta. En este artículo quiero contar el proceso completo, incluyendo los errores, las dudas y la solución final estable, porque estoy seguro de que le ahorrará tiempo a más de uno.

La pantalla en cuestión es una TFT IPS de 0,96 pulgadas, resolución 80×160, (exactamente este modelo https://amzn.to/4p0DjKF ) basada en el controlador ST7735S, muy similar (y compatible) con la conocida Adafruit Mini TFT 0.96″ 160×80. El microcontrolador es un ESP32 DevKit con módulo ESP-WROOM-32.

1. El hardware de partida

El montaje inicial estaba compuesto por:

  • ESP32 DevKit (ESP-WROOM-32)–> comprado en Amazon por unos 11.99€
  • Pantalla TFT IPS 0,96” 80×160 SPI (ST7735S)–>comprada en Amazon por unos 6.99€
  • Comunicación SPI
  • Alimentación desde el propio ESP32

Un punto importante: aunque el módulo TFT permite alimentación a 5 V porque incorpora un regulador en la propia placa, finalmente decidí alimentarlo a 3,3 V directamente desde el ESP32. Es más limpio, más seguro y suficiente para un funcionamiento correcto.

2. Primer obstáculo: el ESP32 no programaba

Antes incluso de ver algo en pantalla, apareció el primer problema serio:el ESP32 no se dejaba programar. El Arduino IDE devolvía errores del tipo:

Failed to connect / no serial data received / exit status 2

En Windows, el chip CP2102 aparecía con el clásico triángulo amarillo en el Administrador de dispositivos. La solución fue sencilla, pero nada obvia para quien empieza con un PC sin todo bien configurado:

  • Instalar el driver oficial CP210x USB-to-UART de Silicon Labs
  • Seleccionar el puerto COM correcto en el Arduino IDE
  • Bajar la velocidad de carga a 115200 baudios
  • Desconectar cualquier periférico hasta conseguir un programa mínimo estable (un Blink)

Una vez superado esto, el ESP32 empezó a comportarse como debía.

3. Pantalla encendida… pero en negro

El siguiente síntoma fue frustrante: la pantalla encendía la retroiluminación, pero no mostraba absolutamente nada.

El primer intento fue con la popular librería TFT_eSPI, que suele funcionar muy bien con ESP32. Sin embargo, aquí empezaron los problemas:

  • El archivo User_Setup_Select.h estaba prácticamente todo comentado
  • No había un controlador ST7735 correctamente definido
  • Los pines SPI no coincidían con los que estaba usando

Incluso después de configurar explícitamente ST7735 y definir pines personalizados (MOSI, SCLK, CS, DC, RST), ningún ejemplo funcionaba, ni siquiera los más básicos.

4. ¿La habré dañado alimentándola a 5 V?

En ese punto surgió la duda clásica:“¿Habré quemado la pantalla?”

Investigando el modelo exacto, encontré que este tipo de módulos incluye un regulador de tensión, exactamente igual que los de Adafruit, diseñados para:

  • VCC = 5 V
  • Lógica SPI a 3,3 V

Conclusión: la probabilidad de daño era baja. El problema no era eléctrico, sino de driver y configuración.

5. El verdadero lío: los buses SPI del ESP32

Aquí estaba una de las claves del problema.

El ESP32 dispone de dos buses SPI por hardware:

  • VSPI (por defecto):
    • SCK = GPIO18
    • MOSI = GPIO23
    • MISO = GPIO19
  • HSPI (alternativo):
    • SCK = GPIO14
    • MOSI = GPIO13
    • MISO = GPIO12 / 19 (configurable)

Yo ya tenía un lector de tarjeta SD funcionando en VSPI, y no quería tocarlo. Intentar colgar la TFT del mismo bus solo complicaba la configuración.

La solución fue clara: 👉 dedicar HSPI exclusivamente a la pantalla TFT.

6. Cambio de estrategia: librerías de Adafruit

La pista definitiva llegó revisando una reseña del propio módulo TFT, donde alguien lo hacía funcionar como si fuera una Adafruit Mini TFT 0.96″ 160×80.

Eso implicaba dos cosas importantes:

  • Usar las librerías:
    • Adafruit_GFX
    • Adafruit_ST7735
  • Inicializar la pantalla con: tft.initR(INITR_MINI160x80);

Este detalle es crítico: no todas las pantallas ST7735 se inicializan igual.

Tras instalar ambas librerías desde el gestor del Arduino IDE, todo empezó a encajar.

7. Conexión definitiva de la pantalla al ESP32

Separando claramente los buses SPI, el esquema final quedó así:

SPI

  • VSPI → Tarjeta SD
  • HSPI → Pantalla TFT

Conexiones TFT → ESP32

  • GND → GND
  • VCC → 3V3
  • SCL (SCK) → GPIO14
  • SDA (MOSI) → GPIO13
  • CS → GPIO26
  • DC → GPIO27
  • RES → GPIO12
  • BLK → 3V3 (retroiluminación permanente)

De este modo, una TFT y un futuro lector/grabador de SD pueden funcionar simultáneamente sin interferencias.

8. Código final que funciona (por fin)

Una prueba basica una vez instaladas desde el propio IDE de Arduino las librerías de Adafruit (Adafruit_GFX.h y Adafruit_ST7735) es el siguiente código funcional:

#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>

#define TFT_CS   26
#define TFT_DC   27
#define TFT_RST  12

Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);

void setup() {
  // Usar HSPI con 14 (SCK) y 13 (MOSI)
  SPI.begin(14, 19, 13);  // SCK=14, MISO=19 (no usado), MOSI=13

  tft.initR(INITR_MINI160x80);  // 0.96" 160x80
  tft.setRotation(3);

  tft.fillScreen(ST77XX_RED);
  delay(1000);
  tft.fillScreen(ST77XX_GREEN);
  delay(1000);
  tft.fillScreen(ST77XX_BLUE);
  delay(1000);
  tft.fillScreen(ST77XX_BLACK);
  tft.setTextColor(ST77XX_WHITE);
  tft.setCursor(0, 0);
  tft.println("Hola");
}

void loop() {}

!Funciona !, ya se ven colores al prinicpio a pantalla completa y al final de la secuencia un texto, pero como inconveniente del código anterior es que el texto que se muestra es demasiado pequeño para poderse leer.

Esta claro que el codigo anterior se puede mejorar. Este es el sketch de prueba definitivo, limpio y funcional:

#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>

#define TFT_CS   26
#define TFT_DC   27
#define TFT_RST  12

Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);

void setup() {
  // HSPI: SCK=14, MISO=19, MOSI=13
  SPI.begin(14, 19, 13);

  tft.initR(INITR_MINI160x80);
  tft.setRotation(3);
  tft.fillScreen(ST77XX_BLACK);
  tft.setTextSize(2);

  tft.setTextColor(ST77XX_RED, ST77XX_BLACK);
  tft.setCursor(5, 10);
  tft.print("0123456789");

  tft.setTextColor(ST77XX_GREEN, ST77XX_BLACK);
  tft.setCursor(5, 35);
  tft.print("0123456789");

  tft.setTextColor(ST77XX_BLUE, ST77XX_BLACK);
  tft.setCursor(5, 60);
  tft.print("0123456789");
}

void loop() {
}

El resultado: imagen estable, colores correctos y orientación perfecta.

9. Conclusiones importantes

Si estás intentando conectar una pantalla TFT pequeña a un ESP32, quédate con estas ideas clave:

  • El ESP32 tiene dos buses SPI: aprovéchalos
  • No todas las pantallas ST7735 usan la misma inicialización
  • La librería Adafruit_ST7735 es a veces más fiable que TFT_eSPI
  • Que la pantalla se ilumine no significa que esté bien configurada
  • Alimentar a 3,3 V suele ser la opción más segura

En el próximo proyecto, esta TFT ya no será un problema… pero este “viaje” merecía ser contado.