Concurrent BLYNK_WRITE's

I recompiled some device code with the new Blynk Library and see behavior change:

I use a BLYNK_WRITE(V12) function to start a calibration process, calibrate(), and “listen” for a second button press within that calibrate process. It used to work fine but now it seems to block any relaunch (no second button press is effected until initial BLYNK_WRITE is completed).

I checked the lib release notes, but couldn’t identify what might be the cause.

Any info on this would be appreciated! Thanks, in advance, for your help.


bool userPress = 0;
bool nowCalibrating = 0;

BLYNK_WRITE(V12) {
  if (param.asInt()) { //if button press
     if (nowCalibrating) { // if in the midst of calibration then flag the second press
       userPress = 1;
       return;
     } else {
       calibrate(); // first press, start calibrating
     }
   }
 }


 void calibrate() { 
   nowCalibrating = 1; 
   showTerm("Calibrate? Press again to confirm.");
 
   int x = 100;
   while ((!userPress) && (--x)) {  //wait for press
     Blynk.run();
     delay(100);
   }
   
   if (!userPress) {
     showTerm("Calibration aborted.");
     nowCalibrating = 0;
     return;
   } 
   // do calibration work 
 }

It’s pretty difficult to make any sense of this, and work-out what the issue may be, without the test of the code.

Pete.

I included the function I call from the BLYNK_WRITE. Not sure why my spaces do not show up - hard to read. I just selected the code then hit the “block quote” button.

Please use the proper and documented method for posting code here :stuck_out_tongue_winking_eye:

Blynk - FTFC

Done. Thanks!

I figured out a work-around by replacing this:

calibrate();

With this:

timer.setTimeout(10L,calibrate);

By using a timer to call the function, the initial button press quickly drops out of the BLYNK_WRITE function and Blynk is again ready to act on the next button press.

I look forward to confirmation of changed blocking behavior of blynk functions and to learn what the new rules are in instantiating blynk functions. I can imagine this helps users from shooting themselves in the foot and allows for a simpler library than the previous, event-driven design of these function calls.

It wasn’t the Blynk function that was blocking, rather your use of the while() statement which is a blocking process, and the “entire MCU process stallingdelay() command.

They are the same as the old rules :stuck_out_tongue_winking_eye: Use more creative timer and logic programming instead of the Arduino standard of brute force repetition in the void loop() countered by other methods consisting mostly of blocking delay(), while() and for() loops.

I find using Blynk encourages me to learn more creatively mad programming skilz :smiley:

In the past, after BLYNK_WRITE was invoked, the same function could be re-invoked before the first instance’s code had completed. The first instance would resume right where it was interrupted once the interrupting instance concluded (i.e. my code worked with previous libraries).

Now, once BLYNK_WRITE is invoked the function must complete fully before it can be invoked again (i.e. my code no longer works). It seems that a second invocation is queued and a third call causes instability (in my brief testing).

The behavior I see in my code sample is different than before and I am asking if something in recent Blynk Libraries is the likely cause or if I should debug elsewhere.

Use more creative timer and logic programming instead of the Arduino standard of brute force repetition in the void loop()

I don’t think I mentioned doing anything in the void loop() - I’m not sure what you mean here. Do you think blynk.run() needs to live outside the void loop(), while{} or for{} constructs? What other house could it live in?

I wonder if nested functions (which I find valuable) may have recently lost support within the BLYNK_WRITE construct. I have used nested functions on other Blynk devices and I am now hesitant to upgrade without knowing if the rules have changed – and I hear that you do not think so:

They are the same as the old rules

I have some projects that have been running for over a year… only updating as availed with new library versions. I have not seen any change in how Blynk functions run, they get called and run as long, and as well, as I program them to.

I was responding in generalities since you haven’t really shown anything that could assist us in understanding what you are attempting, nor why whatever issues you are eluding to could be occurring.

Perhaps you can provide further information?

Perhaps you are not referring to nested functions, BLYNK_WRITE() in particular?, in the way we understand them… even the documentation show that they are independent and not to be run inside other Blynk functions…

@CampHamp i think you’re missing something.
Blynk is not (actually it can not) allow you to run BLYNK_WRITE in parallel.
Unless you … run Blynk.run() in parallel using some threads/cores OS functions (which I do not recommend to do unless you are an expert and the arduino god himself).
What I mean here, BLYNK_WRITE (and others) is called from the context of Blynk.run(). Always.

If you call Blynk.run() inside of BLYNK_WRITE then… well you can end up with growing stack of unhandled requests (if you send too many). I hope this is understandable.

P.S. Please note that Blynk.run() is also called by some other Blynk functions.

1 Like

Thanks Gunner. I am not calling BLYNK_WRITE from the device logic, but simply allowing BLYNK_WRITE to be invoked even before a previous instance has completed. I need to provide a more simple example which I will do today. I appreciate your insights.

“Parallel” is a poorly-chosen word here as it conjures-up “parallel processing” at the thread level. My apologies. I do not mean that.

I think “non-sequential” is better language perhaps? I will make a clean, simple example to help clarify (at least for myself!) what I thought was a change to how BLYNK_WRITE invocations now check to see if one is currently active and if so, queue-up a new request versus just embarking on a new instantiation and “inserting” a sub-process at the time of a new triggering event.

There’s no such logic by default. You can build it on top, if you need this…

Here is a complete example to use instead of my code fragment. You need a project with a button (V0) and a term window (V1). Just press the button a few times using 0.5.* libs vs. 0.4.* to see the different behavior that I have been trying to describe.


#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>

//Blynk setup
char ssid[] = "SSID";
char pass[] = "PWD";
char auth[] = "BlynkProjectToken";

WidgetTerminal blynkTerm(V1);

BLYNK_WRITE(V0) {
  if (param.asInt() == 1) { //if button pressed down
    displayNumbers();
  }
}

void displayNumbers() { 
 for (int i=1; i <= 5; i++){ //count to 5
   blynkTerm.println(i);
   blynkTerm.flush();
   myDelay(500);
 }
}

void myDelay(long ms){
   long stopWhen = ms + millis();
   while(millis() <= stopWhen){
       Blynk.run();
       yield(); //make this loop non-blocking so ESP can maintain WiFi
   }
}

void setup() {
    Blynk.begin(auth, ssid, pass);

    while (!Blynk.connected()) {
    }
}

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

My test ran on a NodeMCU v1.0.

Findings:
Using Blynk Lib ver 0.4.10:
Repeated button presses call BLYNK_WRITE in an event-driven fashion and does not yield to already-running BLYNK_WRITE instances. Many nested instances can run without locking or crashing.

Using Blynk Lib ver 0.5.3:
Two button strikes call BLYNK_WRITE in a sequenced fashion (queuing - the second press yields to an already-running BLYNK_WRITE instance). With 3 rapid button presses, the processing halts without crashing (“queued” calls are dropped and even the running instance is terminated).

I hope the code and your own experience can describe better than what I have tried to put into words!

I am not sure if the queuing and halting I experience is intentional in the 0.5.0 release, but I guess if this is just how it will work from now on, then we probably should limit blynk.run() usage to the void loop() and let the blynk server manage the queue of app events. However, this conclusion causes issues for applications that require blynk.delay() functionality.

I look forward to any comments on these findings / thoughts.

Thanks again!

But blocking BLYNK_WRITE is something you should not do in first place…
Yes we can change something, but it is still not parallel.
You are calling terminal functions, which in some cases decides to call Blynk.run() internally.

The 0.5.* library architecture is blocking BLYNK_WRITE’s, not me! :slight_smile: But, REALLY! There is now a limit of one concurrent instance of BLYNK_WRITE and a maximum of 1 BLYNK_WRITE in the queue. Those limits were introduced in 0.5.0. Anyway, you probably know all this better than anyone else here. If you actually think I am the cause of “the blockage”, then please let me know what I am missing.

It probably makes sense. Developers would want Blynk to respond immediately to BLYNK_WRITE events and not queue them up, disregard them or cause partial code execution. Perhaps a simple reversion to the 0.4.* behavior on these instantiations would be the quick, cheap medicine?

Agreed. I think “concurrent” might be a better description (I changed the thread title as well).

Yes. If you think we would benefit from a “Serial” example, I would be glad to remove the terminal widget and re-post. Just let me know.

Again, thanks!

@CampHamp Perhaps you are running into the anti-flooding protection that was introduced into 0.5.0

It basically helps keep those that don’t use proper timing and logic from flooding and causing disconnections.

I had ran into some governor like loop issues with some of my very fast processing projects (only possible due to internal network and Local Server) and supposedly the bypass command that seemed to help is

#define BLYNK_MSG_LIMIT 0

You can try that, but it will not guaranty that your use of blocking processes will not continue to cause issues as more features are added into the library.

Blocking means that something is causing another thing to timeout. Please point out the timeout condition and the suspect. I may just be missing some mysterious thing, but my logic says as long as we do Blynk.run() constantly and yield() to ESP WiFi, then we are OK.

Next topic: Anti-Flooding condition. Not sending much data and not too fast either, right? I put Serial on there just to be sure and see no errors.

I think what we see is just a design decision that the handler not allow soft-interruption of BLYNK_WRITE. Each event is discreet and must complete before another event is handled. Sounds like the very abstract FIFO description to me in the release notes.

It essentially makes the system queue-based and could provide for course-grain, asynchronous communication. My issue is the upgrade impact is not clear, if you rely on the event-driven interrupts then they will need to be fixed (be made queue-based as well) and the event handler queue is tiny (1 event) and is not graceful when overfilled.

As stated before, I have many projects, some with way too much going on (just code testbenchs) and I have no problems running multiple button, slider, timer called BLYNK_WRITE() functions “simultaneously”… AKA as fast as I can possibly press and adjust widgets.

BLYNK_WRITE() functions in of them selves are not blocking… but if they contain blocking code (AKA something that prevents the MCU from responding to normal stimuli) then that is the issue, not the packaging.

Instead of this cyclical argument… can you provide a sketch and QR code that can definitively show your repeatable issue… without actually using blocking processes like delay() and such? So far what you have provides has programmatic issues (when used in a Blynk based IoT communicative type sketch) that have been referenced by myself or the developers.

I already did that. There is no delay() in the code I provided. Easy to read, install and reproduce. Go for it!