Internet de les coses amb ESP32 i ESP8266

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

Controlem un LED des de Telegram

En aquest exemple farem servir Telegram per controlar l'encesa i l'apagada d'un LED. Inicialment cal crear un xatbot a Telegram, un full de càlcul de Google i un script vinculat a aquest. En aquesta pàgina trobarem la manera de fer aquests passos.

El nostre script es podrà executar de dos maneres. Quan s'executi des del microcontrolador (amb una ordre get) tornarà un valor que llegirà del full de càlcul. Aquest valor pot ser 0 o 1. Quan sigui 1 encendrem el LED i quan sigui 0 l'apagarem. Quan s'executi des de Telegram (amb una ordre post) escriurà 0 o 1 al full de càlcul segons quina sigui l'ordre rebuda.

Des de Telegram enviarem la comanda /on quan vulguem encendre i la comanda /off quan desitgem apagar.

Important: El microcontrolador està treballant com a client i, per tant, l'ordre no es processa fins que el microcontrolador llegeix un nou valor al full de càlcul.

Aquest és l'script que farem servir:

// Funció per interaccionar amb el full de càlcul des de Telegram
// Oriol Boix, 2020   Basat en un exemple de Ferran Mas
// 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
var token = "^^34368844:AAFIpk-e7j3UZtMQYQaTduf4hEhnDqIcNXI"; // API Token de Telegram 
var telegramUrl = "https://api.telegram.org/bot" + token;  // Url que comunica el nostre xatbot amb Telegram 
// Adreça de l'script (ens la donen en el moment de publicar-lo)
var webAppUrl = "https://script.google.com/macros/s/AKfycbzXSTtXkzqyJ_sABbGZanVX2EuRAIUB5vct53WXp6-MbLscpLHO/exec";
// Id del full de càlcul (l'obtenim quan el compartim)
var IdFull = "15CWPEcIVpS512vGw3ziCZHEuMev838GAaQ6yJKnS2Pg";  
// Funció que envia un text a Telegram
function sendText(chatId,text_env){  
  var cont = {
    method: "post",
    payload: {
      method: "sendMessage",
      chat_id: String(chatId),
      text: text_env,
      parse_mode: "HTML"
    }
  }
  UrlFetchApp.fetch(telegramUrl + '/', cont);
}
var sh = SpreadsheetApp.openById(IdFull);  // Obrim el full de càlcul
var sheet = sh.getSheets();  // Agafem els fulls
// Funció que s'executa quan hi ha una ordre get
// La nostra funció tindrà un paràmetre:
      // accio    l'acció que s'ha de realitzar (darrer)
function doGet(e) {
  // Assignem els paràmetres a variables
  var Accio = e.parameter.accio; 
  // Llegim les dades del full
  var full = sheet[0].getDataRange().getValues();
  range = sheet[0].getLastRow();  // Índex de la darrera filera (+1 pels títols)
  if(Accio=="darrer"){
    if (range > 1){    // Si és 1 només hi ha els títols
      var filera = full[range -1];    // Agafem la darrera filera
      var resultat = filera[0];
    }
  } else {
    var resultat = 'Acció incorrecta';
  }
  return ContentService.createTextOutput(resultat); 
}
// Funció que s'executa quan l'script rep una ordre post
function doPost(e){ // Funció que rep les dades enviades per Telegram 
  var fet = 0;
  var resposta = '';
  var dades = JSON.parse(e.postData.contents);  // Tracta les dades rebudes com a JSON i les guarda a dades
  // Extraiem les dades
  var text = dades.message.text;  // Recupera el text del missatge
  var id = dades.message.chat.id;  // Recupera l'identificador de la finestra des de la que s'ha enviat
  var id_usuari = dades.message.from.id; // Recupera l'identificador de l'usuari que ha escrit el missatge
  var id_missatge = dades.message.message_id; // Recupera l'identificador del missatge
  var lang = dades.message.from.language_code ;  // Recupera la llengua que l'usuari té configurada a Telegram 
  var nom_usuari = dades.message.from.first_name ;  // Recupera el nom de l'usuari que ha enviat el missatge
  var posicio = dades.message.location;  // Recupera la localizació de l'usuari, si és possible
  // La comanda pot tenir paràmetres, separats amb @
  // Descomposem el text rebut a partir de les @
  var comanda = text.split("@");  // Separa per les @
  var cmd = comanda[0].toLowerCase();  // Ho passem a minúscules
  if (cmd == '/llistat'){
    var llista = "/llistat - Llista de les comandes disponibles\n";  // Text que es retornarà a Telegram
    llista = llista + "/llegir - Consulta l'estat actual del LED\n";
    llista = llista + "/on - Encén el LED\n";
    llista = llista + "/off - Apaga el LED";
    sendText(id,llista);
    fet = 1;
  }
  if (cmd == '/on'){
    var camps = new Array(1);  // Valors per guardar a la taula
    camps[0] = "LED on";
    sheet[0].appendRow(camps);   // Afegeix una fila amb la llista de dades en format matriu al primer full
    resposta = 'Encenem el LED';
    sendText(id,resposta);
    fet = 1;
  }
  if (cmd == '/off'){
    var camps = new Array(1);  // Valors per guardar a la taula
    camps[0] = "LED off";
    sheet[0].appendRow(camps);   // Afegeix una fila amb la llista de dades en format matriu al primer full
    resposta = 'Apaguem el LED';
    sendText(id,resposta);
    fet = 1;
  }
  if (cmd == '/llegir'){
    var full = sheet[0].getDataRange().getValues();
    var rang = sheet[0].getLastRow();  // Índex de la darrera filera
    if (rang > 1){    // Si és 1 només hi ha els títols
      var filera = full[rang -1];    // Agafem la darrera filera
      resposta = filera[0];
    } else {
      resposta = "No hi ha cap dada guardada";
    }
    sendText(id,resposta);
    fet = 1;
  }
  // Si no hem entrat a cap if vol dir que la comanda no s'ha reconegut
  if (fet == 0){
    resposta = "Comanda incorrecta";
    sendText(id,resposta);
  }
}

Copiem tot el text i l'enganxem a la finestra de l'editor d'scripts. Un cop enganxat, haurem de fer-hi alguns canvis. A les primeres línies del codi hi ha quatre variables en les que haurem de posar les dades concretes per al nostre cas.

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.

Important: ës probable que quan implementem l'script ens proporcioni una nova adreça diferent de l'anterior. Si és així, l'adreça antiga seguirà funcionant però executarà la versió anterior de l'script. És necessari, doncs, actualitzar l'adreça del programa cada cop que el modifiquem.

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é un paràmetre anomenat accio que ha de ser darrer. Tal com s'ha fet, però, es podrien afegir més opcions i, fins i tot, més paràmetres en el futur. La URL hauria de quedar així:

https://script.google.com/macros/s/AKfycbysb5cirzf08AzksvdF-tQ2wc3YMK3qGQJIgfiomwcMHqI3mvE/exec?accio=darrer

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. Al setup fem les configuracions i establim la connexió a la xarxa Wi-Fi. El loop té tres parts que funcionen de manera independent. La primera part té un bucle while que s'encarrega de rebre les dades, si és que n'hi ha disponibles. Les dades arriben caràcter a caràcter i es van guardant a la variable peticio. Quan arriba un salt de línia (\n) es torna a començar i el que s'havia rebut es guarda a peticioAux i es deixa buida peticio per rebre la línia següent. Si es rep una resposta de tipus 200 vol dir que hi ha dades bones i caldrà processar-les a la següents parts del loop. Si, en canvi, arriba un 302 implica que és un redireccionament i n'hem de trobar l'adreça de destí. Si la resposta té LED vol dir que aquella línia és la que ha de controlar el nostre LED i, per tant, la guardarem.

Quan s'ha rebut una resposta bona, mirarem de quin tipus és i canviarem l'estat del LED.

Finalment, cada un cert temps fem una nova petició de lectura. Si, en canvi, havíem trobat un redireccionament la petició és immediata.

#include <SPI.h>    // Carreguem la biblioteca SPI
#include <WiFiNINA.h>    // Carreguem la biblioteca WiFiNINA
#define LED 6    // Sortida que correspon al LED de la placa
#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 char server0[] = "script.google.com";
const char pagina0[] = "/macros/s/AKfycbx2VGLHQK_if-Yw9ySzbE1CZ5VPk9DYlhOQh7Z2BBumHChSs1Y/exec?accio=darrer";
char server[server_len];
char pagina[pag_len];
unsigned long darreraConnexio = 0;
const unsigned long periodeConnexio = 10000UL;
bool pendent, completa, redir;
bool ara = false;
String resposta = "";
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;
void setup() {    // Inicialització
    Serial.begin(9600);    // Monitor sèrie
    pinMode(LED, OUTPUT);
    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(); 
    Serial.println("Anem a connectar al servidor");
    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
        //Serial.write(c);
        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 (peticioAux.indexOf("LED") >= 0){    // Dades esperades
                resposta = peticioAux;
            }
            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, server_len);
                pagina1.toCharArray(pagina, pag_len);
                ara = true;
            }
            completa = false;
        }
    }
    // Hi ha una resposta per processar
    // En aquest cas, les dades estan a la darrera línia rebuda
    if (pendent) {
        pendent = false;
        Serial.print("Rebut: ");
        Serial.println(resposta);
        if (resposta.indexOf("on") >= 0){
            digitalWrite(LED, HIGH);
            Serial.println("Encenem LED");
        }
        if (resposta.indexOf("off") >= 0){
            digitalWrite(LED, LOW);
            Serial.println("Apaguem LED");
        }
        resposta = "";
    }
    // Quan toca, tornem a fer una petició
    if (ara || ((millis() - darreraConnexio > periodeConnexio))) {
        if (redir){
            redir = false;
        } else {
            for (int j = 0; j < server_len; j++){
                server[j] = server0[j];
            }
            for (int j = 0; j < pag_len; j++){
                pagina[j] = pagina0[j];
            }
        }
        ara = false;
        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();
        } else {
            Serial.println("connection failed");
        }
    }
}

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.

Creació de botons

Podem fer que el nostre xatbot retorni uns botons per tal que l'usuari no hagi d'escriure les comandes. El següent script posa quatre botons com els següents:

Botons

// Funció per interaccionar amb el full de càlcul des de Telegram
// Oriol Boix, 2020   Basat en un exemple de Ferran Mas
// 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
var token = "^^34368844:AAFIpk-e7j3UZtMQYQaTduf4hEhnDqIcNXI"; // API Token de Telegram 
var telegramUrl = "https://api.telegram.org/bot" + token;  // Url que comunica el nostre xatbot amb Telegram 
// Adreça de l'script (ens la donen en el moment de publicar-lo)
var webAppUrl = "https://script.google.com/macros/s/AKfycbzXSTtXkzqyJ_sABbGZanVX2EuRAIUB5vct53WXp6-MbLscpLHO/exec";
// Id del full de càlcul (l'obtenim quan el compartim)
var IdFull = "15CWPEcIVpS512vGw3ziCZHEuMev838GAaQ6yJKnS2Pg";  
// Funció que envia un text a Telegram
function sendText(chatId,text_env,keyBoard){  // Función que prepara per enviar un text o un teclat a Telegram 
  keyBoard = keyBoard || 0;
  if(keyBoard.inline_keyboard || keyBoard.keyboard){
    var data = {
      method: "post",
      payload: {
        method: "sendMessage",
        chat_id: String(chatId),
        text: text_env,
        parse_mode: "HTML",
        reply_markup: JSON.stringify(keyBoard)
      }
    }
  } else {
    var data = {
      method: "post",
      payload: {
        method: "sendMessage",
        chat_id: String(chatId),
        text: text_env,
        parse_mode: "HTML"
      }
    }
  }
  UrlFetchApp.fetch( telegramUrl + '/', data);
}
// Funció que crea un teclat
function teclat(id, resp){
  // Lista de botons, cada claudàtor correspon a una línia
  var botons = new Array(["/on","/off"],["/llegir","/llistat"]); 
  var tecles = { keyboard: botons, resize_keyboard: true, one_time_keyboard : true };  // Configura el teclat 
  var titol = resp + "\n\nTria opcio"
  sendText(id,titol,tecles);  // Envia el teclat 
}
var sh = SpreadsheetApp.openById(IdFull);  // Obrim el full de càlcul
var sheet = sh.getSheets();  // Agafem els fulls
// Funció que s'executa quan hi ha una ordre get
// La nostra funció tindrà un paràmetre:
      // accio    l'acció que s'ha de realitzar (darrer)
function doGet(e) {
  // Assignem els paràmetres a variables
  var Accio = e.parameter.accio; 
  // Llegim les dades del full
  var full = sheet[0].getDataRange().getValues();
  range = sheet[0].getLastRow();  // Índex de la darrera filera (+1 pels títols)
  if(Accio=="darrer"){
    if (range > 1){    // Si és 1 només hi ha els títols
      var filera = full[range -1];    // Agafem la darrera filera
      var resultat = filera[0];
    }
  } else {
    var resultat = 'Acció incorrecta';
  }
  return ContentService.createTextOutput(resultat); 
}
// Funció que s'executa quan l'script rep una ordre post
function doPost(e){ // Funciíon que recibe los datos enviados por Telegram que seran por metodo Post 
  var fet = 0;
  var resposta = '';
  var dades = JSON.parse(e.postData.contents);  // Tracta les dades rebudes com a JSON i les guarda a dades
  // Extraiem les dades
  var text = dades.message.text;  // Recupera el text del missatge
  var id = dades.message.chat.id;  // Recupera l'identificador de la finestra des de la que s'ha enviat
  var id_usuari = dades.message.from.id; // Recupera l'identificador de l'usuari que ha escrit el missatge
  var id_missatge = dades.message.message_id; // Recupera l'identificador del missatge
  var lang = dades.message.from.language_code ;  // Recupera la llengua que l'usuari té configurada a Telegram 
  var nom_usuari = dades.message.from.first_name ;  // Recupera el nom de l'usuari que ha enviat el missatge
  var posicio = dades.message.location;  // Recupera la localizació de l'usuari, si és possible
  // La comanda pot tenir paràmetres, separats amb @
  // Descomposem el text rebut a partir de les @
  var comanda = text.split("@");  // Separa per les @
  var cmd = comanda[0].toLowerCase();  // Ho passem a minúscules
  if (cmd == '/llistat'){
    var llista = "/llistat - Llista de les comandes disponibles\n";  // Text que es retornarà a Telegram
    llista = llista + "/llegir - Consulta l'estat actual del LED\n";
    llista = llista + "/on - Encén el LED\n";
    llista = llista + "/off - Apaga el LED";
    teclat(id, llista);
    fet = 1;
  }
  if (cmd == '/on'){
    var camps = new Array(1);  // Valors per guardar a la taula
    camps[0] = "LED on";
    sheet[0].appendRow(camps);   // Afegeix una fila amb la llista de dades en format matriu al primer full
    resposta = 'Encenem el LED';
    teclat(id, resposta);
    fet = 1;
  }
  if (cmd == '/off'){
    var camps = new Array(1);  // Valors per guardar a la taula
    camps[0] = "LED off";
    sheet[0].appendRow(camps);   // Afegeix una fila amb la llista de dades en format matriu al primer full
    resposta = 'Apaguem el LED';
    teclat(id, resposta);
    fet = 1;
  }
  if (cmd == '/llegir'){
    var full = sheet[0].getDataRange().getValues();
    var rang = sheet[0].getLastRow();  // Índex de la darrera filera
    if (rang > 1){    // Si és 1 només hi ha els títols
      var filera = full[rang -1];    // Agafem la darrera filera
      resposta = filera[0];
    } else {
      resposta = "No hi ha cap dada guardada";
    }
    teclat(id, resposta);
    fet = 1;
  }
  // Si no hem entrat a cap if vol dir que la comanda no s'ha reconegut
  if (fet == 0){
    resposta = "Comanda incorrecta";
    teclat(id, resposta);
  }
}

Sobrescrivim la taula

Tal com teníem fet el programa, els nous valors es van afegint a la taula i aquesta va augmentant de mida. Si no ens interessa guardar els valors anteriors, podem fer que el nou estat sobrescrigui l'antic. Cal, però, comprovar que ja hi ha algun valor i si no és el cas caldrà afegir. Podem aconseguir-ho substituïnt les línies marcades en color en la versió anterior per les que es llisten a continuació:

  if ((cmd == '/on') || (cmd == '/off')){
    var camps = new Array(1);  // Valors per guardar a la taula
    if (cmd == '/on'){
      camps[0] = "LED on";
      resposta = 'Encenem el LED';
    }
    if (cmd == '/off'){
      camps[0] = "LED off";
      resposta = 'Apaguem el LED';
    }
    var valu = new Array(1);
    valu[0] = camps;  // Converteix el vector en una matriu d'una filera i una columnes
    var range = sheet[0].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 una
      var rangeVal = sheet[0].getRange(range, 1, 1, 1);  // Selecciona una casella
      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[0].appendRow(camps);
    }
    teclat(id, resposta);
    fet = 1;
  }

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.