Cannot figure out how to read a virtual button (arduino)

Hello all,

I’m trying to use a virtual button to trigger a function. If the button is pressed, my LCD will print some stuff, there is a delay, and then the function is triggered. For some reason, when I set everything up, the virtual button does not trigger any events on the Arduino. I’m using a serial USB connection. When I use examples to digital write to a pin (lighting up an LED), the IF function I am using works. However, when I integrate it into my code, it does not work. Here is the code that works:

BLYNK_WRITE(0) // At global scope (not inside of the function)
{
  int i = param.asInt();
  if (i == 1)
  {
    digitalWrite(48, HIGH);
    digitalWrite(44, LOW);
  }
  else
  {
    digitalWrite(44, HIGH);
    digitalWrite(48, LOW);
  }
}

Here is the code that does not work:

BLYNK_WRITE(V2) // At global scope (not inside of the function)
{
  int i = param.asInt();
  if (i == 1)
  {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Screen");
    lcd.setCursor(0, 2);
    lcd.print("Loading...");
    delay(3000);
    ExecuteFunction();
  }
}

Why is this happening?

Probably because you use the delay. Blynk doesn’t like delays. If you want to wait a certain amount of time you can use the setTimeout() function of the SimpleTimer library, more info here: http://playground.arduino.cc/Code/SimpleTimer

Thanks, Lichtsignaal. I got it working by removing the delay. However, now something else is happening. My button is set up as a “push.” All I want this thing to do is trigger a function, right? So it doesn’t need to stay “on.” When I start the Blynk app, the button seems to act erratically. It’ll stay on sometimes, or go off but stay highlighted. Usually, when I start the app, the button is already on. What’s going on here?
I

I have no idea. Are you using the Blynk.syncAll() function somewhere? I can imagine that will do something to it.

@jeebsinc my guess is that you have some bad code which is crashing the system when the buttons are pressed, and even in PUSH mode the system is remembering it was “ON” when the crash occurred and your system restarts.

I’m not using the sync.all function anywhere. Should I be? @Costas, what do you mean by “bad code?” How would I go about fixing that?

@jeebsinc an example of “bad code” is:

if (x = y){
  // do something
} 

And good code is:

if (x == y){
  // do something
} 

In simple terms software bugs that you have written into your sketch. Both of the above will compile but the bad code actually means set x to be equal to y rather than doing a true comparison that is almost always the intention with an IF statement.

Bug fixing is a big topic. Check and double check everything. Go back to an earlier version of the sketch and add a bit at a time until the bug appears etc.

If you are able to post your sketch and it’s not too long one of the Blynkers might spot the bug. If you post your sketch though ensure it is formatted (highlight sketch and press the </> button).

No you probably don’t need syncAll for your purposes.

Maybe you don’t have any bugs and you just have a badly connected system. IMHO ESP’s in standalone mode are far more reliable than any other system.

Thanks for the reply @Costas. I’m using a serial USB connection while I wait for my wifi modules to come in. Could the USB be cause for bad connection? Will I see better results when I’m using a wifi module?

I have used Ethernet, USB, ESP as shield and ESP standalone.

ESP standalone is by far the easiest, cheapest and most stable system there is.

@Costas, I ordered an ESP standalone. So I’ll be waiting for that to come in. I decided to scrap the buttons in Blynk and use physical push buttons for this project. I realized that anytime a button would need to be pressed, the person would have to be at the physical location of the system anyway to perform another task. Thanks so much for your help.

I have yet another question. I’m attempting to use sliders to set temperature set points as well as time values for a millis countdown I have coded. I would think that all I need to do is use some sort of assignment of variables. Is this possible? How should I go about it?

@jeebsinc I should have added that my passion for ESP’s is for one’s that have USB ports (WeMos, NodeMCU etc and certainly not the ESP01).

Sliders are very easy to use.

Normally you will assign a global variable for use throughout your sketch. Extract below is from one of our sketches. Don’t worry about our functions like rfchoice() etc but hopefully you get the idea.

unsigned int DurationMinutesV26Slider;  // device on for 0 to 60 minutes

BLYNK_WRITE(V26) // Slider 0 to 60 minutesduration
{
  DurationMinutesV26Slider = param.asInt();
  delay(150);     //TODO is this debounce still needed?
  if(DurationMinutesV26Slider > 0){
    CentralHeating = 1;
    Blynk.virtualWrite(V4, 1);
    rfchoice();
    terminal.print(" for ");
    terminal.print(DurationMinutesV26Slider);
    terminal.print(" minute(s).");
    terminal.flush();  
    timer.enable(countdown); // start 1 minute countdown 
  }
  else{  // slider set to zero so turn RF device OFF
    CentralHeating = 0;
    Blynk.virtualWrite(V4, 0);
    rfchoice();    
  }
}

@Costas Thanks for the example. I’ll try to implement when I get home and let you know how it goes.

@Costas So I tried to implement your example sketch. I should have prefaced this with the fact that I’m using Arduino’s PID library. As such, I’m trying to adjust my setpoints with the sliders. It’s not working. It compiles, but the rest of my code starts to act erratically.

My setpoint value is in my setup function, which is (I think) the reason for the erratic behavior. Do you have any idea as to how I could use the slider to adjust my setpoint? I would only need to do it once - not continuously. After the setpoint value is set with the slider, I would press a physical button and my process would start. No need to update setpoint values.

Is it critical to use the PID library or can you manage with some simple comparisons? Memory-wise it would probably be better to do simple comparisons.

If you adapt a piece of code and the rest starts acting weird, try and eliminate the rest and add functions step-by-step so you can trace the problem. This extensive, boring, crappy work is known as debugging (which refers to the first computer ever to be de-bugged, a literal bug was messing with the vacuum tubes and had to be squashed, so now you know!). This is probably the hardest part of writing code, but there is no other solution than step-by-step (or start from scratch, which I do often, it pretty much always gives you new insight as how you code should and should not work).

@Lichtsignaal It is definitely critical to use the PID library. My system has to have precise temperature control with little to no overshoot. I wanted to avoid starting from scratch but it’s looking like that’s the only option. Also…I didn’t know that’s where the term “debugging” came from so thanks for that info. Every day’s a school day, right? Anyway, I’ll start debugging this thing and let everybody know how it turns out. Thanks for the response!

@jeebsinc can you post the code from your slider and the line(s) from setup() that are for the setpoints.

What datatype are the setpoints (integers, floats etc)?

Did you set the slider widget to only update “ON RELEASE”?

@Costas Setpoint is a double. I did set the slider widget to only update on release. I removed my setpoint from setup and placed it in the Blynk.write function.

Here’s the code:

#include <elapsedMillis.h>
#include <LiquidCrystal.h>
#include <PID_v1.h>
#include <OneWire.h>
#include <DallasTemperature.h>

#include <SoftwareSerial.h>
SoftwareSerial DebugSerial(0, 1); // RX, TX
#define BLYNK_PRINT DebugSerial
#include <BlynkSimpleStream.h>
char auth[] = "AUTHcode_here";



unsigned int Temp1;



double Setpoint1, Input1, Output1;
PID myPID1(&Input1, &Output1, &Setpoint1, 200, 10, 15, DIRECT);
int WindowSize = 10000;
unsigned long windowStartTime;

const int a1 = 46;
const int a2 = 48;
const int a3 = 50;
const int a4 = 52;
const int p = 44;
const int h = 40;
const int a = 2;
const int sensor1 = 39;
const int sensor2 = 41;
const int btn = 22;
int btnState = 0; //

#define ONE_WIRE_BUS 51 //data wire is plugged into pin 2 on the Arduino
OneWire oneWire(ONE_WIRE_BUS); //setup a oneWire instance to communicate with any OneWire devices
//(not just Maxim/Dallas temperature ICs)
DallasTemperature sensors(&oneWire); //pass  oneWire reference to Dallas Temperature.

elapsedMillis timeElapsed;
unsigned long previousMillis = 0;

LiquidCrystal lcd(12, 11, 10, 9, 8, 7); //lcd pin declaration

void setup() {
  DebugSerial.begin(9600);
  Serial.begin(9600);
  Blynk.begin(auth, Serial);
  lcd.begin(16, 2);

  digitalWrite(a1, LOW);
  digitalWrite(a2, LOW);
  digitalWrite(a3, LOW);
  digitalWrite(a4, LOW);
  digitalWrite(p, LOW);
  digitalWrite(h, LOW);

  pinMode(a1, OUTPUT);
  pinMode(a2, OUTPUT);
  pinMode(a3, OUTPUT);
  pinMode(a4, OUTPUT);
  pinMode(p, OUTPUT);
  pinMode(h, OUTPUT);
  pinMode(a, OUTPUT);
  pinMode(sensor1, INPUT_PULLUP); // HLT float switch set as input
  pinMode(sensor2, INPUT_PULLUP); // BK float switch set as input

  windowStartTime = millis();

  myPID1.SetOutputLimits(0, WindowSize);
  myPID1.SetMode(AUTOMATIC);

  sensors.begin();
  delay(150);
}


/********************************/
BLYNK_WRITE(V10)
{
  Temp1 = param.asInt();
  delay(150);
  Setpoint1 = Temp1;
}
/********************************/


void loop() {
  Blynk.run();
  sensors.requestTemperatures();
  float tempF_1 = sensors.getTempFByIndex(0);
  Blynk.virtualWrite(3, tempF_1);
  btnState = digitalRead(btn);

  if (btnState == LOW && digitalRead(sensor1) == HIGH) {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Auto Mode");
    lcd.setCursor(0, 2);
    lcd.print("Initializing...");
    delay(3000);
    //some_function_here();
  }
  else {
    lcd.setCursor(0, 0);
    lcd.print("Yo!");
    digitalWrite(a1, LOW);
    digitalWrite(a2, LOW);
    digitalWrite(a3, LOW);
    digitalWrite(a4, LOW);
    digitalWrite(p, LOW);
    digitalWrite(h, LOW);
  }
}

@jeebsinc notice you are using elapsedMillis.h but really you need the much more powerful SimpleTimer that is included in the Blynk libraries download.

Your loop should look like this:

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

Everything else should go in timed functions. At the moment you are trying to read the temperature sensor a thousand times a second. Blynk.virtualWrite(3, tempF_1) is an absolute no no in loop().

Blynk widgets can have the following datatypes:

param.asInt();
param.asFloat();
param.asDouble();
param.asStr();

So you can use Setpoint1 = param.asDouble() as I’m not sure you can cast Temp1 (int) to a double with Setpoint1 = Temp1.

You shouldn’t have any delay() commands in your sketch and that is where SimpleTimer comes in.
A few hundred ms is probably ok but no longer.

delay(150) in V10 is a throwback to the days before Blynk had send ON RELEASE and was a basic debounce of the slider. You can probably remove it now.

@Costas I assigned the setpoint as a double. However, it did not seem to work. When I press my physical button, the process should not advance immediately to userBtn();. However, that’s what it’s doing. It should wait for the temp to get above the setpoint (the value the slider is set to). It should stay in Function1 until that setpoint is reached.

On the topic of me using elapsedmillis.h - I wrote my code using this library and this is the way my delays are working in the functions. If at all possible, I’d rather not figure out how to use the SimpleTimer lib as my current method is working for delay purposes. BUT, if the elapsedmillis.h is what’s causing my problems, I’ll gladly ditch it and try to figure out the simple timer method. @Lichtsignaal

Here is my full code:

#include <SoftwareSerial.h>
SoftwareSerial DebugSerial(0, 1); // RX, TX
#define BLYNK_PRINT DebugSerial
#include <BlynkSimpleStream.h>
char auth[] = "AuthCode";

#include <elapsedMillis.h>
#include <LiquidCrystal.h>
#include <PID_v1.h>
#include <OneWire.h>
#include <DallasTemperature.h>

//PID setup
double Setpoint1, Input1, Output1;

PID myPID1(&Input1, &Output1, &Setpoint1, 200, 10, 15, DIRECT);

int WindowSize = 10000;
unsigned long windowStartTime;

//Sensors & components
const int v1 = 46;
const int v2 = 48;
const int v3 = 50;
const int v4 = 52;
const int p = 44;
const int h = 40;
const int a = 2;
const int sensor1 = 39;
const int sensor2 = 41;
const int Auto_btn = 22;
int A_btnState = 0; //

#define ONE_WIRE_BUS 51 //data wire is plugged into pin 2 on the Arduino
OneWire oneWire(ONE_WIRE_BUS); //setup a oneWire instance to communicate with any OneWire devices
//(not just Maxim/Dallas temperature ICs)
DallasTemperature sensors(&oneWire); //pass  oneWire reference to Dallas Temperature.

elapsedMillis timeElapsed;
unsigned long previousMillis = 0;
unsigned long pDelay;

LiquidCrystal lcd(12, 11, 10, 9, 8, 7); //lcd pin declaration
byte customChar1[8] = {
  0b11111,
  0b11111,
  0b11111,
  0b11111,
  0b11110,
  0b11000,
  0b10000,
  0b11111
};
byte customChar2[8] = {
  0b11111,
  0b11110,
  0b11100,
  0b10000,
  0b00000,
  0b00000,
  0b00000,
  0b11111
};
byte customChar3[8] = {
  0b11111,
  0b01111,
  0b00111,
  0b00001,
  0b00000,
  0b00000,
  0b00000,
  0b11111
};
byte customChar4[8] = {
  0b11111,
  0b11111,
  0b11111,
  0b11111,
  0b00111,
  0b00011,
  0b00001,
  0b11111
};
byte customChar5[8] = {
  0b11111,
  0b00000,
  0b01110,
  0b01000,
  0b01010,
  0b01110,
  0b00000,
  0b11111
};
byte customChar6[8] = {
  0b11111,
  0b00000,
  0b11011,
  0b10101,
  0b10101,
  0b10001,
  0b00000,
  0b11111
};
byte customChar7[8] = {
  0b11111,
  0b00000,
  0b01000,
  0b01000,
  0b01110,
  0b01110,
  0b00000,
  0b11111
};

void setup() {
  DebugSerial.begin(9600);
  Serial.begin(9600);
  Blynk.begin(auth, Serial);

  lcd.begin(16, 2);

  lcd.createChar(0, customChar1);
  lcd.createChar(1, customChar2);
  lcd.createChar(2, customChar3);
  lcd.createChar(3, customChar4);
  lcd.createChar(4, customChar5);
  lcd.createChar(5, customChar6);
  lcd.createChar(6, customChar7);

  digitalWrite(v1, LOW);
  digitalWrite(v2, LOW);
  digitalWrite(v3, LOW);
  digitalWrite(v4, LOW);
  digitalWrite(p, LOW);
  digitalWrite(h, LOW);
  delay(150);

  pinMode(v1, OUTPUT);
  pinMode(v2, OUTPUT);
  pinMode(v3, OUTPUT);
  pinMode(v4, OUTPUT);
  pinMode(p, OUTPUT);
  pinMode(h, OUTPUT);
  pinMode(a, OUTPUT);
  pinMode(sensor1, INPUT_PULLUP); // HLT float switch set as input
  pinMode(sensor2, INPUT_PULLUP); // BK float switch set as input

  windowStartTime = millis();

  myPID1.SetOutputLimits(0, WindowSize);
  myPID1.SetMode(AUTOMATIC);

  //begin reading sensors
  sensors.begin();
  delay(150);
}

BLYNK_WRITE(V10)
{
  Setpoint1 = param.asDouble();
}







void loop() {
  Blynk.run();
  //sensors.requestTemperatures();
  //float tempF_1 = sensors.getTempFByIndex(0);
  //Blynk.virtualWrite(3, tempF_1);
  A_btnState = digitalRead(Auto_btn);

  if (A_btnState == LOW && digitalRead(sensor1) == HIGH) {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Auto Mode");
    lcd.setCursor(0, 2);
    lcd.print("Initializing...");
    delay(3000);
    Function1();
  }
  else {
    lcd.setCursor(0, 0);
    lcd.print("Abcd");
    lcd.setCursor(0, 2);
    lcd.print("  ");
    lcd.write(byte(0));
    lcd.write(byte(1));
    lcd.write(byte(2));
    lcd.write(byte(3));
    lcd.write(byte(4));
    lcd.write(byte(5));
    lcd.write(byte(6));
    lcd.write(byte(0));
    lcd.write(byte(1));
    lcd.write(byte(2));
    lcd.write(byte(3));
    digitalWrite(v1, LOW);
    digitalWrite(v2, LOW);
    digitalWrite(v3, LOW);
    digitalWrite(v4, LOW);
    digitalWrite(p, LOW);
    digitalWrite(h, LOW);
  }
}

void Function1()
{
  pDelay = millis() + 5000;
  while (digitalRead(sensor1) == HIGH) {
    Blynk.run();
    sensors.requestTemperatures();
    float tempF_1 = sensors.getTempFByIndex(0);
    Blynk.virtualWrite(3, tempF_1);
    unsigned long currentMillis = millis();
    lcd.setCursor(0, 0);
    lcd.print("Efgh");
    lcd.setCursor(0, 2);
    lcd.print("Temp: ");
    lcd.print(tempF_1);
    digitalWrite(v1, HIGH);
    digitalWrite(v2, HIGH);
    if (pDelay - millis() >= 5000) {
      delay(100);
      digitalWrite(p, HIGH);
    }
    else {
      digitalWrite(p, LOW);
    }
    Input1 = tempF_1;
    myPID1.Compute();
    unsigned long now = millis();
    if (now - windowStartTime > WindowSize) {
      windowStartTime += WindowSize;
      if (Output1 > now - windowStartTime) {
        digitalWrite(h, HIGH);
      }
      else {
        digitalWrite(h, LOW);
      }
    }
    if (tempF_1 >= Setpoint1) {
      lcd.clear();
      UserBtn();
    }
  }
}

void UserBtn()
{
  while (digitalRead(Auto_btn) == HIGH) {
    Blynk.notify("Do something here!");
    sensors.requestTemperatures();
    float tempF_1 = sensors.getTempFByIndex(0);
    //float tempF_2 = sensors.getTempFByIndex(1);
    Blynk.virtualWrite(0, tempF_1);
    lcd.setCursor(0, 0);
    lcd.print("Something happened.");
    lcd.setCursor(0, 2);
    lcd.print("Press btn!");
    tone(a, 2000);
    delay(500);
    noTone(a);
    Input1 = tempF_1;
    myPID1.Compute();
    unsigned long now = millis();
    if (now - windowStartTime > WindowSize) {
      windowStartTime += WindowSize;
    }
    if (Output1 > now - windowStartTime) {
      digitalWrite(h, HIGH);
    }
    else {
      digitalWrite(h, LOW);
    }
  }
  lcd.clear();
  Step1();
}

void Step1()
{
  while (digitalRead(sensor1) == HIGH) {
    sensors.requestTemperatures();
    float tempF_1 = sensors.getTempFByIndex(0);
    //float tempF_2 = sensors.getTempFByIndex(1);
    Blynk.virtualWrite(3, tempF_1);
    lcd.setCursor(0, 0);
    lcd.print("Ramping temp.");
    lcd.setCursor(0, 2);
    lcd.print("Temp: ");
    lcd.print(tempF_1);
    Input1 = tempF_1;
    myPID1.Compute();
    unsigned long now = millis();
    if (now - windowStartTime > WindowSize) {
      windowStartTime += WindowSize;
    }
    if (Output1 > now - windowStartTime) {
      digitalWrite(h, HIGH);
    }
    else {
      digitalWrite(h, LOW);
    }
    if (tempF_1 >= Setpoint1) {
      lcd.clear();
      Timer12();
    }
  }
}

void Timer12()
{
  timeElapsed = 0;
  while (timeElapsed < 10000) {
    int mins = timeElapsed / 60000;
    int sec = timeElapsed / 1000;
    sensors.requestTemperatures();
    float tempF_1 = sensors.getTempFByIndex(0);
    //float tempF_2 = sensors.getTempFByIndex(1);
    Blynk.virtualWrite(0, tempF_1);
    lcd.setCursor(0, 0);
    lcd.print("[Timer 1] T: ");
    lcd.print(tempF_1);
    lcd.print("F)");
    lcd.setCursor(0, 2);
    lcd.print("Elapsed t: ");
    lcd.print(mins);
    lcd.print(" min");

    Input1 = tempF_1;
    myPID1.Compute();
    unsigned long now = millis();
    if (now - windowStartTime > WindowSize) {
      windowStartTime += WindowSize;
    }
    if (Output1 > now - windowStartTime) {
      digitalWrite(h, HIGH);
    }
    else {
      digitalWrite(h, LOW);
    }
  }
  tone(a, 2000);
  delay(1000);
  noTone(a);
  lcd.clear();
  loop();
}