ESP32 Bluetooth Multiple Apps Example Kết Hợp Tất Cả Bluetooth Apps Trong Một Dự Án Tutorial

Tổng Quan

Ví dụ Multiple Bluetooth Apps (Nhiều Ứng Dụng Bluetooth) minh họa cách kết hợp nhiều giao diện ứng dụng Bluetooth thành một dự án ESP32 duy nhất. Chạy Monitor, Chat, Slider, Joystick, Temperature, Plotter, Table, Analog Gauge, và Rotator tất cả cùng lúc — có thể truy cập thông qua ứng dụng DIYables Bluetooth STEM. Được thiết kế cho các board ESP32 với hỗ trợ cả kết nối BLE (Bluetooth Low Energy)Classic Bluetooth. Điều này lý tưởng cho các dự án phức tạp cần nhiều giao diện điều khiển và hiển thị đồng thời.

Ví dụ này hỗ trợ hai chế độ Bluetooth:

  • ESP32 BLE (Bluetooth Low Energy): Hoạt động trên cả Android và iOS
  • ESP32 Classic Bluetooth: Chỉ hoạt động trên Android. iOS không hỗ trợ Classic Bluetooth. Sử dụng BLE nếu bạn cần hỗ trợ iOS.
ESP32 Bluetooth multiple apps example - kết hợp tất cả Bluetooth apps trong một dự Án tutorial

Tính Năng

  • 9 Apps Trong Một: Monitor, Chat, Slider, Joystick, Temperature, Plotter, Table, Analog Gauge, và Rotator chạy đồng thời
  • Callbacks Độc Lập: Mỗi app có bộ xử lý sự kiện riêng
  • Thời Gian Độc Lập: Khoảng thời gian cập nhật khác nhau cho mỗi app
  • Chuyển Đổi App: Tự do chuyển đổi giữa các app trong ứng dụng di động
  • BLE & Classic Bluetooth: Chọn chế độ Bluetooth phù hợp với dự án của bạn
  • Đa Nền Tảng: Chế độ BLE hoạt động trên cả Android và iOS; Classic Bluetooth hoạt động trên Android
  • Tùy Chọn Tiết Kiệm Điện: Chế độ BLE tiêu thụ ít điện năng hơn Classic Bluetooth

Phần Cứng Cần Thiết

1×mô-đun phát triển ESP-WROOM-32
1×Alternatively, ESP32 Uno-form board
1×Alternatively, ESP32 S3 Uno-form board
1×Cáp USB Type-C
1×breadboard (Bo Mạch Thí Nghiệm)
1×Dây Jumper
1×(Khuyến nghị) Screw Terminal Expansion Board for ESP32
1×(Khuyến nghị) Breakout Expansion Board for ESP32
1×(Khuyến nghị) Power Splitter for ESP32

Or you can buy the following kits:

1×DIYables ESP32 Starter Kit (ESP32 included)
1×DIYables Sensor Kit (30 sensors/displays)
1×DIYables Sensor Kit (18 sensors/displays)

Code ESP32

Các Bước Thực Hiện

Thực hiện theo hướng dẫn từng bước:

  • Nếu đây là lần đầu tiên bạn sử dụng ESP32, hãy tham khảo hướng dẫn về ESP32 - Cài Đặt Phần Mềm.
  • Kết nối board ESP32 với máy tính của bạn bằng cáp USB.
  • Khởi động Arduino IDE trên máy tính của bạn.
  • Chọn board ESP32 và COM port phù hợp.
  • Điều hướng đến biểu tượng Libraries trên thanh bên trái của Arduino IDE.
  • Tìm kiếm "DIYables Bluetooth", sau đó tìm thư viện DIYables Bluetooth của DIYables
  • Nhấp nút Install để cài đặt thư viện.
ESP32 diyables Bluetooth thư viện
  • Bạn sẽ được hỏi về việc cài đặt một số thư viện phụ thuộc khác
  • Nhấp nút Install All để cài đặt tất cả thư viện phụ thuộc.
ESP32 diyables Bluetooth dependency

> Quan Trọng: Vì ví dụ này bao gồm nhiều thư viện ứng dụng Bluetooth, sketch đã biên dịch sẽ lớn hơn bình thường. Bạn phải chọn partition scheme đúng (xem bên dưới).

Chọn một trong hai chế độ Bluetooth bên dưới tùy thuộc vào nhu cầu của bạn:

Code ESP32 Classic Bluetooth (hoạt động với app chỉ trên Android)

Lưu Ý: Classic Bluetooth KHÔNG được hỗ trợ trên iOS. Nếu bạn cần hỗ trợ iOS, hãy sử dụng code BLE bên dưới.

  • Trong Arduino IDE, đi đến File Examples DIYables Bluetooth Esp32Bluetooth_MultipleApps example, hoặc sao chép code trên và dán vào editor của Arduino IDE
/* * DIYables Bluetooth Library - ESP32 Classic Bluetooth Multiple Apps Example * Works with DIYables Bluetooth STEM app on Android * Note: Classic Bluetooth is NOT supported on iOS. Use BLE examples for iOS support. * * This example demonstrates how to use multiple Bluetooth apps simultaneously: * - Monitor: Real-time serial monitoring and logging * - Chat: Bi-directional text messaging * - Slider: Dual slider control (0-255 range) * - Joystick: Interactive joystick control (X, Y coordinates) * - Temperature: Temperature monitoring and display * - Plotter: Real-time data plotting (3 channels) * - Table: Two-column data table with real-time updates * - Analog Gauge: Circular gauge for sensor monitoring * - Rotator: Rotatable disc/knob control * - Pin Control: Digital pin control and monitoring * * Features: * - All apps run simultaneously on a single Bluetooth connection * - Automatic message routing to appropriate app handlers * - Simplified callback system - no manual parsing needed * - All Bluetooth protocol details handled by the library * - Compatible with DIYables Bluetooth Mobile App * * Compatible Boards: * - ESP32 (all variants with Classic Bluetooth) * - ESP32-WROOM-32 * - ESP32-DevKitC * - ESP32-WROVER * * Note: Select "Huge APP (3MB No OTA/1MB SPIFFS)" partition scheme * in Arduino IDE: Tools > Partition Scheme * * Setup: * 1. Upload the sketch to your ESP32 * 2. Open Serial Monitor (115200 baud) * 3. Open DIYables Bluetooth App on your phone * 4. Connect to "ESP32 Multi-App" * 5. Navigate to different screens to test each app * * Tutorial: https://diyables.io/bluetooth-app * Author: DIYables */ #include <DIYables_BluetoothServer.h> #include <DIYables_BluetoothMonitor.h> #include <DIYables_BluetoothChat.h> #include <DIYables_BluetoothSlider.h> #include <DIYables_BluetoothJoystick.h> #include <DIYables_BluetoothTemperature.h> #include <DIYables_BluetoothPlotter.h> #include <DIYables_BluetoothTable.h> #include <DIYables_BluetoothAnalogGauge.h> #include <DIYables_BluetoothRotator.h> #include <platforms/DIYables_Esp32Bluetooth.h> // Create Bluetooth instances DIYables_Esp32Bluetooth bluetooth("ESP32 Multi-App"); DIYables_BluetoothServer bluetoothServer(bluetooth); // Create app instances DIYables_BluetoothMonitor bluetoothMonitor; DIYables_BluetoothChat bluetoothChat; DIYables_BluetoothSlider bluetoothSlider(0, 255, 1); DIYables_BluetoothJoystick bluetoothJoystick(false, 5); DIYables_BluetoothTemperature bluetoothTemperature(-10.0, 50.0, "°C"); DIYables_BluetoothPlotter bluetoothPlotter; DIYables_BluetoothTable bluetoothTable; DIYables_BluetoothAnalogGauge bluetoothGauge(0.0, 100.0, "%"); DIYables_BluetoothRotator bluetoothRotator(ROTATOR_MODE_CONTINUOUS); // ESP32 built-in LED (may vary by board) const int LED_PIN = 2; // State variables int currentSlider1 = 128; int currentSlider2 = 64; int currentJoystickX = 0; int currentJoystickY = 0; float currentTemperature = 25.0; float currentGaugeValue = 50.0; float currentRotatorAngle = 0.0; int messageCount = 0; // Timing variables unsigned long lastMonitorUpdate = 0; unsigned long lastTempUpdate = 0; unsigned long lastPlotUpdate = 0; unsigned long lastTableUpdate = 0; unsigned long lastGaugeUpdate = 0; float plotPhase = 0; void setup() { Serial.begin(115200); delay(1000); Serial.println("DIYables Bluetooth - ESP32 Multiple Apps Example"); // Initialize built-in LED pinMode(LED_PIN, OUTPUT); digitalWrite(LED_PIN, LOW); // Initialize Bluetooth server bluetoothServer.begin(); // Register all apps with the server bluetoothServer.addApp(&bluetoothMonitor); bluetoothServer.addApp(&bluetoothChat); bluetoothServer.addApp(&bluetoothSlider); bluetoothServer.addApp(&bluetoothJoystick); bluetoothServer.addApp(&bluetoothTemperature); bluetoothServer.addApp(&bluetoothPlotter); bluetoothServer.addApp(&bluetoothTable); bluetoothServer.addApp(&bluetoothGauge); bluetoothServer.addApp(&bluetoothRotator); Serial.print("Registered apps: "); Serial.println(bluetoothServer.getAppCount()); // Configure Plotter bluetoothPlotter.setPlotTitle("Sensor Data"); bluetoothPlotter.setAxisLabels("Time", "Value"); bluetoothPlotter.setYAxisRange(-1.5, 1.5); bluetoothPlotter.setMaxSamples(100); bluetoothPlotter.setLegendLabels("Sine", "Cosine", "Random"); // Configure Table rows bluetoothTable.addRow("Status"); bluetoothTable.addRow("Uptime"); bluetoothTable.addRow("Slider 1"); bluetoothTable.addRow("Slider 2"); bluetoothTable.addRow("Joystick X"); bluetoothTable.addRow("Joystick Y"); bluetoothTable.addRow("Temperature"); bluetoothTable.addRow("Gauge Value"); bluetoothTable.addRow("Rotator Angle"); bluetoothTable.addRow("Free Heap"); // Set up all callbacks setupCallbacks(); Serial.println("Waiting for Bluetooth connection..."); } void setupCallbacks() { // ---- Connection events ---- bluetoothServer.setOnConnected([]() { Serial.println("Bluetooth connected!"); digitalWrite(LED_PIN, HIGH); bluetoothMonitor.send("=== ESP32 Multi-App Connected ==="); bluetoothMonitor.send("All apps are ready!"); bluetoothChat.send("Hello! ESP32 Multi-App is connected."); }); bluetoothServer.setOnDisconnected([]() { Serial.println("Bluetooth disconnected!"); digitalWrite(LED_PIN, LOW); }); // ---- Monitor callbacks ---- bluetoothMonitor.onMonitorMessage([](const String& message) { Serial.println("Monitor cmd: " + message); if (message == "HELP") { bluetoothMonitor.send("Commands: STATUS, HELP, LED_ON, LED_OFF, HEAP"); } else if (message == "STATUS") { bluetoothMonitor.send("Slider1=" + String(currentSlider1) + " Slider2=" + String(currentSlider2)); bluetoothMonitor.send("Joystick X=" + String(currentJoystickX) + " Y=" + String(currentJoystickY)); bluetoothMonitor.send("Temp=" + String(currentTemperature, 1) + "°C"); bluetoothMonitor.send("Gauge=" + String(currentGaugeValue, 1) + "%"); bluetoothMonitor.send("Rotator=" + String(currentRotatorAngle, 0) + "°"); } else if (message == "LED_ON") { digitalWrite(LED_PIN, HIGH); bluetoothMonitor.send("LED turned ON"); } else if (message == "LED_OFF") { digitalWrite(LED_PIN, LOW); bluetoothMonitor.send("LED turned OFF"); } else if (message == "HEAP") { bluetoothMonitor.send("Free Heap: " + String(ESP.getFreeHeap()) + " bytes"); } else { bluetoothMonitor.send("Unknown: " + message + " (type HELP)"); } }); // ---- Chat callbacks ---- bluetoothChat.onChatMessage([](const String& message) { Serial.println("Chat: " + message); bluetoothChat.send("Echo: " + message); if (message.equalsIgnoreCase("ping")) { bluetoothChat.send("pong!"); } else if (message.equalsIgnoreCase("status")) { bluetoothChat.send("Uptime: " + String(millis() / 1000) + "s, Apps: " + String(bluetoothServer.getAppCount())); } else if (message.equalsIgnoreCase("heap")) { bluetoothChat.send("Free heap: " + String(ESP.getFreeHeap()) + " bytes"); } }); // ---- Slider callbacks ---- bluetoothSlider.onSliderValue([](int slider1, int slider2) { currentSlider1 = slider1; currentSlider2 = slider2; Serial.print("Slider 1: "); Serial.print(slider1); Serial.print(", Slider 2: "); Serial.println(slider2); // Update gauge based on slider 1 currentGaugeValue = map(slider1, 0, 255, 0, 100); bluetoothGauge.send(currentGaugeValue); // Update table bluetoothTable.sendValueUpdate("Slider 1", String(slider1)); bluetoothTable.sendValueUpdate("Slider 2", String(slider2)); bluetoothTable.sendValueUpdate("Gauge Value", String(currentGaugeValue, 1) + "%"); }); bluetoothSlider.onGetConfig([]() { bluetoothSlider.send(currentSlider1, currentSlider2); }); // ---- Joystick callbacks ---- bluetoothJoystick.onJoystickValue([](int x, int y) { currentJoystickX = x; currentJoystickY = y; Serial.print("Joystick X: "); Serial.print(x); Serial.print(", Y: "); Serial.println(y); // Update table bluetoothTable.sendValueUpdate("Joystick X", String(x)); bluetoothTable.sendValueUpdate("Joystick Y", String(y)); }); bluetoothJoystick.onGetConfig([]() { bluetoothJoystick.send(currentJoystickX, currentJoystickY); }); // ---- Temperature callbacks ---- bluetoothTemperature.onTemperatureRequest([]() { bluetoothTemperature.send(currentTemperature); }); // ---- Plotter callbacks ---- bluetoothPlotter.onDataRequest([]() { Serial.println("Plotter data requested"); }); // ---- Table callbacks ---- bluetoothTable.onDataRequest([]() { Serial.println("Table data requested"); bluetoothTable.sendTableStructure(); updateAllTableValues(); }); // ---- Gauge callbacks ---- bluetoothGauge.onValueRequest([]() { bluetoothGauge.send(currentGaugeValue); }); // ---- Rotator callbacks ---- bluetoothRotator.onRotatorAngle([](float angle) { currentRotatorAngle = angle; Serial.print("Rotator: "); Serial.print(angle); Serial.println("°"); bluetoothTable.sendValueUpdate("Rotator Angle", String(angle, 0) + "°"); }); } void updateAllTableValues() { bluetoothTable.sendValueUpdate("Status", "Running"); unsigned long uptime = millis() / 1000; String uptimeStr; if (uptime >= 60) { uptimeStr = String(uptime / 60) + "m " + String(uptime % 60) + "s"; } else { uptimeStr = String(uptime) + "s"; } bluetoothTable.sendValueUpdate("Uptime", uptimeStr); bluetoothTable.sendValueUpdate("Slider 1", String(currentSlider1)); bluetoothTable.sendValueUpdate("Slider 2", String(currentSlider2)); bluetoothTable.sendValueUpdate("Joystick X", String(currentJoystickX)); bluetoothTable.sendValueUpdate("Joystick Y", String(currentJoystickY)); bluetoothTable.sendValueUpdate("Temperature", String(currentTemperature, 1) + " °C"); bluetoothTable.sendValueUpdate("Gauge Value", String(currentGaugeValue, 1) + "%"); bluetoothTable.sendValueUpdate("Rotator Angle", String(currentRotatorAngle, 0) + "°"); bluetoothTable.sendValueUpdate("Free Heap", String(ESP.getFreeHeap()) + " bytes"); } void loop() { bluetoothServer.loop(); if (!bluetooth.isConnected()) { delay(10); return; } // ---- Monitor: periodic status every 5 seconds ---- if (millis() - lastMonitorUpdate >= 5000) { lastMonitorUpdate = millis(); messageCount++; bluetoothMonitor.send("[INFO] Heartbeat #" + String(messageCount) + " - Uptime: " + String(millis() / 1000) + "s"); } // ---- Temperature: update every 2 seconds ---- if (millis() - lastTempUpdate >= 2000) { lastTempUpdate = millis(); // Simulate temperature with slight variation static float tempOffset = 0; tempOffset += random(-10, 11) / 10.0; if (tempOffset > 5.0) tempOffset = 5.0; if (tempOffset < -5.0) tempOffset = -5.0; currentTemperature = 25.0 + tempOffset; bluetoothTemperature.send(currentTemperature); bluetoothTable.sendValueUpdate("Temperature", String(currentTemperature, 1) + " °C"); } // ---- Plotter: update every 100ms ---- if (millis() - lastPlotUpdate >= 100) { lastPlotUpdate = millis(); float sine = sin(plotPhase); float cosine = cos(plotPhase); float noise = random(-50, 51) / 100.0; bluetoothPlotter.send(sine, cosine, noise); plotPhase += 0.1; if (plotPhase > 2 * PI) plotPhase = 0; } // ---- Table: update uptime every 5 seconds ---- if (millis() - lastTableUpdate >= 5000) { lastTableUpdate = millis(); unsigned long uptime = millis() / 1000; String uptimeStr; if (uptime >= 60) { uptimeStr = String(uptime / 60) + "m " + String(uptime % 60) + "s"; } else { uptimeStr = String(uptime) + "s"; } bluetoothTable.sendValueUpdate("Uptime", uptimeStr); bluetoothTable.sendValueUpdate("Free Heap", String(ESP.getFreeHeap()) + " bytes"); } // ---- Gauge: simulate sensor every 3 seconds ---- if (millis() - lastGaugeUpdate >= 3000) { lastGaugeUpdate = millis(); float sensorValue = 50.0 + 30.0 * sin(millis() / 10000.0); currentGaugeValue = sensorValue; bluetoothGauge.send(currentGaugeValue); bluetoothTable.sendValueUpdate("Gauge Value", String(currentGaugeValue, 1) + "%"); } delay(10); }
  • Quan Trọng: Đi đến Tools > Partition Scheme và chọn "Huge APP (3MB No OTA/1MB SPIFFS)". Điều này là bắt buộc vì ví dụ multiple apps sử dụng nhiều không gian flash hơn đáng kể.
  • Nhấp nút Upload trên Arduino IDE để upload code lên ESP32
  • Mở Serial Monitor
  • Kiểm tra kết quả trên Serial Monitor. Nó trông như sau:
COM6
Send
DIYables Bluetooth - ESP32 Multiple Apps Example Waiting for Bluetooth connection...
Autoscroll Show timestamp
Clear output
9600 baud  
Newline  

Code ESP32 BLE (hoạt động với app trên cả Android và iOS)

  • Trong Arduino IDE, đi đến File Examples DIYables Bluetooth Esp32BLE_MultipleApps example, hoặc sao chép code trên và dán vào editor của Arduino IDE
/* * DIYables Bluetooth Library - ESP32 BLE Multiple Apps Example * Works with DIYables Bluetooth STEM app on Android and iOS * * This example demonstrates how to use multiple Bluetooth apps simultaneously: * - Monitor, Chat, Slider, Joystick, Temperature, Plotter, * Table, Analog Gauge, Rotator * * Compatible Boards: * - ESP32-WROOM-32 * - ESP32-DevKitC * - ESP32-WROVER * - ESP32-S3 * - ESP32-C3 * - Any ESP32 board supporting BLE * * Note: Select "Huge APP (3MB No OTA/1MB SPIFFS)" partition scheme * in Arduino IDE: Tools > Partition Scheme * * Setup: * 1. Upload the sketch to your ESP32 * 2. Open Serial Monitor (115200 baud) * 3. Open DIYables Bluetooth App on your phone * 4. Connect to "ESP32BLE Multi-App" * 5. Navigate to different screens to test each app * * Tutorial: https://diyables.io/bluetooth-app * Author: DIYables */ #include <DIYables_BluetoothServer.h> #include <DIYables_BluetoothMonitor.h> #include <DIYables_BluetoothChat.h> #include <DIYables_BluetoothSlider.h> #include <DIYables_BluetoothJoystick.h> #include <DIYables_BluetoothTemperature.h> #include <DIYables_BluetoothPlotter.h> #include <DIYables_BluetoothTable.h> #include <DIYables_BluetoothAnalogGauge.h> #include <DIYables_BluetoothRotator.h> #include <platforms/DIYables_Esp32BLE.h> // BLE Configuration const char* DEVICE_NAME = "ESP32BLE Multi-App"; const char* SERVICE_UUID = "19B10000-E8F2-537E-4F6C-D104768A1214"; const char* TX_UUID = "19B10001-E8F2-537E-4F6C-D104768A1214"; const char* RX_UUID = "19B10002-E8F2-537E-4F6C-D104768A1214"; // Create Bluetooth instances DIYables_Esp32BLE bluetooth(DEVICE_NAME, SERVICE_UUID, TX_UUID, RX_UUID); DIYables_BluetoothServer bluetoothServer(bluetooth); // Create app instances DIYables_BluetoothMonitor bluetoothMonitor; DIYables_BluetoothChat bluetoothChat; DIYables_BluetoothSlider bluetoothSlider(0, 255, 1); DIYables_BluetoothJoystick bluetoothJoystick(false, 5); DIYables_BluetoothTemperature bluetoothTemperature(-10.0, 50.0, "°C"); DIYables_BluetoothPlotter bluetoothPlotter; DIYables_BluetoothTable bluetoothTable; DIYables_BluetoothAnalogGauge bluetoothGauge(0.0, 100.0, "%"); DIYables_BluetoothRotator bluetoothRotator(ROTATOR_MODE_CONTINUOUS); // State variables int currentSlider1 = 128; int currentSlider2 = 64; int currentJoystickX = 0; int currentJoystickY = 0; float currentTemperature = 25.0; float currentGaugeValue = 50.0; float currentRotatorAngle = 0.0; int messageCount = 0; // Timing variables unsigned long lastMonitorUpdate = 0; unsigned long lastTempUpdate = 0; unsigned long lastPlotUpdate = 0; unsigned long lastTableUpdate = 0; unsigned long lastGaugeUpdate = 0; float plotPhase = 0; void setup() { Serial.begin(115200); delay(1000); Serial.println("DIYables Bluetooth - ESP32 BLE Multiple Apps Example"); pinMode(2, OUTPUT); digitalWrite(2, LOW); bluetoothServer.begin(); bluetoothServer.addApp(&bluetoothMonitor); bluetoothServer.addApp(&bluetoothChat); bluetoothServer.addApp(&bluetoothSlider); bluetoothServer.addApp(&bluetoothJoystick); bluetoothServer.addApp(&bluetoothTemperature); bluetoothServer.addApp(&bluetoothPlotter); bluetoothServer.addApp(&bluetoothTable); bluetoothServer.addApp(&bluetoothGauge); bluetoothServer.addApp(&bluetoothRotator); Serial.print("Registered apps: "); Serial.println(bluetoothServer.getAppCount()); bluetoothPlotter.setPlotTitle("Sensor Data"); bluetoothPlotter.setAxisLabels("Time", "Value"); bluetoothPlotter.setYAxisRange(-1.5, 1.5); bluetoothPlotter.setMaxSamples(100); bluetoothPlotter.setLegendLabels("Sine", "Cosine", "Random"); bluetoothTable.addRow("Status"); bluetoothTable.addRow("Uptime"); bluetoothTable.addRow("Slider 1"); bluetoothTable.addRow("Slider 2"); bluetoothTable.addRow("Joystick X"); bluetoothTable.addRow("Joystick Y"); bluetoothTable.addRow("Temperature"); bluetoothTable.addRow("Gauge Value"); bluetoothTable.addRow("Rotator Angle"); bluetoothTable.addRow("Messages"); setupCallbacks(); Serial.println("Waiting for Bluetooth connection..."); } void setupCallbacks() { bluetoothServer.setOnConnected([]() { Serial.println("Bluetooth connected!"); digitalWrite(2, HIGH); bluetoothMonitor.send("=== ESP32 BLE Multi-App Connected ==="); bluetoothMonitor.send("All apps are ready!"); bluetoothChat.send("Hello! ESP32 BLE Multi-App is connected."); }); bluetoothServer.setOnDisconnected([]() { Serial.println("Bluetooth disconnected!"); digitalWrite(2, LOW); }); bluetoothMonitor.onMonitorMessage([](const String& message) { Serial.println("Monitor cmd: " + message); if (message == "HELP") { bluetoothMonitor.send("Commands: STATUS, HELP, LED_ON, LED_OFF, HEAP"); } else if (message == "STATUS") { bluetoothMonitor.send("Slider1=" + String(currentSlider1) + " Slider2=" + String(currentSlider2)); bluetoothMonitor.send("Joystick X=" + String(currentJoystickX) + " Y=" + String(currentJoystickY)); bluetoothMonitor.send("Temp=" + String(currentTemperature, 1) + "°C"); bluetoothMonitor.send("Gauge=" + String(currentGaugeValue, 1) + "%"); bluetoothMonitor.send("Rotator=" + String(currentRotatorAngle, 0) + "°"); } else if (message == "LED_ON") { digitalWrite(2, HIGH); bluetoothMonitor.send("LED turned ON"); } else if (message == "LED_OFF") { digitalWrite(2, LOW); bluetoothMonitor.send("LED turned OFF"); } else if (message == "HEAP") { bluetoothMonitor.send("Free heap: " + String(ESP.getFreeHeap()) + " bytes"); } else { bluetoothMonitor.send("Unknown: " + message + " (type HELP)"); } }); bluetoothChat.onChatMessage([](const String& message) { Serial.println("Chat: " + message); bluetoothChat.send("Echo: " + message); if (message.equalsIgnoreCase("ping")) { bluetoothChat.send("pong!"); } else if (message.equalsIgnoreCase("status")) { bluetoothChat.send("Uptime: " + String(millis() / 1000) + "s, Apps: " + String(bluetoothServer.getAppCount())); } }); bluetoothSlider.onSliderValue([](int slider1, int slider2) { currentSlider1 = slider1; currentSlider2 = slider2; Serial.print("Slider 1: "); Serial.print(slider1); Serial.print(", Slider 2: "); Serial.println(slider2); currentGaugeValue = map(slider1, 0, 255, 0, 100); bluetoothGauge.send(currentGaugeValue); bluetoothTable.sendValueUpdate("Slider 1", String(slider1)); bluetoothTable.sendValueUpdate("Slider 2", String(slider2)); bluetoothTable.sendValueUpdate("Gauge Value", String(currentGaugeValue, 1) + "%"); }); bluetoothSlider.onGetConfig([]() { bluetoothSlider.send(currentSlider1, currentSlider2); }); bluetoothJoystick.onJoystickValue([](int x, int y) { currentJoystickX = x; currentJoystickY = y; Serial.print("Joystick X: "); Serial.print(x); Serial.print(", Y: "); Serial.println(y); bluetoothTable.sendValueUpdate("Joystick X", String(x)); bluetoothTable.sendValueUpdate("Joystick Y", String(y)); }); bluetoothJoystick.onGetConfig([]() { bluetoothJoystick.send(currentJoystickX, currentJoystickY); }); bluetoothTemperature.onTemperatureRequest([]() { bluetoothTemperature.send(currentTemperature); }); bluetoothPlotter.onDataRequest([]() { Serial.println("Plotter data requested"); }); bluetoothTable.onDataRequest([]() { Serial.println("Table data requested"); bluetoothTable.sendTableStructure(); updateAllTableValues(); }); bluetoothGauge.onValueRequest([]() { bluetoothGauge.send(currentGaugeValue); }); bluetoothRotator.onRotatorAngle([](float angle) { currentRotatorAngle = angle; Serial.print("Rotator: "); Serial.print(angle); Serial.println("°"); bluetoothTable.sendValueUpdate("Rotator Angle", String(angle, 0) + "°"); }); } void updateAllTableValues() { bluetoothTable.sendValueUpdate("Status", "Running"); unsigned long uptime = millis() / 1000; String uptimeStr; if (uptime >= 60) { uptimeStr = String(uptime / 60) + "m " + String(uptime % 60) + "s"; } else { uptimeStr = String(uptime) + "s"; } bluetoothTable.sendValueUpdate("Uptime", uptimeStr); bluetoothTable.sendValueUpdate("Slider 1", String(currentSlider1)); bluetoothTable.sendValueUpdate("Slider 2", String(currentSlider2)); bluetoothTable.sendValueUpdate("Joystick X", String(currentJoystickX)); bluetoothTable.sendValueUpdate("Joystick Y", String(currentJoystickY)); bluetoothTable.sendValueUpdate("Temperature", String(currentTemperature, 1) + " °C"); bluetoothTable.sendValueUpdate("Gauge Value", String(currentGaugeValue, 1) + "%"); bluetoothTable.sendValueUpdate("Rotator Angle", String(currentRotatorAngle, 0) + "°"); bluetoothTable.sendValueUpdate("Messages", String(messageCount)); } void loop() { bluetoothServer.loop(); if (!bluetooth.isConnected()) { delay(10); return; } if (millis() - lastMonitorUpdate >= 5000) { lastMonitorUpdate = millis(); messageCount++; bluetoothMonitor.send("[INFO] Heartbeat #" + String(messageCount) + " - Uptime: " + String(millis() / 1000) + "s"); } if (millis() - lastTempUpdate >= 2000) { lastTempUpdate = millis(); static float tempOffset = 0; tempOffset += random(-10, 11) / 10.0; if (tempOffset > 5.0) tempOffset = 5.0; if (tempOffset < -5.0) tempOffset = -5.0; currentTemperature = 25.0 + tempOffset; bluetoothTemperature.send(currentTemperature); bluetoothTable.sendValueUpdate("Temperature", String(currentTemperature, 1) + " °C"); } if (millis() - lastPlotUpdate >= 100) { lastPlotUpdate = millis(); float sine = sin(plotPhase); float cosine = cos(plotPhase); float noise = random(-50, 51) / 100.0; bluetoothPlotter.send(sine, cosine, noise); plotPhase += 0.1; if (plotPhase > 2 * PI) plotPhase = 0; } if (millis() - lastTableUpdate >= 5000) { lastTableUpdate = millis(); unsigned long uptime = millis() / 1000; String uptimeStr; if (uptime >= 60) { uptimeStr = String(uptime / 60) + "m " + String(uptime % 60) + "s"; } else { uptimeStr = String(uptime) + "s"; } bluetoothTable.sendValueUpdate("Uptime", uptimeStr); bluetoothTable.sendValueUpdate("Messages", String(messageCount)); } if (millis() - lastGaugeUpdate >= 3000) { lastGaugeUpdate = millis(); float sensorValue = 50.0 + 30.0 * sin(millis() / 10000.0); currentGaugeValue = sensorValue; bluetoothGauge.send(currentGaugeValue); bluetoothTable.sendValueUpdate("Gauge Value", String(currentGaugeValue, 1) + "%"); } delay(10); }
  • Quan Trọng: Đi đến Tools > Partition Scheme và chọn "Huge APP (3MB No OTA/1MB SPIFFS)". Điều này là bắt buộc vì ví dụ multiple apps sử dụng nhiều không gian flash hơn đáng kể.
  • Nhấp nút Upload trên Arduino IDE để upload code lên ESP32
  • Mở Serial Monitor
  • Kiểm tra kết quả trên Serial Monitor. Nó trông như sau:
COM6
Send
DIYables Bluetooth - ESP32 BLE Multiple Apps Example Waiting for Bluetooth connection...
Autoscroll Show timestamp
Clear output
9600 baud  
Newline  

Ứng Dụng Di Động

  • Cài đặt DIYables Bluetooth App trên smartphone của bạn: Android | iOS
  • Nếu bạn đang sử dụng code ESP32 Classic Bluetooth, bạn cần ghép đôi ESP32 với điện thoại Android trước khi mở app:
    • Đi đến Settings > Bluetooth của điện thoại
    • Đảm bảo Bluetooth đã được bật
    • Điện thoại của bạn sẽ quét các thiết bị khả dụng
    • Tìm và nhấn "ESP32 Multi-App" trong danh sách thiết bị khả dụng
    • Xác nhận yêu cầu ghép đôi (không cần PIN)
    • Đợi cho đến khi hiển thị "Paired" dưới tên thiết bị
  • Nếu bạn đang sử dụng code ESP32 BLE, không cần ghép đôi. Chỉ cần tiến hành bước tiếp theo.
  • Mở DIYables Bluetooth App
  • Khi mở app lần đầu tiên, nó sẽ yêu cầu quyền truy cập. Vui lòng cấp các quyền sau:
    • Quyền Thiết Bị Gần Đó (Android 12+) / Quyền Bluetooth (iOS) - cần thiết để quét và kết nối với thiết bị Bluetooth
    • Quyền Vị Trí (chỉ Android 11 trở xuống) - yêu cầu bởi các phiên bản Android cũ để quét thiết bị BLE
  • Đảm bảo Bluetooth đã được bật trên điện thoại của bạn
  • Trên màn hình chính, nhấn nút Connect. App sẽ quét cả thiết bị BLE và Classic Bluetooth.
diyables Bluetooth app - màn hình chính với nút quét
  • Tìm và nhấn thiết bị của bạn trong kết quả quét để kết nối:
    • Đối với Classic Bluetooth: nhấn "ESP32 Multi-App"
    • Đối với BLE: nhấn "ESP32BLE Multi-App"
  • Khi đã kết nối, app tự động quay lại màn hình chính. Màn hình chính hiển thị tất cả app khả dụng. 9 apps được khởi tạo trong code Arduino sẽ phản hồi và hoạt động — các app khác trên màn hình chính sẽ xuất hiện nhưng sẽ không hoạt động với sketch này.
diyables Bluetooth app - màn hình chính với multiple apps

Lưu Ý: Bạn có thể nhấn biểu tượng cài đặt trên màn hình chính để ẩn/hiện apps trên màn hình chính. Để biết thêm chi tiết, xem Hướng Dẫn Sử Dụng DIYables Bluetooth App.

  • Nhấn một số app sau để mở và tương tác với ESP32: Monitor, Chat, Slider, Joystick, Temperature, Plotter, Table, Analog Gauge, Rotator
  • Tự do chuyển đổi giữa các app — tất cả đều chia sẻ cùng một kết nối Bluetooth

Bây giờ nhìn lại Serial Monitor trên Arduino IDE. Bạn sẽ thấy:

COM6
Send
Bluetooth connected! Monitor: Uptime: 5s | Free heap: 245780 bytes Temperature: 25.3°C Plotter values sent Table values updated Gauge value: 50.0
Autoscroll Show timestamp
Clear output
9600 baud  
Newline  

Tùy Chỉnh Sáng Tạo - Điều Chỉnh Code Cho Dự Án Của Bạn

Cách Multiple Apps Hoạt Động

Mỗi app được tạo như một đối tượng riêng biệt và đăng ký với cùng một Bluetooth server. Chúng chia sẻ kết nối Bluetooth duy nhất nhưng hoạt động độc lập:

// Include tất cả headers của app #include <DIYables_BluetoothMonitor.h> #include <DIYables_BluetoothChat.h> #include <DIYables_BluetoothSlider.h> #include <DIYables_BluetoothJoystick.h> #include <DIYables_BluetoothTemperature.h> #include <DIYables_BluetoothPlotter.h> #include <DIYables_BluetoothTable.h> #include <DIYables_BluetoothAnalogGauge.h> #include <DIYables_BluetoothRotator.h> // Tạo instances của app DIYables_BluetoothMonitor bluetoothMonitor; DIYables_BluetoothChat bluetoothChat; DIYables_BluetoothSlider bluetoothSlider(0, 100, 1); DIYables_BluetoothJoystick bluetoothJoystick; DIYables_BluetoothTemperature bluetoothTemperature(-10.0, 50.0, "°C"); DIYables_BluetoothPlotter bluetoothPlotter; DIYables_BluetoothTable bluetoothTable; DIYables_BluetoothAnalogGauge bluetoothGauge(0.0, 100.0, "km/h"); DIYables_BluetoothRotator bluetoothRotator(ROTATOR_MODE_LIMITED, 0, 180); // Đăng ký tất cả apps với Bluetooth server bluetoothServer.addApp(bluetoothMonitor); bluetoothServer.addApp(bluetoothChat); bluetoothServer.addApp(bluetoothSlider); bluetoothServer.addApp(bluetoothJoystick); bluetoothServer.addApp(bluetoothTemperature); bluetoothServer.addApp(bluetoothPlotter); bluetoothServer.addApp(bluetoothTable); bluetoothServer.addApp(bluetoothGauge); bluetoothServer.addApp(bluetoothRotator);

Thiết Lập Callbacks Cho Mỗi App

Mỗi app có bộ xử lý sự kiện riêng:

void setup() { // Monitor - nhận lệnh text bluetoothMonitor.onMonitorMessage([](String message) { Serial.println("Monitor received: " + message); }); // Chat - nhắn tin hai chiều bluetoothChat.onChatMessage([](String message) { Serial.println("Chat: " + message); bluetoothChat.send("Echo: " + message); }); // Slider - thay đổi giá trị bluetoothSlider.onSliderValue([](int slider1, int slider2) { Serial.println("Slider: " + String(slider1)); }); // Joystick - đầu vào hướng bluetoothJoystick.onJoystickValue([](int x, int y) { Serial.println("Joystick: X=" + String(x) + " Y=" + String(y)); }); // Rotator - thay đổi góc bluetoothRotator.onRotatorAngle([](float angle) { Serial.println("Rotator: " + String(angle) + "°"); }); }

Khoảng Thời Gian Cập Nhật Độc Lập

Mỗi app có thể có thời gian cập nhật riêng:

void loop() { bluetoothServer.loop(); unsigned long now = millis(); // Monitor: mỗi 5 giây static unsigned long lastMonitor = 0; if (now - lastMonitor >= 5000) { lastMonitor = now; bluetoothMonitor.send("Uptime: " + String(now / 1000) + "s"); } // Temperature: mỗi 2 giây static unsigned long lastTemp = 0; if (now - lastTemp >= 2000) { lastTemp = now; bluetoothTemperature.send(readTemperature()); } // Plotter: mỗi 100ms (cập nhật nhanh cho biểu đồ mượt mà) static unsigned long lastPlotter = 0; if (now - lastPlotter >= 100) { lastPlotter = now; bluetoothPlotter.send(readSensorValue()); } // Table: mỗi 5 giây static unsigned long lastTable = 0; if (now - lastTable >= 5000) { lastTable = now; updateTableValues(); } // Gauge: mỗi 3 giây static unsigned long lastGauge = 0; if (now - lastGauge >= 3000) { lastGauge = now; bluetoothGauge.send(readGaugeValue()); } delay(10); }

Chọn Apps Cần Bao Gồm

Bạn không cần bao gồm tất cả 9 apps. Chỉ chọn những cái bạn cần:

// Ví dụ: Chỉ Monitor + Slider + Temperature #include <DIYables_BluetoothMonitor.h> #include <DIYables_BluetoothSlider.h> #include <DIYables_BluetoothTemperature.h> DIYables_BluetoothMonitor bluetoothMonitor; DIYables_BluetoothSlider bluetoothSlider(0, 100, 1); DIYables_BluetoothTemperature bluetoothTemperature(-10.0, 50.0, "°C"); void setup() { // ... Thiết lập Bluetooth server ... bluetoothServer.addApp(bluetoothMonitor); bluetoothServer.addApp(bluetoothSlider); bluetoothServer.addApp(bluetoothTemperature); // Chỉ 3 apps này sẽ xuất hiện trong ứng dụng di động }

Xử Lý Sự Kiện Kết Nối

bluetoothServer.setOnConnected([]() { Serial.println("Bluetooth connected!"); // Gửi giá trị ban đầu cho tất cả apps bluetoothTemperature.send(currentTemperature); bluetoothGauge.send(currentGaugeValue); bluetoothRotator.send(currentAngle); }); bluetoothServer.setOnDisconnected([]() { Serial.println("Bluetooth disconnected!"); });

Cách Sử Dụng Multiple Apps

Chuyển Đổi App

Trong DIYables Bluetooth App:

  • Màn hình chính hiển thị tất cả apps đã đăng ký dưới dạng nút
  • Nhấn bất kỳ app nào để mở nó
  • Sử dụng nút back hoặc nút home để quay lại và chuyển sang app khác
  • Tất cả apps tiếp tục chạy trên ESP32 bất kể app nào đang được hiển thị

Luồng Dữ Liệu

  • Input Apps (Slider, Joystick, Rotator, Chat): Gửi dữ liệu từ điện thoại đến ESP32
  • Output Apps (Monitor, Temperature, Plotter, Table, Gauge): Gửi dữ liệu từ ESP32 đến điện thoại
  • Bidirectional Apps (Chat, Monitor): Có thể gửi và nhận dữ liệu

Ví Dụ Lập Trình

Dashboard Trạm Thời Tiết

// Temperature gauge cho đọc số chính DIYables_BluetoothTemperature bluetoothTemperature(-10.0, 50.0, "°C"); // Table cho tất cả dữ liệu cảm biến (sensor) DIYables_BluetoothTable bluetoothTable; // Plotter cho trực quan hóa xu hướng DIYables_BluetoothPlotter bluetoothPlotter; // Monitor cho tin nhắn log DIYables_BluetoothMonitor bluetoothMonitor; void setup() { // ... Thiết lập Bluetooth ... bluetoothTable.addRow("Temperature"); bluetoothTable.addRow("Humidity"); bluetoothTable.addRow("Pressure"); bluetoothTable.addRow("Wind Speed"); bluetoothTable.addRow("Rain"); bluetoothPlotter.setPlotTitle("Weather Trends"); bluetoothPlotter.setLegendLabels("Temp", "Humidity", "Pressure"); } void loop() { bluetoothServer.loop(); static unsigned long lastUpdate = 0; if (millis() - lastUpdate >= 2000) { lastUpdate = millis(); float temp = readTemperature(); float hum = readHumidity(); float press = readPressure(); // Temperature gauge bluetoothTemperature.send(temp); // Cập nhật table bluetoothTable.sendValueUpdate("Temperature", String(temp, 1) + " °C"); bluetoothTable.sendValueUpdate("Humidity", String(hum, 0) + "%"); bluetoothTable.sendValueUpdate("Pressure", String(press, 0) + " hPa"); // Plotter bluetoothPlotter.send(temp, hum, press / 10.0); // Monitor log bluetoothMonitor.send("Weather update: " + String(temp, 1) + "°C, " + String(hum, 0) + "%, " + String(press, 0) + "hPa"); } delay(10); }

Bộ Điều Khiển Robot

// Joystick cho di chuyển DIYables_BluetoothJoystick bluetoothJoystick; // Slider cho giới hạn tốc độ DIYables_BluetoothSlider bluetoothSlider(0, 100, 5); // Rotator cho góc cánh tay/turret DIYables_BluetoothRotator bluetoothRotator(ROTATOR_MODE_LIMITED, 0, 180); // Monitor cho phản hồi trạng thái DIYables_BluetoothMonitor bluetoothMonitor; // Gauge cho mức pin DIYables_BluetoothAnalogGauge bluetoothGauge(0.0, 100.0, "%"); int maxSpeed = 50; void setup() { // ... Thiết lập Bluetooth ... bluetoothSlider.onSliderValue([](int slider1, int slider2) { maxSpeed = slider1; bluetoothMonitor.send("Max speed set to: " + String(maxSpeed) + "%"); }); bluetoothJoystick.onJoystickValue([](int x, int y) { int leftMotor = constrain(y + x, -maxSpeed, maxSpeed); int rightMotor = constrain(y - x, -maxSpeed, maxSpeed); setMotors(leftMotor, rightMotor); bluetoothMonitor.send("Motors: L=" + String(leftMotor) + " R=" + String(rightMotor)); }); bluetoothRotator.onRotatorAngle([](float angle) { setArmAngle((int)angle); bluetoothMonitor.send("Arm angle: " + String((int)angle) + "°"); }); } void loop() { bluetoothServer.loop(); // Cập nhật mức pin mỗi 10 giây static unsigned long lastBattery = 0; if (millis() - lastBattery >= 10000) { lastBattery = millis(); float battery = readBatteryLevel(); bluetoothGauge.send(battery); } delay(10); }

Hub Tự Động Hóa Nhà

// Chat cho lệnh voice/text DIYables_BluetoothChat bluetoothChat; // Slider cho dimmer/thermostat DIYables_BluetoothSlider bluetoothSlider(0, 100, 1); // Table cho trạng thái thiết bị DIYables_BluetoothTable bluetoothTable; // Temperature cho nhiệt độ phòng DIYables_BluetoothTemperature bluetoothTemperature(10.0, 40.0, "°C"); // Monitor cho log hoạt động DIYables_BluetoothMonitor bluetoothMonitor; void setup() { // ... Thiết lập Bluetooth ... bluetoothTable.addRow("Living Room Light"); bluetoothTable.addRow("Bedroom Light"); bluetoothTable.addRow("AC Status"); bluetoothTable.addRow("Door Lock"); bluetoothTable.addRow("Security"); bluetoothChat.onChatMessage([](String message) { message.toLowerCase(); if (message == "lights on") { setLights(true); bluetoothChat.send("Lights turned ON"); bluetoothTable.sendValueUpdate("Living Room Light", "ON ✅"); } else if (message == "lights off") { setLights(false); bluetoothChat.send("Lights turned OFF"); bluetoothTable.sendValueUpdate("Living Room Light", "OFF ❌"); } else if (message == "status") { bluetoothChat.send("Temp: " + String(readTemperature(), 1) + "°C"); bluetoothChat.send("All systems normal"); } bluetoothMonitor.send("[CMD] " + message); }); bluetoothSlider.onSliderValue([](int slider1, int slider2) { setDimmer(slider1); bluetoothMonitor.send("[DIMMER] Set to " + String(slider1) + "%"); }); }

Kỹ Thuật Lập Trình Nâng Cao

Giao Tiếp Giữa Các App

Sử dụng dữ liệu từ một app để cập nhật app khác:

// Joystick điều khiển di chuyển, plotter hiển thị quỹ đạo float posX = 0, posY = 0; bluetoothJoystick.onJoystickValue([](int x, int y) { posX += x * 0.1; posY += y * 0.1; // Vẽ vị trí trên plotter bluetoothPlotter.send(posX, posY); // Hiển thị trên table bluetoothTable.sendValueUpdate("Position X", String(posX, 1)); bluetoothTable.sendValueUpdate("Position Y", String(posY, 1)); // Log trong monitor bluetoothMonitor.send("Pos: (" + String(posX, 1) + ", " + String(posY, 1) + ")"); });