1

I'm currently working on some software to drive the ESCs for a quadcopter. Within my ESC class, I need to initialise the PWM pins and wait around 5s for the ESCs to validate, before sending any other throttle values to them.

At the moment, my class stands as follows.

In ESC.h

class ESC
{

  private:

  int ESCPin;
  bool ESCInitialised;

  //Timer Objects
  SimpleTimer startupTimer;
  void endInit();

  public:
  void initESC(int pin);
  bool isInitialised() const { return ESCInitialised; };
  void setPWM(uint16_t dut);
};

In ESC.cpp

#include "ESC.h"

void ESC::initESC(int pin){
    ESCInitialised = false;
    ESCPin = pin;
    InitTimersSafe();
    SetPinFrequency(ESCPin, 500); 
    pwmWriteHR(ESCPin,32768); 
    startupTimer.setTimeout(5000, endInit);

}

void ESC::setPWM(uint16_t dut){
  pwmWriteHR(ESCPin, dut);
}

void ESC::endInit(){
    ESCInitialised = true;
}

I want the ESCInitialised boolean to remain private, as it shouldn't be modifiable by any other class, so I'm using the public getter method isInitialised() to return it. Once the one-shot timer has ended, it should call the endInit() function to set the ESCinitialised bool to true, so that the main code can determine when to pass new throttle values through.

In its current state, I am receiving the error

In static member function 'static void ESC::endInit()':
ESC.h:14: error: invalid use of member 'ESC::ESCInitialised' in static 
member function
   bool ESCInitialised;

Any advice would be much appreciated.

Dave Moore
  • 1,432
  • 4
  • 12
  • 17

1 Answers1

4

Your problem starts at this point:

startupTimer.setTimeout(5000, endInit);

It looks like, that your method needs to get a function which can be called without any object, so that you have to use a static function.

But a static function, defined in a class, can not access data members from an object! Simply there is no object involved while calling a static function from a class.

If you do not want to make your variable also static, which will end up in making everything static, and has the effect that you can not longer use multiple instances of your class you need a way to bind a object and a method to your callback.

I have no idea how your startupTimer.setTimeout is defined. But if it simply takes a pointer to a callable object, it can be made as this:

 startupTimer.setTimeout(5000, [this](){ endInit(); } );

And the endInit method must be NOT static anymore.

If your timer interface did not providing adding some user data to the callback, you have no chance to set the object data to the callback. Such an interface for a timer object is nearly useless. This makes it impossible to use any kind of OOP. So there is only a chance to make a list of static/ global scope helper functions. THIS IS VERY BAD DESIGN

Here a very bad work around:

template <typename T, int n>
class UglyObjectCallbackHandler
{
    static T* obj;
    static void(T::*callback)();

    public:

    static void SetObj( T* _obj ) { obj = _obj; }
    static void SetCallback( void(T::* _callback)() ) { callback = _callback; }

    static void ForwardCallback() { (obj->*callback)(); }
};

template <typename T, int n> T* UglyObjectCallbackHandler<T,n>::obj;
template <typename T, int n> void(T::* UglyObjectCallbackHandler<T,n>::callback)();

class A
{
    private:
        int i;
        void(*callback)();
        std::function<void()> callback_func= [this](){ f(); };


    public:
        A( int _i ): i{_i} { }

        void f() { std::cout << "Callback fired" << i << std::endl; }
};

class TimerFake
{   
    private:
        void (*ptr)();

    public:
        void SetCallback( void (*_ptr)() ) { ptr = _ptr; }

        void Fire() { (*ptr)(); }


};

int main()
{
    // multiple client instances possible
    A a(9);
    UglyObjectCallbackHandler<A,0>::SetObj( &a );
    UglyObjectCallbackHandler<A,0>::SetCallback( &A::f );

    A b(77);
    UglyObjectCallbackHandler<A,1>::SetObj( &b );
    UglyObjectCallbackHandler<A,1>::SetCallback( &A::f );

    TimerFake timer0;
    timer0.SetCallback( &UglyObjectCallbackHandler<A,0>::ForwardCallback );

    TimerFake timer1;
    timer1.SetCallback( &UglyObjectCallbackHandler<A,1>::ForwardCallback );

    timer0.Fire();
    timer1.Fire();
}

The idea is to keep multiple runtime objects ( like your ESC objects ) into a list which is generated in compile time. So here a template provides the second parameter which is an index to the instance you want to call. Because the template provides a static callback method ( forwarder ), it can be used to be used itself as callback to bad designed interfaces. As said: This is a very bad idea. Better use a full featured Timer lib!

Klaus
  • 24,205
  • 7
  • 58
  • 113
  • Thanks for your help! No joy, I'm afraid - it doesn't like that as a method: `error: no matching function for call to 'SimpleTimer::setTimeout(int, ESC::initESC(int)::)'` – Dave Moore Mar 09 '18 at 11:41
  • @DaveMoore We need the declaration of SimpleTimer::setTimeout. – Klaus Mar 09 '18 at 11:48
  • playground.arduino.cc/Code/SimpleTimer - Thanks! – Dave Moore Mar 09 '18 at 11:57
  • @DaveMoore: Sorry, that ugly doc hints what timer_callback f is... so it is useless for me in that way... – Klaus Mar 09 '18 at 12:35
  • @DaveMoore: I wrote a very bad design workaround for your ugly timer library. Maybe it helps to get your work solved... – Klaus Mar 09 '18 at 13:27