J’ai trouvé récemment un capteur DHT11 qui traînais dans un tiroir, et je me suis dit que c’était l’occasion de faire un petit projet sympa avec. Je vais vous montrer comment fabriquer un thermomètre avec un ESP8266 et un capteur DHT11. Ce projet est parfait pour les débutants qui souhaitent se familiariser avec l’ESP8266 et les capteurs de température et d’humidité.
Pré-requis #
Nécessaire:
- Un ESP8266 (J’utiliserais un Wemos D1 Mini mais n’importe quel modèle fait l’affaire)
- Un capteur DHT11
- Un écran OLED 128x32
- Des fils de connexion (des Dupont font l’affaire)
- De quoi souder (fer, étain, etc.) ou une plaque d’essai (breadboard)
- Un PC avec VSCode et PlatformIO installé, ou tout autre IDE de votre choix
- Un peu de patience
Facultatif:
- Une photorésistance pour éteindre l’écran la nuit (sinon je ne dors pas)
- Un capteur capacitif pour basculer l’affichage entre la température et l’humidité (c’est un peu gadget mais je voulais jouer avec)
Montage #
Tous les capteurs sont alimentés en 3.3V, donc pas de soucis d’alimentation. Chaque capteur a sa propre pin pour les données, seul exception pour l’écran OLED qui utilise le bus I2C (donc deux pins seulement) et la photorésistance qui est branchée sur une pin analogique avec une résistance pull-down (10k Ohm).
Si c’est la première fois que vous réaliser ce genre de montage, je vous conseille de commencer sur une plaque d’essai (breadboard) avant de souder le tout et d’utiliser une carte de développement complète comme la Wemos D1 Arduino, qui connecte à l’ESP8266 l’intégralité des I/O avec des prises Dupont.
Code #
Pour le code, j’utilise PlatformIO, qui est un IDE basé sur VSCode et qui permet de gérer facilement les bibliothèques et les dépendances pour son projet, et ceux quelque soit la carte utilisée. Pour installer PlatformIO, il suffit d’installer l’extension PlatformIO IDE sur VSCode. Une fois installé, vous pouvez créer un nouveau projet en sélectionnant la carte ESP8266 et en ajoutant les bibliothèques nécessaires.
Pour ce projet, nous aurons besoin des bibliothèques suivantes :
- Adafruit GFX
- Adafruit SSD1306
- DHT sensor library
- DHT11
Vous pouvez les installer directement depuis les paramètres du projet ou en ajoutant les lignes suivantes dans le fichier platformio.ini
:
[env:d1]
platform = espressif8266
board = d1_mini_lite # adapter selon votre carte
framework = arduino
lib_deps =
adafruit/Adafruit SSD1306@^2.5.13
adafruit/Adafruit GFX Library@^1.12.0
adafruit/DHT sensor library@^1.4.6
adafruit/Adafruit Unified Sensor@^1.1.15
Une fois les bibliothèques installées, vous pouvez (enfin) jouer un peu avec le code. Voici le code que j’ai réalisé :
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <DHT.h>
#include <Wire.h>
// Définition de la taille de l'écran
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32
#define OLED_ADDR 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
// Capteur DHT
#define DHTPIN 2
#define DHTTYPE DHT11
DHT dht(DHTPIN, DHTTYPE);
// Bouton
#define BUTTON_PIN D7
// Photorésistance
#define PHOTORESISTOR_PIN A0 // Broche de la photorésistance
#define LIGHT_THRESHOLD 50 // Seuil de luminosité pour éteindre l'écran
bool isScreenOff = false; // État de l'écran
// États
volatile bool showTemperature = true;
volatile bool needsRefresh = true;
volatile bool triggerAnimation = false;
// Paramètres d'animation
#define ANIMATION_SPEED 6 // Vitesse de l'animation (1-10)
float h = -1; // Humidité
float t = -99; // Température
unsigned long lastSensorUpdate = 0;
unsigned long lastCheckTime = 0;
unsigned long lastAutoScrollTime =
0; // Temps de la dernière transition automatique
const unsigned long autoScrollInterval = 10000; // Intervalle de 10 secondes
// Icônes
const unsigned char epd_bitmap_humidity_mid_28dp[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x06, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x1f, 0x80, 0x00,
0x00, 0x3f, 0xc0, 0x00, 0x00, 0x79, 0xe0, 0x00, 0x00, 0xf0, 0xf0, 0x00,
0x01, 0xe0, 0x78, 0x00, 0x03, 0xc0, 0x3c, 0x00, 0x03, 0x80, 0x1c, 0x00,
0x07, 0x00, 0x0e, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00,
0x06, 0x00, 0x06, 0x00, 0x07, 0xff, 0xfe, 0x00, 0x07, 0xff, 0xfe, 0x00,
0x07, 0xff, 0xfe, 0x00, 0x07, 0xff, 0xfe, 0x00, 0x03, 0xff, 0xfc, 0x00,
0x01, 0xff, 0xf8, 0x00, 0x00, 0xff, 0xf0, 0x00, 0x00, 0x7f, 0xe0, 0x00,
0x00, 0x1f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00};
const unsigned char epd_bitmap_thermostat_28dp[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0xf0, 0x00, 0x00, 0x03, 0xf0, 0x00, 0x00,
0x03, 0x39, 0xff, 0x00, 0x03, 0x19, 0xff, 0x00, 0x03, 0x18, 0x00, 0x00,
0x03, 0x18, 0x00, 0x00, 0x03, 0x18, 0x00, 0x00, 0x03, 0x19, 0xf8, 0x00,
0x03, 0x19, 0xf8, 0x00, 0x03, 0x18, 0x00, 0x00, 0x07, 0x18, 0x00, 0x00,
0x0f, 0x1c, 0x00, 0x00, 0x0e, 0x0e, 0x00, 0x00, 0x0c, 0x06, 0x00, 0x00,
0x0c, 0x06, 0x00, 0x00, 0x0f, 0xfe, 0x00, 0x00, 0x0f, 0xfe, 0x00, 0x00,
0x0f, 0xfc, 0x00, 0x00, 0x07, 0xfc, 0x00, 0x00, 0x03, 0xf8, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
// Fonction pour dessiner un écran avec un décalage horizontal
void drawScreenContent(int16_t xOffset, bool temp) {
if (temp) {
display.drawBitmap(xOffset + 0, 4, epd_bitmap_thermostat_28dp, 28, 28,
SSD1306_WHITE);
display.setTextSize(3);
display.setTextColor(SSD1306_WHITE);
display.setCursor(xOffset + 30, 8);
display.print(F(" "));
if (isnan(t)) {
display.print(F("Er"));
} else {
display.print(int(t));
}
display.write(248); // °
display.print(F("C"));
// display.print(F(" C"));
} else {
display.drawBitmap(xOffset + 0, 4, epd_bitmap_humidity_mid_28dp, 28, 28,
SSD1306_WHITE);
display.setTextSize(3);
display.setTextColor(SSD1306_WHITE);
display.setCursor(xOffset + 30, 8);
display.print(F(" "));
if (isnan(h)) {
display.print(F("Er"));
} else {
display.print(int(h));
}
display.print(F(" %"));
}
}
float easeInOutQuad(float t) {
return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
}
float easeOutCubic(float t) { return (--t) * t * t + 1; }
// Animation de transition avec easing (défilement vers la gauche)
void animateTransition(bool fromTemp, bool toTemp) {
bool scrollLeft = fromTemp && !toTemp; // Temp vers humidité → gauche
bool scrollRight = !fromTemp && toTemp; // Humidité vers temp → droite
for (int step = 0; step <= 100; step += ANIMATION_SPEED) {
float progress = step / 100.0;
float easedProgress = easeOutCubic(progress);
int16_t offset = easedProgress * SCREEN_WIDTH;
display.clearDisplay();
if (scrollLeft) {
drawScreenContent(-offset, fromTemp);
drawScreenContent(SCREEN_WIDTH - offset, toTemp);
} else if (scrollRight) {
drawScreenContent(offset, fromTemp);
drawScreenContent(-SCREEN_WIDTH + offset, toTemp);
}
display.display();
}
}
void refreshScreen() {
display.clearDisplay();
drawScreenContent(0, showTemperature);
display.display();
needsRefresh = false;
}
void IRAM_ATTR handleButtonPress() {
static unsigned long lastInterruptTime = 0;
unsigned long interruptTime = millis();
if (interruptTime - lastInterruptTime > 200) {
showTemperature = !showTemperature;
needsRefresh = true;
triggerAnimation = true;
}
lastInterruptTime = interruptTime;
}
void showStartupScreen() {
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 10); // Position centrée approximativement
display.print(F("LUCAGOC.FR"));
display.display();
delay(2000); // Affiche pendant 2 secondes
}
void checkLightLevel() {
int lightLevel = analogRead(PHOTORESISTOR_PIN);
Serial.println(lightLevel); // Affiche le niveau de lumière sur le moniteur série
if (lightLevel < LIGHT_THRESHOLD && !isScreenOff) {
display.ssd1306_command(SSD1306_DISPLAYOFF); // Éteint l'écran
isScreenOff = true;
} else if (lightLevel >= LIGHT_THRESHOLD && isScreenOff) {
display.ssd1306_command(SSD1306_DISPLAYON); // Allume l'écran
isScreenOff = false;
needsRefresh = true; // Force une mise à jour de l'écran
}
}
void setup() {
Serial.begin(9600);
if (!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR)) {
Serial.println(F("Échec de l'initialisation de l'écran OLED"));
while (true)
;
}
showStartupScreen(); // Affiche l'écran de démarrage
display.clearDisplay();
dht.begin();
delay(2000);
pinMode(BUTTON_PIN, INPUT);
attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), handleButtonPress, RISING);
pinMode(PHOTORESISTOR_PIN, INPUT); // Configure la photorésistance
lastSensorUpdate = millis();
lastCheckTime = millis();
}
void loop() {
unsigned long currentTime = millis();
// Mise à jour des capteurs toutes les 2s
if (currentTime - lastSensorUpdate >= 2000) {
h = dht.readHumidity();
t = dht.readTemperature();
lastSensorUpdate = currentTime;
needsRefresh = true;
}
// Transition automatique toutes les 10 secondes
if (currentTime - lastAutoScrollTime >= autoScrollInterval) {
lastAutoScrollTime = currentTime;
showTemperature = !showTemperature;
triggerAnimation = true;
}
// Vérifie régulièrement si mise à jour de l'écran nécessaire
if (currentTime - lastCheckTime >= 100) {
lastCheckTime = currentTime;
if (triggerAnimation) {
bool previousState = !showTemperature; // On vient de l'inverse
animateTransition(previousState, showTemperature);
triggerAnimation = false;
}
if (needsRefresh) {
refreshScreen();
}
}
checkLightLevel(); // Vérifie le niveau de luminosité
}
Le code initialise l’écran OLED et le capteur DHT11, puis lit les valeurs de température et d’humidité toutes les 2 secondes. Il utilise également un bouton pour basculer entre l’affichage de la température et de l’humidité. Petit bonus, j’ai ajouté une animation de transition entre les deux affichages pour rendre le tout un peu plus joli.
Le dépôt GitHub contient le projet PlatformIO complet avec le code et les fichiers de configuration (et probablement avec des mises à jour).
Conclusion #
Voilà, vous avez maintenant un thermomètre fonctionnel avec un ESP8266 et un capteur DHT11 ! Je n’ai pas traité d’une partie très intéressante des ESP8266: le WiFi. Vous pouvez facilement ajouter une connexion WiFi pour envoyer les données sur un serveur HomeAssistant ou un autre service, j’y travaillerais peut-être dans un futur article.
Il pourrait être sympa d’ajouter aussi un capteur de particule pour avoir une idée de la qualité de l’air (ceux qui ont une imprimante 3D savent de quoi je parle).
En parlant d’imprimante 3D, je vais probablement réaliser un boîtier pour le montage même si je suis très fan des circuits à nu. Un boîtier transparent pourrait être sympa pour voir le montage et les LED.