CoreBluetooth في الممارسة

عملي CoreBluetooth لترجمة الأجهزة الطرفية


قبل عامين ، عندما واجهت البلوتوث لأول مرة في مشروع عمل ، وجدت هذه المقالة ، التي ساعدت كثيرًا في فهم كيفية عملها ، للعثور على نقطة "البداية". أتمنى أن يكون ذلك مفيدًا للمبتدئين.

نبذة عن الكاتب: يوآف شوارتز هو مطور رائد لنظام iOS في Donkey Republic ، وهو نظام مشاركة راكب الدراجة النارية في كوبنهاغن ، يسعى لتغيير المواقف تجاه ركوب الدراجات. بعد ذلك ، سنتحدث نيابة عن المؤلف.

في هذه المقالة ، سأتحدث عن التقنيات العملية للعمل مع CoreBluetooth. أولاً ، حول تقنية Bluetooth منخفضة الطاقة (BLE) لأنه ليس الجميع على دراية بهذه التكنولوجيا ، ثم حول CoreBluetooth ، وهو إطار عمل Apple الذي يمكننا من التفاعل مع أجهزة BLE. سأخبرك أيضًا عن بعض الحيل التنموية التي تعلمتها بنفسي أثناء تصحيح الأخطاء والبكاء وتمزيق الشعر من رأسي.

بلوتوث منخفض الطاقة


بالنسبة للمبتدئين ، ما هو بليه؟ إنه يشبه نوعًا ما البلوتوث ، والذي نستخدمه جميعًا في مكبرات الصوت وسماعات الرأس وما إلى ذلك ، ولكن هناك فرق - يستهلك هذا البروتوكول القليل جدًا من الطاقة. عادةً ، يمكن أن تستمر عملية شحن واحدة للبطارية لجهاز ممكّن لاستخدام BLE لشهور أو حتى سنوات (اعتمادًا بالطبع على كيفية استخدام الجهاز). يتيح لنا ذلك القيام بأشياء لم تكن متاحة من قبل للبلوتوث "العادي". يسمى هذا المعيار Bluetooth 4.0 ، بدأ كل شيء بتقنية تسمى Smart Bluetooth ، والتي تطورت لاحقًا إلى BLE. هناك دليل من 200 صفحة ، يمكنك قراءة وقت النوم ، قراءة مثيرة.

BLE اقتصادي للغاية من حيث استهلاك الطاقة ، والبروتوكول نفسه ليس معقدًا للغاية. فلماذا بلي؟ كيف نستخدمها؟ المثال الأول والأكثر شيوعًا هو مستشعر معدل ضربات القلب. عادةً ما يقيس هذا الجهاز معدل ضربات القلب وينقله عبر البروتوكول. هناك أيضًا جميع أنواع أجهزة الاستشعار التي يمكنك الاتصال بها عبر BLE وقراءة البيانات التي تجمعها. أخيرًا ، هناك iBeacons التي يمكن أن تخبرك "بقربها" من مكان ما. بين علامتي الاقتباس لأن Apple على أجهزة iPhone تحظر القدرة على اكتشاف iBeacons كأجهزة Bluetooth عادية ، لذلك علينا العمل مع CoreLocation. بشكل عام ، هذا هو إنترنت الأشياء: يمكنك الاتصال بتلفزيون أو مكيف هواء والتواصل معه باستخدام هذا البروتوكول.

كيف يعمل؟


لدينا perifer - هذه هي الأجهزة التي تستخدم بروتوكول Bluetooth. كل طرف لديه خدمات ، يمكن أن يكون هناك أي عدد منها ، ولكل منها خصائصه. يمكنك اعتبار perifer كخادم. مع كل العواقب الناتجة: في بعض الأحيان يتم إيقاف تشغيله ، وأحيانًا يستغرق الأمر بعض الوقت لنقل البيانات ، وأحيانًا لا تأتي هذه البيانات على الإطلاق.

بشكل عام ، لدينا خدمة بخصائص عديدة ، كل منها يحتوي على قيمة ونوع وما إلى ذلك. للعمل مع CoreBluetooth ، لا تحتاج إلى معرفة كل شيء ، أهم شيء هو قراءة البيانات. هذا ما نحاول الحصول عليه أو تعديله أو استخدامه لأغراضنا الخاصة. نحن بحاجة إلى هذه البيانات ومعرفة ما يمكننا القيام به معها.

إليك مقدمة سريعة عن BLE لأن هناك الآلاف من الموارد التي تشرح الميزات التقنية أفضل مني.

بلوتوث الأساسية


تم تقديم Core Bluetooth بواسطة Apple منذ وقت طويل ، في iOS 5. بدأت Apple العمل على إدخال BLE في أجهزتها في وقت أبكر بكثير من Android والشعبية المتزايدة للتكنولوجيا. يستخدم العديد من المطورين هذا الإطار في تطبيقاتهم ، وهو بشكل عام مجرد غلاف ، نظرًا لأن بروتوكولات BLE نفسها معقدة للغاية. ليس حقًا ، ولكن صدقوني ، هذا ليس شيئًا أود العمل معه كل يوم. تمامًا مثل العديد من الأشياء الأخرى ، قامت Apple بتغليفها في حزمة جميلة ومريحة ، مما يتيح لك استخدام المصطلحات التي يمكننا جميعًا ، المطورين الغبيين فهمها.

حان دورك الآن لإخبارك بما تحتاج حقًا إلى معرفته عن الفصول الدراسية المرتبطة بالتواصل مع إطار العمل. ممثلنا الرئيسي هو CBCentralManager ، قم بإنشائه:

manager = CBCentralManager(delegate:self, queue:nil, options: nil) 

أعلاه ، أنشأنا مديرًا جديدًا ، يشير إلى مندوبه ، وإلا فلن نتمكن من استخدامه. نشير أيضًا إلى قائمة الانتظار ، في حالتنا صفر ، مما يعني أن جميع الاتصالات مع المدير ستتم في قائمة الانتظار الرئيسية.

تحتاج إلى فهم ما ستفعله بالضبط - سيؤدي استخدام قائمة انتظار منفصلة إلى تعقيد التطبيق ، ولكن ، بالطبع ، سيحبك المستخدمون أكثر. إذا كنت تخطط للتواصل مع جهاز واحد فقط ، فلا يمكنك القلق واستخدام قائمة الانتظار الرئيسية. إذا كنت لا تزال تريد التجربة ، فقم بإنشاء قائمة انتظار ، وحددها في المنشئ ولا تنس الرجوع إلى الصفحة الرئيسية قبل استخدام النتائج في مكان آخر.

خيارات لا يوجد شيء مثير للاهتمام هنا ، ربما الشيء الرئيسي - عند إنشاء مدير وإيقاف تشغيل البلوتوث للمستخدم - سيخبره التطبيق عن ذلك ، ولكن الجميع تقريبًا ينقرون على "موافق" (الذي لا يتضمن بلوتوث حقًا) ، ولهذا السبب أستخدم أيضًا هذا الخيار أنا لا أستعمل.

بادئ ذي بدء ، بعد إنشاء المدير ، يستدعي المفوض الطريقة:

 func centralManagerDidUpdateState(_ central: CBCentralManager) 

لذلك نحصل على استجابة من الأجهزة - سواء كان المستخدم مزود بتقنية البلوتوث أم لا.
النصيحة الأولى: المدير عديم الفائدة حتى نحصل على إجابة بأن البلوتوث قيد التشغيل ، حالته. يمكن استخدام حالات أخرى فقط لمطالبة المستخدم بتشغيل البلوتوث.

بحث الجهاز


الآن بما أن مديرنا يعمل بشكل صحيح ، يمكننا أن نشاهد ،
ما هو من حولنا (بعد تلقي حالة .PoweredOn - نحن نسمي وظيفة ScanForPervipherWithServices :)

 manager.scanForPeripheralsWithServices([CBUUID], options: nil) 

أما بالنسبة للخدمات ، فهي عبارة عن مجموعة من CBUUIDs (فئة عبارة عن معرف فريد عالمي 128 بت للسمات التي تستخدمها تقنية Bluetooth منخفضة الطاقة تقريبًا. .

إذا تجاوزت الصفر كحجة ، يمكننا أن نرى جميع الأجهزة حولها. بالنسبة للأداء ، بالطبع ، من الأفضل تحديد مجموعة من المعلمات التي نحتاجها ، ولكن في حالة عدم معرفتها ، لن يحدث شيء فظيع إذا تجاوزت الصفر ، ولم يمت أحد.

منذ أن بدأنا البحث عن الأجهزة ، يجب علينا إيقافه. خلاف ذلك ، سيستمر البحث ويهبط بطارية المستخدم حتى نوقفها. بمجرد العثور على الجهاز المناسب ، أو اختفاء الحاجة إلى البحث ، سنتوقف:

 manager.stopScan() 

في كل مرة يتم اكتشاف جهاز جديد ، سيقوم مندوب المدير باستدعاء الوظيفة didDiscoverPeripheral في قائمة الانتظار التي حددناها عندما تمت تهيئتها. تنقل الوظيفة إلينا الجهاز الذي تم العثور عليه (الطرفية) ، والمعلومات المتعلقة به (إعلان البيانات - وهو أمر قرر مطورو الشريحة عرضه في كل مرة) ومستوى إشارة RSSI النسبية بالديسيبل.

 func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) 

النصيحة الثانية: حافظ دومًا على رابط قوي إلى أداة الكشف التي تم اكتشافها. إذا لم يتم ذلك ، فسيقرر النظام أننا لسنا بحاجة إلى الجهاز الموجود وسوف نتجاهله. سوف تتذكره ، ولكن لن نتمكن من الوصول إليه بعد ذلك. وإلا فلن نتمكن من العمل مع الجهاز.

اتصال الجهاز


لقد وجدنا الجهاز الذي نحن مهتمون به - هذه هي الطريقة التي تأتي بها إلى حفلة وترى فتاة جميلة. نريد الاتصال ، نسميها وظيفة connectPeripheral - نحن نقدم "شراء مشروب". وبالتالي ، نحاول الاتصال بالجهاز المطلوب (المحيطي) ، ويمكن أن يخبرنا بـ "نعم" أو "لا" ، ولكن جهاز iPhone الخاص بنا جيد حقًا ، لذلك سنسمع إجابة إيجابية.

 manager.connectPeripheral(peripheral, options: nil) 

هنا انتقلنا إلى المدير المسؤول عن الاتصالات ، ونخبره بالجهاز المحدد الذي نتصل به ، ومرة ​​أخرى نعطي صفراً للخيارات (إذا كنت مهتمًا حقًا بمعرفة الخيارات ، اقرأ الوثائق ، ولكن يمكنك عادةً الاستغناء عنها). عند الانتهاء من العمل مع الجهاز ، يمكنك قطع الاتصال به ، حسنًا ، كما تعلمون ، في الصباح - إلغاء الاتصال الداخلي:

 //called to cancel and/or disconnect manager.cancelPeripheralConnection(peripheral) 

بعد أن نقوم بالاتصال أو قطع الاتصال ، سيبلغنا المفوض بذلك:

 //didConnect func centralManager(central: CBCentralManager!, didConnectPeripheral peripheral: CBPeripheral!) //didDisconnect func centralManager(central: CBCentralManager!, didDisconnectPeripheral peripheral: CBPeripheral!, error: NSError!) 

الآن ، نصيحتان أكثر أهمية. يفترض بروتوكول Bluetooth انتهاء مهلة الاتصال ، لكن Apple لا تهتم. سيحاول iOS الاتصال مرارًا وتكرارًا ولن يتوقف حتى تتصل بإلغاء. يمكن أن تستغرق هذه العملية وقتًا طويلاً ، لذلك من الضروري تحديدها في الوقت المناسب ، وإذا لم نتلقى في النهاية رسالة اتصال ناجحة (didConnectPeripheral) ، فنحن بحاجة إلى إبلاغ المستخدم بحدوث خطأ ما.

إذا لم تحتفظ برابط قوي إلى الأجهزة الطرفية ، فسيقوم iOS ببساطة بإعادة تعيين الاتصال. من وجهة نظرها ، سيعني ذلك أنك لست بحاجة إليها ، والحفاظ عليها مهمة مكثفة للطاقة للبطارية ، ونحن نعرف كيف ترتبط Apple باستهلاك الطاقة.

دعونا نجعل الجهاز مفيدًا


وهكذا ، اتصلنا بالجهاز ، لنفعل شيئًا معه. ذكرت سابقًا الخدمات والميزات ، والقيم التي تحتوي عليها ، وهذا ما نحتاج إليه. الآن لدينا جهاز ، إنه متصل ويمكننا الحصول على خدماته عن طريق الاتصال بالخدمات الطرفية.

 peripheral.discoverServices(nil) func peripheral(peripheral: CBPeripheral!, didDiscoverServices error: NSError!) peripheral.services 

الآن سيبدو الأمر مربكًا بعض الشيء ، ولكن يتم استدعاء المفوض في سلسلة المحادثات التي حددناها عند إنشاء المدير ، على الرغم من حقيقة أن هذا مفوض إلى المكتب. أي أن النظام يتذكر أي دفق يعمل معه ، ويتم إجراء جميع اتصالات Bluetooth الخاصة بنا على هذا الدفق. من المهم ألا تنسى العودة إلى الصفحة الرئيسية إذا لم تستخدمها.

حصلنا على الخدمات ، ولكن لا يزال لدينا شيء للعمل معه. بعد ذلك ، تحتاج إلى استدعاء peripheral.discoverCharacteristics ، سيعطينا المفوض جميع الخصائص المتاحة للخدمات المستلمة في didDiscoverCharacteristicsForService. الآن يمكننا قراءة القيم ،
الموجودة هناك (readValueForCharacteristic) أو لطلب إعلامنا بمجرد أن يتغير شيء هناك - setNotifyValue.

 peripheral.discoverCharacteristics(nil, forService: (service as CBService)) func peripheral(peripheral: CBPeripheral!, didDiscoverCharacteristicsForService service: CBService!, error: NSError!) peripheral.readValueForCharacteristic(characteristic) peripheral.setNotifyValue(true, forCharacteristic: characteristic) func peripheral(peripheral: CBPeripheral!, didUpdateValueForCharacteristic characteristic: CBCharacteristic!, error: NSError!) 

على عكس Android ، لا تميز Apple بين القراءة والإشعار. أي أننا لا نعرف ما يحدث - نحن نقرأ شيئًا من الجهاز أو أن هذا الجهاز يخبرنا بشيء.

التسجيل على جهاز


لدينا جهاز ونقرأ المعلومات منه ونديره. لذا ، يمكننا تسجيل المعلومات حولها ، كقاعدة عامة - NSData العادية. تحتاج فقط إلى معرفة ما يتوقعه هذا الجهاز منا وما سيقبله.

تأتي معظم أجهزة BLE بمواصفات ، وهو نوع من API يتضح من خلاله كيفية "الاتصال" بها. يمكنك سحب البيانات من الخصائص للحصول على فكرة تقريبية على الأقل عما يتوقعه الجهاز منا.

من المواصفات ، نتعرف على الخصائص التي نقرأها والخصائص التي نقرأها ، ونكتب فيها ، وما إذا كان سيتم إعلامنا بالتغييرات (isNotifying). في أغلب الأحيان ، ستجد هنا كل ما هو مطلوب للعمل.

 peripheral.writeValue(data: NSData!, forCharacteristic: CBCharacteristic!, type: CBCharacteristicWriteType) characteristic.properties - OptionSet type characteristic.isNotifying func peripheral(peripheral: CBPeripheral!, didWriteValueForCharacteristic characteristic: CBCharacteristic!, error: NSError!) 

أثناء عملية التسجيل ، سيبلغنا المفوض أن كل شيء سار على ما يرام (didWriteValueForCharacteristics) ، وأنه تم تحديث القيمة المطلوبة ، ويمكننا إخبار المستخدم عنها أو استخدام هذه المعلومات بشكل مختلف.

نعتبر الموضوع في قسم ضيق للغاية ، يعتمد على تطبيق Apple ، لذلك هناك عدد من المشاكل التي يجب مواجهتها. على سبيل المثال ، اعتماد قوي للغاية على التفويض ، حتى أبل الحبيبة.

وراثة CBPeripheral؟ إذا كان كل شيء سهلاً للغاية


يبدو أنه نظرًا لأن لدينا جهازًا ، يمكننا البدء في استخدامه ، ولكن في الواقع لن يخبرنا أي شيء عن نفسه. ربما نريد التحكم في القفل أو تكييف الهواء أو مستشعر معدل ضربات القلب. تحتاج إلى معرفة الجهاز الذي نتواصل معه.

يبدو أن الميراث: لدينا حالة خاصة لشيء مشترك. من تجربتي ، يمكنني القول أنه عند استخدام الميراث ، لن يعمل شيء على الإطلاق كما هو متوقع ، ولن يعمل شيء على الإطلاق ، ولن تعرف السبب. بشكل عام ، أحذرك من فكرة وراثة CBPeripheral. ماذا تفعل؟

أنصحك بإضافة CBPeripheral إلى مُنشئ الكائن الذي سيديره. هذا يغلفها داخل هذه الفئة. استخدمه للتفاعل مع الجهاز ، واحتفظ برابط قوي معه حتى لا يقطع iOS الاتصال. لكن الشيء الأكثر أهمية هو أنه سيتم استخدام هذه الفئة كمفوض ، وإلا سيكون من الصعب إدارة جميع الأجهزة في مكان واحد ، وهذا يهدد حفنة من إذا كان آخر.

الاتصال والعمل مع CBPeripheralDelegate


ولذا فإننا نتصل بالجهاز ونريد أن نكون CBPeripheralDelegate. هناك فارق بسيط آخر: أثناء العمل مع الجهاز ، "استجواب" خدماته وخصائصه ، والقراءة والكتابة إليهم ، تحدث جميع الاتصالات تقريبًا مع الطرفية. كل شيء ما عدا الاتصال.

بطبيعة الحال ، نود أن نركز كل الاتصالات في مكان واحد ، ولكن يجب أن يكون المدير على علم بما يحدث مع الجهاز. والصعوبة هي أن يكون لديك مصدر واحد للحقيقة ، للتأكد من إعلام الجميع في الوقت المناسب بما يحدث مع الجهاز. للقيام بذلك ، سنقوم بمراقبة حالة الأجهزة الطرفية - يمكن أن تتغير من انقطاع الاتصال والاتصال والاتصال. يخبرك دائمًا عن الوضع الحالي. يبقى الاشتراك في تغيير الحالة في مرفق التحكم الخاص بنا ، والذي تحدثت عنه سابقًا ، وهذا سيجعل من الممكن التواصل مع الجهاز من مكان واحد.

القرب


نقطة مهمة للغاية ، لأنه من الصعب العثور على وثائق عادية حول هذا الموضوع. في حالة Apple و iBeacons ، كل شيء بسيط ، يقولون لنا مدى قربنا من جهاز البلوتوث.
للأسف ، لا يتم منحنا مثل هذه الطريقة السهلة للعمل مع أجهزة الطرف الثالث. وحدث أكثر من مرة أن هناك حاجة لتحديد أقرب جهاز. من الصعب أيضًا فهم ما إذا كان الجهاز في النطاق المتاح أم لا. في بعض الأحيان ، عند البحث عن الأجهزة ، يمكن أن تتيح لك معرفة نفسها مرة واحدة فقط وتختفي ، ثم ستفشل محاولة الاتصال.

نستخدم الطريقة التالية: احفظ مجموعة مع تسميات التاريخ وقوة الإشارة (RSSI) لكل رسالة يتم تلقيها في DiscoverPeripheral. إذا صادف شخص ما موقع CoreLocation ، فإن طريقتنا مشابهة لطريقة تخزين الطوابع الزمنية والإحداثيات المقابلة لها. عادة ، كلما زادت الإشارة (RSSI) ، كلما اقترب الجهاز. من الصعب فهم ما إذا كان الجهاز في نطاق يمكن الوصول إليه أم لا ، ويرجع ذلك جزئيًا إلى أن هذا المفهوم نفسه مرن تمامًا. لهذا ، أستخدم المتوسط ​​المرجح للإشارة. ضع في اعتبارك أنه يجب طلب قوة إشارة الجهاز المتصل يدويًا في كل مرة تحتاج إلى معرفتها.

ما هي الخطوة التالية؟


لسوء الحظ ، لن تجعلك هذه المقالة خبيرًا إذا قرأتها لك أيضًا.
أصبحت مثيرة للاهتمام - انتبه إلى دليل برمجة CoreBluetooth من Apple ، الدليل ليس كبيرًا جدًا ولكنه مفيد جدًا. لا يزال هناك بثان من WWDC 2012 ( أساسي ومتقدم ) وآخر من 2013 ، ولكن لا تقلق ، لم يتغير شيء يذكر منذ ذلك الحين.

هناك أيضًا مقطع فيديو من Altconf 2015 تم نشره على موقع Realm على الويب ، والذي يشارك تجربة جون شير ، وهو رجل عظيم ومتخصص.

Source: https://habr.com/ru/post/ar425937/


All Articles