154 lines
4.7 KiB
C
154 lines
4.7 KiB
C
// ESP8266 (ESP-01) per AT-Befehle am Arduino Uno: sendet die Messwerte als HTTP-POST
|
|
// an das Backend (POST /api/messwerte, Datenvertrag siehe backend/README.md).
|
|
#include <SoftwareSerial.h>
|
|
#include "wifi_config.h"
|
|
|
|
// --- ESP-01 Verdrahtung ---
|
|
// ESP TX -> Uno Pin 3 (SoftwareSerial-RX)
|
|
// ESP RX -> Uno Pin 4 (SoftwareSerial-TX), über Spannungsteiler 5 V -> 3,3 V (z. B. 1 kΩ / 2 kΩ)
|
|
// ESP VCC und CH_PD -> 3,3 V (Der ESP braucht bis ~300 mA, der 3,3-V-Pin des Uno liefert nur 50 mA!)
|
|
// ESP GND -> GND
|
|
const uint8_t ESP_RX_PIN = 3;
|
|
const uint8_t ESP_TX_PIN = 4;
|
|
SoftwareSerial espSerial(ESP_RX_PIN, ESP_TX_PIN);
|
|
|
|
bool wifiOk = false;
|
|
uint8_t sendeFehler = 0;
|
|
const uint8_t MAX_SENDEFEHLER = 3; // danach wird das WLAN neu verbunden
|
|
|
|
// Verwirft alles, was der ESP noch geschickt hat
|
|
void espLeeren() {
|
|
while (espSerial.available()) {
|
|
espSerial.read();
|
|
}
|
|
}
|
|
|
|
// Liest vom ESP, bis `ziel` (max. 11 Zeichen) vorkommt. false bei Timeout.
|
|
bool espWarteAuf(const char* ziel, unsigned long timeoutMs) {
|
|
char fenster[12];
|
|
const uint8_t n = strlen(ziel);
|
|
memset(fenster, 0, sizeof(fenster));
|
|
|
|
unsigned long start = millis();
|
|
while (millis() - start < timeoutMs) {
|
|
while (espSerial.available()) {
|
|
memmove(fenster, fenster + 1, n - 1);
|
|
fenster[n - 1] = espSerial.read();
|
|
if (memcmp(fenster, ziel, n) == 0) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool espBefehl(const __FlashStringHelper* befehl, const char* ziel = "OK", unsigned long timeoutMs = 2000) {
|
|
espLeeren();
|
|
espSerial.println(befehl);
|
|
return espWarteAuf(ziel, timeoutMs);
|
|
}
|
|
|
|
// Findet die Baudrate des Moduls. SoftwareSerial ist bei 115200 unzuverlässig, deshalb
|
|
// wird ein Modul mit 115200 (Werkseinstellung) dauerhaft auf 9600 umgestellt.
|
|
bool espBaudrateFinden() {
|
|
espSerial.begin(9600);
|
|
if (espBefehl(F("AT"))) {
|
|
return true;
|
|
}
|
|
|
|
espSerial.begin(115200);
|
|
espSerial.println(F("AT+UART_DEF=9600,8,1,0,0")); // neue AT-Firmware
|
|
delay(500);
|
|
espSerial.println(F("AT+CIOBAUD=9600")); // alte AT-Firmware
|
|
delay(500);
|
|
|
|
espSerial.begin(9600);
|
|
return espBefehl(F("AT"));
|
|
}
|
|
|
|
bool wifiVerbinden() {
|
|
wifiOk = false;
|
|
|
|
if (!espBaudrateFinden()) {
|
|
Serial.println(F("ESP8266 antwortet nicht (Pins, Versorgung, Baudrate pruefen)"));
|
|
return false;
|
|
}
|
|
espBefehl(F("ATE0")); // Echo aus
|
|
if (!espBefehl(F("AT+CWMODE=1"))) { // Station-Modus
|
|
Serial.println(F("ESP8266: CWMODE fehlgeschlagen"));
|
|
return false;
|
|
}
|
|
espBefehl(F("AT+CIPMUX=0")); // eine Verbindung gleichzeitig
|
|
|
|
Serial.print(F("Verbinde mit WLAN "));
|
|
Serial.println(F(WLAN_SSID));
|
|
espLeeren();
|
|
espSerial.print(F("AT+CWJAP=\""));
|
|
espSerial.print(F(WLAN_SSID));
|
|
espSerial.print(F("\",\""));
|
|
espSerial.print(F(WLAN_PASSWORT));
|
|
espSerial.println(F("\""));
|
|
wifiOk = espWarteAuf("OK", 20000);
|
|
|
|
Serial.println(wifiOk ? F("WLAN verbunden") : F("WLAN-Verbindung fehlgeschlagen (SSID/Passwort?)"));
|
|
return wifiOk;
|
|
}
|
|
|
|
// Schickt einen Messwertsatz per HTTP-POST an das Backend. true, wenn der Server mit 200 antwortet.
|
|
bool sendeMesswerte(float temperatur, float luftfeuchte, uint16_t eco2, uint16_t tvoc) {
|
|
if (!wifiOk && !wifiVerbinden()) {
|
|
return false;
|
|
}
|
|
|
|
char temp[8], feuchte[8];
|
|
dtostrf(temperatur, 1, 1, temp);
|
|
dtostrf(luftfeuchte, 1, 1, feuchte);
|
|
|
|
char body[72];
|
|
snprintf_P(body, sizeof(body),
|
|
PSTR("{\"temperatur\":%s,\"luftfeuchte\":%s,\"eco2\":%u,\"tvoc\":%u}"),
|
|
temp, feuchte, eco2, tvoc);
|
|
|
|
char anfrage[200];
|
|
int laenge = snprintf_P(anfrage, sizeof(anfrage),
|
|
PSTR("POST /api/messwerte HTTP/1.1\r\nHost: %s:%u\r\n"
|
|
"Content-Type: application/json\r\nContent-Length: %u\r\n"
|
|
"Connection: close\r\n\r\n%s"),
|
|
SERVER_IP, (unsigned int)SERVER_PORT, (unsigned int)strlen(body), body);
|
|
|
|
espLeeren();
|
|
espSerial.print(F("AT+CIPSTART=\"TCP\",\""));
|
|
espSerial.print(F(SERVER_IP));
|
|
espSerial.print(F("\","));
|
|
espSerial.println(SERVER_PORT);
|
|
bool ok = espWarteAuf("OK", 5000);
|
|
|
|
if (ok) {
|
|
espLeeren();
|
|
espSerial.print(F("AT+CIPSEND="));
|
|
espSerial.println(laenge);
|
|
ok = espWarteAuf(">", 2000);
|
|
}
|
|
if (ok) {
|
|
espSerial.print(anfrage);
|
|
ok = espWarteAuf("200 OK", 4000);
|
|
}
|
|
|
|
// Verbindung schließen (der Server hat sie meist schon beendet, dann kommt ERROR)
|
|
espSerial.println(F("AT+CIPCLOSE"));
|
|
espWarteAuf("OK", 300);
|
|
|
|
if (ok) {
|
|
sendeFehler = 0;
|
|
Serial.println(F("An Server gesendet"));
|
|
return true;
|
|
}
|
|
|
|
Serial.println(F("Senden fehlgeschlagen (Server-IP/Port/Firewall pruefen)"));
|
|
if (++sendeFehler >= MAX_SENDEFEHLER) {
|
|
sendeFehler = 0;
|
|
wifiOk = false; // beim nächsten Senden neu verbinden
|
|
}
|
|
return false;
|
|
}
|