DIN - wejscie cyfrowe#

DIN (Digital Input) to obiekt reprezentujacy wejscie cyfrowe - przycisk, czujnik kontaktowy, czujnik ruchu. Wystepuje w trzech wariantach:

  • Lokalny DIN - wirtualny modul vCLU, konfigurowany w wizardzie (emuluje fizyczny modul Grenton)
  • DIN z OM - obiekt z Object Manager, po sparowaniu z fizycznym CLU Grenton
  • GPIO_DIN - obiekt tworzony w user.lua, podlaczony do pinu GPIO Raspberry Pi

Wszystkie warianty maja ten sam interfejs API i te same stale. Obsluguja debounce, rozpoznawanie krotkich i dlugich nacisnieci oraz zdarzenia hold (przytrzymanie).

Lokalny DIN - moduly wirtualne#

Gdy vCLU dziala jako emulator CLU (tryb CLU w wizardzie), mozna dodac wirtualne moduly wejsc. Sa to emulacje fizycznych modulow Grenton - nie czytaja prawdziwego sprzetu, ale pojawiaja sie w OM i moga byc uzywane do testow i integracji.

Konfiguracja w wizardzie#

W kroku 2 wizarda (“Moduly”) mozna dodac moduly z wejsciami cyfrowymi:

Typ moduluWejscia DINWyjscia DOUTAnalogHW Type
DIN88-10x14
DOUT8T8810x1e

Kazdy modul DIN dostaje unikalna nazwe (np. DIN8_1) i numer seryjny. Obiekty I/O sa generowane automatycznie z losowymi identyfikatorami (np. DIN9518).

Jak to dziala#

Wizard → .vclu.json → config.txt + CONFIG.JSON → OM → om.lua → runtime
  1. Uzytkownik dodaje modul w wizardzie
  2. vCLU zapisuje konfiguracje w .vclu.json
  3. Generuje config.txt i CONFIG.JSON z wpisami TFBus
  4. Object Manager (OM) odczytuje konfiguracje i generuje om.lua
  5. Przy starcie Lua laduje om.lua - obiekty DIN sa dostepne w runtime

Dostep do obiektow#

-- 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 moduly DIN nie czytaja fizycznych wejsc - stan mozna zmieniac programowo przez EventBus lub z poziomu panelu webowego. Do podlaczenia prawdziwych przyciskow i czujnikow na Raspberry Pi uzyj `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" (domyslnie "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 uzywac
"up"Pin domyslnie HIGH, przycisk ciagnie do LOWPrzyciski (najczesciej)
"down"Pin domyslnie LOW, czujnik ciagnie do HIGHCzujniki z wyjsciem HIGH
"off"Bez pull - wymaga zewnetrznego rezystoraSpecjalne zastosowania

DIN z OM - obiekty z Object Manager#

Obiekty DIN z OM sa tworzone automatycznie po sparowaniu z fizycznym CLU Grenton:

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

Porownanie wariantow#

Lokalny DINDIN z OMGPIO_DIN
ZrodloWizard (vCLU)Object Manager (OM)user.lua
SprzetBrak (w pamieci)Fizyczny modul GrentonPin 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)

Stale#

Cechy (Features)#

StalaWartoscOpisR/W
DIN.FEATURE_VALUE0Stan wejscia: 1=ON, 0=OFFR
DIN.FEATURE_INTERTION1Czas debounce (ms)R/W
DIN.FEATURE_HOLD_DELAY2Opoznienie hold (ms)R/W
DIN.FEATURE_HOLD_INTERVAL3Interwat cyklicznego hold (ms)R/W

Zdarzenia#

StalaWartoscOpis
DIN.EVENT_ON_CHANGE0Zmiana wartosci (ON→OFF lub OFF→ON)
DIN.EVENT_ON_SWITCH_ON1Przejscie w stan ON
DIN.EVENT_ON_SWITCH_OFF2Przejscie w stan OFF
DIN.EVENT_ON_SHORT_PRESS3Krotkie nacisniecie (po puszczeniu)
DIN.EVENT_ON_LONG_PRESS4Dlugie nacisniecie (po puszczeniu)
DIN.EVENT_ON_HOLD5Cykliczne zdarzenie przy przytrzymaniu
DIN.EVENT_ON_CLICK6Klikniecie (po debounce)

Diagram zdarzen#

Przebieg zdarzen w czasie po nacisnieciu przycisku:

Nacisniecie                                          Puszczenie
    │                                                    │
    ├─── debounce (intertion) ──▶ EVENT_ON_CLICK         │
    │                             EVENT_ON_SWITCH_ON      │
    │                                                    │
    ├─── holdDelay ─────────────▶ (holdFlag = true)      │
    │                                                    │
    ├─── holdInterval ──────────▶ EVENT_ON_HOLD          │
    ├─── holdInterval ──────────▶ EVENT_ON_HOLD          │
    ├─── holdInterval ──────────▶ EVENT_ON_HOLD          │
    │              ...                                   │
    │                                                    │
    ├─── shortPressInterval ────▶ (shortPressFlag)       │
    │                                                    │
    ├─── longPressInterval ─────▶ (longPressFlag)        │
    │                                                    ▼
    │                                    EVENT_ON_SWITCH_OFF
    │                                    EVENT_ON_SHORT_PRESS (lub LONG_PRESS)
  • EVENT_ON_CLICK - emitowany zaraz po debounce (po nacisnieciu)
  • EVENT_ON_SHORT_PRESS - emitowany po puszczeniu, jesli trzymano < longPressInterval
  • EVENT_ON_LONG_PRESS - emitowany po puszczeniu, jesli trzymano > longPressInterval
  • EVENT_ON_HOLD - emitowany cyklicznie co holdInterval ms po przekroczeniu holdDelay

API#

get(feature)#

Odczytuje wartosc cechy.

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

set(feature, value)#

Ustawia wartosc cechy (tylko parametry konfiguracyjne, nie wartosc wejscia).

-- 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 zmiane wartosci. Zwraca funkcje unsubscribe. Uzywany przez system expose().

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

unsub()  -- wyrejestruj

Domyslne parametry#

ParametrWartoscOpis
intertion (debounce)0 msBrak debounce (natychmiastowa reakcja)
holdDelay500 msPo 500ms zaczyna emitowac EVENT_ON_HOLD
holdInterval50 msHold emitowany co 50ms
shortPressInterval2000 msNacisniecie < 2s = krotkie
longPressInterval5000 msNacisniecie > 5s = dlugie

Przyklady#

Przycisk przelaczajacy lampe#

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)

Krotkie/dlugie nacisniecie - rozne 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 zeby rozjasniac#

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)

Zwieksz debounce dla szumiajacego czujnika#

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

Przyklady mieszane - GPIO_DIN + lokalne moduly#

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 podpiety pod Tasmote#

Fizyczny czujnik ruchu na GPIO automatycznie wlacza 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)