Archivi categoria: arduino

Arduino – Sensore di temperatura digitale DS18B20 utilizzo in modalità singola

Nelle attività di laboratorio di Sistemi e TPSEE usiamo molto spesso il sensore di temperatura TMP36, ma un’alternativa interessante al TPM36 è il sensore temperatura digitale DS18B20 utilizzato in modalità diversa dal TPM36.
Questo tipo di sensore è adatto per misure di temperatura ambiente, ma anche del terreno o per rilevare temperature nei liquidi.
Il sensore di solito è disponibile in due form factor, uno che viene fornito nel package TO-92, forma ampiamente utilizzato per i  transistor, mentre un’altro, quello utilizzato da me è inserito all’interno di una sonda impermeabile a forma di siluro, utilissimo quando si ha la necessità di misurare temperature sottoterra, sott’acqua, o lontane dal microcontrollore.

Particolarmente utile inoltre in queste settimane in cui i miei studenti di 4′ automazione stanno realizzando come progetto di PCTO una serra idroponica in cui è essenziale misurare la temperatura dell’acqua del sistema.

Il DS18B20 è abbastanza preciso può misurare temperature da -55 ° C a + 125 ° C con una precisione di ± 0,5 ° C.

Il sensore si basa sul protocollo OneWire introdotto da Dallas Semiconductor ora Maxim e richiede due librerie. La prima è la libreria e la DallasTemperature di Miles Burton, la seconda è la libreria OneWire.
La prima libreria da installare è la DallasTemperature di Miles Burton. Sono disponibili diverse librerie con il nome OneWire, quella che vi consiglio di utilizzare è la Wire Library di Jim Studt, Tom Pollard e altri.

Noterete comunque che durante l’installazione della libreria DallasTemperature vi verrà chiesto in automatico se volete installare anche la OneWire, procedete installando in automatico entrambe le librerie. Leggete attentamente le fasi descritte di seguito in cui riporto tutte le schermate delle fasi di installazione.

Il collegamento è estremamente semplice, come indicato nell’immagine che segue collegate il cavo rosso a 5V o a 3,3V su schede a 3,3V, il cavo nero va connesso al GND e il cavo del segnale “Out”, in genere di colore giallo, bianco o di altro colore, collegatelo ad un pin digitale, nell’esempio viene connesso al pin 2 con una resistenza da 4,7 K Ohm tra il segnale e il pin di alimentazione (5 V o 3,3 V).

Pinout

GND: collegamento al ground
Out: Bus Dati 1-Wire, deve essere collegato a un pin digitale sul microcontrollore.
Vdd: da collegare alla tensione di alimentazione (3,3 – 5 V)

Per tutti i dati tecnici del sensore vi rimando al datasheet.

Schema di collegamento

Circuito – sensore con package TO-92

Circuito – sonda impermeabile

Installazione delle librerie

Il protocollo Dallas OneWire è piuttosto complesso e per nascondere questa complessità installeremo  la libreria DallasTemperature.h in modo da poter impartire semplici comandi per ottenere letture di temperatura dal sensore.

Per installare la libreria, Sketch > Include Library > Manage Libraries

Inserite nel campo di ricerca: ds18b20. Dovrebbero esserci un paio di voci. Tra le varie possibilità selezionate, quella indicata nell’immagine

Il sensore per poter comunicare ha necessità anche della libreria OneWire, libreria che non è specifica per questo sensore, ma viene utilizzata con tutti i dispositivi che utilizzano il protocollo One Wire.

Nel momento in cui installate la prima libreria della Dallas Semiconductor viene chiesto con una finestra aggiuntiva se volete installare anche la libreria OneWire fate click su Install all.

Nel caso in cui avete dimenticato di installare la libreria OneWire potrete cercare sempre attraverso il Manage Libraries inserendo nel campo di ricerca

Esempio 01

Stampa della temperatura rilevata in gradi Celsius.
Il funzionamento del codice è dettagliato nei commenti.

/* 
 *  Prof. Maffucci Michele
 *  data: 24.02.2021
 *  Utilizzo del sensore di temperatura
 *  resistente all'acqua DS18B20
 *  
 *  Sketch 01: stampa delle temperature rilevate
 *  in gradi Celsius 
*/

// librerie per il funzionamento del sensore
#include <OneWire.h>
#include <DallasTemperature.h>

#define ONE_WIRE_BUS 2 // Pin Arduino a cui colleghiamo il pin DQ del sensore
const int pinLed = LED_BUILTIN; // Utilizzo del LED su scheda 

const int soglia = 25; // Accende il LED su scheda se si superano i 25°C

OneWire oneWire(ONE_WIRE_BUS); // Imposta la connessione OneWire

DallasTemperature sensore(&oneWire); // Dichiarazione dell'oggetto sensore

void setup(void)
{
  Serial.begin(9600);       // Inizializzazione della serial monitor
  sensore.begin();          // Inizializzazione del sensore
  pinMode(pinLed, OUTPUT);  // pinLed definito come OUTPUT

  // Stampa del messaggio di avvio
  Serial.println("Temperatura rilevata dal sensore DS18B20");
  Serial.println("----------------------------------------");
  delay(1000);
}

void loop()
{
  sensore.requestTemperatures(); // richiesta lettura temperatura
  
  // Restituzione della temperatura letta
  // in gradi Celsius

  // temperatura in Celsius
  float celsius = sensore.getTempCByIndex(0);
  
  // Visualizzazione delle letture 
  // della temperatura sulla Serial monitor
  
  Serial.print("C:");
  Serial.println(celsius);

  // se la temperatura è oltre la soglia
  // viene accesso il LED su scheda
  // altrimenti rimane spento
  
  if (celsius > soglia) {
    digitalWrite(pinLed, HIGH);
  } else {
    digitalWrite(pinLed, LOW);
  }
  // stampe delle temperature ogni secondo
  delay(1000);
}

All’interno del loop, lo sketch richiede una lettura della temperatura, quindi legge la temperatura in Celsius. Notare che non è necessario eseguire alcuna conversione aritmetica su
i risultati ottenuti dal sensore, tutto viene gestito dalla libreria. Tenete in conto che non è necessario apportare modifiche al codice, ma assicurati di cablare correttamente l’alimentazione del sensore, 3,3 V su schede che funzionano a questa tensione di riferimento o a 5V per schede che funzionano a 5V.

Esempio 02

Stampa della temperatura rilevata in gradi Celsius e Fahrenheit
Il funzionamento del codice è dettagliato nei commenti.

/* 
 *  Prof. Maffucci Michele
 *  data: 24.02.2021
 *  Utilizzo del sensore di temperatura
 *  resistente all'acqua DS18B20
 *  
 *  Sketch 02: stampa delle temperature rilevate
 *  in gradi Celsius e gradi Fahrenheit 
*/

// librerie per il funzionamento del sensore
#include <OneWire.h>
#include <DallasTemperature.h>

#define ONE_WIRE_BUS 2 // Pin Arduino a cui colleghiamo il pin DQ del sensore
const int pinLed = LED_BUILTIN; // Utilizzo del LED su scheda 

const int soglia = 25; // Accende il LED su scheda se si superano i 25°C
// La soglia di 25°C corrisponde a 77 °F
// Formula di conversione (Tc°C × 9/5) + 32 = Tf°F
// dove:
// Tc: temperatura in gradi Celsius
// Tf: temperatura in gradi Fahrenheit

OneWire oneWire(ONE_WIRE_BUS); // Imposta la connessione OneWire

DallasTemperature sensore(&oneWire); // Dichiarazione dell'oggetto sensore

void setup(void)
{
  Serial.begin(9600);       // Inizializzazione della serial monitor
  sensore.begin();          // Inizializzazione del sensore
  pinMode(pinLed, OUTPUT);  // pinLed definito come OUTPUT

  // Stampa del messaggio di avvio
  Serial.println("Temperatura rilevata dal sensore DS18B20");
  Serial.println("----------------------------------------");
  delay(1000);
}

void loop()
{
  sensore.requestTemperatures(); // richiesta lettura temperatura
  
  // Restituzione della temperatura letta
  // in gradi Celsius e gradi Fahrenheit

  // temperatura in Celsius
  float celsius = sensore.getTempCByIndex(0);

  // temperatura in Fahrenheit
  float fahrenheit = sensore.getTempFByIndex(0);
  
  // Visualizzazione delle letture 
  // della temperatura sulla Serial monitor
  
  Serial.print("C:");
  Serial.print(celsius);
  Serial.print(",");
  Serial.print(" F:");
  Serial.println(fahrenheit);

  // se la temperatura è oltre la soglia
  // viene accesso il LED su scheda
  // altrimenti rimane spento
  
  if (celsius > soglia) {
    digitalWrite(pinLed, HIGH);
  } else {
    digitalWrite(pinLed, LOW);
  }
  // stampe delle temperature ogni secondo
  delay(1000);
}

Il codice è molto simile al precedente, all’interno del loop, lo sketch richiede una lettura della temperatura, quindi legge la temperatura in Celsius, poi Fahrenheit.

Sono disponibili alcune utili funzioni che possono essere usate con l’oggetto DallasTemperature di seguito il dettaglio di alcune di esse:

La funzione setResolution() function imposta la risoluzione del convertitore Analogico-Digitale del DS18B20 a 9, 10, 11, or 12-bit, che corrispondono ad incrementi di  0.5°C, 0.25°C, 0.125°C e  0.0625°C.

La funzione setHighAlarmTemp() e setLowAlarmTemp () imposta gli allarmi interni di alta e bassa temperatura in gradi Celsius. L’intervallo valido è compreso tra -55 e 125 ° C

La funzione bool hasAlarm () restituisce true se il sensore rileva una condizione di superamento dei limiti minimo o massimo di temperatura.

Esercizio 1

  • Rilevare ogni 2 secondi 10 misurazioni, memorizzare questi valori in un Array
  • Visualizzare la temperatura minima, media e massima ogni 10 secondi su un display 16×2 I2C
  • Se la temperatura scende sotto il valore minimo o supera il valore massimo impostati viene emesso un allarme (messaggio su display, emissione di un suono, accensione di un LED rosso per il superamento del valore massimo, azzurro se la temperatura scende al di sotto del valore minimo impostato)

Esercizio 2

Realizzare le medesime funzionalità dell’esercizio precedente aggiungendo:

  • Ridurre la luminosità del display dopo 15 secondi di funzionamento
  • Alla pressione di un pulsante viene attivata la retroilluminazione del display
  • Se viene attivato un allarme si attiva la retroilluminazione

Nella prossima lezione vedremo come collegare sullo stesso bus più sensori DS18B20.

Buon Coding a tutti 🙂

Errori comuni nell’uso di Arduino – ordine esecuzione operazioni matematiche e overflow nei calcoli


In realtà l’errore che viene commesso non è di carattere informatico, ma puramente matematico, dimenticando l’ordine con cui vengono eseguite le operazioni matematiche.
L’ordine delle operazioni segue le regole di base: moltiplicazioni e divisioni hanno precedenza massima seguono addizioni e sottrazioni. Se si vuole cambiare l’ordine di precedenza bisogna utilizzare le parentesi. Vediamo alcuni esempi.

int valore = 1 + 2 * 3 + 4;

il risultato sarà 11.

// Prof. Michele Maffucci
// Data: 08.02.2020
// Esempio 01: Ordine di esecuzione operazioni matematiche in C

// per stampare una sola volta il messaggio sulla Serial Monitor  
bool abilitaMessaggio = 0;

void setup() {
  // inizializzazione della comunicazione seriale
  Serial.begin(9600);
}

void loop() {
  // consente di visualizzare sulla Serial Monitor
  // una sola stampa delle stringa
  if (abilitaMessaggio == 0) {
    // ritardo che evita la doppia stampa del messaggio 
    delay(200);
    Serial.println("Calcolo:");
    Serial.println("valore = 1 + 2 * 3 + 4");
    int valore = 1 + 2 * 3 + 4;
    Serial.print("valore = ");
    Serial.println(valore);
    abilitaMessaggio = 1;
  }
}

Per rendere più evidente la sequenza di esecuzione del calcolo possiamo usare le parentesi, pertanto otterremo:

int valore = 1 + (2 * 3) + 4;

Che fornisce sempre il valore 11.

// Prof. Michele Maffucci
// Data: 08.02.2020
// Esempio 02: Ordine di esecuzione operazioni matematiche in C

// per stampare una sola volta il messaggio sulla Serial Monitor  
bool abilitaMessaggio = 0;

void setup() {
  // inizializzazione della comunicazione seriale
  Serial.begin(9600);
}

void loop() {
  // consente di visualizzare sulla Serial Monitor
  // una sola stampa delle stringa
  if (abilitaMessaggio == 0) {
    // ritardo che evita la doppia stampa del messaggio 
    delay(200);
    Serial.println("Calcolo:");
    Serial.println("valore = 1 + (2 * 3) + 4");
    int valore = 1 + (2 * 3) + 4;
    Serial.print("valore = ");
    Serial.println(valore);
    abilitaMessaggio = 1;
  }
}

Per modificare la precedenza utilizziamo le parentesi:

int valore = ((1 + 2) * 3) + 4;

il risultato sarà 13. Viene eseguito prima il calcolo della parentesi più interna (1+2), poi si passa alla parentesi immediatamente successiva, quindi (3 * 3) e poi il risultato viene sommato a 4.

// Prof. Michele Maffucci
// Data: 08.02.2020
// Esempio 03: Ordine di esecuzione operazioni matematiche in C

// per stampare una sola volta il messaggio sulla Serial Monitor  
bool abilitaMessaggio = 0;

void setup() {
  // inizializzazione della comunicazione seriale
  Serial.begin(9600);
}

void loop() {
  // consente di visualizzare sulla Serial Monitor
  // una sola stampa delle stringa
  if (abilitaMessaggio == 0) {
    // ritardo che evita la doppia stampa del messaggio 
    delay(200);
    Serial.println("Calcolo:");
    Serial.println("valore = ((1 + 2) * 3) + 4");
    int valore = ((1 + 2) * 3) + 4;
    Serial.print("valore = ");
    Serial.println(valore);
    abilitaMessaggio = 1;
  }
}

Ovviamente, come già spiegato precedentemente, bisognerà sempre fare attenzione che il risultato faccia parte del tipo di dati giusto, ad esempio quando effettuate una divisione tra interi il cui risultato è un numero decimale, o ancora se superate il valore massimo del tipo di dato che state utilizzando. In entrambi i casi il compilatore non vi segnalerà nessun errore.

Vediamo un esempio:

// 60 secondi in un minuto, 60 minuti in un'ora, 24 ore in un giorno
long secondi_in_un_giorno = 60 * 60 * 24;

In teoria, poiché il risultato è 86.400, questo valore potrà essere contenuto in un tipo long.
Ma in realtà il valore realmente memorizzato in “secondi_in_un_giorno” è 20.864.
86.400  supera più di due volte la dimensione di un intero, il calcolo fatto dal compilatore sarà il seguente:
86.400 – 32.768 * 2 = 20.864

// Prof. Michele Maffucci
// Data: 08.02.2020
// Esempio 04: Ordine di esecuzione operazioni matematiche in C
//             errore di calcolo dovute al tipo del dato (dimensione massima).

// per stampare una sola volta il messaggio sulla Serial Monitor  
bool abilitaMessaggio = 0;

void setup() {
  // inizializzazione della comunicazione seriale
  Serial.begin(9600);
}

void loop() {
  // consente di visualizzare sulla Serial Monitor
  // una sola stampa delle stringa
  if (abilitaMessaggio == 0) {
    // ritardo che evita la doppia stampa del messaggio 
    delay(200);
    Serial.println("Calcolo (errato) numero di secondi in un giorno:");
    Serial.println("secondi_in_un_giorno = 60 * 60 * 24");
    long secondi_in_un_giorno = 60 * 60 * 24;
    Serial.print("Secondi in un giorno = ");
    Serial.println(secondi_in_un_giorno);
    Serial.println("Errore! Il valore doveva essere: 86.400");
    Serial.println("L'errore si verifica perchè il compilatore considera i numeri di tipo int.");
    abilitaMessaggio = 1;
  }
}

Ciò accade perché il compilatore C dell’IDE di Arduino vede un’espressione aritmetica composta da soli numeri interi e quindi considera il risultato come tipo int. Per evitare questo problema bisogna dire al compilatore che deve trattare l’intera espressione come un long aggiungendo L al primo valore che viene valutato nell’espressione:

long secondi_in_un_giorno = 60L * 60 * 24;
// Prof. Michele Maffucci
// Data: 08.02.2020
// Esempio 05: Ordine di esecuzione operazioni matematiche in C
//             Uso corretto del tipo long.

// per stampare una sola volta il messaggio sulla Serial Monitor  
bool abilitaMessaggio = 0;

void setup() {
  // inizializzazione della comunicazione seriale
  Serial.begin(9600);
}

void loop() {
  // consente di visualizzare sulla Serial Monitor
  // una sola stampa delle stringa
  if (abilitaMessaggio == 0) {
    // ritardo che evita la doppia stampa del messaggio 
    delay(200);
    Serial.println("Calcolo corretto del numero di secondi in un giorno:");
    Serial.println("secondi_in_un_giorno = 60L * 60 * 24");
    long secondi_in_un_giorno = 60L * 60 * 24;
    Serial.print("Secondi in un giorno = ");
    Serial.println(secondi_in_un_giorno);
    Serial.println("Giusto! Abbiamo detto con la L che l'intera espressione è da trattate come un long.");
    Serial.println("");
    abilitaMessaggio = 1;
  }
}

Attenzione sempre alle parentesi!
Se le utilizzate ad esempio come nell’esempio che segue farà andare in overflow il risultato:

long secondi_in_un_giorno_piu_uno = 1L + 60 * (60 * 24);
// Prof. Michele Maffucci
// Data: 08.02.2020
// Esempio 06: Ordine di esecuzione operazioni matematiche in C
//             L'uso non corretto delle parentesi fa andare in overflow il risultato.

// per stampare una sola volta il messaggio sulla Serial Monitor  
bool abilitaMessaggio = 0;

void setup() {
  // inizializzazione della comunicazione seriale
  Serial.begin(9600);
}

void loop() {
  // consente di visualizzare sulla Serial Monitor
  // una sola stampa delle stringa
  if (abilitaMessaggio == 0) {
    // ritardo che evita la doppia stampa del messaggio 
    delay(200);
    Serial.println("Calcolo errato somma 1 al numero di secondi in un giorno");
    Serial.println("secondi_in_un_giorno_piu_uno = 1L + 60 * (60 * 24)");
    long secondi_in_un_giorno_piu_uno = 1L + 60 * (60 * 24);
    Serial.print("Secondi in un giorno + 1 = ");
    Serial.println(secondi_in_un_giorno_piu_uno);
    Serial.println("Sbagliato! Attenzione sempre alle parentesi!");
    Serial.println("Se le utilizzate ad esempio come indicato farà andare in overflow il risultato");
    abilitaMessaggio = 1;
  }
}

mentre la seguente espressione non farà andare in overflow il calcolo:

long secondi_in_un_giorno_piu_uno = 1 + 60 * (60L * 24);
// Prof. Michele Maffucci
// Data: 08.02.2020
// Esempio 07: Ordine di esecuzione operazioni matematiche in C
//             L'ordine del calcolo viene stabilito dalle parentesi, in questo modo
//             il calcolo non farà andare in overflow il risultato.

// per stampare una sola volta il messaggio sulla Serial Monitor  
bool abilitaMessaggio = 0;

void setup() {
  // inizializzazione della comunicazione seriale
  Serial.begin(9600);
}

void loop() {
  // consente di visualizzare sulla Serial Monitor
  // una sola stampa delle stringa
  if (abilitaMessaggio == 0) {
    // ritardo che evita la doppia stampa del messaggio 
    delay(200);
    Serial.println("Calcolo errato somma 1 al numero di secondi in un giorno");
    Serial.println("secondi_in_un_giorno_piu_uno = 1 + 60 * (60L * 24)");
    long secondi_in_un_giorno_piu_uno = 1 + 60 * (60L * 24);
    Serial.print("Secondi in un giorno + 1 = ");
    Serial.println(secondi_in_un_giorno_piu_uno);
    Serial.println("Corretto! Il calcolo inizierà dalle parentesi tonde.");
    Serial.println("E' stata aggiunta la L al primo operando tra le parentesi tonde.");
    abilitaMessaggio = 1;
  }
}

Buon Coding a tutti 🙂

Arduino – Utilizzo dell’LCD1602 Keypad Shield della Keyestudio

Questo post è dedicato ai miei allievi Paolo e Sami 🙂 della 3B Automazione, che amano i videogiochi platform a cui ho assegnato un’attività di PCTO in cui è richiesto appunto la progettazione di un gioco elettronico di tipo platform con Arduino.
A tal proposito, per ridurre i tempi di prototipazione verrà usato un LCD Keypad Shield della Keystudio, il tutto verrà poi inserito in un contenitore stampato in 3D, sempre progettato dai due studenti e che dovrà ricordare un Game Boy.

Per le specifiche tecniche della scheda seguire il link allegato, seguono alcune indicazioni  della scheda ricavati dalla pagina del prodotto ed alcuni link a risorse per la produzione di semplici videogiochi con Arduino.

La shield della Keyestudio integra su di esso un display  LCD 1602 e sei pulsanti ed si inserisce su qualdsiasi scheda Arduino UNO R3 compatibile. Il display può comunicare con la scheda Arduino in due modi: ad 8 bit o a 4 bit, la connessione predefinita del display è a 4 bit. Come riportato nell’immagine che segue, al di sotto del display sono presenti  5 pulsanti di controllo (Seleziona, Su, Giù, Sinistra, Destra) e un pulsante di reset, che è collegato direttamente al reset della scheda Arduino.

I 5 pulsanti sono collegati all’ingresso analogico A0, quindi potrete monitorare lo stato dei pulsanti utilizzando un solo ingresso analogico di Arduino. Attenzione però che potrete monitorare solamente la pressione di un pulsante alla volta, quaindi la pressione contemporanea di due o più pulsanti non potrà essere identificata. La shield è inoltre dotata di un trimmer che permette la regolazione della retroilluminazione dell’LCD.

Nel caso si abbia la necessità di utilizzare i restanti pin di Arduino, non utilizzati per il controllo del display, è possibile saldare sulla scheda pin passanti femmina.

Nello sketch che segue, modificato leggermente rispetto a quello usato da Keystudio, la pressione di ogni singolo pulsante, ad esclusione del reset, corrisponderà ad uno specifico valore restituito dall’analogRead sul pin A0, la pressione di ogni pulsante restituirà un valore numerico all’interno di un intervallo specificato, così come indicato nell’immagine precedente, ciò permetterà quindi di intercettare quale pulsante è stato premuto.
I valori dell’analogRead corrispondenti al pulsante premuto verranno visualizzati anche sulla SerialMonitor.

Nei commenti nel codice la spiegazione di ogni sezione.

/*
 * Prof. Michele Maffucci
 * Utilizzo dell'LCD Keypad Shield della Keystudio
 * Data: 08.02.2021
 */
 
// inclusione della libreria LiquidCrystal.h
#include <LiquidCrystal.h>

// inizializza la libreria con i numeri dei pin dell'interfaccia
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

void setup() {
  // impostazione del numero di colonne e righe del display
  lcd.begin(16, 2);
  Serial.begin(9600);
  // Stampa 5 puntini per dare la sensazione di avvio programma
  for (int i = 0; i < 4; i++) {
    lcd.print(".");
    delay(250);
  }
  // Posiziona il cursore in colonna 0 e riga 0
  lcd.setCursor(0, 0);
  // Stampa il messaggio
  lcd.print("Salve Mondo!");
}

void loop() {
  // Posiziona il cursore in colonna 0 e riga 0
  lcd.setCursor(0, 1);
  // Stampa il numero di secondi dall'avvio
  lcd.print(millis() / 1000);

  // Memorizza in val il valore presente su A0
  int val = analogRead(A0);

  // Stampa il valore di val sulla Serial Monitor
  Serial.println(val);

  // In funzione del pulsante premuto val assumerà valori diversi
  if (val >= 0 && val <= 50)
  {
    lcd.setCursor(5, 1);
    lcd.print("Destra  ");
  }
  else if (val >= 50 && val <= 150)
  {
    lcd.setCursor(5, 1);
    lcd.print("Su'     ");
  }
  else if (val >= 150 && val <= 300)
  {
    lcd.setCursor(5, 1);
    lcd.print("Giu'    ");
  }
  else if (val >= 300 && val <= 500)
  {
    lcd.setCursor(5, 1);
    lcd.print("Sinistra");
  }
  else if (val >= 500 && val <= 750)
  {
    lcd.setCursor(5, 1);
    lcd.print("Set     ");
  }
}

Di seguito alcuni link a giochi in cui viene sfruttato un l’LCD 1602 e in cui potreste utilizzata la shield della Keyestudio.

Nel caso in cui fosse necessario realizzare caratteri o icone personalizzate consultare: Disegnare caratteri personalizzati con Arduino per un LCD 16×2

Buon divertimento 🙂

Arduino: controllare il movimento di un motore a spazzola con un transistor

Abbiamo visto a lezione in cosa consiste la Modulazione di Larghezza di Impulso, in altro modo conosciuta come PWM (Pulse Width Modulation). Questo tipo di modulazione permette ad un sistema digitale di controllare dispositivi di tipo analogico in corrente continua facendo variare in modo continuo la potenza erogata, quindi moduliamo la larghezza di un impulso, ovvero la durata temporale di una serie di impulsi che regolerà l’attivazione e disattivazione del motore. Con il PWM agiamo non sulla tensione di alimentazione per controllare la velocità del motore, ma bensì sul tempo, per maggiori informazioni vi rimando alle slide: Alfabeto Arduino – Lezione 2 in cui come esempio viene controllata l’intensità luminosa di LED. (Per i miei studenti fate riferimento al libro di testo e agli appunti di teoria).

Il transistor nell’immagine che segue viene fatto funzionare in modalità ON-OFF e quindi  possiamo assimilarlo ad un interruttore che si apre e si chiude in corrispondenza del segnale presente sulla base del transistor. Se il segnale sulla base è alto il transistor sarà in conduzione e il motore potrà essere alimentato (si chiude verso massa il collegamento del motore). Se il segnale sulla base del transistor è a livello basso il transistor è assimilabile ad un interruttore aperto per cui il motore risulta non alimentato (non si ha collegamento a massa). Quindi al motore verrà applicata una tensione continua proporzionale al duty cycle e quindi variando il duty cycle varieremo la velocità di rotazione del motore.

La generazione dell’onda quadra, che controlla la velocità del motore, viene eseguita da Arduino. Il diodo presente nel circuito è chiamato diodo di ricircolo ed è inserito in parallelo al motore (carico induttivo) per sopprimere i transienti elettrici ed è collegato in modo tale che non conduce quando il carico (il motore) viene alimentato.
Quando il motore viene disattivato rapidamente si avrà un picco di tensione in senso inverso perché l’induttore tenderà a mantenere costante la corrente che circola al suo interno, in questa situazione però il diodo sarà polarizzato direttamente e tutta la corrente scorrerà dall’induttore al diodo e l’energia che era stata immagazzinata dall’induttore  viene dissipata in calore dalla componente resistiva dell’induttore. Inserendo il diodo si eviterà di applicare una sovratensione sul collettore del transistor evitando quindi che si danneggi.

Controlliamo con Arduino il movimento del motore

Lista Componenti

  • Arduino UNO R3
  • Resistore da 1 K Ohm
  • Dido: 1N4001
  • Transistor: 2N2222
  • Condensatore: 0,1 microF

Pinout transistor 2N2222

Circuito 1 di montaggio: alimentazione del motore prelevata da Arduino

Circuito 2 di montaggio: alimentazione del motore esterna ad Arduino

Esempio 1
Si faccia riferimento al circuito 1. Realizziamo uno sketch che permette la regolazione della velocità del motore inserendo un numero compreso tra 0 e 9. Il valore 0 ferma il motore, il valore 9 permette di muovere il motore alla velocità massima, valori intermedi movimenteranno il motore ad una velocità proporzionale al numero inserito.

Valori fuori dall’intervallo impostato restituiscono un messaggio di errore.

Per la realizzazione di questo sketch si utilizza la funzione isDigit() che restituisce TRUE verifica se il carattere inviato sulla Serial Monitor è un numero, altrimenti restituisce FALSE.

/*
   Prof. Maffucci Michele
   Controllo motore a spazzola:
   accensione, spegnimento, controllo velocità.
   All'avvio del programma il motore è spento.

   La velocità del motore è impostata
   con un valore compreso tra 0 e 9

   Valori non numerici restituiscono
   un messaggio di errore
   
   Data: 19.01.2021
*/

// driver del motore collegato al pin 6
const byte pinMotore = 6; // motor driver is connected to pin 3

// per stampare una sola volta il messaggio sulla Serial Monitor  
bool abilitaMessaggio = 0;

void setup()
{
  // inizializzazione della serial monitor
  Serial.begin(9600);

  // inizializzazione OUTPUT del pin 6
  pinMode(pinMotore, OUTPUT);
}

void loop()
{
  // consente di visualizzare sulla Serial Monitor
  // una sola stampa delle stringa
  if (abilitaMessaggio == 0) {
    // ritardo che evita la doppia stampa del messaggio 
    delay(200);
    Serial.println("Controllo velocità motore");
    Serial.println("Inserire la velocità (0 - 9)");
    Serial.println();
    abilitaMessaggio = 1;
  }

  // Controlla se è disponibile almeno un carattere sulla seriale
  // La Serial.available() restituisce
  // 1 se presente un cattere,
  // 0 se non è presente un carattere

  // per maggior informazioni sull'uso di parseInt() consultare il link:
  // https://wp.me/p4kwmk-4Ah
  
  if (Serial.available()) {        // Viene controllato se è disponibile un carattere

    // definizione di una variabile di tipo char in cui memorizzare
    // il carattere inviato ad Arduino mediante la Serial Monitor

    char carattere = Serial.read();

    // La funzione isDigit restituisce TRUE se se il carattere
    // inviato sulla Serial Monitor è un numero altrimenti restituisce FALSE

    if (isDigit(carattere)) // verifica se è un numero
    {
      // mappiamo l'intervallo dei caratteri da '0' a '9'
      // in un valore compreso tra 0 e 255, intervallo di valori
      // del Duty Cycle

      int vel = map(carattere, '0', '9', 0, 255);
      analogWrite(pinMotore, vel);
      
      Serial.print("Valore del Duty Cycle: ");
      Serial.println(vel);
      Serial.println("--------------------------");
      Serial.print("Velocità impostata: ");
      Serial.println(carattere);
      Serial.println("==========================");
            
    }
    else
    {
      // nel caso in cui il carattere inserito non è un numero
      // viene restituito un messaggio e stampa il carattere
      Serial.println();
      Serial.println("******************************");
      Serial.print("Carattere non riconosciuto: ");
      Serial.println(carattere);
      Serial.println("******************************");
      Serial.println();
    }
  }
}

Esempio 2
Si faccia riferimento al circuito 1. Realizziamo uno sketch che permette la regolazione della velocità del motore inserendo un numero compreso tra 0 e 9. Il valore 0 ferma il motore, il valore 9 permette di muovere il motore alla velocità massima, valori intermedi movimenteranno il motore ad una velocità proporzionale al numero inserito.

Valori fuori dall’intervallo impostato fermano il motore.

Per la realizzazione di questo sketch si utilizza la funzione Serial.parseInt() che legge i caratteri sulla seriali e restituisce la loro rappresentazione numerica (tipo long). I caratteri che non sono numeri interi (o con segno meno) vengono ignorati.

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.
/*
   Prof. Maffucci Michele
   Controllo motore a spazzola:
   accensione, spegnimento, controllo velocità.
   All'avvio del programma il motore è spento.

   La velocità del motore è impostata
   con un valore compreso tra 0 e 9

   Valori non numerici fermano il motore.

   Viene utilizzata la funzione Serial.parseInt() per leggere

   Data: 19.01.2021

*/

// driver del motore collegato al pin 6
const byte pinMotore = 6; // motor driver is connected to pin 3

// per stampare una sola volta il messaggio sulla Serial Monitor
bool abilitaMessaggio = 0;

void setup()
{
  // inizializzazione della serial monitor
  Serial.begin(9600);

  // inizializzazione OUTPUT del pin 6
  pinMode(pinMotore, OUTPUT);
}

void loop()
{
  // consente di visualizzare sulla Serial Monitor
  // una sola stampa delle stringa
  if (abilitaMessaggio == 0) {
    // ritardo che evita la doppia stampa del messaggio
    delay(200);
    Serial.println("Controllo velocità motore");
    Serial.println("Inserire la velocità (0 - 9)");
    Serial.println();
    abilitaMessaggio = 1;
  }

  // Controlla se è disponibile almeno un carattere sulla seriale
  // La Serial.available() restituisce
  // 1 se presente un cattere,
  // 0 se non è presente un carattere

  // per maggior informazioni sull'uso di parseInt() consultare il link:
  // https://wp.me/p4kwmk-4Ah

  if (Serial.available()) {        // Viene controllato se è disponibile un carattere

    // definizione di una variabile di tipo char in cui memorizzare
    // il carattere inviato ad Arduino mediante la Serial Monitor

    // per maggior informazioni sull'uso di parseInt() consultare il link:
    // https://wp.me/p4kwmk-4Ah

    int valore = Serial.parseInt();

    // La funzione isDigit verifica se il carattere inviato è un numero
    // e restituisce TRUE se il carattere è un numero altrimenti restituisce FALSE

    // mappiamo l'intervallo dei caratteri da '0' a '9'
    // in un valore compreso tra 0 e 255, intervallo di valori
    // del Duty Cycle

    if (valore >= 0 && valore <= 9) {

      if (valore == 0) {
        Serial.println();
        Serial.println("************************************");
        Serial.println("MOTORE FERMO");
        Serial.println("Valore inserito 0 o fuori intervallo");
        Serial.println("************************************");
        Serial.println();
      }

      int vel = map(valore, 0, 9, 0, 255);
      analogWrite(pinMotore, vel);

      Serial.print("Valore del Duty Cycle: ");
      Serial.println(vel);
      Serial.println("--------------------------");
      Serial.print("Velocità impostata: ");
      Serial.println(valore);
      Serial.println("==========================");
    }
  }
}

Esercizio 1
Aggiungere al circuito 1 due pulsanti che permettono di aumentare o diminuire la velocità del motore. Fare in modo che la velocità impostata del motore sia proporzionale ai valori numerici interi nell’intervallo tra 0 e 9, così come fatto negli esempi precedenti. Ad ogni pressione del pulsante P1 si incrementa di una unità il valore della velocità. Ad ogni pressione del pulsante P2 si decrementa la velocità del motore i una unità. All’avvio di Arduino il motore è spento. Mostrare la velocità impostata sulla serial monitor.

Esercizio 2
Aggiungere all’esercizio precedente un pulsante P3 di emergenza che alla pressione ferma il motore. Se è stata azionata l’emergenza i pulsanti P1 e P2 di incremento e decremento non funzionano. Per poter riattivare il sistema bisogna premere nuovamente P3.

Supporto per motore a spazzola kit ELEGOO

La quasi totalità dei miei studenti, per svolgere le attività di sperimentazione di elettronica e automazione a casa e a scuola in questo periodo di crisi pandemica, ha acquistato uno dei molti kit ELEGOO in cui, anche nella versione base del kit, sono presenti tutti i componenti per svolgere le prime esercitazioni di automazione. All’interno dei diversi kit è presente un motore CC a spazzola 3-6 V come quello indicato nell’immagine che segue a cui è possibile connettere una ventola.

Questo semplice motore viene utilizzato in diverse esperienze di laboratorio: marcia e arresto, controllo di velocità e molto altro. Per far si che il banco di lavoro sia ordinato ho realizzato in 3D tre elementi che permettono di disporre il motore su un supporto di legno ricavato da una comune bacchetta di legno 20×20 mm che può essere acquistata in qualsiasi brico.

Condivido su Thingiverse i sorgenti grafici per la realizzazione del supporto.

Di seguito alcune immagini della semplice struttura… e quando farà caldo tutti avranno il proprio ventilatore personale 🙂

Buon Making a tutti 🙂