0

I'm writing a library for arduino which at a certain point must call a function after a fixed delay without using the delay function.

Since standard c++ libraries are not included, I have to use arduino libraries, and I opted for FlexiTimer2 which is useful to run a function every X time units (this is just an introduction, the problem is not arduino related but c++ related).

.h file

#ifndef StepperDriverController_h
#define StepperDriverController_h

#include <stdlib.h>
#if ARDUINO >= 100
#include <Arduino.h>
#else
#include <WProgram.h>
#include <wiring.h>
#endif

class StepperDriverController
{
    public:
        StepperDriverController(unsigned char step, unsigned char direction);

        void setPulseWidth(unsigned short width);
        // other methods

    protected:
        void stepPinDown();
        void step(bool blocking = true);

    private:
        unsigned char _pins[2];
        // some attributes
};
#endif

.cpp file

#include "StepperDriverController.h"
#include <Thread.h>

void StepperDriverController::stepPinDown()
{
    digitalWrite(_pins[0], LOW);
    FlexiTimer2::stop();
}

void StepperDriverController::setPulseWidth(unsigned short width)
{
    _pulseWidth = width;
    FlexiTimer2::set(width, 1/1000000, stepPinDown);
}

void StepperDriverController::step()
{
    digitalWrite(_pins[1], _direction ? HIGH : LOW);

    digitalWrite(_pins[0], HIGH);
    FlexiTimer2::start();
}

I get invalid use of non-static member function on code FlexiTimer2::set(width, 1/1000000, stepPinDown); and '_pins' was not declared in this scope on digitalWrite(_pins[0], LOW);

I've read many questions related to this error but couldn't find a way to solve it anyway

EDIT

FlexiTimer should not be instantiated, but used as I did, as shown in an official library example

Frank
  • 81
  • 1
  • 9
  • You probably want to have an instance of `FlexiTimer2` as a class member variable of `StepperDriverController`. Also add the scope `StepperDriverController::`before your function definitions.` – πάντα ῥεῖ May 09 '19 at 17:49
  • 4
    I'd recommend you should read a [good book](https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list) about c++ basics. – πάντα ῥεῖ May 09 '19 at 17:54
  • @πάντα ῥεῖ actually if I had not a library but for example a main function, it would work because FlexiTimer is not a class but a namespace and it works exactly like that. Have a look at this: https://codebender.cc/example/FlexiTimer2/FlashLed#FlashLed.ino – Frank May 10 '19 at 14:11

1 Answers1

1

The FlexiTimer2::set() overload you are trying to use is declared like this:

namespace FlexiTimer2 {
    // ...
    void set(unsigned long units, double resolution, void (*f)());
}

The third argument is void (*f)(). That's a function pointer to a free function returning void and taking no arguments. However, you are passing a member function. This can't work.

Then you attempted to change stepPinDown() from a member function into a free function. This won't work either, since making it a free function prevents it from accessing the members of StepperDriverController.

What you can do instead is pass a lambda that you force to be compatible with a free function (using the + syntax) like this:

FlexiTimer2::set(width, 1/1000000, +[]{ /* your code here */ });

Now the problem is how to get access to a StepperDriverController instance and call stepPinDown() on it. Normally with lambdas, you would simply do a lambda capture of the instance:

[&obj]{ obj.stepPinDown(); }

However, such a lambda can not be passed to a function that expects a function pointer. The + notation in +[]{ /* your code here */ } is just there to make it clear that the lambda must not capture anything. You can omit the + if you don't do a capture; it's purely there as a means of having self-documenting and clearer code.

So since you are not allowed to do a lambda capture, and I do not know the exact internals of your code, it's not clear how to best call stepPinDown() inside the lambda. But it seems that StepperDriverController is a singleton object, meaning there's only ever one instance of it. So you could have a global pointer to that instance and use that in the lambda:

FlexiTimer2::set(width, 1/1000000, +[]{ globalInstance->stepPinDown(); });

I can't tell you what the proper solution is in your case, but this is the gist of it.

There's also an issue that's unrelated to any of this, but it's worth pointing out:

1/1000000

This does not do what you think it does. This is integer division and the result will be 0. You need:

1.0/1000000.0

In order to get 0.000001 as the result.

Nikos C.
  • 50,738
  • 9
  • 71
  • 96
  • Okay sorry, it was a typo of mine because I had tried many codes before actually posting here. In fact the error is still **invalid use of non-static member function** at *'void StepperDriverController::setPulseWidth(short unsigned int)'* – Frank May 10 '19 at 14:35
  • @Frank OK, now I understand the issue. Answer updated. – Nikos C. May 10 '19 at 15:01
  • 1
    Ooh I like the `+`. Not necessary at all but presumably leads to better diagnostics if you forget not to capture anything? – Lightness Races in Orbit May 10 '19 at 15:05
  • @LightnessRacesinOrbit Kind of. If you omit the `+` and then do a capture, the error message you get will be about the lambda not being convertable to a function pointer. With the `+`, it will be about the lambda itself. It's not really better though. Both are confusing if you don't know what's going on. But since a `+` forces the lambda to be generated as a free function and thus prevents captures, it kind of seems more "correct" to me. – Nikos C. May 10 '19 at 15:29
  • @Nikos I _might_ start doing it :) – Lightness Races in Orbit May 10 '19 at 15:31
  • 1
    (I would suggest clarifying this a little in your answer, though, because at the moment it kind of reads like the `+` is necessary for the conversion to a fptr) – Lightness Races in Orbit May 10 '19 at 15:32
  • @LightnessRacesinOrbit Yep. Added a note about it. – Nikos C. May 10 '19 at 15:42
  • Nice one. Take care! – Lightness Races in Orbit May 10 '19 at 15:44
  • I fixed the typo. Anyway I didn't understand the logic in passing a global instance in the definition of the library, the instance should be this which raises *was not captured for this lambda function* – Frank May 12 '19 at 18:30
  • @Frank You only have one `StepperDriverController` instance in your program, correct? If yes, then use a global pointer to it, or add a static member function to `StepperDriverController` that returns a pointer to that single instance and use that inside the lambda. – Nikos C. May 12 '19 at 18:33