Migrate from Digital pins to Virtual Pins in Button widget

Hey,

I’m using blynk for a basic home automation project which Turns on/off lights on specified times or via the blynk app with the help of Button widgets(digital pins).

I’m planning to migrate from using digital pins for the button widget to using virtual pins for the same to be able to sync and view the state of the button(lights) via feedback.I’ve searched the forums for the same but all of them have “physical button” state feedback in them which i don’t need , i just wanted to see the state of the lights via the buttons itself when they turn on/off and also control them via the same buttons.Any help would be appreciated.

example part of sketch turns which turns on led lights @18:10

  if( (p_tm->tm_hour == 18) && (p_tm->tm_min == 10) && (digitalRead(D0) == HIGH)){
      digitalWrite(D0,LOW);
      Serial.println("LED 1 & 3 ON - Finally ✅");
      Blynk.virtualWrite(V12, "LED 1 & 3 ON - Finally ✅");
  }

“D0” is configured to button widget in switch mode in the blynk app

Okay, the first thing you need to know about when using virtual pins is that when you have a widget (we’ll talk about buttons, but all widgets work the same way) in your app then any change to that widget triggers a callback function called BLKYNK_WRITE(vPin).

So, if you have a button widget attached to V10 which is configured in Switch (latching) mode it will by default send a “1” when it’s turned on, and a “0” when it’s turned off.

If you have this in your code:

BLYNK_WRITE(V10)
{
  Serial.println("The button attached to V10 changed value");
}

then the “The button attached to V10 changed value” message will be printed to your serial monitor every time the button changes state.

That’s okay if you just want top know that the button widget was pressed, but not much use if you want to know whether it was turned on or off.
To get that piece of information, we need to find out what value (a 1 or a 0) was sent from the button widget via the Blynk server.
we do this using the param.as method. There are three versions of param.as:

param.asInt()
param.asString()
param.asFloat()

As the switch widget is sending us a 1 or a 0 - which are integers - then we’ll use the param.asInt() version. If we wanted to use the state of the switch widget elsewhere then we could save this value to a globally defined integer like this:

int V10_switch_state;

BLYNK_WRITE(V10)
{
  V10_switch_state = param.asInt();
  Serial.print("V10_switch_state = ");
  Serial.println(V10_switch_state);
}

In reality, we’ll want to do different things based on whether the switch widget was on or off. That means using an if statement. This is one way to do it…

BLYNK_WRITE(V10)
{
  int V10_switch_state = param.asInt();
  if (V10_switch_state == 1) // The button widget is on
  {
    Serial.println("The button attached to V10 is ON");
  }
  else // The button widget is not on, so it's off
  {
    Serial.println("The button attached to V10 is OFF");
  }
}

Because in C++ an integer value of 1=true and 0=false, you’ll often see this simplified like this…

BLYNK_WRITE(V10)
{
  if (param.asInt()) // The button widget sent a 1 (true)
  {
    Serial.println("The button attached to V10 is ON");
  }
  else // The button widget is not true, so it's false (0)
  {
    Serial.println("The button attached to V10 is OFF");
  }
}

So, your code might look like this:

BLYNK_WRITE(V10)
{
  if (param.asInt()) // The button widget sent a 1 (true)
  {
    digitalWrite(D0,LOW);    
    Serial.println("LED 1 & 3 on via button V10");
    Blynk.virtualWrite(V12,"LED 1 & 3 on via button V10");
  }
  else // The button widget is not true, so it's false (0)
  {
    digitalWrite(D0,HIGH);    
    Serial.println("LED 1 & 3 off via button V10");
    Blynk.virtualWrite(V12,"LED 1 & 3 ff via button V10");
  }
}

When it comes to syncing, you’ll need to decide what is being synchronised with what.

If the app (actually the Blynk server) is the ‘master’ system and when the MCU starts-up you want to synchronise the LEDs with the state of the buttons in the app then you’d use the BLYNK_CONNECTED callback function. This is another special function that is triggered automatically each time your MCU connects or re-connects with the Blynk server. This usually happens after a reboot, but could be caused by a drop in the connection.

Inside the BLYNK_CONNECTED callback function you can use Blynk.syncVirtual(vPin). This forces the server to send the current values to your MCU. From the MCU’s point of view it seems like the value of the virtual pin has changed, so the corresponding `BLYNK_WRITE(vPin) callback function is triggered…

BLYNK_CONNECTED()
{
  Blynk.syncVirtual(V10); // trigger the BLYNK_WRITE(V10) callback
}

If on the other hand you want to synchronise the app with the current state of the LEDs when your device is powered-up then you’d do this with a Blynk.virtualWrite(vPin,value) command, which could be placed in your BLYNK_CONNECTED callback.

Does this help?

Pete.

Thanks a LOT Pete!!! for that detailed explanation that truly helped a lot , i have tried the following

 timer6 = timer.setInterval(170L, checkPhysicalButton);// in void setup ()

BLYNK_WRITE(V20)
{
  if (param.asInt()) // The button widget sent a 1 (true)
  {
    digitalWrite(D0,LOW);    
  }
  else // The button widget is not true, so it's false (0)
  {
    digitalWrite(D0,HIGH);   
  }
}

void checkPhysicalButton()
{
  // D0
  if (digitalRead(D0) == LOW) {
          // Update Button Widget
      Blynk.virtualWrite(V20, LOW);
    
    
  } else {
    Blynk.virtualWrite(V20, HIGH);
  }
 }

BLYNK_CONNECTED()
{
  Blynk.syncVirtual(V20); // trigger the BLYNK_WRITE(V10) callback
  }

with this the button syncs the state at boot but when press the button they behave weirdly they turn off as soon as i press them and sometimes while continuing pressing them they “register” the press and don’t turn off on their own.In short the button turns back “ON” as soon i turn it “OFF” .Also, where should the BLYNK_WRITE(V20), be ?(in a function/setup). and is it better to use Blynk.syncAll() instead of numbers of Blynk.syncVirtual()?

I’m confused! You said…

But you have a timer checking the state of a physical button attached to D0. What’s this all about?

Maybe you should take a step back and explain more about your hardware setup and excatly what it is that you’re trying to achieve.

Pete.

I’m really sorry for the confusion created ,
The timer doesn’t check any physical button because there aren’t any, it just checks the value that is passed by timed events like these

 if( (p_tm->tm_hour == 18) && (p_tm->tm_min == 10) && (digitalRead(D0) == HIGH)){
      digitalWrite(D0,LOW);
  }

which in turn turns on/off relay connected to D0.

i didn’t want the physical feedback of the switch attached like the one mentioned here

Regarding my post

i think I’ve solved it by changing HIGH & LOW values like below

BLYNK_WRITE(V20)
{
  if (param.asInt()) // The button widget sent a 1 (true)
  {
    digitalWrite(D0,LOW);    
  }
  else // The button widget is not true, so it's false (0)
  {
    digitalWrite(D0,HIGH);   
  }
}

The only problem that I’m still facing is there’s a random behavior of the Button when pressed, sometime when i press the button to turn it “ON” ,it turns “ON” for a few millisecond turns “OFF” for another few milliseconds and then finally turns “ON”,something like a “stuck” button.I thought it might be because of

timer6 = timer.setInterval(170L, checkPhysicalButton);// in void setup ()

so i tried changing the intervals but still the issue persists.

What pin is the physical button/switch attached to? You should be doing the digitalRead() on that pin, not the one your relay is connected to.
Also, you will need to add some code to the checkPhysicalButton() code to get it to actually toggle the relay.

void checkPhysicalButton()
{
  // D0
  if (digitalRead(D0) == LOW) //use button/switch pin, NOT relay pin.
 {
          // Update Button Widget
      Blynk.virtualWrite(V20, LOW);
    //add digitalWrite() code, or use a Blynk.syncVirtual(V20);
    
    
  } else {
    Blynk.virtualWrite(V20, HIGH);
   //add digitalWrite() code, or use a Blynk.syncVirtual(V20);
  }
 }

There isnt any physical switch/button attached the method name is from this example i got it from,
it can literally be void checkDigitalPinState() and not void checkPhysicalButton().

Once again, There aren’t any physical button.Thanks.

Then remove that portion of code, and the timer as well. It is not needed. You only need the BLYNK_WRITE() function.

BLYNK_WRITE(V20)
{
  if (param.asInt()) // The button widget sent a 1 (true)
  {
    digitalWrite(D0,LOW);    
  }
  else // The button widget is not true, so it's false (0)
  {
    digitalWrite(D0,HIGH);   
  }
}

Then how will i know whether the relay was triggered or not? i.e check it’s state via virtual pins.

Maybe in that case you ought to give your function a more meaningful name than checkPhysicalButton

But that isn’t really what your checkPhysicalButton function is doing. It may be checking the state of the D0 pin, which in turn is changed by your
if( (p_tm->tm_hour == 18) && (p_tm->tm_min == 10) && (digitalRead(D0) == HIGH)){digitalWrite(D0,LOW);
statement, but that’s not the same thing.
You’d be better calling this ‘time check’ statement with your timer rather than polling pin D0.

However, I guess it depends on what you want your Blynk button functionality to do.
If you want the button to override the timer then you need to use some variables as flags and check the status of those flags as part of the process.

As I said earlier, I think you need to define what it is you want your overall functionality to be. Also, are you intending to use either the Blynk timer or time input widgets to specify your start/finish times from the app? If so then you’d be better to decide that now rather than later.

Pete.

I am a bit confused by this statement. Could you please explain a bit more as to what your are trying to accomplish?

If you are wanting to keep the virtual button in sync with the state of the relay, you would just update the virtual button every time you change the relay state.

For example, if you were to use the code you had posted earlier for turning the lights on at 18:18, you just need to add Blynk.virtualWrite() to that code.

if( (p_tm->tm_hour == 18) && (p_tm->tm_min == 10) && (digitalRead(D0) == HIGH)){
      digitalWrite(D0,LOW);
      Serial.println("LED 1 & 3 ON - Finally ✅");
      Blynk.virtualWrite(V12, "LED 1 & 3 ON - Finally ✅");
       Blynk.virtualWrite(V20, 1); //update virtual button state to ON
  }

I think that’s what’s needed here.

Pete.

Let me try to explain , the 4 buttons(widgets) below are used to turn on/off lights via the button themselves and also via timed events like i mentioned before via digitalpins (D0,D1…etc).
In-case i press the button manually on the app the button widget changes it’s state ,but when any timed event occurs the button don’t change their state because they are tied via digitalpins and not virtual pins ,so i want to change my widget buttons to make use of virtualpins for this “two-way feedback”

Blynk.virtualWrite(V20, 1); //update virtual button state to ON

this will only give a “one way” feedback .

I have only one issue now is the delay/random behavior while pressing the buttons mentioned here or see the gif above you can see this behaviour.

Yes, that’s true. The other side of this, which makes it 2-way, is the BLYNK_WRITE(V20) code.

The button in the app tells the MCU what to do via BLYNK_WRITE(V20), the MCU tells the app button what to do via a Blynk.virtualWrite(V20) command - hence you have 2-way communication.

Unfortunately, the animated GIF does nothing to explain the problem you are experiencing as far as I’m concerned, it’s just some annoying flashing lights. You’d be better explaining the issue in words, or posting your full code so that we can see what’s happening and upload it to our own devices if necessary.

There are still quite a few unanswered questions as far as I’m concerned.

Pete.

Sorry Pete , I’ve trimmed and slowed down the gif for you

If you may observe when i first press the button(see touch point) the button switches on for few milliseconds,turns off for another few then finally turns “ON” instead of turning “ON” in one go.

I’m not sure why you find this behaviour surprising.

Your button widget on V20 is setting D1 to LOW
Your timer runs the inappropriately named checkPhysicalButton function which looks at Pin D0 and sees that it’s LOW
It then turns your button widget off, via the Blynk.virtualWrite(V20, LOW) command

I think that your are too focused on the code and aren’t giving enough thought to what the functionality should look like.

Pete.

1 Like

I believe the flashing is do to your code. Have you tried it with the checkPhysicalButton() function removed? I suspect you wont have the flashing issue then.

Using Blynk.virtualWrite(); will change the way the virtual button looks in the app. If you do Blynk.virtualWrite(V20, 1); the button will look like it is ON. If you do Blynk.virtualWrite(V20, 0); the button will look like it is OFF. (provided it has the default settings)

So if the

if( (p_tm->tm_hour == 18) && (p_tm->tm_min == 10) && (digitalRead(D0) == HIGH)){
     digitalWrite(D0,LOW);
     Blynk.virtualWrite(V20, 1);
 }

statement runs and the virtual button is OFF, the Blynk.virtualWrite(V20, 1); will turn the button ON in the app.

1 Like

OK , i think i might have solved this flashing issue,huge thanks to @Eugene for this post

Firstly changed the super “inappropriately” named checkPhysicalButton to checkPinState :confused: (that itself might solved all of it ngl)

then change timer accounting for the delay to timer6 = timer.setInterval(1530L, checkPinState);

Lastly call checkPinState for every BLYNK_WRITE like this,

BLYNK_WRITE(V20)
{
  if (param.asInt()) // The button widget sent a 1 (true)
  {
    digitalWrite(D0,HIGH);
    checkPinState();
  }
  else // The button widget is not true, so it's false (0)
  {
    digitalWrite(D0,LOW);
    checkPinState();
  }
}

so that you don’t have to wait for a timer callback.There are still few random flashing of buttons sometimes but very few so can be ignored.

Huge thanks to all for helping me out. :smiley:

I have just one doubt left , is it necessary to call every Blynk.syncVirtual(VXX); separately because I’m just using

  BLYNK_CONNECTED()
{
  Blynk.syncAll();
}

and it seems to work just fine,and I’m calling ` Blynk.syncAll();’ separately in void setup() also, so is that fine too?

It’s difficult to say, based on the snippets of code that you’ve posted, and not knowing why you’re synchronising the device with your app, but Blynk.syncAll() is what we describe in English as “A sledgehammer to crack a nut”.
It forces the server to send updated values for all physical pins, and all 128 virtual pins. This takes time, and can cause timing issues. In my experience, you’re much better-off explicitly synchronising the virtual pins that are needed.

Whilst you may not think it’s important for you to give your variables and functions meaningful names, you’ll very quickly realise that it makes life much easier for you in the future when you come back to the code to try to make some improvements or to debug some issues. And, if you want others to help with coding issues then having meaningful names, along with in-code documentation of the logic, is really important.

That tells me that you still have an underlying flaw with your code structure. If you share your complete code, along with details of what widgets you have in your app and how they are configured, then we’ll take a look at it and point you in the right direction to fix the issue. You’ll find that it’s much better to fix the problem now, rather than later.

Pete.