Módulo Http
El módulo Http es un cliente HTTP: hace peticiones GET y POST a una dirección y te devuelve lo que contesta el servidor. Sirve para hablar con APIs REST, servicios web o aparatos de tu red. Las cabeceras que pongas valen para todas las peticiones siguientes, y status() y error() cuentan cómo fue la última.
use Http

Plataformas
| Windows | ESP32 / ESP32-S3 | ESP32-P4 | ESP32-S31 | |
|---|---|---|---|---|
| La red | La del PC | La WiFi del perfil de la placa | Igual | Igual |
| HTTPS | Sí, y Windows comprueba el certificado del servidor | Sí, pero sin comprobar el certificado del servidor | Sí, y comprueba el certificado del servidor | Igual |
| Redirecciones (301, 302…) | Las sigue y devuelve la página final | No las sigue: devuelve la respuesta de la redirección | Igual | Igual |
Http.status() si no hay respuesta | 0, o un número negativo de -1 a -5 | Un número negativo, de -1 a -11 (0 si la dirección no se entiende) | 0, o -1 si no llega la respuesta | 0, o -1 si no llega la respuesta |
Con Content-Type en setHeader, un post manda | El de setHeader | El de post | El de setHeader | El de setHeader |
No hay un módulo WiFi: la placa se conecta sola al arrancar, con la red que tenga su perfil en ESP32 Tools. Lo cuenta Pantalla, sonido, SD y red.
Funciones
| Función | Qué hace | Devuelve |
|---|---|---|
Http.get(url) | Hace una petición GET | El cuerpo de la respuesta, o nil si no hay respuesta |
Http.post(url, cuerpo) / Http.post(url, cuerpo, tipo) | Hace una petición POST | El cuerpo de la respuesta, o nil si no hay respuesta |
Http.setHeader(nombre, valor) | Pone una cabecera para las peticiones siguientes | true |
Http.clearHeaders() | Quita todas las cabeceras | true |
Http.status() | El código de estado de la última petición | Un entero |
Http.error() | El error de la última petición | Una cadena; vacía si no hubo error |
Http.timeout() / Http.timeout(ms) | Lee o cambia el tiempo de espera | Los milisegundos, o true al cambiarlo |
Mientras espera la respuesta, el programa se para: no se pinta la pantalla ni corren los callbacks de Timer, MQTT o GPIO.
Si les pasas argumentos que no son los que esperan, ninguna lanza una excepción: devuelven un valor de error, que try no captura (ver Errores y excepciones).
get(url)
Pide la dirección url, que empieza por http:// o https://, y devuelve el cuerpo de la respuesta como cadena.
- Si el servidor contesta, devuelve lo que manda, también si contesta con un error como 404 o 500. Mira
Http.status()para saber si fue bien. - Si no hay respuesta (no hay red, no encuentra el servidor, se acaba el tiempo…), devuelve
nil, yHttp.error()dice qué pasó.
use Http
var respuesta = Http.get("https://api.ejemplo.com/datos")
if (respuesta == nil) then
println("Sin respuesta: ", Http.error())
elsif (Http.status() == 200) then
println("Respuesta: ", respuesta)
else
println("El servidor contesta ", Http.status())
end
post(url, cuerpo, tipo)
Manda cuerpo, una cadena, a la dirección url con una petición POST. tipo es opcional: el tipo del contenido, que se manda en la cabecera Content-Type; si no lo pones, "application/json". Devuelve lo mismo que get.
use Http
var datos = '{"temperatura": 25.5, "humedad": 60}'
var respuesta = Http.post("https://api.ejemplo.com/sensores", datos)
if (respuesta != nil) then
println("El servidor contesta ", Http.status(), ": ", respuesta)
else
println("Error en el POST: ", Http.error())
end
Con otro tipo de contenido, por ejemplo un formulario:
use Http
var formulario = "usuario=admin&clave=secreto"
var respuesta = Http.post("https://api.ejemplo.com/login", formulario, "application/x-www-form-urlencoded")
Si has puesto una cabecera Content-Type con setHeader, en Windows, el P4 y el S31 se manda esa, y en ESP32 y ESP32-S3, la de post.
setHeader(nombre, valor)
Pone la cabecera nombre con el valor valor en todas las peticiones siguientes, hasta que llames a clearHeaders(). Si ya había una con ese nombre, cambia su valor. Devuelve true.
use Http
Http.setHeader("Authorization", "Bearer mi_token")
Http.setHeader("X-API-Key", "clave_secreta")
// Las dos cabeceras van en esta petición y en las siguientes
var respuesta = Http.get("https://api.ejemplo.com/privado")
clearHeaders()
Quita todas las cabeceras que hayas puesto con setHeader y devuelve true. Úsalo al pasar de una API a otra.
use Http
Http.setHeader("Authorization", "Bearer token_de_la_api_1")
Http.get("https://api1.ejemplo.com/datos")
Http.clearHeaders()
Http.setHeader("X-Custom-Header", "otro_valor")
Http.get("https://api2.ejemplo.com/otros-datos")
status()
Devuelve el código de estado HTTP de la última petición: el número con el que contesta el servidor.
| Código | Significado |
|---|---|
200 | Bien |
201 | Creado |
301, 302 | La página se ha movido (en las placas, que no siguen las redirecciones) |
400 | La petición está mal hecha |
401 | Falta autenticarse |
403 | No tienes permiso |
404 | No existe |
500 | Error del servidor |
Si no hubo respuesta, vale 0 o un número negativo, según la plataforma (ver Plataformas). Antes de la primera petición, vale 0.
use Http
var respuesta = Http.get("https://api.ejemplo.com/usuario/123")
var codigo = Http.status()
switch (codigo)
case 200:
println("Usuario: ", respuesta)
case 404:
println("Ese usuario no existe")
case 401:
println("No autorizado: mira el token")
default:
println("Código de respuesta: ", codigo)
end
error()
Devuelve el error de la última petición, o una cadena vacía si no lo hubo. Una respuesta con un código de error (404, 500…) no es un error para error(): el servidor ha contestado.
| Mensaje | Dónde | Cuándo |
|---|---|---|
Invalid URL | Windows | La dirección no se entiende. |
Connection error | Windows | No se puede conectar con el servidor. |
Network error | Windows | No llega la respuesta a tiempo, u otro fallo de la red. |
Protocol error, Read error, Write error, Generic error | Windows | Falla la conversación con el servidor. |
begin() failed | ESP32 y ESP32-S3 | La dirección no se entiende. |
GET failed, POST failed | Todas las placas | La petición no llega a hacerse: no hay red, no encuentra el servidor, se acaba el tiempo… En ESP32 y ESP32-S3, Http.status() dice el motivo con su número negativo. |
init() failed, write() short | ESP32-P4 y ESP32-S31 | No se puede preparar la petición, o no se puede mandar el cuerpo entero. |
El nombre del error del chip (ESP_ERR_…) | ESP32-P4 y ESP32-S31 | No se puede abrir la conexión. |
use Http
var respuesta = Http.get("https://servidor-que-no-existe.ejemplo/api")
if (respuesta == nil) then
println("La petición falló: ", Http.error())
end
timeout(ms)
Sin argumentos, devuelve el tiempo máximo que espera cada petición, en milisegundos: de serie, 10000 (10 segundos). Con ms, un entero, lo cambia y devuelve true; un valor negativo cuenta como 0.
En Windows, la petición se abandona cuando pasa ese tiempo y un segundo más de margen.
use Http
println("Espera: ", Http.timeout(), " ms") // Espera: 10000 ms
Http.timeout(30000) // 30 segundos, para una API lenta
Ejemplo completo
Un sensor que manda su lectura a una API cada 30 segundos:
use Http, GPIO
Http.timeout(15000)
Http.setHeader("X-Device-ID", "esp32-sensor-001")
var pinSensor = 34
GPIO.pinmode(pinSensor, GPIO.INPUT)
while (true)
var lectura = GPIO.aread(pinSensor)
var temperatura = lectura * 0.1 // la conversión de tu sensor
var cuerpo = '{"aparato": "esp32-001", "temp": ' + temperatura.toString() + '}'
var respuesta = Http.post("https://api.miservidor.com/telemetria", cuerpo)
if (respuesta == nil) then
println("Sin respuesta: ", Http.error())
elsif (Http.status() == 200 or Http.status() == 201) then
println("Enviado")
else
println("El servidor contesta ", Http.status())
end
pause(30000)
end
Con autenticación
Primero pide un token, y después lo manda en la cabecera Authorization. Lee la respuesta con JSON:
use Http, JSON
var acceso = '{"email": "usuario@ejemplo.com", "clave": "clave123"}'
var respuesta = Http.post("https://api.ejemplo.com/auth/login", acceso)
if (respuesta == nil or Http.status() != 200) then
println("No he podido entrar: ", Http.status(), " ", Http.error())
exit(1)
end
var sesion = JSON.parse(respuesta)
Http.setHeader("Authorization", "Bearer " + sesion["token"])
var perfil = Http.get("https://api.ejemplo.com/usuarios/perfil")
if (perfil != nil and Http.status() == 200) then
var datos = JSON.parse(perfil)
println("Hola, ", datos["nombre"])
end
Con reintentos
Reintenta si no hay respuesta o si falla el servidor (códigos 500 y siguientes), pero no si el error es de la petición:
use Http
func pedir(url)
for (var intento = 1; intento <= 3; intento++)
var respuesta = Http.get(url)
if (respuesta == nil) then
println("Sin respuesta (", Http.error(), "), intento ", intento)
elsif (Http.status() >= 500) then
println("Error del servidor (", Http.status(), "), intento ", intento)
elsif (Http.status() >= 400) then
println("Error de la petición: ", Http.status())
return nil
else
return respuesta
end
pause(2000)
end
return nil
end
var datos = pedir("https://api.ejemplo.com/datos")
if (datos != nil) then
println("Datos: ", datos)
end
Trampas y problemas conocidos
- Un 404 no da
nil.getypostdevuelven lo que contesta el servidor, sea cual sea el código: mira siempreHttp.status(). - Los errores de los argumentos no se capturan. Un argumento de otro tipo, como
Http.get(5), no lanza una excepción: devuelve un valor de error. - El programa espera. Una petición para el programa hasta que llega la respuesta o se acaba el tiempo: en un juego, la pantalla se congela ese rato.
- Las cabeceras se quedan. Lo que pones con
setHeaderva en todas las peticiones siguientes, también a otros servidores. UsaclearHeaders()al cambiar de API. - HTTPS en ESP32 y ESP32-S3 no comprueba el certificado. La conexión va cifrada, pero la placa no comprueba que el servidor sea quien dice ser. No mandes así datos que tengan que estar protegidos.
- Las redirecciones en las placas. Si la dirección redirige a otra (por ejemplo, de
http://ahttps://), en la placa recibes el código301o302: pon en el programa la dirección final.
Todos los problemas conocidos de JARU, juntos, están en Problemas conocidos.