- El próximo feriado. Panel inteligente con e-Paper. Parte 3
- El próximo feriado. Panel inteligente con e-Paper. Parte 2
- El próximo feriado. Panel inteligente con e-Paper. Parte 1
En la primera parte de esta serie de artículos te propuse construir este panel inteligente para mostrar la cantidad de días que faltan para el próximo feriado. En esa ocasión vimos el funcionamiento general del proyecto y también qué es el protocolo NTP y el formato epoch, además de cómo utilizarlos para obtener la hora con precisión. En esta segunda parte te voy a explicar cómo realizar solicitudes HTTPS a un servidor y cómo procesar su respuesta en formato JSON.
Solicitudes HTTPS
El protocolo NTP que usamos antes es una forma de acceder a un servidor en Internet, en este caso específico, para obtener la información de hora y fecha.
Existen otros protocolos que sirven para intercambiar otro tipo de información. Uno de los más usados es HTTP, que permite acceder a sitios web, obtener datos de ellos e incluso enviarles información.
El protocolo HTTP
HTTP nació junto con la Web, a comienzos de la década de 1990, y define la forma en que viajan los datos entre usuarios y servidores. Funciona bajo un modelo cliente–servidor, donde cada parte tiene un rol claro: el servidor almacena la información y el cliente la solicita.
Cada vez que abrimos una página en Internet utilizamos HTTP. Nuestro navegador actúa como cliente y envía una solicitud (request) al servidor. Si todo está correcto, el servidor responde con los datos pedidos —generalmente una página con distintos elementos— en lo que se llama una respuesta (response). Si ocurre algún problema o la información no existe, devuelve un mensaje de error, como el famoso error 404 (Not Found).
Para que esta comunicación sea posible, el cliente debe conocer la URL, es decir, la dirección del servidor en Internet y debe indicar al servidor que acción desea realizar. Para esto, HTTP define varios métodos, de los cuales los mas usados son dos: GET y POST.
- GET: El cliente indica al servidor que quiere recibir una información.
- POST: El cliente indica al servidor que quiere enviarle información.
Si bien es cierto que muchas veces se usa GET, lo mas apropiado es usar POST cuando necesitamos enviar información a un servidor.
Añadiendo seguridad
El protocolo HTTP no es seguro. Si en el camino entre el cliente y el servidor un tercero intercepta la comunicación, puede obtener con facilidad los datos que se están enviado. Imagina que estás haciendo una compra con tu tarjeta: cualquier hacker podría robarte esa información tan sensible!
Por esta razón se hizo una modificación al protocolo HTTP original y surgió HTTPS. Como habrás deducido, la «S» adicional viene de Seguro (Secure). HTTPS es HTTP mas una capa de seguridad que brinda dos características fundamentales:
- Cifrado: A diferencia de HTTP que transmite la información como texto plano, HTTPS encripta la información, de manera que si es interceptada, no pueda ser utilizada.
- Autenticación: El servidor presenta un certificado digital que comprueba su identidad. Esto asegura que el cliente accede al sitio verdadero y no a un impostor.
Hoy en día, la mayoría de los servidores utilizan HTTPS en lugar de HTTP. Incluso es común que una URL comience con http://, pero al acceder, el propio sitio redirija automáticamente a su versión segura https://.
Accediendo con Arduino
Veamos entonces como podemos acceder a un servidor que usa HTTPS para intercambiar información usando el lenguaje Arduino en nuestro ESP32. Para ello tenemos a nuestra disposición dos librerías:
- WiFiClientSecure: Realiza la conexión con el servidor empleando HTTPS. Maneja el cifrado y los certificados.
- HTTPClient: Implementa los métodos HTTP, como GET y POST. Se la puede emplear tanto con HTTP como con HTTPS.
Veamos el siguiente ejemplo, que hace una conexión HTTPS y una solicitud GET. Para simplificar y como conocemos el servidor al que apuntamos, no usaremos certificados.
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <HTTPClient.h>
const char* ssid = "yourSSID";
const char* password = "yourPASS";
String url = "https://httpbin.org/get"; // URL del sitio de pruebas
void setup() {
Serial.begin(115200);
//Conectar al WiFi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi connected!");
WiFiClientSecure client; // Crear objeto para la conexión (HTTPS)
client.setInsecure(); // HTTPS cifrado, sin validar certificado
HTTPClient http; // Crear objeto para gestionar solicitudes HTTP (GET/POST)
// Inicializa la petición HTTP/HTTPS (configura URL + cliente TLS)
if (!http.begin(client, url)) {
Serial.println("http.begin() failed");
delay(5000);
return;
}
// Envía el GET y obtiene el código HTTP de respuesta
int httpCode = http.GET();
Serial.printf("HTTP code: %d\n", httpCode); // Código de estado HTTP
if (httpCode != HTTP_CODE_OK) {
// Hubo algun problema
Serial.println("Respuesta del server (primeros 200 chars):");
String errBody = http.getString();
Serial.println(errBody.substring(0, 200));
http.end();
delay(10000);
return;
}
// La solicitud fue exitosa
String payload = http.getString();
http.end();
Serial.printf("Payload length: %d bytes\n", payload.length());
Serial.println("Payload:");
Serial.println(payload);
}
void loop() {
//Nada que hacer
}Si lo haces funcionar y no hay ningún inconveniente, deberías ver algo así en el monitor serie:
WiFi connected!
HTTP code: 200
Payload length: 298 bytes
Payload:
{
"args": {},
"headers": {
"Accept-Encoding": "identity;q=1,chunked;q=0.1,*;q=0",
"Host": "httpbin.org",
"User-Agent": "ESP32HTTPClient",
"X-Amzn-Trace-Id": "Root=1-6986747c-3f89c6f816ecac1d6f101d05"
},
"origin": "192.141.210.29",
"url": "https://httpbin.org/get"
}
El primer mensaje corresponde a la conexión exitosa del WiFi y luego se muestra el código de estado de la solicitud HTTP, que es 200. Este código es el que a veces nos muestra el browser, como 404 (Not Found) o 403 (Forbidden). 200 significa que todo está OK.
A continuación se muestra cuantos bytes devolvió el servidor como respuesta (Payload lenght) y al final, se imprime el contenido de esta respuesta (Payload).
Mira bien el formato usado en la respuesta. Ese texto encerrado entre símbolos {} es el famoso formato JSON, muy difundido en IoT. Mas adelante nos ocuparemos de él en profundidad.
Volviendo al código, analicemos su funcionamiento.
En la línea 8 se declara la variable url que contiene la dirección del sitio al que haremos la solicitud. Para este ejemplo elegí httpbin.org que se usa a menudo para hacer pruebas.
String url = "https://httpbin.org/get"; // URL del sitio de pruebas
A continuación, se realiza la conexión al WiFi. Recuerda usar el nombre de tu red y tu contraseña de acceso.
En la línea 21 se define el objeto client, perteneciente a la clase WiFiClientSecure. Este objeto se usará luego para hacer la conexión al servidor con HTTPS. La línea 22 que sigue a continuación es muy importante:
WiFiClientSecure client; // Crear objeto para la conexión (HTTPS) client.setInsecure(); // HTTPS cifrado, sin validar certificado
setInsecure indica que el objeto client no validará el certificado digital del servidor. Esto no significa que la conexión deje de ser cifrada: los datos siguen transmitiéndose de forma segura, pero el cliente no verifica la identidad del servidor.
Completando esta etapa de declaraciones, en la línea 24 se declara el objeto http de la clase HTTPClient, que servirá para hacer las solicitudes al servidor.
HTTPClient http; // Crear objeto para gestionar solicitudes HTTP (GET/POST)
Ahora pasamos a la acción. El siguiente bloque prepara la solicitud HTTPS y verifica que se haya podido inicializar correctamente antes de enviar nada al servidor. Si hubo algún problema como que no existe conexión WiFi o la URL provista no es válida, imprime un mensaje de error, hace un retardo para impedir otro intento de inmediato y termina el programa.
// Inicializa la petición HTTP/HTTPS (configura URL + cliente TLS)
if (!http.begin(client, url)) {
Serial.println("http.begin() failed");
delay(5000);
return;
}Si esta inicialización se pudo llevar adelante sin errores, ahora sí se hace la solicitud al servidor. El método GET del objeto http envía la solicitud al servidor y devuelve el código de estado HTTP de la respuesta, que se almacena en la variable httpCode.
// Envía el GET y obtiene el código HTTP de respuesta
int httpCode = http.GET();
Serial.printf("HTTP code: %d\n", httpCode); // Código de estado HTTPComo ya vimos antes este código de estado indica el resultado de la solicitud. Si todo estuvo OK, su valor será 200. Si hubo un error tendrá un valor diferente.
Puedes ver un listado con los distintos códigos de error en esta página.
A continuación se comprueba precisamente eso, si la solicitud fué exitosa y devolvió 200 (en su lugar se usa la contante HTTP_CODE_OK, que tiene el mismo valor).
Si hubo algún error, se muestran los primeros 200 caracteres de la respuesta (porque puede llegar a ser extensa), se finaliza la conexión con lel método end y se termina el programa.
Si no hubo error, se lee el contenido de la respuesta del servidor con http.getString, se cierra la conexión y se muestra la respuesta y su longitud.
Es importante diferenciar el código de estado de la respuesta (cuerpo) propiamente dicha. El primero indica si la solicitud fue exitosa o si ocurrió algún error; la segunda contiene la información solicitada.
if (httpCode != HTTP_CODE_OK) {
// Hubo algun problema
Serial.println("Respuesta del server (primeros 200 chars):");
String errBody = http.getString();
Serial.println(errBody.substring(0, 200));
http.end();
delay(10000);
return;
}
// La solicitud fue exitosa
String payload = http.getString();
http.end();
Serial.printf("Payload length: %d bytes\n", payload.length());
Serial.println("Payload:");
Serial.println(payload);El formato de la respuesta depende del servidor. En este caso responde en formato JSON, así que te voy a explicar de que se trata en los próximos párrafos.
La siguiente imagen resume el procedimiento a seguir para completar la solicitud GET usando HTTPS:

JSON
JSON (se pronuncia “yeison” o “jota-son”) son las siglas de JavaScript Object Notation. Es un formato estándar de intercambio de información que utiliza texto plano, lo que lo hace liviano, fácil de leer por personas y sencillo de procesar por programas.
Se usa con mucha frecuencia en Internet para comunicar aplicaciones y servicios web, y también es muy común en proyectos de IoT, donde dispositivos y servidores intercambian datos de forma simple y eficiente.
Objetos y arreglos
Un texto o archivo en formato JSON está formado por uno o mas objetos. Un objeto contiene propiedades, que son pares clave : valor separados por dos puntos (:)
Por ejemplo, el siguiente es un objeto, que contiene una única propiedad (nombre)
{
"nombre" : "Juan"
}El siguiente es otro objeto, que contiene tres propiedades (nombre, edad y altura):
{
"nombre": "Ana",
"edad": 25,
"altura": 1.67
}Como podés ver, cada propiedad está separada por comas y el objeto está delimitado por llaves.
¿Cómo podriamos representar en un archivo JSON la información de varias personas? La respuesta es: usando un arreglo (array) de objetos:
[
{
"nombre": "Ana",
"edad": 25,
"altura": 1.67
},
{"nombre": "Juan",
"edad": 30,
"altura": 1.80
}
]Este JSON es un arreglo, delimitado por los simbolos «[ ]» que contiene dos objetos, cada uno de los cuales tiene tres propiedades.
Generalizando, podemos dar una definición mas completa de un JSON diciendo que un archivo JSON está formado por objetos o arreglos de objetos.
ArduinoJSON
Para tratar con archivos JSON y operar sobre ellos en Arduino hay varias librerías. Una de las mas populares es ArduinoJSON.
ArduinoJSON se destaca por ser liviana, rápida y especialmente pensada para microcontroladores con recursos limitados, como el ESP32. Entre otras cosas, permite:
- Leer (parsear) JSON recibido desde Internet o desde memoria.
- Acceder a objetos y arreglos de forma sencilla.
- Crear JSON para enviarlo a servidores.
- Trabajar tanto con archivos pequeños como con estructuras más complejas.
- Optimizar el uso de memoria RAM, algo clave en micros.
Gracias a estas características, se volvió prácticamente un estándar de hecho en proyectos IoT, donde el intercambio de información en formato JSON es muy común.
Podés instalar la última versión de ArduinoJSON en el IDE de Arduino facilmente desde el Gestor de Bibliotecas.
Serialización
Se denomina serialización a la conversión de datos o variables internas del programa al formato de texto JSON.
Usando ArduinoJSON puede hacerse de una manera muy simple. Mira el siguiente ejemplo:
#include "ArduinoJson.h"
void setup() {
JsonDocument doc; // Crea el objeto
Serial.begin (115200);
//Cargar valores
doc["nombre"] = "Ana";
doc["edad"] = 25;
doc["altura"] = 1.67;
//Serializar
String docJson;
serializeJson (doc, docJson);
Serial.println (docJson);
}
void loop() {
// put your main code here, to run repeatedly:
}En el código anterior, lo primero es incluir la librería «ArduinoJson.h«. Luego se crea el objeto doc, perteneciente a la clase JsonDocument. Este objeto va a servir para almacenar los valores que luego pasaremos a formato JSON.
En el objeto, los valores se cargan de una manera muy sencilla haciendo referencia a la clave, como
doc["nombre"] = "Ana";
Finalmente, se define una variable tipo String para almacenar el texto JSON y se serializa el contenido del objeto doc en ella.
El resultado se puede ver al mostrar el String porel monitor serie:
{"nombre":"Ana","edad":25,"altura":1.67}
Esa String ya está en formato JSON y podemos usarlo para enviarlo a un servidor en Internet, guardarlo en una tarjeta SD o lo que necesitemos hacer con él.
Otra opción para ver el resultado de la conversión a JSON es serializarlo sobre el monitor serie de forma directa en vez de guardarlo en un archivo. Esto se hace de la siguiente forma:
//Cargar valores doc["nombre"] = "Ana"; doc["edad"] = 25; doc["altura"] = 1.67; //Serializar String docJson; serializeJson (doc, Serial);
Que produce idéntico resultado por el monitor:
{"nombre":"Ana","edad":25,"altura":1.67}Y si querés el JSON en un formato mas legible, lo podés serializar usando serializeJsonPretty:
//Cargar valores doc["nombre"] = "Ana"; doc["edad"] = 25; doc["altura"] = 1.67; //Serializar String docJson; serializeJsonPretty (doc, Serial);
Que produce esta salida, más fácil de leer:
{
"nombre": "Ana",
"edad": 25,
"altura": 1.67
}La instrucción JsonDocument doc crea un objeto que servirá como contenedor de los valores que luego se convertirán al formato JSON al serializarlos. Este objeto ocupa lugar en memoria RAM.
Una declaración como la anterior se denomina dinámica y permite que la biblioteca gestione la asignación de memoria para este objeto. Eso no es un problema en micros con abundante RAM (como un ESP32-S3) pero si puede ser un incoveniente en micros donde la memoria es escasa.
Si se usa en un micro con poca RAM, es mas conveniente hacer una asignación estática usando StaticJsonDocument<bytes>. Por ejemplo:
StaticJsonDocument<128> doc;
En el ejemplo anterior se reservan 128 bytes para el objeto doc. Esto permite un control mas fino de la cantidad de memoria, pero exige hacer una buena estimación de la cantidad de bytes necesarios.
Deserialización
Es el proceso inverso, es decir, pasa de un archivo de texto con formato JSON a variables internas.
#include "ArduinoJson.h"
void setup() {
JsonDocument doc; // Crea el objeto
Serial.begin (115200);
//Crear JSON de prueba
String payload = "{\"nombre\":\"Juan\",\"edad\":30}";
//Deserializar
DeserializationError error = deserializeJson(doc, payload);
if (error) {
Serial.print("Error: ");
Serial.println(error.c_str());
return;
}
const char* nombre = doc["nombre"];
int edad = doc["edad"];
Serial.println(nombre);
Serial.println(edad);
}
void loop() {
// put your main code here, to run repeatedly:
}En este código también creamos un objeto doc de la clase JsonDocument. A modo de prueba, cargamos en la variable payload un texto con el formato similar a JSON. (En la realidad este payload vendría de un servidor)
En la línea 13 deserealizeJson lo transforma a variables y obtiene un código de error.
Este código de error es un objeto especial de la librería ArduinoJson y para ver su contenido de manera legible se lo imprime usando el méto c_str().
Si no hay error, en las líneas 21 y 22 se buscan los objetos por sus claves («nombre» y «edad») y mas abajo se los muestra en el monitor.
Al correr el código de ejemplo, se puede ver lo siguiente en el monitor serie:
Juan 30
En nuestro panel vamos a hacer una solicitud a un servidor pidiendole un listado de todos los días feriados, que estará en formato JSON. Nuestro trabajo consistirá en deserializar ese JSON y extraer la información necesaria, como la fecha de cada feriado, para determinar cuál es el mas cercano a la fecha actual.
En la siguiente parte de esta serie de artículos veremos como mostrar la información en la pantalla e-Paper y uniremos todas las piezas que vimos hasta ahora para construir nuestro panel inteligente.
Nos vemos!


