/* // Typhon firmware // v0.2 alpha 2010-23-11 // N. Enders, R. Ensminger // // This sketch provides firmware for the Typhon LED controller. // It provides a structure to fade 4 independent channels of LED lighting // on and off each day, to simulate sunrise and sunset. // // Current work in progress: // - store all LED variables in EEPROM so they are not reset by a loss of power // // Future developments may include: // - moon phase simulation // - storm simulation // // Sketch developed in Arduino-18 // Requires LiquidCrystal, Wire, EEPROM, EEPROMVar, and Button libraries. // Button is available here: http://www.arduino.cc/playground/Code/Button // EEPROMVar is available here: http://www.arduino.cc/playground/uploads/Profiles/EEPROMVar_01.zip */ // include the libraries: #include #include #include #include #include /**** Define Variables & Constants ****/ /**************************************/ // set the RTC's I2C address #define DS1307_I2C_ADDRESS 0x68 // create the LCD LiquidCrystal lcd(8, 7, 5, 4, 16, 2); // set up backlight int bkl = 6; // backlight pin byte bklIdle = 10; // PWM value for backlight at idle byte bklOn = 70; // PWM value for backlight when on int bklDelay = 10000; // ms for the backlight to idle before turning off unsigned long bklTime = 0; // counter since backlight turned on // create the menu counter int menuCount = 1; int menuSelect = 0; //create manual override variables boolean override = false; byte overmenu = 0; int overpercent = 0; // create the buttons Button menu = Button(12,PULLDOWN); Button select = Button(13,PULLDOWN); Button plus = Button(14,PULLDOWN); Button minus = Button(15,PULLDOWN); // Button state constants. byte buttonNotPressed = 0; byte buttonUniquePress = 1; byte buttonIsPressed = 2; byte slowCount = 3; // number of intervals to do a "slowDelay" when holding a button int slowDelay = 1000; // milliseconds to delay when initially holding a button int fastDelay = 100; // milliseconds to delay after holding the button for "slowCount" intervals // LED variables. These control the behavior of lighting. Change these to customize behavoir int minCounter = 0; // counter that resets at midnight. int oldMinCounter = 0; // counter that resets at midnight. // Used for button hold down int intervalCounter = 0; int oldIntervalCounter; unsigned long currMil = 0; // current millisecond int oneVal = 0; // current value for channel 1 int twoVal = 0; // current value for channel 2 int threeVal = 0; // current value for channel 3 int fourVal = 0; // current value for channel 4 // Variables making use of EEPROM memory: EEPROMVar oneStartMins = 750; // minute to start this channel. EEPROMVar onePhotoPeriod = 720; // photoperiod in minutes for this channel. EEPROMVar oneMax = 100; // max intensity for this channel, as a percentage EEPROMVar oneFadeDuration = 60; // duration of the fade on and off for sunrise and sunset for // this channel. EEPROMVar twoStartMins = 810; EEPROMVar twoPhotoPeriod = 600; EEPROMVar twoMax = 100; EEPROMVar twoFadeDuration = 60; EEPROMVar threeStartMins = 810; EEPROMVar threePhotoPeriod = 600; EEPROMVar threeMax = 100; EEPROMVar threeFadeDuration = 60; EEPROMVar fourStartMins = 480; EEPROMVar fourPhotoPeriod = 510; EEPROMVar fourMax = 100; EEPROMVar fourFadeDuration = 60; typedef struct { int Led; // channel pin int StartMins; // minute to start this channel. int PhotoPeriod; // photoperiod in minutes for this channel. int Max; // max intensity for this channel, as a percentage int FadeDuration; // duration of the fade on and off for sunrise and sunset for // this channel. } channelVals_t; channelVals_t channel[4]; /* int oneStartMins = 1380; // minute to start this channel. int onePhotoPeriod = 120; // photoperiod in minutes for this channel. int oneMax = 100; // max intensity for this channel, as a percentage int oneFadeDuration = 60; // duration of the fade on and off for sunrise and sunset for // this channel. int twoStartMins = 800; int twoPhotoPeriod = 60; int twoMax = 100; int twoFadeDuration = 15; int threeStartMins = 800; int threePhotoPeriod = 60; int threeMax = 100; int threeFadeDuration = 30; int fourStartMins = 800; int fourPhotoPeriod = 120; int fourMax = 100; int fourFadeDuration = 60; */ /****** RTC Functions ******/ /***************************/ // Convert decimal numbers to binary coded decimal byte decToBcd(byte val) { return ( (val/10*16) + (val%10) ); } // Convert binary coded decimal to decimal numbers byte bcdToDec(byte val) { return ( (val/16*10) + (val%16) ); } // Sets date and time, starts the clock void setDate(byte second, // 0-59 byte minute, // 0-59 byte hour, // 1-23 byte dayOfWeek, // 1-7 byte dayOfMonth, // 1-31 byte month, // 1-12 byte year) // 0-99 { Wire.beginTransmission(DS1307_I2C_ADDRESS); Wire.send(0); Wire.send(decToBcd(second)); Wire.send(decToBcd(minute)); Wire.send(decToBcd(hour)); Wire.send(decToBcd(dayOfWeek)); Wire.send(decToBcd(dayOfMonth)); Wire.send(decToBcd(month)); Wire.send(decToBcd(year)); Wire.endTransmission(); } // Gets the date and time void getDate(byte *second, byte *minute, byte *hour, byte *dayOfWeek, byte *dayOfMonth, byte *month, byte *year) { Wire.beginTransmission(DS1307_I2C_ADDRESS); Wire.send(0); Wire.endTransmission(); Wire.requestFrom(DS1307_I2C_ADDRESS, 7); *second = bcdToDec(Wire.receive() & 0x7f); *minute = bcdToDec(Wire.receive()); *hour = bcdToDec(Wire.receive() & 0x3f); *dayOfWeek = bcdToDec(Wire.receive()); *dayOfMonth = bcdToDec(Wire.receive()); *month = bcdToDec(Wire.receive()); *year = bcdToDec(Wire.receive()); } /****** LED Functions ******/ /***************************/ //function to set LED brightness according to time of day //function has three equal phases - ramp up, hold, and ramp down int setLed(int mins, // current time in minutes int ledPin, // pin for this channel of LEDs int start, // start time for this channel of LEDs int period, // photoperiod for this channel of LEDs int fade, // fade duration for this channel of LEDs int ledMax // max value for this channel ) { int val = 0; //fade up if (mins > start || mins <= start + fade) { val = map(mins - start, 0, fade, 0, ledMax); } //fade down if (mins > start + period - fade && mins <= start + period) { val = map(mins - (start + period - fade), 0, fade, ledMax, 0); } //off or post-midnight run. if (mins <= start || mins > start + period) { if((start+period)%1440 < start && (start + period)%1440 > mins ) { val=map((start+period-mins)%1440,0,fade,0,ledMax); } else val = 0; } if (val > ledMax) {val = ledMax;} if (val < 0) {val = 0; } analogWrite(ledPin, map(val, 0, 100, 0, 255)); if(override){val=overpercent;} return val; } /**** Display Functions ****/ /***************************/ // format a number of minutes into a readable time (24 hr format) void printMins(int mins, //time in minutes to print boolean ampm //print am/pm? ) { int hr = (mins%1440)/60; int mn = mins%60; if(hr<10){ lcd.print(" "); } lcd.print(hr); lcd.print(":"); if(mn<10){ lcd.print("0"); } lcd.print(mn); } // format hours, mins, secs into a readable time (24 hr format) void printHMS (byte hr, byte mn, byte sec //time to print ) { if(hr<10){ lcd.print(" "); } lcd.print(hr, DEC); lcd.print(":"); if(mn<10){ lcd.print("0"); } lcd.print(mn, DEC); lcd.print(":"); if(sec<10){ lcd.print("0"); } lcd.print(sec, DEC); } void ovrSetAll(int pct){ analogWrite(channel[0].Led, map(pct,0,100,0,255)); analogWrite(channel[1].Led, map(pct,0,100,0,255)); analogWrite(channel[2].Led, map(pct,0,100,0,255)); analogWrite(channel[3].Led, map(pct,0,100,0,255)); } byte buttonCheck(Button *button) { if (button->uniquePress()) { return buttonUniquePress; } else if (button->isPressed()) { return buttonIsPressed; } else { return buttonNotPressed; } } boolean checkButtonAction(Button *button) { byte buttonState = buttonCheck(button); unsigned long mil = millis(); if (buttonState == buttonUniquePress) { intervalCounter = slowCount; currMil = mil; return true; } else if (buttonState == buttonIsPressed) { if (intervalCounter > 0) { if (currMil < (mil - slowDelay)) { intervalCounter--; currMil = mil; return true; } } else { if (currMil < (mil - fastDelay)) { currMil = mil; return true; } } } return false; } /**** Setup ****/ /***************/ void setup() { // Initialize channel variables. Set LED channel pin and retrieve values from EEPROM channel[0].Led = 9; channel[0].StartMins = oneStartMins; channel[0].PhotoPeriod = onePhotoPeriod; channel[0].Max = oneMax; channel[0].FadeDuration = oneFadeDuration; channel[1].Led = 10; channel[1].StartMins = twoStartMins; channel[1].PhotoPeriod = twoPhotoPeriod; channel[1].Max = twoMax; channel[1].FadeDuration = twoFadeDuration; channel[2].Led = 11; channel[2].StartMins = threeStartMins; channel[2].PhotoPeriod = threePhotoPeriod; channel[2].Max = threeMax; channel[2].FadeDuration = threeFadeDuration; channel[3].Led = 3; channel[3].StartMins = fourStartMins; channel[3].PhotoPeriod = fourPhotoPeriod; channel[3].Max = fourMax; channel[3].FadeDuration = fourFadeDuration; Wire.begin(); pinMode(bkl, OUTPUT); lcd.begin(16, 2); digitalWrite(bkl, HIGH); lcd.print("Typhon-Reef"); lcd.setCursor(0,1); lcd.print(""); delay(5000); lcd.clear(); analogWrite(bkl,bklIdle); } /***** Loop *****/ /****************/ void loop() { byte second, minute, hour, dayOfWeek, dayOfMonth, month, year; getDate(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year); oldMinCounter = minCounter; minCounter = hour * 60 + minute; int i; for (i=0; i<4; i++) { //check & set fade durations if(channel[i].FadeDuration > channel[i].PhotoPeriod/2 && channel[i].PhotoPeriod >0) { channel[i].FadeDuration = channel[i].PhotoPeriod/2; } if(channel[i].FadeDuration<1){channel[i].FadeDuration=1;} } //check & set any time functions //set outputs if(!override){ oneVal = setLed(minCounter, channel[0].Led, channel[0].StartMins, channel[0].PhotoPeriod, channel[0].FadeDuration, channel[0].Max); twoVal = setLed(minCounter, channel[1].Led, channel[1].StartMins, channel[1].PhotoPeriod, channel[1].FadeDuration, channel[1].Max); threeVal = setLed(minCounter, channel[2].Led, channel[2].StartMins, channel[2].PhotoPeriod, channel[2].FadeDuration, channel[2].Max); fourVal = setLed(minCounter, channel[3].Led, channel[3].StartMins, channel[3].PhotoPeriod, channel[3].FadeDuration, channel[3].Max); } else{ oneVal = overpercent; twoVal = overpercent; threeVal = overpercent; fourVal = overpercent; ovrSetAll(overpercent); } // Update EEProms with any values that may have changed if (channel[0].StartMins != oneStartMins) { oneStartMins = channel[0].StartMins; } if (channel[0].PhotoPeriod != onePhotoPeriod) { onePhotoPeriod = channel[0].PhotoPeriod; } if (channel[0].Max != oneMax) { oneMax = channel[0].Max; } if (channel[0].FadeDuration != oneFadeDuration) { oneFadeDuration = channel[0].FadeDuration; } if (channel[1].StartMins != twoStartMins) { twoStartMins = channel[1].StartMins; } if (channel[1].PhotoPeriod != twoPhotoPeriod) { twoPhotoPeriod = channel[1].PhotoPeriod; } if (channel[1].Max != twoMax) { twoMax = channel[1].Max; } if (channel[1].FadeDuration != twoFadeDuration) { twoFadeDuration = channel[1].FadeDuration; } if (channel[2].StartMins != threeStartMins) { threeStartMins = channel[2].StartMins; } if (channel[2].PhotoPeriod != threePhotoPeriod) { threePhotoPeriod = channel[2].PhotoPeriod; } if (channel[2].Max != threeMax) { threeMax = channel[2].Max; } if (channel[2].FadeDuration != threeFadeDuration) { threeFadeDuration = channel[2].FadeDuration; } if (channel[3].StartMins != fourStartMins) { fourStartMins = channel[3].StartMins; } if (channel[3].PhotoPeriod != fourPhotoPeriod) { fourPhotoPeriod = channel[3].PhotoPeriod; } if (channel[3].Max != fourMax) { fourMax = channel[3].Max; } if (channel[3].FadeDuration != fourFadeDuration) { fourFadeDuration = channel[3].FadeDuration; } // if (intervalCounter > 0) { // unsigned long milsec = millis(); // if ((currMil < (milsec - 1000)) || (currMil > milsec)) { // currMil = milsec; // intervalCounter--; // } // } //turn the backlight off and reset the menu if the idle time has elapsed if(bklTime + bklDelay < millis() && bklTime > 0 ){ analogWrite(bkl,bklIdle); menuCount = 1; lcd.clear(); bklTime = 0; } //iterate through the menus if(menu.uniquePress()){ analogWrite(bkl,bklOn); bklTime = millis(); if(menuCount < 20){ menuCount++; }else { menuCount = 1; } lcd.clear(); } switch (menuCount) { case 1: doMainMenu(minCounter, oldMinCounter, hour, minute, second, oneVal, twoVal, threeVal, fourVal); break; case 2: doOverride(second); break; case 3: doStartTime(0); break; case 4: doEndTime(0); break; case 5: doFadeDuration(0); break; case 6: doMaxIntensity(0); break; case 7: doStartTime(1); break; case 8: doEndTime(1); break; case 9: doFadeDuration(1); break; case 10: doMaxIntensity(1); break; case 11: doStartTime(2); break; case 12: doEndTime(2); break; case 13: doFadeDuration(2); break; case 14: doMaxIntensity(2); break; case 15: doStartTime(3); break; case 16: doEndTime(3); break; case 17: doFadeDuration(3); break; case 18: doMaxIntensity(3); break; case 19: setHour(&hour, minute, second, dayOfWeek, dayOfMonth, month, year); break; case 20: setMinute(hour, &minute, second, dayOfWeek, dayOfMonth, month, year); break; } } void doMainMenu(int minCounter, int oldMinCounter, byte hour, byte minute, byte second, int oneVal, int twoVal, int threeVal, int fourVal) { //main screen turn on!!! if (minCounter > oldMinCounter){ lcd.clear(); } lcd.setCursor(0,0); printHMS(hour, minute, second); lcd.setCursor(0,1); lcd.print(oneVal); lcd.setCursor(4,1); lcd.print(twoVal); lcd.setCursor(8,1); lcd.print(threeVal); lcd.setCursor(12,1); lcd.print(fourVal); //debugging function to use the select button to advance the timer by 1 minute //if(select.uniquePress()){setDate(second, minute+1, hour, dayOfWeek, dayOfMonth, month, year);} } void doOverride(byte second) { //Manual Override Menu lcd.setCursor(0,0); lcd.print("Manual Overrides"); lcd.setCursor(0,1); lcd.print("All: "); if(select.uniquePress()){ if(menuSelect < 3){menuSelect++;} else{menuSelect = 0;} bklTime = millis(); } if(menuSelect == 0){ lcd.print("Timer"); override = false;} if(menuSelect == 1){ lcd.print("ON "); overpercent = 100; override = true;} if(menuSelect == 2){ lcd.print("OFF "); overpercent = 0; override = true;} if(menuSelect == 3){ override = true; lcd.print(overpercent,DEC); lcd.print("% "); if (overpercent < 100) { if (checkButtonAction(&plus)) { overpercent++; bklTime = millis(); } } if (overpercent > 0) { if (checkButtonAction(&minus)) { overpercent--; bklTime = millis(); } } } } void doStartTime(int val) { //set start time lcd.setCursor(0,0); lcd.print("Channel "); lcd.print(val+1); lcd.print(" Start"); lcd.setCursor(0,1); printMins(channel[val].StartMins, true); if (channel[val].StartMins < 1440) { if (checkButtonAction(&plus)) { channel[val].StartMins++; if (channel[val].PhotoPeriod > 0) { channel[val].PhotoPeriod--; } else { channel[val].PhotoPeriod = 1439; } bklTime = millis(); } } if (channel[val].StartMins > 0) { if (checkButtonAction(&minus)) { channel[val].StartMins--; if (channel[val].PhotoPeriod < 1439) { channel[val].PhotoPeriod++; } else { channel[val].PhotoPeriod=0; } bklTime = millis(); } } } void doEndTime(int val) { //set end time lcd.setCursor(0,0); lcd.print("Channel "); lcd.print(val+1); lcd.print(" End"); lcd.setCursor(0,1); printMins(channel[val].StartMins+channel[val].PhotoPeriod, true); if (checkButtonAction(&plus)) { if(channel[val].PhotoPeriod < 1439){ channel[val].PhotoPeriod++; } else { channel[val].PhotoPeriod=0; } bklTime = millis(); } if (checkButtonAction(&minus)) { if(channel[val].PhotoPeriod > 0){ channel[val].PhotoPeriod--; } else { channel[val].PhotoPeriod = 1439; } bklTime = millis(); } } void doFadeDuration(int val) { //set fade duration lcd.setCursor(0,0); lcd.print("Channel "); lcd.print(val+1); lcd.print(" Fade"); lcd.setCursor(0,1); printMins(channel[val].FadeDuration, false); if (channel[val].FadeDuration < channel[val].PhotoPeriod/2 || channel[val].FadeDuration == 0) { if (checkButtonAction(&plus)) { channel[val].FadeDuration++; bklTime = millis(); } } if (channel[val].FadeDuration > 1) { if (checkButtonAction(&minus)) { channel[val].FadeDuration--; bklTime = millis(); } } } void doMaxIntensity(int val) { //set intensity lcd.setCursor(0,0); lcd.print("Channel "); lcd.print(val+1); lcd.print(" Max"); lcd.setCursor(1,1); lcd.print(channel[val].Max); if (channel[val].Max < 100) { if (checkButtonAction(&plus)) { lcd.clear(); channel[val].Max++; bklTime = millis(); } } if (channel[val].Max > 0) { if (checkButtonAction(&minus)) { lcd.clear(); channel[val].Max--; bklTime = millis(); } } } void setHour(byte *hour, byte minute, byte second, byte dayOfWeek, byte dayOfMonth, byte month, byte year) { //set hours lcd.setCursor(0,0); lcd.print("Set Time: Hrs"); lcd.setCursor(0,1); printHMS(*hour, minute, second); if (checkButtonAction(&plus)) { if (*hour < 23) { (*hour)++; } else { *hour = 0; } bklTime = millis(); } if (checkButtonAction(&minus)) { if (*hour > 0) { (*hour)--; } else { *hour = 23; } bklTime = millis(); } setDate(second, minute, *hour, dayOfWeek, dayOfMonth, month, year); } void setMinute(byte hour, byte *minute, byte second, byte dayOfWeek, byte dayOfMonth, byte month, byte year) { //set minutes lcd.setCursor(0,0); lcd.print("Set Time: Mins"); lcd.setCursor(0,1); printHMS(hour, *minute, second); if (checkButtonAction(&plus)) { if (*minute < 59) { (*minute)++; } else { *minute = 0; } bklTime = millis(); } if (checkButtonAction(&minus)) { if (*minute > 0) { (*minute)--; } else { *minute = 59; } bklTime = millis(); } setDate(second, *minute, hour, dayOfWeek, dayOfMonth, month, year); }