Menu
Back to Blog
1 min read
IoT

ESP32 Development mit PlatformIO

ESP32 Entwicklung mit PlatformIO. WiFi, Sensoren, MQTT Integration und OTA Updates für IoT-Projekte.

ESP32PlatformIOArduinoIoTWiFiMQTT
ESP32 Development mit PlatformIO

ESP32 Development mit PlatformIO

Meta-Description: ESP32 Entwicklung mit PlatformIO. WiFi, Sensoren, MQTT Integration und OTA Updates für IoT-Projekte.

Keywords: ESP32, PlatformIO, Arduino, IoT, WiFi, MQTT, Sensors, Embedded Development


Einführung

Der ESP32 ist der vielseitigste Microcontroller für IoT-Projekte. Mit WiFi, Bluetooth, dualen Cores und zahlreichen GPIOs – kombiniert mit PlatformIO als moderne Entwicklungsumgebung.


ESP32 Overview

┌─────────────────────────────────────────────────────────────┐
│                    ESP32 ARCHITECTURE                        │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  CPU: Xtensa dual-core 32-bit LX6 @ 240MHz                 │
│                                                             │
│  Memory:                                                    │
│  ├── 520 KB SRAM                                           │
│  ├── 4 MB Flash (external)                                 │
│  └── 448 KB ROM                                            │
│                                                             │
│  Connectivity:                                              │
│  ├── WiFi 802.11 b/g/n (2.4 GHz)                          │
│  ├── Bluetooth 4.2 + BLE                                   │
│  └── Multiple SPI, I2C, UART, I2S                         │
│                                                             │
│  GPIO:                                                      │
│  ├── 34 GPIO Pins                                          │
│  ├── 18 ADC Channels (12-bit)                              │
│  ├── 2 DAC Channels (8-bit)                                │
│  ├── 16 PWM Channels                                       │
│  └── 10 Touch Sensors                                      │
│                                                             │
│  Power:                                                     │
│  ├── Operating Voltage: 3.3V                               │
│  ├── Deep Sleep: ~10µA                                     │
│  └── Active WiFi: ~160mA                                   │
│                                                             │
│  Peripherals: RTC, Timers, Watchdog, Hall Sensor           │
│                                                             │
└─────────────────────────────────────────────────────────────┘

PlatformIO Setup

# VS Code Extension installieren
# Extensions → PlatformIO IDE

# Oder CLI Installation
pip install platformio

# Neues Projekt erstellen
pio project init --board esp32dev --ide vscode
; platformio.ini
[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200
upload_speed = 921600

; Build Flags
build_flags =
    -DCORE_DEBUG_LEVEL=3
    -DCONFIG_ARDUHAL_LOG_COLORS=1

; Libraries
lib_deps =
    bblanchon/ArduinoJson@^7.0.0
    knolleary/PubSubClient@^2.8
    adafruit/DHT sensor library@^1.4.6
    adafruit/Adafruit Unified Sensor@^1.1.14

; OTA Updates
upload_protocol = espota
upload_port = 192.168.1.100

; Filesystem
board_build.filesystem = littlefs

; Partitions
board_build.partitions = min_spiffs.csv

WiFi Connection

// src/main.cpp
#include <Arduino.h>
#include <WiFi.h>
#include <WiFiManager.h>

// Credentials (besser: WiFiManager oder NVS)
const char* ssid = "YourNetwork";
const char* password = "YourPassword";

// Status LED
const int LED_PIN = 2;

void setupWiFi() {
  Serial.println("Connecting to WiFi...");

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);

  int attempts = 0;
  while (WiFi.status() != WL_CONNECTED && attempts < 30) {
    delay(500);
    Serial.print(".");
    digitalWrite(LED_PIN, !digitalRead(LED_PIN)); // Blink
    attempts++;
  }

  if (WiFi.status() == WL_CONNECTED) {
    Serial.println("\nWiFi connected!");
    Serial.print("IP: ");
    Serial.println(WiFi.localIP());
    Serial.print("RSSI: ");
    Serial.println(WiFi.RSSI());
    digitalWrite(LED_PIN, HIGH);
  } else {
    Serial.println("\nWiFi connection failed!");
    // Fallback: AP Mode für Konfiguration
    startConfigPortal();
  }
}

void startConfigPortal() {
  WiFiManager wifiManager;

  // Reset settings for testing
  // wifiManager.resetSettings();

  // Custom Parameters
  WiFiManagerParameter mqtt_server("mqtt", "MQTT Server", "mqtt.local", 40);
  wifiManager.addParameter(&mqtt_server);

  // Auto-connect oder Config Portal
  if (!wifiManager.autoConnect("ESP32-Setup", "password123")) {
    Serial.println("Failed to connect, restarting...");
    delay(3000);
    ESP.restart();
  }

  Serial.println("Connected via WiFiManager!");
  Serial.println(mqtt_server.getValue());
}

void setup() {
  Serial.begin(115200);
  pinMode(LED_PIN, OUTPUT);

  setupWiFi();
}

void loop() {
  // WiFi Reconnect
  if (WiFi.status() != WL_CONNECTED) {
    Serial.println("WiFi lost, reconnecting...");
    setupWiFi();
  }

  delay(1000);
}

Sensor Reading (DHT22)

// src/sensors.cpp
#include <DHT.h>
#include <ArduinoJson.h>

#define DHT_PIN 4
#define DHT_TYPE DHT22

DHT dht(DHT_PIN, DHT_TYPE);

struct SensorData {
  float temperature;
  float humidity;
  float heatIndex;
  bool valid;
};

SensorData readDHT22() {
  SensorData data = {0, 0, 0, false};

  // Mehrere Versuche für zuverlässige Messung
  for (int i = 0; i < 3; i++) {
    data.temperature = dht.readTemperature();
    data.humidity = dht.readHumidity();

    if (!isnan(data.temperature) && !isnan(data.humidity)) {
      data.heatIndex = dht.computeHeatIndex(
        data.temperature,
        data.humidity,
        false  // Celsius
      );
      data.valid = true;
      break;
    }

    delay(2000);  // DHT braucht Zeit zwischen Messungen
  }

  return data;
}

String sensorDataToJson(const SensorData& data) {
  JsonDocument doc;

  doc["device_id"] = WiFi.macAddress();
  doc["timestamp"] = millis();
  doc["temperature"] = round(data.temperature * 10) / 10.0;
  doc["humidity"] = round(data.humidity * 10) / 10.0;
  doc["heat_index"] = round(data.heatIndex * 10) / 10.0;
  doc["valid"] = data.valid;
  doc["rssi"] = WiFi.RSSI();

  String output;
  serializeJson(doc, output);
  return output;
}

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

  // Warm-up Zeit
  delay(2000);
}

void loop() {
  SensorData data = readDHT22();

  if (data.valid) {
    String json = sensorDataToJson(data);
    Serial.println(json);
  } else {
    Serial.println("Sensor read failed!");
  }

  delay(10000);  // Alle 10 Sekunden
}

MQTT Integration

// src/mqtt_client.cpp
#include <WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>

const char* mqtt_server = "mqtt.example.com";
const int mqtt_port = 1883;
const char* mqtt_user = "esp32";
const char* mqtt_pass = "secret";

WiFiClient espClient;
PubSubClient mqtt(espClient);

String deviceId;
String baseTopic;

void mqttCallback(char* topic, byte* payload, unsigned int length) {
  Serial.printf("Message [%s]: ", topic);

  // Payload zu String
  String message;
  for (unsigned int i = 0; i < length; i++) {
    message += (char)payload[i];
  }
  Serial.println(message);

  // JSON parsen
  JsonDocument doc;
  if (deserializeJson(doc, message) == DeserializationError::Ok) {
    handleCommand(doc);
  }
}

void handleCommand(const JsonDocument& doc) {
  const char* action = doc["action"];

  if (strcmp(action, "restart") == 0) {
    Serial.println("Restart command received");
    ESP.restart();
  }
  else if (strcmp(action, "led") == 0) {
    bool state = doc["state"];
    digitalWrite(LED_BUILTIN, state);
    Serial.printf("LED: %s\n", state ? "ON" : "OFF");
  }
  else if (strcmp(action, "config") == 0) {
    // Konfiguration aktualisieren
    int interval = doc["interval"] | 10000;
    Serial.printf("New interval: %d\n", interval);
  }
}

void mqttReconnect() {
  while (!mqtt.connected()) {
    Serial.println("Connecting to MQTT...");

    // Last Will Testament
    String willTopic = baseTopic + "/status";

    if (mqtt.connect(
      deviceId.c_str(),
      mqtt_user,
      mqtt_pass,
      willTopic.c_str(),
      1,     // QoS
      true,  // Retain
      "offline"
    )) {
      Serial.println("MQTT connected!");

      // Online Status
      mqtt.publish(willTopic.c_str(), "online", true);

      // Subscribe to commands
      String cmdTopic = baseTopic + "/command";
      mqtt.subscribe(cmdTopic.c_str());

      // Broadcast Commands
      mqtt.subscribe("devices/all/command");

    } else {
      Serial.printf("MQTT failed, rc=%d\n", mqtt.state());
      delay(5000);
    }
  }
}

void publishSensorData(const SensorData& data) {
  if (!mqtt.connected()) {
    mqttReconnect();
  }

  JsonDocument doc;
  doc["device_id"] = deviceId;
  doc["temperature"] = data.temperature;
  doc["humidity"] = data.humidity;
  doc["heat_index"] = data.heatIndex;
  doc["rssi"] = WiFi.RSSI();
  doc["uptime"] = millis() / 1000;

  String output;
  serializeJson(doc, output);

  String topic = baseTopic + "/data";
  mqtt.publish(topic.c_str(), output.c_str());

  Serial.println("Data published");
}

void setupMQTT() {
  deviceId = "esp32-" + WiFi.macAddress();
  deviceId.replace(":", "");
  baseTopic = "sensors/" + deviceId;

  mqtt.setServer(mqtt_server, mqtt_port);
  mqtt.setCallback(mqttCallback);
  mqtt.setBufferSize(512);  // Größerer Buffer für JSON

  mqttReconnect();
}

void loopMQTT() {
  if (!mqtt.connected()) {
    mqttReconnect();
  }
  mqtt.loop();
}

OTA Updates

// src/ota.cpp
#include <ArduinoOTA.h>
#include <ESPmDNS.h>

void setupOTA() {
  // Hostname
  ArduinoOTA.setHostname("esp32-sensor");

  // Password
  ArduinoOTA.setPassword("ota_secret");

  // Port (default: 3232)
  ArduinoOTA.setPort(3232);

  ArduinoOTA.onStart([]() {
    String type = ArduinoOTA.getCommand() == U_FLASH
      ? "sketch" : "filesystem";
    Serial.println("OTA Start: " + type);

    // MQTT disconnect
    mqtt.disconnect();
  });

  ArduinoOTA.onEnd([]() {
    Serial.println("\nOTA End");
  });

  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
  });

  ArduinoOTA.onError([](ota_error_t error) {
    Serial.printf("Error[%u]: ", error);
    switch (error) {
      case OTA_AUTH_ERROR: Serial.println("Auth Failed"); break;
      case OTA_BEGIN_ERROR: Serial.println("Begin Failed"); break;
      case OTA_CONNECT_ERROR: Serial.println("Connect Failed"); break;
      case OTA_RECEIVE_ERROR: Serial.println("Receive Failed"); break;
      case OTA_END_ERROR: Serial.println("End Failed"); break;
    }
  });

  ArduinoOTA.begin();
  Serial.println("OTA Ready");
}

void loopOTA() {
  ArduinoOTA.handle();
}

Deep Sleep für Batterie-Betrieb

// src/deep_sleep.cpp
#include <esp_sleep.h>

#define uS_TO_S_FACTOR 1000000ULL
#define SLEEP_DURATION_S 300  // 5 Minuten

RTC_DATA_ATTR int bootCount = 0;

void printWakeupReason() {
  esp_sleep_wakeup_cause_t reason = esp_sleep_get_wakeup_cause();

  switch (reason) {
    case ESP_SLEEP_WAKEUP_EXT0:
      Serial.println("Wakeup: External GPIO");
      break;
    case ESP_SLEEP_WAKEUP_TIMER:
      Serial.println("Wakeup: Timer");
      break;
    case ESP_SLEEP_WAKEUP_TOUCHPAD:
      Serial.println("Wakeup: Touchpad");
      break;
    default:
      Serial.printf("Wakeup: Other (%d)\n", reason);
      break;
  }
}

void goToSleep() {
  Serial.println("Going to deep sleep...");
  Serial.flush();

  // Timer Wakeup
  esp_sleep_enable_timer_wakeup(SLEEP_DURATION_S * uS_TO_S_FACTOR);

  // GPIO Wakeup (z.B. Button)
  esp_sleep_enable_ext0_wakeup(GPIO_NUM_33, LOW);

  // WiFi und Bluetooth ausschalten
  WiFi.disconnect(true);
  WiFi.mode(WIFI_OFF);
  btStop();

  // Deep Sleep starten
  esp_deep_sleep_start();
}

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

  bootCount++;
  Serial.printf("Boot count: %d\n", bootCount);
  printWakeupReason();

  // Sensor lesen und senden
  setupWiFi();
  setupMQTT();

  SensorData data = readDHT22();
  if (data.valid) {
    publishSensorData(data);
  }

  // Kurz warten für MQTT
  delay(1000);
  mqtt.loop();

  // Zurück in Deep Sleep
  goToSleep();
}

void loop() {
  // Wird bei Deep Sleep nicht erreicht
}

Web Server für Konfiguration

// src/webserver.cpp
#include <ESPAsyncWebServer.h>
#include <LittleFS.h>
#include <ArduinoJson.h>

AsyncWebServer server(80);

void setupWebServer() {
  // Static Files aus LittleFS
  if (!LittleFS.begin(true)) {
    Serial.println("LittleFS mount failed");
    return;
  }

  server.serveStatic("/", LittleFS, "/www/")
        .setDefaultFile("index.html");

  // API: Sensor Status
  server.on("/api/status", HTTP_GET, [](AsyncWebServerRequest *request) {
    JsonDocument doc;

    doc["device_id"] = deviceId;
    doc["ip"] = WiFi.localIP().toString();
    doc["rssi"] = WiFi.RSSI();
    doc["uptime"] = millis() / 1000;
    doc["free_heap"] = ESP.getFreeHeap();
    doc["temperature"] = lastReading.temperature;
    doc["humidity"] = lastReading.humidity;

    String output;
    serializeJson(doc, output);

    request->send(200, "application/json", output);
  });

  // API: Konfiguration
  server.on("/api/config", HTTP_GET, [](AsyncWebServerRequest *request) {
    JsonDocument doc;

    doc["mqtt_server"] = mqtt_server;
    doc["mqtt_port"] = mqtt_port;
    doc["interval"] = readInterval;

    String output;
    serializeJson(doc, output);

    request->send(200, "application/json", output);
  });

  // API: Konfiguration speichern
  AsyncCallbackJsonWebHandler* configHandler = new AsyncCallbackJsonWebHandler(
    "/api/config",
    [](AsyncWebServerRequest *request, JsonVariant &json) {
      JsonObject obj = json.as<JsonObject>();

      // Konfiguration in NVS speichern
      saveConfig(obj);

      request->send(200, "application/json", "{\"success\": true}");
    }
  );
  server.addHandler(configHandler);

  // API: Restart
  server.on("/api/restart", HTTP_POST, [](AsyncWebServerRequest *request) {
    request->send(200, "application/json", "{\"restarting\": true}");
    delay(100);
    ESP.restart();
  });

  // 404 Handler
  server.onNotFound([](AsyncWebServerRequest *request) {
    request->send(404, "text/plain", "Not Found");
  });

  server.begin();
  Serial.println("Web server started");
}

GPIO & PWM Control

// src/gpio_control.cpp

// LED PWM
const int LED_PIN = 2;
const int PWM_CHANNEL = 0;
const int PWM_FREQ = 5000;
const int PWM_RESOLUTION = 8;

void setupPWM() {
  ledcSetup(PWM_CHANNEL, PWM_FREQ, PWM_RESOLUTION);
  ledcAttachPin(LED_PIN, PWM_CHANNEL);
}

void setLEDBrightness(int brightness) {
  // 0-255
  ledcWrite(PWM_CHANNEL, brightness);
}

void fadeLED() {
  for (int duty = 0; duty <= 255; duty++) {
    ledcWrite(PWM_CHANNEL, duty);
    delay(10);
  }
  for (int duty = 255; duty >= 0; duty--) {
    ledcWrite(PWM_CHANNEL, duty);
    delay(10);
  }
}

// Analog Input
const int ANALOG_PIN = 34;

int readAnalog() {
  // 12-bit ADC: 0-4095
  int raw = analogRead(ANALOG_PIN);

  // Zu Spannung konvertieren (3.3V Ref)
  float voltage = (raw / 4095.0) * 3.3;

  return raw;
}

// Touch Sensor
const int TOUCH_PIN = T0;  // GPIO4

void setupTouch() {
  touchAttachInterrupt(TOUCH_PIN, touchCallback, 40);
}

void touchCallback() {
  Serial.println("Touch detected!");
}

Projektstruktur

esp32-sensor/
├── platformio.ini
├── src/
│   ├── main.cpp
│   ├── wifi.cpp
│   ├── mqtt.cpp
│   ├── sensors.cpp
│   ├── ota.cpp
│   └── webserver.cpp
├── include/
│   ├── config.h
│   └── secrets.h
├── lib/
│   └── CustomLibrary/
├── data/
│   └── www/
│       ├── index.html
│       └── style.css
└── test/
    └── test_main.cpp

Fazit

ESP32 mit PlatformIO bietet:

  1. Dual-Core Power: 240MHz für anspruchsvolle Aufgaben
  2. Connectivity: WiFi + Bluetooth integriert
  3. Low Power: Deep Sleep für Batterie-Betrieb
  4. Modern Tooling: PlatformIO + VS Code

Ideale Plattform für IoT-Projekte.


Bildprompts

  1. "ESP32 microcontroller with connected sensors, breadboard prototype"
  2. "PlatformIO IDE with code and serial monitor, embedded development"
  3. "IoT sensor node with battery and antenna, compact device design"

Quellen