[{"data":1,"prerenderedAt":313},["ShallowReactive",2],{"DefaultLayouten":3,"language-blog-slug-bluetooth-anywhere-i18n-slugs":134,"language-blog-slug-en-bluetooth-anywhere":138},{"app":4,"menu":31,"footer":66},{"githubUrl":5,"youtubeUrl":6,"linkedinUrl":7,"phoneNumber":8,"emailAddress":9,"legal":10,"addresses":20},"https:\u002F\u002Fgithub.com\u002Fvoorhoede\u002F","https:\u002F\u002Fwww.youtube.com\u002Fchannel\u002FUCzHuhQVYFRixtQN2-swcuGg","https:\u002F\u002Fwww.linkedin.com\u002Fcompany\u002Fde-voorhoede","+31 20 2610 954","post@voorhoede.nl",[11,14,17],{"title":12,"value":13},"KvK","56017235",{"title":15,"value":16},"BTW","NL851944620B01",{"title":18,"value":19},"IBAN","NL14TRIO0320142078",[21,26],{"address":22,"city":23,"googleMapsLink":24,"postalCode":25},"Koivistokade 70","Amsterdam","https:\u002F\u002Fwww.google.com\u002Fmaps\u002Fplace\u002FDe+Voorhoede+%7C+Front-end+Development\u002F@52.396847,4.8700823,17z\u002Fdata=!3m1!4b1!4m5!3m4!1s0x47c5e21d502d2d59:0xbf570944a96ebf45!8m2!3d52.347647!4d4.8502154","1013 BB",{"address":27,"city":28,"googleMapsLink":29,"postalCode":30},"Koornmarkt 22","Delft","https:\u002F\u002Fwww.google.nl\u002Fmaps\u002Fplace\u002FKoornmarkt+22,+2611+EG+Delft\u002F@52.0093477,4.3573054,17z\u002F","2611 EG",{"title":32,"callToActions":33,"links":39},"Site Menu",[34],{"id":35,"title":36,"link":37},"163140902","Contact",{"__typename":38},"ContactRecord",[40,46,51,56,61],{"id":41,"title":42,"link":43},"163140904","Impact",{"__typename":44,"slug":45},"PageRecord","impact",{"id":47,"title":48,"link":49},"163140905","Services",{"__typename":50},"ServiceOverviewRecord",{"id":52,"title":53,"link":54},"163140906","Cases",{"__typename":55},"CaseOverviewRecord",{"id":57,"title":58,"link":59},"163140908","About us",{"__typename":44,"slug":60},"about-us",{"id":62,"title":63,"link":64},"d6WdFJq2SOuc3dWtpibbXQ","Work at",{"__typename":44,"slug":65},"work-at",{"links":67,"copyrightTitle":93,"copyrightLabel":94,"copyrightLink":95,"privacyTitle":96,"privacyLabel":97,"privacyLink":98,"certificatesGrid":99},[68,71,74,77,82,85,88],{"id":69,"title":42,"link":70},"144185264",{"__typename":44,"slug":45},{"id":72,"title":48,"link":73},"144185265",{"__typename":50},{"id":75,"title":53,"link":76},"144185266",{"__typename":55},{"id":78,"title":79,"link":80},"144185267","Blog",{"__typename":81},"BlogPostOverviewRecord",{"id":83,"title":58,"link":84},"144185268",{"__typename":44,"slug":60},{"id":86,"title":36,"link":87},"144185269",{"__typename":38},{"id":89,"title":90,"link":91},"144185270","FAQ",{"__typename":44,"slug":92},"faq","Creative Commons licence and disclaimer","CC BY 4.0","https:\u002F\u002Fcreativecommons.org\u002Flicenses\u002Fby\u002F4.0\u002F","De Voorhoede privacy statement (pdf)","Privacy statement","https:\u002F\u002Fwww.datocms-assets.com\u002F6524\u002F1763455455-vh-isms-006-privacy-statement-de-voorhoede-en.pdf",[100,112,123],{"id":101,"image":102,"link":107},"Xq4bBfg_TZ6Fkjax9mkbLQ",{"url":103,"alt":104,"width":105,"height":106},"https:\u002F\u002Fwww.datocms-assets.com\u002F6524\u002F1687353463-b-corp-logo-black-rgb.png","B Corp logo",404,680,{"__typename":108,"id":109,"title":110,"url":111},"ExternalLinkRecord","fGW1ak8XQYaYDLkBSyncog","B Corp","https:\u002F\u002Fwww.bcorporation.net\u002Fen-us\u002Ffind-a-b-corp\u002Fcompany\u002Fde-voorhoede\u002F",{"id":113,"image":114,"link":119},"c5mCXRTiSraRIB25fw1p7Q",{"url":115,"alt":116,"width":117,"height":118},"https:\u002F\u002Fwww.datocms-assets.com\u002F6524\u002F1687353461-dda-boxlogo-black.png","Dutch Digital Agencies logo",627,480,{"__typename":108,"id":120,"title":121,"url":122},"P6Jh7B0cTv2cKyNEeKVWVQ","Dutch Digital Agencies","https:\u002F\u002Fdutchdigitalagencies.com\u002Fleden\u002Fde-voorhoede\u002F",{"id":124,"image":125,"link":129},"MT5SCyNxSTSr_v5eeATMZw",{"url":126,"alt":127,"width":128,"height":128},"https:\u002F\u002Fwww.datocms-assets.com\u002F6524\u002F1775730283-dnv.png","DNV logo",518,{"id":130,"title":131,"link":132},"BRtNB5HnT5i-7HkA8IYzBw","DIV",{"__typename":44,"slug":133},"impact\u002Fdigitale-producten-privacy-by-design",[135],{"locale":136,"value":137},"en","bluetooth-anywhere",{"page":139},{"slug":137,"i18nSlugs":140,"social":142,"title":143,"subtitle":79,"isArchived":146,"headerIllustration":145,"date":147,"authors":148,"introTitle":144,"items":165,"pivots":300,"relatedBlogPosts":311,"tags":312,"onMountedScript":159,"onUnmountedScript":159},[141],{"locale":136,"value":137},{"title":143,"description":144,"image":145},"Bluetooth Anywhere","Experimenting with Bluetooth in JavaScript apps on the web, in hybrid apps and React Native",null,false,"2019-02-08T01:00:00.000+01:00",[149,157],{"name":150,"lastName":151,"slug":152,"image":153},"Peter","Goes","peter",{"url":154,"alt":145,"width":155,"height":156},"https:\u002F\u002Fwww.datocms-assets.com\u002F6524\u002F1744374527-peter-edit.jpg",1381,1839,{"name":158,"lastName":159,"slug":160,"image":161},"Anand","","anand",{"url":162,"alt":145,"width":163,"height":164},"https:\u002F\u002Fwww.datocms-assets.com\u002F6524\u002F1683534636-placeholder.jpg",1235,1646,[166,170,173,184,188,198,201,205,213,216,220,225,228,235,238,241,244,247,250,253,256,260,263,266,269,272,279,283,286,289,292,296],{"__typename":167,"id":168,"title":159,"body":169},"TextSectionRecord","672689","\u003Cp>With the popularity of IoT and Bluetooth support in browsers increasing, we asked ourselves: Can we control a device over Bluetooth, from a JavaScript app, on iOS and Android? Time for an experiment.\u003C\u002Fp>",{"__typename":167,"id":171,"title":159,"body":172},"672690","\u003Cp>We will use 3 different environments: a web page in a desktop browser, a hybrid Cordova app and a React Native app. From each of these environments a connection is made to a Bluetooth device. Information is read from the Bluetooth device and displayed realtime on screen:\u003C\u002Fp>",{"__typename":174,"id":175,"mute":146,"loop":146,"autoplay":146,"caption":159,"video":176,"gif":145},"ResponsiveVideoRecord","672691",{"url":177,"title":178,"height":179,"width":180,"provider":181,"providerUid":182,"thumbnailUrl":183},"https:\u002F\u002Fvimeo.com\u002F316750547","Control a device over Bluetooth via React Native",360,640,"vimeo","316750547","https:\u002F\u002Fi.vimeocdn.com\u002Fvideo\u002F758826579-c0f5d0ec91574b13d2ccaed8f3aaf825b58a261a3182b54f5741fb8bfe3f87b6-d_640",{"__typename":167,"id":185,"title":186,"body":187},"672692","Our testing device: a Nordic Thingy:52","\u003Cp>We found the perfect device; the \u003Ca href=\"https:\u002F\u002Fwww.nordicsemi.com\u002FSoftware-and-Tools\u002FDevelopment-Kits\u002FNordic-Thingy-52\">Nordic Thingy:52\u003C\u002Fa>. The Bluetooth Low Energy (BLE) device opens up a lot of possibilities and challenges for developers to do some fun stuff with.\u003C\u002Fp>",{"__typename":189,"id":190,"image":191,"caption":193,"fullWidth":196,"captionPosition":197},"ImageRecord","176467365",{"url":192,"alt":193,"width":194,"height":195},"https:\u002F\u002Fwww.datocms-assets.com\u002F6524\u002F1549638572-nordicthingy52.jpg","Image of Nordic Semiconductor Thingy:52 IoT Sensor Kit",1600,1200,true,"left",{"__typename":167,"id":199,"title":159,"body":200},"672702","\u003Cp>The Nordic Thingy:52 is a small multi-sensor semiconductor which has environment (temperature, humidity, pressure, air quality) and motion (accelerometer, gyroscope, compass) sensors. Thingy connects to Bluetooth-enabled devices and sends data to\u002Ffrom its sensors and actuators to an app and to the cloud. For Android\u002FiOS there is a Nordic Thing:52 app and a \u003Ca href=\"https:\u002F\u002Fdeveloper.nordicsemi.com\u002Fthingy\u002F52\u002F\">web app\u003C\u002Fa>.\u003C\u002Fp>",{"__typename":167,"id":202,"title":203,"body":204},"672713","How to read values of a Bluetooth Low Energy device?","\u003Cp>When you&rsquo;re connected to the Thingy, you can check which Generic Attributes (\u003Ca href=\"https:\u002F\u002Flearn.adafruit.com\u002Fintroduction-to-bluetooth-low-energy\u002Fgatt\">GATT\u003C\u002Fa>) services are available. There are a \u003Ca href=\"https:\u002F\u002Fwww.bluetooth.com\u002Fspecifications\u002Fgatt\u002Fservices\">list of standardised services\u003C\u002Fa> available in the Bluetooth spec, such as Battery Level, Heart Rate, Location and Navigation.\u003C\u002Fp>\n\u003Cp>Besides the standardised services from the spec, BLE devices can feature their own services and characteristic definitions. The Nordic Thingy:52, for example, has the \u003Ca href=\"https:\u002F\u002Fnordicsemiconductor.github.io\u002FNordic-Thingy52-FW\u002Fdocumentation\u002Ffirmware_architecture.html#arch_env\">Weather station service\u003C\u002Fa>. This service has several characteristics such as temperature, pressure and humidity.\u003C\u002Fp>\n\u003Cp>A mobile phone\u002Ftablet\u002Fbrowser is a GATT Client and the BLE device (Thingy) is the GATT Server. The Client will always initiate the request to a GATT Server. GATT (Generic Attributes) defines a hierarchical data structure that is exposed to connected Bluetooth Low Energy (LE) devices.\u003C\u002Fp>",{"__typename":189,"id":206,"image":207,"caption":209,"fullWidth":146,"captionPosition":212},"672714",{"url":208,"alt":209,"width":210,"height":211},"https:\u002F\u002Fwww.datocms-assets.com\u002F6524\u002F1550062718-bluetooth-gatt-profile.jpg","Bluetooth GATT structure",1480,670,"bottom",{"__typename":167,"id":214,"title":159,"body":215},"672715","\u003Cp>Each service and characteristic has a universally unique identifier (UUID). The UUIDs are necessary to connect and read data from a BLE device. Services from the spec have their UUIDs also standardised. The UUID of the \u003Ca href=\"https:\u002F\u002Fwww.bluetooth.com\u002Fspecifications\u002Fgatt\u002Fviewer?attributeXmlFile=org.bluetooth.service.battery_service.xml\">Battery Service\u003C\u002Fa> for, example, is \u003Cem>0x180F\u003C\u002Fem>.\u003C\u002Fp>\n\u003Cp>If a device has its own services (like the Weather station example in the Thingy:52), it needs to provide UUIDs for those services and characteristics. These UUIDs can usually be found in the \u003Ca href=\"https:\u002F\u002Fnordicsemiconductor.github.io\u002FNordic-Thingy52-FW\u002Fdocumentation\u002Ffirmware_architecture.html#arch_env\">documentation\u003C\u002Fa> of the device.\u003C\u002Fp>\n\u003Cp>Now we have everything we need to start experimenting.\u003C\u002Fp>",{"__typename":167,"id":217,"title":218,"body":219},"672718","Can we control a device over Bluetooth via Web Bluetooth?","\u003Cp>We're going to read the battery level of the Thingy with the Web Bluetooth API from Chrome on desktop. Let's get into the code.\u003C\u002Fp>\n\u003Cp>Requesting Bluetooth devices advertising the Bluetooth GATT Thingy Configuration Service:\u003C\u002Fp>",{"__typename":221,"id":222,"language":223,"body":224},"CodeBlockRecord","672719","javascript","const TCS_UUID = \"ef680100-9b35-4933-9b10-52ffa9740042\" \u002F\u002FThingy Configuration Service\nnavigator.bluetooth.requestDevice({\n  filters: [{ services: [TCS_UUID] }], \n  optionalServices: [\"battery_service\"]\n})",{"__typename":167,"id":226,"title":159,"body":227},"672720","\u003Cp>Using filters only shows the relevant device and saves energy. Executing \u003Ccode>requestDevice()\u003C\u002Fcode> opens a browser dialog window that only shows the device you need.\u003C\u002Fp>",{"__typename":174,"id":229,"mute":146,"loop":146,"autoplay":146,"caption":159,"video":230,"gif":145},"672721",{"url":231,"title":232,"height":179,"width":180,"provider":181,"providerUid":233,"thumbnailUrl":234},"https:\u002F\u002Fvimeo.com\u002F316751231","Control a device over Bluetooth via Web Bluetooth","316751231","https:\u002F\u002Fi.vimeocdn.com\u002Fvideo\u002F758827434-fa8e66f1bcd81471c517ffd3e5e55b0ffce228ac39fecea01d221af7c4cb0c07-d_640",{"__typename":167,"id":236,"title":159,"body":237},"672732","\u003Cp>\u003Ccode>requestDevice()\u003C\u002Fcode> returns a promise. Connect to the GATT server, get the Battery Service and the Battery Level:\u003C\u002Fp>",{"__typename":221,"id":239,"language":223,"body":240},"672733","navigator.bluetooth.requestDevice({...})\n  .then(device => device.gatt.connect())\n  .then(server => server.getPrimaryService('battery_service'))\n  .then(service => service.getCharacteristic('battery_level'))",{"__typename":167,"id":242,"title":159,"body":243},"672734","\u003Cp>To enable notifications when a characteristic changes we use \u003Ccode>startNotifications()\u003C\u002Fcode>. After enabling this we add the event listener \u003Ccode>characteristicvaluechanged\u003C\u002Fcode> to get notified of changes in the characteristic.\u003C\u002Fp>",{"__typename":221,"id":245,"language":223,"body":246},"672735",".then(characteristic => {\n  characteristic.startNotifications()\n    .then(() => {\n      characteristic.addEventListener(\n        'characteristicvaluechanged', \n        onCharacteristicChanged\n      )\n    })\n    .catch(error => console.error(error.code, error.name, error.message))\n})",{"__typename":167,"id":248,"title":159,"body":249},"672737","\u003Cp>Each characteristic requires the data to be read in a specific kind of format. The Thingy docs for battery service mentions to use an unsigned 8-bit integer \u003Ca href=\"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FJavaScript\u002FReference\u002FGlobal_Objects\u002FDataView\u002FgetUint8\">\u003Ccode>getUint8()\u003C\u002Fcode>\u003C\u002Fa>:\u003C\u002Fp>",{"__typename":221,"id":251,"language":223,"body":252},"672738","onCharacteristicChanged (event) {\n  const batteryLevel = event.target.value.getUint8(0)\n  console.log('battery level', batteryLevel)\n}",{"__typename":167,"id":254,"title":159,"body":255},"672739","\u003Cp>\u003Ca href=\"https:\u002F\u002Fcaniuse.com\u002F#search=bluetooth\">Support\u003C\u002Fa> for the Web Bluetooth API is currently very limited. WebKit (iOS\u002FSafari) is unfortunately not considering this API. That means the availability of this API is not to be expected any time soon or in the immediate future for WebKit.\u003C\u002Fp>\n\u003Cp>If you need more support, then developing a hybrid or native mobile app is recommended.\u003C\u002Fp>",{"__typename":167,"id":257,"title":258,"body":259},"685811","Can we control a device over Bluetooth with a hybrid app?","\u003Cp>Since bluetooth on mobile devices is almost always available, we want to find out if we can reach more users with a hybrid app. So, we&rsquo;re going to create a hybrid mobile app with Cordova. Cordova wraps your web app into a native container which can access the device functions of several platforms. These functions are exposed via a unified JavaScript API. With the Cordova hybrid app that is natively installed on a mobile device, we can load a web app for the user interface.\u003C\u002Fp>\n\u003Cp>The Cordova plugin to work with BLE devices is called \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fdon\u002Fcordova-plugin-ble-central\">cordova-plugin-ble-central\u003C\u002Fa>. First we \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fdon\u002Fcordova-plugin-ble-central#scan\">scan\u003C\u002Fa> for Bluetooth devices:\u003C\u002Fp>",{"__typename":221,"id":261,"language":223,"body":262},"685812","ble.scan([], 5, onDiscoverDevice, onError)",{"__typename":167,"id":264,"title":159,"body":265},"685813","\u003Cp>\u003Ccode>ble\u003C\u002Fcode> is globally available via the JavaScript API and we can use it to call methods of the Cordova plugin. In the \u003Ccode>onDiscoverDevice\u003C\u002Fcode> callback, we get an object with information about a device, such as MAC address and name (e.g. &lsquo;Thingy&rsquo;).\u003C\u002Fp>\n\u003Cp>From here on out, the process is roughly the same as the Web Bluetooth version. First we connect to the device; then we listen to notifications on the specified service and characteristic.\u003C\u002Fp>",{"__typename":221,"id":267,"language":223,"body":268},"685823","    function onDiscoverDevice(device) {\n      if (device.name === 'Thingy') {\n        connect(device)\n      }\n    }\n    \n    function connect(device) {\n      var deviceId = device.deviceId\n      var onConnect = function () {\n        ble.startNotification(\n          deviceId, \n          batteryService, \n          batteryLevel, \n          onBatteryLevelChange, \n          onError)\n      }\n      ble.connect(deviceId, onConnect, onError)\n    }",{"__typename":167,"id":270,"title":159,"body":271},"685824","\u003Cp>After a connection has been made with Thingy, we enable to receive \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fdon\u002Fcordova-plugin-ble-central#startnotification\">notifications\u003C\u002Fa> for the battery level. Next we display the value (once again in Uint8Array format) to screen:\u003C\u002Fp>",{"__typename":174,"id":273,"mute":146,"loop":146,"autoplay":146,"caption":159,"video":274,"gif":145},"685828",{"url":275,"title":276,"height":179,"width":180,"provider":181,"providerUid":277,"thumbnailUrl":278},"https:\u002F\u002Fvimeo.com\u002F316751612","Control a device over Bluetooth with a hybrid app","316751612","https:\u002F\u002Fi.vimeocdn.com\u002Fvideo\u002F758827874-012d11b927f56d073279bd2022feda75c472e5a2d05b1f6c97e7ce4365548234-d_640",{"__typename":167,"id":280,"title":281,"body":282},"685829","Can we control a device over Bluetooth via React Native?","\u003Cp>To see if we can get it to work in a React Native environment, we tried to access the temperature value of the Thingy:52.\u003C\u002Fp>\n\u003Cp>Like Cordova, React Native does not support Bluetooth support out of the box. We used the plugin \u003Ca href=\"https:\u002F\u002Fgithub.com\u002FPolidea\u002Freact-native-ble-plx\">react-native-ble-plx\u003C\u002Fa>.\u003C\u002Fp>\n\u003Cp>The process is again fairly similar; Listen for devices, connect to a device, provide service and characteristic and subscribe to notifications:\u003C\u002Fp>",{"__typename":221,"id":284,"language":223,"body":285},"685830","const bleManager = new BleManager()\nbleManager.startDeviceScan(null, null, (err, device) => {\n  if (device.name === 'Thingy') {\n    device.connect()\n      .then(getAllServicesAndCharacteristics)\n      .then(subscribeToTemperatureCharaceristic)\n  }\n})\n\nfunction getAllServicesAndCharacteristics() {\n  \u002F* get all services and characteristics *\u002F\n}\n\nfunction subscribeToTemperatureCharaceristic() {\n  \u002F* Subscribe to temperature characteristic and display to screen *\u002F\n}",{"__typename":167,"id":287,"title":159,"body":288},"685831","\u003Cp>Which results in the following:\u003C\u002Fp>",{"__typename":174,"id":290,"mute":146,"loop":146,"autoplay":146,"caption":159,"video":291,"gif":145},"685832",{"url":177,"title":178,"height":179,"width":180,"provider":181,"providerUid":182,"thumbnailUrl":183},{"__typename":167,"id":293,"title":294,"body":295},"685833","Conclusion","\u003Cp>We can control and read from a device over Bluetooth with JavaScript in a hybrid (Cordova) and native (React Native) app. Since both of those do not support bluetooth out of the box, we are dependent on plugins to make them work. These plugins provide their own APIs. That is something to take into consideration when building an app for production.\u003C\u002Fp>\n\u003Cp>We can also control a device over Web Bluetooth, from a JavaScript app. The support is currently limited to Chrome only. In its defense, the Web Bluetooth API is very stable and more reliable than Cordova\u002FReact Native plugins.\u003C\u002Fp>\n\u003Cp>Want to take it for a spin? \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fvoorhoede\u002Fbluetooth-poc\">Here&rsquo;s our repo\u003C\u002Fa>!\u003C\u002Fp>",{"__typename":167,"id":297,"title":298,"body":299},"685834","Useful links","\u003Cul>\n\u003Cli>\u003Ca href=\"https:\u002F\u002Fgithub.com\u002FNordicPlayground\u002FNordic-Thingy52-Thingyjs\">ThingyJS\u003C\u002Fa>\u003C\u002Fli>\n\u003Cli>\u003Ca href=\"https:\u002F\u002Fmedium.com\u002Fsamsung-internet-dev\u002Fcreating-a-physical-and-immersive-web-mashup-b5418f14b982\">Creating a physical and immersive web mashup\u003C\u002Fa>\u003C\u002Fli>\n\u003Cli>\u003Ca href=\"https:\u002F\u002Fdevelopers.google.com\u002Fweb\u002Fupdates\u002F2015\u002F07\u002Finteract-with-ble-devices-on-the-web\">Interact with BLE devices on the Web (Google)\u003C\u002Fa>\u003C\u002Fli>\n\u003C\u002Ful>",[301],{"title":302,"body":303,"links":304,"mailchimpValue":159,"mailchimpName":159,"mailchimpId":159,"formType":159,"contactPerson":145},"Also in love with the web?","\u003Cp>For us, that’s about technology and user experience. Fast, available for all, enjoyable to use. And fun to build. This is how our team bands together, adhering to the same values, to make sure we achieve a solid result for clients both large and small. Does that fit you?\u003C\u002Fp>\n",[305],{"__typename":306,"id":307,"title":308,"link":309},"InternalLinkRecord","163140992","Join our team",{"__typename":44,"slug":310},"jobs",[],[],1776256145469]