devicescript-http-fetch-api

Peticiones HTTP con fetch en DeviceScript

  • 5 min

La función fetch de DeviceScript permite realizar peticiones HTTP o HTTPS mediante una API asíncrona similar a la del navegador.

En el ecosistema Arduino/C++, realizar una petición web segura (HTTPS) suele ser una tarea titánica. Tienes que gestionar los certificados SSL, asegurarte de que la respuesta cabe en la RAM, y luego usar una librería como ArduinoJson para intentar descifrar esa sopa de letras que recibes.

Podemos descargar texto, procesar JSON y enviar datos sin incorporar una pila HTTP externa. Seguimos trabajando en un microcontrolador, por lo que el tamaño de las respuestas y el coste de TLS importan.

La función fetch

La API Fetch es el estándar moderno para realizar peticiones de red asíncronas. En DeviceScript, se importa desde @devicescript/net.

La estructura básica es:

Llamamos a fetch(url).

Esperamos (await) a que el servidor responda.

Convertimos la respuesta a texto o JSON.

Esperamos (await) a que se procese ese contenido.

Petición GET: leer un JSON

Vamos a empezar con lo más básico: consultar una API pública para obtener datos. Usaremos el clásico servicio de pruebas JSONPlaceholder.

Vamos a obtener la información de una tarea de prueba.

import { fetch } from "@devicescript/net"

// URL de prueba que devuelve un JSON simple
const URL = "https://jsonplaceholder.typicode.com/todos/1"

async function obtenerDatos() {
    console.log("Iniciando petición GET...")

    try {
        // 1. Hacemos la petición
        const respuesta = await fetch(URL)

        // 2. Comprobamos si el servidor nos ha dado el OK (Código 200-299)
        if (respuesta.status === 200) {
            
            // 3. Parseamos el JSON
            const datos = await respuesta.json()

            // 4. Usamos los datos como un objeto normal de JS
            console.log("Datos recibidos:")
            console.log(`ID: ${datos.id}`)
            console.log(`Título: ${datos.title}`)
            console.log(`Completado: ${datos.completed}`)

        } else {
            console.error(`Error en el servidor: ${respuesta.status}`)
        }

    } catch (error) {
        // Capturamos errores de red (ej: WiFi caído)
        console.error("Error de red:", error)
    }
}

// Ejecutamos
obtenerDatos()
Copied!

Analicemos las diferencias con C++

  • Nada de WiFiClientSecure: DeviceScript gestiona la conexión segura TLS por debajo.
  • Nada de DeserializationError: .json() devuelve un objeto de JavaScript listo para usar.
  • Memoria: El sistema gestiona los búferes, pero la respuesta debe caber en la memoria disponible. Mantén los documentos pequeños.

El simulador puede usar la red del ordenador, lo que permite comprobar la respuesta de la API antes de probarla en la placa.

Petición POST: enviar datos

Leer es fácil, pero para el IoT solemos querer enviar datos (telemetría de sensores) a un servidor. Para eso usamos el método POST.

Supongamos que queremos enviar la temperatura actual a un servidor. Necesitamos configurar un poco más la llamada fetch:

import * as ds from "@devicescript/core"
import { fetch } from "@devicescript/net"

async function enviarTelemetria(temperatura: number) {
    const URL_POST = "https://jsonplaceholder.typicode.com/posts"
    
    console.log(`Enviando temperatura: ${temperatura}...`)

    try {
        const respuesta = await fetch(URL_POST, {
            method: "POST", // Indicamos que vamos a escribir
            headers: {
                // Importante: Decirle al servidor que enviamos JSON
                "Content-Type": "application/json" 
            },
            // Convertimos nuestro objeto JS a texto para enviarlo
            body: JSON.stringify({
                sensor: "ESP32-Lab",
                temp: temperatura,
                uptime: ds.millis()
            })
        })

        if (respuesta.status === 201) { // 201 = Created
            console.log("¡Datos guardados correctamente!")
            const confirmacion = await respuesta.json()
            console.log(confirmacion)
        } else {
            console.error(`Fallo al enviar: ${respuesta.status}`)
        }

    } catch (e) {
        console.error("Error de envío", e)
    }
}

// Simulamos un envío
enviarTelemetria(24.5)
Copied!

JSON.stringify() convierte un objeto en texto JSON. DeviceScript incorpora esta función, de modo que no necesitamos construir la cadena manualmente.

Límites que conviene tener presentes

Seguimos trabajando en un microcontrolador, no en un servidor. Hay límites.

Memoria RAM

Al hacer await response.json(), todo el objeto debe caber en la memoria RAM del dispositivo.

  • Si la API devuelve una lista de 5000 elementos, el ESP32 se quedará sin memoria (OOM) y se reiniciará.
  • Solución: Si controlas la API, aplica paginación o filtrado para recibir solo los campos necesarios. DeviceScript no es adecuado para procesar megabytes de JSON.
HTTPS y TLS

DeviceScript soporta HTTPS, pero TLS consume CPU y una parte importante de la memoria. Además, solo puede mantener un socket abierto a la vez.

Tiempo de espera

Las peticiones pueden tardar. Si el servidor es lento, la función async permanecerá suspendida en await. Diseña un tiempo de espera y una estrategia de error acordes con la lógica de la aplicación.

Ejemplo: consultar una API meteorológica

Para cerrar, un reto habitual. ¿Qué tiempo hace? Podemos consultar una API meteorológica para decidir si encendemos un LED. Necesitarás una clave propia del proveedor.

Nota: Este es un esquema conceptual.

// Esquema lógico
const API_KEY = "TU_API_KEY"
const CIUDAD = "Madrid"
const URL = `https://api.openweathermap.org/data/2.5/weather?q=${CIUDAD}&appid=${API_KEY}`

async function consultarClima() {
    const res = await fetch(URL)
    const data = await res.json()
    
    // Accedemos a la estructura profunda del JSON
    const clima = data.weather[0].main // Ej: "Rain", "Clear"
    
    if (clima === "Rain") {
        console.log("Está lloviendo, cerrar toldo.")
        // await motorToldo.start()
    }
}
Copied!