Điều khiển ngọn nến PLAYBULB bằng Bluetooth trên web

1. Chương trình này là gì?

IMG_19700101_023537~2~2.jpg

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 đồ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ó

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:

Ảnh chụp màn hình 16/11/2016 lúc 4.10.42 chiều.png

Trong cửa sổ tiếp theo, nhấp vào biểu tượng Máy chủ web:

9f3c21b2cf6cbfb5.png.

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 chụp màn hình 16/11/2016 lúc 3:40.47 CH.png

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:

Ảnh chụp màn hình 16/11/2016 lúc 3:40.56 chiều.png

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:

Ảnh chụp màn hình lúc 3:20.22 chiều ngày 16 tháng 11 năm 2016.png

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 Androidthiế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ý.

Ảnh chụp màn hình lúc 3:27.12 chiều 16/11/2016.png

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.getPrimaryServiceservice.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.

Ảnh chụp màn hình lúc 3:29.21 chiều 16/11/2016.png

Đọ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.

Ảnh chụp màn hình lúc 3:29.21 chiều 16/11/2016.png

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.

Ảnh chụp màn hình 16/11/2016 lúc 3:31.37 CH.png

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 setCandleEffectColorsetFlashingColor 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.

Ảnh chụp màn hình lúc 3:33.23 chiều 16/11/2016.png

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ỉnh r, 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, setRainbowsetRainbowFade 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