11.4. Annonsering och skanning¶
Två BLE-enheter som aldrig har träffats förut måste först hitta varandra. Nätverk löser detta genom att tilldela varje enhet en adress ur en delad pool och låta endera sidan nå den andra via routrar. BLE har inga routrar, ingen delad pool och – mellan de flesta enhetspar – ingen tidigare relation alls. Generic Access Profile (GAP) löser upptäckt med ett utsändnings-och-lyssna-mönster i stället. Ena sidan annonserar – den sänder ett kort paket på de tre annonseringskanalerna med ett regelbundet intervall, som beskriver vilken den är. Den andra sidan skannar – den sveper över samma tre kanaler och lyssnar efter dessa paket.
GAP definierar fyra roller kring det mönstret, var och en en specifik kombination av annonsering och lyssnande.
11.4.1. De fyra GAP-rollerna¶
De fyra GAP-rollerna. Den vertikala axeln är huruvida enheten annonserar; den horisontella axeln är huruvida den accepterar (eller initierar) anslutningar.¶
En peripheral annonserar paket som säger ”jag är här och du kan ansluta till mig”. När en annan enhet öppnar en anslutning slutar peripheral-enheten annonsera och börjar betjäna GATT-förfrågningar. Pulsband, termometrar och de flesta kameror-som-sensorer agerar som peripheral-enheter.
En central skannar efter peripheral-enheter, väljer en och initierar en anslutning. Efter anslutning talar den GATT som klient. Telefoner, bärbara datorer och kameror som agerar datainsamlare är central-enheter.
En broadcaster annonserar men accepterar aldrig anslutningar. Dess annonseringsnyttolast är datan – det finns inget att ansluta till. iBeacons och de flesta närvarobeacons i butiker är broadcaster-enheter.
En observer skannar efter dessa annonseringar och läser nyttolasten, återigen utan att någonsin ansluta. En kamera som lyssnar efter beacons i närheten och agerar på vad den hör är en observer.
En enda enhet kan spela mer än en roll samtidigt – en kamera kan vara en peripheral som publicerar sitt eget tillstånd och en central som ansluter till en sensor i närheten. Radion multiplexar arbetet.
11.4.2. Vad ett annonseringspaket innehåller¶
Ett annonseringspaket är litet: 31 byte nyttolast, eller 62 om annonsören även publicerar ett scan response som skannrar kan begära i farten. Nyttolasten är en lista över korta typade fält:
Flaggor. Anslutningsbar eller inte, allmänt / begränsat upptäckbar.
Lokalt namn. En kort, mänskligt läsbar sträng – namnet som operativsystemet på en telefon eller bärbar dator visar i sin Bluetooth-meny.
Tjänste-UUID:er. En lista över GATT-tjänsteidentifierare som enheten är värd för, så att en skanner kan känna igen kapabla peripheral-enheter utan att ansluta först. Ett pulsband annonserar
0x180D– standard-UUID:et för Heart-Rate-tjänsten – och en pulsapp på telefonen vet enbart utifrån detta att enheten är värd att ansluta till.Utseende (Appearance). Ett 16-bitarsvärde från Bluetooths lista över tilldelade nummer (sensor, allmänt media, allmän klocka, …) – en ledtråd till central-enheten om vad som ska visas.
Tillverkarspecifik data. Fritt formaterade byten med ett företags-ID som prefix. iBeacons använder detta fält för att bära sitt UUID, major och minor; egna applikationer kan lägga vad de vill här.
Annonseringsnyttolaster är trånga. 31-bytesgränsen gör valet av vad som ska inkluderas till ett verkligt designbeslut – ett långt mänskligt läsbart namn kan snabbt lämna inget utrymme för tjänste-UUID:er. API:et aioble.advertise() tar var och en av dessa som ett nyckelordsargument och sätter ihop byten åt dig, med automatiskt överflöde till scan response om huvudpaketet fylls upp.
11.4.3. Aktiv och passiv skanning¶
En skanner kan köra passivt, där den lyssnar efter annonseringspaket och tolkar det som anländer, eller aktivt, där den även skickar en scan request till varje annonsör och tolkar scan response som kommer tillbaka.
Passiv skanning ser endast det inledande annonseringspaketet (upp till 31 byte). Aktiv skanning fördubblar detta – scan response är ytterligare 31 byte som peripheral-enheten kan använda för fält som inte fick plats. Aktiv skanning kostar också ström på båda sidor, eftersom skannern sänder och annonsören sänder ett extra paket, så det är ett val snarare än ett standardläge.
I aioble-API:et växlar active=True på aioble.scan() läget, och varje ScanResult exponerar den kombinerade adv_data plus resp_data samt hjälpfunktioner som result.name() och result.services() som döljer tolkningen på bytenivå.
Anteckning
Attributen adv_data och resp_data är de råa annonserings- och scan-response-nyttolasterna (bytes). Hjälpfunktionerna – name(), services(), manufacturer() – täcker de vanliga standardfälten och är rätt val 99 % av tiden. Ta till de råa byten endast när du behöver ett leverantörsfält som hjälpfunktionerna inte tolkar (Eddystone-URL:er, iBeacon-UUID/major/minor, egna annonseringstyper). Bytelayouten är den vanliga TLV-typen: varje fält är length, type, value....
11.4.4. Annonseringsintervallet¶
Hur ofta peripheral-enheten sänder är en avvägning mellan ström och upptäcktslatens. Annonseringar som går ut var 20 ms plockas upp nästan omedelbart av en skanner men håller radion upptagen och dränerar batteriet; annonseringar varje sekund använder nästan ingen ström men gör att en skanner-svepning är långsammare med att märka enheten.
interval_us på aioble.advertise() ställer in intervallet i mikrosekunder:
20 000 till 100 000 us (20 ms - 100 ms) – snabb ihopparning, appen förväntar sig ett snabbt svar, inkopplad enhet.
250 000 till 1 000 000 us (250 ms - 1 s) – ett rimligt standardvärde för en batteridriven peripheral-enhet som vill vara upptäckbar utan att bränna laddning.
Över 1 000 000 us – långsam bakgrundsutsändning, beacons som skickar en positionsuppdatering med några sekunders mellanrum.
Skannersidan har sina egna rattar – aioble.scan() tar interval_us och window_us (hur ofta skannern väcker sin radio och hur länge den lyssnar varje gång). Standardvärdena är bra; den enda vanliga ändringen är att sätta båda lika för en kontinuerlig skanning när batteri inte är ett bekymmer.
11.4.5. Anslutningslösa mönster – broadcaster och observer¶
Sidorna om Att agera som kringutrustning och Att agera som en central går igenom den anslutningsbara formen av API:et – där en peripheral-enhet accepterar en anslutning och de två sidorna utbyter data via GATT. Den andra formen är anslutningslös: en broadcaster sänder nyttolast-som-annonsering, och vilken observer som helst inom räckhåll kan läsa den utan att någonsin ansluta. Beacons, närvarosensorer och enkelriktad telemetri lever alla här.
En broadcaster är aioble.advertise() med connectable=False. Tillverkarspecifik data bär nyttolasten:
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())
Nyckelordet timeout_ms avslutar annonseringsanropet efter en sekund; den yttre loopen utfärdar det på nytt med nästa sekvensnummer så att lyssnare ser färsk data. Flaggan connectable=False är det som gör annonseringen broadcaster-aktig – kameran svarar inte på en anslutningsförfrågan även om en anländer.
En observer är den matchande skrivskyddade skannern. Den kör aioble.scan() för evigt, tolkar inkommande annonseringar och anropar aldrig 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 skannar tills kontexthanteraren avslutas; active=False håller observerns egen radio tyst (inga scan-response-förfrågningar) för det lägsta strömuttaget. Argumentet filter= på manufacturer() kasserar varje annonsering som inte matchar företags-ID:t, så loopen utlöses endast för broadcasterns trafik.
11.4.6. Från upptäckt till en anslutning¶
När en central-enhet väl väljer en peripheral-enhet att prata med slutar den lyssna, skickar en connect request på den annonseringskanal som peripheral-enheten senast använde, och båda sidorna faller ner i de hoppande datakanalerna i länklagret. Peripheral-enheten slutar vanligtvis annonsera vid denna punkt. Vad som händer härnäst – anslutningsparametrar, GATT-upptäckt, länkens livstid – finns på Anslutningar.