-
Notifications
You must be signed in to change notification settings - Fork 1.3k
nRF52: BLE Python Helper Classes #586
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Are we talking about Python classes here? Are we following the bluepy (http://ianharvey.github.io/bluepy-doc/) API? I would like to help but need to decide on the API first. |
In the future we could also look into adding HID and ANCS support, they are quite useful. |
Yeah, the goal is ideally something sitting on top of bluepy (for portability reasons), and I'm talking about Python helper classes, I'll clarify that in the subject line. The current bluepy API assumes a reasonable amount of knowledge at the packet level and about BLE, and helper classes can make working with things like indicate/notify and advertising properly much less error prone for end users. |
@arturo182 We already have helpers for a lot of useful services like ANCS for Arduino ... the goal is of course migrating similar helpers over to Python, though the API will obviously be different on a different platform and language: https://github.com/adafruit/Adafruit_nRF52_Arduino/tree/master/libraries/Bluefruit52Lib/src |
If we want to only support s132 and s140, does that mean we drop support for s110, s120 and s130? I see there are many defines for those SDs in the ble driver and I'm not sure how well those have been tested, does CP even run on those 16K/32K RAM nRF51 devices? Maybe we should remove the board and linker files for the nRF51 family and only support nRF52. And another question, is the pca10040 supported? Because I couldn't build it, was missing a board.c file, how did you test the ble code when developing,w as it only on the feather52? |
I think we should also have an issue for more advanced BLE features like security, bonding (and we need somewhere to keep the IRK), advertising whitelists, and connection interval management (important for power efficiency). |
Personally, S132 and S140 are the only targets that I think have a lot of value moving forward. The older devices are too small to be useful. As part of the next PR, I was actually planning on removing a lot of boards, and focus on the micro:bit, feather52 and PCA10056 boards exclusively, or anything similar enough to those to easily maintain without stretching ourselves too thin. The combination of S132/S140 and nRF52832 (32+512KB) or nRF52840 are all that really make sense to me. |
That sounds good, If we remove the older SDs from the ble driver i think it will become way cleaner and since s132 and s140 SDK is fairly similar, there won't be that many ifdefs. I think it's worth leaving the pca10040 in, but remove all the nRF51 boards. Let me know how I can help, don't want to step on your toes. |
It sounds like the bluepy API will work to build the higher level helpers on top of. The current Here is an example of a difference: bluepy vs ubluepy Here is an example of a Python property in C. It would be awesome to introduce a |
For me right now the blocker on this issue (in addition to some time limitations) is agreeing on a API so we can continue BLE development. I know we want a API that would easily port to RPi and similar. So to continue the conversation, I recently found a new library that has some potential, https://github.com/TheCellule/python-bleson It aims to work on Linux, Mac and Windows, there is some mention of them working on a MicroPython port but not sure what's the status on that, TheCellule/python-bleson@cc5d4f8 is best I could find It seems there has been no work for 2 months but I think it offers a good starting point if we wanted to either help them make it work on RPi or fork it. I really like their example code, even though it does not work (classes are not implemented yet), the idea they present is very nice, for example: https://github.com/TheCellule/python-bleson/blob/master/docs/examples.rst#peripheral-example (I really like this API idea, can't get any simpler) Thoughts? |
I'm a little wary of using callbacks because it is a form of concurrency. Overall I like the API though. In general, lets not get too hung up on the API. We can evolve it as we learn what we need. Remember we can potentially have two or more APIs: 1) a low level API that is the minimum the C needs to expose and 2) a user-friendly API (or more) that are for specific use cases. |
Ok then, I will try to get something going then and we'll see how it will end up looking :) |
Awesome! Thank you!
…On Fri, Apr 13, 2018 at 3:04 PM arturo182 ***@***.***> wrote:
Ok then, I will try to get something going then and we'll see how it will
end up looking :)
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#586 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AADNqYi9vs5UqA_vJpt3eMg-EIangmHnks5toSDtgaJpZM4R7JqJ>
.
|
Ok, here is a sketch of what we could do. The idea is that the same object is used on the client and server. It runs in CPython as-is (actual ble is faked). import collections
import struct
# in C
# use this to instantiate unknown devices with known services
all_services = {}
def register_service(cls):
all_services[cls.uuid] = (cls.field_name, cls)
return cls
# use this to instantiate known devices
all_devices = {}
def register_device(cls):
all_services[cls.pnp_id] = cls
return cls
# in C
class UUID:
def __init__(self, value):
self._value = value
class Characteristic:
pass
class StaticCharacteristic(Characteristic):
def __init__(self, starting_data=None):
self._data = starting_data
@property
def raw_data(self):
return self._data
@raw_data.setter
def raw_data(self, value):
self._data = value
# if notify or indicate is possible then act like a queue
class DynamicCharacteristic(Characteristic):
def __init__(self, *, indicate=False):
self._indicate = indicate
self._enabled = False
def get(self):
return None
def put(self, value):
# internal stuff
pass
@property
def enabled(self):
return self._enabled
def enabled(self, value):
self._enabled = value
# in C
class Service:
def __init__(self, device):
self._device = device
class Device:
def __init__(self, id=None):
self.id = id
self.peripheral = id == None
@property
def connected(self):
# c state
return True
# in python lib
class Uint8Characteristic(StaticCharacteristic):
def __init__(self, *, min_value=0, max_value=255):
self._min_value = min_value
self._max_value = max_value
def __get__(self, obj, type=None):
# unpack from the wire format
print("get", self)
return self._max_value
def __set__(self, obj, value):
if not self._min_value <= value <= self._max_value:
raise ValueError("out of range")
# pack to the wire format
print("set", self)
class StaticStruct(StaticCharacteristic):
def __init__(self, value_type, struct_format):
super().__init__()
self._value_type = value_type
self._struct_format = struct_format
def __get__(self, obj, type=None):
# unpack from the wire format
print("get", self)
return self._value_type._make(struct.unpack(self._struct_format, self.raw_data))
def __set__(self, obj, value):
# pack to the wire format
print("set", self)
self.raw_data = struct.pack(self._struct_format, *value)
PnPId = collections.namedtuple("PnPId", ("vendor_id_source", "vendor_id", "product_id", "product_version"))
class PnPIdCharacteristic(StaticStruct):
def __init__(self):
super().__init__(PnPId, "BHHH")
def __set__(self, obj, value):
if not 1 <= value.vendor_id_source <= 2:
raise ValueError("out of range")
super().__set__(obj, value)
# in python lib
class BatteryLevel(Uint8Characteristic):
uuid = UUID(0x2A19)
def __init__(self):
super().__init__(min_value=0, max_value=100)
# in python lib
@register_service
class BatteryService(Service):
uuid = UUID(0x180f)
field_name = "battery"
level = BatteryLevel()
@register_service
class DeviceInformation(Service):
uuid = UUID(0x180a)
field_name = "device"
pnp_id = PnPIdCharacteristic()
# in python user code
@register_device
class MyDevice(Device):
pnp_id=PnPId(2, 0x1234, 0x4567, 1)
def __init__(self, id=None):
super().__init__(id)
self.battery = BatteryService(self)
self.device = DeviceInformation(self)
# set our default state
if self.peripheral:
self.device.pnp_id=MyDevice.pnp_id
print(all_services)
d = MyDevice()
print(d.battery.level)
d.battery.level = 90 |
@dhalbert Has done a bunch of work on this with these libraries:
More will come but this is good for now. |
This makes Advertisement and Service definitions declarative by factoring out parsing logic out into shareable descriptor classes similar to how the Register library works. This also introduces SmartAdapter and SmartConnection which will auto-create the correct Advertisements and Services without requiring any direct use of UUIDs. Instead, classes are used to identify relevant objects to "recognize". This requires adafruit/circuitpython#2236 and relates to adafruit/circuitpython#586.
This PR refines the _bleio API. It was originally motivated by the addition of a new CircuitPython service that enables reading and modifying files on the device. Moving the BLE lifecycle outside of the VM motivated a number of changes to remove heap allocations in some APIs. It also motivated unifying connection initiation to the Adapter class rather than the Central and Peripheral classes which have been removed. Adapter now handles the GAP portion of BLE including advertising, which has moved but is largely unchanged, and scanning, which has been enhanced to return an iterator of filtered results. Once a connection is created (either by us (aka Central) or a remote device (aka Peripheral)) it is represented by a new Connection class. This class knows the current connection state and can discover and instantiate remote Services along with their Characteristics and Descriptors. Relates to #586
Uh oh!
There was an error while loading. Please reload this page.
Easier to use helper classes on top of the current low level BLE library are required for the following:
Peripheral Mode
Core Classes
Service Wrappers
Central Mode
TBD
Requirements
These helpers classes should require no packet level knowledge from users, which is currently the case constructing advertising packets, etc.
The wrappers must work with both S132 (nRF52832 boards) and S140 (nRF52840) SD APIs.
The text was updated successfully, but these errors were encountered: