Skip to Content

Premiers pas avec MaTouch AI ESP32S3 2,8″ TFT ST7789V

Premiers pas avec MaTouch AI ESP32S3 2,8″ TFT ST7789V

Le MaTouch AI ESP32-S3 avec écran TFT ST7789V de 2,8 pouces par Makerfabs est une carte de développement compacte destinée aux projets combinant connectivité sans fil, affichage graphique et apprentissage automatique embarqué.

Construit autour du microcontrôleur ESP32-S3, il intègre des processeurs dual-core Xtensa LX7, le Wi-Fi et le Bluetooth Low Energy (BLE). L’ajout d’un écran TFT 2,8 pouces 240×320 basé sur le contrôleur ST7789V permet un affichage en couleur complète, rendant la carte adaptée aux interfaces utilisateur, à la visualisation de données et aux applications embarquées nécessitant une interaction en temps réel.

Makerfabs a positionné cette carte comme une plateforme polyvalente pour les développeurs souhaitant explorer la vision par ordinateur, la reconnaissance audio et les interfaces graphiques sans avoir besoin de modules externes. Dans ce tutoriel, vous apprendrez à démarrer avec le MaTouch AI ESP32S3 2,8″ TFT ST7789V.

Pièces requises

Vous aurez besoin d’une carte MaTouch AI ESP32S3 2,8″ TFT ST7789V et d’un câble USB-C pour programmer la carte et tester les exemples de code.

MaTouch AI ESP32S3 2,8″ TFT ST7789V

Câble 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.

Matériel du MaTouch AI ESP32S3 2,8″ TFT ST7789V

Le MaTouch ESP32-S3 2,8″ TFT est basé sur le microcontrôleur ESP32-S3, combinant un CPU dual-core Xtensa® LX7 cadencé jusqu’à 240 MHz avec 16 Mo de mémoire flash et 8 Mo de PSRAM.

Cette base matérielle garantit que la carte peut gérer des tâches complexes telles que le traitement audio en temps réel, le rendu graphique et l’inférence en apprentissage automatique directement sur l’appareil. La connectivité sans fil inclut Wi-Fi 802.11 b/g/n et Bluetooth 5.0 LE, rendant la carte polyvalente pour les applications IoT et connectées.

La photo suivante montre le dos de la carte avec les composants individuels étiquetés :

Back of MaTouch AI ESP32S3 2.8" TFT ST7789V
Dos du MaTouch AI ESP32S3 2,8″ TFT ST7789V

Affichage et tactile

La carte est équipée d’un écran TFT LCD de 2,8 pouces piloté par le contrôleur ST7789V avec une résolution de 320 × 240 pixels (QVGA) et un support pour 65K couleurs. Il se connecte via une interface SPI. Un panneau tactile capacitif multi-touch (GT911) supporte jusqu’à cinq points de contact simultanés, permettant des interfaces utilisateur intuitives pour des panneaux de contrôle ou la visualisation de données. La photo ci-dessous montre le devant de la carte avec l’écran et la caméra :

Front of Back of MaTouch AI ESP32S3 2.8" TFT ST7789V
Face avant du MaTouch AI ESP32S3 2,8″ TFT ST7789V

Sous-système audio

L’entrée audio est assurée par deux microphones MEMS numériques INMP441, fournissant une capture sonore stéréo pour des applications telles que la reconnaissance vocale ou la détection audio. Pour la sortie, la carte intègre un amplificateur I²S MAX98357, délivrant jusqu’à 3,2 W sur un haut-parleur 4 Ω, permettant la lecture directe de l’audio ou des réponses vocales sans besoin de DAC ou amplificateur externe.

Stockage et extension

Pour le stockage et la journalisation des données, la carte inclut un slot pour carte MicroSD supportant des cartes jusqu’à 32 Go. Des headers GPIO sont disponibles pour des périphériques supplémentaires tels que capteurs ou actionneurs. La photo ci-dessous montre le brochage des deux ports GPIO (J1 et J3) accessibles à l’arrière de la carte :

Pinout of GPIO ports
Brochage des ports GPIO

Support caméra

La carte intègre une interface caméra compatible avec la caméra OV3660. Cela permet de réaliser des applications de vision par ordinateur telles que classification d’images, détection d’objets ou simple capture vidéo en tirant parti des capacités d’accélération AI de l’ESP32-S3.

Gestion de l’alimentation

La carte peut être alimentée via une interface USB Type-C (4,0 V–5,25 V) et inclut un circuit de charge TP4056 pour batteries lithium-ion ou lithium-polymère. Elle dispose aussi d’un connecteur batterie, d’un interrupteur matériel, et d’un indicateur de charge MAX17048, permettant au système de surveiller la capacité et l’état de charge de la batterie.

Interfaces et contrôle

Pour le développement, la carte offre à la fois une interface USB-to-UART (CH340K) et une connectivité USB native, offrant flexibilité pour la programmation et le débogage. Les boutons Boot et Reset permettent un contrôle bas niveau lors du flashage du firmware ou du dépannage.

LED et horloge

Une LED RGB WS2812 fournit une indication visuelle d’état et peut être programmée pour des notifications ou retours utilisateur. Un module RTC (PCF8563T) assure une mesure précise du temps.

Résumé des spécifications techniques

CaractéristiqueSpécification
ContrôleurESP32-S3, CPU dual-core Xtensa LX7, jusqu’à 240 MHz
Mémoire16 Mo Flash, 8 Mo PSRAM
Sans filWi-Fi 802.11 b/g/n, Bluetooth 5.0 LE
AffichageÉcran TFT 2,8″, 320 × 240 (QVGA), contrôleur ST7789V, interface SPI
Panneau tactileCapacitif, GT911, multi-touch 5 points
Entrée audioMicrophones MEMS numériques INMP441 double canal
Sortie audioAmplificateur I²S MAX98357, 3,2 W @ 4 Ω
StockageSlot carte MicroSD (jusqu’à 32 Go)
CaméraInterface OV3660 supportée
LED RGB1 × LED programmable WS2812
RTCHorloge temps réel PCF8563T
BatteriePort batterie avec interrupteur, chargeur TP4056, indicateur de charge MAX17048
Interfaces USBUSB-to-UART (CH340K), USB natif
BoutonsBoot et Reset
AlimentationUSB Type-C 5 V (4,0–5,25 V)
Extension2 ports GPIO

Vous pouvez trouver le schéma de la carte via le lien suivant :

Installation du Core ESP32

Si c’est votre premier projet avec une carte de la série ESP32, vous devrez d’abord installer le core ESP32. Si les cartes ESP32 sont déjà installées dans votre Arduino IDE, vous pouvez passer cette section.

Commencez par ouvrir la boîte de dialogue Préférences en sélectionnant « Preferences… » dans le menu « File ». Cela ouvrira la fenêtre des Préférences affichée ci-dessous.

Sous l’onglet Settings, vous trouverez un champ de saisie en bas de la fenêtre intitulé « Additional boards manager URLs » :

Additional boards manager URLs in Preferences
URLs supplémentaires du gestionnaire de cartes dans Préférences

Dans ce champ, copiez l’URL suivante :

https://espressif.github.io/arduino-esp32/package_esp32_dev_index.json

Cela permettra à l’Arduino IDE de savoir où trouver les bibliothèques du core ESP32. Ensuite, nous installerons les cartes ESP32 via le Boards Manager.

Ouvrez le Boards Manager via « Tools -> Boards -> Board Manager ». Le gestionnaire de cartes apparaîtra dans la barre latérale gauche. Tapez « ESP32 » dans le champ de recherche en haut et vous verrez deux types de cartes ESP32 : les « Arduino ESP32 Boards » et les cartes « esp32 par Espressif ». Nous voulons les bibliothèques « esp32 par Espressif ». Cliquez sur le bouton INSTALL et attendez que le téléchargement et l’installation soient terminés.

Install ESP32 Core libraries
Installation des bibliothèques Core ESP32

Sélection de la carte

Enfin, il faut sélectionner une carte ESP32. Pour la MaTouch AI ESP32S3, choisissez le module générique « ESP32S3 Dev Module ». Pour cela, cliquez sur le menu déroulant puis sur « Select other board and port… »:

Drop-down Menu for Board Selection
Menu déroulant pour la sélection de la carte

Cela ouvrira une fenêtre où vous pourrez taper « esp32s3 dev » dans la barre de recherche. Vous verrez la carte « ESP32S3 Dev Module » dans la liste. Cliquez dessus, sélectionnez le port COM pour l’activer, puis cliquez sur OK :

Board Selection Dialog "ESP32S3 Dev Module" board
Dialogue de sélection de la carte « ESP32S3 Dev Module »

Notez que vous devez connecter la carte via le câble USB à votre ordinateur avant de pouvoir sélectionner un port COM. La carte dispose de deux ports USB, un natif et un pour TTL/UART. Pour la communication série avec la carte, utilisez le port étiqueté « USB TTL », celui le plus proche du coin :

USB TTL Port for Serial Communication
Port USB TTL pour la communication série

Paramètres de l’outil

Dans les sections suivantes, vous trouverez des exemples de code pour les différents composants matériels de la carte. Certains nécessitent une mémoire importante et vous devrez appliquer les réglages suivants pour qu’ils fonctionnent. Vous trouverez ces réglages dans le menu Tools :

Settings for  MaTouch AI ESP32S3 2.8" TFT ST7789V
Paramètres pour MaTouch AI ESP32S3 2,8″ TFT ST7789V

Le plus important : la taille de la Flash est réglée sur 16MB(128MB), le schéma de partition sur 16M Flash (3MB APP/9.9MB FATFS), la PSRAM sur OPI PSRAM. Les autres réglages doivent être par défaut.

Exemple de code : Interface série

Commençons par tester la communication série. Ouvrez votre Arduino IDE, saisissez le code suivant et téléversez-le sur la MaTouch AI ESP32S3.

void setup() {
  Serial.begin(115200);
}

void loop() {
  Serial.println("Makerguides");
  delay(2000);
}

Puis ouvrez le Moniteur Série et vous devriez voir le texte « Makerguides » s’afficher toutes les deux secondes. Sinon, vérifiez que votre câble USB est bien branché sur le bon port de la carte.

Exemple de code : LED RGB

Ensuite, testons la LED RGB intégrée. Vous devrez installer la Adafruit_NeoPixel bibliothèque pour utiliser cet exemple. Elle change simplement la couleur de la LED RGB de rouge à vert, puis bleu toutes les 200 millisecondes :

#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);
}

Si vous souhaitez plus d’informations sur la LED RGB WS2812 et son utilisation, consultez les LED Ring Clock with WS2812 et les Use WS2812B LED Strip with Arduino tutoriels.

Exemple de code : GPIO

La carte dispose de quatre broches GPIO (IO4, IO5, IO6, IO7). Pour tester les GPIO, connectez une LED avec une résistance de 220 Ohms à la masse (GND) et à une des broches GPIO. J’utilise IO4 dans cet exemple :

Connecting LED to GND and IO4
Connexion de la LED à GND et IO4

Vous pouvez maintenant utiliser le programme classique de clignotement pour allumer et éteindre la LED sur 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);                      
}

Exemple de code : Moniteur de charge de batterie

La carte MaTouch AI ESP32-S3 est équipée d’un connecteur pour batterie LiPo externe et d’un circuit de charge basé sur le TP4056 :

Battery charging circuit
Circuit de charge de batterie (source)

De plus, un MAX17048 fuel gauge permet de surveiller la charge de la batterie. Il est connecté via une interface I2C aux broches SDA=39 et SCL=38 :

MAX17048 fuel gauge
Indicateur de charge MAX17048 (source)

L’exemple de code suivant montre comment utiliser l’indicateur de charge intégré pour mesurer la tension et la capacité restante de la batterie. Notez que vous devrez installer la MAX17048 Library pour que ce code fonctionne.

#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);
}

Ce code affiche la tension actuelle et la capacité restante (en pourcentage) de la batterie dans le Moniteur Série.

Exemple de code : Écran et tactile

Le prochain exemple montre comment utiliser l’écran et le tactile. Il affiche le texte « Makerguides » au centre de l’écran et, si vous touchez l’écran, un petit cercle rouge est dessiné au point de contact. Voir l’exemple ci-dessous :

Avant d’exécuter ce code, vous devrez installer la Adafruit-GFX-Library, la Adafruit-ST7735-Library et la BitBank Capacitive Touch Sensor Library (bb_captouch). Toutes peuvent être installées via le Library Manager de l’Arduino IDE.

Jetez un œil rapide au code, puis nous discuterons de certains détails.

#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);
  }
}

Bibliothèques

Nous incluons d’abord les bibliothèques graphiques et d’affichage Adafruit, qui fournissent les fonctions de dessin pour le contrôleur ST7789, ainsi que l’en-tête bb_captouch.h donnant accès au panneau tactile capacitif.

Constantes

Ensuite, nous définissons les broches utilisées pour connecter l’écran TFT et le contrôleur tactile. Des constantes sont assignées pour la broche du rétroéclairage, les broches de contrôle de l’écran, et les broches I²C utilisées par le panneau tactile. La largeur et la hauteur de l’écran sont aussi définies pour simplifier les calculs ultérieurs.

Objets

Un objet Adafruit_ST7789 nommé tft est créé, représentant la connexion à l’écran TFT. Un objet BBCapTouch nommé touch est aussi créé, utilisé pour lire les événements tactiles du capteur capacitif.

Setup

Dans la fonction setup, la broche du rétroéclairage est configurée en sortie et activée. L’écran TFT est initialisé à une résolution de 240 par 320 pixels. La rotation est réglée à zéro, ce qui signifie que l’écran est utilisé en orientation portrait. L’écran est entièrement effacé en noir, et la couleur du texte est définie en jaune. Le programme définit une taille de police de deux, place le curseur en position (50, 150), et affiche le texte “Makerguides” à l’écran. Après la configuration de l’écran, le contrôleur tactile capacitif est initialisé avec les bonnes broches I²C et la broche de reset.

Boucle

Dans la fonction loop, on vérifie en continu si un nouvel événement tactile a été détecté. Si des données tactiles sont disponibles, le premier échantillon est lu dans les variables x et y. Ces coordonnées sont ensuite transformées pour correspondre au système de coordonnées de l’écran en les soustrayant de la largeur et de la hauteur de l’écran. Enfin, un cercle rouge de rayon cinq pixels est dessiné à l’emplacement du contact tactile.

Exemple de code : Horloge temps réel

La carte MaTouch AI ESP32-S3 contient un PCF8563T pour fournir une horloge temps réel (RTC). Voir le schéma ci-dessous :

Real Time Clock (RTC) circuit
Circuit horloge temps réel (RTC) (source)

Dans cet exemple, nous utiliserons la RTC pour garder l’heure et afficher l’heure actuelle sur l’écran TFT. Cela ressemblera à ceci :

Vous devrez installer la RTCLib pour que le code suivant fonctionne. Jetez un œil rapide au code, puis nous discuterons des détails :

#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);
}

Bibliothèques

Nous commençons par inclure les bibliothèques graphiques et d’affichage Adafruit pour dessiner à l’écran, la bibliothèque RTClib pour accéder à l’horloge temps réel, et la bibliothèque Wire pour la communication I²C.

Constantes

Ensuite, les broches pour l’écran TFT sont définies, incluant chip select, data/command, et les broches SPI. La largeur et la hauteur de l’écran sont aussi définies. Les broches pour la RTC sont listées également, avec RTC_SDA et RTC_SCL assignées aux lignes de données et d’horloge I²C, et RTC_INT définie mais non utilisée dans ce programme.

Objets

Puis, nous créons un objet Adafruit_ST7789 appelé tft pour représenter l’affichage. L’objet RTC_PCF8563 appelé rtc_pcf est créé pour communiquer avec l’horloge temps réel.

rtc_pcf_init

La fonction rtc_pcf_init() tente d’initialiser la RTC. Si l’appareil n’est pas trouvé sur le bus I²C, le programme affiche un message d’erreur et s’arrête. Si la RTC est présente, son heure est réglée à la date et l’heure de compilation du programme via rtc_pcf.adjust(). Cela garantit que l’horloge démarre avec un point de référence connu.

Setup

Dans la fonction setup, la communication série est lancée pour le débogage. La broche du rétroéclairage TFT est activée, et l’écran est initialisé à 240 par 320 pixels. La rotation est réglée à zéro, l’écran est effacé en noir, et la couleur du texte est jaune sur fond noir. La taille du texte est agrandie pour une lecture plus facile. L’interface I²C est initialisée avec les broches SDA et SCL définies, et la RTC est démarrée avec rtc_pcf_init().

Boucle

La fonction loop s’exécute en boucle et récupère l’heure actuelle de la RTC avec rtc_pcf.now(). L’heure est formatée en chaîne sous la forme “HH:MM:SS” avec sprintf, et stockée dans un tampon. Le curseur est positionné aux coordonnées (20, 130), près du centre de l’écran, et la chaîne horaire est affichée en jaune. La boucle attend ensuite une demi-seconde avant de recommencer, mettant à jour l’affichage deux fois par seconde.

En résumé, ce code initialise un écran TFT et une horloge temps réel PCF8563, règle l’horloge à l’heure de compilation, puis affiche en continu l’heure actuelle en heures, minutes et secondes au centre de l’écran.

Exemple de code : Lecture de fichier audio

La carte MaTouch AI ESP32-S3 intègre un amplificateur Class D MAX98357A pour piloter un petit haut-parleur avec 3,2 W sous 4 Ω.

Amplifier circuit
Circuit amplificateur (source)

Dans l’exemple de code suivant, nous jouons un fichier WAV stocké sur la carte 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() {}

Bibliothèques

En tête de code, les bibliothèques nécessaires sont incluses. Le driver I²S est utilisé pour communiquer avec une puce audio numérique externe, les bibliothèques SD et SPI donnent accès à la carte SD, et la bibliothèque FS définit l’interface système de fichiers.

Constantes

Les numéros de broches sont définis pour le bus SPI connecté à la carte SD, et pour les signaux I²S connectés au matériel de sortie audio. La fréquence d’échantillonnage est fixée à 16 000 échantillons par seconde, définissant la vitesse de lecture audio. Un handle pour le canal de transmission I²S est déclaré pour envoyer les données audio au haut-parleur.

SpeakerInit

La fonction SpeakerInit() configure et active la sortie I²S. D’abord, elle crée une configuration de canal par défaut pour le canal I²S 1 en mode maître. Elle initialise ensuite un nouveau canal, sauvegardant le handle de transmission dans tx_chan.

Ensuite, elle configure une configuration I²S standard : l’horloge est définie pour 16 kHz, la configuration de slot spécifie un audio mono 16 bits au format Philips I²S, et la configuration GPIO assigne les broches correctes pour le bit clock, word select et la sortie de données. Le masque de slot est limité au canal gauche uniquement, car l’audio est mono. Enfin, le canal est initialisé en mode standard et activé, rendant le matériel prêt pour la lecture audio.

playWav

La fonction playWav(const char *filename) ouvre un fichier WAV depuis la carte SD. Elle vérifie si le fichier existe et est plus grand que 44 octets, car les 44 premiers octets d’un fichier WAV sont l’en-tête et ne contiennent pas de données sonores.

Si le fichier est valide, le programme saute l’en-tête avec file.seek(44). Il lit ensuite en boucle des blocs de 1024 octets dans un tampon. Avant d’écrire chaque bloc sur l’interface I²S, les échantillons sont éventuellement amplifiés : chaque échantillon signé 16 bits est décalé d’un bit vers la gauche, doublant ainsi son amplitude.

Les données audio traitées sont ensuite écrites sur le canal de transmission I²S avec i2s_channel_write(). À la fin du fichier, le fichier est fermé, le canal I²S désactivé, et un message indique la fin de la lecture.

Setup

Dans la fonction setup(), le port série est initialisé pour le débogage. Le bus SPI est démarré sur les broches spécifiées, et la carte SD est montée. Si le montage échoue, une erreur est affichée et le programme s’arrête. Sinon, le haut-parleur I²S est initialisé, un court délai assure la stabilité, et la fonction playWav("/LightMusic.wav") est appelée pour commencer la lecture du fichier stocké sur la carte SD.

Boucle

La fonction loop() est laissée vide, car toute l’action se déroule dans setup().

En résumé, ce code configure l’ESP32-S3 pour lire un fichier WAV stocké sur la carte SD via un haut-parleur connecté en I²S. Il initialise la carte SD et la sortie I²S, saute l’en-tête du fichier, diffuse les données audio brutes vers le matériel I²S, et produit un son audible à partir de la piste audio stockée.

Exemple de code : Enregistrement audio

La carte MaTouch AI ESP32-S3 est équipée de deux INMP441 microphones à sortie numérique. Voir le schéma ci-dessous :

Microphone circuit
Circuit microphone (source)

Dans l’exemple de code suivant, nous enregistrons 10 secondes d’audio depuis le microphone et le stockons sous forme de fichier WAV sur la carte 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() {}

Bibliothèques

Au début, les bibliothèques nécessaires sont incluses. Le driver I²S donne accès à l’interface audio matérielle, les bibliothèques FS et SD permettent de créer et écrire des fichiers sur la carte SD, et la bibliothèque SPI gère la communication avec la carte SD.

Constantes

Des constantes sont définies pour les broches SPI utilisées par la carte SD et pour les broches I²S connectées au microphone numérique. La fréquence d’échantillonnage est fixée à 16 kHz, la résolution à 16 bits par échantillon, et la durée d’enregistrement à dix secondes. Un handle pour le canal de réception I²S est déclaré pour capturer les données du microphone.

writeWavHeader

La fonction writeWavHeader() écrit un en-tête WAV correct de 44 octets au début du fichier. Elle construit le conteneur RIFF, spécifie le format audio PCM, définit un canal pour l’audio mono, et fixe la fréquence d’échantillonnage, la profondeur de bits et le débit en octets. Elle écrit ensuite la section “data” suivie de la taille totale des échantillons audio. Cela garantit que le fichier résultant peut être lu par n’importe quel logiciel audio standard.

MicInit

La fonction MicInit() configure l’interface I²S pour recevoir l’audio du microphone. Elle crée un nouveau canal I²S en mode maître avec les réglages par défaut. La configuration standard spécifie une horloge pour 16 kHz, un format de slot Philips I²S avec échantillons 16 bits, et un mode mono. La configuration GPIO assigne les broches correctes pour le bit clock, word select et l’entrée de données, tout en laissant le master clock et la sortie de données inutilisés. Enfin, le canal est initialisé en mode standard et activé, prêt à capturer les échantillons audio.

recordWav

La fonction recordWav(const char *filename) réalise l’enregistrement proprement dit. Elle ouvre un fichier sur la carte SD en écriture. Si le fichier ne peut être créé, elle signale une erreur et quitte. Pour laisser de la place à l’en-tête WAV, elle écrit 44 octets nuls au début du fichier. Elle alloue ensuite un tampon de 1024 octets. Une boucle s’exécute pendant la durée définie par RECORD_TIME. Pendant cette période, les données audio sont lues du canal I²S dans le tampon et écrites directement dans le fichier. Un total cumulatif du nombre d’octets audio enregistrés est maintenu. Après la fin de l’enregistrement, la fonction revient au début du fichier et écrit le véritable en-tête WAV avec les tailles correctes. Le fichier est ensuite fermé, et un message affiche la taille finale du fichier.

Setup

Dans la fonction setup(), le port série est lancé pour le débogage. Le bus SPI est initialisé sur les broches données, et la carte SD est montée. Si le montage échoue, le programme signale une erreur et ne continue pas. Si réussi, le microphone est initialisé, un court délai laisse le temps au matériel de se stabiliser, et un message annonce le début de l’enregistrement. La fonction recordWav("/mic_record.wav") est alors appelée pour capturer et sauvegarder dix secondes d’audio sur la carte SD.

Boucle

La fonction loop() est vide, car la tâche d’enregistrement est effectuée une seule fois au démarrage.

En résumé, ce programme initialise la carte SD et le microphone I²S, enregistre dix secondes d’audio à 16 kHz dans un tampon, écrit les données sur la carte SD, ajoute un en-tête WAV correct, et produit un fichier audio standard lisible sur n’importe quel appareil.

Exemple de code : Caméra et affichage

Dans ce dernier exemple, nous capturons une vidéo en direct depuis le module caméra connecté à l’ESP32-S3 et l’affichons directement sur l’écran TFT.

Pour cet exemple, vous devrez installer la Arduino_GFX bibliothèque par moononournation. Ouvrez le LIBRARY MANAGER, tapez « GFX Library for Arduino » dans la barre de recherche, trouvez « GFX Library for Arduino » par Moon, et cliquez sur le bouton INSTALL :

Installation of GFX Library for Arduino
Installation de la GFX Library for Arduino

Voici le code complet pour diffuser la vidéo de la caméra vers l’écran. Jetez un œil rapide, puis nous discuterons des détails :

#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);
}

Bibliothèques

Au début, nous incluons les bibliothèques pour l’affichage (Arduino_GFX_Library.h) et la caméra (esp_camera.h).

Constantes

La première section définit les connexions matérielles. Les broches de la caméra sont listées pour l’interface parallèle : huit broches de données (Y2–Y9), broches d’horloge et de synchronisation (XCLK, PCLK, HREF, et VSYNC), et les broches I²C (SIOD et SIOC) qui configurent le capteur caméra. Les broches de l’écran TFT sont aussi définies pour chip select, data/command, lignes de communication SPI, et rétroéclairage.

Objets

Deux objets sont ensuite créés pour l’affichage. Le premier, bus, représente la connexion SPI à l’écran, et le second, gfx, représente le contrôleur ST7789 lui-même. Ces objets proviennent de la bibliothèque Arduino GFX, qui fournit des fonctions graphiques efficaces pour de nombreux écrans.

camera_init_s3

La fonction camera_init_s3() configure et initialise la caméra. Une structure camera_config_t est remplie avec les bonnes assignations de broches et paramètres. L’horloge externe de la caméra est réglée à 20 MHz. La taille de l’image est fixée à FRAMESIZE_QVGA, correspondant à 320×240 pixels, ce qui correspond à la résolution TFT.

Le format pixel est défini sur PIXFORMAT_RGB565, qui produit un format couleur 16 bits que le TFT peut utiliser directement sans conversion. Le mode de capture est réglé pour capturer les images dès que le tampon est vide, les tampons d’image sont stockés en PSRAM, et deux tampons sont utilisés pour améliorer les performances.

Après avoir rempli la configuration, esp_camera_init() démarre le driver caméra. Si l’initialisation échoue, un code d’erreur est affiché. Si le capteur est un OV3660, une configuration supplémentaire ajuste l’orientation et la luminosité.

Setup

Dans la fonction setup(), le port série est lancé pour le débogage. Le rétroéclairage TFT est activé, et l’écran est initialisé. L’écran est effacé en noir avant que la caméra ne soit démarrée en appelant camera_init_s3().

Boucle

La fonction loop() capture et affiche en boucle les images. Chaque appel à esp_camera_fb_get() récupère un pointeur vers le dernier tampon d’image. Si la capture échoue, un message est affiché et le programme attend brièvement avant de réessayer. Si réussi, le tampon est dessiné sur le TFT avec gfx->draw16bitBeRGBBitmap(). Cette fonction transfère directement les pixels RGB565 du tampon caméra vers l’écran à la position (0,0). Une fois l’image affichée, esp_camera_fb_return(fb) est appelé pour libérer le tampon au driver afin qu’il puisse être réutilisé.

En résumé, ce programme initialise la caméra ESP32-S3 et l’écran TFT ST7789, capture des images au format RGB565, et les diffuse directement à l’écran. Le résultat est un aperçu caméra en temps réel affiché sur le TFT.

Conclusions

Ce tutoriel vous a fourni des exemples de code pour démarrer avec le MaTouch AI ESP32-S3 avec écran TFT ST7789V de 2,8 pouces.

Pour plus d’exemples, consultez Makerfabs’s Github repo for the Matouch display et les Wiki Page. Notez cependant que la plupart des exemples là-bas utilisent la bibliothèque lvgl, que j’ai évitée ici pour garder la complexité faible. De plus, certains exemples ne fonctionnent pas avec le core 3.x actuel.

Si vous avez des questions, n’hésitez pas à les laisser dans la section commentaires.

Bon bricolage 😉