While / if statement returning weird math

Hello!

Hoping to get some help with a little project where I'm stuck.
Whole project is >700 lines long but I'll try to paste the connected parts.

Before setup and loop I establish:

const int s8 = 9; //arduino pin
byte data = 0; //relayboard "off"

unsigned long Minute = 60;
unsigned long spr8long = 30*Minute;
unsigned long spr8longcount = 0;
unsigned long spr8shortcount = 0;
unsigned long spr8short = 6*Minute;

In setup:

pinMode(s8, INPUT_PULLUP);

In loop:

This section is supposed to check the "clock", switch and if the intended runtime(spr8short) is larger than actual runtime(spr8shortcount).
Let's say it just turn 9am, the switch is low and spr8short > spr8shortcount, then this will activate.
Once in this while loop it will write data1 to the relayboard and start increasing spr8shortcount until it reaches spr8short and then "jump back" to the mainloop and write data0 to turn off the relay.

while (hour() >= 9 && s8state == LOW && spr8short > spr8shortcount){
  if(data == 0){
    data = 1;  // to activate a specific relay on relayboard
    digitalWrite(latchPin, LOW);                  // Latch is low while we load new data to register
    shiftOut(dataPin, clockPin, MSBFIRST, data);  // Load the data to register using ShiftOut function
    digitalWrite(latchPin, HIGH);                 // Toggle latch to present the new data on register outputs
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("Section 8 started.");
    }
    if (spr8short > spr8shortcount) {  // if runtime is > timer
      unsigned long currentSec2 = millis()/1000;  // second variable from millis
      spr8shortcount = (currentSec2 - spr8shortcount) + spr8shortcount;  // timer to add seconds
      lcd.setCursor(0,1);
      lcd.print("Time remaining:");
      lcd.setCursor(0,2);
      lcd.print(spr8short - spr8shortcount);
      lcd.setCursor(5,2);
      lcd.print(" seconds.");
    }
   s8state = digitalRead(s8);  //Check if switch is still low
   }

  if(data == 1){
  data = 0;  //turn off the relayboard
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, MSBFIRST, data);
  digitalWrite(latchPin, HIGH);
  lcd.clear();
  }

That part seems to function as intended..
At the end of the loop, at a specific time I try to reset the spr8shortcount, and this is the part that I can't get to work.

// Timer resets!
if(hour() == 12 && minute() == 0 && second() == 1){
  lcd.clear();
  lcd.print("Reset short timer");
  spr8shortcount = 0; //reset the timer
  lcd.setCursor(0,1);
  lcd.print(spr8shortcount); //print to make sure it's 0, this is usually OK
  delay(2000);
  lcd.clear();
}

the lcd.print(spr8shortcount); returns 0 as it should but as soon as the delay is over and I do the same command in the start of the loop(ie print the value), it suddenly has a completely different value, often higher than it has ever been.

So for example:

  1. Program starts and waits for 9 am
  2. Timer part starts, showing a new "menu screen" and happily counts up spr8shortcount
  3. Timer reaches spr8short and exits to the normal screen and turns off the relay
  4. Back at "main screen" spr8shortcount is now == spr8short (doing a lcd.print to check)
  5. After 'Timer reset' function starts at 12:00:01 and sets spr8shortcount to 0, lcdprint also shows 0.
  6. After delay(2000); and lcd.clear(); we're back at main screen again and spr8shortcount suddenly shows as a much larger value instead of 0, which in turn means the reset did not work and the timer function can't start again.
    I.e .. If the spr8shortcount was 10 at the end of the timer, in the reset it shows as 0 but back in the "main loop" it's suddenly 18.

I've tried so many things that I don't even know what to do anymore, would be very happy if someone could look at this and see if they find any obvious faults..

this feels weird

why not do just

      spr8shortcount = currentSec2 ;
2 Likes

can you post that part of loop() and where the variable is defined, presumably it is a global outside of any function?

1 Like

if you want to do a count down you need to remember the time when things started and compare that with the current time until you reach your 6 minutes. This is not doing that

you'd need something like

spr8short - (currentTimeIn_s - recordedStartTimeIn_s) 
1 Like

currentSec2 is stored from millis() and might be any arbitrary value.
I see now tho that the calculation is bonkers to start with since I'm just adding and subtracting the same thing like your second post is onto.
The lcd.print is just comparing the timer(spr8shortcount) to my set value (spr8short).
Somehow that part is working besides the weird math, it counts down, or rather up, to whatever value I set as spr8short.

Will need to think about this part.

spr8count and spr8shortcount are both defined before both setup and loop to make them global.

  unsigned long spr8long = 30*Minute; // Dubbla rotorer framsidan, startar 1'a
  unsigned long spr8longcount = 0;
  unsigned long spr8shortcount = 0;
  unsigned long spr8short = 4;

Been troubleshooting further, specifically looking at the weird math I had come up with, as you pointed out. :grin:

A bit of a hack but this seems to work reliably.
I have defined this BEFORE setup and loop.

  unsigned long startTime = 0;
  unsigned long endTime8 = startTime + spr8short;
  unsigned long currentTime = startTime;

This is the modified "SEKTION 8".
When the while starts, it will first go into the if(data == 0), this triggers a relay, stores the 'start time' and calculates the endTime(by using the spr8short runtime). This only triggers once.

After that it will continiously compare endTime8 to currentTime and once endTime8 > currentTime it will set spr8shortcount to some arbitrary high value(higher than spr8short) and go back to the main loop which turns off the relay again.

Now it happily waits for the reset function which resets the value and everything starts over again. :grin:
So something about the shitty math I used must've been blocking it from resetting.

  //SEKTION 8
  while (hour() >= 9 && s8state == LOW && spr8short > spr8shortcount){
    if(data == 0){
    data = 1;
    digitalWrite(latchPin, LOW);                  // Latch is low while we load new data to register
    shiftOut(dataPin, clockPin, MSBFIRST, data);  // Load the data to register using ShiftOut function
    digitalWrite(latchPin, HIGH);                 // Toggle latch to present the new data on register outputs
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("Section 8 started.");
    startTime = (millis() / 1000);
    endTime8 = startTime + spr8short;
    currentTime = startTime;
    }
    if (endTime8 > currentTime) {
      currentTime = (millis() / 1000);
      lcd.setCursor(0,1);
      lcd.print("Time remaining:");
      lcd.setCursor(0,2);
      lcd.print(endTime8 - currentTime);
      lcd.setCursor(5,2);
      lcd.print(" seconds.");
      }else{
      spr8shortcount = 38000;
      }
    s8state = digitalRead(s8);
    delay(100);
    }

you don't want to do an addition because millis() will overflow in ~50 days. always proceed with a subtraction

just do

    startTime = (millis() / 1000);
    currentTime = startTime;
    }

    if (currentTime - startTime  > spr8short ) { // timeout
...

(also you should keep the clock in ms and only divide by 1000 when you display, you'll be more precise likely)

1 Like

Just thinking out loud, trying to understand the failure scenario:

As far as I can google, millis() seems to be unsigned long type so when it overflows it will start again from 0 I guess?
So 'worst case scenario' is that the addition gets done right before overflow.
To make it easier let's say it overflows at 100 and we add 20 when it's at 90.
Since the time data types are also unsigned long, wouldn't that mean the math ends up at 10 and still get hit after overflow?
Or am I missunderstanding how it works?
I understand that it's probably bad practice, just curious about the "inner workings".

Completely understand and agree about the ms instead of s when calculating. The runtimes are generally 30-90 minutes so if it's off a few seconds here and there it doesn't matter in this usecase and makes it easier for my variables(possibly only in my head).

I figured I'd share the project if someones curious, keep in mind that I'm an absolute rookie and this is by FAR the most complex thing I've ever programmed.
https:// github .com/mrcrankyface/SprinklyA2

An arduino duemilanove is connected to:
-4x20 LCD
-EK006 Relay board(8x relays via serial comms)
-DIY relay-pcb-thing(powerpin) to delay power to EK006 to not trip the PSU/mess with arduinos startup.
-24/5VDC PSU (5V for relaycards and arduino, 24V for solenoids)
-8x solenoids to control watervalves.

This then runs our lawn irrigation, it's a work in progress over the last 2-3 years so installation is very temporary for testing purposes.

Say it overflows at 100 to make it easy
You are at 95 and want a 10 units duration

With addition:
You calculate the end time say as 95 + 10 which is 105 but you rollover so it’s 5
So when you compare the current time (say 96) with this end time (5) you are instantly above and you did not wait at all

With subtraction
Now you compare the difference of current time and the start time with the duration
(96 - 95) is 1 and it’s < 10 so you keep going

The beauty of unsigned math is that when you rolled over and are at 0

(0 - 95) Will be 5 (in unsigned math rolling over at 100) and so still < 10 and so you keep going until you reach 6 and then (6 - 95) is 11 so > 10 and you completed the wait for 10 units

➜ in a nutshell it works with subtraction but not with addition

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.