Code for bottle cap color sorting machine

Hi, how are you guys doing?

First let me say I am very very new at Arduino and coding, so hope you guys can help me out with a few things.
With that said, I'm working on a color sorting machine for bottlecaps and I'm having problem with the type of code that I should use for this type of build. I am using a RGB TCS34725 color sensor for color detection and servomotors to move some arms. I will use a conveyor belt to move the bottle caps along the color sensor then to the correct arm for sorting.

I found a code online that kind of work, but because it's an "if else" kind of code with delays I can't made the color detection run constantly since it has to finish the sequences first before it can start with color detection again. So, I don't know if there's a code to fix this or if I need something extra to store the data gather from the color sensor and then use that data to drive the servomotors.

thanks in advance for your help.

Here is the code I found online and the one I'm using right now

// include libraries
#include "Servo.h"
#include "Wire.h"
#include "Adafruit_TCS34725.h"

// servo positions in degrees for colors, please adjust
const int redPos = 45;
const int bluePos = 45;
const int startPos = -45;

// initialize servo object
Servo myservo;

// Initialize Color Sensor object
// For parameters see: https://learn.adafruit.com/adafruit-color-sensors/program-it
Adafruit_TCS34725 tcs = Adafruit_TCS34725(TCS34725_INTEGRATIONTIME_50MS, TCS34725_GAIN_1X);

// setup() is executed once when the Arduino starts up
void setup() {

  // Serial communication to output the value in the serial monitor
  Serial.begin(9600);
  Serial.println("Makerblog.at - MuMs Color Sensor");

  // Check if Color Sensor reports back
  if (tcs.begin()) {
    // Everything OK
    Serial.println("Sensor found");
  } else {
    // No sensor found. Freeze program at this point
    Serial.println("TCS34725 not found... process stopped!");
    while (1)
      ;  // Stop!
  }

  // The servo hangs on the PWM pin 3
  myservo.attach(3);
  // Move servo to home
  myservo.write(0);
  delay(100);

  myservo.attach(4);
  // Move servo to home
  myservo.write(0);
  delay(100);
}

// loop() is repeated as long as the Arduino is running
void loop() {

  // The sensor returns values ​​for R, G, B and a clear value
  uint16_t clearcol, red, green, blue;
  float average, r, g, b;
  delay(60);  // color measurement takes c. 50ms
  tcs.getRawData(&red, &green, &blue, &clearcol);

  // My attempt at color identification for
  // the 5 M&M colors red, green, blue, orange and yellow

  // average RGB
  average = (red + green + blue) / 3;
  // color values ​​by average,
  // all values ​​are now hovering around 1
  r = red / average;
  g = green / average;
  b = blue / average;

  // Output clear value and r,g,b serially to check
  // r,g and b should be between approx. 0.5 and
  // 1.5. If the sensor sees red, then r should be well above 1.0
  //, g and b between 0.5 and 1.0, etc.
  Serial.print(" \t Red:");
  Serial.print(r);
  Serial.print(" \t Blue:");
  Serial.print(b);


  // Attempt to find color based on r,g,b values.
  // It's best to start with red, green, blue
  //and adjust the thresholds accordingly // with the serial output
  if ((r > 1.4) && (g < 0.9) && (b < 0.9)) {
    delay(5000);
    myservo.attach(4);
    Serial.print("\t RED");
    myservo.write(redPos);
    delay(500);
    myservo.write(startPos);
  }


  else if ((r < 0.8) && (g < 1.2) && (b > 1.2)) {
    delay(500);
    myservo.attach(3);
    Serial.print("\t BLUE");
    myservo.write(bluePos);
    delay(500);
    myservo.write(startPos);

  }

  // If no rule applies, then be honest
  else {
    Serial.print(" \t NOT RECOGNIZED");
    // myservo.write(nonePos);
  }


  // print newlines
  Serial.println("");

  // Adjust wait time for serial debugging
  delay(100);
}

Arduino can do only one action in time, so you should first finish one detection before starting the other.

Split the project into smaller parts,
Focus on reading the colour sensor. When that works add some servo testing code.
Go on like that and tune the different parts together.
What works and what is the closest obstacle.

Is the OP using a Uno?

If the OP is using a 32 bit MCU then I suggest using the Arduino's KNN library for color matching.

You have to get rid of any 'delay()' operations if you want continuous detection of color.

Do the servos knock the caps off the conveyor or put a diverter in place so that the cap falls off the side as the conveyor moves? Either will need to be timed based on the speed of the conveyor. Is the conveyor running at a fixed speed?

Something else that will give you extra time is a mechanical means of creating distance between each of the elements you are checking for color, one way would be to have two conveyors with the downstream conveyor runnning a little faster than the first conveyor so that a gap was created as each element hit the second conveyor, a second method is to feed the product onto the center of a disk and as they travel to the outer edge of the disk the speed increases and again you get separation. I am sure if you google you will find many similar examples.

So you have caps coming along a conveyor belt. Have the sensor read the colour at one point. Create a queue of colours. Then let the cap fall off from the belt, either at the end of the belt or pushed off by a static obstacle. The cap falls into a tube with a rotating end, which points to the right jar of a circle of jars. You have the queue of colours, so you know all the time where to place the next falling cap. No need to wait at the colour sensor until the previous cap has found its jar. Does that make sense?
So you can read the next cap while the previous cap is still on its way to the tube. The critical point is the mechanism that guides the cap to the right jar. It needs its time to find the right jar before the cap may fall off the belt and into the tube. This might require the belt to stop for a moment.

This could be one of the most interesting projects around here, important even. Have you got anything mechanical to show yet, or are you just establishing the principals?

Yes, I have look into those possibilities, but I wanted to try it with just on conveyor to see if it will work.

After thinking about it, it would be better to just create the distance mechanically or I need to use Raspberry Pi instead of Arduino.

I think the Arduino is capable of doing the job you want. It doesn't matter which mcu you choose the mechanics are going to be a critical part of the reliability. There are so many ways this can be done that only you can determine with experimentation. Like someone mentioned split it into pieces, start with a feeder or magazine, go on to the conveying system making sure they are right side up, separate and align them for the vision system, run them through the vision system perhaps in a type of separated or bucket conveyor with an encoder so that you have an index value for each bucket, move on to a diverter system that is linked to the index encoder/counter, divert to a container based on color/bad read/empty index. Like I said there are so many ways but if you design it in phases then some of the ideas in this thread might become useful.

I don't see any need for a Raspberry Pi. All you have is colour detecting, decision making and steering some mechanics. All this done seemingly at the same time. No big data, no AI. An Arduino can do that.

let's say for a red cap it takes 2 seconds for it to reach the sorting arm and during that time another color pass in front of the color sensor, but since the code sequence for redcap is still in process Arduino wont be able to read the other color, right?

or is there a way to make the color sensor keep detecting colors while Arduino drive a servomotor? A code function that doesn't involve "if else" and "delay" function maybe?

thanks for your reply in advance

Add an encoder to the conveyor so you can track its movement. Every X units of conveyor travel equals Y encoder pulses. When the color sensor registers a color drop a code for that color into a shift register stage relating to the sensor position. When no color is sensed drop in a '0'. This way you have an analog of where a cap is on the conveyor and you know its color. As the cap moves downstream continue tracking it and actuate your diverter when a certain color appears in a certain shift register stage.

I hope that these caps will come along the conveyor separated so that you never have two of them in view at the same time.

Yes. Your code commands the servo with one single function, like myservo.write(queue[n]);. Once your code has performed that line, the servo starts working on its own, while your program does other things. The task of your program is to keep track of time.

It could look like this:


1 is a laser beam detecting caps.
2 is the colour sensor.
3 is the servo twisting a tube to make the cap fall into the right jar.

The laser beam times everything. When the light breaks, it creates a new task with a time stamp. As long as the beam is broken, no new tasks are created.

The colour sensor checks the time stamp of the next task, or does nothing if there are no tasks. If the right time has passed, it reads the colour and records it for the task, otherwise it does nothing.

The servo checks the time stamp of the next task, or does nothing if there are no tasks. If the right time has passed, it rotates to the right angle, otherwise it does nothing.

The tasks have to be placed in a queue, an array of some kind. Your loop() would look like:

void loop()
{
    laser();
    colour_sensor();
    servo();
}

The program waits at no point. The loop might perform thousands times per seconds, it doesn't matter. Each function performs its own task. If its time to do something, it does it. If not, it does nothing and lets the program continue.

No, wrong.

Yes.
I think the concept you are missing is a thing called a state machine, read this:
Demonstration for several things at the same time

And this:

Once you understand and can apply those principles the job becomes easy.

Update to the code:

#include <TimerOne.h>
#include "Servo.h"
#include "Wire.h"
#include "Adafruit_TCS34725.h"
#include "rgb_lcd.h"
rgb_lcd lcd;
#define laser 1
int whichLine = 0;
unsigned long myTime;
int sensorPin = A0;
int sensor = 0;
const int Pos = 90;
int Pos1 = 35;
int Pos2 = 35;
int Pos3 = 35;
int Pos4 = 35;
int Pos5 = 35;
const int LightSense = 750;

struct Cap {
  float color;
  long dtime;
  long aotime;
  long actime;
  int del = 2;
};

int lock = 0;

struct Cap List[40];
// initialize servo object

Servo myservo;

// Initialize Color Sensor object
// For parameters see: https://learn.adafruit.com/adafruit-color-sensors/program-it
Adafruit_TCS34725 tcs = Adafruit_TCS34725(TCS34725_INTEGRATIONTIME_50MS, TCS34725_GAIN_1X);


// setup() is executed once when the Arduino starts up
void setup() {
  lcd.begin(16, 2);

  // Serial communication to output the value in the serial monitor
  Serial.begin(9600);
  lcd.print("Limpi Recycling!");

  // Check if Color Sensor reports back
  if (tcs.begin()) {
    // Everything OK
    lcd.println("Sensor found");
  } else {
    // No sensor found. Freeze program at this point
    lcd.println("TCS34725 not found... process stopped!");
    // while (1)
    // ;  // Stop!
  }

  myservo.attach(3);
  myservo.write(0);
  delay(200);
  // Move servo to home
  myservo.write(90);
  //delay(100);
  delay(200);


  myservo.attach(4);
  myservo.write(0);
  delay(200);
  // Move servo to home
  myservo.write(90);
  //delay(100);
  delay(200);


  myservo.attach(6);
  myservo.write(0);
  delay(200);
  // Move servo to home
  myservo.write(90);
  //delay(100);
  delay(200);

  myservo.attach(8);
  myservo.write(0);
  delay(200);
  // Move servo to home
  myservo.write(90);
  //delay(100);
  delay(200);

  myservo.attach(11);
  myservo.write(0);
  delay(200);
  // Move servo to home
  myservo.write(90);
  //delay(100);
  delay(200);

  lcd.clear();
}

void loop() {
  lcd.setCursor(1, whichLine);  // set cursor to 1st character position.
  char c = Wire.read();         // read 1 character
  lcd.print(c);                 //print the 1 charcter to lcd
                                // move to next line

  uint16_t clearcol, red, green, blue;
  float average, r, g, b;
  delay(60);  // color measurement takes c. 50ms
  tcs.getRawData(&red, &green, &blue, &clearcol);

  average = (red + green + blue) / 3;
  // color values ​​by average,
  // all values ​​are now hovering around 1
  r = red / average;
  g = green / average;
  b = blue / average;


  /*Serial.print(" \t Red:");
  Serial.print(r);
  Serial.print(" \t Blue:");
  Serial.print(b);*/
  pinMode(laser, OUTPUT);
  sensor = analogRead(sensorPin);

  //Serial.print("Sensor: ");
  //Serial.println(sensor);



  if (sensor > 700) {
    lock = 0;
    //Serial.print("OPEN Lock: ");
    //Serial.println(lock);
  }

  if ((r > 1.4) && (g < 0.9) && (b < 0.9) && sensor < LightSense && lock == 0) {  //RED
    Serial.print(" \t Red:");
    for (int i = 0; i < 15; i++) {
      if ((List[i].del == 1 || List[i].del == 2) && lock == 0) {
        List[i].color = 1;
        List[i].dtime = millis();
        List[i].aotime = 2280;
        List[i].actime = 2880;
        List[i].del = 0;
        //Serial.print("\t RED\n");
        lcd.print("RED....");
        lock = 1;
        //myTime = millis();
        //Serial.println(myTime);
        Serial.println(lock);
      }
    }

  } else if ((r > 1.4) && (g < 1.0) && (b < 0.7) && sensor < LightSense && lock == 0) {  //ORANGE
    Serial.print(" \t Orange:");
    for (int i = 0; i < 15; i++) {
      if ((List[i].del == 1 || List[i].del == 2) && lock == 0) {
        List[i].color = 2;
        List[i].dtime = millis();
        List[i].aotime = 7770;
        List[i].actime = 8200;
        List[i].del = 0;
        //Serial.print("\t Orange\n");
        lock = 1;
        lcd.print("Orange..");
        Serial.println(lock);
      }
    }
  } else if ((r < 0.8) && (g < 1.2) && (b > 1.2) && sensor < LightSense && lock == 0) {  //Blue
    Serial.print(" \t Blue:");
    for (int i = 0; i < 15; i++) {
      if ((List[i].del == 1 || List[i].del == 2) && lock == 0) {
        List[i].color = 3;
        List[i].dtime = millis();
        List[i].aotime = 7200;
        List[i].actime = 7800;
        List[i].del = 0;
        //Serial.print("\t Blue\n");
        lock = 1;
        lcd.print("Blue....");
        Serial.println(lock);
      }
    }
  } else if ((r > 0.4 && r < 0.95) && (g > 1.4 && g < 1.6) && (b < 0.9 && b > 0.8) && (clearcol > 700) && sensor < LightSense && lock == 0) {  //Green
    Serial.print(" \t Green:");
    for (int i = 0; i < 15; i++) {
      if ((List[i].del == 1 || List[i].del == 2) && lock == 0) {
        List[i].color = 4;
        List[i].dtime = millis();
        List[i].aotime = 6080;
        List[i].actime = 6600;
        List[i].del = 0;
        //Serial.print("\t Green\n");
        lock = 1;
        lcd.print("Green....");
        Serial.println(lock);
      }
    }
  } else if ((r > 1.15) && (g > 1.15) && (b < 0.7) && sensor < LightSense && lock == 0) {  //Yellow
    Serial.print(" \t Yellow:");
    for (int i = 0; i < 15; i++) {
      if ((List[i].del == 1 || List[i].del == 2) && lock == 0) {
        List[i].color = 5;
        List[i].dtime = millis();
        List[i].aotime = 780;
        List[i].actime = 1300;
        List[i].del = 0;
        //Serial.print("\t Yellow\n");
        lock = 1;
        lcd.print("Yellow..");
        Serial.println(lock);
      }
    }
  } else if ((r > 0.90) && (g > 1.10) && (b < 0.90) && sensor < LightSense && lock == 0) {  //Cola Brown
    Serial.print(" \t Brown:");
    for (int i = 0; i < 15; i++) {
      if ((List[i].del == 1 || List[i].del == 2) && lock == 0) {
        List[i].color = 6;
        List[i].dtime = millis();
        List[i].aotime = 4000;
        List[i].actime = 4500;
        List[i].del = 0;
        //Serial.print("\t Brown\n");
        lock = 1;
        lcd.print("Brown...");
        Serial.println(lock);
      }
    }
  } else if ((r > 1.03 && r < 1.10) && (g < 0.85) && (b < 1.16 && b > 0.95) && sensor < LightSense && lock == 0) {  //Purple
    Serial.print(" \t Purple:");
    for (int i = 0; i < 15; i++) {
      if ((List[i].del == 1 || List[i].del == 2) && lock == 0) {
        List[i].color = 7;
        List[i].dtime = millis();
        List[i].aotime = 1000;
        List[i].actime = 1500;
        List[i].del = 0;
        lock = 1;
        Serial.print("Purple Lock: ");
        Serial.println(lock);
        lcd.clear();
      }
    }
  } else if ((r < 0 && r > 0) && (g < 0 && g > 0) && (b < 0 && b > 0) && sensor < LightSense && lock == 0) {  //Grey
    Serial.print(" \t Grey:");
    for (int i = 0; i < 15; i++) {
      if ((List[i].del == 1 || List[i].del == 2) && lock == 0) {
        List[i].color = 9;
        List[i].dtime = millis();
        List[i].aotime = 1000;
        List[i].actime = 1500;
        List[i].del = 0;
        lock = 1;
        Serial.print("Grey Lock: ");
        Serial.println(lock);
      }
    }
  } else if ((r <= 1.10 && r >= 1.03) && (g <= 1.05 && g >= 1.02) && (b <= 0.97 && b >= 0.93) && (clearcol <= 200 && clearcol >= 100) && sensor < LightSense && lock == 0) {  //Black
    Serial.print(" \t Black:");
    for (int i = 0; i < 15; i++) {
      if ((List[i].del == 1 || List[i].del == 2) && lock == 0) {
        List[i].color = 10;
        List[i].dtime = millis();
        List[i].aotime = 1000;
        List[i].actime = 1500;
        List[i].del = 0;
        lock = 1;
        Serial.print("Black Lock: ");
        Serial.println(lock);
      }
    }
  }




  /////////////////////// Open Arms:
  for (int i = 0; i < 15; i++) {
    if ((millis() - List[i].dtime) >= List[i].aotime) {

      if (List[i].color == 1 && List[i].del == 0) {
        //for (Pos1 = 0; Pos1 <= 35; Pos1 += 1) {
        myservo.write(Pos1);
        myservo.attach(4);
        //}
        List[i].del = 3;
        Serial.print("open Red\n");
        myTime = millis();
        Serial.println(myTime);
      }

      else if (List[i].color == 2 && List[i].del == 0) {
        //for (Pos2 = 0; Pos2 <= 35; Pos2 += 1) {
        myservo.write(Pos2);
        myservo.attach(11);
        //}
        List[i].del = 3;
        Serial.print("open Orange\n");

      } else if (List[i].color == 3 && List[i].del == 0) {
        //for (Pos3 = 0; Pos3 <= 35; Pos3 += 1) {
        myservo.write(Pos3);
        myservo.attach(11);
        //}
        List[i].del = 3;
        Serial.print("open Blue\n");

      } else if (List[i].color == 4 && List[i].del == 0) {
        //for (Pos4 = 0; Pos4 <= 35; Pos4 += 1) {
        myservo.write(Pos4);
        myservo.attach(8);
        // }
        List[i].del = 3;
        Serial.print("open Green\n");

      } else if (List[i].color == 5 && List[i].del == 0) {
        //for (Pos5 = 0; Pos5 <= 40; Pos5 += 1) {
        myservo.write(Pos5);
        myservo.attach(3);
        //}
        List[i].del = 3;
        Serial.print("open Yellow\n");

      } else if (List[i].color == 6 && List[i].del == 0) {
        // for (Pos2 = 0; Pos2 <= 30; Pos2 += 1) {
        myservo.write(Pos4);
        myservo.attach(6);
        //}
        List[i].del = 3;
        Serial.print("open Brown\n");

      } else if (List[i].color == 7 && List[i].del == 0) {
        myservo.attach(7);
        myservo.write(Pos);
        List[i].del = 3;
        Serial.print("open Purple\n");

      } else if (List[i].color == 9 && List[i].del == 0) {
        myservo.attach(7);
        myservo.write(Pos);
        List[i].del = 3;
        Serial.print("open Grey\n");

      } else if (List[i].color == 10 && List[i].del == 0) {
        myservo.attach(7);
        myservo.write(Pos);
        List[i].del = 3;
        Serial.print("open Black\n");
      }
    }
  }



  //////////////////////Close Arm:
  for (int i = 0; i < 15; i++) {
    if ((millis() - List[i].dtime) >= List[i].actime) {

      if (List[i].color == 1 && List[i].del == 3) {
        myservo.attach(4);
        myservo.write(Pos);  //Red
        List[i].del = 1;
        Serial.print("close Red\n");

      } else if (List[i].color == 2 && List[i].del == 3) {
        myservo.attach(11);
        myservo.write(Pos);  //Orange
        List[i].del = 1;
        Serial.print("close Orange\n");

      } else if (List[i].color == 3 && List[i].del == 3) {
        myservo.attach(11);
        myservo.write(Pos);
        List[i].del = 1;
        Serial.print("close Blue\n");  //Blue

      } else if (List[i].color == 4 && List[i].del == 3) {
        myservo.attach(8);
        myservo.write(Pos);
        List[i].del = 1;
        Serial.print("close Green\n");  //Green

      } else if (List[i].color == 5 && List[i].del == 3) {
        myservo.attach(3);
        myservo.write(Pos);
        List[i].del = 1;
        Serial.print("close Yellow\n");  //Yellow

      } else if (List[i].color == 6 && List[i].del == 3) {
        myservo.attach(6);
        myservo.write(Pos);
        List[i].del = 1;
        Serial.print("close Brown");  //Brown

      } else if (List[i].color == 7 && List[i].del == 3) {
        myservo.attach(7);
        myservo.write(Pos);
        List[i].del = 1;
        Serial.print("close Purple");  //Purple

      } else if (List[i].color == 9 && List[i].del == 3) {
        myservo.attach(7);
        myservo.write(-Pos);
        List[i].del = 1;
        Serial.print("close Grey");  //grey

      } else if (List[i].color == 10 && List[i].del == 3) {
        myservo.attach(7);
        myservo.write(Pos);
        List[i].del = 1;
        Serial.print("close Black");  //Black
      }
    }
  }
}

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.