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) và 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:
| 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) | | |
Thực hiện theo hướng dẫn từng bước:
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.
> 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:
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.
#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>
DIYables_Esp32Bluetooth bluetooth("ESP32 Multi-App");
DIYables_BluetoothServer bluetoothServer(bluetooth);
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);
const int LED_PIN = 2;
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;
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");
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, 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("Free Heap");
setupCallbacks();
Serial.println("Waiting for Bluetooth connection...");
}
void setupCallbacks() {
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);
});
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)");
}
});
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");
}
});
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("Free Heap", String(ESP.getFreeHeap()) + " bytes");
}
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("Free Heap", String(ESP.getFreeHeap()) + " bytes");
}
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:
DIYables Bluetooth - ESP32 Multiple Apps Example
Waiting for Bluetooth connection...
#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>
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";
DIYables_Esp32BLE bluetooth(DEVICE_NAME, SERVICE_UUID, TX_UUID, RX_UUID);
DIYables_BluetoothServer bluetoothServer(bluetooth);
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);
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;
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:
DIYables Bluetooth - ESP32 BLE Multiple Apps Example
Waiting for Bluetooth connection...
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.

Tìm và nhấn thiết bị của bạn trong kết quả quét để kết nối:
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.
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:
Bluetooth connected!
Monitor: Uptime: 5s | Free heap: 245780 bytes
Temperature: 25.3°C
Plotter values sent
Table values updated
Gauge value: 50.0
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 <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>
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);
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);
Mỗi app có bộ xử lý sự kiện riêng:
void setup() {
bluetoothMonitor.onMonitorMessage([](String message) {
Serial.println("Monitor received: " + message);
});
bluetoothChat.onChatMessage([](String message) {
Serial.println("Chat: " + message);
bluetoothChat.send("Echo: " + message);
});
bluetoothSlider.onSliderValue([](int slider1, int slider2) {
Serial.println("Slider: " + String(slider1));
});
bluetoothJoystick.onJoystickValue([](int x, int y) {
Serial.println("Joystick: X=" + String(x) + " Y=" + String(y));
});
bluetoothRotator.onRotatorAngle([](float angle) {
Serial.println("Rotator: " + String(angle) + "°");
});
}
Mỗi app có thể có thời gian cập nhật riêng:
void loop() {
bluetoothServer.loop();
unsigned long now = millis();
static unsigned long lastMonitor = 0;
if (now - lastMonitor >= 5000) {
lastMonitor = now;
bluetoothMonitor.send("Uptime: " + String(now / 1000) + "s");
}
static unsigned long lastTemp = 0;
if (now - lastTemp >= 2000) {
lastTemp = now;
bluetoothTemperature.send(readTemperature());
}
static unsigned long lastPlotter = 0;
if (now - lastPlotter >= 100) {
lastPlotter = now;
bluetoothPlotter.send(readSensorValue());
}
static unsigned long lastTable = 0;
if (now - lastTable >= 5000) {
lastTable = now;
updateTableValues();
}
static unsigned long lastGauge = 0;
if (now - lastGauge >= 3000) {
lastGauge = now;
bluetoothGauge.send(readGaugeValue());
}
delay(10);
}
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:
#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() {
bluetoothServer.addApp(bluetoothMonitor);
bluetoothServer.addApp(bluetoothSlider);
bluetoothServer.addApp(bluetoothTemperature);
}
bluetoothServer.setOnConnected([]() {
Serial.println("Bluetooth connected!");
bluetoothTemperature.send(currentTemperature);
bluetoothGauge.send(currentGaugeValue);
bluetoothRotator.send(currentAngle);
});
bluetoothServer.setOnDisconnected([]() {
Serial.println("Bluetooth disconnected!");
});
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ị
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
DIYables_BluetoothTemperature bluetoothTemperature(-10.0, 50.0, "°C");
DIYables_BluetoothTable bluetoothTable;
DIYables_BluetoothPlotter bluetoothPlotter;
DIYables_BluetoothMonitor bluetoothMonitor;
void setup() {
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();
bluetoothTemperature.send(temp);
bluetoothTable.sendValueUpdate("Temperature", String(temp, 1) + " °C");
bluetoothTable.sendValueUpdate("Humidity", String(hum, 0) + "%");
bluetoothTable.sendValueUpdate("Pressure", String(press, 0) + " hPa");
bluetoothPlotter.send(temp, hum, press / 10.0);
bluetoothMonitor.send("Weather update: " + String(temp, 1) + "°C, " + String(hum, 0) + "%, " + String(press, 0) + "hPa");
}
delay(10);
}
DIYables_BluetoothJoystick bluetoothJoystick;
DIYables_BluetoothSlider bluetoothSlider(0, 100, 5);
DIYables_BluetoothRotator bluetoothRotator(ROTATOR_MODE_LIMITED, 0, 180);
DIYables_BluetoothMonitor bluetoothMonitor;
DIYables_BluetoothAnalogGauge bluetoothGauge(0.0, 100.0, "%");
int maxSpeed = 50;
void setup() {
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();
static unsigned long lastBattery = 0;
if (millis() - lastBattery >= 10000) {
lastBattery = millis();
float battery = readBatteryLevel();
bluetoothGauge.send(battery);
}
delay(10);
}
DIYables_BluetoothChat bluetoothChat;
DIYables_BluetoothSlider bluetoothSlider(0, 100, 1);
DIYables_BluetoothTable bluetoothTable;
DIYables_BluetoothTemperature bluetoothTemperature(10.0, 40.0, "°C");
DIYables_BluetoothMonitor bluetoothMonitor;
void setup() {
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) + "%");
});
}
Sử dụng dữ liệu từ một app để cập nhật app khác:
float posX = 0, posY = 0;
bluetoothJoystick.onJoystickValue([](int x, int y) {
posX += x * 0.1;
posY += y * 0.1;
bluetoothPlotter.send(posX, posY);
bluetoothTable.sendValueUpdate("Position X", String(posX, 1));
bluetoothTable.sendValueUpdate("Position Y", String(posY, 1));
bluetoothMonitor.send("Pos: (" + String(posX, 1) + ", " + String(posY, 1) + ")");
});