إنشاء جهاز إنترنت الأشياء (IoT) يمكن استخدامه على الويب باستخدام Intel Edison

Kenneth Christiansen
Kenneth Christiansen

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

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

تطبيق العميل

الويب وإنترنت الأشياء (IoT)، وهي تطابق

لا يزال هناك الكثير من العقبات التي يجب التغلب عليها قبل أن تُحقق إنترنت الأشياء نجاحًا كبيرًا. تتمثّل إحدى العقبات في الشركات والمنتجات التي تتطلّب من المستخدمين تثبيت تطبيقات على كل جهاز يشترونها، ما تؤدي إلى إرباك المستخدمين في ظهور العديد من التطبيقات التي نادرًا ما يستخدمونها.

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

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

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

وكدليل على ذلك، أنشأت جهازًا صغيرًا باستخدام اللوحة المصغّرة من Intel® Edison Arduino. ويحتوي الجهاز على جهاز استشعار الحرارة (TMP36) بالإضافة إلى مُشغّل (كاثود LED ملون). يمكن العثور على المخططات لهذا الجهاز في نهاية هذه المقالة.

لوحة توصيل

Intel Edison هو منتج مثير للاهتمام، لأنه يمكنه تشغيل توزيع كامل لنظام التشغيل Linux*. لذلك يمكنني برمجته بسهولة باستخدام Node.js. يتيح لك أداة التثبيت تثبيت معالج Intel* XDK لتسهيل بدء عملية البدء، مع العلم أنّه يمكنك برمجة التطبيقات وتحميلها إلى جهازك يدويًا أيضًا.

بالنسبة إلى تطبيق Node.js، كنتُ بحاجة إلى ثلاث وحدات من العقد، بالإضافة إلى تبعياتها:

  • eddystone-beacon
  • parse-color
  • johnny-five

في الوحدة الأولى، يتم تثبيت noble تلقائيًا، وهي وحدة العقدة التي أستخدمها للتحدث عبر البلوتوث المنخفض الطاقة.

.

يبدو ملف package.json الخاص بالمشروع على النحو التالي:

{
    "name": "edison-webbluetooth-demo-server",
    "version": "1.0.0",
    "main": "main.js",
    "engines": {
    "node": ">=0.10.0"
    },
    "dependencies": {
    "eddystone-beacon": "^1.0.5",
    "johnny-five": "^0.9.30",
    "parse-color": "^1.0.0"
    }
}

الإعلان عن الموقع الإلكتروني

بدءًا من الإصدار 49، يدعم Chrome على نظام Android الشبكة المادية، التي تتيح لـ Chrome رؤية عناوين URL التي تبثها الأجهزة المحيطة به. هناك بعض المتطلبات التي يجب أن يكون المطوّر على دراية بها، مثل الحاجة إلى أن تكون المواقع متاحة للجميع وأن تستخدم بروتوكول HTTPS.

يسمح بروتوكول Eddystone بحد أقصى لحجم عناوين URL البالغ 18 بايت. لذا لضمان عمل عنوان URL لتطبيقي التجريبي (https://webbt-sensor-hub.appspot.com/)، أحتاج إلى استخدام أداة تقصير عنوان URL.

إنّ عملية بث عنوان URL سهلة للغاية. كل ما عليك القيام به هو استيراد المكتبات المطلوبة واستدعاء بعض الدوال. وإحدى الطرق المتاحة لتنفيذ ذلك هي من خلال استدعاء advertiseUrl عند تشغيل شريحة BLE:

var beacon = require("eddystone-beacon");
var bleno = require('eddystone-beacon/node_modules/bleno');

bleno.on('stateChange', function(state) {    
    if (state === 'poweredOn') {
    beacon.advertiseUrl("https://goo.gl/9FomQC", {name: 'Edison'});
    }   
}

لا يمكن أن يكون هذا أسهل من أي وقت مضى. ترى في الصورة أدناه أن Chrome يعثر على الجهاز بشكل جيد.

يعلن Chrome عن إشارات الويب المادية المجاورة.
يتم إدراج عنوان URL لتطبيق الويب.

الاتصال بجهاز الاستشعار/المشغّل

نحن نستخدم Johnny-Five* للتحدث إلى فريق الدعم المسؤول عن تحسين خدماتنا. يتمتع "جوني فايف" بأفكار رائعة للتحدث إلى جهاز الاستشعار TMP36.

يمكنك العثور أدناه على رمز بسيط ليتم إعلامك عند حدوث تغيرات في درجات الحرارة بالإضافة إلى ضبط لون مصباح LED الأولي.

var five = require("johnny-five");
var Edison = require("edison-io");
var board = new five.Board({
    io: new Edison()
});

board.on("ready", function() {
    // Johnny-Five's Led.RGB class can be initialized with
    // an array of pin numbers in R, G, B order.
    // Reference: http://johnny-five.io/api/led.rgb/#parameters
    var led = new five.Led.RGB([ 3, 5, 6 ]);

    // Johnny-Five's Thermometer class provides a built-in
    // controller definition for the TMP36 sensor. The controller
    // handles computing a Celsius (also Fahrenheit & Kelvin) from
    // a raw analog input value.
    // Reference: http://johnny-five.io/api/thermometer/
    var temp = new five.Thermometer({
    controller: "TMP36",
    pin: "A0",
    });

    temp.on("change", function() {
    temperatureCharacteristic.valueChange(this.celsius);
    });

    colorCharacteristic._led = led;
    led.color(colorCharacteristic._value);
    led.intensity(30);
});

يمكنك تجاهل متغيّرات *Characteristic أعلاه في الوقت الحالي، وسيتم تعريفها في القسم لاحقًا حول التفاعل مع البلوتوث.

كما تلاحظ عند إنشاء مثيل لكائن "ميزان الحرارة"، أتحدّث إلى جهاز TMP36 عبر منفذ A0 التناظري. يتم توصيل ساقيات الجهد على قطب LED الملون بدبابيس رقمية 3 و5 و6، والتي تشكل دبابيس تعديل عرض النبض (PWM) على لوحة المصغَّرة "إديسون آردوينو" المصغَّرة.

لوح إديسون

جارٍ التحدّث إلى البلوتوث

أصبح التحدّث إلى البلوتوث أسهل بكثير من استخدام "noble".

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

إنّ إنشاء سمة سهلة للغاية باستخدام "noble". كل ما عليك فعله هو تحديد كيفية تواصل الخاصية وتحديد UUID. خيارات التواصل هي القراءة أو الكتابة أو الإعلام أو أي مجموعة من ذلك. أسهل طريقة لإجراء ذلك هي إنشاء كائن جديد واكتسابه من bleno.Characteristic.

ويظهر الكائن المميز الناتج كما يلي:

var TemperatureCharacteristic = function() {
    bleno.Characteristic.call(this, {
    uuid: 'fc0a',
    properties: ['read', 'notify'],
    value: null
    });
    
    this._lastValue = 0;
    this._total = 0;
    this._samples = 0;
    this._onChange = null;
};

util.inherits(TemperatureCharacteristic, bleno.Characteristic);

نحن نخزِّن قيمة درجة الحرارة الحالية باستخدام المتغيّر this._lastValue. نحتاج إلى إضافة طريقة onReadRequest وترميز قيمة القراءة للعمل.

TemperatureCharacteristic.prototype.onReadRequest = function(offset, callback) {
    var data = new Buffer(8);
    data.writeDoubleLE(this._lastValue, 0);
    callback(this.RESULT_SUCCESS, data);
};

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

TemperatureCharacteristic.prototype.onSubscribe = function(maxValueSize, updateValueCallback) {
    console.log("Subscribed to temperature change.");
    this._onChange = updateValueCallback;
    this._lastValue = undefined;
};

TemperatureCharacteristic.prototype.onUnsubscribe = function() {
    console.log("Unsubscribed to temperature change.");
    this._onChange = null;
};

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

TemperatureCharacteristic.prototype.valueChange = function(value) {
    this._total += value;
    this._samples++;
    
    if (this._samples < NO_SAMPLES) {
        return;
    }
        
    var newValue = Math.round(this._total / NO_SAMPLES);
    
    this._total = 0;
    this._samples = 0;
    
    if (this._lastValue && Math.abs(this._lastValue - newValue) < 1) {
        return;
    }
    
    this._lastValue = newValue;
    
    console.log(newValue);
    var data = new Buffer(8);
    data.writeDoubleLE(newValue, 0);
    
    if (this._onChange) {
        this._onChange(data);
    }
};

كان ذلك جهاز استشعار الحرارة. إن مصباح LED الملون أكثر بساطة. يتم عرض الكائن وطريقة "القراءة" أدناه. يتم تكوين الخاصية للسماح بعمليات "القراءة" و "الكتابة" ولها معرّف UUID مختلف عن خاصية درجة الحرارة.

var ColorCharacteristic = function() {
    bleno.Characteristic.call(this, {
    uuid: 'fc0b',
    properties: ['read', 'write'],
    value: null
    });
    this._value = 'ffffff';
    this._led = null;
};

util.inherits(ColorCharacteristic, bleno.Characteristic);

ColorCharacteristic.prototype.onReadRequest = function(offset, callback) {
    var data = new Buffer(this._value);
    callback(this.RESULT_SUCCESS, data);
};

للتحكّم في مصباح LED من العنصر، تتم إضافة عضو في this._led أستخدمه لتخزين مصباح LED الخاص بـ Johnny-Five. قمت أيضًا بتعيين لون مصباح LED على قيمته التلقائية (أبيض، يُعرف أيضًا باسم #ffffff).

board.on("ready", function() {
    ...
    colorCharacteristic._led = led;
    led.color(colorCharacteristic._value);
    led.intensity(30);
    ...
}

تتلقى طريقة "الكتابة" سلسلة (تمامًا مثلما ترسل "للقراءة" سلسلة)، والتي يمكن أن تتكون من رمز لون CSS (على سبيل المثال: أسماء CSS مثل rebeccapurple أو الرموز السداسية مثل #ff00bb). وأستخدم وحدة عقدة تسمى parse-color للحصول دائمًا على القيمة السداسية وهي ما يتوقعه جوني فايف.

ColorCharacteristic.prototype.onWriteRequest = function(data, offset, withoutResponse, callback) {
    var value = parse(data.toString('utf8')).hex;
    if (!value) {
        callback(this.RESULT_SUCCESS);
        return;
    }
    
    this._value = value;
    console.log(value);

    if (this._led) {
        this._led.color(this._value);
    }
    callback(this.RESULT_SUCCESS);
};

لن ينجح كل ما سبق إذا لم يتم تضمين وحدة bleno. لن يعمل eddystone-beacon مع bleno إلا إذا كنت تستخدم الإصدار noble الموزَّع معه. لحسن الحظ أن القيام بذلك بسيط للغاية:

var bleno = require('eddystone-beacon/node_modules/bleno');
var util = require('util');

كل ما نحتاجه الآن هو الإعلان عن الجهاز (UUID) وخصائصه (معرّفات UUID أخرى).

bleno.on('advertisingStart', function(error) {
    ...
    bleno.setServices([
        new bleno.PrimaryService({
        uuid: 'fc00',
        characteristics: [
            temperatureCharacteristic, colorCharacteristic
        ]
        })
    ]);
});

إنشاء تطبيق الويب للعميل

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

تطبيق العميل على الهاتف.
رسالة الخطأ

يُظهر الجانب الأيمن إصدارًا سابقًا يعرض سجل خطأ بسيطًا أضفته لتسهيل التطوير.

تسهل تقنية Web Bluetooth الاتصال بأجهزة البلوتوث منخفض الطاقة، لذلك دعونا نلقِ نظرة على إصدار مبسط من رمز الاتصال. إذا كنت لا تعرف آلية تطبيق الوعود، يمكنك الاطّلاع على هذا المرجع قبل قراءة المزيد.

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

لا يعمل الإصدار المبسّط من الرمز البرمجي أدناه إلّا مع أحدث واجهة برمجة تطبيقات Web Bluetooth API، وبالتالي يتطلب الإصدار Chrome Dev (M49) على Android.

navigator.bluetooth.requestDevice({
    filters: [{ name: 'Edison' }],
    optionalServices: [0xFC00]
})

.then(device => device.gatt.connect())

.then(server => server.getPrimaryService(0xFC00))

.then(service => {
    let p1 = () => service.getCharacteristic(0xFC0B)
    .then(characteristic => {
    this.colorLedCharacteristic = characteristic;
    return this.readLedColor();
    });

    let p2 = () => service.getCharacteristic(0xFC0A)
    .then(characteristic => {
    characteristic.addEventListener(
        'characteristicvaluechanged', this.onTemperatureChange);
    return characteristic.startNotifications();
    });

    return p1().then(p2);
})

.catch(err => {
    // Catch any error.
})
            
.then(() => {
    // Connection fully established, unless there was an error above.
});

إنّ قراءة وكتابة سلسلة من DataView / ArrayBuffer (ما تستخدمه واجهة برمجة تطبيقات WebBluetooth) سهلة مثل استخدام Buffer على جانب Node.js. كل ما نحتاج إلى استخدامه هو TextEncoder وTextDecoder:

readLedColor: function() {
    return this.colorLedCharacteristic.readValue()
    .then(data => {
    // In Chrome 50+, a DataView is returned instead of an ArrayBuffer.
    data = data.buffer ? data : new DataView(data);
    let decoder = new TextDecoder("utf-8");
    let decodedString = decoder.decode(data);
    document.querySelector('#color').value = decodedString;
    });
},

writeLedColor: function() {
    let encoder = new TextEncoder("utf-8");
    let value = document.querySelector('#color').value;
    let encodedString = encoder.encode(value.toLowerCase());

    return this.colorLedCharacteristic.writeValue(encodedString);
},

فضلاً عن ذلك، يسهل التعامل مع حدث characteristicvaluechanged لجهاز استشعار الحرارة:

onTemperatureChange: function(event) {
    let data = event.target.value;
    // In Chrome 50+, a DataView is returned instead of an ArrayBuffer.
    data = data.buffer ? data : new DataView(data);
    let temperature = data.getFloat64(0, /*littleEndian=*/ true);
    document.querySelector('#temp').innerHTML = temperature.toFixed(0);
},

ملخّص

هؤلاء هم يا رفاق! كما ترى، فإن التواصل مع Bluetooth منخفض الطاقة باستخدام Web Bluetooth من جهة العميل وNode.js في Edison أمر سهل وفعال للغاية.

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

الخصائص الديموغرافية

يمكنك تجربة العميل للحصول على الإلهام حول كيفية إنشاء تطبيقات الويب الخاصة بك للاتصال بأجهزة إنترنت الأشياء (IoT) المخصّصة.

رمز مصدر

يتوفّر رمز المصدر هنا. يُرجى عدم التردد في الإبلاغ عن المشاكل أو إرسال رموز تصحيح.

رسم

إذا كنت مغامرًا حقًا وتريد إعادة إنتاج ما فعلته، فراجع رسم إديسون ولوحة توصيل الأسلاك أدناه:

رسم