Problem controlling stepper motors using joysticks and ESPNOW

curious007
Posts: 6
Joined: Thu May 11, 2023 7:25 pm

Problem controlling stepper motors using joysticks and ESPNOW

Postby curious007 » Thu May 11, 2023 7:35 pm

Hi community !

I'm trying to control:
3 stepper motors via two joysticks
a servo motor using one pushbutton
a dc motor using two push buttons (of joysticks)

I'm doing this using ESPNOW protocol on two ESP32s.
The problem is the stepper motors are not working smoothly or sometimes making noises or ignoring the command.

I've double checked the wiring and tried the code without ESPNOW and it works fines. But with ESPNOW it doesn't work. Below are the codes for both sender and receiver ESP32.

Sender:

Code: Select all

#include <esp_now.h>
#include <WiFi.h>


#define open 12

// Yrotation button pins
#define CW_BUTTON_PIN  32
#define ACW_BUTTON_PIN  33

//joystick pins
#define x1 14
#define y 27
#define x2 34
#define y2 35


// REPLACE WITH YOUR RECEIVER MAC Address
uint8_t broadcastAddress[] = {0xC0, 0x49, 0xEF, 0xD0, 0xDB, 0xB8};

// Structure example to send data
// Must match the receiver structure
typedef struct struct_message {
 bool o;
 int cw;
 int acw;
 int m_x1;
 int m_y;
 int m_x2;
 int m_y2;
} struct_message;

// Create a struct_message called myData
struct_message myData;

esp_now_peer_info_t peerInfo;

// callback when data is sent
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
  Serial.print("\r\nLast Packet Send Status:\t");
  Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
}
 
void setup() {
  // Init Serial Monitor
  Serial.begin(115200);
 pinMode(open,INPUT_PULLUP);
  pinMode(CW_BUTTON_PIN, INPUT_PULLUP);
  pinMode(ACW_BUTTON_PIN, INPUT_PULLUP);

  // Set device as a Wi-Fi Station
  WiFi.mode(WIFI_STA);

  // Init ESP-NOW
  if (esp_now_init() != ESP_OK) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }

  // Once ESPNow is successfully Init, we will register for Send CB to
  // get the status of Trasnmitted packet
  esp_now_register_send_cb(OnDataSent);
  
  // Register peer
  memcpy(peerInfo.peer_addr, broadcastAddress, 6);
  peerInfo.channel = 0;  
  peerInfo.encrypt = false;
  
  // Add peer        
  if (esp_now_add_peer(&peerInfo) != ESP_OK){
    Serial.println("Failed to add peer");
    return;
  }
}

  
void loop() {
  myData.o = digitalRead(open);
  myData.cw = digitalRead(CW_BUTTON_PIN);
  myData.acw = digitalRead(ACW_BUTTON_PIN);
  myData.m_x1 = analogRead(x1);
  myData.m_y = analogRead(y);
  myData.m_x2 = analogRead(x2);
  myData.m_y2 = analogRead(y2);

  esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));
    
    //}
   
  if (result == ESP_OK) {
    Serial.println("Sent with success");
  }
  else {
    Serial.println("Error sending the data");
  }
 // delay(1000);
}
Receiver:

Code: Select all

#include <ESP32MX1508.h>
#include <ESP32Servo.h> 
#include <esp_now.h>
#include <WiFi.h>


// Yrotation driver pins
#define ROTA 12
#define ROTB 14
#define CH1 0
#define CH2 1
#define RES 12

//link pins
const int dirx = 22;   //x
const int stepx = 23;
const int diry = 19;   //y
const int stepy = 18;
const int dirz = 25;   //z
const int stepz = 26;
const int diryr = 32;
const int stepyr = 33;


// Possible PWM GPIO pins on the ESP32: 0(used by on-board button),2,4,5(used by on-board LED),12-19,21-23,25-27,32-33 
int servoPin = 15;  

Servo myservo;  // servo object for gripper
MX1508 motorA(ROTA, ROTB, CH1, CH2, RES); //object for yrot motor
    

// Structure example to receive data
// Must match the sender structure
typedef struct struct_message {
  bool o;
 int cw;
 int acw;
 int m_x1;
 int m_y;
 int m_x2;
 int m_y2;
} struct_message;

// Create a struct_message called myData
struct_message myData;


/*************link moving functions***********************/
//x
void forw_x()
{
  digitalWrite(dirx, HIGH);
  digitalWrite(stepx, HIGH);
  delayMicroseconds(800);
  digitalWrite(stepx, LOW);
  delayMicroseconds(500);
}

void back_x()
{
  digitalWrite(dirx, LOW);
  digitalWrite(stepx, HIGH);
  delayMicroseconds(800);
  digitalWrite(stepx, LOW);
  delayMicroseconds(500);
}

//y
void forw_y()
{
  digitalWrite(diry, HIGH);
  digitalWrite(stepy, HIGH);
  delayMicroseconds(800);
  digitalWrite(stepy, LOW);
  delayMicroseconds(500);
}

void back_y()
{
  digitalWrite(diry, LOW);
  digitalWrite(stepy, HIGH);
  delayMicroseconds(800);
  digitalWrite(stepy, LOW);
  delayMicroseconds(500);
}


//z
void forw_z()
{
  digitalWrite(dirz, HIGH);
  digitalWrite(stepz, HIGH);
  delayMicroseconds(800);
  digitalWrite(stepz, LOW);
  delayMicroseconds(500);
}

void back_z()
{
  digitalWrite(dirz, LOW);
  digitalWrite(stepz, HIGH);
  delayMicroseconds(800);
  digitalWrite(stepz, LOW);
  delayMicroseconds(500);
}

  //yr
  void forw_yr()
  {
  digitalWrite(diryr, HIGH);
  digitalWrite(stepyr, HIGH);
  delayMicroseconds(800);
  digitalWrite(stepyr, LOW);
  delayMicroseconds(500);
  }

  void back_yr()
  {
  digitalWrite(diryr, LOW);
  digitalWrite(stepyr, HIGH);
  delayMicroseconds(800);
  digitalWrite(stepyr, LOW);
  delayMicroseconds(500);
  }


/****************function for links****************/
void links() {

  //move x
  if (myData.m_y == 0)
  {
    forw_x();
    Serial.println(myData.m_y);
  }

  if (myData.m_y >= 4000 && myData.m_x1 < 4000)
  {
    back_x();
    //Serial.println(myData.m_x1);
  }

  //move y
  if (myData.m_x1 == 0)
  {
    forw_y();
    Serial.print(myData.m_x1);
  }

  if (myData.m_y >= 4000 && myData.m_x1 > 4000)
  {
    back_y();
    Serial.print(myData.m_y);
  }

  //move z
  if (myData.m_y2 == 0)
  {
    forw_z();
    Serial.print(myData.m_y2);
  }

  if (myData.m_y2 >= 4000 && myData.m_x1 < 4000)
  {
    back_z();
    Serial.print(myData.m_x2);
  }

 
    //move yr
    if (myData.m_x2 == 0)
    {
      forw_yr();
      Serial.print(myData.m_x2);
    }

    if (myData.m_y2 >= 4000 && myData.m_x2 > 4000)
    {
      back_yr();
      Serial.print(myData.m_y2);
    }
}


/****************function for gripper****************/
void gripper() {
  if (myData.o == LOW){
   Serial.println("Openning");
   myservo.write(0);
 } else {
    // Stop motor if no button is pressed
    Serial.println("Default open");
   myservo.write(180); 
  
  } 
}

/****************function for gripper****************/
void rotation() {
  // Control motor based on button states
  if (myData.cw == LOW && myData.acw == HIGH) {
    //Serial.println("Forward");
    motorA.motorGo(4095);            // Pass the speed to the motor
  } else if (myData.acw == LOW && myData.cw == HIGH) {
    //Serial.println("Reverse");
    motorA.motorRev(4095);           // Pass the speed to the motor
  } else {
    // Stop motor if no button is pressed
    //Serial.println("Stop");
    motorA.motorBrake();            // Hard Stop    -no arguement
  }
}


// callback function that will be executed when data is received
void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
  memcpy(&myData, incomingData, sizeof(myData));
 links();
 gripper();
 rotation();

}

 
void setup() {
  // Initialize Serial Monitor
  Serial.begin(115200);
    //links pins
  pinMode(stepx, OUTPUT);
  pinMode(dirx, OUTPUT);
  pinMode(stepy, OUTPUT);
  pinMode(diry, OUTPUT);
  pinMode(stepz, OUTPUT);
  pinMode(dirz, OUTPUT);
  pinMode(stepyr, OUTPUT);
  pinMode(diryr, OUTPUT);
  
   // Allow allocation of all timers
  ESP32PWM::allocateTimer(0);
  ESP32PWM::allocateTimer(1);
  ESP32PWM::allocateTimer(2);
  ESP32PWM::allocateTimer(3);
  myservo.setPeriodHertz(50);// Standard 50hz servo
  myservo.attach(servoPin, 500, 2400);
  
  // Set device as a Wi-Fi Station
  WiFi.mode(WIFI_STA);

  // Init ESP-NOW
  if (esp_now_init() != ESP_OK) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }
  
  // Once ESPNow is successfully Init, we will register for recv CB to
  // get recv packer info
  esp_now_register_recv_cb(OnDataRecv);
}
 
void loop() {

}

MicroController
Posts: 1192
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Problem controlling stepper motors using joysticks and ESPNOW

Postby MicroController » Fri May 12, 2023 9:21 pm

You seem to be doing quite a lot in the receive callback, specifically I seem to gather you may have up to 3x 1.3ms of delays, plus any time the servo control and serial output takes. You may want to consider moving that stuff out of the esp-now callback into a dedicated task. (Additionally, you can reduce the 3x 1.3ms to 1x 1.3ms if you output all 3 stepper control signals at the same time.)

And you may want to throttle the packet rate of the sender to some reasonable amount.

curious007
Posts: 6
Joined: Thu May 11, 2023 7:25 pm

Re: Problem controlling stepper motors using joysticks and ESPNOW

Postby curious007 » Fri May 12, 2023 10:09 pm

I'll use the accelstepper library for the 1.3ms delay. But what about the dedicated task you're talking about? and how should I reduce the size of the data packet sent?

MicroController
Posts: 1192
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Problem controlling stepper motors using joysticks and ESPNOW

Postby MicroController » Sun May 14, 2023 10:38 am

To reduce the time spent in the RX callback, you should create a FreeRTOS task which does the actual handling of the data and output of control signals. In the RX callback you then only take the received data and send it to the task, e.g. via a queue, so that the actual callback can quickly return control to the WiFi stack.

As to the delays for the steppers, note that these are currently delays using "busy waiting", i.e. during the delay the CPU is 100% occupied doing nothing, which seems sub-optimal. You could avoid that by using e.g. a timer so that the CPU is available to process other things in the mean time.
You could also check if you can extend the wait time (pulse length of the control signal) to the point where you can use FreeRTOS's task delay (i.e. min. 10ms typically). And again, you can try and output all 3 stepper control signals at the same time so that you only have to wait for 1 signalling cycle instead of 3, like

Code: Select all

#define STEP_LEFT -1
#define STEP_NONE 0
#define STEP_RIGHT 1

void move_steppers(int dx, int dy, int dz)
{
  digitalWrite(dirx, dx > STEP_NONE  ? HIGH : LOW);
  digitalWrite(diry, dy > STEP_NONE  ? HIGH : LOW);
  digitalWrite(dirz, dz > STEP_NONE  ? HIGH : LOW);  
  
  digitalWrite(stepx, dx == STEP_NONE ? LOW : HIGH);
  digitalWrite(stepy, dy == STEP_NONE ? LOW : HIGH);
  digitalWrite(stepz, dz == STEP_NONE ? LOW : HIGH);
  
  delayMicroseconds(800);
  
  digitalWrite(stepx, LOW);
  digitalWrite(stepy, LOW);
  digitalWrite(stepz, LOW);
  
  delayMicroseconds(500); // <- Is this needed at all?
}
I was not referring to the size of the data packets but to the rate at which you send packets. Currently, you're sending data packets back to back as fast as possible, which the receiver will have to/try to handle at the same rate. I guess 20-50 Hz would be sufficient for your application, i.e. including a delay of 20-50ms after each packet sent.

curious007
Posts: 6
Joined: Thu May 11, 2023 7:25 pm

Re: Problem controlling stepper motors using joysticks and ESPNOW

Postby curious007 » Thu May 25, 2023 5:56 pm

Thanks, I implemented your idea and it worked.

MicroController
Posts: 1192
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Problem controlling stepper motors using joysticks and ESPNOW

Postby MicroController » Fri May 26, 2023 1:27 pm

Great to hear that :)

curious007
Posts: 6
Joined: Thu May 11, 2023 7:25 pm

Re: Problem controlling stepper motors using joysticks and ESPNOW

Postby curious007 » Fri May 26, 2023 1:33 pm

could you look into another problem ?

curious007
Posts: 6
Joined: Thu May 11, 2023 7:25 pm

Re: Problem controlling stepper motors using joysticks and ESPNOW

Postby curious007 » Fri May 26, 2023 1:34 pm

the servo is not working

Who is online

Users browsing this forum: No registered users and 78 guests