Skip to content

Instantly share code, notes, and snippets.

@devyte
Last active September 26, 2018 23:10
Show Gist options
  • Save devyte/d725e283e7eeb87af0ce6b645ad90631 to your computer and use it in GitHub Desktop.
Save devyte/d725e283e7eeb87af0ce6b645ad90631 to your computer and use it in GitHub Desktop.
#include "Arduino.h"
#include "timeout.h"
void runtest1()
{
using namespace esp8266;
polledTimeoutOneShot timeout(3000);
Serial.print("before while 1\n");
while(!timeout.expired())
yield();
Serial.print("after while 1\n");
Serial.print("reset\n");
timeout.reset();
Serial.print("before while 2\n");
while(!timeout)
yield();
Serial.print("after while 2\n");
}
void runtest2()
{
using namespace esp8266;
polledTimeoutPeriodic timeout(3000);
Serial.print("before while 1\n");
while(!timeout.expired())
yield();
Serial.print("after while 1\n");
Serial.print("no reset needed\n");
Serial.print("before while 2\n");
while(!timeout)
yield();
Serial.print("after while 2\n");
}
void runtest3()
{
using namespace esp8266;
polledTimeoutPeriodic timeout(1000);
int counter = 10;
while(1)
{
if(timeout)
{
Serial.print("*");
if(!--counter)
break;
yield();
}
}
Serial.println();
}
polledTimeoutPeriodic loopTimeout(1000);
void setup()
{
Serial.begin(115200);
delay(10);
Serial.println();
Serial.print("runtest1\n");
runtest1();
Serial.print("done\n");
Serial.print("runtest2\n");
runtest2();
Serial.print("done\n");
Serial.print("runtest3\n");
runtest3();
Serial.print("done\n");
loopTimeout.reset();
}
void loop()
{
if(loopTimeout)
Serial.print("#");
}
#ifndef __ESP8266TIMEOUT_H__
#define __ESP8266TIMEOUT_H__
namespace esp8266
{
template<bool PeriodicT>
class polledTimeout
{
public:
using timeType = unsigned int;
polledTimeout(timeType timeout)
: _timeout(timeout), _start(millis())
{}
bool expired()
{
if(PeriodicT)
return expiredRetrigger();
return expiredOneShot();
}
operator bool()
{
return expired();
}
bool reset()
{
_start = millis();
}
protected:
bool checkExpired(timeType t) const
{
return (t - _start) >= _timeout;
}
bool expiredRetrigger()
{
timeType current = millis();
if(checkExpired(current))
{
_start = current;
return true;
}
return false;
}
bool expiredOneShot() const
{
return checkExpired(millis());
}
timeType _timeout;
timeType _start;
};
using polledTimeoutOneShot = polledTimeout<false>;
using polledTimeoutPeriodic = polledTimeout<true>;
}
#endif
@d-a-v
Copy link

d-a-v commented Sep 25, 2018

I like it !
Could we get rid of one variable, and use instead just this one _endOfDelay(millis() + timeout) ?
cons: we lose the reset() feature

Arduino millis() is unsigned long. I'm ok with time_t if it is unsigned ?

@d-a-v
Copy link

d-a-v commented Sep 25, 2018

reset() could be reset(something) instead, with still only one variable.

@d-a-v
Copy link

d-a-v commented Sep 25, 2018

This deserves a PR on @arduino's repo

@earlephilhower
Copy link

Looks great and I like the usage model, but I can foresee people creating the object waaaaay before using it and having it fail on the first call since the constructor was called XXXms ago. Not sure how to guard against it, other than stressing that you make the new object right before using it...

@earlephilhower
Copy link

Minor vote against time_t since that is traditionally used to store seconds, not milliseconds. uint32_t or unsigned long is your friend (and the return type of millis())

@devyte
Copy link
Author

devyte commented Sep 25, 2018

@earlephilhower Sure, some users will make the mistake, but then they can get referred to RAII.
About time_t, you're right, and the internal type isn't visible anyways.

@devyte
Copy link
Author

devyte commented Sep 25, 2018

@d-a-v I implemented with RAII in mind, so it's really meant for stack declaration, ideally within a specific scope. Given that, there is no difference between 2 uints within the class vs. 1 uint in the class and 1 int passed as argument: both cases eventually allocate 2 uints on the stack. The only difference would be in the case when an object is declared globally, then you'd have one uint in the (global) heap and one in the stack.
I think two uints in the stack is better.
Also, the current way shown by @earlephilhower:

uint32_t start = millis(); 
while (millis() - start < timeout) 
{....}

uses 1 uint on the stack plus 1 temp uint also on the stack, so the same :p

@d-a-v
Copy link

d-a-v commented Sep 26, 2018

What about moving yield() in expired() ? (or optimistic_yield())

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment