adafruit / circuitpython

CircuitPython - a Python implementation for teaching coding with microcontrollers
https://circuitpython.org
Other
4.12k stars 1.22k forks source link

Fix changing the BLE MAC address #6086

Open willwade opened 2 years ago

willwade commented 2 years ago

We would love to be able to change MAC address of a ble device in circuitpy. Right now it’s the last thing holding us back to rewriting alll our code in py

use case. You can see some code here of how we are doing it in c https://github.com/AceCentre/morAce/blob/572e972336961dcf111b94a6fbabcda6ba3ca1be/morAce/morAce.ino#L852

imagine you use switch access on iOS. - if you want a device to change from a system switch interface to say a switch interface for a app then changing the MAC address is the only way to do it. If you don’t (& just change name, manufacturer name, device I’d etc) iOS just refuses to see it as a different device.

Another use case. We have some code that interprets morse code to hid. But then we want a user to use the same interface to use switch scanning. Doing it this way allows this.

tannewt commented 2 years ago

Looking more closely at our implementation, I believe this is already supported. Adapter.address is both readable and writable. So, I think you can do something like:

import _bleio
new_address = _bleio.Address(b"\x00\x00\x00\x00\x00\x00", _bleio.Address.RANDOM_STATIC)
_bleio.adapter.address = new_address

Please give it a try and let me know if it works for you.

willwade commented 2 years ago

Ok. Tried but getting a problem

` import _bleio new_address = _bleio.Address(b"\x00\x00\x00\x00\x00\x00", _bleio.Address.RANDOM_STATIC) _bleio.adapter.address = new_address

And here its output: code.py output: Traceback (most recent call last): File "code.py", line 3, in _bleio.BluetoothError: Could not set address`

tannewt commented 2 years ago

Thanks for trying this @willwade. I suspect it needs to be done when no advertising or scanning is happening. Could you try it with supervisor.disable_ble_workflow() in boot.py? That will prevent CircuitPython from advertising before user code does. Thanks!

willwade commented 2 years ago

Hmmm. Same problem sadly

output: code.py output: Traceback (most recent call last): File "code.py", line 3, in _bleio.BluetoothError: Could not set address

tannewt commented 2 years ago

Thanks for testing! We'll need to take a more detailed look at this.

hyx0329 commented 2 years ago

After digging around, I found that to compose a valid address of type RANDOM_STATIC, the address follows a format like xxxx...xxxx11, that's to say, the 2 MSBs of the address should be 1. Example code below.

address_bytes = bytearray(b'\x00\x00\x00\x00\x00\x00')
address_bytes[-1] = address_bytes[-1] | 0xC0 # now is b'\x00\x00\x00\x00\x00\xc0'
new_address = _bleio.Address(address_bytes, _bleio.Address.RANDOM_STATIC)  # <Address c0:00:00:00:00:00>
_bleio.adapter.address = new_address
willwade commented 2 years ago

ok so I'm perplexed. This was working but now today - same code - it isn't working. What's up with this? Is this some basic logic I'm not getting?

import _bleio

current_address = _bleio.adapter.address
print("current address:", current_address)

address_bytes = bytearray(current_address.address_bytes)
address_bytes [5] = 0xDD
new_address = _bleio.Address(address_bytes, _bleio.Address.RANDOM_STATIC)
print("new addreass:   ", new_address)

_bleio.adapter.address = new_address

print("address is set")

What I get is


current address: <Address eb:ac:4a:3c:55:2a>
new addreass:    <Address dd:ac:4a:3c:55:2a>
Traceback (most recent call last):
  File "code.py", line 11, in <module>
_bleio.BluetoothError: Could not set address
hyx0329 commented 2 years ago

@willwade same code, works on my Makerdiary NRF52840 USB dongle, only if I disable the ble workflow by supervisor.disable_ble_workflow(). You have to call the method in boot.py(before fully boot up, I assume), otherwise you have to do a manual soft reboot(aka. reset the REPL) after that.

willwade commented 2 years ago

@willwade same code, works on my Makerdiary NRF52840 USB dongle, only if I disable the ble workflow by supervisor.disable_ble_workflow(). You have to call the method in boot.py(before fully boot up, I assume), otherwise you have to do a manual soft reboot(aka. reset the REPL) after that.

Hmmm. So that's not making any difference for me on a adafruit itsybitsy nrf52840. What's strange is that I had this running that code snippet fine on day 1. Day 2 I had loaded totally different code. Day 3. Reloaded this code and the error. I've got another itsybitsy and it's erroring too.