Difficulty: Intermediate | Time: 45 Minutes | Cost: ~$42 USD

The Direct Answer: Which BLE Library and Board to Use

When searching for a reliable ble library arduino solution, the ecosystem splits into two distinct paths based on your silicon. If you are using nRF52-based boards (like the Nano 33 BLE), the official ArduinoBLE library is the mandatory standard. If you are using an ESP32, you should bypass the default ESP32 BLE library and use NimBLE-Arduino for vastly superior memory management.

This guide focuses strictly on the Arduino Nano 33 BLE Sense Rev2 (Board part number: ABX00069) using the official ArduinoBLE library. We will build an Environmental Sensing Beacon that reads the onboard HS300x temperature and humidity sensor and broadcasts the data via custom GATT (Generic Attribute Profile) characteristics to any BLE central device, such as a smartphone running nRF Connect.

Bench Note: The Rev2 of the Nano 33 BLE Sense uses the Renesas HS300x sensor. The older Rev1 used the ST HTS221. If you copy code from older 2021-era tutorials using Arduino_HTS221.h, it will fail to compile on the Rev2 hardware. Always check your board revision silkscreen.

Hardware Spec Sheet & Pin Mapping

To keep the I2C bus clean and avoid conflicts with the internal sensor routing, this build uses a minimal external footprint. The Nano 33 BLE operates strictly at 3.3V logic; never feed 5V into its digital pins.

Component Exact Variant / Model Notes
Microcontroller Arduino Nano 33 BLE Sense Rev2 (ABX00069) nRF52840 SoC, 3.3V logic
Onboard Sensor Renesas HS300x (Integrated) Temp/Humidity, Internal I2C
External LED Standard 5mm LED (Any color) Connection status indicator
Current Limiting Resistor 220Ω or 330Ω (1/4W) Required for external LED

Pin Mapping Table

Function Nano 33 BLE Pin Connected To
Connection Status LED D2 LED Anode (via 220Ω resistor)
LED Cathode GND LED Cathode
Onboard Error LED LED_BUILTIN (D13) Internal (Active LOW)

Complete Compilable ArduinoBLE Code

Before compiling, ensure you have installed the Arduino Mbed OS Nano Boards core (version 4.0.2 or newer) via the Boards Manager, and installed both the ArduinoBLE and Arduino_HS300x libraries via the Library Manager.

#include <ArduinoBLE.h>
#include <Arduino_HS300x.h>

// --- PIN DEFINITIONS ---
#define STATUS_LED_PIN 2
#define ERROR_LED_PIN LED_BUILTIN

// --- BLE UUID DEFINITIONS ---
// Custom Service UUID: Environmental Beacon
#define BLE_SERVICE_UUID      "19B10000-E8F2-537E-4F6C-D104768A1214"
#define BLE_TEMP_CHAR_UUID    "19B10001-E8F2-537E-4F6C-D104768A1214"
#define BLE_HUM_CHAR_UUID     "19B10002-E8F2-537E-4F6C-D104768A1214"

BLEService envService(BLE_SERVICE_UUID);
BLEFloatCharacteristic tempChar(BLE_TEMP_CHAR_UUID, BLERead | BLENotify);
BLEFloatCharacteristic humChar(BLE_HUM_CHAR_UUID, BLERead | BLENotify);

void setup() {
  Serial.begin(115200);
  while (!Serial); // Wait for serial monitor (optional, remove for standalone)

  pinMode(STATUS_LED_PIN, OUTPUT);
  pinMode(ERROR_LED_PIN, OUTPUT);
  
  // Active LOW on Nano 33 BLE built-in LED
  digitalWrite(ERROR_LED_PIN, HIGH); 

  // 1. Initialize Onboard Sensor
  if (!HS300x.begin()) {
    Serial.println("Failed to initialize HS300x sensor!");
    blinkError(100); // Fast blink = Sensor fail
  }

  // 2. Initialize BLE Stack
  if (!BLE.begin()) {
    Serial.println("Starting BLE failed!");
    blinkError(500); // Slow blink = BLE stack fail
  }

  // 3. Configure BLE Parameters
  BLE.setLocalName("Nano33-Env-Beacon");
  BLE.setAdvertisedService(envService);

  // 4. Add Characteristics to Service
  envService.addCharacteristic(tempChar);
  envService.addCharacteristic(humChar);
  BLE.addService(envService);

  // Set initial values
  tempChar.writeValue(0.0);
  humChar.writeValue(0.0);

  // 5. Start Advertising
  BLE.advertise();
  Serial.println("BLE Environmental Beacon active. Waiting for connections...");
}

void loop() {
  BLEDevice central = BLE.central();

  if (central) {
    Serial.print("Connected to central: ");
    Serial.println(central.address());
    digitalWrite(STATUS_LED_PIN, HIGH);

    while (central.connected()) {
      float temp = HS300x.readTemperature();
      float hum = HS300x.readHumidity();

      // Update characteristics if readings are valid
      if (!isnan(temp) && !isnan(hum)) {
        tempChar.writeValue(temp);
        humChar.writeValue(hum);
        
        Serial.print("Temp: "); Serial.print(temp);
        Serial.print(" C | Hum: "); Serial.print(hum); Serial.println(" %");
      }
      delay(1000); // 1Hz update rate
    }

    digitalWrite(STATUS_LED_PIN, LOW);
    Serial.println("Disconnected from central.");
  }
}

// Error handling blocking function
void blinkError(int intervalMs) {
  while (true) {
    digitalWrite(ERROR_LED_PIN, LOW);  // Turn ON (Active LOW)
    delay(intervalMs);
    digitalWrite(ERROR_LED_PIN, HIGH); // Turn OFF
    delay(intervalMs);
  }
}

Debugging: First 3 Things to Check When It Fails

When working with the ArduinoBLE library on the nRF52840, failures usually stem from BSP (Board Support Package) mismatches or power delivery issues rather than bad code. If your board hangs or fails to advertise, check these three items first:

  1. Verify the Mbed OS Core Version: The Nano 33 BLE relies on the Mbed OS core. Versions prior to 3.0.0 or the transitionary 4.0.x builds had severe memory leaks in the BLE stack. Open your Boards Manager and ensure Arduino Mbed OS Nano Boards is updated to the latest stable release (4.1.x or newer).
  2. Check USB Power Delivery (Brownouts): The nRF52840 draws current spikes up to 15mA during BLE transmission. If you are powering the board through a low-quality USB hub or a thin, high-resistance micro-USB cable, the voltage will droop below 3.3V, causing the BLE radio to silently reset. Use a short, high-quality data cable directly into a wall adapter or PC motherboard port.
  3. Clear the Smartphone BLE Cache: If your phone previously connected to the Nano 33 but you changed the UUIDs in your code, Android and iOS will aggressively cache the old GATT table. Toggle your phone's Bluetooth off and on, or use the "Clear GATT Cache" option in developer settings before reconnecting.

Exact Error Strings and Ranked Causes

Error String: "Starting BLE failed!" (Printed to Serial, followed by slow LED blink)

  • Cause 1 (Most Likely): The Mbed OS core is corrupted or outdated. Reinstall the board package via the Arduino IDE Boards Manager.
  • Cause 2: Hardware fault on the nRF52840 module. The radio subsystem is dead, often caused by feeding 5V into the 3.3V pin or an ESD strike to the antenna trace.

Error String: "Disconnected from central." (Occurs immediately after connection)

  • Cause 1 (Most Likely): Connection parameter mismatch. The central device (phone) requested a connection interval the nRF52840 rejected. Ensure you aren't flooding the bus with writeValue() calls faster than 10ms.
  • Cause 2: The central device moved out of RF range or experienced 2.4GHz interference from a nearby USB 3.0 hub or Wi-Fi router.

Extending or Simplifying the Build

The code above provides a robust baseline, but real-world deployments require scaling. Here is how to adapt the architecture based on your project constraints.

How to Simplify: Broadcast-Only iBeacon Mode

If you don't need two-way GATT connections and only want to broadcast data to passive scanners (like a Raspberry Pi running an MQTT bridge), strip out the BLEService and BLECharacteristic logic. Instead, use BLE.setManufacturerData() to pack your sensor readings into a raw byte array and broadcast it in the advertising payload. This eliminates connection overhead and reduces power consumption by up to 80%.

How to Extend: Adding Standardized GATT Services

While custom UUIDs (like 19B10000...) are fine for proprietary apps, integrating with Apple HomeKit or standard smart home hubs requires adopting Bluetooth SIG adopted specifications. To extend this build for standard compatibility, replace the custom UUIDs with the official Environmental Sensing Service UUID (0x181A) and the standard Temperature (0x2A6E) and Humidity (0x2A6F) characteristic UUIDs. Note that standard characteristics often require specific data formats (e.g., int16_t scaled by 100 for temperature) rather than raw floats.

Frequently Asked Questions

Can I use the ArduinoBLE library on an ESP32?

No. The ArduinoBLE library is strictly architected for nRF52-based boards (Nano 33 BLE, Portenta) and the Arduino MKR WiFi 1010 (which uses a NINA-W10 module). If you are using an ESP32, you must use the ESP32-specific BLEDevice library or, preferably, the third-party NimBLE-Arduino library, which reduces RAM usage by roughly 70% compared to the default ESP32 BLE stack.

Why does my BLE connection drop after exactly 10 seconds?

This is almost always caused by a missing or blocked BLE.poll() or central.connected() check in your main loop. The nRF52840 requires the main loop to service the BLE stack regularly. If your code enters a blocking delay() or a long-running I2C read that exceeds the supervision timeout (default is often 6-10 seconds on mobile OS), the central device will assume the peripheral is dead and sever the link. Keep your loop execution time under 50ms.

How do I update the BLE device name dynamically in ArduinoBLE?

You cannot change the local name while the BLE stack is actively advertising. To update the name dynamically (for example, appending a MAC address suffix), you must call BLE.end(), update the string via BLE.setLocalName("NewName"), and then call BLE.advertise() to restart the stack. This process takes approximately 150ms and will drop any active central connections.

What is the maximum data payload for a single BLE characteristic?

Under Bluetooth 4.2 (which the nRF52840 supports via backward compatibility), the default ATT_MTU is 23 bytes, leaving 20 bytes for actual payload data. However, the nRF52840 supports Bluetooth 5.0 Data Length Extensions (DLE). By negotiating a larger MTU with the central device, you can push the payload up to 244 bytes per characteristic. In ArduinoBLE, you can define the maximum length by passing a third parameter to the characteristic constructor, like this: BLECharacteristic myChar(UUID, BLERead, 244).

For further reading on nRF52840 hardware capabilities, refer to the official Arduino Nano 33 BLE Sense Rev2 documentation. Always verify your local RF transmission regulations when deploying BLE beacons in commercial environments.