PWM Stepper Motor Control + Reading Hacked Calipers

SUMMARY (TL;DR): I'll offer some more specific details about the nature of the project below, but the problem is simple: I've been controlling a NEMA 23 stepper motor using an Arduino Uno + TB6560 Driver with success. I just added some code to read a hacked set of digital calipers (instructions here) so that I can measure how far the stepper motor stage has traveled, and in doing so, the stepper motor suddenly runs much slower than before.

I am not using the AccelStepper library, because I wanted to keep track of the number of steps I've taken so that I could calculate volume accordingly. Instead, I'm using a pulse delays as a janky form of pulse-width modulation. Now that I've added more code (that takes non-zero time to execute), I fear that it's messed up the degree of pulse delay.

My question is thus two-fold:
(1) Is my hypothesis correct--i.e., could adding code in the loop for reading the calipers mess up my PWM? Suggested changes to the code below are very welcome. I tried to document everything clearly.
(2) Should I just use the AccelStepper library instead? Would that eliminate any need to do PWM manually, while also letting me accurately control stepper speed?

Many thanks,
Luke

FULL VERSION:

  • I'm using an Arduino Uno + TB6560 to control a NEMA 23 stepper motor
  • That stepper motor compresses six syringes--forming a home-made syringe pump
  • Thus far, I've been calculating volume based on the number of steps taken and some characterized info about the linear stage
  • Realizing that there was some backlash in the system & that the stepper motor lacks a rotary encoder (to tell me the actual position), I now want to add position feedback by tacking on a hacked digital caliper
  • The code used to achieve all of this is below. It's involved... there are a lot of components... but my main curiosity is why--now that I've added some code to read out the calipers--the stepper motor runs much slower.
  • I'm looking for (1) coding suggestions on how to make reading the caliper much faster, or (2) coding method to control the stepper motor using manual PWM that semi-accurately controls speed.
  • Thanks for your time & effort!
//------------------------------ **START HERE** -----------------------------//
// @USER: Enter your dispensing rate & volume...
const float setDispRate = 100.0;  // [mL/min]
const float dispenseVol = 16.0; // **USE WHOLE NUMBERS** [mL]
const float dispenseRate = setDispRate/60;  // [mL/s]
// @USER: Don't forget to zero the calipers before you start run.

//--------------------------SETUP & INITIALIZATION---------------------------//
// Configure some ADC stuff:
#include<ADS1115_WE.h> 
#include<Wire.h>
#define I2C_ADDRESS 0x48
ADS1115_WE adc(I2C_ADDRESS);

// Define stepper pin connections:
#define dirPin 9   // stepper speed
#define stepPin 10 // stepper direction
#define upperLim 4 // Hall Effect Sensor, Upper Limit
#define lowerLim 5 // Hall Effect Sensor, Lower Limit

// Calculate stepper speed based on dispensing rate:
const float stepsPerRev = 3200.0;   // set on stepper driver, [steps/rev]
const float lead = 4.0;             // for ball screw, [mm/rev]
const int syringes = 6;           // no. of syringes
const float traVol = (50.0/90)*syringes;  // measured, [mL/mm]    
const float pulseDelay = (1E3*traVol*lead)/(stepsPerRev*dispenseRate);  // [ms]
int pulseWidthMicros = 10;  // [us]
unsigned int steps_total = dispenseVol*stepsPerRev/(lead*traVol); // [steps]
float V = 0.00;
float P;
float V_last;

// Initialize some stuff for caliper measurement: 
int sign;
long value;
float caliper_pos;
float V_meas;
int clockpin = 3;  
int datapin = 2;
unsigned long tempmicros;



//--------------------------FUNCTION DEFINITIONS---------------------------//

// ADC "readChannel" Function
float readChannel(ADS1115_MUX channel) {
  float voltage = 0.0;
  adc.setCompareChannels(channel);
  adc.startSingleMeasurement();
  while(adc.isBusy()){}
  voltage = adc.getResult_V(); // alternative: getResult_mV for Millivolt
  return voltage;
}

// Pressure Function:
float pressure(int sensorVal){
    float p_result;
    float volts = readChannel(ADS1115_COMP_0_GND);
    float offset = 0.00; 
    p_result = 7.6336*volts - 7.6312 - offset;
    return p_result;
}

// Volume Function: 
float vol(int steps){
  float v_result;
  v_result = (steps/stepsPerRev)*lead*traVol; // [mL]
  return v_result;
}

// Read Caliper Function: 
float readCalipers(){
  while (digitalRead(clockpin)==HIGH) {} //if clock is LOW wait until it turns to HIGH
  tempmicros=micros();
  while (digitalRead(clockpin)==LOW) {} //wait for the end of the HIGH pulse
  if ((micros()-tempmicros)>500) { //if the HIGH pulse was longer than 500 micros we are at the start of a new bit sequence
    decode(); //decode the bit sequence
  }
}

// Decode calipers & calc volume fxn:
float decode(){
  sign=1;
  value=0;

  for (int i=0;i<23;i++) {
    while (digitalRead(clockpin)==HIGH) { } //wait until clock returns to HIGH- the first bit is not needed
    while (digitalRead(clockpin)==LOW) {} //wait until clock returns to LOW
    if (digitalRead(datapin)==LOW) {
      if (i<20) {
        value|= 1<<i;
      }
      if (i==20) {
        sign=-1;
      }
    }
  }
  caliper_pos = (value*sign)/100.00;
  V_meas = abs(caliper_pos)*traVol; 
  return V_meas;
} 

//--------------------------VOID SETUP---------------------------//
void setup(){
  // Initialize communication:
  Wire.begin();
  Serial.begin(9600);
  delay(1000);
  
  // Declare pins:
  pinMode(stepPin, OUTPUT);   // sets stepper speed
  pinMode(dirPin, OUTPUT);    // sets stepper direction
  pinMode(upperLim, INPUT);   // upper limit switch 
  pinMode(lowerLim, INPUT);   // lower limit switch
  pinMode(clockpin, INPUT);   // caliper serial comm, SCL
  pinMode(datapin, INPUT);    // caliper serial comm, SDA

  // Initialize steps, print headers:
  Serial.print("Dispense Rate: ");
  Serial.print(setDispRate); 
  Serial.println(" [mL/min]");
  Serial.println("V_calc (mL), V_meas (mL), P (psi)");

  // 

  // Initialize ADC stuff: 
  if(!adc.init()){
    Serial.println("ADS1115 not connected!");
  }
  adc.setVoltageRange_mV(ADS1115_RANGE_6144); // equates to +/- 6144 mV
  
}


//--------------------------VOID LOOP---------------------------//
void loop() {

  //--DISPENSING: 
  for(int steps = 0; steps <= steps_total; steps++){
    // Take Steps:
    digitalWrite(dirPin,LOW); // Note: LOW = DOWN
    digitalWrite(stepPin, HIGH);
    delayMicroseconds(pulseWidthMicros);
    digitalWrite(stepPin, LOW);
    delay(pulseDelay);

    // Print P-V:
    V = vol(steps); //---calculated volume
    Serial.print(V);
    Serial.print(",");
    
    readCalipers(); //---measured volume
    Serial.print(V_meas,2);
    Serial.print(",");
    
    int sensorVal = analogRead(A0); //---pressure
    P = pressure(sensorVal); // function
    Serial.println(P,4); 

    // Emergency Stop, Lower Limit:
    if(digitalRead(lowerLim)==LOW){
      Serial.println("LOWER LIMIT REACHED!!!");
      delay(100);
      break;
    }
  }


  //--PAUSE: 
  delay(10);
  const float V_last = V; // save our last volume reading


  //--SIPHONING: 
  for(int steps = 0; steps <= steps_total; steps++){
    // Take steps: 
    digitalWrite(dirPin,HIGH); // Note: HIGH = UP
    digitalWrite(stepPin, HIGH);
    delayMicroseconds(pulseWidthMicros);
    digitalWrite(stepPin, LOW);
    delay(pulseDelay);    

    // Print P-V:
    V = V_last - vol(steps); //---calcd volume
    Serial.print(V);
    Serial.print(",");
    
    readCalipers(); //---measured volume
    Serial.print(V_meas,2);
    Serial.print(",");
    
    int sensorVal = analogRead(A0); //---pressure
    P = pressure(sensorVal); 
    Serial.println(P,4); 

    // Emergency Stop, Upper Limit:
    if(digitalRead(upperLim)==LOW){
      Serial.println("UPPER LIMIT REACHED!!!");      
      delay(100);
      break;
    }  
  }
  delay(100);
  exit(0);
}```

PWM?

Where are you using PWM ?

Your link to the hacked digital caliper instructions takes me to google calendar.
Can you please correct it?

Fixed! Sorry about that.

Hmmm maybe I shouldn't have called it PWM (I thought calling it "janky" PWM denoted that it's not true PWM), but it's here in the code:

    digitalWrite(dirPin,LOW); // Note: LOW = DOWN
    digitalWrite(stepPin, HIGH);
    delayMicroseconds(pulseWidthMicros);
    digitalWrite(stepPin, LOW);
    delay(pulseDelay);

This results in one step.

I suggest you use AccelStepper and call currentPosition() to keep track of how far you have moved
Multi-tasking in Arduino has a complete stepper example
with details on how to get it to run faster.