ปัจจุบันอินเทอร์เน็ตของสรรพสิ่งเป็นสิ่งที่ทุกคนรออยู่ ทำให้คนแปลกหน้าและโปรแกรมเมอร์อย่างผมตื่นเต้นมาก ไม่มีอะไรจะดีไปกว่าการสร้างสิ่งประดิษฐ์ของคุณเองและ สามารถพูดคุยกับพวกเขาได้!
แต่อุปกรณ์ IoT ที่ติดตั้งแอปที่ไม่ค่อยได้ใช้นั้นน่ารำคาญ เราจึงใช้ประโยชน์จากเทคโนโลยีเว็บที่กำลังจะเกิดขึ้น เช่น Physical Web และ Web Bluetooth เพื่อทำให้อุปกรณ์ IoT ใช้งานง่ายขึ้นและรบกวนน้อยลง
เว็บและ IoT เป็นการจับคู่ที่ตรงกัน
ยังมีอุปสรรคอีกมากมายที่ต้องเอาชนะก่อนที่ Internet of Things จะประสบความสำเร็จอย่างยิ่งใหญ่ อุปสรรคอย่างหนึ่งก็คือบริษัทและผลิตภัณฑ์ที่ต้องให้ผู้ใช้ติดตั้งแอปสำหรับอุปกรณ์แต่ละเครื่องที่ซื้อ ซึ่งทำให้โทรศัพท์ของผู้ใช้เกะกะด้วยแอปมากมายที่ไม่ค่อยใช้
ด้วยเหตุนี้เราจึงตื่นเต้นมากกับโปรเจ็กต์ Physical Web ซึ่งทำให้อุปกรณ์เผยแพร่ URL ไปยังเว็บไซต์ออนไลน์ในลักษณะที่ไม่เป็นการรบกวนได้ เมื่อใช้ร่วมกับเทคโนโลยีเว็บแบบใหม่ เช่น Web Bluetooth, Web USB และ Web NFC เว็บไซต์จะเชื่อมต่อกับอุปกรณ์ได้โดยตรง หรืออย่างน้อยก็อธิบายวิธีที่เหมาะสมในการดำเนินการดังกล่าว
แม้ว่าเราจะเน้นที่เว็บบลูทูธในบทความนี้เป็นหลัก แต่บางกรณีการใช้งานอาจเหมาะกว่า Web NFC หรือ Web USB มากกว่า ตัวอย่างเช่น เราขอแนะนำให้ใช้ Web USB หากคุณต้องใช้การเชื่อมต่อจริงด้วยเหตุผลด้านความปลอดภัย
นอกจากนี้ เว็บไซต์ยังทำหน้าที่เป็น Progressive Web App (PWA) ได้ด้วย เราขอแนะนำให้ผู้อ่านอ่านคำอธิบายของ Google เกี่ยวกับ PWA PWA คือเว็บไซต์ที่มีประสบการณ์การใช้งานคล้ายกับแอปที่ปรับเปลี่ยนตามอุปกรณ์ สามารถทำงานแบบออฟไลน์ และเพิ่มลงในหน้าจอหลักของอุปกรณ์ได้
เพื่อเป็นการพิสูจน์แนวคิด ผมจึงได้สร้างอุปกรณ์ขนาดเล็ก โดยใช้กระดานกลุ่มย่อย Intel® Edison Arduino อุปกรณ์นี้มีเซ็นเซอร์อุณหภูมิ (TMP36) และตัวเปิดใช้งาน (แคโทด LED สี) ดูสคีมาสำหรับอุปกรณ์นี้ได้ที่ส่วนท้ายของบทความนี้
Intel Edison เป็นผลิตภัณฑ์ที่น่าสนใจเพราะสามารถเรียกใช้การจัดจำหน่าย Linux* เต็มรูปแบบได้ ดังนั้นผมจึงเขียนโปรแกรมให้กับ Node.js ได้ง่ายๆ โปรแกรมติดตั้งช่วยให้คุณติดตั้ง Intel* XDK ซึ่งช่วยให้เริ่มต้นใช้งานได้อย่างง่ายดาย แม้ว่าคุณจะตั้งโปรแกรมและอัปโหลดลงในอุปกรณ์ด้วยตนเองได้ก็ตาม
สำหรับแอป Node.js ฉันต้องมีโมดูลโหนด 3 รายการ รวมถึงทรัพยากร Dependency ของโหนดดังกล่าวด้วย
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 จะรองรับ Physical Web ซึ่งทำให้ Chrome สามารถดู URL ที่เผยแพร่โดยอุปกรณ์รอบๆ เบราว์เซอร์ได้ นักพัฒนาซอฟต์แวร์ต้องปฏิบัติตามข้อกำหนดบางอย่างที่นักพัฒนาแอปต้องทราบ เช่น ความจำเป็นสำหรับเว็บไซต์จะต้องเข้าถึงได้แบบสาธารณะและใช้ HTTPS
โปรโตคอล Eddystone มีขีดจำกัดขนาด 18 ไบต์ใน URL ดังนั้นเพื่อให้ 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 หาอุปกรณ์ได้อย่างดี
การสื่อสารกับเซ็นเซอร์/ผู้ดำเนินการ
เราใช้ 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) บนบอร์ดแยกของ Edison Arduino
กำลังพูดกับบลูทูธ
การพูดคุยด้วยบลูทูธนั้นทำได้ง่ายกว่าที่เคยด้วย 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 สีจะเรียบง่ายขึ้น ออบเจ็กต์และวิธีการ "อ่าน" จะแสดงอยู่ด้านล่าง ลักษณะเฉพาะจะได้รับการกำหนดค่าให้อนุญาตการดำเนินการ "อ่าน" และ "เขียน" และมี 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
หรือรหัสฐานสิบหก เช่น #ff00bb
) ฉันใช้โมดูลโหนดชื่อ parse-color เพื่อรับค่าฐานสิบหกเสมอ ซึ่งก็คือสิ่งที่ 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
ที่จัดจำหน่ายมา โชคดีที่วิธีการนี้ค่อนข้างง่าย
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 ช่วยให้สื่อสารกับอุปกรณ์บลูทูธพลังงานต่ำได้ง่ายขึ้น งั้นมาดูรหัสการเชื่อมต่อของฉันแบบง่ายๆ กัน หากไม่ทราบว่าคำสัญญาทำงานอย่างไร โปรดไปที่แหล่งข้อมูลนี้ก่อนอ่านข้อมูลเพิ่มเติม
การเชื่อมต่อกับอุปกรณ์บลูทูธเกี่ยวข้องกับกระบวนการตามสัญญา
ก่อนอื่น เราจะกรองอุปกรณ์ (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 นั้นค่อนข้างง่ายและทรงพลังมาก
เมื่อใช้ Physical Web และ Web Bluetooth แล้ว Chrome จะค้นหาอุปกรณ์และช่วยให้ผู้ใช้เชื่อมต่อกับอุปกรณ์ดังกล่าวได้โดยง่ายโดยไม่ต้องติดตั้งแอปพลิเคชันที่ไม่ค่อยได้ใช้งานซึ่งผู้ใช้อาจไม่ต้องการ และอาจอัปเดตเป็นครั้งคราว
ข้อมูลประชากร
คุณสามารถลองใช้ไคลเอ็นต์เพื่อรับแรงบันดาลใจเกี่ยวกับวิธีสร้างเว็บแอปของคุณเองเพื่อเชื่อมต่อกับอุปกรณ์อินเทอร์เน็ตของสรรพสิ่งที่กำหนดเอง
ซอร์สโค้ด
คุณสามารถดูซอร์สโค้ดได้ที่นี่ โปรดรายงานปัญหาหรือส่งแพตช์
สเก็ตช์
ถ้าคุณอยากลองอะไรใหม่ๆ และอยากทำซ้ำสิ่งที่ฉันทำ ให้ดูที่ภาพเอดิสันและภาพร่างจากแผงวงจรทดลองด้านล่างนี้