OLED vs LCD
OLED est l'acronyme de organic light-emitting diode (diode électroluminescente organique, donc). Contrairement aux cristaux liquides des écrans LCD, les OLED produisent de la lumière et les écrans basés sur cette technologie ne nécessitent donc pas de rétroéclairage, même lorsque vous les utilisez dans l'obscurité. Les images sont plus contrastées et le temps de réponse est plus court. Par contre, on dit que la durée de vie des écrans OLED est plus courte que celle des LCDs (l'image se dégrade avec le temps).
L'écran
Mon écran a une taille de 1,3 pouce (environ 3,5 cm X 1,8 cm) et une résolution de 128 X 64 pixels. Il est basé sur le contrôleur SH1106 et utilise le protocole I2C. L'image produite est blanche sur fond noir.
Branchements
Puisqu'il s'agit d'I2C, les connexions sont assez prévisibles: sans surprise, l'écran comporte deux broches pour l'alimentation (GND et VCC), et deux broches pour le transfert d'information (SCL et SDA).
Le vendeur a spécifié dans sa description que l'écran peut aussi bien supporter une tension de 5 V qu'une tension de 3,3 V, mais j'ignore à quel point c'est vrai. Le SH1106 n'est pas conçu pour des tension de 5 V, mais le fabricant de l'écran a peut-être ajouté un régulateur de tension quelque part. Pour plus de prudence, j'ai utilisé un module abaisseur de tension bidirectionnel spécifiquement conçu pour la communication I2C dans le genre de celui qui est vendu par Adafruit. Il s'agit peut-être d'une précaution inutile, mais ça ne peut pas faire de mal.
Tout ça pour dire que que les 4 broches de l'écran OLED sont branchés à l'Arduino Uno de la façon suivante:
- Broche GND de l'écran à une broche GND de l'Arduino
- Broche VCC de l'écran à la sortie 3.3 V de l'Arduino
- Broche SCL de l'écran à la broche A5 de l'Arduino (adaptateur de niveau logique entre les deux)
- Broche SDA de l'écran à la broche A4 de l'Arduino (adaptateur de niveau logique entre les deux)
Recherche et installation d'une bibliothèque
La bibliothèque U8glib a très bonne réputation parmi les utilisateurs d'écrans OLED. Cependant, j'ai préféré continuer d'utiliser la bibliothèque GFX d'Adafruit, pour que mes programmes déjà réalisés avec un écran LCD de style Nokia soient plus facilement transposables à mon nouvel écran. Petit inconvénient: puisqu'Adafruit ne fabrique aucun produit contenant le contrôleur SH1106 (car ils préfèrent le SSD1306), ils n'ont pas créé de bibliothèque spécifique au SH1106.
J'ai donc installé la bibliothèque Adafruit_SH1106 de wonho-maker qui, contrairement à ce que son nom pourrait faire croire, n'est pas une bibliothèque officielle réalisée par Adafruit. Il s'agit plutôt d'une modification, réalisée par un particulier, de la bibliothèque qu'Adafruit a produite pour le SSD1306. Cette bibliothèque nécessite à son tour la présence de la bibliothèque GFX d'Adafruit (version officielle, cette fois), ce qui représente à mes yeux un énorme avantage: toutes les routines que j'ai utilisées sur mon écran LCD Nokia continueront de fonctionner sur mon écran OLED.
Sketch 1: dessiner des formes, afficher une image bitmap
Commençons par un sketch dont la seule fonction vise à illustrer diverses possibilités de la bibliothèque GFX d'Adafruit: écrire du texte à l'écran...
...dessiner des contours de formes géométriques vides...
...dessiner des formes géométriques pleines...
... contrôler des pixels individuels...
...afficher une image bitmap.
Pour plus de détails, je vous invite à lire l'article qui concerne la première version de ce sketch (réalisé pour un écran LCD Nokia), puisque la majeure partie du programme concerne la bibliothèque GFX plutôt que la bibliothèque SH1106.
L'essentiel de mes modifications (par rapport à ma version initiale conçue pour un écran LCD) a consisté à tenir compte de la nouvelle résolution de l'écran (128 X 64 plutôt que 84 X 48) et de l'inversion des couleurs: par défaut, sur un écran OLED, on dessine en blanc sur fond noir alors que c'était l'inverse sur le LCD. Il est bien sûr possible de mettre le fond de l'écran blanc pour y dessiner en noir, mais les résultats sont beaucoup moins réussis (présence de traînées grisâtres sur le fond blanc).
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/********************************************************************* | |
Démonstration d'un écran OLED i2c SH1106 avec un Arduino, | |
en utilisant une bibliothèque d'Adafruit: | |
https://github.com/adafruit/Adafruit-GFX-Library | |
...et une bibliothèque compatible par Wohho-maker: | |
https://github.com/wonho-maker/Adafruit_SH1106 | |
Plus d'infos: | |
https://electroniqueamateur.blogspot.com/2019/01/ecran-oled-sh1106-i2c-et-arduino.html | |
*********************************************************************/ | |
#include <Wire.h> | |
#include <Adafruit_GFX.h> | |
#include <Adafruit_SH1106.h> | |
/* Puisque la bibliothèque est basée sur celle du SSD1306, le constructeur | |
prévoit qu'on passe le numéro de la broche RESET en argument. Mais le SH1106 | |
n'a pas de broche RESET, alors on écrit n'importe quel entier (de préférence, | |
le numéro d'une broche de l'Arduino que vous n'utilisez pas). */ | |
Adafruit_SH1106 display(23); | |
/* Définition d'une image bitmap: logo du blog Électronique en Amateur | |
Réalisée avec l'outil en ligne http://javl.github.io/image2cpp/ */ | |
const unsigned char myBitmap [] PROGMEM = { | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9f, 0xf0, 0x00, 0x00, 0x03, 0x9f, 0xff, 0xe0, 0x00, 0x07, | |
0x9f, 0xff, 0xfe, 0x00, 0x0f, 0xdf, 0x0f, 0xbe, 0x00, 0x1f, 0xde, 0x06, 0x0e, 0x00, 0x3f, 0xdc, | |
0x04, 0x0e, 0x00, 0x3f, 0xdc, 0x00, 0x06, 0x00, 0x3f, 0xdc, 0x00, 0x06, 0x00, 0x3f, 0xdc, 0x60, | |
0x06, 0x00, 0x3f, 0xdc, 0xf1, 0x8e, 0x00, 0x3f, 0xdc, 0xf3, 0xce, 0x00, 0x3f, 0xde, 0x63, 0xde, | |
0x00, 0x3f, 0xdf, 0x09, 0x9e, 0x00, 0x3f, 0xdf, 0xfc, 0x7e, 0x00, 0x3f, 0xde, 0x7f, 0xfe, 0x00, | |
0x3f, 0xde, 0x7f, 0xf6, 0x00, 0x3f, 0xdf, 0x3f, 0xe6, 0x00, 0x3f, 0xdf, 0x8f, 0x8e, 0x00, 0x3f, | |
0xdf, 0xe0, 0x3e, 0x00, 0x3f, 0x00, 0x3f, 0xfe, 0x00, 0x3e, 0xff, 0xc0, 0x1e, 0x00, 0x3d, 0xff, | |
0xff, 0xc0, 0x00, 0x3b, 0xff, 0xff, 0xfe, 0x00, 0x37, 0xff, 0xff, 0xfe, 0x00, 0x2f, 0xff, 0xff, | |
0xfe, 0x00, 0x0e, 0x1f, 0xff, 0xfe, 0x00, 0x1f, 0x9f, 0x9f, 0xfe, 0x00, 0x3f, 0x9f, 0x9f, 0x9e, | |
0x00, 0x3f, 0x9f, 0x9f, 0x9e, 0x00, 0x3f, 0x9f, 0x9f, 0x9e, 0x00, 0x3f, 0x9f, 0x9f, 0x9c, 0x00, | |
0x1f, 0x9f, 0x9f, 0x98, 0x00, 0x0f, 0x9f, 0x9f, 0x90, 0x00, 0x07, 0x9f, 0x9f, 0x90, 0x00, 0x00, | |
0x9f, 0x9f, 0x90, 0x00, 0x00, 0x9f, 0x91, 0x90, 0x00, 0x00, 0x91, 0x91, 0x90, 0x00, 0x00, 0x91, | |
0x91, 0x90, 0x00, 0x00, 0x91, 0x91, 0x90, 0x00, 0x00, 0x91, 0x91, 0x90, 0x00, 0x00, 0x91, 0x91, | |
0x90, 0x00, 0x00, 0x91, 0x91, 0x90, 0x00, 0x00, 0x91, 0x91, 0x90, 0x00, 0x00, 0x91, 0x91, 0x90, | |
0x00, 0x00, 0xf1, 0xd1, 0x90, 0x00, 0x00, 0x01, 0xc1, 0xf0, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x00 | |
}; | |
void setup() { | |
display.begin(); // initialisation de l'afficheur | |
display.clearDisplay(); // ça efface à la fois le buffer et l'écran | |
} | |
void loop() { | |
/**************** Éxcriture de texte ***************/ | |
// taille par défaut | |
display.setCursor(30, 15); // coordonnées du point de départ du texte | |
display.setTextColor(WHITE); | |
display.setTextSize(1); // taille par défaut | |
display.println("Petit..."); | |
display.display(); // affichage à l'écran | |
delay(1000); | |
// deux fois plus gros | |
display.setCursor(30, 35); // coordonnées du point de départ du texte | |
display.setTextSize(2); // taille double | |
display.println("GROS!"); | |
display.display(); // affichage à l'écran | |
delay(1000); | |
display.clearDisplay(); // on efface tout | |
/******************** Dessiner une ligne ******************************************/ | |
// ligne horizontale au centre de l'écran | |
display.drawLine(0, display.height() / 2, display.width() , display.height() / 2, WHITE); | |
// ligne verticale au centre de l'écran | |
display.drawLine(display.width() / 2, 0, display.width() / 2, display.height(), WHITE); | |
/********************* Dessiner des contours de formes géométriques ******************/ | |
display.drawRect( 15, 5, 30, 15, WHITE); // contour d'un rectangle | |
display.drawCircle(95, 15, 8, WHITE); // contour d'un cercle | |
display.drawRoundRect(15, 40, 30, 16, 5, WHITE); // contour d'un rectangle à coins arrondis | |
display.drawTriangle(95, 40, 80, 55, 110, 55, WHITE); // contour d'un triangle | |
display.display(); | |
delay(1000); | |
/******************* Dessiner des formes géométriqeus pleines *************************/ | |
display.fillRect( 15, 5, 30, 15, WHITE); // contour d'un rectangle | |
display.fillCircle(95, 15, 8, WHITE); // contour d'un cercle | |
display.fillRoundRect(15, 40, 30, 16, 5, WHITE); // contour d'un rectangle à coins arrondis | |
display.fillTriangle(95, 40, 80, 55, 110, 55, WHITE); // contour d'un triangle | |
display.display(); | |
delay(1000); | |
display.clearDisplay(); | |
/********************** Dessiner un pixel à la fois ***********************************************/ | |
// on trace une fonction sinusoidale, point par point | |
for (int x = 1; x < 128; x++) { | |
int y = 32 + round(16.0 * sin(x / 5.0)); | |
display.drawPixel(x, y, WHITE); | |
} | |
display.display(); | |
delay(1000); | |
display.clearDisplay(); | |
/**************************** Dessiner une image bitmap **********************************/ | |
// on affiche l'image stockée dans la constante myBitmap définie au début de ce fichier. | |
display.fillRect(5,5,44,58,WHITE); // fond blanc derrière l'image bitmap | |
display.drawBitmap(10, 10, myBitmap, 34, 48, BLACK); | |
display.setCursor(60, 30); // coordonnées du point de départ du texte | |
display.setTextColor(WHITE); | |
display.setTextSize(1); // taille par défaut | |
display.println("Au revoir!"); | |
display.display(); | |
delay(2000); | |
display.clearDisplay(); | |
} | |
Sketch 2: mesure analogique présentée sous forme de jauge rectangulaire
Ce sketch présente à l'écran la tension à l'entrée A0. La version initiale de ce sketch est décrite de façon plus détaillée ici. Pour en faire l'essai, vous pouvez brancher le curseur d'un potentiomètre à l'entrée A0.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/***************************************************** | |
Affichage d'une mesure analogique sur un écran OLED | |
SH1106 I2C (Arduino) | |
Utilisation d'une bibliothèque d'Adafruit: | |
https://github.com/adafruit/Adafruit-GFX-Library | |
...et d'une bibliothèque compatible par Wohho-maker: | |
https://github.com/wonho-maker/Adafruit_SH1106 | |
Plus d'infos: | |
https://electroniqueamateur.blogspot.com/2019/01/ecran-oled-sh1106-i2c-et-arduino.html | |
*****************************************************/ | |
#include <Wire.h> | |
#include <Adafruit_GFX.h> | |
#include <Adafruit_SH1106.h> | |
Adafruit_SH1106 display(23); | |
void setup() { | |
display.begin(); | |
display.clearDisplay(); | |
} | |
void loop() { | |
int valeur; | |
// mesure de la tension à l'entrée A0 | |
valeur = map(analogRead(A0), 0, 1023, 0, 500); | |
// on écrit "tension", en petits caractères, centré en haut de l'écran: | |
display.setCursor(40, 8); | |
display.setTextColor(WHITE); | |
display.setTextSize(1); | |
display.println("Tension:"); | |
// on écrit la valeur numérique de la tension un peu plus bas, en plus gros | |
display.setCursor(30, 22); | |
display.setTextSize(2); | |
display.print(valeur/100); // partie entière | |
display.print(","); // virgule | |
if ((valeur % 100) < 10){ | |
display.print("0"); | |
} | |
display.print(valeur % 100); // partie décimale, après la virgules | |
display.println(" V"); // unités de mesure | |
// enveloppe de la jauge rectangulaire | |
display.fillRect( 14, 45, 104, 10, BLACK); | |
display.drawRect( 14, 45, 104, 10, WHITE); | |
// partie mobile de la jauge rectangulaire | |
display.fillRect( 16, 47, map(valeur, 0, 500, 0, 100), 6, WHITE); | |
display.display(); | |
delay(500); | |
display.clearDisplay(); | |
} |
Sketch 3: mesure analogique présentée sous forme de graphique cartésien
Cet autre sketch présente également la tension à l'entrée A0, mais sous forme de graphique cartésien, cette fois. Voir cet article pour la première version de ce sketch.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/***************************************************** | |
Affichage d'une mesure analogique sous forme de graphique | |
cartésien sur un écran OLED I2C SH1106 (Arduino) | |
en utilisant une bibliothèque d'Adafruit: | |
https://github.com/adafruit/Adafruit-GFX-Library | |
...et une bibliothèque compatible par Wohho-maker: | |
https://github.com/wonho-maker/Adafruit_SH1106 | |
Plus d'infos: | |
https://electroniqueamateur.blogspot.com/2019/01/ecran-oled-sh1106-i2c-et-arduino.html | |
*****************************************************/ | |
#include <Wire.h> | |
#include <Adafruit_GFX.h> | |
#include <Adafruit_SH1106.h> | |
Adafruit_SH1106 display(23); | |
const int intervalle = 500; // nombre de millisecondes entre deux mesures consécutives | |
int mesures[32]; // les 32 dernieres mesures | |
int compteur = 0; // nombre de mesures prises jusqu'à présent | |
void setup() { | |
display.begin(); | |
display.clearDisplay(); | |
} | |
void loop() { | |
int nouvelleValeur; | |
// mesure de la tension à l'entrée A0 et conversion en pixels | |
nouvelleValeur = 60 - map(analogRead(A0), 0, 1023, 0, 60); // car l'écran a 64 pixels de haut | |
if (compteur > 31) { | |
// l'écran est déjà rempli: on fait glisser le graphique vers la gauche | |
for (int i = 0; i < 31; i++) { | |
mesures[i] = mesures[i + 1]; | |
} | |
mesures[31] = nouvelleValeur; | |
display.clearDisplay(); // on repart à neuf | |
// tracé de la courbe | |
for (int i = 1; i <= 31; i++) { | |
display.drawLine(4 * (i - 1), mesures[i - 1], 4 * i, mesures[i], WHITE); | |
} | |
for (int i = 0; i <= 5; i++) { | |
display.drawFastHLine(0, i * 12, 128, WHITE); | |
} | |
} | |
if ((compteur > 0) && (compteur <= 31)) { | |
// pas assez de mesures pour remplir tout l'écran: on ajoute notre nouvelle valeur | |
// au graphique déjà visible à l'écran | |
mesures[compteur] = nouvelleValeur; | |
// on trace une droite reliant la mesure précédente à notre nouvelle mesure | |
display.drawLine(4 * (compteur - 1), mesures[compteur - 1], 4 * compteur, mesures[compteur], WHITE); | |
compteur = compteur + 1; | |
} | |
else if (compteur == 0) { | |
// c'est la première mesure: on la met en variable, | |
// mais on ne trace rien à l'écran, sauf le quadrillage | |
mesures[0] = nouvelleValeur; | |
compteur = compteur + 1; | |
// 6 lignes horizontales: 0 V, 1 V, 2 V... | |
for (int i = 0; i <= 5; i++) { | |
display.drawFastHLine(0, i * 12, 128, WHITE); | |
} | |
} | |
display.display(); // affichage de notre travail à l'écran | |
delay(intervalle); // on attend un peu avant la prochaine mesure | |
} |
Sketch 4: menus de navigation contrôlés par 4 boutons
Finalement, j'ai également adapté le sketch que j'avais écrit pour établir un système de menus contrôlé par 4 boutons poussoir.
L'écran est toujours branché à l'Arduino de la même façon que pour les circuits précédents, mais on ajoute un bouton poussoir à chacune des broches 6, 7, 8 et 9:
À lire aussi
J'ai également utilisé cet écran OLED avec un Raspberry Pi, avec une carte STM32 (Nucleo ou Blue Pill), avec un ESP8266 et avec un ESP32.
Yves Pelletier (Twitter, Facebook)
Bonjour, j’ai galéré un bon moment avec le modèle SSD1306 qui grâce à votre tuto s’est avéré être un modèle SSH1106. Un grand merci pour cet excellent tuto qui me permet de bien contrôler mon afficheur. Dan
RépondreSupprimerEnfin un tuto en Français pour le SSH1106 ! Et très complet en prime ! Merci !
RépondreSupprimeroui bon tuto pour démarrer avec un SH1106 1.3", les 2 premiers sketchs OK du premier coup !
RépondreSupprimermaintenant je passe à mon appli (mesure ultrason)
Est ce Oled n'est pas influencé par les charges inductive comme les afficheur LCD et tft
RépondreSupprimerMerci Yves pour ce très bon tuto
RépondreSupprimerça fonctionne bien du 1er coup !
Oh oui merci pour ce tuto Yves. Avec loupe 8x, au dos de l'écran, il y a bien un régulateur 3.3 V, un 662K en cms. Donc je pense que c'est ok sur mon modèle 3.3 ou 5 V comme précisé.
RépondreSupprimerBonjour, super tuto! L'adresse I2C est elle programmable sur cet ecran?
RépondreSupprimerTrès cool ces tutos biens utiles pour démarrer, bravo
RépondreSupprimer