11.4. Periklanan dan Pemindaian

Dua perangkat BLE yang belum pernah bertemu sebelumnya harus saling menemukan terlebih dahulu. Jaringan menyelesaikan hal ini dengan memberikan setiap perangkat sebuah alamat dari kumpulan bersama dan mengizinkan salah satu pihak menjangkau pihak lain melalui router. BLE tidak memiliki router, tidak ada kumpulan bersama, dan -- di antara sebagian besar pasangan perangkat -- tidak ada hubungan sebelumnya sama sekali. Generic Access Profile (GAP) menyelesaikan penemuan dengan pola siaran-dan-dengar. Satu sisi mengiklankan -- mengirimkan paket pendek di tiga saluran periklanan pada interval teratur, mendeskripsikan identitasnya. Sisi lain memindai -- menyapu tiga saluran yang sama mendengarkan paket-paket tersebut.

GAP mendefinisikan empat peran di sekitar pola itu, masing-masing merupakan kombinasi spesifik dari periklanan dan mendengarkan.

11.4.1. Empat peran 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.

Empat peran GAP. Sumbu vertikal adalah apakah perangkat mengiklankan; sumbu horizontal adalah apakah perangkat menerima (atau memulai) koneksi.

  • Sebuah peripheral mengiklankan paket yang mengatakan "Saya ada dan Anda dapat terhubung ke saya". Ketika perangkat lain membuka koneksi, peripheral berhenti mengiklankan dan mulai melayani permintaan GATT. Tali detak jantung, termometer, dan sebagian besar kamera-sebagai-sensor bertindak sebagai peripheral.

  • Sebuah central memindai peripheral, memilih satu, dan memulai koneksi. Setelah terhubung, ia berbicara GATT sebagai klien. Ponsel, laptop, dan kamera yang bertindak sebagai pengumpul data adalah central.

  • Sebuah broadcaster mengiklankan tetapi tidak pernah menerima koneksi. Muatan iklannya adalah data -- tidak ada yang perlu dihubungkan. iBeacon dan sebagian besar beacon kehadiran toko adalah broadcaster.

  • Sebuah observer memindai iklan-iklan tersebut dan membaca muatannya, juga tanpa pernah terhubung. Kamera yang mendengarkan beacon terdekat dan bertindak berdasarkan apa yang didengarnya adalah observer.

Satu perangkat dapat memainkan lebih dari satu peran secara bersamaan -- kamera dapat menjadi peripheral yang menerbitkan statusnya sendiri dan central yang terhubung ke sensor terdekat. Radio memultipleks pekerjaan tersebut.

11.4.2. Apa yang terkandung dalam paket iklan

Paket iklan berukuran kecil: 31 byte muatan, atau 62 jika pengiklan juga menerbitkan respons pemindaian yang dapat diminta oleh pemindai secara langsung. Muatan adalah daftar bidang bertipe pendek:

  • Flag. Dapat dihubungkan atau tidak, mode penemuan umum / terbatas.

  • Nama lokal. String pendek yang mudah dibaca manusia -- nama yang ditampilkan sistem operasi di ponsel atau laptop dalam menu Bluetooth-nya.

  • UUID layanan. Daftar pengenal layanan GATT yang di-hosting perangkat, sehingga pemindai dapat mengenali peripheral yang mampu tanpa harus terlebih dahulu terhubung. Tali detak jantung mengiklankan 0x180D -- UUID layanan Heart-Rate standar -- dan aplikasi detak jantung di ponsel mengetahui dari itu saja bahwa perangkat layak untuk dihubungkan.

  • Tampilan. Nilai 16-bit dari daftar nomor yang ditetapkan Bluetooth (sensor, media generik, jam tangan generik, ...) -- petunjuk untuk central tentang apa yang harus ditampilkan.

  • Data spesifik produsen. Byte bebas yang diawali dengan ID perusahaan. iBeacon menggunakan bidang ini untuk membawa UUID, mayor, dan minor mereka; aplikasi kustom dapat menempatkan apa pun di sini.

Muatan iklan sangat terbatas. Batas 31-byte membuat pemilihan konten menjadi keputusan desain nyata -- nama yang panjang dan mudah dibaca dapat dengan cepat menghabiskan ruang untuk UUID layanan. API aioble.advertise() mengambil masing-masing ini sebagai argumen kata kunci dan merakit byte untuk Anda, meluap ke respons pemindaian secara otomatis jika paket utama penuh.

11.4.3. Pemindaian aktif dan pasif

Pemindai dapat berjalan secara pasif, di mana ia mendengarkan paket iklan dan mengurai apa yang datang, atau secara aktif, di mana ia juga mengirimkan permintaan pemindaian ke setiap pengiklan dan mengurai respons pemindaian yang dikembalikan.

Pemindaian pasif hanya melihat paket iklan awal (hingga 31 byte). Pemindaian aktif menggandakan itu -- respons pemindaian adalah 31 byte tambahan yang dapat digunakan peripheral untuk bidang yang tidak muat. Pemindaian aktif juga menghabiskan daya di kedua sisi, karena pemindai mengirimkan dan pengiklan mengirimkan paket tambahan, sehingga ini adalah pilihan, bukan default.

Dalam API aioble, active=True pada aioble.scan() beralih mode, dan setiap ScanResult mengekspos adv_data gabungan ditambah resp_data serta pembantu seperti result.name() dan result.services() yang menyembunyikan penguraian tingkat byte.

Catatan

Atribut adv_data dan resp_data adalah muatan iklan dan respons pemindaian mentah (bytes). Pembantu -- name(), services(), manufacturer() -- mencakup bidang standar umum dan merupakan pilihan yang tepat 99% dari waktu. Gunakan byte mentah hanya saat Anda memerlukan bidang vendor yang tidak diurai oleh pembantu (URL Eddystone, UUID/major/minor iBeacon, jenis iklan kustom). Tata letak byte adalah TLV standar: setiap bidang adalah length, type, value....

11.4.4. Interval periklanan

Seberapa sering peripheral menyiarkan adalah pertukaran antara daya dan latensi penemuan. Iklan yang keluar setiap 20 ms hampir langsung terdeteksi oleh pemindai tetapi membuat radio tetap sibuk dan menguras baterai; iklan setiap detik hampir tidak menggunakan daya tetapi membuat sapuan pemindai lebih lambat mendeteksi perangkat.

interval_us pada aioble.advertise() mengatur interval dalam mikrodetik:

  • 20.000 hingga 100.000 us (20 ms - 100 ms) -- pemasangan cepat, aplikasi mengharapkan respons cepat, perangkat yang terhubung ke daya.

  • 250.000 hingga 1.000.000 us (250 ms - 1 s) -- default yang wajar untuk peripheral bertenaga baterai yang ingin dapat ditemukan tanpa menghabiskan daya.

  • Di atas 1.000.000 us -- siaran latar belakang lambat, beacon yang mengirimkan pembaruan posisi setiap beberapa detik.

Sisi pemindai memiliki pengaturannya sendiri -- aioble.scan() mengambil interval_us dan window_us (seberapa sering pemindai mengaktifkan radionya dan berapa lama ia mendengarkan setiap kali). Defaultnya sudah baik; satu-satunya perubahan umum adalah menyetel keduanya sama untuk pemindaian berkelanjutan saat baterai bukan masalah.

11.4.5. Pola tanpa koneksi -- broadcaster dan observer

Halaman tentang Bertindak sebagai periferal dan Bertindak sebagai central membahas bentuk API yang dapat terhubung -- di mana peripheral menerima koneksi dan kedua sisi bertukar data melalui GATT. Bentuk lainnya adalah tanpa koneksi: broadcaster mengirimkan muatan sebagai iklan, dan observer mana pun dalam jangkauan dapat membacanya tanpa harus terhubung. Beacon, sensor kehadiran, dan telemetri satu arah semuanya berada di sini.

Broadcaster adalah aioble.advertise() dengan connectable=False. Data spesifik produsen membawa muatannya:

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())

Kata kunci timeout_ms mengakhiri panggilan advertise setelah satu detik; loop luar menerbitkannya kembali dengan nomor urut berikutnya sehingga pendengar melihat data yang segar. Flag connectable=False itulah yang membuat iklan berstyle broadcaster -- kamera tidak akan merespons permintaan koneksi bahkan jika ada yang datang.

Observer adalah pemindai hanya-baca yang cocok. Ia menjalankan aioble.scan() selamanya, mengurai iklan yang masuk, dan tidak pernah memanggil 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 memindai hingga manajer konteks keluar; active=False menjaga radio observer sendiri tetap diam (tidak ada permintaan respons pemindaian) untuk konsumsi daya terendah. Argumen filter= pada manufacturer() membuang setiap iklan yang tidak cocok dengan ID perusahaan, sehingga loop hanya aktif untuk lalu lintas broadcaster.

11.4.6. Dari penemuan ke koneksi

Setelah central memilih peripheral untuk diajak bicara, ia berhenti mendengarkan, mengirimkan permintaan koneksi di saluran periklanan yang terakhir digunakan peripheral, dan kedua sisi masuk ke saluran data hopping dari lapisan tautan. Peripheral biasanya berhenti mengiklankan pada titik ini. Apa yang terjadi selanjutnya -- parameter koneksi, penemuan GATT, masa pakai tautan -- ada pada Koneksi.