
Seguimos con nuestra sección del ESP8266 viendo cómo recibir datos y ejecutar acciones a través de peticiones AJAX con la información codificada en JSON.
En entradas anteriores ya habíamos llegado a ver cómo servir un API REST que reciba y devuelva información en JSON. Para ello, habíamos usado nuestro API Rest de ejemplo, formulado como vimos en esta entrada sobre NodeJs.
Ahora nos toca “bajar a tierra” todos estos conocimientos, y convertirlo en algo más concreto y aplicable al ESP8266, que nos permita interactuar con él, leyendo información, o realizando acciones.
Por tanto ¡tranquilos!. Esta entrada va a ser más sencilla que la anterior, así que si entendisteis la entrada del API REST, esta debería ser coser y cantar.
Supongamos que quisiéramos activar o desactivar una entrada digital en el ESP8266. Por otro lado, también queremos obtener información, por ejemplo del estado de un pin, o de una variable, o la lectura de un sensor (o lo que fuera).
¿Cómo hacemos un API Rest funcional, que reciba peticiones AJAX con información en Json? Venga, manos a la obra.
Nuestro programa principal es bastante sencillo, e idéntico al que vimos en la entrada del API Rest.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#include #include #include #include #include “config.h” // Sustituir con datos de vuestra red #include “API.hpp” #include “Server.hpp” #include “ESP8266_Utils.hpp” void setup(void) { Serial.begin(115200); SPIFFS.begin();
ConnectWiFi_STA(); InitServer(); } void loop(void) { } |
Por otro lado, nuestro fichero ‘Server.hpp’ que contiene la definición del servidor, sí ha cambiado, pasando a ser el siguiente,
AsyncWebServer server(80); void InitServer() { server.serveStatic(“/”, SPIFFS, “/”).setDefaultFile(“index.html”); server.on(“/LED”, HTTP_GET, getData); server.on(“/LED”, HTTP_POST, [](AsyncWebServerRequest * request){}, NULL, setData);
server.onNotFound([](AsyncWebServerRequest *request) { request->send(400, “text/plain”, “Not found”); }); server.begin(); Serial.println(“HTTP server started”); } |
Donde hemos definido dos endpoint a la URI ‘/LED’ para peticiones GET y POST respectivamente, y hemos asociado las funciones de callback correspondientes.
Estás están definidas en nuestro fichero ‘API.hpp’ que, ahora sí, es mucho más sencillo que en la entrada anterior sobre el API Rest genérico.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
#include “ESP8266_Utils_APIREST.hpp” void getData(AsyncWebServerRequest *request) { AsyncResponseStream *response = request->beginResponseStream(“application/json”);
// obtendríamos datos de GPIO, estado… StaticJsonDocument<300> jsonDoc; jsonDoc[“id”] = random(0,10); jsonDoc[“status”] = random(0,2); serializeJson(jsonDoc, *response);
request->send(response); } void setData(AsyncWebServerRequest * request, uint8_t *data, size_t len, size_t index, size_t total) { String bodyContent = GetBodyContent(data, len); StaticJsonDocument<200> doc; DeserializationError error = deserializeJson(doc, bodyContent); if (error) { request->send(400); return;} int id = doc[“id”]; bool ledStatus = doc[“status”]; Serial.println(id); Serial.println(ledStatus); // hacer acciones con los datos recibidos request->send(200); } |
Como vemos, tenemos únicamente dos funciones de callback, ‘getData’ y ‘setData’. En la función ‘getData’ devolvemos una cierta información en JSON con Id y Status. En el ejemplo usamos un número aleatorio pero en nuestro proyecto real leeríamos el estado de un pin, un sensor (o lo que fuera).
Por otro lado, la función ‘setData’ realiza acciones sobre el ESP8266. También recibe un JSON con Id y Status. En el ejemplo, simplemente lo mostramos por pantalla pero, nuevamente, en un ejemplo real actuaríamos sobre una salida digital, un actuador (o lo que quisiéramos).
Por su parte, nuestra página web ‘index.html’ es muy sencilla para este ejemplo,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
class=“no-js”lang=“”>
charset=“utf-8”>
http-equiv=“x-ua-compatible” content=“ie=edge”>
name=“description” content=“”>
name=“viewport” content=“width=device-width, initial-scale=1”>
Value: type=“text” id=“ledNumber” name=“ledNumber” value=“10” checked> type=“radio” id=“ledOn” name=“status” value=“true” checked> type=“radio” id=“ledOff” name=“status” value=“false”>
|
Donde únicamente tenemos un textbox para introducir el número de pin sobre el que queremos actuar (simulado), y un radio button para elegir el estado ON/OFF. Por otro lado, un botón para solicitar información al ESP8266, y un label para el texto recibido.
Y para que todo funcione, necesitamos un pequeño fichero Javascript ‘main.js’ que contiene lo siguiente,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
function sendData() { var xhttp = new XMLHttpRequest(); let ledNumber = document.getElementById(‘ledNumber’); let ledStatus = document.querySelector(‘input[name=”status”]:checked’); let ledData = { id: ledNumber.value, status: ledStatus.value } let data = JSON.stringify(ledData); xhttp.addEventListener(‘load’, function(event) { console.log(‘OK’, xhttp); }); xhttp.addEventListener(‘error’, function(event) { console.log(‘error’, xhttp); }); xhttp.open(‘POST’, ‘LED’); xhttp.setRequestHeader(‘Content-Type’, ‘application/json’); xhttp.send(data); } function getData() { var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { if (xhttp.readyState == XMLHttpRequest.DONE) { if (xhttp.status == 200) { console.log((xhttp.responseText)); let json = JSON.parse(xhttp.responseText); console.log();
let receivedMsg = ‘Received: GPIO ‘ + json.id + ‘ ‘ + (json.status == 1 ? “ON” : “OFF”); document.getElementById(‘receivedText’).textContent = receivedMsg; } else { console.log(‘error’, xhttp); } } }; xhttp.open(“GET”, “LED”, true); xhttp.send(); } |
Donde estamos usando un poco de Vanilla Javascript (Javascript “a pelo”, sin librerías) para enviar y recibir la información al ESP8266.
Ahora subimos todo a nuestro ESP8266 y ejecutamos. Al pulsar en los botones veremos que, efectivamente, en el puerto serie el ESP8266 parsea correctamente la información recibida, y muestra las variables en el puerto serie.
Por otro lado, si pulsamos en el botón, veremos en la consola del desarrollador del navegador que recibimos y parseamos correctamente la información, y que se actualiza el label oportuno en la página web.
Esta es la “forma correcta” y limpia de encender un LED a través de Web en el ESP8266. Nada de peticiones GET con parámetros /?LED=ON ni cosas de esas sucias.
¿Complicado? Bueno, espero que si habéis seguido la serie hasta aquí, no lo haya sido tanto. ¡Pero aún podemos mejorarlo! En la próxima entrada veremos cómo hacer algo parecido a través de websockets. ¡Hasta pronto!
Descarga el código
Todo el código de esta entrada está disponible para su descarga en GitHub.