Simple Timer with Action during Button Press

Hy, i am working on a Programm that does two thinks

Every x- Hourse it should turn on and of a relay (200ms) so that a motor is spinning a slow step, but when i press a button the motor should spin as long as i press the button.

I am doing that on a Arduino Nano, and my Code is working when i testet it with spin motor every 10sek.

But now i but in a 4 hour mark and.. after around 30-50min the motor starts spinning and will not stop, also button press will not work anymore.

I don´t get it because the libary need micro sek and 4 hours are 14400000000 microseconds, i but it in and since then i have troubles :frowning:

Maybe you can help me out, i am not programming thats my first try and i take a lot to get that little code running.

Code:


#include <dmtimer.h>

/**
 * Demonstrates the simplest use of the Timer class
 */
int ledPin =  13;      // the number of the LED pin
int ledState = LOW;
int relais1pin = 2;
int sensorpin = 3;


DMTimer myTimer(14400000000); //Create a timer and specify its interval in microseconds

void setup(){
  Serial.begin(115200);

  // set the digital pin as output:
  pinMode(ledPin, OUTPUT);  
  pinMode(relais1pin,OUTPUT);
  pinMode(sensorpin, INPUT);
  pinMode(LED_BUILTIN, OUTPUT);
 
}


void loop(){

int sensorstate = digitalRead(sensorpin);
  if (sensorstate == 1) {
    Serial.println("Sensor active");
    // digitalWrite(ledPin, ledState);
    //digitalWrite(2,LOW);
  digitalWrite(relais1pin,HIGH);

  } else {
 Serial.println("Sensor not active");
 digitalWrite(relais1pin,LOW);
//  digitalWrite(ledPin, ledState);    // turn the LED off by making the voltage LOW

  if(myTimer.isTimeReached()){ //check if execution time has been reached
   Serial.println("Halllllllllllllllllllllllllllllllllllllllllllllo"); //call what you need
   // digitalWrite(2,LOW);
   digitalWrite(relais1pin,HIGH);
   delay (200);
   digitalWrite(relais1pin,LOW);
   myTimer.reset();    
 // digitalWrite(LED_BUILTIN, LOW);
//  
}

   
  }
}

Does this work DMTimer myTimer(14400000000UL);?

That's more than 33 bits as a binary number. Imma guess no matter how you send it on in there, it's not gonna do what you want.

@deadfox if you really want to use that library mechanism, perhaps you could find it has a milliseconds version.

Otherwise you'll have to set the timer for as long as it can be, and count off a number of those smaller intervals and fire your action when enough of them to make up your entire delay have gone by.

Or do the whole thing in an entirely different way in code you should, sooner or later, learn to write yourself.

a7

I tried a lot but with (millis) i was just did come fare, so i tried that libary,.. i did not thought about that 4hours will be to much, but it looks like it is :frowning:

Mybe there is a nother libary i can use, because the dmtimer is only for microseconds i guess ..

The library uses micros(), the documentation for that says,
"Returns the number of microseconds since the Arduino board began running the current program. This number will overflow (go back to zero), after approximately 70 minutes."

But, there is no mention of that at all, on the library site.

you don't really need a timer, millis will do

have a look at something like this

in the simulator

const byte relaisPin  =  2;
const byte buttonPin  =  3;
const byte ledPin     = 13;

const int RELAY_ON = HIGH;
const int RELAY_OFF = LOW;


const unsigned long period = 14400000ul; // 4 hours
unsigned long lastActivation = 0;

bool relayIsActive = false;

void relayOn() {
  digitalWrite(relaisPin, RELAY_ON);
  digitalWrite(ledPin, HIGH);
  lastActivation = millis();  // do this if you want the pulse only 4h after the last forced activation
  relayIsActive = true;
}

void relayOff() {
  digitalWrite(relaisPin, RELAY_OFF);
  digitalWrite(ledPin, LOW);
  relayIsActive = false;
}

void setup() {
  Serial.begin(115200);
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(ledPin, OUTPUT);    digitalWrite(ledPin, LOW);
  pinMode(relaisPin, OUTPUT); digitalWrite(relaisPin, RELAY_OFF);
}

void loop() {
  // forced ON ?
  if (digitalRead(buttonPin) == LOW)   // LOW means pressed
    relayOn();
  else
    relayOff();

  // time to pulse ?
  if (!relayIsActive && (millis() - lastActivation >= period)) {
    // pulse !
    relayOn();
    delay (200);
    relayOff();
    lastActivation = millis();
  }

}

Here is a code-version that should do what you want.
It does not need a lribrary.
There is a small function TimePeriodIsOver that does what the name says
return true or false depending on the timer expired or not

This function is based on millis()
If the timer has expired the timer-variables gets automatically updated
through the "&"-operator in the definition of the function
pass by reference

// MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START *

// a detailed explanation how these macros work is given in this tutorial
// https://forum.arduino.cc/t/comfortable-serial-debug-output-short-to-write-fixed-text-name-and-content-of-any-variable-code-example/888298

#define dbg(myFixedText, variableName) \
  Serial.print( F(#myFixedText " "  #variableName"=") ); \
  Serial.println(variableName);
// usage: dbg("1:my fixed text",myVariable);
// myVariable can be any variable or expression that is defined in scope

#define dbgi(myFixedText, variableName,timeInterval) \
  do { \
    static unsigned long intervalStartTime; \
    if ( millis() - intervalStartTime >= timeInterval ){ \
      intervalStartTime = millis(); \
      Serial.print( F(#myFixedText " "  #variableName"=") ); \
      Serial.println(variableName); \
    } \
  } while (false);
// usage: dbgi("2:my fixed text",myVariable,1000);
// myVariable can be any variable or expression that is defined in scope
// third parameter is the time in milliseconds that must pass by until the next time a
// Serial.print is executed
// end of macros dbg and dbgi
// print only once when value has changed
#define dbgc(myFixedText, variableName) \
  do { \
    static long lastState; \
    if ( lastState != variableName ){ \
      Serial.print( F(#myFixedText " "  #variableName" changed from ") ); \
      Serial.print(lastState); \
      Serial.print( F(" to ") ); \
      Serial.println(variableName); \
      lastState = variableName; \
    } \
  } while (false);
// MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END *

const byte sensorpin  = 3;
const byte relais1pin = 2;

unsigned long myTimer;
//                          4 hours * 60 min/h * 60 sec/min * 1000 milliseconds
unsigned long WaitingTime = 4       * 60       * 60         * 1000ul;


void PrintFileNameDateTime() {
  Serial.println( F("Code running comes from file ") );
  Serial.println( F(__FILE__) );
  Serial.print( F("  compiled ") );
  Serial.print( F(__DATE__) );
  Serial.print( F(" ") );
  Serial.println( F(__TIME__) );
}


// easy to use helper-function for non-blocking timing
boolean TimePeriodIsOver (unsigned long &startOfPeriod, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - startOfPeriod >= TimePeriod ) {
    // more time than TimePeriod has elapsed since last time if-condition was true
    startOfPeriod = currentMillis; // a new period starts right here so set new starttime
    return true;
  }
  else return false;            // actual TimePeriod is NOT yet over
}

const byte    OnBoard_LED = 13;


void BlinkHeartBeatLED(int IO_Pin, int BlinkPeriod) {
  static unsigned long MyBlinkTimer;
  pinMode(IO_Pin, OUTPUT);
  if ( TimePeriodIsOver(MyBlinkTimer, BlinkPeriod) ) {
    digitalWrite(IO_Pin, !digitalRead(IO_Pin) );
  }
}


void setup() {
  Serial.begin(115200);
  Serial.println( F("Setup-Start") );
  pinMode(relais1pin, OUTPUT);
  pinMode(sensorpin, INPUT);
  PrintFileNameDateTime();
  // initialise timer-variable
  //with actual snapshot of time at powerup
  myTimer = millis();
}


void loop() {
  BlinkHeartBeatLED(OnBoard_LED, 250);
  int sensorstate = digitalRead(sensorpin);
  if (sensorstate == HIGH) {
    //Serial.println("Sensor active");
    dbgi("Sensor active", sensorstate, 1000);
    digitalWrite(relais1pin, HIGH);
  }
  else {
    dbgi("Sensor not active", sensorstate, 1000);
    //Serial.println("Sensor not active");
    digitalWrite(relais1pin, LOW);
    //  digitalWrite(ledPin, ledState);    // turn the LED off by making the voltage LOW
    if ( TimePeriodIsOver(myTimer, WaitingTime) ) {
      //Serial.println("Halllllllllllllllllllllllllllllllllllllllllllllo"); //call what you need
      dbg("Timer elapsed", WaitingTime);
      digitalWrite(relais1pin, HIGH);
      delay (200);
      digitalWrite(relais1pin, LOW);
    }
  }
}

best regards Stefan

Hello deadfox

Check and try this small example coded in C++.
You have to modify the sketch to your timing requirements only.

// https://forum.arduino.cc/t/using-millis-to-turn-on-output-after-preset-time-and-turn-it-back-off-after-a-nother-presete-time/1056985
/*
  Using millis() to turn on output after preset time and turn it back off after a nother presete time
*/
constexpr byte ButtonPin{A0};
constexpr byte LedPin{8};
struct TIMER
{
  unsigned long stamp;
  const unsigned long Duration;
  int onOff;
};
TIMER hrTimer {0, 5000, true};
TIMER msecTimer {0, 200, false};
void setup()
{
  Serial.begin(9600);
  pinMode (ButtonPin, INPUT_PULLUP);
  pinMode (LedPin, OUTPUT);
}
void loop()
{
  unsigned long currentTime = millis();
  if (!digitalRead(ButtonPin))
  {
    digitalWrite(LedPin, HIGH);
  }
  else
  {
    if (currentTime - hrTimer.stamp >= hrTimer.Duration)
    {
      hrTimer.stamp = currentTime;
      msecTimer.stamp = currentTime;
      msecTimer.onOff = true;
      digitalWrite(LedPin, HIGH);
    }
    if (currentTime - msecTimer.stamp  >= msecTimer.Duration && msecTimer.onOff)
    {
      msecTimer.onOff = false;
      digitalWrite(LedPin, LOW);
    }
  }
}

Thanks for your anwsers, i am trying that first code at the moment.

it seams to work when i press the button perfectly but i think its not spinning with the timer every 4 hourse.. maybe i have to check that up with a smaller intervall.

Why do i need that UL after the Numbers (does it mean Ultra Long?) i thought i will just but in the milisecounds now.

4h = 14400000 ms

very good observation
The "ul" at the end makes sure that the compiler treats everything as unsigned 32bit-integers.
"ul" stands for unsigned long

Without the "ul" the compiler might treat it as 16bit values which could lead to strange results.
16 bit means maximum value that can be hold by an 16bit integer is
2^16 = 65536 which is smaller than the desired
14.400.000 ms

best regards Stefan

Ok, good to know =)
Now its just a thing that i get that for working.. haha..
i did not figure out till now why its not running with the timer but its doing with the button

not in this case when you only have one value and no operations. The compiler will pick the smaller integral type where the value fits. So here it would be a long.

So I don't really need it but it's a good habit to take to give a hint to the compiler what you want to do. Here I'm forcing unsigned long.


if you press the button my code resets the 4h delay as commented in the relayOn() call

just remove that line if you want 4 hours no matter what you did with the button.

I changed the code now a bit because after some testing i found that RelayOFF / ON are reversed, now i think thats working..

After that i testet today and it seams (tryed with 10sek intervall) after some times the board gets freezed during a re-start because my ESC is sucking to much power for the Motor from the 12 to 5v mini BEC i am using.

I switched the wiring now so that the ESC is powerd all the time and only the Motor gets the input through the Relay, before it was that the ESC is getting powerd for the 200ms.. but that looked like its not working,.. so i will let it run now with 1 hour timer, and a littly sticky note on my motor too see what is going on =)

that was the purpose of

you needed to adjust those

power, noise, etc are to be taken care of when playing with motors

It's when people multiply literals that they get into overflow trouble.