Help with RTC-based PWM

Hi all

As a quick preface: I am attempting to modify and use some fantastic code I found on another website, in order to use 3 PWM channels to control LED lighting above my reef aquarium.

So far I have prototyped all my circuits, and managed to get pleasing results with the included libraries within IDE 1.0.1 (Linux), however the code I have found will allow me to dim 3 channels at set times of day using the DS1307 RTC (which I have already managed to program correctly.)

I am using the Nano V3 Mega328 board.

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:

/*
 * 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:

/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.

With Arduino 1.0+ I think you need to include<Arduino.h>, not the wire library.

http://forum.arduino.cc/index.php/topic,85547.0.html

Thanks for the reply! That would have been easier :oops:

I just rewrote the library to fix the bool and void errors to make it compatible with 1.0 in the end! It is now all working, and has the temperature sensors added too. :sunglasses: