Table des Matières
- Introduction
- Architecture et Concepts
- Installation et Configuration
- API Référence
- Cas d’Utilisation DIY
- Dépannage
- Bonnes Pratiques
1. Introduction
Qu’est-ce que SchreinCloudParser ?
SchreinCloudParser est une bibliothèque C++ optimisée pour les microcontrôleurs ESP8266 et ESP32 permettant la communication bidirectionnelle avec le cloud Schrein. Elle gère automatiquement les connexions WiFi, le parsing des commandes, l’envoi de télémétrie et inclut des fonctionnalités avancées d’économie d’énergie.

Installation de la Bibliothèque
- Téléchargez les fichiers
.h
et.cpp
- Créez un dossier
SchreinCloudParser
dans votre dossierlibraries/Arduino
- Copiez les fichiers dans ce dossier
- Redémarrez l’IDE Arduino
Caractéristiques Principales
- Communication SSE (Server-Sent Events) pour la réception de commandes
- Envoi de télémétrie sécurisé avec authentification Bearer
- Gestion automatique WiFi avec reconnexion intelligente
- Mode économie d’énergie avec deep sleep intégré
- Timeout adaptatif selon les conditions réseau
- Système de checksum pour l’intégrité des données
- Callbacks extensibles pour une intégration flexible
2. Architecture et Concepts
Diagramme de Flux

Format des Trames
//Envoi-From Desktop Application to ESP
// Format sécurisé
<[control|key|value]&checksum>
// Exemple concret
<[temperature|sensor1|23.5]&4F>
// Format no sécurisé
<[control|key|value]>
// Exemple concret
<[temperature|sensor1|23.5]>
//Envoi-From ESP to desktop application
// Format sécurisé
controlName=property:value;&checksum
// Exemple concret
SchreinLabel1=Text:23.5;*4F
// Format no sécurisé
controlName=property:value;
// Exemple concret
SchreinLabel1=Text:23.5;
États du Parser
enum class ParserState {
WAITING_START, // En attente du début de trame
READING_FRAME // Lecture en cours
};
3. Installation et Configuration
Installation
// Dans PlatformIO, ajouter au platformio.ini
lib_deps =
https://github.com/SchreikenLab/SchreinCloudParser.git
Inclusion dans le projet
#include <SchreinCloudParser.h>
Configuration Initiale
// Définir les informations de connexion
const String SERVER = "votre-serveur.schrein.cloud";
const String TOKEN = "votre-token-bearer";
const String DEVICE_ID = "votre-device-id";
// Initialiser le parser
SchreinCloudParser parser(SERVER, TOKEN, DEVICE_ID);
Configuration WiFi
void setup() {
Serial.begin(115200);
// Connexion WiFi
if (parser.begin("SSID_WIFI", "PASSWORD_WIFI")) {
Serial.println("✅ Device prêt!");
} else {
Serial.println("❌ Échec connexion WiFi");
}
// Configurer les callbacks
setupCallbacks();
}
4. API Référence
Méthodes Principales
begin(ssid, password)
Initialise la connexion WiFi.
bool success = parser.begin("MonWiFi", "MonPassword");
loop()
Boucle principale à appeler régulièrement.
void loop() {
parser.loop();
if (parser.isFrameAvailable()) {
traiterCommande();
}
}
sendFrame(frame)
Envoie une trame de télémétrie.
String trame = "temperature=value:23.5;";
parser.sendFrame(trame);
getValue(controlName, key)
Récupère une valeur parsée.
String valeur = parser.getValue("relay", "state");
Gestion des Commandes
command(controlName, propertyName, value)
Génère une commande formatée.
String cmd = parser.command("pump", "duration", "5000");
// Résultat: "pump=duration:5000;"
sendFrames(frames[], count)
Envoie multiple trames.
String trames[] = {
parser.command("sensor1", "temp", "22.1"),
parser.command("sensor2", "humidity", "45")
};
parser.sendFrames(trames, 2);
Gestion Énergétique
setPowerSaving(enabled)
Active/désactive l’économie d’énergie.
parser.setPowerSaving(true); // Mode basse consommation
setDeepSleepDuration(seconds)
Configure la durée du deep sleep.
parser.setDeepSleepDuration(300); // 5 minutes
Callbacks Disponibles
// Configuration complète des callbacks
void setupCallbacks() {
parser.onFrameParsed([](String control, String key, String value) {
Serial.printf("📨 Commande: %s.%s = %s\n",
control.c_str(), key.c_str(), value.c_str());
});
parser.onError([](String error) {
Serial.println("❌ Erreur: " + error);
});
parser.onConnectionStatusChange([](bool connected) {
Serial.println(connected ? "🔗 Connecté" : "🔌 Déconnecté");
});
parser.onWiFiSignalUpdate([](int rssi) {
Serial.printf("📶 Signal WiFi: %d dBm\n", rssi);
});
}
5. Cas d’Utilisation DIY
🏠 1. Station Météo Intelligente
Objectif: Mesurer température/humidité et envoyer les données au cloud.
#include <SchreinCloudParser.h>
#include <DHT.h>
#define DHT_PIN 4
#define DHT_TYPE DHT22
SchreinCloudParser parser("serveur.schrein.cloud", "token", "station-meteo");
DHT dht(DHT_PIN, DHT_TYPE);
void setup() {
Serial.begin(115200);
dht.begin();
parser.begin("WiFi_Maison", "password");
parser.onFrameParsed(handleCommand);
}
void loop() {
parser.loop();
static unsigned long lastRead = 0;
if (millis() - lastRead > 30000) { // Toutes les 30s
envoyerDonneesCapteurs();
lastRead = millis();
}
}
void envoyerDonneesCapteurs() {
float temp = dht.readTemperature();
float hum = dht.readHumidity();
if (!isnan(temp) && !isnan(hum)) {
String trameTemp = parser.command("dht22", "temperature", String(temp));
String trameHum = parser.command("dht22", "humidity", String(hum));
String trames[] = {trameTemp, trameHum};
parser.sendFrames(trames, 2);
Serial.printf("📊 Envoyé: %.1f°C, %.1f%%\n", temp, hum);
}
}
void handleCommand(String control, String key, String value) {
if (control == "display" && key == "refresh") {
envoyerDonneesCapteurs(); // Rafraîchissement à la demande
}
}
💡 2. Contrôleur d’Éclairage Domotique
Objectif: Contrôler des relais/LED via le cloud.
#include <SchreinCloudParser.h>
SchreinCloudParser parser("serveur.schrein.cloud", "token", "eclairage-salon");
const int RELAY_PIN = 5;
bool etatRelay = false;
void setup() {
pinMode(RELAY_PIN, OUTPUT);
parser.begin("WiFi_Maison", "password");
parser.onFrameParsed(handleLightCommand);
parser.onConnectionStatusChange(handleConnectionChange);
}
void loop() {
parser.loop();
}
void handleLightCommand(String control, String key, String value) {
if (control == "light" && key == "state") {
etatRelay = (value == "on" || value == "1");
digitalWrite(RELAY_PIN, etatRelay ? HIGH : LOW);
// Confirmation
String confirmation = parser.command("light", "state", etatRelay ? "on" : "off");
parser.sendFrame(confirmation);
Serial.println("💡 Lumière: " + String(etatRelay ? "ON" : "OFF"));
}
}
void handleConnectionChange(bool connected) {
// Clignoter la LED en cas de déconnexion
if (!connected) {
for (int i = 0; i < 3; i++) {
digitalWrite(LED_BUILTIN, HIGH);
delay(200);
digitalWrite(LED_BUILTIN, LOW);
delay(200);
}
}
}
🌿 3. Système d’Arrosage Automatique
Objectif: Gérer l’arrosage basé sur l’humidité du sol.
#include <SchreinCloudParser.h>
SchreinCloudParser parser("serveur.schrein.cloud", "token", "arrosage-jardin");
const int SOIL_SENSOR = A0;
const int WATER_PUMP = 12;
const int SOIL_THRESHOLD = 500;
void setup() {
pinMode(WATER_PUMP, OUTPUT);
digitalWrite(WATER_PUMP, LOW);
parser.begin("WiFi_Jardin", "password");
parser.onFrameParsed(handleWaterCommand);
parser.setPowerSaving(true); // Économie d'énergie
}
void loop() {
parser.loop();
static unsigned long lastCheck = 0;
if (millis() - lastCheck > 60000) { // Toutes les minutes
gererArrosageAutomatique();
lastCheck = millis();
}
}
void gererArrosageAutomatique() {
int humiditeSol = analogRead(SOIL_SENSOR);
// Envoyer l'humidité
String trameHumidite = parser.command("soil", "moisture", String(humiditeSol));
parser.sendFrame(trameHumidite);
// Arrosage automatique si trop sec
if (humiditeSol > SOIL_THRESHOLD) {
declencherArrosage(5000); // 5 secondes
}
}
void handleWaterCommand(String control, String key, String value) {
if (control == "pump" && key == "duration") {
int duree = value.toInt();
declencherArrosage(duree);
}
}
void declencherArrosage(int dureeMs) {
digitalWrite(WATER_PUMP, HIGH);
String trame = parser.command("pump", "state", "active");
parser.sendFrame(trame);
delay(dureeMs);
digitalWrite(WATER_PUMP, LOW);
trame = parser.command("pump", "state", "inactive");
parser.sendFrame(trame);
Serial.println("💦 Arrosage: " + String(dureeMs) + "ms");
}
🔒 4. Système de Sécurité
Objectif: Surveillance avec capteurs PIR et alertes.
#include <SchreinCloudParser.h>
SchreinCloudParser parser("serveur.schrein.cloud", "token", "alarme-maison");
const int PIR_PIN = 14;
const int BUZZER_PIN = 15;
const int LED_ALERTE = 13;
bool alarmeActive = false;
unsigned long derniereDetection = 0;
void setup() {
pinMode(PIR_PIN, INPUT);
pinMode(BUZZER_PIN, OUTPUT);
pinMode(LED_ALERTE, OUTPUT);
parser.begin("WiFi_Securite", "password");
parser.onFrameParsed(handleSecurityCommand);
Serial.println("🚨 Système de sécurité initialisé");
}
void loop() {
parser.loop();
// Détection mouvement
if (alarmeActive && digitalRead(PIR_PIN) == HIGH) {
if (millis() - derniereDetection > 10000) { // Anti-rebond 10s
declencherAlerte();
derniereDetection = millis();
}
}
}
void handleSecurityCommand(String control, String key, String value) {
if (control == "alarm" && key == "state") {
alarmeActive = (value == "armed");
String statut = parser.command("alarm", "status", alarmeActive ? "armed" : "disarmed");
parser.sendFrame(statut);
Serial.println("🚨 Alarme: " + String(alarmeActive ? "ACTIVÉE" : "DÉSACTIVÉE"));
}
}
void declencherAlerte() {
// Sonner le buzzer
tone(BUZZER_PIN, 1000, 2000);
// Clignoter LED
for (int i = 0; i < 10; i++) {
digitalWrite(LED_ALERTE, HIGH);
delay(200);
digitalWrite(LED_ALERTE, LOW);
delay(200);
}
// Envoyer alerte
String alerte = parser.command("security", "alert", "motion_detected");
parser.sendFrame(alerte);
Serial.println("⚠️ Mouvement détecté! Alerte envoyée.");
}
🔋 5. Moniteur de Batterie Solaire
Objectif: Surveiller une installation solaire autonome.
#include <SchreinCloudParser.h>
SchreinCloudParser parser("serveur.schrein.cloud", "token", "solaire-maison");
const int VOLTAGE_PIN = A0;
const int CURRENT_PIN = A1;
const int CHARGE_RELAY = 16;
float tensionBatterie = 0;
float courantCharge = 0;
void setup() {
pinMode(CHARGE_RELAY, OUTPUT);
parser.begin("WiFi_Solaire", "password");
parser.setPowerSaving(true);
parser.setDeepSleepDuration(300); // Deep sleep 5min entre mesures
parser.onFrameParsed(handleSolarCommand);
}
void loop() {
parser.loop();
static unsigned long lastMeasurement = 0;
if (millis() - lastMeasurement > 30000) { // Mesure toutes les 30s
mesurerParametres();
envoyerDonneesSolaire();
lastMeasurement = millis();
// Deep sleep en mode très basse consommation
if (parser.isPowerSavingEnabled() && tensionBatterie < 11.5) {
Serial.println("🔋 Batterie faible - Deep sleep activé");
parser.enterDeepSleep(); // La librairie gère le wakeup automatique
}
}
}
void mesurerParametres() {
// Lecture tension (diviseur de tension)
int rawVoltage = analogRead(VOLTAGE_PIN);
tensionBatterie = (rawVoltage / 1023.0) * 5.0 * 3; // Conversion selon circuit
// Lecture courant (capteur ACS712)
int rawCurrent = analogRead(CURRENT_PIN);
courantCharge = ((rawCurrent - 512) / 1023.0) * 5.0; // Ajuster selon calibration
}
void envoyerDonneesSolaire() {
String trames[] = {
parser.command("battery", "voltage", String(tensionBatterie, 2)),
parser.command("solar", "current", String(courantCharge, 2)),
parser.command("system", "power_mode", parser.isPowerSavingEnabled() ? "low_power" : "normal")
};
parser.sendFrames(trames, 3);
Serial.printf("☀️ Solaire: %.2fV, %.2fA\n", tensionBatterie, courantCharge);
}
void handleSolarCommand(String control, String key, String value) {
if (control == "charge" && key == "enable") {
bool enableCharge = (value == "true" || value == "1");
digitalWrite(CHARGE_RELAY, enableCharge ? HIGH : LOW);
String statut = parser.command("charge", "status", enableCharge ? "enabled" : "disabled");
parser.sendFrame(statut);
}
}
6. Dépannage
Diagnostic Complet
void diagnosticComplet() {
Serial.println("\n=== DIAGNOSTIC SCHREIN CLOUD ===");
// Statut connexion
Serial.println("📡 " + parser.getConnectionStatus());
// Timeout actuel
Serial.println("⏱ Timeout: " + String(parser.getCurrentTimeout()) + "ms");
// Statistiques d'erreur
Serial.println("❌ Erreurs consécutives: " + String(parser.getConsecutiveErrors()));
Serial.println("⏰ Timeouts consécutifs: " + String(parser.getConsecutiveTimeouts()));
// Dernière commande
Serial.println("🕐 Âge dernière commande: " + String(parser.getLastCommandAge()) + "ms");
// Mode économie
Serial.println("🔋 Mode économie: " + String(parser.isPowerSavingEnabled() ? "ACTIF" : "INACTIF"));
}
// Appeler cette fonction quand besoin
diagnosticComplet();
Codes d’Erreur Courants
void handleErrors(ErrorCode code, String message) {
switch(code) {
case ErrorCode::TIMEOUT:
Serial.println("⏰ Timeout - Vérifiez la connexion réseau");
break;
case ErrorCode::INVALID_FRAME:
Serial.println("📦 Format de trame invalide");
break;
case ErrorCode::CHECKSUM_ERROR:
Serial.println("🔍 Erreur checksum - Données corrompues");
break;
case ErrorCode::BUFFER_OVERFLOW:
Serial.println("💥 Buffer overflow - Trame trop longue");
break;
default:
Serial.println("❌ Erreur inconnue: " + message);
}
}
// Enregistrer le callback
parser.onErrorCode(handleErrors);
Optimisation des Performances
void optimiserPerformance() {
// Réduire timeout si connexion stable
if (parser.getConsecutiveErrors() == 0) {
Serial.println("✅ Connexion stable - Optimisation possible");
}
// Activer économie si batterie
if (estSurBatterie()) {
parser.setPowerSaving(true);
parser.setDeepSleepDuration(600); // 10 minutes
}
// Désactiver checksum si besoin de performance
if (besoinDebitMaximal) {
parser.enableChecksum(false);
}
}
💡 Bonnes Pratiques
1. Gestion de la Mémoire
// Éviter les String dynamiques dans les callbacks
void handleFrame(String control, String key, String value) {
// MAUVAIS: Création de String dynamiques
// String message = "Commande: " + control + "." + key + " = " + value;
// BON: Utiliser printf ou traitement direct
Serial.printf("📨 %s.%s = %s\n", control.c_str(), key.c_str(), value.c_str());
// Traitement immédiat sans allocation
if (control == "relay" && key == "state") {
digitalWrite(RELAY_PIN, value == "on" ? HIGH : LOW);
}
}
2. Gestion des Déconnexions
void robustLoop() {
static unsigned long lastReconnect = 0;
parser.loop();
// Reconnexion forcée périodique
if (millis() - lastReconnect > 300000) { // 5 minutes
if (!parser.isConnectionHealthy()) {
Serial.println("🔄 Reconnexion proactive...");
parser.forceReconnect();
}
lastReconnect = millis();
}
}
3. Logging Intelligent
class Logger {
public:
static void debug(String message) {
#ifdef DEBUG
Serial.println("[DEBUG] " + message);
#endif
}
static void error(String message) {
Serial.println("[ERROR] " + message);
// Envoyer l'erreur au cloud
// parser.sendFrame(parser.command("system", "error", message));
}
};
// Utilisation
Logger::debug("Trame reçue: " + trame);
Logger::error("Timeout connexion");
4. Sécurité
void setupSecurite() {
// Validation des entrées
parser.onFrameParsed([](String control, String key, String value) {
if (control.length() > 50 || key.length() > 50 || value.length() > 100) {
Logger::error("Trame suspecte - taille excessive");
return;
}
// Whitelist des commandes autorisées
String commandesAutorisees[] = {"light", "pump", "sensor", "alarm"};
bool autorise = false;
for (auto& cmd : commandesAutorisees) {
if (control == cmd) {
autorise = true;
break;
}
}
if (!autorise) {
Logger::error("Commande non autorisée: " + control);
return;
}
traiterCommande(control, key, value);
});
}
🎯 Conclusion
La bibliothèque SchreinCloudParser offre une solution complète pour connecter vos projets ESP8266/ESP32 au cloud Schrein. Sa conception robuste avec gestion automatique des connexions, économie d’énergie intégrée et système de callbacks extensible la rend adaptée à une large gamme de projets DIY.
Les 5 cas d’utilisation présentés couvrent les scénarios les plus courants et peuvent servir de base à vos propres créations. N’hésitez pas à adapter le code selon vos besoins spécifiques et à explorer les fonctionnalités avancées comme le timeout adaptatif et le système de checksum.
Prochaines Étapes:
- Commencez avec le cas d’utilisation le plus proche de votre projet
- Testez la connexion avec les outils de diagnostic
- Ajoutez progressivement les fonctionnalités avancées
- Optimisez selon vos contraintes énergétiques
Bonne création ! 🚀