บทนำสู่เว็บบลูทูธ
เผยแพร่แล้ว: 2022-03-10ด้วย Progressive Web Apps เว็บจึงขยับเข้าใกล้แอปแบบเนทีฟมากขึ้นเรื่อยๆ อย่างไรก็ตาม ด้วยประโยชน์เพิ่มเติมที่มีอยู่ในเว็บ เช่น ความเป็นส่วนตัวและความเข้ากันได้ข้ามแพลตฟอร์ม
ตามธรรมเนียมแล้ว เว็บนั้นยอดเยี่ยมมากในการพูดคุยกับเซิร์ฟเวอร์ในเครือข่าย และกับเซิร์ฟเวอร์บนอินเทอร์เน็ตโดยเฉพาะ ขณะนี้เว็บกำลังเคลื่อนไปสู่แอปพลิเคชัน เรายังต้องการความสามารถเดียวกันกับที่แอปที่มาพร้อมเครื่องมี
จำนวนข้อกำหนดและคุณสมบัติใหม่ที่มีการใช้งานในช่วงไม่กี่ปีที่ผ่านมาในเบราว์เซอร์นั้นน่าทึ่งมาก เรามีข้อกำหนดสำหรับจัดการกับ 3D เช่น WebGL และ WebGPU ที่กำลังจะมีขึ้น เราสามารถสตรีมและสร้างเสียง ดูวิดีโอ และใช้เว็บแคมเป็นอุปกรณ์อินพุต เราสามารถรันโค้ดที่ความเร็วเกือบปกติได้โดยใช้ WebAssembly นอกจากนี้ แม้ในขั้นต้นจะเป็นสื่อสำหรับเครือข่ายเท่านั้น แต่เว็บได้เปลี่ยนไปใช้การสนับสนุนออฟไลน์กับพนักงานบริการ
นั่นเป็นสิ่งที่ดีและทั้งหมด แต่พื้นที่หนึ่งเกือบจะเป็นโดเมนพิเศษเฉพาะสำหรับแอปที่มาพร้อมเครื่อง นั่นคือ การสื่อสารกับอุปกรณ์ นั่นคือปัญหาที่เราพยายามแก้ไขมาเป็นเวลานาน และเป็นสิ่งที่ทุกคนอาจเคยพบเจอมา ณ จุดหนึ่ง เว็บเหมาะสำหรับการพูดคุยกับเซิร์ฟเวอร์ แต่ไม่เหมาะสำหรับการพูดคุยกับอุปกรณ์ ลองนึกถึงตัวอย่างเช่น พยายามตั้งค่าเราเตอร์ในเครือข่ายของคุณ โอกาสที่คุณจะต้องป้อนที่อยู่ IP และใช้เว็บอินเตอร์เฟสผ่านการเชื่อมต่อ HTTP ธรรมดาโดยไม่มีการรักษาความปลอดภัยใดๆ นั่นเป็นเพียงประสบการณ์ที่ไม่ดีและความปลอดภัยที่ไม่ดี ยิ่งไปกว่านั้น คุณจะรู้ได้อย่างไรว่าที่อยู่ IP ที่ถูกต้องคืออะไร?
HTTP ยังเป็นปัญหาแรกที่เราพบเมื่อเราพยายามสร้าง Progressive Web App ที่พยายามพูดคุยกับอุปกรณ์ PWAs เป็น HTTPS เท่านั้น และอุปกรณ์ในเครื่องมักจะเป็น HTTP เท่านั้น คุณต้องมีใบรับรองสำหรับ HTTPS และหากต้องการรับใบรับรอง คุณต้องมีเซิร์ฟเวอร์ที่เปิดเผยต่อสาธารณะซึ่งมีชื่อโดเมน (ฉันกำลังพูดถึงอุปกรณ์ในเครือข่ายท้องถิ่นของเราซึ่งอยู่ไกลเกินเอื้อม)
ดังนั้นสำหรับอุปกรณ์จำนวนมาก คุณต้องมีแอปที่มาพร้อมเครื่องเพื่อตั้งค่าและใช้งานอุปกรณ์ เนื่องจากแอปที่มาพร้อมเครื่องไม่ได้ผูกติดกับข้อจำกัดของแพลตฟอร์มเว็บและสามารถมอบประสบการณ์ที่น่าพึงพอใจให้กับผู้ใช้ได้ อย่างไรก็ตาม ฉันไม่ต้องการดาวน์โหลดแอป 500 MB เพื่อทำเช่นนั้น บางทีอุปกรณ์ที่คุณมีอาจอายุไม่กี่ปีแล้ว และแอปนี้ไม่เคยอัปเดตให้ทำงานบนโทรศัพท์เครื่องใหม่ได้เลย บางทีคุณอาจต้องการใช้คอมพิวเตอร์เดสก์ท็อปหรือแล็ปท็อป และผู้ผลิตสร้างเฉพาะแอปบนอุปกรณ์เคลื่อนที่เท่านั้น ยังไม่เป็นประสบการณ์ในอุดมคติ
WebBluetooth เป็นข้อกำหนดใหม่ที่ใช้ใน Chrome และ Samsung Internet ซึ่งช่วยให้เราสื่อสารโดยตรงกับอุปกรณ์ Bluetooth Low Energy จากเบราว์เซอร์ Progressive Web App ร่วมกับ WebBluetooth ให้ความปลอดภัยและความสะดวกของเว็บแอปพลิเคชันที่มีอำนาจในการพูดคุยกับอุปกรณ์โดยตรง
Bluetooth มีชื่อที่ค่อนข้างแย่เนื่องจากช่วงที่จำกัด คุณภาพเสียงไม่ดี และปัญหาในการจับคู่ แต่ปัญหาเหล่านั้นเป็นเพียงอดีต Bluetooth Low Energy เป็นข้อกำหนดที่ทันสมัยซึ่งแทบไม่เกี่ยวข้องกับข้อกำหนด Bluetooth แบบเก่า นอกเหนือจากการใช้คลื่นความถี่เดียวกัน มีอุปกรณ์มากกว่า 10 ล้านเครื่องที่รองรับ Bluetooth ทุกวัน ซึ่งรวมถึงคอมพิวเตอร์และโทรศัพท์ แต่ยังรวมถึงอุปกรณ์ต่างๆ เช่น อัตราการเต้นของหัวใจและเครื่องวัดระดับน้ำตาล อุปกรณ์ IoT เช่น หลอดไฟและของเล่น เช่น รถยนต์และโดรนที่ควบคุมด้วยรีโมท
การอ่านที่แนะนำ : การ ทำความเข้าใจแพลตฟอร์มที่ใช้ API: คู่มือสำหรับผู้จัดการผลิตภัณฑ์
ส่วนทฤษฎีที่น่าเบื่อ
เนื่องจาก Bluetooth เองไม่ใช่เทคโนโลยีเว็บ มันจึงใช้คำศัพท์บางอย่างที่เราอาจดูเหมือนไม่คุ้นเคย มาดูกันว่าบลูทูธทำงานอย่างไรและคำศัพท์บางคำ
อุปกรณ์ Bluetooth ทุกเครื่องอาจเป็น 'อุปกรณ์กลาง' หรือ 'อุปกรณ์ต่อพ่วง' เฉพาะอุปกรณ์ส่วนกลางเท่านั้นที่สามารถเริ่มการสื่อสารและสามารถพูดคุยกับอุปกรณ์ต่อพ่วงได้เท่านั้น ตัวอย่างของอุปกรณ์ส่วนกลางคือคอมพิวเตอร์หรือโทรศัพท์มือถือ
อุปกรณ์ต่อพ่วงไม่สามารถเริ่มการสื่อสารได้และสามารถพูดคุยกับอุปกรณ์ส่วนกลางได้เท่านั้น นอกจากนี้ อุปกรณ์ต่อพ่วงสามารถพูดคุยกับอุปกรณ์กลางได้เพียงเครื่องเดียวในเวลาเดียวกัน อุปกรณ์ต่อพ่วงไม่สามารถพูดคุยกับอุปกรณ์ต่อพ่วงอื่นได้
อุปกรณ์ส่วนกลางสามารถพูดคุยกับอุปกรณ์ต่อพ่วงหลายตัวพร้อมกันและสามารถถ่ายทอดข้อความได้หากต้องการ ดังนั้นเครื่องวัดอัตราการเต้นของหัวใจจึงไม่สามารถพูดคุยกับหลอดไฟของคุณได้ อย่างไรก็ตาม คุณสามารถเขียนโปรแกรมที่ทำงานบนอุปกรณ์ส่วนกลางที่รับอัตราการเต้นของหัวใจและเปลี่ยนเป็นสีแดงได้หากอัตราการเต้นของหัวใจสูงกว่าเกณฑ์ที่กำหนด
เมื่อเราพูดถึง WebBluetooth เรากำลังพูดถึงส่วนเฉพาะของข้อกำหนด Bluetooth ที่เรียกว่า Generic Attribute Profile ซึ่งมีตัวย่อที่ชัดเจนมาก GATT (เห็นได้ชัดว่า GAP ถูกใช้ไปแล้ว)
ในบริบทของ GATT เราไม่ได้พูดถึงอุปกรณ์ส่วนกลางและอุปกรณ์ต่อพ่วงอีกต่อไป แต่เป็นไคลเอ็นต์และเซิร์ฟเวอร์ หลอดไฟของคุณเป็นเซิร์ฟเวอร์ นั่นอาจดูขัดกับสัญชาตญาณ แต่จริงๆ แล้วมันก็สมเหตุสมผลดีถ้าคุณลองคิดดู หลอดไฟมีบริการคือไฟ เช่นเดียวกับเมื่อเบราว์เซอร์เชื่อมต่อกับเซิร์ฟเวอร์บนอินเทอร์เน็ต โทรศัพท์หรือคอมพิวเตอร์ของคุณคือไคลเอนต์ที่เชื่อมต่อกับเซิร์ฟเวอร์ GATT ในหลอดไฟ
แต่ละเซิร์ฟเวอร์เสนอบริการตั้งแต่หนึ่งอย่างขึ้นไป บริการเหล่านี้บางส่วนเป็นส่วนหนึ่งของมาตรฐานอย่างเป็นทางการ แต่คุณสามารถกำหนดบริการของคุณเองได้ ในกรณีของเครื่องวัดอัตราการเต้นของหัวใจ มีบริการอย่างเป็นทางการที่กำหนดไว้ในข้อกำหนด ในกรณีของหลอดไฟไม่มี และผู้ผลิตแทบทุกรายพยายามที่จะคิดค้นล้อใหม่ ทุกบริการมีลักษณะอย่างน้อยหนึ่งอย่าง แต่ละคุณลักษณะมีค่าที่สามารถอ่านหรือเขียนได้ สำหรับตอนนี้ จะเป็นการดีที่สุดที่จะคิดว่ามันเป็นอาร์เรย์ของอ็อบเจ็กต์ โดยแต่ละอ็อบเจ็กต์มีคุณสมบัติที่มีค่า
ไม่เหมือนกับคุณสมบัติของอ็อบเจ็กต์ บริการและคุณลักษณะจะไม่ถูกระบุโดยสตริง บริการและคุณลักษณะแต่ละรายการมี UUID ที่ไม่ซ้ำกันซึ่งมีความยาว 16 หรือ 128 บิต อย่างเป็นทางการ UUID 16 บิตสงวนไว้สำหรับมาตรฐานอย่างเป็นทางการ แต่แทบไม่มีใครทำตามกฎนั้น สุดท้าย ทุกค่าคืออาร์เรย์ของไบต์ ไม่มีประเภทข้อมูลแฟนซีใน Bluetooth
มองใกล้ที่หลอดไฟ Bluetooth
มาดูอุปกรณ์บลูทูธจริงกัน: Mipow Playbulb Sphere คุณสามารถใช้แอพอย่าง BLE Scanner หรือ nRF Connect เพื่อเชื่อมต่อกับอุปกรณ์และดูบริการและคุณสมบัติทั้งหมด ในกรณีนี้ ฉันใช้แอป BLE Scanner สำหรับ iOS
สิ่งแรกที่คุณเห็นเมื่อเชื่อมต่อกับหลอดไฟคือรายการบริการ มีมาตรฐานบางอย่างเช่นบริการข้อมูลอุปกรณ์และบริการแบตเตอรี่ แต่ยังมีบริการที่กำหนดเองบางอย่าง ฉันสนใจบริการนี้เป็นพิเศษด้วย UUID 16 บิตของ 0xff0f
หากคุณเปิดบริการนี้ คุณจะเห็นรายการคุณลักษณะมากมาย ฉันไม่รู้ว่าคุณลักษณะเหล่านี้ส่วนใหญ่ทำอะไร เนื่องจากถูกระบุโดย UUID เท่านั้น และเนื่องจากโชคไม่ดีที่คุณลักษณะเหล่านี้เป็นส่วนหนึ่งของบริการที่กำหนดเอง พวกเขาไม่ได้มาตรฐานและผู้ผลิตไม่ได้จัดเตรียมเอกสารใด ๆ
ลักษณะแรกที่มี UUID ของ 0xfffc
ดูน่าสนใจเป็นพิเศษ มีค่าสี่ไบต์ หากเราเปลี่ยนค่าของไบต์เหล่านี้จาก 0x00000000
เป็น 0x00ff0000
หลอดไฟจะเปลี่ยนเป็นสีแดง การเปลี่ยนเป็น 0x0000ff00
จะทำให้หลอดไฟเป็นสีเขียว และ 0x000000ff
เป็นสีน้ำเงิน นี่คือสี RGB และตรงกับสีฐานสิบหกที่เราใช้ใน HTML และ CSS
ไบต์แรกนั้นทำอะไร? ถ้าเราเปลี่ยนค่าเป็น 0xff000000
หลอดไฟจะเปลี่ยนเป็นสีขาว หลอดไฟประกอบด้วย LED สี่ดวงที่แตกต่างกัน และด้วยการเปลี่ยนค่าของสี่ไบต์แต่ละอัน เราสามารถสร้างทุกสีที่เราต้องการได้
เว็บบลูทูธ API
มันวิเศษมากที่เราสามารถใช้แอพเนทีฟเพื่อเปลี่ยนสีของหลอดไฟ แต่เราจะทำสิ่งนี้จากเบราว์เซอร์ได้อย่างไร ปรากฎว่าด้วยความรู้เกี่ยวกับ Bluetooth และ GATT ที่เราเพิ่งเรียนรู้ สิ่งนี้ค่อนข้างง่ายด้วย WebBluetooth API JavaScript ใช้เวลาเพียงไม่กี่บรรทัดในการเปลี่ยนสีของหลอดไฟ
มาดู WebBluetooth API กัน
การเชื่อมต่อกับอุปกรณ์
สิ่งแรกที่เราต้องทำคือเชื่อมต่อจากเบราว์เซอร์กับอุปกรณ์ เราเรียกใช้ฟังก์ชัน navigator.bluetooth.requestDevice()
และจัดเตรียมฟังก์ชันด้วยอ็อบเจ็กต์การกำหนดค่า ออบเจ็กต์นั้นมีข้อมูลเกี่ยวกับอุปกรณ์ที่เราต้องการใช้และบริการใดที่ API ของเราควรมี
ในตัวอย่างต่อไปนี้ เรากำลังกรองชื่ออุปกรณ์ เนื่องจากเราต้องการดูเฉพาะอุปกรณ์ที่มีคำนำหน้า PLAYBULB
ในชื่อ เรายังระบุ 0xff0f
เป็นบริการที่เราต้องการใช้ เนื่องจากฟังก์ชัน requestDevice()
ส่งกลับคำสัญญา เราจึงสามารถรอผลลัพธ์ได้
let device = await navigator.bluetooth.requestDevice({ filters: [ { namePrefix: 'PLAYBULB' } ], optionalServices: [ 0xff0f ] });
เมื่อเราเรียกใช้ฟังก์ชันนี้ หน้าต่างจะปรากฏขึ้นพร้อมกับรายการอุปกรณ์ที่สอดคล้องกับตัวกรองที่เราระบุไว้ ตอนนี้เราต้องเลือกอุปกรณ์ที่เราต้องการเชื่อมต่อด้วยตนเอง นั่นเป็นขั้นตอนสำคัญสำหรับการรักษาความปลอดภัยและความเป็นส่วนตัวและให้การควบคุมแก่ผู้ใช้ ผู้ใช้ตัดสินใจว่าจะอนุญาตให้เว็บแอปเชื่อมต่อหรือไม่ และแน่นอนว่าอนุญาตให้เชื่อมต่อกับอุปกรณ์ใด เว็บแอปไม่สามารถรับรายการอุปกรณ์หรือเชื่อมต่อโดยที่ผู้ใช้ไม่ได้เลือกอุปกรณ์ด้วยตนเอง
หลังจากที่เราเข้าถึงอุปกรณ์แล้ว เราสามารถเชื่อมต่อกับเซิร์ฟเวอร์ GATT ได้โดยการเรียกฟังก์ชัน connect()
บนคุณสมบัติ gatt
ของอุปกรณ์และรอผล
let server = await device.gatt.connect();
เมื่อเรามีเซิร์ฟเวอร์แล้ว เราสามารถเรียก getPrimaryService()
บนเซิร์ฟเวอร์ด้วย UUID ของบริการที่เราต้องการใช้เป็นพารามิเตอร์และรอผล
let service = await server.getPrimaryService(0xff0f);
จากนั้นเรียก getCharacteristic()
บนบริการด้วย UUID ของคุณสมบัติเป็นพารามิเตอร์และรอผลลัพธ์อีกครั้ง
ตอนนี้เรามีลักษณะเฉพาะของเราที่สามารถใช้เขียนและอ่านข้อมูลได้:
let characteristic = await service.getCharacteristic(0xfffc);
กำลังเขียนข้อมูล
ในการเขียนข้อมูล เราสามารถเรียกใช้ฟังก์ชัน writeValue()
บนคุณลักษณะที่มีค่าที่เราต้องการเขียนเป็น ArrayBuffer ซึ่งเป็นวิธีการจัดเก็บข้อมูลไบนารี เหตุผลที่เราไม่สามารถใช้อาร์เรย์ปกติได้ก็คืออาร์เรย์ปกติสามารถมีข้อมูลประเภทต่างๆ และสามารถมีช่องว่างได้
เนื่องจากเราไม่สามารถสร้างหรือแก้ไข ArrayBuffer โดยตรง เราจึงใช้ 'อาร์เรย์ที่พิมพ์' แทน ทุกองค์ประกอบของอาร์เรย์ที่พิมพ์จะเป็นประเภทเดียวกันเสมอ และไม่มีรูใดๆ ในกรณีของเรา เราจะใช้ Uint8Array
ซึ่งไม่ได้ลงนาม ดังนั้นจึงไม่สามารถมีตัวเลขติดลบได้ จำนวนเต็มจึงไม่มีเศษส่วน และมีขนาด 8 บิตและสามารถมีค่าได้ตั้งแต่ 0 ถึง 255 กล่าวอีกนัยหนึ่งคืออาร์เรย์ของไบต์
characteristic.writeValue( new Uint8Array([ 0, r, g, b ]) );
เรารู้แล้วว่าหลอดไฟชนิดนี้ทำงานอย่างไร เราต้องจัดเตรียมสี่ไบต์ หนึ่งไบต์สำหรับ LED แต่ละตัว แต่ละไบต์มีค่าระหว่าง 0 ถึง 255 และในกรณีนี้ เราต้องการใช้ LED สีแดง สีเขียว และสีน้ำเงินเท่านั้น ดังนั้นเราจึงปิด LED สีขาวไว้โดยใช้ค่า 0
การอ่านข้อมูล
หากต้องการอ่านสีปัจจุบันของหลอดไฟ เราสามารถใช้ readValue()
และรอผล
let value = await characteristic.readValue(); let r = value.getUint8(1); let g = value.getUint8(2); let b = value.getUint8(3);
ค่าที่เราได้รับกลับมาคือ DataView ของ ArrayBuffer และเสนอวิธีการดึงข้อมูลออกจาก ArrayBuffer ในกรณีของเรา เราสามารถใช้ getUint8()
กับดัชนีเป็นพารามิเตอร์เพื่อดึงแต่ละไบต์ออกจากอาร์เรย์
รับแจ้งการเปลี่ยนแปลง
สุดท้าย ยังมีวิธีรับการแจ้งเตือนเมื่อมูลค่าของอุปกรณ์เปลี่ยนแปลง มันไม่มีประโยชน์อะไรสำหรับหลอดไฟ แต่สำหรับเครื่องวัดอัตราการเต้นของหัวใจ เรามีการเปลี่ยนแปลงค่าอยู่ตลอดเวลา และเราไม่ต้องการสำรวจค่าปัจจุบันด้วยตนเองทุก ๆ วินาที
characteristic.addEventListener( 'characteristicvaluechanged', e => { let r = e.target.value.getUint8(1); let g = e.target.value.getUint8(2); let b = e.target.value.getUint8(3); } ); characteristic.startNotifications();
ในการรับการเรียกกลับทุกครั้งที่มีการเปลี่ยนแปลงค่า เราต้องเรียกใช้ฟังก์ชัน addEventListener()
บนคุณลักษณะที่มีพารามิเตอร์ characteristicvaluechanged
และฟังก์ชันเรียกกลับ เมื่อใดก็ตามที่ค่าเปลี่ยนแปลง ฟังก์ชันเรียกกลับจะถูกเรียกด้วยวัตถุเหตุการณ์เป็นพารามิเตอร์ และเราสามารถรับข้อมูลจากคุณสมบัติค่าของเป้าหมายของเหตุการณ์ได้ และสุดท้ายแยกแต่ละไบต์ออกจาก DataView ของ ArrayBuffer
เนื่องจากแบนด์วิดท์บนเครือข่าย Bluetooth มีจำกัด เราจึงต้องเริ่มกลไกการแจ้งเตือนนี้ด้วยตนเองโดยเรียก startNotifications()
บนคุณลักษณะ มิฉะนั้น เครือข่ายจะถูกน้ำท่วมโดยข้อมูลที่ไม่จำเป็น นอกจากนี้ เนื่องจากอุปกรณ์เหล่านี้มักใช้แบตเตอรี่ ทุกๆ ไบต์เดียวที่เราไม่ต้องส่งจะช่วยยืดอายุการใช้งานแบตเตอรี่ของอุปกรณ์ได้อย่างชัดเจน เนื่องจากไม่จำเป็นต้องเปิดวิทยุภายในบ่อย
บทสรุป
ตอนนี้เราใช้ WebBluetooth API ไปแล้วกว่า 90% ด้วยการเรียกใช้ฟังก์ชันเพียงไม่กี่ครั้งและส่ง 4 ไบต์ คุณสามารถสร้างเว็บแอปที่ควบคุมสีของหลอดไฟได้ หากคุณเพิ่มอีกสองสามบรรทัด คุณสามารถควบคุมรถของเล่นหรือบินโดรนได้ ด้วยอุปกรณ์บลูทูธที่ออกสู่ตลาดมากขึ้นเรื่อยๆ ความเป็นไปได้ก็ไม่มีที่สิ้นสุด
แหล่งข้อมูลเพิ่มเติม
- บลูทู ธ ร็อค! สาธิต | (ซอร์สโค้ดบน GitHub)
- “ข้อมูลจำเพาะของ เว็บบลูทูธ” กลุ่มชุมชนเว็บบลูทูธ
- เปิด GATT Registry ชุดเอกสารที่ไม่เป็นทางการสำหรับบริการคุณสมบัติทั่วไปสำหรับอุปกรณ์ Bluetooth Low Energy