كيف أخذت البيانات من ميزان الحرارة بليه من Xiaomi

الخلفية: كواحدة من هواياتي ، حصلت على "المنزل الذكي". أريد أجهزة جميلة ، ولكني أريد أيضًا الحرية والخصوصية. لذلك ، أنا منخرط في التهجين Xiaomi uzhik مع مساعد القنفذ المنزلي .

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

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



تمت إضافة المستشعر إلى تطبيق MiHome دون مشاكل (لدي واجهة باللغة الإنجليزية في كل مكان ، مع النسخة الروسية من MiHome ، كما يقولون ، كانت هناك صعوبات في الترجمة). يُظهر القيم الحالية ومحفوظات التغييرات في القراءات.

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

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

النهج الأول للقذيفة


للبدء ، افتح المحطة على أوبونتو وقم بتشغيل bluetoothctl. نرى ما يلي:

[NEW] Controller 00:1A:7D:DA:71:13 fett [default] [NEW] Device 3F:59:C8:80:70:BE LYWSD02 [NEW] Device 4C:65:A8:DC:0D:AF MJ_HT_V1 

MJ_HT_V1 هو جهاز استشعار درجة الحرارة القديم ، LYWSD02 هو جديد. الفرق في تنسيق تسمية النموذج ينذر بالخطر إلى حد ما.

ثم تحتاج إلى قراءة بطريقة أو بأخرى ، ونوع البيانات بشكل عام يمكن الحصول عليها منا. افتتح مصادر مكتبة mitemp ، والتي تستخدم في Home Assistant لتلقي البيانات من المستشعر القديم. هناك وجدت أن مكتبة blewrap مستخدمة ، والتي بدورها عبارة عن غلاف في مكتبتي Python للعمل مع BLE. لست بحاجة إلى العديد من الطبقات ، فسنستخدم الزرق . هناك وثائق ، ليست كثيرة وليس صغيرة ، نقرأ ونكتب نصًا يمر عبر جميع حقول البيانات الموجودة على الجهاز.

 from bluepy import btle mac = '3F:59:C8:80:70:BE' p = btle.Peripheral(mac) for s in p.getServices(): print('Service:', s.uuid) for c in s.getCharacteristics(): print('\tCharacteristic:', c.uuid) print('\t\t', c.propertiesToString()) if c.supportsRead(): print('\t\t', c.read()) 

بشكل عام ، كل شيء بسيط - يوفر جهاز بليه مجموعة من الخدمات ، كل منها يتكون من مجموعة من الخصائص. يمكن أن تكون كل سمة واحدة من 8 أنواع ، يمكنك تحديد عدة أنواع في نفس الوقت لخاصية واحدة. يتم تحديد الخدمات والميزات بطريقتين - عنوان في شكل قيمة HEX و UUID. أنا أكثر دراية بالعمل مع UUID.

لذلك ، فكرت في جميع مواصفات كل من المستشعرات ونظرت إليها وأدركت مرة أخرى أن الأجهزة التي تنتجها شركات مختلفة تمامًا تباع تحت علامة Xiaomi التجارية. من بين قيم المستشعر القديم ، تم العثور على "Cleargrass Inc" ، وفي الجديد "miaomiaoce.com". يختلف هيكل الخدمات وخصائص هذين المستشعرين تمامًا أيضًا ، وقائمة خصائص المستشعر الجديد تبلغ ضعف هذا الطول. بعد ذلك ، أصبح من الواضح أنك تحتاج إلى كتابة مكتبتك الخاصة للتكامل مع المستشعر (لا ، بالطبع لقد غوغل في البداية ، ربما كان هناك شيء مفيد عند الطلب LYWSD02 ، لكنني لم أقدم أي شيء معقول google).

إذا كيف يمكنك الحصول على البيانات؟


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

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



هذه الأسهم الثلاثية تنشيط الاشتراك.

كانت خصوصية المستشعر القديم هي أن البيانات المتعلقة بالحرارة والرطوبة جاءت في شكل سلسلة UTF ، بينما أعادت الجديدة الجديدة كل شيء في شكل ثنائي.

اشترك في الإخطارات


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

 class MyDelegate(btle.DefaultDelegate): def handleNotification(self, cHandle, data): print(data) mac_addr = '3F:59:C8:80:70:BE' p = btle.Peripheral(mac_addr) p.setDelegate(MyDelegate()) uuid = 'EBE0CCC1-7A0A-4B0C-8A1A-6FF2997DA3A6' #    ,        ch = p.getCharacteristics(uuid=uuid)[0] #     desc = ch.getDescriptors(forUUID=0x2902)[0] #  ,         desc.write(0x01.to_bytes(2, byteorder="little"), withResponse=True) while True: p.waitForNotifications(5.0) 

نحن نفصل الحبوب عن القشر


لحسن الحظ ، تم وضع علامة على ثلاث خصائص فقط كـ WRITE NOTIFY ، بينما جاءت البيانات بترددات مختلفة وميزات بصرية.

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

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

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

تحليل البيانات


عادةً ما أعمل مع البيانات النصية (الحصول على بيانات HTTP في شكل JSON / xml ، ووضعها في ملف أو في قاعدة البيانات) ، لذلك لم أفهم حقًا كيفية التعامل مع المهمة. لذلك ، بدأت في محاولة تحويل البيانات بطرق مختلفة يمكن إجراؤها من الثعبان. كتبت هنا وظيفة التحويل هذه وبدأت أشاهد كيف يرتبط ذلك بالبيانات الموجودة على شاشة المستشعر.

 def parse(v): print([x for x in v]) print('{0:#x}'.format(int.from_bytes(data, byteorder='big'))) print('{0:#x}'.format(int.from_bytes(data, byteorder='little'))) 

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

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

في طريق النجاح


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

T[°C]=45+175 cdot fracST2161



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

T[°C]=45+175 cdot fracST216



ومع ذلك ، ومع ذلك ، فإن البيانات بعد التحويل لم تشبه البيانات الحقيقية عن كثب. كنت هنا أكثر حزنًا ، فتحت الكود المصدري للمكتبة لـ SHTC3 من Adfruit وبدأت في محاولة تكييف كود التحويل من C ++ إلى الثعبان. أحضرت كل شيء إلى الجهاز اللوحي - البيانات الخام ، والبنية المحولة والنتيجة.

 def handleNotification(self, cHandle, data): temp = data[:2] humid = data[2] unpacked = struct.unpack('H', temp)[0] print(data, unpacked, -45 + 175 * unpacked / 2 ** 19, sep='\t') 

حصلت على شيء مثل هذا:

 b',\n2' 2604 -44.130821228027344 b'-\n2' 2605 -44.1304874420166 b'+\n2' 2603 -44.131155014038086 b',\n2' 2604 -44.130821228027344 

نعم ... إنه نوع من البرودة ... لكن ، انتظر ، انتظر ، ما هو 2604؟ هذا هو ، 26.0 درجة على الشاشة! لتأكيد الفرضية ، أخذ مرة أخرى المستشعر إلى البطارية ، والتحقق من أن القيم تتزامن.

نتيجة لذلك ، حصلنا على رمز تحويل البيانات التالي:

 def handleNotification(self, cHandle, data): humid_bytes = data[2] temp_bytes = data[:2] humidity = humid_bytes temperature = struct.unpack('H', temp_bytes)[0] / 100 print(temperature, humidity) 

خاتمة


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

الآن يتم نقل البيانات إلى Home Assistant ، فأنت بحاجة إلى إنهاء رمز التكامل ، وربما ، إعادة كتابته من bluepy إلى bleak ، لأن bleak يستخدم async / await وهو أفضل ملائمًا لـ Home Assistant المكتوبة بواسطة aiohttp.



المراجع:


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


All Articles