Dual syringe pump control via stepper motors and tb6560 3a

Hi all,

I'm creating a dual syringe pump which can really help out with laboratory work. The set up is simple enough: both syringes will have to move at the same speed( 0.5 ml/min) and push until stop.

My approach is to make a single pump work and then add a second one.

For this I have chosen to work with an Arduino Uno and a LCD keypad shield which will control two NEMA 17 bi-polar stepper motors(17H2a;0,4A). Both stepper motors are connected to a tb6560 3a stepper motor driver board.

I have found Arduino code for this online( In attachment) and tried to make one stepper motor work. The results where somewhat strange and my knowledge of Arduino is lacking, so I hope someone here can point me in the right direction.

Connections:

Pin 2 to CLK+ (board 1)
Pin 3 to CW+ (board 1)
Pin 4 to CLK+ (board 2)
Pin 5 to CW+ (board 2)

GND to CLK- and CW- (both boards)
12V to 24V and GND (both boards)

Result:

When I try to move stepper motor one via the LCD keypad controls, Stepper motor 2 (pin3/4) starts to move.
I know the code is for the EasyDriver but is that the reason for this behavior. As how I understand it, pin 3 and 4 should have no activity.

Can anyone advice me what I did wrong and how to solve/approach it? My end goal is to be able to start stop both stepper motors at the same time and be able to increase/decrease the speed.

Feel free to ask me for any info I forgot to provide.

syringePump.ino (8.41 KB)

Please attach Your code inside code tags.

I have the code from : OpenSyringePump/syringePump.ino at master · manimino/OpenSyringePump · GitHub

#include <LiquidCrystal.h>
#include <LCDKeypad.h>

/* -- Constants -- */
#define SYRINGE_VOLUME_ML 30.0
#define SYRINGE_BARREL_LENGTH_MM 80.0

#define THREADED_ROD_PITCH 1.25
#define STEPS_PER_REVOLUTION 200.0
#define MICROSTEPS_PER_STEP 16.0

#define SPEED_MICROSECONDS_DELAY 100 //longer delay = lower speed

long ustepsPerMM = MICROSTEPS_PER_STEP * STEPS_PER_REVOLUTION / THREADED_ROD_PITCH;
long ustepsPerML = (MICROSTEPS_PER_STEP * STEPS_PER_REVOLUTION * SYRINGE_BARREL_LENGTH_MM) / (SYRINGE_VOLUME_ML * THREADED_ROD_PITCH );

/* -- Pin definitions -- */
int motorDirPin = 2;
int motorStepPin = 3;

int triggerPin = A3;
int bigTriggerPin = A4;

/* -- Keypad states -- */
int  adc_key_val[5] ={30, 150, 360, 535, 760 };

enum{ KEY_RIGHT, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_SELECT, KEY_NONE};
int NUM_KEYS = 5;
int adc_key_in;
int key = KEY_NONE;

/* -- Enums and constants -- */
enum{PUSH,PULL}; //syringe movement direction
enum{MAIN, BOLUS_MENU}; //UI states

const int mLBolusStepsLength = 9;
float mLBolusSteps[9] = {0.001, 0.005, 0.010, 0.050, 0.100, 0.500, 1.000, 5.000, 10.000};

/* -- Default Parameters -- */
float mLBolus = 0.500; //default bolus size
float mLBigBolus = 1.000; //default large bolus size
float mLUsed = 0.0;
int mLBolusStepIdx = 3; //0.05 mL increments at first
float mLBolusStep = mLBolusSteps[mLBolusStepIdx];

long stepperPos = 0; //in microsteps
char charBuf[16];

//debounce params
long lastKeyRepeatAt = 0;
long keyRepeatDelay = 400;
long keyDebounce = 125;
int prevKey = KEY_NONE;
        
//menu stuff
int uiState = MAIN;

//triggering
int prevBigTrigger = HIGH;
int prevTrigger = HIGH;

//serial
String serialStr = "";
boolean serialStrReady = false;

/* -- Initialize libraries -- */
LiquidCrystal lcd(8, 13, 9, 4, 5, 6, 7);

void setup(){
  /* LCD setup */  
  lcd.begin(16, 2);
  lcd.clear();

  lcd.print("SyringePump v2.0");

  /* Triggering setup */
  pinMode(triggerPin, INPUT);
  pinMode(bigTriggerPin, INPUT);
  digitalWrite(triggerPin, HIGH); //enable pullup resistor
  digitalWrite(bigTriggerPin, HIGH); //enable pullup resistor
  
  /* Motor Setup */ 
  pinMode(motorDirPin, OUTPUT);
  pinMode(motorStepPin, OUTPUT);
  
  /* Serial setup */
  //Note that serial commands must be terminated with a newline
  //to be processed. Check this setting in your serial monitor if 
  //serial commands aren't doing anything.
  Serial.begin(57600); //Note that your serial connection must be set to 57600 to work!
}

void loop(){
  //check for LCD updates
  readKey();
  
  //look for triggers on trigger lines
  checkTriggers();
  
  //check serial port for new commands
  readSerial();
	if(serialStrReady){
		processSerial();
	}
}

void checkTriggers(){
		//check low-reward trigger line
    int pushTriggerValue = digitalRead(triggerPin);
    if(pushTriggerValue == HIGH && prevTrigger == LOW){
      bolus(PUSH);
			updateScreen();
    }
    prevTrigger = pushTriggerValue;
    
		//check high-reward trigger line
    int bigTriggerValue = digitalRead(bigTriggerPin);
    if(bigTriggerValue == HIGH && prevBigTrigger == LOW){
			//push big reward amount
			float mLBolusTemp = mLBolus;
			mLBolus = mLBigBolus;
			bolus(PUSH);
			mLBolus = mLBolusTemp;

			updateScreen();
    }
    prevBigTrigger = bigTriggerValue;
}

void readSerial(){
		//pulls in characters from serial port as they arrive
		//builds serialStr and sets ready flag when newline is found
		while (Serial.available()) {
			char inChar = (char)Serial.read(); 
			if (inChar == '\n') {
				serialStrReady = true;
			} 
                        else{
			  serialStr += inChar;
                        }
		}
}

void processSerial(){
	//process serial commands as they are read in
	if(serialStr.equals("+")){
		bolus(PUSH);
		updateScreen();
	}
	else if(serialStr.equals("-")){
		bolus(PULL);
		updateScreen();
	}
        else if(serialStr.toInt() != 0){
          int uLbolus = serialStr.toInt();
          mLBolus = (float)uLbolus / 1000.0;
          updateScreen();
        }
        else{
           Serial.write("Invalid command: ["); 
           char buf[40];
           serialStr.toCharArray(buf, 40);
           Serial.write(buf); 
           Serial.write("]\n"); 
        }
        serialStrReady = false;
	serialStr = "";
}

void bolus(int direction){
        //Move stepper. Will not return until stepper is done moving.        
  
	//change units to steps
	long steps = (mLBolus * ustepsPerML);
	if(direction == PUSH){
                digitalWrite(motorDirPin, HIGH);
		steps = mLBolus * ustepsPerML;
		mLUsed += mLBolus;
	}
	else if(direction == PULL){
                digitalWrite(motorDirPin, LOW);
		if((mLUsed-mLBolus) > 0){
			mLUsed -= mLBolus;
		}
		else{
			mLUsed = 0;
		}
	}	

      float usDelay = SPEED_MICROSECONDS_DELAY; //can go down to 20 or 30
    
      for(long i=0; i < steps; i++){ 
        digitalWrite(motorStepPin, HIGH); 
        delayMicroseconds(usDelay); 
    
        digitalWrite(motorStepPin, LOW); 
        delayMicroseconds(usDelay); 
      } 

}

void readKey(){
	//Some UI niceness here. 
        //When user holds down a key, it will repeat every so often (keyRepeatDelay).
        //But when user presses and releases a key, 
        //the key becomes responsive again after the shorter debounce period (keyDebounce).

	adc_key_in = analogRead(0);
	key = get_key(adc_key_in); // convert into key press

	long currentTime = millis();
        long timeSinceLastPress = (currentTime-lastKeyRepeatAt);
        
        boolean processThisKey = false;
	if (prevKey == key && timeSinceLastPress > keyRepeatDelay){
          processThisKey = true;
        }
        if(prevKey == KEY_NONE && timeSinceLastPress > keyDebounce){
          processThisKey = true;
        }
        if(key == KEY_NONE){
          processThisKey = false;
        }  
        
        prevKey = key;
        
        if(processThisKey){
          doKeyAction(key);
  	  lastKeyRepeatAt = currentTime;
        }
}

void doKeyAction(unsigned int key){
	if(key == KEY_NONE){
        return;
    }

	if(key == KEY_SELECT){
		if(uiState == MAIN){
			uiState = BOLUS_MENU;
		}
		else if(BOLUS_MENU){
			uiState = MAIN;
		}
	}

	if(uiState == MAIN){
		if(key == KEY_LEFT){
			bolus(PULL);
		}
		if(key == KEY_RIGHT){
			bolus(PUSH);
		}
		if(key == KEY_UP){
			mLBolus += mLBolusStep;
		}
		if(key == KEY_DOWN){
			if((mLBolus - mLBolusStep) > 0){
			  mLBolus -= mLBolusStep;
			}
			else{
			  mLBolus = 0;
			}
		}
	}
	else if(uiState == BOLUS_MENU){
		if(key == KEY_LEFT){
			//nothin'
		}
		if(key == KEY_RIGHT){
			//nothin'
		}
		if(key == KEY_UP){
			if(mLBolusStepIdx < mLBolusStepsLength-1){
				mLBolusStepIdx++;
				mLBolusStep = mLBolusSteps[mLBolusStepIdx];
			}
		}
		if(key == KEY_DOWN){
			if(mLBolusStepIdx > 0){
				mLBolusStepIdx -= 1;
				mLBolusStep = mLBolusSteps[mLBolusStepIdx];
			}
		}
	}

	updateScreen();
}

void updateScreen(){
	//build strings for upper and lower lines of screen
	String s1; //upper line
	String s2; //lower line
	
	if(uiState == MAIN){
		s1 = String("Used ") + decToString(mLUsed) + String(" mL");
		s2 = (String("Bolus ") + decToString(mLBolus) + String(" mL"));		
	}
	else if(uiState == BOLUS_MENU){
		s1 = String("Menu> BolusStep");
		s2 = decToString(mLBolusStep);
	}

	//do actual screen update
	lcd.clear();

	s2.toCharArray(charBuf, 16);
	lcd.setCursor(0, 1);  //line=2, x=0
	lcd.print(charBuf);
	
	s1.toCharArray(charBuf, 16);
	lcd.setCursor(0, 0);  //line=1, x=0
	lcd.print(charBuf);
}


// Convert ADC value to key number
int get_key(unsigned int input){
  int k;
  for (k = 0; k < NUM_KEYS; k++){
    if (input < adc_key_val[k]){
      return k;
    }
  }
  if (k >= NUM_KEYS){
    k = KEY_NONE;     // No valid key pressed
  }
  return k;
}

String decToString(float decNumber){
	//not a general use converter! Just good for the numbers we're working with here.
	int wholePart = decNumber; //truncate
	int decPart = round(abs(decNumber*1000)-abs(wholePart*1000)); //3 decimal places
        String strZeros = String("");
        if(decPart < 10){
          strZeros = String("00");
        }  
        else if(decPart < 100){
          strZeros = String("0");
        }
	return String(wholePart) + String('.') + strZeros + String(decPart);
}

You say you're using pins 4 and 5 for motor two. Your code specifies that they're being used by the LCD. That is likely why that motor is moving.

I guess it the line : LiquidCrystal lcd(8, 13, 9, 4, 5, 6, 7);

Should I then remove the 4 and 5 from this line or will this result in other errors?

If you have an LCD, you'll need to specify the pins it's attached to - do you?

Hi,
Welcome to the forum.

Can you please post a copy of your circuit, in CAD or a picture of a hand drawn circuit in jpg, png?

Thanks.. Tom... :slight_smile:

So here is my circuit, I don't really know how to draw the keypad shield since it's on top of the arduino but I have added the connections, given by the manufacturer.
[VMA203: LCD & KEYPAD SHIELD FOR ARDUINO® - LCD1602 – Velleman – Wholesaler and developer of electronics]

Pin Layout: LCD keypad Shield

Analogue 0 UP, DOWN, RIGHT, LEFT, SELECT
Digital 4 DB4
Digital 5 DB5
Digital 6 DB6
Digital 7 DB7
Digital 8 RS
Digital 9 E
Digital 10 Backlight

What I take away from this is that the LCD keypad is connected to pins 5 to 10 and thus I can't use them.

Hi,

What I take away from this is that the LCD keypad is connected to pins 5 to 10 and thus I can't use them.

No, the keypad uses gnd and A0 pins, it uses an analog method of reading all those buttons.


Also you cannot use pin 4 and 5 for the stepper, they are being used in the LCD circuit.

Tom.. :slight_smile:

Okay, thanks for the help.

This means that I can still use 11 to 13 for the second stepper motor?

Hi,

Opolip:
This means that I can still use 11 to 13 for the second stepper motor?

Yes, but why?

The pins you are using now are capable of doing the job.

Sorry they are used by the LCD.

Tom.... :slight_smile:

Yes, as long as you've fixed your LCD declaration - earlier it was using pin 13.

So let's say I use pin 2 and 3 for the first stepper motor board and for the second I use 11 and 12.
These shouldn't interfere with the LCD circuit?

Based on what you've told us, yes, those pins should be fine.

Incidentally, are you seeing anything displayed on the LCD?

Yes, with the LCD keypad shield mounted, I can see the text and maneuver through the menus with the buttons. When I connected pin 2 and 3 to the fist stepper motor I can get it to move now :slight_smile:

Although it moves in the same direction for push and pull :sweat_smile:
I guess it's because I have a different motorboard driver then the person who wrote it(easydriver)

I'd drop what you have and build a very basic sketch that just moves the stepper to & fro. Best to prove you can control it without all that other stuff complicating matters. I'd remove the LCD shield too.

I got it all to work :smiley:

The mistake I made was that I misinterpreted the shield overlay. As in I connected the motor board to pin 1 and 2 instead of 2 and 3.

Now I can move towards editing the Push Pull function towards my needs(lab experiment) and add the second stepper motor.

Any tips are welcome and thanks for your help.