Entrada

Sistema de monitoreo de plantas con ESP8266 - Parte 1 (en construcción)

Monitoreo del estado de suelo y datos ambientales para plantas utilizando microcontrolador basado en ESP8266EX

Sistema de monitoreo de plantas con ESP8266 - Parte 1 (en construcción)

Introducción

En esta serie de publicaciones quiero compartir el desarrollo de un proyecto personal que estoy comenzando: un sistema compacto para el monitoreo de plantas.

La idea principal es crear un dispositivo que permita supervisar el estado del suelo y las condiciones ambientales que afectan directamente el bienestar de una planta. El objetivo es atender aspectos esenciales como el nivel de hidratación del suelo (humedad del sustrato) y la humedad del ambiente, lo que resulta clave para mantener una planta saludable, especialmente si no puedes estar pendiente de ella todo el tiempo.

Uno de los objetivos clave que espero cumplir con este proyecto es evitar que se me sigan muriendo las plantas en mi departamento :( y tener una forma sencilla de saber cuando debo regarlas, además de tener la capacidad de monitorear estos datos y acceder a ellos de forma remota.

Este proyecto busca ser simple, práctico y fácilmente replicable. En cada entrega iré detallando los componentes que utilizo, cómo los conecto, el código necesario, los desafíos que voy encontrando y cómo los soluciono.

Disclaimer: No soy ningún experto en lo que respecta a electrónica y componentes. La información entregada en este post ha sido producto de investigacíon / prueba-error y es lo que me ha funcionado a mí en base a mis necesidades

Objetivos

  • Medir la humedad del suelo utilizando un sensor capacitivo.

  • Obtener la temperatura y humedad ambiental mediante un DHT22.

  • Mostrar la información en una pantalla OLED.

  • Dejar el sistema listo para futuras extensiones (como envío de datos por WiFi o notificaciones).

Componentes

ComponenteDescripción
D1 Mini (ESP8266)Microcontrolador principal con WiFi integrado
Pantalla OLED 0.96” (I2C, 128x64)Para mostrar los datos del sensor
DHT22Sensor de temperatura y humedad
Sensor capacitivo de humedad V2.0Mide el nivel de hidratación del sustrato
Cables dupontPara las conexiones básicas
ProtoboardPara pruebas rápidas y conexiones sin soldadura
Fuente de alimentación 3.3V/USBPara energizar el sistema

🧠 D1 Mini (ESP8266)

El corazón del proyecto. Es un microcontrolador basado en el ESP8266, ideal para proyectos IoT por su conectividad Wi-Fi integrada y su tamaño compacto. Elegí este microcontrolador para este proyecto por los siguientes aspectos clave

  • Tamaño compacto
  • Utiliza USB tipo C
  • Soporte para Wifi
  • Suficientes pines digitales y una analógica

Particularmente estoy utilizando el D1 Mini de Wemos


📺 Pantalla OLED 128x64 (SH1106 - I2C)

Una pantalla monocromática que utiliza el protocolo I2C, lo cual ahorra pines en comparación con otros tipos de conexión (como SPI). En este caso, usamos la librería U8g2 de olikraus, que ofrece compatibilidad completa con este controlador (SH1106). Permite mostrar textos, íconos o gráficos como barras de progreso o estados visuales.

  • Voltaje de operación: 3.3V o 5V
  • Comunicación: I2C (SDA/SCK)
  • Resolución: 128x64 píxeles


🌡️ Sensor DHT22 (Temperatura y Humedad)

Sensor digital que mide temperatura y humedad relativa del ambiente. Su precisión es superior a la del DHT11 y tiene mejor rango de operación. Es perfecto para conocer el estado del ambiente donde se encuentra la planta.

Este sensor quizás no es ideal para este proyecto ya que es más grande de lo que me gustaría, pero es lo que tengo a mano de momento. Posiblemente en otra etapa del proyecto se reemplace por algo mas compacto.

  • Rango de temperatura: -40 a +80 °C ±0.5 °C
  • Rango de humedad: 0 a 100% RH ±2–5%
  • Salida digital


🌱 Sensor de Humedad del Suelo Capacitivo V2.0

Este sensor mide la humedad del suelo utilizando la capacitancia del terreno. A diferencia de los sensores resistivos, no tiene partes metálicas expuestas, lo que lo hace más duradero y resistente a la corrosión. Ideal para proyectos a largo plazo en ambientes húmedos.

  • Voltaje de operación: 3.3V–5V
  • Salida analógica (AOUT)
  • Mayor durabilidad comparado con sensores resistivos

Este sensor es de suma importancia ya que nos entregará datos del estado del suelo en donde se encuentra la planta

Conexión de componentes

📺 Pantalla OLED (I2C - SH1106)

Pantalla OLEDD1 Mini (ESP8266)Función
VDD3V3Alimentación
GNDGTierra
SCL (SCK)D1 (GPIO5)Reloj I2C
SDAD2 (GPIO4)Datos I2C

🌡️ Sensor DHT22

DHT22D1 Mini (ESP8266)Función
VCC3V3Alimentación
GNDGTierra
DATAD5 (GPIO14)Señal de datos

🌱 Sensor de humedad de suelo capacitivo V2.0

Sensor HumedadD1 Mini (ESP8266)Función
VCC3V3Alimentación
GNDGTierra
AOUTA0Lectura analógica

Diagrama

Si tenemos todo bien conectado debería quedar algo así. Este es un diagrama que hice en la web cirkitdesigner.

En físico se ve así

Código

Interfaz

Para hacer la interfaz estuve utilizando un recurso web muy interesante llamado lopaka.app, la cual permite hacer interfaces personalizadas para tus proyectos con soporte para distintos tipos de pantallas y resoluciones.

Después de mucho trabajo y ocupar toda mi creatividad terminé con esta interfaz que es mucho más agradable a la vista que simplemente imprimir texto plano sin formato

Una vez se termina de trabajar en la interfaz, es posible copiar el código para agregarlo al proyecto. En mi caso lo dejé en un archivo dentro del proyecto en la ruta /src/display.h

Es importante recordar incluir el código de la interfaz en el archivo main.cpp

Estructura y Código base

1
2
3
4
5
6
7
8
plant-monitor-system/
├── src/                    # Código fuente principal
│   ├── display.h
│   └── main.cpp
├── test/                   # Pruebas unitarias
├── .pio/                   # Carpeta generada por PlatformIO
├── platformio.ini          # Configuración del proyecto, Librerías
├── README.md               # Documentación del proyecto
1
2
3
4
5
6
7
8
9
static const unsigned char wifi_image[] = {0x80,0x0f,0x00,0x60,0x30,0x00,0x18,0xc0,0x00,0x84,0x0f,0x01,0x62,0x30,0x02,0x11,0x40,0x04,0x08,0x87,0x00,0xc4,0x18,0x01,0x20,0x20,0x00,0x10,0x42,0x00,0x80,0x0d,0x00,0x40,0x10,0x00,0x00,0x02,0x00,0x00,0x05,0x00,0x00,0x02,0x00,0x00,0x00,0x00};
static const unsigned char plant_image[] = {0x00,0xc0,0x00,0xf0,0x04,0xf8,0x0e,0xfc,0x1f,0xfc,0x3f,0x7e,0x3f,0x7e,0x3f,0x3e,0x1e,0x0f,0x0c,0x03,0x08,0x01,0x10,0x01,0xa0,0x00,0xc0,0x00,0x80,0x00,0x80,0x00};
static const unsigned char temp_image[] = {0x38,0x00,0x44,0x40,0xd4,0xa0,0x54,0x40,0xd4,0x1c,0x54,0x06,0xd4,0x02,0x54,0x02,0x54,0x06,0x92,0x1c,0x39,0x01,0x75,0x01,0x7d,0x01,0x39,0x01,0x82,0x00,0x7c,0x00};
static const unsigned char air_humidity_image[] = {0x20,0x00,0x20,0x00,0x30,0x00,0x70,0x00,0x78,0x00,0xf8,0x00,0xfc,0x01,0xfc,0x01,0x7e,0x03,0xfe,0x02,0xff,0x06,0xff,0x07,0xfe,0x03,0xfe,0x03,0xfc,0x01,0xf0,0x00};
static const unsigned char not_wifi_image[] = {0x84,0x0f,0x00,0x68,0x30,0x00,0x10,0xc0,0x00,0xa4,0x0f,0x01,0x42,0x30,0x02,0x91,0x40,0x04,0x08,0x85,0x00,0xc4,0x1a,0x01,0x20,0x24,0x00,0x10,0x4a,0x00,0x80,0x15,0x00,0x40,0x20,0x00,0x00,0x42,0x00,0x00,0x85,0x00,0x00,0x02,0x01,0x00,0x00,0x00};
static const unsigned char image_download_4_bits[] = {0x01,0x03,0x07,0x0f};
static const unsigned char image_download_5_bits[] = {0x08,0x0c,0x0e,0x0f};
static const unsigned char image_download_6_bits[] = {0x0f,0x07,0x03,0x01};
static const unsigned char image_download_7_bits[] = {0x0f,0x0e,0x0c,0x08};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#include <Wire.h>
#include <ESP8266WiFi.h>
#include <WiFiManager.h>
#include <U8g2lib.h>
#include <NTPClient.h>
#include "display.h"
#include <DHT.h>
#include <Adafruit_Sensor.h>

#define MOISTURE_PIN A0 // Pin analógico para el sensor de humedad del suelo
#define DRY_VALUE 730  // Valor del sensor en aire seco
#define WET_VALUE 296  // Valor del sensor completamente sumergido

// DHT22
#define DHTPIN D4          // Pin conectado al sensor DHT22
#define DHTTYPE DHT22      // Tipo de sensor DHT
DHT dht(DHTPIN, DHTTYPE);

// Configuración de NTP
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", -4 * 3600, 60000); // UTC-5: Ajusta según tu zona horaria

// Configuración de la pantalla OLED SH1106
U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE, /* clock=*/ D1, /* data=*/ D2);

unsigned long previousMillis = 0;  // Guarda el tiempo de la última lectura
const long interval = 2000;        // Intervalo entre lecturas (2 segundos)

void updateDisplay(String currentTime, int soilHumidity, float airTemp, float airHumidity){

  u8g2.clearBuffer();
  u8g2.setFontMode(1);
  u8g2.setBitmapMode(1);
  u8g2.drawFrame(52, 18, 25, 19);
  u8g2.setFont(u8g2_font_haxrcorp4089_tr);
  u8g2.drawStr(79, 31, "%");
  u8g2.drawXBM(107, 2, 19, 16, wifi_image);
  u8g2.drawStr(89, 31, "Hum");
  u8g2.drawStr(33, 14, "Plant Monitoring");
  u8g2.drawStr(5, 12, currentTime.c_str());
  u8g2.setFont(u8g2_font_timR14_tr);
  char soilHumidityStr[5];
  sprintf(soilHumidityStr, "%02d", soilHumidity);
  u8g2.drawStr(55, 34, soilHumidityStr);
  u8g2.drawXBM(33, 19, 16, 16, plant_image);

  // AIR HUM
  char airHumidityStr[8];
  sprintf(airHumidityStr, "%.1f", airHumidity);
  u8g2.drawStr(27, 57, airHumidityStr);
  u8g2.drawXBM(11, 42, 11, 16, air_humidity_image);

  // TEMP
  char airTempStr[8];
  sprintf(airTempStr, "%.1f", airTemp);
  u8g2.drawStr(70, 56, airTempStr);
  u8g2.drawXBM(103, 42, 16, 16, temp_image);

  u8g2.drawXBM(65, 56, 4, 4, image_download_4_bits);
  u8g2.drawXBM(61, 56, 4, 4, image_download_5_bits);
  u8g2.drawXBM(65, 40, 4, 4, image_download_6_bits);
  u8g2.drawXBM(61, 40, 4, 4, image_download_7_bits);
  u8g2.drawLine(0, 0, 0, 63);
  u8g2.drawLine(1, 63, 127, 63);
  u8g2.drawLine(127, 0, 127, 63);
  u8g2.drawLine(0, 0, 127, 0);
  u8g2.sendBuffer();
}

void setup() {
  // Inicializa el serial
  Serial.begin(115200);

  dht.begin();

  // Inicializa la pantalla OLED
  u8g2.begin();
  u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_ncenB08_tr); // Fuente estándar
  u8g2.drawStr(0, 10, "Config WiFi...");
  u8g2.sendBuffer();

  // Inicializa WiFiManager
  WiFiManager wm;

  // Intentar conectar con configuraciones guardadas o iniciar AP
  if (!wm.autoConnect("AutoConnectAP")) {
    u8g2.clearBuffer();
    u8g2.drawStr(0, 10, "Error WiFi!");
    u8g2.sendBuffer();
    delay(3000);
    ESP.restart();
  }

  // Mostrar SSID conectado en la pantalla OLED
  String ssid = WiFi.SSID();
  u8g2.clearBuffer();
  u8g2.drawStr(0, 10, "Conectado a:");
  u8g2.drawStr(0, 30, ssid.c_str());
  u8g2.sendBuffer();
  delay(2000);
}

void loop() {

  timeClient.update(); // Actualizar hora desde el servidor NTP

  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;

    // Moisture Sensor - Leer humedad del sensor
    int sensorValue = analogRead(MOISTURE_PIN);
    int soilHumidity = map(sensorValue, DRY_VALUE, WET_VALUE, 0, 100);

    // Moisture Sensor - Limitar el porcentaje entre 0 y 100
    if (soilHumidity < 0) soilHumidity = 0;
    if (soilHumidity > 100) soilHumidity = 100;

    // Moisture Sensor - Mostrar la hora en la esquina superior izquierda
    String formattedTime = timeClient.getFormattedTime();
    String currentTime = formattedTime.substring(0, 5); // Extraer HH:MM

    // DHT22 - Leer datos
    float airTemp = dht.readTemperature();
    float airHumidity = dht.readHumidity();

    // DHT22 - Comprobar errores en el sensor
    if (isnan(airTemp) || isnan(airHumidity)) {
        Serial.println("Error leyendo el sensor DHT22!");
        airTemp = 0;
        airHumidity = 0;
    }

    updateDisplay(currentTime, soilHumidity, airTemp, airHumidity);
  }
}

Prototipo funcionando

Imagen tomada con el sensor de humedad sumergido parcialmente en agua.

Características actuales

  • Hora NTP en tiempo real ajustada a la zona horaria que desees
  • Conexión a Wifi con portal cautivo de configuración para el primer inicio y con capacidad de almacenar credenciales
  • Humedad de suelo
  • Humedad ambiental
  • Temperatura ambiental

Siguientes pasos

  • Enviar data HomeAssistant
  • Soldar componentes y hacer un prototipo del enclosure
  • Agregar batería para cargar el dispositivo
  • (Opcional) Agregar recarga de batería con energía solar

Construye, automatiza, repite. ¡Nos vemos en el próximo post!

Esta entrada está licenciada bajo CC BY 4.0 por el autor.