OBJETIVOS
El objetivo de esta práctica es conectar el NodeMCU con un punto de acceso de una red WiFi. Es lo que se conoce como conexión en modo Station (STA). Esta es una de las tres formas de conexión WiFi de que dispone el procesador ESP8266 instalado en el NodeMCU. Las otras dos formas son:
- Conexión en modo Access Point (AP), donde el el NodeMCU crea una red WiFi de área local (Wireless Local Area Network – WLAN ).
- Una suma de los dos modos de conexión Access Point + Station (AP+STA).
Esta conexión la utilizaremos para poder controlar el encendido y apagado de un LED a través otros dispositivos clientes que se conecten al mismo punto de acceso, como PC’s, Smart Phones, Tablets, etc. y aprovechando que el punto de acceso estará conectado a Internet (un router WiFi), mejoraremos la estética página Web, que sirve para el control del LED, incluyendo imágenes de la red nos permitirán conocer su estado (encendido/apagado) de una manera visual.
MATERIAL NECESARIO
CONCEPTOS BÁSICOS
En el modo Station el NodeMCU se conecta con un punto de acceso (Access Point – AP), como con un router, otro NodeMCU, etc., del que tenga su SSID (Service Set Identifier), es decir “nombre de red” y, en su caso, clave de acceso. De esta manera el NodeMCU queda integrado en la red WiFi como servidor/cliente, pudiendo interactuar con el resto de los dispositivos que la forman.
Si hacemos un repaso de las funciones de la librería ESP8266WiFi.h que son utilizadas por el ESP8266 para trabajar en modo Station es bastante extenso. Para hacer un repaso de las mismas conviene agruparlas en los cuatro bloques que se describen a continuación:
WiFi.begin(ssid, password, channel, bssid, connect) |
La conexión del módulo al punto de acceso se realiza con la función begin. De forma predeterminada el ESP8266 del NodeMCU se intentará volver a conectar a la red WiFi a la que estaba conectado si se produce la desconexión –no es necesario programarlo-. Esta es la estructura más habitual de la función, para conectarse a un punto de acceso específico: WiFi.begin(ssid, password); Los parámetros tienen es el siguiente significado:
Para conectar el módulo al último punto de acceso utilizado, la función sería como sigue (el ssid y el password quedan almacenados en la memoria flash): WiFi.begin();
La función con todos sus parámetros es: WiFi.begin(ssid, password, channel, bssid, connect); Los parámetros que faltan por definir tienen es el siguiente significado:
|
WiFi.config(local_ip, gateway, subnet, dns1, dns2) |
Deshabilita el protocolo de configuración dinámica DHCP (Dynamic Host Configuration Protocol) y fija valores definidos por el usuario -estáticos- para la configurar las direcciones IP. La función devuelve true si aplica con éxito el cambio de configuración. Si la configuración no se puede aplicar devuelve false. Los parámetros de IP que se pueden proporcionar son los siguientes:
Ejemplo: #include <ESP8266WiFi.h> const char* ssid = "********"; const char* password = "********"; IPAddress localIP(192,168,1,22); IPAddress gateway(192,168,1,9); IPAddress subnet(255,255,255,0); void setup(void) { Serial.begin(115200); Serial.println(); Serial.printf("Connecting to %s\n", ssid); WiFi.begin(ssid, password); WiFi.config(localIP, gateway, subnet); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(); Serial.print("Connected, IP address: "); Serial.println(WiFi.localIP()); } void loop() {} |
WiFi.reconnect() |
Reconecta el módulo a un punto de acceso. Si no ha estado conectado antes no realiza ninguna función. Ejemplo: Wifi.reconnect(); while (Wifi.status != WL_CONNECTED) { delay(500); Serial.print("."); } |
WiFi.disconnect() |
Borra los parámetros de SSID y password y desconecta el módulo del punto de acceso. Ejemplo: Wifi.disconnect() |
WiFi.isConnected() |
Comprueba si el módulo está conectado a un punto de acceso. Devuelve true si es cierto y false si no lo es. Ejemplo: Wifi.isConnected(); |
WiFi.setAutoConnect() |
Configura el módulo para conectarse automáticamente al último punto de acceso. Ejemplo: Wifi.setAutoConnect(); |
WiFi.getAutoConnect() |
Esta función acompaña a WiFi.getAutoConnect(). Comprueba si el módulo está configurado para autoconectarse al último punto de acceso al que ha estado conectado. Devuelve true si es cierto y false si no lo es. Ejemplo: Wifi.getAutoConnect(); |
WiFi.setAutoReconnect(autoReconnect) |
Configura si el módulo para reconectarse o no hacerlo con el último punto de acceso al que ha estado conectado. Si el parámetro autoReconnect tiene valor true intentará reconectarse, si es false no lo intentará. Ejemplo: Wifi.setAutoReconnect(true); |
WiFi.waitForConnectResult() |
Evalúa el intento de conexión del módulo al punto de acceso. La función devuelve uno de los siguientes estados:
Ejemplo: if(WiFi.waitForConnectResult() != WL_CONNECTED){ Serial.printf("No se puede conectar a la red"); } |
WiFi.macAddress() |
Es la función que utilizamos para conocer la dirección MAC de la conexión con el punto de acceso. Ejemplo 1 -MAC generada mediante una cadena (string)-: if (WiFi.status() == WL_CONNECTED) { Serial.printf("Dirección MAC -Server-: %s\n", WiFi.macAddress().c_str()); } Ejemplo 2 -MAC generada por un puntero de 6 elementos- : if (WiFi.status() == WL_CONNECTED) { uint8_t macAddr[6]; WiFi.macAddress(macAddr); Serial.printf("Dirección MAC -Server-: %02x:%02x:%02x:%02x:%02x:%02x\n", macAddr[0], macAddr[1], macAddr[2], macAddr[3], macAddr[4], macAddr[5]); } |
WiFi.localIP() |
Es la función que utilizamos para conocer la dirección IP de la conexión con el punto de acceso. Ejemplo 1 -IP local generada mediante una cadena (string)-: if (WiFi.status() == WL_CONNECTED) { Serial.printf("IP local: %s\n", WiFi.localIP().toString().c_str()); } Ejemplo 2: -IP local generada como una lista de 4 bytes- if (WiFi.status() == WL_CONNECTED) { IPAddress WlocalIP = WiFi.localIP(); Serial.printf("IP local: %d.%d.%d.%d.%d.%d\n", WlocalIP[0], WlocalIP[1], WlocalIP[2], WlocalIP[3]) WlocalIP[4], WlocalIP[5]; } |
WiFi.subnetMask() |
Es la función que utilizamos para conocer el rango de direcciones IP de las conexiones con el punto de acceso (submáscara de red). Ejemplo 1 -Submáscara generada mediante una cadena (string)-: if (WiFi.status() == WL_CONNECTED) { Serial.printf("Submáscara de red: %s\n", WiFi.subnetMask().toString().c_str()); } Ejemplo 2: -Submáscara generada como una lista de 4 bytes- if (WiFi.status() == WL_CONNECTED) { IPAddress WsubnetmaskIP = WiFi.subnetMask(); Serial.printf("Submáscara de red: %d.%d.%d.%d\n", WsubnetmaskIP[0], WsubnetmaskIP[1], WsubnetmaskIP[2], WsubnetmaskIP[3]); } |
WiFi.dnsIP() |
Es la función que utilizamos para conocer el sistema de nombres de dominio (DNS – Domain Name System) del punto de acceso. La función más importante del DNS es «traducir» nombres inteligibles lo identificadores binarios asociados con los equipos conectados a la red p.e. https://www.esploradores.com ↔ http://46.30.215.53 Ejemplo: if (WiFi.status() == WL_CONNECTED) { Serial.printf("DNS #1, #2 IP: %s, %s\n", WiFi.dnsIP().toString().c_str(), WiFi.dnsIP().toString(1).c_str()); } |
WiFi.gatewayIP() |
Es la función que utilizamos para conocer la dirección IP del punto de acceso (privada). Ejemplo 1 -IP privada generada mediante una cadena (string)-: if (WiFi.status() == WL_CONNECTED) { Serial.printf("IP del punto de acceso (privada): %s\n", WiFi.gatewayIP().toString().c_str()); } Ejemplo 2: -IP privada generada como una lista de 4 bytes- if (WiFi.status() == WL_CONNECTED) { IPAddress WgatewayIP = WiFi.gatewayIP(); Serial.printf("IP del punto de acceso (privada): %d.%d.%d.%d\n", WgatewayIP[0], WgatewayIP[1], WgatewayIP[2], WgatewayIP[3]); } |
WiFi.hostname() |
WiFi.hostname() Por defecto el nombre de cliente está formado por «ESP» más los 6 últimos carácterés de la MAC. Por ejemplo, si la MAC es 88:50:V3:5X:77:CC el nombre de cliente sería ESP5X77CC.
WiFi.hostname(aHostname) El parámetro aHostname indicará el nuevo nombre (puede ser una variable del tipo char, const char o String). La función devuelve true si el cambio se ha realizado correctamente o false si no se ha podido realizar. Ejemplo: Serial.printf("Nombre de cliente por defecto: %s\n", WiFi.hostname().c_str()); WiFi.hostname("ESP8266-HOST_01"); Serial.printf("Nuevo nombre de cliente: %s\n", WiFi.hostname().c_str()); |
WiFi.status() | |||||||||||||||
Devuelve el estado de la conexión del módulo con el punto de acceso. Los posibles estados son los siguientes:
Ejemplo: Serial.printf("Connection status: %d\n", WiFi.status()); //0=WL_IDLE_STATUS, 1=WL_NO_SSID_AVAIL, 3=WL_CONNECTED //4=WL_CONNECTED-FAILED y 6=WL_DISCONNECTED |
WiFi.SSID() |
La función devuelve el nombre de la red WiFi (Service Set Identification – SSID) a la que está conectado el módulo.
Ejemplo: Serial.printf("SSID: %s\n", WiFi.SSID().c_str()); |
WiFi.psk() |
La función devuelve la clave del punto de acceso (en inglés pre-shared key – PSK ) al que está conectado el módulo.
Ejemplo: Serial.printf("SSID: %s\n", WiFi.psk().c_str()); |
WiFi.BSSIDstr() |
La función devuelve la dirección MAC del punto de acceso al que está conectado el módulo.
Ejemplo: Serial.printf("BSSID: %s\n", WiFi.BSSIDstr().c_str()); |
WiFi.RSSI() |
La función devuelve la intensidad (en dB) de la señal de la conexión WiFi (Received Signal Strength Indication – RSSI) del módulo con el punto de acceso.
Ejemplo: Serial.printf("RSSI: %d dB\n", WiFi.RSSI()); |
WiFi.beginWPSConfig() |
El estándar WPS (WiFi Protected Setup) no es en sí un sistema de seguridad, sino una definición los mecanismos a través de los cuales los diferentes dispositivos de la red obtienen las credenciales (nombre red -SSID y clave de red – PSK). Por el momento el ESP8266 únicamente tiene implementado uno de los cuatro posibles mecanismos de conexión, el denominado Push Button Configuration-( PBC). Mediante este mecanismo, para la conexión se debe de inicializar el ESP8266 en modo Station y después pulsar el botón WPS del punto de acceso. Dependiendo del resultado de la conexión la función devuelve true o false.
Ejemplo: #include <ESP8266WiFi.h> void setup(void) { Serial.begin(115200); Serial.printf("\n\nConfigurando conexion WiFi STA. %s\n", WiFi.mode(WIFI_STA) ? "" : "¡Error al inicializar en modo Station!"); Serial.println("Presiona el boton WPS de tu router. "); Serial.print(WiFi.beginWPSConfig() ? "Router detectado. Conectando" : "¡Router no detectado!"); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.printf("\nIP local: %s\n", WiFi.localIP().toString().c_str()); } void loop() {} |
beginSmartConfig() / smartConfigDone() / stopSmartConfig() |
La conexión Smart Config del ESP8266 con un punto de acceso se realiza utilizando paquetes especiales que contienen el nombre de usuario y contraseña de punto de acceso deseado. Las tres funciones siguientes se proporcionan para implementar la conexión Smart Config:
>beginSmartConfig(); Inicializa la conexión Smart Config enviando los paquetes de usuario y contraseña al punto de acceso. Dependiendo del resultado de la conexión la función devuelve true o false.
SmartConfigDone(); Consulta el estado de la conexión Smart Config. La función devuelve true o false en función de si el módulo está conectado por este método o no lo está.
stopSmartConfig(); Finaliza la conexión Smart Config y libera del buffer de memoria los datos de usuario y contraseña. Dependiendo del resultado de la acción la función devuelve true o false. |
CIRCUITO ELECTRÓNICO
SKETCH
En el sketch se hace un repaso de gran parte de las funciones de la librería ESP8266WiFi.h que permiten crear en modo Station. Se han añadido comentarios para facilitar su seguimiento.
También en el sketch está insertado el código HTML de la página Web que visualizaremos con el navegador (Mozzilla, Google Chrome, Internet Explorer, etc.), al realizar la conexión con el NodeMCU.
Para poder comprenderlo mejor resulta conveniente verlo a parte.
- Este es el código HTML que se ejecuta cuando el LED está encendido:
<HTTP/1.1 200 OK> <!DOCTYPE HTML> <meta charset='UTF-8'> <html> <!--Se imprime el estado del LED--> <h1 align=center>El LED está ahora: ENCENDIDO</h1> <!--Se imprime una imagen del estado del LED--> <img src='https://esploradores.com/Practicas_html/LED_encendido.png' alt='Bombilla encendida' style='display:block; margin:auto' width='15%'> <!--Se crea un botón para modificar el estado del LED--> <input type='image' src='https://esploradores.com/Practicas_html/apagar_300x88.png' style='display:block; margin:auto' width='25%' onClick=location.href='/LED=OFF'> </html>
- Y este es el código HTML que se ejecuta cuando el LED está apagado:
<HTTP/1.1 200 OK> <!DOCTYPE HTML> <meta charset='UTF-8'> <html> <!--Se imprime el estado del LED--> <h1 align=center>El LED está ahora: APAGADO</h1> <!--Se imprime una imagen del estado del LED--> <img src='https://esploradores.com/Practicas_html/LED_apagado.png' alt='Bombilla apagada' style='display:block; margin:auto' width='15%'> <!--Se crea un botón para modificar el estado del LED--> <input type='image' src='https://esploradores.com/Practicas_html/encender_300x88.png' style='display:block; margin:auto' width='25%' onClick=location.href='/LED=ON'> </html>
Un navegador visualizará la página Web así , en función del estado del LED:
Como se puede observar, se han insertado fotografías para visualizar el estado del LED y para mejorar el aspecto del botón de encendido/apagado. Estás fotografías están ubicadas en las siguientes direcciones:
- https://esploradores.com/Practicas_html/LED_encendido.png
- https://esploradores.com/Practicas_html/LED_apagado.png
- https://esploradores.com/Practicas_html/encender_300x88.png
- https://esploradores.com/Practicas_html/apagar_300x88.png
Se puede probar a modificaras utilizando otras fotografías de Internet.
El botón de encendido y apagado, para lograr la comunicación con el punto de acceso une la IP local del NodeMCU con la cadena «/LED=ON» o «/LED=OFF» en función de lo que hayamos pulsado. Quedaría algo así, en función de la IP local del NodeMCU:
http://192.168.1.133/LED=ON o http://192.1.133.1/LED=ON
De esta manera el NodeMCU, recibe la petición «/LED=ON» o «/LED=OFF» y la analiza utilizando la función peticion.indexOf(), para determinar las acciones a realizar.
El sketch a subir al NodeMCU será el siguiente:
/* NodeMCU Server - Conexión en modo Station con un punto de acceso (router) por Dani No www.esploradores.es Crea una conexión del NodeMCU en modo Station con un punto de acceso que permite encender y apagar un LED conectado a la salida D4 (GPIO02) del módulo NodeMCU. Este código de ejemplo es de público dominio. */ #include <ESP8266WiFi> //Incluye la librería ESP8266WiFi const char* ssid = "JAZZTEL_GnP"; //Indicamos el nombre de la red WiFi (SSID) a la que queremos conectarnos. const char* password = "sdaebssyeqq"; //Indicamos la contraseña de de red WiFi WiFiServer server(80); //Definimos el puerto de comunicaciones int PinLED = 2; // GPIO2 //Definimos el pin de salida - GPIO2 / D4 int estado = LOW; //Definimos la variable que va a recoger el estado del LED void setup() { Serial.begin(115200); pinMode(PinLED, OUTPUT); //Inicializamos el GPIO2 como salida digitalWrite(PinLED, LOW); //Dejamos inicialmente el GPIO2 apagado WiFi.begin(ssid, password); //Inicializamos la conexión del NodeMCU con la red WiFi Serial.printf("\n\nConectando a la red: %s\n", WiFi.SSID().c_str()); while (WiFi.status() != WL_CONNECTED) { // Verifica el estado de la conexión del NodeMCU cada 0,5s hasta que conecta delay(500); Serial.print("."); } Serial.println(""); Serial.println("WiFi conectada"); // Indica que el NodeMCU conectado con la red WiFi server.begin(); // Inicia el NodeMCU en modo Station Serial.println("Servidor inicializado"); Serial.printf("IP local del NodeMCU: %s\n", WiFi.localIP().toString().c_str()); Serial.printf("DNS #1, #2 IP: %s, %s\n", WiFi.dnsIP().toString().c_str(), WiFi.dnsIP(1).toString().c_str()); Serial.printf("Submascara de red -subnetMask-: %s\n", WiFi.subnetMask().toString().c_str()); Serial.printf("Direccion MAC en modo Server -macAddress-: %s\n", WiFi.macAddress().c_str()); Serial.printf("IP privada del punto de acceso -dnsIP-: %s\n", WiFi.gatewayIP().toString().c_str()); Serial.printf("Nombre del NodeMCU como cliente -hostname-: %s\n", WiFi.hostname().c_str()); Serial.printf("Connection status: %d\n0=WL_IDLE_STATUS, 1=WL_NO_SSID_AVAIL, 3=WL_CONNECTED, 4=WL_CONNECTED-FAILED y 6=WL_DISCONNECTED\n", WiFi.status()); Serial.printf("Nombre de red -SSID-: %s\n", WiFi.SSID().c_str()); Serial.printf("Clave de red -Password-: %s\n", WiFi.psk().c_str()); Serial.printf("Direccion MAC del punto de acceso -BSSID-: %s\n", WiFi.BSSIDstr().c_str()); Serial.printf("Intensidad de la conexion con el punto de acceso -RSSI-: %d dB\n", WiFi.RSSI()); Serial.printf("\n\nUtiliza esta URL para conectar: http://%s/\n", WiFi.localIP().toString().c_str()); } void loop() { // Comprueba si el cliente ha conectado WiFiClient client = server.available(); if (!client) { return; } // Espera hasta que el cliente envía alguna petición Serial.println("nuevo cliente"); while(!client.available()){ delay(1); } // Lee la petición String peticion = client.readStringUntil('\r'); Serial.println(peticion); client.flush(); // Comprueba la petición if (peticion.indexOf('/LED=ON') != -1) { estado = HIGH; } if (peticion.indexOf('/LED=OFF') != -1){ estado = LOW; } //Enciende o apaga el LED en función de la petición digitalWrite(PinLED, estado); // Envía la página HTML de respuesta al cliente client.println("HTTP/1.1 200 OK"); client.println(""); //No olvidar esta línea de separación client.println(""); client.println(""); client.println(""); client.print(<h1 align=center>El LED está ahora: "); if(estado == HIGH) { client.print("ENCENDIDO</h1>"); client.print("<img src='https://esploradores.com/Practicas_html/LED_encendido.png' alt='Bombilla encendida' style='display:block; margin:auto' width='15%'>"); client.print("<input type='image' src='https://esploradores.com/Practicas_html/apagar_300x88.png' style='display:block; margin:auto' width='25%' onClick=location.href='/LED=OFF'>"); } else { client.print("APAGADO</h1>"); client.print("<img src='https://esploradores.com/Practicas_html/LED_apagado.png' alt='Bombilla apagada' style='display:block; margin:auto' width='15%'>"); client.print("<input type='image' src='https://esploradores.com/Practicas_html/encender_300x88.png' style='display:block; margin:auto' width='25%' onClick=location.href='/LED=ON'>"); } client.println("</html>"); delay(1); Serial.println("Peticion finalizada"); // Se finaliza la petición al cliente. Se inicializa la espera de una nueva petición. Serial.println(""); }
CONEXIÓN CON EL ACCESS POINT
Para poder conectar con el NodeMCU realizaremos los siguientes pasos:
- Después de instalar es sketch en el NodeMCU -y sin desconectar el cable USB- abriremos el Monitor Serie. Veremos una pantalla similar a la que aparece a continuación.
- Abriremos un navegador Web en el dispositivo cliente que queremos conectar al NodeMCU y escribiremos la dirección URL que aparece en nuestro Monitor Serie. Inmediatamente visualizaremos la página Web que nos servirá para controlar el encendido o apagado del LED.
19 Comments
Leave your reply.