Enkel- en dubbelklik (Hulp van Claude AI)

Het lijkt zo eenvoudig. Een knop die meerdere functies heeft. We gebruiken het dagelijks bij onze pc-muis. Ook bij mijn elektrische tandenborstel gebruik ik het. Deze apparaten kennen het verschil tussen een enkele- en dubbele klik. Daarbij wordt onderscheid gemaakt in de functie die door de klik uitgevoerd wordt. Hoe werkt dit? Weliswaar wordt dit bij de muis en de tandenborstel met een microcontroller gedaan, ik wil dit met een Pythonscript realiseren.  

Al een tijd ben ik bezig om een pythonscript te ontwikkelen die meerdere functies aan één knop kan toewijzen. Ik denk dan aan een knop met een enkele- en dubbele klik die ieder een eigen functie hebben. Het probleem met de dergelijke scripts is de marginale timing waarbinnen je meerdere malen op de knop moet drukken. Daarnaast is het bouncing- of denderprobleem van de knop er een.

Daar gaan we…

Claude AI

Weken zonder succes gingen voorbij totdat ik met Claude AI in contact kwam. Door het zorgvuldig aangeven wat de functionaliteit van de Pythoncode moest zijn, leverde Claude na enkele iteraties de gewenste oplossing.

Ik gaf Claude de volgende opdracht: ‘Maak een python-script voor de Raspberry Pi met een knop, een relaismodule en en led waarbij bij een singelklik het relais aan of uit geschakeld wordt en bij een dubbelklik het relais 10 seconden ingeschakeld blijft en de led knippert totdat het relais uitschakelt. Een enkele klik annuleert de 10 seconden en schakelt het relais en led direct uit.’

Claude leverde een goed werkend Python script, een aansluitschema en een gedragsbeschrijving voor de Raspberry Pi en diens onderdelen.

Wat kun je met dit script?

Met dit script kun je bijvoorbeeld met één klik een relais inschakelen dat vervolgens de verwarming van een badkamerspiegel inschakelt. Door vervolgens nog één klik te geven schakel je het relais en daarmee de verwarming uit. Je kunt ook een dubbelklik ingeven. Het relais schakelt dan na 10 seconden (tijd is in het script in te stellen) uit. Tijdens deze uitschakelvertraging kun je het relais met één klik direct uitschakelen.

Aansluitschema

Sluit de knop, de relaismodule en de LED als volgt aan op de Raspberry Pi.

Gedrag

Dit is het gedrag van het script en de onderdelen.

Python script


Ik ga ervan uit dat je inmiddels weet hoe je onderstaande script naar een (SSH) editor in Raspberry Pi OS kopieert, opslaat en vervolgens opstart. Zo niet laat het me weten door hieronder een reactie te geven.


#!/usr/bin/env python3

import RPi.GPIO as GPIO
import threading
import time

# ── Pin configuratie ──────────────────────────────────────────────
PIN_BUTTON = 17
PIN_RELAIS = 27
PIN_LED = 22

# ── Timing ────────────────────────────────────────────────────────
DUBBEL_KLIK_WINDOW = 0.35   # seconden tussen twee klikken
DEBOUNCE_MS = 50            # hardware-debounce (milliseconden)
KNIPPERSNELHEID = 0.3       # seconden per knipperperiode (aan + uit)
TIMER_DUUR = 10.0           # seconden dat relais actief is bij dubbele klik

# ── Globale toestand ──────────────────────────────────────────────
relais_aan = False          # huidige toestand relais (enkele-klik modus)
timer_actief = False        # loopt de 10-secondentimer?

_klik_timer = None   # type: threading.Timer
_klik_teller = 0
_lock = threading.Lock()

# ─────────────────────────────────────────────────────────────────
# GPIO initialisatie
# ─────────────────────────────────────────────────────────────────

def gpio_init():
    GPIO.setmode(GPIO.BCM)
    GPIO.setwarnings(False)

    GPIO.setup(PIN_BUTTON, GPIO.IN, pull_up_down=GPIO.PUD_UP)
    # Relaismodule is actief-laag: initieel HIGH = relais uit
    GPIO.setup(PIN_RELAIS, GPIO.OUT, initial=GPIO.HIGH)
    GPIO.setup(PIN_LED, GPIO.OUT, initial=GPIO.LOW)

    GPIO.add_event_detect(
        PIN_BUTTON,
        GPIO.FALLING,
        callback=knop_callback,
        bouncetime=DEBOUNCE_MS,
    )

# ─────────────────────────────────────────────────────────────────
# Relais- en LED-hulpfuncties
# ─────────────────────────────────────────────────────────────────

def relais(aan: bool):
    """Schakel de relaismodule. Actief-laag: LOW = aan, HIGH = uit."""
    GPIO.output(PIN_RELAIS, GPIO.LOW if aan else GPIO.HIGH)

def led(aan: bool):
    GPIO.output(PIN_LED, GPIO.HIGH if aan else GPIO.LOW)

def beide_uit():
    relais(False)
    led(False)

# ─────────────────────────────────────────────────────────────────
# Dubbele-klik modus: timer + knipperthread
# ─────────────────────────────────────────────────────────────────

_knipperthread = None   # type: threading.Thread
_stop_knipper = threading.Event()

def start_dubbele_klik_modus():
    global timer_actief, _knipperthread, _stop_knipper, relais_aan

    with _lock:
        timer_actief = True
        relais_aan = False        # enkele-klik toestand resetten

    relais(True)

    # Start knipperthread voor LED
    _stop_knipper.clear()
    _knipperthread = threading.Thread(target=_knipper_led, daemon=True)
    _knipperthread.start()

    # Timer: na TIMER_DUUR seconden automatisch stoppen
    def timer_klaar():
        stop_dubbele_klik_modus(geannuleerd=False)

    t = threading.Timer(TIMER_DUUR, timer_klaar)
    t.daemon = True
    t.start()

    print(f"[dubbele klik] Relais aan voor {TIMER_DUUR:.0f} s, LED knippert.")

def stop_dubbele_klik_modus(geannuleerd: bool):
    global timer_actief

    with _lock:
        if not timer_actief:
            return
        timer_actief = False

    # Knipperthread stoppen
    _stop_knipper.set()
    if _knipperthread and _knipperthread.is_alive():
        _knipperthread.join(timeout=1.0)

    beide_uit()

    reden = "geannuleerd door klik" if geannuleerd else "timer verlopen"
    print(f"[dubbele klik] Gestopt ({reden}). Relais en LED uit.")

def _knipper_led():
    """Knippert de LED totdat _stop_knipper gezet wordt."""
    while not _stop_knipper.is_set():
        led(True)
        _stop_knipper.wait(KNIPPERSNELHEID / 2)
        led(False)
        _stop_knipper.wait(KNIPPERSNELHEID / 2)

# ─────────────────────────────────────────────────────────────────
# Klik-herkenning (enkelvoudig / dubbel)
# ─────────────────────────────────────────────────────────────────

def knop_callback(channel):
    global _klik_teller, _klik_timer

    with _lock:
        _klik_teller += 1
        teller = _klik_teller

    # Annuleer eventuele lopende wacht-timer
    if _klik_timer and _klik_timer.is_alive():
        _klik_timer.cancel()

    def verwerk_klikken(aantal):
        global _klik_teller
        with _lock:
            _klik_teller = 0

        if aantal >= 2:
            _verwerk_dubbele_klik()
        else:
            _verwerk_enkele_klik()

    # Wacht kort om te zien of er nog een klik volgt
    _klik_timer = threading.Timer(DUBBEL_KLIK_WINDOW, verwerk_klikken, args=[teller])
    _klik_timer.daemon = True
    _klik_timer.start()


def _verwerk_enkele_klik():
    global relais_aan, timer_actief

    with _lock:
        actief = timer_actief

    if actief:
        # Annuleer de dubbele-klik modus
        stop_dubbele_klik_modus(geannuleerd=True)
    else:
        # Toggle relais
        with _lock:
            relais_aan = not relais_aan
            nieuw = relais_aan
        relais(nieuw)
        print(f"[enkele klik] Relais {'aan' if nieuw else 'uit'}.")

def _verwerk_dubbele_klik():
    with _lock:
        actief = timer_actief

    if actief:
        print("[dubbele klik] Timer loopt al, actie genegeerd.")
    else:
        start_dubbele_klik_modus()

# ─────────────────────────────────────────────────────────────────
# Hoofdprogramma
# ─────────────────────────────────────────────────────────────────

def main():
    print("Relais-knop gestart. Druk op Ctrl+C om te stoppen.")
    print(f"  GPIO knop    : {PIN_BUTTON}")
    print(f"  GPIO relais  : {PIN_RELAIS}")
    print(f"  GPIO led     : {PIN_LED}")
    print()

    gpio_init()

    try:
        while True:
            time.sleep(0.1)
    except KeyboardInterrupt:
        print("\nAfsluiten…")
    finally:
        beide_uit()
        GPIO.cleanup()
        print("GPIO opgeruimd. Tot ziens!")

if __name__ == "__main__":
    main()

Conclusie

Sommige complexe scripts kunnen hoofdbrekens opleveren. Wat fantastisch dat Claude Ai ons kan helpen en een werkende oplossing kan bieden. Uitgangspunt is wel dat je Claude exact verteld wat het resultaat van het script moet zijn. Dat is nog een hele kunst, zeker bij grote en complexe toepassingen.

Sommige complexe scripts kunnen hoofdbrekens opleveren. Wat fantastisch dat Claude Ai ons kan helpen en een werkende oplossing kan bieden. Uitgangspunt is wel dat je Claude exact verteld wat het resultaat van het script moet zijn. Dat is nog een hele kunst, zeker bij grote en complexe toepassingen.

Ofschoon Ai een oplossing kan zijn bij moeilijk vraagstukken moeten we alert blijven. Het lijkt een beetje zoals vroeger op school waarbij een vriendje je moeilijke huiswerk maakte. Het proefwerk moet je toch echt zelf doen.

Leren doe je door ZELF te doen……

Have A Nice Day!

Geef als eerste een reactie

Laat een reactie achter

Het e-mailadres wordt niet gepubliceerd.


*