Library for Debouncing, Counting Pulses, and Averaging Analog Signals

Discuss development of drivers for external hardware and components, such as LCD screens, sensors, motor drivers, etc.
Target audience: Users and developers of drivers.
Post Reply
moose
Posts: 9
Joined: Fri Apr 17, 2015 4:22 pm
Location: Anchorage, Alaska
Contact:

Library for Debouncing, Counting Pulses, and Averaging Analog Signals

Post by moose » Mon Apr 20, 2015 5:42 am

I just released a Micro Python library that makes it easier to do certain tasks with input pins. Here is the link to the Repo on GitHub. My motivations were:
  • I wanted to be able to debounce digital inputs in software and run callback functions when the digital inputs changed state.
  • I wanted to be able to count pulses from devices like electric meters and water meters, including some debouncing to compensate for reed switch closure bounces.
  • I wanted to filter noise out of analog input channels by using a moving average of multiple readings.
  • I wanted to read current input values into a data structure that is easy to work with in my data acquistion and controller code.
Here is some sample code that uses the library. It sets up a button input on pin Y1, a counter input on pin Y2 and an analog input on X1. Descriptive names are assigned to the Y1 and X1 inputs. Numerous configuration options are not shown in this simple example:

Code: Select all

import time
from inputs import Manager, Digital, Counter, Analog

# List the desired inputs and pass them to a Manager object. 
mgr = Manager([Digital('Y1:button1'),
               Counter('Y2'),
               Analog('X1:sensor1_volts', convert_func=lambda x: x / 4095 * 3.3)])

# The manager object immediately starts polling the inputs after instantiation.

while True:
    # get a snapshot of all the current input values
    vals = mgr.values()

    print(vals)  # prints the entire dictionary of readings

    # prints two individual readings, using normal dictionary access and also
    # attribute access.
    print(vals['button1'], vals.sensor1_volts, '\n')
    
    time.sleep(1)
Much more documentation is present in the README file on the GitHub site. Feeback is appreciated!

cloudformdesign
Posts: 35
Joined: Wed Mar 11, 2015 7:48 pm

Re: Library for Debouncing, Counting Pulses, and Averaging Analog Signals

Post by cloudformdesign » Sun May 03, 2015 2:00 pm

That is a great idea for a library! A few comments:

- If it is possible, I think it would be better for the library to receive the pin object that the user wants to use, rather than receive a text string.
- I'm not sure what the advantage of the "description" is -- it seems to add complexity that could easily be implemented with comments to the code
- operations taking as long as 80us in an interrupt is a big deal, especially when you are chaining them together for multiple pins -- I wonder if there are ways to speed that up. The first one I can think is to use one of the native code emiters (this is documented here: https://www.kickstarter.com/projects/21 ... sts/664832), the other one is to stop using so many object attributes and instead use global variables (which in standard CPython provides faster access, not sure if as important in uPython).
- related to speedup, if it is possible to pull operations out of the interrupt handler and put them into something like asyncio then that would be great. I'm not sure where this optimization can happen at the moment, I haven't looked at it extensively enough.

I haven't spent too much time looking at it, but I really like the library so I'm planning on looking again for more comments and possibly a pull request :)

pfalcon
Posts: 1155
Joined: Fri Feb 28, 2014 2:05 pm

Re: Library for Debouncing, Counting Pulses, and Averaging Analog Signals

Post by pfalcon » Tue May 05, 2015 6:47 pm

other one is to stop using so many object attributes and instead use global variables (which in standard CPython provides faster access, not sure if as important in uPython).
This is rarely good suggestion from structured programming point of view (I hit that twice during last 2 weeks, which is something - I usually don't deal with such code, and trust me, it makes you want to say those kind of words).

As for speed, looking up globals and object attributes follows the same underlying method - looking up keys in a dictionary. The speed generally depends (for MicroPython) on how many keys there're. As you imagine, number of attributes in an object usually limited, while number of globals is potentially unlimited. So, the suggestion looks not good even from that point of view. Sooner or later we'll have __slots__ support, which may optimize attribute lookup further, but optimizing globals is much harder.

But all in all, for folks who need speed right here right now (without guarantee how it will be in the future), we have beginnings of comparative benchmarking testsuite: https://github.com/micropython/micropyt ... ench-tests
Awesome MicroPython list
Pycopy - A better MicroPython https://github.com/pfalcon/micropython
MicroPython standard library for all ports and forks - https://github.com/pfalcon/micropython-lib
More up to date docs - http://pycopy.readthedocs.io/

moose
Posts: 9
Joined: Fri Apr 17, 2015 4:22 pm
Location: Anchorage, Alaska
Contact:

Re: Library for Debouncing, Counting Pulses, and Averaging Analog Signals

Post by moose » Tue May 05, 2015 11:47 pm

@cloudformdesign: I'm glad the library looks useful to you. As to your comments:

- It would be fairly easy to accept *either* a Pin object or a string for the required first parameter. I can test the type of the parameter and act accordingly. To preserve the "description" parameter, we'd have to separate that out as an optional keyword, as I don't see a good way to bundle it with the Pin object. If I find some time, I'll try to add that feature (unless I get your Pull Request first!).

- I like the "description" attribute for the Input object because I would prefer to access a current value for a pin via "vals.tank_temp" as opposed to "vals.X1". Also "print(vals)" produces understandable dictionary keys instead of more cryptic pin names.

- As to the speed issue, I understand that there may be applications where it becomes a problem. For my first application, the pyboard will primarily be a data acquisition peripheral for a Raspberry Pi. I need to do very little processing on the pyboard, so consuming 50-75% of my CPU resources with the timer interrupt processing will not be a problem. That said, I did do some testing of where the processing time is occurring. Out of the 80 us consumed for servicing one input during the interrupt, about 20 us was consumed just getting to the input processing code, but the other 60 us was used in actual processing inside the "service_input()" method for the input. So, I think the main target for optimization needs to be the code that resides in the "service_input()" method for each input type. I'm pretty ignorant about speed optimization, but those native code emitters you pointed out look pretty easy to use and worth a try. Thanks.

- I do think the input servicing code needs to stay in a Timer interrupt routine because it is fairly time sensitive. For example, some delay in servicing a counter input may cause you to miss a pulse if the pulse frequency is high.

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: Library for Debouncing, Counting Pulses, and Averaging Analog Signals

Post by dhylands » Wed May 06, 2015 12:08 am

You can also create your pin mapping dictionary.

Code: Select all

>>> pinMap = {'tank_temp' : pyb.Pin.board.X1}
>>> pyb.Pin.dict(pinMap)
>>> tank_temp = pyb.Pin('tank_temp')
>>> tank_temp
Pin(Pin.cpu.A0, mode=Pin.IN)

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Library for Debouncing, Counting Pulses, and Averaging Analog Signals

Post by pythoncoder » Wed May 06, 2015 4:45 am

@moose Regarding filtering analog values you might want to look at this https://github.com/peterhinch/micropython-filters which offers fast filter solutions including moving average and more versatile FIR (finite impulse response) filtering.
Peter Hinch
Index to my micropython libraries.

Frank_Muenzner
Posts: 3
Joined: Mon Nov 23, 2015 5:18 pm

How to realize an up/down counter

Post by Frank_Muenzner » Mon Nov 23, 2015 5:35 pm

At first i'm really new to micropython ( i have this board since today) as well python.
I hope somebody can help me and give me some hints how to modyfy the lib.

In my application i have an magnetic encoder which can be clockwise /counterclockwise turn.
Its turned manual so read in frequeny limitation (480Hz?) should be no problem.

To read the encoder i use the A1233 Dual-Channel Hall-Effect Direction Detection Sensor IC, which has a Dir and a step output.
In C /asm it should be really easy to do something like that, but how to do this with the micropython board.

My connections:

X17: DIR input
X18: Step input

With the lib input.py i can realise a upcounter also reset should be possible.
But how it is possible to realize a up/downcounter with mode select by DIR Pin?

I hope someone can help me.


Best regard,
Frank

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: Library for Debouncing, Counting Pulses, and Averaging Analog Signals

Post by dhylands » Mon Nov 23, 2015 8:01 pm

Hi Frank,

So there are a couple of different approaches you could take.

The first is to use input capture and setup a callback. I did an an example here:
https://github.com/dhylands/upy-example ... ic_test.py
which measures the width of a pulse.

In your case you setup the input capture on your step line, and in the callback query the DIR line to figure out if you should either increment or decrement a counter.

I know its possible to have the a timer driven by an external clock. I'm not sure if you can change the up/down by an external signal directly. It may be possible to reconfigure the timer by detecting when the DIR signal changes.

Looking at the datasheet, you might be able to use OUTA and OUTB which look like a quadrature signal. There is support on some of the timers for quadrature decoding. In that case the HW automatically increments or decrements the counter.

Here's an example using quadrature decoding:
https://github.com/dhylands/upy-example ... ncoder2.py
One of the 2 A/B signals needs to goto channel 1 and the other signal needs to goto channel 2 of the same timer.

Timers 1-5 and 8 support quadrature encoding.

Frank_Muenzner
Posts: 3
Joined: Mon Nov 23, 2015 5:18 pm

Re: Library for Debouncing, Counting Pulses, and Averaging Analog Signals

Post by Frank_Muenzner » Wed Nov 25, 2015 7:10 am

Hi Dave,
many thanks for your helpful recommendations.
Originally i had used the four pin A1233 device which has only dir and step output.
Because i'm new to micropython i follow your recommendation, replace the 4 pin devices with the soic8 version which has the dir ,step + quadrature output.
The quadrature reading works fine. ;-)
I'm sure the task can also be easy solved with dir and step output.
But I'm a beginner and have only this week to realize my prototype.

Thank you much for your help

Post Reply