Horloge de chevet NTP OLED ESP8266 sur Powercube Alocacoc
Lire l’heure en toute discrétion durant la nuit

Objectif
Ne pas éveiller sa moitié en lisant l’heure pendant la nuit.
Description
Composants
Les liens ci-dessus sont donnés à titre d’exemple.
Schéma

Boîtier
J’ai conçu ce boîtier pour qu’il puisse se coller à l’aide d’un adhésif double face réutilisable sur l’ USB d’un Powercube en laissant une prise USB libre.
Les fichiers 3D pourront bientôt être téléchargés en cliquant sur le bouton ci-dessous

Si vous souhaitez les obtenir avant, laissez-moi un message.
Vous pouvez adapter les dimensions et modifier les formes à partir du fichier FreeCAD.
Code
J’ai récupéré la partie horloge du programme de mon article « Grande horloge… » .
Pour la programmation, j’utilise Platformio mais l’IDE Arduino convient également.
Il vous faudra adapter le code en fonction de votre fuseau horaire pour le passage à l’heure d’été/hiver !
Vous devrez également remplacer les x par vos propres adresses IP, les *** par votre SSID, votre mot de passe WiFi et les zzzz par le serveur NTP à utiliser.
L’écran peut être commuté par le bouton ON et OFF ou simplement ON avec une temporisation avant de s’éteindre.
Platformio.ini
platform = espressif8266
board = d1_mini
framework = arduino
lib_deps =
adafruit/Adafruit GFX Library@^1.11.11
adafruit/Adafruit SSD1306@^2.5.13
thomasfredericks/Bounce2@^2.72
sstaub/Ticker@^4.4.0
monitor_speed = 115200
main.cpp
#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Fonts/FreeSans24pt7b.h>
#include <Fonts/FreeSansOblique9pt7b.h>
#include <Adafruit_SSD1306.h>
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <bounce2.h>
#include <Ticker.h>
#include <jagprod.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
#define display_pin D3
Bounce bouton_display = Bounce();
String heure;
unsigned int localPort = 2390;
const int NTP_PACKET_SIZE = 48;
byte packetBuffer[NTP_PACKET_SIZE];
IPAddress timeServerIP;
WiFiUDP udp;
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
int s;
int ds;
int m;
int dm;
int h;
int dh;
int gmtbe;
void quelleheure(IPAddress &address);
void plus1s();
Ticker uneseconde(plus1s, 1000);
bool temoinntp = true;
// Mettre la ligne suivante en remarque Si affichage temporisé
bool displaystate = true;
long secondes;
// Variables DSTAuto
int dst = 1;
unsigned long epoch;
unsigned long epochmars;
unsigned long epochoctobre;
unsigned long epochannee;
int seconde;
int minute;
int Heure;
int bis = 0;
int annee;
unsigned long epoch01012025 = 1735689600;
unsigned long nombredejours;
// int mois;
int nombredejoursannee;
int jour;
String joursemaine;
int queljoursemaine;
//void laps();
String mois;
String epoch2string;
void plus1s() // Action à réaliser lorsque le timer uneseconde est écoulé
{
s = s + 1;
Serial.println(s);
}
void setup()
{
Serial.begin(115200);
pinMode(D1, INPUT_PULLUP);
pinMode(D2, INPUT_PULLUP);
Wire.begin(D2, D1);
pinMode(display_pin, INPUT_PULLUP);
bouton_display.attach(display_pin);
bouton_display.interval(10);
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS))
{
Serial.println(F("SSD1306 allocation failed"));
for (;;)
; // Don't proceed, loop forever
}
display.setTextColor(SSD1306_WHITE);
display.setTextSize(1);
}
void loop()
{
uneseconde.update();
bouton_display.update();
//--------------------------------
if (bouton_display.fell())
{
// Pour l'affichage temporisé
/* display.ssd1306_command(SSD1306_DISPLAYON);
uneseconde.pause();
delay(4000);
uneseconde.resume();
display.ssd1306_command(SSD1306_DISPLAYOFF);
s = s - 4;
*/
// Pour l'affichage switché
displaystate = !displaystate;
if (displaystate == true)
{
display.ssd1306_command(SSD1306_DISPLAYON);
}
else
{
display.ssd1306_command(SSD1306_DISPLAYOFF);
}
}
secondes = dh * 36000 + h * 3600 + dm * 600 + m * 60 + ds * 10 + s;
if (secondes == 32400 or secondes == 75600)
{ // Synchro NTP à 9h et 21h
temoinntp = true;
}
if (temoinntp == true) // il faut faire une synchro NTP
{
quelleheure(timeServerIP);
temoinntp = false;
}
if (s > 9)
{
s = 0;
ds = ds + 1;
if (ds > 5)
{
ds = 0;
m = m + 1;
if (m > 9)
{
m = 0;
dm = dm + 1;
if (dm > 5)
{
dm = 0;
h = h + 1;
if (dh == 2 and h == 4)
{
dh = 0;
h = 0;
}
if (h > 9)
{
h = 0;
dh = dh + 1;
}
}
}
heure = String(dh) + String(h) + ":" + String(dm) + String(m);
Serial.println(heure);
display.clearDisplay();
display.setFont(&FreeSans24pt7b);
display.setCursor(5, 32);
display.print(heure);
display.display();
// Mettre la ligne suivante en remarque si affichage switché
// display.ssd1306_command(SSD1306_DISPLAYOFF);
}
}
}
void quelleheure(IPAddress &address) // Synchronisation du temps via NTP
{
uneseconde.pause();
WiFi.mode(WIFI_STA);
display.setFont(&FreeSansOblique9pt7b);
display.clearDisplay();
display.setCursor(37, 14);
display.print("Mise a");
display.setCursor(37, 31);
display.println("l'heure");
display.display();
if (!WiFi.config(ip, gw, mask, ns))
{
}
WiFi.begin(ssid, wifi_sesame);
while (WiFi.status() != WL_CONNECTED)
{
delay(1000);
}
udp.begin(localPort);
WiFi.hostByName(ntpServerName, timeServerIP);
memset(packetBuffer, 0, NTP_PACKET_SIZE);
packetBuffer[0] = 0b11100011;
packetBuffer[1] = 0;
packetBuffer[2] = 6;
packetBuffer[3] = 0xEC;
packetBuffer[12] = 49;
packetBuffer[13] = 0x4E;
packetBuffer[14] = 49;
packetBuffer[15] = 52;
udp.beginPacket(address, 123);
udp.write(packetBuffer, NTP_PACKET_SIZE);
udp.endPacket();
delay(1000);
int cb = udp.parsePacket();
while (!cb)
{
cb = udp.parsePacket();
}
udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer
unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
unsigned long secsSince1900 = highWord << 16 | lowWord;
const unsigned long seventyYears = 2208988800UL;
unsigned long epoch = secsSince1900 - seventyYears - 1; // moins délais udp
WiFi.mode(WIFI_OFF);
annee = 2025;
bis = 0;
epochannee = epoch01012025;
nombredejours = (epoch - epoch01012025) / 86400L + 1; // car le 1 janvier correspond aux secondes 0 à 86400 soit le jour 0
nombredejoursannee = nombredejours;
while (nombredejoursannee > 365 + bis)
{
nombredejoursannee = nombredejoursannee - (365 + bis);
annee = annee + 1;
epochannee = epochannee + (365 + bis) * 86400L;
bis = 0;
if (annee % 400 == 0 or (annee % 4 == 0 and annee % 100 != 0))
{
bis = 1;
}
}
if (nombredejoursannee <= 31)
{
mois = "janvier";
jour = nombredejoursannee;
}
else if (nombredejoursannee <= (59 + bis))
{
mois = "fevrier";
jour = nombredejoursannee - 31;
}
else if (nombredejoursannee <= 90 + bis)
{
mois = "mars";
jour = nombredejoursannee - (59 + bis);
}
else if (nombredejoursannee <= 120 + bis)
{
mois = "avril";
jour = nombredejoursannee - (90 + bis);
}
else if (nombredejoursannee <= 151 + bis)
{
mois = "mai";
jour = nombredejoursannee - (120 + bis);
}
else if (nombredejoursannee <= 181 + bis)
{
mois = "juin";
jour = nombredejoursannee - (151 + bis);
}
else if (nombredejoursannee <= 212 + bis)
{
mois = "juillet";
jour = nombredejoursannee - (181 + bis);
}
else if (nombredejoursannee <= 243 + bis)
{
mois = "aout";
jour = nombredejoursannee - (212 + bis);
}
else if (nombredejoursannee <= 273 + bis)
{
mois = "septembre";
jour = nombredejoursannee - (243 + bis);
}
else if (nombredejoursannee <= 304 + bis)
{
mois = "octobre";
jour = nombredejoursannee - (273 + bis);
}
else if (nombredejoursannee <= 334 + bis)
{
mois = "novembre";
jour = nombredejoursannee - (304 + bis);
}
else if (nombredejoursannee <= 365 + bis)
{
mois = "decembre";
jour = nombredejoursannee - (334 + bis);
}
queljoursemaine = (epoch / 86400L + 4) % 7; // +4 car 1/1/1970 = jeudi = jour 4 (si dimanche = 0)
switch (queljoursemaine)
{
case 0:
joursemaine = "Dimanche";
break;
case 1:
joursemaine = "Lundi";
break;
case 2:
joursemaine = "Mardi";
break;
case 3:
joursemaine = "Mercredi";
break;
case 4:
joursemaine = "Jeudi";
break;
case 5:
joursemaine = "Vendredi";
break;
case 6:
joursemaine = "Samedi";
break;
}
epochmars = epochannee + (90 + bis) * 86400UL; // correspond au dernier jour de de mars
queljoursemaine = (epochmars / 86400UL + 4) % 7; // +4 car 1/1/1970 = jeudi = jour 4 (si dimanche = 0)
while (queljoursemaine != 0)
{
epochmars = epochmars - 86400UL;
queljoursemaine = (epochmars / 86400UL + 4) % 7;
}
epochmars = epochmars + 1 * 3600UL; // le changement se fait à UTC+1 soit +2H car UTC+1 +DST hiver
epochoctobre = epochannee + (303 + bis) * 86400UL; // correspond au dernier jour d'octobre
queljoursemaine = (epochoctobre / 86400UL + 4) % 7; // +4 car 1/1/1970 = jeudi = jour 4 (si dimanche = 0)
while (queljoursemaine != 0)
{
epochoctobre = epochoctobre - 86400UL;
queljoursemaine = (epochoctobre / 86400UL + 4) % 7;
}
epochoctobre = epochoctobre + 1 * 3600UL; // le changement se fait à UTC+1 soit +3H car UTC+1 +DST été
if (epoch >= epochmars and epoch < epochoctobre)
{
dst = 2;
}
else
{
dst = 1;
}
Heure = ((epoch % 86400UL) / 3600UL) + dst; // +1 = Heure d'hiver et +2 = Heure d'été
if (Heure >= 24)
{
Heure = Heure - 24;
}
Serial.print("epoch = ");
Serial.println(epoch);
Serial.print("epochmars = ");
Serial.println(epochmars);
Serial.print("epochoctobre = ");
Serial.println(epochoctobre);
Serial.print("dst = ");
Serial.println(dst);
minute = ((epoch % 3600) / 60);
seconde = (epoch % 60);
// Mise en forme pour l'affichage
dh = Heure / 10;
h = Heure - (dh * 10);
dm = minute / 10;
m = minute - (dm * 10);
ds = seconde / 10;
s = seconde - (ds * 10);
uneseconde.resume();
heure = String(dh) + String(h) + ":" + String(dm) + String(m);
Serial.println(heure);
Serial.println(dst);
display.clearDisplay();
displaystate = false;
display.display();
}


