Natural Transition Aquarium Lights

I have always liked aquariums. It’s fascinating to have a piece of a whole different living biome sitting right there in your living room.

But one thing has always bothered me. The lights. At bedtime the lights either trigger off from a timer, or you flip the switch in the back, and with a little “Click” the beautiful ambiance is destroyed. Your fish are immediately plunged into pitch black night, causing a temporary, skittering panic. Can you blame them? They think their sun just blew up.

Fortunately, a very cool DIY solution is easy to make, and cheap. Your fishes will get to enjoy a beautiful aquatic sunset like they surely remember somewhere in their evolutionary past, and you don’t need to sacrifice live plants, grow lights can be included easily. Check out the below video of our 55 gallon. I adjusted the transition time to 60 seconds so that you can see a full “day”. The night lights at the start and end are a bit dark to show up on the camera, but they look much cooler in person!


Of course, there are some off-the-shelf solutions. For example, these you can find at your local pet store. But they are not grow lights, and only have a night time setting, no transitions. Nope.

These ones are a little closer. For $70, it has both grow lights and RGB lights, with a IR remote control. But as far as I can tell, you just press the button and it goes to whatever setting, there is no transition. The transition was the whole point for me, plus there are only a couple bad reviews. Nah, we can do better!

Lets Make it! What do I need?

Surprisingly little! The total cost will come to about $80 (for a 55 gallon). Here is the BOM:

  1. (1) RGB LED Strip (weather proof) – $16
  2. (1) Cool White LED Strip (weather proof) – $20
  3. (1) Arduino Pro Mini (or similar) – $10
  4. (4) N Channel MOSFET – $7
  5. (1) 10K Linear Potentiometer – $1
  6. (4) 10k Ohm Resistors – $1
  7. (1) 12 Volt Power Supply (3A or more) – $25

Hook it up!

The electrical hook up is pretty straightforward. Refer to the below Fritzing diagram.

Natural Aquarium Lights_bb

Aquarium lighting bread board hook-up.

In case you are not familiar with the components, let’s briefly discuss each one individually.

RGB LED strip: Strip of RGB (Red-Green-Blue) LED lights. RGB LED’s are very cool. Each individual LED is actually 3 different LED’s one Red, Green, and Blue. By varying the brightness of each color (using PWM – see micro controller section), you can create almost any color imaginable, just the same as if you were mixing watercolor paints. Usually you need to consider things like current limiting resistors, but these strips have already taken care of that, all you need is to apply 12 Volts. These strips are “common cathode”. That means that you apply 12 volts to one wire (the black one) and all the red, green, and blue wires need to be attached to ground to create their respective colors.

Cool White LED Strip: Very similar to the RGB led strip. Only apply 12 volts and ground and it will light. This strip was included solely for plant growth, as it has a 10,000 Kelvin lighting temperature. You may have noticed at the store you can buy cool or warm white bulbs. Warm is around 3,000 K, and cool is typically around 6,000 K. The higher Kelvin ratings are generally better for plant growth.

Arduino Pro Mini: The arduino is the brains of the operation. it’s keeps time, and adjusts output based on that. The pins that control the LED’s are capable of Pulse Width Modulation (PWM). PWM is exactly what it sounds like. To control the LED brightness, you turn them on and off very quickly. The higher the % of time spent on vs off, the longer the pulse width, the brighter the light. Cool! Note that not all arduino pins are capable of PWM, that’s why we used the pins we did.

N-Channel MOSFET: Metal-Oxide Semiconducting Field-Effect Transitor. MOSFET. Whew! Think of it like a switch on your wall, they are the muscle of the operation. It’s purpose is to switch high voltage/current loads that the arduino is incapable of doing on it’s own (it’s designed to be a sensitive brain, not a strong muscle). Not only that, but MOSFET’s can switch at very high speeds like we need for PWM.  There are 3 pins. One of them is listening to the arduino signal. The other two are connected (current can flow) when the signal goes high, but otherwise disconnected. Be aware that there are also P-Channel MOSFET’s which function similarly, but would not work as replacements here.

10K Linear Potentiometer: A potentiometer is a variable resistor. You can change it’s resistance value linearly from 0 up to 10k Ohms. In this case, it will be a user control that will set the time of day, as we rotate.

10K Ohm Resistors: These resistors are needed as good practice for wiring. They are called pull-down resistors. Their job is to make sure that if the arduino signal wires are disconnected, the MOSFETs do not switch on and off randomly. 

12 Volt Power Supply: Takes the AC in your wall and makes it 12 Volt DC. Needs to be able to handle the current required by the LED’s. If you have a smaller aquarium, and fewer lights you can get by with less power.

Load the Code.

My favorite part! Connect the arduino via USB and load the following code. There is a variable called transitionTime, set it to something like 10000, or 20000 milliseconds (10-20 seconds) in the beginning so that you can quickly see if you like the transitions speeds and colors. I still think they need tweaking a little. Later you can set it to a full day (86,400,000 ms/day). We will discuss how it works, and how to change colors in the next section.

//This code will adjust an RGB LED strip, and a white LED strip to fade colors between night, sunrise, full, sunset, and back to night.
//potTime (the potentiometer value) sets the current time of the day. Adjust it to show off to your friends, who may or may not be interested at all.

  #define whitePin 9
  #define redPin 3
  #define greenPin 5
  #define bluePin 6
  #define daytimePin A0 //analog input for potentiometer.
  float dayTime=0;
  long currentTime=0;
  double x1Blue = 0;
  double y1Blue = 0;
  double x2Blue = 0;
  double y2Blue = 0;
  double slopeBlue = 0;
  double interceptBlue = 0;
  double x1Red = 0;
  double y1Red = 0;
  double x2Red = 0;
  double y2Red = 0;
  double slopeRed = 0;
  double interceptRed = 0;
  long transitionTime=86400000; //this variable stores the cycle time in milliseconds. It determines how long your day cycle is (good for testing a full day in say 10 sec!)
  int whiteVal=0; //These values are the 8 bit (0-255) values PWM values to write to your MOSFETS/Lights
  int redVal=0;
  int greenVal=0;
  int blueVal=0;
  float potTime = 0;
  double adjustTime = 0; //variable for changes to the potentiometer

void setup (){
  pinMode(whitePin, OUTPUT); //set pin outputss.
  pinMode(redPin, OUTPUT);
  pinMode(greenPin, OUTPUT);
  pinMode(bluePin, OUTPUT);
  analogWrite(bluePin, 0);
  analogWrite(whitePin, 0);
  analogWrite(redPin, 0);
  analogWrite(greenPin, 0);

void loop(){
   if (dayTime -10 > analogRead(daytimePin) || dayTime +10 < analogRead(daytimePin)) { //this if statement monitors the state of the potentiometer "daytime". There is a 10 count cushion on each side because I found that less could make the colors jump randomly.
   // spontaneously when the potentiometer appeared to be stuck between values.
    dayTime = analogRead(daytimePin);
    potTime = dayTime / 1023; //turn the potentiometer value into a ratio of the total.
    adjustTime = potTime*transitionTime; //multiply the tranisition time by the ratio to get the new current time
    currentTime = adjustTime;
  // Night Time!
   if (currentTime <= .15 * transitionTime){ //set the values for night time moon lights
     analogWrite(bluePin, 7);
     analogWrite(whitePin, 2);
     analogWrite(redPin, 2);
     analogWrite(greenPin, 0);

   // Sunrise! Purple-Orange-Yellow
       if (currentTime >= .15 * transitionTime && currentTime <= .225 * transitionTime){
        blueVal = Fade(.15 * transitionTime, .225 * transitionTime, 10, 200, currentTime);
        redVal = Fade(.15 * transitionTime, .225 * transitionTime, 2, 100, currentTime);
        analogWrite(bluePin, blueVal);
        analogWrite(redPin, redVal);
       if (currentTime >= .225 * transitionTime && currentTime <= .3 * transitionTime){
        blueVal = Fade(.225 * transitionTime, .30 * transitionTime, 200, 0, currentTime);
        greenVal = Fade(.225 * transitionTime, .30 * transitionTime, 0, 104, currentTime);
        redVal = Fade(.225 * transitionTime, .30 * transitionTime, 100, 255, currentTime);
        analogWrite(bluePin, blueVal);
        analogWrite(greenPin, greenVal);
        analogWrite(redPin, redVal);
      if (currentTime >= .15 * transitionTime && currentTime <= .3 * transitionTime){
        whiteVal= Fade(.15 * transitionTime, .30 * transitionTime, 10, 200, currentTime);
        analogWrite(whitePin, whiteVal);
   //Full Lights!
     if (currentTime >= .3 * transitionTime && currentTime <= .4 * transitionTime){
        whiteVal= Fade(.30 * transitionTime, .40 * transitionTime, 200, 255, currentTime);
        greenVal = Fade(.30 * transitionTime, .40 * transitionTime, 104, 220, currentTime);
        blueVal = Fade(.3 * transitionTime, .40 * transitionTime, 0, 100, currentTime);
        analogWrite(whitePin, whiteVal);
        analogWrite(greenPin, greenVal);
        analogWrite(bluePin, blueVal);
    //Evening Lights! Yellow
    if (currentTime >= .7 * transitionTime && currentTime <= .775 * transitionTime){
      whiteVal= Fade(.7 * transitionTime, .775 * transitionTime, 255, 180, currentTime);
      blueVal = Fade(.7 * transitionTime, .775 * transitionTime, 100, 0, currentTime);
      greenVal = Fade(.7 * transitionTime, .775 * transitionTime, 220, 150, currentTime);
      analogWrite(whitePin, whiteVal);
      analogWrite(greenPin, greenVal);
      analogWrite(bluePin, blueVal);
     if (currentTime >= .775 * transitionTime && currentTime <= .85 * transitionTime){
      greenVal = Fade(.775 * transitionTime, .85 * transitionTime, 150, 100, currentTime);
      whiteVal= Fade(.775 * transitionTime, .85 * transitionTime, 180, 60, currentTime);
      analogWrite(greenPin, greenVal);
      analogWrite(whitePin, whiteVal);

     if (currentTime >= .85 * transitionTime && currentTime <= .9 * transitionTime){
     greenVal = Fade(.85 * transitionTime, .9 * transitionTime, 100, 0, currentTime);
     whiteVal= Fade(.85 * transitionTime, .9 * transitionTime, 60, 2, currentTime);
     blueVal = Fade(.85 * transitionTime, .9 * transitionTime, 0, 7, currentTime);
     redVal = Fade(.85 * transitionTime, .9 * transitionTime, 255, 2, currentTime);
     analogWrite(greenPin, greenVal);
     analogWrite(whitePin, whiteVal);
     analogWrite(bluePin, blueVal);
     analogWrite(redPin, redVal);
    currentTime = currentTime + 20; //Every time the loop executes, add 20 ms to current time (from delay below). This is not a terribly precise means of keeping time.
  if (currentTime > transitionTime){
    currentTime = 0; //Reset after cycle
double Fade(double x1, double x2, double y1, double y2, double time){
  double slope = (y2-y1)/(x2-x1);
  double intercept = y1-slope*x1;
  double result = slope*time + intercept;
  return result;

Cool! How Does it Work? 

The Arduino program is really pretty simple. We want to vary the brightness of lights based on time right? Lets say time is X and brightness is Y. We want to go from brightness y1 to y2, in the time period x1 to x2. Sounds like the makings of a line to me. At the very bottom of the code you see a function defined called “Fade”. It’s inputs are x1, x2, y1, y2, and time. The task of Fade is to calculate the equation of a line based on those values, and use the time to output the brightness. “Fade” allows us to adjust the colors based on any start and endpoints, at different rates (slopes) without having to include the bulky text to recalculate the line every time.

The timing is controlled simply by delay(20) and the currentTime variable. This is not ideal. What’s happening is that the program is holding 20 ms every iteration, and the variable currentTime gets increased 20 ms every iteration. The problem is that the program takes time to compute everything. Not a lot, but some, and it’s not included included when the code iterates, so eventually (over a couple days) you will notice the program has begun to lag behind real time a little. I suspect there are other ways around this (any tips anyone? Comments!), but with the potentiometer allowing adjustment, it’s easy enough to fix, and the program is simple.


I’m happy with the results, it adds another aspect of peaceful nature-y feel to the aquarium. So let me know what you think, if you build one yourself, if you see ways to improve my code, etc. I did not mention how to mount them, there are many different possibilities, and it depends a bit on the type of light hood you already have. Thanks for reading!

18 thoughts on “Natural Transition Aquarium Lights

  1. Just wanted to add, if your not going to use the potentiometer, you do need to comment oout the first 7 lines from the loop function with the daytime and daytimepin variables. Not removing if causes the lights to flash and flicker.


  2. Hi, may i use 50K linear potentiometer without any changes in code ? And if i understand it, when the potentiometer will be in position 0k … it’s 0 am and in position 10k it’s 12 pm (0 am) ? thx for answering


    1. Michal, sorry for the late reply, you’ve probably already worked through this…but for any others: Better to use something less than 10K if you have it. The issue has to do with the Analog to Digital Convert ADC that converts the resistance to a digital signal (8 bit = 0-1023). Apparently ADC’s work in part by charging a capacitor. If there is a large resistance in series with it, it will charge more slowly, and the capacitor may not reach full charge before the ADC “reads” it. This is at the limits of my electrical knowledge, but this site is a nice reference.

      Regarding the positions, Yes, you are correct. Basically 0K and 10K are right next to each other on the timeline (like a clock from 0-1023 instead of 0-12), and are night time. But as you adjust counterclockwise, you go backwards in time, clockwise takes you forward.

      Did you get it built?


      1. Not yet, but i want to …. i go trhu many post using some RTC and build in RTC in arduino but this is simple and i want to build it. I just want to use 4W RGBW chips with LM317T current limiter. And how about that 20 ms ? Time is extended by 20ms everyday ? Anyway as i said … i like this simple solutions. Have a nice day and thanks for you reply to my previous comment.


      1. Is it possible to use an Arduino Mega in place of the Arduino Mini Pro? I’ve got everything hooked up correctly I believe, but I’m getting nothing. Tried using the Pot in the V1n pin and the 5V pin. Testing the LEDs with a power supply and those light up so it’s gotta be something in my setup.


      2. Nvermind! I got it working. The breadboard through me off – it was split into 2 segments so I wasn’t powering the LEDs. I moved them down to the line a bit and boom.

        So this light will take over after setting the “time” with the Pot and resume it’s cycle?


  3. This was a very easy to follow guide! I’ve got it up and running as a test and it looks great.

    One thing I’m a bit confused about is on the potentiometer – if I turn it all the way left, it’s 0am / night but if I turn it all the way right, it appears to be 12pm / mid-day. How do I adjust the potentiometer to be at night all the way left and right? Otherwise I would need to always set the time in the morning!

    Also, one thing to note – your visual shows you using A1 for your Potentiometer on your Arduino Pro mini, but in your code you’re using A0. Not a big deal, but wanted to let you know!


  4. Success!! I decided to try 511.5 (half of 1023) for the potTime and it worked! Now the Potentiometer is on a 24 hour cycle – it was only a 12 hour cycle before.

    there is a } missing above your last fade by the way.

    This is sooo cool! Thank you very much for posting this! I’m working on a project where I combine your day/night cycle with an interrupt switch, which will trigger rain and lightning storm. It’s going to take awhile, but I will before to post once it’s done and give credit to you for this portion. Thanks again!


    1. Great! Thanks for the notes on A1/A0 discrepancy I’ll make a note and change that, as well as the missing }. I’m not sure I understand the issue with 511 vs 1023, I would have to think about that for awhile, but at least you got it working. Most any Arduino should be 8 bit (1023) analog inputs I think.

      The rain/lightning storm sounds very cool. Definitely show us what it looks like.


      1. 0-1023 -> 0-10K -> 0-8640000ms …. so its 0-24h ….. where is the problem ? if you put 511.5 there you just have this 12-0-12 ….


  5. Very day-night simulation. On your list bit of code:

    currentTime = currentTime + 20; //Every time the loop executes, add 20 ms to current time (from delay below). This is not a terribly precise means of keeping time.
    if (currentTime > transitionTime){
    currentTime = 0; //Reset after cycle

    Why make it 20 ms and not something like 1 ms and reduce delay to 1ms?


    1. Good question Brandon. I don’t remember why I chose 20 ms as the delay, I think (and bear with me, I’m a mechanical engineer not a programmer!) that the reducing the delay would not improve time accuracy. I might think it could hurt it in fact. The delay is accounted for by adding 20 to the current time. What is not accounted for is the time for the loop to execute – for the Arduino to read the values, calculate things, and send other values. If we were to decrease the loop delay to 1 ms, it would mean the program would be recalculating those values more frequently and adding more processing time which is not accounted for. In fact INCREASING the delay time might improve the timing errors…I just don’t remember why I chose 20 ms…

      Any other thoughts and corrections are welcome here. It’s quite possible I’m completely incorrect.


  6. Hi there,
    I am still a beginner in the arduino world. But it looks good!
    I am looking forward to make this for my own aquarium.

    But why not hooking up something like a DS1307 RTC module to keep the time even when the power is off. So the arduino does not have to count all the milliseconds. And you dont need to adjust the potentiometer now end then.

    (Sorry for my English, im Dutch)


  7. Just loaded this to my Nano, changed pins Red9 Green10 Blue 11, to resemble the another builds schematics That I used. removed code for the Pot. and tested it over a 30second test and works great. Now setting the timing back to default and running a full time test. I am not running a White LED strip only two RGB strips I got cheap on Banggood. I will be wrapping these two strips over PVC tubing and placing inside plastic Drain Gutter, for my daughters new 30 gallon freshwater. Was thinking of running this in a timer to turn on in AM.

    What time would be a good starting point in a dark room, to allow later evening viewing before it sun sets.? How many hours of White does it do? Or will it run continually 24hrs? so no need for a timer?

    The moon light might be a little strong being a newbie to programing what do I change to lesson it and can I adjust it so white is stronger?

    I am not running a white strip with it yet. might add on in later.

    Thanks for the programming, this is so simple and easy to make for a simple fish only freshwater tank. Think each strip cost me under $5 each cnd. and $3 for the NANO, a couple bucks for the resistors and MOSFOT. Cheaper then getting a good Florescent bulb.


  8. Thanks for sharing this! I was looking for a natural sunrise/sunset project for my hermit crab terrarium to stimulate their nocturnal activity a little more naturally 🙂

    I modified the colors and timing a little bit to get it where I wanted. I also connected it to a timed outlet to have it switch on and off each day, so I don’t need to worry about the times shifting gradually over time.!AigZD0VFTrwucUNDu0mpiTDrEZg


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s