adafruit / circuitpython

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

WebBluetooth broken on CircuitPlayground Bluefruit 7.0.0 #5554

Closed Parakoos closed 2 years ago

Parakoos commented 2 years ago

CircuitPython version

Adafruit CircuitPython 7.0.0 on 2021-09-20; Adafruit Circuit Playground Bluefruit with nRF52840
Board ID:circuitplayground_bluefruit

Code/REPL

DEVICE_NAME = "Playground / ItsyBitsy" # change as required

import board
import digitalio
import time

from adafruit_ble import BLERadio
from adafruit_ble.advertising.standard import ProvideServicesAdvertisement
from adafruit_ble.services.nordic import UARTService

led = digitalio.DigitalInOut(board.LED)
led.direction = digitalio.Direction.OUTPUT

# Bluetooth Setup
ble = BLERadio()
ble.name = DEVICE_NAME
uart = UARTService()
advertisement = ProvideServicesAdvertisement(uart)

while True:
    ble.start_advertising(advertisement)
    print("Waiting to connect")
    while not ble.connected:
        pass
    ble.stop_advertising()
    print("Connected")
    uart.write("Connected".encode("utf-8"))

    while ble.connected:
        if uart.in_waiting:
            # Read the line, print it, then write it back
            s = uart.readline()
            led.value = True
            string = str(s, 'utf-8')
            print(string)
            uart.write(string.encode("utf-8"))
            time.sleep(1)
            led.value = False

Behavior

All is OK on the board, but the following Javascript will fail. If I switch over to 6.3.0 (uf2 file to bootloader as well as swap the adafruit_ble to corresponding version) then it works without a problem.


try {
    var serviceUUID = '6e400001-b5a3-f393-e0a9-e50e24dcca9e'
    var characteristicUUIDwrite = '6e400002-b5a3-f393-e0a9-e50e24dcca9e'
    var characteristicUUIDread = '6e400003-b5a3-f393-e0a9-e50e24dcca9e'
    var value = 'Hello world!'
    const device = await navigator.bluetooth.requestDevice({
        filters: [{ services: [serviceUUID] }],
    })
    const server = await device.gatt.connect()
    const service = await server.getPrimaryService(serviceUUID)
    const chars = await service.getCharacteristics()
    const characteristicWrite = await service.getCharacteristic(characteristicUUIDwrite)
    const characteristicRead = await service.getCharacteristic(characteristicUUIDread)

    if (!characteristicWrite.properties.writeWithoutResponse)
        throw new Error(characteristicUUIDwrite + ' not mapped to a writeable characteristic.')
    if (!characteristicRead.properties.notify)
        throw new Error(characteristicRead + ' not mapped to a notifyable characteristic.')

    characteristicRead.addEventListener('characteristicvaluechanged', () => {
        console.log("Response: " + new TextDecoder().decode(characteristicRead.value))
    })
    console.log('Event Listener added')
    await characteristicRead.startNotifications()
    console.log('Notifications started')

    const valueWithNewLine = 'Hello world!\n'
    const encoder = new TextEncoder()
    const encodedValue = encoder.encode(valueWithNewLine)
    console.log({ value: valueWithNewLine, encodedValue })
    await characteristicWrite.writeValueWithoutResponse(encodedValue)
    console.info('Success!')
} catch (error) {
    console.error('output', { code: error.code, message: error.message, stack: error.stack })
    console.error(error)
}

The error thrown is on the line where you start the notifications (await characteristicRead.startNotifications()) and what is caught is as follows: {code: 9, message: 'GATT operation not permitted.'}

Description

I have run the exact same javascript and code.py in three different situations:

All the code (javascript and code.py) remains identical in all three situation. The only thing that changes is the version of Circuit Python and the adafruit_ble library (7.0.0 vs 6.3.0) . It works on the 6.3.0 Circuit Playground Bluefruit and 7.0.0 itsybitsy, but if fails on 7.0.0 Circuit Playground Bluefruit .

Additional information

You can run the javascript in the dev console on any screen.

Parakoos commented 2 years ago

Two more pieces of info. First is that the Adafruit Connect app's UART functionality seem to not be affected by this issue.

Second is that if you comment out the line with characteristicRead.startNotifications() then the code will fail at the write, characteristicWrite.writeValueWithoutResponse(encodedValue).

Parakoos commented 2 years ago

I had an idea and went through Windows, removing all mentions of the Circuit Playground as a saved Bluetooth device, upgraded it to v7.0 and tried the code above. It worked.

I am going to close this issue now as I'm not sure I can reproduce it anymore. There may be something weird going on here with upgrading a board from 6.3 to 7.0 that may trip people up.

tannewt commented 2 years ago

Thanks for the update! CircuitPython's BLE internals changed with 7.0.0 that may require erasing the bonding info. Glad you got it working.