Bluetooth LE: scan, connect, discover
Two ESP32-C6 boards run the full BLE story through the real vendor controller: one registers a Battery Service and advertises connectably, the other scans, hears the advertisement, connects as central over a genuine LE link, and discovers the server's GATT services. One script runs on both boards; each derives its role from its own MAC. Watch the serial tabs narrate both sides, follow ADV_IND / CONNECT_IND / ATT frames in the 📡 Sniffer, and click either board's Bluetooth tab for its live link state.

How it works
The whole BLE client/server arc with two ESP32-C6 boards and one symmetric script: each board checks its own MAC's parity to pick a role. The even board registers a real GATT Battery Service (UUID 0x180F with a 0x2A19 level characteristic) and advertises connectably; the odd board scans, finds the advertiser, connects as central, and walks service discovery over the live LE link. The serial tabs narrate both sides: SRV advertising, CLI found, CLI connected, service UUID(0x180f), discovery done.
What's on the bench
- 2× Battery
- 2× ESP32-C6
How it's wired
- Battery · pos→ESP32-C6 · vin
- Battery · neg→ESP32-C6 · gnd
- Battery · pos→ESP32-C6 · vin
- Battery · neg→ESP32-C6 · gnd
The code
This MicroPython script runs on the emulated board every boot; edit it in the Code tab.
import bluetooth, time b = bluetooth.BLE() b.active(1) server = b.config('mac')[1][-1] % 2 == 0 if server: S = (bluetooth.UUID(0x180F), ((bluetooth.UUID(0x2A19), bluetooth.FLAG_READ | bluetooth.FLAG_WRITE),)) ((h,),) = b.gatts_register_services((S,)) def cb(e, d): if e == 1: print('SRV ' + 'central connected') elif e == 2: print('SRV ' + 'central disconnected') b.irq(cb) b.gap_advertise(50000, b'\x02\x01\x06\x05\xffCAFE', connectable=True) print('SRV ' + 'advertising battery service') else: tgt = [] cn = [] def cb(e, d): if e == 5 and not tgt: tgt.append((d[0], bytes(d[1]))) print('CLI ' + 'found advertiser') elif e == 7: cn.append(d[0]) print('CLI ' + 'connected') elif e == 9: print('CLI ' + 'service', d[3]) elif e == 10: print('CLI ' + 'discovery done') b.irq(cb) b.gap_scan(0, 30000, 30000) while not tgt: time.sleep_ms(50) b.gap_scan(None) time.sleep_ms(50) b.gap_connect(tgt[0][0], tgt[0][1]) while not cn: time.sleep_ms(50) time.sleep_ms(300) b.gattc_discover_services(cn[0]) print('CLI discovering services')
Try this
- Open both serial tabs and match the story line for line: SRV on one, CLI on the other.
- Open the 📡 Sniffer: advertising PDUs on channel 37, then the connection’s data channel traffic.
- Click each board's BLE Inspector tab: one shows advertising, the other connected as central.
- Move on to the pairing and bonding examples: same bench, deeper into the stack.


