CO2-Monitor
A small Monitor that displays the progression of the CO2-concentration within one day.
Components and supplies
Through Hole Resistor, 220 ohm
Ceramic Disc Capacitor, 330 pF
Arduino Nano R3
Resistor 100k ohm
Toggle Switch, Toggle
Through Hole Resistor, 10 kohm
Solderless Breadboard Full Size
Illuminated Pushbutton Operator, Yellow
Grove - Carbon Dioxide Sensor(MH-Z16)
Capacitor 1000 µF
Graphic LCD, 128 x 64
Through Hole Resistor, 100 kohm
Linear Regulator (7805)
Electrolytic Capacitor, 330 µF
Ceramic Disc Capacitor, 100 pF
Tools and machines
Soldering iron (generic)
Project description
Code
CO2 monitor software for arduino nano
c_cpp
1/******************************************************************************************************* 2 * 3 * 4 * Einbinden von Libraries 5 * 6 * 7 *******************************************************************************************************/ 8 9 10// 11// EEPROM 12// 13 14#include <EEPROM.h> 15 16 17// 18// Time 19// 20 21#include "TimeLib.h" 22#include <DS1307RTC.h> 23 24 25// 26// Display 27// 28 29/* 30PINOUT: 31MODUL Arduino pin 32============================================ 33BLK GND 34BLA +3.3V (LED backlight) 35PSB (SPI) GND (-> Serielle Daten erwarten) 36E (SCK) D13 37R/W (MOSI) D11 38RS (CS) D10 39VCC +5V 40GND GND 41*/ 42 43 44#include "U8glib.h" 45#define CS_PIN 10 46U8GLIB_ST7920_128X64_1X u8g(CS_PIN); 47 48define logo_width 64 49#define logo_height 64 50 51static const unsigned char PROGMEM logo_bmp[] = { 520x0, 0x0, 0x0, 0xE0, 0x7, 0x0, 0x0, 0x0, 530x0, 0x0, 0x0, 0x10, 0x8, 0x0, 0x0, 0x0, 540x0, 0x0, 0x0, 0x10, 0x8, 0x0, 0x0, 0x0, 550x0, 0x0, 0x1C, 0x10, 0x8, 0x38, 0x0, 0x0, 560x0, 0x0, 0x13, 0x10, 0x8, 0xC8, 0x0, 0x0, 570x0, 0xC0, 0x20, 0x10, 0x8, 0x8, 0x3, 0x0, 580x0, 0x20, 0x20, 0x10, 0x8, 0x4, 0x4, 0x0, 590x0, 0x20, 0x20, 0x1E, 0x78, 0x4, 0x4, 0x0, 600x0, 0x60, 0xC0, 0x1, 0x80, 0x3, 0x6, 0x0, 610x0, 0x40, 0x0, 0x1C, 0x0, 0x0, 0x2, 0x0, 620x0, 0x80, 0x0, 0x1E, 0x0, 0x0, 0x1, 0x0, 630x0, 0x0, 0x1, 0x3E, 0x0, 0x80, 0x0, 0x0, 640x0, 0x0, 0x1, 0x3F, 0x0, 0x80, 0x0, 0x0, 650xC0, 0x81, 0x0, 0x27, 0x0, 0x0, 0x81, 0x3, 660x20, 0x43, 0x0, 0x23, 0x0, 0x0, 0xC2, 0x4, 670x20, 0x24, 0x0, 0x63, 0x0, 0x0, 0x24, 0x4, 680x10, 0x18, 0x0, 0x21, 0x0, 0x0, 0x18, 0x8, 690x10, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xF, 0x8, 700x8, 0x0, 0x0, 0x31, 0x0, 0x0, 0x0, 0x10, 710x8, 0x0, 0x0, 0x31, 0x0, 0x0, 0x0, 0x10, 720x18, 0x0, 0x0, 0x39, 0x0, 0x0, 0x0, 0x18, 730xE0, 0x0, 0x0, 0x9D, 0xFF, 0x7F, 0x0, 0x7, 740x0, 0x1, 0x0, 0x9E, 0x0, 0x40, 0x80, 0x0, 750x0, 0x1, 0x0, 0x9F, 0x0, 0x40, 0x80, 0x0, 760x0, 0xF1, 0xFF, 0xFF, 0x0, 0xC0, 0x8F, 0x0, 770x80, 0x0, 0xC0, 0x87, 0x0, 0x40, 0x0, 0x1, 780x80, 0x0, 0xE0, 0x83, 0xFF, 0x7F, 0x0, 0x1, 790x80, 0x0, 0xF0, 0x7, 0x0, 0x0, 0x0, 0x1, 800xFE, 0x0, 0xF0, 0x4, 0x0, 0x0, 0x0, 0x7F, 810x1, 0x0, 0x78, 0x4, 0x0, 0x0, 0x0, 0x80, 820x1, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xF, 0x80, 830x1, 0x0, 0x1C, 0x7E, 0x0, 0x0, 0x0, 0x80, 840x1, 0x0, 0x1C, 0xFF, 0x20, 0x1, 0x0, 0x80, 850x1, 0x0, 0x8C, 0xFF, 0x21, 0x1, 0x0, 0x80, 860x1, 0x0, 0x8C, 0xCB, 0x21, 0x1, 0x0, 0x80, 870xFE, 0x0, 0x8C, 0x89, 0x23, 0x1, 0x0, 0x7F, 880x80, 0x0, 0x8C, 0x8, 0x23, 0x1, 0x0, 0x1, 890x80, 0xF0, 0xFF, 0xFF, 0x3F, 0xFF, 0xF, 0x1, 900x80, 0x0, 0x88, 0x10, 0x23, 0x1, 0x0, 0x1, 910x0, 0x1, 0x8, 0x11, 0x23, 0x1, 0x80, 0x0, 920x0, 0x1, 0x10, 0x12, 0x21, 0x1, 0x80, 0x0, 930x0, 0x1, 0x20, 0x90, 0x20, 0x1, 0x80, 0x0, 940xE0, 0x0, 0xC0, 0x70, 0x20, 0x1, 0x0, 0x7, 950x18, 0xE0, 0xBF, 0xBF, 0xFF, 0xFF, 0xF, 0x18, 960x8, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 970x8, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 980x10, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x8, 990x10, 0x18, 0x80, 0x41, 0x0, 0x0, 0x18, 0x8, 1000x20, 0x24, 0xC0, 0x43, 0x0, 0x0, 0x24, 0x4, 1010x20, 0x43, 0xE0, 0x43, 0x0, 0x0, 0xC2, 0x4, 1020xC0, 0x81, 0xE0, 0x47, 0x0, 0x0, 0x81, 0x3, 1030x0, 0x0, 0xE1, 0x63, 0x0, 0x80, 0x0, 0x0, 1040x0, 0x0, 0xC1, 0x21, 0x0, 0x80, 0x0, 0x0, 1050x0, 0x80, 0x80, 0x1D, 0x0, 0x0, 0x1, 0x0, 1060x0, 0x40, 0x0, 0x6, 0x0, 0x0, 0x2, 0x0, 1070x0, 0x60, 0xC0, 0x1, 0x80, 0x3, 0x6, 0x0, 1080x0, 0x20, 0x20, 0x1E, 0x78, 0x4, 0x4, 0x0, 1090x0, 0x20, 0x20, 0x10, 0x8, 0x4, 0x4, 0x0, 1100x0, 0xC0, 0x20, 0x10, 0x8, 0x8, 0x3, 0x0, 1110x0, 0x0, 0x13, 0x10, 0x8, 0xC8, 0x0, 0x0, 1120x0, 0x0, 0x1C, 0x10, 0x8, 0x38, 0x0, 0x0, 1130x0, 0x0, 0x0, 0x10, 0x8, 0x0, 0x0, 0x0, 1140x0, 0x0, 0x0, 0x10, 0x8, 0x0, 0x0, 0x0, 1150x0, 0x0, 0x0, 0xE0, 0x7, 0x0, 0x0, 0x0 116}; 117 118 119 120 121 122/******************************************************************************************************* 123 * 124 * 125 * globale Deklarationen 126 * 127 * 128 *******************************************************************************************************/ 129 130 131const int interrupt_pin = 3, 132 pwm_pin = 5, 133 calib_pin = 6, 134 set_pin = 7, 135 menue_pin = 8, 136 record_pin = 9, 137 138 set_led = 14, // A0 139 menue_led = 15, // A1 140 141 142 day_cell = 4 * 24 * 2, 143 mon_cell = day_cell +1, 144 year_cell = mon_cell +1, 145 hour_cell = year_cell +1, 146 minu_cell = hour_cell +1, 147 start_cell = minu_cell +1, 148 149 y_min_abs = 300; 150 151int cell_pointer = -1, 152 data[98], 153 154 pwm_data[5], 155 pwm_pointer = 0, 156 157 this_minute = -1, 158 current_ppm = y_min_abs; 159 160bool recording = false; 161 162unsigned long ppm_collect = 0, 163 ppm_count = 0; 164 165 166 167volatile bool new_value = false; 168volatile unsigned long millis_trigger_goes_high = 0, 169 millis_trigger_goes_low = 0, 170 high_duration, 171 low_duration; 172 173 174 175/******************************************************************************************************* 176 * 177 * 178 * Serielle Ausgabe und PWM-Ausgabe 179 * 180 * 181 *******************************************************************************************************/ 182 183 184 185 186void print_ddot() 187{ 188 Serial.print(F(":")); 189} 190 191 192void print_two_digits(int val) 193{ 194 if (val < 10) Serial.print(F("0")); 195 Serial.print(val); 196} 197 198 199void serial_print_headline() 200{ 201 Serial.println(F("\ 202\ 203aktuelle Messwerte\ 204==================")); 205} 206 207 208// 209// analoges Ausgangssignal fr PWM 210// 211 212void write_ppm_to_pwm(int ppm) 213{ 214 float result = float(ppm) / 2500.0 * 255.0; // 2500 ppm sind 100% PWM-Signal, also = 255 215 if (result > 255.0) result = 255.0; 216 else if (result < 0.0) result = 0.0; 217 218 pwm_data[pwm_pointer] = int(result); 219 pwm_pointer++; 220 if (pwm_pointer > 5) pwm_pointer = 0; 221 222 int mean = 0; 223 for (int i = 0; i < 5; i++) 224 { 225 mean += pwm_data[i]; 226 } 227 mean /= 5; 228 229 analogWrite(pwm_pin, mean); 230} 231 232 233 234 235 236/******************************************************************************************************* 237 * 238 * 239 * ===================== Messroutinen ===================== 240 * 241 * 242 *******************************************************************************************************/ 243 244 245 246 247void detect_change() { 248 if (digitalRead(interrupt_pin) == HIGH) 249 { 250 millis_trigger_goes_high = millis(); 251 low_duration = millis_trigger_goes_high - millis_trigger_goes_low; 252 } 253 else 254 { 255 millis_trigger_goes_low = millis(); 256 high_duration = millis_trigger_goes_low - millis_trigger_goes_high; 257 new_value = true; 258 } 259} 260 261 262 263 264int ppmCO2(int high, 265 int low) 266{ 267 int duration = (high -2); 268 duration += (1002 - low); 269 int result = duration*2; 270 271 return result; 272} 273 274 275 276 277/******************************************************************************************************* 278 * 279 * 280 * L C D - Funktionen 281 * 282 * 283 *******************************************************************************************************/ 284 285 286 287 288// 289// schreibe eine Integerzahl mit 4 Ziffern rechtsbndig 290// 291void draw_int(int xx_offset, int yy_offset, int the_int) 292{ 293 String theString = ""; 294 295 int x = the_int; 296 if (x == 0) x = 1; 297 while(x < 1000) 298 { 299 theString += " "; 300 x *= 10; 301 } 302 theString += String(the_int); 303 char thisline_c[8]; 304 theString.toCharArray(thisline_c, 8); 305 306 u8g.drawStr(xx_offset, yy_offset, thisline_c); 307} 308 309 310// 311// schreibe Zeit und Datum ins Display 312// 313void draw_time() 314{ 315 String theString = ""; // Zeitstring erstellen 316 char thisline_c[8]; 317 318 if (hour() < 10) theString = F("0"); 319 theString += String(hour()); 320 if (second()%2==0) theString += F(":"); 321 else theString += F(" "); 322 323 if (minute() < 10) theString += F("0"); 324 theString += String(minute()); 325 326 theString.toCharArray(thisline_c, 7); // Zeitstring ausgeben 327 u8g.setFont(u8g_font_5x8); // Schriftart ndern 328 u8g.drawStr(0, 7, thisline_c); 329 330 331 if (day() < 10) theString = F("0"); // Datumsstring erstellen 332 else theString = ""; 333 334 theString += String(day()) 335 + F(" "); 336 337 if (month() < 10) theString += F("0"); // Datumsstring ausgeben 338 theString += String(month()); 339 340 theString.toCharArray(thisline_c, 7); 341 342 u8g.setFont(u8g_font_5x8); // Schriftart ndern 343 u8g.drawStr(0, 15, thisline_c); 344 345 u8g.drawLine(11, 13, 12, 13); // Datumspunkt setzen 346 u8g.drawLine(11, 14, 12, 14); 347 348 if ((second()%2==0)&&(digitalRead(record_pin)==LOW)) 349 { 350 u8g.drawStr(124, 4, F("r")); 351 } 352} 353 354 355// 356// Schreibe gro ppm-Wert ins Display 357// 358void draw_ppm(int ppm) 359{ 360 ppm += 5; // ppm-Werte auf 10er-Stelle runden 361 ppm /= 10; 362 ppm *= 10; 363 364 u8g.setFont(u8g_font_courB18); // Schriftart ndern 365 draw_int(25, 16, ppm); 366 367 u8g.setFont(u8g_font_helvR08); // Schriftart ndern 368 u8g.drawStr(81, 16, F(" ppm CO")); 369 u8g.setFont(u8g_font_5x8); // Schriftart ndern 370 u8g.drawStr(123, 20, F("2")); 371} 372 373 374// 375// Zeichne Diagramm 376// 377void draw_graph() 378{ 379 const int y_ddelta = 200, 380 y_res = 63, 381 y_offset = 3, 382 y_width = 40, 383 384 x_res = 127, 385 x_offset = 27; 386 387 int y_range, 388 y_delta; 389 390 391 392 393 // 394 // zeichne x-Achse 395 // 396 397 u8g.drawLine(x_offset, y_res-3, x_res, y_res-3); 398 for (int i = 1; i < 25; i++) 399 { 400 if (i%6==0) u8g.drawLine(x_offset+i*4, y_res-3,x_offset+i*4, y_res); 401 else u8g.drawLine(x_offset+i*4, y_res-3,x_offset+i*4, y_res-1); 402 } 403 404 405 // 406 // beschrifte x-Achse 407 // 408 409 u8g.setFont(u8g_font_5x8); // Schriftart ndern 410 for (int i = 0; i < 5; i++) 411 { 412 if (i == 1) draw_int(x_offset-16 + 24*i, y_res-4, i*6); // zeichne die $6$ um 2 Pixel nach links versetzt 413 else draw_int(x_offset-14 + 24*i, y_res-4, i*6); 414 } 415 416 417 // 418 // zeichne y-Achse 419 // 420 421 u8g.drawLine(x_offset, y_res, x_offset, y_res-y_width-y_offset); 422 for (int i = 0; i < 5; i++) 423 { 424 u8g.drawLine(x_offset, y_res-i*10-y_offset, x_offset-3, y_res-i*10-y_offset); 425 } 426 427 428 // 429 // ermittle y_delta 430 // 431 432 y_range = y_min_abs + y_ddelta; 433 for (int i = 0; i < 96; i++) 434 { 435 while (data[i] > y_range) 436 { 437 y_range+=y_ddelta; 438 } 439 } 440 441 while (current_ppm > y_range) y_range += y_ddelta; 442 443 y_delta = (y_range-y_min_abs)/4; 444 445 446 // 447 // beschrifte y-Achse 448 // 449 450 for (int i = 0; i < 5; i++) 451 { 452 draw_int(0, y_res-i*10, y_min_abs+i*y_delta); 453 } 454 455 456 // 457 // zeichne Messwert 458 // 459 460 if (second()%2==0) 461 { 462 int val = y_min_abs; // stelle sicher, dass ein Wert >= 300 ppm vorliegt 463 if (current_ppm > y_min_abs) val = current_ppm; 464 // zeichne Linie 465 int x = x_offset + hour()*4 + (minute()/15 +1); 466 u8g.drawLine(x, y_res - y_offset, 467 x, y_res - y_offset - int(float((val-y_min_abs))/float((y_range-y_min_abs))*float(y_width))); 468 } 469 470 471 // 472 // zeichne Graph 473 // 474 475 for (int i = 0; i < 94; i++) 476 { 477 int j = i+1; 478 479 if ((data[i] >= y_min_abs)&&(data[j] >= y_min_abs)) // Der Graph wird nur gezeichnet, wenn Anfangs- und Endpunkt definiert sind, also 480 { // einen Messwert >= 300 ppm aufweisen. 481 int y0 = y_res - y_offset - int(float((data[i]-y_min_abs))/float((y_range-y_min_abs))*float(y_width)), 482 y1 = y_res - y_offset - int(float((data[j]-y_min_abs))/float((y_range-y_min_abs))*float(y_width)); 483 484 u8g.drawLine(i+x_offset, y0, 485 i+x_offset+1, y1); 486 } 487 } 488} 489 490 491/******************************************************************************************************* 492 * 493 * 494 * ===================== EEPROM-Protokoll ===================== 495 * 496 * 497 *******************************************************************************************************/ 498 499 500 501 502void begin_protokoll() 503{ 504 EEPROM.put(day_cell, byte(day())); 505 EEPROM.put(mon_cell, byte(month())); 506 EEPROM.put(year_cell, byte(year()%2000)); 507 EEPROM.put(hour_cell, byte(hour())); 508 EEPROM.put(minu_cell, byte(minute())); 509 cell_pointer = start_cell; 510} 511 512 513void record(byte val) 514{ 515 if (cell_pointer < 1024) // Wenn noch Speicher frei ist 516 { 517 EEPROM.put(cell_pointer, val); // Daten speichern 518 cell_pointer++; // Zeiger auf nchstes Element setzen und 519 if (cell_pointer < 1024) // falls dies vergbar ist: 520 { 521 EEPROM.put(cell_pointer, 0); // setze dieses auf "0", um Ende der Aufzeichnung zu markieren. 522 } 523 } 524} 525 526 527void print_two_digits_eeprom(int cell) 528{ 529 byte val; 530 EEPROM.get(cell, val); 531 print_two_digits(val); 532} 533 534 535 536void replay() 537{ 538 Serial.println(F("\ 539hh:mm:ss; CO2/ppm")); 540 for (int i = 0; i < 96; i++) 541 { 542 print_two_digits(i/4); // gib Daten des Displayspeichers auf die serielle Schnittstelle 543 print_ddot(); 544 print_two_digits((i-(i/4)*4)*15); 545 Serial.print(F(":00;")); 546 Serial.println(data[i]); 547 } 548 549 byte val; 550 551 Serial.println(F("\ 552dd.mm.yy hh:mm:ss; CO2/ppm")); 553 print_two_digits_eeprom(day_cell); 554 Serial.print(F(".")); 555 print_two_digits_eeprom(mon_cell); 556 Serial.print(F(".")); 557 print_two_digits_eeprom(year_cell); 558 Serial.print(F(" ")); 559 print_two_digits_eeprom(hour_cell); 560 print_ddot(); 561 print_two_digits_eeprom(minu_cell); 562 Serial.print(F(":00")); 563 564 for (int cell = start_cell; cell < 1024; cell++) 565 { 566 EEPROM.get(cell, val); 567 if (val == 0) break; 568 Serial.print(";"); 569 Serial.println(int(val)*10); 570 } 571 572 serial_print_headline(); 573} 574 575 576/******************************************************************************************************* 577 * 578 * 579 * ===================== Zeit und Datum setzen ===================== 580 * 581 * 582 *******************************************************************************************************/ 583 584 585 586 587void wait_for_high() 588{ 589 int i = 100; 590 do 591 { 592 i--; 593 if ((digitalRead(menue_pin)==LOW)||(digitalRead(set_pin)==LOW)) i = 100; 594 delay(1); 595 } 596 while ( i > 0); 597} 598 599 600bool select_item() 601{ 602 wait_for_high(); // stelle sicher, dass kein KNopf gedrckt 603 while((digitalRead(menue_pin)==HIGH)&&(digitalRead(set_pin)==HIGH)) // warte, solange nichts gedrckt 604 { 605 delay(1); 606 } 607 608 bool menue_pressed = (digitalRead(menue_pin)==LOW); // merke, welcher Knopf gedrckt 609 wait_for_high(); // warte bis alle Knpfe losgelassen 610 return menue_pressed; // gib gedrckten Knopf zurck 611} 612 613 614 615void display_menu(String str) 616{ 617 u8g.firstPage(); 618 619 u8g.setFont(u8g_font_helvR08); 620 u8g.drawStr(0, 8, F("Menue")); 621 622 do 623 { 624 u8g.drawStr(0, 16, F("======")); 625 626 char thisline_c[32]; 627 str.toCharArray(thisline_c, 30); 628 u8g.drawStr(0, 34, thisline_c); 629 } 630 while (u8g.nextPage()); 631} 632 633 634void lcd_Time_Date(int y, int h, int m, int s, char separate, int blankpos) 635{ 636 String theString = ""; // Zeitstring erstellen 637 char thisline_c[8]; 638 639 if (h < 10) theString = F("0"); 640 theString += String(h) 641 + separate; 642 643 if (m < 10) theString += F("0"); 644 theString += String(m) 645 + separate; 646 647 if (s < 10) theString += F("0"); 648 theString += String(s); 649 650 theString.toCharArray(thisline_c, 10); // Zeitstring in Array umwandeln 651 if (blankpos > -1) // Zeichen lschen fr Blinkeffekt 652 { 653 thisline_c[blankpos] = '_'; 654 thisline_c[blankpos+1] = '_'; 655 digitalWrite(set_led, HIGH); 656 } 657 else 658 { 659 digitalWrite(set_led, LOW); 660 } 661 662 do 663 { 664 u8g.drawStr(y, 40, thisline_c); 665 } 666 while (u8g.nextPage()); 667} 668 669 670 671int setdigits(int a, int b, int c, int abc, int minval, int maxval, char sep) 672{ 673 int digitwait = 0, 674 blankblink = -1; 675 bool set_button, menue_button; 676 677 do 678 { 679 do 680 { 681 if (second()%2==0) blankblink = -1; 682 else blankblink = abc*3; 683 u8g.firstPage(); 684 lcd_Time_Date(30, a, b, c, sep, blankblink); 685 set_button = digitalRead(set_pin) == LOW; 686 menue_button = digitalRead(menue_pin) == LOW; 687 }while(!set_button && !menue_button); 688 689 if (set_button) 690 { 691 switch (abc) 692 { 693 case 0: a++; 694 if (a > maxval) a = minval; 695 break; 696 case 1: b++; 697 if (b > maxval) b = minval; 698 break; 699 case 2: c++; 700 if (c > maxval) c = minval; 701 break; 702 } 703 704 if (digitwait < 5) 705 { 706 delay(666); 707 digitwait++; 708 } 709 else{ 710 delay(222); 711 } 712 } 713 else 714 { 715 wait_for_high(); 716 } 717 718 }while(!menue_button); 719 720 digitalWrite(set_led, LOW); 721 722 int val; 723 switch (abc) 724 { 725 case 0: val = a; 726 break; 727 case 1: val = b; 728 break; 729 case 2: val = c; 730 break; 731 } 732 return val; 733} 734 735 736 737void set_time() 738{ 739 int t_hour = hour(), 740 t_min = minute(), 741 t_sec = 0; 742 743 t_hour = setdigits(t_hour, t_min, t_sec, 0, 0, 23, ':'); 744 t_min = setdigits(t_hour, t_min, t_sec, 1, 0, 59, ':'); 745 746 setTime(t_hour, t_min, 0, day(), month(), year()); 747 RTC.set(now()); 748 delay(1000); 749} 750 751 752void set_date() 753{ 754 int d_day = day(), 755 d_mon = month(), 756 d_yea = year()%100; 757 758 d_day = setdigits(d_day, d_mon, d_yea, 0, 0, 30, '.'); 759 d_mon = setdigits(d_day, d_mon, d_yea, 1, 1, 12, '.'); 760 d_yea = setdigits(d_day, d_mon, d_yea, 2, 0, 40, '.'); 761 762 setTime(hour(), minute(), second(), d_day, d_mon, d_yea+2000); 763 //RTC.set(now()); 764} 765 766 767 768void clear_data(bool eeprom) 769{ 770 display_menu(F("-> wird geloescht...")); 771 for (int i = 0; i < 96; i++) 772 { 773 data[i] = y_min_abs; 774 if (eeprom) EEPROM.put(i*2, int(0)); 775 776 } 777 delay(1000); 778} 779 780 781 782void calibrate_400ppm() 783{ 784 display_menu(F("-> wird kalibriert...")); 785 pinMode(calib_pin, OUTPUT); 786 digitalWrite(calib_pin, LOW); 787 delay(10*1000); 788 digitalWrite(calib_pin, HIGH); 789 pinMode(calib_pin, INPUT); 790} 791 792 793 794void menue() 795{ 796 display_menu(F("Messdaten ausgeben?")); 797 if (!select_item()) 798 { 799 replay(); 800 return; 801 } 802 803 804 display_menu(F("Display loeschen?")); 805 if (!select_item()) 806 { 807 clear_data(false); 808 return; 809 } 810 811 812 display_menu(F("Speicher loeschen?")); 813 if (!select_item()) 814 { 815 clear_data(true); 816 return; 817 } 818 819 820 display_menu(F("Uhrzeit stellen?")); 821 if (!select_item()) 822 { 823 set_time(); 824 return; 825 } 826 827 828 display_menu(F("Datum stellen?")); 829 if (!select_item()) 830 { 831 set_date(); 832 return; 833 } 834 835 836 display_menu(F("Sensor 400ppm kalibrieren?")); 837 if (!select_item()) 838 { 839 calibrate_400ppm(); 840 return; 841 } 842} 843 844 845 846 847/******************************************************************************************************* 848 * 849 * 850 * ===================== SETUP ===================== 851 * 852 * 853 *******************************************************************************************************/ 854 855 856 857 858void setup() 859{ 860 // 861 // Startbildschirm: Logo 862 // 863 864 u8g.firstPage(); 865 do 866 { 867 u8g.drawXBMP(32, 0, logo_width, logo_height, logo_bmp); 868 } 869 while (u8g.nextPage()); 870 delay(2000); 871 872 873 // 874 // definiere i/o-Pins 875 // 876 877 pinMode(calib_pin, INPUT); 878 pinMode(set_pin, INPUT_PULLUP); 879 pinMode(menue_pin, INPUT_PULLUP); 880 pinMode(record_pin, INPUT_PULLUP); 881 882 pinMode(menue_led, OUTPUT); 883 pinMode(set_led, OUTPUT); 884 885 pinMode(pwm_pin, OUTPUT); 886 analogWrite(pwm_pin, 0); 887 888 889 890 // 891 // serielle Ausgabe 892 // 893 894 Serial.begin(9600); 895 896 897 898 // 899 // RTC 900 // 901 902 setSyncProvider(RTC.get); // the function to get the time from the RTC 903 904 905 906 // 907 // Messwertspeicher aus dem EEPROM seriell ausgeben und ins RAM bertragen 908 // 909 910 for (int i = 0; i < 96; i++) 911 { 912 EEPROM.get(2*i, data[i]); // bertrage EEPROM-Daten in den Arbeitsspeicher 913 } 914 915 serial_print_headline(); 916 for (int i = 0; i < 5; i++) 917 { 918 pwm_data[i] = y_min_abs; 919 } 920 921 attachInterrupt(digitalPinToInterrupt(interrupt_pin), detect_change, CHANGE); 922 923 u8g.setFont(u8g_font_helvR08); // Schriftart whlen 924 u8g.firstPage(); 925 do 926 { 927 u8g.drawStr(10, 25, F("CO2-Logger V 1.0 V/'21")); 928 u8g.drawStr(10, 45, F("Warte auf Messdaten...")); 929 } 930 while (u8g.nextPage()); 931 932 933 for (int i = 0; i < 7; i++) // berpringe die ersten 7 Messwerte, um konstante Werte zu erhalten 934 { 935 while (!new_value) 936 { 937 delay(100); 938 } 939 new_value = false; 940 } 941} 942 943 944 945/******************************************************************************************************* 946 * 947 * 948 * ===================== LOOP ===================== 949 * 950 * 951 *******************************************************************************************************/ 952 953 954 955 956void loop() 957{ 958 if (new_value) 959 { 960 // 961 // hole neuen Messwert ab und schreibe ihn auf den PWM-Ausgang 962 // 963 964 new_value = false; 965 current_ppm = ppmCO2(high_duration, low_duration); 966 write_ppm_to_pwm(current_ppm); 967 968 969 // 970 // serielle Ausgabe des Messwerts 971 // 972 973 print_two_digits(hour()); 974 print_ddot(); 975 print_two_digits(minute()); 976 print_ddot(); 977 print_two_digits(second()); 978 Serial.print(F("; ")); 979 Serial.println(current_ppm); 980 981 982 // 983 // zeige auf dem Display den aktuellen Wert und den Graph fr die letzten 24 Stunden an 984 // 985 986 u8g.firstPage(); 987 do 988 { 989 draw_time(); 990 draw_ppm(current_ppm); 991 draw_graph(); 992 } 993 while (u8g.nextPage()); 994 995 996 // 997 // halte jede Minute einen Wert fest 998 // 999 1000 if (minute() != this_minute) 1001 { 1002 ppm_collect += current_ppm; 1003 ppm_count++; 1004 this_minute = minute(); 1005 1006 if ((this_minute%15) == 0) 1007 { 1008 ppm_collect /= ppm_count; 1009 if (ppm_collect < y_min_abs) ppm_collect = y_min_abs; 1010 else if (ppm_collect > 2000) ppm_collect = 2000; 1011 1012 int data_pointer = hour()*4 + (minute()/15); 1013 data[data_pointer] = ppm_collect; 1014 1015 EEPROM.put(data_pointer*2, ppm_collect); 1016 if (ppm_collect > 2554) ppm_collect = 2554; 1017 1018 1019 // 1020 // Aufzeichnung von Messwerten vorbereiten 1021 // 1022 1023 if ((digitalRead(record_pin) == LOW)&&(!recording)) // Aufzeichnungsschalter ist gerade umgelegt worden: 1024 { 1025 recording = true; // neuen Aufzeichnungsstatus merken und 1026 begin_protokoll(); // Aufzeichnungsbeginn speichern und Pointer auf Anfang setzen (= alle alten Messwerte damit lschen) 1027 } 1028 else if (digitalRead(record_pin) == HIGH) 1029 { 1030 recording = false; 1031 } 1032 1033 if (recording) record(byte((ppm_collect+5)/10)); // Schreibe Daten in den groen Datenspeicher, wenn seit Anfang an der Record-Schalter kontinuierlich bettigt war 1034 1035 ppm_collect = 0; 1036 ppm_count = 0; 1037 1038 for (int i = 0; i < 4; i++) // leere Datenspeicher hinter den aktuellen Messwerten, um eine 1039 { // graphische Lcke zu erzeugen 1040 data_pointer++; 1041 if (data_pointer > 24*4-1) data_pointer = 0; 1042 data[data_pointer] = 0; 1043 } 1044 } 1045 } 1046 } 1047 1048 if (digitalRead(menue_pin) == LOW) 1049 { 1050 digitalWrite(menue_led, HIGH); 1051 menue(); 1052 digitalWrite(menue_led, LOW); 1053 } 1054 1055 delay(100); 1056} 1057
CO2 monitor software for arduino nano
c_cpp
1/******************************************************************************************************* 2 3 * 4 * 5 * Einbinden von Libraries 6 * 7 * 8 *******************************************************************************************************/ 9 10 11// 12// 13 EEPROM 14// 15 16#include <EEPROM.h> 17 18 19// 20// Time 21// 22 23#include 24 "TimeLib.h" 25#include <DS1307RTC.h> 26 27 28// 29// Display 30// 31 32/* 33PINOUT: 34MODUL 35 Arduino pin 36============================================ 37BLK 38 GND 39BLA +3.3V (LED backlight) 40PSB (SPI) 41 GND (-> Serielle Daten erwarten) 42E (SCK) D13 43R/W 44 (MOSI) D11 45RS (CS) D10 46VCC +5V 47GND 48 GND 49*/ 50 51 52#include "U8glib.h" 53#define CS_PIN 54 10 55U8GLIB_ST7920_128X64_1X u8g(CS_PIN); 56 57define logo_width 64 58#define 59 logo_height 64 60 61static const unsigned char PROGMEM logo_bmp[] = { 620x0, 63 0x0, 0x0, 0xE0, 0x7, 0x0, 0x0, 0x0, 640x0, 0x0, 0x0, 0x10, 65 0x8, 0x0, 0x0, 0x0, 660x0, 0x0, 0x0, 0x10, 0x8, 0x0, 0x0, 67 0x0, 680x0, 0x0, 0x1C, 0x10, 0x8, 0x38, 0x0, 0x0, 690x0, 0x0, 70 0x13, 0x10, 0x8, 0xC8, 0x0, 0x0, 710x0, 0xC0, 0x20, 0x10, 0x8, 72 0x8, 0x3, 0x0, 730x0, 0x20, 0x20, 0x10, 0x8, 0x4, 0x4, 0x0, 74 750x0, 0x20, 0x20, 0x1E, 0x78, 0x4, 0x4, 0x0, 760x0, 0x60, 0xC0, 77 0x1, 0x80, 0x3, 0x6, 0x0, 780x0, 0x40, 0x0, 0x1C, 0x0, 0x0, 79 0x2, 0x0, 800x0, 0x80, 0x0, 0x1E, 0x0, 0x0, 0x1, 0x0, 810x0, 82 0x0, 0x1, 0x3E, 0x0, 0x80, 0x0, 0x0, 830x0, 0x0, 0x1, 0x3F, 84 0x0, 0x80, 0x0, 0x0, 850xC0, 0x81, 0x0, 0x27, 0x0, 0x0, 0x81, 86 0x3, 870x20, 0x43, 0x0, 0x23, 0x0, 0x0, 0xC2, 0x4, 880x20, 0x24, 89 0x0, 0x63, 0x0, 0x0, 0x24, 0x4, 900x10, 0x18, 0x0, 0x21, 0x0, 91 0x0, 0x18, 0x8, 920x10, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xF, 0x8, 93 940x8, 0x0, 0x0, 0x31, 0x0, 0x0, 0x0, 0x10, 950x8, 0x0, 0x0, 96 0x31, 0x0, 0x0, 0x0, 0x10, 970x18, 0x0, 0x0, 0x39, 0x0, 0x0, 98 0x0, 0x18, 990xE0, 0x0, 0x0, 0x9D, 0xFF, 0x7F, 0x0, 0x7, 1000x0, 101 0x1, 0x0, 0x9E, 0x0, 0x40, 0x80, 0x0, 1020x0, 0x1, 0x0, 0x9F, 103 0x0, 0x40, 0x80, 0x0, 1040x0, 0xF1, 0xFF, 0xFF, 0x0, 0xC0, 0x8F, 105 0x0, 1060x80, 0x0, 0xC0, 0x87, 0x0, 0x40, 0x0, 0x1, 1070x80, 0x0, 108 0xE0, 0x83, 0xFF, 0x7F, 0x0, 0x1, 1090x80, 0x0, 0xF0, 0x7, 0x0, 110 0x0, 0x0, 0x1, 1110xFE, 0x0, 0xF0, 0x4, 0x0, 0x0, 0x0, 0x7F, 112 1130x1, 0x0, 0x78, 0x4, 0x0, 0x0, 0x0, 0x80, 1140x1, 0xE0, 0xFF, 115 0xFF, 0xFF, 0xFF, 0xF, 0x80, 1160x1, 0x0, 0x1C, 0x7E, 0x0, 0x0, 117 0x0, 0x80, 1180x1, 0x0, 0x1C, 0xFF, 0x20, 0x1, 0x0, 0x80, 1190x1, 120 0x0, 0x8C, 0xFF, 0x21, 0x1, 0x0, 0x80, 1210x1, 0x0, 0x8C, 0xCB, 122 0x21, 0x1, 0x0, 0x80, 1230xFE, 0x0, 0x8C, 0x89, 0x23, 0x1, 0x0, 124 0x7F, 1250x80, 0x0, 0x8C, 0x8, 0x23, 0x1, 0x0, 0x1, 1260x80, 0xF0, 127 0xFF, 0xFF, 0x3F, 0xFF, 0xF, 0x1, 1280x80, 0x0, 0x88, 0x10, 0x23, 129 0x1, 0x0, 0x1, 1300x0, 0x1, 0x8, 0x11, 0x23, 0x1, 0x80, 0x0, 131 1320x0, 0x1, 0x10, 0x12, 0x21, 0x1, 0x80, 0x0, 1330x0, 0x1, 0x20, 134 0x90, 0x20, 0x1, 0x80, 0x0, 1350xE0, 0x0, 0xC0, 0x70, 0x20, 0x1, 136 0x0, 0x7, 1370x18, 0xE0, 0xBF, 0xBF, 0xFF, 0xFF, 0xF, 0x18, 1380x8, 139 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 1400x8, 0x0, 0x0, 0x20, 141 0x0, 0x0, 0x0, 0x10, 1420x10, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 143 0x8, 1440x10, 0x18, 0x80, 0x41, 0x0, 0x0, 0x18, 0x8, 1450x20, 146 0x24, 0xC0, 0x43, 0x0, 0x0, 0x24, 0x4, 1470x20, 0x43, 0xE0, 0x43, 148 0x0, 0x0, 0xC2, 0x4, 1490xC0, 0x81, 0xE0, 0x47, 0x0, 0x0, 0x81, 150 0x3, 1510x0, 0x0, 0xE1, 0x63, 0x0, 0x80, 0x0, 0x0, 1520x0, 0x0, 153 0xC1, 0x21, 0x0, 0x80, 0x0, 0x0, 1540x0, 0x80, 0x80, 0x1D, 0x0, 155 0x0, 0x1, 0x0, 1560x0, 0x40, 0x0, 0x6, 0x0, 0x0, 0x2, 0x0, 157 1580x0, 0x60, 0xC0, 0x1, 0x80, 0x3, 0x6, 0x0, 1590x0, 0x20, 0x20, 160 0x1E, 0x78, 0x4, 0x4, 0x0, 1610x0, 0x20, 0x20, 0x10, 0x8, 0x4, 162 0x4, 0x0, 1630x0, 0xC0, 0x20, 0x10, 0x8, 0x8, 0x3, 0x0, 1640x0, 165 0x0, 0x13, 0x10, 0x8, 0xC8, 0x0, 0x0, 1660x0, 0x0, 0x1C, 0x10, 167 0x8, 0x38, 0x0, 0x0, 1680x0, 0x0, 0x0, 0x10, 0x8, 0x0, 0x0, 169 0x0, 1700x0, 0x0, 0x0, 0x10, 0x8, 0x0, 0x0, 0x0, 1710x0, 0x0, 172 0x0, 0xE0, 0x7, 0x0, 0x0, 0x0 173}; 174 175 176 177 178 179/******************************************************************************************************* 180 181 * 182 * 183 * globale Deklarationen 184 * 185 * 186 *******************************************************************************************************/ 187 188 189const 190 int interrupt_pin = 3, 191 pwm_pin = 192 5, 193 calib_pin = 6, 194 set_pin 195 = 7, 196 menue_pin = 8, 197 record_pin 198 = 9, 199 200 set_led = 201 14, // A0 202 menue_led = 15, // A1 203 204 205 206 day_cell = 4 * 24 * 2, 207 208 mon_cell = day_cell +1, 209 year_cell 210 = mon_cell +1, 211 hour_cell = year_cell +1, 212 213 minu_cell = hour_cell +1, 214 start_cell 215 = minu_cell +1, 216 217 y_min_abs 218 = 300; 219 220int cell_pointer 221 = -1, 222 data[98], 223 224 pwm_data[5], 225 226 pwm_pointer = 0, 227 228 this_minute 229 = -1, 230 current_ppm = y_min_abs; 231 232bool recording 233 = false; 234 235unsigned long ppm_collect 236 = 0, 237 ppm_count = 0; 238 239 240 241volatile 242 bool new_value = false; 243volatile unsigned long millis_trigger_goes_high 244 = 0, 245 millis_trigger_goes_low = 0, 246 high_duration, 247 248 low_duration; 249 250 251 252/******************************************************************************************************* 253 254 * 255 * 256 * Serielle Ausgabe und PWM-Ausgabe 257 * 258 * 259 *******************************************************************************************************/ 260 261 262 263 264void 265 print_ddot() 266{ 267 Serial.print(F(":")); 268} 269 270 271void print_two_digits(int 272 val) 273{ 274 if (val < 10) Serial.print(F("0")); 275 Serial.print(val); 276} 277 278 279void 280 serial_print_headline() 281{ 282 Serial.println(F("\ 283\ 284aktuelle Messwerte\ 285==================")); 286} 287 288 289// 290// 291 analoges Ausgangssignal fr PWM 292// 293 294void write_ppm_to_pwm(int ppm) 295{ 296 297 float result = float(ppm) / 2500.0 * 255.0; // 2500 ppm sind 100% PWM-Signal, 298 also = 255 299 if (result > 255.0) result = 255.0; 300 else if (result < 0.0) 301 result = 0.0; 302 303 pwm_data[pwm_pointer] = int(result); 304 pwm_pointer++; 305 306 if (pwm_pointer > 5) pwm_pointer = 0; 307 308 int mean = 0; 309 for (int i = 310 0; i < 5; i++) 311 { 312 mean += pwm_data[i]; 313 } 314 mean /= 5; 315 316 317 analogWrite(pwm_pin, mean); 318} 319 320 321 322 323 324/******************************************************************************************************* 325 326 * 327 * 328 * ===================== Messroutinen ===================== 329 * 330 331 * 332 *******************************************************************************************************/ 333 334 335 336 337void 338 detect_change() { 339 if (digitalRead(interrupt_pin) == HIGH) 340 { 341 millis_trigger_goes_high 342 = millis(); 343 low_duration = millis_trigger_goes_high - millis_trigger_goes_low; 344 345 } 346 else 347 { 348 millis_trigger_goes_low = millis(); 349 350 high_duration = millis_trigger_goes_low - millis_trigger_goes_high; 351 352 new_value = true; 353 } 354} 355 356 357 358 359int ppmCO2(int high, 360 361 int low) 362{ 363 int duration = (high -2); 364 duration 365 += (1002 - low); 366 int result = duration*2; 367 368 return 369 result; 370} 371 372 373 374 375/******************************************************************************************************* 376 377 * 378 * 379 * L C D - Funktionen 380 * 381 * 382 *******************************************************************************************************/ 383 384 385 386 387// 388 389// schreibe eine Integerzahl mit 4 Ziffern rechtsbndig 390// 391void draw_int(int 392 xx_offset, int yy_offset, int the_int) 393{ 394 String theString = ""; 395 396 397 int x = the_int; 398 if (x == 0) x = 1; 399 while(x < 1000) 400 { 401 theString 402 += " "; 403 x *= 10; 404 } 405 theString += String(the_int); 406 char thisline_c[8]; 407 408 theString.toCharArray(thisline_c, 8); 409 410 u8g.drawStr(xx_offset, 411 yy_offset, thisline_c); 412} 413 414 415// 416// schreibe Zeit und Datum ins Display 417// 418void 419 draw_time() 420{ 421 String theString = ""; // Zeitstring 422 erstellen 423 char thisline_c[8]; 424 425 if (hour() < 10) theString = F("0"); 426 427 theString += String(hour()); 428 if (second()%2==0) theString += F(":"); 429 430 else theString += F(" "); 431 432 if (minute() < 10) theString 433 += F("0"); 434 theString += String(minute()); 435 436 theString.toCharArray(thisline_c, 437 7); // Zeitstring ausgeben 438 u8g.setFont(u8g_font_5x8); // Schriftart 439 ndern 440 u8g.drawStr(0, 7, thisline_c); 441 442 443 if (day() < 10) theString 444 = F("0"); // Datumsstring erstellen 445 else theString 446 = ""; 447 448 theString += String(day()) 449 + F(" "); 450 451 452 if (month() < 10) theString += F("0"); // Datumsstring ausgeben 453 454 theString += String(month()); 455 456 theString.toCharArray(thisline_c, 7); 457 458 459 u8g.setFont(u8g_font_5x8); // Schriftart ndern 460 u8g.drawStr(0, 15, thisline_c); 461 462 463 u8g.drawLine(11, 13, 12, 13); // Datumspunkt setzen 464 u8g.drawLine(11, 465 14, 12, 14); 466 467 if ((second()%2==0)&&(digitalRead(record_pin)==LOW)) 468 { 469 470 u8g.drawStr(124, 4, F("r")); 471 } 472} 473 474 475// 476// Schreibe gro 477 ppm-Wert ins Display 478// 479void draw_ppm(int ppm) 480{ 481 ppm += 5; // 482 ppm-Werte auf 10er-Stelle runden 483 ppm /= 10; 484 ppm *= 10; 485 486 u8g.setFont(u8g_font_courB18); 487 // Schriftart ndern 488 draw_int(25, 16, ppm); 489 490 u8g.setFont(u8g_font_helvR08); 491 // Schriftart ndern 492 u8g.drawStr(81, 16, F(" ppm CO")); 493 u8g.setFont(u8g_font_5x8); 494 // Schriftart ndern 495 u8g.drawStr(123, 20, F("2")); 496} 497 498 499// 500// 501 Zeichne Diagramm 502// 503void draw_graph() 504{ 505 const int y_ddelta = 200, 506 507 y_res = 63, 508 y_offset = 3, 509 y_width 510 = 40, 511 512 x_res = 127, 513 x_offset 514 = 27; 515 516 int y_range, 517 y_delta; 518 519 520 521 522 523 // 524 // zeichne x-Achse 525 // 526 527 u8g.drawLine(x_offset, y_res-3, 528 x_res, y_res-3); 529 for (int i = 1; i < 25; i++) 530 { 531 if (i%6==0) u8g.drawLine(x_offset+i*4, 532 y_res-3,x_offset+i*4, y_res); 533 else u8g.drawLine(x_offset+i*4, y_res-3,x_offset+i*4, 534 y_res-1); 535 } 536 537 538 // 539 // beschrifte x-Achse 540 // 541 542 543 u8g.setFont(u8g_font_5x8); // Schriftart ndern 544 for (int i = 0; i < 5; i++) 545 546 { 547 if (i == 1) draw_int(x_offset-16 + 24*i, y_res-4, i*6); // zeichne 548 die $6$ um 2 Pixel nach links versetzt 549 else draw_int(x_offset-14 + 550 24*i, y_res-4, i*6); 551 } 552 553 554 // 555 // zeichne y-Achse 556 // 557 558 559 u8g.drawLine(x_offset, y_res, x_offset, y_res-y_width-y_offset); 560 for 561 (int i = 0; i < 5; i++) 562 { 563 u8g.drawLine(x_offset, y_res-i*10-y_offset, 564 x_offset-3, y_res-i*10-y_offset); 565 } 566 567 568 // 569 // ermittle y_delta 570 571 // 572 573 y_range = y_min_abs + y_ddelta; 574 for (int i = 0; i < 96; i++) 575 576 { 577 while (data[i] > y_range) 578 { 579 y_range+=y_ddelta; 580 } 581 582 } 583 584 while (current_ppm > y_range) y_range += y_ddelta; 585 586 y_delta 587 = (y_range-y_min_abs)/4; 588 589 590 // 591 // beschrifte y-Achse 592 // 593 594 595 for (int i = 0; i < 5; i++) 596 { 597 draw_int(0, y_res-i*10, y_min_abs+i*y_delta); 598 599 } 600 601 602 // 603 // zeichne Messwert 604 // 605 606 if (second()%2==0) 607 608 { 609 int val = y_min_abs; // 610 stelle sicher, dass ein Wert >= 300 ppm vorliegt 611 if (current_ppm > y_min_abs) 612 val = current_ppm; 613 // 614 zeichne Linie 615 int x = x_offset + hour()*4 + (minute()/15 +1); 616 u8g.drawLine(x, 617 y_res - y_offset, 618 x, y_res - y_offset - int(float((val-y_min_abs))/float((y_range-y_min_abs))*float(y_width))); 619 620 } 621 622 623 // 624 // zeichne Graph 625 // 626 627 for (int i = 0; i 628 < 94; i++) 629 { 630 int j = i+1; 631 632 if ((data[i] >= y_min_abs)&&(data[j] 633 >= y_min_abs)) // Der Graph wird nur gezeichnet, wenn Anfangs- 634 und Endpunkt definiert sind, also 635 { // 636 einen Messwert >= 300 ppm aufweisen. 637 int y0 = y_res - y_offset - int(float((data[i]-y_min_abs))/float((y_range-y_min_abs))*float(y_width)), 638 639 y1 = y_res - y_offset - int(float((data[j]-y_min_abs))/float((y_range-y_min_abs))*float(y_width)); 640 641 642 u8g.drawLine(i+x_offset, y0, 643 i+x_offset+1, 644 y1); 645 } 646 } 647} 648 649 650/******************************************************************************************************* 651 652 * 653 * 654 * ===================== EEPROM-Protokoll ===================== 655 656 * 657 * 658 *******************************************************************************************************/ 659 660 661 662 663 664void begin_protokoll() 665{ 666 EEPROM.put(day_cell, byte(day())); 667 668 EEPROM.put(mon_cell, byte(month())); 669 EEPROM.put(year_cell, byte(year()%2000)); 670 671 EEPROM.put(hour_cell, byte(hour())); 672 EEPROM.put(minu_cell, byte(minute())); 673 674 cell_pointer = start_cell; 675} 676 677 678void record(byte 679 val) 680{ 681 if (cell_pointer < 1024) // Wenn noch Speicher 682 frei ist 683 { 684 EEPROM.put(cell_pointer, val); // Daten speichern 685 686 cell_pointer++; // Zeiger auf nchstes Element 687 setzen und 688 if (cell_pointer < 1024) // falls dies vergbar 689 ist: 690 { 691 EEPROM.put(cell_pointer, 0); // setze dieses 692 auf "0", um Ende der Aufzeichnung zu markieren. 693 } 694 } 695} 696 697 698void 699 print_two_digits_eeprom(int cell) 700{ 701 byte val; 702 EEPROM.get(cell, val); 703 704 print_two_digits(val); 705} 706 707 708 709void replay() 710{ 711 Serial.println(F("\ 712hh:mm:ss; 713 CO2/ppm")); 714 for (int i = 0; i < 96; i++) 715 { 716 print_two_digits(i/4); 717 // gib Daten des Displayspeichers auf die serielle Schnittstelle 718 719 print_ddot(); 720 print_two_digits((i-(i/4)*4)*15); 721 Serial.print(F(":00;")); 722 723 Serial.println(data[i]); 724 } 725 726 byte val; 727 728 Serial.println(F("\ 729dd.mm.yy 730 hh:mm:ss; CO2/ppm")); 731 print_two_digits_eeprom(day_cell); 732 Serial.print(F(".")); 733 734 print_two_digits_eeprom(mon_cell); 735 Serial.print(F(".")); 736 print_two_digits_eeprom(year_cell); 737 738 Serial.print(F(" ")); 739 print_two_digits_eeprom(hour_cell); 740 print_ddot(); 741 742 print_two_digits_eeprom(minu_cell); 743 Serial.print(F(":00")); 744 745 for 746 (int cell = start_cell; cell < 1024; cell++) 747 { 748 EEPROM.get(cell, val); 749 750 if (val == 0) break; 751 Serial.print(";"); 752 Serial.println(int(val)*10); 753 754 } 755 756 serial_print_headline(); 757} 758 759 760/******************************************************************************************************* 761 762 * 763 * 764 * ===================== Zeit und Datum setzen ===================== 765 766 * 767 * 768 *******************************************************************************************************/ 769 770 771 772 773void 774 wait_for_high() 775{ 776 int i = 100; 777 do 778 { 779 i--; 780 if ((digitalRead(menue_pin)==LOW)||(digitalRead(set_pin)==LOW)) 781 i = 100; 782 delay(1); 783 } 784 while ( i > 0); 785} 786 787 788bool select_item() 789{ 790 791 wait_for_high(); // stelle 792 sicher, dass kein KNopf gedrckt 793 while((digitalRead(menue_pin)==HIGH)&&(digitalRead(set_pin)==HIGH)) 794 // warte, solange nichts gedrckt 795 { 796 delay(1); 797 } 798 799 bool 800 menue_pressed = (digitalRead(menue_pin)==LOW); // merke, welcher 801 Knopf gedrckt 802 wait_for_high(); // 803 warte bis alle Knpfe losgelassen 804 return menue_pressed; // 805 gib gedrckten Knopf zurck 806} 807 808 809 810void display_menu(String str) 811{ 812 813 u8g.firstPage(); 814 815 u8g.setFont(u8g_font_helvR08); 816 u8g.drawStr(0, 817 8, F("Menue")); 818 819 do 820 { 821 u8g.drawStr(0, 16, F("======")); 822 823 824 char thisline_c[32]; 825 str.toCharArray(thisline_c, 30); 826 u8g.drawStr(0, 827 34, thisline_c); 828 } 829 while (u8g.nextPage()); 830} 831 832 833void lcd_Time_Date(int 834 y, int h, int m, int s, char separate, int blankpos) 835{ 836 String theString 837 = ""; // Zeitstring erstellen 838 char thisline_c[8]; 839 840 841 if (h < 10) theString = F("0"); 842 theString += String(h) 843 + 844 separate; 845 846 if (m < 10) theString += F("0"); 847 theString += String(m) 848 849 + separate; 850 851 if (s < 10) theString += F("0"); 852 theString 853 += String(s); 854 855 theString.toCharArray(thisline_c, 10); // Zeitstring 856 in Array umwandeln 857 if (blankpos > -1) // Zeichen 858 lschen fr Blinkeffekt 859 { 860 thisline_c[blankpos] = '_'; 861 thisline_c[blankpos+1] 862 = '_'; 863 digitalWrite(set_led, HIGH); 864 } 865 else 866 { 867 digitalWrite(set_led, 868 LOW); 869 } 870 871 do 872 { 873 u8g.drawStr(y, 40, thisline_c); 874 } 875 876 while (u8g.nextPage()); 877} 878 879 880 881int setdigits(int a, int b, int c, 882 int abc, int minval, int maxval, char sep) 883{ 884 int digitwait = 0, 885 blankblink 886 = -1; 887 bool set_button, menue_button; 888 889 do 890 { 891 do 892 { 893 894 if (second()%2==0) blankblink = -1; 895 else blankblink 896 = abc*3; 897 u8g.firstPage(); 898 lcd_Time_Date(30, a, b, c, sep, blankblink); 899 900 set_button = digitalRead(set_pin) == LOW; 901 menue_button = digitalRead(menue_pin) 902 == LOW; 903 }while(!set_button && !menue_button); 904 905 if (set_button) 906 907 { 908 switch (abc) 909 { 910 case 0: a++; 911 if 912 (a > maxval) a = minval; 913 break; 914 case 1: b++; 915 if 916 (b > maxval) b = minval; 917 break; 918 case 2: c++; 919 if 920 (c > maxval) c = minval; 921 break; 922 } 923 924 if (digitwait 925 < 5) 926 { 927 delay(666); 928 digitwait++; 929 } 930 else{ 931 932 delay(222); 933 } 934 } 935 else 936 { 937 wait_for_high(); 938 939 } 940 941 }while(!menue_button); 942 943 digitalWrite(set_led, LOW); 944 945 946 int val; 947 switch (abc) 948 { 949 case 0: val = a; 950 break; 951 952 case 1: val = b; 953 break; 954 case 2: val = c; 955 break; 956 957 } 958 return val; 959} 960 961 962 963void set_time() 964{ 965 int t_hour = 966 hour(), 967 t_min = minute(), 968 t_sec = 0; 969 970 t_hour = setdigits(t_hour, 971 t_min, t_sec, 0, 0, 23, ':'); 972 t_min = setdigits(t_hour, t_min, t_sec, 1, 0, 973 59, ':'); 974 975 setTime(t_hour, t_min, 0, day(), month(), year()); 976 RTC.set(now()); 977 978 delay(1000); 979} 980 981 982void set_date() 983{ 984 int d_day = day(), 985 986 d_mon = month(), 987 d_yea = year()%100; 988 989 d_day = setdigits(d_day, 990 d_mon, d_yea, 0, 0, 30, '.'); 991 d_mon = setdigits(d_day, d_mon, d_yea, 1, 1, 992 12, '.'); 993 d_yea = setdigits(d_day, d_mon, d_yea, 2, 0, 40, '.'); 994 995 setTime(hour(), 996 minute(), second(), d_day, d_mon, d_yea+2000); 997 //RTC.set(now()); 998} 999 1000 1001 1002void 1003 clear_data(bool eeprom) 1004{ 1005 display_menu(F("-> wird geloescht...")); 1006 1007 for (int i = 0; i < 96; i++) 1008 { 1009 data[i] = y_min_abs; 1010 if (eeprom) 1011 EEPROM.put(i*2, int(0)); 1012 1013 } 1014 delay(1000); 1015} 1016 1017 1018 1019void calibrate_400ppm() 1020{ 1021 1022 display_menu(F("-> wird kalibriert...")); 1023 pinMode(calib_pin, OUTPUT); 1024 1025 digitalWrite(calib_pin, LOW); 1026 delay(10*1000); 1027 digitalWrite(calib_pin, 1028 HIGH); 1029 pinMode(calib_pin, INPUT); 1030} 1031 1032 1033 1034void menue() 1035{ 1036 1037 display_menu(F("Messdaten ausgeben?")); 1038 if (!select_item()) 1039 { 1040 1041 replay(); 1042 return; 1043 } 1044 1045 1046 display_menu(F("Display loeschen?")); 1047 1048 if (!select_item()) 1049 { 1050 clear_data(false); 1051 return; 1052 } 1053 1054 1055 1056 display_menu(F("Speicher loeschen?")); 1057 if (!select_item()) 1058 { 1059 clear_data(true); 1060 1061 return; 1062 } 1063 1064 1065 display_menu(F("Uhrzeit stellen?")); 1066 if 1067 (!select_item()) 1068 { 1069 set_time(); 1070 return; 1071 } 1072 1073 1074 display_menu(F("Datum 1075 stellen?")); 1076 if (!select_item()) 1077 { 1078 set_date(); 1079 return; 1080 1081 } 1082 1083 1084 display_menu(F("Sensor 400ppm kalibrieren?")); 1085 if (!select_item()) 1086 1087 { 1088 calibrate_400ppm(); 1089 return; 1090 } 1091} 1092 1093 1094 1095 1096/******************************************************************************************************* 1097 1098 * 1099 * 1100 * ===================== SETUP ===================== 1101 * 1102 * 1103 1104 *******************************************************************************************************/ 1105 1106 1107 1108 1109void 1110 setup() 1111{ 1112 // 1113 // Startbildschirm: Logo 1114 // 1115 1116 u8g.firstPage(); 1117 1118 do 1119 { 1120 u8g.drawXBMP(32, 0, logo_width, logo_height, logo_bmp); 1121 1122 } 1123 while (u8g.nextPage()); 1124 delay(2000); 1125 1126 1127 // 1128 // definiere 1129 i/o-Pins 1130 // 1131 1132 pinMode(calib_pin, INPUT); 1133 pinMode(set_pin, INPUT_PULLUP); 1134 1135 pinMode(menue_pin, INPUT_PULLUP); 1136 pinMode(record_pin, INPUT_PULLUP); 1137 1138 1139 pinMode(menue_led, OUTPUT); 1140 pinMode(set_led, OUTPUT); 1141 1142 pinMode(pwm_pin, 1143 OUTPUT); 1144 analogWrite(pwm_pin, 0); 1145 1146 1147 1148 // 1149 // serielle 1150 Ausgabe 1151 // 1152 1153 Serial.begin(9600); 1154 1155 1156 1157 // 1158 // RTC 1159 1160 // 1161 1162 setSyncProvider(RTC.get); // the function to get the time from 1163 the RTC 1164 1165 1166 1167 // 1168 // Messwertspeicher aus dem EEPROM seriell 1169 ausgeben und ins RAM bertragen 1170 // 1171 1172 for (int i = 0; i < 96; i++) 1173 1174 { 1175 EEPROM.get(2*i, data[i]); // bertrage 1176 EEPROM-Daten in den Arbeitsspeicher 1177 } 1178 1179 serial_print_headline(); 1180 1181 for (int i = 0; i < 5; i++) 1182 { 1183 pwm_data[i] = y_min_abs; 1184 } 1185 1186 1187 attachInterrupt(digitalPinToInterrupt(interrupt_pin), detect_change, CHANGE); 1188 1189 1190 u8g.setFont(u8g_font_helvR08); // Schriftart whlen 1191 u8g.firstPage(); 1192 1193 do 1194 { 1195 u8g.drawStr(10, 25, F("CO2-Logger V 1.0 V/'21")); 1196 u8g.drawStr(10, 1197 45, F("Warte auf Messdaten...")); 1198 } 1199 while (u8g.nextPage()); 1200 1201 1202 1203 for (int i = 0; i < 7; i++) // berpringe die ersten 7 1204 Messwerte, um konstante Werte zu erhalten 1205 { 1206 while (!new_value) 1207 { 1208 1209 delay(100); 1210 } 1211 new_value = false; 1212 } 1213} 1214 1215 1216 1217/******************************************************************************************************* 1218 1219 * 1220 * 1221 * ===================== LOOP ===================== 1222 * 1223 * 1224 1225 *******************************************************************************************************/ 1226 1227 1228 1229 1230void 1231 loop() 1232{ 1233 if (new_value) 1234 { 1235 // 1236 // hole neuen Messwert 1237 ab und schreibe ihn auf den PWM-Ausgang 1238 // 1239 1240 new_value = 1241 false; 1242 current_ppm = ppmCO2(high_duration, low_duration); 1243 write_ppm_to_pwm(current_ppm); 1244 1245 1246 1247 // 1248 // serielle Ausgabe des Messwerts 1249 // 1250 1251 1252 print_two_digits(hour()); 1253 print_ddot(); 1254 print_two_digits(minute()); 1255 1256 print_ddot(); 1257 print_two_digits(second()); 1258 Serial.print(F("; ")); 1259 1260 Serial.println(current_ppm); 1261 1262 1263 // 1264 // zeige auf dem 1265 Display den aktuellen Wert und den Graph fr die letzten 24 Stunden an 1266 // 1267 1268 1269 u8g.firstPage(); 1270 do 1271 { 1272 draw_time(); 1273 draw_ppm(current_ppm); 1274 1275 draw_graph(); 1276 } 1277 while (u8g.nextPage()); 1278 1279 1280 // 1281 1282 // halte jede Minute einen Wert fest 1283 // 1284 1285 if (minute() != 1286 this_minute) 1287 { 1288 ppm_collect += current_ppm; 1289 ppm_count++; 1290 1291 this_minute = minute(); 1292 1293 if ((this_minute%15) == 0) 1294 1295 { 1296 ppm_collect /= ppm_count; 1297 if (ppm_collect < y_min_abs) 1298 ppm_collect = y_min_abs; 1299 else if (ppm_collect > 2000) ppm_collect = 1300 2000; 1301 1302 int data_pointer = hour()*4 + (minute()/15); 1303 data[data_pointer] 1304 = ppm_collect; 1305 1306 EEPROM.put(data_pointer*2, ppm_collect); 1307 1308 if (ppm_collect > 2554) ppm_collect = 2554; 1309 1310 1311 // 1312 1313 // Aufzeichnung von Messwerten vorbereiten 1314 // 1315 1316 if 1317 ((digitalRead(record_pin) == LOW)&&(!recording)) // Aufzeichnungsschalter 1318 ist gerade umgelegt worden: 1319 { 1320 recording = true; 1321 // neuen Aufzeichnungsstatus merken und 1322 1323 begin_protokoll(); // Aufzeichnungsbeginn 1324 speichern und Pointer auf Anfang setzen (= alle alten Messwerte damit lschen) 1325 1326 } 1327 else if (digitalRead(record_pin) == HIGH) 1328 { 1329 1330 recording = false; 1331 } 1332 1333 if (recording) 1334 record(byte((ppm_collect+5)/10)); // Schreibe Daten in den groen Datenspeicher, 1335 wenn seit Anfang an der Record-Schalter kontinuierlich bettigt war 1336 1337 1338 ppm_collect = 0; 1339 ppm_count = 0; 1340 1341 for (int i = 1342 0; i < 4; i++) // leere Datenspeicher hinter den aktuellen 1343 Messwerten, um eine 1344 { // 1345 graphische Lcke zu erzeugen 1346 data_pointer++; 1347 if (data_pointer 1348 > 24*4-1) data_pointer = 0; 1349 data[data_pointer] = 0; 1350 } 1351 1352 } 1353 } 1354 } 1355 1356 if (digitalRead(menue_pin) == LOW) 1357 { 1358 1359 digitalWrite(menue_led, HIGH); 1360 menue(); 1361 digitalWrite(menue_led, 1362 LOW); 1363 } 1364 1365 delay(100); 1366} 1367
Downloadable files
Setup and Card layout
Setup and Card layout
Experimental breadboard setup
Experimental setup with all components
Experimental breadboard setup

Card layout for soldering
Card layout for soldering

Experimental breadboard setup
Experimental setup with all components
Experimental breadboard setup

Setup and Card layout
Setup and Card layout
Documentation
Case
Housing for the CO2 monitor when soldered on card
Case
Case
Housing for the CO2 monitor when soldered on card
Case
Comments
Only logged in users can leave comments