/* reptile incubator control by Johan, using LCD, PCF8583 real time clock, DS18B20 temperature sensor and a Solid State relay. Time can be set by sending YYMMddhhmmss; over serial. todo: eeprom store problems, cant change vars in menu... correcting lcd glitches */ #include // I2C lib #include // OneWire for d1820 #include template int EEPROM_writeAnything(int ee, const T& value) { const byte* p = (const byte*)(const void*)&value; int i; for (i = 0; i < sizeof(value); i++) EEPROM.write(ee++, *p++); Serial.print(i); Serial.println (" Config bytes written"); } template int EEPROM_readAnything(int ee, T& value) { byte* p = (byte*)(void*)&value; int i; for (i = 0; i < sizeof(value); i++) *p++ = EEPROM.read(ee++); Serial.print(i); Serial.println(" Config bytes read"); } struct config_t{ boolean night_timer ; byte bgn_night ; byte end_night ; float thermoC_d ; float thermoC_n ; }cfg; #include // ds1820 init #define ONE_WIRE_PIN 5 OneWire oneWire(ONE_WIRE_PIN); DallasTemperature sensors(&oneWire); #include // lcd init LiquidCrystal lcd(12, 11, 10, 9, 8, 7); #include // rtc init PCF8583 p (0xA0); int correct_address = 0; long timer_1; //various variables long timer_2; long interval = 5000; long menutimer; int ssrPin = 13; int setbutton = 2, minusbutton = 3, plusbutton = 4, menuitem=0; boolean lcdcleared = false; boolean night ; float tempC; float thermoC; void setup(){ lcd.begin(16,2); // init lcd lcd.print("Inkubator v0.4"); delay(1000); lcd.setCursor(0,1); lcd.print("1wire temp init."); delay(500); sensors.begin(); //init one wire sensors lcd.setCursor(0,1); lcd.print("serial init. "); delay(500); Serial.begin(9600); // opens serial port, sets data rate to 9600 bps Serial.println("Inkubator running"); Serial.println("To set RTC time send YYMMddhhmmss;"); lcd.setCursor(0,1); lcd.print("eeprom settings "); delay(500); EEPROM_readAnything(0, cfg); //read values from eeprom if (cfg.thermoC_d > 50.0 || cfg.thermoC_d < 20.0){ // check for proper in range values, cfg.thermoC_d = 27.5; // correct if needed } if (cfg.thermoC_n > 50.0 || cfg.thermoC_n < 10.0){ cfg.thermoC_n = 22.5; } if (cfg.bgn_night > 23){ cfg.bgn_night = 23; } if (cfg.end_night > 23){ cfg.end_night = 7; } pinMode(setbutton, INPUT); pinMode(plusbutton, INPUT); pinMode(minusbutton, INPUT); pinMode(ssrPin, OUTPUT); // set output for ssrPin attachInterrupt(0, menu_btn, RISING); // attach interrupt for menu lcd.clear(); } void loop(){ menu(); // display menu if menuitem != 0 set_time_serial(); // listen for time input on serial get_time(); // get current time from rtc get_temp(); // get current temp thermostat(); // check temp and turn heater on/off printlcd(); // print various data to lcd if (millis() - timer_1 > interval){ // dump various data to serial after each interval timer_1 = millis(); serial_dump(); } } void get_time(){ p.get_time(); char time[50]; sprintf(time, "%02d/%02d/%02d %02d:%02d:%02d", p.year, p.month, p.day, p.hour, p.minute, p.second); } void get_temp(){ // get temp from sensor sensors.requestTemperatures(); delay(250); tempC = sensors.getTempCByIndex(0); } void printlcd(){ lcd.setCursor(0,0); lcd.print(" "); lcd.setCursor(0,0); lcdprintFloat(tempC,1); lcd.print((char)223); lcd.print("C"); lcd.setCursor(11,0); if(p.hour < 10){ lcd.print(" "); } lcd.print(p.hour); if ((p.second % 2) == 0) { lcd.print(":"); } else { lcd.print(" "); } if(p.minute < 10){ lcd.print("0"); } lcd.print(p.minute); if (millis() - timer_2 > interval){ timer_2 = millis(); if (night == true && cfg.night_timer == true){ do{ lcd.setCursor(0,1); lcd.print(" *night mode* "); } while (millis() - timer_2 < 1000); } } else { lcd.setCursor(0,1); lcd.print(" "); lcd.setCursor(0,1); lcd.print("th:"); lcdprintFloat(thermoC,1); lcd.setCursor(9,1); lcd.print("htr:"); if (digitalRead (ssrPin) == HIGH) { lcd.print("on "); } else{ lcd.print("off"); } } } void thermostat(){ if ( p.hour > cfg.end_night && p.hour < cfg.bgn_night ){ night = false; } else{ night = true; } if (cfg.night_timer == true){ if (night == true){ thermoC = cfg.thermoC_n; } else { thermoC = cfg.thermoC_d; } } else { thermoC = cfg.thermoC_d; } if (tempC < (thermoC - 0.2)){ digitalWrite(ssrPin, HIGH); } else { if (tempC > thermoC) { digitalWrite(ssrPin, LOW); } } } void set_time_serial(){ if(Serial.available() > 0){ p.year= (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48)) + 2000; p.month = (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48)); p.day = (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48)); p.hour = (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48)); p.minute = (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48)); p.second = (byte) ((Serial.read() - 48) * 10 + (Serial.read() - 48)); // Use of (byte) type casting and ascii math to achieve result. if(Serial.read() == ';'){ Serial.println("setting time"); p.set_time(); } } } void serial_dump(){ //prints various data to serial with timestamps if (p.day < 10){ Serial.print("0"); } Serial.print(p.day); Serial.print("/"); Serial.print(p.month); Serial.print("/"); Serial.print(p.year); Serial.print(" "); if (p.hour < 10 ){ Serial.print("0"); } Serial.print(p.hour); Serial.print(":"); if (p.minute < 10 ){ Serial.print("0"); } Serial.print(p.minute); Serial.print("."); if (p.second < 10 ){ Serial.print("0"); } Serial.print(p.second); Serial.print(" temp="); Serial.print(tempC); Serial.print(" heater="); if (digitalRead(ssrPin) == HIGH){ Serial.print("ON"); } else { Serial.print("OFF"); } if (cfg.night_timer = true){ Serial.print(" mode="); if( night == true){ Serial.print("night"); } else { Serial.print("day"); } } Serial.print(" target="); Serial.print(thermoC); Serial.println(); } void menu_btn(){ // menu interrupt handler static unsigned long last_interrupt = 0; unsigned long interrupt_time = millis(); if (interrupt_time - last_interrupt > 250){ menuitem = menuitem + 1; lcdcleared = false; menutimer = millis(); } last_interrupt = interrupt_time; } void menu(){ while(menuitem > 0 && menuitem < 12){ // menu options are from 1 to x if (millis() - menutimer > 15000){ menuitem = 12; } lcd.setCursor(0,0); if (lcdcleared == false ){ // makes sure lcd is cleared when menu item bttn is pressed lcd.clear(); lcdcleared = true; } switch(menuitem){ // various menu items case 1: lcd.print(" **Setup mode** "); lcd.setCursor(0,1); lcd.print("SET = continue"); break; case 2: lcd.print("Day/Night timer"); lcd.setCursor(0,1); if (cfg.night_timer == true){ lcd.print("ON "); } else{ lcd.print("OFF"); } if (digitalRead(minusbutton) == HIGH || digitalRead (plusbutton) == HIGH){ if (cfg.night_timer == true){ cfg.night_timer = false; } else { cfg.night_timer = true; } delay(250); } break; case 3: lcd.print("Day thermo:"); lcd.setCursor(0,1); lcdprintFloat(cfg.thermoC_d, 1); if(digitalRead(minusbutton)==HIGH){ cfg.thermoC_d = cfg.thermoC_d - 0.5; delay(250); // simple debounce, needs improvement } if (digitalRead(plusbutton)==HIGH){ cfg.thermoC_d = cfg.thermoC_d + 0.5; delay(250); } break; case 4: lcd.print("Night thermo:"); lcd.setCursor(0,1); lcdprintFloat(cfg.thermoC_n, 1); if(digitalRead(minusbutton)==HIGH){ cfg.thermoC_n = cfg.thermoC_n - 0.5; delay(250); } if (digitalRead(plusbutton)==HIGH){ cfg.thermoC_n = cfg.thermoC_n + 0.5; delay(250); } break; case 5: lcd.print("Night start:"); lcd.setCursor(0,1); lcd.print(int(cfg.bgn_night)); if(digitalRead(minusbutton)==HIGH){ if(cfg.bgn_night > 17){ cfg.bgn_night= cfg.bgn_night - 1; } delay(250); } if (digitalRead(plusbutton)==HIGH){ if (cfg.bgn_night < 23){ cfg.bgn_night= cfg.bgn_night + 1; } delay(250); } break; case 6: lcd.print("Night end:"); lcd.setCursor(0,1); lcd.print(int(cfg.end_night)); if(digitalRead(minusbutton)==HIGH){ if(cfg.end_night > 5){ cfg.end_night= cfg.end_night - 1; } delay(250); } if (digitalRead(plusbutton)==HIGH){ if (cfg.end_night < 12){ cfg.end_night= cfg.end_night + 1; } delay(250); } break; case 7: lcd.print("RTC hour"); lcd.setCursor(0,1); if (p.hour < 10 ){ lcd.print("0"); } lcd.print( int(p.hour) ); if(digitalRead(minusbutton)==HIGH){ if(p.hour == 0){ p.hour = 23; } else{ if (p.hour > 0 ){ p.hour = p.hour - 1 ; } } delay(250); } if (digitalRead(plusbutton)==HIGH){ if (p.hour == 23){ p.hour = 0; } else{ if (p.hour < 23){ p.hour = p.hour + 1; } } delay(250); } p.set_time(); break; case 8: lcd.print("RTC Minute"); lcd.setCursor(0,1); if (p.minute < 10 ){ lcd.print("0"); } lcd.print( int(p.minute) ); if(digitalRead(minusbutton)==HIGH){ if(p.minute == 0){ p.minute = 59; } else{ if (p.minute > 0 ){ p.minute--; } } delay(250); } if (digitalRead(plusbutton)==HIGH){ if (p.minute == 59){ p.minute = 0; } else{ if (p.minute < 59){ p.minute++; } } delay(250); } p.set_time(); break; default: // displays empty menu items for debugging lcd.print("Menu item: "); lcd.print(menuitem); if (menuitem > 11){ // exits menu when end of options is reached EEPROM_writeAnything(0, cfg); menuitem = 0; menutimer = 0; lcd.clear(); } } } } void lcdprintFloat(float number, int digits) { // Handle negative numbers if (number < 0.0) { lcd.print("-"); number = -number; } // Round correctly so that print(1.999, 2) prints as "2.00" float rounding = 0.5; for (uint8_t i=0; i 0) lcd.print("."); // Extract digits from the remainder one at a time while (digits-- > 0) { remainder *= 10.0; int toPrint = int(remainder); lcd.print(toPrint); remainder -= toPrint; } }