# 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

```bash
# VS Code Extension installieren
# Extensions → PlatformIO IDE

# Oder CLI Installation
pip install platformio

# Neues Projekt erstellen
pio project init --board esp32dev --ide vscode
```

```ini
; 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

```cpp
// 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)

```cpp
// 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

```cpp
// 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

```cpp
// 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

```cpp
// 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

```cpp
// 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

```cpp
// 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

- [PlatformIO ESP32 Documentation](https://docs.platformio.org/en/latest/platforms/espressif32.html)
- [Random Nerd Tutorials ESP32](https://randomnerdtutorials.com/vs-code-platformio-ide-esp32-esp8266-arduino/)
- [ESP32 Arduino Core](https://docs.espressif.com/projects/arduino-esp32/en/latest/)
- [Sunfounder ESP32 Guide](https://www.sunfounder.com/blogs/news/how-to-set-up-esp32-on-platformio-with-vs-code-complete-step-by-step-guide)
