The Planted Tank Forum banner

Improved Arduino LED lighting code

109K views 223 replies 51 participants last post by  Lingwendil 
#1 · (Edited)
I wasn't happy with any existing Arduino code for LED control so I wrote my own. I think it's quite nice though it needs polishing and probably some features added. It does light timing, sunrise/sunset fading and all that jazz.

Some improvements over existing code:

  • Shorter and simpler
  • Accurately set clock from a script on your PC. You don't need to modify any code or upload any sketches!
  • Much, much smoother dimming, especially at the low end (due to a combination of higher resolution PWM output and every-second dimming adjustment instead of every-minute).
  • Higher resolution PWM also means you can dim your LEDs down to a lower intensity than before if your driver is capable. I've found I can dim my LEDs down far enough to be used as moonlights.
  • Flexible timing logic. You can do anything you want (with minor modifications) so long as it's on a 24hr cycle.
There are also some limitations:

  • Your Arduino needs to have either a DS3231(eg: ChronoDot) or DS1307 Real Time Clock chip connected via I2C (because I have one of those). These are probably the two most common RTC chips.
  • It only provides 2 PWM outputs (because I only have 2 LED strings). Of course you can modulate as many strings as you want from a single PWM signal but they'll modulate in unison.
  • It should work with the Arduino Duimilanove/Uno and probably any other micro-controller that uses the Atmega168/328 chip. In particular it will not work as-is with the Arduino Mega due to a difference in the onboard timers.
  • It depends on a few libraries but I've bundled them up in a download link below.
  • Only compiles with versions 0022/0023 of the Arduino IDE, not version 1.0 (yet!)

It works great for me. I'd love feedback, error reports, suggestions, free beers, etc. Post in the thread or PM me.

I hope this will be useful to people! :thumbsup:

Get the code:
Latest version
Libraries bundle

Installing libraries:
The libraries that don't come with the Arduino environment by default are bundled in the zipfile above. Just extract that zip into the libraries folder inside your sketchbook folder and then restart the Arduino IDE. If libraries doesn't exist, create it first.

Until I get around to writing documentation just post or PM installation questions/issues and I'll try to help.

Latest Version:
05/2014 -- O2surplus has made some significant improvements to this code, adding LCD and temp sensor support, please see his posts later on in this thread for the code.
 
See less See more
#125 · (Edited)
Getting it to work on Ver 1 Arduino

It's not much of a contribution but I'm sure this would save you a lot time rewriting the code.

You mention that this code is pre ver 1.0 Arduino

I have managed to compile it get it working on a Ver 1.01 Arduino UNO Rev3 with changing one Library Time.h

Thanks for the great code hope this helps to the cause.
 

Attachments

#126 ·
It's not much of a contribution but I'm sure this would save you a lot time rewriting the code.

You mention that this code is pre ver 1.0 Arduino

I have managed to compile it get it working on a Ver 1.01 Arduino UNO Rev3 with changing one Library Time.h

Thanks for the great code hope this helps to the cause.

Thanks A Lot! That's a big help for people running Arduino 1.0l
 
#127 · (Edited)
Getting it to work with RGB

Ok I have been at this for a week now, I too was looking at getting my RGB lights running off of this simple but wonderful code.

But looking into it getting it to run on the Uno was the easy part but getting 3 CH timer is an other story entirely and I'm not programer I'm more of a band aid hacker lets say.

Well to explain it shortly the Arduino has 3 Timer, Timer0, Timer1, Timer2,
each one of these timers have 2 CH with a variety feature for each.
It polls an compares the millis() and Freq ch A, B. The timers and says hey you need to Turn on, or off, or Fade, ext.. but each timer has 2 Registers to keep track of the time.

And see this is a great feature and what makes this code great, Is its not taking up all your clock times(CPU) while trying to keep track of the time and do other things.

Like keeping track of Temps, or controlling your 3 speed pump or turing them on and off ect..

I tried to add one of the other timer from pin 11 but I don't have all the programming knowledge to get it to work properly, I mean it was working sort of.

So I'm taking the easy route and using the Arduino Mega 2560 it adds 3 more 16 Bit Timers.

Making it easy to modify the code to work withe RGB lights.
Plus if you really wanted too you could use both Timer1 for your main lights and Timer3 for your complementary RGB Lights.

I am plaining to adding lots more features I just need to mash all this code a have together like setting the time from the LCD Unit it self with 2 Buttons or a rotary encoder, Alarms to tell you its time to do Water changes or clean your filter.

If any one is interested when I get it all done I can post it up.

Cheers! Happy coding..
Oh here's the Timer3 file for the Mega I have it using Pin 5,3,2
And you need to add or change this code.
I'm still trying to debug the Error so if you find one or see something wrong let me know.

Code:
#include <TimerThree.h>
const int kChan0Pin = 5; // Channel 0 Pin
const int kChan1Pin = 2; // Channel 1 Pin
const int kChan2Pin = 3; // Channel 2 Pin
Code:
const int kDayState[] = { 1023, 1023, 1023 }; // daytime LED state
const int kNightState[] = { 0, 0, 0 }; // nighttime LED state
Code:
int state_chan1, state_chan2, state_chan3;
Code:
void fader(long start_time, const int start_state[], const int end_state[], int out[3]) {

  float per_second_delta_0 = (float) (end_state[0]-start_state[0])/kFadeDuration;
  float per_second_delta_1 = (float) (end_state[1]-start_state[1])/kFadeDuration;
  float per_second_delta_2 = (float) (end_state[2]-start_state[2])/kFadeDuration;
  
  long elapsed = ctr-start_time; // Calculate the elapsed Time

  out[0] = start_state[0] + per_second_delta_0 * elapsed;
  out[1] = start_state[1] + per_second_delta_1 * elapsed;
  out[2] = start_state[2] + per_second_delta_2 * elapsed;
}
Code:
void set_state(const int state[]) {
  if (state[0] >= 0 && state[0] <= 1023) {
    Timer3.setPwmDuty(kChan0Pin, state[0]);
    state_chan1 = state[0]; }
  if (state[1] >= 0 && state[1] <= 1023) {
    Timer3.setPwmDuty(kChan1Pin, state[1]);
    state_chan2 = state[1]; }
  if (state[2] >= 0 && state[2] <= 1023) {
    Timer3.setPwmDuty(kChan2Pin, state[2]);
    state_chan3 = state[2]; }
}
 

Attachments

#128 ·
so uh... does anyone want to put together a shopping list via amazon.com? :D

I'd love to get started on this to eventually control the lights and co2 of a 125g. I've heard of the arduino and knew it was being used for fun stuff like this. I'm a web developer by trade, so this hardware stuff is quite confusing to me, but i can definitely dig into the code.
 
#129 ·
#133 ·
You have to write code for this but the time change date is arbitrary based on the government. It's easier just to change the time manually.


I need help understanding how an n-fet works i have chosen one ( irf520) it can handel alot of power about 10 amp or so, but why is it getting so hot when it is only handling a load of 1.0 amp?
I have mounted it on a large heat sink because i want to make a salt water one day and i may then nead 6amp pr. Channel!!
But how hot vil it be at that load ?
It's natural for components with large amps to get warm/hot but not boiling hot. Check your circuit for shorts if it gets really hot and the circuit is broken.
 
#132 · (Edited)
I need help understanding how an n-fet works i have chosen one ( irf520) it can handel alot of power about 10 amp or so, but why is it getting so hot when it is only handling a load of 1.0 amp?
I have mounted it on a large heat sink because i want to make a salt water one day and i may then nead 6amp pr. Channel!!
But how hot vil it be at that load ?

Is it becurse i have a resistor betwin Arduino pin 9 and gaten on the fet so it dosent open totely?? Is it nessasery with a resistor?
 
#136 ·
The IRF520 has a max power dissipation at 60w and i have only 10 watt pr channel it is the reason i am concerned that it is hot not much but 45 degrees celsius!!
The IRF520 Has a gate threshold voltage at 4 volts max and the arduino put out 4,8 voltage so i need a resistor between arduino and gate which is 10 ohm or am i wrong ????
 
#143 ·
That has been a common request. I may end up adding a binary (just off or 5V) moonlight output eventually, but I am hesitant to add too much complexity. In many ways, the code is already more complex than I want it to be -- the plan was to have a barebones lighting system that was simple enough behind the scenes that by looking at it, reasonably techie people could see how to add their own things (CO2 control, temp, etc) where needed.

In any case, if I had a single moonlight I would just rig it to be on all of the time. Much easier and you won't notice it on during the day anyway with the normal lighting active.
 
#145 ·
Hey Sink, so i'm really liking the look of your code and would like to use it on my new pico reef. However, I need 6 channels to control the fixture. How would i go about adding 4 more outputs? It seems to me i'd need to add the libraies for the other 2 timers, but will using different timers effect my dimming between channel's? I'm still very new to arduino, (this is my very first project) so i would very much appreciate your help. Thanks!
 
#146 ·
The TimerOne library is only there to provide high-resolution output -- if coarser control is fine for your other channels you don't need libraries for them. Dimming for other channels would be the same, except your range would only be 8 bits (0-255) so you'd have to figure the math for that out in the code. As far as how the timers interrelate to each other...I'm not positive :) It probably varies by device, but Google should be able to provide an answer there. It may be that you are limited to how many PWM outputs you can sent at once using the HW timers.

For making the changes to the code, you'd just basically duplicate what I've done for the two channels for the other four, creating appropriate variables and adding lines where needed in the dimming code etc. It's not that bad.
 
#147 ·
So if I added the libraries would I achieve higher resolution outputs on the other 4? What is the resolution of the outputs as the code stands? Could I perhaps add an I2C pwm breakout to my contoller such as this one? http://adafruit.com/products/815
I'd like to retain the higher resolution on all channels if possible. Also, I am using meanwell LDD-L's to drive my LED's if it makes any difference.
 
#148 ·
So if I added the libraries would I achieve higher resolution outputs on the other 4?
Theoretically, but it is awkward. For example, messing with the settings for Timer0 will affect built in functions like delay() and millis() because they get their timing from it under the hood. There might be libraries to compensate for this, but I haven't really looked.
What is the resolution of the outputs as the code stands?
Normally Arduino PWM outputs are 8 bits. The TimerOne library allows 10 bits resolution on its two outputs.
Could I perhaps add an I2C pwm breakout to my contoller such as this one?
That thing looks sweet! 16 independent outputs with 12 bits of resolution. You'd have to make some changes to the code to use it, but ultimately with 6 (or more) outputs the code would be simpler using something like that. Plus you wouldn't have to deal with the libraries. Do note that at some point the limiting factor for how precisely you can set light level will be the driver itself (and how it interprets the PWM signal) and not the resolution of the PWM output itself.

I'd like to retain the higher resolution on all channels if possible. Also, I am using meanwell LDD-L's to drive my LED's if it makes any difference.
The driver does certainly make a difference, but I couldn't speak as to how well the PWM control is for that driver. The control on some drivers is quite nonlinear at the low end (< 10% of light output) and can be inconsistent for fast PWM signals.
 
#149 ·
Sounds like I need to order that breakout! I already have 2 slave devices on my I2C, I would think adding this would be alright, correct me if I'm wrong. These are the current devices:
http://www.adafruit.com/products/264
http://www.adafruit.com/product/772
Better yet, I can now get full control on the refugium lights as well! 7 channels over a 4 gallon, gotta love DIY!

I was originally planning on the CAT's, then I got talking to a customer while testing his water. He peaked my interest when he told me about the LDD's. Then brought me 2 of the PCB's he had for them as a Christmas gift! From what I've read they have the best of dimming accuracy so I don't think it should be an issue.
 
#151 ·
It is a convention in C to name constants starting with a lowercase k. It helps distinguish later on things that should be constant from things that can be safely reassigned. It is not a requirement though, merely a suggested style.

I would think you will have no issues with multiple devices on I2C.

Cheers.
 
#154 ·
Sorry for the delay in replying!

The RTC communicates over I2C, so you just use whatever pins those are on your board. On the Duemilanove that I have those are A4 and A5. It might be different on the Uno. There isn't anything special about my code in this respect, you hook up I2C the same way you would for any Arduino device. For just one I2C device (the RTC) you'd connect the SDA on the RTC to the SDA on the Arduino, and same for the SCL ports.
 
#156 ·
Hello everyone. I want to thank you all for taking the time to put this information on the forum. I do have a question. Has anyone used the arduino to control LED fixtures made by build my led.com? From what I understand, they are built with LED Drivers built into the fixture. Further more, they can ship with a prefab APEX dimming cable. It appears to be a standard RJ45 connection on the end that would plug into the controller. Would it be possible do use the Arduino with that setup to control the LEDs? if so what would I need?

Thank you in advance for your response.
 
#157 ·
The Apex controller outputs a 0-10V analog signal to dim the leds. So chances are good that the dimmable drivers in the BuildMyLed fixture are designed to accept that type of dimming control signal. The Arduino outputs a 5V PWM signal, so it won't be directly compatible with the drivers in the BML fixture.
If you have your heart set on using the arduino as your controller, just purchase LED drivers that accept a 5V PWM signal directly, and you'll be set.Checkout the Meanwell LDD-L & LDD-H series of drivers. They will work well for your application, and they're cheap too. Here's a link-LDD-1000H | Mean-Well LDD-1000H | USA Warehouse
 
#161 ·
I2C freezing

Hey Sink, wanted to see if you might be able to advise me on my project here. I have your code working beautifully with the Adafruit 16-channel 12-bit pwm driver. However, after integrating it into my full control code i'm having an issue. The code will perform as expected right up to the dimming stage, when it hits my LCD goes blank, LED's flash, and the whole I2C bus stops dead. It seems to me I may have to much going on with the I2C. I'm using an UNO R3 with a DS1307, the Adafruit PWM controller, and their I2C 16x2 LCD on the bus. Please let me know if you have any idea how I might fix this issue. Thank you.


#include <Wire.h>
#include "RTClib.h"
#include <SoftwareSerial.h>
#include <OneWire.h>
#include <Adafruit_PWMServoDriver.h>
#include <Adafruit_MCP23017.h>
#include <Adafruit_RGBLCDShield.h>

#define OFF 0x0
#define WHITE 0x7
#define rxpin 3
#define txpin 2

RTC_DS1307 RTC;
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();

const long kTurnOn = 39000; // 37800time dawn begins - 0900hrs
const long kTurnOff = 59000; // 70200 time sunset begins - 2100hrs
const int kDayState[] = { 3000, 4000, 4000, 4000, 0, 0, 2000, 3000 }; // daytime LED state
const int kNightState[] = { 0, 4, 45, 4, 2000, 0, 0, 4 }; // nighttime LED stat
const long kFadeDuration = 0;
long ctr;
int state_chan1, state_chan2, state_chan3, state_chan4, state_chan5, state_chan6, state_chan7, state_chan8;

Adafruit_RGBLCDShield lcd = Adafruit_RGBLCDShield();
SoftwareSerial myserial(rxpin, txpin);
int DS18S20_Pin = 4;
OneWire ds(DS18S20_Pin);

int floatstate = 0;

long phReadInterval = 10000;
long previousMillisPH = 0;
int PHState = HIGH;
float PH_Val;

void fader(long start_time, const int start_state[], const int end_state[], int out[2]) {

float per_second_delta_0 = (float) (end_state[0]-start_state[0])/kFadeDuration;
float per_second_delta_1 = (float) (end_state[1]-start_state[1])/kFadeDuration;
float per_second_delta_2 = (float) (end_state[2]-start_state[2])/kFadeDuration;
float per_second_delta_3 = (float) (end_state[3]-start_state[3])/kFadeDuration;
float per_second_delta_4 = (float) (end_state[4]-start_state[4])/kFadeDuration;
float per_second_delta_5 = (float) (end_state[5]-start_state[5])/kFadeDuration;
float per_second_delta_6 = (float) (end_state[6]-start_state[6])/kFadeDuration;
float per_second_delta_7 = (float) (end_state[7]-start_state[7])/kFadeDuration;

long elapsed = ctr-start_time;

out[0] = start_state[0] + per_second_delta_0 * elapsed;
out[1] = start_state[1] + per_second_delta_1 * elapsed;
out[2] = start_state[2] + per_second_delta_2 * elapsed;
out[3] = start_state[3] + per_second_delta_3 * elapsed;
out[4] = start_state[4] + per_second_delta_4 * elapsed;
out[5] = start_state[5] + per_second_delta_5 * elapsed;
out[6] = start_state[6] + per_second_delta_6 * elapsed;
out[7] = start_state[7] + per_second_delta_7 * elapsed;
}

long seconds_since_midnight() {
DateTime now = RTC.now();
long hr = now.hour();
long min = now.minute();
long sec = now.second();
long total = hr * 3600 + min * 60 + sec;
return total;
}

void set_state(const int state[]) {
if (state[0] >= 0 && state[0] <= 4096) {
pwm.setPWM(0, 0, state[0]);
state_chan1 = state[0]; }
if (state[1] >= 0 && state[1] <= 4096) {
pwm.setPWM(1, 0, state[1]);
state_chan2 = state[1]; }
if (state[2] >= 0 && state[2] <= 4096) {
pwm.setPWM(2, 0, state[2]);
state_chan3 = state[2]; }
if (state[3] >= 0 && state[3] <= 4096) {
pwm.setPWM(3, 0, state[3]);
state_chan4 = state[3]; }
if (state[4] >= 0 && state[4] <= 4096) {
pwm.setPWM(4, 0, state[4]);
state_chan5 = state[4]; }
if (state[5] >= 0 && state[5] <= 4096) {
pwm.setPWM(5, 0, state[5]);
state_chan6 = state[5]; }
if (state[6] >= 0 && state[6] <= 4096) {
pwm.setPWM(6, 0, state[6]);
state_chan7 = state[6]; }
if (state[7] >= 0 && state[7] <= 4096) {
pwm.setPWM(7, 0, state[7]);
state_chan8 = state[7]; }
}

void determine_state() {
if ( ctr >= 0 && ctr < kTurnOn ) { // night
set_state(kNightState);
} else if ( ctr >= kTurnOn && ctr <= (kTurnOn+kFadeDuration) ) { // sunrise
int foo[2];
fader(kTurnOn, kNightState, kDayState, foo);
set_state(foo);
} else if ( ctr > (kTurnOn+kFadeDuration) && ctr < kTurnOff ) { // day
set_state(kDayState);
} else if ( ctr >= kTurnOff && ctr <= (kTurnOff+kFadeDuration) ) { // sunset
int foo[2];
fader(kTurnOff, kDayState, kNightState, foo);
set_state(foo);
} else if ( ctr > (kTurnOff+kFadeDuration) && ctr < 86400 ) { // night
set_state(kNightState);
}
}

float getTemp(){
//returns the temperature from one DS18S20 in DEG Celsius

byte data[12];
byte addr[8];

if ( !ds.search(addr)) {
//no more sensors on chain, reset search
ds.reset_search();
return -1000;
}

if ( OneWire::crc8( addr, 7) != addr[7]) {
Serial.println("CRC is not valid!");
return -1000;
}

if ( addr[0] != 0x10 && addr[0] != 0x28) {
Serial.print("Device is not recognized");
return -1000;
}

ds.reset();
ds.select(addr);
ds.write(0x44,1); // start conversion, with parasite power on at the end

byte present = ds.reset();
ds.select(addr);
ds.write(0xBE); // Read Scratchpad


for (int i = 0; i < 9; i++) { // we need 9 bytes
data = ds.read();
}

ds.reset_search();

byte MSB = data[1];
byte LSB = data[0];

float tempRead = ((MSB << 8) | LSB); //using two's compliment
float TemperatureSum = tempRead / 16;

return TemperatureSum;

}

void getPHvalue(){
unsigned long currentMillisPH = millis();

if(currentMillisPH - previousMillisPH > phReadInterval){
previousMillisPH = currentMillisPH;
if(PHState == HIGH){
PHState = LOW;
}
else {
PHState = HIGH;
myserial.print("r\r");
while(myserial.available() > 0){
PH_Val = myserial.parseFloat();
if(myserial.read() == '\r');
}
}
}
}

void setup() {
myserial.begin(38400);
RTC.begin();
lcd.begin(16,2);
pinMode(5, OUTPUT);
pinMode(6, OUTPUT);
pinMode(7, INPUT);
pwm.begin();
pwm.setPWMFreq(800);
ctr = seconds_since_midnight();
Serial.print(ctr);
}

void loop(){
ctr = seconds_since_midnight();
determine_state();
floatstate = digitalRead(7);
if (floatstate == HIGH) {
digitalWrite(6, HIGH);
delay(10000);
digitalWrite(6, LOW);
}
else {
digitalWrite(6, LOW);
}

if ( ctr >= 0 && ctr < 26000 ) {
lcd.setBacklight(OFF);
} if ( ctr >= 0 && ctr < kTurnOn ) {
digitalWrite(5, LOW);
} else if ( ctr > (kTurnOff+kFadeDuration) && ctr < 86400 ){
lcd.setBacklight(OFF);
digitalWrite(5, LOW);
}
else {
lcd.setBacklight(WHITE);
digitalWrite(5, HIGH);
}


DateTime now = RTC.now();
lcd.setCursor(0, 1);
lcd.print(now.hour(), DEC);
lcd.print(':');
lcd.print(now.minute(), DEC);

getPHvalue();
lcd.setCursor(0,0);
lcd.print("PH:");
lcd.print(PH_Val);

float temperature = getTemp();
lcd.setCursor(8,0);
lcd.print(temperature);
lcd.print("C");

delay(5000); //just here to slow down the output so it is easier to read

}

My apologies for the post length.
 
#162 ·
Hello all of you
First of all I would like to say big thanks to Sink and O2surplus and all you other guys :)
You all helped me getting started with my LED controller Arduino.
I'll have to read it all again to understand how it really works...
I need to fix up 4 channels with different 100% brightness and sunrise/down and moon light. Also putting in and reading everything to my laptop.
 
#163 ·
Hi all,

I have been avidly reading this thread, and having no experience of Arduino before this (but a little experience with the Pi, Python, and Linux) I am having some problems.
Firstly, I know this code doesn't compile well with IDE v1.X+, but it seems to have exactly the same problems when I attempt compiling on IDE 0022 on another machine.
For this reason, I am attempting to get it to compile on v1.0.1 for linux, in the hope that I can give something back to the community when I finally have it working!

I have already been through the issues with the time library compatibility, and fixed the libraries on my machine, but I'm now having trouble with DS1307RTC libraries, and the errors mean nothing to me, so I'm hoping someone may be able to help.

It's also worth noting that my original aim was to use this code to take advantage of 4 PWM channels on the arduino, but due to a change in design of my lighting rig, I now only need 3 channels. I have left the fourth channel code in place, but have remmed it out to save myself work if I add extra strings in the future.

My modified version of the code is:

Code:
/*
 * Name:	tank_control.pde
 * Author:	User "sink" at plantedtank.net forums
 * URL:		http://bitbucket.org/akl/tank-control
 *
 * This is control code for an aquarium lighting system.  It is intended to be
 * run on an Arduino microcontroller board.  It allows independant
 * high-resolution control of two PWM outputs (normally connected to LED
 * drivers) and complete flexibility with respect to intensity, timing
 * schedules, and sunrise/sunset.
 *
 * This code requires the following libraries: Wire, TimerOne, Time, DS3231RTC.
 * A bundle of the required libraries (except for Wire, which you should
 * already have) is located in the downloads section of the URL above.  You can
 * always find the latest copy of the code at that location.
 */

/*
 * 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 <Wire.h>
//#include <RTClib.h>
#include <TimerOne.h>
#include <Time.h>
#include <DS1307RTC.h>
//#include <DS3231RTC.h>

/*
 * IMPORTANT:  These *must* be the pins corresponding to the Timer1 timer on
 * the ATmega168/328.  These are digital pins 9 and 10 on the Uno/Duemilanove.
 */
const int kChan0Pin = 9; // Channel 0 Pin
const int kChan1Pin = 10; // Channel 1 Pin
const int kChan2Pin = 11; // Channel 2 Pin
//const int kChan3Pin = 6; // Channel 3 Pin

// All times are in seconds since midnight (valid 0 - 86399)
const long kTurnOn = 32400; // time dawn begins - 0900hrs
const long kTurnOff = 75600; // time sunset begins - 2100hrs

/*
 * Light "state" represents the PWM duty cycle for each channel This normally
 * dictates light intensity. It is an array { duty_chan_1, duty_chan_2 }.
 * Possible values for duty cycle are 0 - 1023.
 */
const int kDayState[] = { 600, 800, 800/*, 600*/ }; // daytime LED state
const int kNightState[] = { 0, 0/*, 0*/, 100 }; // nighttime LED state

/*
 * Duration (in seconds) of fade.  At the moment the only fades are sunrise and
 * sunset but this value will apply to any other fades you came up with
 */
const long kFadeDuration = 7200; // 2 hrs

long ctr;

/* hold state info */
int state_chan1, state_chan2, state_chan3/*, state_chan4*/;

/*
 * fader -- Determine output state for a given time to provide smooth fade from
 * one state to another.
 *     Args:
 *     start_time  -- time (in seconds) of start of fade
 *     start_state -- beginning state
 *     end_state   -- ending state
 *     out         -- array to update with state
 */
void fader(long start_time, const int start_state[], const int end_state[], int out[2]) {

  float per_second_delta_0 = (float) (end_state[0]-start_state[0])/kFadeDuration;
  float per_second_delta_1 = (float) (end_state[1]-start_state[1])/kFadeDuration;
  
  long elapsed = ctr-start_time;

  out[0] = start_state[0] + per_second_delta_0 * elapsed;
  out[1] = start_state[1] + per_second_delta_1 * elapsed;
}

// return seconds elapsed since midnight
long seconds_since_midnight() {
  time_t t = now();
  long hr = hour(t);
  long min = minute(t);
  long sec = second(t);
  long total = hr * 3600 + min * 60 + sec;
  return total;
}

// set output state
void set_state(const int state[]) {
  if (state[0] >= 0 && state[0] <= 1023) {
    Timer1.setPwmDuty(kChan0Pin, state[0]);
    state_chan1 = state[0]; }
  if (state[1] >= 0 && state[1] <= 1023) {
    Timer1.setPwmDuty(kChan1Pin, state[1]);
    state_chan2 = state[1]; }
  if (state[2] >= 0 && state[2] <= 1023) {
    Timer1.setPwmDuty(kChan2Pin, state[2]);
    state_chan3 = state[2]; }
  /*if (state[3] >= 0 && state[3] <= 1023) {
    Timer1.setPwmDuty(kChan3Pin, state[1]);
    state_chan4 = state[3]; }*/
}

/*
 * determine_state -- This is where the actual timing logic resides.  We
 * examine ctr (seconds since midnight) and then set output state accordingly.
 * Variable ctr rolls back to 0 at midnight so stages that cross midnight (ie:
 * nighttime) are broken up into two stages.
 */
void determine_state() {
  if ( ctr >= 0 && ctr < kTurnOn ) { // night
      set_state(kNightState);
  } else if ( ctr >= kTurnOn && ctr <= (kTurnOn+kFadeDuration) ) { // sunrise
    int foo[2];
    fader(kTurnOn, kNightState, kDayState, foo);
    set_state(foo);
  } else if ( ctr > (kTurnOn+kFadeDuration) && ctr < kTurnOff ) { // day
    set_state(kDayState);
  } else if ( ctr >= kTurnOff && ctr <= (kTurnOff+kFadeDuration) ) { // sunset
    int foo[2];
    fader(kTurnOff, kDayState, kNightState, foo);
    set_state(foo);
  } else if ( ctr > (kTurnOff+kFadeDuration) && ctr < 86400 ) { // night
    set_state(kNightState);
  }
}

/*
 * 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() {
  Serial.begin(115200); // Max for Arduino Uno
  setSyncProvider(RTC.get);
  Timer1.initialize(6666); // 150Hz PWM
  pinMode(kChan0Pin, OUTPUT);     
  Timer1.pwm(kChan0Pin, 0);
  pinMode(kChan1Pin, OUTPUT);     
  Timer1.pwm(kChan1Pin, 0);
  pinMode(kChan2Pin, OUTPUT);     
  Timer1.pwm(kChan2Pin, 0);
/*  pinMode(kChan3Pin, OUTPUT);     
  Timer1.pwm(kChan3Pin, 0);*/
}

void loop () {
  ctr = seconds_since_midnight();
  determine_state();

  if (Serial.available() >= 5) {
    char data[4];
    for (int i=0; i<5; i++) {
      data[i] = Serial.read();
    }

    Serial.flush(); // ensure we never have more than 5 bytes buffered

    if (data[0] == 'A') { // send current time
      time_t longInt = now();
      unsigned char byteArray[4];
                
      // convert from an unsigned long int to a 4-byte array
      byteArray[0] = (int)((longInt >> 24) & 0xFF);
      byteArray[1] = (int)((longInt >> 16) & 0xFF);
      byteArray[2] = (int)((longInt >> 8) & 0XFF);
      byteArray[3] = (int)((longInt & 0XFF));
      // send time
      Serial.print("Z");
      Serial.print(byteArray[0]);
      Serial.print(byteArray[1]);
      Serial.print(byteArray[2]);
      Serial.print(byteArray[3]);
    }
    else if (data[0] == 'B') { // set time
      union u_tag {
        byte b[4];
        unsigned long ulval;
      } u;

      u.b[0] = data[4];
      u.b[1] = data[3];
      u.b[2] = data[2];
      u.b[3] = data[1];

      RTC.set(u.ulval);
      setTime(u.ulval);
      Serial.print("Z0000");
    }
    else {
      Serial.print("X0000");
    }
  }

 delay(250);


  Serial.print("ctr: ");
  Serial.print(ctr); // display counter
  Serial.println(); 
  Serial.print("channel 1, 2: "); 
  Serial.print(state_chan1); 
  Serial.print(", "); 
  Serial.print(state_chan2); 
  Serial.println(); 
  digitalClockDisplay(); //display time
  Serial.println(); 

}

And the resultant errors are:

Code:
/home/tom/sketchbook/libraries/DS1307RTC/DS1307RTC.cpp: In static member function ‘static time_t DS1307RTC::get()’:
/home/tom/sketchbook/libraries/DS1307RTC/DS1307RTC.cpp:39:19: error: invalid operands of types ‘void’ and ‘bool’ to binary ‘operator==’
/home/tom/sketchbook/libraries/DS1307RTC/DS1307RTC.cpp: At global scope:
/home/tom/sketchbook/libraries/DS1307RTC/DS1307RTC.cpp:54:6: error: prototype for ‘bool DS1307RTC::read(tmElements_t&)’ does not match any in class ‘DS1307RTC’
In file included from /home/tom/sketchbook/libraries/DS1307RTC/DS1307RTC.cpp:26:0:
/home/tom/sketchbook/libraries/DS1307RTC/DS1307RTC.h:19:14: error: candidate is: static void DS1307RTC::read(tmElements_t&)
/home/tom/sketchbook/libraries/DS1307RTC/DS1307RTC.cpp:95:6: error: prototype for ‘bool DS1307RTC::write(tmElements_t&)’ does not match any in class ‘DS1307RTC’
In file included from /home/tom/sketchbook/libraries/DS1307RTC/DS1307RTC.cpp:26:0:
/home/tom/sketchbook/libraries/DS1307RTC/DS1307RTC.h:20:14: error: candidate is: static void DS1307RTC::write(tmElements_t&)
/home/tom/sketchbook/libraries/DS1307RTC/DS1307RTC.cpp:139:17: error: variable or field ‘exists’ declared void

Any help anyone can give me would be greatly appreciated.

I have also purchased a 20x4 LCD display that I hope to use to display PWM value for each channel, and water, heatsink, and air temperatures using the DS18B20 chip. But for now... baby steps!!

Many thanks,
Tom.
 
This is an older thread, you may not receive a response, and could be reviving an old thread. Please consider creating a new thread.
Top