ESP32 ESP-NOW and Blynk

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!

It’s extremely difficult to understand your code without some info on the types and purposes of the widgets you have connected to your various virtual pins.

I have no idea why you’re using a variable to track whether your are connected to Blynk when using Blynk.connected() would be far simpler.

BLYNK_CONNECTED() is a function built-in to the Blynk library that is automatically called whenever your device connects of re-connects to the server.
I’m not aware of a matching BLYNK_DISCONNECTED() built-in function, so I suspect that this is never called.

The serial prints in your BLYNK_WRITE(V0) function are confusing, as they don’t necessarily reflect the actual reality of your Blynk connection status.

Your void loop is a total mess from a Blynk point of view, and needs to be rectified.

Pete.

Apologies for the inconvenience. 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 system status will be controlled using physical buttons located on the transmitter’s breadboard and using the app. In addition, the RSSI value will appear on the OLED screen located on the transmitter’s breadboard and on the app.

None of that addresses the comments I made in my previous post.

Pete.