1

I am trying to make a library for Arduino, which requires an interruption, but the problem is that when I try the attachInterrupt function inside my library, there's a problem about attachInterrupt is static. Right now, in the library I have this in the header file:

#ifndef RegletaCMA_h
#define RegletaCMA_h

#include "Arduino.h"

class DimmerGlider {
public:
    DimmerGlider(int min, int max, int increase);

    int getMin() { return _min; }

    int getMax() { return _max; }

    int getIncrease() { return _increase; }

    void setMin(int min) { _min = min; }

    void setMax(int max) { _max = max; }

    void setIncrease(int increase) { _increase = increase; }

private:
    int _min, _max, _increase;
};

class Dimmer {

public:
    Dimmer(DimmerGlider &glider, int outputPin, int t3);

    void Dimmer::begin() {
        pinMode(_outputPin, OUTPUT);
        digitalWrite(_outputPin, false);
    }

    void Dimmer::dim() {
        delayMicroseconds(_DIMMER_counter);
        digitalWrite(_outputPin, true);  // Turn _DIMMER_pinDimmer on
        delayMicroseconds(_t3);
        digitalWrite(_outputPin, false); // Turn _DIMMER_pinDimmer off

        if (_DIMMER_dir) _DIMMER_counter += _glider.getIncrease(); // If dir is negative add _DIMMER_increase
        else _DIMMER_counter -= _glider.getIncrease();                  // If dir is negative substract _DIMMER_increase

        if (_DIMMER_counter > _glider.getMax()) _DIMMER_dir = false; // Change dir if reached bottom
        if (_DIMMER_counter < _glider.getMin()) _DIMMER_dir = true;  // Change dir if reached top

        _DIMMER_lastExecutionTime_micros = micros(); // Reset timer
    }

    void Dimmer::force(bool status) {
        digitalWrite(_outputPin, status);
    }

private:
    DimmerGlider &_glider;

    int _outputPin;

    int _DIMMER_counter = 1000;
    bool _DIMMER_dir = true;

    unsigned long long _DIMMER_lastExecutionTime_micros = 0;

    int _t3;
};

class DimmerImplementor {
public:
    DimmerImplementor(Dimmer dimmerList[], int optoPin);

    void DimmerImplementor::begin(){
        attachInterrupt(_optoPin, dimAll, RISING);
    }

    void DimmerImplementor::dimAllDimmers() {
        for (int c = 0; c < sizeof(_dimmerList); c++)
            _dimmerList[c].dim();
    }

    void DimmerImplementor::dimAll() {
        DimmerImplementor::dimAllDimmers();
    }

private:
    int _optoPin;
    Dimmer _dimmerList[];
};

#endif

and this in the cpp file:

#include "Arduino.h"
#include "RegletaCMA.h"

DimmerGlider::DimmerGlider(int min, int max, int increase) :
        _min(min),
        _max(max),
        _increase(increase) {
}

Dimmer::Dimmer(DimmerGlider &glider, int outputPin, int t3) :
        _glider(glider),
        _outputPin(outputPin),
        _t3(t3) {
}

DimmerImplementor::DimmerImplementor(Dimmer *dimmerList, int optoPin) :
        _dimmerList(dimmerList),
        _optoPin(optoPin) {
    for(int c = 0; c < sizeof(dimmerList); c++){
        DimmerImplementor::dimmers[c] = dimmerList[c];
    }

    for (int c = 0; c < sizeof(dimmers); c++) {
        Dimmer dimmer = dimmers[c];

        dimmer.begin();
    }
}

and when I try to run the library with the Arduino IDE, with this code:

DimmerGlider dimmer1Glider = DimmerGlider(_DIMMER_1_tempMin, _DIMMER_1_tempMax, _DIMMER_1_increase);
Dimmer dimmer1 = Dimmer(
                   dimmer1Glider,
                   _DIMMER_1_pin,
                   _DIMMER_t3
                 );
DimmerGlider dimmer2Glider = DimmerGlider(_DIMMER_2_tempMin, _DIMMER_2_tempMax, _DIMMER_2_increase);
Dimmer dimmer2 = Dimmer(
                   dimmer2Glider,
                   _DIMMER_2_pin,
                   _DIMMER_t3
                 );
DimmerGlider dimmer3Glider = DimmerGlider(_DIMMER_3_tempMin, _DIMMER_3_tempMax, _DIMMER_3_increase);
Dimmer dimmer3 = Dimmer(
                   dimmer3Glider,
                   _DIMMER_3_pin,
                   _DIMMER_t3
                 );

Dimmer dimmers[] = {
  dimmer1, dimmer2, dimmer3
};

DimmerImplementor implementor = DimmerImplementor(dimmers, _DIMMER_pinOpto);

void Dimmer_Init() {
  implementor.begin();
}

It throws the following error:

C:\Users\Arnym\Documents\Arduino\libraries\RegletaCMA/RegletaCMA.h: In member function 'void DimmerImplementor::begin()':

C:\Users\Arnym\Documents\Arduino\libraries\RegletaCMA/RegletaCMA.h:73:44: error: invalid use of non-static member function

         attachInterrupt(_optoPin, dimAll, 3); // 3 is the same as RISING

                                            ^

I have been messing around with this problem for some days and I can't find an answer, so I would be really pleased if you can help me. If you need any more info, don't doubt on asking. Thanks in advance.

2 Answers 2

2

In C++, you need to distinguish between two separate copies of an object, or having a reference to an existing object. @chrisl got it right when he identified that you were trying to create a second copy of DimmerGlider, so he suggested using a reference - and then showed using a pointer (*) instead.

He was right: Dimmer should hold a reference to the DimmerGlider object, but that is done with &. You need to make the following changes to the given code:

In the .h:

class Dimmer {

public:
    Dimmer(DimmerGlider &glider, int outputPin, int t3);
.
.
.
private:
    DimmerGlider &_glider;
.
.

And in the final .cpp:

DimmerGlider::DimmerGlider(int min, int max, int increase) :
              _min(min),
              _max(max),
              _increase(increase) {
}

Dimmer::Dimmer(DimmerGlider &glider, int outputPin, int t3) :
        _glider(glider),
        _outputPin(outputPin),
        _t3(t3) {
}

The syntax for the Dimmer constructor had to change. You were assigning the class members the values from the constructor parameters. References need to be initialised, not assigned. This is the syntax you need (note how they’re before the {?)

I fixed the DimmerGlider constructor code while I was at it...

Also, note the difference between using a reference versus using a pointer. A pointer needs to be dereferenced, so you need to use *ptr and ptr->_min to refer to the original object. A reference is already dereferenced, so you can simply use it like the original object: ref and ref._min.


Edit: If you want to use the function dimAll() as an interrupt handler function, then it cannot be a (non-static) member of a class. All class functions have a hidden this pointer as the first parameter in the argument list, since it has to know which DimmerImplementor's members to modify. Thus you have to declare the function as static. And as soon as you do that, you cannot call dimAllDimmers(), because that functions accesses an instance of DimmerImplementor's variables. Luckily, you have a global object called implementor, so you could use that:

static void DimmerImplementor::dimAll() {
    implementor.dimAllDimmers();
}

But it's usual to make that variable a static member of the class itself:

class DimmerImplementor {
    .
    .
public: // Static variables
    static DimmerImplementor implementor;
    .
    .
}; // DimmerImplementor

You'd initialise that as follows:

DimmerImplementor DimmerImplementor::implementor(dimmers, _DIMMER_pinOpto);
2
  • Thanks, this solved my first problem, but I am still having troubles with attachInterrupt, the compiler throws error: invalid use of non-static member function, see modified question for the full program Commented Jul 6, 2018 at 15:25
  • @ArnyminerZ Check the edit to my answer Commented Jul 11, 2018 at 7:00
0

The error message says, that in the constructor of Dimmer it cannot find a matching constructor of DimmerGlider. The problem here is that you pass the parameters by value, which will create a new variable of the given type and content for the function/constructor call. So when you create a new Dimmer variable you are calling the constructor of the class. Since the parameters of the constructor contain a DimmerGlider instance, it also has to be created for you, which means calling the constructor. When passing variables by value the default constructor (without any parameters) is used. But the DimmerGlider doesn't have a default constructor (as you didn't define it). The compiler then tells you, that is needs some parameters for the constructor to create a DimmerGlider instance.

I don't know a way to pass these parameters for the DimmerGlider constructor directly through the Dimmer constructor. Also I don't think this is desirable for you. You are creating the DimmerGlider first and sure want the Dimmer objects to refer to exactly this instance, not a different (cloned) instance, that was created with the Dimmer constructor.

So my suggestion is the following: Pass the DimmerGlider by reference, meaning you only pass a pointer to the DimmerGlider object. In the constructor definition of Dimmer change to the following:

Dimmer::Dimmer(DimmerGlider *glider, int outputPin, int t3);

The * says, that this variable will not hold an instance of the DimmerGlider class, but only a pointer to one. A pointer is simple to create, because it is basically an unsigned int (a 16 bit memory address).

Then in your sketch you are passing not the variable itself, but only the address in memory (a pointer to the object) by using the &:

Dimmer dimmer1 = Dimmer(
                   &dimmer1Glider,
                   _DIMMER_1_pin,
                   _DIMMER_t3
                 );

Now the constructor of Dimmer doesn't have to create an instance of DimmerGlider, but only a simple pointer of the type Dimmerglider. The & retrieves the memory address (the pointer) of an object instance. The constructor now simply writes the pointer, that it got as a parameter, into the glider variable of type DimmerGlider-pointer.

If you don't understand, how pointers work, please google for a tutorial on this. This isn't something, that can be easily explained in the short question-answer format of this site.

3
  • But that constructor should be the Dimmer constructor, not the DimmerGlider one Commented Jul 4, 2018 at 15:20
  • The constructor you sent, is the Dimmer one, as specified on the line 29 of the header file: Dimmer(DimmerGlider glider, int outputPin, int t3); and then initialized in the cpp file: Dimmer::Dimmer(DimmerGlider glider, int outputPin, int t3) { _glider = glider; _outputPin = outputPin; _t3 = t3; } The DimmerGlider class only needs three int parameters: DimmerGlider::DimmerGlider(int min, int max, int increase) Commented Jul 4, 2018 at 15:25
  • You are right, I haven't seen that, But now I rellay understand whats happening there. I will change my answer.
    – chrisl
    Commented Jul 4, 2018 at 21:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.