To build a reliable bluetooth proximity sensor, bypass external UART modules (like the HC-05 or HM-10) and use an ESP32 with native BLE (Bluetooth Low Energy). If you must use an external BLE module on a 5V Arduino, wire it via the UART bus at 9600 baud using a BSS138 logic level shifter, never direct 5V. For short-range wired alternatives, I2C proximity sensors (like the VL53L0X Time-of-Flight) require 4.7kΩ pull-up resistors to 3.3V and operate at 400kHz. The physical layer dictates your success; ignoring voltage thresholds or bus capacitance will brick your module or yield phantom readings.

Bus Mechanics: UART (Wireless Bridge) vs I2C (Wired Proximity)

When designing a proximity system, you are choosing between a wireless bridge (UART to BLE module) or a direct wired sensor (I2C). Understanding the physical layer of both buses prevents 90% of bench failures.

Feature UART (for HM-10/HC-05 BLE Modules) I2C (for VL53L0X / APDS-9960 Wired Sensors) Native BLE (ESP32 / nRF52 SoC Internal)
Wires Required 2 (TX, RX) + Power/GND 2 (SDA, SCL) + Power/GND 0 (Antenna trace only)
Max Speed 115,200 baud (typical), up to 1Mbps 100kHz (Standard), 400kHz (Fast) 1 Mbps (BLE 4.2), 2 Mbps (BLE 5.0)
Addressing None (Point-to-Point serial) 7-bit hardware address (e.g., 0x29) MAC Address / UUID
Max Distance ~15cm (inter-MCU to module trace) ~30cm (bus capacitance limit) 10m - 50m (wireless RSSI range)
Voltage Logic 3.3V strictly (5V destroys HM-10) 3.3V or 5V (depends on pull-up rail) 3.3V internal

Physical Wiring and Pull-Up Requirements

The physical layer is where most proximity projects fail. A bluetooth proximity sensor relying on an external HM-10 module communicates via UART, but the HM-10 is strictly a 3.3V device. Feeding it 5V from an Arduino Uno's TX pin will fry the module's internal voltage regulator within seconds.

Warning: Never connect a 5V microcontroller TX pin directly to a 3.3V BLE module RX pin. Use a bidirectional logic level shifter (like the TXS0108E or a discrete BSS138 MOSFET circuit) or a simple voltage divider (1kΩ and 2kΩ resistors) on the TX line.

If you pivot to a wired I2C proximity sensor (like the STMicroelectronics VL53L0X Time-of-Flight sensor) for sub-millimeter accuracy, the bus requires open-drain pull-up resistors. The I2C spec mandates pull-ups to establish the logic HIGH state. For a 100kHz bus with standard capacitance (<200pF), use 4.7kΩ resistors tied to the 3.3V rail. If you push the bus to 400kHz (Fast Mode), drop the pull-ups to 2.2kΩ to decrease the RC rise time of the square wave edges.

Decision Path: Which Protocol Fits Your Proximity Project?

Use this decision matrix to lock in your hardware architecture. Do not mix wired I2C for room-scale presence detection; use BLE RSSI (Received Signal Strength Indicator) instead.

Project Constraint Choose UART + External BLE Module Choose I2C Wired Sensor Choose Native BLE SoC
Detection Range 1m - 10m (RSSI based) 1cm - 200cm (Time-of-Flight / IR) 1m - 50m (RSSI based)
Device Count 1 module per UART port Up to 110+ on one bus (if addresses differ) Up to 20 simultaneous connections
Host MCU Arduino Uno / Mega (5V) Any MCU with I2C peripheral ESP32 / nRF52840 / Raspberry Pi Pico W
PCB Space High (Module + Level Shifter) Low (Single IC + 2 resistors) Lowest (Integrated RF)

The Final Verdict: If you are building a room-scale bluetooth proximity sensor in 2026, buy the ESP32-C3 SuperMini (typically $3.50 USD). It has native BLE 5.0, operates at 3.3V, eliminates the need for external UART modules and level shifters, and allows you to scan for iBeacons or BLE tags directly using the NimBLE stack. Only use the UART + HM-10 route if you are retrofitting a legacy 5V Arduino Mega system that cannot be replaced.

The Classic Failures: Baud Mismatches, Missing Pull-Ups, and Address Clashes

When your proximity sensor returns garbage data or flatlines, check these three physical and link-layer failures first.

  • UART Baud Mismatch (The HM-10 Trap): The HM-10 defaults to 9600 baud, but many clones ship at 115200. If your Serial monitor shows inverted question marks or nothing at all, send the AT command AT+BAUD? at both 9600 and 115200 to query the actual rate. Mismatched baud rates cause framing errors that the MCU silently drops.
  • Missing I2C Pull-Ups (Floating Bus): If your wired VL53L0X fails to initialize (Wire.endTransmission() returns 2), measure the SDA and SCL lines with a multimeter. If they read 0.0V or float randomly, you forgot the pull-up resistors. The I2C peripheral can pull the line LOW, but without pull-ups, it can never drive it HIGH.
  • I2C Address Clash: If you wire two identical proximity sensors to the same I2C bus, they will both respond to address 0x29, causing data collisions and bus lockups. You must use a sensor breakout with an address-select jumper, or use an I2C multiplexer (like the TCA9548A) to isolate the buses.

Minimal Working Exchange: UART AT Commands for BLE Discovery

Before writing complex proximity logic, verify the physical UART bus by sending a raw AT command to the BLE module. This minimal exchange configures the HM-10 as a central device, scans for nearby BLE beacons, and returns their MAC addresses and RSSI (signal strength) values. Lower RSSI (e.g., -90dBm) means further away; higher RSSI (e.g., -40dBm) means closer.

Wiring assumption: Arduino Uno (5V). HM-10 VCC to 3.3V, GND to GND. Uno TX (Pin 11) through BSS138 level shifter to HM-10 RX. HM-10 TX directly to Uno RX (Pin 10). Baud: 9600.

#include <SoftwareSerial.h>

// SoftwareSerial on pins 10 (RX) and 11 (TX)
SoftwareSerial bleSerial(10, 11); 

void setup() {
  Serial.begin(9600);   // Debug console
  bleSerial.begin(9600); // UART bus to HM-10
  
  // Wait for module boot
  delay(1000); 
  
  // Set HM-10 to Central Mode (Role 1) to scan for other BLE devices
  bleSerial.print("AT+ROLE1");
  delay(500);
  
  // Clear any previous serial buffer
  while(bleSerial.available()) bleSerial.read();
}

void loop() {
  // Send discovery command to scan for nearby BLE proximity beacons
  bleSerial.print("AT+DISC?");
  
  unsigned long startTime = millis();
  // Read responses for 5 seconds
  while(millis() - startTime < 5000) {
    if(bleSerial.available()) {
      char c = bleSerial.read();
      Serial.print(c); // Prints OK+DISC:MAC:RSSI to console
    }
  }
  
  Serial.println("\n--- Scan Cycle Complete ---");
  delay(5000); // Wait 5 seconds before next scan
}

How to Sniff and Debug the Bus

When code and wiring look correct but the bus is dead, you must look at the physical signals. Relying solely on Serial.print() hides physical layer faults.

Sniffing the UART Bus: Connect a $15 USB Logic Analyzer (like the Saleae Logic clone) to the TX and RX lines. Use PulseView or the Saleae software to decode the UART protocol. Set the decoder to 9600 baud, 8N1. If you see perfect square waves on the TX line but no response on the RX line, your module is dead or unpowered. If the square waves look like sloped triangles, your baud rate is too high for the wire capacitance.

Sniffing the I2C Bus: Hook an oscilloscope to SDA and SCL. A healthy I2C bus shows crisp 3.3V square waves with slight rounding on the rising edges (due to the RC curve of the pull-ups). If the HIGH level only reaches 1.5V, your pull-up resistors are too weak, or another device is actively dragging the bus low (a classic sign of a shorted sensor IC).

Sniffing the BLE Proximity Link: To verify that your bluetooth proximity sensor is actually broadcasting or scanning correctly without writing MCU code, use the nRF Connect for Mobile app on your smartphone. This app acts as an independent BLE sniffer. If your ESP32 or HM-10 is broadcasting an iBeacon, nRF Connect will show the exact UUID, Major/Minor values, and the raw RSSI in dBm. If the app sees it but your MCU doesn't, the fault is in your firmware's parsing logic, not the RF environment.