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;
}
}