成功組成跟韌體溝通的 protocol 資料後,接下來就是要透過任何 Serial 傳送給 ChameleonUltra 並且等候回應。筆者在開發這個專案時,主要會用 Web Serial API 進行開發,所以先來介紹這個 API 該如何使用。
目前 Web Serial API 支援所有 PC 的作業系統 (ChromeOS, Linux, macOS, and Windows),只要是 Chrome 89 以上的版本就有支援。
因為藍牙會有可能曝露個人資料以及定位,所以 Web BLE API 只能在 HTTPS 網站上使用。
在本地開發過程中也會有這個限制,筆者會在接下來的文章提到要如何解決這個限制。
我們在這個專案中會使用 navigator.bluetooth.requestDevice
的 API,但由於安全性考量,Chrome 限制這個 API 只能在特定的使用者互動的事件中使用,例如 pointerup
、click
、touchend
…等。
首先,我們需要先檢查瀏覽器是否支援 Web Serial API:
function isWebSerialSupported() {
return "serial" in navigator
}
如果有支援的話,下一步就是尋找 Chameleon Ultra,在這個步驟會需要裝置 ID:
const filters = [
{ usbVendorId: 0x6868, usbProductId: 0x8686 }, // Chameleon Tiny
]
// 這行程式碼會跳出選擇裝置的視窗
const port = await navigator.serial.requestPort({ filters })
console.log(port.getInfo())
成功取得 port
以後,就可以開始進行連線:
await port.open({ baudRate: 115200 }) // Chameleon Ultra 的 baudRate 為 115200
以下是 Read from and write to a serial port | Chrome Developers 所提供的範例程式碼:
const reader = port.readable.getReader()
// Listen to data coming from the serial device.
while (true) {
const { value, done } = await reader.read()
if (done) { // Allow the serial port to be closed later.
reader.releaseLock()
break
}
console.log(value) // value is a Uint8Array.
}
但在這個專案中,會需要頻繁檢查資料是否完整傳送,所以筆者選擇寫一個自訂的 WritableStream
,然後使用 pipeTo
將資料寫入 WritableStream
,以下是範例程式碼:
class WritableSink {
constructor() {
this.bufs = []
}
write(chunk) {
this.bufs.push(Buffer.from(chunk)) // convert Uint8Array to Buffer
}
}
const writable = new WritableStream(new WritableSink())
port.readable.pipeTo(writable) // no await prevent blocking
我們會透過筆者自己寫的 Buffer
來組成 ChameleonUltra 的 Protocol,由於這個 Buffer
繼承自 Uint8Array
,所以可以直接當成 Uint8Array
來使用,然後透過 port.writable.write
來寫入資料:
const buf = Buffer.from('11ef03e80000000b0a48656c6c6f20576f726c64e4', 'hex') // 來自 Day 6 的範例
const writer = port.writable.getWriter()
await writer.write(buf)
writer.releaseLock()