Arduino Distance Meassuring through Bluetooth Low Energy Signal Strength (RSSI) Project

Hi guys!

After endless reading sessions into the Esp 32's API, debugging and many trial and error sessions we finally pulled it out;

This code right here can read RSSI, meassure distance (can also be manually calibrated), can activate leds and vibration motors with pattern customisation and, of course it has an active noise filter (lighter than kalman) which can also be calibrated to fit your needs.

This is the code, we tried commenting as many things out as possible so that you can easily modify whatever you need to; uncomment the "define verbose" line for more Arduino monitor feedback, it will print out useful info whenever you need it to :slight_smile:

The whole process is possible by putting the Esp32 in Server mode, we started from the server example and turned it into what it is here.

#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLEAdvertisedDevice.h>
 
// Comment this line out for the final version (terse output in the serial monitor)
//#define VERBOSE

// Comment this out to re-enable connection signalling on pin 8
//#define CONNECT_SIGNALLING

BLECharacteristic *pCharacteristic;
 
// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
 
#define SERVICE_UUID        "6012d087-05f8-41b0-90ed-07a75e80a104"
#define CHARACTERISTIC_UUID "73af693f-f43f-4997-817c-3c226530ad76"
#define DESCRIPTOR_UUID		"e8f45c7e-8be5-4918-bb84-467d3fd354aa"


#ifdef CONNECT_SIGNALLING
// That pin used to be 2, feature removed
const uint8_t LED_CONNECT_PIN = 7;		// Can be any other pin
#endif

const uint8_t LED_ON_PIN = 2; //PUT INTERNAL LED PIN HERE
const uint8_t MOTOR_PIN = 6; // PUT YOUR MOTOR'S PIN HERE

 
template <typename T, size_t N>
void show_address(const T (&address)[N])
{
	Serial.print(address[0], HEX);
	for (uint8_t i = 1; i < N; i++)
		Serial.printf(":%02x", address[i]);
}
 
 
class Monitor: public BLEServerCallbacks
{
public:
	static int16_t connection_id;

	// Motor state bits
	enum motor_states { ENABLED, ON };
	static bool motor_state;

	// Durations for motor ON and motor OFF in milliseconds
	// Note: make sure OFF_DELAY + ON_DELAY is equal to 1000!
	static constexpr uint32_t ON_DELAY = 300;
	static constexpr uint32_t OFF_DELAY = 700;

	/* dBm to distance parameters; How to update distance_factor 1.place the
	 * phone at a known distance (2m, 3m, 5m, 10m) 2.average about 10 RSSI
	 * values for each of these distances, Set distance_factor so that the
	 * calculated distance approaches the actual distances, e.g. at 5m. */
	static constexpr float reference_power  = -50; //rssi reffrence 
	static constexpr float distance_factor = 3.5; 
	 
	static constexpr int8_t motor_threshold = -65;
 
	uint8_t get_value() { return value++; }
	esp_err_t get_rssi() { return esp_ble_gap_read_rssi(remote_addr); }
 
	static float get_distance(const int8_t rssi)
	{ return pow(10, (reference_power - rssi)/(10*distance_factor)); }
 
private:
    void onConnect(BLEServer* pServer, esp_ble_gatts_cb_param_t *param) 
    {
    	// Update connection variables
		connection_id = param->connect.conn_id;
    	memcpy(&remote_addr, param->connect.remote_bda, sizeof(remote_addr));

		// Install the RSSI callback
    	BLEDevice::setCustomGapHandler(&Monitor::rssi_event);

#ifdef VERBOSE
    	// Show new connection info
    	Serial.printf("Connection #: %i, remote: ", connection_id);
		show_address(param->connect.remote_bda);
		Serial.printf(" [Callback installed]\n");
#endif

#ifdef CONNECT_SIGNALLING
	    digitalWrite(LED_CONNECT_PIN, HIGH);
#endif
    }
 
    void onDisconnect(BLEServer* pServer)
    {
		Serial.printf("Connection #%i closed\n", connection_id);
		BLEDevice::setCustomGapHandler(nullptr);
		connection_id = -1;

#ifdef CONNECT_SIGNALLING
		digitalWrite(LED_CONNECT_PIN, LOW);
#endif
    }
 
    static void rssi_event(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param);
 
	static esp_bd_addr_t remote_addr;
	uint8_t value = 0;
};
 
int16_t Monitor::connection_id = -1;
bool Monitor::motor_state = 0;
esp_bd_addr_t Monitor::remote_addr = {};
 
void Monitor::rssi_event(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
{
    static int16_t rssi_average = 0;
 
#ifdef VERBOSE
	show_address(remote_addr);
#endif
    if (event == ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT)
    {
        // Adjust damping_factor to lower values to have a more reactive response
        const float damping_factor = 0.8;
        rssi_average = rssi_average * damping_factor + 
        	param->read_rssi_cmpl.rssi * (1 - damping_factor);

        // Flag motor as enabled, the loop function will turn it on in bursts
        if (rssi_average < motor_threshold)
	        motor_state |= _BV(ENABLED);
	    else
	    {
	    	motor_state &= ~_BV(ENABLED);
	    	digitalWrite(MOTOR_PIN, LOW);
	    }

#ifdef VERBOSE
		Serial.printf(", rssi=%hi, distance~=%g",
#else
		Serial.printf("%hi, %g\n",
#endif
			param->read_rssi_cmpl.rssi, get_distance(rssi_average)
		);
    }
#ifdef VERBOSE
	Serial.printf("\n");
#endif
}
 
Monitor monitor;
 
 
void setup()
{
	Serial.begin(9600);

	pinMode(MOTOR_PIN, OUTPUT);
	pinMode(LED_ON_PIN, OUTPUT);

#ifdef CONNECT_SIGNALLING
	pinMode(LED_CONNECT_PIN, OUTPUT);
#endif
 
	// Create the BLE Device
	BLEDevice::init("Esp-32");
 
	// Create the BLE Server
	BLEServer *pServer = BLEDevice::createServer();
	pServer->setCallbacks(&monitor);
 
	// Create the BLE Service
	BLEService *pService = pServer->createService(SERVICE_UUID);
 
	// Create a BLE Characteristic
	pCharacteristic = pService->createCharacteristic(
		CHARACTERISTIC_UUID,
		BLECharacteristic::PROPERTY_READ   |
		BLECharacteristic::PROPERTY_WRITE  |
		BLECharacteristic::PROPERTY_NOTIFY |
		BLECharacteristic::PROPERTY_INDICATE
	);
 
	// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
	// Create a BLE Descriptor
	pCharacteristic->addDescriptor(new BLEDescriptor(DESCRIPTOR_UUID));
 
	// Start the service
	pService->start();

	// Blink the LED on pin 8 three times
	for (uint8_t i = 0; i < 3; i++)
	{
		digitalWrite(LED_ON_PIN, HIGH);
		delay(500);
		digitalWrite(LED_ON_PIN, LOW);
		delay(500);
	}
 
	// Start advertising
	pServer->getAdvertising()->start();
	Serial.println("Waiting for incoming connections...");
}
 
void loop()
{
	static const uint32_t REFRESH_DELAY = 1000;
	static uint32_t next_detection;

	uint32_t current_time = millis();
	if (Monitor::connection_id != -1)
	{
		if (current_time - next_detection >= REFRESH_DELAY)
		{
			// Prepare for the next detection
			next_detection += REFRESH_DELAY;

			// Update the internal value (what for?)
			auto value = monitor.get_value();
			//Serial.printf("*** NOTIFY: %d ***\n", value);
			pCharacteristic->setValue(&value, sizeof(value));
			pCharacteristic->notify();
	 
			// Request RSSI from the remote address
			if (monitor.get_rssi() != ESP_OK)
				Serial.println("RSSI request failed");
		}
	}

	// Let's turn the motor ON/OFF when enabled. The motor control pin will
	// toggle every time the threshold below is reached.
	static uint32_t motor_threshold;
	if (Monitor::motor_state & _BV(Monitor::ENABLED))
	{
		// Determine the next duration for either ON and OFF states
		const uint32_t next_threshold = Monitor::motor_state & _BV(Monitor::ON) ?
			Monitor::OFF_DELAY : Monitor::ON_DELAY;

		if (current_time - motor_threshold >= next_threshold)
		{
			// Toggle motor state
			Monitor::motor_state ^= _BV(Monitor::ON);
	        digitalWrite(MOTOR_PIN,
	        	Monitor::motor_state & _BV(Monitor::ON) ? HIGH : LOW);
        }
	}
}

I hope that you will use this code to come up with new ideas and solutions for your projects and whatnot.

Also, the link for the Bluetooth Classic version of the code can be found here:

Arduino Distance Meassuring through Bluetooth Classic Signal Strength (RSSI) Project - App Showcase - MIT App Inventor Community

If you need help with any of the code I am sure the community will be able to answer your questions and perhaps assist you, post questions here about the BLE version and post questions about the BT Classic version in the post from link above.

As always, huge thanks to everyone that helped me and to the other guys from Arduino and Electronicity Discord servers.

Remember guys, helping others is one of the greatest things you can do and I hope this will help you complete your projects or start new ones.

Have fun! :slight_smile:

5 Likes

The code seems to be compiling perfect but when I try to use an app to connect a device to it I am unable to find anything to connect to. I was wondering if you have any suggestions on how to fix this problem.

Thank you!