Archivi tag: funzioni

Arduino: stampare più funzioni sulla serial plotter

L’attività di progetto di questa mattina: progettazione di dispositivi elettronici che rispondono a diverse necessità per la salute umana: cura della persona, disabilità, sicurezza sul lavoro, strumenti medici/laboratorio, accessibilità dei luoghi pubblici, ecc…

Con mio piacere ne è nata un’intensa attività di ricerca che ha coinvolto l’intero gruppo classe. Molte le necessità e tra queste ne evidenzio uno, semplice, che riguarda la programmazione, che in più occasioni gli studenti affrontano durante le sperimentazioni e di cui mi sono reso conto di non avere documentato adeguatamente, ovvero: il plot di più funzioni su uno stesso piano cartesiano con Arduino.

Avevo mostrato in più occasioni come effettuare il plot di grandezze variabili nel tempo utilizzando la Serial Plotter dell’IDE di Arduino, uno degli ultimi articoli si riferisce alla realizzazione di uno strumento per la rilevazione di vibrazioni, un semplice sismografo realizzato con un sensore piezoelettrico.

Alcuni studenti questa mattina, si stavano cimentando nella progettazione di un guanto da utilizzare per la movimentazione di un braccio robot industriale della hyundai con l’obiettivo di simulare la movimentazione a distanza di sostanze chimiche pericolose. Nelle prime attività di ricerca si è manifestata la necessità di visualizzare su tre grafici diversi le componenti X, Y, Z dell’accelerazione fornite da un accelerometro connesso ad Arduino.

Come sicuramente saprete l’avvio della Serial Plotter avviene, così come per la Serial Monitor dal menù Tools. Ricordo che Serial Monitor e Serial Plotter non possono essere avviate contemporaneamente.

La Serial Plotter prende i valori che giungono dalla seriale (connessione USB) e li grafica su un piano cartesiano. I dati numerici vengono anche visualizzati nella parte in alto a sinistra della finestra della Serial Plotter.

I valori massimi e minimi rappresentati sull’asse Y vengono regolati automaticamente, mentre la dimensione dell’asse X è fissato a 500 punti e l’aggiornamento del grafico avviene ogni qual volta nel vostro sketch viene eseguita una Serial.println().

Ricordate inoltre di fissare il Baud Rate del Serial Plotter in modo che corrisponda a quello che avete indicato nel codice.

Per tracciare contemporaneamente forme d’onda è possibile agire in due modi:

inserendo uno spazio tra due istruzioni di stampa

Serial.print(temperatura);
Serial.print(" ");
Serial.println(umidita);

oppure inserendo una tabulazione tra due istruzioni di stampa

Serial.print(temperatura);
Serial.print("\t");
Serial.println(umidita);

In questo caso le due grandezze, temperatura e umidità, saranno rappresentate da due funzioni separate e tracciate contemporaneamente sullo stesso piano cartesiano.

A titolo di esempio consideriamo il mio post in cui mostravo come utilizzare un DHT11 e visualizzeremo sulla Serial Plotter le due grandezze fisiche temperatura ed umidità. Di seguito schema di collegamento, sketch e grafici.

// Prof. Maffucci Michele
// Visualizzazione della pressione e dell'umidità rilevata da un DHT11
// sulla Serial Plotter
// 16.09.2021

// Libreria DHT
#include "DHT.h"

// Pin digitale di arduino connesso al DHT
#define DHTPIN 2

// tipo del sensore: DHT 11
#define DHTTYPE DHT11

DHT dht(DHTPIN, DHTTYPE);

void setup() {
  Serial.begin(9600);
  dht.begin();
}

void loop() {
  // Attesa di 1 millisecondo prima di fornire la misura.
  delay(1);

  // Lettura dell'umidità
  float h = dht.readHumidity();
  // Lettura della temperatura in gradi Celsius
  float t = dht.readTemperature();

  // Verifica se le si presenta un errore di lettura (e riprova nuovamente)
  if (isnan(h) || isnan(t)) {
    Serial.println(F("Impossibile leggere dal sensore DHT!"));
    return;
  }

  // Stampa del valore dell'umidità
  Serial.print(h);
  Serial.print(' ');
  // Stampa del valore della temperatura
  Serial.println(t);
}

Il plot delle due grandezze è:

Per completezza realizziamo un semplice sketch che permette di rappresentare 3 funzioni sinusoidali sfasate di 90 gradi una rispetto all’altra con ampiezze diverse:

// Prof. Maffucci Michele
// Visualizzazione di tre funzioni siusoidali
// di ampiezza diversa e sfasate di 90 gradi
// 16.09.2021

void setup() {
  Serial.begin(9600);
}

void loop() {

  // M_PI è la macro definita nell'header math.h
  // che definisce il pi greco che ha il valore di:
  // 3.14159265358979323846
  for(int i = 0; i < 360; i += 2) {
    float ValoreY1 = 1 * sin(i * M_PI / 180);
    float ValoreY2 = 2 * sin((i + 90)* M_PI / 180);
    float ValoreY3 = 4 * sin((i + 180)* M_PI / 180);

    Serial.print(ValoreY1);
    Serial.print(' ');         // deve essere stampato spazio ' ' o  tab '\t' tra due valori.
    Serial.print(ValoreY2);
    Serial.print(' ');         // deve essere stampato spazio ' ' o  tab '\t' tra due valori.
    Serial.println(ValoreY3);  // l'ultimo valore deve avere un ritorno a capo

    delay(1);
  }
}

Buon Coding a tutti 🙂

BBC micro:bit – funzioni

Sto svolgendo in questi giorni il corso sulla didattica laboratoriale e nelle giornata di ieri ho svolto un approfondimento sull’uso delle funzioni con il MakeCode editor di micro:bit che rendo pubblico su questo sito.

NOTA. Gli esempi in questo post hanno il solo scopo di mostrare l’utilizzo delle funzioni (chiamata di una funzione, passaggio per valore, restituzione di un valore da una funzione, ecc…) e non quello di risolvere un specifico problema, pertanto gli esempi potrebbero essere realizzati in modalità sicuramente più efficiente.

Le funzioni con BBC micro:bit

Una funzione consente di creare una porzione di codice che possiamo riutilizzare più volte nel nostro programma, quindi invece di copiare lo stesso codice in molte sezioni del programma, possiamo semplicemente utilizzare un unico blocco funzione da utilizzare all’interno del nostro codice tutte le volte che ci necessita.

Una funzione è definita dal suo nome e dal corpo della funzione che ospita tutte le istruzioni.

La funzione ha un nome univoco e non può essere costituita da parole staccate tra loro. E’ utile assegnare alle funzioni nomi che specificano cosa fa la funzione, ad esempio “calcoloVolume”, “calcoloArea”, “distanzaOstacolo”, “impostaLed”, “displayOn”. Si consiglia inoltre di adottare una notazione camel case (testo a cammello), ovvero scrivere parole composte o frasi, come il nome di funzioni, unendo tutte le parole tra loro, ma lasciando le loro iniziali con lettera maiuscola, in questo modo viene meglio decodificato da un essere umano il significato del nome composto. E’ buona regola adottare una strategia di questo genere anche per il nome delle variabili.
La prima lettera della frase può essere maiuscola o minuscola, tendenzialmente si preferisce usare la lettera minuscola.

Il corpo della funzione è il codice all’interno del blocco funzione, quello che viene chiamato body.

Continua a leggere

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

// Prof. Michele Maffucci
// Es.01 - Usare le funzioni
// Data: 15.11.2020

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  lampeggia();
}

// blink LED una volta
void lampeggia()
{
  digitalWrite(LED_BUILTIN, HIGH); // accende il LED
  delay(1000); // pausa di 1 secondo
  digitalWrite(LED_BUILTIN, LOW); // spegne il LED
  delay(1000); // pausa di 1 secondo
}

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.

// Prof. Michele Maffucci
// Es.02 - Usare le funzioni
// Data: 15.11.2020

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  // chiamiamo la funzione lampeggia passando il valore
  // specificato all'interno delle parentesi tonde
  lampeggia(250);
}

// blink LED una volta
// per far si che la funzione accetti un parametro in input
// bisogna dichiarare il tipo del parametro in ingresso
// nella funzione lampeggia: int pausa

void lampeggia(int pausa)
{
  digitalWrite(LED_BUILTIN, HIGH); // accende il LED
  delay(pausa); // pausa: valore inserito nella variabile "pausa"
  digitalWrite(LED_BUILTIN, LOW); // spegne il LED
  delay(pausa); // pausa: valore inserito nella variabile "pausa"
}

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:

// Prof. Michele Maffucci
// Es.03 - Usare le funzioni
// Data: 15.11.2020

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  // chiamiamo la funzione lampeggia passando il valore
  // specificato all'interno delle parentesi tonde
  lampeggia(250, 1000);
}

// blink LED una volta
// per far si che la funzione accetti duen parametri in input
// bisogna dichiarare il tipo per ogni parametro in ingresso
// nella funzione lampeggia: int pausaOn e int pausaOff

void lampeggia(int pausaOn, int pausaOff)
{
  digitalWrite(LED_BUILTIN, HIGH); // accende il LED
  delay(pausaOn); // delay LED ON
  digitalWrite(LED_BUILTIN, LOW); // spegne il LED
  delay(pausaOff); // delay LED OFF
}

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:

// Prof. Michele Maffucci
// Es.04 - Usare le funzioni
// Data: 15.11.2020

// chiave e la variabile che consente l'esecuzione della
// chiamata della funzione lampegga una sola volta
int chiave = 0;

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  // chiamiamo la funzione lampeggia passando il valore
  // specificato all'interno delle parentesi tonde
  if (chiave == 0) {
    chiave = 1;
    lampeggia(250, 1000, 5);
  }
}

// blink LED una volta
// per far si che la funzione accetti tre parametri in input
// bisogna dichiarare il tipo per ogni parametro in ingresso
// nella funzione lampeggia: int pausaOn, int pausaOff, int contatore
// la variabile contatore definisce il numero di cicli di lampeggio

void lampeggia(int pausaOn, int pausaOff, int contatore)
{
  for (int i = 0; i < contatore; i++) {
    digitalWrite(LED_BUILTIN, HIGH); // accende il LED
    delay(pausaOn); // delay LED ON
    digitalWrite(LED_BUILTIN, LOW); // spegne il LED
    delay(pausaOff); // delay LED OFF
  }
}

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:

// Prof. Michele Maffucci
// Es.05 - Usare le funzioni
// Data: 15.11.2020

// chiave e la variabile che consente l'esecuzione della
// chiamata della funzione lampegga una sola volta
int chiave = 0;

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  // chiamiamo la funzione lampeggia passando il valore
  // specificato all'interno delle parentesi tonde
  if (chiave == 0) {
    chiave = 1;
    lampeggia(250, 1000, 5);
  }
}

// blink LED una volta
// per far si che la funzione accetti tre parametri in input
// bisogna dichiarare il tipo per ogni parametro in ingresso
// nella funzione lampeggia: int pausaOn, int pausaOff, int contatore
// la variabile contatore definisce il numero di cicli di lampeggio

void lampeggia(int pausaOn, int pausaOff, int contatore)
{
  while (contatore > 0) {
     digitalWrite(LED_BUILTIN, HIGH); // accende il LED
    delay(pausaOn); // delay LED ON
    digitalWrite(LED_BUILTIN, LOW); // spegne il LED
    delay(pausaOff); // delay LED OFF
    contatore = contatore - 1; // decremento del contatore
  }
}

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:

// Prof. Michele Maffucci
// Es.06 - Usare le funzioni
// Data: 15.11.2020

const int pinPulsante = 8; // pin a cui colleghiamo il pulsante

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(pinPulsante, INPUT);
  Serial.begin(9600);
}

void loop() {
  Serial.println("Premere il pulsante per interrompere il lampeggio");
  int contatore = lampeggia(500); // lampeggio del LED: 500 ms ON e 500 ms OFF
  Serial.print("Il numero di volte in cui il LED ha lampeggiato è stato di: ");
  Serial.println(contatore);
  while (digitalRead(pinPulsante) == HIGH)
  {
    // non viene fatto nulla fino a quando non rilascio il pulsante
  }
}

// la funzione fa lampeggiare il LED per un periodo specificato (int periodo)
// e restituisce il numero di volte in cui il LED ha lampeggiato

int lampeggia(int periodo)
{
  int contatoreLampeggio = 0;

  while (digitalRead(pinPulsante) == LOW)
    // ripetere finché non viene premuto il pulsante
    // cicla fino a quando il pulsante non viene premuto
  {
    digitalWrite(LED_BUILTIN, HIGH);
    delay(periodo);
    digitalWrite(LED_BUILTIN, LOW);
    delay(periodo);
    contatoreLampeggio = contatoreLampeggio + 1; // incrementa il contatore
  }
  // in questo punto vuol dire che pinPulsante è HIGH
  // ciò vuol dire che il pulsante è premuto

  // contatoreLampeggio è il valore che viene restituito alla funzione chiamante
  return contatoreLampeggio;
}

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:

int 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().