ESP32 Web BLE – Điều khiển ESP32 từ Web qua Bluetooth Low Energy
Với bài hướng dẫn chủ đề ESP32 Web BLE lần này, bạn sẽ biết cách tạo một Web Server để tương tác với ESP32 thông qua công nghệ BLE (Bluetooth Low Energy – Bluetooth năng lượng thấp). Cụ thể, bạn có thể điều khiển một GPIO cụ thể trên ESP32 và truy xuất các giá trị nhận được từ ESP32, thông qua việc đọc / ghi các đặc tính BLE.
Giới thiệu Web BLE
Web BLE (đôi khi còn gọi là Web Bluetooth) là một công nghệ cho phép chúng ta kết nối và điều khiển các thiết bị hỗ trợ BLE như ESP32 trực tiếp từ trình duyệt Web (dựa trên JavaScript).
Với Web BLE, bạn có thể tạo ra các ứng dụng Web để tương tác với ESP32 và điều khiển chân GPIO, trao đổi dữ liệu, quản lý thông tin thiết bị từ xa dễ dàng qua giao diện Web (bạn có thể truy cập vào bằng điện thoại hoặc laptop đều được).
Ưu điểm chính của ESP32 Web BLE là có khả năng tương thích đa nền tảng, hỗ trợ trên cả iOS hoặc Android, chỉ cần thiết bị đó có trình duyệt Web hỗ trợ Web BLE.
Nhờ tính tương thích cao, chúng ta không cần phải cài đặt các ứng dụng dành riêng cho thiết bị, đơn giản hóa trải nghiệm người dùng.
Hiện nay, API Web Bluetooth được xem là ổn định, có thể sử dụng được. Và chúng đang được phát triển hơn nữa. Hiện nay, chúng được triển khai trong Firefox, Chrome, Opera, Edge và hỗ trợ cả Android và Windows, tuy nhiên chúng chưa hỗ trợ trên iOS.
Giới thiệu BLE
Trước khi bắt đầu vào chủ đề ESP32 Web BLE, bạn cần làm quen với các khái niệm BLE cơ bản. Trên Website IoTZone đã có khá nhiều bài viết hướng dẫn về chủ đề ESP32 BLE, bạn có thể xem qua tại:
- BLE là gì? Có nên sử dụng Bluetooth Low Energy không?
- Cách dùng Bluetooth Low Energy (BLE) trong ESP32
Bộ điều khiển trung tâm và ngoại vi BLE
Khi sử dụng BLE, bạn cần hiểu ý nghĩa của bộ điều khiển trung tâm (BLE Controller) và các ngoại vi BLE (BLE Peripheral):
Trong dự án này, ESP32 như một thiết bị ngoại vi BLE, chúng chỉ cung cấp dữ liệu hoặc dịch vụ. Còn điện thoại / laptop là một bộ điều khiển BLE, giúp quả lý kết nối và liên lạc với ESP32.
Máy chủ và máy khách BLE
Trong ESP32 Web BLE có 2 loại thiết bị khác nhau: Máy chủ và máy khách. ESP32 có thể là một máy chủ (Server) hoặc là máy khách (Client).
Tuy nhiên, trong hướng dẫn về ESP32 Web BLE này, chúng sẽ hoạt động như một máy chủ, giúp hiển thị cấu trúc GATT chứa dữ liệu. BLE Server giống một nhà cung cấp thông tin dữ liệu và dịch vụ, còn BLE Client thu thập hoặc sử dụng dịch vụ này.
BLE Server hiển thị sự tồn tại của nó để các thiết bị khác có thể tìm thấy Server và đọc hoặc tương tác với thông tin dữ liệu của Server.
BLE Client sẽ quét các thiết bị lân cận và khi tìm thấy đúng Server mình cần, chúng sẽ kết nối và tương tác với chúng bằng cách đọc / ghi dữ liệu.
Giới thiệu GATT
GATT là viết tắt của Generic Attribute Profile – một concept bên trong công nghệ BLE. Cụ thể, chúng có vai trò như một bản thiết kế chi tiết về cách giao tiếp giữa các thiết bị BLE. Bạn có thể hiểu đơn giản chúng như một ngôn ngữ có cấu trúc mà 2 thiết bị BLE dùng để trao đổi thông tin sao cho liền mạch.
UUID
Đây là một mã định danh kỹ thuật số được dùng trong ESP32 Web BLE và GATT, nhằm phân biệt và định vị các dịch vụ, bộ mô tả và đặc điểm. Chúng giống như một label riêng biệt, giúp đảm bảo tất cả các thiết bị Bluetooth đều có 1 tên duy nhất.
Mỗi dịch vụ, đặc điểm và mô tả đều có 1 UUID duy nhất, đây là một số 128 bit (16byte), ví dụ:
- 55072829-bc9e-4c53-938a-74a6d4c78776
Khi sử dụng UUID với ESP32, đa phần chúng ta sẽ dùng UUID tùy chỉnh. Bạn có thể tạo thông qua Website tạo UUID này.
Tổng quan dự án ESP32 Web BLE
Sau khi đã hiểu hơn về BLE, bây giờ hãy cùng xem qua dự án mà IoTZone sẽ hướng dẫn bạn trong bài viết này.
ESP32 sẽ hoạt động như một máy chủ ngoại vi / BLE Server để hiển thị sự tồn tại của chúng. Điện thoại, laptop hoặc máy tính bảng của bạn sẽ giống một BLE Client / Bộ điều khiển để tương tác với ESP32.
Cấu trúc ESP32 GATT sẽ có một dịch vụ với 2 đặc điểm:
- Đặc tính cảm biến: Lưu giá trị thay đổi theo thời gian (số đọc cảm biến)
- Đặc tính LED: Lưu trạng thái của GPIO. Bằng cách thay đổi giá trị của đặc tính này, chúng ta có thể điều khiển đèn LED được kết nối với GPIO đó.
ESP32 sẽ ghi một giá trị mới vào đặc tính cảm biến theo định kỳ. Trình duyệt của bạn sẽ kết nối với ESP32 Web BLE và nhận được thông báo mỗi khi có giá trị thay đổi, và chúng hiển thị giá trị mới trên Website, ví dụ như hình bên dưới:
Ngoài ra, trình duyệt của bạn cũng kết nối với đặc tính LED và thay đổi giá trị của nó (nút ON / OFF) ở hình trên. ESP32 sẽ kiểm tra giá trị của đặc tính này có thay đổi không, rồi sau đó thay đổi trạng thái của GPIO tương ứng như bật / tắt đèn LED.
Code cho ESP32
Đoạn code sau giúp biến ESP32 thành một thiết bị BLE với 2 đặc tính mà mình đã giới thiệu ở trên.
Bạn chỉ cần upload code sau vào là ESP32 Web BLE sẽ hoạt động ngay lập tức:
#include <BLEDevice.h> #include <BLEServer.h> #include <BLEUtils.h> #include <BLE2902.h> BLEServer* pServer = NULL; BLECharacteristic* pSensorCharacteristic = NULL; BLECharacteristic* pLedCharacteristic = NULL; bool deviceConnected = false; bool oldDeviceConnected = false; uint32_t value = 0; const int ledPin = 2; // Sử dụng chân GPIO bạn đã setup // See the following for generating UUIDs: // https://www.uuidgenerator.net/ #define SERVICE_UUID "19b10000-e8f2-537e-4f6c-d104768a1214" #define SENSOR_CHARACTERISTIC_UUID "19b10001-e8f2-537e-4f6c-d104768a1214" #define LED_CHARACTERISTIC_UUID "19b10002-e8f2-537e-4f6c-d104768a1214" class MyServerCallbacks: public BLEServerCallbacks { void onConnect(BLEServer* pServer) { deviceConnected = true; }; void onDisconnect(BLEServer* pServer) { deviceConnected = false; } }; class MyCharacteristicCallbacks : public BLECharacteristicCallbacks { void onWrite(BLECharacteristic* pLedCharacteristic) { std::string value = pLedCharacteristic->getValue(); if (value.length() > 0) { Serial.print("Characteristic event, written: "); Serial.println(static_cast<int>(value[0])); // Print the integer value int receivedValue = static_cast<int>(value[0]); if (receivedValue == 1) { digitalWrite(ledPin, HIGH); } else { digitalWrite(ledPin, LOW); } } } }; void setup() { Serial.begin(115200); pinMode(ledPin, OUTPUT); // Tạo thiết bị BLE BLEDevice::init("ESP32"); // Tạo BLE Server pServer = BLEDevice::createServer(); pServer->setCallbacks(new MyServerCallbacks()); // Tạo BLE Service BLEService *pService = pServer->createService(SERVICE_UUID); // Tạo BLE Characteristic pSensorCharacteristic = pService->createCharacteristic( SENSOR_CHARACTERISTIC_UUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_NOTIFY | BLECharacteristic::PROPERTY_INDICATE ); // Create the ON button Characteristic pLedCharacteristic = pService->createCharacteristic( LED_CHARACTERISTIC_UUID, BLECharacteristic::PROPERTY_WRITE ); // Register the callback for the ON button characteristic pLedCharacteristic->setCallbacks(new MyCharacteristicCallbacks()); // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml // Create a BLE Descriptor pSensorCharacteristic->addDescriptor(new BLE2902()); pLedCharacteristic->addDescriptor(new BLE2902()); // Start the service pService->start(); // Bắt đầu quảng cáo BLEAdvertising *pAdvertising = BLEDevice::getAdvertising(); pAdvertising->addServiceUUID(SERVICE_UUID); pAdvertising->setScanResponse(false); pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter BLEDevice::startAdvertising(); Serial.println("Waiting a client connection to notify..."); } void loop() { // notify changed value if (deviceConnected) { pSensorCharacteristic->setValue(String(value).c_str()); pSensorCharacteristic->notify(); value++; Serial.print("New value notified: "); Serial.println(value); 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 } // disconnecting if (!deviceConnected && oldDeviceConnected) { Serial.println("Device disconnected."); delay(500); // give the bluetooth stack the chance to get things ready pServer->startAdvertising(); // restart advertising Serial.println("Start advertising"); oldDeviceConnected = deviceConnected; } // connecting if (deviceConnected && !oldDeviceConnected) { // do stuff here on connecting oldDeviceConnected = deviceConnected; Serial.println("Device Connected"); } }
Đây là một trong những đoạn code đơn giản nhất cho ESP32 Web BLE, giúp biến ESP32 thành một thiết bị ESP32 để ghi các thay đổi về đặc tính.
Sau khi upload code lên, bạn hãy mở màn hình Serial Monitor trên Arduino, bạn sẽ thấy chúng đã tạo dịch vụ BLE và đang chờ kết nối từ Client.
Ngoài ra, bạn có thể dùng ứng dụng Web của mình bằng cách truy cập vào URL sau:
Tạo ứng dụng Web BLE
Bạn hãy tạo 1 tập tin HTML có tên là index.html với đoạn code sau (chúng chứa HTML để xây dựng Website và JavaScript để xử lý tác vụ về Web Bluetooth):
<!DOCTYPE html> <html> <head> <title>ESP32 Web BLE App</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="icon" type="image/png" href=""> </head> <body> <h1>ESP32 Web BLE Application</h1> <button id="connectBleButton">Connect to BLE Device</button> <button id="disconnectBleButton">Disconnect BLE Device</button> <p>BLE state: <strong><span id="bleState" style="color:#d13a30;">Disconnected</span></strong></p> <h2>Fetched Value</h2> <p><span id="valueContainer">NaN</span></p> <p>Last reading: <span id="timestamp"></span></p> <h2>Control GPIO 2</h2> <button id="onButton">ON</button> <button id="offButton">OFF</button> <p>Last value sent: <span id="valueSent"></span></p> <p><a href="https://randomnerdtutorials.com/">Created by RandomNerdTutorials.com</a></p> <p><a href="https://RandomNerdTutorials.com/esp32-web-bluetooth/">Read the full project here.</a></p> </body> <script> // DOM Elements const connectButton = document.getElementById('connectBleButton'); const disconnectButton = document.getElementById('disconnectBleButton'); const onButton = document.getElementById('onButton'); const offButton = document.getElementById('offButton'); const retrievedValue = document.getElementById('valueContainer'); const latestValueSent = document.getElementById('valueSent'); const bleStateContainer = document.getElementById('bleState'); const timestampContainer = document.getElementById('timestamp'); //Define BLE Device Specs var deviceName ='ESP32'; var bleService = '19b10000-e8f2-537e-4f6c-d104768a1214'; var ledCharacteristic = '19b10002-e8f2-537e-4f6c-d104768a1214'; var sensorCharacteristic= '19b10001-e8f2-537e-4f6c-d104768a1214'; //Global Variables to Handle Bluetooth var bleServer; var bleServiceFound; var sensorCharacteristicFound; // Connect Button (search for BLE Devices only if BLE is available) connectButton.addEventListener('click', (event) => { if (isWebBluetoothEnabled()){ connectToDevice(); } }); // Disconnect Button disconnectButton.addEventListener('click', disconnectDevice); // Write to the ESP32 LED Characteristic onButton.addEventListener('click', () => writeOnCharacteristic(1)); offButton.addEventListener('click', () => writeOnCharacteristic(0)); // Check if BLE is available in your Browser function isWebBluetoothEnabled() { if (!navigator.bluetooth) { console.log("Web Bluetooth API is not available in this browser!"); bleStateContainer.innerHTML = "Web Bluetooth API is not available in this browser!"; return false } console.log('Web Bluetooth API supported in this browser.'); return true } // Connect to BLE Device and Enable Notifications function connectToDevice(){ console.log('Initializing Bluetooth...'); navigator.bluetooth.requestDevice({ filters: [{name: deviceName}], optionalServices: [bleService] }) .then(device => { console.log('Device Selected:', device.name); bleStateContainer.innerHTML = 'Connected to device ' + device.name; bleStateContainer.style.color = "#24af37"; device.addEventListener('gattservicedisconnected', onDisconnected); return device.gatt.connect(); }) .then(gattServer =>{ bleServer = gattServer; console.log("Connected to GATT Server"); return bleServer.getPrimaryService(bleService); }) .then(service => { bleServiceFound = service; console.log("Service discovered:", service.uuid); return service.getCharacteristic(sensorCharacteristic); }) .then(characteristic => { console.log("Characteristic discovered:", characteristic.uuid); sensorCharacteristicFound = characteristic; characteristic.addEventListener('characteristicvaluechanged', handleCharacteristicChange); characteristic.startNotifications(); console.log("Notifications Started."); return characteristic.readValue(); }) .then(value => { console.log("Read value: ", value); const decodedValue = new TextDecoder().decode(value); console.log("Decoded value: ", decodedValue); retrievedValue.innerHTML = decodedValue; }) .catch(error => { console.log('Error: ', error); }) } function onDisconnected(event){ console.log('Device Disconnected:', event.target.device.name); bleStateContainer.innerHTML = "Device disconnected"; bleStateContainer.style.color = "#d13a30"; connectToDevice(); } function handleCharacteristicChange(event){ const newValueReceived = new TextDecoder().decode(event.target.value); console.log("Characteristic value changed: ", newValueReceived); retrievedValue.innerHTML = newValueReceived; timestampContainer.innerHTML = getDateTime(); } function writeOnCharacteristic(value){ if (bleServer && bleServer.connected) { bleServiceFound.getCharacteristic(ledCharacteristic) .then(characteristic => { console.log("Found the LED characteristic: ", characteristic.uuid); const data = new Uint8Array([value]); return characteristic.writeValue(data); }) .then(() => { latestValueSent.innerHTML = value; console.log("Value written to LEDcharacteristic:", value); }) .catch(error => { console.error("Error writing to the LED characteristic: ", error); }); } else { console.error ("Bluetooth is not connected. Cannot write to characteristic.") window.alert("Bluetooth is not connected. Cannot write to characteristic. \n Connect to BLE first!") } } function disconnectDevice() { console.log("Disconnect Device."); if (bleServer && bleServer.connected) { if (sensorCharacteristicFound) { sensorCharacteristicFound.stopNotifications() .then(() => { console.log("Notifications Stopped"); return bleServer.disconnect(); }) .then(() => { console.log("Device Disconnected"); bleStateContainer.innerHTML = "Device Disconnected"; bleStateContainer.style.color = "#d13a30"; }) .catch(error => { console.log("An error occurred:", error); }); } else { console.log("No characteristic found to disconnect."); } } else { // Throw an error if Bluetooth is not connected console.error("Bluetooth is not connected."); window.alert("Bluetooth is not connected.") } } function getDateTime() { var currentdate = new Date(); var day = ("00" + currentdate.getDate()).slice(-2); // Convert day to string and slice var month = ("00" + (currentdate.getMonth() + 1)).slice(-2); var year = currentdate.getFullYear(); var hours = ("00" + currentdate.getHours()).slice(-2); var minutes = ("00" + currentdate.getMinutes()).slice(-2); var seconds = ("00" + currentdate.getSeconds()).slice(-2); var datetime = day + "/" + month + "/" + year + " at " + hours + ":" + minutes + ":" + seconds; return datetime; } </script> </html>
Kiểm tra hoạt động của ESP32 Web BLE
Lưu file index.html và kéo chúng vào trình duyệt, trên trình duyệt sẽ hiển thị trang sau:
Bạn hãy thử nghiệm hoạt động của Website trên với ESP32 đã nạp code bên trên nhé!
Ví dụ, khi nhấn vào nút Connect to BLE Device, một cửa sổ mới được bật lên và bạn sẽ thấy thiết bị ESP32 của mình trong đó:
Khi nhấn kết nối xong, bạn sẽ thấy trạng thái BLE đổi thành đã kết nối (Connected) và trên trang Web hiển thị các giá trị được ghi bởi ESP32 trên đặc tính cảm biến.
Đồng thời, trên Serial Monitor cũng hiển thị thông báo đã kết nối thành công và các giá trị được lưu trên đặc tính cảm biến.
Để demo hoạt động của ESP32 Web BLE, bạn hãy nhấn nút ON hoặc OFF trên trang Web và quan sát đèn LED của ESP32 nhé! Đèn LED sẽ được bật hoặc tắt tùy theo thao tác của bạn.
Thay đổi giao diện ESP32 Web BLE đẹp hơn
Để giao diện web đẹp hơn, bạn có thể thêm một số đoạn mã CSS như sau:
index.html
Sao chép đoạn code sau vào file index.html của bạn, chúng bao gồm đoạn HTML mình đã cung cấp ở trên nhưng có thêm một số CSS trang trí giao diện Web:
<!DOCTYPE html> <html> <head> <title>ESP32 Web BLE App</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="icon" type="image/png" href="favicon.ico"> <link rel="stylesheet" type="text/css" href="style.css"> <meta charset="UTF-8"> </head> <body> <div class="topnav"> <h1>ESP32 Web BLE Application</h1> </div> <div class="content"> <div class="card-grid"> <div class="card"> <p> <button id="connectBleButton" class="connectButton"> Connect to BLE Device</button> <button id="disconnectBleButton" class="disconnectButton"> Disconnect BLE Device</button> </p> <p class="gray-label">BLE state: <strong><span id="bleState" style="color:#d13a30;">Disconnected</span></strong></p> </div> </div> <div class="card-grid"> <div class="card"> <h2>Fetched Value</h2> <p class="reading"><span id="valueContainer">NaN</span></p> <p class="gray-label">Last reading: <span id="timestamp"></span></p> </div> <div class="card"> <h2>Control GPIO 2</h2> <button id="onButton" class="onButton">ON</button> <button id="offButton" class="offButton">OFF</button> <p class="gray-label">Last value sent: <span id="valueSent"></span></p> </div> </div> </div> <div class="footer"> <p><a href="https://randomnerdtutorials.com/">Created by RandomNerdTutorials.com</a></p> <p><a href="https://RandomNerdTutorials.com/esp32-web-bluetooth/">Read the full project here.</a></p> </div> </body> <script> // DOM Elements const connectButton = document.getElementById('connectBleButton'); const disconnectButton = document.getElementById('disconnectBleButton'); const onButton = document.getElementById('onButton'); const offButton = document.getElementById('offButton'); const retrievedValue = document.getElementById('valueContainer'); const latestValueSent = document.getElementById('valueSent'); const bleStateContainer = document.getElementById('bleState'); const timestampContainer = document.getElementById('timestamp'); //Define BLE Device Specs var deviceName ='ESP32'; var bleService = '19b10000-e8f2-537e-4f6c-d104768a1214'; var ledCharacteristic = '19b10002-e8f2-537e-4f6c-d104768a1214'; var sensorCharacteristic= '19b10001-e8f2-537e-4f6c-d104768a1214'; //Global Variables to Handle Bluetooth var bleServer; var bleServiceFound; var sensorCharacteristicFound; // Connect Button (search for BLE Devices only if BLE is available) connectButton.addEventListener('click', (event) => { if (isWebBluetoothEnabled()){ connectToDevice(); } }); // Disconnect Button disconnectButton.addEventListener('click', disconnectDevice); // Write to the ESP32 LED Characteristic onButton.addEventListener('click', () => writeOnCharacteristic(1)); offButton.addEventListener('click', () => writeOnCharacteristic(0)); // Check if BLE is available in your Browser function isWebBluetoothEnabled() { if (!navigator.bluetooth) { console.log('Web Bluetooth API is not available in this browser!'); bleStateContainer.innerHTML = "Web Bluetooth API is not available in this browser/device!"; return false } console.log('Web Bluetooth API supported in this browser.'); return true } // Connect to BLE Device and Enable Notifications function connectToDevice(){ console.log('Initializing Bluetooth...'); navigator.bluetooth.requestDevice({ filters: [{name: deviceName}], optionalServices: [bleService] }) .then(device => { console.log('Device Selected:', device.name); bleStateContainer.innerHTML = 'Connected to device ' + device.name; bleStateContainer.style.color = "#24af37"; device.addEventListener('gattservicedisconnected', onDisconnected); return device.gatt.connect(); }) .then(gattServer =>{ bleServer = gattServer; console.log("Connected to GATT Server"); return bleServer.getPrimaryService(bleService); }) .then(service => { bleServiceFound = service; console.log("Service discovered:", service.uuid); return service.getCharacteristic(sensorCharacteristic); }) .then(characteristic => { console.log("Characteristic discovered:", characteristic.uuid); sensorCharacteristicFound = characteristic; characteristic.addEventListener('characteristicvaluechanged', handleCharacteristicChange); characteristic.startNotifications(); console.log("Notifications Started."); return characteristic.readValue(); }) .then(value => { console.log("Read value: ", value); const decodedValue = new TextDecoder().decode(value); console.log("Decoded value: ", decodedValue); retrievedValue.innerHTML = decodedValue; }) .catch(error => { console.log('Error: ', error); }) } function onDisconnected(event){ console.log('Device Disconnected:', event.target.device.name); bleStateContainer.innerHTML = "Device disconnected"; bleStateContainer.style.color = "#d13a30"; connectToDevice(); } function handleCharacteristicChange(event){ const newValueReceived = new TextDecoder().decode(event.target.value); console.log("Characteristic value changed: ", newValueReceived); retrievedValue.innerHTML = newValueReceived; timestampContainer.innerHTML = getDateTime(); } function writeOnCharacteristic(value){ if (bleServer && bleServer.connected) { bleServiceFound.getCharacteristic(ledCharacteristic) .then(characteristic => { console.log("Found the LED characteristic: ", characteristic.uuid); const data = new Uint8Array([value]); return characteristic.writeValue(data); }) .then(() => { latestValueSent.innerHTML = value; console.log("Value written to LEDcharacteristic:", value); }) .catch(error => { console.error("Error writing to the LED characteristic: ", error); }); } else { console.error ("Bluetooth is not connected. Cannot write to characteristic.") window.alert("Bluetooth is not connected. Cannot write to characteristic. \n Connect to BLE first!") } } function disconnectDevice() { console.log("Disconnect Device."); if (bleServer && bleServer.connected) { if (sensorCharacteristicFound) { sensorCharacteristicFound.stopNotifications() .then(() => { console.log("Notifications Stopped"); return bleServer.disconnect(); }) .then(() => { console.log("Device Disconnected"); bleStateContainer.innerHTML = "Device Disconnected"; bleStateContainer.style.color = "#d13a30"; }) .catch(error => { console.log("An error occurred:", error); }); } else { console.log("No characteristic found to disconnect."); } } else { // Throw an error if Bluetooth is not connected console.error("Bluetooth is not connected."); window.alert("Bluetooth is not connected.") } } function getDateTime() { var currentdate = new Date(); var day = ("00" + currentdate.getDate()).slice(-2); // Convert day to string and slice var month = ("00" + (currentdate.getMonth() + 1)).slice(-2); var year = currentdate.getFullYear(); var hours = ("00" + currentdate.getHours()).slice(-2); var minutes = ("00" + currentdate.getMinutes()).slice(-2); var seconds = ("00" + currentdate.getSeconds()).slice(-2); var datetime = day + "/" + month + "/" + year + " at " + hours + ":" + minutes + ":" + seconds; return datetime; } </script> </html>
style.css
Trên cùng thư mục của index.html, tạo một file có tên style.css như sau:
html { font-family: Arial, Helvetica, sans-serif; display: inline-block; text-align: center; } h1 { font-size: 1.8rem; color: white; } .topnav { overflow: hidden; background-color: #0A1128; } body { margin: 0; } .content { padding: 50px; } .card-grid { max-width: 800px; margin: 0 auto; margin-bottom: 30px; display: grid; grid-gap: 2rem; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); } .card { background-color: white; box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5); } button { color: white; padding: 14px 20px; margin: 8px 0; border: none; cursor: pointer; border-radius: 4px; } .onButton{ background-color: #1b8a94; } .offButton{ background-color: #5f6c6d; } .connectButton{ background-color: #24af37; } .disconnectButton{ background-color: #d13a30; } .gray-label { color: #bebebe; font-size: 1rem; } .reading { font-size: 1.8rem; }
Thêm favicon
Bạn có thể thêm favicon yêu thích của mình bằng cách tải thư mục .zip về và giải nén qua đường link này.
Demo
Sau khi tạo tất cả các file trên ở trong cùng 1 thư mục, hãy mở index.html trên trình duyệt Web của bạn.
Lúc đó, ứng dụng Web sẽ như sau, khá đẹp hơn phải không :3
Lời kết
Trên đây là hướng dẫn chi tiết với chủ đề ESP32 Web BLE. Khi nắm vững kỹ năng này, bạn có thể tạo các ứng dụng Web trực quan để tương tác với các thiết bị BLE từ bất kỳ trình duyệt Web nào có hỗ trợ Web BLE.
Hy vọng bài hướng dẫn này hữu ích với bạn. Đừng quên theo dõi các bài hướng dẫn ESP32 khác trên Website IoTZone nhé! Chúng tôi có da dạng bài viết hướng dẫn, với nhiều chủ đề khác nhau cho bạn tham khảo.
IoTZone – Chuyên cung cấp thiết bị điện tử & tài liệu cho Makers
- Website: https://www.iotzone.vn/
- Fanpage: https://www.facebook.com/Iotzonemaker
- SDT: 0364174499
- Zalo: https://zalo.me/0364174499