Inhaltsverzeichnis

WWW CO2-Messungen BIO

Basilikum

Fragen und Anregungen an: Makerspace@GymSAS.de

Messgerät

Aufbau

Ergebnis

Vorversuch

48-Stunden-Messung

CO2-07

CO2-08

Analyse der Daten:

CO2-Erzeugung (ohne Licht)

Die Daten herausfiltern, in denen die Temperatur konstant und niedrig ist (Lampe aus). In diesen Zeiträumen wird der CO2-Anstieg pro Minute berechnet.

CO2-Aufnahme (mit Licht)

Die Daten herausfiltern, in denen die Temperatur höher ist (Lampe an). In diesen Zeiträumen wird der CO2-Abfall pro Minute berechnet.

Berechnung:

Ergebnisse:

Die CO2-Produktion und -Aufnahme pro Minute in relevanten Einheiten angeben.

Beispiel

Nachbau

1. Grundfunktion - UNO mit Sensoren

Material

Steckplan

Code

/*********************************************************************************************************
*
* MHZ-19B, BMP 180 2025-06-12
*
*********************************************************************************************************/

#define ALTITUDE 8.0                       // BMP 180 Borgfelde liegt 8 m ueber dem Meer
#include <Wire.h>                          // BMP 180
#include <SFE_BMP180.h>                    // BMP 180
SFE_BMP180 pressure;                       // BMP 180 - Creating an object pressure
float StartZeit, LaufZeit;                 // Zeitpunkt der Messung
String dataString = "";                    // Ausgabestring
int LeerZeile = 0;

/**************************************
*  für den Betrieb des MH-Z19B        *
**************************************/
  const int pwmpin = 6;                    // Der Sensor hängt an Pin 6
  const int range = 5000;                  // Der eingestellte Messbereich (0-5000ppm)
  int  ppm_pwm = 0;  
/**************************************
*  für den Betrieb des BMP 180        *
**************************************/
  double T, P, p0;                         // Speicher fuer temp, Druck relativer Druck

/**************************************
*  Festlegung Messintervall           *
**************************************/
  const  int SchreibPause = 29998;         // naechste Messung erst nach ca. 30 Sekunden     
  int WarteZeit;
void setup() {
  StartZeit = millis();    

/**************************************
 *  für den Betrieb der Ausgaben      *
 **************************************/ 
  Serial.begin(9600);                       // Serieller Monitor mit 9600 bit/s
 
/**************************************
 *  für den Betrieb des MH-Z19B        *
 **************************************/   
    pinMode(pwmpin, INPUT);                   // PWM-Pin auf Eingang setzen

/**************************************
 *  für den Betrieb des BMP-180       *
 **************************************/   
  if (!pressure.begin()) {
    delay(3000);
    Serial.println("BMP-180 init failed!");   // while(true); // wartet, bis der Sensor sich meldet
  }
  Serial.print("Provided altitude: ");
  Serial.print(ALTITUDE, 0);
  Serial.println(" meters");
}

/*********************************************************************************************************
*
*                            H A U P T P R O G R A M M - Loop
*
*********************************************************************************************************/
void loop() {
  DatenAuslesen();
  SerialAusgabe();
}

/********************************************************************************************************
*
*                            Prozedur Datenauslesen
*
*********************************************************************************************************/
void DatenAuslesen(){
  LaufZeit =  millis() - StartZeit;
  ppm_pwm = readCO2PWM();                     // Messung der PWM-Länge mittels einer eigenen Funktion
  DruckUndTemperaturMessung();
}

/********************************************************************************************************
*
*                            Ausgabe am seriellen Monitor
*
*********************************************************************************************************/
void SerialAusgabe(){
  Serial.print(LaufZeit/1000,0);                   
  Serial.print(" s  | "); 
  Serial.print(ppm_pwm);                   
  Serial.print(" ppm   | "); 
  Serial.print(T,1);                   
  Serial.print(" C  |  "); 
  Serial.print(p0);                  
  Serial.print(" hPa  |  "); 
  Serial.print(P);                  
  Serial.print(" hPa Luftdruck gemessen in Borgfelde 6 m ueber NN"); 
  Serial.println();             
}

/********************************************************************************************************
 * 
 *                 Funktionen zum PWM-Auslese des CO2-Wertes
 *                 
 *********************************************************************************************************/
  int readCO2PWM() {
  unsigned long th;
  int ppm_pwm = 0;
  float pulsepercent;
  do {
    th = pulseIn(pwmpin, HIGH, 1004000) / 1000;
    float pulsepercent = th / 1004.0;         // Pulslänge in Prozent (%)
    ppm_pwm = range * pulsepercent;           // PPM-Werte bei gegebenem Range
  } while (th == 0);                          // Der gemessene Wert wird an die loop()-Funktion zurückgegeben,                                                                        
  return ppm_pwm;                             // wo er dann ausgegeben wird.  
}

/********************************************************************************************************
 * 
 *                 Prozedur zum Auslese des BMP-180 (Druck- und Temperatursensor)
 *                 
 ********************************************************************************************************/
void DruckUndTemperaturMessung(){
  char status;
  status = pressure.startTemperature();
  if (status != 0) {
    delay(status);
    status = pressure.getTemperature(T);
    if (status != 0) {
      status = pressure.startPressure(3);
      if (status != 0) {
        delay(status);
        status = pressure.getPressure(P, T);
        if (status != 0) {
          p0 = pressure.sealevel(P, ALTITUDE);
        }
      }
    }
  }
}

2. Nano mit Sensoren und OLED-Display

Material

Schltplan

Steckplan

Code

/*********************************************************************************************************
*
* MHZ-19B, BMP 180 und OLED 2025-06-13
*
*********************************************************************************************************/

#include <Wire.h>                                              // OLED
#include "SSD1306Ascii.h"                                      // OLED
#include "SSD1306AsciiWire.h"                                  // OLED
#define I2C_ADDRESS 0x3C                                       // OLED
SSD1306AsciiWire oled;                                         // OLED


#define ALTITUDE 8.0                       // BMP 180 Borgfelde liegt 8 m ueber dem Meer
#include <Wire.h>                          // BMP 180
#include <SFE_BMP180.h>                    // BMP 180
SFE_BMP180 pressure;                       // BMP 180 - Creating an object pressure

float StartZeit, LaufZeit;                 // Zeitpunkt der Messung
String dataString = "";                    // Ausgabestring
int LeerZeile = 0;

/**************************************
*  für den Betrieb des MH-Z19B        *
**************************************/
  const int pwmpin = 6;                    // Der Sensor hängt an Pin 6
  const int range = 5000;                  // Der eingestellte Messbereich (0-5000ppm)
  int  ppm_pwm = 0;  
/**************************************
*  für den Betrieb des BMP 180        *
**************************************/
  double T, P, p0;                         // Speicher fuer temp, Druck relativer Druck

/**************************************
*  Festlegung Messintervall           *
**************************************/
  const  int SchreibPause = 29998;         // naechste Messung erst nach ca. 30 Sekunden     
  int WarteZeit;
void setup() {
  StartZeit = millis();    

/**************************************
 *  für den Betrieb des OLEDs         *
 **************************************/   
  Wire.begin();
  Wire.setClock(400000L);
  oled.begin(&Adafruit128x64, I2C_ADDRESS);
  oled.setFont(System5x7);                  // Auswahl der Schriftart
  oled.clear();                             // Loeschen der aktuellen Displayanzeige
  oled.println("+ Klimakoffer LMU +");
  delay(1000);
/**************************************
 *  für den Betrieb der Ausgaben      *
 **************************************/ 
  Serial.begin(9600);                       // Serieller Monitor mit 9600 bit/s

/**************************************
 *  für den Betrieb des MH-Z19B        *
 **************************************/   
    pinMode(pwmpin, INPUT);                   // PWM-Pin auf Eingang setzen

/**************************************
 *  für den Betrieb des BMP-180       *
 **************************************/   
  if (!pressure.begin()) {
    delay(3000);
    Serial.println("BMP-180 init failed!");   // while(true); // wartet, bis der Sensor sich meldet
  }
  Serial.print("Provided altitude: ");
  Serial.print(ALTITUDE, 0);
  Serial.println(" meters");
}

/*********************************************************************************************************
*
*                            H A U P T P R O G R A M M - Loop
*
*********************************************************************************************************/
void loop() {
  DatenAuslesen();
  DatenAnzeigen();
  SerialAusgabe();
  delay(5000);
}

/********************************************************************************************************
*
*                            Prozedur Datenauslesen
*
*********************************************************************************************************/
void DatenAuslesen(){
  LaufZeit =  millis() - StartZeit;
  ppm_pwm = readCO2PWM();                     // Messung der PWM-Länge mittels einer eigenen Funktion
  DruckUndTemperaturMessung();
}


/********************************************************************************************************
*
*                            Prozedur Daten auf dem OLED anzeigen
*
*********************************************************************************************************/
void DatenAnzeigen(){
  oled.setFont(System5x7);                    // Auswahl der Schriftart
  oled.clear();                               // Loeschen der aktuellen Displayanzeige
  oled.print("CO2:    ");
  oled.print(ppm_pwm);
  oled.println("  ppm");
  oled.println("-------------------");
  oled.print("Temp.   ");
  oled.print(T,1);
  oled.println("  C");
  oled.println("-------------------");
  oled.print("Druck   ");
  oled.print(p0,0);
  oled.println("  hPa");
  oled.println("-------------------");
  oled.print("Zeit    ");
  oled.print(LaufZeit/1000,0);
  oled.println(" s");
  oled.println("      MB-2025-06-13");
}

/********************************************************************************************************
*
*                            Ausgabe am seriellen Monitor
*
*********************************************************************************************************/
void SerialAusgabe(){
  Serial.print(LaufZeit/1000,0);                   
  Serial.print(" s  | "); 
  Serial.print(ppm_pwm);                   
  Serial.print(" ppm   | "); 
  Serial.print(T,1);                   
  Serial.print(" C  |  "); 
  Serial.print(p0);                  
  Serial.print(" hPa  |  "); 
  Serial.print(P);                  
  Serial.print(" hPa Luftdruck gemessen in Borgfelde 8 m ueber NN"); 
  Serial.println();             
}

/********************************************************************************************************
 * 
 *                 Funktionen zum PWM-Auslese des CO2-Wertes
 *                 
 *********************************************************************************************************/
  int readCO2PWM() {
  unsigned long th;
  int ppm_pwm = 0;
  float pulsepercent;
  do {
    th = pulseIn(pwmpin, HIGH, 1004000) / 1000;
    float pulsepercent = th / 1004.0;         // Pulslänge in Prozent (%)
    ppm_pwm = range * pulsepercent;           // PPM-Werte bei gegebenem Range
  } while (th == 0);                          // Der gemessene Wert wird an die loop()-Funktion zurückgegeben,                                                                        
  return ppm_pwm;                             // wo er dann ausgegeben wird.  
}

/********************************************************************************************************
 * 
 *                 Prozedur zum Auslese des BMP-180 (Druck- und Temperatursensor)
 *                 
 ********************************************************************************************************/
void DruckUndTemperaturMessung(){
  char status;
  status = pressure.startTemperature();
  if (status != 0) {
    delay(status);
    status = pressure.getTemperature(T);
    if (status != 0) {
      status = pressure.startPressure(3);
      if (status != 0) {
        delay(status);
        status = pressure.getPressure(P, T);
        if (status != 0) {
          p0 = pressure.sealevel(P, ALTITUDE);
        }
      }
    }
  }
}

3. Nano mit Sensoren, OLED-Display und SD-Karten-Speicher

Material

Steckplan

Code



4. Nano mit Sensoren, OLED-Display und SD-Karten-Speicher im 3D-Druck-Gehäuse

Material

Schaltplan

Steckplan

Code

/*********************************************************************************************************
*
* SD-Karte, MHZ-19B, BMP 180 und OLED 2025-06-13
*
*********************************************************************************************************/

#include <Wire.h>                                              // OLED
#include "SSD1306Ascii.h"                                      // OLED
#include "SSD1306AsciiWire.h"                                  // OLED
#define I2C_ADDRESS 0x3C                                       // OLED
SSD1306AsciiWire oled;                                         // OLED

/*********************************************************************************************************
 *
 *  SD card datalogger
 *  MOSI - pin 11
 *  MISO - pin 12
 *  CLK - pin 13
 *  CS - pin 4 (for MKRZero SD: SDCARD_SS_PIN)  by Tom Igoe
 *
 ********************************************************************************************************/
#include <SPI.h>                           // SD-Karte
#include <SD.h>                            // SD-Karte In diesem Sketch verwenden wir die <SD.h> library

#define ALTITUDE 8.0                       // BMP 180 Borgfelde liegt 8 m ueber dem Meer
#include <Wire.h>                          // BMP 180
#include <SFE_BMP180.h>                    // BMP 180
SFE_BMP180 pressure;                       // BMP 180 - Creating an object pressure
const int chipSelect = 4;                  // CS-Pin der SD-Karte
bool SchreibZugriff = false;               // Funktionsfaehigkeit beim Datensichern
float StartZeit, LaufZeit;                 // Zeitpunkt der Messung
String dataString = "";                    // Ausgabestring
int LeerZeile = 0;

/**************************************
*  für den Betrieb des MH-Z19B        *
**************************************/
  const int pwmpin = 6;                    // Der Sensor hängt an Pin 6
  const int range = 5000;                  // Der eingestellte Messbereich (0-5000ppm)
  int  ppm_pwm = 0;  
/**************************************
*  für den Betrieb des BMP 180        *
**************************************/
  double T, P, p0;                         // Speicher fuer temp, Druck relativer Druck

/**************************************
*  Festlegung Messintervall           *
**************************************/
  const  int SchreibPause = 29998;         // naechste Messung erst nach ca. 30 Sekunden     
  int WarteZeit;
void setup() {
  StartZeit = millis();    

/**************************************
 *  für den Betrieb des OLEDs         *
 **************************************/   
  Wire.begin();
  Wire.setClock(400000L);
  oled.begin(&Adafruit128x64, I2C_ADDRESS);
  oled.setFont(System5x7);                  // Auswahl der Schriftart
  oled.clear();                             // Loeschen der aktuellen Displayanzeige
  oled.println("+ Klimakoffer LMU + ");
  delay(1000);
/**************************************
 *  für den Betrieb der Ausgaben      *
 **************************************/ 
  Serial.begin(9600);                       // Serieller Monitor mit 9600 bit/s
  Serial.print("Initializing SD card..."); 
  if (!SD.begin(chipSelect)) {              // SD-Karte suchen
    Serial.println("SD-Karte fehlt");       // Keine SD-Karte gefunden
    SchreibZugriff = false;
    oled.setFont(System5x7);                // Auswahl der Schriftart
    for (int i=20; i > 0; i--){
    oled.clear();                           // Loeschen der aktuellen Displayanzeige
    oled.println(" NWP 2024   ");
    oled.println(" ----------------------");
    oled.println("    ");
    oled.println(" KEINE SD-Karte   ");
    oled.println("    ");
    oled.println(" GEFUNDEN!!   ");
    oled.println("    ");
    oled.println(i);
    delay(1000);
    }
  }
  else {
    Serial.println("card initialized.");      // alles ok
    SchreibZugriff = true;
  }

/**************************************
 *  für den Betrieb des MH-Z19B        *
 **************************************/   
    pinMode(pwmpin, INPUT);                   // PWM-Pin auf Eingang setzen

/**************************************
 *  für den Betrieb des BMP-180       *
 **************************************/   
  if (!pressure.begin()) {
    delay(3000);
    Serial.println("BMP-180 init failed!");   // while(true); // wartet, bis der Sensor sich meldet
  }
  Serial.print("Provided altitude: ");
  Serial.print(ALTITUDE, 0);
  Serial.println(" meters");
}

/*********************************************************************************************************
*
*                            H A U P T P R O G R A M M - Loop
*
*********************************************************************************************************/
void loop() {
  DatenAuslesen();
  DatenStringErstellen();
  DatenSichern();
  DatenAnzeigen();
  SerialAusgabe();
  delay(5000);
}

/********************************************************************************************************
*
*                            Prozedur Datenauslesen
*
*********************************************************************************************************/
void DatenAuslesen(){
  LaufZeit =  millis() - StartZeit;
  ppm_pwm = readCO2PWM();                     // Messung der PWM-Länge mittels einer eigenen Funktion
  DruckUndTemperaturMessung();
}

/********************************************************************************************************
*
*                            Prozedur String mit allen Informationen Zusammenstellen
*
*********************************************************************************************************/
void DatenStringErstellen(){
 // make a string for assembling the data to log:
  dataString = "";
  dataString += String(LaufZeit/1000,0);
  dataString += String(";s;");
  dataString += String(ppm_pwm);
  dataString += String(";ppm;");
  dataString += String(T,1);
  dataString += String("; C;");
  dataString += String(p0);
  dataString += String(";hPa;");
  dataString += String(P);
  dataString += String(";hPa");
}

/********************************************************************************************************
*
*                            Prozedur Daten auf der SD-Karte Sichern
*
*********************************************************************************************************/
void DatenSichern(){                          // open the file. note that only one file can be open at a time,
  File dataFile = SD.open("nwp-9-1.csv", FILE_WRITE);          // so you have to close this one before opening another.
  if (dataFile) {                             // if the file is available, write to it:
    dataFile.println(dataString);                                          
    dataFile.close();
    //Serial.println(dataString);             // print to the serial port too:
    SchreibZugriff = true;
  }
  else {                                      // if the file isn't open, pop up an error:
    Serial.println("error opening nwp-9-1.csv");
    SchreibZugriff = false;
  }
}

/********************************************************************************************************
*
*                            Prozedur Daten auf dem OLED anzeigen
*
*********************************************************************************************************/
void DatenAnzeigen(){
  oled.setFont(System5x7);                    // Auswahl der Schriftart
  oled.clear();                               // Loeschen der aktuellen Displayanzeige
  oled.print("CO2:    ");
  oled.print(ppm_pwm);
  oled.println("  ppm");
  oled.println("-------------------");
  oled.print("Temp.   ");
  oled.print(T,1);
  oled.println("  C");
  oled.println("-------------------");
  oled.print("Druck   ");
  oled.print(p0,0);
  oled.println("  hPa");
  oled.println("-------------------");
  oled.print("Zeit    ");
  oled.print(LaufZeit/1000,0);
  oled.println(" s");
  oled.print("SD ");
  if (SchreibZugriff == true){
    oled.print("ok   ");
  }
  else {
    oled.print("no   ");
  }
  oled.println("MB-2025-06-13");
}

/********************************************************************************************************
*
*                            Ausgabe am seriellen Monitor
*
*********************************************************************************************************/
void SerialAusgabe(){
  Serial.print(LaufZeit/1000,0);                   
  Serial.print(" s  | "); 
  Serial.print(ppm_pwm);                   
  Serial.print(" ppm   | "); 
  Serial.print(T,1);                   
  Serial.print(" C  |  "); 
  Serial.print(p0);                  
  Serial.print(" hPa  |  "); 
  Serial.print(P);                  
  Serial.print(" hPa Luftdruck gemessen in Borgfelde 6 m ueber NN"); 
  if (SchreibZugriff == true) {
    Serial.print(" SD ok"); 
  }
  else {
   Serial.print(" SD fehlerhaft"); 
  }
  Serial.println();             
}

/********************************************************************************************************
 * 
 *                 Funktionen zum PWM-Auslese des CO2-Wertes
 *                 
 *********************************************************************************************************/
  int readCO2PWM() {
  unsigned long th;
  int ppm_pwm = 0;
  float pulsepercent;
  do {
    th = pulseIn(pwmpin, HIGH, 1004000) / 1000;
    float pulsepercent = th / 1004.0;         // Pulslänge in Prozent (%)
    ppm_pwm = range * pulsepercent;           // PPM-Werte bei gegebenem Range
  } while (th == 0);                          // Der gemessene Wert wird an die loop()-Funktion zurückgegeben,                                                                        
  return ppm_pwm;                             // wo er dann ausgegeben wird.  
}

/********************************************************************************************************
 * 
 *                 Prozedur zum Auslese des BMP-180 (Druck- und Temperatursensor)
 *                 
 ********************************************************************************************************/
void DruckUndTemperaturMessung(){
  char status;
  status = pressure.startTemperature();
  if (status != 0) {
    delay(status);
    status = pressure.getTemperature(T);
    if (status != 0) {
      status = pressure.startPressure(3);
      if (status != 0) {
        delay(status);
        status = pressure.getPressure(P, T);
        if (status != 0) {
          p0 = pressure.sealevel(P, ALTITUDE);
        }
      }
    }
  }
}

5. Nano mit Sensoren, OLED-Display und SD-Karten-Speicher im 3D-Druck-Gehäuse mit Steckverbindungen

Material

Steckplan

Code

/**************************************************
*
* SD-Karte, CO2-Sensor, BMP 180 und OLED 2023-10-17
*
***************************************************/

#include <Wire.h>                                              // OLED
#include "SSD1306Ascii.h"                                      // OLED
#include "SSD1306AsciiWire.h"                                  // OLED
#define I2C_ADDRESS 0x3C                                       // OLED
SSD1306AsciiWire oled;                                         // OLED

/*  SD card datalogger
 ** MOSI - pin 11
 ** MISO - pin 12
 ** CLK - pin 13
 ** CS - pin 4 (for MKRZero SD: SDCARD_SS_PIN)
  by Tom Igoe
*/
#include <SPI.h>                                               // SD-Karte
#include <SD.h>                                                // SD-Karte In diesem Sketch verwenden wir die <SD.h> library

#define ALTITUDE 8.0                                           // BMP 180 Borgfelde liegt 8 m ueber dem Meer
#include <Wire.h>                                              // BMP 180
#include <SFE_BMP180.h>                                        // BMP 180
SFE_BMP180 pressure;                                           // BMP 180 - Creating an object pressure
const int Taster = 2;                                          // Der Taster zur OLED-Steuerung wurde an Pin 2 angeschlossen
const int chipSelect = 4;                                      // CS-Pin der SD-Karte
bool SchreibZugriff = false;                                   // Funktionsfaehigkeit beim Datensichern
bool Oledan = true;                                            // Status der OLED-Anzeige
bool AnzeigenWechsel = false;                                  // Wurde die Anzeige umgeschaltet
float StartZeit, LaufZeit;                                     // Zeitpunkt der Messung
String dataString = "";                                        // Ausgabestring
int LeerZeile = 0;

volatile int TasterStatus = 0;                                // Variable die den Status des Taster ausliest, wird der sog. "Vektor" 0 zugewiesen                                   
                                                              // Taster Anschluss an Pin 2 entspricht dem Vektor 0   (hier der Fall)
                                                              // Taster Anschluss an Pin 3 entspricht dem Vektor 1

/**************************************
*  für den Betrieb des MH-Z19B        *
**************************************/
  const int pwmpin = 6;                                       // Der Sensor hängt an Pin 6
  const int range = 5000;                                     // Der eingestellte Messbereich (0-5000ppm)
  int  ppm_pwm = 0;
  
/**************************************
*  für den Betrieb des BMP 180        *
**************************************/
  double T, P, p0;                                            //Creating variables for temp, pressure and relative pressure

/**************************************
*  Festlegung Messintervall           *
**************************************/
  const  int SchreibPause = 29998;                            // nächste Messung erst nach ca. 30 Sekunden     
  int WarteZeit;
void setup() {
  StartZeit = millis();    
  pinMode(Taster, INPUT);                                     // Hier wird der Taster als INPUT definiert 
  attachInterrupt(0, TasterUnterbricht, FALLING);             // Hier findet die Einbindung unseres Interrupt-Befehls statt                                                                                         


/**************************************
 *  für den Betrieb des OLEDs         *
 **************************************/   
  Wire.begin();
  Wire.setClock(400000L);
  oled.begin(&Adafruit128x64, I2C_ADDRESS);
  oled.setFont(System5x7);                                    // Auswahl der Schriftart
  oled.clear();                                               // Loeschen der aktuellen Displayanzeige
  oled.println(" NW-Projekt 2023   ");
  delay(1000);
/**************************************
 *  für den Betrieb der Ausgaben      *
 **************************************/ 
  Serial.begin(9600);                                         // Open serial communications and wait for port to open:
  Serial.print("Initializing SD card..."); 
  if (!SD.begin(chipSelect)) {                                // see if the card is present and can be initialized:
    Serial.println("Card failed, or not present");            // don't do anything more:
    SchreibZugriff = false;
    oled.setFont(System5x7);                                  // Auswahl der Schriftart
    for (int i=20; i > 0; i--){
    oled.clear();                                             // Loeschen der aktuellen Displayanzeige
    oled.println(" NW-Projekt 2023   ");
    oled.println(" ----------------------  ");
    oled.println("    ");
    oled.println(" KEINE SD-Karte   ");
    oled.println("    ");
    oled.println(" GEFUNDEN!!   ");
    oled.println("    ");
    oled.println(i);
    delay(1000);
    }
  }
  else {
    Serial.println("card initialized.");                      // alles ok
    SchreibZugriff = true;
  }

/**************************************
 *  für den Betrieb des MH-Z19B        *
 **************************************/   
    pinMode(pwmpin, INPUT);                                   // PWM-Pin auf Eingang setzen

/**************************************
 *  für den Betrieb des BMP-180       *
 **************************************/   
      if (!pressure.begin()) {
    Serial.println("BMP-180 init failed!");
    //while(true);
  }
  Serial.print("Provided altitude: ");
  Serial.print(ALTITUDE, 0);
  Serial.println(" meters");
}

/**********************************************************************************************************
*
*                            H A U P T P R O G R A M M - Loop
*
***********************************************************************************************************/
void loop() {
  // Messen
  DatenAuslesen();
  DatenStringErstellen();
  DatenSichern();
  if (Oledan == 0)
    {DatenAnzeigenAus();}
   else
    {DatenAnzeigen();}
  SerialAusgabe();
  WarteZeit =0;
  while (WarteZeit<SchreibPause && !AnzeigenWechsel) {
    delay(1000);
    WarteZeit = WarteZeit + 1000;
  }
  AnzeigenWechsel = false;
}

/**********************************************************************************************************
*
*                            Prozedur Datenauslesen
*
***********************************************************************************************************/
void DatenAuslesen(){
  LaufZeit =  millis() - StartZeit;
  ppm_pwm = readCO2PWM();                                     // Messung der PWM-Länge mittels einer eigenen Funktion
  DruckUndTemperaturMessung();
}

/**********************************************************************************************************
*
*                            Prozedur String mit allen Informationen Zusammenstellen
*
***********************************************************************************************************/
void DatenStringErstellen(){
 // make a string for assembling the data to log:
  dataString = "";
  dataString += String(LaufZeit/1000,0);
  dataString += String(";s;");
  dataString += String(ppm_pwm);
  dataString += String(";ppm;");
  dataString += String(T,1);
  dataString += String("; C;");
  dataString += String(P);
  dataString += String(";hPa;");
  dataString += String(p0);
  dataString += String(";hPa");
}

/**********************************************************************************************************
*
*                            Prozedur Daten auf der SD-Karte Sichern
*
***********************************************************************************************************/
void DatenSichern(){                                          // open the file. note that only one file can be open at a time,
  File dataFile = SD.open("nw-8-1.csv", FILE_WRITE);          // so you have to close this one before opening another.
  if (dataFile) {                                             // if the file is available, write to it:
    dataFile.println(dataString);                                          
    dataFile.close();
    //Serial.println(dataString);                             // print to the serial port too:
    SchreibZugriff = true;
  }
  else {                                                      // if the file isn't open, pop up an error:
    Serial.println("error opening nw-8-1.csv");
    SchreibZugriff = false;
  }
}

/**********************************************************************************************************
*
*                            Prozedur Daten auf dem OLED anzeigen
*
***********************************************************************************************************/
void DatenAnzeigen(){
  oled.setFont(System5x7);                                    // Auswahl der Schriftart
  oled.clear();                                               // Loeschen der aktuellen Displayanzeige
  oled.print("CO2:    ");
  oled.print(ppm_pwm);
  oled.println("  ppm");
  oled.println("-------------------");
  oled.print("Temp.   ");
  oled.print(T,1);
  oled.println("  C");
  oled.println("-------------------");
  oled.print("Druck   ");
  oled.print(P,0);
  oled.println("  hPa");
  oled.println("-------------------");
  oled.print("Zeit    ");
  oled.print(LaufZeit/1000,0);
  oled.println(" s");
  oled.print("SD ");
  if (SchreibZugriff == true){
    oled.print("ok   ");
  }
  else {
    oled.print("no   ");
  }
  oled.println("MB-20231017");
}


/**********************************************************************************************************
*
*                            Prozedur OLED aus
*
***********************************************************************************************************/
void DatenAnzeigenAus(){
  oled.setFont(System5x7);                                    // Auswahl der Schriftart
  oled.clear();                                               // Loeschen der aktuellen Displayanzeige
  for (int i=0; i<LeerZeile; i++) {
    oled.println("");
  }
  oled.println(".");
  LeerZeile = LeerZeile + 1;
  LeerZeile = LeerZeile % 7;
}
/**********************************************************************************************************
*
*                            Ausgabe am seriellen Monitor
*
***********************************************************************************************************/
void SerialAusgabe(){
  Serial.print(LaufZeit/1000,0);                   
  Serial.print(" s  | "); 
  Serial.print(ppm_pwm);                   
  Serial.print(" ppm   | "); 
  Serial.print(T,1);                   
  Serial.print(" C  |  "); 
  Serial.print(P);                  
  Serial.print(" hPa  |  "); 
  Serial.print(p0);                  
  Serial.print(" hPa Luftdruck gemessen in Borgfelde 6 m ueber NN"); 
  if (SchreibZugriff == true) {
    Serial.print(" SD ok"); 
  }
  else {
   Serial.print(" SD fehlerhaft"); 
  }
  Serial.println();             
}

/**********************************************************************************************************
 * 
 *                 Funktionen zum PWM-Auslese des CO2-Wertes
 *                 
 ***********************************************************************************************************/
  int readCO2PWM() {
  unsigned long th;
  int ppm_pwm = 0;
  float pulsepercent;
  do {
    th = pulseIn(pwmpin, HIGH, 1004000) / 1000;
    float pulsepercent = th / 1004.0;                         // Pulslänge in Prozent (%)
    ppm_pwm = range * pulsepercent;                           // PPM-Werte bei gegebenem Range
  } while (th == 0);                                          // Der gemessene Wert wird an die loop()-Funktion zurückgegeben,                                                                        
  return ppm_pwm;                                             // wo er dann ausgegeben wird.  
}

/**********************************************************************************************************
 * 
 *                 Prozedur zum Auslese des BMP-180 (Druck- und Temperatursensor)
 *                 
 ***********************************************************************************************************/
void DruckUndTemperaturMessung(){
  char status;
  status = pressure.startTemperature();
  if (status != 0) {
    delay(status);
    status = pressure.getTemperature(T);
    if (status != 0) {
      status = pressure.startPressure(3);
      if (status != 0) {
        delay(status);
        status = pressure.getPressure(P, T);
        if (status != 0) {
          p0 = pressure.sealevel(P, ALTITUDE);
        }
      }
    }
  }
}

/**********************************************************************************************************
 * 
 *                 Prozedur INTERRUPT
 *                 
 ***********************************************************************************************************/
void TasterUnterbricht() {                 // Sobald die Unterbrechung "TasterUnterbricht" (der Wert am Pin ändert sich [LOW]) 
  TasterStatus = digitalRead(Taster);      // wird der TasterStatus neu definiert ("volatile"). 
                                           // Die neue Definition geschieht durch das Auslesen des Tasters an Pin 2.
  Oledan = !Oledan;                        // Der Anzeigestatus wird gewechselt
  AnzeigenWechsel = true;
}

Copyright (C) 2025 Biebl/Sankt-Ansgar-Schule Hamburg

"ARDUINO im Unterricht" von Martin Biebl/Sankt-Ansgar-Schule Hamburg,
lizenziert unter CC BY-SA 4.0
(https://creativecommons.org/licenses/by-sa/4.0/)
Diese Programme sind freie Software: Sie können es unter den
Bedingungen der GNU General Public License, wie von der Free
Software Foundation veröffentlicht, weitergeben und/oder
modifizieren, entweder gemäß Version 3 der Lizenz oder (nach
Ihrer Wahl) jeder späteren Version.

Die Veröffentlichung dieser Programme erfolgt in der Hoffnung,
dass es Ihnen von Nutzen sein wird, aber OHNE JEDE GEWÄHRLEISTUNG,
sogar ohne die implizite Gewährleistung der MARKTFÄHIGKEIT oder
EIGNUNG FÜR EINEN BESTIMMTEN ZWECK. Details finden Sie in der
GNU General Public License.

Vollständiger Lizenztext: https://www.gnu.org/licenses/gpl-3.0.de.html