tralamazza / micropython

MicroPython - a lean and efficient Python implementation for microcontrollers and constrained systems
MIT License
29 stars 9 forks source link

bluetooth: ubluepy peripheral 4 byte limit? #85

Closed xmeow closed 7 years ago

xmeow commented 7 years ago

When I try to start a peripheral with modubluepy, I found that any write character accept no more than 4 byte char. PS: I use a PCA10040 devboard and LightBlue app to go through the test.

glennrub commented 7 years ago

This is a bit strange. In most cases you should be able to write up to 20 bytes by default on all characteristics (MTU size - some BLE heading). Could it be the application you are testing with that has this limitation?

These are my test results on the pca10040:

MicroPython code:

from ubluepy import Service, Characteristic, UUID, Peripheral, constants
from pyb import LED
def event_handler(id, handle, data):
    if id == constants.EVT_GAP_CONNECTED:
        # connected
        LED(2).on()
    elif id == constants.EVT_GAP_DISCONNECTED:
        # disconnect
        LED(2).off()
    elif id == constants.EVT_GATTS_WRITE:
        print("Write, len: %d, data:", len(data), data)
    else:
        print("BLE event:", id, "handle:", handle)
s = Service(UUID(0xABCD))
c0 = Characteristic(UUID(0x1234), props = Characteristic.PROP_WRITE | Characteristic.PROP_WRITE_WO_RESP)
s.addCharacteristic(c0)
p = Peripheral()
p.addService(s)
p.setConnectionHandler(event_handler)
p.advertise(device_name="test_char", services=[s])

Linux gatttool command trace:

$ gatttool -b 'FE:70:57:3B:04:50' -t random --interactive

[FE:70:57:3B:04:50][LE]> connect
Attempting to connect to FE:70:57:3B:04:50
Connection successful
[FE:70:57:3B:04:50][LE]> characteristics
handle: 0x0002, char properties: 0x0a, char value handle: 0x0003, uuid: 00002a00-0000-1000-8000-00805f9b34fb
handle: 0x0004, char properties: 0x02, char value handle: 0x0005, uuid: 00002a01-0000-1000-8000-00805f9b34fb
handle: 0x0006, char properties: 0x02, char value handle: 0x0007, uuid: 00002a04-0000-1000-8000-00805f9b34fb
handle: 0x0008, char properties: 0x02, char value handle: 0x0009, uuid: 00002aa6-0000-1000-8000-00805f9b34fb
handle: 0x000c, char properties: 0x0c, char value handle: 0x000d, uuid: 00001234-0000-1000-8000-00805f9b34fb
[FE:70:57:3B:04:50][LE]> char-write-req 0x000d 7468697320697320612074657374
Characteristic value was written successfully
[FE:70:57:3B:04:50][LE]>

As a result the ubluepy event handler gets a write request with the > 4 bytes test string:

>>> Write, len: %d, data: 14 bytearray(b'this is a test')

There is a hidden setting which i have added to get some more verbose logs in the bluetooth driver (only working if you are using UART REPL, and not BLE REPL). https://github.com/tralamazza/micropython/blob/master/nrf/drivers/bluetooth/ble_drv.c#L41 Could you flip this define to 1 and see if you get any traces which could give a hint on why there is a 4-byte limitation for you?

xmeow commented 7 years ago

Thank you for quick reply, I make a mistake that write char only accept 2 byte instead of 4. Here is the code I use, modified from modublupy header. A successful write will trig the EVT_GATTS_WRITE event. I don't have a linux device with ble support, and not sure if this is an issue of iOS or LightBlue app.

from ubluepy import Service, Characteristic, UUID, Peripheral, constants

def event_handler(id, handle, data):
    #print("BLE event:", id, "handle:", handle)
    #print(data)
    global p
    global s
    if id == constants.EVT_GAP_CONNECTED:
        print("connected")
    elif id == constants.EVT_GAP_DISCONNECTED:
        print("diconnected")
        # restart advertisment
        p.advertise(device_name="micr", services=[s])
    elif id == constants.EVT_GATTS_WRITE:
        print("Write, len: %d, data:", len(data), data)

# u0 = UUID("0x180D") # HRM service
# u1 = UUID("0x2A37") # HRM measurement

u0 = UUID("6e400001-b5a3-f393-e0a9-e50e24dcca9e")
u1 = UUID("6e400002-b5a3-f393-e0a9-e50e24dcca9e")
u2 = UUID("6e400003-b5a3-f393-e0a9-e50e24dcca9e")
s = Service(u0)
c0 = Characteristic(u1, props = Characteristic.PROP_WRITE | Characteristic.PROP_WRITE_WO_RESP)
c1 = Characteristic(u2, props = Characteristic.PROP_NOTIFY, attrs = Characteristic.ATTR_CCCD)
s.addCharacteristic(c0)
s.addCharacteristic(c1)
p = Peripheral()
p.addService(s)
p.setConnectionHandler(event_handler)
p.advertise(device_name="micr", services=[s])

And here is debug output with BLE_DRIVER_VERBOSE set to 1.

import pyboard
pyboard.execfile('ubluepy_test.py', device='COM9')
------  start of ublupy -------
Is enabled status: 0
SoftDevice enable status: 0
IRQ enable status: 0
BLE ram size: 9824
BLE enable status: 0
Is enabled status: 0
Is enabled status: 0
Is enabled status: 0
Is enabled status: 0
Device name applied
encoded uuid for service 0: 9e ca dc 24 0e e5 a9 e0 93 f3 a3 b5 01 00 40 6e 
ADV: uuid size: 16, type: 20, uuid: 1, vs_idx: 2
Set Adv data size: 27
------ here I connect with my phone -----
------ and a successful write 0x1234------
GAP CONNECT
connected
GAP CONN PARAM UPDATE
GATTS EVT EXCHANGE MTU REQUEST
GATTS write
Write, len: %d, data: 2 bytearray(b'\x124')
------ if write value longer than 2 byte nothing output --------
glennrub commented 7 years ago

2 bytes sounds like you might be writing the CCCD descriptor of the notification characteristic.

In this case of the ubluepy header example i have swapped the UUID of TX and RX as there was a bug in Nordic SDK naming of the characteristics and implementation (fixed in latest SDK). If the phone/tablet applications have handle values hardcoded, and do not perform a service discovery, it might trigger a write to the wrong handle.

I believe this is the correct UUID's: RX Characteristic (UUID: 6E400002-B5A3-F393-E0A9-E50E24DCCA9E) TX Characteristic (UUID: 6E400003-B5A3-F393-E0A9-E50E24DCCA9E)

You can do one easy test, to swap the UUID's between the two characteristics and see if the app works better.

xmeow commented 7 years ago

Awesome, after swape the uuid everything works as expected. Thank you so much~