Initial commit — KC868-A2 contrôleur solaire ESP32

Fonctionnalités :
- Lecture RS485 Modbus Epever Tracer 4210N (115200 bps, FC03/FC04/FC16)
- Moteur de règles JSON (LittleFS) — commande automatique des relais
- Interface web mobile-first (dashboard, règles, config, historique, EPEVER, debug)
- WiFi AP+STA simultanés avec reconnexion automatique et portail captif
- mDNS configurable (pv.local par défaut)
- Configuration registres EPEVER depuis l'UI (18 registres holding)
- Historique basse/haute résolution avec graphes canvas
- VPN WireGuard optionnel (désactivé par défaut, config via UI)
- OTA firmware + filesystem via ElegantOTA
- Deep sleep / économie d'énergie

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-09 19:25:01 +02:00
commit a8f0d6ccba
88 changed files with 13162 additions and 0 deletions
+37
View File
@@ -0,0 +1,37 @@
This directory is intended for project header files.
A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.
```src/main.c
#include "header.h"
int main (void)
{
...
}
```
Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.
In C, the convention is to give header files names that end with `.h'.
Read more about using header files in official GCC documentation:
* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
+4
View File
@@ -0,0 +1,4 @@
#pragma once
void initBoutons();
void gererBoutons();
+33
View File
@@ -0,0 +1,33 @@
#pragma once
#include <IPAddress.h>
// --- WiFi point d'accès ---
#define WIFI_SSID "kc868-a2"
#define WIFI_PASSWORD "soleil12" // mot de passe WiFi AP
#define WIFI_IP IPAddress(192, 168, 4, 1)
#define WIFI_GATEWAY IPAddress(192, 168, 4, 1)
#define WIFI_SUBNET IPAddress(255, 255, 255, 0)
// --- GPIO ---
#define PIN_RELAY1 15
#define PIN_RELAY2 2 // pin de strapping boot — doit être HIGH au démarrage
#define PIN_RS485_TX 32
#define PIN_RS485_RX 35 // input only
#define PIN_DI1 36 // input only
#define PIN_DI2 39 // input only
// --- OTA ---
#define OTA_USER "admin"
#define OTA_PASSWORD "solar123"
// --- Modbus ---
#define MODBUS_ADRESSE 1 // adresse esclave Epever
#define MODBUS_BAUDRATE 115200 // baudrate principal de l'Epever
#define TIMEOUT_MODBUS 3000 // timeout réponse (ms) — doit être > délai interne lib (1s)
#define MODBUS_DEBUG_BOOT 1 // sonde RS485 détaillée au démarrage
#define MODBUS_DEBUG_RX_MAX 64 // octets max affichés en cas d'erreur
// --- Intervalles (ms) ---
#define INTERVALLE_MODBUS 5000
#define INTERVALLE_REGLES 1000
#define DEBOUNCE_BOUTON 50
+7
View File
@@ -0,0 +1,7 @@
#pragma once
#include <Arduino.h>
void debugLogLine(const char *message);
void debugLogf(const char *format, ...);
void getDebugLogJson(String &out);
void clearDebugLog();
+33
View File
@@ -0,0 +1,33 @@
#pragma once
#include <stdint.h>
#include <Arduino.h>
#include <ArduinoJson.h>
struct EpeverRegDef {
uint16_t reg;
const char *key;
float scale; // raw→float : 0.01 pour les tensions, 1.0 pour les entiers
float valMin;
float valMax;
bool writable;
const char *unit;
const char *label;
const char *aide;
};
extern const EpeverRegDef EPEVER_REGS[];
extern const uint8_t EPEVER_REGS_COUNT;
// Blocs de registres consécutifs
#define EPEVER_BLOC1_REG 0x9000u
#define EPEVER_BLOC1_QTY 14u
#define EPEVER_BLOC2_REG 0x906Bu
#define EPEVER_BLOC2_QTY 4u
void initConfigEpever();
bool lireConfigEpever();
bool ecrireConfigEpever(JsonObject obj, String &erreur);
bool sauvegarderConfigJson();
bool restaurerConfigJson(String &erreur);
void getConfigJson(String &out);
void getConfigSauvegardeJson(String &out);
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include <Arduino.h>
void initHistorique();
void gererHistorique();
void getHistoriqueJson(String &out); // lores : 30h, point toutes les 5 min
void getHistoriqueHiresJson(String &out); // hires : 4h, point toutes les 1 min
void getHistoriqueCsv(String &out); // export CSV lores pour téléchargement
void getHistoriqueStatusJson(String &out); // debug compteurs internes
+16
View File
@@ -0,0 +1,16 @@
#pragma once
#include <stdint.h>
void initModbus();
void gererModbus();
void setIntervallesModbus(uint32_t jour_ms, uint32_t nuit_ms);
void getIntervallesModbus(uint32_t &jour_ms, uint32_t &nuit_ms);
bool reglerHorlogeEpever(uint16_t annee, uint8_t mois, uint8_t jour,
uint8_t heure, uint8_t minute, uint8_t seconde);
// Accès Modbus partagé (pour epever_config.cpp)
bool isModbusBusy();
bool modbusAcquire();
void modbusRelease();
bool modbusLireHolding(uint16_t reg, uint16_t qty, uint16_t *dest, uint16_t timeoutMs);
bool modbusEcrireHolding(uint16_t reg, uint16_t qty, const uint16_t *vals, uint16_t timeoutMs);
+4
View File
@@ -0,0 +1,4 @@
#pragma once
void demarrerOTA();
void gererOTA();
+10
View File
@@ -0,0 +1,10 @@
#pragma once
#include <ArduinoJson.h>
void initRegles();
void gererRegles();
void reglesToJson(JsonArray arr);
bool ajouterRegle(JsonObject obj);
bool supprimerRegle(int id);
bool toggleRegle(int id);
+18
View File
@@ -0,0 +1,18 @@
#pragma once
#include <Arduino.h>
// Appelé au tout début de setup() — entre en deep sleep si réveil timer + nuit
void verifierEtDormirSiNuit();
// Appelé après montage de LittleFS
void chargerConfigSleep();
// Restaure les relais depuis la mémoire RTC après un réveil
void restaurerRelais();
// Appelé dans loop()
void gererSleep();
// API REST
void getSleepConfigJson(String &out);
bool setSleepConfig(bool actif, uint32_t intervalle, float seuil);
+61
View File
@@ -0,0 +1,61 @@
#pragma once
struct SystemState {
// --- PV ---
float pv = 0.0f; // Tension PV (V)
float pvCurrent = 0.0f; // Courant PV (A)
// --- Batterie ---
float battery = 0.0f; // Tension (V)
float batTemperature = 0.0f; // Température (°C)
uint8_t batSOC = 0; // Charge restante (%)
uint8_t batStatut = 0; // 0=arrêt 1=float 2=boost 3=égalisation
bool batSousVoltage = false;
bool batSurVoltage = false;
// --- Sortie de charge (load) ---
float loadVoltage = 0.0f; // Tension (V)
float loadCurrent = 0.0f; // Courant (A)
float loadPower = 0.0f; // Puissance (W)
// --- Énergie (kWh, calculées par l'Epever) ---
float energieGenJour = 0.0f; // Générée aujourd'hui
float energieGenTotal = 0.0f; // Générée total
float energieConJour = 0.0f; // Consommée aujourd'hui
float energieConTotal = 0.0f; // Consommée total
// --- Ensoleillement ---
bool sun = false; // true = jour
bool sunHistoryValid = false;
uint8_t sunHistoryCount = 0;
uint8_t sunHistoryHead = 0;
bool sunHistoryState[5] = {};
char sunHistoryTime[5][20] = {};
// --- Horloge interne Epever ---
bool epeverClockOk = false;
uint8_t epeverSecond = 0;
uint8_t epeverMinute = 0;
uint8_t epeverHour = 0;
uint8_t epeverDay = 0;
uint8_t epeverMonth = 0;
uint16_t epeverYear = 0;
bool espClockOk = false;
// --- Relais ---
bool relay1 = false;
bool relay2 = false;
// --- Boutons DI ---
bool di1 = false;
bool di2 = false;
// --- Mode ---
bool autoMode = true; // true = automatique, false = manuel
// --- Santé RS485 ---
bool rs485_ok = false;
unsigned long last_update = 0;
};
extern SystemState state;
+7
View File
@@ -0,0 +1,7 @@
#pragma once
#include <ESPAsyncWebServer.h>
extern AsyncWebServer server;
void demarrerWebserveur();
void restaurerRelaisNVS();
+11
View File
@@ -0,0 +1,11 @@
#pragma once
#include <Arduino.h>
void demarrerWifi();
void gererWifi(); // boucle principale (DNS + reconnexion STA)
void connecterWifiSTA(const char *ssid, const char *pass); // sauvegarde NVS + connexion
void deconnecterWifiSTA(); // efface NVS + déconnecte STA
void getWifiStatusJson(String &out); // statut AP + STA en JSON
void scannerReseauxJson(String &out); // scan synchrone → JSON
String getMdnsHostname(); // hostname mDNS courant
bool setMdnsHostname(const char *nom); // change hostname + sauvegarde NVS
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include <Arduino.h>
void initWireGuard();
void gererWireGuard();
void getWireGuardJson(String &out);
bool setWireGuardConfig(bool enabled, const char* privKey, const char* pubKey,
const char* psk, const char* endpoint, uint16_t port,
const char* localIP, uint16_t keepalive);