Example project · ESP32
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.

What's on the bench
- 2× Battery
- 2× ESP32-C6
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')