برقراری ارتباط با دستگاه های بلوتوث از طریق جاوا اسکریپت

Web Bluetooth API به وب سایت ها اجازه می دهد تا با دستگاه های بلوتوث ارتباط برقرار کنند.

فرانسوا بوفور
François Beaufort

اگر به شما بگویم وب‌سایت‌ها می‌توانند با دستگاه‌های بلوتوث نزدیک به روشی امن و حفظ حریم خصوصی ارتباط برقرار کنند، چه؟ به این ترتیب، مانیتورهای ضربان قلب، لامپ‌های آوازخوان و حتی لاک‌پشت‌ها می‌توانند مستقیماً با یک وب‌سایت ارتباط برقرار کنند.

تاکنون، امکان تعامل با دستگاه‌های بلوتوث فقط برای اپلیکیشن‌های پلتفرم خاص امکان‌پذیر بوده است. Web Bluetooth API قصد دارد این را تغییر دهد و آن را به مرورگرهای وب نیز بیاورد.

قبل از اینکه شروع کنیم

این سند فرض می‌کند که شما اطلاعات اولیه‌ای در مورد نحوه عملکرد بلوتوث کم انرژی (BLE) و نمایه ویژگی عمومی دارید.

حتی با وجود اینکه مشخصات Web Bluetooth API هنوز نهایی نشده است، نویسندگان مشخصات فعالانه به دنبال توسعه دهندگان مشتاق هستند تا این API را امتحان کنند و در مورد مشخصات و بازخورد پیاده سازی بازخورد ارائه دهند.

زیرمجموعه ای از Web Bluetooth API در ChromeOS، Chrome برای Android 6.0، Mac (Chrome 56) و Windows 10 (Chrome 70) در دسترس است. این بدان معناست که شما باید بتوانید درخواست کنید و به دستگاه‌های کم‌انرژی بلوتوث نزدیک خود متصل شوید ، ویژگی‌های بلوتوث را بخوانید / نوشتن ، اعلان‌های GATT را دریافت کنید ، بدانید که چه زمانی یک دستگاه بلوتوث قطع می‌شود و حتی توصیفگرهای بلوتوث را بخوانید و بنویسید . برای اطلاعات بیشتر به جدول سازگاری مرورگر MDN مراجعه کنید.

برای لینوکس و نسخه‌های قبلی ویندوز، پرچم #experimental-web-platform-features را در about://flags فعال کنید.

برای آزمایشات منشا موجود است

برای دریافت بازخورد هرچه بیشتر از برنامه‌نویسانی که از Web Bluetooth API در این زمینه استفاده می‌کنند، Chrome قبلاً این ویژگی را در Chrome 53 به عنوان نسخه آزمایشی اصلی برای ChromeOS، Android و Mac اضافه کرده است.

این آزمایش در ژانویه 2017 با موفقیت به پایان رسید.

الزامات امنیتی

برای درک معاوضه‌های امنیتی، پست مدل امنیتی بلوتوث وب را از جفری یاسکین، یک مهندس نرم‌افزار در تیم Chrome که روی مشخصات Web Bluetooth API کار می‌کند، توصیه می‌کنم.

فقط HTTPS

از آنجایی که این API آزمایشی یک ویژگی جدید قدرتمند است که به وب اضافه شده است، فقط برای زمینه های امن در دسترس است. این بدان معناست که شما باید با در نظر گرفتن TLS بسازید.

اشاره کاربر مورد نیاز است

به عنوان یک ویژگی امنیتی، کشف دستگاه‌های بلوتوث با navigator.bluetooth.requestDevice باید با حرکت کاربر مانند لمس یا کلیک ماوس فعال شود. ما در مورد گوش دادن به رویدادهای pointerup ، click و touchend صحبت می کنیم.

button.addEventListener('pointerup', function(event) {
  // Call navigator.bluetooth.requestDevice
});

وارد کد شوید

Web Bluetooth API به شدت به وعده های جاوا اسکریپت متکی است. اگر با آنها آشنایی ندارید، این آموزش عالی Promises را بررسی کنید. یک چیز دیگر، () => {} توابع پیکان ECMAScript 2015 هستند.

درخواست دستگاه های بلوتوث

این نسخه از مشخصات Web Bluetooth API به وب‌سایت‌هایی که در نقش مرکزی اجرا می‌شوند اجازه می‌دهد تا از طریق اتصال BLE به سرورهای گات راه دور متصل شوند. از ارتباط بین دستگاه هایی که بلوتوث نسخه 4.0 یا بالاتر را اجرا می کنند پشتیبانی می کند.

وقتی یک وب‌سایت با استفاده از navigator.bluetooth.requestDevice درخواست دسترسی به دستگاه‌های اطراف را می‌کند، مرورگر از کاربر می‌خواهد که یک انتخابگر دستگاه را انتخاب کند که در آن می‌تواند یک دستگاه را انتخاب کند یا درخواست را لغو کند.

درخواست کاربر دستگاه بلوتوث

تابع navigator.bluetooth.requestDevice() یک شی اجباری می گیرد که فیلترها را تعریف می کند. این فیلترها فقط برای بازگرداندن دستگاه‌هایی استفاده می‌شوند که با برخی از سرویس‌های تبلیغاتی بلوتوث گات و/یا نام دستگاه مطابقت دارند.

فیلتر خدمات

برای مثال، برای درخواست دستگاه‌های بلوتوثی که سرویس باتری بلوتوث گات را تبلیغ می‌کنند:

navigator.bluetooth.requestDevice({ filters: [{ services: ['battery_service'] }] })
.then(device => { /* … */ })
.catch(error => { console.error(error); });

اگر سرویس بلوتوث گات شما در لیست خدمات استاندارد گات بلوتوث نیست، می توانید UUID کامل بلوتوث یا فرم کوتاه 16 یا 32 بیتی را ارائه دهید.

navigator.bluetooth.requestDevice({
  filters: [{
    services: [0x1234, 0x12345678, '99999999-0000-1000-8000-00805f9b34fb']
  }]
})
.then(device => { /* … */ })
.catch(error => { console.error(error); });

فیلتر نام

همچنین می‌توانید دستگاه‌های بلوتوث را بر اساس نام دستگاهی که تبلیغ می‌شود با کلید فیلترهای name یا حتی پیشوندی از این نام با کلید فیلترهای namePrefix درخواست کنید. توجه داشته باشید که در این مورد، باید کلید optionalServices را نیز تعریف کنید تا بتوانید به هر سرویسی که در فیلتر سرویس گنجانده نشده است دسترسی پیدا کنید. اگر این کار را نکنید، بعداً هنگام تلاش برای دسترسی به آنها با خطا مواجه خواهید شد.

navigator.bluetooth.requestDevice({
  filters: [{
    name: 'Francois robot'
  }],
  optionalServices: ['battery_service'] // Required to access service later.
})
.then(device => { /* … */ })
.catch(error => { console.error(error); });

فیلتر داده های سازنده

همچنین امکان درخواست دستگاه‌های بلوتوث بر اساس داده‌های خاص سازنده که با کلید فیلترهای manufacturerData داده می‌شود، وجود دارد. این کلید آرایه ای از اشیاء با یک کلید اجباری شناسه شرکت بلوتوث به نام companyIdentifier است. همچنین می توانید یک پیشوند داده ارائه دهید که داده های سازنده را از دستگاه های بلوتوثی که با آن شروع می شوند فیلتر می کند. توجه داشته باشید که برای اینکه بتوانید به هر سرویسی که در فیلتر سرویس گنجانده نشده است دسترسی پیدا کنید، باید کلید optionalServices را نیز تعریف کنید. اگر این کار را نکنید، بعداً هنگام تلاش برای دسترسی به آنها با خطا مواجه خواهید شد.

// Filter Bluetooth devices from Google company with manufacturer data bytes
// that start with [0x01, 0x02].
navigator.bluetooth.requestDevice({
  filters: [{
    manufacturerData: [{
      companyIdentifier: 0x00e0,
      dataPrefix: new Uint8Array([0x01, 0x02])
    }]
  }],
  optionalServices: ['battery_service'] // Required to access service later.
})
.then(device => { /* … */ })
.catch(error => { console.error(error); });

ماسک همچنین می تواند با پیشوند داده برای مطابقت با برخی الگوهای موجود در داده های سازنده استفاده شود. برای کسب اطلاعات بیشتر ، توضیح دهنده فیلترهای داده بلوتوث را بررسی کنید.

فیلترهای حذف

گزینه exclusionFilters در navigator.bluetooth.requestDevice() به شما امکان می دهد برخی از دستگاه ها را از انتخابگر مرورگر حذف کنید. می‌توان از آن برای حذف دستگاه‌هایی استفاده کرد که با فیلتر گسترده‌تری مطابقت دارند اما پشتیبانی نمی‌شوند.

// Request access to a bluetooth device whose name starts with "Created by".
// The device named "Created by Francois" has been reported as unsupported.
navigator.bluetooth.requestDevice({
  filters: [{
    namePrefix: "Created by"
  }],
  exclusionFilters: [{
    name: "Created by Francois"
  }],
  optionalServices: ['battery_service'] // Required to access service later.
})
.then(device => { /* … */ })
.catch(error => { console.error(error); });

بدون فیلتر

در نهایت، به جای filters ، می توانید از کلید acceptAllDevices برای نمایش تمام دستگاه های بلوتوث نزدیک استفاده کنید. همچنین باید کلید optionalServices را تعریف کنید تا بتوانید به برخی از خدمات دسترسی داشته باشید. اگر این کار را نکنید، بعداً هنگام تلاش برای دسترسی به آنها با خطا مواجه خواهید شد.

navigator.bluetooth.requestDevice({
  acceptAllDevices: true,
  optionalServices: ['battery_service'] // Required to access service later.
})
.then(device => { /* … */ })
.catch(error => { console.error(error); });

به یک دستگاه بلوتوث متصل شوید

پس حالا که یک BluetoothDevice دارید چه کار می کنید؟ بیایید به سرور گات راه دور بلوتوث متصل شویم که سرویس و تعاریف مشخصه را در خود جای داده است.

navigator.bluetooth.requestDevice({ filters: [{ services: ['battery_service'] }] })
.then(device => {
  // Human-readable name of the device.
  console.log(device.name);

  // Attempts to connect to remote GATT Server.
  return device.gatt.connect();
})
.then(server => { /* … */ })
.catch(error => { console.error(error); });

یک ویژگی بلوتوث را بخوانید

در اینجا ما به سرور GATT دستگاه بلوتوث راه دور متصل می شویم. حالا می خواهیم یک سرویس اولیه گات بگیریم و مشخصه ای را بخوانیم که متعلق به این سرویس است. برای مثال، بیایید سعی کنیم سطح شارژ فعلی باتری دستگاه را بخوانیم.

در مثال پیش رو، battery_level مشخصه استاندارد شده سطح باتری است.

navigator.bluetooth.requestDevice({ filters: [{ services: ['battery_service'] }] })
.then(device => device.gatt.connect())
.then(server => {
  // Getting Battery Service…
  return server.getPrimaryService('battery_service');
})
.then(service => {
  // Getting Battery Level Characteristic…
  return service.getCharacteristic('battery_level');
})
.then(characteristic => {
  // Reading Battery Level…
  return characteristic.readValue();
})
.then(value => {
  console.log(`Battery percentage is ${value.getUint8(0)}`);
})
.catch(error => { console.error(error); });

اگر از مشخصه GATT بلوتوث سفارشی استفاده می‌کنید، می‌توانید UUID کامل بلوتوث یا یک فرم کوتاه 16 یا 32 بیتی را به service.getCharacteristic ارائه دهید.

توجه داشته باشید که شما همچنین می توانید یک شنونده رویداد characteristicvaluechanged روی یک مشخصه اضافه کنید تا بتواند مقدار آن را بخواند. برای مشاهده نحوه مدیریت اختیاری اعلان‌های GATT نیز ، نمونه Read Characteristic Value Changed Sample را بررسی کنید.

…
.then(characteristic => {
  // Set up event listener for when characteristic value changes.
  characteristic.addEventListener('characteristicvaluechanged',
                                  handleBatteryLevelChanged);
  // Reading Battery Level…
  return characteristic.readValue();
})
.catch(error => { console.error(error); });

function handleBatteryLevelChanged(event) {
  const batteryLevel = event.target.value.getUint8(0);
  console.log('Battery percentage is ' + batteryLevel);
}

برای یک مشخصه بلوتوث بنویسید

نوشتن در یک ویژگی بلوتوث گات به آسانی خواندن آن است. این بار، بیایید از نقطه کنترل ضربان قلب برای بازنشانی مقدار فیلد انرژی مصرف شده در دستگاه سنجش ضربان قلب به 0 استفاده کنیم.

قول می دهم اینجا جادویی وجود ندارد. همه اینها در صفحه ویژگی نقطه کنترل ضربان قلب توضیح داده شده است.

navigator.bluetooth.requestDevice({ filters: [{ services: ['heart_rate'] }] })
.then(device => device.gatt.connect())
.then(server => server.getPrimaryService('heart_rate'))
.then(service => service.getCharacteristic('heart_rate_control_point'))
.then(characteristic => {
  // Writing 1 is the signal to reset energy expended.
  const resetEnergyExpended = Uint8Array.of(1);
  return characteristic.writeValue(resetEnergyExpended);
})
.then(_ => {
  console.log('Energy expended has been reset.');
})
.catch(error => { console.error(error); });

اعلان‌های گات را دریافت کنید

حال، بیایید ببینیم که چگونه وقتی مشخصه اندازه‌گیری ضربان قلب در دستگاه تغییر می‌کند، مطلع شویم:

navigator.bluetooth.requestDevice({ filters: [{ services: ['heart_rate'] }] })
.then(device => device.gatt.connect())
.then(server => server.getPrimaryService('heart_rate'))
.then(service => service.getCharacteristic('heart_rate_measurement'))
.then(characteristic => characteristic.startNotifications())
.then(characteristic => {
  characteristic.addEventListener('characteristicvaluechanged',
                                  handleCharacteristicValueChanged);
  console.log('Notifications have been started.');
})
.catch(error => { console.error(error); });

function handleCharacteristicValueChanged(event) {
  const value = event.target.value;
  console.log('Received ' + value);
  // TODO: Parse Heart Rate Measurement value.
  // See https://github.com/WebBluetoothCG/demos/blob/gh-pages/heart-rate-sensor/heartRateSensor.js
}

Notifications Sample به شما نشان می دهد که چگونه اعلان ها را با stopNotifications() متوقف کنید و شنونده رویداد اضافه شده characteristicvaluechanged را به درستی حذف کنید.

اتصال را از یک دستگاه بلوتوث قطع کنید

برای ارائه یک تجربه کاربری بهتر، ممکن است بخواهید به رویدادهای قطع ارتباط گوش دهید و کاربر را به اتصال مجدد دعوت کنید:

navigator.bluetooth.requestDevice({ filters: [{ name: 'Francois robot' }] })
.then(device => {
  // Set up event listener for when device gets disconnected.
  device.addEventListener('gattserverdisconnected', onDisconnected);

  // Attempts to connect to remote GATT Server.
  return device.gatt.connect();
})
.then(server => { /* … */ })
.catch(error => { console.error(error); });

function onDisconnected(event) {
  const device = event.target;
  console.log(`Device ${device.name} is disconnected.`);
}

همچنین می توانید برای قطع ارتباط برنامه وب خود از دستگاه بلوتوث، device.gatt.disconnect() را فراخوانی کنید. این کار شنوندگان رویداد gattserverdisconnected موجود را فعال می کند. توجه داشته باشید که اگر برنامه دیگری از قبل با دستگاه بلوتوث ارتباط برقرار کند، ارتباط دستگاه بلوتوث را متوقف نخواهد کرد. نمونه قطع اتصال دستگاه و نمونه اتصال مجدد خودکار را بررسی کنید تا عمیق تر شوید.

خواندن و نوشتن برای توصیفگرهای بلوتوث

توصیفگرهای بلوتوث گات ویژگی هایی هستند که یک مقدار مشخصه را توصیف می کنند. شما می توانید آنها را به روشی مشابه ویژگی های بلوتوث گات بخوانید و بنویسید.

بیایید به عنوان مثال نحوه خواندن توضیحات کاربر در مورد فاصله اندازه گیری دماسنج سلامت دستگاه را ببینیم.

در مثال زیر، health_thermometer سرویس Health Thermometer ، measurement_interval مشخصه Measurement Interval ، و gatt.characteristic_user_description توصیفگر مشخصات کاربر است .

navigator.bluetooth.requestDevice({ filters: [{ services: ['health_thermometer'] }] })
.then(device => device.gatt.connect())
.then(server => server.getPrimaryService('health_thermometer'))
.then(service => service.getCharacteristic('measurement_interval'))
.then(characteristic => characteristic.getDescriptor('gatt.characteristic_user_description'))
.then(descriptor => descriptor.readValue())
.then(value => {
  const decoder = new TextDecoder('utf-8');
  console.log(`User Description: ${decoder.decode(value)}`);
})
.catch(error => { console.error(error); });

اکنون که توضیحات کاربر در مورد فاصله اندازه گیری دماسنج سلامت دستگاه را خواندیم، بیایید ببینیم چگونه آن را به روز کنیم و یک مقدار سفارشی بنویسیم.

navigator.bluetooth.requestDevice({ filters: [{ services: ['health_thermometer'] }] })
.then(device => device.gatt.connect())
.then(server => server.getPrimaryService('health_thermometer'))
.then(service => service.getCharacteristic('measurement_interval'))
.then(characteristic => characteristic.getDescriptor('gatt.characteristic_user_description'))
.then(descriptor => {
  const encoder = new TextEncoder('utf-8');
  const userDescription = encoder.encode('Defines the time between measurements.');
  return descriptor.writeValue(userDescription);
})
.catch(error => { console.error(error); });

نمونه ها، دموها و کدها

تمام نمونه های بلوتوث وب زیر با موفقیت آزمایش شده اند. برای لذت بردن از این نمونه‌ها، توصیه می‌کنم [BLE Peripheral Simulator Android App] را نصب کنید که یک دستگاه جانبی BLE را با یک سرویس باتری، یک سرویس ضربان قلب، یا یک سرویس دماسنج سلامت شبیه‌سازی می‌کند.

مبتدی

  • اطلاعات دستگاه - اطلاعات اولیه دستگاه را از یک دستگاه BLE بازیابی کنید.
  • سطح باتری - اطلاعات باتری را از یک دستگاه BLE که اطلاعات مربوط به باتری را تبلیغ می کند، بازیابی کنید.
  • بازنشانی انرژی - بازنشانی انرژی مصرف شده از یک دستگاه BLE برای تبلیغات ضربان قلب.
  • ویژگی های مشخصه - نمایش تمام ویژگی های یک مشخصه خاص از یک دستگاه BLE.
  • اعلان ها - شروع و توقف اعلان های مشخصه از یک دستگاه BLE.
  • Device Disconnect - پس از اتصال به دستگاه BLE، اتصال را قطع کنید و از قطع شدن آن مطلع شوید.
  • دریافت مشخصات - تمام ویژگی های یک سرویس تبلیغ شده را از یک دستگاه BLE دریافت کنید.
  • توصیفگرها را دریافت کنید - تمام توصیفگرهای ویژگی های یک سرویس تبلیغ شده را از یک دستگاه BLE دریافت کنید.
  • فیلتر داده‌های سازنده - اطلاعات اولیه دستگاه را از یک دستگاه BLE که با داده‌های سازنده مطابقت دارد، بازیابی کنید.
  • فیلترهای حذف - اطلاعات اولیه دستگاه را از یک دستگاه BLE با فیلترهای حذف اولیه بازیابی کنید.

ترکیب چندین عملیات

همچنین نسخه‌های نمایشی بلوتوث وب و نرم‌افزارهای رسمی Web Bluetooth Codelabs را بررسی کنید.

کتابخانه ها

  • web-bluetooth-utils یک ماژول npm است که برخی از توابع راحتی را به API اضافه می کند.
  • شیم Web Bluetooth API در noble ، محبوب‌ترین ماژول مرکزی Node.js BLE موجود است. این به شما امکان می دهد بدون نیاز به سرور WebSocket یا سایر پلاگین ها، نوبل را بسته بندی یا مرورگر وب کنید.
  • angular-web-bluetooth یک ماژول برای Angular است که تمام دیگ بخار مورد نیاز برای پیکربندی Web Bluetooth API را انتزاعی می کند.

ابزار

  • با وب بلوتوث شروع کنید یک برنامه وب ساده است که تمام کدهای جاوا اسکریپت را برای شروع تعامل با یک دستگاه بلوتوث تولید می کند. یک نام دستگاه، یک سرویس، یک مشخصه را وارد کنید، ویژگی های آن را تعریف کنید و شما آماده هستید.
  • اگر قبلاً یک توسعه دهنده بلوتوث هستید، افزونه Web Bluetooth Developer Studio کد وب بلوتوث جاوا اسکریپت را نیز برای دستگاه بلوتوث شما ایجاد می کند.

نکات

یک صفحه بلوتوث داخلی در Chrome به about://bluetooth-internals موجود است تا بتوانید همه چیز را در مورد دستگاه‌های بلوتوث نزدیک بررسی کنید: وضعیت، خدمات، ویژگی‌ها و توصیف‌گرها.

اسکرین شات از صفحه داخلی برای رفع اشکال بلوتوث در کروم
صفحه داخلی در کروم برای اشکال زدایی دستگاه های بلوتوث.

من همچنین توصیه می‌کنم صفحه رسمی نحوه فایل‌بندی اشکالات بلوتوث وب را بررسی کنید، زیرا اشکال‌زدایی بلوتوث گاهی اوقات دشوار است.

بعدش چی

ابتدا وضعیت اجرای مرورگر و پلت فرم را بررسی کنید تا بدانید کدام بخش از Web Bluetooth API در حال حاضر در حال پیاده سازی است.

اگرچه هنوز ناقص است، در اینجا نگاهی اجمالی از آنچه در آینده نزدیک باید انتظار داشته باشیم آورده شده است:

  • اسکن برای تبلیغات BLE نزدیک با navigator.bluetooth.requestLEScan() انجام می شود.
  • یک رویداد serviceadded جدید خدمات تازه کشف شده بلوتوث گات را ردیابی می کند در حالی که رویداد serviceremoved خدمات حذف شده را ردیابی می کند. هنگامی که هر مشخصه و/یا توصیفگر از سرویس بلوتوث گات اضافه یا حذف شود، یک رویداد servicechanged سرویس فعال می‌شود.

پشتیبانی از API را نشان دهید

آیا قصد دارید از Web Bluetooth API استفاده کنید؟ پشتیبانی عمومی شما به تیم Chrome کمک می‌کند ویژگی‌ها را اولویت‌بندی کند و به سایر فروشندگان مرورگر نشان می‌دهد که چقدر حمایت از آنها ضروری است.

با استفاده از هشتگ #WebBluetooth یک توییت به ChromiumDev@ ارسال کنید و به ما اطلاع دهید که کجا و چگونه از آن استفاده می کنید.

منابع

سپاسگزاریها

با تشکر از Kayce Basques برای بررسی این مقاله. تصویر قهرمان توسط SparkFun Electronics از بولدر، ایالات متحده .