Hi Naroom,
Thanks for giving suggestion on hackaday comments section to add the flow rate control via LCD keypad shield. Following your advice I made the changes as I copied below. Could you please have a look at my code. I did not build my pump yet, so I cannot test my code on syringe pump yet. All I am doing is connecting two LED to pin 2 and 3 to see the response of inputtuing values through the keypad shield. Many thanks for your great help.
--------------------modified code-------------------------------
// Controls a stepper motor via an LCD keypad shield.
// Accepts triggers and serial commands.
include <LiquidCrystal.h>
include <LCDKeypad.h>
include <AccelStepper.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};
int flowrateStepsIdx = 3;
float Flowrate = mLBolusSteps[flowrateStepsIdx]; //can go down to 20 or 30
/* -- Default Parameters -- */
int motorSpeed = 15000; //maximum steps per second
int motorAccel = 150000; //steps/second/second to accelerate
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 -- */
AccelStepper stepper(1, motorStepPin, motorDirPin); //the "1" tells it we are using a driver
LiquidCrystal lcd(8, 13, 9, 4, 5, 6, 7);
void setup(){
/* LCD setup */
lcd.begin(16, 2);
lcd.clear();
lcd.print("SyringePump v1.0");
/* Triggering setup */
pinMode(triggerPin, INPUT);
pinMode(bigTriggerPin, INPUT);
digitalWrite(triggerPin, HIGH); //enable pullup resistor
digitalWrite(bigTriggerPin, HIGH); //enable pullup resistor
/* 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);
Serial.println("steps");
Serial.println(steps, DEC);
Serial.println("mLBolus");
Serial.println(mLBolus, DEC);
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;
}
}
int usDelay=1.0e6/float(Flowrate*2.0*ustepsPerML);
//to see the above calculation of usDelay, look in to the below python link
//http://www.codeskulptor.org/#user39_GgRMbeBVUD_4.py
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){
if((Flowrate - mLBolusStep) > 0){
Flowrate -= mLBolusStep;
}
else{
Flowrate = 0;
}
}
if(key == KEY_RIGHT){
Flowrate += mLBolusStep;
}
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("BolusStep ")+decToString(mLBolusStep);
s2 = String("Flow rate ")+decToString(Flowrate);
}
//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);
}
Hi Naroom,
Thanks for giving suggestion on hackaday comments section to add the flow rate control via LCD keypad shield. Following your advice I made the changes as I copied below. Could you please have a look at my code. I did not build my pump yet, so I cannot test my code on syringe pump yet. All I am doing is connecting two LED to pin 2 and 3 to see the response of inputtuing values through the keypad shield. Many thanks for your great help.
--------------------modified code-------------------------------
// Controls a stepper motor via an LCD keypad shield.
// Accepts triggers and serial commands.
include <LiquidCrystal.h>
include <LCDKeypad.h>
include <AccelStepper.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};
int flowrateStepsIdx = 3;
float Flowrate = mLBolusSteps[flowrateStepsIdx]; //can go down to 20 or 30
/* -- Default Parameters -- */
int motorSpeed = 15000; //maximum steps per second
int motorAccel = 150000; //steps/second/second to accelerate
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 -- */
AccelStepper stepper(1, motorStepPin, motorDirPin); //the "1" tells it we are using a driver
LiquidCrystal lcd(8, 13, 9, 4, 5, 6, 7);
void setup(){
/* LCD setup */
lcd.begin(16, 2);
lcd.clear();
lcd.print("SyringePump v1.0");
/* Triggering setup */
pinMode(triggerPin, INPUT);
pinMode(bigTriggerPin, INPUT);
digitalWrite(triggerPin, HIGH); //enable pullup resistor
digitalWrite(bigTriggerPin, HIGH); //enable pullup resistor
/* 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;
}
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.
//http://www.codeskulptor.org/#user39_GgRMbeBVUD_4.py
}
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).
}
void doKeyAction(unsigned int key){
if(key == KEY_NONE){
return;
}
}
void updateScreen(){
//build strings for upper and lower lines of screen
String s1; //upper line
String s2; //lower line
}
// 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);
}