Internet de les coses amb ESP32 i ESP8266

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

Guardem les lectures de temperatura i humitat a Google Drive fent servir un script

En aquest exemple farem servir un full de càlcul de Google Drive on guardarem les lectures del sensor de temperatura i humitat. Podem crear un codi executable (anomenat script) que interactuï amb el nostre full de càlcul. Aquest codi rebrà l'ordre d'escriptura que li envia el microcontrolador i afegirà una línia al full de càlcul amb els valors rebuts. Una manera senzilla d'executar aquest codi des del microcontrolador és creant una funció que s'executi quan rep una ordre HTTP de tipus get. El nostre script tindrà, doncs, una adreça URL que ens permetrà executar-lo.

En aquest exemple no pretenem entendre com està fet aquest codi ja que el nostre objectiu és un altre. Així, doncs, el que farem serà explicar com adjuntar l'script al nostre full de càlcul i després comentarem com fer-lo servir.

Obrim la nostra pàgina de Google Drive i creem un full de càlcul nou. A la primera filera posem els títols de les columnes. A la primera, per exemple, hi podem posar Temperatura i Humitat a la segona. Un cop introduït, es veuria d'aquesta manera:

Columnes de la taula

Les pàgines i els serveis de Google canvien d'aspecte amb certa freqüència. Les imatges que trobarem en aquest apartat, per tant, poden no correspondre exactament amb les reals. Aquí l'important són els passos, no l'aspecte.

De moment, el nostre full de càlcul és només accessible per a ús personal. Si hi volem accedir des de l'App Inventor ho haurem de canviar. Piquem sobre el botó Comparteix.... Se'ns obrirà una finestra en la que picarem el botó Opcions avançades. On s'indica Qui hi té accés hi deu dir Privat i ho hem de canviar per Activat: tothom que tingui l'enllaç i en el desplegable on diu Pot visualitzar haurem de triar Pot editar (en aquest cas ho podríem deixar a Pot visualitzar però llavors no podríem modificar el formulari des d'altres aplicacions). En aquesta finestra hi surt un enllaç que copiarem i ens guardarem en algun lloc, per exemple un document del bloc de notes. L'enllaç obtingut serà similar a aquest:

https://docs.google.com/spreadsheets/d/1TThsoSjkeMSfwEKy4mn_4QEYH96sxv3VURqE3WHCTswDA/edit?usp=sharing

D'aquesta adreça ens interessa el codi de la taula, que és una seqüència llarga de caràcters situada entre barres. Aquí l'hem marcada en verd perquè sigui fàcil d'identificar. Piquem al botó Fet. Ara anem a la pestanya Fitxer i piquem sobre l'opció Publica al web.... Se'ns obrirà una finestra en la que picarem el botó Publica, acceptarem la confirmació i tancarem la finestra.

En el nostre full de càlcul, anirem a la pestanya Extensions i triarem l'opció Apps Script. Se'ns obrirà una finestra similar a la següent:

Vista del programa

Hem d'esborrar la funció buida myFunction i deixar l'espai en blanc. Aquest és l'script que farem servir:

// Funció per interaccionar amb el full de càlcul des del microcontrolador
// Oriol Boix, 2020
// Sota llicència Creative Commons BY-NC-ND
// https://creativecommons.org/licenses/by-nc-nd/3.0/deed.es_ES
//
// Les variables següents ens permeten personalitzar l'script al nostre projecte
// En principi, no hauríem de tocar la resta de l'script 
var IdFull = "14eaPdj5ge66EYiFmto1LL37kxdTkmzLZTxrib1rH9CI"; // Identificador del full de càlcul 
              // S'aconsegueix picant a Comparteix i triant Opcions avançades
var numFull = 0;  // Número del full amb el que hem de treballar
// Script per interactuar amb el full de càlcul
// Funció que s'executa quan hi ha una ordre get
// La nostra funció tindrà dos paràmetres:
      // t    Valor de la temperatura
      // h    Valor de la humitat
function doGet(e) {
  var resultat = '';
  var camps = new Array(2);  // Valors per guardar a la taula
  // Assignem els paràmetres a variables
  var Temp = e.parameter.t; 
  var Hum = e.parameter.h; 
  if ((Temp == undefined) || (Hum == undefined)){
    resultat = 'Falten paràmetres';
  } else {
    camps[0] = Temp;
    camps[1] = Hum;
    var sh = SpreadsheetApp.openById(IdFull);
    var sheet = sh.getSheets();
    sheet[numFull].appendRow(camps);   // Afegeix una fila amb la llista de dades en format matriu 
    resultat = 'Valors guardats';
  }
  return ContentService.createTextOutput(resultat); 
}

També podríem seleccionar el full pel seu nom, d'aquesta manera no ens afectaria si es canvien d'ordre.

  var sh = SpreadsheetApp.openById(IdFull).getSheetByName("Llistat");  // Agafem el full
  var dades = sh.appendRow(camps);   // Afegeix una fila amb la llista de dades en format matriu

Copiem tot el text i l'enganxem a la finestra de l'editor d'scripts. Un cop enganxat, haurem de fer-hi un canvi. En la imatge següent s'han requadrat aquelles línies pensades per a que es pugui personalitzar el programa. En el nostre exemple haurem de modificar la línia requadrada en vermell canviant el text entre cometes pel codi de la taula que hem trobat abans. Els elements requadrats en blau no s'han de tocar per a l'exemple que farem però sí pot ser necessari tocar-los si aprofitem el programa per a altres aplicacions.

Vista del programa

Per a altres exemples caldria adaptar la dimensió del vector camps al nombre de paràmetres, les variables on guardem els paràmetres i les accions que es fan sobre aquestes variables.

Un cop personalitzat el programa l'hem de guardar, picant el botó que es mostra a continuació.

Botó guardar

Picarem el botó Implementar.

Implementar

En el desplegable triarem Nueva implementación i s'obrirà una finestra similar a la següent:

Nueva implementación

El primer cop que ho fem, haurem de picar en el botó que es mostra a continuació.

Tipus

I triar l'opció Aplicación web. La finestra ens preguntarà en nom de qui volem que s'executi l'aplicació (li direm Yo) i qui hi té accés (li direm que qualsevol usuari). Finalment, picarem el botó Implementar.

Nueva implementación

Se'ns mostrarà una pantalla en la que se'ns indicarà l'adreça URL de l'aplicació, que haurem de copiar.

URL script https://script.google.com/macros/s/^^fycbxqrJpVA-KT1sUd8HIta643R3bH4ixpDahttayGSGjkpHUBjPQ/exec

Un cop estiguem, podem picar el botó Listo.

Atenció: Hem de recordar que cal guardar el programa (botó del disquet) abans d'implementar, si no ho fem ens implementarà la darrera versió guardada que no serà l'actual.

Ara que ja tenim el nostre full de càlcul preparat, anem a comentar les possibilitats que ens ofereix aquest script, per després poder-les emprar des del microcontrolador. Primer comencem per la variable que ens permet personalitzar el codi. A la taula següent hi ha el seu nom i la descripció corresponent.

Variable Valor
exemple
Descripció
numFull 0 Número de full que fem servir, el primer és el 0
(poden haver-hi diversos fulls amb el mateix enllaç)

Els scripts es poden provar amb el navegador web. Si posem l'adreça URL a la barra d'adreces del navegador obtindrem la resposta a la pantalla. La nostra funció té dos paràmetres anomenats t i h, però es podrien afegir més paràmetres en el futur. La URL, amb un parell de possibles valors, hauria de quedar així:

https://script.google.com/macros/s/AKfycbysb5cirzf08AzksvdF-tQ2wc3YMK3qGQJIgfiomwcMHqI3mvE/exec?t=23.5&h=46.8

En aquest cas, però, cal tenir en compte un problema afegit que ens obligarà a complicar el programa. Si fem aquesta petició des del microcontrolador, el servidor de Google ens enviarà una resposta com la següent:

HTTP/1.1 302 Moved Temporarily
Content-Type: text/html; charset=UTF-8
Access-Control-Allow-Origin: *
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: Mon, 01 Jan 1990 00:00:00 GMT
Date: Tue, 23 Apr 2019 12:27:56 GMT
Location: https://script.googleusercontent.com/macros/echo?user_content_key=oenhEvjO3yPbc5l3PWi6ufvR8cirQxMahB_E8SK3bq6ntEINk5x5P5nifS8kHstSQscGgtkc5KFCdEpp6Y6gAR8RseYwh1icm5_BxDlH2jW0nuo2oDemN9CCS2h10ox_1xSncGQajx_ryfhECjZEnFqIf1F7oW1FfCOAcLCKwzzmi2u-p9u7-sO1n73TUZfMkGAo9dguy94CzIWlTYkUuAOv8ef6LK8V0WMzD7_o4OAjoDVIujcsbA&lib=Mshmn-khZ4-y7RLovKvN0gQ677b5FxXsc
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
Server: GSE
Alt-Svc: quic=":443"; ma=2592000; v="46,44,43,39"
Accept-Ranges: none
Vary: Accept-Encoding
Connection: close

<HTML>
<HEAD>
<TITLE>Moved Temporarily</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000">
<H1>Moved Temporarily</H1>
The document has moved <A HREF="https://script.googleusercontent.com/macros/echo?user_content_key=oenhEvjO3yPbc5l3PWi6ufvR8cirQxMahB_E8SK3bq6ntEINk5x5P5nifS8kHstSQscGgtkc5KFCdEpp6Y6gAR8RseYwh1icm5_BxDlH2jW0nuo2oDemN9CCS2h10ox_1xSncGQajx_ryfhECjZEnFqIf1F7oW1FfCOAcLCKwzzmi2u-p9u7-sO1n73TUZfMkGAo9dguy94CzIWlTYkUuAOv8ef6LK8V0WMzD7_o4OAjoDVIujcsbA&lib=Mshmn-khZ4-y7RLovKvN0gQ677b5FxXsc">here</A>.
</BODY>
</HTML>

En aquesta resposta el més important són les línies marcades en groc. El que ens diu aquesta resposta és un missatge que ens indica que el servidor ha canviat temporalment d'adreça. Aquests redireccionaments no són estranys quan es fan tasques de manteniment d'un servidor però en aquest cas no es tracta d'això. Aquesta és una de les proteccions que posa Google per evitar accions no destijades. Quan nosaltres fem una petició a l'adreça "correcta" ens envien una adreça temporal que dura molt poc temps i que ens permet fer l'acció desitjada. Només si el nostre dispositiu és capaç d'enviar immediatament la petició a la nova adreça es farà l'acció.

Aixi, doncs, hem hagut de complicar el programa per preveure aquest canvi d'adreça i actuar en conseqüència. Malgrat tot, en algunes de les proves que hem fet el programa (o més ben dit la xarxa Wi-Fi) no ha estat prou ràpida en servir la nova connexió i en enviar la segona adreça hem rebut un nou avís de canvi, de manera que hem hagut de fer fins a tres o quatre connexions per obtenir els valors desitjats.

Ara anem a comentar el programa que hem de posar al microcontrolador. Només comentarem aquelles coses que han canviat respecte a exemples anteriors. A la part de definició de variables tenim les variables server i pagina que fins ara havíem definit amb la mida que correspongués per al contingut que hi escrivíem i, en canvi, ara les definim molt més grosses perquè després necessitarem guardar-hi l'adreça que ens retorna el servidor. Com hem vist, el servidor ens retorna adreces molt llargues i, per tant, ens hem curat en salut posant valors relativament grans.

Al començament del loop és quan rebem els caràcters que envia el servidor. Hem afegit unes instruccions per detectar quan ens arriba un salt de línia (\n) i així ens assegurem que només analitzarem línies senceres. En cada connexió cerquem una línia amb un codi de resposta 200 (correcte) o 302 (redirecció temporal) i ens guardem quina hem trobat. Si era la segona, busquem també la línia que comença amb Location per agafar-ne l'adreça que hem de partir en dos trossos (just després de les dues barres) per guardar-la com a nom del servidor i com a adreça de la pàgina.

La resposta rebuda la ignorem. Podríem fer que la mirés per saber si les dades s'han enviat correctament.

Quan fem la petició afegim els paràmetres (variable data) a l'adreça de la pàgina.

// 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
#include "DHT.h"
#define DHTPIN 2
#define DHTTYPE DHT22
#define server_len 50
#define pag_len 400
const char idXarxa[] = "xarxa-wifi";    // Nom del punt d'accés 
const char contrasenya[] = "contrasenya-wifi";    // Contrasenya de connexió 
const String server0 = "script.google.com";
String pagina0 = "";
const String pagina_base = "/macros/s/AKfycbwzJfJIZd9-yS6prAbLRDNHUTFT4Ua122Kv2yb-jo3jd73Gl9x0/exec";
char server[server_len];
char pagina[pag_len];
unsigned long darreraConnexio = 0;
const unsigned long periodeConnexio = 10000UL;
bool pendent, completa, redir;
bool ara = false;
float hum, temp;
String data;
String peticio = "";    // Aquí guardarem una línia de la petició del client
String peticioAux = "";    // i la petició anterior (també ho farem servir de reserva)
int status = WL_IDLE_STATUS;
WiFiSSLClient client;
DHT dht(DHTPIN, DHTTYPE);
void setup() {    // Inicialització
    Serial.begin(9600);    // Monitor sèrie
    dht.begin();
    while (!Serial) {
        ;    // Esperem que l'usuari obri el monitor sèrie
    }
    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
    }
    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(); 
    redir = false;
}
void loop() {    // Programa que es repeteix indefinidament
    // El bucle principal té tres parts: 
    //     1. Gestió dels caràcters que arriben
    //     2. Tractament de les dades rebudes
    //     3. Nova petició quan ha passat el temps
    while (client.available()) {
        // Gestió dels caràcters que arriben
        // Aquest bucle va guardant els caràcters rebuts
        // i espera al moment en que arriba un salt de línia
        char c = client.read();    // Rebem caràcters del servidor
        if (c == '\n') {    // Mirem si és un salt de línia
            peticioAux = peticio;    // Guardem la petició anterior
            peticio = "";    // Ens preparem per a la línia següent
            completa = true;    // Preparat per escriure-ho
        } else {
            peticio += c;    // Afegim el caràcter rebut
        }
        // Quan ha arribat un salt de línia, hem de mirar què ha arribat
        if (completa){  // Ha arribat una línia completa
            if (peticioAux.startsWith("HTTP/1.1 200")){    // Resposta bona
                pendent = true;
            }
            if (peticioAux.startsWith("HTTP/1.1 302")){    // Redireccionament
                redir = true;
            }
            if (redir && (peticioAux.startsWith("Location:"))){
                // Si hi ha redireccionament, hem de buscar l'adreça
                // i extreure'n el servidor i la pàgina
                String adre = peticioAux.substring(peticioAux.indexOf("//") +2);
                String server1 = adre.substring(0, adre.indexOf(".com") +4);
                String pagina1 = adre.substring(adre.indexOf(".com") +4);
                server1.toCharArray(server, 50);
                pagina1.toCharArray(pagina, 400);
                ara = true;
            }
            completa = false;
        }
    }
    // Hi ha una resposta per processar
    // Però ignorarem les dades que ens envia l'script
    if (pendent) {
        pendent = false;
    }
    // Quan toca, tornem a fer una petició
    if (ara || ((millis() - darreraConnexio > periodeConnexio))) {
        if(!ara) {
            hum = dht.readHumidity();
            temp = dht.readTemperature();
            data = "?";
            data += "t=";
            data += temp;
            data += "&h=";
            data += hum;
        }
        if (redir){
            redir = false;
        } else {
            pagina0 = pagina_base + data;
            server0.toCharArray(server, server_len);
            pagina0.toCharArray(pagina, pag_len);
        }
        client.stop();
        if (client.connect(server, 443)) {
            Serial.println("S'ha fet la connexió al servidor");
            client.print("GET ");
            client.print(pagina);
            client.println(" HTTP/1.1");
            client.print("Host: ");
            client.println(server);
            client.println("Connection: close");
            client.println();
            // Guardem quan hem fet la connexió
            darreraConnexio = millis();
            Serial.print("Enviat: ");
            Serial.println(data);
        } else {
            Serial.println("connection failed");
        }
        ara = false;
    }
}

El programa anterior dona per suposat que les dades que envia l'script arriben en una de les dues darreres línies rebudes. Però podria passar que no fos així i, de fet, sovint Google modifica el funcionament de les seves aplicacions per introduir elements de seguretat. Podríem fer que l'script enviés les dades amb un marcador, de manera que la línia on hi ha les dades quedés perfectament identificada. Podeu trobar una mostra de com fer-ho en aquest exemple.

En aquest programa hi ha la condició que el monitor sèrie estigui obert per a que el programa comenci a funcionar. Això ens permet seguir tot el procés encara que triguem a obrir el monitor sèrie. Quan el microcontrolador hagi de funcionar de manera independent de l'ordinador caldrà eliminar les línies següents per evitar aquest bloqueig.

    while (!Serial) {
        ;    // Esperem que l'usuari obri el monitor sèrie
    }

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.

Ens pot interessar que el nostre script guardi els valors sempre a la mateixa filera en lloc d'anar afegint una filera cada cop. En aquest cas, podem fer servir aquest altre script:

// Funció per interaccionar amb el full de càlcul des del microcontrolador
// Oriol Boix, 2020
// Sota llicència Creative Commons BY-NC-ND
// https://creativecommons.org/licenses/by-nc-nd/3.0/deed.es_ES
//
// Les variables següents ens permeten personalitzar l'script al nostre projecte
// En principi, no hauríem de tocar la resta de l'script 
var IdFull = "14eaPdj5ge66EYiFmto1LL37kxdTkmzLZTxrib1rH9CI"; // Identificador del full de càlcul 
              // S'aconsegueix picant a Comparteix i triant Opcions avançades
var numFull = 0;  // Número del full amb el que hem de treballar
// Script per interactuar amb el full de càlcul
// Funció que s'executa quan hi ha una ordre get
// La nostra funció tindrà dos paràmetres:
      // t    Valor de la temperatura
      // h    Valor de la humitat
function doGet(e) {
  var resultat = '';
  var camps = new Array(2);  // Valors per guardar a la taula
  // Assignem els paràmetres a variables
  var Temp = e.parameter.t; 
  var Hum = e.parameter.h; 
  if ((Temp == undefined) || (Hum == undefined)){
    resultat = 'Falten paràmetres';
  } else {
    camps[0] = Temp;
    camps[1] = Hum;
    // Ja tenim les dues caselles que hem d'escriure a la filera
    // Les nostres dades han d'escriure en un espai, que seleccionarem més endavant, 
    //    de dues columnes en una filera
    var valu = new Array(1);
    valu[0] = camps;  // Converteix el vector en una matriu d'una filera i dues columnes
    var sh = SpreadsheetApp.openById(IdFull);
    var sheet = sh.getSheets();
    var range = sheet[numFull].getLastRow();
    if (range > 1){  // Mira si ja hi ha un valor
      // La funció getRange ens permet seleccionar el grup de caselles sobre les que anem a treballar,
      //    en aquest cas a escriure
      // El primer paràmetre és la filera on comença el grup, en el nostre cas la darrera escrita
      // El segon paràmetre és la columna on comença el grup, en el nostre exemple la primera
      // El tercer paràmetre és el nombre de fileres que tindrà el grup, per a nosaltres una
      // El quart paràmetre és el nombre de columnes que ocupa el grup, en el nostre cas dues
      var rangeVal = sheet[numFull].getRange(range, 1, 1, 2);  // Selecciona dues caselles
      rangeVal.setValues(valu);  // Guarda els valors a les caselles, substituïnt els anteriors
    } else {
      // Si no n'hi ha cap, afegeix una fila amb la llista de dades en format matriu
      sheet[numFull].appendRow(camps);
    }
    resultat = 'Valors guardats';
  }
  return ContentService.createTextOutput(resultat); 
}

Podríem fer una aplicació d'App Inventor que ens permeti llegir el darrer valor guardat. Per fer-ho, caldria modificar l'script per afegir la possibilitat de rebre una comanda per a la lectura i que es retorni la corresponent resposta. A continuació es mostra una nova versió del primer script (el que afegeix valors un sota l'altre) que implementa aquesta opció. L'ordre per llegir el darrer valor guardat seria:

https://script.google.com/macros/s/AKfycbysb5cirzf08AzksvdF-tQ2wc3YMK3qGQJIgfiomwcMHqI3mvE/exec?acc=read

que ens retornaria la temperatura i la humitat separades per una barra. Hem marcat en groc les línies que hem afegit.

// Funció per interaccionar amb el full de càlcul des del microcontrolador
// Oriol Boix, 2020
// Sota llicència Creative Commons BY-NC-ND
// https://creativecommons.org/licenses/by-nc-nd/3.0/deed.es_ES
//
// Les variables següents ens permeten personalitzar l'script al nostre projecte
// En principi, no hauríem de tocar la resta de l'script 
var IdFull = "14eaPdj5ge66EYiFmto1LL37kxdTkmzLZTxrib1rH9CI"; // Identificador del full de càlcul 
              // S'aconsegueix picant a Comparteix i triant Opcions avançades
var numFull = 0;  // Número del full amb el que hem de treballar
var numCols = 2;  // Nombre de columnes que hem de llegir
// Script per interactuar amb el full de càlcul
// Funció que s'executa quan hi ha una ordre get
// La nostra funció tindrà dos paràmetres:
      // t    Valor de la temperatura
      // h    Valor de la humitat
function doGet(e) {
  var resultat = '';
  var camps = new Array(2);  // Valors per guardar a la taula
  // Assignem els paràmetres a variables
  var Accio = e.parameter.acc;
  var Temp = e.parameter.t; 
  var Hum = e.parameter.h; 
  if (Accio == undefined){
    if ((Temp == undefined) || (Hum == undefined)){
      resultat = 'Falten paràmetres';
    } else {
      camps[0] = Temp;
      camps[1] = Hum;
      var sh = SpreadsheetApp.openById(IdFull);
      var sheet = sh.getSheets();
      sheet[numFull].appendRow(camps);   // Afegeix una fila amb la llista de dades en format matriu 
      resultat = 'Valors guardats';
    }
  }
  if (Accio == "read"){
    var sh = SpreadsheetApp.openById(IdFull);
    var sheet = sh.getSheets();
    // Llegim les dades del full seleccionat
    var full = sheet[numFull].getDataRange().getValues();
    range = sheet[numFull].getLastRow();  // Índex de la darrera filera (+1 pels títols)
    if (range > 1){    // Si és 1 només hi ha els títols
      var filera = full[range -1];    // Agafem la darrera filera
      var resultat = '';
      for(var j = 0;j < numCols;j++){ 
        if (j > 0){
          resultat = resultat + '/';  // La barra separarà els valors
        }
        resultat = resultat + filera[j];
      }
    }
  }
  return ContentService.createTextOutput(resultat); 
}

Un cop modificat l'script, ja podem fer la nostra aplicació. L'aspecte de l'aplicació serà el següent:

Disposició dels elements

Atès que en aquest web el nostre objectiu no és crear aplicacions, ens hem preocupat només del funcionament i la comprensió però no del disseny. Si ho desitgem, l'App Inventor ens permet personalitzar la disposició, els colors i altres propietats dels elements de la pantalla segons els nostres gustos.

Les següents taules mostren les propietats de cada un dels elements:

Propietat Valor Comentaris
Nom Label_T Aquest nom l'hem de posar en el requadre Components
FontSize 50
Width Fill parent
Text T °C
TextAlignment center

Propietat Valor Comentaris
Nom Label_H Aquest nom l'hem de posar en el requadre Components
FontSize 50
Width Fill parent
Text H %
TextAlignment center

Propietat Valor Comentaris
Nom Button_consulta Aquest nom l'hem de posar en el requadre Components
Text Nova lectura

Propietat Valor Comentaris
Nom Web_connecta Aquest nom l'hem de posar en el requadre Components

Atès que volem que els valors es llegeixin tant quan premem el botó com en inicialitzar la pantalla, hem creat la funció llegir i la cridem en els dos casos. El programa serà el següent:

Programa
Programa

En aquest altre espai web pots trobar informació sobre els scripts.

 

 

 

 

 

 

 

 

 

 

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