Components and supplies
Resistor 221 ohm
Resistor 10k ohm
Temperature sensor with thermistor VMA-320
Alphanumeric LCD, 16 x 2
SD card logging shield VMA-304
Resistor 1k ohm
MQ-135
General Purpose Quad Op-Amp
Arduino UNO
Project description
Code
Time.cpp
c_cpp
Time and date utility
1#include "Arduino.h" 2#include "Time.h" 3 4Time::Time (byte hr, byte mnt, byte sec, byte d, byte m, byte y) { 5 _timeStart = (hr*60UL+mnt)*60UL+ sec; //initialize _timeStart 6 _lastMillis = millis(); //last time millis() was called 7 _curTime = _lastMillis/1000UL; //time since start of program in seconds (rollover after 49000 days, rollover is prevented by updDate() after 1 day) 8 9 //initialize _Time[] 10 _Time[0] = hr; 11 _Time[1] = mnt; 12 _Time[2] = sec; 13 14 //initialize _Date[] 15 _Date[0] = d; 16 _Date[1] = m; 17 _Date[2] = y; 18 19 _dayRollover = false; 20} 21 22 23void Time::updTime() { 24 unsigned long curMillis = millis(); 25 _curTime += curMillis/1000UL - _lastMillis/1000UL; //update _curTime 26 _lastMillis = curMillis; //record _lastMilles for use during next call of updTime() 27 28 unsigned long curTime = _curTime + _timeStart; 29 if (curTime/(24UL*60UL*60UL) > 0) _dayRollover = true; //if time goes beyond 23:23:59 dayrollover occurs and time returs toe 30 //update _Time[] 31 curTime = curTime%(24UL*60UL*60UL); 32 _Time[0] = curTime/(60UL*60UL); 33 curTime = curTime%(60UL*60UL); 34 _Time[1] = curTime/60UL; 35 curTime = curTime%60UL; 36 _Time[2] = curTime; 37 38 //if needed, update _Date 39 if (_dayRollover) updDate(); //if dayrollover, then adjust date 40} 41 42 43void Time::updDate() { 44 const byte maxDays[13] = {0,31,29,31,30,31,30,31,31,30,31,30,31}; //2020 is leap year, next year change num days of February 45 const byte day = 0; //makes code more easy to read 46 const byte month = 1; 47 const byte year = 2; 48 49 _Date[day]++; 50 if (_Date[day] > maxDays[_Date[month]]) { //if monthrollover then adjust month 51 _Date[day] = 1; 52 _Date[month]++; 53 if (_Date[month] > 12) { //if yearrollover then adjust year 54 _Date[month] = 1; 55 _Date[year]++; 56 } 57 } 58 _curTime = _curTime - (24UL*60UL*60UL); //update _curTime (subtract one day) 59 _dayRollover = false; 60} 61 62 63unsigned long Time::lastMillis() { 64 return _lastMillis; 65} 66 67 68String Time::timeToString(String sign) { 69 return toString(_Time, sign); 70} 71 72 73String Time::dateToString(String sign) { 74 return toString(_Date, sign); 75} 76 77 78String Time::toString(byte* tostring, String sign) { 79 String string = ""; 80 string += leadingZeros(tostring[0]); 81 for (byte i=1; i<3; i++) { 82 string+= sign; 83 string+= leadingZeros(tostring[i]); 84 } 85 return string; 86} 87 88 89String Time::leadingZeros(byte value) { 90 String string = ""; 91 if (value < 10) { 92 string += "0"; 93 } 94 string += String(value); 95 return string; 96} 97
Calculations.h
c_cpp
Header to Calculations.ccp
1#include "Arduino.h" 2 3//this library holds all physiscs calculations 4 5//Calculate Temperature from analog input value 6double calcTemp(int Value); 7 8//Calculate vapour pressure from temperature 9double calcPw(double Temp); 10 11//Calculate relative humidity from wet and dry bulb temperature and vapour pressures at respective temperatures 12double calcRH(double Td, double Tw, double Pwd, double Pww); 13 14//Calculate sensitivity factor (Rs/R0) as function of relative humidity and temperature 15double calcfRHT (double RH, double T); 16 17//Calculate sensor resistance (Rs) from analog input, after signal amplification with OpAmp 18double calcRs(int sensorReading); 19 20//Calculate CO2 concentration from measured sensor resistance (Rs) and relative humidity and temperature correction factor 21double calcCO2(double Rs,double fRHT); 22
Calculations.cpp
c_cpp
Calculation of temperature, relative humidity, sensor resistance based on values read from A0, A1 and A2. From these date it calculates water vapour pressures and sensor correction factor are derived. Finally the CO2 concentration is measured
1#include "Arduino.h" 2#include "Calculations.h" 3 4 5double calcTemp(int Value) { //https://playground.arduino.cc/ComponentLib/Thermistor2/ 6 double Temp; 7 Temp = log(10000.0/(1024.0/Value-1)); // for pull-up configuration 8 Temp = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * Temp * Temp )) * Temp ); 9 Temp = Temp - 273.15; // Convert Kelvin to Celcius 10 11 return Temp;//degrees C 12} 13 14 15double calcPw(double Temp) { //R.C. Rodgers and G.E. Hill, Brittish Journal of Anaesthesia (1978),50, 415 16 const double A = 7.16728; 17 const double B = 1716.984; 18 const double C = 232.538; 19 double exponent = A - B/(Temp+C); 20 double Pw = pow(10,exponent); 21 22 return Pw;//kPa 23} 24 25 26double calcRH(double Td, double Tw, double pWd, double pWw) { //https://www.1728.org/relhum.htm 27 double RH; 28 if(Tw >= Td) { //to prevent RH>1 due to measurement errors where Tw >= Td 29 RH = 1.0; 30 } 31 else { 32 double pav = 101.55; //atmospheric pressure in kPa http://www.klimaatatlas.nl/klimaatatlas.php 33 double N = 0.0006687451584; //kPa/K 34 RH = (pWw - N*pav*(1+0.00115*Tw)*(Td-Tw))/pWd; 35 } 36 37 return RH; //dimensionless 38} 39 40 41double calcfRHT (double RH, double T) { //Own fit from MQ-135 datasheet. It is presumed that CO2 readings are equally influenced by temperature and RH as ammonia readings in given graphs 42 const double Intercept = 1.782663144; 43 const double RC_RH = -0.748177946; 44 const double RC_T = -0.015924756; 45 const double RC_RHT = 0.006677957; 46 double calcfRHT = Intercept + RC_RH*RH + RC_T*T + RC_RHT*RH*T; 47 48 return calcfRHT; //Rs/R0 dimensionless 49} 50 51 52double calcRs(int sensorReading) { //Calculates Rs from amplified analog signal 53 const double gain = 1.0 + 10000.0/1000.0; //amplification of opamp as function of resistances in opamp circuit 54 const double Rref = 10.0; //Reference resistance (in kOhm) built in MQ-135 board 55 double sensorOrigReading = sensorReading/gain; 56/*formula derivation: 57 *sensorOrigReading = 1024*Rref/(Rref+Rsensor) 58 *sensorOrigReading = 1024*1/(1+Rsensor/Rref) 59 *(1+Rsensor/Rref) = 1024/sensorOrigReading 60 *Rsensor = (1024/sensorOrigReading-1)*Rref 61 */ 62 double Rsensor = (1024.0/sensorOrigReading-1.0)*Rref; 63 64 return Rsensor; //kOhm 65} 66 67 68double calcCO2(double Rs, double fRHT) { 69 const double R410 = 270.0; //Measured resistance (in kOhm) after one night with open window 70 const double fRHT410 = 1.08; //calcutated correction factor after one night with open window 71 const double a = 410.0; 72 const double b = -2.769034857; //slope from Mad Frog 73 74 double CO2 = a*pow((Rs/fRHT)/(R410/fRHT410),b); //CO2atm = 410 ppm 75 76 return CO2;//ppm 77} 78
CO2v2.ino
c_cpp
Main program
1#include <SPI.h> 2#include <SD.h> 3#include <LiquidCrystal.h> 4#include "Time.h" 5#include "Calculations.h" 6 7const int rs = 8, en = 7, d4 = 6, d5 = 5, d6 = 4, d7 = 3; 8LiquidCrystal lcd(rs, en, d4, d5, d6, d7); 9 10// set up variables using the SD utility library functions: 11Sd2Card card; 12SdVolume volume; 13SdFile root; 14//pin connected to CS of SD card shield 15const int chipSelect = 2; 16 17#define _dd_ 20 18#define _mm_ 1 19#define _yy_ 20 20 21#define _hr_ 17 22#define _min_ 54 23#define _sec_ 0 24 25char fileName[13]; 26 27//Set start time and date 28Time myTime(_hr_,_min_,_sec_,_dd_,_mm_,_yy_); //hr,min,sec,d,m,y 29 30//used connections to Arduino 31const byte Td_sensor = A0; 32const byte Tw_sensor = A1; 33const byte CO2_sensor = A2; 34const byte checkLight = 9; 35 36 37//Data are stored in data array (double data[]) to ease printing and writing to SD card and LCD screen. Here the positions of the data in the array are given. 38const byte Td = 0; //deg Celcius 39const byte Tw = 1; 40const byte RH = 2; //relative humidity 41const byte CO2 = 3; //ppm 42const byte pWd = 4; //kPa 43const byte pWw = 5; 44const byte fRHT = 6; //- 45const byte Rs = 7; //kohm 46const byte numData = 8; //number of data in data [] 47 48 49const String degC = String ("\\xC2\\xB0") + String("C"); 50const String labels[numData] = {"Td" ,"Tw" ,"RH","CO2","pWd","pWw","fRHT","Rs" }; 51const String units [numData] = {degC ,degC ,"-" ,"ppm","Pa" ,"Pa" ,"-" ,"kohm"}; 52 53 54void setup() { 55 pinMode(Td_sensor ,INPUT); 56 pinMode(Tw_sensor ,INPUT); 57 pinMode(CO2_sensor,INPUT); 58 pinMode(checkLight,OUTPUT); 59 60 lcd.begin(16, 2); 61 lcd.print("hello, world!"); 62 63 Serial.begin(9600); 64 String fileNameConst = String("WK") + myTime.dateToString("") + String(".txt"); 65 strcpy(fileName,fileNameConst.c_str()); 66 67 Serial.print("Initializing SD card..."); 68 69 // see if the card is present and can be initialized: 70 if (!SD.begin(chipSelect)) { 71 Serial.println("Card failed, or not present"); 72 // don't do anything more: 73 while (1); 74 } 75 Serial.println("card initialized."); 76 delay(200); 77 78 // if the file is available, write to it: 79 // prepare datastring with current time and datalabels as column headers 80 myTime.updTime(); 81 String dataString = myTime.dateToString(); 82 dataString+= ","; 83 dataString+= myTime.timeToString(); 84 for (int i = 0; i<numData; i++) { 85 dataString+= ","; 86 dataString+= labels[i]; 87 } 88 89 File dataFile = SD.open(fileName,FILE_WRITE); 90 if (dataFile) { 91 dataFile.println(); 92 dataFile.println(dataString); 93 dataFile.close(); 94 } 95 // if the file isn't open, pop up an error: 96 else { 97 Serial.println("error opening datalog.txt"); 98 } 99} 100 101 102void loop() { 103 myTime.updTime(); 104 digitalWrite(checkLight,LOW); //put here to see if the system is working. now not needed anymore as LCD was installed 105 106 Serial.println(myTime.timeToString() + " " + myTime.dateToString()); 107 108 //fill data array with fresh measurement results 109 double data [numData]; 110 data[Td] = calcTemp(readSensor(Td_sensor)); //degrees C 111 data[Tw] = calcTemp(readSensor(Tw_sensor)); 112 data[pWd] = calcPw(data[Td]); //kPa 113 data[pWw] = calcPw(data[Tw]); 114 data[RH] = calcRH(data[Td],data[Tw],data[pWd],data[pWw]); //dimensionless 115 data[fRHT] = calcfRHT(data[RH],data[Td]); //humidity and temperature correction factor, dimensionless 116 data[Rs] = calcRs(readSensor(CO2_sensor)); //kohm 117 data[CO2] = calcCO2(data[Rs],data[fRHT]); //ppm 118 119 dataToScreen(labels,data,units); 120 121 dataToLCD(labels,data); 122 123 // make a string for assembling the data to log: 124 String dataString = ""; 125 126 dataString += myTime.dateToString(); //start with current time and date 127 dataString += ","; 128 dataString += myTime.timeToString(); 129 130 for (int i=0; i<numData; i++) { //add all data to dataString 131 dataString += ","; 132 dataString += String(data[i]); 133 } 134 135 // open the file. note that only one file can be open at a time, 136 // so you have to close this one before opening another. 137 File dataFile = SD.open(fileName, FILE_WRITE); 138 139 // if the file is available, write to it: 140 if (dataFile) { 141 dataFile.println(dataString); 142 dataFile.close(); 143 } 144 // if the file isn't open, pop up an error: 145 else { 146 Serial.println("error opening datalog.txt"); 147 } 148 149 //halfway time: show time, date, Rs and CO2 150 lcd.clear(); 151 lcd.print(myTime.timeToString()); 152 lcd.print(" "); 153 lcd.print(myTime.dateToString()); 154 lcd.setCursor(0,1); 155 lcd.print("Rs="); 156 lcd.print(data[Rs],0); 157 lcd.print(" CO2="); 158 lcd.print(data[CO2],0); 159 160 digitalWrite(checkLight,HIGH); 161 const unsigned long loopDuration = 30000; 162 while (millis() - myTime.lastMillis() < loopDuration) { //wait until loopDuration 163 ; 164 } 165} 166 167 168//return sensor value 169int readSensor(const byte address) { 170 int value = 0; 171 byte numMeas = 32; //should not be larger than 2^6=64 (int value may roll over) 172 for (int i=0; i<numMeas; i++) { 173 value += analogRead(address); 174 delay(100); //this makes the sketch slow (32 times 100 times 3 = 9600 millisec (almost 10 sec) 175 } 176 value /= numMeas; 177 return value; 178} 179 180 181//write data to screen 182void dataToScreen(const String label[],const double data[],const String unit[]) { //all arguments declared constant as this procedure is not supposed to change any data 183 for (int i=0; i<numData; i++) { 184 if (i==RH) { //RH is printed as % instead of fraction 185 Serial.print(label[i]), Serial.print(":\ "), Serial.print(data[i]*100,2), Serial.print("\ "), Serial.println("%"); 186 } 187 else { 188 Serial.print(label[i]), Serial.print(":\ "), Serial.print(data[i],2), Serial.print("\ "), Serial.println(unit[i]); 189 } 190 } 191 Serial.println(); 192} 193 194 195//write data to LCD 196void dataToLCD(const String label[],const double data[]) { 197 lcd.clear(); 198 for (int i = Td; i<=CO2; i++) { 199 lcd.print(label[i]); 200 lcd.print("="); 201 int decimals = 1; 202 if (i==CO2) decimals = 0; 203 if (i== RH) decimals = 2; 204 lcd.print(data[i],decimals); 205 lcd.print(" "); 206 if (i== Td) lcd.print(" "); 207 if (i== Tw) lcd.setCursor(0,1); 208 } 209} 210
Time.h
c_cpp
Header for Time.ccp
1/* 2 *Time.h - Time library 3 *Records starting time and date of project (as given in source code by programmer) 4 *Can return String with current Time (hh:mm:ss) 5 *Can return String with current date (dd-mm-yy) 6 *Can return millis since start Time 7 *Created by Koen Meesters, december 2019 8 *Last edited Februari 2020 9 */ 10#ifndef Time_h 11#define Time_h 12 13#include "Arduino.h" 14 15class Time { 16 public: 17 Time(byte hr = 0, byte mnt = 0, byte sec = 0, byte d = 1, byte m = 1, byte y = 20); // date by default initialized to 1-1-2020 18 19 String timeToString(String sign = ":"); //returns time in a String, default sign for time is : 20 String dateToString(String sign = "-"); //returns date in a String, default sign for date is - 21 22 unsigned long lastMillis(); 23 void updTime(); //update the time in _Time[3] 24 25 private: 26 byte _Time[3]; //stores time in 3 bytes hr min sec 27 byte _Date[3]; //stores date in 3 bytes day month year 28 bool _dayRollover; //flag set if time goes beyond 23:59:59 29 30 unsigned long _timeStart; //start time in seconds 31 unsigned long _curTime; //current time in seconds 32 unsigned long _lastMillis; //millis() last time updTime() was run 33 34 35 void updDate(); //update the date in _Date[3] (should be run at least every day, otherwise it misses 1 dayrollover) 36 37 String toString(byte*, String sign); //makes a string from _Time[3] or _Date[3] 38 String leadingZeros(byte value); //adds leading zeros if number is smaller than 10 ( 3 --> "03") 39}; 40 41#endif 42
Calculations.h
c_cpp
Header to Calculations.ccp
1#include "Arduino.h" 2 3//this library holds all physiscs calculations 4 5//Calculate Temperature from analog input value 6double calcTemp(int Value); 7 8//Calculate vapour pressure from temperature 9double calcPw(double Temp); 10 11//Calculate relative humidity from wet and dry bulb temperature and vapour pressures at respective temperatures 12double calcRH(double Td, double Tw, double Pwd, double Pww); 13 14//Calculate sensitivity factor (Rs/R0) as function of relative humidity and temperature 15double calcfRHT (double RH, double T); 16 17//Calculate sensor resistance (Rs) from analog input, after signal amplification with OpAmp 18double calcRs(int sensorReading); 19 20//Calculate CO2 concentration from measured sensor resistance (Rs) and relative humidity and temperature correction factor 21double calcCO2(double Rs,double fRHT); 22
Calculations.cpp
c_cpp
Calculation of temperature, relative humidity, sensor resistance based on values read from A0, A1 and A2. From these date it calculates water vapour pressures and sensor correction factor are derived. Finally the CO2 concentration is measured
1#include "Arduino.h" 2#include "Calculations.h" 3 4 5double 6 calcTemp(int Value) { //https://playground.arduino.cc/ComponentLib/Thermistor2/ 7 8 double Temp; 9 Temp = log(10000.0/(1024.0/Value-1)); // for pull-up configuration 10 11 Temp = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * Temp * Temp )) * Temp 12 ); 13 Temp = Temp - 273.15; // Convert Kelvin to Celcius 14 15 16 return Temp;//degrees C 17} 18 19 20double calcPw(double Temp) { //R.C. Rodgers 21 and G.E. Hill, Brittish Journal of Anaesthesia (1978),50, 415 22 const double 23 A = 7.16728; 24 const double B = 1716.984; 25 const double C = 232.538; 26 27 double exponent = A - B/(Temp+C); 28 double Pw = pow(10,exponent); 29 30 31 return Pw;//kPa 32} 33 34 35double calcRH(double Td, double Tw, double pWd, 36 double pWw) { //https://www.1728.org/relhum.htm 37 double RH; 38 if(Tw >= Td) 39 { //to prevent RH>1 due to measurement errors where Tw >= Td 40 RH = 1.0; 41 42 } 43 else { 44 double pav = 101.55; //atmospheric pressure in kPa http://www.klimaatatlas.nl/klimaatatlas.php 45 46 double N = 0.0006687451584; //kPa/K 47 RH = (pWw - N*pav*(1+0.00115*Tw)*(Td-Tw))/pWd; 48 49 } 50 51 return RH; //dimensionless 52} 53 54 55double calcfRHT (double 56 RH, double T) { //Own fit from MQ-135 datasheet. It is presumed that CO2 readings 57 are equally influenced by temperature and RH as ammonia readings in given graphs 58 59 const double Intercept = 1.782663144; 60 const double RC_RH = -0.748177946; 61 62 const double RC_T = -0.015924756; 63 const double RC_RHT = 0.006677957; 64 65 double calcfRHT = Intercept + RC_RH*RH + RC_T*T + RC_RHT*RH*T; 66 67 return 68 calcfRHT; //Rs/R0 dimensionless 69} 70 71 72double calcRs(int sensorReading) 73 { //Calculates Rs from amplified analog signal 74 const double gain = 1.0 + 75 10000.0/1000.0; //amplification of opamp as function of resistances in opamp circuit 76 77 const double Rref = 10.0; //Reference resistance (in kOhm) built in MQ-135 board 78 79 double sensorOrigReading = sensorReading/gain; 80/*formula derivation: 81 82 *sensorOrigReading = 1024*Rref/(Rref+Rsensor) 83 *sensorOrigReading = 1024*1/(1+Rsensor/Rref) 84 85 *(1+Rsensor/Rref) = 1024/sensorOrigReading 86 *Rsensor = (1024/sensorOrigReading-1)*Rref 87 88 */ 89 double Rsensor = (1024.0/sensorOrigReading-1.0)*Rref; 90 91 return 92 Rsensor; //kOhm 93} 94 95 96double calcCO2(double Rs, double fRHT) { 97 const 98 double R410 = 270.0; //Measured resistance (in kOhm) after one night with open 99 window 100 const double fRHT410 = 1.08; //calcutated correction factor after one 101 night with open window 102 const double a = 410.0; 103 const double b = -2.769034857; 104 //slope from Mad Frog 105 106 double CO2 = a*pow((Rs/fRHT)/(R410/fRHT410),b); 107 //CO2atm = 410 ppm 108 109 return CO2;//ppm 110} 111
CO2v2.ino
c_cpp
Main program
1#include <SPI.h> 2#include <SD.h> 3#include <LiquidCrystal.h> 4#include 5 "Time.h" 6#include "Calculations.h" 7 8const int rs = 8, en = 7, d4 = 9 6, d5 = 5, d6 = 4, d7 = 3; 10LiquidCrystal lcd(rs, en, d4, d5, d6, d7); 11 12// 13 set up variables using the SD utility library functions: 14Sd2Card card; 15SdVolume 16 volume; 17SdFile root; 18//pin connected to CS of SD card shield 19const int 20 chipSelect = 2; 21 22#define _dd_ 20 23#define _mm_ 1 24#define _yy_ 20 25 26#define 27 _hr_ 17 28#define _min_ 54 29#define _sec_ 0 30 31char fileName[13]; 32 33//Set 34 start time and date 35Time myTime(_hr_,_min_,_sec_,_dd_,_mm_,_yy_); //hr,min,sec,d,m,y 36 37//used 38 connections to Arduino 39const byte Td_sensor = A0; 40const byte Tw_sensor = 41 A1; 42const byte CO2_sensor = A2; 43const byte checkLight = 9; 44 45 46//Data 47 are stored in data array (double data[]) to ease printing and writing to SD card 48 and LCD screen. Here the positions of the data in the array are given. 49const 50 byte Td = 0; //deg Celcius 51const byte Tw = 1; 52const byte RH = 2; //relative 53 humidity 54const byte CO2 = 3; //ppm 55const byte pWd = 4; //kPa 56const byte 57 pWw = 5; 58const byte fRHT = 6; //- 59const byte Rs = 7; //kohm 60const byte 61 numData = 8; //number of data in data [] 62 63 64const String degC = String ("\\xC2\\xB0") 65 + String("C"); 66const String labels[numData] = {"Td" ,"Tw" ,"RH","CO2","pWd","pWw","fRHT","Rs" 67 }; 68const String units [numData] = {degC ,degC ,"-" ,"ppm","Pa" ,"Pa" 69 ,"-" ,"kohm"}; 70 71 72void setup() { 73 pinMode(Td_sensor ,INPUT); 74 75 pinMode(Tw_sensor ,INPUT); 76 pinMode(CO2_sensor,INPUT); 77 pinMode(checkLight,OUTPUT); 78 79 80 lcd.begin(16, 2); 81 lcd.print("hello, world!"); 82 83 Serial.begin(9600); 84 85 String fileNameConst = String("WK") + myTime.dateToString("") + String(".txt"); 86 87 strcpy(fileName,fileNameConst.c_str()); 88 89 Serial.print("Initializing SD 90 card..."); 91 92 // see if the card is present and can be initialized: 93 if 94 (!SD.begin(chipSelect)) { 95 Serial.println("Card failed, or not present"); 96 97 // don't do anything more: 98 while (1); 99 } 100 Serial.println("card 101 initialized."); 102 delay(200); 103 104 // if the file is available, write 105 to it: 106 // prepare datastring with current time and datalabels as column headers 107 108 myTime.updTime(); 109 String dataString = myTime.dateToString(); 110 dataString+= 111 ","; 112 dataString+= myTime.timeToString(); 113 for (int i = 0; i<numData; 114 i++) { 115 dataString+= ","; 116 dataString+= labels[i]; 117 } 118 119 120 File dataFile = SD.open(fileName,FILE_WRITE); 121 if (dataFile) { 122 dataFile.println(); 123 124 dataFile.println(dataString); 125 dataFile.close(); 126 } 127 // if the 128 file isn't open, pop up an error: 129 else { 130 Serial.println("error opening 131 datalog.txt"); 132 } 133} 134 135 136void loop() { 137 myTime.updTime(); 138 139 digitalWrite(checkLight,LOW); //put here to see if the system is working. now 140 not needed anymore as LCD was installed 141 142 Serial.println(myTime.timeToString() 143 + " " + myTime.dateToString()); 144 145 //fill data array with fresh measurement 146 results 147 double data [numData]; 148 data[Td] = calcTemp(readSensor(Td_sensor)); 149 //degrees C 150 data[Tw] = calcTemp(readSensor(Tw_sensor)); 151 data[pWd] 152 = calcPw(data[Td]); //kPa 153 data[pWw] = calcPw(data[Tw]); 154 155 data[RH] = calcRH(data[Td],data[Tw],data[pWd],data[pWw]); //dimensionless 156 157 data[fRHT] = calcfRHT(data[RH],data[Td]); //humidity and temperature correction 158 factor, dimensionless 159 data[Rs] = calcRs(readSensor(CO2_sensor)); //kohm 160 161 data[CO2] = calcCO2(data[Rs],data[fRHT]); //ppm 162 163 dataToScreen(labels,data,units); 164 165 166 dataToLCD(labels,data); 167 168 // make a string for assembling the data to 169 log: 170 String dataString = ""; 171 172 dataString += myTime.dateToString(); 173 //start with current time and date 174 dataString += ","; 175 dataString += 176 myTime.timeToString(); 177 178 for (int i=0; i<numData; i++) { //add 179 all data to dataString 180 dataString += ","; 181 dataString += String(data[i]); 182 183 } 184 185 // open the file. note that only one file can be open at a time, 186 187 // so you have to close this one before opening another. 188 File dataFile = 189 SD.open(fileName, FILE_WRITE); 190 191 // if the file is available, write to it: 192 193 if (dataFile) { 194 dataFile.println(dataString); 195 dataFile.close(); 196 197 } 198 // if the file isn't open, pop up an error: 199 else { 200 Serial.println("error 201 opening datalog.txt"); 202 } 203 204 //halfway time: show time, date, Rs and 205 CO2 206 lcd.clear(); 207 lcd.print(myTime.timeToString()); 208 lcd.print(" "); 209 210 lcd.print(myTime.dateToString()); 211 lcd.setCursor(0,1); 212 lcd.print("Rs="); 213 214 lcd.print(data[Rs],0); 215 lcd.print(" CO2="); 216 lcd.print(data[CO2],0); 217 218 219 digitalWrite(checkLight,HIGH); 220 const unsigned long loopDuration 221 = 30000; 222 while (millis() - myTime.lastMillis() < loopDuration) { //wait until 223 loopDuration 224 ; 225 } 226} 227 228 229//return sensor value 230int readSensor(const 231 byte address) { 232 int value = 0; 233 byte numMeas = 32; //should not be larger 234 than 2^6=64 (int value may roll over) 235 for (int i=0; i<numMeas; i++) { 236 value 237 += analogRead(address); 238 delay(100); //this makes the sketch slow (32 times 239 100 times 3 = 9600 millisec (almost 10 sec) 240 } 241 value /= numMeas; 242 return 243 value; 244} 245 246 247//write data to screen 248void dataToScreen(const String 249 label[],const double data[],const String unit[]) { //all arguments declared constant 250 as this procedure is not supposed to change any data 251 for (int i=0; i<numData; 252 i++) { 253 if (i==RH) { //RH is printed as % instead of fraction 254 Serial.print(label[i]), 255 Serial.print(":\ "), Serial.print(data[i]*100,2), Serial.print("\ "), Serial.println("%"); 256 257 } 258 else { 259 Serial.print(label[i]), Serial.print(":\ "), Serial.print(data[i],2), 260 Serial.print("\ "), Serial.println(unit[i]); 261 } 262 } 263 Serial.println(); 264} 265 266 267//write 268 data to LCD 269void dataToLCD(const String label[],const double data[]) { 270 lcd.clear(); 271 272 for (int i = Td; i<=CO2; i++) { 273 lcd.print(label[i]); 274 lcd.print("="); 275 276 int decimals = 1; 277 if (i==CO2) decimals = 0; 278 if (i== RH) 279 decimals = 2; 280 lcd.print(data[i],decimals); 281 lcd.print(" "); 282 283 if (i== Td) lcd.print(" "); 284 if (i== Tw) lcd.setCursor(0,1); 285 286 } 287} 288
Time.cpp
c_cpp
Time and date utility
1#include "Arduino.h" 2#include "Time.h" 3 4Time::Time (byte 5 hr, byte mnt, byte sec, byte d, byte m, byte y) { 6 _timeStart = (hr*60UL+mnt)*60UL+ 7 sec; //initialize _timeStart 8 _lastMillis = millis(); //last 9 time millis() was called 10 _curTime = _lastMillis/1000UL; //time since 11 start of program in seconds (rollover after 49000 days, rollover is prevented by 12 updDate() after 1 day) 13 14 //initialize _Time[] 15 _Time[0] = hr; 16 17 _Time[1] = mnt; 18 _Time[2] = sec; 19 20 //initialize _Date[] 21 22 _Date[0] = d; 23 _Date[1] = m; 24 _Date[2] = y; 25 26 _dayRollover 27 = false; 28} 29 30 31void Time::updTime() { 32 unsigned long curMillis 33 = millis(); 34 _curTime += curMillis/1000UL - _lastMillis/1000UL; //update 35 _curTime 36 _lastMillis = curMillis; //record _lastMilles 37 for use during next call of updTime() 38 39 unsigned long curTime = _curTime 40 + _timeStart; 41 if (curTime/(24UL*60UL*60UL) > 0) _dayRollover 42 = true; //if time goes beyond 23:23:59 dayrollover occurs and time returs toe 43 44 //update _Time[] 45 curTime = curTime%(24UL*60UL*60UL); 46 _Time[0] 47 = curTime/(60UL*60UL); 48 curTime = curTime%(60UL*60UL); 49 _Time[1] 50 = curTime/60UL; 51 curTime = curTime%60UL; 52 _Time[2] = 53 curTime; 54 55 //if needed, update _Date 56 if (_dayRollover) updDate(); 57 //if dayrollover, then adjust date 58} 59 60 61void Time::updDate() { 62 const 63 byte maxDays[13] = {0,31,29,31,30,31,30,31,31,30,31,30,31}; //2020 is leap year, 64 next year change num days of February 65 const byte day = 0; //makes code more 66 easy to read 67 const byte month = 1; 68 const byte year = 2; 69 70 _Date[day]++; 71 72 if (_Date[day] > maxDays[_Date[month]]) { //if monthrollover then adjust month 73 74 _Date[day] = 1; 75 _Date[month]++; 76 if (_Date[month] > 12) { //if 77 yearrollover then adjust year 78 _Date[month] = 1; 79 _Date[year]++; 80 81 } 82 } 83 _curTime = _curTime - (24UL*60UL*60UL); //update _curTime (subtract 84 one day) 85 _dayRollover = false; 86} 87 88 89unsigned long Time::lastMillis() 90 { 91 return _lastMillis; 92} 93 94 95String Time::timeToString(String sign) 96 { 97 return toString(_Time, sign); 98} 99 100 101String Time::dateToString(String 102 sign) { 103 return toString(_Date, sign); 104} 105 106 107String Time::toString(byte* 108 tostring, String sign) { 109 String string = ""; 110 string += leadingZeros(tostring[0]); 111 112 for (byte i=1; i<3; i++) { 113 string+= sign; 114 string+= leadingZeros(tostring[i]); 115 116 } 117 return string; 118} 119 120 121String Time::leadingZeros(byte value) { 122 123 String string = ""; 124 if (value < 10) { 125 string += "0"; 126 } 127 128 string += String(value); 129 return string; 130} 131
Time.h
c_cpp
Header for Time.ccp
1/* 2 *Time.h - Time library 3 *Records starting time and date of project (as given in source code by programmer) 4 *Can return String with current Time (hh:mm:ss) 5 *Can return String with current date (dd-mm-yy) 6 *Can return millis since start Time 7 *Created by Koen Meesters, december 2019 8 *Last edited Februari 2020 9 */ 10#ifndef Time_h 11#define Time_h 12 13#include "Arduino.h" 14 15class Time { 16 public: 17 Time(byte hr = 0, byte mnt = 0, byte sec = 0, byte d = 1, byte m = 1, byte y = 20); // date by default initialized to 1-1-2020 18 19 String timeToString(String sign = ":"); //returns time in a String, default sign for time is : 20 String dateToString(String sign = "-"); //returns date in a String, default sign for date is - 21 22 unsigned long lastMillis(); 23 void updTime(); //update the time in _Time[3] 24 25 private: 26 byte _Time[3]; //stores time in 3 bytes hr min sec 27 byte _Date[3]; //stores date in 3 bytes day month year 28 bool _dayRollover; //flag set if time goes beyond 23:59:59 29 30 unsigned long _timeStart; //start time in seconds 31 unsigned long _curTime; //current time in seconds 32 unsigned long _lastMillis; //millis() last time updTime() was run 33 34 35 void updDate(); //update the date in _Date[3] (should be run at least every day, otherwise it misses 1 dayrollover) 36 37 String toString(byte*, String sign); //makes a string from _Time[3] or _Date[3] 38 String leadingZeros(byte value); //adds leading zeros if number is smaller than 10 ( 3 --> "03") 39}; 40 41#endif 42
Downloadable files
Wiring CO2 measurement and logging project
Measures CO2 concentration. Correction for temperature and relative humidity. Writes results to SD card and LCD display
Wiring CO2 measurement and logging project
Comments
Only logged in users can leave comments
ArduinoKoen
0 Followers
•0 Projects
Table of contents
Intro
9
0