独自のBluetooth Low Energyデバむス甚のWebアプリケヌションを䜜成する方法は

数週間前、楜しみのために、単玔なロボットアヌムマニピュレヌタヌを組み立お、スマヌトフォンからBluetooth経由で制埡をねじ蟌むこずにしたした。 ネむティブモバむルアプリケヌションの開発経隓はただありたせんが、 Apache Cordovaにはすでに粟通しおいたすが 、 プログレッシブWebアプリのチップで味付けされたWeb Bluetooth APIを䜿甚するこずは興味深いでしょう。



泚目を集める画像

猫の䞋をリヌドするアトラクションの写真



䞀芋するず、キヌワヌドに関する十分な蚘事があるように芋えるかもしれたせんWeb Bluetooth 仕様 、 䟋付きのGoogle Developersブログの詳现な蚘事 、 Bluetooth Low Energyの詳现な分析 、さたざたなBLEデバむスのリバヌス゚ンゞニアリングプロトコルの䟋、そしお点滅する「スマヌト」ラむト、フィットネスブレスレットからブラりザから盎接デヌタを受信する- 䜕が問題になる可胜性がありたすか



だから、自分のデバむスを䜜り、ブラりザから圌ずチャットしようずするたで、私は考えた。 深く掘り䞋げるこずはせず、実際の経隓ずコヌドを共有するだけです。個人的には3週間前に非垞に圹立぀でしょう:)



目次





問題



そのため、デバむスを組み立お、Bluetoothモゞュヌルを装備しおブラりザヌからアクセスするこずにしたした。 以䞋では、䟋ずしお、BluetoothモゞュヌルをArduino Unoに接続する様子を瀺したす。もちろん、Arduinoベヌスのデバむスもありたすが、もちろん、 STM 、 Raspberry 、 ESP8266などを䜿甚しおも基本的な違いはありたせん。 重芁なこずは、コントロヌラヌがUARTプロトコルを介しおBluetoothモゞュヌルず連携するこずです GeektimesたたはWikipediaを参照 。



私のように、叀き良きHC-05 Bluetoothモゞュヌルを既にデバむスに固定し、ファヌムりェアをダりンロヌドし、Googleのサンプルの䞀郚を起動し、ブラりザヌがデバむスを怜出しない理由を理解できない堎合、倱望させたすWeb Bluetoothは「Bluetooth 4暙準」のみをサポヌトしたす。



これが蚘事を曞く理由です。私のように、すぐにBLEモゞュヌルたずえばHM-10などを備えた最寄りのストアから満足のいく状態で戻るず、たったく異なる方法で動䜜し、最も重芁なこずには、シリアルポヌトプロファむル シリアルポヌトプロファむル 、SPP、 Baumanラむブラリの詳现 をサポヌトしたす。これにより、バむトを前埌に䞍甚意に远いかけるこずに慣れおいたす。



Bluetooth Low Energyの抂念、特にGeneric Attribute Profile GATTに぀いおは既にご存知かもしれたせんが、ここで重芁なこずを簡単に説明しおみたしょう。 自䜜のシリアルプロトコルではなく、デバむスがアプリケヌションのセットを提䟛する必芁がありたす接続されたデバむスが読み取りおよび/たたは倉曎できる「特性」。



たずえば、ロボットアヌムを䜿甚するず、3぀の座暙数倀X、Y、Zで空間を移動し、爪を開く0たたは閉じる1こずができたす。 そのため、接続されたデバむスが必芁な倀を認識、読み取り、曞き蟌みできる4぀の特性を読み曞きするようにBLEモゞュヌルを構成する必芁がありたす。



これは玠晎らしいこずですが、これは悪いこずです。「近隣店」たたはAliexpressで入手できる通垞のBLEレベルの「趣味」モゞュヌルHM-10、JDY-08、AT-09、CC41-A、たたはその他のサヌビスや機胜を構成する機胜はありたせん。



代わりに、シリアルポヌトを゚ミュレヌトする特性を1぀だけ提䟛し、それに曞き蟌むすべおのもの、モゞュヌルはTXを介しおコントロヌラヌに送信し、コントロヌラヌからモゞュヌルのRXに送信するすべおは接続されたデバむスに送信されたす。 ちなみに、BLE特性に固有の20バむトずいう制限がありたす。



したがっお、Web Bluetoothは䞀般的な属性のプロファむルの䜿甚に限定されおいるずいう事実にもかかわらず、実際には「家庭」で䜿甚するためにシリアルポヌトプロファむルを䜜成する必芁がありたす。



Bluetooth Low Energyモゞュヌルの構成



たず、BLEモゞュヌルを構成したす。䜕をどのように知っおいれば、それほど時間はかかりたせん。 たたたた、 テキサスむンスツルメンツのCC2541チップ䞊のCC41-Aモゞュヌルを手にしたので 、「近くの店」で340ルヌブルかかりたした。 したがっお、䟋ずしお、その構成を正確に説明したすが、本質は同様のチップを䜿甚する他のモゞュヌルに共通です。



HM-10の䟋のBLEモゞュヌルのピン配列






HM-10の䟋のBLEモゞュヌルのピン配列、クリック可胜



USB-TTLコンバヌタヌをお持ちの堎合は、BLEモゞュヌルを接続するだけで、COMポヌトを介しおコンピュヌタヌからモゞュヌルに盎接アクセスできたす。 モゞュヌルの説明に泚意しおください。3.3Vロゞックで動䜜する可胜性があるため、TX-RXおよびRX-TXラむンでは、 EasyElectronicsの味ず色に電圧レベルシフタヌを䜿甚する必芁がありたす。 CC41-Aモゞュヌルは、「LEVEL3.3V」ず衚瀺されおいたすが、5Vロゞックに完党に察応しおいたす。



BLEモゞュヌルをUSB-TTLコンバンタヌに接続する






BLEモゞュヌルをUSB-TTLコンバヌタヌに接続、クリック可胜



コンバヌタヌの代わりに、最も単玔なシリアルブリッゞを䜿甚しおコントロヌラヌを䜿甚できたす。コントロヌラヌは、あるシリアルポヌトに送信したものをすべお別のシリアルポヌトに送信したす。 Arduino Unoの堎合、 SoftwareSerialラむブラリを䜿甚する必芁がありたす。



Arduino Unoのスケッチ䟋
#include <SoftwareSerial.h> SoftwareSerial SerialBt(2, 3); void setup() { Serial.begin(9600); SerialBt.begin(9600); } void loop() { if (SerialBt.available()) { Serial.write(SerialBt.read()); } if (Serial.available()) { SerialBt.write(Serial.read()); } }
      
      







BLEモゞュヌルをArduino Unoに接続し続ける


BLEモゞュヌルをArduino Unoに接続、クリック可胜



タヌミナルプログラムを実行しArduino IDEからシリアルモニタヌを䜿甚できたす。私はBrayのタヌミナルを䜿甚したす、暙準蚭定のBLEモゞュヌルがハングするCOMポヌトに接続したす。







スタンバむモヌドでは、モゞュヌルはキャリッゞリタヌンずラむンフィヌドで終わるATコマンドに応答したす シリアルモニタヌのCR+LF



、「NLずCRの䞡方」オプション。 䞀郚のBLEモゞュヌルはデフォルトで異なる速床で実行されたすたずえば、38400。䞀郚のモゞュヌルはボヌド䞊のボタンをクリックした埌に構成モヌドに入りたす。䞀郚のモゞュヌルではコマンドが倧文字である必芁はありたせん。具䜓的にはモゞュヌルの仕様を確認しおください。



BLEモゞュヌルの構成䞭のタヌミナルプログラムりィンドり


BLEモゞュヌルの構成䞭の端末プログラムのりィンドり、

クリック可胜



ATコマンドを送信しお接続を確認したしょう。 モゞュヌルは「OK」ず答えるはずです-それですべおが敎いたす。 実際、モゞュヌルがスレヌブモヌドで動䜜し、マスタヌデバむスが接続されるのを埅機し、サヌビスUUIDが0xFFE0



で、特性UUIDが0xFFE1



蚭定されおいるこずを確認するだけで十分です。これは将来必芁になりたす。 私のモゞュヌルで動䜜するいく぀かのコマンド







これで、たずえば、 Characteristic Properties Sampleペヌゞから、サヌビスずしお「0xFFE0」を指定し、 特性ずしお「0xFFE1」を指定しお、BLEモゞュヌルぞの接続を詊みるこずができたす 。 たたは、 通知サンプルペヌゞで端末からブラりザに䜕かを送信するこずもできたす。



端末から送信された特性情報ずデヌタを受信する


端末から送信された特性ずデヌタに関する情報の受信はクリック可胜です



Webアプリケヌションの䜜成



りォヌムアップは終了したした。最も興味深いものに進みたす



コンセプト



デバむスの管理の抂念を怜蚎するこずを提案したす。 ブラりザヌの通垞のHTMLペヌゞで、デバむスずの察話を実装するさたざたなコントロヌルを䜿甚しお特定のUIを䜜成したす。



ロボットアヌムを制埡するためのサンプルUIアプリケヌション


ロボットアヌムを制埡するためのUIアプリケヌションの䟋、

クリック可胜



たずえば、3぀の座暙で移動し、爪を開閉するロボットアヌムの堎合、3぀の数倀スラむダヌたたは2Dサヌフェスであり、クリックするずXおよびY倀が蚈算され、1぀のスラむダヌがZ軞ずボタンに沿っお移動したす開閉爪。 やかんの堎合は、「ボむル」ボタンを䜜成できたす。 これがラゞコン機の堎合、ボタンを「進む」、「戻る」、「巊」、「右」、「ヘッドラむトのオン/オフ」、「信号を送る」などを行うこずができたす。



ボンネットの䞋で䜕が起こっおいるかの党䜓像


ボンネットの䞋で䜕が起こっおいるかの党䜓像はクリック可胜です



JavaScriptで特定のUI芁玠の状態を抌したり倉曎したりするずきにハンドラヌをハングさせるこずにより、Web Bluetooth APIを介しおデバむスに送信するメッセヌゞを䜜成したす。 BLEモゞュヌルはメッセヌゞを受信し、UARTを介しおコントロヌラヌに送信し、コントロヌラヌはメッセヌゞを解析し、必芁なアクションを実行し、同じUARTを䜿甚しおメッセヌゞたたは応答の圢匏でBLEモゞュヌルに返信できたす。その埌、モゞュヌルは接続されたデバむスに送信し、JSを䜿甚しお応答を受信したすブラりザ



たずえば、ボタンをクリックしおクロヌを閉じるず、 onclick



ボタンハンドラヌが起動し、メッセヌゞGRIPPER=CLOSE



が送信されたす。 コントロヌラヌはメッセヌゞを受信し、必芁なものを理解し、クロヌを閉じおメッセヌゞGRIPPER=CLOSED



を送り返したす。 このメッセヌゞを凊理するず、JSで爪の状態が蚘憶され、ボタンのテキストが「開く」に倉曎されたす。



挑戊する



HTMLペヌゞUIを䜜成し、JavaScriptむベントハンドラヌを単玔に操䜜するこずはそれほど難しい問題ではなく、Webテクノロゞヌに関する十分な基瀎知識がありたす。 したがっお、特定のデバむスから抜象化し、デバむスに接続しおメッセヌゞを亀換するタヌミナルアプリケヌションを䜜成するこずをお勧めしたす。



たた、Bluetooth Low Energyデバむスに接続し、 接続が倱われた堎合に再接続し、20バむトのBLE特性の長さの制限をバむパス するプロセスのロギングを実装したす。



最埌に、通垞のHTMLペヌゞをプログレッシブWebアプリケヌション  Google Developersの Progressive Web Apps、英語版のWikipedia に倉換したす。これは、スマヌトフォンのデスクトップにむンストヌルし、むンタヌネットがなくおも䜿甚できたす。



HTMLペヌゞずデバむス、安定した接続、シンプルなAPIの間でメッセヌゞを亀換する機䌚があり、ニヌズに合わせおアプリケヌションを調敎するこずは難しくありたせん。



準備する



お気に入りのIDEに加えお、前に構成した䜜業甚の「デバむス」が必芁です。これは、コンピュヌタヌのタヌミナルプログラムを介しおリアルタむムでメッセヌゞを送受信し、アプリケヌションをテストするのに圹立ちたす。



Web Bluetooth APIは、 Chrome 56以降およびOpera 43 以降でデフォルトで䜿甚可胜です 。 たた、Google Developersの蚘事では、Linuxではchromeを有効にする必芁があるず述べおいたす// flags /enable-experimental-web-platform-features flagずブラりザを再起動



最埌の重芁なポむントWebアプリケヌションは、 HTTPS  GitHub Pagesを䜿甚できたすたたはhttp// localhostで開く必芁がありたす -これらはセキュリティ芁件です。



UI



アプリケヌションは、1぀のHTML index.html



ペヌゞ、1぀のstyles.css



、および1぀のmain.js



ファむルで構成され、すべおの魔法が発生したす。



デバむスに接続するためのボタン、切断ボタン、メッセヌゞ甚のdivコンテナ、およびテキストフィヌルドず「送信」ボタンで構成される送信フォヌムを䜜成したす。



index.html
 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link href="styles.css" rel="stylesheet"> </head> <body> <button id="connect" type="button">Connect</button> <button id="disconnect" type="button">Disconnect</button> <div id="terminal"></div> <form id="send-form"> <input id="input" type="text"> <button type="submit">Send</button> </form> <script src="main.js"></script> </body> </html>
      
      





divコンテナでは、接続ログ、着信および発信メッセヌゞを次の圢匏で衚瀺したす。



 <div id="terminal"> <div>  ...</div> <div class="out"> </div> <div class="in"> </div> </div>
      
      





どのメッセヌゞが発信されたのかを掚枬しないために、スタむルの色でそれらを分けたす。



styles.css
 #terminal div { color: gray; } #terminal div.out { color: red; } #terminal div.in { color: blue; }
      
      





ご芧のずおり、特別なものはありたせん。 むンタヌフェむスの準備ができたした:)



むベントハンドラヌ



main.js



さらに䜜業が行われmain.js







UI芁玠ぞのリンクを取埗し、接続ボタンず切断ボタンをクリックしおフォヌムを送信するこずにより、ハンドラヌを切断したす。



 //     UI let connectButton = document.getElementById('connect'); let disconnectButton = document.getElementById('disconnect'); let terminalContainer = document.getElementById('terminal'); let sendForm = document.getElementById('send-form'); let inputField = document.getElementById('input'); //        Connect connectButton.addEventListener('click', function() { connect(); }); //        Disconnect disconnectButton.addEventListener('click', function() { disconnect(); }); //     sendForm.addEventListener('submit', function(event) { event.preventDefault(); //    send(inputField.value); //     inputField.value = ''; //    inputField.focus(); //      }); //   Bluetooth      function connect() { // } //     function disconnect() { // } //     function send(data) { // }
      
      





デバむス接続



完党な接続アルゎリズムは、いく぀かの段階で構成されおいたす。



  1. Bluetoothデバむスの芁求ブラりザは、最も近いデバむスを怜玢しお遞択するためのダむアログを開始し、ナヌザヌが遞択を行い、アプリケヌションコヌドがオブゞェクトを受け取りたす。
  2. アプリケヌションコヌドからデバむスに接続したす。
    1. 䞀般属性プロファむルサヌバヌGATTサヌバヌぞの接続、
    2. 適切なサヌビスを取埗し、
    3. 所望の特性を取埗したす。
  3. 特性の倉曎に関する通知を含める-デバむスからメッセヌゞを受信する必芁がありたす。




コヌドで実行しおみたしょう



 //     let deviceCache = null; //   Bluetooth      function connect() { return (deviceCache ? Promise.resolve(deviceCache) : requestBluetoothDevice()). then(device => connectDeviceAndCacheCharacteristic(device)). then(characteristic => startNotifications(characteristic)). catch(error => log(error)); } //   Bluetooth  function requestBluetoothDevice() { // } //    ,     function connectDeviceAndCacheCharacteristic(device) { // } //       function startNotifications(characteristic) { // } //    function log(data, type = '') { // }
      
      





Promiseのconnect()



関数では、接続ステップに察応するチェヌンPromiseオブゞェクトを返す関数のチェヌンを実装したした。



たた、倉数deviceCache



導入したした。この倉数には、ナヌザヌが遞択したデバむスのオブゞェクトを埌で曞き留めお、切断した堎合に再接続する察象を確認したす。



connect()



関数の本文の最初の行で、䞉項挔算子はdeviceCache



オブゞェクトで実行されるPromiseがれロに等しくない堎合は盎ちに䜜成し、そうでない堎合はBluetoothデバむスを遞択するための芁求関数を呌び出したす。 したがっお、ナヌザヌが既にデバむスに接続しおいる堎合、次に「接続」ボタンをクリックするず、デバむス遞択ダむアログは衚瀺されたせん。



いずれかの段階で゚ラヌが発生した堎合、 log()



関数を䜿甚しお端末に出力したすが、これも埌で実装したす。



Bluetoothデバむスリク゚スト



Bluetoothデバむスの遞択を芁求するには、関心のあるBluetoothデバむスを蚘述する必須の匕数ずしお構成オブゞェクトを指定しおnavigator.bluetooth.requestDevice()



関数を呌び出す必芁がありたす。 名前、サヌビスごずにフィルタヌを䜿甚できたすが、すべおのデバむスを受け入れるこずができたすが、䜿甚するサヌビスを指定する必芁がありたす。そうしないず、ブラりザヌはアクセスを提䟛したせん。



 //   Bluetooth  function requestBluetoothDevice() { log('Requesting bluetooth device...'); return navigator.bluetooth.requestDevice({ filters: [{services: [0xFFE0]}], }). then(device => { log('"' + device.name + '" bluetooth device selected'); deviceCache = device; return deviceCache; }); }
      
      





䜿甚するBLEモゞュヌルが構成されたUUID 0xFFE0



サヌビスを提䟛するすべおのデバむスをリク゚ストしたす。 ナヌザヌがデバむスを遞択するず、デバむスオブゞェクトを䜿甚しおPromiseが実行され、前述のキャッシュに曞き蟌み、さらに戻りたす。



デバむスぞの接続、サヌビスオブゞェクトず特性の受信



 //    let characteristicCache = null; //    ,     function connectDeviceAndCacheCharacteristic(device) { if (device.gatt.connected && characteristicCache) { return Promise.resolve(characteristicCache); } log('Connecting to GATT server...'); return device.gatt.connect(). then(server => { log('GATT server connected, getting service...'); return server.getPrimaryService(0xFFE0); }). then(service => { log('Service found, getting characteristic...'); return service.getCharacteristic(0xFFE1); }). then(characteristic => { log('Characteristic found'); characteristicCache = characteristic; return characteristicCache; }); }
      
      





私たちは、それ自䜓を語るシンプルなPromiseチェヌンを実行したす。 deviceCache



倉数は、 deviceCache



ず同様に、受信した特性オブゞェクトを保存したす。デヌタを曞き蟌む、぀たり、ブラりザからデバむスにメッセヌゞを送信する必芁がありたす。



getPrimaryService()



およびgetCharacteristic()



関数は、BLEモゞュヌルが機胜するように構成されおいる匕数ずしおUUIDを䜿甚したす。



機胜倉曎通知を有効にする



 //       function startNotifications(characteristic) { log('Starting notifications...'); return characteristic.startNotifications(). then(() => { log('Notifications started'); }); }
      
      





特性オブゞェクトのstartNotifications()



メ゜ッドを参照しお、特性倉曎むベントでハンドラヌをハングさせるだけで十分ですが、それに぀いおは埌で詳しく説明したす。



タヌミナル出力



端末ぞの出力機胜を実装しお、今すぐデバむスぞの接続をテストしたす。



 //    function log(data, type = '') { terminalContainer.insertAdjacentHTML('beforeend', '<div' + (type ? ' class="' + type + '"' : '') + '>' + data + '</div>'); }
      
      





insertAdjacentHTML()



メ゜ッドを䜿甚しお、タヌミナルのdivコンテナの最埌にtype



匕数で指定されたクラスを持぀divを挿入したす。これは非垞に簡単です。



テスト䞭



ブラりザでペヌゞを開き、「接続」ボタンをクリックするず、デバむス遞択ダむアログが起動したす。 デバむスに接続するず、接続プロセスに関するメッセヌゞが端末に衚瀺されたす。



デバむスの遞択ず接続


デバむスの遞択ず接続、クリック可胜



その埌、どこにも曞かれおいない萜ずし穎がいく぀か芋぀かりたした。 サヌビスメッセヌゞの出力は、問題の蚺断ず解決に圹立ちたした。



1぀目は、 Mi Bandが接続されおいる電話機からデバむスに接続し、BLEで動䜜し、近接しおいる堎合、接続は非垞にたれでしたが、接続された堎合、ほずんどすぐに脱萜したこずです。 これはネむティブアプリケヌションでも起こりたした。 私はMi Bandを遠くに連れお行こうずしたした-それは助けにはなりたせんでした。 ブレスレットを解くのではなく、別のスマヌトフォンを䜿甚しおいたす。 同様の問題が発生した堎合は、スマヌトフォンず同時に通信するデバむスに泚意しおください。



2番目の石はおそらく石ではなく、通垞は最初の接続䞭に珟れる機胜です明癜な理由もなく接続が突然倱われる可胜性があり、ブラりザがサポヌトを提䟛しないため、再接続機胜を独自に実装する必芁がありたす。



自動再接続



切断を远跡するために、Web Bluetoothはgattserverdisconnected



むベントを提䟛したす。 gattserverdisconnected



むベントのハンドラヌはデバむスオブゞェクトでハングする必芁がありたす。 最も論理的な堎所は、デバむス遞択機胜です。



 //   Bluetooth  function requestBluetoothDevice() { log('Requesting bluetooth device...'); return navigator.bluetooth.requestDevice({ filters: [{services: [0xFFE0]}], }). then(device => { log('"' + device.name + '" bluetooth device selected'); deviceCache = device; //   deviceCache.addEventListener('gattserverdisconnected', handleDisconnection); return deviceCache; }); } //   function handleDisconnection(event) { let device = event.target; log('"' + device.name + '" bluetooth device disconnected, trying to reconnect...'); connectDeviceAndCacheCharacteristic(device). then(characteristic => startNotifications(characteristic)). catch(error => log(error)); }
      
      





これで、デバむスに接続し、電源をオフにしおBluetooth接続が倱われた堎合、ブラりザヌは1回再接続を詊みたす。



再接続の詊行


再接続を詊行、クリック可胜



デバむスから切断する



切断する前に、割り圓おられたハンドラヌをgattserverdisconnected



むベントから削陀するこずを忘れないこずが重芁です。そうしないず、ブラりザヌは単に再接続したす。



 //     function disconnect() { if (deviceCache) { log('Disconnecting from "' + deviceCache.name + '" bluetooth device...'); deviceCache.removeEventListener('gattserverdisconnected', handleDisconnection); if (deviceCache.gatt.connected) { deviceCache.gatt.disconnect(); log('"' + deviceCache.name + '" bluetooth device disconnected'); } else { log('"' + deviceCache.name + '" bluetooth device is already disconnected'); } } characteristicCache = null; deviceCache = null; }
      
      





deviceCache



リセットするこずはできたせん。次に「接続」ボタンをクリックするず、デバむス遞択ダむアログは衚瀺されず、代わりに前のデバむスに接続したす。



断線


無効化、クリック可胜



デヌタ怜玢



デヌタは、BLE特性の倀が倉化したずきに発生する通知メカニズムを䜿甚しお、デバむスから非同期的に受信されたす。 関連するむベントのcharacteristicvaluechanged



特性のみを賌読する必芁がありたす。 これは、通知を有効にした埌に行う必芁がありたす。 たた、デバむスがオフになっおいるずきに、特性からプロセッサを削陀するこずも正しいでしょう。



 //       function startNotifications(characteristic) { log('Starting notifications...'); return characteristic.startNotifications(). then(() => { log('Notifications started'); //   characteristic.addEventListener('characteristicvaluechanged', handleCharacteristicValueChanged); }); } //     function disconnect() { if (deviceCache) { log('Disconnecting from "' + deviceCache.name + '" bluetooth device...'); deviceCache.removeEventListener('gattserverdisconnected', handleDisconnection); if (deviceCache.gatt.connected) { deviceCache.gatt.disconnect(); log('"' + deviceCache.name + '" bluetooth device disconnected'); } else { log('"' + deviceCache.name + '" bluetooth device is already disconnected'); } } //   if (characteristicCache) { characteristicCache.removeEventListener('characteristicvaluechanged', handleCharacteristicValueChanged); characteristicCache = null; } deviceCache = null; } //   function handleCharacteristicValueChanged(event) { let value = new TextDecoder().decode(event.target.value); log(value, 'in'); }
      
      





event.target.value



は、デバむスからのメッセヌゞがあるArrayBufferを含むDataViewオブゞェクトです。 TextDecoder



 MDN、英語のみ を䜿甚しお、バむト配列をテキストにオヌバヌラむドしたす。



端末からデヌタを送信し、ブラりザで受信する


端末からデヌタを送信し、ブラりザで受信、クリック可胜



テストでは、デバむスからのメッセヌゞの受信は、 CR



、 LF



行末の有無にかかわらず、安定しお機胜するこずが瀺されおいたす。 長いメッセヌゞは完党に届きたすが、20バむトの倍数に分割されたす。



䞭間バッファヌの玹介



20バむトを超えるメッセヌゞのサポヌトは必芁ないかもしれたせんが、完党を期すために、この制限を回避したしょう。 考え方は簡単です。セパレヌタ文字が受信されるたで、着信バッファを䞭間バッファに曞き蟌みたす。 区切り文字を受け取ったら、3番目の関数を呌び出しお、バッファヌからデヌタを枡し、その埌の曞き蟌みのためにバッファヌをクリアしたす。



区切り文字は、文字列 LF



、 \n



を䟛絊するために論理的です。 たた、メッセヌゞの最初ず最埌から空癜を削陀するず䟿利な堎合がありたす。



 //      let readBuffer = ''; //   function handleCharacteristicValueChanged(event) { let value = new TextDecoder().decode(event.target.value); for (let c of value) { if (c === '\n') { let data = readBuffer.trim(); readBuffer = ''; if (data) { receive(data); } } else { readBuffer += c; } } } //    function receive(data) { log(data, 'in'); }
      
      





デバむス固有のWebアプリケヌションを䜜成する堎合、 receive()



関数をニヌズに合わせお倉曎し、デバむスからの確実なメッセヌゞで䜜業しおいるこずを確認できたす。



䞭間バッファヌの導入埌、端末からデヌタを送信し、ブラりザヌで受信する


䞭間バッファの導入埌に端末からデヌタを送信し、ブラりザで受信、クリック可胜




デヌタを送信する



デバむスぞのデヌタの送信は、特性に倀を曞き蟌むこずによっお、より具䜓的には、匕数ずしおwriteValue()



特性オブゞェクトのメ゜ッドを呌び出すこずによっお実行されArrayBuffer



たす。文字列を倉換するArrayBuffer



最も簡単な方法はTextEncoder



英語のみのMDNです。



 //     function send(data) { data = String(data); if (!data || !characteristicCache) { return; } writeToCharacteristic(characteristicCache, data); log(data, 'out'); } //     function writeToCharacteristic(characteristic, data) { characteristic.writeValue(new TextEncoder().encode(data)); }
      
      





念のため、グロヌバルオブゞェクトを䜿甚しおデヌタを文字列型にキャストしたすString



。



このような実装では、20バむトの制限も適甚されたす。スコヌプを超えるものはすべお切り捚おられたす。したがっお、メッセヌゞが20バむトより長い堎合は、メッセヌゞを断片に分割し、少し遅れお順次送信する䟡倀がありたす。



 //     function send(data) { data = String(data); if (!data || !characteristicCache) { return; } data += '\n'; if (data.length > 20) { let chunks = data.match(/(.|[\r\n]){1,20}/g); writeToCharacteristic(characteristicCache, chunks[0]); for (let i = 1; i < chunks.length; i++) { setTimeout(() => { writeToCharacteristic(characteristicCache, chunks[i]); }, i * 100); } } else { writeToCharacteristic(characteristicCache, data); } log(data, 'out'); }
      
      





コントロヌラヌ偎でのメッセヌゞの凊理を容易にするために、送信されたメッセヌゞの最埌に改行文字\n



を远加したす。



次に、メッセヌゞは、キャリッゞリタヌンCR



、\r



およびラむンフィヌドLF



、\n



を正しく凊理する正芏衚珟を䜿甚しお断片に分割され、その埌、最初の郚分がすぐに送信され、100ミリ秒の倍数の遅延を持぀タむマヌが他を送信するように蚭定されたす





端末ずブラりザ間のデヌタ亀換、クリック可胜




うたくいく デバむスずの完党に機胜する双方向のデヌタ亀換を取埗したしたが、それはすべおJSです。



プログレッシブWebアプリ



デバむスがどのような状態になるかを事前に知るこずはできたせん。そのため、むンタヌネットなしで䜜成されたWebアプリケヌションを操䜜できるず䟿利です。そしお、ここでプログレッシブWebアプリの抂念が圹立ちたす英語でGoogle DevelopersたたはWikipediaの詳现を参照。簡単に蚀えば、これらはナヌザヌにずっお通垞たたはモバむルアプリケヌションのように芋えるWebサむトです。PWAテクノロゞヌを䜿甚するず、最初にWebサむトにアクセスしたずきに、スマヌトフォンのデスクトップにアプリケヌションずしおむンストヌルし、オフラむンで䜜業できたす。



アむコン



アむコンは、デスクトップにアプリケヌションをむンストヌルするために必芁です。個人的に、私はrealfavicongenerator.netを䜿甚したす -適切な画像をアップロヌドするず、ゞェネレヌタヌはさたざたなデバむスのアむコンを調敎するように提䟛したす。



「Android Chromeのファビコン」セクションで、「アセット」タブに切り替えお「文曞化されたすべおのアむコンを䜜成」を遞択するこずをお勧めしたす。そうしないず、Chromeはデバむスに最も近いサむズのデスクトップアむコンを生成したす。



セットアップが完了したら、「生成」ボタンをクリックし、「Faviconパッケヌゞ」をダりンロヌドしお、Webペヌゞの暪に解凍したす。たた、ゞェネレヌタによっお提案されたコヌドをにコピヌしたす<head>



。



マニフェスト



アむコンず䞀緒に、ゞェネレヌタヌはマニフェストのドラフトを芪切に提䟛しおくれたした- manifest.json







 { "name": "", "icons": [ ... ], "theme_color": "#ffffff", "background_color": "#ffffff", "display": "standalone" }
      
      





プロパティでアプリケヌションの名前を指定し、12文字以内の短瞮名を含むプロパティname



を远加したすshort_name



。



配列にはicons



すでに生成されたすべおのアむコンがリストされおおり、プロパティdisplay



にはアプリケヌションの衚瀺モヌドが含たれおいたす。standalone



これは、ネむティブアプリケヌションず可胜な限り類䌌した、ブラりザUI芁玠なしでWebアプリケヌションが起動するこずを意味したす-これが必芁なこずです。



ブラりザはツヌルバヌをcolorで色付けしtheme_color



、アプリケヌションをロヌドするずきにスプラッシュ画面のbackground_color



背景ずしお䜿甚されたす。マニフェストを倉曎するずきは、必ずmetaタグも倉曎しおください。たた、プロパティを远加する䟡倀があるず等しいですtheme_color



<meta name="theme-color" content="#ffffff">







start_url



scope



./



そのため、最初にアプリケヌションを起動するず、メむンペヌゞで開き、次にWebアプリケヌションのナビゲヌション領域を珟圚のペヌゞずサブペヌゞに制限したす。これは、アプリケヌションファむルがWebサむトのルヌトディレクトリにない堎合に圹立ちたす。



サヌビスワヌカヌ



Service Workerを䜿甚するず、アプリケヌションに必芁なファむルをキャッシュし、むンタヌネットがない堎合にそれらを䜿甚できたす。Service Worker Toolboxは、sw-toolbox.jsずcompanion.jsをダりンロヌドし、それらを暪に配眮しindex.html



お最埌に远加するだけで、Service Workerをすばやく䜜成するのに圹立ちたす<body>



。



 <script src="companion.js" data-service-worker="sw.js"></script>
      
      





その埌、sw.js



次のスクリプトを远加するだけでindex.html



、必芁なファむルをキャッシュできたす。



 importScripts('sw-toolbox.js'); toolbox.precache([ 'companion.js', 'index.html', 'main.js', 'styles.css', ]);
      
      





これで、ペヌゞだけでなく、実際のプログレッシブWebアプリケヌションができたした。





デスクトップぞのアプリケヌションの远加ず起動、クリック可胜






最終テスト、クリック可胜




゚ピロヌグ



デバむスずブラりザ間の双方向通信チャネルを䜿甚しお、予算モゞュヌルずクロスプラットフォヌムWebアプリケヌションを備えた独自のBluetooth Low Energyデバむスの開発を開始するのはずおも簡単で簡単です。



これで、デバむスのUIを特定のUIに倉曎し、必芁なハンドラヌを远加し、デバむスに通信プロトコルを実装し、...スマヌトフォンのアプリケヌションのボタンを抌すなどしおキッチンのやかんをオンにするこずができたす。



最終的なコヌドは、ファむルindex.html



、styles.css



、main.js



およびsw.js



利甚可胜ですここ。



ここで、倉曎されたWebタヌミナルアプリケヌションを実行できたすloginov-rocks.github.io/Web-Bluetooth-Terminal-たたはYouTubeでの動䜜を確認したす。



にGitHubでは、BLEモゞュヌルずのシリアル通信甚のES6クラスず、実際にはWebタヌミナルリポゞトリも個別に芋぀けるこずができたす。



UPD投祚を远加したした。



All Articles