Sommario:

Invia dati numerici da un Arduino all'altro: 16 passaggi
Invia dati numerici da un Arduino all'altro: 16 passaggi

Video: Invia dati numerici da un Arduino all'altro: 16 passaggi

Video: Invia dati numerici da un Arduino all'altro: 16 passaggi
Video: Arduino invia dati a un'applicazione Processing - #70 2024, Dicembre
Anonim
Invia dati numerici da un Arduino a un altro
Invia dati numerici da un Arduino a un altro

introduzione

di David Palmer, CDIO Tech. all'Aston University.

Hai mai avuto bisogno di inviare dei numeri da un Arduino all'altro? Questo Instructable mostra come.

Puoi facilmente testare che funzioni semplicemente digitando una stringa di numeri da inviare al terminale del monitor seriale e vedere i numeri tornare su un secondo monitor seriale collegato al secondo Arduino. Puoi persino utilizzare un collegamento Bluetooth.

Cosa fa

Due programmi Arduino (schizzi in Arduino parlano) devono essere sviluppati, uno un programma Master per connettersi al computer host che esegue l'Arduino Serial Monitor, uno per fungere da Slave per ricevere il messaggio seriale dal Master, decodificarlo e rispedirlo. Lo slave è facoltativamente in grado di visualizzare i numeri con cui ha a che fare su un secondo monitor seriale di IDE, nel caso in cui si desideri utilizzarlo. Potrebbe essere utile in primo luogo far funzionare le cose e aiutarti se decidi di apportare modifiche ai programmi per soddisfare le tue esigenze.

Attrezzatura

  • 2 Arduino
  • 2 cavi USB
  • cavi patch (come richiesto)
  • 1 PC/laptop caricato con Arduino IDE (disponibile come download gratuito dal sito Arduino.cc)

Passaggio 1: configurazione: prima configurazione dell'hardware

Configurazione: prima configurazione dell'hardware
Configurazione: prima configurazione dell'hardware
Configurazione: prima configurazione dell'hardware
Configurazione: prima configurazione dell'hardware

Collega i 2 Arduino a 2 porte USB sul tuo computer.

Suggerimento, è una buona idea etichettarli come M e S (master e slave) in modo da non entrare in confusione in seguito (come mostrato nelle 2 foto qui).

Passaggio 2: configurazione - Imposta il tuo schermo

Configurazione - Imposta il tuo schermo
Configurazione - Imposta il tuo schermo

La cosa migliore è impostare lo schermo in modo da avere

  • l'IDE caricato con il programma Master a sinistra e
  • quello con lo Schiavo a destra.

Tieni anche i monitor seriali per Maser e Slave a sinistra e a destra, come mostrato nella schermata qui.

Passaggio 3: impostare l'estremità principale, quindi connettersi insieme - Parte 1

Imposta l'estremità principale, quindi connettiti insieme - Parte 1
Imposta l'estremità principale, quindi connettiti insieme - Parte 1

Quando si imposta il monitor seriale finale master per inviare due numeri, è necessario utilizzare sempre l'inizio e la fine, i caratteri delimitatori e il carattere separatore virgola come si vede qui.

Ora devi collegare i 2 Arduino insieme tramite seriale. Questo viene fatto con due cavi patch.

Ho usato il verde e il giallo

  • Prendi prima il giallo, questo deve collegarsi a D6 in un Arduino e D7 nel secondo
  • Poi il contrario per il filo verde, D7 sul primo e D6 sul secondo Arduino.

In alternativa, se hai qualcosa a disposizione come una coppia di moduli Bluetooth, come gli HC-05, anche questi funzioneranno per darti esattamente lo stesso effetto dei fili sopra.

Passaggio 4: impostare l'estremità principale, quindi collegare insieme - Parte 2

Imposta l'estremità principale, quindi connettiti insieme - Parte 2
Imposta l'estremità principale, quindi connettiti insieme - Parte 2
Imposta l'estremità principale, quindi connettiti insieme - Parte 2
Imposta l'estremità principale, quindi connettiti insieme - Parte 2

Stiamo facendo uso della libreria Software Serial. Ulteriori informazioni sono disponibili con questo link

Puoi vederlo richiamato sulla riga 7 di uno dei programmi. Configura i pin digitali 7 e 6 come TX e RX (trasmissione e ricezione). Questo è il modo in cui i dati viaggeranno dal Master Arduino attraverso il filo verde nello Slave e, quando il programma Slave nel secondo Arduino avrà terminato il suo lavoro, attraverso il filo giallo. Nella parte inferiore della stessa illustrazione (nella finestra Serial Monitor) puoi vedere che i dati che abbiamo trasmesso hanno ora completato con successo il ciclo descritto qui e sono tornati nel PC mentre la coppia di numeri interi è ben separata.

Passaggio 5: Panoramica degli schizzi/programmi - Struttura del programma

Panoramica degli schizzi / programmi - Struttura del programma
Panoramica degli schizzi / programmi - Struttura del programma
Panoramica degli schizzi / programmi - Struttura del programma
Panoramica degli schizzi / programmi - Struttura del programma

Layout Come in tutti gli sketch Arduino ci sono 3 parti fondamentali:

  • Le Dichiarazioni
  • Il set up
  • Il ciclo principale

Come spesso accade, qui abbiamo fatto uso di una quarta sezione che è l'aggiunta di 'Funzioni'. Se non hai familiarità con l'utilizzo di Funzioni puoi cercare su Google "Funzioni Arduino" e troverai siti di spiegazione come l'esempio in questo link: www.tutorialspoint.com/arduino/arduino_functions…..

Abbiamo anche fatto uso di schede per separare il programma in blocchi più gestibili.

I tre blocchi che abbiamo usato possono essere visti nella parte superiore di ogni illustrazione delle finestre IDE sopra:

  • simpleRxTx0330Master
  • Comune
  • Appunti

Questi sono in realtà file separati all'interno della cartella del programma, come puoi vedere in questa vista di Windows Explorer dei file del programma Slave.

C'è una buona ragione per cui abbiamo fatto questo.

  • Mentre stavamo costruendo il programma, ci siamo resi conto che la maggior parte del programma per il Master era lo stesso dello Slave.
  • Abbiamo finito per inserire tutte le parti comuni in una scheda, che abbiamo quindi chiamato "comune", e poi ogni volta che abbiamo eseguito il debug di una parte (l'abbiamo testata e siamo stati soddisfatti che funzionasse bene) abbiamo semplicemente copiato e incollato l'intera scheda di fronte da Master a Slave, o viceversa.
  • Anche le schede delle note sono identiche, poiché il design è generico.

Nessuna delle funzioni viene chiamata da setup, sono tutte chiamate da loop, quindi le abbiamo create dopo il setup ma prima di loop.

Passaggio 6: design dall'alto verso il basso

È una buona idea progettare il tuo schizzo partendo da una definizione di ciò che vuoi fare.

Una volta ottenuto questo, puoi iniziare a fare in modo che lo schizzo faccia quelle cose. Generalmente se c'è un dettaglio che non sai ancora come fare, trasformalo in una funzione e lascia la creazione della funzione a più tardi.

Questo segue la filosofia del buon design, insegnata in molte università, chiamata CDIO (Se non conosci già questo puoi Google e trovare siti per spiegarlo come: https://www.cdio.org/s.) Questo in pratica dice: non iniziare il design prima di aver chiarito il concetto. Non avviare l'implementazione finché non hai chiarito il design. Non aspettarti che funzioni prima di aver chiarito l'implementazione. C prima, poi D, I e O. Ad ogni fase successiva si itera (torna indietro intorno al ciclo(i), quindi una volta che sei soddisfatto del tuo ciclo di progettazione iniziale e controlla che soddisfi ancora il Concetto, e aggiorna la C se è necessario. E così via, quindi anche quando devi Operare, torna indietro fino all'inizio e guarda di nuovo come sta guardando la C ora, poi la D e I, e fai e controlla tutto modifiche se necessario. Con gli schizzi di programmazione questo funziona allo stesso modo se si progetta dall'alto verso il basso.

Passaggio 7: concetto e design - Parte 1

Concetto e design - Parte 1
Concetto e design - Parte 1
Concetto e design - Parte 1
Concetto e design - Parte 1

Il Concetto qui assomiglia ai requisiti di contorno indicati nella scheda "Note".'

Il design potrebbe sembrare una prima versione del ciclo, che corrisponde alla scheda delle note e potrebbe assomigliare a quello che vedi in questa figura

Guarda come mi piace iniziare effettivamente CTRL-C copiando prima i commenti nella testa del ciclo, quindi inizia a riempire gli spazi vuoti con i comandi che faranno quelle cose.

Questo in realtà viene compilato correttamente come puoi vedere nella parte inferiore dello schermo nella figura. Questo va dallo stadio CDIO D a I, e mentre sviluppiamo il codice è una buona idea continuare a girare intorno a questo ciclo D-I.

Ora è il momento di passare alla fase successiva, c'è un commento che dice che stiamo per: //ricevere qualcosa dall'USB hardware, quindi lo trasmetteremo sul canale seriale del software. Scriviamo questo codice per farlo accadere - le righe da 133 a 138 mostrate qui in evidenziatore giallo

Passaggio 8: concetto e design - Parte 2

Concetto e design - Parte 2
Concetto e design - Parte 2
Concetto e design - Parte 2
Concetto e design - Parte 2

Le prime due funzioni che introduciamo qui sono (recv() e tran() per fare la ricezione dalla porta hardware e la trasmissione alla porta software - quindi chiamandole con i parametri ' hw ' o ' sw ' mostrati.

Oltre a loro, abbiamo aggiunto un test su una variabile globale chiamata newData. Questo è un flag che imposteremo all'interno della funzione " void recv(); ". Quando viene ricevuto un messaggio, questa variabile viene contrassegnata da false a true. Lo facciamo in modo da trasmettere un messaggio solo se ne è stato ricevuto uno (flag ==true) nella riga 134. E una volta che abbiamo trasmesso il nostro messaggio, questo è "fatto", quindi ripuliamo il flag su false nella riga 137.

Di nuovo possiamo controllare la compilazione (da D a I), e questa volta abbiamo un messaggio di errore 'non dichiarato' (mostrato). Questo ci dice che non abbiamo dichiarato recv(); funzione. Abbiamo intenzione di farlo in seguito, quindi solo per ora per permetterci di ottenere una compilazione pulita abbiamo bisogno di creare una funzione fittizia o segnaposto, come mostrato di seguito.

Di nuovo possiamo controllare compile (da D a I), e questa volta abbiamo un altro messaggio di errore 'non dichiarato' per tran(); funzione. Questo richiede la creazione di uno stub simile. Di nuovo possiamo controllare compile (D a I), e questa volta troveremo che funziona perfettamente; Fin qui tutto bene.

Passaggio 9: terminare il ciclo principale: A) Ricezione da USB, B) Ricezione da Arduino slave

Termina il ciclo principale: A) Ricezione da USB, B) Ricezione da Slave Arduino
Termina il ciclo principale: A) Ricezione da USB, B) Ricezione da Slave Arduino
Termina il ciclo principale: A) Ricezione da USB, B) Ricezione da Slave Arduino
Termina il ciclo principale: A) Ricezione da USB, B) Ricezione da Slave Arduino

C'è un ultimo pezzo che abbiamo aggiunto per completare questa parte, che consiste nell'aggiungere del codice di debug.

C'è un altro Instructable sugli schizzi di debug a cui si può fare riferimento per capire cosa abbiamo fatto qui e perché. Fare riferimento a Instructable "Come costruire e testare gli schizzi Arduino finché non funzionano"

Quindi queste righe di debug [136-139 mostrate] vengono aggiunte successivamente nel ciclo principale e, guardate, è possibile testarle nella parte Master rendendo la variabile di debug true e Compiling (I), quindi se colleghi un Arduino che puoi caricare, aprire il monitor seriale e vedere se ciò che ritorna nel monitor seriale è come mostrato qui (vedi che viene aggiunto il messaggio "MODALITÀ DEBUG"?)

Passaggio 10: ricezione e gestione dei dati nello slave Arduino

Ricezione e gestione dei dati nello Slave Arduino
Ricezione e gestione dei dati nello Slave Arduino
Ricezione e gestione dei dati nello slave Arduino
Ricezione e gestione dei dati nello slave Arduino

Ricezione da Slave Arduino

Aggiungere il codice necessario per il secondo canale al loop principale, il ricevitore seriale software come mostrato - righe da 149 a 155.

Riesci a vedere che la struttura è vagamente basata su ciò che abbiamo scritto sopra per il caso Master?

Inoltre vedrai che otteniamo un errore del compilatore, un'altra funzione non dichiarata - questa volta parseData(); - quindi dobbiamo creare uno stub anche per questo, prima di poter eseguire una compilazione di test priva di errori.

Gestire i dati nello Slave Arduino

Aggiungi il codice del ciclo principale richiesto per Arduino se è configurato come dispositivo Slave come mostrato - righe da 163 a 174. Riesci a vedere che la struttura è molto simile a quella del primo canale?

E dovresti scoprire che questa volta viene compilato perfettamente.

Passaggio 11: scrivere la funzione di ricezione

Scrivi la funzione di ricezione
Scrivi la funzione di ricezione

La funzione Receive - void recv(char from){} - ha due compiti principali.

1 per ricevere una stringa di caratteri dal canale USB, e

2 per riceverne uno dal canale Arduino to Arduino.

Per il primo sarà necessario utilizzare perché utilizza l'UART hardware integrato di Arduino e per il secondo utilizzando la libreria Arduino standard: software UART.

Quando iniziamo ad aggiungere codice a una funzione - per creare una funzione che fa qualcosa, invece di solo uno stub - dobbiamo ricordarci di rimuovere o commentare lo stub che sta sostituendo. Altrimenti otteniamo un errore di compilazione: refefintion of 'void lrec(char)'.

Prova a ottenere l'errore, quindi prova uno dei modi suggeriti sopra per eliminarlo.

Inizia con una funzione che assomigli a quella che mostriamo qui dalle righe da 75 a 88 in giallo.

Ormai sai che avendo il codice dovrai provare l'operazione di compilazione. Ti dà un errore, come quelli che abbiamo avuto in precedenza, del tipo: nome funzione non dichiarato in questo ambito. Avremo bisogno inizialmente di un altro stub per consentirci di compilare oltre questo errore, quindi aggiungine uno come prima e assicurati di poter ottenere una compilazione senza errori.

Ora diamo un'occhiata al codice che abbiamo scritto per la funzione recv().

È abbastanza pulito, puoi vedere l'uso della condizione 'se' per produrre le due parti della funzione di cui sopra.

Il codice all'interno della parte "sw" e della parte "hw" è della stessa forma e lo descriverò qui.

La prima della coppia di righe in ogni caso è l'inizio di un ciclo while. Se non hai familiarità con while puoi cercarlo nel sito Arduino.cc/Reference per la spiegazione e gli esempi. Qui aspettiamo "mentre" la funzione "Serial" incorporata non ha ricevuto alcun carattere e perché la variabile newData è stata disattivata (ovvero la condizione newData == false è vera). Non appena un carattere, o più di un carattere, viene ricevuto, il while passerà alla seconda riga di questa coppia. Ciò chiamerà quindi recAstringChar(char); funzione per gestire il carattere corrente. Questa coppia di righe si alternerà quindi finché (o finché) ci sono ancora caratteri che devono essere ricevuti. Una volta che tutti sono finiti, lo stato while termina, consentendo la fine del livello if o else successivo e, a sua volta, consentendo il rec(char); funzione per terminare. Così ora è stato ricevuto un messaggio completo.

Passaggio 12: scrivere la funzione secondaria di ricezione - Parte 1

Scrivi la sottofunzione Ricevi - Parte 1
Scrivi la sottofunzione Ricevi - Parte 1
Scrivi la sottofunzione Ricevi - Parte 1
Scrivi la sottofunzione Ricevi - Parte 1

Ora dobbiamo scrivere la funzione chiamata recAstringChar(char);. Vedete dal commento alla riga 50 qui in alto, che il suo compito è aggiornare due buffer con copie del messaggio seriale in arrivo. [Si è scoperto mentre stavo cercando di far funzionare tutto questo che una cosa che ho imparato è che avevo bisogno di due buffer diversi - o almeno quello era il modo più semplice per aggirare alcuni problemi, quindi si è evoluto nel bisogno di 2 buffer, quindi Li ho appena creati.] Ho chiamato un buffer: receiverData e l'altro: sharedChars.

I buffer sono variabili globali, quindi sono dichiarati a livello di modulo, vedere le righe 9 e 10 della scheda comune. Ci sono altre variabili dichiarate all'interno di questa funzione che quindi hanno un ambito locale, mostrato qui nelle righe 51-54. Questo non è il luogo per spiegare le differenze tra globali e locali, ma ci sono maggiori informazioni su questo in https://www.arduino.cc/glossary/en/ sotto Locale e Globale.

Puoi anche scoprire tutto sui tipi di dati e sui modificatori di tipo: static, boolean, byte, const, char in https://www.arduino.cc/reference/en/#variables, mostrato qui.

Il flusso principale del programma in questa funzione è controllato da if alla riga 56 qui, e il suo corrispondente else alla riga 74. Si tratta di due scenari

a) [dalla riga 74 in poi] all'inizio del messaggio ricevuto. Questo accade quando viene individuato lo startMarker - questo è stato definito come il carattere ' < ', motivo per cui ogni volta che testiamo lo schizzo iniziamo sempre la nostra stringa con quel carattere. In caso contrario, nulla verrà elaborato come ricevuto, tutto verrà ignorato proprio come se stessimo digitando un'assurdità al prompt della tastiera "Monitor seriale".

b) [righe da 56 a 73] che riceve tutti gli altri caratteri, qualunque essi siano, ma trattano i caratteri solo dopo che si è verificato un inizio valido (è stato ricevuto un '>' come in a) sopra.)

In queste righe (da 74 a 78) mettiamo quello ricevuto < in uno dei buffer (receivedData[0]) ma non nell'altro. Regoliamo il puntatore del buffer (variabile: char ndx) in modo che punti alla successiva posizione del buffer di riserva (receivedData[1]) utilizzando il comando post-incremento (++) nella riga ndx++;, e impostiamo il flag in progress su true.

Il flusso del programma in questa parte della funzione è controllato da if alla riga 57 qui, e il suo corrispondente else alla riga 65. Si tratta di due scenari

a) [dalla riga 65 in poi] al termine del messaggio ricevuto. Questo accade quando viene individuato endMarker, definito come >, motivo per cui ogni volta che testiamo il nostro schizzo terminiamo sempre la nostra stringa con quel carattere. Una delle cose che accade quando viene ricevuto il carattere finale è che il flag globale (tecnicamente variabile) newData è impostato su true proprio quando la funzione termina, in modo che la funzione che ha chiamato la nostra sottofunzione (la funzione chiamante: recv(char);) può "sapere" che sono stati ricevuti nuovi dati validi completi.

b) [righe da 57 a 64] che riceve tutti gli altri caratteri, qualunque essi siano. Si limita a parcheggiarli ordinatamente in file in entrambi i buffer.

Passaggio 13: scrivere la funzione secondaria di ricezione - Parte 2

Scrivi la sottofunzione Ricevi - Parte 2
Scrivi la sottofunzione Ricevi - Parte 2
Scrivi la sottofunzione Ricevi - Parte 2
Scrivi la sottofunzione Ricevi - Parte 2

Potrebbe essere utile fornire un esempio di come appaiono i 2 buffer quando sono stati popolati. Se dovessimo digitare invio, i buffer avrebbero i caratteri mostrati in essi:

Quindi ora puoi vedere che abbiamo un buffer che è esattamente tutti gli stessi caratteri che abbiamo digitato per la prima volta e un buffer che ha solo i due valori e una virgola di separazione. Ora abbiamo del codice che può ricevere i caratteri che digitiamo sulla tastiera del Serial Monitor, possiamo passare dalla fase I del CDIO alla O, digitando alcune stringhe e vedendo cosa succede. Carica il codice sul Master Arduino, apri Serial Monitor e prova a digitare qualcosa di valido, come invio. Ricevi un'eco sullo schermo del monitor seriale come quello mostrato qui?

Passaggio 14: scrivere le funzioni di trasmissione e analisi

Scrivi le funzioni di trasmissione e analisi
Scrivi le funzioni di trasmissione e analisi
Scrivi le funzioni di trasmissione e analisi
Scrivi le funzioni di trasmissione e analisi

Primo per la trasmissione

Quindi ora che abbiamo ricevuto una stringa, possiamo scrivere la funzione di trasmissione: tran(char); per sostituire il suo stub. Questo ci permetterà di inviare una stringa dal Master allo Slave Arduino, quindi assicurati che entrambi i dispositivi siano collegati e collegati insieme per testare questa nuova funzionalità.

Inserisci questa funzione come mostrato qui nelle righe da 117 a 133. Come riconoscerai, ha due parti, una da trasmettere al canale USB (hardware UART) e una da trasmettere all'altro Arduino (software UART). Questo dovrebbe compilare l'errore -free, e puoi caricare immediatamente lo schizzo e vedere cosa succede. Questa volta manderò. Ottieni il risultato mostrato?

La schermata è interessante perché la stringa ricevuta… dovrebbe apparire corretta come prima e la stringa trasmessa… ora dovrebbe apparire corretta. Tuttavia, tieni presente che la conversione di numeri interi non ha funzionato. C'è ancora un po' più di codice da aggiungere per farlo funzionare.

Passaggio 15: scrivere le funzioni di trasmissione e analisi

Scrivi le funzioni di trasmissione e analisi
Scrivi le funzioni di trasmissione e analisi
Scrivi le funzioni di trasmissione e analisi
Scrivi le funzioni di trasmissione e analisi

Allora per il Parse

Questo è un pezzo di codice che analizza la stringa ricevuta per recuperare le stringhe parziali numeriche e le converte in valori interi. È il vuoto parseData(); funzione del ciclo principale

Sostituisci lo stub di analisi con il codice mostrato nelle righe 98 - 113. Caricalo e vediamo se il problema che stavamo riscontrando con i 2 valori interi è stato risolto. Proviamo.

Sì, funziona, come mostrato, gli interi trovati sono 49 e 98.

Fase 16: Finale

Finale!
Finale!

Questi dati hanno fatto il giro del loop dal PC attraverso il Master attraverso lo slave e di nuovo attraverso il Master di nuovo al PC. Con la versione finale di common caricata su entrambe le estremità Master e Slave e con la modalità di debug disattivata ora, possiamo vedere i dati ricevuti correttamente su entrambe le estremità, come mostrato qui.

Consigliato: