Blynk.virtualWrite taking a long time (~200ms per write)

Hi Folks!

First time with Blynk. Everything is great, except for one thing: every Blynk.virtualWrite I run takes a LOT more time than I would have expected, somewhere on the order of 200ms per write. That means that if I have multiple writes, it takes over a second, which really messes up some of my other functions. I would really like to be able to update 2-6 variables per second and have my loops each take under 100ms if possible.

Similarly, if I press a button rapidly (say, 5 times), it holds up my arduino enough that the app thinks it’s disconnected. That’s not an actual use case for me, just helping add to debugging and identifying the problem.

Is this just a hardware/software limitation? Am I doing something wrong? Can I edit the libraries to make this work better? THANKS IN ADVANCE!!

For details:

  1. I am using a local server
  2. Hardware is an Arduino Mega communicating through an esp8266 wifi module
  3. I am not using delays of any kind, except in the setup.
  4. Sketch below is lightly edited to remove some extraneous functions
  5. Yes, I am using timers to avoid writing every loop
    edit to add:
  6. The pause ONLY occurs when the virtualwrite command is triggered (on timer). Almost all iterations take only a handful of milliseconds, except for those in which the virtualwrite is triggered
  7. If I comment out the virtualwrite command, the whole thing runs quickly and smoothly
  8. If I uncomment more of the virtualwrite commands, the delay becomes larger, leading me to conclude that the problem is the virtualwrite commands, NOT anything to do with the rest of the code.
    edit again:
  9. Commenting out the other things in the main loop does not solve my issue.
  10. Iteration time measured from serial monitor printing (not shown).

// Libraries
  #include <Adafruit_MAX31865.h>
  #include <ESP8266_Lib.h>
  #include <BlynkSimpleShieldEsp8266.h>
  #include <PID_v1.h>
  #include <PID_AutoTune_v0.h>
  #include <EEPROM.h>

// General definitions
  #define ON 0
  #define OFF 1
// Pin setup
  // Arduino physical pins
    // Temp probe pins
      const int pin_probe_1_CLK = 13;
      const int pin_probe_1_SDO = 12;
      const int pin_probe_1_SDI = 11;
      const int pin_probe_1_CS = 10;
    // Relay board (element safety, pump, other)    
      const int pin_relay_600w = 42;
      const int pin_relay_1000w = 43;
      const int pin_relay_pump_1 = 40;
    // Float switch / binary sensors
      const int pin_FS_MT;
// EEPROM value addresses
  // temp control PID values
    const int ER_address_PID_1_mode_1_kp = 1;
    const int ER_address_PID_1_mode_1_ki = 2;
    const int ER_address_PID_1_mode_1_kd = 3;
  // temp control offsets
    const int ER_address_temp_probe_1_offset = 10;
    
// Variable declaration
  // Blynk and network authentication
    char ssid[] = "WIFISSID";
    char pass[] = "PASSWORD";
    char auth[] = "TOKEN";
    char IPaddress[] = "IP";
    #define EspSerial Serial1
    #define ESP8266_BAUD 115200
    ESP8266 wifi(&EspSerial);
  // Initialize temp probe objects
    Adafruit_MAX31865 temp_probe_1 = Adafruit_MAX31865(pin_probe_1_CLK, pin_probe_1_SDO, pin_probe_1_SDI, pin_probe_1_CS);
  // Timer
    int timer_start;
  // Mode selection;
    String control_mode="Manual";
  // temp current and target
    double temp_target=0;
    int temp_target_holding = 0;
    double temp_probe_1_current;
  // temp probe 1 offset
    double temp_probe_1_offset;
    double temp_probe_1_offset_level_holding;
  // Element power level (expressed as 0-100)
    double element_power_level=0;
  // PID settings
    double PID_1_mode_1_kp;
    double PID_1_mode_1_ki;
    double PID_1_mode_1_kd;
  // Initialize PID object
    PID main_PID(&temp_probe_1_current, &element_power_level, &temp_target,0,0,0,P_ON_M, DIRECT);
  // Set update timer for updating monitored values
    BlynkTimer update_temp_timer;
    long update_mon_interval = 1000;
  // Initialize Blynk LEDs
    WidgetLED LED_600w(V101);
    WidgetLED LED_1000w(V102);
    WidgetLED LED_pump_1(V103);
    WidgetLED LED_MT_FS(V104);
    WidgetLCD lcd(V100);

void setup()
{
  // Start serial console
    Serial.begin(9600);
    delay(10);
  // Start blynk
    EspSerial.begin(ESP8266_BAUD);
    delay(10);
    Blynk.begin(auth, wifi, ssid, pass, IPaddress, 8080);
  // Relay items (pumps, element safety, valves, etc.)
    // Ensure relay board starts in OFF position
      for (int i=pin_relay_600w; i<=(pin_relay_600w+7);i++){ digitalWrite(i,OFF);}
    // Pins
      pinMode(pin_relay_600w,OUTPUT);
      pinMode(pin_relay_1000w,OUTPUT);
      pinMode(pin_relay_pump_1,OUTPUT);
  // temp probe
    // RTD input pins
      pinMode(pin_probe_1_CLK,INPUT);
      pinMode(pin_probe_1_SDO,INPUT);
      pinMode(pin_probe_1_SDI,INPUT);
      pinMode(pin_probe_1_CS,INPUT);
    // Start temp probe object
      temp_probe_1.begin(MAX31865_3WIRE);
  // Read EEPROM values
    PID_1_mode_1_kp = EEPROM.read(ER_address_PID_1_mode_1_kp);
    PID_1_mode_1_ki = EEPROM.read(ER_address_PID_1_mode_1_ki);
    PID_1_mode_1_kd = EEPROM.read(ER_address_PID_1_mode_1_kd);
    temp_probe_1_offset = EEPROM.read(ER_address_temp_probe_1_offset)/10;
  // Initialize PID object
    main_PID.SetMode(AUTOMATIC);
  // Start temp update timer
    update_temp_timer.setInterval(update_mon_interval, update_mon_blynk);
  // Start timer
    timer_start = millis();
}

// Main Loop
void loop()
{
  // Update current temp (pull from arduino)
    update_probe_1_temp();
  // Update temp readouts (push to arduino)
    update_temp_timer.run();
  // Set PID temp control();
    update_temp_control();
  // Run Blynk
    Blynk.run();
}

void update_mon_blynk()
{
  // Temperatures
    // Temp target to graph
      Blynk.virtualWrite(V111, temp_target);
    // Push current temp target to graph and manual monitor
  //    Blynk.virtualWrite(V110, temp_probe_1_current);
  //    Blynk.virtualWrite(V107, temp_probe_1_current);
  // Element power 
  //  Blynk.virtualWrite(V105,element_power_level/2.55);
  // Lift
  //  Blynk.virtualWrite(V106,LA_current_pos);
  // LCD
    //lcd.print(0, 0, "Mode: " + control_mode);
  
}

void update_temp_control()
{
  // Change PID settings if necessary
    main_PID.SetTunings(PID_1_mode_1_kp, PID_1_mode_1_ki, PID_1_mode_1_kd);
  // Update the PID settings
    main_PID.Compute();
}

// Read temp probe 1 (including conversion to F and offset)
void update_probe_1_temp()
{ temp_probe_1_current = temp_probe_1.temperature(100.0, 430.0)*(9/5)+32 + temp_probe_1_offset; }

// Enable/disable temp probes
void element_600w_enable()
{  digitalWrite(pin_relay_600w,ON); }
// Enable/disable temp probes
void element_600w_disable()
{  digitalWrite(pin_relay_600w,OFF); }
void element_1000w_enable()
{  digitalWrite(pin_relay_1000w,ON); }
// Enable/disable temp probes
void element_1000w_disable()
{  digitalWrite(pin_relay_1000w,OFF); }

void update_probe_1_offset()
{
  // Determine difference between calibrated temp and current readout temp
    double temp_probe_1_offset = temp_probe_1_offset_level_holding - (temp_probe_1.temperature(100.0, 430.0)*(9/5)+32);
  // Write new value to EEPROM
    EEPROM.update(ER_address_temp_probe_1_offset,temp_probe_1_offset*10);
}

void pump_1_on()
{  
  digitalWrite(pin_relay_pump_1,ON);
  LED_pump_1.on();
}

void pump_1_off()
{  
  digitalWrite(pin_relay_pump_1,OFF);
  LED_pump_1.off();
}

// Blynk vpins
  // Manual control inputs
    // Temperature input
      BLYNK_WRITE(V1) {
        temp_target_holding = param.asInt();
        control_mode = "Manual";
      }
    // Set temp from temp input using button
      BLYNK_WRITE(V2) {
        temp_target = temp_target_holding;
        control_mode = "Manual";
      }
    // Turn on 600W element
      BLYNK_WRITE(V10) {
        int y = param.asInt();
        if (y==1){ element_600w_enable(); }
        if (y==0){ element_600w_disable(); }
        control_mode = "Manual";
      }
    // Turn on 1000W element
      BLYNK_WRITE(V11) {
        int y = param.asInt();
        if (y==1){ element_1000w_enable(); }
        if (y==0){ element_1000w_disable(); }
        control_mode = "Manual";
      }
    // Element power level (input as 0-100, converted to 0-255)
      BLYNK_WRITE(V12) {
        int y = param.asInt();
        element_power_level=y*2.55;
        control_mode = "Manual";
      }
    // Pump button
      BLYNK_WRITE(V30) {
        int y = param.asInt();
        if (y==1){ pump_1_on(); }
        if (y==0){ pump_1_off(); }
        control_mode = "Manual";
      }
  // Config inputs
    // temp offset input
      BLYNK_WRITE(V40) { temp_probe_1_offset_level_holding = param.asDouble(); }
    // temp offset set button
      BLYNK_WRITE(V41) { update_probe_1_offset(); }

I think you’ll find that this is where your problem lies.

Pete.

Thanks, but beg to differ, as you will see with the remaining code. Each iteration takes only a handful of milliseconds to run, except for those when the temperature update timer triggers. If I disable the virtualWrite (specifically by commenting out the virtualwrite command), the iterations run smoothly, consistently, and quickly. The pause only occurs with a virtualWrite.

Edit: Added that info to the original post to clarify that the issue is specific to the virtualWrite, NOT the rest of the code. I’ll try to test it later using a totally blank sketch to avoid the distraction.

Okay, have it your way.

Pete.

@shoo
clean your loop

void loop() {
Blynk.run();
}

I have commented out everything in the main loop except blynk run and the timer. The timer only runs the virtual write. The problem as described still happens. Iterations with the virtualwrite triggered (and ONLY those) take a long time.

How are you measuring this “Iteration Time”?
Your void update_mon_blynk() function is being called once per second, but you’re saying that this is taking around 200ms to complete the write. I don’t see how you can know precisely when the virtualWrite in that loop is being triggered, to then know how long it’s taking to execute.

Pete.

I am using the serial monitor (removed from the code above) to monitor the iteration times. Not at home right now so I can’t show it, but measured the delay by printing the time at every iteration from the main loop, and confirmed that the delay was occurring only when the timer triggered by inserting an additional Serial.print in the update_mon_blynk().

Off the top of my head, I don’t remember the exact # of additional milliseconds occurring on the delay, only that it was on the order of around 200 additional ms.

I first noticed the issue when I was trying to update 6 vpins through virtualwrite each second (was not using serial monitor), which as you might expect, caused an endless cycle of updating at each iteration. Commenting out the virtualwrites relieved the issue roughly proportionately by the number of virtualwrites commented out.

Which makes it difficult for others to help/recreate this issue.

Do you have any other hardware (a NodeMCU for example) that you could try the same tests with?
If you do, then try with the standard and optional higher processor speeds to see if this has any impact.

Pete.

I may be new to Blynk, but I am far from new to coding/debugging. I will strip away extraneous code in future asks for help. I realize that the standard “this is slow” issue on Blynk is usually people putting delays in and things in their main loops, though that’s not the case for me, as noted above.

No other hardware available, unfortunately.

I had previously thought the problem was with communication w/ the cloud server, but the problem does not seem to resolve with the local server.

Working with Blynk and other IoT systems is very different to debugging other systems, because of all the stuff that’s happening in the background to make the ‘magic’ happen.
Actually, very few people complain about Blynk speed/latency, and those that do are generally suffering issues caused by their network or internet.

When people use delays, or call functions that starve the Blynk.run process of processor time, the result is generally disconnections or WDT resets rather than speed issues.

If your system could operate with the fewer number of GPIO pins offered by a NodeMCU compared to Arduino; and operate with 3.3v rather than 5v logic levels then you’d be better going for a NodeMCU.
They’re much cheaper, smaller and more powerful than the Arduino/ESP combination and have a number of advantages such as the ability to do OTA wireless updates.

Pete.

2 Likes

Actually, it is one of the leading causes of issues here… Blynk, and other IoT based applications that need exactly timed processes do not work well with standard Arduino style “Run everything fast as possible in the main loop, except when we want to wait, which then stops everything else as well” coding practice.

Unlearn old ways and look to the separate timed functions that more advanced programming uses.

Blynk functions and commands, properly utilised, can and will run as fast as the minimum network lag will allow… I have pushed limits inside a Local network with Local Server to the low ~15ms between groups of Blynk.virtualWrite() commands…

2 Likes

I get that. However, in my case we can eliminate that issue. The problem is apparently occuring between Arduino and app, which may or may not be exacerbated by a problem between chair and computer. I am using the blynk “run loop as fast as possible no matter what with no stops and delays” style, and the ONLY delay of any note between iterations is the virtualwrite command itself.

Any ideas on what else might be going wrong? I too would like 15ms read/writes, and am very confused why I am not getting them.

I may just kill this thread and start over, since the main loop distraction has thrown off this conversation too much to be useful at this point.

@shoo post the bare minimum sketch that exhibits your problem.

Don’t bother, I will just merge it back :stuck_out_tongue: This is all the same forum, and the format is such that it is best to keep your same issue in same topic with current updates bringing it to the forefront.

As for your issue and as has already been recommended… post your demonstrating code and details, otherwise you might as well be asking rhetorical questions.

Ha, fair enough.

Minimal code to reproduce problem below:

// Libraries
  #include <ESP8266_Lib.h>
  #include <BlynkSimpleShieldEsp8266.h>

// Variable declaration
  // Blynk and network authentication
    char ssid[] = "ssid";
    char pass[] = "pass";
    char auth[] = "token";
    char IPaddress[] = "192.168.1.125";
    #define EspSerial Serial1
    #define ESP8266_BAUD 115200
    ESP8266 wifi(&EspSerial);
  
  // Set update timer for updating monitored values
    BlynkTimer update_temp_timer;
    long update_mon_interval = 1000;
void setup()
{
  // Start serial console
    Serial.begin(9600);
    delay(10);
  // Start blynk
    EspSerial.begin(ESP8266_BAUD);
    delay(10);
    Blynk.begin(auth, wifi, ssid, pass, IPaddress, 8080);
  
  // Start temp update timer
    update_temp_timer.setInterval(update_mon_interval, update_mon_blynk);
  // Start timer
    timer_start = millis();
}

// Main Loop
void loop()
{
  // Update temp readouts (push to arduino)
    update_temp_timer.run();
  // Run Blynk
    Blynk.run();
}

void update_mon_blynk()
{
    long VW_timer = millis();
    Blynk.virtualWrite(V111, 12);
    Serial.print("Time for virtualwrite: ");
    Serial.println(millis()-VW_timer);
}

Sample output showing 80-150ms per virtualwrite after it has stabilized (note: print ONLY for virtualwrite, each regular iteration is just a few ms. Earlier ones start are a bit noisier. Admittedly when I look at this now it’s less than the 200 claimed, but still far higher than I would like).

21:35:38.764 -> Time for virtualwrite: 95
21:35:39.738 -> Time for virtualwrite: 99
21:35:40.753 -> Time for virtualwrite: 99
21:35:41.731 -> Time for virtualwrite: 83
21:35:42.732 -> Time for virtualwrite: 87
21:35:43.741 -> Time for virtualwrite: 87
21:35:44.717 -> Time for virtualwrite: 89
21:35:45.740 -> Time for virtualwrite: 89
21:35:46.730 -> Time for virtualwrite: 85
21:35:47.810 -> Time for virtualwrite: 150
21:35:48.738 -> Time for virtualwrite: 97
21:35:49.755 -> Time for virtualwrite: 99
21:35:50.728 -> Time for virtualwrite: 85
21:35:51.751 -> Time for virtualwrite: 88
21:35:52.743 -> Time for virtualwrite: 88
21:35:53.754 -> Time for virtualwrite: 105
21:35:54.738 -> Time for virtualwrite: 91
21:35:55.758 -> Time for virtualwrite: 100
21:35:56.734 -> Time for virtualwrite: 98
21:35:57.753 -> Time for virtualwrite: 100
21:35:58.727 -> Time for virtualwrite: 86
21:35:59.732 -> Time for virtualwrite: 89
21:36:00.749 -> Time for virtualwrite: 88
21:36:01.756 -> Time for virtualwrite: 85
21:36:02.737 -> Time for virtualwrite: 90
21:36:03.754 -> Time for virtualwrite: 85
21:36:04.745 -> Time for virtualwrite: 83
21:36:05.738 -> Time for virtualwrite: 89
21:36:06.753 -> Time for virtualwrite: 85
21:36:07.740 -> Time for virtualwrite: 84
21:36:08.745 -> Time for virtualwrite: 82
21:36:09.752 -> Time for virtualwrite: 83
21:36:10.772 -> Time for virtualwrite: 82
21:36:11.753 -> Time for virtualwrite: 84
21:36:12.752 -> Time for virtualwrite: 83
21:36:13.737 -> Time for virtualwrite: 82
21:36:14.763 -> Time for virtualwrite: 84
21:36:15.771 -> Time for virtualwrite: 81
21:36:16.743 -> Time for virtualwrite: 84
21:36:17.762 -> Time for virtualwrite: 84
21:36:18.765 -> Time for virtualwrite: 87
21:36:19.742 -> Time for virtualwrite: 81
21:36:20.753 -> Time for virtualwrite: 85
21:36:21.787 -> Time for virtualwrite: 81
21:36:22.759 -> Time for virtualwrite: 83
21:36:23.769 -> Time for virtualwrite: 82
21:36:24.750 -> Time for virtualwrite: 87
21:36:25.790 -> Time for virtualwrite: 82
21:36:26.764 -> Time for virtualwrite: 82
21:36:27.797 -> Time for virtualwrite: 85
21:36:28.788 -> Time for virtualwrite: 91
21:36:29.760 -> Time for virtualwrite: 81
21:36:30.789 -> Time for virtualwrite: 86
21:36:31.759 -> Time for virtualwrite: 82
21:36:32.762 -> Time for virtualwrite: 83
21:36:33.786 -> Time for virtualwrite: 109
21:36:34.819 -> Time for virtualwrite: 105
21:36:35.763 -> Time for virtualwrite: 85
21:36:36.780 -> Time for virtualwrite: 83
21:36:37.790 -> Time for virtualwrite: 99
21:36:38.824 -> Time for virtualwrite: 114
21:36:39.801 -> Time for virtualwrite: 81
21:36:40.822 -> Time for virtualwrite: 97
21:36:41.796 -> Time for virtualwrite: 95
21:36:42.799 -> Time for virtualwrite: 97
21:36:43.804 -> Time for virtualwrite: 100
21:36:44.825 -> Time for virtualwrite: 100
21:36:45.831 -> Time for virtualwrite: 107
21:36:46.802 -> Time for virtualwrite: 92
21:36:47.806 -> Time for virtualwrite: 84
21:36:48.787 -> Time for virtualwrite: 97
21:36:49.820 -> Time for virtualwrite: 82

I just noticed… you are running this on an Arduino at 16Mhz as opposed to a standalone ESP8266 at 80Mhz (default)… big difference in processing power there :stuck_out_tongue:

Add in the possible further bottleneck of the Serial to WiFi link for the ESP as Shield, again as compared to the direct WiFi of a standalone ESP8266

1 Like

Nodemcu @160 Mhz :rofl:

14-026122

Mowr PAWAR!! :zap: “Hit the turbo boost KITT”

1 Like

Yup, main processor involved is the 16MHz arduino one. So current thinking is that this is just a raw processing power bottleneck, correct? Sadly I need all those beautiful Arduino mega pins, so I’m stuck with it at the moment. It’s plenty powerful to run my scripts, but sadly not powerful enough for fast read/writes from Blynk, I suppose.

Good to know now before I put the effort into all the workarounds. Main workaround is probably just going to make sure I am staggering my writes so they don’t pile up. I also have at least one process that can freak out with long pauses, so possibly temporarily shut that one down while a read/write is happening. Not ideal, but can definitely make it work.

I’ve thought about doing something crazy and having the arduino handle all the IO and a pi zerow or something handle the UI and communication over serial/WIFI. That’re similar(ish) to my current strategy, which is also why I have all that “extra” in the main loop. The arduino is effectively running the automation scripts, and I have Blynk to tell it what mode to be in and monitor the various sensors of interest.

Also, before someone says it, the automation scripting is far, far more complicated than eventor widgets can handle.