Programování s Raspberry Pi: Semafor

Upozornění

NEZAPOJUJ LED BEZ ODPORU

To bylo napsanéna mé dětské elektronické stavebnici LOGITRONIK 01. Tak na to prosím myslete při zapojování, jinak se LED zničí.

Co mimo samotného Raspberry Pi potřebujete

  • 3 LED (červenou, žlutou, zelenou)
  • 3 odpory 220 až 330 Ω
  • propojovací kabely
  • nepájivé pole
  • volitelně i GPIO Extension Board spolu s 40 žilovým GPIO plochým kabelem pro snadnější propojení GPIO k nepájivému poli

Použitý software

  • Python 3 (ten už pravděpodobně bude nainstalovaný spolu s Ubuntu)
  • VSCodium (s rozšířeními Python, Python Debugger a Python Environments)
  • balíčky s knihovnami python3-tk, python3-gpiozero, python3-lgpio

Balíčky si nainstalují v teminálu:

sudo apt update
sudo apt install -y python3-tk python3-gpiozero python3-lgpio

Principy programu

  • Základní vytvoření a nastavení grafickým okna s tlačítky v knihovně tkinter
  • Práce s GPIO piny
  • Vytvoření a nastavení jednoho vlákna ve kterém běží funkce blikání tak, aby nedošlo k zamrznutí hlavního okna
  • Bezpečné zavření grafického okna, kdy jsou bezpečně vypnuty všechny výstupy.
  • Funkce spouštěné tlačítky nejprve vypnou všechny led až pak zapnou požadovaný stav, aby jednotlivé režimy nebyly v kolizi.

Hlavní importy

Na začátku kódu je potřeba importovat knihovny a třídy.

import tkinter as tk
from gpiozero import LED
from time import sleep
import threading
  • import tkinter as tk načte knihovnu pro grafické okno. Zkratka as tk znamená, že v kódu se pak místo tkinter.Tk() píše kratší tk.Tk().
  • from gpiozero import LED vezme jen třídu LED z knihovny gpiozero. Ta umí zapnout, vypnout a řídit LED přes GPIO.
  • from time import sleep přidá funkci sleep(), která pozastaví program na zadaný počet sekund.
  • import threading přidá práci s vlákny, tedy možnost dělat blikání na pozadí, aby se nezaseklo GUI.

Nastavení pinů

GREEN_PIN = 22
YELLOW_PIN = 27
RED_PIN = 17

Tyto tři řádky pro větší přehlednost ukládají čísla GPIO pinů do proměnných. Pokud jsou LED připojeny k jinému pinu, stačí upravit číslo pinu zde.

Vytvoření objektů LED

green = LED(GREEN_PIN)
yellow = LED(YELLOW_PIN)
red = LED(RED_PIN)

Tady se vytvoří tři objekty reprezentující tři LED diody. Každý objekt je navázaný na konkrétní GPIO pin, takže lze volat například green.on() nebo red.off(). Zde se nastavuje, které fyzické piny bude knihovna ovládat.

Řídicí proměnné pro blikání

blink_thread = None
blink_stop = threading.Event()
  • blink_thread = None jen připraví proměnnou, do které se později uloží vlákno pro blikání.
  • blink_stop = threading.Event() vytvoří „signál“, kterým se dá blikání bezpečně zastavit. Když se tento signál nastaví, smyčka pro blikání skončí.

Funkce all_off()

def all_off():
blink_stop.set()
green.off()
yellow.off()
red.off()

Tahle funkce vypne vše. Nejprve blink_stop.set() řekne vláknu s blikáním, že má skončit. Pak green.off()yellow.off() a red.off() vypnou všechny LED. Je to důležité, protože tím zabráníš tomu, aby běželo blikání současně s jiným stavem světel.

Funkce pro jednotlivé stavy semaforu

def set_green():
all_off()
green.on()
  • Nejprve vypne všechno.
  • Pak zapne jen zelenou LED.

Stejně fungují i ostatní:

def set_yellow():
all_off()
yellow.on()

Zapne jen žlutou.

def set_red():
all_off()
red.on()

Zapne jen červenou.

def set_red_yellow():
all_off()
red.on()
yellow.on()

Zapne současně červenou a žlutou. To odpovídá stavu před změnou na zelenou u semaforu.

Funkce pro blikání žluté

def blink_yellow():
global blink_thread
all_off()
blink_stop.clear()
  • global blink_thread nastaví, že uvnitř funkce de bude pracovat s proměnnou mimo funkci.
  • all_off() vypne předchozí stav.
  • blink_stop.clear() zruší stop signál, aby blikání mohlo začít.

Pak následuje vnitřní funkce:

    def worker():
while not blink_stop.is_set():
yellow.on()
sleep(0.5)
yellow.off()
sleep(0.5)

Tady se děje samotné blikání:

  • while not blink_stop.is_set(): opakuje cyklus, dokud není požadavek na zastavení.
  • yellow.on() rozsvítí žlutou LED.
  • sleep(0.5) počká půl sekundy.
  • yellow.off() LED zhasne.
  • sleep(0.5) zase počká půl sekundy.
    blink_thread = threading.Thread(target=worker, daemon=True)
blink_thread.start()
  • threading.Thread(...) vytvoří nové vlákno, které poběží souběžně s oknem.
  • daemon=True znamená, že vlákno skončí spolu s programem.
  • start() vlákno skutečně spustí.

Bez vlákna by okno při blikání „zamrzlo“, protože hlavní program by byl pořád v nekonečné smyčce.

Okno Tkinter

A nyní se vytvoří okno aplikace s tlačítky pro ovládání semaforu.

root = tk.Tk()

Vytvoří hlavní okno aplikace.

root.title("Semafor GPIO")

Nastaví titulek okna.

root.geometry("320x260")

Určí velikost okna.

root.resizable(False, False)

Zakáže změnu velikosti okna myší.

Tlačítka

Vzor pro vytvoření tlačítka je následující:

btn1 = tk.Button(root, text="Žlutá bliká", width=28, command=blink_yellow)

Tento příkaz vytvoří tlačítko.

  • root je rodičovské okno.
  • text="Žlutá bliká" je popisek tlačítka.
  • width=28 nastaví šířku tlačítka.
  • command=blink_yellow říká, že po kliknutí se má spustit funkce blink_yellow().

Podobně se nastaví i ostatní tlačítka s jejich vlastním popisem a voláním příslušné funkce.

Umístění tlačítek

btn1.pack(pady=4)

pack() vloží tlačítko do okna a pady=4 přidá svislý okraj 4 pixely nahoře i dole. Stejný zápis se opakuje pro všechna tlačítka.

Zavření okna

def on_close():
all_off()
root.destroy()

Po zavření okna tato funkce nejdřív vypne LED a pak root.destroy() korektně ukončí celé GUI.

root.protocol("WM_DELETE_WINDOW", on_close)

Po kliknutí na křížek okna, místo standardního zavření se má zavolat on_close().

Spuštění GUI

root.mainloop()

To je hlavní smyčka Tkinteru. Bez ní by se okno hned zavřelo. Tato smyčka čeká na události, jako je kliknutí na tlačítko nebo zavření okna.

Celý kód

import tkinter as tk
from gpiozero import LED
from time import sleep
import threading

GREEN_PIN = 22
YELLOW_PIN = 27
RED_PIN = 17

green = LED(GREEN_PIN)
yellow = LED(YELLOW_PIN)
red = LED(RED_PIN)

blink_thread = None
blink_stop = threading.Event()

def all_off():
    blink_stop.set()
    green.off()
    yellow.off()
    red.off()

def set_green():
    all_off()
    green.on()

def set_yellow():
    all_off()
    yellow.on()

def set_red():
    all_off()
    red.on()

def set_red_yellow():
    all_off()
    red.on()
    yellow.on()

def blink_yellow():
    global blink_thread
    all_off()
    blink_stop.clear()

    def worker():
        while not blink_stop.is_set():
            yellow.on()
            sleep(0.5)
            yellow.off()
            sleep(0.5)

    blink_thread = threading.Thread(target=worker, daemon=True)
    blink_thread.start()

def on_close():
    all_off()
    root.destroy()

root = tk.Tk()
root.title("Semafor GPIO")
root.geometry("320x260")
root.resizable(False, False)

btn1 = tk.Button(root, text="Žlutá bliká", width=28, command=blink_yellow)
btn2 = tk.Button(root, text="Zelená svítí", width=28, command=set_green)
btn3 = tk.Button(root, text="Žlutá svítí", width=28, command=set_yellow)
btn4 = tk.Button(root, text="Červená svítí", width=28, command=set_red)
btn5 = tk.Button(root, text="Červená a žlutá svítí", width=28, command=set_red_yellow)
btn6 = tk.Button(root, text="Vypnout všechny LED", width=28, command=all_off)

btn1.pack(pady=4)
btn2.pack(pady=4)
btn3.pack(pady=4)
btn4.pack(pady=4)
btn5.pack(pady=4)
btn6.pack(pady=4)

root.protocol("WM_DELETE_WINDOW", on_close)
root.mainloop()

Schema zapojení

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *

Time limit is exhausted. Please reload CAPTCHA.

Web používá Akismet ke snížení množství spamu. Zjistěte, jak jsou zpracovávány údaje z komentářů.