The Planted Tank Forum - View Single Post - Guide: Arduino based LED controller for Current Satellite LED+
View Single Post
Old 08-30-2013, 11:05 PM   #339
Caver
Algae Grower
 
PTrader: (0/0%)
Join Date: Jun 2013
Location: Austin TX
Posts: 56
Default

I'll have to take a look. I may have accidentally hosed something...wouldn't be the first time...

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);}
__________________
--
Tom
Caver is offline   Reply With Quote