Archivi tag: HD44780

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.

Arduino: realizzare un menù di selezione utilizzando un Diaplay LCD 16×2 Hitachi HD44780 1602 con modulo I2C PCF8574T

Continuo la lezione cominciata la scorsa settimana con i miei studenti di 5A Automazione dell’ITIS G.B. Pininfarina di Moncalieri in merito alla gestione degli input mediante pulsanti e segnalazione stato LED sulla Serial Monitor, questa volta utilizzeremo come sistema di output un Display 16×2 con modulo I2C.

Rispetto all’esercizio svolto nella precedente lezione questa volta utilizzeremo 3 pulsanti la cui pressione provocherà l’accensione rispettivamente di un LED Rosso un  LED Verde ed un LED Giallo.

L’esercizio è suddiviso in due parti, la prima parte prevede che l’accensione dei LED avvenga premendo il pulsante ad esso associato, il rilascio provoca lo spegnimento del LED. Lo stato del LED dovrà essere segnalato con il testo ON oppure OFF e nel momento in cui lo stato risulta ON a fianco del testo ON dovrà comparire una sequenza di caratteri che simula la rotazione di un’elica, così come evidenziato dalle immagini che seguono.

Le lettere R, V, G indicano rispettivamente il colore: Rosso, Verde, Giallo dei LED la cui accensione è comandata da un pulsante NO.

La seconda parte dell’esercizio consiste nella realizzazione di uno sketch che permette il mantenimento dello stato ON oppure OFF anche al rilascio del pulsante, il cambiamento di stato avviene nella successiva pressione del pulsante.

Per i dettagli sull’uso del display 16×2 I2C vi rimando al tutorial su questo sito seguendo il link: Utilizzo dell’LCD 16×2 Hitachi HD44780 1602 con modulo I2C PCF8574T

Componenti utilizzati

  • Arduino UNO R3
  • N. 3 pulsanti NO
  • N. 3 resistori da 10 KOhm
  • N. 3 resistori da 220 Ohm
  • N. 1 LED Rosso
  • N. 1 LED Verde
  • N. 1 LED Giallo

Di seguito lo schema di collegamento:

Il funzionamento degli sketch è spiegato mediante commenti all’interno del codice.

Parte 1

/*
   Prof. Michele Maffucci
   Accensione e spegnimento led con segnalazione su Display LCD I2C
   30.09.19

   Accensione e spegnimento di LED mediante pulsanti
   con antirimbalzo e messaggio ripetuto dello stato del LED
   sul Display

   Pulsante Rosso: accensione e spegnimento LED Rosso
   (prima pressione accende, seconda pressione spegne)

   Pulsante Verde: accensione e spegnimento LED Verde
   (prima pressione accende, seconda pressione spegne)

   Pulsante Giallo: accensione e spegnimento LED Giallo
   (prima pressione accende, seconda pressione spegne)

*/

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

// ledRosso variabile di tipo intero a cui viene assegnato 
// il valore intero 2 che sarà associato al pin digitale 2 

int ledRosso = 2;

// ledVerde variabile di tipo intero a cui viene assegnato 
// il valore intero 3 che sarà associato al pin digitale 3

int ledVerde = 3;

// ledGiallo variabile di tipo intero a cui viene assegnato 
// il valore intero 4 che sarà associato al pin digitale 4

int ledGiallo = 4;

// pulsanteRosso variabile di tipo intero a cui viene assegnato
// il valore intero 10 che sarà associato al pin digitale 10
// a cui sarà collegato il pulsante che comanda il LED Rosso 

int pulsanteRosso = 10;

// pulsanteVerde variabile di tipo intero a cui viene assegnato
// il valore intero 11 che sarà associato al pin digitale 11
// a cui sarà collegato il pulsante che comanda il LED Verde 

int pulsanteVerde = 11;

// pulsanteGiallo variabile di tipo intero a cui viene assegnato
// il valore intero 12 che sarà associato al pin digitale 12
// a cui sarà collegato il pulsante che comanda il LED Giallo 

int pulsanteGiallo = 12;

// inizializzazione della variabili in cui verrà memorizzato il valore della
// digitalRead: 0 non premuto, 1 premuto

int valRosso = 0;
int valVerde = 0;
int valGiallo = 0;

// 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
//-----------------------------
void setup()
{
  lcd.begin();      // inizializzazione dell'LCD
  lcd.backlight();  // attivazione della retroilluminazione

  // imposta i pin digitali a cui sono collegati i LED ad OUTPUT
  pinMode(ledRosso, OUTPUT);
  pinMode(ledVerde, OUTPUT);
  pinMode(ledGiallo, OUTPUT);

  // imposta i pin digitali a cui sono collegati i pulsanti ad OUTPUT
  pinMode(pulsanteRosso, INPUT);
  pinMode(pulsanteVerde, INPUT);
  pinMode(pulsanteGiallo, INPUT);

  // messaggio di Avvio visualizzato una sola volta
  lcd.setCursor(0, 0);      // posiziona curasore in colonna 0 e riga 0
  lcd.print("Ciao");        // stampa del testo su display
  lcd.setCursor(0, 1);      // posiziona curasore in colonna 0 e riga 1
  lcd.print("Comando LED"); // stampa del testo su display
  delay(2000);              // pausa di 2 secondi
  lcd.clear();              // cancella il contenuto del display

}
//-----------------------------

void loop()
{
  valRosso = digitalRead(pulsanteRosso);    // lettura dell'input (pulsante) e memorizzazione in valRosso
  valVerde = digitalRead(pulsanteVerde);    // lettura dell'input (pulsante) e memorizzazione in valVerde
  valGiallo = digitalRead(pulsanteGiallo);  // lettura dell'input (pulsante) e memorizzazione in valGiallo

  // ---------- Controllo pulsante LED Rosso ----------

  /* Se il pulsante viene premuto viene acceso il LED rosso
   * posizionato il cursore in colonna 0 riga 0 
   * stampato il testo: R: ON 
   * avviata l'animazione
   */
   
  if (valRosso == HIGH) {
    digitalWrite(ledRosso, HIGH);
    lcd.setCursor(0, 0);
    lcd.print("R: ON ");
    animazione(6,0);
  }

  /* Se il pulsante NON viene premuto viene spento il LED rosso
   * posizionato il cursore in colonna 0 riga 0 
   * stampato il testo: R: OFF 
   * interrotta l'animazione
   */
   
  else {
    digitalWrite(ledRosso, LOW);
    lcd.setCursor(0, 0);
    lcd.print("R: OFF ");
  }

  // ---------- Controllo pulsante LED Verde ----------

   /* Se il pulsante viene premuto viene acceso il LED verde
   * posizionato il cursore in colonna 0 riga 1 
   * stampato il testo: V: ON 
   * avviata l'animazione
   */
   
  if (valVerde == HIGH) {
    digitalWrite(ledVerde, HIGH);
    lcd.setCursor(9, 0);
    lcd.print("V: ON ");
    animazione(15,0);
  }

  /* Se il pulsante NON viene premuto viene spento il LED verde
   * posizionato il cursore in colonna 0 riga 1 
   * stampato il testo: V: OFF 
   * interrotta l'animazione
   */
   
  else {
    digitalWrite(ledVerde, LOW);
    lcd.setCursor(9, 0);
    lcd.print("V: OFF ");
  }

  // ---------- Controllo pulsante LED Giallo ----------

   /* Se il pulsante viene premuto viene acceso il LED giallo
   * posizionato il cursore in colonna 0 riga 1 
   * stampato il testo: G: ON 
   * avviata l'animazione
   */
   
  if (valGiallo == HIGH) {
    digitalWrite(ledGiallo, HIGH);
    lcd.setCursor(0, 1);
    lcd.print("G: ON ");
    animazione(6,1);
  }

  /* Se il pulsante NON viene premuto viene spento il LED giallo
   * posizionato il cursore in colonna 9 riga 0 
   * stampato il testo: G: OFF 
   * interrotta l'animazione
   */
     
  else {
    digitalWrite(ledGiallo, LOW);
    lcd.setCursor(0, 1);
    lcd.print("G: OFF ");
  }
}

// Funzione che stampa un cursore che ruota

void animazione(int colonna, int riga) {
  lcd.setCursor(colonna, riga);
  lcd.print("/");
  delay(150);
  lcd.setCursor(colonna, riga);
  lcd.print("-");
  delay(150);
  lcd.setCursor(colonna, riga);
  lcd.print("|");
  delay(150);
  lcd.setCursor(colonna, riga);
  lcd.print("/");
  delay(150);
  lcd.setCursor(colonna, riga);
  lcd.print("-");
  delay(150);
  lcd.setCursor(colonna, riga);
  lcd.print("|");
  delay(150);
}

Parte 2

/*
   Prof. Michele Maffucci
   Data: 30.09.19

   Accensione e spegnimento di LED mediante pulsanti
   con antirimbalzo e messaggio NON ripetuto dello stato del LED
   sulla su Display LCD I2C

   Stampa 1 sola volta il messaggio dello stato del LED Sul Display I2C
   (non va in loop la stampa dello stato del LED)

   Pulsante Rosso: accensione e spegnimento LED Rosso
   (prima pressione accende, seconda pressione spegne)

   Pulsante Verde: accensione e spegnimento LED Verde
   (prima pressione accende, seconda pressione spegne)

   Pulsante Giallo: accensione e spegnimento LED Giallo
   (prima pressione accende, seconda pressione spegne)

*/

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

// ledRosso variabile di tipo intero a cui viene assegnato
// il valore intero 2 che sarà associato al pin digitale 2

int ledRosso = 2;

// ledVerde variabile di tipo intero a cui viene assegnato
// il valore intero 3 che sarà associato al pin digitale 3

int ledVerde = 3;

// ledGiallo variabile di tipo intero a cui viene assegnato
// il valore intero 4 che sarà associato al pin digitale 4

int ledGiallo = 4;

// pulsanteRosso variabile di tipo intero a cui viene assegnato
// il valore intero 10 che sarà associato al pin digitale 10
// a cui sarà collegato il pulsante che comanda il LED Rosso

int pulsanteRosso = 10;

// pulsanteVerde variabile di tipo intero a cui viene assegnato
// il valore intero 11 che sarà associato al pin digitale 11
// a cui sarà collegato il pulsante che comanda il LED Verde

int pulsanteVerde = 11;

// pulsanteGiallo variabile di tipo intero a cui viene assegnato
// il valore intero 12 che sarà associato al pin digitale 12
// a cui sarà collegato il pulsante che comanda il LED Giallo

int pulsanteGiallo = 12;

// inizializzazione della variabili in cui verrà memorizzato il valore della
// digitalRead: 0 non premuto, 1 premuto

int valRosso = 0;
int valVerde = 0;
int valGiallo = 0;

// inizializzazione della variabili in cui verrà memorizzato lo stato del pulsante
// All'avvio dello sketch i pulsanti non sono premuti

int statoRosso = 0;
int statoVerde = 0;
int statoGiallo = 0;

// inizializzazione della variabili in cui verrà memorizzato lo stato precedente del pulsante
// All'avvio dello sketch i pulsanti non sono premuti

int valRossoOld = 0;
int valVerdeOld = 0;
int valGialloOld = 0;

// inizializzazione delle variabili che consentono la stampa dello stato del LED

int stampoRossoON = 0;
int stampoRossoOFF = 0;

int stampoVerdeON = 0;
int stampoVerdeOFF = 0;

int stampoGialloON = 0;
int stampoGialloOFF = 0;

// 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
//-----------------------------
void setup()
{
  lcd.begin();      // inizializzazione dell'LCD
  lcd.backlight();  // attivazione della retroilluminazione

  // imposta i pin digitali a cui sono collegati i LED ad OUTPUT
  pinMode(ledRosso, OUTPUT);
  pinMode(ledVerde, OUTPUT);
  pinMode(ledGiallo, OUTPUT);

  // imposta i pin digitali a cui sono collegati i pulsanti ad OUTPUT
  pinMode(pulsanteRosso, INPUT);
  pinMode(pulsanteVerde, INPUT);
  pinMode(pulsanteGiallo, INPUT);

  // messaggio di Avvio visualizzato una sola volta
  lcd.setCursor(0, 0);      // posiziona curasore in colonna 0 e riga 0
  lcd.print("Ciao");        // stampa del testo su display
  lcd.setCursor(0, 1);      // posiziona curasore in colonna 0 e riga 1
  lcd.print("Comando LED"); // stampa del testo su display
  delay(2000);              // pausa di 2 secondi
  lcd.clear();              // cancella il contenuto del display
}
//-----------------------------


void loop()
{
  valRosso = digitalRead(pulsanteRosso);    // lettura dell'input (pulsante) e memorizzazione in valRosso
  valVerde = digitalRead(pulsanteVerde);    // lettura dell'input (pulsante) e memorizzazione in valVerde
  valGiallo = digitalRead(pulsanteGiallo);  // lettura dell'input (pulsante) e memorizzazione in valGiallo

  // ---------- Controllo pulsante LED Rosso ----------

  // viene controllato che l'input sia HIGH (pulsante premuto) e cambia lo stato del LED

  if ((valRosso == HIGH) && (valRossoOld == LOW)) {
    statoRosso = 1 - statoRosso;

    // antirimbalzo software - attesa di 15 ms per attendere che l'input si stabilizzi
    delay(15);

    // poichè il pulsante è stato premuto la variabile stampoRossoON viene posta a 0
    // per consentire la stampa del messaggio "LED Rosso ON"

    stampoRossoON = 0;
  }

  // memorizzazione del valore precedente restituito dalla digitalRead

  valRossoOld = valRosso;

  // ---------- Controllo pulsante LED Verde ----------

  // viene controllato che l'input sia HIGH (pulsante premuto) e cambia lo stato del LED

  if ((valVerde == HIGH) && (valVerdeOld == LOW)) {
    statoVerde = 1 - statoVerde;

    // antirimbalzo software - attesa di 15 ms per attendere che l'input si stabilizzi

    delay(15);

    // poichè il pulsante è stato premuto la variabile stampoVerdeON viene posta a 0
    // per consentire la stampa del messaggio "LED Verde ON"

    stampoVerdeON = 0;
  }

  // memorizzazione del valore precedente restituito dalla digitalRead

  valVerdeOld = valVerde;

  // ---------- Controllo pulsante LED Giallo ----------

  // viene controllato che l'input sia HIGH (pulsante premuto) e cambia lo stato del LED

  if ((valGiallo == HIGH) && (valGialloOld == LOW)) {
    statoGiallo = 1 - statoGiallo;

    // antirimbalzo software - attesa di 15 ms per attendere che l'input si stabilizzi

    delay(15);

    // poichè il pulsante è stato premuto la variabile stampoVerdeON viene posta a 0
    // per consentire la stampa del messaggio "LED Verde ON"

    stampoGialloON = 0;
  }

  // memorizzazione del valore precedente restituito dalla digitalRead

  valGialloOld = valGiallo;

  // ---------- Stampa sul display lo stato del LED Rosso ----------

  // Se il pulsante è stato premuto la condizione dell'if risulta vera ed il LED Rosso si accende

  if (statoRosso == 1) {
    digitalWrite(ledRosso, HIGH);

    // Se la variabile stampoRossoON è 0 allora !stampoRossoON vale 1
    // ciò consente la stampa del messaggio "LED Rosso ON"

    if (!stampoRossoON) {
      lcd.setCursor(0, 0);
      lcd.print("R: ON ");

      // Viene posto a 0 stampoRossoOFF per consentire la stampa "LED Rosso OFF"
      // nel caso si prema nuovamente il pulsante che controlla il LED Rosso.

      stampoRossoOFF = 0;
    }
  }
  // nel caso in cui il pulsante non sia premuto o nello stato precedente era stato premuto
  // allora il LED dovrà essere spento ed il messaggio sulla seriale dovrà essere "LED Rosso OFF"

  else {
    digitalWrite(ledRosso, LOW);

    if (!stampoRossoOFF) {
      lcd.setCursor(0, 0);
      lcd.print("R: OFF");             

      // Viene posto a 1 stampoRossoOFF per consentire la stampa "LED Rosso OFF"
      // nel caso si prema nuovamente il pulsante che controlla il LED Rosso.
      
      stampoRossoOFF = 1;
    }
  }

  // ---------- Stampa sul disolay lo stato del LED Verde ----------

  // Se il pulsante è stato premuto la condizione dell'if risulta vera ed il LED Verde si accende

  if (statoVerde == 1) {
    digitalWrite(ledVerde, HIGH);

    // Se la variabile stampoVerdeON è 0 allora !stampoVerdeoON vale 1
    // ciò consente la stampa del messaggio "LED verde ON"

    if (!stampoVerdeON) {
      lcd.setCursor(7, 0);
      lcd.print("V: ON ");

      // Viene posto a 0 stampoVerdeOFF per consentire la stampa "LED Verde OFF"
      // nel caso si prema nuovamente il pulsante che controlla il LED Rosso.

      stampoVerdeOFF = 0;
    }
  }

  // nel caso in cui il pulsante non sia premuto o nello stato precedente era stato premuto
  // allora il LED dovrà essere spento ed il messaggio sulla seriale dovrà essere "LED Verde OFF"

  else {
    digitalWrite(ledVerde, LOW);

    if (!stampoVerdeOFF) {
      lcd.setCursor(7, 0);
      lcd.print("V: OFF");              

      // Viene posto a 1 stampoVerdeOFF per consentire la stampa "LED Verde OFF"
      // nel caso si prema nuovamente il pulsante che controlla il LED Verde.

      stampoVerdeOFF = 1;
    }
  }

  // ---------- Stampa sul disolay lo stato del LED Giallo ----------

  // Se il pulsante è stato premuto la condizione dell'if risulta vera ed il LED Verde si accende

  if (statoGiallo == 1) {
    digitalWrite(ledGiallo, HIGH);

    // Se la variabile stampoGialloON è 0 allora !stampoGialloON vale 1
    // ciò consente la stampa del messaggio "LED giallo ON"

    if (!stampoGialloON) {
      lcd.setCursor(0, 1);     
      lcd.print("G: ON "); 
    
      // Viene posto a 0 stampoGialloOFF per consentire la stampa "LED Giallo OFF"
      // nel caso si prema nuovamente il pulsante che controlla il LED Giallo.

      stampoGialloOFF = 0;
    }
  }

  // nel caso in cui il pulsante non sia premuto o nello stato precedente era stato premuto
  // allora il LED dovrà essere spento ed il messaggio sulla seriale dovrà essere "LED Giallo OFF"

  else {
    digitalWrite(ledGiallo, LOW);

    if (!stampoGialloOFF) {
      lcd.setCursor(0, 1);     
      lcd.print("G: OFF");               
 
      // Viene posto a 1 stampoGialloOFF per consentire la stampa "LED Giallo OFF"
      // nel caso si prema nuovamente il pulsante che controlla il LED Giallo.

      //stampoGialloON = 0;
    }
  }
}

Esercizio: utilizzare gli esempi precedenti per realizzare un sistema che consente di avviare ed arrestare 2 ventole, ciascuna comandata da un pulsante. Un terzo pulsante dovrà essere utilizzato come consenso per permettere l’utilizzo del sistema, quindi:

  • Pulsante Marcia/Arresto ventola 1
  • Pulsante Marcia/Arresto ventola 2
  • Pulsante accensione spegnimento sistema

Gli stati dovranno essere indicati mediante LED e con messaggio sul display.

Buon Lavoro 🙂