The classic Arduino Nano is a workhorse for bench prototyping, but it lacks native wireless connectivity. When your project requires low-power mobile app integration, adding a Bluetooth Low Energy (BLE) module is the standard path. This guide covers the exact hardware integration, logic-level translation, and AT-command debugging required to pair an HM-10 BLE module with an Arduino Nano v3.
Project Spec Sheet & Parts List
| Parameter | Specification |
|---|---|
| Difficulty Rating | 2/5 (Requires basic logic-level awareness) |
| Estimated Time | 45 minutes |
| Target Microcontroller | Arduino Nano v3 (ATmega328P, 16MHz, 5V) |
| BLE Module | HM-10 (CC2541 chip, Firmware V540 or higher) |
| Operating Voltage | 5V (Nano) / 3.3V (HM-10) |
Required Components
- Arduino Nano v3: Genuine or high-quality clone with the CH340 or FT232RL USB-to-serial chip (~$6 to $22).
- HM-10 BLE Module: Ensure it is the CC2541 variant, not the cheaper HC-05/HC-06 (which are Bluetooth Classic, not BLE). Cost: ~$4 to $8.
- Logic Level Converter (BSS138): A 4-channel bi-directional logic level shifter (~$2). Do not skip this. The Nano outputs 5V on its TX pin; feeding 5V directly into the HM-10's 3.3V RX pin will eventually degrade or destroy the CC2541 silicon.
- Breadboard & Jumper Wires: Standard 22 AWG stranded hookup wire.
Wiring the HM-10 to the Nano (Pin Mapping)
The most common point of failure in BLE Nano Arduino builds is improper voltage translation. The ATmega328P reads 3.3V from the HM-10's TX pin as a valid HIGH (the threshold is roughly 2.5V), so the HM-10 TX to Nano RX line can technically be direct. However, the Nano TX to HM-10 RX line must be shifted down to 3.3V.
| Arduino Nano Pin | Logic Level Converter | HM-10 Pin | Notes |
|---|---|---|---|
| 5V | HV (High Voltage Side) | - | Powers the high-side of the shifter |
| 3.3V | LV (Low Voltage Side) | VCC | Powers the HM-10 and low-side shifter |
| GND | GND (Both sides) | GND | Common ground is mandatory |
| D10 (Software TX) | HV1 -> LV1 | RXD | Shifts 5V TX down to 3.3V RX |
| D11 (Software RX) | HV2 -> LV2 | TXD | Shifts 3.3V TX up to 5V RX (optional but recommended for noise immunity) |
Complete BLE Serial Bridge Code
This sketch uses the SoftwareSerial library to communicate with the HM-10, leaving the hardware serial pins (D0/D1) free for USB debugging via the Serial Monitor. It includes a startup handshake to verify the module is responding to AT commands.
#include <SoftwareSerial.h>
// Pin definitions for SoftwareSerial
#define BLE_RX 11 // Nano RX connected to HM-10 TX
#define BLE_TX 10 // Nano TX connected to HM-10 RX
SoftwareSerial bleSerial(BLE_RX, BLE_TX);
// Timeout for AT command responses (milliseconds)
const unsigned long AT_TIMEOUT = 1000;
void setup() {
// Initialize hardware serial for USB debugging
Serial.begin(9600);
while (!Serial) { ; } // Wait for serial port to connect (Leonardo/Micro only, safe for Nano)
Serial.println(F("Booting BLE Nano Arduino Bridge..."));
// Initialize software serial for HM-10
// Default HM-10 baud rate is 9600
bleSerial.begin(9600);
// Handshake: Verify module is alive
if (verifyModule()) {
Serial.println(F("HM-10 Module detected and ready."));
configureModule();
} else {
Serial.println(F("ERROR: HM-10 not responding. Check wiring and baud rate."));
}
}
void loop() {
// Bridge data from Phone/App -> Nano Serial Monitor
if (bleSerial.available()) {
Serial.write(bleSerial.read());
}
// Bridge data from Nano Serial Monitor -> Phone/App
if (Serial.available()) {
bleSerial.write(Serial.read());
}
}
bool verifyModule() {
bleSerial.print("AT");
String response = readBleResponse();
return (response.indexOf("OK") >= 0);
}
void configureModule() {
// Set device name (Max 12 chars for HM-10)
sendATCommand("AT+NANOBLE", "OK");
// Set to Peripheral Mode (Mode 0)
sendATCommand("AT+MODE0", "OK");
}
void sendATCommand(const char* cmd, const char* expected) {
Serial.print(F("Sending: ")); Serial.println(cmd);
bleSerial.print(cmd);
String resp = readBleResponse();
if (resp.indexOf(expected) >= 0) {
Serial.print(F("Success: ")); Serial.println(resp);
} else {
Serial.print(F("Failed! Got: ")); Serial.println(resp);
}
}
String readBleResponse() {
String response = "";
unsigned long startTime = millis();
while (millis() - startTime < AT_TIMEOUT) {
while (bleSerial.available()) {
char c = bleSerial.read();
response += c;
}
if (response.length() > 0) {
// Small delay to catch trailing characters
delay(10);
while(bleSerial.available()) response += (char)bleSerial.read();
break;
}
}
return response;
}
Debugging: 'AT' Returns Garbage or 'ERROR'
When working with the HM-10, the most frequent symptom of failure is sending the AT test command via the Serial Monitor and receiving ??, ÿÿ (garbage characters), or an explicit ERROR string instead of the expected OK.
The First 3 Things to Check When It Fails
- Verify the Baud Rate Match: The HM-10 defaults to 9600 baud. If your module was previously used in another project, it might be set to 115200. Garbage characters (
??) almost always indicate a baud rate mismatch. Change yourbleSerial.begin()to 115200 and test again. - Check the Logic Level Voltage: If you are seeing intermittent garbage or the module resets randomly, probe the HM-10 RXD pin with a multimeter. If you read 5V during transmission, your logic level converter is wired backward or missing. The TI CC2541 datasheet specifies a 3.3V logic threshold.
- Confirm Firmware State (V540+): Modern HM-10 modules ship with firmware V540 or higher. In these versions, if the module is actively connected to a phone, it will reject AT commands and return
ERROR. You must disconnect the phone app before sending AT commands, or sendAT+MODE2to allow AT commands while connected.
Ranked Causes for the Exact Error String: 'ERROR' on AT+NAME
If you send AT+NANOBLE and receive exactly ERROR (not garbage, but the literal word ERROR), the causes are ranked as follows:
- Cause 1 (60%): The name exceeds 12 characters. The HM-10 buffer for the broadcast name is strictly limited. Truncate your string.
- Cause 2 (30%): The module is in Central Mode (Mode 1) instead of Peripheral Mode (Mode 0). Central mode modules cannot advertise a custom name. Send
AT+MODE0first. - Cause 3 (10%): You are using a clone module with a CC2540 chip instead of CC2541, running outdated firmware that requires a reboot after every AT write. Send the command, then power cycle the Nano.
Extending and Simplifying the Build
Once your serial bridge is stable, you have two distinct paths forward depending on your project constraints.
How to Simplify: Upgrade to the Nano 33 BLE Sense
If you are tired of managing logic level shifters and AT-command firmware quirks, migrate to the Arduino Nano 33 BLE Sense. It costs roughly $22 to $28 but features an onboard nRF52840 SoC. This eliminates the HM-10 entirely, runs on native 3.3V logic, and uses the robust ArduinoBLE library, allowing you to define custom GATT services and characteristics in pure C++ without serial bridging.
How to Extend: Adding I2C Sensor Streaming
To make this build useful for telemetry, add an MPU6050 IMU sensor to the Nano's hardware I2C pins (A4 for SDA, A5 for SCL). In the loop() function, read the accelerometer X/Y/Z registers, format them into a comma-separated string, and push them to bleSerial.print(). You can then use a generic BLE terminal app on iOS or Android (like LightBlue or nRF Connect) to log the CSV data to your phone's local storage.
Frequently Asked Questions
Can I use the hardware serial pins (D0/D1) for the HM-10?
You can, but it is highly discouraged for prototyping. D0 and D1 are tied to the Nano's onboard USB-to-serial chip. If you wire the HM-10 to D0/D1, you will experience conflicts when trying to upload new sketches via the Arduino IDE, and the Serial Monitor will show mixed USB and BLE traffic. Always use SoftwareSerial on digital pins (like D10/D11) for the BLE module, reserving D0/D1 for USB debugging.
Why does my phone see the BLE device but fail to connect?
If your phone's Bluetooth scanner sees 'NANOBLE' but the connection drops immediately, the HM-10 is likely in 'iBeacon' mode or 'Sensor Mode' rather than standard UART transparent mode. Send the command AT+DELO2 to force the module back into standard UART transparent peripheral mode. Additionally, ensure your phone app supports BLE UART (Nordic UART Service - NUS); standard Bluetooth Classic terminal apps will not work.
Is the HM-10 compatible with Bluetooth Classic (SPP)?
No. The HM-10 is strictly a Bluetooth Low Energy (BLE 4.0) device based on the TI CC2541 chip. It does not support the Serial Port Profile (SPP) used by Bluetooth Classic. If your project requires pairing with older laptops or legacy industrial equipment that only supports Bluetooth Classic SPP, you must use an HC-05 or HC-06 module instead. Note that HC-05/06 modules are not compatible with iOS devices, whereas the HM-10 is.






