Arduino Distance Meassuring Through Bluetooth Low Energy Signal ...

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:

Tag » Arduino Ble Rssi Example