WebBluetooth 소개
게시 됨: 2022-03-10Progressive Web Apps를 통해 웹은 기본 앱으로 더욱 밀접하게 이동하고 있습니다. 그러나 개인 정보 보호 및 플랫폼 간 호환성과 같은 웹 고유의 추가 이점이 있습니다.
웹은 전통적으로 네트워크의 서버, 특히 인터넷의 서버와 통신하는 데 환상적이었습니다. 이제 웹이 애플리케이션으로 이동하고 있으므로 기본 앱과 동일한 기능도 필요합니다.
지난 몇 년 동안 브라우저에 구현된 새로운 사양과 기능의 양은 어마어마합니다. WebGL 및 곧 출시될 WebGPU와 같은 3D를 처리하기 위한 사양이 있습니다. 오디오를 스트리밍 및 생성하고, 비디오를 보고, 웹캠을 입력 장치로 사용할 수 있습니다. WebAssembly를 사용하여 거의 기본 속도로 코드를 실행할 수도 있습니다. 또한 웹은 처음에는 네트워크 전용 매체였지만 서비스 작업자와 함께 오프라인 지원으로 이동했습니다.
그것은 훌륭하지만 한 영역은 거의 기본 앱의 독점적인 영역이었습니다. 바로 기기와 통신하는 것입니다. 그것은 우리가 오랫동안 해결하려고 노력해 온 문제이며 모든 사람이 한 번쯤 겪을 수 있는 문제입니다. 웹은 서버와 대화하는 데 탁월하지만 장치와 대화하는 데에는 적합하지 않습니다 . 예를 들어 네트워크에 라우터를 설정하려고 한다고 생각해 보십시오. 보안 없이 일반 HTTP 연결을 통해 IP 주소를 입력하고 웹 인터페이스를 사용해야 할 가능성이 있습니다. 그것은 열악한 경험과 나쁜 보안일 뿐입니다. 게다가 올바른 IP 주소가 무엇인지 어떻게 알 수 있습니까?
HTTP는 또한 장치와 통신을 시도하는 프로그레시브 웹 앱을 만들려고 할 때 부딪히는 첫 번째 문제입니다. PWA는 HTTPS 전용이고 로컬 장치는 항상 HTTP입니다. HTTPS용 인증서가 필요하고 인증서를 얻으려면 도메인 이름을 사용하여 공개적으로 사용 가능한 서버가 필요합니다.
따라서 많은 장치의 경우 기본 앱이 웹 플랫폼의 제한에 구속되지 않고 사용자에게 즐거운 경험을 제공할 수 있기 때문에 장치를 설정하고 사용하려면 기본 앱이 필요합니다. 하지만 그렇게 하기 위해 500MB 앱을 다운로드하고 싶지는 않습니다. 가지고 있는 기기가 이미 몇 년 전이고 앱이 새 휴대전화에서 실행되도록 업데이트되지 않았을 수 있습니다. 아마도 당신은 데스크탑이나 노트북 컴퓨터를 사용하고 싶어하는데 제조사는 모바일 앱만 만들었습니다. 또한 이상적인 경험이 아닙니다.
WebBluetooth는 브라우저에서 Bluetooth Low Energy 장치와 직접 통신할 수 있도록 하는 Chrome 및 Samsung 인터넷에 구현된 새로운 사양입니다. WebBluetooth와 결합된 Progressive Web Apps는 장치와 직접 통신할 수 있는 기능을 통해 웹 응용 프로그램의 보안과 편의성을 제공합니다.
블루투스는 제한된 범위, 나쁜 오디오 품질 및 페어링 문제로 인해 꽤 나쁜 이름을 가지고 있습니다. 그러나 거의 모든 문제는 과거의 일입니다. Bluetooth Low Energy는 동일한 주파수 스펙트럼을 사용하는 것을 제외하고는 이전 Bluetooth 사양과 거의 관련이 없는 최신 사양입니다 . 매일 천만 대 이상의 장치가 Bluetooth 지원과 함께 배송됩니다. 여기에는 컴퓨터와 전화뿐만 아니라 심박수 및 혈당 측정기와 같은 다양한 장치, 전구와 같은 IoT 장치, 원격 제어 가능한 자동차 및 드론과 같은 장난감이 포함됩니다.
추천 자료 : API 기반 플랫폼 이해: 제품 관리자를 위한 가이드
지루한 이론적 부분
블루투스 자체는 웹 기술이 아니기 때문에 우리에게 생소해 보일 수 있는 몇 가지 용어를 사용합니다. 이제 Bluetooth 작동 방식과 몇 가지 용어에 대해 알아보겠습니다.
모든 Bluetooth 장치는 '중앙 장치' 또는 '주변 장치'입니다. 중앙 장치만 통신을 시작할 수 있고 주변 장치와만 통신할 수 있습니다. 중앙 장치의 예로는 컴퓨터나 휴대 전화가 있습니다.
주변기기는 통신을 시작할 수 없으며 중앙 장치와만 통신할 수 있습니다. 또한 주변 장치는 동시에 하나의 중앙 장치와만 통신할 수 있습니다. 주변기기는 다른 주변기기와 통신할 수 없습니다.
중앙 장치는 동시에 여러 주변 장치와 통신할 수 있으며 원하는 경우 메시지를 전달할 수 있습니다. 따라서 심박수 모니터는 전구와 통신할 수 없지만 심박수를 수신하고 심박수가 특정 임계값을 초과하면 표시등을 빨간색으로 바꾸는 중앙 장치에서 실행되는 프로그램을 작성할 수 있습니다.
WebBluetooth에 대해 이야기할 때 매우 분명한 약어 GATT가 있는 Generic Attribute Profile이라는 Bluetooth 사양의 특정 부분에 대해 이야기하고 있습니다. (분명히, GAP는 이미 취해졌다.)
GATT의 맥락에서 우리는 더 이상 중앙 장치와 주변 장치가 아니라 클라이언트와 서버에 대해 이야기하고 있습니다. 전구는 서버입니다. 이는 직관적이지 않은 것처럼 보일 수 있지만 실제로 생각해보면 의미가 있습니다. 전구는 서비스, 즉 빛을 제공합니다. 브라우저가 인터넷의 서버에 연결할 때와 마찬가지로 전화기나 컴퓨터는 전구에서 GATT 서버에 연결하는 클라이언트입니다.
각 서버는 하나 이상의 서비스를 제공합니다. 이러한 서비스 중 일부는 공식적으로 표준의 일부이지만 사용자가 직접 정의할 수도 있습니다. 심박수 모니터의 경우 규격에 정의된 공식 서비스가 있습니다. 전구의 경우에는 없고 거의 모든 제조업체에서 바퀴를 재발명하려고 합니다. 모든 서비스에는 하나 이상의 특성이 있습니다. 각 특성에는 읽거나 쓸 수 있는 값이 있습니다. 지금은 각 개체에 값이 있는 속성이 있는 개체의 배열로 생각하는 것이 가장 좋습니다.
객체의 속성과 달리 서비스와 특성은 문자열로 식별되지 않습니다. 각 서비스 및 특성에는 16비트 또는 128비트 길이의 고유한 UUID가 있습니다. 공식적으로 16비트 UUID는 공식 표준을 위해 예약되어 있지만 거의 아무도 그 규칙을 따르지 않습니다. 마지막으로 모든 값은 바이트 배열입니다. 블루투스에는 멋진 데이터 유형이 없습니다.
블루투스 전구 자세히 보기
이제 실제 Bluetooth 장치인 Mipow Playbulb Sphere를 살펴보겠습니다. BLE 스캐너 또는 nRF Connect와 같은 앱을 사용하여 장치에 연결하고 모든 서비스 및 특성을 볼 수 있습니다. 이 경우 iOS용 BLE 스캐너 앱을 사용하고 있습니다.
전구에 연결하면 가장 먼저 보이는 것은 서비스 목록입니다. 기기정보 서비스, 배터리 서비스 등의 표준화된 서비스가 있습니다. 그러나 일부 맞춤 서비스도 있습니다. 특히 16비트 UUID가 0xff0f
인 서비스에 관심이 있습니다. 이 서비스를 열면 긴 특성 목록을 볼 수 있습니다. UUID로만 식별되고 불행히도 사용자 지정 서비스의 일부이기 때문에 이러한 특성의 대부분이 무엇을 하는지 모르겠습니다. 표준화되지 않았으며 제조업체는 문서를 제공하지 않았습니다.
UUID가 0xfffc
인 첫 번째 특성은 특히 흥미로운 것 같습니다. 값은 4바이트입니다. 이 바이트의 값을 0x00000000
에서 0x00ff0000
으로 변경하면 전구가 빨간색으로 바뀝니다. 0x0000ff00
으로 변경하면 전구가 녹색으로 바뀌고 0x000000ff
는 파란색으로 바뀝니다. 이것은 RGB 색상이며 HTML 및 CSS에서 사용하는 16진수 색상과 정확히 일치합니다.
첫 번째 바이트는 무엇을 합니까? 값을 0xff000000
으로 변경하면 전구가 흰색으로 바뀝니다. 전구에는 4개의 서로 다른 LED가 포함되어 있으며 4바이트 각각의 값을 변경하여 원하는 모든 단일 색상을 만들 수 있습니다.
웹블루투스 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();
서버가 있으면 매개변수로 사용하려는 서비스의 UUID를 사용하여 서버에서 getPrimaryService()
를 호출하고 결과를 기다릴 수 있습니다.
let service = await server.getPrimaryService(0xff0f);
그런 다음 특성의 UUID를 매개변수로 사용하여 서비스에서 getCharacteristic()
을 호출하고 다시 결과를 기다립니다.
이제 데이터를 쓰고 읽는 데 사용할 수 있는 특성이 있습니다.
let characteristic = await service.getCharacteristic(0xfffc);
데이터 쓰기
데이터를 쓰기 위해 이진 데이터의 저장 방법인 ArrayBuffer로 쓰고자 하는 값을 가진 특성에 대해 writeValue()
함수를 호출할 수 있습니다. 일반 배열을 사용할 수 없는 이유는 일반 배열이 다양한 유형의 데이터를 포함할 수 있고 빈 구멍이 있을 수도 있기 때문입니다.
ArrayBuffer를 직접 생성하거나 수정할 수 없기 때문에 대신 '형식화된 배열'을 사용하고 있습니다. 유형이 지정된 배열의 모든 요소는 항상 동일한 유형이며 구멍이 없습니다. 우리의 경우 음수를 포함할 수 없도록 부호가 없는 Uint8Array
를 사용할 것입니다. 정수이므로 분수를 포함할 수 없습니다. 8비트이며 0에서 255 사이의 값만 포함할 수 있습니다. 즉, 바이트 배열입니다.
characteristic.writeValue( new Uint8Array([ 0, r, g, b ]) );
우리는 이 특정 전구가 어떻게 작동하는지 이미 알고 있습니다. 각 LED에 하나씩 4바이트를 제공해야 합니다. 각 바이트는 0에서 255 사이의 값을 가지며 이 경우 빨간색, 녹색 및 파란색 LED만 사용하려고 하므로 값 0을 사용하여 흰색 LED를 끕니다.
데이터 읽기
전구의 현재 색상을 읽으려면 readValue()
함수를 사용하고 결과를 기다릴 수 있습니다.
let value = await characteristic.readValue(); let r = value.getUint8(1); let g = value.getUint8(2); let b = value.getUint8(3);
반환되는 값은 ArrayBuffer의 DataView이며 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();
값이 변경될 때마다 콜백을 가져오려면 특성값이 변경된 characteristicvaluechanged
과 콜백 함수를 사용하여 특성에 대해 addEventListener()
함수를 호출해야 합니다. 값이 변경될 때마다 이벤트 객체를 매개변수로 하여 콜백 함수가 호출되며 이벤트 대상의 값 속성에서 데이터를 가져올 수 있습니다. 그리고 마지막으로 ArrayBuffer의 DataView에서 개별 바이트를 다시 추출합니다.
Bluetooth 네트워크의 대역폭이 제한되어 있기 때문에 특성에 대해 startNotifications()
를 호출하여 이 알림 메커니즘을 수동으로 시작해야 합니다. 그렇지 않으면 네트워크가 불필요한 데이터로 가득 차게 됩니다. 또한 이러한 장치는 일반적으로 배터리를 사용하기 때문에 보낼 필요가 없는 모든 단일 바이트는 내부 라디오를 자주 켤 필요가 없기 때문에 장치의 배터리 수명을 확실히 향상시킵니다.
결론
이제 WebBluetooth API의 90% 이상을 사용했습니다. 몇 번의 함수 호출과 4바이트 전송만으로 전구의 색상을 제어하는 웹 앱을 만들 수 있습니다. 몇 줄만 더 추가하면 장난감 자동차를 조종하거나 드론을 날릴 수도 있습니다. 점점 더 많은 Bluetooth 장치가 시장에 출시됨에 따라 가능성은 무한합니다.
추가 리소스
- 블루투스.록! 데모 | (GitHub의 소스 코드)
- "웹 블루투스 사양", 웹 블루투스 커뮤니티 그룹
- Open GATT Registry Bluetooth 저에너지 장치에 대한 일반 속성 서비스에 대한 비공식 문서 모음입니다.