Le SPI est le bus série synchrone et rapide qui relie l’ESP32 à des périphériques exigeants — carte SD, écran TFT, capteurs à haut débit — sur quatre fils : SCK (horloge), MOSI (données sortantes), MISO (données entrantes) et un CS par périphérique. Sur ESP32, deux bus sont utilisables (VSPI et HSPI). Les broches VSPI par défaut sont SCK = GPIO18, MISO = GPIO19, MOSI = GPIO23, CS = GPIO5, et elles sont remappables, au prix d’une vitesse plafonnée si l’on quitte les broches natives.
À quoi ça sert ?
Là où l’I2C plafonne, le SPI transfère vite et en full-duplex (émission et réception simultanées) :
Stocker. Une carte microSD enregistre mesures et journaux. C’est l’usage le plus courant du SPI en projet.
Afficher richement. Les écrans TFT couleur rafraîchissent des images entières — impossible en I2C.
Lire vite. Certains ADC, capteurs de mouvement ou modules radio n’existent qu’en SPI pour leur débit.
Le fonctionnement du protocole (horloge, full-duplex, sélection par CS) est décrit dans la notion transverse SPI. Ici, on voit comment le câbler et le coder sur ESP32.
Procédure pas à pas
Quatre temps : câbler le faisceau, initialiser, exploiter via une bibliothèque, ajouter des périphériques.
1. Câbler : un faisceau partagé, un CS par module
SCK, MOSI et MISO sont communs à tous les périphériques du bus. Seul le CS (Chip Select) est propre à chacun : c’est lui qui désigne le module actif. Contrairement à l’UART, le SPI ne croise pas : MOSI va sur MOSI, MISO sur MISO.
Warning
Périphérique en 3,3 V. L’ESP32 travaille en 3,3 V : un module SPI 5 V (certains lecteurs SD à régulateur) doit soit disposer d’une adaptation de niveau intégrée, soit passer par un adaptateur (voir niveaux de tension).
2. Initialiser le bus
SPI.begin() sans argument utilise VSPI (broches par défaut). Les bibliothèques (SD, TFT) appellent souvent begin() elles-mêmes. On leur passe surtout le CS :
#include <SPI.h>const int CS = 5; // VSPI : SCK=18, MOSI=23, MISO=19 par défaut
3. Exploiter via une bibliothèque
On manipule rarement SPI.transfer() à la main : une bibliothèque (SD, Adafruit GFX pour un TFT) gère le protocole. On lui indique le CS et, au besoin, une vitesse d’horloge.
4. Ajouter des périphériques
Pour un second module, on partage SCK/MOSI/MISO et on lui attribue un autre CS (un GPIO libre). Le code active un seul CS à la fois.
Exemple — Journaliser sur carte microSD
Cas concret : écrire une ligne de mesure dans un fichier sur carte SD, puis la relire. C’est le point d’entrée du datalogger embarqué.
#include <SPI.h>#include <SD.h>const int CS = 5; // CS du lecteur SD sur GPIO5 (VSPI)void setup() { Serial.begin(115200); if (!SD.begin(CS)) { // VSPI par défaut : SCK18 / MISO19 / MOSI23 Serial.println("Carte SD absente ou mal cablee"); return; } File f = SD.open("/mesures.csv", FILE_WRITE); // ouvre (crée si besoin) if (f) { f.println("temps;valeur"); // en-tête f.println("0;512"); // une mesure f.close(); // close() obligatoire : vide le tampon sur la carte Serial.println("Ecrit."); } // relecture f = SD.open("/mesures.csv"); while (f && f.available()) { Serial.write(f.read()); // renvoie le contenu au moniteur } f.close();}void loop() {}
SD.begin(CS) initialise le bus VSPI et monte la carte. SD.open(..., FILE_WRITE) ouvre le fichier en écriture. Le close() est indispensable : il vide le tampon et referme le fichier, sans quoi la dernière écriture peut être perdue. La relecture rouvre le fichier et renvoie son contenu au moniteur.
Au moniteur :
Ecrit.
temps;valeur
0;512
Les deux dernières lignes ne viennent pas d’un println du sketch : c’est le contenu du fichier, relu octet par octet et renvoyé par Serial.write(). La preuve que l’écriture a bien atteint la carte.
Pièges
MOSI et MISO croisés. Réflexe hérité de l’UART, mais le SPI ne croise pas : MOSI sur MOSI, MISO sur MISO. Croiser donne un module muet.
CS oublié ou partagé. Deux modules sur le même CS répondent ensemble et se brouillent. Chaque périphérique a son CS sur un GPIO distinct.
Carte SD non détectée. Souvent un CS erroné, un fil MISO/MOSI inversé, ou une carte mal formatée (FAT32). Vérifier d’abord le câblage broche à broche contre le code.
Périphérique 5 V sur l’ESP32. Les lignes en 5 V agressent les broches 3,3 V. Choisir un module 3,3 V ou adapter le niveau.
Broches remappées, débit en baisse. Passer par d’autres GPIO que les broches natives fait transiter le signal par la matrice interne : la vitesse tombe (~40 MHz au lieu de 80). Pour la performance maximale, garder les broches VSPI par défaut.
Masse non reliée. Comme tout bus, le SPI exige une masse commune entre l’ESP32 et le périphérique.
Exercices
Exercice 1 — Datalogger cadencé
Reprenez la carte SD pour enregistrer une mesure chaque seconde, sans figer le programme. Indice : cadencer sur millis(), ouvrir/écrire/fermer à chaque relevé.
Corrigé
On ouvre en mode ajout, on écrit une ligne, on referme, à cadence non bloquante.
#include <SPI.h>#include <SD.h>const int CS = 5;const int CAPTEUR = 34; // ADC1unsigned long dernier = 0;void setup() { Serial.begin(115200); SD.begin(CS);}void loop() { if (millis() - dernier >= 1000) { // toutes les secondes, sans delay() dernier = millis(); File f = SD.open("/log.csv", FILE_APPEND); // ajoute à la fin if (f) { f.print(millis()); f.print(";"); f.println(analogRead(CAPTEUR)); f.close(); // referme à chaque ligne : rien n'est perdu } }}
FILE_APPEND écrit à la suite sans effacer. Refermer après chaque ligne garantit que la donnée est sur la carte même en cas de coupure.
Exercice 2 — Deux périphériques sur le bus
Ajoutez un second module SPI (par exemple un écran) à côté de la carte SD. Que partagent-ils, qu’est-ce qui les distingue ?
Corrigé
Ils partagent SCK/MOSI/MISO. Chacun a son CS sur un GPIO distinct.
const int CS_SD = 5; // carte SDconst int CS_TFT = 15; // écran, autre CSvoid setup() { pinMode(CS_SD, OUTPUT); pinMode(CS_TFT, OUTPUT); digitalWrite(CS_SD, HIGH); // au repos, les deux CS sont hauts (inactifs) digitalWrite(CS_TFT, HIGH); SPI.begin(); // faisceau VSPI partagé // chaque bibliothèque active son CS le temps de son échange}
Le faisceau (SCK/MOSI/MISO) est commun. C’est le CS, actif à l’état bas, qui désigne le module qui écoute à un instant donné.
Cas particulier — Le second bus et la vitesse
L’ESP32 offre un deuxième bus SPI, HSPI, pour séparer deux groupes de périphériques ou éviter une saturation. On l’instancie via SPIClass :
La vitesse d’horloge se règle par SPISettings (souvent porté par la bibliothèque). Les broches natives (VSPI : 18/19/23/5 ; HSPI : 14/12/13/15) autorisent jusqu’à 80 MHz. Des broches remappées plafonnent plus bas.
Sur les variantes C3 / S3, l’organisation des bus SPI et les broches par défaut diffèrent. Se reporter au brochage via configurer les GPIO.
Raccrochage projet
Preuve de concept — dès qu’il faut stocker des mesures (datalogger) ou afficher richement, le SPI est le bus adapté. L’exemple carte SD valide l’enregistrement en quelques lignes.
Intégration et tests — journaliser sur SD pendant les essais donne une trace exploitable a posteriori. L’écran TFT affiche l’état du système en autonomie.