مجسات النافذة اللاسلكية محلية الصنع: STM32L051 + RFM69 + Android

عمت مساءً يا خبروفيتيس! قبل بضع سنوات ، اشتريت إعلانًا zWave ملونًا وقمت بتثبيت مستشعرات النافذة وفقًا لهذا البروتوكول. تم توصيل USB zWave-Stick بالخادم المنزلي ، الذي لعب دور وحدة التحكم ، وتم كتابة وحدة Java صغيرة تلقت البيانات من وحدة التحكم هذه ، وتم كتابة تطبيق صغير لنظام Android يعرض بشكل جميل حالة جميع أجهزة الاستشعار. يتم إدخال البطاريات ، ويتم تسجيل أجهزة الاستشعار على وحدة تحكم ، عملت كل شيء. ولكن بعد شهرين كانت هناك خيبة أمل كبيرة. أولاً ، تعمل أجهزة استشعار zWave هذه على مبدأ "إرسال رسالة وتغفو دون انتظار التأكيد." في حالتي ، أدى هذا إلى حقيقة أن الإشارة من أبعد أجهزة الاستشعار من وحدة التحكم ببساطة لم تصل إلى وحدة التحكم. حتى تثبيت مكرر zWave إضافية لم يساعد. ثانياً ، استنزفوا البطارية بسرعة بحيث توقفت جميع المستشعرات بعد حوالي ستة أشهر عن العمل. والسبب هو أنهم استيقظوا كل ساعة لإبلاغ المراقب عن حالتهم. تعطيل أو تغيير هذا الخيار لم يعمل ، لأن البرنامج القياسي لم يسمح بذلك بشكل قاطع. بعد عامين من العذاب مع هذه التقنية الخام وغير الموثوق بها وغير الودية ، قررت أن لدي ما يكفي. لكن بدلاً من وضع كل شيء بعيدًا وإلقائه بعيدًا ، حصلت على فكرة ترك الحالات ، ولكن تغيير الإلكترونيات فيها. وقع الاختيار على جهاز إرسال واستقبال RFM69 بسيط إلى حد ما (433 ميغاهيرتز) ، على أساس أنه كان من الممكن جعل كل من لوحة للمستشعر ووحدة تحكم متصلة عبر USB إلى الخادم. تم تشغيل النظام الجديد لمدة 5 أشهر بالفعل ، والموثوقية قريبة من 100 ٪ (ولكن لا تزال هناك بعض الأعطال) ، لا أعتقد أن البطاريات فارغة. أي أنه من الواضح بالفعل أنه قد تم القضاء على جميع أوجه القصور في النظام القديم القائم على zWave ، وأريد أن أشارك في التفاصيل الفنية لهذه المقالة ، انظر الصورة.



من يهتم ، من فضلك ، تحت القط.

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

استند أول نموذج أولي من أجهزة الاستشعار على ESP8266. وحدة التحكم - على أساس دفعتي التي كتبت عنها بالفعل على حبري . لقد عمل النظام كنموذج أولي ، لكن كان علي أن أتخلى عنه لسببين. السبب الأول هو نفسه - في المنزل كانت هناك زوايا ذات مستوى منخفض جدًا من إشارة WiFi ، مما أدى إلى وقت تنشيط طويل للغاية ESP8266 عند الاستيقاظ ، ونتيجة لذلك ، تفريغ بطارية قوي. على الرغم من أنني أعترف أنني لا أعرف كيف أطبخ هذا ESP8266 بالذات ( مثل هذه المقالات تؤكد هذه الأطروحة). لكن السبب الثاني كان أكثر خطورة. منذ أن قررت عدم ترك الحالة فقط ، ولكن أيضًا حجرة البطارية ، اقتصرت على بطارية CR123A ، والتي تبلغ 3.0 فولت. أي أن سعر وتعقد المستشعر زاد بسبب زيادة المحول DC / DC. على الرغم من وجود مقال رائع حول هذا الموضوع عن حبري. ولكن ، على أي حال ، تفوق هذين السببين ورفضت ESP8266.

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

ثم قرر أن يتطلع إلى وحدات الراديو في 433 و 868 ميغاهيرتز ، وأمر عدة وحدات للتجارب: RFM69HCW ، A110LR09A و MRF89XAM8A . من حيث حجم الوحدة النمطية ، والسعر ، وكذلك بسبب توفر كل من المكتبات والأمثلة الجيدة ، استقرت على RFM69HCW ، التي شكلت أساس النظام.

يحتوي النظام على أربعة مكونات فقط:

  • أجهزة الاستشعار اللاسلكية (433 ميغاهيرتز ، بطارية CR123A 3.0V) ،
  • وحدة تحكم الشبكة (433 ميغاهيرتز ، التي تعمل عن طريق USB من الخادم)
  • الخادم (الذي كتبت عنه بالفعل على Habré في هذه المقالة ) ووحدة برنامج الخادم
  • عميل المحمول (تطبيق Android)

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

أجهزة الاستشعار


تستخدم المستشعرات وحدة الراديو RFM69HCW . لديها نطاق جهد تشغيل واسع (1.8 فولت -2.4 فولت 17 ديسيبل ، 2.4 فولت -3.6 فولت 20 ديسيبل) ويتم التحكم فيه عن طريق SPI. هذا هو ، تحتاج إلى متحكم. منذ بعض الوقت تعرفت على سلسلة STM32L وأمرت STM32L051 للتجارب. لقد رشحت لي كتيار صغير في وضع السكون (0.27 μA) ، وفولطية التشغيل ، مطابقة تقريبًا لجهد وحدة الراديو (1.65V-3.6V). بالإضافة إلى انخفاض السعر.

اتضح المخطط التالي:

صورة

الجهد الكهربائي لكل من متحكم وحدة الراديو هو أنها يمكن أن تعمل مباشرة من عنصر CR123A. يحتوي STM32L051 على مرجع جهد داخلي متصل بقناة ADC 17 ، بالإضافة إلى قيمة المعايرة لهذا المصدر ، والتي تتيح لك قياس القيمة الحالية لجهد الإمداد VDD. يتم توصيل وحدة الراديو بالطاقة من خلال جهاز مجال Q1 ، والذي يسمح لك بالتحكم في طاقتها من متحكم.

يتم تنفيذ وضع السكون عن طريق نقل متحكم إلى "الاستعداد". في هذا الوضع ، يحتوي STM32L051 على نواة معطلة ، تقريبًا جميع الأجهزة الطرفية ومنظم جهد داخلي ، مما يضمن الاستهلاك عند مستوى 0.29 μA (مع تعطيل الوقت الفعلي في الوقت الفعلي). ولكن في هذا الوضع هناك ميزة - يستيقظ المتحكم الدقيق فقط على طول الحافة الصاعدة للإشارة على دبوس WKUP (A0). نظرًا لاستخدام مفتاح مغناطيسي يعمل على / قبالة (مغلق أو مفتوح) ، فأنت بحاجة إلى كتلة صغيرة تولد نبضًا لمدة قصيرة عند الإغلاق وعند فتح اتصال مغناطيسي. يتم تغذية هذه النبضة إلى المدخلات A0 من المتحكم وتوقظها. يتم تطبيق هذا المحول على IC3 الحصري- أو من سلسلة 74AUP الموفرة للطاقة (74AUP1G86) ، والتي تعمل في نطاق الجهد من 0.8V-3.6V وتستهلك 0.2 μA. وبالتالي ، يجب أن يكون الاستهلاك الكلي في وضع السكون حوالي 0.5 μA ، وهو ما تؤكده القياسات على جهاز استشعار مجمّع بالكامل.

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

إذا كان الجهد الكهربي للبطارية أكبر من 1.8 فولت ، فسيتم تشغيل وحدة التحكم الدقيقة وتهيئة وحدة الراديو ، وتنقل الحالة إلى وحدة تحكم الشبكة وتنتظر التأكيد. تتضمن حزمة البيانات رقمًا فريدًا من نوع 32 بت (حدث) ، فولطية البطارية ، درجة الحرارة ، حالة تلامس مغناطيسي ، بايت التحكم (CRC7). في حالة التأكيد الناجح ، يسقط المتحكم في النوم. إذا لم يكن كذلك ، فقم بإرسال الحالة مرة أخرى ، ولكن بحد أقصى 10 مرات. لقد قمت بتعيين هذا الحد بحيث لا يحاول المستشعر الانتظار إلى أجل غير مسمى للحصول على استجابة في موقف يتم فيه إيقاف تشغيل وحدة تحكم الشبكة. يتم تخزين آخر رقم حدث فريد تم نقله في EEPROM الداخلي لجهاز التحكم.

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

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

صورة

تحتاج وحدة الراديو إلى هوائي. هذا مجرد قطعة من الأسلاك 164 مم.

بعد التثبيت ، يجب برمجة كل مستشعر ، حيث يتم توفير موصل SWD. تركت الاتصالات المتبقية على السبورة للتجارب المحتملة في المستقبل.
البرنامج الثابت مكتوب بلغة C ++ ، ويتم تنفيذ جزء من الكود في فصول أساسية عالمية إلى حد ما تقوم بتغليف المكالمات إلى مكتبة ST HAL. شفرة المصدر هنا . لأغراض التطوير ، تم استخدام System Workbench لـ STM32. حجم ملف البرنامج الثابت الثنائي النهائي هو 22 كيلو بايت.

وهنا هو الاستشعار في القضية:

صورة

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

من أجل الاهتمام ، قمت بحساب تكلفة مكونات جهاز استشعار واحد (بأسعار كتالوج Mouser والسعر والسعر الإجمالي باليورو)
عنصروصفقيمةرقم الفأرعدتكلفة
IC3حصري أو1G86771-74AUP1G86GW-G10.254
IC1متحكمSTM32L051511-STM32L051K6T612.14
SV1مقبسإدارة الرعاية الاجتماعية68602-406HLF10.157
LED1الصمام 3 مم3MM630 HLMP-K15510.351
Q1P-MOSFETBSH205771-BSH205G2R10.276
S1الاتصال المغناطيسيORD211-0810876-ORD211-081010.625
IC2RFM69HCW وحدة الراديوRFM69HCW474-COM-1391015.36
C1 ، C2 ، C3 ، C6SMD مكثف0.1uF80-C0805C104J5RAC40.1
C5SMD مكثف0.4nFC0805C471K8HACTU10.021
C4SMD مكثف (التنتالوم)47UF581-TAJR225K016RNJ10.334
R1SMD المقاوم10K660 RK73H2ATTDD1002F10.01
R10SMD المقاوم330660 RK73H2ATTDD3300F10.01
R3 ، R4 ، R6 ، R7 ، R8 ، R11SMD المقاوم47K660 RK73H2ATTDD4702F60.06
R5SMD المقاوم56660 RK73H2ATTD56R0F10.013
R9SMD المقاوم56M603-RC0805JR-0756ML10.037
9748

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

وحدة تحكم الشبكة


لوحدة التحكم ، تم استخدام نفس وحدة الراديو ونفس متحكم كما هو الحال بالنسبة لأجهزة الاستشعار. هذا سمح بإعادة استخدام معظم رمز البرنامج الثابت. بالنسبة لوحدة التحكم ، اخترت عامل الشكل هذا للوحة من أجل استخدام العلبة الجاهزة من Raspberry Pi. مقارنة بالمستشعر ، تمت إضافة موصل هوائي خارجي وحلقة USB استنادًا إلى المعزل FT232RL و SI-8621. بالطبع ، يمكن للمرء أن يأخذ بعض STM32 مع USB على متن الطائرة. ولكن بعد ذلك ، أولاً ، سيكون من الضروري فصل رمز البرنامج الثابت ، وثانياً ، تنفيذ برنامج USB نفسه. الخيار مع FT232RL خارجي ، على الرغم من أنه أكثر تكلفة ، هو أكثر موثوقية وأسرع في التنفيذ. والنتيجة هي المخطط التالي :

صورة

وفي شكل تجميعي اتضح مثل هذا:

صورة

صورة

لا يتحكم المتحكم الدقيق في النوم هنا ، حيث تعمل وحدة الراديو للاستقبال ، لكن بعد تلقي حزمة بنجاح من أي من المستشعرات ، تدخل الوحدة في وضع الإرسال وترسل إقرارًا بذلك. بالإضافة إلى ذلك ، يتم إرسال أي حزمة تم تلقيها بنجاح عبر USB إلى الخادم. تنسيق الحزمة هو سلسلة من القيم مفصولة بالرمز "؛":
غيغاواط ؛ 3 ؛ 12 ؛ - 57 ؛ صفر ؛ 146 ؛ 30 ؛ 18
حيث:

  • الموضع الأول هو تسمية الحزمة (دائمًا "GW")
  • الموضع الثاني - نوع البيانات (حالة المستشعر أو رمز الخطأ)
  • المركز الثالث - رقم المستشعر
  • الموضع الرابع - جودة الإشارة على جانب وحدة تحكم الشبكة (RFM69HCW يحافظ على جودة الإشارة أثناء الاستقبال ويسمح لك باستجوابها عند انتهاء الاستقبال)
  • المركز الخامس - حالة النافذة (0 مفتوحة ، 1 مغلقة)
  • المركز السادس - رقم فريد للحزمة (الحدث) من المستشعر. يسمح لك هذا الرقم بالتحكم من جانب الخادم فيما إذا كان الخادم قد تم استلام جميع الأحداث أو حدث واحد أو أكثر من الأحداث.
  • المركز السابع - الجهد الحالي للبطارية (30 يتوافق مع 3.0 فولت)
  • الموضع الأخير الثامن هو درجة الحرارة من جهاز استشعار متحكم داخلي. لم أحسب بعد كيفية استخدامها

رمز مصدر البرنامج الثابت في نفس المكان بالنسبة لجهاز الاستشعار . لديهم ملف رئيسي مشترك ومكتبة مشتركة من الطبقات الأساسية. يتم تحديد خيار البرنامج الثابت (المستشعر أو جهاز التحكم) باستخدام توجيه التعريف في ملف main.cpp.

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

وحدة الخادم


وحدة تحكم الشبكة متصلة بخادم يقوم بتشغيل CentOS 7. يقوم الخادم بتشغيل برنامج مكتوب بلغة Java. وفي هيكله ، وفي التنفيذ ، وفي المهام ، البرنامج بسيط للغاية:

  • باستخدام مكتبة RxTx قديمة جدًا ولم تعد معتمدة ، تتم مراقبة منفذ USB المحدد في التكوين (في حالتي / dev / ttyUSB0). في الوقت الحالي ، هذا هو الجزء الأكثر ضعفًا من النظام بأكمله ، حيث تقوم المكتبة بتحميل المعالج الخادم بكثافة.
  • إذا تم استلام البيانات من منفذ USB ، يتم تسجيلها في ملف سجل وتخزينها في فئة حالة المستشعر. عند إعادة تشغيل الخادم ، يتم فقد الحالة. لاستعادته ، تحتاج إلى التجول في المنزل وفتح جميع النوافذ وإغلاقها يدويًا. قد يكون هذا أكبر عيب في نظامي ، لكنني رفضت تمامًا الاستقصاء الدوري لأجهزة الاستشعار من أجل توفير بطارياتها.
  • يعد التطبيق أيضًا خادم TCP / IP صغيرًا ويستمع إلى منفذ TCP / IP المحدد في التكوين. على هذا المنفذ ، يمكنه قبول الاتصالات من عميل محمول.
  • إذا كان عميل الهاتف المحمول يتصل بالخادم ، فإنه يرسل الحالة الحالية لجميع أجهزة الاستشعار. باستخدام رسائل heartbit الدورية ، يراقب الخادم أيضًا نشاط الاتصال.
  • يقوم الخادم وعميل الهاتف المحمول بتبادل الرسائل بتنسيق XML. يتم تنفيذ هذه الرسائل في شكل مكتبة Java صغيرة ، والتي يتم مشاركتها على جانب الخادم وعلى جانب تطبيق Android للجوال.
  • على الرغم من أن هذا ليس له معنى كبير ، من أجل الاهتمام ، أضفت وظيفة تفويض عميل متنقل عبر IMEI ، وكذلك تشفير AES للرسائل بين الخادم والعميل باستخدام مفتاح مخيط في الكود المصدري (حزمة javax.crypto Java). ولكن هذا أمر محض للتجربة ، لأن منفذ TCP / IP لوحدة الخادم هذه لا يمكن الوصول إليه إلا من الشبكة الداخلية ولا يمكن رؤيته من الخارج. رغم أنني إذا أردت فتح هذا المنفذ للعالم الكبير ، فلن يكون الأمر مخيفًا جدًا للقيام بذلك.

من يهتم ، الكود المصدري لوحدة الخادم موجود هنا .

عميل الجوال (تطبيق Android)


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

صورة

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

صورة

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

تجربة التشغيل


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

التحليل بسيط للغاية - فقط grep في مجلد ملفات السجل على الخادم.

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

وهنا جدول موجز عن تشغيل أجهزة الاستشعار لمدة 5 أشهر.
أرضيةالاستشعارعدد
من الأحداث
من الضائعين
من الأحداث
الجهد
بطاريات
متوسط
القدرات
إشارة
11010503.1-66
1115203.3-70
11212203.3-61
1138903.3-74
114257303.3-68
11526103.3-60
11654323.3-70
11715623.3-74
11817723.2-68
11938433.3-56
2336823.3-62
248603.3-71
2552123.3-59
2611503.3-62
2731623.3-63
2841913.3-60
298903.3-68

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

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

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

يُظهر عمود وسيط قوة الإشارة المتوسطة قيمة RSSI المتوسطة (بالديسيبل) ، حيث يتم الحصول على كل قيمة فردية بعد استلام كل حزمة. يقع المستشعر ذو أفضل إشارة (رقم 19 ، -56 ديسيبل) على مسافة 2 متر في الرؤية المباشرة من وحدة تحكم الشبكة ، لكن الهوائي في هذا المستشعر مطوي بإطار داخل العلبة. وحدة تحكم الشبكة نفسها تقع في الطابق الأرضي. ومع ذلك ، فإن إشارة من مجسات الطابق الثاني جيدة جدا. هذا يرجع إلى حقيقة أنه في جميع مجسات الطابق الثاني ، يتم إزالة الهوائي من غلاف المستشعر.

بدلا من الكلمة الأخيرة


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

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


All Articles