LED fish tank lighting is extremely expensive due to patent licensing issues and can cost upwards of $2000 for a 4ft tank. When we first installed our 4ft tank the lighting was provided by a cheap Chinese LED strip light. The strip light was always quite dim and over the past year has started burning out due to being married with the wrong powersupply. This project idea was to build a quality replacement light.
Fish tank lighting requires specific wavelengths to keep fish healthy. More extreme requirements are placed on those building reef tanks and growing coral or plants. All the LED lighting on the market share a few things in common, they are made of a selection of specific high brightness LED modules and modules are actively cooled to ensure long life. Many controllers also have a sunrise / sunset controllers to ensure fish aren’t shocked by sudden changes in lighting levels. The circuit for this fishtank light is made mostly from off the shelf components. Custom electronics are only used for controlling the fans and the sunrise/sunset controller.
The components are as follows:
- 1x 240VAC – 48VDC 250W powersupply. 48VDC chosen as it provides a good mix of being able to power many series LEDs while still being safe to work with.
- 4x Meanwell LDD-700H 700mA LED constant current driver. Capable of driving 12-15 high brightness LEDs in series when powered via 48V. These also include a PWM dimmer input.
- 10x Cree XT-E 3W Royal Blue LEDs
- 10x Cree XP-G 3W Warm White LEDs
- 5x Cree XP-E 3W Red LEDs
- 5x Cree XP-E 3W Green LEDs
- 2x LEDGroupBuy Turquoise LEDs
- 2x LEDGroupBuy Deep Violet 405nm LEDs
- 1x LM317 based linear regulator to provide36V for Fans
- 3x 12V 80mm fans connected in series
- 1x custom made LED controller (see below).
Electrically the fishtank lighting is quite simple. The 48V supply powers the LED Constant Current Drivers, which in turn power all the LEDs of a specific colour in series. An LM317 steps down the 48V to 36V to power 3 12V fans connected in series, one in the control box, and two in the light fitting. A small controller connects to each of the LED drivers and controls LED dimming. Of more interest is the mechanical construction. Ultrabright LED modules generate heat and must be attached to a heatsink to avoid burning out. The LEDs are laid out on 3 aluminium angle rails. The angle rail acts like a fin on a heatsink increasing surface area and improving cooling. They are separated by pieces of wood. Internally in the light fitting the wood and angles form 3 channels to direct airflow to move across the aluminium providing cooling. In the centre of the light 2 fans are mounted in a raised section. The raised section allows the air pressure to equalise and allows the air to flow more evenly into the channels and ensures each channel gets an even amount of airflow. The control box … is a Tupperware container. Nothing more needs to be said.
To control the LEDs I wanted a controller that would transition between three states, Day, Night, and Off and would allow me to set a custom brightness for each of the LEDs in Day and Night mode. It also had to have adjustable times and an internal clock. Amazingly enough I managed to scrape all this together from various spare parts. The controller board is made of the following:
- Atmel ATMEGA88 microcontroller. An unremarkable 8bit microcontroller loaded with features which go unused. Of note is that every single pin on this microcontroller ended up being used, to the point where I had to remove a button on the controller just to free up a pin.
- A 128×64 TFT LCD. Directly addressable as 2x 64×64 arrays. This screen looks great but uses an incredible amount of I/O on the microcontroller.
- A Dallas DS3231 temperature compensated Real Time clock (More on that in the timekeeping section)
- Another LM317 taking power from the 36V input and dropping it to 5V for the rest of the circuit.
The software runs multiple state machines with timed interrupts generating the dimming output and keeping track of time while the main program loop checks which state we’re in, checks if the time has changed and checks if any buttons have been pressed.
One of the 8 bit timers is used to control the dimming of the LEDs. Timer2 was chosen as it has the highest interrupt priority. The code executes nearly 8000 times per second and counts to 16. The main program will keep track of 4 variables for the desired output of each of the LEDs, these are stored as a number between 0 and 15. If the current count in timer 2 is less than the desired output variable, the output is on, if it’s greater the output is turned off. This way we create a 500Hz Pulse-Width-Modulated signal where the pulse width is controlled by a number between 0 and 15. Our main code loop simply needs to set this number.
Timekeeping is taken care of by Timer1, a 16bit Timer on the microcontroller. Initially the timer was to keep track of time using the ATMEGA88’s internal 8MHz clock. However after an overnight test the clock gained 7 minutes in it’s first night and 1minute in it’s second night which kills any hope of accurate long term time keeping. One option was to attach an external 32.768kHz crystal oscillator to the microcontroller, however the pins XTAL1 and XTAL2 were already in use and I couldn’t spare them as I wouldn’t have been left with enough buttons to control the interface. In my parts bin I found a DS3231 temperature compensated Real Time Clock. This very part is designed for very accurate timekeeping, with an internal 32.768kHz crystal and internal temperature measurement and compensation to ensure the crystal is accurate to within 2 ppm. Unfortunately the normal way of communicating with this device is via SPI, and the SDA and SDL pins on the microcontroller were also already in use. Fortunately the DS3231 can also output it’s accurate clock on one of the pins, which I connected to T1, Timer1’s external reference input. Eurika! Timer1 now executes accurately 32768 times per second. The code in timekeeping counts the 32.768kHz ticks for 30 seconds and then increments a counter. All timekeeping in the microcontroller is done from this counter called “halfmincount” and everything is referenced in the number of 30seconds which have passed since midnight. This leads to some very simple maths:
- If halfmincount >2879, then reset it to zero (a new day has started).
- Halfmincount ÷ 120 = the hour of the day. Integer division always rounds down which is a nice problem I exploited for convenience.
- Halfmincount – (120 × hour of day) = the minute of the hour.
- Adjusting the minutes in the code is as easy as adding or subtracting 2 to halfmincount.
- Adjusting the hours in the code is as easy as adding or subtracting 120 to halfmincount.
Main State Machine
The main state machine checks halfmincount against a set of preset values.
- If halfmincount is less than the day value then the state machine is OFF.
- else if halfmincount is less than the night value then the state machine is in DAY.
- else if halfmincount is less than the off value then the state machine is in NIGHT.
- otherwise the state machine is again OFF.
The state machine is checked every time halfmincount changes value. The code is only executed every 30 seconds because firstly the states can’t change any faster than that, and secondly it frees up the CPU to handle button presses. In each of the states, the appropriate pre-set value for the red, green, blue and white desired outputs are copied into the variables read by Timer2. i.e. in the OFF state r_out = g_out = b_out = w_out = 0; Additionally every time the halfmincount changes the code also checks to see if we are within 8 minutes of a state change. If we are within 8 minutes the output will be governed by an equation depending on the state. This allows us to smoothly transition the states so rather than the light coming on suddenly during the day, it’ll be brought on gradually during an 8 minute period. The equations are as follows:
- OFF – DAY : (desired_output_day × (halfmincounter-halfminday)) ÷ 16
- DAY – NIGHT: (((desired_output_day – desired_out_night) × (17 – (halfmincounter – halfminnight))) ÷ 16) + desired_output_night
- NIGHT – OFF: (desired_output_night × (17 – (halfmincounter – halfminoff))) ÷ 16
Finally the state machine also checks to see if any of the override conditions are present. Override conditions are forcing the controller into DAY state regardless of the timer, or forcing the controller into the OFF state regardless of the timer.
In the end I have a home made alternative to a $2000 fishtank light. It may not look as polished as the commercial versions, however it is more functional, and comes in at less than 1/10th of the total cost.
This project was completed in April 2014
- LED Fish tank Lights" class="mgop-elements-item-link">