Archivi categoria: arduino

Arduino: controllo sequenziale uscite digitali

Durante la progettazione di un sistema di automazione accade frequentemente di avere la necessità di ripetere, sequenzialmente e in modo continuo, l’attivazione di apparati (ad es. motori) oppure la lettura continua dei dati provenienti da più sensori. Come attività di ripasso per i miei studenti ho deciso di riprendere alcuni argomenti affrontati nelle scorse settimane con specifiche esperienze di laboratorio:

  • automi a stati finiti;
  • utilizzo degli array;
  • input valori interi da serial monitor;
  • marcia, arresto, pausa di sequenze;
  • controllo uscite digitali mediante ingressi analogici;
  • realizzazione di commutatori con pulsanti con uno o più pulsanti;
  • utilizzo corretto dei tipi di dati per risparmiare memoria;
  • e molto altro

Di seguito 9 sketch in cui vengono ripresi gli argomenti sopra elencati e che potranno essere utilizzati nei prossimi mesi per sviluppare ulteriori sperimentazioni.
Come sempre all’interno degli sketch proposti trovate le spiegazioni di ogni parte del codice ed in alcuni casi trovate link di approfondimento che rimandano a questo sito.

Per ripercorrere gli argomenti svolti partirò dal classico sketch che permette di realizza l’accensione sequenziale di LED, come quello che potete trovare nelle mie slice: Alfabeto Arduino – Lezione 2 a pagina 66.
I LED nel circuito identificano gli apparati da attivare sequenzialmente, realizzando così il classico effetto “super car” (i diversamente giovani 🙂 sanno perché si chiama così).
Circuito e sketch verranno poi modificati per rispondere alle specifiche indicate ad inizio di ogni esempio.

Sketch 01

Sequenza di accensione e spegnimento da destra e sinistra e viceversa di 8 LED con tempo di accensione di 100 millisecondi.

 

1/* Prof. Michele Maffucci
2   30.12.2020
3   Lezione di riferimento: https://wp.me/p4kwmk-4D3
4 
5   Versione 01
6   Sequenza di accensione e spegnimento alternato
7   da destra e sinistra e viceversa di 8 LED con
8   tempo di accensione di 100 millisecondi.
9    
10   Questo codice è di dominio pubblico
11 */
12 
13// creazione di un array di 8 pin a cui vanno collegati i LED
14// per ulteriori informazioni sull'uso degli array si consulti il seguente link:
16 
17byte ledPin[] = {3, 4, 5, 6, 7, 8, 9, 10};
18 
19// per approfondimenti sull'uso dei tipi di dati
20// si consultino i link:
23 
24// intervallo di accensione/spegnimento
25byte ritardoLed = 100;
26 
27// indicatore di direzione di accensione
28byte direzione = 1;
29 
30// indice dell'array per l'accensione del LED
31byte ledCorrente = 0;
32 
33// variabile in cui memorizzare il tempo di accensione di Arduino
34// per ulteriori informazioni sui tipi unsigned long si consulti il seguente link:
36 
37unsigned long tempoTrascorso;
38 
39void setup() {               
40  // impostiamo tutti i pin ad output
41  for (byte x=0; x<8; x++) {
42    pinMode(ledPin[x], OUTPUT);
43  }
44     
45  // Memorizzazione del tempo trascorso
46  // dal momento in cui avviamo Arduino
47  // Per ulteriori informazioni sull'uso di millis() si consulti il seguente link:
49   
50  tempoTrascorso = millis();
51}
52 
53void loop() {
54  // Se sono passati "ritardoLed" millisecondi dall'ultimo cambiamento
55  if ((millis() - tempoTrascorso) > ritardoLed) {
56    cambiaStatoLed();
57    tempoTrascorso = millis();
58  }
59}
60 
61// la funzione cambiaStatoLed() permette di controllare
62// la sequenza di accensione dei LED
63 
64void cambiaStatoLed() {
65  // spegne tutti i LED
66  for (byte x=0; x<8; x++) {
67    digitalWrite(ledPin[x], LOW);
68  }
69  // accende il LED corrente
70  digitalWrite(ledPin[ledCorrente], HIGH);
71  // incrementa la variabile direzione
72  ledCorrente += direzione;
73  // cambia la direzione se si arriva alla fine
74  if (ledCorrente == 7) {
75    direzione = -1;
76  }
77  if (ledCorrente == 0) {
78    direzione = 1;
79  }
80}

Sketch 02

Sequenza di accensione e spegnimento da destra e sinistra e viceversa di 8 LED. Con un trimmer è possibile variare il tempo di accensione nell’intervallo da 50 millisecondi a 1000 millisecondi (1 secondo).

1/* Prof. Michele Maffucci
2   30.12.2020
3   Lezione di riferimento: https://wp.me/p4kwmk-4D3
4 
5   Versione 02
6   Sequenza di accensione e spegnimento alternato
7   da destra e sinistra e viceversa di 8 LED controllato
8   da un trimmer che permetterà di variare il tempo di accensione
9   da 50 millisecondi a 1000 millisecondi (1 secondo).
10    
11   Questo codice è di dominio pubblico
12 */
13 
14// creazione di un array di 8 pin a cui vanno collegati i LED
15// per ulteriori informazioni sull'uso degli array si consulti il seguente link:
17 
18byte ledPin[] = {3, 4, 5, 6, 7, 8, 9, 10};
19 
20// per approfondimenti sull'uso dei tipi di dati
21// si consultino i link:
24 
25// intervallo di accensione/spegnimento
26int ritardoLed;
27 
28// variabile in cui memorizzare il valore restituito dall'analogRead
29int val = 0;
30 
31// indicatore di direzione di accensione
32byte direzione = 1;
33 
34// indice dell'array per l'accensione del LED
35byte ledCorrente = 0;
36 
37// variabile in cui memorizzare il tempo di accensione di Arduino
38// per ulteriori informazioni sui tipi unsigned long si consulti il seguente link:
40 
41unsigned long tempoTrascorso;
42 
43void setup() {
44  Serial.begin(9600);           
45  // impostiamo tutti i pin ad output
46  for (byte x=0; x<8; x++) {
47    pinMode(ledPin[x], OUTPUT);
48  }
49     
50  // Memorizzazione del tempo trascorso
51  // dal momento in cui avviamo Arduino
52  // Per ulteriori informazioni sull'uso di millis() si consulti il seguente link:
54   
55  tempoTrascorso = millis();
56}
57 
58void loop() {
59 
60  // valore analogico letto su A0 inserito con il trimmer
61  val = analogRead(A0);
62 
63// Togliere il commento per valutare
64// valore massimo/minimo del valore restituito
65// dall'analogRead in questo modo si potranno
66// inserire nella map i valori massimi e minimi
67// dell'intervallo di partenza
68 
69// Serial.println(val);
70// delay(1000);
71 
72  // ValMax = 285, ValMin = 719
73  // riconvertiti nell'intervallo 50, 1000
74   
75  ritardoLed = map(val, 285, 719, 50, 1000);
76   
77  // Se sono passati "ritardoLed" millisecondi dall'ultimo cambiamento
78  if ((millis() - tempoTrascorso) > ritardoLed) {
79    cambiaStatoLed();
80    tempoTrascorso = millis();
81  }
82}
83 
84// la funzione cambiaStatoLed() permette di controllare
85// la sequenza di accensione dei LED
86 
87void cambiaStatoLed() {
88  // spegne tutti i LED
89  for (byte x=0; x<8; x++) {
90    digitalWrite(ledPin[x], LOW);
91  }
92  // accende il LED corrente
93  digitalWrite(ledPin[ledCorrente], HIGH);
94  // incrementa la variabile direzione
95  ledCorrente += direzione;
96  // cambia la direzione se si arriva alla fine
97  if (ledCorrente == 7) {
98    direzione = -1;
99  }
100  if (ledCorrente == 0) {
101    direzione = 1;
102  }
103}

Sketch 03

Sequenza di accensione e spegnimento alternato da destra e sinistra e viceversa di 8 LED. L’accensione di ogni LED è fissato in partenza a 100 millisecondi. Con un messaggio sulle Serial Monitor viene richiesto di inserire un nuovo tempo di accensione e spegnimento di ogni LED (delay), tempo che può essere scelto a piacimento.

Lo schema di collegamento è analogo a quello utilizzato per lo sketch 01.

1/* Prof. Michele Maffucci
2   30.12.2020
3   Lezione di riferimento: https://wp.me/p4kwmk-4D3
4 
5   Versione 03
6   Sequenza di accensione e spegnimento alternato
7   da destra e sinistra e viceversa di 8 LED.
8 
9   Partenza sequenza con 100 millisecondi e messaggio sulla
10   Serial Monitor per modificare il tempo di
11   accensione e spegnimento del singolo LED (delay)
12    
13   Questo codice è di dominio pubblico
14 */
15 
16// creazione di un array di 8 pin a cui vanno collegati i LED
17// per ulteriori informazioni sull'uso degli array si consulti il seguente link:
19 
20byte ledPin[] = {3, 4, 5, 6, 7, 8, 9, 10};
21 
22// per approfondimenti sull'uso dei tipi di dati
23// si consultino i link:
26 
27// intervallo di accensione/spegnimento
28int ritardoLed = 100;
29 
30// indicatore di direzione di accensione
31byte direzione = 1;
32 
33// indice dell'array per l'accensione del LED
34byte ledCorrente = 0;
35 
36// variabile in cui memorizzare il tempo di accensione di Arduino
37// per ulteriori informazioni sui tipi unsigned long si consulti il seguente link:
39 
40unsigned long tempoTrascorso;
41 
42// per stampare una sola volta il messaggio sulla Serial Monitor 
43bool abilitaMessaggio = 0;
44 
45void setup() {
46  // inizializzazione della serial monitor
47  Serial.begin(9600);
48                
49  // impostiamo tutti i pin ad output
50  for (byte x=0; x<8; x++) {
51    pinMode(ledPin[x], OUTPUT);
52  }
53 
54  // Memorizzazione del tempo trascorso
55  // dal momento in cui avviamo Arduino
56  // Per ulteriori informazioni sull'uso di millis() si consulti il seguente link:
58  tempoTrascorso = millis();
59}
60 
61void loop() {
62  // consente di visualizzare sulla Serial Monitor
63  // una sola stampa delle stringa
64  if (abilitaMessaggio == 0) {
65    // ritardo che evita la doppia stampa del messaggio
66    delay(200);
67    Serial.print("Inserisci il ritardo in millisecondi: ");
68    abilitaMessaggio = 1;
69  }
70 
71  // Controlla se è disponibile almeno un carattere sulla seriale
72  // La Serial.available() restituisce
73  // 1 se presente un cattere,
74  // 0 se non è presente un carattere
75 
76  // per maggior informazioni sull'uso di parseInt() consultare il link:
78 
79  if (Serial.available())
80  {
81    // in r viene memorizzato il valore inserito
82    // attraverso la Serial Monitor
83    int r = Serial.parseInt();
84    if (r != 0) {
85      ritardoLed = r;
86      Serial.println(ritardoLed);
87 
88      // abilita alla stampa di una nuova stringa:
89      // "Inserisci il ritardo in millisecondi: "
90      abilitaMessaggio = 0;
91    }
92  }
93   
94  // funzione che fa lampeggiare il LED su Arduino
95  lampeggio();
96}
97 
98void lampeggio() {
99  // Se sono passati "ritardoLed" millisecondi dall'ultimo cambiamento
100  if ((millis() - tempoTrascorso) > ritardoLed) {
101    cambiaStatoLed();
102    tempoTrascorso = millis();
103  }
104}
105 
106// la funzione cambiaStatoLed() permette di controllare
107// la sequenza di accensione dei LED
108 
109void cambiaStatoLed() {
110  // spegne tutti i LED
111  for (byte x = 0; x < 8; x++) {
112    digitalWrite(ledPin[x], LOW);
113  }
114  // accende il LED corrente
115  digitalWrite(ledPin[ledCorrente], HIGH);
116  // incrementa la variabile direzione
117  ledCorrente += direzione;
118  // cambia la direzione se si arriva alla fine
119  if (ledCorrente == 7) {
120    direzione = -1;
121  }
122  if (ledCorrente == 0) {
123    direzione = 1;
124  }
125}

Continua a leggere

Arduino: cicli infiniti ed uscita da un ciclo infinito

Per rispondere ad un quesito che mi è stato posto questa mattina a lezione da alcuni allievi condivido questo breve approfondimento sulla gestione dei cicli infiniti.

Parliamo di un ciclo infinito quando il controllo entra nel corpo del ciclo e non esce mai da esso, ciò accade quando la condizione di test del loop non è mai false.

Possiamo realizzare un ciclo infinito in C in diversi modi:

1for (int i = 0; i >= 0; )
2{
3    // corpo del ciclo in cui la variabile i non viene mai modificata
4}

Nell’esempio la variabile i viene inizializzata a 0 e la condizione di controllo i>=0 all’inizio è vera. La variabile i non viene modificato in nessun punto del codice inoltre l’espressione di aggiornamento della variabile non è presente, ciò implica che i avrà sempre come valore 0 e la condizione di controllo i>=0 non è mai falsa e quindi il corpo del for viene eseguito ad ogni ciclo.

Un modo più sintetico per realizzare un ciclo infinito è quello di utilizzare una condizione sempre vera utilizzando l’istruzione while:

1while (true)
2{
3    // corpo del while
4}

Questo comportamento può essere realizzato anche scrivendo 1 per indicare “condizione sempre vera”.

1while (1)
2{
3    // corpo del while ripetuto per sempre
4}

o ancora utilizzando un’uguaglianza sempre vera:

1while (3==3)
2{
3    // corpo del while ripetuto per sempre
4}

che nella programmazione con Arduino può essere ottenuto anche con:

1while (HIGH)
2{
3    // corpo del while ripetuto per sempre
4}

E’ possibile realizzare un ciclo infinito anche con l’istruzione for omettendo la condizione di ripetizione del ciclo, cioè che bisognerebbe inserire tra i due punti e virgola.

1for (;;)
2{
3    // corpo del for ripetuto per sempre
4}

Terminare un ciclo infinito

Negli esempi visti precedentemente la condizione sempre vera causava la ripetizione infinita del corpo dell’istruzione, però esiste il modo per terminare un ciclo infinito utilizzando l’istruzione break usata appunto per uscire da un ciclo: for, while oppure do..while, ma anche per uscire da una istruzione switch case.

Poiché nelle nostre sperimentazioni utilizziamo Arduino vediamo come realizzare l’uscita da un loop infinito.

Lo scopo è quello di far lampeggiare ripetutamente un LED e bloccare il lampeggio con la pressione di un pulsante.

Realizzimo il seguente circuito:

Utilizzando l’istruzione while.

Esempio 1

1/*
2  Prof. Michele Maffucci
3  Data: 16.11.2020
4  Esempio1: Funzionamento dell'istruzione break
5*/
6const int pinPulsante = 8; // pin digitale 8
7 
8void setup()
9{
10  Serial.begin(9600);
11  pinMode(LED_BUILTIN, OUTPUT); // abilita il pin a cui è connesso il LED come output
12  pinMode(pinPulsante, INPUT);  // abilita il pin a cui è connesso il pulsante come input
13}
14void loop()
15{
16  while (true) // ciclo infinito
17  {
18    if (digitalRead(pinPulsante) == HIGH)
19    {
20      break; // esce dal ciclo se si preme il pulsante
21    }
22    lampeggio(); // chiama la funzione che accende e spegne il LED
23  }
24}
25void lampeggio()
26{
27  digitalWrite(LED_BUILTIN, HIGH);
28  delay(100);
29  digitalWrite(LED_BUILTIN, LOW);
30  delay(100);
31}

Come si può notare viene utilizzata l’istruzione break per uscire dal ciclo. Fino a quando il pulsante non sarà premuto la condizione: digitalRead(pinPulsante) == HIGH sarà falsa, pertanto non verrà eseguito il corpo dell’if e quindi non potrà essere eseguito il break, in questa condizione ciò che segue l’if è la chiamata della funzione lampeggia().

Nel caso in cui il pulsante viene premuto, la condizione digitalRead(pinPulsante) == HIGH risulta vera, pertanto viene eseguito il corpo dell’if che contiene l’istruzione break che causerà l’uscita dal ciclo while.

Il codice sopra indicato funziona anche se nel while viene sostituito true con HIGH:

1...
2while (true) // ciclo infinito
3  {
4       // corpo del while
5  }
6...

con

1...
2while (HIGH) // ciclo infinito
3  {
4       // corpo del while
5  }
6...

Come spiegato ad inizio lezione, un ciclo infinito può essere ottenuto anche con un’istruzione for quindi si ottiene il medesimo risultato se si utilizza il codice che segue.

Esempio 2

1/*
2  Prof. Michele Maffucci
3  Data: 16.11.2020
4  Funzionamento dell'istruzione break
5*/
6const int pinPulsante = 8; // pin digitale 8
7 
8void setup()
9{
10  Serial.begin(9600);
11  pinMode(LED_BUILTIN, OUTPUT); // abilita il pin a cui è connesso il LED come output
12  pinMode(pinPulsante, INPUT);  // abilita il pin a cui è connesso il pulsante come input
13}
14void loop()
15{
16  for (;;) // ciclo infinito
17  {
18    if (digitalRead(pinPulsante) == HIGH)
19    {
20      break; // esce dal ciclo se si preme il pulsante
21    }
22    lampeggio(); // chiama la funzione che accende e spegne il LED
23  }
24}
25void lampeggio()
26{
27  digitalWrite(LED_BUILTIN, HIGH);
28  delay(100);
29  digitalWrite(LED_BUILTIN, LOW);
30  delay(100);
31}

Differenze tra ciclo for e ciclo while

  • Il ciclo for ha necessità di una variabile al suo interno, mente il ciclo while può utilizzare qualsiasi variabile definita prima dell’istruzione while.
  • L’uso del ciclo do-while permette di realizzare sempre una volta il corpo delle istruzioni in esso contenuto (indipendentemente dall’espressione condizionale) e ciò non può essere fatto con un  ciclo for.

Esercizio per i miei studenti

Esercizio 1

Realizzare le stesse funzionalità dell’esempio precedente (usando un ciclo infinito nel loop()) aggiungendo la stampa sequenziale crescente di un numero n = n + 1 sulla Serial Monitor. Alla pressione del pulsante si interrompe il lampeggio del LED e si azzera il contatore.

Buon Coding a tutti 🙂

Arduino: strutturare il codice in blocchi funzionali

Molto spesso durante le prime fasi di realizzazione di uno sketch Arduino si tende a non strutturare il proprio programma in blocchi funzionali separati, tutto il codice viene inserito all’interno del loop(). Questo tipo di approccio, soprattutto se siamo in presenza di un codice molto lungo, rende difficoltosa la lettura del programma da parte di altre persone e non permette una facile comprensione del funzionamento o l’identificazione di possibili errori.

Ho parlato in passato come strutturare il codice in blocchi funzionali nel post:
Arduino – lezione 07: lavorare con gruppi di valori e funzioni esterne, ma in quella lezione utilizzavo istruzioni che in questa fase dell’anno scolastico non sono ancora conosciute da alcuni allievi soprattutto del 3′ anno, che iniziano ad utilizzare Arduino, pertanto la presente lezione dovrebbe chiarire in modo più semplice l’argomento.

Le funzioni vengono utilizzate per organizzare le azioni eseguite dal vostro programma. Ogni funzione può essere identificata da un’azione specifica richiamata dal programma principale loop(), oppure richiamate da altre funzioni sempre all’interno del nostro sketch.

Senza saperlo quando avete iniziato a programmare con Arduino avete utilizzato delle funzioni: loop() e setup() le due funzioni sempre presenti in ogni sketch.

Per creare una funzione bisogna:

  • definire il tipo di valore restituito dalla funzione;
  • assegnare un nome alla funzione;
  • e opzionalmente impostare una serie di parametri che la funzione riceverà quando viene chiamata (si dice anche invocata).

Creiamo una semplice funzione che permette di fare lampeggiare un LED, non possiede parametri e non restituisce nessun valore.
Assegnare alla funzione il tipo void vuol dire che non restituisce nulla e non inserire nulla tra le parentesi tonde indica che la funzione non accetta nessun parametro.

Esempio 1

1// Prof. Michele Maffucci
2// Es.01 - Usare le funzioni
3// Data: 15.11.2020
4 
5void setup() {
6  pinMode(LED_BUILTIN, OUTPUT);
7}
8 
9void loop() {
10  lampeggia();
11}
12 
13// blink LED una volta
14void lampeggia()
15{
16  digitalWrite(LED_BUILTIN, HIGH); // accende il LED
17  delay(1000); // pausa di 1 secondo
18  digitalWrite(LED_BUILTIN, LOW); // spegne il LED
19  delay(1000); // pausa di 1 secondo
20}

Ogni volta che il loop() chiama (invoca) la funzione esterna lampeggia() viene effettuato il blink del LED sulla scheda.

Esempio 2

Realizziamo ora un secondo esempio in cui la funzione “lampeggia” accetta come parametro un valore intero che definisce il delay da impostare all’interno del funzione.

1// Prof. Michele Maffucci
2// Es.02 - Usare le funzioni
3// Data: 15.11.2020
4 
5void setup() {
6  pinMode(LED_BUILTIN, OUTPUT);
7}
8 
9void loop() {
10  // chiamiamo la funzione lampeggia passando il valore
11  // specificato all'interno delle parentesi tonde
12  lampeggia(250);
13}
14 
15// blink LED una volta
16// per far si che la funzione accetti un parametro in input
17// bisogna dichiarare il tipo del parametro in ingresso
18// nella funzione lampeggia: int pausa
19 
20void lampeggia(int pausa)
21{
22  digitalWrite(LED_BUILTIN, HIGH); // accende il LED
23  delay(pausa); // pausa: valore inserito nella variabile "pausa"
24  digitalWrite(LED_BUILTIN, LOW); // spegne il LED
25  delay(pausa); // pausa: valore inserito nella variabile "pausa"
26}

Esempio 3

Realizziamo ora un terzo sketch in cui i parametri di ingresso per la funzione lampeggia() sono due, uno che definisce il tempo di accensione del LED ed uno che definisce il tempo di spegnimento del LED:

1// Prof. Michele Maffucci
2// Es.03 - Usare le funzioni
3// Data: 15.11.2020
4 
5void setup() {
6  pinMode(LED_BUILTIN, OUTPUT);
7}
8 
9void loop() {
10  // chiamiamo la funzione lampeggia passando il valore
11  // specificato all'interno delle parentesi tonde
12  lampeggia(250, 1000);
13}
14 
15// blink LED una volta
16// per far si che la funzione accetti duen parametri in input
17// bisogna dichiarare il tipo per ogni parametro in ingresso
18// nella funzione lampeggia: int pausaOn e int pausaOff
19 
20void lampeggia(int pausaOn, int pausaOff)
21{
22  digitalWrite(LED_BUILTIN, HIGH); // accende il LED
23  delay(pausaOn); // delay LED ON
24  digitalWrite(LED_BUILTIN, LOW); // spegne il LED
25  delay(pausaOff); // delay LED OFF
26}

Esempio 4

Definiamo ora una funzione che ha 3 parametri di ingresso: il tempo in cui il LED rimane acceso, il tempo in cui il LED rimane spento e il numero di volte (cicli) che deve ripetersi il lampeggio, al termine del numero di cicli il LED non lampeggerà più. Come specificato nei commenti la variabile chiave verrà utilizzata per eseguire una volta sola la chiamata della funzione lampeggio:

1// Prof. Michele Maffucci
2// Es.04 - Usare le funzioni
3// Data: 15.11.2020
4 
5// chiave e la variabile che consente l'esecuzione della
6// chiamata della funzione lampegga una sola volta
7int chiave = 0;
8 
9void setup() {
10  pinMode(LED_BUILTIN, OUTPUT);
11}
12 
13void loop() {
14  // chiamiamo la funzione lampeggia passando il valore
15  // specificato all'interno delle parentesi tonde
16  if (chiave == 0) {
17    chiave = 1;
18    lampeggia(250, 1000, 5);
19  }
20}
21 
22// blink LED una volta
23// per far si che la funzione accetti tre parametri in input
24// bisogna dichiarare il tipo per ogni parametro in ingresso
25// nella funzione lampeggia: int pausaOn, int pausaOff, int contatore
26// la variabile contatore definisce il numero di cicli di lampeggio
27 
28void lampeggia(int pausaOn, int pausaOff, int contatore)
29{
30  for (int i = 0; i < contatore; i++) {
31    digitalWrite(LED_BUILTIN, HIGH); // accende il LED
32    delay(pausaOn); // delay LED ON
33    digitalWrite(LED_BUILTIN, LOW); // spegne il LED
34    delay(pausaOff); // delay LED OFF
35  }
36}

All’interno dello sketch precedente la funzione lampeggio utilizza un ciclo for per ripetere, per il numero di volte specificato dalla variabile contatore, il codice di accensione e spegnimento (corpo del for).

Esempio 5

Per completezza e per richiamare il modo con cui utilizzare l’istruzione while, di seguito trovate lo sketch che realizza la medesima funzionalità dello sketch precedente in cui il ciclo for viene sostituito da un while:

1// Prof. Michele Maffucci
2// Es.05 - Usare le funzioni
3// Data: 15.11.2020
4 
5// chiave e la variabile che consente l'esecuzione della
6// chiamata della funzione lampegga una sola volta
7int chiave = 0;
8 
9void setup() {
10  pinMode(LED_BUILTIN, OUTPUT);
11}
12 
13void loop() {
14  // chiamiamo la funzione lampeggia passando il valore
15  // specificato all'interno delle parentesi tonde
16  if (chiave == 0) {
17    chiave = 1;
18    lampeggia(250, 1000, 5);
19  }
20}
21 
22// blink LED una volta
23// per far si che la funzione accetti tre parametri in input
24// bisogna dichiarare il tipo per ogni parametro in ingresso
25// nella funzione lampeggia: int pausaOn, int pausaOff, int contatore
26// la variabile contatore definisce il numero di cicli di lampeggio
27 
28void lampeggia(int pausaOn, int pausaOff, int contatore)
29{
30  while (contatore > 0) {
31     digitalWrite(LED_BUILTIN, HIGH); // accende il LED
32    delay(pausaOn); // delay LED ON
33    digitalWrite(LED_BUILTIN, LOW); // spegne il LED
34    delay(pausaOff); // delay LED OFF
35    contatore = contatore - 1; // decremento del contatore
36  }
37}

Esempio 6

Nello sketch che segue realizziamo una funzione che accetta un parametro e restituisce un valore. Il parametro che viene passato alla funzione definisce la durata dei tempi di accensione e spegnimento del LED (in millisecondi). La funzione continua a far lampeggiare un LED fino a quando non viene premuto un pulsante. Il valore restituito dalla funzione è il numero di lampeggi effettuati, questo valore verrà stampato sulla Serial Monitor:

1// Prof. Michele Maffucci
2// Es.06 - Usare le funzioni
3// Data: 15.11.2020
4 
5const int pinPulsante = 8; // pin a cui colleghiamo il pulsante
6 
7void setup() {
8  pinMode(LED_BUILTIN, OUTPUT);
9  pinMode(pinPulsante, INPUT);
10  Serial.begin(9600);
11}
12 
13void loop() {
14  Serial.println("Premere il pulsante per interrompere il lampeggio");
15  int contatore = lampeggia(500); // lampeggio del LED: 500 ms ON e 500 ms OFF
16  Serial.print("Il numero di volte in cui il LED ha lampeggiato è stato di: ");
17  Serial.println(contatore);
18  while (digitalRead(pinPulsante) == HIGH)
19  {
20    // non viene fatto nulla fino a quando non rilascio il pulsante
21  }
22}
23 
24// la funzione fa lampeggiare il LED per un periodo specificato (int periodo)
25// e restituisce il numero di volte in cui il LED ha lampeggiato
26 
27int lampeggia(int periodo)
28{
29  int contatoreLampeggio = 0;
30 
31  while (digitalRead(pinPulsante) == LOW)
32    // ripetere finché non viene premuto il pulsante
33    // cicla fino a quando il pulsante non viene premuto
34  {
35    digitalWrite(LED_BUILTIN, HIGH);
36    delay(periodo);
37    digitalWrite(LED_BUILTIN, LOW);
38    delay(periodo);
39    contatoreLampeggio = contatoreLampeggio + 1; // incrementa il contatore
40  }
41  // in questo punto vuol dire che pinPulsante è HIGH
42  // ciò vuol dire che il pulsante è premuto
43 
44  // contatoreLampeggio è il valore che viene restituito alla funzione chiamante
45  return contatoreLampeggio;
46}

Il tipo di dato che precede in nome della funzione:

int lampeggia()

indica il tipo di dato restituito dalla funzione. Ricordarsi che nella funzione chiamante, nel nostro caso il loop(), quando chiamiamo la funzione questa deve terminare con un punto e virgola:

1int contatore = lampeggia(500);

Errori comuni che vengono commessi nella chiamata di funzioni

Rimando a queste due brevi note che mostrano alcuni errori tipici:

Buon Making a tutti 🙂

Esercizi per i miei studenti

Esercizio 1

Realizzare un circuito costituito da 4 pulsanti e 4 led, connessi ad Arduino.

  • Alla pressione del pulsante P1 il LED1 effettua il blink con un tempo di 200 ms
  • Alla pressione del pulsante P2 il LED2 effettua il blink con un tempo di 400 ms
  • Alla pressione del pulsante P3 il LED3 effettua il blink con un tempo di 600 ms
  • Alla pressione del pulsante P4 il LED4 effettua il blink con un tempo di 800 ms

Fare in modo tale che ci sia una funzione esterna, chiamata dal loop(), che effettui il blink dei led.

Esercizio 2

Aggiungere all’esercizio 1 un pulsante che se premuto accende in sequenza ripetuta i 4 led. L’accensione in sequenza deve avvenire chiamando una funzione esterna al loop().

Arduino: tipi di dati – ripasso

Noto che durante le sperimentazioni condotte dagli allievi, l’uso dei tipi di dati provoca alcune incertezze, pertanto ho pensato di aggiungere a quanto già pubblicato in passato, una tabella riepilogativa da prendere come riferimento durante le eseritazioni aggiungendo inoltre qualche precisazione. Noto che nella dichiarazione di variabili la scelta più comune del tipo di dato utilizzato è l’int (intero), però molto spesso può essere non necessario o addirittura può aggiungere imprecisioni o errori.

Sebbene il tipo di dati int (abbreviazione di intero) sia la scelta più comune per valori numerici incontrati nelle applicazioni Arduino, è possibile utilizzare le tabelle che segue per determinare il tipo di dati più opportuno, che si adatta all’intervallo di valori previsto dal vostro programma. La tabella mostra i tipi di dati per le schede a 8 bit come Arduino UNO R3.

Tipo di dato

Byte

Intervallo

Uso

int 2 –32768 a 32767 Rappresenta valori interi positivi e negativi.
unsigned int 2 0 a 65535 Rappresenta solo valori interi positivi.
long 4 –2147483648 a 2147483647 Rappresenta un intervallo di interi negativi e positivi molto più estesa rispetto agli int.
unsigned long 4 0 a 4294967295 Rappresenta solo valori interi positivi con intervallo più ampio degli unsigned int.
float 4 3.4028235E+38 a –3.4028235E+38 Rappresenta numeri con la virgola, da utilizzare per rappresentare e approssimare valori che giungono dal mondo reale.
double 4 Come i float In Arduino, double ha lo stesso utilizzo e significato di float, un sinonimo (ciò non è vero per schede a 32 bit).
bool 1 TRUE (1) o FALSE (0) Rappresenta valori veri o falsi.
char 1 –128 a 127 Rappresenta un singolo carattere. Può anche rappresentare un intero con segno compreso tra –128 e 127.
byte 1 0 a 255 Simile al tipo char, ma per valori senza segno.
String Rappresenta una sequenza di caratteri tipicamente utilizzata per contenere il una stringa di testo.
void Utilizzato solo nelle dichiarazioni di funzione in cui non viene restituito alcun valore.

In generale, come potete vedere anche negli sketch di esempio presenti nell’IDE di Arduino, gran parte delle variabli utilizzate è di tipo int, inoltre se non è richiesta la massima efficienza della memoria di Arduino l’uso di int non è un errore, ma ovviamente il tipo int può essere utilizzato solo se i valori che state gestendo non superano l’intervallo degli int e non è necessario lavorare con numeri decimali.

Certamente molto spesso è necessario utilizzare un tipo di dato specifico per la propria applicazione, ciò accade quando ad esempio utilizziamo specifiche funzioni di libreria che restituiscono valori che non appartengono all’intervallo del tipo int.

A scopo di esempio considerate la funzione millis che restituisce il numero di millisecondi trascorsi dall’avvio del programma.

1unsigned long ora;
2 
3void setup() {
4  // inizializzazione della erial Monitor
5  Serial.begin(9600);
6}
7void loop() {
8  Serial.print("Ora: ");
9  ora = millis();
10 
11  Serial.println(ora); // stampa l'ora (in millisecondi) dall'avvio dello sketch
12  delay(1000);         // attesa di un secono, viene utilizzato per non stampare
13                       // velocemente una grande quantità di valori sulla Serial Monitor
14}

Il tipo di valore restituito, come indicato nel reference, è un unsigned long valori compresi tra 0 e 4294967295.

Se usate un tipo int su una scheda a 8 bit per memorizzare un valore restituito da millis() non riceverete un messaggio di errore dal compilatore, ma verranno restituiti valori numerici sbagliati perché un int non è abbastanza grande da contenere il valore massimo di un unsigned long. Se provate a cambiare tipo nello sketch indicato sopra vedrete che dopo aver raggiunto il valore 32.767, il conteggio passerà a -32768.

Se invece provate a sostituire un long, –2147483648 a 2147483647 con un
unsigned int (intero senza segno) 0 a 65535, tornerete a zero dopo aver superato il massimo massimo (65535).

Alcune precisazioni

  • Durante le vostre sperimentazioni avrete bisogno a volte di valori con segno e a volte no, ecco perché sono stati resi disponibili tipi di dati con segno (signed) e senza segno (unsigned). I valori senza segno sono sempre positivi;
  • tutti i tipi che NON recano davanti la parola: unsigned possono assumere valori negativi e positivi;
  • una variabile senza segno (unsigned) può memorizzare il doppio dei valori numerici di una variabile con segno (signed);
  • nella notazione è esplicitato sempre quando la variabile è di tipo unsigned perché in questo modo viene reso evdente che quella variabile non assume valori negativi.

Dalla tabella si può notare che i tipi bool (booleani) hanno due possibili valori: vero o falso. Questo tipo di dato viene utilizzato in genere per memorizzare valori che rappresentano una condizione ON/OFF oppure in altro modo SI/NO.

Potresete vedere tipi booleani utilizzati al posto delle costanti HIGH e LOW utilizzati all’interno ad esempio di una digitalWrite() per impostare l’uscita di un pin digitale, qundi le scritture:

1digitalWrite(pinLed,HIGH);
2e
3digitalWrite(pinLed,1);
4 
5digitalWrite(pinLed,LOW);
6e
7digitalWrite(pinLed,0);

Indicano la stessa cosa.

La stessa cosa accade per una digitalRead() nel caso in cui noi si debba leggere lo stato di un pin digitale. Potete qundi utilizzare true o false al posto di HIGH e LOW o ancora 1 o 0.

Esercizi per i miei studenti

Esercizio 1
Partendo dall’esempio indicato in questa lezione sulla stampa del valore restituito dalla funzione millis(), realizzare un programma che restituisca i valori di millis() memorizzati in tre variabili di tipo diverso:

1unsigned long ora1;
2int ora2;
3unsigned int ora3;

stampare il valore delle tre variabili sulla Serial Monitor per dimostrare quanto affermato in questa lezione.

Esercizio 2
Realizzare le stese funzionalità dell’esercizio precedete aggiungendo un pulsante che se premuto azzera le tre variabili e fa ripartire (partendo da zero) la stampa sulla Serial Monitor.

Esercizio 3
Realizzare le stesse funzionalità dell’eserizio 2 aggiungendo 3 LED, ciascuno indicherà per un tempo di accensione di 10 secondi il superamento del limite del range del tipo di variabile a cui si riferisce es.:

LED1 -> ora1
LED2 -> ora2
LED3 -> ora3

Buon Making a tutti 🙂

Arduino: utilizzo del metodo parseInt() per la conversione di un stringa di testo che rappresenta un numero in un numero

Nel primo esercizio della lezione: Arduino: Stepper 28BYJ-48 – AccelStepper library veniva chiesto di impostare i parametri di azionamento del motore passo paso da Serial Monitor, questa operazione può essere svolta usando la classe toInt(), ma in modo più efficace e semplice possiamo usare la classe parseInt(). In questa breve lezione un esempio di controllo del lampeggio di un LED da Serial Monitor mediante l’uso di parseInt().

È possibile ricevere numeri con più di una cifra utilizzando i metodi parseInt e parseFloat che semplificano l’estrazione di valori numerici da seriale. (Funziona anche con Ethernet e altri oggetti derivati dalla classe Stream)

Serial.parseInt() e Serial.parseFloat() leggono i caratteri seriali e restituiscono la loro rappresentazione numerica.

I caratteri non numerici prima del numero vengono ignorati e il numero termina con il primo carattere che non è una cifra numerica (o “.” Se si utilizza parseFloat). Se non ci sono caratteri numerici nell’input, le funzioni restituiscono 0, quindi bisogna controllare i valori zero e gestirli in modo appropriato.

Nel dettaglio

  • I caratteri iniziali che non sono cifre o sono numeri negativi vengono ignorati;
  • L’analisi si interrompe quando non sono stati letti caratteri per un valore di tempo di timeout che può essere configurato oppure viene letta una non cifra;
  • Se non sono state lette cifre valide quando si verifica il timeout (vedere Serial.setTimeout ()), viene restituito 0; Serial.parseInt () eredita dalla classe Stream.

Se avete la Serial Monitor configurata per inviare una nuova riga o un ritorno a capo (o entrambi) quando fate clic su invia, parseInt o parseFloat proveranno ad interpretare il return come numero, ma poiché il ritorno a capo non è un numero il valore restituito da parseInt o parseFloat sarà zero.

Nell’esempio che segue un invio imposta blinkRitardo a zero il che implica che il LED non lampeggia.

1// Prof. Maffucci Michele
2// 10.11.2020
3// Impostazione del delay del Blink da tastiera
4 
5int lampeggioRitardo = 0;
6int chiave = 0;
7void setup()
8{
9  Serial.begin(9600); // inizializzazione della serial monitor
10  pinMode(LED_BUILTIN, OUTPUT); // imposta il pin come output
11}
12void loop()
13{
14  // ritardo per evitare una doppia scrittura
15  // della prima stampa a monitor
16  delay(1000);
17 
18  // consente di visualizzare sulla Serial Monitor
19  // una sola stampa delle stringa
20  if (chiave == 0) {
21    Serial.print("Inserisci il ritardo in millisecondi: ");
22    chiave = 1;
23  }
24 
25  // Controlla se è disponibile almeno un carattere sulla seriale
26  // La Serial.available() restituisce
27  // 1 se presente un cattere,
28  // 0 se non è presente un carattere
29   
30  if (Serial.available())
31  {
32    int r = Serial.parseInt(); // in r viene memorizzato il valore inserito in numero
33    if (r != 0) {
34      lampeggioRitardo = r;
35      Serial.println(lampeggioRitardo);
36 
37      // abilita alla stampa di una nuova stringa:
38      // "Inserisci il ritardo in millisecondi: "
39      chiave = 0;
40    }
41  }
42  lampeggio(); // funzione che fa lampeggiare il LED su Arduino
43}
44 
45// il LED lampeggia con i tempi di
46// accensione e spegnimento determinati da lampeggioRitardo
47void lampeggio()
48{
49  digitalWrite(LED_BUILTIN, HIGH);
50  delay(lampeggioRitardo); // il delay dipende dal valore in lampeggioRitardo
51  digitalWrite(LED_BUILTIN, LOW);
52  delay(lampeggioRitardo);
53}

Esercizio 1
Dato un LED RGB connesso ad Arduino, realizzare un selettore che da Serial Monitor consente di controllare l’accensione e lo spegnimento del rosso, del verde e del blu

Esercizio 2
Svolgere l’esercizio 1 della lezione Stepper 28BYJ-48 – AccelStepper library usando la parseInt()