aquarium light controller

So I’m building an aquarium light controller for a family member. Now while I’ve done lots of smaller projects for myself, I’ve never worked on a project that’s intended to run 24/7. The application is for controlling DIY LED lights.The LED’s use a constant current power supply that is adjustable via 0-10V input.

So I built a shield that uses one of the PWM outputs, runs it through a simple RC filter circuit and an opamp to achieve 0-10V. There is a button to provide a manual override of the lights and an RTC module to provide time for the lighting schedule.

Through software I’ve implemented adjustable sunrise/sunset timing, the lighting schedule, brightness control, manual override, and a soft start feature for the LED when going in and out of manual override mode. I consider myself an amateur with very little programming experience. So I’d like to post the code that I’ve come up with (which through all my testing works as intended) in order to have some of you who are more experience comment on it.

I’m looking to see if I could have done things different, or more efficient, or used better coding/programming conventions. While this sketch is small, it needs to run reliably 24/7. And I’m thinking if I learn better coding skills now, it’ll help in the long run with larger, more complicated sketches. I’ve done my best to comment as much as possible to show what the code does.

Thanks in advance for any hints, tips, help, or criticism you may provide.

/* Aquarium Light Controller

Written by John Dimo

Sets turn on time, turn off time, power loss failsafe, adjustable sunset/sunrise
times, smooth fading LED on/off, override button, max brightness control,etc.

Arduino Pin 6 is the output for lights.
Arduino Pin 12 is for the override button.
Arduino Pins A0 and A1 are for the RTC module.

Version 1.0 - Initial Version. Basic light timer, with adjustable 
              ramp up/down and total running time.
Version 1.1 - Added code for LCD display to show diagnostic messages.
Version 1.2 - Added adjustable max brightness control.
Version 1.3 - Removed all the delays so that code can loop through and do other 
              stuff during sunrise and sunset timing code.
Version 1.4 - Added Override Button to turn lights on and off.
Version 1.5 - Added RTC module. Now has daily adjustable start time.
Version 1.6 - Added alarm for daily adjustable off time. Added failsafe to restart
              lights in the event of power loss.
Version 1.7 - Added smooth on/off for the lights when using override button or
              when recovering from power loss.

*////////////////////////////////////////////////////////////////////
// user adjustable timing
int rampTime = 10; //Time in minutes to turn on/off the lights
int maxBrightness = 75; //Brightness in percentage
byte alarmOn[] = {8, 15}; // set the hour and minutes to start and stop the
byte alarmOff[] = {15, 15}; // timer, must use 24 hour time format
//////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////
/////// Change nothing below here !!! ////////////////////////////
//////////////////////////////////////////////////////////////////
#include <Time.h>  
#include <Wire.h>  
#include <DS1307RTC.h>

// global variables
int stage = 0; // set initial timing stage
int buttonState;
int lastbuttonState = HIGH;
unsigned long oldButtontime = 0;
unsigned long oldRamptime = 0;
int lightOutput = 0;
int lightSetpoint = 0;
int overridelights = LOW;

//setup loop
void setup() {
  
  ///////////////////////////////
  // Seteup some pins to power the RTC. Will not be needed when building the board
  pinMode(A3, OUTPUT); 
  digitalWrite(A3, HIGH);
  pinMode(A2, OUTPUT);
  digitalWrite(A2, LOW);
  ////////////////////////////////////////////
  
  setSyncProvider(RTC.get); // tell code to get the time from RTC
  pinMode(6, OUTPUT); //setup the output pin
  pinMode(12, INPUT); // setup the input button
  pinMode(13, OUTPUT); //temp diagnostic LED
  maxBrightness = map(maxBrightness,0,100,0,255); // Map percentage to output range.
  rampTime = rampTime * 60000 / maxBrightness; // Setup ramptime delay based on max brightness setting
  
  //setup fail safe to restart lights during lights ON stage.
  if (word(hour(), minute()) >= word(alarmOn[0],alarmOn[1])) { 
    if (word(hour(), minute()) <= word(alarmOff[0],alarmOff[1])){
      lightSetpoint = maxBrightness;
      stage = 2;
    }
  }
}

//main program loop
void loop() {
  unsigned long currentTime = millis(); //set current time
  
  //override button code. ignores if you hold button down. handles hardware debounce
  int reading = digitalRead(12); 
  if (reading != lastbuttonState) { 
    oldButtontime = currentTime;
  }
  if (currentTime - oldButtontime > 50) {
    if (reading != buttonState) {
      buttonState = reading;
      if (buttonState == LOW) {
        //This is the part of code that does something when button is pressed
        overridelights = !overridelights; // toggle the override
      }
    }
  }
  lastbuttonState = reading; // update the state of the button
  
  //Diagnostic LED to if we're in manual override mode or not.
  if (overridelights == HIGH) digitalWrite(13, HIGH); 
  if (overridelights == LOW) digitalWrite(13, LOW);
  
  // Stage 0. Waiting for alarm that starts lighting cycle
  if (stage == 0 && (hour() == alarmOn[0]) && (minute() == alarmOn[1])) stage = 1;
    
  // Stage 1. Sunrise Code.
  if (stage == 1 && currentTime - oldRamptime > rampTime) {
    oldRamptime = currentTime;
    lightSetpoint++;
    if (lightSetpoint == maxBrightness) stage = 2;
  }
  
  // Stage 2. Waiting for alarm that stops lighting cycle.
  if (stage == 2 && (hour() == alarmOff[0]) && (minute() == alarmOff[1])) {
    stage = 3;
  }
  
  // Stage 3. Sunset Code.
  if (stage == 3 && currentTime - oldRamptime > rampTime) {
    oldRamptime = currentTime;
    lightSetpoint--;
    if (lightSetpoint == 0) stage = 0;
  }
  
  //During lights ON stages, override turns off lights
  if (stage != 0 && overridelights == HIGH) {
    for(lightOutput; lightOutput >= 0; lightOutput--) {
      analogWrite(6, lightOutput);
      delay(4);
    }
    lightOutput = 0;
  }
  
  //During lights ON stages, set lights to current setpoint
  if (stage != 0 && overridelights == LOW) {
    if(lightOutput < lightSetpoint) {
      for(lightOutput; lightOutput <= lightSetpoint; lightOutput++) {
        analogWrite(6, lightOutput);
        delay(4);
      }
    }
    if(lightOutput > lightSetpoint) {
      for(lightOutput; lightOutput >= lightSetpoint; lightOutput--) {
        analogWrite(6, lightOutput);
        delay(4);
      }
    }
  }
  
  // During lights OFF stage, overries will turn on the lights.
  if (stage == 0 && overridelights == HIGH) {
    for(lightOutput; lightOutput <= maxBrightness; lightOutput++) {
      analogWrite(6, lightOutput);
      delay(4);
    }
  }
  
  // During lights OFF stage, turn off lights.
  if (stage == 0 && overridelights == LOW) {
    for(lightOutput; lightOutput >= 0; lightOutput--) {
      analogWrite(6, lightOutput);
      delay(4);
    }
    lightOutput = 0;
  }
}

Code looks great! If it works well, I’d say youre 99% there. The rest is tweaks. Try adding an LCD shield such as :

That would allow you to make some menus to scroll thru to set different parameters and show them.

Take a look at my thermostat code. Its very similar:

vulture2600:
Code looks great! If it works well, I'd say youre 99% there. The rest is tweaks. Try adding an LCD shield such as :
LCD Shield Kit w/ 16x2 Character Display - Only 2 pins used! [BLUE AND WHITE] : ID 772 : $19.95 : Adafruit Industries, Unique & fun DIY electronics and kits

That would allow you to make some menus to scroll thru to set different parameters and show them.

Take a look at my thermostat code. Its very similar:

Thermostat RTC v4.0 - Pastebin.com

I did have LCD code in there before for diagnostic purposes, but have since taken it all out. I do see some sort of LCD option with a meny system in the future for on the fly adjustments though.

Although it works as it is and you have included very descriptive comments about each section of code I would like to see descriptive names for the various pins rather than the anonymous pin number such as         analogWrite(6, lightOutput);My personal preference would also be to put each opening and closing brace on its own line to make code blocks more obvious.

Neither of these suggestions affect the operation of the code but could make understanding and modifying it easier.

Hello, your code is much better than mine at this point!

When you say user adjustable lighting, this is done via code, right?

If I want to change the light schedule in my, I have to access the code, change the values, and upload it, in your code it would be the same by changing the values:

byte alarmOn[] = {8, 15};
byte alarmOff[] = {15, 15};

I'm thinking of using an external programmable timer to feed an analog input do the arduino knows it is day or night time, while I can't solve the problem of storing the on and off times after I change them using 3 buttons to access a simple menu.

Any thoughts on that?

Here's my suggestion, you can use 6 potentiometers to set the 6 values (start hr, start min, stop hour, stop min, brightness and ramp duration), and easily set the desired values.

You can then use ReadAnalogVoltage and Round function to set the values. At least this is what I am going to try.

I really hate to bump an old thread, but I've tried to add a moonlight function to this code, and it does not seem to work correctly. Am I completely missing something?!

Thanks !

/* Aquarium Light Controller

Written by John Dimo

Sets turn on time, turn off time, power loss failsafe, adjustable sunset/sunrise
times, smooth fading LED on/off, override button, max brightness control,etc.

Arduino Pin 6 is the output for lights.
Arduino Pin 12 is for the override button.
Arduino Pins A0 and A1 are for the RTC module.

Version 1.0 - Initial Version. Basic light timer, with adjustable 
              ramp up/down and total running time.
Version 1.1 - Added code for LCD display to show diagnostic messages.
Version 1.2 - Added adjustable max brightness control.
Version 1.3 - Removed all the delays so that code can loop through and do other 
              stuff during sunrise and sunset timing code.
Version 1.4 - Added Override Button to turn lights on and off.
Version 1.5 - Added RTC module. Now has daily adjustable start time.
Version 1.6 - Added alarm for daily adjustable off time. Added failsafe to restart
              lights in the event of power loss.
Version 1.7 - Added smooth on/off for the lights when using override button or
              when recovering from power loss.

*////////////////////////////////////////////////////////////////////
// user adjustable timing
int rampTime = 20; //Time in minutes to turn on/off the lights
int maxBrightness = 80; //Brightness in percentage
byte alarmOn[] = {7, 40}; // set the hour and minutes to start and stop the
byte alarmOff[] = {19, 40}; // timer, must use 24 hour time format
byte moonOn[] = {21, 40};
byte moonOff[] = {6, 00};
//////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////
/////// Change nothing below here !!! ////////////////////////////
//////////////////////////////////////////////////////////////////
#include <Time.h>  
#include <Wire.h>  
#include <DS1307RTC.h>

// global variables
int stage = 0; // set initial timing stage
int buttonState;
int lastbuttonState = HIGH;
unsigned long oldButtontime = 0;
unsigned long oldRamptime = 0;
int lightOutput = 0;
int lightSetpoint = 0;
int moonlightSetpoint = 0;
int overridelights = LOW;

//setup loop
void setup() {
  Serial.begin(9600);
  Serial.println("AquaLight Controller");
  ///////////////////////////////
  // Seteup some pins to power the RTC. Will not be needed when building the board
  pinMode(A3, OUTPUT); 
  digitalWrite(A3, HIGH);
  pinMode(A2, OUTPUT);
  digitalWrite(A2, LOW);
  ////////////////////////////////////////////
  
  setSyncProvider(RTC.get); // tell code to get the time from RTC
  pinMode(5, OUTPUT); //setup the output pin -- Sun
  pinMode(6, OUTPUT); //setup the output pin -- Moon
  pinMode(7, INPUT); // setup the input button
  pinMode(13, OUTPUT); //temp diagnostic LED
  maxBrightness = map(maxBrightness,0,100,0,255); // Map percentage to output range.
  rampTime = rampTime * 60000 / maxBrightness; // Setup ramptime delay based on max brightness setting
  
  //setup fail safe to restart lights during lights ON stage.
  if (word(hour(), minute()) >= word(alarmOn[0],alarmOn[1]))
  { 
    if (word(hour(), minute()) <= word(alarmOff[0],alarmOff[1]))
    {
      lightSetpoint = maxBrightness;
      stage = 2;
    }
  }

  if (word(hour(), minute()) >= word(moonOn[0],moonOn[1]))
  { 
    if (word(hour(), minute()) <= word(moonOff[0],moonOff[1]))
    {
      moonlightSetpoint = maxBrightness;
      stage = 4;
    }
  }

}


//main program loop
void loop()
{
  unsigned long currentTime = millis(); //set current time
  
  //override button code. ignores if you hold button down. handles hardware debounce
  int reading = digitalRead(7); 
  if (reading != lastbuttonState)
  { 
    oldButtontime = currentTime;
  }
  if (currentTime - oldButtontime > 50)
  {
    if (reading != buttonState)
    {
      buttonState = reading;
      if (buttonState == LOW)
      {
        //This is the part of code that does something when button is pressed
        overridelights = !overridelights; // toggle the override
      }
    }
  }
  
  lastbuttonState = reading; // update the state of the button
  
  //Diagnostic LED to if we're in manual override mode or not.
  if (overridelights == HIGH) digitalWrite(13, HIGH); 
  if (overridelights == LOW) digitalWrite(13, LOW);
  
  // Stage 0. Waiting for alarm that starts lighting cycle
  if (stage == 0 && (hour() == alarmOn[0]) && (minute() == alarmOn[1])) stage = 1;
    
  // Stage 1. Sunrise Code.
  if (stage == 1 && currentTime - oldRamptime > rampTime)
  {
    oldRamptime = currentTime;
    lightSetpoint++;
    if (lightSetpoint == maxBrightness) stage = 2;
    Serial.println("Entering stage 2");
  }
  
  // Stage 2. Waiting for alarm that stops lighting cycle.
  if (stage == 2 && (hour() == alarmOff[0]) && (minute() == alarmOff[1])) stage = 3;
    Serial.println("Entering stage 3");
  
  // Stage 3. Sunset Code.
  if (stage == 3 && currentTime - oldRamptime > rampTime)
  {
    oldRamptime = currentTime;
    lightSetpoint--;
    if (lightSetpoint == 0) stage = 4;
    Serial.println("Entering stage 4");
  }

  // Stage 4. Moonlight waiting
  if (stage == 4 && (hour() == moonOn[0]) && (minute() == moonOn[1])) stage = 5;
    Serial.println("Entering stage 5");

  // Stage 5. Moonlight ramp up
  if (stage == 5 && currentTime - oldRamptime > rampTime)
  {
    oldRamptime = currentTime;
    moonlightSetpoint++;
    if (moonlightSetpoint == maxBrightness) stage = 6;
    Serial.println("Entering stage 6");
  }

  // Stage 6. Moonlight, waiting for sunrise.
  if (stage == 6 && (hour() == moonOff[0]) && (minute() == moonOff[1])) stage = 7;
    Serial.println("Entering stage 7");

  // Stage 7. Moonlight ramp down
  if (stage == 7 && currentTime - oldRamptime > rampTime)
  {
    oldRamptime = currentTime;
    moonlightSetpoint--;
    if (moonlightSetpoint == 0) stage = 0;
    Serial.println("Going back to stage 0");
  }



  
  //During lights ON stages, override turns off lights
  if (stage != 0 && overridelights == HIGH)
  {
    for(lightOutput; lightOutput >= 0; lightOutput--)
    {
      analogWrite(2, lightOutput);
      delay(4);
    }
    lightOutput = 0;
  }
  
  //During lights ON stages, set lights to current setpoint
  if (stage != 0 && overridelights == LOW)
  {
    if(lightOutput < lightSetpoint)
    {
      for(lightOutput; lightOutput <= lightSetpoint; lightOutput++)
      {
        analogWrite(5, lightOutput);
        delay(4);
      }
    }
    if(lightOutput > lightSetpoint)
    {
      for(lightOutput; lightOutput >= lightSetpoint; lightOutput--)
      {
        analogWrite(5, lightOutput);
        delay(4);
      }
    }

    // Moonlight 
    if(lightOutput < moonlightSetpoint)
    {
      for(lightOutput; lightOutput <= moonlightSetpoint; lightOutput++)
      {
        analogWrite(6, lightOutput);
        delay(4);
      }
    }
    if(lightOutput > moonlightSetpoint)
    {
      for(lightOutput; lightOutput >= moonlightSetpoint; lightOutput--)
      {
        analogWrite(6, lightOutput);
        delay(4);
      }
    }
  }
  
  // During lights OFF stage, overries will turn on the lights.
  if (stage == 0 && overridelights == HIGH) {
    for(lightOutput; lightOutput <= maxBrightness; lightOutput++) {
      analogWrite(5, lightOutput);
      delay(4);
    }
  }
  
  // During lights OFF stage, turn off lights.
  if (stage == 0 && overridelights == LOW) {
    for(lightOutput; lightOutput >= 0; lightOutput--) {
      analogWrite(5, lightOutput);
      delay(4);
    }
    lightOutput = 0;
  }
}

Am I completely missing something?!

Yes. "and it does not seem to work correctly" doesn't tell us squat.

You added some hardware, and some code. Now the code does something. The hardware does something. You expect the code to do something. You expect the hardware to do something.

But, you failed to mention what hardware you added, what code you changed, and what any of those somethings are.

I apologize for my ignorance.

I am using an Pro Micro, which I program using the Leonardo board with the IDE.

As for what I've done, hardware wise, is just add an additional LED to one of the PWM pins. I am using pin 5 for daylight, and pin 6 for moonlight. For right now, I am just using a blue LED with a 150ohm resistor. When I'll put it into use, the PWM pin will drive a Meanwell LDD LED driver.

It is breadboarded, with a DS1307 on battery backup, button on pin 7, held low with a 10k resistor.

As I said in the previous post, I am trying to add moonlight to the code. I tried to duplicate John's method of stages with if statements, but for some reason it does not trigger the moonlight ramp up. I've added serial messages and it streams going through all stages, back to 0, so it's looping fine.

What's going on is that the moonlight stage isn't activating. I've tested the RTC with the read example, and it reads fine after complete power removal. (battery backup) I've adjusted the values in the code to activate the moonlight within 5 minutes of programming, and it still doesn't activate. I also tried adding a new if statement after the stages, with moonlight only variables, thinking there would be some issues.

Here is the current code I'm fooling with:

/* Aquarium Light Controller
Written by John Dimo
Sets turn on time, turn off time, power loss failsafe, adjustable sunset/sunrise
times, smooth fading LED on/off, override button, max brightness control,etc.
Arduino Pin 6 is the output for lights.
Arduino Pin 12 is for the override button.
Arduino Pins A0 and A1 are for the RTC module.

Version 1.0 - Initial Version. Basic light timer, with adjustable 
              ramp up/down and total running time.
Version 1.1 - Added code for LCD display to show diagnostic messages.
Version 1.2 - Added adjustable max brightness control.
Version 1.3 - Removed all the delays so that code can loop through and do other 
              stuff during sunrise and sunset timing code.
Version 1.4 - Added Override Button to turn lights on and off.
Version 1.5 - Added RTC module. Now has daily adjustable start time.
Version 1.6 - Added alarm for daily adjustable off time. Added failsafe to restart
              lights in the event of power loss.
Version 1.7 - Added smooth on/off for the lights when using override button or
              when recovering from power loss.

*////////////////////////////////////////////////////////////////////
// user adjustable timing
int rampTime = 20; //Time in minutes to turn on/off the lights
int maxBrightness = 80; //Brightness in percentage
byte alarmOn[] = {7, 40}; // set the hour and minutes to start and stop the
byte alarmOff[] = {19, 40}; // timer, must use 24 hour time format
byte moonOn[] = {0, 42};
byte moonOff[] = {6, 0};
//////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////
/////// Change nothing below here !!! ////////////////////////////
//////////////////////////////////////////////////////////////////
#include <Time.h>  
#include <Wire.h>  
#include <DS1307RTC.h>

// global variables
int stage = 0; // set initial timing stage
int buttonState;
int lastbuttonState = HIGH;
unsigned long oldButtontime = 0;
unsigned long oldRamptime = 0;
int lightOutput = 0;
int lightSetpoint = 0;
int moonlightOutput = 0;
int moonlightSetpoint = 0;
int overridelights = LOW;

//setup loop
void setup()
{
  Serial.begin(9600);
  Serial.println("AquaLight Controller");
  ///////////////////////////////
  // Seteup some pins to power the RTC. Will not be needed when building the board
  pinMode(A3, OUTPUT); 
  digitalWrite(A3, HIGH);
  pinMode(A2, OUTPUT);
  digitalWrite(A2, LOW);
  ////////////////////////////////////////////
  
  setSyncProvider(RTC.get); // tell code to get the time from RTC
  pinMode(5, OUTPUT); //setup the output pin -- Sun
  pinMode(6, OUTPUT); //setup the output pin -- Moon
  pinMode(7, INPUT); // setup the daylight input button
  pinMode(8, INPUT); //setup the moonlight input button
  pinMode(13, OUTPUT); //temp diagnostic LED
  maxBrightness = map(maxBrightness,0,100,0,255); // Map percentage to output range.
  rampTime = rampTime * 60000 / maxBrightness; // Setup ramptime delay based on max brightness setting
  
  //setup fail safe to restart lights during lights ON stage.
  if (word(hour(), minute()) >= word(alarmOn[0],alarmOn[1]))
  { 
    if (word(hour(), minute()) <= word(alarmOff[0],alarmOff[1]))
    {
      lightSetpoint = maxBrightness;
      stage = 2;
    }
  }
}

//main program loop
void loop()
{
  unsigned long currentTime = millis(); //set current time
  
  //override button code. ignores if you hold button down. handles hardware debounce
  int reading = digitalRead(7); 
  if (reading != lastbuttonState)
  { 
    oldButtontime = currentTime;
  }
  if (currentTime - oldButtontime > 50)
  {
    if (reading != buttonState)
    {
      buttonState = reading;
      if (buttonState == LOW)
      {
        //This is the part of code that does something when button is pressed
        overridelights = !overridelights; // toggle the override
      }
    }
  }
  lastbuttonState = reading; // update the state of the button
  
  //Diagnostic LED to if we're in manual override mode or not.
  if (overridelights == HIGH) digitalWrite(13, HIGH); 
  if (overridelights == LOW) digitalWrite(13, LOW);

  Serial.println("Entering stage 0");
  
  // Stage 0. Waiting for alarm that starts lighting cycle
  if (stage == 0 && (hour() == alarmOn[0]) && (minute() == alarmOn[1])) stage = 1;

  Serial.println("Entering stage 1");
    
  // Stage 1. Sunrise Code.
  if (stage == 1 && currentTime - oldRamptime > rampTime)
  {
    oldRamptime = currentTime;
    lightSetpoint++;
    if (lightSetpoint == maxBrightness) stage = 2;
  }

  Serial.println("Entering stage 2");
  
  // Stage 2. Waiting for alarm that stops lighting cycle.
  if (stage == 2 && (hour() == alarmOff[0]) && (minute() == alarmOff[1]))
  {
    stage = 3;
  }
  
  Serial.println("Entering stage 3");
  
  // Stage 3. Sunset Code.
  if (stage == 3 && currentTime - oldRamptime > rampTime)
  {
    oldRamptime = currentTime;
    lightSetpoint--;
    if (lightSetpoint == 0) stage = 4;
  }

  Serial.println("Entering stage 4");

  // Stage 4. Moonlight waiting
  if (stage == 4 && (hour() == moonOn[0]) && (minute() == moonOn[1]))
  {
    stage = 5;
  }
  
  
  Serial.println("Entering stage 5");


  // Stage 5. Moonlight ramp up
  if (stage == 5 && currentTime - oldRamptime > rampTime)
  {
    oldRamptime = currentTime;
    moonlightSetpoint++;
    if (moonlightSetpoint == maxBrightness) stage = 6;
  }

  Serial.println("Entering stage 6");

  // Stage 6. Moonlight, waiting for sunrise.
  if (stage == 6 && (hour() == moonOff[0]) && (minute() == moonOff[1]))
  {
    stage = 7;
  }

  Serial.println("Entering stage 7");

  // Stage 7. Moonlight ramp down
  if (stage == 7 && currentTime - oldRamptime > rampTime)
  {
    oldRamptime = currentTime;
    moonlightSetpoint--;
    if (moonlightSetpoint == 0) stage = 0;
  }

  Serial.println("Going back to stage 0");

  
  //During lights ON stages, override turns off lights
  if (stage != 0 && overridelights == HIGH)
  {
    for(lightOutput; lightOutput >= 0; lightOutput--)
    {
      analogWrite(5, lightOutput);
      delay(4);
    }
    lightOutput = 0;
  }
  
  //During lights ON stages, set lights to current setpoint
  if (stage != 0 && overridelights == LOW)
  {
    if(lightOutput < lightSetpoint)
    {
      for(lightOutput; lightOutput <= lightSetpoint; lightOutput++)
      {
        analogWrite(5, lightOutput);
        delay(4);
      }
    }
    if(lightOutput > lightSetpoint)
    {
      for(lightOutput; lightOutput >= lightSetpoint; lightOutput--)
      {
        analogWrite(5, lightOutput);
        delay(4);
      }
    }
  }


  //Moonlight
  if (stage != 0 && overridelights == LOW)
  {
    if(moonlightOutput < moonlightSetpoint)
    {
      for(moonlightOutput; moonlightOutput <= moonlightSetpoint; moonlightOutput++)
      {
        analogWrite(6, moonlightOutput);
        delay(4);
      }
    }
    if(moonlightOutput > moonlightSetpoint)
    {
      for(moonlightOutput; moonlightOutput >= moonlightSetpoint; moonlightOutput--)
      {
        analogWrite(6, moonlightOutput);
        delay(4);
      }
    }
  }
  
  // During lights OFF stage, overries will turn on the lights.
  if (stage == 0 && overridelights == HIGH) {
    for(lightOutput; lightOutput <= maxBrightness; lightOutput++) {
      analogWrite(5, lightOutput);
      delay(4);
    }
  }
  
  // During lights OFF stage, turn off lights.
  if (stage == 0 && overridelights == LOW) {
    for(lightOutput; lightOutput >= 0; lightOutput--) {
      analogWrite(5, lightOutput);
      delay(4);
    }
    lightOutput = 0;
  }
}

ominously:
I apologize for my ignorance.

I am using an Pro Micro, which I program using the Leonardo board with the IDE.

...

It is breadboarded, with a DS1307 on battery backup, button on pin 7, held low with a 10k resistor.

Programming a breadboarded microcontroller is fun and all, and possibly you have included hardware for reading serial output from the Pro, for debugging.

However, while in the development and debugging phase, might I suggest using the Leonardo or some other arduino with USB/Serial hardware on it?

The Arduino hardware and IDE is about supplying us developers with the tools for efficient development and debugging via Serial monitor.

Myself I started out with Arduino IDE, then thinking it was a bit limited, I installed Atmel Studio and started programming "naked" atmega328p's in the breadboard, using an Arduino Nano as programmer, etc. However I soon came to realize that as I had no serial output from the uc on the breadboard, debugging became very hard. I considered adding another Nano for reading serial data from the breadboarded uc, but as I also came to realize how the Arduino libraries are a dream to work with, I dropped that.

Arduino is great for prototyping. If size matters, or the finished solution should be a bit more permanent than a bunch of wires sticking out of an Arduino, then the Nano is great, being PCB compatible, plus having that USB/Serial hardware that allows you to program it directly from the IDE, with full Serial Monitor support.

  Serial.println("Entering stage 0");
 
  // Stage 0. Waiting for alarm that starts lighting cycle
  if (stage == 0 && (hour() == alarmOn[0]) && (minute() == alarmOn[1])) stage = 1;

That will print "Entering stage 0" regardless of what the stage is or will become. It is, therefore, a pointless statement.

It is also why putting multiple statements on one line is a pain in the ass.

If the code had been:

if (stage == 0 && (hour() == alarmOn[0]) && (minute() == alarmOn[1]))
{
   stage = 1;
}

like it should have been, it would be easy to see where to put a statement to print out the fact that the stage was changing to 1.

Similarly, none of the rest of the Serial.print() statements that claim to be dealing with entering stages mean anything. So, in spite of your best efforts, you've really learned nothing about what is actually happening in the program.

So, might I suggest that the "Going back to stage 0" statement be replaced with one that shows what the actual stage is?

The block of code labeled // Moonlight does NOT deal with stage = 6. It deals with all stages that are not 0. But that was already done by the code that precedes it.

Rupert909:
Programming a breadboarded microcontroller is fun and all, and possibly you have included hardware for reading serial output from the Pro, for debugging.

However, while in the development and debugging phase, might I suggest using the Leonardo or some other arduino with USB/Serial hardware on it?

The Arduino hardware and IDE is about supplying us developers with the tools for efficient development and debugging via Serial monitor.

Myself I started out with Arduino IDE, then thinking it was a bit limited, I installed Atmel Studio and started programming "naked" atmega328p's in the breadboard, using an Arduino Nano as programmer, etc. However I soon came to realize that as I had no serial output from the uc on the breadboard, debugging became very hard. I considered adding another Nano for reading serial data from the breadboarded uc, but as I also came to realize how the Arduino libraries are a dream to work with, I dropped that.

Arduino is great for prototyping. If size matters, or the finished solution should be a bit more permanent than a bunch of wires sticking out of an Arduino, then the Nano is great, being PCB compatible, plus having that USB/Serial hardware that allows you to program it directly from the IDE, with full Serial Monitor support.

Ah, perhaps I didn't make myself clear. The board is based off of the Atmega32u4. It has pro micro on written on the silkscreen, and does have built in serial. I had stated that I use the Leonardo to program it with the IDE. This is because they use the same exact chip. This is just a smaller version.

PaulS:

  Serial.println("Entering stage 0");

// Stage 0. Waiting for alarm that starts lighting cycle
  if (stage == 0 && (hour() == alarmOn[0]) && (minute() == alarmOn[1])) stage = 1;



That will print "Entering stage 0" regardless of what the stage is or will become. It is, therefore, a pointless statement.

It is also why putting multiple statements on one line is a pain in the ass.

If the code had been:


if (stage == 0 && (hour() == alarmOn[0]) && (minute() == alarmOn[1]))
{
  stage = 1;
}



like it should have been, it would be easy to see where to put a statement to print out the fact that the stage was changing to 1.

Similarly, none of the rest of the Serial.print() statements that claim to be dealing with entering stages mean anything. So, in spite of your best efforts, you've really learned nothing about what is actually happening in the program.

So, might I suggest that the "Going back to stage 0" statement be replaced with one that shows what the actual stage is?

The block of code labeled // Moonlight does NOT deal with stage = 6. It deals with all stages that are not 0. But that was already done by the code that precedes it.

I definitely see where you're coming from. I tried adding the braces, thinking that was the problem. Ended up giving me more problems than I thought. I am by no means a programmer/software guy. I am more of a hardware repair person.

I read over your advice, and moved the serial print lines back into the braces. I did that so I could at least see something going on, because there was a few times where I didn't see anything going on at all.

I was thinking I could just replace the && with a || in this/these statements:

if (stage == 4 && (hour() == moonOn[0]) && (minute() == moonOn[1]))

to this

if (stage == 4 || (hour() == moonOn[0]) && (minute() == moonOn[1]))

First, I removed the "stage == 4" and started fading up on the moonlight LED as it's supposed to, strangely.

It is currently under testing

I appreciate both of your replies. I have been racking my brain with this for a while now. Thanks for your time.

Update:

Finally got it working correctly. Had to fool with the if statements on the bottom that were decreasing the setpoint/light output. With the original sketch, it would ramp down just fine and completely turn off the LED.

However, with the modifications I made, even after taking a fresh copy of John’s code and re-modifying it all over again, the LED would start flashing like crazy as soon as it was supposed to be off. I set the time to 1 minute, and adjust the times to trigger alarms 2 minutes apart.

I’d watch the LED ramp up, stay lit, and then dim back down. As soon as it would appear to transition from a PWM value of 1 to 0, it would go berserk. At first I thought it was the board I was using, so I switched to an Arduino Nano. Same result. After some time, I went and added an if statement towards the bottom, where if the lightOutput would == 1, it would analogWrite 0 to the appropriate pin. After doing this, the LEDs no longer go berserk.

I commented out the overrides, and the failsafe. I will uncomment and continue get the code working as it originally did, with new functionality.

Pin assignments are for the Nano. I’ll try the Pro Micro soon.

/* Aquarium Light Controller
Written by John Dimo

Sets turn on time, turn off time, power loss failsafe, adjustable sunset/sunrise
times, smooth fading LED on/off, override button, max brightness control,etc.

Arduino Pin 9 is the output for day lights.
Arduino Pin 10 is the output for moon lights.
Arduino Pin 12 is for the override button.
Arduino Pins A0 and A1 are for the RTC module.
*////////////////////////////////////////////////////////////////////
// user adjustable timing
int rampTime = 1; //Time in minutes to turn on/off the lights
int maxBrightness = 90; //Brightness in percentage
byte alarmOn[] = {0, 30}; // set the hour and minutes to start and stop the
byte alarmOff[] = {0, 32}; // timer, must use 24 hour time format
byte moonOn[] = {0, 34};
byte moonOff[] = {0, 36};
//////////////////////////////////////////////////////////////////
/////// Change nothing below here !!! ////////////////////////////
//////////////////////////////////////////////////////////////////
#include <Time.h>  
#include <Wire.h>  
#include <DS1307RTC.h>

// global variables
int stage = 0; // set initial timing stage
int buttonState;
int lastbuttonState = HIGH;
unsigned long oldButtontime = 0;
unsigned long oldRamptime = 0;
int lightOutput = 0;
int moonlightOutput = 0;
int lightSetpoint = 0;
int moonlightSetpoint = 0;
int overridelights = LOW;

//setup loop
void setup()
{
  Serial.begin(9600);
  ///////////////////////////////
  // Seteup some pins to power the RTC. Will not be needed when building the board
//  pinMode(A3, OUTPUT); 
//  digitalWrite(A3, HIGH);
//  pinMode(A2, OUTPUT);
//  digitalWrite(A2, LOW);
  ////////////////////////////////////////////
  
  setSyncProvider(RTC.get); // tell code to get the time from RTC
  pinMode(9, OUTPUT); //setup the output pin -- Day
  pinMode(10, OUTPUT); //setup the output pin -- Moon
  pinMode(7, INPUT); // setup the input button
  pinMode(13, OUTPUT); //temp diagnostic LED
  maxBrightness = map(maxBrightness,0,100,0,255); // Map percentage to output range.
  rampTime = rampTime * 60000 / maxBrightness; // Setup ramptime delay based on max brightness setting
  
  //setup fail safe to restart lights during lights ON stage.
//  if (word(hour(), minute()) >= word(alarmOn[0],alarmOn[1]))
//  { 
//    if (word(hour(), minute()) <= word(alarmOff[0],alarmOff[1]))
//    {
//      lightSetpoint = maxBrightness;
//      stage = 2;
//    }
//  }
}

//main program loop
void loop()
{
  unsigned long currentTime = millis(); //set current time
  
//  //override button code. ignores if you hold button down. handles hardware debounce
//  int reading = digitalRead(7); 
//  if (reading != lastbuttonState)
//  { 
//    oldButtontime = currentTime;
//  }
//  if (currentTime - oldButtontime > 50)
//  {
//    if (reading != buttonState)
//    {
//      buttonState = reading;
//      if (buttonState == LOW)
//      {
//        //This is the part of code that does something when button is pressed
//        overridelights = !overridelights; // toggle the override
//      }
//    }
//  }
//  lastbuttonState = reading; // update the state of the button
//  
//  //Diagnostic LED to if we're in manual override mode or not.
//  if (overridelights == HIGH) digitalWrite(13, HIGH); 
//  if (overridelights == LOW) digitalWrite(13, LOW);

  
  // Stage 0. Waiting for alarm that starts lighting cycle
  if (stage == 0 && (hour() == alarmOn[0]) && (minute() == alarmOn[1]))
  {
    stage = 1;
    Serial.println("Entering stage 1");
  }



  // Stage 1. Sunrise Code.
  if (stage == 1 && currentTime - oldRamptime > rampTime)
  {
    oldRamptime = currentTime;
    lightSetpoint++;
    if (lightSetpoint == maxBrightness)
    {
      stage = 2;
      Serial.println("Entering stage 2");
    }
  }



  // Stage 2. Waiting for alarm that stops lighting cycle.
  if (stage == 2 && (hour() == alarmOff[0]) && (minute() == alarmOff[1]))
  {
    stage = 3;
    Serial.println("Entering stage 3");
  }



  // Stage 3. Sunset Code.
  if (stage == 3 && currentTime - oldRamptime > rampTime)
  {
    oldRamptime = currentTime;
    lightSetpoint--;
    if (lightSetpoint == 0)
    {
      stage = 4;
      Serial.println("Entering stage 4");
    }
  }


  // Stage 4. Waiting for alarm to start moon cycle
  if (stage == 4 && (hour() == moonOn[0]) && (minute() == moonOn[1]))
  {
    stage = 5;
    Serial.println("Entering stage 5");
  }



  // Stage 5. Moonrise Code.
  if (stage == 5 && currentTime - oldRamptime > rampTime)
  {
    oldRamptime = currentTime;
    moonlightSetpoint++;
    if (moonlightSetpoint == maxBrightness)
    {
      stage = 6;
      Serial.println("Entering stage 6");
    }
  }



  // Stage 6. Waiting for alarm that stops lighting cycle.
  if (stage == 6 && (hour() == moonOff[0]) && (minute() == moonOff[1]))
  {
    stage = 7;
    Serial.println("Entering stage 7");
  }



  // Stage 7. Moonset Code.
  if (stage == 7 && currentTime - oldRamptime > rampTime)
  {
    oldRamptime = currentTime;
    moonlightSetpoint--;
    if (moonlightSetpoint == 0)
    {
      stage = 0;
      Serial.println("Going back to stage 0");
    }
  }


  Serial.print(hour());
  Serial.print(":");
  Serial.println(minute());
  
  //During lights ON stages, override turns off lights
//  if (stage != 0 && overridelights == HIGH)
//  {
//    for(lightOutput; lightOutput >= 0; lightOutput--)
//    {
//      analogWrite(9, lightOutput);
//      delay(4);
//    }
//    lightOutput = 0;
//  }
  
  //During lights ON stages, set lights to current setpoint
  if (stage != 0 && overridelights == LOW)
  {
    if(lightOutput < lightSetpoint)
    {
      for(lightOutput; lightOutput <= lightSetpoint; lightOutput++)
      {
        analogWrite(9, lightOutput);
        delay(4);
      }
    }
    if(lightOutput > lightSetpoint)
    {
      for(lightOutput; lightOutput > lightSetpoint; lightOutput--)
      {
        analogWrite(9, lightOutput);
        if(lightOutput == 1)
        {
          analogWrite(9, 0);
        }
        delay(4);
        Serial.println(lightOutput);
      }
    }
    if(moonlightOutput < moonlightSetpoint)
    {
      for(moonlightOutput; moonlightOutput <= moonlightSetpoint; moonlightOutput++)
      {
        analogWrite(10, moonlightOutput);
        delay(4);
      }
    }
    if(moonlightOutput > moonlightSetpoint)
    {
      for(moonlightOutput; moonlightOutput > moonlightSetpoint; moonlightOutput--)
      {
        analogWrite(10, moonlightOutput);
        if(moonlightOutput == 1)
        {
          analogWrite(10, 0);
        }
        delay(4);
        Serial.println(moonlightOutput);
      }
    }
  }


  
//  // During lights OFF stage, overries will turn on the lights.
//  if (stage == 0 && overridelights == HIGH)
//  {
//    for(lightOutput; lightOutput <= maxBrightness; lightOutput++)
//    {
//      analogWrite(9, lightOutput);
//      delay(4);
//    }
//  }
  
  // During lights OFF stage, turn off lights.
  if (stage == 0 && overridelights == LOW)
  {
    for(lightOutput; lightOutput >= 0; lightOutput--)
    {
      analogWrite(9, lightOutput);
      delay(4);
    }
    lightOutput = 0;
    for(moonlightOutput; moonlightOutput >= 0; moonlightOutput--)
    {
      analogWrite(10, moonlightOutput);
      delay(4);
    }
    moonlightOutput = 0;
  }
}