-
-
Save wireboy5/e41444a135d7643c92fc3b83aa69058b to your computer and use it in GitHub Desktop.
""" | |
Super basic code to scan for and connect to v5 devices using the python bleak library. Prints out all data read over the user port. | |
""" | |
import argparse | |
import asyncio | |
from bleak import BleakScanner, BleakClient | |
SERVICE_UUID = "08590f7e-db05-467e-8757-72f6faeb13d5" | |
CHAR1_UUID = "08590f7e-db05-467e-8757-72f6faeb1306" | |
CHAR2_UUID = "08590f7e-db05-467e-8757-72f6faeb1316" | |
CHAR3_UUID = "08590f7e-db05-467e-8757-72f6faeb13e5" | |
def char2_callback(characteristic, data: bytearray): | |
print(data) | |
async def main(args: argparse.Namespace): | |
print("scanning for 5 seconds, please wait...") | |
devices = await BleakScanner.discover( | |
return_adv=True, cb=dict(use_bdaddr=args.macos_use_bdaddr), | |
timeout=5.0 | |
) | |
# Find all devices with characteristic 08590f7e-db05-467e-8757-72f6faeb13d5 | |
# and that are from texas instruments (54:6C:0E:*:*:*) | |
devs = [] | |
for k, (d, a) in devices.items(): | |
# We want to scan for texas instruments devices: | |
# 54:6C:0E:*:*:* | |
#if not k.startswith("54:6C:0E"): | |
# continue | |
# We are also trying to identify services with UUID | |
# 08590f7e-db05-467e-8757-72f6faeb13d5 | |
if SERVICE_UUID not in a.service_uuids: | |
continue | |
devs.append((d,a)) | |
# Sort by largest rssi | |
devs = sorted(devs, key = lambda v: v[0].rssi) | |
# If there are no devices, exit | |
if len(devs) < 1: | |
print("No V5 Brains Found") | |
return | |
# Get the first device | |
device = devs[0] | |
async with BleakClient(device[0]) as client: | |
# Read CHAR3. It should return 0xdeadface (big endian) | |
magic = await client.read_gatt_char(CHAR3_UUID) | |
# If not, drop the device | |
if int.from_bytes(magic, "big") != 0xdeadface: | |
print("No V5 Devices Found") | |
return | |
# Now, write 0xffffffff to CHAR3 | |
await client.write_gatt_char(CHAR3_UUID, bytes([0xff, 0xff, 0xff, 0xff])) | |
out = str() | |
while True: | |
# At this point, the brain will show a message with a number. The number needs to be read | |
# from the user as a string, and each digit converted into a byte and send as a payload of four bytes | |
confirm = input("Enter Code On Brain's Screen > ") | |
if len(confirm) != 4 or not confirm.isnumeric(): | |
print("Code must be four numeric characters") | |
continue | |
out = confirm | |
break | |
if len(confirm) != 4 or not confirm.isnumeric(): | |
print("Internal error") | |
exit() | |
# Convert to bytes | |
confirm_bytes = bytes(int(c) for c in out) | |
# Write these bytes to CHAR3 | |
await client.write_gatt_char(CHAR3_UUID, confirm_bytes) | |
# Now read from CHAR3 until we get the confirm bytes back | |
cresp = bytes([]) | |
while cresp != confirm_bytes: | |
cresp = await client.read_gatt_char(CHAR3_UUID) | |
print("Confirmation Complete") | |
# Now CHAR3 can be communicated with using regular V5 serial commands! | |
# Make sure to respect MTU and everything else should work just like a serial port. | |
# VEXCode sets up notifications for characteristics 1 and 2 | |
# Char2 is the user port. Char3 is the system port. | |
await client.start_notify(CHAR2_UUID, char2_callback) | |
while True: | |
await asyncio.sleep(1.0) | |
if __name__ == "__main__": | |
parser = argparse.ArgumentParser() | |
parser.add_argument( | |
"--macos-use-bdaddr", | |
action="store_true", | |
help="when true use Bluetooth address instead of UUID on macOS", | |
) | |
args = parser.parse_args() | |
asyncio.run(main(args)) |
The actual handshake after connecting via BLE is actually very simple:
When you read CHAR3, it should return 0xdeadface (in big-endian) signifying that it is ready for you to complete a handshake.
Writing 0xffffffff to this port displays the handshake number on the v5's screen.
Take each digit of this handshake number and split it into four bytes, so that 1234 becomes 0x01, 0x02, 0x03, 0x04.
Write it to CHAR3, and then try to read them back. If you read back the same bytes, then CHAR3 and CHAR2 are unlocked, becoming the system and user port respectively.
There are way more mac addresses the brain can have. For example, mine started with 00:81:F9
. Here is a full list of addresses:
https://hwaddress.com/company/texas-instruments/
Might just be easier to check the names of each device instead. The brain has this name: VEX_V5
Hmm, interesting. I wonder if it has anything to do with what batch they were in, because all of the brains that I tested on had the same prefix on the mac address, but they were all built at around the same time. The mac address part is not really needed, as it really checks for the Service UUID. It just speeds up discovery a little because it does not require directly requesting information from the Bluetooth device (from what I can tell). I'll remove it for now, and once I can work with a brain again will add the stuff for checking the name.
https://kb.vex.com/hc/en-us/articles/360044904552-Configuring-the-V5-Brain-for-Coding-over-Bluetooth pretty much just follow these steps until the vexcode part, and then run the script.
It requires the bleak library. https://pypi.org/project/bleak/