matthewturner / Eventually

A library for event-based programming to make Arduino programming more fun and intuitive

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

How to work with classes?

leosdad opened this issue · comments

Hi, I'm working on a complex project where I need to work with classes. So I tried the SimpleButtonBlink example, but since resetContext() does not exist anymore I replaced it with reset(), changed pins and made a couple more changes.

#include <Eventually.h>

#define LIGHT_PIN  13
#define BUTTON_PIN 3

bool speed = LOW;
bool pin_state = LOW;
EvtManager mgr;

bool blink()
{
  pin_state = !pin_state;
  digitalWrite(LIGHT_PIN, pin_state);
  return false;
}

bool run()
{
  mgr.reset();
  // Won't this call overflow the stack???
  mgr.addListener(new EvtPinListener(BUTTON_PIN, 20, LOW, (EvtAction)&run));
  mgr.addListener(new EvtTimeListener(speed == HIGH ? 100 : 500, true, (EvtAction)&blink));
  speed = !speed;
}

void setup()
{
  pinMode(LIGHT_PIN, OUTPUT);
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  run();
}

void loop()
{
  mgr.loopIteration();
}

So far so good, worked without problems. Because I need classes in my project, next step was to create a Blinker class to encapsulate the main functionality:

#include <Eventually.h>

#define LIGHT_PIN  13
#define BUTTON_PIN 3

EvtManager mgr;

class Blinker
{
  private:
    bool pin_state = HIGH;
    bool speed = HIGH;
    uint8_t lightPin;
    uint8_t btnPin;
    bool blink();

  public:
    Blinker(uint8_t led, uint8_t button);
    bool run();
};

Blinker::Blinker(uint8_t led, uint8_t button)
{
  lightPin = led;
  btnPin = button;
}

bool Blinker::blink()
{
  pin_state = !pin_state;
  digitalWrite(lightPin, pin_state);
  return false;
}

bool Blinker::run()
{
  mgr.reset();
  // Won't this call overflow the stack???
  mgr.addListener(new EvtPinListener(BUTTON_PIN, 20, LOW, (EvtAction)&run));
  mgr.addListener(new EvtTimeListener(speed == HIGH ? 100 : 500, true, (EvtAction)&blink));
  speed = !speed;
}

Blinker blinker = Blinker(LIGHT_PIN, BUTTON_PIN);

void setup()
{
  blinker.run();
}

void loop()
{
  mgr.loopIteration();
}

But this code doesn't work. The private fields do not seem to retain state, but that's just a guess. I made several other attempts, including passing a pointer to mgr and creating the listeners as variables inside the class (equivalent to the "consider declaring the listeners as global variables..." part in the README). No luck.

It might doing something terribly wrong here, but after several attempts I still have no clue.

Hi, I should probably say that if you are doing something really complex and you have space to play with FreeRTOS might be a good solution.

But that said, I've been able to do a few reasonably complex projects in a Nano and been quite happy with the result.

First question: I don't think the run call will overflow, as you are just registering the callback and not actually executing it.

Second question: I've tried to do something similar with classes and I couldn't get the callback to fire. I'm not an expert in C++ but I wondered whether the callback has to be a static method rather than an instance method?

In the end I favoured keeping the manager at the entry point (in setup and loop) and then delegate to my classes. Little bit of extra code but worked quite well. I would love to see an alternative though.

I have a project called massive-clock which uses the above technique and a couple of other Eventually based libraries you might find useful: EventuallyStateMachine and EventuallyCommand.

Let me know how you get on!

Hi Matt, maybe I'll consider FreeRTOS in the future, but for this project (a toy slot machine) I'm using a Mega 2560 board with a special shield. I'm not too far from my goal, actually, but the number of simultaneous events is growing, so I feel I need to work with events now.

In the end I favoured keeping the manager at the entry point (in setup and loop) and then delegate to my classes. Little bit of extra code but worked quite well. I would love to see an alternative though.

I'd love to see how this would work with the blinker, perhaps you can find some time to add it to the examples. BTW, I don't see a complete example that subclasses EvtListener (other than the EvtAlwaysFiresListener in the Readme). Just some suggestions ;)

I'm also testing a much bigger library called IoAbstraction with which I was able to create a working listener class:

#include <IoAbstraction.h>

#define LIGHT_PIN  13
#define BUTTON_PIN 3

class MyListener : public SwitchListener
{
  private:

    uint8_t _ledPin;
    bool pinState = LOW;
    bool flashing = false;

    void onPressed(uint8_t buttonPin, bool held) override
    {
      if(!held) {
        flashing = !flashing;
      }
    }

    void onReleased(uint8_t buttonPin, bool held) override { }

  public:

    MyListener(uint8_t ledPin)
    {
      _ledPin = ledPin;
    }

    void togglePin()
    {
      if(flashing) {
        pinState = !pinState;
        digitalWrite(_ledPin, pinState);
      } else {
        digitalWrite(_ledPin, LOW);
      }
    }
};

MyListener btnListener = MyListener(LIGHT_PIN);

void setup()
{
  switches.init(ioUsingArduino(), SWITCHES_POLL_EVERYTHING, true);
  switches.addSwitchListener(BUTTON_PIN, &btnListener);
  taskManager.scheduleFixedRate(100, []{ btnListener.togglePin(); });
}

void loop()
{
  taskManager.runLoop();
}

...but I like Eventually better because it's much simpler and I'm also interested in EventuallyStateMachine which is something IoAbstraction doesn't have. Thanks fow now, I'll keep on testing.