Écran OLED SSD1306 avec ESP32

Intégration I2C et affichage graphique sur PCB

Écran OLED SSD1306 avec ESP32

Ce tutoriel explique comment intégrer un écran OLED SSD1306 sur un PCB avec un ESP32, utiliser le bus I2C et afficher du texte et des graphiques.

Durée :
Difficulté :
Documentation réalisée à 100% 100
Créée par :  Alban Petit

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
  Compatibilité des niveaux logiques

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
  Broches I2C personnalisables

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).

  Adresse I2C

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

Configuration du projet PlatformIO

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
  Dépendance GFX Library

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() {}
  Fonction display.display()

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.

Code 2 : Affichage graphique (formes et pixels)

#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
}
  Performances et RAM

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).

Améliorer les performances

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.
  Bien joué !

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