อินเทอร์เน็ตของสรรพสิ่งเป็นหัวข้อที่ทุกคนพูดถึงกันในปัจจุบัน ซึ่งทำให้นักทดลองและโปรแกรมเมอร์อย่างเราตื่นเต้นมาก ไม่มีอะไรเจ๋งไปกว่าการทำให้สิ่งประดิษฐ์ของคุณมีชีวิตขึ้นมาและพูดคุยกับสิ่งประดิษฐ์เหล่านั้น
แต่อุปกรณ์ IoT ที่ติดตั้งแอปที่คุณไม่ค่อยได้ใช้อาจสร้างความรำคาญได้ เราจึงใช้ประโยชน์จากเทคโนโลยีเว็บที่กำลังจะเกิดขึ้น เช่น Physical Web และ Web Bluetooth เพื่อทำให้อุปกรณ์ IoT ใช้งานง่ายขึ้นและไม่รบกวน
เว็บและ IoT ที่ทุกคนต้องมี
ยังคงมีอุปสรรคอีกมากมายที่ต้องฝ่าฟันก่อนที่อินเทอร์เน็ตออฟธิงส์จะประสบความสำเร็จอย่างยิ่งใหญ่ อุปสรรคหนึ่งคือบริษัทและผลิตภัณฑ์ ที่บังคับให้ผู้ใช้ติดตั้งแอปสำหรับอุปกรณ์แต่ละเครื่องที่ซื้อ ซึ่งทำให้โทรศัพท์ของผู้ใช้มีแอปมากมายที่ไม่ค่อยได้ใช้
ด้วยเหตุนี้ เราจึงตื่นเต้นมากกับโปรเจ็กต์ Physical Web ซึ่งช่วยให้อุปกรณ์สามารถออกอากาศ URL ไปยังเว็บไซต์ออนไลน์โดยไม่รบกวน เว็บไซต์สามารถเชื่อมต่อกับอุปกรณ์ได้โดยตรงหรืออย่างน้อยจะอธิบายวิธีการที่เหมาะสมในการจับคู่กับเทคโนโลยีเว็บใหม่ๆ เช่น เว็บบลูทูธ, Web USB และ Web NFC
แม้ว่าเราจะเน้นที่ Web Bluetooth เป็นหลักในบทความนี้ แต่ Use Case บางรายการอาจเหมาะกับ Web NFC หรือ Web USB มากกว่า เช่น แนะนำให้ใช้ Web USB หากคุณต้องใช้การเชื่อมต่อแบบใช้สายเพื่อเหตุผลด้านความปลอดภัย
เว็บไซต์ยังทำหน้าที่เป็น Progressive Web App (PWA) ได้อีกด้วย เราขอแนะนำให้ผู้อ่านอ่านคำอธิบายของ Google เกี่ยวกับ PWA PWA คือเว็บไซต์ที่มอบประสบการณ์การใช้งานที่ปรับเปลี่ยนตามอุปกรณ์และคล้ายกับแอปของผู้ใช้ สามารถทำงานแบบออฟไลน์และเพิ่มลงในหน้าจอหลักของอุปกรณ์ได้
เราได้สร้างอุปกรณ์ขนาดเล็กโดยใช้บอร์ด Breakout Arduino ของ Intel® Edison เพื่อพิสูจน์แนวคิด อุปกรณ์นี้มีเซ็นเซอร์อุณหภูมิ (TMP36) และแอคชูเอเตอร์ (แคโทด LED สี) สคีมาของอุปกรณ์นี้จะอยู่ในตอนท้ายของบทความนี้
Intel Edison เป็นผลิตภัณฑ์ที่น่าสนใจเนื่องจากสามารถเรียกใช้ Linux* ได้อย่างเต็มรูปแบบ ดังนั้นฉันจึงเขียนโปรแกรมโดยใช้ Node.js ได้อย่างง่ายดาย โปรแกรมติดตั้งให้คุณติดตั้ง Intel* XDK ซึ่งจะช่วยให้คุณเริ่มต้นใช้งานได้อย่างง่ายดาย แต่คุณสามารถตั้งโปรแกรมและอัปโหลดไปยังอุปกรณ์ของคุณได้ด้วยตนเองเช่นกัน
สําหรับแอป Node.js ฉันต้องใช้โมดูล Node 3 รายการ รวมถึงข้อกําหนดต่อไปนี้
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 มีขีดจำกัดขนาด 18 ไบต์สำหรับ URL ดังนั้นฉันจึงต้องใช้เครื่องมือย่อ URL เพื่อให้ URL ของแอปเดโมใช้งานได้ (https://webbt-sensor-hub.appspot.com/)
การออกอากาศ URL นั้นค่อนข้างง่าย คุณเพียงแค่ต้องนำเข้าไลบรารีที่จำเป็น
และเรียกใช้ฟังก์ชัน 2-3 รายการ วิธีหนึ่งในการดำเนินการนี้คือเรียกใช้ 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 ค้นหาอุปกรณ์ได้อย่างง่ายดาย
การสื่อสารกับเซ็นเซอร์/ตัวกระตุ้น
เราใช้ Johnny-Five* ในการพูดคุยกับทีมเพิ่มประสิทธิภาพ 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) บนบอร์ด Breakout ของ Arduino สำหรับ Edison
การพูดคุยกับบลูทูธ
การสื่อสารกับบลูทูธนั้นง่ายกว่าที่เคยด้วย noble
ในตัวอย่างต่อไปนี้ เราสร้างลักษณะเฉพาะของบลูทูธพลังงานต่ำ 2 อย่าง คือแบบหนึ่งสำหรับไฟ 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 รายการมาคำนวณ และส่งการอัปเดตเฉพาะเมื่ออุณหภูมิเปลี่ยนแปลงอย่างน้อย 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);
}
};
นั่นคือเซ็นเซอร์อุณหภูมิ LED สีจะใช้งานง่ายกว่า ออบเจ็กต์และเมธอด "read" แสดงอยู่ด้านล่าง ลักษณะได้รับการกําหนดค่าให้อนุญาตการดำเนินการ "อ่าน" และ "เขียน" และมี 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);
...
}
เมธอด "write" จะได้รับสตริง (เช่นเดียวกับ "read" ที่ส่งสตริง) ซึ่งอาจประกอบด้วยรหัสสี CSS (ตัวอย่างเช่น ชื่อ CSS เช่น rebeccapurple
หรือรหัสเลขฐาน 16 เช่น #ff00bb
) ฉันใช้โมดูลโหนดชื่อ parse-color เพื่อให้ได้ค่าเลขฐาน 16 ตามที่ Johnny-Five คาดไว้เสมอ
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
เวอร์ชันที่มาพร้อมกับ bleno โชคดีที่การดำเนินการนี้ทำได้ง่ายมาก เพียงทำดังนี้
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* เป็นตัวอย่าง โดยไม่คำนึงถึงข้อบกพร่องในการทำงานของส่วนที่ไม่ใช่บลูทูธของแอปไคลเอ็นต์มากเกินไป แอปที่ได้จะแสดงอยู่ด้านล่าง
ด้านขวาแสดงเวอร์ชันก่อนหน้า ซึ่งแสดงบันทึกข้อผิดพลาดง่ายๆ ที่เราเพิ่มไว้เพื่อพัฒนาให้ง่ายขึ้น
เว็บบลูทูธช่วยให้สื่อสารกับอุปกรณ์บลูทูธพลังงานต่ำได้ง่าย ดังนั้นเรามาดูโค้ดการเชื่อมต่อเวอร์ชันที่เข้าใจง่ายกัน หากไม่ทราบวิธีการทํางานของคํามั่นสัญญา โปรดดูแหล่งข้อมูลนี้ก่อนอ่านต่อ
การเชื่อมต่อกับอุปกรณ์บลูทูธเกี่ยวข้องกับการสัญญาต่อกันเป็นลำดับ
ก่อนอื่นเราจะกรองอุปกรณ์ (UUID: 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 API ใช้) นั้นง่ายพอๆ กับการใช้ 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);
},
สรุป
เท่านี้ก่อนนะ ดังที่คุณเห็น การสื่อสารกับบลูทูธพลังงานต่ำโดยใช้ Web Bluetooth ฝั่งไคลเอ็นต์และ Node.js บน Edison นั้นค่อนข้างง่ายและมีประสิทธิภาพมาก
เมื่อใช้เว็บที่จับต้องได้และเว็บบลูทูธ Chrome จะค้นหาอุปกรณ์และช่วยให้ผู้ใช้เชื่อมต่อกับอุปกรณ์ได้อย่างง่ายดายโดยไม่ต้องติดตั้งแอปพลิเคชันที่ใช้นานๆ ครั้งซึ่งผู้ใช้อาจไม่ต้องการ และแอปพลิเคชันดังกล่าวอาจมีการอัปเดตเป็นครั้งคราว
สาธิต
ลองใช้ไคลเอ็นต์เพื่อรับแรงบันดาลใจเกี่ยวกับวิธีสร้างเว็บแอปของตัวเองเพื่อเชื่อมต่อกับอุปกรณ์อินเทอร์เน็ตของสรรพสิ่งที่คุณกำหนดเอง
ซอร์สโค้ด
ดูซอร์สโค้ดได้ที่นี่ โปรดรายงานปัญหาหรือส่งแพตช์
ภาพร่าง
หากต้องการลองทำตามสิ่งที่เราทํา โปรดดูภาพสเก็ตช์ของ Edison และ Breadboard ด้านล่าง