Processi paralleli di Arduino. Arduino: connessione parallela e seriale di dispositivi slave al bus SPI. Controllo di un LED e di un emettitore piezoelettrico utilizzando l'operatore Delay()

In generale, Arduino non supporta la vera parallelizzazione delle attività o il multithreading. Ma è possibile ad ogni ripetizione del ciclo ciclo continuo() istruire il microcontrollore a verificare se è il momento di eseguire qualche attività aggiuntiva in background. In questo caso, all'utente sembrerà che più attività vengano eseguite contemporaneamente.

Ad esempio, facciamo lampeggiare un LED a una determinata frequenza e, allo stesso tempo, emettiamo suoni crescenti e decrescenti come una sirena da un emettitore piezoelettrico. Abbiamo già collegato più di una volta sia il LED che l'emettitore piezoelettrico ad Arduino. Assembliamo il circuito come mostrato in figura.

Se colleghi il LED a un pin digitale diverso da "13", non dimenticare una resistenza di limitazione della corrente di circa 220 ohm.

2 Controllo LED ed emettitore piezoelettrico utilizzando l'operatore ritardo()

Scriviamo uno schizzo come questo e carichiamolo su Arduino.

Const int soundPin = 3; /* dichiaro una variabile con il numero del pin a cui è collegato l'elemento piezoelettrico */ const int ledPin = 13; // dichiara una variabile con il numero del pin del LED configurazione nulla() ( pinMode(soundPin, OUTPUT); // dichiara il pin 3 come output. pinMode(LEDPin, USCITA); // dichiara il pin 13 come output. } ciclo vuoto() (// Controllo del suono: tone(soundPin, 700); // emette un suono con una frequenza di 700 Hz delay(200); tono(soundPin, 500); // ad una frequenza di 500 Hz ritardo(200); tono(soundPin, 300); // ad una frequenza di 300 Hz ritardo(200); tono(soundPin, 200); // ad una frequenza di 200 Hz ritardo(200); // Controllo LED: digitalWrite(ledPin, HIGH); // ritardo luce(200); digitalWrite(LEDPin, BASSO); // disattiva il ritardo(200); }

Dopo averla accesa, è chiaro che lo schizzo non viene eseguito esattamente come ci serve: finché la sirena non sarà completamente operativa, il LED non lampeggerà, ma vorremmo che il LED lampeggiasse durante il suono di una sirena. Qual è il problema qui?

Il fatto è che questo problema non può essere risolto nel solito modo. Le attività vengono eseguite dal microcontrollore in modo rigorosamente sequenziale. Operatore ritardo() ritarda l'esecuzione del programma per un periodo di tempo specificato e fino alla scadenza di questo tempo, i seguenti comandi del programma non verranno eseguiti. Per questo motivo, non possiamo impostare una durata di esecuzione diversa per ciascuna attività nel ciclo ciclo continuo() programmi. Pertanto, è necessario simulare in qualche modo il multitasking.

3 Processi paralleli senza l'operatore "delay()".

Gli sviluppatori di Arduino hanno proposto un'opzione in cui Arduino eseguirà attività pseudo-parallele. L'essenza del metodo è quella con ogni ripetizione del ciclo ciclo continuo() controlliamo se è il momento di far lampeggiare il LED (eseguire un'attività in background) o meno. E se è arrivato, invertiamo lo stato del LED. Questa è una sorta di opzione di bypass dell'operatore ritardo().

Const int soundPin = 3; // variabile con il numero di pin dell'elemento piezoelettrico const int ledPin = 13; // variabile con il numero di pin del LED const long ledInterval = 200; // Intervallo di lampeggio del LED, ms. int ledState = BASSO; // stato iniziale del LED unsigned long previousMillis = 0; // memorizza l'ora della precedente attivazione del LED configurazione nulla() ( pinMode(soundPin, OUTPUT); // imposta il pin 3 come uscita. pinMode(LEDPin, USCITA); // imposta il pin 13 come uscita. } ciclo vuoto() (// Controllo del suono: tone(soundPin, 700); ritardo(200); tono(soundPin, 500); ritardo(200); tono(soundPin, 300); ritardo(200); tono(soundPin, 200); ritardo(200); // LED lampeggiante: // tempo trascorso dall'accensione di Arduino, ms: unsigned long currentMillis = millis(); // Se è giunto il momento di lampeggiare, if (currentMillis - previousMillis >= ledInterval) ( previousMillis = currentMillis; // poi ricorda l'ora corrente if (ledState == LOW) ( // e inverte lo stato del LED ledState = HIGH; ) else ( ledState = LOW; ) digitalWrite(ledPin, ledState); }

Uno svantaggio significativo di questo metodo è che la parte di codice prima del blocco di controllo LED deve essere eseguita più velocemente dell'intervallo di tempo di lampeggiamento del LED "ledInterval". Altrimenti, il lampeggio avverrà meno spesso del necessario e non otterremo l'effetto dell'esecuzione parallela delle attività. In particolare, nel nostro sketch, la durata del cambio del suono della sirena è 200+200+200+200 = 800 ms, e impostiamo l'intervallo di lampeggio del LED a 200 ms. Ma il LED lampeggerà con un periodo di 800 ms, ovvero 4 volte più lungo di quanto impostato.

In generale, se il codice utilizza l'operatore ritardo(), in questo caso è difficile simulare lo pseudo-parallelismo, quindi è consigliabile evitarlo.

In questo caso sarebbe necessario che la centrale di controllo del suono della sirena verificasse anche se è arrivata o meno l'ora, e non utilizzare ritardo(). Ma ciò aumenterebbe la quantità di codice e renderebbe il programma meno leggibile.

4 Utilizzando la libreria ArduinoThread per creare thread paralleli

Per risolvere il problema, utilizzeremo una meravigliosa libreria ArduinoThread, che consente di creare facilmente processi pseudo-paralleli. Funziona in modo simile, ma ti consente di evitare di scrivere codice per controllare i tempi, indipendentemente dal fatto che tu debba eseguire un'attività in questo ciclo o meno. Ciò riduce la quantità di codice e migliora la leggibilità dello schizzo. Controlliamo la libreria in azione.


Prima di tutto, scarica l'archivio della libreria dal sito ufficiale e decomprimilo in una directory biblioteche/ Ambiente di sviluppo IDE Arduino. Quindi rinominare la cartella ArduinoThreadmaster V ArduinoThread.

Lo schema di collegamento rimarrà lo stesso. Cambierà solo il codice del programma.

#includere // collega la libreria ArduinoThread const int soundPin = 3; // variabile con il numero di pin dell'elemento piezoelettrico const int ledPin = 13; // variabile con il numero del pin del LED Thread ledThread = Thread(); // crea un thread di controllo LED Thread soundThread = Thread(); // crea un thread di controllo della sirena configurazione nulla() ( pinMode(soundPin, OUTPUT); // dichiara il pin 3 come output. pinMode(LEDPin, USCITA); // dichiara il pin 13 come output. ledThread.onRun(ledBlink); // assegna un'attività al thread ledThread.setInterval(1000); // imposta l'intervallo di risposta, ms soundThread.onRun(sound); // assegna un'attività al thread soundThread.setInterval(20); // imposta l'intervallo di risposta, ms } ciclo vuoto() (// Controlla se è ora che il LED si accenda: if (ledThread.shouldRun()) ledThread.run(); // inizia il thread // Controlla se è il momento di cambiare il tono della sirena: if (soundThread.shouldRun()) soundThread.run(); // inizia il thread } // Flusso LED: vuoto ledBlink() ( statico bool ledStatus = false; // Stato del LED Acceso/Spento ledStatus = !ledStatus; // inverte lo stato digitalWrite(ledPin, ledStatus); // accende/spegne il LED } // Flusso di sirene: suono vuoto() ( tono int statico = 100; // altezza del suono, tono Hz(soundPin, ton); // accendo la sirena a "ton" Hz se (ton )

Nel programma creiamo due thread: ledThread E soundThread, ognuno esegue la propria operazione: uno fa lampeggiare il led, il secondo comanda il suono della sirena. In ogni iterazione del ciclo, per ogni thread, controlliamo se è giunto il momento della sua esecuzione oppure no. Se arriva, viene avviato per l'esecuzione utilizzando il metodo correre(). La cosa principale è non usare l'operatore ritardo(). Il codice fornisce spiegazioni più dettagliate.


Carichiamo il codice nella memoria di Arduino ed eseguiamolo. Ora tutto funziona esattamente come dovrebbe!

Ed ecco un esempio di utilizzo della funzione Arduino attachInterrupt().

Un'interruzione è un segnale che notifica al processore che si è verificato un evento che richiede attenzione immediata. Il processore deve rispondere a questo segnale interrompendo l'esecuzione delle istruzioni correnti e trasferendo il controllo al gestore degli interrupt (ISR, Interrupt Service Routine). Un gestore è una funzione regolare che scriviamo noi stessi e inseriamo lì il codice che dovrebbe rispondere all'evento.

Dopo aver gestito l'interruzione ISR, la funzione completa il suo lavoro e il processore ritorna felicemente alle attività interrotte, continuando l'esecuzione del codice da dove si era interrotto. Tutto ciò avviene automaticamente, quindi il nostro compito è solo quello di scrivere un gestore di interrupt senza rompere nulla o far distrarre troppo spesso il processore da noi. Avrai bisogno di una comprensione del circuito, dei principi di funzionamento dei dispositivi collegati e di un'idea di quanto spesso può essere causata un'interruzione, quali sono le caratteristiche del suo verificarsi. Tutto ciò costituisce la principale difficoltà di lavorare con interruzioni.

Interruzioni hardware e software

Gli interrupt in Arduino possono essere suddivisi in diversi tipi:

  • Interruzioni hardware. Interruzione a livello di architettura del microprocessore. L'evento stesso può verificarsi in un momento produttivo da dispositivo esterno– ad esempio, premendo un pulsante sulla tastiera, spostandosi mouse del computer e così via.
  • Interruzioni del software. Vengono lanciati all'interno del programma utilizzando istruzioni speciali. Utilizzato per chiamare il gestore degli interrupt.
  • Interrupt interni (sincroni).. Un interrupt interno si verifica a seguito di una modifica o di una violazione nell'esecuzione del programma (ad esempio, quando si accede a un indirizzo non valido, a un codice operativo non valido, ecc.).

Perché sono necessari gli interrupt hardware?

Gli interrupt hardware si verificano in risposta a un evento esterno e provengono da un dispositivo hardware esterno. Arduino fornisce 4 tipi di interruzioni hardware. Differiscono tutti nel segnale sul pin di interruzione:

  • Il contatto è tirato a terra. Il gestore dell'interruzione viene eseguito finché è presente un segnale BASSO sul pin di interruzione.
  • Modifica del segnale sul contatto. In questo caso, Arduino esegue un gestore di interruzione quando si verifica una variazione di segnale sul pin di interruzione.
  • Modifica del segnale da BASSO a ALTO su un pin: quando si passa da un segnale basso a uno alto, verrà eseguito il gestore dell'interruzione.
  • Modifica del segnale da ALTO a BASSO su un pin: quando si passa da un segnale alto a un segnale basso, verrà eseguito il gestore dell'interruzione.

Gli interrupt sono utili nei programmi Arduino poiché aiutano a risolvere i problemi di temporizzazione. Ad esempio, quando si lavora con UART, gli interrupt consentono di evitare di dover tracciare l'arrivo di ciascun personaggio. Un dispositivo hardware esterno emette un segnale di interruzione, il processore chiama immediatamente un gestore di interruzione, che cattura il carattere in tempo. Ciò fa risparmiare tempo alla CPU che altrimenti verrebbe impiegato per controllare lo stato dell'UART senza interruzioni; invece, tutte le azioni necessarie vengono eseguite dal gestore degli interrupt senza influenzare il programma principale; Non sono richieste funzionalità speciali dal dispositivo hardware.

I motivi principali per cui è necessario chiamare un interrupt sono:

  • Rilevamento di un cambiamento nello stato dell'uscita;
  • Interruzione del timer;
  • Interruzioni dati tramite SPI, I2C, USART;
  • Conversione da analogico a digitale;
  • Disponibilità all'utilizzo di EEPROM, memoria flash.

Come vengono implementati gli interrupt in Arduino

Quando viene ricevuto un segnale di interruzione, il funzionamento viene sospeso. Inizia l'esecuzione della funzione dichiarata da eseguire quando viene interrotta. Una funzione dichiarata non può accettare valori di input e restituire valori quando esce. L'interruzione non influisce sul codice stesso nel ciclo principale del programma. Per lavorare con gli interrupt in Arduino, viene utilizzata una funzione standard attachInterrupt().

Differenze nell'implementazione degli interrupt nelle diverse schede Arduino

A seconda dell'implementazione dell'hardware modello specifico Il microcontrollore ha diversi interrupt. La scheda Arduino Uno ha 2 interrupt sul secondo e terzo pin, ma se sono necessarie più di due uscite, la scheda supporta una speciale modalità “pin-change”. Questa modalità funziona modificando l'ingresso per tutti i pin. La differenza tra la modalità di interruzione del cambio di ingresso è che gli interruzioni possono essere generati su uno qualsiasi degli otto pin. In questo caso, l'elaborazione sarà più difficile e più lunga, poiché dovrai tenere traccia dello stato più recente di ciascuno dei contatti.

Su altre schede il numero di interruzioni è maggiore. Ad esempio, la scheda ha 6 pin in grado di gestire interruzioni esterne. Per tutte le schede Arduino, quando si lavora con la funzione attachInterrupt (interrupt, funzione, modalità), l'argomento Inerrupt 0 è associato al pin digitale 2.

Interrupt nel linguaggio Arduino

Ora diventiamo pratici e parliamo di come utilizzare gli interrupt nei tuoi progetti.

Sintassi attachInterrupt()

La funzione attachInterrupt viene utilizzata per lavorare con gli interrupt. Serve per connettere un interrupt esterno a un gestore.

Sintassi della chiamata: attachInterrupt(interrupt, funzione, modalità)

Argomenti della funzione:

  • interrupt – numero dell'interrupt da chiamare (standard 0 – per il 2° pin, per la scheda Arduino Uno 1 – per il 3° pin),
  • funzione – il nome della funzione da chiamare in caso di interruzione (importante: la funzione non deve né accettare né restituire alcun valore),
  • modalità – condizione per attivare l'interruzione.

È possibile impostare le seguenti condizioni di trigger:

  • BASSO – eseguito quando il livello del segnale è basso quando il contatto ha un valore zero. L'interruzione può essere ripetuta ciclicamente, ad esempio premendo un pulsante.
  • CHANGE – sul fronte, l'interruzione avviene quando il segnale cambia da alto a basso o viceversa. Viene eseguito una volta per ogni modifica del segnale.
  • RISING – esegue un'interruzione una volta quando il segnale cambia da BASSO ad ALTO.
  • FALLING – esegue un'interruzione una volta quando il segnale cambia da ALTO a BASSO.4

Note importanti

Quando si lavora con gli interrupt è necessario tenere conto delle seguenti importanti limitazioni:

  • La funzione del gestore non dovrebbe essere eseguita per troppo tempo. Il fatto è che Arduino non può gestire più interruzioni contemporaneamente. Mentre la funzione del gestore è in esecuzione, tutte le altre interruzioni verranno ignorate e potresti perdere eventi importanti. Se devi fare qualcosa di grosso, trasferisci semplicemente l'elaborazione degli eventi al ciclo loop() principale. Nel gestore puoi solo impostare il flag dell'evento e nel ciclo puoi controllare il flag ed elaborarlo.
  • Bisogna stare molto attenti con le variabili. Un compilatore C++ intelligente può "riottimizzare" il tuo programma, rimuovendo le variabili che, a suo avviso, non sono necessarie. Il compilatore semplicemente non vedrà che imposti alcune variabili in una parte e le usi in un'altra. Per eliminare questa possibilità nel caso di tipi di dati di base, è possibile utilizzare parola chiave volatile, ad esempio in questo modo: volatile boolean state = 0. Ma questo metodo non funzionerà con strutture dati complesse. Quindi bisogna stare sempre in allerta.
  • Non è consigliabile utilizzare un numero elevato di interruzioni (cercare di non utilizzarne più di 6-8). Un gran numero di la gestione di vari eventi richiede una seria complicazione del codice e, pertanto, porta a errori. Inoltre, è necessario comprendere che nei sistemi con accuratezza temporale dell'esecuzione non esiste grande quantità Non possono esserci interruzioni nel parlato: non capirai mai esattamente quale sia l'intervallo tra le chiamate dei comandi che sono importanti per te.
  • È severamente vietato utilizzare delay() nei gestori. Il meccanismo per determinare l'intervallo di ritardo utilizza i timer e funzionano anche sugli interrupt che il tuo gestore bloccherà. Di conseguenza, tutti aspetteranno tutti e il programma si bloccherà. Per lo stesso motivo non è possibile utilizzare protocolli di comunicazione basati su interrupt (ad esempio i2c).

Esempi di utilizzo di attachInterrupt

Iniziamo e diamo un'occhiata esempio più semplice uso di interruzioni. Nell'esempio definiamo una funzione handler che, quando cambia il segnale sul pin 2 di Arduino Uno, commuterà lo stato del pin 13, a cui tradizionalmente colleghiamo il LED.

#define PIN_LED 13 volatile booleano actionState = LOW; void setup() ( pinMode(PIN_LED, OUTPUT); // Imposta l'interrupt // La funzione myEventListener verrà chiamata quando // sul pin 2 (l'interrupt 0 è collegato al pin 2) // il segnale cambia (non importa in quale direzione) attachInterrupt (0, myEventListener, CHANGE); void loop() ( // Non facciamo nulla nella funzione loop, poiché tutto il codice di gestione degli eventi sarà nella funzione myEventListener) void myEventListener() ( actionState != actionState ; // / / Esegue altre azioni, ad esempio, accende o spegne il LED digitalWrite(PIN_LED, actionState )

Diamo un'occhiata ad alcuni esempi di interruzioni più complesse e ai relativi gestori: per timer e pulsanti.

Si interrompe premendo un pulsante con antirimbalzo

In caso di interruzione, si verifica: prima che i contatti entrino in stretto contatto quando si preme il pulsante, oscilleranno, generando diverse operazioni. Esistono due modi per gestire il rimbalzo: hardware, ovvero saldando un condensatore al pulsante, e software.

Puoi eliminare le chiacchiere utilizzando la funzione: ti consente di misurare il tempo trascorso dalla prima operazione del pulsante.

If(digitalRead(2)==HIGH) ( //quando viene premuto il pulsante //Se sono trascorsi più di 100 millisecondi dalla pressione precedente if (millis() - previousMillis >= 100) ( //L'ora della prima l'operazione viene ricordata previousMillis = millis(); if (led==oldled) ( //controlla che lo stato del pulsante non sia cambiato led=!led; )

Questo codice consente di eliminare il rimbalzo e non blocca l'esecuzione del programma, come nel caso della funzione di ritardo, che non è consentita negli interrupt.

Il timer si interrompe

Un timer è un contatore che conta ad una certa frequenza, ottenuta dal processore da 16 MHz. Il divisore di frequenza può essere configurato per ottenere modalità desiderata conti. È inoltre possibile configurare il contatore per generare interruzioni quando viene raggiunto un valore impostato.

E un'interruzione del timer ti consente di interrompere una volta ogni millisecondo. Arduino ha 3 timer: timer0, timer1 e timer2. Timer0 viene utilizzato per generare interruzioni una volta ogni millisecondo, che aggiorna il contatore e lo passa alla funzione millis(). Questo timer è a otto bit e conta da 0 a 255. Viene generato un interrupt quando il valore raggiunge 255. Per impostazione predefinita, viene utilizzato un divisore di clock di 65 per ottenere una frequenza vicina a 1 kHz.

I registri di confronto vengono utilizzati per confrontare lo stato del timer e i dati memorizzati. IN in questo esempio il codice genererà un interrupt quando il contatore raggiunge 0xAF.

TIMSK0 |= _BV(OCIE0A);

È necessario definire un gestore di interruzione per un vettore di interruzione del timer. Un vettore di interruzione è un puntatore all'indirizzo di posizione del comando che verrà eseguito quando viene richiamato l'interrupt. Più vettori di interruzione vengono combinati in una tabella dei vettori di interruzione. Il timer in questo caso si chiamerà TIMER0_COMPA_vect. Questo gestore eseguirà le stesse azioni di loop().

SIGNAL(TIMER0_COMPA_vect) ( currentMillis lungo senza segno = millis(); spazzatrice1.Update(currentMillis); if(digitalRead(2) == HIGH) ( spazzatrice2.Update(currentMillis); led1.Update(currentMillis); ) led2.Update( currentMillis); led3.Update(currentMillis); //La funzione loop() rimarrà vuota. ciclo vuoto() ( )

Riassumendo

L'interruzione in Arduino è un argomento piuttosto complesso, perché devi pensare subito all'intera architettura del progetto, immaginare come viene eseguito il codice, quali eventi sono possibili, cosa succede quando il codice principale viene interrotto. Non abbiamo deciso di rivelare tutte le caratteristiche del lavoro con questo costrutto linguistico, l'obiettivo principale era introdurre i principali casi d'uso; Nei prossimi articoli continueremo la conversazione sulle interruzioni in modo più dettagliato.

Ciao Andrei. Il tuo approccio nel trasferire le conoscenze e l'esperienza che hai accumulato è molto interessante. Aiuta molto nei tuoi sforzi. Bene, io, iniziando a padroneggiare Arduino, ho voglia di progredire. Inoltre, con un aiuto esterno posso farlo più velocemente. Quindi: all'inizio il mio compito era realizzare un robot che guidasse lungo una linea. L'ho fatto: è andato tutto bene. Ma poi, dotandolo di opzioni aggiuntive, non ho capito perché smettesse di rispondere correttamente alla linea. Mi sono imbattuto in questo articolo e ne ho capito il motivo.

Ora ho una domanda per te: nello sketch riportato di seguito e finito, tenendo conto dei problemi di ritardo, devo passare ai millis ovunque sia presente questa funzione? Se è così, allora capisco che quasi l'intero schizzo dovrà essere rifatto? E non è del tutto chiaro come utilizzare i millis per misurare la distanza? Grazie.

//Robot con funzione di tracciamento della linea bianca

// **********************Installazione dei cavi del motore *********************** *

int MotorLeftSpeed ​​= 5; // VELOCITÀ motore sinistro (A) - ENA

int MotoreLeftForward = 4; // Motore sinistro (A) AVANTI - IN1

int MotoreLeftBack = 3; // Motore sinistro (A) BACK - IN2

int MotoreRightForward = 8; // Motore destro (B) AVANTI - IN3

int MotoreRightBack = 7; // Motore destro (B) BACK - IN4

int MotorRightSpeed ​​= 9; // VELOCITÀ motore destro (B) - ENB

// *********************Installazione delle uscite dei sensori a ultrasuoni************************ * *

int trigPinL = 14; // impostazione del numero pin del sensore ultrasonico trigonometrico sinistro

int echoPinL = 15; // imposta il numero di uscita del sensore ultrasonico dell'eco sinistro

int trigPinC = 10; // impostazione del numero di uscita del sensore ultrasonico trigonometrico centrale

int echoPinC = 11; // impostazione del numero di uscita del sensore a ultrasuoni dell'eco centrale

int trigPinR = 12; // impostazione del numero pin del sensore ultrasonico trigonometrico destro

int echoPinR = 13; // imposta il numero di uscita del sensore ultrasonico dell'eco destro

// ********************* Installazione dei pin del sensore di linea *******************

const int LineSensorLeft = 19; // ingresso sensore linea sinistra

const int LineSensorRight = 18; // input del sensore della linea destra

int SL; // stato del sensore sinistro

intSR; // stato del sensore destro

// ********************Impostazione dell'uscita dell'allarme luminoso e sonoro****************

int Luce = 2; // imposta il numero dell'uscita dell'allarme luminoso

int Zumm = 6; // imposta il numero di uscita del cicalino

int ledState = BASSO; // imposta lo stato del LED con questa variabile

lungo precedenteMillis = 0; // memorizza l'ora dell'ultima commutazione del LED

intervallo lungo = 300; // intervallo tra l'accensione/spegnimento del LED (0,3 secondi)

// *********************Misurazione variabile sensori di distanza************

unsigned int impulsoTimeL=0;

unsigned int impulsoTimeC=0;

unsigned int impulsoTimeR=0;

lungo distL=0; // distanza misurata dal sensore a ultrasuoni sinistro

lunga distanzaC=0; // distanza misurata dal sensore a ultrasuoni centrale

lungo distR=0; // distanza misurata dal sensore a ultrasuoni destro

// ******************************** IMPOSTARE ************ *** *****************

Serial.begin(9600); //avvia la porta seriale (velocità 9600)

//*************** Imposta i contatti del motore****************

pinMode(MotoreRightBack, OUTPUT); // Motore destro (B) BACK

pinMode(MotoreDestraAvanti, OUTPUT); // Motore destro (B) AVANTI

pinMode(MotorePosterioreSinistro, OUTPUT); // Motore sinistro (A) BACK

pinMode(MotoreSinistraAvanti, OUTPUT); // Motore sinistro (A) AVANTI

ritardo(durata);

//*************** Imposta i contatti del sensore della striscia****************

pinMode(LineSensorLeft, INPUT); //definisce il pin del sensore di linea sinistro

pinMode(LineSensorRight, INPUT); // determinazione del pin del sensore di linea destro

// **************Impostazione delle modalità di uscita dei sensori a ultrasuoni************************

pinMode(trigPinL, USCITA); // impostazione della modalità operativa dell'uscita trig sinistra del sensore a ultrasuoni

pinMode(echoPinL, INPUT); // impostazione della modalità operativa dell'uscita del sensore a ultrasuoni dell'eco sinistro

pinMode(trigPinC, USCITA); // impostazione della modalità operativa dell'uscita del sensore a ultrasuoni trigonometrico centrale

pinMode(echoPinC, INPUT); // impostazione della modalità operativa dell'uscita del sensore a ultrasuoni dell'eco centrale

pinMode(trigPinR, USCITA); // impostazione della modalità operativa dell'uscita del sensore a ultrasuoni trigonometrico destro

pinMode(echoPinR, INPUT); // impostazione della modalità operativa dell'uscita del sensore a ultrasuoni dell'eco destro

// **************Imposta i contatti per gli allarmi luminosi e sonori**************************** ******

pinMode(Zumm,USCITA); // imposta la modalità operativa dell'uscita del cicalino

pinMode(Luce,USCITA); //imposta la modalità di funzionamento dell'uscita di segnalazione luminosa

// ****************** Comandi di base movimenti ******************

void avanti (int a, int sa) // AVANTI

analogWrite(MotorRightSpeed, sa);

analogWrite(MotorLeftSpeed, sa);

void right (int b, int sb) // RUOTA A DESTRA (un lato)

digitalWrite(MotoreRightBack, LOW);

digitalWrite(MotorePosterioreSinistro, BASSO);

digitalWrite(MotoreAvantiSinistra, ALTO);

analogWrite(VelocitàMotoreSinistra, sb);

void left (int k, int sk) // GIRA A SINISTRA (un lato)

digitalWrite(MotoreRightBack, LOW);

digitalWrite(MotoreAvantiDestra, ALTA);

analogWrite(MotorRightSpeed, sk);

digitalWrite(MotorePosterioreSinistro, BASSO);

void stopp (int f) // STOP

digitalWrite(MotoreRightBack, LOW);

digitalWrite(MotoreAvantiDestra, BASSO);

digitalWrite(MotorePosterioreSinistro, BASSO);

digitalWrite(MotoreAvantiSinistra, BASSO);

// ********************************Misurazione della distanza**************** ****** *

void izmdistL () // misurazione della distanza con il sensore a ultrasuoni sinistro

digitalWrite(trigPinL, ALTO);

digitalWrite(trigPinL, BASSO); // Impulso da 10 mS all'uscita trigonometrica del sensore a ultrasuoni per misurare la distanza

impulsoTempoL = impulsoIn(echoPinL, ALTO); // lettura della distanza dal sensore a ultrasuoni

distL=impulsoTempoL/58; // Converti in centimetri

void izmdistC () // misurazione della distanza tramite il sensore a ultrasuoni centrale

digitalWrite(trigPinC, ALTO);

digitalWrite(trigPinC, BASSO); // Impulso da 10 mS all'uscita trigger del sensore a ultrasuoni per misurare la distanza

impulsoTempoC = impulsoIn(echoPinC, ALTO); // lettura della distanza dal sensore a ultrasuoni

distC=impulsoTempoC/58; // Converti in centimetri

void izmdistR () // misurazione della distanza tramite il sensore a ultrasuoni centrale

digitalWrite(trigPinR, ALTO);

digitalWrite(trigPinR, BASSO); // Impulso da 10 mS all'uscita trigger del sensore a ultrasuoni per misurare la distanza

impulsoTempoR = impulsoIn(echoPinR, ALTO); // lettura della distanza dal sensore a ultrasuoni

distR=impulsoTempoR/58; // Converti in centimetri

// *********************************** CICLO CONTINUO ************ *********************

// ********************** Modalità seguente LINE *********************** ********* ***

// *********************allarme luminoso e sonoro***************

tono(Zumm,900); // attiva l'audio a 900 Hz

tono(Zumm,900); // attiva l'audio a 800 Hz

correnteMillis lunga senza segno = millis();

if (currentMillis - previousMillis > interval) //controlla se l'intervallo richiesto è passato, se è passato allora

Milli precedenti = Milli attuali; // salva l'ora dell'ultimo passaggio

if (ledState == LOW) // se il LED non è acceso, accenderlo e viceversa

ledState = ALTO;

digitalWrite(Luce, stato led); // imposta gli stati dell'uscita per accendere o spegnere il LED

// ********************** Misurazione della distanza************************ **

Serial.println(distL);

Serial.println(distC);

Serial.println(distR);

if (distL>50 && distC>50 && distR>50) // se la distanza misurata è superiore a 50 centimetri, andiamo

SL = digitalRead(LineSensorLeft); // legge il segnale dal sensore della corsia di sinistra

SR = digitalRead(LineSensorRight); // legge il segnale dal sensore della corsia di destra

// ******************************** Seguendo la linea nera ************ ******* ****

// ROBOT sulla corsia: vai dritto

if (SL == BASSO & SR == BASSO) // BIANCO - BIANCO - va DRITTO

avanti (10, 100); // DIRETTO (tempo, velocità)

// Il ROBOT inizia a uscire dalla corsia: rulliamo

altrimenti se (SL == BASSO & SR == ALTO) // NERO - BIANCO - gira a SINISTRA

sinistra (10, 100); // gira a SINISTRA (tempo, velocità)

altrimenti se (SL == ALTO & SR == BASSO) // BIANCO - NERO - gira a DESTRA

destra (10, 100); // gira a DESTRA (tempo, velocità)

// FINE - ROBOT vede la striscia con entrambi i sensori

else if (SL == ALTO & SR == ALTO) // NERO - NERO - STOP

arresto (50); // ARRESTO

else // se la distanza misurata è inferiore o uguale al minimo, ci fermiamo