SOLID C++ ESP32
Harte Grenzen
- Bevorzuge Dateien unter 400 Zeilen; teile auf, wenn eine Übersetzungseinheit mehrere Verantwortlichkeiten erhält
- Maximal ~30 Methoden pro Klasse
- Bevorzuge Funktionsrümpfe unter ~60 Zeilen (hilfsweise auslagern)
- Keine verschachtelten Klassen — verschiebe innere Typen in einen eigenen Header / eigene Übersetzungseinheit
- Genau ein primärer Typ (
class/struct/enum/enum class) pro öffentlichem Header, wenn das Projekt dies vorsieht (Hochlauf / strikte Oberflächen)
Sprache & Stil
- Commitierter Code / Kommentare / Dokumentation in Englisch
- Verwende immer geschweifte Klammern für
if/for/while/switch - Keine einzeiligen Funktionsrümpfe für nicht-triviale Logik
- Beschreibende Namen (
telemetryManager, nichtmgr/tmpaußer bei trivialen Schleifenzählern) - Bevorzuge einen
namespace-Block pro Übersetzungseinheit - Header deklarieren die Absicht;
.cpp-Dateien bleiben schlicht und direkt - Öffentliche Methoden in
I*.h-Schnittstellen (und anderen optierten öffentlichen Headern) benötigen einen einzeiligen///-Absichtskommentar
Architektur (SOLID + DI)
- Bevorzuge Schnittstellen (
I*) an Domänengrenzen - Hänge in Anwendungs-/Domänencode von Abstraktionen ab; konstruiere konkrete Adapter nur in
main.cppoder einer expliziten Kompositionswurzel - Hardware (BLE, WLAN, Display, NVS, Busse) bleibt hinter Schnittstellen, sodass Host-Komponententests Fakes verwenden – nicht
#ifdef-Operationen - Bevorzuge kleine, reine Parser, die in
native-/Host-Builds komponententestbar sind - Bevorzuge Komposition gegenüber wachsenden God-Objekten
- Caste FreeRTOS
void*sofort in einen benannten, typisierten Zeiger - Bevorzuge Projekt-Hilfsfunktionen (z. B. sicheres C-String-Kopieren) gegenüber
strncpy-Varianten
Domänen-/Schichten-Isolation
- Halte Hub-exklusive Stacks (Web, Auth, OTA, HTTP-API) von Bridge-/Leaf-Zielen fern
- Führe keine
#includevon konkreten Adapter-Headern aus der falschen Schicht durch; wenn ein Guardrail Adapternamen auflistet, befolge es
Keine Testinstrumentierung in Produktions-APIs
Produktionscode darf nicht durch Test-Build-Flags geformt werden.
- Verboten in Headern und
.cpp:UNIT_TEST,#ifdef UNIT_TEST,#ifndef UNIT_TEST,#if defined(UNIT_TEST)oder jegliche äquivalente Test-Only-Abschottung (esp_cpp_guardrails schlägt standardmäßig hart fehl) - Bevorzuge DI: Extrahiere eine Schnittstelle, injiziere einen Fake in Host-Tests, oder schließe Geräteadapter-Übersetzungseinheiten vom
native/Host-build_src_filteraus – niemals Produktionsquellen mit Ifdefs stutzen - Plattformerkenner (
__has_include(<esp_random.h>),ESP_PLATFORM) sind in Ordnung; Test-Build-Flags, die Produktionslogik umformen, sind es nicht - Behalte portable Typen in Headern (
std::mutexusw.) oder bedingungslose gerätespezifische Includes in gerätespezifischen Übersetzungseinheiten
Geruch → Behebung:
| Geruch | Behebung |
|---|---|
#ifndef UNIT_TEST um WLAN-/BLE-Aufrufe in einem Dienst | Injiziere IWifiConnector / IBleClient; Fakes in Host-Tests |
Header stutzt unterschiedliche Typen, wenn UNIT_TEST | Immer portabler Typ, oder Geräteadapter aufteilen |
| God-Klasse benötigt Ifdefs, um auf Host zu kompilieren | Verantwortlichkeiten aufteilen; Kompositionswurzel verdrahtet Adapter |
Geheimnisse
- Niemals WLAN-Passwörter, OTA-/Web-Passwörter, Gerätegeheimnisse, PMKs, Tokens oder LAN-Anmeldeinformationen in Quellen hartcodieren
- Injiziere Geheimnisse über Umgebungsvariablen / Build-Flags, die von PlatformIO (oder Äquivalent) konsumiert werden – committe niemals
.envmit echten Geheimnissen
Dual-Core-FreeRTOS (ESP32)
Klassischer ESP32 hat zwei Kerne:
| Kern | ID | Typische Verwendung |
|---|---|---|
| PRO_CPU | 0 | WLAN / BT / funknahe Arbeiten (ESP-NOW TX/RX-Schleifen, BLE/SPP-Funkaufgaben) |
| APP_CPU | 1 | Schwerere App-Arbeit: Display-UI, HTTP/Web, Parsing-Stöße, SD, nicht funkbezogene Schleifen |
Standardeinstellungen (nur mit dokumentiertem Sonderfall überschreiben):
- Hefte funk-/stackempfindliche Aufgaben mit
xTaskCreatePinnedToCore(..., kRadioCoreId)an, wobeikRadioCoreId = 0 - Hefte UI / schwere nicht-funkbezogene Aufgaben an
kAppCoreId = 1 - Vermeide lange blockierende Arbeit auf Kern 0; es hungert den Funk-Stack aus
- Bevorzuge benannte Konstanten (
kRadioCoreId/kAppCoreId) gegenüber magischen0/1
Validierung
- Führe das Guardrail-Skript des Projekts (oder
tools/esp_cpp_guardrails) nach strukturellen Änderungen aus - Füge die kleinste Host-/Komponentenprüfung hinzu, die fehlschlägt, wenn neue Logik bricht
- Baue nach Schnittstellenänderungen
- Aktualisiere README, wenn sich Verhalten, Skripte, Hardwareannahmen oder Architektur ändern
- Bevorzuge eine blockierende Code-Qualitätsüberprüfung vor dem Zusammenführen, wenn das Projekt dies erfordert
Checkliste
- Geschweifte Klammern überall; englische Bezeichner/Kommentare
- Unter ~400 Zeilen / ~30 Methoden; keine verschachtelte Klasse
- Konkrete Adapter nur an der Kompositionswurzel; Domäne hängt von
I*ab - Kein
UNIT_TEST/ Test-Ifdefs in Headern; vermeide sie in.cpp - Keine hartcodierten Geheimnisse
-
///bei neuen öffentlichen Schnittstellenmethoden - Funkaufgaben auf Kern 0; schwere App-Arbeit auf Kern 1 (außer dokumentierte Ausnahme)
- Guardrails / Tests grün