Javascript, HTML, CSS e... !
0 commenti

Log dei passaggi oltre la barriera laser ed email al destinatario

Allarme Laser con Arduino e Log in Spreadsheet con Google Apps Script

Nonostante non abbia alcuna base certificata di elettronica, mi piace ogni tanto dilettarmi con Arduino (prevalentemente con la parte di sviluppo degli sketch, lasciandomi invece ispirare da qualche schema elettrico già avviato per la parte hardware). Tra i vari componenti low cost che ho acquistato è presente una scheda ethernet, Fig. 1, per collegare Arduino alla rete e non avendola mai provata ho colto l'occasione per trovare un nesso con Google Apps Script.



arduino ethernet hr911105a

Fig. 1 - Arduino Ethernet HR911105A


Circa un anno fa, nel mio blog personale, ho pubblicato un articolo dal titolo "Allarme Laser con Arduino come Barriera di un Passaggio" dove si fa uso di un Laser 650nm, una fotoresistenza ed buzzer che emette un suono quando la barriera viene oltrepassata. Per lo schema, lo sketch ed il video di esempio rimando direttamente all'articolo.

La possibilità di integrare la scheda ethernet a tale progetto è nata dal tentativo di provare a scrivere su uno Spreadsheet tramite Arduino, da qui l'idea di registrare i log di quando l'allarme laser viene sollecitato.
Relativamente allo sketch non ho fatto grandi modifiche a quanto già presente nel mio codice originale se non includere la libreria EtherCard.h ed adattare il già presente esempio 'webClient' come mostrato nel codice proposto a breve. Nel link alla libreria è presente inoltre lo schema di collegamento dei pin della scheda Ethernet ad Arduino, che riporto di seguito:

ENC28J60 Arduino UnoNote
VCC  3.3V  
GND  GND  
SCK  Pin 13  
MISO  Pin 12  
MOSI  Pin 11  
CS  Pin 10 Selezionabile tramite la funzione ether.begin()

 

La Fig. 2 è quanto di meglio sono riuscito a fare, si vede il laser acceso che punta alla fotoresistenza e la scheda ethernet collegata con opportuno cavo RJ45:



schema arduino laser ethernet hr911105a

Fig. 2 - Schema Allarme Laser con Arduino e scheda Ethernet HR911105A


Prima di mostrare il codice devo premettere che ho effettuato diversi tentativi e in alcuni casi mi sono imbattuto nell'errore "HTTP/1.1 301 Moved Permanently". Ho capito sulla mia pelle che se il sito bersaglio è indicato nello sketch senza il www ma questo esiste nella forma con www e quella senza fa redirect a quest'ultima, Arduino non è in grado di seguire tale redirect e genera l'errore in questione (inserendo invece la forma con il www tutto è andato a buon fine sul sito di test che ho interrogato). Questo aspetto è molto importante quando invece si vuole interrogare uno script di Apps Script (pubblicato come applicazione web ed accessibile da chiunque), questo perché, al di là del protocollo https anziché http, nella documentazione ufficiale viene espressamente detto che "Per motivi di sicurezza, il contenuto restituito dal servizio Content non viene pubblicato da script.google.com ma è invece reindirizzato ad un URL one-time con dominio script.googleusercontent.com" (https://developers.google.com/apps-script/guides/content).

Per tale motivo ho dovuto utilizzare un servizio gratuito di terze parti, nel caso specifico chiamato PushingBox, che tra le altre cose permette di interrogare un URL esterno passandogli dei parametri.
Ho quindi creato un nuovo Spreadsheet e nel servizio esterno configurato il suo URL ed il tipo di chiamata, Fig. 3:



pushingbox servizio

Fig. 3 - PushingBox definizione del Servizio


Successivamente ho definito lo scenario, ossia i parametri dinamici da passare (nel caso specifico la zona dove l'allarme è scattato; nel mio prototipo ne esiste una sola ma a scopo esemplificativo è mostrato il passaggio dei valori da Arduino ad Apps Script), Fig. 4:



pushingbox scenario

Fig. 4 - PushingBox definizione dello Scenario


Il codice dello sketch da caricare su Arduino è il seguente (nota: rispetto allo schema dell'articolo indicato all'inizio, il pin 10 riferito al led rosso è stato spostato sul pin 8 per lasciare libero il pin 10 al connettore CS della scheda ethernet che lo prevede di default, nonostante sia comunque configurabile). Nella chiamata al webservice è definito anche un parametro devid (DeviceID) che è un identificativo univoco fornito dal servizio al momento della creazione dello scenario:

#include <EtherCard.h>

// ethernet interface mac address, must be unique on the LAN
static byte mymac[] = { 0x74,0x69,0x69,0x2D,0x30,0x31 };

byte Ethernet::buffer[700];
static uint32_t timer;

const char website[] PROGMEM = "api.pushingbox.com";

// called when the client request is complete
static void my_callback (byte status, word off, word len) {
  Serial.println(">>>");
  Ethernet::buffer[off+300] = 0;
  Serial.print((const char*) Ethernet::buffer + off);

  Serial.println();
  Serial.println("chiamata al webservice effettuata");
}

const int led_rosso = 8; // nello script del laser era 10 ma lo slot è occupato dal pin per la scheda ethernet 
const int led_verde = 9; 
const int pin_input_bottone = 7; 
const int sensorLaser = 5;
const int buzzerPin = 3;
const int debounceDelay = 200;
const int intervallo_base = 250;
int pin_input_bottone_valore=0;
int bottone_stato_pressione = 0;
int luceQty;

unsigned long currentMillis = 0;
unsigned long previousMillis = 0;

String bln_reset_mail_sent = "0";

void setup () {

  pinMode(led_verde, OUTPUT);
  pinMode(led_rosso, OUTPUT);
  pinMode(pin_input_bottone, INPUT);
  pinMode(sensorLaser, OUTPUT); 
  pinMode(A0,INPUT);

  Serial.begin(57600);
  Serial.println(F("
[webClient]"));

  if (ether.begin(sizeof Ethernet::buffer, mymac) == 0) 
    Serial.println(F("Failed to access Ethernet controller"));
  if (!ether.dhcpSetup())
    Serial.println(F("DHCP failed"));

  ether.printIp("IP:  ", ether.myip);
  ether.printIp("GW:  ", ether.gwip);  
  ether.printIp("DNS: ", ether.dnsip);  

#if 1
  // use DNS to resolve the website's IP address
  if (!ether.dnsLookup(website))
    Serial.println("DNS failed");
#elif 2
  // if website is a string containing an IP address instead of a domain name,
  // then use it directly. Note: the string can not be in PROGMEM.
  char websiteIP[] = "192.168.1.1";
  ether.parseIp(ether.hisip, websiteIP);
#else
  // or provide a numeric IP address instead of a string
  byte hisip[] = { 192,168,1,1 };
  ether.copyIp(ether.hisip, hisip);
#endif
    
  ether.printIp("SRV: ", ether.hisip);
}

void loop () {
  ether.packetLoop(ether.packetReceive());

  currentMillis = millis();
  pin_input_bottone_valore = digitalRead(pin_input_bottone);

  if (pin_input_bottone_valore == HIGH) {
    switch (bottone_stato_pressione) {
      case 0:
        bottone_stato_pressione = 1;
        break;
      case 1:
        bottone_stato_pressione = 0;
        break;        
      //default:
        // non fare niente
    }
    delay(debounceDelay);
  } 

  if (bottone_stato_pressione == HIGH) {
    if ((currentMillis - previousMillis) >= (intervallo_base)) {
      digitalWrite(led_rosso, LOW); 
      digitalWrite(led_verde, HIGH); 
      digitalWrite(sensorLaser, 1); 
      luceQty = analogRead(A0);
      if (luceQty<100) {
        tone(buzzerPin, 500, 1000);
        if (bln_reset_mail_sent == "0") {
            Serial.println();
            Serial.print("<<< REQ ");
            ether.browseUrl(PSTR("/pushingbox?devid=v5082EBEXXXXXXXX&zona="), "Porta%20di%20ingresso", website, my_callback);
            bln_reset_mail_sent = "1";
        }
        
      } else {
        bln_reset_mail_sent = "0";
        noTone(buzzerPin);
      }
       updateMillis();
    }  
  } else {
    if ((currentMillis - previousMillis) >= (intervallo_base)) {
      noTone(buzzerPin); 
      digitalWrite(led_verde, LOW); 
      digitalWrite(led_rosso, HIGH); 
      digitalWrite(sensorLaser, 0);
      updateMillis();
    }
  } 
}

void updateMillis() {
  previousMillis = currentMillis;
}

Lo script in Google Apps Script è invece di tipo standalone (nel quale è presente l'identificativo dello Spreadsheet su cui scrivere) e dovrà essere distribuito come applicazione web eseguita come il creatore stesso dello script ma con accesso a chiunque compresi utenti anonimi:

// Lo script viene interrogato dal servizio pushingbox.com tramite l'API api.pushingbox.com
function doGet(e) {
  var result = '';
  var bln_sendMail = false;
  if (e.parameter == undefined) {
    result = 'Nessun parametro rilevato';
  } else {
    var ss_id = '1S0Wq7rHB6OYo-WgktB...'; // Id dello Spreadsheet
    var rowData = [];
    rowData[0] = Utilities.formatDate(new Date(), "Europe/Rome", "YYYY/MM/dd HH:mm:ss");
    for (var param in e.parameter) {
      var value = stripQuotes(e.parameter[param]);
      switch (param) {
        case 'zona': // nome del parametro: 'zona'
          rowData[1] = value; // valore del parametro
          bln_sendMail = true;
          result = 'Parametro rilevato';
          break;
        default:
          result = "Parametro non previsto";
      }
    }    
    if (bln_sendMail) {
      var ss_sh = SpreadsheetApp.openById(ss_id).getSheets()[0]; // Foglio 1
      var newRow = ss_sh.getLastRow() + 1;
      var newRange = ss_sh.getRange(newRow, 1, 1, rowData.length);
      newRange.setValues([rowData]);
      MailApp.sendEmail("miamail@gmail.com", "Allarme " + rowData[1] + ": " + rowData[0], "Suonato allarme nella zona " + rowData[1] + " il " + rowData[0]);
    }
  }
  return ContentService.createTextOutput(result);
}

// Rimuove eventuali apici, doppi e singoli, dall'inizio e della fine della stringa passata
function stripQuotes(str) {
  return str.replace(/^["']|['"]$/g, "");
}

È possibile osservare lo script in esecuzione tramite il log della porta seriale di cui riporto di seguito uno screenshot nel quale sono evidenziate due chiamate al webservice:



sketch arduino e log seriale

Fig. 5 - Sketch Arduino e Log Seriale


Il funzionamento è il seguente: una volta che la barriera laser viene interrotta, una chiamata verrà lanciata da Arduino verso il servizio di terze parti che a sua volta interroga l'URL dello Script Web creato con Apps Script passando l'opportuno parametro "zona". Lo script esegue di default la funzione doGet(e) e se riconosce il parametro passato scrive la data, l'ora e la zona all'interno dello Spreadsheet definito, Fig. 6:



infromazioni sulla violazione dell'allarme registrate in uno spreadsheet

Fig. 6 - Spreadsheet con log delle interruzioni della barriera laser


Lo script contestualmente invia un'email con le stesse informazioni all'indirizzo specificato, Fig. 7:



email inviata al destinatario per via della violazione della barriera laser

Fig. 7 - Email inviata al destinatario per avvisarlo che l'allarme laser è scattato


Questo breve video rappresenta una dimostrazione del progetto in funzione:

 

Lo script, così come il sistema hardware, può essere ampliato con altri laser ipotizzando un uso interno ad un'abitazione e quindi oltre alla porta di ingresso mettere in sicurezza finestre ed altri accessi.

L'approccio per scrivere da Arduino ad uno Spreadsheet può inoltre essere esportato in qualsiasi altro progetto che necessiti di registrare dati tra questi due diversi sistemi.

Tags

Michele Pisani

Michele Pisani

Sviluppatore Javascript ed esperto in Digital Analytics

L'esperienza nel settore Digital Analytics unita ad anni di sviluppo in Javascript ha trovato la massima espressione in Google Apps Script che mi ha permesso, con estrema facilità e poche righe di codice, di realizzare potenti applicazioni interattive e processi automatizzati integrati con i prodotti della G Suite.

Come contattarmi
scrivi un commento

0 Commenti

Non ci sono commenti

Nessuno ha ancora commentato questo articolo, fallo tu per primo!

scrivi un commento

Scrivi un commento

Il tuo indirizzo email non sarà pubblicato.I campi contrassegnati da un * sono obbligatori
Puoi utilizzare i seguenti tag nei commenti:
[bold]testo[/bold] se vuoi evidenziare un testo con il grassetto[code]function helloworld() { }[/code] se vuoi pubblicare una porzione di codice[url]https://www.appsscript.it[/url] se devi riferirti ad un indirizzo web