Il 0% ha trovato utile questo documento (0 voti)
3 visualizzazioni

05 ComunicazioneArduino

Il documento descrive come comunicare tra una Raspberry Pi e un Arduino utilizzando la comunicazione seriale. Spiega come collegare fisicamente le due schede, installare l'IDE Arduino su Raspberry Pi e usare la libreria PySerial in Python per leggere e scrivere sulla porta seriale. Fornisce esempi di codice Python e Arduino per lo scambio di messaggi tra le due schede.

Caricato da

ipasrl.guest
Copyright
© © All Rights Reserved
Formati disponibili
Scarica in formato PDF, TXT o leggi online su Scribd
Il 0% ha trovato utile questo documento (0 voti)
3 visualizzazioni

05 ComunicazioneArduino

Il documento descrive come comunicare tra una Raspberry Pi e un Arduino utilizzando la comunicazione seriale. Spiega come collegare fisicamente le due schede, installare l'IDE Arduino su Raspberry Pi e usare la libreria PySerial in Python per leggere e scrivere sulla porta seriale. Fornisce esempi di codice Python e Arduino per lo scambio di messaggi tra le due schede.

Caricato da

ipasrl.guest
Copyright
© © All Rights Reserved
Formati disponibili
Scarica in formato PDF, TXT o leggi online su Scribd
Sei sulla pagina 1/ 47

Mod 18 –

Programmazione
Raspberry Pi
Comunicazione con Arduino
Raspberry Pi – Comunicazione Arduino
Comunicazione Seriale

TX – Transmitter • Tx e Rx delle GPIO della Raspberry Pi NON


RX - Receiver possono essere utilizzate per collegarlo
direttamente agli stessi pin di Arduino perché
c’è una differenza di potenziale (Raspberry
3.3V e Arduino 5V)
• Dovrei in ogni caso collegare il pin TX di una
scheda al pin RX dell’altra ma in mezzo dovrei
preparare un partitore di tensione
• In alternativa utilizziamo direttamente lo
stesso cavo con cui si collega arduino al pc, è
sempre una comunicazione seriale ed essendo
la Raspberry un microcomputer può
controllare direttamente arduino
Raspberry Pi – Comunicazione Arduino
Arduino IDE

• È possibile installare l’IDE tramite il comando:


sudo apt install arduino
• Tuttavia nelle repository del sistema operativo c’è una
versione datata, la 1.0.5 quindi è meglio scaricare
direttamente il programma dal sito di Arduino
• Scaricare l’ultima versione dal sito:
https://www.arduino.cc/en/software
• ATTENZIONE! Su Raspberry occorre scaricare la versione Linux
ARM 32 bits mentre su macchina virtuale o su un computer
dovrete scaricare la versione Linux 32 bit
• La parola chiave ARM infatti è specifica per i processori
installati sulle Raspberry e non possono funzionare con i
processi di computer normali
Raspberry Pi – Comunicazione Arduino
Arduino IDE

• Il file scaricato si troverà nei download ma è in formato


‘.tar.tx’, ovvero uno dei formati compressi esistenti sui sistemi
Linux
• Per estrarre i file compressi si utilizza il comando tar con la
seguente sintassi (bisogna essere nella stessa cartella in cui si
trova il file compresso):
sudo tar Jxvf arduino-1.8.13-linuxarm.tar.xz
• Il significato delle opzioni è:
J indica il formato del file da decomprimere (.xz)
x indica che vogliamo estrarre i file
v indica che vogliamo essere informati sui file che stanno
venendo estratti (altrimenti il programma non dà output)
f indica di salvare nella stessa posizione dell’archivio
Raspberry Pi – Comunicazione Arduino
Arduino IDE

• Se tutto si è svolto correttamente dovrebbe essere spuntata la cartella arduino-1.8.13


• Una volta estratta la cartella consiglio di spostarla nella directory home, di solito si trovano qui i
programmi dell’utente corrente:
mv arduino-1.8.13 ~
• Ora è possibile cancellare l’archivio compresso dalla cartella Download:
rm arduino-1.8.13-linuxarm.tar.xz
• Ora entriamo nella cartella con il comando cd ~/arduino-1.8.13
• All’interno della cartella si trovano i file di Arduino, quello che ci interessa è un file contenente le
istruzioni per la shell per eseguire l’installazione: install.sh. Eseguiamo questo programma come
superutenti:
sudo ./install.sh
• A questo punto l’IDE funziona e può essere avviato sia dal menù principale nella sezione
programming, sia dalla cartella appena creata eseguendo il comando ./arduino
Raspberry Pi – Comunicazione Arduino
È possibile comunicare con un Arduino anche utilizzando la macchina virtuale!
1. Per fare questo collegare l’Arduino al PC.

2. In seguito aprire VirtualBox e cliccando con tasto destro sull’icona della macchina virtuale del
Raspberry pi selezionare Impostazioni e poi USB

3. Dal menù aggiungere un nuovo dispositivo e selezionare Arduino Uno, poi premere Ok per
confermare.
Raspberry Pi – Comunicazione Arduino
Verificare il collegamento

• Per verificare se il dispositivo è stato correttamente riconosciuto (sia per il Raspberry sia per la
macchina virtuale) utilizzare il comando lsusb
Raspberry Pi – Comunicazione Arduino
Riconoscere la porta seriale

Per sapere a quale porta seriale è connesso l’Arduino utilizzare il comando


sudo dmesg | grep tty
Raspberry Pi – Comunicazione Arduino
Riconoscere la porta seriale

Per sapere a quale porta seriale è connesso l’Arduino utilizzare il comando


sudo dmesg | grep tty

dmesg serve a stampare tutti gli eventi recenti del sistema operativo
grep tty dice al sistema di cercare nell’output del comando precedente (dmesg) tutte le righe che
contengono la stringa ‘tty’

Nel caso in esempio vediamo che la porta seriale dell’Arduino è la porta ttyACM0 (sarebbe
l’equivalente di una COM1 su windows)

Attenzione! Se rimuovete il dispositivo mentre la comunicazione è attiva il sistema potrebbe


assegnargli una porta diversa alla connessione!
Raspberry Pi – Comunicazione Arduino
Comunicazione seriale con Python

• La comunicazione seriale si può gestire con python utilizzando la libreria pyserial


(https://pypi.org/project/pyserial/ )

• Per verificare il funzionamento della libreria pyserial realizzare uno script con il seguente
contenuto:
Raspberry Pi – Comunicazione Arduino

Comunicazione seriale con Python

• Il risultato dovrebbe essere come il seguente (non considerate la riga con ttyAMA0)

• Questo risultato significa che la libreria ha correttamente riconosciuto Arduino sulla porta seriale
Raspberry Pi – Comunicazione Arduino
Pyserial – breve guida

• Per inizializzare la comunicazione seriale la prima cosa da fare è creare un oggetto Serial tramite il
costruttore:
s_obj = serial.Serial(port_name, baudrate=9600)

• Port_name è il nome della porta seriale a cui è collegato arduino, quello che ci ha restituito il
file precedente (/dev/ttyACM0 nel mio caso)
• Baudrate deve essere lo stesso impostato su Arduino altrimenti la comunicazione seriale non
sarà corretta
• La porta verrà automaticamente aperta quando viene creata
• Per conoscere il numero di byte in attesa di essere letti e presenti sulla seriale si utilizza:
s_obj.in_waiting
• ATTENZIONE! Questo è un attributo (quindi una variabile) e non un metodo (quindi una
funzione), è corretto utilizzarlo senza parentesi alla fine!
Raspberry Pi – Comunicazione Arduino
Pyserial – breve guida

• Per leggere dalla porta seriale che è stata aperta un numero di byte pari alla size specificata si può
utilizzare:
s_obj.read(size)
• Questa chiamata attende fino a che non saranno presenti abbastanza byte da leggere sulla
seriale, a meno che non venga impostato un timeout. Fate attenzione perché stiamo parlando di
BYTE e non di CARATTERI
• Per scrivere sulla seriale invece si utilizza la funzione:
s_obj.write(data)
• ATTENZIONE! Data deve essere in formato bytes e NON una stringa
Raspberry Pi – Comunicazione Arduino
Esempio

• Creare un programma per Arduino che scriva «Hello there!» durante la funzione di setup

• Prova a leggere il messaggio con python da terminale


• Attenzione! Chiudere il monitor seriale di Arduino prima di provare altrimenti questo interferisce
con la lettura del seriale
Raspberry Pi – Comunicazione Arduino
Codifica ASCII

• Traduce in modo univoco i valori di byte da 0x20 a 0x7E (32 – 126)

Dec 72 101 108 108 111 32 119 111 114 108 100 33
Hex 0x48 0x65 0x6c 0x6c 0x6f 0x20 0x77 0x6f 0x72 0x6c 0x64 0x21
H e l l o w o r l d !

• Il problema è che questa codifica ha un numero limitato di caratteri e permette quindi di scrivere
solo le cifre arabe, i simboli di base e le lettere utilizzate in inglese (niente accenti e niente
caratteri non presenti nell’alfabeto inglese). Per questo, nel tempo sono state create altre
codifiche.
Raspberry Pi – Comunicazione Arduino
Codifica UTF-8

• È una delle codifiche più utilizzate. Questa codifica permette di scrivere tutti i caratteri in latino e
latino esteso, i simboli e i caratteri di tutti i linguaggi alfabetici (Greco, Cirillico, Arabo …) A
seconda del carattere rappresentato esso può occupare anche più di un byte.

Dec 99 105 195 178 32 195 168 32 117 116 102 45 56

Hex 0x63 0x69 0xc3b2 0x20 0xc3a8 0x20 0x75 0x74 0x66 0x2d 0x38
c i ò è u t f - 8

• Come si può vedere alcuni caratteri occupano 2 byte!


Raspberry Pi – Comunicazione Arduino
Byte in Python

• I byte si possono dichiarare mettendo una b davanti ad una stringa a patto che contenga solo
caratteri ascii:
my_bytes = b’ciao_ciao’ my_bytes = b’ciaoèciao’

• Oppure è possibile utilizzare il metodo encode() specificando il formato, per esempio utf-8
my_bytes = ’ciao_ciao’.encode(‘utf-8’)
my_bytes = ’ciaoèciao ’.encode(‘utf-8’)

• Per trasformare correttamente una sequenza di byte in una stringa utilizzare il metodo decode()
sempre specificando la codifica da utilizzare
print(my_bytes.decode(‘utf-8’)) #-> ciao è ciao
Raspberry Pi – Comunicazione Arduino
Da Byte a String

• Lungo le connessioni della seriale transitano quindi solo byte, per poterli trasmettere e ricevere
correttamente sarà necessario codificarli tramite encode prima dell’invio e poi decodificarli
appena dopo averli ricevuti
decode

Bytes String
encode
Raspberry Pi – Comunicazione Arduino
Scambio messaggi con Arduino
• Proviamo ora a scrivere un messaggio ad Arduino, facciamo un modo che Arduino legga il nostro
messaggio e risponda con lo stesso seguito da «ricevuto». Ovviamente non possiamo utilizzare il
monito seriale dell’IDE altrimenti sarà lui a leggere i dati e non il nostro script python!
Inizializzo una stringa vuota che riceve il messaggio

Inizializzo la stringa che contiene la scritta « ricevuto»

Inizializzo la seriale e abbasso il timeout


(di default è 1000 ms)

Se ci sono dati disponibili nel buffer di ricezione


leggo tutti i dati in arrivo e li salvo in inMessage,
poi invio la risposta
Raspberry Pi – Comunicazione Arduino
Scambio messaggi con Arduino
• Dopo avere chiuso il monitor seriale dell’IDE con l’interprete python testiamo la comunicazione

Appena la porta viene aperta


non ci sono dati in arrivo

Scrivo un messaggio dopo


averlo codificato in byte

Ora ci sono 15 byte da leggere,


Arduino ha risposto

Stampo i dati letti dalla seriale


dopo averli decodificati in
stringa
Raspberry Pi – Comunicazione Arduino
Esercizio 1
• Creare un sistema che legga i dati da una porta digitale ogni secondo
e li salvi in un file csv, inoltre deve mostrare a schermo la media delle
letture
• Collegare un potenziometro all’uscita analogica 0 dell’arduino come
si vede in figura
• Utilizzare la funzione analogRead(PORT) per leggere il dato del
potenziometro
• Utilizzare la funzione map per convertirlo da una lettura analogica a
10bit (da 0 a 1023) a un valore in millivolt (da 0 a 5000)
• Un file csv invece è un file di testo che contiene una determinata
formattazione nella prima riga e tutte le righe successive devono
seguire quella formattazione
Raspberry Pi – Comunicazione Arduino
File CSV

• CSV è l’acronimo di Comma Separated Values, è infatti un file di testo che utilizza le virgole per
separare i dati all’interno di celle diverse in una tabella
• La prima riga contiene l’intestazione, quindi nel nostro caso sarà:

• Mentre tutte le righe successive conterranno sulla prima colonna il tempo in secondi a cui è
avvenuta la lettura e sulla seconda colonna (separata da una virgola) il valore di millivolt letto
Raspberry Pi – Comunicazione Arduino
Parte Arduino Leggo i segnali sulla porta A0

Converto la lettura analogica in un segnale in millivolt

Invio il dato come stringa


Raspberry Pi – Comunicazione Arduino
Parte Raspberry Pi

Apro il file in scrittura, questo


significa che ogni volta che avvio il
programma sovrascrivo il file
precedente

Metto i titoli della tabella in csv


Raspberry Pi – Comunicazione Arduino
Se ci sono dati da leggere Leggo tutti i dati in arrivo e li converto in
attendo un po’ così intanto una stringa
Parte Raspberry Pi
arrivano tutti
Converto la stringa in un intero

Calcolo la media come somma di tutti


i dati ricevuti fratto il numero di dati
ricevuti

Calcolo il tempo come differenza


tra il tempo all’avvio del
programma e il tempo attuale

Scrivo tempo e
tensione nel file
Quando viene premuto ctrl+c csv
chiudo il file e la porta seriale
prima di uscire
Raspberry Pi – Comunicazione Arduino
File voltage_log.csv
Se il programma è stato fatto correttamente, una volta chiuso, nella stessa cartella è possibile
trovare il file voltage_log.csv che, se aperto con un editor di testo ha questa struttura:
Raspberry Pi – Comunicazione Arduino
File voltage_log.csv
La particolarità dei file csv è che possono essere aperti con un foglio di calcolo e quindi utilizzati per
calcoli e grafici (nell’immagine lo stesso file aperto con LibreOffice Calc e con un grafico generato):
Raspberry Pi – Comunicazione Arduino
Funzione millis()

• La funzione millis() di Arduino permette di conoscere esattamente quanto tempo (in


millisecondi) è trascorso da quando è stato acceso il microcontrollore
• Il valore restituito da millis() è da considerarsi un unsigned long e può quindi «contare» fino a
4.294.967.295 (232 -1, un long sono 4 byte), corrisponde a circa 50 giorni e quindi andrà bene se
non prevedete di utilizzare la funzione in un progetto che dovrà rimanere attivo per un periodo
così lungo
• Diversamente dalla funzione delay(ms) non sospendiamo il programma per un numero
determinato e fisso di millisecondi ma possiamo decidere quanto aspettare dall’ultimo evento
che si è verificato
Raspberry Pi – Comunicazione Arduino
Esercizio 2

• Modificare il programma dell’esercizio 1 e, utilizzando la funzione millis(), fare in modo che


l’utente, tramite la Raspberry Pi, possa comunicare all’arduino ogni quanti secondi effettuare la
lettura
Raspberry Pi – Comunicazione Arduino
Parte Arduino Riduco il timeout a 100ms perché devo leggere pochi
dati dalla seriale e quindi sono sufficienti

Visto che Arduino deve eseguire 2 task


contemporaneamente (leggere i dati, controllare i
messaggi in seriale) non si può più utilizzare il delay…
Passo alle funzioni temporizzate con millis()

Devo attendere che sia trascorso il tempo richiesto


prima di eseguire la lettura successiva
E devo poi salvarmi il tempo a cui è avvenuta la lettura
per misurare quanto trascorre prima della lettura
successiva

Se ci sono dati in arrivo li converto in interi e poi faccio


un controllo in modo che non vengano inseriti valori
nulli, l’intervallo di tempo minimo tra una lettura e la
successiva è di 1 secondo
Raspberry Pi – Comunicazione Arduino
Parte Raspberry Pi Anche nella parte di Raspberry Pi devono essere
eseguiti due task contemporaneamente:
1. Legge i valori dalla seriale, calcola la media e li
salva
2. Attende l’input dell’utente e invia i valori

Importo quindi la libreria threading


per gestire i due compiti
separatamente
Raspberry Pi – Comunicazione Arduino
Decido di eseguire in background la parte di lettura dalla seriale e scrittura
Parte Raspberry Pi su file. Per questo creo una funzione che sia eseguita da un thread separato.

Se in una funzione voglio SCRIVERE delle variabili globali devo


dichiarale global all’inizio della funzione. Se devo leggerle o
utilizzare dei metodi ad esse associati non c’è bisogno

Notate che non sono presenti


print in questa funzione
perché il compito di
comunicare con l’utente lo
lasciamo al thread principale
(se entrambi i thread
scrivessero sul terminale si
rischia di far confusione)
Raspberry Pi – Comunicazione Arduino
Definisco il thread e poi lo faccio partire
Parte Raspberry Pi

Se una stringa è molto lunga posso spezzarla su più righe


utilizzando un backslash (‘\’) prima dell’ «a capo»

Se l’utente preme invio senza scrivere nulla (quindi input


ritorna una stringa vuota) assumo che voglia vedere il
numero di misure e la media

Se l’utente scrive una stringa non nulla provo a


convertirla in un numero per vedere se è un valore
valido. In questo modo evito di mandare delle
stringhe sbagliate all’Arduino i cui errori sono sempre
difficili da rilevare
Raspberry Pi – Comunicazione Arduino
Parte Raspberry Pi

Il metodo .join() del thread dice alla funzione


principale di fermarsi ed attendere che il thread sia
finito. Se non uso questa istruzione rischio di chiudere
la porta seriale o il file mentre il thread le sta ancora
usando
Raspberry Pi – Comunicazione Arduino
Trasferire più valori

• Finora abbiamo visto come è possibile trasmettere un singolo valore tra due dispositivi. MA se
occorresse tramettere più di un valore come si può fare? Per esempio se devo trasmettere 1 int
(1234) 1 float (23.5) e 1 bool (True)

1. Utilizzare un delimitatore per i valori

Dec 49 50 51 52 36 50 51 46 53 36 49
Hex 0x31 0x32 0x33 0x34 0x24 0x32 0x33 0x2e 0x35 0x24 0x31
1 2 3 4 $ 2 3 . 5 $ 1

• Posso poi riottenere i valori che cercavo utilizzando una funzione di split sulla stringa con ‘$’
come separatore
Raspberry Pi – Comunicazione Arduino
2. Posso utilizzare un tipo di serializzazione standard, per esempio JSON
Dec 91 49 50 51 52 44 32 50 51 46 53 44 32 116 114 117 101 93
Hex 0x5b 0x31 0x32 0x33 0x34 0x2c 0x20 0x32 0x33 0x2e 0x35 0x2c 0x20 0x74 0x72 0x75 0x65 0x5d
[ 1 2 3 4 , 2 3 . 5 , t r u e ]
• Questa soluzione è molto più flessibile e il parsing può essere delegato a librerie specializzate.
L’unico lato negativo è che può aumentare il numero di byte trasmessi e il tempo di
codifica/decodifica.

3. Si può utilizzare una spaziatura fissa per i valori


Dec 48 49 50 51 52 50 51 46 53 48 48 49
Hex 0x30 0x31 0x32 0x33 0x34 0x32 0x33 0x2e 0x35 0x30 0x30 0x31
0 1 2 3 4 2 3 . 5 0 0 1

• Posso sapere in anticipo quanti caratteri utilizza ogni numero, il problema è che il limita molto i
valori numerici che posso tramettere
Raspberry Pi – Comunicazione Arduino

4. Utilizzare una spaziatura fissa ma trasmette valori binari


Dec 210 4 0 0 188 65 01
Hex 0xd2 0x04 0x00 0x00 0xbc 0x41 0x01

• Questa è sicuramene la soluzione più efficiente in quanto i valori occupano esattamente il


minimo numero di byte possibile.
• Il problema è che richiede la traduzione dei valori in linguaggio binario (su Python si può usare
https://docs.python.org/3.7/library/struct.html ) e che rende il messaggio non leggibile da un
umano, quindi risulta difficile il debug
Raspberry Pi – Comunicazione Arduino
Utilizzare Json per Arduino
• Per prima cosa installiamo
la libreria ArduinoJson
(https://arduinojson.org/ )
per serializzare in formato
JSON dal Library manager
Raspberry Pi – Comunicazione Arduino
Esercizio 3

• Modificare il programma precedente in modo che siano inviati dati di 2 porte analogiche.
• Le funzioni principali JSON che andranno utilizzate su Arduino:
1. DynamicJsonDocument var(size): crea un oggetto di tipo DynamicJsonDocument che
deve essere utilizzato dalle funzioni della libreria JSON ma di fatto si comporta come un
dizionario Python
2. serializeJson(var, Serial): var deve essere del tipo DynamicJsonDocument e tramite
questa funzione è possibile inviare i dati contenuti nel dizionario sulla seriale
• Le funzioni principali JSON che andranno utilizzate su Raspberry Pi:
1. json.loads(input_string): deserializza la stringa appena ricevuta dalla seriale utilizzando il
formato json e permette di ottenere un dizionario con i dati ricevuti
Raspberry Pi – Comunicazione Arduino
Parte Arduino Aggiungo le definizioni della seconda porta

Creo un oggetto DynamicJsonDocument

Creo un «dizionario» nell’oggetto doc

Lo serializzo in JSON e lo invio via seriale


Raspberry Pi – Comunicazione Arduino
Parte Raspberry Pi

Rispetto allo script precedente alcuni valori


sono diventati liste

Ho aggiunto una colonna relativa alla seconda porta


Raspberry Pi – Comunicazione Arduino
Parte Raspberry Pi

Deserializzo la stringa in formato JSON, ottengo un


dizionario

A seconda della key assegno i valori nella


posizione 0 o 1 della lista

Con questo ciclo aggiorno le variabili globali


Raspberry Pi – Comunicazione Arduino
File voltage_log.csv
Raspberry Pi – Comunicazione Arduino
Esercizio 4

• Creare un programma che permetta di attivare le porte dalla 2 alla 13 e una minima interfaccia
testuale per comandarlo.
• Le funzioni principali JSON che andranno utilizzate su Arduino:
1. DeserializeJson(var, Serial): è l’opposto di serializeJson quindi permette di riceve i dati
dalla seriale e salvarli nella variabile var di tipo DynamicJsonDocument
2. DeserializationError e: è la variabile che ritorna la funzione DeserializeJson e permette di
controllare se tutto è andato a buon fine, ovvero se e assume il valore
DeserializationError::Ok
• Le funzioni principali JSON che andranno utilizzate su Raspberry Pi:
1. json.dumps([data1,data2…], ): converte i dati in json, questo andrà poi codificato in utf-8
per inviarlo sulla seriale
Raspberry Pi – Comunicazione Arduino
Parte Arduino Creo un elenco di porte valide così evito di fare
dei danni per errore

Con questo comando acquisisco i dati da


seriale e li deserializzo

Se e ha un valore diverso dalla costante


DeserializationError::Ok vuol dire che non è
riuscita ad interpretare la stringa

Se il valore della porta fornito è tra quelli


validi posso accendere il led
Raspberry Pi – Comunicazione Arduino
Parte Raspberry Pi
Nella prima parte metto un programma di benvenuto, inizializzo la seriale e
attendo 2 s che Arduino si riavii

Il programma prosegue solo se il valore in input è intero (quindi solo


se la conversione non dà errore) e se è 1 o 0

Posso andare a capo all’interno di una parentesi

Se il vlaore ricevuto è valido si converte in


json, poi in bytes e infine si invia tramite
seriale
Raspberry Pi – Comunicazione Arduino
Parte Raspberry Pi
Nella prima parte metto un programma di benvenuto,
inizializzo la seriale e attendo 2 s che Arduino si riavii

Il programma prosegue solo se il valore in input è intero


(quindi solo se la conversione non dà errore) e se è 1 o 0

Posso andare a capo all’interno di una parentesi

Se il valore ricevuto è valido, lo si


converte prima in json, poi in bytes e
infine si invia tramite seriale grazie
alla funzione write

Potrebbero piacerti anche