Apricancello IoT con la board Ganimede.E12

Tramite la board Ganimede.E12, gestiamo l’apertura e la chiusura di un cancello, o di altri apparati elettronici, utilizzando un’applicazione dedicata su smartphone.

Il problema dei dispositivi IoT fai-da-te

Oggigiorno l’attenzione verso la domotica e i dispositivi IoT (Internet of Things) è così ampia che siamo spesso alla ricerca di elettrodomestici o altri dispositivi elettrici dotati di software che ci consentano di gestirli al meglio. Tuttavia, controllare il condizionatore o le tapparelle da remoto ha spesso un costo più elevato rispetto ad apparecchi puramente meccanici, per non parlare del fatto che per controllarli dai nostri cellulari dovremmo sostituirli con apparecchiature più moderne, nonostante le nostre vecchie funzionino ancora più che bene.

In aggiunta a ciò, un aspetto che chi ha meno familiarità con la materia non considera è quello della cybersecurity. È vero che non è necessario implementare misure di sicurezza informatiche simili a quelle di una banca o di un’automobile per gestire dei semplici elettrodomestici, ma è anche vero che sono in tanti coloro che non si fanno problemi ad acquistare una telecamera che consente un live streaming dal proprio telefono, senza preoccuparsi del fatto che quelle immagini passino attraverso server che si trovano dall’altra parte del mondo, dove il GDPR (la normativa per la tutela della privacy in Europa) non è sempre rispettato alla lettera. I più avvezzi opteranno per soluzioni fatte in casa, come l’uso di un proprio server o di un servizio su cloud, ma tutto ciò ha un costo.

Il progetto: Ganimede.E12 e un’app dedicata

In questo articolo proponiamo di realizzare un dispositivo adatto a controllare l’apertura e la chiusura di un cancello da remoto, costituito dalla scheda Ganimede.E12 (già presentata su un precedente post) e da una applicazione mobile appositamente sviluppata. Data la versatilità del progetto (si utilizza un relè per il controllo del carico), sarà possibile estenderne l’utilizzo anche ad altre applicazioni, come il controllo di boiler o caldaie domestiche.

L’applicazione mobile non raccoglie alcun tipo di dato personale, e non trasmette alcuna informazione rilevante da un punto di vista privacy ad alcun server, come si può facilmente verificare con un software che analizza il traffico dati del proprio cellulare. Per concludere, la comunicazione tra l’app e l’apricancello avviene tramite il servizio cloud gratuito offerto da dweet.io, in maniera totalmente sicura.

Schema a blocchi del sistema

In Fig. 1, è il riportato lo schema a blocchi del sistema. Ciò che balza subito all’occhio è il tipo di architettura: serverless, cioè senza alcun server, né domestico, né fornito come servizio da un provider.

Fig. 1 Schema di principio.

La comunicazione è diretta, tra il dispositivo apricancello connesso alla rete WiFi di casa e l’applicazione mobile, per mezzo del servizio cloud fornito da dweet.io, che consente lo scambio di messaggi in modo sicuro ed efficiente. Un vantaggio sia economico sia per l’ambiente, visto che non ci sono server e dispositivi di rete da gestire.

Hardware

Come abbiamo anticipato, per l’implementazione HW ci serviamo della board Ganimede.E12, dotata di un microcontrollore ESP-12F e degli slot di espansione necessari per il collegamento dell’hardware aggiuntivo. In particolare, collegheremo al connettore Neopixel (CN4) il relè 5 Vdc 10 A, in modo da controllarlo utilizzando un PIN atto alla gestione di un output digitale, ed il Display OLED miniatura 0,96” connesso tramite I2C al connettore CN7 predisposto sulla board. L’architettura hardware, riportata in Fig 2, è molto semplice e consentirà di sviluppare un firmware altrettanto lineare e leggibile.

Fig.2 Schema a blocchi HW.

Protocollo

Più volte è stato menzionato dweet.io come fornitore di servizi, ma prima di entrare nel dettaglio dell’architettura software che è stata utilizzata, è necessario capire il funzionamento di questo provider.

Il nome stesso richiama un famoso social network e piattaforma di microblogging, ed è proprio dal noto Twitter che prende spunto. Qui però non sono persone fisiche a scambiarsi messaggi, ma dispositivi IoT. Una volta definito un nome per il proprio dispositivo, il cosiddetto endpoint, tutti potranno scrivere e leggere dei messaggi riferiti a quell’endpoint, come se uno o più utenti stessero twittando riferendosi ad uno specifico argomento usando un hashtag. Molti altri servizi permettono la comunicazione di dispositivi IoT, ma i due fattori che rendono dweet.io particolarmente appetibile sono il protocollo usato e la gratuità del servizio.

Non vengono usati i tipici protocolli del mondo embedded per comunicare (o “dweettare”), ma delle web API, in particolare delle semplici HAPI (Humanized Application Programming Interface).

L’idea dietro le HAPI è creare uno standard per interfacce in modo che siano auto documentate, e quindi facili da essere interpretate, sia dalle macchine che le utilizzano, ma soprattutto dagli sviluppatori. I cardini principali dell’HAPI sono infatti:

  • l’accessibilità attraverso un URL, anche con un semplice web browser;
  • la facilità di lettura di input e output sotto forma di normali frasi;
  • la facilità di lettura, in modo che sia comprensibile anche a persone non tecniche.

Le HAPI sfruttano il protocollo applicativo HTTP, che usiamo quotidianamente per navigare su un qualsiasi sito internet, ma ne semplificano l’uso, poiché vengono utilizzati solo due dei diversi metodi a disposizione: GET e POST. Il primo per leggere un’informazione, o dweet, il secondo per crearla. Con le HAPI vengono standardizzati anche gli URL, così richieste e risposte sono facilmente interpretabili per qualunque tipo di comunicazione. L’idea di limitare la personalizzazione degli URL da parte di colui che si occupa del design delle API nasce dall’esigenza di semplificare le operazioni CRUD (Create, Read, Update, Delete) per la gestione dei dati, senza la necessità di consultare ripetutamente la documentazione.

Definendo un nome per il nostro device, ad esempio “EInApricancello”, il metodo GET che ci consentirà di leggere l’ultimo dweet sarà chiamato dal seguente URL:

https://dweet.io/get/latest/dweet/for/EInApricancello. Allo stesso modo sarà possibile creare un dweet con una richiesta POST all’URL seguente, inserendo un JSON nel corpo della richiesta stessa:

https://dweet.io/dweet/for/EInApricancello. Infine, è utile dare uno sguardo al contenuto completo di una richiesta, ovvero il JSON, che viene inviato quando postiamo un dweet. Supponendo che il corpo della richiesta sia una stringa di test, il dweet completo sarà:

{
“this”: “succeeded”,
“by”: “getting”,
“the”: “dweets”,
“with”: [

{
“thing”: “EInApricancello”,
“created”: “2023-05-15T18:41:17.166Z”,
“content”: {
“test”: “stringa di test!”
      }
    }
  ]
}

Analizzando in dettaglio il JSON del dweet è possibile notare come la lettura sia immediata grazie alla struttura delle HAPI. I campi dell’oggetto sono creati e popolati automaticamente dal servizio dweet.io, che oltre ai valori predefiniti inserisce il nome dell’endpoint, il timestamp, ed il contenuto della nostra richiesta nel campo “content”.

Ci siamo soffermati sulla semplicità di utilizzo del provider e abbiamo evidenziato la gratuità del servizio. Ma proprio quest’ultimo fattore implica delle restrizioni che sono sormontabili utilizzando la versione a pagamento del provider. Le maggiori limitazioni offerte dalla versione gratuita sono il limite temporale di richieste (non è possibile ‘dweettare’ con una frequenza maggiore di 1 Hz) e non sono previsti metodi di autenticazione e cifratura. Il fatto di non poter inviare più di una richiesta al secondo potrebbe essere un problema per applicazioni strict real-time, cioè che hanno bisogno di scambiarsi segnali con frequenze molto alte e tempi di risposta inferiori al millisecondo. Per applicazioni IoT, ed in particolare per un apricancello, non è necessario avere un requisito del genere.

Ovviamente ciò potrebbe essere una limitazione per sistemi embedded in cui le misure di safety sono indispensabili, come ad esempio le ECU utilizzate nel mondo automotive. Potrebbe invece essere un problema la mancanza di autenticazione, che ci garantirebbe di conoscere chi è la persona che sta interagendo con il dispositivo, e la mancanza di cifratura, per nascondere i dettagli dei messaggi inviati. La versione gratuita di dweet.io non solo non ha queste feature, ma al seguente link https://dweet.io/see è possibile vedere i messaggi scambiati dai dispositivi di tutto il mondo. Ciò potrebbe essere un problema per i dispositivi domotici, perché chiunque potrebbe, ad esempio, aprire il cancello di casa nostra, o vedere le informazioni scambiate dai nostri dispositivi. La soluzione più semplice al problema sarebbe acquistare la versione a pagamento del servizio, ma la più interessante è implementare un sistema per criptare il contenuto dei nostri dweet per fare in modo che nessun altro possa interagire con la nostra board, aprendo ad esempio il cancello al posto nostro.

Fig. 3 Schema di cifratura.

Cybersecurity

Per proteggere la comunicazione tra la board e l’applicazione mobile da malintenzionati è necessario implementare un meccanismo in grado di cifrare i messaggi. Come illustrato nel paragrafo precedente, possiamo ‘dweettare’ un messaggio, che non è altro che un JSON contenente i dati che vogliamo inviare. Il corpo di questo messaggio finisce nel campo content del dweet, mentre tutti gli altri parametri sono automaticamente impostati dal provider con dei metadati. Per rendere sicuri i nostri segnali abbiamo bisogno quindi di criptare il campo content, mentre possiamo ignorare il fatto che i metadati vengano inviati in chiaro.

Il primo passo da compiere è scegliere una tipologia di algoritmo che faccia al caso nostro. Per l’applicazione si è pensato di utilizzare un algoritmo a chiave simmetrica, vale a dire in grado di criptare e decifrare un messaggio utilizzando la stessa chiave. Questa tecnica è molto diffusa ed abbastanza semplice, ma ha anche alcuni svantaggi, ad esempio entrambi gli estremi coinvolti nella comunicazione devono avere la stessa chiave, e spesso proprio lo scambio della chiave rappresenta il nodo cruciale di tutta la comunicazione.

Si può quindi intuire che sia la board, che l’applicazione mobile, dovrebbero avere in memoria una chiave necessaria per far funzionare l’algoritmo di cifratura. L’idea è di fare in modo che l’utente imposti la chiave (sostanzialmente una password) all’interno dell’applicazione sul proprio cellulare, ed al primo avvio della board la trasmetta al dispositivo, insieme ad una serie di dati: il nome dell’endpoint dweet, il nome della rete wifi a cui la board si dovrà connettere, e la password di questa rete.

Una volta pensato a come la chiave possa essere usata dai nostri due punti di comunicazione possiamo passare alla descrizione di un generico algoritmo a chiave simmetrica: dato un messaggio P (PlainText o testo in chiaro) ed una chiave k (una password), il mittente utilizzando un algoritmo di crittografia simmetrica S otterrà un nuovo messaggio C (Ciphertext o messaggio cifrato): S (P, k) = C. Il destinatario, ottenuto il messaggio cifrato C, utilizzando la stessa chiave k ed un algoritmo di decrittazione D otterrà il messaggio iniziale in chiaro P: D (C, k) = P. Nel nostro caso sarà sempre l’app a fare da mittente ed a dover criptare il messaggio, mentre il dispositivo embedded dovrà decrittarlo.

Sono molteplici le implementazioni di algoritmi simili, ma per questa applicazione abbiamo scelto l’Advanced Encryption Standard (AES, conosciuto anche come Rijndael). L’AES è uno dei più famosi algoritmi a chiave simmetrica, usato in tutto il mondo per le sue specifiche di sicurezza, perfino come standard dal governo americano. Non entreremo nel merito dei dettagli tecnici e matematici dell’algoritmo perché grazie alla sua notorietà oggi sono tantissime le librerie che offrono robuste implementazioni facili da usare in diversi linguaggi di programmazione. Una volta implementato il nostro algoritmo i nostri dweet avranno il campo content cifrato:

{
“this”: “succeeded”,
“by”: “getting”,
“the”: “dweets”,
“with”: [
{
“thing”: “EInApricancello”,
“created”: “2023-05-15T18:41:17.166Z”,
“content”: {
“ciphertext”: “lkdjf98sdaflkj4lkajsdf!asdlkfj9”
      }
    }
  ]
}

La stringa contenuta dentro il ciphertext, apparentemente incomprensibile, una volta decifrata dalla nostra board conterrà il messaggio originale, ad esempio il comando per aprire il cancello. Ricordiamo che nessuno senza la password potrà decifrare il messaggio, ma il ciphertext sarà sempre visibile a tutti su dweet.io. Ciò potrebbe esporre il nostro sistema ad un attacco di tipo replay: se il nostro ciphertext contiene l’equivalente di un messaggio “Apri il cancello”, un potenziale malintenzionato potrebbe intercettare il messaggio ed inviarlo nuovamente in un secondo momento, riuscendo nell’intento.

Ciò potrebbe essere risolto aggiungendo un layer di autenticazione, ma c’è un’alternativa ben più semplice per ovviare al problema. È sufficiente inserire nel contenuto del nostro messaggio il timestamp dell’applicativo mobile, così facendo la board potrà decifrare il messaggio contenente il timestamp, confrontarlo con il proprio, e se la differenza è superiore al secondo potrà scartare il messaggio. In questo modo un attacco replay non funzionerebbe, perché, anche se un messaggio venisse intercettato e ritrasmesso in un secondo momento, il ciphertext conterrebbe un timestamp così vecchio che verrà sempre ignorato dal dispositivo (si noti anche che il timestamp è a sua volta crittografato, quindi non sarebbe possibile alterarlo per farlo apparire come recente).

Architettura software

Come abbiamo anticipato nell’introduzione, ci serviremo della scheda Ganimede.E12 come piattaforma HW per questo progetto. Come già ampliamente riportato nel numero 272 di ElettronicaIn, uno dei maggiori vantaggi di Ganimede è la possibilità di programmarla utilizzando micropython. L’uso di questo famoso linguaggio interpretato nel mondo embedded, ed in particolare per dispositivi domotici, che non hanno bisogno di performance elevatissime, ci consente di creare un’architettura software pulita, semplice, e con dei tempi di implementazioni bassissimi.

Prima di addentrarci nel codice e nei diagrammi dobbiamo precisare che per quest’articolo si è deciso di utilizzare una programmazione orientata agli oggetti (OOP) come paradigma di programmazione. Potrebbe essere considerata una scelta inusuale, ma probabilmente conveniente in questo caso. La programmazione procedurale è spesso preferita nei dispostivi embedded perché i linguaggi di basso livello, come il C, sono la scelta più usata per controllare la memoria byte per byte e ottimizzare quindi le performance. Sono veloci, danno al programmatore il più alto grado di libertà possibile, ma sono anche complicati, e proni agli errori. Ed ovviamente non prevedono l’uso di classi e oggetti.

È per questo che sono nati negli anni framework, linee guida, architetture e stili di implementazioni da seguire per raggiungere quella flessibilità nativa nei linguaggi di programmazione ad oggetti che nei linguaggi a basso livello sono difficili da riprodurre. Python non ci darà la possibilità di controllare ogni singolo byte che stiamo allocando, ma per un dispositivo IoT non è neanche troppo necessario. Per il nostro specifico caso, l’uso dell’OOP associato ad un linguaggio così flessibile e potente ci consentirà di scrivere del codice semplice, leggibile, pulito e di rispettare i principi SOLID (Single responsibility, Open-closed, Liskov substitution, Interface segregation, Dependency inversion), che consentono di avere un’implementazioni scalabile e mantenibile.

Ricordiamo che una classe non è altro che un modello, una sorta di descrizione di una parte del nostro programma. La classe contiene degli attributi, cioè delle variabili, e dei metodi, cioè funzioni. Quando una classe viene istanziata, allora avremmo creato un oggetto. Le classi che costituiscono il software sono:

AccessPoint: per l’implementazione della rete wifi Ad-Hoc utilizzata per configurare la board al primo avvio o dopo un reset;

WifiStation: che gestisce la connessione alla propria rete WiFi domestica;

Configuration: con un wrapper di metodi per semplificare il salvataggio in NvM delle configurazioni impostabili tramite app, in modo che siano disponibili anche quando effettuiamo un’hard reset (ad esempio spegnimento e riaccensione);

Dweet: che si limita a leggere in polling i messaggi su dweet.io, decriptarli ed effettuare il parser delle richieste. Infine ci sono tre classi che possiamo definire hardware dependent, poiché la loro implementazione è strettamente correlata all’hardware ed ai collegamenti di Ganimede:

Display: in cui sono specificati i pin e i metodi per interagire con il display OLED, utile per stampare messaggi di stato del device;

Button: che gestisce la pressione del bottone utente, in modo da gestire le funzionalità di reset dopo una lunga pressione;

Relay: che attiva e disattiva il relè connesso al connettore neopixel.

In Figura 4 è riportato un diagramma delle classi semplificato (UML Class Diagram), dove vengono riportati i metodi pubblici e privati della nostra implementazione, così come gli attributi con eventuali getter e setter.

Fig. 4 Diagramma UML delle classi.

Micropython automaticamente esegue il file main.py all’avvio della board, per questo lo abbiamo scelto come entry point del nostro software, il posto migliore per istanziare le nostre classi e per implementare la macchina a stati del nostro apricancello. Main.py contiene una sola funzione che esegue all’infinito la macchina a stati riportata in Figura 5.

Fig. 5 Diagramma di flusso main.py

Lo stato INIT verifica se è presente una configurazione in memoria come rappresentato in Figura 6.

Fig.6 Diagramma di flusso stato INIT.

Se non vi è alcuna configurazione, Ganimede attiva la rete WiFi Ad-Hoc per riceverne una, altrimenti passa allo stato CONNECT. In questa fase il dispositivo cercherà di collegarsi alla nostra rete WiFi. Una volta che la connessione effettua la FSM (Finite State Machine) transita allo stato DWEET, come riportato in Figura 7.

Fig. 7 Diagramma di flusso stato CONNECT.

Se la connessione dovesse cadere perché ad esempio vogliamo riavviare il router di casa, la board si riconnetterà automaticamente non appena il segnale ritorna disponibile. Lo stato DWEET è quello in cui il nostro software si troverà per quasi tutto il tempo di esecuzione, perché è proprio in queste poche righe di codice, in particolare con la funzione dweet.get() che andremo a leggere se abbiamo ricevuto un nuovo comando. Ogni qualvolta un nuovo messaggio valido è ricevuto, la FSM passerà a RELAY, ma soltanto per il tempo necessario ad attivare il relè. Il diagramma di flusso dello stato DWEET è riportato in Figura 8, mentre lo stato RELAY in Figura 9.

Fig. 8 Diagramma di flusso stato DWEET.

 

Fig.9 Diagramma di flusso stato RELAY.

Si noti come la FSM sia corredata del codice necessario (Listato 1) per leggere la pressione del bottone ed eventualmente effettuare un reset.

Inoltre il SW è corredato di una serie di messaggi di debug (inviati sulla console micropython) e utente (stampati sul display OLED) per fornire all’utente finale maggiori dettagli sullo stato del device. La classe AccessPoint, riportata nel Listato 2, è estremamente semplice poiché consta solo di due metodi: listen e close.

Grazie ai moduli già inclusi in micropython, l’implementazione di questa funzionalità è realizzabile con una manciata di righe di codice, a differenza delle centinaia necessarie se il linguaggio scelto fosse ad esempio il C. L’idea è di creare una rete WiFi con SSID (il nome della rete) ed una password predefinite e non modificabili, poiché l’interazione tra l’app e la rete Ad-Hoc avviene solo in fase di configurazione, una tantum, e la disponibilità della rete decade una volta che le impostazioni sono state salvate.

Per lo stesso motivo i dati scambiati in questa fase avverranno con l’utilizzo del protocollo HTTP (senza ‘S’). Il protocollo è per definizione non sicuro, ma l’implementazione di misure di sicurezza sofisticate che prevedono la gestione di chiavi e certificati non sembrano per niente adatte in questo caso. Rendere particolarmente sicura la comunicazione tra app e board, che avviene una sola volta nel ciclo di vita del prodotto, e che può durare non più di un paio di minuti, non giustificherebbe l’overhead che si creerebbe in termini di performance (salvataggio e recupero dalla memoria di chiavi e certificati) e in termini di tempi di sviluppo.

Il metodo in questione, come richiama la parola stessa, non fa altro che attivare la rete WiFi e ascoltare l’arrivo di un nuovo messaggio. Una volta che il messaggio è ricevuto, viene salvato in memoria non volatile ed il metodo close viene invocato. Quest’ultimo si limita ad interrompere la rete creata, in modo che nessun client vi si possa connettere, inviando per esempio altri messaggi malevoli.

Abbiamo più volte menzionato il salvataggio in memoria non volatile. Chi ha esperienza con la scrittura di firmware sa che è spesso un tasto dolente. Avere uno stack software in grado di fornire delle interfacce a chi scrive il software applicativo è indispensabile in un progetto medio/grande, altrimenti gli sviluppatori passeranno il tempo non a scrivere codice, ma ad analizzare gli indirizzi della memoria flash e verificare tutte le possibili eccezioni che possono nascere con i propri dati.

Ganimede non solo ci aiuta, ma va ben oltre. Possiamo salvare i nostri dati in semplici file di testo, utilizzando le normali funzioni di libreria di Python, e vi possiamo accedere come se stessimo leggendo il file system del nostro PC. Più che darci una mano Micropython compie una vera e propria rivoluzione sotto questo aspetto, consentendoci di focalizzarci sulla logica applicativa facendoci dimenticare indirizzi, dimensioni e allineamenti vari. La classe Configuration (Listato 3) non fa quindi altro che fornire dei metodi per le operazioni di scrittura, lettura e cancellazioni più comuni e necessarie per salvare i nostri parametri.

Il metodo privato parse_data viene utilizzato per leggere il JSON proveniente dall’app mobile. Il formato JSON è stato scelto per lo scambio dei dati perché è il più diffuso quando si hanno delle web API, è immediato da leggere e semplice da gestire, visto che Python possiede delle funzioni di libreria per la gestione del formato.

I metodi contenuti in questa classe non fanno altro che leggere il contenuto di un file di testo, denominato configuration.txt, scrivere il contenuto del file, ed eventualmente cancellarlo, ad esempio in seguito ad un reset. Un altro importante componente software del nostro sistema è dato dalla classe WifiStation.

La connessione ad una rete WiFi è ormai una procedura integrata nelle librerie standard di molti linguaggi di programmazione. Python non è da meno, basta fornire SSID (il nome della rete) e la password, che prenderemo dalla configurazione precedentemente salvata, alle API fornite dalla libreria e grazie al modulo network importato sarà possibile connettersi. Ovviamente per passare al prossimo stato della FSM dobbiamo assicurarci di essere connessi alla rete, da qui l’esposizione del metodo isConnected che effettua questa verifica. La maggior parte della logica applicativa è gestita all’interno della classe Dweet (Listato 5).

Importando il modulo request abbiamo accesso ad una serie di funzioni in grado di chiamare in polling il metodo GET del protocollo HTTP con l’API definita da dweet.io. Il metodo viene ciclicamente chiamato in attesa di un nuovo comando. Quando il servizio invia una risposta valida, la board deve processare la richiesta. Il primo passo è decriptare i dati. Il metodo privato decrypt che prende in input il messaggio criptato (ciphertext) e la password contenuta nel file config.txt, utilizzando la cryptolib di Python chiama le funzioni di libreria necessarie per decifrare il messaggio utilizzando l’AES spiegato precedentemente.

Nel paragrafo dedicato alla cybersecurity abbiamo menzionato come la validità del messaggio, per evitare un attacco di tipo replay, viene implementata non tramite autenticazione, ma paragonando il timestamp della board con quello inviato dall’applicazione mobile. Se la differenza è sufficientemente bassa il messaggio viene considerato valido. Alla semplice logica che compara gli istanti di tempo, sono state aggiunte le funzioni per convertire i timestamp in modo che siano facilmente confrontabili.

È noto che solitamente per timestamp si intendono i secondi, o in alcuni casi i millisecondi, trascorsi dal 1 gennaio 1970 UTC. Questo è il valore inviato dall’app, mentre il timestamp restituito dal componente RTC (Real Time Clock) della board restituisce i secondi dal 1 gennaio 2000. Le cosiddette epoch sono differenti, è necessario quindi effettuare una conversione. Il lettore più attento noterà che, mentre l’applicazione sul cellulare invierà sempre un orario valido vista la presenza quasi permanente di una connessione ad internet, il dispositivo embedded ha bisogno di precise istruzioni per effettuare questo sync. La seguente linea di codice contenuta in main.py permette la sincronizzazione dell’orario attraverso il protocollo NTP (Network Time Protocol):

ntptime.settime()

Le classi descritte fin qui interagiscono con l’hardware, in modo particolare con i componenti interni al System-on-Chip, ossia l’ESP12F. Passiamo ora all’interazioni con l’hardware esterno, cioè il relè, il display ed il bottone utente presente sulla Ganimede. La classe Button, riportata nel Listato 6, contiene una funzione di inizializzazione in cui sono specificati i pin del pulsante, un metodo privato che rileva se il pulsante è stato premuto, ed un metodo pubblico che restituisce il numero di millisecondi per cui l’utente l’ha tenuto pigiato.

Questa funzionalità è utile per effettuare un reset solo dopo una pressione piuttosto lunga, ad esempio 5 secondi.

Relay.py, descritta dal codice del Listato 7, ha un’analoga funzione di init, un metodo per attivare il relè ed uno per disattivarlo, ed un terzo per chiudere il contatto per un certo numero di millisecondi.

Quest’ultimo è particolarmente utile nel caso dell’attivazione di un motore per il cancello, perché vogliamo che si attivi per un certo numero di secondi necessari all’apertura, nel caso il nostro relè vada fisicamente a pilotare un motore. Nel caso in cui sia sufficiente solo un impulso, possiamo comunque ridurre questa attivazione a piacimento (ad esempio impostando 1 o 2 secondi di attivazione). In Display.py, Listato 8, infine abbiamo le funzioni che permettono di stampare un testo sul display OLED da 0.96”.

Il modulo ssd1306 già contiene tutte le funzioni necessarie, quindi la classe si limita a fare da wrapper per adeguarle meglio al nostro caso d’uso. In questo file però, a differenza dei precedenti, abbiamo usato l’implementazione del singleton pattern, creando l’apposito decorator. Così facendo, ogni qualvolta si tenterà di creare un nuovo oggetto, viene effettuato un controllo: se un’istanza della classe esiste già, verrà restituito il riferimento, altrimenti verrà creata.

Il motivo di questa scelta viene dal fatto che mentre Button e Relay vengono istanziate ed usate sono all’interno di main.py, Display viene usato all’interno di tutte le classi, poiché la stampa di un messaggio di stato può essere effettuata da qualunque componente. Poiché non vogliamo che la funzione di init venga richiamata ogni volta, visto che è sempre la stessa, così come non vogliamo allocazioni multiple di variabili che contengono uguale valore, abbiamo deciso di utilizzare questo paradigma.

Architettura app

Come più volte annunciato la semplicità di utilizzo dell’intero progetto è data anche dall’applicazione mobile che permette una facile interazione con l’hardware. L’applicazione è stata realizzata con un focus particolare alla facilità di utilizzo e alle performance in termini di velocità di esecuzione, con una grafica basata su un’interfaccia Material Design, e la possibilità di avere un prodotto multipiattaforma, Android e iOS. In Fig. 10 è visibile un’immagine dell’interfaccia.

Fig. 10 L’app per Android ed iOS per il controllo dell’apricancello.

L’applicazione è autoesplicativa e molto intuitiva: da un menù laterale si può selezionare la schermata principale per inviare un comando alla baord, una schermata con le impostazioni relative alla configurazione di dweet.io ed un’altra con SSID e password della propria rete WiFi. Per maggiori informazioni relative ai settaggi ed al dispositivo in generale è presente una maschera dedicata alle FAQ, le domande più frequenti.

L’applicazione è pronta per essere usata dall’utente finale, ma per i più curiosi vogliamo fornire maggiori dettagli relativi all’implementazione. Per la realizzazione si è scelto di usare il framework Ionic. Così facendo si può creare un’applicazione per Android e iOS partendo dallo stesso codice. Il framework offre la possibilità di sviluppare utilizzando il linguaggio Javascript associato ad un framework di front-end, nel nostro caso React. Così facendo il tempo di sviluppo è notevolmente ridotto, e allo stesso tempo è possibile utilizzare note librerie per React, come Material UI per lo sviluppo di un’interfaccia grafica moderna e accattivante, piuttosto semplice nel nostro caso. Per concludere sottolineiamo ancora una volta come il comando inviato dall’applicazione non è altro che una richiesta HTTP POST criptata a dweet.io, quindi non è presente alcun intermediario e la nostra privacy è ben tutelata.

Varianti

Il progetto descritto fa uso del seguente hardware esterno alla board: un relè ed un display OLED da 0.96”. Ricordiamo che grazie all’implementazione modulare descritta al paragrafo precedente, per sostituire uno dei componenti non si dovrà fare altro che sostituire la relativa classe. Per utilizzare un altro display basterà modificare Display.py, o basterà rimuovere le chiamate alle funzioni se non vogliamo alcuno schermo. Cambiando il file Relay.py potremmo utilizzare un hardware diverso, come un modulo click-relè o un relè-groove, sfruttando i connettori presenti su Ganimede. Potenzialmente potremmo anche utilizzare un altro pulsante al posto di quello integrato sulla board, tutto grazie alla versatilità ed ai numerosi bus e connettori presenti su Ganimede e già descritti nel numero 272 di ElettronicaIn.

Case

Grazie al contributo di un appassionato della comunità Ganimede (Bruno Luziatelli, che ringraziamo), è stata sviluppata anche una scatola stampabile in 3d, in grado di contenere Ganimede, il relè (codice Futura Elettronica RELAY1CH) ed il display OLED (sempre disponibile sul sito di Futura Elettronica, OLEDGVSCSD). Il risultato (visibile in Fig. 13) è una scatola dotata di staffe per fissaggio e opportuni fori per i cablaggi, che risulta molto utile per l’utilizzo in installazioni reali. Data la presenza di fori per display e cablaggi vari, raccomandiamo comunque l’utilizzo di questa scatola per installazioni in interni, o protette da opportuni contenitori con grado di protezione adeguato all’applicazione. I file Step per la stampa del case possono essere scaricati dal sito della rivista.

Conclusioni

Il progetto presentato in questo numero dimostra come, tramite l’utilizzo di Ganimede.E12 e del framework micropyton correlato, sia possibile realizzare in pochissimo tempo un dispositivo affidabile e sicuro, anche per chi è meno esperto. Troviamo a bassissimo prezzo migliaia di dispositivi in grado di controllare relè a distanza, ma pochissimi offrono un’architettura serverless come quella descritta, e quasi nessuno è realizzato sfruttando servizi gratuiti, open source e sicuri. Il sistema esposto consente inoltre al lettore di trarre spunto per creare altri infiniti dispositivi che hanno bisogno di dialogare con sistemi remoti, sfruttando sia dweet.io, sia le potenzialità di Ganimede e MicroPython.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Menu