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.

Board Variant Target: This guide and code specifically target the Arduino Nano v3 (ATmega328P, 5V logic). If you are using the newer Nano 33 BLE Sense (nRF52840, 3.3V logic), skip to the 'Extending and Simplifying' section, as that board has native BLE and requires a completely different library stack.

Project Spec Sheet & Parts List

ParameterSpecification
Difficulty Rating2/5 (Requires basic logic-level awareness)
Estimated Time45 minutes
Target MicrocontrollerArduino Nano v3 (ATmega328P, 16MHz, 5V)
BLE ModuleHM-10 (CC2541 chip, Firmware V540 or higher)
Operating Voltage5V (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 PinLogic Level ConverterHM-10 PinNotes
5VHV (High Voltage Side)-Powers the high-side of the shifter
3.3VLV (Low Voltage Side)VCCPowers the HM-10 and low-side shifter
GNDGND (Both sides)GNDCommon ground is mandatory
D10 (Software TX)HV1 -> LV1RXDShifts 5V TX down to 3.3V RX
D11 (Software RX)HV2 -> LV2TXDShifts 3.3V TX up to 5V RX (optional but recommended for noise immunity)
Safety Callout: Never power the HM-10 VCC pin from the Nano's 5V pin. The CC2541 chip has an absolute maximum rating of 3.6V. Exceeding this will cause immediate thermal failure. Always use the Nano's 3.3V output, which can supply up to 150mA (sufficient for the HM-10's ~15mA peak transmission draw).

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

  1. 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 your bleSerial.begin() to 115200 and test again.
  2. 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.
  3. 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 send AT+MODE2 to 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+MODE0 first.
  • 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.