Internet de les coses amb ESP32 i ESP8266

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

Termòstat

Programa del grup 5

En aquest cas el microcontrolador funciona com a client i es comunica amb un full de càlcul de Google i un calendari. El programa del microcontrolador és el següent:

#include <SPI.h>
#include <WiFiNINA.h>
#include <DHT.h>
#include <Adafruit_NeoPixel.h>
#define DHTPIN 2
#define DHTTYPE DHT22
#define LED 6
#define server_len 50
#define pag_len 400
#define mis_len 2
#define periode 30000
#define periodeCal 20000
#define periodeExcel 300000
#define periodeExAs 25000
#define periodeComprov 7500
IPAddress IPEx;
const char idXarxa[] = "xarxa-wifi";    // Nom del punt d'accés 
const char contrasenya[] = "contrasenya-wifi";    // Contrasenya de connexió 
char serv[server_len];
char serverEA[server_len];
char pagina[pag_len];
char paginaEA[pag_len];
const char formulari[] = "1FAIpQLSdZTDjKa7v9IndicMq9b8p6-obXYXVctXnjZzv6gQcq5drGWQ";
const char adrePost[] = "/forms/d/e/1FAIpQLSdZTDjKa7v9IndicMq9b8p6-obXYXVctXnjZzv6gQcq5drGWQ/formResponse";
const char paginaExAs[] = "/macros/s/AKfycbxRvhvbFjS8WuMln8oXhoQk0W22_1xHuqQI4EFa8urMlGFGCkYC/exec?accio=darrer";
String camp1 = "entry.1965398139";
String camp2 = "entry.1538944238";
String camp3 = "entry.1785566567";
char serverEx[] = "docs.google.com";
const char serverExAs[] = "script.google.com";
unsigned long ConnexioCal = 0;
unsigned long ConnexioExcel = 0;
unsigned long ConnexioExAs = 0;
unsigned long ullum = 0;
int tempde, hum, temp, consigna, TempCal, consignaAnt, tempAssigEx;
int comp_lin = 0;
byte lloc, col, mode, n;
String temperatura, humitat, tempdes, consi, server1, pagina1, dataEx;
String peticio = "";
String peticioAux = "";
String missatge[mis_len];
String peticioExAs = "";
String peticioExAux = "";
float humEx, tempEx;
bool estat = LOW;
bool enviar = false;
bool stat = false;
bool pendent, completa, redir, pendentExAs, completaExAs, redirExAs;
bool ara = false;
bool araExAs = false;
bool ini_msg = false;
bool comprovar = false;
bool FunCEx = true;
bool LlumC;
String readString;
int status = WL_IDLE_STATUS;
DHT dht(DHTPIN, DHTTYPE);
Adafruit_NeoPixel cadena = Adafruit_NeoPixel(1, 1, NEO_GRB + NEO_KHZ800);
WiFiServer server(80);
WiFiSSLClient client;
void processa(String missat) {
  String Acte, Inici, Final;
  Acte = missat.substring(0, missat.indexOf(","));
  if (Acte.toInt() > 300) {
    Acte = 300;
  } else if (Acte.toInt() < 70) {
    Acte = 70;
  }
  missat = missat.substring(missat.indexOf(",") + 1);
  Inici = missat.substring(0, missat.indexOf(","));
  Final = missat.substring(missat.indexOf(",") + 1);
  Serial.print("Temperatura: ");
  Serial.println(Acte);
  Serial.print("Inici: ");
  Serial.println(Inici);
  Serial.print("Final: ");
  Serial.println(Final);
  consigna = (Acte.toInt()); lloc = 3; comprovar = false; FunCEx = true;
}
void Web() {
  hum = dht.readHumidity() * 10;
  temp = dht.readTemperature() * 10;
  if (temp < (consigna - 5)) {
    stat = true;
    digitalWrite(LED, HIGH);
  } else {
    if (temp > (consigna + 5)) {
      digitalWrite(LED, LOW);
      stat = false;
    } if (temp <  (consigna + 5) && (stat != false)) {
      digitalWrite(LED, HIGH);
    } else {
      digitalWrite(LED, LOW);
    }
  }
  if (temp < tempde - 2 && ((millis() - ullum > periode) || col != 1)) { // cada X temps es repetira la funció
    col = 1;
    ullum = millis();
    delay(500);
    cadena.setPixelColor(0, 40, 0, 0);
    cadena.show();
    delay(1500);
    cadena.setPixelColor(0, 0, 0, 0);
    cadena.show();
  } else if (temp <= tempde + 2 && temp >= tempde - 2 && ((millis() - ullum > periode) || col != 2)) {
    col = 2;
    ullum = millis();
    delay(500);
    cadena.setPixelColor(0, 0, 40, 0);
    cadena.show();
    delay(1500);
    cadena.setPixelColor(0, 0, 0, 0);
    cadena.show();
  } else if (temp > tempde + 2 && ((millis() - ullum > periode) || col != 3)) {
    col = 3;
    ullum = millis();
    delay(500);
    cadena.setPixelColor(0, 0, 0, 40);
    cadena.show();
    delay(1500);
    cadena.setPixelColor(0, 0, 0, 0);
    cadena.show();
  }
  if (status != WiFi.status()) {    // Mirem si ha canviat l'estat de la connexió
    status = WiFi.status();
    if (status == WL_AP_CONNECTED) {
      Serial.println(F("Dispositiu connectat al punt d'accés"));
    } else {
      Serial.println(F("El dispositiu s'ha desconnectat del punt d'accés"));
    }
  }
  WiFiClient client = server.available();
  if (client) {
    Serial.println(F("Nou client"));
    String peticio = "";
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        Serial.write(c);
        if (c == '\n') {
          if (peticio.length() == 0) {
            enviar = true;
            break;
          } else {
            if (peticio.indexOf("GET") != -1) {
              if ((peticio.indexOf("augmentar") != -1 && lloc == 0) && lloc != 3) {
                tempde = (tempde + 5); consigna = tempde; LlumC = true; consignaAnt = consigna;
              } else if ((peticio.indexOf("disminuir") != -1 && lloc == 0) && lloc != 3) {
                tempde = (tempde - 5); consigna = tempde; LlumC = true; consignaAnt = consigna;
              } else if ((peticio.indexOf("casa") != -1) && lloc != 3) {
                lloc = 0, consigna = tempde; LlumC = true; consignaAnt = consigna;
              }
              if ((peticio.indexOf("fcasa") != -1) && lloc != 3) {
                lloc = 1, consigna = 170; LlumC = true; consignaAnt = consigna;
              }
              if ((peticio.indexOf("vac") != -1) && lloc != 3) {
                lloc = 2, consigna = 70; LlumC = true; consignaAnt = consigna;
              }
              if (LlumC) {
                cadena.setPixelColor(0, 255, 255, 255);
                cadena.show();
                delay(100);
                cadena.setPixelColor(0, 0, 0, 0);
                cadena.show();
                comprovar = false; LlumC = false;
              }
            }
            peticio = "";
          }
        } else if (c != '\r') {
          peticio += c;
        }
      }
    }
    if (consigna > 300) {
      consigna = 300;
    } else if (consigna < 70)
      consigna = 70;
    if (tempde > 300) {
      tempde = 300;
    } else if (tempde < 70) {
      tempde = 70;
    }
    if (enviar) {
      if (!(isnan(hum) || isnan(temp))) {    // Comprovació dels valors de temperatura i humitat
        temperatura = String(float(temp) / 10);
        humitat = String(float(hum) / 10);
        consi = String(float(consigna) / 10);
        temperatura.replace('.', ',');
        humitat.replace('.', ',');
        consi.replace('.', ',');
        client.println(F("HTTP/1.1 200 OK"));
        client.println();    // Imprescindible línia en blanc
        client.println(F("<!DOCTYPE HTML>"));
        client.println(F("<meta charset='UTF-8'>"));
        client.println(F("<html>"));
        client.print(F("<body style=background-image:linear-gradient(90deg,#242424,#2b2b2b); "));
        client.print(F("style=background-color:#303030;>"));
        client.print(F("<center>"));
        client.print(F("<h1 style=font-family:arial;color:white;background-color:#808080;"));
        client.println(F("height:37px;text-align:left;> El termòstat està connectat</h1>"));
        client.println(F("<p></p>"));
        client.println(F("<p></p>"));
        if (stat == false) {
          client.println(F("<h2 style = font-family:arial;color:white;>La caldera està apagada</h2>"));
        } else {
          client.println(F("<h2 style = font-family:arial;color:white;>La caldera està encesa</h2>"));
        }
        client.println(F("<p></p>"));
        client.println(F("<p></p>"));
        client.print(F("<p style = font-family:arial;;color:white;>Temperatura actual: "));
        client.print(temperatura);
        client.println(F(" °C</p>"));
        client.println(F("<p></p>"));
        client.print(F("<p style = font-family:arial;color:white;>Humitat actual: "));
        client.print(humitat);
        client.println(F(" %</p>"));
        client.println("<p></p>");
        client.println("<p></p>");
        client.print(F("<p style = font-family:arial;color:white;>La temperatura de consigna és </p>"));
        client.print(F("<h2 style = font-family:arial;text-align:center;color:white;font-size:30px;"));
        client.print(F("background-color:orange;border-radius:17px;height:33px;width:150px;>"));
        client.print("  " + consi + " °C  ");
        client.println(F("</h2>"));
        client.print(F("<button style=height:30px;width:40px;background-color:transparent;"));
        client.print(F("border-color:orange;color:orange;border-radius:13px; "));
        client.println(F("onClick=location.href='/?disminuir'>-</button>"));
        client.print(F("<button style=height:30px;width:80px;background-color:transparent;"));
        client.print(F("border-color:orange;color:orange;border-radius:13px; "));
        client.println(F("onClick=location.href='/'>Actualitzar</button>"));
        client.print(F("<button style=height:30px;width:40px;background-color:transparent;"));
        client.print(F("border-color:orange;color:orange;border-radius:13px; "));
        client.println(F("onClick=location.href='/?augmentar'>+</button>"));
        client.println(F("<p></p>"));
        if (lloc == 0) {
          client.print(F("<button style=height:30px;width:100px;background-color:orange;"));
          client.print(F("border-color:white;color:white;border-radius:13px; "));
          client.println(F("onClick=location.href='/?casa'>Dins de casa</button>"));
          client.print(F("<button style=height:30px;width:100px;background-color:transparent;"));
          client.print(F("border-color:orange;color:orange;border-radius:13px; "));
          client.println(F("onClick=location.href='/?fcasa'>Fora de casa</button>"));
          client.print(F("<button style=height:30px;width:100px;background-color:transparent;"));
          client.print(F("border-color:orange;color:orange;border-radius:13px; "));
          client.println(F("onClick=location.href='/?vac'>Vacances</button>"));
        } if (lloc == 1) {
          client.print(F("<button style=height:30px;width:100px;background-color:transparent;"));
          client.print(F("border-color:orange;color:orange;border-radius:13px; "));
          client.println(F("onClick=location.href='/?casa'>Dins de casa</button>"));
          client.print(F("<button style=height:30px;width:100px;background-color:orange;"));
          client.print(F("border-color:white;color:white;border-radius:13px; "));
          client.println(F("onClick=location.href='/?fcasa'>Fora de casa</button>"));
          client.print(F("<button style=height:30px;width:100px;background-color:transparent;"));
          client.print(F("border-color:orange;color:orange;border-radius:13px; "));
          client.println(F("onClick=location.href='/?vac'>Vacances</button>"));
        } if (lloc == 2) {
          client.print(F("<button style=height:30px;width:100px;background-color:transparent;"));
          client.print(F("border-color:orange;color:orange;border-radius:13px; "));
          client.println(F("onClick=location.href='/?casa'>Dins de casa</button>"));
          client.print(F("<button style=height:30px;width:100px;background-color:transparent;"));
          client.print(F("border-color:orange;color:orange;border-radius:13px; "));
          client.println(F("onClick=location.href='/?fcasa'>Fora de casa</button>"));
          client.print(F("<button style=height:30px;width:100px;background-color:orange;"));
          client.print(F("border-color:white;color:white;border-radius:13px; "));
          client.println(F("onClick=location.href='/?vac'>Vacances</button>"));
        } if (lloc == 3) {
          client.print(F("<button style=height:30px;width:100px;background-color:transparent;"));
          client.print(F("border-color:orange;color:orange;border-radius:13px;disabled; "));
          client.println(F("onClick=location.href='/?casa'>Dins de casa</button>"));
          client.print(F("<button style=height:30px;width:100px;background-color:transparent;"));
          client.print(F("border-color:orange;color:orange;border-radius:13px;disabled; "));
          client.println(F("onClick=location.href='/?fcasa'>Fora de casa</button>"));
          client.print(F("<button style=height:30px;width:100px;background-color:transparent;"));
          client.print(F("border-color:orange;color:orange;border-radius:13px;disabled; "));
          client.println(F("onClick=location.href='/?vac'>Vacances</button>"));
        }
        client.println(F("</center>"));
        client.print(F("<p style=position:fixed;bottom:0;color:white;font-family:arial;"));
        client.println(F("font-size:small;>Projecte Iot - Grup5</p>"));
        client.println(F("</body>"));
        client.println(F("</html>"));
        enviar = false;
        if (lloc != 3) {
          n = lloc;
        }
      }
    }
    client.stop();
    Serial.println("Client desconnectat");
  }
}
void Calendar() {
  while (client.available()) {
    char c = client.read();
    if (c == '\n') {
      peticioAux = peticio;
      peticio = "";
      completa = true;
    } else {
      peticio += c;
    }
    if (completa) {
      if ((ini_msg) && (comp_lin < mis_len) && (peticioAux.length() > 1)) {
        missatge[comp_lin++] = peticioAux;
      }
      if (peticioAux.startsWith(F("HTTP/1.1 200"))) {
        pendent = true;
        redir = false;
      }
      if (peticioAux.startsWith(F("HTTP/1.1 302"))) {
        redir = true;
      }
      if (redir && (peticioAux.startsWith(F("Location:")))) {
        String adre = peticioAux.substring(peticioAux.indexOf("//") + 2);
        server1 = adre.substring(0, adre.indexOf(".com") + 4);
        pagina1 = adre.substring(adre.indexOf(".com") + 4);
        server1.toCharArray(serv, server_len);
        pagina1.toCharArray(pagina, pag_len);
        ara = true;
      }
      if (pendent && peticioAux.startsWith(F("Connection: close"))) {
        ini_msg = true;
      }
      completa = false;
    }
  }
  if (pendent) {
    pendent = false;
    if (comp_lin > 0) {
      for (byte k = 0; k < mis_len; k++) {
        if (missatge[k].length() > 1) {
          Serial.print(F("--- Tasca "));
          Serial.print(k + 1);
          Serial.println(F(" ---"));
          processa(missatge[k]);
          mode = 0;
        }
      }
    } else {
      Serial.println(F("El calendari no té cap esdeveniment proper."));
      consigna = consignaAnt; mode = 0; lloc = n; FunCEx = false;
    }
  }
  if (ara || ((millis() - ConnexioCal > periodeComprov))) {
    ini_msg = false;
    comp_lin = 0;
    for (byte k = 0; k < mis_len; k++) {
      missatge[k] = "";
    }
    if (!redir) {
      server1 = "script.google.com";
      pagina1 = "/macros/s/AKfycbyoY706ozYdpeDX5AXwEadBwS5PfjPwXWXtZgeCZacD_ehxoA9t/exec";
      server1.toCharArray(serv, server_len);
      pagina1.toCharArray(pagina, pag_len);
    }
    ara = false;
    client.stop();
    if (client.connect(serv, 443)) {
      Serial.println(F("S'ha fet la connexió al servidor"));
      client.print(F("GET "));
      client.print(pagina);
      client.println(F(" HTTP/1.1"));
      client.print(F("Host: "));
      client.println(serv);
      client.println(F("Connection: close"));
      client.println();
    } else {
      Serial.println(F("Connexió fallida"));
    }
    ConnexioCal = millis();
  }
}
void Excel() {
  dataEx = "";
  humEx = dht.readHumidity();
  tempEx = dht.readTemperature();
  dataEx += camp1;
  dataEx += "=";
  dataEx += tempEx;
  dataEx += "&";
  dataEx += camp2;
  dataEx += "=";
  dataEx += humEx;
  dataEx += "&";
  dataEx += camp3;
  dataEx += "=";
  dataEx += String(IPEx[0]) + "." + String(IPEx[1]) + "." + String(IPEx[2]) + "." + String(IPEx[3]);
  dataEx += "&submit=Submit";
  if (client.connect(serverEx, 443)) {
    Serial.println(F("S'ha fet la connexió al servidor"));
    client.print(F("POST "));
    client.print(adrePost);
    client.print(F("?formkey="));
    client.print(formulari);
    client.println(F("&ifq HTTP/1.1"));
    client.print(F("Host: "));
    client.println(serverEx);
    client.println(F("Content-Type: application/x-www-form-urlencoded"));
    client.println(F("Connection: close"));
    client.print(F("Content-Length: "));
    client.println(dataEx.length());
    client.println();
    client.print(dataEx);
    client.println();
    Serial.print(F("Enviat     T = "));
    Serial.print(tempEx);
    Serial.print(F("     H = "));
    Serial.println(humEx);
    mode = 0; ConnexioExcel = millis();
  }
  delay(1000);
  dataEx = "";
  client.stop();
  if (!client.connected()) {
    client.stop();
  }
}
void ExcelAs() {
  if (comprovar == false) {
    //consignaAnt = consigna;
    comprovar = true;
  }
  while (client.available()) {
    char c = client.read();
    if (c == '\n') {
      peticioExAux = peticioExAs;
      peticioExAs = "";
      completaExAs = true;
    } else {
      peticioExAs += c;
    }
    if (completaExAs) {
      if (peticioExAux.startsWith("HTTP/1.1 200")) {
        pendentExAs = true;
      }
      if (peticioExAux.startsWith("HTTP/1.1 302")) {
        redirExAs = true;
      }
      if (redirExAs && (peticioExAux.startsWith("Location:"))) {
        String adreEA = peticioExAux.substring(peticioExAux.indexOf("//") + 2);
        String server1EA = adreEA.substring(0, adreEA.indexOf(".com") + 4);
        String pagina1EA = adreEA.substring(adreEA.indexOf(".com") + 4);
        server1EA.toCharArray(serverEA, 50);
        pagina1EA.toCharArray(paginaEA, 400);
        araExAs = true;
      }
      completaExAs = false;
    }
  }
  if (pendentExAs) {
    pendentExAs = false;
    if (peticioExAs == "") {
      peticioExAs = peticioExAux;
      mode = 0; consigna = consignaAnt; lloc = n; FunCEx = true;
      Serial.println(F("No s'ha establert cap temperatura amb l'App."));
    } else {
      tempAssigEx = peticioExAs.toInt();
      Serial.print(F("La temperatura assignada a través de l'App és: "));
      Serial.print(float(tempAssigEx) / 10);
      Serial.println(F(" °C"));
      mode = 0; consigna = tempAssigEx; FunCEx = false;
      ConnexioExAs = millis(); lloc = 3;
    }
  }
  if (araExAs || ((millis() - ConnexioExAs > periodeComprov))) {
    if (redirExAs) {
      redirExAs = false;
    } else {
      for (int j = 0; j < server_len; j++) {
        serverEA[j] = serverExAs[j];
      }
      for (int j = 0; j < pag_len; j++) {
        paginaEA[j] = paginaExAs[j];
      }
    }
    araExAs = false;
    client.stop();
    if (client.connect(serverEA, 443)) {
      Serial.println(F("S'ha fet la connexió al servidor"));
      client.print(F("GET "));
      client.print(paginaEA);
      client.println(F(" HTTP/1.1"));
      client.print(F("Host: "));
      client.println(serverEA);
      client.println(F("Connection: close"));
      client.println();
      ConnexioExAs = millis();
    } else {
      Serial.println(F("Connexió fallida"));
    }
  }
}
void setup() {
  Serial.begin(9600);
  dht.begin();
  pinMode(LED, OUTPUT);
  digitalWrite(LED, LOW);
  cadena.begin();
  cadena.setPixelColor(0, 0, 0, 0);
  cadena.show();
  if (WiFi.status() == WL_NO_MODULE) {
    Serial.println(F("No s'ha trobat el dispositiu Wi-Fi"));
    while (true);
  }
  String versio = WiFi.firmwareVersion();
  if (versio < "1.0.0") {
    Serial.println(F("Convindria actualitzar el firmware"));
  }
  while (status != WL_CONNECTED) {
    Serial.print(F("Connectant a la xarxa "));
    Serial.println(idXarxa);
    status = WiFi.begin(idXarxa, contrasenya);
    delay(7500);
  }
  IPEx = WiFi.localIP();
  server.begin();
  Serial.print(F("Connectat a "));
  Serial.println(WiFi.SSID());
  Serial.print(F("Estat de la connexió: "));
  Serial.println(WiFi.status());
  Serial.print(F("Adreça IP: "));
  Serial.println(WiFi.localIP());
  Serial.print(F("Intensitat: "));
  Serial.print(WiFi.RSSI());
  Serial.println(F(" dBm"));
  Serial.println();
  delay(2500);
  tempde = 10 * ((roundf(dht.readTemperature() * 2)) / 2);
  consigna = tempde; consignaAnt = tempde; lloc = 0; col = 0; mode = 0;
}
void loop() {
  if (ConnexioCal > millis()) {
    ConnexioCal = millis();
  }
  if (ConnexioExcel > millis()) {
    ConnexioExcel = millis();
  }
  if (ConnexioExAs > millis()) {
    ConnexioExAs = millis();
  }
  if  ((((millis() - ConnexioCal) > periodeCal) || (mode == 1)) && FunCEx == true) {
    mode = 1;
  } else if (((millis() - (ConnexioExcel)) > periodeExcel) || mode == 2) {
    mode = 2;
  } else if ((((millis() - (ConnexioExAs)) > periodeExAs) || (mode == 3)) && FunCEx == false) {
    mode = 3;
  }
  if (mode == 0) {
    Web();
  }
  if (mode == 1) {
    Calendar();
  }
  if (mode == 2) {
    Excel();
  }
  if (mode == 3) {
    ExcelAs();
  }
}

També hi ha un script al Google Drive que permetia interactuar amb el calendari en el que es guarda la informació de les temperatures de consigna de la caldera.

var idCal = "hg0nmav8s000ltd36p9ujkumqs@group.calendar.google.com";
function doGet(e) {
  var salt = "\n";
  var cal = CalendarApp.getCalendarById(idCal);
  if (!cal) {  
    resultat = "Calendari no trobat!";
    return ContentService.createTextOutput(resultat);
  }
  var ara = new Date();  
  var final = new Date();
  final.setHours(23);  
  final.setMinutes(59);
  var esdev = cal.getEvents(ara, final);
  var numEsdev = esdev.length;  
  var resultat = "";
  if (numEsdev > 0){  
    resultat = resultat + dades(esdev[0]) + salt;  
  }
  if (numEsdev > 1){ 
    resultat = resultat + dades(esdev[1]) + salt; 
  }
  return ContentService.createTextOutput(resultat); 
}
function dades(esdAct) {
  var descrip = esdAct.getTitle();  
  descrip = parseFloat(descrip.replace(",","."));
  if (isNaN(descrip)) {
    var res = "";
    return res;
  }
  descrip = ((Math.round(descrip*2))/2)*10
  var dataIni = esdAct.getStartTime();  
  var dataFi = esdAct.getEndTime();  
  var ini = dataIni.getHours() + "." + dataIni.getMinutes();
  var fi = dataFi.getHours() + "." + dataFi.getMinutes();
  var resul = descrip + "," + ini + "," + fi;
  
  var dataAct = new Date();
  var horaAct = dataAct.getHours();
  var minutAct = dataAct.getMinutes();
  var temp = horaAct * 60 + minutAct;
  var hr = dataIni.getHours();
  var min = dataIni.getMinutes();
  var dif = ((hr * 60 + min) - temp);
  if (dif < 30) {
    return resul;
  } else {
    var res = "";
    return res;
  }
}

Aquest grup va fer una aplicació amb App Inventor que permetia interaccionar amb el termòstat. La disposició d'elements a la pantalla és com es mostra a les imatges següents:

Pantalla         Pantalla

El programa d'aquesta pantalla és el següent:

Programa
Programa
Programa
Programa
Programa
Programa

 

 

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