Intel Edison के साथ वेब-सुविधा वाला IoT डिवाइस बनाना

Kenneth Christiansen
Kenneth Christiansen

आज-कल इंटरनेट ऑफ़ थिंग्स हर किसी की ज़रूरतों को पूरा करता है और यह मेरे जैसे टिंकरर और प्रोग्रामर को बहुत खुश करता है. अपने आविष्कारों में जान डालने और उनसे बात करने से अच्छा कुछ नहीं है!

हालांकि, ऐसे IoT डिवाइस आपके लिए परेशान कर सकते हैं जो ऐसे ऐप्लिकेशन इंस्टॉल करते हैं जिनका इस्तेमाल कभी-कभार ही किया जाता है. इसलिए, हम आने वाली वेब टेक्नोलॉजी का फ़ायदा लेते हैं, जैसे कि फ़िज़िकल वेब और वेब ब्लूटूथ. इससे IoT डिवाइसों में रुकावट आती है और उन्हें काम करने में आसानी होती है.

क्लाइंट ऐप्लिकेशन

Web और IoT, दोनों एक जैसे होने चाहिए

इससे पहले कि इंटरनेट ऑफ़ थिंग्स एक बड़ी सफलता लेने में अभी कई अड़चनों से निपटने की कई बाधाएं हैं. इसमें एक रुकावट है, कंपनियों और प्रॉडक्ट के लिए जिसमें लोगों को उनके खरीदे गए हर डिवाइस के लिए ऐप्लिकेशन इंस्टॉल करने होते हैं. इनकी वजह से, लोगों के फ़ोन में कई ऐसे ऐप्लिकेशन हो जाते हैं जिनका वे बहुत कम इस्तेमाल करते हैं.

इस वजह से, हम फ़िज़िकल वेब प्रोजेक्ट के लिए बहुत उत्साहित हैं. इसमें डिवाइसों को, बिना रुकावट के किसी ऑनलाइन वेबसाइट पर यूआरएल ब्रॉडकास्ट करने की सुविधा मिलती है. वेब ब्लूटूथ, वेब यूएसबी, और वेब एनएफ़सी जैसी उभरती हुई वेब टेक्नोलॉजी के साथ, साइटें सीधे डिवाइस से कनेक्ट हो सकती हैं या कम से कम ऐसा करने का सही तरीका बता सकती हैं.

इस लेख में हमने मुख्य रूप से वेब ब्लूटूथ पर फ़ोकस किया है. हालांकि, इस्तेमाल के कुछ उदाहरण, वेब एनएफ़सी या वेब यूएसबी के लिए बेहतर हो सकते हैं. उदाहरण के लिए, अगर सुरक्षा की वजह से आपको फ़िज़िकल कनेक्शन की ज़रूरत हो, तो वेब यूएसबी को प्राथमिकता दी जाती है.

वेबसाइट का इस्तेमाल, प्रोग्रेसिव वेब ऐप्लिकेशन (PWA) के तौर पर भी किया जा सकता है. हम पाठकों को PWA के बारे में Google की दी गई जानकारी देखने का सुझाव देते हैं. PWA ऐसी साइटें होती हैं जिन पर ऐप्लिकेशन जैसा रिस्पॉन्सिव अनुभव होता है. ये ऑफ़लाइन भी काम कर सकती हैं और इन्हें डिवाइस की होम स्क्रीन पर जोड़ा जा सकता है.

एक सबूत के तौर पर, मैंने Intel® Edison Arduino ब्रेकआउट बोर्ड का इस्तेमाल करके, एक छोटा डिवाइस बनाया है. इस डिवाइस में तापमान मापने वाला सेंसर (TMP36) और ऐक्टर (कलर एलईडी कैथोड) भी होता है. इस लेख के आखिर में, इस डिवाइस के स्कीमाटिक्स के बारे में बताया गया है.

ब्रेडबोर्ड.

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 से, Android पर Chrome फ़िज़िकल वेब का समर्थन करता है, जिससे Chrome अपने आस-पास के डिवाइस से भेजे जा रहे यूआरएल देख सकता है. कुछ ऐसी ज़रूरी शर्तें हैं जिनके बारे में डेवलपर को पता होना चाहिए. जैसे, साइटों को सार्वजनिक तौर पर ऐक्सेस करने और एचटीटीपीएस का इस्तेमाल करने के लिए.

एडीस्टोन प्रोटोकॉल के यूआरएल पर 18 बाइट साइज़ की सीमा है. इसलिए, मेरे डेमो ऐप्लिकेशन का यूआरएल काम करे (https://webbt-sensor-hub.appspot.com/), मुझे यूआरएल शॉर्टनर का इस्तेमाल करना होगा.

यूआरएल को ब्रॉडकास्ट करना बहुत आसान है. आपको बस ज़रूरी लाइब्रेरी इंपोर्ट करनी होंगी और कुछ फ़ंक्शन को कॉल करना होगा. BLE चिप चालू होने पर advertiseUrl पर कॉल करके भी ऐसा किया जा सकता है:

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 आस-पास के फ़िज़िकल वेब बीकन की घोषणा करता है.
वेब ऐप्लिकेशन का यूआरएल सूची में शामिल होता है.

सेंसर/एक्चुएटर से संपर्क करना

हम अपने बोर्ड को बेहतर बनाने के बारे में बात करने के लिए जॉनी-फ़ाइव* का इस्तेमाल करते हैं. जॉनी-फ़ाइव के पास TMP36 सेंसर से बात करने का अच्छा अनुभव है.

नीचे एक आसान कोड दिया गया है, जिससे आपको तापमान में होने वाले बदलावों की सूचना पाने के साथ-साथ, एलईडी वाली शुरुआती लाइट का रंग सेट करने में भी मदद मिलेगी.

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 वैरिएबल को अनदेखा किया जा सकता है. इन्हें बाद के सेक्शन में, ब्लूटूथ से इंटरफ़ेस करने के बारे में बताया जाएगा.

जैसा कि आपको Thermometer ऑब्जेक्ट के इंस्टैंशिएट में दिखेगा, जिसमें मैंने ऐनालॉग A0 पोर्ट के ज़रिए TMP36 से बात की है. एलईडी कैथॉड पर मौजूद वोल्टेज लेग, डिजिटल पिन 3, 5, और 6 से जुड़े होते हैं. ये एडिसन आर्डुइनो ब्रेकआउट बोर्ड पर पल्स-विथ मॉड्यूलेशन (PWM) पिन की तरह काम करते हैं.

एडिसन बोर्ड

ब्लूटूथ से बात की जा रही है

ब्लूटूथ से बात करना noble के मुकाबले ज़्यादा आसान नहीं हो सकता.

नीचे दिए गए उदाहरण में, हमने ब्लूटूथ कम ऊर्जा से जुड़ी दो विशेषताएं बनाई हैं: एक एलईडी के लिए और दूसरा तापमान मापने वाले सेंसर के लिए. इससे हमें मौजूदा एलईडी के रंग को पढ़ने और नया रंग सेट करने में मदद मिलती है. इससे, हमें तापमान में बदलाव से जुड़े इवेंट की सदस्यता लेने में मदद मिलती है.

noble की मदद से, एट्रिब्यूट की वैल्यू बनाना बहुत आसान है. आपको बस यह तय करना है कि एट्रिब्यूट कैसे बातचीत करता है और यूयूआईडी को तय करता है. कम्यूनिकेशन के विकल्पों में पढ़ने, लिखने, सूचना देने या इनका कोई भी कॉम्बिनेशन इस्तेमाल करने की सुविधा होती है. ऐसा करने का सबसे आसान तरीका, एक नया ऑब्जेक्ट बनाना और 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 सैंपल लेने का विकल्प चुना और तापमान में कम से कम 1 डिग्री तक बदलाव होने पर ही अपडेट भेजे.

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);
    }
};

यह तापमान मापने वाला सेंसर था. एलईडी का रंग आसान है. ऑब्जेक्ट और "पढ़ने" का तरीका नीचे दिखाया गया है. विशेषता को "पढ़ें" और "लिखना" कार्रवाइयों की अनुमति देने के लिए कॉन्फ़िगर किया गया है और इसका यूयूआईडी, तापमान की विशेषता से अलग है.

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);
};

ऑब्जेक्ट से एलईडी को कंट्रोल करने के लिए, मैंने this._led में एक सदस्य जोड़ा है. इसका इस्तेमाल, जॉनी-फ़ाइव एलईडी ऑब्जेक्ट को स्टोर करने में किया जाता है. मैंने एलईडी के रंग को भी उसकी डिफ़ॉल्ट वैल्यू पर सेट किया है (सफ़ेद, जिसे #ffffff भी कहते हैं).

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

"लिखने" वाले तरीके से एक स्ट्रिंग मिलती है (जिस तरह "पढ़ें" एक स्ट्रिंग भेजता है, ठीक उसी तरह जिसमें सीएसएस कलर कोड शामिल हो सकता है (उदाहरण के लिए: सीएसएस के नाम, जैसे कि 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);
};

अगर हम ब्लेनो मॉड्यूल को शामिल नहीं करते हैं, तो ऊपर दिए गए सभी विकल्प काम नहीं करेंगे. eddystone-beacon blno के साथ तब तक काम नहीं करेगा, जब तक कि आप इसके साथ डिस्ट्रिब्यूट किए गए noble वर्शन का इस्तेमाल नहीं करते. अच्छी बात यह है कि यह बेहद आसान है:

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

अब हमें बस इसकी ज़रूरत है कि हम अपने डिवाइस (यूयूआईडी) और उसकी खासियतों (अन्य यूयूआईडी) का विज्ञापन करें

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

क्लाइंट वेब ऐप्लिकेशन बनाना

उदाहरण के तौर पर, हम यह बता सकते हैं कि क्लाइंट ऐप्लिकेशन के ब्लूटूथ के अलावा दूसरे हिस्से कैसे काम करते हैं. इस बारे में ज़्यादा जानकारी दिए बिना, हम पॉलीमर* में बनाए गए रिस्पॉन्सिव यूज़र इंटरफ़ेस को दिखा सकते हैं. नतीजे में मिलने वाला ऐप्लिकेशन नीचे दिखाया गया है:

फ़ोन पर क्लाइंट ऐप्लिकेशन.
गड़बड़ी का मैसेज.

दाईं ओर एक पुराना वर्शन है, जिसमें एक सामान्य गड़बड़ी लॉग दिखता है, जिसे मैंने डेवलपमेंट को आसान बनाने के लिए जोड़ा है.

वेब ब्लूटूथ की मदद से, ब्लूटूथ स्मार्ट डिवाइसों के साथ आसानी से बातचीत की जा सकती है. इसलिए, अपने कनेक्शन कोड के आसान वर्शन पर नज़र डालते हैं. अगर आपको नहीं पता कि वादे कैसे काम करते हैं, तो ज़्यादा जानकारी के लिए यह संसाधन देखें.

ब्लूटूथ डिवाइस से कनेक्ट करने में वादों की एक शृंखला होती है. सबसे पहले हम डिवाइस (यूयूआईडी: FC00, नाम: Edison) के लिए फ़िल्टर लगाते हैं. इससे एक डायलॉग दिखेगा, ताकि उपयोगकर्ता को वह डिवाइस चुनने की अनुमति मिल जाए जो फ़िल्टर के तौर पर दिया गया है. इसके बाद, हम GATT सेवा से कनेक्ट करके, मुख्य सेवा और उससे जुड़ी विशेषताएं हासिल करते हैं. इसके बाद, हम वैल्यू पढ़ते हैं और सूचना कॉलबैक सेट अप करते हैं.

नीचे दिए गए हमारे कोड का आसान वर्शन सिर्फ़ सबसे नए Web Bluetooth API के साथ काम करता है. इसलिए, Android पर Chrome Dev (M49) की ज़रूरत होती है.

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 (Webब्लूटूथ एपीआई किस तरह का इस्तेमाल करता है) से स्ट्रिंग को पढ़ना और लिखना, उतना ही आसान है जितना कि Node.js साइड पर Buffer को इस्तेमाल करना. हमें सिर्फ़ 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);
},

खास जानकारी

बस हो गया दोस्तों! जैसा कि यहां दिख रहा है, क्लाइंट साइड पर वेब ब्लूटूथ और Edison पर Node.js को ब्लूटूथ लो एनर्जी के साथ इस्तेमाल किया जा सकता है. यह बहुत आसान और दमदार है.

फ़िज़िकल वेब और वेब ब्लूटूथ का इस्तेमाल करके, Chrome डिवाइस को ढूंढता है और उपयोगकर्ता को उससे आसानी से कनेक्ट करने देता है. इसके लिए, बहुत कम इस्तेमाल होने वाले ऐसे ऐप्लिकेशन इंस्टॉल किए जाते हैं जिन्हें शायद उपयोगकर्ता न चाहे. इनमें समय-समय पर अपडेट भी हो सकते हैं.

डेमो

इस बारे में प्रेरणा पाने के लिए कि आप अपने कस्टम इंटरनेट ऑफ़ थिंग्स डिवाइसों से कनेक्ट करने के लिए खुद के वेब ऐप्लिकेशन कैसे बना सकते हैं आप क्लाइंट को आज़मा सकते हैं.

सोर्स कोड

सोर्स कोड यहां उपलब्ध है. समस्याओं की शिकायत करें या पैच भेजें.

स्केच

अगर आपने सच में रोमांच महसूस किया है और आपको इस बारे में फिर से जानना है, तो नीचे दिए गए एडिसन और ब्रेडबोर्ड स्केच को देखें:

स्केच