كيف حولت مكتبي القابل لضبط ارتفاعه إلى مكتب إنترنت الأشياء

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



باختصار ، هذا المشروع له ميزتان. أولاً ، يتم توصيل الجدول من Google Smart Home إلى Heroku باستخدام الأوامر الصوتية ، وثانيًا: يتواصل Heroku والجدول نفسه باستخدام بروتوكول MQTT Internet of Things. يعد MQTT حلاً جيدًا لإنترنت الأشياء ، وكذلك للتغلب على بعض العقبات الأخرى التي يتعين علينا مواجهتها.



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



نفس الجدول




جزء الأجهزة



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



أهمية العزم



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



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



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







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



محرك مناسب



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







T=Fr







عزم الدوران هو القوة مضروبة في طول ذراع الرافعة.



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



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



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





الصورتان العلويتان عبارة عن محركات مثبتة بالكامل على الطاولة. الصورتان السفليتان عبارة عن قضيب متكامل يمتد على طول الطاولة بمساعدة المحركات.



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



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



دوران مهم. الاصدار الاخير



أخيرًا أدركت أن الأمر كله يعود إلى شيئين: عزم الدوران ودورانه. كان من الضروري إيجاد محرك بعدد كافٍ من الثورات بعزم دوران معروف بالفعل.



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



باختصار ، كان الشهر التالي شهر انتظار لعلبة تروس من الصين ، والسبت التالي بعد الانتظار ، كان لدي طاولة تتحرك بالسرعة المناسبة.





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



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



بلوتوث



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



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



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



كمقدمة سريعة: تمت كتابة ESP32 بلغة C ++ باستخدام مكتبات Arduino للتفاعل مع تطبيق BLE Terminal على الهاتف. يعد تثبيت وتكوين BLE معقدًا للغاية. أولاً ، تحتاج إلى إنشاء جميع الخصائص للقيم التي ترغب في التحكم فيها عبر BLE. فكر في خاصية كمتغير في التعليمات البرمجية الخاصة بك. يلتف BLE متغيرًا في معالجات متعددة للحصول على قيمة هذا المتغير وتعيينها.



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



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



void setup()
{
   Serial.begin(115200);
   Serial2.begin(TFMINIS_BAUDRATE);
   EEPROM.begin(3); // used for saving the height presets between reboots

   tfminis.begin(&Serial2);
   tfminis.setFrameRate(0);

   ledcSetup(UP_PWM_CHANNEL, PWM_FREQUENCY, PWM_RESOLUTION);
   ledcAttachPin(UP_PWM_PIN, UP_PWM_CHANNEL);

   ledcSetup(DOWN_PWM_CHANNEL, PWM_FREQUENCY, PWM_RESOLUTION);
   ledcAttachPin(DOWN_PWM_PIN, DOWN_PWM_CHANNEL);

   state_machine = new StateMachine();
   state_machine->begin(*t_desk_height, UP_PWM_CHANNEL, DOWN_PWM_CHANNEL);

   BLEDevice::init("ESP32_Desk");
  ...

   BLEServer *p_server = BLEDevice::createServer();
   BLEService *p_service = p_server->createService(BLEUUID(SERVICE_UUID), 20);

   /* ------------------- SET HEIGHT TO PRESET CHARACTERISTIC -------------------------------------- */
   BLECharacteristic *p_set_height_to_preset_characteristic = p_service->createCharacteristic(...);
   p_set_height_to_preset_characteristic->setCallbacks(new SetHeightToPresetCallbacks());
   /* ------------------- MOVE DESK UP CHARACTERISTIC ---------------------------------------------- */
   BLECharacteristic *p_move_desk_up_characteristic = p_service->createCharacteristic(...);
   p_move_desk_up_characteristic->setCallbacks(new MoveDeskUpCallbacks());
   /* ------------------- MOVE DESK UP CHARACTERISTIC ---------------------------------------------- */
   BLECharacteristic *p_move_desk_down_characteristic = p_service->createCharacteristic(...);
   p_move_desk_down_characteristic->setCallbacks(new MoveDeskDownCallbacks());
   /* ------------------- GET/SET HEIGHT 1 CHARACTERISTIC ------------------------------------------ */
   BLECharacteristic *p_get_height_1_characteristic = p_service->createCharacteristic(...);
   p_get_height_1_characteristic->setValue(state_machine->getHeightPreset1(), 1);
   BLECharacteristic *p_save_current_height_as_height_1_characteristic = p_service->createCharacteristic(...);
   p_save_current_height_as_height_1_characteristic->setCallbacks(new SaveCurrentHeightAsHeight1Callbacks());
   /* ------------------- GET/SET HEIGHT 2 CHARACTERISTIC ------------------------------------------ */
  ...
   /* ------------------- GET/SET HEIGHT 3 CHARACTERISTIC ------------------------------------------ */
  ...
   /* ------------------- END CHARACTERISTIC DEFINITIONS ------------------------------------------ */
   p_service->start();

   BLEAdvertising *p_advertising = p_server->getAdvertising();
   p_advertising->start();

   xTaskCreate(
       updateDeskHeight,     // Function that should be called
       "Update Desk Height", // Name of the task (for debugging)
       1024,                 // Stack size
       NULL,                 // Parameter to pass
       5,                    // Task priority
       NULL                  // Task handle
   );
}
      
      





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



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



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





واجهة البلوتوث العارية ... كل ما تبقى في الوقت الحالي هو تعلم كيفية كتابة تطبيقات iOS ...



بعد كل هذا ، قالت زوجتي شيئًا غير المشروع بأكمله: "ماذا لو كنت تتحكم في صوتك؟"



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



إضافة إنترنت الأشياء



الآن دعنا نتحدث عن ترقية مكتبك للتحكم الصوتي عبر Google Smart Home وكيفية جعله صديقًا للواي فاي.



كانت إضافة Wi-Fi سهلة بدرجة كافية. لقد استبدلت متحكم Nordic NRF52 بـ ESP32 بشبكة WiFi مدمجة. كانت معظم البرامج محمولة لأنها كتبت بلغة C ++ ، ويمكن برمجة كلا الجهازين باستخدام Platform.IO ومكتبات Arduino ، بما في ذلك tfmini-s التي كتبتها لقياس ارتفاع الجدول الحالي.



يتم عرض بنية نظام تفاعل الجدول مع Google Smart Home أدناه. لنتحدث عن التفاعل بيني وبين Google.







لذلك تم تشغيل Bluetooth. حان الوقت لمعرفة كيفية التفاعل مع Google Smart Home. تتحكم هذه التقنية في المنزل باستخدام إجراءات المنزل الذكي . الأمر المثير للاهتمام في أفعالها هو أن الخدمة تعمل كخادم OAuth2 ، وليس كعميل. كان معظم العمل المنجز مع الخادم هو تنفيذ تطبيق OAuth2 Node.js Express الذي يصل إلى Heroku ويتواصل مثل الوكيل بين Google ومكتبي.



كنت محظوظًا: كان هناك تطبيق خادم لائق باستخدام مكتبتين. تم العثور هنا على المكتبة الأولى ، node-oauth2-server . تم العثور هنا على مكتبة خادم Express-oauth الثانية للاتصال السريع .



const { Pool } = require("pg");
const crypto = require("crypto");
const pool = new Pool({
   connectionString: process.env.DATABASE_URL
});

module.exports.pool = pool;
module.exports.getAccessToken = (bearerToken) => {...};
module.exports.getClient = (clientId, clientSecret) => {...};
module.exports.getRefreshToken = (bearerToken) => {...};
module.exports.getUser = (email, password) => {...};
module.exports.getUserFromAccessToken = (token) => {...};
module.exports.getDevicesFromUserId = (userId) => {...};
module.exports.getDevicesByUserIdAndIds = (userId, deviceIds) => {...};
module.exports.setDeviceHeight = (userId, deviceId, newCurrentHeight) => {...};
module.exports.createUser = (email, password) => {...};
module.exports.saveToken = (token, client, user) => {...};
module.exports.saveAuthorizationCode = (code, client, user) => {...};
module.exports.getAuthorizationCode = (code) => {...};
module.exports.revokeAuthorizationCode = (code) => {...};
module.exports.revokeToken = (code) => {...};

      
      





يأتي بعد ذلك تكوين تطبيق Express نفسه. فيما يلي نقاط النهاية المطلوبة لخادم OAuth ، ولكن يمكنك قراءة الملف الكامل هنا.



const express = require("express");
const OAuth2Server = require("express-oauth-server");
const bodyParser = require("body-parser");
const cookieParser = require("cookie-parser");
const flash = require("express-flash-2");
const session = require("express-session");
const pgSession = require("connect-pg-simple")(session);
const morgan = require("morgan");

const { google_actions_app } = require("./google_actions");
const model = require("./model");
const { getVariablesForAuthorization, getQueryStringForLogin } = require("./util");
const port = process.env.PORT || 3000;

// Create an Express application.
const app = express();
app.set("view engine", "pug");
app.use(morgan("dev"));

// Add OAuth server.
app.oauth = new OAuth2Server({
   model,
   debug: true,
});

// Add body parser.
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(express.static("public"));

// initialize cookie-parser to allow us access the cookies stored in the browser.
app.use(cookieParser(process.env.APP_KEY));

// initialize express-session to allow us track the logged-in user across sessions.
app.use(session({...}));

app.use(flash());

// This middleware will check if user's cookie is still saved in browser and user is not set, then automatically log the user out.
// This usually happens when you stop your express server after login, your cookie still remains saved in the browser.
app.use((req, res, next) => {...});

// Post token.
app.post("/oauth/token", app.oauth.token());

// Get authorization.
app.get("/oauth/authorize", (req, res, next) => {...}, app.oauth.authorize({...}));

// Post authorization.
app.post("/oauth/authorize", function (req, res) {...});
app.get("/log-in", (req, res) => {...});
app.post("/log-in", async (req, res) => {...});
app.get("/log-out", (req, res) => {...});
app.get("/sign-up", async (req, res) => {...});
app.post("/sign-up", async (req, res) => {...});
app.post("/gaction/fulfillment", app.oauth.authenticate(), google_actions_app);
app.get('/healthz', ((req, res) => {...}));
app.listen(port, () => {
   console.log(`Example app listening at port ${port}`);
});
      
      





يوجد الكثير من التعليمات البرمجية ، لكنني سأشرح النقاط الرئيسية. مساران OAuth2 المستخدمان للخادم هما / oauth / token و / oauth / authorize. يتم استخدامها لتلقي رمز جديد أو تحديث الرموز المميزة منتهية الصلاحية. بعد ذلك ، تحتاج إلى جعل الخادم يستجيب لإجراءات Google. ستلاحظ أن نقطة نهاية / gaction / Complment تشير إلى كائن google_actions_app



.



ترسل Google الطلبات إلى خادمك بتنسيق معين وتوفر مكتبة للمساعدة في معالجة هذه الطلبات. فيما يلي الوظائف المطلوبة للتواصل مع Google ، والملف بأكمله موجود هنا. أخيرًا ، هناك نقطة النهاية / healthz ، والتي سأغطيها في نهاية المقالة.



تستخدم نقطة نهاية / gaction / تنفيذ برامج وسيطة تسمى app.oauth.authenticate () ، وكان العمل الشاق لإنجاح خادم OAuth2 هو تشغيل هذه البرامج الوسيطة. يتحقق من أن رمز الحامل المقدم إلينا من Google يشير إلى مستخدم حالي ولم تنته صلاحيته. ثم يرسل المسار الطلب والرد على الكائن google_actions_app



.



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



const { smarthome } = require('actions-on-google');
const mqtt = require('mqtt');
const mqtt_client = mqtt.connect(process.env.CLOUDMQTT_URL);

const model = require('./model');
const { getTokenFromHeader } = require('./util');

mqtt_client.on('connect', () => {
   console.log('Connected to mqtt');
});

const updateHeight = {
   "preset one": (deviceId) => {
       mqtt_client.publish(`/esp32_iot_desk/${deviceId}/command`, "1");
   },
   "preset two": (deviceId) => {
       mqtt_client.publish(`/esp32_iot_desk/${deviceId}/command`, "2");
   },
   "preset three": (deviceId) => {
       mqtt_client.publish(`/esp32_iot_desk/${deviceId}/command`, "3");
   },
};

const google_actions_app = smarthome({...});
google_actions_app.onSync(async (body, headers) => {...});
google_actions_app.onQuery(async (body, headers) => {...});
google_actions_app.onExecute(async (body, headers) => {...});
module.exports = { google_actions_app };
      
      





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



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



"ميزات" (سمة) جهاز Google Smart Home



تستخدم Google ميزات خاصة بالجهاز لتوفير عناصر واجهة المستخدم للتحكم في أجهزتك لـ Google ، ولإنشاء قوالب اتصالات التحكم الصوتي. تتضمن بعض الميزات الإعدادات التالية: ColorSetting و Modes و OnOff و StartStop. لقد استغرق الأمر بعض الوقت لتحديد الميزة التي ستعمل بشكل أفضل في تطبيقي ، لكنني اخترت فيما بعد الأوضاع.



يمكنك التفكير في الأوضاع كقائمة منسدلة حيث يتم تحديد إحدى قيم N المحددة مسبقًا أو ، في حالتي ، ارتفاع محدد مسبقًا. قمت بتسمية الوضع "ارتفاع" والقيم المحتملة هي "الإعداد المسبق واحد" و "الإعداد المسبق الثاني" و "الإعداد المسبق الثلاثة". يتيح لي ذلك التحكم في مكتبي بالقول ، "مرحبًا Google ، اضبط ارتفاع مكتبي على واحد محدد مسبقًا" ، وسوف ترسل Google طلب تنفيذ مطابق إلى نظامي. يمكنك قراءة المزيد حول ميزات أجهزة Google هنا .



المشروع قيد التنفيذ



أخيرًا ، بدأ Google Smart Home وجهاز الكمبيوتر الخاص بي في الاتصال. قبل ذلك ، استخدمت ngrok لتشغيل خادم Express محليًا . الآن وبعد أن أصبح خادمي يعمل بشكل جيد بما فيه الكفاية ، فقد حان الوقت لإتاحته لـ Google في أي وقت. لذلك ، كان من الضروري استضافة التطبيق على Heroku - وهو مزود PaaS الذي يسهل نشر التطبيقات وإدارتها.



واحدة من المزايا الرئيسية لـ Heroku هي الوضع الإضافي. مع الإضافات ، من السهل جدًا إضافة خادم CloudMQTT و Postgres إلى تطبيقك. فائدة أخرى لاستخدام Heroku هي سهولة التجميع والنشر. يكتشف Heroku تلقائيًا الكود الذي تستخدمه ويقوم ببنائه / نشره لك. يمكنك العثور على مزيد من التفاصيل حول هذا من خلال قراءة حول Heroku Buildpacks . في حالتي ، عندما أقوم بدفع الكود إلى بوابة Heroku عن بُعد ، فإنه يقوم بتثبيت جميع الحزم الخاصة بي ، ويزيل جميع تبعيات التطوير وينشر التطبيق ، وكل ذلك باستخدام أمر "git push heroku main" البسيط.



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



أعتقد أن الحاجة إلى Postgres لا تحتاج إلى شرح ، لكن CloudMQTT تستحق المزيد من الاهتمام.



من الإنترنت إلى شبكة خاصة. الطريق الصعب



هناك عدة طرق لتوفير الوصول إلى تطبيق أو ، في حالتي ، جهاز إنترنت الأشياء. الأول هو فتح منفذ على شبكتي المنزلية لإحضار الجهاز إلى الإنترنت. في هذه الحالة ، سيرسل تطبيق Heroku Express طلبًا إلى جهازي باستخدام عنوان IP العام. سيتطلب ذلك مني الحصول على IP ثابت عام بالإضافة إلى IP ثابت لـ ESP32. سيتعين على ESP32 أيضًا العمل كخادم HTTP والاستماع إلى الإرشادات من Heroku طوال الوقت. يعد هذا عبئًا كبيرًا لجهاز يتلقى التعليمات عدة مرات في اليوم.



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



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



من الإنترنت إلى شبكة خاصة عبر إنترنت الأشياء



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



لذا ، يتصل Google بـ Heroku ، يتم تحليل هذا الطلب لتحديد الجهاز المطلوب وحالته أو وضعه الجديد. ينشر تطبيق Heroku بعد ذلك رسالة إلى خادم CloudMQTT المنشور كإضافة على Heroku يوجه الجدول للانتقال إلى الإعداد المسبق الجديد. أخيرًا ، يشترك الجدول في الموضوع ويتلقى رسالة منشورة بواسطة تطبيق Heroku ، وأخيرًا ، يقوم الجدول بضبط ارتفاعه حسب الطلب! في ملف googleactionsapp ، ستلاحظ وجود وظيفة updateHeight تنشر رقم MQTT واحد لمعرف جهاز معين. هذه هي الطريقة التي ينشر بها تطبيق Heroku طلب نقل جدول إلى MQTT.



الخطوة الأخيرة هي تلقي الرسالة على ESP32 وتحريك الجدول. سأعرض لك بعض النقاط البارزة في رمز الجدول أدناه ، وجميع التعليمات البرمجية المصدر هنا .



void setup()
{
 Serial.begin(115200);
...
 tfminis.begin(&Serial2);
 tfminis.setFrameRate(0);

...

 state_machine = new StateMachine();
 state_machine->begin(*t_desk_height, UP_PWM_CHANNEL, DOWN_PWM_CHANNEL);

 setup_wifi();

 client.setServer(MQTT_SERVER_DOMAIN, MQTT_SERVER_PORT);
 client.setCallback(callback);
...
}
      
      





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



void callback(char *topic, byte *message, unsigned int length)
{
 ...

 String messageTemp;

 for (int i = 0; i < length; i++)
 {
   messageTemp += (char)message[i];
 }

 if (messageTemp == "1") {
   state_machine->requestStateChange(ADJUST_TO_PRESET_1_HEIGHT_STATE);
 }

 if (messageTemp == "2") {
   state_machine->requestStateChange(ADJUST_TO_PRESET_2_HEIGHT_STATE);
 }

 if (messageTemp == "3") {
   state_machine->requestStateChange(ADJUST_TO_PRESET_3_HEIGHT_STATE);
 }
...
}
      
      





يسجل جهاز الحالة تغيير الحالة المتلقاة في موضوع MQTT. ثم يقوم بمعالجة الحالة الجديدة في الحلقة الرئيسية.



void loop()
{
 if (!client.connected())
 {
   reconnect();
 }
 client.loop();
 state_machine->processCurrentState();
}
      
      





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



هذا كل شئ! يتم التحكم الصوتي بالكامل في الجدول ويتواصل مع Google لتلقي الأوامر!



ملاحظات حديثة



آخر نقطة نهاية لم أذكرها هي نقطة النهاية / healthz. هذا يرجع إلى حقيقة أن Google تتوقع استجابة سريعة إلى حد ما ، ولا يعمل تحميل تطبيق Heroku عند كل طلب في حالتي. أقوم بإعداد خدمة ping لاختبار نقطة نهاية / healthz كل دقيقة للحفاظ على صحة الخدمة وجاهزة للاستجابة. إذا كنت تخطط للقيام بشيء من هذا القبيل ، فتذكر أنه سيتم إنفاق جميع الساعات المجانية في الكشك. كل شيء على ما يرام الآن: هذا هو التطبيق الوحيد المستخدم في Heroku. بالإضافة إلى ذلك ، مقابل 7 دولارات شهريًا ، يمكنك الترقية إلى خطة Heroku's Hobby التي تحافظ على تشغيل التطبيق.



يأتي بناء جهاز إنترنت الأشياء مع الكثير من النفقات العامة في البداية. لقد صممت الأجهزة ، وأنشأت نظام التحكم ، وقمت بتهيئة خادم MQTT ، وكتبت خادم Express OAuth2 ، وتعلمت كيفية التفاعل مع Google Smart Home من خلال الإجراءات. كانت النفقات الأولية ضخمة ، لكنني أشعر أنني أنجزت الكثير! ناهيك عن أنه يمكن استخدام خادم MQTT وخادم تطبيق Express OAuth2 و Google Smart Home Actions لمشروع آخر. أنا مهتم بالمنازل الذكية ، ويمكنني محاولة توسيع مخزوني من أجهزة إنترنت الأشياء لتشمل أجهزة استشعار تتعقب ما يحدث حول منزلي والإبلاغ عنه من خلال MQTT. ستكون مستشعرات مراقبة التربة ودرجة الحرارة ومستشعرات الضوء مثيرة جدًا للمراقبة والتحليل.



ماذا بعد؟



يتم الآن قياس ارتفاعات سطح العمل غير موثوق بها في أحسن الأحوال. أنا أستخدم بشكل عام جهاز استشعار مسافة الأشعة تحت الحمراء TFMini-S. لوحظ أن ارتفاع الطاولة يتغير قليلاً خلال النهار عندما تتغير الإضاءة المحيطة في الغرفة. لقد طلبت مستشعر زاوية دوران لعد دورات قضيب من خلال الطاولة. هذا من شأنه أن يمنحني حركة أكثر دقة في أي وقت من اليوم. لدي أيضًا وصول إلى الخادم الذي أستضيفه في الطابق السفلي. يمكنني استكشاف خادم Mosquitto MQTT الخاص بي وتطبيقات Node-RED و Express OAuth2 إذا كنت أرغب في استضافة شيء بنفسي. أخيرًا ، الآن جميع الأجهزة الإلكترونية موجودة على مكتبي. أخطط لتنظيم الأجهزة بحيث يكون كل شيء جميلًا وأنيقًا!



شكرا لقرائتك المجلة! للراحة ، أقدم جميع الروابط.




صورة









All Articles