GPIO#
GPIO (General Purpose Input/Output) pozwala na bezpośrednie sterowanie pinami Raspberry Pi z poziomu Lua. Na tej warstwie bazują obiekty wyższego poziomu - GPIO_DOUT i GPIO_DIN.
Architektura#
graph TD
lua["user.lua / om.lua"]
obj["GPIO_DOUT / GPIO_DIN<br/><i>obiekty wysokiego poziomu</i>"]
api["gpio.*<br/><i>niskopoziomowe API Lua</i>"]
ctrl["Go Controller<br/><i>RPiController (ARM64)<br/>lub MockController (dev)</i>"]
lua --> obj --> api --> ctrl- RPiController - używa
/dev/gpiochip0na Raspberry Pi (linux + arm64) - MockController - symuluje GPIO na innych platformach (macOS, x86) - do developmentu
Numeracja pinów#
GPIO używa numeracji BCM (Broadcom), nie fizycznej numeracji headerów. Popularne piny:
| BCM | Opis | Typowe zastosowanie |
|---|---|---|
| 17 | GPIO17 | przekaźnik 1 |
| 27 | GPIO27 | przycisk 1 |
| 22 | GPIO22 | przekaźnik 2 |
| 23 | GPIO23 | przycisk 2 |
| 24 | GPIO24 | przekaźnik 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" (domyślnie "off") |
safeState | boolean | Stan fizyczny po zamknięciu (domyślnie 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 wartość pinu.
local val = gpio.read(27) -- 0 lub 1gpio.write(pin, value)#
Ustawia wartość pinu wyjściowego.
gpio.write(17, true) -- HIGH
gpio.write(17, false) -- LOWgpio.toggle(pin)#
Przełącza wartość pinu na przeciwną.
gpio.toggle(17) -- HIGH → LOW lub LOW → HIGHgpio.release(pin)#
Zwalnia pin (kasuje rejestrację i resetuje w kontrolerze).
gpio.release(17)gpio.stats()#
Zwraca statystyki kontrolera GPIO.
local s = gpio.stats()
-- { allocatedPins = 3, pollQueueSize = 0, ... }Rejestr pinów#
Każdy pin może być użyty tylko raz. Próba użycia zajętego pinu wyrzuca błąd:
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 również rejestrują piny - nie można użyć tego samego pinu dwa razy:
local relay = GPIO_DOUT:new("RELAY1", 17)
gpio.setup(17, "input") -- ERROR: pin 17 already allocated by RELAY1Polling zmian wejść#
Kontroler Go polluje piny wejściowe co 50ms. Wykryte zmiany trafiają do kolejki, którą Lua odpytuje przez gpio.pollChanges(). Każda 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 zdarzeń (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 używać
GPIO_DINzamiast surowegogpio.setup+EventBus- daje debounce, rozpoznawanie krótkich/długich naciśnięć i pełną kompatybilność z API vCLU.
Kiedy używać gpio.* a kiedy GPIO_DOUT/GPIO_DIN#
| Scenariusz | Użyj |
|---|---|
| Przekaźnik, lampa, zawór | GPIO_DOUT |
| Przycisk, czujnik binarny | GPIO_DIN |
| Niestandardowe urządzenie (PWM, syrena, LED) | gpio.* bezpośrednio |
| Debugging, testy | gpio.read(), gpio.stats() |