basduino: Arduino yun come display arrivi per gli autobus romani

 

TotemEra da tempo che cercavo un progettino sul quale cimentarmi con il mio arduino yun, uno di quelli che mi impegnasse almeno più di un giorno, quando, camminando per Roma, mi sono imbattuto in uno di questi pannelli. L’Atac che è l’agenzia della mobilità romana, ha da qualche tempo installato alcuni di questi pannelli in giro per la città. La loro funzione (usando il condizionale) sarebbe quella di informare gli utenti di bus e tram degli orari di arrivo per una determinata fermata o banchina. Una cosa molto utile soprattutto per i turisti che, magari, non possono usare le utili applicazioni per smartphone che come il pannello forniscono le stesse informazioni. Fra le varie applicazioni e servizi che offrono questo tipo di informazioni c’è il sito: muovi.roma , che non è altro che una versione più accessibile del sito Atac. Fra i vari link presenti su quella pagina non mi sono fatto sfuggire questo: Atac MobileChe mi ha portato alla pagina del progetto Atac Open Data. Con Enorme Sorpresa ho quindi scoperto che l’Atac ha realizzato una serie di strumenti ed api, completamente gratuite ed open source, per interfacciarsi con il sistema di trasporto romano.

Navigando sul sito del progetto e spulciando fra la documentazione delle varie api, ho trovato quello che faceva al caso mio: l’api “ paline.Previsioni ” con relativo esempio di utilizzo. Questa non fa altro che prendere in input un numero palina (La fermata che volete monitorare) e restituire una struttura dati contenente gli arrivi e la lista delle linee che passano per quella fermata, o palina. Avendo quindi trovato il dove (prendere le previsioni), non restava altro che pensare al come.

L’architettura:Schema

  • Uno script python raccoglie le previsioni grazie all’uso delle api Open Data scrivendole in un file;
  • Un demone linux:
    1. Controlla lo script avviandolo a tempo e inizializzandolo con i valori contenuti in un file d’impostazioni;
    2. Logga l’attività dello script python;
  • Un’interfaccia php permette:
    1. L’avvio e l’arresto del demone;
    2. Il cambio palina o fermata, salvando i parametri nel file di configurazione e, se necessario, permette di riavviare il demone;
    3. Letture ed eliminazione del file di log;
  • Il software Arduino che legge il file creato dallo script python, e lo scrive nel display paginandolo;

L’Hardware:

  • Un Arduino yun;
  • Un alimentatore usb;
  • Una scheda micro sd;
  • Un display LCD (quello che uso io è di tipo seriale 4×20);
  • Jumper o cavi dupont per i collegamenti elettrici;
  • Una connessione internet per il download dei dati;
  • Il codice sorgente che potete scaricare da qui;

Preparativi:

Prima di cominciare a smanettare con il codice, se non l’avete già fatto vi consiglio di fare un paio di cose al vostro yun:

  • Aumentare lo spazio di memorizzazione inserendo la scheda micro sd, seguendo il tutorial presente sul sito di Arduino.
  • Aggiornare Linino all’ultima versione disponibile seguendo questo tutorial sempre presente sul sito di Arduino.

Ancora più importante è avere un ambiente di lavoro il più produttivo possibile, che vi permetta di scrivere il codice nel migliore dei modi. Dato che yun è sprovvisto di un’interfaccia grafica, il più classico dei modi per il mondo Unix/Linux è l’uso di un text editor come VI o nano. Fra i due, quello che uso da anni è nano, che per qualche arcano motivo sullo yun non voleva partire, fino a quando non ho aggiunto al sistema questa variabile d’ambiente con il comando:

echo “export TERM=xterm” >> /etc/profile.

Fatto ciò, c’è da dire che fa comodo avere un editor testuale da usare direttamente nella shell ma nel 2015 ci sono altre possibilità, come ad esempio la condivisione dei file con un computer dove è installato un editor di testo grafico(che magari abbia funzioni più avanzate di quelle di nano e VI). Tra i protocolli di condivisione file più usati ci sono NFS; SFTP/SCP e Samba, ma forse quello più utilizzato è Samba, che non è altro che l’implementazione Open Source della condivisione file di Windows. Installandolo sul vostro Yun potrete accedere e modificare i file del vostro Arduino Yun direttamente sul vostro pc, sia esso Linux, Windows o Mac. Se vi interessa potete dare uno sguardo a questo breve tutorial.

Lo script python:

Bene, siamo quasi pronti per la nostra prima richiesta che, come detto in precedenza, farà uso del codice fornito nell’esempio presente sul sito open bus. Ma prima di ciò parliamo di un paio di cose:

  • La prima è che queste api sono state realizzate in python, un linguaggio di programmazione molto in voga negli ultimi anni che, con grande semplicità, permette di realizzare qualsiasi tipo di applicazione o script. Python è disponibile gratuitamente per tutte le maggiori piattaforme e fra queste c’è proprio Linux che nel nostro caso è nella variate(distribuzione) Linino/Openwrt il sistema operativo dell’Arduino yun.
  • La seconda cosa da sapere è che come protocollo di trasmissione delle informazioni viene usato XML-RPC , un protocollo che vi permette di effettuare chiamate a procedure che sono su un web server remoto.

Detto ciò, passiamo ai fatti. Atac fornisce un semplice esempio di utilizzo delle api, ma se non avete mai sentito parlare di python vi consiglio di leggere o guardare uno dei tantissimi tutorial che affollano la rete, così da avere un’infarinatura generale. Per accedere alle api bisogna prima registrarsi come indicato nella pagina del progetto Open Data, dopo questa formalità si è pronti per cominciare creando il primo script python:

Esempio python

Una volta creato il file e, dopo aver inserito la chiave ottenuta dal modulo di registrazione, basterà digitare “python nomefile.py” per avviare lo script appena creato. Come output avremo il “pprint” della risposta, ma cos’è “pprint” ? E’ un modulo python usato per “stampare” il contenuto di una struttura dati in maniera più accattivante. Se la risposta fosse troppo grande da essere visualizzata nel vostro terminale, potete effettuare un redirect verso un file con: “python nomefile.py > output.txt”: in questa maniera l’output del vostro script python viene scritto in un file di testo chiamato “output.txt”.

Output prova

Analizzando il contenuto della risposta, troveremo le previsioni di arrivo per la nostra palina nel formato descritto nella pagina di documentazione dell’api, ma come avete potuto vedere l’output contiene una rappresentazione degli arrivi che non può essere ancora utilizzata per il nostro scopo. Abbiamo quindi bisogno di selezionare e formattare quello che ci interessa, e di parametrizzare lo script, cioè trovare un modo per cambiare il numero palina dinamicamente, senza mettere mano allo script. Per la parametrizzazione, possiamo semplicemente prendere il numero palina e la chiave come parametri del nostro script. Aprendo il file “atac.py” dalla cartella “palina” del codice sorgente, possiamo quindi vedere che con l’istruzione “NUMERO_PALINA = str(sys.argv[2])” assegniamo il valore del secondo parametro di linea di comando alla variabile numero palina.

Per la selezione, invece, basta scorrere la lista “primi per palina” con un ciclo “for” e selezionare con una serie di “if” gli arrivi con le proprietà che più ci interessano, per esempio non visualizzando gli arrivi delle linee non monitorate o quelle per le quali non è previsto nessun arrivo. Dato il piccolo display a nostra disposizione dobbiamo rendere l’output del nostro script più leggibile: per fare ciò eliminiamo i caratteri come le parentesi, e riduciamo parole come capolinea in “Capol” e fermate in “Ferm”, facendo delle piccole sostituzioni – es: ”.replace(“Capolinea”, “Capol”). Eseguendo lo script dalla riga di comando con “python atatc.py chiave_atac numero_fermata” avremo il seguente output:
Output

Che verrà poi visualizzato sul display così:

Output display

Per rendere il tutto più solido possiamo anche aggiungere dei blocchi “try, except” per una corretta gestione delle eccezioni, in modo da non vedere “esplodere” il nostro script al primo problema.

Il demone Linux:

Per il controllo dello script python ho deciso di utilizzare un demone linux (che non ha nulla a che vedere con i Black Sabbath). In un sistema Unix/Linux, un demone è un programma o servizio che viene eseguito in background. Di norma un demone viene lanciato all’avvio della macchina dal processo init, che è il primo processo ad essere eseguito dal kernel dopo il bootstrap. Solitamente un demone è composto da due parti:

  • Uno script di controllo: invocato da init o dall’utente per avviare, fermare o fare quel che si voglia con il demone. Questo viene scritto in un linguaggio di scipting per shell, fra le sue funzioni c’è anche quella di impedire che vengano eseguite due istanze dello stesso demone allo stesso tempo (impedendo l’accesso concorrente alle risorse condivise).
  • L’eseguibile del demone, ovvero quella parte software che esegue ciò per cui il demone viene creato. Nel nostro caso: temporizzare e parametrizzare la richiesta degli arrivi per una determinata palina. L’eseguibile può essere di qualsiasi tipo, ad esempio: un programma realizzato in linguaggio c, uno script bash o per assurdo uno script php(attraverso l’interfaccia php-cli).

Per approfondire l’argomento vi consiglio di leggere uno dei tanti tutorial presenti in rete, tra i quali per Linino/Openwrt vi consiglio la nota tecnica presente sul sito ufficiale di Openwrt.

 Aprendo il file “/etc/init.d/pallina” contenuto nell’archivio del codice sorgente potrete vedere la mia implementazione dello script di controllo. Il codice è commentato e dovrebbe essere di facile comprensione, l’unica parte degna di nota è forse il blocco “reload”, nel quale dopo aver controllato che l’eseguibile sia in esecuzione, invio il segnale SIGUSR1. Questo segnale è un segnale “User defined” al quale si può dare il significato che si vuole, nel mio caso lo uso per ordinare al programma ricevente di ricaricare le impostazioni dal file.

Veniamo ora al corpo del demone: il file “/root/pallina/pallinad” il quale è scritto in bash (uno dei linguaggi di scripting più usati al mondo). Linino non ha bash pre installato perché su openwrt la shell di default è ash, per installare bash dovete eseguire i seguenti comandi: “opkg update; opkg install bash”.

Lo script parte chiamando la routine “loadSettings” la quale carica i parametri dal file “settings.ini”, in questo file sono troviamo: il numero palina da monitorare, la chiave atac e il numero di secondi da attendere prima di una nuova richiesta.

Il secondo step e dichiarare due Traps:

  • Una, per i segnali SIGHUP, SIGINT e SIGTERM che sono inviati dal sistema operativo per informare lo script di un’imminente chiusura. Nel caso in cui uno di questi segnali venga ricevuto, l’esecuzione si sposterà al routine “clean_up” che termina l’esecuzione dello script.
  • L’altra, per il segnale SIGUSR1 che, se ricevuto, porterà all’esecuzione della routine “reload”, che ricarica i parametri di funzionamento dal file “settings.ini”.

A questo punto informiamo il mondo dell’avvio del demone in due modi:

  • Invocando la routine “addToLog …“ che serve ad aggiungere una entry contente data e ora al file di log.
  • Invocando la routine “setOutputFile …” che serve ad impostare un valore nel file ultima.txt (il file che verrà poi visualizzato sul display) aggiungendo un’ulteriore riga contente una sequenza speciale di caratteri “#*#”, il cui scopo è quello di indicare che la scrittura sul file è completata.

Siamo ora nel vivo del codice. Dopo aver verificato che il parametro “TIMER” sia maggiore di 10 secondi(per evitare di bombardare il server con troppe richieste), siamo pronti per far partire il while che serve a creare il timer delle richieste. Fino a quando la variabile “TIMER_ENABLED” ha valore “TRUE” eseguiremo un semplice ciclo nel quale richiederemo le previsioni con la chiamata alla routine “aggiorna”, per poi dormire per un numero di secondi uguale della variabile “TIMER”.

Perché ho creato un ciclo che dorme (istruzione sleep) per un solo secondo alla volta, anziché dormire con unica istruzione per un numero di secondi pari al valore della variabile “TIMER” ? Ve lo spiego con un esempio: se sospendessimo l’esecuzione per 15 secondi non avremmo la possibilità di ricevere nessun segnale dal sistema operativo per tutta la durata di questo tempo, cosa che renderebbe il nostro codice poco efficiente. Con il ciclo, invece, dormiamo per un solo secondo alla volta, processando le eventuali richieste dell’OS in tempo per dare un feedback all’utente.

Nella routine “aggiorna” dopo aver controllato che tutti i parametri necessari abbiano un valore, passiamo all’azione con questa istruzione: “$DOWNLOADER $CHIAVE_ATAC $NUMEROPALINA > /tmp/tmp.txt 2>&1”. Chiamiamo lo script python, che è rappresentato dalla variabile “DOWNLOADER=”python /root/pallina/atac.py”” inviandogli i due parametri “$CHIAVE_ATAC” e “$NUMEROPALINA”, riversando l’output nel file “tmp.txt” e non nel file “ultima.txt”. Questo perché, dobbiamo prima verificare che l’output che vogliamo inviare al display contenga le previsioni della palina richiesta senza nessun errore, se tutto è apposto possiamo versare il contenuto del file “tmp.txt” nel file “ultima.txt”, aggiungendo quella strana sequenza di caratteri segnalati prima: “#*#”. Lo scopo di questa stringa è quello di dichiarare che la scrittura del file è terminata, in modo da evitare che sia Arduino che l’interfaccia php leggano un file non completo. Questo perché ogni parte di questo progetto (Arduino, demone, interfaccia php) lavora in maniera asincrona.

L’interfaccia web:

Come ben sappiamo, la cosa che rende Arduino Yun diverso da i suoi fratelli Uno e Leonardo, sta nel fatto che Yun oltre ad essere un microcontrollore programmabile è anche un piccolo computer Linux che può fare affidamento su un vasto se non illimitato parco software fatto da: librerie, interpreti, server web ecc … estendendo di fatto le potenzialità della piattaforma Arduino. Fra questi c’è php, un linguaggio di scripting lato server che, ad oggi, viene usato da milioni di sviluppatori per rendere i loro siti web dinamici.

Data questa possibilità ho deciso di realizzare un’interfaccia web php, le cui funzioni sono:

  • Avvio e Arresto del demone pallina.
  • Cambio del numero palina/fermata.
  • Consultazione degli arrivi.
  • Consultazione, Scarico ed Eliminazione del file di log.
Interfaccia

L’interfaccia web

Per installare php sul vostro yun seguite questi semplici passi:

  • Da terminale: “opkg update; opkg install php5 php5-cgi php5-cli zoneinfo-europe”.
  • Sempre da terminale modificate il file \“/etc/config/uhttpd” togliendo la grata dalla riga “list interpreter       “.php=/usr/bin/php-cgi”\” che attiverà il supporto php per il nostro webserver.
  • Attivate il supporto per il nostro fuso orario aggiungendo questa riga \”date.timezone = “Europe/Rome”\” al file “/etc/php.ini”.
  • Riavviate il vostro yun.

I file che compongono l’interfaccia si trovano nella cartella “/www/sd/”. Il file “main.php” è la parte principale di questa interfaccia. La pagina è composta da una tabella che contiene cinque righe, le quali contengono:

  • Il blocco <div> “status” che con una cadenza di 15 secondi aggiorna lo stato del demone e genera il tasto di avvio e arresto. Il div viene creato dall’esecuzione dello script php “deamon.php”.
  • Il riferimento allo scipt che visualizza e cambia il numero della pallina “cambia_pallina.php”.
  • Il riferimento allo scipt che visualizza e cambia la chiave per accedere ai servizi Open Data “cambia_pallina.php”.
  • Il blocco <div> “file_arrivi” che con una cadenza di 5 secondi visualizza il contenuto del file “ultima.txt”, questo div viene creato con l’esecuzione dello script php “leggi_arrivi.php”.
  • Il blocco <div> “log_file” che con una cadenza di 5 secondi visualizza il contenuto del file di log con l’esecuzione dello script php “leggi_log.php”.

Per aggiornare le varie parti della pagina senza ricaricare la pagina stessa, uso jQuery, un potentissimo framework gratuito basato su javascript che con poche righe di codice vi permette di dotare di effetti dinamici le vostre pagine web.

main.phpIn questo piccolo esempio tratto da “main.php” definisco due funzioni:

  • Una che viene invocata ogni 5 secondi per generare i div contenenti gli arrivi e il file di log.
  • L’atra che viene invocata ogni 15 secondi per generare il div “status”.

Il resto dei file previa lettura dovrebbe essere di facile comprensione. Se avete dubbi o non riuscite a capire parte del codice, lasciate un commento a fine articolo, tempo permettendo cercherò di rispondervi.

Lo Sketch Arduino:

In tutti o quasi i miei progetti troverete l’uso del watchdog e di un led di stato, questo perché il watchdog aumenta l’affidabilità del software, e il led di stato informa l’utente della vitalità del sistema. In un sistema di controllo lo scopo del watchdog è quello di resettare il microprocessore\microcontrollore se questo non risponde ad uno stimolo entro una deadline (Intervallo di tempo) configurabile. Il microcontrollore del nostro Arduino Yun (che è un ATmega32u4) è dotato anche di un watchdog configurabile, che può arrivare ad una deadline massima di 8 secondi.

Su Arduino l’accesso alle funzionalità del watchdog avviene grazie alla libreria “avr/wdt.h“, che ha sostanzialmente tre routine:

  • “wdt_disable()”: per disabilitare il watchdog.
  • “wdt_enable(WDTO_4S)”: per attivare il watchdog settando la lunghezza della deadline.
  • “wdt_reset()”: per resettarne il contatore.

Se guardate il blocco setup (quello eseguito all’avvio dello schetch) del file “yunlcd.ino”, potrete vedere che la prima cosa che faccio è quella di disabilitare il watchdog (nel caso fosse rimasto attivo dall’ultimo reset), eseguo poi tutte le operazioni necessarie all’avvio dello schetch e, prima di passare alla routine loop, abilito il watchdog con una deadline di 4 secondi. Questo farà si che se per un tempo maggiore di 4 secondi NON resetto il watchdog, questo resetterà il Microcontrollore dello yun. La lunghezza della deadline dipende dal tipo di applicazione che state realizzando, ma su questo argomento si sono dibattuti in tanti, quindi non vado oltre poichè ne potremmo parlare per giorni, se non per mesi. Se ne siete interessati, la rete ha tutte le risposte che cercate. Se avete seguito il mio discorso, avrete anche capito che da questo momento non potete interrompere il flusso di esecuzione con l’istruzione delay per più di quattro secondi, a meno che non vogliate forzatamente resettare il vostro Arduino. Quindi, cosa si può fare in questo caso? Semplice, si può dividere il tempo per il quale si deve “dormire” in piccoli time slice (termine preso in prestito dai sistemi in time sharing) e al termine di ognuno di questi si può resettare il watchdog, fino ad arrivare ad una somma di time slice uguale al tempo per il quale vogliamo dormire. Questo si vede nella routine wdDelay nel file “yunlcd.ino”:

Sketch ArduinoDove ho anche aggiunto la funzionalità del led di stato, con la routine “led()” che come prima cosa resetta il watchdog e, se necessario, cambia lo stato del led da accesso a spento con una frequenza definita nella variabile “nextLedTick”. Se dopo aver letto il codice avete ancora le idee confuse sulla multiprogrammazione con Arduino, vi consiglio di leggere questo tutorial che vi spiegherà come far lampeggiare un led senza l’uso del classico delay.

Il resto del codice si concentra sulla scrittura nel display del contenuto del file “/pallina/ultima.txt“che come sappiamo contiene le previsioni di arrivo. Il display che ho deciso di utilizzare in questo progetto è un 4 righe per 20 caratteri di tipo seriale (LCM1602 IIC V1). Questo tipo di display si controlla con solo 2 cavi e con un costo leggermente superiore a quello dei display paralleli vi permette di perdere meno tempo nei collegamenti, e lascia liberi alle vostre esigenze gli altri pin di input/output. La libreria che vi permette di interfacciarvi con questo tipo di display è la “LiquidCrystal_I2C”, che potete trovare a questo indirizzo, con relativi esempi di utilizzo. Credo che il resto del codice accompagnato dai relativi commenti sia di facile comprensione, l’unica cosa che potrebbe darvi noia, se non l’avete vista prima, è la coda “QueueList <String> queue;” che uso per memorizzare le righe contenute nel file delle previsioni. In generale una struttura dati è un modo per rappresentare dei dati in un computer, sono esempi di strutture dati: le variabili, gli array(vettori) e le code. Le code sono strutture dati di tipo FIFO, il primo dato ad essere inserito(con l’operazione push) è anche il primo ad essere estratto(con l’operazione pop). In Arduino le code si implementano con la classe “QueueList” che potete trovare qui, nel mio esempio questa contiene delle stringhe ma può contenere qualsiasi tipo di dato. Come detto in precedenza se avete dubbi o non riuscite a capire parte del codice, lasciate un commento a fine articolo, tempo permettendo cercherò di rispondervi.

Conclusioni:

Lo scopo che mi ero preposto in questo primo articolo era quello di mostrare Arduino Yun nel pieno della sua potenza, con un esempio pratico che mostrasse la parte Linux al fianco di quella classica. Un fattore che credo non debba essere sottovalutato quando si sviluppa con Arduino Yun è la portabilità del codice che con poche modifiche può girare su qualsiasi piattaforma Linux, rendendo Arduino Yun una economica piattaforma di apprendimento, oltre che un potente strumento per lo sviluppo delle proprie idee. Spero che sia l’articolo che il codice allegato (che è sotto licenza GNU GPL V2) possa esservi d’aiuto o spunto, se avete domande, commenti o idee non esitate a postarle.

Alla prossima.

Questa voce è stata pubblicata in Arduino, Linux e contrassegnata con , , , , , , , , , , , . Contrassegna il permalink.

Rispondi