1. Chương trình này là gì?
Trong lớp học lập trình được khai sáng này, bạn sẽ tìm hiểu cách điều khiển nghe nến không cháy LED PLAYBULB chỉ bằng JavaScript, nhờ có API Bluetooth dành cho web. Ngoài ra, bạn cũng sẽ dùng được các tính năng của JavaScript ES2015, chẳng hạn như Lớp, Hàm mũi tên, Bản đồ và Lời hứa.
Kiến thức bạn sẽ học được
- Cách tương tác với thiết bị Bluetooth ở gần trong JavaScript
- Cách sử dụng các lớp ES2015, hàm mũi tên, bản đồ và lời hứa
Bạn cần có
- Hiểu biết cơ bản về phát triển web
- Kiến thức cơ bản về Bluetooth năng lượng thấp (BLE) và Cấu hình thuộc tính chung (GATT)
- Trình chỉnh sửa văn bản do bạn chọn
- Máy Mac, Chromebook hoặc thiết bị Android M có Ứng dụng trình duyệt Chrome và cáp USB micro sang USB.
2. Phát trước
Bạn nên xem phiên bản hoàn thiện của ứng dụng mà bạn sắp tạo tại https://googlecodelabs.github.io/candle-bluetooth và dùng thử thiết bị Bluetooth PLAYBULB nến mà bạn có thể tuỳ ý sử dụng trước khi thực sự tham gia lớp học lập trình này.
Bạn cũng có thể xem tôi thay đổi màu sắc tại https://www.youtube.com/watch?v=fBCPA9gIxlU
3. Bắt đầu thiết lập
Tải mã mẫu xuống
Bạn có thể nhận mã mẫu cho mã này bằng cách tải xuống tệp zip tại đây:
hoặc bằng cách sao chép kho lưu trữ git này:
git clone https://github.com/googlecodelabs/candle-bluetooth.git
Nếu bạn đã tải tệp nguồn xuống dưới dạng tệp zip, thì việc giải nén tệp đó sẽ cung cấp cho bạn một thư mục gốc candle-bluetooth-master
.
Cài đặt và xác minh máy chủ web
Mặc dù bạn có thể thoải mái sử dụng máy chủ web của riêng mình, nhưng lớp học lập trình này được thiết kế để hoạt động tốt với Máy chủ web Chrome. Nếu chưa cài đặt ứng dụng đó, bạn có thể cài đặt ứng dụng từ Cửa hàng Chrome trực tuyến.
Sau khi cài đặt ứng dụng Máy chủ web dành cho Chrome, nhấp vào lối tắt Ứng dụng trên thanh dấu trang:
Trong cửa sổ tiếp theo, nhấp vào biểu tượng Máy chủ web:
Tiếp theo, bạn sẽ thấy hộp thoại này. Hộp thoại này cho phép bạn định cấu hình máy chủ web cục bộ:
Nhấp vào nút chọn thư mục và chọn gốc của kho lưu trữ đã sao chép (hoặc huỷ lưu trữ). Thao tác này sẽ cho phép bạn phân phát công việc đang tiến hành của mình thông qua URL được đánh dấu trong hộp thoại máy chủ web (trong phần (các) URL máy chủ web).
Trong Tùy chọn, chọn hộp bên cạnh "Tự động hiển thị chỉ mục.html", như được hiển thị bên dưới:
Bây giờ, hãy truy cập trang web của bạn trong trình duyệt web (bằng cách nhấp vào URL Máy chủ Web được đánh dấu) và bạn sẽ thấy một trang như sau:
Nếu muốn xem ứng dụng này trông như thế nào trên điện thoại Android của mình, bạn cần phải bật Gỡ lỗi từ xa trên Android và thiết lập Chuyển tiếp cổng (số cổng theo mặc định là 8887). Sau đó, bạn chỉ cần mở một thẻ Chrome mới để truy cập http://localhost:8887 trên điện thoại Android của mình.
Tiếp theo
Hiện tại, ứng dụng web này chưa làm gì nhiều. Hãy bắt đầu thêm tính năng hỗ trợ Bluetooth!
4. Khám phá ngọn nến
Chúng ta sẽ bắt đầu bằng cách viết một thư viện sử dụng Lớp JavaScript ES2015 cho thiết bị Bluetooth PLAYBULB nến.
Bình tĩnh. Cú pháp lớp không đưa vào JavaScript một mô hình kế thừa mới hướng đối tượng. Mô-đun này chỉ cung cấp cú pháp rõ ràng hơn nhiều để tạo đối tượng và xử lý tính kế thừa, như bạn có thể đọc dưới đây.
Trước tiên, hãy xác định một lớp PlaybulbCandle
trong playbulbCandle.js
và tạo một thực thể playbulbCandle
sẽ có trong tệp app.js
sau này.
playbulbCandle.js
(function() {
'use strict';
class PlaybulbCandle {
constructor() {
this.device = null;
}
}
window.playbulbCandle = new PlaybulbCandle();
})();
Để yêu cầu quyền truy cập vào một thiết bị Bluetooth ở gần, chúng tôi cần gọi navigator.bluetooth.requestDevice
. Vì thiết bị PLAYBULB Nến quảng cáo liên tục (nếu chưa được ghép nối) một UUID Dịch vụ Bluetooth GATT không đổi được biết ở dạng ngắn là 0xFF02
, nên chúng ta chỉ cần xác định một hằng số và thêm hằng số này vào tham số dịch vụ bộ lọc trong phương thức connect
công khai mới của lớp PlaybulbCandle
.
Chúng ta cũng sẽ theo dõi đối tượng BluetoothDevice
trong nội bộ để có thể truy cập vào đối tượng đó sau này nếu cần. Vì navigator.bluetooth.requestDevice
trả về một JavaScript ES2015 Promise, nên chúng ta sẽ thực hiện việc này trong phương thức then
.
playbulbCandle.js
(function() {
'use strict';
const CANDLE_SERVICE_UUID = 0xFF02;
class PlaybulbCandle {
constructor() {
this.device = null;
}
connect() {
let options = {filters:[{services:[ CANDLE_SERVICE_UUID ]}]};
return navigator.bluetooth.requestDevice(options)
.then(function(device) {
this.device = device;
}.bind(this));
}
}
window.playbulbCandle = new PlaybulbCandle();
})();
Là một tính năng bảo mật, tính năng phát hiện các thiết bị Bluetooth ở gần bằng navigator.bluetooth.requestDevice
phải được gọi thông qua một cử chỉ của người dùng, chẳng hạn như chạm hoặc nhấp chuột. Đó là lý do chúng ta gọi phương thức connect
khi người dùng nhấp vào nút "Kết nối" trong tệp app.js
:
app.js
document.querySelector('#connect').addEventListener('click', function(event) {
document.querySelector('#state').classList.add('connecting');
playbulbCandle.connect()
.then(function() {
console.log(playbulbCandle.device);
document.querySelector('#state').classList.remove('connecting');
document.querySelector('#state').classList.add('connected');
})
.catch(function(error) {
console.error('Argh!', error);
});
});
Chạy ứng dụng
Tại thời điểm này, hãy truy cập trang web của bạn trong trình duyệt web (bằng cách nhấp vào URL máy chủ web được đánh dấu trong ứng dụng máy chủ web) hoặc chỉ cần làm mới trang hiện có. Nhấp vào nút "Kết nối" màu xanh lục chọn thiết bị trong trình chọn rồi mở bảng điều khiển Công cụ cho nhà phát triển mà bạn yêu thích bằng phím tắt Ctrl + Shift + J và để ý thấy đối tượng BluetoothDevice
đã được ghi nhật ký.
Bạn có thể gặp lỗi nếu Bluetooth đang tắt và/hoặc thiết bị bluetooth PLAYBULB nến đang tắt. Trong trường hợp đó, hãy bật tính năng này rồi tiếp tục.
Phần thưởng bắt buộc
Tôi không biết bạn thế nào nhưng tôi đã thấy quá nhiều function() {}
trong mã này. Thay vào đó, hãy chuyển sang Hàm mũi tên () => {}
JavaScript ES2015. Chúng là những cứu cánh tuyệt đối: Tất cả tính năng tuyệt vời của các chức năng ẩn danh, không có nỗi buồn nào về sự ràng buộc.
playbulbCandle.js
(function() {
'use strict';
const CANDLE_SERVICE_UUID = 0xFF02;
class PlaybulbCandle {
constructor() {
this.device = null;
}
connect() {
let options = {filters:[{services:[ CANDLE_SERVICE_UUID ]}]};
return navigator.bluetooth.requestDevice(options)
.then(device => {
this.device = device;
});
}
}
window.playbulbCandle = new PlaybulbCandle();
})();
app.js
document.querySelector('#connect').addEventListener('click', event => {
playbulbCandle.connect()
.then(() => {
console.log(playbulbCandle.device);
document.querySelector('#state').classList.remove('connecting');
document.querySelector('#state').classList.add('connected');
})
.catch(error => {
console.error('Argh!', error);
});
});
Tiếp theo
– Được rồi... tôi có thể nói chuyện với ngọn nến này hay gì không?
– Chắc chắn rồi... chuyển sang bước tiếp theo
Câu hỏi thường gặp
5. Đọc nội dung nào đó
Vậy bây giờ, bạn làm gì khi có BluetoothDevice
được trả về từ lời hứa của navigator.bluetooth.requestDevice
? Hãy kết nối với Máy chủ GATT từ xa qua Bluetooth có chứa dịch vụ Bluetooth và các định nghĩa đặc trưng bằng cách gọi device.gatt.connect()
:
playbulbCandle.js
class PlaybulbCandle {
constructor() {
this.device = null;
}
connect() {
let options = {filters:[{services:[ CANDLE_SERVICE_UUID ]}]};
return navigator.bluetooth.requestDevice(options)
.then(device => {
this.device = device;
return device.gatt.connect();
});
}
}
Đọc tên thiết bị
Ở đây, chúng ta đã kết nối với Máy chủ GATT của thiết bị Bluetooth PLAYBULB nến. Bây giờ, chúng ta muốn nhận Dịch vụ GATT chính (trước đây được quảng cáo là 0xFF02
) và đọc đặc điểm tên thiết bị (0xFFFF
) thuộc về dịch vụ này. Bạn có thể dễ dàng thực hiện việc này bằng cách thêm một phương thức mới getDeviceName
vào lớp PlaybulbCandle
, đồng thời sử dụng device.gatt.getPrimaryService
và service.getCharacteristic
. Phương thức characteristic.readValue
thực sự sẽ trả về một DataView
mà chúng ta chỉ giải mã bằng TextDecoder
.
playbulbCandle.js
const CANDLE_DEVICE_NAME_UUID = 0xFFFF;
...
getDeviceName() {
return this.device.gatt.getPrimaryService(CANDLE_SERVICE_UUID)
.then(service => service.getCharacteristic(CANDLE_DEVICE_NAME_UUID))
.then(characteristic => characteristic.readValue())
.then(data => {
let decoder = new TextDecoder('utf-8');
return decoder.decode(data);
});
}
Hãy thêm phương thức này vào app.js
bằng cách gọi playbulbCandle.getDeviceName
sau khi chúng ta đã kết nối và hiện tên thiết bị.
app.js
document.querySelector('#connect').addEventListener('click', event => {
playbulbCandle.connect()
.then(() => {
console.log(playbulbCandle.device);
document.querySelector('#state').classList.remove('connecting');
document.querySelector('#state').classList.add('connected');
return playbulbCandle.getDeviceName().then(handleDeviceName);
})
.catch(error => {
console.error('Argh!', error);
});
});
function handleDeviceName(deviceName) {
document.querySelector('#deviceName').value = deviceName;
}
Tại thời điểm này, hãy truy cập trang web của bạn trong trình duyệt web (bằng cách nhấp vào URL máy chủ web được đánh dấu trong ứng dụng máy chủ web) hoặc chỉ cần làm mới trang hiện có. Đảm bảo Nến PLAYBULB được bật, sau đó nhấp vào "Kết nối" trên trang và bạn sẽ thấy tên thiết bị bên dưới công cụ chọn màu.
Đọc mức pin
Thiết bị Bluetooth PLAYBULB nến cũng có một đặc điểm Bluetooth tiêu chuẩn về mức pin cho biết mức pin của thiết bị. Tức là chúng ta có thể sử dụng các tên tiêu chuẩn như battery_service
cho UUID của Dịch vụ Bluetooth GATT và battery_level
cho UUID đặc trưng Bluetooth GATT.
Hãy thêm một phương thức getBatteryLevel
mới vào lớp PlaybulbCandle
và đọc mức pin theo phần trăm.
playbulbCandle.js
getBatteryLevel() {
return this.device.gatt.getPrimaryService('battery_service')
.then(service => service.getCharacteristic('battery_level'))
.then(characteristic => characteristic.readValue())
.then(data => data.getUint8(0));
}
Chúng tôi cũng cần cập nhật đối tượng JavaScript options
để đưa dịch vụ pin vào khoá optionalServices
, vì đối tượng này không được quảng cáo bởi thiết bị PLAYBULB nến Bluetooth nhưng vẫn bắt buộc phải truy cập vào khoá này.
playbulbCandle.js
let options = {filters:[{services:[ CANDLE_SERVICE_UUID ]}],
optionalServices: ['battery_service']};
return navigator.bluetooth.requestDevice(options)
Như trước đó, hãy cắm thiết bị này vào app.js
bằng cách gọi playbulbCandle.getBatteryLevel
sau khi chúng ta có tên thiết bị và hiển thị mức pin.
app.js
document.querySelector('#connect').addEventListener('click', event => {
playbulbCandle.connect()
.then(() => {
console.log(playbulbCandle.device);
document.querySelector('#state').classList.remove('connecting');
document.querySelector('#state').classList.add('connected');
return playbulbCandle.getDeviceName().then(handleDeviceName)
.then(() => playbulbCandle.getBatteryLevel().then(handleBatteryLevel));
})
.catch(error => {
console.error('Argh!', error);
});
});
function handleDeviceName(deviceName) {
document.querySelector('#deviceName').value = deviceName;
}
function handleBatteryLevel(batteryLevel) {
document.querySelector('#batteryLevel').textContent = batteryLevel + '%';
}
Tại thời điểm này, hãy truy cập trang web của bạn trong trình duyệt web (bằng cách nhấp vào URL máy chủ web được đánh dấu trong ứng dụng máy chủ web) hoặc chỉ cần làm mới trang hiện có. Nhấp vào nút "Kết nối" trên trang và bạn sẽ thấy cả tên thiết bị và mức pin.
Tiếp theo
– Làm cách nào để thay đổi màu của bóng đèn này? Vì vậy, tôi ở đây!
– Sắp xong rồi, tôi hứa...
Câu hỏi thường gặp
6. Thay đổi màu sắc
Bạn có thể thay đổi màu sắc dễ dàng như viết một tập hợp lệnh cụ thể vào Đặc tính Bluetooth (0xFFFC
) trong Dịch vụ GATT chính được quảng cáo dưới dạng 0xFF02
. Ví dụ: chuyển nến PLAYBULB sang màu đỏ sẽ ghi một mảng gồm các số nguyên 8 bit chưa ký bằng [0x00, 255, 0, 0]
, trong đó 0x00
là độ bão hoà màu trắng và 255, 0, 0
lần lượt là các giá trị màu đỏ, xanh lục và xanh dương .
Chúng ta sẽ sử dụng characteristic.writeValue
để thực sự ghi một số dữ liệu vào đặc điểm Bluetooth trong phương thức công khai setColor
mới của lớp PlaybulbCandle
. Ngoài ra, chúng ta cũng sẽ trả về các giá trị màu đỏ, xanh lục và xanh dương thực tế khi thực hiện lời hứa để có thể sử dụng các giá trị đó trong app.js
sau này:
playbulbCandle.js
const CANDLE_COLOR_UUID = 0xFFFC;
...
setColor(r, g, b) {
let data = new Uint8Array([0x00, r, g, b]);
return this.device.gatt.getPrimaryService(CANDLE_SERVICE_UUID)
.then(service => service.getCharacteristic(CANDLE_COLOR_UUID))
.then(characteristic => characteristic.writeValue(data))
.then(() => [r,g,b]);
}
Hãy cập nhật hàm changeColor
trong app.js
để gọi playbulbCandle.setColor
khi lệnh "No Effect" (Không có hiệu ứng) đã chọn nút chọn. Biến màu r, g, b
chung đã được đặt khi người dùng nhấp vào canvas công cụ chọn màu.
app.js
function changeColor() {
var effect = document.querySelector('[name="effectSwitch"]:checked').id;
if (effect === 'noEffect') {
playbulbCandle.setColor(r, g, b).then(onColorChanged);
}
}
Tại thời điểm này, hãy truy cập trang web của bạn trong trình duyệt web (bằng cách nhấp vào URL máy chủ web được đánh dấu trong ứng dụng máy chủ web) hoặc chỉ cần làm mới trang hiện có. Nhấp vào nút "Kết nối" trên trang và nhấp vào công cụ chọn màu để thay đổi màu của Nến PLAYBULB bao nhiêu lần tuỳ thích.
Hiệu ứng nến rêu
Nếu bạn đã từng thắp nến trước đó, thì bạn sẽ biết đèn không ở trạng thái tĩnh. Thật may cho chúng tôi, có một đặc điểm Bluetooth khác (0xFFFB
) trong Dịch vụ chính của GATT được quảng cáo là 0xFF02
cho phép người dùng thiết lập một số hiệu ứng nến.
Đặt "hiệu ứng nến" chẳng hạn như có thể đạt được bằng cách viết [0x00, r, g, b, 0x04, 0x00, 0x01, 0x00]
. Và bạn cũng có thể đặt "hiệu ứng nhấp nháy" cùng với [0x00, r, g, b, 0x00, 0x00, 0x1F, 0x00]
.
Hãy thêm các phương thức setCandleEffectColor
và setFlashingColor
vào lớp PlaybulbCandle
.
playbulbCandle.js
const CANDLE_EFFECT_UUID = 0xFFFB;
...
setCandleEffectColor(r, g, b) {
let data = new Uint8Array([0x00, r, g, b, 0x04, 0x00, 0x01, 0x00]);
return this.device.gatt.getPrimaryService(CANDLE_SERVICE_UUID)
.then(service => service.getCharacteristic(CANDLE_EFFECT_UUID))
.then(characteristic => characteristic.writeValue(data))
.then(() => [r,g,b]);
}
setFlashingColor(r, g, b) {
let data = new Uint8Array([0x00, r, g, b, 0x00, 0x00, 0x1F, 0x00]);
return this.device.gatt.getPrimaryService(CANDLE_SERVICE_UUID)
.then(service => service.getCharacteristic(CANDLE_EFFECT_UUID))
.then(characteristic => characteristic.writeValue(data))
.then(() => [r,g,b]);
}
Hãy cập nhật hàm changeColor
trong app.js
để gọi playbulbCandle.setCandleEffectColor
khi "Hiệu ứng nến" nút chọn sẽ được chọn và playbulbCandle.setFlashingColor
khi thông báo "Nhấp nháy" đã chọn nút chọn. Lần này, chúng tôi sẽ sử dụng switch
nếu bạn đồng ý.
app.js
function changeColor() {
var effect = document.querySelector('[name="effectSwitch"]:checked').id;
switch(effect) {
case 'noEffect':
playbulbCandle.setColor(r, g, b).then(onColorChanged);
break;
case 'candleEffect':
playbulbCandle.setCandleEffectColor(r, g, b).then(onColorChanged);
break;
case 'flashing':
playbulbCandle.setFlashingColor(r, g, b).then(onColorChanged);
break;
}
}
Tại thời điểm này, hãy truy cập trang web của bạn trong trình duyệt web (bằng cách nhấp vào URL máy chủ web được đánh dấu trong ứng dụng máy chủ web) hoặc chỉ cần làm mới trang hiện có. Nhấp vào nút "Kết nối" trên trang và phát với Nến và Hiệu ứng nhấp nháy.
Tiếp theo
– Vậy là xong? 3 hiệu ứng nến? Đây có phải là lý do tôi ở đây không?
– Còn nhiều chương trình nữa nhưng lần này bạn sẽ phải tự mình nộp bài.
7. Tiến xa hơn nữa
Vậy là chúng tôi đã sẵn sàng! Bạn có thể nghĩ rằng ứng dụng vẫn chưa kết thúc nhưng ứng dụng vẫn chưa kết thúc. Hãy xem liệu bạn có thực sự hiểu nội dung mình đã sao chép và dán trong lớp học lập trình này hay không. Sau đây là những việc bạn muốn tự làm để làm nổi bật ứng dụng này.
Thêm hiệu ứng còn thiếu
Dưới đây là dữ liệu về các hiệu ứng còn thiếu:
- Nhịp:
[0x00, r, g, b, 0x01, 0x00, 0x09, 0x00]
(bạn có thể điều chỉnhr, g, b
giá trị ở đó) - Cầu vồng:
[0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00]
(Những người thích động mạnh có thể muốn tránh lựa chọn này) - Mờ dần màu cầu vồng:
[0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x26, 0x00]
Về cơ bản, điều này có nghĩa là thêm các phương thức setPulseColor
, setRainbow
và setRainbowFade
mới vào lớp PlaybulbCandle
và gọi các phương thức đó trong changeColor
.
Khắc phục lỗi "không có hiệu ứng"
Như bạn có thể nhận thấy, trạng thái "không có hiệu ứng" không đặt lại bất kỳ hiệu ứng nào đang diễn ra, đây là việc nhỏ nhưng vẫn sẽ. Hãy khắc phục vấn đề này. Trong phương thức setColor
, trước tiên, bạn cần kiểm tra xem một hiệu ứng có đang diễn ra hay không thông qua một biến lớp mới _isEffectSet
. Nếu là true
, hãy tắt hiệu ứng đó trước khi đặt màu mới bằng dữ liệu sau: [0x00, r, g, b, 0x05, 0x00, 0x01, 0x00]
.
Ghi tên thiết bị
Câu này khá dễ! Việc viết tên thiết bị tuỳ chỉnh cũng đơn giản như việc ghi vào đặc điểm tên thiết bị Bluetooth trước đó. Bạn nên sử dụng phương thức TextEncoder
encode
để lấy Uint8Array
chứa tên thiết bị.
Sau đó, tôi thêm một "thông tin đầu vào" eventListener
đến document.querySelector('#deviceName')
và gọi playbulbCandle.setDeviceName
để duy trì tính đơn giản.
Tôi tự đặt tên cho mình là PLAY💡 CANDLE!
8. Vậy là xong!
Kiến thức bạn học được
- Cách tương tác với thiết bị Bluetooth ở gần trong JavaScript
- Cách sử dụng các lớp ES2015, hàm mũi tên, bản đồ và lời hứa
Bước tiếp theo
- Tìm hiểu thêm về API Bluetooth trên web
- Duyệt qua Mẫu Bluetooth trên web và Bản minh hoạ chính thức
- Xem chú mèo gắt gỏng đang bay