Unable to Stop Looped Function Consistently

• Arduino Mega 2560 + ESP8266
• Android
• v1.0.0-beta.3

Hello, and thank you in advance for whoever helps. I am a novice so please excuse improper structure and errors in my code. Any tips and whatnot are always appreciated.

So, my program currently will run either a basic motor function with set duration, speed, and interval if one button is pressed (V1). The other function will run with a selected speed read from a slider widget (V3) on a button press (V5). Both these functions need to loop infinitely, and so far from my testing I believe this code achieves this. However, I am sure there is a better way.

The problem that I am currently facing is that when I do press my stop buttons (V2 and V6), the functions will stop only when they reach the delay(5000) at the end of the function, and in some cases it does not stop at all if I press the buttons only once.


//Include the Arduino Stepper Library
#include <Stepper.h>

//Include the Blynk Library
#define BLYNK_PRINT Serial
#include <ESP8266_Lib.h> 
#include <BlynkSimpleShieldEsp8266.h>

//Blynk Initialization
char auth[] = 
char ssid[] = 
char pass[] = 
#define EspSerial Serial3
#define ESP8266_BAUD 115200
ESP8266 wifi(&EspSerial);

//Blynk Variables
int switchStatus1; //start button for default function
int latch1; //latch for default function loop

int switchStatus2; //start button for custom function
int latch2; //latch for custom function loop
int speedInput; //variable for custom function speed
int rotationInput; //variable for custom function # of rotations

//Motor Variables
// Number of steps per internal motor revolution 
const float STEPS_PER_REV = 32; 

// Number of Steps Required
int StepsRequired;


// The pins used are 8,9,10,11 
// Connected to ULN2003 Motor Driver In1, In2, In3, In4 
// Pins entered in sequence 1-3-2-4 for proper step sequencing

Stepper steppermotor(STEPS_PER_REV, 8, 10, 9, 11);

void setup()
{
  Serial.begin(115200);
  delay(10);
  EspSerial.begin(ESP8266_BAUD); //serial initialization and default baud rate for ESP module
  delay(10);

Blynk.begin(auth, wifi, ssid, pass);                          //Reguler server
//Blynk.begin(auth, wifi, ssid, pass,"blynk-cloud.com", 8080);    //Local server
//Blynk.begin(auth, ssid, pass, IPAddress(45,55,96,146), 8080);
}

//Blynk Functions

  //Default Function
  // write data from blynk on virtual 1 (start default function)
  BLYNK_WRITE(V1) {
  switchStatus1 = param.asInt();
  latch1 = true;
  if(latch1 == true){ //if latch is set loop function
    defaultRun();
    if(latch1 == false){
      return;
  }
 }  
}

  // write data from blynk on virtual 2 (stop functions)
  BLYNK_WRITE(V2) {
  if (param.asInt());
   latch1 = false; //unlatch
}

  //Custom Function  
  // write data from blynk on virtual 3 (speed input)
  BLYNK_WRITE(V3) {
  speedInput = param.asInt();
  }
  
 /*// write data from blynk on virtual 4 (rotation input)
  BLYNK_WRITE(V4) {
  rotationInput = param.asInt();
  }*/
  
// write data from blynk on virtual 6 (start custom function)
   BLYNK_WRITE(V5) {
  switchStatus2 = param.asInt();
  latch2 = true;
  if(latch2==true){ //if latch is set loop function
    customRun();
    if(latch2 == false){
      return;
  }
 }    
}

  // write data from blynk on virtual 2 (stop functions)
  BLYNK_WRITE(V6) {
  if (param.asInt());
   latch2 = false; //unlatch 
}

void loop()
{
  Blynk.run(); //begin blynk
}

void defaultRun(){
  steppermotor.setSpeed(1000);    
  StepsRequired  =  2050;      
  steppermotor.step(StepsRequired);
  delay(2000);
  
  steppermotor.setSpeed(1000);    
  StepsRequired  =  - 2050;
  steppermotor.step(StepsRequired);
  
  // Wait before rotation again
  delay(5000);
 }

void customRun(){
  steppermotor.setSpeed(speedInput);    
  StepsRequired  =  2050;   
  steppermotor.step(StepsRequired);
  delay(2000);
  
  steppermotor.setSpeed(speedInput);    
  StepsRequired  =  - 2050; 
  steppermotor.step(StepsRequired);
  
  // Wait before rotation again
  delay(5000);
 }

I don’t see how, as your code contains no loop that will re-trigger the rotation once your *Run() functions have completed - unless of course you are using a widget on a Push interval (not the way to do this).

You can’t use delays with Blynk.
Blynk requires constant communication between the device and the server. For this to happen, Blynk.run() - which is in your void loop - needs to be processed every few milliseconds.
Using delay() blocks all code execution for the duration of the delay, so Blynk.run doesn’t get executed for the duration of the delay.
This makes the device unresponsive to widget button presses, and will lead to disconnections from the Blynk server.

You probably need to use interval timers and a flag to trigger your repeated rotation, but may need to use some interval timers as well. It’s difficult to say without understanding more about the goal of the overall project and how you are driving your motors.

If you want to learn more about how to use timers then read this:

Pete.

Thank you for the information Pete, I have read through the post, and have gotten the loop to work using a timer. However, I am having trouble replacing the delays. I have tried using a timeout but to no avail.

I am making a watch winder utilizing a 28-BYJ48 stepper motor with a ULN2003 motor driver. When I push a start button it is to run a preset function (defaultRun) that will rotate the watches clockwise 15 times wait 2 seconds, then repeat that in the counter clockwise direction. After these two sets of rotations it will wait 1 hour and then repeat. For the purpose of testing I have reduced these times in the code here.Upon the press of the stop button it will stop this function.

The next function (customRun) will operate the same as the previous with the exception that slider widgets (reading into int speedInput and int rotation input) can be used to change the speed of the motor and the number of rotations during the program loop. I haven’t yet coded the rotation slider code as I need to remap the values to correspond with the motor variables.

In the code I’m posting now I have removed the customRun variables as they are not relevant right now. But in my previous code you can see the original functionality

Anyways, as I was saying I don’t think I am using the timeout function of blynktimer correctly. It will run the loop, but does not pause as I would think it does; and after 2 loops or so it just seems to ignore the timeouts. The stop button does work but only after the 2 rotations are done, which isn’t a big issue. Here is the code that I currently have after implementing the timer.


//Include the Arduino Stepper Library
#include <Stepper.h>

//Include the Blynk Library
#define BLYNK_PRINT Serial
#include <ESP8266_Lib.h> 
#include <BlynkSimpleShieldEsp8266.h>

//Blynk Initialization
char auth[] = 
char ssid[] =
char pass[] = 
#define EspSerial Serial3
#define ESP8266_BAUD 115200
ESP8266 wifi(&EspSerial);

//Blynk Variables
int switchStatus1; //start button for default function
bool latch1 = false; //latch for default function loop


BlynkTimer timer; //define timer for default function

//Motor Variables
// Number of steps per internal motor revolution 
const float STEPS_PER_REV = 32; 

// Number of Steps Required
int StepsRequired;

// The pins used are 8,9,10,11 
// Connected to ULN2003 Motor Driver In1, In2, In3, In4 
// Pins entered in sequence 1-3-2-4 for proper step sequencing

Stepper steppermotor(STEPS_PER_REV, 8, 10, 9, 11);

void setup()
{
  timer.setInterval(9000L, defaultRun);//call default function every 9 seconds
  Serial.begin(115200);
  delay(10);
  EspSerial.begin(ESP8266_BAUD); //serial initialization and default baud rate for ESP module
  delay(10);

Blynk.begin(auth, wifi, ssid, pass);                          //Reguler server
//Blynk.begin(auth, wifi, ssid, pass,"blynk-cloud.com", 8080);    //Local server
//Blynk.begin(auth, ssid, pass, IPAddress(45,55,96,146), 8080);
}

//Blynk Functions

  //Default Function
  // write data from blynk on virtual 1 (start default function)
  BLYNK_WRITE(V1) {
  switchStatus1 = param.asInt();
  latch1 = true;
}

  // write data from blynk on virtual 2 (stop functions)
  BLYNK_WRITE(V2) {
  if (param.asInt());
  latch1 = false;
}

  
void loop()
{
  timer.run(); //call timer 
  Blynk.run(); //begin blynk

}

void defaultRun(){
  if(latch1 == true){
    steppermotor.setSpeed(1000);    
    StepsRequired  =  2050;      
    steppermotor.step(StepsRequired);
    
    timer.setTimeout(2000L, [](){
       steppermotor.setSpeed(1000);    
      StepsRequired  =  - 2050;
      steppermotor.step(StepsRequired);
      timer.setTimeout(5000L, defaultRun);
   });
  }
 }

I need the function to pause for 2 seconds before switching rotation direction. Then at the end pause for 5 seconds before looping again.

That’s correct. It’s not a direct replacement for a delay, so you have to understand how it works.
I’d suggest that you put some meaningful serial print debug messages in your code, so that you can track what is happening.

I’m not familiar with the way that the stepper library works. Does this piece of code:

complete before the next lines of code are executed, or does the program execution proceed while the stepper is doing its thing?

Pete.

Your issue, that I see, is that you are trying to run the defaultRun() function independently via two different timers, and after a few iterations they will start conflicting.

Your lambda routine seems correct to me (without testing) and after the initial function call to defaultRun(), it should continue to do it’s thing and call itself at the end without further outside intervention, as long at latch1 == 1

So I see your solution would be to remove the initial interval timer and replace it with a direct one-time function call command, from your V1 button press, that starts the self looping function.

I would also recommend combining both the enabling and disabling of the latch with a single button set to switch mode and if, else code. This way you cannot accidently keep calling the defaultRun() function (and setting off multiple timer functions) with multiple button presses during the timed process.

Thank you very much, that solved my issues, and I implemented the button as a switch and it functions perfectly thank you.

If you would like me to create a new post I can but, I was wondering if there’s a good way to change the slider values to larger numbers when their read from the app.

Here’s the current code I have. The variables read 0 according to the serial monitor. I have also tried placing the map code within void loop but it functions the same way.


//Include the Arduino Stepper Library
#include <Stepper.h>

//Include the Blynk Library
#define BLYNK_PRINT Serial
#include <ESP8266_Lib.h> 
#include <BlynkSimpleShieldEsp8266.h>

//Blynk Initialization
char auth[] = 
char ssid[] = 
char pass[] = 
#define EspSerial Serial3
#define ESP8266_BAUD 115200
ESP8266 wifi(&EspSerial);

//Blynk Variables
int switchStatus1; //start button for default function
bool latch1 = false; //latch for default function loop

int switchStatus2; //start button for custom function
bool latch2; //latch for custom function loop
unsigned long rotationInput; //variable for custom function # of rotations
unsigned long pauseDelay; //variable for custom function, determines the pause duration at the end of the rotation cycle before repeating the cycle again

BlynkTimer timer; //define timer for function delays

//Motor Variables
// Number of steps per internal motor revolution 
const float STEPS_PER_REV = 32; 

// Number of Steps Required
int StepsRequired;

// The pins used are 8,9,10,11 
// Connected to ULN2003 Motor Driver In1, In2, In3, In4 
// Pins entered in sequence 1-3-2-4 for proper step sequencing

Stepper steppermotor(STEPS_PER_REV, 8, 10, 9, 11);

void setup()
{
  Serial.begin(115200);
  delay(10);
  EspSerial.begin(ESP8266_BAUD); //serial initialization and default baud rate for ESP module
  delay(10);

Blynk.begin(auth, wifi, ssid, pass);                          //Reguler server
//Blynk.begin(auth, wifi, ssid, pass,"blynk-cloud.com", 8080);    //Local server
//Blynk.begin(auth, ssid, pass, IPAddress(45,55,96,146), 8080);
}

//Blynk Functions

  //Default Function
  // write data from blynk on virtual 1 (start default function)
  BLYNK_WRITE(V1) {
  switchStatus1 = param.asInt(); //if switch is on set latch and begin function loop
  latch1 = true;
  defaultRun();
  if(switchStatus1 == 0){ //if switch is off unlatch
    latch1 = false;
  }
}

//Custom Function  
 // write data from blynk on virtual 2 (start custom function)
  BLYNK_WRITE(V2) {
  switchStatus2 = param.asInt(); //if switch is on set latch and begin function loop
  latch2 = true;
  customRun();
  if(switchStatus2 == 0){ //if switch is off unlatch
    latch2 = false;
  }
}
 
 // write data from blynk on virtual 3 (rotation input)
  BLYNK_WRITE(V3) {
 rotationInput = map(param.asInt(), 0, 10, 0, 36000000);
  }

   // write data from blynk on virtual 4 (speed input slider)
  BLYNK_WRITE(V4) {
  pauseDelay = map(param.asInt(), 0, 10, 0, 36000000);
  }

void loop()
{
  timer.run(); //call timer 
  Blynk.run(); //begin blynk
}

//Default Function
/*================================================*/
void defaultRun(){
  if(latch1 == true){ //only run if latch is true
    steppermotor.setSpeed(1000);//define speed of motor rotation    
    StepsRequired  =  2050; //one rotation = 2050 steps aprox.      
    steppermotor.step(StepsRequired);
    
    timer.setTimeout(2000L, defaultRun_Reverse); //after 2 seconds call reverse function
  }
 }

void defaultRun_Reverse(){
  if(latch1 == true){
    steppermotor.setSpeed(1000);    
    StepsRequired  =  - 2050; //rotate in opposite direction
    steppermotor.step(StepsRequired);
    timer.setTimeout(5000L, defaultRun); //wait 5 seconds before calling defaultRun function and repeating loop
 }
}
/*================================================*/

//Custom Function
/*================================================*/
void customRun(){
  if(latch2 == true){
    Serial.println(rotationInput);
    steppermotor.setSpeed(1000);    
    StepsRequired  =  rotationInput;      
    steppermotor.step(StepsRequired);
    
    timer.setTimeout(2000L, customRun_Reverse);
  }
 }
 
void customRun_Reverse(){
  if(latch2 == true){
    Serial.println(pauseDelay);
    steppermotor.setSpeed(1000);    
    StepsRequired  =  -(rotationInput);
    steppermotor.step(StepsRequired);
    timer.setTimeout((pauseDelay), customRun);
  }
 }
/*================================================*/

The rotationInput variable needs to read the slider which is set from 0 - 30 and then change that value into a number range of 0 - 61500 (2050 X 15). The other slider needs to go from 0 - 10 on the app and then be changed to 0 - 36000000 (10 hours)

Take a look at the C++ map command, that will do what you want.

Pete.

I have used the map command Pete.

Unless of course you are referring to something else?

So, are you saying that isn’t returning the values you require?

Pete.

Sorry Pete, please disregard my initial post in this thread. I have gotten the map to change the values. It will run the customRun function correctly.

However, I have encountered two issues:

  • As soon as I move the sliders in the app, it says I have disconnected. But, I am still able to run the function remotely after this with the start button.

  • Then if I press the stop button at any time during the function loop, it will run only the initial function customRun (only clockwise rotation) and then stop.

I believe the map functions are somehow interfering with the Blynk communication but I am unsure how.

//Include the Arduino Stepper Library
#include <Stepper.h>

//Include the Blynk Library
#define BLYNK_PRINT Serial
#include <ESP8266_Lib.h> 
#include <BlynkSimpleShieldEsp8266.h>

//Blynk Initialization
char auth[] = "khKRhoan-PFv7ZoQRdRf48DRb-HxAriL";
char ssid[] = "BELL300";
char pass[] = "4FA9F4A7";
#define EspSerial Serial3
#define ESP8266_BAUD 115200
ESP8266 wifi(&EspSerial);

//Blynk Variables
int switchStatus1; //start button for default function
bool latch1 = false; //latch for default function loop

int switchStatus2; //start button for custom function
bool latch2; //latch for custom function loop
unsigned long rotationInput; //variable for custom function # of rotations
unsigned long pauseDelay; //variable for custom function, determines the pause duration at the end of the rotation cycle before repeating the cycle again

BlynkTimer timer; //define timer for function delays

//Motor Variables
// Number of steps per internal motor revolution 
const float STEPS_PER_REV = 32; 

// Number of Steps Required
int StepsRequired;

// The pins used are 8,9,10,11 
// Connected to ULN2003 Motor Driver In1, In2, In3, In4 
// Pins entered in sequence 1-3-2-4 for proper step sequencing

Stepper steppermotor(STEPS_PER_REV, 8, 10, 9, 11);

void setup()
{
  Serial.begin(115200);
  delay(10);
  EspSerial.begin(ESP8266_BAUD); //serial initialization and default baud rate for ESP module
  delay(10);

Blynk.begin(auth, wifi, ssid, pass);                          //Reguler server
//Blynk.begin(auth, wifi, ssid, pass,"blynk-cloud.com", 8080);    //Local server
//Blynk.begin(auth, ssid, pass, IPAddress(45,55,96,146), 8080);
}

//Blynk Functions

  //Default Function
  // write data from blynk on virtual 1 (start default function)
  BLYNK_WRITE(V1) {
  switchStatus1 = param.asInt(); //if switch is on set latch and begin function loop
  latch1 = true;
  defaultRun();
  if(switchStatus1 == 0){ //if switch is off unlatch
    latch1 = false;
  }
}

//Custom Function  
 // write data from blynk on virtual 2 (start custom function)
  BLYNK_WRITE(V2) {
  switchStatus2 = param.asInt(); //if switch is on set latch and begin function loop
  latch2 = true;
  customRun();
  if(switchStatus2 == 0){ //if switch is off unlatch
    latch2 = false;
  }
}
 
 // write data from blynk on virtual 3 (rotation input)
  BLYNK_WRITE(V3) {
  rotationInput = map(param.asInt(), 0, 30, 0, 61500); //change slider value from 0-30 on app, to 0-61500 (61500 = # steps per single rotation X 30 = 2050 X 30)
  }

   // write data from blynk on virtual 4 (duration input slider)
  BLYNK_WRITE(V4) {
  pauseDelay = map(param.asInt(), 0, 10, 0, 36000000); //change slider value from 0-30 on app, to 0-36000000 (36000000 = 10 hours)
  }

void loop()
{
  timer.run(); //call timer 
  Blynk.run(); //begin blynk
}

//Default Function
/*================================================*/
void defaultRun(){
  if(latch1 == true){ //only run if latch is true
    steppermotor.setSpeed(1000);//define speed of motor rotation    
    StepsRequired  =  2050; //one rotation = 2050 steps aprox.      
    steppermotor.step(StepsRequired);
    
    timer.setTimeout(2000L, defaultRun_Reverse); //after 2 seconds call reverse function
  }
 }

void defaultRun_Reverse(){
  if(latch1 == true){
    steppermotor.setSpeed(1000);    
    StepsRequired  =  - 2050; //rotate in opposite direction
    steppermotor.step(StepsRequired);
    timer.setTimeout(5000L, defaultRun); //wait 5 seconds before calling defaultRun function and repeating loop
 }
}
/*================================================*/

//Custom Function
/*================================================*/
void customRun(){
  if(latch2 == true){
    Serial.println(rotationInput);
    steppermotor.setSpeed(1000);    
    StepsRequired  =  rotationInput;      
    steppermotor.step(StepsRequired);
    
    timer.setTimeout(2000L, customRun_Reverse);
  }
 }
 
void customRun_Reverse(){
  if(latch2 == true){
    Serial.println(pauseDelay);
    steppermotor.setSpeed(1000);    
    StepsRequired  =  -(rotationInput);
    steppermotor.step(StepsRequired);
    timer.setTimeout((pauseDelay), customRun);
  }
 }

As I said earlier, I’m not familiar with the functionality of the stepper library, but it may be that the library works in a blocking way, preventing any code execution until the step command has completed.
If this is the case then maybe the library isn’t suitable for use with Blynk for high value step iterations.

Pete.

I do not believe this is the case Pete as without the map code that I recently added, the code functions exactly how I want it to with regard to the defaultRun function.

From what I have observed with my serial print, the program execution proceeds while the stepper is operating from the set code

Which stepper library are you using?
The Arduino stepper library states that its steps command is blocking…

https://www.arduino.cc/en/Reference/StepperStep

Pete.

That is my mistake then Pete. You are correct. I will try to use the Accelstepper library as it is non blocking.

Thank you for your help.