En este tutorial voy a explicar cómo puedes utilizar una pantalla TFT Nextion de Itead conectada a OpenWeatherMap y CloudMQTT a través de un ESP8266 para recibir datos meteorológicos en tiempo real.
Cuando terminés tendrás la nociones básicas sobre Nextion Editor, cómo conectarte a la API OpenWeatherMap y cómo utilizar el servicio CloudMQTT con un ESP8266.
Todo explicado paso a paso para que puedas construir una estación o panel meteorológico tu mismo y poderlo utilizar en tu casa.
Y esto es solo la punta de iceberg. Las pantallas Nextion ofrecen muchas más posibilidades. Sólo tienes que explorar el Nextion Editor para descubrirlas.
Ponte cómodo que empezamos el tutorial paso a paso de Nextion, OpenWeatherMap, CloudMQTT y ESP8266.
Indice de contenidos
¿Qué es una pantalla Nextion?
Nextion es una interfaz hombre-máquina o HMI (del inglés Human Machine Interface) que no es más que una pantalla TFT donde cualquier persona puede interactuar con la propia pantalla o con otros dispositivos.
Están compuestas por un procesador integrado y una pantalla TFT táctil que puede ser programada a través de un software que se llama Nextion Editor.
Se trata de un software propietario que permite programar una pantalla Nextion tanto a nivel de diseño como a nivel de acciones e interacciones con el usuario y otros dispositivos. Más adelante veremos cómo podemos diseñar nuestra propia pantalla Nextion para un caso práctico.
La pantalla Nextion tiene la capacidad de conectarse a un microcontrolador a través de un puerto serie o TTL. Esto quiere decir que podemos conectar fácilmente un Arduino o un ESP8266 y así poder conectarse a Internet.
Las pantallas Nextion son fabricadas por Itead, la empresa china que también comercializa productos como los relés WiFi Sonoff basados en el ESP8266.
Una de las ventajas de las pantallas Nextion es su bajo precio. Comparado con el resto de soluciones HMI que hay en el mercado, hay una gran diferencia.
También es cierto que la calidad con respecto a marcas punteras como Siemens o Mitshubishi se nota. Aún así, una pantalla Nextion tiene una buena relación calidad precio.
Otro de los aspectos muy valorados es su facilidad a la hora de diseñar y programar y su compatibilidad con Arduino y ESP8266.
Todo esto lo iremos viendo a lo largo de este tutorial. Pero antes de meternos en los aspectos técnicos, vamos a ver los tipos de pantallas que existen.
Tipos de pantallas Nextion
Itead nos ofrece un amplio abanico de pantallas Nextion. Elegir una u otra pantalla dependerá de las necesidades del proyecto. Se dividen en tres categorías:
- Intelligent Series
- Enhanced Series
- Basic Series
A parte de los diferentes tamaños que vas a encontrar en cada categoría de pantallas Nextion, también se diferencian en cuanto al hardware y las capacidades. Veamos un resumen de cada categoría empezando por las pantallas TFT Basic Series.
Pantallas TFT Nextion Basic Series
La categoría Basic Series de Nextion ofrece pantallas TFT HMI de diferentes tamaños y diferentes características.
Antes de ver las pantallas Nextion de esta categoría tengo que aclarar lo del tamaño. Se mide en pulgadas y cuando dice por ejemplo 3,2″ (se lee tres coma dos pulgadas) está diciendo que la diagonal de la pantalla mide 8,128 centímetros.
1 pulgada equivale a 2,54 centímetros.
Modelo: | NX3224T024 | NX3224T028 | NX4024T032 | NX4832T035 |
---|---|---|---|---|
Tamaño | 2,4″ | 2,8″ | 3,2″ | 3,5″ |
Resolución | 320 x 240 | 320 x 240 | 400 x 240 | 480 x 320 |
Flash | 4 MB | 4 MB | 4 MB | 16 MB |
Precio * | 17€ | 19€ | 23€ | 28€ |
Modelo: | NX4827T043 | NX8048T050 | NX8048T070 |
---|---|---|---|
Tamaño | 4,3″ | 5,0″ | 7,0″ |
Resolución | 480 x 272 | 800 x 480 | 800 x 480 |
Flash | 16 MB | 16 MB | 16 MB |
Precio * | 45€ | 65€ | 75€ |
* Se trata de un precio aproximado según Itead Nextion.
Todas las pantallas TFT Nextion tienen una memoria RAM de 3584 bytes y un microcontrolador integrado de 48 MHz.
Pantallas TFT Nextion Enhanced Series
Las pantallas TFT Nextion de la categoría Enhanced Series son un salto de cualidad con respecto a las Basic Series. Aunque disponen de los mismos tamaños, las grandes diferencias se encuentran a partir de las pantallas de 3,5″.
Las mejoras, como veremos a continuación, se basan sobre todo en la potencia del microcontrolador, la memoria flash y la memoria SRAM.
A parte, incorpora un reloj en tiempo real o RTC (del inglés Real Time Clock), memoria EEPROM y 8 pines digitales de los cuales 4 son pines PWM.
Modelo: | NX3224K024 | NX3224K028 | NX4024K032 | NX4832K035 |
---|---|---|---|---|
Tamaño | 2,4″ | 2,8″ | 3,2″ | 3,5″ |
Resolución | 320 x 240 | 320 x 240 | 400 x 240 | 480 x 320 |
Flash | 16 MB | 16 MB | 16 MB | 32 MB |
RAM | 3584 bytes | 3584 bytes | 3584 bytes | 8192 bytes |
MCU | 48 MHz | 48 MHz | 48 MHz | 108 MHz |
Precio * | 21€ | 23€ | 28€ | 33€ |
Modelo: | NX4827K043 | NX8048K050 | NX8048K070 | NX8048K070 011R/011C |
---|---|---|---|---|
Tamaño | 4,3″ | 5,0″ | 7,0″ | 7,0″ |
Resolución | 480 x 272 | 800 x 480 | 800 x 480 | 800 x 480 |
Flash | 32 MB | 32 MB | 32 MB | 32 MB |
RAM | 8192 bytes | 8192 bytes | 8192 bytes | 8192 bytes |
MCU | 108 MHz | 108 MHz | 108 MHz | 108 MHz |
Precio * | 55€ | 85€ | 83€ | 89/109€ |
* Se trata de un precio aproximado según Itead Nextion.
Todas las pantallas tienen una EEPROM de 1024 bytes, 8 GPIO y 1 RTC.
Pantalla TFT Nextion Intelligent Series
Las pantallas TFT Nextion de la categoría Intelligent Series son las pantallas más avanzadas y de más calidad. Solo presentan dos tamaños de 7,0″ y 10,1″.
Las pantallas TFT Intelligent Series tienen un hardware más potente en cuanto a la potencia del microcontrolador o MCU, la memoria flash y la memoria RAM comparadas con las pantallas TFT de Enhanced Series y Basic Series.
A parte, a nivel de software las pantallas Nextion Intelligent Series admiten funciones avanzadas como animaciones, audio, vídeo y alguna funcionalidad más que iremos viendo.
Se trata de la solución más avanzada y potente que ofrece Nextion e Itead con respecto a pantallas TFT o HMI.
Modelo: | NX8048P070-011R | NX8048P070-011R-Y | NX8048P070-011C |
---|---|---|---|
Tamaño | 7,0″ | 7,0″ | 7,0″ |
Resolución | 800 x 480 | 800 x 480 | 800 x 480 |
Precio* | 79€ | 87€ | 86€ |
Modelo: | NX8048P070-011C-Y | NX1060P101-011R-I | NX1060P101-011C-I |
---|---|---|---|
Tamaño | 7,0″ | 10,1″ | 10,1″ |
Resolución | 800 x 480 | 1024 x 600 | 1024 x 600 |
Precio* | 105€ | 121€ | 131€ |
* Se trata de un precio aproximado según Itead Nextion.
Todas las pantallas TFT Nextion de Intelligent Series tiene además una memoria flash de 128 MB, una RAM de 512 KB, una MCU de 200 MHz, una EEPROM de 1024 bytes, 8 GPIO y un RTC.
Y una vez que tenemos claro qué es Nextion y que tipos de pantallas TFT nos ofrecen, vamos a pasar a ver cómo hacer un ejercicio práctico para que veas cómo se trabaja con las pantallas Nextion.
Panel meteorológica DIY con pantalla Nextion
Sí, está claro. Puedes comprar una estación meteorológica en Amazon o en el Mediamarkt que te proporcione datos de temperatura, humedad, presión, etc..
Hay infinidad de marcas y modelos. Unas funcionan mejor y otras son más económicas.
Sin embargo, con este tutorial te planteo otra forma de hacer una estación o panel meteorológico con una pantalla Nextion. Se trata de hacer una estación meteorológica tu mismo, DIY.
De hecho, no es la primera vez que hablo de este tema. Hace tiempo publiqué una estación meteorológica solar con Arduino ¿te acuerdas?
En ese caso utilicé un Arduino MKRFOX1200 con conexión a SigFox.
Ahora te planteo otro reto. Te voy a explicar paso a paso (como a mi me gusta) cómo hacer un panel de control donde mostrar información del tiempo obtenida de la API gratuita OpenWeatherMap a través de un ESP8266.
También te enseñaré cómo puedes obtener los datos de temperatura de dos dispositivos conectados a un sensor de temperatura y repartidos por tu casa utilizando MQTT.
Y todo se mostrará en una pantalla Nextion de 7″ a todo color con la que podrás interactuar.
Vamos con ello. Primero vamos a empezar viendo el material que vas a necesitar para seguir este tutorial.
Material necesario
El siguiente material es el que voy a utilizar a lo largo del tutorial.
- Pantalla Nextion. El modelo que voy a utilizar NX8048P070-011R-Y de Intelligent Series. Puedes utilizar cualquier otra pantalla pero intenta que sea de 7″ ya que de otro modo no conseguirás ver bien la información.
- Conversor serie USB o FTDI o TTL. Te recomiendo que utilices uno que pueda funcionar a 3V3 o a 5V.
- 3 x ESP8266. Como mínimo vas a necesitar uno que va conectado a la pantalla. Yo voy a utilizar un Wemos D1 Mini pero puedes utilizar el que quieras.
- 2 x sensor de temperatura. Necesitarás también dos sensores de temperatura. Yo en este proyecto utilizaré el DS18B20.
- Como no, también necesitarás cables y alguna protoboard para montar los circuitos.
Para poder desarrollar aplicaciones y programas con una pantalla TFT Nextion necesitas tener un Windows instalado ya que el software de edición, Nextion Editor, solo está para este sistema operativo.
Se trata de un proyecto que no es muy complejo pero sí que es largo. Por eso, lo primero que quiero que tengas claro es una perspectiva general de lo que vamos a hacer y lo que se va a conseguir.
Siempre me gusta hacer esto tanto en los proyectos que hago para mi mismo, como en los artículos que publico en el blog o en los mis cursos de formación.
Esquema general
Más o menos la idea general del proyecto la puedes ver en el siguiente esquema.
Partimos de un ESP8266 que hará de gateway o pasarela entre los diferentes componentes del proyecto. Sus funciones son:
- Consultar los datos meteorológicos de la API de OpenWeatherMap a través de HTTP.
- Recibir los datos de temperatura de los nodos vía MQTT a través del servicio CloudMQTT.
- Actualización de la pantalla TFT Nextion a través de la comunicación serie.
Por otro lado tenemos los dos nodos ESP8266 conectados a los sensores DS18B20. Son los encargados de registrar la temperatura y enviarla al gateway a través de un topic MQTT.
El broker MQTT es un servicio en la nube que se llama CloudMQTT. Utiliza por debajo Mosquitto. Luego veremos cómo configurarlo.
El servicio OpenWeatherMap es una API que nos permite consultar datos meteorológicos en tiempo real de cientos de ciudades. Luego también veremos cómo se configura.
Por último la pantalla TFT Nextion que hará de interfaz gráfica o HMI para poder visualizar los datos de temperatura de tu ciudad y de los nodos.
Creo que más o menos ha quedado el proyecto en términos generales. Ahora, para poder afrontar este proyecto de una forma sencilla lo mejor es dividirlo en fases que se puedan ir realizando poco a poco.
- Diseño y configuración de la pantalla TFT Nextion
- Configuración del servicio OpenWeatherMap
- Conexión y actualización de la pantalla TFT Nextion con los datos del tiempo obtenidos con un ESP8266
- Configuración CloudMQTT con ESP8266
- Configuración de los nodos para medir temperatura con un ESP8266 y un DS18B20
Tenemos mucho trabajo por delante así que vamos a empezar configurando y diseñando la pantalla TFT Nextion con el Nextion Editor.
Nextion Editor para programar y diseñar pantallas TFT HMI
Cuando recibes la pantalla en tu casa no tiene nada instalado. Bueno, si acaso tendrá un programa demo que suelen tener las pantallas Nextion.
En esta fase del proyecto nos centraremos en diseñar la pantalla.
El software Nextion Editor tiene muchas opciones que, por supuesto, no veremos en este tutorial. Me voy a centrar solo en las necesarias e indispensables para este proyecto concreto. Si necesitas saber más te aconsejo que consultes la documentación de Nextion.
Lo primero es instalar el software. Puedes descargarlo desde aquí. El único inconveniente que tiene es que de momento solo está para Windows. Lo siento por los usuarios de Linux o de Mac.
Instalarlo es muy sencillo y no merece la pena hacer ninguna aclaración salvo que lo instales con permisos de administrador (botón derecho y Ejecutar como administrador).
Una vez instalado ya podrías empezar a diseñar tus propias pantallas pero antes hay que conectar la pantalla TFT Nextion al ordenador. Esto lo hacemos a través del FTDI o TTL.
Junto con la pantalla tendrás 4 cables que se unen por un extremo con un conector JST y por el otro lado están suelto. El conector JST con los 4 cables unidos debes conectarlo a la pantalla en el conector donde pone
El otro extremo, los 4 cables sueltos, deben ir conectados al FTDI o TTL que a su vez irá conectado a un puerto USB de tu ordenador.
Ojo, que la conexión se tiene que hacer cruzada. El cable amarillo RX de la pantalla TFT Nextion debe ir conectado al pin TX del FTDI. El cable azul TX de la pantalla TFT Nextion debe ir conectado al pin RX del FTDI. Los otros dos cables van el negro al GND y el rojo a 5V.
Bien, ahora sí, ya podemos empezar a diseñar y programar la pantalla. Para hacerlo vas a necesitar las siguientes imágenes. Descargarlas a tu ordenador.
Sigue las instrucciones del siguiente vídeo donde te enseñaré a diseñar una pantalla con Nextion Editor.
Si quieres saber más sobre cómo utilizar Nextion Editor a continuación te dejo los enlaces a la documentación:
Ahora vamos a pasar a la segunda fase donde configuramos OpenWeatherMap y programamos el ESP8266 para consultar la API.
ESP8266 y OpenWeatherMap
Lo primero es explicarte qué es OpenWeatherMap. Se trata de un servicio online que proporciona datos meteorológicos de todo el mundo. Es mundialmente conocida y se la utilizan multitud de aplicaciones de Internet como apps o webs.
Obtiene los datos de diferentes fuentes como estaciones meteorológicas de aeropuertos, estaciones de radar y de estaciones de los servicios públicos de meteorología de todo el mundo.
A parte de proporcionar datos meteorológicos en tiempo real, proporciona datos de previsiones.
A parte de todo esto, el servicio de OpenWeatherMap se centra en el aspecto social y pretende que los usuarios y dueños de estaciones meteorológicas se conecten al servicio para proporcionar más datos aumentando así la precisión de la información.
Imagínate la cantidad de información que puede llegar a almacenar OpenWeatherMap.
Aunque cuenta con varias cuentas de pago según la necesidad, también está disponible una cuenta gratuita con restricciones pero suficiente para el uso que vamos a dar nosotros.
Te tienes que dar de alta. Es muy sencillo. Lo único que necesitas es un email y una contraseña.
En el siguiente vídeo te explico más sobre esta plataforma y cómo puedes consultar el tiempo de tu ciudad.
Estos son los enlaces de los que hablo en el vídeo:
- Postman programa para hacer peticiones HTTP
- Documentación de la API de OpenWeatherMap
- Listado de iconos de OpenWeatherMap
Lo siguiente que vamos a hacer es conectar el ESP8266 a la pantalla TFT Nextion.
Conexión ESP8266 y pantalla Nextion
En principio la conexión no tiene mucho misterio, lo único que hacemos es conectar los pines RX y TX de la pantalla TFT Nextion a los pines TX y RX del ESP8266.
Ojo que se deben cruzar. El RX con el TX y el TX con el RX.
El problema lo tenemos con la alimentación. Desde la propia página de Nextion aconsejan que se alimente con un cargador de móvil por ejemplo de 5V y 1A.
Yo voy a utilizar una fuente de alimentación para protoboard que suministra 3V3 y 5V. Tu puedes utilizar lo que mejor te convenga.
En la pantalla que yo tengo junto con los cables venía un adaptador USB para poder obtener la alimentación de un cargador de móvil USB.
El esquema de conexión es el siguiente.
Con esto tenemos todo preparado. Te has dado de alta en OpenWeatherMap, has conectado el ESP8266 a la pantalla TFT Nextion y ahora solo nos queda programar el ESP8266 para que se conecte a la API de OpenWeatherMap y obtenga los datos.
A continuación te dejo el código completo que verás en el vídeo. Copialo y pégalo en el IDE de Arduino.
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 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 |
#define _DEBUG_ #include "ESP8266WiFi.h" #include <ESP8266HTTPClient.h> #include "Nextion.h" #include <ArduinoJson.h> // Definición de constantes pantalla nextion #define ICON1D 1 #define ICON2D 2 #define ICON3D 3 #define ICON4D 3 #define ICON9D 4 #define ICON10D 5 #define ICON11D 6 #define ICON13D 7 #define ICON50D 8 // Configuración red WiFi const char* ssid = "TU-SSID"; // Pon el nombre de tu red WiFi const char* password = "TU-PASS"; // Pon la contraseña de tu red WiFi // Cliente web HTTPClient clienteHttp; // Datos OpenWeatherMap String hostOpenWeatherMap = "http://api.openweathermap.org/data/2.5/weather"; String idCiudad = "TU-ID-CIUDAD"; // Sustituye por el id de tu ciudad String apiKey = "TU-API-KEY-OPENWEATHERMAP"; // Sustituye por tu API Key // Campos pantalla Nextion Nextion // NexText(PageID, ComponentID, ComponentName) NexText nexTemp = NexText(0, 7, "t5"); NexText nexHumedad = NexText(0, 3, "t1"); NexText nexViento = NexText(0, 4, "t2"); NexText nexTempMin = NexText(0, 5, "t3"); NexText nexTempMax = NexText(0, 6, "t4"); NexText nexCiudad = NexText(0, 2, "t0"); NexPicture nexIcono = NexPicture(0, 8, "p1"); NexText nexTempExt1 = NexText(0, 11, "t8"); NexText nexTempExt2 = NexText(0, 12, "t9"); NexText nexNameTempExt1 = NexText(0, 9, "t6"); NexText nexNameTempExt2 = NexText(0, 10, "t7"); // Temporizador unsigned long ultimaConsulta = 0; unsigned long tiempoConsulta = 5000; // Tiempo en milisegundos void configWifi() { #ifdef _DEBUG_ // Conexión con la red WiFi Serial.print("Conectando con "); Serial.println(ssid); #endif // Configuración en modo cliente WiFi.mode(WIFI_STA); // Iniciar conexión con la red WiFi WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); #ifdef _DEBUG_ Serial.print("."); #endif } #ifdef _DEBUG_ Serial.println(""); Serial.println("Conectado a la WiFi"); #endif } void obtenerDatosOpenWeather() { // Crear URL para hacer la pretición String url = hostOpenWeatherMap; url += "?id="; url += idCiudad; url += "&appid="; url += apiKey; #ifdef _DEBUG_ Serial.print("URL petición HTTP: "); Serial.println(url); #endif // Conexión con el servidor y configuración de la petición clienteHttp.begin(url); // Envío de petición HTTP al servidor int codigoHttp = clienteHttp.GET(); #ifdef _DEBUG_ Serial.print("Codigo HTTP: "); Serial.println(codigoHttp); #endif // Si todo ha ido bien devolverá un número positivo mayor que cero if (codigoHttp > 0) { // Si ha encontrado el recurso en el servidor responde un código 200 if (codigoHttp == HTTP_CODE_OK) { #ifdef _DEBUG_ Serial.print("Archivo JSON: "); Serial.println(clienteHttp.getString()); #endif // Parsear archivo JSON // Para obtener tamaño del buffer vistiar https://arduinojson.org/v6/assistant/ const size_t capacity = JSON_ARRAY_SIZE(3) + 2 * JSON_OBJECT_SIZE(1) + JSON_OBJECT_SIZE(2) + 3 * JSON_OBJECT_SIZE(4) + JSON_OBJECT_SIZE(5) + JSON_OBJECT_SIZE(6) + JSON_OBJECT_SIZE(12) + 304; DynamicJsonDocument doc(capacity); // Parsear objeto JSON DeserializationError error = deserializeJson(doc, clienteHttp.getString()); if (error) { // Si hay error no se continua #ifdef _DEBUG_ Serial.print("Fallo al parsear JSON. Error: "); Serial.println(error.c_str()); #endif return; } // Temperatura float tempF = doc["main"]["temp"]; tempF = tempF - 273.15; // A grados Celsius char temp[7]; snprintf(temp, 7, "%.0f C", tempF); // Humedad String humedadS = String(int(doc["main"]["humidity"])) + " %"; char humedad[7]; humedadS.toCharArray(humedad, 7); // Temperatura mínima float tempMinF = doc["main"]["temp_min"]; tempMinF = tempMinF - 273.15; // A grados Celsius char tempMin[7]; snprintf(tempMin, 7, "%.0f C", tempMinF); // Temperatura máxima float tempMaxF = doc["main"]["temp_max"]; tempMaxF = tempMaxF - 273.15; // A grados Celsius char tempMax[7]; snprintf(tempMax, 7, "%.0f C", tempMaxF); // Viento float vientoF = doc["wind"]["speed"]; char viento[8]; snprintf(viento, 8, "%.0f m/S", vientoF); // Ciudad const char* ciudad = doc["name"]; nexCiudad.setText(ciudad); // Volcado de datos a la pantalla nexTemp.setText(temp); nexHumedad.setText(humedad); nexTempMin.setText(tempMin); nexTempMax.setText(tempMax); nexViento.setText(viento); // Icono tiempo const char* icono = doc["weather"][0]["icon"]; // Asignación icono comparar icono diurno y nocturno if (strcmp(icono, "01d") == 0 || strcmp(icono, "01n") == 0) { nexIcono.setPic(ICON1D); } else if (strcmp(icono, "02d") == 0 || strcmp(icono, "02n") == 0) { nexIcono.setPic(ICON2D); } else if (strcmp(icono, "03d") == 0 || strcmp(icono, "03n") == 0) { nexIcono.setPic(ICON3D); } else if (strcmp(icono, "04d") == 0 || strcmp(icono, "04n") == 0) { nexIcono.setPic(ICON4D); } else if (strcmp(icono, "09d") == 0 || strcmp(icono, "09n") == 0) { nexIcono.setPic(ICON9D); } else if (strcmp(icono, "10d") == 0 || strcmp(icono, "10n") == 0) { nexIcono.setPic(ICON10D); } else if (strcmp(icono, "11d") == 0 || strcmp(icono, "11n") == 0) { nexIcono.setPic(ICON11D); } else if (strcmp(icono, "13d") == 0 || strcmp(icono, "13n") == 0) { nexIcono.setPic(ICON13D); } else if (strcmp(icono, "50d") == 0 || strcmp(icono, "50n") == 0) { nexIcono.setPic(ICON50D); } else { nexIcono.setPic(ICON1D); } #ifdef _DEBUG_ Serial.println("Datos OpenWeatherMap"); Serial.print("Temperatura: "); Serial.println(temp); Serial.print("Humedad: "); Serial.println(humedad); Serial.print("Temp. Min: "); Serial.println(tempMin); Serial.print("Temp. Max: "); Serial.println(tempMax); Serial.print("Viento: "); Serial.println(viento); Serial.print("Icono: "); Serial.println(icono); Serial.print("Ciudad: "); Serial.println(ciudad); #endif } else { #ifdef _DEBUG_ Serial.println("Error al recibir petición."); #endif } } } void temporizador() { // Comprobar si se ha dado la vuelta if (millis() < ultimaConsulta) { // Asignar un nuevo valor ultimaConsulta = millis(); } if ((millis() - ultimaConsulta) > tiempoConsulta) { // Marca de tiempo ultimaConsulta = millis(); // Llamada a la función para obtener los datos y actualziar pantalla Nextion obtenerDatosOpenWeather(); } } void setup(){ // Iniciar comunicación con la pantalla // Baudrate por defecto son 9600 para seleccionar en el monitor serie nexInit(); // Esta función se encuentra en NextHardware.h // Configurar WiFi configWifi(); } void loop() { // Temporizador temporizador(); } |
En el siguiente vídeo te explico cómo obtener los datos de OpenWeatherMap y mostrarlos en la pantalla TFT Nextion.
A continuación te dejo los enlaces a los que hago referencia en el vídeo anterior.
El resultado hasta aquí es espectacular. Tenemos una pantalla diseñada por nosotros mismos y hemos sido capaces de conectar un ESP8266 a un servicio como OpenWeatherMap y mostrar los datos en el la pantalla TFT Nextion.
Solo nos queda una cosa y es poder mostrar también información de temperatura de otros sensores situados por casa. Esto lo vamos a hacer gracias al protocolo MQTT.
Configuración CloudMQTT y ESP8266
En esta primera parte de MQTT vamos a ver cómo configurar CloudMQTT y a modificar el código del ESP8266. La idea es poder recibir información a través de dos topics a los que se tiene que suscribir el ESP8266 conectado a la pantalla TFT Nextion.
Aunque en este tutorial voy a utilizar CloudMQTT (luego te explico qué es este servicio) podrías hacerlo de la misma forma utilizando tu propio broker como Mosquitto.
Sea como sea, lo único que necesitas es una red MQTT funcionando.
¿Qué es CloudMQTT?
CloudMQTT es un servicio en la nube que nos permite utilizar una red de datos que utiliza el protocolo MQTT sin tener que montar nosotros la infraestructura o el broker.
Se trata de servidores alojados en AWS (Amazon Web Services) donde tienen instalados varias instancias del broker Mosquitto.
Ofrecen un plan gratuito con limitaciones y si necesitas más funcionalidades y capacidad, debes pasar por caja como es lógico.
El plan más básico, el gratis, permite la conexión de 5 clientes y una velocidad de transmisión de 10 kbit/s. Se llama Cute Cat (lindo gatito en español).
Si necesitas más conexiones de clientes tendrías que pasar al plan que cuesta 5 dólares por mes que permite hasta 25 usuarios y una velocidad de 20 kbit/s.
Y así podrías llegar hasta los 10.000 clientes con un coste de 299 dólares por mes.
Ahora toca programar. Hay que adaptar el código que hemos visto antes donde el ESP8266 se conecta a la API de OpenWeatherMap y recibe los datos de meteorología y ahora necesita también suscribirse a dos topics.
Te dejo el código completo a continuación.
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 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 |
6700#define _DEBUG_ #include "ESP8266WiFi.h" #include <ESP8266HTTPClient.h> #include "Nextion.h" #include <ArduinoJson.h> #include <PubSubClient.h> // Definición de constantes pantalla nextion #define ICON1D 1 #define ICON2D 2 #define ICON3D 3 #define ICON4D 3 #define ICON9D 4 #define ICON10D 5 #define ICON11D 6 #define ICON13D 7 #define ICON50D 8 // Configuración red WiFi const char* ssid = "TU-SSID"; // Pon el nombre de tu red WiFi const char* password = "TU-PASS"; // Pon la contraseña de tu red WiFi // Cliente web HTTPClient clienteHttp; // MQTT const char* brokerUrl = "URL-BROKER"; int brokerPuerto = --; //Cambia por el puerto correspondiente const char* userMQTT = "USER-BROKER"; const char* passMQTT = "PASS-BROKER"; WiFiClient espClient; PubSubClient clienteMqtt(espClient); // Datos OpenWeatherMap String hostOpenWeatherMap = "http://api.openweathermap.org/data/2.5/weather"; String idCiudad = "TU-ID-CIUDAD"; // Sustituye por el id de tu ciudad String apiKey = "TU-API-KEY-OPENWEATHERMAP"; // Sustituye por tu API Key // Campos pantalla Nextion Nextion // NexText(PageID, ComponentID, ComponentName) NexText nexTemp = NexText(0, 7, "t5"); NexText nexHumedad = NexText(0, 3, "t1"); NexText nexViento = NexText(0, 4, "t2"); NexText nexTempMin = NexText(0, 5, "t3"); NexText nexTempMax = NexText(0, 6, "t4"); NexText nexCiudad = NexText(0, 2, "t0"); NexPicture nexIcono = NexPicture(0, 8, "p1"); NexText nexTempExt1 = NexText(0, 11, "t8"); NexText nexTempExt2 = NexText(0, 12, "t9"); NexText nexNameTempExt1 = NexText(0, 9, "t6"); NexText nexNameTempExt2 = NexText(0, 10, "t7"); // Temporizador unsigned long ultimaConsulta = 0; unsigned long tiempoConsulta = 5000; // Temperaturas casa #define SIZETEMPEXT 7 char tempExt1[SIZETEMPEXT]; char* nombreTempExt1 = "Terraza"; char* topicTempExt1 = "casa/temp/ext/1"; char tempExt2[SIZETEMPEXT]; char* nombreTempExt2 = "Salon"; char* topicTempExt2 = "casa/temp/ext/2"; void configWifi() { #ifdef _DEBUG_ // Conexión con la red WiFi Serial.print("Conectando con "); Serial.println(ssid); #endif // Configuración en modo cliente WiFi.mode(WIFI_STA); // Iniciar conexión con la red WiFi WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); #ifdef _DEBUG_ Serial.print("."); #endif } #ifdef _DEBUG_ Serial.println(""); Serial.println("Conectado a la WiFi"); #endif } void obtenerDatosOpenWeather() { // Crear URL para hacer la pretición String url = hostOpenWeatherMap; url += "?id="; url += idCiudad; url += "&appid="; url += apiKey; #ifdef _DEBUG_ Serial.print("URL petición HTTP: "); Serial.println(url); #endif // Conexión con el servidor y configuración de la petición clienteHttp.begin(url); // Envío de petición HTTP al servidor int codigoHttp = clienteHttp.GET(); #ifdef _DEBUG_ Serial.print("Codigo HTTP: "); Serial.println(codigoHttp); #endif // Si todo ha ido bien devolverá un número positivo mayor que cero if (codigoHttp > 0) { // Si ha encontrado el recurso en el servidor if (codigoHttp == HTTP_CODE_OK) { #ifdef _DEBUG_ Serial.print("Archivo JSON: "); Serial.println(clienteHttp.getString()); #endif // Parsear archivo JSON // Para obtener tamaño del buffer vistiar https://arduinojson.org/v6/assistant/ const size_t capacity = JSON_ARRAY_SIZE(3) + 2 * JSON_OBJECT_SIZE(1) + JSON_OBJECT_SIZE(2) + 3 * JSON_OBJECT_SIZE(4) + JSON_OBJECT_SIZE(5) + JSON_OBJECT_SIZE(6) + JSON_OBJECT_SIZE(12) + 304; DynamicJsonDocument doc(capacity); // Parsear objeto JSON DeserializationError error = deserializeJson(doc, clienteHttp.getString()); if (error) { // Si hay error no se continua #ifdef _DEBUG_ Serial.print("Fallo al parsear JSON. Error: "); Serial.println(error.c_str()); #endif return; } // Temperatura float tempF = doc["main"]["temp"]; tempF = tempF - 273.15; // A grados Celsius char temp[7]; snprintf(temp, 7, "%.0f C", tempF); // Humedad String humedadS = String(int(doc["main"]["humidity"])) + " %"; char humedad[7]; humedadS.toCharArray(humedad, 7); // Temperatura mínima float tempMinF = doc["main"]["temp_min"]; tempMinF = tempMinF - 273.15; // A grados Celsius char tempMin[7]; snprintf(tempMin, 7, "%.0f C", tempMinF); // Temperatura máxima float tempMaxF = doc["main"]["temp_max"]; tempMaxF = tempMaxF - 273.15; // A grados Celsius char tempMax[7]; snprintf(tempMax, 7, "%.0f C", tempMaxF); // Viento float vientoF = doc["wind"]["speed"]; char viento[8]; snprintf(viento, 8, "%.0f m/S", vientoF); // Ciudad const char* ciudad = doc["name"]; nexCiudad.setText(ciudad); // Volcado de datos a la pantalla nexTemp.setText(temp); nexHumedad.setText(humedad); nexTempMin.setText(tempMin); nexTempMax.setText(tempMax); nexViento.setText(viento); // Temperaturas exteriores nexTempExt1.setText(tempExt1); nexTempExt2.setText(tempExt2); nexNameTempExt1.setText(nombreTempExt1); nexNameTempExt2.setText(nombreTempExt2); // Icono tiempo const char* icono = doc["weather"][0]["icon"]; // Asignación icono comparar icono diurno y nocturno if (strcmp(icono, "01d") == 0 || strcmp(icono, "01n") == 0) { nexIcono.setPic(ICON1D); } else if (strcmp(icono, "02d") == 0 || strcmp(icono, "02n") == 0) { nexIcono.setPic(ICON2D); } else if (strcmp(icono, "03d") == 0 || strcmp(icono, "03n") == 0) { nexIcono.setPic(ICON3D); } else if (strcmp(icono, "04d") == 0 || strcmp(icono, "04n") == 0) { nexIcono.setPic(ICON4D); } else if (strcmp(icono, "09d") == 0 || strcmp(icono, "09n") == 0) { nexIcono.setPic(ICON9D); } else if (strcmp(icono, "10d") == 0 || strcmp(icono, "10n") == 0) { nexIcono.setPic(ICON10D); } else if (strcmp(icono, "11d") == 0 || strcmp(icono, "11n") == 0) { nexIcono.setPic(ICON11D); } else if (strcmp(icono, "13d") == 0 || strcmp(icono, "13n") == 0) { nexIcono.setPic(ICON13D); } else if (strcmp(icono, "50d") == 0 || strcmp(icono, "50n") == 0) { nexIcono.setPic(ICON50D); } else { nexIcono.setPic(ICON1D); } #ifdef _DEBUG_ Serial.println("Datos OpenWeatherMap"); Serial.print("Temperatura: "); Serial.println(temp); Serial.print("Humedad: "); Serial.println(humedad); Serial.print("Temp. Min: "); Serial.println(tempMin); Serial.print("Temp. Max: "); Serial.println(tempMax); Serial.print("Viento: "); Serial.println(viento); Serial.print("Icono: "); Serial.println(icono); Serial.print("Ciudad: "); Serial.println(ciudad); Serial.print("Temp. Ext1: "); Serial.println(tempExt1); Serial.print("Temp. Ext2: "); Serial.println(tempExt2); #endif } else { #ifdef _DEBUG_ Serial.println("Error al recibir petición."); #endif } } } void temporizador() { // Comprobar si se ha dado la vuelta if (millis() < ultimaConsulta) { // Asignar un nuevo valor ultimaConsulta = millis(); } if ((millis() - ultimaConsulta) > tiempoConsulta) { // Marca de tiempo ultimaConsulta = millis(); // Llamada a la función para obtener los datos y actualziar pantalla Nextion obtenerDatosOpenWeather(); } } void callback(char* topic, byte* payload, unsigned int length) { #ifdef _DEBUG_ Serial.print("Mensaje recibido ["); Serial.print(topic); Serial.println("]"); for (int i = 0; i < length; i++) { Serial.print((char)payload[i]); } Serial.println(); #endif // Comprobación de qué topic viene if (strcmp(topic, topicTempExt1) == 0) { // Protección fuera de rango if (length + 1 < SIZETEMPEXT) { // Recorrer el array de bytes for (int i = 0; i < length; i++) { // Convertir cada byte en un char y concatenarlo tempExt1[i] = (char)payload[i]; } tempExt1[length] = ' '; tempExt1[length + 1] = 'C'; } else { #ifdef _DEBUG_ Serial.println("Payload demasiado largo"); #endif } } else if (strcmp(topic, topicTempExt2) == 0) { // Protección fuera de rango if (length + 1 < SIZETEMPEXT) { // Recorrer el array de bytes for (int i = 0; i < length; i++) { // Convertir cada byte en un char y concatenarlo tempExt2[i] = (char)payload[i]; } tempExt2[length] = ' '; tempExt2[length + 1] = 'C'; } else { #ifdef _DEBUG_ Serial.println("Payload demasiado largo"); #endif } } } void reconnect() { // Repetir hasta que se conecte while (!clienteMqtt.connected()) { #ifdef _DEBUG_ Serial.print("Intentando conectarse al broker MQTT..."); #endif // Nombre único del cliente. No se puede repetir String clientId = "ESP8266-Nextion"; // Intentando conectar if (clienteMqtt.connect(clientId.c_str(), userMQTT, passMQTT)) { #ifdef _DEBUG_ Serial.println("Conectado"); #endif // Suscripción a los topics clienteMqtt.subscribe(topicTempExt1); clienteMqtt.subscribe(topicTempExt2); } else { #ifdef _DEBUG_ Serial.print("Fallo al conectar al broker, rc="); Serial.print(clienteMqtt.state()); Serial.println(" intentando conectar en 5 segundos"); #endif // Este delay habría que mejorarlo para que no bloquee delay(5000); } } } void setup() { // Iniciar comunicación con la pantalla // Baudrate por defecto son 9600 para seleccionar en elmonitor serie nexInit(); // Esta función se encuentra en NextHardware.h // Configurar WiFi configWifi(); // Configuración MQTT clienteMqtt.setServer(brokerUrl, brokerPuerto); clienteMqtt.setCallback(callback); } void loop() { // Temporizador temporizador(); // Gestión conexión MQTT if (!clienteMqtt.connected()) { reconnect(); } clienteMqtt.loop(); } |
En el siguiente vídeo te enseño cómo configurar CloudMQTT y el ESP8266 para recibir datos desde un cliente MQTT.
Ya está preparado tanto la pantalla TFT Nextion como el ESP8266 para recibir los datos que nos envíen los nodos con los sensores de temperatura. Ahora solo queda configurar los nodos que es lo que veremos a continuación.
ESP8266, DS18B20 y MQTT
Los nodos ESP8266 van a ser los encargados de recoger la información de temperatura y enviarla al ESP8266 conectado a la pantalla Nextion vía MQTT.
En este ejemplo yo voy a utilizar un sensor DS18B20 pero realmente puedes utilizar cualquier tipo de sensor de temperatura con Arduino.
El esquema eléctrico para cada nodo sería el siguiente.
Este sería el código de uno de los nodos, el nodo 1. Para cargar el mismo código en el nodo 2 solo tienes que cambiar es el topic MQTT y el id del cliente.
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 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
2700#define _DEBUG_ #include <ESP8266WiFi.h> #include <PubSubClient.h> #include <OneWire.h> #include <DallasTemperature.h> // Parámetros configuración WiFi const char* ssid = "TU-SSID"; const char* password = "TU-PASS"; // Parámetros configuración broker MQTT const char* brokerUrl = "URL-BROKER"; int brokerPuerto = --; //Cambia por el puerto correspondiente const char* userMQTT = "USER-BROKER"; const char* passMQTT = "PASS-BROKER"; const char* topicMQTT = "casa/temp/ext/1"; const String clienteId = "ESP8266TempExt1"; // Instancia al objeto WiFiClient y PubSubClient WiFiClient clienteESP; PubSubClient clienteMqtt(clienteESP); // Parámetros sensor temperatura (DS18B20) const int pinDS18B20 = D5; OneWire oneWireObjeto(pinDS18B20); DallasTemperature sensorDS18B20(&oneWireObjeto); // Temporizador unsigned long ultimaConsulta = 0; unsigned long tiempoConsulta = 5000; void configuracionWifi() { #ifdef _DEBUG_ Serial.print("Inicio conexión con la red WiFi "); Serial.println(ssid); #endif // Modo estación WiFi.mode(WIFI_STA); // Inicio comunicación WiFi.begin(ssid, password); // Conexión con red WiFi while (WiFi.status() != WL_CONNECTED) { delay(500); #ifdef _DEBUG_ Serial.print("."); #endif } #ifdef _DEBUG_ Serial.println(""); Serial.print("Conectado a la red WiFi "); Serial.println(ssid); Serial.print("Con la IP "); Serial.println(WiFi.localIP()); #endif } void obtenerEnviarTemperatura() { // Mensaje lectura temperatura sensorDS18B20.requestTemperatures(); // Leer temperatura float temperatura = sensorDS18B20.getTempCByIndex(0); #ifdef _DEBUG_ Serial.print("La temperatura es de "); Serial.println(temperatura, 3); #endif // Convertir la temperatura a un array de caracteres char payload[7]; // Número de caracteres máximo 6 (XXX.XX) snprintf(payload, 7, "%.0f", temperatura); // Convertir un float en un array de caracteres // Enviar mensaje con la información de la temperatura clienteMqtt.publish(topicMQTT, payload); #ifdef _DEBUG_ Serial.print("Enviado mensaje con temperatura "); Serial.println(payload); #endif } void reconnect() { // Repetir hasta que se conecte while (!clienteMqtt.connected()) { #ifdef _DEBUG_ Serial.print("Intentando conectarse al broker MQTT..."); #endif // Intentando conectar if (clienteMqtt.connect(clienteId.c_str(), userMQTT, passMQTT)) { #ifdef _DEBUG_ Serial.println("Conectado"); #endif } else { #ifdef _DEBUG_ Serial.print("Fallo al conectar al broker, rc="); Serial.print(clienteMqtt.state()); Serial.println(" intentando conectar en 5 segundos"); #endif // Este delay habría que mejorarlo para que no bloquee delay(5000); } } } void temporizador() { // Comprobar si se ha dado la vuelta if (millis() < ultimaConsulta) { // Asignar un nuevo valor ultimaConsulta = millis(); } if ((millis() - ultimaConsulta) > tiempoConsulta) { // Marca de tiempo ultimaConsulta = millis(); // Llamada a la función para obtener los datos y actualziar pantalla Nextion obtenerEnviarTemperatura(); } } void setup() { #ifdef _DEBUG_ Serial.begin(9600); #endif // Configuración WiFi configuracionWifi(); // Iniciar bus DS18B20 sensorDS18B20.begin(); // Configuración MQTT clienteMqtt.setServer(brokerUrl, brokerPuerto); } void loop() { // Gestión conexión MQTT if (!clienteMqtt.connected()) { reconnect(); } clienteMqtt.loop(); // Temporizador temporizador(); } |
En el siguiente vídeo te explico el código que hay que cargar a cada nodo.
Y con esto doy por finalizada este tutorial donde te he mostrado cómo hacer un panel de control con una pantalla TFT Nextion y un ESP8266.
A parte hemos visto cómo hacer una petición HTTP a una API como OpenWeatherMap.
Y por último te he mostrado cómo configurar el servicio CloudMQTT para utilizarlo con ESP8266.
Espero tus comentarios, dudas y preguntas aquí abajo. Muchas gracias :)
Gracias a Shutterstock por ceder los derechos de las siguientes imágenes: