{"id":135306,"date":"2023-11-16T15:58:16","date_gmt":"2023-11-16T15:58:16","guid":{"rendered":"https:\/\/randomnerdtutorials.com\/?p=135306"},"modified":"2024-06-11T14:09:37","modified_gmt":"2024-06-11T14:09:37","slug":"esp32-web-bluetooth","status":"publish","type":"post","link":"https:\/\/randomnerdtutorials.com\/esp32-web-bluetooth\/","title":{"rendered":"ESP32 Web Bluetooth (BLE): Getting Started Guide"},"content":{"rendered":"\n<p>This guide provides a beginner-friendly introduction to using Web Bluetooth with the ESP32. We&#8217;ll explain what Web Bluetooth is and walk you through creating a web application for interacting with an ESP32 Bluetooth Low Energy (BLE) device. Within the web app, you&#8217;ll be able to control the ESP32 GPIOs and retrieve values sent by the ESP32 through writing to and reading from its BLE characteristics.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" fetchpriority=\"high\" decoding=\"async\" width=\"1200\" height=\"675\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/ESP32-Web-BLE-Getting-Started.jpg?resize=1200%2C675&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Web Bluetooth with the ESP32 Getting Started Guide\" class=\"wp-image-136346\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/ESP32-Web-BLE-Getting-Started.jpg?w=1280&amp;quality=100&amp;strip=all&amp;ssl=1 1280w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/ESP32-Web-BLE-Getting-Started.jpg?resize=300%2C169&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/ESP32-Web-BLE-Getting-Started.jpg?resize=1024%2C576&amp;quality=100&amp;strip=all&amp;ssl=1 1024w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/ESP32-Web-BLE-Getting-Started.jpg?resize=768%2C432&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 1200px) 100vw, 1200px\" \/><\/figure><\/div>\n\n\n<h2 class=\"wp-block-heading\">Table of Contents<\/h2>\n\n\n\n<p>Throughout this tutorial, we&#8217;ll cover the following:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"#web-bluetooth-intro\" title=\"\">Web Bluetooth Introduction<\/a><\/li>\n\n\n\n<li><a href=\"#ble-basic-concepts\" title=\"\">Bluetooth Low Energy Introduction &#8211; Basic Concepts<\/a>\n<ul class=\"wp-block-list\">\n<li><a href=\"#ble-peripheral-controller\" title=\"\">BLE Peripheral and Controller (Central Device)<\/a><\/li>\n\n\n\n<li><a href=\"#ble-server-client\" title=\"\">BLE Server and Client<\/a><\/li>\n\n\n\n<li><a href=\"#gatt\" title=\"\">GATT<\/a><\/li>\n\n\n\n<li><a href=\"#uuid\" title=\"\">UUID<\/a><\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><a href=\"#project-overview\" title=\"\">Project Overview<\/a><\/li>\n\n\n\n<li><a href=\"#esp32-ble-device-arduino-code\" title=\"\">Part 1: ESP32 BLE Device &#8211; Arduino Code<\/a><\/li>\n\n\n\n<li><a href=\"#create-web-ble-app\" title=\"\">Part 2: Creating the Web BLE App<\/a><\/li>\n\n\n\n<li><a href=\"#host-web-ble-app\" title=\"Part 3: Hosting your Web BLE App\">Part 3: Hosting your Web BLE App<\/a><\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"web-bluetooth-intro\">Introducing Web Bluetooth<\/h2>\n\n\n\n<p>Web Bluetooth (also sometimes referred to as Web BLE) is a technology that allows you to connect and control BLE-enabled devices, like the ESP32, directly from your web browser using JavaScript.<\/p>\n\n\n\n<p>With Web BLE, you can create web applications that interact with your ESP32 devices via Bluetooth, enabling you to control GPIO pins, exchange data, and manage your devices remotely through a web interface (this means any device that supports a web browser like your computer or smartphone).<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" decoding=\"async\" width=\"1200\" height=\"369\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/Web-BLE-ESP32-Example.png?resize=1200%2C369&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Explaining Web Bluetooth with ESP32\" class=\"wp-image-136390\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/Web-BLE-ESP32-Example.png?w=3126&amp;quality=100&amp;strip=all&amp;ssl=1 3126w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/Web-BLE-ESP32-Example.png?resize=300%2C92&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/Web-BLE-ESP32-Example.png?resize=1024%2C315&amp;quality=100&amp;strip=all&amp;ssl=1 1024w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/Web-BLE-ESP32-Example.png?resize=768%2C236&amp;quality=100&amp;strip=all&amp;ssl=1 768w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/Web-BLE-ESP32-Example.png?resize=1536%2C473&amp;quality=100&amp;strip=all&amp;ssl=1 1536w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/Web-BLE-ESP32-Example.png?resize=2048%2C630&amp;quality=100&amp;strip=all&amp;ssl=1 2048w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/Web-BLE-ESP32-Example.png?w=2400&amp;quality=100&amp;strip=all&amp;ssl=1 2400w\" sizes=\"(max-width: 1200px) 100vw, 1200px\" \/><\/figure><\/div>\n\n\n<p>One of the key advantages of Web BLE is its cross-platform compatibility. Unlike traditional mobile apps developed for Android or iOS, Web BLE applications are web-based and can run on any device with a modern web browser that supports Web BLE. <\/p>\n\n\n\n<p>This cross-platform compatibility removes the need for users to download and install dedicated mobile apps, simplifying the user experience and reducing development efforts. <\/p>\n\n\n\n<p>This means you can use a smartphone, tablet, or desktop computer to connect and control ESP32 devices using a Web BLE application.<\/p>\n\n\n\n<p>The Web Bluetooth API is still under development, but it is generally considered to be stable and usable. It has been implemented in Chrome, Edge, Opera, and Firefox, and it is supported on Android and Windows.<strong> However, it is not yet supported on iOS<\/strong>.<\/p>\n\n\n\n<p class=\"rntbox rntcred\"><strong>Note: <\/strong> At the time of writing this tutorial, Web BLE is not supported on iOS.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"ble-basic-concepts\">Bluetooth Low Energy Introduction &#8211; Basic Concepts<\/h2>\n\n\n\n<p>Before proceeding, it&#8217;s important to get familiar with some basic BLE concepts. We also recommend that you take a quick look at our BLE getting started guides and tutorials:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-bluetooth-low-energy-ble-arduino-ide\/\">Getting Started with ESP32 Bluetooth Low Energy (BLE) on Arduino IDE<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-ble-server-client\/\">ESP32 BLE Server and Client (Bluetooth Low Energy)<\/a><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"ble-peripheral-controller\">BLE Peripheral and Controller (Central Device)<\/h3>\n\n\n\n<p>When using Bluetooth Low Energy (BLE), it&#8217;s important to understand the roles of BLE Peripheral and BLE Controller (also referred to as the Central Device).<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" decoding=\"async\" width=\"1200\" height=\"736\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/BLE-Peripheral-Controller.png?resize=1200%2C736&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"BLE Peripheral and BLE Controller (Central)\" class=\"wp-image-136349\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/BLE-Peripheral-Controller.png?w=3126&amp;quality=100&amp;strip=all&amp;ssl=1 3126w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/BLE-Peripheral-Controller.png?resize=300%2C184&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/BLE-Peripheral-Controller.png?resize=1024%2C628&amp;quality=100&amp;strip=all&amp;ssl=1 1024w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/BLE-Peripheral-Controller.png?resize=768%2C471&amp;quality=100&amp;strip=all&amp;ssl=1 768w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/BLE-Peripheral-Controller.png?resize=1536%2C942&amp;quality=100&amp;strip=all&amp;ssl=1 1536w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/BLE-Peripheral-Controller.png?resize=2048%2C1256&amp;quality=100&amp;strip=all&amp;ssl=1 2048w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/BLE-Peripheral-Controller.png?w=2400&amp;quality=100&amp;strip=all&amp;ssl=1 2400w\" sizes=\"(max-width: 1200px) 100vw, 1200px\" \/><\/figure><\/div>\n\n\n<p>In our particular example, the ESP32 takes the role of the BLE Peripheral, serving as the device that provides data or services. Your smartphone or computer acts as the BLE Controller, managing the connection and communication with the ESP32.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"ble-server-client\">BLE Server and Client<\/h3>\n\n\n\n<p>With Bluetooth Low Energy, there are two types of devices: the server and the client. The ESP32 can act either as a client or as a server. But, in our particular example, it will act as a server, exposing its GATT structure containing data. The BLE Server acts as a provider of data or services, while the BLE Client consumes or uses these services.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"421\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/BLE-Client-Server.png?resize=1024%2C421&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 BLE Client and BLE Server\" class=\"wp-image-136350\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/BLE-Client-Server.png?resize=1024%2C421&amp;quality=100&amp;strip=all&amp;ssl=1 1024w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/BLE-Client-Server.png?resize=300%2C123&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/BLE-Client-Server.png?resize=768%2C315&amp;quality=100&amp;strip=all&amp;ssl=1 768w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/BLE-Client-Server.png?resize=1536%2C631&amp;quality=100&amp;strip=all&amp;ssl=1 1536w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/BLE-Client-Server.png?resize=2048%2C841&amp;quality=100&amp;strip=all&amp;ssl=1 2048w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/BLE-Client-Server.png?w=2400&amp;quality=100&amp;strip=all&amp;ssl=1 2400w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure><\/div>\n\n\n<p>The server advertises its existence, so it can be found by other devices and contains data that the client can read or interact with. The client scans the nearby devices, and when it finds the server it is looking for, it establishes a connection and can interact with that device by reading or writing on its <em>characteristics<\/em>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"gatt\">GATT<\/h3>\n\n\n\n<p>GATT, which stands for Generic Attribute Profile, is a fundamental concept in Bluetooth Low Energy (BLE) technology. Essentially, it serves as a blueprint for how BLE devices communicate with each other. Think of it as a structured language that two BLE devices use to exchange information seamlessly.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"629\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/BLE-basic-GATT.png?resize=1024%2C629&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"GATT example structure\" class=\"wp-image-136351\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/BLE-basic-GATT.png?resize=1024%2C629&amp;quality=100&amp;strip=all&amp;ssl=1 1024w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/BLE-basic-GATT.png?resize=300%2C184&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/BLE-basic-GATT.png?resize=768%2C472&amp;quality=100&amp;strip=all&amp;ssl=1 768w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/BLE-basic-GATT.png?resize=1536%2C944&amp;quality=100&amp;strip=all&amp;ssl=1 1536w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/BLE-basic-GATT.png?resize=2048%2C1259&amp;quality=100&amp;strip=all&amp;ssl=1 2048w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/BLE-basic-GATT.png?w=2400&amp;quality=100&amp;strip=all&amp;ssl=1 2400w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure><\/div>\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Profile<\/strong>: standard collection of services for a specific use case;<\/li>\n\n\n\n<li><strong>Service<\/strong>: collection of related information, like sensor readings, battery level, heart rate, etc. ;<\/li>\n\n\n\n<li><strong>Characteristic<\/strong>: it is where the actual data is saved on the hierarchy (<strong>value<\/strong>);<\/li>\n\n\n\n<li><strong>Descriptor<\/strong>: metadata about the data;<\/li>\n\n\n\n<li><strong>Properties<\/strong>: describe how the characteristic value can be interacted with. For example: read, write, notify, broadcast, indicate, etc.<\/li>\n<\/ul>\n\n\n\n<p class=\"rntbox rntclblue\">For a more detailed introduction to these BLE concepts, read: <a href=\"https:\/\/randomnerdtutorials.com\/esp32-bluetooth-low-energy-ble-arduino-ide\/\" title=\"\">Getting Started with ESP32 Bluetooth Low Energy (BLE) on Arduino IDE<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"uuid\">UUID<\/h3>\n\n\n\n<p>A UUID is a unique digital identifier used in BLE and GATT to distinguish and locate services, characteristics, and descriptors. It&#8217;s like a distinct label that ensures every component in a Bluetooth device has a unique name.<\/p>\n\n\n\n<p>Each service, characteristic, and descriptor has a UUID (Universally Unique Identifier). A UUID is a unique 128-bit (16 bytes) number. For example:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><strong>55072829-bc9e-4c53-938a-74a6d4c78776<\/strong><\/pre>\n\n\n\n<p>There are shortened and default UUIDs for services, and characteristics specified in the&nbsp;<a href=\"https:\/\/www.bluetooth.com\/specifications\/gatt\/services\" target=\"_blank\" rel=\"noreferrer noopener\">SIG (Bluetooth Special Interest Group)<\/a>. This means, that if you have a BLE device that uses the default UUIDs for its services and characteristics, you&#8217;ll know exactly how to interact with that device to get or interact with the information you&#8217;re looking for.<\/p>\n\n\n\n<p>Most of the time, with the ESP32, you&#8217;ll use your custom UUIDs. You can generate them using this&nbsp;<a href=\"https:\/\/www.uuidgenerator.net\/\" target=\"_blank\" rel=\"noreferrer noopener\">UUID generator website<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"project-overview\">Project Overview<\/h2>\n\n\n\n<p>Now that you&#8217;re more familiar with BLE concepts, let&#8217;s go through a quick overview of the project we&#8217;ll build.<\/p>\n\n\n\n<p>The ESP32 will act as a BLE Peripheral\/BLE Server that advertises its existence. Your computer, smartphone, or tablet will act as a BLE Controller\/Client that interacts with the ESP32 device.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"315\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/Web-BLE-ESP32-Example-1024x315.png?resize=1024%2C315&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Web BLE\" class=\"wp-image-136390\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/Web-BLE-ESP32-Example.png?resize=1024%2C315&amp;quality=100&amp;strip=all&amp;ssl=1 1024w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/Web-BLE-ESP32-Example.png?resize=300%2C92&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/Web-BLE-ESP32-Example.png?resize=768%2C236&amp;quality=100&amp;strip=all&amp;ssl=1 768w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/Web-BLE-ESP32-Example.png?resize=1536%2C473&amp;quality=100&amp;strip=all&amp;ssl=1 1536w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/Web-BLE-ESP32-Example.png?resize=2048%2C630&amp;quality=100&amp;strip=all&amp;ssl=1 2048w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/Web-BLE-ESP32-Example.png?w=2400&amp;quality=100&amp;strip=all&amp;ssl=1 2400w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure><\/div>\n\n\n<p>The ESP32 GATT structure will have one <em>service <\/em>with two <em>characteristics<\/em>. One characteristic (let&#8217;s call it <em>sensor characteristic<\/em>) will be the place to save a <em>value <\/em>that changes over time (like sensor readings). The other characteristic (let&#8217;s call it <em>LED characteristic<\/em>) will be the place to save the state of a GPIO. By changing the <em>value<\/em> of that <em>characteristic<\/em>, we&#8217;ll be able to control an LED connected to that GPIO.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"629\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/BLE-basic-GATT.png?resize=1024%2C629&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Profile GATT example\" class=\"wp-image-136351\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/BLE-basic-GATT.png?resize=1024%2C629&amp;quality=100&amp;strip=all&amp;ssl=1 1024w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/BLE-basic-GATT.png?resize=300%2C184&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/BLE-basic-GATT.png?resize=768%2C472&amp;quality=100&amp;strip=all&amp;ssl=1 768w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/BLE-basic-GATT.png?resize=1536%2C944&amp;quality=100&amp;strip=all&amp;ssl=1 1536w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/BLE-basic-GATT.png?resize=2048%2C1259&amp;quality=100&amp;strip=all&amp;ssl=1 2048w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/BLE-basic-GATT.png?w=2400&amp;quality=100&amp;strip=all&amp;ssl=1 2400w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure><\/div>\n\n\n<p>The ESP32 will write a new value to the sensor characteristic periodically. Your browser will connect to the ESP32 Bluetooth device and will receive notifications whenever the value changes and will display the new value on the web page (see Fetched Value in the picture below).<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"369\" height=\"735\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/ESP32-Web-BLE-App-Smartphone.png?resize=369%2C735&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Web BLE App\" class=\"wp-image-136342\" style=\"width:277px;height:551px\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/ESP32-Web-BLE-App-Smartphone.png?w=369&amp;quality=100&amp;strip=all&amp;ssl=1 369w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/ESP32-Web-BLE-App-Smartphone.png?resize=151%2C300&amp;quality=100&amp;strip=all&amp;ssl=1 151w\" sizes=\"(max-width: 369px) 100vw, 369px\" \/><\/figure><\/div>\n\n\n<p>Additionally, your browser will also connect to the <em>LED characteristic<\/em> and will change its value (see the ON and OFF buttons above). The ESP32 checks that the value of that characteristic changed, and it will change the state of the GPIO accordingly, turning the LED either on or off.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"esp32-ble-device-arduino-code\">ESP32 BLE Device &#8211; Arduino Code<\/h2>\n\n\n\n<p>The following code turns the ESP32 into a BLE device with one service and two characteristics as we&#8217;ve mentioned previously. <\/p>\n\n\n\n<p>Upload the following code to your board, and it will work straight away.<\/p>\n\n\n<pre style=\"max-height: 40em; margin-bottom: 20px;\"><code class=\"language-c\">\/*\n  Rui Santos &amp; Sara Santos - Random Nerd Tutorials\n  Complete project details at https:\/\/RandomNerdTutorials.com\/esp32-web-bluetooth\/\n  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.\n  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n*\/\n#include &lt;BLEDevice.h&gt;\n#include &lt;BLEServer.h&gt;\n#include &lt;BLEUtils.h&gt;\n#include &lt;BLE2902.h&gt;\n\nBLEServer* pServer = NULL;\nBLECharacteristic* pSensorCharacteristic = NULL;\nBLECharacteristic* pLedCharacteristic = NULL;\nbool deviceConnected = false;\nbool oldDeviceConnected = false;\nuint32_t value = 0;\n\nconst int ledPin = 2; \/\/ Use the appropriate GPIO pin for your setup\n\n\/\/ See the following for generating UUIDs:\n\/\/ https:\/\/www.uuidgenerator.net\/\n#define SERVICE_UUID        &quot;19b10000-e8f2-537e-4f6c-d104768a1214&quot;\n#define SENSOR_CHARACTERISTIC_UUID &quot;19b10001-e8f2-537e-4f6c-d104768a1214&quot;\n#define LED_CHARACTERISTIC_UUID &quot;19b10002-e8f2-537e-4f6c-d104768a1214&quot;\n\nclass MyServerCallbacks: public BLEServerCallbacks {\n  void onConnect(BLEServer* pServer) {\n    deviceConnected = true;\n  };\n\n  void onDisconnect(BLEServer* pServer) {\n    deviceConnected = false;\n  }\n};\n\nclass MyCharacteristicCallbacks : public BLECharacteristicCallbacks {\n  void onWrite(BLECharacteristic* pLedCharacteristic) {\n    String value = pLedCharacteristic-&gt;getValue();\n    if (value.length() &gt; 0) {\n      Serial.print(&quot;Characteristic event, written: &quot;);\n      Serial.println(static_cast&lt;int&gt;(value[0])); \/\/ Print the integer value\n\n      int receivedValue = static_cast&lt;int&gt;(value[0]);\n      if (receivedValue == 1) {\n        digitalWrite(ledPin, HIGH);\n      } else {\n        digitalWrite(ledPin, LOW);\n      }\n    }\n  }\n};\n\nvoid setup() {\n  Serial.begin(115200);\n  pinMode(ledPin, OUTPUT);\n\n  \/\/ Create the BLE Device\n  BLEDevice::init(&quot;ESP32&quot;);\n\n  \/\/ Create the BLE Server\n  pServer = BLEDevice::createServer();\n  pServer-&gt;setCallbacks(new MyServerCallbacks());\n\n  \/\/ Create the BLE Service\n  BLEService *pService = pServer-&gt;createService(SERVICE_UUID);\n\n  \/\/ Create a BLE Characteristic\n  pSensorCharacteristic = pService-&gt;createCharacteristic(\n                      SENSOR_CHARACTERISTIC_UUID,\n                      BLECharacteristic::PROPERTY_READ   |\n                      BLECharacteristic::PROPERTY_WRITE  |\n                      BLECharacteristic::PROPERTY_NOTIFY |\n                      BLECharacteristic::PROPERTY_INDICATE\n                    );\n\n  \/\/ Create the ON button Characteristic\n  pLedCharacteristic = pService-&gt;createCharacteristic(\n                      LED_CHARACTERISTIC_UUID,\n                      BLECharacteristic::PROPERTY_WRITE\n                    );\n\n  \/\/ Register the callback for the ON button characteristic\n  pLedCharacteristic-&gt;setCallbacks(new MyCharacteristicCallbacks());\n\n  \/\/ https:\/\/www.bluetooth.com\/specifications\/gatt\/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml\n  \/\/ Create a BLE Descriptor\n  pSensorCharacteristic-&gt;addDescriptor(new BLE2902());\n  pLedCharacteristic-&gt;addDescriptor(new BLE2902());\n\n  \/\/ Start the service\n  pService-&gt;start();\n\n  \/\/ Start advertising\n  BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();\n  pAdvertising-&gt;addServiceUUID(SERVICE_UUID);\n  pAdvertising-&gt;setScanResponse(false);\n  pAdvertising-&gt;setMinPreferred(0x0);  \/\/ set value to 0x00 to not advertise this parameter\n  BLEDevice::startAdvertising();\n  Serial.println(&quot;Waiting a client connection to notify...&quot;);\n}\n\nvoid loop() {\n  \/\/ notify changed value\n  if (deviceConnected) {\n    pSensorCharacteristic-&gt;setValue(String(value).c_str());\n    pSensorCharacteristic-&gt;notify();\n    value++;\n    Serial.print(&quot;New value notified: &quot;);\n    Serial.println(value);\n    delay(3000); \/\/ bluetooth stack will go into congestion, if too many packets are sent, in 6 hours test i was able to go as low as 3ms\n  }\n  \/\/ disconnecting\n  if (!deviceConnected &amp;&amp; oldDeviceConnected) {\n    Serial.println(&quot;Device disconnected.&quot;);\n    delay(500); \/\/ give the bluetooth stack the chance to get things ready\n    pServer-&gt;startAdvertising(); \/\/ restart advertising\n    Serial.println(&quot;Start advertising&quot;);\n    oldDeviceConnected = deviceConnected;\n  }\n  \/\/ connecting\n  if (deviceConnected &amp;&amp; !oldDeviceConnected) {\n    \/\/ do stuff here on connecting\n    oldDeviceConnected = deviceConnected;\n    Serial.println(&quot;Device Connected&quot;);\n  }\n}\n<\/code><\/pre>\n\t<p style=\"text-align:center\"><a class=\"rntwhite\" href=\"https:\/\/github.com\/RuiSantosdotme\/Random-Nerd-Tutorials\/raw\/master\/Projects\/ESP32\/BLE\/ESP32_BLE_Notify_Receive.ino\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<p>This is one of the simplest BLE code examples that turn the ESP32 into a BLE device that writes and listens to changes on characteristics. If you understand how this code works, you can easily modify it to serve more complex projects with more characteristics and services.<\/p>\n\n\n\n<p>We recommend continuing reading to learn how the code works.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">How the Code Works<\/h2>\n\n\n\n<p>In summary, this code sets up a BLE server with two characteristics\u2014one for reading sensor data and another for controlling an LED. When a BLE client connects, it can read sensor data and send commands (write on the characteristic) to turn the LED on or off. The code also handles device connection and disconnection events.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Including Libraries<\/h3>\n\n\n\n<p>First, you need to import the following libraries to deal with Bluetooth.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>#include &lt;BLEDevice.h&gt;\n#include &lt;BLEServer.h&gt;\n#include &lt;BLEUtils.h&gt;\n#include &lt;BLE2902.h&gt;<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Creating Global Variables<\/h3>\n\n\n\n<p>Then you create some global variables to use later in your code.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>BLEServer* pServer = NULL;\nBLECharacteristic* pSensorCharacteristic = NULL;\nBLECharacteristic* pLedCharacteristic = NULL;\nbool deviceConnected = false;\nbool oldDeviceConnected = false;\nuint32_t value = 0;\n\nconst int ledPin = 2; \/\/ Use the appropriate GPIO pin for your setup<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><span class=\"rnthl rntliteral\">pServer<\/span>: Pointer to the <span class=\"rnthl rntliteral\">BLEServer<\/span> object.<\/li>\n\n\n\n<li><span class=\"rnthl rntliteral\">pSensorCharacteristic<\/span>: Pointer to the <span class=\"rnthl rntliteral\">BLECharacteristic<\/span> for reading the sensor value.<\/li>\n\n\n\n<li><span class=\"rnthl rntliteral\">pLedCharacteristic<\/span>: Pointer to the <span class=\"rnthl rntliteral\">BLECharacteristic<\/span> for controlling an LED.<\/li>\n\n\n\n<li><span class=\"rnthl rntliteral\">deviceConnected<\/span>: A boolean variable to track whether a BLE device is connected.<\/li>\n\n\n\n<li><span class=\"rnthl rntliteral\">oldDeviceConnected<\/span>: A boolean variable to track the previous connection status.<\/li>\n\n\n\n<li><span class=\"rnthl rntliteral\">value<\/span>: An integer variable used to store and send sensor values.<\/li>\n\n\n\n<li><span class=\"rnthl rntliteral\">ledPin<\/span>: The GPIO pin number to which an LED is connected.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">UUID Definitions<\/h3>\n\n\n\n<p>Then, you set the UUIDs for the Service and Characteristics. Those UUIDs were created using the <a href=\"https:\/\/www.uuidgenerator.net\/\" target=\"_blank\" rel=\"noopener\" title=\"\">uuidgenerator website<\/a>. You can generate your own UUIDs for your application, but for this example, we recommend using the same UUIDs we&#8217;re using.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>#define SERVICE_UUID        \"19b10000-e8f2-537e-4f6c-d104768a1214\"\n#define SENSOR_CHARACTERISTIC_UUID \"19b10001-e8f2-537e-4f6c-d104768a1214\"\n#define LED_CHARACTERISTIC_UUID \"19b10002-e8f2-537e-4f6c-d104768a1214\"<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">BLE Server and Callbacks<\/h3>\n\n\n\n<p>Then, you create several callback functions. The <span class=\"rnthl rntliteral\">MyServerCallbacks<\/span> defines a callback function for device connection and disconnection events. In this case, we change the value of the <span class=\"rnthl rntliteral\">deviceConnected<\/span> variable to <span class=\"rnthl rntliteral\">true<\/span> or <span class=\"rnthl rntliteral\">false<\/span> depending on the connection state.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>class MyServerCallbacks: public BLEServerCallbacks {\n    void onConnect(BLEServer* pServer) {\n      deviceConnected = true;\n    };\n\n    void onDisconnect(BLEServer* pServer) {\n      deviceConnected = false;\n    }\n};<\/code><\/pre>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">MyCharacteristicCallbacks<\/span> defines a callback function to read the value of a characteristic when it changes. In our code, we&#8217;ll set this callback function for the <span class=\"rnthl rntliteral\">LED_CHARACTERISTIC<\/span> to detect when the value of the characteristic has changed. We&#8217;ll turn the LED on and off accordingly to the value of that characteristic.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>class MyCharacteristicCallbacks : public BLECharacteristicCallbacks {\n    void onWrite(BLECharacteristic* onCharacteristic) {\n        String value = onCharacteristic->getValue();\n        if (value.length() > 0) {\n            Serial.print(\"Characteristic event, written: \");\n            Serial.println(static_cast&lt;int>(value&#091;0])); \/\/ Print the integer value\n\n            int receivedValue = static_cast&lt;int>(value&#091;0]);\n            if (receivedValue == 1) {\n                digitalWrite(ledPin, HIGH);\n            } else {\n                digitalWrite(ledPin, LOW);\n            }\n        }\n    }\n};<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">setup()<\/h3>\n\n\n\n<p>In the <span class=\"rnthl rntliteral\">setup()<\/span>, initialize serial communication for debugging.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>Serial.begin(115200);<\/code><\/pre>\n\n\n\n<p>Set the <span class=\"rnthl rntliteral\">ledPin<\/span> as an output.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>pinMode(ledPin, OUTPUT);<\/code><\/pre>\n\n\n\n<p>Initialize the ESP32 as BLE device called <span class=\"rnthl rntliteral\">ESP32<\/span>. You can call it any other name, but for this project we recommend using this name to be compatible with the app we&#8217;ll build later on.<\/p>\n\n\n\n<p>Then, create a BLE server and set its callbacks for connection and disconnection.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Create the BLE Server\npServer = BLEDevice::createServer();\npServer-&gt;setCallbacks(new MyServerCallbacks());<\/code><\/pre>\n\n\n\n<p>Create a BLE service with the UUID we&#8217;ve defined earlier.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Create the BLE Service\nBLEService *pService = pServer-&gt;createService(SERVICE_UUID);<\/code><\/pre>\n\n\n\n<p>Create a BLE characteristic (inside the service we just created) for sensor values and set its properties (read, write, notify, indicate).<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Create a BLE Characteristic\npSensorCharacteristic = pService-&gt;createCharacteristic(\n                    SENSOR_CHARACTERISTIC_UUID,\n                    BLECharacteristic::PROPERTY_READ   |\n                    BLECharacteristic::PROPERTY_WRITE  |\n                    BLECharacteristic::PROPERTY_NOTIFY |\n                    BLECharacteristic::PROPERTY_INDICATE\n                  );<\/code><\/pre>\n\n\n\n<p>Create a BLE characteristic for the LED characteristic and set its property (write).<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Create the ON button Characteristic\npLedCharacteristic = pService-&gt;createCharacteristic(\n                    LED_CHARACTERISTIC_UUID,\n                    BLECharacteristic::PROPERTY_WRITE\n                  );<\/code><\/pre>\n\n\n\n<p>Register the callback for the LED characteristic, so that we detect when a new value was written on that characteristic.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>pLedCharacteristic-&gt;setCallbacks(new MyCharacteristicCallbacks());<\/code><\/pre>\n\n\n\n<p>Add BLE descriptors (<span class=\"rnthl rntliteral\">BLE2902<\/span>) to both characteristics. <\/p>\n\n\n\n<div class=\"wp-block-group rntbox rntclgray\"><div class=\"wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained\">\n<p><strong>Note:<\/strong> BLE2902 is a specific descriptor that is often used for Client Characteristic Configuration (CCC). CCC descriptors are used to configure how a client (the device connecting to the server) wants to be notified or indicated of changes in a characteristic&#8217;s value. In simpler terms, they control whether the client should receive notifications or indications when the value of the associated characteristic changes.<\/p>\n\n\n\n<p>By adding BLE2902 descriptors to both characteristics, you make it possible for clients to configure how they want to be notified or updated when the values of these characteristics change. Clients can use these descriptors to enable or disable notifications or indications, depending on their preferences or requirements for real-time updates from the ESP32 server.<\/p>\n<\/div><\/div>\n\n\n\n<p>Start the BLE service.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Start the service\npService-&gt;start();<\/code><\/pre>\n\n\n\n<p>And finally, configure advertising settings and start advertising.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Start advertising\nBLEAdvertising *pAdvertising = BLEDevice::getAdvertising();\npAdvertising-&gt;addServiceUUID(SERVICE_UUID);\npAdvertising-&gt;setScanResponse(false);\npAdvertising-&gt;setMinPreferred(0x0);  \/\/ set value to 0x00 to not advertise this parameter\nBLEDevice::startAdvertising();\nSerial.println(\"Waiting a client connection to notify...\");<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">loop()<\/h3>\n\n\n\n<p>In the <span class=\"rnthl rntliteral\">loop()<\/span>, if a BLE device is connected (<span class=\"rnthl rntliteral\">deviceConnected<\/span> is <span class=\"rnthl rntliteral\">true<\/span>), it updates the sensor value, notifies the client, and increments the value.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>if (deviceConnected) {\n    pSensorCharacteristic-&gt;setValue(String(value).c_str());\n    pSensorCharacteristic-&gt;notify();\n    value++;\n    Serial.print(\"New value notified: \");\n    Serial.println(value);\n    delay(3000); \/\/ bluetooth stack will go into congestion, if too many packets are sent, in 6 hours test i was able to go as low as 3ms\n}<\/code><\/pre>\n\n\n\n<p>If a device disconnects and <span class=\"rnthl rntliteral\">oldDeviceConnected<\/span> is <span class=\"rnthl rntliteral\">true<\/span>, it restarts advertising and logs a message.<br>If a device connects and <span class=\"rnthl rntliteral\">oldDeviceConnected<\/span> is <span class=\"rnthl rntliteral\">false<\/span>, it logs a message (you can add your own logic here for actions to be taken on connection).<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ disconnecting\nif (!deviceConnected &amp;&amp; oldDeviceConnected) {\n    Serial.println(\"Device disconnected.\");\n    delay(500); \/\/ give the bluetooth stack the chance to get things ready\n    pServer-&gt;startAdvertising(); \/\/ restart advertising\n    Serial.println(\"Start advertising\");\n    oldDeviceConnected = deviceConnected;\n}\n\/\/ connecting\nif (deviceConnected &amp;&amp; !oldDeviceConnected) {\n    \/\/ do stuff here on connecting\n    oldDeviceConnected = deviceConnected;\n    Serial.println(\"Device Connected\");\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Uploading the Code<\/h2>\n\n\n\n<p>Upload the code to your ESP32 board. After uploading, open the Serial Monitor and restart your board.<\/p>\n\n\n\n<p>You&#8217;ll see that it initialized the BLE service and is waiting for a connection from a client.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"601\" height=\"381\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/ESP32-BLE-Waiting-Client-Notify.png?resize=601%2C381&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 BLE Server waiting client to notify\" class=\"wp-image-136260\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/ESP32-BLE-Waiting-Client-Notify.png?w=601&amp;quality=100&amp;strip=all&amp;ssl=1 601w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/ESP32-BLE-Waiting-Client-Notify.png?resize=300%2C190&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 601px) 100vw, 601px\" \/><\/figure><\/div>\n\n\n<p>Now that you&#8217;ve set the ESP32 as a BLE Client, we&#8217;ll create the web app so that we can interact with the ESP32 via Bluetooth using our web browser.<\/p>\n\n\n\n<p>Alternatively, you can use our web app by going to this URL:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>&nbsp;<a href=\"https:\/\/ruisantosdotme.github.io\/esp32-web-ble\/\"><\/a><a href=\"https:\/\/ruisantosdotme.github.io\/esp32-web-ble\/\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/ruisantosdotme.github.io\/esp32-web-ble<\/a><\/li>\n<\/ul>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"369\" height=\"735\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/ESP32-Web-BLE-App-Smartphone.png?resize=369%2C735&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Web BLE App\" class=\"wp-image-136342\" style=\"width:185px;height:368px\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/ESP32-Web-BLE-App-Smartphone.png?w=369&amp;quality=100&amp;strip=all&amp;ssl=1 369w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/ESP32-Web-BLE-App-Smartphone.png?resize=151%2C300&amp;quality=100&amp;strip=all&amp;ssl=1 151w\" sizes=\"(max-width: 369px) 100vw, 369px\" \/><\/figure><\/div>\n\n\n<p>Connect to your ESP32 BLE device and see the values sent by the ESP32 being displayed on the interface and control the ESP32 on-board LED with the ON and OFF buttons. If you want to learn how to build the web app, continue reading.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"create-web-ble-app\">Creating the Web BLE App<\/h2>\n\n\n\n<p>Create an HTML file called <em>index.html<\/em> with the following code (it contains both the HTML to build the web page and Javascript to handle Web Bluetooth).<\/p>\n\n\n<pre style=\"max-height: 40em; margin-bottom: 20px;\"><code class=\"language-html\">&lt;!--\n  Rui Santos\n  Complete project details at https:\/\/RandomNerdTutorials.com\/esp32-web-bluetooth\/\n\n  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.\n  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n--&gt;\n\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;title&gt;ESP32 Web BLE App&lt;\/title&gt;\n    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot;&gt;\n    &lt;link rel=&quot;icon&quot; type=&quot;image\/png&quot; href=&quot;&quot;&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n  &lt;h1&gt;ESP32 Web BLE Application&lt;\/h1&gt;\n  &lt;button id=&quot;connectBleButton&quot;&gt;Connect to BLE Device&lt;\/button&gt;\n  &lt;button id=&quot;disconnectBleButton&quot;&gt;Disconnect BLE Device&lt;\/button&gt;\n  &lt;p&gt;BLE state: &lt;strong&gt;&lt;span id=&quot;bleState&quot; style=&quot;color:#d13a30;&quot;&gt;Disconnected&lt;\/span&gt;&lt;\/strong&gt;&lt;\/p&gt;\n  &lt;h2&gt;Fetched Value&lt;\/h2&gt;\n  &lt;p&gt;&lt;span id=&quot;valueContainer&quot;&gt;NaN&lt;\/span&gt;&lt;\/p&gt;\n  &lt;p&gt;Last reading: &lt;span id=&quot;timestamp&quot;&gt;&lt;\/span&gt;&lt;\/p&gt;\n  &lt;h2&gt;Control GPIO 2&lt;\/h2&gt;\n  &lt;button id=&quot;onButton&quot;&gt;ON&lt;\/button&gt;\n  &lt;button id=&quot;offButton&quot;&gt;OFF&lt;\/button&gt;\n  &lt;p&gt;Last value sent: &lt;span id=&quot;valueSent&quot;&gt;&lt;\/span&gt;&lt;\/p&gt;\n  &lt;p&gt;&lt;a href=&quot;https:\/\/randomnerdtutorials.com\/&quot;&gt;Created by RandomNerdTutorials.com&lt;\/a&gt;&lt;\/p&gt;\n  &lt;p&gt;&lt;a href=&quot;https:\/\/RandomNerdTutorials.com\/esp32-web-bluetooth\/&quot;&gt;Read the full project here.&lt;\/a&gt;&lt;\/p&gt;\n&lt;\/body&gt;\n&lt;script&gt;\n    \/\/ DOM Elements\n    const connectButton = document.getElementById('connectBleButton');\n    const disconnectButton = document.getElementById('disconnectBleButton');\n    const onButton = document.getElementById('onButton');\n    const offButton = document.getElementById('offButton');\n    const retrievedValue = document.getElementById('valueContainer');\n    const latestValueSent = document.getElementById('valueSent');\n    const bleStateContainer = document.getElementById('bleState');\n    const timestampContainer = document.getElementById('timestamp');\n\n    \/\/Define BLE Device Specs\n    var deviceName ='ESP32';\n    var bleService = '19b10000-e8f2-537e-4f6c-d104768a1214';\n    var ledCharacteristic = '19b10002-e8f2-537e-4f6c-d104768a1214';\n    var sensorCharacteristic= '19b10001-e8f2-537e-4f6c-d104768a1214';\n\n    \/\/Global Variables to Handle Bluetooth\n    var bleServer;\n    var bleServiceFound;\n    var sensorCharacteristicFound;\n\n    \/\/ Connect Button (search for BLE Devices only if BLE is available)\n    connectButton.addEventListener('click', (event) =&gt; {\n        if (isWebBluetoothEnabled()){\n            connectToDevice();\n        }\n    });\n\n    \/\/ Disconnect Button\n    disconnectButton.addEventListener('click', disconnectDevice);\n\n    \/\/ Write to the ESP32 LED Characteristic\n    onButton.addEventListener('click', () =&gt; writeOnCharacteristic(1));\n    offButton.addEventListener('click', () =&gt; writeOnCharacteristic(0));\n\n    \/\/ Check if BLE is available in your Browser\n    function isWebBluetoothEnabled() {\n        if (!navigator.bluetooth) {\n            console.log(&quot;Web Bluetooth API is not available in this browser!&quot;);\n            bleStateContainer.innerHTML = &quot;Web Bluetooth API is not available in this browser!&quot;;\n            return false\n        }\n        console.log('Web Bluetooth API supported in this browser.');\n        return true\n    }\n\n    \/\/ Connect to BLE Device and Enable Notifications\n    function connectToDevice(){\n        console.log('Initializing Bluetooth...');\n        navigator.bluetooth.requestDevice({\n            filters: [{name: deviceName}],\n            optionalServices: [bleService]\n        })\n        .then(device =&gt; {\n            console.log('Device Selected:', device.name);\n            bleStateContainer.innerHTML = 'Connected to device ' + device.name;\n            bleStateContainer.style.color = &quot;#24af37&quot;;\n            device.addEventListener('gattservicedisconnected', onDisconnected);\n            return device.gatt.connect();\n        })\n        .then(gattServer =&gt;{\n            bleServer = gattServer;\n            console.log(&quot;Connected to GATT Server&quot;);\n            return bleServer.getPrimaryService(bleService);\n        })\n        .then(service =&gt; {\n            bleServiceFound = service;\n            console.log(&quot;Service discovered:&quot;, service.uuid);\n            return service.getCharacteristic(sensorCharacteristic);\n        })\n        .then(characteristic =&gt; {\n            console.log(&quot;Characteristic discovered:&quot;, characteristic.uuid);\n            sensorCharacteristicFound = characteristic;\n            characteristic.addEventListener('characteristicvaluechanged', handleCharacteristicChange);\n            characteristic.startNotifications();\n            console.log(&quot;Notifications Started.&quot;);\n            return characteristic.readValue();\n        })\n        .then(value =&gt; {\n            console.log(&quot;Read value: &quot;, value);\n            const decodedValue = new TextDecoder().decode(value);\n            console.log(&quot;Decoded value: &quot;, decodedValue);\n            retrievedValue.innerHTML = decodedValue;\n        })\n        .catch(error =&gt; {\n            console.log('Error: ', error);\n        })\n    }\n\n    function onDisconnected(event){\n        console.log('Device Disconnected:', event.target.device.name);\n        bleStateContainer.innerHTML = &quot;Device disconnected&quot;;\n        bleStateContainer.style.color = &quot;#d13a30&quot;;\n\n        connectToDevice();\n    }\n\n    function handleCharacteristicChange(event){\n        const newValueReceived = new TextDecoder().decode(event.target.value);\n        console.log(&quot;Characteristic value changed: &quot;, newValueReceived);\n        retrievedValue.innerHTML = newValueReceived;\n        timestampContainer.innerHTML = getDateTime();\n    }\n\n    function writeOnCharacteristic(value){\n        if (bleServer &amp;&amp; bleServer.connected) {\n            bleServiceFound.getCharacteristic(ledCharacteristic)\n            .then(characteristic =&gt; {\n                console.log(&quot;Found the LED characteristic: &quot;, characteristic.uuid);\n                const data = new Uint8Array([value]);\n                return characteristic.writeValue(data);\n            })\n            .then(() =&gt; {\n                latestValueSent.innerHTML = value;\n                console.log(&quot;Value written to LEDcharacteristic:&quot;, value);\n            })\n            .catch(error =&gt; {\n                console.error(&quot;Error writing to the LED characteristic: &quot;, error);\n            });\n        } else {\n            console.error (&quot;Bluetooth is not connected. Cannot write to characteristic.&quot;)\n            window.alert(&quot;Bluetooth is not connected. Cannot write to characteristic. \\n Connect to BLE first!&quot;)\n        }\n    }\n\n    function disconnectDevice() {\n        console.log(&quot;Disconnect Device.&quot;);\n        if (bleServer &amp;&amp; bleServer.connected) {\n            if (sensorCharacteristicFound) {\n                sensorCharacteristicFound.stopNotifications()\n                    .then(() =&gt; {\n                        console.log(&quot;Notifications Stopped&quot;);\n                        return bleServer.disconnect();\n                    })\n                    .then(() =&gt; {\n                        console.log(&quot;Device Disconnected&quot;);\n                        bleStateContainer.innerHTML = &quot;Device Disconnected&quot;;\n                        bleStateContainer.style.color = &quot;#d13a30&quot;;\n\n                    })\n                    .catch(error =&gt; {\n                        console.log(&quot;An error occurred:&quot;, error);\n                    });\n            } else {\n                console.log(&quot;No characteristic found to disconnect.&quot;);\n            }\n        } else {\n            \/\/ Throw an error if Bluetooth is not connected\n            console.error(&quot;Bluetooth is not connected.&quot;);\n            window.alert(&quot;Bluetooth is not connected.&quot;)\n        }\n    }\n\n    function getDateTime() {\n        var currentdate = new Date();\n        var day = (&quot;00&quot; + currentdate.getDate()).slice(-2); \/\/ Convert day to string and slice\n        var month = (&quot;00&quot; + (currentdate.getMonth() + 1)).slice(-2);\n        var year = currentdate.getFullYear();\n        var hours = (&quot;00&quot; + currentdate.getHours()).slice(-2);\n        var minutes = (&quot;00&quot; + currentdate.getMinutes()).slice(-2);\n        var seconds = (&quot;00&quot; + currentdate.getSeconds()).slice(-2);\n\n        var datetime = day + &quot;\/&quot; + month + &quot;\/&quot; + year + &quot; at &quot; + hours + &quot;:&quot; + minutes + &quot;:&quot; + seconds;\n        return datetime;\n    }\n\n\n&lt;\/script&gt;\n\n&lt;\/html&gt;\n<\/code><\/pre>\n\t<p style=\"text-align:center\"><a class=\"rntwhite\" href=\"https:\/\/github.com\/RuiSantosdotme\/Random-Nerd-Tutorials\/raw\/master\/Projects\/ESP32\/BLE\/Web_BLE_App\/index_no_css.html\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">How does it Work?<\/h2>\n\n\n\n<p>This HTML and JavaScript code represents a web application that allows you to connect to an ESP32 device over Bluetooth Low Energy (BLE). The application provides a user interface to interact with the ESP32, including reading values and controlling an LED. Let&#8217;s break down the code and understand how it works.<\/p>\n\n\n\n<p>Below you can see the application we&#8217;ll build (on the right with CSS, and on the left without CSS).<\/p>\n\n\n\n<div class=\"wp-block-jetpack-tiled-gallery aligncenter is-style-rectangular\"><div class=\"\"><div class=\"tiled-gallery__gallery\"><div class=\"tiled-gallery__row\"><div class=\"tiled-gallery__col\" style=\"flex-basis:50.01146%\"><figure class=\"tiled-gallery__item\"><img decoding=\"async\" srcset=\"https:\/\/i2.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/Web-BLE-connected-ESP32.png?strip=info&#038;w=600&#038;ssl=1 600w, https:\/\/i2.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/Web-BLE-connected-ESP32.png?strip=info&#038;w=714&#038;ssl=1 714w\" alt=\"ESP32 Web BLE App (no CSS)\" data-height=\"556\" data-id=\"136322\" data-link=\"https:\/\/randomnerdtutorials.com\/?attachment_id=136322#main\" data-url=\"https:\/\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/Web-BLE-connected-ESP32.png\" data-width=\"714\" src=\"https:\/\/i2.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/Web-BLE-connected-ESP32.png?ssl=1\" data-amp-layout=\"responsive\"\/><\/figure><\/div><div class=\"tiled-gallery__col\" style=\"flex-basis:49.98854%\"><figure class=\"tiled-gallery__item\"><img decoding=\"async\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/ESP32-BLE-application.png?strip=info&#038;w=600&#038;ssl=1 600w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/ESP32-BLE-application.png?strip=info&#038;w=860&#038;ssl=1 860w\" alt=\"ESP32 Web BLE App\" data-height=\"670\" data-id=\"136320\" data-link=\"https:\/\/randomnerdtutorials.com\/?attachment_id=136320#main\" data-url=\"https:\/\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/ESP32-BLE-application.png\" data-width=\"860\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/ESP32-BLE-application.png?ssl=1\" data-amp-layout=\"responsive\"\/><\/figure><\/div><\/div><\/div><\/div><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">HTML Page<\/h3>\n\n\n\n<p>First, we create a simple HTML page. We first create two buttons: one to connect the browser to BLE devices and another to disconnect.<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;button id=\"connectBleButton\"&gt; Connect to BLE Device&lt;\/button&gt;\n&lt;button id=\"disconnectBleButton\"&gt; Disconnect BLE Device&lt;\/button&gt;<\/code><\/pre>\n\n\n\n<p>Then, we have a paragraph that will then display the BLE connection state.<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;p&gt;BLE state: &lt;strong&gt;&lt;span id=\"bleState\" style=\"color:#d13a30;\"&gt;Disconnected&lt;\/span&gt;&lt;\/strong&gt;&lt;\/p&gt;<\/code><\/pre>\n\n\n\n<p>Then, we have a section to display the value written by the ESP32 on the sensor value characteristic.<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;h2&gt;Fetched Value&lt;\/h2&gt;\n&lt;p&gt;&lt;span id=\"valueContainer\"&gt;NaN&lt;\/span&gt;&lt;\/p&gt;<\/code><\/pre>\n\n\n\n<p>We&#8217;ll also display the timestamp of when the value was received in the following paragraph.<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;p&gt;Last reading: &lt;span id=\"timestamp\"&gt;&lt;\/span&gt;&lt;\/p&gt;<\/code><\/pre>\n\n\n\n<p>Finally, we have a section with two buttons. One to write 1 on the LED characteristic (to turn it on) and another to write 0 on the LED characteristic (to turn it off).<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;button id=\"onButton\"&gt;ON&lt;\/button&gt;\n&lt;button id=\"offButton\"&gt;OFF&lt;\/button&gt;<\/code><\/pre>\n\n\n\n<p>We also have a paragraph to display the last value that was sent to control the LED.<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;p&gt;Last value sent: &lt;span id=\"valueSent\"&gt;&lt;\/span&gt;&lt;\/p&gt;<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">JavaScript &#8211; Web BLE<\/h2>\n\n\n\n<p>Next, inside the <span style=\"color: #333399;\">&lt;script&gt;<\/span><span style=\"color: #333399;\">&lt;\/script&gt;<\/span> tags, we have the Javascript code responsible for handling the buttons and connecting to the ESP32 via Bluetooth and interacting with its characteristics.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">DOM Elements<\/h3>\n\n\n\n<p>First, we select the HTML elements and assign them to a variable name for easier manipulation along the code.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>const connectButton = document.getElementById('connectBleButton');\nconst disconnectButton = document.getElementById('disconnectBleButton');\nconst onButton = document.getElementById('onButton');\nconst offButton = document.getElementById('offButton');\nconst retrievedValue = document.getElementById('valueContainer');\nconst latestValueSent = document.getElementById('valueSent');\nconst bleStateContainer = document.getElementById('bleState');\nconst timestampContainer = document.getElementById('timestamp');<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">BLE Device Specifications<\/h3>\n\n\n\n<p>Then, we add the BLE Device specifications we want to connect to. We already set up the ESP32 as a BLE server. So, we can set its specs here, the name, the UUID of the service, and the UUIDs of the characteristics we want to interact with\u2014these should be the same you&#8217;ve set on your Arduino code.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>\/\/Define BLE Device Specs\nvar deviceName ='ESP32';\nvar bleService = '19b10000-e8f2-537e-4f6c-d104768a1214';\nvar ledCharacteristic = '19b10002-e8f2-537e-4f6c-d104768a1214';\nvar sensorCharacteristic= '19b10001-e8f2-537e-4f6c-d104768a1214';<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Defining Global Variables<\/h3>\n\n\n\n<p>Then, we create some global variables to handle Bluetooth communication and device discovery later on in our code.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>var bleServer;\nvar bleServiceFound;\nvar sensorCharacteristicFound;<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Assigning Events to Buttons<\/h3>\n\n\n\n<p>Then, we add event listeners to the buttons to trigger actions when they are clicked.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Connect To BLE Device Button<\/h4>\n\n\n\n<p>The <em>Connect To BLE Device<\/em> button will trigger the <span class=\"rnthl rntliteral\">connectToDevice()<\/span> function. But first, we check if the Web BLE Javascript API is available in your browser before proceeding and we display a message on the <span class=\"rnthl rntliteral\">bleStateContainer<\/span> in case Web BLE is not supported.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>\/\/ Connect Button (search for BLE Devices only if BLE is available)\nconnectButton.addEventListener('click', (event) =&gt; {\n    if (isWebBluetoothEnabled()){\n        connectToDevice();\n    }\n});<\/code><\/pre>\n\n\n\n<p>To check if the Web Bluetooth is enabled in your browser, we created a function called <span class=\"rnthl rntliteral\">isWebBluetoothEnabled()<\/span>. The method that checks if Web BLE is enabled is <span class=\"rnthl rntliteral\">nagivator.bluetooth<\/span>.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>\/\/ Check if BLE is available in your Browser\nfunction isWebBluetoothEnabled() {\n    if (!navigator.bluetooth) {\n        console.log(\"Web Bluetooth API is not available in this browser!\");\n         bleStateContainer.innerHTML = \"Web Bluetooth API is not available in this browser\/device!\";\n         return false\n    }\n    console.log('Web Bluetooth API supported in this browser.');\n    return true\n}<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Disconnect BLE Device Button<\/h4>\n\n\n\n<p>The <em>Disconnect BLE Device<\/em> Button will call the <span class=\"rnthl rntliteral\">disconnectDevice<\/span> function.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>\/\/ Disconnect Button\ndisconnectButton.addEventListener('click', disconnectDevice);<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">ON and OFF Buttons<\/h4>\n\n\n\n<p>The on and off buttons will trigger the <span class=\"rnthl rntliteral\">writeOnCharacteristic<\/span> function. In case of the ON button, we&#8217;ll pass a <span class=\"rnthl rntliteral\">1<\/span> as a parameter, and in case of the OFF button, we&#8217;ll pass <span class=\"rnthl rntliteral\">0<\/span> as a parameter.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>\/\/ Write to the ESP32 LED Characteristic\nonButton.addEventListener('click', () =&gt; writeOnCharacteristic(1));\noffButton.addEventListener('click', () =&gt; writeOnCharacteristic(0));<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Connecting to BLE Device and search Services and Characteristics<\/h3>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">connectToDevice()<\/span> function is triggered when you click on the <em>Connect to BLE Device<\/em> button. This function searches for our specific BLE Device, its Service and Characteristics.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>function connectToDevice(){<\/code><\/pre>\n\n\n\n<p>The following lines of code search for BLE Devices with the name and Service that we&#8217;ve defined. There are other filters you can define to search for BLE devices, or you can opt to not add any filters and return all the BLE devices found.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>navigator.bluetooth.requestDevice({\n    filters: &#091;{name: deviceName}],\n    optionalServices: &#091;bleService]\n})<\/code><\/pre>\n\n\n\n<p>Once we&#8217;ve connected to your device, we display on the HTML interface that we are now connected to our device. We also add an event listener to our device, in case it disconnects (it will call the <span class=\"rnthl rntliteral\">onDisconnected<\/span> function).<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>.then(device =&gt; {\n    console.log('Device Selected:', device.name);\n    bleStateContainer.innerHTML = 'Connected to device ' + device.name;\n    bleStateContainer.style.color = \"#24af37\";\n    device.addEventListener('gattservicedisconnected', onDisconnected);\n    return device.gatt.connect();\n})<\/code><\/pre>\n\n\n\n<p>From the device, we can get our GATT server (the hierarchical structure that stores data in BLE protocol). We save our GATT server on our <span class=\"rnthl rntliteral\">bleServer<\/span> global variable. From the GATTT server, we can get the service with the UUID we&#8217;ve defined at the beginning of the code, <span class=\"rnthl rntliteral\">bleService<\/span>.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>.then(gattServer =&gt;{\n    bleServer = gattServer;\n    console.log(\"Connected to GATT Server\");\n    return bleServer.getPrimaryService(bleService);\n })<\/code><\/pre>\n\n\n\n<p>Once we&#8217;ve found our service, we save it on the global variable <span class=\"rnthl rntliteral\">bleServiceFound<\/span>, and get the sensor characteristic from the service.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>.then(service =&gt; {\n    bleServiceFound = service;\n    console.log(\"Service discovered:\", service.uuid);\n    return service.getCharacteristic(sensorCharacteristic);\n})<\/code><\/pre>\n\n\n\n<p>Now that we&#8217;ve found our sensor characteristic, we assigned it to the global <span class=\"rnthl rntliteral\">sensorCharacteristicFound<\/span> variable. We add an event listener to our characteristic to handle what happens when the characteristic value changes\u2014we call the <span class=\"rnthl rntliteral\">handleCharacteristicChange<\/span> function. We also start notifications on that characteristic. Finally, we return the current value written on the characteristic.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>.then(characteristic =&gt; {\n    console.log(\"Characteristic discovered:\", characteristic.uuid);\n    sensorCharacteristicFound = characteristic;\n    characteristic.addEventListener('characteristicvaluechanged', handleCharacteristicChange);\n    characteristic.startNotifications();\n    console.log(\"Notifications Started.\");\n    return characteristic.readValue();\n})<\/code><\/pre>\n\n\n\n<p>Next, we display the value read from the characteristic on the corresponding HTML element.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>.then(value =&gt; {\n    console.log(\"Read value: \", value);\n    const decodedValue = new TextDecoder().decode(value);\n    console.log(\"Decoded value: \", decodedValue);\n    retrievedValue.innerHTML = decodedValue;\n})<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Handle Characteristic Change<\/h3>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">handleCharacteristicChange()<\/span> function will be called when the sensor characteristic value changes. That function gets the new value written on the characteristic and places it on the corresponding HTML element. Additionally, we also get the date and time to display when was the last time that the characteristic value has changed.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>function handleCharacteristicChange(event){\n    const newValueReceived = new TextDecoder().decode(event.target.value);\n    console.log(\"Characteristic value changed: \", newValueReceived);\n    retrievedValue.innerHTML = newValueReceived;\n    timestampContainer.innerHTML = getDateTime();\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Write on Characteristics<\/h3>\n\n\n\n<p>When you click on the ON or OFF buttons, the <span class=\"rnthl rntliteral\">writeOnCharacteristic<\/span>function will be called. That function first checks if we are connected to the BLE server. If we are, it gets the LED characteristic from the BLE Service we&#8217;ve found previously, and it writes the value we&#8217;ve passed as an argument on the characteristic.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>if (bleServer &amp;&amp; bleServer.connected) {\n    bleServiceFound.getCharacteristic(ledCharacteristic)\n     .then(characteristic =&gt; {\n        console.log(\"Found the LED characteristic: \", characteristic.uuid);\n        const data = new Uint8Array(&#091;value]);\n        return characteristic.writeValue(data);\n})<\/code><\/pre>\n\n\n\n<p>If we&#8217;re successful in writing to the characteristic, we update the last value written on the HTML interface.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>.then(() =&gt; {\n    latestValueSent.innerHTML = value;\n    console.log(\"Value written to LEDcharacteristic:\", value);\n})<\/code><\/pre>\n\n\n\n<p>In case we try to write to the characteristic without being connected to BLE first, we&#8217;ll create a pop-up window informing that we need to connect to BLE first.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>} else {\n    console.error (\"Bluetooth is not connected. Cannot write to characteristic.\")\n    window.alert(\"Bluetooth is not connected. Cannot write to characteristic. \\n Connect to BLE first!\")\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Disconnect BLE Device<\/h3>\n\n\n\n<p>When you click on the <em>Disconnect BLE Device<\/em> button, the <span class=\"rnthl rntliteral\">disconnectDevice()<\/span> function is called. This function first checks if we&#8217;re connected to the server. Then, we stop the notifications on the <span class=\"rnthl rntliteral\">sensorCharacteristic<\/span> and we disconnect from the GATT server. Additionally, we also display some messages on the HTML interface and a pop-up window in case Bluetooth is not connected.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>function disconnectDevice() {\n    console.log(\"Disconnect Device.\");\n    if (bleServer &amp;&amp; bleServer.connected) {\n        if (sensorCharacteristicFound) {\n            sensorCharacteristicFound.stopNotifications()\n                .then(() =&gt; {\n                    console.log(\"Notifications Stopped\");\n                    return bleServer.disconnect();\n                })\n                .then(() =&gt; {\n                    console.log(\"Device Disconnected\");\n                    bleStateContainer.innerHTML = \"Device Disconnected\";\n                    bleStateContainer.style.color = \"#d13a30\";\n\n                })\n                .catch(error =&gt; {\n                    console.log(\"An error occurred:\", error);\n                });\n        } else {\n            console.log(\"No characteristic found to disconnect.\");\n        }\n    } else {\n        \/\/ Throw an error if Bluetooth is not connected\n        console.error(\"Bluetooth is not connected.\");\n        window.alert(\"Bluetooth is not connected.\")\n    }\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Testing the Web BLE App<\/h2>\n\n\n\n<p>Save your <em>index.html <\/em>file and drag it to your browser. The following page will open.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"714\" height=\"556\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/ESP32-Basic-Web-BLE-Application-1.png?resize=714%2C556&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Web BLE Application\" class=\"wp-image-136325\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/ESP32-Basic-Web-BLE-Application-1.png?w=714&amp;quality=100&amp;strip=all&amp;ssl=1 714w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/ESP32-Basic-Web-BLE-Application-1.png?resize=300%2C234&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 714px) 100vw, 714px\" \/><\/figure><\/div>\n\n\n<p>With the ESP32 running the code we&#8217;ve provided previously, let&#8217;s test the web app.<\/p>\n\n\n\n<p>Start by clicking on the <em>Connect to BLE Device<\/em> button. A window will pop up and you should see the ESP32 BLE Device. Connect to that device.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"714\" height=\"556\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/Web-BLE-connect-ESP32.png?resize=714%2C556&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Web BLE Connect to ESP32 Device\" class=\"wp-image-136326\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/Web-BLE-connect-ESP32.png?w=714&amp;quality=100&amp;strip=all&amp;ssl=1 714w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/Web-BLE-connect-ESP32.png?resize=300%2C234&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 714px) 100vw, 714px\" \/><\/figure><\/div>\n\n\n<p>You&#8217;ll see that the BLE Status will change to connected and you&#8217;ll start receiving the values written by the ESP32 on the sensor characteristic.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"714\" height=\"556\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/Web-BLE-connected-ESP32.png?resize=714%2C556&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Web BLE App (no CSS)\" class=\"wp-image-136322\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/Web-BLE-connected-ESP32.png?w=714&amp;quality=100&amp;strip=all&amp;ssl=1 714w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/Web-BLE-connected-ESP32.png?resize=300%2C234&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 714px) 100vw, 714px\" \/><\/figure><\/div>\n\n\n<p>Simultaneously, you should get the following messages on the Arduino Serial Monitor showing that the connection was successful and the values being written on the sensor characteristic.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"601\" height=\"381\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/ESP32-BLE-Notify-Values-Serial-Monitor.png?resize=601%2C381&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 BLE Server Notify Clients Serial Monitor\" class=\"wp-image-136327\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/ESP32-BLE-Notify-Values-Serial-Monitor.png?w=601&amp;quality=100&amp;strip=all&amp;ssl=1 601w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/ESP32-BLE-Notify-Values-Serial-Monitor.png?resize=300%2C190&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 601px) 100vw, 601px\" \/><\/figure><\/div>\n\n\n<p>Getting back to the app, if you click the ON and OFF buttons you&#8217;ll be able to control the ESP32 LED on and off.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"714\" height=\"556\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/ESP32-Web-BLE-Control-GPIOs-Copy.png?resize=714%2C556&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Control ESP32 GPIOs via Web BLE\" class=\"wp-image-136328\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/ESP32-Web-BLE-Control-GPIOs-Copy.png?w=714&amp;quality=100&amp;strip=all&amp;ssl=1 714w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/ESP32-Web-BLE-Control-GPIOs-Copy.png?resize=300%2C234&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 714px) 100vw, 714px\" \/><\/figure><\/div>\n\n\n<p>On the Serial Monitor, you&#8217;ll see that it detects the changes in the LED characteristic value.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"601\" height=\"381\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/ESP32-Serial-Monitor-Characteristic-Event.png?resize=601%2C381&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 BLE Server - detect characteristic change.\" class=\"wp-image-136329\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/ESP32-Serial-Monitor-Characteristic-Event.png?w=601&amp;quality=100&amp;strip=all&amp;ssl=1 601w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/ESP32-Serial-Monitor-Characteristic-Event.png?resize=300%2C190&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 601px) 100vw, 601px\" \/><\/figure><\/div>\n\n\n<p>Consequently, it will control the ESP32 onboard LED according to the value written on that characteristic.<\/p>\n\n\n\n<div class=\"wp-block-jetpack-tiled-gallery aligncenter is-style-rectangular\"><div class=\"\"><div class=\"tiled-gallery__gallery\"><div class=\"tiled-gallery__row\"><div class=\"tiled-gallery__col\" style=\"flex-basis:50.00000%\"><figure class=\"tiled-gallery__item\"><img decoding=\"async\" srcset=\"https:\/\/i2.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/10\/ESP32-board-Built_in-LED-turned-on-HIGH.jpg?strip=info&#038;w=600&#038;ssl=1 600w, https:\/\/i2.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/10\/ESP32-board-Built_in-LED-turned-on-HIGH.jpg?strip=info&#038;w=750&#038;ssl=1 750w\" alt=\"\" data-height=\"422\" data-id=\"120420\" data-link=\"https:\/\/randomnerdtutorials.com\/?attachment_id=120420#main\" data-url=\"https:\/\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/10\/ESP32-board-Built_in-LED-turned-on-HIGH.jpg\" data-width=\"750\" src=\"https:\/\/i2.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/10\/ESP32-board-Built_in-LED-turned-on-HIGH.jpg?ssl=1\" data-amp-layout=\"responsive\"\/><\/figure><\/div><div class=\"tiled-gallery__col\" style=\"flex-basis:50.00000%\"><figure class=\"tiled-gallery__item\"><img decoding=\"async\" srcset=\"https:\/\/i2.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/04\/ESP32-board-Built_in-LED-turned-off-LOW.jpg?strip=info&#038;w=600&#038;ssl=1 600w, https:\/\/i2.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/04\/ESP32-board-Built_in-LED-turned-off-LOW.jpg?strip=info&#038;w=750&#038;ssl=1 750w\" alt=\"ESP32 board Built in LED turned off LOW\" data-height=\"422\" data-id=\"96166\" data-link=\"https:\/\/randomnerdtutorials.com\/esp32-board-built_in-led-turned-off-low\/\" data-url=\"https:\/\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/04\/ESP32-board-Built_in-LED-turned-off-LOW.jpg\" data-width=\"750\" src=\"https:\/\/i2.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/04\/ESP32-board-Built_in-LED-turned-off-LOW.jpg?ssl=1\" data-amp-layout=\"responsive\"\/><\/figure><\/div><\/div><\/div><\/div><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Styling the Web BLE App<\/h2>\n\n\n\n<p>To make your web interface look better, we&#8217;ll add some CSS.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">index.html<\/h3>\n\n\n\n<p>Copy the following to your <em>index.html<\/em> file. This is the same HTML we provided previously, but we added some CSS classes to style the web page.<\/p>\n\n\n<pre style=\"max-height: 40em; margin-bottom: 20px;\"><code class=\"language-html\">&lt;!--\r\n  Rui Santos\r\n  Complete project details at https:\/\/RandomNerdTutorials.com\/esp32-web-bluetooth\/\r\n\r\n  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.\r\n  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\r\n--&gt;\r\n\r\n&lt;!DOCTYPE html&gt;\r\n&lt;html&gt;\r\n&lt;head&gt;\r\n    &lt;title&gt;ESP32 Web BLE App&lt;\/title&gt;\r\n    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot;&gt;\r\n    &lt;link rel=&quot;icon&quot; type=&quot;image\/png&quot; href=&quot;favicon.ico&quot;&gt;\r\n    &lt;link rel=&quot;stylesheet&quot; type=&quot;text\/css&quot; href=&quot;style.css&quot;&gt;\r\n    &lt;meta charset=&quot;UTF-8&quot;&gt;\r\n&lt;\/head&gt;\r\n&lt;body&gt;\r\n    &lt;div class=&quot;topnav&quot;&gt;\r\n        &lt;h1&gt;ESP32 Web BLE Application&lt;\/h1&gt;\r\n    &lt;\/div&gt;\r\n    &lt;div class=&quot;content&quot;&gt;\r\n        &lt;div class=&quot;card-grid&quot;&gt;\r\n            &lt;div class=&quot;card&quot;&gt;\r\n                &lt;p&gt;\r\n                    &lt;button id=&quot;connectBleButton&quot; class=&quot;connectButton&quot;&gt; Connect to BLE Device&lt;\/button&gt;\r\n                    &lt;button id=&quot;disconnectBleButton&quot; class=&quot;disconnectButton&quot;&gt; Disconnect BLE Device&lt;\/button&gt;\r\n                &lt;\/p&gt;\r\n                &lt;p class=&quot;gray-label&quot;&gt;BLE state: &lt;strong&gt;&lt;span id=&quot;bleState&quot; style=&quot;color:#d13a30;&quot;&gt;Disconnected&lt;\/span&gt;&lt;\/strong&gt;&lt;\/p&gt;\r\n            &lt;\/div&gt;\r\n        &lt;\/div&gt;\r\n        &lt;div class=&quot;card-grid&quot;&gt;\r\n            &lt;div class=&quot;card&quot;&gt;\r\n                &lt;h2&gt;Fetched Value&lt;\/h2&gt;\r\n                &lt;p class=&quot;reading&quot;&gt;&lt;span id=&quot;valueContainer&quot;&gt;NaN&lt;\/span&gt;&lt;\/p&gt;\r\n                &lt;p class=&quot;gray-label&quot;&gt;Last reading: &lt;span id=&quot;timestamp&quot;&gt;&lt;\/span&gt;&lt;\/p&gt;\r\n            &lt;\/div&gt;\r\n\r\n            &lt;div class=&quot;card&quot;&gt;\r\n                &lt;h2&gt;Control GPIO 2&lt;\/h2&gt;\r\n                &lt;button id=&quot;onButton&quot; class=&quot;onButton&quot;&gt;ON&lt;\/button&gt;\r\n                &lt;button id=&quot;offButton&quot; class=&quot;offButton&quot;&gt;OFF&lt;\/button&gt;\r\n                &lt;p class=&quot;gray-label&quot;&gt;Last value sent: &lt;span id=&quot;valueSent&quot;&gt;&lt;\/span&gt;&lt;\/p&gt;\r\n            &lt;\/div&gt;\r\n        &lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n    &lt;div class=&quot;footer&quot;&gt;\r\n        &lt;p&gt;&lt;a href=&quot;https:\/\/randomnerdtutorials.com\/&quot;&gt;Created by RandomNerdTutorials.com&lt;\/a&gt;&lt;\/p&gt;\r\n        &lt;p&gt;&lt;a href=&quot;https:\/\/RandomNerdTutorials.com\/esp32-web-bluetooth\/&quot;&gt;Read the full project here.&lt;\/a&gt;&lt;\/p&gt;\r\n    &lt;\/div&gt;\r\n&lt;\/body&gt;\r\n&lt;script&gt;\r\n    \/\/ DOM Elements\r\n    const connectButton = document.getElementById('connectBleButton');\r\n    const disconnectButton = document.getElementById('disconnectBleButton');\r\n    const onButton = document.getElementById('onButton');\r\n    const offButton = document.getElementById('offButton');\r\n    const retrievedValue = document.getElementById('valueContainer');\r\n    const latestValueSent = document.getElementById('valueSent');\r\n    const bleStateContainer = document.getElementById('bleState');\r\n    const timestampContainer = document.getElementById('timestamp');\r\n\r\n    \/\/Define BLE Device Specs\r\n    var deviceName ='ESP32';\r\n    var bleService = '19b10000-e8f2-537e-4f6c-d104768a1214';\r\n    var ledCharacteristic = '19b10002-e8f2-537e-4f6c-d104768a1214';\r\n    var sensorCharacteristic= '19b10001-e8f2-537e-4f6c-d104768a1214';\r\n\r\n    \/\/Global Variables to Handle Bluetooth\r\n    var bleServer;\r\n    var bleServiceFound;\r\n    var sensorCharacteristicFound;\r\n\r\n    \/\/ Connect Button (search for BLE Devices only if BLE is available)\r\n    connectButton.addEventListener('click', (event) =&gt; {\r\n        if (isWebBluetoothEnabled()){\r\n            connectToDevice();\r\n        }\r\n    });\r\n\r\n    \/\/ Disconnect Button\r\n    disconnectButton.addEventListener('click', disconnectDevice);\r\n\r\n    \/\/ Write to the ESP32 LED Characteristic\r\n    onButton.addEventListener('click', () =&gt; writeOnCharacteristic(1));\r\n    offButton.addEventListener('click', () =&gt; writeOnCharacteristic(0));\r\n\r\n    \/\/ Check if BLE is available in your Browser\r\n    function isWebBluetoothEnabled() {\r\n        if (!navigator.bluetooth) {\r\n            console.log('Web Bluetooth API is not available in this browser!');\r\n            bleStateContainer.innerHTML = &quot;Web Bluetooth API is not available in this browser\/device!&quot;;\r\n            return false\r\n        }\r\n        console.log('Web Bluetooth API supported in this browser.');\r\n        return true\r\n    }\r\n\r\n    \/\/ Connect to BLE Device and Enable Notifications\r\n    function connectToDevice(){\r\n        console.log('Initializing Bluetooth...');\r\n        navigator.bluetooth.requestDevice({\r\n            filters: [{name: deviceName}],\r\n            optionalServices: [bleService]\r\n        })\r\n        .then(device =&gt; {\r\n            console.log('Device Selected:', device.name);\r\n            bleStateContainer.innerHTML = 'Connected to device ' + device.name;\r\n            bleStateContainer.style.color = &quot;#24af37&quot;;\r\n            device.addEventListener('gattservicedisconnected', onDisconnected);\r\n            return device.gatt.connect();\r\n        })\r\n        .then(gattServer =&gt;{\r\n            bleServer = gattServer;\r\n            console.log(&quot;Connected to GATT Server&quot;);\r\n            return bleServer.getPrimaryService(bleService);\r\n        })\r\n        .then(service =&gt; {\r\n            bleServiceFound = service;\r\n            console.log(&quot;Service discovered:&quot;, service.uuid);\r\n            return service.getCharacteristic(sensorCharacteristic);\r\n        })\r\n        .then(characteristic =&gt; {\r\n            console.log(&quot;Characteristic discovered:&quot;, characteristic.uuid);\r\n            sensorCharacteristicFound = characteristic;\r\n            characteristic.addEventListener('characteristicvaluechanged', handleCharacteristicChange);\r\n            characteristic.startNotifications();\r\n            console.log(&quot;Notifications Started.&quot;);\r\n            return characteristic.readValue();\r\n        })\r\n        .then(value =&gt; {\r\n            console.log(&quot;Read value: &quot;, value);\r\n            const decodedValue = new TextDecoder().decode(value);\r\n            console.log(&quot;Decoded value: &quot;, decodedValue);\r\n            retrievedValue.innerHTML = decodedValue;\r\n        })\r\n        .catch(error =&gt; {\r\n            console.log('Error: ', error);\r\n        })\r\n    }\r\n\r\n    function onDisconnected(event){\r\n        console.log('Device Disconnected:', event.target.device.name);\r\n        bleStateContainer.innerHTML = &quot;Device disconnected&quot;;\r\n        bleStateContainer.style.color = &quot;#d13a30&quot;;\r\n\r\n        connectToDevice();\r\n    }\r\n\r\n    function handleCharacteristicChange(event){\r\n        const newValueReceived = new TextDecoder().decode(event.target.value);\r\n        console.log(&quot;Characteristic value changed: &quot;, newValueReceived);\r\n        retrievedValue.innerHTML = newValueReceived;\r\n        timestampContainer.innerHTML = getDateTime();\r\n    }\r\n\r\n    function writeOnCharacteristic(value){\r\n        if (bleServer &amp;&amp; bleServer.connected) {\r\n            bleServiceFound.getCharacteristic(ledCharacteristic)\r\n            .then(characteristic =&gt; {\r\n                console.log(&quot;Found the LED characteristic: &quot;, characteristic.uuid);\r\n                const data = new Uint8Array([value]);\r\n                return characteristic.writeValue(data);\r\n            })\r\n            .then(() =&gt; {\r\n                latestValueSent.innerHTML = value;\r\n                console.log(&quot;Value written to LEDcharacteristic:&quot;, value);\r\n            })\r\n            .catch(error =&gt; {\r\n                console.error(&quot;Error writing to the LED characteristic: &quot;, error);\r\n            });\r\n        } else {\r\n            console.error (&quot;Bluetooth is not connected. Cannot write to characteristic.&quot;)\r\n            window.alert(&quot;Bluetooth is not connected. Cannot write to characteristic. \\n Connect to BLE first!&quot;)\r\n        }\r\n    }\r\n\r\n    function disconnectDevice() {\r\n        console.log(&quot;Disconnect Device.&quot;);\r\n        if (bleServer &amp;&amp; bleServer.connected) {\r\n            if (sensorCharacteristicFound) {\r\n                sensorCharacteristicFound.stopNotifications()\r\n                    .then(() =&gt; {\r\n                        console.log(&quot;Notifications Stopped&quot;);\r\n                        return bleServer.disconnect();\r\n                    })\r\n                    .then(() =&gt; {\r\n                        console.log(&quot;Device Disconnected&quot;);\r\n                        bleStateContainer.innerHTML = &quot;Device Disconnected&quot;;\r\n                        bleStateContainer.style.color = &quot;#d13a30&quot;;\r\n\r\n                    })\r\n                    .catch(error =&gt; {\r\n                        console.log(&quot;An error occurred:&quot;, error);\r\n                    });\r\n            } else {\r\n                console.log(&quot;No characteristic found to disconnect.&quot;);\r\n            }\r\n        } else {\r\n            \/\/ Throw an error if Bluetooth is not connected\r\n            console.error(&quot;Bluetooth is not connected.&quot;);\r\n            window.alert(&quot;Bluetooth is not connected.&quot;)\r\n        }\r\n    }\r\n\r\n    function getDateTime() {\r\n        var currentdate = new Date();\r\n        var day = (&quot;00&quot; + currentdate.getDate()).slice(-2); \/\/ Convert day to string and slice\r\n        var month = (&quot;00&quot; + (currentdate.getMonth() + 1)).slice(-2);\r\n        var year = currentdate.getFullYear();\r\n        var hours = (&quot;00&quot; + currentdate.getHours()).slice(-2);\r\n        var minutes = (&quot;00&quot; + currentdate.getMinutes()).slice(-2);\r\n        var seconds = (&quot;00&quot; + currentdate.getSeconds()).slice(-2);\r\n\r\n        var datetime = day + &quot;\/&quot; + month + &quot;\/&quot; + year + &quot; at &quot; + hours + &quot;:&quot; + minutes + &quot;:&quot; + seconds;\r\n        return datetime;\r\n    }\r\n\r\n\r\n&lt;\/script&gt;\r\n\r\n&lt;\/html&gt;\r\n<\/code><\/pre>\n\t<p style=\"text-align:center\"><a class=\"rntwhite\" href=\"https:\/\/github.com\/RuiSantosdotme\/Random-Nerd-Tutorials\/raw\/master\/Projects\/ESP32\/BLE\/Web_BLE_App\/index.html\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">style.css<\/h3>\n\n\n\n<p>On the <strong>same folder<\/strong> of your <em>index.html<\/em> file, create a file called <em>style.css<\/em> with the following.<\/p>\n\n\n<pre style=\"max-height: 40em; margin-bottom: 20px;\"><code class=\"language-css\">html {\r\n    font-family: Arial, Helvetica, sans-serif;\r\n    display: inline-block;\r\n    text-align: center;\r\n}\r\nh1 {\r\n    font-size: 1.8rem;\r\n    color: white;\r\n}\r\n.topnav {\r\n    overflow: hidden;\r\n    background-color: #0A1128;\r\n}\r\nbody {\r\n    margin: 0;\r\n}\r\n.content {\r\n    padding: 50px;\r\n}\r\n.card-grid {\r\n    max-width: 800px;\r\n    margin: 0 auto;\r\n    margin-bottom: 30px;\r\n    display: grid;\r\n    grid-gap: 2rem;\r\n    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));\r\n}\r\n.card {\r\n    background-color: white;\r\n    box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5);\r\n}\r\nbutton {\r\n    color: white;\r\n    padding: 14px 20px;\r\n    margin: 8px 0;\r\n    border: none;\r\n    cursor: pointer;\r\n    border-radius: 4px;\r\n}\r\n\r\n.onButton{\r\n    background-color: #1b8a94;\r\n}\r\n\r\n.offButton{\r\n    background-color: #5f6c6d;\r\n}\r\n\r\n.connectButton{\r\n    background-color: #24af37;\r\n}\r\n\r\n.disconnectButton{\r\n    background-color: #d13a30;\r\n}\r\n\r\n.gray-label {\r\n    color: #bebebe;\r\n    font-size: 1rem;\r\n}\r\n\r\n.reading {\r\n    font-size: 1.8rem;\r\n}\r\n<\/code><\/pre>\n\t<p style=\"text-align:center\"><a class=\"rntwhite\" href=\"https:\/\/github.com\/RuiSantosdotme\/Random-Nerd-Tutorials\/raw\/master\/Projects\/ESP32\/BLE\/Web_BLE_App\/style.css\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">favicon<\/h3>\n\n\n\n<p>Additionally, add the following favicon to your folder.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/github.com\/RuiSantosdotme\/Random-Nerd-Tutorials\/raw\/master\/Projects\/ESP32\/BLE\/Web_BLE_App\/favicon.zip\" target=\"_blank\" rel=\"noopener\" title=\"\">Download the .zip folder and unzip it to get the favicon.ico file<\/a><\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Demonstration<\/h2>\n\n\n\n<p>After creating all the required files in the same folder, open the <em>index.html<\/em> file on your web browser.<\/p>\n\n\n\n<p>This is what the application will look like. It works exactly the same way as we&#8217;ve seen previously, but it looks much better.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"860\" height=\"670\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/ESP32-BLE-application.png?resize=860%2C670&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Web BLE Application\" class=\"wp-image-136320\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/ESP32-BLE-application.png?w=860&amp;quality=100&amp;strip=all&amp;ssl=1 860w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/ESP32-BLE-application.png?resize=300%2C234&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/ESP32-BLE-application.png?resize=768%2C598&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 860px) 100vw, 860px\" \/><\/figure><\/div>\n\n\n<h2 class=\"wp-block-heading\" id=\"host-web-ble-app\">Taking it Further &#8211; Hosting your Web BLE App<\/h2>\n\n\n\n<p>At the moment, you can only connect to the ESP32 via BLE by opening the <em>index.html<\/em> file on the web browser of your computer. If you want to open it on your smartphone or any other device you would need to copy that file to the device and then, open it on the web browser. This is not very convenient.<\/p>\n\n\n\n<p>The best way to have access to your web app on any device is to host your files on a server. To work with BLE, the files need to be served via HTTPS.<\/p>\n\n\n\n<p>To host our web app, we&#8217;ll use GitHub pages. If you don&#8217;t have a GitHub account, create one before proceeding.<\/p>\n\n\n\n<p>1. On your account dashboard, click on the <strong>+<\/strong> icon and create a <strong>New repository<\/strong>.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"998\" height=\"299\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/1-Github-pages-create-new-repository-1.png?resize=998%2C299&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Github Create New Repository\" class=\"wp-image-136334\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/1-Github-pages-create-new-repository-1.png?w=998&amp;quality=100&amp;strip=all&amp;ssl=1 998w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/1-Github-pages-create-new-repository-1.png?resize=300%2C90&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/1-Github-pages-create-new-repository-1.png?resize=768%2C230&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 998px) 100vw, 998px\" \/><\/figure><\/div>\n\n\n<p>2. The following page will load. Give a name to your repository, and make sure it is set to <strong>Public<\/strong>. Then, click on <strong>Create repository<\/strong>.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"751\" height=\"898\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/2-Github-pages-create-repository-name.png?resize=751%2C898&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"GitHub give repository name\" class=\"wp-image-136335\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/2-Github-pages-create-repository-name.png?w=751&amp;quality=100&amp;strip=all&amp;ssl=1 751w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/2-Github-pages-create-repository-name.png?resize=251%2C300&amp;quality=100&amp;strip=all&amp;ssl=1 251w\" sizes=\"(max-width: 751px) 100vw, 751px\" \/><\/figure><\/div>\n\n\n<p>3. Then, click on <strong>Add file<\/strong> &gt; <strong>Upload files to repository<\/strong>.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1022\" height=\"553\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/3-upload-files-repository.png?resize=1022%2C553&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"GitHub repository upload files\" class=\"wp-image-136336\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/3-upload-files-repository.png?w=1022&amp;quality=100&amp;strip=all&amp;ssl=1 1022w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/3-upload-files-repository.png?resize=300%2C162&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/3-upload-files-repository.png?resize=768%2C416&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 1022px) 100vw, 1022px\" \/><\/figure><\/div>\n\n\n<p>4. Then, drag your <em>index.html<\/em>, <em>style.css<\/em>, and <em>favicon.ico<\/em> files to the repository. Then, click on <strong>Commit changes<\/strong>.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"997\" height=\"851\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/4-Github-drag-files-repository.png?resize=997%2C851&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"GitHub repository drag files\" class=\"wp-image-136337\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/4-Github-drag-files-repository.png?w=997&amp;quality=100&amp;strip=all&amp;ssl=1 997w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/4-Github-drag-files-repository.png?resize=300%2C256&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/4-Github-drag-files-repository.png?resize=768%2C656&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 997px) 100vw, 997px\" \/><\/figure><\/div>\n\n\n<p>5. Then, go to <strong>Settings <\/strong>&gt; <strong>Pages<\/strong> and make sure you have the options highlighted in red below. Then, click the <strong>Save <\/strong>button.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1006\" height=\"677\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/5-Github-submit-branch-pages.png?resize=1006%2C677&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"GitHub Pages Host Repository\" class=\"wp-image-136338\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/5-Github-submit-branch-pages.png?w=1006&amp;quality=100&amp;strip=all&amp;ssl=1 1006w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/5-Github-submit-branch-pages.png?resize=300%2C202&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/5-Github-submit-branch-pages.png?resize=768%2C517&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 1006px) 100vw, 1006px\" \/><\/figure><\/div>\n\n\n<p>After submitting, wait a few minutes for the web page to be available. Your web app will be in the following domain:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">YOUR_GITHUB_USERNAME.github.io\/YOUR_REPOSITORY_NAME<\/pre>\n\n\n\n<p>In our case, it is available on the following web page: <a href=\"https:\/\/ruisantosdotme.github.io\/esp32-web-ble\/\" target=\"_blank\" rel=\"noopener\" title=\"\">https:\/\/ruisantosdotme.github.io\/esp32-web-ble\/<\/a><\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">https:\/\/ruisantosdotme.github.io\/esp32-web-ble\/<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Demonstration<\/h2>\n\n\n\n<p>Now, you can access your <a href=\"https:\/\/ruisantosdotme.github.io\/esp32-web-ble\/\" target=\"_blank\" rel=\"noopener\" title=\"\">Web BLE App on any device<\/a> (that supports Web BLE) with a web browser by going to that URL. Then, you can connect to the ESP32 via BLE using that device and read and write on its characteristics.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"759\" height=\"701\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/Web-BLE-App-Hosted-computer-browser.png?resize=759%2C701&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Web BLE App Computer Browser\" class=\"wp-image-136339\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/Web-BLE-App-Hosted-computer-browser.png?w=759&amp;quality=100&amp;strip=all&amp;ssl=1 759w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/Web-BLE-App-Hosted-computer-browser.png?resize=300%2C277&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 759px) 100vw, 759px\" \/><\/figure><\/div>\n\n\n<p>Now you can access the Web App on your smartphone or tablet and control your ESP32 BLE Device from there.<\/p>\n\n\n<div class=\"wp-block-image is-resized\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"369\" height=\"735\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/ESP32-Web-BLE-App-Smartphone.png?resize=369%2C735&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Web BLE App Smartphone\" class=\"wp-image-136342\" style=\"aspect-ratio:0.5020408163265306;width:227px;height:auto\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/ESP32-Web-BLE-App-Smartphone.png?w=369&amp;quality=100&amp;strip=all&amp;ssl=1 369w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/ESP32-Web-BLE-App-Smartphone.png?resize=151%2C300&amp;quality=100&amp;strip=all&amp;ssl=1 151w\" sizes=\"(max-width: 369px) 100vw, 369px\" \/><\/figure><\/div>\n\n\n<h2 class=\"wp-block-heading\">Wrapping Up<\/h2>\n\n\n\n<p>In this tutorial, you learned about the Web BLE technology. In simple terms, it is a JavaScript API that allows us to create web apps to interact with BLE devices from any web browser that supports Web BLE.<\/p>\n\n\n\n<p>You learned how to set the ESP32 as a BLE device with a Service and Characteristics and you create a web application to interact with the ESP32 characteristics. This way, we are now able to control the ESP32 via BLE using our browser.<\/p>\n\n\n\n<p>We hope you&#8217;ve found this tutorial useful and that it helped you start into this relatively new technology.<\/p>\n\n\n\n<p>We have other ESP32 tutorials related to Bluetooth that you may like:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-bluetooth-low-energy-ble-arduino-ide\/\">Getting Started with ESP32 Bluetooth Low Energy (BLE) on Arduino IDE<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-ble-server-client\/\">ESP32 BLE Server and Client (Bluetooth Low Energy)<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-bluetooth-classic-arduino-ide\/\">ESP32 Bluetooth Classic with Arduino IDE \u2013 Getting Started<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-ble-server-environmental-sensing-service\/\">ESP32 BLE Peripheral (Server): Environmental Sensing Service<\/a><\/li>\n<\/ul>\n\n\n\n<p>Do you want to learn more about the ESP32? Check out all our Resources:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/learn-esp32-with-arduino-ide\/\">Learn ESP32 with Arduino IDE (eBook)<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/build-web-servers-esp32-esp8266-ebook\/\">Build Web Servers with ESP32 and ESP8266 (eBook)<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/firebase-esp32-esp8266-ebook\/\">Firebase Web App with ESP32 and ESP8266 (eBook)<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/smart-home-ebook\/\">SMART HOME with Raspberry Pi, ESP32, and ESP8266<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/projects-esp32\/\">Free ESP32 Projects and Tutorials\u2026<\/a><\/li>\n<\/ul>\n\n\n\n<p>Thanks for reading.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This guide provides a beginner-friendly introduction to using Web Bluetooth with the ESP32. We&#8217;ll explain what Web Bluetooth is and walk you through creating a web application for interacting with &#8230; <\/p>\n<p class=\"read-more-container\"><a title=\"ESP32 Web Bluetooth (BLE): Getting Started Guide\" class=\"read-more button\" href=\"https:\/\/randomnerdtutorials.com\/esp32-web-bluetooth\/#more-135306\" aria-label=\"Read more about ESP32 Web Bluetooth (BLE): Getting Started Guide\">CONTINUE READING \u00bb<\/a><\/p>\n","protected":false},"author":5,"featured_media":136346,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[281,276,277,299,264],"tags":[],"class_list":["post-135306","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-esp32-project","category-esp32","category-esp32-arduino-ide","category-0-esp32","category-project"],"aioseo_notices":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2023\/09\/ESP32-Web-BLE-Getting-Started.jpg?fit=1280%2C720&quality=100&strip=all&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts\/135306","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/comments?post=135306"}],"version-history":[{"count":38,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts\/135306\/revisions"}],"predecessor-version":[{"id":158758,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts\/135306\/revisions\/158758"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/media\/136346"}],"wp:attachment":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/media?parent=135306"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/categories?post=135306"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/tags?post=135306"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}