Internet de les coses amb ESP32 i ESP8266

Exemples Referència Plaques   Recursos CITCEA
Projectes Programació Perifèrics   Inici

Controlem la lluminositat d'un LED enviant un valor enter

En aquest exemple tindrem el mateix LED que a l'exemple anterior però controlarem la seva lluminositat fent servir un valor PWM sobre la sortida 7.

Si no teniu cap LED disponible podeu emprar el LED de la placa, que està a la pota 6.

La pàgina que veurà l'usuari tindrà el següent contingut (vegeu les etiquetes del llenguatge html).

<!DOCTYPE HTML>
<meta charset='UTF-8'>
<html>
<h1>El LED està a 127</h1>
<form action='/' method='get'>
<p></p>
<p></p>
<input type='text' name='LED'>
<p></p>
<p></p>
<input type='submit'>
</form>
</html>

Que ens donarà una pàgina similar a aquesta:

Pàgina web

El programa connecta el microcontrolador a un punt d'accés existent (del que hem de saber l'identificador i la contrasenya), escriu al monitor sèrie la seva adreça IP i es queda a l'espera que algun usuari s'hi connecti. Quan ho fa, li envia la pàgina html en la que l'estat del LED es personalitza segons com estigui en aquell moment. Quan es prem el botó es torna a carregar la pàgina afegint el paràmetre corresponent. Les opcions són l'adreça sense paràmetres o amb un valor entre 0 i 255.

aaa.bbb.ccc.ddd/
aaa.bbb.ccc.ddd/?LED=127

La major part del programa d'aquest exemple és similar a la de l'exemple anterior excepte el tros on es tracta l'ordre GET rebuda i la part en la que es genera la pàgina web que s'envia al navegador.

En els exemples que hem vist fins ara els paràmetres que s'enviaven només podien ser LED=0 o LED=1 i, per tant, era fàcil fer la comprovació. Però quan les opcions són moltes més cal un sistema més eficient per obtenir els paràmetres, especialment en el cas que siguin valors numèrics.

Per això farem servir la funció Dades, que comentarem després. Aquesta funció té dos paràmetres. El primer és la petició que ha rebut el microcontrolador i el segon és el nom del paràmetre que ens interessa. Per exemple, amb la petició

aaa.bbb.ccc.ddd/?LED=1&cas=activar&temp=23.5

Podem obtenir els valors que es posen en els següents comentaris per a cada crida a la funció:

    String peticio = client.readString();
    Serial.print("LED: ");
    Serial.println(Dades(peticio, "LED"));    // Dóna "1"
    Serial.print("cas: ");
    Serial.println(Dades(peticio, "cas"));    // Dóna "activar"
    Serial.print("temp: ");
    Serial.println(Dades(peticio, "temp"));    // Dóna "23.5"

En cas que el valor esperat sigui numèric el podem convertir:

    int led = Dades(peticio, "LED").toInt();
    float temp = Dades(peticio, "temp").toFloat();

A continuació tenim la funció que tot seguit comentarem.

String Dades(String peti, String param){
    String valor = "";
    String sepa;
    int pos;
    if (peti.indexOf("?") != -1) {    // Si no ho troba torna -1
        peti = peti.substring(peti.indexOf("?")+1);
        if (peti.indexOf(param) != -1) {    // Si no ho troba torna -1
            peti = peti.substring(peti.indexOf(param)+param.length());
            sepa = peti.substring(0, 1);
            peti = peti.substring(1);
            pos = peti.indexOf("&");
            if (pos == -1) {
                pos = peti.indexOf(" ");
            }
            if ((pos != 0) && (sepa == "=")){
                valor = peti.substring(0, pos);
            }
        } 
    }
    return valor;
}

Per començar, cerca un interrogant a la petició. Si no el troba vol dir que la petició no conté paràmetres i, per tant, no cal fer res més. Tot seguit es queda amb el que hi ha després de l'interrogant (els paràmetres) i descarta la resta. Llavors mira si troba el nom del paràmetre, si no el troba tornarà una cadena buida i ja hem acabat. Si troba el nom del paràmetre, comprova que vagi seguit del signe = i es queda amb tot el que hi ha entre el signe = i el signe & (quan el paràmetre no és el darrer) o un espai en blanc (quan el paràmetre és el darrer) i això és el que retorna.

El programa d'aquest exemple, que fa servir la funció comentada, és el següent:

// Aquest programa està parcialment basat en els exemples de la pàgina
// https://www.arduino.cc/en/Tutorial/LibraryExamples#wifi1010
#include <SPI.h>    // Carreguem la biblioteca SPI
#include <WiFiNINA.h>    // Carreguem la biblioteca WiFiNINA
#define LED 7    // pota on hem connectat el LED
const char idXarxa[] = "xarxa-wifi";    // Nom del punt d'accés 
const char contrasenya[] = "contrasenya-wifi";    // Contrasenya de connexió 
byte valorLED = 0;    // Variable que recorda el valor enviat al LED
bool enviar = false;
int status = WL_IDLE_STATUS;
WiFiServer server(80);    // Creem un objecte de comunicació amb el port 80
                          // El port 80 és el de defecte per a http
String Dades(String peti, String param){
    String valor = "";
    String sepa;
    int pos;
    if (peti.indexOf("?") != -1) {    // Si no ho troba torna -1
        peti = peti.substring(peti.indexOf("?")+1);
        if (peti.indexOf(param) != -1) {    // Si no ho troba torna -1
            peti = peti.substring(peti.indexOf(param)+param.length());
            sepa = peti.substring(0, 1);
            peti = peti.substring(1);
            pos = peti.indexOf("&");
            if (pos == -1) {
                pos = peti.indexOf(" ");
            }
            if ((pos != 0) && (sepa == "=")){
                valor = peti.substring(0, pos);
            }
        } 
    }
    return valor;
}
void setup() {    // Inicialització
    Serial.begin(9600);    // Monitor sèrie
    pinMode(LED, OUTPUT);    // La pota del LED és sortida
    analogWrite(LED, 0);    // Comencem amb el LED apagat
    if (WiFi.status() == WL_NO_MODULE) {
        Serial.println("No s'ha trobat el dispositiu Wi-Fi");
        while (true);    // Bloquegem el programa
    }
    String versio = WiFi.firmwareVersion();
    if (versio < "1.0.0") {
        Serial.println("Convindria actualitzar el firmware");
    }
    while (status != WL_CONNECTED) {
        Serial.print("Connectant a la xarxa ");
        Serial.println(idXarxa);
        status = WiFi.begin(idXarxa, contrasenya);
        delay(10000);    // Ho tornarem a intentar passats 10 s
    }
    server.begin();    // Posem en marxa el servidor
    Serial.print("Connectat a "); 
    Serial.println(WiFi.SSID());
    Serial.print("Estat de la connexió: ");
    Serial.println(WiFi.status()); 
    Serial.print("Adreça IP del dispositiu: ");
    Serial.println(WiFi.localIP()); 
    Serial.print("Intensitat del senyal: ");
    Serial.print(WiFi.RSSI()); 
    Serial.println(" dBm");
    Serial.println(); 
    Serial.print("Ja pots fer la teva connexió al dispositiu ");
    Serial.println(WiFi.localIP()); 
}
void loop() {    // Programa que es repeteix indefinidament
    if (status != WiFi.status()) {    // Mirem si ha canviat l'estat de la connexió
        status = WiFi.status();
        if (status == WL_AP_CONNECTED) {
            Serial.println("Dispositiu connectat al punt d'accés");
        } else {
            Serial.println("El dispositiu s'ha desconnectat del punt d'accés");
        }
    }
    WiFiClient client = server.available();    // Mirem si hi ha clients
    if (client) {                             // Si hi ha un client...
        Serial.println("Nou client");
        String peticio = "";                // Aquí guardarem una línia de la petició del client
        while (client.connected()) {            // Mentre el client estigui connectat
            if (client.available()) {             // Si hi ha dades disponibles
                char c = client.read();             // Llegim un byte
                Serial.write(c);
                if (c == '\n') {                    // Mirem si és un salt de línia
                    // Si hem rebut una línia buida vol dir que tenim dos salts de línia seguits
                    // vol dir que ha acabat la petició del client i enviem la resposta:
                    if (peticio.length() == 0) {
                        enviar = true;    // Llestos per enviar la resposta
                        break;    // Sortim del while
                    } else {      // Si hem rebut un salt de línia
                        // Ha acabat una línia de la petició, anem a veure si és la que ens interessa
                        // Rebrem un munt de línies però només hem d'analitzar la que comença per GET
                        if (peticio.indexOf("GET") != -1) {    // Si no ho troba torna -1
                            // Ara cal comprovar si s'ha demanat una acció sobre el LED
                            if (Dades(peticio, "LED") != "") {    // Hi havia el paràmetre?
                                valorLED = Dades(peticio, "LED").toInt();
                            } 
                            analogWrite(LED, valorLED);    // Enviem el valor al LED
                        }
                        peticio = "";    // Comencem línia amb una línia buida
                    }
                } else if (c != '\r') {    // Si el caràcter no és un retorn
                    peticio += c;      // Afegim el caràcter rebut
                }
            }
        }
        if (enviar) {
            // Resposta al client
            client.println("HTTP/1.1 200 OK");
            client.println();    // Imprescindible línia en blanc
            client.println("<!DOCTYPE HTML>");
            client.println("<meta charset='UTF-8'>");
            client.println("<html>");
            // En el títol l'estat del LED
            client.print("<h1>El LED està a ");                 
            client.print(valorLED);  
            client.println("</h1>");
            client.println("<form action='/' method='get'>");
            client.println("<p></p>");
            client.println("<p></p>");
            client.println("<input type='text' name='LED'>");
            client.println("<p></p>");
            client.println("<p></p>");
            client.println("<input type='submit'>");
            client.println("</form>");
            client.println("</html>"); 
            delay(1);
            enviar = false;
        }
        client.stop();    // Tanquem la connexió
        Serial.println("Fi de la connexió amb el client");
    }
}

Per provar-ho, cal editar el programa per posar-hi l'identificador de xarxa i la contrasenya per fer la connexió i enviar el programa al microcontrolador. Tingueu present que la xarxa eduroam que fem servir habitualment a la UPC no serveix, ja que té un sistema de connexió diferent. Un cop connectat el microcontrolador a la xarxa podrem veure la seva adreça IP consultant el monitor sèrie de l'entorn de programació. Llavors, podem agafar un dispositiu mòbil i connectar-nos a la mateixa xarxa Wi-Fi. Un cop connectats a la xarxa podem obrir un navegador i posar l'adreça IP del microcontrolador a la barra d'adreces. Llavors ens hauria de mostrar la pàgina que em vist més amunt i picant els botons podem encendre i apagar el LED.

En aquest exemple hem posat un enllaç per encendre i un altre per apagar. També podíem haver posat un únic enllaç que segons l'estat del LED ens permetés passar-lo a l'estat contrari. Això es pot fer que el destí de l'enllaç i el text corresponent siguin diferents segons l'estat del LED.

El nostre programa de prova escriu unes quantes coses al monitor sèrie (instruccions Serial.print i Serial.println). Quan ja tinguem clar que el programa funciona, les podem eliminar quasi totes i probablement anirà una mica més ràpid.

 

 

 

 

 

 

 

 

 

 

Llicència de Creative Commons
Aquesta obra d'Oriol Boix està llicenciada sota una llicència no importada Reconeixement-NoComercial-SenseObraDerivada 3.0.