01 · Punto de partida
El idioma de la web
Con el bloque de redes cerrado, el dato ya puede salir del dispositivo y llegar a un servidor por TCP/IP. Ahora hace falta que ambos se entiendan: que el servidor sepa qué es lo que llegó y qué se espera de él. Ése es el trabajo de los protocolos de aplicación, y el primero que vemos es HTTP (HyperText Transfer Protocol), el que usa la web.
HTTP tiene una virtud decisiva para IoT: es el lenguaje que hablan casi todos los servicios. Si querés guardar un dato en una base de datos en la nube, publicar una lectura en una plataforma, consultar la hora o descargar una configuración, lo más probable es que haya una interfaz HTTP. Atraviesa cualquier firewall (el puerto 443 casi siempre está abierto), se puede probar con un navegador o curl, y hay bibliotecas en cualquier plataforma. Tiene también sus costos: es un protocolo de texto con cabeceras pesadas, pensado para páginas web, no para sensores con batería. Este tema enseña a usarlo con los ojos abiertos.
Los fragmentos de C++ siguen el estilo Arduino para ESP32; los nombres de las clases pueden variar entre versiones de la biblioteca.
02 · Estructura
Anatomía de una conversación HTTP
HTTP es un protocolo de pedido y respuesta. El cliente (el dispositivo) envía una petición; el servidor devuelve una respuesta. Ambas tienen la misma forma, en texto plano:
- Una línea inicial. En la petición: el método, la ruta y la versión. En la respuesta: la versión, el código de estado y su texto.
- Las cabeceras: pares
Nombre: valor, una por línea, con metadatos del mensaje. - Una línea en blanco que marca el fin de las cabeceras.
- El cuerpo (opcional): los datos en sí.
Observá lo que el diagrama muestra: los datos de la lectura ([41]) ocupan unos pocos bytes dentro de un mensaje de más de doscientos. Todo lo demás es el «sobre» necesario para que el servidor sepa a dónde va, qué formato tiene y quién lo envía.
Una aclaración de versiones. Lo que ves es HTTP/1.1, un protocolo de texto que es el más sencillo de implementar en un microcontrolador y el que soporta casi todo. Las versiones más nuevas (HTTP/2 y HTTP/3) son binarias, más eficientes y multiplexan varias conversaciones, pero exigen más memoria y bibliotecas más complejas; en dispositivos pequeños, HTTP/1.1 sigue siendo lo habitual.
03 · Qué pedir y dónde
Métodos y direcciones
El método dice qué se quiere hacer con el recurso. Los cinco más usados:
| Método | Significado | ¿Cambia algo en el servidor? | ¿Idempotente? | Uso en IoT |
|---|---|---|---|---|
| GET | Leer un recurso | No | Sí | Descargar configuración, consultar la hora o un comando pendiente |
| POST | Crear un recurso o enviar datos para procesar | Sí | No | Enviar una lectura |
| PUT | Reemplazar un recurso completo | Sí | Sí | Fijar el estado de un dispositivo (tema 11) |
| PATCH | Modificar una parte de un recurso | Sí | Depende | Cambiar un parámetro de la configuración |
| DELETE | Eliminar un recurso | Sí | Sí | Borrar un registro o una suscripción |
La idempotencia (el mismo pedido repetido deja el mismo resultado) es la clave para decidir si un método se puede reintentar sin riesgo. Un GET o un PUT repetidos son inocuos. Un POST repetido puede crear dos lecturas iguales: por eso conviene que el cuerpo lleve un identificador único para que el servidor descarte duplicados (tema 18). El tema 20 profundiza en GET y POST.
La URL dice dónde está el recurso. Se compone de partes que ya conocés del tema 17:
httpses el esquema: HTTP sobre TLS (puerto 443 por defecto);httpes sin cifrar (puerto 80).datos.ejemplo.comes el servidor, que se resuelve por DNS; opcionalmente va un puerto./api/v1/lecturases la ruta del recurso. Incluir la versión (v1) permite cambiar la interfaz sin romper a los equipos instalados.?dispositivo=c07es la consulta: parámetros en paresnombre=valor.
04 · Metadatos
Las cabeceras que importan
Las cabeceras son información sobre el mensaje. Hay decenas definidas; para un dispositivo IoT alcanzan unas pocas, y conviene enviar solo las necesarias, porque cada una cuesta bytes en cada petición.
| Cabecera | Dirección | Qué dice | ¿Imprescindible? |
|---|---|---|---|
Host | Petición | El nombre del servidor (permite varios sitios en una misma dirección) | Sí, en HTTP/1.1 |
Content-Type | Ambas | El formato del cuerpo (application/json) | Sí, si hay cuerpo |
Content-Length | Ambas | Cuántos bytes tiene el cuerpo, para saber dónde termina | Sí, si hay cuerpo |
Authorization | Petición | Credencial del cliente (un token o una clave) | Si la interfaz exige autenticación |
Connection | Ambas | close cierra tras la respuesta; keep-alive mantiene la conexión | Recomendable |
Accept | Petición | Qué formatos acepta el cliente | No |
User-Agent | Petición | Quién es el cliente (modelo y versión del firmware) | No, pero útil para diagnóstico |
Retry-After | Respuesta | Cuántos segundos esperar antes de reintentar (con 429 y 503) | Hay que respetarla si llega |
Location | Respuesta | La nueva dirección, en una redirección | Si el código es de redirección |
Date | Respuesta | La hora del servidor | No, pero sirve para sincronizar el reloj |
La Authorization merece atención especial. Es la forma habitual de identificar al dispositivo (un token por equipo, nunca uno compartido por toda la flota), y solo es seguro sobre HTTPS: sobre HTTP, el token viaja a la vista de cualquiera. Y la cabecera Date es un regalo: cada respuesta trae la hora del servidor, una forma gratuita de poner en hora el reloj del dispositivo, algo necesario para validar certificados.
05 · Interpretar la respuesta
Códigos de estado: qué hacer con cada uno
La primera cosa que mira un cliente al recibir la respuesta es el código de estado: un número de tres cifras cuya primera cifra indica la familia. Un cliente de dispositivo no se limita a «anduvo o no anduvo»: decide qué hacer según el código.
| Código | Significado | Acción del dispositivo |
|---|---|---|
| 200 OK | Todo bien, con cuerpo | Procesar la respuesta; borrar del buffer lo enviado |
| 201 Created / 204 No Content | Creado / bien, sin cuerpo | Igual que 200 |
| 301, 302, 307, 308 | Redirección: el recurso está en otra dirección (Location) | Seguir la nueva dirección, con un máximo de saltos; nunca bajar de HTTPS a HTTP |
| 400 Bad Request | El mensaje está mal formado | No reintentar: hay un error de formato; registrar y reportar |
| 401 Unauthorized | Credencial ausente o inválida | No reintentar sin parar; renovar la credencial o reportar |
| 403 Forbidden | Autenticado, pero sin permiso | No reintentar; reportar |
| 404 Not Found | El recurso no existe | No reintentar; revisar la dirección |
| 408 Request Timeout | El servidor se cansó de esperar | Reintentar con espera creciente |
| 429 Too Many Requests | El cliente envía demasiado | Esperar lo que indique Retry-After; reducir la frecuencia |
| 500 Internal Server Error | Falla del servidor | Reintentar unas pocas veces con espera creciente; si persiste, reportar |
| 502, 503, 504 | Servidor o intermediario caído, saturado o sin respuesta | Reintentar con espera creciente y retardo aleatorio |
La regla de fondo, que enlaza con el tema 18: los códigos 4xx son errores del cliente (el dispositivo hizo algo mal) y reintentar el mismo pedido dará el mismo resultado, con el costo de energía y de tráfico; los 5xx son errores del servidor y suelen ser transitorios. La excepción son 408 y 429, que son 4xx pero se recuperan con tiempo. Y el 429 trae una instrucción explícita: obedecé el Retry-After. Ignorarla es la mejor forma de que el servidor bloquee al dispositivo.
06 · Programar el cliente
Un cliente HTTP en el microcontrolador
Las plataformas con Wi-Fi traen una biblioteca de cliente HTTP que se ocupa de abrir la conexión, armar las cabeceras y leer la respuesta. El siguiente fragmento envía una lectura por POST sobre HTTPS, con tiempo máximo y manejo de los resultados:
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <HTTPClient.h>
#include "secretos.h" // define TOKEN_DISPOSITIVO y el certificado raíz CERT_RAIZ
// Devuelve: 0 = enviado, 1 = reintentar más tarde, 2 = error permanente
int enviarLectura(const String &json) {
WiFiClientSecure cliente;
cliente.setCACert(CERT_RAIZ); // verificar el certificado del servidor (tema 41)
HTTPClient http;
http.setTimeout(5000); // 5 s como máximo en cada espera
if (!http.begin(cliente, "https://datos.ejemplo.com/api/v1/lecturas")) return 1;
http.addHeader("Content-Type", "application/json");
http.addHeader("Authorization", String("Bearer ") + TOKEN_DISPOSITIVO);
int codigo = http.POST(json);
int resultado;
if (codigo < 0) { // la biblioteca no pudo completar la petición
Serial.print("Error de red: ");
Serial.println(HTTPClient::errorToString(codigo));
resultado = 1; // sin red, sin DNS, sin conexión o sin respuesta: reintentar
} else if (codigo >= 200 && codigo < 300) {
resultado = 0; // éxito
} else if (codigo == 408 || codigo == 429 || codigo >= 500) {
resultado = 1; // falla transitoria
} else {
Serial.printf("Rechazado con código %d\n", codigo);
resultado = 2; // 400, 401, 403, 404...: no reintentar igual
}
http.end(); // liberar la conexión y la memoria
return resultado;
}
Tres decisiones de este fragmento reflejan lo aprendido. Una: los códigos negativos son errores de la biblioteca, no del servidor (conexión rechazada, tiempo agotado, sin respuesta) y se tratan como fallas de red: el dato queda en el buffer y se reintenta. Otra: cada código se traduce en una de tres acciones: listo, reintentar o descartar el reintento. Y otra más: se llama a http.end() siempre, porque la memoria de un microcontrolador es escasa y una conexión abierta olvidada la agota.
Dos cuidados de memoria y de seguridad. Primero, no cargar respuestas grandes completas en RAM: una respuesta de 100 KB no entra en un microcontrolador chico. Se lee por trozos y se procesa a medida que llega (o se limita el tamaño máximo aceptado). Segundo, las respuestas pueden venir en transferencia fragmentada (Transfer-Encoding: chunked) cuando el servidor no conoce de antemano el largo; las bibliotecas lo manejan, pero hay que contar con que no hay Content-Length. Y si se usa HTTPS, se verifica el certificado del servidor: desactivar la verificación «para que ande» deja la puerta abierta a quien se interponga (bloque de seguridad).
07 · Elegir con criterio
Cuándo conviene HTTP y cuánto cuesta
HTTP es el punto de partida más simple para llevar datos a un servidor, pero no siempre el más eficiente. Pesan tres costos que ya conocés de temas anteriores:
- Bytes. Las cabeceras de una petición suelen ser mucho más grandes que unos pocos bytes de datos. Una lectura de 53 bytes útiles puede viajar en unos 270 a 880 bytes.
- Viajes. Cada petición sobre una conexión nueva paga DNS, TCP y TLS (tema 17): hasta cinco viajes de ida y vuelta para un dato.
- Energía. Todo lo anterior es tiempo de radio encendida (tema 14).
| HTTP conviene cuando… | Conviene otro protocolo (como MQTT, tema 25) cuando… |
|---|---|
| El dispositivo envía de vez en cuando, con alimentación eléctrica o ritmo bajo | El envío es frecuente y la batería es crítica |
| Hay que integrar con servicios web existentes (APIs REST, plataformas en la nube) | El dispositivo debe recibir órdenes del servidor con poca demora |
| Se necesita atravesar firewalls corporativos restrictivos | Hay muchos dispositivos que publican y muchos consumidores que escuchan |
| La simplicidad y la facilidad de depuración pesan más que los bytes | El ancho de banda cuesta (celular, LoRa) o es muy limitado |
| El intercambio es de pedido y respuesta (consultar la configuración, enviar un lote) | Hace falta una conversación bidireccional continua |
El truco que hace razonable a HTTP en muchos casos es el que ya vimos: pocos envíos, con lotes de lecturas, cabeceras mínimas y conexión cifrada reutilizada. Un POST por hora con sesenta lecturas pesa casi lo mismo en cabeceras que un POST con una sola, y reparte esos bytes entre sesenta medidas.
08 · Analogía con programación
Medir una petición en Python
El programa construye peticiones y respuestas HTTP reales, línea por línea, y cuenta sus bytes. Prueba tres juegos de cabeceras: mínimas (solo las imprescindibles), habituales (agregan el agente de usuario, el formato aceptado y la credencial) y pesadas (suman idioma, cookie, control de caché e identificador de petición, como una aplicación web típica). El cuerpo es el mensaje compacto del tema 12 con 1, 10 o 60 lecturas. Al final imprime una petición completa y una función accion que traduce cada código de estado a una decisión.
Python en tu navegador. Los bloques son editables: cambiá los valores y presioná «Ejecutar Python». La primera vez tarda unos segundos en cargar.
import json
HOST = "datos.ejemplo.com"
RUTA = "/api/v1/lecturas"
TOKEN = "k7Qz3Wm9Xr2Lp5Ty8Vb1Nc4Jd6Hf0Gs3Aa5Ze7Uo9Ii2Eu4Ow"[:43]
COOKIE = "a1b2c3d4" * 8
UUID = "123e4567-e89b-12d3-a456-426614174000"
def cuerpo(n):
"""Mensaje compacto con n lecturas de temperatura (décimas de grado)."""
valores = [41 + (i * 3) % 8 for i in range(n)]
return json.dumps({"d": "c07", "t": 1794061920, "i": 60, "v": valores}, separators=(",", ":"))
def peticion(n, juego):
cuerpo_json = cuerpo(n)
lineas = [f"POST {RUTA} HTTP/1.1",
f"Host: {HOST}",
"Content-Type: application/json",
f"Content-Length: {len(cuerpo_json)}",
"Connection: close"]
if juego in ("habituales", "pesadas"):
lineas += ["User-Agent: sensor-esp32/1.2.0", "Accept: application/json", f"Authorization: Bearer {TOKEN}"]
if juego == "pesadas":
lineas += ["Accept-Language: es-AR,es;q=0.9", f"Cookie: sesion={COOKIE}", "Cache-Control: no-cache", f"X-Request-Id: {UUID}"]
return "\r\n".join(lineas) + "\r\n\r\n" + cuerpo_json
def respuesta(juego):
cuerpo_json = '{"ok":true}'
lineas = ["HTTP/1.1 200 OK",
"Content-Type: application/json",
f"Content-Length: {len(cuerpo_json)}",
"Connection: close"]
if juego in ("habituales", "pesadas"):
lineas += ["Date: Sat, 07 Nov 2026 14:32:01 GMT", "Server: nginx/1.25.3"]
if juego == "pesadas":
lineas += [f"Set-Cookie: sesion={COOKIE}; Path=/; HttpOnly; Secure", "Cache-Control: no-store",
f"X-Request-Id: {UUID}", "Strict-Transport-Security: max-age=31536000"]
return "\r\n".join(lineas) + "\r\n\r\n" + cuerpo_json
print("cabeceras lecturas útil petición respuesta total % útil")
for juego in ("mínimas", "habituales", "pesadas"):
clave = "minimas" if juego == "mínimas" else juego
for n in (1, 10, 60):
util = len(cuerpo(n)) + len('{"ok":true}')
pet = len(peticion(n, clave))
res = len(respuesta(clave))
total = pet + res
print(f"{juego:<12} {n:>7} {util:>6} {pet:>9} {res:>10} {total:>7} {100 * util / total:>7.1f} %")
print()
print(peticion(1, "habituales"))
def accion(codigo):
if 200 <= codigo < 300:
return "éxito: seguir"
if codigo in (301, 302, 307, 308):
return "redirección: seguir la nueva dirección, con límite de saltos"
if codigo in (408, 429, 500, 502, 503, 504):
return "falla transitoria: reintentar con espera creciente"
if 400 <= codigo < 500:
return "error del cliente: no reintentar, corregir y reportar"
return "otro: registrar y reportar"
print()
for codigo in (200, 201, 204, 302, 400, 401, 404, 408, 429, 500, 503):
print(codigo, "->", accion(codigo))
Resultado con los valores iniciales. Con cabeceras mínimas, una lectura por petición necesita 172 bytes de petición y 101 de respuesta (273 en total, de los cuales 53 son datos: 19,4 %); con 10 lecturas, 300 bytes (26,7 % útil); con 60, 451 bytes (51,0 %). Con cabeceras habituales: 457, 484 y 635 bytes (11,6 %, 16,5 % y 36,2 % útil). Con cabeceras pesadas: 881, 908 y 1059 bytes (6,0 %, 8,8 % y 21,7 %). En cuanto a los códigos, 200, 201 y 204 se consideran éxito; el 302 es una redirección; 408, 429, 500 y 503 son transitorios y se reintentan con espera creciente; y 400, 401 y 404 son errores del cliente que no se reintentan.
Las cifras dicen tres cosas. Una: con una lectura por petición, la mayor parte del tráfico son cabeceras, incluso con las mínimas. Otra: las cabeceras de más se pagan en cada envío: las «pesadas» triplican el tamaño de la conversación sin aportar nada que el dispositivo use. Y la tercera: agrupar sesenta lecturas baja el costo por lectura de 273 a 7,5 bytes, 36 veces menos, con las mismas cabeceras mínimas.
09 · Exploración interactiva
¿Cuánto pesa una petición?, de extremo a extremo
Este laboratorio construye las mismas peticiones y respuestas que el código anterior y cuenta sus bytes. Elegí cuántas lecturas lleva el cuerpo y qué conjunto de cabeceras usa el cliente. Cada barra muestra el tráfico total de una conversación completa (petición y respuesta) dividido en: la línea inicial con las cabeceras mínimas, las cabeceras adicionales de la petición, las cabeceras de la respuesta y los datos útiles.
Peso de una petición POST con una respuesta
Elegí los parámetros y presioná «Armar la petición».
Recorrido guiado
- Con los valores iniciales (10 lecturas, cabeceras habituales), armá la petición. 324 bytes de petición y 160 de respuesta: 484 en total, 16,5 % de datos y 48,4 bytes por lectura: «Aceptable».
- Cambiá a 1 lectura: 457 bytes en total y apenas 11,6 % de datos. «Casi todo es sobre»: 457 bytes de tráfico por una sola medida.
- Pasá a cabeceras mínimas con 1 lectura: 273 bytes y 19,4 %. Quitar cabeceras que el servidor no necesita ahorró 184 bytes por envío.
- Elegí Con cookies y rastreo y 1 lectura: 881 bytes y 6,0 % de datos. Las cabeceras de una aplicación web pesan más del triple que las mínimas.
- Con cabeceras mínimas, subí a 60 lecturas: 451 bytes en total, 51,0 % de datos y 7,5 bytes por lectura: «Eficiente». El mismo cliente, 36 veces menos tráfico por lectura.
- Con 60 lecturas y cabeceras pesadas: 1059 bytes y 21,7 %. Aun así 17,7 bytes por lectura, menos que una lectura suelta con cabeceras mínimas. Agrupar es más rentable que podar cabeceras, y lo mejor es hacer ambas cosas.
Desafío: el costo de una cookie
La cookie del juego pesado ocupa 81 bytes en la petición (Cookie: sesion=… más el salto de línea) y 111 en la respuesta (Set-Cookie con sus atributos): 192 bytes por intercambio. Si el dispositivo envía cada 15 minutos (96 veces por día), son 18.432 bytes diarios, unos 0,55 MB al mes por equipo, solo para llevar una cookie que el dispositivo ni siquiera usa. En una flota de 10.000 equipos son 5,5 GB al mes. Con un plan celular de US$ 2 por MB, ese único campo innecesario costaría unos US$ 11.000 al mes. En IoT cada cabecera se multiplica por todos los envíos de todos los equipos: conviene preguntarse, para cada una, quién la usa.
10 · Método de trabajo
Lista de verificación de un cliente HTTP
- HTTPS siempre para credenciales y datos, con verificación del certificado.
- Cabeceras mínimas:
Host,Content-Type,Content-Length,Authorizationy poco más. - Un token por dispositivo, revocable, guardado en memoria no volátil.
- Tiempos máximos en la conexión y en la lectura de la respuesta.
- Decisión por código: éxito, reintento con espera creciente o descarte del reintento, según la tabla de estados; respetar
Retry-After. - Lotes: varias lecturas por petición, con identificador para descartar duplicados.
- Memoria: limitar el tamaño de las respuestas aceptadas y liberar siempre la conexión.
- Redirecciones con límite de saltos y sin bajar de HTTPS a HTTP.
- Hora: aprovechar la cabecera
Datede las respuestas para mantener el reloj. - Pruebas: contra un servidor que responda cada código (200, 401, 404, 429, 500, 503), que tarde de más y que corte a mitad de la respuesta.
Una herramienta valiosa en el escritorio: probá primero la misma petición con curl -v y comprobá que el servidor responde lo esperado. Si anda en curl y no en el dispositivo, el problema está en el firmware o en la red del dispositivo, no en la interfaz.
11 · Comprensión
Confusiones frecuentes
«Si el código es distinto de 200, hay que reintentar»
Solo los errores transitorios (408, 429, 5xx) se reintentan. Un 400, 401, 403 o 404 volverá a fallar igual: reintentar solo gasta batería y tráfico, y puede hacer que el servidor bloquee al dispositivo.
«HTTP es solo para páginas web»
También es el transporte de la mayoría de las APIs de servicios en la nube. Un dispositivo que envía datos con POST usa HTTP igual que cualquier aplicación; lo que cambia son las restricciones de energía y de memoria.
«Las cabeceras no importan, son unos pocos bytes»
Con mensajes de decenas de bytes, las cabeceras son la mayor parte del tráfico, y se pagan en cada envío de cada equipo. Multiplicadas por una flota, son costo real en datos y en energía.
«Un token en una cabecera es seguro porque va oculto»
No va oculto: es texto plano. Solo está protegido si la conexión es HTTPS. Sobre HTTP, cualquiera en el camino lo lee y lo reutiliza.
«Para ahorrar tiempo puedo desactivar la verificación del certificado»
Funciona hasta que alguien se interpone en la red: sin verificación, el dispositivo envía sus credenciales y sus datos a un impostor sin darse cuenta. Si no anda con la verificación, se corrige la causa (certificado raíz, reloj), no se la desactiva.
«Las respuestas siempre caben en memoria»
Una respuesta inesperadamente grande (una página de error, una descarga) puede agotar la RAM y colgar el dispositivo. Se limita el tamaño aceptado y se lee por trozos.
12 · Práctica guiada
Predecí antes de comprobar
1. Armar una cabecera
El cuerpo de una petición es {"t":22.3}. ¿Qué valor lleva la cabecera Content-Length? ¿Y qué cabecera indica que es JSON?
Ver solución
El cuerpo tiene 10 caracteres, así que Content-Length: 10. El formato se indica con Content-Type: application/json. Si Content-Length no coincide con el tamaño real, el servidor puede cortar el mensaje o quedarse esperando bytes que no llegan.
2. Qué hacer con cada código
Indicá si reintentarías y cómo: (a) 503; (b) 401; (c) 429 con Retry-After: 120; (d) 404; (e) 500 por tercera vez seguida.
Ver solución
(a) Sí, con espera creciente y retardo aleatorio. (b) No sin cambios: la credencial es inválida; renovarla o reportar. (c) Sí, pero esperando 120 segundos como indica el servidor, y reducir la frecuencia de envíos. (d) No: la dirección no existe; revisar la configuración y reportar. (e) Un 500 repetido ya no parece transitorio: espaciar los intentos (disyuntor), conservar los datos en el buffer y reportar.
3. Idempotencia
Un POST de una lectura llega al servidor pero la respuesta se pierde, y el dispositivo lo reenvía. ¿Qué ocurre y cómo se evita el daño?
Ver solución
El POST no es idempotente: el servidor recibe dos veces la misma lectura y, sin protección, la guarda duplicada. Se evita incluyendo en el cuerpo un identificador único (dispositivo y momento o secuencia) para que el servidor la descarte, o usando un método idempotente como PUT sobre un recurso con identificador propio.
4. Costo de las cabeceras
Una lectura suelta con cabeceras mínimas pesa 273 bytes en total (53 de datos). Sesenta lecturas en un lote con las mismas cabeceras pesan 451. ¿Cuántos bytes por lectura en cada caso y cuántas veces menos?
Ver solución
Suelta: 273 bytes por lectura. En lote: 451 / 60 ≈ 7,5 bytes por lectura. Es 36 veces menos tráfico por lectura con las mismas cabeceras, sin perder ninguna medida (y con una sola conexión en lugar de sesenta).
5. Redirección
Una petición a http://datos.ejemplo.com/api recibe un 301 con Location: https://datos.ejemplo.com/api. ¿Qué debe hacer el dispositivo? ¿Y si el Location apuntara a un servidor http:// distinto?
Ver solución
Seguir la redirección hacia HTTPS, con un límite de saltos (por ejemplo, 3), y guardar la nueva dirección para usarla directamente la próxima vez (cada redirección cuesta un viaje más). Si el destino bajara de HTTPS a http:// o a un servidor no previsto, no debe seguirlo: podría enviar la credencial en claro o a un impostor.
6. Errores de la biblioteca
Una llamada a http.POST devuelve −1. ¿Qué significa en general y qué hace el firmware con la lectura?
Ver solución
Un valor negativo no es un código HTTP: es un error de la biblioteca, por ejemplo conexión rechazada o tiempo agotado (sin red, sin DNS, servidor inaccesible). El firmware lo trata como una falla de red: conserva la lectura en el buffer, aplica la espera creciente y reintenta más tarde, sin descartar el dato.
13 · Síntesis
Ideas para recordar
- HTTP es el idioma de la web y de la mayoría de las APIs: simple, universal, fácil de depurar, aunque pensado para páginas y no para sensores con batería.
- Una conversación es un pedido y una respuesta, cada uno con una línea inicial, cabeceras, una línea en blanco y un cuerpo opcional; en HTTP/1.1 es texto plano.
- El método dice qué hacer: GET lee, POST envía datos, PUT fija un recurso, DELETE lo borra; GET y PUT son idempotentes, POST no.
- Hay que enviar solo las cabeceras necesarias;
Authorizationdebe ir siempre sobre HTTPS, con un token distinto por dispositivo. - Los códigos 2xx son éxito; los 4xx son errores del cliente que no se reintentan igual (salvo 408 y 429); los 5xx suelen ser transitorios y se reintentan con espera creciente.
- El
Retry-Afterde un 429 o un 503 se obedece. - Un cliente HTTP en un microcontrolador pone tiempos máximos, traduce cada código en una acción, trata los códigos negativos como fallas de red, limita el tamaño de las respuestas y libera siempre la conexión.
- Se verifica el certificado del servidor; desactivarlo deja al dispositivo expuesto a impostores.
- Las cabeceras son la mayor parte del tráfico de un mensaje corto; agrupar lecturas en lotes y podar cabeceras reduce el costo por lectura decenas de veces.
¿Listo para el tema 20?
Antes de avanzar, probá con curl -v una petición POST a un servicio de prueba y mirá cada línea de la conversación. Contá los bytes de petición y respuesta, marcá cuáles cabeceras el servicio realmente necesita y calculá cuánto ahorrarías en una flota de mil equipos si las demás se quitaran.
En el próximo tema trabajamos con las peticiones GET y POST: cómo el dispositivo lee su configuración del servidor, cómo envía lecturas y qué diferencias hay en el diseño de uno y otro.