Search code examples
inputcountermicrocontrollermicropythonraspberry-pi-pico

How to detect when a voltage changes from high to low or low to high with micropython on a Pico for 20 different inputs each with separate counters


I have a mechanical button testing device that tests a couple different types of buttons but I need it to count every voltage drop from high to low and for the other type I need Low-high-low. From a previous question someone explained that you need to detect a falling edge for each of the 20 buttons but how do you do this so that if all 20 are hit at the same time, or different times it still functions? This should be functioning at the same speed continuously but if a future person changes something or a part it may mess up the counters I currently have so I am looking to make it so it counts the change in voltages at any point in time for any of the 20 inputs not on a set schedule and will count each push no matter what.

I have tried various nested loops, started looking into ADC, tried a previous suggestion:

old_state = False
old_time = time.time()  # get seconds since EPOCH (just an arbitrary point in history)
while True:
    state = Pin.input(4)
    if old_state is False and state is True:  # detect falling edge
        now = time.time()  # get current time
        delta = now - old_time
        if delta <= 4:  # duration of press was 2s or less?
            countA2 += 1
    if old_state is True and state is False:  # detect rising edge
        old_time = now
    old_state = state  # memorize last state
    time.sleep(.01)  # only a short delay do detect short button presses

but all lead to printing a none stop count or forever 0 for all counters. or it would only count one button at a time.

There will be a length of time where it should have current and a length of time without but I need xhanges documented.


Solution

  • As pmacfarlane stated in his comment: you need an array for the buttons and similar arrays for counters and old_times.

    But instead of implementing that straightforward I suggest you create a class that handles the buttons, counters and timespans for you:

    class CountingButton:
        def __init__(self, pin, lo_hi_lo = False):
            self.pin = pin
            self.lo_hi_lo = lo_hi_lo
    
            self.old_state = False
            self.old_time = time.time()
            self.counter = 0
        
        def check(self):
            state = self.pin.value() # get current state of pin
            if self.old_state is False and state is True:  # detect falling edge
                if lo_hi_lo == False: # simple counting button
                    counter += 1
                else: # lo-hi-lo button with time control
                    now = time.time()  # get current time
                    delta = now - old_time
                    if delta <= 4:  # duration of press was 4s or less?
                        counter += 1
            if self.old_state is True and state is False:  # detect rising edge
                self.old_time = now
            self.old_state = state  # memorize last state
            return state # for convenience return the current state of the button
    
        def get_counter(self):
            return counter 
    
        def reset(self):
            self.counter = 0
    

    Now you can create instances of CountingButtons and let them handle themselves:

    buttons = []
    buttons.append(CountingButton(A1)) # assuming you already created A1 as input
    buttons.append(CountingButton(A2, lo_hi_lo = True)) # now a timed button
    # etc...
    
    # check all buttons:
    for i in buttons:
        i.check()
    
    # display counters of all buttons
    for i in buttons:
        print(i.pin, i.get_counter())
    
    

    The check() method needs to be called regularly in the body of your main loop. It contains basically the whole code of your "while True:" loop. Be aware that "time.sleep(.01)" is not called in here, as you don't want to have pauses between every check of a pin but rather after all pins are checked.

    reset() is just a convenience method to clear the counter if needed.