2020-10-31 16:06:36 +01:00
# include <Arduino.h>
# include "MHZ19.h"
# include "SSD1306Wire.h"
# include <Adafruit_NeoPixel.h>
2020-11-19 13:13:10 +01:00
# include "fonts-custom.h"
2020-11-23 22:50:05 +01:00
# include <Preferences.h>
2020-11-25 21:33:28 +01:00
# include "uptime_formatter.h"
2020-10-31 16:06:36 +01:00
// Maximum CO² levels for green and yellow, everything above is considered red.
# define GREEN_CO2 800
2020-11-12 19:46:23 +01:00
# define YELLOW_CO2 1000
2020-10-31 16:06:36 +01:00
// Measurement interval in miliseconds
2020-11-19 13:13:10 +01:00
# define INTERVAL 15000
2020-11-25 21:33:28 +01:00
# define CALINTERVAL 180000
2020-10-31 16:06:36 +01:00
// Pins for MH-Z19
# define RX_PIN 16
# define TX_PIN 17
// Pins for SD1306
# define SDA_PIN 21
# define SCL_PIN 22
// Pin for LED
# define LED_PIN 4
2020-11-12 19:46:23 +01:00
// number of LEDs connected
2020-11-24 01:19:10 +01:00
# define NUMPIXELS 12
2020-11-12 19:46:23 +01:00
2020-11-23 22:50:05 +01:00
Preferences preferences ;
2020-11-19 13:13:10 +01:00
2020-10-31 16:06:36 +01:00
MHZ19 myMHZ19 ;
HardwareSerial mySerial ( 1 ) ;
SSD1306Wire display ( 0x3c , SDA_PIN , SCL_PIN ) ;
2020-11-14 00:25:50 +01:00
Adafruit_NeoPixel pixels = Adafruit_NeoPixel ( NUMPIXELS , LED_PIN , NEO_RGB + NEO_KHZ800 ) ;
2020-10-31 16:06:36 +01:00
unsigned long getDataTimer = 0 ;
2020-11-25 21:33:28 +01:00
unsigned long getDataTimer1 = 0 ;
2020-11-25 23:22:48 +01:00
int countdown = 0 ;
2020-10-31 16:06:36 +01:00
int lastvals [ 120 ] ;
int dheight ;
2020-11-25 23:22:48 +01:00
String ampelversion = " 0.11 " ;
2020-11-23 22:50:05 +01:00
int safezone = 1 ;
int tocalibrateornot ;
2020-11-25 21:33:28 +01:00
int initloop = 1 ;
2020-11-24 01:19:10 +01:00
2020-11-25 21:33:28 +01:00
void switchBootMode ( int bm ) {
switch ( bm ) {
case 23 :
preferences . putUInt ( " cal " , 42 ) ;
Serial . println ( " startmodus naechster reboot: messmodus " ) ;
break ;
case 42 :
preferences . putUInt ( " cal " , 23 ) ;
Serial . println ( " startmodus naechster reboot: kalibrierungsmodus " ) ;
break ;
case 69 :
Serial . println ( " EEPROM lesen war nicht moeglich! " ) ;
break ;
default :
Serial . print ( " EEPROM lesen lieferte unerwarteten wert: " ) ;
Serial . println ( bm ) ;
break ;
}
}
2020-11-23 22:50:05 +01:00
2020-10-31 16:06:36 +01:00
void setup ( ) {
2020-11-25 21:33:28 +01:00
Serial . begin ( 115200 ) ;
2020-11-26 15:52:07 +01:00
Serial . println ( " Starte... " ) ;
Serial . print ( " CO2-Ampel Firmware: " ) ; Serial . println ( ampelversion ) ;
2020-11-23 22:50:05 +01:00
preferences . begin ( " co2 " , false ) ;
2020-11-23 23:50:36 +01:00
tocalibrateornot = preferences . getUInt ( " cal " , 69 ) ; // wir lesen unser flag ein, 23 = reboot vor safezone, wir wollen kalibrieren, 42 = reboot nach safezone, wir tun nichts
2020-11-23 22:50:05 +01:00
preferences . putUInt ( " cal " , 23 ) ; // wir sind gerade gestartet
2020-11-25 21:33:28 +01:00
2020-10-31 16:06:36 +01:00
display . init ( ) ;
2020-11-23 23:50:36 +01:00
display . setFont ( Cousine_Regular_54 ) ;
2020-10-31 16:06:36 +01:00
display . setContrast ( 255 ) ;
2020-11-23 22:50:05 +01:00
delay ( 500 ) ;
2020-10-31 16:06:36 +01:00
display . clear ( ) ;
2020-11-12 19:46:23 +01:00
display . flipScreenVertically ( ) ;
2020-11-23 22:50:05 +01:00
display . setTextAlignment ( TEXT_ALIGN_CENTER ) ;
display . drawString ( 64 , 0 , String ( ampelversion ) ) ;
display . display ( ) ;
2020-10-31 16:06:36 +01:00
dheight = display . getHeight ( ) ;
2020-11-25 21:33:28 +01:00
2020-11-26 15:52:07 +01:00
mySerial . begin ( 9600 , SERIAL_8N1 , RX_PIN , TX_PIN ) ;
myMHZ19 . begin ( mySerial ) ;
myMHZ19 . autoCalibration ( false ) ; // baseline calibration erstmal aus
2020-11-25 21:33:28 +01:00
char myVersion [ 4 ] ;
myMHZ19 . getVersion ( myVersion ) ;
2020-11-26 15:52:07 +01:00
Serial . print ( " \n MH-Z19b Firmware Version: " ) ;
Serial . print ( myVersion [ 0 ] ) ; Serial . print ( myVersion [ 1 ] ) ; ; Serial . print ( " . " ) ; Serial . print ( myVersion [ 2 ] ) ; Serial . println ( myVersion [ 3 ] ) ;
Serial . print ( " Range: " ) ; Serial . println ( myMHZ19 . getRange ( ) ) ;
Serial . print ( " Background CO2: " ) ; Serial . println ( myMHZ19 . getBackgroundCO2 ( ) ) ;
Serial . print ( " Temperature Cal: " ) ; Serial . println ( myMHZ19 . getTempAdjustment ( ) ) ;
2020-11-25 21:33:28 +01:00
Serial . print ( " ABC Status: " ) ; myMHZ19 . getABC ( ) ? Serial . println ( " ON " ) : Serial . println ( " OFF " ) ;
2020-11-26 15:52:07 +01:00
Serial . print ( " read EEPROM value: " ) ; Serial . println ( tocalibrateornot ) ;
2020-11-24 01:19:10 +01:00
2020-11-25 21:33:28 +01:00
switch ( tocalibrateornot ) {
case 23 :
Serial . println ( " startmodus aktuell: kalibrierungsmodus " ) ;
break ;
case 42 :
Serial . println ( " startmodus aktuell: messmodus " ) ;
break ;
}
2020-11-26 15:52:07 +01:00
// Pre-Fill array of last measurements with -1
2020-10-31 16:06:36 +01:00
for ( int x = 0 ; x < = 119 ; x = x + 1 ) {
lastvals [ x ] = - 1 ;
}
2020-11-12 19:46:23 +01:00
2020-10-31 16:06:36 +01:00
pixels . begin ( ) ;
2020-11-26 15:52:07 +01:00
pixels . clear ( ) ;
2020-11-12 19:46:23 +01:00
for ( int i = 0 ; i < NUMPIXELS ; i + + ) {
2020-11-14 00:25:50 +01:00
pixels . setPixelColor ( i , 0 , 0 , 50 ) ;
2020-11-12 19:46:23 +01:00
pixels . show ( ) ;
}
2020-11-25 21:33:28 +01:00
switchBootMode ( tocalibrateornot ) ; // beim naechsten boot im anderen modus starten
2020-10-31 16:06:36 +01:00
}
int calc_vpos_for_co2 ( int co2val , int display_height ) {
return display_height - int ( ( float ( display_height ) / 3000 ) * co2val ) ;
}
void set_led_color ( int co2 ) {
if ( co2 < GREEN_CO2 ) {
// Green
2020-11-12 19:46:23 +01:00
for ( int i = 0 ; i < NUMPIXELS ; i + + ) {
pixels . setPixelColor ( i , 30 , 0 , 0 ) ;
}
2020-10-31 16:06:36 +01:00
} else if ( co2 < YELLOW_CO2 ) {
// Yellow
2020-11-12 19:46:23 +01:00
for ( int i = 0 ; i < NUMPIXELS ; i + + ) {
pixels . setPixelColor ( i , 40 , 40 , 0 ) ;
}
2020-10-31 16:06:36 +01:00
} else {
// Red
2020-11-12 19:46:23 +01:00
for ( int i = 0 ; i < NUMPIXELS ; i + + ) {
pixels . setPixelColor ( i , 0 , 90 , 0 ) ;
}
2020-10-31 16:06:36 +01:00
}
pixels . show ( ) ;
}
2020-11-23 22:50:05 +01:00
2020-11-24 01:19:10 +01:00
void rainbow ( int wait ) {
for ( long firstPixelHue = 0 ; firstPixelHue < 65536 ; firstPixelHue + = 256 ) {
for ( int i = 0 ; i < NUMPIXELS ; i + + ) {
int pixelHue = firstPixelHue + ( i * 65536L / NUMPIXELS ) ;
pixels . setPixelColor ( i , pixels . gamma32 ( pixels . ColorHSV ( pixelHue ) ) ) ;
}
pixels . show ( ) ;
delay ( wait ) ;
}
}
2020-11-25 21:33:28 +01:00
void calibrateCO2 ( ) {
display . clear ( ) ;
display . drawString ( 64 , 0 , " CAL! " ) ;
display . display ( ) ;
Serial . println ( " brace yourself, calibration starting! things either be better or all fucked up beyond this point... " ) ;
myMHZ19 . setRange ( 5000 ) ;
delay ( 500 ) ;
myMHZ19 . calibrateZero ( ) ;
delay ( 500 ) ;
myMHZ19 . autoCalibration ( false ) ;
delay ( 500 ) ;
display . clear ( ) ;
display . drawString ( 64 , 0 , " DONE " ) ;
display . display ( ) ;
preferences . putUInt ( " cal " , 42 ) ;
display . clear ( ) ;
}
2020-11-24 01:19:10 +01:00
void readco2 ( ) {
2020-10-31 16:06:36 +01:00
if ( millis ( ) - getDataTimer > = INTERVAL ) {
// Get new CO² value.
int CO2 = myMHZ19 . getCO2 ( ) ;
// Shift entries in array back one position.
for ( int x = 1 ; x < = 119 ; x = x + 1 ) {
lastvals [ x - 1 ] = lastvals [ x ] ;
}
// Add new measurement at the end.
lastvals [ 119 ] = CO2 ;
// Clear display and redraw whole graph.
display . clear ( ) ;
for ( int h = 1 ; h < 120 ; h = h + 1 ) {
int curval = lastvals [ h ] ;
if ( curval > 0 ) {
int vpos = calc_vpos_for_co2 ( lastvals [ h ] , dheight ) ;
int vpos_last = calc_vpos_for_co2 ( lastvals [ h - 1 ] , dheight ) ;
display . drawLine ( h - 1 , vpos_last , h , vpos ) ;
}
}
// Set LED color and print value on display
2020-11-25 23:22:48 +01:00
if ( tocalibrateornot = = 42 ) { set_led_color ( CO2 ) ; }
2020-11-25 21:33:28 +01:00
//display.setLogBuffer(1, 30);
2020-11-12 19:46:23 +01:00
display . setFont ( Cousine_Regular_54 ) ;
display . setTextAlignment ( TEXT_ALIGN_CENTER ) ;
display . drawString ( 64 , 0 , String ( CO2 ) ) ;
2020-11-25 21:33:28 +01:00
//display.drawLogBuffer(0, 0);
2020-10-31 16:06:36 +01:00
display . display ( ) ;
// Debug output
Serial . print ( " CO2 (ppm): " ) ;
2020-11-19 13:13:10 +01:00
Serial . print ( CO2 ) ;
2020-11-25 21:33:28 +01:00
Serial . print ( " Background CO2: " + String ( myMHZ19 . getBackgroundCO2 ( ) ) ) ;
2020-11-25 23:22:48 +01:00
Serial . print ( " Temperature: " + String ( myMHZ19 . getTemperature ( ) ) + " Temperature Adjustment: " + String ( myMHZ19 . getTempAdjustment ( ) ) ) ;
2020-11-25 21:33:28 +01:00
//Serial.print(myMHZ19.getBackgroundCO2());
Serial . println ( " uptime: " + uptime_formatter : : getUptime ( ) ) ;
2020-10-31 16:06:36 +01:00
getDataTimer = millis ( ) ;
}
2020-11-12 19:46:23 +01:00
}
2020-11-24 01:19:10 +01:00
2020-11-25 21:33:28 +01:00
2020-11-24 01:19:10 +01:00
void loop ( ) {
2020-11-25 21:33:28 +01:00
if ( initloop = = 1 ) {
if ( millis ( ) > 10000 ) {
Serial . println ( " === safe zone === " ) ;
switchBootMode ( 23 ) ;
// preferences.putUInt("cal", 42); // wir haben die safe zone erreicht, beim naechsten boot nicht kalibrieren!
safezone = 0 ;
initloop = 0 ;
}
}
if ( safezone = = 0 ) {
if ( tocalibrateornot = = 23 ) {
2020-11-25 23:22:48 +01:00
if ( millis ( ) - getDataTimer1 < = CALINTERVAL ) {
2020-11-24 01:19:10 +01:00
rainbow ( 10 ) ;
display . clear ( ) ;
2020-11-25 21:33:28 +01:00
display . setTextAlignment ( TEXT_ALIGN_CENTER ) ;
2020-11-25 23:22:48 +01:00
//countdown = (CALINTERVAL - (getDataTimer1 + millis() * -1)) / 1000;
//countdown = (millis() + getDataTimer1 - CALINTERVAL) * -1 / 1000;
countdown = ( ( getDataTimer1 + CALINTERVAL ) - millis ( ) ) / 1000 ;
Serial . println ( " Countdown: " + String ( countdown ) ) ;
display . drawString ( 64 , 0 , String ( countdown ) ) ;
2020-11-24 01:19:10 +01:00
display . display ( ) ;
}
2020-11-25 21:33:28 +01:00
else if ( millis ( ) - getDataTimer1 > = CALINTERVAL ) {
calibrateCO2 ( ) ;
getDataTimer1 = millis ( ) ;
}
2020-11-24 01:19:10 +01:00
}
else if ( tocalibrateornot = = 42 ) {
2020-11-25 21:33:28 +01:00
if ( initloop = = 1 ) {
2020-11-24 01:19:10 +01:00
Serial . println ( " fake news, nobody has the intention to do calibration.... " ) ;
2020-11-25 21:33:28 +01:00
initloop = 0 ;
2020-11-24 01:19:10 +01:00
}
2020-11-25 21:33:28 +01:00
2020-11-24 01:19:10 +01:00
}
}
2020-11-25 21:33:28 +01:00
readco2 ( ) ;
2020-11-24 01:19:10 +01:00
}