zeitgeist87/InterruptHandler

Multiple attachments

Closed this issue · 5 comments

I'm new to C++, so please, forgive me if I'm wrong, but this allows for only one attachment per object, correct?

For instance, if I want an attachment on CHANGE and on LOW, it will only call the handler method for the last one. How could I achieve multiple attachments for each object?

I'm new to C++, so please, forgive me if I'm wrong, but this allows for only one attachment per object, correct?

Well it depends on what exactly you mean by that. You can attach one object to two different pins. You only have to short pin 2 and 3, so that they get the same signals:

class MyClass : InterruptHandler {
  byte inputPin;
  byte inputPin2;
  
public:
  MyClass(byte pin = 2, byte pin2 = 3) : inputPin(pin), inputPin2(pin2) {}
  
  void begin() {
    pinMode(inputPin, INPUT);
    pinMode(inputPin2, INPUT);
    attachInterrupt(digitalPinToInterrupt(inputPin), CHANGE);
    attachInterrupt(digitalPinToInterrupt(inputPin2), LOW);
  }

  virtual void handleInterrupt(int8_t interruptNum) {
  }
};

But every pin can only be attached to one object in one mode. I think that is a hardware limitation. The stock attachInterrupt works the same way.

For instance, if I want an attachment on CHANGE and on LOW, it will only call the handler method for the last one. How could I achieve multiple attachments for each object?

You could do something like this, but I don't think it is exactly what you want, and the performance is probably a little bit worse. The function digitalRead() is pretty slow, but you can find faster versions online. Just google "arduino faster digitalRead".

class MyClass : InterruptHandler {
  byte inputPin;
  
public:
  MyClass(byte pin = 2) : inputPin(pin) {}
  
  void begin() {
    pinMode(inputPin, INPUT);
    attachInterrupt(digitalPinToInterrupt(inputPin), CHANGE);
  }

  void stop() {
    detachInterrupt(digitalPinToInterrupt(inputPin));
  }

  void onRise() { /* do something */ }
  void onFall() { /* do something */ }

  virtual void handleInterrupt(int8_t interruptNum) {
    byte value = digitalRead(inputPin);
    if (value)
      onRise();
    else
      onFall();
  }
};

Hmm, I didn't know about that attachment limitation. Unfortunately, as the UNO only have 2 interrupt pins, I can't short it to another pin because I'll need both of them.

Your solution won't solve what I need. To give you some context, currently I'm writing code to use a button to input a two digit value. When the user taps the button (push and release quickly) he increments the current digit and when he holds the button (long press) he moves to the other digit.

So on CHANGE, if the button is pressed, I'd store the current timestamp and when the button is released, I'd compare the current timestamp with the previous one to decide if it's a tap or a long press. However, I would not be able to change the editing digit before the user releases the button, which is an issue.

That's why I thought I would need CHANGE and LOW, because while it's LOW, I'd call a callback function sending the current time the button is being pressed.

Is there a "timeout" function? If so, it would be possible to only use LOW triggering the "release" callback when the timeout fires, because the code would be assuming LOW isn't called because it's not the current value anymore.

You can do your updates in the normal loop() function. You don't have to do everything in the interrupt handler. How about this (I haven't actually tested it!):

class TwoDigitDisplay : InterruptHandler {
  byte inputPin;
  
public:
  TwoDigitDisplay(byte pin = 2) : inputPin(pin) {}
  
  void begin() {
    pinMode(inputPin, INPUT);
    attachInterrupt(digitalPinToInterrupt(inputPin), CHANGE);
  }

  void stop() {
    detachInterrupt(digitalPinToInterrupt(inputPin));
  }

  void update() {
    /* Interpret the timestamp and decide if it was a long press,
       then update the display. If it was a long press, then clear the timestamp */
  }

  void onRelease() {
    /* Clear the timestamp, and update the display if it was a short tap */
 }
  void onPress() { 
    /* Save the timestamp */
  }

  virtual void handleInterrupt(int8_t interruptNum) {
    /* Modify this to work with your button */
    if (digitalRead(inputPin))
      onRelease();
    else
      onPress();
  }
};

TwoDigitDisplay disp(2);

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

void loop() {
  disp.update();
}

Yes, I guess that update would work. I'll try it out.

It's working perfectly. Thanks