Vollständige Next.js 15 Portfolio-Website mit: - Blog-System mit 100+ Artikeln - Supabase-Integration - Responsive Design mit Tailwind CSS - TypeScript-Konfiguration - Testing-Setup mit Vitest und Playwright Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
735 lines
17 KiB
Markdown
735 lines
17 KiB
Markdown
# 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)
|