ESP32 S3 UNO - DIYables Bluetooth App Chat

Overview

This ESP32 S3 Uno Bluetooth chat tutorial shows you how to send and receive text messages between the ESP32 S3 Uno form board and your smartphone over BLE (Bluetooth Low Energy). You use the free DIYables Bluetooth App as a wireless chat window and serial monitor. You can type commands on your phone, get replies from the board, and forward text from the Arduino IDE Serial Monitor to the app.

In this tutorial, you will:

  1. Install the DIYables Bluetooth library and upload the BLE chat code
  2. Connect the DIYables Bluetooth App to your board without pairing
  3. Handle text commands like ping, status and LED ON
  4. Build a Serial-to-Bluetooth bridge for wireless debugging
  5. Control LEDs, relays, a servo and a motor with chat commands

BLE only, no Classic Bluetooth

The ESP32 S3 Uno supports BLE only. It has no Classic Bluetooth radio, so the BluetoothSerial library and the Classic Bluetooth chat example (Esp32Bluetooth_Chat) do not work on this board. That is why this page uses only the BLE example. The good news: BLE works with the app on both Android and iOS, and you do not need to pair the board in your phone settings.

ESP32 S3 Uno Bluetooth chat

Features

A chat screen is one of the most flexible ways to talk to your project. Any word you type can become a command, and the board can answer in plain text.

Two-way messaging

You send text from the phone, and the board sends text back, in real time.

Echo mode

The example code echoes every message back to the app, so you can see right away that the link works.

Command handling

The sketch already knows four commands: ping, status, time and heap. You can add your own in a few lines.

Periodic messages

The board sends a status update every 10 seconds while the phone is connected.

Serial-to-Bluetooth bridge

Text you type in the Arduino IDE Serial Monitor goes straight to the app.

Android and iOS

BLE works on both phone systems, and it uses little power.

Hardware Preparation

1×ESP32 S3 Uno-form Board
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×LED Kit
1×LED (red)
1×LED Module
1×Alternatively, Button and LED Kit
1×220Ω Resistor
1×Breadboard
1×Jumper Wires

Or you can buy the following kits:

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 .

Buy Note: Use the LED Module for easier wiring. It includes an integrated resistor.

The basic chat example needs only the board and a USB cable. The LED and resistor are for the video demo code, which turns an LED on and off with chat commands.

ESP32 S3 Uno Pinout

The basic chat example needs no wiring, because the BLE radio is inside the board. When you add LEDs, relays or sensors later, use the pinout diagram below to find the Uno header pins (D2, D13, A0…) and their GPIO numbers.

ESP32 S3 Uno pinout diagram

ESP32 S3 Uno Code

You need the DIYables Bluetooth library before you upload the code. It handles all the BLE work, so your sketch only deals with the chat messages.

Detailed Instructions

  1. New to the ESP32 S3 Uno? Follow ESP32 S3 Uno - Getting Started first.
  2. Connect the board to your computer with a USB Type-C cable.
  3. Open Arduino IDE, choose the ESP32S3 Dev Module board and the correct COM port.
  4. Open the Library Manager by clicking the Libraries icon on the left bar of the Arduino IDE.
  5. Search for "DIYables Bluetooth" and find the DIYables Bluetooth library by DIYables.
  6. Install it by clicking the Install button.
  • Search for DIYables Bluetooth created by DIYables and click the Install button.
∞
Newbiely | Arduino IDE 2.3.8
──
☐
✕
File
Edit
Sketch
Tools
Help
ESP32S3 Dev Module
Library Manager
Type:
All
Topic:
All
DIYables Bluetooth by DIYables
A comprehensive Bluetooth library that connects Arduino to the DIYables Bluetooth STEM mobile app (Android & iOS) via BLE and Classic Bluetooth. Features 11 built-in apps: Monitor, Chat, Digital Pin Control, Joystick, Sliders, Plotter, Analog Gauge, Rotator, Temperature, RTC, and Data Table. Clean architecture with platform abstraction. Works with Arduino Uno R4 WiFi, Arduino Nano 33 BLE, ESP32, and other BLE-capable boards. No mobile coding required. More info
1.0.1
INSTALL
Newbiely.ino
···
1 void setup() {
Output
Serial Monitor
Ln 1, Col 1
ESP32S3 Dev Module on COM15
1
  1. Install the dependencies. When the IDE asks about other required libraries, click Install All.
  2. Tip: Bluetooth sketches are big. If the upload fails with a "sketch too big" error, open Tools > Partition Scheme and pick Huge APP (3MB No OTA/1MB SPIFFS).

ESP32 S3 Uno BLE Code (works with the app on Android and iOS)

This sketch starts a BLE server named ESP32BLE_Chat, echoes every message it gets, and answers four commands. It also sends a status message every 10 seconds and forwards any text you type in the Serial Monitor to the app.

  1. Open the example: in the Arduino IDE, go to File Examples DIYables Bluetooth Esp32BLE_Chat, or copy the code below into the editor.
/* * This ESP32 S3 Uno code was developed by newbiely.com * * This ESP32 S3 Uno code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/esp32-s3-uno/esp32-s3-uno-diyables-bluetooth-app-chat */ /* * DIYables Bluetooth Library - ESP32 S3 Uno BLE Chat Example * Works with DIYables Bluetooth STEM app on Android and iOS * * This example demonstrates the Bluetooth Chat feature: * - Two-way text messaging via Bluetooth * - Receive messages from mobile app * - Send messages to mobile app * * Board: ESP32 S3 Uno form board (BLE only, no Classic Bluetooth) * Arduino IDE board: ESP32S3 Dev Module * * 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 S3 Uno * 2. Open Serial Monitor (115200 baud) to see connection status and messages * 3. Use DIYables Bluetooth App to connect and chat * * Tutorial: https://diyables.io/bluetooth-app * Author: DIYables */ #include <DIYables_BluetoothServer.h> #include <DIYables_BluetoothChat.h> #include <platforms/DIYables_Esp32BLE.h> // BLE Configuration const char* DEVICE_NAME = "ESP32BLE_Chat"; 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 Chat app instance DIYables_BluetoothChat bluetoothChat; // Variables for periodic messages unsigned long lastMessageTime = 0; const unsigned long MESSAGE_INTERVAL = 10000; // Send message every 10 seconds int messageCount = 0; void setup() { Serial.begin(115200); delay(1000); Serial.println("DIYables Bluetooth - ESP32 S3 Uno BLE Chat Example"); // Initialize Bluetooth server with platform-specific implementation bluetoothServer.begin(); // Add chat app to server bluetoothServer.addApp(&bluetoothChat); // Set up connection event callbacks bluetoothServer.setOnConnected([]() { Serial.println("Bluetooth connected!"); bluetoothChat.send("Hello! ESP32 S3 Uno BLE is ready to chat."); }); bluetoothServer.setOnDisconnected([]() { Serial.println("Bluetooth disconnected!"); messageCount = 0; }); // Set up callback for received chat messages bluetoothChat.onChatMessage([](const String& message) { Serial.print("Received: "); Serial.println(message); // Echo the message back String response = "Echo: "; response += message; bluetoothChat.send(response); // Custom command handling if (message.equalsIgnoreCase("ping")) { bluetoothChat.send("pong!"); } else if (message.equalsIgnoreCase("status")) { bluetoothChat.send("ESP32 S3 Uno BLE is running normally"); } else if (message.equalsIgnoreCase("time")) { String timeMsg = "Uptime: "; timeMsg += String(millis() / 1000); timeMsg += " seconds"; bluetoothChat.send(timeMsg); } else if (message.equalsIgnoreCase("heap")) { String heapMsg = "Free heap: "; heapMsg += String(ESP.getFreeHeap()); heapMsg += " bytes"; bluetoothChat.send(heapMsg); } }); Serial.println("Waiting for Bluetooth connection..."); Serial.println("Type 'ping', 'status', 'time', or 'heap' in the app to test commands"); } void loop() { bluetoothServer.loop(); if (bluetooth.isConnected() && millis() - lastMessageTime >= MESSAGE_INTERVAL) { lastMessageTime = millis(); messageCount++; String statusMsg = "Status update #"; statusMsg += String(messageCount); statusMsg += " - All systems operational"; bluetoothChat.send(statusMsg); Serial.print("Sent: "); Serial.println(statusMsg); } if (Serial.available()) { String serialMsg = Serial.readStringUntil('\n'); serialMsg.trim(); if (serialMsg.length() > 0 && bluetooth.isConnected()) { bluetoothChat.send(serialMsg); Serial.print("Sent from Serial: "); Serial.println(serialMsg); } } delay(10); }
  1. Upload the code by clicking the Upload button.
  2. Open the Serial Monitor and set the baud rate to 115200.
  3. Check the output. You should see something like this:
∞
Newbiely | Arduino IDE 2.3.8
──
☐
✕
File
Edit
Sketch
Tools
Help
ESP32S3 Dev Module
Newbiely.ino
···
8 Serial.println("Hello World!");
Output
Serial Monitor
Message (Enter to send message to 'ESP32S3 Dev Module' on 'COM15')
New Line
9600 baud
DIYables Bluetooth - ESP32 S3 Uno BLE Chat Example Waiting for Bluetooth connection... Type 'ping', 'status', 'time', or 'heap' in the app to test commands
Ln 11, Col 1
ESP32S3 Dev Module on COM15
2
  1. Tip: If the Serial Monitor stays empty, press the RESET button on the board once. The first lines are printed only at startup.

Mobile App

The app is your chat window. It finds the board over BLE and shows every message in a list, like a messenger app.

  1. Install the DIYables Bluetooth App on your phone: Android | iOS
  2. Skip pairing. The ESP32 S3 Uno uses BLE, so you do not pair it in the phone settings. The app connects directly.
  3. Open the app and grant the permissions it asks for the first time:
  • Nearby Devices permission (Android 12+) or Bluetooth permission (iOS). The app needs it to scan and connect.
  • Location permission (Android 11 or older only). Older Android versions need it to scan for BLE devices.
  1. Turn on Bluetooth on your phone.
  2. Tap Connect. On the home screen, tap the Connect button. The app scans for nearby devices.
DIYables Bluetooth App - Home Screen with Scan Button
  1. Pick your board. Tap "ESP32BLE_Chat" in the scan results.
  2. Open the Chat app. After connecting, the app goes back to the home screen. Tap Chat in the app menu.
DIYables Bluetooth App - Home Screen with Chat App

※ NOTE THAT:

You can tap the settings icon on the home screen to show or hide apps. For more details, read the DIYables Bluetooth App User Manual.

  1. Send a message. Type some text in the input box and tap Send.
DIYables Bluetooth App - Chat Screen

Now look at the Serial Monitor in the Arduino IDE. You should see lines like these:

∞
Newbiely | Arduino IDE 2.3.8
──
☐
✕
File
Edit
Sketch
Tools
Help
ESP32S3 Dev Module
Newbiely.ino
···
8 Serial.println("Hello World!");
Output
Serial Monitor
Message (Enter to send message to 'ESP32S3 Dev Module' on 'COM15')
New Line
9600 baud
Bluetooth connected! Received: Hello Received: ping Received: status Sent: Status update #1 - All systems operational
Ln 11, Col 1
ESP32S3 Dev Module on COM15
2

Try the built-in commands ping, status, time and heap in the app. You can also type text in the Serial Monitor and press Enter, and it shows up in the app chat.

Creative Customization - Adapt the Code to Your Project

The example is only a starting point. The snippets below show the main building blocks, so you can turn the chat into a control panel for your own project.

Receive Messages from App

The onChatMessage() callback runs every time a message arrives from the app. Inside it, you compare the text with your own command words and act on them. Here, an LED on D13 (GPIO12) turns on and off:

bluetoothChat.onChatMessage([](const String& message) { Serial.print("Received: "); Serial.println(message); // Process the message if (message == "LED_ON") { digitalWrite(12, HIGH); // D13 (GPIO12) bluetoothChat.send("LED turned ON"); } else if (message == "LED_OFF") { digitalWrite(12, LOW); // D13 (GPIO12) bluetoothChat.send("LED turned OFF"); } else { bluetoothChat.send("Unknown command: " + message); } });

Add more else if blocks for more commands. For example, use RELAY_ON / RELAY_OFF for a relay, or READ to take a sensor reading. Remember to call pinMode(12, OUTPUT); in setup() first.

Send Messages to App

The board can send text to the app at any time, not only as a reply. This is handy for alerts and sensor values.

// Send a simple text message bluetoothChat.send("Hello from ESP32 S3 Uno!"); // Send sensor data as text float temperature = 25.3; bluetoothChat.send("Temperature: " + String(temperature) + "°C"); // Send status updates bluetoothChat.send("System ready");

Handle Connection Events

You can run your own code when the phone connects or disconnects. A welcome message on connect tells the user that the board is ready.

// Called when the app connects to the ESP32 S3 Uno bluetoothServer.setOnConnected([]() { Serial.println("Bluetooth connected!"); bluetoothChat.send("Welcome! ESP32 S3 Uno is ready to chat."); }); // Called when the app disconnects from the ESP32 S3 Uno bluetoothServer.setOnDisconnected([]() { Serial.println("Bluetooth disconnected!"); }); // Check connection status anywhere in your code if (bluetoothServer.isConnected()) { bluetoothChat.send("Still connected!"); }

Serial-to-Bluetooth Bridge

This loop reads one line from the Serial Monitor and sends it to the app. It is a simple way to debug a project without a USB cable on the phone side.

void loop() { bluetoothServer.loop(); // Read from Serial and send to Bluetooth if (Serial.available()) { String serialMsg = Serial.readStringUntil('\n'); serialMsg.trim(); if (serialMsg.length() > 0 && bluetooth.isConnected()) { bluetoothChat.send(serialMsg); Serial.print("Sent from Serial: "); Serial.println(serialMsg); } } delay(10); }

How to Use the Chat

The chat screen looks like a normal messenger. Knowing its parts and the built-in commands helps you test the link fast.

App Interface Controls

Message list

A scrollable list shows the messages you sent and the ones you received.

Text input

You type your message in the box at the bottom.

Send button

Tap it to send the typed message to the board.

Auto-scroll

The list jumps to the newest message by itself.

Built-in Commands

The example code answers these commands (upper or lower case both work):

Command Reply
ping pong!
status ESP32 S3 Uno BLE is running normally
time Uptime in seconds
heap Free heap memory in bytes

Programming Examples

These examples are complete callback blocks you can drop into the chat sketch. Each one shows a common use of chat commands. All pins are Uno header pins, written as GPIO numbers in the code.

Basic Echo Chat

The simplest possible chat: the board sends back every message it gets. Use it to check that both directions work.

void setup() { // Set up chat callback bluetoothChat.onChatMessage([](const String& message) { Serial.print("Received: "); Serial.println(message); // Echo the message back bluetoothChat.send("Echo: " + message); }); }

Command-Based LED Control

This example drives one LED on D13 and an RGB LED on D2, D3 and D4. Type HELP in the app to get the list of commands.

const int LED_PIN = 12; // D13 (GPIO12), external LED const int RED_PIN = 18; // D2 (GPIO18) const int GREEN_PIN = 17; // D3 (GPIO17) const int BLUE_PIN = 19; // D4 (GPIO19) void setup() { pinMode(LED_PIN, OUTPUT); pinMode(RED_PIN, OUTPUT); pinMode(GREEN_PIN, OUTPUT); pinMode(BLUE_PIN, OUTPUT); bluetoothChat.onChatMessage([](const String& message) { String cmd = message; cmd.toUpperCase(); if (cmd == "LED ON") { digitalWrite(LED_PIN, HIGH); bluetoothChat.send("LED is now ON"); } else if (cmd == "LED OFF") { digitalWrite(LED_PIN, LOW); bluetoothChat.send("LED is now OFF"); } else if (cmd == "RED") { digitalWrite(RED_PIN, HIGH); digitalWrite(GREEN_PIN, LOW); digitalWrite(BLUE_PIN, LOW); bluetoothChat.send("Red LED activated"); } else if (cmd == "GREEN") { digitalWrite(RED_PIN, LOW); digitalWrite(GREEN_PIN, HIGH); digitalWrite(BLUE_PIN, LOW); bluetoothChat.send("Green LED activated"); } else if (cmd == "BLUE") { digitalWrite(RED_PIN, LOW); digitalWrite(GREEN_PIN, LOW); digitalWrite(BLUE_PIN, HIGH); bluetoothChat.send("Blue LED activated"); } else if (cmd == "OFF") { digitalWrite(RED_PIN, LOW); digitalWrite(GREEN_PIN, LOW); digitalWrite(BLUE_PIN, LOW); bluetoothChat.send("All LEDs off"); } else if (cmd == "HELP") { bluetoothChat.send("Commands: LED ON, LED OFF, RED, GREEN, BLUE, OFF, HELP"); } else { bluetoothChat.send("Unknown command. Type HELP for list."); } }); }

Sensor Query System

Here the phone asks for sensor values on demand. A DHT22 sensor on D2 (GPIO18) gives temperature and humidity. Power the DHT22 from 3.3V so its data line stays at a safe level for the ESP32 S3 Uno.

#include <DHT.h> DHT dht(18, DHT22); // DHT22 data pin on D2 (GPIO18) void setup() { dht.begin(); bluetoothChat.onChatMessage([](const String& message) { String cmd = message; cmd.toUpperCase(); if (cmd == "TEMP") { float temp = dht.readTemperature(); if (!isnan(temp)) { bluetoothChat.send("Temperature: " + String(temp, 1) + " °C"); } else { bluetoothChat.send("Error reading temperature sensor"); } } else if (cmd == "HUMIDITY") { float hum = dht.readHumidity(); if (!isnan(hum)) { bluetoothChat.send("Humidity: " + String(hum, 1) + " %"); } else { bluetoothChat.send("Error reading humidity sensor"); } } else if (cmd == "ALL") { float temp = dht.readTemperature(); float hum = dht.readHumidity(); bluetoothChat.send("Temp: " + String(temp, 1) + "°C | Humidity: " + String(hum, 1) + "%"); } else if (cmd == "HELP") { bluetoothChat.send("Commands: TEMP, HUMIDITY, ALL, HELP"); } }); }

Relay Control with Confirmation

Two relays on D7 (GPIO14) and D8 (GPIO21) switch on and off by chat. The board always replies, so you know the command arrived.

const int RELAY_1 = 14; // D7 (GPIO14) const int RELAY_2 = 21; // D8 (GPIO21) bool relay1State = false; bool relay2State = false; void setup() { pinMode(RELAY_1, OUTPUT); pinMode(RELAY_2, OUTPUT); digitalWrite(RELAY_1, LOW); digitalWrite(RELAY_2, LOW); bluetoothChat.onChatMessage([](const String& message) { String cmd = message; cmd.toUpperCase(); if (cmd == "R1 ON") { digitalWrite(RELAY_1, HIGH); relay1State = true; bluetoothChat.send("Relay 1: ON"); } else if (cmd == "R1 OFF") { digitalWrite(RELAY_1, LOW); relay1State = false; bluetoothChat.send("Relay 1: OFF"); } else if (cmd == "R2 ON") { digitalWrite(RELAY_2, HIGH); relay2State = true; bluetoothChat.send("Relay 2: ON"); } else if (cmd == "R2 OFF") { digitalWrite(RELAY_2, LOW); relay2State = false; bluetoothChat.send("Relay 2: OFF"); } else if (cmd == "STATUS") { bluetoothChat.send("Relay 1: " + String(relay1State ? "ON" : "OFF")); bluetoothChat.send("Relay 2: " + String(relay2State ? "ON" : "OFF")); } else if (cmd == "HELP") { bluetoothChat.send("Commands: R1 ON, R1 OFF, R2 ON, R2 OFF, STATUS, HELP"); } }); }

※ NOTE THAT:

Use a relay module that accepts a 3.3V control signal. Many 5V relay modules work with 3.3V input, but check the module first.

Advanced Programming Techniques

Once the basics work, you can make the chat smarter. These techniques add timed messages, logging and commands with arguments.

Periodic Status Updates

The board sends a status line every 10 seconds, but only while the phone is connected. It also reports uptime and free heap memory.

unsigned long lastMessageTime = 0; const unsigned long MESSAGE_INTERVAL = 10000; // 10 seconds void loop() { bluetoothServer.loop(); // Send periodic status updates (only when connected) if (bluetooth.isConnected() && millis() - lastMessageTime >= MESSAGE_INTERVAL) { lastMessageTime = millis(); messageCount++; String statusMsg = "Status update #" + String(messageCount); statusMsg += " - Uptime: " + String(millis() / 1000) + "s"; statusMsg += " - Heap: " + String(ESP.getFreeHeap()) + " bytes"; bluetoothChat.send(statusMsg); } delay(10); }

Message Logging

Every incoming message and every reply is printed to the Serial Monitor with a time stamp in seconds. A helper function builds the reply.

// Log all messages to Serial with timestamps bluetoothChat.onChatMessage([](const String& message) { unsigned long timestamp = millis() / 1000; Serial.print("["); Serial.print(timestamp); Serial.print("s] Received: "); Serial.println(message); // Process and respond String response = processCommand(message); bluetoothChat.send(response); Serial.print("["); Serial.print(millis() / 1000); Serial.print("s] Sent: "); Serial.println(response); }); String processCommand(const String& cmd) { if (cmd.equalsIgnoreCase("ping")) return "pong!"; if (cmd.equalsIgnoreCase("status")) return "All systems operational"; if (cmd.equalsIgnoreCase("time")) return "Uptime: " + String(millis() / 1000) + "s"; return "Echo: " + cmd; }

Command Parser with Arguments

This parser splits a message into a command and an argument, like "PWM 128". The PWM output is on D5 (GPIO20). For the PIN command, type the GPIO number, not the Uno label. For example, "PIN 12 HIGH" sets D13 (GPIO12) high.

bluetoothChat.onChatMessage([](const String& message) { // Parse command and argument (format: "COMMAND ARG") int spaceIndex = message.indexOf(' '); String command, argument; if (spaceIndex > 0) { command = message.substring(0, spaceIndex); argument = message.substring(spaceIndex + 1); } else { command = message; argument = ""; } command.toUpperCase(); if (command == "PWM") { int value = argument.toInt(); value = constrain(value, 0, 255); analogWrite(20, value); // D5 (GPIO20) bluetoothChat.send("PWM set to " + String(value)); } else if (command == "DELAY") { int ms = argument.toInt(); bluetoothChat.send("Waiting " + String(ms) + "ms..."); delay(ms); bluetoothChat.send("Done!"); } else if (command == "PIN") { // Format: "PIN 12 HIGH" or "PIN 12 LOW" (GPIO number, GPIO12 = D13) int secondSpace = argument.indexOf(' '); if (secondSpace > 0) { int pin = argument.substring(0, secondSpace).toInt(); String state = argument.substring(secondSpace + 1); state.toUpperCase(); digitalWrite(pin, state == "HIGH" ? HIGH : LOW); bluetoothChat.send("Pin " + String(pin) + " set " + state); } } });

WARNING

Do not let the PIN command drive GPIO0, GPIO3 (D6), GPIO45 or GPIO46 (D9) unless you know what is connected. These are boot strapping pins on the ESP32 S3 Uno.

Hardware Integration Examples

Chat commands are great for moving things. These two examples control a servo and a DC motor with plain words.

Servo Control via Chat

Send a number from 0 to 180, or "SERVO 90", and the servo moves to that angle. "SWEEP" moves it all the way and back. The servo signal is on D3 (GPIO17). The code uses the ESP32Servo library by Kevin Harrington.

#include <ESP32Servo.h> Servo myServo; const int SERVO_PIN = 17; // D3 (GPIO17) void setup() { myServo.attach(SERVO_PIN); bluetoothChat.onChatMessage([](const String& message) { // Accept angle commands like "90" or "SERVO 90" String cmd = message; cmd.toUpperCase(); int angle = -1; if (cmd.startsWith("SERVO ")) { angle = cmd.substring(6).toInt(); } else { angle = cmd.toInt(); if (angle == 0 && cmd != "0") angle = -1; // Not a number } if (angle >= 0 && angle <= 180) { myServo.write(angle); bluetoothChat.send("Servo moved to " + String(angle) + "°"); } else if (cmd == "SWEEP") { bluetoothChat.send("Sweeping servo..."); for (int a = 0; a <= 180; a += 5) { myServo.write(a); delay(30); } for (int a = 180; a >= 0; a -= 5) { myServo.write(a); delay(30); } bluetoothChat.send("Sweep complete"); } }); }

Motor Control via Chat

A motor driver gets its speed on D5 (GPIO20) and its direction on D7 (GPIO14) and D8 (GPIO21). Type FORWARD, REVERSE, STOP or "SPEED 150".

const int MOTOR_PWM = 20; // D5 (GPIO20) const int MOTOR_DIR1 = 14; // D7 (GPIO14) const int MOTOR_DIR2 = 21; // D8 (GPIO21) void setup() { pinMode(MOTOR_PWM, OUTPUT); pinMode(MOTOR_DIR1, OUTPUT); pinMode(MOTOR_DIR2, OUTPUT); bluetoothChat.onChatMessage([](const String& message) { String cmd = message; cmd.toUpperCase(); if (cmd == "FORWARD") { digitalWrite(MOTOR_DIR1, HIGH); digitalWrite(MOTOR_DIR2, LOW); analogWrite(MOTOR_PWM, 200); bluetoothChat.send("Motor running forward"); } else if (cmd == "REVERSE") { digitalWrite(MOTOR_DIR1, LOW); digitalWrite(MOTOR_DIR2, HIGH); analogWrite(MOTOR_PWM, 200); bluetoothChat.send("Motor running reverse"); } else if (cmd == "STOP") { analogWrite(MOTOR_PWM, 0); bluetoothChat.send("Motor stopped"); } else if (cmd.startsWith("SPEED ")) { int speed = cmd.substring(6).toInt(); speed = constrain(speed, 0, 255); analogWrite(MOTOR_PWM, speed); bluetoothChat.send("Motor speed: " + String(speed)); } }); }

BLE on the ESP32 S3 Uno

The DIYables Bluetooth library has two chat examples: one for Classic Bluetooth and one for BLE. On the ESP32 S3 Uno you only have one choice, because the chip has a BLE radio and no Classic Bluetooth radio. The table shows what you get with BLE.

Feature BLE (Esp32BLE_Chat) on the ESP32 S3 Uno
iOS Support Yes
Android Support Yes
Power Consumption Low
Range About 10 to 30 m indoors
Data Rate Enough for text chat
Pairing Required No (connect inside the app)
Classic Bluetooth (BluetoothSerial) Not supported on this board

Video Tutorial

Watch the video below to see this ESP32 S3 Uno project step by step. The demo code turns an external LED on and off from the chat, makes it blink, and reports its status. The ESP32 S3 Uno has no built-in LED on the Uno headers, so connect an LED to D13 (GPIO12) through a 220 ohm resistor.

LED Pin ESP32 S3 Uno Pin
Anode (long leg, through 220 ohm resistor) D13 (GPIO12)
Cathode (short leg) GND

Type HELP in the app to see the commands: LED ON, LED OFF, STATUS, UPTIME and BLINK.

/* * This ESP32 S3 Uno code was developed by newbiely.com * * This ESP32 S3 Uno code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/esp32-s3-uno/esp32-s3-uno-diyables-bluetooth-app-chat */ /* * DIYables Bluetooth Library - ESP32 S3 Uno BLE Chat Example * Works with DIYables Bluetooth STEM app on Android and iOS * * This example demonstrates the Bluetooth Chat feature: * - Two-way text messaging via Bluetooth * - Receive messages from mobile app * - Send messages to mobile app * * Board: ESP32 S3 Uno form board (BLE only, no Classic Bluetooth) * Arduino IDE board: ESP32S3 Dev Module * The ESP32 S3 Uno has no built-in LED on the Uno headers, * so connect an external LED (with a 220 ohm resistor) to pin D13 (GPIO12). * * 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 S3 Uno * 2. Open Serial Monitor (115200 baud) to see connection status and messages * 3. Use DIYables Bluetooth App to connect and chat * * Tutorial: https://diyables.io/bluetooth-app * Author: DIYables */ #include <DIYables_BluetoothServer.h> #include <DIYables_BluetoothChat.h> #include <platforms/DIYables_Esp32BLE.h> // BLE Configuration const char* DEVICE_NAME = "ESP32BLE_Chat"; 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 Chat app instance DIYables_BluetoothChat bluetoothChat; // LED Pin: external LED on D13 (GPIO12) of the ESP32 S3 Uno #define LED_PIN 12 // Variables for periodic messages unsigned long lastMessageTime = 0; const unsigned long MESSAGE_INTERVAL = 10000; // Send message every 10 seconds int messageCount = 0; void setup() { Serial.begin(115200); delay(1000); pinMode(LED_PIN, OUTPUT); digitalWrite(LED_PIN, LOW); Serial.println("DIYables Bluetooth - ESP32 S3 Uno BLE Chat Example"); // Initialize Bluetooth server with platform-specific implementation bluetoothServer.begin(); // Add chat app to server bluetoothServer.addApp(&bluetoothChat); // Set up connection event callbacks bluetoothServer.setOnConnected([]() { Serial.println("Bluetooth connected!"); bluetoothChat.send("Hello! ESP32 S3 Uno BLE is ready to chat."); }); bluetoothServer.setOnDisconnected([]() { Serial.println("Bluetooth disconnected!"); messageCount = 0; }); // Set up callback for received chat messages bluetoothChat.onChatMessage([](const String& message) { String cmd = message; cmd.toUpperCase(); cmd.trim(); Serial.print("Received from App: "); Serial.println(cmd); if (cmd == "LED ON") { digitalWrite(LED_PIN, HIGH); bluetoothChat.send("LED is now: ON"); } else if (cmd == "LED OFF") { digitalWrite(LED_PIN, LOW); bluetoothChat.send("LED is now: OFF"); } else if (cmd == "STATUS") { int state = digitalRead(LED_PIN); String statusMsg = "--- ESP32 S3 Uno STATUS ---\n"; statusMsg += "LED: " + String(state == HIGH ? "ON" : "OFF") + "\n"; statusMsg += "Uptime: " + String(millis() / 1000) + "s\n"; statusMsg += "Chip: ESP32 S3 Uno Dual-Core"; bluetoothChat.send(statusMsg); } else if (cmd == "UPTIME") { bluetoothChat.send("System Uptime: " + String(millis() / 1000) + " seconds"); } else if (cmd == "BLINK") { bluetoothChat.send("Blinking LED..."); for(int i=0; i<3; i++) { digitalWrite(LED_PIN, HIGH); delay(200); digitalWrite(LED_PIN, LOW); delay(200); } bluetoothChat.send("Blink Done!"); } else if (cmd == "HELP") { String help = "--- COMMAND LIST ---\n" "- LED ON / OFF\n" "- STATUS\n" "- UPTIME\n" "- BLINK\n"; bluetoothChat.send(help); } else { bluetoothChat.send("Unknown command. Type 'HELP'."); } }); Serial.println("Waiting for Bluetooth connection..."); } void loop() { bluetoothServer.loop(); if (Serial.available()) { String serialMsg = Serial.readStringUntil('\n'); serialMsg.trim(); if (serialMsg.length() > 0 && bluetooth.isConnected()) { bluetoothChat.send(serialMsg); Serial.print("Sent from Serial: "); Serial.println(serialMsg); } } delay(10); }

Common Issues and Debug Tips

Most chat problems come from the connection, the Serial Monitor settings or a missing callback. Check these points before you change your code.

The device does not show up in the app

Make sure the sketch is uploaded and the board is powered. Turn on Bluetooth on your phone, and on Android 11 or older also turn on Location. Remember that the ESP32 S3 Uno shows up as a BLE device named ESP32BLE_Chat, so you never pair it in the phone settings.

Messages do not appear in the app

Check the connection status in the app and in the Serial Monitor. Make sure your code calls bluetoothChat.send(). If it still fails, disconnect and connect again.

Echo does not work

Check that the onChatMessage() callback is set up in setup(). Make sure the Chat screen is open in the app, and watch the Serial Monitor for "Received" lines.

The connection drops often

Move the phone closer to the board and keep other BLE devices away. Use a good USB cable and a stable power source.

The Serial-to-Bluetooth bridge does nothing

Set the Serial Monitor to 115200 baud and the line ending to "Newline". The bridge only sends text while bluetooth.isConnected() is true, and the Serial.available() code must be inside loop().

Sketch too large

Go to Tools > Partition Scheme and choose "Huge APP (3MB No OTA/1MB SPIFFS)" or "No OTA (Large APP)". The default scheme gives only about 1.2MB for code, which is too small for the Bluetooth libraries. The bigger scheme gives about 3MB by removing the OTA (over-the-air update) space.

Debug Tips

This helper prints the message, its length and the free heap memory. Call it at the start of your chat callback when something looks wrong.

void debugChatMessage(const String& message) { Serial.println("=== Chat Debug ==="); Serial.println("Message: " + message); Serial.println("Length: " + String(message.length())); Serial.println("Free Heap: " + String(ESP.getFreeHeap())); Serial.println("=================="); }

Project Ideas

A text chat fits many kinds of projects. Here are some ideas to get you started.

Remote control projects

Control a robot with text commands, build a home automation command center, switch relays with text feedback, or set a servo position from the chat.

Monitoring projects

Ask the board for sensor values, run health checks by chat, log data with Bluetooth feedback, or debug your project without a cable.

Interactive projects

Make a quiz or trivia game, write a step-by-step guide that answers in the chat, build a small chatbot about your project, or create a remote settings tool.

Education projects

Learn the basics of Bluetooth communication, practice string parsing and command handling, and design your own simple message protocol.

Integration with Other Bluetooth Apps

The DIYables Bluetooth library can run several app screens at the same time. The chat is a good partner for other screens, because it can switch modes or apply presets.

Combine with Bluetooth Monitor

Use the chat to start and stop monitoring, and the monitor screen for the live data. The sensor is on A0 (GPIO2).

// In setup(): set the ADC attenuation to 11 dB (up to ~3.3V input) // analogSetAttenuation(ADC_11db); bluetoothChat.onChatMessage([](const String& message) { if (message.equalsIgnoreCase("start")) { monitoring = true; bluetoothChat.send("Monitoring started"); } else if (message.equalsIgnoreCase("stop")) { monitoring = false; bluetoothChat.send("Monitoring stopped"); } }); // In loop: send data to monitor when active if (monitoring) { bluetoothMonitor.send("Sensor: " + String(analogRead(2))); // A0 (GPIO2), 0 to 4095 }

The ESP32 S3 Uno ADC gives values from 0 to 4095, not 0 to 1023. Add analogSetAttenuation(ADC_11db); in setup() so the analog pin can read the full range, from 0V up to about 3.3V. Without it, the reading hits 4095 too early and the values look wrong.

Combine with Bluetooth Slider

Use the chat to load preset values, and the sliders for fine control.

bluetoothChat.onChatMessage([](const String& message) { if (message.equalsIgnoreCase("preset1")) { currentSlider1 = 25; currentSlider2 = 75; bluetoothSlider.send(currentSlider1, currentSlider2); bluetoothChat.send("Applied Preset 1: 25%, 75%"); } else if (message.equalsIgnoreCase("preset2")) { currentSlider1 = 50; currentSlider2 = 50; bluetoothSlider.send(currentSlider1, currentSlider2); bluetoothChat.send("Applied Preset 2: 50%, 50%"); } });

Next Steps

When the chat works well, try the other app screens with your ESP32 S3 Uno:

  1. ESP32 S3 Uno - Bluetooth Monitor - stream text from the board to the app
  2. ESP32 S3 Uno - Bluetooth Slider - set values with sliders

Support

If you get stuck, these places can help:

  1. Read the API reference of the DIYables Bluetooth library.
  2. Ask in the Arduino community forums.

FAQ

Does the ESP32 S3 Uno support Classic Bluetooth for this chat?

No. The ESP32 S3 Uno has BLE only, so the BluetoothSerial library and the Esp32Bluetooth_Chat example do not compile or do not work on it. Use the Esp32BLE_Chat example from this page. It works with the DIYables Bluetooth App on both Android and iOS.

Do I need to pair the ESP32 S3 Uno in my phone's Bluetooth settings?

No. BLE devices connect inside the app. Open the DIYables Bluetooth App, tap Connect and choose ESP32BLE_Chat. If you paired it in the phone settings by mistake, remove it there and connect from the app.

Can I change the Bluetooth name of my board?

Yes. Change the DEVICE_NAME value at the top of the sketch, for example to "Kitchen_Chat", and upload again. Use a short name so it fits in the scan list. Give each board a different name if you have more than one nearby.

Is the chat code the same as the UNO R4 WiFi version?

The idea and the callbacks are the same, but the platform file is different. The UNO R4 WiFi uses DIYables_ArduinoBLE.h, while the ESP32 S3 Uno uses DIYables_Esp32BLE.h and the DIYables_Esp32BLE class. The ESP32 S3 Uno sketch also uses 115200 baud and adds the heap command.

What if my project needs more pins than the Uno headers give?

The ESP32 S3 Uno has two extra rows of holes with more GPIOs. GPIO15 and GPIO16 work as input, output, PWM and analog. GPIO45 and GPIO35 to GPIO42 work as input, output and PWM. GPIO47 and GPIO48 are output only, with PWM. Solder pin headers to use them, and write the GPIO number in your code. For extra relays or LEDs controlled by chat, GPIO35 to GPIO42 are a good choice; for extra analog sensors, use GPIO15 or GPIO16. Be careful with two things. First, GPIO47 and GPIO48 may run at 1.8V instead of 3.3V on some modules (especially ones with "V" in the name, like R8V or R16V), so 3.3V devices can be damaged or act strangely. Second, GPIO0, GPIO3 (D6), GPIO45 and GPIO46 (D9) are boot strapping pins, and a wrong connection can stop the board from booting or uploading code. Use the other extra pins first.

Why does the monitor example call analogSetAttenuation(ADC_11db)?

It sets the ESP32 S3 Uno ADC input range to about 0 to 3.3V, so the full sensor signal maps to 0 to 4095. Without it the range is smaller and readings reach 4095 too early. The UNO R4 does not need this line, it is for ESP32 boards only. Keep the sensor output at 3.3V or less.

Troubleshooting

Problem Possible Cause Solution
No COM port in Arduino IDE USB driver missing or charge-only cable Install the CP210x or CH340 driver, use a data USB cable, or hold BOOT while pressing RESET
ESP32BLE_Chat is not in the scan list Sketch not running or phone Bluetooth off Press RESET on the board, turn on Bluetooth (and Location on Android 11 or older), then scan again
Compile error about BluetoothSerial Classic Bluetooth example used Use the Esp32BLE_Chat example, the ESP32 S3 Uno has BLE only
Sketch too big error Default partition is too small for Bluetooth In Tools > Partition Scheme, choose Huge APP
Serial Monitor shows strange characters Wrong baud rate Set the Serial Monitor to 115200 baud
Text from Serial Monitor is not sent Line ending not set or phone not connected Set the line ending to Newline and connect the app first
LED in the demo does not light Wrong pin or LED reversed Connect the LED long leg to D13 (GPIO12) through 220 ohm and the short leg to GND
Board resets when a relay or motor turns on Power drop on the board Power relays and motors from a separate supply and connect the grounds

※ 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!