171 lines
4.9 KiB
C++
171 lines
4.9 KiB
C++
#include <LiquidCrystal.h>
|
|
#include "DHT.h"
|
|
#include <Wire.h>
|
|
#include <EEPROM.h>
|
|
#include "Adafruit_CCS811.h"
|
|
|
|
// --- LCD Setup ---
|
|
// Initialisierung des LCDs mit den Pins aus deinem Schaltplan
|
|
// RS = 7, E = 8, D4 = 9, D5 = 10, D6 = 11, D7 = 12
|
|
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);
|
|
|
|
// --- DHT11 Setup ---
|
|
#define DHTPIN 2 // Der DHT11 Daten-Pin ist an Pin 2 angeschlossen
|
|
#define DHTTYPE DHT11 // Sensortyp festlegen
|
|
DHT dht(DHTPIN, DHTTYPE);
|
|
|
|
// --- CJMCU-811 (CCS811) Setup ---
|
|
// I2C: SDA = A4, SCL = A5 (Arduino Uno). Der WAK-Pin des Moduls muss an GND liegen.
|
|
// Standardadresse 0x5A (ADDR an GND), alternativ 0x5B.
|
|
Adafruit_CCS811 ccs;
|
|
bool ccsOk = false;
|
|
|
|
// --- CCS811-Baseline im EEPROM ---
|
|
// Die Baseline ist der Kalibrierwert des Sensors. Wird sie nach einem Neustart
|
|
// wieder gesetzt, ist der eCO2/TVOC-Wert sofort deutlich stabiler.
|
|
// Gespeichert wird erst nach 20 Minuten Laufzeit, danach stündlich.
|
|
// Das EEPROM hält ca. 100.000 Schreibzyklen, stündliches Speichern reicht Jahre.
|
|
const unsigned long BASELINE_AUFWAERMZEIT = 20UL * 60UL * 1000UL;
|
|
const unsigned long BASELINE_INTERVALL = 60UL * 60UL * 1000UL;
|
|
const uint8_t BASELINE_MAGIC = 0xC5; // Kennung für "EEPROM enthält eine Baseline"
|
|
const int BASELINE_ADRESSE = 0;
|
|
unsigned long naechsteBaselineSicherung = BASELINE_AUFWAERMZEIT;
|
|
|
|
struct BaselineSpeicher {
|
|
uint8_t magic;
|
|
uint16_t baseline;
|
|
uint8_t pruefsumme;
|
|
};
|
|
|
|
uint8_t baselinePruefsumme(uint16_t baseline) {
|
|
return (baseline >> 8) ^ (baseline & 0xFF) ^ BASELINE_MAGIC;
|
|
}
|
|
|
|
// Stellt die gespeicherte Baseline wieder her (falls vorhanden und gültig)
|
|
void baselineLaden() {
|
|
BaselineSpeicher b;
|
|
EEPROM.get(BASELINE_ADRESSE, b);
|
|
if (b.magic == BASELINE_MAGIC && b.pruefsumme == baselinePruefsumme(b.baseline)) {
|
|
ccs.setBaseline(b.baseline);
|
|
Serial.print("Baseline geladen: ");
|
|
Serial.println(b.baseline);
|
|
} else {
|
|
Serial.println("Keine gueltige Baseline im EEPROM");
|
|
}
|
|
}
|
|
|
|
// Speichert die aktuelle Baseline (EEPROM.put schreibt nur geänderte Bytes)
|
|
void baselineSpeichern() {
|
|
BaselineSpeicher b;
|
|
b.magic = BASELINE_MAGIC;
|
|
b.baseline = ccs.getBaseline();
|
|
b.pruefsumme = baselinePruefsumme(b.baseline);
|
|
EEPROM.put(BASELINE_ADRESSE, b);
|
|
Serial.print("Baseline gespeichert: ");
|
|
Serial.println(b.baseline);
|
|
}
|
|
|
|
// Anzeige wechselt zwischen zwei Seiten, da das LCD nur 2 Zeilen hat
|
|
bool zeigeLuftqualitaet = false;
|
|
|
|
void setup() {
|
|
// Serial Monitor (9600 Baud) zur Kontrolle der Messwerte
|
|
Serial.begin(9600);
|
|
|
|
// LCD initialisieren (16 Spalten, 2 Zeilen)
|
|
lcd.begin(16, 2);
|
|
|
|
// DHT Sensor starten
|
|
dht.begin();
|
|
|
|
// CCS811 starten: erst 0x5A (ADDR an GND), dann 0x5B (ADDR an VCC)
|
|
ccsOk = ccs.begin(0x5A) || ccs.begin(0x5B);
|
|
if (ccsOk) {
|
|
baselineLaden();
|
|
}
|
|
|
|
// Kurze Startnachricht
|
|
lcd.setCursor(0, 0);
|
|
lcd.print("Sensor startet..");
|
|
|
|
// Der DHT11 braucht nach dem Start einen Moment
|
|
delay(2000);
|
|
lcd.clear();
|
|
}
|
|
|
|
void loop() {
|
|
// WICHTIG: Der DHT11 ist ein langsamer Sensor.
|
|
// Er benötigt ca. 2 Sekunden Pause zwischen den Messungen.
|
|
delay(2000);
|
|
|
|
// Werte für Luftfeuchtigkeit und Temperatur auslesen
|
|
float h = dht.readHumidity();
|
|
float t = dht.readTemperature();
|
|
|
|
// Überprüfen, ob das Auslesen fehlgeschlagen ist (z.B. Kabel locker)
|
|
if (isnan(h) || isnan(t)) {
|
|
lcd.clear();
|
|
lcd.setCursor(0, 0);
|
|
lcd.print("Sensor Fehler!");
|
|
return; // Loop hier abbrechen und von vorne starten
|
|
}
|
|
|
|
// --- Seite 2: CO2 und TVOC vom CCS811 ---
|
|
if (zeigeLuftqualitaet) {
|
|
zeigeLuftqualitaet = false;
|
|
|
|
lcd.clear();
|
|
if (!ccsOk) {
|
|
lcd.setCursor(0, 0);
|
|
lcd.print("CCS811 Fehler!");
|
|
return;
|
|
}
|
|
|
|
// Temperatur/Feuchte zur Kompensation an den CCS811 geben
|
|
ccs.setEnvironmentalData(h, t);
|
|
|
|
if (ccs.available() && !ccs.readData()) {
|
|
lcd.setCursor(0, 0);
|
|
lcd.print("CO2: ");
|
|
lcd.print(ccs.geteCO2());
|
|
lcd.print(" ppm");
|
|
|
|
lcd.setCursor(0, 1);
|
|
lcd.print("TVOC: ");
|
|
lcd.print(ccs.getTVOC());
|
|
lcd.print(" ppb");
|
|
|
|
Serial.print("eCO2: ");
|
|
Serial.print(ccs.geteCO2());
|
|
Serial.print(" ppm TVOC: ");
|
|
Serial.print(ccs.getTVOC());
|
|
Serial.println(" ppb");
|
|
|
|
// Baseline regelmäßig sichern (Vergleich überlaufsicher für millis())
|
|
if ((long)(millis() - naechsteBaselineSicherung) >= 0) {
|
|
baselineSpeichern();
|
|
naechsteBaselineSicherung = millis() + BASELINE_INTERVALL;
|
|
}
|
|
} else {
|
|
lcd.setCursor(0, 0);
|
|
lcd.print("CCS811 wartet..");
|
|
}
|
|
return;
|
|
}
|
|
|
|
// --- Seite 1: Ausgabe auf dem LCD Display ---
|
|
zeigeLuftqualitaet = true;
|
|
|
|
// Zeile 1: Temperatur
|
|
lcd.setCursor(0, 0); // Spalte 0, Zeile 0
|
|
lcd.print("Temp: ");
|
|
lcd.print(t, 1); // Die ", 1" sorgt dafür, dass nur eine Nachkommastelle gezeigt wird
|
|
lcd.print(" C "); // Leerzeichen am Ende überschreiben eventuelle alte Zeichen
|
|
|
|
// Zeile 2: Luftfeuchtigkeit
|
|
lcd.setCursor(0, 1); // Spalte 0, Zeile 1
|
|
lcd.print("Luft: ");
|
|
lcd.print(h, 1);
|
|
lcd.print(" % ");
|
|
}
|