enirstad
Published © CC BY-NC-SA

CO2-Monitor

A small Monitor that displays the progression of the CO2-concentration within one day.

IntermediateWork in progress622
CO2-Monitor

Things used in this project

Hardware components

Grove - Carbon Dioxide Sensor(MH-Z16)
Seeed Studio Grove - Carbon Dioxide Sensor(MH-Z16)
×1
Arduino Nano R3
Arduino Nano R3
×1
Through Hole Resistor, 100 kohm
Through Hole Resistor, 100 kohm
×1
Graphic LCD, 128 x 64
×1
Through Hole Resistor, 10 kohm
Through Hole Resistor, 10 kohm
×1
Resistor 100k ohm
Resistor 100k ohm
×1
Through Hole Resistor, 220 ohm
Through Hole Resistor, 220 ohm
×1
Capacitor 1000 µF
Capacitor 1000 µF
×1
Electrolytic Capacitor, 330 µF
Electrolytic Capacitor, 330 µF
×1
Ceramic Disc Capacitor, 330 pF
Ceramic Disc Capacitor, 330 pF
×1
Ceramic Disc Capacitor, 100 pF
Ceramic Disc Capacitor, 100 pF
×1
Linear Regulator (7805)
Linear Regulator (7805)
×1
Toggle Switch, Toggle
Toggle Switch, Toggle
×2
Illuminated Pushbutton Operator, Yellow
Illuminated Pushbutton Operator, Yellow
×2
Solderless Breadboard Full Size
Solderless Breadboard Full Size
×1

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)

Story

Read more

Custom parts and enclosures

Case

Housing for the CO2 monitor when soldered on card

Schematics

Experimental breadboard setup

Experimental setup with all components

Setup and Card layout

Card layout for soldering

Code

CO2 monitor software for arduino nano

C/C++
/*******************************************************************************************************
 * 
 * 
 *   Einbinden von Libraries
 * 
 * 
 *******************************************************************************************************/


//
// EEPROM
//

#include <EEPROM.h>


//
// Time
//

#include "TimeLib.h"
#include <DS1307RTC.h>


//
// Display
//

/*
PINOUT:
MODUL                  Arduino pin
============================================
BLK                    GND
BLA                    +3.3V (LED backlight)
PSB (SPI)              GND (-> Serielle Daten erwarten)
E (SCK)                D13
R/W (MOSI)             D11
RS (CS)                D10
VCC                    +5V
GND                    GND
*/


#include "U8glib.h"
#define CS_PIN 10
U8GLIB_ST7920_128X64_1X u8g(CS_PIN);

define logo_width 64
#define logo_height 64

static const unsigned char PROGMEM logo_bmp[] = { 
0x0, 	0x0, 	0x0, 	0xE0, 	0x7, 	0x0, 	0x0, 	0x0, 
0x0, 	0x0, 	0x0, 	0x10, 	0x8, 	0x0, 	0x0, 	0x0, 
0x0, 	0x0, 	0x0, 	0x10, 	0x8, 	0x0, 	0x0, 	0x0, 
0x0, 	0x0, 	0x1C, 	0x10, 	0x8, 	0x38, 	0x0, 	0x0, 
0x0, 	0x0, 	0x13, 	0x10, 	0x8, 	0xC8, 	0x0, 	0x0, 
0x0, 	0xC0, 	0x20, 	0x10, 	0x8, 	0x8, 	0x3, 	0x0, 
0x0, 	0x20, 	0x20, 	0x10, 	0x8, 	0x4, 	0x4, 	0x0, 
0x0, 	0x20, 	0x20, 	0x1E, 	0x78, 	0x4, 	0x4, 	0x0, 
0x0, 	0x60, 	0xC0, 	0x1, 	0x80, 	0x3, 	0x6, 	0x0, 
0x0, 	0x40, 	0x0, 	0x1C, 	0x0, 	0x0, 	0x2, 	0x0, 
0x0, 	0x80, 	0x0, 	0x1E, 	0x0, 	0x0, 	0x1, 	0x0, 
0x0, 	0x0, 	0x1, 	0x3E, 	0x0, 	0x80, 	0x0, 	0x0, 
0x0, 	0x0, 	0x1, 	0x3F, 	0x0, 	0x80, 	0x0, 	0x0, 
0xC0, 	0x81, 	0x0, 	0x27, 	0x0, 	0x0, 	0x81, 	0x3, 
0x20, 	0x43, 	0x0, 	0x23, 	0x0, 	0x0, 	0xC2, 	0x4, 
0x20, 	0x24, 	0x0, 	0x63, 	0x0, 	0x0, 	0x24, 	0x4, 
0x10, 	0x18, 	0x0, 	0x21, 	0x0, 	0x0, 	0x18, 	0x8, 
0x10, 	0xE0, 	0xFF, 	0xFF, 	0xFF, 	0xFF, 	0xF, 	0x8, 
0x8, 	0x0, 	0x0, 	0x31, 	0x0, 	0x0, 	0x0, 	0x10, 
0x8, 	0x0, 	0x0, 	0x31, 	0x0, 	0x0, 	0x0, 	0x10, 
0x18, 	0x0, 	0x0, 	0x39, 	0x0, 	0x0, 	0x0, 	0x18, 
0xE0, 	0x0, 	0x0, 	0x9D, 	0xFF, 	0x7F, 	0x0, 	0x7, 
0x0, 	0x1, 	0x0, 	0x9E, 	0x0, 	0x40, 	0x80, 	0x0, 
0x0, 	0x1, 	0x0, 	0x9F, 	0x0, 	0x40, 	0x80, 	0x0, 
0x0, 	0xF1, 	0xFF, 	0xFF, 	0x0, 	0xC0, 	0x8F, 	0x0, 
0x80, 	0x0, 	0xC0, 	0x87, 	0x0, 	0x40, 	0x0, 	0x1, 
0x80, 	0x0, 	0xE0, 	0x83, 	0xFF, 	0x7F, 	0x0, 	0x1, 
0x80, 	0x0, 	0xF0, 	0x7, 	0x0, 	0x0, 	0x0, 	0x1, 
0xFE, 	0x0, 	0xF0, 	0x4, 	0x0, 	0x0, 	0x0, 	0x7F, 
0x1, 	0x0, 	0x78, 	0x4, 	0x0, 	0x0, 	0x0, 	0x80, 
0x1, 	0xE0, 	0xFF, 	0xFF, 	0xFF, 	0xFF, 	0xF, 	0x80, 
0x1, 	0x0, 	0x1C, 	0x7E, 	0x0, 	0x0, 	0x0, 	0x80, 
0x1, 	0x0, 	0x1C, 	0xFF, 	0x20, 	0x1, 	0x0, 	0x80, 
0x1, 	0x0, 	0x8C, 	0xFF, 	0x21, 	0x1, 	0x0, 	0x80, 
0x1, 	0x0, 	0x8C, 	0xCB, 	0x21, 	0x1, 	0x0, 	0x80, 
0xFE, 	0x0, 	0x8C, 	0x89, 	0x23, 	0x1, 	0x0, 	0x7F, 
0x80, 	0x0, 	0x8C, 	0x8, 	0x23, 	0x1, 	0x0, 	0x1, 
0x80, 	0xF0, 	0xFF, 	0xFF, 	0x3F, 	0xFF, 	0xF, 	0x1, 
0x80, 	0x0, 	0x88, 	0x10, 	0x23, 	0x1, 	0x0, 	0x1, 
0x0, 	0x1, 	0x8, 	0x11, 	0x23, 	0x1, 	0x80, 	0x0, 
0x0, 	0x1, 	0x10, 	0x12, 	0x21, 	0x1, 	0x80, 	0x0, 
0x0, 	0x1, 	0x20, 	0x90, 	0x20, 	0x1, 	0x80, 	0x0, 
0xE0, 	0x0, 	0xC0, 	0x70, 	0x20, 	0x1, 	0x0, 	0x7, 
0x18, 	0xE0, 	0xBF, 	0xBF, 	0xFF, 	0xFF, 	0xF, 	0x18, 
0x8, 	0x0, 	0x0, 	0x20, 	0x0, 	0x0, 	0x0, 	0x10, 
0x8, 	0x0, 	0x0, 	0x20, 	0x0, 	0x0, 	0x0, 	0x10, 
0x10, 	0x0, 	0x0, 	0x20, 	0x0, 	0x0, 	0x0, 	0x8, 
0x10, 	0x18, 	0x80, 	0x41, 	0x0, 	0x0, 	0x18, 	0x8, 
0x20, 	0x24, 	0xC0, 	0x43, 	0x0, 	0x0, 	0x24, 	0x4, 
0x20, 	0x43, 	0xE0, 	0x43, 	0x0, 	0x0, 	0xC2, 	0x4, 
0xC0, 	0x81, 	0xE0, 	0x47, 	0x0, 	0x0, 	0x81, 	0x3, 
0x0, 	0x0, 	0xE1, 	0x63, 	0x0, 	0x80, 	0x0, 	0x0, 
0x0, 	0x0, 	0xC1, 	0x21, 	0x0, 	0x80, 	0x0, 	0x0, 
0x0, 	0x80, 	0x80, 	0x1D, 	0x0, 	0x0, 	0x1, 	0x0, 
0x0, 	0x40, 	0x0, 	0x6, 	0x0, 	0x0, 	0x2, 	0x0, 
0x0, 	0x60, 	0xC0, 	0x1, 	0x80, 	0x3, 	0x6, 	0x0, 
0x0, 	0x20, 	0x20, 	0x1E, 	0x78, 	0x4, 	0x4, 	0x0, 
0x0, 	0x20, 	0x20, 	0x10, 	0x8, 	0x4, 	0x4, 	0x0, 
0x0, 	0xC0, 	0x20, 	0x10, 	0x8, 	0x8, 	0x3, 	0x0, 
0x0, 	0x0, 	0x13, 	0x10, 	0x8, 	0xC8, 	0x0, 	0x0, 
0x0, 	0x0, 	0x1C, 	0x10, 	0x8, 	0x38, 	0x0, 	0x0, 
0x0, 	0x0, 	0x0, 	0x10, 	0x8, 	0x0, 	0x0, 	0x0, 
0x0, 	0x0, 	0x0, 	0x10, 	0x8, 	0x0, 	0x0, 	0x0, 
0x0, 	0x0, 	0x0, 	0xE0, 	0x7, 	0x0, 	0x0, 	0x0
};





/*******************************************************************************************************
 * 
 * 
 *   globale Deklarationen
 * 
 * 
 *******************************************************************************************************/


const int               interrupt_pin =  3,
                        pwm_pin       =  5,
                        calib_pin     =  6,
                        set_pin       =  7,
                        menue_pin     =  8,
                        record_pin    =  9,
                        
                        set_led       = 14, // A0
                        menue_led     = 15, // A1
                        
                        
                        day_cell     = 4 * 24 * 2,
                        mon_cell     = day_cell  +1,
                        year_cell    = mon_cell  +1,
                        hour_cell    = year_cell +1,
                        minu_cell    = hour_cell +1,
                        start_cell   = minu_cell +1,
                        
                        y_min_abs    =  300;
                        
int                     cell_pointer = -1,
                        data[98],

                        pwm_data[5],
                        pwm_pointer   = 0,
                       
                        this_minute   = -1,
                        current_ppm   = y_min_abs;

bool                    recording     = false;                       
              
unsigned long           ppm_collect   = 0,
                        ppm_count     = 0;


                        
volatile bool           new_value                = false;
volatile unsigned long  millis_trigger_goes_high = 0,
                        millis_trigger_goes_low  = 0,
                        high_duration,
                        low_duration; 



/*******************************************************************************************************
 * 
 * 
 *   Serielle Ausgabe und PWM-Ausgabe
 * 
 * 
 *******************************************************************************************************/




void print_ddot()
{
  Serial.print(F(":"));
}


void print_two_digits(int val)
{
  if (val < 10) Serial.print(F("0"));
  Serial.print(val);
}


void serial_print_headline()
{
  Serial.println(F("\n\naktuelle Messwerte\n=================="));
}


//
// analoges Ausgangssignal fr PWM
//

void write_ppm_to_pwm(int ppm)
{
  float result = float(ppm) / 2500.0 * 255.0;  // 2500 ppm sind 100% PWM-Signal, also = 255
  if (result > 255.0) result = 255.0;
  else if (result < 0.0) result = 0.0;

  pwm_data[pwm_pointer] = int(result);
  pwm_pointer++;
  if (pwm_pointer > 5) pwm_pointer = 0;

  int mean = 0;
  for (int i = 0; i < 5; i++)
  {
    mean += pwm_data[i];
  }
  mean /= 5;
  
  analogWrite(pwm_pin, mean);
}





/*******************************************************************************************************
 * 
 * 
 *   ===================== Messroutinen =====================
 * 
 * 
 *******************************************************************************************************/




void detect_change() {
  if (digitalRead(interrupt_pin) == HIGH)
  {
    millis_trigger_goes_high = millis();
    low_duration             = millis_trigger_goes_high - millis_trigger_goes_low;
  }
  else
  {
    millis_trigger_goes_low  = millis();               
    high_duration            = millis_trigger_goes_low - millis_trigger_goes_high;    
    new_value = true;
  }
}




int ppmCO2(int high,
           int low)
{
  int            duration =  (high -2);
                 duration += (1002 - low);
  int            result    = duration*2; 
  
  return result;
}




/*******************************************************************************************************
 * 
 * 
 * L C D - Funktionen
 * 
 * 
 *******************************************************************************************************/




// 
// schreibe eine Integerzahl mit 4 Ziffern rechtsbndig
//
void draw_int(int xx_offset, int yy_offset, int the_int)
{
  String theString = "";
     
  int x = the_int;
  if (x == 0) x = 1;
  while(x < 1000)
  {
    theString += " ";
    x *= 10;
  }
  theString += String(the_int);
  char thisline_c[8];
  theString.toCharArray(thisline_c, 8);
           
  u8g.drawStr(xx_offset, yy_offset, thisline_c);
}


// 
// schreibe Zeit und Datum ins Display
//
void draw_time()
{
  String theString             = "";                  // Zeitstring erstellen
  char   thisline_c[8];

  if (hour() < 10) theString   = F("0"); 
  theString += String(hour());
  if (second()%2==0) theString += F(":");
  else               theString += F(" ");
  
  if (minute() < 10) theString += F("0"); 
  theString += String(minute());
  
  theString.toCharArray(thisline_c, 7);              // Zeitstring ausgeben
  u8g.setFont(u8g_font_5x8); // Schriftart ndern
  u8g.drawStr(0, 7, thisline_c);


  if (day() < 10) theString   = F("0");             // Datumsstring erstellen
  else            theString   = "";

  theString += String(day())
            +  F(" ");
  
  if (month() < 10) theString += F("0");            // Datumsstring ausgeben
  theString += String(month());
  
  theString.toCharArray(thisline_c, 7);
  
  u8g.setFont(u8g_font_5x8); // Schriftart ndern
  u8g.drawStr(0, 15, thisline_c);

  u8g.drawLine(11, 13, 12, 13);                   // Datumspunkt setzen
  u8g.drawLine(11, 14, 12, 14);

  if ((second()%2==0)&&(digitalRead(record_pin)==LOW))
  {
    u8g.drawStr(124, 4, F("r"));
  }
}


//
// Schreibe gro ppm-Wert ins Display
//
void draw_ppm(int ppm)
{
  ppm +=  5;                     // ppm-Werte auf 10er-Stelle runden
  ppm /= 10;
  ppm *= 10;

  u8g.setFont(u8g_font_courB18); // Schriftart ndern
  draw_int(25, 16, ppm);

  u8g.setFont(u8g_font_helvR08); // Schriftart ndern
  u8g.drawStr(81, 16, F(" ppm CO"));
  u8g.setFont(u8g_font_5x8);    // Schriftart ndern
  u8g.drawStr(123, 20, F("2"));
}


//
// Zeichne Diagramm
//
void draw_graph()
{
  const int y_ddelta  =    200,
            y_res     =     63,
            y_offset  =      3,
            y_width   =     40,
              
            x_res     =    127,
            x_offset  =     27;
            
  int       y_range,
            y_delta;



  
  //
  // zeichne x-Achse
  //
  
  u8g.drawLine(x_offset, y_res-3, x_res, y_res-3);
  for (int i = 1; i < 25; i++)
  {
    if (i%6==0) u8g.drawLine(x_offset+i*4, y_res-3,x_offset+i*4, y_res);
    else        u8g.drawLine(x_offset+i*4, y_res-3,x_offset+i*4, y_res-1);
  }
  
  
  //
  // beschrifte x-Achse
  //
  
  u8g.setFont(u8g_font_5x8); // Schriftart ndern
  for (int i = 0; i < 5; i++)
  {
    if (i == 1) draw_int(x_offset-16 + 24*i, y_res-4, i*6);    // zeichne die $6$ um 2 Pixel nach links versetzt
    else        draw_int(x_offset-14 + 24*i, y_res-4, i*6);
  }
  
  
  //
  // zeichne y-Achse
  //
  
  u8g.drawLine(x_offset, y_res, x_offset, y_res-y_width-y_offset);
  for (int i = 0; i < 5; i++)
  {
     u8g.drawLine(x_offset, y_res-i*10-y_offset, x_offset-3, y_res-i*10-y_offset);
  }

  
  //
  // ermittle y_delta
  //
  
  y_range = y_min_abs + y_ddelta;
  for (int i = 0; i < 96; i++)
  {
    while (data[i] > y_range)
    {
      y_range+=y_ddelta;
    }
  }   

  while (current_ppm > y_range) y_range += y_ddelta;
  
  y_delta = (y_range-y_min_abs)/4;


  //
  // beschrifte y-Achse
  //
  
  for (int i = 0; i < 5; i++)
  {
    draw_int(0, y_res-i*10, y_min_abs+i*y_delta);
  }
  
  
  //
  // zeichne Messwert
  //

  if (second()%2==0)
  {
    int val = y_min_abs;                                                     // stelle sicher, dass ein Wert >= 300 ppm vorliegt
    if (current_ppm > y_min_abs) val = current_ppm;
                                                                             // zeichne Linie
    int x = x_offset + hour()*4 + (minute()/15 +1);
    u8g.drawLine(x, y_res - y_offset, 
                 x, y_res - y_offset - int(float((val-y_min_abs))/float((y_range-y_min_abs))*float(y_width)));
  }
  
  
  //
  // zeichne Graph
  //

  for (int i = 0; i < 94; i++)
  {
    int j  = i+1;
    
    if ((data[i] >= y_min_abs)&&(data[j] >= y_min_abs))                       // Der Graph wird nur gezeichnet, wenn Anfangs- und Endpunkt definiert sind, also
    {                                                                         // einen Messwert >= 300 ppm aufweisen.
      int y0 = y_res - y_offset - int(float((data[i]-y_min_abs))/float((y_range-y_min_abs))*float(y_width)),
          y1 = y_res - y_offset - int(float((data[j]-y_min_abs))/float((y_range-y_min_abs))*float(y_width));
        
      u8g.drawLine(i+x_offset,   y0,
                   i+x_offset+1, y1);
    }
  }
}


/*******************************************************************************************************
 * 
 * 
 *   ===================== EEPROM-Protokoll =====================
 * 
 * 
 *******************************************************************************************************/

 
 
 
void begin_protokoll()
{
  EEPROM.put(day_cell,  byte(day()));
  EEPROM.put(mon_cell,  byte(month()));
  EEPROM.put(year_cell, byte(year()%2000));
  EEPROM.put(hour_cell, byte(hour()));
  EEPROM.put(minu_cell, byte(minute()));
  cell_pointer = start_cell;                     
}


void record(byte val)
{
  if (cell_pointer < 1024)                         // Wenn noch Speicher frei ist
  {
    EEPROM.put(cell_pointer, val);                 // Daten speichern
    cell_pointer++;                                // Zeiger auf nchstes Element setzen und
    if (cell_pointer < 1024)                       // falls dies vergbar ist:
    {
      EEPROM.put(cell_pointer, 0);                 // setze dieses auf "0", um Ende der Aufzeichnung zu markieren.
    }
  }
}


void print_two_digits_eeprom(int cell)
{
  byte val;
  EEPROM.get(cell, val);
  print_two_digits(val);
}



void replay()
{ 
  Serial.println(F("\nhh:mm:ss; CO2/ppm"));
  for (int i = 0; i < 96; i++)
  {
    print_two_digits(i/4);                       // gib Daten des Displayspeichers auf die serielle Schnittstelle
    print_ddot();
    print_two_digits((i-(i/4)*4)*15);
    Serial.print(F(":00;"));
    Serial.println(data[i]);
  }

  byte val;
 
  Serial.println(F("\ndd.mm.yy hh:mm:ss; CO2/ppm"));
  print_two_digits_eeprom(day_cell);
  Serial.print(F("."));
  print_two_digits_eeprom(mon_cell);
  Serial.print(F("."));
  print_two_digits_eeprom(year_cell);
  Serial.print(F(" "));
  print_two_digits_eeprom(hour_cell);
  print_ddot();
  print_two_digits_eeprom(minu_cell);
  Serial.print(F(":00"));
  
  for (int cell = start_cell; cell < 1024; cell++)
  {
    EEPROM.get(cell, val);
    if (val == 0) break;
    Serial.print(";");
    Serial.println(int(val)*10);
  }

  serial_print_headline();
}


/*******************************************************************************************************
 * 
 * 
 *   ===================== Zeit und Datum setzen =====================
 * 
 * 
 *******************************************************************************************************/




void wait_for_high()
{
  int i = 100;
  do
  {
    i--;
    if ((digitalRead(menue_pin)==LOW)||(digitalRead(set_pin)==LOW)) i = 100;
    delay(1);
  }
  while ( i > 0);
}


bool select_item()
{
  wait_for_high();                                                       // stelle sicher, dass kein KNopf gedrckt
  while((digitalRead(menue_pin)==HIGH)&&(digitalRead(set_pin)==HIGH))    // warte, solange nichts gedrckt
  {
    delay(1);
  }
  
  bool menue_pressed = (digitalRead(menue_pin)==LOW);                    // merke, welcher Knopf gedrckt
  wait_for_high();                                                       // warte bis alle Knpfe losgelassen
  return menue_pressed;                                                  // gib gedrckten Knopf zurck
}



void display_menu(String str)
{
  u8g.firstPage();  

  u8g.setFont(u8g_font_helvR08);
  u8g.drawStr(0,  8, F("Menue"));

  do
  {
    u8g.drawStr(0,  16, F("======"));
  
    char thisline_c[32];
    str.toCharArray(thisline_c, 30);
    u8g.drawStr(0, 34, thisline_c);
  }   
  while (u8g.nextPage());
}


void lcd_Time_Date(int y, int h, int m, int s, char separate, int blankpos)
{
  String theString             = "";                  // Zeitstring erstellen
  char   thisline_c[8];

  if (h < 10) theString        = F("0"); 
  theString += String(h)
            +  separate;
  
  if (m < 10) theString += F("0"); 
  theString += String(m)
            +  separate;
  
  if (s < 10) theString += F("0"); 
  theString += String(s);

  theString.toCharArray(thisline_c, 10);              // Zeitstring in Array umwandeln
  if (blankpos > -1)                                  // Zeichen lschen fr Blinkeffekt   
  {
    thisline_c[blankpos]   = '_';
    thisline_c[blankpos+1] = '_';
    digitalWrite(set_led, HIGH);
  }
  else
  {
    digitalWrite(set_led, LOW);
  }

  do
  {
    u8g.drawStr(y, 40, thisline_c);
  }
  while (u8g.nextPage());
}



int setdigits(int a, int b, int c, int abc, int minval, int maxval, char sep)
{
  int  digitwait   = 0,
       blankblink = -1;
  bool set_button, menue_button;

  do
  { 
    do
    { 
      if (second()%2==0) blankblink = -1;
      else               blankblink = abc*3;
      u8g.firstPage();
      lcd_Time_Date(30, a, b, c, sep, blankblink);
      set_button   = digitalRead(set_pin)   == LOW;
      menue_button = digitalRead(menue_pin) == LOW;
    }while(!set_button && !menue_button);

    if (set_button)
    {
      switch (abc)
      {
        case 0: a++;
                if (a > maxval) a = minval;
                break;
        case 1: b++;
                if (b > maxval) b = minval;
                break;
        case 2: c++;
                if (c > maxval) c = minval;
                break;
      }

      if (digitwait < 5)
      {
        delay(666);
        digitwait++;
      }
      else{
        delay(222);
      }
    }
    else
    {      
      wait_for_high();
    }
    
  }while(!menue_button); 

  digitalWrite(set_led, LOW);

  int val;
  switch (abc)
  {
    case 0: val = a;
            break;
    case 1: val = b;
            break;
    case 2: val = c;
            break;
  }
  return val;
}



void set_time()
{
  int t_hour = hour(),
      t_min  = minute(),
      t_sec  = 0;

  t_hour = setdigits(t_hour, t_min, t_sec, 0, 0, 23, ':');
  t_min  = setdigits(t_hour, t_min, t_sec, 1, 0, 59, ':');

  setTime(t_hour, t_min, 0, day(), month(), year());
  RTC.set(now());
  delay(1000);
}


void set_date()
{
  int d_day  = day(),
      d_mon  = month(),
      d_yea  = year()%100;

  d_day = setdigits(d_day, d_mon, d_yea, 0, 0, 30, '.');
  d_mon = setdigits(d_day, d_mon, d_yea, 1, 1, 12, '.');
  d_yea = setdigits(d_day, d_mon, d_yea, 2, 0, 40, '.');

  setTime(hour(), minute(), second(), d_day, d_mon, d_yea+2000);
  //RTC.set(now());
}



void clear_data(bool eeprom)
{
  display_menu(F("-> wird geloescht..."));
  for (int i = 0; i < 96; i++)
  {
    data[i] = y_min_abs;
    if (eeprom) EEPROM.put(i*2, int(0));

  }
  delay(1000);
}



void calibrate_400ppm()
{
  display_menu(F("-> wird kalibriert...")); 
  pinMode(calib_pin,      OUTPUT);
  digitalWrite(calib_pin, LOW);
  delay(10*1000);
  digitalWrite(calib_pin, HIGH);
  pinMode(calib_pin,      INPUT);
}



void menue()
{  
  display_menu(F("Messdaten ausgeben?"));
  if (!select_item())
  {
    replay(); 
    return;
  } 


  display_menu(F("Display loeschen?"));
  if (!select_item())
  {
    clear_data(false);
    return;
  } 


  display_menu(F("Speicher loeschen?"));
  if (!select_item())
  {
    clear_data(true);
    return;
  } 


  display_menu(F("Uhrzeit stellen?"));
  if (!select_item())
  {
    set_time(); 
    return;
  } 


  display_menu(F("Datum stellen?"));
  if (!select_item())
  {
    set_date();
    return;
  } 


  display_menu(F("Sensor 400ppm kalibrieren?"));
  if (!select_item())
  {
    calibrate_400ppm();
    return;
  } 
}




/*******************************************************************************************************
 * 
 * 
 *   ===================== SETUP =====================
 * 
 * 
 *******************************************************************************************************/




void setup() 
{
  //
  // Startbildschirm: Logo
  //
  
  u8g.firstPage();
  do
  {  
    u8g.drawXBMP(32, 0, logo_width, logo_height, logo_bmp);
  }
  while (u8g.nextPage()); 
  delay(2000);

  
  //
  // definiere i/o-Pins
  //
  
  pinMode(calib_pin,  INPUT);
  pinMode(set_pin,    INPUT_PULLUP);
  pinMode(menue_pin,  INPUT_PULLUP);
  pinMode(record_pin, INPUT_PULLUP);

  pinMode(menue_led,  OUTPUT);
  pinMode(set_led,    OUTPUT);
  
  pinMode(pwm_pin,    OUTPUT);
  analogWrite(pwm_pin, 0);
  

  
  //
  // serielle Ausgabe
  //
  
  Serial.begin(9600);



  //
  // RTC
  //
  
  setSyncProvider(RTC.get);   // the function to get the time from the RTC  


  
  //
  // Messwertspeicher aus dem EEPROM seriell ausgeben und ins RAM bertragen
  //

  for (int i = 0; i < 96; i++)
  {
    EEPROM.get(2*i, data[i]);                                 // bertrage EEPROM-Daten in den Arbeitsspeicher
  }

  serial_print_headline();  
  for (int i = 0; i < 5; i++)
  {
    pwm_data[i] = y_min_abs;
  }
  
  attachInterrupt(digitalPinToInterrupt(interrupt_pin), detect_change, CHANGE);  

  u8g.setFont(u8g_font_helvR08); // Schriftart whlen
  u8g.firstPage();
  do
  {  
    u8g.drawStr(10, 25, F("CO2-Logger V 1.0 V/'21"));
    u8g.drawStr(10, 45, F("Warte auf Messdaten..."));
  }
  while (u8g.nextPage());  

  
  for (int i = 0; i < 7; i++)                    // berpringe die ersten 7 Messwerte, um konstante Werte zu erhalten
  {
    while (!new_value)
    {
      delay(100);
    }
    new_value = false;
  }
}



/*******************************************************************************************************
 * 
 * 
 *   ===================== LOOP =====================
 * 
 * 
 *******************************************************************************************************/




void loop() 
{
  if (new_value)
  {
    //
    // hole neuen Messwert ab und schreibe ihn auf den PWM-Ausgang
    //
    
    new_value    = false;
    current_ppm  = ppmCO2(high_duration, low_duration);   
    write_ppm_to_pwm(current_ppm);
    
  
    //
    // serielle Ausgabe des Messwerts
    //
    
    print_two_digits(hour());
    print_ddot();
    print_two_digits(minute());
    print_ddot();
    print_two_digits(second());
    Serial.print(F("; "));
    Serial.println(current_ppm);
    
  
    //
    // zeige auf dem Display den aktuellen Wert und den Graph fr die letzten 24 Stunden an
    //
    
    u8g.firstPage();  
    do
    {
      draw_time();
      draw_ppm(current_ppm);
      draw_graph();
    }   
    while (u8g.nextPage());


    //
    // halte jede Minute einen Wert fest
    //
    
    if (minute() != this_minute)
    {
      ppm_collect += current_ppm;
      ppm_count++;
      this_minute = minute();
      
      if ((this_minute%15) == 0)
      {
        ppm_collect /= ppm_count;
        if      (ppm_collect <  y_min_abs) ppm_collect =  y_min_abs;
        else if (ppm_collect > 2000) ppm_collect = 2000;
        
        int data_pointer = hour()*4 + (minute()/15);
        data[data_pointer] = ppm_collect;
        
        EEPROM.put(data_pointer*2, ppm_collect);
        if (ppm_collect > 2554) ppm_collect = 2554;

        
        //
        // Aufzeichnung von Messwerten vorbereiten
        //
    
        if ((digitalRead(record_pin) == LOW)&&(!recording))       // Aufzeichnungsschalter ist gerade umgelegt worden:
        {        
          recording = true;                                       // neuen Aufzeichnungsstatus merken und                        
          begin_protokoll();                                      // Aufzeichnungsbeginn speichern und Pointer auf Anfang setzen (= alle alten Messwerte damit lschen)
        }
        else if (digitalRead(record_pin) == HIGH)
        {
          recording = false;
        }
        
        if (recording) record(byte((ppm_collect+5)/10));          // Schreibe Daten in den groen Datenspeicher, wenn seit Anfang an der Record-Schalter kontinuierlich bettigt war
        
        ppm_collect = 0;
        ppm_count   = 0;

        for (int i = 0; i < 4; i++)                               // leere Datenspeicher hinter den aktuellen Messwerten, um eine
        {                                                         // graphische Lcke zu erzeugen
          data_pointer++;
          if (data_pointer > 24*4-1) data_pointer = 0;
          data[data_pointer] = 0;
        }
      }
    }
  }
  
  if (digitalRead(menue_pin) == LOW) 
  {
    digitalWrite(menue_led, HIGH);
    menue();
    digitalWrite(menue_led, LOW);
  }
  
  delay(100);
}

Credits

enirstad

enirstad

0 projects • 1 follower

Comments