E
eww
Guest
By your description this must be the same sketch we have been running except we have the control buttons on the board. I'm glad you got it all sorted out. Who knew we had to be structural engineers, chemists, biologists, electricians, wood/acrylic/glass workers and computer programmers to run a reef tank.
I'll post it Cesar. This program is so much better than the older two channel. One obvious reason is that it has four channels instead of two.
The older one ramped up in ten percent increments (which was very noticeable) and this does one percent. This one also works better for the fading in and out. Right now I have channel one set to come on at 8:00 with a half hour ramp up, channel two at 8:30 with a one hour ramp up and channel three at 9:00 with a one hour ramp up. Channel four comes on at one-o-clock with a half hour ramp up to 100% for only three hours at full power. A full duration of four hours.
The one thing that had me confused about this program as opposed to the old one is that the max duration is the total light cycle including the ramp up and down phase. The old sketch kept the all three lighting cycles separate. Ramp up, max percentage, then ramp down.
Anyway, here is the new sketch. FWIW the values are set for my lights to be on from 8AM until 9PM with a max value of 70%
PHP:/* // Typhon firmware // v0.2 alpha 2010-23-11 // N. Enders, R. Ensminger // v0.9 alpha 2010-25-02 // Revised by B. Duthie // This sketch provides firmware for the Typhon LED controller. // It provides a structure to fade MAX_CHANNELS independent channels of LED lighting // on and off each day, to simulate sunrise and sunset. // // Requires LiquidCrystal, Wire, EEPROM, EEPROMVar libraries. // EEPROMVar is available here: http://www.arduino.cc/playground/uploads/Profiles/EEPROMVar_01.zip // // 02/12/12 to support leds, without use of buttons */ // include the libraries: #include <LiquidCrystal.h> #include <Wire.h> #include <EEPROM.h> #include <EEPROMVar.h> /**** Define Variables & Constants ****/ /**************************************/ // printing to Serial if debugging #define DEBUG_ON // General debugging //#define DEBUG_LED // LED Fade // ONLY INCLUDE THIS IF YOU ARE CHANGING VALUES STORED IN EEPROM //#define RESET_EEPROM // set the RTC's I2C address #define DS1307_I2C_ADDRESS 0x68 // create the LCD //LiquidCrystal lcd(8, 7, 5, 4, 16, 2); // Typhon //int bkl = 6; // backlight pin LiquidCrystal lcd(12, 8, 7, 6, 5, 4, 2); // Duanes int bkl = 13; // backlight pin //create manual override variables int overSelect = 0; // allow the override menu to remember its current setting // 0 = Timer, 1 = All On, 2 = All Off, 3 = % Value int overPercent = 0; // setting of 60, with internal delays, 0-100 percent takes about 15 seconds (per channel) int rampDelayUp = 120; int rampDelayDn = 60; // Variables making use of EEPROM memory: EEPROMVar<int> oneStartMins = 480; // minute to start this channel. EEPROMVar<int> onePhotoPeriod = 780; // photoperiod in minutes for this channel. EEPROMVar<int> oneMin = 0; // min intensity for this channel, as a percentage EEPROMVar<int> oneMax = 70; // max intensity for this channel, as a percentage EEPROMVar<int> oneFadeDuration = 30; // duration of the fade on and off for sunrise and sunset EEPROMVar<int> twoStartMins = 510; EEPROMVar<int> twoPhotoPeriod = 720; EEPROMVar<int> twoMin = 0; // min intensity for this channel, as a percentage EEPROMVar<int> twoMax = 700; EEPROMVar<int> twoFadeDuration = 60; EEPROMVar<int> threeStartMins = 540; EEPROMVar<int> threePhotoPeriod = 660; EEPROMVar<int> threeMin = 0; // min intensity for this channel, as a percentage EEPROMVar<int> threeMax = 170; EEPROMVar<int> threeFadeDuration = 60; EEPROMVar<int> fourStartMins = 720; EEPROMVar<int> fourPhotoPeriod = 240; EEPROMVar<int> fourMin = 0; // min intensity for this channel, as a percentage EEPROMVar<int> fourMax = 100; EEPROMVar<int> fourFadeDuration = 60; // descriptions need to be 4 characters due to display area char* oneDesc = "BR "; char* twoDesc = "RB "; char* threeDesc = "CW "; char* fourDesc = "NW"; #define MAX_CHANNELS 4 typedef struct { char* Desc; // Channel Description int Led; // channel pin int StartMins; // minute to start this channel. int PhotoPeriod; // photoperiod in minutes for this channel. int ledMin; // min intensity for this channel, as a percentage int ledMax; // max intensity for this channel, as a percentage int ledVal; // current intensity for this channel, as a percentage int FadeDuration; // duration of the fade on and off for sunrise and sunset for this channel. int RampDelay; } channelVals_t; channelVals_t channel[MAX_CHANNELS]; // freeMemory returns the amount of available memory for variables extern unsigned int __bss_end; extern unsigned int __heap_start; extern void *__brkval; int freeMemory() { int free_memory; if((int)__brkval == 0) free_memory = ((int)&free_memory) - ((int)&__bss_end); else free_memory = ((int)&free_memory) - ((int)__brkval); return free_memory; } /****** RTC Functions ******/ /***************************/ // Convert decimal numbers to binary coded decimal uint8_t decToBcd(byte val) { return ( (val/10*16) + (val%10) ); } // Convert binary coded decimal to decimal numbers uint8_t 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((uint8_t)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((uint8_t)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()); } /** Menu Control Functions **/ void showMainMenu () { Serial.println(""); Serial.println("LED Control Actions"); Serial.println("L+ All Max"); Serial.println("L- All Min"); Serial.println("Lnnn All nnn%"); Serial.println("LT Timer Mode"); Serial.println(""); } int parseMenuCommand () { int command; int value = 0; int pct = 0; command = Serial.read(); switch (command) { case 'l': case 'L': if (Serial.available() > 0) { value = Serial.read(); if (value == 't' || value == 'T') { overSelect = 0; Serial.println("Setting Timer Mode"); } if (value == '+') { overSelect = 1; Serial.println("All Max"); } if (value == '-') { overSelect = 2; Serial.println("All Min"); } if (value >= '0' && value <= '9') { pct = value - '0'; while (Serial.available() > 0) { value = Serial.read(); if (value >= '0' && value <= '9') pct = pct * 10 + value - '0'; else break; } if (overPercent <= 100) { overSelect = 3; overPercent = pct; Serial.print("Setting "); Serial.print(overPercent); Serial.println("%"); } } } else { // unrecognized command } } Serial.flush(); // remove all incoming data return (0); } /****** 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 c // current channel ) { // channel values, just to make code read a little easier int ledPin = channel[c].Led; // pin for this channel of LEDs int start = channel[c].StartMins; // start time for this channel of LEDs int period = channel[c].PhotoPeriod; // photoperiod for this channel of LEDs int fade = channel[c].FadeDuration; // fade duration for this channel of LEDs int ledMin = channel[c].ledMin; // min value for this channel int ledMax = channel[c].ledMax; // max value for this channel int ledVal = channel[c].ledVal; // current value for this channel int val = ledMin; // start with the assumption that this channel is off int pinVal; // adjust for midnite rollover if (mins < start && start + period > 1439 && mins <= (start + period) % 1440) mins += 1440; //fade up or currently on or currently off if (mins >= start) { if (mins - start <= fade) { // fading up val = map(mins - start, 0, fade, ledMin, ledMax); } else if (mins < start + (period - fade)) { // on val = ledMax; } else if (mins > start + period) { // off - nothing to do, but print debug info } } //fade down if (mins >= start + period - fade && mins <= start + period) { val = map(mins - (start + period - fade), 0, fade, ledMax, ledMin); } // if All On = use ledMax, All Off = use ledMin, otherwise use set percent switch(overSelect){ case 1: // All On val = ledMax; break; case 2: // All Off val = ledMin; break; case 3: // specific percent val = overPercent; break; } // Ramp Up/Down slowly -- do nothing if no change // This will also impact setting of the override % value, and all on/off options if (channel[c].RampDelay > 0) // avoid using delay() in loop() --channel[c].RampDelay; if (channel[c].RampDelay == 0 && val < ledVal) { --ledVal; channel[c].RampDelay = rampDelayDn; } if (channel[c].RampDelay == 0 && val > ledVal) { ++ledVal; channel[c].RampDelay = rampDelayUp; } if (ledVal != channel[c].ledVal) { // ledVal is the percentage (0-100) to set for this channel // pinVal is the value (0-255) to set for the Arduino PWM pin // comment out the line you don't want to use, or create your own // pinVal = map(ledVal, 0, 100, 0, 255); // normal linear value for PWM pinVal = min(255, (pow(2, ledVal/10.0) + 2.99) / 4.0); // value for Meanwell "D" analogWrite(ledPin, pinVal); } #ifdef DEBUG_LED if (ledVal != channel[c].ledVal) { if (start + period > 1440) Serial.println("Midnite rollover "); Serial.print("mins ="); Serial.print(mins); Serial.print(", "); Serial.print(channel[c].Desc); Serial.print(", start = "); Serial.print(start); Serial.print(", period = "); Serial.print(period); Serial.print(", fade = "); Serial.print(fade); Serial.print(", min = "); Serial.print(ledMin); Serial.print(", max = "); Serial.print(ledMax); if(overSelect){ Serial.print(", overSelect: "); Serial.print(val); } Serial.print(", ledVal = "); Serial.print(ledVal); Serial.print(", PWM = "); Serial.print(map(ledVal, 0, 100, 0, 255), DEC); Serial.print(", pin = "); Serial.println(pinVal, DEC); } #endif return ledVal; } /**** Display Functions ****/ void printMDY (byte mm, byte dd, byte yy) { if(mm<10){ lcd.print(" "); } lcd.print(mm, DEC); lcd.print("/"); if(dd<10){ lcd.print(" "); } lcd.print(dd, DEC); lcd.print("/"); if(yy<10){ lcd.print("0"); } lcd.print(2000 + yy, DEC); } void printDay (byte dday) { char* daynames[8] = {"???","Sun","Mon","Tue","Wed","Thu","Fri","Sat"}; lcd.print(daynames[dday%8]); } // format a number of minutes into a readable time (24 hr format) void showMins(int mins, //time in minutes to print boolean ampm = false //print am/pm? - future implementation (or not) ) { 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 showHMS (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); } /**** Setup ****/ /***************/ void setup() { #ifdef RESET_EEPROM oneStartMins = 480; // minute to start this channel. onePhotoPeriod = 780; // photoperiod in minutes for this channel. oneMin = 0; // min intensity for this channel, as a percentage oneMax = 70; // max intensity for this channel, as a percentage oneFadeDuration = 30; // duration of the fade on and off for sunrise and sunset for // this channel. twoStartMins = 510; twoPhotoPeriod = 720; twoMin = 0; // min intensity for this channel, as a percentage twoMax = 70; twoFadeDuration = 60; threeStartMins = 540; threePhotoPeriod = 660; threeMin = 0; // min intensity for this channel, as a percentage threeMax = 70; threeFadeDuration = 60; fourStartMins = 720; fourPhotoPeriod = 600; fourMin = 0; // min intensity for this channel, as a percentage fourMax = 100; fourFadeDuration = 30; #endif // Initialize channel variables. Set LED channel pin and retrieve values from EEPROM channel[0].Desc = oneDesc; channel[0].Led = 11; channel[0].StartMins = oneStartMins; channel[0].PhotoPeriod = 780; // onePhotoPeriod; channel[0].ledMin = oneMin; channel[0].ledMax = oneMax; channel[0].ledVal = 0; channel[0].FadeDuration = oneFadeDuration; channel[1].Desc = twoDesc; channel[1].Led = 10; channel[1].StartMins = twoStartMins; channel[1].PhotoPeriod = 720; //twoPhotoPeriod; channel[1].ledMin = twoMin; channel[1].ledMax = twoMax; channel[1].ledVal = 0; channel[1].FadeDuration = twoFadeDuration; channel[2].Desc = threeDesc; channel[2].Led = 9; channel[2].StartMins = threeStartMins; channel[2].PhotoPeriod = 660; //threePhotoPeriod; channel[2].ledMin = threeMin; channel[2].ledMax = threeMax; channel[2].ledVal = 0; channel[2].FadeDuration = threeFadeDuration; channel[3].Desc = fourDesc; channel[3].Led = 3; channel[3].StartMins = fourStartMins; channel[3].PhotoPeriod = fourPhotoPeriod; channel[3].ledMin = fourMin; channel[3].ledMax = fourMax; channel[3].ledVal = 0; channel[3].FadeDuration = fourFadeDuration; Serial.begin(57600); Serial.println("Duanes-Reef"); for (int i = 0; i < MAX_CHANNELS; ++i) { Serial.print(channel[i].Desc); Serial.print(", Start: "); Serial.print(channel[i].StartMins); Serial.print(", Period: "); Serial.print(channel[i].PhotoPeriod); Serial.print(", Min: "); Serial.print(channel[i].ledMin, DEC); Serial.print(", Max: "); Serial.print(channel[i].ledMax, DEC); Serial.print(", Fade: "); Serial.print(channel[i].FadeDuration); Serial.println(); } Wire.begin(); pinMode(bkl, OUTPUT); // backlight control for the lcd display lcd.begin(16, 2); digitalWrite(bkl, HIGH); lcd.print("Duanes-Reef"); byte second, minute, hour, dayOfWeek, dayOfMonth, month, year; getDate(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year); lcd.setCursor(0,1); printMDY(month, dayOfMonth, year); lcd.print(" - "); printDay(dayOfWeek); delay(5000); lcd.clear(); updateEEPromVars(); Serial.print("Free Memory = "); Serial.println(freeMemory()); showMainMenu(); } /***** Loop *****/ /****************/ void loop() { byte second, minute, hour, dayOfWeek, dayOfMonth, month, year; int minCounter; getDate(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year); minCounter = hour * 60 + minute; if (Serial.available() > 0) { parseMenuCommand(); showMainMenu(); } for (int i = 0; i < MAX_CHANNELS; i++) { //check & adjust 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; } // set led outputs for (int i = 0; i < MAX_CHANNELS; i++) channel[i].ledVal = setLed(minCounter, i); doMainMenu(hour, minute, second); } void doMainMenu(byte hour, byte minute, byte second) { lcd.setCursor(0,0); showHMS(hour, minute, second); // display the current intensity for each led channel showChannelValues(); //debugging function to use the select button to advance the timer by 1 minute #ifdef DEBUG_ON if (0) { byte dayOfWeek, dayOfMonth, month, year; getDate(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year); ++minute; if (minute > 59) { minute = 0; ++hour; } if (hour > 23) hour = 0; setDate(second, minute+1, hour, dayOfWeek, dayOfMonth, month, year); delay(100); } #endif DEBUG_ON } void doOverride() { //Manual Override Menu lcd.setCursor(0,0); switch (overSelect){ case 0: lcd.print("Control by Timer"); break; case 1: lcd.print("LED: All Max "); break; case 2: lcd.print("LED: All Min "); break; case 3: lcd.print("All Set: "); if (overPercent < 10) lcd.print(" "); else if (overPercent < 100) lcd.print(" "); lcd.print(overPercent,DEC); lcd.print("% "); break; default: overSelect = 0; } // showChannelValues(); // disabled so that changes only start after menu is selected again } void showChannelValues() { // display the current intensity for each led channel lcd.setCursor(9,0); switch (overSelect) { case 0: lcd.print("Timer "); break; case 1: lcd.print("All Max"); break; case 2: lcd.print("All Min"); break; case 3: lcd.print("All "); if (overPercent < 10) lcd.print(" "); lcd.print(overPercent); lcd.print("%"); break; } lcd.setCursor(0,1); for (int i = 0; i < MAX_CHANNELS; i++) { lcd.print(" "); if (channel[i].ledVal < 10) lcd.print(" "); else if (channel[i].ledVal < 100) lcd.print(" "); lcd.print(channel[i].ledVal); } } // Actually needs to become a feature of the EEPromVar library -- later // should just be a function of the assignment operators void updateEEPromVars() { // Update EEProms with any values that may have changed // Only updates values that have changed #ifdef DEBUG_ON Serial.println("Updating EEPromVars"); #endif if (channel[0].StartMins != oneStartMins) { oneStartMins = channel[0].StartMins; } if (channel[0].PhotoPeriod != onePhotoPeriod) { onePhotoPeriod = channel[0].PhotoPeriod; } if (channel[0].ledMin != oneMin) { oneMin = channel[0].ledMin; } if (channel[0].ledMax != oneMax) { oneMax = channel[0].ledMax; } 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].ledMin != twoMin) { twoMax = channel[1].ledMax; } if (channel[1].ledMax != twoMax) { twoMax = channel[1].ledMax; } 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].ledMin != threeMin) { threeMin = channel[2].ledMin; } if (channel[2].ledMax != threeMax) { threeMax = channel[2].ledMax; } 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].ledMin != fourMin) { fourMin = channel[3].ledMin; } if (channel[3].ledMax != fourMax) { fourMax = channel[3].ledMax; } if (channel[3].FadeDuration != fourFadeDuration) { fourFadeDuration = channel[3].FadeDuration; } }