# Guide: Arduino based LED controller for Current Satellite LED+



## Indychus

So I made an off-hand comment in one of Current USA's threads last night about an Arduino controller for their LED+ freshwater fixture. In the 12 or so hours since posting that, I have probably received a dozen PM's requesting code, IR protocol, sketches, or other information. Instead of responding to each request for information separately, it became obvious that I just needed to put together a thread for my project. This is that thread.

First, a disclaimer. While I'm not your average DIY tinkerer, I'm also not an electronics expert. I'm a mechanical engineer by day, and like to think of myself as a modern mad scientist by night. If you decide to build your own controller based on this thread, be aware that you're on your own. That's not to say I won't help... I think open source is the way of the future and love to pass knowledge on. I just mean that if you burn up your Arduino, yourself, or your house, that it's on you.

I'm putting together a guide to follow along, but none of the steps should be considered the last step. The nice thing about Arduino is how easy it is to constantly modify your code and add features. Throughout this thread, I will periodically post my newest code so that you can upload it and get the newer features. If you're experienced with Arduino, feel free to hack my code apart and have your way with it.

*This process should work for any light fixture with some tweaking!

I'm writing this step-by-step so hopefully anyone can follow along. The more Arduino users we have in the world, the better life can be for everyone! If you're new to Arduino, don't be overwhelmed... it's not as difficult as it seems at first glance. And if you're an old hand please don't bash my primitive coding too much!*


_Everything in this post should be considered open source and public domain. Feel free to use my code, distribute it, hack it up, whatever._


----------



## Indychus

*Check this post for the most up to date code and features.*

*Most Current Version : 3.6* 
Other versions can be found throughout this thread.

_Functionality:_

Allows user to set up to 24 triggers to activate pre-defined functions at any time.
Random thunderstorms.
RTC support maintains time even if power is lost.
Range of around 10 feet for IR emitter.
No modification to the fixture or remote required.
Can be powered by USB or via a wall wart.
Factory remote still functions. Will return to programmed functions at next trigger.
All factory functions can be programmed to activate at any time.
Supports a 20 x 4 LCD screen.

_Default scheduling:_

07.00 am - Dawn/Dusk
09.00 am - Cloud2
11.00 am - FullSpec
03.00 pm - Cloud2
07.00 pm - Dawn/Dusk
09.00 pm - Night2

_Required libraries:_

Time
TimeAlarms
RTClib
IRremote
LiquidCrystal



Code:


///////////////////////////////////////////////////////////////////
// Current Satellite LED+ Controller  V3.6                       //
//   Indychus...Dahammer...mistergreen @ plantedtank.net         //
//   This code is public domain.  Pass it on.                    //
//   Confirmed on Arduino UNO 1.0.5                              //
//   Req. Time, TimeAlarms, RTClib, IRremote                     //
///////////////////////////////////////////////////////////////////
//
// This version uses Ken Shirriff's IRremote library to Rx/Tx the IR codes
// http://www.righto.com/2009/08/multi-protocol-infrared-remote-library.html
// 
// This code does NOT use PIN 13 on the Uno, as do previous versions
// Instead PIN 3, which is a PWM pin, is used. So you'll need to connect
// your LED to PIN 3 instead of PIN 13 for it to work.

// Install LCD per instructions at http://learn.adafruit.com/character-lcds/overview

#include <Wire.h>
#include <RTClib.h>
#include <Time.h>
#include <TimeAlarms.h>
#include <IRremote.h>
#include <LiquidCrystal.h>

RTC_DS1307 RTC;
IRsend irsend;
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);

/*********** BEGIN USER DEFINED VARIABLES ***********/
// DEBUG_IR adds the option to test the IR commands via the Arduino software's serial monitor
// You can send any value from 1 to 32 and it send the corresponding IR code
// The codes follow the remote controller, left to right, top to bottom
// e.g 1 = Orange, 2 = Blue, 21 = Moon1, etc
#define DEBUG_IR

int postDelay = 100;         // Delay after codes are sent
int randAnalogPin = 0;       // This needs to be set to an unused Analog pin, Used by ThunderStorm()

// Current Satellite+ IR Codes (NEC Protocol)
unsigned long codeHeader = 0x20DF; // Always the same

// Remote buttons listed left to right, top to bottom
unsigned int codeOrange = 0x3AC5;
unsigned int codeBlue = 0xBA45;
unsigned int codeRose = 0x827D;
unsigned int codePowerOnOff = 0x02FD;
unsigned int codeWhite = 0x1AE5;
unsigned int codeFullSpec = 0x9A65;
unsigned int codePurple = 0xA25D;
unsigned int codePlay = 0x22DD;
unsigned int codeRedUp = 0x2AD5;
unsigned int codeGreenUp = 0xAA55;
unsigned int codeBlueUp = 0x926D;
unsigned int codeWhiteUp = 0x12ED;
unsigned int codeRedDown = 0x0AF5;
unsigned int codeGreenDown = 0x8A75;
unsigned int codeBlueDown = 0xB24D;
unsigned int codeWhiteDown = 0x32CD;
unsigned int codeM1Custom = 0x38C7;
unsigned int codeM2Custom = 0xB847;
unsigned int codeM3Custom = 0x7887;
unsigned int codeM4Custom = 0xF807;
unsigned int codeMoon1 = 0x18E7;
unsigned int codeMoon2 = 0x9867;
unsigned int codeMoon3 = 0x58A7;
unsigned int codeDawnDusk = 0xD827;
unsigned int codeCloud1 = 0x28D7;
unsigned int codeCloud2 = 0xA857;
unsigned int codeCloud3 = 0x6897;
unsigned int codeCloud4 = 0xE817;
unsigned int codeStorm1 = 0x08F7;
unsigned int codeStorm2 = 0x8877;
unsigned int codeStorm3 = 0x48B7;
unsigned int codeStorm4 = 0xC837;

void SetAlarms()
{
  // Set up your desired alarms here
  // The default value of dtNBR_ALARMS is 6 in Alarms.h.
  // This code sets 12 alarms by default, so you'll need to change dtNBR_ALARMS to 12 or more
  Alarm.alarmRepeat(7,00,0, DawnDusk);
  Alarm.alarmRepeat(9,00,0, Cloud2);     // (HR,MIN,SEC,FUNCTION)
  Alarm.alarmRepeat(11,00,0, FullSpec);
  Alarm.alarmRepeat(15,00,0, Cloud2);
  Alarm.alarmRepeat(19,00,0, DawnDusk);
  Alarm.alarmRepeat(21,00,0, Moon2);
  
  // Comment these out if you don't want the chance of a random storm each day
  Alarm.alarmRepeat(12,00,00, ThunderStorm);
  ThunderStorm();  // Sets up intial storm so we don't have wait until alarm time
}
/************* END USER DEFINED VARIABLES *************/

void setup()
{
  Wire.begin();
  RTC.begin();
  lcd.begin(20, 4);
  Serial.begin(9600);
      //Serial.println(freeRam());
  
  if (! RTC.isrunning()) { 
    Serial.println("RTC Error");
    RTC.adjust(DateTime(__DATE__, __TIME__));}  //Adjust to compile time
    
  
  setSyncProvider(syncProvider);     //reference our syncProvider function instead of RTC_DS1307::get()
  
  Alarm.timerRepeat(900, digitalClockDisplay);  // Display the time every 15 minutes
  digitalClockDisplay();
  SetAlarms();
 Serial.print("SRAM : ");          //un-comment these line to check available SRAM
 Serial.println(freeRam());}   

void loop()
{
#ifdef DEBUG_IR 
  if (Serial.available() > 0) {
    delay(5); //Wait for transmission to finish
    CurrentCMDs(SerialReadInt());
  }
#endif
  Alarm.delay(100); 
  // Service alarms & wait (msec)
  lcdClockDisplay();
}

time_t syncProvider()
{
  //this does the same thing as RTC_DS1307::get()
  return RTC.now().unixtime();
}

void ThunderStorm ()
{ 
  // Schedules a storm between 1 & 9 in the evening
  // It sets Storm2, followed by Cloud2 or DawnDusk or Moon2, depending on when the storm is over
  randomSeed(analogRead(randAnalogPin));  // Generate random seed on unused pin
  byte RH = random(23);                   // Randomizer for thunderstorm
  byte RM = random(59);
  byte RS = random(59);
  byte TSDurationH = random(2);
  byte TSDurationM = random(59);
  
  if (RH <= 12)
    {
      Serial.println("No storm today");
      lcd.setCursor(0,1);
      lcd.print("No storm today");
      return;
    }
      
  if (RH > 12)                             // If random value is after 1 pm, allow storm
    {
      Alarm.alarmOnce(RH,RM,RS,Storm2);
      Serial.print("Next Storm: ");
      Serial.print(RH);
      printDigits(RM);
      printDigits(RS);
      Serial.print("   ");
      Serial.print("Duration = ");
      Serial.print(TSDurationH);
      printDigits(TSDurationM);
      Serial.println();
     lcd.setCursor(0,1);
     lcd.print("Next Storm: ");
     lcdHRdigits(RH);
     lcdDigits(RM);}
      
      if ((RH + TSDurationH) < 19)   // Return to Cloud2 if storm ends between 1-7pm
        {Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,Cloud2);}
      else if ((RH + TSDurationH) < 21)  // Return to DawnDusk if storm ends between 7-9pm
        {Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,DawnDusk);}
      else                                       // Return to Night2 if storm ends after 9pm
        {Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,Moon2);}
    }


void digitalClockDisplay()          // Digital clock
{ 
  Serial.print("Time = ");
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.println(); }

  
void lcdClockDisplay()  
  {lcd.setCursor(0,0);
    lcdHRdigits(hour());
  lcdDigits(minute());}

void printDigits(int digits)        // Add :
{
  Serial.print(":");
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits);
}

void lcdDigits(int digits)        // Add :
{
  lcd.print(":");
  if(digits < 10)
    lcd.print('0');  
  lcd.print(digits);
}
void lcdHRdigits(int HRdigits)        // Preface hour with 0
{
  if(HRdigits < 10)
    lcd.print('0');  
  lcd.print(HRdigits);
}

#ifdef DEBUG_IR
int SerialReadInt()
{
  int i, numAva;
  char inBytes[3];                  // Array to hold the bytes
  char * inBytesPtr = &inBytes[0];  // Pointer to the first element of the array
      
    numAva = Serial.available();    // Read number of input bytes
    if (numAva > 2)
      numAva = 2;                   // Only allow 2 characters to prevent overflow
      
    for (i=0; i<numAva; i++)        // Load input bytes into array
      inBytes[i] = Serial.read();
    inBytes[i] =  '\0';             // Put NULL character at the end
    return atoi(inBytesPtr);        // Call atoi function and return result
}

void CurrentCMDs (int cmd)
{switch (cmd)
  {
    case 1:
      Orange();
      break;
    case 2:
      Blue();
      break;
    case 3:
      Rose();
      break;
    case 4:
      PowerOnOff();
      break;
    case 5:
      White();
      break;
    case 6:
      FullSpec();
      break;
    case 7:
      Purple();
      break;
    case 8:
      Play();
      break;
    case 9:
      RedUp();
      break;
    case 10:
      GreenUp();
      break;
    case 11:
      BlueUp();
      break;
    case 12:
      WhiteUp();
      break;
    case 13:
      RedDown();
      break;
    case 14:
      GreenDown();
      break;
    case 15:
      BlueDown();
      break;
    case 16:
      WhiteDown();
      break;
    case 17:
      M1Custom();
      break;
    case 18:
      M2Custom();
      break;
    case 19:
      M3Custom();
      break;
    case 20:
      M4Custom();
      break;
    case 21:
      Moon1();
      break;
    case 22:
      Moon2();
      break;
    case 23:
      Moon3();
      break;
    case 24:
      DawnDusk();
      break;
    case 25:
      Cloud1();
      break;
    case 26:
      Cloud2();
      break;
    case 27:
      Cloud3();
      break;
    case 28:
      Cloud4();
      break;
    case 29:
      Storm1();
      break;
    case 30:
      Storm2();
      break;
    case 31:
      Storm3();
      break;
    case 32:
      Storm4();
      break;
    default:
      Serial.println("Invalid Choice");}}
#endif

void SendCode (unsigned int code, byte numTimes, const char *sMessage)
{unsigned long irCode = (codeHeader << 16) + code; // Header is 2 bytes, shift all the way to left & add code to it
  
  for( int i = 0; i < numTimes; i++)
  {irsend.sendNEC(irCode,32); // Send code
    Alarm.delay(postDelay);}
    
  Serial.println(sMessage);  // Print message
  lcd.setCursor(6,0);
  lcd.print(sMessage);
  for(byte i = sizeof(sMessage); i <= 14; i++)
    lcd.print(" ");
  digitalClockDisplay();}

void Orange()
{SendCode(codeOrange, 2, "Orange");}

void Blue()
{SendCode(codeBlue, 2, "Blue");}

void Rose()
{SendCode(codeRose, 2, "Rose");}

void PowerOnOff()
{SendCode(codePowerOnOff, 1, "Power On/Off");}

void White()
{SendCode(codeWhite, 2, "White");}

void FullSpec()
{SendCode(codeFullSpec, 2, "Full Spectrum");}

void Purple()
{SendCode(codePurple, 2, "Purple");}

void Play()
{SendCode(codePlay, 1, "Play/Pause:");}

void RedUp()
{SendCode(codeRedUp, 1, "Red Up");}

void GreenUp()
{SendCode(codeGreenUp, 1, "Green Up");}

void BlueUp()
{SendCode(codeBlueUp, 1, "Blue");}

void WhiteUp()
{SendCode(codeWhiteUp, 1, "White Up");}

void RedDown()
{SendCode(codeRedDown, 1, "Red Down");}

void GreenDown()
{SendCode(codeGreenDown, 1, "Green Down");}

void BlueDown()
{SendCode(codeBlueDown, 1, "Blue Down");}

void WhiteDown()
{SendCode(codeWhite, 1, "White Down");}

void M1Custom()
{SendCode(codeM1Custom, 2, "Custom Mix 1");}

void M2Custom()
{SendCode(codeM2Custom, 2, "Custom Mix 2");}

void M3Custom()
{SendCode(codeM3Custom, 2, "Custom Mix 3");}

void M4Custom()
{SendCode(codeM4Custom, 2, "Custom Mix 4");}

void Moon1()
{SendCode(codeMoon1, 2, "Moonlight 1");}

void Moon2()
{SendCode(codeMoon2, 2, "Moonlight 2");}

void Moon3()
{SendCode(codeMoon3, 2, "Moonlight 3");}

void DawnDusk()
{SendCode(codeDawnDusk, 2, "Dawn/Dusk");}

void Cloud1()
{SendCode(codeCloud1, 2, "Cloud Cover 1");}

void Cloud2()
{SendCode(codeCloud2, 2, "Cloud Cover 2");}
  
void Cloud3()
{SendCode(codeCloud3, 2, "Cloud Cover 3");}

void Cloud4()
{SendCode(codeCloud4, 2, "Cloud Cover 4");}

void Storm1()
{SendCode(codeStorm1, 2, "Thunderstorm 1");}

void Storm2()
{SendCode(codeStorm2, 2, "Thunderstorm 2");}

void Storm3()
{SendCode(codeStorm3, 2, "Thunderstorm 3");}

void Storm4()
{SendCode(codeStorm4, 2, "Thunderstorm 4");}

int freeRam () {
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}


----------



## Indychus

Here's what you need to get started. These parts can be had at radioshack or other electonics supplies, although e b a y and Amazon are great sources.


*Arduino:* I used an Uno for this project, which is $29 straight from Arduino, or around $9 for a knock-off board. I prefer the genuine Arduino, but many of the knock-offs are very nice. Boards other than Uno can be used, but some of the pins may be different. I am using a Revision 3 board running Arduino version 1.0.5. If you're new to this, I'd suggest a starter kit that includes a breadboard, jumpers, and an assortment of goodies.


*IR Receiver:* The Current system uses a 38 kHz frequency just like most A/V equipment. At least, my 48" Satellite LED+ does. You can use an oscilloscope and photodiode to determine the frequency if you are trying to control a light other than Current. Any 38 khz IR receiver should work. They are around $2-3 usually, but can be had for as cheap as $.50 if you buy more than one. I used a *TSOP4838*.


*IR Emitter LED:* Again, any emitter should work, and they are dirt cheap. I used *Lite-On LTE-5208A* which are around $0.12 each. Buy a pack of 20 or so, you'll find uses for them.


*150 ohm Resistor:* If you use the same parts as I have listed above, a 150 ohm resistor is needed. One resistor is around $0.02. Again, I recommend buying an assortment since they're so cheap. If you're not using identical components as those listed above, you will need to figure out what resistor is needed for your specific needs. I can help with this if needed.


*Real Time Clock:* An RTC is needed so that your Arduino knows what time it is and your program can resume operation properly after a power outage. I recommend any RTC based on the DS1307. You can buy kits that must be assembled, assembled boards that need the headers soldered on, or complete plug and play units. It's up to you. Cost is between $5-$25.


*LCD Screen:* The code supports a 20 x 4 LCD screen, but it is completely optional. If you want the extra ease of use and eye-candy, I suggest the 20 x 4 backlit LCD from Adafruit.


----------



## O2surplus

Do the Current Led fixtures accept a 5V PWM dimming signal directly?


----------



## Indychus

O2surplus said:


> Do the Current Led fixtures accept a 5V PWM dimming signal directly?


I'm not sure what is going on on the Current side of things. One of my goals was to maintain the remote functionality, so I chose to go with the IR and haven't looked into the fixture itself much. I'm fairly sure that it will be 5V PWM if you investigate it though.

I have the IR protocol in hex, I'll be posting it shortly! You should be able to skip the IR stuff and wire the IR lead from the fixture directly to the Arduino if wanted.


----------



## crazymittens

Someone in the lighting sub-forum was asking about this...I'm curious if there are any biological benefits to this. Is this cool (it is), or functionally cool?


----------



## Indychus

crazymittens said:


> Someone in the lighting sub-forum was asking about this...I'm curious if there are any biological benefits to this. Is this cool (it is), or functionally cool?


I've seen a few studies implying that fish in a system with dynamic lighting are more sexual active, and therefor healthier... But I don't think anything absolutely conclusive has been done. My main goal is the cool factor. With the Current product announcement and their own controllers out now, there isn't much reason to build your own economically speaking (I was a little shocked at how low their MSRP is), but this does allow you more control and the ability to tailor it exactly to your needs.

When I first started this, Current had no controller available. So I had a nice new light with dozens of functions that I had to control manually. It was a bit of a bummer.


----------



## Indychus

I've never had occasion to post pics of my tanks (mainly because they look pretty shabby compared to many on here), so here's a pair of them. I have 6 total, but these are the only ones worthy of being posted.

Here's my 20H. This is my favorite tank right now. There's a pair of male dwarf gourami, a bunch of chili rasbora, some otos, and some cories in here.









And this is my 55. It's not looking too good right now, as it's recovering from the destruction caused by a yellow-bellied slider. This is the tank that the LED+ and automation is going on.


----------



## mistergreen

I'd suggest you get a Real Time Clock (RTC) component for your arduino. It'll keep time and date for you even in a power outage. It comes with a battery.


----------



## Indychus

mistergreen said:


> I'd suggest you get a Real Time Clock (RTC) component for your arduino. It'll keep time and date for you even in a power outage. It comes with a battery.


I have one on the way! Using a software clock right now, but syncing to the PC is proving to be a headache. I finally broke down and ordered an RTC.


----------



## O2surplus

Indychus said:


> I'm not sure what is going on on the Current side of things. One of my goals was to maintain the remote functionality, so I chose to go with the IR and haven't looked into the fixture itself much. I'm fairly sure that it will be 5V PWM if you investigate it though.
> 
> I have the IR protocol in hex, I'll be posting it shortly! You should be able to skip the IR stuff and wire the IR lead from the fixture directly to the Arduino if wanted.


Oh I see what you're up to now. Don't you have to install the Arduino between the IR controller and the internal led drivers? Seems to me that you would, as the IR controller is only programmed to perform the commands that it receives from the IR remote control? The Arduino could then perform other programmed "tricks" that aren't available from the IR remote, and the IR controller would simply become an input to the Arduino.


----------



## Indychus

*Now for the hardware!*

This is the basic setup. The IR range is limited to around 3 feet like this due to the current limit on the Arduino's output channel. Later, I will build an amplifier to boost the range to hopefully around 10 feet. Since the controller will sit right next to the IR receiver on the fixture, this isn't really an issue.

Here's what your IR receiver should look like:









Now connect it to your Arduino, like this:









The IR lead is connected to the Arduino's 2 pin. If you're not using an UNO, any PWM or digital channel will work. Connect the 5V and ground leads to the appropriate pins on the Arduino.


----------



## Indychus

O2surplus said:


> Oh I see what you're up to now. Don't you have to install the Arduino between the IR controller and the internal led drivers? Seems to me that you would, as the IR controller is only programmed to perform the commands that it receives from the IR remote control? The Arduino could then perform other programmed "tricks" that aren't available from the IR remote, and the IR controller would simply become an input to the Arduino.


Yeah, it's certainly possible to do it that way, and that's what I plan to do eventually. Right now, the Arduino is basically just mimicking the remote, so custom functions are not possible. I figured this method would be more appetizing to most people since they wouldn't have to modify their fixture.


----------



## Indychus

Now for the emitter. LED's need to be installed with the proper polarity, in most cases the negative lead is denoted with a flat spot on the side of the bulb.










Next, you need a resistor to limit the current through the LED and protect the channel on the Arduino. In this case, I used a 150 ohm resistor which gives me a current of 33 mA... safely below the Arduino's rating of 40 mA for this channel. The emitter is designed to operate at 100 mA, so it has a short range right now, but a transistor amplifier will fix that in the near future.

Wire it like this:









The perspective in that picture is off, so don't let it fool you. The yellow wire is connected to the GND pin, and the green to pin 13. Again, you can use any non-digital pin for this purpose. Connect the ground to the - side of the emitter, then the + side to the resistor, then the resistor to channel 13 on the Arduino.

*Note that the most current code uses PWM pin 3 instead of pin 13*


That's all there is for the hardware, until I add an amplifier in the future and an RTC for timing. This is enough for basic functions and pretty much operates as an overly-complicated timer. More to come though...


----------



## scotie aquatic

Here we go, im subcribed. And will be asking alot of questions soon lol


----------



## O2surplus

Indychus said:


> Yeah, it's certainly possible to do it that way, and that's what I plan to do eventually. Right now, the Arduino is basically just mimicking the remote, so custom functions are not possible. I figured this method would be more appetizing to most people since they wouldn't have to modify their fixture.


I have to admit that I'm not familiar with the remote controls functionality with regards to the Current lighting fixture, but your approach to automating the fixture is novel. Since you've hacked the IR control protocols, it should be relatively easy to program the arduino to act as a "stand in" for the human thumb and perform different commands to the lighting system based on pre programmed code. Since the Micro-Controller controlled IR transmitter and receiver electronics have a much faster reaction time than the human finger, custom lighting effects _should_ be possible.


----------



## Indychus

O2surplus said:


> I have to admit that I'm not familiar with the remote controls functionality with regards to the Current lighting fixture, but your approach to automating the fixture is novel. Since you've hacked the IR control protocols, it should be relatively easy to program the arduino to act as a "stand in" for the human thumb and perform different commands to the lighting system based on pre programmed code. Since the Micro-Controller controlled IR transmitter and receiver electronics have a much faster reaction time than the human finger, custom lighting effects _should_ be possible.


I hope so... Each color level is individually adjustable... RGB and white... plus you can store 4 custom colors. I intend to use the RGB/White level adjustments to create fades. There are also 3 cloud covers, 3 T-storms, moon lights, dusk/dawn, etc. built in to the current fixture. The effects are decent, but I'll eventually open it up and control the LED drivers directly.

I'm hoping this approach is easy enough for anyone to follow without boring those of you who are familiar with Arduino


----------



## JJBTEXAS

Indychus said:


> With the Current product announcement and their own controllers out now, there isn't much reason to build your own economically speaking (I was a little shocked at how low their MSRP is)


What controllers? I can't find anything about this online.


----------



## Indychus

JJBTEXAS said:


> What controllers? I can't find anything about this online.


They literally just announced it last night at some big expo. They should be on the market soon. MSRP for controlling a single fixture is supposed to be $39... dual is more. This Arduino setup can control an infinite number of fixtures though, provided their IR sensors are within range of the emitter on the Arduino. :hihi:


----------



## Indychus

*Software*

Ok, so now that you have the basic device built, it's time to make it do something. Again, I'm writing this in case anyone completely new to Arduino wants to follow along. If you're familiar with this stuff, feel free to skip ahead. 

*Here's the basic premise of the device:*


Decode IR signal from remote
Store IR signal in an Arduino sketch
Keep time (now software based, adding an RTC soon)
Recall IR signal at set times to activate functions on the fixture

It's really that simple. The Arduino is basically doing the same thing you would do with the remote, but doing it automatically at set times. No modification to the remote or fixture is necessary.

Note that this procedure can be used to control _any IR device _that operates at 38 khz. That's pretty much everything... your TV, BluRay player, audio equipment, etc. And with a little tweaking and an appropriate receiver, it can work on devices which are not at a frequency of 38 kHz as well. In short, you can automate anything with an IR receiver on it.


----------



## Indychus

So first off, go to the Arduino start up page to download the appropriate software and load a blink sketch to test your board. It can be found here:

http://arduino.cc/en/Guide/HomePage

Once that's done, you'll need to download and install a few libraries for this to work. You just download the .zip file, unzip into a folder on your desktop, then move the folder to the libraries folder inside the Arduino folder. Be sure to maintain any folder/subfolder structure in the libraries.

For this to work, you'll need these libraries:


Time
TimeAlarms
DateTime
DateTimeStrings

These can be found here (Time) and here (TimeAlarms). I'm pretty sure the other two are included in one of those libraries. If not, they can be found on Arduino Playground or via a quick google search.

As this project progresses, other libraries may be needed. If I write them, I will provide them, but most likely they'll be by someone else and I'll point you to a link to download them.

A lot of errors during compiling are due to libraries being in the wrong spot. Don't get frustrated if you get errors, they are usually easily resolved.


----------



## Indychus

Once that's done, you'll need a sketch for decoding the IR signals from your remote. I've already done quite a few of them, but you can use this to operate any IR device, so it's a handy bit of code to know. I can't take credit for this code, as I got it from adafruit.com. That's the beauty of Arduino, for nearly every project you can find something someone else has done and tweak it to suit your needs.

Here's the sketch. Just copy it and paste it into a new sketch in the Arduino software. Save as IR Decoder or something similar. Once saved, change the value "#define IRpin x" to match the channel that your receiver is plugged in to. If you're following step by step, it's already ready to go. Upload to the Arduino, open the Serial Monitor (top right corner), wait for the prompt (Ready to decode IR), then point your remote at your receiver (1-2 inches away) and press a button.



Code:


/* Raw IR decoder sketch!
This sketch/program uses the Arduno and a PNA4602 to
decode IR received. This can be used to make a IR receiver
(by looking for a particular code)
or transmitter (by pulsing an IR LED at ~38KHz for the
durations detected
Code is public domain, check out www.ladyada.net and adafruit.com
for more tutorials!
*/
 
// We need to use the 'raw' pin reading methods
// because timing is very important here and the digitalRead()
// procedure is slower!
//uint8_t IRpin = 2;
// Digital pin #2 is the same as Pin D2 see
// http://arduino.cc/en/Hacking/PinMapping168 for the 'raw' pin mapping
#define IRpin_PIN PIND
#define IRpin 2
// for MEGA use these!
//#define IRpin_PIN PINE
//#define IRpin 4
 
// the maximum pulse we'll listen for - 65 milliseconds is a long time
#define MAXPULSE 65000
 
// what our timing resolution should be, larger is better
// as its more 'precise' - but too large and you wont get
// accurate timing
#define RESOLUTION 20
 
// we will store up to 100 pulse pairs (this is -a lot-)
uint16_t pulses[100][2]; // pair is high and low pulse
uint8_t currentpulse = 0; // index for pulses we're storing
 
void setup(void) {
  Serial.begin(9600);
  Serial.println("Ready to decode IR!");
}
 
void loop(void) {
  uint16_t highpulse, lowpulse; // temporary storage timing
  highpulse = lowpulse = 0; // start out with no pulse length
  
  
// while (digitalRead(IRpin)) { // this is too slow!
    while (IRpin_PIN & (1 << IRpin)) {
     // pin is still HIGH
 
     // count off another few microseconds
     highpulse++;
     delayMicroseconds(RESOLUTION);
 
     // If the pulse is too long, we 'timed out' - either nothing
     // was received or the code is finished, so print what
     // we've grabbed so far, and then reset
     if ((highpulse >= MAXPULSE) && (currentpulse != 0)) {
       printpulses();
       currentpulse=0;
       return;
     }
  }
  // we didn't time out so lets stash the reading
  pulses[currentpulse][0] = highpulse;
  
  // same as above
  while (! (IRpin_PIN & _BV(IRpin))) {
     // pin is still LOW
     lowpulse++;
     delayMicroseconds(RESOLUTION);
     if ((lowpulse >= MAXPULSE) && (currentpulse != 0)) {
       printpulses();
       currentpulse=0;
       return;
     }
  }
  pulses[currentpulse][1] = lowpulse;
 
  // we read one high-low pulse successfully, continue!
  currentpulse++;
}
 
void printpulses(void) {
  Serial.println("\n\r\n\rReceived: \n\rOFF \tON");
  for (uint8_t i = 0; i < currentpulse; i++) {
    Serial.print(pulses[i][0] * RESOLUTION, DEC);
    Serial.print(" usec, ");
    Serial.print(pulses[i][1] * RESOLUTION, DEC);
    Serial.println(" usec");
  }
  
  // print it in a 'array' format
  Serial.println("int IRsignal[] = {");
  Serial.println("// ON, OFF (in 10's of microseconds)");
  for (uint8_t i = 0; i < currentpulse-1; i++) {
    Serial.print("\t"); // tab
    Serial.print(pulses[i][1] * RESOLUTION / 10, DEC);
    Serial.print(", ");
    Serial.print(pulses[i+1][0] * RESOLUTION / 10, DEC);
    Serial.println(",");
  }
  Serial.print("\t"); // tab
  Serial.print(pulses[currentpulse-1][1] * RESOLUTION / 10, DEC);
  Serial.print(", 0};");
}


----------



## Indychus

Once that's done, you'll get an output that looks like this in your Serial Monitor window:










These numbers are delays, in microseconds, for each flash of the IR emitter in a signal data transfer. I won't get into IR theory (it isn't that complicated and you should definitely check it out) but these are the codes we need to get things going. The first group is raw data, and the second group is the same data, in an easier to look at format. The very first value can be ignored, as it's the delay between starting the sketch and pressing the button.

I suggest creating a spreadsheet to keep the data organized, like the one I am working on here:










I will post the entire spreadsheet once I finish decoding and testing all of the functions. Here, you can see that each transfer is composed of a header, which is a device ID, the actual code, then a footer, which just tells the fixture that the transfer is done and to be ready to potentially expect another packet.

You device ID may be different than mine, though I doubt it. If it is, you just change the values in the control sketch (which I am about to post) to reflect your ID. Since the device ID and footer are identical on every transmission, you can just copy and paste the data and change appropriate values.


----------



## Indychus

At this point, more advanced users may be asking where the hex codes are. Well, here you go.

If you're new, you can ignore this post.

Looking at the remote, these are ordered left to right, top to bottom. In every case, the footer is FFFFFFFF. The device ID (20DF) may or may not be the same on all Current products.

*Orange* 20DF3AC5
*Blue* 20DFBA45
*Rose Pink* 20DF827D
*Power On/Off* 20DF02FD
*White* 20DF1AE5
*Full Spectrum* 20DF9A65
*Purple* 20DFA25D
*Play/Pause* 20DF22DD
*Red Up * 20DF2AD5
*Green Up * 20DFAA55
*Blue Up * 20DF926D
*White Up * 20DF12ED
*Red Down* 20DF0AF5
*Green Down* 20DF8A75
*Blue Down * 20DFB24D
*White Down * 20DF32CD
*M1 * 20DF38C7
*M2 * 20DFB847
*M3 * 20DF7887
*M4 * 20DFF807
*Moon 1 * 20DF18E7
*Moon 2 * 20DF9867
*Moon 3 * 20DF58A7
*Dawn/Dusk* 20DFD827
*Cloud 1 * 20DF28D7
*Cloud 2 * 20DFA857
*Cloud 3 * 20DF6897
*Fading Sun* 20DFE817
*T-Storm 1* 20DF08F7
*T-Storm 2 * 20DF8877
*T-Storm 3 * 20DF48B7
*Fade All * 20DFC837


----------



## Indychus

*Now we're ready to automate a light!*

Here's my code for the Current fixture. I will update it frequently as the device matures and more features are added. For right now, this is a good place to test your system. Run this code for a few days and make sure everything is working fine. I have had this code running for around 2 weeks now without issue.

*How to adapt this code to your system:*

There are several things you need to do to ensure this works for you. First, you'll see a value for "int IRLedPin" that's currently set to 13. Change this to whatever channel your emitter is connected to. Next, change the 9600 in "Serial.begin(9600)" to the baud rate you are using to communicate with your Arduino. If you don't know this, you can probably leave it alone and have no issues. Next, change the values in setTime(HR,MIN,SEC,MO,DAY,YR) to match the time right before you upload the sketch. This sets the Arduino's time. If power is lost or you edit the sketch, you will need to update the time again. This will be fixed soon when we add an RTC to the device. Finally, check the IR commands at the end of the code. Make sure the device ID (first 17 number pairs) is the same as the values you got from your remote and change them if necessary. It's ok if they're off by as much as 100. You're looking for values that are off by 500 or more.

In addition to those tweaks to the code, you'll also need to edit the header file for the TimeAlarm library. This is because the header that comes in the library sets the maximum number of alarms to 6. If you try to add more, they won't work. To fix this, open the file TimeAlarms.h in the Arduino > Libraries > TimeAlarms directory with a text editor (NotePad works fine). It will look like this:










In the highlighted area, you'll see mine says 24 and yours should say 6. Change the value to 24 and save the document. This allows you to have 24 alarm triggers. In the future, you can add more, but it's wise to not overdo it as each alarm reserves space in the Arduino's memory.

*How it works:*
Basically, this is a fancy alarm clock right now. The values under "ALARM FUNCTIONS" are triggers. Each trigger has parameters (HR,MIN,SEC,FUNCTION). The FUNCTION refers to the IR codes at the bottom of my sketch. Whenever the time here is reached, that function is fired. It fires the emitter once, then repeats it 1 second later to be sure the command was received. Change the time to whatever values you desire, using the same syntax as I have used. Note that the clock is 24hr format. The FUNCTION names to use are labeled in the "void XXXXXXX()" lines of the code under "FIXTURE FUNCTIONS."

Right now, these functions are available:

PowerButton - toggles power on/off
DawnDusk - puts fixture into dawn and dusk mode
Cloud2 - heavy cloud cover with full spectrum cycle
FullSpec - solid full spectrum light, all-on.
Night2 - soft, fading moonlight.





Code:


///////////////////////////////////////////////////////////////////
// Current Satellite LED+ Controller                             //
//   Ken Bunton (Indychus)                                       //
//   This code is public domain.  Pass it on.                    //
//   Confirmed on Arduino UNO 1.0.5                              //
//   Req. Time, TimeAlarms, DateTime, DateTimeStrings libraries  //
///////////////////////////////////////////////////////////////////

////////////SETUP//////////////////////////////////////////////////
#include <Time.h>
#include <TimeAlarms.h>
#include <DateTime.h>
#include <DateTimeStrings.h>

int IRledPin =  13;                  // Pin location for IR output

void setup()               
{ pinMode(IRledPin, OUTPUT);         // Designate IRledPin as Output
  Serial.begin(9600);                // Connect @ (Baud)
  setTime(13,04,00,7,23,13);          // set time (HR,MIN,SEC,MO,DAY,YR)
  
  
////////////ALARM FUNCTIONS/////////////////////////////////////////
  Alarm.alarmRepeat(7,00,0, DawnDusk);
  Alarm.alarmRepeat(7,00,1, DawnDusk);
  Alarm.alarmRepeat(9,00,0, Cloud2);     // (HR,MIN,SEC,FUNCTION)
  Alarm.alarmRepeat(9,00,1, Cloud2);
  Alarm.alarmRepeat(13,00,0, FullSpec);
  Alarm.alarmRepeat(13,00,1, FullSpec);
  Alarm.alarmRepeat(15,00,0, Cloud2);
  Alarm.alarmRepeat(15,00,1, Cloud2);
  Alarm.alarmRepeat(19,00,0, DawnDusk);
  Alarm.alarmRepeat(19,00,1, DawnDusk);
  Alarm.alarmRepeat(21,00,0, Night2);
  Alarm.alarmRepeat(21,00,1, Night2);    }


////////////CLOCK///////////////////////////////////////////////////
void  loop(){                       
  digitalClockDisplay();
  Alarm.delay(1000); }              // Clock display update frequency (msec)

void digitalClockDisplay()          // Digital clock
{ Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.println(); }

void printDigits(int digits)        // Add :
{Serial.print(":");
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits);}

  
////////////SIGNAL///////////////////////////////////////////////////
// Create Frequency (38khz/26msec)
void pulseIR(long microsecs) 
{  cli();                           // kill interupts
  while (microsecs > 0)
{  digitalWrite(IRledPin, HIGH);    // ~3 msec
   delayMicroseconds(7);            // ~delay
   digitalWrite(IRledPin, LOW);     // ~3 msec
   delayMicroseconds(7);            // ~delay
    microsecs -= 26;  }
sei();  }                           // zombie interupts


////////////FIXTURE FUNCTIONS////////////////////////////////////////
void PowerButton()                             //Fixture power on/off toggle

{ Serial.println("Power Toggle");
     pulseIR(8840);
  delayMicroseconds(4320);
     pulseIR(620);
  delayMicroseconds(480);
    pulseIR(600);
  delayMicroseconds(500);
    pulseIR(540);
  delayMicroseconds(1660);
    pulseIR(580);
  delayMicroseconds(520);
    pulseIR(520);
  delayMicroseconds(560);
    pulseIR(600);
  delayMicroseconds(480);
    pulseIR(620);
  delayMicroseconds(480);
    pulseIR(540);
  delayMicroseconds(540);
    pulseIR(620);
  delayMicroseconds(1600);
    pulseIR(520);
  delayMicroseconds(1660);
    pulseIR(580);
  delayMicroseconds(520);
    pulseIR(520);
  delayMicroseconds(1660);
    pulseIR(600);
  delayMicroseconds(1580);
    pulseIR(620);
  delayMicroseconds(1580);
    pulseIR(540);
  delayMicroseconds(1660);
    pulseIR(600);
  delayMicroseconds(1580);
    pulseIR(600);
  delayMicroseconds(500);
    pulseIR(600);
  delayMicroseconds(500);
    pulseIR(520);
  delayMicroseconds(560);
    pulseIR(600);
  delayMicroseconds(500);
    pulseIR(600);
  delayMicroseconds(500);
    pulseIR(520);
  delayMicroseconds(560);
    pulseIR(580);
  delayMicroseconds(1620);
    pulseIR(540);
  delayMicroseconds(540);
    pulseIR(600);
  delayMicroseconds(1600);
    pulseIR(520);
  delayMicroseconds(1660);
    pulseIR(580);
  delayMicroseconds(1620);
    pulseIR(580);
  delayMicroseconds(1620);
    pulseIR(520);
  delayMicroseconds(1660);
    pulseIR(580);
  delayMicroseconds(1600);
    pulseIR(580);
  delayMicroseconds(520);
    pulseIR(580);
  delayMicroseconds(1620);
    pulseIR(540);
  delayMicroseconds(38980);
    pulseIR(8860);
  delayMicroseconds(2120);
    pulseIR(620);  }

 
void Night2()                                   //Fixture Initialize Night2 Mode
 { Serial.println("Night 2 Mode Initialized");
    pulseIR(8840);
  delayMicroseconds(4320);
    pulseIR(620);
  delayMicroseconds(480);
    pulseIR(600);
  delayMicroseconds(500);
    pulseIR(540);
  delayMicroseconds(1660);
    pulseIR(580);
  delayMicroseconds(520);
    pulseIR(520);
  delayMicroseconds(560);
    pulseIR(600);
  delayMicroseconds(480);
    pulseIR(620);
  delayMicroseconds(480);
    pulseIR(540);
  delayMicroseconds(540);
    pulseIR(620);
  delayMicroseconds(1600);
    pulseIR(520);
  delayMicroseconds(1660);
    pulseIR(580);
  delayMicroseconds(520);
    pulseIR(520);
  delayMicroseconds(1660);
    pulseIR(600);
  delayMicroseconds(1580);
    pulseIR(620);
  delayMicroseconds(1580);
    pulseIR(540);
  delayMicroseconds(1660);
    pulseIR(600);
  delayMicroseconds(1580);
    pulseIR(600);
  delayMicroseconds(1640);
    pulseIR(600);
  delayMicroseconds(500);
    pulseIR(520);
  delayMicroseconds(560);
    pulseIR(600);
  delayMicroseconds(1620);
    pulseIR(600);
  delayMicroseconds(1640);
    pulseIR(520);
  delayMicroseconds(560);
    pulseIR(580);
  delayMicroseconds(540);
    pulseIR(540);
  delayMicroseconds(540);
    pulseIR(600);
  delayMicroseconds(480);
    pulseIR(520);
  delayMicroseconds(1660);
    pulseIR(580);
  delayMicroseconds(1620);
    pulseIR(580);
  delayMicroseconds(500);
    pulseIR(520);
  delayMicroseconds(500);
    pulseIR(580);
  delayMicroseconds(1600);
    pulseIR(580);
  delayMicroseconds(1560);
    pulseIR(580);
  delayMicroseconds(1620);
    pulseIR(540);
  delayMicroseconds(38980);
    pulseIR(8860);
  delayMicroseconds(2120);
    pulseIR(620);  }
  
    
  void Cloud2()                                         //Fixture Initialize Cloud2 Mode
 { Serial.println("Cloud Cover 2 Mode Initialized");
    pulseIR(8840);
  delayMicroseconds(4320);
    pulseIR(620);
  delayMicroseconds(480);
    pulseIR(600);
  delayMicroseconds(500);
    pulseIR(540);
  delayMicroseconds(1660);
    pulseIR(580);
  delayMicroseconds(520);
    pulseIR(520);
  delayMicroseconds(560);
    pulseIR(600);
  delayMicroseconds(480);
    pulseIR(620);
  delayMicroseconds(480);
    pulseIR(540);
  delayMicroseconds(540);
    pulseIR(620);
  delayMicroseconds(1600);
    pulseIR(520);
  delayMicroseconds(1660);
    pulseIR(580);
  delayMicroseconds(520);
    pulseIR(520);
  delayMicroseconds(1660);
    pulseIR(600);
  delayMicroseconds(1580);
    pulseIR(620);
  delayMicroseconds(1580);
    pulseIR(540);
  delayMicroseconds(1660);
    pulseIR(600);
  delayMicroseconds(1580);
    pulseIR(600);
  delayMicroseconds(1580);
    pulseIR(600);
  delayMicroseconds(500);
    pulseIR(520);
  delayMicroseconds(1620);
    pulseIR(600);
  delayMicroseconds(500);
    pulseIR(600);
  delayMicroseconds(1600);
    pulseIR(520);
  delayMicroseconds(560);
    pulseIR(580);
  delayMicroseconds(480);
    pulseIR(540);
  delayMicroseconds(540);
    pulseIR(600);
  delayMicroseconds(560);
    pulseIR(520);
  delayMicroseconds(1660);
    pulseIR(580);
  delayMicroseconds(560);
    pulseIR(580);
  delayMicroseconds(1620);
    pulseIR(520);
  delayMicroseconds(560);
    pulseIR(580);
  delayMicroseconds(1600);
    pulseIR(580);
  delayMicroseconds(1640);
    pulseIR(580);
  delayMicroseconds(1620);
    pulseIR(540);
  delayMicroseconds(38980);
    pulseIR(8860);
  delayMicroseconds(2120);
    pulseIR(620);  }
  
  
    void FullSpec()                                  //Fixture Initialize Full Spectrum Mode
{ Serial.println("Full Spectrum Mode Initialized");
    pulseIR(8840);
  delayMicroseconds(4320);
    pulseIR(620);
  delayMicroseconds(480);
    pulseIR(600);
  delayMicroseconds(500);
    pulseIR(540);
  delayMicroseconds(1660);
    pulseIR(580);
  delayMicroseconds(520);
    pulseIR(520);
  delayMicroseconds(560);
    pulseIR(600);
  delayMicroseconds(480);
    pulseIR(620);
  delayMicroseconds(480);
    pulseIR(540);
  delayMicroseconds(540);
    pulseIR(620);
  delayMicroseconds(1600);
    pulseIR(520);
  delayMicroseconds(1660);
    pulseIR(580);
  delayMicroseconds(520);
    pulseIR(520);
  delayMicroseconds(1660);
    pulseIR(600);
  delayMicroseconds(1580);
    pulseIR(620);
  delayMicroseconds(1580);
    pulseIR(540);
  delayMicroseconds(1660);
    pulseIR(600);
  delayMicroseconds(1580);
    pulseIR(600);
  delayMicroseconds(1580);
    pulseIR(600);
  delayMicroseconds(500);
    pulseIR(520);
  delayMicroseconds(480);
    pulseIR(600);
  delayMicroseconds(1580);
    pulseIR(600);
  delayMicroseconds(1600);
    pulseIR(520);
  delayMicroseconds(560);
    pulseIR(580);
  delayMicroseconds(1600);
    pulseIR(540);
  delayMicroseconds(540);
    pulseIR(600);
  delayMicroseconds(560);
    pulseIR(520);
  delayMicroseconds(1660);
    pulseIR(580);
  delayMicroseconds(1580);
    pulseIR(580);
  delayMicroseconds(540);
    pulseIR(520);
  delayMicroseconds(560);
    pulseIR(580);
  delayMicroseconds(1600);
    pulseIR(580);
  delayMicroseconds(480);
    pulseIR(580);
  delayMicroseconds(1620);
    pulseIR(540);
  delayMicroseconds(38980);
    pulseIR(8860);
  delayMicroseconds(2120);
    pulseIR(620);  }
    
    
    
    
        void DawnDusk()                         //F ixture Initialize Dawn/Dusk Mode
{ Serial.println("Dawn/Dusk Mode Initialized");
    pulseIR(8840);
  delayMicroseconds(4320);
    pulseIR(620);
  delayMicroseconds(480);
    pulseIR(600);
  delayMicroseconds(500);
    pulseIR(540);
  delayMicroseconds(1660);
    pulseIR(580);
  delayMicroseconds(520);
    pulseIR(520);
  delayMicroseconds(560);
    pulseIR(600);
  delayMicroseconds(480);
    pulseIR(620);
  delayMicroseconds(480);
    pulseIR(540);
  delayMicroseconds(540);
    pulseIR(620);
  delayMicroseconds(1600);
    pulseIR(520);
  delayMicroseconds(1660);
    pulseIR(580);
  delayMicroseconds(520);
    pulseIR(520);
  delayMicroseconds(1660);
    pulseIR(600);
  delayMicroseconds(1580);
    pulseIR(620);
  delayMicroseconds(1580);
    pulseIR(540);
  delayMicroseconds(1660);
    pulseIR(600);
  delayMicroseconds(1580);
    pulseIR(600);
  delayMicroseconds(1660);
    pulseIR(600);
  delayMicroseconds(1560);
    pulseIR(520);
  delayMicroseconds(480);
    pulseIR(600);
  delayMicroseconds(1580);
    pulseIR(600);
  delayMicroseconds(1600);
    pulseIR(520);
  delayMicroseconds(560);
    pulseIR(580);
  delayMicroseconds(500);
    pulseIR(540);
  delayMicroseconds(540);
    pulseIR(600);
  delayMicroseconds(560);
    pulseIR(520);
  delayMicroseconds(500);
    pulseIR(580);
  delayMicroseconds(1580);
    pulseIR(580);
  delayMicroseconds(540);
    pulseIR(520);
  delayMicroseconds(560);
    pulseIR(580);
  delayMicroseconds(1660);
    pulseIR(580);
  delayMicroseconds(1580);
    pulseIR(580);
  delayMicroseconds(1620);
    pulseIR(540);
  delayMicroseconds(38980);
    pulseIR(8860);
  delayMicroseconds(2120);
    pulseIR(620);  }


----------



## Michael M

crazymittens said:


> Someone in the lighting sub-forum was asking about this...I'm curious if there are any biological benefits to this. Is this cool (it is), or functionally cool?


I can tell you straight up that my african clawed frogs are not fans of the dynamic 'thunderstorm' mode. In fact it scares the crap out of them and sends them swimming around frantically.. they are really skittish animals so maybe fish will not mind as much.

I think the dynamic weather is mostly for the cool factor though.


----------



## O2surplus

Indychus said:


> *Now we're ready to automate a light!*
> 
> Still editing, instructions incoming!


 Is the code you posted intended to be a library file?


----------



## Indychus

O2surplus said:


> Is the code you posted intended to be a library file?


No, but I guess I can package it all together into one. Would probably make it easier to have everything together. I've never had a need to compile a library before, but I'll give it a shot.


----------



## O2surplus

Indychus said:


> No, but I guess I can package it all together into one. Would probably make it easier to have everything together. I've never had a need to compile a library before, but I'll give it a shot.



I was just thinking out loud when I posted, but the thought had occurred to me that all the IR command coding could be placed into a custom library. Since all the "heavy lifting" is done inside the various libraries used in particular sketch, the remaining main sketch could be simplified greatly, and therefore much more user friendly for the end user. Just my thoughts LOL.


----------



## Indychus

O2surplus said:


> I was just thinking out loud when I posted, but the thought had occurred to me that all the IR command coding could be placed into a custom library. Since all the "heavy lifting" is done inside the various libraries used in particular sketch, the remaining main sketch could be simplified greatly, and therefore much more user friendly for the end user. Just my thoughts LOL.


I agree, my coding is very clunky and could be greatly simplified. I'll play around with making a library tonight.


----------



## O2surplus

Indychus said:


> I agree, my coding is very clunky and could be greatly simplified. I'll play around with making a library tonight.



I think the work you've done so far is awesome and I appreciate it very much. I couldn't code my way out of "a wet paper bag", but I'm learning a lot by following your work. LOL I'm good with understanding the electronics/hardware side of Arduino projects, it's the software side that has me baffled. If you ever want to "shrink down" the whole hardware side of this project into a single PcB let me know. I'd love to help.


----------



## mistergreen

I might be missing something but what's sending/receiving the IR calls to control the devices?

Is the arduino sending or receiving?
There's a device missing or something


----------



## Indychus

The Arduino is sending. The factory receiver on the fixture receives. The Arduino acts just like the factory remote, it just does it on its own without you standing there pressing buttons. The receiver on the Arduino is just for decoding unknown signals; it isnt needed once you have the protocol and codes.


----------



## Indychus

O2surplus said:


> I think the work you've done so far is awesome and I appreciate it very much. I couldn't code my way out of "a wet paper bag", but I'm learning a lot by following your work. LOL I'm good with understanding the electronics/hardware side of Arduino projects, it's the software side that has me baffled. If you ever want to "shrink down" the whole hardware side of this project into a single PcB let me know. I'd love to help.


That would be pretty cool... Once I get all of the features nailed down we might can look at a single PCB. I know I still need to add an RTC, and I'm thinking about an Ethernet or WiFi shield to allow remote control from a PC or android phone. I have an NPN transistor circuit in the works also to amplify the IR emitter for better range.


----------



## mistergreen

Ah, I see. This will only work for devices with IR receivers with some logic built in like a TV etc....


----------



## Indychus

Since there's been some question as to how this operates, here's a picture of mine right now running the light on my 55g. You can see I just have it aimed at the factory IR receiver on the Current fixture. The remote still works if you want to manually change the mode, but if you don't touch it, the Arduino automatically changes modes according to the times you set up in the sketch. 

The box underneath the Arduino and Current receiver is an old DSL WiFi box that I have gutted to use as a case for this project.

Note that the range right now is around 2 feet, I only have it this close for accuracy during testing. In the near future, I hope to extend the range to around 10 feet (the same as the factory remote).


----------



## Indychus

mistergreen said:


> Ah, I see. This will only work for devices with IR receivers with some logic built in like a TV etc....


Exactly... I guess it's more accurate to say that I'm automating the remote as opposed to automating the light. I'm hoping this kind of approach is more appealing to those who might not want to open the light fixture up and void their warranty. I already have a 24" LED+ in pieces on my workbench though :hihi:


----------



## Dahammer

Very interesting indeed. Once upon a time, I dabbled a bit with home built PCBs using Eagle software and the photo method. I think adding a RTC is a great idea. I also like the idea of single PCB. But in lieu of that, you could also just do a simple PCB for the IR emitter and whatever other electronics you add to the project and place everything inside a project box to keep it simple and clean.

I wander if you could wire the Arduino in between the fixture's IR receiver and the fixture, allowing the remote to continue to work as well. That way you could hide the controller in a cabinet or something and still use the remote when you wanted too. And you wouldn't need the emitter.

Now I have to order one so I can monkey with it.


----------



## Indychus

The remote still works, until another command is sent from the Arduino, at which point it defaults to the programming again. It would be easy to put the Arduino inline between the receiver and light fixture if you don't mind cutting the factory wires to the receiver, allowing you to hide the controller and still have the receiver exposed. The Arduino outputs 5V on this channel, so you'd just have to make sure 5V is what the fixture needs. I'm pretty sure most PWM signals are 5V, so it should be good.


----------



## Dahammer

Indychus said:


> The remote still works, until another command is sent from the Arduino, at which point it defaults to the programming again. It would be easy to put the Arduino inline between the receiver and light fixture if you don't mind cutting the factory wires to the receiver, allowing you to hide the controller and still have the receiver exposed. The Arduino outputs 5V on this channel, so you'd just have to make sure 5V is what the fixture needs. I'm pretty sure most PWM signals are 5V, so it should be good.


Yeah, that's kind of the direction I was thinking of going with it in order to get everything out of sight. You'd still have the wire to hide though. Did you see this RTC kit?
http://learn.adafruit.com/ds1307-real-time-clock-breakout-board-kit


----------



## Indychus

Dahammer said:


> Yeah, that's kind of the direction I was thinking of going with it in order to get everything out of sight. You'd still have the wire to hide though. Did you see this RTC kit?
> http://learn.adafruit.com/ds1307-real-time-clock-breakout-board-kit


That's pretty cool! I found an off-the-shelf RTC for $4 that should be here tomorrow. I wish I would have seen this before ordering one, as I much prefer to DIY whatever I can.


----------



## Vermino

Indychus said:


> With the Current product announcement and their own controllers out now, there isn't much reason to build your own economically speaking (I was a little shocked at how low their MSRP is), but this does allow you more control and the ability to tailor it exactly to your needs.


Yeah sorry to let that cat out of the bag on that one. There are still people wanting to have more control than a "out of the box" controller though. Anything I buy, I usually tweak like a mad scientist. Love the DIY picture BTW indychus =)


----------



## Indychus

Vermino said:


> Yeah sorry to let that cat out of the bag on that one. There are still people wanting to have more control than a "out of the box" controller though. Anything I buy, I usually tweak like a mad scientist. Love the DIY picture BTW indychus =)


Yeah, I emailed current a few weeks ago and they said they wouldn't release the IR protocol but that they had a controller coming out, so I knew it was coming soon. I didn't expect it to be so affordable though.


----------



## Dahammer

I've got to hand it too you, Indychus, this is an awesome little DIY project. My wife is already cussing you, since I've just found my next "spousal substitute" as she labels my projects.

I'm going to have to do the IR end of it because it's just too cool not too. I can think of all kinds of uses, aside from controlling the light fixture, where I can use this device. If nothing else I can turn on the bedroom TV at 3:00AM a couple times a week to freak the wife out. lol!

I'll probably still end up with one hard wired into the fixture though, so if you reverse engineer the fixture you have torn apart on your bench, be sure and let us know!


----------



## Indychus

My wife feels the same way about my projects... I have so many hobbies its hard to keep track of them. I love the Arduino though, after I bought my first one and realized what it was capable of, I ordered 2 more within a week!


----------



## somewhatshocked

This is a terrific DIY! Way cheaper than buying a $100 controller. (You should link to this in your signature)

I think I'm going to bite the bullet and try this out over the weekend. I'm a huge tech geek but I'm sure I'll have tons of questions... since this will be my first fun with Arduino.

Thanks for sharing all the details.


----------



## Indychus

Glad you've enjoyed it so far... I'm working on a random function to kick it into t-storm mode randomly right now, and hopefully RTC will be here today so I can get that going soon. It still has a long way to go!

Sent from my HTC One X using Tapatalk 4 Beta


----------



## Indychus

RTC just showed up! 

This will allow the unit to keep it's own time without having to reset it any time the power goes out. I'll be back this afternoon hopefully with some details on how to hook it up and some updated code.

This is the TinyRTC V1.1... a DS1307 based real time clock.


----------



## Indychus

Here is the *2.0 beta* version of the new code including random thunderstorms. The code has been tested and compiles, but the randomness and frequency of the storms has yet to be seen. 

*This code is in testing and may cause strange operation until verified.*

This code:


Randomly schedules a time for Storm2 mode.
Allows scheduled storm to proceed if chosen time is after 1 pm. Delays 24 hours and chooses new time if not.
Has random storm duration from 0-3 hours.
Has random after-storm gloom (Dawn/Dusk mode) from 0-1 hours.
Should return to previous mode after storm and gloom conclusion.
Should allow 1-2 storms per week to proceed.



Code:


///////////////////////////////////////////////////////////////////
// Current Satellite LED+ Controller  V2.0 Beta                  //
//   Indychus...Dahammer...mistergreen @ plantedtank.net         //
//   This code is public domain.  Pass it on.                    //
//   Confirmed on Arduino UNO 1.0.5                              //
//   Req. Time, TimeAlarms, DateTime, DateTimeStrings libraries  //
///////////////////////////////////////////////////////////////////

////////////SETUP//////////////////////////////////////////////////
#include <Time.h>
#include <TimeAlarms.h>
#include <DateTime.h>
#include <DateTimeStrings.h>

int IRledPin =  13;                  // Pin location for IR output

void setup()               
{ 
  pinMode(IRledPin, OUTPUT);         // Designate IRledPin as Output
  Serial.begin(9600);                // Connect @ (Baud)
  setTime(11,59,55,7,26,13);          // set time (HR,MIN,SEC,MO,DAY,YR)

  ////////////ALARM FUNCTIONS/////////////////////////////////////////
 
  ThunderStorm ();
 
  Alarm.alarmRepeat(7,00,0, DawnDusk);
  Alarm.alarmRepeat(7,00,0, DawnDusk);
  Alarm.alarmRepeat(9,00,0, Cloud2);     // (HR,MIN,SEC,FUNCTION)
  Alarm.alarmRepeat(9,00,0, Cloud2);
  Alarm.alarmRepeat(13,00,0, FullSpec);
  Alarm.alarmRepeat(13,00,0, FullSpec);
  Alarm.alarmRepeat(15,00,0, Cloud2);
  Alarm.alarmRepeat(15,00,0, Cloud2);
  Alarm.alarmRepeat(19,00,0, DawnDusk);
  Alarm.alarmRepeat(19,00,0, DawnDusk);
  Alarm.alarmRepeat(21,00,0, Night2);
  Alarm.alarmRepeat(21,00,0, Night2);
  Alarm.alarmRepeat(00,00,00, ThunderStorm); 
}   
////////////THUNDERSTORMS///////////////////////////////////////////// 
void ThunderStorm ()
{
  // Called everyday at midnight & randomly schedules a storm between 1 & 9 in the evening
  unsigned long RH = random(0,23);                   // Randomizer for thunderstorm
  unsigned long RM = random(0,59);
  unsigned long RS = random(0,59);
  unsigned long TSDuration = random(0,10800);     // Random T-Storm duration from 0-3hr

  Serial.print("Storm = ");
  Serial.print(RH);
  Serial.print(".");
  Serial.print(RM);
  Serial.print(".");
  Serial.print(RS);
  Serial.println();

  if (RH > 12)                             // If random value is after 1 pm, allow storm
  {
    Alarm.alarmOnce(RH,RM,RS,Storm2);
    if ((RH + (TSDuration/3600)) < 19) {
      // If storm is between 1 & 7
      Alarm.alarmOnce((RH + (TSDuration/3600)),RM,RS,Cloud2);
    }
    else if ((RH + (TSDuration/3600)) < 21) {
      // If storm is between 1 & 9
      Alarm.alarmOnce((RH + (TSDuration/3600)),RM,RS,DawnDusk);
    }
    else {
      Alarm.alarmOnce((RH + (TSDuration/3600)),RM,RS,Night2);
    }
  }
}   


////////////CLOCK///////////////////////////////////////////////////
void  loop(){                       
  digitalClockDisplay();
  Alarm.delay(1000);               // Clock display update frequency (msec)
}

void digitalClockDisplay()          // Digital clock
{ 
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.println(); 
}

void printDigits(int digits)        // Add :
{
  Serial.print(":");
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits);
}


////////////SIGNAL///////////////////////////////////////////////////
// Create Frequency (38khz/26msec)
void pulseIR(long microsecs) 
{  
  cli();                           // kill interupts
  while (microsecs > 0)
  {  
    digitalWrite(IRledPin, HIGH);    // ~3 msec
    delayMicroseconds(7);            // ~delay
    digitalWrite(IRledPin, LOW);     // ~3 msec
    delayMicroseconds(7);            // ~delay
    microsecs -= 26;  
  }
  sei();  
}                           // zombie interupts


////////////FIXTURE FUNCTIONS////////////////////////////////////////
void DeviceID ()
{
  int codes[17][2] = { 
    {
      8840,4320    }
    , 
    {
      620,480    }
    , 
    {
      600,500    }
    , 
    {
      540,1660    }
    , 
    {
      580,520    }
    , 
    {
      520,560    }
    , 
    {
      600,480    }
    , 
    {
      620,480    }
    , 
    {
      540,540    }
    , 
    {
      620,1600    }
    , 
    {
      520,1660    }
    , 
    {
      580,520    }
    , 
    {
      520,1660    }
    , 
    {
      600,1580    }
    , 
    {
      620,1580    }
    , 
    {
      540,1660    }
    , 
    {
      600,1580    } 
  };

  for( int i = 0 ; i < 17; i++) {
    pulseIR(codes[i][0]);
    delayMicroseconds(codes[i][1]);
  }
}

void Footer ()
{
  int codes[3][2] = { 
    {
      580,1620    }
    ,
    {
      540,38980    }
    ,
    {
      8860,2120    } 
  };

  for( int i = 0 ; i < 3; i++) {
    pulseIR(codes[i][0]);
    delayMicroseconds(codes[i][1]);
  }
  pulseIR(620);
}

void IR(int codes[15][2]) 
{
  DeviceID();

  for( int i = 0 ; i < 15; i++) {
    pulseIR(codes[i][0]);
    delayMicroseconds(codes[i][1]);
  }  
  Footer(); 
}

void PowerButton()                             //Fixture power on/off toggle
{
  int codes[15][2] = { 
    {
      600,500    }
    ,
    {
      600,500    }
    ,
    {
      520,560    }
    ,
    {
      600,500    }
    ,
    {
      600,500    }
    ,
    {
      520,560    }
    ,
    {
      580,1620    }
    ,
    {
      540,540    }
    ,
    {
      600,1600    }
    ,
    {
      520,1660    }
    ,
    {
      580,1620    }
    ,
    {
      580,1620    }
    ,
    {
      520,1660    }
    ,
    {
      580,1600    }
    ,
    {
      580,520    } 
  }; 

  Serial.println("Power Toggle");
  IR(codes);
}

void Night2()                                   //Fixture Initialize Night2 Mode
{
  int codes[15][2] = { 
    {
      600,1640    }
    ,
    {
      600,500    }
    ,
    {
      520,560    }
    ,
    {
      600,1620    }
    ,
    {
      600,1640    }
    ,
    {
      520,560    }
    ,
    {
      580,540    }
    ,
    {
      540,540    }
    ,
    {
      600,480    }
    ,
    {
      520,1660    }
    ,
    {
      580,1620    }
    ,
    {
      580,500    }
    ,
    {
      520,500    }
    ,
    {
      580,1600    }
    ,
    {
      580,1560    } 
  }; 

  Serial.println("Night 2 Mode Initialized");
  IR(codes);
}

void Cloud2()                                         //Fixture Initialize Cloud2 Mode
{ 
  int codes[15][2] = { 
    {
      600,1580    }
    ,
    {
      600,500    }
    ,
    {
      520,1620    }
    ,
    {
      600,500    }
    ,
    {
      600,1600    }
    ,
    {
      520,560    }
    ,
    {
      580,480    }
    ,
    {
      540,540    }
    ,
    {
      600,560    }
    ,
    {
      520,1660    }
    ,
    {
      580,560    }
    ,
    {
      580,1620    }
    ,
    {
      520,560    }
    ,
    {
      580,1600    }
    ,
    {
      580,1640    } 
  };

  Serial.println("Cloud Cover 2 Mode Initialized");
  IR(codes);
}

void FullSpec()                                  //Fixture Initialize Full Spectrum Mode
{
  int codes[15][2] = { 
    {
      600,1580    }
    ,
    {
      600,500    }
    ,
    {
      520,480    }
    ,
    {
      600,1580    }
    ,
    {
      600,1600    }
    ,
    {
      520,560    }
    ,
    {
      580,1600    }
    ,
    {
      540,540    }
    ,
    {
      600,560    }
    ,
    {
      520,1660    }
    ,
    {
      580,1580    }
    ,
    {
      580,540    }
    ,
    {
      520,560    }
    ,
    {
      580,1600    }
    ,
    {
      580,480    } 
  };

  Serial.println("Full Spectrum Mode Initialized");
  IR(codes);
}

void DawnDusk()                         // Fixture Initialize Dawn/Dusk Mode
{
  int codes[15][2] = { 
    {
      600,1660    }
    ,
    {
      600,1560    }
    ,
    {
      520,480    }
    ,
    {
      600,1580    }
    ,
    {
      600,1600    }
    ,
    {
      520,560    }
    ,
    {
      580,500    }
    ,
    {
      540,540    }
    ,
    {
      600,560    }
    ,
    {
      520,500    }
    ,
    {
      580,1580    }
    ,
    {
      580,540    }
    ,
    {
      520,560    }
    ,
    {
      580,1660    }
    ,
    {
      580,1580    } 
  };

  Serial.println("Dawn/Dusk Mode Initialized");
  IR(codes);
}

void Storm2()                         //Fixture Initialize Storm2 Mode
{
  int codes[15][2] = { 
    {
      600,1660    }
    ,
    {
      600,540    }
    ,
    {
      520,480    }
    ,
    {
      600,500    }
    ,
    {
      600,1600    }
    ,
    {
      520,560    }
    ,
    {
      580,500    }
    ,
    {
      540,540    }
    ,
    {
      600,560    }
    ,
    {
      520,1660    }
    ,
    {
      580,1580    }
    ,
    {
      580,1620    }
    ,
    {
      520,560    }
    ,
    {
      580,1660    }
    ,
    {
      580,1580    } 
  };

  Serial.println("Storm2 Mode Initialized");
  IR(codes);
}


----------



## mistergreen

if the delayMicroseconds & pulseIR pair are always the same count, you can probably put them in an array and then loop through. It'll make things more tidy.


something like



Code:


int storm1_array[][] = { {1660,600}, {1560,520}, .... }

for( int i = 0 ; i < 15; i++) {
    delayMicroseconds(storm1_array[i][0]);
    pulseIR(storm1_array[i][1]);
}


----------



## Indychus

Nice, thanks for the input. For each set, the first half of the values is the device ID and never changes, so that should work fine for those. I'll go back through and clean that up this weekend.

Sent from my HTC One X using Tapatalk 4 Beta


----------



## Dahammer

You could take it even further and just have a single function that you call to process the lighting changes, passing an array with the codes in it to the function when you call it. I think I'd also define all of the variables that users may want/need to modify in a library file. That way they'll be easy to find and you want have to go searching through all of the code looking for them.

I've order my fixture, now I just have to get an Arduino and some other parts.


----------



## Indychus

I told you guys my programming was clunky haha. I'm pretty good at getting it to work, but making it elegant and user friendly takes me quite a while. I really need to get the RTC hooked up and working so I don't have to reset the time every time I upload a new sketch. Once that's done my main priority will be decoding the rest of the remote functions and cleaning the code up. I'd like to make a library with everything in it, but I don't have much experience making libraries and I keep getting some crazy errors.

V2.0 is running on my 55 right now, so far so good. It has scheduled a storm for around 5pm tomorrow. I have manually tested the storm's scheduling but tomorrow will be the first automated run.

Sent from my HTC One X


----------



## Dahammer

Haha! I hear that. If it works it works, eye candy is .... well eye candy. I haven't written much code other an occasional Perl script in years but I'll play with it some when I get a chance. 

I have my Arduino Uno and companions on their way.


----------



## mistergreen

If you're interested in Dahammer's suggestion it would be something like this



Code:


int storm1_array[][] = { {1660,600}, {1560,520}, .... }
int dawn_array[][] = { {1600,400}, {1450,220}, .... }

void Storm1() {
    IR(storm1_array);
}

void Dawn() {
   IR(dawn_array);
}

void IR(int inArray[][]) {
    for( int i = 0 ; i < 15; i++) {
      delayMicroseconds(inArray[i][0]);
      pulseIR(inArray[i][1]);
    }
}


----------



## Indychus

Thanks guys, I really appreciate the help. I will be out of town most of the weekend, but l will try to implement these suggestions early next week. I'm glad some of you have fixtures and/or Arduinos on the way to test this out on several lights. 

Current has said that their IR protocol will be changing soon to help reduce interference with TVs, etc.... But it's easy to hack new protocols. I'm interested to see if different fixtures use the same device ID or not.

Sent from my HTC One X


----------



## Dahammer

The below code saves 2172 bytes. I haven't tested it, so be warned, but it does compile.



Code:


////////////FIXTURE FUNCTIONS////////////////////////////////////////
void DeviceID ()
{
  int codes[17][2] = { {8840,4320}, 
		       {620,480}, 
		       {600,500}, 
		       {540,1660}, 
		       {580,520}, 
		       {520,560}, 
		       {600,480}, 
		       {620,480}, 
		       {540,540}, 
		       {620,1600}, 
		       {520,1660}, 
		       {580,520}, 
		       {520,1660}, 
		       {600,1580}, 
		       {620,1580}, 
		       {540,1660}, 
		       {600,1580} };

  for( int i = 0 ; i < 17; i++) {
    pulseIR(codes[i][0]);
    delayMicroseconds(codes[i][1]);
  }
}

void Footer ()
{
  int codes[3][2] = { {580,1620},
                      {540,38980},
                      {8860,2120} };
  	                            
  for( int i = 0 ; i < 3; i++) {
    pulseIR(codes[i][0]);
    delayMicroseconds(codes[i][1]);
    }
    pulseIR(620);
}

void PowerButton()                             //Fixture power on/off toggle
{
 int codes[15][2] = { {600,500},
                      {600,500},
                      {520,560},
                      {600,500},
                      {600,500},
                      {520,560},
                      {580,1620},
                      {540,540},
                      {600,1600},
                      {520,1660},
                      {580,1620},
                      {580,1620},
                      {520,1660},
                      {580,1600},
                      {580,520} }; 
  
  Serial.println("Power Toggle");
  DeviceID();

  for( int i = 0 ; i < 15; i++) {
    pulseIR(codes[i][0]);
    delayMicroseconds(codes[i][1]);
  }  
  Footer(); 
}
 
void Night2()                                   //Fixture Initialize Night2 Mode
{
  int codes[15][2] = { {600,1640},
                       {600,500},
                       {520,560},
                       {600,1620},
                       {600,1640},
                       {520,560},
                       {580,540},
                       {540,540},
                       {600,480},
                       {520,1660},
                       {580,1620},
                       {580,500},
                       {520,500},
                       {580,1600},
                       {580,1560} }; 
   
  Serial.println("Night 2 Mode Initialized");
  DeviceID();

  for( int i = 0 ; i < 15; i++) {
    pulseIR(codes[i][0]);
    delayMicroseconds(codes[i][1]);
  }
  Footer();
}
     
void Cloud2()                                         //Fixture Initialize Cloud2 Mode
{ 
   int codes[15][2] = { {600,1580},
                        {600,500},
                        {520,1620},
                        {600,500},
                        {600,1600},
                        {520,560},
                        {580,480},
                        {540,540},
                        {600,560},
                        {520,1660},
                        {580,560},
                        {580,1620},
                        {520,560},
                        {580,1600},
                        {580,1640} };
                        
  Serial.println("Cloud Cover 2 Mode Initialized");
  DeviceID();

  for( int i = 0 ; i < 15; i++) {
    pulseIR(codes[i][0]);
    delayMicroseconds(codes[i][1]);
  }
  Footer();
}
   
void FullSpec()                                  //Fixture Initialize Full Spectrum Mode
{
  int codes[15][2] = { {600,1580},
                       {600,500},
                       {520,480},
                       {600,1580},
                       {600,1600},
                       {520,560},
                       {580,1600},
                       {540,540},
                       {600,560},
                       {520,1660},
                       {580,1580},
                       {580,540},
                       {520,560},
                       {580,1600},
                       {580,480} };
  
  Serial.println("Full Spectrum Mode Initialized");
  DeviceID();

  for( int i = 0 ; i < 15; i++) {
    pulseIR(codes[i][0]);
    delayMicroseconds(codes[i][1]);
  }
  Footer();
}
    
void DawnDusk()                         // Fixture Initialize Dawn/Dusk Mode
{
  int codes[15][2] = { {600,1660},
                       {600,1560},
                       {520,480},
                       {600,1580},
                       {600,1600},
                       {520,560},
                       {580,500},
                       {540,540},
                       {600,560},
                       {520,500},
                       {580,1580},
                       {580,540},
                       {520,560},
                       {580,1660},
                       {580,1580} };
    
  Serial.println("Dawn/Dusk Mode Initialized");
  DeviceID();

  for( int i = 0 ; i < 15; i++) {
    pulseIR(codes[i][0]);
    delayMicroseconds(codes[i][1]);
  }
  Footer();
}

void Storm2()                         //Fixture Initialize Storm2 Mode
{
  int codes[15][2] = { {600,1660},
                       {600,540},
                       {520,480},
                       {600,500},
                       {600,1600},
                       {520,560},
                       {580,500},
                       {540,540},
                       {600,560},
                       {520,1660},
                       {580,1580},
                       {580,1620},
                       {520,560},
                       {580,1660},
                       {580,1580} };
    
  Serial.println("Storm2 Mode Initialized");
  DeviceID();

  for( int i = 0 ; i < 15; i++) {
    pulseIR(codes[i][0]);
    delayMicroseconds(codes[i][1]);
  }
  Footer();
}


----------



## Dahammer

Using mistergreen's suggestion saves another 272 bytes. You probably won't ever use all of the Uno's memory, but you can't ever tell. I left the code arrays local instead of making them global. If you wanted to make them global you could do that though, but I don't see any benefit other than it would allow you to keep all of them in one spot like at the top of the source. Wish I had an Arduino to play with. Might have to make trip to Radio Shack!



Code:


////////////FIXTURE FUNCTIONS////////////////////////////////////////
void DeviceID ()
{
  int codes[17][2] = { {8840,4320}, 
		       {620,480}, 
		       {600,500}, 
		       {540,1660}, 
		       {580,520}, 
		       {520,560}, 
		       {600,480}, 
		       {620,480}, 
		       {540,540}, 
		       {620,1600}, 
		       {520,1660}, 
		       {580,520}, 
		       {520,1660}, 
		       {600,1580}, 
		       {620,1580}, 
		       {540,1660}, 
		       {600,1580} };

  for( int i = 0 ; i < 17; i++) {
    pulseIR(codes[i][0]);
    delayMicroseconds(codes[i][1]);
  }
}

void Footer ()
{
  int codes[3][2] = { {580,1620},
                      {540,38980},
                      {8860,2120} };
  	                            
  for( int i = 0 ; i < 3; i++) {
    pulseIR(codes[i][0]);
    delayMicroseconds(codes[i][1]);
    }
    pulseIR(620);
}

void IR(int codes[15][2]) 
{
  DeviceID();

  for( int i = 0 ; i < 15; i++) {
    pulseIR(codes[i][0]);
    delayMicroseconds(codes[i][1]);
  }  
  Footer(); 
}

void PowerButton()                             //Fixture power on/off toggle
{
 int codes[15][2] = { {600,500},
                      {600,500},
                      {520,560},
                      {600,500},
                      {600,500},
                      {520,560},
                      {580,1620},
                      {540,540},
                      {600,1600},
                      {520,1660},
                      {580,1620},
                      {580,1620},
                      {520,1660},
                      {580,1600},
                      {580,520} }; 
  
  Serial.println("Power Toggle");
  IR(codes);
}
 
void Night2()                                   //Fixture Initialize Night2 Mode
{
  int codes[15][2] = { {600,1640},
                       {600,500},
                       {520,560},
                       {600,1620},
                       {600,1640},
                       {520,560},
                       {580,540},
                       {540,540},
                       {600,480},
                       {520,1660},
                       {580,1620},
                       {580,500},
                       {520,500},
                       {580,1600},
                       {580,1560} }; 
   
  Serial.println("Night 2 Mode Initialized");
  IR(codes);
}
     
void Cloud2()                                         //Fixture Initialize Cloud2 Mode
{ 
   int codes[15][2] = { {600,1580},
                        {600,500},
                        {520,1620},
                        {600,500},
                        {600,1600},
                        {520,560},
                        {580,480},
                        {540,540},
                        {600,560},
                        {520,1660},
                        {580,560},
                        {580,1620},
                        {520,560},
                        {580,1600},
                        {580,1640} };
                        
  Serial.println("Cloud Cover 2 Mode Initialized");
  IR(codes);
}
   
void FullSpec()                                  //Fixture Initialize Full Spectrum Mode
{
  int codes[15][2] = { {600,1580},
                       {600,500},
                       {520,480},
                       {600,1580},
                       {600,1600},
                       {520,560},
                       {580,1600},
                       {540,540},
                       {600,560},
                       {520,1660},
                       {580,1580},
                       {580,540},
                       {520,560},
                       {580,1600},
                       {580,480} };
  
  Serial.println("Full Spectrum Mode Initialized");
  IR(codes);
}
    
void DawnDusk()                         // Fixture Initialize Dawn/Dusk Mode
{
  int codes[15][2] = { {600,1660},
                       {600,1560},
                       {520,480},
                       {600,1580},
                       {600,1600},
                       {520,560},
                       {580,500},
                       {540,540},
                       {600,560},
                       {520,500},
                       {580,1580},
                       {580,540},
                       {520,560},
                       {580,1660},
                       {580,1580} };
    
  Serial.println("Dawn/Dusk Mode Initialized");
  IR(codes);
}

void Storm2()                         //Fixture Initialize Storm2 Mode
{
  int codes[15][2] = { {600,1660},
                       {600,540},
                       {520,480},
                       {600,500},
                       {600,1600},
                       {520,560},
                       {580,500},
                       {540,540},
                       {600,560},
                       {520,1660},
                       {580,1580},
                       {580,1620},
                       {520,560},
                       {580,1660},
                       {580,1580} };
    
  Serial.println("Storm2 Mode Initialized");
  IR(codes);
}


----------



## mistergreen

Making it global helps to organize in one place like if you add another device. You can also move those arrays into another file too like a library. You can also destroy the array too from anywhere if you need extra memory.


----------



## Dahammer

mistergreen said:


> Making it global helps to organize in one place like if you add another device. You can also move those arrays into another file too like a library. You can also destroy the array too from anywhere if you need extra memory.


True. He was having issues moving code to a library earlier, not sure if he worked that out or not.

I've been working on it in preparation of getting mine going, but I don't have any way of testing it yet, so I'm hesitant to post it. But here is what I have so far if anyone else wants to play with it. Just be WARNED, it may not work. I moved the code arrays to a library file (LED_Controller.h)(wasn't sure what IndyChus was going to name this project) and added code to make use of an RTC. The RTC code requires a library from Adafruit and there is a link at the top of the source. It compiles fine for me, but that's all I've done with it. 

Again, I've got several years of rust on my C/C++ hacking, so I'm likely to have made an error somewhere.



Code:


///////////////////////////////////////////////////////////////////
// Current Satellite LED+ Controller                             //
//   Ken Bunton (Indychus)                                       //
//   This code is public domain.  Pass it on.                    //
//   Confirmed on Arduino UNO 1.0.5                              //
//   Req. Time, TimeAlarms, DateTime, DateTimeStrings libraries  //
///////////////////////////////////////////////////////////////////

/*
BETA!! NOT TESTED!!!!!!
Requires RTClib & an RTC board for the Arduino to get RTC function
https://github.com/adafruit/RTClib/archive/master.zip
*/

////////////SETUP//////////////////////////////////////////////////
#include <Wire.h>
#include "RTClib.h"
#include "Time.h"
#include "TimeAlarms.h"
#include "LED_Controller.h"


RTC_DS1307 RTC;

int IRledPin =  13;                  // Pin location for IR output

void setup()               
{ 
  pinMode(IRledPin, OUTPUT);         // Designate IRledPin as Output
  Serial.begin(9600);                // Connect @ (Baud)    
  Wire.begin();
  RTC.begin();
  
  if (! RTC.isrunning()) {
    Serial.println("RTC is NOT running!");
    // following line sets the RTC to the date & time this sketch was compiled
    RTC.adjust(DateTime(__DATE__, __TIME__));
  }
  setSyncProvider(syncProvider);     //reference our syncProvider function instead of RTC_DS1307::get()

////////////ALARM FUNCTIONS/////////////////////////////////////////
  Alarm.alarmRepeat(7,00,0, DawnDusk);
  Alarm.alarmRepeat(7,00,1, DawnDusk);
  Alarm.alarmRepeat(9,00,0, Cloud2);     // (HR,MIN,SEC,FUNCTION)
  Alarm.alarmRepeat(9,00,1, Cloud2);
  Alarm.alarmRepeat(13,00,0, FullSpec);
  Alarm.alarmRepeat(13,00,1, FullSpec);
  Alarm.alarmRepeat(15,00,0, Cloud2);
  Alarm.alarmRepeat(15,00,1, Cloud2);
  Alarm.alarmRepeat(19,00,0, DawnDusk);
  Alarm.alarmRepeat(19,00,1, DawnDusk);
  Alarm.alarmRepeat(21,00,0, Night2);
  Alarm.alarmRepeat(21,00,1, Night2);    
  
  
 ////////////THUNDERSTORMS///////////////////////////////////////////// 
  unsigned long RH = random(0,23);                   // Randomizer for thunderstorm
  unsigned long RM = random(0,59);
  unsigned long RS = random(0,59);
  unsigned long TSDuration = random(0,10800000);     // Random T-Storm duration from 0-3hr
  unsigned long RecoveryDelay = random(0,3600000);   // Random after storm gloominess from 0-1hr
  
  Serial.print("RH = ");
  Serial.print(RH);
  Serial.println();
  
    
  if (RH > 12)                             // If random value is after 1 pm, allow storm
  {Alarm.alarmOnce(RH,RM,RS,Storm2);
  
  if (12 < (RH + (TSDuration/3600000)) < 19) 
  {Alarm.alarmOnce((RH + (TSDuration/3600000) + (RecoveryDelay/3600000)),Cloud2);}
  
  if (12 <= (RH + (TSDuration/3600000)) < 21)
  {Alarm.alarmOnce((RH + (TSDuration/3600000)),DawnDusk);}
  
  if ((RH + (TSDuration/3600000)) >= 21)
  {Alarm.alarmOnce((RH + (TSDuration/3600000)),Night2);}
  
  
      RH = random(0,23);                                  // Reset random values after storm
      RM = random(0,59);
      RS = random(0,59);
      TSDuration = random(0,10800000);
      RecoveryDelay = random(0,3600000);}
      
   
if (RH <= 12)                                           // Delay when storm = FALSE
{Alarm.alarmOnce(0,0,0,reset);}}
  
void reset(){
  unsigned long RH = random(0,23);                   // Randomizer for thunderstorm
  unsigned long RM = random(0,59);
  unsigned long RS = random(0,59);
  unsigned long TSDuration = random(0,10800000);     // Random T-Storm duration from 0-3hr
  unsigned long RecoveryDelay = random(0,3600000);}   // Random after storm gloominess from 0-1hr} 


////////////CLOCK///////////////////////////////////////////////////
void  loop(){                       
  digitalClockDisplay();
  Alarm.delay(3600000/4); }              // Clock display update frequency (msec)

time_t syncProvider()     //this does the same thing as RTC_DS1307::get()
{
  return RTC.now().unixtime();
}

void digitalClockDisplay()          // Digital clock
{ Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.println(); }

void printDigits(int digits)        // Add :
{Serial.print(":");
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits);}

  
////////////SIGNAL///////////////////////////////////////////////////
// Create Frequency (38khz/26msec)
void pulseIR(long microsecs) 
{  cli();                           // kill interupts
  while (microsecs > 0)
{  digitalWrite(IRledPin, HIGH);    // ~3 msec
   delayMicroseconds(7);            // ~delay
   digitalWrite(IRledPin, LOW);     // ~3 msec
   delayMicroseconds(7);            // ~delay
    microsecs -= 26;  }
sei();  }                           // zombie interupts


////////////FIXTURE FUNCTIONS////////////////////////////////////////
void DeviceID ()
{
  for( int i = 0 ; i < 17; i++) {
    pulseIR(DeviceID_codes[i][0]);
    delayMicroseconds(DeviceID_codes[i][1]);
  }
}

void Footer ()
{
 for( int i = 0 ; i < 3; i++) {
    pulseIR(Footer_codes[i][0]);
    delayMicroseconds(Footer_codes[i][1]);
    }
    pulseIR(620);
}

void IR(int codes[15][2]) 
{
  DeviceID();

  for( int i = 0 ; i < 15; i++) {
    pulseIR(codes[i][0]);
    delayMicroseconds(codes[i][1]);
  }  
  Footer(); 
}

void PowerButton()                             //Fixture power on/off toggle
{
  Serial.println("Power Toggle");
  IR(PowerButton_codes);
}
 
void Night2()                                   //Fixture Initialize Night2 Mode
{
  Serial.println("Night 2 Mode Initialized");
  IR(Night2_codes);
}
     
void Cloud2()                                         //Fixture Initialize Cloud2 Mode
{
  Serial.println("Cloud Cover 2 Mode Initialized");
  IR(Cloud2_codes);
}
   
void FullSpec()                                  //Fixture Initialize Full Spectrum Mode
{
  Serial.println("Full Spectrum Mode Initialized");
  IR(FullSpec_codes);
}
    
void DawnDusk()                         // Fixture Initialize Dawn/Dusk Mode
{
  Serial.println("Dawn/Dusk Mode Initialized");
  IR(DawnDusk_codes);
}

void Storm2()                         //Fixture Initialize Storm2 Mode
{
  Serial.println("Storm2 Mode Initialized");
  IR(Storm2_codes);
}

Here is the library file:


Code:


//   LED_Controller.h

int DeviceID_codes[17][2] = { {8840,4320},{620,480},{600,500},{540,1660},{580,520}, 
                              {520,560},{600,480},{620,480},{540,540},{620,1600}, 
                              {520,1660},{580,520},{520,1660},{600,1580},{620,1580}, 
                              {540,1660},{600,1580} };

int Footer_codes[3][2] = { {580,1620},{540,38980},{8860,2120} };
                           
int PowerButton_codes[15][2] = { {600,500},{600,500},{520,560},{600,500},{600,500},
                                 {520,560},{580,1620},{540,540},{600,1600},{520,1660},
                                 {580,1620},{580,1620},{520,1660},{580,1600},{580,520} };
                                  
int Night2_codes[15][2] = { {600,1640},{600,500},{520,560},{600,1620},{600,1640},
                            {520,560},{580,540},{540,540},{600,480},{520,1660},
                            {580,1620},{580,500},{520,500},{580,1600},{580,1560} };
                                  
int Cloud2_codes[15][2] = { {600,1580},{600,500},{520,1620},{600,500},{600,1600},
                            {520,560},{580,480},{540,540},{600,560},{520,1660},
                            {580,560},{580,1620},{520,560},{580,1600},{580,1640} };  

int FullSpec_codes[15][2] = { {600,1580},{600,500},{520,480},{600,1580},{600,1600},
                              {520,560},{580,1600},{540,540},{600,560},{520,1660},
                              {580,1580},{580,540},{520,560},{580,1600},{580,480} };
                              
int DawnDusk_codes[15][2] = { {600,1660},{600,1560},{520,480},{600,1580},{600,1600},
                              {520,560},{580,500},{540,540},{600,560},{520,500},
                              {580,1580},{580,540},{520,560},{580,1660},{580,1580} };
                              
int Storm2_codes[15][2] = { {600,1660},{600,540},{520,480},{600,500},{600,1600},
                            {520,560},{580,500},{540,540},{600,560},{520,1660},
                            {580,1580},{580,1620},{520,560},{580,1660},{580,1580} };

Also, I don't see how the thunderstorm code can work as is if the random hour is <= 12. That last alarm calls reset() and it looks like reset() attempts to change variables that are local to setup(). And setup() doesn't execute any time other than at startup (as I understand the Arduino), so you'd probably need the thunderstorm code in it's on function, so you could call it with the event to re-randomize them.


----------



## mistergreen

I'll write up a quick tut on how to write libraries tomorrow when I get the chance.


Sent from my iPad using Tapatalk HD


----------



## Dahammer

I noticed some errors and edited my previous post.


----------



## Indychus

Wow! You guys are a tremendous help. I will be able to merge your code into mine and test it tomorrow when I get home. 

In the meantime, here's some output that my PC at home just emailed me:

RH = 17
RH = 8
11:59:00
12:14:00
12:29:00
12:44:00
12:59:00
Full Spectrum Mode Initialized
Full Spectrum Mode Initialized
13:14:00
13:29:00
13:44:00
13:59:00
14:14:00
14:29:00
14:44:00
14:59:00
Cloud Cover 2 Mode Initialized
Cloud Cover 2 Mode Initialized
15:14:00
15:29:00
15:44:00
15:59:00
16:14:00
16:29:00
16:44:00
16:59:00
Storm2 Mode Initialized
17:14:00
17:29:00
17:44:00
17:59:00
Cloud Cover 2 Mode Initialized
Dawn/Dusk Mode Initialized
18:14:00
18:29:00
18:44:00
18:59:00
Dawn/Dusk Mode Initialized
Dawn/Dusk Mode Initialized
19:14:00
19:29:00
19:44:00
19:59:00
20:14:00
20:29:00
20:44:00
20:59:00
Night 2 Mode Initialized
Night 2 Mode Initialized
21:14:00
21:29:00
21:44:00
21:59:00
...
...
5:59:00
6:14:00
6:29:00
6:44:00
6:59:00
Dawn/Dusk Mode Initialized
Dawn/Dusk Mode Initialized
7:14:00
7:29:00
7:44:00
7:59:00
8:14:00
8:29:00
8:44:00
8:59:00
Cloud Cover 2 Mode Initialized
Cloud Cover 2 Mode Initialized
9:14:00
9:29:00
9:44:00
9:59:00
...
...
12:14:00
12:29:00
12:44:00

You can see that it triggered a successful storm yesterday a little after 5 pm, then recovered and continued into normal operation again. It did fire the cloud2 and dawn/dusk modes after the storm backwards for some reason; I'll troubleshoot that tonight.


----------



## Indychus

I'll look into the reset function issue you point out as well... It did reset this morning, but it was only after a storm (RH >12).

Sent from my HTC One X


----------



## Dahammer

Yeah, I think there are issues with the thunderstorm code. The reason it fired backwards is probably here:


Code:


  if (12 < (RH + (TSDuration/3600000)) < 19) 
  {Alarm.alarmOnce((RH + (TSDuration/3600000) + (RecoveryDelay/3600000)),Cloud2);}
  
  if (12 <= (RH + (TSDuration/3600000)) < 21)
  {Alarm.alarmOnce((RH + (TSDuration/3600000)),DawnDusk);}

First, I don't think this works as you are intending. RH has to be greater than 12 to reach the above code. Both of the expressions are always TRUE. First it compares 12 to ((RH + (TSDuration/3600000)), which is going to be true otherwise we wouldn't be executing this code. TRUE becomes a 1 and then 1 is less than 19 & 21, so that's also true, so it sets both alarms. 

The reason they went in reverse is because RecoveryDelay is added to alarm on the for the clouds. I'm not sure I understand exactly are you wanting to do here. 

Could also use a toggle switch to turn on/off features like the thunderstorm feature. That would be neat.


----------



## Indychus

I'm trying to get it to always do dawn/dusk for a random time after the storm, then return to the previous mode... cloud2 before 19.00 and night2 after 21.00. from 19.00 to 21.00 it would just stay in dawn/dusk.

**Edited code in above post


----------



## Indychus

Ok, edited it once again... it seems to be working right (it compiles and schedules a random storm)... since it's only psuedo-random I get an RH of 17 again to start with. Still not sure about scheduling the dawndusk and cloud2 when (RH + TSDuration) is between 19 and 21. If I manually set RH to any value outside of this range it seems to work as intended. See what you think.


----------



## Dahammer

Setting the storm time variables to global variables isn't really doing anything. alarmOnce uses the values you call it with to schedule an alarm. Changing those values after the call isn't going to have any effect on the scheduled alarm.

As is, you should only get 1 storm (if that) and then never see it again. This is because the setup() code is only ran once, only at startup, so your call to it in setup() never happens again after the Ardunio starts up. You could just add an alarmRepeat call to storm() in setup so it's called every day. Then you'd get a chance for a storm every day. If you don't want a chance of storm every day, then you can use dowDayofweek in your call to alarmRepeat to only do it on certain days, like dowSaturday for Saturdays and etc.


----------



## Dahammer

Indychus said:


> Ok, edited it once again... it seems to be working right (it compiles and schedules a random storm)... since it's only psuedo-random I get an RH of 17 again to start with. Still not sure about scheduling the dawndusk and cloud2 when (RH + TSDuration) is between 19 and 21. If I manually set RH to any value outside of this range it seems to work as intended. See what you think.


I think I see what you are trying to do. You want the storms to last for 0-3 hours and always be followed up by clouds if the time is between 13 & 19. If it's between 19 & 21, you want the storm followed by dawndusk. If it's after 21, you want it followed by night. This correct?


----------



## Indychus

Dahammer said:


> I think I see what you are trying to do. You want the storms to last for 0-3 hours and always be followed up by clouds if the time is between 13 & 19. If it's between 19 & 21, you want the storm followed by dawndusk. If it's after 21, you want it followed by night. This correct?


Right. I added the random storm variables to the global variables just for the first time... after the first storm, it should always use the variables from the reset function. I need to call storm at the end of reset i think....


----------



## mistergreen

When you need things to happen in sequence it's good to use a 'finite state machine' coding method. It's basically, 'switches' to turn things on & off.

You can use 'if/then' conditions but easier to see/read with 'switch/case'.

It'll look something like this



Code:


void loop()
{
  static int state = 1; // initial state is 1, the "idle" state.
  static unsigned long ts;  // To store the "current" time in for delays.

  switch(state)
  {
    case 1:
      // We don't need to do anything here, waiting for a forced state change.
      break;
    case 2:
      // turn on storm
      ts = millis();  // Remember the current time
      state = 3;  // Move to the next state
      break;
    case 3:
      // If 19 seconds has passed, then move on to the next state.
      if(millis() > ts + 19000)
      {
        state = 4;
      }
      break;
    case 4:
       //turn on dawn - randomly or not. You can use a random function
      // like 
      // if(random(4) == 1) callDawnFunction
       break;
    default:
      state = 1;
      break;
   }
}


----------



## Dahammer

Try this, less the RTC code. I also eliminated the reset function and globals to store the times in.



Code:


void setup()               
{ 
  pinMode(IRledPin, OUTPUT);         // Designate IRledPin as Output
  Serial.begin(9600);                // Connect @ (Baud)    
  Wire.begin();
  RTC.begin();
  
  if (! RTC.isrunning()) {
    Serial.println("RTC is NOT running!");
    // following line sets the RTC to the date & time this sketch was compiled
    RTC.adjust(DateTime(__DATE__, __TIME__));
  }
  setSyncProvider(syncProvider);     //reference our syncProvider function instead of RTC_DS1307::get()

////////////ALARM FUNCTIONS/////////////////////////////////////////
  Alarm.alarmRepeat(7,00,0, DawnDusk);
  Alarm.alarmRepeat(7,00,1, DawnDusk);
  Alarm.alarmRepeat(9,00,0, Cloud2);     // (HR,MIN,SEC,FUNCTION)
  Alarm.alarmRepeat(9,00,1, Cloud2);
  Alarm.alarmRepeat(11,00,0, FullSpec);
  Alarm.alarmRepeat(11,00,1, FullSpec);
  Alarm.alarmRepeat(15,00,0, Cloud2);
  Alarm.alarmRepeat(15,00,1, Cloud2);
  Alarm.alarmRepeat(19,00,0, DawnDusk);
  Alarm.alarmRepeat(19,00,1, DawnDusk);
  Alarm.alarmRepeat(21,00,0, Night2);
  Alarm.alarmRepeat(21,00,1, Night2);
  Alarm.alarmRepeat(0,0,0, ThunderStorm);

  ThunderStorm(); // Schedule the first storm
  
}

void ThunderStorm ()
{
  // Called everyday at midnight & randomly schedules a storm between 1 & 9 in the evening
  unsigned long RH = random(0,23);                   // Randomizer for thunderstorm
  unsigned long RM = random(0,59);
  unsigned long RS = random(0,59);
  unsigned long TSDuration = random(0,10800);     // Random T-Storm duration from 0-3hr
  unsigned long RecoveryDelay = random(0,3600);   // Random after storm gloominess from 0-1hr
  
  Serial.print("RH = ");
  Serial.print(RH);
  Serial.println();
      
  if (RH > 12)                             // If random value is after 1 pm, allow storm
    {
      Alarm.alarmOnce(RH,RM,RS,Storm2);
      if ((RH + (TSDuration/3600)) < 19) {
        // If storm is between 1 & 7, follow with clouds
        Alarm.alarmOnce((RH + (TSDuration/3600)),RM,RS,Cloud2);
      }
      else if ((RH + (TSDuration/3600)) < 21) {
        // If storm is between 7 & 9, follow with dawndusk
        Alarm.alarmOnce((RH + (TSDuration/3600)),RM,RS,DawnDusk);
        }
      else {
        Alarm.alarmOnce((RH + (TSDuration/3600)),RM,RS,Night2);
      }
    }
}


----------



## Indychus

Dahammer said:


> Try this, less the RTC code.
> 
> 
> 
> Code:
> 
> 
> void setup()
> {
> pinMode(IRledPin, OUTPUT);         // Designate IRledPin as Output
> Serial.begin(9600);                // Connect @ (Baud)
> Wire.begin();
> RTC.begin();
> 
> if (! RTC.isrunning()) {
> Serial.println("RTC is NOT running!");
> // following line sets the RTC to the date & time this sketch was compiled
> RTC.adjust(DateTime(__DATE__, __TIME__));
> }
> setSyncProvider(syncProvider);     //reference our syncProvider function instead of RTC_DS1307::get()
> 
> ////////////ALARM FUNCTIONS/////////////////////////////////////////
> Alarm.alarmRepeat(7,00,0, DawnDusk);
> Alarm.alarmRepeat(7,00,1, DawnDusk);
> Alarm.alarmRepeat(9,00,0, Cloud2);     // (HR,MIN,SEC,FUNCTION)
> Alarm.alarmRepeat(9,00,1, Cloud2);
> Alarm.alarmRepeat(11,00,0, FullSpec);
> Alarm.alarmRepeat(11,00,1, FullSpec);
> Alarm.alarmRepeat(15,00,0, Cloud2);
> Alarm.alarmRepeat(15,00,1, Cloud2);
> Alarm.alarmRepeat(19,00,0, DawnDusk);
> Alarm.alarmRepeat(19,00,1, DawnDusk);
> Alarm.alarmRepeat(21,00,0, Night2);
> Alarm.alarmRepeat(21,00,1, Night2);
> Alarm.alarmRepeat(0,0,0, ThunderStorm);
> 
> }
> 
> void ThunderStorm ()
> {
> // Called everyday at midnight & randomly schedules a storm between 1 & 9 in the evening
> unsigned long RH = random(0,23);                   // Randomizer for thunderstorm
> unsigned long RM = random(0,59);
> unsigned long RS = random(0,59);
> unsigned long TSDuration = random(0,10800);     // Random T-Storm duration from 0-3hr
> unsigned long RecoveryDelay = random(0,3600);   // Random after storm gloominess from 0-1hr
> 
> Serial.print("RH = ");
> Serial.print(RH);
> Serial.println();
> 
> if (RH > 12)                             // If random value is after 1 pm, allow storm
> {
> Alarm.alarmOnce(RH,RM,RS,Storm2);
> if ((RH + (TSDuration/3600)) < 19) {
> // If storm is between 1 & 7
> Alarm.alarmOnce((RH + (TSDuration/3600)),RM,RS,Cloud2);
> }
> else if ((RH + (TSDuration/3600)) < 21) {
> // If storm is between 1 & 9
> Alarm.alarmOnce((RH + (TSDuration/3600)),RM,RS,DawnDusk);
> }
> else {
> Alarm.alarmOnce((RH + (TSDuration/3600)),RM,RS,Night2);
> }
> }
> }


Ok, yeah... that looks like it's doing the same thing I was trying to do, just much simpler. I was trying to do an approach like mistergreen posted above based on cases and using the millis(), but couldn't get it to compile... I got so deep into it that by the time I went back to if/then I couldn't see the forest for the trees haha. 

Just uploaded it to the Uno, we'll see how it does...


----------



## Dahammer

Indychus said:


> Ok, yeah... that looks like it's doing the same thing I was trying to do, just much simpler. I was trying to do an approach like mistergreen posted above based on cases and using the millis(), but couldn't get it to compile... I got so deep into it that by the time I went back to if/then I couldn't see the forest for the trees haha.
> 
> Just uploaded it to the Uno, we'll see how it does...


Notice I added the first call to ThunderStorm() after you saw the code. You too quick. Haha. I left it out previously, so if you don't add it, it will be midnight before the first one is scheduled.


----------



## Indychus

Ok, here we go... This is definitely working, but I want to let it run for a few days before calling it good. Also, I'm gonna go back through the thread and delete some of my failed attempts to clean it up in case anyone else is trying to make sense of my mess haha.



Code:


///////////////////////////////////////////////////////////////////
// Current Satellite LED+ Controller  V2.0 Beta                  //
//   Indychus...Dahammer...mistergreen @ plantedtank.net         //
//   This code is public domain.  Pass it on.                    //
//   Confirmed on Arduino UNO 1.0.5                              //
//   Req. Time, TimeAlarms, DateTime, DateTimeStrings libraries  //
///////////////////////////////////////////////////////////////////

////////////SETUP//////////////////////////////////////////////////
#include <Time.h>
#include <TimeAlarms.h>
#include <DateTime.h>
#include <DateTimeStrings.h>

int IRledPin =  13;                  // Pin location for IR output

void setup()               
{ pinMode(IRledPin, OUTPUT);         // Designate IRledPin as Output
  Serial.begin(9600);                // Connect @ (Baud)
  setTime(18,24,15,7,26,13);          // set time (HR,MIN,SEC,MO,DAY,YR)

  ////////////ALARM FUNCTIONS/////////////////////////////////////////
 
  ThunderStorm ();
 
  Alarm.alarmRepeat(7,00,0, DawnDusk);
  Alarm.alarmRepeat(7,00,0, DawnDusk);
  Alarm.alarmRepeat(9,00,0, Cloud2);     // (HR,MIN,SEC,FUNCTION)
  Alarm.alarmRepeat(9,00,0, Cloud2);
  Alarm.alarmRepeat(13,00,0, FullSpec);
  Alarm.alarmRepeat(13,00,0, FullSpec);
  Alarm.alarmRepeat(15,00,0, Cloud2);
  Alarm.alarmRepeat(15,00,0, Cloud2);
  Alarm.alarmRepeat(19,00,0, DawnDusk);
  Alarm.alarmRepeat(19,00,0, DawnDusk);
  Alarm.alarmRepeat(21,00,0, Night2);
  Alarm.alarmRepeat(21,00,0, Night2);
  Alarm.alarmRepeat(00,00,00, ThunderStorm); 
}   
////////////THUNDERSTORMS///////////////////////////////////////////// 
void ThunderStorm ()
{ // Called everyday at midnight & randomly schedules a storm between 1 & 9 in the evening
  unsigned long RH = random(0,23);                   // Randomizer for thunderstorm
  unsigned long RM = random(0,59);
  unsigned long RS = random(0,59);
  unsigned long TSDuration = random(0,10800);     // Random T-Storm duration from 0-3hr

  Serial.print("Storm = ");
  Serial.print(RH);
  Serial.print(".");
  Serial.print(RM);
  Serial.print(".");
  Serial.print(RS);
  Serial.println();

  if (RH > 12)                             // If random value is after 1 pm, allow storm
      {Alarm.alarmOnce(RH,RM,RS,Storm2);
      
      if ((RH + (TSDuration/3600)) < 19)   // Return to Cloud2 if storm ends between 1-7pm
        {Alarm.alarmOnce((RH + (TSDuration/3600)),RM,RS,Cloud2);}
      
      else if ((RH + (TSDuration/3600)) < 21)  // Return to DawnDusk if storm ends between 7-9pm
        {Alarm.alarmOnce((RH + (TSDuration/3600)),RM,RS,DawnDusk);}
      
      else                                       // Return to Night2 if storm ends after 9pm
        {Alarm.alarmOnce((RH + (TSDuration/3600)),RM,RS,Night2);}}}   


////////////CLOCK///////////////////////////////////////////////////
void  loop(){                       
  digitalClockDisplay();
  Alarm.delay(1000);               // Clock display update frequency (msec)
}

void digitalClockDisplay()          // Digital clock
{ 
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.println(); 
}

void printDigits(int digits)        // Add :
{
  Serial.print(":");
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits);
}


////////////SIGNAL///////////////////////////////////////////////////
// Create Frequency (38khz/26msec)
void pulseIR(long microsecs) 
{  
  cli();                           // kill interupts
  while (microsecs > 0)
  {  
    digitalWrite(IRledPin, HIGH);    // ~3 msec
    delayMicroseconds(7);            // ~delay
    digitalWrite(IRledPin, LOW);     // ~3 msec
    delayMicroseconds(7);            // ~delay
    microsecs -= 26;  
  }
  sei();  
}                           // zombie interupts


////////////FIXTURE FUNCTIONS////////////////////////////////////////
void DeviceID ()
{
  int codes[17][2] = { 
    {8840,4320}, 
    {620,480}, 
    {600,500}, 
    {540,1660}, 
    {580,520}, 
    {520,560}, 
    {600,480},
    {620,480}, 
    {540,540}, 
    {620,1600}, 
    {520,1660}, 
    {580,520},
    {520,1660}, 
    {600,1580}, 
    {620,1580}, 
    {540,1660}, 
    {600,1580}};

  for( int i = 0 ; i < 17; i++) {
    pulseIR(codes[i][0]);
    delayMicroseconds(codes[i][1]);}}

void Footer ()
{int codes[3][2] = { 
    {580,1620},
    {540,38980},
    {8860,2120}};

  for( int i = 0 ; i < 3; i++) {
    pulseIR(codes[i][0]);
    delayMicroseconds(codes[i][1]);}
  pulseIR(620);}

void IR(int codes[15][2]) 
{DeviceID();

  for( int i = 0 ; i < 15; i++) {
    pulseIR(codes[i][0]);
    delayMicroseconds(codes[i][1]);}  
  Footer(); }

void PowerButton()                             //Fixture power on/off toggle
{int codes[15][2] = { 
    {600,500},
    {600,500},
    {520,560},
    {600,500},
    {600,500},
    {520,560},
    {580,1620},
    {540,540},
    {600,1600},
    {520,1660},
    {580,1620},
    {580,1620},
    {520,1660},
    {580,1600},
    {580,520}}; 

  Serial.println("Power Toggle");
  IR(codes);}

void Night2()                                   //Fixture Initialize Night2 Mode
{int codes[15][2] = { 
    {600,1640},
    {600,500},
    {520,560},
    {600,1620},
    {600,1640},
    {520,560},
    {580,540},
    {540,540},
    {600,480},
    {520,1660},
    {580,1620},
    {580,500},
    {520,500},
    {580,1600},
    {580,1560}}; 

  Serial.println("Night 2 Mode Initialized");
  IR(codes);}

void Cloud2()                                         //Fixture Initialize Cloud2 Mode
{int codes[15][2] = { 
    {600,1580},
    {600,500},
    {520,1620},
    {600,500},
    {600,1600},
    {520,560},
    {580,480},
    {540,540},
    {600,560},
    {520,1660},
    {580,560},
    {580,1620},
    {520,560},
    {580,1600},
    {580,1640}};

  Serial.println("Cloud Cover 2 Mode Initialized");
  IR(codes);}

void FullSpec()                                  //Fixture Initialize Full Spectrum Mode
{int codes[15][2] = { 
    {600,1580},
    {600,500},
    {520,480},
    {600,1580},
    {600,1600},
    {520,560},
    {580,1600},
    {540,540},
    {600,560},
    {520,1660},
    {580,1580},
    {580,540},
    {520,560},
    {580,1600},
    {580,480}};

  Serial.println("Full Spectrum Mode Initialized");
  IR(codes);}

void DawnDusk()                         // Fixture Initialize Dawn/Dusk Mode
{int codes[15][2] = { 
    {600,1660},
    {600,1560},
    {520,480},
    {600,1580},
    {600,1600},
    {520,560},
    {580,500},
    {540,540},
    {600,560},
    {520,500},
    {580,1580},
    {580,540},
    {520,560},
    {580,1660},
    {580,1580}};

  Serial.println("Dawn/Dusk Mode Initialized");
  IR(codes);}

void Storm2()                         //Fixture Initialize Storm2 Mode
{  int codes[15][2] = { 
    {600,1660},
    {600,540},
    {520,480},
    {600,500}, 
    {600,1600},
    {520,560},
    {580,500},
    {540,540},
    {600,560},
    {520,1660},
    {580,1580},
    {580,1620},
    {520,560},
    {580,1660},
    {580,1580}};

  Serial.println("Storm2 Mode Initialized");
  IR(codes);}


----------



## Dahammer

I don't have my fixture yet, it should be here Wednesday hopefully. But I have a question. Earlier in the thread you said that night2 mode is a soft, fading moonlight. Does it eventually turn the fixture off? If so, that's just too cool.


----------



## Indychus

Dahammer said:


> I don't have my fixture yet, it should be here Wednesday hopefully. But I have a question. Earlier in the thread you said that night2 mode is a soft, fading moonlight. Does it eventually turn the fixture off? If so, that's just too cool.


No, it just fades between a dark blue to a dark purple. It's the darkest built-in mode without turning the fixture off. It would be easy to add an alarm to turn it off, but since the on/off is a toggle I've been hesitant to add it in case it misfires once.

You can independently control each color and store 4 custom colors, so I plan to match a custom color to the night2, then fade it down to off using the independent color control. It would be like Night2 > Custom purple > Fade > Fade > Fade > Fade etc, until it's all off. Probably starting the fade sequence at midnight. Then fade back on in the morning.


----------



## Indychus

And Current has confirmed that they will use different IR codes starting with the next shipment of fixtures, so depending on where yours comes from it may be different. It should be pretty easy to crack the codes, provided they don't change the frequency. A new frequency would require an o-scope to determine and a different receiver. It's easy to tune the code to a different frequency one it's determined.


----------



## Dahammer

Maybe they won't change the frequency, but if they do I can probably get access to a scope. My fixture is already in transit, but I bought it at Foster & Smith and noticed that they were out of them a week or 2 ago, so mine may be different. We'll see soon.

I hadn't thought about a misfire on power off; that's an excellent point. I guess my brain is still thinking in hardwired mode. I have a timer on the A/C circuit mine will be plugged into, so I could use it to turn the fixture on/off also. I like the fade till off/on idea better though.


----------



## Indychus

There's a good how-to somewhere (maybe adafruit) on making an o-scope with an Arduino haha... I have an Arduino Mega that I thought about making a scope with, but I'm not sure I would use it enough to justify using the mega. Might pick up another uno for that.


----------



## Indychus

http://www.instructables.com/id/Arduino-Oscilloscope-poor-mans-Oscilloscope/


----------



## Dahammer

Haha! That's fitting to do it with the Arduino.

Which circuit are you going to use to bump up the current to the IR LED? I've found several different schematics but I'm not sure which one would be best and you're the engineer.


----------



## Indychus

Dahammer said:


> Haha! That's fitting to do it with the Arduino.
> 
> Which circuit are you going to use to bump up the current to the IR LED? I've found several different schematics but I'm not sure which one would be best and you're the engineer.


_Mechanical_ engineer haha... I have a good friend who's electrical and helping me out with the amplifier .. It's based on a NPN transistor.

It's pretty simple, similar to this:










But the resistance values are different. Im not sure on the transistor specs, I left that up to him. It should drive the IR emitter at it's max, which is 100 mA. It's only at 33 mA now, due to the Arduino being limited to 40 mA on the output channels.


----------



## Dahammer

Mechanical.......Electrical......Semantics. Haha! Yeah, that is something like what I was playing around with.

I'm just a beginner at this, so I need all of the help I can get in this area. I'm thinking about using the 2N3904 (276-2016) NPN Transistor from Radio Shack. The 2N2222 would probably be better but they only have it in a 15 pack. Here are the specs: 
V(CE): 0.2 
V(BE): 0.85
I(C) Max: 200mA 
Power Dissipation: 350mW

I only need 100mA to the IR LED and it has an I(C) of 200mA, so I'm good there. 

It has a 350mW Pd, so:
Pd = Power Dissipation
Vce = Collector saturation voltage
I = Current

Pd = Vce * I = Watts
Pd = (0.2V) * (0.1A) = 0.02W = 20mW
20mW is well within the 350mW spec, so should be good there.

I want to supply the IR LED with no more than 100mA, so I need to limit the current out of the transistor with a resistor. To calculate that, I do
R = Resistor
Vin = Supply voltage
Vce = Collector saturation voltage
Vf = Forward voltage of IR LED
I = Current

R = (Vin – Vce - Vf)/I 
R = (5V – 0.2V – 1.4V)/0.1A = 34ohms

So I need a 34ohm resistor (33ohm and 36ohm are standard values) in series between the collector pin on the transistor and the IR LED. If I drop back to the 36ohm, then:

I = (Vin – Vce – Vf) / R
I = (5V – 0.2V – 1.4V) / 0.036 = 94.44mA

Or if I use the 33ohm:
I = (5V – 0.2V – 1.4V) / 0.033 = 103.03mA

Probably better to stay under the rated 100mAm, so I'll go with a 36ohm resistor.

Then I need to limit the current to the transistor to what the Arduino is capable of. The specs say the max in 40mA, but from what I’ve read it’s best to stay around 20-30mA. But I can saturate the transistor with less than that, so no point in over driving it. From what I’ve read the rule of thumb is that supplying about 1/10th of the needed collector current will saturate the transistor.

Ic = Voltage at collector
Vbe = Voltage at base

R = (Vin-Vbe) / (Ic/10)
R = (5 – 0.85) / (100/10) = 0.415 (415ohms)

The standard resistor values are 390ohm and 430ohm, so if I drop back to the 390ohm resistor, that means:
I = (5 – 0.85) / 0.390 = 10.64mA

Or to see the max I could use and stay around 30mA:

R = (5 – 0.85) / 30 = 138ohm

So anything from 130ohm to 390ohm would be acceptable, I think.

If you get a chance, run this by your buddy and see how bad off it is. Anybody looking on should consider that I don't even qualify as an amateur at this, so I don't know if it will work or not. It may result in some pretty fireworks. :icon_mrgr

Here is the schematic I did on digikey's website.


----------



## Indychus

Yes, that all looks correct to me. I'm sure anyone else following along will appreciate you posting the logic you used to move through the calculations. My EE buddy is working nights on a big project right now, but I'll get him to verify ASAP. I have a circuit on a breadboard that is working and has increased the range to around 8 feet, but again I want my buddy to verify it before I start soldering it up.

I have the RTC hooked up and running, I'll post a quick run-through of what I did and the code to get it working in a few minutes. Some of the libraries were not compatible with Arduino 1.0+.


----------



## Indychus

Ok, so here we'll be adding an RTC. This allows the controller to sync with your PC time every time you upload the code and has a button cell battery that saves the time in the event of loss of power to the unit.

First, you'll need an RTC shield for your Arduino. They typically cost around $9-15 depending on what you get. They range from component kits that are basically just a box of parts that you solder onto a blank PCB to fully complete units including headers. 

I used a *TinyRTC V1.1* shield. Any shield will do, but I will recommend one based on the DS1307 RTC. The DS1307 is accurate enough for our needs while being a good bit cheaper than other options. It has wide support in the Arduino world and in many other applications. Compared to more expensive RTCs it does tend to lose a few minutes per year, but our needs are not so critical that a few minutes is crucial. Plus we will allow it to sync every time we push new code to the controller, so at most you're looking at a few second discrepancy.










Here is the schematic for the shield if you're interested in making your own or trying to figure out what's going on.










And here's the breakout for wiring it up that we'll use:


----------



## Indychus

My shield does not have headers installed, so I'll be attaching jumper wires to connect it to the arduino. There are many ways to attach these, so do whatever works best for you. Here's what I did.

First, lay out your tools. You need a soldering iron, solder, a vise or adjustable pliers to hold your work piece, and of course the jumpers and RTC. I use a little foot stool to work on so I can watch cartoons with my 3 year old while working.









Any soldering iron will do, but this is my all-time favorite. I prefer butane irons over electric because they heat up quicker and you're not tied to the wall.









Now's a good time to watch some videos if you've never soldered before, youtube is a great source of information. I'm not going to go into much detail because I'm not a very good solderer myself, as the following picture testifies. The main thing is to heat the wire, not the solder, and let the heat wick the molten solder into the connection.

Here's my hideous solder job:









Now you'll notice that there are two spots for headers on this board. This is handy for checking your solder work. Place one lead of a multimeter at the end of each of your jumpers and the other lead at the corresponding location on the empty header. You should read damn near 0 ohms. If it's open or shows resistance, reheat and resolder your connection.

Now here it is inside my project box. This is an old DSL router that I gutted and re-purposed. I just lined the Arduino up with the existing hole in the back and hot-glued it into place. Then I used an existing screw to attach the RTC shield and wired it all up per the breakout in the last post. The breadboard is temporary until the emitter amplifier circuit is finalized.










If you've been paying attention, you'll notice that the DS wire is connected to digital pin2 on the Arduino-- the same pin we connected the IR receiver to earlier. We'll leave the RTC on pin2 and change the location of the receiver to pin3 as it's code is easier to change. Be sure to update the code for the receiver.


----------



## Indychus

And here's the updated code for everything, including the RTC and random storms. It all seems to be working, so I'm calling this V2.0. Any issues will be sorted in subsequent updates. I am updating post #2 to reflect the current version. You will need to download and install RTClib for this to work. It should be noted that this library is not fully compatible with the new Arduino 1.0+ IDE.




*Current LED+ Controller V2.0*



Code:


///////////////////////////////////////////////////////////////////
// Current Satellite LED+ Controller  V2.0                       //
//   Indychus...Dahammer...mistergreen @ plantedtank.net         //
//   This code is public domain.  Pass it on.                    //
//   Confirmed on Arduino UNO 1.0.5                              //
//   Req. Time, TimeAlarms, DateTime, DateTimeStrings, RTClib    //
///////////////////////////////////////////////////////////////////

////////////SETUP//////////////////////////////////////////////////

#include <Wire.h>
#include <RTClib.h>
#include <Time.h>
#include <TimeAlarms.h>
#include <DateTime.h>
#include <DateTimeStrings.h>

RTC_DS1307 RTC;

int IRledPin =  13;                  // Pin location for IR output

void setup()               
 { Wire.begin();
   RTC.begin();
  
  if (! RTC.isrunning()) 
  { Serial.println("RTC is NOT running!");}
    setSyncProvider(syncProvider);     //reference our syncProvider function instead of RTC_DS1307::get()

  RTC.adjust(DateTime(__DATE__, __TIME__));  //Always adjust to compile time
  
  pinMode(IRledPin, OUTPUT);         // Designate IRledPin as Output
  Serial.begin(9600);                // Connect @ (Baud)
 
 if(timeStatus()!= timeSet)
     Serial.println("Failure to syncronize system time with RTC");
  else
     Serial.println("System time successfully syncronized with RTC");


  ////////////ALARM FUNCTIONS/////////////////////////////////////////
 
  ThunderStorm ();              // Schedule intitial storm
 
  Alarm.alarmRepeat(7,00,0, DawnDusk);
  Alarm.alarmRepeat(7,00,0, DawnDusk);
  Alarm.alarmRepeat(9,00,0, Cloud2);     // (HR,MIN,SEC,FUNCTION)
  Alarm.alarmRepeat(9,00,0, Cloud2);
  Alarm.alarmRepeat(13,00,0, FullSpec);
  Alarm.alarmRepeat(13,00,0, FullSpec);
  Alarm.alarmRepeat(15,00,0, Cloud2);
  Alarm.alarmRepeat(15,00,0, Cloud2);
  Alarm.alarmRepeat(19,00,0, DawnDusk);
  Alarm.alarmRepeat(19,00,0, DawnDusk);
  Alarm.alarmRepeat(21,00,0, Night2);
  Alarm.alarmRepeat(21,00,0, Night2);
  Alarm.alarmRepeat(00,00,00, ThunderStorm); 
}   
////////////THUNDERSTORMS///////////////////////////////////////////// 
void ThunderStorm ()
{ // Called everyday at midnight & randomly schedules a storm between 1 & 9 in the evening
  unsigned long RH = random(0,23);                   // Randomizer for thunderstorm
  unsigned long RM = random(0,59);
  unsigned long RS = random(0,59);
  unsigned long TSDuration = random(0,10800);     // Random T-Storm duration from 0-3hr

  
  if (RH <= 12)
      Serial.print("No storm scheduled today");

  if (RH > 12)                             // If random value is after 1 pm, allow storm
      {Alarm.alarmOnce(RH,RM,RS,Storm2);
       Serial.print("Next storm = ");
  Serial.print(RH);
  Serial.print(":");
  if(RM < 10)
    Serial.print('0');
  Serial.print(RM);
  Serial.print(":");
  if(RS < 10)
    Serial.print('0');
  Serial.print(RS);
  Serial.println();
      
      if ((RH + (TSDuration/3600)) < 19)   // Return to Cloud2 if storm ends between 1-7pm
        {Alarm.alarmOnce((RH + (TSDuration/3600)),RM,RS,Cloud2);}
      
      else if ((RH + (TSDuration/3600)) < 21)  // Return to DawnDusk if storm ends between 7-9pm
        {Alarm.alarmOnce((RH + (TSDuration/3600)),RM,RS,DawnDusk);}
      
      else                                       // Return to Night2 if storm ends after 9pm
        {Alarm.alarmOnce((RH + (TSDuration/3600)),RM,RS,Night2);}}}   


////////////CLOCK///////////////////////////////////////////////////
void  loop(){                       
  digitalClockDisplay();
  Alarm.delay(60000);               // Clock display update frequency (msec)
}

time_t syncProvider()     //this does the same thing as RTC_DS1307::get()
{return RTC.now().unixtime();}

void digitalClockDisplay()          // Digital clock
{ Serial.print("Current Time = ");
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.println(); }

void printDigits(int digits)        // Add :
{Serial.print(":");
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits);}


////////////SIGNAL///////////////////////////////////////////////////
// Create Frequency (38khz/26msec)
void pulseIR(long microsecs) 
{  cli();                           // kill interupts
  while (microsecs > 0)
  { digitalWrite(IRledPin, HIGH);    // ~3 msec
    delayMicroseconds(7);            // ~delay
    digitalWrite(IRledPin, LOW);     // ~3 msec
    delayMicroseconds(7);            // ~delay
    microsecs -= 26;  }
  sei();  }                           // zombie interupts


////////////FIXTURE FUNCTIONS////////////////////////////////////////
void DeviceID ()
{
  int codes[17][2] = { 
    {8840,4320}, 
    {620,480}, 
    {600,500}, 
    {540,1660}, 
    {580,520}, 
    {520,560}, 
    {600,480},
    {620,480}, 
    {540,540}, 
    {620,1600}, 
    {520,1660}, 
    {580,520},
    {520,1660}, 
    {600,1580}, 
    {620,1580}, 
    {540,1660}, 
    {600,1580}};

  for( int i = 0 ; i < 17; i++) {
    pulseIR(codes[i][0]);
    delayMicroseconds(codes[i][1]);}}

void Footer ()
{int codes[3][2] = { 
    {580,1620},
    {540,38980},
    {8860,2120}};

  for( int i = 0 ; i < 3; i++) {
    pulseIR(codes[i][0]);
    delayMicroseconds(codes[i][1]);}
  pulseIR(620);}

void IR(int codes[15][2]) 
{DeviceID();

  for( int i = 0 ; i < 15; i++) {
    pulseIR(codes[i][0]);
    delayMicroseconds(codes[i][1]);}  
  Footer(); }

void PowerButton()                             //Fixture power on/off toggle
{int codes[15][2] = { 
    {600,500},
    {600,500},
    {520,560},
    {600,500},
    {600,500},
    {520,560},
    {580,1620},
    {540,540},
    {600,1600},
    {520,1660},
    {580,1620},
    {580,1620},
    {520,1660},
    {580,1600},
    {580,520}}; 

  Serial.println("Power Toggle");
  IR(codes);}

void Night2()                                   //Fixture Initialize Night2 Mode
{int codes[15][2] = { 
    {600,1640},
    {600,500},
    {520,560},
    {600,1620},
    {600,1640},
    {520,560},
    {580,540},
    {540,540},
    {600,480},
    {520,1660},
    {580,1620},
    {580,500},
    {520,500},
    {580,1600},
    {580,1560}}; 

  Serial.println("Night 2 Mode Initialized");
  IR(codes);}

void Cloud2()                                         //Fixture Initialize Cloud2 Mode
{int codes[15][2] = { 
    {600,1580},
    {600,500},
    {520,1620},
    {600,500},
    {600,1600},
    {520,560},
    {580,480},
    {540,540},
    {600,560},
    {520,1660},
    {580,560},
    {580,1620},
    {520,560},
    {580,1600},
    {580,1640}};

  Serial.println("Cloud Cover 2 Mode Initialized");
  IR(codes);}

void FullSpec()                                  //Fixture Initialize Full Spectrum Mode
{int codes[15][2] = { 
    {600,1580},
    {600,500},
    {520,480},
    {600,1580},
    {600,1600},
    {520,560},
    {580,1600},
    {540,540},
    {600,560},
    {520,1660},
    {580,1580},
    {580,540},
    {520,560},
    {580,1600},
    {580,480}};

  Serial.println("Full Spectrum Mode Initialized");
  IR(codes);}

void DawnDusk()                         // Fixture Initialize Dawn/Dusk Mode
{int codes[15][2] = { 
    {600,1660},
    {600,1560},
    {520,480},
    {600,1580},
    {600,1600},
    {520,560},
    {580,500},
    {540,540},
    {600,560},
    {520,500},
    {580,1580},
    {580,540},
    {520,560},
    {580,1660},
    {580,1580}};

  Serial.println("Dawn/Dusk Mode Initialized");
  IR(codes);}

void Storm2()                         //Fixture Initialize Storm2 Mode
{  int codes[15][2] = { 
    {600,1660},
    {600,540},
    {520,480},
    {600,500}, 
    {600,1600},
    {520,560},
    {580,500},
    {540,540},
    {600,560},
    {520,1660},
    {580,1580},
    {580,1620},
    {520,560},
    {580,1660},
    {580,1580}};

  Serial.println("Storm2 Mode Initialized");
  IR(codes);}


----------



## Harry Muscle

I've been following this thread for the last few days and I'm quite intrigued. I'm planning on getting a 75G with one or two of these lights when I move in a few months, so I've decided to fill my time until then with creating one of these. I'm so so with electronics, but I have a lot of programming experience.

So I thought I'd share what I'm hoping to create by building on what Indychus has started.

First, my equipment will consist of a Arduino Uno with an Ethernet shield and a TP-Link WR702N mini wireless router (about $55 worth of stuff if you get the cheap knock offs on eBay). For the software I'm hoping to create a web interface for the timer so that you can log in from a computer (or mobile phone) and schedule when you want things to happen. If getting the correct IR codes proves to be an ongoing challenge (ie: Current keeps changing them) I might even incorporate something into the web interface to capture codes and reuse them. Otherwise I'll take advantage of the hard work done already in deciphering the codes. All of this is obviously contingent on the Arduino Uno having enough memory to run this, but based on it's capabilities that I've seen so far I think this project is highly doable.

I'm open to suggestions, recommendations, feature requests, etc. in case anyone is interested in this idea. Hopefully I'll have my equipment soon and can start coding.

I also wanted to say a big thank you to Indychus for starting this and getting things off the ground by doing all the "dirty" work.

Thanks,
Harry


----------



## Dahammer

Wow! It looks like you've been busy today. Yeah I posted the calculations so that I would have a reference for the next time I needed to do it. Haha!

Did you get that RTC from dfrobot? It looks like it has more features than the one I ordered from adafruit, plus it’s half the price! What kind of compatibility issues did you encounter with RTClib?

I noticed in your latest code that you are including DateTimeStrings.h and DateTime.h. I don’t think those are being used anywhere? They’re not even installed on my system and it compiles fine without them.

The only other thing I noticed is an error I made in the fixture function routines. They call serial.print prior to calling IR() and actually sending the codes. Doesn’t have any affect unless you happened to be debugging and used those messages for a reference or something.

Anyway, excellent work! I’m looking forward to hearing how it runs over the next few days. My parts should be here later this week, but I’m not sure if get a chance to work on it or not and I’m going to be out of town this weekend. Bummer!


----------



## Dahammer

Harry Muscle said:


> I've been following this thread for the last few days and I'm quite intrigued. I'm planning on getting a 75G with one or two of these lights when I move in a few months, so I've decided to fill my time until then with creating one of these. I'm so so with electronics, but I have a lot of programming experience.
> 
> So I thought I'd share what I'm hoping to create by building on what Indychus has started.
> 
> First, my equipment will consist of a Arduino Uno with an Ethernet shield and a TP-Link WR702N mini wireless router (about $55 worth of stuff if you get the cheap knock offs on eBay). For the software I'm hoping to create a web interface for the timer so that you can log in from a computer (or mobile phone) and schedule when you want things to happen. If getting the correct IR codes proves to be an ongoing challenge (ie: Current keeps changing them) I might even incorporate something into the web interface to capture codes and reuse them. Otherwise I'll take advantage of the hard work done already in deciphering the codes. All of this is obviously contingent on the Arduino Uno having enough memory to run this, but based on it's capabilities that I've seen so far I think this project is highly doable.
> 
> I'm open to suggestions, recommendations, feature requests, etc. in case anyone is interested in this idea. Hopefully I'll have my equipment soon and can start coding.
> 
> I also wanted to say a big thank you to Indychus for starting this and getting things off the ground by doing all the "dirty" work.
> 
> Thanks,
> Harry


 That sounds awesome, Harry. I noticed in Radio Shack tonight that they had one of the Ethernet shields on clearance for $20, then a newer model at twice that. I started to grab one tonight but didn’t. Looks like I may have too now! There are several different versions of the Arduino, with varying amounts of memory, so that shouldn’t be a problem. The Uno has 32k, where the Mega has 256k.

I think being able to change the settings via PC/Phone would be awesome! At some point, I’ll probably hard wire mine to the fixture and get away from the IR remote. It’s great to have real programmers on board!


----------



## Caver

OK...This is all you guys fault...:icon_eek:

You went and found a way for me to combine my electronics hobby and aquariums!

I've had my 6 gal shrimp tank and my 20 Long FW up and running for only a couple of weeks and now I've got to program the LED+ on the 20!

I picked up a Rat-Shack Arduino Uno today and got the blinky and fade led routines to run...so I know I can talk to the Arduino. I just ordered a RTC and some IR LEDs that should be here by the end of the week...so I'm gonna start playing with this with the goal to get it up & running over the long Labor Day weekend.

When I got the LED+, the first thing that came to my mind was that it would be really cool to program the moonlight and dusk/dawn routines to "soften" the on-off cycles of the light.

I'm going to study the code...it looks like if you preface a line with a /, it's like commenting a line out of the code. I'd like to disable the random storms. They make some of my fish nervous.

I'll be following this thread with great interest and may pop in with questions.

Good work all! roud:


----------



## Indychus

Caver said:


> I'm going to study the code...it looks like if you preface a line with a /, it's like commenting a line out of the code. I'd like to disable the random storms. They make some of my fish nervous.


To comment out a line, use 




Code:


// Comments out a single line.

/* Comments out multiple lines.
    Useful for removing large blocks of code.
    Don't forget to close the comment with  */ 

// Like most langauges, a single "/" is treated as division in C++.

You only need to comment out the inital storm call and the scheduled storm reset to disable the storms. Like so:



Code:


// ThunderStorm ();              // Schedule intitial storm
 
  Alarm.alarmRepeat(7,00,0, DawnDusk);
  Alarm.alarmRepeat(7,00,0, DawnDusk);
  Alarm.alarmRepeat(9,00,0, Cloud2);     // (HR,MIN,SEC,FUNCTION)
  Alarm.alarmRepeat(9,00,0, Cloud2);
  Alarm.alarmRepeat(13,00,0, FullSpec);
  Alarm.alarmRepeat(13,00,0, FullSpec);
  Alarm.alarmRepeat(15,00,0, Cloud2);
  Alarm.alarmRepeat(15,00,0, Cloud2);
  Alarm.alarmRepeat(19,00,0, DawnDusk);
  Alarm.alarmRepeat(19,00,0, DawnDusk);
  Alarm.alarmRepeat(21,00,0, Night2);
  Alarm.alarmRepeat(21,00,0, Night2);
//  Alarm.alarmRepeat(00,00,00, ThunderStorm); 
}


----------



## Indychus

Dahammer said:


> Did you get that RTC from dfrobot? It looks like it has more features than the one I ordered from adafruit, plus it’s half the price! What kind of compatibility issues did you encounter with RTClib?
> 
> I noticed in your latest code that you are including DateTimeStrings.h and DateTime.h. I don’t think those are being used anywhere? They’re not even installed on my system and it compiles fine without them.
> !


I got my RTC from SainSmart.

Honestly, I don't know enough to explain the compatibility issues. I could not get the code to compile and finally found a thread on the Arduino forum that said there were compatibility issues with the RTClib and 1.0+ and it will only work if wire.h and RTClib.h are included first, and in that order. I moved them to the top and all is well.

Removed DateTimeStrings.h and DateTime.h and confirmed it works fine. I had left those in there from a previous version and did not realize they were no longer needed. Also moved the IRcodes to call prior to the serial print.


----------



## Indychus

*V 2.1*

Only minor changes from 2.0 that do not affect operation.



Code:


///////////////////////////////////////////////////////////////////
// Current Satellite LED+ Controller  V2.1                       //
//   Indychus...Dahammer...mistergreen @ plantedtank.net         //
//   This code is public domain.  Pass it on.                    //
//   Confirmed on Arduino UNO 1.0.5                              //
//   Req. Time, TimeAlarms, RTClib                               //
///////////////////////////////////////////////////////////////////

////////////SETUP//////////////////////////////////////////////////

#include <Wire.h>
#include <RTClib.h>
#include <Time.h>
#include <TimeAlarms.h>

RTC_DS1307 RTC;

int IRledPin =  13;                  // Pin location for IR output

void setup()               
 { Wire.begin();
   RTC.begin();
  
  if (! RTC.isrunning()) 
  { Serial.println("RTC is NOT running!");}
    setSyncProvider(syncProvider);     //reference our syncProvider function instead of RTC_DS1307::get()

  RTC.adjust(DateTime(__DATE__, __TIME__));  //Always adjust to compile time
  
  pinMode(IRledPin, OUTPUT);         // Designate IRledPin as Output
  Serial.begin(9600);                // Connect @ (Baud)
 
  if(timeStatus()!= timeSet)
     Serial.println("Failure to syncronize system time with RTC");
  else
     Serial.println("System time successfully syncronized with RTC");


  ////////////ALARM FUNCTIONS/////////////////////////////////////////
 
 // Note: TimeAlarms.h must be edited to allow for 24 alarms.  By default it is set to 6.
 
  ThunderStorm ();              // Schedule intitial storm
 
  Alarm.alarmRepeat(7,00,0, DawnDusk);
  Alarm.alarmRepeat(7,00,1, DawnDusk);
  Alarm.alarmRepeat(9,00,0, Cloud2);     // (HR,MIN,SEC,FUNCTION)
  Alarm.alarmRepeat(9,00,1, Cloud2);
  Alarm.alarmRepeat(13,00,0, FullSpec);
  Alarm.alarmRepeat(13,00,1, FullSpec);
  Alarm.alarmRepeat(15,00,0, Cloud2);
  Alarm.alarmRepeat(15,00,1, Cloud2);
  Alarm.alarmRepeat(19,00,0, DawnDusk);
  Alarm.alarmRepeat(19,00,1, DawnDusk);
  Alarm.alarmRepeat(21,00,0, Night2);
  Alarm.alarmRepeat(21,00,1, Night2);
  Alarm.alarmRepeat(12,00,00, ThunderStorm); 
}   
////////////THUNDERSTORMS///////////////////////////////////////////// 
void ThunderStorm ()
{ // Called everyday at noon & randomly schedules a storm between 1 & 9 in the evening
  unsigned long RH = random(0,23);                   // Randomizer for thunderstorm
  unsigned long RM = random(0,59);
  unsigned long RS = random(0,59);
  unsigned long TSDuration = random(0,10800);     // Random T-Storm duration from 0-3hr

  
  if (RH <= 12)
      Serial.print("No storm scheduled today");
      Serial.println();
      
  if (RH > 12)                             // If random value is after 1 pm, allow storm
      {Alarm.alarmOnce(RH,RM,RS,Storm2);
       Serial.print("Next storm = ");
   Serial.print(RH);
  printDigits(RM);
  printDigits(RS);
  Serial.println();
      
      if ((RH + (TSDuration/3600)) < 19)   // Return to Cloud2 if storm ends between 1-7pm
        {Alarm.alarmOnce((RH + (TSDuration/3600)),RM,RS,Cloud2);}
      
      else if ((RH + (TSDuration/3600)) < 21)  // Return to DawnDusk if storm ends between 7-9pm
        {Alarm.alarmOnce((RH + (TSDuration/3600)),RM,RS,DawnDusk);}
      
      else                                       // Return to Night2 if storm ends after 9pm
        {Alarm.alarmOnce((RH + (TSDuration/3600)),RM,RS,Night2);}}}   


////////////CLOCK///////////////////////////////////////////////////
void  loop(){                       
  digitalClockDisplay();
  Alarm.delay(900000);               // Clock display update frequency (msec)
}

time_t syncProvider()     //this does the same thing as RTC_DS1307::get()
{return RTC.now().unixtime();}

void digitalClockDisplay()          // Digital clock
{ Serial.print("Current Time = ");
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.println(); }

void printDigits(int digits)        // Add :
{Serial.print(":");
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits);}


////////////SIGNAL///////////////////////////////////////////////////
// Create Frequency (38khz/26msec)
void pulseIR(long microsecs) 
{  cli();                           // kill interupts
  while (microsecs > 0)
  { digitalWrite(IRledPin, HIGH);    // ~3 msec
    delayMicroseconds(7);            // ~delay
    digitalWrite(IRledPin, LOW);     // ~3 msec
    delayMicroseconds(7);            // ~delay
    microsecs -= 26;  }
  sei();  }                           // zombie interupts


////////////FIXTURE FUNCTIONS////////////////////////////////////////
void DeviceID ()
{
  int codes[17][2] = { 
    {8840,4320}, 
    {620,480}, 
    {600,500}, 
    {540,1660}, 
    {580,520}, 
    {520,560}, 
    {600,480},
    {620,480}, 
    {540,540}, 
    {620,1600}, 
    {520,1660}, 
    {580,520},
    {520,1660}, 
    {600,1580}, 
    {620,1580}, 
    {540,1660}, 
    {600,1580}};

  for( int i = 0 ; i < 17; i++) {
    pulseIR(codes[i][0]);
    delayMicroseconds(codes[i][1]);}}

void Footer ()
{int codes[3][2] = { 
    {580,1620},
    {540,38980},
    {8860,2120}};

  for( int i = 0 ; i < 3; i++) {
    pulseIR(codes[i][0]);
    delayMicroseconds(codes[i][1]);}
  pulseIR(620);}

void IR(int codes[15][2]) 
{DeviceID();

  for( int i = 0 ; i < 15; i++) {
    pulseIR(codes[i][0]);
    delayMicroseconds(codes[i][1]);}  
  Footer(); }

void PowerButton()                             //Fixture power on/off toggle
{int codes[15][2] = { 
    {600,500},
    {600,500},
    {520,560},
    {600,500},
    {600,500},
    {520,560},
    {580,1620},
    {540,540},
    {600,1600},
    {520,1660},
    {580,1620},
    {580,1620},
    {520,1660},
    {580,1600},
    {580,520}}; 
  IR(codes);
  Serial.println("Power Toggle");}

void Night2()                                   //Fixture Initialize Night2 Mode
{int codes[15][2] = { 
    {600,1640},
    {600,500},
    {520,560},
    {600,1620},
    {600,1640},
    {520,560},
    {580,540},
    {540,540},
    {600,480},
    {520,1660},
    {580,1620},
    {580,500},
    {520,500},
    {580,1600},
    {580,1560}}; 
  IR(codes);
  Serial.println("Night 2 Mode Initialized");}

void Cloud2()                                         //Fixture Initialize Cloud2 Mode
{int codes[15][2] = { 
    {600,1580},
    {600,500},
    {520,1620},
    {600,500},
    {600,1600},
    {520,560},
    {580,480},
    {540,540},
    {600,560},
    {520,1660},
    {580,560},
    {580,1620},
    {520,560},
    {580,1600},
    {580,1640}};
  IR(codes);
  Serial.println("Cloud Cover 2 Mode Initialized");}

void FullSpec()                                  //Fixture Initialize Full Spectrum Mode
{int codes[15][2] = { 
    {600,1580},
    {600,500},
    {520,480},
    {600,1580},
    {600,1600},
    {520,560},
    {580,1600},
    {540,540},
    {600,560},
    {520,1660},
    {580,1580},
    {580,540},
    {520,560},
    {580,1600},
    {580,480}};
  IR(codes);
  Serial.println("Full Spectrum Mode Initialized");}

void DawnDusk()                         // Fixture Initialize Dawn/Dusk Mode
{int codes[15][2] = { 
    {600,1660},
    {600,1560},
    {520,480},
    {600,1580},
    {600,1600},
    {520,560},
    {580,500},
    {540,540},
    {600,560},
    {520,500},
    {580,1580},
    {580,540},
    {520,560},
    {580,1660},
    {580,1580}};
  IR(codes);
  Serial.println("Dawn/Dusk Mode Initialized");}

void Storm2()                         //Fixture Initialize Storm2 Mode
{  int codes[15][2] = { 
    {600,1660},
    {600,540},
    {520,480},
    {600,500}, 
    {600,1600},
    {520,560},
    {580,500},
    {540,540},
    {600,560},
    {520,1660},
    {580,1580},
    {580,1620},
    {520,560},
    {580,1660},
    {580,1580}};
  IR(codes);
  Serial.println("Storm2 Mode Initialized");}


----------



## Caver

Thanks Indychus,

That pretty much tells me exactly what I was looking for.

I've always been a hardware guy, and I can follow all that part of the discussion with no problem...but programming...I'm pretty much a novice. I did a fair amount of programming in BASIC many years ago and at one time I was a whiz with DOS, but I don't know much of the modern programming languages. Guess this will be an impetus to learn.

Of course if this works, I'm going to have to get another LED+ for the shrimp tank so they're all on the same system...:icon_conf


----------



## Dahammer

Indychus said:


> I got my RTC from SainSmart.
> 
> Honestly, I don't know enough to explain the compatibility issues. I could not get the code to compile and finally found a thread on the Arduino forum that said there were compatibility issues with the RTClib and 1.0+ and it will only work if wire.h and RTClib.h are included first, and in that order. I moved them to the top and all is well.
> 
> Removed DateTimeStrings.h and DateTime.h and confirmed it works fine. I had left those in there from a previous version and did not realize they were no longer needed. Also moved the IRcodes to call prior to the serial print.


Yeah, I had the same issue when I first added it. Loading them first solved it, so I didn't try and trace it down either. I should have mentioned that, but failed too. Sorry.

How is the thunderstorm code working? Had a storm yet?

Caver,
I intend to add a toggle switch to my circuit so that I can turn the storm mode on/off at will, without having to reload the Arduino. It's very simple to do and can be done with an external pullup resistor or by using one of the internal ones that are already in the Arduino. In fact I have some PCB mount switches that have 5 on/off switches per switch that I had though about using to turn other features on/off as well. But if Harry gets the ethernet code up and running, then you could control everything from a PC.

Check out this page for adding a switch:
http://sheepdogguides.com/arduino/aht0button.htm


----------



## Dahammer

IndyChus,

I just noticed that you changed the time on the 2nd alarm for each function to where they are at the same time as the first. You were previously sending them 1 second later. It seems to me that having them set to the same time would not send the signals twice, since the events are at the same time. Is it still in fact sending them twice?

And another tweak, this in the ThunderStorm function:


Code:


  Serial.print(RH);
  Serial.print(":");
  if(RM < 10)
    Serial.print('0');
  Serial.print(RM);
  Serial.print(":");
  if(RS < 10)
    Serial.print('0');
  Serial.print(RS);
  Serial.println();

Can be reduced to this:


Code:


  Serial.print(RH);
  printDigits(RM);
  printDigits(RS);
  Serial.println();


----------



## Indychus

The time change on the alarms was inadvertent and I dint even notice I had done it. I'll add the one second delay back and update the storm code.

I've had one storm and its skipped the past 2 days (RH < 12) as intended. No idea when it will schedule the next one.

Sent from my HTC One X


----------



## Caver

Dahammer, Thanks for the info on the switch to disable the storms. I'll probably just do it in the code.

I've experimented with the various modes on the light, and I'm just not that enamored with the storms. Just not my "cup-O-tea" I guess.

I've been reading up on the programming conventions and the code is beginning to make a little more sense to me. I may experiment with the fade commands from the remote to use in the transitions between daylight-moonlight-off-on. Mostly for my aesthetics more so than the fish...:biggrin:

I'll be interested to see how the Ethernet code works in with this or maybe the wireless shield? It would be cool to have this accessible from my home WiFi network...especially if we could talk to it from a smartphone or tablet...:icon_idea


----------



## Dahammer

Indychus said:


> The time change on the alarms was inadvertent and I dint even notice I had done it. I'll add the one second delay back and update the storm code.
> 
> I've had one storm and its skipped the past 2 days (RH < 12) as intended. No idea when it will schedule the next one.
> 
> Sent from my HTC One X


Cool.

I was looking back through the thread to see which input pins you are using and noticed that you connected your RTC's DS signal to digital pin 2. The DS signal is for the RTC's built in temperature sensor circuit. Since you aren't using it, it's optional whether or not to connect it.


----------



## Dahammer

I just noticed this in the TimeAlarms readme, while looking for a way to turn some alarms off.



Code:


Q: How many alarms can be created?
A: Up to six alarms can be scheduled.  
The number of alarms can be changed in the TimeAlarms header file (set by the constant dtNBR_ALARMS,
note that the RAM used equals dtNBR_ALARMS  * 11)


----------



## Indychus

Yeah, I've got a screenshot of it in the thread somewhere from when I changed it. I have it set to 24 right now, which is 264 bytes and I haven't had any ram issues. The uno has 2kb sram. 

Sent from my HTC One X


----------



## Dahammer

Indychus said:


> Yeah, I've got a screenshot of it in the thread somewhere from when I changed it. I have it set to 24 right now, which is 264 bytes and I haven't had any ram issues. The uno has 2kb sram.
> 
> Sent from my HTC One X


Ah, ok, I must have missed it.


----------



## Dahammer

Indychus said:


> Yeah, I've got a screenshot of it in the thread somewhere from when I changed it. I have it set to 24 right now, which is 264 bytes and I haven't had any ram issues. The uno has 2kb sram.
> 
> Sent from my HTC One X


I found it and I may know why you have been having difficulties with the library files (.h). They are in Unix format and Notepad doesn't play very nice with that format. I love UltraEdit myself, but it isn't free. It is a very powerful editor, especially for writing code (any code) though. There are probably free ones available for Windows though.


----------



## Indychus

Edited V2.1 above. Changed the ThunderStorm alarm to noon (12,00,00), as it was behaving oddly when scheduled at (00,00,00), corrected the failsafe 1-second repeat on the alarms, and changed the Serial.print in the ThunderStorm function.

Now at 11,994 of 32,256 bytes... plenty of room left 

I have ordered an LCD display that I'll be adding later this week.


----------



## Caver

Cool...Now you're getting 'fancy'...:icon_cool


----------



## Indychus

Not much to report... V2.1 still seems to be running ok.

Got some solderable PCB's for the final wiring.


----------



## O2surplus

Indychus said:


> Not much to report... V2.1 still seems to be running ok.
> 
> Got some solderable PCB's for the final wiring.


If you haven't done so already, please post up the final wiring diagram for the Arduino and associated components. If you'd like I can create a custom Arduino "shield" design for you _or_ create a custom PcB that "boils down" the Arduino and all the other components needed into one small PcB. 
The bare PcB for either option can be manufactured for as little as $2 each.


----------



## Indychus

Nice! I still have a few things to add... LCD screen and amplifier circuit for the IR emitter. Once that's done I'll put together a schematic for you. I don't have any software specifically for electronics, but I can draw it up in AutoCAD.

Sent from my HTC One X


----------



## O2surplus

Indychus said:


> Nice! I still have a few things to add... LCD screen and amplifier circuit for the IR emitter. Once that's done I'll put together a schematic for you. I don't have any software specifically for electronics, but I can draw it up in AutoCAD.
> 
> Sent from my HTC One X



That will work fine. I'll get to work on both the "shield" and a "stand alone controller" once you've posted the diagrams. 
I did the same thing for "MISTERGREEN"s "Close Enough PAR Meter" a few years ago. It was an Arduino based project that we ended up "boiling down" to a stand alone device.


----------



## Dahammer

$2 a board, wow! I use to etch my own using the photo method and it cost a lot more than that to do it. A custom board would make for a clean setup.

My fixture came yesterday. Love it! The rest of my stuff will be here tomorrow. I hope it comes early so I can put it together tomorrow, otherwise it will be next week.

IndyChus, is storm2 the K key on the remote? And is cloud2 the F key on the remote? Have you had a chance to extract the rest of the codes? I guess that's where I'll start when I get mine up and going.

I added code last night that uses an interrupt on the Arduino to watch for a change in state of pin 4, which I will attach a switch too. I'm going to use the switch to enable/disable storms. The possibilities are endless, it's just a matter of figuring out what you want the light to do.


----------



## Indychus

Here's how my function names correspond to the remote:










The yellow ones I have already decoded and added to the sketch.

I'm attaching my spreadsheet as well. Still have a lot to decode, but I haven't had much time to work on it this week.


----------



## Indychus

I've gotten DawnDusk also, but forgot to highlight it above.


----------



## Dahammer

Here is another tweak, if you want to add it. Instead of setting a 2nd alarm event 1 minute following the 1st for each alarm, you could just add a delay to the functions.

Here is an example:


Code:


void Storm2()                         //Fixture Initialize Storm2 Mode
{  int codes[15][2] = { 
    {600,1660},
    {600,540},
    {520,480},
    {600,500}, 
    {600,1600},
    {520,560},
    {580,500},
    {540,540},
    {600,560},
    {520,1660},
    {580,1580},
    {580,1620},
    {520,560},
    {580,1660},
    {580,1580}};
  IR(codes);
  Alarm.delay(1000); // Wait 1 second & resend
  IR(codes);
  Serial.println("Storm2 Mode Initialized");}

Unfortunately my Arduino and other hardware still haven't shown up.


----------



## Caver

Got my RTC & IR LEDs.

I seem to be having problems talking to the RTC. Even if the RTC isn't installed, I still see the message "System time successfully syncronized with RTC".

Also when I unplug the Arduino from the USB port and let it sit for a while, when I plug it back in, it comes back at the time it left off, not the current real time.

Need to play with it this weekend and see if I can figure it out...

Looking forward to seeing the LCD stuff.


----------



## Dahammer

Caver said:


> Got my RTC & IR LEDs.
> 
> I seem to be having problems talking to the RTC. Even if the RTC isn't installed, I still see the message "System time successfully syncronized with RTC".
> 
> Also when I unplug the Arduino from the USB port and let it sit for a while, when I plug it back in, it comes back at the time it left off, not the current real time.
> 
> Need to play with it this weekend and see if I can figure it out...
> 
> Looking forward to seeing the LCD stuff.


Looking at the code, there are some issues with it. The first issue is that it prints to the serial port prior starting the serial port. So you never see whether or not it detects the RTC.

The 2nd issue is that it adjusts the clock to the compile time after it syncs it with the RTC time. That's really no good since if you power down the Arduino and then power it back up later on, the code causes it to adjust the RTC to the old compile time.

The third issue is that the "Failure to syncronize system time with RTC" is somewhat misleading. The code that prints that message checks to see if the time is set and it always will be as it is written.

If you are going to use an RTC, there is no reason to reset the clock to the compile time everytime since it will be sync'ed with the RTC. Only if the RTC is not present would you want to do that, and even then you'd need to re-upload the code to get it accurate.

Try this code:


Code:


void setup()               
 { Wire.begin();
   RTC.begin();
   pinMode(IRledPin, OUTPUT);         // Designate IRledPin as Output
   Serial.begin(9600);                // Connect @ (Baud)
 
  if (! RTC.isrunning()) { 
    Serial.println("RTC is NOT running!");
    RTC.adjust(DateTime(__DATE__, __TIME__));  //Adjust to compile time
    }
  
  setSyncProvider(syncProvider);     //reference our syncProvider function instead of RTC_DS1307::get()
  
  if(timeStatus()!= timeSet)
     Serial.println("Failure to syncronize system time with RTC");
  else
     Serial.println("System time successfully syncronized with RTC");


  ////////////ALARM FUNCTIONS/////////////////////////////////////////
 
  ThunderStorm ();              // Schedule intitial storm
 
  Alarm.alarmRepeat(7,00,0, DawnDusk);
  Alarm.alarmRepeat(7,00,1, DawnDusk);
  Alarm.alarmRepeat(9,00,0, Cloud2);     // (HR,MIN,SEC,FUNCTION)
  Alarm.alarmRepeat(9,00,1, Cloud2);
  Alarm.alarmRepeat(13,00,0, FullSpec);
  Alarm.alarmRepeat(13,00,1, FullSpec);
  Alarm.alarmRepeat(15,00,0, Cloud2);
  Alarm.alarmRepeat(15,00,1, Cloud2);
  Alarm.alarmRepeat(19,00,0, DawnDusk);
  Alarm.alarmRepeat(19,00,1, DawnDusk);
  Alarm.alarmRepeat(21,00,0, Night2);
  Alarm.alarmRepeat(21,00,1, Night2);
  Alarm.alarmRepeat(5,00,00, ThunderStorm); 
}

Here is the output I get with the RTC installed:


Code:


System time successfully syncronized with RTC
Current Time = 0:37:02

And with it out:


Code:


RTC is NOT running!
System time successfully syncronized with RTC
Current Time = 23:46:25

So it is working and syncing the time with the RTC. As you can see, with the RTC removed it falls back to the compile time and sets the clock to that. But that message is still misleading. Will have to work on that.


----------



## Dahammer

I'm up and running and can confirm that all of the codes that IndyChus posted work on my fixture as well. But unfortunately I'm not getting much range out of my IR LED driver circuit. It's good up to about 3 feet and that's it. I bumped the LED limiting resistor up to 43ohm, so it's only pushing it to about 79mA, but I was expecting a lot more than 3'.


----------



## mistergreen

Dahammer said:


> I'm up and running and can confirm that all of the codes that IndyChus posted work on my fixture as well. But unfortunately I'm not getting much range out of my IR LED driver circuit. It's good up to about 3 feet and that's it. I bumped the LED limiting resistor up to 43ohm, so it's only pushing it to about 79mA, but I was expecting a lot more than 3'.


What's the spec on the ir led? You might want better ones with better optical focus.

A tv remote has a range of something like 10 feet so its possible.


**
Oh, long range IR uses modulation too.

Sent from my iPad using Tapatalk HD


----------



## Dahammer

mistergreen said:


> What's the spec on the ir led? You might want better ones with better optical focus.
> 
> A tv remote has a range of something like 10 feet so its possible.
> 
> 
> **
> Oh, long range IR uses modulation too.
> 
> Sent from my iPad using Tapatalk HD


It's this one:
http://www.adafruit.com/products/387

The spec sheet is here:
http://www.adafruit.com/datasheets/IR333_A_datasheet.pdf

It can handle up to 1a pulses, so I swapped in a 2N2222 with just a 133ohm resistor at the base and no resistor on the LED. That should have opened it up wide open. But I still only get 4 or 5 feet maximum and it doesn't work consistently at that range. That may be all it's capable of, I'm not sure. Putting the LED in a shroud may help also, I'm not sure. I need to get around 10 feet out of it to do what I wanted to do.

The one for my Samsung TV has no problem working at 30 feet away.


----------



## Indychus

Hmmm... We're pulsing at around 36 kHz, which seems to have the most reliable response from the fixture. You can try tweaking the values for the frequency in the code. If you bump both delays up to 8 or 9 you get pretty close to 38 kHz, which is standard. I had issues with the fixture responding at that frequency. In effect, we are using modulation, just with an analog signal.

The other thing to consider is wavelength. There is really no easy way to tell what wavelength the current receiver uses, and we are just gambling on it being 940nm which is pretty standard. It may take some experimentation with difference wavelength emitters to find the correct one. 

Also, the current remote uses a smaller emitter, which likely has a narrower beam width.

My guess is the wavelength is off. If you're pulsing at 1A and 36-38 kHz, wavelength is really the only variable we haven't put any thought into.

Sent from my HTC One X


----------



## Caver

Well...

Thanks to some help from you guys I think I have my controller running. It should turn my tank lights on in about 10 minutes. We'll see how it does this afternoon...:bounce:

FYI..I don't remember if it's been mentioned in this thread, but a cool trick to make sure your IR LED is lighting up...Most digital cameras can see into the IR range. My Samsung Galaxy Note 2 smartphone can see the LED on this unit quite well. An easy way to check that it's lighting up. :biggrin:


----------



## Caver

To answer my own post...

My controller isn't talking to the light. Don't know if it's alignment with the emitter/receiver or something else yet.

If one of you guys has a test routine program you used to play with commands from the remote I'd appreciate a post.

I'd just like to avoid editing an alarm time, recompiling & loading just to test comms.


----------



## mistergreen

Dahammer said:


> It's this one:
> http://www.adafruit.com/products/387
> 
> The spec sheet is here:
> http://www.adafruit.com/datasheets/IR333_A_datasheet.pdf
> 
> It can handle up to 1a pulses, so I swapped in a 2N2222 with just a 133ohm resistor at the base and no resistor on the LED. That should have opened it up wide open. But I still only get 4 or 5 feet maximum and it doesn't work consistently at that range. That may be all it's capable of, I'm not sure. Putting the LED in a shroud may help also, I'm not sure. I need to get around 10 feet out of it to do what I wanted to do.
> 
> The one for my Samsung TV has no problem working at 30 feet away.


nice... Are you giving it 1A? The arduino can only give you 200/300mA unless you have an external power supply.


----------



## Indychus

If it's a newer light, the IR codes may have changed. Current confirmed that they were changing them. There is a sketch at the beginning of this thread somewhere that you can upload to capture the IR signal and verify if its the same or not.

Sent from my HTC One X


----------



## Dahammer

I'm not pushing the LED anywhere near an amp. The 2N2222 has a max Ic of 600mA, according the specs, but I'm limiting the base input to around 33mA with the 133ohm resistor in order to keep from overloading the Uno. So I'm guessing it is only putting out around 320mA or so to the LED. If I'm calculating correctly. But I don't have a spec sheet on the transistor, just the specs that were on the package.

I'll play with it when I get back Monday. You may be right, it may be frequency or something else. I'm not sure what range to expect from it though go be honest, this is all new to me.

I may have to switch to a MOSFET to power the LED to get the range I'm looking for. I really don't know. I need to research it a bit. I just assumed the transistor would be enough since I've seen TV remote designs for the Arduino that use them. Anyone know what the range to power ratio is approximately?


----------



## Indychus

I'm getting around 2 feet right now, but I was thinking the transistor amplifier would remedy that. I'm only pushing mine at 33 mA.

Sent from my HTC One X


----------



## Caver

Thanks Indychus,

I checked the device ID already, and it's the same. I bought my light on Amazon about a month ago.

I'm using the cheap IR LED from Radio Shack at about 33mA. I found it has a range of about 2"...:icon_roll

I rigged up a little tubular coupler with the LED in one end and the light receiver in the other and it appears to work.

I'm letting it run now and see if it goes through the sequence I have programmed.

These Arduino things are pretty cool! I may have to go get another to play with...:icon_smil


----------



## Indychus

There are still some issues with the thunderstorm code. It seems to run ok for 3-4 days then it starts scheduling storms for 5 pm every day. You guys may want to comment that out for now. I know there are issues with the rtc code as well. We will get this ironed out. I think dahammer and mistergreen have a good grasp on the programming aspect... A lot of it is above my head. I don't get to play with electronics much at work, and all of my school and training is in aerospace.

My LCD screen is supposed to be at my house right now, I wont be back to get it until Tuesday. My EE buddy is supposed to be tinkering with a solution to the short range.

Sent from my HTC One X


----------



## mistergreen

Just a quick look at your codes

Alarm.alarmOnce((RH + (TSDuration/3600)),RM,RS,Cloud2);

RH + (TSDuration/3600) might be a float at some point and may blow up Alarms?

maybe cast it to int 
RH + (int) (TSDuration/3600)


----------



## Dahammer

Caver,

You can replace the loop() function with the one below and it will just continuously emit clock display, wait 15 seconds, dawndusk, wait 15 seconds, fullspec, wait 15 seconds, night2, and then repeat endlessly. That will keep you from having to change the alarm times and reload it so much.



Code:


void loop(){                       
  digitalClockDisplay();
  //Alarm.delay(60000);               // Clock display update frequency (msec)
  
  
  Alarm.delay(15000);
  DawnDusk();
  Alarm.delay(15000);
  FullSpec();
  Alarm.delay(15000);
  Night2();
  Alarm.delay(15000);
}

Does anyone have an oscilloscope? It would be interesting to know what the exact frequency is. Could also use it to see exactly what frequency the Arduino code is on. From what I've read, the digitalWrite function has a little latency.


----------



## Indychus

The digitalwrite latency is around 3 ms. 38khz is around 26 ms, so the total time for the frequency function can be estimated from the digitalwrite latency and the delays. I started at 26 ms then just tweaked it around until the fixture responded every time. If I change it either way it starts dropping a few commands. I really think the wavelength might be the issue with the range right now. I'm going to order a few different wavelengths and experiment with it some.

I have a few other Arduinos laying around, so I may try to make an o-scope.

Sent from my HTC One X


----------



## Dahammer

mistergreen said:


> Just a quick look at your codes
> 
> Alarm.alarmOnce((RH + (TSDuration/3600)),RM,RS,Cloud2);
> 
> RH + (TSDuration/3600) might be a float at some point and may blow up Alarms?
> 
> maybe cast it to int
> RH + (int) (TSDuration/3600)


That's a good point. The division may very well mess it up. On the Uno, an int is 2 bytes (-32,768 to 32,767), so no reason that won't work. All of those variables could be changed to int. I think the unsigned longs are remnants of some of the old code.


----------



## mistergreen

Dahammer said:


> That's a good point. The division may very well mess it up. On the Uno, an int is 2 bytes (-32,768 to 32,767), so no reason that won't work. All of those variables could be changed to int. I think the unsigned longs are remnants of some of the old code.


Oh right, your duration is long datatype. It doesn't need to be that big. You'd save memory too.
Or leave it as is and just cast to long

RH + (long) (TSDuration/3600)


----------



## Indychus

Yeah, I had some pretty large values in there initially and was having issues doing arithmetic with int and unsigned long together, so I changed them all to unsigned long. Now that those large values are no longer used we can change it back to int.

Sent from my HTC One X


----------



## Caver

YAY!!! It seems to be working! :bounce:

Once I figured out I had extremely short range with my LED setup, I kludged an adapter together and turned it loose on the tank light. It's cycled through a couple of my alarms and looks to be working. 

I set up a full 24 hour cycle and will let that run through and see how it looks.

I went out and picked up a second UNO. Since the one I started with will be dedicated to the tank light, I needed one to play with...:tongue:

Looking forward to see how this develops. Great work all ! roud:


----------



## Indychus

Congrats man, hopefully we'll get the range issues figured out.

I have ordered a few different wavelength IR leds to play with.

Here's version 2.2, with some fixes and improvements from Dahammer. I also changed the thunderstorm code some to try to prevent the float errors I was getting. It still does a random duration from 0-3 hours, but now it does it in hours and minutes instead of msecs, allowing all values to be stored as integers.

This compiles fine but I am not able to load it onto my board until Tuesday, so it has not been tested.

*Version 2.2*



Code:


 See post 144


----------



## Dahammer

You could use byte now instead of int. Byte is unsigned with a max value of 0xFF or 255. Thanks for the updates, hopefully those solve the issues with the storms.

Which wavelength LEDs did you order? I was just looking at them on Jameco's website and they have these:


830
850
875
880
890
935
940

Any idea how many different ones there are? I wander if Current would sell us some. haha.

I came across this info on Ken Shirriff's blog:


> For output, connect an IR LED and appropriate resistor to PWM output pin 3. Make sure the polarity of the LED is correct, or it won't illuminate - the long lead is positive. I used a NTE 3027 LED (because that's what was handy) and 100 ohm resistor; the range is about 15 feet. For additional range, you can amplify the output with a transistor.


The forward voltage on that LED is about 1.3, so with a 100ohm resistor the current is around 37mA. If he can get 15 feet with 37mA, we must have other issues. 

Speaking of Ken Shirriff, his IR library uses a different approach in Rx/Tx of the IR codes. It uses one of the PWM pins on the Uno. I may play with that also, when I get a chance.


----------



## Indychus

I posted the hex codes for all of the remote functions earlier in the thread somewhere if that helps at all... I had initially tried to use a pwm pin as well but could not get the light to respond at all. 

I have an 850 nm and 980 nm led on the way. I also found some 940 nm labeled as "high output"... Their specs seemed just like any other emitters I've seen, but I figured I'd give them a shot so they're on the way too.

I was able to get a range of around 5 feet by cutting the end off of a pen cap, lining the inside with aluminum foil, and sticking the led inside to focus the beam. 5 feet still isn't acceptable though, and there has to be a better solution.

Sent from my HTC One X


----------



## mistergreen

Can't you ask the light manufacturer what wavelength IR they use?


----------



## Caver

I have version 2.2 up & running. I'll let you know if I see anything of note.

Along those lines...I find that on my fixture, the delay and resend of the IR code causes an issue with the PowerButton function. It's a toggle, rather than a discrete on/off signal, so the delay & resend ends up where it started. I commented the delay out on that function and it seems to work.

Keep it up guys...roud:


----------



## Indychus

mistergreen said:


> Can't you ask the light manufacturer what wavelength IR they use?


I've been in touch with Current several times trying to get details on the IR system. They don't want the liability that comes with helping consumers hack their products. The guy I talked to was very nice and interested in what I was doing, but also very firm about not releasing any details.


----------



## Indychus

Caver said:


> the delay and resend of the IR code causes an issue with the PowerButton function. It's a toggle, rather than a discrete on/off signal, so the delay & resend ends up where it started.
> Keep it up guys...roud:












I was hesitant to program any alarms based on the power toggle for this very reason, and I completely forgot about it when adding the delays. Fixed below, I'll delete the one above.


----------



## Indychus

*Version 2.2*



Code:


///////////////////////////////////////////////////////////////////
// Current Satellite LED+ Controller  V2.2                       //
//   Indychus...Dahammer...mistergreen @ plantedtank.net         //
//   This code is public domain.  Pass it on.                    //
//   Confirmed on Arduino UNO 1.0.5                              //
//   Req. Time, TimeAlarms, RTClib                               //
///////////////////////////////////////////////////////////////////

////////////SETUP//////////////////////////////////////////////////

#include <Wire.h>
#include <RTClib.h>
#include <Time.h>
#include <TimeAlarms.h>

RTC_DS1307 RTC;

int IRledPin =  13;                  // Pin location for IR output

void setup()               
 { Serial.begin(9600);  
   Wire.begin();
   
   RTC.begin();
  
   if (! RTC.isrunning()) { 
    Serial.println("RTC is NOT running!");
    RTC.adjust(DateTime(__DATE__, __TIME__));  //Adjust to compile time
    }
    setSyncProvider(syncProvider);     //reference our syncProvider function instead of RTC_DS1307::get()

 
  
  pinMode(IRledPin, OUTPUT);         // Designate IRledPin as Output

  ////////////ALARM FUNCTIONS/////////////////////////////////////////
 
 // Note: TimeAlarms.h must be edited to allow for 24 alarms.  By default it is set to 6.
 
  ThunderStorm ();              // Schedule intitial storm
 
  Alarm.alarmRepeat(7,00,0, DawnDusk);
  Alarm.alarmRepeat(9,00,0, Cloud2);     // (HR,MIN,SEC,FUNCTION)
  Alarm.alarmRepeat(13,00,0, FullSpec);
  Alarm.alarmRepeat(15,00,0, Cloud2);
  Alarm.alarmRepeat(19,00,0, DawnDusk);
  Alarm.alarmRepeat(21,00,0, Night2);
  Alarm.alarmRepeat(12,00,00, ThunderStorm); 
}   
////////////THUNDERSTORMS///////////////////////////////////////////// 
void ThunderStorm ()
{ // Called everyday at noon & randomly schedules a storm between 1 & 9 in the evening
  byte RH = random(0,23);                   // Randomizer for thunderstorm
  byte RM = random(0,59);
  byte RS = random(0,59);
  byte TSDurationH = random(0,2); 
  byte TSDurationM = random(0,59);  

  
  if (RH <= 12)
      Serial.print("No storm scheduled today");
      
  if (RH > 12)                             // If random value is after 1 pm, allow storm
      {Alarm.alarmOnce(RH,RM,RS,Storm2);
       Serial.print("Next storm = ");
   Serial.print(RH);
  printDigits(RM);
  printDigits(RS);
  Serial.print("   ");
  Serial.print("Storm duration = ");
   Serial.print(TSDurationH);
  printDigits(TSDurationM);
  
  Serial.println();
      
      if ((RH + TSDurationH) < 19)   // Return to Cloud2 if storm ends between 1-7pm
        {Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,Cloud2);}
      
      else if ((RH + TSDurationH) < 21)  // Return to DawnDusk if storm ends between 7-9pm
        {Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,DawnDusk);}
      
      else                                       // Return to Night2 if storm ends after 9pm
        {Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,Night2);}}}   


////////////CLOCK///////////////////////////////////////////////////
void  loop(){                       
  digitalClockDisplay();
  Alarm.delay(900000);               // Clock display update frequency (msec)
}

time_t syncProvider()     //this does the same thing as RTC_DS1307::get()
{return RTC.now().unixtime();}

void digitalClockDisplay()          // Digital clock
{ Serial.print("Current Time = ");
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.println(); }

void printDigits(int digits)        // Add :
{Serial.print(":");
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits);}


////////////SIGNAL///////////////////////////////////////////////////
// Create Frequency (38khz/26msec)
void pulseIR(long microsecs) 
{  cli();                           // kill interupts
  while (microsecs > 0)
  { digitalWrite(IRledPin, HIGH);    // ~3 msec
    delayMicroseconds(7);            // ~delay
    digitalWrite(IRledPin, LOW);     // ~3 msec
    delayMicroseconds(7);            // ~delay
    microsecs -= 26;  }
  sei();  }                           // zombie interupts


////////////FIXTURE FUNCTIONS////////////////////////////////////////
void DeviceID ()
{
  int codes[17][2] = { 
    {8840,4320}, 
    {620,480}, 
    {600,500}, 
    {540,1660}, 
    {580,520}, 
    {520,560}, 
    {600,480},
    {620,480}, 
    {540,540}, 
    {620,1600}, 
    {520,1660}, 
    {580,520},
    {520,1660}, 
    {600,1580}, 
    {620,1580}, 
    {540,1660}, 
    {600,1580}};

  for( int i = 0 ; i < 17; i++) {
    pulseIR(codes[i][0]);
    delayMicroseconds(codes[i][1]);}}

void Footer ()
{int codes[3][2] = { 
    {580,1620},
    {540,38980},
    {8860,2120}};

  for( int i = 0 ; i < 3; i++) {
    pulseIR(codes[i][0]);
    delayMicroseconds(codes[i][1]);}
  pulseIR(620);}

void IR(int codes[15][2]) 
{DeviceID();

  for( int i = 0 ; i < 15; i++) {
    pulseIR(codes[i][0]);
    delayMicroseconds(codes[i][1]);}  
  Footer(); }

void PowerButton()                             //Fixture power on/off toggle
{int codes[15][2] = { 
    {600,500},
    {600,500},
    {520,560},
    {600,500},
    {600,500},
    {520,560},
    {580,1620},
    {540,540},
    {600,1600},
    {520,1660},
    {580,1620},
    {580,1620},
    {520,1660},
    {580,1600},
    {580,520}}; 
  IR(codes);
   Serial.println("Power Toggle");}

void Night2()                                   //Fixture Initialize Night2 Mode
{int codes[15][2] = { 
    {600,1640},
    {600,500},
    {520,560},
    {600,1620},
    {600,1640},
    {520,560},
    {580,540},
    {540,540},
    {600,480},
    {520,1660},
    {580,1620},
    {580,500},
    {520,500},
    {580,1600},
    {580,1560}}; 
  IR(codes);
  Alarm.delay(1000); // Wait 1 second & resend
  IR(codes);
  Serial.println("Night 2 Mode Initialized");}

void Cloud2()                                         //Fixture Initialize Cloud2 Mode
{int codes[15][2] = { 
    {600,1580},
    {600,500},
    {520,1620},
    {600,500},
    {600,1600},
    {520,560},
    {580,480},
    {540,540},
    {600,560},
    {520,1660},
    {580,560},
    {580,1620},
    {520,560},
    {580,1600},
    {580,1640}};
  IR(codes);
  Alarm.delay(1000); // Wait 1 second & resend
  IR(codes);
  Serial.println("Cloud Cover 2 Mode Initialized");}

void FullSpec()                                  //Fixture Initialize Full Spectrum Mode
{int codes[15][2] = { 
    {600,1580},
    {600,500},
    {520,480},
    {600,1580},
    {600,1600},
    {520,560},
    {580,1600},
    {540,540},
    {600,560},
    {520,1660},
    {580,1580},
    {580,540},
    {520,560},
    {580,1600},
    {580,480}};
  IR(codes);
  Alarm.delay(1000); // Wait 1 second & resend
  IR(codes);
  Serial.println("Full Spectrum Mode Initialized");}

void DawnDusk()                         // Fixture Initialize Dawn/Dusk Mode
{int codes[15][2] = { 
    {600,1660},
    {600,1560},
    {520,480},
    {600,1580},
    {600,1600},
    {520,560},
    {580,500},
    {540,540},
    {600,560},
    {520,500},
    {580,1580},
    {580,540},
    {520,560},
    {580,1660},
    {580,1580}};
  IR(codes);
  Alarm.delay(1000); // Wait 1 second & resend
  IR(codes);
  Serial.println("Dawn/Dusk Mode Initialized");}

void Storm2()                         //Fixture Initialize Storm2 Mode
{  int codes[15][2] = { 
    {600,1660},
    {600,540},
    {520,480},
    {600,500}, 
    {600,1600},
    {520,560},
    {580,500},
    {540,540},
    {600,560},
    {520,1660},
    {580,1580},
    {580,1620},
    {520,560},
    {580,1660},
    {580,1580}};
  IR(codes);
  Alarm.delay(1000); // Wait 1 second & resend
  IR(codes);
  Serial.println("Storm2 Mode Initialized");}


----------



## Caver

No biggie Indychus.

Having done some coding in Basic back in the day...I remember how easy it is to overlook something like that, especially if your mind is occupied with another function that is giving you problems...:confused1:

It just so happened I wanted to use the PowerButton function to turn the light completely off at bedtime and back on the next day, so I noticed it when turn-on time came up.

Also...I originally wanted to turn the light off at midnight, but it didn't like 00:00:00...(nothing happened). I didn't try 24:00:00...but 23:59:00 works and is close enough...:icon_mrgr

Looking forward to seeing your progress on this project, especially a resolution the the IR wavelength question. I think that will help range issues some of us are seeing.

I'm really enjoying this controller project, so much so, I ordered another LED+ for my 6 gal tank so both my aquariums will be running off this controller. I'm also collecting the parts to make an Arduino dual channel digital thermometer to monitor both tanks.

I haven't gotten this enthused over a home DIY electronics project in many years!


----------



## Indychus

I'm not sure what the issue with 00:00:00 is... I had originally scheduled the t-storm reset to be at midnight, but it never fired so I changed it to noon.

Arduino is addictive once you realize what it is capable of. There are a lot of other microcontrollers aimed towards DIY tinkerers (I particularly like raspberry pi), but Arduino's IDE is so easy to use and the community is so vast that for me it's hard to justify using anything else for most projects.


----------



## Dahammer

When I got home this afternoon I had every intention of trying to measure the current going through the LED in my test board. But wouldn’t you know it, my cheap DMM won’t measure more than 200mA! So confident I was pushing more than that through the LED, I went in a different direction. I'm going to have to get better tools to work with.

I decided to test Ken Shirriff’s IRremote library. I’m having rather good luck with it. I can operate the fixture from around 10 feet pretty reliably using it. After I get beyond 10 feet, it is hit or miss. That’s better than it was though.

Here is my test code, if any of you want to play with it. Just download & install the library. You use the serial monitor to send in numbers for the commands that you want to test. The numbering follows the remote left to right, top to bottom. All of the ones I tested work with my fixture but I didn’t test them all.

*Please note that the pin configuration is different than it is in IndyChus's code. You'll need your IR LED circuit connected to PIN 3 on the Arduino instead of PIN 13.*



Code:


// IR tests for Current Satellite+ fixtures
// This codes uses Ken Shirriff's IR library
// http://www.righto.com/2009/08/multi-protocol-infrared-remote-library.html
//
// You'll need Ken's library to use this code
// You'll need to connect your IR LED circuit to PIN 3 on the Arduino
//
// Use the serial monitor in the Arduino software to send numbers to the Arduino Uno
// The code accepts 1 - 32. See below

#include <IRremote.h>

IRsend irsend;

int cmd = 0;

void setup()
{
  Serial.begin(9600);
}

void loop() {
  
  if (Serial.available() > 0) { // wait for something to be sent
    
    delay(5); //Wait for transmission to finish
    cmd = SerialReadInt();
    Serial.print("You entered: ");
    Serial.println(cmd);
    CurrentCMDs();
  }
}

int SerialReadInt()
{
  int i, numAva;
  char inBytes[3];                  // Array to hold the bytes
  char * inBytesPtr = &inBytes[0];  // Pointer to the first element of the array
      
    numAva = Serial.available();  // Read number of input bytes
    if (numAva > 2)
      numAva = 2; // Only allow 2 characters to prevent overflow
      
    for (i=0; i<numAva; i++)       // Load input bytes into array
      inBytes[i] = Serial.read();
    inBytes[i] =  '\0';             // Put NULL character at the end
    return atoi(inBytesPtr);
}

void CurrentCMDs() {
    //Serial.println(cmd);
    
    switch (cmd) {
      case 1:
        irsend.sendNEC(0x20DF3AC5,32); // Orange
        break;
      case 2:
        irsend.sendNEC(0x20DFBA45,32); // Blue
        break;
      case 3:
        irsend.sendNEC(0x20DF827D,32); // Rose
        break;
      case 4:
        irsend.sendNEC(0x20DF02FD,32); // Power On/Off
        break;
      case 5:
        irsend.sendNEC(0x20DF1AE5,32); // White
        break;
      case 6:
        irsend.sendNEC(0x20DF9A65,32); // FullSpec
        break;
      case 7:
        irsend.sendNEC(0x20DFA25D,32); // Purple
        break;
      case 8:
        irsend.sendNEC(0x20DF22DD,32); // Play/Pause
        break;
      case 9:
        irsend.sendNEC(0x20DF2AD5,32); // Red Up
        break;
      case 10:
        irsend.sendNEC(0x20DFAA55,32); // Green Up
        break;
      case 11:
        irsend.sendNEC(0x20DF926D,32); // Blue Up
        break;
      case 12:
        irsend.sendNEC(0x20DF12ED,32); // White Up
        break;
      case 13:
        irsend.sendNEC(0x20DF0AF5,32); // Red Down
        break;
      case 14:
        irsend.sendNEC(0x20DF8A75,32); // Green Down
        break;
      case 15:
        irsend.sendNEC(0x20DFB24D,32); // Blue Down
        break;
      case 16:
        irsend.sendNEC(0x20DF32CD,32); // White Down
        break;
      case 17:
        irsend.sendNEC(0x20DF38C7,32); // M1 Custom
        break;
      case 18:
        irsend.sendNEC(0x20DFB847,32); // M2 Custom
        break;
      case 19:
        irsend.sendNEC(0x20DF7887,32); // M3 Custom
        break;
      case 20:
        irsend.sendNEC(0x20DFF807,32); // M4 Custom
        break;
      case 21: // 1
        irsend.sendNEC(0x20DF18E7,32); // Moon1
        break;
      case 22:
        irsend.sendNEC(0x20DF9867,32); // Moon2
        break;
      case 23:
        irsend.sendNEC(0x20DF58A7,32); // Moon3
        break;
      case 24:
        irsend.sendNEC(0x20DFD827,32); // DawnDusk
        break;
      case 25:
        irsend.sendNEC(0x20DF28D7,32); // Cloud1
        break;
      case 26:
        irsend.sendNEC(0x20DFA857,32); // Cloud2
        break;
      case 27:
        irsend.sendNEC(0x20DF6897,32); // Cloud3
        break;
      case 28:
        irsend.sendNEC(0x20DFE817,32); // Cloud4
        break;
      case 29:
        irsend.sendNEC(0x20DF08F7,32); // Storm1
        break;
      case 30:
        irsend.sendNEC(0x20DF8877,32); // Storm2
        break;
      case 31:
        irsend.sendNEC(0x20DF48B7,32); // Storm3
        break;
      case 32:
        irsend.sendNEC(0x20DFC837,32); // Storm 4
        break;
      default:
        irsend.sendNEC(0x20DF9A65,32); // FullSpec
      }
      delay(40);
}


----------



## Dahammer

I went ahead and added IndyChus' code to the code I was testing using the IRremote library. It seems to be working for me, but it needs testing.

It uses Ken Shirriff's IRremote library, which means it uses Pin 3 on the Uno versus Pin 13.

I've built in the ability to send in commands via the serial monitor for testing each code. There are of course 32 of them, 1 for each button on the remote. They start with the top left button (Orange) being 1 and go left to right, top to bottom, ending with the bottom right button (Storm4) being 32. If you comment out the "#define DEBUG_IR" at the top, this option won't be included, saving a little space.

I "think" I fixed a bug in the ThunderStorm() routine where it wasn't generating real random numbers. Most of the time I was getting my storms scheduled at 17:05:36 for 46 minutes or something like that. Now I get a different time every time. It does require an unused analog pin. I've set that to analog pin 0, if that isn't open on yours then change it to something else.

If the alarmRepeat() function would allow you to pass an argument to the function it sets the alarm on, this code could be made smaller. We could modify the TimeAlarms library to make it happen, if we need the space.



Code:


///////////////////////////////////////////////////////////////////
// Current Satellite LED+ Controller  V3.2                       //
//   Indychus...Dahammer...mistergreen @ plantedtank.net         //
//   This code is public domain.  Pass it on.                    //
//   Confirmed on Arduino UNO 1.0.5                              //
//   Req. Time, TimeAlarms, RTClib, IRremote                     //
///////////////////////////////////////////////////////////////////
//
// This version uses Ken Shirriff's IRremote library to Rx/Tx the IR codes
// http://www.righto.com/2009/08/multi-protocol-infrared-remote-library.html
// 
// This code does NOT use PIN 13 on the Uno, as do previous versions
// Instead PIN 3, which is a PWM pin, is used. So you'll need to connect
// your LED to PIN 3 instead of PIN 13 for it to work.

#include <Wire.h>
#include <RTClib.h>
#include <Time.h>
#include <TimeAlarms.h>
#include <IRremote.h>

RTC_DS1307 RTC;
IRsend irsend;

/*********** BEGIN USER DEFINED VARIABLES ***********/
// DEBUG_IR adds the option to test the IR commands via the Arduino software's serial monitor
// You can send any value from 1 to 32 and it send the corresponding IR code
// The codes follow the remote controller, left to right, top to bottom
// e.g 1 = Orange, 2 = Blue, 21 = Moon1, etc
#define DEBUG_IR

int postDelay = 100;         // Delay after codes are sent
int randAnalogPin = 0;       // This needs to be set to an unused Analog pin, Used by ThunderStorm()

// Current Satellite+ IR Codes (NEC Protocol)
unsigned long codeHeader = 0x20DF; // Always the same

// Remote buttons listed left to right, top to bottom
unsigned int codeOrange = 0x3AC5;
unsigned int codeBlue = 0xBA45;
unsigned int codeRose = 0x827D;
unsigned int codePowerOnOff = 0x02FD;
unsigned int codeWhite = 0x1AE5;
unsigned int codeFullSpec = 0x9A65;
unsigned int codePurple = 0xA25D;
unsigned int codePlay = 0x22DD;
unsigned int codeRedUp = 0x2AD5;
unsigned int codeGreenUp = 0xAA55;
unsigned int codeBlueUp = 0x926D;
unsigned int codeWhiteUp = 0x12ED;
unsigned int codeRedDown = 0x0AF5;
unsigned int codeGreenDown = 0x8A75;
unsigned int codeBlueDown = 0xB24D;
unsigned int codeWhiteDown = 0x32CD;
unsigned int codeM1Custom = 0x38C7;
unsigned int codeM2Custom = 0xB847;
unsigned int codeM3Custom = 0x7887;
unsigned int codeM4Custom = 0xF807;
unsigned int codeMoon1 = 0x18E7;
unsigned int codeMoon2 = 0x9867;
unsigned int codeMoon3 = 0x58A7;
unsigned int codeDawnDusk = 0xD827;
unsigned int codeCloud1 = 0x28D7;
unsigned int codeCloud2 = 0xA857;
unsigned int codeCloud3 = 0x6897;
unsigned int codeCloud4 = 0xE817;
unsigned int codeStorm1 = 0x08F7;
unsigned int codeStorm2 = 0x8877;
unsigned int codeStorm3 = 0x48B7;
unsigned int codeStorm4 = 0xC837;

void SetAlarms()
{
  // Set up your desired alarms here
  // The default value of dtNBR_ALARMS is 6 in Alarms.h.
  // This code sets 12 alarms by default, so you'll need to change dtNBR_ALARMS to 12 or more
  Alarm.alarmRepeat(7,00,0, DawnDusk);
  Alarm.alarmRepeat(9,00,0, Cloud2);     // (HR,MIN,SEC,FUNCTION)
  Alarm.alarmRepeat(11,00,0, FullSpec);
  Alarm.alarmRepeat(15,00,0, Cloud2);
  Alarm.alarmRepeat(19,00,0, DawnDusk);
  Alarm.alarmRepeat(21,00,0, Moon2);
  
  ThunderStorm(); // Sets up intial storm; Comment out if you don't want storms; Adds 4 additional Alarms  
}
/************* END USER DEFINED VARIABLES *************/

void setup()
{
  Wire.begin();
  RTC.begin();
  Serial.begin(9600);
  
  if (! RTC.isrunning()) { 
    Serial.println("RTC is NOT running!");
    RTC.adjust(DateTime(__DATE__, __TIME__));  //Adjust to compile time
    }
  
  setSyncProvider(syncProvider);     //reference our syncProvider function instead of RTC_DS1307::get()
  
  Alarm.timerRepeat(900, digitalClockDisplay);  // Display the time every 15 minutes
  digitalClockDisplay();
  SetAlarms();
}

void loop()
{
#ifdef DEBUG_IR 
  if (Serial.available() > 0) {
    delay(5); //Wait for transmission to finish
    CurrentCMDs(SerialReadInt());
  }
#endif
  Alarm.delay(100);               // Service alarms & wait (msec)
}

time_t syncProvider()
{
  //this does the same thing as RTC_DS1307::get()
  return RTC.now().unixtime();
}

void ThunderStorm ()
{ 
  // Schedules a storm between 1 & 9 in the evening
  randomSeed(analogRead(randAnalogPin));  // Generate random seed on unused pin
  byte RH = random(23);                   // Randomizer for thunderstorm
  byte RM = random(59);
  byte RS = random(59);
  byte TSDurationH = random(2);
  byte TSDurationM = random(59);
  
  Alarm.alarmRepeat(12,00,00, ThunderStorm); // Sets an alarm to call this code every day

  if (RH <= 12)
    {
      Serial.println("No storm scheduled today");
      return;
    }
      
  if (RH > 12)                             // If random value is after 1 pm, allow storm
    {
      Alarm.alarmOnce(RH,RM,RS,Storm2);
      Serial.print("Next storm = ");
      Serial.print(RH);
      printDigits(RM);
      printDigits(RS);
      Serial.print("   ");
      Serial.print("Storm duration = ");
      Serial.print(TSDurationH);
      printDigits(TSDurationM);
      Serial.println();
      
      if ((RH + TSDurationH) < 19)   // Return to Cloud2 if storm ends between 1-7pm
        {
          Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,Cloud2);
        }
      else if ((RH + TSDurationH) < 21)  // Return to DawnDusk if storm ends between 7-9pm
        {
          Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,DawnDusk);
        }
      else                                       // Return to Night2 if storm ends after 9pm
        {
          Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,Moon2);
        }
    }
}

void digitalClockDisplay()          // Digital clock
{ 
  Serial.print("Current Time = ");
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.println(); 
}

void printDigits(int digits)        // Add :
{
  Serial.print(":");
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits);
}

#ifdef DEBUG_IR
int SerialReadInt()
{
  int i, numAva;
  char inBytes[3];                  // Array to hold the bytes
  char * inBytesPtr = &inBytes[0];  // Pointer to the first element of the array
      
    numAva = Serial.available();    // Read number of input bytes
    if (numAva > 2)
      numAva = 2;                   // Only allow 2 characters to prevent overflow
      
    for (i=0; i<numAva; i++)        // Load input bytes into array
      inBytes[i] = Serial.read();
    inBytes[i] =  '\0';             // Put NULL character at the end
    return atoi(inBytesPtr);        // Call atoi function and return result
}

void CurrentCMDs (int cmd)
{
  switch (cmd)
  {
    case 1:
      Orange();
      break;
    case 2:
      Blue();
      break;
    case 3:
      Rose();
      break;
    case 4:
      PowerOnOff();
      break;
    case 5:
      White();
      break;
    case 6:
      FullSpec();
      break;
    case 7:
      Purple();
      break;
    case 8:
      Play();
      break;
    case 9:
      RedUp();
      break;
    case 10:
      GreenUp();
      break;
    case 11:
      BlueUp();
      break;
    case 12:
      WhiteUp();
      break;
    case 13:
      RedDown();
      break;
    case 14:
      GreenDown();
      break;
    case 15:
      BlueDown();
      break;
    case 16:
      WhiteDown();
      break;
    case 17:
      M1Custom();
      break;
    case 18:
      M2Custom();
      break;
    case 19:
      M3Custom();
      break;
    case 20:
      M4Custom();
      break;
    case 21:
      Moon1();
      break;
    case 22:
      Moon2();
      break;
    case 23:
      Moon3();
      break;
    case 24:
      DawnDusk();
      break;
    case 25:
      Cloud1();
      break;
    case 26:
      Cloud2();
      break;
    case 27:
      Cloud3();
      break;
    case 28:
      Cloud4();
      break;
    case 29:
      Storm1();
      break;
    case 30:
      Storm2();
      break;
    case 31:
      Storm3();
      break;
    case 32:
      Storm4();
      break;
    default:
      Serial.println("Invalid Choice");
    }
}
#endif

void SendCode (unsigned int code, byte numTimes, const char *sMessage)
{
  unsigned long irCode = (codeHeader << 16) + code; // Header is 2 bytes, shift all the way to left & add code to it
  
  for( int i = 0; i < numTimes; i++)
  {
    irsend.sendNEC(irCode,32); // Send code
    Alarm.delay(postDelay);
  }
  Serial.print(sMessage);  // Print message
  digitalClockDisplay();
}

void Orange()
{ 
  SendCode(codeOrange, 2, "Orange: "); 
}

void Blue()
{ 
  SendCode(codeBlue, 2, "Blue: "); 
}

void Rose()
{ 
  SendCode(codeRose, 2, "Rose: "); 
}

void PowerOnOff()
{ 
  SendCode(codePowerOnOff, 1, "Power On/Off: ");
}

void White()
{ 
  SendCode(codeWhite, 2, "White: "); 
}

void FullSpec()
{
  SendCode(codeFullSpec, 2, "FullSpec: ");
}

void Purple()
{
  SendCode(codePurple, 2, "Purple: ");
}

void Play()
{ 
  SendCode(codePlay, 1, "Play/Pause: ");
}

void RedUp()
{
  SendCode(codeRedUp, 1, "RedUp: ");
}

void GreenUp()
{
  SendCode(codeGreenUp, 1, "GreenUp: ");
}

void BlueUp()
{
  SendCode(codeBlueUp, 1, "BlueUp: ");
}

void WhiteUp()
{
  SendCode(codeWhiteUp, 1, "WhiteUp: ");
}

void RedDown()
{
  SendCode(codeRedDown, 1, "RedDown: ");
}

void GreenDown()
{
  SendCode(codeGreenDown, 1, "GreenDown: ");}

void BlueDown()
{
  SendCode(codeBlueDown, 1, "BlueDown: ");
}

void WhiteDown()
{
  SendCode(codeWhite, 1, "WhiteDown: ");
}

void M1Custom()
{
  SendCode(codeM1Custom, 2, "M1Custom: ");
}

void M2Custom()
{
  SendCode(codeM2Custom, 2, "M2Custom: ");
}

void M3Custom()
{
  SendCode(codeM3Custom, 2, "M3Custom: ");
}

void M4Custom()
{
  SendCode(codeM4Custom, 2, "M4Custom: ");
}

void Moon1()
{
  SendCode(codeMoon1, 2, "Moon1: ");
}

void Moon2()
{
  SendCode(codeMoon2, 2, "Moon2: ");
}

void Moon3()
{
  SendCode(codeMoon3, 2, "Moon3: ");
}

void DawnDusk()
{
  SendCode(codeDawnDusk, 2, "DawnDusk: ");
}

void Cloud1()
{
  SendCode(codeCloud1, 2, "Cloud1: ");
}

void Cloud2()
{
  SendCode(codeCloud2, 2, "Cloud2: ");
}
  
void Cloud3()
{
  SendCode(codeCloud3, 2, "Cloud3: ");
}

void Cloud4()
{
  SendCode(codeCloud4, 2, "Cloud4: ");
}

void Storm1()
{
  SendCode(codeStorm1, 2, "Storm1: ");
}

void Storm2()
{
  SendCode(codeStorm2, 2, "Storm2: ");
}

void Storm3()
{
  SendCode(codeStorm3, 2, "Storm3: ");
}

void Storm4()
{
  SendCode(codeStorm4, 2, "Storm4: ");
}


----------



## Indychus

Looks good! Can't wait to get back to the house and try it out. I was supposed to be on the way home now, but we had some delays on the project I'm working on currently and probably won't be home until Friday.

Are you still using the transistor amplifier circuit, or is that 10 ft range with just the LED and resistor?

Sent from my HTC One X


----------



## Dahammer

Indychus said:


> Looks good! Can't wait to get back to the house and try it out. I was supposed to be on the way home now, but we had some delays on the project I'm working on currently and probably won't be home until Friday.
> 
> Are you still using the transistor amplifier circuit, or is that 10 ft range with just the LED and resistor?
> 
> Sent from my HTC One X


The 10' is with a transistor, a 2N2222. The only resistor is a 133ohm between the transistor base and pin 3. I'm going to drop that to 120ohm though when I pick one up. I have enough range for my purposes as is but I'd still like to extend it. I'll probably experiment with some other LEDs and possibly a MOSFET when I get time.


----------



## jpappy789

Dang, if only I'd done some coding and wiring before. This feature would be almost exactly what I would want should I go the Current LED+ route. Wish I had some of my engineering friends from school around again...might have to wait out for the new model and see how it compares.


----------



## Indychus

We will get the code nailed down... It's about 90% there now. All of the basic functionality is already working. If you can cobble the hardware together, we can help you get it running. Right now, if you can unzip files and copy/paste some code, you can get it running.

Sent from my HTC One X


----------



## jpappy789

Well it might be a neat project to try then if you wouldn't mind. I'm a bit hesitant to do any wiring as I've literally never messed around with anything like this before.

Just want to make sure, does post #3 contain the up to date parts needs? I sort of read through the thread, but it's heavy text so I can't really tell what's been updated or not.


----------



## Caver

I just compiled & loaded v3.

My IR range improved DRASTICALLY! Easily 8-10 feet. I'm using the 2N3904 amp circuit posted earlier.

Gonna let it run and see how it goes.

My 2nd LED+ fixture is due tomorrow. I'm going to put it on my 6 gal. shrimp tank and run both fixtures off the controller.

fun...fun...fun...:biggrin:


----------



## Indychus

jpappy789 said:


> Well it might be a neat project to try then if you wouldn't mind. I'm a bit hesitant to do any wiring as I've literally never messed around with anything like this before.
> 
> Just want to make sure, does post #3 contain the up to date parts needs? I sort of read through the thread, but it's heavy text so I can't really tell what's been updated or not.


Post 3 will get you started, and you'll want to get an RTC shield as well. Get one based on the ds1307, and make sure it has headers attached already if you've never soldered before. Dahammer has a post somewhere in here with the parts needed to amplify the emitter if you want to, but it isn't necessary just to get it running. Feel free to link whatever you find here and one of us will let you know if it will work for you.

Don't be overwhelmed, it's really pretty simple to get going.

Sent from my HTC One X


----------



## jpappy789

Thanks for the assurance. Gunna have to see if it's something I have the time to do now I know that the Current timers are coming out soon. The price will certainly be nice though.


----------



## Dahammer

I copied IndyChus’ idea and put a section of ink pen tube around the LED and that bumped the range up even more. It was a perfect fit, btw; awesome idea! I’m now getting a fairly reliable 15’ to 16’ with it. That’s as far away as I get from my fixture without taking it into another room. That’s good enough for me.

What I ended up with is a 120 ohm resistor between pin 3 and the base of a 2N2222 transistor. Well actually a 100 ohm and two 10 ohm resistors in series because I didn’t find a 120 ohm. I didn’t put a resistor between the LED and Vcc. I didn’t really see the point since I can’t saturate the transistor enough to pull enough current through it to cause an overload. I'll post an updated schematic in a little while.

I also went ahead and soldered everything onto a project board and installed it in an enclosure. Here she is.


----------



## Caver

Just a quick report before bed...

Compiled & loaded v3 earlier this evening with my alarm schedule loaded.

It looks like the RTC isn't running correctly. The time never displays in the serial monitor window and the controller never switched modes per the alarms set.

The manual control via the serial monitor works great, so the LED is working.

Unfortunately...being a work night, I can't stay up to play with it, so I'll dig into it after work tomorrow and keep watch here.

Night all...


----------



## Dahammer

Caver said:


> Just a quick report before bed...
> 
> Compiled & loaded v3 earlier this evening with my alarm schedule loaded.
> 
> It looks like the RTC isn't running correctly. The time never displays in the serial monitor window and the controller never switched modes per the alarms set.
> 
> The manual control via the serial monitor works great, so the LED is working.
> 
> Unfortunately...being a work night, I can't stay up to play with it, so I'll dig into it after work tomorrow and keep watch here.
> 
> Night all...


 Ok, thanks. I see what's happening. If you have DEBUG_IR defined (and you do since you can send in tests), then it just sits and waits on you to send it something. The Alarms don't happen because Alarm.delay(which is required to make them work) isn't being called while it just sits in loop() waiting for a command. Once you send in a command, then Alarm.delay gets called and all of your alarms you had set that have backed up should fire all at once. Not good.

The reason the time isn't displayed is because I omitted it when in debug mode. The reason I omitted it and the Alarm.delay call is because otherwise it would sit in the delay for 15 minutes before checking to see if you sent in a command. Also not good.

I should have done a better job of implementing the serial command feature. But I viewed it more as a test feature that you’d use to verify you could control the light and then you’d disable it. But I guess it is a nice a feature to have that opens up all sorts of opportunities, like enabling/disabling storm modes on the fly, changing the alarm times on the fly and so on.

So with that in mind, I’ve modified the code so that you can leave DEBUG_IR defined and still have everything else work like it’s supposed to. It should work now….maybe..

I'll update it above, instead of re-posting it. Here is a link:
http://www.plantedtank.net/forums/showpost.php?p=3994585&postcount=148


----------



## Caver

Thanks Dahammer...roud:

I certainly didn't expect you to jump on the code last night! We all appreciate the work you, Indychus and others are doing on the development of this.

Personally, I like having the serial option available simultaneously with the alarms...especially right now during development. It makes it easier to not have to load different versions of the code to test something. I'll give the code a try tonight when I get off work.

I like your packaging. I'm planning on doing something similar. I'll try to post pics when I do it.


----------



## mistergreen

Dahammer said:


> I also went ahead and soldered everything onto a project board and installed it in an enclosure. Here she is.


Where did you get that enclosure box? Hard to find good ones.


----------



## O2surplus

Somebody post a wiring diagram for this! I'm chomping at the bit to shrink this thing down to something tiny!LOL


----------



## Indychus

O2surplus said:


> Somebody post a wiring diagram for this! I'm chomping at the bit to shrink this thing down to something tiny!LOL



PM me your email and I can send you something once its finalized... Probably this weekend or early next week. I keep getting delayed at work. It looks like dahammer has solved the range issue, but I still want to add the LCD screen to it. After that, I think it's done hardware-wise. I think a shield is probably better suited to this application than a full board, so tinkerers can add thermocouples or other sensors to further automate their tank.

I'm thinking something with a header to wire to the Arduino, a header to wire to an optional LCD, then the RTC, transistor amplifier circuit, IR receiver and emitter all on board.

Sent from my HTC One X


----------



## Caver

Sounds cool Indychus!

What are your ideas for the LCD display?

A dual channel thermometer is on my tinker list, unfortunately my temp sensors are on the proverbial "slow boat from China" quite literally...:icon_roll


----------



## Dahammer

mistergreen said:


> Where did you get that enclosure box? Hard to find good ones.


It came from Adafruit. The have them in 3 different colors and they are $15 each. I like it and would buy another one but I was expecting a little better fit. The stand offs for Uno are right on the money and fit fine but the top & bottom of the enclosure don't fit together as well as they could. I also didn't like that it has cut outs for an LCD on both the top and bottom, yet they only included 1 plug with mine. So I have a hole where an LCD goes. I guess they want me to order an LCD. Haha. It might just be a missing part with mine though, because it also has 2 smaller cut outs and they included plugs for both of those.


----------



## Dahammer

Caver said:


> Thanks Dahammer...roud:
> 
> I certainly didn't expect you to jump on the code last night! We all appreciate the work you, Indychus and others are doing on the development of this.
> 
> Personally, I like having the serial option available simultaneously with the alarms...especially right now during development. It makes it easier to not have to load different versions of the code to test something. I'll give the code a try tonight when I get off work.
> 
> I like your packaging. I'm planning on doing something similar. I'll try to post pics when I do it.



I am like that. Once I get started, I can't put it down until it's done. Normally, I wouldn't post untested code, but you guys seem to like being Guinea Pigs, so I posted it.

Yeah, the serial option is nice to have. I just did a poor job of implementing it originally because I didn't intend to leave it.


----------



## O2surplus

Indychus said:


> PM me your email and I can send you something once its finalized... Probably this weekend or early next week. I keep getting delayed at work. It looks like dahammer has solved the range issue, but I still want to add the LCD screen to it. After that, I think it's done hardware-wise. I think a shield is probably better suited to this application than a full board, so tinkerers can add thermocouples or other sensors to further automate their tank.
> 
> I'm thinking something with a header to wire to the Arduino, a header to wire to an optional LCD, then the RTC, transistor amplifier circuit, IR receiver and emitter all on board.
> 
> Sent from my HTC One X


Haha- no hurry. I was just bored by my actual work today, so I thought I could kill some time working on a PcB design for this project. I'll wait until you've added the LCD and what ever else you have planned before I draw anything up. Being that you mentioned adding thermocouples and what not, I can always add breakout headers to the PcB for the remaining available Arduino I/O pins. The way I see it- all the components needed for this project could be mounted to a PcB smaller than the 16 X 2 LCD. The Atmega 328, DS1307, transistor amplifier, IR leds, 5V regulator,FTDI header, I2C header and other bits can be made to fit with plenty of room to spare. Save the full size Arduino for tinkering on other projects. A custom built PcB makes more sense.


----------



## Dahammer

Indychus said:


> PM me your email and I can send you something once its finalized... Probably this weekend or early next week. I keep getting delayed at work. It looks like dahammer has solved the range issue, but I still want to add the LCD screen to it. After that, I think it's done hardware-wise. I think a shield is probably better suited to this application than a full board, so tinkerers can add thermocouples or other sensors to further automate their tank.
> 
> I'm thinking something with a header to wire to the Arduino, a header to wire to an optional LCD, then the RTC, transistor amplifier circuit, IR receiver and emitter all on board.
> 
> Sent from my HTC One X


My only concern with a stack on shield would be that unless you could put a shroud around the IR LED and direct the light towards the fixture's receiver (like you do for a TV remote for instance, instead of just straight up, range would suffer. But you I think you could counter that by using a MOSFET to switch power to the LED and bump the power on up. It just depends on how much range you need. Othewise, a plug in shield would be the way to go for sure.


----------



## Dahammer

Here is a schematic for the circuit that I'm using to drive my IR LED. And also a part list.

Everlight IR333-A IR LED
NPN 2N2222 Transistor
120 ohm 1/4 watt resistor

The LED came from Adafruit. It's rated for up to 100mA continuous current and 1W pulses. This circuit should be pulsing it at around 350mA - 375mA.

The transistor came in a package of transistors from Radio Shack. I don't have a good spec sheet on it, but it's supposed to be able to handle 600mA. If you were to use a 2N3904 transistor instead, then you'd need a 240 ohm resistor in order to limit the current to around half, since the 2N3904 is only rated for 200mA.

We could use a MOSFET for the LED, pump up the current and increase the range. But the range is pretty good now and maybe IndyChus's tests of other wavelengths will increase it even more. The other thing is that I'm taxing the Uno's Pin3 pretty good at 35mA - 37mA and while it's rated at 40mA, I wander the long term effects.


----------



## Caver

Thanks to Dahammer for doing some late night code updates...I have V3 code up & running. The controller has made the first mode change of the night.

Looks like it's working.

Got my enclosure boxes and some other Arduino doo-dads in the mail today.

Somewhere around here I have an old Keychain led flashlight. I may see if I can remove the reflector assembly to use with this controller.

I just have to find the darned flashlight...:eek5:


----------



## Dahammer

Caver said:


> Thanks to Dahammer for doing some late night code updates...I have V3 code up & running. The controller has made the first mode change of the night.
> 
> Looks like it's working.
> 
> Got my enclosure boxes and some other Arduino doo-dads in the mail today.
> 
> Somewhere around here I have an old Keychain led flashlight. I may see if I can remove the reflector assembly to use with this controller.
> 
> I just have to find the darned flashlight...:eek5:


Seems like every time I look at it I find another way to improve it. Tonight is no exception. I've updated it yet again. This time it's just an optimization that makes the footprint a good bit smaller. Hopefully I didn't break anything with the tweak. I just edited the original post again versus re-posting it.

http://www.plantedtank.net/forums/showpost.php?p=3994585&postcount=148

That should do it for a while, I hope.


----------



## somewhatshocked

My comment won't be so much pertinent to the DIYing but I finally set my rig up this morning and it works like a charm. Really excited to use this method on other fixtures, as well.


----------



## Jester946

somewhatshocked said:


> My comment won't be so much pertinent to the DIYing but I finally set my rig up this morning and it works like a charm. Really excited to use this method on other fixtures, as well.



I too, am waiting for my Satellite LED +...and will be ordering all of the hardware this week as well.

Excited to set this up...


----------



## Dahammer

Back for a nightly update. I noticed a bug with the way scheduling of storms was handled where it would end with a bunch of alarms scheduled at once to set up storms after a few days. I'm re-posting the code below.


Code:


///////////////////////////////////////////////////////////////////
// Current Satellite LED+ Controller  V3.3                       //
//   Indychus...Dahammer...mistergreen @ plantedtank.net         //
//   This code is public domain.  Pass it on.                    //
//   Confirmed on Arduino UNO 1.0.5                              //
//   Req. Time, TimeAlarms, RTClib, IRremote                     //
///////////////////////////////////////////////////////////////////
//
// This version uses Ken Shirriff's IRremote library to Rx/Tx the IR codes
// http://www.righto.com/2009/08/multi-protocol-infrared-remote-library.html
// 
// This code does NOT use PIN 13 on the Uno, as do previous versions
// Instead PIN 3, which is a PWM pin, is used. So you'll need to connect
// your LED to PIN 3 instead of PIN 13 for it to work.

#include <Wire.h>
#include <RTClib.h>
#include <Time.h>
#include <TimeAlarms.h>
#include <IRremote.h>

RTC_DS1307 RTC;
IRsend irsend;

/*********** BEGIN USER DEFINED VARIABLES ***********/
// DEBUG_IR adds the option to test the IR commands via the Arduino software's serial monitor
// You can send any value from 1 to 32 and it send the corresponding IR code
// The codes follow the remote controller, left to right, top to bottom
// e.g 1 = Orange, 2 = Blue, 21 = Moon1, etc
#define DEBUG_IR

int postDelay = 100;         // Delay after codes are sent
int randAnalogPin = 0;       // This needs to be set to an unused Analog pin, Used by ThunderStorm()

// Current Satellite+ IR Codes (NEC Protocol)
unsigned long codeHeader = 0x20DF; // Always the same

// Remote buttons listed left to right, top to bottom
unsigned int codeOrange = 0x3AC5;
unsigned int codeBlue = 0xBA45;
unsigned int codeRose = 0x827D;
unsigned int codePowerOnOff = 0x02FD;
unsigned int codeWhite = 0x1AE5;
unsigned int codeFullSpec = 0x9A65;
unsigned int codePurple = 0xA25D;
unsigned int codePlay = 0x22DD;
unsigned int codeRedUp = 0x2AD5;
unsigned int codeGreenUp = 0xAA55;
unsigned int codeBlueUp = 0x926D;
unsigned int codeWhiteUp = 0x12ED;
unsigned int codeRedDown = 0x0AF5;
unsigned int codeGreenDown = 0x8A75;
unsigned int codeBlueDown = 0xB24D;
unsigned int codeWhiteDown = 0x32CD;
unsigned int codeM1Custom = 0x38C7;
unsigned int codeM2Custom = 0xB847;
unsigned int codeM3Custom = 0x7887;
unsigned int codeM4Custom = 0xF807;
unsigned int codeMoon1 = 0x18E7;
unsigned int codeMoon2 = 0x9867;
unsigned int codeMoon3 = 0x58A7;
unsigned int codeDawnDusk = 0xD827;
unsigned int codeCloud1 = 0x28D7;
unsigned int codeCloud2 = 0xA857;
unsigned int codeCloud3 = 0x6897;
unsigned int codeCloud4 = 0xE817;
unsigned int codeStorm1 = 0x08F7;
unsigned int codeStorm2 = 0x8877;
unsigned int codeStorm3 = 0x48B7;
unsigned int codeStorm4 = 0xC837;

void SetAlarms()
{
  // Set up your desired alarms here
  // The default value of dtNBR_ALARMS is 6 in Alarms.h.
  // This code sets 12 alarms by default, so you'll need to change dtNBR_ALARMS to 12 or more
  Alarm.alarmRepeat(7,00,0, DawnDusk);
  Alarm.alarmRepeat(9,00,0, Cloud2);     // (HR,MIN,SEC,FUNCTION)
  Alarm.alarmRepeat(11,00,0, FullSpec);
  Alarm.alarmRepeat(15,00,0, Cloud2);
  Alarm.alarmRepeat(19,00,0, DawnDusk);
  Alarm.alarmRepeat(21,00,0, Moon2);
  
  // Comment these out if you don't want the chance of a random storm each day
  Alarm.alarmRepeat(12,00,00, ThunderStorm);
  ThunderStorm();  // Sets up intial storm so we don't have wait until alarm time
}
/************* END USER DEFINED VARIABLES *************/

void setup()
{
  Wire.begin();
  RTC.begin();
  Serial.begin(9600);
  
  if (! RTC.isrunning()) { 
    Serial.println("RTC is NOT running!");
    RTC.adjust(DateTime(__DATE__, __TIME__));  //Adjust to compile time
    }
  
  setSyncProvider(syncProvider);     //reference our syncProvider function instead of RTC_DS1307::get()
  
  Alarm.timerRepeat(900, digitalClockDisplay);  // Display the time every 15 minutes
  digitalClockDisplay();
  SetAlarms();
  
}

void loop()
{
#ifdef DEBUG_IR 
  if (Serial.available() > 0) {
    delay(5); //Wait for transmission to finish
    CurrentCMDs(SerialReadInt());
  }
#endif
  Alarm.delay(100);               // Service alarms & wait (msec)
}

time_t syncProvider()
{
  //this does the same thing as RTC_DS1307::get()
  return RTC.now().unixtime();
}

void ThunderStorm ()
{ 
  // Schedules a storm between 1 & 9 in the evening
  // It sets Storm2, followed by Cloud2 or DawnDusk or Moon2, depending on when the storm is over
  randomSeed(analogRead(randAnalogPin));  // Generate random seed on unused pin
  byte RH = random(23);                   // Randomizer for thunderstorm
  byte RM = random(59);
  byte RS = random(59);
  byte TSDurationH = random(2);
  byte TSDurationM = random(59);
  
  if (RH <= 12)
    {
      Serial.println("No storm scheduled today");
      return;
    }
      
  if (RH > 12)                             // If random value is after 1 pm, allow storm
    {
      Alarm.alarmOnce(RH,RM,RS,Storm2);
      Serial.print("Next storm = ");
      Serial.print(RH);
      printDigits(RM);
      printDigits(RS);
      Serial.print("   ");
      Serial.print("Storm duration = ");
      Serial.print(TSDurationH);
      printDigits(TSDurationM);
      Serial.println();
      
      if ((RH + TSDurationH) < 19)   // Return to Cloud2 if storm ends between 1-7pm
        {
          Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,Cloud2);
        }
      else if ((RH + TSDurationH) < 21)  // Return to DawnDusk if storm ends between 7-9pm
        {
          Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,DawnDusk);
        }
      else                                       // Return to Night2 if storm ends after 9pm
        {
          Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,Moon2);
        }
    }
}

void digitalClockDisplay()          // Digital clock
{ 
  Serial.print("Current Time = ");
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.println(); 
}

void printDigits(int digits)        // Add :
{
  Serial.print(":");
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits);
}

#ifdef DEBUG_IR
int SerialReadInt()
{
  int i, numAva;
  char inBytes[3];                  // Array to hold the bytes
  char * inBytesPtr = &inBytes[0];  // Pointer to the first element of the array
      
    numAva = Serial.available();    // Read number of input bytes
    if (numAva > 2)
      numAva = 2;                   // Only allow 2 characters to prevent overflow
      
    for (i=0; i<numAva; i++)        // Load input bytes into array
      inBytes[i] = Serial.read();
    inBytes[i] =  '\0';             // Put NULL character at the end
    return atoi(inBytesPtr);        // Call atoi function and return result
}

void CurrentCMDs (int cmd)
{
  switch (cmd)
  {
    case 1:
      Orange();
      break;
    case 2:
      Blue();
      break;
    case 3:
      Rose();
      break;
    case 4:
      PowerOnOff();
      break;
    case 5:
      White();
      break;
    case 6:
      FullSpec();
      break;
    case 7:
      Purple();
      break;
    case 8:
      Play();
      break;
    case 9:
      RedUp();
      break;
    case 10:
      GreenUp();
      break;
    case 11:
      BlueUp();
      break;
    case 12:
      WhiteUp();
      break;
    case 13:
      RedDown();
      break;
    case 14:
      GreenDown();
      break;
    case 15:
      BlueDown();
      break;
    case 16:
      WhiteDown();
      break;
    case 17:
      M1Custom();
      break;
    case 18:
      M2Custom();
      break;
    case 19:
      M3Custom();
      break;
    case 20:
      M4Custom();
      break;
    case 21:
      Moon1();
      break;
    case 22:
      Moon2();
      break;
    case 23:
      Moon3();
      break;
    case 24:
      DawnDusk();
      break;
    case 25:
      Cloud1();
      break;
    case 26:
      Cloud2();
      break;
    case 27:
      Cloud3();
      break;
    case 28:
      Cloud4();
      break;
    case 29:
      Storm1();
      break;
    case 30:
      Storm2();
      break;
    case 31:
      Storm3();
      break;
    case 32:
      Storm4();
      break;
    default:
      Serial.println("Invalid Choice");
    }
}
#endif

void SendCode (unsigned int code, byte numTimes, const char *sMessage)
{
  unsigned long irCode = (codeHeader << 16) + code; // Header is 2 bytes, shift all the way to left & add code to it
  
  for( int i = 0; i < numTimes; i++)
  {
    irsend.sendNEC(irCode,32); // Send code
    Alarm.delay(postDelay);
  }
  Serial.print(sMessage);  // Print message
  digitalClockDisplay();
}

void Orange()
{ 
  SendCode(codeOrange, 2, "Orange: "); 
}

void Blue()
{ 
  SendCode(codeBlue, 2, "Blue: "); 
}

void Rose()
{ 
  SendCode(codeRose, 2, "Rose: "); 
}

void PowerOnOff()
{ 
  SendCode(codePowerOnOff, 1, "Power On/Off: ");
}

void White()
{ 
  SendCode(codeWhite, 2, "White: "); 
}

void FullSpec()
{
  SendCode(codeFullSpec, 2, "FullSpec: ");
}

void Purple()
{
  SendCode(codePurple, 2, "Purple: ");
}

void Play()
{ 
  SendCode(codePlay, 1, "Play/Pause: ");
}

void RedUp()
{
  SendCode(codeRedUp, 1, "RedUp: ");
}

void GreenUp()
{
  SendCode(codeGreenUp, 1, "GreenUp: ");
}

void BlueUp()
{
  SendCode(codeBlueUp, 1, "BlueUp: ");
}

void WhiteUp()
{
  SendCode(codeWhiteUp, 1, "WhiteUp: ");
}

void RedDown()
{
  SendCode(codeRedDown, 1, "RedDown: ");
}

void GreenDown()
{
  SendCode(codeGreenDown, 1, "GreenDown: ");}

void BlueDown()
{
  SendCode(codeBlueDown, 1, "BlueDown: ");
}

void WhiteDown()
{
  SendCode(codeWhite, 1, "WhiteDown: ");
}

void M1Custom()
{
  SendCode(codeM1Custom, 2, "M1Custom: ");
}

void M2Custom()
{
  SendCode(codeM2Custom, 2, "M2Custom: ");
}

void M3Custom()
{
  SendCode(codeM3Custom, 2, "M3Custom: ");
}

void M4Custom()
{
  SendCode(codeM4Custom, 2, "M4Custom: ");
}

void Moon1()
{
  SendCode(codeMoon1, 2, "Moon1: ");
}

void Moon2()
{
  SendCode(codeMoon2, 2, "Moon2: ");
}

void Moon3()
{
  SendCode(codeMoon3, 2, "Moon3: ");
}

void DawnDusk()
{
  SendCode(codeDawnDusk, 2, "DawnDusk: ");
}

void Cloud1()
{
  SendCode(codeCloud1, 2, "Cloud1: ");
}

void Cloud2()
{
  SendCode(codeCloud2, 2, "Cloud2: ");
}
  
void Cloud3()
{
  SendCode(codeCloud3, 2, "Cloud3: ");
}

void Cloud4()
{
  SendCode(codeCloud4, 2, "Cloud4: ");
}

void Storm1()
{
  SendCode(codeStorm1, 2, "Storm1: ");
}

void Storm2()
{
  SendCode(codeStorm2, 2, "Storm2: ");
}

void Storm3()
{
  SendCode(codeStorm3, 2, "Storm3: ");
}

void Storm4()
{
  SendCode(codeStorm4, 2, "Storm4: ");
}


----------



## Indychus

Nice! You've been busy. Hopefully we will get signed off on this project today and I can update post #2 with your newest version later tonight. I'm itching to get home and try out the new code and get my LCD hooked up. I'm going to be super pissed if I have to work through the weekend again....

Sent from my HTC One X


----------



## somewhatshocked

After some tinkering, I realized I had to bump 'dtNBR_ALARMS' up and throw in a 'PowerButton' at the beginning and end of the Alarm Functions in order to turn the fixture on and off. Seems to be working well, though.


----------



## Indychus

Yeah, we had been reluctant to add a call for powerbutton in the alarms since its a toggle... If any of the others misfires it will correct itself on the next trigger, but if powerbutton misfires for some reason the entire sequence will be out of whack. I guess it doesn't matter too much if you're around to keep an eye on it.

I'm really stoked to see so much participation in this... When I first started the thread I didn't really think anyone else would get involved. After adding in the RTC, an LCD, power brick for wall power, a nice case, etc., etc., its going to cost the same or more than current's ramp timers.... But we beat them to it and I hope we will have more functionality and customizability once it's all said and done.

Sent from my HTC One X


----------



## somewhatshocked

I'm all in for under $35 for everything - including a repurposed case from some old electronics. I think - even with occasional glitches - the price alone makes it worth it. 

I've avoided the RTC because I don't want to solder but I am using a 9v battery and I get the sense that it's going to last much longer than I expected. 

Looking forward to all the updates and adjustments to be made for the setup. Others should feel comfortable jumping on board if they're looking for the convenience of automatically using some of this fixture's cool features.

P.S. I suppose I could get around the 'PowerButton' issue by using a physical timer for the turning the fixture on and off. But where's the fun in that?


----------



## Dahammer

somewhatshocked said:


> P.S. I suppose I could get around the 'PowerButton' issue by using a physical timer for the turning the fixture on and off. But where's the fun in that?


That's exactly what I'm doing but I already had the fixture plugged into a timer controlled receptacle that I made a few years ago for my other fixture. It's kind of hard to see in the picture, but it's in a standard box with an extension cord attached and lays in the bottom of the stand my tank sits on.


----------



## Dahammer

Indychus said:


> Yeah, we had been reluctant to add a call for powerbutton in the alarms since its a toggle... If any of the others misfires it will correct itself on the next trigger, but if powerbutton misfires for some reason the entire sequence will be out of whack. I guess it doesn't matter too much if you're around to keep an eye on it.


I don't think it's going to be much of an issue so long as you keep the controller within a decent range of the fixture's sensor. I just moved my controller across the room from the sensor inside and behind the clear glass door of a curio cabinet. The distance is approximately 9 1/2 feet from LED to sensor. The sensor is on top of one the tank's canister return pipes. I test it in that position probably a dozen on/off cycles and it never misfired. We will see how it does.



Indychus said:


> I'm really stoked to see so much participation in this... When I first started the thread I didn't really think anyone else would get involved. After adding in the RTC, an LCD, power brick for wall power, a nice case, etc., etc., its going to cost the same or more than current's ramp timers.... But we beat them to it and I hope we will have more functionality and customizability once it's all said and done.
> 
> Sent from my HTC One X


I've probably got $75 in mine due to the extras I ordered, like the enclosure, prototype board, and 9v power supply. But that's ok, I've enjoyed tinkering with it and have gotten that much satisfaction out of seeing the end results.

It would be cool if we could connect to the UART wirelessly, so that we could send in commands and/or reload the Uno without connecting it to USB. I poked around last night and all the options I could come up with were in the $100 range, a bit too much for my purposes.


----------



## jeffkrol

Dahammer;4022497
It would be cool if we could connect to the UART wirelessly said:


> OR you could build one..
> http://www.ti.com/lit/an/swra039/swra039.pdf
> 
> or..
> 433M Arduino Wireless Serial Data Communication Kit RF Module UART RS232 AVR PIC
> $36.59........................including shipping


----------



## Dahammer

somewhatshocked said:


> I avoided the RTC because I don't want to solder but I am using a 9v battery and I get the sense that it's going to last much longer than I expected.


If you are not going to install an RTC, the clock will be off if it gets reset and you don't re-upload the code. The below code will reset the clock to the time the code was compiled and uploaded every time the Uno is reset, unless you have an RTC installed and running.


Code:


  if (! RTC.isrunning()) { 
    Serial.println("RTC is NOT running!");
    RTC.adjust(DateTime(__DATE__, __TIME__));  //Adjust to compile time
    }

If you don't want to solder, I think the one IndyChus has came pre-assembled. You could also get one of these:
http://www.adafruit.com/products/255#Description


----------



## Dahammer

jeffkrol said:


> OR you could build one..
> http://www.ti.com/lit/an/swra039/swra039.pdf
> 
> or..
> 433M Arduino Wireless Serial Data Communication Kit RF Module UART RS232 AVR PIC
> $36.59........................including shipping


Haha! I'm not up to rolling my own but I will take a peek at the other one, thanks.


----------



## Indychus

Mine was pre-assembled, but I still had to solder the jumper wires to it. There are several available with headers already installed though.

Sent from my HTC One X


----------



## somewhatshocked

Right now, I'll only have to re-upload the code when I swap out the 9v battery. Which isn't a big deal. Plus it'll give me the chance to tweak things, change times, upload new code as this thread evolves.

Or I can just plug it into a UPS and never have to worry about it.

Just trying to keep things as simple and cheap as possible while still enjoying the benefits.



Dahammer said:


> If you are not going to install an RTC, the clock will be off if it gets reset and you don't re-upload the code. The below code will reset the clock to the time the code was compiled and uploaded every time the Uno is reset, unless you have an RTC installed and running.
> 
> 
> Code:
> 
> 
> if (! RTC.isrunning()) {
> Serial.println("RTC is NOT running!");
> RTC.adjust(DateTime(__DATE__, __TIME__));  //Adjust to compile time
> }
> 
> If you don't want to solder, I think the one IndyChus has came pre-assembled. You could also get one of these:
> http://www.adafruit.com/products/255#Description


----------



## Dahammer

I've noticed another minor bug, if you want to call it that, in the SerialReadInt() function. The function only reads 2 bytes off of the serial buffer, so if you sent something like 123, it would process the code for "12" (WhiteUp) and then it would also turn around and process the code for "3" (Rose). What happens is that it only takes the 12 off the buffer and leaves the 3. Then when it gets back to loop() it sees the 3 is there, so off it goes back to SerialReadInt() to process it. It could be fixed with something like this:


Code:


int SerialReadInt()
{
  int i, numAva;
  char inBytes[3];                  // Array to hold the bytes
  char * inBytesPtr = &inBytes[0];  // Pointer to the first element of the array
      
    numAva = Serial.available();    // Read number of input bytes
    if (numAva > 2)
      numAva = 2;                   // Only allow 2 characters to prevent overflow
      
    for (i=0; i<numAva; i++)        // Load input bytes into array
      inBytes[i] = Serial.read();
    inBytes[i] =  '\0';             // Put NULL character at the end
    while (Serial.read() >= 0)
      ; // do nothing
    return atoi(inBytesPtr);        // Call atoi function and return result
}

Might also have to add a delay to make sure all the data has time to transmit. But I'm going to leave mine running for a few days before I fiddle with it, since it is really of no consequence.


----------



## Indychus

Looks like I'm still on the job until maybe monday... I love what I do, but I hate when it interferes with my other projects. Dahammer's gonna be on version 5 before I even get to try out version 3 if this keeps up haha.


----------



## Dahammer

somewhatshocked said:


> Right now, I'll only have to re-upload the code when I swap out the 9v battery. Which isn't a big deal. Plus it'll give me the chance to tweak things, change times, upload new code as this thread evolves.
> 
> Or I can just plug it into a UPC and never have to worry about it.
> 
> Just trying to keep things as simple and cheap as possible while still enjoying the benefits.


I'm thinking those 9 volt batteries are going to get expensive. From what I've read, the Uno draws around 42mA constantly. That's without the pulsing IR LED, which is only on for microseconds. A standard 9v battery is only rated at around 600mAh, so that's not going to last long. Standard AAs are around 2000mAh, but even those will be gone before you know it. It will be interesting to see how long it takes to drain them though. Update us when you have to change them.


----------



## somewhatshocked

They're rechargeable, so I'll actually swap them out. Right now, they're lasting almost two days - longer than I thought.

Only plan to do this until I can move the UPS near enough to connect the Arduino.



Dahammer said:


> I'm thinking those 9 volt batteries are going to get expensive. From what I've read, the Uno draws around 42mA constantly. That's without the pulsing IR LED, which is only on for microseconds. A standard 9v battery is only rated at around 600mAh, so that's not going to last long. Standard AAs are around 2000mAh, but even those will be gone before you know it. It will be interesting to see how long it takes to drain them though. Update us when you have to change them.


----------



## somewhatshocked

Just under two days it is but I finally moved the UPS! Now the Arduino and an air pump will never, ever run out of juice. 

On a related note: In playing around with things, I've run into issues using more than 6 with 'dtNBR_ALARMS' - it'll occasionally just stop firing after that. Anyone else hit that issue?


----------



## mistergreen

Dahammer said:


> Code:
> 
> 
> int SerialReadInt()
> {
> int i, numAva;
> char inBytes[3];                  // Array to hold the bytes
> char * inBytesPtr = &inBytes[0];  // Pointer to the first element of the array
> 
> numAva = Serial.available();    // Read number of input bytes
> if (numAva > 2)
> numAva = 2;                   // Only allow 2 characters to prevent overflow
> 
> for (i=0; i= 0)
> ; // do nothing
> return atoi(inBytesPtr);        // Call atoi function and return result
> }


What calls this function? I don't see it in the code.
Usually you can do something simpler like



Code:


buffer=0;
//in some loop
if(Serial.available) {
//look for the end of stream; could be \0
    if(Serial.read() != '\0') {
         inByte[buffer++]= Serial.read();
     } else {
         return atoi(inByte);
    }
}

This way it can be any bytes, 1,2,3,4 etc.. You'll just look for a terminating byte.


----------



## Caver

somewhatshocked said:


> On a related note: In playing around with things, I've run into issues using more than 6 with 'dtNBR_ALARMS' - it'll occasionally just stop firing after that. Anyone else hit that issue?


I haven't noticed any freezes or stoppages, but my unit has been getting a power reset every day or so, by me pulling it to update the software, change an alarm or play with the LED transistor circuit. I'm about to the point where I want it (until one of you guys makes another software update), so I'm just letting it run.

I'm sure you noticed it, but I'll mention it anyway...I edited the 'dtNBR_ALARMS' value in my library file to 24 from the 6 that is their default. I have almost a dozen alarms set, cycling from moonlight in the morning, through several cloud modes...fullspec midday...back through clouds to moon at night. I'm really liking how it looks! roud:

Been playing with an LCD display and some RGB LED's on my spare Arduino. Still waiting for my waterproof temperature sensors (DS18b20's) to get here, so I can build up the 2 channel thermometer for the tanks.

Would be cool if we could incorporate the thermometer code into this one...then one unit could do both jobs...:biggrin:


----------



## somewhatshocked

Weird: When I try using 8 alarms, no dice. When I up it to 24 (as suggested above), it works like a charm.


----------



## Dahammer

mistergreen said:


> What calls this function? I don't see it in the code.
> Usually you can do something simpler like
> 
> 
> 
> Code:
> 
> 
> buffer=0;
> //in some loop
> if(Serial.available) {
> //look for the end of stream; could be \0
> if(Serial.read() != '\0') {
> inByte[buffer++]= Serial.read();
> } else {
> return atoi(inByte);
> }
> }
> 
> This way it can be any bytes, 1,2,3,4 etc.. You'll just look for a terminating byte.



It's called in the loop() function:


Code:


void loop()
{
#ifdef DEBUG_IR 
  if (Serial.available() > 0) {
    delay(5); //Wait for transmission to finish
    CurrentCMDs(SerialReadInt());
  }
#endif
  Alarm.delay(100);               // Service alarms & wait (msec)
}

Are you sure the serial stream is automatically terminated with a NULL? I didn't think it was from what I had read but didn't try it to see. The reason I did it the way I did, was because I was only interested in the first 2 bytes that were sent and just inserted the NULL behind them and threw anything else away.


----------



## Dahammer

somewhatshocked said:


> Weird: When I try using 8 alarms, no dice. When I up it to 24 (as suggested above), it works like a charm.


Are you changing dtNBR_ALARMS in TimeAlarms.h?



Code:


#define dtNBR_ALARMS 24   // max is 255

Currently, the source code requires a minimum of 10 alarms, if you use storms. The comments in the code say 12, but it's actually 10. Without storms enabled, it needs a minimum of 7. If you have dtNBR_ALARMS below that, behavior will be erratic. If you've added additional alarms to it, then you'll need to account for those as well. I think you added some to toggle the power on/off, if I recall correctly, so don't forget those.


----------



## Caver

somewhatshocked said:


> Weird: When I try using 8 alarms, no dice. When I up it to 24 (as suggested above), it works like a charm.


Way back in Indychus' first post of the code, there was a comment line...

"// Note: TimeAlarms.h must be edited to allow for 24 alarms. By default it is set to 6".

Being the Arduino coding newbie...I did it when I first installed the library, so maybe that's why mine doesn't freeze. I'm assuming it just needs to be set to a number larger than the number of alarms you have set.

If you didn't edit yours, I bet that's why you get freezes.


----------



## somewhatshocked

Yep, that's where I changed it (the 24 bit from the beginning now makes more sense!). Was actually using 8 alarms. All is well now, though.

Haven't tried storms yet.


----------



## mistergreen

Dahammer said:


> Are you sure the serial stream is automatically terminated with a NULL?


OH! I see. No it doesn't terminate with a NULL. You have to code it to do that. Not sure where Serial.write is in the code. I did a search and didn't find any.
You can use anything like a newline '\n'.

When I write apps to listen to the serial port, I use the terminator byte to help me out.

A char string is automatically terminated with a null; for future reference.


----------



## Dahammer

mistergreen said:


> OH! I see. No it doesn't terminate with a NULL. You have to code it to do that. Not sure where Serial.write is in the code. I did a search and didn't find any.
> You can use anything like a newline '\n'.
> 
> When I write apps to listen to the serial port, I use the terminator byte to help me out.
> 
> A char string is automatically terminated with a null; for future reference.


There isn't a serial.write. It never sends anything out the serial stream. It just listens for inbound commands being sent from the Arduino IDE's serial monitor. And since the command list just included decimal numbers 1 - 32, I wasn't interested in receiving anything beyond that. It was just a quick way to test that the IR codes were working without having to wait for a timer to fire. If it was going to be used to parse much else, then it would need a rewrite for sure.


----------



## Dahammer

somewhatshocked said:


> Yep, that's where I changed it (the 24 bit from the beginning now makes more sense!). Was actually using 8 alarms. All is well now, though.
> 
> Haven't tried storms yet.


8 would not be enough for you if you added alarms for the power on/off.


----------



## Dahammer

Caver said:


> Way back in Indychus' first post of the code, there was a comment line...
> 
> "// Note: TimeAlarms.h must be edited to allow for 24 alarms. By default it is set to 6".
> 
> Being the Arduino coding newbie...I did it when I first installed the library, so maybe that's why mine doesn't freeze. I'm assuming it just needs to be set to a number larger than the number of alarms you have set.
> 
> If you didn't edit yours, I bet that's why you get freezes.


It's in this code as well, although I have it Alarms.h and it should be TimeAlarms.h:


Code:


void SetAlarms()
{
  // Set up your desired alarms here
  // The default value of dtNBR_ALARMS is 6 in Alarms.h.
  // This code sets 12 alarms by default, so you'll need to change dtNBR_ALARMS to 12 or more

Also note that the higher you set dtNBR_ALARMS, the more RAM it uses. It's RAM = dtNBR_ALARMS * 11


----------



## somewhatshocked

I'd removed a couple alarms, so I had exactly 8. But it works fine now with 24.


----------



## Dahammer

somewhatshocked said:


> I'd removed a couple alarms, so I had exactly 8. But it works fine now with 24.


Did you count this one in the 8?


Code:


Alarm.timerRepeat(900, digitalClockDisplay);  // Display the time every 15 minutes


----------



## Indychus

So here's the code with LCD support... just showing the time and current mode for now.

This is the same as Dahammer's V3.3, but with LCD support for a 20 x 4 LCD. Different sizes will require tweaking. This SHOULD run even if there is no LCD installed...

*Version 3.4
*


Code:


///////////////////////////////////////////////////////////////////
// Current Satellite LED+ Controller  V3.3                       //
//   Indychus...Dahammer...mistergreen @ plantedtank.net         //
//   This code is public domain.  Pass it on.                    //
//   Confirmed on Arduino UNO 1.0.5                              //
//   Req. Time, TimeAlarms, RTClib, IRremote                     //
///////////////////////////////////////////////////////////////////
//
// This version uses Ken Shirriff's IRremote library to Rx/Tx the IR codes
// http://www.righto.com/2009/08/multi-protocol-infrared-remote-library.html
// 
// This code does NOT use PIN 13 on the Uno, as do previous versions
// Instead PIN 3, which is a PWM pin, is used. So you'll need to connect
// your LED to PIN 3 instead of PIN 13 for it to work.

// Install LCD per instructions at http://learn.adafruit.com/character-lcds/overview

#include <Wire.h>
#include <RTClib.h>
#include <Time.h>
#include <TimeAlarms.h>
#include <IRremote.h>
#include <LiquidCrystal.h>

RTC_DS1307 RTC;
IRsend irsend;
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);

/*********** BEGIN USER DEFINED VARIABLES ***********/
// DEBUG_IR adds the option to test the IR commands via the Arduino software's serial monitor
// You can send any value from 1 to 32 and it send the corresponding IR code
// The codes follow the remote controller, left to right, top to bottom
// e.g 1 = Orange, 2 = Blue, 21 = Moon1, etc
#define DEBUG_IR

int postDelay = 100;         // Delay after codes are sent
int randAnalogPin = 0;       // This needs to be set to an unused Analog pin, Used by ThunderStorm()

// Current Satellite+ IR Codes (NEC Protocol)
unsigned long codeHeader = 0x20DF; // Always the same

// Remote buttons listed left to right, top to bottom
unsigned int codeOrange = 0x3AC5;
unsigned int codeBlue = 0xBA45;
unsigned int codeRose = 0x827D;
unsigned int codePowerOnOff = 0x02FD;
unsigned int codeWhite = 0x1AE5;
unsigned int codeFullSpec = 0x9A65;
unsigned int codePurple = 0xA25D;
unsigned int codePlay = 0x22DD;
unsigned int codeRedUp = 0x2AD5;
unsigned int codeGreenUp = 0xAA55;
unsigned int codeBlueUp = 0x926D;
unsigned int codeWhiteUp = 0x12ED;
unsigned int codeRedDown = 0x0AF5;
unsigned int codeGreenDown = 0x8A75;
unsigned int codeBlueDown = 0xB24D;
unsigned int codeWhiteDown = 0x32CD;
unsigned int codeM1Custom = 0x38C7;
unsigned int codeM2Custom = 0xB847;
unsigned int codeM3Custom = 0x7887;
unsigned int codeM4Custom = 0xF807;
unsigned int codeMoon1 = 0x18E7;
unsigned int codeMoon2 = 0x9867;
unsigned int codeMoon3 = 0x58A7;
unsigned int codeDawnDusk = 0xD827;
unsigned int codeCloud1 = 0x28D7;
unsigned int codeCloud2 = 0xA857;
unsigned int codeCloud3 = 0x6897;
unsigned int codeCloud4 = 0xE817;
unsigned int codeStorm1 = 0x08F7;
unsigned int codeStorm2 = 0x8877;
unsigned int codeStorm3 = 0x48B7;
unsigned int codeStorm4 = 0xC837;

void SetAlarms()
{
  // Set up your desired alarms here
  // The default value of dtNBR_ALARMS is 6 in Alarms.h.
  // This code sets 12 alarms by default, so you'll need to change dtNBR_ALARMS to 12 or more
  Alarm.alarmRepeat(7,00,0, DawnDusk);
  Alarm.alarmRepeat(9,00,0, Cloud2);     // (HR,MIN,SEC,FUNCTION)
  Alarm.alarmRepeat(11,00,0, FullSpec);
  Alarm.alarmRepeat(15,00,0, Cloud2);
  Alarm.alarmRepeat(19,00,0, DawnDusk);
  Alarm.alarmRepeat(21,18,0, Cloud2);
  
  // Comment these out if you don't want the chance of a random storm each day
  Alarm.alarmRepeat(12,00,00, ThunderStorm);
  ThunderStorm();  // Sets up intial storm so we don't have wait until alarm time
}
/************* END USER DEFINED VARIABLES *************/

void setup()
{
  Wire.begin();
  RTC.begin();
  lcd.begin(20, 4);
  Serial.begin(9600);
      //Serial.println(freeRam());
  
  if (! RTC.isrunning()) { 
    Serial.println("RTC Error");
    RTC.adjust(DateTime(__DATE__, __TIME__));}  //Adjust to compile time
    
  
  setSyncProvider(syncProvider);     //reference our syncProvider function instead of RTC_DS1307::get()
  
  Alarm.timerRepeat(900, digitalClockDisplay);  // Display the time every 15 minutes
  digitalClockDisplay();
  SetAlarms();
  }

void loop()
{
#ifdef DEBUG_IR 
  if (Serial.available() > 0) {
    delay(5); //Wait for transmission to finish
    CurrentCMDs(SerialReadInt());
  }
#endif
  Alarm.delay(100); 
  // Service alarms & wait (msec)
  lcdClockDisplay();
}

time_t syncProvider()
{
  //this does the same thing as RTC_DS1307::get()
  return RTC.now().unixtime();
}

void ThunderStorm ()
{ 
  // Schedules a storm between 1 & 9 in the evening
  // It sets Storm2, followed by Cloud2 or DawnDusk or Moon2, depending on when the storm is over
  randomSeed(analogRead(randAnalogPin));  // Generate random seed on unused pin
  byte RH = random(23);                   // Randomizer for thunderstorm
  byte RM = random(59);
  byte RS = random(59);
  byte TSDurationH = random(2);
  byte TSDurationM = random(59);
  
  if (RH <= 12)
    {
      Serial.println("No storm today");
      return;
    }
      
  if (RH > 12)                             // If random value is after 1 pm, allow storm
    {
      Alarm.alarmOnce(RH,RM,RS,Storm2);
      Serial.print("Storm @ ");
      Serial.print(RH);
      printDigits(RM);
      printDigits(RS);
      Serial.print("   ");
      Serial.print("Duration = ");
      Serial.print(TSDurationH);
      printDigits(TSDurationM);
      Serial.println();
      
      if ((RH + TSDurationH) < 19)   // Return to Cloud2 if storm ends between 1-7pm
        {
          Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,Cloud2);
        }
      else if ((RH + TSDurationH) < 21)  // Return to DawnDusk if storm ends between 7-9pm
        {
          Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,DawnDusk);
        }
      else                                       // Return to Night2 if storm ends after 9pm
        {
          Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,Moon2);
        }
    }
}

void digitalClockDisplay()          // Digital clock
{ 
  Serial.print("Time = ");
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.println(); }

  
void lcdClockDisplay()  
  {lcd.setCursor(0,0);
    lcd.print(hour());
  lcdDigits(minute());}

void printDigits(int digits)        // Add :
{
  Serial.print(":");
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits);
}

void lcdDigits(int digits)        // Add :
{
  lcd.print(":");
  if(digits < 10)
    lcd.print('0');  
  lcd.print(digits);
}

#ifdef DEBUG_IR
int SerialReadInt()
{
  int i, numAva;
  char inBytes[3];                  // Array to hold the bytes
  char * inBytesPtr = &inBytes[0];  // Pointer to the first element of the array
      
    numAva = Serial.available();    // Read number of input bytes
    if (numAva > 2)
      numAva = 2;                   // Only allow 2 characters to prevent overflow
      
    for (i=0; i<numAva; i++)        // Load input bytes into array
      inBytes[i] = Serial.read();
    inBytes[i] =  '\0';             // Put NULL character at the end
    return atoi(inBytesPtr);        // Call atoi function and return result
}

void CurrentCMDs (int cmd)
{
  switch (cmd)
  {
    case 1:
      Orange();
      break;
    case 2:
      Blue();
      break;
    case 3:
      Rose();
      break;
    case 4:
      PowerOnOff();
      break;
    case 5:
      White();
      break;
    case 6:
      FullSpec();
      break;
    case 7:
      Purple();
      break;
    case 8:
      Play();
      break;
    case 9:
      RedUp();
      break;
    case 10:
      GreenUp();
      break;
    case 11:
      BlueUp();
      break;
    case 12:
      WhiteUp();
      break;
    case 13:
      RedDown();
      break;
    case 14:
      GreenDown();
      break;
    case 15:
      BlueDown();
      break;
    case 16:
      WhiteDown();
      break;
    case 17:
      M1Custom();
      break;
    case 18:
      M2Custom();
      break;
    case 19:
      M3Custom();
      break;
    case 20:
      M4Custom();
      break;
    case 21:
      Moon1();
      break;
    case 22:
      Moon2();
      break;
    case 23:
      Moon3();
      break;
    case 24:
      DawnDusk();
      break;
    case 25:
      Cloud1();
      break;
    case 26:
      Cloud2();
      break;
    case 27:
      Cloud3();
      break;
    case 28:
      Cloud4();
      break;
    case 29:
      Storm1();
      break;
    case 30:
      Storm2();
      break;
    case 31:
      Storm3();
      break;
    case 32:
      Storm4();
      break;
    default:
      Serial.println("Invalid Choice");
    }
}
#endif

void SendCode (unsigned int code, byte numTimes, const char *sMessage)
{
  unsigned long irCode = (codeHeader << 16) + code; // Header is 2 bytes, shift all the way to left & add code to it
  
  for( int i = 0; i < numTimes; i++)
  {
    irsend.sendNEC(irCode,32); // Send code
    Alarm.delay(postDelay);
  }
  Serial.print(sMessage);  // Print message
  digitalClockDisplay();
}

void Orange()
{ 
  SendCode(codeOrange, 2, "Orange: "); 
  lcd.setCursor(6,0);
  lcd.print("Orange        ");
}

void Blue()
{ 
  SendCode(codeBlue, 2, "Blue: ");
 lcd.setCursor(6,0);
  lcd.print("Blue          "); 
}

void Rose()
{ 
  SendCode(codeRose, 2, "Rose: ");
 lcd.setCursor(6,0);
  lcd.print("Rose        "); 
}

void PowerOnOff()
{ 
  SendCode(codePowerOnOff, 1, "Power On/Off: ");
}

void White()
{ 
  SendCode(codeWhite, 2, "White: ");
 lcd.setCursor(6,0);
  lcd.print("White         "); 
}

void FullSpec()
{
  SendCode(codeFullSpec, 2, "FullSpec: ");
  lcd.setCursor(6,0);
  lcd.print("Full Spectrum ");
}

void Purple()
{
  SendCode(codePurple, 2, "Purple: ");
  lcd.setCursor(6,0);
  lcd.print("Purple        ");
}

void Play()
{ 
  SendCode(codePlay, 1, "Play/Pause: ");
}

void RedUp()
{
  SendCode(codeRedUp, 1, "RedUp: ");

}

void GreenUp()
{
  SendCode(codeGreenUp, 1, "GreenUp: ");
}

void BlueUp()
{
  SendCode(codeBlueUp, 1, "BlueUp: ");

}

void WhiteUp()
{
  SendCode(codeWhiteUp, 1, "WhiteUp: ");

}

void RedDown()
{
  SendCode(codeRedDown, 1, "RedDown: ");

}

void GreenDown()
{
  SendCode(codeGreenDown, 1, "GreenDown: ");
}

void BlueDown()
{
  SendCode(codeBlueDown, 1, "BlueDown: ");

}

void WhiteDown()
{
  SendCode(codeWhite, 1, "WhiteDown: ");

}

void M1Custom()
{
  SendCode(codeM1Custom, 2, "M1Custom: ");
  lcd.setCursor(6,0);
  lcd.print("Custom Mix 1  ");
}

void M2Custom()
{
  SendCode(codeM2Custom, 2, "M2Custom: ");
  lcd.setCursor(6,0);
  lcd.print("Custom Mix 2  ");
}

void M3Custom()
{
  SendCode(codeM3Custom, 2, "M3Custom: ");
  lcd.setCursor(6,0);
  lcd.print("Custom Mix 3  ");
}

void M4Custom()
{
  SendCode(codeM4Custom, 2, "M4Custom: ");
  lcd.setCursor(6,0);
  lcd.print("Custom Mix 4  ");
}

void Moon1()
{
  SendCode(codeMoon1, 2, "Moon1: ");
  lcd.setCursor(6,0);
  lcd.print("Moonlight 1   ");
}

void Moon2()
{
  SendCode(codeMoon2, 2, "Moon2: ");
  lcd.setCursor(6,0);
  lcd.print("Moonlight 2   ");
}

void Moon3()
{
  SendCode(codeMoon3, 2, "Moon3: ");
  lcd.setCursor(6,0);
  lcd.print("Moonlight 3   ");
}

void DawnDusk()
{
  SendCode(codeDawnDusk, 2, "DawnDusk: ");
  lcd.setCursor(6,0);
  lcd.print("Dawn / Dusk   ");
}

void Cloud1()
{
  SendCode(codeCloud1, 2, "Cloud1: ");
  lcd.setCursor(6,0);
  lcd.print("Cloud Cover 1 ");
}

void Cloud2()
{
  SendCode(codeCloud2, 2, "Cloud2: ");
  lcd.setCursor(6,0);
  lcd.print("Cloud Cover 2 ");
}
  
void Cloud3()
{
  SendCode(codeCloud3, 2, "Cloud3: ");
  lcd.setCursor(6,0);
  lcd.print("Cloud Cover 3 ");
}

void Cloud4()
{
  SendCode(codeCloud4, 2, "Cloud4: ");
  lcd.setCursor(6,0);
  lcd.print("Cloud Cover 4 ");
}

void Storm1()
{
  SendCode(codeStorm1, 2, "Storm1: ");
  lcd.setCursor(6,0);
  lcd.print("Thunderstorm 1");
}

void Storm2()
{
  SendCode(codeStorm2, 2, "Storm2: ");
  lcd.setCursor(6,0);
  lcd.print("Thunderstorm 2");
}

void Storm3()
{
  SendCode(codeStorm3, 2, "Storm3: ");
  lcd.setCursor(6,0);
  lcd.print("Thunderstorm 3");
}

void Storm4()
{
  SendCode(codeStorm4, 2, "Storm4: ");
  lcd.setCursor(6,0);
  lcd.print("Thunderstorm 4");
}

int freeRam () {
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}


----------



## Dahammer

On a side note, the Uno only has 2k of SRAM and this is where it creates and manipulates all of it's variables. When I was testing early in the week, I started off with longer string messages like was in the other code, "FullSpec Mode has been Initialized", for all of the IR codes. The code maxed out the 2k SRAM and I got strange behavior from it, so I had to scale the strings back some. If this code continues to evolve, we may have to move some of it to EEPROM and/or optimize some of the code.


----------



## Indychus

And here's the LCD installed:










I would normally do a step-by-step of it, but I just followed the Adafruit guide for LCDs.


----------



## somewhatshocked

Indychus: You're going to bankrupt me with all of these ideas.


----------



## Dahammer

Indychus said:


> So here's the code with LCD support... just showing the time for now, but will add in the current mode, upcoming storm time, and temperature display if you want to use a thermocouple in the tank. Maybe pH meter support as well, but I'm not so sure what's involved in measuring pH just yet.
> 
> This is the same as Dahammer's V3.3, but with LCD support for a 20 x 4 LCD. Different sizes will require tweaking. This SHOULD run even if there is no LCD installed...


Got home early, eh? Cool! My only thought is that if you want a tick the clock along every second, like a real clock, it may be better to service it in loop() rather than with an alarm every second. You can change that delay in loop() to update it every second if you want. I had it set to a 10th of a second just to get a fast response from data being sent in on the serial port. Servicing the clock would be fine there, I think. You may just notice some delays when you send in a command.

Did the newer code work on your fixture?


----------



## Indychus

Yeah, newer code works great! Changing clock right now actually, and added in the mode display on the LCD... only around 250 bytes available in sram with all of the extra strings though... gonna be tight.

Will shortening variable names free up some more sram?


----------



## Indychus

Code:


{
#ifdef DEBUG_IR 
  if (Serial.available() > 0) {
    delay(5); //Wait for transmission to finish
    CurrentCMDs(SerialReadInt());
  }
#endif
  Alarm.delay(100);               // Service alarms & wait (msec)
}

Does the delay(5) here not stop everything for 5 ms? I have been trying to avoid using delays because they stop the entire program from proceeding. I'd like to use a delay for the clock update frequency...


----------



## mistergreen

You guys are inspiring me to finish up my aquarium web controller. maybe heh... I have to learn another bit of tech for a teaching gig... It never ends.


----------



## Indychus

Edited V3.4 above to reflect the changes to the clock and add current mode to the LCD display.


----------



## Indychus




----------



## Indychus

Dahammer said:


> Got home early, eh? Cool!


Yeah, finally got everything working as it should and a happy customer, so I get to come home. Should have a week or two to tinker before my next job.


----------



## Dahammer

Indychus said:


> Code:
> 
> 
> {
> #ifdef DEBUG_IR
> if (Serial.available() > 0) {
> delay(5); //Wait for transmission to finish
> CurrentCMDs(SerialReadInt());
> }
> #endif
> Alarm.delay(100);               // Service alarms & wait (msec)
> }
> 
> Does the delay(5) here not stop everything for 5 ms? I have been trying to avoid using delays because they stop the entire program from proceeding. I'd like to use a delay for the clock update frequency...


Yes, it does but only if there is data available on the serial port. It may not be needed. I just added it to make sure the data was ready to read and never tried it without it.

We can't process everything at once, so we'll just have to prioritize things. You could move the LCD clock to the top of loop() and make it priority #1, then check the serial port to see if anything is on it, then finally service the alarms. You can change Alarm.delay(100) to 0, if you want and it will still service the alarms. You may get faster response for the clock using delay() instead, since it doesn't have to service the alarms.

The only problem with ticking seconds on the clock is that if something comes in on the serial port and/or an alarm triggers, then the clock update is going to be delayed a bit. But there isn't anything you can do about that other than just not display seconds.


----------



## Indychus

I already removed the seconds... I'm just calling the LCD clock function in loop now and it seems to work ok with no delay. Not sure how it affects anything else though.


----------



## Dahammer

Indychus said:


> Will shortening variable names free up some more sram?


No, the compiler doesn't store the names of variables in the assembled code that is ultimately loaded onto the Atmel. It couldn't careless what the names of the variables are, it just addresses them by addresses in memory.

Only thing we can do is optimize the code, shorten variable lengths by using shorter strings and/or smaller types where we don't need the larger ones, and/or move stuff to EEPROM. A lot of the static varibles, like the strings and IR codes can be move to EEPROM to free up room in sram.


----------



## mistergreen

You can use millis() without using delay(). It's an old trick to not slow down your app.



Code:


 unsigned long currentMillis = millis();

 if(currentMillis - previousMillis > 100) {
    // save the last time time
    previousMillis = currentMillis;   
     if (Serial.available() > 0) {
         CurrentCMDs(SerialReadInt());
     }
    
  }


----------



## Dahammer

Code:


void Orange()
{ 
  SendCode(codeOrange, 2, "Orange: "); 
}

We don't really "need" all of these strings in all those ir code functions, if push comes to shove. It's just for debugging and eye candy. They could be made optional when in debug mode or something along those lines.


----------



## Indychus

Yeah, removing them would save some space. Most people will never see stuff printed to the serial monitor anyways, and its redundant if you use an LCD.

I'm curious about sending some of the stuff to EEPROM... Seems like a good option, but no idea how to implement it. As I understand it, we aren't using any of the available space there currently.

I have a bit of code at the end of the last version above that displays available sram, but the call for it is commented out. I'm getting 251 remaining right now. Not sure if usage will increase over time while the sketch runs or not.

Sent from my HTC One X


----------



## Indychus

That 251 is with 24 alarms reserved... We could reduce that, but I don't think 11 bytes each is enough to worry about... 

Sent from my HTC One X


----------



## Dahammer

We can also use some of the available flash memory, aka PROGMEM, to store stuff, if need be.

Here is a link to some info on EEPROM.

They're both kind of a pain to deal with and I wouldn't bother except as a last resort.


----------



## Dahammer

You could add you lcd code to SendCode() and save a good bit of sram and some flash as well

Like so:


Code:


void SendCode (unsigned int code, byte numTimes, const char *sMessage)
{
  unsigned long irCode = (codeHeader << 16) + code; // Header is 2 bytes, shift all the way to left & add code to it
  
  for( int i = 0; i < numTimes; i++)
  {
    irsend.sendNEC(irCode,32); // Send code
    Alarm.delay(postDelay);
  }
  Serial.print(sMessage);  // Print message
  lcd.setCursor(6,0);
  lcd.print(sMessage);
  digitalClockDisplay();
}

void Orange()
{ 
  SendCode(codeOrange, 2, "Orange        ");
}

It would be even more efficient to write a function that will add your space characters to the end of the string for you on the fly, rather than to state them statically in the code as "Orange " and so on. You could use printf/sprintf, I think, but I haven't tried it under the Arduino, so I'm not sure if it is fully functional or not; plus it may bloat the code so much that it would be a smaller footprint to just roll your own. There are probably some C++ library functions available as well, but I'm not that familiar with C++, I stuck to mostly C back in the day. And it's been so long I have to look all of those up these days. haha!


----------



## Indychus

Hmmm... It seems that just adding the F here stores the string in flash instead of sram... Haven't tried it yet but we probably have more than enough room to move all of the text strings to flash...



Code:


 Serial.println(F("This string will be stored in flash memory"));

Sent from my HTC One X


----------



## Dahammer

Indychus said:


> Hmmm... It seems that just adding the F here stores the string in flash instead of sram... Haven't tried it yet but we probably have more than enough room to move all of the text strings to flash...
> 
> 
> 
> Code:
> 
> 
> Serial.println(F("This string will be stored in flash memory"));
> 
> Sent from my HTC One X


By George, it appears that you are correct! Nice of the developers to do that, huh? 

You might want to read this thread and suppress the warnings that will result.


----------



## Caver

I loaded the latest LCD code Indycus posted into my spare Arduino with a 16x2 LCD shield.

After I edited it for the right pinout and shortened some of the text lines it looks pretty good.

The time display for 12:30am is pretty funky...0:30...but most people will never see it...except for us night owl tinkerers...:biggrin:

Thanks again for all the work! I'll be following with great interest!


----------



## Indychus

Yeah, I need to fix that. Noticed it early this morning. I'm going to preface single digit hours with a 0 so it overwrites the previous time correctly. Midnight will still be 0, but right now it overwrites 11:59 with 0:01 and you get 0:019... The 9 hangs around until it gets back to a double digit hour.

Sent from my HTC One X


----------



## Indychus

This fixes the overwrite issue on the LCD clock and merges the LCD mode prints into Dahammer's code to save SRAM. 403 bytes of SRAM available (up from 251).

Also shows scheduled storm on LCD now.

*Version 3.5*



Code:


///////////////////////////////////////////////////////////////////
// Current Satellite LED+ Controller  V3.5                       //
//   Indychus...Dahammer...mistergreen @ plantedtank.net         //
//   This code is public domain.  Pass it on.                    //
//   Confirmed on Arduino UNO 1.0.5                              //
//   Req. Time, TimeAlarms, RTClib, IRremote                     //
///////////////////////////////////////////////////////////////////
//
// This version uses Ken Shirriff's IRremote library to Rx/Tx the IR codes
// http://www.righto.com/2009/08/multi-protocol-infrared-remote-library.html
// 
// This code does NOT use PIN 13 on the Uno, as do previous versions
// Instead PIN 3, which is a PWM pin, is used. So you'll need to connect
// your LED to PIN 3 instead of PIN 13 for it to work.

// Install LCD per instructions at http://learn.adafruit.com/character-lcds/overview

#include <Wire.h>
#include <RTClib.h>
#include <Time.h>
#include <TimeAlarms.h>
#include <IRremote.h>
#include <LiquidCrystal.h>

RTC_DS1307 RTC;
IRsend irsend;
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);

/*********** BEGIN USER DEFINED VARIABLES ***********/
// DEBUG_IR adds the option to test the IR commands via the Arduino software's serial monitor
// You can send any value from 1 to 32 and it send the corresponding IR code
// The codes follow the remote controller, left to right, top to bottom
// e.g 1 = Orange, 2 = Blue, 21 = Moon1, etc
#define DEBUG_IR

int postDelay = 100;         // Delay after codes are sent
int randAnalogPin = 0;       // This needs to be set to an unused Analog pin, Used by ThunderStorm()

// Current Satellite+ IR Codes (NEC Protocol)
unsigned long codeHeader = 0x20DF; // Always the same

// Remote buttons listed left to right, top to bottom
unsigned int codeOrange = 0x3AC5;
unsigned int codeBlue = 0xBA45;
unsigned int codeRose = 0x827D;
unsigned int codePowerOnOff = 0x02FD;
unsigned int codeWhite = 0x1AE5;
unsigned int codeFullSpec = 0x9A65;
unsigned int codePurple = 0xA25D;
unsigned int codePlay = 0x22DD;
unsigned int codeRedUp = 0x2AD5;
unsigned int codeGreenUp = 0xAA55;
unsigned int codeBlueUp = 0x926D;
unsigned int codeWhiteUp = 0x12ED;
unsigned int codeRedDown = 0x0AF5;
unsigned int codeGreenDown = 0x8A75;
unsigned int codeBlueDown = 0xB24D;
unsigned int codeWhiteDown = 0x32CD;
unsigned int codeM1Custom = 0x38C7;
unsigned int codeM2Custom = 0xB847;
unsigned int codeM3Custom = 0x7887;
unsigned int codeM4Custom = 0xF807;
unsigned int codeMoon1 = 0x18E7;
unsigned int codeMoon2 = 0x9867;
unsigned int codeMoon3 = 0x58A7;
unsigned int codeDawnDusk = 0xD827;
unsigned int codeCloud1 = 0x28D7;
unsigned int codeCloud2 = 0xA857;
unsigned int codeCloud3 = 0x6897;
unsigned int codeCloud4 = 0xE817;
unsigned int codeStorm1 = 0x08F7;
unsigned int codeStorm2 = 0x8877;
unsigned int codeStorm3 = 0x48B7;
unsigned int codeStorm4 = 0xC837;

void SetAlarms()
{
  // Set up your desired alarms here
  // The default value of dtNBR_ALARMS is 6 in Alarms.h.
  // This code sets 12 alarms by default, so you'll need to change dtNBR_ALARMS to 12 or more
  Alarm.alarmRepeat(7,00,0, DawnDusk);
  Alarm.alarmRepeat(9,00,0, Cloud2);     // (HR,MIN,SEC,FUNCTION)
  Alarm.alarmRepeat(11,00,0, FullSpec);
  Alarm.alarmRepeat(15,00,0, Cloud2);
  Alarm.alarmRepeat(19,00,0, DawnDusk);
  Alarm.alarmRepeat(21,18,0, Cloud2);
  
  // Comment these out if you don't want the chance of a random storm each day
  Alarm.alarmRepeat(12,00,00, ThunderStorm);
  ThunderStorm();  // Sets up intial storm so we don't have wait until alarm time
}
/************* END USER DEFINED VARIABLES *************/

void setup()
{
  Wire.begin();
  RTC.begin();
  lcd.begin(20, 4);
  Serial.begin(9600);
      //Serial.println(freeRam());
  
  if (! RTC.isrunning()) { 
    Serial.println("RTC Error");
    RTC.adjust(DateTime(__DATE__, __TIME__));}  //Adjust to compile time
    
  
  setSyncProvider(syncProvider);     //reference our syncProvider function instead of RTC_DS1307::get()
  
  Alarm.timerRepeat(900, digitalClockDisplay);  // Display the time every 15 minutes
  digitalClockDisplay();
  SetAlarms();
 Serial.print("SRAM : ");          //un-comment these line to check available SRAM
 Serial.println(freeRam());}   

void loop()
{
#ifdef DEBUG_IR 
  if (Serial.available() > 0) {
    delay(5); //Wait for transmission to finish
    CurrentCMDs(SerialReadInt());
  }
#endif
  Alarm.delay(100); 
  // Service alarms & wait (msec)
  lcdClockDisplay();
}

time_t syncProvider()
{
  //this does the same thing as RTC_DS1307::get()
  return RTC.now().unixtime();
}

void ThunderStorm ()
{ 
  // Schedules a storm between 1 & 9 in the evening
  // It sets Storm2, followed by Cloud2 or DawnDusk or Moon2, depending on when the storm is over
  randomSeed(analogRead(randAnalogPin));  // Generate random seed on unused pin
  byte RH = random(23);                   // Randomizer for thunderstorm
  byte RM = random(59);
  byte RS = random(59);
  byte TSDurationH = random(2);
  byte TSDurationM = random(59);
  
  if (RH <= 12)
    {
      Serial.println("No storm today");
      lcd.setCursor(0,1);
      lcd.print("No storm today");
      return;
    }
      
  if (RH > 12)                             // If random value is after 1 pm, allow storm
    {
      Alarm.alarmOnce(RH,RM,RS,Storm2);
      Serial.print("Next Storm: ");
      Serial.print(RH);
      printDigits(RM);
      printDigits(RS);
      Serial.print("   ");
      Serial.print("Duration = ");
      Serial.print(TSDurationH);
      printDigits(TSDurationM);
      Serial.println();
     lcd.setCursor(0,1);
     lcd.print("Next Storm: ");
     lcdHRdigits(RH);
     lcdDigits(RM);}
      
      if ((RH + TSDurationH) < 19)   // Return to Cloud2 if storm ends between 1-7pm
        {Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,Cloud2);}
      else if ((RH + TSDurationH) < 21)  // Return to DawnDusk if storm ends between 7-9pm
        {Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,DawnDusk);}
      else                                       // Return to Night2 if storm ends after 9pm
        {Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,Moon2);}
    }


void digitalClockDisplay()          // Digital clock
{ 
  Serial.print("Time = ");
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.println(); }

  
void lcdClockDisplay()  
  {lcd.setCursor(0,0);
    lcdHRdigits(hour());
  lcdDigits(minute());}

void printDigits(int digits)        // Add :
{
  Serial.print(":");
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits);
}

void lcdDigits(int digits)        // Add :
{
  lcd.print(":");
  if(digits < 10)
    lcd.print('0');  
  lcd.print(digits);
}
void lcdHRdigits(int HRdigits)        // Preface hour with 0
{
  if(HRdigits < 10)
    lcd.print('0');  
  lcd.print(HRdigits);
}

#ifdef DEBUG_IR
int SerialReadInt()
{
  int i, numAva;
  char inBytes[3];                  // Array to hold the bytes
  char * inBytesPtr = &inBytes[0];  // Pointer to the first element of the array
      
    numAva = Serial.available();    // Read number of input bytes
    if (numAva > 2)
      numAva = 2;                   // Only allow 2 characters to prevent overflow
      
    for (i=0; i<numAva; i++)        // Load input bytes into array
      inBytes[i] = Serial.read();
    inBytes[i] =  '\0';             // Put NULL character at the end
    return atoi(inBytesPtr);        // Call atoi function and return result
}

void CurrentCMDs (int cmd)
{switch (cmd)
  {
    case 1:
      Orange();
      break;
    case 2:
      Blue();
      break;
    case 3:
      Rose();
      break;
    case 4:
      PowerOnOff();
      break;
    case 5:
      White();
      break;
    case 6:
      FullSpec();
      break;
    case 7:
      Purple();
      break;
    case 8:
      Play();
      break;
    case 9:
      RedUp();
      break;
    case 10:
      GreenUp();
      break;
    case 11:
      BlueUp();
      break;
    case 12:
      WhiteUp();
      break;
    case 13:
      RedDown();
      break;
    case 14:
      GreenDown();
      break;
    case 15:
      BlueDown();
      break;
    case 16:
      WhiteDown();
      break;
    case 17:
      M1Custom();
      break;
    case 18:
      M2Custom();
      break;
    case 19:
      M3Custom();
      break;
    case 20:
      M4Custom();
      break;
    case 21:
      Moon1();
      break;
    case 22:
      Moon2();
      break;
    case 23:
      Moon3();
      break;
    case 24:
      DawnDusk();
      break;
    case 25:
      Cloud1();
      break;
    case 26:
      Cloud2();
      break;
    case 27:
      Cloud3();
      break;
    case 28:
      Cloud4();
      break;
    case 29:
      Storm1();
      break;
    case 30:
      Storm2();
      break;
    case 31:
      Storm3();
      break;
    case 32:
      Storm4();
      break;
    default:
      Serial.println("Invalid Choice");}}
#endif

void SendCode (unsigned int code, byte numTimes, const char *sMessage)
{unsigned long irCode = (codeHeader << 16) + code; // Header is 2 bytes, shift all the way to left & add code to it
  
  for( int i = 0; i < numTimes; i++)
  {irsend.sendNEC(irCode,32); // Send code
    Alarm.delay(postDelay);}
    
  Serial.println(sMessage);  // Print message
  lcd.setCursor(6,0);
  lcd.print(sMessage);
  digitalClockDisplay();}

void Orange()
{SendCode(codeOrange, 2, "Orange        ");}

void Blue()
{SendCode(codeBlue, 2, "Blue           ");}

void Rose()
{SendCode(codeRose, 2, "Rose           ");}

void PowerOnOff()
{SendCode(codePowerOnOff, 1, "Power On/Off  ");}

void White()
{SendCode(codeWhite, 2, "White         ");}

void FullSpec()
{SendCode(codeFullSpec, 2, "Full Spectrum ");}

void Purple()
{SendCode(codePurple, 2, "Purple        ");}

void Play()
{SendCode(codePlay, 1, "Play/Pause:   ");}

void RedUp()
{SendCode(codeRedUp, 1, "Red Up        ");}

void GreenUp()
{SendCode(codeGreenUp, 1, "Green Up      ");}

void BlueUp()
{SendCode(codeBlueUp, 1, "Blue          ");}

void WhiteUp()
{SendCode(codeWhiteUp, 1, "White Up      ");}

void RedDown()
{SendCode(codeRedDown, 1, "Red Down      ");}

void GreenDown()
{SendCode(codeGreenDown, 1, "Green Down    ");}

void BlueDown()
{SendCode(codeBlueDown, 1, "Blue Down     ");}

void WhiteDown()
{SendCode(codeWhite, 1, "White Down    ");}

void M1Custom()
{SendCode(codeM1Custom, 2, "Custom Mix 1  ");}

void M2Custom()
{SendCode(codeM2Custom, 2, "Custom Mix 2  ");}

void M3Custom()
{SendCode(codeM3Custom, 2, "Custom Mix 3  ");}

void M4Custom()
{SendCode(codeM4Custom, 2, "Custom Mix 4  ");}

void Moon1()
{SendCode(codeMoon1, 2, "Moonlight 1   ");}

void Moon2()
{SendCode(codeMoon2, 2, "Moonlight 2   ");}

void Moon3()
{SendCode(codeMoon3, 2, "Moonlight 3   ");}

void DawnDusk()
{SendCode(codeDawnDusk, 2, "Dawn/Dusk     ");}

void Cloud1()
{SendCode(codeCloud1, 2, "Cloud Cover 1 ");}

void Cloud2()
{SendCode(codeCloud2, 2, "Cloud Cover 2 ");}
  
void Cloud3()
{SendCode(codeCloud3, 2, "Cloud Cover 3 ");}

void Cloud4()
{SendCode(codeCloud4, 2, "Cloud Cover 4 ");}

void Storm1()
{SendCode(codeStorm1, 2, "Thunderstorm 1");}

void Storm2()
{SendCode(codeStorm2, 2, "Thunderstorm 2");}

void Storm3()
{SendCode(codeStorm3, 2, "Thunderstorm 3");}

void Storm4()
{SendCode(codeStorm4, 2, "Thunderstorm 4");}

int freeRam () {
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}


----------



## Indychus

Caver said:


> Still waiting for my waterproof temperature sensors (DS18b20's) to get here, so I can build up the 2 channel thermometer for the tanks.
> 
> Would be cool if we could incorporate the thermometer code into this one...then one unit could do both jobs...:biggrin:



I just ordered a DS18b20 from sparkfun, so I'll get that running when it shows up. 

I've been looking into what it takes to economically read TDS and pH, but I think we are beginning to tax the capabilities of the Uno.


----------



## Caver

Indychus said:


> I just ordered a DS18b20 from sparkfun, so I'll get that running when it shows up.
> 
> I've been looking into what it takes to economically read TDS and pH, but I think we are beginning to tax the capabilities of the Uno.


I ordered a few Arduino doo-dads on Amazon last week. When I was shopping, I found a package of 5 waterproof DS18b20's that qualified for cheap 'Prime' shipping, but I didn't notice that they shipped from China until after I ordered...so I won't have mine for a couple of weeks...:confused1:

I've found a few tutorials online for indoor/outdoor thermometers with that sensor that look like they will work for my application to monitor 2 tanks. Just modify the display code for tanks rather than in/out. After playing with your code for my light I know how to do that now...:biggrin:

Just about to the point I'm going to look into packaging mine so it looks 'pretty'.


----------



## Indychus

There are a lot of cases available specifically for the Arduino, but an old router or modem case and a little hot glue works wonders for damn near free.


----------



## Caver

By trade, I'm an electronics tech...old school...I know hardware pretty well, but software makes my head hurt...:icon_eek:

Packaging little projects like this is one of my favorite things. I have a couple of Arduino boxes, but they aren't quite right for this piece.

I may fabricate my own shield board for the RTC and LED driver on some perf board so the display board lines up better. I'll have to rummage through my junk box to see if I have something I can modify for the box.


----------



## Dahammer

Here is what I meant by adding the white spaces dynamically versus storing them in sram:


Code:


  lcd.print(sMessage);
  for(byte i = sizeof(sMessage); i <= 14; i++)
    lcd.print(" ");
  digitalClockDisplay();}

 That will save probably another 100 bytes or so of sram.

You can also store the strings in flash using the F() you discovered and still pass them to the SendCode() function, if you want too. You'll just need to change the function's declaration like so:


Code:


void SendCode (unsigned int code, byte numTimes, const __FlashStringHelper* sMessage)

And of course you'll have to wrap all of the strings that are being passed to the function with F() calls:


Code:


void Orange()
{tSendCode(codeOrange, 2, F("Orange"));}


----------



## Indychus

Here's mine with everything crammed into my old router case. Finally soldered everything up and got rid of the breadboard. Pretty good fit... I still have to cut the hole in the top for the LCD. I know it's a rat's nest, but it's all working properly.


----------



## Indychus

Sweet, at 535 SRAM remaining with that change. I'm gonna hold off on moving the strings to flash unless it's absolutely necessary.

*Version 3.6*


Code:


///////////////////////////////////////////////////////////////////
// Current Satellite LED+ Controller  V3.5                       //
//   Indychus...Dahammer...mistergreen @ plantedtank.net         //
//   This code is public domain.  Pass it on.                    //
//   Confirmed on Arduino UNO 1.0.5                              //
//   Req. Time, TimeAlarms, RTClib, IRremote                     //
///////////////////////////////////////////////////////////////////
//
// This version uses Ken Shirriff's IRremote library to Rx/Tx the IR codes
// http://www.righto.com/2009/08/multi-protocol-infrared-remote-library.html
// 
// This code does NOT use PIN 13 on the Uno, as do previous versions
// Instead PIN 3, which is a PWM pin, is used. So you'll need to connect
// your LED to PIN 3 instead of PIN 13 for it to work.

// Install LCD per instructions at http://learn.adafruit.com/character-lcds/overview

#include <Wire.h>
#include <RTClib.h>
#include <Time.h>
#include <TimeAlarms.h>
#include <IRremote.h>
#include <LiquidCrystal.h>

RTC_DS1307 RTC;
IRsend irsend;
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);

/*********** BEGIN USER DEFINED VARIABLES ***********/
// DEBUG_IR adds the option to test the IR commands via the Arduino software's serial monitor
// You can send any value from 1 to 32 and it send the corresponding IR code
// The codes follow the remote controller, left to right, top to bottom
// e.g 1 = Orange, 2 = Blue, 21 = Moon1, etc
#define DEBUG_IR

int postDelay = 100;         // Delay after codes are sent
int randAnalogPin = 0;       // This needs to be set to an unused Analog pin, Used by ThunderStorm()

// Current Satellite+ IR Codes (NEC Protocol)
unsigned long codeHeader = 0x20DF; // Always the same

// Remote buttons listed left to right, top to bottom
unsigned int codeOrange = 0x3AC5;
unsigned int codeBlue = 0xBA45;
unsigned int codeRose = 0x827D;
unsigned int codePowerOnOff = 0x02FD;
unsigned int codeWhite = 0x1AE5;
unsigned int codeFullSpec = 0x9A65;
unsigned int codePurple = 0xA25D;
unsigned int codePlay = 0x22DD;
unsigned int codeRedUp = 0x2AD5;
unsigned int codeGreenUp = 0xAA55;
unsigned int codeBlueUp = 0x926D;
unsigned int codeWhiteUp = 0x12ED;
unsigned int codeRedDown = 0x0AF5;
unsigned int codeGreenDown = 0x8A75;
unsigned int codeBlueDown = 0xB24D;
unsigned int codeWhiteDown = 0x32CD;
unsigned int codeM1Custom = 0x38C7;
unsigned int codeM2Custom = 0xB847;
unsigned int codeM3Custom = 0x7887;
unsigned int codeM4Custom = 0xF807;
unsigned int codeMoon1 = 0x18E7;
unsigned int codeMoon2 = 0x9867;
unsigned int codeMoon3 = 0x58A7;
unsigned int codeDawnDusk = 0xD827;
unsigned int codeCloud1 = 0x28D7;
unsigned int codeCloud2 = 0xA857;
unsigned int codeCloud3 = 0x6897;
unsigned int codeCloud4 = 0xE817;
unsigned int codeStorm1 = 0x08F7;
unsigned int codeStorm2 = 0x8877;
unsigned int codeStorm3 = 0x48B7;
unsigned int codeStorm4 = 0xC837;

void SetAlarms()
{
  // Set up your desired alarms here
  // The default value of dtNBR_ALARMS is 6 in Alarms.h.
  // This code sets 12 alarms by default, so you'll need to change dtNBR_ALARMS to 12 or more
  Alarm.alarmRepeat(7,00,0, DawnDusk);
  Alarm.alarmRepeat(9,00,0, Cloud2);     // (HR,MIN,SEC,FUNCTION)
  Alarm.alarmRepeat(11,00,0, FullSpec);
  Alarm.alarmRepeat(15,00,0, Cloud2);
  Alarm.alarmRepeat(19,00,0, DawnDusk);
  Alarm.alarmRepeat(21,00,0, Moon2);
  
  // Comment these out if you don't want the chance of a random storm each day
  Alarm.alarmRepeat(12,00,00, ThunderStorm);
  ThunderStorm();  // Sets up intial storm so we don't have wait until alarm time
}
/************* END USER DEFINED VARIABLES *************/

void setup()
{
  Wire.begin();
  RTC.begin();
  lcd.begin(20, 4);
  Serial.begin(9600);
      //Serial.println(freeRam());
  
  if (! RTC.isrunning()) { 
    Serial.println("RTC Error");
    RTC.adjust(DateTime(__DATE__, __TIME__));}  //Adjust to compile time
    
  
  setSyncProvider(syncProvider);     //reference our syncProvider function instead of RTC_DS1307::get()
  
  Alarm.timerRepeat(900, digitalClockDisplay);  // Display the time every 15 minutes
  digitalClockDisplay();
  SetAlarms();
 Serial.print("SRAM : ");          //un-comment these line to check available SRAM
 Serial.println(freeRam());}   

void loop()
{
#ifdef DEBUG_IR 
  if (Serial.available() > 0) {
    delay(5); //Wait for transmission to finish
    CurrentCMDs(SerialReadInt());
  }
#endif
  Alarm.delay(100); 
  // Service alarms & wait (msec)
  lcdClockDisplay();
}

time_t syncProvider()
{
  //this does the same thing as RTC_DS1307::get()
  return RTC.now().unixtime();
}

void ThunderStorm ()
{ 
  // Schedules a storm between 1 & 9 in the evening
  // It sets Storm2, followed by Cloud2 or DawnDusk or Moon2, depending on when the storm is over
  randomSeed(analogRead(randAnalogPin));  // Generate random seed on unused pin
  byte RH = random(23);                   // Randomizer for thunderstorm
  byte RM = random(59);
  byte RS = random(59);
  byte TSDurationH = random(2);
  byte TSDurationM = random(59);
  
  if (RH <= 12)
    {
      Serial.println("No storm today");
      lcd.setCursor(0,1);
      lcd.print("No storm today");
      return;
    }
      
  if (RH > 12)                             // If random value is after 1 pm, allow storm
    {
      Alarm.alarmOnce(RH,RM,RS,Storm2);
      Serial.print("Next Storm: ");
      Serial.print(RH);
      printDigits(RM);
      printDigits(RS);
      Serial.print("   ");
      Serial.print("Duration = ");
      Serial.print(TSDurationH);
      printDigits(TSDurationM);
      Serial.println();
     lcd.setCursor(0,1);
     lcd.print("Next Storm: ");
     lcdHRdigits(RH);
     lcdDigits(RM);}
      
      if ((RH + TSDurationH) < 19)   // Return to Cloud2 if storm ends between 1-7pm
        {Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,Cloud2);}
      else if ((RH + TSDurationH) < 21)  // Return to DawnDusk if storm ends between 7-9pm
        {Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,DawnDusk);}
      else                                       // Return to Night2 if storm ends after 9pm
        {Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,Moon2);}
    }


void digitalClockDisplay()          // Digital clock
{ 
  Serial.print("Time = ");
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.println(); }

  
void lcdClockDisplay()  
  {lcd.setCursor(0,0);
    lcdHRdigits(hour());
  lcdDigits(minute());}

void printDigits(int digits)        // Add :
{
  Serial.print(":");
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits);
}

void lcdDigits(int digits)        // Add :
{
  lcd.print(":");
  if(digits < 10)
    lcd.print('0');  
  lcd.print(digits);
}
void lcdHRdigits(int HRdigits)        // Preface hour with 0
{
  if(HRdigits < 10)
    lcd.print('0');  
  lcd.print(HRdigits);
}

#ifdef DEBUG_IR
int SerialReadInt()
{
  int i, numAva;
  char inBytes[3];                  // Array to hold the bytes
  char * inBytesPtr = &inBytes[0];  // Pointer to the first element of the array
      
    numAva = Serial.available();    // Read number of input bytes
    if (numAva > 2)
      numAva = 2;                   // Only allow 2 characters to prevent overflow
      
    for (i=0; i<numAva; i++)        // Load input bytes into array
      inBytes[i] = Serial.read();
    inBytes[i] =  '\0';             // Put NULL character at the end
    return atoi(inBytesPtr);        // Call atoi function and return result
}

void CurrentCMDs (int cmd)
{switch (cmd)
  {
    case 1:
      Orange();
      break;
    case 2:
      Blue();
      break;
    case 3:
      Rose();
      break;
    case 4:
      PowerOnOff();
      break;
    case 5:
      White();
      break;
    case 6:
      FullSpec();
      break;
    case 7:
      Purple();
      break;
    case 8:
      Play();
      break;
    case 9:
      RedUp();
      break;
    case 10:
      GreenUp();
      break;
    case 11:
      BlueUp();
      break;
    case 12:
      WhiteUp();
      break;
    case 13:
      RedDown();
      break;
    case 14:
      GreenDown();
      break;
    case 15:
      BlueDown();
      break;
    case 16:
      WhiteDown();
      break;
    case 17:
      M1Custom();
      break;
    case 18:
      M2Custom();
      break;
    case 19:
      M3Custom();
      break;
    case 20:
      M4Custom();
      break;
    case 21:
      Moon1();
      break;
    case 22:
      Moon2();
      break;
    case 23:
      Moon3();
      break;
    case 24:
      DawnDusk();
      break;
    case 25:
      Cloud1();
      break;
    case 26:
      Cloud2();
      break;
    case 27:
      Cloud3();
      break;
    case 28:
      Cloud4();
      break;
    case 29:
      Storm1();
      break;
    case 30:
      Storm2();
      break;
    case 31:
      Storm3();
      break;
    case 32:
      Storm4();
      break;
    default:
      Serial.println("Invalid Choice");}}
#endif

void SendCode (unsigned int code, byte numTimes, const char *sMessage)
{unsigned long irCode = (codeHeader << 16) + code; // Header is 2 bytes, shift all the way to left & add code to it
  
  for( int i = 0; i < numTimes; i++)
  {irsend.sendNEC(irCode,32); // Send code
    Alarm.delay(postDelay);}
    
  Serial.println(sMessage);  // Print message
  lcd.setCursor(6,0);
  lcd.print(sMessage);
  for(byte i = sizeof(sMessage); i <= 14; i++)
    lcd.print(" ");
  digitalClockDisplay();}

void Orange()
{SendCode(codeOrange, 2, "Orange");}

void Blue()
{SendCode(codeBlue, 2, "Blue");}

void Rose()
{SendCode(codeRose, 2, "Rose");}

void PowerOnOff()
{SendCode(codePowerOnOff, 1, "Power On/Off");}

void White()
{SendCode(codeWhite, 2, "White");}

void FullSpec()
{SendCode(codeFullSpec, 2, "Full Spectrum");}

void Purple()
{SendCode(codePurple, 2, "Purple");}

void Play()
{SendCode(codePlay, 1, "Play/Pause:");}

void RedUp()
{SendCode(codeRedUp, 1, "Red Up");}

void GreenUp()
{SendCode(codeGreenUp, 1, "Green Up");}

void BlueUp()
{SendCode(codeBlueUp, 1, "Blue");}

void WhiteUp()
{SendCode(codeWhiteUp, 1, "White Up");}

void RedDown()
{SendCode(codeRedDown, 1, "Red Down");}

void GreenDown()
{SendCode(codeGreenDown, 1, "Green Down");}

void BlueDown()
{SendCode(codeBlueDown, 1, "Blue Down");}

void WhiteDown()
{SendCode(codeWhite, 1, "White Down");}

void M1Custom()
{SendCode(codeM1Custom, 2, "Custom Mix 1");}

void M2Custom()
{SendCode(codeM2Custom, 2, "Custom Mix 2");}

void M3Custom()
{SendCode(codeM3Custom, 2, "Custom Mix 3");}

void M4Custom()
{SendCode(codeM4Custom, 2, "Custom Mix 4");}

void Moon1()
{SendCode(codeMoon1, 2, "Moonlight 1");}

void Moon2()
{SendCode(codeMoon2, 2, "Moonlight 2");}

void Moon3()
{SendCode(codeMoon3, 2, "Moonlight 3");}

void DawnDusk()
{SendCode(codeDawnDusk, 2, "Dawn/Dusk");}

void Cloud1()
{SendCode(codeCloud1, 2, "Cloud Cover 1");}

void Cloud2()
{SendCode(codeCloud2, 2, "Cloud Cover 2");}
  
void Cloud3()
{SendCode(codeCloud3, 2, "Cloud Cover 3");}

void Cloud4()
{SendCode(codeCloud4, 2, "Cloud Cover 4");}

void Storm1()
{SendCode(codeStorm1, 2, "Thunderstorm 1");}

void Storm2()
{SendCode(codeStorm2, 2, "Thunderstorm 2");}

void Storm3()
{SendCode(codeStorm3, 2, "Thunderstorm 3");}

void Storm4()
{SendCode(codeStorm4, 2, "Thunderstorm 4");}

int freeRam () {
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}


----------



## Dahammer

Indychus said:


> Sweet, at 535 SRAM remaining with that change. I'm gonna hold off on moving the strings to flash unless it's absolutely necessary.


Yeah, I don't think it will have any problems with that much SRAM available. That number is going to fluctuate some as the program runs, since the stack will grown/shrink with local variables and etc, but it should be fine.

There are a lot things that can be changed to free up space, if need be. I started putting the IR codes into an array earlier (probably should have done that originally) and then stopped because I didn't want to wade through it all for 64 bytes of SRAM. I didn't do it originally, because I figured it would be easier for those with 0 coding experience to understand/follow/modify if each code had it's own name.

Anyway, as long as it works as desired, it doesn't make much difference.


----------



## Indychus

Considering that I had 8 bytes available at one point, 535 is a huge improvement haha.

Sent from my HTC One X


----------



## mistergreen

hmm, I wonder what's taking up so much RAM.
I was building a huge WIFI web controller on an UNO and it took 1.3K out of 2K of RAM. The app you're building is relative small, maybe it's the large libraries? I write most of my own libraries so I can streamline a lot of things.

I'm thinking of upgrading to a mega though. I haven't finished and it'll only get bigger.


----------



## Indychus

I think it's due to the number of variables we have for the IR codes and the text strings for debugging and now for the LCD display. We're only at around 15 kb on flash, so we have plenty of room to move all of the text strings over to flash if needed. 

Right now, at around 75% capacity on the SRAM, everything seems to run smoothly. It was running ok with only around 80 bytes available, but that was too close for comfort. I think a buffer of several hundred bytes is probably acceptable.


----------



## mistergreen

Indychus said:


> I think it's due to the number of variables we have for the IR codes and the text strings for debugging and now for the LCD display. We're only at around 15 kb on flash, so we have plenty of room to move all of the text strings over to flash if needed.
> 
> Right now, at around 75% capacity on the SRAM, everything seems to run smoothly. It was running ok with only around 80 bytes available, but that was too close for comfort. I think a buffer of several hundred bytes is probably acceptable.


Oh, I know what it is. The more Alarms you have, the more memory it takes up. Every alarm is an instance of a particular Alarm object which takes up a good chunk of memory. I wrote a library based on the Alarm library so I'll be facing that issue too.
Your IR variables shouldn't take up much, maybe 64 Bytes or so.


----------



## Indychus

mistergreen said:


> Oh, I know what it is. The more Alarms you have, the more memory it takes up. Every alarm is an instance of a particular Alarm object which takes up a good chunk of memory. I wrote a library based on the Alarm library so I'll be facing that issue too.
> Your IR variables shouldn't take up much, maybe 64 Bytes or so.


According to the documentation on the TimeAlarms library, each alarm should only take up 11b, and it's reserved if it's used or not... So with 24 alarms specified in the .h file, it should only be using 264b. That's still a pretty hefty chunk of the total, but I'm not sure as to what's using the other ~1200b. I guess all of the text strings collectively take up a large portion as well.


----------



## Dahammer

I've moved the IR codes to flash and made a few other optimizations, I will post it once I'm done tinkering with it.


----------



## Highlander

May I ask you folks a question? What made you pick to use this style controller for a per made led over a DIY led? The reason I ask is because I am between build my led with a Typhon and a current + with this setup. Don't mean to hijack, just didn't know how to get a hold of your attention. Thanks


----------



## jeffkrol

acamp said:


> May I ask you folks a question? What made you pick to use this style controller for a per made led over a DIY led? The reason I ask is because I am between build my led with a Typhon and a current + with this setup. Don't mean to hijack, just didn't know how to get a hold of your attention. Thanks


Well lets take this tangent a bit farther 1)do you mean buildmyled as in a company? not sure if the Typhon will work well since I believe the buildmyled is 10V analog dimming and 2)Unless you know others the Typhon is a simple 4 channel "fade in fade out" controller (as programmed normally) w/ PWM output (trust me don't try using it analog 10V dimmer drivers) not the bells and whistles Current sat + w/ clouds ect..
3)They are modding the commands to controller basically but not the controller itself (and its built in logic). I (which doesn't mean all that much) don't know any cheap controllers w/ all the bells and whistles of the Sat +

Which is this in fish form.............


----------



## Caver

As for me...I bought the light before I knew about the controller. I looked into DIY LED fixtures, but I couldn't find one that I felt like doing all the mechanical fixture building. Plus I personally like the slim trim physical appearance of the LED+. The LED+ also has the dynamic cloud modes built in.

From an electronics DIY point of view, I personally find it simpler to build up this Arduino IR remote controller than the control circuitry for the other products available. I'm pretty comfortable working with the electronics hardware, but I'm not so good at software, so I'm riding the coat-tails of the programming guys...:biggrin:

Now if I wanted a high light - hi tech tank with ferts & CO2, I might have gone in a different direction. But for a low - medium light, low tech tank, I like this a lot!

All my personal opinions. I'm sure others will chime in.


----------



## Indychus

I bought the light because it's sleek, packed with features, and puts me right between low and medium light on my tank, which is what I'm aiming for. Once I got it I thought it was kind of silly to have all of the dynamic weather effects and nearly infinitely adjustable colors with no automation. Plugged into a regular on/off timer it was kinda lame. I had a spare Arduino lying around and realized how easy it would be to control the fixture if I could figure out the IR codes. So here we are. 

The light is about as cool as you can get in this price range, and our controller is endlessly more customizable than what current plans to offer (when it finally hits the market). I have yet to find another solution as cost effective and loaded with features. Once it's all said and done, I will have around $175 in the setup, including the 48" light fixture. I don't think you can get complete automation, dynamic weather, and full spectrum RGB LEDs in any other setup for even twice that price.

Sent from my HTC One X


----------



## jeffkrol

Indychus said:


> I don't think you can get complete automation, dynamic weather, and full spectrum RGB LEDs in any other setup for even twice that price.


Yep, pretty good move on Current's part.. AND yours..


----------



## Highlander

Jeffkrol, I did mean the company buildmyled but it could be any source for led's. thanks again for all the input from all of you. I still have a lot to research and the decision will be tough for me. Thanks again


----------



## Highlander

Well I bought the Current satellite + for my 56g. I will be upgrading my tank size next summer to a 90 and will go med-high light with DIY lights and controller. Maybe my kids tabk can use the light when i upgrade. I am subscribing to this thread now so I can practice with putting a broad together and learn how to use your codes. Thank for doing the grunt work on this project. I wish I knew how to even start writing codes. Thanks again for the help.


----------



## gus6464

Don't know why current doesn't release a high power version of this light. Even at twice the price it would still be a good deal for what you get.


----------



## Indychus

They are supposedly working on a high-par planted tank version. It's been delayed.a few times though. My guess is they have trouble with heat in such a slim fixture with no fans and no room for passive cooling.

Sent from my HTC One X


----------



## Indychus

acamp said:


> Well I bought the Current satellite + for my 56g. I will be upgrading my tank size next summer to a 90 and will go med-high light with DIY lights and controller. Maybe my kids tabk can use the light when i upgrade. I am subscribing to this thread now so I can practice with putting a broad together and learn how to use your codes. Thank for doing the grunt work on this project. I wish I knew how to even start writing codes. Thanks again for the help.


Welcome aboard! Great to have another potential Arduino fan. I'm not the best at coding, but thankfully dahammer and mistergreen are. If you can unzip a few files and copy/paste you should be good to go. Very few tweaks are needed to get it working how you want, and its easy to pick up the basics of the code just by playing with it. Putting the hardware together is as easy or difficult as you want, depending on what components you buy and features you want to include.

Sent from my HTC One X


----------



## gus6464

If you are looking for a cheap Uno I found a seller on ebay which sells good quality stuff at good prices. I ordered a Mega from them for another project.

Look up ebay item 121149850931 for an Uno for $10.

$30 ebay 271178601107 gets you Uno, LCD, and proto board so you can attach the IR and RTC for a much cleaner look.


----------



## Caver

Absolutely!

I'm a little surprised that there isn't more hoopla on the net about the Current USA Satellite LED+! Maybe it's because it's in the low-medium light class for plants.

When I did my research to buy an LED light a couple of months ago, I couldn't find anything at it's price point with all the features.

At first I was a little dismayed that the only way to control it was with the remote, but now that is turning out to be an advantage. I really like the lights build quality. It feels very solid and looks great on the tank.

It will be interesting to see if Current USA will keep up their creativity and quality in the next products.


----------



## mistergreen

gus6464 said:


> If you are looking for a cheap Uno I found a seller on ebay which sells good quality stuff at good prices. I ordered a Mega from them for another project.
> 
> Look up ebay item 121149850931 for an Uno for $10.


These are arduino clones. They should work but sometimes there are little bugs here and there. Just watch out for it.


----------



## gus6464

mistergreen said:


> These are arduino clones. They should work but sometimes there are little bugs here and there. Just watch out for it.


Is the real thing really worth 3x the price?


----------



## mistergreen

gus6464 said:


> Is the real thing really worth 3x the price?


Dunno. The clones will work but if you're doing complicated things with it bugs might show up. It should work fine for this project. Make sure the arduino firmware rev. 0001 is on it for the codes to work. Sometimes the clones have the old firmware on it.


----------



## Highlander

I will probably order straight from arduino. I like to make sure I get quality the first time I build something (I build firearms) and after I do the first then I usually downgrade on quality. Probably backwards from what most do, but oh well. Thanks again guys, imam sure I will have many more questions in the near future.


----------



## Indychus

Nothing wrong with some of the clones if they are up front about it... Some of them are just as nice as the Arduino... The ones to look out for are the illegal clones labeled as Arduino even though they aren't. The hardware is open source for anyone to use and copy, but the Arduino name is protected. There are dozens of legitimate clones sold under various names. I prefer the genuine board and feel that it's worth the extra cost if only to support the development of future Arduino products. If you choose to go with a clone just google it and see how it works out for others.

Sent from my HTC One X


----------



## Indychus

*Version 3.7*

Another minor update... this moves all text strings to flash to free up some SRAM. Now at around 949 bytes free (~50% capacity for Uno).

Also reduced storm frequency. Prior to this update, there was a 50% chance of a storm on any given day. This reduces that chance to 20% and should yield about one (~1.4) storm per week.



Code:


///////////////////////////////////////////////////////////////////
// Current Satellite LED+ Controller  V3.7                       //
//   Indychus...Dahammer...mistergreen @ plantedtank.net         //
//   This code is public domain.  Pass it on.                    //
//   Confirmed on Arduino UNO 1.0.5                              //
//   Req. Time, TimeAlarms, RTClib, IRremote                     //
///////////////////////////////////////////////////////////////////
//
// This version uses Ken Shirriff's IRremote library to Rx/Tx the IR codes
// http://www.righto.com/2009/08/multi-protocol-infrared-remote-library.html
// 
// This code does NOT use PIN 13 on the Uno, as do previous versions
// Instead PIN 3, which is a PWM pin, is used. So you'll need to connect
// your LED to PIN 3 instead of PIN 13 for it to work.

// Install LCD per instructions at http://learn.adafruit.com/character-lcds/overview

#include <Wire.h>
#include <RTClib.h>
#include <Time.h>
#include <TimeAlarms.h>
#include <IRremote.h>
#include <LiquidCrystal.h>

RTC_DS1307 RTC;
IRsend irsend;
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);

/*********** BEGIN USER DEFINED VARIABLES ***********/
// DEBUG_IR adds the option to test the IR commands via the Arduino software's serial monitor
// You can send any value from 1 to 32 and it send the corresponding IR code
// The codes follow the remote controller, left to right, top to bottom
// e.g 1 = Orange, 2 = Blue, 21 = Moon1, etc
#define DEBUG_IR

int postDelay = 100;         // Delay after codes are sent
int randAnalogPin = 0;       // This needs to be set to an unused Analog pin, Used by ThunderStorm()

// Current Satellite+ IR Codes (NEC Protocol)
unsigned long codeHeader = 0x20DF; // Always the same

// Remote buttons listed left to right, top to bottom
unsigned int codeOrange = 0x3AC5;
unsigned int codeBlue = 0xBA45;
unsigned int codeRose = 0x827D;
unsigned int codePowerOnOff = 0x02FD;
unsigned int codeWhite = 0x1AE5;
unsigned int codeFullSpec = 0x9A65;
unsigned int codePurple = 0xA25D;
unsigned int codePlay = 0x22DD;
unsigned int codeRedUp = 0x2AD5;
unsigned int codeGreenUp = 0xAA55;
unsigned int codeBlueUp = 0x926D;
unsigned int codeWhiteUp = 0x12ED;
unsigned int codeRedDown = 0x0AF5;
unsigned int codeGreenDown = 0x8A75;
unsigned int codeBlueDown = 0xB24D;
unsigned int codeWhiteDown = 0x32CD;
unsigned int codeM1Custom = 0x38C7;
unsigned int codeM2Custom = 0xB847;
unsigned int codeM3Custom = 0x7887;
unsigned int codeM4Custom = 0xF807;
unsigned int codeMoon1 = 0x18E7;
unsigned int codeMoon2 = 0x9867;
unsigned int codeMoon3 = 0x58A7;
unsigned int codeDawnDusk = 0xD827;
unsigned int codeCloud1 = 0x28D7;
unsigned int codeCloud2 = 0xA857;
unsigned int codeCloud3 = 0x6897;
unsigned int codeCloud4 = 0xE817;
unsigned int codeStorm1 = 0x08F7;
unsigned int codeStorm2 = 0x8877;
unsigned int codeStorm3 = 0x48B7;
unsigned int codeStorm4 = 0xC837;

void SetAlarms()
{
  // Set up your desired alarms here
  // The default value of dtNBR_ALARMS is 6 in Alarms.h.
  // This code sets 12 alarms by default, so you'll need to change dtNBR_ALARMS to 12 or more
  Alarm.alarmRepeat(7,00,0, DawnDusk);
  Alarm.alarmRepeat(9,00,0, Cloud2);     // (HR,MIN,SEC,FUNCTION)
  Alarm.alarmRepeat(11,00,0, FullSpec);
  Alarm.alarmRepeat(15,00,0, Cloud2);
  Alarm.alarmRepeat(19,00,0, DawnDusk);
  Alarm.alarmRepeat(21,00,0, Moon2);
  
  // Comment these out if you don't want the chance of a random storm each day
  Alarm.alarmRepeat(12,00,00, ThunderStorm);
  ThunderStorm();  // Sets up intial storm so we don't have wait until alarm time
}
/************* END USER DEFINED VARIABLES *************/

void setup()
{
  Wire.begin();
  RTC.begin();
  lcd.begin(20, 4);
  Serial.begin(9600);
      //Serial.println(freeRam());
  
  if (! RTC.isrunning()) { 
    Serial.println(F("RTC Error"));
    RTC.adjust(DateTime(__DATE__, __TIME__));}  //Adjust to compile time
    
  
  setSyncProvider(syncProvider);     //reference our syncProvider function instead of RTC_DS1307::get()
  
  Alarm.timerRepeat(900, digitalClockDisplay);  // Display the time every 15 minutes
  digitalClockDisplay();
  SetAlarms();
 Serial.print(F("SRAM : "));          //un-comment these line to check available SRAM
 Serial.println(freeRam());}   

void loop()
{
#ifdef DEBUG_IR 
  if (Serial.available() > 0) {
    delay(5); //Wait for transmission to finish
    CurrentCMDs(SerialReadInt());
  }
#endif
  Alarm.delay(100); 
  // Service alarms & wait (msec)
  lcdClockDisplay();
}

time_t syncProvider()
{
  //this does the same thing as RTC_DS1307::get()
  return RTC.now().unixtime();
}

void ThunderStorm ()
{ 
  // Schedules a storm between 1 & 9 in the evening
  // It sets Storm2, followed by Cloud2 or DawnDusk or Moon2, depending on when the storm is over
  randomSeed(analogRead(randAnalogPin));  // Generate random seed on unused pin
  byte RH = random(60);                   // Randomizer for thunderstorm
  byte RM = random(59);
  byte RS = random(59);
  byte TSDurationH = random(2);
  byte TSDurationM = random(59);
  
  if (RH < 13 || RH > 23)
    {
      Serial.println(F("No storm today"));
      lcd.setCursor(0,1);
      lcd.print(F("No storm today"));
      return;
    }
    
      
  if (RH > 12 && RH < 24)                             // If random value is after 1 pm, allow storm
    {
      Alarm.alarmOnce(RH,RM,RS,Storm2);
      
      lcd.setCursor(0,1);
     lcd.print(F("Next Storm:  "));
     lcdHRdigits(RH);
     lcdDigits(RM);
   lcd.setCursor(0,2);
     lcd.print(F("Duration:     "));
     lcd.print(TSDurationH);
     lcdDigits(TSDurationM);
      Serial.print(F("Next Storm: "));
      Serial.print(RH);
      printDigits(RM);
      printDigits(RS);
      Serial.println();
      Serial.print(F("Duration: "));
      Serial.print(TSDurationH);
      printDigits(TSDurationM);
      Serial.println();}
     
      
      if ((RH + TSDurationH) < 19)   // Return to Cloud2 if storm ends between 1-7pm
        {Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,Cloud2);}
      else if ((RH + TSDurationH) < 21)  // Return to DawnDusk if storm ends between 7-9pm
        {Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,DawnDusk);}
      else                                       // Return to Night2 if storm ends after 9pm
        {Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,Moon2);}
    }

void digitalClockDisplay()          // Digital clock
{ 
  Serial.print(F("Time = "));
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.println(); }

  
void lcdClockDisplay()  
  {lcd.setCursor(0,0);
    lcdHRdigits(hour());
  lcdDigits(minute());}

void printDigits(int digits)        // Add :
{
  Serial.print(":");
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits);
}

void lcdDigits(int digits)        // Add :
{
  lcd.print(":");
  if(digits < 10)
    lcd.print('0');  
  lcd.print(digits);
}
void lcdHRdigits(int HRdigits)        // Preface hour with 0
{
  if(HRdigits < 10)
    lcd.print('0');  
  lcd.print(HRdigits);
}

#ifdef DEBUG_IR
int SerialReadInt()
{
  int i, numAva;
  char inBytes[3];                  // Array to hold the bytes
  char * inBytesPtr = &inBytes[0];  // Pointer to the first element of the array
      
    numAva = Serial.available();    // Read number of input bytes
    if (numAva > 2)
      numAva = 2;                   // Only allow 2 characters to prevent overflow
      
    for (i=0; i<numAva; i++)        // Load input bytes into array
      inBytes[i] = Serial.read();
    inBytes[i] =  '\0';             // Put NULL character at the end
    return atoi(inBytesPtr);        // Call atoi function and return result
}

void CurrentCMDs (int cmd)
{switch (cmd)
  {
    case 1:
      Orange();
      break;
    case 2:
      Blue();
      break;
    case 3:
      Rose();
      break;
    case 4:
      PowerOnOff();
      break;
    case 5:
      White();
      break;
    case 6:
      FullSpec();
      break;
    case 7:
      Purple();
      break;
    case 8:
      Play();
      break;
    case 9:
      RedUp();
      break;
    case 10:
      GreenUp();
      break;
    case 11:
      BlueUp();
      break;
    case 12:
      WhiteUp();
      break;
    case 13:
      RedDown();
      break;
    case 14:
      GreenDown();
      break;
    case 15:
      BlueDown();
      break;
    case 16:
      WhiteDown();
      break;
    case 17:
      M1Custom();
      break;
    case 18:
      M2Custom();
      break;
    case 19:
      M3Custom();
      break;
    case 20:
      M4Custom();
      break;
    case 21:
      Moon1();
      break;
    case 22:
      Moon2();
      break;
    case 23:
      Moon3();
      break;
    case 24:
      DawnDusk();
      break;
    case 25:
      Cloud1();
      break;
    case 26:
      Cloud2();
      break;
    case 27:
      Cloud3();
      break;
    case 28:
      Cloud4();
      break;
    case 29:
      Storm1();
      break;
    case 30:
      Storm2();
      break;
    case 31:
      Storm3();
      break;
    case 32:
      Storm4();
      break;
    default:
      Serial.println("Invalid Choice");}}
#endif

void SendCode (unsigned int code, byte numTimes, const __FlashStringHelper* sMessage)
{unsigned long irCode = (codeHeader << 16) + code; // Header is 2 bytes, shift all the way to left & add code to it
  
  for( int i = 0; i < numTimes; i++)
  {irsend.sendNEC(irCode,32); // Send code
    Alarm.delay(postDelay);}
    
  Serial.println(sMessage);  // Print message
  lcd.setCursor(6,0);
  lcd.print(sMessage);
  digitalClockDisplay();}

void Orange()
{SendCode(codeOrange, 2, F("Orange        "));}

void Blue()
{SendCode(codeBlue, 2, F("Blue          "));}

void Rose()
{SendCode(codeRose, 2, F("Rose          "));}

void PowerOnOff()
{SendCode(codePowerOnOff, 1, F("Power On/Off  "));}

void White()
{SendCode(codeWhite, 2, F("White         "));}

void FullSpec()
{SendCode(codeFullSpec, 2, F("Full Spectrum "));}

void Purple()
{SendCode(codePurple, 2, F("Purple        "));}

void Play()
{SendCode(codePlay, 1, F("Play/Pause:   "));}

void RedUp()
{SendCode(codeRedUp, 1, F("Red Up        "));}

void GreenUp()
{SendCode(codeGreenUp, 1, F("Green Up      "));}

void BlueUp()
{SendCode(codeBlueUp, 1, F("Blue          "));}

void WhiteUp()
{SendCode(codeWhiteUp, 1, F("White Up      "));}

void RedDown()
{SendCode(codeRedDown, 1, F("Red Down      "));}

void GreenDown()
{SendCode(codeGreenDown, 1, F("Green Down    "));}

void BlueDown()
{SendCode(codeBlueDown, 1, F("Blue Down     "));}

void WhiteDown()
{SendCode(codeWhite, 1, F("White Down    "));}

void M1Custom()
{SendCode(codeM1Custom, 2, F("Custom Mix 1  "));}

void M2Custom()
{SendCode(codeM2Custom, 2, F("Custom Mix 2  "));}

void M3Custom()
{SendCode(codeM3Custom, 2, F("Custom Mix 3  "));}

void M4Custom()
{SendCode(codeM4Custom, 2, F("Custom Mix 4  "));}

void Moon1()
{SendCode(codeMoon1, 2, F("Moonlight 1   "));}

void Moon2()
{SendCode(codeMoon2, 2, F("Moonlight 2   "));}

void Moon3()
{SendCode(codeMoon3, 2, F("Moonlight 3   "));}

void DawnDusk()
{SendCode(codeDawnDusk, 2, F("Dawn/Dusk     "));}

void Cloud1()
{SendCode(codeCloud1, 2, F("Cloud Cover 1 "));}

void Cloud2()
{SendCode(codeCloud2, 2, F("Cloud Cover 2 "));}
  
void Cloud3()
{SendCode(codeCloud3, 2, F("Cloud Cover 3 "));}

void Cloud4()
{SendCode(codeCloud4, 2, F("Cloud Cover 4 "));}

void Storm1()
{SendCode(codeStorm1, 2, F("Thunderstorm 1"));}

void Storm2()
{SendCode(codeStorm2, 2, F("Thunderstorm 2"));}

void Storm3()
{SendCode(codeStorm3, 2, F("Thunderstorm 3"));}

void Storm4()
{SendCode(codeStorm4, 2, F("Thunderstorm 4"));}

int freeRam () {
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}


----------



## Dahammer

Code:


  for(byte i = sizeof(sMessage); i <= 14; i++)
    lcd.print(" ");

Does this work as intended?

Looking at it now, I can't see how it could, even though I posted it earlier. sizeof() is a compile time function, so it should get the size of sMessage at compile time and sMessage is a pointer to an address so it should always be equal to 2 on the Arduino. I don't have an LCD, but I assume you're adding the trailing spaces to make it look better on the LCD?

At any rate, I've rewritten some of the code, moved the IR codes to PROGMEM, as well a the string. But haven't had a chance to test it much, so haven't posted it. I ended up just writing a function to fetch the strings from PROGMEM, versus relying solely on the F() function. I also ended up using sprintf() because I tired of all of the Serial.print() calls but it adds some overhead so I haven't decided whether to leave it or not.


----------



## Indychus

Yeah, it's working perfectly haha. The spaces are to overwrite previous messages.

Sent from my HTC One X


----------



## Indychus

Actually, no... it's wrapping extra characters (spaces) to the third line and I didn't realize it until just now when I tried to display something on that line. Due to the way the LCD logic is set up, extra text from line 1 wraps to line 3 so line 2 was still displaying fine. Just gonna put the spaces back in for now... it doesn't affect the SRAM anymore so a few extra bytes on flash for the spaces is not a big deal.


----------



## Indychus

Updated V3.7 above.


----------



## mistergreen

Dahammer is right about the sizeof(smessage) is always the same. It's always tricky using sizeof in c.

An easy solution is 


Code:


//clear out all characters from line 6
lcd.setCursor(6,0);
for(byte i = 0; i <= 14; i++)
    lcd.print(" ");
//print message
lcd.setCursor(6,0);
lcd.print(sMessage);
digitalClockDisplay();


----------



## Dahammer

mistergreen said:


> Dahammer is right about the sizeof(smessage) is always the same. It's always tricky using sizeof in c.
> 
> An easy solution is
> 
> 
> Code:
> 
> 
> //clear out all characters from line 6
> lcd.setCursor(6,0);
> for(byte i = 0; i <= 14; i++)
> lcd.print(" ");
> //print message
> lcd.setCursor(6,0);
> lcd.print(sMessage);
> digitalClockDisplay();


If I understand correctly, he was wanting to add trailing spaces onto the strings to make the entire string 14 characters long, not add 14 spaces onto each string.

At any rate, having them in PROGMEM complicates it. You'd have to count each character as you read the string out of PROGMEM, to get a count, then add the spaces.


----------



## Dahammer

Here is the code that I've been tinkering with. I won't be home until tomorrow night, so I can't test it with my fixture until then (although I do have my controller with me and it runs fine on it). And I don't have an LCD to test that end of it, so could some of you try it out and see how it does?

I bumped the version up to a major release, since there are so many changes in the way the code works. Here are some of the highlights:


Redirected STDOUT to the UART so that the fprint family of functions could be used instead of Serial.print()
Moved all strings and the IR codes to PROGMEM
Optimized some of routines & removed others
 
The main thing is the switch to fprint. I grew tired of the lack of formatting capabilities of Serial.print(). fprint adds some overhead, but it is much much more powerful and by the time I was done the code is only around 200 bytes larger than previous versions. It's also a lot cleaner looking without all of the Serial.print calls.

*Version 4.0*


Code:


///////////////////////////////////////////////////////////////////
// Current Satellite LED+ Controller  V4.0                       //
//   Indychus...Dahammer...mistergreen @ plantedtank.net         //
//   This code is public domain.  Pass it on.                    //
//   Confirmed on Arduino UNO 1.0.5                              //
//   Req. Time, TimeAlarms, RTClib, IRremote                     //
///////////////////////////////////////////////////////////////////
//
// This version uses Ken Shirriff's IRremote library to Rx/Tx the IR codes
// http://www.righto.com/2009/08/multi-protocol-infrared-remote-library.html
// 
// This code does NOT use PIN 13 on the Uno, as do previous versions
// Instead PIN 3, which is a PWM pin, is used. So you'll need to connect
// your LED to PIN 3 instead of PIN 13 for it to work.
//
// You can test the IR commands via the Arduino software's serial monitor
// by sending in a value from 1 - 32. Values follow the remote control, 
// left to right, top to bottom (e.g 1 = Orange, 2 = Blue, 21 = Moon1, etc)
//
// Install LCD per instructions at http://learn.adafruit.com/character-lcds/overview
//
#include <Wire.h>
#include <RTClib.h>
#include <Time.h>
#include <TimeAlarms.h>
#include <IRremote.h>
#include <LiquidCrystal.h>

RTC_DS1307 RTC;
IRsend irsend;

byte randAnalogPin = 0;   // This needs to be set to an unused Analog pin, Used by RandomStorm()
#define LCD_COLS 20      // Number of columns on the LCD (e.g. 16, 20, etc)
#define LCD_ROWS 4       // Number of rows on the LCD (e.g. 2, 4, etc)
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);

// Current Satellite+ IR Codes (NEC Protocol)
unsigned long codeHeader = 0x20DF; // Always the same
// Remote buttons listed left to right, top to bottom
PROGMEM unsigned int arrCodes[32] = {0x3AC5,  // 1 -  Orange
                                     0xBA45,  // 2 -  Blue
                                     0x827D,  // 3 -  Rose
                                     0x02FD,  // 4 -  Power On/Off
                                     0x1AE5,  // 5 -  White
                                     0x9A65,  // 6 -  FullSpec
                                     0xA25D,  // 7 -  Purple
                                     0x22DD,  // 8 -  Play/Pause
                                     0x2AD5,  // 9 -  Red Up
                                     0xAA55,  // 10 - Green Up
                                     0x926D,  // 11 - Blue Up
                                     0x12ED,  // 12 - White Up
                                     0x0AF5,  // 13 - Red Down
                                     0x8A75,  // 14 - Green Down
                                     0xB24D,  // 15 - Blue Down
                                     0x32CD,  // 16 - White Down
                                     0x38C7,  // 17 - M1 Custom
                                     0xB847,  // 18 - M2 Custom
                                     0x7887,  // 19 - M3 Custom
                                     0xF807,  // 20 - M4 Custom
                                     0x18E7,  // 21 - Moon 1
                                     0x9867,  // 22 - Moon 2
                                     0x58A7,  // 23 - Moon 3
                                     0xD827,  // 24 - Dawn/Dusk
                                     0x28D7,  // 25 - Cloud 1
                                     0xA857,  // 26 - Cloud 2
                                     0x6897,  // 27 - Cloud 3
                                     0xE817,  // 28 - Cloud 4
                                     0x08F7,  // 29 - Storm 1
                                     0x8877,  // 30 - Storm 2
                                     0x48B7,  // 31 - Storm 3
                                     0xC837}; // 32 - Storm 4

// These are the messages that print on the serial monitor & lcd when each IR code is sent
#define MAX_MSG_LEN 13  // Maximum length of the arrMSG messages
prog_char PROGMEM arrMSG[][MAX_MSG_LEN+1] = {"Orange",        "Blue",          "Rose",          "Power On/Off",
                                             "White",         "Full Spectrum", "Purple",        "Play/Pause",
                                             "Red Up",        "Green Up",      "Blue Up",       "White Up",
                                             "Red Down",      "Green Down",    "Blue Down",     "White Down",
                                             "Custom Mix 1",  "Custom Mix 2",  "Custom Mix 3",  "Custom Mix 4",
                                             "Moonlight 1",   "Moonlight 2",   "Moonlight 3",   "Dawn/Dusk",
                                             "Cloud Cover 1", "Cloud Cover 2", "Cloud Cover 3", "Cloud Cover 4",
                                             "Storm 1",       "Storm 2",       "Storm 3",       "Storm 4"};

void SetAlarms()
{
  // Set up your desired alarms here
  // The default value of dtNBR_ALARMS is 6 in TimeAlarms.h.
  // This code sets 9 alarms by default, so you'll need to change dtNBR_ALARMS to 9 or more
  // Changes the times to suit yourself. Add as many alarms as you like, just stay within dtNBR_ALARMS
  Alarm.alarmRepeat(7,00,0, DawnDusk);
  Alarm.alarmRepeat(9,00,0, Cloud2);     // (HR,MIN,SEC,FUNCTION)
  Alarm.alarmRepeat(10,00,0, FullSpec);
  Alarm.alarmRepeat(16,00,0, Cloud2);
  Alarm.alarmRepeat(19,00,0, DawnDusk);
  Alarm.alarmRepeat(21,00,0, Moon2);
  
  // Comment these out if you don't want the chance of a random storm each day
  Alarm.alarmRepeat(12,00,00, RandomStorm);
  RandomStorm();  // Sets up intial storm so we don't have wait until alarm time
}

// Setup file structure & output function for stdout redirect
static FILE uartout = {0}; // create a FILE structure to reference our UART output function
static int uart_putchar (char c, FILE *stream)
{
  // Serial write fumction
  Serial.write(c);
  return 0;
}

// File structure & output function for lcd output
static FILE lcdout = {0} ;      // lcd FILE structure
static int lcd_putchar(char ch, FILE* stream)
{
  // lcd write function
  lcd.write(ch);
  return (0);
}

void setup()
{
  Wire.begin();
  RTC.begin();
  Serial.begin(9600);
  lcd.begin(LCD_COLS, LCD_ROWS);
  
  // fill in the UART file descriptor with pointer to writer.
  fdev_setup_stream (&uartout, uart_putchar, NULL, _FDEV_SETUP_WRITE);
  stdout = &uartout;  // Output stdout to UART
   
  // fill in lcd file descriptor (we'll use fprintf to output to it)
  fdev_setup_stream (&lcdout, lcd_putchar, NULL, _FDEV_SETUP_WRITE);
  
  if (! RTC.isrunning()) {
    // If no RTC is installed, set time to compile time at each reset
    printf_P(PSTR("RTC is NOT running!\n"));  // Store this string in PROGMEM
    RTC.adjust(DateTime(__DATE__, __TIME__));
    }
  
  setSyncProvider(syncProvider);     // reference our syncProvider function instead of RTC_DS1307::get()
  
  printf_P(PSTR("Time: %02d:%02d:%02d\n"), hour(), minute(), second());  // Print the time
  SetAlarms();  // Set up above alarms
  
  // Print available SRAM for debugging, comment out if you want
  printf_P(PSTR("SRAM: %d\n"), freeRam());
  
  printf_P(PSTR("To test IR codes, send 1 - 32\n"));
}

void loop()
{
  if (Serial.available() > 0) {
    delay(5); //Wait for transmission to finish
    TestCodes(SerialReadInt());  // Go grab IR code and send it
  }
  Alarm.delay(100);   // Service alarms & wait (msec)
  lcd.setCursor(0,0);
  fprintf(&lcdout, "%02d:%02d", hour(), minute());  // Print the time HH:MM to the lcd
}

time_t syncProvider()
{
  //this does the same thing as RTC_DS1307::get()
  return RTC.now().unixtime();
}

void RandomStorm ()
{ 
  // Schedules a storm between 1 & 8 in the evening
  // It sets Storm2, followed by Cloud2 or DawnDusk or Moon2, depending on when the storm is over
  randomSeed(analogRead(randAnalogPin));  // Generate random seed on unused pin
  byte RH = random(23);                   // Randomizer for RandomStorm
  byte RM = random(59);
  byte RS = random(59);
  byte TSDurationH = random(2);
  byte TSDurationM = random(59);
  
  lcd.setCursor(0,1);
  if (RH >= 13 && RH <= 21)
    { // If 1:00PM - 8:00PM schedule a storm
      Alarm.alarmOnce(RH,RM,RS,Storm2);
      
      // Store strings in PROGMEM
      printf_P(PSTR("Scheduled Storm: %02d:%02d:%02d Duration: %02d:%02d\n"), RH, RM, RS, TSDurationH, TSDurationM);
      fprintf_P(&lcdout, PSTR("Sch Storm: %02d:%02d"), RH, RM);
          
      if ((RH + TSDurationH) < 19)   // Switch to Cloud2 if storm ends between 1:00-6:59pm
        {
          Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,Cloud2);
        }
      else if ((RH + TSDurationH) < 21)  // Switch to DawnDusk if storm ends between 7:00-8:59pm
        {
          Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,DawnDusk);
        }
      else                               // Otherwise, switch to Night2
        {
          Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,Moon2);
        }
    }
    else
    { // Don't really need this, but it can stay till we need the space
      printf_P(PSTR("No storm today\n"));
      fprintf_P(&lcdout, PSTR("No storm today\n"));
    }
}

int SerialReadInt()
{
  // Reads first 2 bytes from serial monitor; anything more is tossed
  byte i;
  char inBytes[3];
  char * inBytesPtr = &inBytes[0];  // Pointer to first element
    
    for (i=0; i<2; i++)             // Only want first 2 bytes
      inBytes[i] = Serial.read();
    inBytes[i] =  '\0';             // Put NULL character at the end
    while (Serial.read() >= 0)      // If anything else is there, throw it away
      ; // do nothing      
    return atoi(inBytesPtr);        // Convert to decimal
}

void TestCodes (int cmd)
{
  // Handles commands sent in from the serial monitor
  if (cmd >= 1 && cmd <= 32)
    { 
      // cmd must be 1 - 32
      SendCode(cmd-1, 1);
    }
    else { printf_P(PSTR("Invalid Choice\n")); }
}

void SendCode ( int cmd, byte numTimes )
{ // cmd = the element of the arrCode[] array that holds the IR code to be sent
  // numTimes = number of times to emmit the command
  // Shift header 16 bits to left, fetch code from PROGMEM & add it
  unsigned long irCode = (codeHeader << 16) + pgm_read_word_near(arrCodes + cmd);
  for( byte i = 0; i < numTimes; i++)
  {
    irsend.sendNEC(irCode,32); // Send/emmit code
    delay(100);
  }
  // Print the string associated with the IR code & the time
  //printf("%S: 0x%lx %02d:%02d:%02d\n", arrMSG[cmd], irCode, hour(), minute(), second());
  printf("%S: %02d:%02d:%02d\n", arrMSG[cmd], hour(), minute(), second());
  if (LCD_COLS == 16)
    {
      lcd.setCursor(0,1);
      fprintf(&lcdout, "%-16S", arrMSG[cmd]);
    }
  else
    {
      lcd.setCursor(6,0);
      fprintf(&lcdout, "%-14S", arrMSG[cmd]);
    }
}

int freeRam ()
{
  // Returns available SRAM
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}

// IR Code functions, called by alarms
void Orange() {SendCode(0,2);}
void Blue() {SendCode(1,2);}
void Rose() {SendCode(2,2);}
void PowerOnOff() {SendCode(3,1);}
void White() {SendCode(4,2);}
void FullSpec() {SendCode(5,2);}
void Purple() {SendCode(6,2);}
void Play() {SendCode(7,1);}
void RedUp() {SendCode(8,1);}
void GreenUp() {SendCode(9,1);}
void BlueUp() {SendCode(10,1);}
void WhiteUp() {SendCode(11,1);}
void RedDown() {SendCode(12,1);}
void GreenDown() {SendCode(13,1);}
void BlueDown() {SendCode(14,1);}
void WhiteDown() {SendCode(15,1);}
void M1Custom() {SendCode(16,2);}
void M2Custom() {SendCode(17,2);}
void M3Custom() {SendCode(18,2);}
void M4Custom() {SendCode(19,2);}
void Moon1() {SendCode(20,2);}
void Moon2() {SendCode(21,2);}
void Moon3() {SendCode(22,2);}
void DawnDusk() {SendCode(23,2);}
void Cloud1() {SendCode(24,2);}
void Cloud2() {SendCode(25,2);}
void Cloud3() {SendCode(26,2);}
void Cloud4() {SendCode(27,2);}
void Storm1() {SendCode(28,2);}
void Storm2() {SendCode(29,2);}
void Storm3() {SendCode(30,2);}
void Storm4() {SendCode(31,2);}


----------



## mistergreen

Dahammer said:


> If I understand correctly, he was wanting to add trailing spaces onto the strings to make the entire string 14 characters long, not add 14 spaces onto each string.


From what I understand with LCD, you have to clear out the characters or you'll get incoherent letters sometimes like:
you previously printed : 'bucket of fish'
and then you print in the same line: 'yellow'
the result is
'yellow of fish'. 

The code code I showed would fill out the line with ' ', wiping out all character and then print 'yellow'. Notice the cursor position.

I'm assuming 14 characters but it's probably 20.


----------



## Dahammer

mistergreen said:


> From what I understand with LCD, you have to clear out the characters or you'll get incoherent letters sometimes like:
> you previously printed : 'bucket of fish'
> and then you print in the same line: 'yellow'
> the result is
> 'yellow of fish'.
> 
> The code code I showed would fill out the line with ' ', wiping out all character and then print 'yellow'. Notice the cursor position.
> 
> I'm assuming 14 characters but it's probably 20.


Ahh! I've never messed with LCDs before but that makes sense. I got the impression that he was wanting 14 total characters from the way he was padding all of the strings to be that size. It never occurred to me that he simply wanted to overwrite any existing characters.


----------



## Caver

I loaded up v4.0 this afternoon. My LCD display isn't displaying right, but I'm using a 16x2 LCD, not a 20x4 like Indychus. The serial monitor is displaying correctly.

I had it working on v3.5, but apparently I didn't do the edits correctly in v4.

No problem...that will be something for me to play with until my 20x4 LCD arrives in the mail in the next day or so. Then I'll be on the same page as you guys.

Got my 18" LED+ yesterday and have it installed on the 6gal shrimp tank. It and the 20 long are both being run on a controller running v3.5 and look great!


----------



## Dahammer

Hmm, did you change this:


Code:


lcd.begin(20, 4);

to this?


Code:


lcd.begin(16, 2);

That should be all you have to change. It could be the file stream I'm printing too with fprintf, but it should work. But I don't have one to test with, got to get one.


----------



## Caver

Yep...That's exactly the one I changed.


----------



## Indychus

You need to remove 4 spaces from each of the text strings in the IR codes. Each string is 14 characters long and only needs to be 10 for a 16 x 2. The extra spaces will wrap around and overwrite anything you have on line 2.

Sent from my HTC One X


----------



## Indychus

Well, nevermind... Just noticed its different in the V4 that dahammer posted earlier. I haven't tried that one yet. No clue how to place text at certain spots on the LCD without setting the cursor.

Sent from my HTC One X


----------



## Dahammer

Caver said:


> Yep...That's exactly the one I changed.


Are you getting any output on the LCD or is it simply blank? Re-reading what you posted earlier, I'm guessing the output is there but just not correct?

I see a problem. Try changing line 246 (last line in the SendCode() function) from this:


Code:


fprintf(&lcdout, "%-14S", "%-14s: %02d:%02d", arrMSG[cmd], hour(), minute());

to this:


Code:


fprintf(&lcdout, "%-10S: %02d:%02d", arrMSG[cmd], hour(), minute());

I had too many specifiers in the code and too many spaces for that size screen. I'll fix it to where you don't have to hunt down & change the code for a 16x2 LCD once we confirm it's working as intended.


----------



## Dahammer

Indychus said:


> Well, nevermind... Just noticed its different in the V4 that dahammer posted earlier. I haven't tried that one yet. No clue how to place text at certain spots on the LCD without setting the cursor.
> 
> Sent from my HTC One X



Check out a printf reference.

It looks complex, but it really isn't once you understand how it works. I "think" the only specifier that doesn't work on the Arduino is the one for floats.



Code:


fprintf(&lcdout, "%-10S: %02d:%02d", arrMSG[cmd], hour(), minute());

&lcdout = reference to the file stream you want to print too. Could also be &uartout in this case.

%-10S
% = format specifier follows
-10 = left justify 10 characters wide
S = string in PROGMEM (arrMSG[cmd] in this case)

%02d: = HH: 

% = format specifier follows
0 = adds the leading zero for numbers less than 10
2 = 2 characters wide
d = decimal value (hour() in this case)
: = colon between the HH:MM

So what we end up with is yyyyyyyyyy: HH:MM

To get familiar with it, code yourself a simple test sketch and play with printf. Hope that helps.


----------



## Dahammer

Indychus said:


> No clue how to place text at certain spots on the LCD without setting the cursor.
> 
> Sent from my HTC One X


Bingo! I need some sleep. I left the code that sets the cursor off by accident when I copied the LCD code over. I'll fixed it above.

It should work correctly now.


----------



## Caver

Thanks for all the suggestions, unfortunately they came a little too late for me to try out last night and I only had a few minutes to play with them before work this morning.

I loaded up the revised v4 and made the couple of edits for my 16x2 display. It came up with the clock on line 1 and "No Thunderstorm today" on line 2. I then sent a code 6 (Full Spectrum) command in the serial monitor. The Serial monitor displayed correctly.

Here is what I got on the LCD. Excuse the cruddy cell phone pic. FYI...this display works fine on v3.5...

Hopefully today my 20x4 LCD will get here along with some DS18b20 temperature probes. Then my weekend play project will have all it's parts.


----------



## Dahammer

Sorry Caver, I must have been brain dead last night from lack of sleep. The fix I added last night didn't fix it. I shouldn't even be printing the time to the LCD in SendCode(). Anyway, I think I have it now. Recopy/paste the code and try it. I think I have it fixed now.....maybe..

Also, I added the below definitions at the top to keep folks from having to search through the code and change the LCD size. Just change them to reflect your LCD size. I've only implemented 16x2 and 20x4, though.



Code:


#define LCD_COLS 16      // Number of columns on the LCD (e.g. 16, 20, etc)
#define LCD_ROWS 2       // Number of rows on the LCD (e.g. 2, 4, etc)

Unfortunately, the * width specifier isn't implemented in the Arduino, so I couldn't use it to set the number of spaces in the fprintf() call in SendCode.


----------



## Caver

Don't worry about fixing the 16x2 display just for me. I should have a 20x4 this afternoon.

This is all very instructional for me to work with the Arduino tutorials online. Then play with the new versions of the controller code from you guys. I feel like I'm picking up things pretty well (for me!).

I should also have a couple of DS18b20 thermometer sensors to play with. I'm going to build up a dual channel thermometer to monitor my two tanks. There's not much code to the thermometer. I may have to see if I can figure out how to splice it into the controller code so one box can do both jobs...then again I may be way off in left field with that idea...:confused1:


----------



## Dahammer

I have a 16x2 LCD on the way to go in mine. The 20x4 won't fit in my enclosure without some modification, so I'll use the 16x2. If you don't mind, before you switch to the 20x4, could you see if the 16x2 works correctly? Just curious. 

I looked at the thermometer libraries yesterday and looked like it wouldn't be too much trouble to add it to this project. I almost ordered one of the stainless steel tipped DS18B20 sensors last night when I ordered the LCD but decided not too because I didn't think I'd use it. My controller is across the room from my tank inside a curio cabinet, so displaying temp on it just isn't practical.

Anyway you could probably just set the sensor up as shown here and then just add a function for an alarm to call every hour or so (or ever how often you want it updated).

Here is another good reference to the printf set of functions, if you interested in learning how to use it's formatting capabilities:
http://www.eecs.wsu.edu/~cs150/reading/printf.htm


----------



## Caver

No problem DaHammer.

I'll try the code on the 16x2 after I get home this afternoon. I probably won't get the 20x4 set up till later in the weekend.

As it happens, my controller is sitting on a shelf right next to my tanks, so running the temp sensor wires shouldn't be too big a deal. The Dallas Semi link you posted is one of the ones I was reading. I like that you can put multiple sensors on the same port.

I'm hoping there's enough room in the Arduino to incorporate the thermometer code with the controller. My current WAG (wild a**ed guess) is to let the light controller use display line 1 & 2 and the thermometer use lines 3 & 4.

Either way I'm really digging the mode switching on these lights in my tanks. The simple on/off from the timer was just too dull and I didn't like having to manually grab the remote to take advantage of all the features on the light. Plus I can fine tune the lights for the needs of the plants in the tank.

I'll try to post this evening on how the code works with the 16x2 display.


----------



## Indychus

I have mine set up with a thermometer right now, it just displays the temp in the bottom left corner of the display. There is enough room on the bottom line to display up to three individual temps. Alternately, you could do 2 and the average. I need to work on moving it into the latest code that dahammer posted, then I'll post it.

Sent from my HTC One X


----------



## Dahammer

Going to need a bigger screen for pH, nitrite, nitrate, ammonia, KH and GH. :biggrin:

A float switch that signaled the Arduino to turn off a valve in the canister lines in case of a leak would be neat as well.


----------



## Caver

Cool Indychus...roud:

If snail mail follows through, my new display and some waterproof temp sensors should be in my mailbox when I get home.

Looks like my weekend project plans may come together!


----------



## Caver

Dahammer said:


> Going to need a bigger screen for pH, nitrite, nitrate, ammonia, KH and GH. :biggrin:
> 
> A float switch that signaled the Arduino to turn off a valve in the canister lines in case of a leak would be neat as well.


We can only wish!

Think we'd have to move this to a Mega to do all that...:tongue:


----------



## Caver

Dahammer said:


> If you don't mind, before you switch to the 20x4, could you see if the 16x2 works correctly? Just curious.


Hi DaHammer,

I loaded up your modified code on my 16x2 display. It's working much better, but not quite perfectly.

Basically it isn't clearing all the text from the previous message and you can see remaining characters after the new message.

Pic 1 is your code with no mods.
Pic 2 is modded to display the last remote code sent on line 2.
Pic 3 is the power off/on code sent after a full spectrum code. You can see the m left over from the previous code.

Here's some very crummy cell phone pics...


----------



## Dahammer

Ok, thanks. I had assumed that the code in the previous version handled the 16x2 correctly and didn't really pay any attention when I copied it over. The code messages are too long to display on the same line as the time on the 16x2. So it's going to be a trade off either way you go.

I've modified the code and reposted it to where the time in on the 1 row by itself on the 16x2 and the code messages display on the 2nd row by themselves. The down side is that the storm time gets overwritten, so you don't if one is scheduled or not unless you catch it before the next state change.

You could also just shorten the code messages in the array up at the top of the source, to where the time and message would both fit on the same line. This is probably what I'll do when mine comes in, as just a few characters to let me know what mode it is is all I really need.

Or you could do away with the time altogether and have the messages on 1 line and the scheduled storm time on the other.


----------



## Caver

For me personally...

I'm not using the storms, so having the time on line 1 and the mode on line 2 is my preferred method.

I got my 20x4 display and some DS18b20's today. I got the temp sensors working...kinda...:icon_roll

I got the sensor addresses and they work when they're displayed in the serial monitor, but they don't want to work with my LCD 16x2 display shield board. There's some kind of scrambling of the temperature data from the sensors and the temp displays as trash characters or 0°C. The 16x2 display is a full on shield board with buttons and everything. I haven't gone through it to see what pins the buttons may be on. I suspect those are conflicting with the temp sensors.

Tomorrow I'm going to try to wire up the 20x4 and see if it works better. I'll need the extra two lines of display to make it work with the light controller anyway. That board is just the display, so I can wire it how I want.

I haven't committed to a specific enclosure for this yet, so it can grow as it wants and I'll work out the box later.

Fun...fun...fun...:hihi:


----------



## Dahammer

That looks like one of the Adafruit LCD shields. If so, I think they only use analog pins 4 & 5. I believe the onewire library uses pin 2 by default. If you are using v4 and fprintf, it could be some of the code in the fprintf call causing the weird output on the LCD.


----------



## Caver

Sorry,

I probably should have been more specific. I haven't tried to integrate the thermometer code into the light controller code yet. Indychus said he did and I was going to wait and see how he did it. 

I was playing with some of the DS18b20 tutorial sketches I found online. My plan was to verify I could get the thermometers working as a standalone before I tried to splice it into the controller. As it is, I found this apparent compatibility problem with my LCD shield board (a Sainsmart btw).

The 20x4 display is significantly larger than the 16x2. I'm going to have to do a completely new physical layout on some perf board to make this pretty.

Looks like a trip down to Rat Shack or Frys today. Some of the people there know me on a first name basis...:icon_roll


----------



## Caver

Got my 20x4 display working.

Now to wire up some temp sensors and see if they work with this one.

Used the 'good' camera this time...


----------



## Caver

Yay...:bounce:

Temperature sensors work with the 20x4 display!

Now comes the fun part...try to figure out how to marry the two sketches so they'll work together properly...:icon_eek:

I may be asking a bunch of questions!


----------



## Caver

OK...

I made a stab at combining the v4 controller code with a 2 sensor thermometer tutorial off the web.

It seems to work. I'm going to let it run on the bench for a while and see what happens.

I can't get my code to show up in the nice scrollable box like you guys have done. Besides, I'm sure I committed several dozen programming sins combining the sketches and I wouldn't want you to fall out of your chairs laughing...:eek5:


----------



## Indychus

Pretty much anywhere on a computer if you wrap something with [CODE.] Text here [/CODE.] it will put it into that format. Remove the periods in the brackets for it to work. I haven't messed with version 4 too much, I'm still running 3.7. Im trying to figure out exactly how the new code works before I update to it.

I damn sure won't do any laughing... I can write just enough code to be dangerous. Every time I program something at work the software guys redo it. Not in my job description haha.

Sent from my HTC One X


----------



## Caver

Here's my attempt at combining v4 and thermometer code.



Code:


///////////////////////////////////////////////////////////////////
//   Current Satellite LED+ Controller  V4.1b                    //
//   Indychus...Dahammer...mistergreen @ plantedtank.net         //
//   Clumsy mod to include dual channel thermometer              //
//   by Caver @ plantedtank.net                                  //
//   This code is public domain.  Pass it on.                    //
//   Confirmed on Arduino UNO 1.0.5                              //
//                                                               //
///////////////////////////////////////////////////////////////////
//
// This version uses Ken Shirriff's IRremote library to Rx/Tx the IR codes
// http://www.righto.com/2009/08/multi-protocol-infrared-remote-library.html
// 
// This code does NOT use PIN 13 on the Uno, as do previous versions
// Instead PIN 3, which is a PWM pin, is used. So you'll need to connect
// your LED to PIN 3 instead of PIN 13 for it to work.
//
// You can test the IR commands via the Arduino software's serial monitor
// by sending in a value from 1 - 32. Values follow the remote control, 
// left to right, top to bottom (e.g 1 = Orange, 2 = Blue, 21 = Moon1, etc)
//
// Install LCD per instructions at http://learn.adafruit.com/character-lcds/overview
//
#include <Wire.h>
#include <RTClib.h>
#include <Time.h>
#include <TimeAlarms.h>
#include <IRremote.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <LiquidCrystal.h>

RTC_DS1307 RTC;
IRsend irsend;

byte randAnalogPin = 0;   // This needs to be set to an unused Analog pin, Used by RandomStorm()
#define LCD_COLS 20      // Number of columns on the LCD (e.g. 16, 20, etc)
#define LCD_ROWS 4       // Number of rows on the LCD (e.g. 2, 4, etc)
LiquidCrystal lcd(10, 8, 7, 6, 5, 4);

// Data wire is plugged into pin 2 on the Arduino
#define ONE_WIRE_BUS 2

// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);

// Assign the addresses of your 1-Wire temp sensors.
// See the tutorial on how to obtain these addresses:
// http://www.hacktronics.com/Tutorials/arduino-1-wire-address-finder.html

DeviceAddress insideThermometer = { 0x28, 0x98, 0x82, 0xA8, 0x04, 0x00, 0x00, 0x6D }; //Red
DeviceAddress outsideThermometer = { 0x28, 0xA6, 0x33, 0xA8, 0x04, 0x00, 0x00, 0x11 }; //blue

// Current Satellite+ IR Codes (NEC Protocol)
unsigned long codeHeader = 0x20DF; // Always the same
// Remote buttons listed left to right, top to bottom
PROGMEM unsigned int arrCodes[32] = {0x3AC5,  // 1 -  Orange
                                     0xBA45,  // 2 -  Blue
                                     0x827D,  // 3 -  Rose
                                     0x02FD,  // 4 -  Power On/Off
                                     0x1AE5,  // 5 -  White
                                     0x9A65,  // 6 -  FullSpec
                                     0xA25D,  // 7 -  Purple
                                     0x22DD,  // 8 -  Play/Pause
                                     0x2AD5,  // 9 -  Red Up
                                     0xAA55,  // 10 - Green Up
                                     0x926D,  // 11 - Blue Up
                                     0x12ED,  // 12 - White Up
                                     0x0AF5,  // 13 - Red Down
                                     0x8A75,  // 14 - Green Down
                                     0xB24D,  // 15 - Blue Down
                                     0x32CD,  // 16 - White Down
                                     0x38C7,  // 17 - M1 Custom
                                     0xB847,  // 18 - M2 Custom
                                     0x7887,  // 19 - M3 Custom
                                     0xF807,  // 20 - M4 Custom
                                     0x18E7,  // 21 - Moon 1
                                     0x9867,  // 22 - Moon 2
                                     0x58A7,  // 23 - Moon 3
                                     0xD827,  // 24 - Dawn/Dusk
                                     0x28D7,  // 25 - Cloud 1
                                     0xA857,  // 26 - Cloud 2
                                     0x6897,  // 27 - Cloud 3
                                     0xE817,  // 28 - Cloud 4
                                     0x08F7,  // 29 - Storm 1
                                     0x8877,  // 30 - Storm 2
                                     0x48B7,  // 31 - Storm 3
                                     0xC837}; // 32 - Storm 4

// These are the messages that print on the serial monitor & lcd when each IR code is sent
#define MAX_MSG_LEN 13  // Maximum length of the arrMSG messages
prog_char PROGMEM arrMSG[][MAX_MSG_LEN+1] = {"Orange",        "Blue",          "Rose",          "Power On/Off",
                                             "White",         "Full Spectrum", "Purple",        "Play/Pause",
                                             "Red Up",        "Green Up",      "Blue Up",       "White Up",
                                             "Red Down",      "Green Down",    "Blue Down",     "White Down",
                                             "Custom Mix 1",  "Custom Mix 2",  "Custom Mix 3",  "Custom Mix 4",
                                             "Moonlight 1",   "Moonlight 2",   "Moonlight 3",   "Dawn/Dusk",
                                             "Cloud Cover 1", "Cloud Cover 2", "Cloud Cover 3", "Cloud Cover 4",
                                             "Storm 1",       "Storm 2",       "Storm 3",       "Storm 4"};

void SetAlarms()
{
  // Set up your desired alarms here
  // The default value of dtNBR_ALARMS is 6 in TimeAlarms.h.
  // This code sets 9 alarms by default, so you'll need to change dtNBR_ALARMS to 9 or more
  // Changes the times to suit yourself. Add as many alarms as you like, just stay within dtNBR_ALARMS
  Alarm.alarmRepeat(13,00,0, PowerOnOff);
  Alarm.alarmRepeat(13,02,0, Moon1);
  Alarm.alarmRepeat(13,45,0, DawnDusk);
  Alarm.alarmRepeat(14,00,0, Cloud2);     // (HR,MIN,SEC,FUNCTION)
  Alarm.alarmRepeat(15,00,0, Cloud1);
  Alarm.alarmRepeat(17,00,0, FullSpec);
  Alarm.alarmRepeat(19,00,0, Cloud1);
  Alarm.alarmRepeat(20,00,0, Cloud2);
  Alarm.alarmRepeat(21,45,0, Moon3);
  Alarm.alarmRepeat(22,00,0, Moon1);
  Alarm.alarmRepeat(23,00,0, Moon2);
  Alarm.alarmRepeat(23,59,0, PowerOnOff);
   
  // Comment these out if you don't want the chance of a random storm each day
  // Alarm.alarmRepeat(12,00,00, RandomStorm);
  // RandomStorm();  // Sets up intial storm so we don't have wait until alarm time
}

// Setup file structure & output function for stdout redirect
static FILE uartout = {0}; // create a FILE structure to reference our UART output function
static int uart_putchar (char c, FILE *stream)
{
  // Serial write fumction
  Serial.write(c);
  return 0;
}

// File structure & output function for lcd output
static FILE lcdout = {0} ;      // lcd FILE structure
static int lcd_putchar(char ch, FILE* stream)
{
  // lcd write function
  lcd.write(ch);
  return (0);
}

void setup()
{
  Wire.begin();
  RTC.begin();
  Serial.begin(9600);
  lcd.begin(LCD_COLS, LCD_ROWS);
  
  // Start up the library
sensors.begin();
// set the resolution to 10 bit (good enough?)
sensors.setResolution(insideThermometer, 9);
sensors.setResolution(outsideThermometer, 9);

lcd.begin(20,4); // columns, rows. use 16,2 for a 16x2 LCD, etc.
lcd.clear(); // start with a blank screen
  
  // fill in the UART file descriptor with pointer to writer.
  fdev_setup_stream (&uartout, uart_putchar, NULL, _FDEV_SETUP_WRITE);
  stdout = &uartout;  // Output stdout to UART
   
  // fill in lcd file descriptor (we'll use fprintf to output to it)
  fdev_setup_stream (&lcdout, lcd_putchar, NULL, _FDEV_SETUP_WRITE);
  
  if (! RTC.isrunning()) {
    // If no RTC is installed, set time to compile time at each reset
    printf_P(PSTR("RTC is NOT running!\n"));  // Store this string in PROGMEM
    RTC.adjust(DateTime(__DATE__, __TIME__));
    }
  
  setSyncProvider(syncProvider);     // reference our syncProvider function instead of RTC_DS1307::get()
  
  printf_P(PSTR("Time: %02d:%02d:%02d\n"), hour(), minute(), second());  // Print the time
  SetAlarms();  // Set up above alarms
  
  // Print available SRAM for debugging, comment out if you want
  printf_P(PSTR("SRAM: %d\n"), freeRam());
  
  printf_P(PSTR("To test IR codes, send 1 - 32\n"));
}

void loop()
{
  if (Serial.available() > 0) {
    delay(5); //Wait for transmission to finish
    TestCodes(SerialReadInt());  // Go grab IR code and send it
  }
  Alarm.delay(100);   // Service alarms & wait (msec)
  lcd.setCursor(0,0);
  fprintf(&lcdout, "%02d:%02d", hour(), minute());  // Print the time HH:MM to the lcd
    
delay(1000);
sensors.requestTemperatures();
lcd.setCursor(0,2);
lcd.print("Tank 1: ");
printTemperature(insideThermometer);
lcd.setCursor(0,3);
lcd.print("Tank 2: ");
printTemperature(outsideThermometer);
} 

time_t syncProvider()
{
  //this does the same thing as RTC_DS1307::get()
  return RTC.now().unixtime();
}

void RandomStorm ()
{ 
  // Schedules a storm between 1 & 8 in the evening
  // It sets Storm2, followed by Cloud2 or DawnDusk or Moon2, depending on when the storm is over
  randomSeed(analogRead(randAnalogPin));  // Generate random seed on unused pin
  byte RH = random(23);                   // Randomizer for RandomStorm
  byte RM = random(59);
  byte RS = random(59);
  byte TSDurationH = random(2);
  byte TSDurationM = random(59);
  
  lcd.setCursor(0,1);
  if (RH >= 13 && RH <= 21)
    { // If 1:00PM - 8:00PM schedule a storm
      Alarm.alarmOnce(RH,RM,RS,Storm2);
      
      // Store strings in PROGMEM
      printf_P(PSTR("Scheduled Storm: %02d:%02d:%02d Duration: %02d:%02d\n"), RH, RM, RS, TSDurationH, TSDurationM);
      fprintf_P(&lcdout, PSTR("Sch Storm: %02d:%02d"), RH, RM);
          
      if ((RH + TSDurationH) < 19)   // Switch to Cloud2 if storm ends between 1:00-6:59pm
        {
          Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,Cloud2);
        }
      else if ((RH + TSDurationH) < 21)  // Switch to DawnDusk if storm ends between 7:00-8:59pm
        {
          Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,DawnDusk);
        }
      else                               // Otherwise, switch to Night2
        {
          Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,Moon2);
        }
    }
    else
    { // Don't really need this, but it can stay till we need the space
      printf_P(PSTR("No storm today\n"));
      fprintf_P(&lcdout, PSTR("No storm today\n"));
    }
}

void printTemperature(DeviceAddress deviceAddress)
{
float tempC = sensors.getTempC(deviceAddress);
if (tempC == -127.00) {
lcd.print("Error");
} else {
// lcd.print(tempC);
// lcd.print("/");
lcd.print(DallasTemperature::toFahrenheit(tempC));
}
}
int SerialReadInt()
{
  // Reads first 2 bytes from serial monitor; anything more is tossed
  byte i;
  char inBytes[3];
  char * inBytesPtr = &inBytes[0];  // Pointer to first element
    
    for (i=0; i<2; i++)             // Only want first 2 bytes
      inBytes[i] = Serial.read();
    inBytes[i] =  '\0';             // Put NULL character at the end
    while (Serial.read() >= 0)      // If anything else is there, throw it away
      ; // do nothing      
    return atoi(inBytesPtr);        // Convert to decimal
}

void TestCodes (int cmd)
{
  // Handles commands sent in from the serial monitor
  if (cmd >= 1 && cmd <= 32)
    { 
      // cmd must be 1 - 32
      SendCode(cmd-1, 1);
    }
    else { printf_P(PSTR("Invalid Choice\n")); }
}

void SendCode ( int cmd, byte numTimes )
{ // cmd = the element of the arrCode[] array that holds the IR code to be sent
  // numTimes = number of times to emmit the command
  // Shift header 16 bits to left, fetch code from PROGMEM & add it
  unsigned long irCode = (codeHeader << 16) + pgm_read_word_near(arrCodes + cmd);
  for( byte i = 0; i < numTimes; i++)
  {
    irsend.sendNEC(irCode,32); // Send/emmit code
    delay(100);
  }
  // Print the string associated with the IR code & the time
  //printf("%S: 0x%lx %02d:%02d:%02d\n", arrMSG[cmd], irCode, hour(), minute(), second());
  printf("%S: %02d:%02d:%02d\n", arrMSG[cmd], hour(), minute(), second());
  if (LCD_COLS == 16)
    {
      lcd.setCursor(0,1);
      fprintf(&lcdout, "%-16S", arrMSG[cmd]);
    }
  else
    {
      lcd.setCursor(0,1);
      // lcd.setCursor(6,0);
      fprintf(&lcdout, "%-14S", arrMSG[cmd]);
    }
}

int freeRam ()
{
  // Returns available SRAM
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}

// IR Code functions, called by alarms
void Orange() {SendCode(0,2);}
void Blue() {SendCode(1,2);}
void Rose() {SendCode(2,2);}
void PowerOnOff() {SendCode(3,1);}
void White() {SendCode(4,2);}
void FullSpec() {SendCode(5,2);}
void Purple() {SendCode(6,2);}
void Play() {SendCode(7,1);}
void RedUp() {SendCode(8,1);}
void GreenUp() {SendCode(9,1);}
void BlueUp() {SendCode(10,1);}
void WhiteUp() {SendCode(11,1);}
void RedDown() {SendCode(12,1);}
void GreenDown() {SendCode(13,1);}
void BlueDown() {SendCode(14,1);}
void WhiteDown() {SendCode(15,1);}
void M1Custom() {SendCode(16,2);}
void M2Custom() {SendCode(17,2);}
void M3Custom() {SendCode(18,2);}
void M4Custom() {SendCode(19,2);}
void Moon1() {SendCode(20,2);}
void Moon2() {SendCode(21,2);}
void Moon3() {SendCode(22,2);}
void DawnDusk() {SendCode(23,2);}
void Cloud1() {SendCode(24,2);}
void Cloud2() {SendCode(25,2);}
void Cloud3() {SendCode(26,2);}
void Cloud4() {SendCode(27,2);}
void Storm1() {SendCode(28,2);}
void Storm2() {SendCode(29,2);}
void Storm3() {SendCode(30,2);}
void Storm4() {SendCode(31,2);}


----------



## Indychus

That delay (1000) in the loop function is going to cause you issues. It doesnt just delay the temp sensor, it stops the entire code from proceeding for a second. Being in the loop function, those seconds will add up quick. I'm not using a delay on mine and it seems to work ok. If you need the delay, look up the millis() function to create a delay that doesn't stop everything.


----------



## mistergreen

He probably does need a delay because you can't poll a device continuously or it'll error out. Use my millis() delay example from earlier instead of delay().
You can probably poll the temp sensor every minute instead of every second if you wanted to.


----------



## Caver

Thanks for the info.

I read a little on the millis function.

I'm going to have to sleep on that one and try again later. My brain doesn't want to wrap around that tonight.

It was a busy day...building up the display, testing sensors and playing with the code.

I also installed connectors on the sensors and the IR LED to facilitate tearing the unit apart for mods.

I think it's time to kick back, watch the fish and turn my brain off.


----------



## mistergreen

something like this



Code:


unsigned long currentMillis = 0;
unsigned long previousMillis = 0;

void setup() {
    // print initially so we don't have to wait around 
    // for a minute for the temperature to show up
    temperaturePrint();
}

void loop() {
//some code

  currentMillis = millis();
 //every minute
 if(currentMillis - previousMillis > 60000) {
    // save the last time
    previousMillis = currentMillis;
    temperaturePrint();
  }

}

void temperaturePrint() {
    //temperature stuff   
    sensors.requestTemperatures();
    lcd.setCursor(0,2);
    lcd.print("Tank 1: ");
    printTemperature(insideThermometer);
    lcd.setCursor(0,3);
    lcd.print("Tank 2: ");
    printTemperature(outsideThermometer);
}


----------



## Dahammer

How often are you wanting to update the temperature readings? I noticed that you have 2 calls to lcd.begin() in setup, only one is needed. You can also call sensors.getTempF instead of sensors.getTempC if you want the temp in Fahrenheit. But getTempF just calls toFahrenheit which is what you are doing as well. Haha!

You can also add the special character degree symbol using createChar function, which is part of the LiquidCrystal library.

Edit:
Actually, it may already be included, try this or check the datasheet for your LCD:

lcd.print((char)223));


----------



## mistergreen

hmmm, actually since you guys are using the timeAlarms library, use that instead of millis()
It's neater.



Code:


Alarm.timerRepeat(60, temperaturePrint);
//Create a timer that will call a function every at an interval of "seconds".


----------



## Dahammer

mistergreen said:


> hmmm, actually since you guys are using the timeAlarms library, use that instead of millis()
> It's neater.
> 
> 
> 
> Code:
> 
> 
> Alarm.timerRepeat(60, temperaturePrint);
> //Create a timer that will call a function every at an interval of "seconds".


Yeah that's what I'd do also and update it every how often you like. He'd just need to move his print code to the printTemperature and modify it so it didn't require passing the device address or use another function and pass them with it.


----------



## Caver

Dahammer said:


> Actually, it may already be included, try this or check the datasheet for your LCD:
> 
> lcd.print((char)223));


Your above trick works to print the degree symbol, but I had to remove one of the close parenthesis to get it to compile.

Thanks for all the ideas. I'm going to have to study to figure out how to implement them.

This was my very clumsy beginner attempt at programming the Arduino for my specific application. I haven't done any programming in literally decades...so I'm going to make a lot of very basic mistakes.

For this sketch I literally copied pieces of a tutorial sketch for an indoor/outdoor thermometer and pasted the pieces into v4 where I thought they needed to go. I'm sure there's all kinds of 'wrong' stuff!

Some things I've noticed with my controller running on the bench tonight...Every so often, right after a code is sent, the temperature goes to 3 decimal places. I thought I had it set for 2 by setting the sensors to 9 bit resolution. Obviously I'm missing something. Actually, I'd be satisfied with 1 decimal place for the temp.

Oh well...almost bedtime. Maybe some of this coding stuff will make more sense to me after I get some sleep and a couple of cups of coffee in the morning.


----------



## Dahammer

Oops, sorry. Yeah, 1 too many parenthesis.

What sensor do you have? Check out the calculateTemperature function in DallasTemperature.cpp. Looking at the datasheet, I'm guessing you have a DS18S20 based sensor, since it allows for more than 1 sensor on the same wire. For that sensor, the code in calculateTemperature function calculates an extended resolution regardless of what you set it too.


----------



## Caver

Morning DaHammer.

I'm using the DS18b20 sensors. I got some already on a cable and waterproofed.

The ends are a stainless steel cap. Hope that's ok for Red Cherry Shrimp...


----------



## Dahammer

Hmm. Then 9 bit resolution should yield a temperature to the nearest 0.5 degree.


----------



## Caver

Yeah,

The temp reading to 2 decimal places is a pretty minor thing.

I'm studying the coding for reading the sensors so I can understand the delays and your suggestions for using millis. If I'm understanding what I'm reading correctly...the sensors need approx. 750 mS to complete a read and transfer data.

I just want to clean up the code so the system doesn't barf on me after running a few hours/days...

Of course if someone on here wants to do the thermometer thing and knows how to clean it up, I have no qualms with riding coat tails...:icon_mrgr


----------



## mistergreen

Can't you limit the decimal places like this?

print(val,format);
lcd.print(4.3224,1) -> 4.3


----------



## Caver

Boy...

That Alarm.timerRepeat is kicking my butt!

I've read several tutorials on it and I keep getting compiler errors.

I'm going to keep playing with it and reading while I finish laundry. I'll figure it out eventually...:icon_conf


----------



## mistergreen

what's your code and what error message are you getting?


----------



## Caver

mistergreen said:


> what's your code and what error message are you getting?


I'm not sure how to even begin mistergreen...

Basically I'm trying to clean up my sketch from post #296, where I patched in code for two DS18B20 thermometers to the light controller.

There were some comments that the 1 second delay in the loop function for the sensors would cause problems, so I'm trying to do something other than the standard delay that stops the whole device. From what I've read on the sensors, they need a delay in polling or they'll get errors.

You and DaHammer suggested the Alarm.timerRepeat and moving the print code...etc. I've tried multiple iterations based on the tutorials I've read and get many different errors.

I suspect it has to do with the passing of the sensor device addresses and I'm probably messing up moving the print and sensor address code. 

I'm so new at this coding process, I'm probably overlooking something very basic. I can build hardware all day long, but software is much more difficult for me.


----------



## Dahammer

Caver said:


> I'm not sure how to even begin mistergreen...
> 
> Basically I'm trying to clean up my sketch from post #296, where I patched in code for two DS18B20 thermometers to the light controller.
> 
> There were some comments that the 1 second delay in the loop function for the sensors would cause problems, so I'm trying to do something other than the standard delay that stops the whole device. From what I've read on the sensors, they need a delay in polling or they'll get errors.
> 
> You and DaHammer suggested the Alarm.timerRepeat and moving the print code...etc. I've tried multiple iterations based on the tutorials I've read and get many different errors.
> 
> I suspect it has to do with the passing of the sensor device addresses and I'm probably messing up moving the print and sensor address code.
> 
> I'm so new at this coding process, I'm probably overlooking something very basic. I can build hardware all day long, but software is much more difficult for me.


Post the code and we will help. Basically, what you need to do is remove the code you put into the loop() function, create a function to handle your alarm events, and then set an alarm to call your new function ever how often you want. If it were me, I'd think an updated temp every hour or so would be sufficient.

You shouldn't need a delay as long as you don't query the temp sensors for a temp every second or so.


----------



## Dahammer

Try this:

Up at the top, redefine your sensor addresses to this (makes more sense since you're not really doing inside & outside):



Code:


DeviceAddress thermometer_1 = { 0x28, 0x98, 0x82, 0xA8, 0x04, 0x00, 0x00, 0x6D }; //Red
DeviceAddress thermometer_2 = { 0x28, 0xA6, 0x33, 0xA8, 0x04, 0x00, 0x00, 0x11 }; //blue

Changing the names will also require changing the setResolution calls in Setup():


Code:


  sensors.setResolution(thermometer_1, 9);
  sensors.setResolution(thermometer_2, 9);

Then add this following code to the SetAlarms() function, doesn't matter where you add it. You can also change the alarm frequency if you like. It's second based, but alarming every second would probably cause problems.


Code:


  Alarm.timerRepeat(1800, PrintTemp);  // Print temps every 30 minutes
  PrintTemp(); // Display initial temps

Then add the following function somewhere:


Code:


void PrintTemp()
{
  float tempC = sensors.getTempC(thermometer_1);
  lcd.setCursor(0,2);
  lcd.print("Tank: 1 ");
  lcd.print(DallasTemperature::toFahrenheit(tempC), 1);
  lcd.print((char)223);
  
  tempC = sensors.getTempC(thermometer_2);
  lcd.setCursor(0,3);
  lcd.print("Tank 2: ");
  lcd.print(DallasTemperature::toFahrenheit(tempC), 1);
  lcd.print((char)223);
}

Then remove the code you put in the loop() function, as well the printTemperature function that you added. You could also add code to the TestCodes() function so that you could initiate a test from the serial terminal.



Code:


void TestCodes (int cmd)
{
  // Handles commands sent in from the serial monitor
  if (cmd >= 1 && cmd <= 32)
    { 
      // cmd must be 1 - 32
      SendCode(cmd-1, 1);
    }
    else if (cmd == 33)
    {
    	PrintTemp();  // Send 33 to test temp sensors
    }
    else { printf_P(PSTR("Invalid Choice\n")); }
}


----------



## Caver

Let me play with it for a bit and when I get my best guess I'll post something.

Right now I've tried 5-6 variations and I don't have any idea which is closer and I get different and complex error messages each time.

One specific question...the example sketch in the TimeAlarms library shows the "Alarm.timerRepeat(15, Repeats);" statement going in "void setup()".

I'm assuming I should be able to add the same statement to the setup section of the controller code as in the above example and pull the section of code dealing with polling and printing the sensors currently in "void loop()" and creating another function (for the sake of simplicity) called "void Repeats()".

Also as I understand it, I can change the 15 to any value down to 1. Can 'Repeats' be changed to another function name?

The section of code I'm pulling out of the loop is

delay(1000) // Supposedly this is what needs to be pulled out of the loop
sensors.requestTemperatures();
lcd.setCursor(0,2);
lcd.print("Tank 1: ");
printTemperature(insideThermometer);
lcd.print((char)223);
lcd.setCursor(0,3);
lcd.print("Tank 2: ");
printTemperature(outsideThermometer);
lcd.print((char)223);

I'm adding it back in outside the loop as...

void Repeats();
{
sensors.requestTemperatures();
lcd.setCursor(0,2);
lcd.print("Tank 1: ");
printTemperature(insideThermometer);
lcd.print((char)223);
lcd.setCursor(0,3);
lcd.print("Tank 2: ");
printTemperature(outsideThermometer);
lcd.print((char)223);
}

I'm thinking there's something in there that the Alarm.timerRepeat can't process. It doesn't seem to matter where I put it. I get different error messages every time.

That's basically what I've been trying.

As for polling frequency...I'd like something at least every few seconds so it would react quickly to changing conditions. Sure...most of the time it wouldn't be necessary, but the nerd in me wants 'realtime' data...:icon_mrgr

I see DaHammer has responded with some code for me to try. I just tried it, but I got some errors. I probably made a typo or something. I'm going to get something to eat and play with it some more.

Thanks for all the help. I don't mean to distract from the original direction of the thread for my customizations. If we get too far out in left field, just say so. This is a really cool gadget even without the thermometers...roud:


----------



## Dahammer

I corrected a typo or 2, so you may want to retry the code above if you copied it after I first posted it.

Consider the following call:


Code:


Alarm.alarmRepeat(1800, PrintTemp);

1800 is number of seconds to wait. PrintTemp is the function that will be called after 1800 seconds. You can change 1800 to any value you like, up to 24 hours best I recall, but I wouldn't do it every second. If you did, then you'll likely run into problems with the sensors not being able to keep up with the demand. Besides the temperature should be rather stable, so it's really not necessary to poll them so frequently. PrintTemp is just a name of a function. It can be changed to the name of any valid function in the program that you want to call with the timer.


----------



## mistergreen

put alarmRepeat where the other alarms are, inside void SetAlarms()


----------



## Caver

Hi Guys,

It works ! :bounce:
I input DaHammers suggestions. I corrected a couple of typos. In the printTemp routine I changed a Serial.print call to lcd.print to get the temps to display.

I changed the Alarm.alarmRepeat to Alarm.timerRepeat and put it in with the rest of the alarms. Otherwise the temps apparently weren't updating, even if I reduced the time for testing. And the temps are displaying to one decimal point.

Now to let it run for a few days and see what happens.

I'll quit bothering you guys now so we can get back to the original topic of the thread.

Here's my code as it stands tonight. Feel free to use/ignore as you see fit.

Thanks again to all for your help and comments! roud:




Code:


///////////////////////////////////////////////////////////////////
//   Current Satellite LED+ Controller  V4.1c                    //
//   Indychus...Dahammer...mistergreen @ plantedtank.net         //
//   Clumsy mod to include dual channel thermometer by           //
//   Caver @ plantedtank.net with lots of help from the above.   //
//   This code is public domain.  Pass it on.                    //
//   Confirmed on Arduino UNO 1.0.5                              //
///////////////////////////////////////////////////////////////////
//
// This version uses Ken Shirriff's IRremote library to Rx/Tx the IR codes
// http://www.righto.com/2009/08/multi-protocol-infrared-remote-library.html
// 
// This code does NOT use PIN 13 on the Uno, as do previous versions
// Instead PIN 3, which is a PWM pin, is used. So you'll need to connect
// your LED to PIN 3 instead of PIN 13 for it to work.
//
// You can test the IR commands via the Arduino software's serial monitor
// by sending in a value from 1 - 32. Values follow the remote control, 
// left to right, top to bottom (e.g 1 = Orange, 2 = Blue, 21 = Moon1, etc)
//
// Install LCD per instructions at http://learn.adafruit.com/character-lcds/overview
//
#include <Wire.h>
#include <RTClib.h>
#include <Time.h>
#include <TimeAlarms.h>
#include <IRremote.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <LiquidCrystal.h>

RTC_DS1307 RTC;
IRsend irsend;

byte randAnalogPin = 0;   // This needs to be set to an unused Analog pin, Used by RandomStorm()
#define LCD_COLS 20      // Number of columns on the LCD (e.g. 16, 20, etc)
#define LCD_ROWS 4       // Number of rows on the LCD (e.g. 2, 4, etc)
LiquidCrystal lcd(10, 8, 7, 6, 5, 4);

// Data wire is plugged into pin 2 on the Arduino
#define ONE_WIRE_BUS 2

// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);

// Assign the addresses of your 1-Wire temp sensors.
// See the tutorial on how to obtain these addresses:
// http://www.hacktronics.com/Tutorials/arduino-1-wire-address-finder.html

DeviceAddress thermometer_1 = { 0x28, 0x98, 0x82, 0xA8, 0x04, 0x00, 0x00, 0x6D }; //Red
DeviceAddress thermometer_2 = { 0x28, 0xA6, 0x33, 0xA8, 0x04, 0x00, 0x00, 0x11 }; //blue

// Current Satellite+ IR Codes (NEC Protocol)
unsigned long codeHeader = 0x20DF; // Always the same
// Remote buttons listed left to right, top to bottom
PROGMEM unsigned int arrCodes[32] = {0x3AC5,  // 1 -  Orange
                                     0xBA45,  // 2 -  Blue
                                     0x827D,  // 3 -  Rose
                                     0x02FD,  // 4 -  Power On/Off
                                     0x1AE5,  // 5 -  White
                                     0x9A65,  // 6 -  FullSpec
                                     0xA25D,  // 7 -  Purple
                                     0x22DD,  // 8 -  Play/Pause
                                     0x2AD5,  // 9 -  Red Up
                                     0xAA55,  // 10 - Green Up
                                     0x926D,  // 11 - Blue Up
                                     0x12ED,  // 12 - White Up
                                     0x0AF5,  // 13 - Red Down
                                     0x8A75,  // 14 - Green Down
                                     0xB24D,  // 15 - Blue Down
                                     0x32CD,  // 16 - White Down
                                     0x38C7,  // 17 - M1 Custom
                                     0xB847,  // 18 - M2 Custom
                                     0x7887,  // 19 - M3 Custom
                                     0xF807,  // 20 - M4 Custom
                                     0x18E7,  // 21 - Moon 1
                                     0x9867,  // 22 - Moon 2
                                     0x58A7,  // 23 - Moon 3
                                     0xD827,  // 24 - Dawn/Dusk
                                     0x28D7,  // 25 - Cloud 1
                                     0xA857,  // 26 - Cloud 2
                                     0x6897,  // 27 - Cloud 3
                                     0xE817,  // 28 - Cloud 4
                                     0x08F7,  // 29 - Storm 1
                                     0x8877,  // 30 - Storm 2
                                     0x48B7,  // 31 - Storm 3
                                     0xC837}; // 32 - Storm 4

// These are the messages that print on the serial monitor & lcd when each IR code is sent
#define MAX_MSG_LEN 13  // Maximum length of the arrMSG messages
prog_char PROGMEM arrMSG[][MAX_MSG_LEN+1] = {"Orange",        "Blue",          "Rose",          "Power On/Off",
                                             "White",         "Full Spectrum", "Purple",        "Play/Pause",
                                             "Red Up",        "Green Up",      "Blue Up",       "White Up",
                                             "Red Down",      "Green Down",    "Blue Down",     "White Down",
                                             "Custom Mix 1",  "Custom Mix 2",  "Custom Mix 3",  "Custom Mix 4",
                                             "Moonlight 1",   "Moonlight 2",   "Moonlight 3",   "Dawn/Dusk",
                                             "Cloud Cover 1", "Cloud Cover 2", "Cloud Cover 3", "Cloud Cover 4",
                                             "Storm 1",       "Storm 2",       "Storm 3",       "Storm 4"};

void SetAlarms()
{
  // Set up your desired alarms here
  // The default value of dtNBR_ALARMS is 6 in TimeAlarms.h.
  // This code sets 9 alarms by default, so you'll need to change dtNBR_ALARMS to 9 or more
  // Changes the times to suit yourself. Add as many alarms as you like, just stay within dtNBR_ALARMS
  Alarm.alarmRepeat(13,00,0, PowerOnOff);
  Alarm.alarmRepeat(13,02,0, Moon1);
  Alarm.alarmRepeat(13,45,0, DawnDusk);
  Alarm.alarmRepeat(14,00,0, Cloud2);     // (HR,MIN,SEC,FUNCTION)
  Alarm.alarmRepeat(15,00,0, Cloud1);
  Alarm.alarmRepeat(17,00,0, FullSpec);
  Alarm.alarmRepeat(19,00,0, Cloud1);
  Alarm.alarmRepeat(20,00,0, Cloud2);
  Alarm.alarmRepeat(21,45,0, Moon3);
  Alarm.alarmRepeat(22,00,0, Moon1);
  Alarm.alarmRepeat(23,00,0, Moon2);
  Alarm.alarmRepeat(23,59,0, PowerOnOff);
  Alarm.timerRepeat(2, PrintTemp);
  PrintTemp(); // Display initial temps
   
  // Comment these out if you don't want the chance of a random storm each day
  // Alarm.alarmRepeat(12,00,00, RandomStorm);
  // RandomStorm();  // Sets up intial storm so we don't have wait until alarm time
}

// Setup file structure & output function for stdout redirect
static FILE uartout = {0}; // create a FILE structure to reference our UART output function
static int uart_putchar (char c, FILE *stream)
{
  // Serial write fumction
  Serial.write(c);
  return 0;
}

// File structure & output function for lcd output
static FILE lcdout = {0} ;      // lcd FILE structure
static int lcd_putchar(char ch, FILE* stream)
{
  // lcd write function
  lcd.write(ch);
  return (0);
}

void setup()
{
  Wire.begin();
  RTC.begin();
  Serial.begin(9600);
  lcd.begin(LCD_COLS, LCD_ROWS);
  
  // Start up the library
sensors.begin();
// set the resolution to 10 bit (good enough?)

  sensors.setResolution(thermometer_1, 9);
  sensors.setResolution(thermometer_2, 9);

lcd.clear(); // start with a blank screen
  
  // fill in the UART file descriptor with pointer to writer.
  fdev_setup_stream (&uartout, uart_putchar, NULL, _FDEV_SETUP_WRITE);
  stdout = &uartout;  // Output stdout to UART
   
  // fill in lcd file descriptor (we'll use fprintf to output to it)
  fdev_setup_stream (&lcdout, lcd_putchar, NULL, _FDEV_SETUP_WRITE);
  
  if (! RTC.isrunning()) {
    // If no RTC is installed, set time to compile time at each reset
    printf_P(PSTR("RTC is NOT running!\n"));  // Store this string in PROGMEM
    RTC.adjust(DateTime(__DATE__, __TIME__));
    }
  
  setSyncProvider(syncProvider);     // reference our syncProvider function instead of RTC_DS1307::get()
  
  printf_P(PSTR("Time: %02d:%02d:%02d\n"), hour(), minute(), second());  // Print the time
  SetAlarms();  // Set up above alarms
  
  // Print available SRAM for debugging, comment out if you want
  printf_P(PSTR("SRAM: %d\n"), freeRam());
  
  printf_P(PSTR("To test IR codes, send 1 - 32\n"));
}

void loop()
{
    
  if (Serial.available() > 0) {
    delay(5); //Wait for transmission to finish
    TestCodes(SerialReadInt());  // Go grab IR code and send it
  }
  Alarm.delay(100);   // Service alarms & wait (msec)
  lcd.setCursor(7,0);
  fprintf(&lcdout, "%02d:%02d", hour(), minute());  // Print the time HH:MM to the lcd
}
time_t syncProvider()
{
  //this does the same thing as RTC_DS1307::get()
  return RTC.now().unixtime();
}

void RandomStorm ()
{ 
  // Schedules a storm between 1 & 8 in the evening
  // It sets Storm2, followed by Cloud2 or DawnDusk or Moon2, depending on when the storm is over
  randomSeed(analogRead(randAnalogPin));  // Generate random seed on unused pin
  byte RH = random(23);                   // Randomizer for RandomStorm
  byte RM = random(59);
  byte RS = random(59);
  byte TSDurationH = random(2);
  byte TSDurationM = random(59);
  
  lcd.setCursor(0,1);
  if (RH >= 13 && RH <= 21)
    { // If 1:00PM - 8:00PM schedule a storm
      Alarm.alarmOnce(RH,RM,RS,Storm2);
      
      // Store strings in PROGMEM
      printf_P(PSTR("Scheduled Storm: %02d:%02d:%02d Duration: %02d:%02d\n"), RH, RM, RS, TSDurationH, TSDurationM);
      fprintf_P(&lcdout, PSTR("Sch Storm: %02d:%02d"), RH, RM);
          
      if ((RH + TSDurationH) < 19)   // Switch to Cloud2 if storm ends between 1:00-6:59pm
        {
          Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,Cloud2);
        }
      else if ((RH + TSDurationH) < 21)  // Switch to DawnDusk if storm ends between 7:00-8:59pm
        {
          Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,DawnDusk);
        }
      else                               // Otherwise, switch to Night2
        {
          Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,Moon2);
        }
    }
    else
    { // Don't really need this, but it can stay till we need the space
      printf_P(PSTR("No storm today\n"));
      fprintf_P(&lcdout, PSTR("No storm today\n"));
    }
}
int SerialReadInt()
{
  // Reads first 2 bytes from serial monitor; anything more is tossed
  byte i;
  char inBytes[3];
  char * inBytesPtr = &inBytes[0];  // Pointer to first element
    
    for (i=0; i<2; i++)             // Only want first 2 bytes
      inBytes[i] = Serial.read();
    inBytes[i] =  '\0';             // Put NULL character at the end
    while (Serial.read() >= 0)      // If anything else is there, throw it away
      ; // do nothing      
    return atoi(inBytesPtr);        // Convert to decimal
}

void TestCodes (int cmd)
{
  // Handles commands sent in from the serial monitor
  if (cmd >= 1 && cmd <= 32)
    { 
      // cmd must be 1 - 32
      SendCode(cmd-1, 1);
    }
    else { printf_P(PSTR("Invalid Choice\n")); }
}

void SendCode ( int cmd, byte numTimes )
{ // cmd = the element of the arrCode[] array that holds the IR code to be sent
  // numTimes = number of times to emmit the command
  // Shift header 16 bits to left, fetch code from PROGMEM & add it
  unsigned long irCode = (codeHeader << 16) + pgm_read_word_near(arrCodes + cmd);
  for( byte i = 0; i < numTimes; i++)
  {
    irsend.sendNEC(irCode,32); // Send/emmit code
    delay(100);
  }
  // Print the string associated with the IR code & the time
  //printf("%S: 0x%lx %02d:%02d:%02d\n", arrMSG[cmd], irCode, hour(), minute(), second());
  printf("%S: %02d:%02d:%02d\n", arrMSG[cmd], hour(), minute(), second());
  if (LCD_COLS == 16)
    {
      lcd.setCursor(0,1);
      fprintf(&lcdout, "%-16S", arrMSG[cmd]);
    }
  else
    {
      lcd.setCursor(0,1);
      // lcd.setCursor(6,0);
      fprintf(&lcdout, "%-14S", arrMSG[cmd]);
    }
}

int freeRam ()
{
  // Returns available SRAM
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}

void PrintTemp()
{
  sensors.requestTemperatures();
  float tempC = sensors.getTempC(thermometer_1);
  lcd.setCursor(0,2);
  lcd.print("Tank 1: ");
  lcd.print(DallasTemperature::toFahrenheit(tempC), 1);
  lcd.print((char)223);
  
  tempC = sensors.getTempC(thermometer_2);
  lcd.setCursor(0,3);
  lcd.print("Tank 2: ");
  lcd.print(DallasTemperature::toFahrenheit(tempC), 1);
  lcd.print((char)223);
  
}

// IR Code functions, called by alarms
void Orange() {SendCode(0,2);}
void Blue() {SendCode(1,2);}
void Rose() {SendCode(2,2);}
void PowerOnOff() {SendCode(3,1);}
void White() {SendCode(4,2);}
void FullSpec() {SendCode(5,2);}
void Purple() {SendCode(6,2);}
void Play() {SendCode(7,1);}
void RedUp() {SendCode(8,1);}
void GreenUp() {SendCode(9,1);}
void BlueUp() {SendCode(10,1);}
void WhiteUp() {SendCode(11,1);}
void RedDown() {SendCode(12,1);}
void GreenDown() {SendCode(13,1);}
void BlueDown() {SendCode(14,1);}
void WhiteDown() {SendCode(15,1);}
void M1Custom() {SendCode(16,2);}
void M2Custom() {SendCode(17,2);}
void M3Custom() {SendCode(18,2);}
void M4Custom() {SendCode(19,2);}
void Moon1() {SendCode(20,2);}
void Moon2() {SendCode(21,2);}
void Moon3() {SendCode(22,2);}
void DawnDusk() {SendCode(23,2);}
void Cloud1() {SendCode(24,2);}
void Cloud2() {SendCode(25,2);}
void Cloud3() {SendCode(26,2);}
void Cloud4() {SendCode(27,2);}
void Storm1() {SendCode(28,2);}
void Storm2() {SendCode(29,2);}
void Storm3() {SendCode(30,2);}
void Storm4() {SendCode(31,2);}


----------



## Dahammer

Caver said:


> Hi Guys,
> 
> It works ! :bounce:
> I input DaHammers suggestions. I corrected a couple of typos. In the printTemp routine I changed a Serial.print call to lcd.print to get the temps to display.


It seems that no matter what I do, I always do something boneheaded like that.



Caver said:


> I changed the Alarm.alarmRepeat to Alarm.timerRepeat and put it in with the rest of the alarms. Otherwise the temps apparently weren't updating, even if I reduced the time for testing. And the temps are displaying to one
> decimal point.


Ha! Good find. Alarm.alarmRepeat actually triggers an alarm at the same time every day and not after every x seconds. timerRepeat is the one that repeats every x seconds. Another boneheaded mistake on my part. I went back to the previous post and fixed the mistakes you found.



Caver said:


> Now to let it run for a few days and see what happens.
> 
> I'll quit bothering you guys now so we can get back to the original topic of the thread.
> 
> Here's my code as it stands tonight. Feel free to use/ignore as you see fit.
> 
> Thanks again to all for your help and comments! roud:


You're not bothering me. Glad to help, the only downside is as you've found out I'm prone to boneheaded mistakes. Usually I find most of them through testing myself, but in the case of the LCD and thermometer, I have nothing to test yet. Glad you got it going. I was surprised at the overhead the thermometer libraries/code add, 5k, wow!


----------



## ipkiss

This is impressive. Great job, guys! Let's see how long I can hold out before I succumb to picking up on a new project that I can't finish!


----------



## Dahammer

Mine hasn't missed a beat. I added a 16x2 LCD to it. I still haven't gotten around to ramping the lights up a d down though.


----------



## Indychus

Dahammer said:


> Mine hasn't missed a beat. I added a 16x2 LCD to it. I still haven't gotten around to ramping the lights up a d down though.


Nice! Mine has been running the older software (3.7) for several weeks now without any issues. I haven't been home to update it or tinker with it any in a while. Luckily the wife is pretty tech-savvy and can keep an eye on it for me.

Sent from my HTC One X


----------



## Caver

Yeah...

It's been kinda quiet here the last couple of weeks. I figured no news is good news...everyones controllers are perking along nicely.

Mine is. I'm running the last version of code I posted, controlling lights on a 20 gal Long and a 6 gal shrimp tank. The lights are working flawlessly. I get an occasional glitch with the temperature sensors...but I think that's because of the funky plug in wiring. I still haven't cleaned it up in a permanent way.

Another thing I've noticed is the RTC runs fast big time. I have to hook the controller up to a PC and reset the clock every couple of weeks...it gains almost 10 minutes in that time frame. Not a big deal in the overall scheme of things...but I'm kinda picky about clocks being accurate.


----------



## Indychus

I think the RTC is running fast is due to the delay(100) in the SendCode function on the latest software version. 

Sent from my HTC One X


----------



## Indychus

That's just me thinking out loud... It would make more sense if the time was slow, but in my experience using the delay() function always causes issues.

Sent from my HTC One X


----------



## mistergreen

It's not the RTC that's fast, it's the time the arduino is keeping that's off, I assuming. In the code it's best to re-sync the arduino time with the RTC maybe every 24hours.


Code:


setSyncProvider(syncProvider);

The arduino/Time.h library uses millis() which isn't accurate.


----------



## Caver

OK.

After a VERY quick look at SyncProvider info...I'm at work and for some reason they keep asking me to do stuff...

To implement a resync to the RTC every 24 hours, I'd need to add a line...

setSyncInterval (86400); //RTC sync interval in seconds

Would that go right after the "setSyncProvider(syncProvider);" in void setup ()?


----------



## Dahammer

Caver said:


> OK.
> 
> After a VERY quick look at SyncProvider info...I'm at work and for some reason they keep asking me to do stuff...
> 
> To implement a resync to the RTC every 24 hours, I'd need to add a line...
> 
> setSyncInterval (86400); //RTC sync interval in seconds
> 
> Would that go right after the "setSyncProvider(syncProvider);" in void setup ()?


syncInterval is set to 300 by default in time.cpp, so you don't have to reset it. It should resync automatically every 5 minutes. I suspect there is something wrong with your RTC. My time is dead on and hasn't been unplugged in over a week.


----------



## Dahammer

mistergreen said:


> It's not the RTC that's fast, it's the time the arduino is keeping that's off, I assuming. In the code it's best to re-sync the arduino time with the RTC maybe every 24hours.
> 
> 
> Code:
> 
> 
> setSyncProvider(syncProvider);
> 
> The arduino/Time.h library uses millis() which isn't accurate.



The code already does:


Code:


  setSyncProvider(syncProvider);     // reference our syncProvider function instead of RTC_DS1307::get()




Code:


time_t syncProvider()
{
  //this does the same thing as RTC_DS1307::get()
  return RTC.now().unixtime();
}


----------



## Dahammer

Indychus said:


> I think the RTC is running fast is due to the delay(100) in the SendCode function on the latest software version.
> 
> Sent from my HTC One X


The RTC circuit keeps time independent of the program running on the Arduino, so I'm not sure how the program has any effect on it.


----------



## mistergreen

Dahammer said:


> The code already does:
> 
> 
> Code:
> 
> 
> setSyncProvider(syncProvider);     // reference our syncProvider function instead of RTC_DS1307::get()
> 
> 
> 
> 
> Code:
> 
> 
> time_t syncProvider()
> {
> //this does the same thing as RTC_DS1307::get()
> return RTC.now().unixtime();
> }


Yeah, does it do only once on setup()?


----------



## mistergreen

Ah, just found it... You can resync.



Code:


 setSyncInterval(interval);  // Set the number of
                               //  seconds between re-sync

so if you want to re-synce in a day



Code:


 setSyncInterval(60*60*24);  // Set the number of
                               //  seconds between re-sync

ps. put this in the setup() with syncProvider


----------



## Caver

mistergreen said:


> Ah, just found it... You can resync.
> 
> 
> 
> Code:
> 
> 
> setSyncInterval(interval);  // Set the number of
> //  seconds between re-sync
> 
> so if you want to re-synce in a day
> 
> 
> 
> Code:
> 
> 
> setSyncInterval(60*60*24);  // Set the number of
> //  seconds between re-sync


Wouldn't this do the same thing?


Code:


setSyncInterval(86400); //RTC sync interval in seconds

Or is there a reason you used 60*60*24 in the parenthesis?


----------



## mistergreen

Caver said:


> Wouldn't this do the same thing?
> 
> 
> Code:
> 
> 
> setSyncInterval(86400); //RTC sync interval in seconds
> 
> Or is there a reason you used 60*60*24 in the parenthesis?


yes, same thing. I didn't want to do the math and it's easy to read 

so if you want to do every 10 hours, 60*60*10.


----------



## Caver

OK...Cool!

I thought maybe I missed some programming protocol for setSyncInterval.

So would that statement go right after the "setSyncProvider(syncProvider);" in void setup ()?


----------



## mistergreen

Caver said:


> OK...Cool!
> 
> I thought maybe I missed some programming protocol for setSyncInterval.
> 
> So would that statement go right after the "setSyncProvider(syncProvider);" in void setup ()?


yup, looks like it.


----------



## Dahammer

mistergreen said:


> Yeah, does it do only once on setup()?


Yes, there is no need to call it more than once, which is done in setup() here, and there is no need to call setSyncInterval() at all unless you want to change the default 300 second (5 minute) sync interval.

The time library automatically resyncs the Arduino clock to the RTC every 5 minutes by default with the code as is. It checks nextSyncTime in the now() function to see if the interval has passed. now() is called by hour(), minute() and etc every time you call for a time.

*Time.cpp*


Code:


time_t now() {
  while (millis() - prevMillis >= 1000){      
    sysTime++;
    prevMillis += 1000;    
#ifdef TIME_DRIFT_INFO
    sysUnsyncedTime++; // this can be compared to the synced time to measure long term drift     
#endif
  }
[B]  if (nextSyncTime <= sysTime) {  // See if sync interval has passed (every 300 seconds by default)
    if (getTimePtr != 0) {        // Did the user set a pointer to their own function?
      time_t t = getTimePtr();    // Yes, so call their function to get the time
      if (t != 0) {               // Successfull?
        setTime(t);               // Yes, so go set the Arduino's time using this time[/B]
      } else {
        nextSyncTime = sysTime + syncInterval;
        Status = (Status == timeNotSet) ?  timeNotSet : timeNeedsSync;
      }
    }
  }  
  return (time_t)sysTime;
}

Our code calls setSyncProvider(), which sets getTimePtr to a pointer to our syncProvider() function, which returns a time from our RTC. So without any modifications, the Arduino's clock is sync'ed to the RTC's clock every 300 seconds by default. I'd think changing the sync interval from once every 5 minutes to once every 24 hours would make the clock less accurate, so I'd leave it alone.

So either there is something wrong with Caver's RTC, or he has removed the calls to hour(), minute(), and second() in SendCode() and loop(). If you don't fetch a time every so often, then it doesn't get sync'ed.


----------



## mistergreen

Ah, makes sense.

Carver, make sure you have the latest code. So some reason, you're not syncing to the RTC every 5 minutes?


----------



## Caver

I'll have to take a look. I may have accidentally hosed something...wouldn't be the first time...:hihi:

This is what I'm currently running...



Code:


///////////////////////////////////////////////////////////////////
//   Current Satellite LED+ Controller  V4.1c                    //
//   Indychus...Dahammer...mistergreen @ plantedtank.net         //
//   Clumsy mod to include dual channel thermometer by           //
//   Caver @ plantedtank.net with lots of help from the above.   //
//   This code is public domain.  Pass it on.                    //
//   Confirmed on Arduino UNO 1.0.5                              //
///////////////////////////////////////////////////////////////////
//
// This version uses Ken Shirriff's IRremote library to Rx/Tx the IR codes
// http://www.righto.com/2009/08/multi-protocol-infrared-remote-library.html
// 
// This code does NOT use PIN 13 on the Uno, as do previous versions
// Instead PIN 3, which is a PWM pin, is used. So you'll need to connect
// your LED to PIN 3 instead of PIN 13 for it to work.
//
// You can test the IR commands via the Arduino software's serial monitor
// by sending in a value from 1 - 32. Values follow the remote control, 
// left to right, top to bottom (e.g 1 = Orange, 2 = Blue, 21 = Moon1, etc)
//
// Install LCD per instructions at http://learn.adafruit.com/character-lcds/overview
//
#include <Wire.h>
#include <RTClib.h>
#include <Time.h>
#include <TimeAlarms.h>
#include <IRremote.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <LiquidCrystal.h>

RTC_DS1307 RTC;
IRsend irsend;

byte randAnalogPin = 0;   // This needs to be set to an unused Analog pin, Used by RandomStorm()
#define LCD_COLS 20      // Number of columns on the LCD (e.g. 16, 20, etc)
#define LCD_ROWS 4       // Number of rows on the LCD (e.g. 2, 4, etc)
LiquidCrystal lcd(10, 8, 7, 6, 5, 4);

// Data wire is plugged into pin 2 on the Arduino
#define ONE_WIRE_BUS 2

// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);

// Assign the addresses of your 1-Wire temp sensors.
// See the tutorial on how to obtain these addresses:
// http://www.hacktronics.com/Tutorials/arduino-1-wire-address-finder.html

DeviceAddress thermometer_1 = { 0x28, 0x98, 0x82, 0xA8, 0x04, 0x00, 0x00, 0x6D }; //Red
DeviceAddress thermometer_2 = { 0x28, 0xA6, 0x33, 0xA8, 0x04, 0x00, 0x00, 0x11 }; //blue

// Current Satellite+ IR Codes (NEC Protocol)
unsigned long codeHeader = 0x20DF; // Always the same
// Remote buttons listed left to right, top to bottom
PROGMEM unsigned int arrCodes[32] = {0x3AC5,  // 1 -  Orange
                                     0xBA45,  // 2 -  Blue
                                     0x827D,  // 3 -  Rose
                                     0x02FD,  // 4 -  Power On/Off
                                     0x1AE5,  // 5 -  White
                                     0x9A65,  // 6 -  FullSpec
                                     0xA25D,  // 7 -  Purple
                                     0x22DD,  // 8 -  Play/Pause
                                     0x2AD5,  // 9 -  Red Up
                                     0xAA55,  // 10 - Green Up
                                     0x926D,  // 11 - Blue Up
                                     0x12ED,  // 12 - White Up
                                     0x0AF5,  // 13 - Red Down
                                     0x8A75,  // 14 - Green Down
                                     0xB24D,  // 15 - Blue Down
                                     0x32CD,  // 16 - White Down
                                     0x38C7,  // 17 - M1 Custom
                                     0xB847,  // 18 - M2 Custom
                                     0x7887,  // 19 - M3 Custom
                                     0xF807,  // 20 - M4 Custom
                                     0x18E7,  // 21 - Moon 1
                                     0x9867,  // 22 - Moon 2
                                     0x58A7,  // 23 - Moon 3
                                     0xD827,  // 24 - Dawn/Dusk
                                     0x28D7,  // 25 - Cloud 1
                                     0xA857,  // 26 - Cloud 2
                                     0x6897,  // 27 - Cloud 3
                                     0xE817,  // 28 - Cloud 4
                                     0x08F7,  // 29 - Storm 1
                                     0x8877,  // 30 - Storm 2
                                     0x48B7,  // 31 - Storm 3
                                     0xC837}; // 32 - Storm 4

// These are the messages that print on the serial monitor & lcd when each IR code is sent
#define MAX_MSG_LEN 13  // Maximum length of the arrMSG messages
prog_char PROGMEM arrMSG[][MAX_MSG_LEN+1] = {"Orange",        "Blue",          "Rose",          "Power On/Off",
                                             "White",         "Full Spectrum", "Purple",        "Play/Pause",
                                             "Red Up",        "Green Up",      "Blue Up",       "White Up",
                                             "Red Down",      "Green Down",    "Blue Down",     "White Down",
                                             "Custom Mix 1",  "Custom Mix 2",  "Custom Mix 3",  "Custom Mix 4",
                                             "Moonlight 1",   "Moonlight 2",   "Moonlight 3",   "Dawn/Dusk",
                                             "Cloud Cover 1", "Cloud Cover 2", "Cloud Cover 3", "Cloud Cover 4",
                                             "Storm 1",       "Storm 2",       "Storm 3",       "Storm 4"};

void SetAlarms()
{
  // Set up your desired alarms here
  // The default value of dtNBR_ALARMS is 6 in TimeAlarms.h.
  // This code sets 9 alarms by default, so you'll need to change dtNBR_ALARMS to 9 or more
  // Changes the times to suit yourself. Add as many alarms as you like, just stay within dtNBR_ALARMS
  Alarm.alarmRepeat(13,00,0, PowerOnOff);
  Alarm.alarmRepeat(13,02,0, Moon1);
  Alarm.alarmRepeat(13,45,0, DawnDusk);
  Alarm.alarmRepeat(14,00,0, Cloud2);     // (HR,MIN,SEC,FUNCTION)
  Alarm.alarmRepeat(15,00,0, Cloud1);
  Alarm.alarmRepeat(17,00,0, FullSpec);
  Alarm.alarmRepeat(19,00,0, Cloud1);
  Alarm.alarmRepeat(20,00,0, Cloud2);
  Alarm.alarmRepeat(21,45,0, Moon3);
  Alarm.alarmRepeat(22,00,0, Moon1);
  Alarm.alarmRepeat(23,00,0, Moon2);
  Alarm.alarmRepeat(23,59,0, PowerOnOff);
  Alarm.timerRepeat(30, PrintTemp); // Time in seconds
  PrintTemp(); // Display initial temps
   
  // Comment these out if you don't want the chance of a random storm each day
  // Alarm.alarmRepeat(12,00,00, RandomStorm);
  // RandomStorm();  // Sets up intial storm so we don't have wait until alarm time
}

// Setup file structure & output function for stdout redirect
static FILE uartout = {0}; // create a FILE structure to reference our UART output function
static int uart_putchar (char c, FILE *stream)
{
  // Serial write fumction
  Serial.write(c);
  return 0;
}

// File structure & output function for lcd output
static FILE lcdout = {0} ;      // lcd FILE structure
static int lcd_putchar(char ch, FILE* stream)
{
  // lcd write function
  lcd.write(ch);
  return (0);
}

void setup()
{
  Wire.begin();
  RTC.begin();
  Serial.begin(9600);
  lcd.begin(LCD_COLS, LCD_ROWS);
  
  // Start up the library
sensors.begin();
// set the resolution to 10 bit (good enough?)

  sensors.setResolution(thermometer_1, 9);
  sensors.setResolution(thermometer_2, 9);

lcd.clear(); // start with a blank screen
  
  // fill in the UART file descriptor with pointer to writer.
  fdev_setup_stream (&uartout, uart_putchar, NULL, _FDEV_SETUP_WRITE);
  stdout = &uartout;  // Output stdout to UART
   
  // fill in lcd file descriptor (we'll use fprintf to output to it)
  fdev_setup_stream (&lcdout, lcd_putchar, NULL, _FDEV_SETUP_WRITE);
  
  if (! RTC.isrunning()) {
    // If no RTC is installed, set time to compile time at each reset
    printf_P(PSTR("RTC is NOT running!\n"));  // Store this string in PROGMEM
    RTC.adjust(DateTime(__DATE__, __TIME__));
    }
  
  setSyncProvider(syncProvider);     // reference our syncProvider function instead of RTC_DS1307::get()
  
  printf_P(PSTR("Time: %02d:%02d:%02d\n"), hour(), minute(), second());  // Print the time
  SetAlarms();  // Set up above alarms
  
  // Print available SRAM for debugging, comment out if you want
  printf_P(PSTR("SRAM: %d\n"), freeRam());
  
  printf_P(PSTR("To test IR codes, send 1 - 32\n"));
}

void loop()
{
    
  if (Serial.available() > 0) {
    delay(5); //Wait for transmission to finish
    TestCodes(SerialReadInt());  // Go grab IR code and send it
  }
  Alarm.delay(100);   // Service alarms & wait (msec)
  lcd.setCursor(7,0);
  fprintf(&lcdout, "%02d:%02d", hour(), minute());  // Print the time HH:MM to the lcd
}
time_t syncProvider()
{
  //this does the same thing as RTC_DS1307::get()
  return RTC.now().unixtime();
}

void RandomStorm ()
{ 
  // Schedules a storm between 1 & 8 in the evening
  // It sets Storm2, followed by Cloud2 or DawnDusk or Moon2, depending on when the storm is over
  randomSeed(analogRead(randAnalogPin));  // Generate random seed on unused pin
  byte RH = random(23);                   // Randomizer for RandomStorm
  byte RM = random(59);
  byte RS = random(59);
  byte TSDurationH = random(2);
  byte TSDurationM = random(59);
  
  lcd.setCursor(0,1);
  if (RH >= 13 && RH <= 21)
    { // If 1:00PM - 8:00PM schedule a storm
      Alarm.alarmOnce(RH,RM,RS,Storm2);
      
      // Store strings in PROGMEM
      printf_P(PSTR("Scheduled Storm: %02d:%02d:%02d Duration: %02d:%02d\n"), RH, RM, RS, TSDurationH, TSDurationM);
      fprintf_P(&lcdout, PSTR("Sch Storm: %02d:%02d"), RH, RM);
          
      if ((RH + TSDurationH) < 19)   // Switch to Cloud2 if storm ends between 1:00-6:59pm
        {
          Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,Cloud2);
        }
      else if ((RH + TSDurationH) < 21)  // Switch to DawnDusk if storm ends between 7:00-8:59pm
        {
          Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,DawnDusk);
        }
      else                               // Otherwise, switch to Night2
        {
          Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,Moon2);
        }
    }
    else
    { // Don't really need this, but it can stay till we need the space
      printf_P(PSTR("No storm today\n"));
      fprintf_P(&lcdout, PSTR("No storm today\n"));
    }
}
int SerialReadInt()
{
  // Reads first 2 bytes from serial monitor; anything more is tossed
  byte i;
  char inBytes[3];
  char * inBytesPtr = &inBytes[0];  // Pointer to first element
    
    for (i=0; i<2; i++)             // Only want first 2 bytes
      inBytes[i] = Serial.read();
    inBytes[i] =  '\0';             // Put NULL character at the end
    while (Serial.read() >= 0)      // If anything else is there, throw it away
      ; // do nothing      
    return atoi(inBytesPtr);        // Convert to decimal
}

void TestCodes (int cmd)
{
  // Handles commands sent in from the serial monitor
  if (cmd >= 1 && cmd <= 32)
    { 
      // cmd must be 1 - 32
      SendCode(cmd-1, 1);
    }
    else { printf_P(PSTR("Invalid Choice\n")); }
}

void SendCode ( int cmd, byte numTimes )
{ // cmd = the element of the arrCode[] array that holds the IR code to be sent
  // numTimes = number of times to emmit the command
  // Shift header 16 bits to left, fetch code from PROGMEM & add it
  unsigned long irCode = (codeHeader << 16) + pgm_read_word_near(arrCodes + cmd);
  for( byte i = 0; i < numTimes; i++)
  {
    irsend.sendNEC(irCode,32); // Send/emmit code
    delay(100);
  }
  // Print the string associated with the IR code & the time
  //printf("%S: 0x%lx %02d:%02d:%02d\n", arrMSG[cmd], irCode, hour(), minute(), second());
  printf("%S: %02d:%02d:%02d\n", arrMSG[cmd], hour(), minute(), second());
  if (LCD_COLS == 16)
    {
      lcd.setCursor(0,1);
      fprintf(&lcdout, "%-16S", arrMSG[cmd]);
    }
  else
    {
      lcd.setCursor(0,1);
      // lcd.setCursor(6,0);
      fprintf(&lcdout, "%-14S", arrMSG[cmd]);
    }
}

int freeRam ()
{
  // Returns available SRAM
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}

void PrintTemp()
{
  sensors.requestTemperatures();
  float tempC = sensors.getTempC(thermometer_1);
  lcd.setCursor(0,2);
  lcd.print("Tank 1: ");
  lcd.print(DallasTemperature::toFahrenheit(tempC), 1);
  lcd.print((char)223);
  
  tempC = sensors.getTempC(thermometer_2);
  lcd.setCursor(0,3);
  lcd.print("Tank 2: ");
  lcd.print(DallasTemperature::toFahrenheit(tempC), 1);
  lcd.print((char)223);
  
}

// IR Code functions, called by alarms
void Orange() {SendCode(0,2);}
void Blue() {SendCode(1,2);}
void Rose() {SendCode(2,2);}
void PowerOnOff() {SendCode(3,1);}
void White() {SendCode(4,2);}
void FullSpec() {SendCode(5,2);}
void Purple() {SendCode(6,2);}
void Play() {SendCode(7,1);}
void RedUp() {SendCode(8,1);}
void GreenUp() {SendCode(9,1);}
void BlueUp() {SendCode(10,1);}
void WhiteUp() {SendCode(11,1);}
void RedDown() {SendCode(12,1);}
void GreenDown() {SendCode(13,1);}
void BlueDown() {SendCode(14,1);}
void WhiteDown() {SendCode(15,1);}
void M1Custom() {SendCode(16,2);}
void M2Custom() {SendCode(17,2);}
void M3Custom() {SendCode(18,2);}
void M4Custom() {SendCode(19,2);}
void Moon1() {SendCode(20,2);}
void Moon2() {SendCode(21,2);}
void Moon3() {SendCode(22,2);}
void DawnDusk() {SendCode(23,2);}
void Cloud1() {SendCode(24,2);}
void Cloud2() {SendCode(25,2);}
void Cloud3() {SendCode(26,2);}
void Cloud4() {SendCode(27,2);}
void Storm1() {SendCode(28,2);}
void Storm2() {SendCode(29,2);}
void Storm3() {SendCode(30,2);}
void Storm4() {SendCode(31,2);}


----------



## mistergreen

debug time.. see if the sync function gets called besides the first time.


Code:


time_t syncProvider()
{
  //
  Serial.println("arduino:");
  Serial.print(hour());
  Serial.print(":");
  Serial.print(minute());
  //

 //
  Serial.println("RTC:");
  Serial.print(RTC.hour());
  Serial.print(":");
  Serial.print(RTC.minute());
  //

  //this does the same thing as RTC_DS1307::get()
  return RTC.now().unixtime();
}


----------



## Dahammer

Caver said:


> I have to hook the controller up to a PC and reset the clock every couple of weeks...it gains almost 10 minutes in that time frame


How are you resetting the clock? Does "RTC is NOT running!" print on the serial monitor when you reset the Arduino?


----------



## Caver

To reset the clock, I've been using the 'Set Time' sketch in the examples of the DS1307 RTC library.

I pull the controller, load & run the sketch, reload the light controller sketch and put the controller back in service.

Probably unnecessary...probably misunderstanding something...but it gets the controller back closer to actual time.

I don't recall ever seeing the RTC not running message. It's possible I never checked for that.

I just now pulled the controller to clean up some temporary wiring and I added the setSyncinterval command as we discussed earlier. I'll see how it runs for the next week or so.


----------



## Dahammer

Caver said:


> To reset the clock, I've been using the 'Set Time' sketch in the examples of the DS1307 RTC library.
> 
> I pull the controller, load & run the sketch, reload the light controller sketch and put the controller back in service.
> 
> Probably unnecessary...probably misunderstanding something...but it gets the controller back closer to actual time.
> 
> I don't recall ever seeing the RTC not running message. It's possible I never checked for that.
> 
> I just now pulled the controller to clean up some temporary wiring and I added the setSyncinterval command as we discussed earlier. I'll see how it runs for the next week or so.


The DS1307RTC library is a completely different library from the one we are using, RTClib. But yeah that sketch will set the RTC to the compile time.

Anyway, adding a setSyncinterval call isn't going to help you. There is something going on with your RTC. Here is a excerpt from serial monitor after adding some debugging:


Code:


Arduino sync'ed to RTC: 22:05:20
Time: 22:05:20
No storm today
SRAM: 893
To test IR codes, send 1 - 32
Arduino Time: 22:06:20
Arduino Time: 22:07:20
Arduino Time: 22:08:20
Arduino Time: 22:09:20
Arduino sync'ed to RTC: 22:10:21
Arduino Time: 22:10:21
Arduino Time: 22:11:21
Arduino Time: 22:12:21
Arduino Time: 22:13:21
Arduino Time: 22:14:21
Arduino sync'ed to RTC: 22:15:21
Arduino Time: 22:15:21
Arduino Time: 22:16:21
Arduino Time: 22:17:21
Arduino Time: 22:18:21
Arduino Time: 22:19:21
Arduino sync'ed to RTC: 22:20:21

As you can see, it's resyncing every 5 minutes as expected.


----------



## Dahammer

Here is the debug code I used. If you want to try it replace the loop() & syncprovider() functions with the ones below and add cseconds and pseconds to the global variables at the top somewhere.


Code:


unsigned int cseconds = 0;
unsigned int pseconds = 0;

void loop()
{
  cseconds = millis() / 1000;
  if (Serial.available() > 0) {
    delay(5); //Wait for transmission to finish
    TestCodes(SerialReadInt());  // Go grab IR code and send it
  }
  Alarm.delay(100);   // Service alarms & wait (msec)
  lcd.setCursor(0,0);
  fprintf(&lcdout, "%02d:%02d", hour(), minute());  // Print the time HH:MM to the lcd
  if (cseconds - pseconds >= 60)
  {
    printf("Arduino Time: %02d:%02d:%02d\n", hour(), minute(), second());
    pseconds = cseconds;
  }
}

time_t syncProvider()
{
  printf("Arduino sync'ed to RTC: %02d:%02d:%02d\n", RTC.now().hour(), RTC.now().minute(), RTC.now().second());
  return RTC.now().unixtime();
}

Calling hour() or minute() inside syncProvider() locks the Arduino up, so I moved the Arduino time to loop() and did a counter to display it every minute.


----------



## arkhamresident

Wow... I was looking to buy something like this for my LED + and stumbled upon this forum. I've never attempted anything like this before so I hope you guys don't mind a few questions _when_ I run into some issues.

I'm almost giddy.


----------



## Aquatechtoo

*RTC and Time Libraries*

I'm working from the start of the thread to build this controller and am running into an error with "millis" not being declared in the scope in the Time library when I attempt to verify the code in 1.0.5. Arduino-related forums say to make sure I'm running the latest version of that library, which I am, or that I can replace Wprogram.h with Arduino.h in the TimeAlarms.cpp file. Neither of these suggestions solved the problem like it did for the forum posters so I suspect I'm doing something wrong, procedurally.

However, my question is this: if I'm going to use an RTC instead of the software clock, do I need to worry about this error?


----------



## Indychus

Try changing the order of the #include commands at the beginning of the sketch. Sometimes that can fix issues with the "not declared" error. You shouldn't have to include Arduino.h since its always included, but it doesn't hurt.

Also, make sure that you've placed the libraries into the user library directory and not into the built-in library directory.



Sent from my HTC One X using Tapatalk


----------



## Dahammer

Which version of this code are you using? Post the exact error you are getting when you compile it.


----------



## Aquatechtoo

Upon further review, I'm certain there must be a dumb little detail that is incorrect based on how I'm managing the software, libraries, etc. I switched the order of the libraries being used under Setup in the code several times. Same errors. 

I did not realize that the libraries should be placed in the My Documents folder instead of the Libraries folder inside C:/Program Files(x86)/Arduino/Libraries - one of those small details that is good for me to learn. I moved (cut/paste) these 4 libraries (Time, DateTime, TimeAlarm, DateTimeSettings), along with the DS1307RTC library, into the My Documents folder. I ran the sketch, which is saved in the Arduino folder within My Documents, and observed the same resulting errors, attached.

I am using Arduino v1.0.5. The libraries are the newest versions available (to my knowledge) which I got here: http://playground.arduino.cc//Code/Time
The code I'm using is copied from Indychus' post #25 of this thread.
I changed the *int IRLedPin* from 13 to 3.
I changed the time/date values.
I changed *#define dtNBR_ALARMS* from 6 to 24.
I have NOT explored the Device ID's in the code yet to see how they compare to my values. I know they are different, but I was going to take that step after clearing up this verification error prior to uploading.


----------



## mistergreen

sounds more like version problem.
replace Wprogram.h" with "Arduino.h in the library


----------



## Aquatechtoo

This is what I thought too, but "Wprogram.h" was not found in any library, except "Time.cpp", in the Time library, in which I replaced it with "Arduino.h". It did not clear the error. As I originally mentioned, most of the forums I read pointed to that issue and replacing that in the header file seemed to solve the problem, which is why I can't figure out what I'm doing differently.


----------



## Aquatechtoo

*First Problem Solved?*

I started over and manually installed the libraries listed here: http://playground.arduino.cc/Code/Time
I noticed that there is no library listed for DateTime or DateTimeString in the .zip file. However, I was able to compile the code w/o problems. I opened the Serial Monitor and watched it keep track of the time. 

When I closed out of the IDE, added the DateTime and DateTimeString libraries in, opened the IDE and compiled the code, I got the errors again.


----------



## Dahammer

Aquatechtoo said:


> I started over and manually installed the libraries listed here: http://playground.arduino.cc/Code/Time
> I noticed that there is no library listed for DateTime or DateTimeString in the .zip file. However, I was able to compile the code w/o problems. I opened the Serial Monitor and watched it keep track of the time.
> 
> When I closed out of the IDE, added the DateTime and DateTimeString libraries in, opened the IDE and compiled the code, I got the errors again.


DateTime & DateTimeString are not used in the code. So just omit them. Best I recall, they were just remnants of some other code.

At any rate the version you have chosen does not support an RTC. That came along later in the development. You'll probably want to switch to one of the later versions to get support for the RTC. Also, at some point (post #148) we switched to using the IRremote library to send the commands and that drastically increased the IR range. There were lots of bugs along the way as well, so I'd probably either use the last one Indy posted or the last one I posted.


----------



## Aquatechtoo

Ok, Dahammer, Indy, et. al, thanks for the help so far. Progress is noted. I've been able to trigger the alarms from (7,00,0, Dawn/Dusk) to Full Spec (13,0,0) but I cannot get the lights to step back down at 15,00,0, 19,00,0, or 21,00,0. Any thoughts why that might be?

Dahammer, would you suggest just wiring the controller w/ the RTC (Chronodot 2.1) and LCD (16x2 Blue from Adafruit) and get all the hardware in place in the enclosure straightaway? And then use one of the final pieces of code (v4.X.X) you and Indy posted and troubleshoot from there, instead of adding a piece of hardware along with the corresponding code/libraries for each stop along the way?


----------



## Dahammer

Aquatechtoo said:


> Ok, Dahammer, Indy, et. al, thanks for the help so far. Progress is noted. I've been able to trigger the alarms from (7,00,0, Dawn/Dusk) to Full Spec (13,0,0) but I cannot get the lights to step back down at 15,00,0, 19,00,0, or 21,00,0. Any thoughts why that might be?
> 
> Dahammer, would you suggest just wiring the controller w/ the RTC (Chronodot 2.1) and LCD (16x2 Blue from Adafruit) and get all the hardware in place in the enclosure straightaway? And then use one of the final pieces of code (v4.X.X) you and Indy posted and troubleshoot from there, instead of adding a piece of hardware along with the corresponding code/libraries for each stop along the way?


I'd add the RTC at least. It doesn't matter about LCD. The later versions of code that support the LCD will work whether or not an LCD is present. Then I'd test each command to make certain that they all work. You can send in commands via the serial terminal with the later versions of the code. 

Here is the code I'm currently using. It has been running flawlessly for the last couple of months. It adds support for 2 thermometers, per code developed in this thread. But I do not have any thermometers on mine, therefore I have not tested that portion of this code and they are disabled by default in the code. It is also set up for a 16x2 LCD, since that's what I have on mine. Also notice that I have some of the alarms disabled, so enable them as you see fit.



Code:


///////////////////////////////////////////////////////////////////
// Current Satellite LED+ Controller  V4.1                       //
//   Indychus...Dahammer...mistergreen @ plantedtank.net         //
//   This code is public domain.  Pass it on.                    //
//   Confirmed on Arduino UNO 1.0.5                              //
//   Req. Time, TimeAlarms, RTClib, IRremote                     //
///////////////////////////////////////////////////////////////////
//
// This version uses Ken Shirriff's IRremote library to Rx/Tx the IR codes
// http://www.righto.com/2009/08/multi-protocol-infrared-remote-library.html
// 
// This code does NOT use PIN 13 on the Uno, as do previous versions
// Instead PIN 3, which is a PWM pin, is used. So you'll need to connect
// your LED to PIN 3 instead of PIN 13 for it to work.
//
// You can test the IR commands via the Arduino software's serial monitor
// by sending in a value from 1 - 32. Values follow the remote control, 
// left to right, top to bottom (e.g 1 = Orange, 2 = Blue, 21 = Moon1, etc)
//
// Install LCD per instructions at http://learn.adafruit.com/character-lcds/overview
//
// Added support for 2 Thermometers but it hasn't been tested
//
#include <Wire.h>
#include <RTClib.h>
#include <Time.h>
#include <TimeAlarms.h>
#include <IRremote.h>
#include <LiquidCrystal.h>
#include <OneWire.h>
#include <DallasTemperature.h>

RTC_DS1307 RTC;
IRsend irsend;

#define RANDPIN 0            // This needs to be set to an unused Analog pin, Used by RandomStorm()
static FILE uartout = {0};   // UART FILE structure
static FILE lcdout = {0} ;   // LCD FILE structure

//---------- LCD SETUP
#define LCD_COLS 16      // Number of columns on the LCD (e.g. 16, 20, etc)
#define LCD_ROWS 2       // Number of rows on the LCD (e.g. 2, 4, etc)
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);  // Arduino pins that the LCD is connected too

//---------- THERMOMETER SETUP
//#define ONE_WIRE_BUS 2                // Ardunio pin for thermometer; comment out if you don't have a thermometer
#ifdef ONE_WIRE_BUS
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
DeviceAddress thermometer_1 = { 0x28, 0x98, 0x82, 0xA8, 0x04, 0x00, 0x00, 0x6D }; // You'll need to change to match your sensor
DeviceAddress thermometer_2 = { 0x28, 0xA6, 0x33, 0xA8, 0x04, 0x00, 0x00, 0x11 }; // You'll need to change to match your sensor
#endif

// Current Satellite+ IR Codes (NEC Protocol)
unsigned long codeHeader = 0x20DF; // Always the same
// Remote buttons listed left to right, top to bottom
PROGMEM unsigned int arrCodes[32] = {0x3AC5,  // 1 -  Orange
                                     0xBA45,  // 2 -  Blue
                                     0x827D,  // 3 -  Rose
                                     0x02FD,  // 4 -  Power On/Off
                                     0x1AE5,  // 5 -  White
                                     0x9A65,  // 6 -  FullSpec
                                     0xA25D,  // 7 -  Purple
                                     0x22DD,  // 8 -  Play/Pause
                                     0x2AD5,  // 9 -  Red Up
                                     0xAA55,  // 10 - Green Up
                                     0x926D,  // 11 - Blue Up
                                     0x12ED,  // 12 - White Up
                                     0x0AF5,  // 13 - Red Down
                                     0x8A75,  // 14 - Green Down
                                     0xB24D,  // 15 - Blue Down
                                     0x32CD,  // 16 - White Down
                                     0x38C7,  // 17 - M1 Custom
                                     0xB847,  // 18 - M2 Custom
                                     0x7887,  // 19 - M3 Custom
                                     0xF807,  // 20 - M4 Custom
                                     0x18E7,  // 21 - Moon 1
                                     0x9867,  // 22 - Moon 2
                                     0x58A7,  // 23 - Moon 3
                                     0xD827,  // 24 - Dawn/Dusk
                                     0x28D7,  // 25 - Cloud 1
                                     0xA857,  // 26 - Cloud 2
                                     0x6897,  // 27 - Cloud 3
                                     0xE817,  // 28 - Cloud 4
                                     0x08F7,  // 29 - Storm 1
                                     0x8877,  // 30 - Storm 2
                                     0x48B7,  // 31 - Storm 3
                                     0xC837}; // 32 - Storm 4

// These are the messages that print on the serial monitor & lcd when each IR code is sent
#define MAX_MSG_LEN 13  // Maximum length of the arrMSG messages
prog_char PROGMEM arrMSG[][MAX_MSG_LEN+1] = {"Orange",        "Blue",          "Rose",          "On/Off",
                                             "White",         "Full Spec",     "Purple",        "Play/Pause",
                                             "Red Up",        "Green Up",      "Blue Up",       "White Up",
                                             "Red Down",      "Green Down",    "Blue Down",     "White Down",
                                             "Custom 1",      "Custom 2",      "Custom 3",      "Custom 4",
                                             "Moon 1",        "Moon 2",        "Moon 3",        "Dawn/Dusk",
                                             "Cloud 1",       "Cloud 2",       "Cloud 3",       "Cloud 4",
                                             "Storm 1",       "Storm 2",       "Storm 3",       "Storm 4"};

void SetAlarms()
{
  // Set up your desired alarms here
  // The default value of dtNBR_ALARMS is 6 in TimeAlarms.h.
  // This code sets 9 alarms by default, so you'll need to change dtNBR_ALARMS to 9 or more
  // Changes the times to suit yourself. Add as many alarms as you like, just stay within dtNBR_ALARMS
  Alarm.alarmRepeat(10,00,0, DawnDusk);
  //Alarm.alarmRepeat(7,30,0, Cloud2);     // (HR,MIN,SEC,FUNCTION)
  Alarm.alarmRepeat(11,00,0, FullSpec);
  //Alarm.alarmRepeat(18,00,0, Cloud2);
  Alarm.alarmRepeat(17,00,0, DawnDusk);
  Alarm.alarmRepeat(19,00,0, Moon2);
  
  // Comment these out if you don't want the chance of a random storm each day
  Alarm.alarmRepeat(12,00,00, RandomStorm);
  RandomStorm();  // Sets up intial storm so we don't have wait until alarm time

#ifdef ONE_WIRE_BUS  
  Alarm.timerRepeat(1800, PrintTemp);  // Print temps every 30 minutes
  PrintTemp();                         // Display initial temps
#endif
}

void setup()
{
  Wire.begin();
  RTC.begin();
  Serial.begin(9600);
  lcd.begin(LCD_COLS, LCD_ROWS);

#ifdef ONE_WIRE_BUS
  sensors.begin();   // Start temp sensors
  sensors.setResolution(thermometer_1, 9);  // Set them to 9 bit mode
  sensors.setResolution(thermometer_2, 9);
#endif

  // fill in the UART file descriptor with pointer to writer.
  fdev_setup_stream (&uartout, uart_putchar, NULL, _FDEV_SETUP_WRITE);
  stdout = &uartout;  // Output stdout to UART
   
  // fill in lcd file descriptor (we'll use fprintf to output to it)
  fdev_setup_stream (&lcdout, lcd_putchar, NULL, _FDEV_SETUP_WRITE);
  
  if (! RTC.isrunning()) {
    // If no RTC is installed, set time to compile time at each reset
    printf_P(PSTR("RTC is NOT running!\n"));  // Store this string in PROGMEM
    RTC.adjust(DateTime(__DATE__, __TIME__));
    }
  
  setSyncProvider(syncProvider);     // reference our syncProvider function instead of RTC_DS1307::get()
  
  printf_P(PSTR("Time: %02d:%02d:%02d\n"), hour(), minute(), second());  // Print the time
  SetAlarms();  // Set up above alarms
  
  // Print available SRAM for debugging, comment out if you want
  printf_P(PSTR("SRAM: %d\n"), freeRam());
  
  printf_P(PSTR("To test IR codes, send 1 - 32\n"));
}

void loop()
{
  if (Serial.available() > 0) {
    delay(5); //Wait for transmission to finish
    TestCodes(SerialReadInt());  // Go grab IR code and send it
  }
  Alarm.delay(100);   // Service alarms & wait (msec)
  lcd.setCursor(0,0);
  fprintf(&lcdout, "%02d:%02d", hour(), minute());  // Print the time HH:MM to the lcd
}

time_t syncProvider()
{
  //this does the same thing as RTC_DS1307::get()
  return RTC.now().unixtime();
}

void RandomStorm ()
{ 
  // Schedules a storm between 1 & 8 in the evening
  // It sets Storm2, followed by Cloud2 or DawnDusk or Moon2, depending on when the storm is over
  randomSeed(analogRead(RANDPIN));        // Generate random seed on unused pin
  byte RH = random(23);                   // Randomizer for RandomStorm
  byte RM = random(59);
  byte RS = random(59);
  byte TSDurationH = random(2);
  byte TSDurationM = random(59);
  
  lcd.setCursor(0,1);
  if (RH >= 13 && RH <= 21)
    { // If 1:00PM - 8:00PM schedule a storm
      Alarm.alarmOnce(RH,RM,RS,Storm2);
      
      // Store strings in PROGMEM
      printf_P(PSTR("Scheduled Storm: %02d:%02d:%02d Duration: %02d:%02d\n"), RH, RM, RS, TSDurationH, TSDurationM);
      fprintf_P(&lcdout, PSTR("Sch Storm: %02d:%02d"), RH, RM);
          
      if ((RH + TSDurationH) < 19)   // Switch to Cloud2 if storm ends between 1:00-6:59pm
        {
          Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,Cloud2);
        }
      else if ((RH + TSDurationH) < 21)  // Switch to DawnDusk if storm ends between 7:00-8:59pm
        {
          Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,DawnDusk);
        }
      else                               // Otherwise, switch to Night2
        {
          Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,Moon2);
        }
    }
    else
    { // Don't really need this, but it can stay till we need the space
      printf_P(PSTR("No storm today\n"));
      fprintf_P(&lcdout, PSTR("No storm today  "));
    }
}

int SerialReadInt()
{
  // Reads first 2 bytes from serial monitor; anything more is tossed
  byte i;
  char inBytes[3];
  char * inBytesPtr = &inBytes[0];  // Pointer to first element
    
    for (i=0; i<2; i++)             // Only want first 2 bytes
      inBytes[i] = Serial.read();
    inBytes[i] =  '\0';             // Put NULL character at the end
    while (Serial.read() >= 0)      // If anything else is there, throw it away
      ; // do nothing      
    return atoi(inBytesPtr);        // Convert to decimal
}

void TestCodes (int cmd)
{
  // Handles commands sent in from the serial monitor
  if (cmd >= 1 && cmd <= 32)
    { 
      // cmd must be 1 - 32
      SendCode(cmd-1, 1);
    }
#ifdef ONE_WIRE_BUS
    else if (cmd == 33)
    {
      PrintTemp();  // Send 33 to test temp sensors
    }
#endif
    else { printf_P(PSTR("Invalid Choice\n")); }
}

void SendCode ( int cmd, byte numTimes )
{ // cmd = the element of the arrCode[] array that holds the IR code to be sent
  // numTimes = number of times to emmit the command
  // Shift header 16 bits to left, fetch code from PROGMEM & add it with bitwise or
  unsigned long irCode = (codeHeader << 16) | pgm_read_word_near(arrCodes + cmd);
  for( byte i = 0; i < numTimes; i++)
  {
    irsend.sendNEC(irCode,32); // Send/emmit code
    delay(100);
  }
  // Print the string associated with the IR code & the time
  //printf("%S: 0x%lx %02d:%02d:%02d\n", arrMSG[cmd], irCode, hour(), minute(), second());
  printf("%S: %02d:%02d:%02d\n", arrMSG[cmd], hour(), minute(), second());

  lcd.setCursor(6,0);
  fprintf(&lcdout, "%-10S", arrMSG[cmd]);
}

int freeRam ()
{
  // Returns available SRAM
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}

// Output function for stdout redirect
static int uart_putchar (char c, FILE *stream)
{
  // Serial write fumction
  Serial.write(c);
  return 0;
}

// Output function for lcd output
static int lcd_putchar(char ch, FILE* stream)
{
  // lcd write function
  lcd.write(ch);
  return (0);
}

#ifdef ONE_WIRE_BUS
void PrintTemp()
{
  float tempC = sensors.getTempC(thermometer_1);
  lcd.setCursor(0,2);
  lcd.print("Tank 1: ");
  lcd.print(DallasTemperature::toFahrenheit(tempC), 1);
  lcd.print((char)223);
  
  tempC = sensors.getTempC(thermometer_2);
  lcd.setCursor(0,3);
  lcd.print("Tank 2: ");
  lcd.print(DallasTemperature::toFahrenheit(tempC), 1);
  lcd.print((char)223);
}
#endif

// IR Code functions, called by alarms
void Orange() {SendCode(0,2);}
void Blue() {SendCode(1,2);}
void Rose() {SendCode(2,2);}
void PowerOnOff() {SendCode(3,1);}
void White() {SendCode(4,2);}
void FullSpec() {SendCode(5,2);}
void Purple() {SendCode(6,2);}
void Play() {SendCode(7,1);}
void RedUp() {SendCode(8,1);}
void GreenUp() {SendCode(9,1);}
void BlueUp() {SendCode(10,1);}
void WhiteUp() {SendCode(11,1);}
void RedDown() {SendCode(12,1);}
void GreenDown() {SendCode(13,1);}
void BlueDown() {SendCode(14,1);}
void WhiteDown() {SendCode(15,1);}
void M1Custom() {SendCode(16,2);}
void M2Custom() {SendCode(17,2);}
void M3Custom() {SendCode(18,2);}
void M4Custom() {SendCode(19,2);}
void Moon1() {SendCode(20,2);}
void Moon2() {SendCode(21,2);}
void Moon3() {SendCode(22,2);}
void DawnDusk() {SendCode(23,2);}
void Cloud1() {SendCode(24,2);}
void Cloud2() {SendCode(25,2);}
void Cloud3() {SendCode(26,2);}
void Cloud4() {SendCode(27,2);}
void Storm1() {SendCode(28,2);}
void Storm2() {SendCode(29,2);}
void Storm3() {SendCode(30,2);}
void Storm4() {SendCode(31,2);}


----------



## Aquatechtoo

Thank you very much for your recommendations and sharing your code. I will get to work on this in the coming days/week and report back with celebrations and challenges.


----------



## Indychus

Did you change the number of alarms in the TimeAlarms.h file? See post 25. That could explain why only half of your alarms are working.

Sent from my HTC One X using Tapatalk


----------



## Aquatechtoo

Good call, Indy. 13,00,1 would be the 6th alarm, I believe. I had changed it to 24 yesterday, but I'm now wondering if I forgot to go back to that file since I wiped the libraries out and started over tonight. I'll check that tomorrow.


----------



## Christophe

Just saw this thread, cool work! I just set up my own Arduino controller for Current Satellite LED+ last week. I'm a software engineer by trade, so it's been pretty quick & easy.

For a long time I noticed how much fish jump when lights kick on/off if they're just on a timer. I was mostly interested in natural, slow transitions from lights off to full on and back over the course of the day, and in controlling photoperiod for plants. The LED+ has been great for that on my moderately planted, no CO2 40g breeder, but I hate having to manually mess with it via the remote control. I'm also a little leery of the fact that that wispy little remote is the only way to control it.

My program uses a couple of sine functions to control the light levels, one for RGB, and one for the white lights. They're fed time from a DS1307 RTC. The user provides lights-on time, lights-off time, and amplitude, which controls both how quickly it ramps, and the percentage of the photoperiod spent at full on. It's also tunable to provide multiple photoperiods in a day, though I don't see much reason to do more than two per day.

I'll keep watching this thread, let me know if there's something I can contribute!


----------



## Guest

*Will the program work without the IR receiver?*

Hi,

I like the project - thanks for sharing. 

Will the program work without the IR receiver? 

Thanks!


----------



## Aquatechtoo

*LCD Issue*

After much wrestling with the ChronoDot RTC, I was able to finally isolate the compatibility issue thanks to the good folks in the Adafruit forums. I have the LCD wired up but it isn't displaying any output. I double checked the wiring from the tutorial Indy suggested: http://learn.adafruit.com/character-lcds/wiring-a-character-lcd but I'm not getting a reading from the LED controller code or from the example sketch in the LCD library. I'm wondering if this could be related to the fact that I didn't solder the headers onto the display yet? I want to prove that things are wired up and working correctly before making permanent connections and installing the boards in a chassis. For what its worth, the display lights up and the potentiometer is working so the contrast and brightness must be fine. I'm just not getting any print out on the display.

I'm using a 16x2 display and Dahammer's code, above.


----------



## Aquatechtoo

*Storm Length and Chance*

I got the LCD working - soldering on the headers was the answer. The controller has been running fine for a few hours so far. We've experienced our first storm already this evening!

I have attempted to reduce the max length of the storm to 45 min. Can you tell me if I changed the code correctly for this?

randomSeed(analogRead(RANDPIN)); // Generate random seed on unused pin
byte RH = random(23); // Randomizer for RandomStorm
byte RM = random(59);
byte RS = random(59);
byte TSDurationH = random(0);
byte TSDurationM = random(45);

Also, if I want to reduce the chances of a storm on a given day, where would I change this in the code?


----------



## Dahammer

Aquatechtoo said:


> I got the LCD working - soldering on the headers was the answer. The controller has been running fine for a few hours so far. We've experienced our first storm already this evening!
> 
> I have attempted to reduce the max length of the storm to 45 min. Can you tell me if I changed the code correctly for this?
> 
> randomSeed(analogRead(RANDPIN)); // Generate random seed on unused pin
> byte RH = random(23); // Randomizer for RandomStorm
> byte RM = random(59);
> byte RS = random(59);
> byte TSDurationH = random(0);
> byte TSDurationM = random(45);


Yeah, that should work fine. Basically all it does it set a new alarm that changes to a non-storm mode after TSDurationH/M.



Aquatechtoo said:


> Also, if I want to reduce the chances of a storm on a given day, where would I change this in the code?


There is no built in way to do this. You'd need to write your own code to do what you wanted. The codes sets an alarm that calls the RandomStorm routine every day at 12:00. You could either change it to where it didn't call it every day or add code to the RandomStorm routine that just kicks it out on certain days, or add code that decreases the odds on certain days. You could decrease the odds by narrowing the window where they are allowed (only between 1 & 8 PM by default) or maybe run the randomizer more than once or some other way.


----------



## Aquatechtoo

Cool, thank you. It seems like there are a number of ways I can manipulate small pieces of the existing code without too much trouble, such as several options you mentioned. Now that I read through the RandomStorm routine, it makes more sense with your explanation. This whole project has opened up a number of things that Arduino controllers can solve for me outside of aquarium lighting that I never considered! 

My hat's off to you, Indy, and the rest of the forum collaborators on this solution. It was probably a little cheaper than buying a commercial solution and much more educational! Your collective approach to something which started with Indy asking a seemingly innocuous question that grew into an open DIY solution is the way learning and collaborating ought to be approached more frequently. Instead of the siloed, departmentalized general business model created under the guise of maximizing profits, you all decided to share knowledge in a way that doesn't thwart creativity. It is a great example for the rest of us to follow! Soapbox = OFF.


----------



## Texan78

Well it took me several hours but I read through all 25 pages of this thread and enjoyed every page. A great bunch of knowledgable folks on this project. I have a AI SOLS on my Apex controller for my reef tank. I wanted something along the same lines for my planted tank made for freshwater but wasn't about to spend that same amount of money I did for the reef tank for lights not built for a planted tank. After a lot of research I found the LED Plus+ and started doing some homework to see how I could have it do what the AI lights do. That's when I found this tread and I said ok I am sold lets get them. So I ordered two lights today and hopefully will have them no later than Thursday. Now just need to go get the goodies to build this box. I just have a couple of questions and ideas I want to pass by you guys if you don't mind. 

1. If I understand this correctly the thunderstorms are entirely random correct?

2. The way I was hoping to set it up was morning, dusk/dawn mode, early/mid morning light, full spectrum for a few hours, afternoon light, then dusk/dawn mode again into moonlights for the night until the cycle starts over. Then randomly sprinkled in there both day or night rolling clouds and thunderstorms. Is that possible?

3. I have been doing some more research and homework on this. A really good base has been made on this and I thank those who have put a lot of time into this. I see it started way back in July. Now while it makes sense to use IR since the light excepts IR. I started thinking because I like clean installs under my cabinet or maybe hidden away in my office. I know no one will see it but I am OCD like that. What about changing out the IR for RF? Now hear me out first as it's just an idea. In theory it sounds good but may not be possible so wanted to ask. No modification to the light is needed. If anyone is familiar with Home Theater systems and the Logitech Harmony remotes. They have a RF blaster. The remotes can send IR or RF. If you use RF you need the blaster which the remote sends a signal to the box and then the box transmitters to a emitter connected to it a IR signal. It's mainly for those who hide their devices in cabinets. The idea I had uses the same premis. If they make a RF shield for the UNO, which I see they do make RF shields, not sure if it works with the UNO. You could add it to transmit a RF signal to a small box at the tank with a RF signal so the box could stay connected at the computer. The box at the tank would hold a RF Receiver with the IR Receiver from the tank in it. The RF Receiver would trigger the IR connected to it in the box. So it would be a very small footprint and could be hidden away under your cabinet with velcro or however you chose and the UNO could stay plugged into the computer who hidden somewhere else. Just an idea, would this be do-able? If not, I have a WiFi solution too. 

4. Do they make any Audio shields for the UNO that has RCA jacks or shields with speakers on it? I think you see where I am going with this. I am not familiar with the UNO programing and I am sure with the memory size of the UNO but if you could have small wave files of thunder to trigger with the storms it would be a cool effect. Of course this would mainly just be ear candy to go along with the eye candy, but it could have its advantages if you can hear a storm and you're in another room you know triggers are being fired as they should. Just another idea. 

If this is posible I think I am going to try to do it. I am good with hardware just not familiar with the Arduino stuff or software coding. I have an extensive background in Networking and web based coding and the languages use the same logic just in different formats. I am really interested in the RF/IR blaster box and if that doesn't work then I am sure I can come up with a WiFi solution with the research I have done so far. 

Any input is greatly appreciated and can't wait to get my light and start playing. 

-Thanks!


----------



## zodduska

First off thank you for making this easily accessible to newbies like myself!

I've got my parts on the way, the Arduino, breadboard and LCD arrived yesterday so I hooked them up and loaded the latest code.

I went with the Generic 4x20 LCD from Amazon for $9 and it seems a little different since it has a backpack to reduce the amount of pins needed to connect to the Arduino, I found some code on an Amazon review to get it going which worked fine to print "Hello World"

I tried to replace the correct parts in your code with the one for this LCD but it doesn't light up when running it, I'm not sure if that's because I don't have any of the other parts yet would cause the code to stall before initializing the LCD but I figured I'd post it here to see if anyone could tell me if it looks right or not to get a head start on fixing it if needed.

I also had to use a different LCD library to make it work with the backpack
LiquidCrystal_V1.2.1.zip

The Amazon LCD code



Code:


#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>

#define I2C_ADDR 0x27
#define BACKLIGHT_PIN 3
#define En_pin 2
#define Rw_pin 1
#define Rs_pin 0
#define D4_pin 4
#define D5_pin 5
#define D6_pin 6
#define D7_pin 7

LiquidCrystal_I2C lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin);

void setup()
{
lcd.begin (20,4,LCD_5x8DOTS);
lcd.setBacklightPin(BACKLIGHT_PIN,POSITIVE);
}

void loop()
{
lcd.setBacklight(HIGH);
lcd.home ();
lcd.print("HELLO WORLD");
}

(incorrectly?)Modified 3.6



Code:


///////////////////////////////////////////////////////////////////
// Current Satellite LED+ Controller  V3.6                       //
//   Indychus...Dahammer...mistergreen @ plantedtank.net         //
//   This code is public domain.  Pass it on.                    //
//   Confirmed on Arduino UNO 1.0.5                              //
//   Req. Time, TimeAlarms, RTClib, IRremote                     //
///////////////////////////////////////////////////////////////////
//
// This version uses Ken Shirriff's IRremote library to Rx/Tx the IR codes
// http://www.righto.com/2009/08/multi-protocol-infrared-remote-library.html
// 
// This code does NOT use PIN 13 on the Uno, as do previous versions
// Instead PIN 3, which is a PWM pin, is used. So you'll need to connect
// your LED to PIN 3 instead of PIN 13 for it to work.

// Install LCD per instructions at http://learn.adafruit.com/character-lcds/overview

#include <Wire.h>
#include <RTClib.h>
#include <Time.h>
#include <TimeAlarms.h>
#include <IRremote.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>

#define I2C_ADDR 0x27
#define BACKLIGHT_PIN 3
#define En_pin 2
#define Rw_pin 1
#define Rs_pin 0
#define D4_pin 4
#define D5_pin 5
#define D6_pin 6
#define D7_pin 7

RTC_DS1307 RTC;
IRsend irsend;
LiquidCrystal_I2C lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin);

/*********** BEGIN USER DEFINED VARIABLES ***********/
// DEBUG_IR adds the option to test the IR commands via the Arduino software's serial monitor
// You can send any value from 1 to 32 and it send the corresponding IR code
// The codes follow the remote controller, left to right, top to bottom
// e.g 1 = Orange, 2 = Blue, 21 = Moon1, etc
#define DEBUG_IR


int postDelay = 100;         // Delay after codes are sent
int randAnalogPin = 0;       // This needs to be set to an unused Analog pin, Used by ThunderStorm()

// Current Satellite+ IR Codes (NEC Protocol)
unsigned long codeHeader = 0x20DF; // Always the same

// Remote buttons listed left to right, top to bottom
unsigned int codeOrange = 0x3AC5;
unsigned int codeBlue = 0xBA45;
unsigned int codeRose = 0x827D;
unsigned int codePowerOnOff = 0x02FD;
unsigned int codeWhite = 0x1AE5;
unsigned int codeFullSpec = 0x9A65;
unsigned int codePurple = 0xA25D;
unsigned int codePlay = 0x22DD;
unsigned int codeRedUp = 0x2AD5;
unsigned int codeGreenUp = 0xAA55;
unsigned int codeBlueUp = 0x926D;
unsigned int codeWhiteUp = 0x12ED;
unsigned int codeRedDown = 0x0AF5;
unsigned int codeGreenDown = 0x8A75;
unsigned int codeBlueDown = 0xB24D;
unsigned int codeWhiteDown = 0x32CD;
unsigned int codeM1Custom = 0x38C7;
unsigned int codeM2Custom = 0xB847;
unsigned int codeM3Custom = 0x7887;
unsigned int codeM4Custom = 0xF807;
unsigned int codeMoon1 = 0x18E7;
unsigned int codeMoon2 = 0x9867;
unsigned int codeMoon3 = 0x58A7;
unsigned int codeDawnDusk = 0xD827;
unsigned int codeCloud1 = 0x28D7;
unsigned int codeCloud2 = 0xA857;
unsigned int codeCloud3 = 0x6897;
unsigned int codeCloud4 = 0xE817;
unsigned int codeStorm1 = 0x08F7;
unsigned int codeStorm2 = 0x8877;
unsigned int codeStorm3 = 0x48B7;
unsigned int codeStorm4 = 0xC837;

void SetAlarms()
{
  // Set up your desired alarms here
  // The default value of dtNBR_ALARMS is 6 in Alarms.h.
  // This code sets 12 alarms by default, so you'll need to change dtNBR_ALARMS to 12 or more
  Alarm.alarmRepeat(7,00,0, DawnDusk);
  Alarm.alarmRepeat(9,00,0, Cloud2);     // (HR,MIN,SEC,FUNCTION)
  Alarm.alarmRepeat(11,00,0, FullSpec);
  Alarm.alarmRepeat(15,00,0, Cloud2);
  Alarm.alarmRepeat(19,00,0, DawnDusk);
  Alarm.alarmRepeat(21,00,0, Moon2);
  
  // Comment these out if you don't want the chance of a random storm each day
  Alarm.alarmRepeat(12,00,00, ThunderStorm);
  ThunderStorm();  // Sets up intial storm so we don't have wait until alarm time
}
/************* END USER DEFINED VARIABLES *************/

void setup()
{
  Wire.begin();
  RTC.begin();
  lcd.begin(20,4,LCD_5x8DOTS);
  lcd.setBacklightPin(BACKLIGHT_PIN,POSITIVE);
  Serial.begin(9600);
      //Serial.println(freeRam());
  
  if (! RTC.isrunning()) { 
    Serial.println("RTC Error");
    RTC.adjust(DateTime(__DATE__, __TIME__));}  //Adjust to compile time
    
  
  setSyncProvider(syncProvider);     //reference our syncProvider function instead of RTC_DS1307::get()
  
  Alarm.timerRepeat(900, digitalClockDisplay);  // Display the time every 15 minutes
  digitalClockDisplay();
  SetAlarms();
 Serial.print("SRAM : ");          //un-comment these line to check available SRAM
 Serial.println(freeRam());}   

void loop()
{
#ifdef DEBUG_IR 
  if (Serial.available() > 0) {
    delay(5); //Wait for transmission to finish
    CurrentCMDs(SerialReadInt());
  }
#endif
  Alarm.delay(100); 
  // Service alarms & wait (msec)
  lcdClockDisplay();
}

time_t syncProvider()
{
  //this does the same thing as RTC_DS1307::get()
  return RTC.now().unixtime();
}

void ThunderStorm ()
{ 
  // Schedules a storm between 1 & 9 in the evening
  // It sets Storm2, followed by Cloud2 or DawnDusk or Moon2, depending on when the storm is over
  randomSeed(analogRead(randAnalogPin));  // Generate random seed on unused pin
  byte RH = random(23);                   // Randomizer for thunderstorm
  byte RM = random(59);
  byte RS = random(59);
  byte TSDurationH = random(2);
  byte TSDurationM = random(59);
  
  if (RH <= 12)
    {
      Serial.println("No storm today");
      lcd.setCursor(0,1);
      lcd.print("No storm today");
      return;
    }
      
  if (RH > 12)                             // If random value is after 1 pm, allow storm
    {
      Alarm.alarmOnce(RH,RM,RS,Storm2);
      Serial.print("Next Storm: ");
      Serial.print(RH);
      printDigits(RM);
      printDigits(RS);
      Serial.print("   ");
      Serial.print("Duration = ");
      Serial.print(TSDurationH);
      printDigits(TSDurationM);
      Serial.println();
     lcd.setCursor(0,1);
     lcd.print("Next Storm: ");
     lcdHRdigits(RH);
     lcdDigits(RM);}
      
      if ((RH + TSDurationH) < 19)   // Return to Cloud2 if storm ends between 1-7pm
        {Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,Cloud2);}
      else if ((RH + TSDurationH) < 21)  // Return to DawnDusk if storm ends between 7-9pm
        {Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,DawnDusk);}
      else                                       // Return to Night2 if storm ends after 9pm
        {Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,Moon2);}
    }


void digitalClockDisplay()          // Digital clock
{ 
  Serial.print("Time = ");
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.println(); }

  
void lcdClockDisplay()  
  {lcd.setCursor(0,0);
    lcdHRdigits(hour());
  lcdDigits(minute());}

void printDigits(int digits)        // Add :
{
  Serial.print(":");
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits);
}

void lcdDigits(int digits)        // Add :
{
  lcd.print(":");
  if(digits < 10)
    lcd.print('0');  
  lcd.print(digits);
}
void lcdHRdigits(int HRdigits)        // Preface hour with 0
{
  if(HRdigits < 10)
    lcd.print('0');  
  lcd.print(HRdigits);
}

#ifdef DEBUG_IR
int SerialReadInt()
{
  int i, numAva;
  char inBytes[3];                  // Array to hold the bytes
  char * inBytesPtr = &inBytes[0];  // Pointer to the first element of the array
      
    numAva = Serial.available();    // Read number of input bytes
    if (numAva > 2)
      numAva = 2;                   // Only allow 2 characters to prevent overflow
      
    for (i=0; i<numAva; i++)        // Load input bytes into array
      inBytes[i] = Serial.read();
    inBytes[i] =  '\0';             // Put NULL character at the end
    return atoi(inBytesPtr);        // Call atoi function and return result
}

void CurrentCMDs (int cmd)
{switch (cmd)
  {
    case 1:
      Orange();
      break;
    case 2:
      Blue();
      break;
    case 3:
      Rose();
      break;
    case 4:
      PowerOnOff();
      break;
    case 5:
      White();
      break;
    case 6:
      FullSpec();
      break;
    case 7:
      Purple();
      break;
    case 8:
      Play();
      break;
    case 9:
      RedUp();
      break;
    case 10:
      GreenUp();
      break;
    case 11:
      BlueUp();
      break;
    case 12:
      WhiteUp();
      break;
    case 13:
      RedDown();
      break;
    case 14:
      GreenDown();
      break;
    case 15:
      BlueDown();
      break;
    case 16:
      WhiteDown();
      break;
    case 17:
      M1Custom();
      break;
    case 18:
      M2Custom();
      break;
    case 19:
      M3Custom();
      break;
    case 20:
      M4Custom();
      break;
    case 21:
      Moon1();
      break;
    case 22:
      Moon2();
      break;
    case 23:
      Moon3();
      break;
    case 24:
      DawnDusk();
      break;
    case 25:
      Cloud1();
      break;
    case 26:
      Cloud2();
      break;
    case 27:
      Cloud3();
      break;
    case 28:
      Cloud4();
      break;
    case 29:
      Storm1();
      break;
    case 30:
      Storm2();
      break;
    case 31:
      Storm3();
      break;
    case 32:
      Storm4();
      break;
    default:
      Serial.println("Invalid Choice");}}
#endif

void SendCode (unsigned int code, byte numTimes, const char *sMessage)
{unsigned long irCode = (codeHeader << 16) + code; // Header is 2 bytes, shift all the way to left & add code to it
  
  for( int i = 0; i < numTimes; i++)
  {irsend.sendNEC(irCode,32); // Send code
    Alarm.delay(postDelay);}
    
  Serial.println(sMessage);  // Print message
  lcd.setCursor(6,0);
  lcd.print(sMessage);
  for(byte i = sizeof(sMessage); i <= 14; i++)
    lcd.print(" ");
  digitalClockDisplay();}

void Orange()
{SendCode(codeOrange, 2, "Orange");}

void Blue()
{SendCode(codeBlue, 2, "Blue");}

void Rose()
{SendCode(codeRose, 2, "Rose");}

void PowerOnOff()
{SendCode(codePowerOnOff, 1, "Power On/Off");}

void White()
{SendCode(codeWhite, 2, "White");}

void FullSpec()
{SendCode(codeFullSpec, 2, "Full Spectrum");}

void Purple()
{SendCode(codePurple, 2, "Purple");}

void Play()
{SendCode(codePlay, 1, "Play/Pause:");}

void RedUp()
{SendCode(codeRedUp, 1, "Red Up");}

void GreenUp()
{SendCode(codeGreenUp, 1, "Green Up");}

void BlueUp()
{SendCode(codeBlueUp, 1, "Blue");}

void WhiteUp()
{SendCode(codeWhiteUp, 1, "White Up");}

void RedDown()
{SendCode(codeRedDown, 1, "Red Down");}

void GreenDown()
{SendCode(codeGreenDown, 1, "Green Down");}

void BlueDown()
{SendCode(codeBlueDown, 1, "Blue Down");}

void WhiteDown()
{SendCode(codeWhite, 1, "White Down");}

void M1Custom()
{SendCode(codeM1Custom, 2, "Custom Mix 1");}

void M2Custom()
{SendCode(codeM2Custom, 2, "Custom Mix 2");}

void M3Custom()
{SendCode(codeM3Custom, 2, "Custom Mix 3");}

void M4Custom()
{SendCode(codeM4Custom, 2, "Custom Mix 4");}

void Moon1()
{SendCode(codeMoon1, 2, "Moonlight 1");}

void Moon2()
{SendCode(codeMoon2, 2, "Moonlight 2");}

void Moon3()
{SendCode(codeMoon3, 2, "Moonlight 3");}

void DawnDusk()
{SendCode(codeDawnDusk, 2, "Dawn/Dusk");}

void Cloud1()
{SendCode(codeCloud1, 2, "Cloud Cover 1");}

void Cloud2()
{SendCode(codeCloud2, 2, "Cloud Cover 2");}
  
void Cloud3()
{SendCode(codeCloud3, 2, "Cloud Cover 3");}

void Cloud4()
{SendCode(codeCloud4, 2, "Cloud Cover 4");}

void Storm1()
{SendCode(codeStorm1, 2, "Thunderstorm 1");}

void Storm2()
{SendCode(codeStorm2, 2, "Thunderstorm 2");}

void Storm3()
{SendCode(codeStorm3, 2, "Thunderstorm 3");}

void Storm4()
{SendCode(codeStorm4, 2, "Thunderstorm 4");}

int freeRam () {
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}

I'd really appreciate any advice as to whether or not this looks right.

edit: sorry if this is a stupid question, I'm very inexperienced with coding. I also noticed the newer versions as I've just finished reading the whole thread.


----------



## zodduska

Got it running without the LCD for now, very cool! I'll desolder the LCD from the back pack tomorrow.


----------



## zodduska

Ended up just snapping off the LCD backpack after an hour of failing to completely desolder it. 

If anyone wants very dim moonlights, what I did is turn all the channels down manually then up one notch for each of the RGB and save it to a Custom preset. You could also sacrifice one of the Custom presets for a non-toggle "off" setting by turning all the channels down and saving it.


----------



## Indychus

Looking good man!

Sent from my HTC One X using Tapatalk


----------



## salman

Really nice work. I'll try this since i just got my satellite+ yesterday. @Indychus all of your images are not displaying for some reason in the thread. Were those deleted? thanks.


----------



## zodduska

Thanks, I had a ton of fun putting it together and figuring out how everything works. I've got a smoke Arduino box and PSU from Adafruit on the way so I can clean it up and solder it to a pcb. 

One thing I don't really understand though, what's the purpose of the IR receiver? To capture codes if they were to be different from the original Current ones? If I was following the thread correctly it seems one of the pins it was connected to ended up being taken over by another component.


----------



## Indychus

Not sure why some pics are missing, but I'll check into it. 

The receiver is not necessary unless you have different IR codes. Current had said they were changing their protocol, but as of now they have not that I'm aware of.

Sent from my HTC One X using Tapatalk


----------



## Texan78

I got mine yesterday as well so I will probably start building one starting tomorrow if all the parts are local. Otherwise I will need to order them all. 

Also waiting on some feed back on my previous post before I move forward.

http://www.plantedtank.net/forums/showpost.php?p=4704490&postcount=364

-Thanks!


----------



## Aquatechtoo

zodduska said:


> Thanks, I had a ton of fun putting it together and figuring out how everything works. I've got a smoke Arduino box and PSU from Adafruit on the way so I can clean it up and solder it to a pcb.
> 
> One thing I don't really understand though, what's the purpose of the IR receiver? To capture codes if they were to be different from the original Current ones? If I was following the thread correctly it seems one of the pins it was connected to ended up being taken over by another component.


Zodduska, how did you get the Custom settings into the controller? Didn't you need to use the IR receiver for that?


----------



## Aquatechtoo

Texan78, I'm not the best one to answer your questions, but if I understand it correctly, yes, the storms are random. However, RandomStorm will only be called between the hours of 1:00 PM and 8:00 PM, based on how these guys wrote the program.

I'm sure you can mix this in, but I wouldn't know how to do it. I think it would take some re-writing to include 2 codes in memory for that random function. Expanding the time the RandomStorm function would be called should be as easy as allowing it to be called earlier than 13:00.

I've thought of using sound too, and I think you can use an Arduino board to process sound w/ RCA cables, but I have not looked up RF options. One question I would have is how much memory the Uno has free if the same board is managing the lights and sound. I don't think we're close to the max with just the lights, at least as v4.1 is written, but I don't know how much mem is left. I like the idea, too, but I'm not sure if this board could process both signals?

I know this isn't much help, but hopefully I'm not giving you any wrong information. If I am, I'm sure someone will jump in and correct me.


----------



## Texan78

Thanks Aquatechtoo that was a big help. I guess the same thing could be wrote for random cloud cover too like with the thunderstorms? I would really like to have a nice random mix to mimic actual weather so I will learn how this works and hopefully see if I can mix that in with the thunderstorms. Don't think it should be to hard. 

The sound option I figured would be stretching the memory. Only need like a very small like 2-3 sec wav file. If this is feasible I may get a Mega. I just picked up the Uno. I haven't played with this code before or yet but I don't see why it couldn't be possible to set a trigger based on the storm to trigger the .wav file. Only issue I see would be having the lights and sound synced. Then again I don't think it would matter ether way if it's synced or not. Not always do you hear thunder when you see lightning and vice versa. Hooking speakers up is the easy part. 

I picked up my Uno just now and the resistor but this rat shack didn't have the IR emmitters. So I will need to look at some other places and if they don't have them order them when I order the LCD and box to enclose this all in. At the same time I am going to get the RF parts and experiment with that. I have been thinking about it for a few days and think I could get it to work. Only thing I need to sort out is powering the RF receiver box. I think a simple small battery like the RTC uses should work. 

My logic is to have a small black box that has a small PCB in it just big enough to connect the RF receiver and an IR emitter in it. When the RF receives a signal from the Uno it will fire the IR emitter in the box which will also have the IR receivers for the lights in it. RF has better range than IR and will allow you to conceal the boxes and make changing the programing easier. Here is a small example on this page. As you can see when the Key FOB is pressed it lights a LED, which in our case would be an IR emitter would fire instead to trigger the light. This site has a kit that works with the Uno so this is something I am really going to dig into. This could really make this setup versatile. This is just in theory. Not sure if this would actually work.

http://www.karlssonrobotics.com/cart/rf-link-transmitter-315mhz/?gclid=CMHo0oy9-boCFRFo7AodywIALA

Example 

http://www.adafruit.com/products/1096


----------



## zodduska

Cool ideas! 



Aquatechtoo said:


> Zodduska, how did you get the Custom settings into the controller? Didn't you need to use the IR receiver for that?



No receiver needed. Just save the preset you want on the regular remote by holding the custom button. When setting up the alarms these are what you'd put for custom functions:

M1Custom
M2Custom
M3Custom
M4Custom

All of the remote buttons are defined at the bottom of the code too. This is with 4.1


----------



## Aquatechtoo

Perhaps you can write a similar random function for clouds like was done for the storm and then use a control structure like If/Else to pull them together!? Just a shot in the dark on my part; object-oriented programming is NOT a strength of mine.

Good luck with the sound, too. Keep us posted. I think it would be a more natural experience if the sound and lighting aren't in sync with each other. This concept also reminded me of Xmas lights syncing to music and a quick search reveled this: http://forum.arduino.cc/index.php?topic=87956.0 and while it isn't exactly what you are thinking about, it could serve as inspiration as you start devising potential solutions? This is a case where the lights and sound would fire in concert with each other, but if you could take this concept and offset the lights and sound by a second or two, it might take this idea of storms over a planted tank to a "4D" level!?


----------



## zodduska

It seems like the dynamic fade functions are all pretty simple loops on the LED+, you could time lightning strikes pretty easily and add the right amount of delay(s) for the audio to sync then have it repeat until the random storm duration is up. Though I've no idea how to code it.


----------



## Aquatechtoo

zodduska said:


> Cool ideas!
> 
> 
> No receiver needed. Just save the preset you want on the regular remote by holding the custom button. When setting up the alarms these are what you'd put for custom functions:
> 
> M1Custom
> M2Custom
> M3Custom
> M4Custom
> 
> All of the remote buttons are defined at the bottom of the code too. This is with 4.1


Oh, yeah - that's pretty obvious now that I read your explanation. Thanks.


----------



## Texan78

Aquatechtoo said:


> Perhaps you can write a similar random function for clouds like was done for the storm and then use a control structure like If/Else to pull them together!? Just a shot in the dark on my part; object-oriented programming is NOT a strength of mine.


I am thinking the same thing as you. Just copy the random storms coding and and replace it with the rolling clouds calls. In theory that should create random clouds and random storms. I have the Current USA Ramp timer and have it hooked up and testing it. If it works out well I will use the ramp timer to control the cycles and the Uno to control the random weather events. That should also free up some memory for sound. 



Aquatechtoo said:


> I think it would be a more natural experience if the sound and lighting aren't in sync with each other. This concept also reminded me of Xmas lights syncing to music and a quick search reveled this: http://forum.arduino.cc/index.php?topic=87956.0 and while it isn't exactly what you are thinking about, it could serve as inspiration as you start devising potential solutions? This is a case where the lights and sound would fire in concert with each other, but if you could take this concept and offset the lights and sound by a second or two, it might take this idea of storms over a planted tank to a "4D" level!?


I am very familiar with what you're talking about. I was doing those Christmas lights 7 years ago with the L.O.R. (Light-o-rama) controllers. LoL So I have a lot of experience with that. 




zodduska said:


> It seems like the dynamic fade functions are all pretty simple loops on the LED+, you could time lightning strikes pretty easily and add the right amount of delay(s) for the audio to sync then have it repeat until the random storm duration is up. Though I've no idea how to code it.


That is an excellant idea! That makes a lot of sense. Just find out the timing between the flashes and add a delay. I will have to figure it out and play with it. I should be able to figure it out once I start digging into the code and learning it. 

I am getting all my parts together now for this. I am really thinking this RF thing could work. I have found all the parts and there is even an Arduino Library for these RF parts. If you could have a small box hidden behind your tank or under it so you and keep the Uno plugged in somewhere else so you can make easy changes that will be sweet!


----------



## Aquatechtoo

Texan78, I just had a realization that may help you with identifying the amount of memory available on the Uno as you start implementing on your idea. The geniuses who worked on this code included the ability to identify the amount of free SRAM. In latter versions of their code, you can see the amount of free SRAM from the serial monitor printout! In my case (I'm using Dalhammer's v4.1 code and tweaked a few minor things and I have a few more alarms firing during the day) I have 967 bytes free.


----------



## Texan78

Aquatechtoo said:


> Texan78, I just had a realization that may help you with identifying the amount of memory available on the Uno as you start implementing on your idea. The geniuses who worked on this code included the ability to identify the amount of free SRAM. In latter versions of their code, you can see the amount of free SRAM from the serial monitor printout! In my case (I'm using Dalhammer's v4.1 code and tweaked a few minor things and I have a few more alarms firing during the day) I have 967 bytes free.


Great thanks! I remember during all my reading somewhere where that was mentioned so I will go back and see if I can locate it. I am about to head out and see if I can locate some of the last parts I need to at least get the basic functions work then I can go from there with getting the RF capabilities working and then adding the other features.

I bought the ramp timer for this as well to test and play with it. It works fine on the preset I was using to dim the lights. I was disappointing with the fact it doesn't dim to moonlight, it just dims them to off. So I am probably going to send those back. I was hoping to use the Ramp Timer to control the dimming of the lunar/day cycle and use the Uno to handle events hoping that will free some memory up too. Looks like that won't be the case. Just so I have an understand and clear. With this mod this will dim to moonlight at night?

-Thanks!


----------



## zodduska

With the way it's set up by default it's switching the built in modes at the end of the day to the dusk/dawn dynamic fade before switching to moonloght, its not set up yet for a true ramp up/ ramp down. You could do it with a bunch of alarms using the individual channel intensity adjustments but you'd need a lot at intervals, the TimeAlarms library is limited to 256 and I'm not sure that's the cleanest way to do it as you may run out of memory before that limit is reached.


----------



## Indychus

The best way to trigger a fade would be to use a sinusoidal function to repeatedly press the up or down command for each channel. You'd have to write a new function for this, and just trigger the function with a single alarm. This is something I have been toying around with, but I've been out of the country with work and haven't had much time to play with it.


----------



## Texan78

Awesome, guess I need to get this set up and at least to a working state first and play catch up then go from there. Good to know it is at least possible. Oh wait, in order to have it to dim to moonlight it would have to have control of 2 separate channels right?


----------



## zodduska

Oh nice, sounds like that'd be a lot easier to set up, I just tested and it takes 42 "White Down" for the White LED to go from max to off with my setup.


----------



## zodduska

Texan78 said:


> Awesome, guess I need to get this set up and at least to a working state first and play catch up then go from there. Good to know it is at least possible. Oh wait, in order to have it to dim to moonlight it would have to have control of 2 separate channels right?


The code would have send signals to dim the red, green, blue and white individually.


----------



## Texan78

zodduska said:


> The code would have send signals to dim the red, green, blue and white individually.


Ah crap you're right. I forgot it has RGB plus white. So it would need to control 3 channels. But this is possible since the remote can fade out each RGB and white channel. Hmmmmmmm This is getting interesting. Ok I am heading out. I need to play catch up so I start playing and digging into this.


----------



## zodduska

cleaned up my temporary build a little


----------



## Tiger Muskie

Hi Indychus and others:

Thank you very much for providing this clear step-by-step guide for the project. I have it up and running on my 29 gallon tank with the RTC and LCD display. 

*Question:* Has anyone worked out a method for turning off the LCD backlight at night? 
My fish all gather in the corner of the tank by the LCD and stare at it all night, and more importantly, my wife doesn't like it! 

Thanks!


----------



## zodduska

I was looking into dimming it, I think you can use a PWM pin and some code to do it but I'm not sure how. I'm personally adding a resistor to the default hookup tonight to reign in the brightness a little.

Edit: I put a 1k ohm resistor between the backlight 5v, red wire—second from the right in my pic, it's no longer annoyingly bright. Use a higher value resistor if you want it even dimmer.


----------



## Tiger Muskie

*Method to turn off LCD at night*



zodduska said:


> I was looking into dimming it, I think you can use a PWM pin and some code to do it but I'm not sure how. I'm personally adding a resistor to the default hookup tonight to reign in the brightness a little.
> 
> Edit: I put a 1k ohm resistor between the backlight 5v, red wire—second from the right in my pic, it's no longer annoyingly bright. Use a higher value resistor if you want it even dimmer.


Thanks Zodduska, I also tried a few resistors found found one that gave a pleasing brightness.

Also I worked out a way to turn it on and off with alarms: I used Pin 13 as the power source for the backlight, and controlled it like this (with new "cases" for testing):



Code:


void SetAlarms()
{
// ... later ... 
  pinMode(13, OUTPUT); //DJT 11/25/13 for LCD screen power on and power off
  LCDPowerOn();        //DJT 11/25/13 Turn on LCD when program initializes
  Alarm.alarmRepeat(6,00,0, LCDPowerOn); 
  Alarm.alarmRepeat(7,00,0, M1Custom);
//... later ... end of day alarms
  Alarm.alarmRepeat(21,00,0, Moon2);
  Alarm.alarmRepeat(22,00,0, LCDPowerOff);
//... later ... cases for testing
    case 33:  //DJT 
      LCDPowerOn();
      break;
    case 34:   //DJT
      LCDPowerOff();
// ... later ... the functions:
void LCDPowerOn() //DJT
{digitalWrite(13, HIGH);}
void LCDPowerOff()  //DJT
{digitalWrite(13, LOW);}

This works so far - now my fish won't stay up all night watching the LCD!:drool::drool::drool:


----------



## zodduska

Nice, thanks for the code!


----------



## Texan78

Hey guys, I just need some advice since this is where I fail when it comes to the technical side. I am ordering all my parts and the last thing I am stumped on is how to power this PCB that will contain the RF receiver with IR emitter to fire at the fixture IR. My thought was to use a 3.6V @ 110mAh coin cell battery breakout. Would that been enough to power the PCB with the RF receiver and IR emitter? 

-Thanks!


----------



## zodduska

edit: Nevermind, I forgot you were doing the RF thing.


----------



## Texan78

zodduska said:


> edit: Nevermind, I forgot you were doing the RF thing.


:icon_mrgr Yeah I am going to be the odd ball guinea pig on this. I did order the parts to do it the regular way though too in case it doesn't work.


----------



## Indychus

It won't take much power to fire the LED. I'd start by looking at the spec sheet for your RF receiver.

Sent from my HTC One X using Tapatalk


----------



## Texan78

Here is the spec sheet for the one I am going to be using. It says 5V for the supply power. These cell batteries only supply 3V so I am concerned that won't be enough or that is what receiver puts out. 

http://dlnmh9ip6v2uc.cloudfront.net..._315MHz_ASK_RF_Receiver_Module_Data_Sheet.pdf


----------



## cmbranch13

I suggest that you consider using a pre-build zigbee, bluetooth, or z-wave ardunio shield rather than trying to build you own unless you are very skilled in electronics. Wireless and RF are fairly difficult to get working.

Take a look at this shield http://arduino.cc/en/Main/ArduinoXbeeShield.

It should meet your requirements and will be much more reliable and easier to use.


----------



## Dave6265

Is anyone out there putting this DIY into a nice package and selling it? 

I know I am not the only one hoping someone can deliver on the goods we were all hoping would come when we bought these lights. I am completely clueless when it comes to coding, and attempting this would only lead to wasted time, frustration, and a broken controller. :fish:


----------



## Indychus

You don't need to know any coding, we've already done it for you. If you can put it together and copy/paste the code, you're good to go. I had hopes of making a complete unit to sell, but my work schedule is crazy right now and I'm always out of the country.

My intentions from the beginning were for this to be open-source. If someone else has the skills and time to build these to sell I'm fine with it, as long as its sold at as low a cost as possible to support the community and not to make money.

Sent from my HTC One X using Tapatalk


----------



## O2surplus

Indychus said:


> You don't need to know any coding, we've already done it for you. If you can put it together and copy/paste the code, you're good to go. I had hopes of making a complete unit to sell, but my work schedule is crazy right now and I'm always out of the country.
> 
> My intentions from the beginning were for this to be open-source. If someone else has the skills and time to build these to sell I'm fine with it, as long as its sold at as low a cost as possible to support the community and not to make money.
> 
> Sent from my HTC One X using Tapatalk


If you'll post a complete wiring diagram and parts list, I'll convert it into a Printed Circuit Board for you. This project can be boiled down to a "stand alone" arduino based controller that will free up your stock UNO or MEGA for other uses.


----------



## Texan78

I got all my goodies finally and I am jumping into this build and trying to play catch up. Where do I find these libraries? 

RTClib
IRremote
LiquidCrystal

When I try to load the most recent sketch I am getting this error which I am sure it is because I am missing those libraries. 



Code:


Satellite__3_6:23: error: 'time_t' does not name a type
Satellite__3_6:19: error: 'RTC_DS1307' does not name a type
Satellite__3_6:20: error: 'IRsend' does not name a type
Satellite__3_6.ino: In function 'void SetAlarms()':
Satellite__3_6:75: error: 'Alarm' was not declared in this scope
Satellite__3_6.ino: In function 'void setup()':
Satellite__3_6:91: error: 'RTC' was not declared in this scope
Satellite__3_6:98: error: 'DateTime' was not declared in this scope
Satellite__3_6:101: error: 'syncProvider' was not declared in this scope
Satellite__3_6:101: error: 'setSyncProvider' was not declared in this scope
Satellite__3_6:103: error: 'Alarm' was not declared in this scope
Satellite__3_6.ino: In function 'void loop()':
Satellite__3_6:117: error: 'Alarm' was not declared in this scope
Satellite__3_6.ino: At global scope:
Satellite__3_6:122: error: 'time_t' does not name a type
Satellite__3_6.ino: In function 'void ThunderStorm()':
Satellite__3_6:149: error: 'Alarm' was not declared in this scope
Satellite__3_6:165: error: 'Alarm' was not declared in this scope
Satellite__3_6:167: error: 'Alarm' was not declared in this scope
Satellite__3_6:169: error: 'Alarm' was not declared in this scope
Satellite__3_6.ino: In function 'void digitalClockDisplay()':
Satellite__3_6:176: error: 'hour' was not declared in this scope
Satellite__3_6:177: error: 'minute' was not declared in this scope
Satellite__3_6:178: error: 'second' was not declared in this scope
Satellite__3_6.ino: In function 'void lcdClockDisplay()':
Satellite__3_6:184: error: 'hour' was not declared in this scope
Satellite__3_6:185: error: 'minute' was not declared in this scope
Satellite__3_6.ino: In function 'void SendCode(unsigned int, byte, const char*)':
Satellite__3_6:333: error: 'irsend' was not declared in this scope
Satellite__3_6:334: error: 'Alarm' was not declared in this scope


----------



## dswiese

Texan78 said:


> I got all my goodies finally and I am jumping into this build and trying to play catch up. Where do I find these libraries?
> 
> RTClib
> IRremote
> LiquidCrystal
> 
> When I try to load the most recent sketch I am getting this error which I am sure it is because I am missing those libraries.


I am just jumping into this project as well and the links are scattered through the thread. LiquidCrystal should be a default in the sdk. 
I saved the others here for my own purposes (zip file) https://dl.dropboxusercontent.com/u/6572814/Archive.zip


----------



## Texan78

I've pretty much put this project on the back burner until I have some free time with nothing to do. I am not using the Sat+ anymore for my primary lights. I am just using them for moonlights and the Ramp Timer takes care of that in conjunction with my BMLs on my Apex.


----------



## dswiese

@texan - yeah i need some free time as well. maybe the OP can use that link in the main post for newbies. 

I am going to take this on in the near future. 

@all - should I stick with the uno or go with the due if there is a chance I will want to add on to this concept for a more full tank automation? any gotcha's i should worry about ?


----------



## jlboygenius

this is an awesome project!
I just got this LED light and am working to get mine setup with arduino control.

is 4.1 the most recent version? 

Has anyone thought about putting the code on GitHub or something like that, to make it easier to share and version?

thanks!


----------



## Jsty093083

Been trying to follow this project for a few weeks now, have some soldering experience but not much as far as reading schematics and such. Got all the stuff listed today in the mail but I may have gotten the wrong RTC.

It has WAY more pins that any that you guys listed, pulled up a diagram and wondering if you can point me in the direction of which I would need to use?









Edit:
So I think I have it mostly figured out, but on this diagram what are the brown and purple, cant seem to locate those on my RTC.


----------



## jlboygenius

Jsty093083 said:


> Been trying to follow this project for a few weeks now, have some soldering experience but not much as far as reading schematics and such. Got all the stuff listed today in the mail but I may have gotten the wrong RTC.
> 
> It has WAY more pins that any that you guys listed, pulled up a diagram and wondering if you can point me in the direction of which I would need to use?
> 
> 
> 
> 
> 
> 
> 
> 
> 
> Edit:
> So I think I have it mostly figured out, but on this diagram what are the brown and purple, cant seem to locate those on my RTC.



I think the only pins you need to worry about are the ones in the top left. I imagine that they are all actually the same, just different ways to connect to them. I just hooked up mine, and all i needed was +5, grnd, SDA and SCL.


----------



## Jsty093083

jlboygenius said:


> I think the only pins you need to worry about are the ones in the top left. I imagine that they are all actually the same, just different ways to connect to them. I just hooked up mine, and all i needed was +5, grnd, SDA and SCL.


alrighty, I was hoping I would be able to just copy the code all you guys had written, as I know nothing about code, without the DS wire going to 2 is it going to be messed up if I copy it? 

Went into this with a lot of confidence, but gotta admit, im not feeling it anymore lol.


----------



## jlboygenius

Jsty093083 said:


> alrighty, I was hoping I would be able to just copy the code all you guys had written, as I know nothing about code, without the DS wire going to 2 is it going to be messed up if I copy it?
> 
> Went into this with a lot of confidence, but gotta admit, im not feeling it anymore lol.


the one thing I'll note where that picture is wrong(probably) is where you connect SDA and SCL. for older arudino's, this is correct. If you're using an arduino Duo(and you most likely are), there are seperate pins for SDA and SCL.

If you're looking at your arduino the same way that one is laid out in the picture, the SDA/SCL pins will be in the top left.

When you have it connected, watch the serial Port out out. It will show the time output, so you can see that it's working. One thing to note, i believe the Arduino software defaults to 9600bps for the serial, but this code is setup to use 57600(i think). If you see gibberish in the serial monitor window, check that you have the speed set correctly.


----------



## jlboygenius

Jsty093083 said:


> alrighty, I was hoping I would be able to just copy the code all you guys had written, as I know nothing about code, without the DS wire going to 2 is it going to be messed up if I copy it?
> 
> Went into this with a lot of confidence, but gotta admit, im not feeling it anymore lol.



Hm, looks like only the arduino Uno R3's moved the sda/scl ports.
see:









this is the back, they are the top right pins


----------



## Jsty093083

Alrighty, I think i got all the hardware setup, went through some of the stuff at the beginning, with the IR sensor and remote and no issues there. Copied the latest sketch code and getting an error when trying to compile. 

C:\Program Files (x86)\Arduino\libraries\DateTime\DateTime.cpp: In member function 'void DateTimeClass::setTime(time_t)':
C:\Program Files (x86)\Arduino\libraries\DateTime\DateTime.cpp:28: error: 'millis' was not declared in this scope
C:\Program Files (x86)\Arduino\libraries\DateTime\DateTime.cpp: In member function 'time_t DateTimeClass::now()':
C:\Program Files (x86)\Arduino\libraries\DateTime\DateTime.cpp:43: error: 'millis' was not declared in this scope

I am very very new at this and have no code experience at all so any clue what I need to do to get this working? downloaded all the libraries needed I believe.

EDIT: figured out partly, forgot to edit the timealarm.h to 24 from 6. Now im getting:

controller_sketch.ino: In function 'void setup()':
controller_sketch:24: error: 'Alarm' was not declared in this scope
controller_sketch.ino: In function 'void loop()':
controller_sketch:41: error: 'Alarm' was not declared in this scope


----------



## Aquatechtoo

Jsty093083 said:


> Alrighty, I think i got all the hardware setup, went through some of the stuff at the beginning, with the IR sensor and remote and no issues there. Copied the latest sketch code and getting an error when trying to compile.
> 
> C:\Program Files (x86)\Arduino\libraries\DateTime\DateTime.cpp: In member function 'void DateTimeClass::setTime(time_t)':
> C:\Program Files (x86)\Arduino\libraries\DateTime\DateTime.cpp:28: error: 'millis' was not declared in this scope
> C:\Program Files (x86)\Arduino\libraries\DateTime\DateTime.cpp: In member function 'time_t DateTimeClass::now()':
> C:\Program Files (x86)\Arduino\libraries\DateTime\DateTime.cpp:43: error: 'millis' was not declared in this scope
> 
> I am very very new at this and have no code experience at all so any clue what I need to do to get this working? downloaded all the libraries needed I believe.
> 
> EDIT: figured out partly, forgot to edit the timealarm.h to 24 from 6. Now im getting:
> 
> controller_sketch.ino: In function 'void setup()':
> controller_sketch:24: error: 'Alarm' was not declared in this scope
> controller_sketch.ino: In function 'void loop()':
> controller_sketch:41: error: 'Alarm' was not declared in this scope


You might try reading through some of the posts starting around #349. I had some of the same issues and the answers should be documented clearly enough in those who answered my posts that they may help you too.


----------



## Jsty093083

Alrighty, did some reading and went back and deleted my library and started from scratch. Compiled fine, but now that its loaded into my arduino im getting a blank lcd with nothing showing just line 1 and line 3 contrast bars. not sure whats happening.

I did run the hello world sketch and it worked fine, so not sure where im messed up here.


----------



## Jsty093083

Oh and im using Indychus's latest update from the front page.


----------



## Jsty093083

Woooo got it! Only question I have now may be an easy one, the clock is showing 24 minutes fast, even when hooked to my computer, not sure why, any help there? Seems my RTC is not working every time I unplug it and replug it in it starts at the same time, 2346.


----------



## Jsty093083

Got it, Indychus figured it out.


----------



## t2ak

Just put this together and it work great! Awesome job everyone. Thank you 

One quick question. My clock is 6 minutes fast. How can I adjust it?

Sent using these stoopid hooves..


----------



## t2ak

Also I could only get this to run on my uno. I tried my mega as I would like to add some other functions as I learn more but the led would not work. Same exact wiring as the uno too.

Sent using these stoopid hooves..


----------



## t2ak

t2ak said:


> Also I could only get this to run on my uno. I tried my mega as I would like to add some other functions as I learn more but the led would not work. Same exact wiring as the uno too.
> 
> Sent using these stoopid hooves..


I figured out why pin 3 will not work. The mega's OCR2B pin is pin 9 not 3. I will test this out tonight after work. 

Sent using these stoopid hooves..


----------



## cbridgeman

I setup an account on here just so I could say thank you for putting this guide up. 

I built this with a Adafruit RGB LCD I2C shield. All I had to do was change the LCD library and add this line *Adafruit_RGBLCDShield lcd = Adafruit_RGBLCDShield();*. 

Now to add in my temperature sensor, relay board, and other goodies.


----------



## JoeD

*Sunrise / Sunset relay for Finnex 16" Planted+*
I am looking to "Arduino" my 16" Finnex Planted+ with a siesta lighting schedule. I have no want for additional functions.

I am new to microprocessors and relays. I plan on an Arduino Mega (Uno supply not available presently), RTC1307. Learning to read/write sketches and electrical - to a degree.

The largest source of my confusion (today) is which PWM relay system is best suited to the "16" Finnex Planted+ " fixture. 

The only Specs I could find for this fixture are:
Transformer # (Finnex a-al-r16wm), 110-120v, DC15V, 11.85watt 
60 x 7000k Daylight, 32 x True 660nm Deep Red, 4 x Actinic Blue Moonlights

I love reading these threads. Thanks for your contribution. 

Now awaiting your assistance and China's slow boat... Thanks everyone.


----------



## jeffkrol

JoeD said:


> *Sunrise / Sunset relay for Finnex 16" Planted+*
> I am looking to "Arduino" my 16" Finnex Planted+ with a siesta lighting schedule. ... Thanks everyone.


Hate to be the bearer of bad tidings.. but it doesn't quite work that way...


----------



## JoeD

jeffkrol said:


> Hate to be the bearer of bad tidings.. but it doesn't quite work that way...


Thanks for your help...


----------



## bcarl_10gal

I read this entire thread the other night trying to see if I wanted to undertake this project. I had some questions regarding functionality of the controller and wanted to get some input. I had my roommate (computer science major) help me understand some of the code and will help me with it. 

1) Would one controller be able to control 2 satellite plus' at once?
2) Since there isnt a way to dim the light could you add more alarms in between by using the 4 custom settings and have them serve as a more "gradual" change (If you eliminate the use of the lcd this would potentially free up enough memory)?
3) Does anyone use this controller with live plants and how does this controller/you control algae?
4) In the opening post it states that you could use it with any fixture? Merely speculation but would this be compatible with the ecoxotic's new plant light (due to be release Spring 14)?

Sorry if any of these questions have been answered earlier in the post and I missed them. This is a really cool project that you all have put a lot of time into!


----------



## Indychus

U


bcarl_10gal said:


> I read this entire thread the other night trying to see if I wanted to undertake this project. I had some questions regarding functionality of the controller and wanted to get some input. I had my roommate (computer science major) help me understand some of the code and will help me with it.
> 
> 1) Would one controller be able to control 2 satellite plus' at once?
> 2) Since there isnt a way to dim the light could you add more alarms in between by using the 4 custom settings and have them serve as a more "gradual" change (If you eliminate the use of the lcd this would potentially free up enough memory)?
> 3) Does anyone use this controller with live plants and how does this controller/you control algae?
> 4) In the opening post it states that you could use it with any fixture? Merely speculation but would this be compatible with the ecoxotic's new plant light (due to be release Spring 14)?
> 
> Sorry if any of these questions have been answered earlier in the post and I missed them. This is a really cool project that you all have put a lot of time into!


1) A single controller can control any number of fixtures within range of the controller, provided they all use the same IR protocol.

2) You could use the 4 custom mixes to ramp the light up or down; I prefer to use the up/down arrow function for each individual color to ramp the colors and save the mixes for other stuff... I have 64 alarms running on an Uno now with memory to spare.

3) I think we all use this setup with live plants. I can't speak for algae control.... All of my tanks are heavily planted and the plants out-compete any algae, so I never have any algae issues.

4) If you're willing to do the groundwork necessary to hack the IR protocol and codes, this controller can automate any IR device... Aquarium light fixtures, TVs, Bluray players, home audio, etc.  If you use the wiring in early posts and include the IR receiver, hacking the codes is easy. It doesn't matter what kind of device you have, if it has an IR remote, you can use this controller to run it.


Sent from my HTC One X using Tapatalk


----------



## bcarl_10gal

Thank you for your help! Few quick responses so I understand what you've said correctly. 

1) So based on that 2 current plus' should be on the same IR protocol and therefore able to work. If a second light had a different IR protocol it would not work.

2) So your code for example would be every X amount of minutes it the controller would up X color one click. Therefore effectively creating a true "ramp" 

The reason for question one is that I will be upgrading my tank and need a stronger light. My thought is to use the current planted + for my "effects" light and use another light that is on a standard timer to run during "full spectrum" mode and adjust the random t storms that could potentially come on during that time. This is a really great project you have developed that is a great alternative to a Apex controller.


----------



## Curt_Planted

Thanks a ton for the work you put into this. I modified your code, wrote some of my own functions and created a simple code that just uses the arduino for timing the siesta sunrise and sunset schedule I want. I added in an initial and final 30 minute period with a particular lighting tone (faded in also) that really makes my fish pop so I can enjoy them in the morning and evening. 

I am going to reconfigure it to be more adjustable and user friendly over the next week then I will post the code. All you will need for this code is an arduino and an IR led.  

Also I found two errors in the code, typos. It was either white up or white down was calling the wrong function. Also I needed to add a 2 to the IR function instead for one of the colors to get it to transmit. I can point them out specifically later.

I'd highly recommend anyone doing their own fades to increase the colors first when increasing light to decrease how apparent the big steps in brightness are from the increasing of the white lights. Of course do the opposite when decreasing the light levels. By adjusting color then white or vice versa I also found this also solves a bit of flickering I noticed when adjusting them a step at a time in sequence. (adjusting the colors with the whites bright tends to make the whites dim slightly during the transition. There is probably not enough transient voltage suppressing capacitors in the pwm circuits controlling the brightness or perhaps the power supply is not well filtered to prevent this.)

**I can confirm that other than those two issues these hex codes work with the two units I just picked up last week without modifications. Which is awesome because I don't have an IR transistor!**

Anyone think it would be good to do seasonal changes to the lighting schedule to promote more natural seasonal behaviors? Could I make it significant enough to do that without messing up the balance of the tank? Thanks again! Great Job! roud:


----------



## Dragonfish

I've been watching this thread for a while. I run a Satellite + on my dart frog tank and would love to build one of these. 

Curt, I'm looking forward to your code! It just might be what it takes for me to pull the trigger. Will an Arduino Uno R3 work? Thanks for all the hard work everyone!

~edit: oh wow I need to update my signature


----------



## Curt_Planted

I have attached a text doc with the adapted code. Please copy and paste it into the Arduino editor. It should work without a problem on an Uno but I'm running a Duemilanove so you'll have to try it to confirm. Read through the code with special attention to the notes at the beginning and throughout the user defined variables area! All you need is an IR led and the appropriate resistors. You have to define your lighting schedule times, a custom accent lighting setting and the current time. That's it in the code! For your remote set the M4 preset to zero light and as before you will need to connect your led to pin 3. I'm running mine from a cell phone charger although my pc keeps power to the ports even when sleeping so I could just use that.


----------



## Dragonfish

Safe to assume I still need an RTC?

~edit: Oh! It doesn't use an RTC? Cool! Looking over the code now. Thanks for using lots of notes. I'm pretty solid in powershell, but still learning this.


----------



## Curt_Planted

I actually just ordered an RTC. When I first implemented this code I didn't realize that the number of seconds in a day exceeded the max size an integer type could store. I came home at 1AM to a fully lit tank and assumed it was an issue with the internal timer. I impulsively ordered the RTC and then realized my seconds in the last hours of the day were negative! No wonder the lights were turning on! lol, so I will be adding an RTC although it is not needed as I fixed the issue with the code.  It might drift by a couple seconds a day, but I figure all it takes is a quick upload after correcting the time to correct that, so having to do that every couple of months should be no biggie. The RTC was only 9 bucks though, so if you want to add one I should have the code updated for it in a week or so.

How's the code working for you?


----------



## kman

Tagged. I need to put together a list of links to parts asap so I can give this a try!


----------



## Anor

@Curt_Planted , looking forward to your code including the RTC. Would love to have the dimming functions, as i just run the "plain" code from here (which is very nice but wish i had some dimming.


----------



## bcarl_10gal

Curt do you think you could find a way to incorporate the random t storms in with your code?


----------



## Curt_Planted

Might be a bit on the RTC, didn't realize it was so small and after my small soldering iron died I attempted it with a more broad tipped one... that is so not happening on the IC, lol! So I have to order a new iron before I can even hope to clean up the solder mess I made of my RTC kit!

Anor: If you look in my code I have already coded the sunrise and sunset as cases as in the original code, they can also be directly called as functions! Look at the main loop. For the customaccent fade the only issue is updating all the other functions to keep count of the light trackers. There is no way for the arduino to know what the light level is without doing that. To get it somewhere you want it to be it has to know where its at. I will work on adding in light tracker updates to the other functions and comment/uncomment the right spots for full manual control so you can see what I mean (Case 33 and 34 are sunrise and sunset if memory serves.) Not sure what to do with the dynamic effects... can't really assign a light level to them so may need to terminate them with a full sunrise or sunset to get to a known light level.

bcarl: I think it would be pretty straight forward to add in random t storms. Just a random number generator that initiates a break from the if-else statements, starts the tstorm function and then returns to previous lighting condition after. The issue I have with doing that is having already observed how bad the flashes freak out my fish! I certainly wouldn't use it for fear of stressing them!


----------



## Curt_Planted

On another note, I am working on a much broader plan to automate my tank with a pair of arduinos with full redundancy. My eventual goal is to have one arduino controlling lighting, dosing, water changes, and co2 as well as acting as a tank/hose leak alert system. The second arduino will act as a fail safe, using pressure sensors, redundant water level sensors and photoresistors to confirm directly conditions are within the primary arduino's parameters and implementing a limp mode to minimize damage/risk to fish. It will also control a redundant solenoid to shut down CO2 if needed, and will control a relay controlling the power supply for the dosing system to shut it down if it fails, and a tertiary sprinkler valve to shut off water for the auto water change system ( there will already be a mechanical valve for fill control and electrical controls in place from the primary arduino). It will also include an ethernet shield to send me text alerts in the event of a failure. I have already set up a web cam and remote control of my pc with my phone so I can check the tank at work to confirm any issues.  I just finished an outline plan for different phases of each control aspect and the key coding/technical issues to be solved. See what happens when a rocket scientist falls in love with planted tanks?   I want to build it so there is no single point of failure and I will know within minutes if something goes wrong. It all started with me reading about how unreliable CO2 solenoids were. People suggested using 2 series pairs in parallel, and all I could think was: well it's great it will keep working if a valve fails, but what good is that? if you don't know there's a problem to fix, you've lost redundancy and your just delaying a system failure. So I want to solve that issue using sensors, and extend that idea to other damage/fauna critical systems in the controller.


----------



## Aquatechtoo

*IR Emitter*

I used much of this code and equipment from @Indychus, @Dalhammer, et. al. original posts to build a controller that's firing to two Satellite LED+ fixtures. I'm wondering if you guys know why the emitter doesn't fire consistently to both of the Satellite IR receivers. I'm assuming the emitter is the problem because randomly, either one of the fixtures appears not to receive the command from the controller because that fixture will be on one of the previous settings from earlier in the day. Some days, both lights work fine. Other days, either light can be sitting on another light setting, generally from earlier in the day. As a visual, here is how the setup looks.


----------



## Curt_Planted

Aquatech2, the emission angle is not that wide. I have my two receivers staggered diagonally so the clear part of them is right next to eachother as seen from the emitter. Even then I still have them about 4 inches from the emitter. HTH


----------



## Aquatechtoo

Thanks, it does, sorta. If I recall correctly when I was building the controller, the emitter only had a range of maybe an inch. Do I need to boost that signal strength by using different hardware?


----------



## Curt_Planted

If your range is that short you may have too high a resistance with the LED. Look at the spec sheet for the ir led and see what the voltage requirement (V_ir) is. Then subtract the voltage requirement from 5 (the output voltage from the arduino I believe). That will give you the voltage left across your resistor. Since you know the current you want through the circuit (just shy of the 20 mA limit for the arduino) then the resistance is just that voltage divided by the current. 

In equation form: R=(5-V_ir)/.02 

For my IR led: (5-1.35)/.02=182.5 ohms

The IR led I am using needs 1.35 volts (its the forward voltage lower limit on the data sheet), and I rounded the resistance I calculated up to 200 ohms which gives me 18 mA current (slightly below the limit on the arduino). I have about a foot or so range.


----------



## Aquatechtoo

Thanks, Curt_Planted. I'm using the IR led from one of Indychus' first posts. Its a LTE-5208A and according to the spec sheet, the typical V_ir is 1.2 (there's no minimum listed). I am using a 150 ohm resistor. Based on the resistance formula, it is 190. However, I'm not sure what this tells me?


----------



## Curt_Planted

Perhaps the lower resistance is preventing the arduino from maintaining proper voltage accross the led due to overdrawing current. Try 200 ohms (im using 2 100 ohms in series) and see what happens.


----------



## kman

Hi guys! I'm just about ready to jump on the bandwagon. Can someone confirm that the parts I've selected will get the job done before I pull the trigger?

Arduino UNO R3 board with DIP ATmega328P - $28
http://amzn.com/B006H06TVG

SainSmart I2C RTC DS1307 AT24C32 Real Time Clock module+board for AVR ARM PIC - $9.20
http://amzn.com/B006J4FZW4

SainSmart LCD Module For Arduino 20 X 4, PCB Board, White On Blue - $12.29
http://amzn.com/B003B22UR0

Super-bright 5mm IR LED - 940nm - $1.50 (for two... Or I may just buy local)
http://www.adafruit.com/products/387

So just over $50 for the majority of what I need. 

Resistors (local purchase, once I confirm what's needed)

I have plenty of USB chargers and cables so I think power is not an issue. I have a couple of different soldering irons/guns (minor wiring, been nearly 30 years since I soldered a circuit board but I doubt much has changed). I think some wire for jumpers and perhaps a small breadbox is all I need to get going?

Does this current software do ramping on/off yet? I have the simple ramp timer but I want to incorporate sunrise and sunset lighting to the schedule, and perhaps moonlight for a couple of hours. It would be nice if I could still have the lights ramp on and off, though. A second lighting period that just brings the sunrise mode on for feeding time before work would be icing on the cake, else I can manually trigger it with the remote as I do now.


----------



## zodduska

Thanks for the new code to play with Curt_Planted, I'm excited to try it out tonight.


----------



## Dragonfish

It does not do ramping as written, it uses alarms to change modes at designated times. In the section below you can specify what mode you want at what time. You are allowed up to 24 alarms as it is set up now (as outlined in the first few posts of this thread) so in theory you could use your custom colors to have a 4 step "ramp" up to your brightest setting. The max number of alarms is 255 (if you modify the library) but each one uses 12 kb memory so if you set up too many of them you will run out of memory. There may be other creative ways to accomplish this but that seems the simplest. You would also put any moonlight settings in here. I just got my kit so I'll put it together in the next couple days and post my settings once I settle on them, as I had similar ideas to you.

void SetAlarms()
{
// Set up your desired alarms here
// The default value of dtNBR_ALARMS is 6 in Alarms.h.
// This code sets 12 alarms by default, so you'll need to change dtNBR_ALARMS to 12 or more
Alarm.alarmRepeat(7,00,0, DawnDusk);
Alarm.alarmRepeat(9,00,0, Cloud2); // (HR,MIN,SEC,FUNCTION)
Alarm.alarmRepeat(11,00,0, FullSpec);
Alarm.alarmRepeat(15,00,0, Cloud2);
Alarm.alarmRepeat(19,00,0, DawnDusk);
Alarm.alarmRepeat(21,00,0, Moon2);

// Comment these out if you don't want the chance of a random storm each day
Alarm.alarmRepeat(12,00,00, ThunderStorm);
ThunderStorm(); // Sets up intial storm so we don't have wait until alarm time


----------



## Dragonfish

Interesting, in the FAQ for TimeAlarm library they state that each Alarm.alarm uses 12 bytes up to a max of 255, but Alarm.timerOnce is released immediately and there is no reset limit so maybe we can use them to fire WhiteUp or other colors repeatedly until the desired setting is acheived for a ramped effect with no performance hit? Kind of like this:

Alarm.alarmRepeat(7,00,0, DawnDusk);
Alarm.timerOnce(30, WhiteUp); // called once after 30 seconds 
Alarm.timerOnce(30, WhiteUp); 
Alarm.timerOnce(30, WhiteUp); 

This would in effect start at DawnDusk, then press the white up button every 30 seconds until you've hit max (you'd have to figure out how many times that would be). You could of course use the other colors Up functions to finish at a specific color. In the evening you would then simply reverse the order with WhiteDown until you hit your dusk color eventually hitting moonlight. I can see I'm going to have to play with this some more.


Q: How many alarms can be created?
A: Up to six alarms can be scheduled. 
The number of alarms can be changed in the TimeAlarms header file (set by the constant dtNBR_ALARMS, note that 12 bytes of RAM are required for each timer)

onceOnly Alarms and Timers are freed when they are triggered so another onceOnly alarm can be set to trigger again.
There is no limit to the number of times a onceOnly alarm can be reset.


----------



## Curt_Planted

I went straight with loops for the sunrise/sunset. To assure any missed transmissions dont lead to a drift in the actual lights brightness I followed the sunrise with a full spectrum command and the sunset with a call to M4 that I set on the remote to be a zero light setting. 

Using if's to activate lighting changes made it pretty straight forward to test counters for each color to adjust lighting to user chosen settings for the custom accent function. I think you should be able to directly grab the sunrise, sunset, and custom accent functions and use them in the original code. You'll just have to bring over the global lighting counter variables and update them after any other lighting changes as well. Or you can just delete them if you don't want the customaccent function. Just give sunrise and sunset the right inputs ( the time delay in ms between adjustments) as described in the notes and I'm sure the IDE will point out anything else you need to do to get it to work!


----------



## Curt_Planted

Oh and 42 lighting steps through arduino transmissions, 21 with the remote. They must have included the answer to the universe in our lights  I noticed that the dynamic effect transitions were much smoother than the manual adjustments and now we know why. The built in controller in the light uses the 42 steps and the remote sends a double transmission with each adjustment button press.


----------



## Mustang5L5

Found this thread about 3 weeks ago. Hard to believe I've never heard of an arduino before, but I educated myself in a hurry and have been enjoying them. Went from buying 1, to having 4 at the moment. 

Anyway was fun to follow this thread and learn a few things. Currently wrapping up my controller for my 48" Satellite LED+ and want to add a few things and test it before building it into a project box. 

Want to add a temperature display, and PH probe as a bit of info, as well as perhaps wire in a red LED as a warning if certain parameters fall outside of a specific range I want. I figure it will allow me to glance at a distance. 


I am interested in a ramp sort of lighting schedule though. I'll wait until i get all my sensors and display tested and finalized before working on schedule. 

These things are fun. Already have plans for my other 3. Currently planning on an OBD2 display with readouts and GPS for my car. :bounce:


----------



## Curt_Planted

Glad your enjoying your Arduinos Mustang! I go in spurts of obsession with it... just picked up a relay to combine co2 solenoid control with my lighting program.


----------



## Curt_Planted

I have attached the latest sunrise/sunset code (siesta lighting) with custom accent at start and end. Use the RTC_time_set code to set the RTC timing with no error. Configure the time in that code to be 19 seconds after you press upload. Do not open the serial monitor. Then immediately upload the time check code, open the serial monitor and confirm it matches you computer's time. Without doing that there is an offset of the timing of the RTC with the typical time setting method (from the difference between compile time and how long it takes the code to upload and start running). 

Once the RTC is set, you just need to configure the times in the main code that you want the lighting to change and the color levels of the custom accent. The notes should guide you. :smile: I will be ordering a relay module to control my co2 solenoid (the one I have won't work, current capacity is too low). Aquatech still hasn't gotten back to me about the current draw of the CO2 solenoid.  I will integrate this with the present code when I get it set up. If anyone else wants CO2 control, I will upload it. Let me know!

EDIT: Thanks to Zodduska I discovered I uploaded the wrong code for the Time check and it was resetting the clock every time. It has now been corrected and uploaded.


----------



## zodduska

Awesome!! Can't wait to give this a shot, It'll be nice not to have to reset the time every time I power down the tank. Thank you!


----------



## Curt_Planted

Your welcome! Thanks for catching my dumb mistake on the time check code! :icon_redf


----------



## zodduska

No problem! It's working great, I changed "int tstep1=2000;" to 5000 so I can enjoy the ramping a bit more. I'd never tried a siesta lighting schedule before and I'm really liking it, I reduced it to two five hour photoperiods because I was getting a little too much algae. I'd be interested in whatever you do to add the co2 control, if you wouldn't mind sharing what parts are needed and connecting them to the Arduino as well I'd definitely give it a go.


----------



## Anor

@curt_planted, first of all thank you for your code. But i have some questions, i haven't uploaded it yet because i'm a little confused (NOOB). This code seems to only dim down and up on set times, is it possible to add the full spectrum in between dimming up and down, or any other preset? Maybe it's just me but i don't see how.


----------



## Curt_Planted

Your welcome. So just to clarify how it works, the lighting level stays at whatever it was last set at. So if a lighting change time passes it activates the sunrise function and it stays at the full spectrum setting until the next lighting change (a sunset or custom accent). You can only change the custom accent function by inputting the lighting level for each color. It's described in the notes how to manually input it in the function itself instead of just using the prewritten variables in the user input area. The code can easily be changed for whatever you want if you understand basic functions and if conditionals. The real issue is just that I have not adapted any of the functions except sunrise, sunset, and custom accent to keep the color tracking variables up to date. Without updating them the custom accent function will not work appropriately because it depends on knowing what the current lighting level is to get it where it wants to be. Loose track of that and its going to do weird stuff.


----------



## Curt_Planted

Zodduska, it will be a really simple addition to the code. I will add in a co2 start up offset variable so you can set how far in advance the co2 turns on before the lights, and a separate one for how early it turns off before the lights do. It will just be an analog write to a pin to activate a opto-isolated relay that will power the co2 solenoid. I will be using a 7.99 relay module from Sainsmart and it is already on the way.


----------



## Mustang5L5

Interested in the CO2 function as well. Just checked out the relay module and probably would be interested in adding this later on.

Just got the V4.1b up and running with a single thermometer for my setup. 

Couple general questions for the entire thread....

#1) Anyone find a good, visually appealing case to use with a 20x4 LCD to package this all up? I've found some for 16x2's, but want to use a 20x4 for temp display

#2) I want to dim the LCD at night so I do not see the bright display at night. Is there a way to accomplish this?


----------



## Curt_Planted

I'm thinking for the case your better off going DIY. I'm trying to figure out how to cut a square neatly into a plastic project box I bought at radio shack. Hot knife? 

If there is a good case out there with the room needed for stacking the arduino, lcd, 5v power converter board and the inch tall relay module, I'd like one as well!

I did get the relay in and got it all wired up and working. The last couple days I've had the relay voltage hooked up to an LED while awaiting the relay, so I can confirm it works before actually controlling the co2 with it. Still working the bugs out though!


----------



## Mustang5L5

I think I'm going to do something like this

http://www.spikenzielabs.com/Catalo...h=2_61&zenid=81422c9ef26ad028b419d0e2ae7f00eb

They only have stands for a 16x2 or the 128x64 display. 

Basically, what I'm going to do is make a project box to hold the boards, and the various wiring from the temp sensor, the power cord, the IR LED, and then run a small multi-conductor to the LCD just for the display. Then, i can put it on my desk next to the tank and use it as a clock, as well as let me know what the tank temp is, room temp, and what mode i'm on. The project box can stay under the tank out of the way and I'll run the IR LED wire up to the light to control that. I've ordered a waterproof DS18B20 with a 3 foot cable, so that should be long enough to reach the tank. 

Thing is, i'm using a 20x4 display, so no stand. I might have to fab my own somehow. 


I might try to integrate the 128x64 display. I know it's a different code than what the 16x2/20x4 uses. 

Seems like the more i read and research, the more i learn and want to add.


----------



## Curt_Planted

I know what you mean about adding stuff! I'm about to add a darn touch screen! Lol


----------



## Curt_Planted

Just finished up the relay... was a bit perplexed by this! I wanted it to keep the power off when the arduino is reset or the control pin is not configured (or comes unplugged) so the relay doesn't keep the co2 on 24-7 in event of a connection issue. One would assume that the relay would then of course be operating in normally off mode. Umm... not so much. In order to do that I had to set the relay up to turn on when the pin is low and turn off when high otherwise if the control line is unplugged or arduino is powered off the relay turns on... makes no sense! but I have it working! lol, I tried it wired up to the opposite way as well and it is off when voltage is low, on when high, but when the pin is disconnected it turns on... :confused1:. I gotta be missing something! For a mechanical relay does it matter if I connect the power wires to the same terminals but switch them around? Maybe because this relay uses an second 5v source to reduce the control current, the control voltage disconnects that second 5v source that actually powers the relay coil? then the high off low on would still be normally off...

I also configured a second pin to act as a second output that outputs high when co2 is on to light an led to act as an indicator. That may also work well for anyone with a more logically operating relay than my Sainsmart! :icon_roll 

My plan is to keep this relay (a sainsmart 4 channel mechanical relay $7.99) and all the higher voltage stuff in the nice little $8 project box I got from radioshack and just velcro a proper case for the arduino on top. I was surprised how well the outlet plug-in seated in the side wall! I grabbed the output plug from the $4 walmart analog timer and wired the hot black line straight to the relay and then bypassed earth ground and ground straight into the power input wires. 

The code has a user set co2 prestart (before lighting turns on) and prestop (before lighting turns off). For my setup the inline reactor takes a while to dissolve the CO2 in it, so I notice my fish gasping if I wait til the lights turn off to shut off CO2. I'll post pics and code tomorrow. Hopefully I will wake up to that indicator led lit and the co2 on with the lights. :icon_mrgr


----------



## Mustang5L5

Curt_Planted said:


> Just finished up the relay... was a bit perplexed by this! I wanted it to keep the power off when the arduino is reset or the control pin is not configured (or comes unplugged) so the relay doesn't keep the co2 on 24-7 in event of a connection issue. One would assume that the relay would then of course be operating in normally off mode. Umm... not so much. In order to do that I had to set the relay up to turn on when the pin is low and turn off when high otherwise if the control line is unplugged or arduino is powered off the relay turns on... makes no sense! but I have it working! lol, I tried it wired up to the opposite way as well and it is off when voltage is low, on when high, but when the pin is disconnected it turns on... :confused1:. I gotta be missing something! For a mechanical relay does it matter if I connect the power wires to the same terminals but switch them around? Maybe because this relay uses an second 5v source to reduce the control current, the control voltage disconnects that second 5v source that actually powers the relay coil? then the high off low on would still be normally off...


I assume this is the relay you are using?










I just got one myself yesterday. I haven't wired it up, or played with code, but looking at the 4 relays, it looks like there is a normally open, and normally closed position. 

Looks like if you use the two left pots, it's normally closed, and using the two right, it's normally open?

Again, have yet to put power to it, but that's how I took it. I was going to put the line in the center and have the relay ON when powered so it fails normally open if the power goes out. 

Could be wrong though


----------



## Mustang5L5

Curt_Planted said:


> I know what you mean about adding stuff! I'm about to add a darn touch screen! Lol


I'm making one for myself and a friend....and he just requested a PH probe in addition to CO2 relay...


So looks like I'm going to wait for my 128x64 display to show up, and figure out how to modify the code to display on that and then add CO2 and PH to the Temp


----------



## Curt_Planted

Mike, that's the same one. Your right about the normally opened/closed but the control voltage is inverted for normally closed. High=off, Low=on, and it turns off if the control signal is disconnected or the arduino looses power. Not sure if this has to do with using the jumper across the vcc pins (not using a second power supply and forgoing the opto-isolation). 

BTW, the updated code is working great, no issues in 2 days.  I'm still lagging on commenting the changes properly so maybe sometime this weekend I will get around to it and upload it. I have also added additional robustness to the code. No more issues no matter if you load it just prior to a lighting change. 

There is also a new moonlighting function that comes on at sunset in the evening (the second sunset). The user sets how long it stays on after sunset and can set it to zero if it's not wanted. It uses some tricky math to go from full moon at month start to no moonlight at mid month and then back to full moonlight for the end of the month (16/42 blue, 8/42 red and green). Any higher lighting and I notice my fish don't reduce their activity. I have 2 of these lights (36") above my 36" long/24" high 50g so that may differ based on other peoples lighting.


----------



## Curt_Planted

Here is the latest code with moonlight and co2 control. Hope someone finds it useful. Had to zip the text doc as the limits on text docs are pretty skimpy on this forum. There is plenty of cleaning up to do in the code to minimize the file size but it's not even using half the memory of my arduino so if it aint broke....


----------



## zodduska

IThe full spectrum ended up being too intense for my tank so I reverted to non ramping code for now. Curt, would it be possible for me to change something simple in your code to have it ramp up to say 50% intensity?


----------



## Curt_Planted

That should be pretty simple. Anywhere you see a sunrise command swap it for a custom accent function with the lighting level you want. FYI I'm down to 9 hours lighting on my tank. These lights are brighter than they appear!


----------



## zodduska

like this?



Code:


//these variables below store the number of steps each color is above completely dark for 
//your custom accent color. To get this number, set your tank to the lighting you want 
//then go through each color one by one and reduce the lighting to dark counting the number of 
//times you had to press the down button to do so. There are about 21 steps in brightness for the 
// remote for each color but they can vary with the length of the button press. This code uses the light's max 
// resolution of 42 steps so you will have to multiply the number of button presses by 2 with
// 42 being the max and zero meaning the led color is off. 

int Blue1=0;
int Red1=42;
int Green1=10;
int White1=6;

int Blue2=21;
int Red2=21;
int Green2=21;
int White2=21;




Code:


if ((Tnow > LowSunriseT) && (Tnow < BrightenUpT))
   {
   CustomAccent(Red1,Green1,Blue1,White1,tstep1);
   
   }
   else if ((Tnow > BrightenUpT) && (Tnow < SunsetT))
   {
    CustomAccent(Red2,Green2,Blue2,White2,tstep1);
   }
   else if ((Tnow > SunsetT) && (Tnow < SunriseT))
   {
   Sunset(tstep1);
   }
   else if ((Tnow > SunriseT) && (Tnow < BrightenDownT))
   {
   CustomAccent(Red2,Green2,Blue2,White2,tstep1);
   }
 else if ((Tnow > BrightenDownT) && (Tnow < LowSunsetT))
 {
    CustomAccent(Red1,Green1,Blue1,White1,tstep1);
   }
 else if ((Tnow > LowSunsetT) && (Tnow < LowSunsetT+Evening_Moon*60)){
   MoonPhase(tstep1);
   }
   else if ((Tnow > LowSunsetT+Evening_Moon*60) || (Tnow < LowSunriseT)){
   Sunset(tstep1);
   }
   }

this part too?



Code:


else if ((Tnow >= BrightenUpT) && (Tnow < SunsetT)){
   Serial.print("Tnow ");
   Serial.println(Tnow);
   Serial.print("BrightenUpT ");
   Serial.println(BrightenUpT);
  RedNow=0;
GreenNow=0;
BlueNow=0;
WhiteNow=0;
digitalWrite(CO2RelayPin, LOW);
digitalWrite(CO2RelayLED, HIGH);
   Serial.println("CO2 On");
   CustomAccent(Red2,Green2,Blue2,White2,tstep1);
   }
else if ((Tnow >= SunsetT) && (Tnow < SunriseT)){
Serial.print("Tnow ");
   Serial.println(Tnow);
   Serial.print("SunsetT ");
   Serial.println(SunsetT); 
 digitalWrite(CO2RelayPin, HIGH); 
 digitalWrite(CO2RelayLED, LOW);
   Serial.println("CO2 Off"); 
   
Sunset(initial_tstep);
}
else if ((Tnow >= SunriseT) && (Tnow < BrightenDownT)){
  Serial.print("Tnow ");
   Serial.println(Tnow);
   Serial.print("SunriseT ");
   Serial.println(SunriseT); 
  digitalWrite(CO2RelayPin, LOW);
  digitalWrite(CO2RelayLED, HIGH);
  Serial.println("CO2 On"); 
  RedNow=0;
GreenNow=0;
BlueNow=0;
WhiteNow=0;
   CustomAccent(Red2,Green2,Blue2,White2,tstep1);}




Code:


//initialize color counters to full lighting to assure sunset is completed smoothly
RedNow=21;
GreenNow=21;
BlueNow=21;
WhiteNow=21;

Another quick question, would setting the timers to extend beyond midnight (for the final sunset) screw with the programs time tracking triggers? (ex, 1am is now < than 4pm, rather than 11pm > 4pm)


----------



## Curt_Planted

Looks good. The change to the counter initialization isn't needed, but will speed up any initial sunset (assuming it is never set above 21 lighting level when you start the program). In the interest of robustness at the cost of an extra bit of time to fade down and then fade back up to another color (depending on when you start the code) it may be better to just leave it at 42. 
The cross over from one day to another is something I didn't anticipate would be an issue... but if someone works night shift I guess that could come up. Is that a hypothetical question, or a real issue for you? 

So half lighting is enough? What plant's are you growing?

Edit: Actually since the sunset is followed with a zero light M4 command, the lighting counters don't matter for the initialization... there would at worst case be a sudden jump to darkness if the lighting level was above 21 for any of the colors.


----------



## zodduska

Curt_Planted said:


> Looks good. The change to the counter initialization isn't needed, but will speed up any initial sunset (assuming it is never set above 21 lighting level when you start the program). In the interest of robustness at the cost of an extra bit of time to fade down and then fade back up to another color (depending on when you start the code) it may be better to just leave it at 42.
> The cross over from one day to another is something I didn't anticipate would be an issue... but if someone works night shift I guess that could come up. Is that a hypothetical question, or a real issue for you?
> 
> So half lighting is enough? What plant's are you growing?
> 
> Edit: Actually since the sunset is followed with a zero light M4 command, the lighting counters don't matter for the initialization... there would at worst case be a sudden jump to darkness if the lighting level was above 21 for any of the colors.


Great, thank you I'll give it a go tonight! 

The light is suspended about 10-12" over the substrate on a 12 gallon long Mr. Aqua which a couple days ago switched to low-medium light, stopped dosing and turned off co2 for my shrimp's sake. I've got downoi, fissidens, xmas moss, rotala indica, rotala mini butterfly, hydrocotyle tripartita, Alternanthera reineckii, anubias nana petite and dwarf hairgrass. I'm sure some of them won't make the transition. Even with co2 and lots of ferts the Full Spec was too much, so 50% is just a starting point for this. Currently I'm using Cloud4.

Ideally I like the light to be on when I get home from work or shortly after (around 4pm) and sunset at midnight, after that accent light or moonlight until 1 or 2am since I'm usually still awake.


----------



## Mustang5L5

That's a nice tank. 

I'm using a 48" fixture over a 24" deep Oscar tank...so my plant growth isn't as good. But unfortunately i'm pretty limited on what I can grown in there anyway that won't become Oscar-fodder. 

Still trying to assemble my setup into something a bit visually appealing. Waiting on long 3Meter temp probes

The relay is for Co2 on a friend's planted tank, so I'll need to dig into that at some point. Just pretty much waiting on parts from China right now with a prototype sitting on my work bench waiting for me to make 2 final versions from it.


----------



## Curt_Planted

Beautiful tank!! Here's mine...


----------



## zodduska

Thanks guys! Curt I really like your tank, very cool fish too!


----------



## Curt_Planted

Thanks Zodduska! It's definately a different look than yours. Yours is more aquascape art, mine is more just jungle with too many fish!  I don't even need to dose KN04. Currently my stock is:

2 Bolvian Rams
2 Denisonii Barbs
1 Pearl Gourami
3 Otos
2 Serpaes
1 Black Phantom
1 Rummy nose tetra
5 harlequins
5 pristellas
2 Saemese Algae eaters
7 Amano shrimp (always hiding on the heater)
4 Nerrite Snails
Some MTS

I still want more! lol, I have such a variety thanks to the loss of previous schools from a callamus worm infection when I first got into fish keeping! Thats a rare and brutal worm that will take out half your tank if your lucky! :frown: I've only been at it since last July and jumped from 10 to 29 and now to 50 gallon for the last 5 months or so.

Have you had to fight bba yet? Was still battling it in that pic but it has been staying away nicely now that the co2 is more dialed in and consistent from the relay control. A bit of excel overdosing (cheating... I know, I know) with a flagfish and 2 SAE's cleared it up something fast! :red_mouth


----------



## zodduska

Thanks! That 50 looks huge, I love the Denisonii Barbs. BBA is a a real pain, I haven't had to deal with it much in this tank but I have in the past. Usually increasing Co2 stability and amount is the key, it'll actually disappear on its own once its dialed in. The trick is you want enough surface agitation to be able to achieve a high concentration and equilibrium. You don't want it starting low and continually creeping up throughout the photo period. I used a diy Rex Reactor and started my co2 about three to four hours before lights on. Another method if you can't increase co2 for the fish's sake is to reduce light intensity. Once you get those dialed in you won't see much BBA.

Also increased surface agitation allows higher concentration of co2 before fish begin to show signs of stress because there's more oxygen in the water to offset the suffocation effects of co2.

Edit: just noticed you've got a HOB filter so you should be set as far as surface agitation goes, might just need to start the co2 earlier or up the amount a bit.


----------



## Curt_Planted

Oh I've got it covered now. The issue was the co2 was driven off of a very cheap mechanical timer and the lights a precision microcontroller. There was alot of timing drift from the timer and turning the co2 on early was always a crap shoot with the 30 minute steps on the mechanical timer. Now that I have the lighting and co2 controlled from the arduino with precise prestart times the tank is the cleanest I've ever seen! Really brings out the bright greens when the leaves are perfectly clear of algae.


----------



## Mustang5L5

Biggest holdup for me was how to display the LCD panel. Didn't find much for a 20x4, so ended up making my own. I should have a pic soon.


----------



## chinton

I wanted to say thank you to everyone who contributed to this thread. I had been wanting to get into arduinos for awhile now but hadnt had a use up until now.

I purchased by Satellite+ a month or so ago when it was onsale. I believe this may be the newer model since the IR receiver is different than what I have seen posted here. It is a small LED at the end of the lead. I did not have to modify frequency or IR codes. Below is the information from for my build.

Parts I used:
-Sainsmart Uno R3 (amazon)
-Sainsmart TinyRTC w/ scl/sda passthrough (amazon)
-Sainsmart 20x4 LCD with I2C (amazon)
-bread board (amazon)
-IR Detector/Emitter set (radioshack)
-100 ohm resistor (radioshack)

Code Base: 4.1b
-commented out thermometer code for now but will implement later
-added alarms to poweron/off in the am, afternoon, and after midnight to provide "siesta"
-added alarms to turn on/off lcd backlight when I am away
-modified functions to display current mode on LCD

Troubleshooting:
1. LCD would not display via pass-through on RTC. This may be due to my jumpers. My fix was to wire the lcd in series via the breadboard.
2. Time was displaying 2hrs and some minutes ahead. Had to manually set the time on the RTC.

Future plans:
-implement thermometer reading
-control outlet for heater based on temperature (over temp protection if heater sticks ON)


----------



## Curt_Planted

Welcome Chinton! Good idea on the thermometer backup! When I was shopping for heaters I read quite a few horror stories of people's fish getting cooked. If your using a water level switch for an alarm it would be good to auto shut off the heater also when the water level gets low in case you forget.


----------



## nayr2

Oh man, maybe I should have googled "satellite led plus arudino" BEFORE getting my prototype working because this thread would have been super helpful. I thought I was soooo clever that nobody would have done this yet!

Nevertheless as of yesterday I have a 24 hour day/night cycle working with fades from blue to orange, yellow, and full power, and then back down to the darkest blue at night.

http://chattypics.com/files/photo3JPG_aoorjvs6di.jpg

My code is a bit different as it doesn't use alarms, it can fade from any RGB/Brightness to any other RGB/Brightness over any set period of time. I set a target color and the duration and it will send the IR codes to fade as smoothly as possible over that period of time. Right now I have it changing every hour, but I'm planning to do clouds passing by and other weather variations and effects. I'm still working on things but I'll post it here for anybody interested, or for the next sucker that forgets to google before getting their project working :redface:


----------



## Curt_Planted

Nayr2 that sounds like my code! Great minds!  Post your code so I can steal any improvements! :icon_mrgr


----------



## nayr2

I've done very little testing and no re-factoring or re-evaluation of my implementation, but if you're interested I've posted the code here: 

https://github.com/nayr974/Arduino-...umLightController/AquariumLightController.ino

I'll probably be working on this for the next few weeks, as I really would like weather patterns. I picked up a wifi shield and I'm going to try to get the weather in real time for a location and have it represented. I also have a water circulator I'd like to PWM control and have tied into the weather states.


----------



## jeffkrol

nayr2 said:


> I'll probably be working on this for the next few weeks, as I really would like weather patterns. I picked up a wifi shield and I'm going to try to get the weather in real time for a location and have it represented. I also have a water circulator I'd like to PWM control and have tied into the weather states.


now "that" is fancy.. So are you going to add rain as well???

The Ulitmate tank. Looking at it you can tell the weather outside.. 
Well except for me.. snow and 30 below aren't really conducive to a tank..


----------



## Mustang5L5

So, after looking around and not seeing any 20x4 LCD box's I liked, i decided to make my own. 

I had a few resources i could use, so I designed up a stand in CAD, and then used a CNC laser cutter to cut the design out of 0.080" Aluminum. 

So from my CAD model I got this









And then I bent it up into this


















My first trial, and i found some things I'd like to modify. Need to add a mounting location and screws for my RTC. Also want to add a few more 1/2" holes for bulkhead connectors for my wires. The one wire you see is for my long temp probe to go into the tank. Need another for my IR wires. Plus future expension :red_mouth

Once i get the layout right, i'll solder up the wires and power it up. 

I also have to think about how to mount the arduino on it. 

So while i might need help with coding....I'm an ME so this sort of mechanical design is up my alley :thumbsup:


----------



## rachnhaze

following.. builiding a led controller for my saltwater tank and would love the real time weather simulation


----------



## bigd603

nayr2 said:


> I'll probably be working on this for the next few weeks, as I really would like weather patterns. I picked up a wifi shield and I'm going to try to get the weather in real time for a location and have it represented. I also have a water circulator I'd like to PWM control and have tied into the weather states.



Now you've gone and stolen MY idea! And here I was thinking I was SO clever that no one else would have that idea... 

In all seriousness, do you think tying the local weather would be easier by connecting the arduino to a raspberry pi, and having the pi get the weather status, or does the arduino have the ability to get that data when connected to ethernet/wifi? Clearly I haven't done any work on this just in the idea stage.


----------



## bigd603

Actually, it would be really cool to set this up in a biotope tank, and have the setup grab weather data from the location you are trying to mimic. Sitting in your living room, and your fishtank starts to flash. "oh looks like there's a lightning storm in lake Venezuala."


----------



## FischAutoTechGarten

Subscribed.
Cool. Building my Own Aquarium Controller. I'll compare notes as I make more progress.
Cheating a bit on my Probe Measures.. using some components from Atlas-Scientific.com.


----------



## nayr2

I find that I'm getting more lost commands than I'm happy with. If I go up 42 steps and down 42 steps I'm always a few places off from where I expect to be. Sometimes I can see a command sent and the light just not do anything.

I've tried soldering my IR output directly to the middle pin of the IR receiver of the light and that did help, but I'm still seeing it miss commands. Does anybody have an idea to get this as close to 100% as possible?


----------



## Curt_Planted

Mustang, that looks awesome!! Are you going to encase the whole of it with additional metal panels? Wish I had access to the cutters at my work but I'm sure you know how the big soul-less corporations can be! 

Nayr, if you look at my code each sunrise or sunset is followed with a hard coded command to the final lighting setting to prevent drift (either full spectrum or all lights off). I've never really noticed it making a big jump, but even being off a single lighting level will add up to a big difference over just a few days if there is nothing to make sure it is reset at the right lighting. Too bad we can't just read the lighting level from the light itself....


----------



## nayr2

Thanks Curt. I do the same thing at night, where I have one of the preset set to just a dim blue. However I see the drift during my sunset where at dusk I only wanted to have a dim red/orange only up one or two brightness. Sometimes I end up with a green sunset


----------



## Mustang5L5

Curt_Planted said:


> Mustang, that looks awesome!! Are you going to encase the whole of it with additional metal panels? Wish I had access to the cutters at my work but I'm sure you know how the big soul-less corporations can be!


I wasn't going to get too crazy with it. I do actually like the rear open just to show off my handiwork. Down the road, i might tweak things and recut something that encloses it more. 

I still need to figure out how to wire the temp sensor in with the pull-up resistor. Not sure if i can do it on the protoshield I bought. It does have a location for the LCD wiring and potentiometer which made hooking that up easy. RTC hookup was also easy. 

Getting there...Still a bit more to do


----------



## nayr2

That really does look pretty slick Mustang


----------



## Curt_Planted

nayr2 said:


> That really does look pretty slick Mustang


Kits?


----------



## Curt_Planted

Nayr, set the lighting where you want it and hard code it at each setting to one of the custom mode buttons on the remote. Then after each transition just send that command so it ends up where you want. Also make sure there is atleast a 200 ms delay between each transmission (I noticed them failing without it when looping). I haven't noticed any drift so not sure what is different...


----------



## Mustang5L5

Curt_Planted said:


> Kits?


It would be nice, but can't. My work gives me a little leeway with side projects, but not going to push it.


----------



## AnotherHobby

Today I just ordered 2 of these lights for my 60cm tank. I had never dropped into this thread before because I didn't own one. Reading through this, I'm super pumped! This is awesome! I happened to have all of the parts on hand from other projects, so I threw one together today. Near as I can tell, it should work when the lights get here. It's the same controller that I also use to run dosing pumps.

I don't like leaving stuff breadboarded, so I still need to solder it all up on that proto shield in the background.


----------



## nayr2

Floating what? Floating GROUND, NOOB. I figured out why I was losing commands randomly. When I went from the IR LED to hard-wiring it to the lights I pulled out the pulldown resistor I was using. I can't believe I didn't think of it but that's what you get when you drop out of electronics for software engineering and don't do any projects at home for a few years.

Glad I've got that sorted out, but I've been busy getting everything else set up. Found a stand and I've been rebuilding it to my liking. Ran plumbing and electrical, put in the sump and filter. Tomorrow, I drill the tank and attach the overflow. 

Here's a timelapse of a sunrise. My camera ran out of juice before I could record a whole 24hours: https://www.youtube.com/watch?v=R5EA7GHj2FQ&hd=1


----------



## AnotherHobby

nayr2 said:


> Here's a timelapse of a sunrise. My camera ran out of juice before I could record a whole 24hours: https://www.youtube.com/watch?v=R5EA7GHj2FQ&hd=1


That looks awesome!


----------



## biglos201

AnotherHobby said:


> That looks awesome!


yeh, thats dope! So you set it to ramp up and down thats exactly what I'm looking for! I ordered the Ramp Timer pro but I hear its a bit clunky. I'll give it a shot, sucks thatI can't use my other dynamic mode


----------



## Curt_Planted

Looks great Nayr! Did you specifically have it up the reds more at the start? I've been meaning to try playing with that to make the hues match the changes in a real sunrise (red to orange to yellow to white).


----------



## nayr2

Yeah a little red first, then a bit of green to make yellow, and finally blue. This project is on the backburner as I've been setting up the rest of my tank. I drilled it, sumped it, set up co2 injection last week, and have been spending most of my time admiring my very healthy aquascape of glorious hair algea.


----------



## Linwood

I'm working on doing a similar setup but with Raspberry Pi, so it is easier to network, and just ... well, because I wanted to do something with pi.

Was any solution found to the power toggle? I'm thinking of running all four colors to minimum intensity as "off" instead of using power, though that's not exactly a pleasant option. My second thought was to put a photo sensor that has the light visible, when the power is toggled, and if it goes the unexpected way (on -> dimmer) then toggle again, but that sounds really kludgy also. The final alternative which is more hardware intensive than may be worth it is to put a relay in and run either AC or DC through it before the light, instead of using the IR code for power.

All of these are a lot more complex than a discrete on/off code, but I gather you never got one? 

PS. Is there any interest in a Pi version, if so when I get this done I'll post something. At present I have a web-based remote image where you click buttons and all work, but no scheduling. My intent is a simple minded web based scheduling as well, and then if I get ambitious some sensors in the tank to display as well.

[EDIT] PS: I found I could turn it off reasonably quickly by sending a white command followed by 50 white-dimmer commands (50 is automated, minimal pause, it's maybe 5 seconds or so). Another alternative but it depends on preprogramming would be to set a preset (e.g. M4) to completely dim on all LED's, then invoke that as "off", but that depends on the user not changing anything. So this handles a pseudo "off" discrete code, I have no idea how to do an "on" without knowing the initial state, but in terms of using it as a scheduler in this way I can just not ever send a power toggle.


----------



## AnotherHobby

Linwood said:


> Was any solution found to the power toggle? I'm thinking of running all four colors to minimum intensity as "off" instead of using power, though that's not exactly a pleasant option. My second thought was to put a photo sensor that has the light visible, when the power is toggled, and if it goes the unexpected way (on -> dimmer) then toggle again, but that sounds really kludgy also. The final alternative which is more hardware intensive than may be worth it is to put a relay in and run either AC or DC through it before the light, instead of using the IR code for power.
> 
> All of these are a lot more complex than a discrete on/off code, but I gather you never got one?


I'm building a touch screen controller that has relays so that I can just physically power them off. The relays are very easy to interact with, and a bank of 8 was only $12. If you just want one, then a single relay is $3 from DX.com.


----------



## Linwood

AnotherHobby said:


> I'm building a touch screen controller that has relays so that I can just physically power them off. ....[/URL].


I may end up just going that route, though I was trying not to have to loop either DC or AC power from the lamp through the device. But maybe that's not a goal worth it. I need to look at the DC connector, maybe I can put a male and female version of it on the PI with relay in the box, and it will look OK, plus not have to splice the cable. I'd really rather not have 110v running into the PI case. 

I don't like what I did with the White-then-dim, as it has to flash white if you were already in a dim scene, so think next try is to require programming M4 to off, and use it instead (I can actually automate the programming of M4). 

Then off to scheduling.


----------



## AnotherHobby

I'm trying to figure out how to send the M1 thru M4 commands to actually tell the light to save a mode. To do this with the factory remote, you hold the button down for 4 seconds. The code here sends the command quickly though. Does anybody know how you'd send the 4 second command to save one of the memory button spots?


----------



## Linwood

AnotherHobby said:


> I'm trying to figure out how to send the M1 thru M4 commands to actually tell the light to save a mode. To do this with the factory remote, you hold the button down for 4 seconds. The code here sends the command quickly though. Does anybody know how you'd send the 4 second command to save one of the memory button spots?


I have that working on the Raspberry PI using LIRC. I do a SEND_START, then wait 5 seconds and SEND_STOP of the Mx key. 

The up/down are similar, I just SEND_START on the down for 9 seconds, and then SEND_STOP.

There are issues with this on the rPI, in that there appears to be a certain amount of buffering somewhere, so the times need to be experimented with a bit.

Here's the whole bash script I'm using. It's just brute force and not pretty, and 90% of the code is generating the HTTP response, but I'm still in the experimental stage and it's easier to instrument/debug/change like this:

The only significant portions are the SLEEP and IRSEND which should be somewhat self explanatory. 

I also tried this in a straight loop, but on the pi the SEND_ONCE is asynchronous, so there's no way to know when it is actually done, so if you send too many too fast you get an error. On the Arduino, you may have more control over the buffer/timing, but my suggestion is just send them as fast as you can for 4+ seconds.



Code:


#!/bin/bash
if ERROR=$(irsend SEND_ONCE ledplus White  2>&1 >/dev/null)
then
    sleep 1
else
    echo "Status: 500 Error"
    echo "Content-type: text/plain"
    echo ""
    echo "Error code from White process = $ERROR"
    exit
fi
if ERROR=$(irsend SEND_START ledplus WhiteDown  2>&1 >/dev/null)
then
    sleep 5
else
    echo "Status: 500 Error"
    echo "Content-type: text/plain"
    echo ""
    echo "Error code from WhiteDown process = $ERROR"
    exit
fi

if ERROR=$(irsend SEND_STOP ledplus WhiteDown 2>&1 >/dev/null)
then
    sleep 1
else
    echo "Status: 500 Error"
    echo "Content-type: text/plain"
    echo ""
    echo "Error code from WhiteDown process = $ERROR"
    exit
fi

if ERROR=$(irsend SEND_START ledplus M4  2>&1 >/dev/null)
then
   sleep 1
else
    echo "Status: 500 Error"
    echo "Content-type: text/plain"
    echo ""
    echo "Error code from WhiteDown process = $ERROR"
    exit
fi

if ERROR=$(irsend SEND_STOP ledplus M4  2>&1 >/dev/null)
then
    echo "Status: 200 Success"
    echo "Content-type: text/plain"
    echo ""
    echo "Sent successfully"
else
    echo "Status: 500 Error"
    echo "Content-type: text/plain"
    echo ""
    echo "Error code from WhiteDown process = $ERROR"
fi


----------



## AnotherHobby

In case anybody cares, here is how I got it to save the 4 memory positions. You basically just send it the memory button and then blast the footer for 2 seconds. I'm pretty new to C/C++, so there is probably a better way of doing this. This is for memory button 1.



HTML:


void saveM1()
{
  SendCode(0x20DF38C7, 1);  // send M1 button

  unsigned long currentMillis = millis(); // get current millis
  unsigned long blastMillis = millis(); // get current millis

  while ((blastMillis-currentMillis)<3000)  // blast IR footer for 3 seconds
  {
    irsend.sendNEC(0xFFFFFFFF,32);
    blastMillis = millis(); // get current millis
  }
}


----------



## drftndakota

Im curious if anyone is having trouble communicating with a new LED+ bar? have they changed the IR coding? i went back to the beginning to test for new ir codes but i am having trouble figureing out how to take the data from the serial monitor and turning that into the appropriate hex codes to test for a different coding. Just curious if anyone might be able to lend a hand on this. I can post a copy of the serial data if that is helpful.

Thanks


----------



## Linwood

I don't know how old mine is (I bought it less than a month ago but... ). 

I can provide my codes, however these are on Raspberry Pi using LIRC, so I'm not completely sure if they map to what you are doing, but I think they may.

This is more or less top to bottom left to right. I use M4 for "Off".

I guess there's no reason they might not have changed codes, though it would seem a bit silly as they'd have to reprogram that remote itself and keep two different part numbers, etc. There's no part number on mine but there is a V.1.0 on the remote itself.



Code:


          Orange                   0x3AC5
          Blue                     0xBA45
          RosePink                 0x827D
          Power                    0x02FD
          White                    0x1AE5
          FullSpectrum             0x9A65
          Purple                   0xA25D
          PlayStop                 0x22DD
          RedUp                    0x2AD5
          GreenUp                  0xAA55
          BlueUp                   0x926D
          WhiteUp                  0x12ED
          RedDown                  0x0AF5
          GreenDown                0x8A75
          BlueDown                 0xB24D
          WhiteDown                0x32CD
          M1                       0x38C7
          M2                       0xB847
          M3                       0x7887
          OffDim                   0xF807
          MoonIntense              0x18E7
          MoonFading               0x9867
          MoonFadingCloudy         0x58A7
          DawnGentleCloud          0xD827
          BrightWhiteRollingCloud  0x28D7
          CloudIntense             0xA857
          CloudGentle              0x6897
          CloudBright              0xE817
          RainIntense              0x08F7
          RainSlower               0x8877
          RainLow                  0x48B7
          GentleFade               0xC837


----------



## drftndakota

Is your device ID the same as the one in the original code?

Thanks for the quick reply my remote says V1.0 as well
so maybe I'm having issues elsewhere.. Since the codes i am using are the same as yours..


----------



## Linwood

drftndakota said:


> Is your device ID the same as the one in the original code?
> 
> Thanks for the quick reply my remote says V1.0 as well
> so maybe I'm having issues elsewhere.. Since the codes i am using are the same as yours..


Sorry, was at a ball game. 

I didn't use the code in this thread, I recorded them with a different library, so I suspect it calls things something different. Here are the general settings I got: 



Code:


  name  ledplus
  bits           16
  flags SPACE_ENC|CONST_LENGTH
  eps            30
  aeps          100

  header       8964  4453
  one           598  1633
  zero          598   521
  ptrail        588
  repeat        8971  2210
  pre_data_bits   16
  pre_data      0x20DF
  gap           107308
  toggle_bit_mask 0x0

Most of these are in decimal, but it put the "pre_data" in hex, which seems to match the OP's header.


----------



## AnotherHobby

drftndakota said:


> ...i went back to the beginning to test for new ir codes but i am having trouble figureing out how to take the data from the serial monitor and turning that into the appropriate hex codes to test for a different coding...


This little sketch will spit IR HEX codes right into your serial monitor for you. It's super easy, and no need to "decode" or figure anything out.



Code:


#include <IRremote.h> // use the library
int receiver = 11; // pin 1 of IR receiver to Arduino digital pin 11
IRrecv irrecv(receiver); // create instance of 'irrecv'
decode_results results;
void setup()
{
  Serial.begin(9600); // for serial monitor output
  irrecv.enableIRIn(); // Start the receiver
}
void loop()
{
  if (irrecv.decode(&results)) // have we received an IR signal?
  {
    Serial.println(results.value, HEX); // display it on serial monitor in hex
    irrecv.resume(); // receive the next value
  }  // Your loop can do other things while waiting for an IR command
}


----------



## drftndakota

Thanks for those that helped. I tested my remote and all codes were the same. Somehow I ended up with a bad led went and picked up another and everything works perfect. Anyway i have been working with Curts siesta code i have added serial LCD support to save pins. There is also instructions to switch to a standard lcd breakout.. I have started to merge the random storm from the Indychus code into the sketch as well. currently this is all commented out as i have not had time to finish it. If you want CO2 support all you need is to uncomment that code and it should work..

if anyone wants to add to it or help getting the tstorms working i wouldnt be offended lol..

Just thought i could help out a little all the people that helped me get this started.




Code:


///////////////////////////////////////////////////////////////////
// Current Satellite LED+ Controller II V1.0                     //
//   An Adaptation of code originally by Indychus...Dahammer...  //
//   and mistergreen @ plantedtank.net                           //
//                                                               //
//   This code is public domain.  Pass it on.                    //
//   Confirmed on Arduino duemilanove                            //
//   Req. Time, TimeAlarms, IRremote                             //
//   Modified By Curt_Planted @ plantedtank.net to exclude all   //
//   external hardware, include new custom fade functions        //
//   and automatic timing                                        //
///////////////////////////////////////////////////////////////////
//
// This version uses Ken Shirriff's IRremote library to Rx/Tx the IR codes
// http://www.righto.com/2009/08/multi-protocol-infrared-remote-library.html
// 
// This code does NOT use PIN 13 on the Uno, as do previous versions
// Instead PIN 3, which is a PWM pin, is used for the IR led. So you'll need to connect
// your IR LED to PIN 3 instead of PIN 13 for it to work. If you have a relay to control that 
// CO2 that is active low, off high, connect the control input to pin 4 and an indicator LED
// to pin 7. If your relay is active high, off low, connect it to pin 7.
// Connect the RTC (DS1307) SCL pin to A5 and SDA to A4
// This code also requires that you set the M4 preset of your light to all lights out
// if you use the sunset function for it to work properly. To do this dim all the colors
// of your lights down until they are all out then press and hold m4 for 5 seconds.
// change the lighting setting to another then press M4 to confirm, they will not flash
// to confirm the preset as usual at a no light level!

#include <IRremote.h>
#include <Wire.h>
#include "RTClib.h"
#include <SoftwareSerial.h>
#include <serLCD.h>
// #include <LiquidCrystal.h>

RTC_DS1307 rtc;
IRsend irsend;

int postDelay = 100;         
int randAnalogPin = 0;       // This needs to be set to an unused Analog pin, Used by ThunderStorm()
int CO2RelayPin = 4;
int CO2RelayLED = 7;

// Serial LCD 16x2 must be used connect your RX wire to pin 10 on the uno
SoftwareSerial SLCD(0,10);
serLCD lcd(10); 
// if you dont care about using so many pins or dont have a serial ready LCD comment out the above code as well as  #include <SoftwareSerial.h>, #include <serLCD.h>
// then uncomment the below command you will also need to remove the 9600 from the lcd.begin command 
// LiquidCrystal lcd(7, 8, 9, 10, 11, 12);

// Current Satellite+ IR Codes (NEC Protocol)
unsigned long codeHeader = 0x20DF; // Always the same

// Remote buttons listed left to right, top to bottom
unsigned int codeOrange = 0x3AC5;
unsigned int codeBlue = 0xBA45;
unsigned int codeRose = 0x827D;
unsigned int codePowerOnOff = 0x02FD;
unsigned int codeWhite = 0x1AE5;
unsigned int codeFullSpec = 0x9A65;
unsigned int codePurple = 0xA25D;
unsigned int codePlay = 0x22DD;
unsigned int codeRedUp = 0x2AD5;
unsigned int codeGreenUp = 0xAA55;
unsigned int codeBlueUp = 0x926D;
unsigned int codeWhiteUp = 0x12ED;
unsigned int codeRedDown = 0x0AF5;
unsigned int codeGreenDown = 0x8A75;
unsigned int codeBlueDown = 0xB24D;
unsigned int codeWhiteDown = 0x32CD;
unsigned int codeM1Custom = 0x38C7;
unsigned int codeM2Custom = 0xB847;
unsigned int codeM3Custom = 0x7887;
unsigned int codeM4Custom = 0xF807;
unsigned int codeMoon1 = 0x18E7;
unsigned int codeMoon2 = 0x9867;
unsigned int codeMoon3 = 0x58A7;
unsigned int codeDawnDusk = 0xD827;
unsigned int codeCloud1 = 0x28D7;
unsigned int codeCloud2 = 0xA857;
unsigned int codeCloud3 = 0x6897;
unsigned int codeCloud4 = 0xE817;
unsigned int codeStorm1 = 0x08F7;
unsigned int codeStorm2 = 0x8877;
unsigned int codeStorm3 = 0x48B7;
unsigned int codeStorm4 = 0xC837;
//**********************************USER DEFINED VARIABLES************************************************
//******************define time in hours and minutes that are triggers************************************
 
//these are the times in order for the start of the different lighting stages for a siesta lighting schedule
//(fade up to accent color, full sunrise, sunset, sunrise, fade down to accent color, then sunset.)
//Without sacrificing full spectrum during the day it allows viewing in the accent color in the AM 
//and PM when you are more likely to be home! :) The code will automatically Dim the lights gradually 
// then bring them back up to the lighting for the initialization time in the setup loop

long LowSunriseH=5; //5:30am
long LowSunriseM=45;

long BrightenUpH=6; //5:45am
long BrightenUpM=0;

long SunriseH=10; //11am
long SunriseM=30;

long SunsetH=16;//345pm
long SunsetM=00;

long BrightenDownH=20;//845pm 
long BrightenDownM=30;

long LowSunsetH=21; //9pm
long LowSunsetM=0;

//byte TSDurationH;
//byte TSDurationM;



//time in minutes prior to lights turning on that co2 turns on
long CO2_Prestart=25;

//time in minutes before lights turn off that co2 turns off
long CO2_Prestop=20;

//time in minutes after the last sunset of the day that moonlighting is on. The moonlight will vary in brightness based
//on the day of the month, transitioning from brightest at the month start to dimmest at midmonth, then back to brightest
//by night end.
long Evening_Moon=60;

//  is the global delay between each code transmission (controls fade speed of all lighting changes) keep above 200 ms 
// or transmissions will fail. Each 1000 miliseconds adds about 3 minutes to a sunrise or sunset thus the default of 5000
// makes for a 15 minute sunrise and sunset.
// This variable is an input to all the light changing functions so you also can adjust them manually for each fade by inputing 
// them directly in the functions IE Sunrise(300);
int tstep1=5000; 

//these variables below store the number of steps each color is above completely dark for 
//your custom accent color. To get this number, set your tank to the lighting you want 
//then go through each color one by one and reduce the lighting to dark counting the number of 
//times you had to press the down button to do so. There are about 21 steps in brightness for the 
// remote for each color but they can vary with the length of the button press. This code uses the light's max 
// resolution of 42 steps so you will have to multiply the number of button presses by 2 with
// 42 being the max and zero meaning the led color is off. 

int Blue1=0;
int Red1=42;
int Green1=10;
int White1=6;

//You can also swap the CustomAccent() function in the main loop in for any of the other lighting changes
//or add your own if loops for additional lighting changes. The CustomAccent function has the
//format CustomAccent(Red, Green, Blue, White, Fade speed) See tstep above for fade speed input description
//and as long as you have initialized the counters to the current state, this will adjust the colors up or 
//down as needed to get the color you input. While it can be used for a sunrise or sunset it is better to
//use the built in functions to be sure the color counters do not drift over time due to missed transmissions.

/************* END USER DEFINED VARIABLES ****************************************************************/

int CondDelay=5; //time in seconds that time conditionals are true 

//this defines the time variables that will later store the above times converted to seconds in the day
//convert hours and minutes to seconds in the day
long LowSunriseT=(LowSunriseH*60+LowSunriseM)*60;

long BrightenUpT=(BrightenUpH*60+BrightenUpM)*60;

long SunsetT=(SunsetH*60+SunsetM)*60;

//long TStorm=(12*60+00)*60;

long SunriseT=(SunriseH*60+SunriseM)*60;

long BrightenDownT=(BrightenDownH*60+BrightenDownM)*60;

long LowSunsetT=(LowSunsetH*60+LowSunsetM)*60;

//These are your tracker variables for the current light levels of each color
int BlueNow;
int GreenNow;
int RedNow;
int WhiteNow;
int t;
long Time;
long Hour;
long Minute;
long Second;
long Tnow;
int Day;
//long WxNow;
//long WxEnd;


/*void ThunderStorm ()
{ 
  // Schedules a storm between 1 & 9 in the evening
  // It sets Storm2, followed by Cloud2 or DawnDusk or Moon2, depending on when the storm is over
  randomSeed(analogRead(randAnalogPin));  // Generate random seed on unused pin
  byte RH = random(23);                   // Randomizer for thunderstorm
  byte RM = random(59);
  byte RS = random(59);
  byte TSDurationH = random(2);
  byte TSDurationM = random(59);
  long WxNow=(RH*60+RM)*60;
  long WxEnd=((RM+TSDurationH)*60+(RM+TSDurationM))*60;
  
  if (RH <= 12)
    {
      Serial.println("No WX today");
      lcd.setCursor(7,1);
      lcd.print("No WX");
      return;
    }
      
  if (RH > 12)                             // If random value is after 1 pm, allow storm
    {
      
      //Serial.print("Next Storm: ");
      //Serial.print(RH);
      //Serial.print("   ");
      Serial.print("Duration = ");
      Serial.print(TSDurationH);
      Serial.println();
     lcd.setCursor(7,1);
     lcd.print("WX: ");
     lcd.print(RH);
     lcd.print(":");
     lcd.print(RM);}
     
      
      if ((RH + TSDurationH) < 19)   // Return to Cloud2 if storm ends between 1-7pm
        {Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,Cloud2);}
      else if ((RH + TSDurationH) < 21)  // Return to DawnDusk if storm ends between 7-9pm
        {Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,DawnDusk);}
      else                                       // Return to Night2 if storm ends after 9pm
        {Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,Moon2);}
    }*/

void setup()
{
//*************************Initialization time needs to be set!***********************
 //this is the time when you power on the arduino or upload the code. For max precision
 //set this about a minute later than the time on your clock or phone etc, upload the code
 // then unplug the arduino and wait for a second before that time. the format of this is
 // setTime(hour,minute,second,day of month,month,year). Assure you start your arduino atleast
 // 5 minutes before or after any of the lighting change times to assure no conflicts 
 // between initialization and normal scheduled lighting changes.

  
 
/************* END USER DEFINED VARIABLES***************************************************************/


  Wire.begin();
  rtc.begin();
   
   Serial.begin(9600);
   //lcd.begin(9600);  with lcd.begin(16,2); for non Serial LCD
   lcd.begin(9600);
 //pinMode(CO2RelayPin, OUTPUT);
 //pinMode(CO2RelayLED, OUTPUT);
      //Serial.println(freeRam());
// Uncomment to reset time
//   rtc.adjust(DateTime(__DATE__, __TIME__));  //Adjust to compile time

//get current time from RTC and convert to seconds of the day, need to convert int from time to long! Lots of seconds in the day!

lcd.clear();
DateTime now = rtc.now();
Hour=(long) now.hour();
Minute=(long) now.minute();
Second=(long) now.second();
Day= now.day();
Tnow=(Hour*60+Minute)*60+Second;

lcd.setCursor(2,1);
lcd.print(Hour);
lcd.print(":");
lcd.print(Minute);
lcd.setBrightness(8);
//initialize color counters to full lighting to assure sunset is completed smoothly
RedNow=42;
GreenNow=42;
BlueNow=42;
WhiteNow=42;
//initialize co2 and night trackers
int initial_tstep=300;
if ((Tnow > LowSunriseT) && (Tnow < BrightenUpT)){
   //Serial.print("Tnow ");
   //Serial.println(Tnow);
   //Serial.print("LowSunriseT ");
   //Serial.println(LowSunriseT);
    lcd.setCursor(1,1);
    lcd.clearLine(1);
    lcd.print("Low Sunrise ");
   //digitalWrite(CO2RelayPin, LOW);
   //digitalWrite(CO2RelayLED, HIGH);
   //Serial.println("CO2 On");
   
   Sunset(initial_tstep);
   CustomAccent(Red1,Green1,Blue1,White1,initial_tstep);
   
   }
else if ((Tnow >= BrightenUpT) && (Tnow < SunsetT)){
   //Serial.print("Tnow ");
   //Serial.println(Tnow);
   //Serial.print("BrightenUpT ");
   //Serial.println(BrightenUpT);
      lcd.setCursor(1,1); 
      lcd.clearLine(1);
   lcd.print("Sunrise");
   RedNow=0;
   GreenNow=0;
   BlueNow=0;
   WhiteNow=0;
   //digitalWrite(CO2//digitalWrite(CO2RelayPin, LOW);
   //digitalWrite(CO2RelayLED, HIGH);
   //Serial.println("CO2 On");
   Sunrise(initial_tstep);
   }
   
   else if ((Tnow >= BrightenUpT) && (Tnow < SunsetT)){
   //Serial.print("Tnow ");
   //Serial.println(Tnow);
   //Serial.print("BrightenUpT ");
   //Serial.println(BrightenUpT);
      lcd.setCursor(1,1); lcd.clearLine(1);
   lcd.print("Sunrise");
  RedNow=0;
GreenNow=0;
BlueNow=0;
WhiteNow=0;
//digitalWrite(CO2//digitalWrite(CO2RelayPin, LOW);
//digitalWrite(CO2RelayLED, HIGH);
//   Serial.println("CO2 On");
   Sunrise(initial_tstep);
   }
else if ((Tnow >= SunsetT) && (Tnow < SunriseT)){
//Serial.print("Tnow ");
   //Serial.println(Tnow);
   //Serial.print("SunsetT ");
   //Serial.println(SunsetT);
   lcd.setCursor(1,1); lcd.clearLine(1);
  lcd.print("Sunset ");
  //digitalWrite(CO2RelayPin, HIGH); 
 //digitalWrite(CO2RelayLED, LOW);
   //Serial.println("CO2 Off"); 
   
Sunset(initial_tstep);
}
else if ((Tnow >= SunriseT) && (Tnow < BrightenDownT)){
  //Serial.print("Tnow ");
   //Serial.println(Tnow);
   //Serial.print("SunriseT ");
   //Serial.println(SunriseT); 
      lcd.setCursor(1,1); lcd.clearLine(1);
   lcd.print("Evening");
  //digitalWrite(CO2RelayPin, LOW);
  //digitalWrite(CO2RelayLED, HIGH);
  //Serial.println("CO2 On"); 
  RedNow=0;
GreenNow=0;
BlueNow=0;
WhiteNow=0;
   Sunrise(initial_tstep);}
else if ((Tnow >= BrightenDownT) && (Tnow < LowSunsetT)){
     //Serial.print("Tnow ");
   //Serial.println(Tnow);
   //Serial.print("BrightenDownT ");
   //Serial.println(BrightenDownT); 
      lcd.setCursor(1,1); lcd.clearLine(1);
   lcd.print("Simmer Down");
    //digitalWrite(CO2RelayPin, HIGH); 
    //digitalWrite(CO2RelayLED, LOW);
   //Serial.println("CO2 Off"); 
   
  Sunset(initial_tstep);
   CustomAccent(Red1,Green1,Blue1,White1,300);
   }
else if ((Tnow >= LowSunsetT) || (Tnow < LowSunriseT)){
 //Serial.print("Tnow ");
   //Serial.println(Tnow);
   //Serial.print("LowSunsetT ");
   //Serial.println(LowSunsetT);   
   lcd.setCursor(1,1); lcd.clearLine(1);
   lcd.print("Low Sunset");
    //digitalWrite(CO2RelayPin, HIGH); 
    //digitalWrite(CO2RelayLED, LOW);
   //Serial.println("CO2 Off"); ,
   Sunset(initial_tstep);
}

}

void loop(){
//get current time in seconds of the day, need to convert int from time to long! Lots of seconds in the day!!
//SunriseH 10:30 SunsetH 16 BrightenDownH 20:30 LowSunset 21:00

DateTime now = rtc.now();
Hour=(long) now.hour();
Minute=(long) now.minute();
Second=(long) now.second();
Tnow=(Hour*60+Minute)*60+Second;
lcd.setCursor(2,1);
     lcd.print(Hour);
     lcd.print(":");
     lcd.print(Minute);

if ((Tnow > LowSunriseT-CO2_Prestart*60) && (Tnow <= SunsetT-CO2_Prestop*60))
   {
   //digitalWrite(CO2RelayPin, LOW); 
  //digitalWrite(CO2RelayLED, HIGH); 
   //Serial.println("CO2 On"); 
   }
else if ((Tnow > SunsetT-CO2_Prestop*60) && (Tnow <= SunriseT-CO2_Prestart*60)){
  
   //digitalWrite(CO2RelayPin, HIGH); 
  //digitalWrite(CO2RelayLED, LOW); 
   //Serial.println("CO2 Off");
   
   } 
else if ((Tnow > SunriseT-CO2_Prestart*60) && (Tnow <= LowSunsetT-CO2_Prestop*60)){
  
   //digitalWrite(CO2RelayPin, LOW);  
   //digitalWrite(CO2RelayLED, HIGH);
   //Serial.println("CO2 On");
    
}  
 else if ((Tnow > LowSunsetT-CO2_Prestop*60)||(Tnow <= LowSunriseT-CO2_Prestart*60)) {
   
   //digitalWrite(CO2RelayPin, HIGH); 
  //digitalWrite(CO2RelayLED, LOW); 
   //Serial.println("CO2 Off");
    
 }
//define conditionals to initiate lighting changes 


if ((Tnow > LowSunriseT) && (Tnow < BrightenUpT))
   {
   CustomAccent(Red1,Green1,Blue1,White1,tstep1);
   
   }
   else if ((Tnow > BrightenUpT) && (Tnow < SunsetT))
   {
    Sunrise(tstep1);
       lcd.setCursor(1,1); lcd.clearLine(1);
   lcd.print("Sunrise");
   }
/*      else if ((Tnow > BrightenUpT) && (Tnow = TStorm))
   {
   ThunderStorm();
   }
   */
   else if ((Tnow > SunsetT) && (Tnow < SunriseT))
   {
   Sunset(tstep1);
      lcd.setCursor(1,1); lcd.clearLine(1);
   lcd.print("Low Sunset");
   }
   else if ((Tnow > SunriseT) && (Tnow < BrightenDownT))
   {
   Sunrise(tstep1);
      lcd.setCursor(1,1); lcd.clearLine(1);
   lcd.print("Full Spectrum");
   }
  /*    else if ((Tnow > SunriseT) && (Tnow >= WxNow) && (Tnow < WxEnd))
   {
 
     Storm2();
      lcd.setCursor(1,1); lcd.clearLine(1);
   lcd.print("Wx:");
   lcd.print(TSDurationH);
   lcd.print(":");
   lcd.print(TSDurationM);
   }*/
 else if ((Tnow > BrightenDownT) && (Tnow < LowSunsetT))
 {
    CustomAccent(Red1,Green1,Blue1,White1,tstep1);
       lcd.setCursor(1,1); lcd.clearLine(1);
   lcd.print("Low Sunset");
   }
 else if ((Tnow > LowSunsetT) && (Tnow < LowSunsetT+Evening_Moon*60)){
   MoonPhase(tstep1);
      lcd.setCursor(1,1); lcd.clearLine(1);
   lcd.print("Moon");
   }
   else if ((Tnow > LowSunsetT+Evening_Moon*60) || (Tnow < LowSunriseT)){
   Sunset(tstep1);
      lcd.setCursor(1,1); lcd.clearLine(1);
   lcd.print("Night Night");
   }
   }


#ifdef DEBUG_IR
int SerialReadInt()
{
  int i, numAva;
  char inBytes[3];                  // Array to hold the bytes
  char * inBytesPtr = &inBytes[0];  // Pointer to the first element of the array
      
    numAva = Serial.available();    // Read number of input bytes
    if (numAva > 2)
      numAva = 2;                   // Only allow 2 characters to prevent overflow
      
    for (i=0; i<numAva; i++)        // Load input bytes into array
      inBytes[i] = Serial.read();
    inBytes[i] =  '\0';             // Put NULL character at the end
    return atoi(inBytesPtr);        // Call atoi function and return result
}

void CurrentCMDs (int cmd)
{switch (cmd)
  {
    case 1:
      Orange();
      break;
    case 2:
      Blue();
      break;
    case 3:
      Rose();
      break;
    case 4:
      PowerOnOff();
      break;
    case 5:
      White();
      break;
    case 6:
      FullSpec();
      break;
    case 7:
      Purple();
      break;
    case 8:
      Play();
      break;
    case 9:
      RedUp();
      break;
    case 10:
      GreenUp();
      break;
    case 11:
      BlueUp();
      break;
    case 12:
      WhiteUp();
      break;
    case 13:
      RedDown();
      break;
    case 14:
      GreenDown();
      break;
    case 15:
      BlueDown();
      break;
    case 16:
      WhiteDown();
      break;
    case 17:
      M1Custom();
      break;
    case 18:
      M2Custom();
      break;
    case 19:
      M3Custom();
      break;
    case 20:
      M4Custom();
      break;
    case 21:
      Moon1();
      break;
    case 22:
      Moon2();
      break;
    case 23:
      Moon3();
      break;
    case 24:
      DawnDusk();
      break;
    case 25:
      Cloud1();
      break;
    case 26:
      Cloud2();
      break;
    case 27:
      Cloud3();
      break;
    case 28:
      Cloud4();
      break;
    case 29:
      Storm1();
      break;
    case 30:
      Storm2();
      break;
    case 31:
      Storm3();
      break;
    case 32:
      Storm4();
      break;
    case 33:
      RedNow=0;
      GreenNow=0; 
      BlueNow=0;
      WhiteNow=0;
      Sunrise(300);
      break;
    case 34:
       RedNow=42;
      GreenNow=42; 
      BlueNow=42;
      WhiteNow=42;
      Sunset(300);
      break;
    case 35:
    Sunset(300);
   CustomAccent(Red1,Green1,Blue1,White1,300);
   break;
    default:
      //Serial.println("Invalid Choice");}}
#endif

void SendCode (unsigned int code, byte numTimes, const char *sMessage)
{unsigned long irCode = (codeHeader << 16) + code; // Header is 2 bytes, shift all the way to left & add code to it
  
  for( int i = 0; i < numTimes; i++)
  {irsend.sendNEC(irCode,32); // Send code
  
    
 // Serial.println(sMessage);  // Print message
 }}

void Orange()
{SendCode(codeOrange, 2, "Orange");
RedNow=42;
GreenNow=30; 
BlueNow=0;
WhiteNow=42;}

void Blue()
{SendCode(codeBlue, 2, "Blue");
 RedNow=0;
      GreenNow=0; 
      BlueNow=42;
      WhiteNow=42;}

void Rose()
{SendCode(codeRose, 2, "Rose");
  RedNow=42;
      GreenNow=0; 
      BlueNow=42;
      WhiteNow=42;}

void PowerOnOff()
{SendCode(codePowerOnOff, 1, "Power On/Off");}

void White()
{SendCode(codeWhite, 2, "White");
  RedNow=0;
      GreenNow=0; 
      BlueNow=0;
      WhiteNow=42;}

void FullSpec()
{SendCode(codeFullSpec, 2, "Full Spectrum");
   RedNow=42;
      GreenNow=42; 
      BlueNow=42;
      WhiteNow=42;}

void Purple()
{SendCode(codePurple, 2, "Purple");
RedNow=0;
      GreenNow=0; 
      BlueNow=42;
      WhiteNow=30;}

void Play()
{SendCode(codePlay, 1, "Play/Pause:");}

void RedUp()
{SendCode(codeRedUp, 2, "Red Up");
RedNow+=1;}

void GreenUp()
{SendCode(codeGreenUp, 1, "Green Up");
GreenNow+=1;}

void BlueUp()
{SendCode(codeBlueUp, 1, "Blue Up");
BlueNow+=1;}

void WhiteUp()
{SendCode(codeWhiteUp, 1, "White Up");
WhiteNow+=1;}

void RedDown()
{SendCode(codeRedDown, 1, "Red Down");
RedNow=RedNow-1;}

void GreenDown()
{SendCode(codeGreenDown, 1, "Green Down");
GreenNow=GreenNow-1;}

void BlueDown()
{SendCode(codeBlueDown, 1, "Blue Down");
BlueNow=BlueNow-1;}

void WhiteDown()
{SendCode(codeWhiteDown, 2, "White Down");
WhiteNow=WhiteNow-1;}

void M1Custom()
{SendCode(codeM1Custom, 2, "Custom Mix 1");}

void M2Custom()
{SendCode(codeM2Custom, 2, "Custom Mix 2");}

void M3Custom()
{SendCode(codeM3Custom, 2, "Custom Mix 3");}

void M4Custom()
{SendCode(codeM4Custom, 2, "Custom Mix 4");}

void Moon1()
{SendCode(codeMoon1, 2, "Moonlight 1");}

void Moon2()
{SendCode(codeMoon2, 2, "Moonlight 2");}

void Moon3()
{SendCode(codeMoon3, 2, "Moonlight 3");}

void DawnDusk()
{SendCode(codeDawnDusk, 2, "Dawn/Dusk");}

void Cloud1()
{SendCode(codeCloud1, 2, "Cloud Cover 1");}

void Cloud2()
{SendCode(codeCloud2, 2, "Cloud Cover 2");}
  
void Cloud3()
{SendCode(codeCloud3, 2, "Cloud Cover 3");}

void Cloud4()
{SendCode(codeCloud4, 2, "Cloud Cover 4");}

void Storm1()
{SendCode(codeStorm1, 2, "Thunderstorm 1");}

void Storm2()
{SendCode(codeStorm2, 2, "Thunderstorm 2");}

void Storm3()
{SendCode(codeStorm3, 2, "Thunderstorm 3");}

void Storm4()
{SendCode(codeStorm4, 2, "Thunderstorm 4");}

void CustomAccent(int RedIn, int GreenIn, int BlueIn, int WhiteIn, int tstep )
{
  if(WhiteNow>WhiteIn){
  for (int i=1;i<43;i++){
  if(WhiteNow<WhiteIn){
  WhiteUp();
  delay(tstep);
  }
    if(WhiteNow>WhiteIn){
  WhiteDown();
  delay(tstep);
  }}}
for (int i=1;i<43;i++){
  if(RedNow<RedIn){
  RedUp();
  delay(tstep);
  }
    if(RedNow>RedIn){
  RedDown();
  delay(tstep);
  }
   if(GreenNow>GreenIn){
  GreenDown();
  delay(tstep);
  }
  if(GreenNow<GreenIn){
  GreenUp();
  delay(tstep);
  }
   if(BlueNow>BlueIn){
  BlueDown();
  delay(tstep);
  }
   if(BlueNow<BlueIn){
  BlueUp();
  delay(tstep);
  }}
  
  if(WhiteNow<WhiteIn){
  for (int i=1;i<43;i++){
  if(WhiteNow<WhiteIn){
  WhiteUp();
  delay(tstep);
  }
    if(WhiteNow>WhiteIn){
  WhiteDown();
  delay(tstep);
  }}}

} 

void Sunrise( int tstep )
{
  for (int i=1;i<43;i++){
  if(RedNow<42){
  RedUp();
  delay(tstep);
  }
    if(RedNow>42){
  RedDown();
  delay(tstep);
  }
   if(GreenNow>42){
  GreenDown();
  delay(tstep);
  }
  if(GreenNow<42){
  GreenUp();
  delay(tstep);
  }
   if(BlueNow>42){
  BlueDown();
  delay(tstep);
  }
   if(BlueNow<42){
  BlueUp();
  delay(tstep);
  }
 }
 for (int i=1;i<43;i++){
  if(WhiteNow<42){
  WhiteUp();
  delay(tstep);
  }
    if(WhiteNow>42){
  WhiteDown();
  delay(tstep);
  }
 }
FullSpec();
 RedNow=42;
  GreenNow=42;
  BlueNow=42;
  WhiteNow=42;
  delay(tstep);
} 

void Sunset( int tstep )
{
  for (int i=1;i<43;i++){
  if(WhiteNow<0){
  WhiteUp();
  delay(tstep);
  }
    if(WhiteNow>0){
  WhiteDown();
  delay(tstep);
  }
 }
  
for (int i=1;i<43;i++){
  if(RedNow<0){
  RedUp();
  delay(tstep);
  }
    if(RedNow>0){
  RedDown();
  delay(tstep);
  }
   if(GreenNow>0){
  GreenDown();
  delay(tstep);
  }
  if(GreenNow<0){
  GreenUp();
  delay(tstep);
  }
   if(BlueNow>0){
  BlueDown();
  delay(tstep);
  }
   if(BlueNow<0){
  BlueUp();
  delay(tstep);
  }
 }
 
M4Custom();
 RedNow=0;
  GreenNow=0;
  BlueNow=0;
  WhiteNow=0;
  delay(tstep);
} 
void MoonPhase(int tstep)
{
  
  int MoonBlue=(1+abs(31-(Day-1)*2))/2;
  int MoonRed=(1+abs(31-(Day-1)*2))/4;
  int MoonGreen=(1+abs(31-(Day-1)*2))/4;
  CustomAccent(MoonRed, MoonGreen, MoonBlue, 0, tstep);
}

  
  


int freeRam () {
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}




Curt_Planted said:


> Here is the latest code with moonlight and co2 control. Hope someone finds it useful. Had to zip the text doc as the limits on text docs are pretty skimpy on this forum. There is plenty of cleaning up to do in the code to minimize the file size but it's not even using half the memory of my arduino so if it aint broke....


----------



## Linwood

drftndakota said:


> Somehow I ended up with a bad led went and picked up another and everything works perfect.


I did that as well. I think it was during debugging when I would turn it on steady as opposed to pulsing. They take a LOT more current pulsed than steady.


----------



## 75ona76

I thought I would share with you guys that I have two fixtures and a strip. The strip has a different device ID then the fixture lights. It has the smaller IR receiver. One of my fixtures has the small and one had the large round eye.


----------



## maxwellag

I keep getting compile error: "time_t does not name a type"


----------



## 75ona76

Do you have the time library listed when you look for examples? I have those types if errors when it can't find the library files it needs.


----------



## maxwellag

75ona76 said:


> Do you have the time library listed when you look for examples? I have those types if errors when it can't find the library files it needs.


Yeah I imported all of the libraries it needed, and it showed them in the list. Not sure why it happens. After I imported them, nothing happens. the Time.h and every other library except LiquidCrystal.h aren't orange.


----------



## mistergreen

The libraries should be in documents:Arduino/libraries/
If you don't have a libraries folder, you can create one.

Oh, and restart the Arduino IDE.


----------



## maxwellag

mistergreen said:


> The libraries should be in documents:Arduino/libraries/
> If you don't have a libraries folder, you can create one.
> 
> Oh, and restart the Arduino IDE.


Thanks, I got it all sorted out. Code compiles fine now... Now my issue is wiring. I can't figure out a way to wire my 16x2 LCD, 6 pin RTC, IR receiver and IR LED all at once. Indychus, I understand if it's too much to ask, but could you please post a diagram of the final product wiring? I'm not sure which pins the LED and receiver are supposed to go in because they changed from your original post.


----------



## Curt_Planted

I was playing around tonight with my remote and adjusting my two lights for different color accents and found it looks amazing with different colors on the lights (one red one orange-yellow so it gives a graduated color scheme). I also found the adjustment much smoother by turning the lights on one at a time. 

So the question is, how can the IR remote library be edited to accept an argument in the controller code to transmit from two different IR leds to allow independent control of two different lights? Obviously the arduino is capable of transmitting pwm on more than one pin but after working through the library I couldn't find a clear path to defining the led pin from function inputs. I got as far as the mark and space functions in the cpp file and then couldn't trace back clearly to the led definition in the .h file. I'm new to editing libraries in general so hopefully someone on here knows this library better than I. Any suggestions? I was thinking creating a second duplicate library that uses a different PIN could be a dirty way to do it, but not sure if the function name and variable conflicts could be easily avoided... perhaps by renaming the class for the function we are using? :help:

Bump: Just had an idea for a hardware solution that would be pretty simple: just apply a voltage to transistor from a different pin to change which led is activated. Lol, or I could still dive into the code... :eek5:


----------



## ipkiss

Hi Guys, 

Has anyone tried to apply this to the Ecoxotic E-Series yet? Any compatibility between that and this Current Satellite+ ?


----------



## AnotherHobby

ipkiss said:


> Hi Guys,
> 
> Has anyone tried to apply this to the Ecoxotic E-Series yet? Any compatibility between that and this Current Satellite+ ?


Yes, it works great, but the remote codes are different. It's the same codes, but mapped to different buttons. I'm pretty sure that power is the same, but most of the rest are different. I only bothered to decode the ones I use:

REDUP = 0x20DF0AF5
REDDOWN = 0x20DF38C7
GREENUP = 0x20DF8A75
GREENDOWN = 0x20DFB847
BLUEUP = 0x20DFB24D
BLUEDOWN = 0x20DF7887
WHITEUP = 0x20DF32CD
WHITEDOWN = 0x20DFF807
M1 = 0x20DF58A7
M2 = 0x20DF9867
M3 = 0x20DF18E7
M4 = 0x20DFD827

If you need to decode any more, this sketch with an IR receiver works perfectly. Just open a serial window and shoot away:



Code:


#include <IRremote.h> // use the library
int receiver = 2; // pin 1 of IR receiver
IRrecv irrecv(receiver); // create instance of 'irrecv'
decode_results results;
void setup()
{
  Serial.begin(9600); // for serial monitor output
  irrecv.enableIRIn(); // Start the receiver
}
void loop()
{
  if (irrecv.decode(&results)) // have we received an IR signal?
  {
    Serial.println(results.value, HEX); // display it on serial monitor in hexadecimal
    irrecv.resume(); // receive the next value
  }  // Your loop can do other things while waiting for an IR command
}


----------



## ipkiss

AnotherHobby, 

Thanks and that's great news! I'll have to take a serious look at building one of these then. It's a shame the thunderstorm mode on the e-series cycles so fast. Is it the same on the satellite+? Was contemplating someone's idea of doing thunderstorms when its raining but the cycle in and out of full light just seems a bit silly. Since we're not able to reprogram the ramp timer itself, I don't imagine there being some sort of override is there?


----------



## Nathan.G

Indychus, Want to give u ur props on this, if stuff like this weren't a complete mystery to me I have one of these thrown together this weekend! i have 3 of these lights and was real disappointed in the dual controller, but this seems to be exactly what I'm looking for. Now i just have to find some one to throw one of these to gather for me.


----------



## Linwood

Speaking of multiple lights, has anyone tried that yet with the controller? 

I have four, and also one. The one I'm controlling quite happily, but the four I am still using with a remote as I get the stand finished. And even with the remote, they are not working consistently. At least one seems to keep forgetting learned codes, for example (the M1-4 keys). And if I use the power switch I can never get them to all turn on or off together, then they start alternating.

Note: I try to use the M4 key, programmed to "fully dim" instead of on/off so as to avoid this alternating issue. That's why forgetting their programming is an issue.

Are people having good luck controlling multiple with the same schedule?


----------



## AnotherHobby

Linwood said:


> Speaking of multiple lights, has anyone tried that yet with the controller?
> 
> I have four, and also one. The one I'm controlling quite happily, but the four I am still using with a remote as I get the stand finished. And even with the remote, they are not working consistently. At least one seems to keep forgetting learned codes, for example (the M1-4 keys). And if I use the power switch I can never get them to all turn on or off together, then they start alternating.
> 
> Note: I try to use the M4 key, programmed to "fully dim" instead of on/off so as to avoid this alternating issue. That's why forgetting their programming is an issue.
> 
> Are people having good luck controlling multiple with the same schedule?


I controlled 2 lights with one controller. I didn't control them independently, so they both got the same IR commands. You need to make sure that both light's IR receivers pointed accurately at the IR emitter. 

Mine have never forgot learned codes. I'd contact Current for support on that. They should repair/replace it.

Both of my controllers (the first one I made, and my iAqua one) do ramping with these lights. They aren't 100% responsive to every single RGBW up and down command, so I use M1-M4 in-between the ramps to keep the lights from drifting. If you want to do ramping this way, I can explain in more detail.

Also, the on/off remote code never failed for me in the several months I ran two lights. They never got out of sync that way. That's how I controlled power.


----------



## Linwood

AnotherHobby said:


> I controlled 2 lights with one controller. I didn't control them independently, so they both got the same IR commands. You need to make sure that both light's IR receivers pointed accurately at the IR emitter.
> 
> Mine have never forgot learned codes. I'd contact Current for support on that. They should repair/replace it.
> 
> Both of my controllers (the first one I made, and my iAqua one) do ramping with these lights. They aren't 100% responsive to every single RGBW up and down command, so I use M1-M4 in-between the ramps to keep the lights from drifting. If you want to do ramping this way, I can explain in more detail.
> 
> Also, the on/off remote code never failed for me in the several months I ran two lights. They never got out of sync that way. That's how I controlled power.


I think the power may resolve itself when I have the receivers in a fixed position. Right now I have them wire tied together. But the power on/off is really annoying, as there is no fix for it really other than either (a) try to isolate the receiver, which is surprisingly hard by hand, or (b) unplug the ones you don't want to control first. Both annoying. 

But yes, the M1-4 are what i use to keep the repeated send in sync as well. I've got a "program" sequence I send to it which sets a specific value and saves it. Works well on one. Haven't yet tried it on four.

I am not trying to control them independently. Though... now that you mention it... if I can't get this overly reliable I may just do that. Build a little opaque container for each one and its corresponding transmitted (heck, I could just tape them together). Then at least the program could deal with each separately if needed. But that wasn't the intent.

yes, it's specifically that it "forgets" the M4 code. And this from the remote control. It will work for a while, then one evening when I go to turn off (I use M4 = all dim fully) one won't turn off. Isolate it and test, and it's been reprogrammed to something white -- not sure exactly what, it doesn't look full on. So I switch to white, down-repeatedly on white to fully off, press it four seconds....

Half the time at this point after about 2-3 seconds it comes on again, as though it's not continuously sending (at this point I'm holding both remote and receiver in one hand). And I start over.

Half the time it works, and then for a while M4 will turn it off. 

Until the next time.

I need to start paying attention and see if it is always the same one. Frankly with four I loose track. But yes, you are right -- maybe it is just defective.

I've got the new Pi to build the controller but as I haven't figured out the details on the stand and plumbing cover, I don't know where anything will be positioned, so I have not wanted to wire it up. In fact one thing I want to test is whether it can control it from about 20' away, where I have wired network, so I don't have to use wireless (which with the one I got for the Pi runs really hot). So still experimenting. Sorry, rambling... 

Anyone else try doing multiple and have issues, or is it just me? 

PS. I sure wish manufacturers would get away from toggles -- an "on" and "off" is so much more reliable!


----------



## r45t4m4n

Happened to me once as well. I have the Pi controlling two of them. I reprogrammed them separately (plug one in, turn it off, turn it on, program M4 with the remote that came with it, repeat with second) and I have not had an issue since.

The first time I programmed them I did both at the same time with the same remote.


----------



## strike21

I found your post @ 2 weeks ago and was inspired to make one. So far I have put it together minus the LCD display (hope to get it today). It’s been running V3.6 for the past 2 days with no problems. My hats off to all that have provided guidance, directions and pictures. Thank you.


----------



## kman

kman said:


> Hi guys! I'm just about ready to jump on the bandwagon. Can someone confirm that the parts I've selected will get the job done before I pull the trigger?
> 
> Arduino UNO R3 board with DIP ATmega328P - $28
> http://amzn.com/B006H06TVG
> 
> SainSmart I2C RTC DS1307 AT24C32 Real Time Clock module+board for AVR ARM PIC - $9.20
> http://amzn.com/B006J4FZW4
> 
> SainSmart LCD Module For Arduino 20 X 4, PCB Board, White On Blue - $12.29
> http://amzn.com/B003B22UR0
> 
> Super-bright 5mm IR LED - 940nm - $1.50 (for two... Or I may just buy local)
> http://www.adafruit.com/products/387
> 
> So just over $50 for the majority of what I need.
> 
> Resistors (local purchase, once I confirm what's needed)
> 
> I have plenty of USB chargers and cables so I think power is not an issue. I have a couple of different soldering irons/guns (minor wiring, been nearly 30 years since I soldered a circuit board but I doubt much has changed). I think some wire for jumpers and perhaps a small breadbox is all I need to get going?


So I'm waiting for the iAqua project to get a little more finalized before I really dig in (also, o2surplus' shield to save me 3 days of soldering!) to that beast of a project.

And the thought occurs that I ordered two Arduino Megas just so I had another to play with. If I add a $12 LCD (and even that is optional), I'm wondering if I use the spare Mega and IR LED, I may be able to get THIS project up and running in the next few days?

Only problem is all of Indychus' photos on how to actually assemble of this stuff are gone from the early pages of this thread. Does anyone have them archived, or is there some way I can get some help putting it all together?


----------



## theknight

kman said:


> So I'm waiting for the iAqua project to get a little more finalized before I really dig in (also, o2surplus' shield to save me 3 days of soldering!) to that beast of a project.
> 
> And the thought occurs that I ordered two Arduino Megas just so I had another to play with. If I add a $12 LCD (and even that is optional), I'm wondering if I use the spare Mega and IR LED, I may be able to get THIS project up and running in the next few days?
> 
> Only problem is all of Indychus' photos on how to actually assemble of this stuff are gone from the early pages of this thread. Does anyone have them archived, or is there some way I can get some help putting it all together?


Sounds like you have the right parts, I'll put together a schematic for you, compared to the iAqua it is real simple.


----------



## strike21

theknight said:


> Sounds like you have the right parts, I'll put together a schematic for you, compared to the iAqua it is real simple.


I tried to hook up the LCD tonight... Total fail ....:help: If you can share the schematic it would be greatly appreciated.


----------



## theknight

theknight said:


> Sounds like you have the right parts, I'll put together a schematic for you, compared to the iAqua it is real simple.


I believe you have the same LCD that I used, this is what I used to get it to work:

#define I2C_ADDR 0x3f // I2C address of PCF8574A
#define BACKLIGHT_PIN 3
#define En_pin 2
#define Rw_pin 1
#define Rs_pin 0
#define D4_pin 4
#define D5_pin 5
#define D6_pin 6
#define D7_pin 7

LiquidCrystal_I2C lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin, BACKLIGHT_PIN, POSITIVE);

If I did it right there is also a schematic attached based on the UNO, if you use the Mega, SCL & SDA pins will be different. Also AH uses D9 on Mega for the IR so that might also have to change. Hope this helps. I added temp and PH and have been using it while I wait for my new screen and shield to make it from China.

These are the libraries used:
#include <Wire.h>
#include <RTClib.h>
#include <Time.h>
#include <TimeAlarms.h>
#include <IRremote.h>
#include <LiquidCrystal.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>


----------



## kman

Is that the schematic, or is that for theknight?

What I need is the electronics / Arduino for dummies.  It's been 30 years since I did anything more complicated than solder ends and wires together. I have all the parts, but I literally don't know what to do with them (I've never used a breadboard, for instance) It seems like (from the descriptions) Indychus' photos showed more detail, but they're gone.

Does anyone have photos of the breadboarded layout?

Edit: Nevermind... Indychus' photos have mysteriously returned! It's a little late tonight, but between those photos and some googling on breadboards, I think I have a general handle on it. I'll see if I can give it a go tomorrow.


----------



## Linwood

*Power Supply and Power Usage (and light output presumably)*

I have four LED+ on one tank, and wanted to get rid of the four power supplies, so I grabbed an old 400w power supply from a PC, and hooked it up instead.

Not good -- power draw fell 13%, it would not hold 12V, it fell from open voltage of 12.21 to 11.31 with full power of four lights.

Interestingly, the provided power supplies (which I also measured) also fell further than expected: they start at 12.30, and on full power fall to 11.80, with a current draw of 2.1A. This translates to 25 watts, by the way, not the 30 rated. 

So today I ran across an old 850W power supply sitting around, so I rewired it and hooked it up instead. It's open voltage is 12.37, and it drops only to 12.01V under load, drawing 2.3A and thus 27.6 watts, about 11% more than the stock power bricks.

The nice thing about this setup is: only one plug, if you are using a timer only one timer (if you are using a computer irrelevant), and the PC power supplies run a LOT cooler than the OEM bricks. I didn't measure AC power draw, but I'm sure it is less, at least less wasted as heat.

AND you get 11% more light, maybe, probably... the power is going somewhere.


----------



## kman

^^ Interesting idea! I wonder what the actual power draw is of that big honking PC power supply compared to several wall warts? (when running all the lights, that is)

Is the fan noise from the PC power supply distracting (or even audible)?


----------



## Linwood

kman said:


> ^^ Interesting idea! I wonder what the actual power draw is of that big honking PC power supply compared to several wall warts? (when running all the lights, that is)
> 
> Is the fan noise from the PC power supply distracting (or even audible)?


If it's a decent power supply you won't be able to hear it, especially under a tank.

I didn't measure the actual AC draw (I didn't have an AC breakout handy), but the fact that it doesn't get hot, and the OEM bricks get VERY hot, answers that question for me -- it's better. Though if the lights draw 11% more it may be a net wash -- but I'd rather have more light than heat!


----------



## jeffkrol

Linwood said:


> If it's a decent power supply you won't be able to hear it, especially under a tank.
> 
> I didn't measure the actual AC draw (I didn't have an AC breakout handy), but the fact that it doesn't get hot, and the OEM bricks get VERY hot, answers that question for me -- it's better. Though if the lights draw 11% more it may be a net wash -- but I'd rather have more light than heat!



Alternate.. 
http://ca.mouser.com/ProductDetail/...Rh2Mm9fDxA==&gclid=CICH0Z-17sACFYZzMgod2DMAPg
http://www.meanwell.com/search/RS-150/

and no fan...........


----------



## kman

Interesting, Jeff. Have you used one?


----------



## jeffkrol

kman said:


> Interesting, Jeff. Have you used one?


no but Meanwell PS are fairly highly rated on Reef Central DIY's whom many I trust..

Last PS I got were "NOS" N2Power chassis mounts.. 

The 2 I have for backup are fanless 54V 5.2A switching PS's

Meanwell is high average as far as I'm concerned, and like this one.. no wasted "power" w/ like 800W (yea right) computer PS's which most wouldn't know which wires to jumper to even get to run.. Besides most still use bad caps..and even worse fans.. 
(personal rant).. 



XL-280-54CS..
http://www.n2power.com/wp-content/uploads/2013/05/XL280-AC-DC-Series-XL280-AC-DC_datasheet.pdf
http://www.n2power.com/wp-content/uploads/2013/05/XL280-AC-DC-Series-Power-Supplies-705501.pdf

The Meanwells certainly beats out the Chinese knockoffs or those plastic bricks.. IF their spec sheet is accurate...

And few would pay $200 or a 12V PS.


> XL280-12-CS from N2Power, 280 Watt, Dual Output
> 
> Pricing:
> 1 to 9:	$221.77
> 
> Call for availability
> For delivery details, please contact us











http://www.powergatellc.com/n2power-xl280-power-supply.html

In case you got the wrong impression, I never would pay $200 for a PS.. 

An in-betweener.. 
http://www.newark.com/tdk-lambda/ls...iD9654UC|pcrid|41566056141|plid|&CMP=KNC-GPLA


> TDK LAMBDA LS200-12 AC-DC CONV, ENCLOSED, 1 O/P, 200W, 16.7A, 12V $72.50


http://us.tdk-lambda.com/ftp/Specs/ls200.pdf

convection or fan cooled..

HWS series.. limited lifetime warranty.. over $200
You get what you pay for (sometime)

http://www.newark.com/tdk-lambda/hw...iD9654UC|pcrid|41566056141|plid|&CMP=KNC-GPLA











Sorry to bore you.. but I find power supplies interesting.. for some twisted reason..


----------



## Linwood

jeffkrol said:


> Meanwell is high average as far as I'm concerned, and like this one.. no wasted "power" w/ like 800W (yea right) computer PS's which most wouldn't know which wires to jumper to even get to run.. Besides most still use bad caps..and even worse fans..
> (personal rant)..


Yeah, but I like the idea of free. I suspect anyone reading this thread on building your own controller throws away a PC power supply at least every couple years, and wouldn't have too much trouble with "hook green to black, and book together any wires that are in the same pin on the biggest connector, hook yellow to +, black to - on the LED, and cut the rest off". :icon_surp

I may get ambitious and look at power draw on the AC side later. I suspect with just a tiny bit of effort, maybe just a wire to the card, I could get the Pi to set the power supply into standby.

In fact, now that I think about it, I'll use this power supply for the Pi. The standby power should be more than adequate (though I can't recall which voltages are there on standby), so maybe I won't need the brick for the Pi either.


----------



## kman

$200 for a power supply?!? Cool as it is (and it IS cool), that's WAY more than I'm willing to spend in order to avoid using a second OEM power brick. (4th, in Linwood's case, but still)

And Google can help anyone figure out the pinput for a PC power supply. 

I can see why the monster tank reef guys would be interested, but it's more than a little overkill for my little 60P.


----------



## jeffkrol

kman said:


> $200 for a power supply?!? Cool as it is (and it IS cool), that's WAY more than I'm willing to spend in order to avoid using a second OEM power brick. (4th, in Linwood's case, but still)
> 
> And Google can help anyone figure out the pinput for a PC power supply.
> 
> I can see why the monster tank reef guys would be interested, but it's more than a little overkill for my little 60P.


 I paid $1 for a 12V 5.5A power brick... Would run 2 Currents..
That was the strongest I've found for cheap.. Surplus of course. I wouldn't pay $200 either.. but I like the hunt...
My first "commercial high grade" PS was 56V and retailed for $200 plus. Got it for $15.. 

Sadly, I only got one of those.. The other 2 I mentioned were also only $15 each plus shipping..

Never got a cheap lambda though..

I needed some high V for series strings.. @ 56V (53 effective after driver "losses") I could run 14 3W LED's in series easily..
14,9,4,4 and it had a separate 12V circuit to run the Typhon, eliminating another brick. T
also had a 5v logic power tap..and on board intelligence.. 
It is a thing of (mostly unnecessary) beauty.. 

looked like this:









finding connectors was a bit of a PIA.. but fortunately some (ironic) computer PS cables were modifiable..


----------



## gus6464

TDK Lambda is serious business. That's a forever power supply right there.


----------



## kman

Grabbed the N2Power PS from eBay as suggested. Thanks, Jeff! Wish me luck!


----------



## mistergreen

You can buy a cheap power supply and add a meanwell-LDD driver (constant current) if you need to use it for DIY LED, plus you can dim.


----------



## jeffkrol

kman said:


> Grabbed the N2Power PS from eBay as suggested. Thanks, Jeff! Wish me luck!


good luck.. You can probably gut a computer PS to put everything in.. 

I left the AC filter circuit in and the switch.. Removed the fan for space though.. it could be left and wired in.. 

Anyways always use caution w/ open frame PS..


----------



## gus6464

I bought the TDK Lambda 650. Probably going to have to change the fan with a Noctua.


----------



## kman

theknight said:


> I believe you have the same LCD that I used, this is what I used to get it to work:
> 
> #define I2C_ADDR 0x3f // I2C address of PCF8574A
> #define BACKLIGHT_PIN 3
> #define En_pin 2
> #define Rw_pin 1
> #define Rs_pin 0
> #define D4_pin 4
> #define D5_pin 5
> #define D6_pin 6
> #define D7_pin 7
> 
> LiquidCrystal_I2C lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin, BACKLIGHT_PIN, POSITIVE);
> 
> If I did it right there is also a schematic attached based on the UNO, if you use the Mega, SCL & SDA pins will be different. Also AH uses D9 on Mega for the IR so that might also have to change. Hope this helps. I added temp and PH and have been using it while I wait for my new screen and shield to make it from China.
> 
> These are the libraries used:
> #include <Wire.h>
> #include <RTClib.h>
> #include <Time.h>
> #include <TimeAlarms.h>
> #include <IRremote.h>
> #include <LiquidCrystal.h>
> #include <LCD.h>
> #include <LiquidCrystal_I2C.h>


Been busy with other things, and just getting back to this project.

I have your schematic (thanks again!), and I've soldered leads on the RTC (the biggest stumbling block to date). I've connected the RTC, the IR LED, and the resistor to the the breadboard and Arduino (Mega):










I'm actually quite pleased with my soldering of the circuit board leads, considering how long its been since I did any real soldering!










I think the main thing that's left to wire up is the LCD display itself. I'm really confused, though... your schematic only seems to show 4 wires going to the LCD (which seem to coincide nicely with headers coming off the bottom): GND, VCC (5v?), SDA and SCL.

Thing is, when I look at the earlier project pictures, I'm seeing pictures of almost the entire row (at least 6 wires) along the back edge being used, like this one posted by Indychus in post # 206:










Further, on my LCD, there is a little sub-board soldered onto the back of the LCD that then has the 4 little leaders I mentioned, for the 4 wires I mentioned above (and which seem to be reflected in your schematic).

I note that in post 367, zodduska mentions "Ended up just snapping off the LCD backpack after an hour of failing to completely desolder it." And offers a pic showing similarly complex wiring:










So my question is, do I simply use the little connectors hanging off the little sub-board on the back, and wire up GND, VCC, SDA and SCL as indicated in your schematic, and I'm good to go, or do I need do something with all the other wires used by Indychus and zodduska as well? And what about the little sub-board, is it doing something useful that I need, or do I simply hack it off (and wire it all up with many jumpers)?

My 20x4 LCD (you can just see the edge of the sub-board/backpack below the contacts):










And the back, clearly showing the small sub-board / backpack and it's leads:


----------



## kman

Hrm. I realize I don't have the LCD hooked up yet, but as long as I have the IR library added, shouldn't the code compile and upload? I get about a zillion errors when I try to compile and upload the current (3.6) code:



Code:


  This report would have more information with
  "Show verbose output during compilation"
  enabled in File > Preferences.
Arduino: 1.0.6 (Mac OS X), Board: "Arduino Mega 2560 or Mega ADK"
CurrentSatPlusController-3.6.ino:29: error: 'time_t' does not name a type
CurrentSatPlusController-3.6.ino:25: error: 'RTC_DS1307' does not name a type
CurrentSatPlusController-3.6.ino:26: error: 'IRsend' does not name a type
CurrentSatPlusController-3.6.ino: In function 'void SetAlarms()':
CurrentSatPlusController-3.6.ino:81: error: 'Alarm' was not declared in this scope
CurrentSatPlusController-3.6.ino: In function 'void setup()':
CurrentSatPlusController-3.6.ino:97: error: 'RTC' was not declared in this scope
CurrentSatPlusController-3.6.ino:104: error: 'DateTime' was not declared in this scope
CurrentSatPlusController-3.6.ino:107: error: 'syncProvider' was not declared in this scope
CurrentSatPlusController-3.6.ino:107: error: 'setSyncProvider' was not declared in this scope
CurrentSatPlusController-3.6.ino:109: error: 'Alarm' was not declared in this scope
CurrentSatPlusController-3.6.ino: In function 'void loop()':
CurrentSatPlusController-3.6.ino:123: error: 'Alarm' was not declared in this scope
CurrentSatPlusController-3.6.ino: At global scope:
CurrentSatPlusController-3.6.ino:128: error: 'time_t' does not name a type
CurrentSatPlusController-3.6.ino: In function 'void ThunderStorm()':
CurrentSatPlusController-3.6.ino:155: error: 'Alarm' was not declared in this scope
CurrentSatPlusController-3.6.ino:171: error: 'Alarm' was not declared in this scope
CurrentSatPlusController-3.6.ino:173: error: 'Alarm' was not declared in this scope
CurrentSatPlusController-3.6.ino:175: error: 'Alarm' was not declared in this scope
CurrentSatPlusController-3.6.ino: In function 'void digitalClockDisplay()':
CurrentSatPlusController-3.6.ino:182: error: 'hour' was not declared in this scope
CurrentSatPlusController-3.6.ino:183: error: 'minute' was not declared in this scope
CurrentSatPlusController-3.6.ino:184: error: 'second' was not declared in this scope
CurrentSatPlusController-3.6.ino: In function 'void lcdClockDisplay()':
CurrentSatPlusController-3.6.ino:190: error: 'hour' was not declared in this scope
CurrentSatPlusController-3.6.ino:191: error: 'minute' was not declared in this scope
CurrentSatPlusController-3.6.ino: In function 'void SendCode(unsigned int, byte, const char*)':
CurrentSatPlusController-3.6.ino:339: error: 'irsend' was not declared in this scope
CurrentSatPlusController-3.6.ino:340: error: 'Alarm' was not declared in this scope

Have I missed something obvious? I looked over the user-defined variables section (and I just commented out the thunderstorm code since that's not something I'm interested in). But the IR LED, its resistor, and the RTC are all hooked up as discussed in my post above, and I'm plugged into my laptop, so it has USB power, of course. The alarms are all left alone at their default settings for now.

What am I missing?


----------



## dshuld

kman were you able to figure out the issue? Just ordered the light a cple ago days after reading through the first few pages on the sat+ thread....before reaching the part about currents controller not having full capabilities built in for it lol. 

Been many years since i've done anything like these builds and like others I can't see op's pics. Was hoping a more current poster may have pics so I can set up automation as well. But I've never messed with arduino before....


----------



## kman

dshuld said:


> kman were you able to figure out the issue? Just ordered the light a cple ago days after reading through the first few pages on the sat+ thread....before reaching the part about currents controller not having full capabilities built in for it lol.
> 
> Been many years since i've done anything like these builds and like others I can't see op's pics. Was hoping a more current poster may have pics so I can set up automation as well. But I've never messed with arduino before....


No, not so far. I've been hoping someone will post back with advice, as I'm new to this sort of thing. :/

The OPs pics came back to me later, I suggest trying again? Not sure why they didn't come up at first, but they're there now. Maybe try another browser, and be sure to hit refresh! They helped quite a bit, although there is quite a bit that's not really covered thoroughly, as I have discovered...

Once I'm past this step I think it will work well, and I'm looking forward to it.  It's been pretty straightforward so far, once I got all the materials together. I might put together a How-To to assist others once mine is set up and running properly, but I'm not really there yet...


----------



## dshuld

It may just be my pc at work lol. they came up when I tried on my tablet. I may have missed it but was anyone able to confirm if the ir codes had been changed?


----------



## kman

dshuld said:


> It may just be my pc at work lol. they came up when I tried on my tablet. I may have missed it but was anyone able to confirm if the ir codes had been changed?


IIRC, it's discussed somewhere in this thread. I think the IR codes did NOT end up changing, there was just rumblings that it might.

(Talking about Sat+, of course... they're different for the E series and any other make of light, naturally)


----------



## dshuld

cool thanks. guess ill start shopping for parts to see if I can get the light part figured out...may try the temp prob if I can get the light side working lol.


----------



## Greg0u812

kman said:


> No, not so far. I've been hoping someone will post back with advice, as I'm new to this sort of thing. :/
> 
> The OPs pics came back to me later, I suggest trying again? Not sure why they didn't come up at first, but they're there now. Maybe try another browser, and be sure to hit refresh! They helped quite a bit, although there is quite a bit that's not really covered thoroughly, as I have discovered...
> 
> Once I'm past this step I think it will work well, and I'm looking forward to it.  It's been pretty straightforward so far, once I got all the materials together.* I might put together a How-To to assist others once mine is set up and running properly, but I'm not really there yet...*


I have researched Arduino before and just felt like I would be getting in over my head with what I wanted to use it for.

I run DSunY lighting on my main tank and the lighting is pretty good.
I originally wanted the sat+ lights but could the remote controller (as opposed to a fully automated setup) was not a doable option at all for me! I actually returned the six sat+ lights and ordered the DSunY lights. Yeah, my lfs wasn't very happy when I made that return!

I have most of what I need now for the Arduino project (hardware wise) but still feel somewhat lost at sea.
Once I have the few remaining pieces, I would LOVE to see a fairly in-depth "How-to" write up!:bounce:
My goal is being able to control 9 sat+ lights on 4 aquariums with 1 Arduino! I just want all four tanks to have the exact same timing and features.

I just don't understand why I am having a difficult time understanding how to work with Arduino. If I can get a good start (and I do mean "start") then I think I can wrap my brain around it better and take off with it.
I have been able to make the light blink at different speeds on the Arduino board! 
Baby steps man, baby steps!


----------



## dshuld

Greg0u812 said:


> I have researched Arduino before and just felt like I would be getting in over my head with what I wanted to use it for.
> 
> I run DSunY lighting on my main tank and the lighting is pretty good.
> I originally wanted the sat+ lights but could the remote controller (as opposed to a fully automated setup) was not a doable option at all for me! I actually returned the six sat+ lights and ordered the DSunY lights. Yeah, my lfs wasn't very happy when I made that return!
> 
> I have most of what I need now for the Arduino project (hardware wise) but still feel somewhat lost at sea.
> Once I have the few remaining pieces, I would LOVE to see a fairly in-depth "How-to" write up!:bounce:
> My goal is being able to control 9 sat+ lights on 4 aquariums with 1 Arduino! I just want all four tanks to have the exact same timing and features.
> 
> I just don't understand why I am having a difficult time understanding how to work with Arduino. If I can get a good start (and I do mean "start") then I think I can wrap my brain around it better and take off with it.
> I have been able to make the light blink at different speeds on the Arduino board!
> Baby steps man, baby steps!


 
Lol I hear ya. I'll admit I'm a lil overwhelmed by the thought of this especially since some of the original posters have stopped posting in this thread. But I'm gonna give it a shot cause like you I'd like to run multiple lights on multiple tanks on the same schedule. Part of the reason I ordered the sat+ was for the "weather" features but sadly I didnt read far enough into that thread before ordering lol. I'm moving from a multiple Home Depot diamond plate light setup to led hopefully if I can figure this out.

My light should arrive from amazon today according to the email lol. I probably wont start ordering parts til some time this week though. One thing I'm wondering is how difficult it would be to combine the random pattern of this with the rest of the functions that were setup on the iaqua touch screen one. I may pm to see if that person thinks its possible and how difficult it'd be.


----------



## theknight

kman said:


> No, not so far. I've been hoping someone will post back with advice, as I'm new to this sort of thing. :/
> 
> The OPs pics came back to me later, I suggest trying again? Not sure why they didn't come up at first, but they're there now. Maybe try another browser, and be sure to hit refresh! They helped quite a bit, although there is quite a bit that's not really covered thoroughly, as I have discovered...
> 
> Once I'm past this step I think it will work well, and I'm looking forward to it.  It's been pretty straightforward so far, once I got all the materials together. I might put together a How-To to assist others once mine is set up and running properly, but I'm not really there yet...


There are a lot of people on this forum that know much more about the Arduino than I do. But looking at all your errors I think it may be the way your files are setup. The sketch needs to be able to find all the libraries listed in the sketch and it has a certain way of looking.

First make sure that you are saving the sketch under the Arduino folder in your document section (Library). You can do this by simply saving the file, verify that it now shows up under the file tab in "Sketchbook" when you open Arduino. Next make sure you have a subfolder under the same Arduino folder named "libraries" no variation in the spelling or lower case. Then make sure all the libraries used in the sketch are placed in the libraries subfolder. If you still have problems make sure that you are not changing anything in the sketch, even changing a lowercase to uppercase will prevent the sketch from finding the proper path. Arduino is very picky about format.

See if you sketch will compile now, if not make sure that you are identifying the proper board (nano vs Mega) under the tool tab. I went through similar problems in the beginning until I learned to check these simple things out.

Good luck and by the way no one has reported a change in IR codes, so you should be good to go with the codes in the sketch.


----------



## dshuld

*@ theknight*

sorry I tried to read back through quickly and guess I missed which version you ended up running. Did you go with storms and temp probs or just one or the other?


----------



## theknight

dshuld said:


> sorry I tried to read back through quickly and guess I missed which version you ended up running. Did you go with storms and temp probs or just one or the other?


I went with the original version, commented out the storms and later I modified the code to include temperature and PH


----------



## Linwood

Greg0u812 said:


> My goal is being able to control 9 sat+ lights on 4 aquariums with 1 Arduino! I just want all four tanks to have the exact same timing and features.


If you plan to have the 9 be independent be sure to plan for 9 emitters and some way to isolate them, e.g. tying emitter and receiver into a light tight bundle (and make sure it doesn't over-drive the receiver then in some way). 

I haven't done my 4-light system yet, still doing stand skins, but I am worried about that. Using one remote control I'd say 50% of the time I do not get consistent reaction, even when I try hard to make sure all the receivers are pointing in the same direction. ESPECIALLY on things like holding down a dimmer button, widely varying dimming rates between LED+'s. 

But even power off -- it's really annoying if one misses an "off" or "on", as then they just toggle, and you have to go try to shade one receiver to get them back in synch. That's why I program M4 as "all the way dim" and try to use that... 

But then I am still having the trouble that randomly two of them (i.e. HALF of them), are periodically forgetting the Mx button settings. So I might have to program around that.

I am looking forward to getting it computer controlled where I can do things more methodically. For example, I could send spaced out dim signals, see if they would respond more consistently with more space between. And I can send some signals repeatedly, e.g. if I want a specific color I can send it, with a gap, several times to make sure all got it.

PS. I'm using a Raspberry Pi. I decided the web enabled, more-full-unix approach there was easier to deal with.


----------



## Greg0u812

dshuld said:


> Lol I hear ya. I'll admit I'm a lil overwhelmed by the thought of this especially since some of the original posters have stopped posting in this thread. But I'm gonna give it a shot cause like you I'd like to run multiple lights on multiple tanks on the same schedule. Part of the reason I ordered the sat+ was for the "weather" features but sadly I didnt read far enough into that thread before ordering lol. I'm moving from a multiple Home Depot diamond plate light setup to led hopefully if I can figure this out.
> 
> My light should arrive from amazon today according to the email lol. I probably wont start ordering parts til some time this week though. One thing I'm wondering is how difficult it would be to combine the random pattern of this with the rest of the functions that were setup on the iaqua touch screen one. I may pm to see if that person thinks its possible and how difficult it'd be.


The possibility of random weather was what pushed me to start ordering parts and buying Arduinos.
I highly doubt you will regret changing out the HD diamond plates (which do look very nice in a well kept garage or shop (I have 12 of them hanging in mine)) for the LED lights.




theknight said:


> ...
> 
> First make sure that you are saving the sketch under the Arduino folder in your document section (Library). You can do this by simply saving the file, verify that it now shows up under the file tab in "Sketchbook" when you open Arduino. Next make sure you have a subfolder under the same Arduino folder named "libraries" no variation in the spelling or lower case. Then make sure all the libraries used in the sketch are placed in the libraries subfolder. ...


THAT's the type of thing I'm talking about!

Thank you theknight.

Like I said...
Baby steps, that's where I'm at with this Arduino stuff.


----------



## kman

theknight said:


> There are a lot of people on this forum that know much more about the Arduino than I do. But looking at all your errors I think it may be the way your files are setup. The sketch needs to be able to find all the libraries listed in the sketch and it has a certain way of looking.
> 
> First make sure that you are saving the sketch under the Arduino folder in your document section (Library). You can do this by simply saving the file, verify that it now shows up under the file tab in "Sketchbook" when you open Arduino. Next make sure you have a subfolder under the same Arduino folder named "libraries" no variation in the spelling or lower case. Then make sure all the libraries used in the sketch are placed in the libraries subfolder. If you still have problems make sure that you are not changing anything in the sketch, even changing a lowercase to uppercase will prevent the sketch from finding the proper path. Arduino is very picky about format.
> 
> See if you sketch will compile now, if not make sure that you are identifying the proper board (nano vs Mega) under the tool tab. I went through similar problems in the beginning until I learned to check these simple things out.
> 
> Good luck and by the way no one has reported a change in IR codes, so you should be good to go with the codes in the sketch.


Thank you so much! I should have some time to play with this again on Sunday, so I'll try that out for sure! I thought the library was in the right place but maybe not. I definitely set it to Mega in the software. 

What about the LCD? Do you know if I need to cut off that backpack thing and wire up all 9 or so wires, or do I genuinely only need the 4-5 wires depicted in your schematic to make this LCD work?


----------



## theknight

kman said:


> Thank you so much! I should have some time to play with this again on Sunday, so I'll try that out for sure! I thought the library was in the right place but maybe not. I definitely set it to Mega in the software.
> 
> What about the LCD? Do you know if I need to cut off that backpack thing and wire up all 9 or so wires, or do I genuinely only need the 4-5 wires depicted in your schematic to make this LCD work?


If I remember correctly you have the same LCD that I am using, if you modify the code like this:

#define I2C_ADDR 0x3f // I2C address of PCF8574A
#define BACKLIGHT_PIN 3
#define En_pin 2
#define Rw_pin 1
#define Rs_pin 0
#define D4_pin 4
#define D5_pin 5
#define D6_pin 6
#define D7_pin 7

LiquidCrystal_I2C lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin, BACKLIGHT_PIN, POSITIVE);

The above replaces this line " LiquidCrystal lcd(7, 8, 9, 10, 11, 12); "

Then add the libraries - LiquidCrystal_I2C.h and LCD.h in the included libraries and again make sure they are included in the "libraries" subfolder.

It should work as I2C with just 4 wires, 5 Volt +, 5 Volt -, SDA & SCL. I guess you could go through the trouble of removing the LCD and wiring it with the 9 wires or so, but I don't know why you would want to do that when you already paid and have something that will simplify the connection.

Find an "Hello World" sketch for the LCD and experiment with it first to make sure you have the code correct, it might make it easier for you to understand one part at a time


----------



## kman

You rock. THANK YOU!!!

I can't wait to try this out! 

It's killing me that I'm not going to be able to pull the project back out until tomorrow evening...


----------



## Greg0u812

Linwood said:


> If you plan to have the 9 be independent be sure to plan for 9 emitters and some way to isolate them, e.g. tying emitter and receiver into a light tight bundle (and make sure it doesn't over-drive the receiver then in some way).
> 
> I haven't done my 4-light system yet, still doing stand skins, but I am worried about that. Using one remote control I'd say 50% of the time I do not get consistent reaction, even when I try hard to make sure all the receivers are pointing in the same direction. ESPECIALLY on things like holding down a dimmer button, widely varying dimming rates between LED+'s.
> 
> But even power off -- it's really annoying if one misses an "off" or "on", as then they just toggle, and you have to go try to shade one receiver to get them back in synch. That's why I program M4 as "all the way dim" and try to use that...
> 
> But then I am still having the trouble that randomly two of them (i.e. HALF of them), are periodically forgetting the Mx button settings. So I might have to program around that.
> 
> I am looking forward to getting it computer controlled where I can do things more methodically. For example, I could send spaced out dim signals, see if they would respond more consistently with more space between. And I can send some signals repeatedly, e.g. if I want a specific color I can send it, with a gap, several times to make sure all got it.
> 
> PS. I'm using a Raspberry Pi. I decided the web enabled, more-full-unix approach there was easier to deal with.



This is definitely something that has been a concern to me.
I would MUCH rather be able to hard-wire the setup because I honestly do believe this is going to cause some issues for me over a period of time. It's all part of the game though.

I could end up running multiple Arduinos and synching them to a time server. That could solve a couple issues and maybe even open up some other possibilities.

My primary goal at this point is to have one board reliably control one sat+ and have it act the way I would like it to.
After that has been accomplished then I can start adding functions and features. I should also be able to diagnose issues that arise and (hopefully) figure out how to correct those issues.

Unfortunately, it will most likely be early January before I can afford the physical and mental time to commit to this. I do have a couple hours per day for the next several weeks that I can try and get into the mental side though.


----------



## kman

Greg, I wonder if you could fire multiple LEDs instead of just the one? I can't imagine there isn't enough juice to push 2-3 LEDs. Then you could bundle them into smaller groups, carefully positioned and taped so they don't move out of position. That might give you a bit more redundancy. Just a thought, anyway. Might be easier than making multiple Arduinos.


----------



## kman

theknight said:


> There are a lot of people on this forum that know much more about the Arduino than I do. But looking at all your errors I think it may be the way your files are setup. The sketch needs to be able to find all the libraries listed in the sketch and it has a certain way of looking.
> 
> First make sure that you are saving the sketch under the Arduino folder in your document section (Library). You can do this by simply saving the file, verify that it now shows up under the file tab in "Sketchbook" when you open Arduino. Next make sure you have a subfolder under the same Arduino folder named "libraries" no variation in the spelling or lower case. Then make sure all the libraries used in the sketch are placed in the libraries subfolder. If you still have problems make sure that you are not changing anything in the sketch, even changing a lowercase to uppercase will prevent the sketch from finding the proper path. Arduino is very picky about format.
> 
> See if you sketch will compile now, if not make sure that you are identifying the proper board (nano vs Mega) under the tool tab. I went through similar problems in the beginning until I learned to check these simple things out.
> 
> Good luck and by the way no one has reported a change in IR codes, so you should be good to go with the codes in the sketch.


Ok, here were the issues:

1) I was indeed missing libraries. The code itself discusses (specifically) needing "Ken Shirriff's IRremote library" and provides a download link. BUT there are other libraries that need to be added, also. Naturally, NOW I see, in the top credits box, that it says "Req. Time, TimeAlarms, RTClib, IRremote" but that wasn't clear enough for this Arduino n00b to catch. Thus my confusion. Clearly my bad, although it could be made clearer for noobs like myself, ideally with the links like they include for the IRremote library.


Time (need to download HERE and add, mentioned in post 21)
TimeAlarms (need to download HERE and add, mentioned in post 21)
RTClib (need to download HERE and add, mentioned in post 88)
IRremote (need to download from link in code)
LiquidCrystal (included with Arduino IDE, no need to do anything)
2) In addition to adding the libraries, since I don't want any thunderstorms, I needed to comment out the ENTIRE "void ThunderStorm" section of the sketch, starting from "void thunderstorm: and ending just before "void digitalClockDisplay". The code comments seem to imply that all you need to do to eliminate the Thunderstorms is comment out the code at the beginning, but I was getting errors compiling under I nuked that whole void thunderstorm section.

3) And next, two more missing libraries (at least, for the LCD we're using):


LiquidCrystal_I2C (this, I discovered, is included with the library package below... and adding both to your library is *bad*!)
LCD (need to download HERE and add)
It looks like the new LCD library includes a newer version of LiquidCrystal, as well, but it seems to work so I'll ignore the apparent conflict.

4) Also: Apparently you can't add a new library without adding an include statement to the sketch, based on the next batch of compile errors, so I added with the other includes:



Code:


#include <LCD.h>
#include <LiquidCrystal_I2C.h>

5) Your code had a few spacing errors, or perhaps it just went wonky when one of us pasted? I believe it should be (no spaces anywhere in the parenthesis):



Code:


LiquidCrystal_I2C lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin,BACKLIGHT_PIN,POSITIVE);

And NOW, I finally have code that compiles clean! Passed check no problem, and uploaded clean!

Now I just have to figure out how to wire the LCD in. I'm not very good with schematics, unfortunately. :iamwithst

I THINK I need to wire this through the breadboard, so the RTC (currently plugged directly into 5V under the "Power" section of the board, and into GND as well) can share power and signal with the LCD. And I THINK the SCL and SDA from the LCD then plug into the board directly, in the "Communication" section pins, at 20 and 21?


----------



## kman

kman said:


> I THINK I need to wire this through the breadboard, so the RTC (currently plugged directly into 5V under the "Power" section of the board, and into GND as well) can share power and signal with the LCD. And I THINK the SCL and SDA from the LCD then plug into the board directly, in the "Communication" section pins, at 20 and 21?


Update: This SEEMS to have worked... once I corrected the I2C code to use the correct address for the LCD I have. Apparently I have a slightly different model from theknight: My LCD I2C backpack is "YwRobot Arduino LCM1602 IIC V1". I found details here:

http://arduino-info.wikispaces.com/LCD-Blue-I2C#v1

Apparently the address for mine is 0x27, not 0x3f (theknight's I2C address). Once I corrected that, wired as described above, it fired up and displayed! The test code I found worked, too, and I got a "Hello Arduino" displaying correctly. 

Unfortunately, the ONLY thing the display seems to be showing is the time... sort of. It says 17:21 right now (it's 9:42pm local time). I'm not clear on how to set the time on the clock. Further, it doesn't show the light mode currently running. I have to assume it is actually broacastinhg IR commands, since I'm in another room from my lights while testing all this.

And now I just glanced and it and it now says 17:18. And then it changed to 17:19 and 17:20. WTF?

If it helps, here is the full code I'm using currently, as modified:



Code:


///////////////////////////////////////////////////////////////////
// Current Satellite LED+ Controller  V3.6  - MODIFIED FOR KMAN  //
//   Indychus...Dahammer...mistergreen @ plantedtank.net         //
//   This code is public domain.  Pass it on.                    //
//   Confirmed on Arduino UNO 1.0.5                              //
//   Req. Time, TimeAlarms, RTClib, IRremote                     //
///////////////////////////////////////////////////////////////////
//
// This version uses Ken Shirriff's IRremote library to Rx/Tx the IR codes
// http://www.righto.com/2009/08/multi-protocol-infrared-remote-library.html
// 
// This code does NOT use PIN 13 on the Uno, as do previous versions
// Instead PIN 3, which is a PWM pin, is used. So you'll need to connect
// your LED to PIN 3 instead of PIN 13 for it to work.

// Install LCD per instructions at http://learn.adafruit.com/character-lcds/overview

#include <Wire.h>
#include <RTClib.h>
#include <Time.h>
#include <TimeAlarms.h>
#include <IRremote.h>
#include <LiquidCrystal.h>
#include <LCD.h>  //added for use with I2C LCD backpack
#include <LiquidCrystal_I2C.h> //added for use with I2C LCD backpack

RTC_DS1307 RTC;
IRsend irsend;
//LiquidCrystal lcd(7, 8, 9, 10, 11, 12);

//BEGIN new section added for I2C LCD backpack
#define I2C_ADDR 0x27 // 0x3f = I2C address of PCF8574A  ~ OR ~ 0x27 is for backpack: YwRobot Arduino LCM1602 IIC V1
#define BACKLIGHT_PIN 3
#define En_pin 2
#define Rw_pin 1
#define Rs_pin 0
#define D4_pin 4
#define D5_pin 5
#define D6_pin 6
#define D7_pin 7

LiquidCrystal_I2C lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin,BACKLIGHT_PIN,POSITIVE);
//END new section added for I2C LCD backpack

/*********** BEGIN USER DEFINED VARIABLES ***********/
// DEBUG_IR adds the option to test the IR commands via the Arduino software's serial monitor
// You can send any value from 1 to 32 and it send the corresponding IR code
// The codes follow the remote controller, left to right, top to bottom
// e.g 1 = Orange, 2 = Blue, 21 = Moon1, etc
#define DEBUG_IR

int postDelay = 100;         // Delay after codes are sent
int randAnalogPin = 0;       // This needs to be set to an unused Analog pin, Used by ThunderStorm()

// Current Satellite+ IR Codes (NEC Protocol)
unsigned long codeHeader = 0x20DF; // Always the same

// Remote buttons listed left to right, top to bottom
unsigned int codeOrange = 0x3AC5;
unsigned int codeBlue = 0xBA45;
unsigned int codeRose = 0x827D;
unsigned int codePowerOnOff = 0x02FD;
unsigned int codeWhite = 0x1AE5;
unsigned int codeFullSpec = 0x9A65;
unsigned int codePurple = 0xA25D;
unsigned int codePlay = 0x22DD;
unsigned int codeRedUp = 0x2AD5;
unsigned int codeGreenUp = 0xAA55;
unsigned int codeBlueUp = 0x926D;
unsigned int codeWhiteUp = 0x12ED;
unsigned int codeRedDown = 0x0AF5;
unsigned int codeGreenDown = 0x8A75;
unsigned int codeBlueDown = 0xB24D;
unsigned int codeWhiteDown = 0x32CD;
unsigned int codeM1Custom = 0x38C7;
unsigned int codeM2Custom = 0xB847;
unsigned int codeM3Custom = 0x7887;
unsigned int codeM4Custom = 0xF807;
unsigned int codeMoon1 = 0x18E7;
unsigned int codeMoon2 = 0x9867;
unsigned int codeMoon3 = 0x58A7;
unsigned int codeDawnDusk = 0xD827;
unsigned int codeCloud1 = 0x28D7;
unsigned int codeCloud2 = 0xA857;
unsigned int codeCloud3 = 0x6897;
unsigned int codeCloud4 = 0xE817;
unsigned int codeStorm1 = 0x08F7;
unsigned int codeStorm2 = 0x8877;
unsigned int codeStorm3 = 0x48B7;
unsigned int codeStorm4 = 0xC837;

void SetAlarms()
{
  // Set up your desired alarms here
  // The default value of dtNBR_ALARMS is 6 in Alarms.h.
  // This code sets 12 alarms by default, so you'll need to change dtNBR_ALARMS to 12 or more
  Alarm.alarmRepeat(7,00,0, DawnDusk);
  Alarm.alarmRepeat(9,00,0, Cloud2);     // (HR,MIN,SEC,FUNCTION)
  Alarm.alarmRepeat(11,00,0, FullSpec);
  Alarm.alarmRepeat(15,00,0, Cloud2);
  Alarm.alarmRepeat(19,00,0, DawnDusk);
  Alarm.alarmRepeat(21,00,0, Moon2);
  
  // Comment these out if you don't want the chance of a random storm each day
//  Alarm.alarmRepeat(12,00,00, ThunderStorm);
//  ThunderStorm();  // Sets up intial storm so we don't have wait until alarm time
}
/************* END USER DEFINED VARIABLES *************/

void setup()
{
  Wire.begin();
  RTC.begin();
  lcd.begin(20, 4);
  Serial.begin(9600);
      //Serial.println(freeRam());
  
  if (! RTC.isrunning()) { 
    Serial.println("RTC Error");
    RTC.adjust(DateTime(__DATE__, __TIME__));}  //Adjust to compile time
    
  
  setSyncProvider(syncProvider);     //reference our syncProvider function instead of RTC_DS1307::get()
  
  Alarm.timerRepeat(900, digitalClockDisplay);  // Display the time every 15 minutes
  digitalClockDisplay();
  SetAlarms();
 Serial.print("SRAM : ");          //un-comment these line to check available SRAM
 Serial.println(freeRam());}   

void loop()
{
#ifdef DEBUG_IR 
  if (Serial.available() > 0) {
    delay(5); //Wait for transmission to finish
    CurrentCMDs(SerialReadInt());
  }
#endif
  Alarm.delay(100); 
  // Service alarms & wait (msec)
  lcdClockDisplay();
}

time_t syncProvider()
{
  //this does the same thing as RTC_DS1307::get()
  return RTC.now().unixtime();
}

//void ThunderStorm ()
//{ 
//  // Schedules a storm between 1 & 9 in the evening
//  // It sets Storm2, followed by Cloud2 or DawnDusk or Moon2, depending on when the storm is over
//  randomSeed(analogRead(randAnalogPin));  // Generate random seed on unused pin
//  byte RH = random(23);                   // Randomizer for thunderstorm
//  byte RM = random(59);
//  byte RS = random(59);
//  byte TSDurationH = random(2);
//  byte TSDurationM = random(59);
//  
//  if (RH <= 12)
//    {
//      Serial.println("No storm today");
//      lcd.setCursor(0,1);
//      lcd.print("No storm today");
//      return;
//    }
//      
//  if (RH > 12)                             // If random value is after 1 pm, allow storm
//    {
//      Alarm.alarmOnce(RH,RM,RS,Storm2);
//      Serial.print("Next Storm: ");
//      Serial.print(RH);
//      printDigits(RM);
//      printDigits(RS);
//      Serial.print("   ");
//      Serial.print("Duration = ");
//      Serial.print(TSDurationH);
//      printDigits(TSDurationM);
//      Serial.println();
//     lcd.setCursor(0,1);
//     lcd.print("Next Storm: ");
//     lcdHRdigits(RH);
//     lcdDigits(RM);}
//      
//      if ((RH + TSDurationH) < 19)   // Return to Cloud2 if storm ends between 1-7pm
//        {Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,Cloud2);}
//      else if ((RH + TSDurationH) < 21)  // Return to DawnDusk if storm ends between 7-9pm
//        {Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,DawnDusk);}
//      else                                       // Return to Night2 if storm ends after 9pm
//        {Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,Moon2);}
//    }


void digitalClockDisplay()          // Digital clock
{ 
  Serial.print("Time = ");
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.println(); }

  
void lcdClockDisplay()  
  {lcd.setCursor(0,0);
    lcdHRdigits(hour());
  lcdDigits(minute());}

void printDigits(int digits)        // Add :
{
  Serial.print(":");
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits);
}

void lcdDigits(int digits)        // Add :
{
  lcd.print(":");
  if(digits < 10)
    lcd.print('0');  
  lcd.print(digits);
}
void lcdHRdigits(int HRdigits)        // Preface hour with 0
{
  if(HRdigits < 10)
    lcd.print('0');  
  lcd.print(HRdigits);
}

#ifdef DEBUG_IR
int SerialReadInt()
{
  int i, numAva;
  char inBytes[3];                  // Array to hold the bytes
  char * inBytesPtr = &inBytes[0];  // Pointer to the first element of the array
      
    numAva = Serial.available();    // Read number of input bytes
    if (numAva > 2)
      numAva = 2;                   // Only allow 2 characters to prevent overflow
      
    for (i=0; i<numAva; i++)        // Load input bytes into array
      inBytes[i] = Serial.read();
    inBytes[i] =  '\0';             // Put NULL character at the end
    return atoi(inBytesPtr);        // Call atoi function and return result
}

void CurrentCMDs (int cmd)
{switch (cmd)
  {
    case 1:
      Orange();
      break;
    case 2:
      Blue();
      break;
    case 3:
      Rose();
      break;
    case 4:
      PowerOnOff();
      break;
    case 5:
      White();
      break;
    case 6:
      FullSpec();
      break;
    case 7:
      Purple();
      break;
    case 8:
      Play();
      break;
    case 9:
      RedUp();
      break;
    case 10:
      GreenUp();
      break;
    case 11:
      BlueUp();
      break;
    case 12:
      WhiteUp();
      break;
    case 13:
      RedDown();
      break;
    case 14:
      GreenDown();
      break;
    case 15:
      BlueDown();
      break;
    case 16:
      WhiteDown();
      break;
    case 17:
      M1Custom();
      break;
    case 18:
      M2Custom();
      break;
    case 19:
      M3Custom();
      break;
    case 20:
      M4Custom();
      break;
    case 21:
      Moon1();
      break;
    case 22:
      Moon2();
      break;
    case 23:
      Moon3();
      break;
    case 24:
      DawnDusk();
      break;
    case 25:
      Cloud1();
      break;
    case 26:
      Cloud2();
      break;
    case 27:
      Cloud3();
      break;
    case 28:
      Cloud4();
      break;
    case 29:
      Storm1();
      break;
    case 30:
      Storm2();
      break;
    case 31:
      Storm3();
      break;
    case 32:
      Storm4();
      break;
    default:
      Serial.println("Invalid Choice");}}
#endif

void SendCode (unsigned int code, byte numTimes, const char *sMessage)
{unsigned long irCode = (codeHeader << 16) + code; // Header is 2 bytes, shift all the way to left & add code to it
  
  for( int i = 0; i < numTimes; i++)
  {irsend.sendNEC(irCode,32); // Send code
    Alarm.delay(postDelay);}
    
  Serial.println(sMessage);  // Print message
  lcd.setCursor(6,0);
  lcd.print(sMessage);
  for(byte i = sizeof(sMessage); i <= 14; i++)
    lcd.print(" ");
  digitalClockDisplay();}

void Orange()
{SendCode(codeOrange, 2, "Orange");}

void Blue()
{SendCode(codeBlue, 2, "Blue");}

void Rose()
{SendCode(codeRose, 2, "Rose");}

void PowerOnOff()
{SendCode(codePowerOnOff, 1, "Power On/Off");}

void White()
{SendCode(codeWhite, 2, "White");}

void FullSpec()
{SendCode(codeFullSpec, 2, "Full Spectrum");}

void Purple()
{SendCode(codePurple, 2, "Purple");}

void Play()
{SendCode(codePlay, 1, "Play/Pause:");}

void RedUp()
{SendCode(codeRedUp, 1, "Red Up");}

void GreenUp()
{SendCode(codeGreenUp, 1, "Green Up");}

void BlueUp()
{SendCode(codeBlueUp, 1, "Blue");}

void WhiteUp()
{SendCode(codeWhiteUp, 1, "White Up");}

void RedDown()
{SendCode(codeRedDown, 1, "Red Down");}

void GreenDown()
{SendCode(codeGreenDown, 1, "Green Down");}

void BlueDown()
{SendCode(codeBlueDown, 1, "Blue Down");}

void WhiteDown()
{SendCode(codeWhite, 1, "White Down");}

void M1Custom()
{SendCode(codeM1Custom, 2, "Custom Mix 1");}

void M2Custom()
{SendCode(codeM2Custom, 2, "Custom Mix 2");}

void M3Custom()
{SendCode(codeM3Custom, 2, "Custom Mix 3");}

void M4Custom()
{SendCode(codeM4Custom, 2, "Custom Mix 4");}

void Moon1()
{SendCode(codeMoon1, 2, "Moonlight 1");}

void Moon2()
{SendCode(codeMoon2, 2, "Moonlight 2");}

void Moon3()
{SendCode(codeMoon3, 2, "Moonlight 3");}

void DawnDusk()
{SendCode(codeDawnDusk, 2, "Dawn/Dusk");}

void Cloud1()
{SendCode(codeCloud1, 2, "Cloud Cover 1");}

void Cloud2()
{SendCode(codeCloud2, 2, "Cloud Cover 2");}
  
void Cloud3()
{SendCode(codeCloud3, 2, "Cloud Cover 3");}

void Cloud4()
{SendCode(codeCloud4, 2, "Cloud Cover 4");}

void Storm1()
{SendCode(codeStorm1, 2, "Thunderstorm 1");}

void Storm2()
{SendCode(codeStorm2, 2, "Thunderstorm 2");}

void Storm3()
{SendCode(codeStorm3, 2, "Thunderstorm 3");}

void Storm4()
{SendCode(codeStorm4, 2, "Thunderstorm 4");}

int freeRam () {
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}

Ugh. I just re-uploaded the code, and it's reading "17:18" again.

Just to be sure, I just switched back to the example code sketch, which works perfectly, so it's at least somewhat hooked up correctly!



Code:


/* YourDuino.com Example Software Sketch
 20 character 4 line I2C Display
 Backpack Interface labelled "YwRobot Arduino LCM1602 IIC V1"
 Connect Vcc and Ground, SDA to A4, SCL to A5 on Arduino
 [email protected] */

/*-----( Import needed libraries )-----*/
#include <Wire.h>  // Comes with Arduino IDE
// Get the LCD I2C Library here: 
// https://bitbucket.org/fmalpartida/new-liquidcrystal/downloads
// Move any other LCD libraries to another folder or delete them
// See Library "Docs" folder for possible commands etc.
#include <LiquidCrystal_I2C.h>

/*-----( Declare Constants )-----*/
/*-----( Declare objects )-----*/
// set the LCD address to 0x27 for a 20 chars 4 line display
// Set the pins on the I2C chip used for LCD connections:
//                    addr, en,rw,rs,d4,d5,d6,d7,bl,blpol
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // Set the LCD I2C address


/*-----( Declare Variables )-----*/


void setup()   /*----( SETUP: RUNS ONCE )----*/
{
  Serial.begin(9600);  // Used to type in characters

  lcd.begin(20,4);         // initialize the lcd for 20 chars 4 lines, turn on backlight

// ------- Quick 3 blinks of backlight  -------------
  for(int i = 0; i< 3; i++)
  {
    lcd.backlight();
    delay(250);
    lcd.noBacklight();
    delay(250);
  }
  lcd.backlight(); // finish with backlight on  

//-------- Write characters on the display ------------------
  // NOTE: Cursor Position: Lines and Characters start at 0  
  lcd.setCursor(3,0); //Start at character 4 on line 0
  lcd.print("Hello, world!");
  delay(1000);
  lcd.setCursor(2,1);
  lcd.print("From YourDuino");
  delay(1000);  
  lcd.setCursor(0,2);
  lcd.print("20 by 4 Line Display");
  lcd.setCursor(0,3);
  delay(2000);   
  lcd.print("http://YourDuino.com");
  delay(8000);
  // Wait and then tell user they can start the Serial Monitor and type in characters to
  // Display. (Set Serial Monitor option to "No Line Ending")
  lcd.setCursor(0,0); //Start at character 0 on line 0
  lcd.print("Start Serial Monitor");
  lcd.setCursor(0,1);
  lcd.print("Type chars 2 display");  


}/*--(end setup )---*/


void loop()   /*----( LOOP: RUNS CONSTANTLY )----*/
{
  {
    // when characters arrive over the serial port...
    if (Serial.available()) {
      // wait a bit for the entire message to arrive
      delay(100);
      // clear the screen
      lcd.clear();
      // read all the available characters
      while (Serial.available() > 0) {
        // display each character to the LCD
        lcd.write(Serial.read());
      }
    }
  }

}/* --(end main loop )-- */


/* ( THE END ) */

... and reloading the Sat+ control sketch once again goes back to displaying 17:18.

I'm close, that's for sure.


----------



## theknight

The current light mode on my LCD will not show until it runs and hits the next alarm time.

As far as the clock, here is a sketch that will set the clock to your current date and time:

///////////////////////////////////////////
// RTC data and time setter //
// //
// This sample program allows the user //
// to set the date and time of an RTC //
// using I2C. //
// //
// Codes by: //
// eGizmo Mechatronix Central //
// Taft, Manila, Philippines //
// http://www.egizmo.com //
// April 15, 2013 //
///////////////////////////////////////////

#include <Wire.h>
const int DS1307 = 0x68; // Address of DS1307 see data sheets
const char* days[] =
{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
const char* months[] =
{"January", "February", "March", "April", "May", "June", "July", "August","September", "October", "November", "December"};

// Initializes all values: 
byte second = 0;
byte minute = 0;
byte hour = 0;
byte weekday = 0;
byte monthday = 0;
byte month = 0;
byte year = 0;

void setup() {
Wire.begin();
Serial.begin(9600);
delay(2000); // This delay allows the MCU to read the current date and time.

Serial.print("The current date and time is: ");
printTime();
Serial.println("Please change to newline ending the settings on the lower right of the Serial Monitor");
Serial.println("Would you like to set the date and time now? Y/N");

while (!Serial.available()) delay(10);
if (Serial.read() == 'y' || Serial.read() == 'Y')

// This set of functions allows the user to change the date and time
{
Serial.read();
setTime();
Serial.print("The current date and time is now: ");
printTime();
}


Serial.println("Thank you.");
}

// Continuous function for converting bytes to decimals and vice versa
void loop() {
}
byte decToBcd(byte val) {
return ((val/10*16) + (val%10));
}
byte bcdToDec(byte val) {
return ((val/16*10) + (val%16));
}


// This set of codes is allows input of data
void setTime() {
Serial.print("Please enter the current year, 00-99. - ");
year = readByte();
Serial.println(year);
Serial.print("Please enter the current month, 1-12. - ");
month = readByte();
Serial.println(months[month-1]);
Serial.print("Please enter the current day of the month, 1-31. - ");
monthday = readByte();
Serial.println(monthday);
Serial.println("Please enter the current day of the week, 1-7.");
Serial.print("1 Sun | 2 Mon | 3 Tues | 4 Weds | 5 Thu | 6 Fri | 7 Sat - ");
weekday = readByte();
Serial.println(days[weekday-1]);
Serial.print("Please enter the current hour in 24hr format, 0-23. - ");
hour = readByte();
Serial.println(hour);
Serial.print("Please enter the current minute, 0-59. - ");
minute = readByte();
Serial.println(minute);
second = 0;
Serial.println("The data has been entered.");

// The following codes transmits the data to the RTC
Wire.beginTransmission(DS1307);
Wire.write(byte(0));
Wire.write(decToBcd(second));
Wire.write(decToBcd(minute));
Wire.write(decToBcd(hour));
Wire.write(decToBcd(weekday));
Wire.write(decToBcd(monthday));
Wire.write(decToBcd(month));
Wire.write(decToBcd(year));
Wire.write(byte(0));
Wire.endTransmission();
// Ends transmission of data
}


byte readByte() {
while (!Serial.available()) delay(10);
byte reading = 0;
byte incomingByte = Serial.read();
while (incomingByte != '\n') {
if (incomingByte >= '0' && incomingByte <= '9')
reading = reading * 10 + (incomingByte - '0');
else;
incomingByte = Serial.read();
}
Serial.flush();
return reading;
}


void printTime() {
char buffer[3];
const char* AMPM = 0;
readTime();
Serial.print(days[weekday-1]);
Serial.print(" ");
Serial.print(months[month-1]);
Serial.print(" ");
Serial.print(monthday);
Serial.print(", 20");
Serial.print(year);
Serial.print(" ");
if (hour > 12) {
hour -= 12;
AMPM = " PM";
}
else AMPM = " AM";
Serial.print(hour);
Serial.print(":");
sprintf(buffer, "%02d", minute);
Serial.print(buffer);
Serial.println(AMPM);
}


void readTime() {
Wire.beginTransmission(DS1307);
Wire.write(byte(0));
Wire.endTransmission();
Wire.requestFrom(DS1307, 7);
second = bcdToDec(Wire.read());
minute = bcdToDec(Wire.read());
hour = bcdToDec(Wire.read());
weekday = bcdToDec(Wire.read());
monthday = bcdToDec(Wire.read());
month = bcdToDec(Wire.read());
year = bcdToDec(Wire.read());
}


Hope this helps


----------



## kman

Hmm. Now I'm starting to wonder if something is wrong with my RTC.

I uploaded your sketch, and started to get really excited once I figured out the serial monitor was needed, open, so I could enter the time. But here is what I got when I ran it:



Code:


Please change to newline ending the settings on the lower right of the Serial Monitor
Would you like to set the date and time now? Y/N
Please enter the current year, 00-99. - 14
Please enter the current month, 1-12. - October
Please enter the current day of the month, 1-31. - 6
Please enter the current day of the week, 1-7.
1 Sun | 2 Mon | 3 Tues | 4 Weds | 5 Thu | 6 Fri | 7 Sat - Monday
Please enter the current hour in 24hr format, 0-23. - 9
Please enter the current minute, 0-59. - 32
The data has been entered.
The current date and time is now:  xÕÿIó7÷_[censored]r»íöëü¿:¿ÿï÷ýÄÎ½ãÿ,*Ï\ŸúÌ³wêß´ý;WõùMîöÍ7uï�^ÿá½_¿hÅîVj<_ãáù‡Y¿—Óí·Ýb¾ßvùWßÏÞçkþ›úÙ¶¼º®þ½ýúFÿõküéZµö6%ÐŸn'ß_ß;ÿ^‘¾óÏyÿþÌÏOÿ[h¿ßß;ûùÜï½û¯çµ;[censored]v÷¾î�sw»ù~<µ¦™÷~ÎµÞŸ¼Ÿ›™ïí×ÿ[censored]wûàª$©:¡Ÿµ?ßxÿ|uñï®yß½w¥þ³�<Ÿ¯Úßæ~?ó[censored]÷�µhkÖ_¿·ÿÝ5×ÍŸ½ïÿüý_yÒÊ©§öëËü{×ýûŽ_1¸ÉvÝTí¹Üû¿éÍ¿o[censored]??îGs§~<(ûNÏÜµÿe5ùÏßWÏ6mþß~ûúÏ»Ï_ÿ£ðùož¤wó¾¾hþÞXWý7Å_ôÿÞ]íc¬á-þ™ö\Ënû½ó÷þo¦ê±ù&êÌ¿ÿÿ·õ5ÿûxÛë»KMrù+>»ô÷[u{üW«æ?Ý?¹)¯³µm×³ÝJ_Á—÷ý=,ëî¾[censored]–ÿ¿¥…‘µŸÊu=õ}«^Õ{}ß³ÿ¿Oþ;õÅy×÷[censored]ïçVxz³…_¾üw{”¿®zý=Ü5¾ÿòŸ¾oúÛ´\4—vsxùÿôöý·ë®~÷ýïÝwìÚèÿÍ"ãñ)ëœ[censored]û´Ö‹þýþ}Í}´6ìÕþ½ç5/bÏÿîÿñm§Vÿ–^·»}†7Õ
3ïä=î÷ï½Ïw_Ýÿú<û/ýkžÿÿ(þß÷ï}7;¾÷ýNÊÑ�=Ð_~¯ø¾ÿ�‘>þ™üïsékþÉ»çßæ[ã}}ýôÌßüª¾ÓSü¯Wìïý•»wý@Çuýw/ú®µ�iküýn·}íš=Ö}÷g”¯ç]/TþQ
ôîïýýc�Owÿ}Ã§nu‡¿ý2ñßý÷ÀüÕðÏ/ßr—óÓÁ÷žîÚ9¾ß~_¯ôrßÿO¾Æîÿöø>ûýP¹»vï—Y�Gßåþ¬åÿŠç_û¿oëÜûøÌílúÿ•ß*g´Þ�¿³ú¾óyß.üdé»H~ÐÍîùåÑþÿ¯èmä~öíòwÎ}Ùù;¶·7°íÿ?³ßö[ÙßSü{û©:³²|î[ÞwÙÝ].ü³÷ÝŸx¬öÍÿOïë
¾œwge_Þ["to²çÑ_þóáõ{þÕú»Í¾ü‰Ïÿþ�¢/ÎÞM¯'W“0ýR¿•«=�mþÿßßçôñýöõøòUûûŽ^°‹×înn¯ÞSÉ¾_½è3ß5ÇÿþûÒèïïïÿœ»ú‰ùOäñ—¶;™–Œ�¾¤>û§ßï¿½Ž)ý;Ýwm½Œƒºo[4Îéý÷ïk×Íï~swóeÀÌ�«ýñßþ�ëöÿïT®×G¯?_ÖŸ¦zÝ«ùÔ‡ÿ»ÿúïk½çïÿwüMñ0©ÜuÁ��å~oßCÄëoô»¾k¶²Ûÿÿ•·Yö;í?—¬§Ým¶ûÉ×²õï[G•§ú¤éÍŸ××÷ÌûÞ=»w¿ö›ÿêÝwï÷}�ÿï�æ^WíÝú|¾þwxÝãŸ¿›÷?ÓÞ¶mlë=ÎÿG^(©ÿÒÛîÿÜõ»úû±ý~Ÿø[censored]:óÍú¿Ï–á÷þÿ�W}4éW÷ÚiÇþ¾‹ÔÒ÷CœÝºe¢öýýçŽñ×]óï]jg«}áKÁÞöÿ¿ýçÜ{|ÿÉÚï§žÏ§o÷Ý}Í]ýß~«Û½/÷Ç“ßìæÿï—ûgýÿþ÷¬QÕU|ïo3¿ÐýTõ–¼¦¯èÿ…öþôýw¾kXºå÷oÊ³¿i,F}ÿw~é§Ì[censored]¯ôyóMÿü®¹ƒó×{çî¿ÿ®ûýÛ¬¿½ï6Ðx/ÿý/ÕÜ_[ÓwúßÎsÓûs¸ÿüó]Fó_ç¢ÿïÿsTÿJßûþõ=®õ?ÔÑÐÿ5§ÿSÿú¿¼MGÿ;¿7ÿÏuîèvûßÿ^/ÿí¿ëïo½šnÿÎ_uýfï‘[½çOûÿÜôíOÂÿš©wŒåsÓß¿ÿ·¶»m{Ógú_äkÿ_ÒØ]Þ7û/¿T~�ý‰¹þïkÍáûSå>{ÿsßóºú[qÛ^oÆãž7^_þ{§„âÛ³ÿï¿Q®ÿ×Æþ]oq+i_.ÀôÆlíý\Ö·ûy}¾�ëEÿßßú®M³Iþëÿwýì·¥|îo÷Þ~7ózýúç/ñ¿íVxU?ï:Ë®¿ù¶wÞÿ¯éë~Tû§èï¿Ÿºï÷—?4{ßóŠ³ý?ÿ÷ûsŸŸÿ¿Ònþý«›�ïðÖMžÌÊsîPîÂ¯‡~ÿý7Çþfýùüö~½÷¿×ÙÛªÝR?µÕ[censored]Ú�ú·ÿ|í6úóÐï}ühÿÇî�8Cþïå]õ÷÷¾®kŸk}ÚÙ÷5P÷æW™¿²YûúŸþþÙÿ½þ]Ï¾ew÷´ÿ3Ý½ûæ÷wYíëºø÷}û«Çè×ËßÿõyªÛñ×åHûÂ¸Ç¾(÷Oœ¡´÷oY‹yoøšÞßMßGÐJ©ÏËëý¨÷H{¤#O×W9í¼÷Â˜—çÿ~ßæ[censored]ú÷ËTújµ9‡Ï/gþqïµ«Ç×ã‰áoœY»ïßÿžÏ—þþùßÏŽÚ‚wwðßï³î½ýíÎý5Zº_9?÷¸¿¾}ÞsÓéWjõg³³¿ÿ·ê7WßjWý¯Zë_oþöïÞ¨ýÕC.É=õîîÿvkô·ïñýÊÏoôãÿ®ÖÓþýwûsGWê
w„ü÷úï[¹uêÖïÞ8YüþÜJËùì[censored]ÊsývŸãöKÿÿíúV»¶¤7Ý¬²kß«nï¿ñ~ŸK_»wàÒÿ²™ä’#ëÿ£öåeÔ;‰ßäÇ¼ï‡½fnçÞ»ïõþ®ŠÎ¾Çl¤}ÿ’¦iúÈLpò/&Ïö[censored]ýÿ¶ÿ×pÿà¯[censored]¿+
ý×ç¢{ÜwÕ¼wÅ¦ÏÌ¶°ïçò×óßªO¼~H·ö÷ó¶Úò[Ô)Eu?ãïó:·ü»>ïýÓ'ûØG§o~ø§�ýâuÝ¯ä�îÿì¿ç’å÷—õî�¢¯ö>Ãµü�{«Ê³þÿ÷ïwÏÕûÎsŽòzý¿úƒþ8ÝãÞ�½{¹÷AnÛûçÞ¿óõ¾þÇÙÇ÷^ç†ã�÷T÷7oò]]šÿ0ìðuØÝyýÆæ”ß�tç~ëó]«ô]Ö_ù_o;;ñ÷ÿòû�½ööýõãÓßöw^4ÿ³½Ò—ùÜ&ïÿ÷{GýîÎCJS/ŽZô·ÿþÿžÿê/6ä¹ßúÏô·Ó¼ÅûþŸëzÁ¹{—îÖå+ÌúsÃo¯=Â¶wé®ýóþ©›ê?§ï§¿7öïùŽù3Oýýe}é·5ÏÿìÌí£é¶¯¬ÎŽ·;Ùè×�;îÿÓgòÏÛ²åýÇ©ýÜÖpoqùßßáõ§yŸ÷?ï±þþß\ý“þ¿îóõ=?¬×MŸ»o}µn6ò9UÉ¿ÛöV¯Û¾K_¿í¹„ÿV’Ö?Ç¯uÿþÿ¹¸ÿ½ý§^?=8Ýð½ëê[á½WÚíª½üÞ]]ì‰?ò²�¹É<ÿÈËÖ¹¹ÏÛ9ó½«÷nïí™ì½‘÷Þ 165, 20165 153:165 PM
Thank you.

I started googling "Tiny RTC" to see if I could turn up anything else, and found another time set sketch (which apparently uses manually plugged in numbers directly to the code):



Code:


/* Downloaded from http://projectsfromtech.blogspot.com/
*Connect SCL, SDA, Vcc, and GND
*Set date in function below.
*Upload and enjoy!
*/


//Arduino 1.0+ Only

#include "Wire.h"

#define DS1307_ADDRESS 0x68
byte zero = 0x00; //workaround for issue #527

void setup(){
  Wire.begin();
  

  
  Serial.begin(9600);
  setDateTime(); //MUST CONFIGURE IN FUNCTION
}

void loop(){
  printDate();
  delay(1000);
}

void setDateTime(){

  byte second =      0; //0-59
  byte minute =      27; //0-59
  byte hour =        9; //0-23
  byte weekDay =     2; //1-7
  byte monthDay =    6; //1-31
  byte month =       10; //1-12
  byte year  =       14; //0-99

  Wire.beginTransmission(DS1307_ADDRESS);
  Wire.write(zero); //stop Oscillator

  Wire.write(decToBcd(second));
  Wire.write(decToBcd(minute));
  Wire.write(decToBcd(hour));
  Wire.write(decToBcd(weekDay));
  Wire.write(decToBcd(monthDay));
  Wire.write(decToBcd(month));
  Wire.write(decToBcd(year));

  Wire.write(zero); //start 

  Wire.endTransmission();

}

byte decToBcd(byte val){
// Convert normal decimal numbers to binary coded decimal
  return ( (val/10*16) + (val%10) );
}

byte bcdToDec(byte val)  {
// Convert binary coded decimal to normal decimal numbers
  return ( (val/16*10) + (val%16) );
}

void printDate(){

  // Reset the register pointer
  Wire.beginTransmission(DS1307_ADDRESS);
  Wire.write(zero);
  Wire.endTransmission();

  Wire.requestFrom(DS1307_ADDRESS, 7);

  int second = bcdToDec(Wire.read());
  int minute = bcdToDec(Wire.read());
  int hour = bcdToDec(Wire.read() & 0b111111); //24 hour time
  int weekDay = bcdToDec(Wire.read()); //0-6 -> sunday - Saturday
  int monthDay = bcdToDec(Wire.read());
  int month = bcdToDec(Wire.read());
  int year = bcdToDec(Wire.read());

  //print the date EG   3/1/11 23:59:59
  Serial.print(month);
  Serial.print("/");
  Serial.print(monthDay);
  Serial.print("/");
  Serial.print(year);
  Serial.print(" ");
  Serial.print(hour);
  Serial.print(":");
  Serial.print(minute);
  Serial.print(":");
  Serial.println(second);

}

But the serial output for that sketch appears to be this:

165/165/165 45:165:165​
...which clearly isn't right, either.

Putting the Sat+ software back on, and, you guessed it, 17:18 comes back up.

Swapping back to your RTC setting sketch, it opens up with showing the current time is "165, 20165 153:165 PM".

Back to Sat+, and once again, I'm staring at 17:18.

*sigh*

Interesting. I just fired up the serial monitor with the Sat+ controller software running, and it shows this:

RTC RTC Error
Time = 17:18:09
SRAM : 6387​
Time for work, I'll have to revisit this tonight. Just for grins, I'll leave the Arduino connected to my laptop and running all day while I'm gone, to see if any alarms trigger and we get anything else on screen.

I do have a second RTC I could wire up as a test. (the one I was going to use for iAqua, but it seems like I probably have time to get another one in, as that project will be a long time in coming to fruition...


----------



## theknight

kman said:


> Hmm. Now I'm starting to wonder if something is wrong with my RTC.
> 
> I uploaded your sketch, and started to get really excited once I figured out the serial monitor was needed, open, so I could enter the time. But here is what I got when I ran it:
> 
> 
> 
> Code:
> 
> 
> Please change to newline ending the settings on the lower right of the Serial Monitor
> Would you like to set the date and time now? Y/N
> Please enter the current year, 00-99. - 14
> Please enter the current month, 1-12. - October
> Please enter the current day of the month, 1-31. - 6
> Please enter the current day of the week, 1-7.
> 1 Sun | 2 Mon | 3 Tues | 4 Weds | 5 Thu | 6 Fri | 7 Sat - Monday
> Please enter the current hour in 24hr format, 0-23. - 9
> Please enter the current minute, 0-59. - 32
> The data has been entered.
> The current date and time is now:  xÕÿIó7÷_[censored]r»íöëü¿:¿ÿï÷ýÄÎ½ãÿ,*Ï\ŸúÌ³wêß´ý;WõùMîöÍ7uï�^ÿá½_¿hÅîVj<_ãáù‡Y¿—Óí·Ýb¾ßvùWßÏÞçkþ›úÙ¶¼º®þ½ýúFÿõküéZµö6%ÐŸn'ß_ß;ÿ^‘¾óÏyÿþÌÏOÿ[h¿ßß;ûùÜï½û¯çµ;[censored]v÷¾î�sw»ù~<µ¦™÷~ÎµÞŸ¼Ÿ›™ïí×ÿ[censored]wûàª$©:¡Ÿµ?ßxÿ|uñï®yß½w¥þ³�<Ÿ¯Úßæ~?ó[censored]÷�µhkÖ_¿·ÿÝ5×ÍŸ½ïÿüý_yÒÊ©§öëËü{×ýûŽ_1¸ÉvÝTí¹Üû¿éÍ¿o[censored]??îGs§~<(ûNÏÜµÿe5ùÏßWÏ6mþß~ûúÏ»Ï_ÿ£ðùož¤wó¾¾hþÞXWý7Å_ôÿÞ]íc¬á-þ™ö\Ënû½ó÷þo¦ê±ù&êÌ¿ÿÿ·õ5ÿûxÛë»KMrù+>»ô÷[u{üW«æ?Ý?¹)¯³µm×³ÝJ_Á—÷ý=,ëî¾[censored]–ÿ¿¥…‘µŸÊu=õ}«^Õ{}ß³ÿ¿Oþ;õÅy×÷[censored]ïçVxz³…_¾üw{”¿®zý=Ü5¾ÿòŸ¾oúÛ´\4—vsxùÿôöý·ë®~÷ýïÝwìÚèÿÍ"ãñ)ëœ[censored]û´Ö‹þýþ}Í}´6ìÕþ½ç5/bÏÿîÿñm§Vÿ–^·»}†7Õ
> 3ïä=î÷ï½Ïw_Ýÿú<û/ýkžÿÿ(þß÷ï}7;¾÷ýNÊÑ�=Ð_~¯ø¾ÿ�‘>þ™üïsékþÉ»çßæ[ã}}ýôÌßüª¾ÓSü¯Wìïý•»wý@Çuýw/ú®µ�iküýn·}íš=Ö}÷g”¯ç]/TþQ
> ôîïýýc�Owÿ}Ã§nu‡¿ý2ñßý÷ÀüÕðÏ/ßr—óÓÁ÷žîÚ9¾ß~_¯ôrßÿO¾Æîÿöø>ûýP¹»vï—Y�Gßåþ¬åÿŠç_û¿oëÜûøÌílúÿ•ß*g´Þ�¿³ú¾óyß.üdé»H~ÐÍîùåÑþÿ¯èmä~öíòwÎ}Ùù;¶·7°íÿ?³ßö[ÙßSü{û©:³²|î[ÞwÙÝ].ü³÷ÝŸx¬öÍÿOïë
> ¾œwge_Þ["to²çÑ_þóáõ{þÕú»Í¾ü‰Ïÿþ�¢/ÎÞM¯'W“0ýR¿•«=�mþÿßßçôñýöõøòUûûŽ^°‹×înn¯ÞSÉ¾_½è3ß5ÇÿþûÒèïïïÿœ»ú‰ùOäñ—¶;™–Œ�¾¤>û§ßï¿½Ž)ý;Ýwm½Œƒºo[4Îéý÷ïk×Íï~swóeÀÌ�«ýñßþ�ëöÿïT®×G¯?_ÖŸ¦zÝ«ùÔ‡ÿ»ÿúïk½çïÿwüMñ0©ÜuÁ��å~oßCÄëoô»¾k¶²Ûÿÿ•·Yö;í?—¬§Ým¶ûÉ×²õï[G•§ú¤éÍŸ××÷ÌûÞ=»w¿ö›ÿêÝwï÷}�ÿï�æ^WíÝú|¾þwxÝãŸ¿›÷?ÓÞ¶mlë=ÎÿG^(©ÿÒÛîÿÜõ»úû±ý~Ÿø[censored]:óÍú¿Ï–á÷þÿ�W}4éW÷ÚiÇþ¾‹ÔÒ÷CœÝºe¢öýýçŽñ×]óï]jg«}áKÁÞöÿ¿ýçÜ{|ÿÉÚï§žÏ§o÷Ý}Í]ýß~«Û½/÷Ç“ßìæÿï—ûgýÿþ÷¬QÕU|ïo3¿ÐýTõ–¼¦¯èÿ…öþôýw¾kXºå÷oÊ³¿i,F}ÿw~é§Ì[censored]¯ôyóMÿü®¹ƒó×{çî¿ÿ®ûýÛ¬¿½ï6Ðx/ÿý/ÕÜ_[ÓwúßÎsÓûs¸ÿüó]Fó_ç¢ÿïÿsTÿJßûþõ=®õ?ÔÑÐÿ5§ÿSÿú¿¼MGÿ;¿7ÿÏuîèvûßÿ^/ÿí¿ëïo½šnÿÎ_uýfï‘[½çOûÿÜôíOÂÿš©wŒåsÓß¿ÿ·¶»m{Ógú_äkÿ_ÒØ]Þ7û/¿T~�ý‰¹þïkÍáûSå>{ÿsßóºú[qÛ^oÆãž7^_þ{§„âÛ³ÿï¿Q®ÿ×Æþ]oq+i_.ÀôÆlíý\Ö·ûy}¾�ëEÿßßú®M³Iþëÿwýì·¥|îo÷Þ~7ózýúç/ñ¿íVxU?ï:Ë®¿ù¶wÞÿ¯éë~Tû§èï¿Ÿºï÷—?4{ßóŠ³ý?ÿ÷ûsŸŸÿ¿Ònþý«›�ïðÖMžÌÊsîPîÂ¯‡~ÿý7Çþfýùüö~½÷¿×ÙÛªÝR?µÕ[censored]Ú�ú·ÿ|í6úóÐï}ühÿÇî�8Cþïå]õ÷÷¾®kŸk}ÚÙ÷5P÷æW™¿²YûúŸþþÙÿ½þ]Ï¾ew÷´ÿ3Ý½ûæ÷wYíëºø÷}û«Çè×ËßÿõyªÛñ×åHûÂ¸Ç¾(÷Oœ¡´÷oY‹yoøšÞßMßGÐJ©ÏËëý¨÷H{¤#O×W9í¼÷Â˜—çÿ~ßæ[censored]ú÷ËTújµ9‡Ï/gþqïµ«Ç×ã‰áoœY»ïßÿžÏ—þþùßÏŽÚ‚wwðßï³î½ýíÎý5Zº_9?÷¸¿¾}ÞsÓéWjõg³³¿ÿ·ê7WßjWý¯Zë_oþöïÞ¨ýÕC.É=õîîÿvkô·ïñýÊÏoôãÿ®ÖÓþýwûsGWê
> w„ü÷úï[¹uêÖïÞ8YüþÜJËùì[censored]ÊsývŸãöKÿÿíúV»¶¤7Ý¬²kß«nï¿ñ~ŸK_»wàÒÿ²™ä’#ëÿ£öåeÔ;‰ßäÇ¼ï‡½fnçÞ»ïõþ®ŠÎ¾Çl¤}ÿ’¦iúÈLpò/&Ïö[censored]ýÿ¶ÿ×pÿà¯[censored]¿+
> ý×ç¢{ÜwÕ¼wÅ¦ÏÌ¶°ïçò×óßªO¼~H·ö÷ó¶Úò[Ô)Eu?ãïó:·ü»>ïýÓ'ûØG§o~ø§�ýâuÝ¯ä�îÿì¿ç’å÷—õî�¢¯ö>Ãµü�{«Ê³þÿ÷ïwÏÕûÎsŽòzý¿úƒþ8ÝãÞ�½{¹÷AnÛûçÞ¿óõ¾þÇÙÇ÷^ç†ã�÷T÷7oò]]šÿ0ìðuØÝyýÆæ”ß�tç~ëó]«ô]Ö_ù_o;;ñ÷ÿòû�½ööýõãÓßöw^4ÿ³½Ò—ùÜ&ïÿ÷{GýîÎCJS/ŽZô·ÿþÿžÿê/6ä¹ßúÏô·Ó¼ÅûþŸëzÁ¹{—îÖå+ÌúsÃo¯=Â¶wé®ýóþ©›ê?§ï§¿7öïùŽù3Oýýe}é·5ÏÿìÌí£é¶¯¬ÎŽ·;Ùè×�;îÿÓgòÏÛ²åýÇ©ýÜÖpoqùßßáõ§yŸ÷?ï±þþß\ý“þ¿îóõ=?¬×MŸ»o}µn6ò9UÉ¿ÛöV¯Û¾K_¿í¹„ÿV’Ö?Ç¯uÿþÿ¹¸ÿ½ý§^?=8Ýð½ëê[á½WÚíª½üÞ]]ì‰?ò²�¹É<ÿÈËÖ¹¹ÏÛ9ó½«÷nïí™ì½‘÷Þ 165, 20165 153:165 PM
> Thank you.
> 
> I started googling "Tiny RTC" to see if I could turn up anything else, and found another time set sketch (which apparently uses manually plugged in numbers directly to the code):
> 
> 
> 
> Code:
> 
> 
> /* Downloaded from http://projectsfromtech.blogspot.com/
> *Connect SCL, SDA, Vcc, and GND
> *Set date in function below.
> *Upload and enjoy!
> */
> 
> 
> //Arduino 1.0+ Only
> 
> #include "Wire.h"
> 
> #define DS1307_ADDRESS 0x68
> byte zero = 0x00; //workaround for issue #527
> 
> void setup(){
> Wire.begin();
> 
> 
> 
> Serial.begin(9600);
> setDateTime(); //MUST CONFIGURE IN FUNCTION
> }
> 
> void loop(){
> printDate();
> delay(1000);
> }
> 
> void setDateTime(){
> 
> byte second =      0; //0-59
> byte minute =      27; //0-59
> byte hour =        9; //0-23
> byte weekDay =     2; //1-7
> byte monthDay =    6; //1-31
> byte month =       10; //1-12
> byte year  =       14; //0-99
> 
> Wire.beginTransmission(DS1307_ADDRESS);
> Wire.write(zero); //stop Oscillator
> 
> Wire.write(decToBcd(second));
> Wire.write(decToBcd(minute));
> Wire.write(decToBcd(hour));
> Wire.write(decToBcd(weekDay));
> Wire.write(decToBcd(monthDay));
> Wire.write(decToBcd(month));
> Wire.write(decToBcd(year));
> 
> Wire.write(zero); //start
> 
> Wire.endTransmission();
> 
> }
> 
> byte decToBcd(byte val){
> // Convert normal decimal numbers to binary coded decimal
> return ( (val/10*16) + (val%10) );
> }
> 
> byte bcdToDec(byte val)  {
> // Convert binary coded decimal to normal decimal numbers
> return ( (val/16*10) + (val%16) );
> }
> 
> void printDate(){
> 
> // Reset the register pointer
> Wire.beginTransmission(DS1307_ADDRESS);
> Wire.write(zero);
> Wire.endTransmission();
> 
> Wire.requestFrom(DS1307_ADDRESS, 7);
> 
> int second = bcdToDec(Wire.read());
> int minute = bcdToDec(Wire.read());
> int hour = bcdToDec(Wire.read() & 0b111111); //24 hour time
> int weekDay = bcdToDec(Wire.read()); //0-6 -> sunday - Saturday
> int monthDay = bcdToDec(Wire.read());
> int month = bcdToDec(Wire.read());
> int year = bcdToDec(Wire.read());
> 
> //print the date EG   3/1/11 23:59:59
> Serial.print(month);
> Serial.print("/");
> Serial.print(monthDay);
> Serial.print("/");
> Serial.print(year);
> Serial.print(" ");
> Serial.print(hour);
> Serial.print(":");
> Serial.print(minute);
> Serial.print(":");
> Serial.println(second);
> 
> }
> 
> But the serial output for that sketch appears to be this:
> 
> 165/165/165 45:165:165​
> ...which clearly isn't right, either.
> 
> Putting the Sat+ software back on, and, you guessed it, 17:18 comes back up.
> 
> Swapping back to your RTC setting sketch, it opens up with showing the current time is "165, 20165 153:165 PM".
> 
> Back to Sat+, and once again, I'm staring at 17:18.
> 
> *sigh*
> 
> Interesting. I just fired up the serial monitor with the Sat+ controller software running, and it shows this:
> 
> RTC RTC Error
> Time = 17:18:09
> SRAM : 6387​
> Time for work, I'll have to revisit this tonight. Just for grins, I'll leave the Arduino connected to my laptop and running all day while I'm gone, to see if any alarms trigger and we get anything else on screen.
> 
> I do have a second RTC I could wire up as a test. (the one I was going to use for iAqua, but it seems like I probably have time to get another one in, as that project will be a long time in coming to fruition...


Just a thought, seeing you're getting a lot of garbage on serial monitor. When you open the serial monitor make sure in the lower right corner it is set for 9600 and Newline, if this is set wrong the RTC and serial monitor won't be in sync and you will get a bunch of nonsense characters.


----------



## kman

theknight said:


> Just a thought, seeing you're getting a lot of garbage on serial monitor. When you open the serial monitor make sure in the lower right corner it is set for 9600 and Newline, if this is set wrong the RTC and serial monitor won't be in sync and you will get a bunch of nonsense characters.


Yes, it is. The sketch you posted above gives a warning about that, and you can't fill in the responses until it's set correctly. (the next question won't show up, as I learned)

Set to 9600 and Newline, that's the garbage output I got from the script. Twice.


----------



## kman

Ugh.

I wired up my other RTC. SAME ERROR. And this is a different make (SainSmart, vs. some no-name Tiny RTC)

So I tried copying everything over to my PC laptop, on the off chance there was a issue related to Macs. (Copied and pasted directly from the code posted in this thread, not copied over the network.) Same deal. 

Now I'm at a loss.  I have two Arduinos, as well, so I guess I'll try the other one, next, but this is pretty strange considering everything else seems fine.

EDIT: Tried Arduino #2. Same exact results, across the board. Pretty sure it's not the Arduino.


----------



## kman

Breakthrough!

But not necessarily in a good way. :/

I found some code on a Russian Arduino site that tests to see if the RTC is working at all:

http://arduino.ru/forum/apparatnye-voprosy/podklyuchenie-tiny-rtc-i2c (translated version here, but it doesn't really help)



Code:


#include <Wire.h>
#include "RTClib.h"
RTC_DS1307 RTC;
  
void setup () {
  //Initialize the serial port, wire library and RTC module
    Serial.begin(9600);
    Wire.begin();
    RTC.begin();
  //We check if the RTC module is working, if not we sent Error
  if (! RTC.isrunning()) {
    Serial.println("RTC is NOT running!");
    //If we remove the comment from the following line, we will set up the module time and date with the computer one
    RTC.adjust(DateTime(__DATE__, __TIME__));
  }
}
  
void loop () {
    DateTime now = RTC.now();
    //We print the day
    Serial.print(now.day(), DEC);
    Serial.print('/');
    //We print the month
    Serial.print(now.month(), DEC);
    Serial.print('/');
    //We print the year
    Serial.print(now.year(), DEC);
    Serial.print(' ');
    //We print the hour
    Serial.print(now.hour(), DEC);
    Serial.print(':');
    //We print the minutes
    Serial.print(now.minute(), DEC);
    Serial.print(':');
    //We print the seconds
    Serial.print(now.second(), DEC);
    Serial.println();
    //We check the time and sent through the serial port every 3s
    delay(3000);
}

RTC5/165/2165 165:165:85
RTC is NOT running!
165/165/2165 165:165:85
165/165/2165 165:165:85

I did some Googling, and it seems the 165 is a dead giveaway that the RTC is not working. (see here for one indication)

It's good thing I ordered another RTC today! I guess there's nothing for me to do until it arrives.  On the bright side, perhaps that's been the problem all along, and once I get a functional RTC here, I'll be up and running, finally! Fingers crossed...


----------



## kman

Ok. RTC #3 is here, and I have it hooked up.

Same issue. So the odds seems astronomical that they're ALL bad. The problem has to be me, and probably how I have things hooked up.

Let me trace out the connections. Perhaps someone can help me find where I've messed up?



5V on the Arduino goes to breadboard. On the same row, it goes to VCC on the RTC and VCC on the LCD.

GND goes to GND on the LCD. (I used the GND on the opposite side of the board for the other GND connections)

Continuing down the left:


A4 on the Arduino goes to SDA on the RTC.
A5 on the Arduino goes to SCL on the RTC.

Continuing to the other side: 


SCL21 on the Arduino goes to SCL on the LCD.
SCL20 on the Arduino goes to SDA on the LCD.

(that covers all 4 wires on the LCD)

Continuing up the other side of the Arduino:


PWM3 goes to the breadboard, connecting to the resistor. The resistor connects (via breadboard) to the IR LED. 
The other pin of the IR LED goes to GND on the Arduino (the one on the top right). Sharing that same breadboard row (to the ground) is the GND on the RTC.
That's it. 8 wires total plug into the Arduino. (at least until I add temp! LOL) I'll see if I can draw up some sort of connection graphic over and Arduino photo, just to make it more clear, but I can't see where I've gone wrong. Can anyone else assist? Sorry to be such a pain. I try to be more self-sufficient, normally. 

EDIT: Here is a crude graphic layout of the connections I've used... if it's correct, perhaps someone will find it useful.  If it's not correct... I'll be thrilled to learn where I went wrong!


----------



## Guy.hall

Maybe a daft question, but have you jumpered the 2 GND rails?


----------



## kman

Guy.hall said:


> Maybe a daft question, but have you jumpered the 2 GND rails?


Maybe not a daft question... what do you mean, "jumpered the GND rails"? It's not jumpered internally? (via the board itself)

I'm going to feel really stupid if the jacks labeled GND aren't all "live". :/

That said, I just moved the jumper from the top of the board's GND to the open (2nd) GND on the bottom of the board (power section), right next to the LCD's GND slot, and nothing changed (operationally). The LCD works (test script functions perfectly) so I think both of those, at least, are both live.


----------



## theknight

Try using the same SDA and SDL connections for the RTC that you are using for the LCD (20 & 21)


----------



## AnotherHobby

theknight said:


> Try using the same SDA and SDL connections for the RTC that you are using for the LCD (20 & 21)


This is the first thing I thought when I saw his diagram.


----------



## theknight

AnotherHobby said:


> This is the first thing I thought when I saw his diagram.


Yes, I think the problems stems from using a Mega instead of the Nano where the SDA & SCL are pins A4 & A5. This also makes me wonder if using the pin 3 for the IR will cause a conflict with the LCD. On your design for the Mega, pin 9 is used is the IR pin.


----------



## AnotherHobby

theknight said:


> Yes, I think the problems stems from using a Mega instead of the Nano where the SDA & SCL are pins A4 & A5. This also makes me wonder if using the pin 3 for the IR will cause a conflict with the LCD. On your design for the Mega, pin 9 is used is the IR pin.


Yeah, that wasn't specifically my design, that's just what the IR remote library defaults to. I think you can change it, but I'm not sure.


----------



## kman

theknight said:


> Yes, I think the problems stems from using a Mega instead of the Nano where the SDA & SCL are pins A4 & A5. This also makes me wonder if using the pin 3 for the IR will cause a conflict with the LCD. On your design for the Mega, pin 9 is used is the IR pin.


YESYESYESYESYESYESYESYESYESYES!!!!!!!!!!!!!!!!!!!!!!!!!!

Moving the RTC to share jumpers A20-A21 fixed it!

I guess there IS a difference in using a Mega vs. a Nano.

The RTC is working perfectly now.

I'm going to leave everything hooked up for a day to see if the mode trips properly and lists the light mode, but I think I'm set now. 

So relieved. I actually had that thought once or twice, but everything was pretty clearly set to use A4 and A5 so I thought surely it was just me. I didn't really consider the Mega vs. Nano differences. 

Thank you SOOOO much!!!!!!!

I guess we'll find out (next) if the IR has any issues. Once this is working and triggering properly, I'll start tweaking the code to align with my own time settings, and give it a try for real.

Of course, everything is complicated by the fact that my second Sat+ just went bad. I've had problems with that light since the start; I probably should have replaced it sooner, but starting it manually seemed to have solved the problems at the time. But now it won't respond to the OEM remote at all, so we'll see how Current deals with things...


----------



## theknight

kman said:


> YESYESYESYESYESYESYESYESYESYES!!!!!!!!!!!!!!!!!!!!!!!!!!
> 
> Moving the RTC to share jumpers A20-A21 fixed it!
> 
> I guess there IS a difference in using a Mega vs. a Nano.
> 
> The RTC is working perfectly now.
> 
> I'm going to leave everything hooked up for a day to see if the mode trips properly and lists the light mode, but I think I'm set now.
> 
> So relieved. I actually had that thought once or twice, but everything was pretty clearly set to use A4 and A5 so I thought surely it was just me. I didn't really consider the Mega vs. Nano differences.
> 
> Thank you SOOOO much!!!!!!!
> 
> I guess we'll find out (next) if the IR has any issues. Once this is working and triggering properly, I'll start tweaking the code to align with my own time settings, and give it a try for real.
> 
> Of course, everything is complicated by the fact that my second Sat+ just went bad. I've had problems with that light since the start; I probably should have replaced it sooner, but starting it manually seemed to have solved the problems at the time. But now it won't respond to the OEM remote at all, so we'll see how Current deals with things...


I think my suspicions are correct on the IR, if it doesn't work move it from pin 3 to 9. From what AH is saying the IR library defaults to 9 on the Mega. Some people use their cell phone camera to pick up the IR image, I was able to see it on my IR emitter.


----------



## kman

theknight said:


> I think my suspicions are correct on the IR, if it doesn't work move it from pin 3 to 9. From what AH is saying the IR library defaults to 9 on the Mega. Some people use their cell phone camera to pick up the IR image, I was able to see it on my IR emitter.


Now that the thing will actually run, I should be able to test that. Until I got the RTC working, there was no functional clock so the code wouldn't trigger any IR events! I'll bear this in mind, though. And I'll put together an updated (corrected) layout graphic for other people wanting to give this a go. 

If your cell camera won't show IR activity, btw, often a regular (digital) camera will. (on the screen)


----------



## kman

Ok, the good news is I came home to a properly-running Arduino, with the LCD showing the running light mode. 

The bad news is, as you guessed, the IR doesn't work on pin 3. Or pin 9.

I went to the website where the IR library came from, and found some useful info in the comments, but I'm not sure what to do with the info. Here's the scoop:



> *Harrison said...*
> 
> This is great!
> 
> Could you please tell me how I can change the output pin? (I have an arduino clone with pin 13 as LED and IR-Tx)
> 
> *Ken Shirriff said...*
> 
> Harrison: you have to use pin 3 as the IR LED output because that's the PWM pin. (You could use a different PWM pin, but it would take substantial modification to the code.)
> 
> *australopitecus said...*
> 
> Hi.
> I really like this project. Thank you very much. It helps me a lot.
> But I have one problem. Everything works, except sending the IR pulse.Neither in the example IRrecord nor with IRsendDemo.
> There´s no output at the PWM pin 3. I´ve tested it with a camera, a LED instead of the IR-diode and the oscilloscope. There´s definitely no signal.
> First i mentioned there´s something wrong with the sourcecode. But it´s programmed really sophisticated and i couldn´t find a mistake, though i consider myself a quite acceptable programmer.Sorry for self-praise.
> But Giorgio O wrote, the diode is sending. So there´s another problem.
> Is there anything I could have forgot or has anybody else had a similar problem? I´m trying for 4 days....
> I use the Arduino mega board with the 0017 software.
> Excuse my mistakes in English. I´m not a native speaker.
> Thanks for help..
> 
> *Ken Shirriff said...*
> 
> australopitecus: My code won't work with an Arduino mega as it has a different processor and the pins are all different. (Sorry I didn't mention that in my original article.) You could see if there's anything on PWM pin 9; I believe that's where the OC2B output connects on the mega. Probably you'll need to study the atmega 1280 datasheet and see how the PWM flags are different for the mega's processor, and change the code appropriately. Unfortunately I don't have a mega, so I can't try this myself.
> 
> *australopitecus said...*
> 
> Hi.
> That was the problem, yes.
> Today I found in the o-file "pins_arduino.c.o" the path to the "cores" folder and a file named "pins_aruino.c".
> According to this, in the digital_pin_to_timer, which is a part of the program memory, I think, Timer2B is related to PWM9. I tried it and it worked. I had only to change the output of the pinMode.
> I don´t know if that´s exactly the same you mentioned, but I have a signal now. So I can receive and send.


----------



## AnotherHobby

kman said:


> Ok, the good news is I came home to a properly-running Arduino, with the LCD showing the running light mode.
> 
> The bad news is, as you guessed, the IR doesn't work on pin 3. Or pin 9.
> 
> I went to the website where the IR library came from, and found some useful info in the comments, but I'm not sure what to do with the info. Here's the scoop:


Look at the IRremoteInt.h file in the IRremote library folder. Open it an look for a group of lines like this, and edit it to match:




Code:


// Arduino Mega
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
  //#define IR_USE_TIMER1   // tx = pin 11
  #define IR_USE_TIMER2     // tx = pin 9
  //#define IR_USE_TIMER3   // tx = pin 5
  //#define IR_USE_TIMER4   // tx = pin 6
  //#define IR_USE_TIMER5   // tx = pin 46

I believe I had to do this on my iAqua project for the IR to work on the Mega.


----------



## kman

AnotherHobby said:


> Look at the IRremoteInt.h file in the IRremote library folder. Open it an look for a group of lines like this, and edit it to match:
> 
> 
> 
> Code:
> 
> 
> // Arduino Mega
> #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
> //#define IR_USE_TIMER1   // tx = pin 11
> #define IR_USE_TIMER2     // tx = pin 9
> //#define IR_USE_TIMER3   // tx = pin 5
> //#define IR_USE_TIMER4   // tx = pin 6
> //#define IR_USE_TIMER5   // tx = pin 46
> 
> I believe I had to do this on my iAqua project for the IR to work on the Mega.


That's how it looks in there already:



Code:


// Arduino Mega
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
  //#define IR_USE_TIMER1   // tx = pin 11
  #define IR_USE_TIMER2     // tx = pin 9
  //#define IR_USE_TIMER3   // tx = pin 5
  //#define IR_USE_TIMER4   // tx = pin 6
  //#define IR_USE_TIMER5   // tx = pin 46

So since the line referencing pin 9 is already un-commented, shouldn't it already be set to pin 9?

Is IRremoteInt.h called in by IRremote.h? I don't see it among the declared libraries at the top of the Sat+ controller code.

Hey, I had a thought as to testing: If I was to swap in a plain red LED (no idea of the specs, it's from an old bag of LEDs I've had forever) would I be able to visually see the LED fire without having to look through a camera at it?


----------



## AnotherHobby

kman said:


> Hey, I had a thought as to testing: If I was to swap in a plain red LED (no idea of the specs, it's from an old bag of LEDs I've had forever) would I be able to visually see the LED fire without having to look through a camera at it?


Absolutely. This is how I tested mine.


----------



## kman

Still no luck on the IR emitter.

As soon as I nail down the changes needed for the Mega to output on pin 9 instead of the pin 3 that the smaller Arduinos use, I should be in business. AnotherHobby, you use this same library for iAqua, right? Does it detect that it's a Mega and automatically use pin 9, or did you do something else to switch it from 3 to 9?

I updated the wiring diagram with the corrected Mega wiring. Also fixed the flipped LED.


----------



## kman

Hey, I was just going over the thread... I'm currently running a lightly modified version of 3.6, but I see a number of posts with higher version numbers. Should I be using the 4.x version (or later?) as a base, or is the version on the front page the best, most stable version?


----------



## AnotherHobby

In looking at my IRremote library files, they all have modification dates from 2013, so I never edited them for the Mega. I'd double check your wiring. In your diagram you have the resistor between the IR LED and the Mega. On my project I put it between the IR LED and ground. I don't know if I did it right or wrong (I'm not an electronics expert by any stretch), but it works. I just noticed that's different on mine. Also, double check do make sure the LED is not wired backwards (+/-). I've done that. Try it both ways.


----------



## kman

AnotherHobby said:


> In looking at my IRremote library files, they all have modification dates from 2013, so I never edited them for the Mega. I'd double check your wiring. In your diagram you have the resistor between the IR LED and the Mega. On my project I put it between the IR LED and ground. I don't know if I did it right or wrong (I'm not an electronics expert by any stretch), but it works. I just noticed that's different on mine. Also, double check do make sure the LED is not wired backwards (+/-). I've done that. Try it both ways.


Hmm. I definitely tried the LED both ways (I've done that before, too!).

I have it wired up the way Indychus' post 14 indicates:

"Connect the ground to the - side of the emitter, then the + side to the resistor, then the resistor to channel 3 on the Arduino" (this was moved to pin 9, for the Mega)

I did just flip it around, just now, to test, but I didn't notice any difference. That said, since I have yet to see this LED fire at all, ever, perhaps my camera isn't picking up the blinks? (that other LED didn't work either... it will light up dimly if I apply 5V directly from the power source, no resistor, but that's it)

Btw, I've been using the IRsend demo sketch the IRremote library to test:



Code:


#include <IRremote.h>
IRsend irsend;

void setup()
{
  Serial.begin(9600);
}

void loop() {
  if (Serial.read() != -1) {
    for (int i = 0; i < 3; i++) {
      irsend.sendSony(0xa90, 12); // Sony TV power code
      delay(100);
    }
  }
}

I hit upload and watch (immediately) to see if the LED fires (using a camera screen to see, of course)


----------



## kman

tl;dr: It works!

I got tired of beating my head against the wall, so I picked up a cheap $12 knock-off Uno on Amazon. Still nothing. I switched to Dahammer's 4.1 code. Still nothing. Hmm, maybe I have a bad IR LED? So I picked up a new one.

But suddenly, I'm not 100% sure it wasn't working before.  I swapped in a new LED, and STILL didn't see anything on the screen.

Turns out, I'm pretty sure the iPhone camera simply can't see the IR this thing is putting out. Because I moved everything over to the lights and tried it anyway. And it worked! (The serial monitor test display in Dahammer's code helped a lot with this.)

I hooked it all up and it should shut the lights down on schedule shortly. FINALLY! 

I'm both curious to see now, if the Mega was working in Pin 9 after all. And of course, now I have parts to build about 3 of these things. *sigh* Assuming lights turn off as scheduled, and then turn back on tomorrow on time, it'll finally be time to put this thing into some sort of enclosure and call it a day...

Once that's all done, I'll see if I can whip up an easy-to-follow, comprehensive how-to for electronics n00bs (kinda like myself) to follow.


----------



## kman

Controller has been working perfectly for a couple of days now. It was a little tricky working out the best way to place the IR LED and the receivers, but otherwise, no problems. 

I took a cue from Indychus and used an old router to house the project, for now. I'd like to figure out a better project box at some point, but it works for now.









_(no mode listed since I had just re-plugged everything back in to get it in the box)_

My big project last night was creating a permanently-wired shield from a blank Perf board and wire, so I can get my breadbox back and consolidate everything better inside the box. This was my first time attempting anything remotely like this, and I had NO idea what I was doing... I was totally winging it. But it worked perfectly! My soldering was atrocious, and I'm sure my wiring layout (which I was making up on the fly) would give an EE fits and convulsions, but hey, it works, and I learned a lot in the process. Pics to come, but it ain't pretty.

Next up I may tinker with adding the temp probe to take advantage of the extra space on my 20x4 LCD. Hmm, not to mention the dosing pumps I have sitting in a bag... Also, I might play with the entirely new codebase that drftndakota posted up in post 516, or Curt_Planted's new code, posted in post 467, since the hardware side is finally done.

I'd like to figure out if there is a way to dim the backlight through a command send through the I2C controller, because this sucker is super bright.

I did a quick test last night, and I think I finally have this all working correctly on a Mega, as well as the Uno. Now that I have my breadboard back I'll experiment more, since I have a couple of Megas laying around but only the one Uno. I need to pick up another LCD and I should have everything I need to make another controller just to play with for pure experimentation. If only I had a spare light for testing! LOL The biggest issue is going to be pulling the IR receivers out of the box to make sure everything is working as it should, when I want to test something.

And finally, I'm nearly done with a full How-To write-up, that should help someone with little experience get up and running with a minimum of fuss. Anyone have any good saved YouTube links that shows basic soldering to someone who has never done it? I don't really want to write up a full basic soldering tutorial, so currently my write-up goes in assuming a base level of simple soldering ability. You don't need elite skills that will get you a subsistence job in a mainland China manufacturing district, just the ability to stick two wires together with solder, and put a small blob on a circuitboard without burning the house down.


----------



## AnotherHobby

kman said:


> My big project last night was creating a permanently-wired shield from a blank Perf board and wire, so I can get my breadbox back and consolidate everything better inside the box. This was my first time attempting anything remotely like this, and I had NO idea what I was doing... I was totally winging it. But it worked perfectly! My soldering was atrocious, and I'm sure my wiring layout (which I was making up on the fly) would give an EE fits and convulsions, but hey, it works, and I learned a lot in the process. Pics to come, but it ain't pretty.


Awesome! If it works, who cares if it ain't pretty?! Well done! The best way to learn is to just dive in and do it!



> I'd like to figure out if there is a way to dim the backlight through a command send through the I2C controller, because this sucker is super bright.


I'm just using a PWM pin for dimming. The screen is low power enough that I didn't use a transistor or anything — just grab an open PWM pin and feed it straight to the backlight power. Then just do an analog write from 0-254 to the pin to adjust brightness. I'm also using a cheap ambient light sensor, and then autodimming the screen based on ambient light. It's actually really easy, and the screen self-adjusts nicely.


----------



## kman

AnotherHobby said:


> Awesome! If it works, who cares if it ain't pretty?! Well done! The best way to learn is to just dive in and do it!
> 
> 
> 
> I'm just using a PWM pin for dimming. The screen is low power enough that I didn't use a transistor or anything — just grab an open PWM pin and feed it straight to the backlight power. Then just do an analog write from 0-254 to the pin to adjust brightness. I'm also using a cheap ambient light sensor, and then autodimming the screen based on ambient light. It's actually really easy, and the screen self-adjusts nicely.




Thx, but since my LCD is using an I2C backpack, I don't have direct access to the backlight power pin on the LCD. I've read there is a way to control it with a simple command through the I2C, I just haven't had a chance to track down the details on how.


----------



## Guy.hall

Doesn't the backpack have a jumper on it? Mine has a removable jumper that provides the power, you can disconnect that and pwm straight to the pin.


----------



## kman

kman said:


> My big project last night was creating a permanently-wired shield from a blank Perf board and wire, so I can get my breadbox back and consolidate everything better inside the box. This was my first time attempting anything remotely like this, and I had NO idea what I was doing... I was totally winging it. But it worked perfectly! My soldering was atrocious, and I'm sure my wiring layout (which I was making up on the fly) would give an EE fits and convulsions, but hey, it works, and I learned a lot in the process. Pics to come, but it ain't pretty.


Ok, so here are the photos, along with some explanation that will make the electronics pros bored stiff, but might be handy to the newbies. Essentially, of course, to build the basic controller you usually go from THIS wiring diagram (two diagrams follow, one for a Mega, another slightly different for the Uno, but the idea is the same for each, you just use slightly different pins):










Here's a schematic for the same controller, using a Mega instead of the Uno:









... to something like THIS breadboard setup, just to make sure everything is working as expected (LCD is just out of frame):










That's great, once it's all working, happy happy. But, everything is also just a smidge loosey-goosey, awkward, and wow, that's a lot of crap to stuff into a project box!

The way to boil all those wires down, since the hardware side is all set, is to make a custom "shield" ... a little sub-board that bakes in all the wiring craziness into a neat tidy package that mounts directly onto the Arduino for a nice tight package. Not all of us have O2surplus' masterful ability to design and make professional custom shields, but you can make your own with just a small, cheap piece of "Perf board" (perforated PCB with solder pads surrounding all the holes) and some wire. A few little extras like standoffs and mounting points for LEDs and such help, too.

My little perf board (from DealExtreme) was like this:










Here is the end result, with no wires except for the ones connecting to the LCD, so I can disconnect as needed and have some freedom for placement:









_(Arduino on top, custom shield, and LCD, with I2C backpack/subboard)_

Next, you can see the underside of the board, with the connections for the RTC, some headers to connect the LCD, and a little screw mount where the LCD slots in, so I have some flexibility. I told you the soldering was sloppy, but there are no short circuits!










And here, below, is the other side, where the magic happens. The RTC (on the right) is securely attached via some bent over headers, then some 90 degree headers in the middle make it easy to connect (and disconnect, when needed for service) the LCD, and on the left are the screw terminals I used to seat the IR LED. Key are the two little groups of standoff pins on the right side of the board, which slot directly into the A4-A5, and Power (+/-) holes on the Arduino, plus the single header on the right part, which pokes directly into the PWM-3 pin to control the LED. The resistor is also mounted directly to the board:










Here, below, you can see from the side, where the header pins are slotted directly into the Arduino, making a nice flat package:










Here's the underside of the assembled package:










Finally, we come to assembly. As I mentioned before, I borrowed Indychus' idea of gutting an old router, and cut a hole in the top panel for the LCD to show through. I routed the IR receivers from my two Sat+ lights into a handy hole, and mounted them (with a highly technical rubberband) to a little piece of metal bent into an L shape. This gives me a great shape to position them right in front from the IR LED once everything is in place. (the sharp-eyed will notice I simply grabbed an old PCI slot cover I had from an old computer system and bent it)










Here is the placed Arduino and LCDs. Eventually, I'll screw the LCD into the top, if I don't move the whole thing to a better enclosure at some point, but this works for the time being.










With the upper and lower panels in place, and the IR receivers positioned, all I need to do is pop the front panel back on, and I'm ready to run!










And last, we have the (semi-) final product. Easy-peasy!


----------



## kman

Guy.hall said:


> Doesn't the backpack have a jumper on it? Mine has a removable jumper that provides the power, you can disconnect that and pwm straight to the pin.


Yes, the backpack has a jumper that says "LED" right next to a little LED.

But even if I knew how to wire it, I think I'd rather just use a simple command to set brightness level via the I2C with a single line of code (I assume) than have another wire snaking around.


----------



## alpha1172

Very nice!! im going to have to steal a few ideas from you. 

Did you remove the IR recievers from the light or do they come like that?
i havnt been home to see the light i bought yet


----------



## kman

alpha1172 said:


> Very nice!! im going to have to steal a few ideas from you.
> 
> Did you remove the IR recievers from the light or do they come like that?
> i havnt been home to see the light i bought yet


The lights come with IR receivers on a cord like that so you can place them wherever.


----------



## kman

Closer and closer to final...

Some more toys came in over the past few days. I picked up a 16x2 LCD so I'd have a second one to play with. And as much as the old router enclosure works, I really wanted something that I could make nice and stable, so I decided to get a new enclosure box that I could really mount the controller in, for long term stability. Also, after talking to AnotherHobby, he made me aware of a huge advantage to a clear enclosure box: Inside an opaque box, the controller works great, but I can't use the actual remote control if I want to make any changes other than the programmed events!

I bought this: Hammond 1591STCL Translucent Polycarbonate Project Box -- Inches (4.3" x 1.6" x 3.3") mm (110mm x 40mm x 85mm)










So today, the new enclosure box arrived, and I was pleased to see it works nicely with the Uno-based controller. It was a bit small for the 20x4 LCD so I switched over to the 16x2, which fortunately works exactly the same once the two numbers are changed in the one section of code (well done, guys!). So tonight I moved the controller into the new box: 

I carefully drilled holes for the USB and DC input and filed them square. I didn't have screws small enough to fit through the PCB holes of the Uno (apparently the smallest Home Depot carries, #6, is a little too big, darn it!), so I just put one screw right on the front edge with a nut to snug it up against the back, which holds it nice and firm so I can easily insert USB or power cords, and it doesn't move the board a bit. (just like any commercial device)

Then of course I found a couple of screws at the bottom of my toolbox that would have worked fine. Fortunately, as I needed them for the LCD, so I mounted it into the lid. (note the larger screws would have worked in the 20x4 LCD which has larger mounting holes)

I need to figure out solid method to fix the IR receivers from the lights in one place so the box can hit them reliably. Meanwhile, I just rubberbanded the same L-shaped metal to the new enclosure. The IR LED fires right through the case to hit the receivers, and works great! And now I can use the remote anytime I want to mess with things, off-program, without having to touch anything.

Easy access to ports!









Everything fits nicely









One machine screw anchors the Arduino in place, pressed up against the back panel









Top view









Finished product









I'm pretty pleased right now. 

Next up, I need to finish that how-to write-up, and start messing with testing the temperature sensor. And now that I have a spare setup that works, it will be a lot easier to play, while the tank continues to run normally. roud:


----------



## AnotherHobby

Wow! You full on swiped my concept project's clear case idea — LOL! And it looks great, just like suspected! 

Nice job on it.


----------



## kman

AnotherHobby said:


> Wow! You full on swiped my concept project's clear case idea — LOL! And it looks great, just like suspected!
> 
> Nice job on it.


Yup. Even gave you full credit in the first paragraph.  Your solution to the problem was too brilliant to not steal!

And thanks! It should work well for your project, too. Your case is a little different (thicker Lexan vs plastic? might be more -- or less -- difficult to work with), but the concept is definitely the same.


----------



## AnotherHobby

kman said:


> Yup. Even gave you full credit in the first paragraph.  Your solution to the problem was too brilliant to not steal!
> 
> And thanks! It should work well for your project, too. Your case is a little different (thicker Lexan vs plastic? might be more -- or less -- difficult to work with), but the concept is definitely the same.


It's all good, was just hoping to share it myself.


----------



## kman

AnotherHobby said:


> It's all good, was just hoping to share it myself.


Based on what you told me, a clear project box is the least of what you're about to unleash. It's going to blow people away!


----------



## 75ona76

Nicely done kman!


----------



## Dmarksvr

Has anyone added sound responsiveness so their lights flash in sync with thunder from audio source? ....or better yet add the ability to load tracks and play them at set times for set durations along with storm feature and sound responsiveness? Preferably for the E series and then make me one


----------



## AnotherHobby

Dmarksvr said:


> Has anyone added sound responsiveness so their lights flash in sync with thunder from audio source? ....or better yet add the ability to load tracks and play them at set times for set durations along with storm feature and sound responsiveness? Preferably for the E series and then make me one


There would be no way to make the audio sync to the lights flashing. The light flashing is random (or perhaps on a "script") and is not controllable. The best you could do would be to play thunderstorm sounds, but they would not sync up with the flashes.


----------



## mistergreen

Dmarksvr said:


> Has anyone added sound responsiveness so their lights flash in sync with thunder from audio source? ....or better yet add the ability to load tracks and play them at set times for set durations along with storm feature and sound responsiveness? Preferably for the E series and then make me one


There are a few libraries out there that will play sound file from an SD card.

Lightning an thunder rarely sync in real life though.  There a few seconds delay. Hope you're not around when they do sync.


----------



## Greg0u812

mistergreen said:


> There are a few libraries out there that will play sound file from an SD card.
> 
> Lightning an thunder rarely sync in real life though.  There a few seconds delay. Hope you're not around when they do sync.



Was on a camping trip 14 years ago when it synched up on me. Hit the tree my rig was parked under!

Happened again 2 years ago at my house. Hit the chimney and fried my air conditioning system (actually caught the air handler on fire).


You feel it and smell it right before it hits. Makes your hair stand on end. Not something I want to experience a third time!


Very nice looking setup kman!
I'm still needing a few things to get my setup going but hope to start on it fairly soon.


----------



## toaduck

I have read back and forth on this thread and have some questions. I'm looking to get started on one of these. I only want the led control for 2 sat plus units and none of the bells and whistles. Only the necessities. 

Is the lcd screen necessary?

Is the real time clock necessary?

I'm all for supporting arduino but are the clones ok. Which one would you recommend? Looking to minimize cost. 

Is there some where I can find a complete schematic tutorial for wiring it. It seems spread out all over the forum. 

Thanks guys for committing your time and energy to this project.


----------



## kman

toaduck said:


> I have read back and forth on this thread and have some questions. I'm looking to get started on one of these. I only want the led control for 2 sat plus units and none of the bells and whistles. Only the necessities.
> 
> Is the lcd screen necessary?
> 
> Is the real time clock necessary?
> 
> I'm all for supporting arduino but are the clones ok. Which one would you recommend? Looking to minimize cost.
> 
> Is there some where I can find a complete schematic tutorial for wiring it. It seems spread out all over the forum.
> 
> Thanks guys for committing your time and energy to this project.


The LCD screen isn't strictly necessary, but it makes life a LOT easier. I think it's possible to do it without the RTC, too, but if you ever lose power (or unplug it for a moment) you have to reset the clock, and the only way to do that is reconnect to a computer. HUGE PITA. The RTC is well worth the $6 (or less).

I posted the full schematic just page back, in post #603. (assuming you use an i2c LCD, or no LCD ... if you have to wire all the LCD pins it's a LOT more work)

The full parts list is simply: Arduino, RTC, IR LED, Resistor, and perhaps an LCD. 5 Parts, total.

I used an UNO clone from Amazon that is working great ($13): 
UNO R3 Rev3

The Funduino clones from DealExtreme.com work as well, I'm told, but at $15 you're not saving any money over Amazon and you'll wait weeks for delivery. Arduino is supposed to be an open source platform, meant for others to build on, so buying a clone that works well and makes no pretense at being an Arduino-BRAND Arduino, there is no problem whatsoever with buying a legit compatible board, like the ones I've listed here.

You'll want a DS1307 based RTC. They're cheap. I used this one ($6):
SainSmart Tiny RTC I2C DS1307 AT24C32 24C32 memory Real Time Clock Module for Arduino


You'll need an IR LED. $2.50 at Radio Shack is as good as you're going to get for a single LED, unless you have a local electronics supply store that can do better. They're a lot cheaper (each) in bulk, of course, but if you're only building one, that won't help you.

I recommend an LCD, but technically you can do it without it. A 16x2 LCD with i2c connection is fairly cheap at Amazon at $11:
SainSmart IIC/I2C/TWI 1602 Serial LCD Module Display
If you're willing to wait a month for shipping, you can probably save about $6 and get one for $5 from overseas.

The only other thing you probably need to buy is a 150 ohm resistor. Radio Shack can help ($1.50), or your local electronics supply.

You'll also need a soldering iron, solder, wires to connect it all, a USB power supply (an old phone charger, perhaps?) and some sort of enclosure, but the parts I listed about is really all you absolutely have to have.

If you want to get fancy, some perf board to make a more permanent setup (see my post #603 linked above), but that's not strictly necessary... it could technically be done with all with wires if you really need to pinch pennies.

Assuming you can scrounge that last stuff, $34 plus tax is all you're looking at (less if you can wait for shipments from China). Pretty reasonable, IMO.


----------



## toaduck

kman said:


> The LCD screen isn't strictly necessary, but it makes life a LOT easier. I think it's possible to do it without the RTC, too, but if you ever lose power (or unplug it for a moment) you have to reset the clock, and the only way to do that is reconnect to a computer. HUGE PITA. The RTC is well worth the $6 (or less).
> 
> I posted the full schematic just page back, in post #603. (assuming you use an i2c LCD, or no LCD ... if you have to wire all the LCD pins it's a LOT more work)
> 
> I used an UNO clone from Amazon that is working great ($13):
> UNO R3 Rev3
> 
> The Funduino clones from DealExtreme.com work as well, I'm told, but at $15 you're not saving any money over Amazon and you'll wait weeks for delivery. Arduino is supposed to be an open source platform, meant for others to build on, so buying a clone that works well and makes no pretense at being an Arduino-BRAND Arduino, there is no problem whatsoever with buying a legit compatible board, like the ones I've listed here.
> 
> You'll want a DS1307 based RTC. They're cheap. I used this one ($6):
> SainSmart Tiny RTC I2C DS1307 AT24C32 24C32 memory Real Time Clock Module for Arduino
> 
> You'll need an IR LED. $2.50 at Radio Shack is as good as you're going to get for a single LED, unless you have a local electronics supply store that can do better. They're a lot cheaper (each) in bulk, of course, but if you're only building one, that won't help you.
> 
> I recommend an LCD, but technically you can do it without it. A 16x2 LCD with i2c connection is fairly cheap at Amazon at $11:
> SainSmart IIC/I2C/TWI 1602 Serial LCD Module Display
> If you're willing to wait a month for shipping, you can probably save about $6 and get one for $5 from overseas.
> 
> The only other thing you probably need to buy is a 150 ohm resistor. Radio Shack can help ($1.50), or your local electronics supply.
> 
> You'll also need a soldering iron, solder, wires to connect it all, a USB power supply (an old phone charger, perhaps?) and some sort of enclosure, but the parts I listed about is really all you absolutely have to have.
> 
> Assuming you can scrounge that last stuff, $34 plus tax is all you're looking at (less if you can wait for shipments from China). Pretty reasonable, IMO.


What does the output voltage on the phone need to be or does it matter.


----------



## toaduck

Oh and is the ir receiver necessary? It's just for when he was experimenting, right?


----------



## kman

toaduck said:


> What does the output voltage on the phone need to be or does it matter.





toaduck said:


> Oh and is the ir receiver necessary? It's just for when he was experimenting, right?


Any phone charger where you put a USB connector into the charger is going to be USB standard 5v, which is exactly what you need.

And yes, that's right. The IR receiver was only necessary in the early stages so the OP could learn the IR codes for the Current lights. Since you have Current lights, all of that work is done. If you had some other brand of light that used IR but different codes, then you would have some work to do, and would need the IR receiver as well. (The Ecotech E series codes have also been worked out by AnotherHobby, so if you end up with those lights, also no need for IR receiver) But no, you're all set with Current lights.


----------



## toaduck

Thanks so much. You have just made up my mind. I'm going to do this. My wife thanks you.......lololol


----------



## kman

toaduck said:


> Thanks so much. You have just made up my mind. I'm going to do this. My wife thanks you.......lololol


Have fun, and post pictures!


----------



## AnotherHobby

If you want to make this super stupid easy, get a data logging shield. They are less than $5 shipped on evilbay (for example, search item 191138232800). It has an RTC built in, so you don't have to solder that, and it has a small prototyping area to solder the resistor and IR emitter, so you don't need a proto-shield. The proto area has 5v, ground, and access to all the pins you need. You are reduced to 3 simple soldering joints for the IR stuff, and that's it. It has an SD card you won't use, but for less than $5 who cares.


----------



## toaduck

AnotherHobby said:


> If you want to make this super stupid easy, get a data logging shield. They are less than $5 shipped on evilbay (for example, search item 191138232800). It has an RTC built in, so you don't have to solder that, and it has a small prototyping area to solder the resistor and IR emitter, so you don't need a proto-shield. The proto area has 5v, ground, and access to all the pins you need. You are reduced to 3 simple soldering joints for the IR stuff, and that's it. It has an SD card you won't use, but for less than $5 who cares.


Thanks. I wish I understood this stuff more. I wouldn't know where to solder what. I'm good at following schematics.....my soldering skills are good but that's about it. Can you explain where would solder where in relation to using kmans schematic?


----------



## AnotherHobby

toaduck said:


> Thanks. I wish I understood this stuff more. I wouldn't know where to solder what. I'm good at following schematics.....my soldering skills are good but that's about it. Can you explain where would solder where in relation to using kmans schematic?


Here is a 5 minute photoshop job for you — if you are a visual person, this should really help. I changed the "legs" of the resistor and LED to yellow and green instead of silver so they show up better in the pic. 

The resistor is 150 ohms. The IR LED has a +/- side, so you just need to make sure the - side goes to the resistor and then to ground. The - side physically has a flat spot on that side of the LED, and a shorter leg. The resistor orientation does not matter.

You can just bend the legs to reach their spots. Only 3 solder joints, highlighted in red. It can't get much easier than this.


----------



## toaduck

AnotherHobby said:


> Here is a 5 minute photoshop job for you — if you are a visual person, this should really help. I changed the "legs" of the resistor and LED to yellow and green instead of silver so they show up better in the pic.
> 
> The resistor is 150 ohms. The IR LED has a +/- side, so you just need to make sure the - side goes to the resistor and then to ground. The - side physically has a flat spot on that side of the LED, and a shorter leg. The resistor orientation does not matter.
> 
> You can just bend the legs to reach their spots. Only 3 solder joints, highlighted in red. It can't get much easier than this.



How do you attatch this to the arduino?


----------



## kman

toaduck said:


> How do you attatch this to the arduino?


It's an Arduino shield. They just stack right on top of the Arduino, and the "legs" on the bottom of the shield align perfectly with, and slide directly into, the headers on the Arduino itself.

Great find, AH! I wish I had known about this when I built mine!


----------



## toaduck

kman said:


> It's an Arduino shield. They just stack right on top of the Arduino, and the "legs" on the bottom of the shield align perfectly with, and slide directly into, the headers on the Arduino itself.
> 
> Great find, AH! I wish I had known about this when I built mine!


Ah ok. I'm learning. Thanks guys. 

Are you lurking brookster? Lol


----------



## Dahammer

Wow! I see that this project is still alive and well after all this time! My Arduino and Sat+ fixture is still going strong and hasn't missed a beat.


----------



## Linwood

Are any of you controlling a log of LED+ with the same controller? Like 4? 

I have 4 I'm doing with a Raspberry Pi (I thought it better for network control), and while the final setup for mounting the receivers is not done, I still find that it periodically "misses" one. Last night for example it changed from blue to off, and one of the four was still blue. 

Not always the same one either.

I have the system sending the same signal twice with a half second between, to try to be a bit redundant. I could send it even more frequently of course, but first in the final mounting place I want to experiment with diffusers and see how that helps.

But just wondering if anyone else is struggling with synchronization of many LED+'s from the same controller?


----------



## AnotherHobby

Linwood said:


> Are any of you controlling a log of LED+ with the same controller? Like 4?
> 
> I have 4 I'm doing with a Raspberry Pi (I thought it better for network control), and while the final setup for mounting the receivers is not done, I still find that it periodically "misses" one. Last night for example it changed from blue to off, and one of the four was still blue.
> 
> Not always the same one either.
> 
> I have the system sending the same signal twice with a half second between, to try to be a bit redundant. I could send it even more frequently of course, but first in the final mounting place I want to experiment with diffusers and see how that helps.
> 
> But just wondering if anyone else is struggling with synchronization of many LED+'s from the same controller?


I think it's the internal controller of the Sat + that misses stuff. I have 3 of them, and they all do it (sometimes fail to make an adjustment). They will do it alone, or when used together. They all also occasionally flicker every once and a while when making an adjustment. They never failed to switch modes (from M1 to M2 or whatever) and also never failed to turn off or on, but they didn't respond 100% to red, green, blue, or white adjustments up and down.

This is why if you are using the fading code (which I was doing), you need to hard reset at the end of each fade using the memory buttons, or else you will get drift. I don't think they were designed to be continually adjusted — I think they were designed to be set to what you want and left alone. 

I have since switched to an E-Series that is smooth as butter with adjustments, never misses anything, and doesn't flicker when making adjustments.


----------



## Linwood

AnotherHobby said:


> I think it's the internal controller of the Sat + that misses stuff. I have 3 of them, and they all do it (sometimes fail to make an adjustment).
> 
> ....
> I have since switched to an E-Series that is smooth as butter with adjustments, never misses anything, and doesn't flicker when making adjustments.


Thanks, glad to know it isn't just me.

Yeah, but with $400+ invested in LED+'s, I think I'll write some more code first. 

Frankly "off" is the hardest one. I've taken to programming M4 to be off (drive all the colors to off separately and save the setting). But even that is not 100% reliable, as once in a while they seem to "forget" the Mx button settings and have to be reprogrammed. 

I think I may change the "off" to be a sequence of spaced out color-down steps over time. Like 2-3 times as many as really needed to make sure they work, and forget the M4 setting. 

The "off" problem could be circumvented if manufacturers would learn from the programmable remote world -- provide a discrete code for "off" and "on" and not (just) "toggle".


----------



## toaduck

Linwood said:


> Thanks, glad to know it isn't just me.
> 
> Yeah, but with $400+ invested in LED+'s, I think I'll write some more code first.
> 
> Frankly "off" is the hardest one. I've taken to programming M4 to be off (drive all the colors to off separately and save the setting). But even that is not 100% reliable, as once in a while they seem to "forget" the Mx button settings and have to be reprogrammed.
> 
> I think I may change the "off" to be a sequence of spaced out color-down steps over time. Like 2-3 times as many as really needed to make sure they work, and forget the M4 setting.
> 
> The "off" problem could be circumvented if manufacturers would learn from the programmable remote world -- provide a discrete code for "off" and "on" and not (just) "toggle".


I may need a tutorial for writing the code. My stuff for this project came in the mail today and will be constructing the unit tomorrow but have no idea about the coding/software part. Anyone know of anything that would help me understand the code writing part?


----------



## AnotherHobby

toaduck said:


> I may need a tutorial for writing the code. My stuff for this project came in the mail today and will be constructing the unit tomorrow but have no idea about the coding/software part. Anyone know of anything that would help me understand the code writing part?


You shouldn't have to actually _write_ any code. Depending on what you want, the most you should have to do is light editing. There are full projects posted in this thread (including 4-channel RGBW fading between modes). While it make take a little work to figure out what the code is doing, all of the hard work has been done. What parts did you order and what features are you looking for?


----------



## Linwood

kman said:


> You'll need an IR LED. $2.50 at Radio Shack is as good as you're going to get for a single LED, unless you have a local electronics supply store that can do better.


Just noticed this... I tried that LED and found it to be pretty weak, max 100ma.

If you are willing to pay a tiny bit more for the emitter as well, part number 276-142 (2760142) has a 150mw version. 

The radiant power specs are not clear to me, the combo pack says 75 mw/sr, but the online specs say 13-15mw (no /sr specified). 

But I did find that the combo pack emitter seemed more powerful, and I had good success with it (the detector also worked fine with lirc in linux, not sure in arduino).


----------



## Linwood

Is anyone having this problem... 

I am trying to allow a web control of the fixtures while also allowing the OEM remote to work. That means I do not know the state of the light at any given time in the code (well, other than assuming it is powered on).

I am also finding that some controls (specifically the RGBW down/up controls) do not function when a programmed display (e.g. the bottom buttons) is running. They appear to be simply ignored.

The play/stop button would seem the obvious solution, but it does not seem to work; I can only get the RGBW up/down to work after explicitly selecting one of the color keys at the top.

My goal was to do a gradual dim from whatever the current setting is -- without flashing to some fixed color display. But that does not appear to work if any of the programmed displays are run.

Am I missing something, or is it just not possible?


----------



## kman

Linwood said:


> Just noticed this... I tried that LED and found it to be pretty weak, max 100ma.
> 
> If you are willing to pay a tiny bit more for the emitter as well, part number 276-142 (2760142) has a 150mw version.
> 
> The radiant power specs are not clear to me, the combo pack says 75 mw/sr, but the online specs say 13-15mw (no /sr specified).
> 
> But I did find that the combo pack emitter seemed more powerful, and I had good success with it (the detector also worked fine with lirc in linux, not sure in arduino).


That's good to know, thanks! Perhaps I'll snag another one. I've had to deal with occasional misses as well. I figured it was probably more to do with alignment, and since I'm moving to iAqua as soon as I finish building the darn thing out, I wasn't too worried. It would be nice to lick that problem, though. One light control works great, but I, too, have had occasional issues with two. At some point I might play with Curt_Planted's code, too, to see how it does compared to the DaHammer code I've been using. As long as I'm at it, though, I may as well try the stronger emitter, and a $4 part swap seems well worthwhile. Using the same 150 ohm resistor is fine, right?

Bump:


toaduck said:


> I may need a tutorial for writing the code. My stuff for this project came in the mail today and will be constructing the unit tomorrow but have no idea about the coding/software part. Anyone know of anything that would help me understand the code writing part?


AnotherHobby is right. You don't have to know anything about programming. (I sure don't) The code is all posted in this thread. All you have to do is copy the text from here and paste it into the Arduino software. If you like the default timing and mode settings, you don't have to do a thing to the code, just assemble the parts, upload the code and you're on your way. If you want a different start / stop time, just change the alarm timer section of the code (it's very clear) and write in your own times. That's it.


----------



## Linwood

kman said:


> I may as well try the stronger emitter, and a $4 part swap seems well worthwhile. Using the same 150 ohm resistor is fine, right?


I'll be curious what you find. I also found I needed to over-drive it on the Pi. I.e. since it is pulsed, it can handle a lot more amperage pulsed than it could in a steady on state (and in fact I burned one out testing it by turning it on rather than pulsing). I experimented with the current until I seemed to get decent brightness (by camera phone) in comparison to the real remote, and then I had decent range.

I'm redoing the way I handle a lot of things on the Pi today. I switched to sending long strings of color-down to handle turning it off. It fades gradually, and by sending an extra few I'm hoping it will work with 4 (I am testing right now with one as my PC is near that one).

I haven't found any solution for "is a scene mode running". If it is, then everything I send in terms of up/down is just ignored. I'm really disappointed that "stop" doesn't leave it in a mode where up/down will work on the colors.

PS. If I get this working OK I'll start a new thread for Pi based, and post instructions, if anyone is interested in doing it instead of arduino.


----------



## kman

Linwood said:


> I'll be curious what you find. I also found I needed to over-drive it on the Pi. I.e. since it is pulsed, it can handle a lot more amperage pulsed than it could in a steady on state (and in fact I burned one out testing it by turning it on rather than pulsing). I experimented with the current until I seemed to get decent brightness (by camera phone) in comparison to the real remote, and then I had decent range.
> 
> I'm redoing the way I handle a lot of things on the Pi today. I switched to sending long strings of color-down to handle turning it off. It fades gradually, and by sending an extra few I'm hoping it will work with 4 (I am testing right now with one as my PC is near that one).
> 
> I haven't found any solution for "is a scene mode running". If it is, then everything I send in terms of up/down is just ignored. I'm really disappointed that "stop" doesn't leave it in a mode where up/down will work on the colors.
> 
> PS. If I get this working OK I'll start a new thread for Pi based, and post instructions, if anyone is interested in doing it instead of arduino.


I'm sure people will be interested.  I may or may not bother, since I'm pretty invested in the iAqua at the point, but I'd still be curious to see how it's handled. And someday, I may want to see if the techniques can be adapted to bring the iAqua online...

As for the scene issue, perhaps you can simply switch the lights to whatever mode they would normally be in at that time of day (per the regular timers) before changing them? Not ideal, perhaps, but at least workable.


----------



## Linwood

kman said:


> As for the scene issue, perhaps you can simply switch the lights to whatever mode they would normally be in at that time of day (per the regular timers) before changing them? Not ideal, perhaps, but at least workable.


It's the automation programmer in me (have done a lot of warehouse control systems). I am trying to make it immune to the user screwing it up, whether that be me or someone who might use it downstream. So I can sanity check any scheduling someone sets up from the web program, but had hoped to make it idiot proof when using the remote also.


----------



## toaduck

I'm at a stand still. The lcd I received doesn't have the other side chip with the dimmer switch on it. Not sore how to wire it in.


----------



## kman

toaduck said:


> I'm at a stand still. The lcd I received doesn't have the other side chip with the dimmer switch on it. Not sore how to wire it in.


Technically, you can run it headless with no LCD. It's just a little trickier to test, and doesn't look at cool. The LCD isn't strictly necessary, so you should be able to get it up and running without the LCD, and just add it later.

I'd return that LCD and get one with the I2C backpack instead. It's the difference between a simple 4 wire connection (with headers already soldered in) and soldering 12 or more wires directly to the circuit board. See this post for when IndyChus first hooked up the LCD without an i2c:
http://www.plantedtank.net/forums/showpost.php?p=4032753&postcount=206

That's not a dimmer, btw, that's for adjusting contrast.

The 16x2 board I linked you originally definitely includes the i2c backpackfor $11: Amazon.com: SainSmart IIC/I2C/TWI 1602 Serial LCD Module Display for Arduino UNO MEGA R3 *Blue on White*: Industrial & Scientific


And here is a 20x4 if you prefer for $14: www.amazon.com/SainSmart-Serial-Module-Shield-Arduino/dp/B0080DYTZQ/

(there are many others, just search 16x2 (or 20x4) lcd i2c on Amazon and you'll turn up a bunch, just pick the color and price you like)


----------



## toaduck

when I try to run the code i get this 

This report would have more information with
"Show verbose output during compilation"
enabled in File > Preferences.
Arduino: 1.0.6 (Windows 7), Board: "Arduino Uno"
In file included from sketch_nov30a.ino:21:
C:\Program Files (x86)\Arduino\libraries\TimeAlarms/TimeAlarms.h:48: error: 'time_t' does not name a type
C:\Program Files (x86)\Arduino\libraries\TimeAlarms/TimeAlarms.h:49: error: 'time_t' does not name a type
C:\Program Files (x86)\Arduino\libraries\TimeAlarms/TimeAlarms.h:61: error: 'time_t' has not been declared
C:\Program Files (x86)\Arduino\libraries\TimeAlarms/TimeAlarms.h:67: error: 'time_t' has not been declared
C:\Program Files (x86)\Arduino\libraries\TimeAlarms/TimeAlarms.h:69: error: 'time_t' has not been declared
C:\Program Files (x86)\Arduino\libraries\TimeAlarms/TimeAlarms.h:71: error: expected ',' or '...' before 'DOW'
C:\Program Files (x86)\Arduino\libraries\TimeAlarms/TimeAlarms.h:71: error: ISO C++ forbids declaration of 'timeDayOfWeek_t' with no type
C:\Program Files (x86)\Arduino\libraries\TimeAlarms/TimeAlarms.h:73: error: 'time_t' has not been declared
C:\Program Files (x86)\Arduino\libraries\TimeAlarms/TimeAlarms.h:75: error: expected ',' or '...' before 'DOW'
C:\Program Files (x86)\Arduino\libraries\TimeAlarms/TimeAlarms.h:75: error: ISO C++ forbids declaration of 'timeDayOfWeek_t' with no type
C:\Program Files (x86)\Arduino\libraries\TimeAlarms/TimeAlarms.h:77: error: 'time_t' has not been declared
C:\Program Files (x86)\Arduino\libraries\TimeAlarms/TimeAlarms.h:80: error: 'time_t' has not been declared
C:\Program Files (x86)\Arduino\libraries\TimeAlarms/TimeAlarms.h:94: error: 'time_t' has not been declared
C:\Program Files (x86)\Arduino\libraries\TimeAlarms/TimeAlarms.h:95: error: 'time_t' does not name a type
C:\Program Files (x86)\Arduino\libraries\TimeAlarms/TimeAlarms.h:103: error: 'time_t' does not name a type
sketch_nov30a:29: error: 'time_t' does not name a type
sketch_nov30a:25: error: 'RTC_DS1307' does not name a type
sketch_nov30a:26: error: 'IRsend' does not name a type
sketch_nov30a.ino: In function 'void setup()':
sketch_nov30a:97: error: 'RTC' was not declared in this scope
sketch_nov30a:104: error: 'DateTime' was not declared in this scope
sketch_nov30a:107: error: 'syncProvider' was not declared in this scope
sketch_nov30a:107: error: 'setSyncProvider' was not declared in this scope
sketch_nov30a.ino: At global scope:
sketch_nov30a:128: error: 'time_t' does not name a type
sketch_nov30a.ino: In function 'void digitalClockDisplay()':
sketch_nov30a:182: error: 'hour' was not declared in this scope
sketch_nov30a:183: error: 'minute' was not declared in this scope
sketch_nov30a:184: error: 'second' was not declared in this scope
sketch_nov30a.ino: In function 'void lcdClockDisplay()':
sketch_nov30a:190: error: 'hour' was not declared in this scope
sketch_nov30a:191: error: 'minute' was not declared in this scope
sketch_nov30a.ino: In function 'void SendCode(unsigned int, byte, const char*)':
sketch_nov30a:339: error: 'irsend' was not declared in this scope
sketch_nov30a.ino: In function 'int freeRam()':
sketch_nov30a:448: error: expected `}' at end of input


----------



## kman

^^ I'm not positive, but one thing that jumps out at me is the path to your libraries:

C:\Program Files (x86)\Arduino\libraries\TimeAlarms/TimeAlarms.h

The last slash between TimeAlarms and TimeAlarms.h is backwards: "TimeAlarms/TimeAlarms.h"

Note all the other slashes are back slashes.

Otherwise, it looks like maybe there is an issue with your RTC. Is it hooked up correctly? Try a time set sketch to check it and be sure you can.

A quick google turned up this one:
http://www.instructables.com/id/Setting-the-DS1307-Real-Time-Clock-using-the-Seria/

...but there are many others out there.

So upload the RTC Set sketch and make sure it sets correctly and shows the current time correctly, and then assuming all is well, upload the Sat+ controller sketch again and start troubleshooting from there.


----------



## toaduck

kman said:


> ^^ I'm not positive, but one thing that jumps out at me is the path to your libraries:
> 
> C:\Program Files (x86)\Arduino\libraries\TimeAlarms/TimeAlarms.h
> 
> The last slash between TimeAlarms and TimeAlarms.h is backwards: "TimeAlarms/TimeAlarms.h"
> 
> Note all the other slashes are back slashes.
> 
> Otherwise, it looks like maybe there is an issue with your RTC. Is it hooked up correctly? Try a time set sketch to check it and be sure you can.
> 
> A quick google turned up this one:
> http://www.instructables.com/id/Setting-the-DS1307-Real-Time-Clock-using-the-Seria/
> 
> ...but there are many others out there.
> 
> So upload the RTC Set sketch and make sure it sets correctly and shows the current time correctly, and then assuming all is well, upload the Sat+ controller sketch again and start troubleshooting from there.


yea the rtc is setup correctly. Am I right in that I just copy and paste the code from the box in the post and verify and then upload?

Bump: This report would have more information with
"Show verbose output during compilation"
enabled in File > Preferences.
Arduino: 1.0.6 (Windows 7), Board: "Arduino Uno"
sketch_nov30a:29: error: 'time_t' does not name a type
sketch_nov30a:25: error: 'RTC_DS1307' does not name a type
sketch_nov30a:26: error: 'IRsend' does not name a type
sketch_nov30a.ino: In function 'void SetAlarms()':
sketch_nov30a:81: error: 'Alarm' was not declared in this scope
sketch_nov30a.ino: In function 'void setup()':
sketch_nov30a:97: error: 'RTC' was not declared in this scope
sketch_nov30a:104: error: 'DateTime' was not declared in this scope
sketch_nov30a:107: error: 'syncProvider' was not declared in this scope
sketch_nov30a:107: error: 'setSyncProvider' was not declared in this scope
sketch_nov30a:109: error: 'Alarm' was not declared in this scope
sketch_nov30a.ino: In function 'void loop()':
sketch_nov30a:123: error: 'Alarm' was not declared in this scope
sketch_nov30a.ino: At global scope:
sketch_nov30a:128: error: 'time_t' does not name a type
sketch_nov30a.ino: In function 'void ThunderStorm()':
sketch_nov30a:155: error: 'Alarm' was not declared in this scope
sketch_nov30a:171: error: 'Alarm' was not declared in this scope
sketch_nov30a:173: error: 'Alarm' was not declared in this scope
sketch_nov30a:175: error: 'Alarm' was not declared in this scope
sketch_nov30a.ino: In function 'void digitalClockDisplay()':
sketch_nov30a:182: error: 'hour' was not declared in this scope
sketch_nov30a:183: error: 'minute' was not declared in this scope
sketch_nov30a:184: error: 'second' was not declared in this scope
sketch_nov30a.ino: In function 'void lcdClockDisplay()':
sketch_nov30a:190: error: 'hour' was not declared in this scope
sketch_nov30a:191: error: 'minute' was not declared in this scope
sketch_nov30a.ino: In function 'void SendCode(unsigned int, byte, const char*)':
sketch_nov30a:339: error: 'irsend' was not declared in this scope
sketch_nov30a:340: error: 'Alarm' was not declared in this scope


----------



## kman

toaduck said:


> yea the rtc is setup correctly. Am I right in that I just copy and paste the code from the box in the post and verify and then upload?


Yes. Although you need to have all of the libraries referenced at the top of the sketch copied to the right location, too.

I don't know which codebase you are working from, but mine shows these:

#include <Wire.h>
#include <RTClib.h>
#include <Time.h>
#include <TimeAlarms.h>
#include <IRremote.h>
#include <LiquidCrystal.h>
#include <OneWire.h>
#include <DallasTemperature.h>

Those last two won't be needed if you're not including a temp sensor, I believe. But you definitely need Time, TimeAlarms, IRremote and RTClib. Very easy to find if you google "arduino time library" and "arduino timealarms library" and so on.

EDIT: Actually, I had started (but not finished) a full A-Z writeup on how to do all of this, so I have the links collected:



> Before anything else can happen, you need to download a bunch of supplemental “libraries” which are essentially the drivers of the Arduino world, to use PC lingo. All you have to do is download each, and copy the “___.h” files into this folder: “[your documents folder]\Arduino\libraries”
> 
> *Time*: http://www.pjrc.com/teensy/td_libs_Time.html (_get Time.zip_)
> 
> *TimeAlarms*: http://www.pjrc.com/teensy/td_libs_TimeAlarms.html (_get TimeAlarms.zip_)
> 
> *RTClib*: https://github.com/adafruit/RTClib (_click “Download Zip” on the right_)
> 
> *IRremote*: https://github.com/shirriff/Arduino-IRremote (click “Download Zip” on the right)
> (_Unzip it and rename the directory shirriff-Arduino-IRremote-nnn to IRremote, then To install, move the downloaded IRremote directory to: arduino/libraries/IRremote where “arduino” is your Arduino file directory in Documents_)
> EDIT: The github link library may have been updated to a version that does not work anymore. Try this link if you have compiling problems with the github library: https://www.pjrc.com/teensy/td_libs_IRremote.html (see post #692 and just prior for details)
> 
> *LCD*: https://bitbucket.org/fmalpartida/new-liquidcrystal/wiki/Home (_follow the Download links to download the LiquidCrystal_V1.2.1.zip file, and follow instructions to install_)


I didn't quite finish the writeup so it's not 100% completed and error-checked (who knows, there may even be a mistake above), but if you want my current draft to see if it's helpful, shoot me a PM and I'll paste it into a message for you.


----------



## toaduck

This report would have more information with
"Show verbose output during compilation"
enabled in File > Preferences.
Arduino: 1.0.6 (Windows 7), Board: "Arduino Uno"
sat_:26: error: 'IRsend' does not name a type
sat_.ino: In function 'void SetAlarms()':
sat_:81: error: 'Alarm' was not declared in this scope
sat_.ino: In function 'void setup()':
sat_:109: error: 'Alarm' was not declared in this scope
sat_.ino: In function 'void loop()':
sat_:123: error: 'Alarm' was not declared in this scope
sat_.ino: In function 'void ThunderStorm()':
sat_:155: error: 'Alarm' was not declared in this scope
sat_:171: error: 'Alarm' was not declared in this scope
sat_:173: error: 'Alarm' was not declared in this scope
sat_:175: error: 'Alarm' was not declared in this scope
sat_.ino: In function 'void SendCode(unsigned int, byte, const char*)':
sat_:339: error: 'irsend' was not declared in this scope
sat_:340: error: 'Alarm' was not declared in this scope


----------



## kman




----------



## AnotherHobby

kman said:


> Otherwise, it looks like maybe there is an issue with your RTC. Is it hooked up correctly?


An incorrectly hooked up RTC will not result in compile errors. 

toaduck, since I see you figured out how to load the software, I'll just respond here instead of to the PM you sent. kman is correct about the libraries. All of your compile errors are because you don't have all of the libraries in the correct place. You'll need to sort that out first, so I'd recommend looking up some Arduino guides or something.


----------



## toaduck

AnotherHobby said:


> An incorrectly hooked up RTC will not result in compile errors.
> 
> toaduck, since I see you figured out how to load the software, I'll just respond here instead of to the PM you sent. kman is correct about the libraries. All of your compile errors are because you don't have all of the libraries in the correct place. You'll need to sort that out first, so I'd recommend looking up some Arduino guides or something.


I,m sorry for bothering everyone. I'm an idiot. I was putting the libraries in the C/: directory instead of user/documents. 

Anyway, thanks so much everyone, I got it working, without the lcd of course. I would show it but its nothing special and the wiring looks like a cross between Ray Charles and Edward Scissor hands did the work. :hihi:

I'm ordering an lcd today with the back pack and going to tweak later.


BTW, Ive been reading the thread back and forth.....which and whos code supports the gradual sunrise and sunset?


----------



## Linwood

I've touched on a Raspberry Pi vserion in a few posts. I will quit polluting his thread with that, and have started a new one describing that build: here


----------



## jlboygenius

I implemented this a few months ago, and other than the RTC being a few hours off and needing to be reset, it's worked very well.

Can we put this code on GitHub or something? The only trouble I had with this project was finding the right post that had the latest code. 

kman's post was great, and should be the readme on the GitHub site.


----------



## toaduck

jlboygenius said:


> I implemented this a few months ago, and other than the RTC being a few hours off and needing to be reset, it's worked very well.
> 
> Can we put this code on GitHub or something? The only trouble I had with this project was finding the right post that had the latest code.
> 
> kman's post was great, and should be the readme on the GitHub site.


I agree. 

Im trying different codes now. I uploaded the siesta code today. I hope it works correctly. It would be nice to have a list of codes in one spot explaining what each code did. I know it's easy for the guys who are really computer savvy to tell what does what but some of us have a hard time understanding it.


----------



## kman

toaduck said:


> I agree.
> 
> Im trying different codes now. I uploaded the siesta code today. I hope it works correctly. It would be nice to have a list of codes in one spot explaining what each code did. I know it's easy for the guys who are really computer savvy to tell what does what but some of us have a hard time understanding it.


What do you mean by "code"? IR code? That's all pretty well annotated in the various sketches since the start, I thought. If there is a specific section you're trying to figure out, just let me know.


----------



## toaduck

kman said:


> What do you mean by "code"? IR code? That's all pretty well annotated in the various sketches since the start, I thought. If there is a specific section you're trying to figure out, just let me know.


I was meaning sketches. Sorry. Still learning the terminology. 

I guess what I'm asking is, does the siesta sketch that "curt planted" wrote for the ramping up and down feature work correctly. It's on post 452. I like the idea of the gradual ramping but after reading I couldn't determine if he ever got it working perfectly. 

You did a fantastic job on the tutorial write up.


----------



## toaduck

another question just to make sure. All i do is replace times in parenthesis in the sketch to customize it to my desired schedule, correct? nothing else to change for that?







Code:


// Set up your desired alarms here
  // The default value of dtNBR_ALARMS is 6 in TimeAlarms.h.
  // This code sets 9 alarms by default, so you'll need to change dtNBR_ALARMS to 9 or more
  // Changes the times to suit yourself. Add as many alarms as you like, just stay within dtNBR_ALARMS
  Alarm.alarmRepeat(16,59,0, PowerOnOff);   // power on at 4:59pm (still moonlight mode)
  Alarm.alarmRepeat(17,00,0, DawnDusk);     // Dawn/Dusk mode at 5pm for 30 min
  //Alarm.alarmRepeat(7,30,0, Cloud2); // (HR,MIN,SEC,FUNCTION)
  Alarm.alarmRepeat(17,30,0, FullSpec);    // FullSpectrum at 5:30
  //Alarm.alarmRepeat(18,00,0, Cloud2);
  Alarm.alarmRepeat(23,40,0, DawnDusk);    // Back to Dawn/Dusk at 11:40pm for 20 min
  Alarm.alarmRepeat(23,59,0, Moon2);       // Moonlight mode at 11:59pm for 45 min
  Alarm.alarmRepeat(0,45,0, PowerOnOff);   // Power off at 12:45am


----------



## kman

toaduck said:


> I was meaning sketches. Sorry. Still learning the terminology.
> 
> I guess what I'm asking is, does the siesta sketch that "curt planted" wrote for the ramping up and down feature work correctly. It's on post 452. I like the idea of the gradual ramping but after reading I couldn't determine if he ever got it working perfectly.
> 
> You did a fantastic job on the tutorial write up.


Thanks! 

No worries on the lingo, I just wasn't sure if you were trying to determine specific IR codes or something else. "Code" can be an unfortunately vague term, I agree.

I can't answer your question about Curt_Planted's sketch, unfortunately. That's something I've been meaning to look into myself. But since my Controller is working reasonably well with Dahammer's sketch (calling it that even though Indychus and others contributed, just for the record, you haven't been forgotten, guys!), and I've been busy trying to get my iAqua controller finished (WAY more complicated), I just haven't had a chance to download Curt's code and play with it yet. If he doesn't chime in soon, you may want to send him a PM? 

Personally, though, in the meantime, I'd set up the Dahammer 4.1 sketch and get that dialed in, since it's much simpler and many people are using it successfully. Once you have everything fully functional and working, then start playing with Curt's fancy sketch. You'll always have a fully functional fall-back position while you experiment. That's what I've done, anyway, so perhaps I'm biased about that being a good idea. LOL



toaduck said:


> another question just to make sure. All i do is replace times in parenthesis in the sketch to customize it to my desired schedule, correct? nothing else to change for that?
> 
> 
> 
> Code:
> 
> 
> // Set up your desired alarms here
> // The default value of dtNBR_ALARMS is 6 in TimeAlarms.h.
> // This code sets 9 alarms by default, so you'll need to change dtNBR_ALARMS to 9 or more
> // Changes the times to suit yourself. Add as many alarms as you like, just stay within dtNBR_ALARMS
> Alarm.alarmRepeat(16,59,0, PowerOnOff);   // power on at 4:59pm (still moonlight mode)
> Alarm.alarmRepeat(17,00,0, DawnDusk);     // Dawn/Dusk mode at 5pm for 30 min
> //Alarm.alarmRepeat(7,30,0, Cloud2); // (HR,MIN,SEC,FUNCTION)
> Alarm.alarmRepeat(17,30,0, FullSpec);    // FullSpectrum at 5:30
> //Alarm.alarmRepeat(18,00,0, Cloud2);
> Alarm.alarmRepeat(23,40,0, DawnDusk);    // Back to Dawn/Dusk at 11:40pm for 20 min
> Alarm.alarmRepeat(23,59,0, Moon2);       // Moonlight mode at 11:59pm for 45 min
> Alarm.alarmRepeat(0,45,0, PowerOnOff);   // Power off at 12:45am


Yes, exactly. For instance, on the line that says "Alarm.alarmRepeat(*17,30*,0, FullSpec);" which turns on full spectrum lighting at 5:30pm, if you wanted that to happen at 10am instead, just change the time itself (bolded here) to "Alarm.alarmRepeat(*10,00*,0, FullSpec);"


----------



## toaduck

and this text below (time alarms lib) in red is the number you change to increase time alarms correct? I increased to 25






Code:


//  TimeAlarms.h - Arduino Time alarms header for use with Time library

#ifndef TimeAlarms_h
#define TimeAlarms_h

#include <inttypes.h>

#include "Time.h"

#define dtNBR_ALARMS 25   // max is 255

#define USE_SPECIALIST_METHODS  // define this for testing


----------



## AnotherHobby

toaduck said:


> and this text below (time alarms lib) in red is the number you change to increase time alarms correct? I increased to 25
> 
> 
> 
> 
> 
> 
> Code:
> 
> 
> //  TimeAlarms.h - Arduino Time alarms header for use with Time library
> 
> #ifndef TimeAlarms_h
> #define TimeAlarms_h
> 
> #include <inttypes.h>
> 
> #include "Time.h"
> 
> #define dtNBR_ALARMS 25   // max is 255
> 
> #define USE_SPECIALIST_METHODS  // define this for testing


Yes.


----------



## O2surplus

Here's the sketch that I use to set the time and date on my DS1307 RTC. This sketch was written by "Sink", a forum member here at PlantedTank.net This sketch pulls the time directly from your PC, and loads it directly to the clock chip. There's no need to adjust any variables, just open the sketch, & hit the upload button. Easy Peasy



Code:


 /*
 * Name:    timeset.pde
 * Author:    User "sink" at plantedtank.net forums
 * URL:        http://bitbucket.org/akl/tank-control
 *
 * This code sets the time on a DS1307 RTC chip attached to an Arduino
 * microcontroller board.  The time is set to the system time when the
 * sketch was compiled (using preprocessor macros), so make sure that:
 *
 *     1. Your system (PC) time is accurate.
 *     2. You recompile the sketch and then upload immediately.
 *
 * The time is skewed forward some seconds to accomodate for compilation and
 * upload time.  If you find the time is consistently off by the same amount,
 * you can modify the adjustment constant below.
 *
 * This code requires the following libraries: Wire, Time, DS1307RTC
 *
 * The latest version of this code can always be found at above url.
 */

/*
 * Copyright (c) 2011, User "sink" at plantedtank.net forums
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.

 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.  
 */

#include <Time.h>
#include <Wire.h>
#include <DS1307RTC.h>

/*
 * Time adjustment forward in seconds.  Used to compensate for
 * compilation/upload time.
 */
const int kAdjustment = 25;

/*
 * Utility function for pretty digital clock time output
 * From example code in Time library -- author unknown
 */
void printDigits(int digits) {
  Serial.print(":");
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits);
}

/*
 * Display time
 * Adapted from example code in Time library -- author unknown
 */
void digitalClockDisplay() {
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.print(" ");
  Serial.print(month());
  Serial.print("/");
  Serial.print(day());
  Serial.print("/");
  Serial.print(year()); 
  Serial.println(); 
}

void setup() {

  // convert compilation time and set system clock
  char const *date = __DATE__;
  char const *time = __TIME__;
  int sec, min, hour, day, month, year;
  
  char s_month[5];
  static const char month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
  sscanf(date, "%s %d %d", s_month, &day, &year);
  month = (strstr(month_names, s_month)-month_names)/3;
  sscanf(time, "%d:%d:%d", &hour, &min, &sec);

  setTime(hour, min, sec, day, month+1, year);
  adjustTime(kAdjustment); // crude fwd correction
  time_t t = now();

  // set RTC
  RTC.set(t);

  Serial.begin(115200);
}

void loop () {
  digitalClockDisplay();  
  Serial.println(); 
  delay(1000);
}


----------



## kman

Nice, O2! I'll grab that. It's better the one I'd been using. (Which was already way better than others I've seen!)


----------



## Greg0u812

WOW!!!!!
Thank you @kman !

I hope that instructional post can get get stickied around here.

I had a couple of very simple and stupid questions that Google has not been able to answer for me.

The answers were right in front of me!


----------



## kman

O2surplus said:


> Here's the sketch that I use to set the time and date on my DS1307 RTC. This sketch was written by "Sink", a forum member here at PlantedTank.net This sketch pulls the time directly from your PC, and loads it directly to the clock chip. There's no need to adjust any variables, just open the sketch, & hit the upload button. Easy Peasy


Hmm. I tried this code today, and I can't get it to compile and verify.

At first, it complained about RTC.set not being defined, but then I realized I needed to add the libraries from the link in the code. (in particular, ds1307rtc)

So I added the libraries and restarted the IDE, and now it's giving me a bunch of errors from within the ds12307rtc library:



Code:


  This report would have more information with
  "Show verbose output during compilation"
  enabled in File > Preferences.
Arduino: 1.0.6 (Mac OS X), Board: "Arduino Uno"
/Users/kman/Documents/Arduino/libraries/DS1307RTC/DS1307RTC.cpp: In static member function 'static void DS1307RTC::read(tmElements_t&)':
/Users/kman/Documents/Arduino/libraries/DS1307RTC/DS1307RTC.cpp:56: error: 'class TwoWire' has no member named 'send'
/Users/kman/Documents/Arduino/libraries/DS1307RTC/DS1307RTC.cpp:62: error: 'class TwoWire' has no member named 'receive'
/Users/kman/Documents/Arduino/libraries/DS1307RTC/DS1307RTC.cpp:63: error: 'class TwoWire' has no member named 'receive'
/Users/kman/Documents/Arduino/libraries/DS1307RTC/DS1307RTC.cpp:64: error: 'class TwoWire' has no member named 'receive'
/Users/kman/Documents/Arduino/libraries/DS1307RTC/DS1307RTC.cpp:65: error: 'class TwoWire' has no member named 'receive'
/Users/kman/Documents/Arduino/libraries/DS1307RTC/DS1307RTC.cpp:66: error: 'class TwoWire' has no member named 'receive'
/Users/kman/Documents/Arduino/libraries/DS1307RTC/DS1307RTC.cpp:67: error: 'class TwoWire' has no member named 'receive'
/Users/kman/Documents/Arduino/libraries/DS1307RTC/DS1307RTC.cpp:68: error: 'class TwoWire' has no member named 'receive'
/Users/kman/Documents/Arduino/libraries/DS1307RTC/DS1307RTC.cpp: In static member function 'static void DS1307RTC::write(tmElements_t&)':
/Users/kman/Documents/Arduino/libraries/DS1307RTC/DS1307RTC.cpp:74: error: 'class TwoWire' has no member named 'send'
/Users/kman/Documents/Arduino/libraries/DS1307RTC/DS1307RTC.cpp:76: error: 'class TwoWire' has no member named 'send'
/Users/kman/Documents/Arduino/libraries/DS1307RTC/DS1307RTC.cpp:77: error: 'class TwoWire' has no member named 'send'
/Users/kman/Documents/Arduino/libraries/DS1307RTC/DS1307RTC.cpp:78: error: 'class TwoWire' has no member named 'send'
/Users/kman/Documents/Arduino/libraries/DS1307RTC/DS1307RTC.cpp:79: error: 'class TwoWire' has no member named 'send'
/Users/kman/Documents/Arduino/libraries/DS1307RTC/DS1307RTC.cpp:80: error: 'class TwoWire' has no member named 'send'
/Users/kman/Documents/Arduino/libraries/DS1307RTC/DS1307RTC.cpp:81: error: 'class TwoWire' has no member named 'send'
/Users/kman/Documents/Arduino/libraries/DS1307RTC/DS1307RTC.cpp:82: error: 'class TwoWire' has no member named 'send'


----------



## O2surplus

kman said:


> Hmm. I tried this code today, and I can't get it to compile and verify.
> 
> At first, it complained about RTC.set not being defined, but then I realized I needed to add the libraries from the link in the code. (in particular, ds1307rtc)
> 
> So I added the libraries and restarted the IDE, and now it's giving me a bunch of errors from within the ds12307rtc library:
> 
> 
> 
> Code:
> 
> 
> This report would have more information with
> "Show verbose output during compilation"
> enabled in File > Preferences.
> Arduino: 1.0.6 (Mac OS X), Board: "Arduino Uno"
> /Users/kman/Documents/Arduino/libraries/DS1307RTC/DS1307RTC.cpp: In static member function 'static void DS1307RTC::read(tmElements_t&)':
> /Users/kman/Documents/Arduino/libraries/DS1307RTC/DS1307RTC.cpp:56: error: 'class TwoWire' has no member named 'send'
> /Users/kman/Documents/Arduino/libraries/DS1307RTC/DS1307RTC.cpp:62: error: 'class TwoWire' has no member named 'receive'
> /Users/kman/Documents/Arduino/libraries/DS1307RTC/DS1307RTC.cpp:63: error: 'class TwoWire' has no member named 'receive'
> /Users/kman/Documents/Arduino/libraries/DS1307RTC/DS1307RTC.cpp:64: error: 'class TwoWire' has no member named 'receive'
> /Users/kman/Documents/Arduino/libraries/DS1307RTC/DS1307RTC.cpp:65: error: 'class TwoWire' has no member named 'receive'
> /Users/kman/Documents/Arduino/libraries/DS1307RTC/DS1307RTC.cpp:66: error: 'class TwoWire' has no member named 'receive'
> /Users/kman/Documents/Arduino/libraries/DS1307RTC/DS1307RTC.cpp:67: error: 'class TwoWire' has no member named 'receive'
> /Users/kman/Documents/Arduino/libraries/DS1307RTC/DS1307RTC.cpp:68: error: 'class TwoWire' has no member named 'receive'
> /Users/kman/Documents/Arduino/libraries/DS1307RTC/DS1307RTC.cpp: In static member function 'static void DS1307RTC::write(tmElements_t&)':
> /Users/kman/Documents/Arduino/libraries/DS1307RTC/DS1307RTC.cpp:74: error: 'class TwoWire' has no member named 'send'
> /Users/kman/Documents/Arduino/libraries/DS1307RTC/DS1307RTC.cpp:76: error: 'class TwoWire' has no member named 'send'
> /Users/kman/Documents/Arduino/libraries/DS1307RTC/DS1307RTC.cpp:77: error: 'class TwoWire' has no member named 'send'
> /Users/kman/Documents/Arduino/libraries/DS1307RTC/DS1307RTC.cpp:78: error: 'class TwoWire' has no member named 'send'
> /Users/kman/Documents/Arduino/libraries/DS1307RTC/DS1307RTC.cpp:79: error: 'class TwoWire' has no member named 'send'
> /Users/kman/Documents/Arduino/libraries/DS1307RTC/DS1307RTC.cpp:80: error: 'class TwoWire' has no member named 'send'
> /Users/kman/Documents/Arduino/libraries/DS1307RTC/DS1307RTC.cpp:81: error: 'class TwoWire' has no member named 'send'
> /Users/kman/Documents/Arduino/libraries/DS1307RTC/DS1307RTC.cpp:82: error: 'class TwoWire' has no member named 'send'



Ah ha! The old "pre- Arduino 1.0" library incompatibility raised it's ugly head again. The libraries that you downloaded from "Sink"'s site haven't been updated to work with Arduino 1.0 & above.

Try these copies of the libraries. They work fine for me.


----------



## kman

O2surplus said:


> Ah ha! The old "pre- Arduino 1.0" library incompatibility raised it's ugly head again. The libraries that you downloaded from "Sink"'s site haven't been updated to work with Arduino 1.0 & above.
> 
> Try these copies of the libraries. They work fine for me.


Ha! That did it, thanks! I'll definitely use that sketch next time I tinker with the code on my controller. 

(I already have a current Time library, of course, it just needed the ds1307 one updated)


----------



## toaduck

Hi. I'm trying to set my light to 50% intensity. How would I do this. I tried following curts instructions on his previous post but didn't work. Any help?


----------



## kman

toaduck said:


> Hi. I'm trying to set my light to 50% intensity. How would I do this. I tried following curts instructions on his previous post but didn't work. Any help?


You should be able to set it to Yellow, or any of the other preset colors (not Dynamic modes, since those can't be altered), and then use the up and down arrows on the remote to lower the brightness.


----------



## toaduck

kman said:


> You should be able to set it to Yellow, or any of the other preset colors (not Dynamic modes, since those can't be altered), and then use the up and down arrows on the remote to lower the brightness.


Yea I know that but how many total clicks does it take to go from on to of. I wanted to determine where half way was.


----------



## kman

toaduck said:


> Yea I know that but how many total clicks does it take to go from on to of. I wanted to determine where half way was.


It's not always the same, but IIRC AnotherHobby pegged it at around 40 or so? Best to just count it. Once you have your middle level, save it to a preset.

Edit: Actually, per Curt_Planted's posts around page 32 of this thread, there are 42 steps between zero and max, for the Sat+. But each button press sends double commands for some reason, so you'll actually only press the remote button 21 times, going from zero to max. I'm not certain that 10 or 11 presses will definitely put you at a true 50% output, however, so you may have to futz with it until you find what you like.

Are you using Curt_Planted's code, or one of DaHammer's code versions?


----------



## toaduck

kman said:


> It's not always the same, but IIRC AnotherHobby pegged it at around 40 or so? Best to just count it. Once you have your middle level, save it to a preset.
> 
> Edit: Actually, per Curt_Planted's posts around page 32 of this thread, there are 42 steps between zero and max, for the Sat+. But each button press sends double commands for some reason, so you'll actually only press the remote button 21 times, going from zero to max. I'm not certain that 10 or 11 presses will definitely put you at a true 50% output, however, so you may have to futz with it until you find what you like.
> 
> Are you using Curt_Planted's code, or one of DaHammer's code versions?


I'm not sure who's code I'm using.......I'll have to look. Does curt's code have the "color up" code in it. It would be nice to have it so I could be sure it was 50 %. I'm just trying to get to a certain par level. 

I just tested doing it with the remote and sometimes it took 30 clicks and sometimes over 40. I tried it several times. Started to eat the remote one time....frustrated. lolol [emoji14]


----------



## kman

toaduck said:


> I'm not sure who's code I'm using.......I'll have to look. Does curt's code have the "color up" code in it. It would be nice to have it so I could be sure it was 50 %. I'm just trying to get to a certain par level.
> 
> I just tested doing it with the remote and sometimes it took 30 clicks and sometimes over 40. I tried it several times. Started to eat the remote one time....frustrated. lolol [emoji14]


You're not going to hit a specific PAR level without using a PAR meter to verify. 50% of clicks does not necessarily correlate to 50% of max PAR for the light. That would depend on the specific output curve used by the electronics, which to my knowledge, no one has ever attempted to map out. I would recommend simply shoot for ~50% (20-21 clicks) and see how it works in the real world, rather than obsessing over a mathematically-accurate number. In other words, just try it.  Fine tune it once it's otherwise working.


----------



## Linwood

kman said:


> You're not going to hit a specific PAR level without using a PAR meter to verify. 50% of clicks does not necessarily correlate to 50% of max PAR for the light. That would depend on the specific output curve used by the electronics, which to my knowledge, no one has ever attempted to map out. I would recommend simply shoot for ~50% (20-21 clicks) and see how it works in the real world, rather than obsessing over a mathematically-accurate number. In other words, just try it.  Fine tune it once it's otherwise working.


One thing you can do that's probably more accurate than just picking the middle is, if you have one, put an ammeter on the power supply (either the 110v or 12v as you have capability for). If you then adjust until you get half the current draw for a given color combination you are probably somewhere a bit close to the 50% PAR. Though of course PAR is usable light and amps include heat generated, etc.... but many people have ammeters who don't have PAR meters.


----------



## Freemananana

A great thread, but I'm only about half way through reading the whole thing. I have a quick question:

I only want to control my SAT+, this is the guide for me, right? 

I have read a lot of the other arduino threads and they all seem to include things such as dosing pumps. I don't want anything aside from lighting control at the moment.


----------



## toaduck

This is the thread for you. Ignore the posts about the dosing and ph controllers etc etc.


----------



## toaduck

There's a condensed picture guide towards the end.


----------



## Freemananana

toaduck said:


> There's a condensed picture guide towards the end.


Guess I'll keep looking haha :hihi:


----------



## kman

Freemananana said:


> Guess I'll keep looking haha :hihi:


Take a look at page 42, to start.


----------



## Freemananana

kman said:


> Take a look at page 42, to start.


Thanks! I was about 10 pages prior to that when I made that comment. I have since then read through the entirety of the thread. You have made a huge effort to help the new comers, I appreciate that! 

I am going to order the pieces I need and I should be good to go. Deciding on how I want the light to function is going to take some debating and trial and error.


----------



## Dazzlin Dave™

New member here. I applaud all involved with this controller build. You have done an exceptional job !
I am currrently trying this build out for the Sat+, but I'm having problems compiling the sketch...
I'm using Ardunio ver 1.06 now as the newest ver. gave to many errors.
This is what my error report says:
This report would have more information with
"Show verbose output during compilation"
enabled in File > Preferences.
Arduino: 1.0.6 (Windows 7), Board: "Arduino Uno"
test4.1.ino:36: error: 'IRsend' does not name a type
test4.1.ino: In function 'void SendCode(int, byte)':
test4.1.ino:276: error: 'irsend' was not declared in this scope

I have tried compiling all versions from 3.7 to 4.1, and get same compiling errors in all of them. I am no coder, and google search has turned up no help to me. Would there be an issue with the irsend.h file? something I need to change? There is just way to much informaton here for me to find/remember and impliment.

Im using all library files from kman's great writeup on page 44.

If anyone could help or shed some light on my problem, my fish and I would be extremely greatfull . :icon_smil

I am using the OSEPP UNO R3 plus board.


----------



## kman

I know I just PM'd this, but just to make sure, all your libraries are in the right place?

On my Windows 7 (64bit) laptop, my libraries are here:










EDIT:

Also, you say you are running "4.1" but are you running the modified 4.1 that is pasted in my thread on page 44? I can't recall if there is a significant difference, but that would certainly reduce one variable. The code is in my post, and starts off with "Current Satellite LED+ Controller V4.1 - MODIFIED FOR KMAN v2".

EDIT 2:

Make sure your IR LED is on the right pin. I think it's discussed in the post, but there are options for Mega and options for Uno. Make sure both the code, and the pin, are correct for the Uno.


----------



## Dazzlin Dave™

Yes, my library location is same as yours :icon_smil
and yes, using this: "Current Satellite LED+ Controller V4.1 - MODIFIED FOR KMAN v2".

I am only using these librarys:
DS1307RTC
Iremote
LiquidCrystal
RTClib
Time
TimeAlarms
Dallas Temperature
OneWire
Do I need the others that you have showing?
/edit
are you saying that the ir led needs to be on the board atm?
/edit 2
I have nothing hooked to the uno right now..just my usb cable. I will feel pretty stupid if it has to be connedted to everyhing right now...


----------



## Dazzlin Dave™

Ok, have the resistor/led hooked to uno on pin 3 and grd..looking better 

now, just have this error message:
This report would have more information with
"Show verbose output during compilation"
enabled in File > Preferences.
Arduino: 1.0.6 (Windows 7), Board: "Arduino Uno"
test41.ino: In function 'void SendCode(int, byte)':
test41:276: error: 'class IRsend' has no member named 'sendNEC'


----------



## kman

Progress!

Can't hurt to try to download your IRremote library one more time, just in case. I don't know that it's the problem, but its a quick fix, if so. Be sure to exit and relaunch the Arduino software.


----------



## Dazzlin Dave™

kman said:


> Progress!
> 
> Can't hurt to try to download your IRremote library one more time, just in case. I don't know that it's the problem, but its a quick fix, if so. Be sure to exit and relaunch the Arduino software.


I'll give it another try...maybe 4th time is the charm...lol

I'm soldering the components up right now...boy, talk about rusty !
I have no idea what I will use for a case, so I'm putting 8in wires between the pcb and the rtc with 90deg headers on both..might make battery changes simpler.


----------



## kman

One more thought:

I know we have addressed the library folder, but make sure that you are saving the sketch under the Arduino folder in your document section.


----------



## Dazzlin Dave™

kman said:


> One more thought:
> 
> I know we have addressed the library folder, but make sure that you are saving the sketch under the Arduino folder in your document section.


That checks out ok as well.

I installed all software on another computer, and have exact same issues with all sketches from 3.7 and up.

Could it be an issue with my resistor? I'm using a 100 ohm rather than the 150 you listed. Led is RS 276-0143 .


----------



## Dazzlin Dave™

kman said:


> One more thought:
> 
> I know we have addressed the library folder, but make sure that you are saving the sketch under the Arduino folder in your document section.


That ckecks out ok.


----------



## Dazzlin Dave™

kman said:


> One more thought:
> 
> I know we have addressed the library folder, but make sure that you are saving the sketch under the Arduino folder in your document section.


I notice that the iremote files from the link have been updated recently. Can you share yours from when you compiled?


----------



## Dazzlin Dave™

Finally got the compiling done ! The gethub link has an updated file, and was causing the issue.I found an older ver online that works.

Now to figure out why the lcd isnt showing characters. At least the light is working


----------



## Dazzlin Dave™

Everything hooked up, lcd working, running tests now for the timers. 
Fingers crossed !


----------



## kman

Dazzlin Dave™ said:


> Finally got the compiling done ! The gethub link has an updated file, and was causing the issue.I found an older ver online that works.
> 
> Now to figure out why the lcd isnt showing characters. At least the light is working


Oh, wow, that's good to know.

What's the link to the updated file? I'll edit my post, if I can, and add the new one with the version that works.

Or, yeesh, maybe I should just package up all the libraries with the current, working versions and add that.


----------



## Dazzlin Dave™

kman said:


> Oh, wow, that's good to know.
> 
> What's the link to the updated file? I'll edit my post, if I can, and add the new one with the version that works.
> 
> Or, yeesh, maybe I should just package up all the libraries with the current, working versions and add that.


Heres the link to the file : https://www.pjrc.com/teensy/td_libs_IRremote.html

Everything is looking/working good as far as your alarms kman. I will adjust/change them as needed. Thanks for all the assistance last night !

And once again, thanks to everyone involved in this project. Your work is awesome and very much appreciated by me !
There is so much good code and help around here...woot :icon_smil


----------



## kman

Dazzlin Dave™ said:


> Heres the link to the file : https://www.pjrc.com/teensy/td_libs_IRremote.html
> 
> Everything is looking/working good as far as your alarms kman. I will adjust/change them as needed. Thanks for all the assistance last night !
> 
> And once again, thanks to everyone involved in this project. Your work is awesome and very much appreciated by me !
> There is so much good code and help around here...woot :icon_smil


No problem, thanks for the link! I added a pointer to it to help people with future issues. If I think about it, at some point, I'll zip up all the libraries I used, so working versions will all be collected in one place they can download right from here.


----------



## alpha1172

So i built one of these a bit ago, running version 3.6 works great!

I really wanted to try the version 1.0 by curt with the built in fading but it never controlled the light correctly. Did anyone ever get it to work correctly


----------



## kman

I never tried it. I have since moved over to the iAqua Lite for my smooth fading needs.  (which borrowed Curt's crossfade code, IIRC)

Now I have to decide if I'm going to sell my two controllers off, or break them down to play with on other projects...


----------



## alpha1172

U still using the sat +?


----------



## kman

alpha1172 said:


> U still using the sat +?


Me? I have both Sat+ and E-Series on my main tank.


----------



## alpha1172

oph awesome i think i have a few megas sitting around. gonna have to try it out id need to order that button/lcd shield though


----------



## gerwen

Another noob here.

Huge thanks for all the work done here. This is a fantastic project and something that pushed me into trying out the Arduino. I've been interested in it for years, but had no suitable project to try.

Now my problem:

I've got everything up and running with the version 3.6 sketch and a 16x2 LCD.

I'd like to get up to speed on the newer 4.1 based sketches, but it won't compile for me. I've gone over my libraries a few times to see if there was anything missing, but haven't had time to really dig into the problem. Here's the error:


Code:


 Arduino: 1.6.4 (Windows 7), Board: "Arduino Uno"

light_cont_4_1:59: error: variable 'arrCodes' must be const in order to be put into read-only section by means of '__attribute__((progmem))'
light_cont_4_1:94: error: 'prog_char' does not name a type
light_cont_4_1.ino: In function 'void SendCode(int, byte)':
light_cont_4_1:264: error: 'arrMSG' was not declared in this scope
Multiple libraries were found for "IRremote.h"

 Used: C:\Users\gerwen\Documents\Arduino\libraries\IRremote

 Not used: C:\Program Files (x86)\Arduino\libraries\RobotIRremote

 Not used: C:\Program Files (x86)\Arduino\libraries\IRremote

Multiple libraries were found for "LiquidCrystal.h"

 Used: C:\Users\gerwen\Documents\Arduino\libraries\LiquidCrystal

 Not used: C:\Program Files (x86)\Arduino\libraries\LiquidCrystal

variable 'arrCodes' must be const in order to be put into read-only section by means of '__attribute__((progmem))'

Has anyone seen this and solved it?

It seems to have a problem with progmem dumping into flash. If i put a const on that, then the 'does not have a type' errors persist.

Any ideas where i should be looking?


----------



## gerwen

I think my previous problem is still waiting mod approval to show. I've solved it.

Versions 4.1 and up of the controller sketches seem to require the Arduino Dev enviroment version 1.0.6, rather than the newer 1.6.x that I had been using. I haven't tested yet, but it compiled with no errors.

The older 3.6 (iirc) on the first page of the thread works just fine with the newer dev environment.

*edit in case my previous post never shows up*

I had the page one code version 3.6 working, but trying to compile 4.1, i threw a bunch of errors. The first of which complained about this line:

PROGMEM unsigned int arrCodes[32] = {0x3AC5, // 1 - Orange

saying that PROGMEM needed a CONST in order to push the array into flash

there were other errors, but that's the first one, so if you see something to that effect, check your environment version.


----------



## Curt_Planted

To answer the questions I saw earlier: 

There is a 42 step resolution on the controller for each color and the remote gives a double command for each step. This is why you can see a noticeable jump when you press the button, but the fades are so much smoother in the automated lighting modes like cloudy day ect.

I did get my lighting control code working. I have since added in additional code for having a different lighting schedule on the weekends. I found on weekends I tended to sleep in and would wake up to the lights out and would also want to show off the tank later in the evening when I had company and the lights would be out. If anyone wants the latest code, let me know.

Has anyone directly hardware hacked this light? One of my connectors got screwed up and fried the lighting controller in the light but the LED's are still good and the wiring clearly labeled. I may save myself $100 and hack it with some directly arduino controlled regulators. If anyone has done it, any tips?


----------



## jeffkrol

Curt_Planted said:


> Has anyone directly hardware hacked this light? One of my connectors got screwed up and fried the lighting controller in the light but the LED's are still good and the wiring clearly labeled. I may save myself $100 and hack it with some directly arduino controlled regulators. If anyone has done it, any tips?


AFAICT the controller circuit is just like this (I assume there are programming differences.. ):
http://www.lightingever.com/44-key-...troller.html?gclid=CPLlg_OAv8YCFYQ8aQodkbIJ7g









Since the LED's are run in constant voltage mode "external drivers" are unnecessary and really wouldn't do..

The above pictured controllers do PWM chopping directly on the feed voltage.
You just need to make sure the I/O specs are OK..
AS to Aduino direct control.. This might help:
http://www.elcojacobs.com/using-shiftpwm-to-control-led-strips-with-arduino/
https://learn.adafruit.com/rgb-led-strips/usage


----------



## Curt_Planted

Sweet! That's pretty cheap!! Thanks for the links and info! I'll look it over in more depth later.


----------



## Nathan Triplett

Hi to the thread (and forum in general).

First to the OP, thanks so much. This is awesome.
Second, I took your code and made my own version of it (attached below) which fades the lights smoothly on/off over a 24 hour period. And the fade up/down times, rates, and colors are all configurable. I thought I'd share the code in case it helps someone out.

My version is a stripped down as it comes. It doesn't have an LCD display (I may add that later) or IR receiver. It's just a 24 hour fader.

I'm running this version, but will probably clean up the code some next time I have a chance to dig in. I'll add some more comments and explain whats going on if people want as well. Cheers and thanks again for the guide.



Code:


#include <Wire.h>
#include <RTClib.h>
#include <Time.h>
#include <TimeAlarms.h>
#include <IRremote.h>

RTC_DS1307 RTC;
IRsend irsend;

int postDelay = 100;         // Delay after codes are sent
int randAnalogPin = 0;       // This needs to be set to an unused Analog pin, Used by ThunderStorm()

// Current Satellite+ IR Codes (NEC Protocol)
unsigned long codeHeader = 0x20DF; // Always the same

// Remote buttons listed left to right, top to bottom
unsigned int codeOrange = 0x3AC5;
unsigned int codeBlue = 0xBA45;
unsigned int codeRose = 0x827D;
unsigned int codePowerOnOff = 0x02FD;
unsigned int codeWhite = 0x1AE5;
unsigned int codeFullSpec = 0x9A65;
unsigned int codePurple = 0xA25D;
unsigned int codePlay = 0x22DD;
unsigned int codeRedUp = 0x2AD5;
unsigned int codeGreenUp = 0xAA55;
unsigned int codeBlueUp = 0x926D;
unsigned int codeWhiteUp = 0x12ED;
unsigned int codeRedDown = 0x0AF5;
unsigned int codeGreenDown = 0x8A75;
unsigned int codeBlueDown = 0xB24D;
unsigned int codeWhiteDown = 0x32CD;
unsigned int codeM1Custom = 0x38C7;
unsigned int codeM2Custom = 0xB847;
unsigned int codeM3Custom = 0x7887;
unsigned int codeM4Custom = 0xF807;
unsigned int codeMoon1 = 0x18E7;
unsigned int codeMoon2 = 0x9867;
unsigned int codeMoon3 = 0x58A7;
unsigned int codeDawnDusk = 0xD827;
unsigned int codeCloud1 = 0x28D7;
unsigned int codeCloud2 = 0xA857;
unsigned int codeCloud3 = 0x6897;
unsigned int codeCloud4 = 0xE817;
unsigned int codeStorm1 = 0x08F7;
unsigned int codeStorm2 = 0x8877;
unsigned int codeStorm3 = 0x48B7;
unsigned int codeStorm4 = 0xC837;

int wdticks = 0;
int rdticks = 0;
int bdticks = 0;
int gdticks = 0;
int wuticks = 0;
int ruticks = 0;
int buticks = 0;
int guticks = 0;


void SetAlarms()
{
  
  Alarm.alarmRepeat(6 ,0,0,MorningR);
  Alarm.alarmRepeat(8 ,0,0,MorningG);
  Alarm.alarmRepeat(9 ,0,0,MorningW);
  Alarm.alarmRepeat(9,50,0,MorningB);
  
  Alarm.alarmRepeat(17 ,0,0,EveningG);
  Alarm.alarmRepeat(18 ,0,0,EveningW);
  Alarm.alarmRepeat(19 ,0,0,EveningR);
  Alarm.alarmRepeat(19 ,0,0,EveningB);  

  Alarm.timerRepeat(15,timeNow);
  
}

void MorningW(){  Ramp(10800,43,WhiteUp, wuticks ); }
void MorningR(){  Ramp(18000,43,RedUp ,  ruticks ); }
void MorningB(){  Ramp(10800,43,BlueUp,  buticks ); }
void MorningG(){  Ramp( 7200,43,GreenUp, guticks ); }

void EveningW(){  Ramp(10800,43,WhiteDown, wdticks ); }
void EveningR(){  Ramp(10800,43,RedDown ,  rdticks ); }
void EveningB(){  Ramp(18000,43,BlueDown,  bdticks ); }
void EveningG(){  Ramp(10800,43,GreenDown, gdticks ); }

void Ramp(unsigned int seconds, unsigned int steps, void(*f)(), int & cntr ){
  Serial.println("starting Ramp");
  int interval = (int) ( 0.5 + float(seconds)/float(steps) );
  cntr = steps;
  Serial.print("enabling with interval");
  Serial.print(interval);
  Serial.print("and for");
  Serial.print(steps);
  Serial.print("steps");
  Serial.println();
  Alarm.timerRepeat(interval, f);
}

void setup()
{
  Wire.begin();
  RTC.begin();
  Serial.begin(9600);
  Serial.println("begin");

  if (! RTC.isrunning()) { 
    Serial.println("RTC Reset");
    timeNow();
    RTC.adjust(DateTime(__DATE__, __TIME__));
    timeNow();
  }  //Adjust to compile time  
  
  setSyncProvider(syncProvider);
  timeNow();
  
  SetAlarms();
 
}

void timeNow(){
  Serial.print(year(), DEC);
  Serial.print('/');
  Serial.print(month(), DEC);
  Serial.print('/');
  Serial.print(day(), DEC);
  Serial.print(' ');
  Serial.print(hour(), DEC);
  Serial.print(':');
  Serial.print(minute(), DEC);
  Serial.print(':');
  Serial.print(second(), DEC);
  Serial.println();
}

void loop()
{
  Alarm.delay(100); 
}

void SendCode (unsigned int code, byte numTimes, const char *sMessage){
  unsigned long irCode = (codeHeader << 16) + code; // Header is 2 bytes, shift all the way to left & add code to it
  
  for( int i = 0; i < numTimes; i++){
    irsend.sendNEC(irCode,32); // Send code
    Alarm.delay(postDelay);
  } 
}

void WhiteDown(){
  if( wdticks > 0 ){
    wdticks--;
    Serial.println("White Down");
    SendCode(codeWhiteDown, 1, "White Down");
  }else{
    Alarm.disable(Alarm.getTriggeredAlarmId());
    Alarm.free(Alarm.getTriggeredAlarmId());
    uint8_t x = Alarm.count();
    Serial.println(x);
  }
}

void WhiteUp(){
  if( wuticks > 0 ){
    wuticks--;
    Serial.println("White up");
    SendCode(codeWhiteUp, 1, "White Up");
  }else{
    Alarm.disable(Alarm.getTriggeredAlarmId());
    Alarm.free(Alarm.getTriggeredAlarmId());
    uint8_t x = Alarm.count();
    Serial.println(x);
  }
}

void RedDown(){
  if( rdticks > 0 ){
    rdticks--;
    Serial.println("Red Down");
    SendCode(codeRedDown, 1, "Red Down");
  }else{
    Alarm.disable(Alarm.getTriggeredAlarmId());
    Alarm.free(Alarm.getTriggeredAlarmId());
    uint8_t x = Alarm.count();
    Serial.println(x);
  }
}

void RedUp(){
  if( ruticks > 0 ){
    ruticks--;
    Serial.println("Red up");
    SendCode(codeRedUp, 1, "Red Up");
  }else{
    Alarm.disable(Alarm.getTriggeredAlarmId());
    Alarm.free(Alarm.getTriggeredAlarmId());
    uint8_t x = Alarm.count();
    Serial.println(x);
  }
}

void GreenDown(){
  if( gdticks > 0 ){
    gdticks--;
    Serial.println("Green Down");
    SendCode(codeGreenDown, 1, "Green Down");
  }else{
    Alarm.disable(Alarm.getTriggeredAlarmId());
    Alarm.free(Alarm.getTriggeredAlarmId());
    uint8_t x = Alarm.count();
    Serial.println(x);
  }
}

void GreenUp(){
  if( guticks > 0 ){
    guticks--;
    Serial.println("Green up");
    SendCode(codeGreenUp, 1, "Green Up");
  }else{
    Alarm.disable(Alarm.getTriggeredAlarmId());
    Alarm.free(Alarm.getTriggeredAlarmId());
    uint8_t x = Alarm.count();
    Serial.println(x);
  }
}

void BlueDown(){
  if( bdticks > 0 ){
    bdticks--;
    Serial.println("Blue Down");
    SendCode(codeBlueDown, 1, "Blue Down");
  }else{
    Alarm.disable(Alarm.getTriggeredAlarmId());
    Alarm.free(Alarm.getTriggeredAlarmId());
    uint8_t x = Alarm.count();
    Serial.println(x);
  }
}

void BlueUp(){
  if( buticks > 0 ){
    buticks--;
    Serial.println("Blue up");
    SendCode(codeBlueUp, 1, "Blue Up");
  }else{
    Alarm.disable(Alarm.getTriggeredAlarmId());
    Alarm.free(Alarm.getTriggeredAlarmId());
    uint8_t x = Alarm.count();
    Serial.println(x);
  }
}


time_t syncProvider(){ return RTC.now().unixtime(); }


----------



## vanish

Does anybody have one of these they want to sell? I'd love to have one for my tank. I have this light that does all these cool functions and don't use them!

I can handle programming just fine, but I'm terrible when it comes to assembling electronics.


----------



## Nathan Triplett

If it's mostly the soldering that worries you, you can build a simple version of this with no soldering required at all.

Grab the Arduino board, wires, resistors, LED, and a breadboard:
https://www.adafruit.com/products/193

You can get just those bits individually, but this link has it all and is easy.

You need a clock. The ones from adafruit are kits, but you can get one all soldered together already here:
Amazon.com: Holdding DS3231 AT24C32 IIC Module Precision Real Time Clock Memory Module Arduino/Precise Time Clock Module with a Temperature-compensated Crystal Oscillator: Computers & Accessories

After that it's just a matter of connecting everything into the breadboard together which is way easier than you might think. You just use those jumper wires to connect it all. The end result won't be as slick and small as the ones people solder together, but it will work. Mine is still all on a breadboard at the moment as I keep putting off soldering it all together.

If you want to go this route and don't want to bother with the fancy display, I'm also happy to give you nice schematics on how to connect it all up on your breadboard.


----------



## vanish

Thanks Nathan. Does that RTC just plug in to the breadboard? I'm not terribly concerned with bundling it up pretty as it will be out of view anyway.


----------



## aviphysics

Anyone looked into incorporating an ambient light sensor, to automatically adjust the light relative to the room light?

My plan will be to get a Current Satellite LED+ Pro for now; and work out the fancy arduino stuff, if I can find the time.

My end goal would be to have the light in the tank always slightly higher than room light level, to give the tank that nice gentle glow. Also maybe tracking the mean light level in order to adjust the response of the ambient light sensor, with the goal of maintaining an average daily light dosage for the plants (let me know if that last part doesn't make sense, and I can try to explain what I am thinking better.)


----------



## trogdan

aviphysics said:


> Anyone looked into incorporating an ambient light sensor, to automatically adjust the light relative to the room light?
> 
> My plan will be to get a Current Satellite LED+ Pro for now; and work out the fancy arduino stuff, if I can find the time.
> 
> My end goal would be to have the light in the tank always slightly higher than room light level, to give the tank that nice gentle glow. Also maybe tracking the mean light level in order to adjust the response of the ambient light sensor, with the goal of maintaining an average daily light dosage for the plants (let me know if that last part doesn't make sense, and I can try to explain what I am thinking better.)


So, I believe I have currently wired up my arduino, but it does not appear to control the Satellite LED+ Pro I just purchased. LCD works fine, and I can see the LED flashing in the phone camera.

I'm going to grab a IR receiver, and do a dump of the codes. In the mean-time, maybe my LFS will let me try the rig on one of their Current LED+ to make sure it works. I think they're interested in me building them one XD.


----------



## trogdan

*Code for LED+ Pro*

So, not sure why the sketch didn't work before, but the codes on the pro remote match the regular remote buttons, the functions have just changed. I did re-write the 3.6 version of the code so that it actually reflects what is on the remote, and this actually uses liquidcrystal_i2c, since it's easier to wire up.



Code:


///////////////////////////////////////////////////////////////////////
// Current Satellite LED+ Pro Controller  V3.6b+                     //
//   Indychus...Dahammer...Trogdan...mistergreen @ plantedtank.net   //
//   This code is public domain.  Pass it on.                        //
//   Confirmed on Arduino Duemilanove 1.0.6                          //
//   Req. Time, TimeAlarms, RTClib, IRremote                         //
///////////////////////////////////////////////////////////////////////
//
// This version uses Ken Shirriff's IRremote library to Rx/Tx the IR codes
// http://www.righto.com/2009/08/multi-protocol-infrared-remote-library.html
// 
// This code does NOT use PIN 13 on the Uno, as do previous versions
// Instead PIN 3, which is a PWM pin, is used. So you'll need to connect
// your LED to PIN 3 instead of PIN 13 for it to work.

// Install LCD per instructions at http://learn.adafruit.com/character-lcds/overview

#include <Wire.h>
#include <RTClib.h>
#include <Time.h>
#include <TimeAlarms.h>
#include <IRremote.h>
//#include <LiquidCrystal.h>
#include <LiquidCrystal_I2C.h>

RTC_DS1307 RTC;
IRsend irsend;
//LiquidCrystal lcd(7, 8, 9, 10, 11, 12);
LiquidCrystal_I2C lcd(0x3f, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // Set the LCD I2C address

/*********** BEGIN USER DEFINED VARIABLES ***********/
// DEBUG_IR adds the option to test the IR commands via the Arduino software's serial monitor
// You can send any value from 1 to 32 and it send the corresponding IR code
// The codes follow the remote controller, left to right, top to bottom
// e.g 1 = Orange, 2 = Blue, 21 = Moon1, etc
#define DEBUG_IR 0

int postDelay = 100;         // Delay after codes are sent
int randAnalogPin = 0;       // This needs to be set to an unused Analog pin, Used by ThunderStorm()

// Current Satellite+ IR Codes (NEC Protocol)
unsigned long codeHeader = 0x20DF; // Always the same

// Remote buttons listed left to right, top to bottom
// for the LED+ Pro Remote
unsigned int codeSetClock = 0x3AC5;
unsigned int codeOnTime = 0xBA45;
unsigned int codeOffTime = 0x827D;
unsigned int codePowerOnOff = 0x02FD;
unsigned int codeHour = 0x872D;
unsigned int codeMinute = 0x9A65;
unsigned int codeEnter = 0xA25D;
unsigned int codeResume = 0x22DD;
unsigned int codeOrange = 0x2AD5; 
unsigned int codeYellow = 0x1AE5;
unsigned int codeBlue = 0x926D; 
unsigned int codeViolet = 0x12ED; 
unsigned int codeRedUp = 0x0AF5;
unsigned int codeGreenUp = 0x8A75;
unsigned int codeBlueUp = 0xB24D;
unsigned int codeWhiteUp = 0x32CD;
unsigned int codeRedDown = 0x38C7;
unsigned int codeGreenDown = 0xB847;
unsigned int codeBlueDown = 0x7887;
unsigned int codeWhiteDown = 0xF807;
unsigned int codeM1Custom = 0x18E7;
unsigned int codeM2Custom = 0x9867;
unsigned int codeDaylight = 0x58A7;
unsigned int codeMoonlight = 0xD827;
unsigned int codeMoon1 = 0x28D7;
unsigned int codeMoon2 = 0xA857;
unsigned int codePartlyCloudy = 0x6897;
unsigned int codeDawnDusk = 0xE817;
unsigned int codeMostlyCloudy = 0x08F7;
unsigned int codeStorm1 = 0x8877;
unsigned int codeStorm2 = 0x48B7;
unsigned int codeEveningClouds = 0xC837;

void SetAlarms()
{
  // Set up your desired alarms here
  // The default value of dtNBR_ALARMS is 6 in Alarms.h.
  // This code sets 12 alarms by default, so you'll need to change dtNBR_ALARMS to 12 or more
  Alarm.alarmRepeat(7,00,0, DawnDusk);
  Alarm.alarmRepeat(9,00,0, PartlyCloudy);     // (HR,MIN,SEC,FUNCTION)
  Alarm.alarmRepeat(21,58,0, Daylight);
  Alarm.alarmRepeat(15,00,0, PartlyCloudy);
  Alarm.alarmRepeat(19,00,0, DawnDusk);
  //Alarm.alarmRepeat(21,00,0, Moon2);

  // Comment these out if you don't want the chance of a random storm each day
  Alarm.alarmRepeat(12,00,00, ThunderStorm);
  ThunderStorm();  // Sets up intial storm so we don't have wait until alarm time
}
/************* END USER DEFINED VARIABLES *************/

void setup()
{
  Wire.begin();
  RTC.begin();
  lcd.begin(20, 4);
  Serial.begin(9600);
  //Serial.println(freeRam());

  if (! RTC.isrunning()) { 
    Serial.println("RTC Error");
    RTC.adjust(DateTime(__DATE__, __TIME__));
  }  //Adjust to compile time


  setSyncProvider(syncProvider);     //reference our syncProvider function instead of RTC_DS1307::get()

  Alarm.timerRepeat(900, digitalClockDisplay);  // Display the time every 15 minutes
  digitalClockDisplay();
  SetAlarms();
  Serial.print("SRAM : ");          //un-comment these line to check available SRAM
  Serial.println(freeRam());
}   

void loop()
{
#ifdef DEBUG_IR 
  if (Serial.available() > 0) {
    delay(5); //Wait for transmission to finish
    CurrentCMDs(SerialReadInt());
  }
#endif
  Alarm.delay(100); 
  // Service alarms & wait (msec)
  lcdClockDisplay();
}

time_t syncProvider()
{
  //this does the same thing as RTC_DS1307::get()
  return RTC.now().unixtime();
}

void ThunderStorm ()
{ 
  // Schedules a storm between 1 & 9 in the evening
  // It sets Storm2, followed by Cloud2 or DawnDusk or Moon2, depending on when the storm is over
  randomSeed(analogRead(randAnalogPin));  // Generate random seed on unused pin
  byte RH = random(23);                   // Randomizer for thunderstorm
  byte RM = random(59);
  byte RS = random(59);
  byte TSDurationH = random(2);
  byte TSDurationM = random(59);

  if (RH <= 12)
  {
    Serial.println("No storm today");
    lcd.setCursor(0,1);
    lcd.print("No storm today");
    return;
  }

  if (RH > 12)                             // If random value is after 1 pm, allow storm
  {
    Alarm.alarmOnce(RH,RM,RS,Storm2);
    Serial.print("Next Storm: ");
    Serial.print(RH);
    printDigits(RM);
    printDigits(RS);
    Serial.print("   ");
    Serial.print("Duration = ");
    Serial.print(TSDurationH);
    printDigits(TSDurationM);
    Serial.println();
    lcd.setCursor(0,1);
    lcd.print("Next Storm: ");
    lcdHRdigits(RH);
    lcdDigits(RM);
  }

  if ((RH + TSDurationH) < 19)   // Return to Cloud2 if storm ends between 1-7pm
  {
    Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,PartlyCloudy);
  }
  else if ((RH + TSDurationH) < 21)  // Return to DawnDusk if storm ends between 7-9pm
  {
    Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,DawnDusk);
  }
  else                                       // Return to Night2 if storm ends after 9pm
  {
    Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,Moon2);
  }
}


void digitalClockDisplay()          // Digital clock
{ 
  Serial.print("Time = ");
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.println(); 
}


void lcdClockDisplay()  
{
  lcd.setCursor(0,0);
  lcdHRdigits(hour());
  lcdDigits(minute());
}

void printDigits(int digits)        // Add :
{
  Serial.print(":");
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits);
}

void lcdDigits(int digits)        // Add :
{
  lcd.print(":");
  if(digits < 10)
    lcd.print('0');  
  lcd.print(digits);
}
void lcdHRdigits(int HRdigits)        // Preface hour with 0
{
  if(HRdigits < 10)
    lcd.print('0');  
  lcd.print(HRdigits);
}

#ifdef DEBUG_IR
int SerialReadInt()
{
  int i, numAva;
  char inBytes[3];                  // Array to hold the bytes
  char * inBytesPtr = &inBytes[0];  // Pointer to the first element of the array

  numAva = Serial.available();    // Read number of input bytes
  if (numAva > 2)
    numAva = 2;                   // Only allow 2 characters to prevent overflow

  for (i=0; i<numAva; i++)        // Load input bytes into array
    inBytes[i] = Serial.read();
  inBytes[i] =  '\0';             // Put NULL character at the end
  return atoi(inBytesPtr);        // Call atoi function and return result
}

void CurrentCMDs (int cmd)
{
  switch (cmd)
  {
  case 1:
    SetClock();
    break;
  case 2:
    OnTime();
    break;
  case 3:
    OffTime();
    break;
  case 4:
    PowerOnOff();
    break;
  case 5:
    Hour();
    break;
  case 6:
    Minute();
    break;
  case 7:
    Enter();
    break;
  case 8:
    Resume();
    break;
  case 9:
    Orange();
    break;
  case 10:
    Yellow();
    break;
  case 11:
    Blue();
    break;
  case 12:
    Violet();
    break;
  case 13:
    RedUp();
    break;
  case 14:
    GreenUp();
    break;
  case 15:
    BlueUp();
    break;
  case 16:
    WhiteUp();
    break;
  case 17:
    RedDown();
    break;
  case 18:
    GreenDown();
    break;
  case 19:
    BlueDown();
    break;
  case 20:
    WhiteDown();
    break;
  case 21:
    M1Custom();
    break;
  case 22:
    M2Custom();
    break;
  case 23:
    Daylight();
    break;
  case 24:
    Moonlight();
    break;
  case 25:
    Moon1();
    break;
  case 26:
    Moon2();
    break;
  case 27:
    PartlyCloudy();
    break;
  case 28:
    DawnDusk();
    break;
  case 29:
    MostlyCloudy();
    break;
  case 30:
    Storm1();
    break;
  case 31:
    Storm2();
    break;
  case 32:
    EveningClouds();
    break;
  default:
    Serial.println("Invalid Choice");
  }
}
#endif

void SendCode (unsigned int code, byte numTimes, const char *sMessage)
{
  unsigned long irCode = (codeHeader << 16) + code; // Header is 2 bytes, shift all the way to left & add code to it

  for( int i = 0; i < numTimes; i++)
  {
    irsend.sendNEC(irCode,32); // Send code
    Alarm.delay(postDelay);
  }

  Serial.println(sMessage);  // Print message
  lcd.setCursor(6,0);
  lcd.print(sMessage);
  for(byte i = sizeof(sMessage); i <= 14; i++)
    lcd.print(" ");
  digitalClockDisplay();
}

void SetClock()
{
  SendCode(codeSetClock, 2, "SetClock");
}

void OnTime()
{
  SendCode(codeOnTime, 2, "OnTime");
}

void OffTime()
{
  SendCode(codeOffTime, 2, "OffTime");
}

void PowerOnOff()
{
  SendCode(codePowerOnOff, 1, "Power On/Off");
}

void Hour()
{
  SendCode(codeHour, 2, "Hour");
}

void Minute()
{
  SendCode(codeMinute, 2, "Minute");
}

void Enter()
{
  SendCode(codeEnter, 2, "Enter");
}

void Resume()
{
  SendCode(codeSetClock, 2, "Resume");
}

void Orange()
{
  SendCode(codeOrange, 2, "Orange");
}

void Yellow()
{
  SendCode(codeYellow, 2, "Yellow");
}

void Blue()
{
  SendCode(codeBlue, 2, "Blue");
}

void Violet()
{
  SendCode(codeViolet, 2, "Violet");
}

void RedUp()
{
  SendCode(codeRedUp, 1, "Red Up");
}

void GreenUp()
{
  SendCode(codeGreenUp, 1, "Green Up");
}

void BlueUp()
{
  SendCode(codeBlueUp, 1, "Blue");
}

void WhiteUp()
{
  SendCode(codeWhiteUp, 1, "White Up");
}

void RedDown()
{
  SendCode(codeRedDown, 1, "Red Down");
}

void GreenDown()
{
  SendCode(codeGreenDown, 1, "Green Down");
}

void BlueDown()
{
  SendCode(codeBlueDown, 1, "Blue Down");
}

void WhiteDown()
{
  SendCode(codeWhiteDown, 1, "White Down");
}

void M1Custom()
{
  SendCode(codeM1Custom, 2, "Custom Mix 1");
}

void M2Custom()
{
  SendCode(codeM2Custom, 2, "Custom Mix 2");
}

void Daylight()
{
  SendCode(codeDaylight, 2, "Daylight");
}

void Moonlight()
{
  SendCode(codeMoonlight, 2, "Moonlight");
}

void Moon1()
{
  SendCode(codeMoon1, 2, "Moonlight 1");
}

void Moon2()
{
  SendCode(codeMoon2, 2, "Moonlight 2");
}

void PartlyCloudy()
{
  SendCode(codePartlyCloudy, 2, "Partly Cloudy");
}

void DawnDusk()
{
  SendCode(codeDawnDusk, 2, "Dawn/Dusk");
}

void MostlyCloudy()
{
  SendCode(codeMostlyCloudy, 2, "Mostly Cloudy");
}

void Storm1()
{
  SendCode(codeStorm1, 2, "Thunderstorm 1");
}

void Storm2()
{
  SendCode(codeStorm2, 2, "Thunderstorm 2");
}

void EveningClouds()
{
  SendCode(codeEveningClouds, 2, "Evening Clouds");
}

int freeRam () {
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}


----------



## Vinster8108

I just finished a very similar project using a raspberry pi. I like how everything is 100% customizable now, possibilities are endless:
http://www.plantedtank.net/forums/1...m-24-7-led-light-cycle-any-led-ir-remote.html


----------



## liz.eidsness

I have used this code to implement on an Adafruit feather HUZZAH (wifi). I have soldered the components to a feather wing proto board, and plan to have a case 3D printed (adafruit supplies the 3D print files). I will one day add a display, but I don't have the component yet, and having a status webpage is a good alternative. Apart from the status page, you can send it a command to change the light remotely. It gets the time from an NTP server. I will post when I have it cleaned up a bit and it's a bit more stable. 

I have noticed that sometimes the light doesn't change, but the IR code has been sent. Is this why certain codes are sent twice? I was trying to figure out the meaning behind this part of SendCode function.

Thanks for posting this - it's great for this light. I was really frustrated that I couldn't use my regular wall timer when I upgraded to LED. 

Liz


----------



## Dahammer

Blast from the past. Evening folks, was just looking through some old files, came across this code, and thought give a report. My light and Arduino controller are still going strong, 7 years later. I did have to replace the power supply on the light a months back, but other than that the setup has not missed a beat.


----------

