The MaTouch AI ESP32-S3 con display TFT ST7789V da 2,8 pollici di Makerfabs è una scheda di sviluppo compatta pensata per progetti che combinano connettività wireless, output grafico e machine learning integrato.
Costruita attorno al microcontrollore ESP32-S3, integra processori dual-core Xtensa LX7, Wi-Fi e Bluetooth Low Energy (BLE). L’aggiunta di un display TFT da 2,8 pollici 240×320 basato sul driver ST7789V consente la visualizzazione a colori completi, rendendo la scheda adatta a interfacce utente, visualizzazione dati e applicazioni embedded che richiedono interazione in tempo reale.
Makerfabs ha posizionato la scheda come una piattaforma versatile per sviluppatori che vogliono esplorare visione artificiale, riconoscimento audio e interfacce grafiche senza necessità di moduli esterni. In questo tutorial imparerai come iniziare con la MaTouch AI ESP32S3 2.8″ TFT ST7789V.
Parti Necessarie
Ti serviranno una scheda MaTouch AI ESP32S3 2.8″ TFT ST7789V e un cavo USB-C per programmare la scheda e provare gli esempi di codice.

MaTouch AI ESP32S3 2.8″ TFT ST7789V

Cavo USB C
Makerguides is a participant in affiliate advertising programs designed to provide a means for sites to earn advertising fees by linking to Amazon, AliExpress, Elecrow, and other sites. As an Affiliate we may earn from qualifying purchases.
Hardware della MaTouch AI ESP32S3 2.8″ TFT ST7789V
La MaTouch ESP32-S3 2.8″ TFT è basata sul microcontrollore ESP32-S3, che combina una CPU dual-core Xtensa® LX7 fino a 240 MHz con 16 MB di memoria flash e 8 MB di PSRAM.
Questa base hardware garantisce che la scheda possa gestire compiti complessi come elaborazione audio in tempo reale, rendering grafico e inferenza di machine learning direttamente sul dispositivo. La connettività wireless include Wi-Fi 802.11 b/g/n e Bluetooth 5.0 LE, rendendo la scheda versatile per applicazioni IoT e connesse.
La foto seguente mostra il retro della scheda con i singoli componenti etichettati:

Display e Touch
La scheda dispone di un display TFT LCD da 2,8 pollici pilotato dal controller ST7789V con risoluzione di 320 × 240 pixel (QVGA) e supporto per 65K colori. Si connette tramite un’interfaccia SPI. Un pannello multi-touch capacitivo (GT911) supporta fino a cinque punti di tocco simultanei, permettendo interfacce utente intuitive per pannelli di controllo o visualizzazione dati. L’immagine sotto mostra il fronte della scheda con display e fotocamera:

Sottosistema Audio
L’ingresso audio è gestito da microfoni MEMS digitali INMP441 duali, che forniscono acquisizione audio stereo per applicazioni come riconoscimento vocale o rilevamento audio. Per l’uscita, la scheda integra un amplificatore I²S MAX98357, che eroga fino a 3,2 W su un altoparlante da 4 Ω, permettendo la riproduzione diretta di audio o risposte vocali senza bisogno di DAC o amplificatori esterni.
Memoria e Espansione
Per l’archiviazione e il logging dati, la scheda include uno slot per schede MicroSD che supporta schede fino a 32 GB. Sono disponibili header GPIO per periferiche aggiuntive come sensori o attuatori. L’immagine sotto mostra il pinout delle due porte GPIO (J1 e J3) accessibili sul retro della scheda:

Supporto Fotocamera
La scheda integra un interfaccia per fotocamera compatibile con la fotocamera OV3660. Questo permette di implementare applicazioni di visione artificiale come classificazione immagini, rilevamento oggetti o semplice acquisizione video sfruttando le capacità di accelerazione AI dell’ESP32-S3.
Gestione Energetica
La scheda può essere alimentata tramite un interfaccia USB Type-C (4,0 V–5,25 V) e include un circuito di ricarica TP4056 per batterie al litio o polimeri di litio. Presenta inoltre un connettore per batteria, un interruttore hardware e un fuel gauge MAX17048, che permette al sistema di monitorare capacità e stato di carica della batteria.
Interfacce e Controllo
Per lo sviluppo, la scheda offre sia un interfaccia USB-to-UART (CH340K) che connettività USB nativa, offrendo flessibilità nella programmazione e nel debug. I pulsanti Boot e Reset consentono il controllo a basso livello durante il flashing del firmware o la risoluzione dei problemi.
LED e Orologio
Un LED RGB WS2812 fornisce indicazioni visive di stato e può essere programmato per notifiche o feedback utente. Un modulo RTC (PCF8563T) garantisce una precisa gestione del tempo.
Riepilogo delle Specifiche Tecniche
| Caratteristica | Specifiche |
|---|---|
| Controller | ESP32-S3, CPU dual-core Xtensa LX7, fino a 240 MHz |
| Memoria | 16 MB Flash, 8 MB PSRAM |
| Wireless | Wi-Fi 802.11 b/g/n, Bluetooth 5.0 LE |
| Display | TFT LCD 2,8″, 320 × 240 (QVGA), driver ST7789V, interfaccia SPI |
| Touch Panel | Capacitivo, GT911, multi-touch a 5 punti |
| Ingresso Audio | Microfoni MEMS digitali INMP441 duali |
| Uscita Audio | Amplificatore I²S MAX98357, 3,2 W @ 4 Ω |
| Memoria | Slot per scheda MicroSD (fino a 32 GB) |
| Fotocamera | Supporto interfaccia OV3660 |
| LED RGB | 1 × LED programmabile WS2812 |
| RTC | Orologio in tempo reale PCF8563T |
| Batteria | Porta batteria con interruttore, caricatore TP4056, fuel gauge MAX17048 |
| Interfacce USB | USB-to-UART (CH340K), USB nativo |
| Pulsanti | Boot e Reset |
| Alimentazione | USB Type-C 5 V (4,0–5,25 V) |
| Espansione | 2 porte GPIO |
Puoi trovare lo schema elettrico della scheda al seguente link:
Installazione del Core ESP32
Se è il tuo primo progetto con una scheda della serie ESP32, devi prima installare il core ESP32. Se le schede ESP32 sono già installate nel tuo Arduino IDE, puoi saltare questa sezione.
Inizia aprendo la finestra Preferenze selezionando “Preferences…” dal menu “File”. Si aprirà la finestra Preferenze mostrata sotto.
Nella scheda Settings troverai una casella di modifica in fondo alla finestra etichettata “Additional boards manager URLs“:

In questo campo incolla il seguente URL:
https://espressif.github.io/arduino-esp32/package_esp32_dev_index.json
Questo permetterà all’Arduino IDE di sapere dove trovare le librerie core ESP32. Successivamente installeremo le schede ESP32 tramite il Boards Manager.
Apri il Boards Manager tramite “Tools -> Boards -> Board Manager”. Vedrai il Boards Manager apparire nella barra laterale sinistra. Digita “ESP32” nel campo di ricerca in alto e dovresti vedere due tipi di schede ESP32; le “Arduino ESP32 Boards” e le schede “esp32 di Espressif”. Noi vogliamo le librerie “esp32 di Espressif”. Clicca sul pulsante INSTALL e attendi che il download e l’installazione siano completati.

Selezione della Scheda
Infine dobbiamo selezionare una scheda ESP32. Nel caso della MaTouch AI ESP32S3, scegliamo il generico “ESP32S3 Dev Module”. Per farlo, clicca sul menu a tendina e poi su “Select other board and port…”:

Si aprirà una finestra dove puoi digitare “esp32s3 dev” nella barra di ricerca. Vedrai la scheda “ESP32S3 Dev Module” sotto Boards. Cliccaci sopra, seleziona la porta COM per attivarla e poi clicca OK:

Nota che devi collegare la scheda al computer tramite cavo USB prima di poter selezionare una porta COM. La scheda ha due porte USB, una nativa e una per TTL/UART. Per la comunicazione seriale con la scheda devi usare la porta etichettata “USB TTL”, che è quella più vicina all’angolo:

Impostazioni Strumenti
Nelle sezioni successive troverai esempi di codice per i vari componenti hardware della scheda. Alcuni richiedono memoria considerevole e avrai bisogno delle seguenti impostazioni per farli funzionare. Puoi trovare queste impostazioni nel menu Tools:

Soprattutto, Flash Size è impostato a 16MB(128Mb), Partition Scheme è 16M Flash (3MB APP/9.9MB FATFS), PSRAM è impostato su OPI PSRAM. Le altre impostazioni dovrebbero essere quelle di default.
Esempio di Codice: Interfaccia Serial
Iniziamo testando la comunicazione Serial. Apri il tuo Arduino IDE, inserisci il seguente codice e caricalo sulla MaTouch AI ESP32S3.
void setup() {
Serial.begin(115200);
}
void loop() {
Serial.println("Makerguides");
delay(2000);
}
Poi apri il Monitor Serial e dovresti vedere il testo “Makerguides” stampato ogni due secondi. In caso contrario, assicurati che il cavo USB sia collegato alla porta corretta della scheda.
Esempio di Codice: LED RGB
Proviamo ora il LED RGB integrato. Dovrai installare la Adafruit_NeoPixel libreria per usare questo esempio. Cambia semplicemente il colore del LED RGB da rosso, a verde, a blu ogni 200 millisecondi:
#include "Adafruit_NeoPixel.h"
const byte dataIn = 0;
Adafruit_NeoPixel pixels(1, dataIn, NEO_GRB + NEO_KHZ800);
void setup() {
pixels.begin();
pixels.clear();
pixels.setBrightness(50);
pixels.show();
}
void loop() {
pixels.setPixelColor(0, 255, 0, 0); // red
pixels.show();
delay(200);
pixels.setPixelColor(0, 0, 255, 0); // green
pixels.show();
delay(200);
pixels.setPixelColor(0, 0, 0, 255); // blue
pixels.show();
delay(200);
}
Se vuoi maggiori informazioni sul LED RGB WS2812 e su come usarlo, dai un’occhiata ai LED Ring Clock with WS2812 e ai Use WS2812B LED Strip with Arduino tutorial.
Esempio di Codice: GPIO
La scheda ha quattro pin GPIO (IO4, IO5, IO6, IO7). Per testare i GPIO colleghiamo un LED con una resistenza da 220 Ohm a GND e a uno dei pin GPIO. In questo esempio uso IO4:

Ora possiamo usare il solito programma blink per accendere e spegnere il LED su GPIO4:
#define LED_PIN 4
void setup() {
pinMode(LED_PIN, OUTPUT);
}
void loop() {
digitalWrite(LED_PIN, HIGH);
delay(1000);
digitalWrite(LED_PIN, LOW);
delay(1000);
}
Esempio di Codice: Monitoraggio Carica Batteria
La scheda MaTouch AI ESP32-S3 dispone di un connettore per batteria LiPo esterna e di un circuito di ricarica basato su TP4056:

Inoltre è presente un MAX17048 fuel gauge che permette di monitorare la carica della batteria. È collegato via interfaccia I2C a SDA=39 e SCL=38:

Il seguente esempio di codice mostra come usare il fuel gauge integrato per misurare la tensione e la capacità residua della batteria. Nota che dovrai installare la MAX17048 Library perché questo codice funzioni.
#include "Wire.h"
#include "MAX17048.h"
#define SDA 39
#define SCL 38
MAX17048 power;
void setup() {
Serial.begin(115200);
Wire.begin(SDA, SCL);
power.attatch(Wire);
}
void loop() {
float volts = power.voltage();
int pcnt = power.percent();
Serial.printf("%.2fV (%d%%)\n", volts, pcnt);
delay(3000);
}
Questo codice stamperà sul Monitor Serial la tensione attuale e la capacità residua (in percentuale) della batteria.
Esempio di Codice: Display e Touch Screen
Il prossimo esempio mostra come usare il display e il touch screen. Stampa il testo “Makerguides” al centro dello schermo e, se tocchi lo schermo, disegna un piccolo cerchio rosso nel punto di contatto. Vedi l’esempio sotto:

Prima di eseguire il codice, devi installare la Adafruit-GFX-Library, la Adafruit-ST7735-Library e la BitBank Capacitive Touch Sensor Library (bb_captouch). Tutte possono essere installate tramite il Library Manager dell’Arduino IDE.
Dai un’occhiata veloce al codice e poi discuteremo alcuni dettagli.
#include <Adafruit_GFX.h>
#include <Adafruit_ST7789.h>
#include <bb_captouch.h>
#define TFT_BLK 45
#define TFT_RES -1
#define TFT_SCLK 48
#define TFT_MISO 12
#define TFT_MOSI 13
#define TFT_SD_CS 47
#define TFT_CS 40
#define TFT_DC 21
#define TOUCH_INT 14
#define TOUCH_SDA 39
#define TOUCH_SCL 38
#define TOUCH_RST 18
#define SCREEN_H 320
#define SCREEN_W 240
Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RES);
BBCapTouch touch;
void setup() {
pinMode(TFT_BLK, OUTPUT);
digitalWrite(TFT_BLK, HIGH);
tft.init(SCREEN_W, SCREEN_H);
tft.setRotation(0);
tft.fillScreen(ST77XX_BLACK);
tft.setTextColor(ST77XX_YELLOW);
tft.setTextSize(2);
tft.setCursor(50, 150);
tft.println("Makerguides");
touch.init(TOUCH_SDA, TOUCH_SCL, TOUCH_RST, TOUCH_INT);
}
void loop() {
TOUCHINFO ti;
if (touch.getSamples(&ti)) {
int x = SCREEN_W - ti.x[0];
int y = SCREEN_H - ti.y[0];
tft.fillCircle(x, y, 5, ST77XX_RED);
}
}
Librerie
Per prima cosa includiamo le librerie Adafruit per grafica e display, che forniscono funzioni di disegno per il controller ST7789, e l’header bb_captouch.h, che permette l’accesso al pannello touch capacitivo.
Costanti
Definiamo i pin usati per collegare il display TFT e il controller touch. Sono assegnate costanti per il pin della retroilluminazione, i pin di controllo del display e i pin I²C usati dal touch panel. Sono definite anche larghezza e altezza dello schermo per semplificare i calcoli successivi.
Oggetti
Viene creato un oggetto Adafruit_ST7789 chiamato tft, che rappresenta la connessione allo schermo TFT. Viene creato anche un oggetto BBCapTouch chiamato touch, che sarà usato per leggere gli eventi touch dal sensore capacitivo.
Setup
Nella funzione setup, prima il pin della retroilluminazione del display è configurato come output e acceso. Il display TFT viene inizializzato con risoluzione 240×320 pixel. La rotazione è impostata a zero, quindi lo schermo è in orientamento verticale. L’intero schermo viene pulito con il colore nero e il colore del testo impostato a giallo. Il programma imposta una dimensione del font pari a due, sposta il cursore alla posizione (50, 150) e stampa il testo “Makerguides” sullo schermo. Dopo aver configurato il display, il controller touch capacitivo viene inizializzato con i pin I²C e il pin di reset corretti.
Loop
Nella funzione loop controlliamo continuamente se è stato rilevato un nuovo evento touch. Se sono disponibili dati touch, il primo campione viene letto nelle variabili x e y. Queste coordinate vengono poi trasformate per corrispondere al sistema di coordinate dello schermo sottraendole dalla larghezza e altezza dello schermo. Infine, disegniamo un cerchio rosso con raggio di cinque pixel nel punto di contatto.
Esempio di Codice: Orologio in Tempo Reale
La scheda MaTouch AI ESP32-S3 contiene un PCF8563T per fornire un orologio in tempo reale (RTC). Vedi lo schema sotto:

In questo esempio useremo il RTC per tenere il tempo e mostrare l’ora corrente sul display TFT. Avrà questo aspetto:

Dovrai installare la RTCLib perché il codice seguente funzioni. Dai un’occhiata veloce al codice e poi discuteremo i dettagli:
#include <Adafruit_GFX.h>
#include <Adafruit_ST7789.h>
#include <RTClib.h>
#include <Wire.h>
#define TFT_BLK 45
#define TFT_RES -1
#define TFT_SCLK 48
#define TFT_MISO 12
#define TFT_MOSI 13
#define TFT_CS 40
#define TFT_DC 21
#define SCREEN_W 240
#define SCREEN_H 320
// RTC pins
#define RTC_SCL 38
#define RTC_SDA 39
#define RTC_INT 15
Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RES);
RTC_PCF8563 rtc_pcf;
void rtc_pcf_init() {
if (!rtc_pcf.begin()) {
Serial.println("Couldn't find RTC");
Serial.flush();
while (1) delay(10);
}
rtc_pcf.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
void setup() {
Serial.begin(115200);
pinMode(TFT_BLK, OUTPUT);
digitalWrite(TFT_BLK, HIGH);
tft.init(SCREEN_W, SCREEN_H);
tft.setRotation(0);
tft.fillScreen(ST77XX_BLACK);
tft.setTextColor(ST77XX_YELLOW, ST77XX_BLACK);
tft.setTextSize(4);
Wire.begin(RTC_SDA, RTC_SCL);
rtc_pcf_init();
}
void loop() {
static char buf[16];
DateTime now = rtc_pcf.now();
sprintf(buf, "%02d:%02d:%02d", now.hour(), now.minute(), now.second());
tft.setCursor(20, 130);
tft.print(buf);
delay(500);
}
Librerie
Iniziamo includendo le librerie Adafruit per grafica e display per disegnare sullo schermo, la libreria RTClib per accedere all’orologio in tempo reale, e la libreria Wire per la comunicazione I²C.
Costanti
Definiamo i pin per il display TFT, inclusi chip select, data/command e pin SPI. Sono definite anche larghezza e altezza dello schermo. Sono elencati anche i pin per il RTC, con RTC_SDA e RTC_SCL assegnati alle linee dati e clock I²C, e RTC_INT definito ma non usato in questo programma.
Oggetti
Creiamo un oggetto Adafruit_ST7789 chiamato tft per rappresentare il display. L’oggetto RTC_PCF8563 chiamato rtc_pcf è creato per comunicare con l’orologio in tempo reale.
rtc_pcf_init
La funzione rtc_pcf_init() tenta di inizializzare il RTC. Se il dispositivo non viene trovato sul bus I²C, il programma stampa un messaggio di errore e si ferma. Se il RTC è presente, l’ora viene impostata alla data e ora di compilazione del programma usando rtc_pcf.adjust(). Questo assicura che l’orologio parta da un punto di riferimento noto.
Setup
Nella funzione setup(), la comunicazione seriale viene avviata per il debug. Il pin della retroilluminazione TFT è abilitato e il display inizializzato a 240×320 pixel. La rotazione è impostata a zero, lo schermo viene pulito a nero e il colore del testo impostato a giallo su sfondo nero. La dimensione del testo è aumentata per una lettura più facile. L’interfaccia I²C è inizializzata con i pin SDA e SCL definiti, e il RTC è avviato con rtc_pcf_init().
Loop
La funzione loop() viene eseguita ripetutamente e recupera l’ora corrente dal RTC con rtc_pcf.now(). L’ora è formattata come stringa nel formato “HH:MM:SS” usando sprintf, e memorizzata in un buffer. Il cursore è posizionato alle coordinate (20, 130), vicino al centro dello schermo, e la stringa dell’ora è disegnata sul display in giallo. Il loop poi attende mezzo secondo prima di ripetere, così il display si aggiorna due volte al secondo.
In sintesi, questo codice inizializza un display TFT e un orologio in tempo reale PCF8563, imposta l’orologio all’ora di compilazione e poi mostra continuamente l’ora corrente in ore, minuti e secondi al centro dello schermo.
Esempio di Codice: Riproduzione File Audio
La scheda MaTouch AI ESP32-S3 ha un amplificatore di classe D integrato MAX98357A per pilotare un piccolo altoparlante (3,2 W @ 4 Ω).

Nel seguente esempio di codice riproduciamo un file WAV memorizzato sulla scheda SD:
#include <driver/i2s_std.h>
#include "FS.h"
#include "SD.h"
#include "SPI.h"
#define SCLK 48
#define MISO 12
#define MOSI 13
#define SD_CS 47
// Speaker pins
#define I2S_OUT_BCLK 20
#define I2S_OUT_LRC 1
#define I2S_OUT_DOUT 19
#define SAMPLE_RATE 16000U
static i2s_chan_handle_t tx_chan;
void SpeakerInit() {
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_1, I2S_ROLE_MASTER);
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_chan, NULL));
// Standard mode, 16-bit mono
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT,
I2S_SLOT_MODE_MONO),
.gpio_cfg = {
.mclk = I2S_GPIO_UNUSED,
.bclk = (gpio_num_t)I2S_OUT_BCLK,
.ws = (gpio_num_t)I2S_OUT_LRC,
.dout = (gpio_num_t)I2S_OUT_DOUT,
.din = I2S_GPIO_UNUSED,
},
};
std_cfg.slot_cfg.slot_mask = I2S_STD_SLOT_LEFT; // left channel only
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_chan, &std_cfg));
ESP_ERROR_CHECK(i2s_channel_enable(tx_chan));
}
void playWav(const char *filename) {
File file = SD.open(filename);
if (!file || file.size() <= 44) {
Serial.println("Invalid or missing WAV file.");
return;
}
file.seek(44); // skip header
uint8_t buffer[1024];
size_t bytesRead, bytesWritten;
while ((bytesRead = file.read(buffer, sizeof(buffer))) > 0) {
// optional ×2 volume boost
for (int i = 0; i < bytesRead; i += 2) {
int16_t *s = (int16_t *)&buffer[i];
*s <<= 1;
}
i2s_channel_write(tx_chan, buffer, bytesRead, &bytesWritten, portMAX_DELAY);
}
file.close();
i2s_channel_disable(tx_chan);
Serial.println("Playback finished.");
}
void setup() {
Serial.begin(115200);
SPI.begin(SCLK, MISO, MOSI);
if (!SD.begin(SD_CS, SPI, 80'000'000)) {
Serial.println(F("ERROR: SD mount failed!"));
return;
}
SpeakerInit();
delay(500);
playWav("/LightMusic.wav");
}
void loop() {}
Librerie
All’inizio del codice sono incluse le librerie necessarie. Il driver I²S è usato per comunicare con un chip audio digitale esterno, le librerie SD e SPI forniscono accesso alla scheda SD, e la libreria FS definisce l’interfaccia del file system.
Costanti
Sono definiti i numeri dei pin per il bus SPI che collega la scheda SD e per i segnali I²S che collegano l’hardware audio di uscita. La frequenza di campionamento è impostata a 16.000 campioni al secondo, che definisce la velocità di riproduzione audio. È dichiarato un handle per il canale di trasmissione I²S per inviare i dati audio all’altoparlante.
SpeakerInit
La funzione SpeakerInit() configura e abilita l’uscita I²S. Prima crea una configurazione di canale predefinita per il canale I²S 1 in modalità master. Poi inizializza un nuovo canale, salvando l’handle di trasmissione in tx_chan.
Successivamente imposta una configurazione I²S standard: l’orologio è definito per la frequenza di 16 kHz, la configurazione dello slot specifica audio mono a 16 bit in formato Philips I²S, e la configurazione GPIO assegna i pin corretti per bit clock, word select e uscita dati. La maschera dello slot è limitata al canale sinistro, poiché si riproduce audio mono. Infine, il canale è inizializzato in modalità standard e abilitato, rendendo l’hardware pronto per la riproduzione audio.
playWav
La funzione playWav(const char *filename) apre un file WAV dalla scheda SD. Controlla se il file esiste ed è più grande di 44 byte, perché i primi 44 byte di un file WAV sono l’intestazione e non contengono dati audio veri.
Se il file è valido, il programma salta l’intestazione con file.seek(44). Poi legge ripetutamente blocchi di 1024 byte in un buffer. Prima di scrivere ogni blocco sull’interfaccia I²S, i campioni sono opzionalmente amplificati: ogni campione a 16 bit con segno è shiftato a sinistra di un bit, raddoppiandone l’ampiezza.
I dati audio processati sono poi scritti sul canale di trasmissione I²S usando i2s_channel_write(). Quando si raggiunge la fine del file, il file viene chiuso, il canale I²S disabilitato e viene stampato un messaggio che indica la fine della riproduzione.
Setup
Nella funzione setup(), la porta seriale è inizializzata per il debug. Il bus SPI è avviato sui pin specificati e la scheda SD è montata. Se il montaggio fallisce, viene stampato un errore e il programma si ferma. Altrimenti, l’altoparlante I²S è inizializzato, un breve ritardo assicura stabilità, e la funzione playWav("/LightMusic.wav") è chiamata per iniziare la riproduzione del file memorizzato sulla scheda SD.
Loop
La funzione loop() è lasciata vuota, poiché tutta l’azione avviene una sola volta in setup().
In sintesi, questo codice configura l’ESP32-S3 per riprodurre un file WAV memorizzato sulla scheda SD tramite un altoparlante collegato in I²S. Configura la scheda SD e l’uscita I²S, salta l’intestazione del file, trasmette i dati audio grezzi all’hardware I²S e produce suono udibile dalla traccia audio memorizzata.
Esempio di Codice: Registrazione Audio
La scheda MaTouch AI ESP32-S3 è dotata di due INMP441 microfoni a uscita digitale. Vedi lo schema sotto:

Nel seguente esempio di codice registriamo 10 secondi di audio dal microfono e lo salviamo come file WAV sulla scheda SD:
#include <driver/i2s_std.h>
#include "FS.h"
#include "SD.h"
#include "SPI.h"
#define SCLK 48
#define MISO 12
#define MOSI 13
#define SD_CS 47
// Microphone pins
#define I2S_IN_BCLK 42
#define I2S_IN_LRC 2
#define I2S_IN_DIN 41
#define SAMPLE_RATE 16000U
#define SAMPLE_BITS 16
#define RECORD_TIME 10 // seconds
static i2s_chan_handle_t rx_chan;
void writeWavHeader(File &file, uint32_t dataSize) {
uint32_t chunkSize = 36 + dataSize;
uint16_t audioFormat = 1; // PCM
uint16_t numChannels = 1;
uint32_t sampleRate = SAMPLE_RATE; // copy macro into variable
uint16_t bitsPerSample = SAMPLE_BITS; // copy macro into variable
uint32_t byteRate = sampleRate * numChannels * bitsPerSample / 8;
uint16_t blockAlign = numChannels * bitsPerSample / 8;
file.seek(0);
file.write((const uint8_t *)"RIFF", 4);
file.write((uint8_t *)&chunkSize, 4);
file.write((const uint8_t *)"WAVE", 4);
file.write((const uint8_t *)"fmt ", 4);
uint32_t subChunk1Size = 16;
file.write((uint8_t *)&subChunk1Size, 4);
file.write((uint8_t *)&audioFormat, 2);
file.write((uint8_t *)&numChannels, 2);
file.write((uint8_t *)&sampleRate, 4);
file.write((uint8_t *)&byteRate, 4);
file.write((uint8_t *)&blockAlign, 2);
file.write((uint8_t *)&bitsPerSample, 2);
file.write((const uint8_t *)"data", 4);
file.write((uint8_t *)&dataSize, 4);
}
void MicInit() {
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, NULL, &rx_chan)); // RX channel only
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT,
I2S_SLOT_MODE_MONO),
.gpio_cfg = {
.mclk = I2S_GPIO_UNUSED,
.bclk = (gpio_num_t)I2S_IN_BCLK,
.ws = (gpio_num_t)I2S_IN_LRC,
.dout = I2S_GPIO_UNUSED,
.din = (gpio_num_t)I2S_IN_DIN,
},
};
std_cfg.slot_cfg.slot_mask = I2S_STD_SLOT_RIGHT;
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_chan, &std_cfg));
ESP_ERROR_CHECK(i2s_channel_enable(rx_chan));
}
void recordWav(const char *filename) {
File file = SD.open(filename, FILE_WRITE);
if (!file) {
Serial.println("Failed to open file for writing!");
return;
}
// Reserve header space
for (int i = 0; i < 44; i++) file.write((uint8_t)0);
const size_t bufferSize = 1024;
uint8_t buffer[bufferSize];
size_t bytesRead;
uint32_t totalBytes = 0;
uint32_t startMs = millis();
while ((millis() - startMs) < RECORD_TIME * 1000) {
if (i2s_channel_read(rx_chan, buffer, bufferSize, &bytesRead, portMAX_DELAY) == ESP_OK) {
file.write(buffer, bytesRead);
totalBytes += bytesRead;
}
}
// Write real header
writeWavHeader(file, totalBytes);
file.close();
Serial.printf("Recording finished: %s (%lu bytes)\n", filename, (unsigned long)totalBytes);
}
void setup() {
Serial.begin(115200);
SPI.begin(SCLK, MISO, MOSI);
if (!SD.begin(SD_CS, SPI, 80'000'000)) {
Serial.println("SD mount failed!");
return;
}
MicInit();
delay(2000);
Serial.println("Recording...");
recordWav("/mic_record.wav");
}
void loop() {}
Librerie
All’inizio sono incluse le librerie necessarie. Il driver I²S fornisce accesso all’interfaccia hardware audio, le librerie FS e SD permettono di creare e scrivere file sulla scheda SD, e la libreria SPI gestisce la comunicazione con la scheda SD.
Costanti
Sono definite costanti per i pin SPI usati dalla scheda SD e per i pin I²S collegati al microfono digitale. La frequenza di campionamento è impostata a 16 kHz, la risoluzione a 16 bit per campione, e la durata della registrazione a dieci secondi. È dichiarato un handle per il canale di ricezione I²S per catturare i dati del microfono.
writeWavHeader
La funzione writeWavHeader() scrive una corretta intestazione WAV di 44 byte all’inizio del file. Costruisce il contenitore RIFF, specifica il formato audio come PCM, definisce un canale per audio mono, e imposta frequenza di campionamento, profondità bit e byte rate. Poi scrive l’intestazione della sezione “data” seguita dalla dimensione totale dei campioni audio. Questo garantisce che il file risultante possa essere riprodotto da qualsiasi software audio standard.
MicInit
La funzione MicInit() configura l’interfaccia I²S per ricevere audio dal microfono. Crea un nuovo canale I²S in modalità master con impostazioni predefinite. La configurazione standard specifica un clock per la frequenza di 16 kHz, formato slot Philips I²S con campioni a 16 bit, e modalità canale mono. La configurazione GPIO assegna i pin corretti per bit clock, word select e ingresso dati, lasciando inutilizzati master clock e uscita dati. Infine, il canale è inizializzato in modalità standard e abilitato, pronto per catturare campioni audio.
recordWav
La funzione recordWav(const char *filename) esegue la registrazione vera e propria. Apre un file sulla scheda SD per la scrittura. Se il file non può essere creato, segnala un errore ed esce. Per lasciare spazio all’intestazione WAV, scrive 44 byte zero all’inizio del file. Poi alloca un buffer di 1024 byte. Un ciclo gira per la durata definita da RECORD_TIME. Durante questo periodo, i dati audio sono letti dal canale I²S nel buffer e scritti direttamente nel file. Viene mantenuto un totale dei byte audio registrati. Dopo la fine della registrazione, la funzione torna all’inizio del file e scrive la vera intestazione WAV con le dimensioni corrette. Il file viene chiuso e viene stampato un messaggio con la dimensione finale del file.
Setup
Nella funzione setup(), la porta seriale è avviata per il debug. Il bus SPI è inizializzato sui pin indicati e la scheda SD è montata. Se il montaggio fallisce, il programma segnala un errore e non continua. Se ha successo, il microfono è inizializzato, un breve ritardo lascia tempo all’hardware di stabilizzarsi, e un messaggio annuncia l’inizio della registrazione. La funzione recordWav("/mic_record.wav") è poi chiamata per catturare e salvare dieci secondi di audio sulla scheda SD.
Loop
La funzione loop() è vuota, poiché la registrazione avviene una sola volta all’avvio.
In sintesi, questo programma inizializza la scheda SD e il microfono I²S, registra dieci secondi di audio a 16 kHz in un buffer, scrive i dati sulla scheda SD, aggiunge una corretta intestazione WAV e produce un file audio standard riproducibile su qualsiasi dispositivo.
Esempio di Codice: Fotocamera e Display
In quest’ultimo esempio catturiamo video in diretta dal modulo fotocamera collegato all’ESP32-S3 e lo visualizziamo direttamente sullo schermo TFT.
Per questo esempio dovrai installare la Arduino_GFX libreria di moononournation. Apri il LIBRARY MANAGER, digita “GFX Library for Arduino” nella barra di ricerca, trova “GFX Library for Arduino” di Moon e clicca sul pulsante INSTALL:

Di seguito il codice completo per lo streaming video dalla fotocamera al display. Dai un’occhiata veloce e poi discuteremo i dettagli:
#include <Arduino_GFX_Library.h>
#include "esp_camera.h"
// === Camera pins ===
#define PWDN_GPIO_NUM -1
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 9 // CSI_MCLK
#define SIOD_GPIO_NUM 39 // TWI_SDA
#define SIOC_GPIO_NUM 38 // TWI_SCK
#define Y9_GPIO_NUM 46 // CSI D7
#define Y8_GPIO_NUM 3 // CSI D6
#define Y7_GPIO_NUM 8 // CSI D5
#define Y6_GPIO_NUM 16 // CSI D4
#define Y5_GPIO_NUM 6 // CSI D3
#define Y4_GPIO_NUM 4 // CSI D2
#define Y3_GPIO_NUM 5 // CSI D1
#define Y2_GPIO_NUM 7 // CSI D0
#define VSYNC_GPIO_NUM 11 // CSI VSYNC
#define HREF_GPIO_NUM 10 // CSI HSYNC
#define PCLK_GPIO_NUM 17 // CSI PCLK
// === TFT pins ===
#define TFT_BLK 45
#define TFT_RES -1
#define TFT_CS 40
#define TFT_DC 21
#define MOSI 13
#define MISO 12
#define SCLK 48
Arduino_ESP32SPI *bus = new Arduino_ESP32SPI(
TFT_DC, TFT_CS, SCLK, MOSI, MISO, HSPI, true
);
Arduino_GFX *gfx = new Arduino_ST7789(
bus, TFT_RES, 1 , true
);
// === Camera init ===
void camera_init_s3() {
camera_config_t config;
config.ledc_channel = LEDC_CHANNEL_0;
config.ledc_timer = LEDC_TIMER_0;
config.pin_d0 = Y2_GPIO_NUM;
config.pin_d1 = Y3_GPIO_NUM;
config.pin_d2 = Y4_GPIO_NUM;
config.pin_d3 = Y5_GPIO_NUM;
config.pin_d4 = Y6_GPIO_NUM;
config.pin_d5 = Y7_GPIO_NUM;
config.pin_d6 = Y8_GPIO_NUM;
config.pin_d7 = Y9_GPIO_NUM;
config.pin_xclk = XCLK_GPIO_NUM;
config.pin_pclk = PCLK_GPIO_NUM;
config.pin_vsync = VSYNC_GPIO_NUM;
config.pin_href = HREF_GPIO_NUM;
config.pin_sccb_sda = SIOD_GPIO_NUM;
config.pin_sccb_scl = SIOC_GPIO_NUM;
config.pin_pwdn = PWDN_GPIO_NUM;
config.pin_reset = RESET_GPIO_NUM;
config.xclk_freq_hz = 20000000;
config.frame_size = FRAMESIZE_QVGA; // 320x240
config.pixel_format = PIXFORMAT_RGB565; // direct TFT compatible
config.grab_mode = CAMERA_GRAB_WHEN_EMPTY;
config.fb_location = CAMERA_FB_IN_PSRAM;
config.jpeg_quality = 12;
config.fb_count = 2;
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("Camera init failed with error 0x%x\n", err);
return;
}
sensor_t *s = esp_camera_sensor_get();
if (s->id.PID == OV3660_PID) {
s->set_hmirror(s, 1);
s->set_vflip(s, 1);
}
}
void setup() {
Serial.begin(115200);
pinMode(TFT_BLK, OUTPUT);
digitalWrite(TFT_BLK, HIGH);
gfx->begin();
gfx->fillScreen(BLACK);
camera_init_s3();
}
void loop() {
camera_fb_t *fb = esp_camera_fb_get();
if (!fb) {
Serial.println("Camera capture failed");
delay(100);
return;
}
gfx->draw16bitBeRGBBitmap(0, 0, (uint16_t *)fb->buf, fb->width, fb->height);
esp_camera_fb_return(fb);
}
Librerie
All’inizio includiamo le librerie per il display (Arduino_GFX_Library.h) e per la fotocamera (esp_camera.h).
Costanti
La prima sezione definisce le connessioni hardware. I pin della fotocamera sono elencati per l’interfaccia parallela: otto pin dati (Y2–Y9), pin di clock e sincronizzazione (XCLK, PCLK, HREF, e VSYNC), e i pin I²C (SIOD e SIOC) che configurano il sensore della fotocamera. Sono definiti anche i pin del display TFT per chip select, data/command, linee di comunicazione SPI e retroilluminazione.
Oggetti
Vengono creati due oggetti per il display. Il primo, bus, rappresenta la connessione SPI al display, e il secondo, gfx, rappresenta il controller ST7789 stesso. Questi oggetti provengono dalla libreria Arduino GFX, che fornisce funzioni grafiche efficienti per molti display.
camera_init_s3
La funzione camera_init_s3() configura e inizializza la fotocamera. Una struttura camera_config_t è compilata con le assegnazioni pin e parametri corretti. Il clock esterno per la fotocamera è impostato a 20 MHz. La dimensione del frame è impostata a FRAMESIZE_QVGA, corrispondente a 320×240 pixel, in linea con la risoluzione TFT.
Il formato pixel è impostato a PIXFORMAT_RGB565, che produce un formato colore a 16 bit che il TFT può usare direttamente senza conversione. La modalità di acquisizione è impostata per catturare frame ogni volta che il buffer è vuoto, i buffer dei frame sono memorizzati in PSRAM, e sono usati due buffer per migliorare le prestazioni.
Dopo aver compilato la configurazione, esp_camera_init() avvia il driver della fotocamera. Se l’inizializzazione fallisce, viene stampato un codice di errore. Se il sensore è un OV3660, viene applicata una configurazione extra per regolare orientamento e luminosità.
Setup
Nella funzione setup(), la porta seriale è avviata per il debug. La retroilluminazione TFT è abilitata e il display inizializzato. Lo schermo è pulito a nero prima che la fotocamera venga avviata chiamando camera_init_s3().
Loop
La funzione loop() cattura e visualizza ripetutamente i frame. Ogni chiamata a esp_camera_fb_get() recupera un puntatore all’ultimo buffer frame. Se la cattura fallisce, viene stampato un messaggio e il programma attende brevemente prima di riprovare. Se ha successo, il buffer frame è disegnato sul TFT usando gfx->draw16bitBeRGBBitmap(). Questa funzione trasferisce direttamente i dati pixel RGB565 dal buffer della fotocamera al display in posizione (0,0). Una volta visualizzato il frame, esp_camera_fb_return(fb) è chiamato per rilasciare il buffer al driver affinché possa essere riutilizzato.
In sintesi, questo programma inizializza la fotocamera ESP32-S3 e il display TFT ST7789, cattura frame in formato RGB565 e li trasmette direttamente allo schermo. Il risultato è un’anteprima video in tempo reale visualizzata sul TFT.
Conclusioni
Questo tutorial ti ha fornito esempi di codice per iniziare con la MaTouch AI ESP32-S3 con display TFT ST7789V da 2,8 pollici.
Per altri esempi vedi Makerfabs’s Github repo for the Matouch display e il Wiki Page. Nota però che la maggior parte degli esempi lì usa la libreria lvgl, che ho evitato qui per mantenere bassa la complessità. Inoltre, alcuni esempi non funzionano con l’attuale core 3.x.
Se hai domande, sentiti libero di lasciarle nella sezione commenti.
Buon divertimento con il tinkering 😉

