Hi, I already asked this question on StackExchange, but I didn't got any answer, so I'm posting here too.
I'm trying to control 28BYj48 stepper motors over wifi, with creating an Access Point and connect to it, and send requests to the motor. My final goal is to control it from an app, but now I'm happy with a basic website. As I read and as I understood/tried the esp32 has 2 cores so the multithreading is possible, and the AsyncWebServer is doing it by default. I tried the AP demo code and the multiple stepper motor demo code and it worked, but after merging them it became very slow, that's why I choose multithreading with AsyncWebServer.
I tried based on these tutorials:
- ESP32 Web Server: Control Stepper Motor | Random Nerd Tutorials
- ESP32 Web Server: Control Stepper Motor (HTML Form) - Electrorules
-Setting up RTOS for dual-core & multi-threaded operation | Tutorialspoint. - ESP32 Dual Core with Arduino IDE | Random Nerd Tutorials
Now the code looks like this and the stepper not moving at all, how can I solve this problem?
/*
Rui Santos
Complete project details at https://RandomNerdTutorials.com/stepper-motor-esp32-web-server/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
*/
#include <Arduino.h>
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <Stepper.h>
#include <WiFiClient.h>
#include <WiFiAP.h>
// Stepper Motor Settings
const int stepsPerRevolution = 2048; // change this to fit the number of steps per revolution
#define IN1 19
#define IN2 18
#define IN3 5
#define IN4 17
Stepper myStepper(stepsPerRevolution, IN1, IN3, IN2, IN4);
// Create AsyncWebServer object on port 80
AsyncWebServer server(80);
// Search for parameters in HTTP POST request
const char* PARAM_INPUT_1 = "direction";
const char* PARAM_INPUT_2 = "steps";
// Variables to save values from HTML form
String direction;
String steps;
// Variable to detect whether a new request occurred
bool newRequest = false;
// HTML to build the web page
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
<title>Stepper Motor</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<h1>Stepper Motor Control</h1>
<form action="/" method="POST">
<input type="radio" name="direction" value="CW" checked>
<label for="CW">Clockwise</label>
<input type="radio" name="direction" value="CCW">
<label for="CW">Counterclockwise</label><br><br><br>
<label for="steps">Number of steps:</label>
<input type="number" name="steps">
<input type="submit" value="GO!">
</form>
</body>
</html>
)rawliteral";
IPAddress local_IP(192, 168, 1, 1); // fix IP
IPAddress gateway(192, 168, 1, 1); // same as local_IP
IPAddress subnet(255, 255, 255, 0); // same as local_IP
// Set these to your desired credentials.
const char *ssid = "yourAP";
const char *password = "123456789";
// Initialize WiFi
void initWiFi() {
/*WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
Serial.print("Connecting to WiFi ..");
while (WiFi.status() != WL_CONNECTED) {
Serial.print('.');
delay(1000);
}
Serial.println(WiFi.localIP());*/
if (!WiFi.softAP(ssid, password)) {
log_e("Soft AP creation failed.");
while(1);
}
WiFi.softAPConfig(local_IP, gateway, subnet);
IPAddress myIP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(myIP);
server.begin();
Serial.println("Server started");
}
void setup() {
Serial.begin(115200);
initWiFi();
myStepper.setSpeed(5);
// Web Server Root URL
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(200, "text/html", index_html);
});
// Handle request (form)
server.on("/", HTTP_POST, [](AsyncWebServerRequest *request) {
int params = request->params();
for(int i=0;i<params;i++){
AsyncWebParameter* p = request->getParam(i);
if(p->isPost()){
// HTTP POST input1 value (direction)
if (p->name() == PARAM_INPUT_1) {
direction = p->value().c_str();
Serial.print("Direction set to: ");
Serial.println(direction);
}
// HTTP POST input2 value (steps)
if (p->name() == PARAM_INPUT_2) {
steps = p->value().c_str();
Serial.print("Number of steps set to: ");
Serial.println(steps);
}
}
}
request->send(200, "text/html", index_html);
newRequest = true;
});
server.begin();
}
void loop() {
// Check if there was a new request and move the stepper accordingly
if (newRequest){
if (direction == "CW"){
// Spin the stepper clockwise direction
myStepper.step(steps.toInt());
}
else{
// Spin the stepper counterclockwise direction
myStepper.step(-steps.toInt());
}
newRequest = false;
}
}
The console output sometimes look like this:
ets Jun 8 2016 00:22:57
rst:0x1 (POWERON_RESET),boot:0x12 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0030,len:1344
load:0x40078000,len:13964
load:0x40080400,len:3600
entry 0x400805f0
AP IP address: 192.168.1.1
Server started
Direction set to: CW
Number of steps set to: 1000
sometimes look like this:
E (3760) wifi:addba response cb: ap bss deleted
E (4304) wifi:addba response cb: ap bss deleted
E (4532) wifi:addba response cb: ap bss deleted
E (5134) wifi:addba response cb: ap bss deleted
E (5489) wifi:addba response cb: ap bss deleted
E (6063) wifi:addba response cb: ap bss deleted
E (6800) wifi:addba response cb: ap bss deleted
E (7505) wifi:addba response cb: ap bss deleted
E (8225) wifi:addba response cb: ap bss deleted
E (8815) wifi:addba response cb: ap bss deleted
E (9494) wifi:addba response cb: ap bss deleted
E (10257) wifi:addba response cb: ap bss deleted
E (11569) wifi:addba response cb: ap bss deleted
Direction set to: CCW
Number of steps set to: 100
Direction set to: CCW
Number of steps set to: 1000
Direction set to: CW
Number of steps set to: 1000
For example at this last output the motor turned on CW direction but didn't move in CCW direction.
Tried to make it using multithreading:
/*
Rui Santos
Complete project details at https://RandomNerdTutorials.com/stepper-motor-esp32-web-server/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
*/
#include <Arduino.h>
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <Stepper.h>
#include <WiFiClient.h>
#include <WiFiAP.h>
#include <freertos/semphr.h>
SemaphoreHandle_t motorControlMutex;
// Stepper Motor Settings
const int stepsPerRevolution = 2048; // change this to fit the number of steps per revolution
#define IN1 19
#define IN2 18
#define IN3 5
#define IN4 17
Stepper myStepper(stepsPerRevolution, IN1, IN3, IN2, IN4);
// Create AsyncWebServer object on port 80
AsyncWebServer server(80);
// Search for parameters in HTTP POST request
const char* PARAM_INPUT_1 = "direction";
const char* PARAM_INPUT_2 = "steps";
// Variables to save values from HTML form
String direction;
String steps;
// Variable to detect whether a new request occurred
bool newRequest = false;
// HTML to build the web page
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
<title>Stepper Motor</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<h1>Stepper Motor Control</h1>
<form action="/" method="POST">
<input type="radio" name="direction" value="CW" checked>
<label for="CW">Clockwise</label>
<input type="radio" name="direction" value="CCW">
<label for="CW">Counterclockwise</label><br><br><br>
<label for="steps">Number of steps:</label>
<input type="number" name="steps">
<input type="submit" value="GO!">
</form>
</body>
</html>
)rawliteral";
IPAddress local_IP(192, 168, 1, 1); // fix IP
IPAddress gateway(192, 168, 1, 1); // same as local_IP
IPAddress subnet(255, 255, 255, 0); // same as local_IP
// Set these to your desired credentials.
const char *ssid = "yourAP";
const char *password = "123456789";
// Initialize WiFi
void initWiFi() {
/*WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
Serial.print("Connecting to WiFi ..");
while (WiFi.status() != WL_CONNECTED) {
Serial.print('.');
delay(1000);
}
Serial.println(WiFi.localIP());*/
if (!WiFi.softAP(ssid, password)) {
log_e("Soft AP creation failed.");
while(1);
}
WiFi.softAPConfig(local_IP, gateway, subnet);
IPAddress myIP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(myIP);
server.begin();
Serial.println("Server started");
}
void taskMotorControl(void *parameter) {
myStepper.setSpeed(5);
while (1) {
if (xSemaphoreTake(motorControlMutex, (TickType_t)10) == pdTRUE) {
if (newRequest) {
if (direction == "CW") {
myStepper.step(steps.toInt());
} else {
myStepper.step(-steps.toInt());
}
newRequest = false;
}
xSemaphoreGive(motorControlMutex);
}
}
}
void taskAP(void *parameter) {
initWiFi();
// Web Server Root URL
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(200, "text/html", index_html);
});
server.begin();
while (1) {
// Handle request (form)
server.on("/", HTTP_POST, [](AsyncWebServerRequest *request) {
int params = request->params();
for(int i=0;i<params;i++){
AsyncWebParameter* p = request->getParam(i);
if(p->isPost()){
// HTTP POST input1 value (direction)
if (p->name() == PARAM_INPUT_1) {
if (xSemaphoreTake(motorControlMutex, (TickType_t)10) == pdTRUE) {
direction = p->value().c_str();
xSemaphoreGive(motorControlMutex);
}
Serial.print("Direction set to: ");
Serial.println(direction);
}
// HTTP POST input2 value (steps)
if (p->name() == PARAM_INPUT_2) {
if (xSemaphoreTake(motorControlMutex, (TickType_t)10) == pdTRUE) {
steps = p->value().c_str();
xSemaphoreGive(motorControlMutex);
}
Serial.print("Number of steps set to: ");
Serial.println(steps);
}
}
}
request->send(200, "text/html", index_html);
if (xSemaphoreTake(motorControlMutex, (TickType_t)10) == pdTRUE) {
newRequest = true;
xSemaphoreGive(motorControlMutex);
}
});
}
}
void setup() {
Serial.begin(115200);
xTaskCreatePinnedToCore(taskMotorControl, "MotorControlTask", 10000, NULL, 1, NULL, 0);
xTaskCreatePinnedToCore(taskAP, "APTask", 10000, NULL, 1, NULL, 0);
motorControlMutex = xSemaphoreCreateMutex();
}
void loop() {}
With this the console output was:
Rebooting...
ets Jun 8 2016 00:22:57
rst:0xc (SW_CPU_RESET),boot:0x12 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0030,len:1344
load:0x40078000,len:13964
load:0x40080400,len:3600
entry 0x400805f0
assert failed: xQueueSemaphoreTake queue.c:1545 (( pxQueue ))
Backtrace: 0x4008373d:0x3ffccb10 0x4008ccf9:0x3ffccb30 0x400922c5:0x3ffccb50 0x4008dd09:0x3ffccc80 0x400d291f:0x3ffcccc0
How can I solve this problem?