InicioTarjetas de DesarrolloTermómetro para Objetos con UNIT Pulsar ESP32-C6 y Sensor MLX90614-DCC

Termómetro para Objetos con UNIT Pulsar ESP32-C6 y Sensor MLX90614-DCC

En este tutorial aprenderás a crear un termómetro sin contacto usando la UNIT Pulsar ESP32-C6 y el sensor MLX90614-DCC de DFRobot. El proyecto te guiará paso a paso para:

  • Conectar el sensor por I2C y configurar la UNIT Pulsar ESP32-C6 en modo SoftAP, creando su propia red WiFi sin necesidad de conexión a internet.
  • Acceder a una interfaz web responsiva desde tu celular o computadora, donde podrás:
    • Obtener lecturas de temperatura del objeto y la temperatura ambiente.
    • Borrar lecturas

INTRODUCCIÓN

Este proyecto permite medir la temperatura de objetos y del ambiente de forma precisa y visualizar los datos en una interfaz web, sin necesidad de internet, gracias a que la UNIT Pulsar ESP32-C6 crea su propia red WiFi. Es una solución práctica para makers, prototipos y aplicaciones industriales, como monitoreo de procesos, control de calidad o sistemas de automatización.

Ahora que conoces el objetivo del proyecto, comencemos con el desarrollo paso a paso. Sigue cada sección con atención para que puedas replicar el proyecto sin complicaciones.

MATERIALES

¡Manos a la obra! Antes de empezar, asegúrate de tener a la mano los siguientes materiales:

Otros recursos que necesitaremos:

DESARROLLO

¡Listo! Ahora sí, comencemos con el tutorial. Sigue los pasos que se indican a continuación.

1. Configuración de entorno de programación

Para comenzar, utilizaremos el Arduino IDE como entorno de desarrollo. Asegúrate de tener lo siguiente listo:

  • Agrega la placas de desarrollo UNIT Electronics ESP32 a Arduino IDE
    Abre el IDE y ve a Archivo > Preferencias.
    En el campo “Gestor de URLs adicionales de tarjetas”, agrega la siguiente dirección:
    👉 https://raw.githubusercontent.com/UNIT-Electronics/Uelectronics-ESP32-Arduino-Package/main/package_Uelectronics_esp32_index.json
Termómetro con ESP32
  • Instala las placas UNIT Electronics ESP32 desde el Gestor de placas de Arduino IDE
    Después la URL al preferencias dirígete a la pestaña Herramientas > Placa > Gestor de placas, busca “UNIT Electronics ESP32” e instala las placas. En la siguiente imagen tienes la referencia.
Termómetro con ESP32

DIAGRAMA DE CONEXIÓN

Las conexiones son muy simples. Puedes aprovechar el cable incluido con el sensor MLX90614 junto con un arnés QWIIC, lo que hará más rápida y ordenada la conexión con la UNIT Pulsar ESP32-C6.

Para simplificar las conexiones, se utilizara el puerto QWIIC de la UNIT Pulsar ESP32-C6, el cual está diseñado para trabajar con dispositivos I2C de manera rápida y segura. Este puerto no solo facilita la conexión, sino que también está optimizado para bajo consumo de energía.

En la siguiente imagen se muestra el diagrama de conexión que debes seguir para tener comunicación entre el sensor y la tarjeta.

Termómetro con ESP32
MLX90614 (DFRobot)UNIT Pulsar ESP32-C6
VCC (3.3V)3.3V
GNDGND
SDAGPIO6 / LP_SDA
SCLGPIO7 / LP_SCL

Si no cuentas con un cable QWIIC, también puedes realizar la conexión directamente utilizando los pines GPIO22 (SDIO_DATA2) para SDA y GPIO23 (SDIO_DATA3) para SCL, manteniendo la misma funcionalidad en la comunicación con el sensor.

Si deseas que este proyecto sea portátil y de bajo consumo de energía, lo más recomendable es utilizar el puerto QWIIC. Además, puedes soldar un conector PH2.0mm en la parte inferior de la UNIT Pulsar ESP32-C6, lo que te permitirá conectar fácilmente una batería LiPo y alimentar el dispositivo sin depender de una fuente externa.

CÓDIGOS

Una vez que hayas realizado todas las conexiones, el siguiente paso es instalar la librería MLX90614 en el Arduino IDE, ya que esta es necesaria para que la UNIT Pulsar ESP32-C6 pueda comunicarse correctamente con el sensor.

Para hacerlo, descarga la librería del repositorio oficial de DFRobot en GitHub, en el siguiente enlace podrás descargar la librería. Luego, solo tendrás que importarla a Arduino IDE.

Una vez importada la librería MLX90614 en el Arduino IDE, el siguiente paso es verificar que las conexiones estén correctas y que el sensor esté tomando lecturas de manera adecuada.

Para esto, utilizaremos un código de ejemplo que hemos modificado para que sea compatible con la UNIT Pulsar ESP32-C6. Este código te permitirá visualizar en el monitor serie las lecturas obtenidas por el sensor, asegurando que todo funcione correctamente antes de avanzar con el proyecto.

Antes de cargar el código selecciona la placa UNIT Pulsar ESP32-C6, como se muestra a continuación:

Después selecciona el puerto COM que se le asigno a la tarjeta y verifica que tengas seleccionado los siguientes parámetros:

Estos parámetros que permiten a la UNIT Pulsar ESP32-C6 funcione correctamente y aproveche sus recursos al máximo. La frecuencia de 160MHz ofrece buen rendimiento con bajo consumo, mientras que la memoria Flash de 4MB se divide entre el programa y el sistema de archivos SPIFFS, ideal para guardar la interfaz web y otros recursos. Además, al habilitar USB CDC, permite establecer comunicación y visualizar en el monitor serie las lecturas del sensor.

Código para lecturas en el monitor serial

Ya configurados los parámetros antes mencionados compila y carga el siguiente código:

// Incluye la librería para comunicación I2C
#include <Wire.h>

// Incluye la librería para el sensor MLX90614
#include <DFRobot_MLX90614.h>

// Define el pin SDA (Datos) para la comunicación I2C
#define SDA_PIN 6  

// Define el pin SCL (Reloj) para la comunicación I2C
#define SCL_PIN 7  

// Crea un objeto llamado 'sensor' para manejar el MLX90614 a través de I2C
DFRobot_MLX90614_I2C sensor;

void setup() {
  // Inicializa la comunicación serial a 115200 baudios para depuración
  Serial.begin(115200);

  // Inicializa el bus I2C con los pines definidos SDA y SCL
  Wire.begin(SDA_PIN, SCL_PIN);

  // Pequeña pausa para asegurar que el bus I2C esté listo antes de continuar
  delay(1000);
  
  // Mensaje informativo en el monitor serial
  Serial.println("Iniciando MLX90614...");
  
  // --- Escaneo del bus I2C para detectar dispositivos conectados ---
  
  byte error, address;     // Variables para manejar errores y direcciones detectadas
  int nDevices = 0;        // Contador de dispositivos encontrados
  
  Serial.println("Escaneando bus I2C...");
  
  // Recorre todas las direcciones I2C posibles (1 a 126)
  for(address = 1; address < 127; address++ ) {
    Wire.beginTransmission(address); // Inicia comunicación con la dirección actual
    error = Wire.endTransmission();  // Finaliza y guarda el resultado
    
    // Si no hubo error, significa que hay un dispositivo en esa dirección
    if (error == 0) {
      Serial.print("Dispositivo encontrado en: 0x");
      if (address < 16) Serial.print("0"); // Formato correcto para direcciones menores a 0x10
      Serial.println(address, HEX);        // Muestra la dirección en formato hexadecimal
      nDevices++;                           // Aumenta el contador de dispositivos encontrados
    }
  }
  
  // Mensaje dependiendo de si se encontraron dispositivos o no
  if (nDevices == 0) {
    Serial.println("No se encontraron dispositivos I2C!");
  } else {
    Serial.println("Escaneo completado");
    
    // --- Inicialización del sensor MLX90614 ---
    
    // Si la inicialización es exitosa
    if (sensor.begin() == NO_ERR) {
      Serial.println("MLX90614 iniciado correctamente");
    } else {
      // Si falla la inicialización
      Serial.println("Error al iniciar MLX90614");
    }
  }
}

void loop() {
  // Verifica si el sensor sigue inicializando correctamente
  if (sensor.begin() == NO_ERR) {
    // Lee y muestra la temperatura del objeto detectado
    Serial.print("Objeto: ");
    Serial.print(sensor.getObjectTempCelsius());
    Serial.print("°C, Ambiente: ");

    // Lee y muestra la temperatura ambiente
    Serial.print(sensor.getAmbientTempCelsius());
    Serial.println("°C");
  } else {
    // Si hay error de comunicación, muestra el mensaje
    Serial.println("Error de comunicación con sensor");
  }

  // Espera 2 segundos antes de la siguiente lectura
  delay(2000);
}

Este código permite conectar y verificar el funcionamiento del sensor MLX90614 con la UNIT Pulsar ESP32-C6 mediante comunicación I2C. Primero, realiza un escaneo del bus I2C para confirmar que el sensor está bien conectado. Luego, en el ciclo principal, lee y muestra en el monitor serial la temperatura del objeto detectado y la temperatura ambiente cada 2 segundos. Si ocurre un error en la comunicación, muestra un mensaje de advertencia para facilitar la detección de problemas en la conexión.

Al cargar el código abre el monitor serie y podrás visualizar que el sensor se inicia correctamente y si como sus lecturas de temperatura.

Código para SoftAP y Interfaz WEB

Ya que el sensor funciona correctamente ya podrás cargar el siguiente código completo del proyecto:

/* Código completo: UNIT Pulsar ESP32-C6 + MLX90614 + SoftAP
   - SDA = GPIO6, SCL = GPIO7
   - SoftAP IP: 192.168.4.1 (configuración por defecto)
   - Rutas:
       /        -> interfaz HTML
       /get     -> devuelve JSON con objectTemp y ambientTemp
       /clear   -> borra las lecturas (servidor)
*/

// Incluimos las librerías necesarias
#include <WiFi.h>                // Biblioteca para WiFi (maneja SoftAP)
#include <WebServer.h>           // Biblioteca para servidor HTTP simple
#include <Wire.h>                // Biblioteca I2C
#include <DFRobot_MLX90614.h>    // Biblioteca del sensor MLX90614 de DFRobot

// Definición de pines I2C para ESP32-C6 (SDA y SCL)
#define SDA_PIN 6  // SDA en GPIO6 (D13)
#define SCL_PIN 7  // SCL en GPIO7 (D11)

// Credenciales para la red WiFi en modo Access Point (SoftAP)
const char *ssid = "UNIT-Termometro";   // Nombre de la red que generará el ESP
const char *password = "12345678";   // Contraseña (mínimo 8 caracteres)

// Crear instancia del servidor web en el puerto 80 (HTTP)
WebServer server(80);

// Crear instancia del objeto sensor MLX90614 usando la clase I2C
DFRobot_MLX90614_I2C sensor;

// Variables globales para almacenar lecturas y estados
float objectTemp = 0.0;    // Última lectura de temperatura del objeto (°C)
float ambientTemp = 0.0;   // Última lectura de temperatura ambiente (°C)
bool hasReading = false;   // Indica si hay una lectura válida disponible
bool clearData = false;    // Bandera para indicar que se borraron las lecturas

// Función que devuelve la página HTML completa como string
// Dentro del string HTML agregamos comentarios explicativos (<!-- -->) para cada sección
String getHTML() {
  // Aquí devolvemos la interfaz de usuario como una cadena raw (sin procesar)
  // La página contiene:
  //  - 2 campos de texto readonly para mostrar las temperaturas
  //  - Botón "Obtener Lecturas" que pide /get al servidor
  //  - Botón "Borrar" que pide /clear al servidor y limpia los campos localmente
  return R"rawliteral(
  <!DOCTYPE html>
  <html lang="es">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>UNIT Termómetro</title>
    <style>
      /* Estilos para un diseño responsivo y limpio */
      * { margin: 0; padding: 0; box-sizing: border-box; }
      body { font-family: Arial, sans-serif; background-color: #f4f4f4; text-align: center; padding: 20px; }
      h1 { color: #333; margin-bottom: 20px; }
      .container { max-width: 400px; margin: auto; background: #fff; padding: 20px; border-radius: 10px;
                   box-shadow: 0 0 10px rgba(0,0,0,0.1); }
      .input-group { margin-bottom: 15px; text-align: left; }
      label { font-weight: bold; display: block; margin-bottom: 5px; }
      input[type="text"] { width: 100%; padding: 10px; border: 1px solid #ccc; border-radius: 5px;
                           font-size: 16px; text-align: center; }
      .buttons { display: flex; justify-content: space-between; gap: 10px; margin-top: 20px; }
      button { flex: 1; padding: 10px; font-size: 16px; border: none; border-radius: 8px; cursor: pointer; color: white; }
      .get-btn { background-color: #28a745; } .get-btn:hover { background-color: #218838; }
      .clear-btn { background-color: #dc3545; } .clear-btn:hover { background-color: #c82333; }
      @media (max-width: 500px) { .buttons { flex-direction: column; } }
    </style>
  </head>
  <body>
    <!-- Título -->
    <h1>UNIT TERMÓMETRO PARA OBJETOS</h1>

    <!-- Contenedor principal de la tarjeta -->
    <div class="container">

      <!-- Campo para temperatura del objeto -->
      <div class="input-group">
        <label for="obj">Temperatura Objeto (°C)</label>
        <!-- valor inicial "--" -->
        <input type="text" id="obj" readonly value="--">
      </div>

      <!-- Campo para temperatura ambiente -->
      <div class="input-group">
        <label for="amb">Temperatura Ambiente (°C)</label>
        <!-- valor inicial "--" -->
        <input type="text" id="amb" readonly value="--">
      </div>

      <!-- Botones: Obtener Lecturas y Borrar -->
      <div class="buttons">
        <button class="get-btn" onclick="getReading()">Obtener Lectura</button>
        <button class="clear-btn" onclick="clearFields()">Borrar</button>
      </div>
    </div>

    <script>
      // Función que solicita la lectura al servidor (ruta /get)
      async function getReading() {
        try {
          // Llamada fetch que espera respuesta JSON con las temperaturas
          const response = await fetch('/get');
          const data = await response.json();
          // Rellenar los campos con los datos recibidos
          document.getElementById('obj').value = data.objectTemp;
          document.getElementById('amb').value = data.ambientTemp;
        } catch (error) {
          // Si hay error (por ejemplo timeout o red), avisar al usuario
          alert('Error al obtener lectura');
        }
      }

      // Función que limpia las casillas localmente y pide al servidor que borre su estado
      function clearFields() {
        fetch('/clear'); // petición al servidor para que ponga clearData = true
        // Limpiar visualmente los campos en el navegador
        document.getElementById('obj').value = '--';
        document.getElementById('amb').value = '--';
      }
    </script>
  </body>
  </html>
  )rawliteral";
} // fin de getHTML()

// ===================== SETUP =====================
void setup() {
  Serial.begin(115200);                  // Inicia comunicación serial para mensajes de depuración
  Wire.begin(SDA_PIN, SCL_PIN);          // Inicializa bus I2C en los pines definidos (SDA, SCL)
  delay(500);                            // Pequeña pausa para estabilizar

  // Configura y enciende el punto de acceso (SoftAP)
  Serial.println("Iniciando SoftAP...");
  WiFi.softAP(ssid, password);           // Inicia SoftAP con SSID y contraseña
  Serial.print("IP del AP: ");
  Serial.println(WiFi.softAPIP());       // Muestra la IP (debería ser 192.168.4.1 por defecto)

  // Intento de inicializar el sensor MLX90614
  if (sensor.begin() == NO_ERR) {        // sensor.begin() devuelve NO_ERR si la comunicación I2C está correcta
    Serial.println("MLX90614 iniciado correctamente");
  } else {
    Serial.println("Error al iniciar MLX90614"); // Mensaje de error si no responde
  }

  // Ruta raíz: entrega la página HTML principal
  server.on("/", []() {
    server.send(200, "text/html", getHTML()); // Responde con la interfaz
  });

  // Ruta /get: cuando el cliente solicita una lectura
  server.on("/get", []() {
    // Si previamente se había marcado 'borrado', lo desactivamos para permitir nuevas lecturas
    clearData = false;

    // Tomar lecturas del sensor (bloque corto)
    objectTemp = sensor.getObjectTempCelsius();   // Lectura de objeto en °C
    ambientTemp = sensor.getAmbientTempCelsius(); // Lectura de ambiente en °C
    hasReading = true;                            // Marcar que tenemos una lectura válida

    // Construir JSON manualmente con dos decimales
    String json = "{\"objectTemp\":\"" + String(objectTemp, 2) + "\",\"ambientTemp\":\"" + String(ambientTemp, 2) + "\"}";

    // Enviar respuesta JSON al cliente que pidió /get
    server.send(200, "application/json", json);

    // Imprimir en serial para debug
    Serial.printf("Objeto: %.2f °C | Ambiente: %.2f °C\n", objectTemp, ambientTemp);
  });

  // Ruta /clear: limpiado de lecturas (servidor)
  server.on("/clear", []() {
    clearData = true;      // Activar bandera de borrado para evitar enviar lecturas hasta nueva petición
    hasReading = false;    // No hay lectura válida
    objectTemp = 0.0;      // Reset de variables
    ambientTemp = 0.0;
    server.send(200, "text/plain", "Lecturas borradas"); // Confirmación simple
    Serial.println("Lecturas borradas y variables reiniciadas");
  });

  // Iniciar el servidor HTTP
  server.begin();
  Serial.println("Servidor web iniciado");
} // fin setup()

// ===================== LOOP =====================
void loop() {
  // Procesa las peticiones entrantes al servidor de forma no bloqueante
  server.handleClient();
  // (Aquí no hacemos lecturas periódicas; las hacemos solo cuando el usuario presiona "Obtener Lecturas")
}

Este código convierte la UNIT Pulsar ESP32-C6 en un termómetro infrarrojo con interfaz web. Crea una red Wi-Fi propia (“UNIT-Termometro”) donde cualquier dispositivo conectado puede acceder a una página web para obtener temperaturas de objeto y ambiente desde el sensor MLX90614. Los usuarios pueden tomar nuevas lecturas o borrar los datos mostrados con solo pulsar botones en la interfaz. Ideal para medición remota sin contacto.

Una vez cargado el código, abre el monitor serial para verificar que todo funcione correctamente. Si la configuración es correcta, la UNIT Pulsar ESP32-C6 iniciará el SoftAP, activará el sensor y pondrá en marcha el servidor web. Luego, podrás conectarte a la red WiFi generada por la UNIT Pulsar ESP32-C6 y acceder a la interfaz web ingresando la dirección 192.168.4.1 en tu navegador, como se muestra en la siguiente imagen.

Explicación del código

Si tienes dudas sobre cómo funciona el código, aquí te explico los puntos más importantes.

1. I2C / Sensor
  • Wire.begin(SDA_PIN, SCL_PIN);
    Inicializa el bus I2C usando los pines físicos del UNIT Pulsar ESP32-C6 (GPIO6 y GPIO7). Es importante hacerlo antes de usar la librería del sensor.
  • sensor.begin()
    Intenta establecer comunicación I2C con el MLX90614. Si falla, revisa cableado/pullups/voltaje.
  • sensor.getObjectTempCelsius() / sensor.getAmbientTempCelsius()
    Devuelven la temperatura del objeto y la temperatura ambiente en °C. Si obtienes -273.15 indica fallo de lectura (no hay datos válidos).
2. SoftAP (Red autónoma)
  • WiFi.softAP(ssid, password)
    Pone a la UNIT Pulsar ESP32-C6 en modo punto de acceso (crea una red WiFi propia). Conectar un teléfono/PC a esa red permite acceder a la IP del ESP (por defecto 192.168.4.1).
  • WiFi.softAPIP()
    Muestra la IP del AP (útil para confirmar).

Consejo: puedes usar WiFi.softAPConfig(local_IP, gateway, subnet) si quieres forzar exactamente 192.168.4.1. En la mayoría de casos softAP usa esa IP por defecto.

3. Servidor HTTP y rutas
  • WebServer server(80); crea el servidor.
  • server.on("/", ...) — entrega la página HTML.
  • server.on("/get", ...) — al llamar /get:
    • Desactiva clearData.
    • Lee el sensor y construye un JSON con las dos temperaturas.
    • Devuelve el JSON al navegador.
  • server.on("/clear", ...) — al llamar /clear:
    • Setea clearData = true y reinicia las variables; devuelve texto confirmando.
4. Interfaz web (HTML + JS)
  • La interfaz tiene 2 input readonly para mostrar lecturas y 2 botones.
  • getReading() envía fetch('/get'), recibe JSON y actualiza las casillas.
  • clearFields() llama a /clear y pone -- en los campos localmente.
5. Diseño responsivo
  • CSS con max-width y media query para ajustar el layout en móviles.
  • Botones apilables en pantallas pequeñas.

FUNCIONAMIENTO

En el siguiente video se demuestra el funcionamiento del termómetro.

CONCLUSIONES

Has desarrollado un termómetro IR autónomo que combina simplicidad y funcionalidad. Utilizando la UNIT Pulsar ESP32-C6 y el sensor MLX90614 de DFRobot, lograste crear un dispositivo con red WiFi propia (SoftAP) y una interfaz web responsiva que permite obtener y borrar lecturas fácilmente desde cualquier dispositivo.

Gracias a su diseño eficiente y estable, este proyecto es ideal como prototipo para makers o como base para aplicaciones industriales ligeras, como estaciones de monitoreo o pruebas locales sin necesidad de conectividad externa. Además, ofrece un gran potencial para expandirse, incorporando funciones como registro de datos, alertas por umbrales, integración con MQTT, o mejoras en la interfaz de usuario para proyectos más avanzados.

TUTORIALES RELACIONADOS