Error: Invalid use of non-static member function

I am trying to make my own library for step motor control (28byj48 with ULN2003 driver) based on timer interrupts so that it supposed to be a non blocked code. Bellow is my .h, .ccp and example of it. When compile the example, I get tons of erros, also attached bellow. I wonder to have some advice about how to make my code working... Thanks

//this is the Sm.h

#ifndef Sm_h
#define Sm_h

#include "Arduino.h"
 
class Sm
{
public:
  Sm(int pin4, int pin3, int pin2, int pin1);
  void run(int steps, int vel, char dir);
  int where();

private:
  void onTimer1ms();
  void go();
  int p4,p3,p2,p1,inivel=5;
  volatile int xsteps=0;
  volatile int xfase=0, xvel=5;
  volatile char xdir='R';
};
 
#endif
//This is the .cpp

#include "Sm.h"

//----------------------------------------------------------------------
Sm::Sm(int pin4, int pin3, int pin2, int pin1)
{
  pinMode(pin4,OUTPUT);                              //define the driver motor pins as output
  pinMode(pin3,OUTPUT);                              //
  pinMode(pin2,OUTPUT);                              //
  pinMode(pin1,OUTPUT);                              //

  digitalWrite(pin4,0);                              //initialize then with zeroes
  digitalWrite(pin3,0);                              //
  digitalWrite(pin2,0);                              //
  digitalWrite(pin1,0);                              //

  p4=pin4, p3=pin3, p2=pin2, p1=pin1;                //copy pins to internal variables

  hw_timer_t *timer1ms = NULL;

  timer1ms = timerBegin(0, 80, true);                //prescaler of 80 = 80Mhz/80 = 1us (1Mhz) per tick
  timerAttachInterrupt(timer1ms, onTimer1ms, true);  //usr attached to interrupts (onTimer1ms)
  timerAlarmWrite(timer1ms, 1000, true);             //usr called each 1000us=1Khz
  timerAlarmEnable(timer1ms);                        //start interrupts generation  
}

//----------------------------------------------------------------------
void Sm::run(int steps, int vel, char dir)
{
  inivel=vel;xvel=vel;xdir=dir;xsteps=steps;
}

//----------------------------------------------------------------------
int Sm::where()
{
  return xsteps;
}

//----------------------------------------------------------------------
void onTimer1ms()
{
  xvel--;
  if (xvel==0){
    xvel=inivel;
    if (xsteps>0){
      go();
      xfase++;
      if (xfase==4){xfase=0;xsteps--;}
    }
    if (xsteps==0){digitalWrite(p1, 0);digitalWrite(p2, 0);digitalWrite(p3, 0);digitalWrite(p4, 0);}  
  }  
}

//----------------------------------------------------------------------
void go()
{
  if ((xdir=='R')||(xdir=='r')){
    if (xfase==0){
      digitalWrite(p1, 1); //0x09
      digitalWrite(p2, 0);
      digitalWrite(p3, 0);
      digitalWrite(p4, 1);
    }
    if (xfase==1){
      digitalWrite(p1, 1); //0x03
      digitalWrite(p2, 1);
      digitalWrite(p3, 0);
      digitalWrite(p4, 0);
    }
    if (xfase==2){
      digitalWrite(p1, 0); //0x06
      digitalWrite(p2, 1);
      digitalWrite(p3, 1);
      digitalWrite(p4, 0);
    }
    if (xfase==3){    
      digitalWrite(p1, 0); //0x0C
      digitalWrite(p2, 0);
      digitalWrite(p3, 1);
      digitalWrite(p4, 1);
    }
  }
  if ((xdir=='L')||(xdir=='l')){
    if (xfase==0){
      digitalWrite(p1, 0); //0x0C
      digitalWrite(p2, 0);
      digitalWrite(p3, 1);
      digitalWrite(p4, 1);
    }
    if (xfase==1){
      digitalWrite(p1, 0); //0x06
      digitalWrite(p2, 1);
      digitalWrite(p3, 1);
      digitalWrite(p4, 0);
    }
    if (xfase==2){
      digitalWrite(p1, 1); //0x03
      digitalWrite(p2, 1);
      digitalWrite(p3, 0);
      digitalWrite(p4, 0);
    }
    if (xfase==3){    
      digitalWrite(p1, 1); //0x09
      digitalWrite(p2, 0);
      digitalWrite(p3, 0);
      digitalWrite(p4, 1);
    }
  }
}
//----------------------------------------------------------------------
//This is the use sample

#include <Sm.h>

Sm sm(4,16,17,5);        //define ULN2003 driver motor pins in4,in3,in2,in1

void setup()
{
}

void loop()
{
  while (sm.where()>0){} //wait until step motor arrives to its last destination
  sm.run(512, 5, 'R');   //start one 360 degrees turn right 
  while (sm.where()>0){} //wait until step motor arrives to its last destination
  sm.run(512, 5, 'L');   //start one 360 degrees turn left
}
//Here the tons of example compiling erros

C:\Users\Antonio Testa\Documents\Arduino\libraries\Sm\src\Sm.cpp: In constructor 'Sm::Sm(int, int, int, int)':
C:\Users\Antonio Testa\Documents\Arduino\libraries\Sm\src\Sm.cpp:21:50: error: invalid use of non-static member function 'void Sm::onTimer1ms()'
   timerAttachInterrupt(timer1ms, onTimer1ms, true);  //usr attached to interrupts (onTimer1ms)
                                                  ^
In file included from C:\Users\Antonio Testa\Documents\Arduino\libraries\Sm\src\Sm.cpp:1:
C:\Users\Antonio Testa\Documents\Arduino\libraries\Sm\src\Sm.h:14:8: note: declared here
   void onTimer1ms();
        ^~~~~~~~~~
C:\Users\Antonio Testa\Documents\Arduino\libraries\Sm\src\Sm.cpp: In function 'void onTimer1ms()':
C:\Users\Antonio Testa\Documents\Arduino\libraries\Sm\src\Sm.cpp:41:3: error: 'xvel' was not declared in this scope
   xvel--;
   ^~~~
C:\Users\Antonio Testa\Documents\Arduino\libraries\Sm\src\Sm.cpp:43:10: error: 'vel' was not declared in this scope
     xvel=vel;
          ^~~
C:\Users\Antonio Testa\Documents\Arduino\libraries\Sm\src\Sm.cpp:43:10: note: suggested alternative: 'ceil'
     xvel=vel;
          ^~~
          ceil
C:\Users\Antonio Testa\Documents\Arduino\libraries\Sm\src\Sm.cpp:44:9: error: 'xsteps' was not declared in this scope
     if (xsteps>0){
         ^~~~~~
C:\Users\Antonio Testa\Documents\Arduino\libraries\Sm\src\Sm.cpp:44:9: note: suggested alternative: 'mkstemps'
     if (xsteps>0){
         ^~~~~~
         mkstemps
C:\Users\Antonio Testa\Documents\Arduino\libraries\Sm\src\Sm.cpp:45:7: error: 'anda' was not declared in this scope
       anda();
       ^~~~
C:\Users\Antonio Testa\Documents\Arduino\libraries\Sm\src\Sm.cpp:45:7: note: suggested alternative: 'rand'
       anda();
       ^~~~
       rand
C:\Users\Antonio Testa\Documents\Arduino\libraries\Sm\src\Sm.cpp:46:7: error: 'xfase' was not declared in this scope
       xfase++;
       ^~~~~
C:\Users\Antonio Testa\Documents\Arduino\libraries\Sm\src\Sm.cpp:49:9: error: 'xsteps' was not declared in this scope
     if (xsteps==0){digitalWrite(p1, 0);digitalWrite(p2, 0);digitalWrite(p3, 0);digitalWrite(p4, 0);}
         ^~~~~~
C:\Users\Antonio Testa\Documents\Arduino\libraries\Sm\src\Sm.cpp:49:9: note: suggested alternative: 'mkstemps'
     if (xsteps==0){digitalWrite(p1, 0);digitalWrite(p2, 0);digitalWrite(p3, 0);digitalWrite(p4, 0);}
         ^~~~~~
         mkstemps
C:\Users\Antonio Testa\Documents\Arduino\libraries\Sm\src\Sm.cpp:49:33: error: 'p1' was not declared in this scope
     if (xsteps==0){digitalWrite(p1, 0);digitalWrite(p2, 0);digitalWrite(p3, 0);digitalWrite(p4, 0);}
                                 ^~
C:\Users\Antonio Testa\Documents\Arduino\libraries\Sm\src\Sm.cpp:49:33: note: suggested alternative: 'T1'
     if (xsteps==0){digitalWrite(p1, 0);digitalWrite(p2, 0);digitalWrite(p3, 0);digitalWrite(p4, 0);}
                                 ^~
                                 T1
C:\Users\Antonio Testa\Documents\Arduino\libraries\Sm\src\Sm.cpp:49:53: error: 'p2' was not declared in this scope
     if (xsteps==0){digitalWrite(p1, 0);digitalWrite(p2, 0);digitalWrite(p3, 0);digitalWrite(p4, 0);}
                                                     ^~
C:\Users\Antonio Testa\Documents\Arduino\libraries\Sm\src\Sm.cpp:49:53: note: suggested alternative: 'T2'
     if (xsteps==0){digitalWrite(p1, 0);digitalWrite(p2, 0);digitalWrite(p3, 0);digitalWrite(p4, 0);}
                                                     ^~
                                                     T2
C:\Users\Antonio Testa\Documents\Arduino\libraries\Sm\src\Sm.cpp:49:73: error: 'p3' was not declared in this scope
     if (xsteps==0){digitalWrite(p1, 0);digitalWrite(p2, 0);digitalWrite(p3, 0);digitalWrite(p4, 0);}
                                                                         ^~
C:\Users\Antonio Testa\Documents\Arduino\libraries\Sm\src\Sm.cpp:49:73: note: suggested alternative: 'T3'
     if (xsteps==0){digitalWrite(p1, 0);digitalWrite(p2, 0);digitalWrite(p3, 0);digitalWrite(p4, 0);}
                                                                         ^~
                                                                         T3
C:\Users\Antonio Testa\Documents\Arduino\libraries\Sm\src\Sm.cpp:49:93: error: 'p4' was not declared in this scope
     if (xsteps==0){digitalWrite(p1, 0);digitalWrite(p2, 0);digitalWrite(p3, 0);digitalWrite(p4, 0);}
                                                                                             ^~
C:\Users\Antonio Testa\Documents\Arduino\libraries\Sm\src\Sm.cpp:49:93: note: suggested alternative: 'T4'
     if (xsteps==0){digitalWrite(p1, 0);digitalWrite(p2, 0);digitalWrite(p3, 0);digitalWrite(p4, 0);}
                                                                                             ^~
                                                                                             T4

Hi @AntonioTesta ,

I recommend to check if there is a typo in the function

onTimer1ms()

There is a call to anda() ... Is this a valid function?

And the variable vel is only declared as a parameter in another function. You cannot access it in a different one

It is not a rare case that simple mistakes leads to numerous error messages...

Good luck!
ec2021

Thanks ec2021. I did all your suggestions and correct few small mistakes (edited on my post already) but the result was exactly the same...

Private member functions are still members, so their definitions must be scoped as well:

int Sm::where()  // like this public one
void Sm::onTimer1ms()
void Sm::go()

and then they can access all those other members the compiler is complaining "not declared in this scope" about.

This one is harder. Looks like you're trying to register a callback function. If that function is part of a class, and it is static that is fine, because that's "just a function" that is scoped inside a class. As a static function there would be no class member involved.

That is not the case with onTimer1ms -- it is a member function that accesses other (non-static) members of the class like xvel. The method is "called from" the perspective of the object instance, in effect an object and a function pointer; not just a function pointer.

If you only have one or a few static instances, then the simplest thing is something like

Sm sm(4,16,17,5);

void globalPerMilli() {
  sm.onTimer1ms();
}

void setup() {
  hw_timer_t *timer1ms = timerBegin(0, 80, true);
  timerAttachInterrupt(timer1ms, globalPerMilli, true);
  timerAlarmWrite(timer1ms, 1000, true);
  timerAlarmEnable(timer1ms);            
}

If you need Sm to be more dynamic, and every time you create one, it hooks into the timer -- that's more work.

You cannot implement the timer handling routine inside the class as you try it here (you have to use a static class member!) and I also think that you put too much functionality in the timer handler ...

I recommend to work as follows:

  • Implement the functionality that shall be called every millisecond in a separate function (let's call it "Sm.run()".
  • Let this function be called in loop() as often as possible.
  • Inside Sm.run() you check if a certain public boolean (e.g. "boolean Sm.doRun") has been set to true (then perform the functions and reset the variable) else immediately return.
  • Write a separate timer handler in the main sketch that sets the Sm.doRun to true every millisecond.

This way the timer routine (handler) can be kept very small and quick, independent of the work performed in Sm.run() and you do not have to cope with static class members ...

If you want to integrate the timer handler in you class this post may be of some assistance
https://esp32.com/viewtopic.php?t=26070
at least give you a hint ...

Still I'd recommend to separate the timer routine from the further functionality.

Good luck!
ec2021

Actually, there's a sneaky template trick you can do to call a member function while still supplying a static function to the timerAttachInterrupt() function. I learned it Here.

Here's a minimalist example applicable to @AntonioTesta's class code (compiles but untested):
Sm.h:

#ifndef Sm_h
#define Sm_h

#include "Arduino.h"

class Sm {
  public:
    Sm() {}
    void begin();

  private:
    void onTimer1ms();
    static Sm *isrTable[];

    using isrFunct = void (*)();
    template<uint8_t NUM_INTERRUPTS = SOC_TIMER_GROUP_TOTAL_TIMERS>
    static isrFunct getIsr(uint8_t timerNumber);
};

template<uint8_t NUM_INTERRUPTS>
Sm::isrFunct Sm::getIsr(uint8_t timerNumber) {
  if (timerNumber == (NUM_INTERRUPTS - 1)) {
    return [] {
      isrTable[NUM_INTERRUPTS - 1]->onTimer1ms();
    };
  }
  return getIsr < NUM_INTERRUPTS - 1 > (timerNumber);
}

template<>
inline Sm::isrFunct Sm::getIsr<0>(uint8_t timerNumber) {
  (void) timerNumber;
  return nullptr;
}

#endif

Sm.cpp:

#include "Sm.h"

void Sm::begin() {
  const uint8_t timerNumber = 0;
  hw_timer_t *timer1ms = NULL;
  timer1ms = timerBegin(timerNumber, 80, true);
  auto isr = getIsr(timerNumber);
  timerAttachInterrupt(timer1ms, isr, true);
}

void Sm::onTimer1ms() {
}

Sm *Sm::isrTable[SOC_TIMER_GROUP_TOTAL_TIMERS];

Main .ino:

#include "Sm.h"

Sm sm;

void setup() {
  sm.begin();
}

void loop() {
}
2 Likes

Thanks @gfvalvo , that's an enlightening insight! This solution is somewhere between sneaky and brilliant :wink:

The thread you linked provides excellent background information! Good to know that it is possible when required.

@AntonioTesta ,

all eyes are on you now :slight_smile:
With a little bit of luck (and support by gvalvo) it looks absolutely promising!

ec2021

Thank you guys jfvalvo and ec2021 for precious info. I think my problem is finally fixed !!!

I’d say it’s a brilliantly sneaky way to let the compiler hide the messiness of all those redundant static functions using templates. Expanding the templates, it would look more or less like this:
Sm.ino:

#ifndef Sm_h
#define Sm_h

#include "Arduino.h"

class Sm {
  public:
    Sm() {}
    void begin();

  private:
    using isrFunct = void (*)();
    void onTimer1ms();
    static Sm *isrTable[];
    static isrFunct getIsr4(uint8_t timerNumber);
    static isrFunct getIsr3(uint8_t timerNumber);
    static isrFunct getIsr2(uint8_t timerNumber);
    static isrFunct getIsr1(uint8_t timerNumber);
    static isrFunct getIsr0(uint8_t timerNumber);
};

#endif

Sm.cpp:

#include "Sm.h"

void Sm::begin() {
  const uint8_t timerNumber = 0;
  hw_timer_t *timer1ms = NULL;
  timer1ms = timerBegin(timerNumber, 80, true);
  auto isr = getIsr4(timerNumber);
  timerAttachInterrupt(timer1ms, isr, true);
}

Sm *Sm::isrTable[SOC_TIMER_GROUP_TOTAL_TIMERS];

void Sm::onTimer1ms() {
}

Sm::isrFunct Sm::getIsr4(uint8_t timerNumber) {
  if (timerNumber == (4 - 1)) {
    return [] {
      isrTable[4 - 1]->onTimer1ms();
    };
  }
  return getIsr3(timerNumber);
}

Sm::isrFunct Sm::getIsr3(uint8_t timerNumber) {
  if (timerNumber == (3 - 1)) {
    return [] {
      isrTable[3 - 1]->onTimer1ms();
    };
  }
  return getIsr2(timerNumber);
}

Sm::isrFunct Sm::getIsr2(uint8_t timerNumber) {
  if (timerNumber == (2 - 1)) {
    return [] {
      isrTable[2 - 1]->onTimer1ms();
    };
  }
  return getIsr1(timerNumber);
}

Sm::isrFunct Sm::getIsr1(uint8_t timerNumber) {
  if (timerNumber == (1 - 1)) {
    return [] {
      isrTable[1 - 1]->onTimer1ms();
    };
  }
  return getIsr0(timerNumber);
}

inline Sm::isrFunct Sm::getIsr0(uint8_t timerNumber) {
  (void) timerNumber;
  return nullptr;
}
1 Like

gfvalvo I am sad to say that suggestion you gave in post #6 does not work... It seams that usr routine (onTimer1ms) is not being called as expected. Bellow is my test code. The variable intcount still zeroes all the time but supposed it should be increased by 1 every 1ms... Do you have any idea about why it is not working ? Thanks so much

// Sm.h

#ifndef Sm_h
#define Sm_h

#include "Arduino.h"

class Sm {
  public:
    Sm() {}
    void begin();
    uint32_t getintcount();
    volatile uint32_t intcount=0;

  private:
    void onTimer1ms();
    static Sm *isrTable[];

    using isrFunct = void (*)();
    template<uint8_t NUM_INTERRUPTS = SOC_TIMER_GROUP_TOTAL_TIMERS>
    static isrFunct getIsr(uint8_t timerNumber);
};

template<uint8_t NUM_INTERRUPTS>
Sm::isrFunct Sm::getIsr(uint8_t timerNumber) {
  if (timerNumber == (NUM_INTERRUPTS - 1)) {
    return [] {
      isrTable[NUM_INTERRUPTS - 1]->onTimer1ms();
    };
  }
  return getIsr < NUM_INTERRUPTS - 1 > (timerNumber);
}

template<>
inline Sm::isrFunct Sm::getIsr<0>(uint8_t timerNumber) {
  (void) timerNumber;
  return nullptr;
}

#endif
// Sm.cpp

#include "Sm.h"

void Sm::begin() {
  const uint8_t timerNumber = 0;
  hw_timer_t *timer1ms = NULL;
  timer1ms = timerBegin(timerNumber, 80, true);
  auto isr = getIsr(timerNumber);
  timerAttachInterrupt(timer1ms, isr, true);
}


uint32_t Sm::getintcount() {
  return intcount;
}


void Sm::onTimer1ms() {
  intcount++;
}

Sm *Sm::isrTable[SOC_TIMER_GROUP_TOTAL_TIMERS];
// test.ino

#include "Sm.h"

Sm sm;

void setup() {
  sm.begin();
  Serial.begin(115200);
}

void loop() {
  Serial.println(sm.getintcount());
  delay(1000);
}

As I said, mine was a minimalist example to demonstrate the template technique for creating isr functions, which does indeed work. However, it was not meant as a complete, working example integrated with the timer API. Perhaps you haven’t included all the API calls necessary to get timer interrupts going?

Good question... The problem is that my skill is not enough to put to work such complex issue... !!! I am going to search and study more info about. Thanks

Update your Sm.cpp file:

#include "Sm.h"

void Sm::begin() {
	const uint8_t timerNumber = 0;
	hw_timer_t *timer1ms = NULL;
	timer1ms = timerBegin(timerNumber, 80, true);
    isrTable[timerNumber] = this;
	auto isr = getIsr(timerNumber);
	timerAttachInterrupt(timer1ms, isr, false);
	timerAlarmWrite(timer1ms, 1000000, true);
	timerAlarmEnable(timer1ms);
}

uint32_t Sm::getintcount() {
	return intcount;
}

void Sm::onTimer1ms() {
	intcount++;
}

Sm *Sm::isrTable[SOC_TIMER_GROUP_TOTAL_TIMERS];
1 Like

Bingo !!! It is working. You have saved my skin. Thank you gfvalvo so much.

You also need the IRAM_ATTR attribute for the ISR:

void IRAM_ATTR Sm::onTimer1ms() {
	intcount++;
}

I do not know exactly what the IRAM_ATTR does but I followed your suggestion even it is working well without that attribute. Thanks

Right now the program is trivially small. As its memory use grows, not having that attribute for the ISR may come back and bite you. See: https://esp32.com/viewtopic.php?t=4978

Are you interested in review?

The 70% of the library code is the go() function, which consists of dozens of identical blocks:

 if (xcw){
      if (xfase==0){
        digitalWrite(p1, 0); //0x01
        digitalWrite(p2, 0);
        digitalWrite(p3, 0);
        digitalWrite(p4, 1);
      }
      if (xfase==1){
        digitalWrite(p1, 0); //0x02
        digitalWrite(p2, 0);
        digitalWrite(p3, 1);
        digitalWrite(p4, 0);
      }
      if (xfase==2){
        digitalWrite(p1, 0); //0x04
        digitalWrite(p2, 1);
        digitalWrite(p3, 0);
        digitalWrite(p4, 0);
      }
      if (xfase==3){    
        digitalWrite(p1, 1); //0x08
        digitalWrite(p2, 0);
        digitalWrite(p3, 0);
        digitalWrite(p4, 0);
      }
    }

Put the wrinting to the pins to a separate function and the code can be made about 5 times shorter.

@b707 you are right. I will do that. Tks

Following the @b707 suggestion I did some optimization on cpp (version 2.1) code but I think it would be better !!!

1 Like