When comparing time intervals, use unsigned long instead of long.
if ( ... (((unsigned long)(currMicros - prevMicros)) >= intervalON) )
The time counter overflows at some point -- micros() overflows after 70 minutes, milis() overflows after nearly a month -- but these are unsigned values.
When an unsigned value is interpreted as a signed value -- (long)
instead of (unsigned long)
-- then half of the values are interpreted as negative numbers. So after 40 minutes, the micros() value looks like a negative number, and the (curr-prev) >= interval comparison returns false. After 70 minutes, the micros() overflows back to 0, and the signed comparisons work as expected.
The solution is to use unsigned long for all delta-time comparisons.
This is easier to see if you consider a smaller, 3-bit number: there are 8 values for a 3-bit number. If the 3-bit number is unsigned, then the values are 0, 1, 2, 3, 4, 5, 6, 7. But if the 3-bit number is signed, then those same binary code numbers are interpreted as 0, 1, 2, 3, -4, -3, -2, -1. Same principle for int and long values, just a larger range of values.
EDIT:
Based on your comment below, I did some testing on my own hardware (Sparkfun "red board" Arduino UNO R3 clone), and added some code to help make it more testable. Some highlights:
- Added a diagnostic LED on Arduino pin 9, to indicate when the timer overflow has happened (i.e. new time is less than previous time).
- Added a mask to force the timer overflow to happen sooner, so I don't have to wait an hour to verify whether the code had any effect.
- Calculated timeSinceLastChange and applied mask, to handle overflow.
- Added TIMER_DATATYPE based on jippie's comment
The complete code:
//--- based on comment from jippie about using C++ standard width integer types
// #define TIMER_DATATYPE unsigned long
#define TIMER_DATATYPE uint32_t
//--- MarkU: diagnostic LED to indicate that timer overflow happened.
// Comment out this line for normal operation.
#define DIAGNOSTIC_LED_TIME_OVERFLOW 9
//---
//--- MarkU: diagnostic: force timer overflow to happen sooner.
// This must be significantly longer than the intervals being measured.
// Must be a binary all-1's bitmask.
// 0x3FFFFFF overflow after approximately 1 minute
// 0xFFFFFFF overflow after approximately 5 minutes
// 0xFFFFFFFF overflow after approximately 70 minutes
#define DIAGNOSTIC_TIMER_OVERFLOW_MASK 0x3FFFFFF
//---
int fan = 3;
int led = 13;
int ledState = HIGH; //all'inizio gira
//unsigned long time;
TIMER_DATATYPE prevMicros = 0; // will store last time LED was updated +
TIMER_DATATYPE interval = 7000000; // lunghezza della pausa in secondi
TIMER_DATATYPE intervalON = 1000000; // lunghezza del ON in secondi
void setup() {
pinMode(fan, OUTPUT);
pinMode(led, OUTPUT);
//--- MarkU: diagnostic LED indicate that timer overflow happened
#if DIAGNOSTIC_LED_TIME_OVERFLOW
pinMode(DIAGNOSTIC_LED_TIME_OVERFLOW, OUTPUT);
digitalWrite(DIAGNOSTIC_LED_TIME_OVERFLOW, LOW);
#endif // DIAGNOSTIC_LED_TIME_OVERFLOW
//---
prevMicros = micros();
}
void loop() {
TIMER_DATATYPE currMicros = micros();
//--- MarkU: diagnostic: force timer overflow to happen sooner
#if DIAGNOSTIC_TIMER_OVERFLOW_MASK
currMicros = (TIMER_DATATYPE)(currMicros & ((TIMER_DATATYPE)DIAGNOSTIC_TIMER_OVERFLOW_MASK));
#endif // DIAGNOSTIC_TIMER_OVERFLOW_MASK
//--- MarkU: consolidate time delta calculation
TIMER_DATATYPE timeSinceLastChange;
if (currMicros < prevMicros) {
//--- MarkU: handle timer overflow
#if DIAGNOSTIC_LED_TIME_OVERFLOW
digitalWrite(DIAGNOSTIC_LED_TIME_OVERFLOW, HIGH);
#endif // DIAGNOSTIC_LED_TIME_OVERFLOW
#if DIAGNOSTIC_TIMER_OVERFLOW_MASK
timeSinceLastChange = (currMicros - prevMicros) & DIAGNOSTIC_TIMER_OVERFLOW_MASK;
#else // DIAGNOSTIC_TIMER_OVERFLOW_MASK
timeSinceLastChange = (currMicros - prevMicros) & 0xFFFFFFFF;
#endif // DIAGNOSTIC_TIMER_OVERFLOW_MASK
} else {
timeSinceLastChange = currMicros - prevMicros;
}
//---
// manage overflow? BOH
if (ledState == HIGH && (timeSinceLastChange >= intervalON)) {
prevMicros = currMicros;
ledState = LOW;
}
else if (ledState == LOW && (timeSinceLastChange >= interval)) {
ledState = HIGH;//riparte
prevMicros = currMicros;
}
digitalWrite(led, ledState);
digitalWrite(fan, ledState);
}
(((unsigned long)(currMicros - prevMicros)) >= intervalON)
instead of(((long)(currMicros - prevMicros)) >= intervalON)
, since the timing value from micros() is unsigned?uint32_t
instead ofunsigned long
for readability and cross platform portability.extern unsigned long timer0_overflow_count;
then, insetup()
, set this global variable to0x001f0000
. This way the high bit ofmicros()
will toggle in ≈ 67 seconds. Set it to0x003f0000
andmicros()
will roll back to zero, again in ≈ 67 seconds.