bxparks / AceButton

An adjustable, compact, event-driven button library for Arduino that debounces and dispatches events to a user-defined event handler.
MIT License
385 stars 37 forks source link

How to make AceButton work with multiplexed wiring scheme #32

Closed quangvankts closed 4 years ago

quangvankts commented 4 years ago

Hi I have 3 GPIO pins available but want to use 5 buttons using diode multiplexing. Pressing button 1-5 will pull pin 1-3 to ground following this order: Button 1 - pin 1 Button 2 - pin 2 Button 3 - pin 3 Button 4 - pin 1 + 2 Button 5 - pin 2 + 3 Will this work with AceButton ? If yes can you provide some example code if possible ? Thanks

bxparks commented 4 years ago

Hi, That's a really interesting wiring. So with 3 pins and 3 diodes, you could support up to 7 buttons. Currently the library does not support this because the assumption that one button corresponds to one pin is baked into the code.

Initially I thought that it would be very difficult to extend AceButton to support this, without rewriting the core AceButton::check() method. But I got an idea: What if you defined a set of virtual pin numbers (1-7) corresponding to the 7 different possible pull-down states of the 3 actual pins. Let's make the mapping equivalent to the base-2 representation of the actual pins (you'll see why in the code below), so in other words v1=(1), v2=(2), v3=(2,1), v4=(3), v5=(3, 1), v6=(3, 2), v7=(3, 2, 1). I think the only place where the AceButton::mPin is used is in the call to ButtonConfig::readButton(pin). You would create a new subclass of ButtonConfig (it is designed to be subclass-able) called MultiButtonConfig (for example), and in the MultiButtonConfig::readButton(), you map the virtual pin number to the actual pin-combo. So something like this:

class MultiButtonConfig : public ButtonConfig {
  public:
    /**
     * Return state of the virtual 'pin' number, corresponding to the 
     * pull-down states of the actual pins. LOW means that the corresponding
     * virtual pin was pushed.
     */
    int readButton(uint8_t pin) override {
      int s1 = digitlaRead(ACTUAL_PIN1);
      int s2 = digitalRead(ACTUAL_PIN2);
      int s3 = digitalRead(ACTUAL_PIN3);

      // Map the actual pin state combo to the virtual button.
      uint8_t virtualPin = (s1 == LOW) | ((s2 == LOW) << 1) | ((s3 == LOW) << 2); 
      return (virtualPin == pin) ? LOW : HIGH;
    }

  private:
    static const uint8_t ACTUAL_PIN1 = 1;
    static const uint8_t ACTUAL_PIN2 = 2;
    static const uint8_t ACTUAL_PIN3 = 3;
}

Does this point you in the right direction? As much as I would like to test this on real hardware, I unfortunately do not have any spare time right now. I would greatly appreciate it if you were able to verify this idea.

Updated: Fixed an off-by-one error in comparing the virtualPin to pin, since pin numbers start at 1 not 0. Updated: Reverted the off-by-one correction. I was right the first time, virtualPin==0 corresponds to no button being pressed.

quangvankts commented 4 years ago

Thanks for the quick and detailed response ! I'll try it out and report here soon

quangvankts commented 4 years ago

Sorry I'm struggling with the code as I'm still beginner programmer. Can you post a complete example for the mentioned 5 buttons scheme if possible please ? Thanks

bxparks commented 4 years ago

First you should look at the code in examples/TunerButtons and get that to work, so that you understand how multiple buttons work with multiple ButtonConfig objects in the AceButton library. Then try to figure out how to modify that for your diode wiring using the MultiButtonConfig that I outlined above. I'm not 100% sure that it will work, but I think it should. If you are a beginning programmer, don't give up so quickly, you can figure this out. I won't be able to look at this until around the Xmas break time, maybe.

seisfeld commented 4 years ago

This sounds a bit line reverse charlieplexing. Awesome that AceButton is going to support this!

bxparks commented 4 years ago

I haven't digested the charlieplexing wiki page in full detail, but it looks like it's a way to multiplex N(N-1) LEDs using using only N pins (instead of the traditional 2N pins). In other words, it is output multiplexing/decoding. This is about input multiplexing/encoding. [Edit: I missed your reverse, so yes, I guess you could say that. Though I'm not sure this matches the scaling formula of charlieplexing.]

I realized after some research that the wiring described by @quangvankts is a specific implementation of binary encoding. It can be done using using a 74LS148 chip, or discrete logic gates (ANDs and ORs), or using diodes (see http://www.learnabout-electronics.org/Digital/dig44.php). The 74LS148 chip is very interesting, 2 can be chained together to support 16 inputs with only 4 pins.

I created 2 subclasses of ButtonConfig (Encoded8To3ButtonConfig and Encoded4To2ButtonConfig) and verified that the AceButton library works on 3 pins or 2 pins with simple diode encoding. All the event detection (Click, DoubleClick, LongPress, RepeatPress) algorithms seem to just work.

In the near future, when I get the parts, I'll verify the 74LS148 for my own curiosity. But the code itself is basically done. I just need to write some documentation before pushing a release.

bxparks commented 4 years ago

This will be shortly released as v1.4. Thanks to @quangvankts for prompting me down this road, I learned a lot myself. Hope this helps. Closing issue.

quangvankts commented 4 years ago

Sorry I haven't got time till now. Just tried your new update and everything works great. I tried creating two instances of Encoded8To3ButtonConfig classes for more than 8 buttons setup and it works great as well. Thank you for your time and effort on this ! Well done !

bxparks commented 4 years ago

Thanks for verifying! If you need 16 buttons, the 16to4 encoding might be the way to go. I got some ideas about how to handle such large number of buttons. Hope to have some time to look into that in a few weeks.

bxparks commented 4 years ago

I got some unexpected free time this weekend, so I released v1.4.1 yesterday which contains a generalized EncodedButtonConfig class that supports M buttons using N pins, where M = 2^N-1. Details near the bottom of https://github.com/bxparks/AceButton/blob/develop/docs/binary_encoding/README.md. I also created an example program at https://github.com/bxparks/AceButton/tree/develop/examples/Encoded16To4Buttons.