I’m working on a project where I need to calculate the RSSI values from a receiver using ESP32. I have two separate codes: one calculates RSSI in ESP-NOW mode (without WiFi connection) and the other calculates RSSI while connected to a hotspot and Blynk. However, I’m having trouble combining these two methods.
Problem:
I want the default behavior to be ESP-NOW mode when the device is not connected to a hotspot. Once the ESP32 connects to a hotspot, it should switch to using ESP-NOW for RSSI calculations. When the device disconnects from the hotspot, it should revert back to ESP-NOW mode for RSSI calculations.
No matter what I try, I can’t seem to combine the two methods effectively.
Code 1: RSSI in ESP-NOW Mode (without Blynk and Hotspot)
#include <esp_now.h>
#include <WiFi.h>
#include <algorithm>
#include <esp_wifi.h> // Required for promiscuous mode
// ESP-NOW Receiver MAC Address
uint8_t receiverMacAddress[] = {0x20, 0x43, 0xA8, 0x63, 0x35, 0xA8};
// Hardware Pins
#define BUTTON1_PIN 15
#define BUTTON2_PIN 23
#define LED1_PIN 5
#define LED2_PIN 21
// RSSI Settings
#define SAMPLES 30
#define WINDOW_SIZE 5
#define DEAD_BAND_SPEED 0.05
#define MIN_DEADBAND 0.1
#define MAX_DEADBAND 2.0
// Kalman Filter Parameters
const float Q = 0.1; // Process noise
const float R = 1.5; // Measurement noise
struct PacketData {
byte activeButton;
};
struct PacketData2 {
int rssiValue;
};
// Global Variables
float rssiSamples[SAMPLES];
float kalmanFilteredRSSI = 0;
int sampleIndex = 0;
int latestRSSI = 0;
float kalmanRSSI = 0;
float kalmanP = 1;
float prevRSSI = 0;
unsigned long prevTime = 0;
PacketData data;
PacketData2 data2;
bool lastButton1State = false;
bool lastButton2State = false;
bool button1Active = false;
bool button2Active = true;
// Enhanced Filtering Functions
float applyMovingAverage(float newValue) {
static float buffer[WINDOW_SIZE] = {0};
static byte index = 0;
static float sum = 0;
sum -= buffer[index];
buffer[index] = newValue;
sum += buffer[index];
index = (index + 1) % WINDOW_SIZE;
return sum / WINDOW_SIZE;
}
float kalmanUpdate(float measurement) {
kalmanP = kalmanP + Q;
float K = kalmanP / (kalmanP + R);
kalmanRSSI = kalmanRSSI + K * (measurement - kalmanRSSI);
kalmanP = (1 - K) * kalmanP;
return kalmanRSSI;
}
float calculateDynamicDeadband() {
float variance = 0;
for(int i=0; i<SAMPLES; i++) {
variance += pow(rssiSamples[i] - kalmanFilteredRSSI, 2);
}
variance /= SAMPLES;
return constrain(DEAD_BAND_SPEED * (1 + variance*0.1), MIN_DEADBAND, MAX_DEADBAND);
}
void updateRSSI() {
// Take burst samples
float burstSamples[5];
for(int i=0; i<5; i++) {
burstSamples[i] = latestRSSI;
delay(2);
}
// Median filter
std::sort(burstSamples, burstSamples+5);
float medianRSSI = burstSamples[2];
// Apply processing chain
float smoothed = applyMovingAverage(medianRSSI);
kalmanFilteredRSSI = kalmanUpdate(smoothed);
}
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 OnDataRecv(const esp_now_recv_info_t *info, const uint8_t *incomingData, int len) {
memcpy(&data2, incomingData, sizeof(data2));
if (data2.rssiValue != 0) {
rssiSamples[sampleIndex] = data2.rssiValue;
sampleIndex = (sampleIndex + 1) % SAMPLES;
updateRSSI();
float dynamicDeadband = calculateDynamicDeadband();
if (abs(kalmanFilteredRSSI - prevRSSI) < dynamicDeadband) {
kalmanFilteredRSSI = prevRSSI;
}
prevTime = millis();
prevRSSI = kalmanFilteredRSSI;
Serial.printf("Raw: %6.2f | Filtered: %6.2f | Deadband: %4.2f\n",
data2.rssiValue, kalmanFilteredRSSI, dynamicDeadband);
}
}
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_STA);
// Initialize hardware
pinMode(BUTTON1_PIN, INPUT_PULLUP);
pinMode(BUTTON2_PIN, INPUT_PULLUP);
pinMode(LED1_PIN, OUTPUT);
pinMode(LED2_PIN, OUTPUT);
digitalWrite(LED1_PIN, LOW);
digitalWrite(LED2_PIN, HIGH); // LED2 דלוק כברירת מחדל
// Initialize ESP-NOW
if (esp_now_init() != ESP_OK) {
Serial.println("ESP-NOW Init Failed");
ESP.restart();
}
esp_now_peer_info_t peerInfo;
memset(&peerInfo, 0, sizeof(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");
ESP.restart();
}
// Register callbacks
esp_now_register_recv_cb(OnDataRecv);
esp_wifi_set_promiscuous(true);
esp_wifi_set_promiscuous_rx_cb(&promiscuousRxCB);
Serial.println("Transmitter Ready - System OFF (Default)");
}
void loop() {
// Button handling
bool currentButton1State = !digitalRead(BUTTON1_PIN);
if (currentButton1State && !lastButton1State) {
button1Active = true;
button2Active = false;
data.activeButton = 1;
digitalWrite(LED1_PIN, HIGH);
digitalWrite(LED2_PIN, LOW);
Serial.println("🔴 System ON");
}
lastButton1State = currentButton1State;
bool currentButton2State = !digitalRead(BUTTON2_PIN);
if (currentButton2State && !lastButton2State) {
button1Active = false;
button2Active = true;
data.activeButton = 2;
digitalWrite(LED1_PIN, LOW);
digitalWrite(LED2_PIN, HIGH);
Serial.println("🟢 System OFF");
}
lastButton2State = currentButton2State;
// Send data
esp_now_send(receiverMacAddress, (uint8_t *)&data, sizeof(data));
delay(30);
}
Code number 2 (Blynk and hotspot on RSSI is good, when Blynk and hotspot off RSSI is bad):
#define BLYNK_TEMPLATE_ID "--"
#define BLYNK_TEMPLATE_NAME "--"
#define BLYNK_AUTH_TOKEN "--"
#include <esp_now.h>
#include <WiFi.h>
#include <BlynkSimpleEsp32.h>
// WiFi Credentials
char ssid[] = "---";
char pass[] = "---";
// ESP-NOW Receiver MAC Address
const uint8_t receiverMacAddress[] = {0x20, 0x43, 0xA8, 0x63, 0x35, 0xA8};
// Hardware Pins
#define BUTTON1_PIN 15
#define BUTTON2_PIN 23
#define LED1_PIN 5
#define LED2_PIN 21
// RSSI Settings
#define WINDOW_SIZE 7
#define RSSI_TIMEOUT 2000 // ms
#define CONTROL_SEND_INTERVAL 30 // ms
#define RSSI_SEND_INTERVAL 100 // ms
// Kalman Filter Parameters
const float Q = 0.01; // Process noise
const float R = 2.0; // Measurement noise
struct PacketData {
byte activeButton;
};
struct PacketData2 {
int rssiValue;
};
// Global Variables
float kalmanRSSI = -70; // Initial reasonable value
float kalmanP = 1;
int latestRSSI = -70;
unsigned long lastPacketTime = 0;
bool rssiTimeout = false;
PacketData data;
PacketData2 data2;
bool lastButton1State = false;
bool lastButton2State = false;
bool button1Active = false;
bool button2Active = false;
bool blynkConnected = false;
unsigned long lastBlynkUpdate = 0;
bool wifiConnected = false;
// Moving Average Filter
float applyMovingAverage(float newValue) {
static float buffer[WINDOW_SIZE] = {0};
static byte index = 0;
static float sum = 0;
sum -= buffer[index];
buffer[index] = newValue;
sum += buffer[index];
index = (index + 1) % WINDOW_SIZE;
return sum / WINDOW_SIZE;
}
// Improved Kalman Filter with timeout handling
float kalmanUpdate(float measurement) {
// Check for timeout
if(millis() - lastPacketTime > RSSI_TIMEOUT) {
rssiTimeout = true;
return kalmanRSSI; // Return last good value
}
// Validate measurement (-100dBm to 0dBm)
if(measurement > 0 || measurement < -100) return kalmanRSSI;
rssiTimeout = false;
kalmanP = kalmanP + Q;
float K = kalmanP / (kalmanP + R);
kalmanRSSI = kalmanRSSI + K * (measurement - kalmanRSSI);
kalmanP = (1 - K) * kalmanP;
return constrain(kalmanRSSI, -100, 0);
}
void OnDataRecv(const esp_now_recv_info_t *info, const uint8_t *incomingData, int len) {
// Get RSSI from ESP-NOW packet metadata
latestRSSI = info->rx_ctrl->rssi;
lastPacketTime = millis();
// Apply processing chain
float smoothed = applyMovingAverage(latestRSSI);
kalmanRSSI = kalmanUpdate(smoothed);
// Send to Blynk if connected
if(blynkConnected && millis() - lastBlynkUpdate >= 500) {
lastBlynkUpdate = millis();
Blynk.virtualWrite(V1, kalmanRSSI);
}
// Debug output
Serial.printf("RSSI: %.2f dBm\n", kalmanRSSI);
}
BLYNK_CONNECTED() {
blynkConnected = true;
Blynk.syncVirtual(V0);
Blynk.virtualWrite(V0, button1Active ? 1 : 0);
Blynk.virtualWrite(V1, kalmanRSSI);
}
BLYNK_DISCONNECTED() {
blynkConnected = false;
}
BLYNK_WRITE(V0) {
int buttonState = param.asInt();
if(buttonState == 1 && !button1Active) {
button1Active = true;
button2Active = false;
data.activeButton = 1;
digitalWrite(LED1_PIN, HIGH);
digitalWrite(LED2_PIN, LOW);
Serial.println("🔴 System ON (from Blynk)");
}
else if(buttonState == 0 && !button2Active) {
button1Active = false;
button2Active = true;
data.activeButton = 2;
digitalWrite(LED1_PIN, LOW);
digitalWrite(LED2_PIN, HIGH);
Serial.println("🟢 System OFF (from Blynk)");
}
}
void maintainWiFiConnection() {
static unsigned long lastCheck = 0;
if(millis() - lastCheck >= 10000) { // Check every 10 seconds
lastCheck = millis();
if(WiFi.status() != WL_CONNECTED) {
WiFi.disconnect();
WiFi.begin(ssid, pass);
wifiConnected = false;
} else if(!wifiConnected) {
wifiConnected = true;
Serial.println("WiFi reconnected");
}
}
}
void setup() {
Serial.begin(115200);
// Initialize hardware
pinMode(BUTTON1_PIN, INPUT_PULLUP);
pinMode(BUTTON2_PIN, INPUT_PULLUP);
pinMode(LED1_PIN, OUTPUT);
pinMode(LED2_PIN, OUTPUT);
digitalWrite(LED1_PIN, LOW);
digitalWrite(LED2_PIN, HIGH);
// Start WiFi in STA mode only
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, pass);
Serial.println("Connecting to WiFi...");
// Initialize ESP-NOW
if(esp_now_init() != ESP_OK) {
Serial.println("ESP-NOW Init Failed");
ESP.restart();
}
esp_now_peer_info_t peerInfo;
memset(&peerInfo, 0, sizeof(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");
ESP.restart();
}
esp_now_register_recv_cb(OnDataRecv);
// Initialize Blynk with separate connection handling
Blynk.config(BLYNK_AUTH_TOKEN);
Blynk.connect(1000); // Shorter timeout
blynkConnected = Blynk.connected();
Serial.println("System Ready");
}
void loop() {
// Handle Blynk connection separately
static unsigned long lastBlynkReconnect = 0;
if(!blynkConnected && millis() - lastBlynkReconnect > 5000) {
lastBlynkReconnect = millis();
if(WiFi.status() == WL_CONNECTED) {
Blynk.connect(1000);
blynkConnected = Blynk.connected();
if(blynkConnected) Serial.println("Reconnected to Blynk");
}
}
if(blynkConnected) {
Blynk.run();
}
// Maintain WiFi connection
maintainWiFiConnection();
// Button handling
bool currentButton1State = !digitalRead(BUTTON1_PIN);
if(currentButton1State && !lastButton1State) {
button1Active = true;
button2Active = false;
data.activeButton = 1;
digitalWrite(LED1_PIN, HIGH);
digitalWrite(LED2_PIN, LOW);
Serial.println("🔴 System ON (Physical Button)");
if(blynkConnected) {
Blynk.virtualWrite(V0, 1);
}
delay(100); // Simple debounce
}
lastButton1State = currentButton1State;
bool currentButton2State = !digitalRead(BUTTON2_PIN);
if(currentButton2State && !lastButton2State) {
button1Active = false;
button2Active = true;
data.activeButton = 2;
digitalWrite(LED1_PIN, LOW);
digitalWrite(LED2_PIN, HIGH);
Serial.println("🟢 System OFF (Physical Button)");
if(blynkConnected) {
Blynk.virtualWrite(V0, 0);
}
delay(100); // Simple debounce
}
lastButton2State = currentButton2State;
// Non-blocking control packet send
static unsigned long lastControlSend = 0;
if(millis() - lastControlSend >= CONTROL_SEND_INTERVAL) {
esp_err_t result = esp_now_send(receiverMacAddress, (uint8_t *)&data, sizeof(data));
lastControlSend = millis();
}
// Non-blocking RSSI packet send
static unsigned long lastRSSISend = 0;
if(millis() - lastRSSISend >= RSSI_SEND_INTERVAL) {
data2.rssiValue = latestRSSI;
esp_now_send(receiverMacAddress, (uint8_t *)&data2, sizeof(data2));
lastRSSISend = millis();
}
// Handle RSSI timeout
if(!rssiTimeout && millis() - lastPacketTime > RSSI_TIMEOUT) {
rssiTimeout = true;
Serial.println("Warning: RSSI timeout - no recent packets");
}
}
What I Need Help With:
- How can I effectively switch between ESP-NOW mode and WIFI depending on the WiFi connection status?
- Any suggestions on cleaning up or combining the code better for my use case?
I would really appreciate any help or suggestions!