Archivi categoria: i miei allievi

Errori comuni nell’uso di Arduino – fare più azioni nell’if senza l’uso delle parentesi graffe

Continua la segnalazione degli errori che spesso vengono commessi durante le prime attività di programmazione con Arduino.
La sequenza di istruzioni da eseguire se risulta vera la condizione dell’istruzione if  (il corpo della if) deve essere racchiusa tra parentesi graffe. L’errore è immaginare che l’indentazione sia sufficiente per indicarne l’appartenenza al corpo della if, modalità valida in altri linguaggi di programmazione come in Python, ma non in C:

Uso non corretto

if (temperatura > 25)
  digitalWrite (normaleTemp, LOW);
  digitalWrite (allarmeTemp, HIGH);

Uso corretto

if (temperatura > 25)
{
  digitalWrite (normaleTemp, LOW);
  digitalWrite (allarmeTemp, HIGH);
} // fine del controllo temperatura > 25

Buon lavoro 🙂

Gestire con micro:bit un display 1602 I2C

Ho realizzato questo tutorial come risposta ad un quesito che mi è stato posto da un’amica collega sull’utilizzo del display 1602 I2C comandato da micro:bit.
Per quanto riguarda la descrizione tecnica del display vi rimando al post su questo sito:

Utilizzo dell’LCD 16×2 Hitachi HD44780 1602 con modulo I2C PCF8574T

Per un utilizzo con micro:bit è sufficiente conoscere il tipo esatto di integrato I2C che gestisce la comunicazione tra display e micro:bit nella modalità indicata da questo tutorial.

Aprite un nuovo progetto dall’ambiente grafico di programmazione di micro:bit.
Aggiungiamo l’estensione che consente la gestione di un display 16×2 I2C, per far ciò selezionate “+Extensions”:

nel campo di ricerca inserite: I2C

dovreste ottenere il seguente risultato:

Selezionate l’estensione indicata nell’immagine:

All’interno dei blocchi disponibili ritroverete quelli che fanno riferimento al display:

Ogni dispositivo I2C dispone di un indirizzo, un codice a cui bisogna far riferimento in fase di programmazione e per quanto riguarda la scheda di comunicazione I2C disposta sul retro del display troverete due tipi di dispositivi con indirizzo differenti l’uno dall’altro. La differenza consiste nel tipo di circuito integrato che gestisce la comunicazione I2C:

  • PCF8574 > indirizzo 39
  • PCF8574A > indirizzo 63

L’indirizzo dovrà essere inserito all’interno di un apposito blocco come indicato di seguito.

Per sapere quale indirizzo usare potete leggere la sigla dell’integrato, come indicato nell’immagine che segue:

nel mio caso l’indirizzo da inserire sarà il 39.

L’altro metodo consiste nel sostituire, nel codice che segue, gli indirizzi sopra indicati in modo da individuare il numero corretto che permette la visualizzazione del testo sul  display.

Per il collegamento del circuito avete necessità di alimentare il display a 5V, in commercio esistono degli edge connector (connettore per collegare micro:bit alla breadboard) che prelevano dalla USB i 5 volt, nel mio caso questa funzionalità non è fornita, l’unica tensione disponibile è di 3,3V non sufficienti per alimentare il display.

Se anche voi siete nelle medesime condizioni allora alimenterete via USB il micro:bit mentre il display dovrà essere alimentato esternamente mediante una tensione di 5V. Se agite in questo modo ricordate che la terra (GND) dovrà essere la medesima (collegate insieme) per entrambi i dispositivi. Fate riferimento allo schema di seguito indicato:

Sul micro:bit il pin 20 corrisponde all’SDA e il pin 19 all’SCL, pin che avranno una corrispondenza con il display. Per le mie sperimentazioni dispongo di un cavo USB modificato per poter inserire su breadboard la tensione di 5V proveniente dalla USB del computer.

Realizzeremo un semplice programma che consente di visualizzare su due righe il testo:

BBC micro:bit
Italy timer

dove timer è il numero di secondi trascorsi dall’avvio del programma.

L’inserimento della prima istruzione provocherà la visualizzazione del display I2C:

L’istruzione “LCD initialize with Address 39” assegna al display l’indirizzo 39

L’istruzione “show string” mostrerà sul display il testo incluso nel primo campo: “BBC micro:bit” che verrà scritto a partire dalla colonna 0 e riga 0 (in prima riga).

La seconda “show string” mostrerà sul display il testo “Italy” alla colonna 0 e riga 1 (in seconda riga):

Definiamo una nuova variabile che andremo successivamente ad incrementare:

assegnamo alla variabile il nome: contatore

All’interno della funzione “forever” inserire l’istruzione “change” per incrementare di una unità la variabile “contatore”.

Con l’istruzione “show number” visualizziamo sul display un numero, inserendo nel primo campo la variabile “contatore” il numero visualizzato sarà quello memorizzato nella variabile contatore:

La visualizzazione del numero contenuto in “contatore” avverrà in colonna 6 riga 1:

Aggiungendo una pausa di 1 secondo l’incremento del numero che appare sul display sarà di 1 secondo:

Questo il risultato:

Buon Coding a tutti 🙂

Errori comuni nell’uso di Arduino – uso non corretto della digitalWrite

Errore: considerare la “,” come separatore di istruzioni e non come separatore di valori. In C una scrittura del tipo: (a, b, c) è una sequenza di espressioni separate da virgola che valuta l’ultima espressione c, mentre {a; b; c;} è una sequenza di istruzioni che non valuta nulla.
La virgola si inserisce solamente tra due espressioni a differenza del punto e virgola che si inserisce alla fine di un’istruzione (ma non di un’istruzione di blocco come: if, for, while, do while).

Esempio non corretto

digitalWrite ((5, 6, 7), HIGH);

Esempio corretto

digitalWrite (5, HIGH);
digitalWrite (6, HIGH);
digitalWrite (7, HIGH);

O ancora meglio usando un ciclo for:

for (byte i = 5; i<= 7; i++)
  digitalWrite(i, HIGH);

Domanda che in genere faccio ai ragazzi:
“perché ho dichiarato i come byte e non come int?”

La risposta la trovate in: Appunti di programmazione su Arduino: tipi di dati in cui viene evidenziato che la dimensione del tipo byte è di 8 bit (1 byte) e rappresenta interi (senza decimali) ed hanno un range da 0 a 255, mentre gli int sono dei tipi di dato usati per memorizzare numeri senza decimali e memorizzano valori a 16 bit (2 byte) nel range da 32.767 a -32.768, quindi usando il tipo byte occupiamo meno spazio in memoria.

Buon Coding a tutti 🙂

Disegnare caratteri personalizzati con Arduino per un LCD 16×2

Durante le attività di sperimentazione capita spesso la necessità di utilizzare caratteri speciali o icone che non fanno parte del set di caratteri ASCII standard (https://www.asciitable.com) visualizzatili su un display 16×2. Ovviamente un display LCD 16×2 non permette risoluzioni elevate, ma la qualità che si riesce ad ottenere è più che accettabile.

Tutti i display LCD basati sul controller Hitachi HD44780 hanno due tipi di memorie in cui vengono memorizzati i caratteri: CGROM e CGRAM (Character Generator ROM & RAM). La memoria CGROM non è volatile e non può essere modificata mentre la memoria CGRAM è volatile e può essere modificata in qualsiasi momento.

CGROM è usato per memorizzare tutti i caratteri permanenti che possono essere visualizzati usando il loro codice ASCII. Ad esempio, se scriviamo 0x4D, sul display viene visualizzato il carattere “M”. CGRAM è un’altra memoria che può essere utilizzata per la memorizzazione di caratteri definiti dall’utente.

Questa RAM è limitata a 64 byte, cioè implica che per LCD a 5 × 8 pixel come LCD 16×2 Hitachi HD44780, nel CGRAM possono essere memorizzati fino a 8 caratteri definiti dall’utente.

Un carattere sul display viene realizzato utilizzando una matrice di pixel 5 × 8, quindi per definire un nostro carattere dovremo lavorare in quest’area.

La definizione del carattere personale avviene utilizzando la funzione createChar() della libreria LiquidCrystal.

Prima di utilizzare la libreria createChar() è necessario impostare un array di 8 byte ed ognuno di essi definisce una riga della matrice costituita dalla lettera b che definisce il tipo del dato (byte) e la serie di 1 e 0 definiscono i pixel attivi o disattivi: nell’array i bit a 1 indicano i pixel attivi, mentre gli 0 indicano i pixel disattivi.

Nell’esempio che segue viene utilizzato un display 16×2 i2c

Per quanto riguarda la libreria LiquidCrystal_I2C vi rimando alla lezione:
Utilizzo dell’LCD 16×2 Hitachi HD44780 1602 con modulo I2C PCF8574T

Nel caso abbiate necessità di sviluppare in modo più semplice ed agevole i vostri caratteri personalizzati potete utilizzare una soluzione grafica che immediatamente vi permettessi impostare l’array di byte, fate riferimento a questi due link:

da cui ho realizzato velocemente le icone, il codice corrispondenti è poi stato inserito all’interno dello sketch come si può evincere dallo sketch che segue:

/* 
 *  Prof. Michele Maffucci
 *  Crezione di caratteri personali
 *  Utilizzo di un display LCD 16×2 Hitachi HD44780 1602
 *  con modulo i2C PCF8574T
 *  Data: 17.01.2020 - v01
*/

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

// inizializzazione della libreria in cui è descritta la modalità di utilizzo dei pin
LiquidCrystal_I2C lcd(0x27, 16, 2); // impostazione dell'indirizzo dell'LCD 0x27 di 16 caratteri e 2 linee

// caratteri personalizzati

byte lucchettoChiuso[8] = {
  0b01110,
  0b10001,
  0b10001,
  0b10001,
  0b11111,
  0b11011,
  0b11011,
  0b11111
};

byte lucchettoAperto[8] = {
  0b01110,
  0b10000,
  0b10000,
  0b10000,
  0b11111,
  0b11011,
  0b11011,
  0b11111
};

byte Altoparlante[8] = {
  0b00001,
  0b00011,
  0b01111,
  0b01111,
  0b01111,
  0b00011,
  0b00001,
  0b00000
};

byte batteriaMezza[8] = {
  0b01110,
  0b11011,
  0b10001,
  0b10001,
  0b10001,
  0b11111,
  0b11111,
  0b11111
};

byte alieno[8] = {
  0b10001,
  0b01010,
  0b11111,
  0b10101,
  0b11111,
  0b11111,
  0b01010,
  0b11011
};


byte pacmanBoccaChiusa[8] = {
  0b01110,
  0b11101,
  0b11111,
  0b11111,
  0b11000,
  0b11111,
  0b11111,
  0b01110
};

byte pacmanBoccaAperta[8] = {
  0b01110,
  0b11101,
  0b11111,
  0b11100,
  0b11000,
  0b11000,
  0b11111,
  0b01110
};

byte fantasmino[8] = {
  0b01110,
  0b11111,
  0b10101,
  0b11111,
  0b11111,
  0b11111,
  0b11111,
  0b10101
};

void setup()
{
  lcd.begin();      // inizializzazione dell'LCD
  lcd.backlight();  // attivazione della retroilluminazione

  // creazione nuovi caratteri
  lcd.createChar(0, lucchettoChiuso);
  lcd.createChar(1, lucchettoAperto);
  lcd.createChar(2, Altoparlante);
  lcd.createChar(3, batteriaMezza);
  lcd.createChar(4, alieno);
  lcd.createChar(5, pacmanBoccaChiusa);
  lcd.createChar(6, pacmanBoccaAperta);
  lcd.createChar(7, fantasmino);
  
  // Cancella il display
  lcd.clear();

  // Stampa la stringa
  lcd.print("maffucci.it");

}

void loop()
{
  lcd.setCursor(0, 1);
  lcd.write(byte(0));

  lcd.setCursor(2, 1);
  lcd.write(byte(1));

  lcd.setCursor(4, 1);
  lcd.write(byte(2));

  lcd.setCursor(6, 1);
  lcd.write(byte(3));

  lcd.setCursor(8, 1);
  lcd.write(byte(4));

  lcd.setCursor(10, 1);
  lcd.write(byte(5));

  lcd.setCursor(12, 1);
  lcd.write(byte(6));

  lcd.setCursor(14, 1);
  lcd.write(byte(7));
  
}

Dopo aver incluso la libreria, è necessario inizializzare l’array che definisce il carattere personalizzato definito da 8 byte.

...
byte alieno[8] = {
  0b10001,
  0b01010,
  0b11111,
  0b10101,
  0b11111,
  0b11111,
  0b01010,
  0b11011
};
...

Nel setup() bisogna inizializzare il carattere personalizzato mediante la funzione createChar(), che accetta due parametri, il primo, compreso tra  0 e 7 è utilizzato come indirizzo ad uno degli 8 caratteri creati personalizzati, il secondo parametro definisce il nome dell’array di byte che definisce il carattere personalizzato.

  // creazione nuovi caratteri
  lcd.createChar(0, lucchettoChiuso);
  lcd.createChar(1, lucchettoAperto);
  lcd.createChar(2, Altoparlante);
  lcd.createChar(3, batteriaMezza);
  lcd.createChar(4, alieno);
  lcd.createChar(5, pacmanBoccaChiusa);
  lcd.createChar(6, pacmanBoccaAperta);
  lcd.createChar(7, fantasmino);

Successivamente nel loop, per la visualizzazione del carattere personalizzato viene utilizzata la funzione write() che ha come parametro il numero (l’indirizzo) assegnato al carattere.

...
void loop()
{
  lcd.setCursor(0, 1);
  lcd.write(byte(0));

  lcd.setCursor(2, 1);
  lcd.write(byte(1));

  lcd.setCursor(4, 1);
  lcd.write(byte(2));

  lcd.setCursor(6, 1);
  lcd.write(byte(3));

  lcd.setCursor(8, 1);
  lcd.write(byte(4));

  lcd.setCursor(10, 1);
  lcd.write(byte(5));

  lcd.setCursor(12, 1);
  lcd.write(byte(6));

  lcd.setCursor(14, 1);
  lcd.write(byte(7));
}
...

Esercizi per i miei studenti

Esercizio 1

Realizzare uno sketch che mostra un omino che cammina da sinistra verso destra e ritorno, in modo continuo. Il movimento deve essere accompagnato dall’emissione di due note che mettono in evidenza il passo.

Esercizio 2

Realizzare un Pac Man che partendo dalla riga 0 colonna 0 mangi una serie di puntini , scende alla riga 1 colonna 0 e prosegue fino alla riga 1 colonna 15 dove si trova un fantasma che deve essere mangiato. Predisporre un buzzer che emette due tipi di suoni, uno che identifica quando Pac Man mangia un puntino ed uno quando Pac Man mangia il fantasma. Quando il fantasma viene mangiato il display fa un 3 blink e l’azione comincia nuovamente con il Pac Man che si posizione in riga 0 colonna 0.

Esercizio 3

Realizzare un sistema che rilevi il livello di carica di batterie da 1,5V.
Utilizzare 7 icone che definiscono livelli diversi di carica della batteria e a fianco di ogni icona deve anche apparire in modo numerico il valore di tensione misurato.

Esercizio 4

Realizzare uno sketch che valuta in percentuale la quantità di luce in una stanza e la mostra su display mediante numero in percentuale e una barra di livello realizzata con caratteri personalizzati che può aumentare o diminuire in funzione della quantità di luce che colpisce l’LDR.
Mediante la pressione di un pulsante viene attivata la calibrazione del sistema, questa fase dura 5 secondi in cui viene misurate il valore minimo di luce (comprendo con mano il sensore) e valore massimo della luce (togliendo la mano dal sensore). La fase di calibrazione deve essere evidenziato dalla scrittura su display del messaggio: “calibrazione” e icone animate che dovete inventare.

Utilizzare un orologio RTC con Arduino – Modulo Tiny RTC I2C – Visualizzazione su display I2C

Continuo la serie di post dedicati all’uso dell’RTC con integrato DS1307, in questa lezione viene suggerito come visualizzare su un display 16×2 Hitachi HD44780 1602 con modulo I2C PCF8574T: giorno della settimana, data e ora.
L’obiettivo che si vorrà raggiungere nei prossimi tutorial sarà quello di realizzare un timer programmabile da utilizzare in diverse esercitazioni di automazione.

Lo Schema di collegamento è il seguente:

Allego lo Sketch generale in cui ho inserito commenti di spiegazione sulle varie parti del codice e sull’utilizzo di specifiche funzioni.

Per quanto riguarda la libreria LiquidCrystal_I2C vi rimando alla lezione:
Utilizzo dell’LCD 16×2 Hitachi HD44780 1602 con modulo I2C PCF8574T

/* Prof. Maffucci Michele
   15.01.2020
   Orologio - v01
*/

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

// La data e l'ora funzionano usando un RTC DS1307 collegato tramite I2C e Wire lib
#include "RTClib.h"

RTC_DS1307 rtc;

char stringa1[8]; // per memorizzare la stringa che include la data (dimensione massima data: 8 caratteri)
char stringa2[6]; // per memorizzare la stringa che include l'ora (dimensione massima ora: 6 caratteri)

// Variabile per la verifica della cancellazione del display
int chiaveCancella = 0;

// Array multidimensionale costituito
// da 7 righe (giorni della settimana)
// 4 colonne (le lettere che compongono il giorno più il carattere null con cui deve terminare una stringa)

char giornoDellaSettimana[7][4] = {"Dom", "Lun", "Mar", "Mer", "Gio", "Ven", "Sab"};

// Inizializzazione della libreria in cui è descritta la modalità di utilizzo dei pin dell'LCD,
// impostazione dell'indirizzo dell'LCD 0x27 di 16 colonne e 2 linee

LiquidCrystal_I2C lcd(0x27, 16, 2);

void setup () {

  lcd.begin();      // inizializzazione dell'LCD
  lcd.backlight();  // attivazione della retroilluminazione

  if (!rtc.isrunning()) {
    lcd.setCursor(0, 0);
    lcd.print("RTC non funzionante!");
    // la riga che segue permette di impostare data e ora prendendo l'informazione
    // dal computer a cui è collegato Arduino
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    // la riga che segue permette di impostare
    // esplicitamente da parte dell'utente data e ora
    // Gennaio 21, 2014 alle 3 del pomeriggio ybisognrà scrivere:
    // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
  }
}

void loop () {

/* Controllo del funzionamento dell'RTC all'interno del loop

   Il controllo della disconnessione dell'RTC viene effettuato anche all'interno del loop,
   se ciò non venisse effettuato, una disconnessione dell'RTC non permetterebbe la visualizzazione
   del messaggio "RTC non risponde", ma verrebbero visualizzati valori numerici errati.
   La successiva connessione dell'RTC farà riapparire data e ora, ma in una modalità non allineata,
   in cui saranno mostrati i valori numerici derivanti dalla precedente disconessione.
*/

  if (!rtc.isrunning()) {
    lcd.setCursor(0, 0);
    lcd.print("RTC non risponde");
    lcd.setCursor(0, 1);
    lcd.print("                ");
    chiaveCancella = 1;
  }

  else
  {
    if (chiaveCancella == 1) {
      
      /* La cancellazione del display avviene solamente una sola volta, solo se si è verificata
         una precedente disconnessione dell'RTC. L'azione è necessaria perché in fase
         di riconnessione dell'RTC appaiono sul display numeri non coerenti.
         La cancellazione potrebbe essere effettuata  direttamente nel corpo della prima if,
         ma ciò causerebbe un flikering del testo.
      */
      
      lcd.clear();
      chiaveCancella = 0;
    }

    DateTime now = rtc.now();

    lcd.setCursor(0, 0);

    // estrae dall'array giornoDellaSettimana il nome del giorno
    lcd.print(giornoDellaSettimana[now.dayOfTheWeek()]);
    lcd.setCursor(5, 0);

    /*
      int sprintf(char *str, const char *format, ...);
      ha lo stesso funzionamento della printf, con la differenza che
      l'output non sarà visualizzato sullo schermo (standard output), ma
      immagazzinato nel vettore str

      %d è uno dei possibili specificatori di formato che può essere usato nella sprintf
      ha il compito di indicare alla funzione (la sprintf) il tipo della variabile che deve essere
      visualizzata, in questo caso con d indichiamo decimale.
      Con %02d si specifica la stampa di solo due numeri decimali.
    */

    sprintf(stringa1, "%2d/%02d/%d", now.day(), now.month(), now.year());
    lcd.print(stringa1);
    lcd.setCursor(0, 1);
    sprintf(stringa2, "%02d:%02d:%02d", now.hour(), now.minute(), now.second());
    lcd.print(stringa2);
    delay(1000);
  }
}

Di seguito riprendo quanto già inserito nei commenti:

char giornoDellaSettimana[7][4] = {"Dom", "Lun", "Mar", "Mer", "Gio", "Ven", "Sab"};

Definisce un Array multidimensionale costituito da 7 righe, i giorni della settimana e 4 colonne, le lettere che compongono il giorno (3 lettere) più il carattere null con cui deve terminare una stringa.

...
 if (!rtc.isrunning()) {
    lcd.setCursor(0, 0);
    lcd.print("RTC non risponde");
    lcd.setCursor(0, 1);
    lcd.print("                ");
    chiaveCancella = 1;
  }
...

Il controllo della disconnessione dell’RTC viene effettuato anche all’interno del loop, se ciò non venisse eseguito, una disconnessione dell’RTC non permetterebbe la visualizzazione del messaggio “RTC non risponde”, ma verrebbero mostrati valori numerici errati. La successiva connessione dell’RTC farà riapparire data e ora, ma in una modalità non allineata, in cui saranno mostrati i valori numerici derivanti dalla precedente disconessione.

...
  else
  {
    if (chiaveCancella == 1) {
      
      /* La cancellazione del display avviene solamente una sola volta, solo se si è verificata
         una precedente disconnessione dell'RTC. L'azione è necessaria perché in fase
         di riconnessione dell'RTC appaiono sul display numeri non coerenti.
         La cancellazione potrebbe essere effettuata  direttamente nel corpo della prima if,
         ma ciò causerebbe un flikering del testo.
      */
      
      lcd.clear();
      chiaveCancella = 0;
    }
...

La cancellazione del display avviene una sola volta, solo se si è verificata una precedente disconnessione dell’RTC. L’azione è necessaria perché in fase di riconnessione dell’RTC appaiono sul display numeri non coerenti. La cancellazione potrebbe essere effettuata direttamente nel corpo della prima if, ma ciò causerebbe un flikering del testo così come appare nell’immagine che segue:

sprintf(stringa1, "%2d/%02d/%d", now.day(), now.month(), now.year());

int sprintf(char *str, const char *format, …); ha lo stesso funzionamento della printf, con la differenza che l’output non sarà visualizzato sullo schermo (standard output), ma immagazzinato nel vettore str.

%d è uno dei possibili specificatori di formato che può essere usato nella sprintf ha il compito di indicare alla funzione (la sprintf) il tipo della variabile che deve essere visualizzata, in questo caso con d indichiamo decimale. Con %02d si specifica la stampa di solo due numeri decimali.

Per i miei allievi:

Esercizio 1

Modificare lo sketch proposto in modo che la retroilluminazione dello schermo venga spenta dopo 15 secondi e la pressione di un pulsante la riattivi.

Esercizio 2

Modificare lo sketch realizzato al punto 2 inserendo anche un sensore DHT11 che mostra temperatura ed umidità dell’ambiente.