Use of timer-library in custom library (cannot call function)

Hi there

I'm trying to build a library for a motor, which allows me to send commands through a parallel IO-interface (various pins to set HIGH or LOW which can then be interpreted by the motor-controller). So far, I did this with functions in my main ino-file. But now, since everything gets a bit bigger, I would like to clean up a bit and put everything relating to that motor in a library.

Now the problem:
the IO-interface works so far, but when set for example the DRIVE-pin to HIGH, I want this pin after a delay to go LOW again (of course I could do this by a delay(), but I hope it is understandable that I do not want this).

For this purpose, I use the library <SimpleTimer>, with which I can exactly set up to do so (well, at least in my theory :confused: :)).

Below the code of the library I reduced to the main problem:

/* ley32cIO.h */

#ifndef LEY32CIO_h
#define LEY32CIO_h

#include "Arduino.h"
#include "SimpleTimer.h"

class ley32cIO
{
  public:
    ley32cIO();
    void begin();
    void tick();

  private:
    SimpleTimer *timer;
    int resetDelay = 50;

    void Drive(bool command);
    void Drive();
    void ResetDrive();
};

#endif
/* ley32cIO.cpp */

#include "Arduino.h"
#include "ley32cIO.h"

ley32cIO::ley32cIO() {
  timer = new SimpleTimer;
}

void ley32cIO::begin() {
}

void ley32cIO::tick() {
}

void ley32cIO::Drive(bool command) {
  /*...other commands...*/
  timer->setTimeout(resetDelay, ResetDrive);
}

void ley32cIO::Drive() {
  Drive(true);
}

void ley32cIO::ResetDrive() {
  Drive(false);
}

When I try to run this code, I get the following error-message:

sketch\ley32cIO.cpp: In member function 'void ley32cIO::Drive(bool)':
ley32cIO.cpp:19:43: error: invalid use of non-static member function 'void ley32cIO::ResetDrive()'
   timer->setTimeout(resetDelay, ResetDrive);
                                           ^
In file included from sketch\ley32cIO.cpp:4:0:
sketch\ley32cIO.h:22:10: note: declared here
     void ResetDrive();
          ^~~~~~~~~~
exit status 1
invalid use of non-static member function 'void ley32cIO::ResetDrive()'

If I then declare the void ResetDrive to static, this error disappears. But then I get a new error which tells me, that I cannot call the DRIVE-function without object (because I'm now static):

/* ley32cIO.h */
/*...rest of .h-file...*/

static void ResetDrive();
sketch\ley32cIO.cpp: In static member function 'static void ley32cIO::ResetDrive()':
ley32cIO.cpp:27:10: error: cannot call member function 'void ley32cIO::Drive(bool)' without object
   Drive(0);
          ^
exit status 1
cannot call member function 'void ley32cIO::Drive(bool)' without object

What do I need to change to make this work?
I would like to use SimpleTimer, because I will use this library at other places in my project and would like to always use the same library.

Hope all is clear. Many thanks in advance for any help

Cheers raymeout

as defined, ResetDrive() is a method that is not static: simply put it's not a class method, it requires an instance to exist and thus a context.

The pointer to this "function" you use in timer->setTimeout(resetDelay, [color=red]ResetDrive[/color]); does not mean anything in itself, it would require an instance context to be used. C++ does not allow this.

The simpleTimer library is intended to often be used with only one instance and then deal with timerIDs, it's not well suited for what you are trying to do (need to call run on the instance to update each "task")

As simpleTimer is based on polling for time, you could very easily embed the very same functionality into the ley32cIO, instead of having a simpleTimer instance, just memorize the value of millis() at the starting point (say in a variable startTime) and then in the main loop you poll every instance of ley32cIO to see if they are to done (call a method that will check if the allocated time has elapsed if (millis() - startTime >= duration) {...}

Thanks for your explanation.

The reason, I wanted to use a timer-instance was, that I will need to set various different reset-timers independently from each other and did not want to create a lot of variables to memorize all those timestamps. It seemed close to use a already fully implemented solution for this tasks. But well, if that's not possible, I will have to go for this "alternative" way.

But this means, that using libraries which use callbacks is not possible inside classes like mine? For example, if I would want to use the Button2-library (e.g.) inside the ley32cIO-class, this would also result in the very same problem (a quick test resulted in the fact, that I had to define the callback functions as well static)?

Because, if I would for example like to use singleclick/doubleclick functionalities - which Button2 would provide - I could not use those, but instead had to implement this functionality by myself (simply put: reimplementing the wheel)? Or can't I say this in general for those examples?

Thank you, raymeout

If you want different timeout you will need memory to store them regardless of the solution. Using the library you mention actually allocates a static array for all possibles tasks so wastes memory compared to embedding this storage in your instance - then you will have the exact count needed.

Callbacks are possible within class but you need a class method - something that does not depend on any instance variable. Given what you are trying to do (stop something after a while) I don’t see any use for it. Just keep a list of all your instances and ping them at every loop so that they check if their time is over.

Hi

Please have a look to my lib easyRun.h
Install and test examples.

You will be able to do exactly what you want.

It provides many classes to handle some kind of scheduled tasks, buttons up to tripleclick, etc.
Each class is designed to be used with external callbacks, but can also be easily derived with methods as callbacks.

raymeout:
But this means, that using libraries which use callbacks is not possible inside classes like mine?

The cleanest way I've found for allowing instance functions as callbacks is to make sure that the library offering the callback supports Parameterized Callbacks. One such library is arduino-timer. See my Reply #2 in this thread.

Another such library is OneButton.

Another alternative is my millisDelay class for multiple timers see
my tutorial How to write Timers and Delays in Arduino covers one shot and repeating and re-triggering timers

I really think going to libraries is just increasing complexity for something really simple as there is really no need for a callback, everything happens within the instance, so can be dealt with upon timeout in the instance

here is a quick example code with 2 instances ticking and having a different timeout

Ley32cIO.h

#ifndef LEY32CIO_h
#define LEY32CIO_h
#include "Arduino.h"

class Ley32cIO
{
  public:
    Ley32cIO(const char * id, uint32_t timeOut = 1000);
    void begin();
    bool tick();

  private:
    const char* name;
    uint32_t timerTimeout;
    uint32_t startTime;
};
#endif

Ley32cIO.cpp

#include "Ley32cIO.h"

Ley32cIO::Ley32cIO(const char * id, uint32_t timeOut) : name(id), timerTimeout(timeOut) {}

void Ley32cIO::begin() {
  startTime = millis();
}

bool Ley32cIO::tick() {
  bool timedOut = millis() - startTime >= timerTimeout;
  if (timedOut) {
    Serial.print(millis() / 1000.0, 1); Serial.write('\t'); Serial.print(name); Serial.println(F(" -> TIME'S UP"));
    startTime = millis(); // restarting it
  }
  return timedOut;
}

test.ino

#include "Ley32cIO.h"
Ley32cIO leyArray[] = {{"instance 1", 1000}, {"instance 2", 2500}};

void setup() {
  Serial.begin(115200);
  for (auto & l : leyArray) l.begin(); // start each instance
}

void loop() {
  for (auto & l : leyArray) l.tick(); // tickle each instance
  // here you can do something else
}

Serial Monitor (@ 115200 bauds) will show

[color=purple]
1.0 instance 1 -> TIME'S UP
2.0 instance 1 -> TIME'S UP
[color=red]2.5 instance 2 -> TIME'S UP[/color]
3.0 instance 1 -> TIME'S UP
4.0 instance 1 -> TIME'S UP
5.0 instance 1 -> TIME'S UP
[color=red]5.0 instance 2 -> TIME'S UP[/color]
6.0 instance 1 -> TIME'S UP
7.0 instance 1 -> TIME'S UP
[color=red]7.5 instance 2 -> TIME'S UP[/color]
8.0 instance 1 -> TIME'S UP
9.0 instance 1 -> TIME'S UP
[color=red]10.0 instance 2 -> TIME'S UP[/color]
10.0 instance 1 -> TIME'S UP
11.0 instance 1 -> TIME'S UP
12.0 instance 1 -> TIME'S UP
[color=red]12.5 instance 2 -> TIME'S UP[/color]
13.0 instance 1 -> TIME'S UP
14.0 instance 1 -> TIME'S UP
[color=red]15.0 instance 2 -> TIME'S UP[/color]
...
[/color]

Thank you all for the replies.
I implemented now a very simple class which does kind of exactly what drmpf's library does. Start the timer with timer_eg.runIn(30) and then check every tick() if the timer is due.

Also thank you for your example J-M-L... But I think I did specify my requirements a bit unclear. I need various reset-timers(/delays) inside EVERY instance of ley32cIO. If I only had to memorize only one timestamp per instance, I had taken your approach right away (which I now did, but putting it in a class).
I now moved all this variables inside a timer-class so there are not a lot variables around just for this timing (but only the timer instances and the check in tick() ).

But good to know about the parametrized callbacks. I did already see this functions in the header-file of OneButton already, but did not fully understand what its purpose is. Now it's a bit more clear.
And might look back to the easyRun-library which contains a lot of things, I sometimes look for.

Cheers, raymeout

raymeout:
But good to know about the parametrized callbacks. I did already see this functions in the header-file of OneButton already, but did not fully understand what its purpose is. Now it's a bit more clear.

There are examples of using OneButton's parameterized callback in Reply #5 and #9 in this thread.

And might look back to the easyRun-library which contains a lot of things, I sometimes look for.

If easyRun does not have this feature, you could fork the library on GitHub, implement it yourself, and then submit a Pull Request back to the author to have it formally incorporated.

BTW, the Arduino cores for ESP8266 and ESP32 include versions of attachInterrupt() that support this concept. That makes some interrupt-related coding much easier.

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