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);
}```