Skoro každou chytrou žárovku můžete ovládat ve vlastním programu. Ať už pomocí oficiálního API, nebo hackingu. Dnes proměníme WiFi žárovku WiZ v barevný semafor

Skoro každou chytrou žárovku můžete ovládat ve vlastním programu. Ať už pomocí oficiálního API, nebo hackingu. Dnes proměníme WiFi žárovku WiZ v barevný semafor

RGB LED WiZ vypadá na první pohled jako každá jiná Wi-Fi žárovka pro závit E27, tato ale umí jedno kouzlo. V zapojení do sítě s dalšími kusy umí detekovat člověka 

RGB LED WiZ vypadá na první pohled jako každá jiná Wi-Fi žárovka pro závit E27, tato ale umí jedno kouzlo. V zapojení do sítě s dalšími kusy umí detekovat člověka 

RGB LED WiZ vypadá na první pohled jako každá jiná Wi-Fi žárovka pro závit E27, tato ale umí jedno kouzlo. V zapojení do sítě s dalšími kusy umí detekovat člověka 

RGB LED WiZ vypadá na první pohled jako každá jiná Wi-Fi žárovka pro závit E27, tato ale umí jedno kouzlo. V zapojení do sítě s dalšími kusy umí detekovat člověka 

Červený semafor

Červený semafor

Oranžový semafor

Oranžový semafor

Zelenožlutý semafor

Zelenožlutý semafor

Modrý semafor

Modrý semafor

Schéma zapojení na nepájivém prototypovacím poli

Schéma zapojení na nepájivém prototypovacím poli

Barevný model HSV

Barevný model HSV

Barevný model HSV

Barevný model HSV

Odstín HSL v rozsahu 200-0°, který použijeme jako barevnou škálu teplot 10-30 °C

Odstín HSL v rozsahu 200-0°, který použijeme jako barevnou škálu teplot 10-30 °C

Kontrolní výstup do sériového monitoru. Žárovka má IP adresu 172.17.16.198 a na každou instrukci s barvami RGB odpoví potvrzovací zprávou

Kontrolní výstup do sériového monitoru. Žárovka má IP adresu 172.17.16.198 a na každou instrukci s barvami RGB odpoví potvrzovací zprávou

Kontrolní výstup do sérového monitoru. Na začátku program rozeslal do sítě broadcastovou zprávu a uložil si IP adresy zařízení WiZ, která odpověděla

Kontrolní výstup do sérového monitoru. Na začátku program rozeslal do sítě broadcastovou zprávu a uložil si IP adresy zařízení WiZ, která odpověděla

Schéma zapojení na nepájivém prototypovacím poli

Schéma zapojení na nepájivém prototypovacím poli

Odstín HSL v rozsahu 0-220°, který použijeme jako barevnou škálu relativní vlhkosti 30-80 %

Odstín HSL v rozsahu 0-220°, který použijeme jako barevnou škálu relativní vlhkosti 30-80 %

RGB LED WiZ vypadá na první pohled jako každá jiná Wi-Fi žárovka pro závit E27, tato ale umí jedno kouzlo. V zapojení do sítě s dalšími kusy umí detekovat člověka 
RGB LED WiZ vypadá na první pohled jako každá jiná Wi-Fi žárovka pro závit E27, tato ale umí jedno kouzlo. V zapojení do sítě s dalšími kusy umí detekovat člověka 
Červený semafor
Oranžový semafor
26
Fotogalerie

Toto vaše žárovka neumí. Proměníme ji v teploměr, vlhkoměr, indikátor CPU a parodii na Philips Ambilight

  • Skoro každou chytrou žárovku můžete ovládat ve vlastním programu
  • Ať už pomocí oficiálního API, nebo hackingu
  • Dnes proměníme WiFi žárovku WiZ v barevný semafor

Máte doma alespoň jednu chytrou RGB LED žárovku? Pak ji můžete ovládat z mobilní aplikace a nastavovat barvu a jas dle libosti. Fajn, ale to je málo. Z toho se v roce 2023 na zadek nikdo neposadí.

Ukážeme si, co s takovou žárovkou dokáže každý domácí kutil, který ji na pár řádcích kódu promění v barevný semafor. Ten pak bude měnit odstín od temně modré po sytou rudou třeba podle teploty vzduchu, vlhkosti, dění na ploše počítače nebo zátěže procesoru.

Něco takového drtivá většina chytrých světýlek vůbec neumí, ale my to zvládneme levou zadní v Pythonu na velkém počítači a v Arduinu na malé destičce s Wi-Fi čipem ESP32. Takže dost řečí, jdeme na to.

Dnešní experiment provádíme s chytrými RGB LED žárovkami značky WiZ od Signify (někdejší Philips Lighting). Konkrétně pak s nejvýkonnějším 13W modelem pro závit E27.

RGB LED žárovka jako teploměr

Co kdyby měnila žárovka odstín podle teploty? Pak by mohla sloužit jako efektní indikátor třeba ve skleníku, kde nesmí klesnout teplota pod stanovenou mez, anebo v serverovně, kde by byla zase její rudá barva dostatečně důrazným varováním, že je nejvyšší čas investovat do lepšího chlazení.

Podívejte se, jak jsme proměnili lampu WiZ v teploměr:

Vyzkoušíme si to s digitálním teploměrem řady Sensirion SHT3x na některém z mnoha prototypovacích modulů, který skrze sběrnici I²C připojíme k základní desce s Wi-Fi konektivitou.

V našem seriálu pracujeme povětšinou s českou deskou ESP32-LPKit s Wi-Fi čipem ESP32, ale samozřejmě můžete sáhnout po čemkoliv jiném. K desce připojíme skrze sběrnici I²C ještě maličký 0,96“ OLED za stokorunu s řadičem SSD1306, na kterém budeme pro kontrolu vypisovat aktuální teplotu vzduchu.

f09a1b8c-b0b5-4b72-86bf-1387edf039a2
Schéma zapojení na nepájivém prototypovacím poli

Teplotu přepočítáme na barevnou škálu HSV

JAK to bude fungovat? Každou sekundu změříme teplotu vzduchu a hodnotu převedeme na nějakou hezkou barevnou škálu od nejchladnějšího po nejteplejší odstín. Vyrobí ji za nás barevný model HSV.

11a620e4-951c-4a23-a5dd-d90ad93fe7b7f06eaddc-190a-450a-a8c4-afb99a7043be
Barevný model HSV

HSV pracuje se složkami hue (odstín), saturation (sytost) a value (hodnota jasu). Pro nás je klíčová služka hue, protože ji můžeme vyjádřit ve stupních 0-360 a celý tento rozsah představuje v podstatě po sobě jdoucí barvy duhy:

  • 0°: rudá
  • 60°: žlutá
  • 120°: zelená
  • 250°: modrá
  • 300°: fialová
  • 360°: opět rudá

Aby byl efekt změny barvy redakční LED dostatečně rychlý, budeme pracovat s teplotním rozsahem 10-30 °C a přepočítáme jej na rozsah odstínu HSL 200-0°.

81fb4941-6bbd-44de-a721-dbdb0b5f67db
Odstín HSL v rozsahu 200-0°, který použijeme jako barevnou škálu teplot 10-30 °C

Získáme tak krásné a spojité přechodové barvy od světlemodré pro teplotu 10 °C a méně po rudou pro teplotu 30 °C a výše. Jamile získáme barvu v modelu HSV, přepočítáme ji na model RGB a tuto hodnotu už pošleme do žárovky.

Barvu žárovky nastavíme pomocí UDP

Pro přímou komunikaci s žárovkou použijeme síťový protokol UDP, skrze který budeme do zařízení posílat krátké textové instrukce ve formátu JSON. Komunikace se zařízeními WiZ v lokální síti LAN totiž nevyžaduje žádnou formu autorizace a není ani nikterak šifrovaná.

a1166a07-0965-434f-b66e-2eb9a706ad1b
Kontrolní výstup do sériového monitoru. Žárovka má IP adresu 172.17.16.198 a na každou instrukci s barvami RGB odpoví potvrzovací zprávou

Kdybychom chtěli nastavit barvu světla třeba na plnou zelenou ve formátu RGB s osmibitovou hloubkou kanálů (0, 255, 0), bude JSON instrukce, kterou skrze UDP odešleme do žárovky, vypadat takto:

{"method": "setPilot", "params": {"r": 0, "g": 255, "b": 0, "dimming": 100}}

Všimněte si posledního klíče dimming (100 %), kterým nastavujeme jas. V tomto příkladu tedy svítí žárovka zeleně a zároveň s plným jasem.

Program najde všechny žárovky v síti

Ještě ale musíme znát cílovou IP adresu zařízení. Buď ji vyhledáme ručně (já pro sken sítě LAN používám skvělou mobilní apku WiFiman od Ubiquiti), anebo to za nás udělá přímo program pro Arduino.

Na jeho začátku totiž odešleme na předdefinovanou broadcastovací LAN IP a UDP port 38899 instrukci:

{"method": "registration", "params": {"phoneMac": "AAAAAAAAAAAA", "register": false, "phoneIp": "1.2.3.4"}}

Podstatný je klíč registration. Klíče phoneMac a phoneIp jsou fiktivní.

Díky broadcastu se zpráva rozešle na všechna zařízení v místní síti, zareagují ale jen žárovky WiZ, protože jako jediné poslouchají na UDP portu 38899. Zprávu rozluští, všimne si požadavku registration a odpoví nám krátkou zprávou, že existují.

48534534-9784-46ff-89b2-7d8d5c2dfca4
Kontrolní výstup do sérového monitoru. Na začátku program rozeslal do sítě broadcastovou zprávu a uložil si IP adresy zařízení WiZ, která odpověděla

Jakmile zachytíme odpověď, získáme zároveň IP adresu odesílatele, no a budeme ji používat v programu jako adresáta všech dalších zpráv. Pokud bude v síti více zařízení WiZ, na broadcast mohou odpovědět také, no a náš program pak bude posílat instrukci pro nastavení barvy odpovídající teplotě i na ně.  Podle teploty by se pak nastavila všechna světla v bytě.

Zdrojový kód RGB LED teploměru

A to je celé. Níže si můžete projít kompletní kód aplikace pro vývojové prostředí Arduino s doinstalovanou podporou pro čipy ESP32. V kódu používáme knihovny ArduinoJson, Adafruit SSD1306, Adafruit GFX a ClosedCube SHT31D, které budete muset doinstalovat ze správce knihoven Arduina, anebo stáhnout z GitHubu.

// Vestavene knihovny pro
// praci s WI-Fi a UDP
#include <WiFi.h>
#include <WiFiUdp.h>

// Knihovna pro praci s JSON
// https://arduinojson.org
// Lze doinstalovat ze spravce knihoven
#include <ArduinoJson.h>

// Knihovny pro praci s 0.96" I2C OLED
// Lze doinstalovat ze spravce knihoven
#include <Adafruit_SSD1306.h>
#include <Adafruit_GFX.h>

// Knihovny pro praci s I2C teplomery STH3x
// Lze doinstalovat ze spravce knihoven
#include <ClosedCube_SHT31D.h>

// Knihovna pro praci s C++ vektory
#include <vector>

// Funkce pro prevod barev mezi HSV a RGB
// Pouzijeme pro barevnou skalu od modre po cervenou
// Soubor hsv.h musi byt ve stejne slozce jakoc ztento zdrojovy kod
#include "hsv.h"

// Prihlasovaci udaje k Wi-Fi
const char *ssid = "Nazev2.4GHzWiFi";
const char *heslo = "velmiTajneHeslo";

// Broadcastovaci IP adresa v teto siti LAN
// Muzete zkusit i univerzalni 255.255.255.255
// Broadcastovaci IP adresa ma u beznych siti
// zpravidla formu xxx.xxx.xxx.255
// Treba: 192.168.1.255 aj.
IPAddress broadcast_ip(172, 17, 16, 255);

// Vektor pro IP adrssy nalezenych zarizeni WiZ
std::vector<IPAddress> wiz_ip;
// UDP port, na kterem poslouchaji zarizeni WiZ
uint16_t wiz_port = 38899;

// Trida pro praci s UDP, poslochaci port
// a pamet pro prichozi zpravu
WiFiUDP udp;
uint16_t udp_port = 12345;
char udp_zprava[200];

// Pamet pro JSON na 200 znaku
StaticJsonDocument<200> doc;

// Trida teplomeru SHT3x
ClosedCube_SHT31D sht3xd;

// Trida 0.96" OLED s radicwem SSD1306
Adafruit_SSD1306 display(128, 64, &Wire, -1);

// Prodleva mezi merenim teploty a aktua;lizaci zarovek WiZ
uint32_t prodleva_ms = 1000;
uint32_t t = 0;

// Textove zpravy v JSON, kterem budeme posilat do zarovky
String registrace = "{\"method\":\"registration\",\"params\":{\"phoneMac\":\"AAAAAAAAAAAA\",\"register\":false,\"phoneIp\":\"1.2.3.4\"}}";
String sablona_barva = "{\"method\":\"setPilot\",\"params\":{\"r\":%R%,\"g\":%G%,\"b\":%B%,\"dimming\":%JAS%}}";
String barva;

// Funkce pro odeslani UDP zpravy do vsech nalezenych zarovek WiZ
void odesliZpravuDoLampicek(String zprava) {
  for (auto ip = begin(wiz_ip); ip != end(wiz_ip); ++ip) {
    Serial.printf("Zprava pro %s: %s\r\n", ip->toString().c_str(), zprava.c_str());
    udp.beginPacket(*ip, wiz_port);
    udp.print(zprava);
    udp.endPacket();
    delay(10);
  }
}

// Funkce pro vyhledani vsech zarovek WiZ v teo siti LAN pomoci UDP broadcastu
void odesliUdpSken() {
  Serial.printf("Skenuji sit a hledam zarizeni WiZ na broadcast IP adrese %s...\r\n", broadcast_ip.toString().c_str());
  udp.beginPacket(broadcast_ip, wiz_port);
  udp.print(registrace);
  udp.endPacket();
}
// Funkce pro prekresleni displeje
void prekresliDisplej(String nadpis, float hodnota) {
  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);
  display.setTextSize(2);
  display.setCursor(0, 0);
  display.print(nadpis);
  display.setTextSize(3);
  display.setCursor(0, 30);
  display.print(hodnota);
  display.display();
}


// Funkce pro zmereni teploty, spocitani barvy a aktualizace zarovek
void teplomer() {
  // Zkopirujeme si sablonu s JSON pro nastaveni barev
  String barva = sablona_barva;
  // Zmerime teplotu
  SHT31D cidlo = sht3xd.readTempAndHumidity(SHT3XD_REPEATABILITY_LOW, SHT3XD_MODE_CLOCK_STRETCH, 50);
  float teplota = cidlo.t;
  // Nakreslime udaj o teplote na displej
  prekresliDisplej("TEPLOTA", teplota);
  // Prepocitame rozsah teplot 10-30 na rozsah odstinu HSV 200-0
  float hue = map(constrain((int)teplota, 10, 30), 10, 30, 200, 0);
  // Prepocitame model HSV s hodnotou saturace 100 % a jasu 10 % na RGB
  // Funkce pro prepocet jsou v druhem souboru naseho projektu hsv.h
  struct HSV hsv = {hue, 1.0, 0.1};
  struct RGB rgb = HSV2RGB(hsv);
  // V textovem retezci s sablonkou JSONu pro nastaveni barvy nahradime zastupne vyrazy hodnotami R, G, B a jasem zarovky na 100 %
  barva.replace("%R%", String(rgb.R));
  barva.replace("%G%", String(rgb.G));
  barva.replace("%B%", String(rgb.B));
  barva.replace("%JAS%", "100");
  // Odesleme textovy retezec skrze UDP do detekovanych zarovek WiZ
  odesliZpravuDoLampicek(barva);
}

// Hlavni funkce setup se spusti hned na zacatku
void setup() {
  Serial.begin(115200); // Nastartovani seriove linky rychlosti 115200 b/s
  delay(1000);
  Wire.begin(); // Nastartovani sbernice I2C
  sht3xd.begin(0x44); // Pripojeni k teplomeru SHT3x na I2C adrese 0x44
  display.begin(SSD1306_SWITCHCAPVCC, 0x3c); // Pripojeni k OLED displeji na I2C adrese 0x3c
  // Smazani displeje
  display.clearDisplay();
  display.display();
  barva.reserve(500); // Rezwervuj 500 bajtu pro textovy retezec barva
  Serial.println("*** START PROGRAMU ***");
  // Pripojime se k Wi-Fi
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, heslo);
  Serial.printf("Pripojuji se k %s...", ssid);
  while (WiFi.status() != WL_CONNECTED) {
    delay(100);
    Serial.print(".");
  }
  Serial.println(" OK");
  udp.begin(udp_port); // Nastartujeme UDP
  Serial.printf("Moje IP adresa: %s\r\n", WiFi.localIP().toString().c_str());
  Serial.printf("UDP posloucha na portu %d\r\n\r\n", udp_port);
}

// Funkce loop se opakuje stale dokola
void loop() {
  // Pokud zatim neznam ani jednu IP adresu lampicky WiZ,
  // spustim broadcastovy UDP sken,
  // na ktery by mely lampicky odpovedet
  if (wiz_ip.size() == 0) {
    odesliUdpSken();
    delay(1000);
  }
  // Zjistim, jestli dorazil nejaky novy UDP datagram/packet
  int paket = udp.parsePacket();
  // Pokud ano, vypisu jeho obsah a IP adresu odesilatele
  if (paket) {
    IPAddress ip = udp.remoteIP();
    Serial.printf("Odpoved od %s: ", ip.toString().c_str());
    if (udp.read(udp_zprava, 200) == 0) Serial.printf("Zadna data\r\n");
    else {
      Serial.printf("%s\r\n", udp_zprava);
      // Pokusim se zpracovat JSON
      if (!deserializeJson(doc, udp_zprava)) {
        // Pokud ma klic "method" hodnotu "registration", je to odpoved lampicky na broadcastovy sken
        if (!doc["method"].isNull()) {
          if (strcmp(doc["method"].as<const char *>(), "registration") == 0) {
            // Pokud je klic "result/success" roven true,
            // Vypisu MAC a IP lampicky a IP zaroven ulozim do pole lampicek
            if (doc["result"]["success"]) {
              Serial.println("Nasel jsem zarizeni WiZ:");
              Serial.printf(" - IP: %s\r\n", ip.toString().c_str());
              Serial.printf(" - MAC: %s\r\n", doc["result"]["mac"].as<const char *>());
              wiz_ip.push_back(udp.remoteIP());
            }
          }
        }
      }
    }
  }

  // Se stanovenou prodlevou spoustim funkci teplomer
  if (millis() - t > prodleva_ms) {
    teplomer();
    t = millis();
  }
}

Zdrojový kód hlavičkového souboru hsv.h so funkcemi pro přepočet modelu HSV na RGB. Soubor hsv.h musí bát ve stejném adresáři jako halvní zdrojový kód:

// Struktua s 8bitovymi kanaly modelu RGB
struct RGB
{
  uint8_t R;
  uint8_t G;
  uint8_t B;
};

// Struktura se slozkami modelu HSV
struct HSV
{
  double H;
  double S;
  double V;
};

// Funbcke pro prevod HSV na RGB
struct RGB HSV2RGB(struct HSV hsv) {
  double r = 0, g = 0, b = 0;

  if (hsv.S == 0)
  {
    r = hsv.V;
    g = hsv.V;
    b = hsv.V;
  }
  else
  {
    int i;
    double f, p, q, t;

    if (hsv.H == 360)
      hsv.H = 0;
    else
      hsv.H = hsv.H / 60;

    i = (int)trunc(hsv.H);
    f = hsv.H - i;

    p = hsv.V * (1.0 - hsv.S);
    q = hsv.V * (1.0 - (hsv.S * f));
    t = hsv.V * (1.0 - (hsv.S * (1.0 - f)));

    switch (i)
    {
      case 0:
        r = hsv.V;
        g = t;
        b = p;
        break;

      case 1:
        r = q;
        g = hsv.V;
        b = p;
        break;

      case 2:
        r = p;
        g = hsv.V;
        b = t;
        break;

      case 3:
        r = p;
        g = q;
        b = hsv.V;
        break;

      case 4:
        r = t;
        g = p;
        b = hsv.V;
        break;

      default:
        r = hsv.V;
        g = p;
        b = q;
        break;
    }

  }

  struct RGB rgb;
  rgb.R = r * 255;
  rgb.G = g * 255;
  rgb.B = b * 255;

  return rgb;
}

V další kapitole proměníme žárovku ve vlhkoměr!

Pokračování 2 / 4

RGB LED žárovka jako vlhkoměr

Jelikož jsme použili teplotní snímač řady Sensirion SHT3x (SHT30, SHT31 aj.), můžeme si bez změny elektrického obvody a jen s velmi kosmetickou úpravou zdrojového kódu proměnit lampičku také v barevný indikátor relativní vlhkosti vzduchu.

Podívejte se, jak jsme proměnili lampu WiZ v teploměr:

Barevný a vlhkostní rozsah tentokrát upravíme na 30-80 % relativní vlhkosti vzduchu, a tato procenta budeme přepočítávat opět na odstín barevného modelu HSV v rozsahu 0-220°. Barevnou škálu jsme tedy otočili a ještě více rozšířili do modré.

0820db91-e22d-4222-832e-8f14205fb985
Schéma zapojení je stejné jako v předchozím příkladu

Když bude v místnosti relativní vlhkost vzduchu 30 % a méně, RGB LED žárovka zčervená. Když bude v místnosti 80 % a více, naopak zmodrá. Ideální tedy bude, když bude svítit zeleně a vlhkost se bude pohybovat zhruba uprostřed.

fcc03e94-b4e2-4d71-a94c-c512daed1c51
Odstín HSL v rozsahu 0-220°, který použijeme jako barevnou škálu relativní vlhkosti 30-80 %

V bytech a kancelářích bychom měli mít relativní vlhkost ideálně mezi 45-55 %. Při vyšších hodnotách už hrozí zvláště na chladnějších plochách růst plísní, no a při nižších zase vyšší prašnost, vysychání sliznic a z toho plynoucí snazší pronikání virů, bakterií nebo alergenů do lidských tkání.

Zdrojový kód RGB LED vlhkoměru

Kód se oproti tomu z první kapitoly liší jen velmi kosmeticky. Namísto teploty získáváme vlhkost vzduchu a přepočítáváme ji na otočenou a o něco více roztaženou škálu HSV od rudé po modrou. Stejně tak vypisujeme odlišné hodnoty na maličký OLED displej.

// Vestavene knihovny pro
// praci s WI-Fi a UDP
#include <WiFi.h>
#include <WiFiUdp.h>

// Knihovna pro praci s JSON
// https://arduinojson.org
// Lze doinstalovat ze spravce knihoven
#include <ArduinoJson.h>

// Knihovny pro praci s 0.96" I2C OLED
// Lze doinstalovat ze spravce knihoven
#include <Adafruit_SSD1306.h>
#include <Adafruit_GFX.h>

// Knihovny pro praci s I2C teplomery STH3x
// Lze doinstalovat ze spravce knihoven
#include <ClosedCube_SHT31D.h>

// Knihovna pro praci s C++ vektory
#include <vector>

// Funkce pro prevod barev mezi HSV a RGB
// Pouzijeme pro barevnou skalu od modre po cervenou
// Soubor hsv.h musi byt ve stejne slozce jakoc ztento zdrojovy kod
#include "hsv.h"

// Prihlasovaci udaje k Wi-Fi
const char *ssid = "Nazev2.4GHzWiFi";
const char *heslo = "velmiTajneHeslo";

// Broadcastovaci IP adresa v teto siti LAN
// Muzete zkusit i univerzalni 255.255.255.255
// Broadcastovaci IP adresa ma u beznych siti
// zpravidla formu xxx.xxx.xxx.255
// Treba: 192.168.1.255 aj.
IPAddress broadcast_ip(172, 17, 16, 255);

// Vektor pro IP adrssy nalezenych zarizeni WiZ
std::vector<IPAddress> wiz_ip;
// UDP port, na kterem poslouchaji zarizeni WiZ
uint16_t wiz_port = 38899;

// Trida pro praci s UDP, poslochaci port
// a pamet pro prichozi zpravu
WiFiUDP udp;
uint16_t udp_port = 12345;
char udp_zprava[200];

// Pamet pro JSON na 200 znaku
StaticJsonDocument<200> doc;

// Trida teplomeru SHT3x
ClosedCube_SHT31D sht3xd;

// Trida 0.96" OLED s radicwem SSD1306
Adafruit_SSD1306 display(128, 64, &Wire, -1);

// Prodleva mezi merenim teploty a aktua;lizaci zarovek WiZ
uint32_t prodleva_ms = 1000;
uint32_t t = 0;

// Textove zpravy v JSON, kterem budeme posilat do zarovky
String registrace = "{\"method\":\"registration\",\"params\":{\"phoneMac\":\"AAAAAAAAAAAA\",\"register\":false,\"phoneIp\":\"1.2.3.4\"}}";
String sablona_barva = "{\"method\":\"setPilot\",\"params\":{\"r\":%R%,\"g\":%G%,\"b\":%B%,\"dimming\":%JAS%}}";
String barva;

// Funkce pro odeslani UDP zpravy do vsech nalezenych zarovek WiZ
void odesliZpravuDoLampicek(String zprava) {
  for (auto ip = begin(wiz_ip); ip != end(wiz_ip); ++ip) {
    Serial.printf("Zprava pro %s: %s\r\n", ip->toString().c_str(), zprava.c_str());
    udp.beginPacket(*ip, wiz_port);
    udp.print(zprava);
    udp.endPacket();
    delay(10);
  }
}

// Funkce pro vyhledani vsech zarovek WiZ v teo siti LAN pomoci UDP broadcastu
void odesliUdpSken() {
  Serial.printf("Skenuji sit a hledam zarizeni WiZ na broadcast IP adrese %s...\r\n", broadcast_ip.toString().c_str());
  udp.beginPacket(broadcast_ip, wiz_port);
  udp.print(registrace);
  udp.endPacket();
}
// Funkce pro prekresleni displeje
void prekresliDisplej(String nadpis, float hodnota) {
  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);
  display.setTextSize(2);
  display.setCursor(0, 0);
  display.print(nadpis);
  display.setTextSize(3);
  display.setCursor(0, 30);
  display.print(hodnota);
  display.display();
}


// Funkce pro zmereni vlhkosti, spocitani barvy a aktualizace zarovek
void vlhkomer() {
  // Zkopirujeme si sablonu s JSON pro nastaveni barev
  String barva = sablona_barva;
  // Zmerime vlhkost
  SHT31D cidlo = sht3xd.readTempAndHumidity(SHT3XD_REPEATABILITY_LOW, SHT3XD_MODE_CLOCK_STRETCH, 50);
  int vlhkost = (int)cidlo.rh;
  // Nakreslime udaj o teplote na displej
  prekresliDisplej("VLHKOST", vlhkost);
  // Prepocitame rozsah vlhkosti 30-80 % na rozsah odstinu HSV 0-220
  float hue = map(constrain(vlhkost, 30, 80), 30, 80, 0, 220);
  // Prepocitame model HSV s hodnotou saturace 100 % a jasu 10 % na RGB
  // Funkce pro prepocet jsou v druhem souboru naseho projektu hsv.h
  struct HSV hsv = {hue, 1.0, 0.05};
  struct RGB rgb = HSV2RGB(hsv);
  // V textovem retezci s sablonkou JSONu pro nastaveni barvy nahradime zastupne vyrazy hodnotami R, G, B a jasem zarovky na 100 %
  barva.replace("%R%", String(rgb.R));
  barva.replace("%G%", String(rgb.G));
  barva.replace("%B%", String(rgb.B));
  barva.replace("%JAS%", "100");
  // Odesleme textovy retezec skrze UDP do detekovanych zarovek WiZ
  odesliZpravuDoLampicek(barva);
}

// Hlavni funkce setup se spusti hned na zacatku
void setup() {
  Serial.begin(115200); // Nastartovani seriove linky rychlosti 115200 b/s
  delay(1000);
  Wire.begin(); // Nastartovani sbernice I2C
  sht3xd.begin(0x44); // Pripojeni k teplomeru SHT3x na I2C adrese 0x44
  display.begin(SSD1306_SWITCHCAPVCC, 0x3c); // Pripojeni k OLED displeji na I2C adrese 0x3c
  // Smazani displeje
  display.clearDisplay();
  display.display();
  barva.reserve(500); // Rezwervuj 500 bajtu pro textovy retezec barva
  Serial.println("*** START PROGRAMU ***");
  // Pripojime se k Wi-Fi
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, heslo);
  Serial.printf("Pripojuji se k %s...", ssid);
  while (WiFi.status() != WL_CONNECTED) {
    delay(100);
    Serial.print(".");
  }
  Serial.println(" OK");
  udp.begin(udp_port); // Nastartujeme UDP
  Serial.printf("Moje IP adresa: %s\r\n", WiFi.localIP().toString().c_str());
  Serial.printf("UDP posloucha na portu %d\r\n\r\n", udp_port);
}

// Funkce loop se opakuje stale dokola
void loop() {
  // Pokud zatim neznam ani jednu IP adresu lampicky WiZ,
  // spustim broadcastovy UDP sken,
  // na ktery by mely lampicky odpovedet
  if (wiz_ip.size() == 0) {
    odesliUdpSken();
    delay(1000);
  }
  // Zjistim, jestli dorazil nejaky novy UDP datagram/packet
  int paket = udp.parsePacket();
  // Pokud ano, vypisu jeho obsah a IP adresu odesilatele
  if (paket) {
    IPAddress ip = udp.remoteIP();
    Serial.printf("Odpoved od %s: ", ip.toString().c_str());
    if (udp.read(udp_zprava, 200) == 0) Serial.printf("Zadna data\r\n");
    else {
      Serial.printf("%s\r\n", udp_zprava);
      // Pokusim se zpracovat JSON
      if (!deserializeJson(doc, udp_zprava)) {
        // Pokud ma klic "method" hodnotu "registration", je to odpoved lampicky na broadcastovy sken
        if (!doc["method"].isNull()) {
          if (strcmp(doc["method"].as<const char *>(), "registration") == 0) {
            // Pokud je klic "result/success" roven true,
            // Vypisu MAC a IP lampicky a IP zaroven ulozim do pole lampicek
            if (doc["result"]["success"]) {
              Serial.println("Nasel jsem zarizeni WiZ:");
              Serial.printf(" - IP: %s\r\n", ip.toString().c_str());
              Serial.printf(" - MAC: %s\r\n", doc["result"]["mac"].as<const char *>());
              wiz_ip.push_back(udp.remoteIP());
            }
          }
        }
      }
    }
  }

  // Se stanovenou prodlevou spoustim funkci teplomer
  if (millis() - t > prodleva_ms) {
    vlhkomer();
    t = millis();
  }
}

Nezapomeňte opět na soubor hsl.h z první kapitoly, který obsahuje funkce pro přepočet barevného modelu HSV na odstíny RGB!

V další kapitole už spustíme velký počítač a Arduino C/C++ nahradíme za Python. Proměníme RGB LED lampičku WiZ v indikátor zátěže procesoru!

Pokračování 3 / 4

RGB LED žárovka jako indikátor zátěže CPU

Jistě by vás napadla hromada dalších experimentů s žárovkou, kterou ovládáme vlastním firmwarem na čipu prototypovací desky ESP32-LPKit. Mohli bychom k ní připojit třeba senzor plynů a měnit odstín podle jejich koncentrace, nebo třeba mikrofon a barvou vyjadřovat zase hlasitost.

My se ale už přesuneme na velký počítač, ze kterého budeme ovládat RGB LED WiZ pomocí Pythonu. Ukážeme si to na Windows, stejně tak by to ale fungovalo i na Linuxu nebo macOS. Na mašině musí běžet Python ve verzi nejméně 3.7, takže jej nezapomeňte nainstalovat.

Podívejte se, jak jsme proměnili lampu WiZ v indikátor CPU

V prvním příkladu pro Python proměníme žárovku v indikátor zátěže CPU. Opět využijeme barevnou škálu HSV. Když bude procesor málo zatížený, žárovka se zbarví dozelena. Když se naopak zapotí na 100 %, žárovka zrudne.

Budeme potřebovat dvě knihovny

Zátěž procesoru v procentech v Pythonu snadno získáme pomocí knihovny psutil, kterou můžete z příkazové řádky doinstalovat skrze balíčkový manažer pip, který je součástí Pythonu:

pip install psutil

Pro ovládání samotné žárovky WiZ pak budeme muset doinstalovat ještě knihovnu pywizlight:

pip install pywizlight

Právě tato knihovna vyžaduje Python nejméně ve verzi 3.7. Pokud máte na počítači starší, pywizlight se sice nainstaluje, ale bude příliš zastaralý a náš kód nebude fungovat.

Zjišťujeme IP adresu žárovky

Zatímco v prvních dvou příkladech si náš program našel žárovky WiZ v síti automaticky pomocí UDP broadcastu, pro jednoduchost příkladu v Pythonu už budeme předpokládat, že znáte IP adresu žárovky. Dohledáte ji třeba v mé oblíbené mobilní aplikace WiFiman, která nabízí skener všech zařízení v místní síti.

55ad648c-7dda-404c-be62-7efa3d1cde6a
Dohledání IP adresy žárovky WiZ v mobilním skeneru LAN WiFiman

Součástí knihovny pywizlight je nicméně i ovládání přímo z příkazové řádky včetně UDP skeneru pro zařízení WiZ, takže stačí zavolat příkaz:

wizlight discover

A vyplnit broadcastovou IP adresu v místní síti. Jak už jsme si řekli v první kapitole, zpravidla má podobu 255.255.255.255, anebo zkuste xxx.xxx.xxx.255. Takže pokud by váš domácí Wi-Fi router přiděloval místní IP adresy ve formě 192.168.1.xxx, pak bude nejspíše fungovat broadcastová IP adresa 192.168.1.255.

57335ff0-8be1-491a-9b9f-1bf4f4708e20
Dohledání IP adresy žárovky pomocí UDP skeneru, který nabíz íknihovna pywizligh

Náš skript v akci

Jakmile získáme IP adresu žárovky, můžeme zavolat skript našeho programu níže příkazem:

python cpubarvicky.py ipadresa

Program poté bude stále dokola v nekonečné smyčce zjišťovat zátěž CPU v procentech a adekvátně obarvovat textový výstup. Podle zátěže spočítá na barevné škále HSV odpovídající odstín mezi zelenou a červenou barvou, převede jej do modelu RGB a odešle příkaz do žárovky pomocí knihovny pywizlight.

df8272b2-375d-4dc8-b6f0-1e9f842db8b8ace54746-f4ee-49f3-8658-dc01098f519cbf12e495-56af-4254-b261-a1c631b252d30cbdc938-7100-40f3-acff-fba0dbc227eb
Program neustále měří zátěž CPU, pomocí škály HSV počítá odpovídající barvu a posílá instrukci do žárovky

Co je to vlastně procentuální zátěž CPU?

Zátěž procesoru v procentech je poměrně abstraktní a neurčitý údaj, náš skript se tedy bude chovat trošku jinak než zátěž, kterou udává třeba Správce úloh ve Windows. Procesor samotný totiž samozřejmě žádnou zátěž v procentech neudává.

Relativní zátěž počítáme z CPU času, který spotřebují jednotlivé procesy za jednotku času. Dejme tomu, že bychom na procesoru spustili jen jeden jediný program, který by svoji práci zpracoval za 100 ms a skončil. Pokud bychom uvažovali časové okno o délce 1 sekundy, znamená to, že náš program v něm zatížil procesor z 10 %. Kdybychom ale uvažovali okno o délce 2 sekund, relativní zátěž programu během této doby klesne na 5 %.

a6f489a0-346f-4b09-ac28-075c9f21911c
Relativní zátěž CPU v procentech závisí na tom, jak dlouhé vzorkovací okno použijeme

Při odhadu relativní zátěže CPU v procentech tedy vždy záleží na tom, jak dlouhé časové okno zvolíme. V našem programu pracujeme s intervalem 1 sekundy a výstup bude v klidovém stavu pravděpodobně výrazně nižší (jednotky procent) než při pohledu na zatížení podle Správce úloh.

Zdrojový kód indikátoru CPU

# Vestavena knihovna pro asynchronni beh
import asyncio

# Knihovna pro zjisteni zateze CPU
# https://pypi.org/project/psutil/
import psutil

# Knihovna pro praci s RGB LED WiZ
# https://github.com/sbidy/pywizlight
# https://pypi.org/project/pywizlight/
# Je treba mit novejsi Python 3.7+
from pywizlight import wizlight, PilotBuilder

# Z knihovny sys importujeme funkci argv
# pro cteni argumentu skriptu
from sys import argv

# Analogie nasi funkce v C/C++ a Arduinu pro prepocet modelu HSV na RGB
def hsv2rgb(h, s, v):
        if s == 0.0: v*=255; return (v, v, v)
        i = int(h*6.)
        f = (h*6.)-i; p,q,t = int(255*(v*(1.-s))), int(255*(v*(1.-s*f))), int(255*(v*(1.-s*(1.-f)))); v*=255; i%=6
        if i == 0: return (v, t, p)
        if i == 1: return (q, v, p)
        if i == 2: return (p, v, t)
        if i == 3: return (p, q, v)
        if i == 4: return (t, p, v)
        if i == 5: return (v, p, q)

# Funkce pro ziskani barevneho textoveho retezce v RGB
# obaleneho terminalovymi escape sekvencemi
def rgb_text(r,g,b, text):
    return f"\x1b[38;2;{r};{g};{b}m{text}\x1b[0m"

# Analogie funkce map z Arduina pro prepocet jednoho rozsahu na druhy
def map_range(x, in_min, in_max, out_min, out_max):
  return (x - in_min) * (out_max - out_min) // (in_max - in_min) + out_min

# Aynchronni hlavni funkce main, ve ktere bude hlavni logika naseho skriptu
async def main():
    # IP adresa lampy WiZ, se kterou se chceme spojit,
    # musi byt parametrem skriptu
    if len(argv) != 2:
        print("pouziti: cpubarvicky.py ipadresa")
    else:
        ip = argv[1]
        print(f"💡💡💡 {rgb_text(255, 255, 255, 'CPU Lampička 1.0')} 💡💡💡")
        print(f"Připojuji se k lampičce {ip}... ", end="")
         # Spojime se s lampou na zadane IP
        zarovka = wizlight(ip)
        if zarovka != None:
            print(f"{rgb_text(0, 255, 0, 'OK')}")
            # Ziskame informace o lampicce
            hw = await zarovka.getBulbConfig()
            hw = hw["result"]
            print(f"Model: {rgb_text(127, 219, 245, hw['moduleName'])}")
            print(f"Verze: {rgb_text(127, 219, 245, hw['fwVersion'])}")
            print("")
            smycka = True
        else:
            print(f"{rgb_text(255, 0, 0, 'CHYBA')}")
            smycka = False
        # Vychozi hodnoty barev
        # Budeme menit barvu lampy jen tehdy,
        # pokud se zmenila barva na monitoru
        r=0
        g=0
        b=0
        # Nekonecnba smycka, ze ktere vyskocime ukoncenim procesu Pythonu,
        # anebo stiskem CTRL+C v prikazove radce
        while smycka:
            print("Zátěž CPU: ", end="")
            # Zjistime relativn izatez CPU v casovem okne o delce 1 s
            cpu = psutil.cpu_percent(1)
            # Pokud je zatez vyssi nez 5 %
            if cpu > 5:
                # Prepocitam rozsah 6-100 % na rozsah odstinu hue 90-0 (zelena az ruda)
                hue = map_range(cpu, 6, 100, 90, 0)
                # PRevedeme model HSV na RGB
                _r, _g, _b = hsv2rgb(hue/360, 1, 1)
            else:
                # Pro zatez < 5 % nastavime pevnou zelenou barvu,
                # at nam zarovka v klidovem stavu neblika
                _r, _g, _b = 128, 255, 0
            barevny_text = rgb_text(_r, _g, _b, f"{int(cpu):3}%")
            print(f"{barevny_text}")
            barevny_text = rgb_text(_r, _g, _b, f"{_r:03}, {_g:03}, {_b:03}")
            print(f"Barva lampičky: {barevny_text}")
            print("\033[F\033[F", end="")
            # Pokud se kanaly RGB lisi od tech starych
            if r != _r or g != _g or b != _b: 
                # Rozsvitime zarovku odstinem _r, _g, _b
                await zarovka.turn_on(PilotBuilder(rgb = (_r, _g, _b)))
                # A aktualizujeme stare hodnoty kanalu RGB
                _r = r
                _g = g
                _b = b
            # Pockame 100 ms a opakujeme
            await asyncio.sleep(.1)

# Asynchronne spustime funkci main s nekonecnou smyckou
# Knihovna pywizlight je postavena na vestavene asynchronni knihovne asyncio,
# a tak pomoci ni obalujeme i hlavni logiku programu
asyncio.run(main())

Ještě nekončíme! V poslední kapitole proměníme RGB LED WiZ v parodii na Philips Ambilight. Žárovka bude měnit barvu podle převládajícího odstínu na hlavní obrazovce počítače. 

Pokračování 4 / 4

RGB LED žárovka se obarví odstínem monitoru

V posledním experimentu dnešního bastlení proměníme žárovku WiZ v takový velmi jednoduchý ambilight – lampička bude měnit odstín podle převládajících odstínu R, G a B na hlavním monitoru počítače. Když bude na obrazovce dostatek zelené barvy, zezelená i žárovka. No a když bude plocha počítače spíše růžová, nastaví růžovou barvu i lampička WiZ.

Podívejte se, jak jsme proměnili lampu WiZ v „Ambilight pro PC“

Do hry vedle Pythonu a knihovny pywizlight zapojíme další knihovnu PIL/Pillow pro práci s rastrovými daty. Stejně jako v předchozích případech ji můžeme doinstalovat pomocí vestavěného správce balíčků pro Python:

pip install pillow

Nejprve získáme snímek obrazovky

Nejprve se s žárovkou spojíme identickým způsobem jako v předchozí kapitole a poté budeme v nekonečné smyčce stále dokola ukládat do RAM snímek hlavní obrazovky jako pole pixelů pomocí metody ImageGrab.grab, kterou nabízí právě knihovna Pillow. Nebude to příliš rychlé, sejmutí obrazovky totiž dle výkonu počítače a rozlišení zabere i jednotky sekund. Pro základní experiment to ale stačí.

c4b26696-b235-4510-966c-6ff7a5964bbea95b4d3f-c559-4674-bca1-222ad351716ee6689d48-afb3-48f4-b4f1-270b5f229e6f
Snímky obrazovky a vypočítané typické kanály R, G a B podle našeho algoritmu

Poté projdeme ve snímku pixel po pixelu a jejich kanály R, G a B a budeme je sčítat. Na závěr je podělíme počtem pixelů snímku a získáme tak výsledné odstíny R, G a B, které v obrazu převládají. Je to jedna z nejjednodušších metod, jak zjistit typickou barvu scény.

Pak spočítáme převládající kanály R, G a B

Jelikož ale není zrovna nejsofistikovanější, detekce barvy bude fungovat opravdu jen tehdy, když bude na monitoru v dostatečném množství (desítky procent) nějaká solidní – unikátní – barva. V opačném případě se nám odstíny slijí spíše dohromady a žárovka se rozsvítí bílou barvou.

ad09a8d2-b185-4394-a0d7-bfc34ee300a0
Běh programu s barevnou indikací, na jaký odstín zrovna nastavujeme žárovku

Pokud budete mít tapetu plnou barevných přechodů, zprůměrují se opět do bílé. Metod ke zjištění typické barvy je opravdu hromada, takže můžete zkusit nějakou jinou.

Zdrojový kód

Zbytek programu je opět totožný s předchozím příkladem. Jakmile získáme barvu monitoru, bude-li se lišit od předchozího průběhu smyčky, nastavíme ji i na žárovce.

Na běžném PC zabere průběh smyčky se vším všudy 1-3 sekundy a právě to bude i prodleva žárovky oproti dění na monitoru. Na rychlé změny barev třeba při sledování filmu to tedy není, ale pro základní experimentování to funguje na jedničku.

# Vestavena knihovna pro asynchronni beh
import asyncio

# Knihovna pro praci s rastrovymi daty
# https://pillow.readthedocs.io/en/stable/
# https://pypi.org/project/Pillow/
# Pouzijeme prpo sejhmuti bitmapy hlavniho monitoru 
from PIL import ImageGrab

# Knihovna pro praci s RGB LED WiZ
# https://github.com/sbidy/pywizlight
# https://pypi.org/project/pywizlight/
# Je treba mit novejsi Python 3.7+
from pywizlight import wizlight, PilotBuilder

# Z knihovny sys importujeme funkci argv
# pro cteni argumentu skriptu
from sys import argv


# Spocitej celkovou intenzitu RGB kanalu ve vstuopnim obrazku
# Jedna z mnoha moznych implementaci pro hledani „dulezite barvy“
# Funguje jen tehdy, pokud je monitor vyplneny opravdu dostatek nejake spojite barvy (zelene, modre cervene aj.)
def spocitej_intenzitu_rgb(obrazek):
    sirka, vyska = obrazek.size
    r_celkem = 0
    g_celkem = 0
    b_celkem = 0
    pocet = 0
    # Projdeme pixely obrazku
    for x in range(0, sirka):
        for y in range(0, vyska):
            # Precteme RGB kanaly pixelu
            r, g, b = obrazek.getpixel((x, y))
            # Navysime celkove hodnoty kanalu RGB
            r_celkem += r
            g_celkem += g
            b_celkem += b
            pocet += 1
    # Podelime clekove intenzity kanalu poctem pixelu a vratime vysledny odstin RGB
    return (int(r_celkem/pocet), int(g_celkem/pocet), int(b_celkem/pocet))


# Funkce pro ziskani barevneho textoveho retezce v RGB
# obaleneho terminalovymi escape sekvencemi
def rgb_text(r,g,b, text):
    return f"\x1b[38;2;{r};{g};{b}m{text}\x1b[0m"

# Aynchronni hlavni funkce main, ve ktere bude hlavni logika naseho skriptu
async def main():
    # IP adresa lampy WiZ, se kterou se chceme spojit,
    # musi byt parametrem skriptu
    if len(argv) != 2:
        print("pouziti: barvicky.py ipadresa")
    else:
        ip = argv[1]
        print(f"💡💡💡 {rgb_text(255, 255, 255, 'Ambilight Lampička 1.0')} 💡💡💡")
        print(f"Připojuji se k lampičce {ip}... ", end="")
         # Spojime se s lampou na zadane IP
        zarovka = wizlight(ip)
        if zarovka != None:
            print(f"{rgb_text(0, 255, 0, 'OK')}")
            # Ziskame informace o lampicce
            hw = await zarovka.getBulbConfig()
            hw = hw["result"]
            print(f"Model: {rgb_text(127, 219, 245, hw['moduleName'])}")
            print(f"Verze: {rgb_text(127, 219, 245, hw['fwVersion'])}")
            print("")
            smycka = True
        else:
            print(f"{rgb_text(255, 0, 0, 'CHYBA')}")
            smycka = False
        # Vychozi hodnoty barev
        # Budeme menit barvu lampy jen tehdy,
        # pokud se zmenila barva na monitoru
        r=0
        g=0
        b=0
        # Nekonecnba smycka, ze ktere vyskocime ukoncenim procesu Pythonu,
        # anebo stiskem CTRL+C v prikazove radce
        while smycka:
            print("Zjišťuji převládající barvu na monitoru...", end="")
             # Pomoci knihovny PIL/Pillow sejmeme snimek hlavni obrazovky
             # Mzue to trvat az nekolik sekund
            snimek = ImageGrab.grab()
            # Spocitame prevladajcici kanaly RGB na snimku
            _r,_g,_b = spocitej_intenzitu_rgb(snimek)
            print("OK")
            # Pokud se kanaly RGB lisi od tech starych
            if r != _r and g != _g and b != _b:
                barevny_text = rgb_text(_r, _g, _b, f"Nová převládající barva monitoru: {_r}, {_g}, {_b}")
                print(barevny_text)
                # Pokud je kanal R o 20 a vice vetsi nez kanaly G a B, 
                # rozsvitime zarovku sytou cervenou barvou 
                if (_r-_g) > 20 and (_r > _b) > 20:
                    await zarovka.turn_on(PilotBuilder(rgb = (255, 0, 0)))
                # Pokud je kanal G o 20 a vice vetsi nez kanaly R a B, 
                # rozsvitime zarovku sytou zelenou barvou 
                elif (_g - _r) > 20 and (_g - _b) > 20:
                    await zarovka.turn_on(PilotBuilder(rgb = (0, 255, 0)))
                # Pokud je kanal B o 20 a vice vetsi nez kanaly R a G, 
                # rozscvitime zarovku sytou cervenou modrou 
                elif (_b - _r) > 20 and (_b - _g) > 20:
                    await zarovka.turn_on(PilotBuilder(rgb = (0, 0, 255)))
                # Pokud jsou rozdily mensi, nastavime barvu,
                # jak jsme ji spocitali funkci spocitej_intenzitu_rgb
                # Pravdepodobne to bude spise bile svetlo 
                else:
                    await zarovka.turn_on(PilotBuilder(rgb = (_r, _g, _b)))
                # Aktualizujeme stare hodnoty kanalu RGB
                r = _r
                g = _g
                b = _b
            # Pockame 100 ms a opakujeme
            # Pozor, to neznamena, ze jedeme rychlosti 10 Hz
            # Jen sejmuti snimku obrazovky zabere dle veliksoti i nekolik sekund
            await asyncio.sleep(.1)


# Asynchronne spustime funkci main s nekonecnou smyckou
# Knihovna pywizlight je postavena na vestavene asynchronni knihovne asyncio,
# a tak pomoci ni obalujeme i hlavni logiku programu
asyncio.run(main())
Když navštívíte internetový obchod přes náš katalog zboží, Živě za to může získat provizi. Službu Zboží Živě provozujeme ve spolupráci s Heureka.cz.

Určitě si přečtěte

Články odjinud