Introduction
L’écran OLED SSD1306 est un afficheur graphique monochrome compact et économe en énergie, idéal pour les projets embarqués. Associé à l’ESP32, il permet d’afficher des textes, graphiques, icônes et interfaces utilisateur sans rétroéclairage.
Dans ce tutoriel, vous apprendrez à :
- Comprendre le protocole I2C utilisé par le SSD1306
- Intégrer un écran OLED directement sur un PCB avec l’ESP32
- Afficher du texte, des graphiques et des images bitmap
- Optimiser la consommation mémoire et les performances
L’ESP32 fonctionne en logique 3.3V. Le SSD1306 accepte une alimentation de 3.0V à 5.5V et ses entrées I2C sont compatibles 3.3V. La connexion directe est possible sans adaptateur de niveau.
Le protocole I2C et le SSD1306
Le bus I2C
Le SSD1306 communique via le protocole I2C (Inter-Integrated Circuit), un bus série à 2 fils :
| Signal |
Rôle |
| SDA (Data) |
Ligne bidirectionnelle de données |
| SCL (Clock) |
Horloge générée par le maître (ESP32) |
L’I2C permet de connecter plusieurs périphériques sur le même bus grâce à un système d’adresses uniques.
Broches I2C par défaut (ESP32-S3)
| Fonction |
GPIO ESP32-S3 |
| SDA |
GPIO 8 |
| SCL |
GPIO 9 |
Sur ESP32-S3, le bus I2C peut être assigné à n’importe quelles broches via Wire.begin(SDA, SCL). Les broches GPIO 8 et 9 sont celles utilisées dans ce tutoriel et correspondent au câblage de la carte ESP32 S3 UNO.
Caractéristiques du SSD1306
- Résolution : 128×64 pixels (version 0.96”) ou 128×32 (version 0.91”)
- Adresse I2C : 0x3C (par défaut) ou 0x3D (configurable par hardware)
- Alimentation : 3.0V à 5.5V
- Courant : ~20mA en affichage normal, < 10µA en veille
- Technologie : OLED (auto-émissif, pas de rétroéclairage)
- Contraste : Réglable par logiciel (0-255)
Intégration sur PCB
Choix du module
Il existe deux formats principaux :
- Module breakout I2C : 4 broches (VCC, GND, SCL, SDA), pull-up intégrées
- Module raw : 7-8 broches, nécessite des pull-up externes
Pour un PCB, préférez un module breakout avec pull-up intégrées (simplifie le routage).
La plupart des modules utilisent l’adresse 0x3C par défaut. Certains modules exposent un jumper ou un pad de soudure pour basculer à 0x3D si plusieurs écrans doivent coexister sur le même bus I2C.
Schéma de connexion
ESP32
GPIO8 ─────────── SDA ─┐
GPIO9 ─────────── SCL ─┤ Écran SSD1306
3.3V ─────────── VCC ─┤ (module I2C)
GND ─────────── GND ─┘
Résistances de pull-up
Les lignes I2C nécessitent des résistances de pull-up vers 3.3V :
Pull-up sur SDA et SCL
Ajoutez une résistance de 4.7kΩ vers 3.3V sur les lignes suivantes :
- SDA : maintient la ligne haute quand aucun périphérique ne transmet
- SCL : maintient la ligne haute entre les impulsions d’horloge
Attention : La plupart des modules breakout OLED intègrent déjà ces pull-up (typiquement 10kΩ). Vérifiez votre schéma avant d’en ajouter. Des pull-up trop faibles (< 2.2kΩ au total) peuvent causer des problèmes de communication.
Condensateurs de découplage
Découplage de l'alimentation
Placez les condensateurs suivants entre VCC et GND du module, au plus près de la broche VCC :
- 100nF en céramique : filtre les parasites haute fréquence
- 10µF en céramique ou tantale : absorbe les pics de courant lors du rafraîchissement de l’écran
Ces condensateurs stabilisent l’alimentation et évitent les scintillements ou corruptions d’affichage.
Checklist avant la mise sous tension
Vérifications avant le premier test
Avant d’alimenter votre PCB pour la première fois :
- Vérifiez visuellement les soudures du module (pas de court-circuit entre les pads)
- Vérifiez la continuité entre GND du module et GND de l’ESP32
- Vérifiez la continuité entre VCC du module et 3.3V
- Vérifiez la présence des condensateurs de découplage (100nF + 10µF)
- Vérifiez que SDA et SCL ne sont pas inversés
- Mesurez 3.3V sur VCC avant d’alimenter l’ESP32
Code
La bibliothèque Adafruit_SSD1306 est la plus répandue. Ajoutez-la dans platformio.ini :
[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200
lib_deps =
adafruit/Adafruit SSD1306@^2.5.7
adafruit/Adafruit GFX Library@^1.11.3
La bibliothèque Adafruit_SSD1306 dépend de Adafruit_GFX_Library pour les fonctions de dessin (lignes, cercles, texte). Les deux doivent être installées.
Code 1 : Initialisation et affichage de texte
#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1 // Pas de pin RESET (partagé avec ESP32)
#define SCREEN_ADDRESS 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
void setup() {
Serial.begin(115200);
delay(500);
// Initialiser l'I2C
Wire.begin(8, 9); // SDA=8, SCL=9
// Initialiser l'écran
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println("Erreur : écran SSD1306 non détecté");
while (true) delay(1000);
}
Serial.println("Écran SSD1306 initialisé avec succès !");
// Effacer l'écran
display.clearDisplay();
// Configurer le texte
display.setTextSize(1); // Taille normale
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
// Afficher du texte
display.println("ESP32 + SSD1306");
display.println("");
display.setTextSize(2);
display.println("Hello!");
// Envoyer vers l'écran
display.display();
}
void loop() {}
L’affichage est bufferisé : toutes les fonctions de dessin modifient un tampon RAM. Il faut appeler display.display() pour envoyer le tampon vers l’écran physique. Cela permet d’optimiser les performances en regroupant les mises à jour.
#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define SCREEN_ADDRESS 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
void setup() {
Serial.begin(115200);
Wire.begin(8, 9);
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println("Erreur SSD1306");
while (true);
}
display.clearDisplay();
// Dessiner un rectangle
display.drawRect(10, 10, 50, 30, SSD1306_WHITE);
// Dessiner un cercle plein
display.fillCircle(100, 25, 15, SSD1306_WHITE);
// Dessiner une ligne
display.drawLine(0, 50, 127, 50, SSD1306_WHITE);
// Dessiner des pixels individuels
for (int i = 0; i < 20; i++) {
display.drawPixel(i * 6, 55 + random(-5, 5), SSD1306_WHITE);
}
display.display();
}
void loop() {}
Code 3 : Animation et mise à jour dynamique
#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define SCREEN_ADDRESS 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
int ballX = 64;
int ballY = 32;
int ballVX = 2;
int ballVY = 1;
const int ballRadius = 3;
void setup() {
Serial.begin(115200);
Wire.begin(8, 9);
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println("Erreur SSD1306");
while (true);
}
display.clearDisplay();
display.display();
}
void loop() {
// Effacer l'écran
display.clearDisplay();
// Mettre à jour la position de la balle
ballX += ballVX;
ballY += ballVY;
// Rebondir sur les bords
if (ballX <= ballRadius || ballX >= SCREEN_WIDTH - ballRadius) {
ballVX = -ballVX;
}
if (ballY <= ballRadius || ballY >= SCREEN_HEIGHT - ballRadius) {
ballVY = -ballVY;
}
// Dessiner la balle
display.fillCircle(ballX, ballY, ballRadius, SSD1306_WHITE);
// Afficher le compteur de frames
display.setCursor(0, 0);
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.print("FPS: ");
display.print(1000 / 50); // Approximation
// Envoyer vers l'écran
display.display();
delay(50); // ~20 FPS
}
Le buffer d’affichage consomme 1024 octets de RAM pour un écran 128×64. Sur ESP32, ce n’est pas un problème. Évitez cependant les display.display() trop fréquents : chaque appel transfère 1 Ko via I2C (~8ms à 400 kHz). Visez 10-30 FPS maximum pour les animations.
Dépannage
L’écran ne s’allume pas
Vérifier l'alimentation
Vérifiez que l’écran est alimenté :
- Mesurez 3.3V sur la broche VCC du module
- Vérifiez la continuité entre GND du module et GND de l’ESP32
- Testez avec une alimentation externe 3.3V pour isoler le problème
Scanner l'adresse I2C
Utilisez le code suivant pour scanner le bus I2C et détecter l’adresse du SSD1306 :
#include <Wire.h>
void setup() {
Serial.begin(115200);
Wire.begin(8, 9);
Serial.println("Scan I2C...");
for (byte address = 1; address < 127; address++) {
Wire.beginTransmission(address);
if (Wire.endTransmission() == 0) {
Serial.print("Périphérique trouvé à 0x");
Serial.println(address, HEX);
}
}
Serial.println("Scan terminé.");
}
void loop() {}
L’adresse devrait être 0x3C ou 0x3D. Si rien n’apparaît, vérifiez le câblage.
L’affichage est corrompu ou scintille
Problème de découplage ou de pull-up
Vérifiez :
- La présence des condensateurs de découplage (100nF + 10µF)
- Que les résistances de pull-up ne sont pas trop faibles (< 2.2kΩ total)
- Que les pistes I2C ne sont pas trop longues (< 10 cm recommandé)
- Que la fréquence I2C n’est pas trop élevée : réduisez à 100 kHz avec
Wire.setClock(100000);
L’écran affiche du texte tronqué
Débordement de buffer
Le texte qui dépasse les limites de l’écran est simplement ignoré. Vérifiez :
- Que
setCursor(x, y) pointe bien dans la zone d’affichage (x < 128, y < 64)
- Que la taille du texte (
setTextSize()) ne fait pas déborder les caractères
- Utilisez
display.getTextBounds() pour calculer la largeur d’un texte avant de l’afficher
L’ESP32 redémarre lors de l’initialisation
Problème de RAM ou d'alimentation
Sur ESP32, le buffer SSD1306 consomme 1 Ko de RAM. Si vous avez beaucoup de variables globales, cela peut saturer la pile.
Si le problème persiste :
- Vérifiez que l’alimentation 3.3V de l’ESP32 peut fournir > 500mA
- Ajoutez un condensateur 100µF sur la ligne 3.3V de l’ESP32
- Réduisez la fréquence I2C :
Wire.setClock(100000);
Optimisations
Réduire la consommation mémoire
Affichage partiel
Pour économiser de la RAM, affichez seulement une partie de l’écran :
// Créer un buffer pour seulement 128×32 pixels (512 octets)
Adafruit_SSD1306 display(128, 32, &Wire, OLED_RESET);
Ou utilisez la bibliothèque U8g2 qui propose des modes de tampon réduits (1/8, 1/4 de page).
Augmenter la vitesse I2C
Par défaut, l’I2C fonctionne à 100 kHz. Augmentez à 400 kHz pour des transferts plus rapides :
Wire.setClock(400000); // 400 kHz (Fast Mode)
Attention : des pistes longues ou des pull-up trop faibles peuvent causer des erreurs à 400 kHz.
Ressources
Conclusion
Vous savez maintenant comment intégrer un écran OLED SSD1306 sur un PCB avec l’ESP32 et afficher du texte, des graphiques et des animations.
Points clés à retenir
- Bus I2C : SDA=8, SCL=9 (broches utilisées sur la carte ESP32 S3 UNO)
- Pull-up 4.7kΩ sur SDA et SCL (souvent intégrées au module)
- Découplage : 100nF + 10µF au plus près de VCC
- Buffer RAM : 1024 octets pour 128×64 pixels
- Appeler
display.display() après chaque mise à jour pour rafraîchir l’écran
Pour approfondir
- Utilisez U8g2 pour une gestion mémoire optimisée et plus de polices
- Affichez des bitmaps (logos, icônes) avec
display.drawBitmap()
- Créez des interfaces utilisateur avec menus et boutons
- Associez l’écran avec des capteurs pour afficher température, humidité, etc.
Votre ESP32 peut maintenant afficher du contenu graphique sur un écran OLED. C’est une brique essentielle pour créer des interfaces utilisateur et des projets IoT interactifs.
Pour aller plus loin
Autres tutoriels ESP32
Explorez d'autres tutoriels pour maîtriser l'ESP32 et ses périphériques