The Physical Layer: Wiring and Level Shifting
The most common mistake when wiring an Arduino to a Bluetooth module is ignoring logic levels. Standard Arduinos (Uno, Mega, Nano) operate at 5V logic. Almost all modern Bluetooth modules (HC-05, HM-10, ESP32) operate at 3.3V logic and are not 5V tolerant on their RX pins. Feeding 5V directly into the module's RX pin will degrade or destroy the silicon over time.
Regarding pull-up resistors: standard UART lines (TX/RX) do not require external pull-ups; they are actively driven push-pull lines. However, if you are using an I2C-to-UART bridge (like the SC16IS750) to save hardware serial pins, the I2C SDA and SCL lines require 4.7kΩ pull-up resistors to VCC, or the bus will float and drop packets.
Bus Mechanics and Protocol Specifications
Bluetooth for microcontrollers is a two-stage bus: the local wired UART bridge, and the remote wireless RF link. Understanding the constraints of both layers prevents buffer overflows and dropped connections.
| Protocol Layer | Wires / Pins | Speed (Max Practical) | Addressing Scheme | Max Distance |
|---|---|---|---|---|
| UART (Local Bridge) | TX, RX, VCC, GND | 115,200 bps | None (Point-to-Point) | < 1 meter (breadboard) |
| Bluetooth Classic (SPP) | RF (2.4 GHz) | ~2.1 Mbps (RF) / 115k (UART) | 48-bit MAC Address | 10m (Class 2) to 100m (Class 1) |
| Bluetooth Low Energy | RF (2.4 GHz) | 1 Mbps or 2 Mbps PHY | UUIDs (Service/Characteristic) | Up to 100m (depending on Tx power) |
Notice the speed bottleneck: even if BLE supports a 2 Mbps PHY over the air, your local UART bridge limits the actual payload throughput to your configured baud rate. If you stream sensor data at 115,200 baud, your maximum theoretical throughput is roughly 11.5 KB/s, regardless of the RF protocol's raw speed.
The Classic Failures (And How to Fix Them)
When an Arduino and Bluetooth module refuse to communicate, the failure almost always falls into one of three categories.
1. Baud Rate Mismatch
The HC-05 module has two distinct modes: Data Mode (default 9600 baud) and AT Command Mode (fixed at 38400 baud). A classic failure occurs when a user tries to send AT commands while the Arduino Serial Monitor is set to 9600 baud, or attempts to read sensor data while the monitor is stuck at 38400. Always verify the module's state (the LED blinks slowly in AT mode, rapidly in Data mode) and match your Serial.begin() accordingly.
2. Missing Level Shifting (Fried RX Pin)
If the module powers on and advertises, but the Arduino cannot send data to it, the module's RX pin is likely dead from 5V overvoltage. Test this by swapping the TX/RX lines and using a logic analyzer or oscilloscope to verify the 3.3V signal is actually leaving the module's TX pin.
3. Address Clash and iOS Rejection
Apple's iOS strictly prohibits Bluetooth Classic (SPP profile) connections for non-MFi certified devices. If your Android phone sees the HC-05 but your iPhone does not, this is not a hardware failure; it is an OS-level block. You must switch to a BLE module (HM-10) and use a BLE central app. Furthermore, BLE address clashes occur when multiple identical modules broadcast the same default MAC address; use the AT+ADDR command to assign unique addresses if deploying a mesh of sensors.
Minimal Working Exchange: UART to BLE
Below is a minimal, robust implementation for an Arduino Uno reading a potentiometer and transmitting the value over BLE using an HM-10 module. This assumes the voltage divider is physically wired as described in the Physical Layer section.
Wiring Map:
- Arduino 5V → HM-10 VCC
- Arduino GND → HM-10 GND
- Arduino Pin 11 (SoftwareSerial RX) → HM-10 TX
- Arduino Pin 10 (SoftwareSerial TX) → 1kΩ Resistor → HM-10 RX
- HM-10 RX → 2kΩ Resistor → GND
#include <SoftwareSerial.h>
// Pin 10 is TX (to HM-10 RX via voltage divider)
// Pin 11 is RX (from HM-10 TX)
SoftwareSerial bleSerial(11, 10);
const int POT_PIN = A0;
int lastVal = -1;
void setup() {
Serial.begin(9600); // Debug monitor
bleSerial.begin(9600); // HM-10 default baud
Serial.println('BLE HM-10 Initialized. Waiting for central...');
}
void loop() {
int currentVal = analogRead(POT_PIN);
// Only transmit if value changes by more than 2 to prevent bus flooding
if (abs(currentVal - lastVal) > 2) {
lastVal = currentVal;
// Format as string with newline for easy parsing on the receiving app
String payload = String('POT:') + String(currentVal) + '\n';
bleSerial.print(payload);
// Small delay to prevent overwhelming the HM-10 internal UART buffer
delay(20);
}
// Echo any incoming BLE commands to the debug monitor
if (bleSerial.available()) {
Serial.write(bleSerial.read());
}
}
Sniffing and Debugging the RF Bus
When the code compiles and the wiring is correct, but the mobile app shows no data, you must sniff the bus to isolate the break in the chain.
- Verify the UART Bridge: Disconnect the Bluetooth module. Connect the Arduino TX line directly to the Arduino RX line (a local loopback). Open the Serial Monitor and type a message. If it echoes back, your microcontroller's UART and code are functioning perfectly. The fault lies in the RF layer or the module.
- Sniff the RF Layer (BLE): Download the nRF Connect for Mobile app (by Nordic Semiconductor). Scan for your module. If nRF Connect sees the device and can read the custom Service UUID, your RF transmission is working, and the bug is in your custom mobile app's parsing logic.
- Deep Packet Inspection: For advanced debugging of BLE advertising intervals or connection parameter rejections, use an ESP32 running the ESP-IDF Bluetooth stack to capture HCI snoop logs, or use a dedicated hardware sniffer like the nRF52840 Dongle running Wireshark.
Decision Tree: Which Bluetooth Path to Take
Do not default to the first module you find in a starter kit. Use this decision matrix to select the exact hardware for your architecture.
| Project Requirement | Protocol / Module | Concrete Pick (Part Number) |
|---|---|---|
| Need simple serial terminal to Android/Windows PC; low cost; no custom app development. | Bluetooth Classic (SPP) | HC-05 (with ZS-040 breakout board) |
| Must connect to iOS/iPhone; battery powered; sending small sensor payloads. | Bluetooth Low Energy (BLE) | HM-10 or AT-09 (CC2541 based) |
| Need high throughput, native WiFi+BT, eliminating UART wiring and level shifters entirely. | Native BLE 5.0 / Classic | ESP32-C3 SuperMini or ESP32-S3 DevKit |
| Long range (100m+); outdoor telemetry; requires external antenna. | BLE Long Range (Coded PHY) | ESP32-C6 with u.FL antenna connector |






