Skip to content

Final Project

🏹 My purpose

My original idea, since the start of FabAcademy journey, was to do a color sorting machine for bottle caps. I had this desire because I work in a studio-fablab and since March 2020 we have collected 3d printed PLA scraps from other labs and universities in Milan to recycle them. This initiative, which is very cool, is also time consuming in one specific moment: the division and sorting of the scraps by colour, which you can see in the following video.

We also collect plastic bottles caps that we then shred and we divide these by colour, too. My purpose is to change this process, designing an Automatic Cap Color Sorter Machine, or for short, ACCSM! The idea is to start with the caps and not with the scraps because the caps have a similar size and shape, while the scraps can be big, small, squared, triangular, etc: the shape cannot be controlled and therefore the useful space cannot be designed.

first sketch

πŸ˜Άβ€πŸŒ«οΈ Research

On the market, there are a lot of industrial colour sorters and their functioning is quite sofisticated. They are able to sort even super small particles of plastic, in a huge amount of other colors. However, the maker world has found solutions for the color sorting problem, that may not concern only the bottle caps. I guess that people like Skittle to be divided by colour (KIDDING) since there are a lot of Arduino projects that devide the skitles by colour.

What I find interesting in these projects are the structure solutions, for example the fact that the Skittles are collected in a circular frame. I see the perks of the design behind the circular structure:

  • you need two motors

  • everything is “condensed” in less space

Even if I saw the potentials in these decisions, I decided to go on with another design that could be more similar to a game like a flipper, e.i.

πŸ₯Έ Planning vs reality

Week Plan Reality
Principles and Practices Sketch Sketch
Computer Aided Design 3d models 3d models done, then changed
Computer Controlled Cutting Laser the structure I decided to change the model, I did not laser the structure
3D Scanning and Printing print part of the structure I printed a vase
Electronics Design Design a board with a button to learn how it works Board designed, then changed and designed with servo motors
Embedded Programming Control the servo Controlled the servo
Output Devices Control the servo Controlled the servo
Input Devices Test color sensors Potentiomenter and servo
Networking & Communications Make communicate a board with servos with one with color sensor Which boards?
Project Development Put everything together Somehow managed to

πŸͺ± Functions

Scheme of the functioning

πŸ‘οΈ Steps

🐟 Principles and Practices

I started off by this sketch I did by hand:

first sketch

The ACCSM should have a funnel to insert the caps in. Right after the funnel there could be a sensor capable of reading the colour of the caps, and based on the response of the sensor, the caps will go in a a direction or in another. Here there is a rough sketch of the functioning of the machine, altouhg the shape is not defined yet. Going on I did a more accurate, yet still vague, sketch of the machine: there will be 8 colours identified, which are the most present in the caps we collect. The boxed colours will be the containers of the sorted caps.

last sketch

πŸ‘½ Computer Aided Design

The previous week I sketched on the machine I want to create. So I started the design from this 2D sketch. I designed this sketch using Figma, a collaborative web application for interface design.

last sketch

I then proceeded to use Fusion360 to model the strucure of my machine. I started off with the tree, since I wanted to 3d print it. I the modeled accordingly the back of the structure and made holes for the servomotors.

sketch

Done this, I had the majority of the structure ready to be printed or lasered.

🍽️ Computer controlled cutting

Even if I did not do it during the computer controlled cutting week, for this part I did the structure in wood of my machine. I had already designed the structure in Fusion360 and I then exported the sketch as a dxf.

dxf

With that dxf I could open Rhinoceros and laser cut it a piece of MDF. The laser cutting was easy since I only had to cut the borders. I did some tests before laser cutting everything because I had to be sure that the kerf was nicely calculated.

laser

Luckily, I managed to laser it perfectly and I had the prove of that because the pieces I 3dprinted fitted perfectly.

laser

🦐 3d printing

During this week, I could focus on the project but I decided to 3dprint a vase for my bonsai tree. It was not a problem because I could print the pieces whenever I wanted, since I have the machines in my studio.

I decided to 3d model a tree structure for the sorting part of the machine and to 3d print it in order to have an assemblable and jointable structure. Indeed the sorting walls are a few tenth of a millimiter smaller than the holes in the MDF.

After modeling the pieces, I exported them in stl and opened them in Simplify3D. I printed them with a 0.4mm nozzle and with a layer height of 0.2mm.

I printed the pieces with a Delta Wasp 2040 in transparent PETG because I wanted the shiniest effect.

laser

🦧 Electronics design

For this part, I designed two boards: one for the servo motors and one for the color sensor. Let’s start off with the servo board.

πŸ› ESP32

In this board I needed enogh pins to have:

  • 7 servo motors;
  • one IR sensors;
  • one switch;
  • a voltage regulator;
  • diode;
  • led;
  • photoresistor;

Therefore, my FABolous instructor suggested me to use an ESP32. I started off by searching the ESP32 datasheet and pinout.

Pinout

I created a new project in EAGLE, and I started to add the components I needed as it follows:

  • For each servo: CONN_031X03_NO_SILK
  • For the IR sensors: CONN_021X02_NO_SILK
  • 2 switches: SW_SWITCH_TACTILE_6MM
  • An FTDI connector: CONN_06_FTDI-SMD-HEADER
  • Voltage regulator: REGULATOR_SOT223
  • One slide switch
  • Diode
  • Led: LEDFAB1206
  • Photoresistor

Schematic eagle

Since the ESP32 has 3.3V power and the servos need 5V I needed also two different VCC signals in my schematic. For the servos I also decided to connect them in a line of GND and VCC so that I could have a better organisation of space.

Focus servos

Once the schematic was ready, I went onto the board and with a lot of patience managed to route everything.

Board eagle

🦚 Electronic production

I exported the board from EAGLE and proceeded in doing as I explained in Electronic Production. Here some passages:

fab modules

  • I went on FabModules and separated the process in two because the esp32 traces needed the traces to be thinner.

esp32fab

  • I milled the board:

Components

milled

This is the board milled and cleaned up.

Components

I started off collecting all the components I needed for my board.

Components

I then started to solder the ESP32. ESP32 is the most fun and easy to solder microcontroller. KIDDING.

ESP32 soldered

It is pure hell. But eventually I managed to solder everything. After three tries.

everything soldered

🐿️ Embedded programming

🐬 TinkerCAD

For embedded programming section, I started off on TinkerCAD. I started to do a simulation with Arduino UNO, a servo and a photoresistor as you can see in the following image.

Tcad0

I then wrote a code that made the servo move accordingly to the value that the photoresistor senses.

Tcad0

Here is the code:

#include <Servo.h>

int lightval; 
int lightpin = A0; 
int tm = 100; 
int servopin = 9; 

Servo myservo; 

int angle; 

void setup() {
  Serial.begin(9600);
  pinMode(lightpin, INPUT);
  myservo.attach(servopin);
  pinMode(servopin, OUTPUT);
}

void loop() {
  lightval = analogRead(lightpin);
  Serial.println(lightval);
  delay(tm);

  angle = lightval/5;
  myservo.write(angle);
  Serial.println("angle is");
  Serial.prinln(angle);
}

Tcad0

I leveled the game up by adding two photoresistors to simulate the R, G and B values.

Tcad0

🦭 Input devices

For the input devices part, I focused of course on the color sensor. To start off, I used Adafruit Adafruit_TCS34725 color sensor.

πŸ¦• Color sensor

🐑 Adafruit_TCS34725

Adafruit_TCS34725 is a board with the TCS34725, the actual color sensor, which has RGB and Clear light sensing elements. An IR blocking filter, integrated on-chip and localized to the color sensing photodiodes, minimizes the IR spectral component of the incoming light and allows color measurements to be made accurately. The filter means you’ll get much truer color than most sensors, since humans don’t see IR. The sensor also has an incredible 3,800,000:1 dynamic range with adjustable integration time and gain so it is suited for use behind darkened glass.

colorsens

🐼 Testing the sensor

Research and experimentation was necessary to understand the correct functioning of the colour sensor. First of all, a treasure hunt with my beloved little friend William began in the laboratory in search of the most diverse objects that met certain characteristics. Same colour but different shapes and transparencies, but also different colours and similar shapes.

colorsens

And it was here that things started to get serious. By scanning and analysing each object at different distances, it was possible to compile a table of values that described as accurately as possible what the sensor was seeing. At this point I was in possession of a fairly accurate classification of the colours sensed by the sensor.

These tests led to the realisation that the RGB colour method was not the most correct for describing my objects and that I therefore had to find another way for this.

🦎 Color theory

HSV is a cylindrical color model that remaps the RGB primary colors into dimensions that are easier for humans to understand. Like the Munsell Color System, these dimensions are hue, saturation, and value. Hue specifies the angle of the color on the RGB color circle.

The HSV model defines color perception in a way that is close to how the human eye usually does. In terms of a mixture of primary colors, RGB describes color. The HSV color model is frequently favored over the RGB model in circumstances where color description is crucial.

hsv

The terms “Hue,” “Saturation,” and “Value” denote the color, the proportion of that color to white in each case, and the proportion of that color to black in each case (Gray level).

We are unable to distinguish brightness from color information in RGB. To separate the color and brightness information in an image, utilize HSV, or hue saturation value.

We choose HSV over RGB or BGR for color recognition and thresholding because HSV is more resistant to changes in ambient lighting. This indicates that hue values shift significantly less than RGB values when there are slight variations in the ambient lighting (such as pale shadows, etc.).

Two tints of red, for instance, could have comparable Hue values but vastly differing RGB values. We must do everything possible to ensure that our application functions properly regardless of environmental changes in real-world circumstances, such as object tracking based on color. Therefore, HSV colour thresholding is preferred over RGB.

As the project involves the use of caps of different transparencies and colours, the need to be able to identify colours as consequential ranges based on an initial pilot value (hue) was decisive in the decision to use this reference system as opposed to RGB.

πŸ” The code

Given all the things I said above, for the final I realised I had to convert the RGB values to HSV values. To do this, I created a function called RGBtoHSV as follows:

void RGBtoHSV(int RGBColor[], int HSVColor[]) {
 double color[3];
 double Cmax, Cmin, delta;
 double hue, sat, val;
 double hueAngle, satPerc, valPerc;

 color[0] = RGBColor[0];
 color[1] = RGBColor[1];
 color[2] = RGBColor[2];

 color[0] /= 255;
 color[1] /= 255;
 color[2] /= 255;

 Cmax = arrayMax(color, 3);
 Cmin = arrayMin(color, 3);
 delta = Cmax - Cmin;

 if (delta == 0) {
   hue = 0;
 }
 if (Cmax == color[0]) {
   hue = fmod(60 * (((color[1] - color[2]) / delta) + 360), 360);
   if (color[1] - color[2] < 0) {
     hue += 255;
   }
 }
 if (Cmax == color[1]) {
   hue = fmod(60 * (((color[2] - color[0]) / delta) + 120), 360);
 }
 if (Cmax == color[2]) {
   hue = fmod(60 * (((color[0] - color[1]) / delta) + 240), 360);
 }

 if (Cmax == 0) {
   sat = 0;
 } else {
   sat = (delta / Cmax) * 100;
 }

 val = Cmax * 100;

 HSVColor[0] = (int)hue;
 HSVColor[1] = (int)sat;
 HSVColor[2] = (int)val;
}

Moreover, I created a tridimensional array to put my calibrated values for the color I had. The array is composed by 8 colors, in which I have a maximum and a minimum value and three values for each max and min.

const double calibratedvalues[8][2][3] = {
   {
     {
       //RED
       { 0.000, 81.23, 73.87 },
       { 117.7, 105.9, 108.4 },
     },
     {
       //ORANGE
       { 12.00, 60.00, 86.69 },
       { 35.67, 120.5, 120.5 },
     },
     {
       //YELLOW
       { 38.01, 87.88, 70.59 },
       { 62.54, 148.5, 149.4 },
     },
     {
       //GREEN
       { 97.11, 108.6, 57.48 },
       { 108.1, 130.0, 62.52 },
     },
     {
       //BLUE
       { 126.2, 110.0, 36.63 },
       { 142.1, 200.0, 58.03 },
     },
     {
       //WHITE TO DO
       { 0.000, 81.23, 73.87 },
       { 117.7, 105.9, 108.4 },
     },
     {
       //BLACK. TO DO
       { 0.000, 81.23, 73.87 },
       { 117.7, 105.9, 108.4 },
     },
     {
       //PINK. TO DO
       { 0.000, 81.23, 73.87 },
       { 117.7, 105.9, 108.4 },
     },
   };
 }

For the sensing section, I wrote this code for my sensor that collects data in rgb format and then there is the convertion I talked about before.

void measureColor(int measuredColor[]) {
 float r, g, b;
 uint16_t red, green, blue, clear;
 uint32_t sum;

 // Get raw data and store it in variables
 tcs.getRawData(&red, &green, &blue, &clear);

 // Do calculations to convert the measured values to adjusted 0-255 RGB values
 sum = clear;
 r = red;
 g = green;
 b = blue;
 r /= sum;
 g /= sum;
 b /= sum;
 r *= 256;
 g *= 256;
 b *= 256;
 measuredColor[0] = r;
 measuredColor[1] = g;
 measuredColor[2] = b;
}

Color sensor pcb

I also produced a color sensor pcb for my project very similar to the Adafruit TCTCS34725. Here there are the traces, output and holes files:

top

top

top

🐏 Output devices

πŸˆβ€β¬› Servos

To understand the whole logic of the code, I did a step back and used an ArduinoUno and a breadboard power supply to power the servos.

To connect the servos, I crimped seven wires (actually 42 crimpes) and numbered them in groups of 3 to have clear which servo is which.

I wrote a code that could help me in debugging and understanding the right angles for each color. Here it is:

#include <Servo.h>

#define SERVO_AMOUNT 7

Servo servo[SERVO_AMOUNT];

int servo_pin[SERVO_AMOUNT] = {9, 10, 6, 11, 12, 3, 5};
int angle_init[SERVO_AMOUNT]  = {70, 70, 70, 70, 70, 70, 70};
int pos;

const int d = 50;
const int s = 150;

enum Colors { RED, ORANGE, YELLOW, GREEN, BLUE, WHITE, BLACK, PINK, END };    //i percorsi x colore

int rotations[Colors::END][SERVO_AMOUNT] = {      //dico ai servo che rotazione fare per ogni Colors
 {s, s, d, s, d, d, d},
 {s, s, d, d, d, d, d},
 {s, d, d, d, s, d, d},
 {s, d, d, d, d, d, d},
 {d, s, s, d, d, s, d},
 {d, s, s, d, d, d, d},
 {d, d, d, s, d, d, s},
 {d, d, d, s, d, d, d},
};

struct Pattern {                                  //associa la rotazione al colore
 int* rotation;
 Colors color;
};

Pattern p[Colors::END];

void setup() {
 Serial.begin(9600);
 Serial.println("Daje");

 for (short i = 0; i < Colors::END; i++) {       //associo pattern alle rotazioni
   p[i].rotation = &rotations[i][0];
 }

 for (int i = 0; i < SERVO_AMOUNT; i++) {
   pinMode(servo_pin[i], OUTPUT);
   servo[i].attach(servo_pin[i]);
   servo[i].write(angle_init[i]);                //posizione iniziale
 }
}

void loop() {
 if (Serial.available()) {
   String state = Serial.readString();
   int i = int(state[0]) - 48;
   if (i < Colors::END) {
     for (short k = 0; k < SERVO_AMOUNT; k++) {
       servo[k].write(p[i].rotation[k]);
     }
   } 
   else {
     Serial.println("Ma che stai a fa'?");
   }
 }
}

Once the breadboard supply was connected with jumpers to the Arduino (5V to the 5V and GND with GND) and all the servos were connected (the VCC to the + of the breadboard supply and the GND to the - of the breadboard) I used a power supply with 9V and 2000mA to power the power supply.

There were some problems and then saw that the ams1117 of the power supply was FUSING. We decided to do some math:

  • Arduino needs 250mA.
  • Each servo needs ~170mA (170*7 = 1190mA).

This means that the overall consuption is of 1440mA. We saw on the datasheet of the ams1117 that it has an output of 800mA. So of course it was overloaded.

I opted on using a DIN power supply then and it actually worked. So I got back to Embedded programming to close the code.

πŸ‰ The whole code

#include <Servo.h>
#include "Adafruit_TCS34725.h"
#include "functions.h"

#define SERVO_AMOUNT 7

Servo servo[SERVO_AMOUNT];

int servo_pin[SERVO_AMOUNT] = { 9, 10, 6, 11, 12, 3, 5 };
int angle_init[SERVO_AMOUNT] = { 70, 70, 70, 70, 70, 70, 70 };
int pos;

const int d = 50;
const int s = 150;

enum Colors { RED,
             ORANGE,
             YELLOW,
             GREEN,
             BLUE,
             WHITE,
             BLACK,
             PINK,
             END };  //i percorsi x colore

int rotations[Colors::END][SERVO_AMOUNT] = {
 //dico ai servo che rotazione fare per ogni Colors
 { s, s, d, s, d, d, d },
 { s, s, d, d, d, d, d },
 { s, d, d, d, s, d, d },
 { s, d, d, d, d, d, d },
 { d, s, s, d, d, s, d },
 { d, s, s, d, d, d, d },
 { d, d, d, s, d, d, s },
 { d, d, d, s, d, d, d },
};

struct Pattern {  //associa la rotazione al colore
 int* rotation;
 Colors color;
};

Pattern p[Colors::END];

Adafruit_TCS34725 tcs = Adafruit_TCS34725();  //inizializza il sensore colore

void setup() {
 Serial.begin(9600);
 Serial.println("Daje");

 for (short i = 0; i < Colors::END; i++) {  //associo pattern alle rotazioni
   p[i].rotation = &rotations[i][0];
 }

 for (int i = 0; i < SERVO_AMOUNT; i++) {
   pinMode(servo_pin[i], OUTPUT);
   servo[i].attach(servo_pin[i]);
   servo[i].write(angle_init[i]);  //posizione iniziale
 }

 /************* Color Sensor ************************/
 if (tcs.begin()) {  //vedere se il sensore Γ¨ connesso
   Serial.println("Found sensor");
 } else {
   Serial.println("No TCS34725 found ... check your connections");
   while (1)
     ;
 }

 int rgb[3] = { 255, 0, 0 };
 int hsv[3];

 RGBtoHSV(rgb, hsv);
}

void loop() {
 if (Serial.available()) {
   String state = Serial.readString();
   int i = int(state[0]) - 48;
   if (i < Colors::END) {
     for (short k = 0; k < SERVO_AMOUNT; k++) {
       servo[k].write(p[i].rotation[k]);
     }
   } else {
     Serial.println("Ma che stai a fa'?");
   }
 }
  void measureColor(int measuredColor[]);
  void RGBtoHSV(int RGBColor[], int HSVColor[]);
  Colors determineColor(int measuredColor[]);
}

void measureColor(int measuredColor[]) {
 float r, g, b;
 uint16_t red, green, blue, clear;
 uint32_t sum;

 // Get raw data and store it in variables
 tcs.getRawData(&red, &green, &blue, &clear);

 // Do calculations to convert the measured values to adjusted 0-255 RGB values
 sum = clear;
 r = red;
 g = green;
 b = blue;
 r /= sum;
 g /= sum;
 b /= sum;
 r *= 256;
 g *= 256;
 b *= 256;
 measuredColor[0] = r;
 measuredColor[1] = g;
 measuredColor[2] = b;
}

void RGBtoHSV(int RGBColor[], int HSVColor[]) {
 double color[3];
 double Cmax, Cmin, delta;
 double hue, sat, val;
 double hueAngle, satPerc, valPerc;

 color[0] = RGBColor[0];
 color[1] = RGBColor[1];
 color[2] = RGBColor[2];

 color[0] /= 255;
 color[1] /= 255;
 color[2] /= 255;

 Cmax = arrayMax(color, 3);
 Cmin = arrayMin(color, 3);
 delta = Cmax - Cmin;

 if (delta == 0) {
   hue = 0;
 }
 if (Cmax == color[0]) {
   hue = fmod(60 * (((color[1] - color[2]) / delta) + 360), 360);
   if (color[1] - color[2] < 0) {
     hue += 255;
   }
 }
 if (Cmax == color[1]) {
   hue = fmod(60 * (((color[2] - color[0]) / delta) + 120), 360);
 }
 if (Cmax == color[2]) {
   hue = fmod(60 * (((color[0] - color[1]) / delta) + 240), 360);
 }

 if (Cmax == 0) {
   sat = 0;
 } else {
   sat = (delta / Cmax) * 100;
 }

 val = Cmax * 100;

 HSVColor[0] = (int)hue;
 HSVColor[1] = (int)sat;
 HSVColor[2] = (int)val;
}

Colors determineColor(int measuredColor[]) {
 int i;
 Colors color = unknown;

 for (i = 0; i < 8; i++) {
   if ((measuredColor[0] >= calibratedvalues[i][0][0][0]) &&  // H lower bound check
       (measuredColor[0] <= calibratedvalues[i][0][1][0]) &&  // H upper bound check
       (measuredColor[1] >= calibratedvalues[i][0][0][1]) &&  // S lower bound check
       (measuredColor[1] <= calibratedvalues[i][0][1][1])) {  // S upper bound check
     // Set the item color when a check is succesful
     color = (Colors)i;
     i = 8;
   }
   return color;
 }

 const double calibratedvalues[8][2][3] = {
   {
     {
       //RED
       { 0.000, 81.23, 73.87 },
       { 117.7, 105.9, 108.4 },
     },
     {
       //ORANGE
       { 12.00, 60.00, 86.69 },
       { 35.67, 120.5, 120.5 },
     },
     {
       //YELLOW
       { 38.01, 87.88, 70.59 },
       { 62.54, 148.5, 149.4 },
     },
     {
       //GREEN
       { 97.11, 108.6, 57.48 },
       { 108.1, 130.0, 62.52 },
     },
     {
       //BLUE
       { 126.2, 110.0, 36.63 },
       { 142.1, 200.0, 58.03 },
     },
     {
       //WHITE 
       { 0.000, 81.23, 73.87 },
       { 117.7, 105.9, 108.4 },
     },
     {
       //BLACK. 
       { 0.000, 20.23, 73.87 },
       { 117.7, 35.9, 108.4 },
     },
     {
       //PINK. 
       { 0.000, 81.23, 73.87 },
       { 117.7, 105.9, 108.4 },
     },
   };
 }

πŸ¦™ Bill of Materials

Material Specs Quantity Cost €
STRUCTURE
MDF 400x400x5 mm 1 0,50€
Transparent PETG 100g 0,30€
Plexyglass 400x400x4 mm 1 0,50€
ELECTRONICS
Servo motors micro 9 20,50€
Color sensor 1 0,40€
ESP32 1 3,92€
Phenolic paper 200x100 mm 1 10,00€
Resistors 6 0,60€
Capacitors 6 1,20€
Switch buttons 3 1,00€
LED 5 1,25€
Voltage regulator 2 0,5€
Diode 5 0,03€
Pin Connectors 60 15,00€
FTDI 5 0,20€
IR sensor
Photoresistor
TOTAL COST 55,90€

Video

Final

Final

πŸ”ͺ Files

Principles and Practises

Computer Aided Design

Computer Controlled Cutting

3dPrinting

Electronic production

Embedded programming

Input devices


Last update: December 1, 2022