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