DIN - wejście cyfrowe#

DIN (Digital Input) to obiekt reprezentujący wejście cyfrowe - przycisk, czujnik kontaktowy, czujnik ruchu. Występuje w trzech wariantach:

  • Lokalny DIN - wirtualny moduł vCLU, konfigurowany w wizardzie
  • DIN z OM - obiekt z Object Manager, po sparowaniu z zewnętrznym kontrolerem
  • GPIO_DIN - obiekt tworzony w user.lua, podłączony do pinu GPIO Raspberry Pi

Wszystkie warianty mają ten sam interfejs API i te same stałe. Obsługują debounce, rozpoznawanie krótkich i długich naciśnięć oraz zdarzenia hold (przytrzymanie).

Lokalny DIN - moduły wirtualne#

Gdy vCLU działa w trybie CLU (wizardzie), można dodać wirtualne moduły wejść. Nie czytają prawdziwego sprzętu, ale pojawiają się w OM i mogą być używane do testów i integracji.

Konfiguracja w wizardzie#

W kroku 2 wizarda (“Moduły”) można dodać moduły z wejściami cyfrowymi:

Typ modułuWejścia DINWyjścia DOUTAnalogHW Type
DIN88-10x14
DOUT8T8810x1e

Każdy moduł DIN dostaje unikalną nazwę (np. DIN8_1) i numer seryjny. Obiekty I/O są generowane automatycznie z losowymi identyfikatorami (np. DIN9518).

Jak to działa#

graph LR
    A[Wizard] --> B[.vclu.json] --> C["config.txt +<br/>CONFIG.JSON"] --> D[OM] --> E[om.lua] --> F[runtime]
  1. Użytkownik dodaje moduł w wizardzie
  2. vCLU zapisuje konfigurację w .vclu.json
  3. Generuje config.txt i CONFIG.JSON z wpisami TFBus
  4. Object Manager (OM) odczytuje konfigurację i generuje om.lua
  5. Przy starcie Lua ładuje om.lua - obiekty DIN są dostępne w runtime

Dostęp do obiektów#

-- Przez ID z OM
local przycisk = _:get("CLU.DIN9518")
przycisk:add_event(DIN.EVENT_ON_CLICK, function()
    log.info("Klik!")
end)

-- Albo przez nazwe nadana w wizardzie
local przycisk = _:get("CLU.DIN8_1_DIN1")

.vclu.json#

{
  "device": {
    "modules": [
      {
        "typeId": "din8",
        "serial": "181000015",
        "name": "DIN8_1",
        "hardwareType": 20
      }
    ],
    "ioObjects": [
      {
        "id": "DIN9518",
        "typeId": "din",
        "index": 0,
        "name": "DIN8_1_DIN1",
        "luaType": 3
      }
    ]
  }
}

Lokalne moduły DIN nie czytają fizycznych wejść - stan można zmieniać programowo przez EventBus lub z poziomu panelu webowego. Do podłączenia prawdziwych przycisków i czujników na Raspberry Pi użyj GPIO_DIN.

GPIO_DIN - tworzenie#

local btn = GPIO_DIN:new(name, pin, pull)
ParametrTypOpis
namestringNazwa globalna (np. "BTN1")
pinnumberNumer pinu BCM
pullstring"up", "down" lub "off" (domyślnie "up")
-- Przycisk z pull-up (typowe - przycisk zwiera pin do GND)
local btn = GPIO_DIN:new("BTN1", 27)

-- Czujnik z pull-down
local sensor = GPIO_DIN:new("SENSOR1", 22, "down")

Pull-up vs pull-down#

TypOpisKiedy używać
"up"Pin domyślnie HIGH, przycisk ciągnie do LOWPrzyciski (najczęściej)
"down"Pin domyślnie LOW, czujnik ciągnie do HIGHCzujniki z wyjściem HIGH
"off"Bez pull - wymaga zewnętrznego rezystoraSpecjalne zastosowania

DIN z OM - obiekty z Object Manager#

Obiekty DIN z OM są tworzone automatycznie po sparowaniu z zewnętrznym kontrolerem:

local przycisk = _:get("CLU.DIN1")
przycisk:add_event(DIN.EVENT_ON_CLICK, function()
    log.info("Klik!")
end)

Porównanie wariantów#

Lokalny DINDIN z OMGPIO_DIN
ŹródłoWizard (vCLU)Object Manager (OM)user.lua
SprzętBrak (w pamięci)Zewnętrzny kontrolerPin GPIO Raspberry Pi
Konfiguracja.vclu.jsonom.luaOM generuje om.luaGPIO_DIN:new() w kodzie
ZastosowanieTestowanie, emulacjaProdukcja z prawdziwym CLUProdukcja na Raspberry Pi
APIget/set/add_eventget/set/add_eventget/set/add_event
GenericButtontaktaktak
expose()taktaktak
exposeRegistry()tak (automatycznie)tak (automatycznie)tak (po registerObject)

Stałe#

Cechy (Features)#

StałaWartośćOpisR/W
DIN.FEATURE_VALUE0Stan wejścia: 1=ON, 0=OFFR
DIN.FEATURE_INTERTION1Czas debounce (ms)R/W
DIN.FEATURE_HOLD_DELAY2Opóźnienie hold (ms)R/W
DIN.FEATURE_HOLD_INTERVAL3Interwał cyklicznego hold (ms)R/W

Zdarzenia#

StałaWartośćOpis
DIN.EVENT_ON_CHANGE0Zmiana wartości (ON→OFF lub OFF→ON)
DIN.EVENT_ON_SWITCH_ON1Przejście w stan ON
DIN.EVENT_ON_SWITCH_OFF2Przejście w stan OFF
DIN.EVENT_ON_SHORT_PRESS3Krótkie naciśnięcie (po puszczeniu)
DIN.EVENT_ON_LONG_PRESS4Długie naciśnięcie (po puszczeniu)
DIN.EVENT_ON_HOLD5Cykliczne zdarzenie przy przytrzymaniu
DIN.EVENT_ON_CLICK6Kliknięcie (po debounce)

Diagram zdarzeń#

Przebieg zdarzeń w czasie po naciśnięciu przycisku:

sequenceDiagram
    participant btn as Przycisk

    Note right of btn: Naciśnięcie
    btn->>btn: debounce (intertion)
    btn->>btn: EVENT_ON_CLICK + EVENT_ON_SWITCH_ON

    Note right of btn: holdDelay
    btn->>btn: holdFlag = true

    btn->>btn: EVENT_ON_HOLD (co holdInterval)
    btn->>btn: EVENT_ON_HOLD
    btn->>btn: EVENT_ON_HOLD

    Note right of btn: shortPressInterval
    btn->>btn: shortPressFlag

    Note right of btn: longPressInterval
    btn->>btn: longPressFlag

    Note right of btn: Puszczenie
    btn->>btn: EVENT_ON_SWITCH_OFF
    btn->>btn: EVENT_ON_SHORT_PRESS (lub LONG_PRESS)
  • EVENT_ON_CLICK - emitowany zaraz po debounce (po naciśnięciu)
  • EVENT_ON_SHORT_PRESS - emitowany po puszczeniu, jeśli trzymano < longPressInterval
  • EVENT_ON_LONG_PRESS - emitowany po puszczeniu, jeśli trzymano > longPressInterval
  • EVENT_ON_HOLD - emitowany cyklicznie co holdInterval ms po przekroczeniu holdDelay

API#

get(feature)#

Odczytuje wartość cechy.

local stan = btn:get(DIN.FEATURE_VALUE)        -- 0 lub 1
local debounce = btn:get(DIN.FEATURE_INTERTION) -- ms

set(feature, value)#

Ustawia wartość cechy (tylko parametry konfiguracyjne, nie wartość wejścia).

-- Ustaw debounce na 50ms
btn:set(DIN.FEATURE_INTERTION, 50)

-- Ustaw opoznienie hold na 1 sekundy
btn:set(DIN.FEATURE_HOLD_DELAY, 1000)

-- Ustaw interwal hold na 200ms
btn:set(DIN.FEATURE_HOLD_INTERVAL, 200)

add_event(event, callback)#

Rejestruje callback na zdarzenie.

btn:add_event(DIN.EVENT_ON_CLICK, function()
    log.info("Klik!")
end)

btn:add_event(DIN.EVENT_ON_SHORT_PRESS, function()
    log.info("Krotkie nacisniecie")
end)

btn:add_event(DIN.EVENT_ON_LONG_PRESS, function()
    log.info("Dlugie nacisniecie")
end)

btn:add_event(DIN.EVENT_ON_HOLD, function()
    log.info("Trzymam...")
end)

onChange(callback)#

Rejestruje callback na zmianę wartości. Zwraca funkcję unsubscribe. Używany przez system expose().

local unsub = btn:onChange(function(newValue, oldValue)
    log.info("Zmiana: " .. oldValue .. " → " .. newValue)
end)

unsub()  -- wyrejestruj

Domyślne parametry#

ParametrWartośćOpis
intertion (debounce)0 msBrak debounce (natychmiastowa reakcja)
holdDelay500 msPo 500ms zaczyna emitować EVENT_ON_HOLD
holdInterval50 msHold emitowany co 50ms
shortPressInterval2000 msNaciśnięcie < 2s = krótkie
longPressInterval5000 msNaciśnięcie > 5s = długie

Przykłady#

Przycisk przełączający lampę#

local btn = GPIO_DIN:new("BTN1", 27)
local relay = GPIO_DOUT:new("RELAY1", 17, {activeLow = true})

btn:add_event(DIN.EVENT_ON_CLICK, function()
    relay:execute(DOUT.METHOD_SWITCH)
end)

Krótkie/długie naciśnięcie - różne akcje#

local btn = GPIO_DIN:new("BTN1", 27)

btn:add_event(DIN.EVENT_ON_SHORT_PRESS, function()
    log.info("Krotkie - przelacz lampe")
    RELAY1:execute(DOUT.METHOD_SWITCH)
end)

btn:add_event(DIN.EVENT_ON_LONG_PRESS, function()
    log.info("Dlugie - wylacz wszystko")
    RELAY1:execute(DOUT.METHOD_SWITCH_OFF)
    RELAY2:execute(DOUT.METHOD_SWITCH_OFF)
    RELAY3:execute(DOUT.METHOD_SWITCH_OFF)
end)

Czujnik ruchu z eksponowaniem#

local motion = GPIO_DIN:new("MOTION1", 22, "down")
expose(motion, "motion", {name = "Ruch korytarz"})

motion:add_event(DIN.EVENT_ON_SWITCH_ON, function()
    log.info("Wykryto ruch!")
    SWIATLO:execute(DOUT.METHOD_SWITCH_ON, 120000)  -- 2 minuty
end)

Czujnik kontaktowy (drzwi/okno)#

local drzwi = GPIO_DIN:new("DRZWI1", 23)
expose(drzwi, "sensor", {name = "Drzwi wejsciowe"})

drzwi:add_event(DIN.EVENT_ON_SWITCH_ON, function()
    log.info("Drzwi otwarte")
end)

drzwi:add_event(DIN.EVENT_ON_SWITCH_OFF, function()
    log.info("Drzwi zamkniete")
end)

Dimmer - przytrzymaj żeby rozjaśniać#

local btn = GPIO_DIN:new("BTN_DIM", 25)
local brightness = 50

btn:set(DIN.FEATURE_HOLD_DELAY, 300)
btn:set(DIN.FEATURE_HOLD_INTERVAL, 100)

btn:add_event(DIN.EVENT_ON_CLICK, function()
    _:get("CLU.DIMM1"):execute(DOUT.METHOD_SWITCH)
end)

btn:add_event(DIN.EVENT_ON_HOLD, function()
    brightness = clamp(brightness + 2, 0, 100)
    _:get("CLU.DIMM1"):set(0, brightness)
end)

Przycisk z OM#

local przycisk = _:get("CLU.DIN1")

przycisk:add_event(DIN.EVENT_ON_CLICK, function()
    _:get("CLU.DOUT1"):execute(DOUT.METHOD_SWITCH)
end)

przycisk:add_event(DIN.EVENT_ON_LONG_PRESS, function()
    runScene("wylacz_wszystko")
end)

Zwiększ debounce dla szumiącego czujnika#

local czujnik = GPIO_DIN:new("CZUJNIK1", 24, "up")
czujnik:set(DIN.FEATURE_INTERTION, 100)  -- 100ms debounce

Przykłady mieszane - GPIO_DIN + lokalne moduły#

Przycisk GPIO steruje lokalnymi DOUT z wizarda#

-- Fizyczny przycisk na Raspberry Pi
local btn = GPIO_DIN:new("BTN1", 27)

-- Lokalne DOUT z wizarda
local lampa_salon = _:get("CLU.DOU5048")
local lampa_kuchnia = _:get("CLU.DOU4458")

-- Klik - przelacz salon
btn:add_event(DIN.EVENT_ON_CLICK, function()
    lampa_salon:execute(DOUT.METHOD_SWITCH)
end)

-- Dlugie nacisniecie - wylacz wszystko
btn:add_event(DIN.EVENT_ON_LONG_PRESS, function()
    lampa_salon:execute(DOUT.METHOD_SWITCH_OFF)
    lampa_kuchnia:execute(DOUT.METHOD_SWITCH_OFF)
end)

Czujnik ruchu GPIO + lokalny DOUT podpięty pod Tasmotę#

Fizyczny czujnik ruchu na GPIO automatycznie włącza gniazdko Tasmota (przez lokalny DOUT):

local mqtt = MQTT:new("tasmota")
mqtt:setHost("localhost"):setPort(1883):connect()

-- Czujnik ruchu na GPIO
local motion = GPIO_DIN:new("MOTION1", 22, "down")
expose(motion, "motion", {name = "Ruch korytarz"})

-- Lokalny DOUT zsynchronizowany z Tasmota
local swiatlo = _:get("CLU.DOU5048")
expose(swiatlo, "switch", {name = "Swiatlo korytarz"})

swiatlo:add_event(DOUT.EVENT_ON_SWITCH_ON, function()
    mqtt:publish("cmnd/swiatlo_korytarz/POWER", "ON")
end)
swiatlo:add_event(DOUT.EVENT_ON_SWITCH_OFF, function()
    mqtt:publish("cmnd/swiatlo_korytarz/POWER", "OFF")
end)

-- Ruch wykryty - wlacz swiatlo na 2 minuty
motion:add_event(DIN.EVENT_ON_SWITCH_ON, function()
    swiatlo:execute(DOUT.METHOD_SWITCH_ON, 120000)
end)