Andrea FiaschiFederico Micheletti
Published © GPL3+

UDOO Neo Automated Hematoogy Laboratory Tubes Unscramble

A device that reads a bar code placed on the tube and, based on it, moving two step motors to place the tube in the correct position.

IntermediateFull instructions providedOver 8 days2,581
UDOO Neo Automated Hematoogy Laboratory Tubes Unscramble

Things used in this project

Hardware components

UDOO NEO
UDOO NEO
×1
barcode scanner
×1
Servos (Tower Pro MG996R)
×1
step motor NEMA 17
×1
step motor M35SP-11NK
×1
pololu A4988 stepper driver
×2
Male/Female Jumper Wires
Male/Female Jumper Wires
×6
Male/Male Jumper Wires
×7
Male Header 80 Position 2 Row (0.1")
Male Header 80 Position 2 Row (0.1")
×1
Female Header 8 Position 1 Row (0.1")
Female Header 8 Position 1 Row (0.1")
×4
Pushbutton switch 12mm
SparkFun Pushbutton switch 12mm
×3
5 mm LED: Red
5 mm LED: Red
×1
5 mm LED: Green
5 mm LED: Green
×1
5 mm LED: Yellow
5 mm LED: Yellow
×1
12V power supply
×1

Software apps and online services

Arduino IDE
Arduino IDE
Altium Designer 16
CircuitCam

Hand tools and fabrication machines

LPKF E34 ProtoMat

Story

Read more

Schematics

Personal UDOO shield - Schematic

This is the Schematic of our personal shield. Here you can see, named as "JP" the headers of the Pololu driver while "J" stands for the headers of the other components we used. As you can see the "blue" words just represent the names of the NET, while the "red" words represent the effective connections between two or more parts, even if the wire is not represented.

Personal UDOO shield - Circuit routing

This is the circuit routing of our shield. In "red", you can see the routing of the top side, in "blue" the routing of the bottom side. The "light blue circles" are some holes used to attach the UDOO board with our shield on the printer casing.

Personal UDOO shield - LPKF Prototype Generator

This is the output file of the program "Circuit Cam" used to convert the circuit routing in a "gerber file", so the LPKF machine can read this file and carve the shield on a sheet of copper, to obtain our shield.

Code

Code for ARM Cortex M4 - ARM Cortex A9 communication

C/C++
This code is used to send messages from A9 side to M4 side. After this program is launched, the A9 processor receives the input values from the bar code reader and then it sends them through the internal serial "/dev/ttyMCC" to the M4 co-processor. Note that a terminal is needed to write the values that have to be sent through the serial, just use the normal GNU/LINUX bash, like "LXTerminal" already installed on the desktop of the UDOO NEO.
#include <termios.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>



int set_interface_attribs (int fd, int speed, int parity)
{
        struct termios tty;
        memset (&tty, 0, sizeof tty);
        if (tcgetattr (fd, &tty) != 0)
        {
                printf ("error %d from tcgetattr", errno);
                return -1;
        }

        cfsetospeed (&tty, speed);
        cfsetispeed (&tty, speed);

        tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;     // 8-bit chars
        // disable IGNBRK for mismatched speed tests; otherwise receive break
        // as \000 chars
        tty.c_iflag &= ~IGNBRK;         // ignore break signal
        tty.c_lflag = 0;                // no signaling chars, no echo,
                                        // no canonical processing
        tty.c_oflag = 0;                // no remapping, no delays
        tty.c_cc[VMIN]  = 0;            // read doesn't block
        tty.c_cc[VTIME] = 5;            // 0.5 seconds read timeout

        tty.c_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl

        tty.c_cflag |= (CLOCAL | CREAD);// ignore modem controls,
                                        // enable reading
        tty.c_cflag &= ~(PARENB | PARODD);      // shut off parity
        tty.c_cflag |= parity;
        tty.c_cflag &= ~CSTOPB;
        tty.c_cflag &= ~CRTSCTS;

        if (tcsetattr (fd, TCSANOW, &tty) != 0)
        {
                printf ("error %d from tcsetattr", errno);
                return -1;
        }
        return 0;
}

void set_blocking (int fd, int should_block)
{
        struct termios tty;
        memset (&tty, 0, sizeof tty);
        if (tcgetattr (fd, &tty) != 0)
        {
                printf ("error %d from tggetattr", errno);
                return;
        }

        tty.c_cc[VMIN]  = should_block ? 1 : 0;
        tty.c_cc[VTIME] = 5;            // 0.5 seconds read timeout

        if (tcsetattr (fd, TCSANOW, &tty) != 0)
                printf ("error %d setting term attributes", errno);
}

int main(void) {
	char *portname = "/dev/ttyMCC";
	
	int fd = open (portname, O_RDWR | O_NOCTTY | O_SYNC);
	if (fd < 0)
	{
			printf("error %d opening %s: %s", errno, portname, strerror (errno));
			return 0;
	}

	set_interface_attribs (fd, B115200, 0);  // set speed to 115,200 bps, 8n1 (no parity)
	set_blocking (fd, 0);                // set no blocking
	
	
	char numberX;
	char numberY;
	char nl;
	while(1){
			
		scanf("%c%c%c",&numberX,&numberY,&nl);
		write(fd, &numberX, 1);           		// send the number of the test_tube through the serial to the M4 processor
		write(fd, &numberY, 1); 
		sleep(1);					// wait 1 second
		
	}
	return 0;
}

ARM Cortex M4 Code

Arduino
This is the code used to program the "ARM Cortex M4" real time co-processor, based on Arduino IDE.
#include <Servo.h> 

Servo myservo;                // create the object "servo" to control  
                              // the servo motor.
int pos = 0;                  // variable used to store the servo position.
int step_motorbig = 3;        // connect the "step" pin of the motor_big on pin 3 of the UDOO NEO.
int dir_motorbig = 2;         // connect the pin "direction" of the motor_big on pin 2 of the UDOO NEO.
int step_motorsmall = 13;     // connect the pin "step" of the motor_big on pin 13 of the UDOO NEO.
int dir_motorsmall = 12;      // connect the pin "direction" of the motor_big on pin 12 of the UDOO NEO.
int enable_steppers = 8 ;     // connect the pin "enable" of the two motors on pin 8 of the UDOO NEO.
int flag = 0;                 // create a flag to control the flow of the instructions.
int test_tube_numberX = 0;    // Contains the number of the test tube for the X axis (from 0 to 9).
int test_tube_numberY = 0;    // Contains the number of the test tube for the Y axis (from 0 to 9).
const int defaultX = 495;     // used to move the motor_big the necessary steps to arrive at the "zero" position on X axis.
const int defaultY = 440;     // used to move the motor_small the necessary steps to arrive at the "zero" position on Y axis.
const int StepX = 115;        // constant that shows the steps between 2 close holes for the motor_big.
const int StepY = 792;        // constant that shows the steps between 2 close holes for the motor_small.
int Change_deck = 0;          // variable that allows to change "Deck".

void LedGreenOn(){            // function that turns on the green LED.
  digitalWrite(9, HIGH);
}

void LedGreenOff(){           // function that turns off the green LED.
  digitalWrite(9, LOW);
}

void LedYellowOn(){           // function that turns on the yellow LED.
digitalWrite(10, HIGH);
}

void LedYellowblink2(){       // function that blinks the yellow LED 2 times.
  for (int i=0;i<2;i++)
  {
    digitalWrite(10, HIGH);
    delay(100);
    digitalWrite(10, LOW);
    delay(100);
  }
}

void LedYellowblink1(){       // function that blinks the yellow LED 1 time.
  for (int i=0;i<1;i++)
  {
    digitalWrite(10, HIGH);
    delay(100);
    digitalWrite(10, LOW);
    delay(100);
  }
}

void LedYellowOff(){          // function that turns off the yellow LED.
  digitalWrite(10, LOW);
}

void LedRedOn(){              // function that turns on the red LED.
  digitalWrite(11, HIGH);
}

void LedRedOff(){             // function that turns off the red LED.
  digitalWrite(11, LOW);
}

void Start(){                 // function that establishes the start procedure.
  LedGreenOff();
  LedRedOn();
  LedYellowOff();
  delay(45000);
  LedRedOff();
  LedGreenOn();
  digitalWrite(dir_motorsmall, LOW);
}

void StepperSmall_ReturnInitPos(){         // function that returns the motor_small 
                                           // at the "origin".
  delay(1000);
  LedGreenOff();
  LedRedOff();
  LedYellowOn();
  CloseServo();
  digitalWrite(enable_steppers, LOW);     // "enable" on, so the relative driver is on (pololu A4988). 
  if (digitalRead(4)!= HIGH){             // move the motor if the end stroke button is not pressed. 
    LedGreenOff();
    LedRedOff();
    LedYellowOn();
    if (dir_motorsmall == LOW){
      digitalWrite(dir_motorsmall, HIGH);  
    }
    else{
       digitalWrite(dir_motorsmall, LOW);
    }
    
    while (digitalRead(4) != HIGH){       // move the motor untill the end stroke button is pressed.
      
      digitalWrite(step_motorsmall, HIGH);   
      delayMicroseconds(650);        
      digitalWrite(step_motorsmall, LOW); 
      delayMicroseconds(650);
    }
  }
}

void StepperBig_ReturnInitPos(){          // function that returns the motor_small 
                                          // at the "origin".
  
  if (digitalRead(5)!= HIGH){             // move the motor if the end stroke button is not pressed.
    LedGreenOff();
    LedRedOff();
    LedYellowOn();
    digitalWrite(dir_motorbig, LOW);   
    
    while (digitalRead(5) != HIGH){       // move the motor untill the end stroke button is pressed.
      
      digitalWrite(step_motorbig, HIGH);   
      delayMicroseconds(900);        
      digitalWrite(step_motorbig, LOW); 
      delayMicroseconds(900);
          
    }
  }
  LedGreenOn();
  LedYellowOff();
  LedRedOff();
  digitalWrite(enable_steppers, HIGH);    
  flag = 0;
}

void OpenServo(){                         // function that rotate the servo motor to open the hole
                                          // that contains the test tube.
  delay(500);

  for(pos = 0; pos <= 100; pos += 50)     
  {                                       
    myservo.write(pos);                   // servo motor goes to the "pos" position.
    delay(15);                            
  }

}

void CloseServo(){                        // function that rotate the servo motor to close the hole
                                          // that contains the test tube.  
  for(pos = 100; pos>=0; pos-= 50)       
  {                                
    myservo.write(pos);                   // servo motor goes to the "pos" position.
    delay(15);                       
  } 
  
}

void MoveMotors(){                        // function that moves the two step motors to the position where the test tube  
                                          // will be released.
   
  digitalWrite(dir_motorbig, HIGH);       // set the "direction". 
  digitalWrite(dir_motorsmall, HIGH);     
  delay(2000);
  digitalWrite(enable_steppers, LOW);     
  LedGreenOff();
  LedRedOff();
  LedYellowOn();
  test_tube_numberY=test_tube_numberY+Change_deck;            
  if (test_tube_numberY>4 || test_tube_numberY<0 || test_tube_numberX>9 || test_tube_numberX<0 ){
    // check if the read code is right (0<X<9; 0<Y<4). If one of these values is not respected,
    // the test tube will be released in the "waste area".  
   
    for (int i = 0; i<200; i++){            // go to the "waste area".
      digitalWrite(step_motorbig, HIGH);   
      delayMicroseconds(900);        
      digitalWrite(step_motorbig, LOW);
      delayMicroseconds(900);
    }
    for (int i = 0; i<2500; i++){            // go to the "waste area".
      digitalWrite(step_motorsmall, HIGH);   
      delayMicroseconds(650);        
      digitalWrite(step_motorsmall, LOW); 
      delayMicroseconds(650);
    }
    
  }
  
  else{
  int A = defaultX+(test_tube_numberX*StepX);  // "A" contains the X position.
  for (int i = 0; i<A; i++){
    digitalWrite(step_motorbig, HIGH);   
    delayMicroseconds(900);        
    digitalWrite(step_motorbig, LOW);
    delayMicroseconds(900);
  }

  int B = defaultY+(test_tube_numberY*StepY);  // "B" contains the Y position.
  for (int i = 0; i<B; i++){
    digitalWrite(step_motorsmall, HIGH);   
    delayMicroseconds(650);        
    digitalWrite(step_motorsmall, LOW); 
    delayMicroseconds(650);
  }
  }
  digitalWrite(enable_steppers,HIGH);
  delay(500);
  OpenServo();
  delay(2000);
  digitalWrite(enable_steppers,LOW);
 
}


void ReadCode(){                                // function that read the two values insert from keyboard.
  if(Serial.available() >= 2){                  // check if the are 2 or more bytes in the receive buffer of the UART.             
     test_tube_numberY = (int ) Serial.read();  // read the first byte. 
     test_tube_numberX = (int ) Serial.read();  // read the second byte.
     test_tube_numberX -= 48;
     test_tube_numberY -= 48;
     delay(200);
     Serial.print(test_tube_numberY );
     Serial.print("  ");
     Serial.println(test_tube_numberX);
     while(Serial.available() > 0){
        Serial.read();
     }
      flag = 1; 
      
  }
  delay(50);
}

void Deck(){                                // function that choose the "Deck": "Deck1" for test tubes from 0 to 49,
                                            // "Deck2" for test tubes from 50 to 99.
     delay(500);
    if (digitalRead(7) == HIGH){
      if (Change_deck == 0){
      LedYellowblink2();
      Change_deck =- 5;
      LedYellowOff();
      }
      else {
      LedYellowblink1();
      Change_deck = 0;
      LedYellowOff();
      }
    }
    
}

void setup() {
  
  delay(20000);
  pinMode(9, OUTPUT);               // pin 9 for green LED.
  pinMode(11, OUTPUT);              // pin 11 for red LED.
  pinMode(10, OUTPUT);              // pin 10 for yellow LED.
  pinMode(5, INPUT);                // pin 5 for the end stroke button of the motor_big.
  pinMode(4, INPUT);                // pin 4 for the end stroke button of the motor_small.
  pinMode(7,INPUT);                 // pin 7 for the button that choose the "Deck".
  pinMode(step_motorbig, OUTPUT);
  pinMode(dir_motorbig, OUTPUT);  
  pinMode(step_motorsmall, OUTPUT);
  pinMode(dir_motorsmall, OUTPUT);
  pinMode(enable_steppers, OUTPUT);
  Start();
  myservo.attach(6);                 // connect the "servo" on pin 6.
  Serial.begin(115200);              // active the serial with baud rate of 115200 byte/s.
  delay(500);
  StepperSmall_ReturnInitPos();
  StepperBig_ReturnInitPos();
  
}

void loop() {
  
  while ( flag == 0 ){
    ReadCode();
    Deck();
  }
  
  MoveMotors();
  StepperSmall_ReturnInitPos();
  StepperBig_ReturnInitPos();

}

Credits

Andrea Fiaschi

Andrea Fiaschi

1 project • 4 followers
I was born in 1993, I'm studying at University of Siena at the Faculty of Information Engineering and Computer science, specialized in Electronics.
Federico Micheletti

Federico Micheletti

1 project • 4 followers
I was born in 1993, I'm studying at the University of Study of Siena at the Faculty of Information Engineering and Computer science specialized in Electronics

Comments