0

I want to reliably, repeatedly and extendably send a list of length 15 containing numbers 0-128 from a PC using Windows 10 64Bit to an Adafruit ItsyBitsy nRF52840 (circuitpython). A response will be sent from receiver to sender so that I can be sure the correct data was sent. I want to avoid using any time.sleep() or asyncio.sleep() delays in my code. My current code is as follows:

Sender side code:

async def run(write_value):
    # Scan for devices
    mac_addr = "XX:XX:XX:XX:XX"
    tx_charac = "X"
    rx_charac = "X"

    #devices = await discover()
    client = BleakClient(mac_addr, timeout=30)
    await client.connect()
    await client.write_gatt_char(tx_charac, bytearray(write_value))
    await asyncio.sleep(5)
    answer = await client.read_gatt_char(rx_charac)
    print(answer)
    await client.disconnect()
    del client
    return answer

answer = asyncio.run(run(x))

Receiver side code

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

ble = BLERadio()
uart = UARTService()
advertisement = ProvideServicesAdvertisement(uart)

while True:
    print("running")
    ble.start_advertising(advertisement)
    print("waiting to connect to BLE central")
    while not ble.connected:
        pass
    print("connected")
    while ble.connected:
        s = uart.read()
        uart.write(s)
        if s:
            sequence = [x for x in s]
            if len(sequence)>1:
                #Do some processing
                print(sequence)
            del s
            del sequence

Unfortunately it seems like the code is working unreliably.

Is there anything I can do to improve the reliability, repeatability and extendebility of the sending and receiving process?

Thank you in advance!

I run into the following issues:

  • First of all I always have to introduce a waiting time, in order to receive the right echo.
  • Sometimes the device does not want to reconnect anymore after a while returning the error in connect self._device_info = device.details.adv.bluetooth_address AttributeError: 'NoneType' object has no attribute 'bluetooth_address' "in connect self._device_info = device.details.adv.bluetooth_address AttributeError: 'NoneType' object has no attribute 'bluetooth_address'
  • Furthermore later on I'd like to send lists containing more than 15 elements (e.g. 100) and I'm not sure yet how exactly to transmit a large amount of data at once.

1 Answers1

0

If you want a reliable write then the write_gatt_char method has a response parameter. This doesn't return anything to the user but on the lower levels of the Bluetooth stack it does get a response to say the write was successful.

I don't have an Adafruit ItsyBitsy, but I have a different Nordic dev board that I setup an UART echo server on that reversed the values sent. This appeared to work reliably for me.

import asyncio
from bleak import BleakClient


async def run(write_value):
    mac_addr = "E1:4B:6C:22:56:F0"
    tx_charac = "6e400003-b5a3-f393-e0a9-e50e24dcca9e"
    rx_charac = "6e400002-b5a3-f393-e0a9-e50e24dcca9e"

    async with BleakClient(mac_addr, timeout=30) as client:
        await client.write_gatt_char(
            tx_charac, 
            bytearray(write_value), 
            response=True)


asyncio.run(run(b"desserts#"))

If you wanted to get a response value from the server then starting notifications on the response characteristic might be a good approach. You would still have a sleep in there but only to keep the loop alive while you wait for the response. e.g.

import asyncio
from bleak import BleakClient


async def run(write_value):
    mac_addr = "E1:4B:6C:22:56:F0"
    tx_charac = "6e400003-b5a3-f393-e0a9-e50e24dcca9e"
    rx_charac = "6e400002-b5a3-f393-e0a9-e50e24dcca9e"

    loop = asyncio.get_event_loop()
    async with BleakClient(mac_addr, timeout=30) as client:
        def response_handler(sender, data: bytes):
            print(f"Information from {sender}")
            print(f"Data returned: {data.decode('ascii')}")
            loop.create_task(client.disconnect())
        await client.start_notify(rx_charac, response_handler)
        await client.write_gatt_char(
            tx_charac,
            bytearray(write_value),
            response=True)
        while client.is_connected:
            await asyncio.sleep(0.1)

asyncio.run(run(b"desserts#"))

which gave the output:

Information from 6e400002-b5a3-f393-e0a9-e50e24dcca9e (Handle: 43): Nordic UART RX
Data returned: stressed

With reference to writing long values, you can query what is the maximum size that is supported with: https://bleak.readthedocs.io/en/latest/api/index.html#bleak.backends.characteristic.BleakGATTCharacteristic.max_write_without_response_size

This is normally 20 bytes. You would need to break your data in to 20 byte chunks to write.

ukBaz
  • 6,985
  • 2
  • 8
  • 31