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:

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







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