#CircuitPython BLE ManufacturerData
1 messages · Page 1 of 1 (latest)
Thanks @north ember! the second github project was extremely helpful. I now understand how the Manufacture data is used and was able to get my scan code working.
This might be a little specific but I cant seem to get my ManufacturerDataFields to parse correctly now that I have the ManufactureData working. Any ideas? Here is my current code:
class SonyAdvertisement(ProvideServicesAdvertisement):
"""Sony camera ProvideServicesAdvertisement subclass."""
# Defines a key to match this class with scanned ProvideServicesAdvertisement
# Matches Sony Manufacture Code (0x2D, 0x01) and Device Code (camera = 0x03, 0x00) in little endian
match_prefixes = (struct.pack(">BBBBB", 0xFF, 0x2D, 0x01, 0x03, 0x00),)
name = None
"""Human readable name, populated later with the data from an Advertisement."""
# Defines the manufacture_data structure based on the provided Compand ID. Necessary for ManufacturerDataField parsing.
manufacturer_data = LazyObjectField(
ManufacturerData,
"manufacturer_data",
advertising_data_type = 0xFF,
company_id = 0x2D01,
key_encoding = ">B",
)
pairing_status = ManufacturerDataField(0x22, ">H")
"""Pairing status of the device."""
def ScanForCameras():
logger.info("Scanning")
devices = {}
for advertisement in ble.start_scan(SonyAdvertisement, Advertisement, timeout=5):
if isinstance(advertisement, SonyAdvertisement):
if advertisement.address not in devices:
devices[advertisement.address] = advertisement
logger.debug(f"Found: {advertisement}")
elif isinstance(advertisement, Advertisement):
if advertisement.address in devices and devices[advertisement.address].name is None:
devices[advertisement.address].name = advertisement.complete_name
logger.debug(f"{advertisement.complete_name} is at {advertisement.address}")
else:
logger.warning(f"Unknown advertisement type: {type(advertisement)}")
for address, advertisement in devices.items():
logger.info(f"Found {advertisement.name} at {address}, status: {advertisement.pairing_status}")
Its a bit hard to read but here is my code output:
70937.5: INFO - Scanning
70937.6: DEBUG - Found: <SonyAdvertisement manufacturer_data=<ManufacturerData company_id=2d01 data=03 00 65 00 0f 33 22 bb 00 21 60 00 23 b7 0c 00 00 00 00 00 > services=<BoundServiceList: UUID(0x1800)> flags=<AdvertisingFlags limited_discovery general_discovery le_only > >
70937.6: DEBUG - Jingleheimer-A7M4 is at <Address d0:40:ef:e0:ff:ba>
70942.5: INFO - Found Jingleheimer-A7M4 at <Address d0:40:ef:e0:ff:ba>, status: None
I can see the key 0x22 I want in the ManufacturerData but for some reason, status in the last line (which is my ManufactuererDataField) is still coming back as None
you are using big-endian indicators > in a lot of places where I usually see little endian indicators <. Are you sure that's right?
one way to debug this is to get the .py version of the adafruit_ble library and put some print statements in various places
The endian indicators are on purpose to match the guides I am following that reverse engineered the data.
Interesting idea, so essentially drop some debug into the library to see whats going on. Ill give that a shot. Thanks!
@north ember thanks again for your help so far, I am slowly but surely making progress I think. I've hit another roadblock though and wonder if you might have some wisdom. I've got my app using the BLE library to sending commands to a WRITE Characteristic as the Central. However, I'm struggling to get data back from that same Service via a NOTIFY Characteristic.
Digging through the BLE and BLEIO libraries I noticed the BLE wrapper library does not expose the ability to enable notifications via the CCCD function BLEIO exposes (https://circuitpython.readthedocs.io/en/latest/shared-bindings/_bleio/index.html#bleio.Characteristic.set_cccd) Am I missing something? Additionally, I'm not seeing a good way to set a callback in the BLE library for Notify characteristics like you can in the Arduino implementation, maybe the BLE lib is missing this whole feature?
you can set properties=Characteristic.NOTIFY. Examples are the BatteryService in __init__.py and see adafruit_ble/characteristics/stream.py as well.
Yea I used the BatteryService as a template for creating my Service class implementation, so I'm glad I stumbled on that. Maybe I am just accessing the Notify data improperly?.... Here is what I have for my service class and how I'm using the connection:
class CameraRemoteControlService(Service):
"""Provides a Service to control the camera with a small subset of features."""
SHUTTER_RELEASED = 0x0601
PRESS_TO_FOCUS = 0x0701
HOLD_FOCUS = 0x0801
TAKE_PICTURE = 0x0901
uuid = VendorUUID("8000FF00-FF00-FFFF-FFFF-FFFFFFFFFFFF")
RemoteNotify = StructCharacteristic(
"<BBB",
properties=Characteristic.NOTIFY,
uuid=StandardUUID(0xFF02),
)
RemoteCommand = Uint32Characteristic(
properties=Characteristic.WRITE,
uuid=StandardUUID(0xFF01),
)
# Logic in here that scans and establishes the connection omitted for cleanliness....
## Attempt to use one of the provided device services
if CameraRemoteControlService in connection:
logger.debug("Found service!")
connection[CameraRemoteControlService].RemoteCommand = CameraRemoteControlService.PRESS_TO_FOCUS
time.sleep(1.5)
print(connection[CameraRemoteControlService].RemoteNotify) # Is this right for accessing the notify data?
else:
logger.debug("No service found...")
print(connection)
What I get is the following error from accessing RemoteNotify:
160049.0: DEBUG - Found service!
Traceback (most recent call last):
File "code.py", line 159, in <module>
File "code.py", line 149, in ScanForCameras
File "adafruit_ble/characteristics/__init__.py", line 246, in __get__
File "adafruit_ble/characteristics/__init__.py", line 142, in __get__
_bleio.BluetoothError: Unknown gatt error: 0x0102
Interestingly, the BatterService is both READ and NOTIFY. The Characteristic im attempting to get is only NOTIFY according to the nRF Connect app I used to get a little more insight. Maybe that is why I cant read it via the Connection?
Are you providing the CameraRemoteControlService or is it a service that is remote to you? Are you acting as a central or a peripheral?
I am acting as a central. the CameraRemoteControlService is remote to me
Here's a bit of info I scanned on the service Im trying to use:
I can see the data im interested (three bytes) via the app when I enable the Notification for the NOTIFY Characteristic. Needing to enable those notifications is what led to my question about setting the CCCD in BLEIO
Take a look at this service code, which defines a service that is a remote NOTIFY-only characteristic that reutrns temperature and humidity. Note that it uses a PacketBuffer to store the incoming notifications. We read the data from the packet buffer, not by accessing the characteristic directly. https://github.com/adafruit/Adafruit_CircuitPython_BLE_LYWSD03MMC/blob/main/adafruit_ble_lywsd03mmc.py
note the bind(), which has a set_cccd() in it, and then temperature_humidity(), which reads from the _readings_buf into data, and then checks the length. The _readings_buf is a one-element buffer, so it doesn't keep more than one reading at a time.
this is all quite complicated, sorry, and not well documented