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¶
Č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í.