Zurück zur Startseite

Ereignisgesteuerte Programmierung auf Arduino: Muster und Beispiele

Der Artikel demonstriert die Implementierung ereignisgesteuerter Architektur in Arduino-Projekten durch die EVA Core-Bibliothek. Prinzipien der Komponenten-Zusammensetzung, Code-Beispiele für Sensoren und Peripheriegeräte sowie Einschränkungen des Ansatzes werden betrachtet. Material für Middle-/Senior-Entwickler.

Event-Modell für Arduino: von Theorie zur Praxis
Advertisement 728x90

# Eventgesteuerte Programmierung auf Arduino: Enterprise-Muster für Embedded-Systeme

Arduino wird traditionell mit linearen Sketches und der zyklischen loop() in Verbindung gebracht. Doch wenn Projekte komplexer werden, führt dieser Ansatz zu Spaghetti-Code. Wir stellen eine Methode vor, um ereignisgesteuerte Architektur aus Enterprise-Anwendungen in die Embedded-Entwicklung zu übertragen – mit der EVA Core-Bibliothek.

Das Problem der Struktur in Mikrocontrollern

In Desktop- und Server-Anwendungen ist der Code um Event-Handler organisiert: Klicks, Timer, Netzwerkpakete. Bei Arduino hingegen wird alles in eine einzige loop() gezwängt, wo Zustände manuell mit millis() und Flags verfolgt werden müssen. Das erzeugt zwei kritische Einschränkungen:

  • Keine Isolation der Komponentenlogik – Timing-Informationen werden durch die gesamte Objekt-Hierarchie propagiert
  • Fehlende klare Verträge zwischen Modulen bezüglich Timing-Anforderungen

Standard-Arduino-Bibliotheken beheben das nur stückweise: Es gibt Button-Handler, separate Timer, aber kein einheitliches Event-System. Versuche, eine „All-in-One“-Lösung zu bauen, stoßen auf enge Kopplung an eine spezifische loop()-Implementierung.

Google AdInline article slot

EVA Core: Drei Komponenten in einem einheitlichen System

Die EVA Core-Bibliothek (Event-Driven Architecture) löst das Problem durch drei interagierende Schichten:

  • Callback-Mechanismus für Klassenmethoden — ermöglicht das Abonnieren von Events ohne globale Funktionen
  • Tickable-Interface — gibt Objekten Zugriff auf die Zeit-Schleife über die tick()-Methode
  • Survival Kit — eine Sammlung vorgefertigter Komponenten (Buttons, Timer), die auf den ersten beiden Prinzipien basieren

Schlüsselinnovation – Trennung von Timing-Logik und Geschäftsregeln. Der Sensor verwaltet seine eigenen Timings, während die Anwendung nur auf Events reagiert. Das ermöglicht:

  • Isolation von Filter- und Debouncing-Algorithmen in Komponenten
  • Garantie für das Update-Rhythmus bei verschachtelten Objekten
  • Wegfall der Notwendigkeit, Aufrufe durch die Hierarchie zu propagieren

Schauen wir uns die Implementierung anhand eines Temperatursensors an:

Google AdInline article slot
#include <evaTickable.h>
#include <evaHandler.h>

using namespace eva;

class TempSensor : public Tickable {
private:
  IHandler* listener = nullptr;
  unsigned long lastRead = 0;

public:
  void subscribe(IHandler* handler) {
    listener = handler;
  }

private:
  void tick() override {
    if (millis() - lastRead > 1000) { // Interval 1 sek
      int value = readTemperature();
      if (listener && value != lastValue) {
        CallbackInfo info;
        info.eventType = TEMP_UPDATE;
        info.eventArg = value;
        listener->invoke(this, info);
      }
      lastRead = millis();
    }
  }
};

class ClimateControl : public IHandler {
private:
  TempSensor sensor;

  void onTempUpdate(int value) {
    if (value > 25) activateCooling();
  }

public:
  ClimateControl() {
    sensor.subscribe(this);
  }

  void invoke(void* sender, CallbackInfo info) override {
    if (info.eventType == TEMP_UPDATE) {
      onTempUpdate(info.eventArg);
    }
  }
};

void setup() {
  static ClimateControl system;
}

void loop() {
  eva::tac(); // Edinaya point update
}

Vorteile des Ansatzes:

  • TempSensor kapselt Polling- und Filterlogik
  • ClimateControl kennt nur Events, keine Timing-Details
  • Hinzufügen neuer Sensoren erfordert keine Änderungen an loop()
  • Testen der Geschäftslogik isoliert vom Timing

LEGO-Architektur für Peripheriegeräte

Survival Kit implementiert das Kompositionsprinzip durch Compile-Time-Templates. Zum Beispiel wird Multi-Button-Handling an einem analogen Eingang als Kette von Transformern aufgebaut:

template <int PIN, int PIN_MODE, signed short... LEVELS>
using PinMultiButton = Button<QuantizeDecor<DebounceDecor<AnalogPinReader<PIN, PIN_MODE>>, LEVELS...>>;

Signalverarbeitungsarchitektur:

Google AdInline article slot
  • Analogsignal →
  • Debounce →
  • Quantisierung nach Stufen →
  • Button-Logik →
  • Events

Dieses Pattern erlaubt es, benutzerdefinierte Handler aus vorgefertigten Komponenten zusammenzubauen. Beispiel – ein Keypad aus diskreten Buttons:

#include <evaSwitch.h>

class MyKeypad {
public:
  MyKeypad() {
    pinMode(2, INPUT_PULLUP);
    // ... initialization ostalnykh pinov
  }

  signed short getValue() {
    if (digitalRead(2) == LOW) return 'u';
    // ... processing drugikh knopok
    return 0;
  }
};

// Withborka chains processing
Switch<DebounceDecor<MyKeypad>> keypad;

// Podpiska on events
keypad.setListener(new Handler<App>(this, &App::onKeyPress), ON_PRESS);

Jede Schicht übernimmt ihre eigene Verantwortung. Bei Bedarf können Sie Debounce durch einen ausgeklügelteren Algorithmus ersetzen oder Press-Duration-Filtering hinzufügen – ohne die Kernanwendungslogik zu verändern.

Einschränkungen und Anwendungsfälle

EVA Core ist als methodisches Tool positioniert, nicht als universelle Lösung. Verwenden Sie es, wenn:

  • Projekte mit komplexen Objekthierarchien gebaut werden
  • Klare Trennung von Timing und Geschäftslogik benötigt wird
  • Prinzipien der ereignisgesteuerten Architektur auf einfachen Geräten vermittelt werden

Kritische Einschränkungen:

  • Erhöhter RAM-Verbrauch durch virtuelle Tabellen
  • Komplexität für Einsteiger (erfordert Kenntnisse von Templates und Method-Pointern)
  • Overkill für einfache Projekte (z. B. Steuerung einer einzelnen LED)

Wichtig zu verstehen: Das Ziel der Bibliothek ist die Demonstration von Architekturpatterns, nicht die Ressourcenoptimierung. Für Produktionsprojekte mit engen Restriktionen müssen Sie den Overhead analysieren.

Wichtige Erkenntnisse

  • Event-Modell isoliert Timing-Abhängigkeiten in Komponenten
  • Tickable-Interface gewährleistet regelmäßige Updates für verschachtelte Objekte
  • Template-Komposition bietet Flexibilität ohne Laufzeit-Overhead
  • Keine Propagierung von Aufrufen vereinfacht Refactoring und Testing
  • Arduino wird zum Spielplatz für Enterprise-Muster

Dieser Ansatz beweist, dass selbst auf ressourcenbeschränkten Mikrocontrollern professionelle Designmethoden angewendet werden können. Der größte Vorteil – der Code wird vorhersehbar und skalierbar, statt nur „irgendwie zu funktionieren“.

— Editorial Team

Advertisement 728x90

Weiterlesen