Aurora HomeAurora HomeDocs
DocsConventions ESP32

Conventions — Firmware ESP32

Standards de code C++ utilisés dans le projet aurora-home-esp32.

Conventions de nommage

camelCase + préfixeFonctions API publiques (préfixe module)
sensorsInitBh1750()sensorsReadScd30()netBegin()netMqttPublish()fusionAverage()
AURORA_UPPER_SNAKE_CASEConstantes de configuration (AURORA_)
AURORA_MQTT_PORTAURORA_I2C_SDAAURORA_PUBLISH_INTERVAL_MS
camelCaseVariables locales & objets internes
co2PpmavgTempCmqttBackoffMshaveScdData

Structure du projet

aurora-home-esp32/
aurora-home-esp32/
platformio.ini# Configuration build & dépendances
include/# Headers publics
Config.h# Paramètres firmware (gitignored)
Config.h.example# Template — cp → Config.h
Logger.h# Macros LOG_INFO / LOG_WARN / LOG_ERROR
sensors.h# API capteurs I2C
net.h# API WiFi Soft-AP + MQTT + mDNS
telemetry.h# Sérialisation JSON (ArduinoJson)
fusion.h# Fusion capteurs (header-only)
src/
main.cpp# Orchestration setup() + loop()
sensors.cpp# Drivers BH1750 / SCD30 / BME280
net.cpp# Soft-AP WiFi, mDNS, MQTT, LWT
telemetry.cpp# Format JSON payload
test/# Tests Unity natifs (sans hardware)
stubs# Stubs Arduino / WiFi / PubSubClient
test_native_fusion
test_native_contracts
test_native_net
test_native_telemetry
examples# Exemples par capteur
scripts# Scripts CI (coverage)

Organisation de main.cpp

main.cpp ne contient que setup() et loop(). La logique métier est répartie dans sensors.cpp, net.cpp et telemetry.cpp :

src/main.cpp
#include "Config.h"
#include "Logger.h"
#include "fusion.h"
#include "net.h"
#include "sensors.h"
#include "telemetry.h"

// setup() — Wire.begin → sensorsInit*() → netBegin() → watchdog
void setup() { ... }

// loop() — netMqtt*() → sensorsRead*()
//         → fusionAverage() → telemetryFormat() → netMqttPublish()
void loop() { ... }

Pattern init / read

Chaque capteur a deux fonctions dédiées retournant un bool :

include/sensors.h
// Initialisation — retourne true si OK
bool sensorsInitBme280();   // essai 0x76 puis 0x77
bool sensorsInitScd30();    // reset + firmware check
bool sensorsInitBh1750();

// Lecture — retourne true UNIQUEMENT si données disponibles
bool sensorsReadScd30(float& co2Ppm, float& tempC, float& humPct);
bool sensorsReadBme280(float& tempC, float& humPct, float& pressureHpa);
bool sensorsReadBh1750(float& lux);

Gestion des erreurs

Erreur critique (setup)

Si un capteur obligatoire (SCD30, BME280) échoue à l'initialisation, fatalReboot() log l'erreur et redémarre l'ESP32 :

if (!sensorsInitScd30()) fatalReboot("SCD30 init");
if (!sensorsInitBme280()) fatalReboot("BME280 init");

// fatalReboot() : LOG_ERROR → delay(2000) → ESP.restart()

Erreur non-critique (loop)

Les erreurs de lecture dans la boucle sont loguées via LOG_WARN mais n'interrompent pas le cycle :

if (!sensorsReadBme280(bmeTempC, bmeHumPct, pressureHpa))
    LOG_WARN("BME280 read failed");

if (!sensorsReadBh1750(lux))
    LOG_WARN("BH1750 read failed");

Boucle non-bloquante

src/main.cpp — loop()
void loop() {
  esp_task_wdt_reset();
  const uint32_t now = millis();

  // 1. Attendre client WiFi
  if (!netHasClient()) { delay(500); return; }

  // 2. Reconnexion MQTT avec backoff exponentiel (1s → 60s)
  if (!netMqttConnected()) {
      if (now - mqttLastAttemptMs < mqttBackoffMs) { delay(50); return; }
      mqttLastAttemptMs = now;
      if (!netMqttTryConnect()) {  // mDNS d'abord, puis scan IPs
          mqttBackoffMs = min(mqttBackoffMs * 2, kMqttBackoffMaxMs);
          return;
      }
      mqttBackoffMs = kMqttBackoffInitialMs;
  }
  netMqttLoop();

  // 3. Lire SCD30 si donnée prête (~toutes les 2s, non-bloquant)
  float c = 0, t = 0, h = 0;
  if (sensorsReadScd30(c, t, h)) { co2Ppm = c; scdTempC = t; haveScdData = true; }

  // 4. Publier selon AURORA_PUBLISH_INTERVAL_MS (défaut 30s)
  if (!haveScdData || now - lastPublishMs < AURORA_PUBLISH_INTERVAL_MS) {
      delay(10); return;
  }
  lastPublishMs = now;
  sensorsReadBme280(bmeTempC, bmeHumPct, pressureHpa);
  sensorsReadBh1750(lux);
  const float avgTempC  = fusionAverage(scdTempC, bmeTempC);
  const float avgHumPct = fusionAverage(scdHumPct, bmeHumPct);
  telemetryFormat(payload, sizeof(payload), avgTempC, avgHumPct, ...);
  netMqttPublish(AURORA_MQTT_TOPIC_DATA, payload);
}

Configuration I2C

src/main.cpp — setup()
Wire.begin(AURORA_I2C_SDA, AURORA_I2C_SCL);  // GPIO21 (SDA), GPIO22 (SCL)
// SCD30 : adresse 0x61 — BH1750 : 0x23 — BME280 : 0x76/0x77 (auto)

Commandes PlatformIO

platformio run -e esp32dev
platformio run -e esp32dev -t upload
platformio device monitor --baud 115200
platformio run -e esp32dev -t clean