Toggling a value during a loop without time.sleep()

Question:

I’m trying to make a toggleable value in my program, during a loop. I can’t use time.sleep() in the program, as I can’t have the program stopping completely (so that you can’t press buttons during this time).

I could just do this:

while True:
    if button.is_pressed():
        # do task

But then, the value will be toggled every frame. This is too fast.

I also tried this:

while True:
    if button.value:
        cur_state_right = button.value
        if cur_state_right != prev_state_right:
            # do task
        prev_state_right = cur_state_right

This makes you press then release the button before the task can be done again. However in practice, the task is only done sometimes when you press the button, like it must be pressed at a specific time. For some reason, the code also stops when you hold the button down. This is bad, as this code runs a digital clock, which shouldn’t stop randomly.

Just in case it’s the rest of the code that’s causing this, here it is:

import board
import busio
from adafruit_ht16k33 import segments
import adafruit_gps
from adafruit_datetime import timedelta, datetime
import time
import digitalio
import microcontroller

btn_right = digitalio.DigitalInOut(board.GP5)
btn_right.switch_to_input(pull=digitalio.Pull.UP)

btn_confirm = digitalio.DigitalInOut(board.GP9)
btn_confirm.switch_to_input(pull=digitalio.Pull.UP)

btn_left = digitalio.DigitalInOut(board.GP13)
btn_left.switch_to_input(pull=digitalio.Pull.UP)

i2c = busio.I2C(board.GP27, board.GP26)
display = segments.Seg7x4(i2c)
gps = adafruit_gps.GPS_GtopI2C(i2c)

display.fill(1)
time.sleep(1)
display.fill(0)

gps.send_command(
    b"PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"
)  # start gps data grab
gps.send_command(b"PMTK220,1000")  # set ur to 1hz
gps.send_command(b"PMTK285,2,100") # activate pps if not already on

def load(filename):
    f = open(filename + ".txt", "r")
    output = f.read()
    f.close()
    return output
    
def save(filename, data):
    f = open(filename + ".txt", "w")
    f.write(data)
    f.close()

def button_input():
    prev_state_left = btn_left.value
    prev_state_centre = btn_confirm.value
    prev_state_right = btn_right.value
    while True:
        cur_state_left = btn_left.value
        if cur_state_left != prev_state_left:
            if not cur_state_left:
                return "<"
        prev_state_left = cur_state_left

        cur_state_centre = btn_confirm.value
        if cur_state_centre != prev_state_centre:
            if not cur_state_centre:
                return "!"
        prev_state_centre = cur_state_centre

        cur_state_right = btn_right.value
        if cur_state_right != prev_state_right:
            if not cur_state_right:
                return ">"
        prev_state_right = cur_state_right


def savings_check():
    dst = 0
    while True:
        display.fill(0)
        display.print(dst)
        option = button_input()
        if option == "!":
            break
        while True:
            if option == ">":
                dst += 1
                break
            if option == "<":
                dst -= 1
                break
            else:
                break
    return dst

dst = savings_check()

gps.update()
while True:
    try:
        time.sleep(1)
        gps.update()
        timeset = datetime(
            gps.timestamp_utc.tm_year,
            gps.timestamp_utc.tm_mon,
            gps.timestamp_utc.tm_mday,
            gps.timestamp_utc.tm_hour,
            gps.timestamp_utc.tm_min,
            gps.timestamp_utc.tm_sec,
        )
        break
    except Exception as e:
        display.print("LOAD")
        print(e)
        print("Time load fail. Retrying. Satellites:", gps.satellites)
print("Load success!")

if dst != 0:
    timeset = timeset + timedelta(hours=dst)

timeset = timeset + timedelta(seconds=15)

last_print = time.monotonic()
last_update = time.monotonic()

pps_state = True

prev_state_right = not btn_right.value
while True:
    current = time.monotonic()
    if btn_left.value and btn_confirm.value and btn_right.value:
        if current - last_print >= 1.0:
                last_print = current
                timeset = timeset + timedelta(seconds=1)
                display.print(
                    "{:02d}".format(timeset.hour) + "{:02d}".format(timeset.minute)
                )
                print(timeset, "With", gps.satellites, "Satellites")
                if timeset.second % 2 == 0:
                    display.colon = True
                else:
                    display.colon = False
                    
    elif not btn_left.value:
        display.colon = False
        display.print(
            "{:02d}".format(timeset.day) + "{:02d}".format(timeset.month)
        )
        
    elif not btn_confirm.value:
        display.colon = False
        display.print(timeset.year)
        
    elif not btn_right.value:
        cur_state_right = not btn_right.value
        if cur_state_right != prev_state_right:
            if pps_state:
                gps.send_command(b"PMTK285,0,100")
                print("PPS off")
            if not pps_state:
                gps.send_command(b"PMTK285,2,100")
                print("PPS on")
            pps_state = not pps_state
        prev_state_right = cur_state_right
        
    elif not btn_left.value and not btn_confirm.value and not btn_right.value:
        microcontroller.reset()
Asked By: Commodore 64

||

Answers:

The problem was that I had put the wrong code in or out of the if statement.

It looked like this:

while True:
    if button.value:
        cur_state_right = button.value
        if cur_state_right != prev_state_right:
            # do task
        prev_state_right = cur_state_right

However it should have been like this:

prev_state_right = btn_right.value
while True:
    cur_state_right = btn_right.value
    if cur_state_right != prev_state_right:
        if cur_state_right:
            # do task
        prev_state_right = cur_state_right
Answered By: Commodore 64

Due to your setup, your values will be backwards. You are pulling the button up so, at rest, it’s value will be 1. You have your button wired to ground so, when you press it, the value will be 0

All that being said. Here is the logic to capture button events

rbtn = btn_right.value

while True:
    #value = 1, last_value = 0
    #in your setup, this means released
    if btn_right.value and not rbtn:
        #do something
        ...

    #last_value = 1, value = 0
    #in your setup, this means pressed
    if rbtn and not btn_right.value:
        #do something
        ...
    
    #update
    rbtn = btn_right.value
Answered By: OneMadGypsy
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.