Arduino Nano ESP32 - DIYables Bluetooth App Multiple Apps

Overview

This example demonstrates running 9 Bluetooth app widgets simultaneously on a single Arduino Nano ESP32 using BLE. Using BLE (Bluetooth Low Energy), the sketch combines Monitor, Chat, Slider, Joystick, Temperature, Plotter, Table, Analog Gauge, and Rotator into one sketch. All widgets share a single BLE connection and can interact with each other. Suitable for comprehensive dashboards, complex IoT projects, and learning multiple widget types at once.

Note: The Arduino Nano ESP32 supports BLE only — Classic Bluetooth is not supported. The DIYables Bluetooth App works on both Android and iOS with BLE.

Arduino Nano ESP32 Bluetooth Multiple Apps Example - All-in-One BLE Tutorial

Features

  • 9 Widgets in One: Monitor, Chat, Slider, Joystick, Temperature, Plotter, Table, Analog Gauge, Rotator
  • Cross-Widget Interaction: Slider updates gauge and table; joystick updates table, etc.
  • Single BLE Connection: All widgets share one connection efficiently
  • Real-Time Updates: Each widget updates at its own configured interval
  • Comprehensive Dashboard: View all data from a single device
  • Android & iOS Support: BLE is compatible with both platforms
  • No Pairing Required: BLE connects without manual pairing

Included Apps

AppDescriptionUpdate Interval
MonitorText-based status display5 seconds
ChatTwo-way text messagingOn demand
SliderValue control (0-255)On change
Joystick2D position controlOn change
TemperatureTemperature gauge (-10 to 50°C)2 seconds
PlotterReal-time data graph100ms
TableStructured data (10 rows)5 seconds
Analog GaugeDial-style gauge (0-100%)3 seconds
RotatorAngle control (continuous)On change

Hardware Preparation

1×Arduino Nano ESP32
1×USB Cable Type-A to Type-C (for USB-A PC)
1×USB Cable Type-C to Type-C (for USB-C PC)
1×Recommended: Screw Terminal Expansion Board for Arduino Nano
1×Recommended: Breakout Expansion Board for Arduino Nano
1×Recommended: Power Splitter for Arduino Nano ESP32

Or you can buy the following kits:

1×DIYables Sensor Kit (30 sensors/displays)
1×DIYables Sensor Kit (18 sensors/displays)
Disclosure: Some of the links provided in this section are Amazon affiliate links. We may receive a commission for any purchases made through these links at no additional cost to you.
Additionally, some of these links are for products from our own brand, DIYables .

Arduino Nano ESP32 Code

Detailed Instructions

  • New to the Arduino Nano ESP32? Start with the Arduino Nano ESP32 getting started guide.
  • Connect the Arduino Nano ESP32 to your computer via USB.
  • Open Arduino IDE.
  • Select the Arduino Nano ESP32 board and the correct COM port.
  • Click the Libraries icon in the left sidebar.
  • Search for "DIYables Bluetooth" and select the DIYables Bluetooth library by DIYables.
  • Click Install.
Arduino Nano ESP32 DIYables Bluetooth library
  • When prompted to install dependencies, click Install All.
Arduino Nano ESP32 DIYables Bluetooth dependency

BLE Code

  • In Arduino IDE, open File Examples DIYables Bluetooth ArduinoBLE_MultipleApps, or paste the code into the editor.
/* * 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 * * 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); }
  • Click Upload to flash the sketch to the board.
  • Open the Serial Monitor.
  • The Serial Monitor output should look like:
COM6
Send
DIYables Bluetooth - Multiple Apps Example Waiting for Bluetooth connection...
Autoscroll Show timestamp
Clear output
9600 baud  
Newline  

Mobile App

  • Install the DIYables Bluetooth App on your smartphone: Android | iOS

Note: The DIYables Bluetooth App works on both Android and iOS with BLE. No manual pairing is required.

  • Launch the DIYables Bluetooth App.
  • On first launch, grant the following permissions:
    • Nearby Devices (Android 12+) / Bluetooth (iOS) — required to scan and connect to Bluetooth devices
    • Location (Android 11 and below only) — required by older Android versions to scan for BLE
  • Ensure Bluetooth is enabled on your device.
  • Tap Connect on the home screen. The app will scan for BLE devices.
DIYables Bluetooth App - Home Screen with Scan Button
  • Tap "DIYables Multi-App" in the scan results.
  • After connecting, the home screen displays all available app widgets. The 9 widgets initialised in the Arduino sketch will respond and function — other widgets on the home screen will appear but will not work with this sketch.
DIYables Bluetooth App - Home Screen with Multiple Apps

Tap the settings icon on the home screen to show or hide apps. See the DIYables Bluetooth App User Manual for details.

  • Open any of the following widgets to interact with the Arduino: Monitor, Chat, Slider, Joystick, Temperature, Plotter, Table, Analog Gauge, Rotator.
  • Switch between widgets freely — all share the same BLE connection.

Now look back at the Serial Monitor on Arduino IDE. You will see:

COM6
Send
Bluetooth connected! Monitor: System running, uptime: 5s Chat message: Hello Slider value: 128 Joystick: X=0.50, Y=-0.30 Temperature: 22.50 °C
Autoscroll Show timestamp
Clear output
9600 baud  
Newline  

How It Works

App Initialisation

Each widget is created with its own configuration and callbacks:

// All apps share the same Bluetooth server DIYables_BluetoothServer bluetoothServer(bluetooth); // Create individual apps DIYables_BluetoothMonitor bluetoothMonitor(bluetoothServer); DIYables_BluetoothChat bluetoothChat(bluetoothServer); DIYables_BluetoothSlider bluetoothSlider(bluetoothServer, 0, 255, 1); DIYables_BluetoothJoystick bluetoothJoystick(bluetoothServer, false, 5); DIYables_BluetoothTemperature bluetoothTemp(bluetoothServer, -10.0, 50.0, "°C"); DIYables_BluetoothPlotter bluetoothPlotter(bluetoothServer); DIYables_BluetoothTable bluetoothTable(bluetoothServer); DIYables_BluetoothAnalogGauge bluetoothGauge(bluetoothServer, 0.0, 100.0, "%"); DIYables_BluetoothRotator bluetoothRotator(bluetoothServer, ROTATOR_MODE_CONTINUOUS);

Cross-App Interaction

Apps can interact with each other. When one widget receives input, it can update other widgets:

// Slider updates gauge and table bluetoothSlider.onSliderValue([](int value) { float percent = value * 100.0 / 255.0; bluetoothGauge.send(percent); bluetoothTable.sendValueUpdate("Slider 1", String(value)); }); // Joystick updates table bluetoothJoystick.onJoystickValue([](float x, float y) { bluetoothTable.sendValueUpdate("Joystick X", String(x, 2)); bluetoothTable.sendValueUpdate("Joystick Y", String(y, 2)); }); // Rotator updates table bluetoothRotator.onRotatorAngle([](float angle) { bluetoothTable.sendValueUpdate("Rotator Angle", String(angle, 1) + "°"); });

Update Timing

Each app has its own update interval to balance responsiveness with BLE bandwidth:

void loop() { bluetoothServer.loop(); unsigned long now = millis(); // Plotter: fastest updates (100ms) if (now - lastPlotterTime >= 100) { ... } // Temperature: every 2 seconds if (now - lastTempTime >= 2000) { ... } // Gauge: every 3 seconds if (now - lastGaugeTime >= 3000) { ... } // Monitor, Table: every 5 seconds if (now - lastMonitorTime >= 5000) { ... } if (now - lastTableTime >= 5000) { ... } }

Table Structure

The example creates a table with 10 rows showing data from all apps:

RowLabelDescription
0StatusConnection/running status
1UptimeTime since boot
2Slider 1Current slider value
3Slider 2Second slider value
4Joystick XJoystick X position
5Joystick YJoystick Y position
6TemperatureCurrent temperature
7Gauge ValueCurrent gauge percentage
8Rotator AngleCurrent rotation angle
9MessagesChat message count

Creative Customization - Adapt the Code to Your Project

Add or Remove Widgets

Include only the widgets required for your project:

// Minimal setup: just Monitor and Slider DIYables_BluetoothMonitor bluetoothMonitor(bluetoothServer); DIYables_BluetoothSlider bluetoothSlider(bluetoothServer, 0, 100, 1); // That's it! The app will only show these two

Custom Cross-App Logic

// Example: Temperature alarm via Monitor void checkTemperatureAlarm(float temp) { if (temp > 40.0) { bluetoothMonitor.send("⚠️ HIGH TEMP ALERT: " + String(temp, 1) + "°C"); bluetoothChat.send("Temperature alarm triggered!"); } }

Troubleshooting

Common Issues

1. Device not visible in the app

  • Confirm the board is powered on and the sketch is uploaded
  • Verify Bluetooth is enabled on your phone
  • On Android 11 and below, enable Location services as well

2. Some widgets not showing in the menu

  • All initialised widgets will appear automatically
  • Verify each widget object is properly created
  • The app discovers available widgets from the Arduino

3. Updates seem slow

  • Each widget has different update intervals by design
  • BLE bandwidth is limited; too many rapid updates can cause congestion
  • Reduce update frequency for widgets that do not require real-time data

4. Cross-widget updates not working

  • Verify the callback functions are properly registered
  • Check that table row names match exactly (case-sensitive)
  • Confirm the target widget object is accessible within the callback scope

5. Memory issues or crashes

  • Running 9 widgets uses significant memory
  • Remove unused widgets to free resources
  • Reduce table row count if needed

6. Upload fails or board not recognized

  • Install the latest Arduino Nano ESP32 board package via Board Manager
  • Try a different USB cable or port

Project Ideas

  • Comprehensive IoT dashboard
  • Robot control panel (joystick + monitor + sliders)
  • Weather station (temperature + gauge + plotter + table)
  • Home automation hub (sliders + pins + monitor + chat)
  • STEM learning platform (all apps for experimentation)

Next Steps

After completing the Multiple Apps example, explore each individual tutorial for deeper understanding:

  1. Bluetooth Chat — Messaging details
  2. Bluetooth Slider — Value control details
  3. Bluetooth Plotter — Data visualisation details
  4. Bluetooth RTC — Time synchronisation using the built-in hardware RTC

Support

For additional help:

※ OUR MESSAGES

  • As freelancers, We are AVAILABLE for HIRE. See how to outsource your project to us
  • Please feel free to share the link of this tutorial. However, Please do not use our content on any other websites. We invested a lot of effort and time to create the content, please respect our work!