ESP32 - RSSI and NEO-6M

I'm working on an ESP32-based project that estimates distance using RSSI. My setup consists of two ESP32 modules: one is stationary and continuously transmits signals, while the other is mobile and measures the received signal strength (RSSI). When the RSSI drops below a certain threshold for 3 seconds, an alarm system (LED, buzzer, and servo motor) activates. If the RSSI remains strong for 3 seconds, the system deactivates.

Here’s my main issue:

  • RSSI fluctuations are quite high, even when the distance remains the same.
  • The signal is affected by obstacles and reflections, making it unreliable.
  • I’m using ESP-NOW for communication and also tried promiscuous mode to capture packets directly.

My questions:

  1. How can I make the RSSI measurements more stable? Would filtering techniques like an average filter or Kalman filter help?
  2. Are there any alternative methods to estimate distance more accurately using ESP32?
  3. I’m considering using the NEO-6M GPS module alongside RSSI. Would this improve distance estimation?
  4. Is it possible to combine both RSSI-based estimation and GPS data for better accuracy? If so, how?

reciver code

#include <ESP32Servo.h>
#include <esp_now.h>
#include <WiFi.h>
#include <esp_wifi.h>

#define SERVO_PIN 26    
#define RED_LED_PIN 33  
#define GREEN_LED_PIN 25 
#define BUZZER_PIN 27   

Servo servo;
bool systemActive = false;  
int RSSI_THRESHOLD = -58;  
volatile int latestRSSI = 0;  
unsigned long weakSignalStartTime = 0;    // Timer for weak signal detection
unsigned long strongSignalStartTime = 0;  // Timer for strong signal detection
const unsigned long triggerDelay = 3000;  // 3-second delay

bool buzzerActive = false;
unsigned long lastBuzzerTime = 0;
int buzzerFreq = 1000;  
bool increasing = true;

void promiscuousRxCB(void *buf, wifi_promiscuous_pkt_type_t type) {
    if (type == WIFI_PKT_MGMT) {  
        wifi_promiscuous_pkt_t *pkt = (wifi_promiscuous_pkt_t *)buf;
        latestRSSI = pkt->rx_ctrl.rssi;  
    }
}

void updateSiren() {
    if (buzzerActive) {
        unsigned long currentMillis = millis();
        if (currentMillis - lastBuzzerTime >= 50) { 
            lastBuzzerTime = currentMillis;
            buzzerFreq += increasing ? 100 : -100;
            if (buzzerFreq >= 3000) increasing = false;
            if (buzzerFreq <= 1000) increasing = true;
            tone(BUZZER_PIN, buzzerFreq);
        }
    } else {
        noTone(BUZZER_PIN);
    }
}

void activateSystem() {
    Serial.println("🔴 System Activated!");
    systemActive = true;
    servo.attach(SERVO_PIN);
    servo.write(180);  
    delay(300);
    digitalWrite(RED_LED_PIN, HIGH);  
    digitalWrite(GREEN_LED_PIN, LOW);
    buzzerActive = true;
}

void deactivateSystem() {
    Serial.println("🟢 System Deactivated!");
    systemActive = false;
    servo.write(0);
    delay(300);
    servo.detach();
    digitalWrite(RED_LED_PIN, LOW);  
    digitalWrite(GREEN_LED_PIN, HIGH);
    buzzerActive = false;
}

void OnDataRecv(const esp_now_recv_info_t *info, const uint8_t *incomingData, int len) {
    Serial.print("📡 RSSI: ");
    Serial.println(latestRSSI);

    if (latestRSSI < RSSI_THRESHOLD) {
        Serial.println("📉 Weak signal detected!");
        if (weakSignalStartTime == 0) {  
            weakSignalStartTime = millis();  
            Serial.println("⏳ Starting 3-second timer for activation...");
        }
        strongSignalStartTime = 0;  // Reset the deactivation timer

        if (!systemActive && (millis() - weakSignalStartTime >= triggerDelay)) {
            Serial.println("🚨 Weak signal for 3 seconds! Activating system...");
            activateSystem();
        }
    } else {  
        Serial.println("📶 Strong signal detected!");
        if (strongSignalStartTime == 0) {  
            strongSignalStartTime = millis();
            Serial.println("⏳ Starting 3-second timer for deactivation...");
        }
        weakSignalStartTime = 0;  // Reset the activation timer

        if (systemActive && (millis() - strongSignalStartTime >= triggerDelay)) {
            Serial.println("✅ Strong signal for 3 seconds! Deactivating system...");
            deactivateSystem();
        }
    }
}

void setup() {
    Serial.begin(115200);
    Serial.println("📡 Initializing ESP32...");
    WiFi.mode(WIFI_STA);
    WiFi.disconnect();
    delay(100);

    if (esp_now_init() != ESP_OK) {
        Serial.println("❌ ESP-NOW initialization failed!");
        return;
    }
    Serial.println("✅ ESP-NOW initialized successfully.");
    esp_now_register_recv_cb(OnDataRecv);

    pinMode(RED_LED_PIN, OUTPUT);
    pinMode(GREEN_LED_PIN, OUTPUT);
    pinMode(BUZZER_PIN, OUTPUT);
    digitalWrite(RED_LED_PIN, LOW);
    digitalWrite(GREEN_LED_PIN, HIGH);
    noTone(BUZZER_PIN);

    servo.attach(SERVO_PIN);
    delay(200);
    servo.write(0);
    delay(500);
    servo.detach();

    esp_wifi_set_promiscuous(true);
    esp_wifi_set_promiscuous_rx_cb(&promiscuousRxCB);

    Serial.println("🚀 System is ready!");
}

void loop() {
    updateSiren();
}

transmitter code

#include <esp_now.h>
#include <WiFi.h>

uint8_t receiverMacAddress[] = {0x20, 0x43, 0xA8, 0x63, 0x35, 0xA8};  // Update with the receiver's MAC address

struct PacketData {
    byte switch1Value;
};
PacketData data;

void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
    Serial.print("🔵 Packet Send Status: ");
    Serial.println(status == ESP_NOW_SEND_SUCCESS ? "✅ Success" : "❌ Failure");
}

void setup() {
    Serial.begin(115200);
    WiFi.mode(WIFI_STA);

    if (esp_now_init() != ESP_OK) {
        Serial.println("❌ ESP-NOW initialization failed!");
        return;
    }
    Serial.println("✅ ESP-NOW initialized successfully.");
    esp_now_register_send_cb(OnDataSent);

    esp_now_peer_info_t peerInfo;
    memcpy(peerInfo.peer_addr, receiverMacAddress, 6);
    peerInfo.channel = 0;
    peerInfo.encrypt = false;

    if (esp_now_add_peer(&peerInfo) != ESP_OK) {
        Serial.println("❌ Failed to add peer.");
        return;
    }
    Serial.println("✅ Peer added successfully.");
}

void loop() {
    data.switch1Value = 1;
    esp_err_t result = esp_now_send(receiverMacAddress, (uint8_t *)&data, sizeof(data));
    if (result == ESP_OK) {
        Serial.println("📡 Packet sent successfully.");
    } else {
        Serial.println("❌ Error sending packet.");
    }
    delay(1000);  
}

Yep, that happens.

Lots of people, assume you can use RSSI but it can vary considerably due to objects blocking the path or changes in orientation of the TX and RX antennas. So its mostly a waste of time, although maybe you could tell the distance between close, say within 5m, and far away say 50m.

Easy to test, just get the RSSI printed out to serial terminal, or recorded onto an SD card, as a CSV file and check the results in a PC spreadsheet.

If the TX and RX antennas are fixed in location and orientation, say in a large open field, with no objects in the path, then the RSSI can be fairly stable.

GPSs are OK for distance measuring, but only outdoors, and the variation in location can be +/- 5m or more at each reported fix.

Most people learning about radio communications think of that at some point, then if they do the experiments, they discover that the idea doesn't work well enough to be useful.

RSSI depends more strongly antenna quality, relative antenna orientation and on the presence of nearby absorbing and reflecting surfaces than it does on distance.

Okay, so do you have an idea on how I can improve my project?

You could start by reading up on what people have done in the past, using web search phrases like "rssi location estimation"

One randomly chosen example

Its not really possible to advise.

You have not said what the project actually is or where its located, indoors, outdoors, underground etc.

And no information on distances or the location accuracy or resolution you want.

The project consists of two separate ESP32 modules: one as a receiver and the other as a transmitter. Each ESP32 is mounted on a different breadboard.

The transmitter ESP32 is placed on a breadboard along with additional components, including a buzzer, an LED, and a servo motor. When the RSSI value between the receiver and the transmitter reaches a certain threshold (e.g., -50 dBm), the LED, buzzer, and servo motor on the transmitter’s breadboard will be activated.

The project is intended for indoor use, with an expected operating distance of approximately 100–150 meters between the two breadboards.

You have not said what sort of location accuracy you were after ......

However indoor environments have too many obstacles, walls, people etc to make distance 'estimation' practical with simple RSSI measurements. Walls can absorb a great deal of RF so long range RF systems can be needed indoors. If it were easy you would be able to find heaps of online examples and projects.

Maybe you need a system such as Posyx;

https://www.pozyx.io/