Pojďme programovat elektroniku | Wi-Fi | Odposlech

Některé chytré LED žárovky mohou sledovat náš pohyb. Vyzkoušíme si to na čipu ESP32

  • Chytrá LED žárovka WiZ umí detekovat pohyb
  • Dokáže totiž analyzovat změny v elektromagnetickém poli
  • Teoreticky to zvládne každé Wi-Fi zařízení, které podporuje 802.11n

Naše redakce sídlí v historickém centru Brna. Budova je proto součástí památkové rezervace a přinejmenším její sklepy a zbytky mohutných zdí, do kterých se otiskly příběhy mnoha generací před námi, pamatují středověk.

Když tu jste sami a za dlouhých večerů studujete podklady pro některý z příštích článků, dům žije svým vlastním životem a občas se z jeho útrob ozývají všelijaké zvuky. Nedávno se k těmto vjemům přidalo i optické tajemno v místnosti Tomáše Holčíka.

V sousední místnosti se náhle rozsvítila světla

Tomášova kancelář se nachází hned vedle redakční kuchyňky s kávovarem. Když jsem se jednoho večera šoupal pro další kofeinový šot, v pokoji se náhle rozsvítily dvě bludičky. Nebyl to žádný spínač na bázi PIR (pasivní infračervený pohybový detektor), který je už roky součástí prakticky každého zabezpečovacího systému, obě lampy totiž byly za rohem stěny a nemohly mě vidět.

65879f53-7fdc-4761-9ee7-01f571d3f3fa
Testovací LED žárovka WiZ, která dokáže vidět lidi

V takových situacích vám před očima prolétnou všechny rané epizody Akta X a instinktivně se podíváte na žebroví úzké větrací šachty, jestli jsou všechny šroubky na svém místě. Teprve pak se ve vás probudí racionální člověk a jdete té nepatřičnosti přijít na kloub.

Podívejte se na video, jak dnes budeme detekovat člověka:

Wi-Fi LED žárovky WiZ umějí detekovat pohyb

Co bylo výsledkem pátrání? V Tomášově cimře rozhodně neúřadují duchové našich exkolegů, ale jen dvojice Wi-Fi LED žárovek WiZ pro závit E27.

ac3f87a6-d3a8-439a-8ced-63c9c96012028c2e7284-9b58-4a41-9a02-cedb61b1c935
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 

Značka WiZ Connected sice pochází z Hongkongu, dnes ale patří pod křídla nizozemské společnosti Signify, kterou všichni dobře znáte, do roku 2018 totiž používala jméno Philips Lightning. Podstatné je ale to, že všechny produkty značky WiZ, které se na trh dostaly v létě 2021 a později, jsou vyzbrojené technologií SpaceSense.

SpaceSense

SpaceSense je detektor pohybu, který ale k práci nepotřebuje žádnou další snímací elektroniku. Ne, součástí žárovky není žádný PIR, není tam schovaný mikrofon, který zareaguje na náhlou změnu hlasitosti, no a není tam ani žádný dedikovaný a všesměrový mikrovlnný detektor.

Screenshot_20230219_144525_WiZ V2.jpgScreenshot_20230219_144734_WiZ V2.jpgScreenshot_20230219_144738_WiZ V2.jpgScreenshot_20230219_144743_WiZ V2.jpgScreenshot_20230219_144753_WiZ V2.jpgScreenshot_20230219_144801_WiZ V2.jpg
Nastavení SpaceSense na LED žárovkách WiZ

Je tam jen běžný rádiový vysílač, který žárovka používá k vlastní komunikaci. V případě chytrých světel WiZ je to Wi-Fi a Bluetooth pro rychlé párování v aplikaci. Skrze Bluetooth se do žárovky přenese informace o Wi-Fi, ke které se má světýlko připojit, aniž by se musel majitel připojovat k dočasné síti Wi-Fi spuštěné přímo na žárovce, což na mnoha telefonech vždy zlobilo.

SpaceSense na propagačním videu:

Rušení způsobují i pohybující se objekty

Wi-Fi (a Bluetooth už také) je velmi robustní komunikační protokol. Jen díky tomu žárovka i mnohá další zařízení udrží rádiové spojení s domácím Wi-Fi routerem, ačkoliv se musejí neustále vypořádávat s neustálým rušením v přeplněném pásmu 2,4 GHz.

Jelikož je to pásmo nelicencované – při dodržení vysílacích výkonů a dalších parametrů v něm můžeme vysílat i bez komerční licence –, snaží se do něj vecpat kdekdo.

Rušení ovšem nezpůsobuje pouze interference s ostatními rádiovými signály, ale i prostá okolní hmota, se kterou už 2,4GHz vlny s délkou 12,5 cm reagují a jejím průstupem ztratí část energie. U 5GHz Wi-Fi nám vlnová délka klesne k 6 cm, interakce s hmotou tedy ještě zesílí a 5GHz Wi-Fi pak leckomu nedosáhne ani na záchod, což v nejedné domácnosti způsobí nepříjemné mrzení.

Neviditelná elektromagnetická bouře

Zatímco zdi, pračka nebo třeba můj laminátový vepř způsobují relativně konstantní rušení, protože jsou stále na jednom místě, když se místností projde člověk, který se z více než poloviny skládá z vody, což je docela dobrý rádiový izolant, způsobí s menší nadsázkou neviditelnou elektromagnetickou bouři.

Rychlý pohyb těla sice může způsobit krátký výpadek citlivých true-wireless sluchátek, mnohem robustnější Wi-Fi se s tím ale vypořádá a mobil v kapse kalhot si na vyšší aplikační úrovní vůbec ničeho nevšimne. Náhlou deformaci elektromagnetického pole nicméně dokáže nepřímo změřit, na okamžik se totiž změní kvalita připojení. A přesně to dělá ve svých žárovkách i WiZ.

Budeme detekovat pohyb pomocí čipů ESP32

My si to dnes na té nejnižší možné úrovní ukážeme pomocí oblíbeného Wi-Fi čipu z rodiny Espressif ESP32. Funkci jsem otestoval na dvojici prototypovacích desek:

Zatímco první a jednodušší destičku s čipem ESP32-C3 seženete i v Česku (pokud bude skladem) za směšných 150 korun, druhá a oficiální deska od Espressifu přijde mnohem dráž, ale nabízí v tuto chvíli nejvýkonnější SoC ze stáje asijského výrobce (tedy do chvíle, než dorazí ESP32-P4).

3e6d074e-1277-4ecb-b1f5-2a3b5d65b197bcdb8205-0283-46f9-b95a-2830eb160ace
Drobná deska XIAO s čipem ESP32-C3 a větší a výkonnější devkit s čipem ESP32-S3 

Budeme pingat, ale o pingy vlastně nejde

Co přesně s těmito deskami budeme dělat? Nahrajeme do nich kód, který po připojení k redakční Wi-Fi spustí smyčku, ve které bude stále dokola a skrze protokol ICMP posílat zprávy na redakční router a čekat na odpověď. Ano, bystří čtenáři už pochopili, že si naprogramujeme jednoduchý ping.

Samotný ping nás ale ve skutečnosti vůbec nezajímá. Bude to totiž jen umělá aktivita, kterou by stejně tak mohlo nahradit dotazování třeba na nějaký webový server. Podstatné je to, že jsou naše destičky připojené do redakční LAN skrze Wi-Fi, takže při každém takovém pingu můžeme měřit kvalitu samotné rádiové komunikace.

Ne, běžné RSSI nám k detekci stačit nebude

Leckoho teď možná napadlo, že budeme měřit jen prachsprosté RSSI (Received Signal Strength Indiciation), ale to je příliš zobecnělý údaj, který nám pouze řekne, že:

  • -30 dBm: Teoretické maximum, prakticky nedosažitelné
  • -67 dBm: Minimum pro nejnáročnější aplikace
  • -70 dBm: Minimum pro bezchybné čtení webu
  • -80 dBm: Možná se ještě připojíte, ale bude to nestabilní 
  • -90 dBm: K této Wi-Fi se už nedokážete připojit
be9a8b6c-86ba-4888-88a3-3262c6807dea
Síla signálu Wi-Fi v aplikaci WiFiman od Ubiquiti

Představa, že když se projdeme po pokoji, během několika milisekund se to projeví v RSSI, je bláhová. Potřebujeme něco mnohem přesnějšího, co zároveň můžeme sledovat doslova v reálném čase.

CSI odhalí, co se opravdu dělo se signálem

Řeč je technologii CSI (Channel State Information), která je součástí specifikace Wi-Fi počínaje verzí 802.11n. Nadšence do Wi-Fi přesměruji na tento 36stránkový dokument (PDF), no a my ostatní se spokojíme s definicí, že CSI obsahuje numerická data popisující, jak se šířil signál od vysílače k přijímači na jednotlivých subsignálech modulace OFDM.

947f51cb-5d8e-4891-8844-a097550831fe
Základní koncept OFDM modulace, kterou používá i Wi-Fi. Signál je rozdělený na drobnější subsignály s odlišnou frekvencí (carrier)

Každý se mohl k cíli šířit trošku jinak, mohl se na rozdíl od druhého více utlumit, odrazit (prodloužením trasy došlo k fázovému posunu) a toto všechno se pokouší zjednodušit právě údaj CSI. A jelikož šíření signálu prostorem ovlivňují okolní překážky – a jednou z nich je i pohybující se masa člověka plná vody –, když CSI informace o subsignálech vyneseme na časovou osu, měli bychom v surových datech při pohybu zaznamenat patrnou změnu.

da05cb92-32d6-4d60-bfde-b043bbc0c27f
Surová data CSI, která jsme získali z čipu ESP32-C3 a zobrazili je na PC. Jakákoliv mírná změna může představovat pohyb člověka v prostoru mezi vysílačem a přijímačem

Drobný záchvěv daný tím, že přinejmenším některé subsignály OFDM na vlastní vlnu poznaly, co je to člověk (ony to poznaly všechny, ale jen u některých frekvencí byla změna natolik silná, aby se to projevilo v grafu).

Z dat lze zrekonstruovat i 3D model člověka

Hlubší analýzou CSI, strojovým učením a modelováním pak lze z těchto surových hodnot o průchodu signálu prostorem vyrobit dokonce i poměrně věrohodný 3D model pohybujícího se tělesa, jak se to nedávno povedlo třeba inženýrům z univerzity Carnegie Mellon. V podstatě tak trochu proměnili Wi-Fi router v kameru.

b77688c7-504a-4cf6-a943-d07c2c2e4a41
Experiment z Carnegie Mellon: Vlevo kontrolní záběr z kamery, vpravo pak detekce a AI modelace výhradně pomocí dat CSI o kvalitě komunikace Wi-Fi

Ačkoliv si vše ukážeme na destičkách s čipy ESP32, které můžeme programovat v Arduinu a jazyku C++, toto je už poněkud vyšší dívčí, a tak vše napíšeme v C a oficiálním vývojovém frameworku od Espressifu ESP-IDF (IoT Development Framework), který je společně se všemi nástroji pro překlad a flash k dispozici i ve formě pluginu pro oblíbený vývojářský editor Visual Studio Code.

Zkoušíme analyzovat grafický výstup

Kód nebudeme muset psát od píky, už ho za nás totiž vytvořili inženýři právě z Espressifu, kteří na GitHub umístili projekt esp-csi s několika ukázkami, jak pracovat s CSI právě na těchto čipech. Já si pro potřeby dnešního článku mírně upravil kód csi_recv_router, který sbírá CSI právě pomocí zasílání pingů na router skrze Wi-Fi, a konečně skript pro Python csi_data_read_parse.py, který se postará o čtení dat z čipu ESP32 skrze sériovou linku a jejich vykreslování do časové osy.

d49a5465-1be1-416a-9588-0c99c9d7fc91
Svojí tělesnou schránkou zastiňuji anténu destičky ESP32-C3
3dfb9560-f580-4c06-84b3-96a922cfbe44
Přecházím od antény k PC stolu
cd5e3e27-a89a-41cd-a616-19ac2d942813
Sedím u PC a anténa desky s ESP32-C3 má přímý výhled na redakční Wi-Fi hotspot

Právě v ní při pohybu mezi anténou malé destičky s čipem ESP32 a Wi-Fi AP na protější stěně místnosti uvidíme, jak se hodnoty některých subsignálů náhle snížily nebo zvýšily. Právě detekcí těchto změn můžeme odhadnout, že došlo k nějaké významné události.

Pokud víme, že jsou přijímač i vysílač v pevné pozici, mají stabilní napájení a CSI měříme na periodickém vzoru stále stejných dat (ping), zdroj události musí být vnější – ve fyzickém prostoru. Musí to být prostě pohyb. Ať už průchod člověka, anebo pád opravdu velké krabice plné harampádí, které vyvolalo stejné elektromagnetické rušení.

Je to věda, která vyžaduje kalibraci

Jak ale vidno z grafu, odhadnout pohyb člověka není vůbec jednoduché. Záleží na kvalitě přijímače, záleží samozřejmě i na Wi-Fi AP, záleží v obou případech na anténách, jejich směrovosti, zisku… Stručně řečeno, těch možných parametrů jsou desítky, takže i WiZ, ať už pro detekci používá Wi-Fi, nebo Bluetooth, musí na začátku provést kalibraci.

Screenshot_20230219_144801_WiZ V2 (1).jpg
Kalibrace systému SpaceSense na LED žárovkách WiZ

Firmware LED žárovek se musí jednoduše stanovit klidovou bazální hladinu, aby věděl, jak se liší od toho, když mezi žárovkami projde člověk. S párem svítidel to bude fungovat občas, s větším počtem zapojených prvků to pak bude spolehlivější.

V každém případě nelze než doufat, že se podobná funkce na chytrých zařízeních domácnosti bude nadále rozšiřovat. Kdyby ji totiž uměly opravdu všechny krabičky a navzájem by si to předávaly, mohla by chytrá domácnost s vysokým prostorovým rozlišením a bez dalších čidel rozpoznat, ve které místnosti se právě teď nacházíte a podle toho třeba automaticky rozsvítit, nastavit hlasitost reproduktorů a tak dále.

Kód programu

Kód programu pro čip ESP32-C3/S3 je tentokrát pouze orientační, ESP-IDF totiž opravdu není Arduino a je třeba správně nastavit i projekt (především povolit samotnou technologii CSI).

f9966e69-a3a3-468a-ad43-5f734d402630
Začátečníci v ESP-IDF nesmějí zapomenout aktivovat CSI v konfiguraci projektu, jinak program havaruje. Takto to vypadá v případě ESP-IDF jako doplňku pro Visual Studio Code

Kód je také mnohem delší, než jsme zvyklí. Začátečníkům dá ale hrubou představu, jak se programuje v ESP-IDF.

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "nvs_flash.h"
#include "esp_mac.h"
#include "rom/ets_sys.h"
#include "lwip/inet.h"
#include "ping/ping_sock.h"

#define SSID "NaseSkvelaWiFi" // SSID Wi-Fi
#define HESLO "supertajneheslo" // WPA2 heslo Wi-Fi
#define MAX_POKUSY 5 // Maximalni pocet pokusu pripojovani vk Wi-Fi
#define PING_FREKVENCE 500 // Hz (realne leda s navysenim rychlosti seriove komunikace z vychozich 115200 b/s)

// Pomocne promenne a makra
static EventGroupHandle_t stav_pripojeni;
#define PRIPOJENO    BIT0
#define NEPRIPOJENO  BIT1

// Funkce pro zjisteni stavu pripojovani k Wi-Fi
// Vychazime z prikladu: https://github.com/espressif/esp-idf/tree/master/examples/wifi/getting_started/station
static void pripojovani_udalost(void* arg, esp_event_base_t udalost, int32_t udalost_id, void* udalost_data){
    static uint8_t pokusy = 0;
    if (udalost == WIFI_EVENT && udalost_id == WIFI_EVENT_STA_START) esp_wifi_connect();
    else if (udalost == WIFI_EVENT && udalost_id == WIFI_EVENT_STA_DISCONNECTED) {
        if (pokusy < MAX_POKUSY) {
            ets_printf("%d. pokus o pripojeni\n", ++pokusy);
            esp_wifi_connect();
        } else{
            ets_printf("Ale ne! Ani na %d. pokus se mi nepodarilo pripojit k Wi-Fi. Muze za to Kalousek\n", MAX_POKUSY);
            xEventGroupSetBits(stav_pripojeni, NEPRIPOJENO);
        }
    } else if (udalost == IP_EVENT && udalost_id == IP_EVENT_STA_GOT_IP) {
        ip_event_got_ip_t* data = (ip_event_got_ip_t*) udalost_data;
        ets_printf("*** JSEM PRIPOJENY K WI-FI ***\n");
        ets_printf("IP ADRESA: %d.%d.%d.%d\n", IP2STR(&data->ip_info.ip));
        pokusy = 0;
        xEventGroupSetBits(stav_pripojeni, PRIPOJENO);
    }
}

// Funkce pro pripojeni k Wi-Fi
// Vychazime z prikladu: https://github.com/espressif/esp-idf/tree/master/examples/wifi/getting_started/station
void pripojit_k_wifi(void){
    stav_pripojeni = xEventGroupCreate();
    esp_netif_init();
    esp_event_loop_create_default();
    esp_netif_create_default_wifi_sta();

    wifi_init_config_t konfigurace = WIFI_INIT_CONFIG_DEFAULT();
    esp_wifi_init(&konfigurace);

    esp_event_handler_instance_t udalost_obecne;
    esp_event_handler_instance_t udalost_mam_ip;
    esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &pripojovani_udalost, NULL, &udalost_obecne);
    esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &pripojovani_udalost, NULL, &udalost_mam_ip);

    wifi_config_t konfigurace_wifi = {
        .sta = {
            .ssid = SSID,
            .password = HESLO,
            .threshold.authmode = WIFI_AUTH_WPA2_PSK,
            .sae_pwe_h2e = WPA3_SAE_PWE_BOTH,
        },
    };
    esp_wifi_set_mode(WIFI_MODE_STA);
    esp_wifi_set_config(WIFI_IF_STA, &konfigurace_wifi);
    esp_wifi_start();

    xEventGroupWaitBits(stav_pripojeni, PRIPOJENO | NEPRIPOJENO, pdFALSE, pdFALSE, portMAX_DELAY);
}

// Nas callback, ktery zavola cip ESP32, jakmile ziska popsine informace o spojeni CSI
// Soucasti funkce je staticke pocitadlo. Kdyz bude rovno 0 (na zacatku), vypismeme do seriove linky textovou hlavicku
// V dalsich volani funkce uz pocitadlo navysujeme a posilame jken binarni proud  popisnych informaci
static void wifi_csi_rx_data(void *ctx, wifi_csi_info_t *info)
{
    if (!info || !info->buf || !info->mac) {
        ets_printf("<%s> wifi_csi_cb", esp_err_to_name(ESP_ERR_INVALID_ARG));
        return;
    }
    if (memcmp(info->mac, ctx, 6)) {
        return;
    }
    static uint32_t pocitadlo = 0;
    const wifi_pkt_rx_ctrl_t *rx_ctrl = &info->rx_ctrl;

    // Poprve vypiseme hlavicku
    if (!pocitadlo) {
        ets_printf("================ CSI RECV ================");
        ets_printf("type,seq,mac,rssi,rate,sig_mode,mcs,bandwidth,smoothing,not_sounding,aggregation,stbc,fec_coding,sgi,noise_floor,ampdu_cnt,channel,secondary_channel,local_timestamp,ant,sig_len,rx_state,len,first_word,data\n");
    }

    // Omezime subkanaly s CSI daty jen na ty nejnizsi
    info->len = 128;

    // Nejprve vypiseme ve formatu CSV (s carkou jako oddelovac) metadata o radiovem paketu
    // Vsimnete si, ze je v nich i RSSI, udaje o sumu, ultumu apod.
    printf("CSI_DATA,%ld," MACSTR ",%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
            pocitadlo++, MAC2STR(info->mac), rx_ctrl->rssi, rx_ctrl->rate, rx_ctrl->sig_mode,
            rx_ctrl->mcs, rx_ctrl->cwb, rx_ctrl->smoothing, rx_ctrl->not_sounding,
            rx_ctrl->aggregation, rx_ctrl->stbc, rx_ctrl->fec_coding, rx_ctrl->sgi,
            rx_ctrl->noise_floor, rx_ctrl->ampdu_cnt, rx_ctrl->channel, rx_ctrl->secondary_channel,
            rx_ctrl->timestamp, rx_ctrl->ant, rx_ctrl->sig_len, rx_ctrl->rx_state);

    printf(",%d,%d,\"[%d", info->len, info->first_word_invalid, info->buf[0]);

    // A toto je uz samotny vypis surovych CSI dat pro jendotlive subkanaly
    for (int i = 1; i < info->len; i++) {
        printf(",%d", info->buf[i]);
    }

    // Radek s hodnotami oddelenym icarkou ukoncime zalomenim
    printf("]\"\n");
}


// Funkce pro incializaci zprav informaci CSI
// Jakmile ziskame popisna data o spojeni, zavolame funkci wifi_csi_rx_data
// https://en.wikipedia.org/wiki/Channel_state_information
static void wifi_csi_inicializace(){
    wifi_csi_config_t csi_konfigurace = {
        .lltf_en           = true,
        .htltf_en          = false,
        .stbc_htltf2_en    = false,
        .ltf_merge_en      = true,
        .channel_filter_en = true,
        .manu_scale        = true,
        .shift             = true,
    };

    static wifi_ap_record_t ap_info = {0};
    ESP_ERROR_CHECK(esp_wifi_sta_get_ap_info(&ap_info));
    ESP_ERROR_CHECK(esp_wifi_set_csi_config(&csi_konfigurace));
    ESP_ERROR_CHECK(esp_wifi_set_csi_rx_cb(wifi_csi_rx_data, ap_info.bssid));
    ESP_ERROR_CHECK(esp_wifi_set_csi(true));
}

// Funkce pro start ulohy, ktera bude periodicky posilat ping na nejblizsi router (gateway)
// Samotne pingy jsou zastupne, vytvareji ale umely radiovy provoz, u ktereho muzeme sledovat jeho kvalitu skrze CSI
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/protocols/icmp_echo.html
static esp_err_t wifi_ping_router_start(){
    static esp_ping_handle_t ping_handle = NULL;
    // Konfigurace 
    esp_ping_config_t ping_config = {
        .count           = 0,
        .interval_ms     = 1000 / PING_FREKVENCE,
        .timeout_ms      = 1000,
        .data_size       = 1,
        .tos             = 0,
        .task_stack_size = 4096,
        .task_prio       = 0,
    };

    esp_netif_ip_info_t local_ip;
    esp_netif_get_ip_info(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"), &local_ip);
    inet_addr_to_ip4addr(ip_2_ip4(&ping_config.target_addr), (struct in_addr *)&local_ip.gw);
    esp_ping_callbacks_t cbs = { 0 };
    esp_ping_new_session(&ping_config, &cbs, &ping_handle);
    esp_ping_start(ping_handle);

    return ESP_OK;
}


// Hlavni funkce main, zacatek programu
void app_main(void)
{
    // Inicializace persistentniho uloziste NVS v pameti flash
    // https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/storage/nvs_flash.html
    nvs_flash_init();

    // Pripojeni k Wi-Fi
    pripojit_k_wifi();

    // Inicializace Channel State Information
    // https://en.wikipedia.org/wiki/Channel_state_information
    wifi_csi_inicializace();

    //Start ulohy, ktera bude kazdych 500 ms pingat na nejblizsi router (gateway)
    wifi_ping_router_start();
}
Diskuze (14) Další článek: Jak by se AI měla chovat a kdo by o tom měl rozhodnout. ChatGPT se přizpůsobí lidem s různým pohledem na svět

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.

Témata článku: , , , , , , , , , , , , , , , , , , , , , , , , ,