Stepper: ESP8266 crashes if I do too many steps at once

Hi,

following scenario:
stepper motor
Driver A4988
ESP8266
contolled by webinterface ( using wifi)

The motor works pretty well. It's moving smoothly forward and reverse and everything is fine. Despite of one thing :smiley:

If I let do the motor too many steps at once to move a sled all over my testing area (maybe 40cm), the esp crashes and reboots.

on the seral interface it looks like this:

Connecting to fritzboxdampf
...........
WiFi connected
Server started
Use this URL to connect: http://192.168.178.98/
new client
GET /Command=backward HTTP/1.1
Client disonnected

new client
GET /favicon.ico HTTP/1.1
Client disonnected

new client
GET /Command=forward HTTP/1.1

--------------- CUT HERE FOR EXCEPTION DECODER ---------------

Soft WDT reset

>>>stack>>>

ctx: cont
sp: 3ffffd90 end: 3fffffc0 offset: 01a0
3fffff30:  3fffdad0 00000000 00000000 40100f20  
3fffff40:  40100260 3ffee72c 000004e2 40100290  
3fffff50:  3fffdad0 000004e2 3ffee72c 402012c0  
3fffff60:  40206518 00000000 00001388 00010d51  
3fffff70:  00000000 00000000 3fff0104 00000000  
3fffff80:  3ffef6dc 001d001f 8a00616d 40100164  
3fffff90:  3fffdad0 00000000 3ffee7a4 3ffee7b8  
3fffffa0:  3fffdad0 00000000 3ffee7a4 4020371c  
3fffffb0:  feefeffe feefeffe 3ffe85f0 40100c01  
<<<stack<<<

--------------- CUT HERE FOR EXCEPTION DECODER ---------------

 ets Jan  8 2013,rst cause:2, boot mode:(3,6)

load 0x4010f000, len 3460, room 16 
tail 4
chksum 0xcc
load 0x3fff20b8, len 40, room 4 
tail 4
chksum 0xc9
csum 0xc9
v00044d30
~ld


Connecting to fritzboxdampf
.......
WiFi connected
Server started
Use this URL to connect: http://192.168.178.98/

It never happens if i let him move only have the way.
I do not have any clue why this happens.
Here is my code. Maybe you have an idea.



#include <ESP8266WiFi.h>

const char* ssid = "fritzboxdampf";
const char* password = "mypassword";
const int switch1 = 14;
const int switch2 = 12;
int switchval1 = 1;
int switchval2 = 1;
int pinRelais = 5;
int stepPin = 0; //GPIO0---D3 of Nodemcu--Step of stepper motor driver
int dirPin  = 2; //GPIO2---D4 of Nodemcu--Direction of stepper motor driver
// Motor steps per rotation
const int STEPS_PER_REV = 200;

WiFiServer server(80);

void setup() {
  Serial.begin(115200);
  delay(10);
  pinMode(pinRelais , OUTPUT);
  pinMode(switch1, INPUT);
  pinMode(switch2, INPUT);
  pinMode(stepPin, OUTPUT); //Step pin as output
  pinMode(dirPin,  OUTPUT); //Direcction pin as output
  digitalWrite(stepPin, LOW); // Currently no stepper motor movement
  digitalWrite(dirPin, LOW);
  //relais aus
  digitalWrite(pinRelais , LOW);
  delay(1000);

  // Connect to WiFi network
  Serial.println();
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");

  // Start the server
  server.begin();
  Serial.println("Server started");


  // Print the IP address on serial monitor
  Serial.print("Use this URL to connect: ");
  Serial.print("http://");    //URL IP to be typed in mobile/desktop browser
  Serial.print(WiFi.localIP());
  Serial.println("/");

}

void loop() {
  // Check if a client has connected
  WiFiClient client = server.available();
  if (!client) {
    return;
  }


  // Wait until the client sends some data
  Serial.println("new client");
  while (!client.available()) {
    delay(1);
  }

  // Read the first line of the request
  String request = client.readStringUntil('\r');
  Serial.println(request);
  client.flush();

  // Match the request
  int i = 0;
  int value = LOW;

  if (request.indexOf("/Command=forward") != -1)    {
    //relais an
    digitalWrite(pinRelais , HIGH);
    delay(100);

    // Set motor direction clockwise
    digitalWrite(dirPin, HIGH);

    // Spin motor one rotation slowly
    for (int x = 0; x < STEPS_PER_REV * 17 && switchval1 == 1; x++) {
      switchval1 = digitalRead(switch1);
      digitalWrite(stepPin, HIGH);
      delayMicroseconds(700);  //maximum 500, das ist das schnellste bei dem PM42S. 700 hรถrt sich gesund an
      digitalWrite(stepPin, LOW);
      delayMicroseconds(700);
      switchval2 = 1;
    }
    digitalWrite(pinRelais , LOW);
  }
  if (request.indexOf("/Command=backward") != -1) { // Set motor direction counterclockwise

    //relais an
    digitalWrite(pinRelais , HIGH);
    delay(100);

    digitalWrite(dirPin, LOW);

    // Spin motor two rotations quickly
    for (int x = 0; x < STEPS_PER_REV * 17 && switchval2 == 1 ; x++) {
      switchval2 = digitalRead(switch2);
      digitalWrite(stepPin, HIGH);
      delayMicroseconds(700);
      digitalWrite(stepPin, LOW);
      delayMicroseconds(700);
      switchval1 = 1;
    }
    digitalWrite(pinRelais , LOW);
  }

  // Return the response
  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: text/html");
  client.println(""); //  do not forget this one
  client.println("<!DOCTYPE HTML>");
  client.println("<html>");
  client.println("<h1 align=center>Stepper motor controlled over WiFi</h1><br><br>");
  client.print("Stepper motor moving= ");

  if (value == HIGH) {
    client.print("Forward");
  } else {
    client.print("Backward");
  }
  client.println("<br><br>");
  client.println("<a href=\"/Command=forward\"\"><button>Forward </button></a>");
  client.println("<a href=\"/Command=backward\"\"><button>Backward </button></a><br />");
  client.println("</html>");
  delay(1);
  Serial.println("Client disonnected");
  Serial.println("");

}

WDT reset suggests insufficient yield

You block loop when you create your steps. On ESP8266 loop most not be blocked too long or it crashes. With every loop() cycle yield() is called once to do internal tasks of ESP8266. If there is too much time between yield() calls, the WDT will reset the processor as AWOL already stated.
Step over to a nonblocking design, or at least call yield() while you create the steps.

hm, ok. that sounds new and interesting. How could I redesign it? Do I just have to do separate functions for forward and reverse?

Try putting a yield(); in the loop where you have delays. This tells the ESP to reset the WDT. (WatchDogTimer).

The correct fix is to not write blocking code and not use delay(). But that would be starting over. See if the yield() works first.

Hey Steve,

that is working but then the movement isn't completly smooth anymore.
Is there an other way to achieve my goal?

The problem is, that yield() sometimes needs a considerable amount of time to execute. This disturbs your stepper timing.

You could try my MobaTools library to create the step pulses. This library creates the pulses in an ISR. That means that the pulses are also created during the execution of yield().
But you must obey that all MobaTools methods are not blocking. If you want still use your blocking design, you must block by yourself while the motor turns, e.g. with a while loop ( don't forget to call yield() in that loop :wink: ).

hey, thank you very much. I did it with the mobatools-library. Now It runs smoothly all over the distance.

But another, a second problem appeared now.
I start the movement by using a webinterface. Unfortunately when I do so, it seems to be that the loop is just waiting for the input of the webserver. During this time, I can't do any other input readings by the pins. I want to use two limit switches. When the sledge accidently touches one of the switches, I can't interrupt the movement by using the moba-library but I can switch off a relais which cuts the power to the stepper.
That's my intention.
But at the moment, this does not work.



#include <ESP8266WiFi.h>
#include <MobaTools.h>

// Adjust pins, steps and time as needed
const byte stepPin = 0;
const byte dirPin  = 2;
const int stepsProUmdr = 800;   // Steps per Revolution ( exammple with 1/4 microsteps )
int stepsProTickf = -400;         // Steps to be executed every intervall
int stepsProTickr = 400;         // Steps to be executed every intervall
//const int intervallZeit = 3000; // Steps are executed every second
//const byte intervallMax = 3;     // change direction every intervallMax intervals

byte intervallZaehler;

MoToStepper myStepper ( stepsProUmdr, STEPDIR );
MoToTimebase intervall;

const char* ssid = "fritzboxdampf";
const char* password = "password";
const int switch1 = 14;
const int switch2 = 12;
int switchval1 = 1;
int switchval2 = 1;
int pinRelais = 5;

WiFiServer server(80);
WiFiClient client;
void setup() {
  Serial.begin(115200);
  delay(10);
  pinMode(pinRelais , OUTPUT);
  pinMode(switch1, INPUT);
  pinMode(switch2, INPUT);
  //  pinMode(stepPin, OUTPUT); //Step pin as output
  //  pinMode(dirPin,  OUTPUT); //Direcction pin as output
  //  digitalWrite(stepPin, LOW); // Currently no stepper motor movement
  //  digitalWrite(dirPin, LOW);
  //relais aus
  digitalWrite(pinRelais , LOW);
  myStepper.attach( stepPin, dirPin );
  myStepper.setSpeed( 600 );  // 60 Umdrehungen/Min
  myStepper.setRampLen( 20 );
  //   intervall.setBasetime( intervallZeit );

  delay(1000);

  // Connect to WiFi network
  Serial.println();
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");

  // Start the server
  server.begin();
  Serial.println("Server started");


  // Print the IP address on serial monitor
  Serial.print("Use this URL to connect: ");
  Serial.print("http://");    //URL IP to be typed in mobile/desktop browser
  Serial.print(WiFi.localIP());
  Serial.println("/");

}

void loop() {
  // Check if a client has connected
  WiFiClient client = server.available();
  if (!client) {
    return;
  }


  // Wait until the client sends some data
  Serial.println("new client");
  while (!client.available()) {
    delay(1);

  }

  switchval1 = digitalRead(switch1);
  switchval2 = digitalRead(switch2);
  Serial.println(switchval1);
  Serial.println(switchval2);
  if ( switchval2 == 0  ) {

    digitalWrite(pinRelais , LOW);
    Serial.println(switchval2);
  }
  if ( switchval1 == 0  ) {

    digitalWrite(pinRelais , LOW);
    Serial.println(switchval1);
  }

  // Read the first line of the request
  String request = client.readStringUntil('\r');
  Serial.println(request);
  client.flush();

  // Match the request
  int i = 0;
  int value = LOW;

  if (request.indexOf("/Command=forward") != -1)    {
    //relais on
    digitalWrite(pinRelais , HIGH);
    delay(100);
    Serial.println(switchval1);

    if ( switchval1 == 1  ) {
      myStepper.doSteps( stepsProTickf );
      Serial.println(switchval1);

      switchval1 = digitalRead(switch1);
      switchval2 = 1;
    }


  }
  if (request.indexOf("/Command=backward") != -1) { // Set motor direction counterclockwise

    //relais on
    digitalWrite(pinRelais , HIGH);
    delay(100);
    Serial.println(switchval2);

    if ( switchval2 == 1  ) {
      myStepper.doSteps( stepsProTickr );
      Serial.println(switchval2);

      switchval2 = digitalRead(switch2);
      switchval1 = 1;
    }


  }

  // Return the response
  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: text/html");
  client.println(""); //  do not forget this one
  client.println("<!DOCTYPE HTML>");
  client.println("<html>");
  client.println("<h1 align=center>Stepper motor controlled over WiFi</h1><br><br>");
  client.print("Stepper motor moving= ");

  if (value == HIGH) {
    client.print("Forward");
  } else {
    client.print("Backward");
  }
  client.println("<br><br>");
  client.println("<a href=\"/Command=forward\"\"><button>Forward </button></a>");
  client.println("<a href=\"/Command=backward\"\"><button>Backward </button></a><br />");
  client.println("</html>");
  delay(1);
  Serial.println("Client disonnected");
  Serial.println("");

}

You should probably switch to either ESP8266Webserver or the ASync version for your web interface.

It's never a good idea to block loop() completely. loop() should always do what it is intended to do: looping.
If you don't block loop, you can at any time read your limit switches and react as apropriate.
If you really want to stay with blocking, you could read your limit switches within the while block.

I would like to avoid blocking it. But I don't know how :confused:
I have no experiences with the webserver thing

Start with looking at the examples, but choose which webserver you want to use, and show us the progress.

Try this: ( only the changed lines )

void loop() {
    //////////////////////////////////
    // check your limit switches here
    ////////////////////////////////////
    if ( !client ) {
        client = server.available();
    } else if ( client.available() ) {


        // Wait until the client sends some data
        Serial.println("new client");
        switchval1 = digitalRead(switch1);
        switchval2 = digitalRead(switch2);
....
        if (request.indexOf("/Command=forward") != -1)    {
            //relais on
            digitalWrite(pinRelais , HIGH);
            delay(100);      // <--- is this really necessary ? why?
            value = HIGH;    //<--- must be set to send correct state to Wifi client ( forward/backward)
            Serial.println(switchval1);
...
...
        client.println("</html>");
        Serial.println("Client disonnected");
        client.stop();
        Serial.println("");
    }
}

P.S. Sorry, forgot some changed lines ( clearing the receive buffer ) :

        Serial.println("Reading client ...");
        String request = client.readStringUntil('\r');
        Serial.println(request);
        // clear receive buffer
        while ( client.read() >= 0 );