Project tutorial

Color Sorting Machine © GPL3+

A unique spin on a classic project!

  • 6,477 views
  • 10 comments
  • 66 respects

Components and supplies

Ard nano
Arduino Nano R3
×1
TCS 34725 RGB sensor
×1
LM2596 buck converter
×1
MG-90S servos
×2
MR105ZZ ball bearing (5 x 10 x 4 mm)
×3
608RS or 608ZZ ball bearing (8 x 22 x 7mm)
×1
6003RS ball bearing (17x35x10mm)
×1
M3*15mm countersunk screw
×1
M2x10 self taping screws
×20
RGB led (common Anode(+))
×1
220 ohm resistors
×3
wires
×1
2.1x5.5mm jack plug
×1
heatshrink tubing
×1
dupont connectors
×1
9V or 12v power supply (1 amp minimum)
×1
5X 22mm(OD)x100mm test tubes (from Danmu Art)
×1
Skittles!!!!!!!!!!
×1

Necessary tools and machines

3D printer
screwdriver
wire stripper
pliers
soldering iron
multimeter
crimping tool

About this project


INTRO.

The funny story is that this project was never meant to be.

About 3.5 years ago I started design a much funkier color sorting machine and like many of my projects it ended up half finished collecting dust on the window ledge (I am sure nobody else can relate to this haha).

Long story short, a few months ago I decided to pick up this old project back up. The first thing I needed to work out was how to collect Skittles from a reservoir and check their color quite reliably while keeping the system as compact as I could (which I failed to achieve the first time). I managed talking myself into designing a simpler color sorting machine just to get the design of the collection system right. I foolishly thought It would be a 2 weekends job, nice, easy and quick... Oh boy!!! was I wrong...

I took me 2 completely different design and many tweaked prototypes to get something that worked in a acceptable manner (I am still experiencing some pick up issues from time to time).

Even though it's not perfect I am quite pleased with the result.

HOW DOES IT WORKS.

The machine can be split in 2 main stages.

At the top you can find the reservoir for the unsorted Skittles. At the bottom of this reservoir is the feeding mechanism responsible for collecting Skittles and positioning them in front of the color sensor where their color will be checked.

The feeding mechanism include an arm that shakes the content of the reservoir to make sure the Skittles flow nicely through the orifice of the feeding mechanism without jamming inside the reservoir ( unfortunately it still happen sometime ).

Once the color of the Skittle has been found, the second stage of the machine comes into action. The test tubes holder align the right tube in front of the feeding mechanism for the Skittle to be dropped in.

The cycle can then be repeated until the reservoir is empty.

WANT TO MAKE YOUR OWN???

..................https://www.littlefrenchkev.com/colour-sorting-machine.......................

ASSEMBLY.

Once you have downloaded and printed the parts you should end up with something like this :

Note that the front shell comes in 2 versions. No real difference they just look different.

The rest of the parts needed.

Here is a video of the assembly process. I even tried to be an artist by making an intro with slowmo 3D printer shots!!!

INITIAL SETUP.

The first thing the machine does when you turn the power on is to move both servos to their initial position. When you start the machine for the first time you need to make sure the gear/shaft collecting the Skittles is at its top position and that the tube holder is align with the feeding mechanism.

TROUBLESHOOTING.

If you decide to make this sorting machine yourself you might run into a few common issues. here is how you can resolve them.

LED blinking red - no starting sequence:

After moving to it's initial position the machine should go through its starting sequence. If it doesn't and instead the LED blinks red, it means you have some issues with your RBG sensor not being found. There is either a problem with your wiring or the sensor being faulty.

Tubes don't align properly:

Cause 1

During the starting sequence the tubes holder will move 5 times, once for each color. You might notice that the tubes don't align too well with the feeding mechanism. This could be due to the servos no being super precise or to some variations in the print dimensions.

This can be corrected through software by modifying the colour_angle variable array. Each values in this array represent the servo position for each color, by changing those values you can fine tune the where the tube is going to stop for each colors.

Cause 2

Due to printing tolerances you might also get some backlash in the tubes holder gears. Depending which way the tubes are spinning their inertia can make them overshoot their stopping position. This can also be corrected through software by modifying the forward and backwash anti backlash variable.Those value will be added to or removed from the color angle values depending which way the holder is spinning to compensate for this backlash.

Color mixing:

If you experience some color mixing, you will have to modify colour_limit_values arrays. Each values in those array represent the minimum and maximum values for Red, Green and Blue. To have a better idea of what the values should be, load the machine with only one color, plug the machine to your computer and open the serial monitor. Every time a Skittle is being checked you should get its read RGB values and what color the program think it is. This should give you a better idea of what sort of values should associated with each color.

A FEW COOL FEATURES.

(well I think they are anyway!)

The RGB LED:

There is a RGB LED fitted to the base of the machine. During the sorting process it changes color to match the color of the Skittle being dropped. It goes through every single Skittle colors continuously once the reservoir is empty.

It will also bling red on start up if an issue is detected with the sensor.

Anti-backlash compensation:

The tubes holder is spun using a couple of spur gears. Due to printing tolerance is is very difficult to completely get rid of any play in the gears. This means the tube holder tend to overshoot its ideal position. The code allows to compensate for this by stopping the holder earlier by a certain amount depending on what direction the holder is spinning.

Delay with extra:

The code include a "set_led(ms, color)" function. It act exactly like the "delay(ms)" function but allows the LED to smoothly shift to its new color while the delay happen. It work a treat to change the colors of the LED while the tube holder spin to its position !

EXTRA NOTES.

Note 1: I printed my machine in black, this will have an impact on what color values the sensor will read especially when no Skittles is collected by the feeding mechanism.

Note 2: After going through the sorting process several time the Skittles colors seem to become more difficult to read reliably. I assume this is due to them losing their shine after rubbing inside the reservoir to many times or them going a little stickier when handled by hand (sounds gross!). When calibrating the color values try to use 'fresh' Skittles.

Note 3: Please make sure you take all precaution necessary when handling food. This include making sure you use food safe printing filament.

Note 4: I try to post some work in progress and what project I am up to on Instagram. If it's your thing you can have a look here : https://www.instagram.com/little_french_kev/


Code

LFK_sorting_machineC/C++
Here is the Arduino code for the sorting machine .
#include <Wire.h>
#include "Adafruit_TCS34725.h"
#include<Servo.h>

/* Based on example code for the Adafruit TCS34725 breakout library */


//=======================Variables related to RGB sensor=========================


Adafruit_TCS34725 tcs = Adafruit_TCS34725(TCS34725_INTEGRATIONTIME_24MS, TCS34725_GAIN_1X);  // setup RGB sensor

int colour = 0;//0-black, 1-yelow, 2-green, 3-orange, 4-red, 5-purple
int previous_colour = colour;
int black_count = 0;  // count of empty/unknown color

int start_measurment_angle = 88; //servo position to align skittle in front of sensor
int measurment_sweep = 2; //number of measure taken . used to average error
int measurment_angle_increment = 4; //angle increment between measurments

//limit values[] = {min_red_values, max_red_values, min_green_values, max_green_values, min_blue_values, max_blue_values}
uint16_t black_limit_values[] = {0, 200, 0, 200, 0, 200};
uint16_t yellow_limit_values[] = {850, 2200, 800, 2200, 400, 1100};
uint16_t green_limit_values[] = {400, 800, 700, 1400, 300, 700};
uint16_t orange_limit_values[] = {650, 1800, 300, 850, 210, 600};
uint16_t red_limit_values[] = {400, 950, 150, 450, 150, 400};
uint16_t purple_limit_values[] = {150, 400, 150, 450, 150, 500};


//=======================Variables related to RGB led=========================

byte R_pin = 5;
byte G_pin = 6;
byte B_pin = 11;

int current_red_value = 255;
int current_green_value = 255;
int current_blue_value = 255;

const int orange[] = {0, 230, 255};
const int red[] = {0, 255, 255};
const int green[] = {255, 0, 255};
const int yellow[] = {0, 170, 255};
const int purple[] = {0, 255, 40};


//======================= Variables related to feeder servos =========================

int release_delay = 220;
const int colour_angles[] = {10, 46, 82, 118, 154};//{10, 46, 82, 118, 154};
//int holder_offset = 0;  // offset adjustment for tubes holder . to be kept between 0 and 36
const int backward_anti_backlash = 2;
const int forward_anti_backlash = 2;
Servo feeder_servo; //declare feeder servo
Servo holder_servo; //declare tubes holder servo

//=================================== SETUP =========================================

void setup(void) {

  Serial.begin(9600);

  pinMode(R_pin, OUTPUT);   //set LED pin
  pinMode(G_pin, OUTPUT);
  pinMode(B_pin, OUTPUT);

  analogWrite(R_pin, 255);  //turn off LED
  analogWrite(G_pin, 255);
  analogWrite(B_pin, 255);

  feeder_servo.attach(3);  //attach feeder servo to pin 3
  holder_servo.attach(2);   //attach holder servo to pin 2
  feeder_servo.write(0);
  holder_servo.write(colour_angles[0]);

  int white[] = {0, 0, 0}; //local white values variable
  int black[] = {255, 255, 255}; //local black values variable
  set_led(1000, white);   // turn LED on (white)

  if (tcs.begin()) {    //check if sensor found
    Serial.println("Found sensor");
    starting_sequence();
  } else {
    Serial.println("No TCS34725 found ... check your connections");
    while (1)  //fast red blinking if sensor not found
    {
      set_led(300, red);
      set_led(300, black);
    }
  }
}

//=================================== LOOP =========================================

void loop(void) {

  if (black_count < 10)
  {
    feeder_servo.write(0); // set servo position to the top for skittle collection
    delay(450);  // delay to let the feeder servo enough time to get in position
    previous_colour = colour;
    get_colour(); // read skittle colour

    if (colour == 0) {
      black_count ++; //if no colour found, increment black count
      shake();//gives a bit more shake to the reservoir even if no skittle is there
    }
    else if (colour == 6) {
      black_count ++; //if colour unknown, increment black count - no skittle release
    }
    else { //if colour found
      move_holder(); // move tube holder
      release_skittle(); // release skittle
      black_count = 0;
    }
  }

  else {
    end_loop(1000);
  }
}

//=================================== GET COLOUR =========================================

void get_colour() {

  uint16_t r, g, b, c;//, colorTemp, lux;
  uint16_t total_r = 0;
  uint16_t total_g = 0;
  uint16_t total_b = 0;

  feeder_servo.write(start_measurment_angle); //move to start measurment angle
  delay(200);

  Serial.println("----------------");
  for (int i = 0; i <= measurment_sweep; i++) //loop for each measure
  {
    tcs.getRawData(&r, &g, &b, &c);    //get color data

    feeder_servo.write(start_measurment_angle + i * measurment_angle_increment); //increment servo angle for next measure
    total_r += r; //add red value to total red value
    total_g += g; //add green value to total green value
    total_b += b; //add blue value to total blue value
    delay(15);
  }


  total_r /= measurment_sweep;  //average values accross all measurments
  total_g /= measurment_sweep;
  total_b /= measurment_sweep;

  Serial.print(total_r); Serial.print(" ");
  Serial.print(total_g); Serial.print(" ");
  Serial.print(total_b); Serial.print(" ");
  Serial.println(" ");

  //compare values to determine the color .
  if ((total_r < black_limit_values[1]) &&    //check for black
      (total_g < black_limit_values[3]) &&
      (total_b < black_limit_values[5]))
  { Serial.println("black");
    colour = 0;
  }
  else if ((total_r >= yellow_limit_values[0]) &&   //check for yellow
           (total_r < yellow_limit_values[1]) &&
           (total_g >= yellow_limit_values[2]) &&
           (total_g < yellow_limit_values[3]) &&
           (total_b >= yellow_limit_values[4]) &&
           (total_b < yellow_limit_values[5]))
  { Serial.println("yellow");
    colour = 1;
  }
  else if ((total_r >= green_limit_values[0]) &&   //check for green
           (total_r < green_limit_values[1]) &&
           (total_g >= green_limit_values[2]) &&
           (total_g < green_limit_values[3]) &&
           (total_b >= green_limit_values[4]) &&
           (total_b < green_limit_values[5]))
  { Serial.println("green");
    colour = 2;
  }
  else if ((total_r >= orange_limit_values[0]) &&   //check for orange
           (total_r < orange_limit_values[1]) &&
           (total_g >= orange_limit_values[2]) &&
           (total_g < orange_limit_values[3]) &&
           (total_b >= orange_limit_values[4]) &&
           (total_b < orange_limit_values[5]))
  { Serial.println("orange");
    colour = 3;
  }
  else if ((total_r >= red_limit_values[0]) &&   //check for red
           (total_r < red_limit_values[1]) &&
           (total_g >= red_limit_values[2]) &&
           (total_g < red_limit_values[3]) &&
           (total_b >= red_limit_values[4]) &&
           (total_b < red_limit_values[5]))
  { Serial.println("red");
    colour = 4;
  }
  else if ((total_r >= purple_limit_values[0]) &&   //check for purple
           (total_r < purple_limit_values[1]) &&
           (total_g >= purple_limit_values[2]) &&
           (total_g < purple_limit_values[3]) &&
           (total_b >= purple_limit_values[4]) &&
           (total_b < purple_limit_values[5]))
  { Serial.println("purple");
    colour = 5;
  }
  else {
    Serial.println("unknown");    //if color not detected, set colour to 0 to force new check
    colour = 6;
  }

}

//============================== MOVE HOLDER =============================
// adjust delay depending how much the holder needs to move
// include an backlash compensation

void move_holder() {

  //int new_holder_position = ((colour - 1) * 36);  // could be simpler if colour assigment started from 0 instead of
  int new_holder_position = colour_angles[colour - 1];
  int holder_delay = (abs(previous_colour - colour) * 70);// - release_delay; //leaves 70ms for the holder servo to turn 36 degrees

  if (holder_delay < 0) {  //keep delay superior to 0
    holder_delay = 0;
  }
  if (previous_colour > colour)
  {
    //forces the holder servo to go back further than normal position to compensation for gearing backlash
    int anti_backlash_angle = new_holder_position - backward_anti_backlash;
    holder_servo.write(anti_backlash_angle);
    //delay(holder_delay);
  }
  else
  {
    int anti_backlash_angle = new_holder_position + forward_anti_backlash;
    //holder_servo.write(anti_backlash_angle);
    holder_servo.write(new_holder_position); // move holder in position
    //delay(holder_delay);
  }

  if (colour == 1) {
    set_led(holder_delay , yellow);
  }
  else if (colour == 2) {
    set_led(holder_delay , green);
  }
  else if (colour == 3) {
    set_led(holder_delay , orange);
  }
  else if (colour == 4) {
    set_led(holder_delay , red);
  }
  else if (colour == 5) {
    set_led(holder_delay , purple);
  }
  else {}

}

//=============================  RELEASE SKITTLE  ===========================

void release_skittle() {

  feeder_servo.write(180); // set servo position to the bottom for skittle release
  delay(release_delay);

}
//=================================  SHAKE  =================================

void shake() {
  
  int shake_delay = 80;
  int shake_amount = 5;
  int shake_min_value = 90;
  int shake_max_value = 180;

  feeder_servo.write(180); // set servo position to the bottom for skittle release
  delay(release_delay);
  feeder_servo.write(120); // set servo position to the bottom for skittle release
  delay(80);
  for (int i = 0; i <= shake_amount; i++) //loop for each measure
  {
    feeder_servo.write(shake_min_value); // set servo position to the bottom for skittle release
    delay(shake_delay);
    feeder_servo.write(shake_max_value); // set servo position to the bottom for skittle release
    delay(shake_delay);
  }
}

//============================ STARTING SEQUENCE ============================

void starting_sequence() {

  colour = 1;
  move_holder();
  set_led(300, yellow);
  delay (400);

  colour = 2;
  move_holder();
  set_led(300, green);
  delay (400);

  colour = 3;
  move_holder();
  set_led(300, orange);
  delay (400);

  colour = 4;
  move_holder();
  set_led(300, red);
  delay (400);

  colour = 5;
  move_holder();
  set_led(300, purple);
  delay (400);

  previous_colour = colour;
  colour = 3;
  move_holder();

  end_loop(200);
}

//================================ END LOOP =================================

void end_loop(int duration) {

  set_led(duration , orange);
  set_led(duration , red);
  set_led(duration , green);
  set_led(duration , yellow);
  set_led(duration , purple);
}

//============================   SET RGB LED COLOUR   =======================
//   This act as a delay() but allows the LED to change color while waiting .

void set_led(int duration, int color[3]) {

  int start_time = millis();     // start time value
  int current_time = start_time; // current time value
  int current_duration = 0;      // total duration

  while (current_duration < duration)
  {
    analogWrite(R_pin, map(current_duration, 0, duration, current_red_value, color[0]));  //ramp up red value for each loop
    analogWrite(G_pin, map(current_duration, 0, duration, current_green_value, color[1])); //ramp up green value for each loop
    analogWrite(B_pin, map(current_duration, 0, duration, current_blue_value, color[2])); //ramp up blue value for each loop

    current_time = millis();   //update current time
    current_duration = current_time - start_time;  //calculate total duration
  }

  current_red_value = color[0];   // set new red current value
  current_green_value = color[1]; // set new green current value
  current_blue_value = color[2];  // set new blue current value

}

Schematics

Wiring diagram
The wiring is pretty straight forward . Just make sure your buck converter output 5V before plugging anything to it !!!!!
Compact sorter v2 fdrvqsxoao

Comments

Similar projects you might like

DIY Vending Machine using Arduino & 1Sheeld

Project tutorial by Mahmoud Ahmed

  • 10,117 views
  • 8 comments
  • 38 respects

Color Sensor Prototype: Detect Color Name from Objects

Project tutorial by moretticb

  • 6,780 views
  • 7 comments
  • 32 respects

Arduino Color Recognition

by millerman4487

  • 10,885 views
  • 10 comments
  • 33 respects

Musical Color

Project tutorial by juraspb

  • 7,259 views
  • 9 comments
  • 27 respects

Coin Op Personal Vending Machine

Project tutorial by comptek4

  • 5,051 views
  • 13 comments
  • 28 respects

CNC Machine

Project tutorial by Manan Thareja

  • 14,839 views
  • 5 comments
  • 31 respects
Add projectSign up / Login