Sensor afuera, display adentro: sin pasar cables

Hay un escenario clásico en domótica casera: quieres medir la temperatura del patio o de la pieza de servicio, pero quieres ver la lectura en una pantalla cómoda dentro del living. Conectar todo con cables implica pasar por paredes, perforar marcos y resignarte a un cable colgado. La alternativa moderna es ESP-NOW, un protocolo punto a punto de Espressif que conecta dos ESP32 sin pasar por un router WiFi, con latencias por debajo de 5 ms y un consumo bajísimo.

En este tutorial vamos a montar un termómetro inalámbrico de dos piezas: un transmisor con un sensor DS18B20 (waterproof, ideal para exterior) y un receptor con pantalla que muestra la temperatura. Vas a comparar dos arquitecturas, ESP-NOW directo y vía servidor HTTP local, y a entender cuál te conviene según tu caso de uso real.

Transmisor real: ESP32 Dev Module con el sensor DS18B20 waterproof conectado

El tutorial original es de Engr. Shahzada Fahad (Electronic Clinic) y usa un receptor con pantalla e-Paper Makerfabs de 4.2 pulgadas tricolor manejado por un ESP32-S3. Acá replicamos la misma lógica inalámbrica y, para el receptor, proponemos una alternativa accesible en Chile.

ESP-NOW vs HTTP/TCP: ¿cuándo elegir cuál?

Aspecto ESP-NOW HTTP (WiFi tradicional)
Latencia menos de 5 ms 30 a 200 ms
Consumo TX ~30 mA pico ~120 mA pico
Alcance 30 a 50 m (igual que WiFi) depende del router
¿Necesita router? No
¿Funciona en corte de luz? Sí (si los nodos tienen batería) No (cae con el router)
Integración con apps móviles Difícil Trivial
Encriptación Sí (PMK + LMK opcional) TLS si la implementas

Conclusión rápida: ESP-NOW gana en autonomía, latencia y resiliencia. HTTP/TCP gana si necesitas que la lectura esté disponible para otros dispositivos (Home Assistant, app móvil, dashboard remoto). Por eso vamos a implementar ambas, y al final eliges según tu uso.

Hardware del transmisor (sensor exterior)

  • ESP32 DevKit (cualquier WROOM-32 sirve)
  • Sensor DS18B20 waterproof (sonda metálica encapsulada, soporta de menos 55 °C a más 125 °C)
  • Resistencia 4.7 kΩ (pull-up del bus 1-Wire, sin esto el sensor no responde)
  • Cables y opcionalmente fuente de alimentación 5V o batería

Cableado del DS18B20:

DS18B20 ESP32
Rojo (VCC) 3.3 V
Negro (GND) GND
Amarillo/Blanco (DATA) GPIO 4 + pull-up 4.7 kΩ a 3.3 V

Diagrama de conexión del DS18B20 al ESP32 DevKit con resistencia pull-up en la línea de datos GPIO 4

Por qué la pull-up es innegociable: el protocolo 1-Wire del DS18B20 usa una sola línea para datos. Cuando ni el maestro ni el sensor están manejando la línea, queda flotante. La resistencia a VCC asegura que esté en alto por defecto, y cualquier dispositivo puede tirarla a tierra para comunicar. Sin pull-up, el sensor lee 85.0 °C de forma constante (valor por defecto del registro de temperatura al encender) o devuelve -127.0. El valor estándar del datasheet es 4.7 kΩ; el diagrama de referencia usa una resistencia parecida en el mismo punto.

Hardware del receptor

El tutorial original usa una pantalla e-Paper Makerfabs de 4.2 pulgadas con ESP32-S3. En Chile no se vende ese display específico, así que vamos a implementar el receptor con un OLED SSD1306 + ESP32. Para la lectura de temperatura ambiente típica (de menos 10 °C a 40 °C) un OLED es perfectamente legible.

  • ESP32 DevKit (puede ser el mismo modelo que el transmisor)
  • Pantalla OLED 128x32 I2C SSD1306 (alternativa al e-Paper de 4.2 pulgadas)

Cableado del OLED:

OLED ESP32
VCC 3.3 V
GND GND
SDA GPIO 21
SCL GPIO 22

Versión 1: ESP-NOW directo

Paso 1: obtener la MAC del receptor

Antes de programar el transmisor necesitas saber a quién enviarle. Carga este sketch al receptor, abre el monitor serial, y anota la MAC que imprime:

C++
#include <WiFi.h>
void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  Serial.print("MAC: ");
  Serial.println(WiFi.macAddress());
}
void loop() {}

Arduino IDE con el código del transmisor a la izquierda y el monitor serial del receptor mostrando la MAC Address 3C:84:27:C0:E8:BC

Paso 2: transmisor ESP-NOW

Reemplaza la MAC 0x3C, 0x84, 0x27, 0xC0, 0xE8, 0xBC por la tuya:

C++
#include <WiFi.h>
#include <esp_now.h>
#include <OneWire.h>
#include <DallasTemperature.h>

const int oneWireBus = 4;
OneWire oneWire(oneWireBus);
DallasTemperature sensors(&oneWire);

uint8_t receiverMAC[] = {0x3C, 0x84, 0x27, 0xC0, 0xE8, 0xBC};

typedef struct struct_message {
  float temperature;
} struct_message;

struct_message dataToSend;

void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
  Serial.print("Status: ");
  Serial.println(status == ESP_NOW_SEND_SUCCESS ? "OK" : "FAIL");
}

void setup() {
  Serial.begin(115200);
  sensors.begin();
  WiFi.mode(WIFI_STA);
  if (esp_now_init() != ESP_OK) { ESP.restart(); }
  esp_now_register_send_cb(OnDataSent);

  esp_now_peer_info_t peerInfo = {};
  memcpy(peerInfo.peer_addr, receiverMAC, 6);
  peerInfo.channel = 0;
  peerInfo.encrypt = false;
  esp_now_add_peer(&peerInfo);
}

void loop() {
  sensors.requestTemperatures();
  float tempC = sensors.getTempCByIndex(0);
  dataToSend.temperature = tempC;
  esp_now_send(receiverMAC, (uint8_t *)&dataToSend, sizeof(dataToSend));
  delay(5000);  // cada 5 segundos
}

Paso 3: receptor ESP-NOW + OLED

C++
#include <WiFi.h>
#include <esp_now.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

typedef struct struct_message {
  float temperature;
} struct_message;

struct_message incomingData;
float latestTemp = -100.0;

void onReceive(const uint8_t *mac, const uint8_t *data, int len) {
  memcpy(&incomingData, data, sizeof(incomingData));
  latestTemp = incomingData.temperature;
}

void showTemp(float t) {
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.setCursor(0, 8);
  if (t < -50) display.print("--.- C");
  else { display.print(t, 1); display.print(" C"); }
  display.display();
}

void setup() {
  Serial.begin(115200);
  Wire.begin();
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  showTemp(-100);

  WiFi.mode(WIFI_STA);
  WiFi.disconnect();
  if (esp_now_init() != ESP_OK) return;
  esp_now_register_recv_cb(onReceive);
}

void loop() {
  static unsigned long last = 0;
  if (millis() - last > 1000) {
    showTemp(latestTemp);
    last = millis();
  }
}

Versión 2: HTTP/TCP por WiFi local

Si quieres que otros dispositivos (Home Assistant, una app móvil, un script Python) también lean la temperatura, conviene usar HTTP. El transmisor abre conexión al receptor y le manda el valor como texto plano (puedes evolucionar a JSON cuando agregues más sensores).

Transmisor

C++
#include <WiFi.h>
#include <OneWire.h>
#include <DallasTemperature.h>

const int oneWireBus = 4;
OneWire oneWire(oneWireBus);
DallasTemperature sensors(&oneWire);

const char* WIFI_SSID = "TuRedWiFi";
const char* WIFI_PASS = "TuContrasena";
const char* HOST = "192.168.1.50";  // IP del receptor
const int PORT = 80;

void setup() {
  Serial.begin(115200);
  sensors.begin();
  WiFi.begin(WIFI_SSID, WIFI_PASS);
  while (WiFi.status() != WL_CONNECTED) delay(500);
}

void loop() {
  sensors.requestTemperatures();
  float t = sensors.getTempCByIndex(0);
  WiFiClient client;
  if (client.connect(HOST, PORT)) {
    client.println(String(t));
  }
  client.stop();
  delay(5000);
}

Receptor

El receptor levanta un WiFiServer en el puerto 80, acepta conexiones, lee la línea recibida y actualiza la pantalla. La idea es la misma que en ESP-NOW pero usando TCP en lugar de paquetes ESP-NOW directos.

Pruebas: calienta el sensor y mira la pantalla

Con ambos programas cargados, la forma más rápida de validar el sistema es forzar un cambio de temperatura. En el tutorial original se acerca la llama de un fósforo a la sonda del DS18B20: la lectura sube de inmediato y, tras el siguiente refresco, la pantalla del receptor muestra el nuevo valor.

Prueba de funcionamiento: se acerca la llama de un fósforo a la sonda del DS18B20 para subir la temperatura

A los pocos segundos el receptor refresca y muestra la temperatura actualizada, nítida y legible desde cualquier ángulo.

Pantalla del receptor mostrando la temperatura actualizada tras aplicar calor a la sonda

Por qué refrescamos solo cada cierto tiempo

Tanto el OLED como las pantallas e-Paper tienen un costo no trivial en cada refresco:

  • OLED SSD1306: cada display.display() envía 1 KB por I2C a 400 kHz, unos 25 ms. Si lo haces a 100 Hz, saturas el bus.
  • e-Paper: cada refresco toma de 2 a 3 segundos completos y desgasta el panel (vida útil típica: cerca de 1 millón de refrescos). Por eso el código original solo refresca cada 2 minutos.

Si usas OLED puedes refrescar cada segundo sin problema. Si usas e-Paper, mantén el intervalo de 2 a 3 minutos. La gran ventaja del e-ink es que, una vez impreso el valor, la imagen se queda en pantalla incluso con la alimentación cortada, ideal para sensores con batería que duran meses.

Variantes y mejoras

Extensiones útiles que no están en el tutorial original:

  • Multisensor con un solo receptor: el bus 1-Wire del DS18B20 soporta varios sensores en paralelo. Conecta 3 o 4 DS18B20 al mismo pin del transmisor, mándalos como arreglo por ESP-NOW, y muestra todos en el receptor. Útil para monitorear distintas piezas con un solo nodo transmisor.
  • Modo deep sleep para batería de 6+ meses: entre transmisiones, pon el ESP32 en esp_deep_sleep_start(). Con un despertar cada 5 minutos y una 18650 de 3000 mAh, llegas tranquilamente a 6 meses de autonomía.
  • Encriptación PMK + LMK en ESP-NOW: por defecto ESP-NOW va en claro. Si te preocupa que un vecino con un ESP32 cercano pueda capturar tus paquetes, agrega esp_now_set_pmk() con una clave de 16 bytes compartida entre los dos nodos. AES 128 por defecto.

Personalización para Chile

Componentes en MechatronicStore con stock local:

  • ESP32 con WiFi y Bluetooth ESP-WROOM-32 Tipo C (SKU X2-10V2): $7.990 CLP (necesitas 2: uno para el transmisor, uno para el receptor).
  • Sensor de Temperatura DS18B20 tipo termocupla (SKU G-232): $2.990 CLP (es exactamente el waterproof del tutorial original).
  • Pantalla OLED 128x32 I2C 0.91" SSD1306 (SKU D-112): $3.990 CLP (el tutorial original usa una pantalla e-Paper Makerfabs 4.2 pulgadas tricolor que no se vende en Chile. El OLED muestra perfectamente la temperatura ambiente y es mucho más barato, aunque sin el efecto papel del e-ink).
  • Resistencia 4.7 kΩ (SKU GK2-15): $100 CLP (es el valor estándar de pull-up del bus 1-Wire del DS18B20; en la página del producto eliges el valor 4.7 kΩ).
  • Protoboard 830 puntos MB102 (SKU C-302): $3.790 CLP (necesitas 2).
  • Cables macho macho 30cm (SKU C-417): $2.990 CLP.

Total proyecto (2 nodos completos): aproximadamente $32.000 CLP.

Nota sobre el display e-Paper del original: si te interesa el efecto papel y la persistencia sin consumo, podemos pedir el módulo Waveshare equivalente bajo demanda. Pregunta en MechatronicStore.

Recursos

Versión chilena basada en el tutorial de Electronic Clinic, con componentes en stock local en MechatronicStore.