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/gpiochip0 na 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:

BCMOpisTypowe zastosowanie
17GPIO17przekaźnik 1
27GPIO27przycisk 1
22GPIO22przekaźnik 2
23GPIO23przycisk 2
24GPIO24przekaźnik 3
25GPIO25przycisk 3

Niskopoziomowe API - gpio.*#

gpio.setup(pin, mode, pull, safeState)#

Konfiguruje pin GPIO.

ParametrTypOpis
pinnumberNumer pinu BCM
modestring"input" lub "output"
pullstring"up", "down" lub "off" (domyślnie "off")
safeStatebooleanStan 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 1

gpio.write(pin, value)#

Ustawia wartość pinu wyjściowego.

gpio.write(17, true)   -- HIGH
gpio.write(17, false)  -- LOW

gpio.toggle(pin)#

Przełącza wartość pinu na przeciwną.

gpio.toggle(17)  -- HIGH → LOW lub LOW → HIGH

gpio.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 direct

Zwalnianie:

gpio.release(17)
gpio.setup(17, "input")  -- OK

Obiekty 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 RELAY1

Polling 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_DIN zamiast surowego gpio.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#

ScenariuszUżyj
Przekaźnik, lampa, zawórGPIO_DOUT
Przycisk, czujnik binarnyGPIO_DIN
Niestandardowe urządzenie (PWM, syrena, LED)gpio.* bezpośrednio
Debugging, testygpio.read(), gpio.stats()