BLE: Central Device cannot pair/bond Peripheral Device
Port, board and/or hardware
esp32c3 (Also tested on esp32 as a central device)
MicroPython version
Tested on v1.24.1 and v1.25.0
Reproduction
I use the minimal code to test this. The peripheral device can be paired with nRF Connect.
Here is my code of peripheral device:
import bluetooth
from ble_util import bleconst
import time
print("peripheral mode")
handle = None
ble = bluetooth.BLE()
def irq(event, data):
global handle, ble
print("IRQ:", event, data)
if event == bleconst.IRQ_CENTRAL_CONNECT:
conn_handle, addr_type, addr = data
handle = conn_handle
print(" IRQ_CENTRAL_CONNECT", conn_handle, addr_type, addr)
elif event == bleconst.IRQ_PASSKEY_ACTION:
conn_handle, action, passkey = data
print(" IRQ_PASSKEY_ACTION", conn_handle, action, passkey)
if action == bleconst.PASSKEY_ACTION_DISPLAY:
print(" PASSKEY_ACTION_DISPLAY")
print(" Display passkey:", 123456)
ble.gap_passkey(conn_handle, action, 123456)
ble.active(True)
ble.config(io=bleconst.IO_CAPABILITY_DISPLAY_ONLY,
mitm=True,
le_secure=True,
bond=True)
ble.irq(irq)
ble.gap_advertise(0)
time.sleep(100)
When the peripheral device is connected with nRF Connect, the output is as below:
peripheral mode
IRQ: 1 (1, 1, <memoryview>)
IRQ_CENTRAL_CONNECT 1 1 <memoryview>
IRQ: 19 (1, 3, <memoryview>)
IRQ: 27 (1, 6, 0, 500, 0)
IRQ: 27 (1, 36, 0, 500, 0)
IRQ: 29 (2, 0, None)
IRQ: 29 (1, 0, <memoryview>)
IRQ: 29 (2, 0, <memoryview>)
IRQ: 31 (1, 3, 0)
IRQ_PASSKEY_ACTION 1 3 0
PASSKEY_ACTION_DISPLAY
Display passkey: 123456
IRQ: 28 (1, 1, 1, 1, 16)
IRQ: 30 (1, <memoryview>, <memoryview>)
IRQ: 29 (1, 0, <memoryview>)
IRQ: 30 (2, <memoryview>, <memoryview>)
IRQ: 29 (1, 0, <memoryview>)
And here is my code of central device:
import bluetooth
from ble_util import bleconst
import time
print("central mode")
handle = None
ble = bluetooth.BLE()
def irq(event, data):
global handle, ble
print("IRQ:", event, data)
if event == bleconst.IRQ_PERIPHERAL_CONNECT:
conn_handle, addr_type, addr = data
handle = conn_handle
print(" IRQ_PERIPHERAL_CONNECT", conn_handle, addr_type, addr)
ble.gap_pair(conn_handle)
elif event == bleconst.IRQ_PASSKEY_ACTION:
conn_handle, action, passkey = data
print(" IRQ_PASSKEY_ACTION", conn_handle, action, passkey)
if action == bleconst.PASSKEY_ACTION_INPUT:
print(" PASSKEY_ACTION_INPUT")
print(" Input passkey:", 123456)
ble.gap_passkey(conn_handle, action, 123456)
ble.active(True)
ble.config(io=bleconst.IO_CAPABILITY_KEYBOARD_ONLY,
mitm=True,
le_secure=True,
bond=True)
ble.irq(irq)
ble.gap_connect(0, bytes.fromhex('123456789abc'))
time.sleep(100)
When connected to the peripheral device, I use gap_pair to pair/bond the peripheral device.
I run peripheral first, and then central. Here are the outputs:
Peripheral
=======================================
peripheral mode
IRQ: 1 (1, 0, <memoryview>)
IRQ_CENTRAL_CONNECT 1 0 <memoryview>
IRQ: 29 (2, 0, None)
IRQ: 29 (1, 0, <memoryview>)
IRQ: 29 (2, 0, <memoryview>)
IRQ: 31 (1, 3, 0)
IRQ_PASSKEY_ACTION 1 3 0
PASSKEY_ACTION_DISPLAY
Display passkey: 123456
IRQ: 28 (1, 0, 0, 0, 0)
##############################################
Central
========================================
central mode
IRQ: 7 (0, 0, <memoryview>)
IRQ_PERIPHERAL_CONNECT 0 0 <memoryview>
IRQ: 29 (2, 0, <memoryview>)
IRQ: 29 (2, 0, None)
IRQ: 29 (1, 0, <memoryview>)
IRQ: 28 (0, 0, 0, 0, 0)
The result indicates that IRQ_PASSKEY_ACTION is not triggered in the central device, which is not as expected.
Expected behaviour
The IRQ_PASSKEY_ACTION should be triggered in the central device and then complete the pairing/bonding.
Observed behaviour
The IRQ_PASSKEY_ACTION is not triggered in the central device.
Additional Information
Is there any example of pairing/bonding between central device and peripheral device?
I saw there is examples/bluetooth/ble_bonding_peripheral.py, but there is no examples/bluetooth/ble_bonding_central.py.
Code of Conduct
Yes, I agree
unable to connect to nintendo switch joystick using bluetooth
Port, board and/or hardware
waveshare c6 zero
MicroPython version
Hi
I am unable to use bluetooth to connect to nintendo switch joystick. The AI said:
The short answer is that, even though the ESP32-C6 hardware in the Waveshare C6 Zero is dual-mode BLE capable, MicroPython’s current ESP32 “ubluetooth” port only implements the Peripheral (server) role. Central-mode APIs like gap_scan() and gap_connect() are either stubbed out or not fully functional in the v1.25.0 firmware you’re running, so it will never actually discover or connect to a Joy-Con.
Is it true we don't have "central" mode for bluetooth?
thanks
Peter
Reproduction
import ubluetooth
from machine import Pin
import time
import struct
# Bluetooth constants
BLE_NINTENDO_VID = 0x057e # Nintendo's Vendor ID (confirmed)
BLE_HID_SERVICE = "1812" # HID Service UUID
BLE_SCAN_DURATION = 15000 # Scan for 15 seconds per cycle
# BLE event codes
IRQ_SCAN_RESULT = 5
IRQ_SCAN_DONE = 6
IRQ_CENTRAL_CONNECT = 1
IRQ_CENTRAL_DISCONNECT = 2
IRQ_GATTC_SERVICE_RESULT = 7
IRQ_GATTC_CHARACTERISTIC_RESULT = 8
IRQ_GATTC_READ_RESULT = 11
IRQ_GATTC_NOTIFY = 16
class BLEJoyCon:
def __init__(self):
self.ble = ubluetooth.BLE()
self.ble.active(True)
self.ble.irq(self.ble_irq)
self.connected = False
self.conn_handle = None
self.hid_char_handle = None
self.target_addr = None
self.scan()
def ble_irq(self, event, data):
try:
if event == IRQ_SCAN_RESULT:
addr_type, addr, adv_type, rssi, adv_data = data
addr = bytes(addr)
addr_str = ':'.join(['%02x' % b for b in addr])
vid_bytes = BLE_NINTENDO_VID.to_bytes(2, 'little')
vid_detected = vid_bytes in adv_data
# Print ALL advertisement data as hex for debugging
#print("ADV DATA HEX:", [hex(b) for b in adv_data], "Address:", addr_str, "RSSI:", rssi)
# Check for Joy-Con (R) specifically
if b'Joy-Con (R)' in adv_data or vid_detected:
print("Found Nintendo Joy-Con (R):", adv_data)
self.target_addr = (addr_type, addr)
self.ble.gap_scan(None)
self.ble.gap_connect(addr_type, addr)
elif event == IRQ_SCAN_DONE:
print("Scan completed")
if not self.connected and not self.target_addr:
print("No Nintendo controller found, attempting direct connection...")
elif event == IRQ_CENTRAL_CONNECT:
self.conn_handle, addr_type, addr = data
self.connected = True
addr_str = ':'.join(['%02x' % b for b in bytes(addr)])
print("Connected to", addr_str)
self.ble.gattc_discover_services(self.conn_handle)
elif event == IRQ_CENTRAL_DISCONNECT:
self.connected = False
self.conn_handle = None
self.target_addr = None
print("Disconnected")
self.scan()
elif event == IRQ_GATTC_SERVICE_RESULT:
conn_handle, start_handle, end_handle, uuid = data
# Print ALL discovered service UUIDs for debugging
print("Service found:", uuid)
if conn_handle == self.conn_handle and uuid == ubluetooth.UUID(BLE_HID_SERVICE):
print("Found HID service")
self.ble.gattc_discover_characteristics(self.conn_handle, start_handle, end_handle)
elif event == IRQ_GATTC_CHARACTERISTIC_RESULT:
conn_handle, def_handle, value_handle, properties, uuid = data
if conn_handle == self.conn_handle and "2A4D" in str(uuid):
self.hid_char_handle = value_handle
print("Found HID characteristic")
self.ble.gattc_write(self.conn_handle, self.hid_char_handle, b'\x01\x00', 1)
elif event == IRQ_GATTC_NOTIFY:
conn_handle, value_handle, notify_data = data
if conn_handle == self.conn_handle and value_handle == self.hid_char_handle:
self.parse_hid_data(notify_data)
except Exception as e:
print("IRQ error:", e)
def scan(self):
print("Scanning for devices...")
self.ble.gap_scan(BLE_SCAN_DURATION, 30000, 30000)
def parse_hid_data(self, data):
try:
if len(data) >= 10:
buttons = data[3]
stick_x = (data[6] | (data[7] << 8)) & 0xFFF
stick_y = (data[8] | (data[9] << 8)) & 0xFFF
print("Buttons:", bin(buttons), "Stick X:", stick_x, "Stick Y:", stick_y)
if buttons & 0x01:
print("A button pressed")
if buttons & 0x02:
print("B button pressed")
if buttons & 0x04:
print("X button pressed")
if buttons & 0x08:
print("Y button pressed")
else:
print("Short HID report:", [hex(b) for b in data])
except Exception as e:
print("Error parsing HID data:", e)
def stop(self):
if self.connected:
self.ble.gap_disconnect(self.conn_handle)
self.ble.active(False)
# Initialize and run
try:
joycon = BLEJoyCon()
while True:
time.sleep(1)
except KeyboardInterrupt:
joycon.stop()
print("Stopped")
except Exception as e:
print("Main error:", e)
Expected behaviour
Observed behaviour
Additional Information
No, I've provided everything above.
Code of Conduct
Yes, I agree