Stepper motor control by loadcell

Hi everyone, I need to control the stepper motor based on the weight obtained from the loadcell: after selecting the traction force, holding time, treatment time, press start, the motor starts running (the motor runs slowly because the function is to pull the spine, it cannot be pulled suddenly), when it reaches the set force, hold, the timer counts down until the holding time is over, the motor returns to its original position according to the initial pulling speed. While pulling, when pressing the emergency stop button, the motor will stop. But after selecting the force, the motor does not rotate. Please help me fix the code, God will bless good people, thank you!!!

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

// Chân điều khiển động cơ bước
#define STEP1_PIN 10
#define DIR1_PIN 11
#define EN1_PIN 13

// Chân load cell
#define LOADCELL_DOUT_PIN 3
#define LOADCELL_SCK_PIN 2

// Chân nút nhấn
#define BUTTON_THRESHOLD 4
#define BUTTON_HOLDTIME 5
#define BUTTON_STARTSTOP 6
#define BUTTON_EMERGENCY 7

// Load cell
HX711 scale;
float calibration_factor = -7050.0; // Hệ số hiệu chuẩn
float weightThresholds[] = {1.02, 1.53, 2.04}; // Ngưỡng trọng lượng (kg)
int currentThresholdIndex = 0;

// Giữ thời gian (giây)
unsigned int holdTimes[] = {10, 15, 20};
int currentHoldTimeIndex = 0;

// LCD I2C
LiquidCrystal_I2C lcd(0x27, 16, 2);

// Biến điều khiển
bool isRunning = false;
bool holding = false;
bool returning = false;
bool emergencyStop = false;
unsigned long holdStartTime = 0;

// Lọc trọng lượng
const int numReadings = 10;
float readings[numReadings];
int readIndex = 0;
float total = 0;
float averageWeight = 0;

void setup() {
  Serial.begin(9600);

  // Thiết lập các chân điều khiển
  pinMode(STEP1_PIN, OUTPUT);
  pinMode(DIR1_PIN, OUTPUT);
  pinMode(EN1_PIN, OUTPUT);
  pinMode(BUTTON_THRESHOLD, INPUT_PULLUP);
  pinMode(BUTTON_HOLDTIME, INPUT_PULLUP);
  pinMode(BUTTON_STARTSTOP, INPUT_PULLUP);
  pinMode(BUTTON_EMERGENCY, INPUT_PULLUP);

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

  // Tắt động cơ ban đầu
  digitalWrite(EN1_PIN, HIGH);
  digitalWrite(STEP1_PIN, LOW);
  digitalWrite(DIR1_PIN, LOW);

  // LCD
  lcd.begin();
  lcd.backlight();
  lcd.print("System Ready");

  delay(2000);
  clearReadings();
  updateLCD();
}

void loop() {
  // Cập nhật giá trị trọng lượng
  updateWeight();

  // Dừng khẩn cấp
  if (digitalRead(BUTTON_EMERGENCY) == LOW) {
    handleEmergencyStop();
    return;
  }

  // Hiển thị trọng lượng và ngưỡng
  lcd.setCursor(0, 0);
  lcd.print("W: ");
  lcd.print(averageWeight, 2);
  lcd.print("kg Th: ");
  lcd.print(weightThresholds[currentThresholdIndex], 2);
  lcd.print("kg ");

  // Xử lý nút nhấn
  handleButtons();

  // Xử lý logic động cơ khi chạy
  if (isRunning && !holding && !returning) {
    if (averageWeight < weightThresholds[currentThresholdIndex]) {
      moveMotor(true); // Quay động cơ để kéo tải trọng
    } else {
      stopMotor();
      holding = true;
      holdStartTime = millis();
      Serial.println("Holding position...");
    }
  }

  // Thời gian giữ
  if (holding) {
    unsigned long elapsedHoldTime = (millis() - holdStartTime) / 1000;
    unsigned int remainingHoldTime = holdTimes[currentHoldTimeIndex] - elapsedHoldTime;

    if (remainingHoldTime > 0) {
      lcd.setCursor(0, 1);
      lcd.print("Hold: ");
      lcd.print(remainingHoldTime);
      lcd.print("s    ");
    } else {
      holding = false;
      returning = true;
      Serial.println("Returning to initial position...");
    }
  }

  // Trả động cơ về vị trí ban đầu
  if (returning) {
    if (averageWeight > 0.1) { // Giả sử 0.1kg là trọng lượng gần bằng 0
      moveMotor(false); // Quay động cơ ngược lại
    } else {
      stopMotor();
      returning = false;
      isRunning = false;
      Serial.println("Returned to initial position.");
      lcd.setCursor(0, 1);
      lcd.print("System Ready     ");
    }
  }
}

// Cập nhật giá trị trọng lượng trung bình
void updateWeight() {
  total -= readings[readIndex];
  readings[readIndex] = scale.get_units();
  total += readings[readIndex];
  readIndex = (readIndex + 1) % numReadings;
  averageWeight = total / numReadings;
}

// Xử lý các nút nhấn
void handleButtons() {
  static unsigned long lastButtonPress = 0;

  if (digitalRead(BUTTON_THRESHOLD) == LOW && millis() - lastButtonPress > 300) {
    lastButtonPress = millis();
    currentThresholdIndex = (currentThresholdIndex + 1) % 3;
    updateLCD();
    Serial.print("Threshold changed to: ");
    Serial.println(weightThresholds[currentThresholdIndex]);
  }

  if (digitalRead(BUTTON_HOLDTIME) == LOW && millis() - lastButtonPress > 300) {
    lastButtonPress = millis();
    currentHoldTimeIndex = (currentHoldTimeIndex + 1) % 3;
    updateLCD();
    Serial.print("Hold time changed to: ");
    Serial.println(holdTimes[currentHoldTimeIndex]);
  }

  if (digitalRead(BUTTON_STARTSTOP) == LOW && millis() - lastButtonPress > 300) {
    lastButtonPress = millis();
    isRunning = !isRunning;
    if (isRunning) {
      Serial.println("System Started");
    } else {
      Serial.println("System Stopped");
      stopMotor();
    }
  }
}

// Điều khiển quay động cơ
void moveMotor(bool forward) {
  digitalWrite(EN1_PIN, LOW);
  digitalWrite(DIR1_PIN, forward ? HIGH : LOW);
  digitalWrite(STEP1_PIN, HIGH);
  delayMicroseconds(500); // Tạo xung
  digitalWrite(STEP1_PIN, LOW);
  delayMicroseconds(500);
}

// Dừng động cơ
void stopMotor() {
  digitalWrite(EN1_PIN, HIGH);
  Serial.println("Motor Stopped.");
}

// Dừng khẩn cấp
void handleEmergencyStop() {
  emergencyStop = true;
  isRunning = false;
  holding = false;
  returning = false;
  stopMotor();
  Serial.println("Emergency Stop Activated!");
  lcd.setCursor(0, 1);
  lcd.print("EMERGENCY STOP  ");
  delay(500);
}

// Làm sạch bộ nhớ đọc giá trị trọng lượng
void clearReadings() {
  for (int i = 0; i < numReadings; i++) {
    readings[i] = 0;
  }
  total = 0;
  averageWeight = 0;
}

// Cập nhật hiển thị LCD
void updateLCD() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Th: ");
  lcd.print(weightThresholds[currentThresholdIndex], 2);
  lcd.print("kg Hold: ");
  lcd.print(holdTimes[currentHoldTimeIndex]);
  lcd.print("s");
}

Of what?

2 Likes

I am quite certain that is a message... saving someone from paralysis for copy/pasting code rather than learning it.

1 Like

my program is simulating spinal decompression, can you help me make it work? thank you so much!!!

it's almost due so i haven't done much research yet, would appreciate it if you could help me get it done!!

That is your problem.

Also your problem.

Also your problem.

Your code should not work because you use the wrong calls with your LCD. After correcting, your code works in a simulator. Very sloppy to have "EMERGENCY STOP" when the motor is running. Are you certain you do not mind jeopardizing someone's life? Verify your wiring.

diagram.json for wokwi.com
{
  "version": 1,
  "author": "Anonymous maker",
  "editor": "wokwi",
  "parts": [
    { "type": "wokwi-arduino-nano", "id": "nano", "top": -33.6, "left": -0.5, "attrs": {} },
    {
      "type": "wokwi-hx711",
      "id": "cell1",
      "top": -103,
      "left": 185,
      "attrs": { "type": "50kg" }
    },
    { "type": "wokwi-vcc", "id": "vcc1", "top": -133.64, "left": 163.2, "attrs": {} },
    { "type": "wokwi-gnd", "id": "gnd1", "top": -9.6, "left": 172.2, "attrs": {} },
    {
      "type": "wokwi-pushbutton",
      "id": "btn1",
      "top": -109,
      "left": -9.6,
      "attrs": { "color": "green" }
    },
    {
      "type": "wokwi-pushbutton",
      "id": "btn2",
      "top": -157,
      "left": -9.6,
      "attrs": { "color": "green" }
    },
    {
      "type": "wokwi-pushbutton",
      "id": "btn3",
      "top": -205,
      "left": -9.6,
      "attrs": { "color": "green" }
    },
    {
      "type": "wokwi-pushbutton",
      "id": "btn4",
      "top": -253,
      "left": -9.6,
      "attrs": { "color": "green" }
    },
    { "type": "wokwi-a4988", "id": "drv1", "top": -129.6, "left": -168, "attrs": {} },
    {
      "type": "wokwi-stepper-motor",
      "id": "stepper1",
      "top": -240.01,
      "left": -92.58,
      "attrs": { "size": "8" }
    },
    { "type": "wokwi-vcc", "id": "vcc2", "top": -181.64, "left": -115.2, "attrs": {} },
    { "type": "wokwi-gnd", "id": "gnd2", "top": -38.4, "left": -106.2, "attrs": {} },
    {
      "type": "wokwi-lcd1602",
      "id": "lcd1",
      "top": 73.6,
      "left": 159.2,
      "attrs": { "pins": "i2c" }
    }
  ],
  "connections": [
    [ "vcc1:VCC", "cell1:VCC", "red", [ "v0" ] ],
    [ "gnd1:GND", "cell1:GND", "black", [ "v0" ] ],
    [ "cell1:SCK", "nano:2", "green", [ "h0" ] ],
    [ "cell1:DT", "nano:3", "green", [ "h0" ] ],
    [ "nano:GND.2", "btn1:2.r", "black", [ "v0" ] ],
    [ "nano:GND.2", "btn2:2.r", "black", [ "v0" ] ],
    [ "nano:GND.2", "btn3:2.r", "black", [ "v0" ] ],
    [ "nano:GND.2", "btn4:2.r", "black", [ "v0" ] ],
    [ "nano:4", "btn4:1.r", "green", [ "v0" ] ],
    [ "nano:5", "btn3:1.r", "green", [ "v-163.2", "h-76.8" ] ],
    [ "nano:6", "btn2:1.r", "green", [ "v0" ] ],
    [ "nano:7", "btn1:1.r", "green", [ "v0" ] ],
    [ "stepper1:A-", "drv1:2A", "green", [ "v0" ] ],
    [ "stepper1:A+", "drv1:2B", "green", [ "v0" ] ],
    [ "stepper1:B+", "drv1:1B", "green", [ "v67.2", "h-38.4" ] ],
    [ "stepper1:B-", "drv1:1A", "green", [ "v0" ] ],
    [ "vcc2:VCC", "drv1:VMOT", "red", [ "v0" ] ],
    [ "gnd2:GND", "drv1:GND.1", "black", [ "v0" ] ],
    [ "gnd2:GND", "drv1:GND.2", "black", [ "v0" ] ],
    [ "vcc2:VCC", "drv1:VDD", "red", [ "v0" ] ],
    [ "drv1:SLEEP", "drv1:RESET", "green", [ "h-9.6", "v-9.6" ] ],
    [ "nano:10", "drv1:STEP", "green", [ "v-9.6", "h-220.8", "v-19.2" ] ],
    [ "nano:11", "drv1:DIR", "green", [ "v-19.2", "h-201.6", "v-9.6" ] ],
    [ "nano:13", "drv1:ENABLE", "green", [ "v9.6", "h-211.2", "v-153.6" ] ],
    [ "lcd1:GND", "nano:GND.1", "black", [ "h0" ] ],
    [ "lcd1:VCC", "nano:5V", "red", [ "h0" ] ],
    [ "lcd1:SDA", "nano:A4", "green", [ "h0" ] ],
    [ "lcd1:SCL", "nano:A5", "green", [ "h0" ] ]
  ],
  "dependencies": {}
}

1 Like

Hi, @dung_vn

Emergency Stop should not be a software action.

Emergency Stops disconnect power to all pieces of equipment to stop reliably and instantly.

I would definitely want the E-Stop to function when the motor is running, one of the main reasons for an E-Stop.

Tom... :smiley: :+1: :coffee: :australia:

1 Like

Sorry. My poor writing without thinking...

The code has the motor running while displaying "EMERGENCY STOP" from the previous stop, still displayed on the LCD. The LCD should be cleared of the previous action.

1 Like

Thank you very much for your dedication, i have revised and refined the display a bit: after setting the time and threshold force i press the start button the screen will hide the two information time and threshold force to make room for the actual force display from the loadcell on the LCD, however the screen has not displayed the actual force from the loadcell correctly and the motor has not held when the force reaches the threshold. i really hope you can help me, thank god for sending you down to support us!!

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

// Chân điều khiển động cơ bước
#define STEP1_PIN 10
#define DIR1_PIN 11
#define EN1_PIN 13

// Chân load cell
#define LOADCELL_DOUT_PIN 3
#define LOADCELL_SCK_PIN 2

// Chân nút nhấn
#define BUTTON_THRESHOLD 4
#define BUTTON_HOLDTIME 5
#define BUTTON_STARTSTOP 6
#define BUTTON_EMERGENCY 7

// Load cell
HX711 scale;
float calibration_factor = 5000.0; // Hệ số hiệu chuẩn
float weightThresholds[] = {1.02, 1.53, 2.04}; // Ngưỡng trọng lượng (kg)
int currentThresholdIndex = 0;

// Giữ thời gian (giây)
unsigned int holdTimes[] = {10, 15, 20};
int currentHoldTimeIndex = 0;

// LCD I2C
LiquidCrystal_I2C lcd(0x27, 16, 2);

// Biến điều khiển
bool isRunning = false;
bool holding = false;
bool returning = false;
bool emergencyStop = false;
unsigned long holdStartTime = 0;

// Lọc trọng lượng
const int numReadings = 10;
float readings[numReadings];
int readIndex = 0;
float total = 0;
float averageWeight = 0;

void setup() {
  Serial.begin(9600);

  // Thiết lập các chân điều khiển
  pinMode(STEP1_PIN, OUTPUT);
  pinMode(DIR1_PIN, OUTPUT);
  pinMode(EN1_PIN, OUTPUT);
  pinMode(BUTTON_THRESHOLD, INPUT_PULLUP);
  pinMode(BUTTON_HOLDTIME, INPUT_PULLUP);
  pinMode(BUTTON_STARTSTOP, INPUT_PULLUP);
  pinMode(BUTTON_EMERGENCY, INPUT_PULLUP);

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

  // Tắt động cơ ban đầu
  digitalWrite(EN1_PIN, HIGH);
  digitalWrite(STEP1_PIN, LOW);
  digitalWrite(DIR1_PIN, LOW);

  // LCD
  lcd.init();
  lcd.backlight();
  lcd.print("System Ready");

  delay(2000);
  clearReadings();
  updateLCD();
}

void loop() {
  // Cập nhật giá trị trọng lượng
  updateWeight();

  // Dừng khẩn cấp
  if (digitalRead(BUTTON_EMERGENCY) == LOW) {
    handleEmergencyStop();
    return;
  }

  // Hiển thị thời gian giữ khi chưa nhấn Start
  if (!isRunning) {
    lcd.setCursor(0, 0);
    lcd.print("Hold: ");
    lcd.print(holdTimes[currentHoldTimeIndex]);
    lcd.print("s       ");
    lcd.setCursor(0, 1);
    lcd.print("Th: ");
    lcd.print(weightThresholds[currentThresholdIndex], 2);
    lcd.print("kg       ");
  }

  // Xử lý nút nhấn
  handleButtons();

  // Khi nhấn Start
  if (isRunning && !holding && !returning) {
    lcd.setCursor(0, 0);
    lcd.print("W: ");
    lcd.print(averageWeight, 2);
    lcd.print("kg       ");
    lcd.setCursor(0, 1);
    lcd.print("                "); // Xóa dòng dưới

    if (averageWeight < weightThresholds[currentThresholdIndex]) {
      moveMotor(true); // Quay động cơ để kéo tải trọng
    } else {
      stopMotor();
      holding = true;
      holdStartTime = millis();
      Serial.println("Holding position...");
    }
  }

  // Hiển thị thời gian đếm ngược khi giữ
  if (holding) {
    unsigned long elapsedHoldTime = (millis() - holdStartTime) / 1000;
    unsigned int remainingHoldTime = holdTimes[currentHoldTimeIndex] - elapsedHoldTime;

    lcd.setCursor(0, 0);
    lcd.print("Hold: ");
    lcd.print(remainingHoldTime);
    lcd.print("s       ");
    if (remainingHoldTime == 0) {
      holding = false;
      returning = true;
      Serial.println("Returning to initial position...");
    }
  }

  // Trả động cơ về vị trí ban đầu
  if (returning) {
    if (averageWeight > 0.1) { // Giả sử 0.1kg là trọng lượng gần bằng 0
      moveMotor(false); // Quay động cơ ngược lại
    } else {
      stopMotor();
      returning = false;
      isRunning = false;
      Serial.println("Returned to initial position.");
      lcd.setCursor(0, 1);
      lcd.print("System Ready     ");
    }
  }
}

// Cập nhật giá trị trọng lượng trung bình
void updateWeight() {
  total -= readings[readIndex];
  readings[readIndex] = scale.get_units();
  total += readings[readIndex];
  readIndex = (readIndex + 1) % numReadings;
  averageWeight = total / numReadings;
}

// Xử lý các nút nhấn
void handleButtons() {
  static unsigned long lastButtonPress = 0;

  if (digitalRead(BUTTON_THRESHOLD) == LOW && millis() - lastButtonPress > 300) {
    lastButtonPress = millis();
    currentThresholdIndex = (currentThresholdIndex + 1) % 3;
    updateLCD();
    Serial.print("Threshold changed to: ");
    Serial.println(weightThresholds[currentThresholdIndex]);
  }

  if (digitalRead(BUTTON_HOLDTIME) == LOW && millis() - lastButtonPress > 300) {
    lastButtonPress = millis();
    currentHoldTimeIndex = (currentHoldTimeIndex + 1) % 3;
    updateLCD();
    Serial.print("Hold time changed to: ");
    Serial.println(holdTimes[currentHoldTimeIndex]);
  }

  if (digitalRead(BUTTON_STARTSTOP) == LOW && millis() - lastButtonPress > 300) {
    lastButtonPress = millis();
    isRunning = !isRunning;
    if (isRunning) {
      Serial.println("System Started");
    } else {
      Serial.println("System Stopped");
      stopMotor();
    }
  }
}

// Điều khiển quay động cơ
void moveMotor(bool forward) {
  digitalWrite(EN1_PIN, LOW);
  digitalWrite(DIR1_PIN, forward ? HIGH : LOW);
  digitalWrite(STEP1_PIN, HIGH);
  delayMicroseconds(500); // Tạo xung
  digitalWrite(STEP1_PIN, LOW);
  delayMicroseconds(500);
}

// Dừng động cơ
void stopMotor() {
  digitalWrite(EN1_PIN, HIGH);
  Serial.println("Motor Stopped.");
}

// Dừng khẩn cấp
void handleEmergencyStop() {
  emergencyStop = true;
  isRunning = false;
  holding = false;
  returning = false;
  stopMotor();
  Serial.println("Emergency Stop Activated!");
  lcd.setCursor(0, 1);
  lcd.print("EMERGENCY STOP  ");
  delay(500);
}

// Làm sạch bộ nhớ đọc giá trị trọng lượng
void clearReadings() {
  for (int i = 0; i < numReadings; i++) {
    readings[i] = 0;
  }
  total = 0;
  averageWeight = 0;
}

// Cập nhật hiển thị LCD
void updateLCD() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Hold: ");
  lcd.print(holdTimes[currentHoldTimeIndex]);
  lcd.print("s       ");
  lcd.setCursor(0, 1);
  lcd.print("Th: ");
  lcd.print(weightThresholds[currentThresholdIndex], 2);
  lcd.print("kg       ");
}

[https://wokwi.com/projects/418475757212323841](https://wokwi.com/projects/418475757212323841)

I did it like this, please give your comments!!

  1. Print instructions to the LCD on how to start (add weight, increase time, other)
  2. After every button press, print instructions to lead the user to successfully starting the device.

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