Utilizzare M5StickC con l’IDE UIFlow

UIFlow è una piattaforma di programmazione appositamente progettata per i dispositivi M5Stack. L’IDE di programmazione grafico a blocchi è basato su blockly, linguaggio grafico di programmazione ben conosciuto in campo didattico. UIFlow consente inoltre di programmare qualsiasi oggetto M5Stack in MicroPython, implementazione di Python 3 per microcontrollori e sistemi embedded. MicroPython, come molti di voi sapranno, è un linguaggio di programmazione snello ed efficiente ideale per attività di Coding a scuola e da sempre impiegato in campo scientifico.

UIFlow fornisce le funzionalità necessarie per la realizzazione, in maniera estremamente semplice, progetti con forte interazione con il mondo reale, ideale quindi per chi si avvicina al mondo della prototipazione elettronica, dell’automazione e della programmazione.

L’IDE UIFlow può essere utilizzato on-line oppure localmente scaricandolo dal sito principale. Per prelevarlo collegatevi al sito https://m5stack.com/ e selezionate dal menù principale: software > download:

Nella pagina posizionate il mouse sul pulsante “Download” di UIFlow-Desktop-IDE, apparirà un menù a discesa, selezionate il sistema operativo su cui dovrà essere istallato UIFlow

Una volta prelevato il programma scompattate il file ed eseguite facendo doppio click

All’avvio dell’IDE viene controllato se sono presenti i driver necessari per poter gestire i dispositivi M5StickC.
M5StickC non necessita di installazione driver per i sistemi operativi Windows 10, Mac (High Sierra+), Linux. Per altri sistemi operativi, oppure se il dispositivo non dovesse essere rilevato dai sopracitati SO dovete installare il driver CP210X per il vostro sistema operativo, il driver può essere prelevato sempre dall’area download.

Al termine dell’installazione di UIFlow collegate M5StickC con il cavo USB C al computer.

M5StickC può essere programmato via USB o in modalità WiFi, in questo tutorial procederemo ad una prima programmazione in modalità USB.

Una pressione di 2 secondi sul power button, sulla sinistra di M5SickC provoca l’accensione del dispositivo, non appena compare il logo di UIFlow premete il pulsante grande M5 per accedere al menù di setting. Per spostarsi tra i menù disponibili utilizzare il pulsante sulla destra in alto di M5StickC:

Dal menù Setup potrete scegliere la modalità di programmazione e potrete impostare il WiFi. Per questo tutorial selezionate la modalità USB.

Selezionare la modalità di programmazione:

Selezionare la modalità USB:

Nell’IDE di programmazione dovete selezionare la seriale facendo click sulla parte in basso a sinistra della finestra dove compare la segnalazione COM:

Il click permetterà di aprire la finestra di selezione della COM:

Dal menù a discesa COM selezionare la porta seriale a cui avete collegato l dispositivo:

Per Windows 10 la seriale è identificata da COM seguito da un numero, mentre per sistemi Mac e Linux dalla sigla tty seguito da una serie di numeri e lettere. La modalità di selezione della seriale è la medesima per tutti i sistemi operativi.

Realizziamo il primo programma: Accensione del LED interno

Selezionare “Hardwares” dalla blocks list, selezionare LED e successivamente l’istruzione “LED ON” che dovrete trascinare nell’area di programmazione collegandola al blocco Setup.

Per eseguire il programma sul dispositivo fate click sul pulsante play che trovate in alto a destra della finestra, il LED rosso della scheda si accende.

Realizziamo il secondo programma: Blink del LED interno

Dalla sezione Event selezionare Loop ed collegarlo al Setup, nel Loop inserire la sequenza delle istruzioni: LED ON, Wait 1 S, LED OFF, Wait 1 S, così come indicato nell’immagine che segue. Per eseguire sul dispositivo un click sul pulsante Play:

Interfaccia di programmazione

01. Titolo del progetto
Inserite il nome del vostro progetto in questa area.

02. Blockly/Python
Consente di selezionare la modalità di programmazione Blockly oppure Python

03. Tab menù
Per accedere al forum, alla documentazione, agli esempi, ripetere l’ultima azione eseguita, fare l’upload dei file, eseguire il programma ed accedere alle impostazioni del dispositivo.

04. Anteprima UI
Trascinare testo e immagini sullo schermo del dispositivo virtuale per creare l’interfaccia grafica. Non appena inserite elementi grafici, appariranno nella lista delle istruzioni centrali le funzioni specifiche per manipolare gli oggetti grafici

05. Units
Aggiungere unità hardware che fanno parte del progetto tra quelle standard disponibili su M5Steck e stabilire le porte di connessione.

06. Hide UI
Possibilità di nascondere il dispositivo virtuale per aumentare lo spazio disponibile per la programmazione

07. Menù istruzioni Code Block
Contiene tutti i blocchi necessari per la programmazione. La sezione Hardware contiene le istruzioni che agiscono direttamente sull’elettronica dell’M5StickC. Troverete la sezione Math che include operazioni matematiche e Logic che raccoglie operatori logici e strutture di controllo.

08. Area di Codice
L’area in cui saranno trascinati i blocchi per realizzare il programma

Lista delle istruzioni

01. Event
Funzione Loop e gli eventi legati alla pressione dei pulsanti

02. Hardware
Blocchi dedicati all’elettronica del dispositivo: LED, IMU e gestione alimentazione

03. Units
Ogni volta che si aggiunge un’unità specifica in questa sezione appaiono i blocchi specifici alla sua gestione

04. Math
Blocchi per eseguire calcoli matematici

05. Logic
Blocchi per l’esecuzione di operazioni logiche e controllo di flusso

06. Advanced
Sezione dedicata per i programmatori esperti. Blocchi per il networking, per la gestione dei pin analogici e digitali e molto altro.

Come costruire un programma con Blockly

Setup

Il blocco Setup è essenziale per eseguire qualsiasi programma è il primo blocco che viene eseguito non appena il programma viene caricato sul dispositivo. Il Setup viene eseguito una sola volta.

Loop

Questo blocco esegui in modo continuo per sempre tutte le istruzioni in esso contenute, ciò implica che l’esecuzione del loop può essere interrotta solamente se viene tolta l’alimentazione al dispositivo.

Wait

Il blocco Wait pone in attesa il programma per un tempo specificato dal parametro numerico che può essere impostato dall’utente.

Sequenza di programmazione

Connessione blocchi

Come sicuramente avrete intuito blockly ricorda molto la filosofia di programmazione di altri ambienti di programmazione come ad esempio Scratch o il MakeCode editor di BBC micro:bit. Anche per UIFlow che utilizza blockly, i blocchi si uniscono “magneticamente” insieme, incastrandosi e cambiando colore, nel caso non si abbia un cambiamento colore vuol dire che blocchi non sono uniti.

Alcuni trucchi per chi comincia con UIFlow

  • Per duplicare un blocco tasto destro del mouse sul blocco e fare click sulla voce del menù contestuale che appare: duplicate.
  • Per cancellare un blocco trascinarlo sulla lista della collezioni delle istruzioni oppure sull’icona del cestino in basso a destra o ancora click tasto destro del mouse e “Delete block”

Nella prossima lezione vedremo come realizzare i primi programmi.

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 🙂

M5 StickC

E’ da qualche mese che sto utilizzando M5 StickC per la progettazione di future esercitazioni di laboratorio, mi piace considerarlo il fratellino 🙂 di M5Stack Fire segnalato su queste pagine qualche mese fa.
M5StickC ESP32 Development Board è una scheda di sviluppo estremamente versatile programmabile in diverse modalità e vi permette di realizzare facilmente e rapidamente progetti IoT. In questi mesi ho realizzato una serie di progetti: datalogger, telecomando WiFi e IR,  utilizzo con sensore PIR, controllo velocità motori e molto altro. Grazie all’interfaccia GROVE e GPIO è possibile connettere dispositivi esterni, sul sito ufficiale ne trovate moltissimi. Attualmente, connesso ad un piccolo altoparlante, sto sviluppando un dispositivo per persone ipovedenti.

Il sito ufficiale è: https://m5stack.com/

Sul sito di riferimento troverete moltissimi dispositivi e moduli di espansione, tra questi il già citato M5Stack FIRE ed altri prodotti molto interessanti di recente presentazione: ATOM Matrix ESP32 e M5StickV K210 AI Camera di cui vi parlerò in prossimi post e che sto utilizzando per altre tipologie di sperimentazioni.

Il piccolo “mattoncino” ha dimensioni estremamente contenute 5×2,5cm

all’interno trovano posto:

  • ESP32
  • display da 0,96 pollici (risoluzione 160×80)
  • microfono
  • buzzer
  • trasmettitore IR
  • WiFi
  • Bluetooth
  • accelerometro
  • giroscopio (6 gradi di libertà)
  • LED di segnalazione integrato
  • due pulsanti (A e B) programmabili
  • batteria da 80 mAh
  • memoria flash da 4MB
  • modalità di programmazione via USB e WiFi
  • Linguaggi di programmazione: UIFlow (blockly), C (pronto per essere programmato con IDE Arduino), MicroPython

I due video che allego di seguito forniscono una rapida presentazione sulla programmazione usando UIFlow e Arduino:

M5 Stick C può essere acquistato anche con un pratico cinturino da polso, ciò vi permetterà ad esempio di realizzare il vostro smartwatch IoT.

Per l’acquisto di M5 Stick C potete utilizzare il sito di riferimento, oppure direttamente su AliExpress. Alcuni prodotti di M5Stack potete trovarli anche su Amazon.

Per comprendere possibili applicazioni vi segnalo le sezioni del sito ufficiale:

Seguiranno a questo post una serie di tutorial sulla configurazione e aggiornamento del firmware, programmazione ed esempi di applicazioni.

Buon Making 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.