Intel Edison ile web özellikli bir IoT cihazı oluşturma

Kenneth Christiansen
Kenneth Christiansen

Bugünlerde Nesnelerin İnterneti herkesin dudaklarında. Bu, benim gibi tamirciler ve programcılar için de çok heyecan verici. Kendi icatlarınızı hayata geçirip onlarla konuşabilmekten daha güzel bir şey olamaz.

Ancak nadiren kullandığınız uygulamaları yükleyen IoT cihazları can sıkıcı olabilir. Bu nedenle, IoT cihazlarını daha sezgisel ve daha az rahatsız edici hale getirmek için Fiziksel Web ve Web Bluetooth gibi yeni web teknolojilerinden yararlanıyoruz.

İstemci uygulaması

Web ve IoT bir arada

Nesnelerin İnterneti büyük bir başarıya ulaşmak için aşılması gereken birçok engel var. Bu engellerden biri, kullanıcıların satın aldıkları her cihaz için uygulama yüklemelerini gerektiren şirketler ve ürünlerdir. Bu durum, kullanıcıların telefonlarında nadiren kullandıkları çok sayıda uygulamayla karışıklığa yol açar.

Bu nedenle, cihazların rahatsız edici olmayacak şekilde çevrimiçi bir web sitesinin URL'sini yayınlamasına olanak tanıyan Fiziksel Web projesi bizi çok heyecanlandırıyor. Web Bluetooth, Web USB ve Web NFC gibi yeni web teknolojileriyle kombinasyon halinde, siteler cihaza doğrudan bağlanabilir veya en azından bunun doğru yolunu açıklayabilir.

Bu makalede öncelikle Web Bluetooth'a odaklanıyor olsak da, bazı kullanım alanları Web NFC veya Web USB için daha uygun olabilir. Örneğin, güvenlik nedeniyle fiziksel bir bağlantıya ihtiyacınız varsa Web USB tercih edilir.

Bu web sitesi progresif web uygulaması (PWA) olarak da kullanılabilir. Okuyucuları, PWA'larla ilgili Google'ın açıklamasına göz atmaya teşvik ediyoruz. PWA'lar, duyarlı, uygulama benzeri bir kullanıcı deneyimi sunan, çevrimdışı çalışabilen ve cihazın ana ekranına eklenebilen sitelerdir.

Kavram kanıtı olarak, Intel® Edison Arduino patlama kartını kullanarak küçük bir cihaz ürettim. Cihazda bir sıcaklık sensörü (TMP36) ve bir aktüatör (renkli LED katot) bulunur. Bu cihazın şemalarını bu makalenin sonunda bulabilirsiniz.

Devre tahtası.

Intel Edison, tam Linux* dağıtımını çalıştırabildiği için ilginç bir üründür. Bu sayede, Node.js kullanarak kolayca programlayabiliyorum. Yükleyici, Intel* XDK'yi yüklemenize olanak tanır. Böylece programı kolayca başlatabilirsiniz. Bununla birlikte, programı cihazınıza manuel olarak da yükleyebilirsiniz.

Node.js uygulamam için üç düğüm modülüne ve bunların bağımlılıklarına ihtiyacım vardı:

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

İlki, Bluetooth Düşük Enerji üzerinden konuşmak için kullandığım düğüm modülü olan noble'i otomatik olarak yüklüyor.

Proje için package.json dosyası aşağıdaki gibi görünür:

{
    "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"
    }
}

Web sitesini duyurma

Sürüm 49'dan itibaren Android'deki Chrome, Chrome'un etrafındaki cihazlar tarafından yayınlanan URL'leri görmesine olanak tanıyan Fiziksel Web'i desteklemektedir. Geliştiricinin, sitelerin herkesin erişimine açık olması ve HTTPS kullanması gibi bazı gereksinimler vardır.

Eddystone protokolü, URL'lerde 18 baytlık bir boyut sınırına sahiptir. Demo uygulamamın URL'sinin (https://webbt-sensor-hub.appspot.com/) çalışması için bir URL kısaltıcı kullanmam gerekiyor.

URL'yi yayınlamak oldukça basit bir işlemdir. Tek yapmanız gereken, gerekli kitaplıkları içe aktarıp birkaç işlev çağırmak. Bunu yapmanın bir yolu, BDE çipi açıkken advertiseUrl çağrısı yapmaktır:

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

Bu gerçekten daha kolay olamazdı. Aşağıdaki resimde Chrome'un cihazı güzel bulduğunu görüyorsunuz.

Chrome, yakındaki Fiziksel Web işaretçilerini duyurur.
Web uygulaması URL'si listelenir.

Sensör/aktüatör ile iletişim kurma

Yönetim kurulu geliştirmelerimizle konuşmak için Johnny-Five* şirketini kullanıyoruz. Johnny-Five'ın TMP36 sensörüyle konuştuğu güzel bir soyutlama var.

Aşağıda, ilk LED rengini belirlemenin yanı sıra sıcaklık değişiklikleri hakkında bildirim almayı sağlayan basit bir kod bulunmaktadır.

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

Şimdilik yukarıdaki *Characteristic değişkenlerini yoksayabilirsiniz. Bunlar, Bluetooth ile arayüzle ilgili sonraki bölümde tanımlanacaktır.

Termometre nesnesinin örneklendirmesinde fark edebileceğiniz gibi, TMP36 ile analog A0 bağlantı noktası üzerinden konuşuyorum. Renkli LED katotun üzerindeki voltaj ayakları 3, 5 ve 6 numaralı dijital pimlere bağlıdır. Bunlar, Edison Arduino patlama panosundaki nabız-genişliği modülasyon (PWM) pimleridir.

Edison panosu

Bluetooth ile konuşuluyor

Bluetooth ile konuşmak, noble ile olduğundan çok daha kolay olamaz.

Aşağıdaki örnekte, biri LED ve sıcaklık sensörü için olmak üzere iki Bluetooth Düşük Enerji özelliği oluşturuyoruz. Işık kutusu, mevcut LED rengini okumamızı ve yeni bir renk ayarlamamızı sağlar. İkincisi, sıcaklık değişikliği etkinliklerine abone olmamızı sağlıyor.

noble ile özellik oluşturmak oldukça kolaydır. Tek yapmanız gereken, özelliğin nasıl iletişim kurduğunu ve bir UUID tanımlamaktır. İletişim seçenekleri okuma, yazma, bildir veya bunların herhangi bir birleşimidir. Bunu yapmanın en kolay yolu, yeni bir nesne oluşturmak ve nesneyi bleno.Characteristic nesnesinden devralmaktır.

Ortaya çıkan karakteristik nesne aşağıdaki gibi görünür:

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

Mevcut sıcaklık değerini this._lastValue değişkeninde saklıyoruz. Bir onReadRequest yöntemi eklememiz ve "okuma"nın çalışması için değeri kodlamamız gerekir.

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

"Bilgilendirme" için abonelikleri ve aboneliği iptal etmeyi sağlayacak bir yöntem eklememiz gerekir. Temel olarak, bir geri çağırmayı saklarız. Göndermek istediğimiz yeni bir sıcaklık nedenimiz olduğunda, geri çağırmayı yeni değerle (yukarıda olduğu gibi kodlanan) çağırırız.

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

Değerler biraz dalgalanabileceğinden TMP36 sensöründen aldığımız değerleri yumuşatmamız gerekir. 100 örneğin ortalamasını almayı ve yalnızca sıcaklık en az 1 derece değiştiğinde güncelleme göndermeyi tercih ettim.

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

Sıcaklık sensörü buydu. Renkli LED daha basittir. Nesne ve "okuma" yöntemi aşağıda gösterilmiştir. Bu özellik, "okuma" ve "yazma" işlemlerine izin verecek şekilde yapılandırılmıştır ve sıcaklık özelliğinden farklı bir UUID'ye sahiptir.

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'i nesneden kontrol etmek için Johnny-Five LED nesnesini saklamak istediğim bir this._led üyesi ekliyorum. Ayrıca LED\'in rengini de varsayılan değerine (beyaz, #ffffff) ayarlıyorum.

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

"write" yöntemi, CSS renk kodundan (Örneğin: rebeccapurple gibi CSS adları veya #ff00bb gibi onaltılık kodlar) oluşan bir dize alır ("okuma"nın gönderdiği dize gibi). Johnny-Five'ın beklediği gibi her zaman onaltılık değeri elde etmek için parse-color adlı bir düğüm modülü kullanıyorum.

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 modülü dahil edilmezse yukarıdakilerin hiçbiri çalışmaz. eddystone-beacon, onunla birlikte dağıtılan noble sürümünü kullanmadığınız sürece bleno ile çalışmaz. Neyse ki bunu yapmak oldukça basit:

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

Artık tek ihtiyacımız olan, cihazmızın (UUID) ve özelliklerinin (diğer UUID'ler) reklamını yapmasıdır.

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

İstemci web uygulamasını oluşturma

Örnek olarak, istemci uygulamasının Bluetooth olmayan parçalarının çalışma şekliyle ilgili çok fazla hataya girmeden Polymer'de* oluşturulmuş duyarlı bir kullanıcı arayüzünü gösterebiliriz. Elde edilen uygulama aşağıda gösterilmiştir:

Telefondaki istemci uygulaması.
Hata mesajı.

Sağ tarafta ise geliştirmeyi kolaylaştırmak için eklediğim basit bir hata günlüğünü gösteren önceki sürüm gösterilmektedir.

Web Bluetooth, Bluetooth Düşük Enerjili cihazlarla iletişim kurmayı kolaylaştırır. Şimdi bağlantı kodumun basitleştirilmiş bir sürümüne bakalım. Vaatlerin işleyiş şeklini bilmiyorsanız daha fazla okumadan önce bu kaynağa göz atın.

Bir Bluetooth cihazına bağlanma, bir dizi söz içerir. İlk olarak cihaz (UUID: FC00, ad: Edison) için filtrelenir. Kullanıcının, filtreye göre cihazı seçmesine izin veren bir iletişim kutusu görüntülenir. Ardından GATT hizmetine bağlanıp birincil hizmeti ve ilişkili özellikleri alırız. Ardından değerleri okuyup bildirim geri çağırmalarını ayarlarız.

Aşağıdaki kodumuzun basitleştirilmiş sürümü yalnızca en yeni Web Bluetooth API ile çalıştığından Android'de Chrome Geliştirici (M49) gerektirir.

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

Bir DataView / ArrayBuffer öğesinden (WebBluetooth API'nin kullanır) dize okumak ve yazmak, Node.js tarafında Buffer kullanmak kadar kolaydır. Yalnızca TextEncoder ve TextDecoder öğelerini kullanmamız gerekiyor:

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

Sıcaklık sensörü için characteristicvaluechanged etkinliğini işlemek de oldukça kolaydır:

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

Özet

Hepsi bu kadar! Gördüğünüz gibi, istemci tarafında Web Bluetooth ve Edison'da Node.js kullanarak Bluetooth Düşük Enerji ile iletişim kurmak oldukça kolay ve çok güçlüdür.

Chrome, Fiziksel Web ve Web Bluetooth'unu kullanarak cihazı bulur ve kullanıcının istemediği ve zaman zaman güncellenebilecek, nadir kullanılan uygulamaları yüklemeden kolayca cihaza bağlanmasına olanak tanır.

Demografi

Özel Nesnelerin İnterneti cihazlarınıza bağlanmak üzere kendi web uygulamalarınızı nasıl oluşturabileceğiniz konusunda ilham almak için istemciyi deneyebilirsiniz.

Kaynak kodu

Kaynak kodunu burada bulabilirsiniz. Sorunları bildirebilir veya yama gönderebilirsiniz.

Skeç

Gerçekten maceracıysanız ve benim yaptığımı yeniden oluşturmak istiyorsanız aşağıdaki Edison ve devre tahtası çizimine bakın:

Skeç