ESP32 VirtualSync - Help / Implementation - How to capture state into program variable

Hey everyone - I am working on a project that requires a very stable (total count) variable. The device must maintain the count on power loss or disconnection and retain
the total number of button presses. I have read a few similar topics and cant seem to wrap my head around how virtual sync works, or how to implement it in my situation. The theory is simple, but implementation hasnt given me much to work with.

Tried using a BLYNK_CONNECTED() with syncAll() and syncVirtual() but each time it is power cycled the variable is lost. Maybe there is some nuance I am missing but the docs and examples are limited so I am not finding much useful info.

If anyone has some code implementation they could suggest that would be great.

Preferably with an explanation of what I am missing as well. Ideally, I would like to be able to virtualRead the current value and set cycle_count equal to that to reset the volatile (MCU) value to the known last server value, but that doesnt seem to be how it works.

ESP32 | edgent | Arduino 2.3.2

In the below code, Serial monitor states “SYNC RAN” then “0” each time. Then cycle_count starts from 0 on button press. I have tried a myriad of solutions but nothing has done anything notable so this is a starting point.

TEST CODE - simplified for clarity and testing output. not final implementation

#define BLYNK_TEMPLATE_ID "***"
#define BLYNK_TEMPLATE_NAME "***"

#define BLYNK_FIRMWARE_VERSION "0.1.0"
#define BLYNK_PRINT Serial
#define APP_DEBUG
#define USE_ESP32_DEV_MODULE

#include "BlynkEdgent.h"

int button_pin = 27;
int total_count;

void setup() {
  Serial.begin(115200);
  pinMode(button_pin, INPUT_PULLUP);
  BlynkEdgent.begin();
}

BLYNK_CONNECTED() {
  Blynk.syncVirtual(V0);
  Serial.println("SYNC RAN");
  Serial.println(V0);
  delay(10000);
}

void loop() {
  BlynkEdgent.run();

  if (!digitalRead(button_pin)) {
    total_count++;
    Blynk.virtualWrite(V0, total_count);
    delay(100);
  }

  Serial.println(total_count);
}

The part you’re missing is the method of obtaining the V0 value from the Blynk server and assigning it to your total_count variable.

Add this code…

BLYNK_WRITE(V0) 
{
  total_count = param.asInt();
  Serial.print("V0 value retrieved from server = ");
  Serial.println(total_count );
}

When you call Blynk.syncVirtual(V0) or when you change the V0 datastream using a dashboard widget, it causes the BLYNK_WRITE(V0) callback function to “fire”. This will retrieve your V0 value from the Blynk server using the para,asInt() command and store it in your total_count variable.

You need to remove these two lines from your BLYNK_CONNECTED() callback function…

In your final code you need to stop doing all of this in your void loop…

and use either an interrupt or a BlynkTimer to read and process the presses of your button_pin digital pin.

Pete.

1 Like

Excellent, thank you Pete! Exacely what I was looking for. I have seen this code in others sketches, but assumed the edgent wrapper (or whatever it would be classified as) was handling this in the background in some way. Havent seen sync and edgent used together in any other threads.

Tried and worked perfect - Code that follows (at the end) works great for anyone else needing a solution.

Yes, this was me simply trying to understand the variable. Only posted the code I was using to try and understand and debug the sync() and show that it was indeed triggering !

I am curious about the last part though:

and

…

I have read about keeping the loop() “clear” - the timer docs state it should not be spammed (so to speek) with a Blynk.virtualWrite() and should retain the ability to ping the server via BlynkEdgent.run() at least every 100mS to not lose connection. So I am generally aware of the limitations, but I guess not completely?

However, would a simple IF comparison cause an issue, or is this just best practice? I havent seen a problem in my bench testing but would obviously prefer integrity of the code.

That said, what would you suggest for an ISR to trigger the Blynk.virtualWrite() ?? calling the Blynk.virtualWrite() within the ISR (unsuprisingly) causes a WDT event and core panic. However doing as I am here with debounce timer and flag, it still requires an IF as well as Blynk.virtualWrite() called within the loop.

Timers would not be my first thought, since what I am doing simply waits until the button is pressed and is not streaming timeseries data. But I guess could just poll for button press in a similar way as the ISR if ran fast enough, just seems like between the two IRS would be more responsive.

Really appreciate the help Pete. Your replies throughout the forum have been as useful as the Docs themselves!

#define BLYNK_TEMPLATE_ID "***"
#define BLYNK_TEMPLATE_NAME "***"

#define BLYNK_FIRMWARE_VERSION "0.1.0"
#define BLYNK_PRINT Serial
#define APP_DEBUG
#define USE_ESP32_DEV_MODULE

#include "BlynkEdgent.h"

#define DEBOUNCE_TIME 250

int button_pin = 27;
volatile int total_count;
int last_count;

void IRAM_ATTR handleButtonPress() {
  static unsigned long lastInterruptMillis = 0;
  if (millis() - lastInterruptMillis > DEBOUNCE_TIME) {
    total_count++;
    lastInterruptMillis = millis();
  }
}

void setup() {
  Serial.begin(115200);
  pinMode(button_pin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(button_pin), handleButtonPress, FALLING);
  BlynkEdgent.begin();
}

BLYNK_CONNECTED() {
  Blynk.syncVirtual(V0);
}

BLYNK_WRITE(V0) 
{
  total_count = param.asInt();
  Serial.print("V0 value retrieved from server = ");
  Serial.println(total_count);
}

void loop() {
  BlynkEdgent.run();

  if (last_count != total_count) {
    Blynk.virtualWrite(V0, total_count);
    Serial.println(total_count);
    last_count = total_count;
  }
}

You really should keep your void loop as clean as possible, and there should never be a Blynk.virtualWrite() in the void loop, even if it is inside an if statement.

Have you tried calling a function from your ISR?

Pete.

I did try that first - each time i got a “guru meditation error - core 1 panicked”. Even just the Blynk.virtualWrite() but definately with debounce delays

So its more of a safety element of assuring you dont overload/crash the connection? That was also why I had the delay(100) - to essentially max out at 10 samples / sec worst case scenario.

Mostly curious because there is a lot of other funcitonal code that is needed - what I am posting is essentially just the blynk part. Perhaps during testing I can make a serial loop ms timer to assure it never exceeds a max delay of BlynkEdgent.run(). But the ESP will have a lot of other tasks to perform

Blynk needs to see Blynk.run() - or in this case the BlynkEdgent.run() command executed as many times per second as possible. This is why keeping the void loop clean is important, and why you can’t use blocking delays in your code.

Don’t do that!
BlynkTimer does this, and in a much more elegant way - with anti-flood safeguards built-in.
Use BlynkTimer, and set-up one of your timers to poll the physical switch at whatever frequency you need to have the correct responsiveness without bounces (you don’t need debounce code in there).

Pete.

Tested timers with simlar debounce and low interval (100L) seems to work great and is a bit more simple. So I will stick with that.

I am not finding an in depth timer library documentation available. Github timer library that arduino links to doesnt have a timer.timeout function everyone on the firum keeps referencing. Any idea where I can find docs?

As I said, you shouldn’t need a debounce - unless it’s actually to prevent multiple consecutive button presses rather than contact debouncing (which lasts a few nanoseconds usually).

Pete.

1 Like