Page 1 sur 2

Les petits écrans OLED

Posté : 08 août 2026, 10:52
par likiki
Les petits écrans OLED.


Voilà une petite vidéo qui présente un petit écran OLED de 1,3". Les deux programmes sont juste après.

.

.

Le câblage est très simple :

La borne VCC de l'écran vas sur 3v3 ou 5V selon le modèle
La borne GND de l'écran vas sur GND
La borne SCL de l'écran vas sur D1 (GPIO5)
La borne SDA de l'écran vas sur D2 (GPIO4)
C'est le câblage I2C standard sur Wemos D1 Mini (D1 = SCL, D2 = SDA par défaut)

Bibliothèque à utiliser

Comme votre écran utilise le chip SH1106 (et non SSD1306, attention à ne pas confondre), il faut une bibliothèque compatible. La plus simple est U8g2 (par olikraus), qui gère très bien le SH1106.

Dans l'IDE Arduino : Outils > Gérer les bibliothèques → rechercher "U8g2" → installer.


Programme 1

Code : Tout sélectionner

#include <Arduino.h>
#include <U8g2lib.h>
#include <Wire.h>

// Configuration pour écran SH1106 128x64 I2C (Adresse 0x3C par défaut)
U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);

void setup() {
  u8g2.begin();
}

void loop() {
  u8g2.clearBuffer();
  u8g2.sendBuffer();                 // Efface l'écran
  delay(2000);
  u8g2.setFont(u8g2_font_6x10_tf);     // Choix d'une police adaptée
  u8g2.drawStr(10, 25, "Bonjour,");    // Ligne 1 (X=10, Y=25)
  u8g2.sendBuffer();
  delay (2000);
  u8g2.drawStr(10, 45, "bienvenue a vous."); // Ligne 2 (X=10, Y=45)
  
  u8g2.sendBuffer();                   // Envoie le rendu à l'écran
  
  delay(2000);

}

sketch_simple_copy_20260808103447.ino

Programme 2

Code : Tout sélectionner


#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <U8g2lib.h>
#include <Wire.h>

// --- Point d'accès Wi-Fi ---
const char* ap_ssid = "WemosOLED";
const char* ap_password = "12345678"; // minimum 8 caractères

// --- Écran OLED ---
U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);

// --- Serveur web ---
ESP8266WebServer server(80);

String texteActuel = "En attente...";

// --- Textes préformatés (à personnaliser) ---
const char* textesPredefinis[10] = {
  "Hello les terriens !",
  "Texte numero 2",
  "Texte numero 3",
  "Texte numero 4",
  "Ne pas me deranger, je fait ma sieste.",
  "Texte numero 6",
  "Au telephone avec ma femme, cela vas durer longtemps. Allez vous balader.",
  "Texte numero 8",
  "Vive Passionnement",
  "Texte numero 10"
};

// --- Gestion du défilement (marquee) ---
const int ecranLargeur = 128;
const int ecranHauteur = 64;
int scrollX = ecranLargeur;
int texteWidthPx = 0;
unsigned long dernierScroll = 0;
const unsigned long delaiScroll = 50; // ms entre chaque décalage (plus petit = plus rapide)
const int vitesseScroll = 2;          // pixels par pas
bool defilementActif = true;

// --- Gestion de l'inactivité / économiseur d'écran ---
unsigned long derniereActivite = 0;
const unsigned long delaiInactivite = 30000; // 30 secondes
bool modeEconomiseur = false;

// --- Point rebondissant ---
float pointX = 10, pointY = 10;
float pointDX = 1.2, pointDY = 0.9;
const int pointRayon = 2;
unsigned long dernierPoint = 0;
const unsigned long delaiPoint = 20; // ms entre chaque déplacement

void signalerActivite() {
  derniereActivite = millis();
  if (modeEconomiseur) {
    modeEconomiseur = false;
    // Réaffiche l'état courant en sortant de l'économiseur
    if (defilementActif) {
      scrollX = ecranLargeur;
    } else {
      afficherFixeDeclare(); // déclaration anticipée ci-dessous
    }
  }
}

// Découpe le texte en lignes qui tiennent dans la largeur de l'écran (respecte les mots)
int decouperEnLignes(String texte, String lignes[], int maxLignes) {
  int nbLignes = 0;
  String ligne = "";
  int start = 0;

  while (start < (int)texte.length() && nbLignes < maxLignes) {
    int espace = texte.indexOf(' ', start);
    if (espace == -1) espace = texte.length();
    String mot = texte.substring(start, espace);

    String essai = ligne.length() > 0 ? ligne + " " + mot : mot;
    if (u8g2.getUTF8Width(essai.c_str()) > ecranLargeur && ligne.length() > 0) {
      lignes[nbLignes++] = ligne;
      ligne = mot;
    } else {
      ligne = essai;
    }
    start = espace + 1;
  }
  if (ligne.length() > 0 && nbLignes < maxLignes) {
    lignes[nbLignes++] = ligne;
  }
  return nbLignes;
}

void afficherFixe(String texte) {
  u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_ncenB08_tr);

  const int maxLignes = 4;
  const int hauteurLigne = 15;
  String lignes[maxLignes];
  int nbLignes = decouperEnLignes(texte, lignes, maxLignes);

  int hauteurTotale = nbLignes * hauteurLigne;
  int yDepart = (ecranHauteur - hauteurTotale) / 2 + 11;
  if (yDepart < 11) yDepart = 11;

  for (int i = 0; i < nbLignes; i++) {
    int largeur = u8g2.getUTF8Width(lignes[i].c_str());
    int x = (ecranLargeur - largeur) / 2;
    if (x < 0) x = 0;
    u8g2.setCursor(x, yDepart + i * hauteurLigne);
    u8g2.print(lignes[i]);
  }

  u8g2.sendBuffer();
}

void afficherFixeDeclare() {
  afficherFixe(texteActuel);
}

void preparerTexte(String texte) {
  texteActuel = texte;
  u8g2.setFont(u8g2_font_ncenB08_tr);
  texteWidthPx = u8g2.getUTF8Width(texte.c_str());
  scrollX = ecranLargeur; // repart depuis la droite

  if (!modeEconomiseur) {
    if (!defilementActif) {
      afficherFixe(texteActuel);
    }
  }
}

void mettreAJourScroll() {
  if (!defilementActif) return;
  if (texteActuel.length() == 0) return;

  unsigned long maintenant = millis();
  if (maintenant - dernierScroll < delaiScroll) return;
  dernierScroll = maintenant;

  scrollX -= vitesseScroll;
  if (scrollX < -texteWidthPx) {
    scrollX = ecranLargeur; // recommence à droite
  }

  u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_ncenB08_tr);
  int yCentre = (ecranHauteur / 2) + 4;
  u8g2.setCursor(scrollX, yCentre);
  u8g2.print(texteActuel);
  u8g2.sendBuffer();
}

void mettreAJourEconomiseur() {
  unsigned long maintenant = millis();
  if (maintenant - dernierPoint < delaiPoint) return;
  dernierPoint = maintenant;

  pointX += pointDX;
  pointY += pointDY;

  if (pointX <= pointRayon || pointX >= ecranLargeur - pointRayon) {
    pointDX = -pointDX;
    pointX = constrain(pointX, pointRayon, ecranLargeur - pointRayon);
  }
  if (pointY <= pointRayon || pointY >= ecranHauteur - pointRayon) {
    pointDY = -pointDY;
    pointY = constrain(pointY, pointRayon, ecranHauteur - pointRayon);
  }

  u8g2.clearBuffer();
  u8g2.drawDisc((int)pointX, (int)pointY, pointRayon);
  u8g2.sendBuffer();
}

void gererAffichage() {
  unsigned long maintenant = millis();

  if (!modeEconomiseur && (maintenant - derniereActivite > delaiInactivite)) {
    modeEconomiseur = true;
    u8g2.clearBuffer();
    u8g2.sendBuffer();
  }

  if (modeEconomiseur) {
    mettreAJourEconomiseur();
  } else {
    mettreAJourScroll();
  }
}

void handleRoot() {
  signalerActivite();

  String html = "<!DOCTYPE html><html><head>";
  html += "<meta charset='UTF-8'>";
  html += "<meta name='viewport' content='width=device-width, initial-scale=1'>";
  html += "<title>Afficheur OLED</title>";
  html += "<style>body{font-family:sans-serif;text-align:center;margin-top:30px;}";
  html += "input{padding:10px;font-size:16px;width:80%;max-width:300px;}";
  html += "button{padding:10px 20px;font-size:16px;margin-top:10px;margin-left:5px;}";
  html += ".effacer{background-color:#e74c3c;color:white;border:none;border-radius:5px;}";
  html += ".toggle{background-color:" + String(defilementActif ? "#27ae60" : "#95a5a6") + ";color:white;border:none;border-radius:5px;}";
  html += ".predefini{display:block;width:80%;max-width:300px;margin:6px auto;padding:10px;";
  html += "font-size:15px;background-color:#3498db;color:white;border:none;border-radius:5px;}";
  html += ".predefini:active{background-color:#2980b9;}";
  html += "hr{width:80%;max-width:300px;margin:25px auto;}</style>";
  html += "</head><body>";
  html += "<h2>Afficher un texte sur l'ecran OLED</h2>";

  html += "<form action='/set' method='GET'>";
  html += "<input type='text' name='texte' placeholder='Votre texte ici' maxlength='60'>";
  html += "<br><button type='submit'>Envoyer</button>";
  html += "</form>";

  html += "<form action='/toggleScroll' method='GET'>";
  html += "<button type='submit' class='toggle'>Defilement : " + String(defilementActif ? "ON" : "OFF") + "</button>";
  html += "</form>";

  html += "<form action='/clear' method='GET'>";
  html += "<button type='submit' class='effacer'>Effacer l'ecran</button>";
  html += "</form>";

  html += "<hr><h3>Textes predefinis</h3>";
  for (int i = 0; i < 10; i++) {
    html += "<form action='/set' method='GET'>";
    html += "<input type='hidden' name='texte' value='" + String(textesPredefinis[i]) + "'>";
    html += "<button type='submit' class='predefini'>" + String(textesPredefinis[i]) + "</button>";
    html += "</form>";
  }

  html += "<p>Texte actuel : " + texteActuel + "</p>";
  html += "</body></html>";

  server.send(200, "text/html", html);
}

void handleSet() {
  signalerActivite();
  if (server.hasArg("texte")) {
    preparerTexte(server.arg("texte"));
  }
  server.sendHeader("Location", "/");
  server.send(303);
}

void handleToggleScroll() {
  signalerActivite();
  defilementActif = !defilementActif;
  if (!defilementActif) {
    afficherFixe(texteActuel);
  }
  server.sendHeader("Location", "/");
  server.send(303);
}

void handleClear() {
  signalerActivite();
  texteActuel = "";
  u8g2.clearBuffer();
  u8g2.sendBuffer();
  server.sendHeader("Location", "/");
  server.send(303);
}

void setup() {
  Serial.begin(115200);
  u8g2.begin();
  derniereActivite = millis();
  preparerTexte("Demarrage...");

  WiFi.mode(WIFI_AP);
  WiFi.softAP(ap_ssid, ap_password);

  IPAddress ip = WiFi.softAPIP();
  Serial.print("Point d'acces demarre, IP : ");
  Serial.println(ip);

  server.on("/", handleRoot);
  server.on("/set", handleSet);
  server.on("/toggleScroll", handleToggleScroll);
  server.on("/clear", handleClear);
  server.begin();

  preparerTexte("WiFi: " + String(ap_ssid) + " IP: " + ip.toString());
  derniereActivite = millis();
}

void loop() {
  server.handleClient();
  gererAffichage();
}

Test_Oled.ino


:a_suivre:

Re: Les petits écrans OLED

Posté : 08 août 2026, 11:27
par bunny94
:PetitPoucejpg: :bienv: :bienv:

Re: Les petits écrans OLED

Posté : 08 août 2026, 11:35
par C2Vues-BricoTrain
:bienv: Christian :bienv:


:PetitPoucejpg: :PetitPoucejpg: :PetitPoucejpg: :PetitPoucejpg:

Re: Les petits écrans OLED

Posté : 08 août 2026, 12:15
par ramasoft
Cette présentation est très bien :bienv: :bienv:
Comme tu dis il n'y a que l'imagination qui limite les utilisations.
C'est un écran monochrome ?
J'avais pensé faire un panneau publicitaire pour mettre sur la "place de la gare" avec des publicités d'époque (70-80) qui changent toutes les minutes avec un écran RGB comme celui du panneau de la gare, mais un peu plus grand.
On peut même diffuser de la vidéo avec les ESP32 qui ont un débit suffisant.
Le seul truc qui limite un peu c'est la taille du circuit imprimé de l'écran qui oblige à faire un panneau un peu mastoc.
Quelqu'un sur YT m'a dit qu'on pouvait recouper le circuit imprimé presque au ras de l'écran ... :gratte: j'ai un peu peur quand même mais dès que j'aurai l'occasion j'en achèterai plusieurs. C'est dommage que je n'en ai pas fait un stock car celui que j'ai n'avait couté qu'1,89€ ... mais c'était avant :siffle:

Attention: les TFT couleur sont en 3,3V et leurs broches attendent des signaux logiques de 3,3V et ça claque à 3,6V ...
Avec un ESP32, pas de problème mais avec un microcontrôleur en 5V il faut ajouter des résistances d'adaptation sur toutes les lignes ce qui peut poser des problèmes de stabilité.

Re: Les petits écrans OLED

Posté : 08 août 2026, 13:04
par likiki
:merciiii: Patrick, venant de toi c'est gratiffiant.
ramasoft a écrit : 08 août 2026, 12:15 C'est un écran monochrome ?
Oui, avec lettrage en blanc.
ramasoft a écrit : 08 août 2026, 12:15 J'avais pensé faire un panneau publicitaire pour mettre sur la "place de la gare"
Tu a devancé mon idée, je pensais faire cela en monochrome, juste du texte un poil humoristique. Du style "Calcon, la lessive poids lourds pour laver votre camion" (Merci Coluche).
ramasoft a écrit : 08 août 2026, 12:15 Attention: les TFT couleur sont en 3,3V et leurs broches attendent des signaux logiques de 3,3V et ça claque à 3,6V ...
Avec un ESP32, pas de problème mais avec un microcontrôleur en 5V il faut ajouter des résistances d'adaptation sur toutes les lignes ce qui peut poser des problèmes de stabilité.
L'avantage du Wemos est qu'il bosse en 3,3V.

:tire langue

Re: Les petits écrans OLED

Posté : 08 août 2026, 13:33
par ramasoft
Faudra que je les essaye, depuis le temps que je t'entends parler des Wemos.
Mais avec ces putains de taxes, ça bloque un peu les achats.


PS: et je viens de tomber sur une promo sur Amazon à 3,64€ les 3 : https://www.amazon.fr/AZDelivery-D1-Min ... B0754N794H

Voilà, c'est dans la boite, j'avais justement une commande à faire :cooooool:

Re: Les petits écrans OLED

Posté : 08 août 2026, 13:51
par likiki
Tu as tout compris, il faut sauter a pieds joints sur toutes les bonnes affaires.

Je prépare les articles qui m'intéresse et dès que les promos arrivent ou que les prix baisse, op commande validé.

Re: Les petits écrans OLED

Posté : 08 août 2026, 13:59
par ramasoft
Je fais aussi ça pour Amazon car je ne veux pas payer pour Prime si bien que je note ce dont j'ai besoin et j'attends d'avoir les 35€ de commande pour ne pas payer le port. Sauf urgence
Et chez les chinois, je boycotte en espérant qu'ils comprennent enfin que la taxe de 3€ est par groupe d'article et non par article...
S'ils appliquaient la taxe normalement, ça ne se sentirait pas beaucoup, mais là t’achètes un lot de 50 BC547 à 1,40€ et un autre lot de BC557 à 1,40€ et ça fait 7,20€ de taxes en plus ... :3: :3:

Pour voir la taxe appliquée, il faut aller jusqu'au paiement sinon juste avec le panier elle n'apparaît pas et on se dit que c'est comme avant...

Re: Les petits écrans OLED

Posté : 08 août 2026, 14:03
par likiki
Perso j'abandonne ali et baba le temps que tout cela se calme.

Par contre sur Amazon, il y a de plus en plus d'article qui disent que pour le vendeur ont est hors zone de livraison.

Est-ce un moyen pour Amazon de les obliger a leur vendre le matériel en grosse quantité pour redistribution avec marge ensuite ? ? ?

Re: Les petits écrans OLED

Posté : 08 août 2026, 14:12
par ramasoft
Sur Amazon ,il faut se méfier d'où ça vient: il n'y a pas longtemps, j'avais commandé des produits WD40 pour l'entretien du vélo et lorsque j'avais payé je vois la livraison à 3 semaines .... ça venait de Chine !!! pour du WD40 ...
J'ai contacté le service client pour râler et je suis tombé sur une femme très gentille qui a recherché s'ils en avaient en stock ailleurs. Résultat je les ai reçus 48h après.
Mais maintenant je vérifie que c'est bien indiqué "expédié par Amazon"
Il faut aussi se méfier des produits d'occasion qui sont fréquents et qui ne sont pas toujours clairement signalés comme tels.

Re: Les petits écrans OLED

Posté : 08 août 2026, 14:34
par likiki
En règle générale il faut se méfier de tout.

Tout est fait pour nous piéger.

Tu choisi un produit et il est noté livraison sous 48H et quand tu as payé c'est 72H ou plus. :3: :3: :3:

Re: Les petits écrans OLED

Posté : 08 août 2026, 16:03
par likiki
Avec un habillage façon Patrick, ça le fait bien. :yess:

.
IMG_0614.jpeg
.


:a_suivre:

Re: Les petits écrans OLED

Posté : 08 août 2026, 16:33
par ramasoft
Tu dois aussi pouvoir faire défiler le texte horizontalement ou verticalement.
Je ne connais pas cette bibliothèque mais dans celle d'Adafruit que j'avais utilisée pour l'écran du tachymètre il y avait cette possibilité.
Essaye avec l'autocomplétion pour voir s'il y a ce genre d'instruction: display.startscrollright(start, stop);
Sinon demande à une IA.

Re: Les petits écrans OLED

Posté : 08 août 2026, 17:11
par likiki
Je vais regarder cela Patrick.

Voilà l'écran monté sur un support complet. Ce montage servira pour les test hors réseau.

.
IMG_0615.jpeg
.


:a_suivre:

Re: Les petits écrans OLED

Posté : 08 août 2026, 17:21
par C2Vues-BricoTrain
:bienv: :bienv: :bienv: :bienv:

C'est bien tout ça !

:PetitPoucejpg: :PetitPoucejpg: :PetitPoucejpg: :PetitPoucejpg:

Re: Les petits écrans OLED

Posté : 08 août 2026, 17:40
par Lherve54
:bienv: :bienv:

Re: Les petits écrans OLED

Posté : 08 août 2026, 19:01
par bunny94
Bon je m'introduis dans ce fil juste pour rappeler que je vois et filme souvent en expo le réseau Dejeanville en N où des petits écrans sont utilisés pour des indications sur les quais et un plus grand pour la devanture du cinéma où l'auteur du réseau fait défiler des images de films Titanic, Top etc. et le créateur du réseau pousse le détail jusqu' à faire figurer le nom de la ville où se déroule l'expo comme dans la vidéo ci-dessous à partir de 3' 07".
>
>

Re: Les petits écrans OLED

Posté : 08 août 2026, 19:16
par likiki
Cela fait une animation supplémentaire. :bienv: :bienv: :bienv:

Re: Les petits écrans OLED

Posté : 08 août 2026, 19:26
par ramasoft
Olivier a aussi dans l'idée de faire un cinéma en plein air avec un écran un peu plus grand.
La devanture du cinéma est une bonne trouvaille.

Re: Les petits écrans OLED

Posté : 08 août 2026, 21:13
par C2Vues-BricoTrain
:bienv: :bienv:

:PetitPoucejpg: :PetitPoucejpg: :PetitPoucejpg:

Alain,

J'y suis allé l'année dernière, et cette année aussi.

Pour information, le stand que tu filmes au tout début, c'est le stand de M. LABOUREL qui vend des modèles toutes échelles confondues de trains anciens, il expose depuis plusieurs années (et aussi mensuellement le dimanche au marché de Juvisy (91) et le mercredi au marché de St Mandé (94)).

Malheureusement il va arrêter définitivement en Décembre (il a 75 ans je crois...), et il va peut-être brader certaines choses...
Alors si vous voulez savoir si des éléments peuvent vous intéresser, je vous mets son téléphone, on ne sait jamais... :
06 75 30 27 04

...

Re: Les petits écrans OLED

Posté : 10 août 2026, 16:15
par likiki
ramasoft a écrit : 08 août 2026, 16:33 Tu dois aussi pouvoir faire défiler le texte horizontalement ou verticalement.
Je ne connais pas cette bibliothèque mais dans celle d'Adafruit que j'avais utilisée pour l'écran du tachymètre il y avait cette possibilité.
Essaye avec l'autocomplétion pour voir s'il y a ce genre d'instruction: display.startscrollright(start, stop);
Sinon demande à une IA.
.
IMG_0622.gif
.

Le programme

.
Test_Oled_V2.ino

Code : Tout sélectionner

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <U8g2lib.h>
#include <Wire.h>

// --- Point d'accès Wi-Fi ---
const char* ap_ssid = "WemosOLED";
const char* ap_password = "12345678"; // minimum 8 caractères

// --- Écran OLED ---
U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);

// --- Serveur web ---
ESP8266WebServer server(80);

String texteActuel = "En attente...";

// --- Textes préformatés (à personnaliser) ---
const char* textesPredefinis[10] = {
  "Bonjour !",
  "Bienvenue",
  "sur PASSIONNEMENT",
  "Tu trouvera ici",
  "des sujets sur tout",
  "Meme les marmottes",
  "ont leur sujet",
  "au plaisir de te",
  "lire bientot",
  "Vive PASSIONNEMENT."
};

// --- Mode d'affichage (texte principal) ---
enum ModeAffichage { MODE_FIXE, MODE_HORIZONTAL, MODE_VERTICAL };
ModeAffichage modeAffichage = MODE_HORIZONTAL;

// --- Mode d'affichage (textes de l'economiseur) ---
ModeAffichage modeEcoTexte = MODE_FIXE;

const int ecranLargeur = 128;
const int ecranHauteur = 64;
const int hauteurTexteLigne = 13;

// --- Défilement horizontal / vertical (texte principal) ---
int scrollX = ecranLargeur;
int scrollY = -hauteurTexteLigne;
int texteWidthPx = 0;

// --- Défilement horizontal / vertical (textes economiseur) ---
int ecoScrollX = ecranLargeur;
int ecoScrollY = -hauteurTexteLigne;
int ecoTexteWidthPx = 0;
String ecoTexteCourant = "";

unsigned long dernierScroll = 0;
const unsigned long delaiScroll = 70; // ms entre chaque décalage (plus petit = plus rapide)
const int vitesseScroll = 2;          // pixels par pas

// --- Gestion de l'inactivité / économiseur d'écran ---
unsigned long derniereActivite = 0;
const unsigned long delaiInactivite = 10000; // 10 secondes
bool modeEconomiseur = false;

// --- Cycle de l'économiseur : boule puis textes ---
enum EcoPhase { ECO_BALLE, ECO_TEXTE };
EcoPhase ecoPhase = ECO_BALLE;
unsigned long ecoPhaseDebut = 0;
const unsigned long dureeBalle = 20000;  // 20  secondes
const unsigned long dureeTexte = 10000;  // 10 secondes par texte
int ecoTexteIndex = 0;

// --- Point rebondissant ---
float pointX = 10, pointY = 10;
float pointDX = 1.2, pointDY = 0.9;
const int pointRayon = 2;
unsigned long dernierPoint = 0;
const unsigned long delaiPoint = 50; // ms entre chaque déplacement

void afficherFixe(String texte); // déclaration anticipée

void demarrerPhaseBalle() {
  ecoPhase = ECO_BALLE;
  ecoPhaseDebut = millis();
  pointX = 10; pointY = 10;
}

// Prépare l'affichage d'un texte d'économiseur selon modeEcoTexte
void preparerEcoTexte(String texte) {
  ecoTexteCourant = texte;
  u8g2.setFont(u8g2_font_ncenB08_tr);
  ecoTexteWidthPx = u8g2.getUTF8Width(texte.c_str());
  ecoScrollX = ecranLargeur;
  ecoScrollY = -hauteurTexteLigne;

  if (modeEcoTexte == MODE_FIXE) {
    afficherFixe(texte);
  }
}

void demarrerPhaseTexte() {
  ecoPhase = ECO_TEXTE;
  ecoPhaseDebut = millis();
  ecoTexteIndex = 0;
  preparerEcoTexte(String(textesPredefinis[ecoTexteIndex]));
}

void entrerEconomiseur() {
  modeEconomiseur = true;
  demarrerPhaseBalle();
}

void reprendreAffichageNormal() {
  if (modeAffichage == MODE_FIXE) {
    afficherFixe(texteActuel);
  } else if (modeAffichage == MODE_HORIZONTAL) {
    scrollX = ecranLargeur;
  } else if (modeAffichage == MODE_VERTICAL) {
    scrollY = -hauteurTexteLigne;
  }
}

void signalerActivite() {
  derniereActivite = millis();
  if (modeEconomiseur) {
    modeEconomiseur = false;
    reprendreAffichageNormal();
  }
}

// Découpe le texte en lignes qui tiennent dans la largeur de l'écran (respecte les mots)
int decouperEnLignes(String texte, String lignes[], int maxLignes) {
  int nbLignes = 0;
  String ligne = "";
  int start = 0;

  while (start < (int)texte.length() && nbLignes < maxLignes) {
    int espace = texte.indexOf(' ', start);
    if (espace == -1) espace = texte.length();
    String mot = texte.substring(start, espace);

    String essai = ligne.length() > 0 ? ligne + " " + mot : mot;
    if (u8g2.getUTF8Width(essai.c_str()) > ecranLargeur && ligne.length() > 0) {
      lignes[nbLignes++] = ligne;
      ligne = mot;
    } else {
      ligne = essai;
    }
    start = espace + 1;
  }
  if (ligne.length() > 0 && nbLignes < maxLignes) {
    lignes[nbLignes++] = ligne;
  }
  return nbLignes;
}

void afficherFixe(String texte) {
  u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_ncenB08_tr);

  const int maxLignes = 4;
  String lignes[maxLignes];
  int nbLignes = decouperEnLignes(texte, lignes, maxLignes);

  int hauteurTotale = nbLignes * hauteurTexteLigne;
  int yDepart = (ecranHauteur - hauteurTotale) / 2 + 11;
  if (yDepart < 11) yDepart = 11;

  for (int i = 0; i < nbLignes; i++) {
    int largeur = u8g2.getUTF8Width(lignes[i].c_str());
    int x = (ecranLargeur - largeur) / 2;
    if (x < 0) x = 0;
    u8g2.setCursor(x, yDepart + i * hauteurTexteLigne);
    u8g2.print(lignes[i]);
  }

  u8g2.sendBuffer();
}

void preparerTexte(String texte) {
  texteActuel = texte;
  u8g2.setFont(u8g2_font_ncenB08_tr);
  texteWidthPx = u8g2.getUTF8Width(texte.c_str());
  scrollX = ecranLargeur;
  scrollY = -hauteurTexteLigne;

  if (!modeEconomiseur && modeAffichage == MODE_FIXE) {
    afficherFixe(texteActuel);
  }
}

void mettreAJourHorizontal() {
  if (texteActuel.length() == 0) return;

  unsigned long maintenant = millis();
  if (maintenant - dernierScroll < delaiScroll) return;
  dernierScroll = maintenant;

  scrollX -= vitesseScroll;
  if (scrollX < -texteWidthPx) {
    scrollX = ecranLargeur;
  }

  u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_ncenB08_tr);
  int yCentre = (ecranHauteur / 2) + 4;
  u8g2.setCursor(scrollX, yCentre);
  u8g2.print(texteActuel);
  u8g2.sendBuffer();
}

void mettreAJourVertical() {
  if (texteActuel.length() == 0) return;

  unsigned long maintenant = millis();
  if (maintenant - dernierScroll < delaiScroll) return;
  dernierScroll = maintenant;

  scrollY += vitesseScroll;
  if (scrollY > ecranHauteur) {
    scrollY = -hauteurTexteLigne;
  }

  u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_ncenB08_tr);
  int largeur = u8g2.getUTF8Width(texteActuel.c_str());
  int x = (ecranLargeur - largeur) / 2;
  if (x < 0) x = 0;
  u8g2.setCursor(x, scrollY);
  u8g2.print(texteActuel);
  u8g2.sendBuffer();
}

void mettreAJourDefilement() {
  if (modeAffichage == MODE_HORIZONTAL) {
    mettreAJourHorizontal();
  } else if (modeAffichage == MODE_VERTICAL) {
    mettreAJourVertical();
  }
  // MODE_FIXE : rien à faire, l'écran a déjà été rendu par afficherFixe()
}

// --- Défilement des textes de l'économiseur ---
void mettreAJourEcoHorizontal() {
  if (ecoTexteCourant.length() == 0) return;

  unsigned long maintenant = millis();
  if (maintenant - dernierScroll < delaiScroll) return;
  dernierScroll = maintenant;

  ecoScrollX -= vitesseScroll;
  if (ecoScrollX < -ecoTexteWidthPx) {
    ecoScrollX = ecranLargeur;
  }

  u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_ncenB08_tr);
  int yCentre = (ecranHauteur / 2) + 4;
  u8g2.setCursor(ecoScrollX, yCentre);
  u8g2.print(ecoTexteCourant);
  u8g2.sendBuffer();
}

void mettreAJourEcoVertical() {
  if (ecoTexteCourant.length() == 0) return;

  unsigned long maintenant = millis();
  if (maintenant - dernierScroll < delaiScroll) return;
  dernierScroll = maintenant;

  ecoScrollY += vitesseScroll;
  if (ecoScrollY > ecranHauteur) {
    ecoScrollY = -hauteurTexteLigne;
  }

  u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_ncenB08_tr);
  int largeur = u8g2.getUTF8Width(ecoTexteCourant.c_str());
  int x = (ecranLargeur - largeur) / 2;
  if (x < 0) x = 0;
  u8g2.setCursor(x, ecoScrollY);
  u8g2.print(ecoTexteCourant);
  u8g2.sendBuffer();
}

void mettreAJourEcoDefilement() {
  if (modeEcoTexte == MODE_HORIZONTAL) {
    mettreAJourEcoHorizontal();
  } else if (modeEcoTexte == MODE_VERTICAL) {
    mettreAJourEcoVertical();
  }
  // MODE_FIXE : rien à faire, déjà affiché par afficherFixe() dans preparerEcoTexte()
}

void mettreAJourPointRebondissant() {
  unsigned long maintenant = millis();
  if (maintenant - dernierPoint < delaiPoint) return;
  dernierPoint = maintenant;

  pointX += pointDX;
  pointY += pointDY;

  if (pointX <= pointRayon || pointX >= ecranLargeur - pointRayon) {
    pointDX = -pointDX;
    pointX = constrain(pointX, pointRayon, ecranLargeur - pointRayon);
  }
  if (pointY <= pointRayon || pointY >= ecranHauteur - pointRayon) {
    pointDY = -pointDY;
    pointY = constrain(pointY, pointRayon, ecranHauteur - pointRayon);
  }

  u8g2.clearBuffer();
  u8g2.drawDisc((int)pointX, (int)pointY, pointRayon);
  u8g2.sendBuffer();
}

void gererEconomiseur() {
  unsigned long maintenant = millis();

  if (ecoPhase == ECO_BALLE) {
    mettreAJourPointRebondissant();
    if (maintenant - ecoPhaseDebut >= dureeBalle) {
      demarrerPhaseTexte();
    }
  } else { // ECO_TEXTE
    mettreAJourEcoDefilement();
    if (maintenant - ecoPhaseDebut >= dureeTexte) {
      ecoTexteIndex++;
      if (ecoTexteIndex >= 10) {
        demarrerPhaseBalle();
      } else {
        ecoPhaseDebut = maintenant;
        preparerEcoTexte(String(textesPredefinis[ecoTexteIndex]));
      }
    }
  }
}

void gererAffichage() {
  unsigned long maintenant = millis();

  if (!modeEconomiseur && (maintenant - derniereActivite > delaiInactivite)) {
    entrerEconomiseur();
  }

  if (modeEconomiseur) {
    gererEconomiseur();
  } else {
    mettreAJourDefilement();
  }
}

String nomMode(ModeAffichage m) {
  if (m == MODE_FIXE) return "Fixe";
  if (m == MODE_HORIZONTAL) return "Horizontal";
  return "Vertical";
}

void handleRoot() {
  signalerActivite();

  String html = "<!DOCTYPE html><html><head>";
  html += "<meta charset='UTF-8'>";
  html += "<meta name='viewport' content='width=device-width, initial-scale=1'>";
  html += "<title>Afficheur OLED</title>";
  html += "<style>body{font-family:sans-serif;text-align:center;margin-top:30px;}";
  html += "input{padding:10px;font-size:16px;width:80%;max-width:300px;}";
  html += "button{padding:10px 20px;font-size:16px;margin-top:10px;margin-left:5px;}";
  html += ".effacer{background-color:#e74c3c;color:white;border:none;border-radius:5px;}";
  html += ".toggle{background-color:#2980b9;color:white;border:none;border-radius:5px;}";
  html += ".toggleEco{background-color:#8e44ad;color:white;border:none;border-radius:5px;}";
  html += ".predefini{display:block;width:80%;max-width:300px;margin:6px auto;padding:10px;";
  html += "font-size:15px;background-color:#3498db;color:white;border:none;border-radius:5px;}";
  html += ".predefini:active{background-color:#2980b9;}";
  html += "hr{width:80%;max-width:300px;margin:25px auto;}</style>";
  html += "</head><body>";
  html += "<h2>Afficher un texte sur l'ecran OLED</h2>";

  html += "<form action='/set' method='GET'>";
  html += "<input type='text' name='texte' placeholder='Votre texte ici' maxlength='60'>";
  html += "<br><button type='submit'>Envoyer</button>";
  html += "</form>";

  html += "<form action='/toggleMode' method='GET'>";
  html += "<button type='submit' class='toggle'>Mode texte : " + nomMode(modeAffichage) + "</button>";
  html += "</form>";

  html += "<form action='/toggleModeEco' method='GET'>";
  html += "<button type='submit' class='toggleEco'>Mode eco : " + nomMode(modeEcoTexte) + "</button>";
  html += "</form>";

  html += "<form action='/clear' method='GET'>";
  html += "<button type='submit' class='effacer'>Effacer l'ecran</button>";
  html += "</form>";

  html += "<hr><h3>Textes predefinis</h3>";
  for (int i = 0; i < 10; i++) {
    html += "<form action='/set' method='GET'>";
    html += "<input type='hidden' name='texte' value='" + String(textesPredefinis[i]) + "'>";
    html += "<button type='submit' class='predefini'>" + String(textesPredefinis[i]) + "</button>";
    html += "</form>";
  }

  html += "<p>Texte actuel : " + texteActuel + "</p>";
  html += "</body></html>";

  server.send(200, "text/html", html);
}

void handleSet() {
  signalerActivite();
  if (server.hasArg("texte")) {
    preparerTexte(server.arg("texte"));
  }
  server.sendHeader("Location", "/");
  server.send(303);
}

void handleToggleMode() {
  signalerActivite();
  if (modeAffichage == MODE_FIXE) {
    modeAffichage = MODE_HORIZONTAL;
    scrollX = ecranLargeur;
  } else if (modeAffichage == MODE_HORIZONTAL) {
    modeAffichage = MODE_VERTICAL;
    scrollY = -hauteurTexteLigne;
  } else {
    modeAffichage = MODE_FIXE;
    afficherFixe(texteActuel);
  }
  server.sendHeader("Location", "/");
  server.send(303);
}

void handleToggleModeEco() {
  signalerActivite();
  if (modeEcoTexte == MODE_FIXE) {
    modeEcoTexte = MODE_HORIZONTAL;
  } else if (modeEcoTexte == MODE_HORIZONTAL) {
    modeEcoTexte = MODE_VERTICAL;
  } else {
    modeEcoTexte = MODE_FIXE;
  }
  server.sendHeader("Location", "/");
  server.send(303);
}

void handleClear() {
  signalerActivite();
  texteActuel = "";
  u8g2.clearBuffer();
  u8g2.sendBuffer();
  server.sendHeader("Location", "/");
  server.send(303);
}

void setup() {
  Serial.begin(115200);
  u8g2.begin();
  derniereActivite = millis();
  preparerTexte("Demarrage...");

  WiFi.mode(WIFI_AP);
  WiFi.softAP(ap_ssid, ap_password);

  IPAddress ip = WiFi.softAPIP();
  Serial.print("Point d'acces demarre, IP : ");
  Serial.println(ip);

  server.on("/", handleRoot);
  server.on("/set", handleSet);
  server.on("/toggleMode", handleToggleMode);
  server.on("/toggleModeEco", handleToggleModeEco);
  server.on("/clear", handleClear);
  server.begin();

  preparerTexte("WiFi: " + String(ap_ssid) + " IP: " + ip.toString());
  derniereActivite = millis();
}

void loop() {
  server.handleClient();
  gererAffichage();
}


:a_suivre:

Re: Les petits écrans OLED

Posté : 10 août 2026, 16:28
par ramasoft
:PetitPoucejpg: :PetitPoucejpg:

Re: Les petits écrans OLED

Posté : 10 août 2026, 18:00
par Amigorl
:PetitPoucejpg: :bienv:

Re: Les petits écrans OLED

Posté : 10 août 2026, 18:39
par C2Vues-BricoTrain
:bienv: :bienv:

Sympa le mode défilement.

:PetitPoucejpg: :PetitPoucejpg: :PetitPoucejpg:

Re: Les petits écrans OLED

Posté : 10 août 2026, 21:14
par bunny94
:bienv: :bienv: