# 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 #include #include // 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 #include #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 #include #include 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 #include 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 #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 #include #include 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(); // 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)