Browse Source

Gestion ESP32S3 + upload web OTA

master
scayac 2 months ago
parent
commit
ed3da859c2
  1. 1
      .gitignore
  2. 434
      README.md
  3. BIN
      data/favicon.ico
  4. 4
      data/update.html
  5. 23
      platformio.ini
  6. 47
      src/main.cpp

1
.gitignore vendored

@ -3,3 +3,4 @@ @@ -3,3 +3,4 @@
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch
config.json

434
README.md

@ -1,182 +1,402 @@ @@ -1,182 +1,402 @@
# ESP32 Contrôleur Solaire pour Chauffe-Eau
Application ESP32 avec serveur web, stockage LittleFS et mises à jour OTA.
Application ESP32 pour contrôler intelligemment un chauffe-eau avec l'excédent de production solaire.
## Fonctionnalités
- ✅ Serveur web avec stockage HTML sur LittleFS
- ✅ Protection par mot de passe (authentification SHA-256)
- ✅ Page d'accueil "Hello World" à la racine
- ✅ Interface de mise à jour OTA accessible via `/update.html`
- ✅ Mise à jour du firmware via OTA
- ✅ Mise à jour du filesystem via OTA
### Interface Web
- ✅ Serveur web asynchrone (ESPAsyncWebServer) avec authentification SHA-256
- ✅ Interface responsive avec suivi en temps réel
- ✅ Configuration complète via interface web
- ✅ Mise à jour OTA du firmware et du filesystem via `/update.html`
- ✅ Support ArduinoOTA pour mise à jour via l'IDE
## Configuration
### Modes de Fonctionnement
- **Mode OFF (0)**: Chauffe-eau désactivé
- **Mode JOUR (2)**: Chauffe progressive pour atteindre la température maximale à l'heure cible
- **Mode SOLEIL (4)**: Chauffe uniquement avec l'excédent solaire
- **Mode NUIT (8)**: Chauffe forcée 100% pendant la période nocturne jusqu'à la température minimale fixée en paramètre
- **Mode ON (16)**: Chauffe forcée 100% en continu
- **Modes combinables**: JOUR+SOLEIL (6), NUIT+SOLEIL (12), NUIT+JOUR (10), NUIT+SOLEIL+JOUR (14)
### Contrôle Intelligent
- ✅ PWM logiciel 1 Hz pour contrôle précis de la puissance
- ✅ Intégration passerelle Enphase pour données solaires en temps réel
- ✅ Réception température via API ESPEasy
- ✅ API REST complète pour contrôle externe
### Configuration
La configuration se fait exclusivement via l'interface web à `/settings.html`. Toutes les modifications sont enregistrées dans `config.json`.
#### Paramètres WiFi
- **SSID et mot de passe** : Configurables via l'interface web
- **Mot de passe AP** : Pour le mode Access Point (min 8 caractères)
- **Mode AP automatique** : Si la connexion WiFi échoue, l'ESP32 démarre un point d'accès
#### Authentification Web
- **Utilisateur par défaut** : `admin`
- **Mot de passe par défaut** : `password`
- **Changement de mot de passe** : Via l'interface `/settings.html` (hash SHA-256 automatique)
- **Timeout de session** : Configurable (défaut: 60 minutes)
#### Paramètres Système
- **Hostname** : Nom de l'appareil sur le réseau
- **Puissance max chauffe-eau** : En Watts (défaut: 2400W)
- **Température eau min/max** : Plages de température (défaut: 40-60°C)
- **Coefficient jour** : Pour calcul mode JOUR (défaut: 350)
- **Heure cible** : Heure pour atteindre temp max en mode JOUR (défaut: 17h)
- **Période nuit** : Début et fin (défaut: 0h-4h)
- **Coordonnées GPS** : Latitude/Longitude pour calcul lever/coucher soleil
#### Intégration Enphase
- **IP passerelle** : Adresse IP de la passerelle Enphase
- **Token JWT** : Token d'authentification
- **Intervalle mise à jour** : En secondes (défaut: 5s)
### 1. Modifier les identifiants WiFi
Éditez `src/main.cpp` et modifiez les lignes suivantes:
```cpp
const char* ssid = "VotreSSID"; // Votre nom de réseau WiFi
const char* password = "VotreMotDePasse"; // Votre mot de passe WiFi
```
### 2. Configuration OTA (optionnel)
## Installation
```cpp
const char* otaHostname = "ESP32-Controleur-Solaire"; // Nom de l'appareil sur le réseau
const char* otaPassword = "admin"; // Mot de passe pour OTA via IDE
### Matériel requis
- **ESP32-S3** (testé avec ESP32-S3-supermini)
- QFN56, révision v0.2
- Flash 4MB (XMC)
- PSRAM 2MB (AP_3v3)
- Crystal 40MHz
- USB-Serial/JTAG intégré
- **GPIO12** : Sortie pour commande chauffe-eau (PWM logiciel 1Hz)
### Configuration PlatformIO
Le fichier `platformio.ini` est déjà configuré pour l'ESP32-S3 :
```ini
[env:esp32-s3-supermini]
platform = espressif32
board = esp32-s3-devkitc-1
framework = arduino
board_build.mcu = esp32s3
board_build.f_cpu = 240000000L
board_build.flash_size = 4MB
board_build.partitions = default.csv
board_build.filesystem = littlefs
build_flags =
-DBOARD_HAS_PSRAM
-DARDUINO_USB_MODE=1
-DARDUINO_USB_CDC_ON_BOOT=1
lib_deps =
esphome/ESPAsyncWebServer-esphome@^3.1.0
bblanchon/ArduinoJson@^7.2.1
me-no-dev/AsyncTCP@^1.1.4
```
### 3. Configuration de l'authentification web
Par défaut, l'accès au serveur web est protégé par mot de passe:
- **Utilisateur**: `admin`
- **Mot de passe**: `password`
#### Changer le mot de passe:
1. **Générer le hash SHA-256 de votre nouveau mot de passe:**
Sous Linux/Mac:
```bash
echo -n "votre_nouveau_mot_de_passe" | sha256sum
```
Sous Windows (PowerShell):
```powershell
$stringAsStream = [System.IO.MemoryStream]::new()
$writer = [System.IO.StreamWriter]::new($stringAsStream)
$writer.write("votre_nouveau_mot_de_passe")
$writer.Flush()
$stringAsStream.Position = 0
Get-FileHash -InputStream $stringAsStream -Algorithm SHA256 | Select-Object Hash
```
Ou utilisez un outil en ligne: https://emn178.github.io/online-tools/sha256.html
2. **Éditer le fichier `data/config.json`:**
```json
{
"auth": {
"username": "admin",
"password_hash": "VOTRE_NOUVEAU_HASH_ICI"
}
}
```
3. **Re-uploader le filesystem:**
```bash
platformio run --target uploadfs
```
**⚠ Important**: Le mot de passe est stocké sous forme de hash SHA-256 dans le fichier de configuration pour plus de sécurité.
## Installation
### Première installation (câble USB)
1. **Compiler et uploader le code:**
1. **Compiler et uploader le firmware:**
```bash
platformio run --target upload
platformio run --target upload --environment esp32-s3-supermini
```
2. **Uploader le filesystem LittleFS:**
```bash
platformio run --target uploadfs
platformio run --target uploadfs --environment esp32-s3-supermini
```
3. **Accéder à l'interface web:**
- En mode AP : connectez-vous au réseau WiFi "Routeur solaire" (mot de passe: `123456`)
- Ouvrez http://192.168.4.1
- Connectez-vous avec `admin` / `password`
- Configurez votre WiFi dans `/settings.html`
### Mises à jour ultérieures
#### Via l'interface web `/update.html`:
#### Via l'interface web `/update.html` (recommandé):
1. Accédez à `http://[IP_de_votre_ESP]/update.html`
2. Sélectionnez le fichier `.bin` approprié:
- **Firmware**: `.pio/build/esp32/firmware.bin`
- **Filesystem**: `.pio/build/esp32/littlefs.bin`
- **Firmware**: `.pio/build/esp32-s3-supermini/firmware.bin`
- **Filesystem**: `.pio/build/esp32-s3-supermini/littlefs.bin`
3. Cliquez sur le bouton d'upload correspondant
4. Attendez la fin du transfert (100%)
5. Le message de succès s'affiche pendant 10s (firmware) ou 8s (filesystem)
6. Redirection automatique vers `/index.html` après redémarrage
#### Via ArduinoOTA (depuis PlatformIO):
```bash
# Mise à jour du firmware
platformio run --target upload --upload-port [IP_de_votre_ESP]
platformio run --target upload --environment esp32-s3-supermini --upload-port [IP_de_votre_ESP]
# Mise à jour du filesystem
platformio run --target uploadfs --upload-port [IP_de_votre_ESP]
platformio run --target uploadfs --environment esp32-s3-supermini --upload-port [IP_de_votre_ESP]
```
**Note**: Le mot de passe OTA est `password` (configurable dans `main.cpp`)
## Structure du projet
```
esp32_controleur_solaire/
├── platformio.ini # Configuration PlatformIO
RouteurSolaire/
├── platformio.ini # Configuration PlatformIO ESP32-S3
├── src/
│ └── main.cpp # Code principal
│ └── main.cpp # Code principal (1400+ lignes)
├── data/ # Fichiers pour LittleFS
│ ├── config.json # Configuration (hash du mot de passe)
│ ├── index.html # Console principale (tout CSS inclus inline)
│ ├── login.html # Page de connexion (tout CSS inclus inline)
│ ├── update.html # Interface de mise à jour OTA (tout CSS inclus inline)
│ └── settings.html # Page de configuration (tout CSS inclus inline)
│ ├── config.json # Configuration persistante
│ ├── index.html # Console principale avec graphiques
│ ├── login.html # Page de connexion
│ ├── settings.html # Configuration système
│ ├── update.html # Interface OTA
│ └── favicon.ico # Icône du site
├── include/
├── lib/
└── test/
```
**Note**: Tous les fichiers HTML incluent leur CSS inline. Aucun fichier CSS externe n'est nécessaire.
## API REST
### Endpoints authentifiés
#### GET `/api/data` - Données temps réel
Retourne les données de monitoring :
```json
{
"solar_production": 1500.0,
"power_consumption": 800.0,
"heater_power": 75,
"water_temperature": 48.5,
"sunrise_time": "08:32",
"sunset_time": "17:15",
"timestamp": 123456789
}
```
#### POST `/api/data` - Mise à jour manuelle
Envoyer des données manuellement :
```json
{
"solar_production": 1500.0,
"power_consumption": 800.0,
"water_temperature": 48.5
}
```
#### GET `/api/mode` - Lire le mode actuel
```json
{
"mode": 14
}
```
#### POST `/api/mode` - Changer le mode
```json
{
"mode": 14
}
```
#### GET `/api/settings` - Configuration système
Retourne toute la configuration (WiFi, système, Enphase)
#### POST `/api/settings` - Modifier configuration
Envoyer les paramètres à modifier (voir structure dans `config.json`)
### Endpoints publics (sans authentification)
#### GET `/api/espeasy?temperature=48.5`
Endpoint simplifié pour ESPEasy :
```
http://[IP]/api/espeasy?temperature=48.5
```
Retourne: `OK` (200) ou message d'erreur (400)
#### GET `/api/mode?mode=8`
Changer le mode via URL simple :
```
http://[IP]/api/mode?mode=8
```
Retourne: `OK` (200) ou message d'erreur (400)
**Valeurs de mode** :
- `0` = OFF
- `2` = JOUR
- `4` = SOLEIL
- `6` = JOUR+SOLEIL
- `8` = NUIT
- `10` = NUIT+JOUR
- `12` = NUIT+SOLEIL
- `14` = NUIT+SOLEIL+JOUR (défaut)
- `16` = ON
## URLs disponibles
- **http://[IP]/login** - Page de connexion
- **http://[IP]/** - Console principale (protégée)
- **http://[IP]/settings.html** - Configuration (protégée)
- **http://[IP]/update.html** - Interface de mise à jour OTA (protégée)
- **http://[IP]/update.html** - Interface OTA (protégée)
- **http://[IP]/logout** - Déconnexion
**Note**: Toutes les pages sauf `/login` nécessitent une authentification.
**Note**: Toutes les pages sauf `/login` nécessitent une authentification via cookie de session.
## Moniteur série
Pour voir les logs de démarrage:
Pour voir les logs en temps réel:
```bash
platformio device monitor
platformio device monitor --environment esp32-s3-supermini
```
Vous verrez:
- L'adresse IP attribuée
- Le statut de montage de LittleFS
- Les informations de connexion WiFi
- Les événements OTA
**Logs typiques au démarrage:**
```
Démarrage ESP32 - Contrôleur Solaire
LittleFS monté avec succès
GPIO12 configurée pour le chauffe-eau
Configuration d'authentification chargée
Connexion à VotreSSID ........
WiFi connecté!
Adresse IP: 192.168.0.29
Heure actuelle: 14:23:45
[SUN] Lever: 08:32, Coucher: 17:15
ArduinoOTA prêt
Hostname OTA: Routeur solaire
Serveur HTTP AsyncWebServer démarré
Timer PWM initialisé (100Hz)
```
**Logs en fonctionnement:**
```
[ESPEASY] Température reçue: 48.5°C
Enphase - Total: 1500.0W, Panneaux: 800.0W, Net: 700.0W
[MODE] Mode changé via GET: 14
[OTA] Mise à jour démarrée: firmware.bin
[OTA] Espace sketch disponible
[OTA] Mise à jour réussie: 1441792 octets
[OTA] Redémarrage planifié dans 2 secondes...
```
## Intégration ESPEasy
Pour envoyer la température de l'eau depuis un capteur DS18B20 sur ESPEasy :
1. Configurez votre capteur DS18B20 dans ESPEasy
2. Créez une règle pour envoyer la température :
```
on DS18B20#Temperature do
SendToHTTP 192.168.0.29,80,/api/espeasy?temperature=[DS18B20#Temperature]
endon
```
## Calcul des Modes
### Mode JOUR
Calcul progressif pour atteindre `max_water_temp` à l'heure cible :
```
Puissance(%) = 100 × CoeffJour × (TempMax - TempActuelle) / SecondesRestantes
```
- Plus on approche de l'heure cible, plus la puissance augmente
- Si température atteinte avant l'heure, chauffe = 0%
### Mode SOLEIL
Utilise uniquement l'excédent solaire :
```
Puissance(%) = (ProductionSolaire - Consommation) × 100 / PuissanceMaxChauffeEau
```
- Ajustement dynamique en temps réel (toutes les 5s)
- Maximum plafonné à 100%
### Mode NUIT
Chauffe forcée pendant la période configurée :
- Si `TempEau < TempMin` → Puissance = 100%
- Sinon → Puissance = 0%
### Modes Combinés
Prend le **maximum** entre les modes actifs.
Exemple: JOUR+SOLEIL (6) = max(PuissanceJour, PuissanceSoleil)
## Dépannage
### Impossible de se connecter (authentification)
- Vérifiez les identifiants par défaut: `admin` / `password`
- Assurez-vous que le fichier `config.json` a été uploadé avec le filesystem
- Vérifiez les logs série pour confirmer que la configuration a été chargée
- Effacez les cookies de votre navigateur et réessayez
- Connectez-vous au mode AP si aucun WiFi configuré : "Routeur solaire" / `123456`
- Effacez les cookies de votre navigateur
- Vérifiez les logs série pour confirmer que `config.json` a été chargé
### LittleFS ne monte pas
- Assurez-vous d'avoir uploadé le filesystem avec `uploadfs`
- Uploadez le filesystem : `platformio run --target uploadfs --environment esp32-s3-supermini`
- Vérifiez que `board_build.filesystem = littlefs` est dans `platformio.ini`
- Vérifiez la taille de partition dans les logs série
### Impossible de se connecter au WiFi
- Vérifiez les identifiants WiFi dans `main.cpp`
- Vérifiez la force du signal WiFi
### WiFi ne se connecte pas
- En cas d'échec, l'ESP32 démarre automatiquement en mode AP
- SSID par défaut : "Routeur solaire"
- Mot de passe AP : `123456` (modifiable dans `/settings.html`)
- Configurez votre WiFi via l'interface web
- Le système redémarre automatiquement après changement WiFi
### OTA ne fonctionne pas
- Vérifiez que l'ESP et votre ordinateur sont sur le même réseau
- Vérifiez le mot de passe OTA si vous utilisez ArduinoOTA
- Assurez-vous que le port 8266 n'est pas bloqué par un pare-feu
- **Via web** : Vérifiez que vous utilisez les bons fichiers `.bin` depuis `.pio/build/esp32-s3-supermini/`
- **Via ArduinoOTA** : Vérifiez que l'ESP et votre ordinateur sont sur le même réseau
- Mot de passe OTA : `password` (défini dans `main.cpp`)
- Attendez que le message de succès s'affiche (10s pour firmware, 8s pour filesystem)
- Si la page reste bloquée, attendez 15-20s puis rafraîchissez manuellement
### Données Enphase non reçues
- Vérifiez l'IP de la passerelle dans `/settings.html`
- Vérifiez le token JWT (doit être valide)
- Consultez les logs série pour voir les erreurs HTTP
- Intervalle par défaut : 5 secondes
### Température ESPEasy non reçue
- Testez manuellement : `http://[IP]/api/espeasy?temperature=50.5`
- Vérifiez la règle ESPEasy
- Consultez les logs série : `[ESPEASY] Température reçue: XX.X°C`
- Plage acceptée : 0-100°C
### PWM ne fonctionne pas
- Vérifiez la connexion sur GPIO12
- Le PWM est logiciel à 1Hz (1 seconde de période)
- Consultez les logs pour voir `heaterPower` calculé
- Timer fonctionne à 100Hz (interruption toutes les 10ms)
## Caractéristiques Techniques
- **Plateforme** : ESP32-S3 (espressif32)
- **Framework** : Arduino
- **Fréquence CPU** : 240 MHz
- **Flash** : 4 MB
- **PSRAM** : 2 MB
- **Filesystem** : LittleFS
- **Serveur Web** : ESPAsyncWebServer (asynchrone)
- **PWM** : Logiciel 1 Hz (période 1000ms) via timer hardware 100Hz
- **Contrôle** : GPIO12 (sortie digitale)
- **Sécurité** : SHA-256 pour authentification, sessions avec timeout
- **OTA** : Web + ArduinoOTA (port série et réseau)
## Bibliothèques utilisées
```ini
lib_deps =
esphome/ESPAsyncWebServer-esphome@^3.1.0
bblanchon/ArduinoJson@^7.2.1
me-no-dev/AsyncTCP@^1.4.0
```
## Performances
### Style CSS
- Tous les styles sont désormais inclus directement dans chaque fichier HTML (`<style>...</style>`).
- Le fichier `style.css` a été supprimé, il n'est plus nécessaire.
- **Mémoire** : ~150KB RAM utilisée (avec PSRAM disponible)
- **CPU** : <5% en idle, ~20% pendant fetch Enphase
- **Réseau** : Latence <50ms pour API REST
- **PWM** : Précision ±10ms (timer 100Hz)
- **Update rate** : Enphase configurable (défaut 5s), calcul PWM 1s
## Contributeurs
Projet développé par C SCAYA avec l'assistance de GitHub Copilot (Claude Sonnet AI).
## Licence
MIT
MIT License - Libre d'utilisation, modification et distribution.
---
**Version**: 1.0.0
**Date**: Janvier 2026
**Compatibilité**: ESP32-S3, Arduino Framework, PlatformIO

BIN
data/favicon.ico

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

4
data/update.html

@ -217,7 +217,7 @@ @@ -217,7 +217,7 @@
showMessage('success', 'firmwareMessage', 'Firmware mis à jour avec succès! Redémarrage en cours...');
setTimeout(() => {
window.location.href = '/index.html';
}, 5000);
}, 10000);
} else {
showMessage('error', 'firmwareMessage', 'Erreur lors de la mise à jour: ' + xhr.responseText);
}
@ -264,7 +264,7 @@ @@ -264,7 +264,7 @@
showMessage('success', 'filesystemMessage', 'Filesystem mis à jour avec succès! Redémarrage en cours...');
setTimeout(() => {
window.location.href = '/index.html';
}, 5000);
}, 10000);
} else {
showMessage('error', 'filesystemMessage', 'Erreur lors de la mise à jour: ' + xhr.responseText);
}

23
platformio.ini

@ -12,4 +12,25 @@ lib_deps = @@ -12,4 +12,25 @@ lib_deps =
;upload_protocol = espota
;upload_port = 192.168.0.28
;upload_flags =
; --auth=password
; --auth=password
[env:esp32-s3-supermini]
platform = espressif32
board = esp32-s3-devkitc-1
framework = arduino
board_upload.flash_size = 4MB
board_build.filesystem = littlefs
board_build.partitions = default.csv
monitor_speed = 115200
lib_deps =
https://github.com/me-no-dev/ESPAsyncWebServer.git
https://github.com/me-no-dev/AsyncTCP.git
bblanchon/ArduinoJson@^7.2.1
build_flags =
-DARDUINO_USB_CDC_ON_BOOT=1
-DBOARD_HAS_PSRAM
; Configuration ArduinoOTA pour firmware et filesystem
; upload_protocol = espota
; upload_port = 192.168.0.29
; upload_flags =
; --auth=password

47
src/main.cpp

@ -59,6 +59,10 @@ float waterTemperature = 0.0; // Température de l'eau du chauffe-eau en °C @@ -59,6 +59,10 @@ float waterTemperature = 0.0; // Température de l'eau du chauffe-eau en °C
// Configuration GPIO
#define HEATER_GPIO 12 // GPIO12 pour contrôle chauffe-eau
// Variables pour la gestion OTA
bool otaRebootPending = false;
unsigned long otaRebootTime = 0;
// Variables pour la gestion PWM avec timer (période 1s)
const unsigned long PWM_PERIOD = 1000; // Période de 1 seconde en ms
volatile unsigned long pwmCycleCount = 0; // Compteur de cycles PWM
@ -520,10 +524,10 @@ void calculateHeaterPower() { @@ -520,10 +524,10 @@ void calculateHeaterPower() {
// Calculer les secondes restantes
int secondsRemaining = targetTimeInSeconds - currentTimeInSeconds;
// Si on est après l'heure cible ou à l'heure cible, calculer jusqu'au lendemain
if (secondsRemaining <= 0) {
secondsRemaining = 86400 + secondsRemaining; // 86400 = 24h en secondes
}
// // Si on est après l'heure cible ou à l'heure cible, calculer jusqu'au lendemain
// if (secondsRemaining < 0) {
// secondsRemaining = 86400 + secondsRemaining; // 86400 = 24h en secondes
// }
float tempDiff = maxWaterTemp - waterTemperature;
@ -1259,22 +1263,7 @@ void setup() { @@ -1259,22 +1263,7 @@ void setup() {
});
// Route POST /update - Mise à jour OTA (firmware ou filesystem)
server.on("/update", HTTP_POST,
[](AsyncWebServerRequest *request) {
// Handler appelé après la fin de l'upload
bool shouldReboot = !Update.hasError();
if (Update.hasError()) {
request->send(500, "text/plain", "Erreur lors de la mise à jour");
} else {
request->send(200, "text/plain", "Mise à jour réussie! Redémarrage...");
}
if (shouldReboot) {
delay(1000);
ESP.restart();
}
},
server.on("/update", HTTP_POST,NULL,
[](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
// Handler appelé pendant l'upload des données
if (index == 0) {
@ -1317,8 +1306,20 @@ void setup() { @@ -1317,8 +1306,20 @@ void setup() {
if (final) {
if (Update.end(true)) {
Serial.printf("[OTA] Mise à jour réussie: %u octets\n", index + len);
// Envoyer la réponse au client
AsyncWebServerResponse *response = request->beginResponse(200, "text/plain", "Mise à jour réussie! Redémarrage...");
response->addHeader("Connection", "close");
response->addHeader("Content-Length", "38");
request->send(response);
// Planifier le redémarrage dans 2 secondes (géré dans loop)
otaRebootPending = true;
otaRebootTime = millis() + 2000;
Serial.println("[OTA] Redémarrage planifié dans 2 secondes...");
} else {
Update.printError(Serial);
request->send(500, "text/plain", "Erreur lors de la mise à jour");
}
}
});
@ -1381,6 +1382,12 @@ void setup() { @@ -1381,6 +1382,12 @@ void setup() {
}
void loop() {
// Vérifier si un redémarrage OTA est en attente
if (otaRebootPending && millis() >= otaRebootTime) {
Serial.println("[OTA] Redémarrage maintenant...");
ESP.restart();
}
// Gérer les requêtes ArduinoOTA
ArduinoOTA.handle();

Loading…
Cancel
Save