// This arduino code is used to run the syringe pump module // The communication is based on https://forum.arduino.cc/t/serial-input-basics-updated/382007 // The stepper is driven uding the AccelStepper library, which is documented here //https://hackaday.io/project/183279-accelstepper-the-missing-manual/details //Librabies #include // Pin definitions const int dirPin = 2; // pin used to set the direction the stepper motor should rotate const int stepPin = 3; // pin used to make one step in the specified direction #define max_fill_button 4 //pin of button which is pressed, when the syringe is full #define min_fill_button 5 //pin of button which is pressed, when the syringe is empty // Define motor interface type #define motorInterfaceType 1 // Global variables int last_time = 0; //last time measurement data was sent by the arduino in s int time = 0; //actual time int timeinterval = 1; //time interval in which measurements should be made and their data sent const byte numChars = 32; // number of characters in the message received from the PC. Can be much bigger, because the buffer is cleared before it is full char receivedChars[numChars]; // Array for received characters char tempChars[numChars]; // temporary array for use when parsing to not change the original data receivedChars const int stepsPerRevolution = 200; // change this to fit the number of steps per revolution of the stepper motor float mm_per_ml = 42.0/5.0; //mm traveled per volume of syringe float steps_per_mm =200/(10*3.1415); //conversion factor from steps to distance in mm long set_steps=0; //by the set distance specified number of steps, which should be traveled // variables to hold the parsed data // The received data has the format "" with the name of the arduino and the actor, whichs value should be changed to the actorvalue char Arduino[numChars] = {0}; // Variable for the name of the arduino, whichs actors should act char Actor[numChars] = {0}; // Variable for the name of the actor, whichs value should be changed float Actorvalue = 0; // Variable for the value the actor should be changed to boolean NewInstructions = false; // Indicates if message is completely received // Creates an instance AccelStepper myStepper(motorInterfaceType, stepPin, dirPin); // Setup of pin's and communication void setup() { Serial.begin(9600); myStepper.setMaxSpeed(1000); //sets the maximum speed in steps/s. Speeds over 1000 may be unreliable. myStepper.setAcceleration(50); //sets the acceleration in steps/s^2 myStepper.setSpeed(200); //sets the speed in steps/s. Speeds over 1000 may be unreliable. This speed is only used when calling runSpeed() and not when calling run() myStepper.setCurrentPosition(0); //makes the current motor position and the target position equal to the value specified pinMode(max_fill_button, INPUT_PULLUP); //limit switch is pulled up to 5 V and pulled down to ground, when the switch is pressed pinMode(min_fill_button, INPUT_PULLUP); //limit switch is pulled up to 5 V and pulled down to ground, when the switch is pressed } // Main loop running continuously void loop() { ReceiveInstructions(); // the ReceiveInstructions function is defined below and receives the instructions from the PC to the arduino using serial communication if (NewInstructions == true) { //if new instructions have been received ExecuteInstructions(); // execute the instructions by setting the values of the actors. The ExecuteInstructions function is defined below NewInstructions = false; //set the variable to show, that the instructions have been executed } time = millis()/1000; // calculate actual time in s if((time-last_time)>=timeinterval){ // check if it is time to send new measurement data SendData(); // send the data using the SendData function defined below last_time = time; // remember the last time data has been sent } } void ReceiveInstructions() { //receives the instructions from the PC to the arduino using serial communication static boolean recvInProgress = false; //indicates if startMarker has been received and message is collected static byte ndx = 0; //index for the char array char startMarker = '<'; //startMarker for the array char endMarker = '>'; //endMarker for the array "New Line" in arduino IDE serial monitor puts a '\n' at the end of each message as endMarker char rc; // single received character which is put into the receivedChars array while (Serial.available() > 0 && NewInstructions == false) { //if new data is available and the message is not completed rc = Serial.read(); //read single character from buffer if (recvInProgress == true) { //if the start marker has been received and the message is now collected if (rc != endMarker) { //if the received character is not the endMarker receivedChars[ndx] = rc; //attach the character to the array ndx++; //increase index of the received character array if (ndx >= numChars) { //check that not too many characters have been received ndx = numChars - 1; } } else { //if the received character is the endMarker receivedChars[ndx] = '\0'; // terminate the string recvInProgress = false; //set the indicator that the message is completely received ndx = 0; //reset the array index NewInstructions = true; //set the indicator that the data can be displayed } } else if (rc == startMarker) { //if the received character is the startMarker recvInProgress = true; //set the indicator that the characters should be collected to the message } } } void ExecuteInstructions() { //process the received data and execute the instructions // Parse the received Data into it's components char * strtokIndx; // char * is a pointer (the memory adress for char data) this is used by strtok() as an index strcpy(tempChars, receivedChars); //copies the string in receivedChars into tempChars // this temporary copy is necessary to protect the original data // because strtok() used in parseData() replaces the commas with \0 strtokIndx = strtok(tempChars,","); // strtok splits a string (tempChars) into pieces (tokens) using a delimiter (,). it replaces the delimiter with \0 (NULL) and returns the pointer to the next token each time it is called strcpy(Arduino, strtokIndx); // copy it to messageFromPC strtokIndx = strtok(NULL, ","); // this continues where the previous call left off strcpy(Actor, strtokIndx); // copy it to messageFromPC strtokIndx = strtok(NULL, ","); // this continues where the previous call left off Actorvalue = atof(strtokIndx); // convert this part to a float number // Execute the received instructions if (strcmp(Arduino,"Ard_Syringe")==0){ if (strcmp(Actor,"Syringe")==0){ set_steps=steps_per_mm*mm_per_ml*Actorvalue; myStepper.moveTo(myStepper.currentPosition()+set_steps); //moveTo set the desired absolute target position to the current position (returned by currentPosition()) plus the number of steps while((myStepper.distanceToGo()!=0) && (((digitalRead(max_fill_button)==HIGH) && (set_steps > 0)) || ((digitalRead(min_fill_button)==HIGH) && (set_steps < 0) ))){ //distanceToGo returns the distance from the current position to the target position in steps. If this distance is not zero and none of the limit switches is pressed, the motor should run myStepper.run(); //make one step to the target position } myStepper.setCurrentPosition(0); //makes the current motor position and the target position equal to the value specified and therefore prevents the motor from pushing against the limit switches set_steps=-set_steps; myStepper.moveTo(myStepper.currentPosition()+set_steps); //moveTo set the desired absolute target position to the current position (returned by currentPosition()) plus the number of steps while((myStepper.distanceToGo()!=0) && (((digitalRead(max_fill_button)==HIGH) && (set_steps > 0)) || ((digitalRead(min_fill_button)==HIGH) && (set_steps < 0) ))){ //distanceToGo returns the distance from the current position to the target position in steps. If this distance is not zero and none of the limit switches is pressed, the motor should run myStepper.run(); //make one step to the target position } myStepper.setCurrentPosition(0); //makes the current motor position and the target position equal to the value specified and therefore prevents the motor from pushing against the limit switches } } } void SendData() { //sent the acutal timestamp and the measurement values from the arduino to the pc Serial.print("max_fill_button"); Serial.print(','); Serial.print(digitalRead(max_fill_button)); Serial.print(','); Serial.print("min_fill_button"); Serial.print(','); Serial.println(digitalRead(min_fill_button)); }