Using a mobile device to control a motor via PID.

I have not been able to get the tacho working on the Wemos D1 mini apparently due to interrpts being "interrupted" by wifi priority! Not sure if this is the cause but I can't get regular pulses from the sensor.

I have therefore decided to go back to plan "a" and use 2 processors. A uno for rpm sensor and pid motor control to esc, and the Wemos for everything else including wifi direct to mobile device for control interface.

I have chosen SPI as the method of communication between the wemos and uno but I really stuggle to understand all of this comms stuff. There must be a key ingredient that ties it all together that I just haven't "got" yet. :confused:

So, I'm trying to send arrays from master, (uno), to slave, (wemos, and the have the wemos reply with an array.
If anyone has the time to have a look and comment, you will no doubt notice I have tried to merge an spi example into my sketch.

sketches are attached, thanks in advance.

master_with_uno.ino (5.88 KB)

master_with_wemos.ino (4.85 KB)

I've had a chance to look around the ESP8266 code

This may help you understand whats going on

void setup() {
//...
   SPISlave.onData([](uint8_t * data, size_t len) {
        int message = int((int *)data); //  << Lets look at this shortly
   });
//...
}

what is happening here... This code is an anonymous function also called Lambda
an alternate way to write this would look like:

void MyFunction(uint8_t * data, size_t len){  // uint8_t  is the same as char
           int message = int((int *)data); 
}

void setup() {
//...
   SPISlave.onData(MyFunction); // Stores a pointer to your function to be used when data is recieved
//...
}

The two ways are the same. We are using this type of code already with attachInterrupts() when we pass the function name to the attachInterrupts function.
Lambda basically creates a function without a name to only be used once. by passing the function into the SPISlave.onData class. When data is received this function is triggered. It is an interrupt but it doesn't stop other interrupts from occurring as far as I can tell but overall coding needs to be short just in case it is called again.

the data varable that we are passed is a char array of unsigned bytes (0 to 255) if the value returned from the uno can be used as an unsigned byte rather than an int then we can keep it simple
char Val = data[0];
int message = int((int *)data); // data is a char array and message is an int.
I'm struggling to see this conversion working correctly a union would be better
something simple could work

union DataMask{
        float Float;
	uint32_t UnsignedLong;
	int32_t  Long;
        uint16_t UnsignedInt[2];
        int16_t  Int[2];
        int8_t   Byte[4];
	uint8_t  Char[4];
};
DataMask Data;

to get the 4 chars on the uno. set the data into the union Data.Float = 10.2; and you can send Data.char (contains 4 chars) over the SPI bus
to get the float back
have the same union at the ESP and set the characters into Data.Char[] always set 4 chars and you can get any type out.

I need to send lots of data so I am working on something like this:

typedef struct sensorData_t { // This structure must be less than 32 bytes to work easily 
  byte stat;
  byte sensorId;
  byte sensortype;
  byte isWet;
  uint16_t temp;
  float volts;
  byte signal;
};

#define PACKET_SIZE sizeof(sensorData_t)

typedef union SPI_Packet_t {
 sensorData_t sensor;
 uint8_t SPIPacket[PACKET_SIZE]; 
};

The union links the two data types together they must be the same size to easily do this
The structure of x bytes becomes a nice string of x bytes and can be sent using SPI
the limit is 32 bytes without additional modifications to the code.

Z

Zhomeslice, I cannot thank you enough for taking the time to look at my code and reply in such a way as to explain what this stuff means.

I have been reading for days about this stuff and I am no closer to working code than what I was when I started. I either cannot find examples that fit my use, or when I do find an example it won't compile and I find myself distracted from the objective to no avail.
I think I understand how to communicate with a device like a digital pot ok, but when it comes to 2 way between master and slave, it just doesn't sink in!

Your explanation goes a long way towards helping me understand this stuff, thank you.

union DataMask{
float Float;
uint32_t UnsignedLong;
int32_t Long;
uint16_t UnsignedInt[2];
int16_t Int[2];
int8_t Byte[4];
uint8_t Char[4];
};
DataMask Data;

The uintx_t has me confused. Is this the address in the byte?
Using the DataMask format above, I can send a float + a long + an int + a Char together in one byte?

moose4621:
Zhomeslice, I cannot thank you enough for taking the time to look at my code and reply in such a way as to explain what this stuff means.

I have been reading for days about this stuff and I am no closer to working code than what I was when I started. I either cannot find examples that fit my use, or when I do find an example it won't compile and I find myself distracted from the objective to no avail.
I think I understand how to communicate with a device like a digital pot ok, but when it comes to 2 way between master and slave, it just doesn't sink in!

Your explanation goes a long way towards helping me understand this stuff, thank you.

The uintx_t has me confused. Is this the address in the byte?
Using the DataMask format above, I can send a float + a long + an int + a Char together in one byte?

UNO is the master so set a timer to send the slave data about every half second for example. the slave receives the data and stores it. meanwhile the slave is updating a structure with data like setpoint for your motor it is stored. then when data is sent you trigger the SPISlave.setData(answerUnion.Char); which sends the data back in 32 byte chuncks. if you keep it smaller than 32 bytes it will fit in one transmition

This is the code for the UNO not yet tested

/*
    SPI Safe Master Demo Sketch
    Connect the SPI Master device to the following pins on the esp8266:

    GPIO    NodeMCU   Name  |   Uno
   ===================================
     15       D8       SS   |   D10
     13       D7      MOSI  |   D11
     12       D6      MISO  |   D12
     14       D5      SCK   |   D13

    Note: If the ESP is booting at a moment when the SPI Master has the Select line HIGH (deselected)
    the ESP8266 WILL FAIL to boot!
    This sketch tries to go around this issue by only pulsing the Slave Select line to reset the command
    and keeping the line LOW all other time.

*/
#include <SPI.h>

#include <ESPSafeMaster.h>

ESPSafeMaster esp(SS);
typedef struct Data { // This structure must be less than 32 bytes to work easily 
  float RPM;
  char ErrorMessage[21];
};

#define Data_PACKET_SIZE sizeof(Data)

typedef union Data_Packet {
 Data MyData;
 uint8_t SPIPacket[Data_PACKET_SIZE]; 
};

typedef struct Settings { // This structure must be less than 32 bytes to work easily 
 int setpoint;
 float Kp;
 float Ki;
 float Kd; 
};

#define Settings_PACKET_SIZE sizeof(Settings)

typedef union Settings_Packet {
 Settings MySettings;
 uint8_t SPIPacket[Settings_PACKET_SIZE]; 
};

void send()
{
    Data_Packet DPacket;  // Create an instance of Data_Packet
    DPacket.MyData.RPM = 37.2;// Load the data
    char errorStr[] = "All Ok";// Load the data
    strncpy ( DPacket.MyData.ErrorMessage, errorStr, min(20,sizeof(errorStr)) );
    esp.writeData(DPacket.SPIPacket);
    delay(10);  // I have a way to not block here I'll send you later but for now we will use delay()
    Settings_Packet SPacket; // create an instance of Settings_Packet
    char data[33]; // buffer to hold the data
    esp.readData(data); // load the buffer
    memcpy(SPacket.SPIPacket,data,Settings_PACKET_SIZE);// load the structure
    Serial.println(SPacket.MySettings.setpoint); // read the settings
    Serial.println(SPacket.MySettings.Kp);
    Serial.println(SPacket.MySettings.Ki);
    Serial.println(SPacket.MySettings.Kd);
    
}

ESPSafeMaster.cpp (3.03 KB)

ESPSafeMaster.h (871 Bytes)

This is the code for the ESP8266 (WeMos) I removed the Lambda and used regular funcitons

This is also untested I'll try it out tomorrow. Good Night :slight_smile:

/*
    SPI Slave Demo Sketch
    Connect the SPI Master device to the following pins on the esp8266:

    GPIO    NodeMCU   Name  |   Uno
  ===================================
     15       D8       SS   |   D10
     13       D7      MOSI  |   D11
     12       D6      MISO  |   D12
     14       D5      SCK   |   D13

    Note: If the ESP is booting at a moment when the SPI Master has the Select line HIGH (deselected)
    the ESP8266 WILL FAIL to boot!
    See SPISlave_SafeMaster example for possible workaround

*/
#include "SPISlave.h"
typedef struct Data { // This structure must be less than 32 bytes to work easily
  float RPM;
  char ErrorMessage[21];
};

#define Data_PACKET_SIZE sizeof(Data)

typedef union Data_Packet {
  Data MyData;
  uint8_t SPIPacket[Data_PACKET_SIZE];
};

typedef struct Settings { // This structure must be less than 32 bytes to work easily
  int setpoint;
  float Kp;
  float Ki;
  float Kd;
};

#define Settings_PACKET_SIZE sizeof(Settings)

typedef union Settings_Packet {
  Settings MySettings;
  uint8_t SPIPacket[Settings_PACKET_SIZE];
};
Data_Packet DPacket;  // Create an instance of Data_Packet
Settings_Packet SPacket; // create an instance of Settings_Packet


void RecieveData(uint8_t * data, size_t len) {
  memcpy(DPacket.SPIPacket, data, Data_PACKET_SIZE); // load the structure with recieved data
  char settings[33];
  memcpy(settings, SPacket.SPIPacket, Settings_PACKET_SIZE);
  SPISlave.setData(settings); // Prep the data for return

  Serial.println(DPacket.MyData.RPM); // Display the Data Recieved
  Serial.println(DPacket.MyData.ErrorMessage);
}

void DataSuccessfullySent() {
  Serial.println("Answer Sent");
}

void RecieveStatus(uint32_t data) {
  Serial.printf("Status: %u\n", data);
  SPISlave.setStatus(millis()); //set next status

}


void StatusSuccessfullySent() {
  Serial.println("Status Sent");
}



void setup()
{
  Serial.begin(115200);
  Serial.setDebugOutput(true);

  // data has been received from the master. Beware that len is always 32
  // and the buffer is autofilled with zeroes if data is less than 32 bytes long
  // It's up to the user to implement protocol for handling data length
  SPISlave.onData(RecieveData);

  // The master has read out outgoing data buffer
  // that buffer can be set with SPISlave.setData
  SPISlave.onDataSent(DataSuccessfullySent);

  // status has been received from the master.
  // The status register is a special register that bot the slave and the master can write to and read from.
  // Can be used to exchange small data or status information
  SPISlave.onStatus(RecieveStatus);

  // The master has read the status register
  SPISlave.onStatusSent(StatusSuccessfullySent);

  // Setup SPI Slave registers and pins
  SPISlave.begin();

  // Set the status register (if the master reads it, it will read this value)
  //   SPISlave.setStatus(millis());

  // Sets the data registers. Limited to 32 bytes at a time.
  // SPISlave.setData(uint8_t * data, size_t len); is also available with the same limitation
  SPISlave.setData("");
}

void loop() {
  // you have access to both structures make the changed to them that you want
  static unsigned long _ATimer;
  if ( (unsigned long)(millis() - _ATimer) >= (1000)) {
    _ATimer = millis();
    SPacket.MySettings.Kp += 1; // load the data to be given to the UNO when it asks
    SPacket.MySettings.Ki += 10;
    SPacket.MySettings.Kd += 100;
    SPacket.MySettings.setpoint += 1000;
  }
}

Z

I'm running into a glitch where the Structure or the union isn't the same size. so the resulting data is corrupted I'm Trying to create a solution but meanwhile here's the code for you to try :slight_smile:

SPISlave_Test_Structure_Example.ino (3.89 KB)

SPISlave_SafeMaster_Structure_Example.ino (2.57 KB)

zhomeslice:
I'm running into a glitch where the Structure or the union isn't the same size. so the resulting data is corrupted I'm Trying to create a solution but meanwhile here's the code for you to try :slight_smile:

Only using arduinodroid on the phone at the moment. Sitting on a houseboat at rainbow beach :slight_smile: Happy new year!
I came up with errors when I tried to compile your uno code.
The esp code compiled OK but no testing till Monday night.
I see you have the esp doing the pid compute. I was going to have the uno do pid and tacho. What are your thoughts?

moose4621:
Only using arduinodroid on the phone at the moment. Sitting on a houseboat at rainbow beach :slight_smile: Happy new year!
I came up with errors when I tried to compile your uno code.
The esp code compiled OK but no testing till Monday night.
I see you have the esp doing the pid compute. I was going to have the uno do pid and tacho. What are your thoughts?

I'm Jealous :slight_smile: I just loaded some pictures of rainbow beach and it's beautiful! I'm berried in .6 meters (2 feet) of snow lol. We had a white Christmas and we are staying warm. Webcam @ Idaho Falls

I see you have the esp doing the pid compute.

Actually, it is storing the Kp Ki and Kd values passing them to the UNO over SPI. You are correct in your direction of having the UNO do the task of controlling the motor. While I am not having my UNO control a motor it is controlling a Phase dimming circuit that has many of the same requirements and it also uses PID. So our end communication needs will almost be the same.
Enjoy your vacation :slight_smile: and Happy New Year!

zhomeslice:
I'm Jealous :slight_smile: I just loaded some pictures of rainbow beach and it's beautiful! I'm berried in .6 meters (2 feet) of snow lol. We had a white Christmas and we are staying warm. Webcam @ Idaho Falls

Yes it is beautiful, pity I wasn't there for longer. The snow is something I would like to experience. The webcam shots are great!

zhomeslice:
Actually, it is storing the Kp Ki and Kd values passing them to the UNO over SPI.

Doh! of course. Need to READ what I am reading.

The setpoint from the esp to uno is not being reported correctly.
UNO serial output;
.

13	 45535020486572650000A09A	 ESP Here	 -24576	 
13	 45535020486572650000A09A	 ESP Here	 -24576	 
13	 45535020486572650000A067	 ESP Here	 -24576	 
13	 45535020486572650000A09A	 ESP Here	 -24576	 
13	 45535020486572650000A067	 ESP Here	 -24576	 
13	 45535020486572650000A067	 ESP Here	 -24576	 
13	 45535020486572650000A00	 ESP Here	 -24576

I assume the PACKET_SIZE of 13 is correct?

On esp, the TestValue from uno is not being reported.

14	 45535020486572650000A0F0	RPM 17.90	ErrorMessage UNO All Ok	TestValue 0.00	 
Answer Sent
	RPM 64.20	ErrorMessage UNO All Ok	TestValue 0.00	 
Answer Sent
14	 45535020486572650000A0F0	RPM 46.40	ErrorMessage UNO All Ok	TestValue 0.00	 
Answer Sent
	RPM 35.90	ErrorMessage UNO All Ok	TestValue 0.00	 
Answer Sent

In the SPISlave_Test sketch, why use "strncpy" in "strncpy ( SPacket.MySettings.ErrorMessage, errorStr, _min(10, sizeof(errorStr)) );" instead of "errorStr = ..."? I have not seen strncpy before.

Thanks again for your help Zhomeslice.

Also, in the SPISlave_SafeMaster_Structure_Example you have defined the setpoint as"int16_t setpoint;" Is there a reason for defining it as 16bit rather than 8 bit since, in my case, the setpoint will never exceed 40 rpm?

I have many more dumb questions like this one but will refrain from pestering you :wink:

moose4621:
In the SPISlave_Test sketch, why use "strncpy" in "strncpy ( SPacket.MySettings.ErrorMessage, errorStr, _min(10, sizeof(errorStr)) );" instead of "errorStr = ..."? I have not seen strncpy before.

Thanks again for your help Zhomeslice.

The (Structure).errorStr[10] is pointing to a specific point in memory and has reserved 10 spots. if we want to continue to use this memory we can't point to a different place in memory we need to tediously copy each char to the .errorStr's memory. thankfully we have standard c functions like strncpy() and strcpy() http://www.cplusplus.com/reference/cstring/strncpy/
I'm experiencing the corruption of the numbers also. I believe the compiler for the ESP and the UNO do different things with structures. the UNO structure is slightly smaller than the ESP Structure even though they are the same!
I think we are going to have to have additional code to handle the transition.

Also, in the SPISlave_SafeMaster_Structure_Example you have defined the setpoint as"int16_t setpoint;" Is there a reason for defining it as 16bit rather than 8 bit since, in my case, the setpoint will never exceed 40 rpm?

I have many more dumb questions like this one but will refrain from pestering you :wink:

No questions are dumb :slight_smile: I was there not to long ago and I'm learning along with you
Either data type will be fine. I plan on shifting my setpoint by multiplying it by 10 to convert a floating point number to an int the shifting it back to get 1 decimal point of percision on the other end.
(float) 40.26 * 10 = (int) 402

Send it over SPI >>>
(int) 402 * 0.1 = (float) 40.2
for example
Z

I am trying to merge the SPI code above into my UNO sketch but am getting errors I don't understand.

The sketch is attached.

The errors,

Arduino: 1.6.11 (Linux), Board: "Arduino/Genuino Uno"

master_with_uno_R1:56: error: use of deleted function 'Data_Packet::Data_Packet()'
 Data_Packet DPacket;  // Create an instance of Data_Packet
             ^
/home/chris/Arduino/seeder controller with wemos and uno/master_with_uno_R1/master_with_uno_R1.ino:39:15: note: 'Data_Packet::Data_Packet()' is implicitly deleted because the default definition would be ill-formed:
 typedef union Data_Packet {
               ^
master_with_uno_R1:40: error: union member 'Data_Packet::MyData' with non-trivial 'Data::Data()'
   Data MyData;
        ^
/home/chris/Arduino/seeder controller with wemos and uno/master_with_uno_R1/master_with_uno_R1.ino: In function 'void send()':
master_with_uno_R1:61: error: 'Average' was not declared in this scope
   DPacket.MyData.RPM = Average;
                        ^
Multiple libraries were found for "SPI.h"
 Used: /home/chris/.arduino15/packages/arduino/hardware/avr/1.6.14/libraries/SPI
 Not used: /home/chris/Downloads/arduino-1.6.11/libraries/SPI
exit status 1
use of deleted function 'Data_Packet::Data_Packet()'

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.

master_with_uno_R1.ino (7.57 KB)

moose4621:
I am trying to merge the SPI code above into my UNO sketch but am getting errors I don't understand.

The sketch is attached.

The errors,

Arduino: 1.6.11 (Linux), Board: "Arduino/Genuino Uno"

master_with_uno_R1:56: error: use of deleted function 'Data_Packet::Data_Packet()'
Data_Packet DPacket;  // Create an instance of Data_Packet
            ^
/home/chris/Arduino/seeder controller with wemos and uno/master_with_uno_R1/master_with_uno_R1.ino:39:15: note: 'Data_Packet::Data_Packet()' is implicitly deleted because the default definition would be ill-formed:
typedef union Data_Packet {
              ^
master_with_uno_R1:40: error: union member 'Data_Packet::MyData' with non-trivial 'Data::Data()'
  Data MyData;
       ^
/home/chris/Arduino/seeder controller with wemos and uno/master_with_uno_R1/master_with_uno_R1.ino: In function 'void send()':
master_with_uno_R1:61: error: 'Average' was not declared in this scope
  DPacket.MyData.RPM = Average;
                       ^
Multiple libraries were found for "SPI.h"
Used: /home/chris/.arduino15/packages/arduino/hardware/avr/1.6.14/libraries/SPI
Not used: /home/chris/Downloads/arduino-1.6.11/libraries/SPI
exit status 1
use of deleted function 'Data_Packet::Data_Packet()'

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.

change to

typedef struct Data { // This structure must be less than 32 bytes to work easily
  float RPM;
  char ErrorMessage[11];
  float TestValue;
  float Average; // Can't set defaults sorry removed = 0
};

zhomeslice:
change to

typedef struct Data { // This structure must be less than 32 bytes to work easily

float RPM;
  char ErrorMessage[11];
  float TestValue;
  float Average; // Can't set defaults sorry removed = 0
};

typedef struct Data { // This structure must be less than 32 bytes to work easily
  float RPM;
  char ErrorMessage[11];
  float TestValue;
 };
 float Average;

Should not have had "float average" inside "typedef struct Data"

Still have corrupt setpoint from esp to uno, and rpm from uno to esp.

moose4621:

typedef struct Data { // This structure must be less than 32 bytes to work easily

float RPM;
 char ErrorMessage[11];
 float TestValue;
};
float Average;




Should not have had "float average" inside "typedef struct Data"

Still have corrupt setpoint from esp to uno, and rpm from uno to esp.

Yes, it seems that the data structure is different for the esp and the UNO we may need to do multiple transactions one for each piece of data.
Z

My latest sketch for wemos but still having trouble with SPI. Tacho and setpoint still not reporting correctly. I tried dividing the data structures but got too many errors. I will try again tonight.

master_with_wemos_R2.ino (11.2 KB)

master_with_uno_R1.ino (7.57 KB)

I think I may have bitten off a bit more than I can chew. :frowning:
I have put a considerable amount of time into this project and I am getting a bit frustrated with the lack of my own progress.

I have the index.html somewhere near what I want as far as layout is concerned, (sort of), but I have only got the on/off buttons working so far. I still need to work out how to get the live rpm displayed on the remote device.
I have added setpoint +/- buttons and kph +/- buttons as well as variable rate select and linkage switch select check boxes but as yet, these do nothing.

I have tried to get an image into the index.html via spiffs but that was unsuccessful too. :frowning:

The SPI is still a problem. I still cannot get any reliable data to and from the wemos and uno. :confused:

I know I am punching above my weight by taking on this project but I can't give up now.

I hope and would appreciate if someone might have the time to take a look at the code and give me some pointers..

master_with_wemos_R5.ino (16.9 KB)

master_with_uno_R1.ino (7.57 KB)

A change in drive motors has come about due mainly to the complications and reliability of the encoder for rpm feedback when using a DC motor.

So, a nema34 stepper motor has been chosen as the drive motor which hopefully will eliminate the need for any form of feedback.

With this new drive system, I thought I should be able to drive the stepper and maintain direct wifi control using just a Wemos D1 mini, (ESP8266).

I have a TB6600 stepper motor driver set at 1/16th stepping which gives me nice smooth operation at 0.1RPM and I can maintain 30RPM without wifi activated on the Wemos using this sketch.

//basic stepper tester
const byte stepPin = 4;
const byte ledPin = 5;
const byte potPin = A0;
int stepsPerRev = 200;
int microStep = 16;
float setPoint;
volatile long delaytime;
unsigned long oldDelayTime = micros();
volatile long oldmillis;

void setup() {
  pinMode(stepPin, OUTPUT);
  pinMode(ledPin, OUTPUT);
  pinMode(potPin, INPUT);
  Serial.begin(115200);

}

void loop() {
unsigned long currentmillis = millis();
  if ((currentmillis - oldmillis) >= 200) {
    oldmillis = currentmillis;
    setPoint = map(analogRead(potPin), 1, 1023, 1, 300);
  delaytime = 1000000 / (stepsPerRev * microStep * ((setPoint / 60) / 10));
  float rpm = setPoint / 10;
    Serial.print("Set point "); Serial.print(setPoint);
    Serial.print(" RPM "); Serial.print(float(setPoint/10));
    Serial.print(" Delay time "); Serial.println(delaytime);
  }
  
  unsigned long currentMillis = micros();
  if ((currentMillis - oldDelayTime) >= delaytime) {
    oldDelayTime = currentMillis;
    digitalWrite(stepPin, 1);
    delayMicroseconds(3);
    digitalWrite(stepPin, 0);
    digitalWrite(ledPin, !digitalRead(ledPin));

  }
  
}

Great so far!

As soon as I enable Wifi server, the step output on stepPin in interrupted every 200 milliseconds, (approx), presumably for the Wifi using the sketch attached. I thought the Serial output every 200m/s was the culprit but the problem persisted after disabling the Serial.

My question is: has anyone successfully got an ESP8266 to simultaneously run a stepper and Wifi direct server to control a stepper at high step rates, ie, 1.5Khz? I know there are a few examples out there but the ones I found are running at very low step rates.

Any suggestions welcome.

Wemos_D1_Mini_Seeder_Controller_R1.ino (8.48 KB)

Not sure if I should have started a new topic or not, but this is still the same project 10 months later so here it stays for now.

Where am I at? I now believe I can use a Wemos D1 mini as he only processor to control the stepper motor, receive data from a GPS, read a proximity sensor, and talk to a mobile device via wifi.

I eventually tried the accelStepper library which seems to control a stepper in a non-blocking way and now I can run a stepper at 3.2kHz without any interference from the wifi code. :slight_smile: All other methods I tried using timers and microsecond delays produced an audible and visible "hickup" of the stepper motor at around 1Hz which according to what I have read coincides with the wifi tasks the esp8266 carries out while in wifi mode.

So, I can forget about SPI communication with another device. :slight_smile:

I still have a long way to go and many problems to overcome but I am if nothing else, persistent.

I have started a new sketch by combining an accelStepper example, "Constant Speed" with a WebSocket script I use for a wifi gate controller.

The result is the wifi part of the script works great but when I activate the stepper via the remote device, it registers the change in motorStatus and prints to the serial monitor that this has in fact occurred but will not start the stepper. Refer line 164-176.

If I move the "stepper.runSpeed()" down to within the loop the stepper runs as it should.

This has got me baffled because I simple removed the H-bridge motor control from the original script and added the stepper control.

If anyone can shed some light I would be most appreciative.

#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <WebSocketsServer.h>
#include <Hash.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>

#define USE_SERIAL Serial

static const char ssid[] = "stepper";
static const char password[] = "";
MDNSResponder mdns;

static void writeStepperDriver(bool);

ESP8266WiFiMulti WiFiMulti;

ESP8266WebServer server(80);
WebSocketsServer webSocket = WebSocketsServer(81);

//for motor control
#include <AccelStepper.h>

AccelStepper stepper(1, 4, 5);
//AccelStepper (DRIVER);

static const char PROGMEM INDEX_HTML[] = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
<meta name = "viewport" content = "width = device-width, initial-scale = 1.0, maximum-scale = 1.0, user-scalable=0">
<title>Front Gate Controller</title>
<style>
"body { background-color: #74A5EE; font-family: Arial, Helvetica, Sans-Serif; Color: #FF6F00; }"
"h1 {text-align:center; Color: #FF6F00;}"
p {text-align:center;}
</style>
<script>
var websock;
function start() {
  websock = new WebSocket('ws://' + window.location.hostname + ':81/');
  websock.onrun = function(evt) { console.log('websock run'); };
  websock.onstop = function(evt) { console.log('websock stop'); };
  websock.onerror = function(evt) { console.log(evt); };
  websock.onmessage = function(evt) {
    console.log(evt);
    var e = document.getElementById('ledstatus');
    if (evt.data === 'motorStart') {
      e.style.color = 'red';
    }
    else if (evt.data === 'motorStop') {
      e.style.color = 'green';
    }
    else {
      console.log('unknown event');
    }
  };
}
function buttonclick(e) {
  websock.send(e.id);
}
</script>
</head>
<body onload="javascript:start();">
<table cellpadding="10%" cellspacing="10%" align="center" bgcolor="#74A5EE" width="100%" style="text-align: center; ">
<tr>
<td><span align="centre"><h1>XXXXXXXXXXX</h1></span></td>
</tr>
<tr>
<td><span align="centre"><h1>Seeder Controller</h1></span></td>
</tr>
<tr>
<td><div class="centre" id="ledstatus"><h1>Motor</h1></div></td>
</tr>
<tr>
<td><div class="centre"><button id="motorStart"  type="button" onclick="buttonclick(this);"><h1>-RUN-</h1></button></div></td>
</tr>
<tr>
<td><centre><button id="motorStop" type="button" onclick="buttonclick(this);"><h1>-STOP-</h1></button></centre></td>
</tr>
</table>
</body>
</html>
)rawliteral";

float setpointVariable = 20;//Fixed at 20rpm for now.
long steps = 200;//steps per revolution
long microStep = 16;//microstepping
bool motorStatus;// Current gate status

// Commands sent through Web Socket
const char RUN[] = "motorStart";
const char STOP[] = "motorStop";

void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length)
{
  USE_SERIAL.printf("webSocketEvent(%d, %d, ...)\r\n", num, type);
  switch (type) {
  case WStype_DISCONNECTED:
    USE_SERIAL.printf("[%u] Disconnected!\r\n", num);
    break;
  case WStype_CONNECTED:
  {
               IPAddress ip = webSocket.remoteIP(num);
               USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\r\n", num, ip[0], ip[1], ip[2], ip[3], payload);
               // Send the current LED status
               if (motorStatus) {
                 webSocket.sendTXT(num, RUN, strlen(RUN));
               }
               else {
                 webSocket.sendTXT(num, STOP, strlen(STOP));
               }
  }
    break;
  case WStype_TEXT:
    USE_SERIAL.printf("[%u] get Text: %s\r\n", num, payload);

    if (strcmp(RUN, (const char *)payload) == 0) {
      writeStepperDriver(true);
    }
    else if (strcmp(STOP, (const char *)payload) == 0) {
      writeStepperDriver(false);
    }
    else {
      USE_SERIAL.println("Unknown command");
    }
    // send data to all connected clients
    webSocket.broadcastTXT(payload, length);
    break;
  case WStype_BIN:
    USE_SERIAL.printf("[%u] get binary length: %u\r\n", num, length);
    hexdump(payload, length);

    // echo data back to browser
    webSocket.sendBIN(num, payload, length);
    break;
  default:
    USE_SERIAL.printf("Invalid WStype [%d]\r\n", type);
    break;
  }
}

void handleRoot()
{
  server.send_P(200, "text/html", INDEX_HTML);
}

void handleNotFound()
{
  String message = "File Not Found\n\n";
  message += "URI: ";
  message += server.uri();
  message += "\nMethod: ";
  message += (server.method() == HTTP_GET) ? "GET" : "POST";
  message += "\nArguments: ";
  message += server.args();
  message += "\n";
  for (uint8_t i = 0; i<server.args(); i++){
    message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
  }
  server.send(404, "text/plain", message);
}

static void writeStepperDriver(bool startMotor)
{
  motorStatus = startMotor;
  if (startMotor) {
    Serial.print(" motor should start ");Serial.println(motorStatus);
    stepper.runSpeed();
  }
    else {
    Serial.print(" motor should stop ");Serial.println(motorStatus);
      stepper.stop();
       }
  
}

void setup()
{
  pinMode(4, OUTPUT);//step pin
  pinMode(5, OUTPUT);//dir pin
  writeStepperDriver(false);
  
stepper.setMaxSpeed(3200);
  stepper.setSpeed(3200);//pulses per second

  USE_SERIAL.begin(115200);

  //Serial.setDebugOutput(true);

  USE_SERIAL.println();
  USE_SERIAL.println();
  USE_SERIAL.println();

  for (uint8_t t = 4; t > 0; t--) {
    USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\r\n", t);
    USE_SERIAL.flush();
    delay(1000);
  }

//  WiFiMulti.addAP(ssid, password);
//
//  while (WiFiMulti.run() != WL_CONNECTED) {
//    Serial.print(".");
//    delay(100);
//  }

  WiFi.softAP(ssid, password);
  IPAddress myIP = WiFi.softAPIP();
  USE_SERIAL.print("AP IP address: ");
  USE_SERIAL.println(myIP);

  USE_SERIAL.println("");
  USE_SERIAL.print("Connected to ");
  USE_SERIAL.println(ssid);
  USE_SERIAL.print("IP address: ");
  USE_SERIAL.println(WiFi.localIP());

  if (mdns.begin("espWebSock", WiFi.localIP())) {
    USE_SERIAL.println("MDNS responder started");
    mdns.addService("http", "tcp", 80);
    mdns.addService("ws", "tcp", 81);
  }
  else {
    USE_SERIAL.println("MDNS.begin failed");
  }
  USE_SERIAL.print("Connect to http://espWebSock.local or http://");
  USE_SERIAL.println(WiFi.localIP());

  server.on("/", handleRoot);
  server.onNotFound(handleNotFound);

  server.begin();

  webSocket.begin();
  webSocket.onEvent(webSocketEvent);
}

void loop()
{
  webSocket.loop();
  server.handleClient();
  //stepper.runSpeed();
}