Stepper motor for loadcell

I am writing a program for spinal traction machine on arduino using loadcell and stepper motor. The program works as follows, when powered, the display screen to select one of two programs program1 and program2 by using the button pin number 8 to switch between the two programs and the button pin number 9 to confirm the program selection. After selecting program1 or program2:

  • program1: the program will ask the user to use the button pin number 5 to switch between the loadcell force threshold setting modes and the force holding time threshold when the loadcell pulling motor reaches the selected force threshold and pin number 4 to adjust between the force threshold and the holding time threshold. After setting, press the start button, the motor will start rotating and pull the loadcell to reach the threshold force, the motor will stop and hold the threshold force during the holding time, the pulling force on the loadcell, the data time is updated in real time on the LCD, when the holding time is over, the motor will rotate back to the original position step equal to 0 at a slow speed.
  • program2: the program will require the user to use the foot button number 5 to switch between the upper force threshold, lower force threshold, holding time and treatment time settings and foot number 4 to adjust between the upper/lower force thresholds and holding time thresholds, treatment time. After setting, press the start button, the motor starts running, pulls the loadcell, the weight on the loadcell is updated in real time and displayed on the LCD, when the pulling force on the loadcell reaches the lower threshold, the motor stops and holds according to the holding time, when the holding time is over, the motor will continue to rotate to pull the loadcell to the upper threshold and continue to hold according to the holding time, when the holding time is over, the motor will rotate back to keep the force on the loadcell at the lower threshold, when the holding time is over, it rotates back to pull the loadcell to the upper threshold, repeating like that until the end of the treatment time, the motor will rotate back to the original position, step equals 0 at a slow speed. After the motor returns to step 0, the program returns to select the spinal traction program and sets it up as before to perform the next treatment cycle. The program uses a push button at pin 7 for emergency stop at any time to stop the program and when pressed again the motor returns to step 0.

Wokwi simulation

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include "HX711.h"

// -------------------- Khai báo chung --------------------
#define BUTTON_NEXT 8       // Nút chuyển chế độ
#define BUTTON_SELECT 9     // Nút xác nhận chế độ
#define BUTTON_ADJUST 4     // Nút điều chỉnh
#define BUTTON_MODE 5       // Nút chuyển giữa các chế độ cài đặt
#define BUTTON_STARTSTOP 6  // Nút bắt đầu/dừng chương trình
#define BUTTON_EMERGENCY 7  // Nút dừng khẩn cấp

#define STEP_PIN 10         // Chân điều khiển xung động cơ
#define DIR_PIN 11          // Chân điều khiển chiều động cơ
#define EN_PIN 13           // Chân kích hoạt động cơ

#define LOADCELL_DOUT_PIN 3
#define LOADCELL_SCK_PIN 2

LiquidCrystal_I2C lcd(0x27, 16, 2); // Khởi tạo đối tượng LCD
HX711 scale;

int selectedProgram = 0;    // Chương trình được chọn (0: Program 1, 1: Program 2)
bool programSelected = false;
bool isRunning = false;
bool holding = false;
bool movingToUpper = true;
bool returning = false;
bool emergencyStop = false;

unsigned long holdStartTime = 0;  // Thời điểm bắt đầu giữ
unsigned long treatmentStartTime = 0;  // Thời gian bắt đầu điều trị
long stepsTaken = 0;           // Số bước động cơ đã di chuyển

float currentWeight = 0.0;     // Trọng lượng hiện tại từ Load Cell
float calibration_factor = 500.0;

// -------------------- Chương trình 1 --------------------
float weightThresholds[] = {1, 1.5, 2};
int currentThresholdIndex = 0;

unsigned int holdTimes[] = {10, 15, 20};
int currentHoldTimeIndex = 0;

enum Mode { ADJUST_HOLD_TIME, ADJUST_THRESHOLD };
Mode currentModeProgram1 = ADJUST_HOLD_TIME;

// -------------------- Chương trình 2 --------------------
float upperLimits[] = {2.5, 3.0, 3.5};
int currentUpperLimitIndex = 0;

float lowerLimits[] = {1.0, 1.5, 2.0};
int currentLowerLimitIndex = 0;

unsigned int treatmentTimes[] = {60, 90, 120};
int currentTreatmentTimeIndex = 0;

unsigned int holdTimeProgram2 = 10; // Thời gian giữ cho Program 2
enum Mode2 { ADJUST_UPPER_LIMIT, ADJUST_LOWER_LIMIT, ADJUST_TREATMENT_TIME, ADJUST_HOLD_TIME_PROGRAM2 };
Mode2 currentModeProgram2 = ADJUST_UPPER_LIMIT;

// -------------------- Prototype các hàm --------------------
void resetToProgramSelection();
void updateProgramSelection();
void setupProgram1();
void loopProgram1();
void setupProgram2();
void loopProgram2();
void handleEmergencyStop();
void moveMotor(bool forward);
void stopMotor();
void updateWeight();
void displayWeight();
void runHoldCycle();
void runHoldCycleProgram2();
void runTreatmentCycleProgram2();
void returnToStart(bool slow);

// -------------------- Setup --------------------
void setup() {
  lcd.init();
  lcd.backlight();

  pinMode(BUTTON_NEXT, INPUT_PULLUP);
  pinMode(BUTTON_SELECT, INPUT_PULLUP);
  pinMode(BUTTON_ADJUST, INPUT_PULLUP);
  pinMode(BUTTON_MODE, INPUT_PULLUP);
  pinMode(BUTTON_STARTSTOP, INPUT_PULLUP);
  pinMode(BUTTON_EMERGENCY, INPUT_PULLUP);

  pinMode(STEP_PIN, OUTPUT);
  pinMode(DIR_PIN, OUTPUT);
  pinMode(EN_PIN, OUTPUT);
  digitalWrite(EN_PIN, HIGH); // Vô hiệu hóa động cơ ban đầu

  scale.begin(LOADCELL_DOUT_PIN, LOADCELL_SCK_PIN);
  scale.set_scale(calibration_factor);
  scale.tare();

  resetToProgramSelection();
}

// -------------------- Loop --------------------
void loop() {
  if (digitalRead(BUTTON_EMERGENCY) == LOW) {
    handleEmergencyStop();
    return;
  }

  if (selectedProgram == 0) {
    loopProgram1(); // Vòng lặp Program 1
  } else {
    loopProgram2(); // Vòng lặp Program 2
  }
}

// -------------------- Program Selection --------------------
void resetToProgramSelection() {
  programSelected = false;
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Select Program:");
  updateProgramSelection();

  while (!programSelected) {
    if (digitalRead(BUTTON_NEXT) == LOW) {
      delay(200);
      selectedProgram = (selectedProgram + 1) % 2; // Chuyển giữa Program 0 và 1
      updateProgramSelection();
    }
    if (digitalRead(BUTTON_SELECT) == LOW) {
      delay(200);
      programSelected = true; // Xác nhận lựa chọn
    }
  }

  lcd.clear();

  // Đặt lại các trạng thái ban đầu
  isRunning = false;
  holding = false;
  returning = false;
  stepsTaken = 0;
  currentThresholdIndex = 0;
  currentHoldTimeIndex = 0;
  currentUpperLimitIndex = 0;
  currentLowerLimitIndex = 0;
  currentTreatmentTimeIndex = 0;

  // Chuyển đến chương trình đã chọn
  if (selectedProgram == 0) {
    setupProgram1(); // Chuyển đến thiết lập Program 1
    loopProgram1();  // Cho phép cài đặt thông số
  } else {
    setupProgram2(); // Chuyển đến thiết lập Program 2
    loopProgram2();  // Cho phép cài đặt thông số
  }
}

void updateProgramSelection() {
  lcd.setCursor(0, 1);
  if (selectedProgram == 0) {
    lcd.print("> Program 1     ");
  } else {
    lcd.print("> Program 2     ");
  }
}

// -------------------- Program 1 --------------------
void setupProgram1() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Program 1 Setup");
  delay(2000);
}

void loopProgram1() {
  if (!isRunning) {
    // Hiển thị giao diện cài đặt
    if (currentModeProgram1 == ADJUST_HOLD_TIME) {
      lcd.setCursor(0, 0);
      lcd.print("Set Hold Time:");
      lcd.setCursor(0, 1);
      lcd.print(holdTimes[currentHoldTimeIndex]);
      lcd.print("s ");
    } else {
      lcd.setCursor(0, 0);
      lcd.print("Set Threshold:");
      lcd.setCursor(0, 1);
      lcd.print(weightThresholds[currentThresholdIndex], 2);
      lcd.print("kg ");
    }

    // Cho phép điều chỉnh thông số
    if (digitalRead(BUTTON_ADJUST) == LOW) {
      delay(200);
      if (currentModeProgram1 == ADJUST_HOLD_TIME) {
        currentHoldTimeIndex = (currentHoldTimeIndex + 1) % 3;
      } else {
        currentThresholdIndex = (currentThresholdIndex + 1) % 3;
      }
    }

    // Chuyển đổi giữa các chế độ cài đặt
    if (digitalRead(BUTTON_MODE) == LOW) {
      delay(200);
      currentModeProgram1 = (Mode)((currentModeProgram1 + 1) % 2);
    }

    // Bắt đầu chương trình
    if (digitalRead(BUTTON_STARTSTOP) == LOW) {
      delay(200);
      isRunning = true;
      lcd.clear();
      lcd.print("Starting...");
      delay(1000);
    }
  } else {
    // Logic vận hành chương trình
    updateWeight();
    displayWeight();

    if (!holding && currentWeight < weightThresholds[currentThresholdIndex]) {
      moveMotor(true); // Động cơ quay
      stepsTaken++;
    } else if (!holding) {
      stopMotor();
      holding = true;
      holdStartTime = millis();
    }

    if (holding) {
      runHoldCycle();
    }
  }
}

// -------------------- Program 2 --------------------
void setupProgram2() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Program 2 Setup");
  delay(2000);
}

void loopProgram2() {
  if (!isRunning) {
    switch (currentModeProgram2) {
      case ADJUST_UPPER_LIMIT:
        lcd.setCursor(0, 0);
        lcd.print("Set Upper Limit:");
        lcd.setCursor(0, 1);
        lcd.print(upperLimits[currentUpperLimitIndex], 2);
        lcd.print("kg ");
        break;
      case ADJUST_LOWER_LIMIT:
        lcd.setCursor(0, 0);
        lcd.print("Set Lower Limit:");
        lcd.setCursor(0, 1);
        lcd.print(lowerLimits[currentLowerLimitIndex], 2);
        lcd.print("kg ");
        break;
      case ADJUST_TREATMENT_TIME:
        lcd.setCursor(0, 0);
        lcd.print("Set Treat Time:");
        lcd.setCursor(0, 1);
        lcd.print(treatmentTimes[currentTreatmentTimeIndex]);
        lcd.print("s ");
        break;
      case ADJUST_HOLD_TIME_PROGRAM2:
        lcd.setCursor(0, 0);
        lcd.print("Set Hold Time:");
        lcd.setCursor(0, 1);
        lcd.print(holdTimeProgram2);
        lcd.print("s ");
        break;
    }

    if (digitalRead(BUTTON_ADJUST) == LOW) {
      delay(200);
      switch (currentModeProgram2) {
        case ADJUST_UPPER_LIMIT:
          currentUpperLimitIndex = (currentUpperLimitIndex + 1) % 3;
          break;
        case ADJUST_LOWER_LIMIT:
          currentLowerLimitIndex = (currentLowerLimitIndex + 1) % 3;
          break;
        case ADJUST_TREATMENT_TIME:
          currentTreatmentTimeIndex = (currentTreatmentTimeIndex + 1) % 3;
          break;
        case ADJUST_HOLD_TIME_PROGRAM2:
          holdTimeProgram2 = (holdTimeProgram2 == 10 ? 15 : (holdTimeProgram2 == 15 ? 20 : 10));
          break;
      }
    }

    if (digitalRead(BUTTON_MODE) == LOW) {
      delay(200);
      currentModeProgram2 = (Mode2)((currentModeProgram2 + 1) % 4);
    }

    if (digitalRead(BUTTON_STARTSTOP) == LOW) {
      delay(200);
      isRunning = true;
      lcd.clear();
      lcd.print("Starting...");
      treatmentStartTime = millis();
      delay(1000);
    }
  } else {
    runTreatmentCycleProgram2();
  }
}

// -------------------- Hỗ trợ --------------------
void moveMotor(bool forward) {
  digitalWrite(EN_PIN, LOW);
  digitalWrite(DIR_PIN, forward ? HIGH : LOW);
  digitalWrite(STEP_PIN, HIGH);
  delayMicroseconds(3000); // Tăng thời gian trễ để giảm tốc độ động cơ
  digitalWrite(STEP_PIN, LOW);
  delayMicroseconds(3000);
}

void stopMotor() {
  digitalWrite(EN_PIN, HIGH);
}

void updateWeight() {
  currentWeight = scale.get_units();
}

void displayWeight() {
  lcd.setCursor(0, 0);
  lcd.print("Weight: ");
  lcd.print(currentWeight, 2);
  lcd.print("kg ");
}

void runHoldCycle() {
  unsigned long elapsedHoldTime = (millis() - holdStartTime) / 1000;
  lcd.setCursor(0, 1);
  lcd.print("Hold: ");
  lcd.print(holdTimes[currentHoldTimeIndex] - elapsedHoldTime);
  lcd.print("s ");

  if (elapsedHoldTime >= holdTimes[currentHoldTimeIndex]) {
    holding = false;
    returning = true;
  }

  if (returning) {
    returnToStart(true);
  }
}

void runHoldCycleProgram2() {
  unsigned long elapsedHoldTime = (millis() - holdStartTime) / 1000;
  updateWeight(); // Cập nhật trọng lượng trong thời gian giữ
  lcd.setCursor(0, 0);
  lcd.print("Weight: ");
  lcd.print(currentWeight, 2);
  lcd.print("kg ");

  lcd.setCursor(0, 1);
  lcd.print("Hold: ");
  lcd.print(holdTimeProgram2 - elapsedHoldTime);
  lcd.print("s ");

  if (elapsedHoldTime >= holdTimeProgram2) {
    holding = false;
  }
}

void runTreatmentCycleProgram2() {
  unsigned long elapsedTreatmentTime = (millis() - treatmentStartTime) / 1000;

  if (elapsedTreatmentTime >= treatmentTimes[currentTreatmentTimeIndex]) {
    stopMotor();
    isRunning = false;
    returnToStart(true); // Quay về ngay màn hình chọn chương trình
    return;
  }

  if (holding) {
    runHoldCycleProgram2();
  } else {
    updateWeight();
    displayWeight();

    if (movingToUpper && currentWeight < upperLimits[currentUpperLimitIndex]) {
      moveMotor(true);
      stepsTaken++;
    } else if (!movingToUpper && currentWeight > lowerLimits[currentLowerLimitIndex]) {
      moveMotor(false);
      stepsTaken++;
    } else {
      stopMotor();
      holding = true;
      holdStartTime = millis();
      movingToUpper = !movingToUpper; // Chuyển hướng động cơ sau khi giữ
    }
  }
}

void returnToStart(bool slow) {
  while (stepsTaken > 0) {
    moveMotor(false);
    stepsTaken--;
    if (slow) delay(10); // Di chuyển chậm khi quay về
  }
  stopMotor();
  stepsTaken = 0; // Đảm bảo không vượt quá vị trí step = 0
  resetToProgramSelection(); // Quay trực tiếp đến chọn chương trình
}

void handleEmergencyStop() {
  emergencyStop = true;
  isRunning = false;
  stopMotor();
  lcd.clear();
  lcd.print("EMERGENCY STOP");
  delay(2000);
  resetToProgramSelection();
}


however, after the end of program2 time, the motor does not return to the original position (step=0), very grateful if you can help me, thank you very much, wish you have lots of fun in life!!

It is not a good idea to use the controller to provide Emergency Stop. We interrupt the power to the system to stop it from operating.

1 Like

my stepper motor control program is constantly changing its position back and forth, so i need code to track its exact position so i can control it to go back to its original position. i haven't done it yet, it's so hard, i would appreciate it if someone could help me solve this problem, thank you very much!!!

How many guesses do we get?

One.

What driver are you using ?
Please post your code. - properly - then we might be able to help more.

@dung_vn - Here is a simulation that rotates "out," keeps track of steps, then returns "home."

1 Like

None of the several Stepper libraries have a sample that clues you in?

@dung_vn, please do not cross-post. Threads merged.

thank you very much, but in my program2, the motor needs to move back and forth between positions so it needs to be tracked precisely so that when it returns it is in the same original position, do you think I can use a limit switch?

That is what the rotate() function in the simulation does.

You may use any device to indicate a boundary, so counting steps would not be needed.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.