If you need an Arduino BLE board for sensor-rich prototyping, buy the Arduino Nano 33 BLE Sense Rev2 (typically $48-$55). It packs a Nordic nRF52840 SoC, strictly 3.3V logic, and onboard environmental and IMU sensors, eliminating the need for messy external I2C breakout boards. This guide walks through building a Bluetooth Low Energy (BLE) environmental beacon, wiring it safely, and debugging the exact errors that stall nRF52840 projects.

The Arduino BLE Board Decision Matrix

Not every project needs a $50 board. Use this decision path to select the right hardware, terminating at the default recommendation for comprehensive maker projects.

If your project requires... Then choose this board... Approx. Cost
Ultra-compact wearable form factor (<20mm) Seeed Studio XIAO nRF52840 $15
High-power/long range + dual WiFi/BLE ESP32-C3 DevKitM-1 $6
Built-in sensors, official Arduino support, and SWD debugging headers Arduino Nano 33 BLE Sense Rev2 $48 - $55
Default Pick: For 90% of bench prototyping and IoT sensor nodes, the Arduino Nano 33 BLE Sense Rev2 is the definitive choice. The onboard HS3003 temp/humidity sensor and LSM9DS1 IMU save hours of wiring, and the native ArduinoBLE library support is rock-solid. All code and wiring in this guide targets this exact board variant.

Project Spec Sheet: BLE Environmental Beacon

This build creates a standalone BLE peripheral that broadcasts local temperature and humidity to any central device (like a smartphone running nRF Connect) without requiring a WiFi network.

  • Difficulty Rating: Intermediate (Requires understanding of BLE GATT services and 3.3V logic constraints)
  • Time to Build: 45 minutes
  • Target Board: Arduino Nano 33 BLE Sense Rev2 (nRF52840 SoC)

Parts List

  • 1x Arduino Nano 33 BLE Sense Rev2 (with headers soldered)
  • 1x 3.7V LiPo Battery (500mAh - 1000mAh) with JST-PH 2.0 connector
  • 1x Half-size breadboard (Note: Keep metal-backed breadboards away from the antenna)
  • Smartphone with nRF Connect for Mobile app installed

Wiring and Power Constraints for the nRF52840

The most common way to permanently kill an Arduino BLE board is ignoring the logic level. The nRF52840 is a strictly 3.3V device. Feeding 5V into any GPIO, I2C, or SPI pin will instantly destroy the silicon.

Pin Mapping and Power Table

Function Pin / Connector Voltage / Constraint
I2C Data (SDA) A4 3.3V max (Pulled up internally to 3.3V)
I2C Clock (SCL) A5 3.3V max
LiPo Battery Input JST-PH 2.0 Connector 3.7V nominal (4.2V fully charged). Onboard MP2625B handles charging.
5V Input VIN pin 5V to 18V (Regulated down to 3.3V onboard)
SWD Debug (Unpopulated) Bottom pads (SWDIO, SWCLK, GND, 3V3) Required for Segger J-Link hard-fault debugging.
Antenna Clearance: The nRF52840 antenna is located at the end of the board opposite the USB port. Do not place this end over the metal power rail of a breadboard or inside a metal enclosure. Metal within 10mm of the antenna will detune it and drop your BLE range from ~20 meters to under 1 meter.

Complete Compilable BLE Beacon Code

This code initializes the onboard HS3003 temperature/humidity sensor, creates a custom BLE GATT service, and updates the characteristics every 2 seconds. It includes explicit error handling for the BLE stack and sensor initialization.

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

// BLE Service and Characteristic UUIDs (Custom 128-bit)
#define BLE_UUID_ENV_SERVICE      "19B10000-E8F2-537E-4F6C-D104768A1214"
#define BLE_UUID_TEMP_CHAR        "19B10001-E8F2-537E-4F6C-D104768A1214"
#define BLE_UUID_HUM_CHAR         "19B10002-E8F2-537E-4F6C-D104768A1214"

// Pin Definitions (Built-in LED for status indication)
const int LED_PIN = LED_BUILTIN;

BLEService envService(BLE_UUID_ENV_SERVICE);
BLEFloatCharacteristic tempChar(BLE_UUID_TEMP_CHAR, BLERead | BLENotify);
BLEFloatCharacteristic humChar(BLE_UUID_HUM_CHAR, BLERead | BLENotify);

void setup() {
  Serial.begin(115200);
  while (!Serial); // Wait for serial monitor (optional, remove for battery operation)
  
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, LOW);

  // 1. Initialize Environmental Sensor
  if (!HS300x.begin()) {
    Serial.println("FATAL: Failed to initialize HS300x sensor! Check I2C bus.");
    while (1) {
      digitalWrite(LED_PIN, HIGH); delay(100);
      digitalWrite(LED_PIN, LOW); delay(100); // Fast blink = Sensor fail
    }
  }

  // 2. Initialize BLE Stack
  if (!BLE.begin()) {
    Serial.println("FATAL: BLE.begin() failed! Check 3.3V power rail stability.");
    while (1) {
      digitalWrite(LED_PIN, HIGH); delay(500);
      digitalWrite(LED_PIN, LOW); delay(500); // Slow blink = BLE fail
    }
  }

  // 3. Configure BLE Metadata
  BLE.setLocalName("Nano33-EnvBeacon");
  BLE.setAdvertisedService(envService);

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

  // 5. Set Initial Values
  tempChar.writeValue(0.0);
  humChar.writeValue(0.0);

  // 6. Start Advertising
  BLE.advertise();
  Serial.println("BLE Environmental Beacon active. Waiting for central connection...");
  digitalWrite(LED_PIN, HIGH); // Solid ON = Advertising
}

void loop() {
  // Poll for BLE events
  BLE.poll();

  // Read sensors and update characteristics
  float temperature = HS300x.readTemperature();
  float humidity = HS300x.readHumidity();

  // Basic sanity check for sensor read errors (HS300x returns NaN on I2C failure)
  if (!isnan(temperature) && !isnan(humidity)) {
    tempChar.writeValue(temperature);
    humChar.writeValue(humidity);
    
    Serial.print("Temp: "); Serial.print(temperature);
    Serial.print(" C | Hum: "); Serial.print(humidity); Serial.println(" %");
  } else {
    Serial.println("Warning: Sensor read returned NaN. I2C bus may be locked.");
  }

  delay(2000); // 2-second broadcast interval
}

Debugging: First Three Checks and Exact Error Strings

The nRF52840 SoftDevice (the underlying Bluetooth stack) is notoriously strict about memory and power. When your build fails, follow this exact diagnostic sequence.

The First Three Things to Check

  1. Power Rail Stability (Brownouts): The BLE radio draws peak current (~15mA spikes) during transmission. If your USB port or LiPo battery cannot supply this, the 3.3V regulator drops out, resetting the nRF52840 silently. Measure the 3.3V pin with a multimeter; it must not dip below 3.1V during a broadcast.
  2. USB Cable Data Lines: If the board isn't showing up in the Arduino IDE port list, you are likely using a charge-only USB cable. Swap to a verified data-sync cable.
  3. Antenna Shielding: If the code uploads and runs, but your phone cannot find the "Nano33-EnvBeacon" network, move the board off the metal breadboard and away from your laptop chassis.

Exact Error Strings and Ranked Causes

Exact Error String (Serial Monitor) Ranked Causes (Most to Least Likely) Fix
BLE.begin() failed! 1. 3.3V brownout during radio init.
2. I2C bus lockup preventing internal PMIC communication.
3. Corrupted SoftDevice firmware.
Add a 100µF decoupling capacitor across the 3.3V and GND pins. If that fails, double-tap the reset button to enter bootloader mode and re-flash the Arduino Mbed OS Nano 33 BLE core.
NRF_ERROR_NO_MEM 1. Too many characteristics allocated in the SoftDevice.
2. TX/RX buffer sizes exceed available RAM.
Reduce the number of BLE characteristics. The nRF52840 has 256KB RAM, but the SoftDevice reserves a large chunk. Keep custom UUID payloads under 20 bytes.
Warning: Sensor read returned NaN. 1. I2C address collision.
2. Missing pull-up resistors on external I2C devices.
3. HS3003 sensor hardware failure.
Disconnect any external I2C devices. The onboard sensors share the I2C bus; an external device pulling SDA low will crash the internal sensor reads.

Extending and Simplifying the Build

Once the basic beacon is running, you will likely need to adapt it for production or strip it down for a low-power wearable.

How to Extend (Add OTA and Deep Sleep)

  • Add Over-The-Air (OTA) Updates: To update firmware without a USB cable, implement the Bluetooth SIG standard DFU (Device Firmware Update) service. The ArduinoBLE library supports this via the BLE.setDeviceName() and custom DFU characteristic routing, though using the Nordic nRF Connect SDK (Zephyr) is preferred for production OTA.
  • Implement Deep Sleep: The nRF52840 supports System OFF mode, dropping current draw to ~1.5 µA. Use the LowPower.attachInterruptWakeup() function from the Arduino Low Power library to wake the board via the onboard LSM9DS1 accelerometer's tap-detect interrupt, rather than running a continuous delay() loop.

How to Simplify (Raw ADC and Minimal Footprint)

If you don't need the environmental sensors and just want to read a raw analog voltage (like a thermistor or battery monitor) and broadcast it:

  1. Remove the #include <Arduino_HS300x.h> library to save flash space.
  2. Replace the sensor read block with analogRead(A0).
  3. Map the 10-bit ADC value (0-1023) to a 3.3V float: float voltage = (analogRead(A0) * 3.3) / 1023.0;.
  4. Update the tempChar with this voltage value to broadcast raw telemetry to your central hub.

By terminating your hardware selection on the Nano 33 BLE Sense Rev2 and respecting the 3.3V logic and power constraints of the nRF52840, you eliminate the vast majority of hardware-level failures, leaving you to focus entirely on BLE GATT architecture and sensor logic.