Enkel- en dubbelklik voor verwarmde badkamerspiegel

Recent heb ik mijn badkamer verbouwd en een spiegel geplaatst met verlichting in de lijst en een verwarming achter de spiegel. De spiegel heeft onderin in het midden een ge-etst vierkantje dat de locatie van de aanraaksensor weergeeft. Het enige dat de achterliggende sensor doet is een relais bekrachtigen waardoor de spiegelverwarming en de lichtlijst geschakeld worden. Door het douchen slaat soms de condens op de spiegel. Na het inschakelen van de spiegelverwarming is de spiegel meestal na zo'n 10 minuten droog. Wat zou het handig zijn als de spiegel zichzelf uitschakelt.  

Al een tijd ben ik bezig om een pythonscript te ontwikkelen die meerdere functies aan één knop kan toewijzen. Dit principe wil ik gebruiken om bij mijn badkamerspiegel de ingebouwde ledstrip en verwarming te schakelen. Ik denk dan aan een knop met een enkele- en dubbele klik die ieder een eigen functie heeft. Het probleem met dergelijke scripts is de marginale timing waarbinnen je twee keer op de knop moet drukken. Daarnaast is het bouncing- of denderprobleem van de mechanische knop er een. Een digitale aanraak- of touchsensor, zoals de TTP223 kent dit fenomeen niet.

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 aanraaksensor (TTP223), een relaismodule en een led waarbij met een singelklik het relais aan- of uitgeschakeld 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 verrassend goed werkend Python script, een aansluitschema en een gedragsbeschrijving voor de Raspberry Pi en diens onderdelen.

De timerduur van 10 seconden is voor de demo. In praktijk zal dit zo’n 10 minuten zijn, dat mag je zelf bepalen.

Wat kun je met dit script?

Zoals de opdracht aan Claude al aangaf: Met dit script kun je bijvoorbeeld met een enkele klik/touch een relais inschakelen dat vervolgens de verwarming en lamp van een badkamerspiegel inschakelt, de led volgt de status van het relais. Door vervolgens nog een klik/touch te geven schakel je het relais en daarmee de verwarming en verlichting uit. Je kunt ook een dubbele klik/touch ingeven, het relais schakelt dan na 10 seconden (de timer is in het script in te stellen) de verwarming en de verlichting uit, de led knippert tijdens de timermodus. Tijdens deze uitschakelvertraging kun je het relais met één klik direct uitschakelen.

Als het script opgestart is, kun je in de terminal de status ervan volgen.

Aansluitschema

Sluit de aanraaksensor, de relaismodule en de led als volgt aan op de Raspberry Pi. De verlichting wordt samen met de verwarming geschakeld. Omdat beide onderdelen waarschijnlijk verschillende spanningen gebruiken kun je hiervoor het beste een relais met een dubbelpolige uitgang nemen, een voor de verlichting en een voor de verwarming.

Gedrag

Dit is het gedrag van het script en de onderdelen.

Python script

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

#!/usr/bin/env python3
"""
Relais-knop met TTP223 capacitieve touchsensor.

TTP223 (standaard modus A):
  - Uitgang HIGH bij aanraking
  - Uitgang LOW in rust
  - Geen externe pull-up nodig
  - Geen mechanische bounce → kortere debouncetijd volstaat
"""
import RPi.GPIO as GPIO
import threading
import time

# ── Pin configuratie ──────────────────────────────────────────────
PIN_TOUCH  = 17   # SIG-pin van TTP223
PIN_RELAIS = 27
PIN_LED    = 22

# ── Timing ────────────────────────────────────────────────────────
DUBBEL_KLIK_WINDOW = 0.35   # seconden tussen twee aanrakingen
DEBOUNCE_MS        = 20     # capacitieve sensor → minder bounce, kortere tijd
KNIPPERSNELHEID    = 0.3    # seconden per knipperperiode (aan + uit)
TIMER_DUUR         = 10.0   # seconden dat relais actief is bij dubbele aanraking

# ── Globale toestand ──────────────────────────────────────────────
relais_aan   = False   # huidige toestand relais (enkelvoudige 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)

    # TTP223: geen pull-up/pull-down nodig, sensor stuurt zelf HIGH/LOW
    GPIO.setup(PIN_TOUCH, GPIO.IN)

    # 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)

    # Detecteer RISING edge: sensor gaat van LOW naar HIGH bij aanraking
    GPIO.add_event_detect(
        PIN_TOUCH,
        GPIO.RISING,
        callback=touch_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-aanraking modus: timer + knipperthread
# ─────────────────────────────────────────────────────────────────

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

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

    with _lock:
        timer_actief = True
        relais_aan   = False   # enkele modus 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_touch_modus(geannuleerd=False)

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

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

def stop_dubbele_touch_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 aanraking" if geannuleerd else "timer verlopen"
    print(f"[dubbele touch] 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)

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

def touch_callback(channel):
    """Wordt aangeroepen bij elke RISING edge van de TTP223."""
    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_aanrakingen(aantal):
        global _klik_teller
        with _lock:
            _klik_teller = 0

        if aantal >= 2:
            _verwerk_dubbele_touch()
        else:
            _verwerk_enkele_touch()

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


def _verwerk_enkele_touch():
    global relais_aan, timer_actief

    with _lock:
        actief = timer_actief

    if actief:
        # Annuleer de dubbele-touch modus
        stop_dubbele_touch_modus(geannuleerd=True)
    else:
        # Toggle relais
        with _lock:
            relais_aan = not relais_aan
            nieuw = relais_aan
        relais(nieuw)
        led(nieuw)   # LED volgt relaistoestand in enkele modus
        print(f"[enkele touch] Relais {'aan' if nieuw else 'uit'}.")

def _verwerk_dubbele_touch():
    with _lock:
        actief = timer_actief

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

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

def main():
    print("Relais-touch gestart (TTP223). Druk op Ctrl+C om te stoppen.")
    print(f"  GPIO touch   : {PIN_TOUCH}")
    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 kan helpen om de gewenste functionaliteit te 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 gebruik van Ai lijkt een beetje zoals vroeger op school waarbij een vriendje je moeilijke huiswerk maakte. Om het proefwerk te kunnen doen moet de kennis toch echt vanuit jezelf komen.

Leren doe je door (samen) te doen……

Have A Nice Day!

Geef als eerste een reactie

Laat een reactie achter

Het e-mailadres wordt niet gepubliceerd.


*