Bonjour a tous, je vous présente ce sur quoi je travail en ce moment et par la même demander un peu d’aide afin d’intégrer une nouvelle « carte ».
Le projet:
Ma compagne s’installe en tisanerie (production des plants, récolte, séchage, conditionnement et vente) je suis donc mis à contribution pour le séchoir. L’idée est d’optimiser le séchage, pour cela j’ai créer une « boite » en bois avec des clayettes (la partie facile) et ensuite j’ai bossé sur la ventilation/régulation.
Le matériel:
- ESP32 Kitdev v1 (avec un petit écran donc)
- mosfet irlz44n
- diode 1n4007
- ventilateur 12v 2.89A
- capteurs temp/hum sht31
- bouton (pour changer les « modes »
le schéma:
le montage:
esphome:
name: sechoir
friendly_name: séchoir
esp32:
board: esp32dev
framework:
type: arduino
# Enable logging
# Enable Home Assistant API
api:
encryption:
key: "xxxxxx"
ota:
- platform: esphome
password: "xxxxxx"
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
fast_connect: true
reboot_timeout: 15min
power_save_mode: none
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Sechoir Fallback Hotspot"
password: "xxxxxx"
captive_portal:
# Configuration I2C
i2c:
sda: 21
scl: 22
scan: true
frequency: 100kHz
# Capteurs
sensor:
- platform: sht3xd
temperature:
name: "Température Séchoir"
id: temp_sechoir
accuracy_decimals: 1
filters:
- sliding_window_moving_average:
window_size: 3
send_every: 2
humidity:
name: "Humidité Séchoir"
id: humidity_sechoir
accuracy_decimals: 1
filters:
- sliding_window_moving_average:
window_size: 3
send_every: 2
address: 0x44
update_interval: 15s
- platform: template
name: "Vitesse Ventilateurs"
id: fan_speed_percent
unit_of_measurement: "%"
accuracy_decimals: 0
- platform: template
name: "Puissance Estimée"
id: power_estimate
unit_of_measurement: "W"
accuracy_decimals: 1
# Capteur de temps de fonctionnement
- platform: template
name: "Temps Séchage"
id: drying_time
unit_of_measurement: "h"
accuracy_decimals: 1
# AJOUT : Capteur pour diagnostiquer la vitesse réelle
- platform: template
name: "Vitesse Réelle Ventilateur"
id: real_fan_speed
unit_of_measurement: "%"
accuracy_decimals: 0
lambda: |-
if (id(ventilateurs_sechoir).state) {
return id(ventilateurs_sechoir).speed;
} else {
return 0;
}
update_interval: 5s
# Configuration des boutons physiques pour contrôle local
binary_sensor:
# Bouton MODE (GPIO4 - avec pullup interne)
- platform: gpio
pin:
number: GPIO4
inverted: true
mode:
input: true
pullup: true
name: "Bouton Mode"
id: button_mode
filters:
- delayed_on: 50ms
- delayed_off: 50ms
on_press:
then:
- lambda: |-
// Cycle entre les modes de séchage
int current_mode = id(drying_mode);
current_mode = (current_mode + 1) % 4; // 4 modes au total
id(drying_mode) = current_mode;
// Application des paramètres selon le mode
switch(current_mode) {
case 0: // Plantes délicates
id(target_temp) = 35.0;
id(max_humidity) = 45.0;
id(min_fan_speed) = 0.2;
ESP_LOGI("mode", "Mode: Plantes délicates (35°C, 45%% max)");
break;
case 1: // Standard
id(target_temp) = 40.0;
id(max_humidity) = 50.0;
id(min_fan_speed) = 0.3;
ESP_LOGI("mode", "Mode: Standard (40°C, 50%% max)");
break;
case 2: // Rapide
id(target_temp) = 45.0;
id(max_humidity) = 40.0;
id(min_fan_speed) = 0.4;
ESP_LOGI("mode", "Mode: Rapide (45°C, 40%% max)");
break;
case 3: // Personnalisé
id(target_temp) = 42.0;
id(max_humidity) = 45.0;
id(min_fan_speed) = 0.35;
ESP_LOGI("mode", "Mode: Personnalisé (42°C, 45%% max)");
break;
}
// Force la mise à jour de l'écran
id(mon_display).update();
# Alertes de sécurité
- platform: template
name: "Alerte Température Haute"
id: temp_high_alert
lambda: |-
if (!id(temp_sechoir).has_state()) return false;
return id(temp_sechoir).state > 50;
filters:
- delayed_on: 60s
- delayed_off: 30s
on_press:
then:
- logger.log:
level: WARN
format: "ALERTE: Température trop élevée %.1f°C - ARRÊT DE SÉCURITÉ"
args: [ 'id(temp_sechoir).state' ]
- switch.turn_off: sechoir_active
- platform: template
name: "Alerte Humidité Haute"
id: humidity_high_alert
lambda: |-
if (!id(humidity_sechoir).has_state()) return false;
return id(humidity_sechoir).state > 75;
filters:
- delayed_on: 300s # 5 minutes avant alerte
- delayed_off: 60s
on_press:
then:
- logger.log:
level: WARN
format: "ALERTE: Humidité persistante %.1f%% - Vérifier ventilation"
args: [ 'id(humidity_sechoir).state' ]
# Écran avec informations complètes
display:
- platform: ssd1306_i2c
model: "SSD1306 128x64"
address: 0x3C
id: mon_display
update_interval: 2s
lambda: |-
// Titre et ligne de séparation
it.printf(0, 0, id(font1), "SECHOIR PLANTES");
it.line(0, 12, 128, 12);
// Données de capteurs
if (id(temp_sechoir).has_state() && id(humidity_sechoir).has_state()) {
it.printf(0, 16, id(font2), "%.1f°C %.0f%%",
id(temp_sechoir).state, id(humidity_sechoir).state);
}
// Mode de séchage
const char* modes[] = {"Delicat", "Standard", "Rapide", "Perso"};
it.printf(0, 28, id(font2), "Mode: %s", modes[id(drying_mode)]);
// Cible et ventilation
it.printf(0, 40, id(font2), "Cible: %.0f°C", id(target_temp));
if (id(fan_speed_percent).has_state()) {
it.printf(70, 40, id(font2), "V:%.0f%%", id(fan_speed_percent).state);
}
// État et temps
if (id(sechoir_active).state) {
it.printf(0, 52, id(font2), "ACTIF");
if (id(drying_time).has_state()) {
it.printf(50, 52, id(font2), "%.1fh", id(drying_time).state);
}
} else {
it.printf(0, 52, id(font2), "PRET - APPUI ONOFF");
}
# Polices optimisées
font:
- file: "font/arial.ttf"
id: font1
size: 12
- file: "font/arial.ttf"
id: font2
size: 10
# Sortie PWM pour ventilateurs
output:
- platform: ledc
pin: GPIO18
id: fan_pwm_output
frequency: 25000 Hz
# Contrôleur de ventilateurs - CORRIGÉ
fan:
- platform: speed
output: fan_pwm_output
name: "Ventilateurs Séchoir"
id: ventilateurs_sechoir
speed_count: 100 # AJOUT : Force 100 niveaux de vitesse explicites
# Variables globales pour fonctionnement autonome
globals:
# Paramètres de régulation (modifiés par les modes)
- id: target_temp
type: float
restore_value: true
initial_value: '40.0' # Standard par défaut
- id: temp_tolerance
type: float
restore_value: true
initial_value: '2.0'
- id: max_humidity
type: float
restore_value: true
initial_value: '50.0' # Standard par défaut
- id: min_fan_speed
type: float
restore_value: true
initial_value: '0.3' # Standard par défaut
# Mode de séchage (0=délicat, 1=standard, 2=rapide, 3=personnalisé)
- id: drying_mode
type: int
restore_value: true
initial_value: '1' # Mode standard par défaut
# Compteur de temps
- id: start_time
type: unsigned long
restore_value: false
initial_value: '0'
# Interrupteur principal (peut être contrôlé via web ou bouton)
switch:
- platform: template
name: "Séchoir Actif"
id: sechoir_active
restore_mode: RESTORE_DEFAULT_OFF
optimistic: true
turn_on_action:
- logger.log: "Séchoir activé - Mode autonome"
- lambda: 'id(start_time) = millis();'
- component.update: mon_display
turn_off_action:
- logger.log: "Séchoir désactivé"
- fan.turn_off: ventilateurs_sechoir
- lambda: 'id(drying_time).publish_state(0);'
- component.update: mon_display
# Automatisation principale - TOTALEMENT AUTONOME
interval:
# Régulation principale toutes les 15 secondes
- interval: 15s
then:
- lambda: |-
// Mise à jour du temps de fonctionnement
if (id(sechoir_active).state && id(start_time) > 0) {
unsigned long elapsed = millis() - id(start_time);
float hours = elapsed / 3600000.0; // Conversion en heures
id(drying_time).publish_state(hours);
}
// Si séchoir inactif, arrêter les ventilateurs
if (!id(sechoir_active).state) {
id(ventilateurs_sechoir).turn_off();
id(fan_speed_percent).publish_state(0);
return;
}
// Vérifier que les capteurs sont prêts
if (!id(temp_sechoir).has_state() || !id(humidity_sechoir).has_state()) {
ESP_LOGW("main", "Capteurs non prêts, attente...");
return;
}
float current_temp = id(temp_sechoir).state;
float current_humidity = id(humidity_sechoir).state;
float target = id(target_temp);
float tolerance = id(temp_tolerance);
float max_hum = id(max_humidity);
float min_speed = id(min_fan_speed);
float fan_speed = 0.0;
// === ALGORITHME DE RÉGULATION AVANCÉ ===
// 1. Régulation basée sur la température
if (current_temp < (target - tolerance)) {
// Température trop basse - ventilation minimale pour circulation
fan_speed = min_speed;
} else if (current_temp > (target + tolerance)) {
// Température trop haute - ventilation maximale
fan_speed = 1.0;
} else {
// Dans la plage cible - régulation proportionnelle
float temp_diff = current_temp - (target - tolerance);
float temp_range = 2 * tolerance;
fan_speed = min_speed + (1.0 - min_speed) * (temp_diff / temp_range);
}
// 2. Correction pour l'humidité excessive
if (current_humidity > max_hum) {
float humidity_excess = current_humidity - max_hum;
float humidity_factor = humidity_excess / 25.0; // Sur 25% d'excès
fan_speed = std::max(fan_speed, std::min(1.0f, 0.6f + humidity_factor));
}
// 3. Boost automatique si humidité très élevée (>65%)
if (current_humidity > 65.0) {
fan_speed = std::max(fan_speed, 0.8f);
ESP_LOGI("regulation", "Boost humidité activé: %.1f%%", current_humidity);
}
// 4. Protection surchauffe
if (current_temp > 48.0) {
fan_speed = 1.0; // Ventilation maximale
ESP_LOGW("regulation", "Protection surchauffe: %.1f°C", current_temp);
}
// 5. Application de la vitesse calculée - CORRIGÉ
fan_speed = std::max(0.0f, std::min(1.0f, fan_speed));
if (fan_speed > 0.05) {
// CORRECTION PRINCIPALE : convertir explicitement en pourcentage entier
int speed_percent = static_cast<int>(fan_speed * 100);
speed_percent = std::max(1, std::min(100, speed_percent)); // Force entre 1-100
auto call = id(ventilateurs_sechoir).turn_on();
call.set_speed(speed_percent); // Envoie un entier, pas un float
call.perform();
// Log pour debug
ESP_LOGI("fan_control", "Vitesse calculée: %.2f -> Envoyé: %d%%", fan_speed, speed_percent);
} else {
id(ventilateurs_sechoir).turn_off();
ESP_LOGI("fan_control", "Ventilateur arrêté");
}
// Mise à jour des capteurs de suivi
id(fan_speed_percent).publish_state(fan_speed * 100);
id(power_estimate).publish_state(fan_speed * 2.85 * 12 * 0.8);
// Log de debug
ESP_LOGI("regulation", "T:%.1f°C H:%.1f%% Fan:%.0f%% Mode:%d",
current_temp, current_humidity, fan_speed*100, id(drying_mode));
# Cycle automatique de fin de séchage (optionnel)
- interval: 300s # Toutes les 5 minutes
then:
- lambda: |-
// Arrêt automatique si séchage terminé (conditions optimales atteintes)
if (id(sechoir_active).state &&
id(temp_sechoir).has_state() &&
id(humidity_sechoir).has_state()) {
float temp = id(temp_sechoir).state;
float humidity = id(humidity_sechoir).state;
float target = id(target_temp);
unsigned long elapsed = millis() - id(start_time);
float hours = elapsed / 3600000.0;
// Conditions d'arrêt automatique après minimum 4h
if (hours > 4.0 &&
abs(temp - target) < 1.0 &&
humidity < (id(max_humidity) - 5.0)) {
ESP_LOGI("auto_stop", "Séchage terminé automatiquement après %.1fh", hours);
id(sechoir_active).turn_off();
}
}
# Diagnostic système toutes les minutes
- interval: 60s
then:
- lambda: |-
ESP_LOGI("diagnostic", "WiFi: %s, Uptime: %ds, Free RAM: %d",
WiFi.isConnected() ? "OK" : "Offline",
millis()/1000,
ESP.getFreeHeap()
);
# Logs système
logger:
level: INFO
logs:
sensor: INFO
fan: INFO
wifi: WARN # Réduit car WiFi optionnel
api: WARN # Réduit car API optionnelle
Pour ne rien vous cacher mes compétences en code sont nuls j’ai donc demander un code à une IA (claude 4) pour avoir une base et je l’ai retravailler pour corriger/modifier des choses.
En théorie tous marche (je dis en théorie car l’humidité la ou je test est à plus de 75% du coup le ventilateur se met à fond directement!!! Il diminue bien si je le fait manuellement reste à le mettre en place pour tester en condition!!
Prochaine étape, je souhaiterai réutiliser l’alimentation d’origine du ventilateur (actuellement je travail avec une alimentation stabilisé de bureau). Celle-ci comprend un potentiomètre, je souhaiterai modifier cette « carte » pour réutiliser l’alimentation d’origine.
Là je sèche un peu… si vous avez des idées…
la « carte »
Le branchement à la prise est en haut…
N’hésitez pas à me donner votre avis!!
edit: je l’ai mis dans vos projets plutot que question…