WS2812B LED gebaseerd op domoticz security state

Oke, first things first:

  • Het is geen goed idee om te paraderen met de arming state van je alarmsysteem, dus wees subtiel!
  • De manier waarop dit project verbinding maakt tussen LED-strip en RPI is door middel van korte LED-strips, anders overbelast je je GPIO, en kun je je RPI weggooien. Doe dus goed onderzoek :)

Missie

Wat ik wilde bereiken met dit project was grafisch duidelijk maken wat de arming state van m'n alarmsysteem (vanuit Domoticz) was. Echter, ik had geen ervaring met LED-strips, Raspberry Pi GPIO, een klein beetje python kennis. Wat er uiteindelijk is ontstaan is een constructie waarbij Domoticz mijn alarmsysteem beheert, de staat scriptmatig vertaalt naar een kleur en die kleur fancy laat zien op verschillende momenten. State-change is zo'n moment, maar ook elk uur vindt er een check-up plaats.

Benodigdheden

  • Raspberry PI 3b/4b (link)
  • Domoticz draaiende op de RPI van hierboven (link)
  • Wizzard72's Domoticz Alarm System plugin * (link)
  • rpi_ws281x python module (link)
  • Script (komen we zo op)
  • GPIO kabels (link)
  • WS2812B LED strip (link)

* deze plugin is uiteraard gratis en open source en werkt veel fijner dan de ingebouwde security class waaraan je een alarmsysteem moet knopen.

LED-strip aansluiten

Zet eerst even je Raspberry uit (sudo shutdown now) en trek daarna de stroomkabel eruit. Het aansluiten van de LED-strip is niet complex, maar je moet goed in de gaten houden wat je doet. Normaliter zou je voor de stabiliteit en de bescherming van je Raspberry PI GPIO een externe power supply gebruiken voor de stroom en alleen een data-lijn vanuit de GPIO aansluiten. Echter, mijn strip is zo klein (10 LED's) dat het prima werkt en veilig kan. De RPI trekt ongeveer 3 Ampere, de LED's tussen de 0,02A en 0,06A per (afhankelijk van type en brightness). Ik kies voor dit soort projecten de middenroute, 0,04A en dat x10 LEDS, is dus 0,4A. Dit komt bovenop de load van de RPI (3A) en trekt in totaal dus piek zo'n 3,4A.

De manier waarop de LED-strip in dit scenario aangesloten worden op de GPIO van de Raspberry PI is als volgt:

Dit plaatje komt van: https://learn.adafruit.com/neopixels-on-raspberry-pi/raspberry-pi-wiring. Check hier als je een ander bedradingsscenario nodig hebt omdat jouw LED-vereisten anders zijn.

Let ook goed op het pijltje op de LED-strip, dat is de richting van aansluiting van LED's. Als je de GPIO in tegengestelde richting van het pijltje aansluit, doet de LED-strip het niet. Zorg dus dat de GPIO leidt naar het begin van de pijl op de LED-strip. Vervolgens kun je de LED-strip op maat knippen (knippen op het koperen stripje met de verticale lijn er doorheen). Dit kun je overal in de LED-strip doen en je hoeft hem niet 'af te sluiten', hij houdt vanzelf op als hij ophoudt. Deze WS2812B strips zijn immers individueel addresseerbaar. Gooi het restant niet weg, deze kun je volgens bovenstaand schema weer opnieuw aansluiten. Dan heb je alleen een soldeerbout, tin en soldeerskills nodig. Ten slotte kun je de LED-strip monteren, afwerken met een diffusor, maar ik stel voor dat je dat nog niet doet, laten we eerst kijken of alles werkt. Doe de stroomaansluiting weer terug en de Raspberry zal opstarten.

Wizzard72's domoticz-alarm plugin

Als eerste softwarematige stap moet de domoticz-alarm plugin van Wizzard72 geinstalleerd worden. Echter, dit is buiten-scope van dit document. Lees de readme uit de git en daar kom je écht wel uit. Wat je nodig hebt voor mijn verhaal is de IDX van de "SIREN", zoek die op en noteer die, in mijn geval is het 232.

rpi_ws281x

Deze python module is bedoeld om vanuit de Raspberry PI GPIO direct met een WS281x-serie LED strip te praten, waaronder dus ook de WS2812b die ik nu gebruik. Een alternatieve module is neopixel, maar ik vond rpi_ws281x erg makkelijk en customizable (geldt voor neopixel ook, maar daar heb ik me nog niet genoeg in verdiept). Deze module heb je nodig, dus op de RPI doe je:
sudo pip3 install rpi_ws281x
Na enige tijd is de module geinstalleerd en kun je hem gebruiken.

Script

Ik ben na enig experimenteren op het volgende script uitgekomen:
#!/usr/bin/env python3
# ============================================================
# alarmLed v2
# Author: J. Erkelens
# Date: March, 2021
#
# Params: 4: alarmLed2.py runtimes R-value G-value B-value
#         example: alarmLed2.py 4 255 0 255
#         will flash the led 4 times in purple color
# ============================================================
import sys
import time
from rpi_ws281x import *
import urllib.request, json

LED_COUNT      = 10      # Number of LED pixels.
LED_PIN        = 18      # GPIO pin connected to the pixels (18 uses PWM!).
LED_FREQ_HZ    = 800000  # LED signal frequency in hertz (usually 800khz)
LED_DMA        = 10      # DMA channel to use for generating signal (try 10)
LED_BRIGHTNESS = 255     # Set to 0 for darkest and 255 for brightest
LED_INVERT     = False   # True to invert the signal (when using NPN transistor level shift)
LED_CHANNEL    = 0       # set to '1' for GPIOs 13, 19, 41, 45 or 53
arg = sys.argv


#
# Colorwipe
#
def colorWipe(strip, color, wait_ms=25):
    for i in range(strip.numPixels()):
        strip.setPixelColor(i, color)
        strip.show()
        time.sleep(wait_ms/1000.0)

if __name__ == '__main__':
    strip = Adafruit_NeoPixel(LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, LED_BRIGHTNESS, LED_CHANNEL)
    strip.begin()

    #
    # Args
    #
    if len(arg) == 6:
       runTimes = int(sys.argv[1])
       valueR = int(sys.argv[2])
       valueG = int(sys.argv[3])
       valueB = int(sys.argv[4])
       run = 0
       while run < runTimes:
          colorWipe(strip, Color(valueR, valueG, valueB))
          colorWipe(strip, Color(0, 0, 0))
          run += 1

    #
    # No args
    #
    arg = sys.argv
    if len(arg) == 1:
       with urllib.request.urlopen("http://[[DomoticzIP:DomoticzPoort]]/json.htm?type=devices&rid=232") as url:
          data = json.loads(url.read().decode())
          result = str(data['result'][0]['Level'])
          print('Alarm level: ' + result)

          # Armed Away / Red
          if (int(result) == 20):
             run = 1
             while run < 2:
                colorWipe(strip, Color(255, 0, 0))
                colorWipe(strip, Color(0, 0, 0))
                run += 1

          # Armed Home / Blue
          if (int(result) == 10):
             run = 1
             while run < 2:
                colorWipe(strip, Color(0, 0, 255))
                colorWipe(strip, Color(0, 0, 0))
                run += 1

          # Disamed / Green
          if (int(result) == 0):
             run = 1
             while run < 2:
                colorWipe(strip, Color(0, 255, 0))
                colorWipe(strip, Color(0, 0, 0))
                run += 1

Kopieer de tekst en sla het op als Python file, bijvoorbeeld: alarmLed2.py. Pas even '[[DomoticzIP:DomoticzPoort]]' aan en maak het script uitvoerbaar met: sudo chmod +x alarmLed2.py en open hem daarna met een editor met regelnummers. Je ziet een aantal dingen gebeuren:

Regel 17: LED_PIN = 18
- Hier geef je aan elke data-pin je van GPIO hebt gebruikt (middelste kabel van de LED-strip)

Regel 58: with urllib.request.urlopen("http://[[DomoticzIP:DomoticzPoort]]/json.htm?type=devices&rid=232") as url:
- Hier geef je 2 items aan: de Domoticz server (inclusief poort) en de IDX van het SIREN-device (in mijn geval was dat dus 232

Dit script doet een aantal dingen op basis van script argumenten:

  • Als je geen argumenten meegeeft, doet het script een check-up (regel 53-85). Hij kijkt via de Domoticz API naar de status van de alarm selector switch en leidt van deze waarde de arming state van het alarmsysteem af
  • Als je wel argumenten meegeeft, geeft het script die argument als kleur door (regel 26-52). De argumenten zijn respectievelijk: aantal herhalingen van de animatie, waarde Rood, waarde Groen, waarde Blauw
Het idee hiervan is dat je parameters gebruikt in Domoticz en parameter-loos het script aanroept via cron.

Domoticz

Als je in Domoticz gebruik maakt van Wizzard72's Alarm plugin, heb je een selector switch gekregen, genaamd 'Alarmsysteem - Arming Mode (Zone 0)'. Deze kun je openen in Domoticz ('aanpassen') en bij 'Selectie acties' kun je scripts koppelen:

Let op dat je 3x een forward slash ('/') gebruikt bij script, als je Domoticz hebt draaien op Raspberry OS. En zorg dat je de juiste argumenten meegeeft aan het script, respectievelijk: Disarmed, Armed Home en Armed Away.

Crontab

So far so good, maar nu licht de LED-strip alleen op als je een statuswijziging van de arming state doorvoert. Met andere woorden: van een staat naar een andere staat. Maar het is natuurlijk niet tof als je moet wijzigen alvorens je ziet op welke staat het alarmsysteem staat. Ja ok, je kan in Domoticz kijken, maar dat willen we niet. Hier is een rol voor crontab weggelegd, de ingebouwde task scheduler van Raspberry OS. Je bent hier helemaal vrij in of en hoe je dit wilt doen, ik vond het wel tof om elk uur een updateje te krijgen. Ik doe dat door het script parameter-loos te draaien.
Typ: sudo crontab -e
En vul daar onderaan het volgende toe: 0 */1 * * * cd /home/pi/scripts && python3 alarmLed2.py
Doe daarna: CTRL X gevolgd door y en tadaa.. Klaar :)

Je kan de LED-strip virtueel overal plakken waar je hem wilt, zolang hij maar in de buurt van je Raspberry PI ligt, in verband met de kabeltjes). Ik gebruik kabels van 60cm en die LED-strip zit geplakt in mijn tv-kast, geeft een tof effect in de kast. Je zou natuurlijk ook de LED-strip achter een fotolijstje kunnen plaatsen, etc. Houd wel in je achterhoofd dat als je hem als indirect licht gebruikt, je met dit kleine LED aantal, overdags weinig zult zien van het licht. Je kunt hem ook als directe lichtbron gebruiken, maar dan moet je iets verzinnen met een LED diffusor. Een hitte-bestendige pingpongbal kan ook al een diffusor zijn, Google maar!