Getting an Arduino talking to a phone or PC over Bluetooth usually means bridging a 5V microcontroller to a 3.3V wireless module via UART, or upgrading to an ESP32 for native BLE. The direct answer for legacy Arduino Uno builds is to use an HC-05 (Classic SPP) or HM-10 (BLE) module, but you must use a voltage divider on the module's RX pin to avoid frying the 3.3V silicon. If you are starting a new project in 2026, skip the external module entirely and use an ESP32-WROOM-32 with native Bluetooth.
The Physical Layer: Wiring 5V Arduinos to 3.3V Bluetooth
The physical connection between a standard 5V Arduino (like the Uno or Mega) and almost all modern Bluetooth modules is a UART serial link. This requires three shared lines: TX, RX, and GND. However, the logic voltage mismatch is the number one killer of Bluetooth modules on the bench.
Most Arduino boards operate at 5V logic. Modules like the HM-10 (BLE), ESP-01, and nRF52-based breakout boards are strictly 3.3V tolerant. Sending a 5V HIGH signal from the Arduino's TX pin directly into the 3.3V RX pin of the Bluetooth module will degrade or instantly destroy the module's input buffer.
To drop the Arduino's 5V TX output down to a safe 3.3V for the module's RX input, use a simple resistor voltage divider. A 2kΩ resistor (R1) in series with the signal, and a 3kΩ resistor (R2) to ground, yields:
Vout = 5V × (3000 / (2000 + 3000)) = 3.0V.This 3.0V is well within the 3.3V logic HIGH threshold (usually 2.0V to 3.6V) and keeps the silicon safe. If you don't have 2k/3k, a 1k/2k combination works identically.
Pull-up Requirements: Unlike I2C, UART does not strictly require pull-up resistors to function. However, on many cheap HM-10 and HC-05 clones, a floating RX pin can pick up breadboard noise during boot, causing the module to misinterpret garbage data as AT commands and lock up. Soldering a 10kΩ pull-up resistor from the module's RX pin to its 3.3V VCC pin is a proven bench practice to ensure a stable idle HIGH state.
Bus Mechanics & Protocol Selection
When designing an Arduino to Bluetooth link, you are actually managing two distinct buses: the local wired UART bus, and the wireless RF link. Understanding the constraints of both dictates your module choice.
| Module / Platform | Protocol | Default UART Baud | Logic Level | RF Range (Practical) | iOS Compatible? |
|---|---|---|---|---|---|
| HC-05 (Legacy) | Classic SPP v2.0 | 9600 (Data) / 38400 (AT) | 3.3V (w/ 5V VCC reg) | 10m (Class 2) | No (MFi required) |
| HM-10 (CC2541) | BLE 4.0 | 9600 | 3.3V Strict | 10m - 30m | Yes |
| ESP32-WROOM-32 | Native BLE 4.2 + WiFi | N/A (Internal Bus) | 3.3V Strict | 20m - 50m | Yes |
| Adafruit nRF52840 | Native BLE 5.0 | N/A (Internal Bus) | 3.3V Strict | 50m+ | Yes |
Which protocol fits your build? Choose Classic SPP (HC-05) if you need simple, high-throughput serial streaming to an Android app or PC terminal, and don't care about iOS. Choose BLE (HM-10 or ESP32 Native) if you need iOS compatibility, low power consumption for battery-operated sensor nodes, or are broadcasting to multiple devices (one-to-many via advertising). For device counts exceeding 7 simultaneous connections, standard point-to-point Bluetooth fails; you must move to a BLE Mesh or ESP-NOW topology.
| Layer | Wires | Speed | Addressing | Max Distance |
|---|---|---|---|---|
| Local UART (Arduino to Module) | TX, RX, GND (3) | 9600 - 115200 Baud | None (Point-to-Point) | ~1m (breadboard) |
| RF Link: Classic SPP (HC-05) | None (2.4 GHz) | ~115 kbps practical | MAC Address + PIN | 10m (Class 2) |
| RF Link: BLE 4.0+ (HM-10/ESP32) | None (2.4 GHz) | 1-2 Mbps PHY | UUID / MAC Whitelist | 10m - 50m |
Minimal Working Exchange & Debugging the Link
Below is a minimal, compilable UART bridge sketch for an Arduino Uno connected to an HC-05 or HM-10 module. This sketch uses SoftwareSerial to free up the hardware UART (pins 0 and 1) for debugging via the Serial Monitor.
| Arduino Uno Pin | Direction | Bluetooth Module Pin | Notes |
|---|---|---|---|
| 5V | Power | VCC | HC-05 has onboard reg; HM-10 needs 3.3V source |
| GND | Common | GND | Must share common ground |
| Pin 10 (RX) | Input | TXD | Direct connection (3.3V is read as HIGH by 5V Uno) |
| Pin 11 (TX) | Output | RXD | MUST go through 2k/3k voltage divider |
#include <SoftwareSerial.h>
// Pin definitions for UART bridge
const int BT_RX_PIN = 10; // Arduino RX -> Connect to BT TX
const int BT_TX_PIN = 11; // Arduino TX -> Connect to BT RX (via divider)
// Initialize SoftwareSerial at 9600 baud (standard for HC-05/HM-10 data mode)
SoftwareSerial BTSerial(BT_RX_PIN, BT_TX_PIN);
void setup() {
// Hardware serial for PC debugging
Serial.begin(9600);
while (!Serial) { ; } // Wait for serial port to connect (Leonardo/Micro only)
// Software serial for Bluetooth module
BTSerial.begin(9600);
Serial.println("Bluetooth UART Bridge Ready.");
Serial.println("Type in Serial Monitor to send to BT.");
}
void loop() {
// Forward data from Bluetooth module to PC Serial Monitor
if (BTSerial.available()) {
char c = BTSerial.read();
Serial.print(c);
}
// Forward data from PC Serial Monitor to Bluetooth module
if (Serial.available()) {
char c = Serial.read();
BTSerial.print(c);
}
}
1. The iOS Rejection: Apple devices do not support the Classic SPP profile used by the HC-05. If your HC-05 isn't showing up on your iPhone, it's not broken; it's just the wrong protocol. You must use a BLE module (HM-10) or an ESP32 with native BLE.
2. The Baud Rate Trap: The HC-05 enters AT command mode at 38400 baud, but defaults to 9600 baud in normal data mode. If your Serial Monitor shows garbage characters, verify your module's state and match the baud rate.
3. The Brownout Reset: Bluetooth modules draw 100mA to 200mA peak currents during RF transmission. Powering an HC-05 from the Arduino Uno's 5V pin is usually fine, but doing so on a Nano or Pro Mini clone will overheat the onboard linear regulator, causing brownouts and random reboots. Use a dedicated 3.3V LDO or buck converter for the RF module.
How to Sniff and Debug the Bus
When the link fails, isolate the problem by splitting the system in half. To debug the local wired UART bus, clip a $12 USB logic analyzer (like a Saleae clone) to the TX and RX lines and use PulseView to decode the serial frames. If the microcontroller is swallowing bytes or the voltage divider is marginal, you will see the exact hex values, bit timings, and framing errors on the screen.
For the wireless RF link, abandon the Arduino IDE and use dedicated mobile apps. For Classic SPP, use Serial Bluetooth Terminal (Android) to view raw hex dumps, log connection drops, and send custom macros. For BLE debugging, nRF Connect (iOS/Android) is the industry standard; it allows you to inspect advertising packets, read/write to specific GATT characteristics by UUID, and map RSSI signal strength across a room. If nRF Connect sees the advertising packet but the Arduino Serial Monitor shows nothing, your RF link is fine, and your UART wiring or baud rate is the culprit.






