I often use the following "schema" for functions that I'll be calling over and over in loop() or derivatives, and that need to run on certain delays:
void DoSomething(){
static unsigned long M = 0;
if (millis() - M < 1000)
return;
code that does something
M = millis();
}
void Another(){
static unsigned long M = 0;
if (millis() - M < 60 * 1000L)
return;
code that does something
M = millis();
}
void loop(){
DoSomething();
Another();
AndSoOn();
}
Now, since those lines look ugly, so to speak, I had the idea of using a macro, so I could write something like this instead:
VOID_MILLIS(DoSomething, 1000){
code that does something
}
VOID_MILLIS(Another, 60 * 1000L){
code that does something
}
So the first thing that came to mind was to look at the ISR macro to see how it works... but I don't seem to get how it "injects" the lines that you write in the body of your code. Here's what it does:
The macro does not really do something and your use of millis() is wrong.
When both 'c' and 'c++' are used, then 'c++' overrides 'c'. Therefor the Arduino uses 'c++' rather than 'c'.
The "__cplusplus" is of course valid, therefor the first part of the macro is used.
The 'c++' and 'c' are not fully compatible. When 'c++' is used, the 'c' language is no longer according to the standard 'c'. You will never notice that, it is only for a very few special situations.
The microcontroller or processor has a table with vectors. When an interrupt occurs, the vector for that interrupt is where the program starts to execute. That is the ISR. In the ISR all the important registers are stored on the stack.
Tutorial about interrupts: https://gammon.com.au/interrupts
The macro only shortens the long definition of an interrupt routine to "ISR". The compiler will generate the same code.
The millis() counts from 0x00000000 to 0xFFFFFFFF and after that it rolls over to 0x00000000 and continues to count.
That rollover happens after 0xFFFFFFFF milliseconds, that is about 50 days.
You should not use the absolute value of millis() and you should not add or substract something to the millis value. Always use millis() relative to a previous millis value.
The BlinkWithoutDelay shows how to use millis(): https://www.arduino.cc/en/tutorial/BlinkWithoutDelay [EDIT] Looking at it once more, I think that is what you are doing. I spoke too soon, sorry for that :-[
Cosme_Fulanito:
I often use the following "schema" for functions that I'll be calling over and over in loop() or derivatives, and that need to run on certain delays:
I like to do this using classes.
You define an abstract superclass:
class StuffDoer {
const unsigned long delay;
public:
StuffDoer(unsigned long _delay) : delay(_delay) {}
void VOID_MILLIS() {
if(/*make the check*/) {
doStuff();
}
}
virtual void doStuff() = 0;
}
And them you would define classes that implement doStuff()
class LightBlinker : public StuffDoer {
public:
LightBlinker(unsigned long _delay) : StuffDoer(_delay) {}
void doStuff() {
// make the light blink
}
}
You can then create arrays or lists of pointers to StuffDoer, and invoke your VOID_MILLIS function on each.
LightBlinker a = LightBlinker(2000);
LightBlinkerWithLabel b = LightBlinker(3000, "whatever");
GateSensor c = GateSensor(1000, A0);
StuffDoer *allmyStuff[] = {&a, &b, &c};
void loop() {
for(int i = 0; i<3; i++) {
allMyStuff[i]->VOID_MILLIS();
}
}
Oh, and allmyStuff can be defined as "pointer to VOID_MILLIS in some StuffDoer instance", and so be called like [nobbc]allMyStuff[i]()[/nobbc] . But that's a little obscure and I'm not 100% sure how you would do it.
And all of the code is clearly visible for when errors need to be dealt with.
...R
Oh yes, it's a matter of taste. The only technical argument one could make I guess is that I've been bitten more than once by my mistake of forgetting to put "static" in front of "unsigned long M", so anything that replaces hand written code is good. Flaky argument that, but hey, I'm willing to argue it anyways.
In any case this is more of an experiment than anything else, to see if what I come up with happens somehow to be better than what I already use... could be the other way around entirely.
So far I've come up with two approaches, not really satisfactory.
#define MILLIS_(ms) {static unsigned long M = 0; if(millis() - M < ms) return;
#define _MILLIS M = millis();
void Something() MILLIS_(1000)
do things
_MILLIS
}
and
#define VOID_MILLIS(fname, ms, ...) void fname(__VA_ARGS__) {static unsigned long M = 0; if(millis() - M < ms) return;
#define END_MILLIS M = millis();}
VOID_MILLIS(DoSomething, 1000)
do something
END_MILLIS
I'd characterize both solutions as "Frankenstein".
The first one is "better" for the sole fact that the Sloeber (Eclipse) formatter, which I use a lot, mauls the second one (END_MILLIS with wrong indentation) and my OCD kicks in.
Yes, it's stupid, but then again, there's no point in doing any of this, I just have the time to experiment.
#define MILLIS_(ms) {static unsigned long M = 0; if(millis() - M < ms) return;
#define _MILLIS M = millis();
void Something() MILLIS_(1000)
do things
_MILLIS
}
Yes it's legal but embedding a single opening brace in a macro is ASKING for trouble. You can't look at the code and see that the braces match and any IDE feature that highlights braces or indents code is going to get it wrong.
Macros are useful for exactly this sort of thing. If you don't like having to type out that line each time then put it in a macro. My take on this is...
#define MILLIS_(ms) do {static unsigned long M = 0; if(millis() - M < ms) return;else M +=ms;} while(false)
void Something() {
MILLIS_(1000);
//do things
}
So, I removed the dangling { and replaced it with the do{}while(0) structure which creates a block that only executes once. This block keeps the temporary variable M private inside the block and plays nice when you put a semicolon on the end when you use the macro. I also fixed the "drift" that occurs when you do M=millis() by adding the interval to M.