/*This code was written by Harvey Schneider. You may freely use it for you personal projects. It is believed to be functionally correct, but no guarantee of performance is given. Use at your own risk. Creative Commons License: This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.*/ #include #include #include #include #include #include // addr,en,rw,rs,d4,d5,d6,d7,bl,blpol LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); #include #include Adafruit_BMP085_Unified bmp = Adafruit_BMP085_Unified(10085); #include #include #define DHTPIN 2 // what pin dht is connected to #define DHTTYPE DHT11 // define type DHT_Unified dht(DHTPIN, DHTTYPE); //uint32_t delayMS; #define SDChipSelectPin 10 boolean startup = 1;//startup allows display and recording on first pass through //---------------------------- void setup() { bmp.begin(); Serial.begin(115200); Wire.begin(); dht.begin(); lcd.begin(20, 4); lcd.clear(); lcd.setBacklight(1); // switch on the backlight // one of these may be used for an alarm //RTC.SetOutput(LOW); //RTC.SetOutput(HIGH); //RTC.SetOutput(DS1307_SQW1HZ); //RTC.SetOutput(DS1307_SQW4KHZ); //RTC.SetOutput(DS1307_SQW8KHZ); //RTC.SetOutput(DS1307_SQW32KHZ); Serial.print(F("Initializing SD card...")); // make sure that the default chip select pin is set to // output, even if not used it: pinMode(SDChipSelectPin, OUTPUT); // see if the card is present and can be initialized: if (!SD.begin(SDChipSelectPin)) { Serial.println(F("Card failed, or not present")); lcd.clear(); lcd.setCursor(1,0); lcd.print(F("SD card failed")); // don't do anything more: // I should add function checks for the other devices. return; } Serial.println(F("card initialized")); } //----------------------------- void loop() { if(checkTime())//check to see if it is time to update the parameters //Don't do more data fetches than is necessary DHT is very slow and conversions cost energy. { float P = getSensors(1);// bmp pressure float t1 = getSensors(2);// bmp temperature float h = getSensors(3);// dht humidity // float t2 = getSensors(4);// dht temperature. I decided not to use this because of low resolution. float dewPt = dewPoint(t1,h); displayParam(h,t1,dewPt,P); recordParam(h,t1,dewPt,P); startup = 0; } displayTime(); } //------------------------------------------ boolean checkTime() { int timeInterval = 2; static boolean timeFlag;// allow one posting of parameters when "timeFlag" is set to "0" //When minutes is evenly divisible by "timeInterval" parameters need to be updated. tmElements_t tm; RTC.read(tm); int time = tm.Minute % timeInterval; //time = minutes modulo 2 remainder if ((time == 0 && timeFlag == 0) || startup == 1)//the first time since modulo was satisfied { timeFlag = 1;// timeFlag == 0 indicates this is the first time through since time was satisfied return(1); } else if(time != 0 && timeFlag != 0)// when modulo is not satisfied, ready 'first time' flag { timeFlag = 0; } return(0); } //---------------------------------------- void displayTime() { tmElements_t tm; RTC.read(tm); lcd.setCursor(12,1); if (tm.Hour <= 9)//hours { lcd.print('0');//correct for single digit } lcd.print(tm.Hour); lcd.print(F(":")); if (tm.Minute <= 9)//minutes { lcd.print('0');//correct for single digit } lcd.print (tm.Minute);//minutes lcd.print(F(":")); if (tm.Second <= 9)//seconds { lcd.print('0');//correct for single digit } lcd.print(tm.Second);//seconds } //-------------------------- //fast calculation of relative humidity, //not as accurate as full calc but good, within 0.17 degrees //doubles needed during calculation. //The accuracy of this may suffer from the fact that an Arduino "double" is //only 32 bits. double dewPoint(double celsius, double humidity) { double a = 17.271; double b = 237.7; double temp = (a * celsius) / (b + celsius) + log(humidity * 0.01); double Td = (b * temp) / (a - temp); return Td; } //---------------------------- void recordParam(float H,float T,float dewPt,float P) { tmElements_t tm; RTC.read(tm); // make a string for assembling the data to log: String dataString = ""; // build data string: if (tm.Month <= 9)//month { dataString += "0";// fix single digit } dataString += tm.Month;//month dataString += "/"; if (tm.Day <= 9)//day { dataString += "0";// fix single digit } dataString += tm.Day;//day dataString += "/"; dataString += tmYearToCalendar(tm.Year);//year dataString += ", "; if (tm.Hour <= 9)//hour { dataString += '0';// fix single digit } dataString += tm.Hour;//hour dataString += ":"; if (tm.Minute <= 9)//minutes { dataString += '0';// fix single digit } dataString += tm.Minute;//minutes dataString += ", "; // open the file. note that only one file can be open at a time, // so you have to close this one before opening another. File dataFile = SD.open("datalog.csv", FILE_WRITE); // if the file is available, write to it: if (dataFile) { if (startup == 1) { dataFile.println(F("Date,, Time,, %Humidity,, Temperature,, Dew Point,, Pressure")); //Extra coma's align headings with data when imported into a spreadsheet } dataFile.print(dataString); dataFile.print(H); dataFile.print(F(", %, ")); dataFile.print(T * 1.8 + 32); dataFile.print(F(", ºF, ")); dataFile.print(dewPt * 1.8 + 32); dataFile.print(F(", ºF, ")); dataFile.print(P); dataFile.println(F(", inHg, ")); dataFile.close(); //also send to serial monitor //this can be deleted if space is tight if (startup == 1) { Serial.println(F("Date, Time, % Humidity, Temperature, Dew Point, Pressure")); //Extra spaces align headings with data on a terminal emulator } Serial.print(dataString); Serial.print(H); Serial.print(F(" %, ")); Serial.print(T * 1.8 + 32); Serial.print(F(" ºF, ")); Serial.print(dewPt * 1.8 + 32); Serial.print(F(" ºF, ")); Serial.print(P); Serial.println(F(" inHg,")); } // if the file isn't open, print an error statement: else { Serial.println(F("error opening datalog.csv")); } } //--------------------------- void displayParam(float H, float T, float dewPt, float P) { const char deg = 223; //This is the degree symbol for the lcd display. lcd.setCursor(0,0);//top left lcd.print(F("Humidity= ")); lcd.print(H);//relative humidity lcd.print(F("% ")); lcd.setCursor(0,1);// second line left lcd.print(F("Temp ")); lcd.print(T * 1.8 + 32);//temperature lcd.print( deg); lcd.setCursor(0,2);// Third line left lcd.print(F("Dew Point= ")); lcd.print(dewPt * 1.8 + 32);//dew point degrees F lcd.print( deg);// degree symbol lcd.setCursor(0,3);//bottom line left lcd.print(F("Pressure= ")); lcd.print(P); lcd.print(F(" inHg")); } //----------------------------------- // float getSensors(int sensorType) { float pressureCorrection = 23.5;//Adjustment for elevation at home 648ft = 23.5mB sensors_event_t event; switch (sensorType) { case 1: // bmp Pressure // bmp.getEvent(&event); bmp.getEvent(&event); return((event.pressure + pressureCorrection) * 0.02953); //corrected to 650 ft above sea level and converted to inHg break; case 2: // bmp temperature // bmp.getEvent(&event); bmp.getEvent(&event); float temperature; bmp.getTemperature(&temperature); return(temperature); break; case 3: // dht Humidity dht.humidity().getEvent(&event); return(event.relative_humidity); break; case 4: // dht temperature dht.temperature().getEvent(&event); return(event.temperature); break; case 5: //default break; } }