顯示具有 ESP32 標籤的文章。 顯示所有文章
顯示具有 ESP32 標籤的文章。 顯示所有文章

2024年6月30日 星期日

ESP32CAM + 麥克納姆倫 + Car

Purpose:
這個專題結合了ESP32CAM影像Web傳輸及Web http Control Mecanum wheel Car to move.



Fundamental&BOM:
Mecanum wheel and TT motor
Reference https://en.wikipedia.org/wiki/Mecanum_wheel
方向控制:


L298N

Mega2560


Circuit:









Reference
https://randomnerdtutorials.com/esp32-cam-car-robot-web-server/

All source code put on:

ESP32CAM Code:
#include <WiFi.h>
#include "soc/soc.h"             //用於電源不穩不重開機
#include "soc/rtc_cntl_reg.h"    //用於電源不穩不重開機
#include "esp_camera.h"    

// Select camera model
//#define CAMERA_MODEL_WROVER_KIT
//#define CAMERA_MODEL_M5STACK_PSRAM
#define CAMERA_MODEL_AI_THINKER

const char* ssid = "ras_2.4";   //Enter SSID WIFI Name
const char* password = "181415181415";   //Enter WIFI Password

#if defined(CAMERA_MODEL_WROVER_KIT)
#define PWDN_GPIO_NUM    -1
#define RESET_GPIO_NUM   -1
#define XCLK_GPIO_NUM    21
#define SIOD_GPIO_NUM    26
#define SIOC_GPIO_NUM    27

#define Y9_GPIO_NUM      35
#define Y8_GPIO_NUM      34
#define Y7_GPIO_NUM      39
#define Y6_GPIO_NUM      36
#define Y5_GPIO_NUM      19
#define Y4_GPIO_NUM      18
#define Y3_GPIO_NUM       5
#define Y2_GPIO_NUM       4
#define VSYNC_GPIO_NUM   25
#define HREF_GPIO_NUM    23
#define PCLK_GPIO_NUM    22


#elif defined(CAMERA_MODEL_AI_THINKER)
#define PWDN_GPIO_NUM     32
#define RESET_GPIO_NUM    -1
#define XCLK_GPIO_NUM      0
#define SIOD_GPIO_NUM     26
#define SIOC_GPIO_NUM     27

#define Y9_GPIO_NUM       35
#define Y8_GPIO_NUM       34
#define Y7_GPIO_NUM       39
#define Y6_GPIO_NUM       36
#define Y5_GPIO_NUM       21
#define Y4_GPIO_NUM       19
#define Y3_GPIO_NUM       18
#define Y2_GPIO_NUM        5
#define VSYNC_GPIO_NUM    25
#define HREF_GPIO_NUM     23
#define PCLK_GPIO_NUM     22

#else
#error "Camera model not selected"
#endif

// GPIO Setting
extern int gpLb =  2; // Left 1
extern int gpLf = 14; // Left 2
extern int gpRb = 15; // Right 1
extern int gpRf = 13; // Right 2
extern int gpLed =  4; // Light
extern String WiFiAddr ="";

void startCameraServer();

void setup() {
  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);  //關閉電源不穩就重開機的設定

  Serial.begin(115200);
  Serial.setDebugOutput(true);
  Serial.println();

  setupCam();

  pinMode(gpLb, OUTPUT); //Left Backward
  pinMode(gpLf, OUTPUT); //Left Forward
  pinMode(gpRb, OUTPUT); //Right Forward
  pinMode(gpRf, OUTPUT); //Right Backward
  pinMode(gpLed, OUTPUT); //Light

  //initialize
  digitalWrite(gpLb, LOW);
  digitalWrite(gpLf, LOW);
  digitalWrite(gpRb, LOW);
  digitalWrite(gpRf, LOW);
  digitalWrite(gpLed, LOW);

  WifiConnect();

  startCameraServer();

  Serial.print("Camera Ready! Use 'http://");
  Serial.print(WiFi.localIP());
  Serial.println("' to connect");

}

void loop()
{
 
  while(1)
  {
    if (WiFi.status() != WL_CONNECTED)
    {
      WifiConnect();
    }
   
  }

}
//-----------------------------------------
//開始WiFi連線
void WifiConnect()
{
  //開始WiFi連線
  //WiFi.begin("Wokwi-GUEST", "", 6);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }
 
  WiFiAddr = WiFi.localIP().toString();
  Serial.print("IP Address:");
  Serial.println(WiFi.localIP());
  Serial.println("WiFi連線成功");
}
//-----------------------------------------
//鏡頭設定
void setupCam() {  
  // #define CAMERA_MODEL_AI_THINKER
  //視訊組態設定  https://github.com/espressif/esp32-camera/blob/master/driver/include/esp_camera.h
  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer = LEDC_TIMER_0;
  config.pin_d0 = Y2_GPIO_NUM;
  config.pin_d1 = Y3_GPIO_NUM;
  config.pin_d2 = Y4_GPIO_NUM;
  config.pin_d3 = Y5_GPIO_NUM;
  config.pin_d4 = Y6_GPIO_NUM;
  config.pin_d5 = Y7_GPIO_NUM;
  config.pin_d6 = Y8_GPIO_NUM;
  config.pin_d7 = Y9_GPIO_NUM;
  config.pin_xclk = XCLK_GPIO_NUM;
  config.pin_pclk = PCLK_GPIO_NUM;
  config.pin_vsync = VSYNC_GPIO_NUM;
  config.pin_href = HREF_GPIO_NUM;
  config.pin_sscb_sda = SIOD_GPIO_NUM;
  config.pin_sscb_scl = SIOC_GPIO_NUM;
  config.pin_pwdn = PWDN_GPIO_NUM;
  config.pin_reset = RESET_GPIO_NUM;
  config.xclk_freq_hz = 20000000;
  config.pixel_format = PIXFORMAT_JPEG;
  //
  // WARNING!!! PSRAM IC required for UXGA resolution and high JPEG quality
  //            Ensure ESP32 Wrover Module or other board with PSRAM is selected
  //            Partial images will be transmitted if image exceeds buffer size
  //  
  // if PSRAM IC present, init with UXGA resolution and higher JPEG quality
  //                      for larger pre-allocated frame buffer.
  if(psramFound()){  //是否有PSRAM(Psuedo SRAM)記憶體IC
    config.frame_size = FRAMESIZE_UXGA;
    config.jpeg_quality = 10;
    config.fb_count = 2;
  } else {
    config.frame_size = FRAMESIZE_SVGA;
    config.jpeg_quality = 12;
    config.fb_count = 1;
  }

  //視訊初始化
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);
    ESP.restart();
  }

  //可自訂視訊框架預設大小(解析度大小)
  sensor_t * s = esp_camera_sensor_get();
  // initial sensors are flipped vertically and colors are a bit saturated
  if (s->id.PID == OV3660_PID) {
    s->set_vflip(s, 1); // flip it back
    s->set_brightness(s, 1); // up the brightness just a bit
    s->set_saturation(s, -2); // lower the saturation
  }
  // drop down frame size for higher initial frame rate
  s->set_framesize(s, FRAMESIZE_CIF);    //解析度 UXGA(1600x1200), SXGA(1280x1024), XGA(1024x768), SVGA(800x600), VGA(640x480), CIF(400x296), QVGA(320x240), HQVGA(240x176), QQVGA(160x120), QXGA(2048x1564 for OV3660)

  //s->set_vflip(s, 1);  //垂直翻轉
  //s->set_hmirror(s, 1);  //水平鏡像
  //s->set_pRotation(s, 1);  //0=不旋轉 1-轉90度 2- 轉180度 3-轉270
}

Mega2560 Code:
#include <Robojax_L298N_DC_motor.h>
#include <Servo.h>
//-------------L298(1)---------------------------------------------------
// motor 1 settings
#define CHA_1 0
#define ENA_1 2 // this pin must be PWM enabled pin if Arduino board is used
#define IN1_1 30
#define IN2_1 32
// motor 2 settings
#define IN3_1 34
#define IN4_1 36
#define ENB_1 7// this pin must be PWM enabled pin if Arduino board is used
#define CHB_1 1
//-------------L298(2)---------------------------------------------------
// motor 3 settings
#define CHA_2 0
#define ENA_2 2 // this pin must be PWM enabled pin if Arduino board is used
#define IN1_2 38
#define IN2_2 40
// motor 4 settings
#define IN3_2 42
#define IN4_2 44
#define ENB_2 7// this pin must be PWM enabled pin if Arduino board is used
#define CHB_2 1

const int CCW = 2; // do not change
const int CW  = 1; // do not change

#define motor1 1
#define motor2 2

// for two motors without debug information // Watch video instruciton for this line: https://youtu.be/2JTMqURJTwg
Robojax_L298N_DC_motor motors1(IN1_1, IN2_1, ENA_1, CHA_1,  IN3_1, IN4_1, ENB_1, CHB_1);
Robojax_L298N_DC_motor motors2(IN1_2, IN2_2, ENA_2, CHA_2,  IN3_2, IN4_2, ENB_2, CHB_2);

#define Pin22   22
#define Pin23   23
#define Pin24   24
#define Pin25   25
 
Servo servo1;  //底座  
Servo servo2;  //左臂
Servo servo3;  //右臂
Servo servo4;  //夾子

void setup()
{
  Serial.begin(9600);
  pinMode(Pin22,  INPUT);
  pinMode(Pin23,  INPUT);
  pinMode(Pin24,  INPUT);
  pinMode(Pin25,  INPUT);
  Serial.println(F("init"));
  pinMode(IN1_1, OUTPUT);
  pinMode(IN2_1, OUTPUT);
  pinMode(IN3_1, OUTPUT);
  pinMode(IN4_1, OUTPUT);
  pinMode(ENA_1, OUTPUT);
  pinMode(ENB_1, OUTPUT);
  pinMode(IN1_2, OUTPUT);
  pinMode(IN2_2, OUTPUT);
  pinMode(IN3_2, OUTPUT);
  pinMode(IN4_2, OUTPUT);
  pinMode(ENA_2, OUTPUT);
  pinMode(ENB_2, OUTPUT);
  servo1.attach(8);  // 設置舵機控制腳位
  servo2.attach(9);
  servo3.attach(10);
  servo4.attach(11);

  servo1.write(30);
  servo2.write(50);
  servo3.write(20);
  servo4.write(20);
}

void loop()
{
  char key = 0;
  int a=1,b=1,c=1,d=1;
  int fg = 0;
  boolean flag= false;
 
  while(1)
  {  
    if ( Serial.available())
    {
      key = Serial.read();
      flag=true;
    }

    a= digitalRead (Pin22);
    b= digitalRead (Pin23);
    c= digitalRead (Pin24);
    d= digitalRead (Pin25);

    Serial.print(digitalRead(22)) ;  //purple >  14
    Serial.print(digitalRead(23)) ;  //blue 2
    Serial.print(digitalRead(24)) ;  //yellow > 13
    Serial.println(digitalRead(25)); //15
   

    if (flag==false)
    {
       if (a==1 and b==0 and c==1 and d==0) {
          key ='f';
       }
       else if (a==0 and b==1 and c==0 and d==1) {
          key ='b';
       }
       else if (a==1 and b==0 and c==0 and d==1) {
          key ='x';
       }
       else if (a==0 and b==1 and c==1 and d==0) {
         key ='y';
       }
       else if (a==0 and b==0 and c==1 and d==1) {
         key ='a';
       }
       else if (a==1 and b==1 and c==0 and d==0) {
         key ='c';
       }
       else if (a==0 and b==0 and c==0 and d==0) {
         key ='s';
       }
       else if (a==1 and b==1 and c==1 and d==1) {
         key ='s';
       }
       else if (a==0 and b==0 and c==0 and d==1) { //---servo action 1 A1 Stretch
         key ='1';
       }
       else if (a==0 and b==0 and c==1 and d==0) { //---servo action 2 A2 Clip
         key ='2';
       }
       else if (a==0 and b==1 and c==0 and d==0) { //---servo action 3 A3 Retract
         key ='3';
       }
       else if (a==1 and b==0 and c==0 and d==0) { //---servo action 4 A4 Turn
         key ='4';
       }
       else if (a==1 and b==1 and c==1 and d==0) { //---servo action 5 A5 Release<
         key ='5';
       }
       else if (a==1 and b==1 and c==0 and d==1) { //---servo action 6 A6 Origin
         key ='6';
       }
       else if (a==1 and b==0 and c==1 and d==1) { //---servo action 7
         key ='7';
       }
       else if (a==0 and b==1 and c==1 and d==1) { //---servo action 8
         key ='8';
       }
    }

    switch ( key ) {
      case 'f': // 前進
        Serial.println(F("Forward"));
        Forward();
        break;
      case 'b': // 後退
        Serial.println(F("Reverse"));
        Reverse();
        break;
      case 'l': // 左移
        Serial.println(F("Left"));
        Left();
        break;
      case 'r': // 右移
        Serial.println(F("Right"));
        Right();
        break;  
      case 'a': // 左平移
        Serial.println(F("ShiftLeft"));
        S_Left();
        break;
      case 'c': // 右平移
        Serial.println(F("ShiftRight"));
        S_Right();
        break;  
      case 'x': // 逆左旋轉
        Serial.println(F("X"));
        Xtrun();
        break;  
      case 'y': // 順右旋轉
        Serial.println(F("Y"));
        Ytrun();
        break;  
      case '1': // 伸爪
        Serial.println(F("3"));
        servo3.write(40);
        delay(500);
        Serial.println(F("1"));
        servo1.write(50);
        delay(500);
        servo1.write(70);
        delay(500);
        servo1.write(90);
        delay(500);
        servo1.write(130);
        delay(500);
        servo1.write(160);
        delay(500);
        break;  
      case '2': // 夾
        Serial.println(F("2"));
        servo2.write(70);
        delay(500);
        servo2.write(100);
        delay(500);
        servo2.write(140);
        delay(500);
        break;  
      case '3':  //收爪
        Serial.println(F("1"));
        servo1.write(110);
        delay(500);
        servo1.write(90);
        delay(500);
        servo1.write(50);
        delay(500);
        servo1.write(30);
        delay(500);
        break;  
      case '4':  //後轉
        Serial.println(F("4"));
        servo4.write(60);
        delay(500);
        servo4.write(90);
        delay(500);
        servo4.write(120);
        delay(500);
        servo4.write(160);
        delay(500);
        servo4.write(180);
        delay(500);
        break;
      case '5':  //放夾
        Serial.println(F("2"));
        servo2.write(120);
        delay(500);
        servo2.write(90);
        delay(500);
        servo2.write(50);
        delay(500);
        /**
        Serial.println(F("4"));
        servo4.write(160);
        delay(500);;
        servo4.write(120);
        delay(500);
        servo4.write(80);
        delay(500);
        servo4.write(40);
        delay(500);
        servo4.write(20);
        delay(500);**/
        break;  
      case '6': //回原點
        Serial.println(F("4"));
        servo4.write(160);
        delay(500);;
        servo4.write(120);
        delay(500);
        servo4.write(80);
        delay(500);
        servo4.write(40);
        delay(500);
        servo4.write(20);
        delay(500);
        break;
      case 's': // 前進
        //Serial.println(F("Stop"));
        Stop();
        break;
      default:
        Stop();
        flag= false;
        break;

    }
   
  } //---while(1)
}

void Forward() //電機前進
{
  motors1.brake(1);  
  motors1.brake(2);  
  motors2.brake(1);  
  motors2.brake(2);  

  motors1.rotate(motor1, 70, CW);//run motor1 at 60% speed in CW direction (1)
  motors1.rotate(motor2, 70, CCW);//run motor2 at 60% speed in CCW direction (2)
  motors2.rotate(motor1, 70, CCW);//run motor1 at 60% speed in CCW direction (3)
  motors2.rotate(motor2, 70, CW);//run motor2 at 60% speed in CW direction  (4)
}

void Reverse(){
  motors1.brake(1);  
  motors1.brake(2);  
  motors2.brake(1);  
  motors2.brake(2);
  motors1.rotate(motor1, 70, CCW);
  motors1.rotate(motor2, 70, CW);
  motors2.rotate(motor1, 70, CW);
  motors2.rotate(motor2, 70, CCW);
}
void Left()
{
  motors1.brake(1);  
  motors1.brake(2);  
  motors2.brake(1);  
  motors2.brake(2);  
  motors1.rotate(motor1, 70, CW);//run motor1 at 60% speed in CW direction (1)
  //motors1.rotate(motor2, 70, CCW);//run motor2 at 60% speed in CCW direction (2)
  //motors2.rotate(motor1, 70, CCW);//run motor1 at 60% speed in CCW direction (3)
  motors2.rotate(motor2, 70, CW);//run motor2 at 60% speed in CW direction  (4)
}
void S_Left()  //左平移
{
  motors1.brake(1);  
  motors1.brake(2);  
  motors2.brake(1);  
  motors2.brake(2);  
  motors1.rotate(motor1, 70, CW);//run motor1 at 60% speed in CW direction (1)
  motors1.rotate(motor2, 70, CW);//run motor2 at 60% speed in CCW direction (2)
  motors2.rotate(motor1, 70, CW);//run motor1 at 60% speed in CCW direction (3)
  motors2.rotate(motor2, 70, CW);//run motor2 at 60% speed in CW direction  (4)
}
void S_Right()  //右平移
{
  motors1.brake(1);  
  motors1.brake(2);  
  motors2.brake(1);  
  motors2.brake(2);  
  motors1.rotate(motor1, 70, CCW);//run motor1 at 60% speed in CW direction (1)
  motors1.rotate(motor2, 70, CCW);//run motor2 at 60% speed in CCW direction (2)
  motors2.rotate(motor1, 70, CCW);//run motor1 at 60% speed in CCW direction (3)
  motors2.rotate(motor2, 70, CCW);//run motor2 at 60% speed in CW direction  (4)
}
void Right()  //右
{
  motors1.brake(1);  
  motors1.brake(2);  
  motors2.brake(1);  
  motors2.brake(2);  
  //motors1.rotate(motor1, 70, CW);//run motor1 at 60% speed in CW direction (1)
  motors1.rotate(motor2, 70, CCW);//run motor2 at 60% speed in CCW direction (2)
  motors2.rotate(motor1, 70, CCW);//run motor1 at 60% speed in CCW direction (3)
  //motors2.rotate(motor2, 70, CW);//run motor2 at 60% speed in CW direction  (4)
}

void Ytrun() // 逆右旋轉
{
  motors1.brake(1);  
  motors1.brake(2);  
  motors2.brake(1);  
  motors2.brake(2);  

  motors1.rotate(motor1, 70, CCW);//run motor1 at 60% speed in CW direction (1)
  motors1.rotate(motor2, 70, CW);//run motor2 at 60% speed in CCW direction (2)
  motors2.rotate(motor1, 70, CCW);//run motor1 at 60% speed in CCW direction (3)
  motors2.rotate(motor2, 70, CW);//run motor2 at 60% speed in CW direction  (4)
}

void Xtrun() // 逆左旋轉
{
  motors1.brake(1);  
  motors1.brake(2);  
  motors2.brake(1);  
  motors2.brake(2);  

  motors1.rotate(motor1, 70, CW);//run motor1 at 60% speed in CW direction (1)
  motors1.rotate(motor2, 70, CCW);//run motor2 at 60% speed in CCW direction (2)
  motors2.rotate(motor1, 70, CW);//run motor1 at 60% speed in CCW direction (3)
  motors2.rotate(motor2, 70, CCW);//run motor2 at 60% speed in CW direction  (4)
}

void Stop() //電機停止
{
  motors1.brake(1);
  motors1.brake(2);  
  motors2.brake(1);
  motors2.brake(2);  
}


YouTubeDemo:






2024年1月3日 星期三

ESP32 Grbl Controller

Purpose:
利用在網路上分享的Git連結 Grbl_ESP32 code upload 到自己的ESP32, 來進行將G 代碼從配備 USB 連接埠的電腦傳輸到數控機床(例如3D 列印機或雷射切割機)的步進馬達控制器.

Use the Git link shared on the Internet to upload the Grbl_ESP32 code to your own ESP32 to transfer the G-code from a computer equipped with a USB port to the stepper motor controller of a CNC machine tool (such as a 3D printer or laser cutting machine) .

Circuit:
Y axis set and Z axis set are the same method


#define MACHINE_NAME            "ESP32_V4"

#define X_STEP_PIN              GPIO_NUM_12
#define X_DIRECTION_PIN         GPIO_NUM_14
#define Y_STEP_PIN              GPIO_NUM_26
#define Y_DIRECTION_PIN         GPIO_NUM_15
#define Z_STEP_PIN              GPIO_NUM_27
#define Z_DIRECTION_PIN         GPIO_NUM_33

#define X_LIMIT_PIN             GPIO_NUM_17
#define Y_LIMIT_PIN             GPIO_NUM_4
#define Z_LIMIT_PIN             GPIO_NUM_16


// OK to comment out to use pin for other features
#define STEPPERS_DISABLE_PIN    GPIO_NUM_13

#define SPINDLE_TYPE            SpindleType::PWM
#define SPINDLE_OUTPUT_PIN      GPIO_NUM_2   // labeled SpinPWM
#define SPINDLE_ENABLE_PIN      GPIO_NUM_22  // labeled SpinEnbl

#define COOLANT_MIST_PIN        GPIO_NUM_21  // labeled Mist
#define COOLANT_FLOOD_PIN       GPIO_NUM_25  // labeled Flood
#define PROBE_PIN               GPIO_NUM_32  // labeled Probe


Fundamental:

在之前的文章中Arduino應用之3D印表機改裝成繪圖機是利用 Arduino Mega2560自己去寫控制步進馬達的部分, 最近看網路文章發現有神人把Grbl(Grbl是一款針對Arduino/AVR328晶片的嵌入式G代碼編譯和運動控制器。)放進ESP32裡面, 所以就來試試!! 網路Git連結Grbl_ESP32, 詳細說明如下連結Grbl_Esp32 Wiki介紹得很清楚!!

圖一:運作流程圖
Inkscape
Inkscape is professional quality vector graphics software which runs on Linux, Mac OS X and Windows desktop computers.

Universal Gcode Sender

A free and full featured gcode platform used for interfacing with advanced CNC controllers like GRBL , TinyG, g2core and Smoothieware. Universal Gcode Sender is a self-contained Java application which includes all external dependencies and can be used on most computers running Windows, MacOSX or Linux.
圖二:UGS 操作畫面

Reference the previous article link on blog:
ESP32 control stepper motor
ESP32 3-axis controller
三軸辨識系統-馬達篇


ESP32 Code:
https://github.com/bdring/Grbl_Esp32


YouTube Demo:



2023年12月27日 星期三

ESP32 3-axis controller

Purpose:
我利用手上沒在使用的三個滑軌和三個57步進馬達架構一個XYZ三軸方向的移動平台, 並使用ESP32作為3Axis controller控制移動方向. 有單軸指令, 例如 :X10  :Y10  :Z10跟三軸連動指令:P10,10,10

I used three unused slide rails and three 57 stepper motors to construct a mobile platform in the XYZ three-axis direction, and used ESP32 as the 3Axis controller to control the movement direction. There are single-axis instructions, for example: X10:Y10: Z10 and three-axis linkage command: P10,10,10

Architectures:  




Circuit:
Y axis set and Z axis set are the same method.

#define MACHINE_NAME            "ESP32_V4"

#define X_STEP_PIN              GPIO_NUM_12
#define X_DIRECTION_PIN         GPIO_NUM_14
#define Y_STEP_PIN              GPIO_NUM_26
#define Y_DIRECTION_PIN         GPIO_NUM_15
#define Z_STEP_PIN              GPIO_NUM_27
#define Z_DIRECTION_PIN         GPIO_NUM_33

#define X_LIMIT_PIN             GPIO_NUM_17
#define Y_LIMIT_PIN             GPIO_NUM_4
#define Z_LIMIT_PIN             GPIO_NUM_16


// OK to comment out to use pin for other features
#define STEPPERS_DISABLE_PIN    GPIO_NUM_13

#define SPINDLE_TYPE            SpindleType::PWM
#define SPINDLE_OUTPUT_PIN      GPIO_NUM_2   // labeled SpinPWM
#define SPINDLE_ENABLE_PIN      GPIO_NUM_22  // labeled SpinEnbl

#define COOLANT_MIST_PIN        GPIO_NUM_21  // labeled Mist
#define COOLANT_FLOOD_PIN       GPIO_NUM_25  // labeled Flood
#define PROBE_PIN               GPIO_NUM_32  // labeled Probe

Fundamental:

DM542 microstep driver
The DM542 is a new generation microstep stepper motor driver. Due to the adoption of the advanced bipolar constant-current chopper driver technology, it shows stable operation, provides excellent high torque. Moreover, it significantly reduces the noises and vibration of the operating motor. The DM542 has the feature of low-noise, low-vibration and low-heating. The DM542is DC18-50V power supply. It applies to 2-phase hybrid stepper motor under 4.2Acurrent The DM542 has 15 kinds of microsteps. The maximum step number is 25600 steps/rev (microstep is 1/128). The peak operating current ranges from 1.0A to 4.2A., and the output current has 8 stalls. The DM542 has automatic semi-flow, motor misconnected , over-voltage, under-voltage, andover-current protection functions.

Reference the previous article link on blog:
Arduino 控制三顆馬達

YouTube Demo:



ESP32 Code:
#include <AccelStepper.h>
#include <MultiStepper.h>
//--------- Flag structure --------------------------------------
typedef struct _vFlag
{
  uint8_t BTFlag = 0;
  uint8_t initial_Flag = 0;
} vFlag;
vFlag *flag_Ptr;
vFlag flag;

//------LED------------------
#define LED_BUILTIN 2
char ctemp[20];
//--------- uart structure --------------------------------------
//----------uart--------------
#define LINE_BUFFER_LENGTH 64
typedef struct _vUart
{
  char c;
  int lineIndex = 0;
  int line1Index = 0;
  int BTlineIndex = 0;
  bool lineIsComment;
  bool lineSemiColon;
  char line[128];
  char BTline[20];
  String inputString;
  String BTinputString;
  int V[16];
  char ctemp[30];
  char I2C_Data[80];
  float Voltage[16];
  int Buffer[128];
} vUart;
vUart *Uart_Ptr;
vUart Uart;
//--------- motor structure --------------------------------------
typedef struct _axis_state_t
{
  uint8_t pulPin[6]; //--X,Y,Z,A,B,C
  uint8_t dirPin[6];
  uint8_t enaPin[6];
  uint8_t HomePin[6];
  long RotatorAngle[6];
  boolean AXISFlag[6];
  uint8_t HomeFlag[6];
  uint8_t RotatorFlag[6];
  float FeedRate[6];
  float MaxSpeed[6];
  float Acceleration[6];
  int AXISNumber[2];
  int DC_Speed[6];
} axis_state_t;
axis_state_t *gc_Ptr;
axis_state_t gc;
//----------multi motor ---------------------------------------
MultiStepper steppers;
long positions[3];  
#define Stepper_ENABLE 13  
#define X_LIMIT_PIN    17
#define Y_LIMIT_PIN    4
#define Z_LIMIT_PIN    16
AccelStepper stepperX (1,12,14); // name of stepper motor (1 = driver, pin 2 = pulse, pin 3 = direction)  
AccelStepper stepperY (1,26,15); // name of stepper motor (1 = driver, pin 4 = step, pin 5 = direction)
AccelStepper stepperZ (1,27,33); // name of stepper motor (1 = driver, pin 6 = step, pin 7 = direction)
//------------------------------------------------------------------------------
//-------------------------------------
TaskHandle_t huart;
TaskHandle_t hfunction;
TaskHandle_t hmultimotor;  //TaskHandler

void MultiMotorTask(void *pvParameters);
void vUARTTask(void *pvParameters);
void vFunctionTask(void *pvParameters);
//------------------------------------------------------------------------------
void initial()
{
  Serial.println(F("Create Task"));
  //----------------------------------------------------------------------
  xTaskCreatePinnedToCore(
    vUARTTask, "UARTTask" // A name just for humans
    ,
    1024 // This stack size can be checked & adjusted by reading the Stack Highwater
    ,
    NULL, 3 // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
    ,
    &huart //handle
    ,
    0);

  //----------------------------------------------------------------------
  initMultiMotorTask();
  //----------------------------------------------------------------------
}
void initMultiMotorTask(void)
{
  // Create the task
  xTaskCreatePinnedToCore(MultiMotorTask, "MultiMotorTask", 1024, NULL, 3, &hmultimotor, 1);
  // Check the results
  if (hmultimotor == NULL)
  {
    Serial.println("Create MultiMotor task failed");
  }
  else
  {
    Serial.println("MultiMotor task up and running");
    vTaskSuspend(hmultimotor);
  }
}
//------------------------------------------------------------------------------
void MultiMotorTask(void *pvParameters)
{
  (void)pvParameters;

  while(1)
  {
    digitalWrite(Stepper_ENABLE, LOW);
    if (gc.AXISFlag[0] || gc.AXISFlag[1] || gc.AXISFlag[2])
    {
      if (gc.RotatorFlag[0] == 1)
      {
        //Serial.println( "1" );
        gc.RotatorAngle[0] = gc.RotatorAngle[0] * 500;
        //gc.RotatorAngle[0] = gc.RotatorAngle[0] * 100;   //F4設定
        positions[0] = gc.RotatorAngle[0];
      }
      else
      {
        positions[0] = stepperX.currentPosition();
      }

      if (gc.RotatorFlag[1] == 1)
      {
        //Serial.println( "2" );
        gc.RotatorAngle[1]=gc.RotatorAngle[1]*500;//F4設定
        positions[1] = gc.RotatorAngle[1];
      }
      else
      {
        positions[1] = stepperY.currentPosition();
      }

      if (gc.RotatorFlag[2] == 1)
      {
        //Serial.println( "3" );
        gc.RotatorAngle[2] = gc.RotatorAngle[2] * 100;
        //gc.RotatorAngle[2] = gc.RotatorAngle[2] * 250;//F4設定
        positions[2] = gc.RotatorAngle[2];
      }
      else
      {
        positions[2] = stepperZ.currentPosition();
      }
      //----multi motor
      //positions[0]=gc.RotatorAngle[0];
      //positions[1]=gc.RotatorAngle[1];
      //vTaskSuspend(hled);
      steppers.moveTo(positions);
      steppers.runSpeedToPosition();
      //steppers.run();

      if (abs(stepperX.currentPosition()) == abs(gc.RotatorAngle[0]))
      {
        gc.AXISFlag[0] = false;
        if (gc.RotatorFlag[0] == 1)
        {
          gc.AXISNumber[0]--;
        }
        Serial.println( "X_OK" );
        //vTaskSuspend( hmultimotor );
      }
      if (abs(stepperY.currentPosition()) == abs(gc.RotatorAngle[1]))
      {
        gc.AXISFlag[1] = false;
        if (gc.RotatorFlag[1] == 1)
        {
          gc.AXISNumber[0]--;
        }
        Serial.println( "Y_OK" );
        //vTaskSuspend( hmultimotor );
      }
      if (abs(stepperZ.currentPosition()) == abs(gc.RotatorAngle[2]))
      {
        gc.AXISFlag[2] = false;
        if (gc.RotatorFlag[2] == 1)
        {
          gc.AXISNumber[0]--;
        }
        Serial.println( "Z_OK" );
        //vTaskSuspend( hmultimotor );
      }
      if (gc.AXISNumber[0] == 0)
      {
        //vTaskResume(hled);
        for (int i = 0; i < 6; i++)
        {
          gc.RotatorFlag[i] = 0;
        }
        //vTaskSuspend(hmultimotor);
      }
    }
   
    vTaskDelay(1);
    //vTaskDelay(configTICK_RATE_HZ);
  }
}
void initMultiMotorSetup(void)
{
  gc.pulPin[0] = 26;
  gc.dirPin[0] = 25;
  //gc.HomePin[0] = 8;
  //gc.enaPin[0]=8;
  gc.pulPin[1] = 32;
  gc.dirPin[1] = 33;
  //gc.enaPin[1]=8;
  //gc.HomePin[1] = 9;
  gc.pulPin[2] = 34;
  gc.dirPin[2] = 35;
  //gc.enaPin[2]=8;
  //gc.HomePin[2] = 10;

  for (int i = 0; i < 6; i++)
  {
    //pinMode(gc.pulPin[i], OUTPUT);
    //pinMode(gc.dirPin[i], OUTPUT);
    //pinMode(gc.enaPin[i], OUTPUT);
    //digitalWrite(gc.enaPin[i], HIGH);

    gc.RotatorAngle[i] = 0;
    gc.AXISFlag[i] = false;
    gc.HomeFlag[i] = 0;
    gc.RotatorFlag[i] = 0;

    gc.FeedRate[i] = 1000;
    gc.MaxSpeed[i] = 4000;
    gc.Acceleration[i] = 800;
  }

  gc.AXISNumber[0] = 0;
  gc.AXISNumber[1] = 0;


  pinMode(Stepper_ENABLE, OUTPUT);
  digitalWrite(Stepper_ENABLE, HIGH);
  //pinMode(26, OUTPUT);
  stepperX.setMaxSpeed(4000);
  stepperX.setAcceleration(1000);
  stepperX.setSpeed(4000); //:f4000,4000,4000


  stepperY.setMaxSpeed(4000); //:ms8000,8000,8000
  stepperY.setAcceleration(1000);
  stepperY.setSpeed(1000);

  stepperZ.setMaxSpeed(4000);
  stepperZ.setAcceleration(800);
  stepperZ.setSpeed(2000);

  // Then give them to MultiStepper to manage
  steppers.addStepper(stepperX);
  steppers.addStepper(stepperY);
  steppers.addStepper(stepperZ);
}
//-------------------------------------------------
void setup()
{
  Serial.begin(9600);
  Serial.println(F("init"));
  initial();
  initMultiMotorSetup();
}
//-----------------------------------------
void loop()
{
  Serial.print(F("Main at core:"));
  Serial.println(xPortGetCoreID());
  while(1)
  {
    vTaskDelay(1);
  }
}
//----------------------------------------
void processCommand(char *data)
{
  int len, xlen, ylen, zlen, alen;
  int tempDIO;
  String stemp;

  len = Uart.inputString.length();
  //memset(ctemp, 0, sizeof(ctemp));
  //---------------------------------------
  if (strstr(data, "VER") != NULL)
  {
    Serial.println(F("ESP32_20231226"));
  }
  //-----------Motor----------------------
  if (strstr(data, ":P") != NULL)
  {
    //:p200,100,300
    for (int i = 2; i < len; i++)
    {
      ctemp[i - 2] = data[i];
      if (data[i] == ',')
      {
        xlen = i;
        ctemp[xlen] = '\0';
        gc.RotatorAngle[0] = atoi(ctemp);
        //Serial.println(xlen);
        //Serial.println(gc.RotatorAngle[0]);
        break;
      }
    }

    int j = (xlen + 1);
    for (int i = j; i < len; i++)
    {
      ctemp[i - j] = data[i];
      if (data[i] == ',')
      {
        ylen = i;
        ctemp[ylen] = '\0';
        gc.RotatorAngle[1] = atoi(ctemp);
        //Serial.print("debug- ");
        //Serial.println(gc.RotatorAngle[1]);
        break;
      }
    }
    int k = (ylen + 1);
    for (int i = k; i < len; i++)
    {
      zlen = i;
      ctemp[i - k] = data[i];
    }
    ctemp[zlen] = '\0';
    gc.RotatorAngle[2] = atoi(ctemp);
    //Serial.println(zlen);
    //Serial.println(gc.RotatorAngle[2]);

    gc.AXISFlag[0] = true;
    gc.AXISFlag[1] = true;
    gc.AXISFlag[2] = true;
    gc.RotatorFlag[0] = 1;
    gc.RotatorFlag[1] = 1;
    gc.RotatorFlag[2] = 1;
    gc.AXISNumber[0] = 3;
    vTaskResume(hmultimotor);
  }

  if (strstr(data, ":F"))
  {
    //:f4000,4000,2000
    for (int i = 2; i < len; i++)
    {
      Uart.ctemp[i - 2] = data[i];
      if (data[i] == ',')
      {
        xlen = i;
        Uart.ctemp[xlen] = '\0';
        gc.FeedRate[0] = atoi(Uart.ctemp);
        //Serial.println(xlen);
        //Serial.println(gc.RotatorAngle[0]);
        break;
      }
    }
    int j = (xlen + 1);
    for (int i = j; i < len; i++)
    {
      Uart.ctemp[i - j] = data[i];
      if (data[i] == ',')
      {
        ylen = i;
        Uart.ctemp[ylen] = '\0';
        gc.FeedRate[1] = atoi(Uart.ctemp);
        //Serial.println(ylen);
        //Serial.println(gc.RotatorAngle[1]);
        break;
      }
    }
    int k = (ylen + 1);
    for (int i = k; i < len; i++)
    {
      zlen = i;
      Uart.ctemp[i - k] = data[i];
    }
    Uart.ctemp[zlen] = '\0';
    gc.FeedRate[2] = atoi(Uart.ctemp);

    stepperX.setSpeed(gc.FeedRate[0]);
    stepperY.setSpeed(gc.FeedRate[1]);
    stepperZ.setSpeed(gc.FeedRate[2]);
  }

  if (strstr(data, ":MS"))
  {
    //float MaxSpeed[6];
    //:p200,100,300
    for (int i = 2; i < len; i++)
    {
      Uart.ctemp[i - 2] = data[i];
      if (data[i] == ',')
      {
        xlen = i;
        Uart.ctemp[xlen] = '\0';
        gc.MaxSpeed[0] = atoi(Uart.ctemp);
        //Serial.println(xlen);
        //Serial.println(gc.RotatorAngle[0]);
        break;
      }
    }
    int j = (xlen + 1);
    for (int i = j; i < len; i++)
    {
      Uart.ctemp[i - j] = data[i];
      if (data[i] == ',')
      {
        ylen = i;
        Uart.ctemp[ylen] = '\0';
        gc.MaxSpeed[1] = atoi(Uart.ctemp);
        //Serial.println(ylen);
        //Serial.println(gc.RotatorAngle[1]);
        break;
      }
    }
    int k = (ylen + 1);
    for (int i = k; i < len; i++)
    {
      zlen = i;
      Uart.ctemp[i - k] = data[i];
    }
    Uart.ctemp[zlen] = '\0';
    gc.MaxSpeed[2] = atoi(Uart.ctemp);

    stepperX.setMaxSpeed(gc.MaxSpeed[0]);
    stepperY.setMaxSpeed(gc.MaxSpeed[1]);
    stepperZ.setMaxSpeed(gc.MaxSpeed[2]);
  }
  if (strstr(data, ":ACC"))
  {
    //float Acceleration[6];
    //:ACC1000,1000,1000
    for (int i = 2; i < len; i++)
    {
      Uart.ctemp[i - 2] = data[i];
      if (data[i] == ',')
      {
        xlen = i;
        Uart.ctemp[xlen] = '\0';
        gc.Acceleration[0] = atoi(Uart.ctemp);
        //Serial.println(xlen);
        //Serial.println(gc.RotatorAngle[0]);
        break;
      }
    }
    int j = (xlen + 1);
    for (int i = j; i < len; i++)
    {
      Uart.ctemp[i - j] = data[i];
      if (data[i] == ',')
      {
        ylen = i;
        Uart.ctemp[ylen] = '\0';
        gc.Acceleration[1] = atoi(Uart.ctemp);
        //Serial.println(ylen);
        //Serial.println(gc.RotatorAngle[1]);
        break;
      }
    }
    int k = (ylen + 1);
    for (int i = k; i < len; i++)
    {
      zlen = i;
      Uart.ctemp[i - k] = data[i];
    }
    Uart.ctemp[zlen] = '\0';
    gc.Acceleration[2] = atoi(Uart.ctemp);

    stepperX.setAcceleration(gc.Acceleration[0]);
    stepperY.setAcceleration(gc.Acceleration[1]);
    stepperZ.setAcceleration(gc.Acceleration[2]);
  }

  if (strstr(data, ":X") != NULL)
  {
    gc.HomeFlag[0] = 3;
    gc.AXISFlag[0] = true;
    for (int i = 2; i < len; i++)
    {
      ctemp[i - 2] = data[i];
    }
    ctemp[len - 2] = '\0';
    gc.RotatorAngle[0] = atoi(ctemp);
    gc.RotatorFlag[0] = 1;
    //Serial.println(gc.RotatorAngle[0]);
    gc.AXISNumber[0] = 1;
    vTaskResume(hmultimotor);
  }
  if (strstr(data, ":Y") != NULL)
  {
    gc.AXISFlag[1] = true;
    for (int i = 2; i < len; i++)
    {
      ctemp[i - 2] = data[i];
    }
    ctemp[len - 2] = '\0';
    gc.RotatorAngle[1] = atoi(ctemp);
    //Serial.println(gc.RotatorAngle[1]);
    gc.RotatorFlag[1] = 1;

    gc.AXISNumber[0] = 1;
    vTaskResume(hmultimotor);
  }

  if (strstr(data, ":Z") != NULL)
  {
    gc.AXISFlag[2] = true;
    for (int i = 2; i < len; i++)
    {
      ctemp[i - 2] = data[i];
    }
    ctemp[len - 2] = '\0';
    gc.RotatorAngle[2] = atoi(ctemp);
    //Serial.println(gc.RotatorAngle[2]);
    gc.RotatorFlag[2] = 1;

    gc.AXISNumber[0] = 1;
    vTaskResume(hmultimotor);
  }
 
}
//-----------------------------------------

//-------------------------------------------
void vUARTTask(void *pvParameters)
{
  (void)pvParameters;

  Serial.print(F("UARTTask at core:"));
  Serial.println(xPortGetCoreID());
  for (;;)
  {
    while (Serial.available() > 0)
    {
      Uart.c = Serial.read();
 
      if ((Uart.c == '\n') || (Uart.c == '\r'))
      { // End of line reached
        if (Uart.lineIndex > 0)
        { // Line is complete. Then execute!
          Uart.line[Uart.lineIndex] = '\0'; // Terminate string
          //Serial.println( F("Debug") );
          //Serial.println( Uart.inputString );
          processCommand(Uart.line);
          Uart.lineIndex = 0;
          Uart.inputString = "";
        }
        else
        {
          // Empty or comment line. Skip block.
        }
        Uart.lineIsComment = false;
        Uart.lineSemiColon = false;
        Serial.println(F("ok>"));
      }
      else
      {
        //Serial.println( c );
        if ((Uart.lineIsComment) || (Uart.lineSemiColon))
        {
          if (Uart.c == ')')
            Uart.lineIsComment = false; // End of comment. Resume line.
        }
        else
        {
          if (Uart.c == '/')
          { // Block delete not supported. Ignore character.
          }
          else if (Uart.c == '~')
          { // Enable comments flag and ignore all characters until ')' or EOL.
            Uart.lineIsComment = true;
          }
          else if (Uart.c == ';')
          {
            Uart.lineSemiColon = true;
          }
          else if (Uart.lineIndex >= LINE_BUFFER_LENGTH - 1)
          {
            Serial.println("ERROR - lineBuffer overflow");
            Uart.lineIsComment = false;
            Uart.lineSemiColon = false;
          }
          else if (Uart.c >= 'a' && Uart.c <= 'z')
          { // Upcase lowercase
            Uart.line[Uart.lineIndex] = Uart.c - 'a' + 'A';
            Uart.lineIndex = Uart.lineIndex + 1;
            Uart.inputString += (char)(Uart.c - 'a' + 'A');
          }
          else
          {
            Uart.line[Uart.lineIndex] = Uart.c;
            Uart.lineIndex = Uart.lineIndex + 1;
            Uart.inputString += Uart.c;
          }
        }
      }
    } //while (Serial.available() > 0)
   
    vTaskDelay(1);
  }
}