New Technology Design

SchreinSerialPaser

1. Introduction

SchreinSerialParser est une bibliothèque Arduino professionnelle dédiée à la communication série pour les projets IoT et embarqués. Elle permet de recevoir, parser et envoyer des trames structurées entre votre carte Arduino/ESP et une interface de supervision (comme Schrein UI Builder).

1.1. Caractéristiques techniques

1.2. Avantages

✅ Zéro allocation dynamique – Pas de new/malloc, sûr pour les systèmes critiques
✅ Non-bloquant – La fonction loop() ne bloque jamais l’exécution
✅ File circulaire – Jusqu’à 4 trames en attente
✅ Checksum intégré – Détection d’erreurs optionnelle
✅ Callbacks – Programmation événementielle
✅ Statistiques – Monitoring des performances


2. Installation

2.1. Installation depuis l’IDE Arduino

  1. Téléchargez les fichiers .h et .cpp
  2. Créez un dossier SchreinSerialParser dans votre dossier libraries/Arduino
  3. Copiez les fichiers dans ce dossier
  4. Redémarrez l’IDE Arduino

2.2. Structure des fichiers

SchreinSerialParser/
├── examples/
│   └── Basic_Usage
│       └── Basic_Usage.ino
├── src/
│   ├── SchreinSerialParser.h
│   └── SchreinSerialParser.cpp



├── README.txt
├── library.properties
└── keywords.txt


3. Format de trame

La bibliothèque utilise un format de trame simple, robuste et compatible avec Schrein UI Builder.

3.1. Format standard (sans checksum)

3.1.1. De Schrein UI BuilderCarte embarquée

Format : <[CONTROL|KEY|VALUE]>

Exemples :

// Mettre à jour une jauge de température
<[gaugeTemperature|value|24.5]>
// Changer la couleur d'un texte
<[labelStatus|color|#00FF00]>

// Allumer/éteindre une LED
<[ledRouge|state|1]>

// Définir l'angle d'un servo-moteur
<[servoMoteur|angle|90]>

// Envoyer une commande à un bouton
<[btnStart|click|1]>

// Contrôler une prise connectée
<[priseSalon|power|ON]>

3.1.2. De la carte embarquéeSchrein UI Builder

Format : CONTROL=KEY:VALUE;

Exemples :

// Envoyer une température depuis un capteur DHT
gaugeTemperature=value:24.5;

// Envoyer l'état d'un capteur de présence
capteurPresence=state:1;

// Envoyer une pression
gaugePression=value:1013.25;

// Envoyer une luminosité
gaugeLuminosite=value:450;

// Envoyer un texte de log
console=message:"Capteur démarré";

// Envoyer une alerte
alerte=message:"Température élevée"

3.2. Format avec checksum (sécurisé)

Le checksum est un XOR sur l’ensemble des caractères du corps de la trame (entre [ et ] inclus, ou entre = et ; inclus).

3.2.1. De Schrein UI BuilderCarte embarquée

Format : <[CONTROL|KEY|VALUE]&XX>

XX représente le checksum en hexadécimal (2 caractères).

Exemples :

// Avec checksum
<[gaugeTemperature|value|24.5]&3F>

// Changer couleur avec checksum
<[labelStatus|color|#00FF00]&5A>

// Allumer LED avec checksum
<[ledRouge|state|1]&2C>

// Commande servo avec checksum
<[servoMoteur|angle|90]&1E>

// Contrôle prise avec checksum
<[priseSalon|power|ON]&4B>

3.2. De la carte embarquée Schrein UI Builder

Format : CONTROL=KEY:VALUE;&XX

XX représente le checksum en hexadécimal (2 caractères).

Exemples :

// Envoyer température avec checksum
SchreinVThermometer2=value:24.5;&3F

// Envoyer état capteur avec checksum
SchreinToggleSwitch5=state:1;&2C

// Envoyer pression avec checksum
SchreinAnalogicGauge1=value:1013.25;&7E

// Envoyer luminosité avec checksum
SchreinVProgressBar4=value:450;&A3

// Envoyer log avec checksum
SchreinLabel1=message:"Capteur démarré";&5F


4. Installation et inclusion

4.1. Inclusion de la bibliothèque

#include <SchreinSerialParser.h>

// Créer une instance sur le port Serial (ou Serial1, Serial2...)
SchreinSerialParser parser(Serial);


5. API principale

5.1. Configuration de base

void setup() {
    Serial.begin(115200);
    
    // Optionnel : activer le checksum (désactivé par défaut)
    parser.enableChecksum(true);
    
    // Optionnel : définir un timeout (défaut: 1000ms)
    parser.setTimeout(2000);
    
    Serial.println("SchreinSerialParser prêt !");
}

5.2. Réception de trames (boucle principale)

void loop() {
    // À appeler à chaque cycle - traite les données entrantes
    parser.loop();
    
    // Vérifier si une trame est disponible
    if (parser.isFrameAvailable()) {
        // Récupérer une valeur spécifique
        const char* temperature = parser.getValue("gaugeTemperature", "value");
        
        if (strlen(temperature) > 0) {
            float temp = atof(temperature);
            Serial.print("Température reçue : ");
            Serial.println(temp);
        }
        
        // Réinitialiser l'état de la trame
        parser.resetFrame();
    }
}

5.3. Envoi de commandes (3 méthodes)

Méthode 1 : sendCommand() – La plus simple ✅

// Envoyer une valeur à une jauge
parser.sendCommand("SchreinVThermometer", "Value", "24.5");

// Changer la couleur d'un texte
parser.sendCommand("SchreinLabel1", "color", "#00FF00");

// Allumer/éteindre une LED virtuelle
parser.sendCommand("SchreinToggleSwitch6", "IsOn", "1");

Méthode 2 : sendFrame() – Trame brute

parser.sendFrame("SchreinVThermometer1=Value:24.5;");

Méthode 3 : buildCommand() – Avec buffer pré-construit

char buffer[64];
SchreinSerialParser::buildCommand(buffer, sizeof(buffer), 
                                   "servoMoteur", "angle", "90");
parser.sendFrame(buffer);


6. Système de callbacks

Les callbacks permettent une programmation événementielle plus propre et réactive.

6.1. Callback de trame parsée

void onFrameParsed(const char* control, const char* key, const char* value) {
    Serial.print("Reçu - Ctrl:");
    Serial.print(control);
    Serial.print(" Key:");
    Serial.print(key);
    Serial.print(" Val:");
    Serial.println(value);
    
    // Traitement spécifique par contrôleur
    if (strcmp(control, "ledRouge") == 0 && strcmp(key, "etat") == 0) {
        digitalWrite(LED_BUILTIN, atoi(value) ? HIGH : LOW);
    }
}

void setup() {
    Serial.begin(115200);
    parser.onFrameParsed(onFrameParsed);
}

6.2. Callback de données brutes

void onRawData(const char* data) {
    Serial.print("Donnée brute reçue : ");
    Serial.println(data);
}

void setup() {
    parser.onDataReceived(onRawData);
}

6.3. Callback d’erreur

void onError(const char* error) {
    Serial.print("ERREUR : ");
    Serial.println(error);
}

void setup() {
    parser.onError(onError);
}


7. File de trames (Queue)

Pour les applications à haut débit, la bibliothèque stocke jusqu’à 4 trames en attente.

7.1. Utilisation de la file

void loop() {
    parser.loop();
    
    // Traiter toutes les trames en attente
    ParsedFrame frame;
    while (parser.dequeueFrame(frame)) {
        if (frame.valid) {
            Serial.print("Control: ");
            Serial.print(frame.controlName);
            Serial.print(" | Key: ");
            Serial.print(frame.key);
            Serial.print(" | Value: ");
            Serial.println(frame.value);
        }
    }
}

7.2. Vérification du nombre de trames en attente

uint8_t pending = parser.pendingFrameCount();
if (pending > 0) {
    Serial.print(pending);
    Serial.println(" trames en attente");
}


8. Gestion des erreurs

8.1. Codes d’erreur

8.2. Récupération d’erreur

void loop() {
    parser.loop();
    
    if (parser.getLastError() != SchreinSerialParser::ErrorCode::NO_ERROR) {
        Serial.print("Erreur : ");
        Serial.println(parser.getLastErrorMessage());
        parser.clearError();
    }
}


9. Statistiques et debug

9.1. Métriques de performance

void afficherStats() {
    Serial.print("Trames reçues : ");
    Serial.println(parser.getFramesReceived());
    
    Serial.print("Trames perdues : ");
    Serial.println(parser.getFramesDropped());
    
    Serial.print("Erreurs checksum : ");
    Serial.println(parser.getChecksumErrors());
    
    Serial.print("Temps max parse : ");
    Serial.print(parser.getParseTimeUsMax());
    Serial.println(" µs");
    
    Serial.print("Trames en attente : ");
    Serial.println(parser.pendingFrameCount());
}

9.2. Mode debug

// Afficher le buffer de réception
parser.debugOutput("receiveBuffer");

// Afficher la dernière trame parsée
parser.debugOutput("parsedFrame");

// Afficher la dernière erreur
parser.debugOutput("error");

// Afficher les statistiques
parser.debugOutput("stats");

// Afficher l'état de la file
parser.debugOutput("queue");


10. Exemples complets

10.1. Réception basique

#include <SchreinSerialParser.h>

SchreinSerialParser parser(Serial);

void setup() {
    Serial.begin(115200);
    pinMode(LED_BUILTIN, OUTPUT);
    
    parser.enableChecksum(false);
    Serial.println("Prêt à recevoir des trames...");
}

void loop() {
    parser.loop();
    
    if (parser.isFrameAvailable()) {
        // Lire la température
        const char* temp = parser.getValue("gaugeTemp", "value");
        if (strlen(temp) > 0) {
            Serial.print("Température: ");
            Serial.println(temp);
        }
        
        // Lire l'état LED
        const char* etat = parser.getValue("ledEtat", "state");
        if (strlen(etat) > 0) {
            digitalWrite(LED_BUILTIN, atoi(etat) ? HIGH : LOW);
        }
        
        parser.resetFrame();
    }
}

10.2. Envoi de données capteur (DHT22)

#include <SchreinSerialParser.h>
#include <DHT.h>

SchreinSerialParser parser(Serial);
DHT dht(2, DHT22);

void setup() {
    Serial.begin(115200);
    dht.begin();
}

void loop() {
    static unsigned long lastSend = 0;
    
    if (millis() - lastSend > 2000) {
        float temp = dht.readTemperature();
        float hum = dht.readHumidity();
        
        // Envoyer température
        parser.sendCommand("gaugeTemp", "value", String(temp).c_str());
        
        // Envoyer humidité
        parser.sendCommand("gaugeHum", "value", String(hum).c_str());
        
        lastSend = millis();
    }
    
    parser.loop();
}

10.3. Gestion avancée avec queue et callbacks

#include <SchreinSerialParser.h>

SchreinSerialParser parser(Serial);

void onFrame(const char* ctrl, const char* key, const char* val) {
    if (strcmp(ctrl, "moteur") == 0 && strcmp(key, "vitesse") == 0) {
        analogWrite(9, atoi(val));
    }
    else if (strcmp(ctrl, "servo") == 0 && strcmp(key, "angle") == 0) {
        // Commande servo
    }
}

void onError(const char* err) {
    Serial.print("⚠️ Erreur: ");
    Serial.println(err);
}

void setup() {
    Serial.begin(115200);
    parser.onFrameParsed(onFrame);
    parser.onError(onError);
    parser.enableChecksum(true);
}

void loop() {
    parser.loop();
    
    // Traitement par lots via la queue
    ParsedFrame frame;
    while (parser.dequeueFrame(frame)) {
        if (frame.valid) {
            onFrame(frame.controlName, frame.key, frame.value);
        }
    }
}

10.4. Projet complet : Station météo

#include <SchreinSerialParser.h>
#include <DHT.h>

#define DHT_PIN 2
#define DHT_TYPE DHT22

SchreinSerialParser parser(Serial);
DHT dht(DHT_PIN, DHT_TYPE);

unsigned long lastSend = 0;
char buffer[16];

void setup() {
    Serial.begin(115200);
    dht.begin();
    
    parser.enableChecksum(false);
    Serial.println("Station météo connectée");
}

void loop() {
    parser.loop();
    
    // Envoi des données toutes les 2 secondes
    if (millis() - lastSend > 2000) {
        float temp = dht.readTemperature();
        float hum = dht.readHumidity();
        
        if (!isnan(temp)) {
            dtostrf(temp, 4, 1, buffer);
            parser.sendCommand("gaugeTemperature", "value", buffer);
        }
        
        if (!isnan(hum)) {
            dtostrf(hum, 4, 1, buffer);
            parser.sendCommand("gaugeHumidite", "value", buffer);
        }
        
        lastSend = millis();
    }
    
    // Réception des commandes
    if (parser.isFrameAvailable()) {
        const char* cmd = parser.getValue("systeme", "commande");
        if (strlen(cmd) > 0 && strcmp(cmd, "reset") == 0) {
            // Réinitialiser la carte
        }
        parser.resetFrame();
    }
}


11. Compatibilité avec Schrein UI Builder

La bibliothèque est nativement compatible avec les trames générées par Schrein UI Builder.

11.1. Schéma de communication typique


12. Optimisations mémoire

12.1. Pour Arduino Uno (2KB RAM)

Modifiez les constantes dans SchreinSerialParser.h :

#define MAX_CONTROL_NAME_LEN  16   // au lieu de 24
#define MAX_KEY_LEN           12   // au lieu de 16
#define MAX_VALUE_LEN         32   // au lieu de 48
#define PARSER_BUFFER_SIZE    64   // au lieu de 128
#define MAX_PENDING_FRAMES     2   // au lieu de 4


12.2. Pour ESP32 (520KB RAM)

Les valeurs par défaut sont déjà optimisées.

12.3. Estimation de l’empreinte mémoire


13. Dépannage

13.1. Problèmes courants

13.2. Debug pas à pas

// 1. Vérifier la connexion série
void setup() {
    Serial.begin(115200);
    Serial.println("Test de communication");
}

// 2. Activer le mode debug
parser.debugOutput("stats");

// 3. Vérifier les erreurs
if (parser.getLastError() != SchreinSerialParser::ErrorCode::NO_ERROR) {
    parser.debugOutput("error");
}


14. Référence rapide (Cheatsheet)

// Inclusion
#include <SchreinSerialParser.h>

// Instance
SchreinSerialParser parser(Serial);

// Setup
parser.enableChecksum(true);      // Activer checksum
parser.setTimeout(1000);          // Timeout 1s
parser.onFrameParsed(callback);   // Callback trame

// Loop
parser.loop();                     // Traitement série

// Réception simple
if (parser.isFrameAvailable()) {
    const char* val = parser.getValue("controleur", "propriete");
    parser.resetFrame();
}

// Réception avec queue
ParsedFrame f;
while (parser.dequeueFrame(f)) {
    // Traiter f.controlName, f.key, f.value
}

// Envoi (3 méthodes)
parser.sendCommand("ctrl", "prop", "val");
parser.sendFrame("<[ctrl|prop|val]>");
parser.buildCommand(buf, sizeof(buf), "ctrl", "prop", "val");

// Debug
parser.debugOutput("stats");


15. Support et ressources

15.1. Liens utiles

📊 Comparaison de Performance

Utilisation Mémoire


Performance


Plateformes suppertés


Applications Types