GPIO#
GPIO (General Purpose Input/Output) pozwala na bezposrednie sterowanie pinami Raspberry Pi z poziomu Lua. Na tej warstwie bazuja obiekty wyzszego poziomu - GPIO_DOUT i GPIO_DIN.
Architektura#
user.lua / om.lua
│
┌────┴─────────────────┐
│ GPIO_DOUT / GPIO_DIN │ ← obiekty wysokiego poziomu
└────┬─────────────────┘
│
┌────┴──────────┐
│ gpio.* │ ← niskopoziomowe API Lua
└────┬──────────┘
│
┌────┴──────────┐
│ Go Controller │ ← RPiController (ARM64) lub MockController (dev)
└───────────────┘- RPiController - uzywa
/dev/gpiochip0na Raspberry Pi (linux + arm64) - MockController - symuluje GPIO na innych platformach (macOS, x86) - do developmentu
Numeracja pinow#
GPIO uzywa numeracji BCM (Broadcom), nie fizycznej numeracji headerow. Popularne piny:
| BCM | Opis | Typowe zastosowanie |
|---|---|---|
| 17 | GPIO17 | przekaznik 1 |
| 27 | GPIO27 | przycisk 1 |
| 22 | GPIO22 | przekaznik 2 |
| 23 | GPIO23 | przycisk 2 |
| 24 | GPIO24 | przekaznik 3 |
| 25 | GPIO25 | przycisk 3 |
Niskopoziomowe API - gpio.*#
gpio.setup(pin, mode, pull, safeState)#
Konfiguruje pin GPIO.
| Parametr | Typ | Opis |
|---|---|---|
pin | number | Numer pinu BCM |
mode | string | "input" lub "output" |
pull | string | "up", "down" lub "off" (domyslnie "off") |
safeState | boolean | Stan fizyczny po zamknieciu (domyslnie false) |
-- Pin 17 jako wyjscie
gpio.setup(17, "output")
-- Pin 27 jako wejscie z pull-up
gpio.setup(27, "input", "up")gpio.read(pin)#
Odczytuje wartosc pinu.
local val = gpio.read(27) -- 0 lub 1gpio.write(pin, value)#
Ustawia wartosc pinu wyjsciowego.
gpio.write(17, true) -- HIGH
gpio.write(17, false) -- LOWgpio.toggle(pin)#
Przelacza wartosc pinu na przeciwna.
gpio.toggle(17) -- HIGH → LOW lub LOW → HIGHgpio.release(pin)#
Zwalnia pin (kasuje rejestracje i resetuje w kontrolerze).
gpio.release(17)gpio.stats()#
Zwraca statystyki kontrolera GPIO.
local s = gpio.stats()
-- { allocatedPins = 3, pollQueueSize = 0, ... }Rejestr pinow#
Kazdy pin moze byc uzyty tylko raz. Proba uzycia zajeto pinu wyrzuca blad:
gpio.setup(17, "output")
gpio.setup(17, "input") -- ERROR: gpio: pin 17 already allocated by directZwalnianie:
gpio.release(17)
gpio.setup(17, "input") -- OKObiekty GPIO_DOUT i GPIO_DIN rowniez rejestruja piny - nie mozna uzyc tego samego pinu dwa razy:
local relay = GPIO_DOUT:new("RELAY1", 17)
gpio.setup(17, "input") -- ERROR: pin 17 already allocated by RELAY1Polling zmian wejsc#
Kontroler Go polluje piny wejsciowe co 50ms. Wykryte zmiany trafiaja do kolejki, ktora Lua odpytuje przez gpio.pollChanges(). Kazda zmiana emituje trzy zdarzenia:
gpio.pin.{pin}.OnChange ← zdarzenie per-pin
gpio.OnChange ← zdarzenie globalne
state_changed ← StateBus (dla MQTT/HomeKit)Payload zdarzenia:
{
pin = 27,
value = 1, -- nowa wartosc
oldValue = 0 -- poprzednia wartosc
}Subskrypcja zdarzen (niskopoziomowa)#
-- Reaguj na zmiane konkretnego pinu
EventBus:getShared():on("gpio.pin.27.OnChange", function(data)
if data.value == 1 then
log.info("Pin 27: HIGH")
else
log.info("Pin 27: LOW")
end
end)
-- Reaguj na kazda zmiane GPIO
EventBus:getShared():on("gpio.OnChange", function(data)
log.info("Pin " .. data.pin .. " → " .. data.value)
end)W praktyce lepiej uzywac `GPIO_DIN` zamiast surowego `gpio.setup` + `EventBus` - daje debounce, rozpoznawanie krotkich/dlugich nacisnieci i kompatybilnosc z API Grenton.
Kiedy uzywac gpio.* a kiedy GPIO_DOUT/GPIO_DIN#
| Scenariusz | Uzyj |
|---|---|
| Przekaznik, lampa, zawor | GPIO_DOUT |
| Przycisk, czujnik binarny | GPIO_DIN |
| Niestandardowe urzadzenie (PWM, syrena, LED) | gpio.* bezposrednio |
| Debugging, testy | gpio.read(), gpio.stats() |