# Fluval Edge 6G Cree LED and Arduino Controller



## Chiumanfu (Oct 30, 2014)

Just wanted to share my modded Fluval Edge 6G with CREE lighting. I found that with the lighting in the Edge, you really need to extend past the opening to get light to spread to the corners. I decided try to DIY this without losing too much of the "edgeiness" and still look stock.

On this tank I do 1/2 EI dosing regime and weekly 50% water changes. Standard Excel daily dose except I do not do the big dose after water changes as they suggest. The tank has been running for about 4 months now.

Full tank shot at PWM level of 180 of a possible 1024. This is the level that I typically leave it at to balance out the algae with my low tech tank and 8 hour photoperiod.









Full tank shot with the stock Fluval LED strapped into it's original position for comparison. Same camera exposure settings as the other shots so you can directly compare the amount of light. The stock setup is pretty lacking. Plants would only grow well in the center.









Full tank shot at full power for the driver. It is really to bright and the fish don't like it. Algae blooms within a couple days under this intensity.









Here is some shots of the DIY light. I used a 12" heatsink and 4 Cree XM-L neutral white LEDs. I probably could have gone with XP-G series LEDs to save a couple bucks but I already had these from another project. Thermal compound is Arctic Silver and the wiring is recycled from an old PC. The stock light was hacked apart with a dremel... it's not pretty but everything is hidden by the lid anyways.
CREE XM-L Neutral White LED - Rapid LED
Drilled/tapped 1.4" x 12" Aluminum Heatsink - Rapid LED













































Here is a shot of the driver. I used the Meanwell LDD-1000H. This driver only has 1 amp max output power which sounds like a waste when you consider the XM-L LEDs can be driven up to 3 amps but the light intensity at 1 amp is already way too high. I have to dim them anyways to keep algae at bay. Also, running XM-L LEDs at 1 amp keeps everything nice and cool. You can touch the LEDs without first degree burns and the heatsink only gets slightly warm to the touch.
Mean Well LDD-1000HW dimmable driver - Rapid LED
If you need more power, Meanwell just released a LDD-1500L which can push 1.5A but only 30V so your limited to a string of about 9 LEDs.
Power comes from a 24V 5A power supply from eBy. The 24V output feeds the LDD-1000H driver (black block) and a small switching power regulator (green pcb) also from eBy. From there I bump the voltage down to 5 volts and power an Arduino Nano V3.0 clone (blue pcb). The Arduino controls the PWM dimming of the light according to the time set in the real time clock module (black pcb).

















Here is the Arduino code. This controller uses high resolution PWM ramping so the transitions are much smoother than simply using the basic analogWrite() function (1024 steps in lighting intensity vs 256 steps). The menu system is pretty simple. Button 1 sets the maximum light level. Button 2 sets the on time. Button 3 sets the off time. Button 4 sets the fade duration. When you are in one of the settings, button one is "up", button 2 is "down", and button 4 is "set".


```
// Aquarium Light Controller by Chiu Fang

// Include Libraries
#include <Wire.h>              // For DS1307 Real Time Clock
#include <DS1307RTC.h>         // For DS1307 Real Time Clock
#include <Time.h>              // For Time functions
#include <LiquidCrystalFast.h> // For 16x1 LCD Display
#include <Keypad.h>            // For Keypad
#include <EEPROMex.h>          // For EEPROM
#include <TimerOne.h>          // For 16-bit PWM

// Set constants
const byte pwmPin = 10;        // PWM light control pin
const byte blPin = 9;          // Backlight pin
                               // Pin A4 ? I2C SDA, Pin A5 ? I2C SCL
LiquidCrystalFast lcd (2, 3, 4, 5, 6, 7, 8); // rs,rw,en1,d4,d5,d6,d7 Init LCD
const byte ROWS = 1; // Keypad one rows
const byte COLS = 4; // Keypad four columns
char keys[ROWS][COLS] = {
  {'1','2','3','4'}
};
byte rowPins[ROWS] = {12}; // Row pinouts of the keypad
byte colPins[COLS] = {15, 14, 17, 16}; // Column pinouts of the keypad (A0, A1, A2, A3)
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS ); // Initialize keypad

// Set Parameter Values
int maxPWM;               // Brightest light level
byte timeOnStart;         // Time that the lights start to ramp on
byte timeOffStart;        // Time that the lights start to ramp off
byte fadeTime;            // Time that the fade takes


// Declare Variables
int valuePWM = 0;
unsigned long blDelay = 0;        // Time that backlight stays on after a key is pressed
unsigned long pwmDelay = 0;       // Time between PWM increment or decrement
unsigned long timer = 0;          // PWM timer
boolean onFlag = false;           // Flag indicates on ramp is happening
boolean offFlag = false;          // Flag indicates off ramp is happening
boolean onFinishFlag = false;     // Flag indicates on ramp is complete
boolean offFinishFlag = false;    // Flag indicates off ramp is complete

char key;              // Buffer for key press

void setup () {
  maxPWM = EEPROM.readInt(0);
  timeOnStart = EEPROM.readByte(2);
  timeOffStart = EEPROM.readByte(3);
  fadeTime = EEPROM.readByte(4);
  
//  Serial.begin(115200);         // For debug
  lcd.begin(8, 2);                // Config LCD
  Timer1.initialize(2000);        // 500Hz PWM
  Timer1.pwm(pwmPin, valuePWM);   // Init high resolution timer
  pinMode(blPin, OUTPUT);         // Set backlight pin mode
}

void loop () {
  
/***** Real Time Clock *****/

  lcd.setCursor(0, 0);
  tmElements_t tm;           // Calls RTC lib
  if (RTC.read(tm)) {        // Read DS1307
    if (tm.Hour >= 0 && tm.Hour < 10) {
      lcd.print('0');
    }
    lcd.print(tm.Hour);
    lcd.print(':');
    if (tm.Minute >= 0 && tm.Minute < 10) {
      lcd.print('0');
    }
    lcd.print(tm.Minute);
    lcd.print(':');
    if (tm.Second >= 0 && tm.Second < 10) {
      lcd.print('0');
    }
    lcd.print(tm.Second);
    lcd.setCursor(0, 1);
    lcd.print(" ->     ");
    lcd.setCursor(4, 1);
    lcd.print(valuePWM);
  } else {
    if (RTC.chipPresent()) {
      lcd.print(F("RTC Stop"));
    } else {
      lcd.print(F("RTC Err "));
    }
  }


/***** Backlight Timer *****/

if ((millis() - blDelay) >= 60000){    // 1 minute
  digitalWrite(blPin, LOW);
}


/***** Lights ON if powered on after timeOnStart *****/

  if (tm.Hour > timeOnStart && tm.Hour < timeOffStart && onFinishFlag == false) {
    valuePWM = maxPWM;
    Timer1.setPwmDuty(pwmPin, valuePWM);
    onFlag = false;
    onFinishFlag = true;
  }


/***** Lights ON *****/
  
  if (tm.Hour == timeOnStart && onFlag == false && onFinishFlag == false) { //tm.Hour
    pwmDelay = (fadeTime * 60000) / maxPWM;
    onFlag = true;
    onFinishFlag = false;
    valuePWM ++;
    Timer1.setPwmDuty(pwmPin, valuePWM);
    timer = millis();
  }

  if (valuePWM == maxPWM && onFinishFlag == false) {
    onFlag = false;
    onFinishFlag = true;
    offFinishFlag = false;
  }

  if (onFlag == true) {
    if ((millis() - timer) >= pwmDelay){
      valuePWM ++;
      Timer1.setPwmDuty(pwmPin, valuePWM);
      timer = millis();
    }
  }


/***** Lights OFF *****/

  if (tm.Hour == timeOffStart && offFlag == false && offFinishFlag == false && onFinishFlag == true) {
    pwmDelay = (fadeTime * 60000) / maxPWM;
    offFlag = true;
    offFinishFlag = false;
    valuePWM --;
    Timer1.setPwmDuty(pwmPin, valuePWM);
    timer = millis();
  }

  if (valuePWM == 0 && offFinishFlag == false){
    offFlag = false;
    offFinishFlag = true;
    onFinishFlag = false;
  }
  
  if (offFlag == true) {
    if ((millis() - timer) >= pwmDelay){
      valuePWM --;
      Timer1.setPwmDuty(pwmPin, valuePWM);
      timer = millis();
    }
  }


/***** Menu System *****/

  key = keypad.getKey();
  if(key) {
    digitalWrite(blPin, HIGH);
    switch(key) {
    
    case '1':
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print(F("Max PWM "));
      lcd.setCursor(0, 1);
      lcd.print(F("=       "));
      lcd.setCursor(2, 1);
      lcd.print(maxPWM);
      do {
        key = keypad.waitForKey();
        if(key == '1' && maxPWM < 1023) {
          maxPWM++;
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print(F("Max PWM "));
          lcd.setCursor(0, 1);
          lcd.print(F("=       "));
          lcd.setCursor(2, 1);
          lcd.print(maxPWM);
        } else if(key == '1' && maxPWM >= 1023) {
          maxPWM = 0;
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print(F("Max PWM "));
          lcd.setCursor(0, 1);
          lcd.print(F("=       "));
          lcd.setCursor(2, 1);
          lcd.print(maxPWM);
        } else if(key == '2' && maxPWM > 0) {
          maxPWM--;
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print(F("Max PWM "));
          lcd.setCursor(0, 1);
          lcd.print(F("=       "));
          lcd.setCursor(2, 1);
          lcd.print(maxPWM);
        } else if(key == '2' && maxPWM == 0) {
          maxPWM = 1023;
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print(F("Max PWM "));
          lcd.setCursor(0, 1);
          lcd.print(F("=       "));
          lcd.setCursor(2, 1);
          lcd.print(maxPWM);
        }
      } while(key != '4');
      EEPROM.updateInt(0,maxPWM);
      valuePWM = maxPWM;
      Timer1.setPwmDuty(pwmPin, valuePWM);
      blDelay = millis();
      break;

    case '2':
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print(F("On Time "));
      lcd.setCursor(0, 1);
      lcd.print(F("=       "));
      lcd.setCursor(2, 1);
      lcd.print(timeOnStart);
      do {
        key = keypad.waitForKey();
        if(key == '1' && timeOnStart < 23) {
          timeOnStart++;
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print(F("On Time "));
          lcd.setCursor(0, 1);
          lcd.print(F("=       "));
          lcd.setCursor(2, 1);
          lcd.print(timeOnStart);
        } else if(key == '1' && timeOnStart == 23) {
          timeOnStart = 0;
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print(F("On Time "));
          lcd.setCursor(0, 1);
          lcd.print(F("=       "));
          lcd.setCursor(2, 1);
          lcd.print(timeOnStart);
        } else if(key == '2' && timeOnStart > 0) {
          timeOnStart--;
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print(F("On Time "));
          lcd.setCursor(0, 1);
          lcd.print(F("=       "));
          lcd.setCursor(2, 1);
          lcd.print(timeOnStart);
        } else if(key == '2' && timeOnStart == 0) {
          timeOnStart = 23;
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print(F("On Time "));
          lcd.setCursor(0, 1);
          lcd.print(F("=       "));
          lcd.setCursor(2, 1);
          lcd.print(timeOnStart);
        }
      } while(key != '4');
      EEPROM.updateByte(2,timeOnStart);
      blDelay = millis();
      break;

    case '3':
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print(F("Off Time"));
      lcd.setCursor(0, 1);
      lcd.print(F(" =      "));
      lcd.setCursor(3, 1);
      lcd.print(timeOffStart);
      do {
        key = keypad.waitForKey();
        if(key == '1' && timeOffStart < 23) {
          timeOffStart++;
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print(F("Off Time"));
          lcd.setCursor(0, 1);
          lcd.print(F(" =      "));
          lcd.setCursor(3, 1);
          lcd.print(timeOffStart);
        }else if(key == '1' && timeOffStart == 23) {
          timeOffStart = 0;
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print(F("Off Time"));
          lcd.setCursor(0, 1);
          lcd.print(F(" =      "));
          lcd.setCursor(3, 1);
          lcd.print(timeOffStart);
        } else if(key == '2' && timeOffStart > 0) {
          timeOffStart--;
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print(F("Off Time"));
          lcd.setCursor(0, 1);
          lcd.print(F(" =      "));
          lcd.setCursor(3, 1);
          lcd.print(timeOffStart);
        } else if(key == '2' && timeOffStart == 0) {
          timeOffStart = 23;
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print(F("Off Time"));
          lcd.setCursor(0, 1);
          lcd.print(F(" =      "));
          lcd.setCursor(3, 1);
          lcd.print(timeOffStart);
        }
      } while(key != '4');
      EEPROM.updateByte(3,timeOffStart);
      blDelay = millis();
      break;
    
    case '4':
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print(F("Fade Tim"));
      lcd.setCursor(0, 1);
      lcd.print(F("e =     "));
      lcd.setCursor(4, 1);
      lcd.print(fadeTime);
      do {
        key = keypad.waitForKey();
        if(key == '1') {
          fadeTime++;
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print(F("Fade Tim"));
          lcd.setCursor(0, 1);
          lcd.print(F("e =     "));
          lcd.setCursor(4, 1);
          lcd.print(fadeTime);
        } else if(key == '2') {
          fadeTime--;
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print(F("Fade Tim"));
          lcd.setCursor(0, 1);
          lcd.print(F("e =     "));
          lcd.setCursor(4, 1);
          lcd.print(fadeTime);
        }
      } while(key != '4');
      EEPROM.updateByte(4,fadeTime);
      blDelay = millis();
      break;
    }
  }
}
```
Here is the Fritzing diagram showing how everything is wired. The LCD I used is actually a 16x1 and these are not addressed like most other LCDs. The first 8 characters are adddressed as the first line and the last 8 characters are line 2. Keep this in mind if you are customizing the code. Also, the keypad is 4x1 not 4x3 as shown. Everything else is pretty straight forward.









The lid was made with some scrap acrylic and black Krylon paint. I used super glue and epoxy to keep it all together.


















Plant list left to right
Flame Moss on the shrimp breeder tube.
Echinodorus Reni - Tropica
Pogostemon Helferi
Staurogyne Repens
Fissidens Fontanus
Anubias Barteri var. Nana - Tropica
Ludwigia Palustris
Alternanthera Reineckii Mini
Microsorum Pteropus Narrow - Tropica

Hardscape
Seiryu Stone
Hardwood branch from the backyard
Eco-complete substrate

Livestock
7 x Neon Tetras
2 x Otocinclus
1 x Amano Shrimp
10 x Yellow Fire Neocaridina Heteropoda Shrimp

Water Parameters
pH = 7.2
Temp = 23C
Ammonia = 0
NitrIte = 0
NitrAte = 30-40ppm
GH = 8
KH = 3
TDS = 200ppm


----------



## Chiumanfu (Oct 30, 2014)

Some random shots


----------



## Bobsidd (Dec 28, 2012)

Wow. 

What an awesome mod! And a very nice 'scape. 

How much experience did you have with electronics before rigging this all together? I'm too scared to try anything like this for fear of burning my house to the ground. 

Cheers,
Rob


----------



## Chiumanfu (Oct 30, 2014)

Thanks Bob. 

I'm an electronic designer by trade so this was all pretty straight forward. Its almost all low voltage DC so as long as you use the appropriate gauge wire and your solder joints are clean, there is zero risk of spontaneous combustion .


----------



## SeaHorse_Fanatic (Apr 22, 2010)

Excellent job my friend. Very impressive setup, both the light/controller & the actual aquascaping.


----------



## Spit1A (Apr 7, 2014)

Looks great! I'll have to save this thread for reference. At some point I would like to build something like this!


----------



## Chiumanfu (Oct 30, 2014)

If you need any help with your own build, just shoot me a message.


----------



## Big Dog (Feb 6, 2015)

*Great job. Your planted aquarium looks great also.*


----------



## Tommy72a (Jan 6, 2013)

Awesome build and thanks for sharing. I purchased from Rapid LED too and I think they are amazing. I did a 75g and 8g built (I posted a diy planted aquarium post, some time ago on my 75g) but for my 8 g, I hacked an old ikea desk lamp. 
They are so strong, I can only run it at about 60 percent or it turns into a algae soup. 
Please keep us posted!


----------



## Arcteryx (Nov 23, 2011)

Wow, fabulous stuff. Great writeup and it looks awesome.


----------



## mysticalnet (Apr 22, 2010)

Very nice! !


----------



## Jousters (Jan 16, 2014)

Great work.Fluval should hire you to design there next tank!


----------



## jagermelifter (Feb 12, 2015)

good stuff bro


Sent from my iPhone using Tapatalk


----------

