The Planted Tank Forum banner
1 - 1 of 1 Posts

·
Registered
Joined
·
2 Posts
Discussion Starter · #1 · (Edited)
Hi yall, been lurking on these forums for a few years now and this place has given me a ton of useful knowledge and advice when I first got started in this hobby. I would like to give back a little by documenting a quick tutorial on getting a custom timer to work that works with the finnex 24/7 lights. I have one of these and my biggest regrets with them are the accuracy/customizability of that built in timer... Its simply just junk and definitely ruins the experience and potential of the light.

So, instead of writing an angry and annoyed amazon review, which I was almost about to do, I decided I would build a custom timer/controller for the LED light. All it basically does is clone the commands from the controller, then sends them back out again on a time schedule. The system has proved to be reliable with my limited testing over the past few days. As a novice in electronics and c++, I was able to get something thats 100% way better than the stock timer for appx 35 dollars if you shop around for the parts; some of these parts I had to buy in qty of 8, 50, 100 in order to just get my hands on one of these. I could have probably just raided a goodwill for old electronics and steal their components now that I'm writing about this thinking about it. Oh well.

You'll need to do a little bit of programming in order to get this up and running. Don't be scared away from the idea of trying to program this thing! You only need to know if/else statements to program your own schedule and I promise you it'll end up being so much better than the options you would otherwise be left with (mechanical timer with a simple on/off? yuck am I right? :p)

Here's a demo of it working:


Cycle starts off at M4, then works its way up to match the brightness in M1, which is the main "day light" mode of the schedule for about 5 hours after which it slowly ramps back down.

Commands Supported:

Set ModeX where X is a number


Code:
sendData(mXRawBuffer, sizeof(mXRawBuffer), sizeof(mXRawBuffer[0]), 68);
Increment Red/Blue/White channels.

I found the green channel to be rather ugly...

C++:
incrementWhiteChannelFinnex();
decrementWhiteChannelFinnex();
incrementRedChannelFinnex();
decrementRedChannelFinnex();
incrementBlueChannelFinnex();
decrementBlueChannelFinnex();
Parts Required


Libraries Required
Optional/Useful
The Circuit



*Notes : you may omit the IR receiver module from the circuit unless you want to want to record your own custom commands (ie adding in support for green channels)

Calculate required Resistance for resistor A and B

Ohms = (Power source voltage - component operating voltage) / (operating current in AMPS.)
It's important to use the right resistors with the components you end up with as you can easily burn out a data pin on the Arduino with an IR Led or end up with a fried IR Led. Yikes, I hope you've got good ventilation if that happens. The peak draw of a typical IR Diode is appx 100ma, but the max current carrying capacity in an Arduino is only 40MA for ALL components. I would use a 20 ohms of resistance if using a 3.3v source and 200ohms if using a 5 volt source.

For example, my IR Led current rating is 120 ma peak at 1.6v. My power source is DC 3.3v. Therefore ohms = (3.3v - 1.6v) / (120ma/1000 ma/a) = 14.166 ohm. For my circuit, I use 2 10ohms resister in series for a total of 20 ohms.

If the IR Led is connected to the Arduino, I would swap out 120ma as the operating current for 30ma. While I was able to get a transmission from as low as 30mah from the 3.3v pins, its not powerful enough to be practical. Use an external power circuit.


For reliability, I suggest routing and taping the IR Led directly next to the area where the receiver is on the light, which is the area in which the LED indicators are. I also covered the led in foil for good measures...not sure how effective it is.

Purple Automotive lighting Automotive tire Bicycle tire Violet


Next we need to install the RTC Clock module. It can easily be added on top of the circuit diagrammed above and its circuitry could be found here. Next, you will want to download the RTC Clock module library and install it using the exe installer. Make sure you select all the options as there are packages that are dependent on each other. I did not sift through them all to figure out what is required.

To set the time, in the Arduino IDE, go to file > examples and you should have example sketches for setting and getting time on the RTC Module. Set the time using the set time sketch then verify the time using the get time sketch. That's it!
Green Azure Rectangle Font Screenshot



After you have everything all wired up correctly (if you have it exactly as described in the schematics then and RTC tutorial, then you should be good to go on.

The Code

To program the controller is all about using some if/else statements. The RTC module/libraries keep track of time using 24 hour format. The hour and minute is passed into a function setMode() and inside of there is where we do our logic to control the led.

C++:
//set up command lines tools:
//./arduino-cli.exe core install arduino:samd
//./arduino-cli.exe core list

//to compile and upload  ./arduino-cli.exe compile './main/main.ino'

#include <IRremote.hpp>
// #include <IRLibRecvPCI.h>

//needed libraries for the RTC module for the arduino
#include <Wire.h>
#include <TimeLib.h>
#include <DS1307RTC.h>

bool demoView = false;

//create our IRsender object with IRremote.hpp
IRsend irSender;

int currentMinute = -1;

//RAW BUFFERS FOR YOUR REMOTE GOES HERE
static uint16_t pBtnRawBuffer[68]={
    8910, 4578, 482, 1762, 478, 654, 478, 658, 478,
    650, 482, 654, 478, 654, 482, 654, 478, 654, 478,
    654, 478, 1762, 478, 1762, 478, 1766, 478,
    1758, 478, 1786, 458, 1786, 454, 1762, 478,
    654, 478, 654, 478, 658, 478, 654, 478, 654, 478,
    654, 478, 658, 478, 654, 478, 1762, 478, 1762, 478,
    1762, 478, 1762, 478, 1762, 478, 1762, 478, 1762,
    478, 1762, 478, 1000};

static uint16_t m1RawBuffer[68]={
    8914, 4582, 478, 1762, 478, 654, 478, 682,
    450, 658, 478, 654, 478, 654, 478, 658,
    474, 662, 474, 654, 478, 1762, 478, 1762,
    478, 1762, 478, 1762, 478, 1762, 478, 1762,
    478, 1762, 478, 654, 478, 1762, 478, 1762,
    478, 1762, 478, 1762, 478, 654, 478, 654,
    478, 658, 478, 1762, 478, 654, 478, 654,
    478, 658, 478, 654, 478, 1762, 478, 1762,
    478, 1762, 478, 1000};

static uint16_t m2RawBuffer[68]={
    8934, 4562, 498, 1738, 506, 634, 498, 634,
    498, 634, 502, 630, 502, 630, 502, 634,
    502, 630, 502, 634, 498, 1742, 498, 1738,
    506, 1738, 502, 1734, 506, 1738, 502, 1734,
    506, 1734, 506, 1738, 502, 1738, 502, 1766,
    474, 1738, 502, 1742, 498, 630, 506, 630,
    502, 634, 498, 630, 502, 634, 502, 630,
    502, 630, 502, 658, 478, 1738, 502, 1734,
    502, 1738, 502, 1000};

static uint16_t m3RawBuffer[68]={
    8930, 4566, 498, 1742, 498, 634, 498, 638,
    494, 638, 498, 634, 498, 638, 494, 638,
    498, 634, 498, 662, 470, 1746, 494, 1746,
    494, 1746, 494, 1746, 494, 1746, 494, 1750,
    490, 1750, 490, 1750, 490, 642, 494, 1746,
    494, 1770, 470, 1746, 494, 638, 494, 1746,
    494, 638, 494, 666, 470, 1746, 494, 638,
    494, 642, 490, 642, 490, 1774, 470, 638,
    494, 1746, 494, 1000};

static uint16_t m4RawBuffer[68]={
    8886, 4582, 478, 1762, 478, 658, 474, 658, 478, 678, 454, 658, 474, 658, 478, 678, 454, 658, 474, 658, 478, 1766, 474, 1790, 450, 1766, 474, 1766, 478, 1762, 478, 1762, 478, 1766, 474, 658, 478, 658, 474, 1762, 478, 1766, 474, 1766, 478, 658, 474, 1762, 478, 658, 478, 1762, 478, 1762, 478, 678, 454, 658, 474, 658, 478, 1762, 478, 654, 478, 1766, 478, 1000};

static uint16_t whiteChannel[68]={
    8906, 4586, 474, 1766, 474, 662, 470, 686, 450, 658, 474, 670, 470, 654, 474, 654, 478, 666, 474, 650, 474, 1766, 474, 1766, 474, 1778, 462, 1774, 466, 1766, 474, 1790, 450, 1766, 474, 658, 478, 1762, 478, 1762, 474, 682, 454, 1766, 474, 658, 474, 658, 482, 654, 474, 1762, 478, 658, 474, 682, 450, 1766, 482, 654, 478, 1758, 478, 1762, 474, 1766, 478, 1000};
static uint16_t whiteChannelDown[68]={
    8886, 4582, 478, 1762, 478, 654, 482, 654, 478, 654, 478, 654, 478, 658, 478, 678, 454, 658, 474, 658, 478, 1762, 478, 1762, 478, 1762, 478, 1762, 478, 1762, 478, 1762, 478, 1762, 482, 654, 478, 1786, 454, 654, 478, 1766, 478, 1786, 454, 654, 478, 654, 478, 658, 478, 1762, 478, 658, 474, 1766, 478, 654, 478, 654, 478, 1762, 478, 1762, 482, 1762, 478, 1000};
static uint16_t redChannelDown[68]={
    8910, 4578, 482, 1758, 482, 650, 482, 654, 482, 650, 482, 650, 482, 654, 478, 678, 454, 654, 482, 650, 482, 1758, 482, 1758, 482, 1758, 482, 1754, 482, 1762, 478, 1762, 478, 1782, 458, 1758, 482, 1758, 482, 650, 482, 1758, 482, 1758, 482, 650, 482, 650, 482, 654, 478, 654, 482, 650, 482, 1758, 482, 654, 478, 650, 486, 1758, 478, 1758, 482, 1762, 478, 1000};
static uint16_t redChannelUp[68]={
    8918, 4574, 486, 1758, 482, 650, 482, 650, 482, 650, 482, 650, 486, 646, 486, 650, 482, 646, 486, 650, 486, 1754, 486, 1754, 486, 1754, 486, 1758, 482, 1758, 482, 1758, 482, 1758, 478, 1758, 482, 1758, 482, 1758, 482, 650, 486, 1758, 482, 650, 482, 650, 482, 650, 486, 650, 482, 646, 486, 650, 482, 1758, 482, 650, 486, 1758, 482, 1754, 486, 1754, 486, 1000};
static uint16_t blueChannelUp[68]={
8906, 4578, 478, 1786, 454, 654, 478, 654, 478, 654, 478, 658, 474, 654, 478, 654, 478, 658, 474, 658, 478, 1758, 478, 1762, 478, 1762, 478, 1762, 474, 1762, 478, 1762, 478, 1758, 478, 658, 474, 682, 454, 1758, 478, 658, 474, 1762, 478, 654, 478, 1786, 454, 654, 478, 1762, 478, 1762, 478, 654, 478, 1786, 450, 658, 478, 1762, 474, 654, 478, 1762, 478, 1000};
static uint16_t blueChannelDown[68]={
    8910, 4574, 482, 1754, 486, 650, 482, 650, 486, 646, 486, 646, 486, 650, 482, 674, 458, 650, 486, 646, 486, 1778, 462, 1754, 482, 1754, 486, 1754, 486, 1754, 486, 1750, 486, 1758, 482, 650, 486, 646, 486, 646, 486, 1754, 486, 1754, 486, 646, 486, 1754, 482, 650, 486, 1754, 482, 1754, 486, 1778, 462, 646, 486, 650, 482, 1758, 482, 646, 486, 1754, 482, 1000};

void setup() {
    Serial.begin(9600);
    irSender.begin(3);
    //irReciever.enableIRIn();
}
void loop() {
    tmElements_t time;
    if(RTC.read(time) && demoView == false){
        int hour = time.Hour;
        int minute = time.Minute;
        setMode(hour, minute);
    }else{
        demoSchedule();
    }
    // decrementWhiteChannelFinnex();
    delay(5000);
}
void repeatKey(unsigned int repeat){
    static uint16_t repeatCommand[4]={
        8878, 2370, 446, 1000
    };
    delay(10);
    unsigned int i = 0;
    do{
        sendData(repeatCommand, sizeof(repeatCommand), sizeof(repeatCommand[0]), 68);
        i++; 
    } while (i < repeat);
}
void incrementChannelFinnex(uint16_t *dataBuffPTR, int arrSize, int sizeOfFirstElement, int khz){
    //oh finnex lights, first buffer commands white, then subsequent smaller buffers indicates hold down to incremement brightness gradually
    irSender.sendRaw(dataBuffPTR, arrSize / sizeOfFirstElement, khz);
    repeatKey(14);
}

void incrementWhiteChannelFinnex(){
    incrementChannelFinnex(whiteChannel, sizeof(whiteChannel), sizeof(whiteChannel[0]), 68);
}
void decrementWhiteChannelFinnex(){
    incrementChannelFinnex(whiteChannelDown, sizeof(whiteChannelDown), sizeof(whiteChannelDown[0]), 68);
}
void incrementRedChannelFinnex(){
    incrementChannelFinnex(redChannelUp, sizeof(redChannelUp), sizeof(redChannelUp[0]), 68);
}
void decrementRedChannelFinnex(){
    incrementChannelFinnex(redChannelDown, sizeof(redChannelDown), sizeof(redChannelDown[0]), 68);
}
void incrementBlueChannelFinnex(){
    incrementChannelFinnex(blueChannelUp, sizeof(blueChannelUp), sizeof(blueChannelUp[0]), 68);
}
void decrementBlueChannelFinnex(){
    incrementChannelFinnex(blueChannelDown, sizeof(blueChannelDown), sizeof(blueChannelDown[0]), 68);
}
void sendData(uint16_t *dataBuffPTR, int arrSize, int sizeOfFirstElement, int khz){
    irSender.sendRaw(dataBuffPTR, arrSize / sizeOfFirstElement, khz);
}
void setMode(int hour, int minute){
    Serial.print(hour);
    Serial.print(", ");
    Serial.print(minute);
    Serial.println("");
    if(currentMinute != minute){
    currentMinute = minute;

//**YOUR CODE GOES HERE**


    }
}
void demoSchedule(){
    for( int hour = 10; hour < 24; hour++){
        for(int minute = 0; minute < 60; minute++){
            setMode(hour, minute);
            delay(50);
        }
    }
}
Here's my setMode function for my tank:

C++:
//11AM
if(hour == 11){ //sunrise at 11 am. index in switches for this action is switches[0];
    if(minute == 35){
        incrementWhiteChannelFinnex();
        delay(1000);
        incrementRedChannelFinnex();
        delay(1000);
        incrementRedChannelFinnex();
    }else if(minute == 50){
        // sendData(m2RawBuffer, sizeof(m2RawBuffer), sizeof(m2RawBuffer[0]), 68);
        incrementRedChannelFinnex();
        delay(1000);
        incrementRedChannelFinnex();
    }
}
//12PM
else if(hour == 12){
    if(minute == 10){
        Serial.println("incrementing...");
        incrementWhiteChannelFinnex();
        delay(1000);
        incrementRedChannelFinnex();
        delay(1000);
        incrementRedChannelFinnex();
    }else if(minute == 40){
        Serial.println("incrementing...");
        incrementWhiteChannelFinnex();
        delay(1000);
        incrementRedChannelFinnex();
        delay(1000);
        incrementRedChannelFinnex();
    }else if(minute == 50){
        Serial.println("incrementing...");
        incrementWhiteChannelFinnex();
    }
}
/// 1PM
else if(hour == 13){
    if(minute == 0){
        sendData(m1RawBuffer, sizeof(m1RawBuffer), sizeof(m1RawBuffer[0]), 68);
    }
}
//  5PM
else if(hour == 17){
    if(minute == 0){
        decrementWhiteChannelFinnex();
    }

    else if(minute == 25){
        decrementWhiteChannelFinnex();
        delay(1000);
        incrementBlueChannelFinnex();
    }
    else if(minute == 45){
        decrementWhiteChannelFinnex();
    }
}
//6pm
else if(hour == 18){
    if(minute == 20){
        decrementWhiteChannelFinnex();
        delay(1000);
        decrementRedChannelFinnex();
        delay(1000);
        decrementRedChannelFinnex();
    }
    else if(minute == 35){
        decrementWhiteChannelFinnex();
        delay(1000);
        decrementRedChannelFinnex();
        delay(1000);
        decrementRedChannelFinnex();
    }
}
//7pm we switch back to mode 3 to reset any drift from press and hold keys...should be pretty reliable at this point.
else if(hour == 19 && minute == 0){
    sendData(m3RawBuffer, sizeof(m3RawBuffer), sizeof(m3RawBuffer[0]), 68);
}
//after 10pm
else if(hour >= 22 && minute == 0){
    sendData(m4RawBuffer, sizeof(m4RawBuffer), sizeof(m4RawBuffer[0]), 68);
}
If you would like to demo your schedule, change this bool value to true:
C++:
bool demoView = false;
to
bool demoView = true;
That's it! If you have any improvements or suggestions regarding changes I could make let me know. Hope you found this helpful
 
1 - 1 of 1 Posts
Top