Unterschiede
Hier werden die Unterschiede zwischen zwei Versionen angezeigt.
| Beide Seiten der vorigen RevisionVorhergehende ÜberarbeitungNächste Überarbeitung | Vorhergehende Überarbeitung | ||
| www-messdatenerfassung-sas:sensor_code [2026/09/12 13:31] – [Programmcode der Sensor-Boxen] biebl | www-messdatenerfassung-sas:sensor_code [2026/10/19 20:01] (aktuell) – biebl | ||
|---|---|---|---|
| Zeile 1: | Zeile 1: | ||
| ====== Programmcode der Sensor-Boxen ====== | ====== Programmcode der Sensor-Boxen ====== | ||
| - | ^ Seite ^ Inhalt | + | * [[biebl: |
| - | | [[www-messdatenerfassung-sas: | + | |
| + | {{: | ||
| + | |||
| + | ^ Seite ^ Inhalt | ||
| + | | [[www-messdatenerfassung-sas: | ||
| | [[www-messdatenerfassung-sas: | | [[www-messdatenerfassung-sas: | ||
| - | | [[www-messdatenerfassung-sas: | + | | [[www-messdatenerfassung-sas: |
| - | | [[www-messdatenerfassung-sas: | + | | [[www-messdatenerfassung-sas: |
| - | | [[www-messdatenerfassung-sas: | + | | [[www-messdatenerfassung-sas: |
| - | | [[Platinenschaltung|Schaltung]] | Platine | | + | | [[Platinenschaltung|Schaltung]] |
| - | | [[3D-Gehäuse|Gehäuse]] | 3D-Druck oder LASER-Cutter | | + | | [[3D-Gehäuse|Gehäuse]] |
| - | | [[Unterrichtseinsatz|Schule]] | + | | [[Unterrichtseinsatz|Schule]] |
| - | | [[Sonstiges|Sonstiges]]| dies und das....| | + | | [[Sonstiges|Sonstiges]] |
| Zeile 972: | Zeile 977: | ||
| Angepasst gegenüber der ursprünglichen Fassung: Netzwerk-Konfiguration | Angepasst gegenüber der ursprünglichen Fassung: Netzwerk-Konfiguration | ||
| auf das Bereich-A/ | auf das Bereich-A/ | ||
| - | dieser Seite). Messintervall | + | dieser Seite). |
| - | belassen | + | gewählten Bereich gekoppelt: Bereich A (mobiles Testnetz) = 10 |
| - | Schwankungen | + | Sekunden |
| - | Für den EmonCMS-Feed entsprechend Interval 20 verwenden. | + | (Schulnetz, Test- und Livebetrieb) = 20 Sekunden für den |
| + | Produktivbetrieb im Klassenzimmer. Beim Umschalten zwischen den | ||
| + | Bereichen ändert sich das Intervall automatisch mit - keine | ||
| + | zusätzliche Anpassung nötig. | ||
| + | Interval | ||
| + | je nachdem, mit welchem Bereich zuletzt gemessen wurde. | ||
| </ | </ | ||
| Zeile 985: | Zeile 995: | ||
| | | ||
| | | ||
| - | | + | |
| | | ||
| | | ||
| Zeile 992: | Zeile 1002: | ||
| | | ||
| | | ||
| - | | + | |
| + | | ||
| + | | ||
| | | ||
| #include < | #include < | ||
| Zeile 1066: | Zeile 1078: | ||
| /*** 3) Messintervall (Langzeitbetrieb) *****************************************************/ | /*** 3) Messintervall (Langzeitbetrieb) *****************************************************/ | ||
| - | const int Schreibpause = 20000; | + | const int Schreibpause = NETZ_A_MOBIL ? 10000 : 20000; |
| /*** 4) Sensor-Hardware *********************************************************************/ | /*** 4) Sensor-Hardware *********************************************************************/ | ||
| Zeile 1523: | Zeile 1535: | ||
| </ | </ | ||
| - | ----------------------------------------------------alt - ohne RASPI-time ---------------------- | + | ---- |
| - | ====== Programmcode der Sensor-Boxen ====== | + | |
| - | Diese Seite enthält den vollständigen Arduino-Code aller Sensor-Boxen, | + | ===== Temperatursensor Innen/ |
| - | ===== Netzwerk-Auswahlschema | + | Zwei DS18B20-Temperatursensoren an einem gemeinsamen Datenpin |
| - | Alle Sketches auf dieser Seite nutzen dasselbe Schema, um zwischen drei Netzwerk-/ | + | **Verkabelung:** |
| - | * **Bereich A – SAS-ARDUINO-mobil: | + | ^ Anschluss ^ Verbunden mit ^ Hinweis ^ |
| - | * **Bereich B – SAS-ARDUINO, Schul-Testbetrieb: | + | | DS18B20 VCC (beide) | 3,3V oder 5V | | |
| - | | + | | DS18B20 GND (beide) | GND | | |
| + | | DS18B20 DATA (beide) | D2 (GPIO4) | Beide Sensoren parallel an denselben Pin | | ||
| + | | 4,7 kΩ Widerstand | zwischen DATA und VCC | Pull-Up, nur **ein** Widerstand für beide Sensoren | | ||
| - | Im Kopf jedes Sketches wird **genau eine** der drei Zeilen | + | **Sensor-Adressen ermitteln:** Jeder DS18B20 hat eine feste, einmalige 8-Byte-Adresse. Den Sketch zuerst mit den Beispieladressen hochladen und den seriellen Monitor (115200 Baud) öffnen – beim Start werden alle am Bus gefundenen Sensoren mit ihrer Adresse ausgegeben (Zeilen |
| - | <code cpp> | + | **Messintervall: |
| - | #define NETZ_A_MOBIL true // SAS-ARDUINO-mobil | + | |
| - | #define NETZ_B_TEST | + | |
| - | #define NETZ_C_LIVE | + | |
| - | #if (NETZ_A_MOBIL + NETZ_B_TEST + NETZ_C_LIVE) != 1 | + | **Gesendete Felder: |
| - | #error "Bitte genau EINE der drei NETZ_-Zeilen oben auf true setzen!" | + | |
| - | #endif | + | |
| - | </ | + | |
| - | + | ||
| - | Darunter folgen drei '' | + | |
| - | + | ||
| - | < | + | |
| - | Wichtig bei Weitergabe an andere Schulen: Bereich A (SAS-ARDUINO-mobil) | + | |
| - | enthält persönliche/ | + | |
| - | Veröffentlichung KOMPLETT gelöscht werden | + | |
| - | Block). Bereich B und C beziehen sich auf das isolierte, nach der | + | |
| - | Hauptanleitung bei jeder Schule identisch aufgebaute Schulnetz | + | |
| - | (" | + | |
| - | müssen andere Schulen nur ihr eigenes WLAN-Passwort und ihre eigenen | + | |
| - | API-Keys eintragen. | + | |
| - | </ | + | |
| - | + | ||
| - | ---- | + | |
| - | + | ||
| - | ===== Testsensor (ohne echte Sensoren) ===== | + | |
| - | + | ||
| - | Dient ausschließlich dazu, die EmonCMS-Einrichtung (Inputs, Feeds, Dashboard) zu testen, **bevor** eine echte Sensor-Box angeschlossen ist. Es werden keine Sensoren ausgelesen – die Werte für Temperatur, Luftfeuchtigkeit und CO2 werden im Code simuliert (Temperatur/ | + | |
| - | + | ||
| - | < | + | |
| - | Verwendet SENSOR_ID 999 als reservierte Test-Kennung - bitte NICHT | + | |
| - | für echte Sensor-Boxen verwenden (siehe SENSOR_ID-Übersichtstabelle | + | |
| - | in der Hauptanleitung). | + | |
| - | </ | + | |
| <code cpp> | <code cpp> | ||
| / | / | ||
| - | | + | |
| * | * | ||
| - | | + | |
| - | | + | |
| + | | ||
| + | | ||
| * | * | ||
| - | | + | |
| - | | + | |
| + | | ||
| + | | ||
| + | | ||
| + | * | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| | | ||
| + | |||
| #include < | #include < | ||
| #include < | #include < | ||
| #include < | #include < | ||
| + | #include < | ||
| + | #include < | ||
| - | #define SENSOR_ID 999 // Reservierte Test-ID - NICHT für echte Boxen verwenden! | + | /*** 1) Identität **************************************************************************/ |
| - | # | + | # |
| + | /*** 2) Netzwerk ***************************************************************************/ | ||
| + | // Aus SENSOR_ID werden Client-Name (" | ||
| char CLIENT[8]; | char CLIENT[8]; | ||
| IPAddress ip(192, 168, 1, SENSOR_ID); | IPAddress ip(192, 168, 1, SENSOR_ID); | ||
| - | IPAddress SERVER_IP(192, | ||
| - | const int httpPort = 80; | ||
| - | // ============================================================ | + | IPAddress SERVER_IP(192, |
| - | // NETZWERK-AUSWAHL - genau EINE der drei Zeilen auf " | + | |
| - | // ============================================================ | + | |
| - | #define NETZ_A_MOBIL true // SAS-ARDUINO-mobil (persönliches Testnetz) | + | |
| - | #define NETZ_B_TEST | + | |
| - | #define NETZ_C_LIVE | + | |
| - | + | ||
| - | #if (NETZ_A_MOBIL + NETZ_B_TEST + NETZ_C_LIVE) != 1 | + | |
| - | #error "Bitte genau EINE der drei NETZ_-Zeilen oben auf true setzen!" | + | |
| - | #endif | + | |
| - | + | ||
| - | // ---- BEREICH A: SAS-ARDUINO-mobil (persönliches Testnetz) ------------- | + | |
| - | // Vor Weitergabe des Codes an andere Schulen DIESEN BLOCK LÖSCHEN! | + | |
| - | #if NETZ_A_MOBIL | + | |
| - | const char* ssid = " | + | |
| - | const char* password = "< | + | |
| - | const char* apikey | + | |
| - | #endif | + | |
| - | + | ||
| - | // ---- BEREICH B: SAS-ARDUINO, | + | |
| - | #if NETZ_B_TEST | + | |
| - | const char* ssid = " | + | |
| - | const char* password = "< | + | |
| - | const char* apikey | + | |
| - | #endif | + | |
| - | + | ||
| - | // ---- BEREICH C: SAS-ARDUINO, | + | |
| - | #if NETZ_C_LIVE | + | |
| - | const char* ssid = " | + | |
| - | const char* password = "< | + | |
| - | const char* apikey | + | |
| - | #endif | + | |
| - | + | ||
| - | IPAddress gateway(192, | + | |
| - | IPAddress subnet (255, 255, 255, 0); | + | |
| - | IPAddress dns (0, 0, 0, 0); | + | |
| - | + | ||
| - | void setup() { | + | |
| - | Serial.begin(115200); | + | |
| - | delay(200); | + | |
| - | snprintf(CLIENT, | + | |
| - | Serial.printf(" | + | |
| - | + | ||
| - | WiFi.mode(WIFI_STA); | + | |
| - | WiFi.config(ip, | + | |
| - | WiFi.begin(ssid, | + | |
| - | + | ||
| - | Serial.print(" | + | |
| - | while (WiFi.status() != WL_CONNECTED) { | + | |
| - | delay(300); | + | |
| - | Serial.print(" | + | |
| - | } | + | |
| - | Serial.printf(" | + | |
| - | } | + | |
| - | + | ||
| - | void loop() { | + | |
| - | // ---- Simulierte Werte (KEINE echten Sensoren!) ---- | + | |
| - | float temp = 20.0 + 5.0 * sin(millis() / 60000.0); | + | |
| - | float hum = 45.0 + 10.0 * sin(millis() / 90000.0); | + | |
| - | static int fakeCo2 = 400; | + | |
| - | fakeCo2 += random(-5, 6); // wandert leicht um 400 ppm | + | |
| - | + | ||
| - | char path[96]; | + | |
| - | snprintf(path, | + | |
| - | char body[128]; | + | |
| - | snprintf(body, | + | |
| - | + | ||
| - | if (WiFi.status() == WL_CONNECTED) { | + | |
| - | WiFiClient client; | + | |
| - | HTTPClient http; | + | |
| - | if (http.begin(client, | + | |
| - | http.addHeader(" | + | |
| - | int code = http.POST(body); | + | |
| - | Serial.printf(" | + | |
| - | http.end(); | + | |
| - | } | + | |
| - | } else { | + | |
| - | Serial.println(" | + | |
| - | WiFi.begin(ssid, | + | |
| - | } | + | |
| - | + | ||
| - | delay(SENDEINTERVALL_MS); | + | |
| - | } | + | |
| - | </ | + | |
| - | + | ||
| - | Gesendete Felder: '' | + | |
| - | + | ||
| - | ---- | + | |
| - | + | ||
| - | ===== Einfacher Sensor: nur BME280 (Temperatur, | + | |
| - | + | ||
| - | Ein echter, aber minimaler Sensor – guter nächster Schritt nach dem simulierten Testsensor, bevor die vollständigen Boxen (Klimamessbox, | + | |
| - | + | ||
| - | **Verkabelung (I²C-Modus): | + | |
| - | + | ||
| - | ^ BME280-Pin ^ Verbunden mit ^ Hinweis ^ | + | |
| - | | VCC | 3,3V | | | + | |
| - | | GND | GND | | | + | |
| - | | SCL | D1 (GPIO5) | Standard-I²C-Clock beim D1 Mini | | + | |
| - | | SDA | D2 (GPIO4) | Standard-I²C-Data beim D1 Mini | | + | |
| - | | CSB | 3,3V | Zwingend nötig, um den I²C-Modus zu aktivieren (statt SPI) | | + | |
| - | | SDO | **GND** | Setzt die I²C-Adresse auf **0x76** – dieselbe Adresse wie bei der Klimamessbox (Teil 3), bewusst einheitlich gehalten, damit später auch die Raummessbox (aktuell noch BMP180+DHT22) beim geplanten Umstieg auf BME280 ohne Adresskonflikt eingebunden werden kann | | + | |
| - | + | ||
| - | <code cpp> | + | |
| - | / | + | |
| - | | + | |
| - | * | + | |
| - | | + | |
| - | | + | |
| - | * | + | |
| - | | + | |
| - | | + | |
| - | | + | |
| - | #include < | + | |
| - | #include < | + | |
| - | #include < | + | |
| - | #include < | + | |
| - | #include < | + | |
| - | + | ||
| - | #define SENSOR_ID 998 // Test-ID für diesen einfachen Sensor - vor Produktivbetrieb anpassen! | + | |
| - | + | ||
| - | // ==== TEST-EINSTELLUNG ======================================================= | + | |
| - | // 10 Sekunden statt der später üblichen 30-120 Sekunden - bewusst kurz gehalten, | + | |
| - | // damit sich Änderungen beim Testen sofort in EmonCMS zeigen. Für Dauerbetrieb | + | |
| - | // erhöhen (z. B. auf 300000 wie bei der Klimamessbox), | + | |
| - | // unnötig zu belasten! | + | |
| - | #define SENDEINTERVALL_MS 10000 | + | |
| - | // ============================================================================== | + | |
| - | + | ||
| - | #define BME280_ADDRESS 0x76 // SDO auf GND → Adresse 0x76 (SDO auf 3,3V wäre 0x77) | + | |
| - | #define ALTITUDE 42.0 // Höhe über Meeresspiegel in Metern, für Luftdruck-Korrektur | + | |
| - | + | ||
| - | Adafruit_BME280 bme; | + | |
| - | + | ||
| - | char CLIENT[8]; | + | |
| - | IPAddress ip(192, 168, 1, SENSOR_ID); | + | |
| - | IPAddress SERVER_IP(192, | + | |
| const int httpPort = 80; | const int httpPort = 80; | ||
| Zeile 1737: | Zeile 1596: | ||
| // NETZWERK-AUSWAHL - genau EINE der drei Zeilen auf " | // NETZWERK-AUSWAHL - genau EINE der drei Zeilen auf " | ||
| // ============================================================ | // ============================================================ | ||
| - | #define NETZ_A_MOBIL true // SAS-ARDUINO-mobil (persönliches Testnetz) | + | #define NETZ_A_MOBIL |
| - | #define NETZ_B_TEST | + | #define NETZ_B_TEST |
| - | #define NETZ_C_LIVE | + | #define NETZ_C_LIVE |
| #if (NETZ_A_MOBIL + NETZ_B_TEST + NETZ_C_LIVE) != 1 | #if (NETZ_A_MOBIL + NETZ_B_TEST + NETZ_C_LIVE) != 1 | ||
| Zeile 1769: | Zeile 1628: | ||
| IPAddress gateway(192, | IPAddress gateway(192, | ||
| IPAddress subnet (255, 255, 255, 0); | IPAddress subnet (255, 255, 255, 0); | ||
| - | IPAddress dns (0, 0, 0, 0); | + | IPAddress dns (0, |
| + | static bool g_lastSendFailed = false; | ||
| - | void setup() { | + | /*** 3) Intervall **************************************************************************/ |
| - | | + | // Pause am Ende jedes Zyklus. Messung |
| - | delay(200); | + | // 1 s weniger als das gewünschte Gesamtintervall: |
| - | snprintf(CLIENT, sizeof(CLIENT), " | + | const int SCHREIBPAUSE_MS |
| - | | + | |
| - | Wire.begin(); | + | /*** 4) Sensoren: 2× DS18B20 ***************************************************************/ |
| - | if (!bme.begin(BME280_ADDRESS)) { | + | #define ONE_WIRE_BUS D2 |
| - | Serial.println(" | + | |
| - | while (true) delay(100); | + | |
| - | } | + | |
| - | Serial.println(" | + | |
| - | WiFi.mode(WIFI_STA); | + | OneWire oneWire(ONE_WIRE_BUS); |
| - | | + | DallasTemperature sensors(& |
| - | WiFi.begin(ssid, | + | |
| - | Serial.print(" | + | // WICHTIG: Hier die echten Sensor-Adressen eintragen! |
| - | while (WiFi.status() != WL_CONNECTED) { | + | // Die Adressen erscheinen beim Start im seriellen Monitor |
| - | delay(300); | + | // " |
| - | Serial.print("."); | + | // Format: {0x28, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX} |
| - | } | + | |
| - | Serial.printf(" | + | |
| - | } | + | |
| - | void loop() { | + | // Sensor INNEN (Ti) |
| - | float hum | + | DeviceAddress SENSOR_ADDR_INNEN |
| - | float temp = bme.readTemperature(); | + | |
| - | float pressure = bme.readPressure() / 100.0F; | + | |
| - | float p0 = bme.seaLevelForAltitude(ALTITUDE, pressure); // auf Meereshöhe umgerechnet | + | |
| - | char path[96]; | + | // Sensor AUSSEN (Ta) |
| - | snprintf(path, | + | DeviceAddress SENSOR_ADDR_AUSSEN |
| - | char body[128]; | + | |
| - | snprintf(body, | + | |
| - | if (WiFi.status() == WL_CONNECTED) { | + | // Flags, ob Sensoren gefunden wurden |
| - | WiFiClient client; | + | bool sensorInnenOK |
| - | HTTPClient http; | + | bool sensorAussenOK |
| - | if (http.begin(client, SERVER_IP.toString(), | + | |
| - | | + | |
| - | int code = http.POST(body); | + | |
| - | | + | |
| - | http.end(); | + | |
| - | } | + | |
| - | } else { | + | |
| - | Serial.println(" | + | |
| - | WiFi.begin(ssid, | + | |
| - | } | + | |
| - | delay(SENDEINTERVALL_MS); | + | /*** 5) Zustände |
| - | } | + | static bool |
| - | </code> | + | static uint32_t g_bootMs |
| - | + | const | |
| - | Gesendete Felder: '' | + | const uint32_t |
| - | + | const uint8_t | |
| - | ---- | + | |
| - | + | ||
| - | ===== Klimamessbox (Außenbereich) ===== | + | |
| - | + | ||
| - | Sensoren: BME280 (Temperatur, | + | |
| - | + | ||
| - | < | + | |
| - | Angepasst gegenüber der ursprünglichen Fassung: Netzwerk-Konfiguration | + | |
| - | auf das Bereich-A/ | + | |
| - | dieser Seite), MODE_LIVE-Umschalter entfällt dadurch (die Netzauswahl | + | |
| - | übernimmt das jetzt). Messintervall von 120 auf 300 Sekunden (5 Min.) | + | |
| - | geändert - schont den SDS011-Lüfter im Dauerbetrieb spürbar, ohne bei | + | |
| - | der eher langsam schwankenden Außenluftbelastung relevant an | + | |
| - | Aussagekraft zu verlieren. | + | |
| - | </ | + | |
| - | + | ||
| - | <code cpp> | + | |
| - | /******************************************************************************************** | + | |
| - | | + | |
| - | * | + | |
| - | | + | |
| - | | + | |
| - | | + | |
| - | | + | |
| - | | + | |
| - | * | + | |
| - | | + | |
| - | | + | |
| - | * | + | |
| - | | + | |
| - | | + | |
| - | | + | |
| - | + | ||
| - | // ========== BIBLIOTHEKEN ========== | + | |
| - | #include < | + | |
| - | #include < | + | |
| - | #include < | + | |
| - | #include < | + | |
| - | #include < | + | |
| - | #include < | + | |
| - | + | ||
| - | // ========== KONFIGURATION (HIER NUR DIESE ZEILEN ÄNDERN!) ========== | + | |
| - | // Jeder Sensor bekommt eine eindeutige Nummer. Diese Nummer bestimmt: | + | |
| - | // - Den Namen des Sensors (z.B. " | + | |
| - | // - Die IP-Adresse im Netzwerk (z.B. 192.168.1.134) | + | |
| - | + | ||
| - | #define SENSOR_ID 134 // <<< | + | |
| - | + | ||
| - | // CO2-Kalibrierungs-Offset (wie bei der Raumklima-Box): | + | |
| - | // An echter Frischluft (z.B. früh morgens, wenig Verkehr) prüfen, was der Sensor anzeigt, | + | |
| - | // und den Offset so wählen, dass ca. 420-440 ppm (aktueller globaler Referenzwert) erreicht werden. | + | |
| - | #define CO2_OFFSET 40 | + | |
| - | + | ||
| - | // Gewünschtes GESAMT-Intervall zwischen zwei Sendungen an EmonCMS, in Millisekunden. | + | |
| - | // <<< | + | |
| - | // | + | |
| - | // der Rest wird als Wartezeit am Ende jedes Zyklus eingehalten (siehe Schreibpause unten). | + | |
| - | // | + | |
| - | // sonst entstehen unnötige Interpolationslücken in den Graphen. | + | |
| - | #define MESSINTERVALL_MS 300000 | + | |
| - | + | ||
| - | // ========== DEBUG-MODUS ========== | + | |
| - | #define DEBUG true // <<< | + | |
| - | + | ||
| - | // ========== DEBUG-MAKROS ========== | + | |
| - | #if DEBUG | + | |
| - | #define DBGPRINT(x) | + | |
| - | #define DBGPRINTLN(x) Serial.println(x) | + | |
| - | #define DBGF(fmt, ...) Serial.printf(fmt, | + | |
| - | #else | + | |
| - | #define DBGPRINT(x) | + | |
| - | #define DBGPRINTLN(x) | + | |
| - | #define DBGF(fmt, ...) | + | |
| - | #endif | + | |
| - | + | ||
| - | // ========== NETZWERK-KONFIGURATION ========== | + | |
| - | char CLIENT[8]; | + | |
| - | IPAddress ip(192, 168, 1, SENSOR_ID); | + | |
| - | + | ||
| - | IPAddress SERVER_IP(192, | + | |
| - | const int httpPort = 80; // HTTP-Port (Standard) | + | |
| - | + | ||
| - | // ============================================================ | + | |
| - | // NETZWERK-AUSWAHL - genau EINE der drei Zeilen auf " | + | |
| - | // ============================================================ | + | |
| - | #define NETZ_A_MOBIL true // SAS-ARDUINO-mobil (persönliches Testnetz) | + | |
| - | #define NETZ_B_TEST | + | |
| - | #define NETZ_C_LIVE | + | |
| - | + | ||
| - | #if (NETZ_A_MOBIL + NETZ_B_TEST + NETZ_C_LIVE) != 1 | + | |
| - | #error "Bitte genau EINE der drei NETZ_-Zeilen oben auf true setzen!" | + | |
| - | #endif | + | |
| - | + | ||
| - | // ---- BEREICH A: SAS-ARDUINO-mobil (persönliches Testnetz) ------------- | + | |
| - | // Vor Weitergabe des Codes an andere Schulen DIESEN BLOCK LÖSCHEN! | + | |
| - | #if NETZ_A_MOBIL | + | |
| - | const char* ssid = " | + | |
| - | const char* password | + | |
| - | const char* apikey | + | |
| - | #endif | + | |
| - | + | ||
| - | // ---- BEREICH B: SAS-ARDUINO, | + | |
| - | #if NETZ_B_TEST | + | |
| - | const char* ssid = " | + | |
| - | const char* password = "< | + | |
| - | const char* apikey | + | |
| - | #endif | + | |
| - | + | ||
| - | // ---- BEREICH C: SAS-ARDUINO, | + | |
| - | #if NETZ_C_LIVE | + | |
| - | const char* ssid = " | + | |
| - | const char* password = "< | + | |
| - | const char* apikey | + | |
| - | #endif | + | |
| - | + | ||
| - | // Standard-Gateway (Router) und Subnetzmaske | + | |
| - | IPAddress gateway(192, | + | |
| - | IPAddress subnet (255, 255, 255, 0); | + | |
| - | IPAddress dns (0, 0, 0, 0); // DNS nicht verwendet (0.0.0.0 = deaktiviert) | + | |
| - | + | ||
| - | // ========== SENSOR-HARDWARE ========== | + | |
| - | + | ||
| - | // === BME280 (Temperatur, | + | |
| - | // Der BME280 wird über I²C angesprochen (Standard-Pins D1=SCL, D2=SDA) | + | |
| - | Adafruit_BME280 bme; // BME280-Objekt erstellen | + | |
| - | #define BME280_ADDRESS 0x76 // I²C-Adresse des BME280 (manchmal 0x77) | + | |
| - | #define ALTITUDE 42.0 // Höhe über Meeresspiegel in Metern (für Luftdruck-Korrektur) | + | |
| - | + | ||
| - | // === MH-Z19B CO2-Sensor - an 5V angeschlossen und mit Spannungsteiler zur Datenübertragung an den D1-mini === | + | |
| - | // RX = D6 (GPIO12) mit Spannungsteiler 5V→3.3V (1kΩ + 2kΩ) | + | |
| - | // TX = D5 (GPIO14) | + | |
| - | SoftwareSerial mhzSerial(12, | + | |
| - | + | ||
| - | // Kommando zum Auslesen des CO2-Wertes (wird zum Sensor gesendet) | + | |
| - | byte cmdReadCO2[] = {0xFF, 0x01, 0x86, 0, 0, 0, 0, 0, 0x79}; | + | |
| - | + | ||
| - | // Kommando zum Abschalten der automatischen Baseline-Korrektur (ABC). | + | |
| - | // ABC geht davon aus, dass der niedrigste 24h-Wert ~400 ppm (Frischluft) entspricht. | + | |
| - | // Das passt für Innenraum-Sensoren, | + | |
| - | // Sensor in der Stadt (Hintergrundwert schwankt/ | + | |
| - | // Sensor über die Zeit falsch kalibrieren. Wird einmalig beim Boot gesendet. | + | |
| - | byte cmdABCOff[] = {0xFF, 0x01, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86}; | + | |
| - | + | ||
| - | // === SDS011 Feinstaub-Sensor - an 5V angeschlossen aber ohne Spannungsteiler zur Datenübertragung an den D1-mini === | + | |
| - | // RX = D7 (GPIO13) OHNE Spannungsteiler | + | |
| - | // TX = D8 (GPIO15) | + | |
| - | SoftwareSerial sdsSerial(13, | + | |
| - | + | ||
| - | // SDS011 Sleep-Kommandos (um den Sensor ein-/ | + | |
| - | byte sds011_sleep[] | + | |
| - | byte sds011_wakeup[] = {0xAA, 0xB4, 0x06, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x06, 0xAB}; | + | |
| - | + | ||
| - | // === LDR (Lichtsensor) === | + | |
| - | #define LDR_PIN A0 | + | |
| - | const int LDR_INVERTED_OFFSET = 450; // Offset für Invertierung (hell→klein, | + | |
| - | + | ||
| - | // ========== SYSTEM-PARAMETER ========== | + | |
| - | const long schreibpauseRoh = (long)MESSINTERVALL_MS - 10000; | + | |
| - | const int Schreibpause = (int)(schreibpauseRoh > 2000 ? schreibpauseRoh : 2000); | + | |
| - | const uint32_t BOOT_UPLOAD_DEADLINE_MS | + | |
| - | const uint32_t | + | |
| - | const uint8_t WIFI_BOOT_TRIES = 3; // Anzahl der WLAN-Verbindungsversuche beim Start | + | |
| - | + | ||
| - | const float SENSOR_ERROR_VALUE = -999.0f; | + | |
| - | const int CO2_ERROR_VALUE = -1; | + | |
| - | + | ||
| - | // ========== GLOBALE VARIABLEN ========== | + | |
| - | static bool g_firstCo2Init = true; | + | |
| - | static bool g_uploadedOnce = false; | + | |
| - | static bool g_lastSendFailed = false; | + | |
| - | static uint32_t g_bootMs = 0; | + | |
| static uint32_t g_wifiDownSince = 0; | static uint32_t g_wifiDownSince = 0; | ||
| - | static uint32_t g_lastUploadMs = 0; | ||
| - | // ========== FUNKTIONS-PROTOTYPEN ========== | + | /*** 6) Prototypen *************************************************************************/ |
| bool ensureWifi(uint32_t timeoutMs = 15000); | bool ensureWifi(uint32_t timeoutMs = 15000); | ||
| bool wifiBootConnect(); | bool wifiBootConnect(); | ||
| bool netSoftReset(uint32_t connectTimeoutMs = 15000); | bool netSoftReset(uint32_t connectTimeoutMs = 15000); | ||
| bool tcpPreflight(uint16_t port = 80, uint16_t graceMs = 50); | bool tcpPreflight(uint16_t port = 80, uint16_t graceMs = 50); | ||
| - | bool readMHZ19B_Safe(int &ppm, int & | + | void sendData(float |
| - | bool readSDS011(float | + | void initSensors(); |
| - | void setSDS011Sleep(bool sleep); | + | |
| - | float readLDR(); | + | |
| - | void sendData(float feuchtigkeit, | + | |
| - | // ========================================== | + | /*** 7) SETUP ******************************************************************************/ |
| - | // ========== | + | |
| - | // ========================================== | + | |
| void setup() { | void setup() { | ||
| - | | + | Serial.begin(115200); |
| - | | + | delay(150); |
| - | delay(200); | + | |
| - | } | + | |
| - | + | ||
| - | DBGPRINTLN(" | + | |
| - | DBGPRINTLN(" | + | |
| - | DBGPRINTLN(" | + | |
| snprintf(CLIENT, | snprintf(CLIENT, | ||
| - | | + | |
| - | DBGF("IP-Adresse: | + | CLIENT, ip.toString().c_str()); |
| - | DBGPRINTLN(); | + | |
| - | | + | // WLAN-Start: 3 saubere Versuche mit Funk-Reset & Backoff |
| - | + | if (!wifiBootConnect()) { | |
| - | DBGPRINT(" | + | |
| - | if (!bme.begin(BME280_ADDRESS)) { | + | |
| - | | + | |
| - | DBGPRINTLN("BME280 | + | |
| - | | + | |
| - | | + | |
| - | DBGPRINTLN(" | + | |
| - | while (true) delay(100); | + | |
| } | } | ||
| - | DBGPRINTLN(" | ||
| - | | + | |
| - | Adafruit_BME280:: | + | initSensors(); |
| - | Adafruit_BME280:: | + | |
| - | Adafruit_BME280:: | + | |
| - | Adafruit_BME280:: | + | |
| - | | + | delay(250); |
| - | mhzSerial.begin(9600); | + | |
| - | | + | |
| - | mhzSerial.write(cmdABCOff, | + | |
| - | DBGPRINTLN(" | + | |
| - | DBGPRINTLN(" | + | |
| - | + | ||
| - | DBGPRINTLN(" | + | |
| - | sdsSerial.begin(9600); | + | |
| - | setSDS011Sleep(true); | + | |
| - | DBGPRINTLN(" | + | |
| - | + | ||
| - | DBGPRINTLN(" | + | |
| - | if (!wifiBootConnect()) { | + | |
| - | DBGPRINTLN(" | + | |
| - | DBGPRINTLN(" | + | |
| - | delay(2000); | + | |
| - | ESP.restart(); | + | |
| - | } | + | |
| + | // Startverzögerung (0..Schreibpause) entkoppelt Massenstart bei vielen Geräten | ||
| randomSeed(ESP.getChipId()); | randomSeed(ESP.getChipId()); | ||
| - | | + | |
| - | DBGF(" | + | |
| - | delay(startDelay); | + | |
| - | delay(3000); | ||
| g_bootMs = millis(); | g_bootMs = millis(); | ||
| - | |||
| - | DBGPRINTLN(" | ||
| } | } | ||
| - | // ========================================== | + | /*** 8) LOOP ********************************************************************************/ |
| - | // ========== | + | |
| - | // ========================================== | + | |
| void loop() { | void loop() { | ||
| - | | + | |
| + | sensors.requestTemperatures(); | ||
| + | delay(800); | ||
| - | | + | |
| - | bme.takeForcedMeasurement(); | + | float tempInnen |
| - | + | | |
| - | float hum = bme.readHumidity(); | + | |
| - | float temp = bme.readTemperature(); | + | if (tempInnen == DEVICE_DISCONNECTED_C || isnan(tempInnen) || tempInnen |
| - | | + | |
| - | float p0 = bme.seaLevelForAltitude(ALTITUDE, pressure); | + | |
| - | + | ||
| - | | + | |
| - | if (isnan(temp)) temp = SENSOR_ERROR_VALUE; | + | |
| - | if (isnan(p0) || p0 < 0) p0 = SENSOR_ERROR_VALUE; | + | |
| - | + | ||
| - | DBGPRINTLN(" | + | |
| - | DBGF(" | + | |
| - | DBGF(" | + | |
| - | | + | |
| - | + | ||
| - | DBGPRINT(" | + | |
| - | int ppm_val = CO2_ERROR_VALUE; | + | |
| - | int tco2 = 0; | + | |
| - | + | ||
| - | if (g_firstCo2Init) { | + | |
| - | uint32_t t0 = millis(); | + | |
| - | while (!readMHZ19B_Safe(ppm_val, | + | |
| - | delay(200); | + | |
| } | } | ||
| - | g_firstCo2Init = false; | ||
| - | DBGPRINTLN(" | ||
| - | } else { | ||
| - | readMHZ19B_Safe(ppm_val, | ||
| } | } | ||
| - | | + | |
| - | if (ppm_val > 0) { | + | float tempAussen |
| - | | + | if (sensorAussenOK) { |
| - | DBGPRINTLN(" | + | |
| - | | + | |
| - | | + | |
| - | DBGPRINTLN("FEHLER (verwende letzten Wert)"); | + | |
| - | DBGF(" | + | } |
| } | } | ||
| - | | + | |
| - | setSDS011Sleep(false); | + | |
| - | delay(10000); | + | |
| - | | + | // WLAN pflegen & ggf. reconnecten |
| - | float pm10 = SENSOR_ERROR_VALUE; | + | |
| - | + | ||
| - | if (readSDS011(pm25, | + | |
| - | DBGPRINTLN(" | + | |
| - | DBGF(" | + | |
| - | DBGF(" | + | |
| - | } else { | + | |
| - | DBGPRINTLN(" | + | |
| - | pm25 = 0; | + | |
| - | pm10 = 0; | + | |
| - | } | + | |
| - | + | ||
| - | setSDS011Sleep(true); | + | |
| - | + | ||
| - | DBGPRINT(" | + | |
| - | float ldrWert = readLDR(); | + | |
| - | DBGPRINTLN(" | + | |
| - | DBGF(" | + | |
| - | + | ||
| - | DBGPRINTLN(" | + | |
| ensureWifi(10000); | ensureWifi(10000); | ||
| - | | + | |
| - | sendData(hum, temp, p0, (float)ppm_uart, | + | sendData(tempInnen, tempAussen); |
| + | // Selbstheilung / Watchdog | ||
| uint32_t now = millis(); | uint32_t now = millis(); | ||
| - | + | | |
| - | | + | |
| - | | + | delay(300); ESP.restart(); |
| - | DBGPRINTLN(" | + | |
| - | delay(500); | + | |
| - | | + | |
| } | } | ||
| - | + | | |
| - | | + | |
| - | | + | delay(300); ESP.restart(); |
| - | DBGPRINTLN(" | + | |
| - | delay(500); | + | |
| - | | + | |
| } | } | ||
| - | | + | |
| - | | + | |
| - | DBGF(" | + | |
| - | DBGPRINTLN(" | + | |
| - | + | ||
| - | delay(actualDelay); | + | |
| } | } | ||
| - | // ========================================== | + | /*** 9) Sensor-Initialisierung *************************************************************/ |
| - | // ========== WLAN-FUNKTIONEN ========== | + | void initSensors() { |
| - | // ========================================== | + | |
| - | bool wifiBootConnect() { | + | |
| - | | + | |
| - | | + | |
| - | WiFi.setAutoConnect(true); | + | |
| - | WiFi.setAutoReconnect(true); | + | |
| - | WiFi.setOutputPower(20.5); | + | |
| - | WiFi.setSleepMode(WIFI_NONE_SLEEP); | + | |
| - | | + | |
| - | DBGF("[BOOT] WLAN-Verbindungsversuch | + | |
| - | WiFi.disconnect(true); | + | if (count < 2) { |
| - | | + | |
| - | WiFi.forceSleepBegin(); | + | |
| - | delay(200); | + | |
| - | WiFi.forceSleepWake(); | + | |
| - | delay(200); | + | |
| - | + | ||
| - | WiFi.config(ip, | + | |
| - | WiFi.begin(ssid, | + | |
| - | + | ||
| - | if (ensureWifi(20000)) { | + | |
| - | DBGF("[WIFI] Verbunden! IP: %s, RSSI: %d dBm\n", | + | |
| - | | + | |
| - | + | ||
| - | WiFiClient poke; | + | |
| - | poke.connect(gateway, | + | |
| - | poke.stop(); | + | |
| - | + | ||
| - | return true; | + | |
| - | } | + | |
| - | + | ||
| - | delay(1000 + random(0, 2000)); | + | |
| } | } | ||
| - | | + | |
| - | } | + | DeviceAddress tempAddr; |
| + | for (int i = 0; i < count; i++) { | ||
| + | if (sensors.getAddress(tempAddr, | ||
| - | bool ensureWifi(uint32_t timeoutMs) { | + | // Adresse ausgeben |
| - | if (WiFi.status() == WL_CONNECTED) { | + | |
| - | return true; | + | |
| - | } | + | if (tempAddr[j] < 16) Serial.print(" |
| - | + | | |
| - | DBGPRINT("[WIFI] Warte auf Verbindung"); | + | |
| - | uint32_t start = millis(); | + | |
| - | + | ||
| - | while (WiFi.status() != WL_CONNECTED && millis() - start < timeoutMs) { | + | |
| - | delay(250); | + | |
| - | DBGPRINT(" | + | |
| - | } | + | |
| - | + | ||
| - | if (WiFi.status() == WL_CONNECTED) { | + | |
| - | DBGPRINTLN(" | + | |
| - | g_wifiDownSince = 0; | + | |
| - | return true; | + | |
| - | } | + | |
| - | + | ||
| - | DBGPRINTLN(" | + | |
| - | + | ||
| - | if (!g_wifiDownSince) { | + | |
| - | g_wifiDownSince = millis(); | + | |
| - | } | + | |
| - | + | ||
| - | return false; | + | |
| - | } | + | |
| - | + | ||
| - | bool netSoftReset(uint32_t connectTimeoutMs) { | + | |
| - | DBGPRINTLN(" | + | |
| - | + | ||
| - | WiFi.disconnect(true); | + | |
| - | delay(200); | + | |
| - | WiFi.forceSleepBegin(); | + | |
| - | delay(200); | + | |
| - | WiFi.forceSleepWake(); | + | |
| - | delay(200); | + | |
| - | + | ||
| - | WiFi.config(ip, gateway, subnet, dns); | + | |
| - | WiFi.begin(ssid, | + | |
| - | + | ||
| - | return ensureWifi(connectTimeoutMs); | + | |
| - | } | + | |
| - | + | ||
| - | bool tcpPreflight(uint16_t port, uint16_t graceMs) { | + | |
| - | WiFiClient poke; | + | |
| - | + | ||
| - | if (!poke.connect(SERVER_IP, | + | |
| - | return false; | + | |
| - | } | + | |
| - | + | ||
| - | delay(graceMs); | + | |
| - | poke.stop(); | + | |
| - | + | ||
| - | return true; | + | |
| - | } | + | |
| - | + | ||
| - | // ========================================== | + | |
| - | // ========== SENSOR-FUNKTIONEN ========== | + | |
| - | // ========================================== | + | |
| - | bool readMHZ19B_Safe(int &ppm, int &tempC) { | + | |
| - | while (mhzSerial.available()) { | + | |
| - | mhzSerial.read(); | + | |
| - | } | + | |
| - | + | ||
| - | mhzSerial.write(cmdReadCO2, | + | |
| - | + | ||
| - | uint8_t resp[9]; | + | |
| - | int i = 0; | + | |
| - | uint32_t start = millis(); | + | |
| - | + | ||
| - | while (millis() - start < 200) { | + | |
| - | if (mhzSerial.available()) { | + | |
| - | | + | |
| - | if (i == 9) break; | + | |
| - | } | + | |
| - | } | + | |
| - | + | ||
| - | if (i != 9) { | + | |
| - | return false; | + | |
| - | } | + | |
| - | + | ||
| - | if (resp[0] != 0xFF || resp[1] != 0x86) { | + | |
| - | return false; | + | |
| - | } | + | |
| - | + | ||
| - | | + | |
| - | for (int k = 1; k < 8; k++) { | + | |
| - | sum += resp[k]; | + | |
| - | } | + | |
| - | sum = 0xFF - sum + 1; | + | |
| - | + | ||
| - | | + | |
| - | return false; | + | |
| - | } | + | |
| - | + | ||
| - | ppm = resp[2] * 256 + resp[3]; | + | |
| - | tempC = resp[4] - 40; | + | |
| - | + | ||
| - | return true; | + | |
| - | } | + | |
| - | + | ||
| - | bool readSDS011(float &pm25, float &pm10) { | + | |
| - | uint8_t buf[10]; | + | |
| - | int idx = 0; | + | |
| - | uint32_t start = millis(); | + | |
| - | + | ||
| - | while (millis() - start < 2000) { | + | |
| - | if (sdsSerial.available()) { | + | |
| - | uint8_t b = sdsSerial.read(); | + | |
| - | + | ||
| - | if (idx == 0 && b != 0xAA) { | + | |
| - | continue; | + | |
| } | } | ||
| - | | + | |
| + | if (memcmp(tempAddr, | ||
| + | Serial.println(" | ||
| + | sensorInnenOK = true; | ||
| + | sensors.setResolution(tempAddr, | ||
| - | if (idx == 10) { | + | |
| - | | + | |
| - | pm25 = (buf[2] + 256 * buf[3]) / 10.0f; | + | |
| - | | + | |
| - | return | + | |
| - | | + | |
| - | idx = 0; | + | |
| - | } | + | |
| - | } | + | |
| - | } | + | |
| - | } | + | |
| - | return false; | + | |
| - | } | + | |
| - | + | ||
| - | void setSDS011Sleep(bool sleep) { | + | |
| - | if (sleep) { | + | |
| - | sdsSerial.write(sds011_sleep, | + | |
| - | | + | |
| - | | + | |
| - | } | + | |
| - | delay(100); | + | |
| - | } | + | |
| - | + | ||
| - | float readLDR() { | + | |
| - | int raw = analogRead(LDR_PIN); | + | |
| - | raw = LDR_INVERTED_OFFSET - raw; | + | |
| - | + | ||
| - | if (raw < 0) raw = 0; | + | |
| - | + | ||
| - | return (float)raw; | + | |
| - | } | + | |
| - | + | ||
| - | // ========================================== | + | |
| - | // ========== DATEN SENDEN ========== | + | |
| - | // ========================================== | + | |
| - | void sendData(float feuchtigkeit, | + | |
| - | if (WiFi.status() != WL_CONNECTED) { | + | |
| - | g_lastSendFailed = true; | + | |
| - | DBGPRINTLN(" | + | |
| - | return; | + | |
| - | } | + | |
| - | + | ||
| - | if (DEBUG) { | + | |
| - | DBGF("[WIFI] Signalstärke: | + | |
| - | } | + | |
| - | + | ||
| - | char path[128]; | + | |
| - | snprintf(path, | + | |
| - | "/ | + | |
| - | | + | |
| - | + | ||
| - | char body[256]; | + | |
| - | snprintf(body, | + | |
| - | " | + | |
| - | feuchtigkeit, | + | |
| - | + | ||
| - | if (g_lastSendFailed) { | + | |
| - | bool ok = tcpPreflight(httpPort, | + | |
| - | DBGF(" | + | |
| - | } | + | |
| - | + | ||
| - | String hostStr = SERVER_IP.toString(); | + | |
| - | + | ||
| - | auto doPostOnce = [&](int attempt) -> int { | + | |
| - | if (WiFi.status() != WL_CONNECTED) { | + | |
| - | ensureWifi(8000); | + | |
| - | if (WiFi.status() != WL_CONNECTED) { | + | |
| - | DBGF(" | + | |
| - | return -10000; | + | |
| } | } | ||
| } | } | ||
| - | |||
| - | WiFiClient client; | ||
| - | client.setNoDelay(true); | ||
| - | client.setTimeout(15000); | ||
| - | |||
| - | HTTPClient http; | ||
| - | http.setTimeout(15000); | ||
| - | http.setReuse(false); | ||
| - | |||
| - | if (!http.begin(client, | ||
| - | DBGPRINTLN(" | ||
| - | return -2; | ||
| - | } | ||
| - | |||
| - | http.addHeader(" | ||
| - | http.addHeader(" | ||
| - | |||
| - | int code = http.POST(body); | ||
| - | String err = http.errorToString(code); | ||
| - | String payload = (code > 0) ? http.getString() : String(); | ||
| - | http.end(); | ||
| - | |||
| - | DBGF(" | ||
| - | |||
| - | if (code == 200) { | ||
| - | if (DEBUG && payload.length() > 0) { | ||
| - | DBGPRINT(" | ||
| - | DBGPRINTLN(payload); | ||
| - | } | ||
| - | DBGF(" | ||
| - | | ||
| - | g_uploadedOnce | ||
| - | g_lastSendFailed = false; | ||
| - | g_lastUploadMs | ||
| - | } | ||
| - | return code; | ||
| - | }; | ||
| - | |||
| - | int code = -9999; | ||
| - | |||
| - | for (int attempt = 1; attempt <= 3; ++attempt) { | ||
| - | code = doPostOnce(attempt); | ||
| - | if (code == 200) return; | ||
| - | delay(250 * attempt); | ||
| } | } | ||
| - | | + | |
| - | if (netSoftReset(15000)) { | + | if (sensorInnenOK |
| - | code = doPostOnce(4); | + | Serial.println(" |
| - | if (code == 200) return; | + | |
| - | } | + | |
| - | + | ||
| - | DBGPRINTLN(" | + | |
| - | if (netSoftReset(15000)) { | + | |
| - | code = doPostOnce(5); | + | |
| - | if (code == 200) return; | + | |
| - | } | + | |
| - | + | ||
| - | g_lastSendFailed = true; | + | |
| - | DBGF(" | + | |
| - | | + | |
| - | } | + | |
| - | </ | + | |
| - | + | ||
| - | ---- | + | |
| - | + | ||
| - | ===== Raummessbox | + | |
| - | + | ||
| - | Sensoren: DHT22 (Luftfeuchte), | + | |
| - | + | ||
| - | < | + | |
| - | Angepasst gegenüber der ursprünglichen Fassung: Netzwerk-Konfiguration | + | |
| - | auf das Bereich-A/ | + | |
| - | dieser Seite). Messintervall bei den ursprünglichen 20 Sekunden | + | |
| - | belassen (gut geeignet für ein Klassenzimmer, | + | |
| - | Schwankungen - z. B. CO2-Anstieg beim Lüften - interessant sind). | + | |
| - | Für den EmonCMS-Feed entsprechend Interval 20 verwenden. | + | |
| - | </ | + | |
| - | + | ||
| - | <code cpp> | + | |
| - | / | + | |
| - | | + | |
| - | * | + | |
| - | | + | |
| - | | + | |
| - | | + | |
| - | | + | |
| - | | + | |
| - | | + | |
| - | | + | |
| - | | + | |
| - | | + | |
| - | | + | |
| - | | + | |
| - | | + | |
| - | #include < | + | |
| - | #include < | + | |
| - | #include < | + | |
| - | #include " | + | |
| - | #include < | + | |
| - | #include < | + | |
| - | #include < | + | |
| - | #include < | + | |
| - | + | ||
| - | / | + | |
| - | // | + | |
| - | // 1) Geräte-Identität | + | |
| - | #define SENSOR_ID 111 // eindeutige Sensor-Nummer | + | |
| - | // | + | |
| - | // 2) Bauart - Länge der LED-Anzeige | + | |
| - | #define NUM_LEDS | + | |
| - | // | + | |
| - | // 3) CO2-Kalibrierung | + | |
| - | #define CO2_OFFSET 40 // Kalibrierung an Frischluft wird auf 400 ppm gesetzt. | + | |
| - | // | + | |
| - | // 4) Netzwerk-Auswahl siehe weiter unten (Bereich A/B/C) | + | |
| - | // | + | |
| - | / | + | |
| - | + | ||
| - | // Aus SENSOR_ID werden automatisch Client-Name (" | + | |
| - | char CLIENT[8]; // wird im setup() mit " | + | |
| - | IPAddress ip(192, 168, 1, SENSOR_ID); | + | |
| - | + | ||
| - | IPAddress SERVER_IP(192, | + | |
| - | const char* host = " | + | |
| - | const int | + | |
| - | + | ||
| - | // ============================================================ | + | |
| - | // NETZWERK-AUSWAHL - genau EINE der drei Zeilen auf " | + | |
| - | // ============================================================ | + | |
| - | #define NETZ_A_MOBIL true // SAS-ARDUINO-mobil (persönliches Testnetz) | + | |
| - | #define NETZ_B_TEST | + | |
| - | #define NETZ_C_LIVE | + | |
| - | + | ||
| - | #if (NETZ_A_MOBIL + NETZ_B_TEST + NETZ_C_LIVE) != 1 | + | |
| - | #error "Bitte genau EINE der drei NETZ_-Zeilen oben auf true setzen!" | + | |
| - | #endif | + | |
| - | + | ||
| - | // ---- BEREICH A: SAS-ARDUINO-mobil (persönliches Testnetz) ------------- | + | |
| - | // Vor Weitergabe des Codes an andere Schulen DIESEN BLOCK LÖSCHEN! | + | |
| - | #if NETZ_A_MOBIL | + | |
| - | const char* ssid = " | + | |
| - | const char* password = "< | + | |
| - | const char* apikey | + | |
| - | #endif | + | |
| - | + | ||
| - | // ---- BEREICH B: SAS-ARDUINO, | + | |
| - | #if NETZ_B_TEST | + | |
| - | const char* ssid = " | + | |
| - | const char* password = "< | + | |
| - | const char* apikey | + | |
| - | #endif | + | |
| - | + | ||
| - | // ---- BEREICH C: SAS-ARDUINO, | + | |
| - | #if NETZ_C_LIVE | + | |
| - | const char* ssid = " | + | |
| - | const char* password = "< | + | |
| - | const char* apikey | + | |
| - | #endif | + | |
| - | + | ||
| - | IPAddress gateway(192, | + | |
| - | IPAddress subnet (255, 255, 255, 0); | + | |
| - | IPAddress dns (0, | + | |
| - | static bool g_lastSendFailed = false; | + | |
| - | + | ||
| - | + | ||
| - | /*** 3) Messintervall (Langzeitbetrieb) *****************************************************/ | + | |
| - | const int Schreibpause = 20000; | + | |
| - | + | ||
| - | /*** 4) Sensor-Hardware *********************************************************************/ | + | |
| - | // DHT22 (Luftfeuchte/ | + | |
| - | #define DHTPIN | + | |
| - | #define DHTTYPE | + | |
| - | DHT dht(DHTPIN, DHTTYPE); | + | |
| - | + | ||
| - | // BMP180 (Luftdruck/ | + | |
| - | #define ALTITUDE 42.0 // Höhe [m] für p0-Korrektur | + | |
| - | SFE_BMP180 pressure; | + | |
| - | + | ||
| - | // MH-Z19B (CO₂, UART) | + | |
| - | SoftwareSerial mhzSerial(12, | + | |
| - | byte cmdReadCO2[] = {0xFF, | + | |
| - | + | ||
| - | // LDR (Helligkeit, | + | |
| - | #define LDR_PIN A0 | + | |
| - | + | ||
| - | // LED-Band (CO₂-Ampel) | + | |
| - | #define LED_PIN | + | |
| - | + | ||
| - | Adafruit_NeoPixel strip(NUM_LEDS, | + | |
| - | + | ||
| - | /*** 5) Selbstheilungs-Parameter ************************************************************/ | + | |
| - | const uint32_t BOOT_UPLOAD_DEADLINE_MS = 180000; // 3 min: bis dahin muss 1 Upload geklappt haben | + | |
| - | const uint32_t WIFI_DOWN_REBOOT_MS | + | |
| - | const uint8_t | + | |
| - | + | ||
| - | /*** 6) Zustände ****************************************************************************/ | + | |
| - | static bool | + | |
| - | static bool | + | |
| - | static uint32_t g_bootMs | + | |
| - | static uint32_t g_wifiDownSince | + | |
| - | + | ||
| - | /*** 7) Prototypen **************************************************************************/ | + | |
| - | bool ensureWifi(uint32_t timeoutMs = 15000); | + | |
| - | bool wifiBootConnect(); | + | |
| - | void LED_Band(int co2); | + | |
| - | float readLDR(); | + | |
| - | bool readMHZ19B_Safe(int | + | |
| - | void sendData(float feuchtigkeit, | + | |
| - | + | ||
| - | /*** 8) SETUP *******************************************************************************/ | + | |
| - | void setup() { | + | |
| - | | + | |
| - | delay(200); | + | |
| - | | + | |
| - | + | ||
| - | // CLIENT-Name aus SENSOR_ID erzeugen (z. B. 111 → " | + | |
| - | snprintf(CLIENT, | + | |
| - | Serial.printf(" | + | |
| - | + | ||
| - | // LED-Band initial | + | |
| - | strip.begin(); | + | |
| - | strip.show(); | + | |
| - | + | ||
| - | // Sensoren | + | |
| - | dht.begin(); | + | |
| - | if (!pressure.begin()) { | + | |
| - | Serial.println(" | + | |
| - | while (true) delay(10); // „hart stehenbleiben" | + | |
| - | } | + | |
| - | + | ||
| - | // WLAN-Start: 3 saubere Versuche mit Funk-Reset & Backoff | + | |
| - | if (!wifiBootConnect()) { | + | |
| - | Serial.println(" | + | |
| - | delay(800); | + | |
| - | ESP.restart(); | + | |
| - | } | + | |
| - | + | ||
| - | // CO₂-Sensor starten (UART) | + | |
| - | mhzSerial.begin(9600); | + | |
| - | Serial.println(" | + | |
| - | + | ||
| - | // Zufällige Startverzögerung (0..Schreibpause) → entkoppelt Massenstart nach Stromausfall | + | |
| - | randomSeed(ESP.getChipId()); | + | |
| - | int startDelay = random(0, Schreibpause); | + | |
| - | Serial.printf(" | + | |
| - | delay(startDelay); | + | |
| - | + | ||
| - | // kurze Sensor-Aufwärmzeit | + | |
| - | delay(3000); | + | |
| - | + | ||
| - | // Boot-Zeit merken | + | |
| - | g_bootMs = millis(); | + | |
| - | } | + | |
| - | + | ||
| - | /*** 9) LOOP ********************************************************************************/ | + | |
| - | void loop() { | + | |
| - | /***** DHT22: Luftfeuchte (°C vom DHT kannst du bei Bedarf zusätzlich nutzen) *****/ | + | |
| - | float hum = dht.readHumidity(); | + | |
| - | if (isnan(hum)) hum = -1.0; // -1 = „ungültig" | + | |
| - | + | ||
| - | /***** BMP180: Temperatur (T), Stationsdruck (P), p0 (auf Meereshöhe) *****/ | + | |
| - | char status; | + | |
| - | double T = NAN, P = NAN, p0 = NAN; | + | |
| - | status = pressure.startTemperature(); | + | |
| - | if (status) { | + | |
| - | delay(status); | + | |
| - | if (pressure.getTemperature(T)) { | + | |
| - | status = pressure.startPressure(3); | + | |
| - | if (status) { | + | |
| - | delay(status); | + | |
| - | if (pressure.getPressure(P, | + | |
| - | p0 = pressure.sealevel(P, | + | |
| - | } | + | |
| - | } | + | |
| - | } | + | |
| - | } | + | |
| - | + | ||
| - | /***** MH-Z19B: sanfter Erststart (max 1,5 s warten), dann normal *****/ | + | |
| - | int ppm_val = -1, tco2 = 0; | + | |
| - | if (g_firstCo2Init) { | + | |
| - | uint32_t t0 = millis(); | + | |
| - | while (!readMHZ19B_Safe(ppm_val, | + | |
| - | delay(200); // Sensor bekommt Zeit für erstes vollständiges Frame | + | |
| - | } | + | |
| - | g_firstCo2Init = false; | + | |
| } else { | } else { | ||
| - | | + | |
| + | if (!sensorAussenOK) Serial.println(" | ||
| + | Serial.println(" | ||
| } | } | ||
| - | | + | Serial.println(); |
| - | if (ppm_val > 0) ppm_uart = ppm_val + CO2_OFFSET; | + | |
| - | + | ||
| - | /***** LDR: umgekehrt (hell → klein, dunkel → groß) *****/ | + | |
| - | float ldrWert = readLDR(); | + | |
| - | + | ||
| - | /***** LED-Ampel & WLAN-Status *****/ | + | |
| - | LED_Band(ppm_uart); | + | |
| - | ensureWifi(10000); | + | |
| - | + | ||
| - | /***** NaN-Schutz: nie „nan" | + | |
| - | if (isnan(T)) | + | |
| - | if (isnan(p0)) p0 = -1.0; | + | |
| - | if (ppm_uart < 0) ppm_uart = 0; | + | |
| - | + | ||
| - | /***** Upload zu EmonCMS *****/ | + | |
| - | sendData(hum, | + | |
| - | + | ||
| - | /***** Selbstheilung / Watchdogs *****/ | + | |
| - | uint32_t now = millis(); | + | |
| - | + | ||
| - | // a) Beim Boot: wenn 3 min lang kein Upload erfolgreich → Neustart | + | |
| - | if (!g_uploadedOnce && (now - g_bootMs > BOOT_UPLOAD_DEADLINE_MS)) { | + | |
| - | | + | |
| - | delay(500); | + | |
| - | ESP.restart(); | + | |
| - | } | + | |
| - | + | ||
| - | // b) Laufbetrieb: | + | |
| - | if (g_wifiDownSince && (now - g_wifiDownSince > WIFI_DOWN_REBOOT_MS)) { | + | |
| - | Serial.println(" | + | |
| - | delay(500); | + | |
| - | ESP.restart(); | + | |
| - | } | + | |
| - | + | ||
| - | // Informationen zur Signalstärke - zur Ausrichtung der Antenne... | + | |
| - | static uint32_t cnt = 0; | + | |
| - | if ((++cnt % 6) == 0) { // alle ~3 Minuten bei 30s-Intervall | + | |
| - | Serial.printf(" | + | |
| - | } | + | |
| - | + | ||
| - | /***** Intervall + kleiner Jitter (±0,3 s) - zur WLAN und EMONCMS Lastverteilung *****/ | + | |
| - | int jitter = random(-300, | + | |
| - | delay(Schreibpause + jitter); | + | |
| } | } | ||
| - | |||
| - | /*** ENDE von LOOP *************************************************************************/ | ||
| /*** 10) WLAN-Helfer ***********************************************************************/ | /*** 10) WLAN-Helfer ***********************************************************************/ | ||
| Zeile 2753: | Zeile 1806: | ||
| // Mehrstufiger WLAN-Boot: Funk hart zurücksetzen, | // Mehrstufiger WLAN-Boot: Funk hart zurücksetzen, | ||
| bool wifiBootConnect() { | bool wifiBootConnect() { | ||
| - | WiFi.persistent(false); | + | WiFi.persistent(false); |
| WiFi.mode(WIFI_STA); | WiFi.mode(WIFI_STA); | ||
| WiFi.setAutoConnect(true); | WiFi.setAutoConnect(true); | ||
| WiFi.setAutoReconnect(true); | WiFi.setAutoReconnect(true); | ||
| - | WiFi.setOutputPower(20.5); | + | WiFi.setOutputPower(20.5); |
| - | WiFi.setSleepMode(WIFI_NONE_SLEEP); | + | WiFi.setSleepMode(WIFI_NONE_SLEEP); |
| for (uint8_t attempt = 1; attempt <= WIFI_BOOT_TRIES; | for (uint8_t attempt = 1; attempt <= WIFI_BOOT_TRIES; | ||
| Serial.printf(" | Serial.printf(" | ||
| - | // Funk & Assoc/ | + | // Funkzustand |
| WiFi.disconnect(true); | WiFi.disconnect(true); | ||
| delay(200); | delay(200); | ||
| Zeile 2774: | Zeile 1827: | ||
| if (ensureWifi(20000)) { | if (ensureWifi(20000)) { | ||
| - | | + | WiFiClient poke; poke.connect(gateway, |
| - | WiFi.localIP().toString().c_str(), | + | |
| - | // Optional: Gateway kurz „anticken", | + | |
| - | | + | |
| return true; | return true; | ||
| } | } | ||
| Zeile 2795: | Zeile 1845: | ||
| uint32_t start = millis(); | uint32_t start = millis(); | ||
| while (WiFi.status() != WL_CONNECTED && millis() - start < timeoutMs) { | while (WiFi.status() != WL_CONNECTED && millis() - start < timeoutMs) { | ||
| - | delay(250); | + | delay(250); Serial.print(" |
| - | | + | |
| } | } | ||
| if (WiFi.status() == WL_CONNECTED) { | if (WiFi.status() == WL_CONNECTED) { | ||
| - | Serial.printf(" | + | Serial.printf(" |
| + | | ||
| g_wifiDownSince = 0; | g_wifiDownSince = 0; | ||
| return true; | return true; | ||
| } | } | ||
| Serial.println(" | Serial.println(" | ||
| - | if (!g_wifiDownSince) g_wifiDownSince = millis(); | + | if (!g_wifiDownSince) g_wifiDownSince = millis(); |
| return false; | return false; | ||
| - | } | ||
| - | |||
| - | /*** 11) Sensor-/ | ||
| - | |||
| - | // MH-Z19B „sicher" | ||
| - | bool readMHZ19B_Safe(int &ppm, int &tempC) { | ||
| - | // alten UART-Puffer leeren (verhindert Mischpakete) | ||
| - | while (mhzSerial.available()) mhzSerial.read(); | ||
| - | |||
| - | // Anfrage senden | ||
| - | mhzSerial.write(cmdReadCO2, | ||
| - | |||
| - | // Antwort (max. 200 ms warten) | ||
| - | uint8_t resp[9]; int i = 0; uint32_t start = millis(); | ||
| - | while (millis() - start < 200) { | ||
| - | if (mhzSerial.available()) { | ||
| - | resp[i++] = mhzSerial.read(); | ||
| - | if (i == 9) break; | ||
| - | } | ||
| - | delay(1); | ||
| - | } | ||
| - | |||
| - | // Header + Länge prüfen | ||
| - | if (i != 9 || resp[0] != 0xFF || resp[1] != 0x86) return false; | ||
| - | |||
| - | // Checksumme prüfen | ||
| - | uint8_t sum = 0; for (int k = 1; k < 8; k++) sum += resp[k]; | ||
| - | sum = 0xFF - sum + 1; | ||
| - | if (sum != resp[8]) return false; | ||
| - | |||
| - | // Werte extrahieren | ||
| - | ppm = resp[2] * 256 + resp[3]; | ||
| - | tempC = resp[4] - 40; | ||
| - | return true; | ||
| - | } | ||
| - | |||
| - | // LDR umgekehrt: hell → großer Wert, dunkel → kleiner Wert (0..1023) | ||
| - | float readLDR() { | ||
| - | int raw = analogRead(LDR_PIN); | ||
| - | raw = 450 - raw; // Umkehrung - heller wird es in der BOX nicht | ||
| - | return (float)raw; | ||
| } | } | ||
| // Sanfte Netzwerk-Kur: | // Sanfte Netzwerk-Kur: | ||
| - | bool netSoftReset(uint32_t connectTimeoutMs | + | bool netSoftReset(uint32_t connectTimeoutMs) { |
| Serial.println(" | Serial.println(" | ||
| - | WiFi.disconnect(true); | + | WiFi.disconnect(true); |
| delay(200); | delay(200); | ||
| - | WiFi.forceSleepBegin(); | + | WiFi.forceSleepBegin(); |
| delay(200); | delay(200); | ||
| - | WiFi.forceSleepWake(); | + | WiFi.forceSleepWake(); |
| delay(200); | delay(200); | ||
| - | WiFi.config(ip, | + | WiFi.config(ip, |
| WiFi.begin(ssid, | WiFi.begin(ssid, | ||
| return ensureWifi(connectTimeoutMs); | return ensureWifi(connectTimeoutMs); | ||
| Zeile 2864: | Zeile 1873: | ||
| // Kurzer TCP-Preflight: | // Kurzer TCP-Preflight: | ||
| - | // Hilft ARP/ | + | bool tcpPreflight(uint16_t port, uint16_t graceMs) { |
| - | bool tcpPreflight(uint16_t port = 80, uint16_t graceMs | + | |
| WiFiClient poke; | WiFiClient poke; | ||
| if (!poke.connect(SERVER_IP, | if (!poke.connect(SERVER_IP, | ||
| Zeile 2873: | Zeile 1881: | ||
| } | } | ||
| - | /****************************************************** SENDEN der DATEN **********************************************************************/ | + | /*** 11) Upload an EmonCMS |
| - | void sendData(float | + | void sendData(float |
| if (WiFi.status() != WL_CONNECTED) { | if (WiFi.status() != WL_CONNECTED) { | ||
| Serial.println(" | Serial.println(" | ||
| Zeile 2881: | Zeile 1889: | ||
| } | } | ||
| - | // Pfad/Query, weil wir die 5-Parameter-Variante mit Host-String nutzen | + | // POST zu /emoncms/ |
| String path = "/ | String path = "/ | ||
| String query = "? | String query = "? | ||
| - | String body = " | + | |
| - | ", | + | // Ti_C = Temperatur Innen, Ta_C = Temperatur Außen |
| - | ", | + | |
| - | ", | + | ", |
| - | ", | + | |
| // Preflight nach Fehlschlag | // Preflight nach Fehlschlag | ||
| Zeile 2897: | Zeile 1904: | ||
| } | } | ||
| - | // Host-String aus IPAddress erzeugen (für http.begin(...)) | ||
| String hostStr = SERVER_IP.toString(); | String hostStr = SERVER_IP.toString(); | ||
| - | auto doPostOnce = [&](int attempt)-> | + | auto doPostOnce = [&](int attempt) -> int { |
| if (WiFi.status() != WL_CONNECTED) { | if (WiFi.status() != WL_CONNECTED) { | ||
| ensureWifi(8000); | ensureWifi(8000); | ||
| Zeile 2933: | Zeile 1939: | ||
| if (code == 200) { | if (code == 200) { | ||
| if (payload.length()) { Serial.print(" | if (payload.length()) { Serial.print(" | ||
| - | Serial.printf(" | + | Serial.printf(" |
| - | feuchtigkeit, | + | |
| g_uploadedOnce | g_uploadedOnce | ||
| g_lastSendFailed = false; | g_lastSendFailed = false; | ||
| Zeile 2962: | Zeile 1967: | ||
| } | } | ||
| - | g_lastSendFailed = true; // alles fehlgeschlagen | + | g_lastSendFailed = true; // alles fehlgeschlagen |
| - | Serial.printf(" | + | Serial.printf(" |
| - | CLIENT, | + | CLIENT, |
| } | } | ||
| - | / | ||
| - | |||
| - | /*** 12) CO₂-LED-Ampel (Balkenanzeige links → rechts) *************************************/ | ||
| - | void LED_Band(int co2) { | ||
| - | |||
| - | const uint32_t BLAU = strip.Color(0, | ||
| - | const uint32_t GRUEN = strip.Color(0, | ||
| - | const uint32_t GELB = strip.Color(255, | ||
| - | const uint32_t ROT = strip.Color(255, | ||
| - | const uint32_t WEISS = strip.Color(255, | ||
| - | const uint32_t AUS = strip.Color(0, | ||
| - | |||
| - | // ── NUR HIER ÄNDERN: 5 = normal, 4 = Gerät mit 4 LEDs ─────────────────── | ||
| - | const int LEDS = NUM_LEDS; | ||
| - | // ───────────────────────────────────────────────────────────────────────── | ||
| - | |||
| - | // Alarm ab 2700: Rot-Weiß blitzen, dann dauerhaft Rot | ||
| - | if (co2 >= 2700) { | ||
| - | strip.setBrightness(255); | ||
| - | for (int i = 0; i < 5; i++) { | ||
| - | strip.fill(ROT); | ||
| - | strip.fill(WEISS); | ||
| - | strip.clear(); | ||
| - | } | ||
| - | // Nach dem Blitzen: alle vorhandenen LEDs dauerhaft Rot | ||
| - | strip.setBrightness(120); | ||
| - | for (int i = 0; i < LEDS; i++) { | ||
| - | strip.setPixelColor(i, | ||
| - | } | ||
| - | // Übrige LEDs aus (bei 4 LEDs: LED4 dunkel) | ||
| - | for (int i = LEDS; i < 5; i++) { | ||
| - | strip.setPixelColor(i, | ||
| - | } | ||
| - | strip.show(); | ||
| - | return; | ||
| - | } | ||
| - | |||
| - | // Vollständiges 5-LED-Muster (LED0=links ... LED4=rechts) | ||
| - | uint32_t pat[5]; | ||
| - | uint8_t | ||
| - | |||
| - | // LED0 | ||
| - | if (co2 < 500) { pat[0]=BLAU; | ||
| - | else if (co2 < 650) { pat[0]=BLAU; | ||
| - | else if (co2 < 800) { pat[0]=BLAU; | ||
| - | else if (co2 < 950) { pat[0]=BLAU; | ||
| - | else if (co2 < 1200) { pat[0]=BLAU; | ||
| - | else if (co2 < 1350) { pat[0]=GRUEN; | ||
| - | else if (co2 < 1500) { pat[0]=GRUEN; | ||
| - | else if (co2 < 1650) { pat[0]=GRUEN; | ||
| - | else if (co2 < 1800) { pat[0]=GRUEN; | ||
| - | else if (co2 < 1950) { pat[0]=GELB; | ||
| - | else if (co2 < 2100) { pat[0]=GELB; | ||
| - | else if (co2 < 2250) { pat[0]=GELB; | ||
| - | else { pat[0]=GELB; | ||
| - | |||
| - | // Nur so viele LEDs ansteuern wie vorhanden (4 oder 5) | ||
| - | strip.setBrightness(brightness); | ||
| - | for (int i = 0; i < LEDS; i++) { | ||
| - | strip.setPixelColor(i, | ||
| - | } | ||
| - | // Übrige LEDs ausschalten (bei 4 LEDs: LED4 bleibt dunkel) | ||
| - | for (int i = LEDS; i < 5; i++) { | ||
| - | strip.setPixelColor(i, | ||
| - | } | ||
| - | strip.show(); | ||
| - | } | ||
| - | </ | ||
| - | |||
| - | ---------------------------------------------------2026-08-12------------------------------------------------------- | ||
| - | ====== Programmcode der Sensor-Boxen ====== | ||
| - | |||
| - | Diese Seite enthält den vollständigen Arduino-Code aller Sensor-Boxen, | ||
| - | |||
| - | ===== Netzwerk-Auswahlschema (Bereich A/B/C) ===== | ||
| - | |||
| - | Alle Sketches auf dieser Seite nutzen dasselbe Schema, um zwischen drei Netzwerk-/ | ||
| - | |||
| - | * **Bereich A – SAS-ARDUINO-mobil: | ||
| - | * **Bereich B – SAS-ARDUINO, | ||
| - | * **Bereich C – SAS-ARDUINO, | ||
| - | |||
| - | Im Kopf jedes Sketches wird **genau eine** der drei Zeilen auf '' | ||
| - | |||
| - | <code cpp> | ||
| - | #define NETZ_A_MOBIL true // SAS-ARDUINO-mobil (persönliches Testnetz) | ||
| - | #define NETZ_B_TEST | ||
| - | #define NETZ_C_LIVE | ||
| - | |||
| - | #if (NETZ_A_MOBIL + NETZ_B_TEST + NETZ_C_LIVE) != 1 | ||
| - | #error "Bitte genau EINE der drei NETZ_-Zeilen oben auf true setzen!" | ||
| - | #endif | ||
| - | </ | ||
| - | |||
| - | Darunter folgen drei ''# | ||
| - | |||
| - | < | ||
| - | Wichtig bei Weitergabe an andere Schulen: Bereich A (SAS-ARDUINO-mobil) | ||
| - | enthält persönliche/ | ||
| - | Veröffentlichung KOMPLETT gelöscht werden (den ganzen # | ||
| - | Block). Bereich B und C beziehen sich auf das isolierte, nach der | ||
| - | Hauptanleitung bei jeder Schule identisch aufgebaute Schulnetz | ||
| - | (" | ||
| - | müssen andere Schulen nur ihr eigenes WLAN-Passwort und ihre eigenen | ||
| - | API-Keys eintragen. | ||
| - | </ | ||
| - | |||
| - | ---- | ||
| - | |||
| - | ===== Testsensor (ohne echte Sensoren) ===== | ||
| - | |||
| - | Dient ausschließlich dazu, die EmonCMS-Einrichtung (Inputs, Feeds, Dashboard) zu testen, **bevor** eine echte Sensor-Box angeschlossen ist. Es werden keine Sensoren ausgelesen – die Werte für Temperatur, Luftfeuchtigkeit und CO2 werden im Code simuliert (Temperatur/ | ||
| - | |||
| - | < | ||
| - | Verwendet SENSOR_ID 999 als reservierte Test-Kennung - bitte NICHT | ||
| - | für echte Sensor-Boxen verwenden (siehe SENSOR_ID-Übersichtstabelle | ||
| - | in der Hauptanleitung). | ||
| - | </ | ||
| - | |||
| - | <code cpp> | ||
| / | / | ||
| - | | + | |
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| * | * | ||
| - | | + | |
| - | | + | |
| * | * | ||
| - | | + | |
| - | | + | |
| + | | ||
| + | | ||
| + | | ||
| | | ||
| - | #include < | ||
| - | #include < | ||
| - | #include < | ||
| - | |||
| - | #define SENSOR_ID 999 // Reservierte Test-ID - NICHT für echte Boxen verwenden! | ||
| - | #define SENDEINTERVALL_MS 30000 // Sendetakt in ms (hier kurz gehalten: alle 30 s, für schnelles Testen) | ||
| - | |||
| - | char CLIENT[8]; | ||
| - | IPAddress ip(192, 168, 1, SENSOR_ID); | ||
| - | IPAddress SERVER_IP(192, | ||
| - | const int httpPort = 80; | ||
| - | |||
| - | // ============================================================ | ||
| - | // NETZWERK-AUSWAHL - genau EINE der drei Zeilen auf " | ||
| - | // ============================================================ | ||
| - | #define NETZ_A_MOBIL true // SAS-ARDUINO-mobil (persönliches Testnetz) | ||
| - | #define NETZ_B_TEST | ||
| - | #define NETZ_C_LIVE | ||
| - | |||
| - | #if (NETZ_A_MOBIL + NETZ_B_TEST + NETZ_C_LIVE) != 1 | ||
| - | #error "Bitte genau EINE der drei NETZ_-Zeilen oben auf true setzen!" | ||
| - | #endif | ||
| - | |||
| - | // ---- BEREICH A: SAS-ARDUINO-mobil (persönliches Testnetz) ------------- | ||
| - | // Vor Weitergabe des Codes an andere Schulen DIESEN BLOCK LÖSCHEN! | ||
| - | #if NETZ_A_MOBIL | ||
| - | const char* ssid = " | ||
| - | const char* password = "< | ||
| - | const char* apikey | ||
| - | #endif | ||
| - | |||
| - | // ---- BEREICH B: SAS-ARDUINO, | ||
| - | #if NETZ_B_TEST | ||
| - | const char* ssid = " | ||
| - | const char* password = "< | ||
| - | const char* apikey | ||
| - | #endif | ||
| - | |||
| - | // ---- BEREICH C: SAS-ARDUINO, | ||
| - | #if NETZ_C_LIVE | ||
| - | const char* ssid = " | ||
| - | const char* password = "< | ||
| - | const char* apikey | ||
| - | #endif | ||
| - | |||
| - | IPAddress gateway(192, | ||
| - | IPAddress subnet (255, 255, 255, 0); | ||
| - | IPAddress dns (0, 0, 0, 0); | ||
| - | |||
| - | void setup() { | ||
| - | Serial.begin(115200); | ||
| - | delay(200); | ||
| - | snprintf(CLIENT, | ||
| - | Serial.printf(" | ||
| - | |||
| - | WiFi.mode(WIFI_STA); | ||
| - | WiFi.config(ip, | ||
| - | WiFi.begin(ssid, | ||
| - | |||
| - | Serial.print(" | ||
| - | while (WiFi.status() != WL_CONNECTED) { | ||
| - | delay(300); | ||
| - | Serial.print(" | ||
| - | } | ||
| - | Serial.printf(" | ||
| - | } | ||
| - | |||
| - | void loop() { | ||
| - | // ---- Simulierte Werte (KEINE echten Sensoren!) ---- | ||
| - | float temp = 20.0 + 5.0 * sin(millis() / 60000.0); | ||
| - | float hum = 45.0 + 10.0 * sin(millis() / 90000.0); | ||
| - | static int fakeCo2 = 400; | ||
| - | fakeCo2 += random(-5, 6); // wandert leicht um 400 ppm | ||
| - | |||
| - | char path[96]; | ||
| - | snprintf(path, | ||
| - | char body[128]; | ||
| - | snprintf(body, | ||
| - | |||
| - | if (WiFi.status() == WL_CONNECTED) { | ||
| - | WiFiClient client; | ||
| - | HTTPClient http; | ||
| - | if (http.begin(client, | ||
| - | http.addHeader(" | ||
| - | int code = http.POST(body); | ||
| - | Serial.printf(" | ||
| - | http.end(); | ||
| - | } | ||
| - | } else { | ||
| - | Serial.println(" | ||
| - | WiFi.begin(ssid, | ||
| - | } | ||
| - | |||
| - | delay(SENDEINTERVALL_MS); | ||
| - | } | ||
| </ | </ | ||
| - | Gesendete Felder: '' | + | ====== |
| - | + | ||
| - | ---- | + | |
| - | + | ||
| - | ===== Einfacher Sensor: nur BME280 (Temperatur, | + | |
| - | + | ||
| - | Ein echter, aber minimaler Sensor – guter nächster Schritt nach dem simulierten Testsensor, bevor die vollständigen Boxen (Klimamessbox, | + | |
| - | + | ||
| - | **Verkabelung (I²C-Modus): | + | |
| - | + | ||
| - | ^ BME280-Pin ^ Verbunden mit ^ Hinweis ^ | + | |
| - | | VCC | 3,3V | | | + | |
| - | | GND | GND | | | + | |
| - | | SCL | D1 (GPIO5) | Standard-I²C-Clock beim D1 Mini | | + | |
| - | | SDA | D2 (GPIO4) | Standard-I²C-Data beim D1 Mini | | + | |
| - | | CSB | 3,3V | Zwingend nötig, um den I²C-Modus zu aktivieren (statt SPI) | | + | |
| - | | SDO | **GND** | Setzt die I²C-Adresse auf **0x76** – dieselbe Adresse wie bei der Klimamessbox (Teil 3), bewusst einheitlich gehalten, damit später auch die Raummessbox (aktuell noch BMP180+DHT22) beim geplanten Umstieg auf BME280 ohne Adresskonflikt eingebunden werden kann | | + | |
| - | + | ||
| - | <code cpp> | + | |
| - | / | + | |
| - | | + | |
| - | * | + | |
| - | | + | |
| - | | + | |
| - | * | + | |
| - | | + | |
| - | | + | |
| - | | + | |
| - | #include < | + | |
| - | #include < | + | |
| - | #include < | + | |
| - | #include < | + | |
| - | #include < | + | |
| - | + | ||
| - | #define SENSOR_ID 998 // Test-ID für diesen einfachen Sensor - vor Produktivbetrieb anpassen! | + | |
| - | + | ||
| - | // ==== TEST-EINSTELLUNG ======================================================= | + | |
| - | // 10 Sekunden statt der später üblichen 30-120 Sekunden - bewusst kurz gehalten, | + | |
| - | // damit sich Änderungen beim Testen sofort in EmonCMS zeigen. Für Dauerbetrieb | + | |
| - | // erhöhen (z. B. auf 120000 wie bei der Klimamessbox), | + | |
| - | // unnötig zu belasten! | + | |
| - | #define SENDEINTERVALL_MS 10000 | + | |
| - | // ============================================================================== | + | |
| - | + | ||
| - | #define BME280_ADDRESS 0x76 // SDO auf GND → Adresse 0x76 (SDO auf 3,3V wäre 0x77) | + | |
| - | #define ALTITUDE 42.0 // Höhe über Meeresspiegel in Metern, für Luftdruck-Korrektur | + | |
| - | + | ||
| - | Adafruit_BME280 bme; | + | |
| - | + | ||
| - | char CLIENT[8]; | + | |
| - | IPAddress ip(192, 168, 1, SENSOR_ID); | + | |
| - | IPAddress SERVER_IP(192, | + | |
| - | const int httpPort = 80; | + | |
| - | + | ||
| - | // ============================================================ | + | |
| - | // NETZWERK-AUSWAHL - genau EINE der drei Zeilen auf " | + | |
| - | // ============================================================ | + | |
| - | #define NETZ_A_MOBIL true // SAS-ARDUINO-mobil (persönliches Testnetz) | + | |
| - | #define NETZ_B_TEST | + | |
| - | #define NETZ_C_LIVE | + | |
| - | + | ||
| - | #if (NETZ_A_MOBIL + NETZ_B_TEST + NETZ_C_LIVE) != 1 | + | |
| - | #error "Bitte genau EINE der drei NETZ_-Zeilen oben auf true setzen!" | + | |
| - | #endif | + | |
| - | + | ||
| - | // ---- BEREICH A: SAS-ARDUINO-mobil (persönliches Testnetz) ------------- | + | |
| - | // Vor Weitergabe des Codes an andere Schulen DIESEN BLOCK LÖSCHEN! | + | |
| - | #if NETZ_A_MOBIL | + | |
| - | const char* ssid = " | + | |
| - | const char* password = "< | + | |
| - | const char* apikey | + | |
| - | #endif | + | |
| - | + | ||
| - | // ---- BEREICH B: SAS-ARDUINO, | + | |
| - | #if NETZ_B_TEST | + | |
| - | const char* ssid = " | + | |
| - | const char* password = "< | + | |
| - | const char* apikey | + | |
| - | #endif | + | |
| - | + | ||
| - | // ---- BEREICH | + | |
| - | #if NETZ_C_LIVE | + | |
| - | const char* ssid = " | + | |
| - | const char* password = "< | + | |
| - | const char* apikey | + | |
| - | #endif | + | |
| - | + | ||
| - | IPAddress gateway(192, | + | |
| - | IPAddress subnet (255, 255, 255, 0); | + | |
| - | IPAddress dns (0, 0, 0, 0); | + | |
| - | + | ||
| - | void setup() { | + | |
| - | Serial.begin(115200); | + | |
| - | delay(200); | + | |
| - | snprintf(CLIENT, | + | |
| - | Serial.printf(" | + | |
| - | + | ||
| - | Wire.begin(); | + | |
| - | if (!bme.begin(BME280_ADDRESS)) { | + | |
| - | Serial.println(" | + | |
| - | while (true) delay(100); | + | |
| - | } | + | |
| - | Serial.println(" | + | |
| - | + | ||
| - | WiFi.mode(WIFI_STA); | + | |
| - | WiFi.config(ip, | + | |
| - | WiFi.begin(ssid, | + | |
| - | + | ||
| - | Serial.print(" | + | |
| - | while (WiFi.status() != WL_CONNECTED) { | + | |
| - | delay(300); | + | |
| - | Serial.print(" | + | |
| - | } | + | |
| - | Serial.printf(" | + | |
| - | } | + | |
| - | + | ||
| - | void loop() { | + | |
| - | float hum | + | |
| - | float temp = bme.readTemperature(); | + | |
| - | float pressure | + | |
| - | float p0 = bme.seaLevelForAltitude(ALTITUDE, | + | |
| - | + | ||
| - | char path[96]; | + | |
| - | snprintf(path, | + | |
| - | char body[128]; | + | |
| - | snprintf(body, | + | |
| - | + | ||
| - | if (WiFi.status() == WL_CONNECTED) { | + | |
| - | WiFiClient client; | + | |
| - | HTTPClient http; | + | |
| - | if (http.begin(client, | + | |
| - | http.addHeader(" | + | |
| - | int code = http.POST(body); | + | |
| - | Serial.printf(" | + | |
| - | http.end(); | + | |
| - | } | + | |
| - | } else { | + | |
| - | Serial.println(" | + | |
| - | WiFi.begin(ssid, | + | |
| - | } | + | |
| - | + | ||
| - | delay(SENDEINTERVALL_MS); | + | |
| - | } | + | |
| - | </ | + | |
| - | + | ||
| - | Gesendete Felder: '' | + | |
| - | + | ||
| - | ---- | + | |
| - | + | ||
| - | ===== Klimamessbox (Außenbereich) ===== | + | |
| - | + | ||
| - | //(folgt im nächsten Schritt)// | + | |
| - | + | ||
| - | ---- | + | |
| - | + | ||
| - | ===== Raummessbox (Innenbereich) ===== | + | |
| - | + | ||
| - | //(folgt im nächsten Schritt)// | + | |
| - | + | ||
| - | ----------------------------------------------------------alt - 30 Sekunden ---------------------------- | + | |
| - | ====== Programmcode der Sensor-Boxen ====== | + | |
| - | + | ||
| - | Diese Seite enthält den vollständigen Arduino-Code aller Sensor-Boxen, | + | |
| - | + | ||
| - | ===== Netzwerk-Auswahlschema (Bereich A/B/C) ===== | + | |
| - | + | ||
| - | Alle Sketches auf dieser Seite nutzen dasselbe Schema, um zwischen drei Netzwerk-/ | + | |
| - | + | ||
| - | * **Bereich A – SAS-ARDUINO-mobil: | + | |
| - | * **Bereich B – SAS-ARDUINO, | + | |
| - | * **Bereich C – SAS-ARDUINO, | + | |
| - | + | ||
| - | Im Kopf jedes Sketches wird **genau eine** der drei Zeilen auf '' | + | |
| - | + | ||
| - | <code cpp> | + | |
| - | #define NETZ_A_MOBIL true // SAS-ARDUINO-mobil (persönliches Testnetz) | + | |
| - | #define NETZ_B_TEST | + | |
| - | #define NETZ_C_LIVE | + | |
| - | + | ||
| - | #if (NETZ_A_MOBIL + NETZ_B_TEST + NETZ_C_LIVE) != 1 | + | |
| - | #error "Bitte genau EINE der drei NETZ_-Zeilen oben auf true setzen!" | + | |
| - | #endif | + | |
| - | </ | + | |
| - | + | ||
| - | Darunter folgen drei ''# | + | |
| < | < | ||
| - | Wichtig bei Weitergabe an andere | + | " |
| - | enthält persönliche/ | + | lizenziert unter CC BY-SA 4.0 |
| - | Veröffentlichung KOMPLETT gelöscht werden | + | (https:// |
| - | Block). Bereich B und C beziehen sich auf das isolierte, nach der | + | |
| - | Hauptanleitung bei jeder Schule identisch aufgebaute Schulnetz | + | |
| - | (" | + | |
| - | müssen andere Schulen nur ihr eigenes WLAN-Passwort und ihre eigenen | + | |
| - | API-Keys eintragen. | + | |
| </ | </ | ||
| - | |||
| - | ---- | ||
| - | |||
| - | ===== Testsensor (ohne echte Sensoren) ===== | ||
| - | |||
| - | Dient ausschließlich dazu, die EmonCMS-Einrichtung (Inputs, Feeds, Dashboard) zu testen, **bevor** eine echte Sensor-Box angeschlossen ist. Es werden keine Sensoren ausgelesen – die Werte für Temperatur, Luftfeuchtigkeit und CO2 werden im Code simuliert (Temperatur/ | ||
| < | < | ||
| - | Verwendet SENSOR_ID 999 als reservierte Test-Kennung - bitte NICHT | + | Diese Programme sind freie Software: Sie können es unter den |
| - | für echte Sensor-Boxen verwenden (siehe SENSOR_ID-Übersichtstabelle | + | Bedingungen der GNU General Public License, wie von der Free |
| - | in der Hauptanleitung). | + | Software Foundation veröffentlicht, |
| - | </ | + | modifizieren, |
| + | Ihrer Wahl) jeder späteren Version. | ||
| - | <code cpp> | + | Die Veröffentlichung dieser Programme erfolgt in der Hoffnung, |
| - | / | + | dass es Ihnen von Nutzen sein wird, aber OHNE JEDE GEWÄHRLEISTUNG, |
| - | * TESTSENSOR für ESP8266 D1 Mini | + | sogar ohne die implizite Gewährleistung |
| - | * | + | EIGNUNG FÜR EINEN BESTIMMTEN ZWECK. Details finden Sie in der |
| - | | + | GNU General Public License. |
| - | | + | |
| - | * | + | |
| - | | + | |
| - | * durch den jeweils passenden ersetzen (siehe unten auf dieser Seite). | + | |
| - | ********************************************************************************************/ | + | |
| - | #include < | + | |
| - | #include < | + | |
| - | #include < | + | |
| - | #define SENSOR_ID 999 // Reservierte Test-ID - NICHT für echte Boxen verwenden! | + | Vollständiger Lizenztext: https://www.gnu.org/licenses/gpl-3.0.de.html |
| - | #define SENDEINTERVALL_MS 30000 // Sendetakt in ms (hier kurz gehalten: alle 30 s, für schnelles Testen) | + | |
| - | + | ||
| - | char CLIENT[8]; | + | |
| - | IPAddress ip(192, 168, 1, SENSOR_ID); | + | |
| - | IPAddress SERVER_IP(192, | + | |
| - | const int httpPort = 80; | + | |
| - | + | ||
| - | // ============================================================ | + | |
| - | // NETZWERK-AUSWAHL - genau EINE der drei Zeilen auf " | + | |
| - | // ============================================================ | + | |
| - | #define NETZ_A_MOBIL true // SAS-ARDUINO-mobil (persönliches Testnetz) | + | |
| - | #define NETZ_B_TEST | + | |
| - | #define NETZ_C_LIVE | + | |
| - | + | ||
| - | #if (NETZ_A_MOBIL + NETZ_B_TEST + NETZ_C_LIVE) != 1 | + | |
| - | #error "Bitte genau EINE der drei NETZ_-Zeilen oben auf true setzen!" | + | |
| - | #endif | + | |
| - | + | ||
| - | // ---- BEREICH A: SAS-ARDUINO-mobil (persönliches Testnetz) ------------- | + | |
| - | // Vor Weitergabe des Codes an andere Schulen DIESEN BLOCK LÖSCHEN! | + | |
| - | #if NETZ_A_MOBIL | + | |
| - | const char* ssid = " | + | |
| - | const char* password = "< | + | |
| - | const char* apikey | + | |
| - | #endif | + | |
| - | + | ||
| - | // ---- BEREICH B: SAS-ARDUINO, | + | |
| - | #if NETZ_B_TEST | + | |
| - | const char* ssid = " | + | |
| - | const char* password = "< | + | |
| - | const char* apikey | + | |
| - | #endif | + | |
| - | + | ||
| - | // ---- BEREICH C: SAS-ARDUINO, | + | |
| - | #if NETZ_C_LIVE | + | |
| - | const char* ssid = " | + | |
| - | const char* password = "< | + | |
| - | const char* apikey | + | |
| - | #endif | + | |
| - | + | ||
| - | IPAddress gateway(192, | + | |
| - | IPAddress subnet (255, 255, 255, 0); | + | |
| - | IPAddress dns (0, 0, 0, 0); | + | |
| - | + | ||
| - | void setup() { | + | |
| - | Serial.begin(115200); | + | |
| - | delay(200); | + | |
| - | snprintf(CLIENT, | + | |
| - | Serial.printf(" | + | |
| - | + | ||
| - | WiFi.mode(WIFI_STA); | + | |
| - | WiFi.config(ip, | + | |
| - | WiFi.begin(ssid, | + | |
| - | + | ||
| - | Serial.print(" | + | |
| - | while (WiFi.status() != WL_CONNECTED) { | + | |
| - | delay(300); | + | |
| - | Serial.print(" | + | |
| - | } | + | |
| - | Serial.printf(" | + | |
| - | } | + | |
| - | + | ||
| - | void loop() { | + | |
| - | | + | |
| - | float temp = 20.0 + 5.0 * sin(millis() / 60000.0); | + | |
| - | float hum = 45.0 + 10.0 * sin(millis() / 90000.0); | + | |
| - | static int fakeCo2 = 400; | + | |
| - | fakeCo2 += random(-5, 6); // wandert leicht um 400 ppm | + | |
| - | + | ||
| - | char path[96]; | + | |
| - | snprintf(path, | + | |
| - | char body[128]; | + | |
| - | snprintf(body, | + | |
| - | + | ||
| - | if (WiFi.status() == WL_CONNECTED) { | + | |
| - | WiFiClient client; | + | |
| - | HTTPClient http; | + | |
| - | if (http.begin(client, | + | |
| - | http.addHeader(" | + | |
| - | int code = http.POST(body); | + | |
| - | Serial.printf(" | + | |
| - | http.end(); | + | |
| - | } | + | |
| - | } else { | + | |
| - | Serial.println(" | + | |
| - | WiFi.begin(ssid, password); | + | |
| - | } | + | |
| - | + | ||
| - | delay(SENDEINTERVALL_MS); | + | |
| - | } | + | |
| </ | </ | ||
| - | |||
| - | Gesendete Felder: '' | ||
| - | |||
| - | ---- | ||
| - | |||
| - | ===== Einfacher Sensor: nur BME280 (Temperatur, | ||
| - | |||
| - | Ein echter, aber minimaler Sensor – guter nächster Schritt nach dem simulierten Testsensor, bevor die vollständigen Boxen (Klimamessbox, | ||
| - | |||
| - | **Verkabelung (I²C-Modus): | ||
| - | |||
| - | ^ BME280-Pin ^ Verbunden mit ^ Hinweis ^ | ||
| - | | VCC | 3,3V | | | ||
| - | | GND | GND | | | ||
| - | | SCL | D1 (GPIO5) | Standard-I²C-Clock beim D1 Mini | | ||
| - | | SDA | D2 (GPIO4) | Standard-I²C-Data beim D1 Mini | | ||
| - | | CSB | 3,3V | Zwingend nötig, um den I²C-Modus zu aktivieren (statt SPI) | | ||
| - | | SDO | **GND** | Setzt die I²C-Adresse auf **0x76** – dieselbe Adresse wie bei der Klimamessbox (Teil 3), bewusst einheitlich gehalten, damit später auch die Raummessbox (aktuell noch BMP180+DHT22) beim geplanten Umstieg auf BME280 ohne Adresskonflikt eingebunden werden kann | | ||
| - | |||
| - | <code cpp> | ||
| - | / | ||
| - | | ||
| - | * | ||
| - | | ||
| - | | ||
| - | * | ||
| - | | ||
| - | | ||
| - | | ||
| - | #include < | ||
| - | #include < | ||
| - | #include < | ||
| - | #include < | ||
| - | #include < | ||
| - | |||
| - | #define SENSOR_ID 998 // Test-ID für diesen einfachen Sensor - vor Produktivbetrieb anpassen! | ||
| - | #define SENDEINTERVALL_MS 30000 // alle 30 Sekunden senden | ||
| - | |||
| - | #define BME280_ADDRESS 0x76 // SDO auf GND → Adresse 0x76 (SDO auf 3,3V wäre 0x77) | ||
| - | #define ALTITUDE 42.0 // Höhe über Meeresspiegel in Metern, für Luftdruck-Korrektur | ||
| - | |||
| - | Adafruit_BME280 bme; | ||
| - | |||
| - | char CLIENT[8]; | ||
| - | IPAddress ip(192, 168, 1, SENSOR_ID); | ||
| - | IPAddress SERVER_IP(192, | ||
| - | const int httpPort = 80; | ||
| - | |||
| - | // ============================================================ | ||
| - | // NETZWERK-AUSWAHL - genau EINE der drei Zeilen auf " | ||
| - | // ============================================================ | ||
| - | #define NETZ_A_MOBIL true // SAS-ARDUINO-mobil (persönliches Testnetz) | ||
| - | #define NETZ_B_TEST | ||
| - | #define NETZ_C_LIVE | ||
| - | |||
| - | #if (NETZ_A_MOBIL + NETZ_B_TEST + NETZ_C_LIVE) != 1 | ||
| - | #error "Bitte genau EINE der drei NETZ_-Zeilen oben auf true setzen!" | ||
| - | #endif | ||
| - | |||
| - | // ---- BEREICH A: SAS-ARDUINO-mobil (persönliches Testnetz) ------------- | ||
| - | // Vor Weitergabe des Codes an andere Schulen DIESEN BLOCK LÖSCHEN! | ||
| - | #if NETZ_A_MOBIL | ||
| - | const char* ssid = " | ||
| - | const char* password = "< | ||
| - | const char* apikey | ||
| - | #endif | ||
| - | |||
| - | // ---- BEREICH B: SAS-ARDUINO, | ||
| - | #if NETZ_B_TEST | ||
| - | const char* ssid = " | ||
| - | const char* password = "< | ||
| - | const char* apikey | ||
| - | #endif | ||
| - | |||
| - | // ---- BEREICH C: SAS-ARDUINO, | ||
| - | #if NETZ_C_LIVE | ||
| - | const char* ssid = " | ||
| - | const char* password = "< | ||
| - | const char* apikey | ||
| - | #endif | ||
| - | |||
| - | IPAddress gateway(192, | ||
| - | IPAddress subnet (255, 255, 255, 0); | ||
| - | IPAddress dns (0, 0, 0, 0); | ||
| - | |||
| - | void setup() { | ||
| - | Serial.begin(115200); | ||
| - | delay(200); | ||
| - | snprintf(CLIENT, | ||
| - | Serial.printf(" | ||
| - | |||
| - | Wire.begin(); | ||
| - | if (!bme.begin(BME280_ADDRESS)) { | ||
| - | Serial.println(" | ||
| - | while (true) delay(100); | ||
| - | } | ||
| - | Serial.println(" | ||
| - | |||
| - | WiFi.mode(WIFI_STA); | ||
| - | WiFi.config(ip, | ||
| - | WiFi.begin(ssid, | ||
| - | |||
| - | Serial.print(" | ||
| - | while (WiFi.status() != WL_CONNECTED) { | ||
| - | delay(300); | ||
| - | Serial.print(" | ||
| - | } | ||
| - | Serial.printf(" | ||
| - | } | ||
| - | |||
| - | void loop() { | ||
| - | float hum = bme.readHumidity(); | ||
| - | float temp = bme.readTemperature(); | ||
| - | float pressure = bme.readPressure() / 100.0F; | ||
| - | float p0 = bme.seaLevelForAltitude(ALTITUDE, | ||
| - | |||
| - | char path[96]; | ||
| - | snprintf(path, | ||
| - | char body[128]; | ||
| - | snprintf(body, | ||
| - | |||
| - | if (WiFi.status() == WL_CONNECTED) { | ||
| - | WiFiClient client; | ||
| - | HTTPClient http; | ||
| - | if (http.begin(client, | ||
| - | http.addHeader(" | ||
| - | int code = http.POST(body); | ||
| - | Serial.printf(" | ||
| - | http.end(); | ||
| - | } | ||
| - | } else { | ||
| - | Serial.println(" | ||
| - | WiFi.begin(ssid, | ||
| - | } | ||
| - | |||
| - | delay(SENDEINTERVALL_MS); | ||
| - | } | ||
| - | </ | ||
| - | |||
| - | Gesendete Felder: '' | ||
| - | ==== serieller Monitor ==== | ||
| - | < | ||
| - | 13: | ||
| - | 13: | ||
| - | 13: | ||
| - | 13: | ||
| - | 13: | ||
| - | </ | ||
| - | ---- | ||
| - | |||
| - | [[biebl: | ||
| - | ===== Klimamessbox (Außenbereich) ===== | ||
| - | |||
| - | //(folgt im nächsten Schritt)// | ||
| - | |||
| - | ---- | ||
| - | |||
| - | ===== Raummessbox (Innenbereich) ===== | ||
| - | |||
| - | //(folgt im nächsten Schritt)// | ||
| - | |||