11.4. Vysílání (advertising) a skenování#

Dvě BLE zařízení, která se nikdy předtím nesetkala, se musí nejprve najít. Síťování to řeší tím, že každému zařízení přidělí adresu ze sdíleného fondu a umožní oběma stranám dosáhnout té druhé přes routery. BLE nemá žádné routery, žádný sdílený fond a – mezi většinou dvojic zařízení – vůbec žádný předchozí vztah. Generic Access Profile (GAP) řeší objevování namísto toho vzorcem vysílej-a-naslouchej. Jedna strana vysílá (advertises) – v pravidelném intervalu odesílá krátký paket na třech vysílacích kanálech a popisuje, kdo je. Druhá strana skenuje – prochází stejné tři kanály a naslouchá těmto paketům.

GAP definuje kolem tohoto vzorce čtyři role, každou jako specifickou kombinaci vysílání a naslouchání.

11.4.1. Čtyři role GAP#

A two-by-two matrix. Rows are labelled "advertises" and "does not advertise". Columns are labelled "accepts connections" and "does not accept connections". The four cells contain the role names: Peripheral, Broadcaster, Central, Observer.

Čtyři role GAP. Svislá osa udává, zda zařízení vysílá; vodorovná osa udává, zda přijímá (nebo iniciuje) spojení.#

  • Peripheral vysílá pakety, které říkají „jsem tady a můžeš se ke mně připojit“. Když jiné zařízení otevře spojení, peripheral přestane vysílat a začne obsluhovat GATT požadavky. Hrudní pásy na měření tepu, teploměry a většina kamer ve funkci senzorů se chovají jako peripherals.

  • Central skenuje peripherals, vybere jeden a iniciuje spojení. Po připojení komunikuje přes GATT jako klient. Telefony, notebooky a kamery ve funkci sběračů dat jsou centrals.

  • Broadcaster vysílá, ale nikdy nepřijímá spojení. Jeho vysílaná data jsou tím obsahem – není k čemu se připojovat. iBeacons a většina beaconů pro detekci přítomnosti v obchodech jsou broadcasters.

  • Observer skenuje tato vysílání a čte jejich obsah, opět aniž by se kdy připojil. Kamera, která naslouchá blízkým beaconům a reaguje na to, co slyší, je observer.

Jediné zařízení může hrát více než jednu roli současně – kamera může být peripheral, který publikuje svůj vlastní stav, a zároveň central, který se připojuje k blízkému senzoru. Rádio práci multiplexuje.

11.4.2. Co obsahuje vysílací paket#

Vysílací paket je malý: 31 bajtů obsahu, nebo 62, pokud vysílač zveřejňuje také odpověď na sken (scan response), o kterou si skenery mohou za běhu požádat. Obsah je seznam krátkých typovaných polí:

  • Flags. Připojitelný či ne, obecně / omezeně objevitelný.

  • Local name. Krátký, člověku přívětivý řetězec – jméno, které operační systém v telefonu nebo notebooku zobrazuje ve své Bluetooth nabídce.

  • Service UUIDs. Seznam identifikátorů GATT služeb, které zařízení hostuje, takže skener dokáže rozpoznat schopné peripherals bez nutnosti se nejprve připojit. Hrudní pás na měření tepu vysílá 0x180D – standardní UUID služby Heart-Rate – a aplikace na měření tepu v telefonu už jen z toho ví, že se zařízení vyplatí připojit.

  • Appearance. 16bitová hodnota ze seznamu přiřazených čísel Bluetooth (senzor, generic media, generic watch, …) – nápověda pro central, co zobrazit.

  • Manufacturer-specific data. Volně formátované bajty s předponou v podobě ID firmy. iBeacons toto pole používají k přenosu svého UUID, major a minor; vlastní aplikace sem mohou vložit cokoli chtějí.

Vysílaný obsah je úsporný. Limit 31 bajtů činí z volby toho, co zahrnout, skutečné návrhové rozhodnutí – dlouhé člověku čitelné jméno může rychle nezanechat místo pro service UUIDs. API aioble.advertise() přijímá každé z těchto polí jako klíčový argument a sestaví bajty za vás, přičemž při zaplnění hlavního paketu automaticky přetéká do odpovědi na sken.

11.4.3. Aktivní a pasivní skenování#

Skener může běžet pasivně, kdy naslouchá vysílacím paketům a parsuje to, co přichází, nebo aktivně, kdy navíc odesílá každému vysílači požadavek na sken (scan request) a parsuje odpověď na sken, která se vrátí.

Pasivní skenování vidí pouze počáteční vysílací paket (až 31 bajtů). Aktivní skenování to zdvojnásobí – odpověď na sken je dalších 31 bajtů, které peripheral může použít pro pole, jež se nevešla. Aktivní skenování také stojí energii na obou stranách, protože skener vysílá a vysílač odesílá paket navíc, takže jde o volbu, nikoli o výchozí nastavení.

V API aioble přepíná režim active=True u aioble.scan() a každý ScanResult zpřístupňuje kombinovaná adv_data plus resp_data i pomocné funkce jako result.name() a result.services(), které skrývají parsování na úrovni bajtů.

Poznámka

Atributy adv_data a resp_data jsou surová (raw) data vysílání a odpovědi na sken (bytes). Pomocné funkce – name(), services(), manufacturer() – pokrývají běžná standardní pole a jsou správnou volbou v 99 % případů. Po surových bajtech sáhněte jen tehdy, když potřebujete pole výrobce, které pomocné funkce neparsují (Eddystone URL, iBeacon UUID/major/minor, vlastní typy vysílání). Rozložení bajtů je standardní TLV: každé pole je length, type, value....

11.4.4. Interval vysílání#

Jak často peripheral vysílá je kompromis mezi spotřebou a latencí objevení. Vysílání odcházející každých 20 ms zachytí skener téměř okamžitě, ale udržuje rádio vytížené a vybíjí baterii; vysílání každou sekundu spotřebuje téměř žádnou energii, ale způsobí, že skener zařízení zaregistruje pomaleji.

interval_us u aioble.advertise() nastavuje interval v mikrosekundách:

  • 20 000 až 100 000 us (20 ms - 100 ms) – rychlé párování, aplikace očekává rychlou odezvu, zařízení napájené ze sítě.

  • 250 000 až 1 000 000 us (250 ms - 1 s) – rozumné výchozí nastavení pro peripheral napájený z baterie, který chce být objevitelný, aniž by spaloval nabití.

  • Nad 1 000 000 us – pomalé vysílání na pozadí, beacony, které odesílají aktualizaci polohy každých několik sekund.

Strana skeneru má své vlastní ovládací prvky – aioble.scan() přijímá interval_us a window_us (jak často skener probouzí své rádio a jak dlouho pokaždé naslouchá). Výchozí hodnoty jsou v pořádku; jedinou běžnou změnou je nastavit obě hodnoty stejně pro nepřetržité skenování, když spotřeba baterie není problémem.

11.4.5. Bezspojové vzorce – broadcaster a observer#

Stránky Vystupování jako periferie a Vystupování jako central rozebírají spojovou (connectable) podobu API – kde peripheral přijímá spojení a obě strany si vyměňují data přes GATT. Druhou podobou je bezspojová (connectionless): broadcaster vysílá obsah jako vysílání a kterýkoli observer v dosahu jej může číst, aniž by se kdy připojil. Beacony, senzory přítomnosti a jednosměrná telemetrie patří sem.

Broadcaster je aioble.advertise() s connectable=False. Data nese Manufacturer-specific data:

import aioble
import asyncio
import struct

_COMPANY_ID = const(0xFFFF)                # 0xFFFF is "no specific vendor"

async def beacon():
    seq = 0
    while True:
        seq = (seq + 1) & 0xFFFF
        payload = struct.pack("<H", seq)
        await aioble.advertise(
            interval_us=500000,
            connectable=False,
            name="openmv-beacon",
            manufacturer=(_COMPANY_ID, payload),
            timeout_ms=1000,                # one cycle, then loop
        )

asyncio.run(beacon())

Klíčový argument timeout_ms ukončí volání advertise po jedné sekundě; vnější smyčka jej znovu vydá s dalším pořadovým číslem, takže posluchači vidí čerstvá data. Příznak connectable=False je tím, co dělá vysílání broadcasterovým stylem – kamera nebude reagovat na požadavek o připojení, i kdyby nějaký dorazil.

Observer je odpovídající skener pouze pro čtení. Spouští aioble.scan() donekonečna, parsuje příchozí vysílání a nikdy nevolá connect()

import aioble
import asyncio

_COMPANY_ID = const(0xFFFF)

async def watch():
    async with aioble.scan(duration_ms=0, active=False) as scanner:
        async for result in scanner:
            for company, data in result.manufacturer(filter=_COMPANY_ID):
                print(result.device.addr_hex(),
                      "rssi", result.rssi, "data", data)

asyncio.run(watch())

duration_ms=0 skenuje, dokud správce kontextu neukončí činnost; active=False udržuje vlastní rádio observeru tiché (žádné požadavky na odpověď na sken) pro nejnižší spotřebu energie. Argument filter= u manufacturer() zahodí každé vysílání, které neodpovídá ID firmy, takže smyčka se spustí pouze pro provoz broadcasteru.

11.4.6. Od objevení ke spojení#

Jakmile si central vybere peripheral, se kterým chce komunikovat, přestane naslouchat, odešle požadavek na připojení (connect request) na vysílacím kanálu, který peripheral naposledy použil, a obě strany přejdou do přeskakujících datových kanálů linkové vrstvy. Peripheral v tomto okamžiku obvykle přestane vysílat. Co následuje dále – parametry spojení, GATT objevování, životnost spojení – je na stránce Připojení.