ubluepy --- طرفية ومركز Bluetooth LE#

وحدة ubluepy هي واجهة برمجة Bluetooth LE القديمة المرفقة مع منفذ nRF من MicroPython. وهي مصممة بشكل فضفاض على غرار مكتبة Python المسماة bluepy وتعمل مباشرة فوق Nordic SoftDevice --- لا يوجد خلفية قابلة للنقل، لذا فإن الوحدة متاحة فقط على أهداف Nordic (وهي Arduino Nano 33 BLE Sense ضمن تشكيلة OpenMV). أما واجهات bluetooth / aioble الأحدث فهي غير مفعّلة في هذا الإصدار، لذا فإن ubluepy هي الطريقة الوحيدة لتشغيل الراديو المدمج في الشريحة.

تعتمد مجموعة الميزات المتاحة على SoftDevice المُبرمج في الذاكرة بواسطة البرنامج الثابت:

  • s140 (Nano 33 BLE Sense) --- دورا الطرفية والمركز (الماسح) معًا. هذا ما يأتي به برنامج OpenMV الثابت.

  • s132 --- دورا الطرفية والمركز معًا.

  • s110 --- الطرفية فقط؛ طرائق الماسح / الاتصال مستبعدة من التجميع.

مثال على الطرفية#

الإعلان كطرفية Bluetooth LE بخدمة استشعار بيئي واحدة وإرسال إشعار بخاصية درجة الحرارة عند كل كتابة إلى واصف تكوين خاصية العميل (CCCD):

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

notif_enabled = False

def event_handler(event_id, handle, data):
    global notif_enabled
    if event_id == constants.EVT_GAP_CONNECTED:
        LED("LED_GREEN").on()
    elif event_id == constants.EVT_GAP_DISCONNECTED:
        LED("LED_GREEN").off()
        periph.advertise(device_name="Nano 33", services=[svc])
    elif event_id == constants.EVT_GATTS_WRITE:
        notif_enabled = bool(data[0])

svc = Service(UUID("181A"))            # Environmental Sensing
char = Characteristic(UUID("2A6E"),
                      props=Characteristic.PROP_NOTIFY | Characteristic.PROP_READ,
                      attrs=Characteristic.ATTR_CCCD)
svc.addCharacteristic(char)

periph = Peripheral()
periph.addService(svc)
periph.setConnectionHandler(event_handler)
periph.advertise(device_name="Nano 33", services=[svc])

مثال على المركز#

المسح بحثًا عن الأجهزة المُعلِنة القريبة لمدة 100 مللي ثانية وفك ترميز بيانات الإعلان لكل ScanEntry

from ubluepy import Scanner, constants

s = Scanner()
for entry in s.scan(100):
    print(entry.addr(), entry.rssi(), "dBm")
    for ad_type, name, value in entry.getScanData():
        print(" ", ad_type, name, bytes(value))

محتويات الوحدة#

الأصناف#

class ubluepy.UUID(value)#

إنشاء معرّف UUID لبلوتوث بطول 16 أو 128 بت.

value

أحد القيم التالية:

  • int --- معرّف UUID رقمي بطول 16 بت (UUID(0x180A)).

  • سلسلة نصية من 6 أحرف بصيغة "0xXXXX" --- معرّف UUID بطول 16 بت، مثل UUID("0x181A").

  • سلسلة نصية من 36 حرفًا بصيغة "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" --- معرّف UUID كامل بطول 128 بت. يُسجَّل الجزء الخاص بالمورّد لدى SoftDevice عند الإنشاء.

  • نسخة UUID أخرى --- ينفّذ عملية نسخ.

أي طول آخر يثير ValueError("Invalid UUID string length").

binVal() int#

يعيد البتات الـ 16 الدنيا من معرّف UUID كقيمة int. بالنسبة لمعرّفات UUID بطول 128 بت يُعاد فقط حقل الـ 16 بت المضمّن داخل معرّف UUID الخاص بالمورّد (القيمة الكاملة بطول 128 بت يمكن الوصول إليها فقط عبر فهرس المورّد في SoftDevice).

class ubluepy.Service(uuid: UUID, type: int = Service.PRIMARY)#

تعريف خدمة GATT سيتم تسجيلها لدى SoftDevice عند إضافتها إلى Peripheral.

uuid

نسخة UUID. تمرير كائن ليس من نوع UUID يثير ValueError.

type

إما Service.PRIMARY (الافتراضي) أو Service.SECONDARY. القيم الأخرى تثير ValueError.

uuid() UUID#

يعيد نسخة UUID الخاصة بالخدمة.

addCharacteristic(characteristic: Characteristic) None#

تسجيل Characteristic مع الخدمة. يُسنَد مقبض GATT الخاص بالخاصية أثناء هذا الاستدعاء.

getCharacteristic(uuid: UUID) Characteristic | None#

البحث عن Characteristic سبقت إضافتها باستخدام UUID. يعيد نسخة الخاصية، أو None إذا لم يُعثر على تطابق.

getCharacteristics() list#

يعيد قائمة بكل خاصية أُضيفت إلى الخدمة.

PRIMARY: int#

ثابت نوع الخدمة للخدمات الأساسية (1).

SECONDARY: int#

ثابت نوع الخدمة للخدمات الثانوية (2).

class ubluepy.Characteristic(uuid: UUID, *, props: int = PROP_READ | PROP_WRITE, attrs: int = 0)#

تعريف خاصية GATT. أضفها إلى Service باستخدام Service.addCharacteristic() قبل أن تبدأ Peripheral الأم في الإعلان.

uuid

نسخة UUID.

props (وسيط مسمى فقط)

قناع بتي لقيمة أو أكثر من قيم Characteristic.PROP_* تصف العمليات التي تدعمها الخاصية.

attrs (وسيط مسمى فقط)

قناع بتي لسمات GATT إضافية. استخدم Characteristic.ATTR_CCCD لإرفاق واصف تكوين خاصية العميل --- وهو مطلوب لتشغيل خصائص PROP_NOTIFY / PROP_INDICATE.

uuid() UUID#

يعيد نسخة UUID الخاصة بالخاصية.

properties() int#

يعيد قناع البتات props المُعيّن وقت الإنشاء.

read() bytearray#

دور المركز فقط. قراءة قيمة الخاصية من النظير المتصل. يعيد bytearray يحتوي على آخر قيمة. على الطرفية لا تُجري أي عملية وتعيد None.

write(data, *, with_response: bool = False) None#

الكتابة إلى الخاصية.

  • على الطرفية، إذا كانت PROP_NOTIFY مضبوطة في خصائص الخاصية فإن القيمة تُرسَل كإشعار GATT إلى المركز المتصل؛ وإلا تُحدَّث قيمة السمة المحلية.

  • على المركز، تُكتب القيمة إلى النظير البعيد. اضبط with_response=True لإصدار طلب كتابة وانتظار إقرار النظير بدلًا من أمر كتابة.

data هو أي كائن يدعم بروتوكول المخزن المؤقت (bytes أو bytearray أو memoryview).

PROP_BROADCAST: int#

يمكن للخاصية بث قيمتها (0x01).

PROP_READ: int#

تدعم الخاصية القراءة (0x02).

PROP_WRITE_WO_RESP: int#

تدعم الخاصية الكتابة دون استجابة (0x04).

PROP_WRITE: int#

تدعم الخاصية الكتابة مع استجابة (0x08).

PROP_NOTIFY: int#

يمكن للخاصية دفع الإشعارات إلى مركز مشترك (0x10).

PROP_INDICATE: int#

يمكن للخاصية دفع المؤشرات (إشعارات مُقَرّة) إلى مركز مشترك (0x20).

PROP_AUTH_SIGNED_WR: int#

تدعم الخاصية عمليات الكتابة الموقّعة الموثّقة (0x40).

ATTR_CCCD: int#

إضافة واصف تكوين خاصية العميل إلى الخاصية (0x01). مطلوب لتمكين العملاء من الاشتراك في الإشعارات/المؤشرات.

class ubluepy.Descriptor(uuid: UUID)#

صنف هيكلي لتمثيل واصفات GATT. لا يخزّن التنفيذ الحالي سوى UUID ولا يعرض أي طرائق --- وهو موفّر للتوافق المستقبلي مع المراجعات اللاحقة للوحدة.

class ubluepy.Peripheral#

جهاز Bluetooth LE المحلي. يُستخدم الصنف ذاته لدوري الطرفية والمركز معًا؛ ويُحدَّد الدور بحسب الطرائق التي تستدعيها (advertise() تختار الطرفية، وconnect() تختار المركز).

addService(service: Service) None#

تسجيل Service (وكل خصائصها المضافة سابقًا) لدى خادم GATT المحلي.

getServices() list#

يعيد قائمة الخدمات المسجلة حاليًا لدى Peripheral هذه.

advertise(*, device_name: str | None = None, services: list | None = None, data: bytes | None = None, connectable: bool = True) None#

بدء الإعلان في دور الطرفية.

device_name

الاسم المحلي الكامل المُعلَن في حمولة GAP.

services

قائمة بنسخ Service للإعلان عنها. يُضمَّن معرّف UUID لكل خدمة في الإعلان.

data

حمولة إعلان خام اختيارية (bytes / bytearray) تُلحق بالترويسة المولّدة تلقائيًا. استخدم هذا للحمولات الخاصة بالمورّد أو حمولات المنارات (beacon) مثل Eddystone.

connectable

عند ضبطها على True (الافتراضي)، يُعلَن عن الجهاز كجهاز قابل للاتصال وتُسجَّل معالِجات أحداث GAP / GATTS بحيث تُطلَق دالة رد النداء المُهيّأة عبر setConnectionHandler() عند الاتصال وقطع الاتصال وكتابات CCCD. وعند ضبطها على False، يُعلَن عن الجهاز كمنارة --- لا تُرفَق أي معالِجات ولا يمكن الاتصال بالجهاز.

advertise_stop() None#

إيقاف أي إعلان قيد التنفيذ.

setConnectionHandler(func) None#

تسجيل دالة رد نداء تُستدعى عند أحداث GAP وGATTS. تُستدعى دالة رد النداء بالصيغة func(event_id, conn_handle, data) حيث يكون event_id إحدى قيم constants.EVT_GAP_CONNECTED أو constants.EVT_GAP_DISCONNECTED أو constants.EVT_GATTS_WRITE، ويكون conn_handle مقبض اتصال SoftDevice (أو مقبض السمة لكتابات GATTS)، ويكون data حمولة الحدث الخام بصيغة bytearray (أو None للاتصال / قطع الاتصال).

setNotificationHandler(func) None#

تسجيل دالة رد نداء لأحداث الإشعارات المستلمة في دور المركز.

withDelegate(delegate: DefaultDelegate) None#

إرفاق نسخة DefaultDelegate لاستقبال أحداث GATT المفكوكة الترميز.

disconnect() None#

إسقاط الاتصال النشط (هو حاليًا عنصر هيكلي لا يجري أي عملية في هذا الإصدار).

connect(addr, *, addr_type: int = constants.ADDR_TYPE_PUBLIC) None#

دور المركز فقط. الاتصال بالنظير ذي العنوان المعطى واكتشاف خدماته الأساسية وخصائصه بشكل متزامن. يحجب التنفيذ حتى يُنشأ الاتصال ويكتمل الاكتشاف؛ وعندها تصبح الخدمات المكتشفة متاحة عبر getServices().

addr

عنوان النظير كسلسلة نصية من 17 حرفًا بصيغة "xx:xx:xx:xx:xx:xx" (مثلًا مأخوذ من ScanEntry.addr()).

addr_type (وسيط مسمى فقط)

إما constants.ADDR_TYPE_PUBLIC (الافتراضي) أو constants.ADDR_TYPE_RANDOM_STATIC.

class ubluepy.Scanner#

مراقِب GAP لاكتشاف الأجهزة المُعلِنة القريبة. متاح فقط عندما يُبنى البرنامج الثابت مع SoftDevice يدعم دور المركز (s132 / s140).

scan(timeout: int) list#

تشغيل مسح سلبي لمدة timeout بالمللي ثانية وإعادة قائمة بنسخ ScanEntry --- نسخة واحدة لكل تقرير إعلان مستلم خلال النافذة الزمنية.

class ubluepy.ScanEntry#

تقرير إعلان واحد التقطته Scanner.scan(). تُعاد النسخ من الماسح --- لا يوجد بانٍ عام.

addr() str#

يعيد عنوان النظير كسلسلة نصية من 17 حرفًا بصيغة "xx:xx:xx:xx:xx:xx".

addr_type() int#

يعيد نوع عنوان النظير (constants.ADDR_TYPE_PUBLIC أو constants.ADDR_TYPE_RANDOM_STATIC).

rssi() int#

يعيد مؤشر قوة الإشارة بوحدة dBm.

getScanData() list#

فك ترميز حمولة الإعلان إلى قائمة من الصفوف (ad_type, description, value). يكون ad_type بايت نوع AD الرقمي (انظر constants.ad_types)، ويكون description اسم الثابت المطابق كسلسلة نصية (أو None إذا كان النوع غير معروف)، ويكون value جسم سجل AD بصيغة bytearray.

class ubluepy.DefaultDelegate#

الصنف الأساسي للكائنات المُمرَّرة إلى Peripheral.withDelegate(). اشتق منه وأعد تعريف handleConnection() / handleNotification() للتفاعل مع أحداث GATT.

handleConnection() None#

تُستدعى عند أحداث الاتصال / قطع الاتصال في GAP. التنفيذ الافتراضي فارغ.

handleNotification() None#

تُستدعى عند ورود إشعارات GATT. التنفيذ الافتراضي فارغ.

الثوابت#

السمة constants في الوحدة هي نطاق أسماء يحتوي على معرّفات أحداث GAP/GATT، وقيم أنواع العناوين، ونطاق الأسماء المتداخل ad_types.

ubluepy.constants: type#

حاوية تعرض الثوابت أدناه.

constants.EVT_GAP_CONNECTED: int#

قيمة event_id لمعالِج اتصال Peripheral لاتصال GAP (16).

constants.EVT_GAP_DISCONNECTED: int#

قيمة event_id لمعالِج اتصال Peripheral لقطع اتصال GAP (17).

constants.EVT_GATTS_WRITE: int#

قيمة event_id لمعالِج اتصال Peripheral للكتابة إلى سمة GATT محلية، بما في ذلك الكتابات إلى CCCD التي تُمكّن/تُعطّل الإشعارات (80).

constants.UUID_CCCD: int#

معرّف UUID القياسي لبلوتوث لواصف تكوين خاصية العميل (0x2902).

constants.ADDR_TYPE_PUBLIC: int#

عنوان جهاز بلوتوث عام (0).

constants.ADDR_TYPE_RANDOM_STATIC: int#

عنوان جهاز بلوتوث عشوائي ثابت (1).

constants.ad_types: type#

نطاق أسماء لثوابت أنواع AD لبيانات الإعلان من ملحق مواصفات بلوتوث الأساسية. يرتبط كل اسم بنوع AD المقابل بطول بايت واحد:

الاسم

القيمة

AD_TYPE_FLAGS

0x01

AD_TYPE_16BIT_SERVICE_UUID_MORE_AVAILABLE

0x02

AD_TYPE_16BIT_SERVICE_UUID_COMPLETE

0x03

AD_TYPE_32BIT_SERVICE_UUID_MORE_AVAILABLE

0x04

AD_TYPE_32BIT_SERVICE_UUID_COMPLETE

0x05

AD_TYPE_128BIT_SERVICE_UUID_MORE_AVAILABLE

0x06

AD_TYPE_128BIT_SERVICE_UUID_COMPLETE

0x07

AD_TYPE_SHORT_LOCAL_NAME

0x08

AD_TYPE_COMPLETE_LOCAL_NAME

0x09

AD_TYPE_TX_POWER_LEVEL

0x0A

AD_TYPE_CLASS_OF_DEVICE

0x0D

AD_TYPE_SIMPLE_PAIRING_HASH_C

0x0E

AD_TYPE_SIMPLE_PAIRING_RANDOMIZER_R

0x0F

AD_TYPE_SECURITY_MANAGER_TK_VALUE

0x10

AD_TYPE_SECURITY_MANAGER_OOB_FLAGS

0x11

AD_TYPE_SLAVE_CONNECTION_INTERVAL_RANGE

0x12

AD_TYPE_SOLICITED_SERVICE_UUIDS_16BIT

0x14

AD_TYPE_SOLICITED_SERVICE_UUIDS_128BIT

0x15

AD_TYPE_SERVICE_DATA

0x16

AD_TYPE_PUBLIC_TARGET_ADDRESS

0x17

AD_TYPE_RANDOM_TARGET_ADDRESS

0x18

AD_TYPE_APPEARANCE

0x19

AD_TYPE_ADVERTISING_INTERVAL

0x1A

AD_TYPE_LE_BLUETOOTH_DEVICE_ADDRESS

0x1B

AD_TYPE_LE_ROLE

0x1C

AD_TYPE_SIMPLE_PAIRING_HASH_C256

0x1D

AD_TYPE_SIMPLE_PAIRING_RANDOMIZER_R256

0x1E

AD_TYPE_SERVICE_DATA_32BIT_UUID

0x20

AD_TYPE_SERVICE_DATA_128BIT_UUID

0x21

AD_TYPE_URI

0x24

AD_TYPE_3D_INFORMATION_DATA

0x3D

AD_TYPE_MANUFACTURER_SPECIFIC_DATA

0xFF