<!-- READY FOR REVIEW --> #How to debounce a GP Input in a python script using acmepins <abstract> How to debounce a GP Input in a python script using acmepins </abstract> In the <a HREF="https://www.acmeSystems.it/gpio">classical article</a> (in the Python subsection) there are several examples about how to read GPIO (General Purpose Input Output), both in polling and event mode, using <a HREF="https://github.com/AcmeSystems/acmepins">acmepins.py</a> module. ##Why is debouncing needed ? Really it is. Lets consider a possible, naively written, implementation that could be as follows: <pre class="prettyprint"> from acmepins import GPIO from time import sleep from time import time Button=GPIO('PC17','INPUT') def event_handler(): print ("Status: %d" % Button.digitalRead()) Button.set_edge("both",event_handler) i=0 while True: print i i=i+1 sleep(0.5) </pre> Alas, testing the avove code, we can see, quite often, that simply pressing just once the button, we get more events than expected: <pre class="terminal"> ... ... 3 4 5 Status: 0 Status: 1 6 7 Status: 0 Status: 1 8 Status: 0 Status: 1 Status: 1 9 ... ... </pre> this is caused by the mechanical structure built-in into the physical switch: a small and very thin plate that is behaving as a small spring that is bouncing back and further. That, together with the extremely fast reading rate of CPU and its I/O circuit, make the simple software above to read all the bounces as regular events. So, some smarter technique is in order to avoid that unconvenience. ##Debounce class Here a simple debouncing class is presented: <pre class="prettyprint"> from acmepins import GPIO from time import time class PushButton(GPIO): OFF = 0 PRESSED = 1 def __init__(self, name): GPIO.__init__(self,name,'INPUT') self.status = PushButton.OFF self.x = 0.0 self.set_edge("both",self.event_handler) def pressed (self): pass def released (self): pass def event_handler(self): if (self.status == PushButton.OFF): if self.digitalRead() == 0: self.status = PushButton.PRESSED self.x = time() self.pressed() elif (self.status == PushButton.PRESSED): if self.digitalRead() == 0: self.x = time() else: if (time() - self.x) > 0.005: self.status = PushButton.OFF self.released(); else: printf ("Fatal error: status unknown: %d" % self.status) </pre> The <b>PushButton</b> class is derived from GPIO in acmepins: that is quite straightforward as in our context a push button is, from the software perspective, a GPIO. Some purist could argue here that a relation has-a captures better, in general, the concept of push button because, after all, there are even push buttons built upon other mechanism. Nonetheless, we use a simple subclassing. Our constructor (__init__ method), take care to built the parent object: <pre class="prettyprint"> def __init__(self, name): GPIO.__init__(self,name,'INPUT') ... </pre> Later in the ctor, the event handler is registered in the parent class: <pre class="prettyprint"> self.set_edge("both",self.event_handler) </pre> note that set_edge method is inherited from parent class GPIO and the <i>OFF</i> and <i>PRESSED</i> are class static variables, in other words, are variables shared by all instances of the class. <i>pressed</i> and <i>release</i> are two placeholder do-nothing methods, that are recalled when the respective events are detected (more on this later). <i>event_handler</i> is the callback that implements the debouncing logic. A small state machine is defined here, with two states, OFF and PRESSED. When the push button is in its default position, not surprisingly the state is OFF. Once the user press the button, the handler is recalled (as the GPIO switches from 1 to 0 and we put "both" as first parameter in set_edge) and the first if branch is followed (status == OFF and digitalRead == 0): the state is then changed to PRESSED and the current time is noted in x variable. Lastly the pressed() method is called. Now, even if, due to the mechanical behavior mentioned, the GPIO changes repeatedly from 0 to 1 and back to 0, as we are in PRESSED state, the pressed() method is ever called again. Each time the GPIO bounces to 0, the timestamp in x is updated, whilst if it remains to 1 for more than 5 ms (showing that button is back to default unpressed status), the state is changed to off and virtual method released() is called. ##How to derive a more specific worker class So, in order to exploit that class one has to derive (again) a more specific class that is able to do a useful job. First thing to do is to declare the new class, so it is derived from the parent one: <pre class="prettyprint"> class MyButtonClass(PushButton): </pre> next a proper contructor has to be defined, where the parent's constructor is explictly called: <pre class="prettyprint"> def __init__(self, name): PushButton.__init__(self, name) </pre> in our case we passed the string identifying the GIOP pin, up to parent class. Lastly, the two workers method are redefined accordingly to our purposes: <pre class="prettyprint"> def pressed (self): print ">>> Button pressed" def released (self): print ">>> Button released" </pre> here we are just printing some informative message. Below you find a working example that, when run on Arietta, is displaying the actions done on the embedded white push button. Note that in most cases, only the pressed() method is useful, as we need to react immediately to user input. <pre class="prettyprint"> from gpio_classes import PushButton from time import sleep class MyButtonClass(PushButton): def __init__(self, name): PushButton.__init__(self, name) def pressed (self): print ">>> Button pressed" def released (self): print ">>> Button released" button = MyButtonClass('PC17') i=0 while True: print button.status, i i=i+1 sleep(0.5) </pre> ##Link Really, on this specific topic, there is a quite large literature, for a good recap see, for example: * [Jack Ganssle: A Guide to Debouncing](http://www.eng.utah.edu/~cs5780/debouncing.pdf) * [Hadi Esmaeilzadeh: Debouncing switches](http://www.cc.gatech.edu/~hadi/teaching/cs3220/02-2015fa/doc/debounce.pdf) @include='bio_andrea_montefusco'
Le attività del TanzoLab si svolgono ogni mercoledi sera, salvo casi speciali, dalle ore 18:30 presso i locali della Acme Systems srl e consistono in:
Le attività vengono coordinate tramite questo sito, in cui vengono pubblicati tutti i lavori svolti o in via di sviluppo, e tramite un gruppo Telegram con cui per interagire direttamente via chat con gli altri membri.
2018 Ⓒ TanzoLab