Introducción
En el desarrollo de sistemas IoT, la elección del protocolo de comunicación es crucial para el rendimiento, eficiencia energética y escalabilidad del proyecto. HTTP y MQTT son los dos protocolos más utilizados, cada uno con características específicas que los hacen ideales para diferentes escenarios.
En esta lección compararemos ambos protocolos desde una perspectiva práctica, implementando ejemplos con ESP32 y DHT11 para que puedas tomar la mejor decisión en tus proyectos IoT.
Objetivos de Aprendizaje
- Comprender las diferencias fundamentales entre HTTP y MQTT
- Analizar ventajas y desventajas de cada protocolo
- Implementar comunicación HTTP con ESP32
- Comparar rendimiento y eficiencia energética
- Determinar cuándo usar cada protocolo
Comparación de Protocolos
HTTP (HyperText Transfer Protocol)
HTTP es el protocolo fundamental de la web, basado en un modelo de solicitud-respuesta. Aunque no fue diseñado específicamente para IoT, su simplicidad y amplia adopción lo hacen útil para muchos casos de uso.
Ventajas de HTTP
- Simplicidad: Protocolo bien conocido y fácil de implementar
- Debugging: Herramientas abundantes para monitoreo y depuración
- Compatibilidad: Funciona con cualquier servidor web estándar
- REST APIs: Ideal para APIs RESTful bien estructuradas
- Cacheable: Soporte nativo para caché de respuestas
Desventajas de HTTP
- Overhead: Headers adicionales aumentan el tamaño del mensaje
- Conexiones: Necesita establecer conexión para cada request
- Consumo energético: Mayor gasto de batería en dispositivos móviles
- Tiempo real: No ideal para comunicación en tiempo real
- Polling: Requiere polling para obtener actualizaciones
MQTT (Message Queuing Telemetry Transport)
MQTT es un protocolo de mensajería ligero diseñado específicamente para comunicaciones machine-to-machine (M2M) e IoT. Utiliza un modelo publish-subscribe que permite comunicación eficiente y desacoplada.
Ventajas de MQTT
- Ligero: Protocolo extremadamente eficiente en ancho de banda
- Publish-Subscribe: Comunicación desacoplada y escalable
- QoS Levels: Garantías de entrega configurables (0, 1, 2)
- Persistent Sessions: Mantiene estado de suscripciones
- Retain Messages: Últimos valores disponibles para nuevos suscriptores
- Tiempo real: Push notifications instantáneas
Desventajas de MQTT
- Broker dependency: Requiere un broker MQTT centralizado
- Complejidad: Curva de aprendizaje más pronunciada
- Debugging: Herramientas de debugging menos comunes
- No RESTful: No sigue principios REST estándar
- Topic structure: Requiere diseño cuidadoso de la jerarquía de topics
Tabla Comparativa
Aspecto | HTTP | MQTT |
---|---|---|
Patrón de comunicación | Request-Response | Publish-Subscribe |
Overhead del protocolo | Alto (headers HTTP) | Muy bajo (2 bytes mínimo) |
Consumo energético | Alto | Muy bajo |
Tiempo real | Polling requerido | Push instantáneo |
Escalabilidad | Limitada | Excelente |
Complejidad de implementación | Simple | Moderada |
Debugging y monitoreo | Excelente | Bueno |
Garantías de entrega | Básicas (TCP) | Configurables (QoS 0,1,2) |
Implementación Práctica
Ejemplo HTTP: ESP32 enviando datos por HTTP POST
Implementación de comunicación HTTP donde el ESP32 envía datos del sensor DHT11 a un servidor Flask mediante requests POST.
#include <WiFi.h>
#include <HTTPClient.h>
#include <DHT.h>
#include <ArduinoJson.h>
// Configuración WiFi
const char* ssid = "TU_RED_WIFI";
const char* password = "TU_PASSWORD";
// Configuración servidor
const char* serverURL = "http://192.168.1.100:5000/api/sensor-data";
// Configuración DHT11
#define DHT_PIN 4
#define DHT_TYPE DHT11
DHT dht(DHT_PIN, DHT_TYPE);
// Variables de control
unsigned long lastSend = 0;
const long sendInterval = 30000; // Enviar cada 30 segundos
void setup() {
Serial.begin(115200);
dht.begin();
// Conectar WiFi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi conectado!");
Serial.print("IP: ");
Serial.println(WiFi.localIP());
}
void loop() {
unsigned long now = millis();
if (now - lastSend > sendInterval) {
lastSend = now;
sendSensorDataHTTP();
}
delay(1000);
}
void sendSensorDataHTTP() {
if (WiFi.status() == WL_CONNECTED) {
HTTPClient http;
http.begin(serverURL);
http.addHeader("Content-Type", "application/json");
// Leer datos del sensor
float temperature = dht.readTemperature();
float humidity = dht.readHumidity();
// Verificar lecturas válidas
if (isnan(temperature) || isnan(humidity)) {
Serial.println("Error leyendo DHT11!");
return;
}
// Crear JSON payload
StaticJsonDocument<200> doc;
doc["device_id"] = "ESP32_HTTP_001";
doc["temperature"] = temperature;
doc["humidity"] = humidity;
doc["timestamp"] = millis();
doc["protocol"] = "HTTP";
String jsonString;
serializeJson(doc, jsonString);
// Enviar POST request
int httpResponseCode = http.POST(jsonString);
if (httpResponseCode > 0) {
String response = http.getString();
Serial.printf("HTTP Response: %d\n", httpResponseCode);
Serial.printf("Response: %s\n", response.c_str());
Serial.printf("Datos enviados - Temp: %.2f°C, Humedad: %.2f%%\n",
temperature, humidity);
} else {
Serial.printf("Error HTTP: %d\n", httpResponseCode);
}
http.end();
} else {
Serial.println("WiFi desconectado!");
}
}
from flask import Flask, request, jsonify
import json
import sqlite3
from datetime import datetime
import logging
app = Flask(__name__)
# Configurar logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Inicializar base de datos
def init_db():
conn = sqlite3.connect('iot_data.db')
c = conn.cursor()
c.execute('''CREATE TABLE IF NOT EXISTS sensor_readings
(id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp DATETIME,
device_id TEXT,
temperature REAL,
humidity REAL,
protocol TEXT)''')
conn.commit()
conn.close()
def save_reading(device_id, temperature, humidity, protocol="HTTP"):
conn = sqlite3.connect('iot_data.db')
c = conn.cursor()
c.execute('''INSERT INTO sensor_readings
(timestamp, device_id, temperature, humidity, protocol)
VALUES (?, ?, ?, ?, ?)''',
(datetime.now(), device_id, temperature, humidity, protocol))
conn.commit()
conn.close()
@app.route('/api/sensor-data', methods=['POST'])
def receive_sensor_data():
try:
# Obtener datos JSON del request
data = request.get_json()
if not data:
return jsonify({"error": "No JSON data received"}), 400
# Validar campos requeridos
required_fields = ['device_id', 'temperature', 'humidity']
for field in required_fields:
if field not in data:
return jsonify({"error": f"Missing field: {field}"}), 400
# Extraer datos
device_id = data['device_id']
temperature = float(data['temperature'])
humidity = float(data['humidity'])
protocol = data.get('protocol', 'HTTP')
# Validar rangos de datos
if not (-40 <= temperature <= 85):
return jsonify({"error": "Temperature out of range"}), 400
if not (0 <= humidity <= 100):
return jsonify({"error": "Humidity out of range"}), 400
# Guardar en base de datos
save_reading(device_id, temperature, humidity, protocol)
# Log de la recepción
logger.info(f"Data received from {device_id}: T={temperature}°C, H={humidity}%")
# Respuesta exitosa
response = {
"status": "success",
"message": "Data received successfully",
"device_id": device_id,
"timestamp": datetime.now().isoformat(),
"data_received": {
"temperature": temperature,
"humidity": humidity,
"protocol": protocol
}
}
return jsonify(response), 200
except ValueError as e:
logger.error(f"Value error: {e}")
return jsonify({"error": "Invalid data format"}), 400
except Exception as e:
logger.error(f"Unexpected error: {e}")
return jsonify({"error": "Internal server error"}), 500
@app.route('/api/latest-readings', methods=['GET'])
def get_latest_readings():
try:
conn = sqlite3.connect('iot_data.db')
c = conn.cursor()
c.execute('''SELECT timestamp, device_id, temperature, humidity, protocol
FROM sensor_readings
ORDER BY timestamp DESC
LIMIT 50''')
readings = []
for row in c.fetchall():
readings.append({
"timestamp": row[0],
"device_id": row[1],
"temperature": row[2],
"humidity": row[3],
"protocol": row[4]
})
conn.close()
return jsonify({"readings": readings}), 200
except Exception as e:
logger.error(f"Error fetching readings: {e}")
return jsonify({"error": "Failed to fetch readings"}), 500
@app.route('/health', methods=['GET'])
def health_check():
return jsonify({
"status": "healthy",
"timestamp": datetime.now().isoformat(),
"service": "IoT Data Receiver"
})
if __name__ == '__main__':
init_db()
app.run(host='0.0.0.0', port=5000, debug=True)
Comparación de Performance: HTTP vs MQTT
Analicemos las diferencias de rendimiento entre ambos protocolos con métricas reales:
# Análisis de tráfico de red (bytes por mensaje)
HTTP POST Request:
POST /api/sensor-data HTTP/1.1
Host: 192.168.1.100:5000
Content-Type: application/json
Content-Length: 89
Connection: keep-alive
{"device_id":"ESP32_001","temperature":23.5,"humidity":65.2,"timestamp":12345}
Total HTTP: ~350 bytes por mensaje
MQTT Publish:
Topic: sensors/data (12 bytes)
Payload: {"temp":23.5,"hum":65.2} (26 bytes)
MQTT headers: ~4 bytes
Total MQTT: ~42 bytes por mensaje
Ratio de eficiencia: HTTP/MQTT = 350/42 ≈ 8.3x más datos en HTTP
Métricas de Rendimiento
HTTP
- Overhead: ~250-300 bytes por request
- Latencia: 50-200ms (establecer conexión)
- Batería: Alto consumo energético
- Frecuencia máxima: ~1 request/segundo
MQTT
- Overhead: ~2-8 bytes por mensaje
- Latencia: 5-20ms (conexión persistente)
- Batería: Bajo consumo energético
- Frecuencia máxima: 100+ mensajes/segundo
Casos de Uso y Recomendaciones
Cuándo usar HTTP
HTTP es ideal para:
- APIs REST: Cuando necesitas una API RESTful estándar
- Integración web: Comunicación directa con servicios web existentes
- Debugging simple: Proyectos que requieren debugging fácil
- Datos ocasionales: Envío de datos poco frecuente (>1 minuto)
- Dispositivos con alimentación: No hay restricciones de batería
- Configuración de dispositivos: Interfaces de administración
Cuándo usar MQTT
MQTT es ideal para:
- Tiempo real: Monitoreo y control en tiempo real
- Dispositivos battery-powered: Optimización de consumo energético
- Redes con limitaciones: Ancho de banda limitado o inestable
- Múltiples suscriptores: Datos que múltiples clientes necesitan
- Alta frecuencia: Envío de datos frecuente (<1 minuto)
- Sistemas distribuidos: Arquitecturas escalables y desacopladas
- Garantías de entrega: Cuando necesitas QoS específicos
Arquitecturas Híbridas
En muchos proyectos reales, la mejor solución combina ambos protocolos:
Ejemplo de Arquitectura Híbrida
- MQTT: Para datos de sensores en tiempo real (temperatura, humedad)
- HTTP: Para configuración de dispositivos y actualizaciones de firmware
- MQTT: Para comandos de control inmediatos
- HTTP: Para reportes y análisis históricos
# Ejemplo de decision tree para elegir protocolo:
if (datos_en_tiempo_real && frecuencia_alta):
usar_protocolo = "MQTT"
elif (dispositivo_battery_powered):
usar_protocolo = "MQTT"
elif (necesitas_rest_api_estandar):
usar_protocolo = "HTTP"
elif (debugging_frecuente):
usar_protocolo = "HTTP"
elif (multiples_suscriptores):
usar_protocolo = "MQTT"
else:
usar_protocolo = "HTTP" # Default para simplicidad