Incorrect value being calculated

I am using the below logic to calculate the PWM signal based on time of day. However, I can’t seem to get the correct value. I have defined a series of float and int values to calculate using the current time (in seconds from a 1307RTC - however I had to divide everything by 10 because using tm.Hour*3600 was giving me weird results).

The reason I wanted to go down this path instead of simply incrementing the value is because I will eventually add processes that delay the loop and I want it to recalculate even if there is a 5 minute interruption, for example to access a menu. This means that even if the code were interrupted at 9:30am, the value at 10am would still be 127.

blueStart is the time where the light turns on
blueEnd is the time the light turns off
fade_b is the time taken to get from 0-255 or 255-0
max_bPWM is the maximum PWM value
blue_PWM is the current PWM value

Seconds = ((tm.Hour*360.0)+(tm.Minute*6.0)+(tm.Second/10.0));

if (blueStart <= Seconds && blueEnd > Seconds) {

  blue_PWM = ((Seconds - blueStart) /  60) / fade_b * max_bPWM;
}

if (blueEnd > Seconds) {
  blue_PWM = (fade_b - (Seconds - blueEnd) / 60) / fade_b * max_bPWM;
  
}
if (blue_PWM > 255) {
    blue_PWM = 255;
  }
  if (blue_PWM < 0) {
    blue_PWM = 0;
  }
analogWrite(b_pin,blue_PWM);

Serial output:

14:54 Blue = 255 RB = 255 WW = 255 14:54:34 5366.90 5040 324
blueStart=4320
fade_b=24
max_bPWM=255

So, based on this, the current seconds are 5366.9, therefore > blueStart (4320) and < blueEnd.

→ using blue_PWM = ((Seconds - blueStart) / 60) / fade_b * max_bPWM
blue_PWM = ((5366.9 - 4320) / 60 / 24 * 255
= 185.38…
As an integer, the PWM value should be 185

Why am I getting 255?

Always post ALL the code, because we need to see how variables are defined.

You probably have several different math problems. For example, tm.Hour*3600 will overflow a signed int variable (32767 max on AVR based Arduinos) and you are mixing float and integer calculations.

Avoid using float variables (or numbers with decimal points) with time and date calculations.

To calculate time in seconds use unsigned long integers, as you would with millis().

#include <Wire.h>
#include <TimeLib.h>
#include <DS1307RTC.h>

//Initializing LED and fan Pins
int ww_pin = 2;
int nw_pin = 3;
int b_pin = 4;
int rb_pin = 5;
int g_pin = 6;
int r_pin = 7;
int uv_pin = 8;
int fan_pin = 9;

// declare fade duration per channel
int fade_wwL = 180;
int fade_nwL = 180;
int fade_bL = 240;
int fade_rbL = 240;
int fade_gL = 180;
int fade_rL = 180;
int fade_uvL = 210;

// adjust fade duration per channel
int fade_ww = fade_wwL/10;
int fade_nw = fade_nwL/10;
int fade_b = fade_bL/10;
int fade_rb = fade_rbL/10;
int fade_g = fade_gL/10;
int fade_r = fade_rL/10;
int fade_uv = fade_uvL/10;

// declare max percent output of each channel
int max_ww = 100;
int max_nw = 100;
int max_b = 100;
int max_rb = 100;
int max_g = 100;
int max_r = 100;
int max_uv = 100;

// declare max PWM output of each channel
int max_wwPWM;
int max_nwPWM;
int max_bPWM;
int max_rbPWM;
int max_gPWM;
int max_rPWM;
int max_uvPWM;

// declare start times by colour
int blueStart_h = 15;
int blueStart_m = 0;
int blueStart = blueStart_h * 360+blueStart_m * 6;
int rbStart_h = 7;
int rbStart_m = 0;
int rbStart = rbStart_h * 360+rbStart_m * 6;
int wwStart_h = 8;
int wwStart_m = 0;
int wwStart = wwStart_h * 360 + wwStart_m * 6;
int nwStart_h = 8;
int nwStart_m = 0;
int nwStart = nwStart_h * 360 + nwStart_m * 6;
int uvStart_h = 7;
int uvStart_m = 30;
int uvStart = uvStart_h * 360 + uvStart_m * 6;
int secStart = 0;

// declare end times by colour
int blueEnd_h = 18;
int blueEnd_m = 0;
int blueEnd = (blueEnd_h * 360)+(blueEnd_m * 6) - (fade_b * 6);
int rbEnd_h = 18;
int rbEnd_m = 0;
int rbEnd = (rbEnd_h * 360)+(rbEnd_m * 6) - (fade_rb * 6);
int wwEnd_h = 18;
int wwEnd_m = 0;
int wwEnd = (wwEnd_h * 360)+(wwEnd_m * 6) - (fade_ww * 6);
int nwEnd_h = 18;
int nwEnd_m = 0;
int nwEnd = (nwEnd_h * 360)+(nwEnd_m * 6) - (fade_nw * 6);
int uvEnd_h = 17;
int uvEnd_m = 30;
int uvEnd = (uvEnd_h * 360)+(uvEnd_m * 6) - (fade_uv * 6);
int secEnd = 0;

// declare PWM level
int blue_PWM;
int rb_PWM;
int ww_PWM;
int nw_PWM;
int uv_PWM;
int g_PWM;
int r_PWM;

int Seconds;
int Hours;
int Minutes;

tmElements_t tm;

void setup() {

Serial.begin(9600);
  
pinMode(ww_pin, OUTPUT);
pinMode(nw_pin, OUTPUT);
pinMode(b_pin, OUTPUT);
pinMode(rb_pin, OUTPUT);
pinMode(g_pin, OUTPUT);
pinMode(r_pin, OUTPUT);
pinMode(uv_pin, OUTPUT);
pinMode(fan_pin, OUTPUT);

max_wwPWM = 2.55* max_ww;
max_nwPWM = 2.55* max_nw;
max_bPWM = 2.55* max_b;
max_rbPWM = 2.55* max_rb;
max_gPWM = 2.55* max_g;
max_rPWM = 2.55* max_r;
max_uvPWM = 2.55* max_uv;

}

void loop() {

Seconds = ((tm.Hour*360)+(tm.Minute*6)+(tm.Second/10));

if (blueStart <= Seconds && blueEnd > Seconds) {

  blue_PWM = ((Seconds - blueStart) /  60) / fade_b * max_bPWM;
}

if (blueEnd > Seconds) {
  blue_PWM = (fade_b - (Seconds - blueEnd) / 60) / fade_b * max_bPWM;
  
}
if (blue_PWM > max_bPWM) {
    blue_PWM = max_bPWM;
  }
if (blue_PWM < 0) {
    blue_PWM = 0;
  }
analogWrite(b_pin,blue_PWM);  

if (rbStart <= Seconds && rbEnd > Seconds) {

  rb_PWM = ((Seconds - rbStart) /  60) / fade_rb * max_rbPWM;
  
}

if (rbEnd < Seconds) {
  rb_PWM = (fade_rb - (Seconds - rbEnd) / 60) / fade_rb * max_rbPWM;
  
}
if (rb_PWM > max_rbPWM) {
    rb_PWM = max_rbPWM;
  }
if (rb_PWM < 0) {
    rb_PWM = 0;
  }
analogWrite(rb_pin,rb_PWM); 

if (wwStart <= Seconds && wwEnd > Seconds) {

  ww_PWM = ((Seconds - wwStart) /  60) / fade_ww * max_wwPWM;
}

if (wwEnd < Seconds) {
  ww_PWM = (fade_ww - (Seconds - wwEnd) / 60) / fade_ww * max_wwPWM;
}
if (ww_PWM > max_wwPWM) {
    ww_PWM = max_wwPWM;
  }
if (ww_PWM < 0) {
    ww_PWM = 0;
  }
analogWrite(ww_pin,ww_PWM); 

if (nwStart <= Seconds && nwEnd > Seconds) {

  nw_PWM = ((Seconds - nwStart) /  60) / fade_nw * max_nwPWM;
}

if (nwEnd < Seconds) {
  nw_PWM = (fade_nw - (Seconds - nwEnd) / 60) / fade_nw * max_nwPWM;
  
if (nw_PWM > max_nwPWM) {
    nw_PWM = max_nwPWM;
  }
if (nw_PWM < 0) {
    nw_PWM = 0;
  }
}
analogWrite(nw_pin,nw_PWM);

if (RTC.read(tm)) {

if (tm.Hour < 10) {
  Serial.print("0");
}
Serial.print(tm.Hour);
Serial.write(':');
if (tm.Minute<10) {
  Serial.print("0");
}
Serial.print(tm.Minute);
Serial.print("  Blue = ");
Serial.print(blue_PWM);
Serial.print("  RB = ");
Serial.print(rb_PWM);
Serial.print("  WW = ");
Serial.println(ww_PWM);
Serial.print(tm.Hour);
Serial.print(":");
Serial.print(tm.Minute);
Serial.print(":");
Serial.print(tm.Second);
Serial.print("  ");
Serial.print(Seconds);
Serial.print("  ");
Serial.print(tm.Hour*360);
Serial.print("  ");
Serial.println(tm.Minute*6);
Serial.print("blueStart=");
Serial.println(blueStart);
Serial.print("fade_b=");
Serial.println(fade_b);
Serial.print("max_bPWM=");
Serial.println(max_bPWM);
}
delay(5000);

}

Thanks for the response. I’ve posted all the code, I have also changed all float values to integers. As mentioned, I found issues with tm.Hour3600 (I had no idea why but what you said makes sense) so I am using tm.Hour360 and adjusting other values to match.
I realise the sketch probably does not follow normal conventions, I am an absolute beginner.

I am an absolute beginner.

That is a big mouthful for a first bite! We recommend that beginners start with simpler projects and study the language elements.

I had no idea why

Did you look up the meaning of "unsigned long integer"?

Always use unsigned long integers for time calculations, and the overflow problems will go away. You should also use unsigned integers for variables like PWM values that always represent positive whole numbers, as the range is larger.

These are inappropriate float calculations:

max_wwPWM = 2.55* max_ww;

The correct way to do that, using integer math, is as follows. The order of multiply and divide is important!

max_wwPWM = (255* max_ww)/100;

Thanks again for the response.

That is a big mouthful for a first bite! We recommend that beginners start with simpler projects and study the language elements.

I’ve played with some basics like LCD display, RTC, temperature display and basic LEDs. So far everything is just practising with a breadboard.

Did you look up the meaning of “unsigned long integer”?

I did after your initial response. The limitations of integers was exactly what I experienced, I adjusted the values to negate the impact and have not changed everything to unsigned long as it isn’t needed with the adjusted values.

These are inappropriate float calculations:

Makes sense, I have amended these per your suggestion.

The issue appears to be here:

Serial.println(tm.Hour);
Serial.println(tm.Minute);
Serial.println(tm.Second);
Serial.println(Seconds);
Serial.println(blueEnd);
Serial.println(blueEnd_inv);

10

39

18

0

6120

4294961176

where:

unsigned long Seconds = ((tm.Hour*360)+(tm.Minute*6)+(tm.Second/10));
unsigned long bPWM;
unsigned long blueEnd_inv = (Seconds - blueEnd);

Please post the revised code.

This is pretty silly, because the variable called "Seconds" does not represent seconds. It is a good idea to use sensible variable names, for yourself (looking at the code six months later), and for anyone else trying to make sense of it.

unsigned long Seconds = ((tm.Hour*360)+(tm.Minute*6)+(tm.Second/10));

For on/off timer purposes, many people keep track of daytime using self explanatory variable names like "minutes_past_midnight" or "seconds_past_midnight".

Yep, noted. It was obviously supposed to be seconds before I recalculated. I have changed everything to minutes and the designation is Minute_of_day.

#include <Wire.h>
#include <TimeLib.h>
#include <DS1307RTC.h>

//Initializing LED and fan Pins
int ww_pin = 2;
int nw_pin = 3;
int b_pin = 4;
int rb_pin = 5;
int g_pin = 6;
int r_pin = 7;
int uv_pin = 8;
int fan_pin = 9;

// declare fade duration per channel
int fade_ww = 180;
int fade_nw = 180;
int fade_b = 240;
int fade_rb = 240;
int fade_g = 180;
int fade_r = 180;
int fade_uv = 210;

// declare max output of each channel
int max_ww = 100;
int max_nw = 100;
int max_b = 100;
int max_rb = 100;
int max_g = 100;
int max_r = 100;
int max_uv = 100;

// declare max PWM output of each channel
int max_wwPWM;
int max_nwPWM;
int max_bPWM;
int max_rbPWM;
int max_gPWM;
int max_rPWM;
int max_uvPWM;

// declare start times by colour
int blueStart_h = 7;
int blueStart_m = 0;
int blueStart = blueStart_h * 60+blueStart_m;
int rbStart_h = 7;
int rbStart_m = 0;
int rbStart = rbStart_h * 60+rbStart_m;
int wwStart_h = 8;
int wwStart_m = 0;
int wwStart = wwStart_h * 60 + wwStart_m;
int nwStart_h = 8;
int nwStart_m = 0;
int nwStart = nwStart_h * 60 + nwStart_m;
int uvStart_h = 7;
int uvStart_m = 30;
int uvStart = uvStart_h * 60 + uvStart_m;
int secStart = 0;

// declare end times by colour
int blueEnd_h = 21;
int blueEnd_m = 0;
unsigned long blueEnd = (blueEnd_h * 60)+blueEnd_m - (fade_b);
int rbEnd_h = 18;
int rbEnd_m = 0;
unsigned long rbEnd = (rbEnd_h * 60)+rbEnd_m - (fade_rb);
int wwEnd_h = 18;
int wwEnd_m = 0;
unsigned long wwEnd = (wwEnd_h * 60)+wwEnd_m - (fade_ww);
int nwEnd_h = 18;
int nwEnd_m = 0;
unsigned long nwEnd = (nwEnd_h * 60)+nwEnd_m - (fade_nw);
int uvEnd_h = 17;
int uvEnd_m = 30;
unsigned long uvEnd = (uvEnd_h * 60)+uvEnd_m - (fade_uv);
int secEnd = 0;

// declare values to calculate fade increments
// float step_ww;
// float step_nw;
// float step_b;
// float step_rb;
// float step_g;
// float step_r;
// float step_uv;

// declare PWM level
int blue_PWM;
int rb_PWM;
int ww_PWM;
int nw_PWM;
int uv_PWM;
int g_PWM;
int r_PWM;

tmElements_t tm;

int Hours = 60*tm.Hour;
int Minute_of_day = tm.Minute + Hours;

void setup() {

Serial.begin(9600);
  
pinMode(ww_pin, OUTPUT);
pinMode(nw_pin, OUTPUT);
pinMode(b_pin, OUTPUT);
pinMode(rb_pin, OUTPUT);
pinMode(g_pin, OUTPUT);
pinMode(r_pin, OUTPUT);
pinMode(uv_pin, OUTPUT);
pinMode(fan_pin, OUTPUT);

max_wwPWM = 255* max_ww/100;
max_nwPWM = 255* max_nw/100;
max_bPWM = 255* max_b/100;
max_rbPWM = 255* max_rb/100;
max_gPWM = 255* max_g/100;
max_rPWM = 255* max_r/100;
max_uvPWM = 255* max_uv/100;

}

void loop() {

if (blueStart <= Minute_of_day && blueEnd > Minute_of_day) {

  blue_PWM = ((Minute_of_day - blueStart) /  6) / fade_b * max_bPWM;
}

if (Minute_of_day > blueEnd) {
  blue_PWM = max_bPWM * ((fade_b - (tm.Minute - blueEnd)) / fade_b ) ; 
}
Serial.println();
Serial.println(tm.Hour);
Serial.println(tm.Minute);
Serial.println(Minute_of_day);
Serial.println(blueEnd);
Serial.println(blueEnd_inv);

if (blue_PWM > max_bPWM) {
    blue_PWM = max_bPWM;
  }
if (blue_PWM < 0) {
    blue_PWM = 0;
  }
analogWrite(b_pin,blue_PWM);  

if (rbStart <= Minute_of_day && rbEnd > Minute_of_day) {

  rb_PWM = ((Minute_of_day - rbStart) /  60) / fade_rb * max_rbPWM;
}

if (rbEnd > Minute_of_day) {
  rb_PWM = (fade_rb - (Minute_of_day - rbEnd) / 60) / fade_rb * max_rbPWM;
  
}
if (rb_PWM > max_rbPWM) {
    rb_PWM = max_rbPWM;
  }
if (rb_PWM < 0) {
    rb_PWM = 0;
  }
analogWrite(rb_pin,rb_PWM); 

if (wwStart <= Minute_of_day && wwEnd > Minute_of_day) {

  ww_PWM = ((Minute_of_day - wwStart) /  60) / fade_ww * max_wwPWM;
}

if (wwEnd > Minute_of_day) {
  ww_PWM = (fade_ww - (Minute_of_day - wwEnd) / 60) / fade_ww * max_wwPWM;
}
if (ww_PWM > max_wwPWM) {
    ww_PWM = max_wwPWM;
  }
if (ww_PWM < 0) {
    ww_PWM = 0;
  }
analogWrite(ww_pin,ww_PWM); 

if (nwStart <= Minute_of_day && nwEnd > Minute_of_day) {

  nw_PWM = ((Minute_of_day - nwStart) /  60) / fade_nw * max_nwPWM;
}

if (nwEnd < Minute_of_day) {
  nw_PWM = (fade_nw - (Minute_of_day - nwEnd) / 60) / fade_nw * max_nwPWM;
  
if (nw_PWM > max_nwPWM) {
    nw_PWM = max_nwPWM;
  }
if (nw_PWM < 0) {
    nw_PWM = 0;
  }
}
analogWrite(nw_pin,nw_PWM);

if (RTC.read(tm)) {

if (tm.Hour < 10) {
  Serial.print("0");
}
Serial.print(tm.Hour);
Serial.write(':');
if (tm.Minute<10) {
  Serial.print("0");
}
Serial.print(tm.Minute);
Serial.print("  Blue = ");
Serial.print(blue_PWM);
Serial.print("  RB = ");
Serial.print(rb_PWM);
Serial.print("  WW = ");
Serial.println(ww_PWM);

Serial.println(Minute_of_day);

Serial.print("blueStart=");
Serial.println(blueStart);
Serial.print("fade_b=");
Serial.println(fade_b);
Serial.print("max_bPWM=");
Serial.println(max_bPWM);
Serial.print("blueEnd = ");
Serial.println(blueEnd);
}
delay(5000);

}

Does the most recently posted code function as expected?

These lines don't belong outside of a function. What do you imagine that they are doing? And, of course "Hours" does not represent hours.

int Hours = 60*tm.Hour;
int Minute_of_day = tm.Minute + Hours;

No, still not having any luck. I’m still getting weird results with the same piece of calculation.

if (blueStart <= Minute_of_day && blueEnd > Minute_of_day) {

  blue_PWM = (Minute_of_day - blueStart) / fade_b * max_bPWM;
}

if (Minute_of_day > blueEnd) {
  blue_PWM = max_bPWM * ((fade_b - (Minute_of_day - blueEnd)) / fade_b ) ; 
}

Serial.print("blue_PWM = ");
Serial.println(blue_PWM);

According to the serial output:
blue_PWM = 0
Hours = 17
Minutes = 7
Minute of Day = 1027
17:07
Blue = 0 RB = 255 WW = 255
blueStart=480
fade_b=240
max_bPWM=255
blueEnd = 1020

Minute_of_day (1027) > blueEnd (1020) therefore:
blue_PWM = 255 * ((240 - (1027 - 1020)) / 240
= 255 * (233/240)
= 248 (as an integer)

As you can see, this is 0

aarons1:
Minute_of_day (1027) > blueEnd (1020) therefore:
blue_PWM = 255 * ((240 - (1027 - 1020)) / 240
= 255 * (233/240)
= 248 (as an integer)

WRONG!!!
Integer divison of (233/240) IS zero! Therefore blue_PWM will be equal to 0
IMHO, assuming your variables are uint16_t (or uint32_t just to be safe), if you try (255 * 233)/240 instead, you should get your answer.
hope that helps....

Legend. That was my issue.

Changing:

blue_PWM = max_bPWM * ((fade_b - (Minute_of_day - blueEnd)) / fade_b )

to:

blue_PWM = max_bPWM * (fade_b - (Minute_of_day - blueEnd)) / fade_b ;

worked a treat. It’s now 7:35 pm and the PWM signal is reducing through around 90 steps, just as expected.

Thank you so much.

That is why I stated in reply #3: "The order of multiply and divide is important!"

Additionally you have to be careful that the multiply result does not overflow the maximum of the variable type.

16 bit integers have maximum value 32767, whereas unsigned 16 bit integers have maximum 65535. It is usually best to use unsigned long for time calculations.