RGB LED color reader
I am making a color detector with RGB LEDs and a time multiplexed LDR photoresistor. This project demonstrate how real life sensing systems work with modest amount of components.
Created by
CosmicDev404
Tier 4
2 views
0 followers
Iamalive 🚀
requested changes for RGB LED color reader ago
Heyo! Nice work so far :) Can you add the individual pcb files(pcb & pro) and also make a BOM.csv that lists all the item you're planning on buying along with the quanity and link to vendor? Thanks!
CosmicDev404
submitted RGB LED color reader for ship review ago
Shaurya Bisht
requested changes for RGB LED color reader ago
needs shipping options on second screenshot and also have those pcb files unzipped as well please
CosmicDev404
added to the journal ago
Updated github repo
I updated my github repository to include CAD files and BOM. Also changed folder names and file locations for clear navigation. I designed and exported my 3D models from EasyEDA. I also exported the interactive BOM from EasyEDA which shows 3D render of my PCB with all the parts. The git repo is now in accordance with custom projects shipping guidelines. The 3D render of PCB screenshots:

and the interactive BOM

CosmicDev404
submitted RGB LED color reader for ship review ago
CosmicDev404
added to the journal ago
Rerouted the PCB
I took a printout of PCB on A4 paper. The Pro mini was not sitting on the female headers...
I have Arduino Pro mini, hence I decided to insert female headers on the PCB to avoid the extra cost of an MCU board. The female headers were smaller than the pin spacing of Pro mini, hence, in real life, the Pro mini would never sit on female header. I removed the headers from pcb design and inserted 2.54mm headers. The new design is okay and Pro mini sits on printout. I have also generated the Gerber files for this design. A few more indication and instruction labellings were also made. Here are the PCB pictures.


CosmicDev404
added to the journal ago
Created schematic and PCB gerber file!
I have created the schematic and PCB design for this device using EasyEDA software. The following screenshots show them.


I have introduced modes in the code to for RGB+CMY detection and Specific color detection, hence, a MODE button is added. The RGB lights each flash independently in reading mode(will be done by the READ button). The ILLUMINATE button sends signal to the LED to emit all RGB colors, ie white, to illuminate the surface. I have also decided to show the output on an OLED display.
The schematic is:
SchematicColor2026-01-07 (1)
CosmicDev404
added to the journal ago
Introduced Modes
Since many colors were conflicting with RGB and CMY, I introduced three modes, which may be used to give either RGB color output, CMY output, point to a specific color from the list in the function or all at once. Following is the output of readings of a dark blue/indigo plastic cap in serial monitor. The output correspond to following findings(in order):
- Red color code reading
- Green color code reading
- Blue color code reading
- color from RGB
- distance from actual RGB code
- color from CMY
- distance from actual RGB code
- color from specific list
- distance from actual RGB code
The updated function signature is
public char color_mapper(int mode, int R, int G, int B)where valid value of mode is 0, 1 or 2.
The corresponding code is:
char* color_mapper(int mode, int R, int G, int B) { //passes color names for inputted RGB codes
static char color[50];
//float minimum;
float red;
float green;
float blue;
float cyan;
float magenta;
float yellow;
float orange;
float kesari; //the Indian Saffron
float CBrown;
float Lgreen;
float indigo;
float saffron;
float pink;
float wheat;
float gold;
float turquoise;
float white;
float gray;
float teal;
float plotR[3] = {255-R, G, B};
avg_arr(plotR, red);
float plotG[3] = {R, 255-G, B};
avg_arr(plotG, green);
float plotB[3] = {R, G, 255-B};
avg_arr(plotB, blue);
float plotC[3] = {R, 255-G, 255-B};
avg_arr(plotC, cyan);
float plotM[3] = {255-R, G, 255-B};
avg_arr(plotM, magenta);
float plotY[3] = {255-R, 255-G, B};
avg_arr(plotY, yellow);
float plotO[3] = {255-R, abs(165-G), B};
avg_arr(plotO, orange);
float plotK[3] = {255-R, abs(119-G), abs(34-B)};
avg_arr(plotK, kesari);
float plotCB[3] = {abs(187-R), abs(142-G), abs(81-B)};
avg_arr(plotCB, CBrown);
float plotLG[3] = {abs(50-R), abs(205-G), abs(50-B)};
avg_arr(plotLG, Lgreen);
float plotI[3] = {abs(75-R), G, abs(130-B)};
avg_arr(plotI, indigo);
float plotSaffron[3] = {abs(255-R), abs(183-G), abs(206-B)};
avg_arr(plotSaffron, saffron);
float plotpink[3] = {abs(251-R), abs(116-G), abs(168-B)};
avg_arr(plotpink, pink);
float plotwheat[3] = {abs(245-R), abs(222-G), abs(179-B)};
avg_arr(plotwheat, wheat);
float plotgold[3] = {abs(229-R), abs(184-G), abs(11-B)};
avg_arr(plotgold, gold);
float plottur[3] = {abs(4-R), abs(255-G), abs(247-B)};
avg_arr(plottur, turquoise);
float plotwhite[3] = {abs(255-R), abs(255-G), abs(255-B)};
avg_arr(plotwhite, white);
float plotgray[3] = {abs(128-R), abs(128-G), abs(128-B)};
avg_arr(plotgray, gray);
float plotteal[3] = {R, abs(128-G), abs(128-B)};
avg_arr(plotteal, teal);
if (mode==0){
float val_array[3]= {red, blue, green};
min_finder(val_array, 3, minimum);
}
if (mode==1){
float val_array[3]= {cyan, magenta, yellow};
min_finder(val_array, 3, minimum);
}
if (mode==2){
float val_array[19]= {red, blue, green, cyan, magenta, yellow, orange, kesari, CBrown, Lgreen, indigo, saffron, pink, wheat, gold, turquoise, white, gray, teal};
min_finder(val_array, 19, minimum);
}
if(minimum==cyan){ // CMY
strcpy(color, "Cyan");
}else if(minimum==magenta){
strcpy(color, "Magenta");
}else if(minimum==yellow){
strcpy(color, "Yellow");
}else if(minimum==red){ //RGB
strcpy(color, "Red");
}else if(minimum==green){
strcpy(color, "Green");
}else if(minimum==blue){
strcpy(color, "Blue");
}else if(minimum==orange){ //tirtiary colors and others
strcpy(color, "Orange");
}else if(minimum==kesari){
strcpy(color, "Kesari");
}else if(minimum==CBrown){
strcpy(color, "Cardboard Brown");
}else if(minimum==Lgreen){
strcpy(color, "Lime Green");
}else if(minimum==indigo){
strcpy(color, "Indigo");
}else if(minimum==saffron){
strcpy(color, "Saffron");
}else if(minimum==pink){
strcpy(color, "Pink");
}else if(minimum==wheat){
strcpy(color, "Wheat");
}else if(minimum==gold){
strcpy(color, "Gold");
}else if(minimum==turquoise){
strcpy(color, "Turquoise");
}else if(minimum==white){ //neutrals
strcpy(color, "White");
}else if(minimum==gray){
strcpy(color, "Gray");
}else if(minimum==teal){
strcpy(color, "Teal");
}else{strcpy(color, "!error!");} //can only occur when logic fails silently
return color;
}
CosmicDev404
added to the journal ago
Updated the color detection logic
I was using ratio based logic to detect colors. It was brain breaking to create logic for new colors, lighting conditions affected the readings wildly, sometimes it felt that the code is making a guess. Then when I was thinking about the alternate, it clicked! I decided to used vector based distance measurements to true RGB codes and output colors whose readings came nearest to an RGB code. And I immediately scribbled that idea to my notebook. The next day(today), I implemented it in my code and it worked! Now it can even detect specific shades like teal, baby pink, cardboard brown, etc.
Here are the updated logic functions that I implemented today:
char* color_mapper(int R, int G, int B) { //passes color names for inputted RGB codes
static char color[50];
float minimum;
float red;
float green;
float blue;
float cyan;
float magenta;
float yellow;
float orange;
float kesari; //the Indian Saffron
float CBrown;
float Lgreen;
float indigo;
float brown;
float baby;
float pink;
float wheat;
float gold;
float turquoise;
float white;
float black;
float gray;
float teal;
float plotR[3] = {255-R, G, B};
avg_arr(plotR, red);
float plotG[3] = {R, 255-G, B};
avg_arr(plotG, green);
float plotB[3] = {R, G, 255-B};
avg_arr(plotB, blue);
float plotC[3] = {R, 255-G, 255-B};
avg_arr(plotC, cyan);
float plotM[3] = {255-R, G, 255-B};
avg_arr(plotM, magenta);
float plotY[3] = {255-R, 255-G, B};
avg_arr(plotY, yellow);
float plotO[3] = {255-R, abs(165-G), B};
avg_arr(plotO, orange);
float plotK[3] = {255-R, abs(119-G), abs(34-B)};
avg_arr(plotK, kesari);
float plotCB[3] = {abs(187-R), abs(142-G), abs(81-B)};
avg_arr(plotCB, CBrown);
float plotLG[3] = {abs(50-R), abs(205-G), abs(50-B)};
avg_arr(plotLG, Lgreen);
float plotI[3] = {abs(75-R), G, abs(130-B)};
avg_arr(plotI, indigo);
float plotBr[3] = {abs(150-R), abs(75-G), B};
avg_arr(plotBr, brown);
float plotBpink[3] = {abs(255-R), abs(183-G), abs(206-B)};
avg_arr(plotBpink, baby);
float plotpink[3] = {abs(251-R), abs(116-G), abs(168-B)};
avg_arr(plotpink, pink);
float plotwheat[3] = {abs(245-R), abs(222-G), abs(179-B)};
avg_arr(plotwheat, wheat);
float plotgold[3] = {abs(229-R), abs(184-G), abs(11-B)};
avg_arr(plotgold, gold);
float plottur[3] = {abs(4-R), abs(255-G), abs(247-B)};
avg_arr(plottur, turquoise);
float plotwhite[3] = {abs(255-R), abs(255-G), abs(255-B)};
avg_arr(plotwhite, white);
float plotblack[3] = {abs(0-R), abs(0-G), abs(0-B)};
avg_arr(plotblack, black);
float plotgray[3] = {abs(128-R), abs(128-G), abs(128-B)};
avg_arr(plotgray, gray);
float plotteal[3] = {R, abs(128-G), abs(128-B)};
avg_arr(plotteal, teal);
float val_array[21]= {red, blue, green, cyan, magenta, yellow, orange, kesari, CBrown, Lgreen, indigo, brown, baby, pink, wheat, gold, turquoise, white, black, gray, teal};
min_finder(val_array, 21, minimum);
if(minimum==cyan){ // CMY
strcpy(color, "Cyan");
}else if(minimum==magenta){
strcpy(color, "Magenta");
}else if(minimum==yellow){
strcpy(color, "Yellow");
}else if(minimum==red){ //RGB
strcpy(color, "Red");
}else if(minimum==green){
strcpy(color, "Green");
}else if(minimum==blue){
strcpy(color, "Blue");
}else if(minimum==orange){ //tirtiary colors and others
strcpy(color, "Orange");
}else if(minimum==kesari){
strcpy(color, "Kesari");
}else if(minimum==CBrown){
strcpy(color, "Cardboard Brown");
}else if(minimum==Lgreen){
strcpy(color, "Lime Green");
}else if(minimum==indigo){
strcpy(color, "Indigo");
}else if(minimum==brown){
strcpy(color, "Brown");
}else if(minimum==baby){
strcpy(color, "Baby Pink");
}else if(minimum==pink){
strcpy(color, "Pink");
}else if(minimum==wheat){
strcpy(color, "Wheat");
}else if(minimum==gold){
strcpy(color, "Gold");
}else if(minimum==turquoise){
strcpy(color, "Turquoise");
}else if(minimum==white){ //neutrals
strcpy(color, "White");
}else if(minimum==black){
strcpy(color, "Black");
}else if(minimum==gray){
strcpy(color, "Gray");
}else if(minimum==teal){
strcpy(color, "Teal");
}else{strcpy(color, "!error!");} //can only occur when logic fails silently
return color;
}
int avg_arr(float array_to_be_averaged[], float &avg_found){ //makes the average value for the color inputted
avg_found=0;
for(int j = 0; j<3; j++){
avg_found+=array_to_be_averaged[j];
}
avg_found=avg_found/3;
return avg_found;
}
int min_finder(float ARRAY[], int size, float &smallest){ //finds the minimum value stored in input array
smallest=255;
for(int k = 0; k<size; k++){
if(ARRAY[k]<smallest){
smallest=ARRAY[k];
}
}
return smallest;
}
The notes that i used to approach this logic(sorry for the handwriting):





CosmicDev404
added to the journal ago
Updated the code to read LDR data.
I made a voltage divider circuit using LDR and 10k resistor and updated the code to normalise and read RGB data from readings. Currently, the code makes 10 readings of each of RGB(30 total) light to determine colors. It can successfully detect
- red
- yellow
- purple
- gray
- white
- & black.
I have noticed that the reflections from glossy surfaces may reflect light of very high intensity, hence, the detector, as of now is incapable for those surfaces.The following attachment shows the prototype with LDR and rubber cap to block ambient light.


Edit(same day): I have been trying to remap values of RGB lights for LDR readings. Red light is being detected very sensitively by the LDR. I reduced its brightness with PWM and remapped its values. Still, the code was unable to detect dark shades of the colors, hence, instead of threshold RGB color mapping conditions, I updated them to compare the ratios instead. Now those ratios are being calibrated to sense basic colors.
Color mapper function (returns string names for direct output):
char* color_mapper(int R, int G, int B) {
static char color[10];
int clr = R+G+B;
if (clr < 40) {
strcpy(color, "Black"); //calibration done
return color;
}
if(R>2*G && R>2*B && (G-B<=noise || B-G<=noise)){
strcpy(color, "Red"); //calibration done
}else if(G>2*B && G>2*R && (R-B<=noise || B-R<=noise)){
strcpy(color, "Green");
}else if(B>2*G && B>2*R && (G-R<=noise || R-G<=noise)){
strcpy(color, "Blue");
}else if (R-B>=noise && G<=2*noise && R>2*G){
strcpy(color, "Pink");
}else if (R-G<=noise && R>=2*B){
strcpy(color, "Yellow"); //calibration done
}else if (R-B<=noise && G<=noise && R>G){
strcpy(color, "Purple"); //calibration done
}else if (R>2*G && G>2*B && B<=noise){
strcpy(color, "Orange");
}else if (R>2*G && R-B>=2*noise && B<=noise){
strcpy(color, "Brown");
}else if (B-G<=noise && B>2*R && R<=noise){
strcpy(color, "Cyan");
}else if(B>R && B>G && R-G>=2*noise && B-R>=2*noise && B-G>=noise){
strcpy(color, "Sky Blue");
}else if(R-G<=noise && G-B<=noise && B-G<=noise){
if(R>41 && R<=205){
strcpy(color, "Gray"); //calibration done
}else{
strcpy(color, "White"); //calibration done
}
}else{
strcpy(color, "unknown");
}
return color;
}
CosmicDev404
added to the journal ago
Calibrated the RGB LED to emit lights of same intensity.
I am working on an RGB color reader/detector. I have successfully figured out the correct resistor values for my RG&B colors(150, 100 & 100 ohms respectively).
My red LED was not bright enough and green/blue were too intense against it, hence, I reduced the resistor to 100 ohms, burning the LED. I had to change the whole RGB LED but same problem persisted. I used software calibration to get it to emit the lights of same intensity via PWM. Following are the attachments for the current progress.
irl
CosmicDev404
started RGB LED color reader ago
1/3/2026 - Calibrated the RGB LED to emit lights of same intensity.
I am working on an RGB color reader/detector. I have successfully figured out the correct resistor values for my RG&B colors(150, 100 & 100 ohms respectively).
My red LED was not bright enough and green/blue were too intense against it, hence, I reduced the resistor to 100 ohms, burning the LED. I had to change the whole RGB LED but same problem persisted. I used software calibration to get it to emit the lights of same intensity via PWM. Following are the attachments for the current progress.
irl
1/4/2026 - Updated the code to read LDR data.
I made a voltage divider circuit using LDR and 10k resistor and updated the code to normalise and read RGB data from readings. Currently, the code makes 10 readings of each of RGB(30 total) light to determine colors. It can successfully detect
- red
- yellow
- purple
- gray
- white
- & black.
I have noticed that the reflections from glossy surfaces may reflect light of very high intensity, hence, the detector, as of now is incapable for those surfaces.The following attachment shows the prototype with LDR and rubber cap to block ambient light.


Edit(same day): I have been trying to remap values of RGB lights for LDR readings. Red light is being detected very sensitively by the LDR. I reduced its brightness with PWM and remapped its values. Still, the code was unable to detect dark shades of the colors, hence, instead of threshold RGB color mapping conditions, I updated them to compare the ratios instead. Now those ratios are being calibrated to sense basic colors.
Color mapper function (returns string names for direct output):
char* color_mapper(int R, int G, int B) {
static char color[10];
int clr = R+G+B;
if (clr < 40) {
strcpy(color, "Black"); //calibration done
return color;
}
if(R>2*G && R>2*B && (G-B<=noise || B-G<=noise)){
strcpy(color, "Red"); //calibration done
}else if(G>2*B && G>2*R && (R-B<=noise || B-R<=noise)){
strcpy(color, "Green");
}else if(B>2*G && B>2*R && (G-R<=noise || R-G<=noise)){
strcpy(color, "Blue");
}else if (R-B>=noise && G<=2*noise && R>2*G){
strcpy(color, "Pink");
}else if (R-G<=noise && R>=2*B){
strcpy(color, "Yellow"); //calibration done
}else if (R-B<=noise && G<=noise && R>G){
strcpy(color, "Purple"); //calibration done
}else if (R>2*G && G>2*B && B<=noise){
strcpy(color, "Orange");
}else if (R>2*G && R-B>=2*noise && B<=noise){
strcpy(color, "Brown");
}else if (B-G<=noise && B>2*R && R<=noise){
strcpy(color, "Cyan");
}else if(B>R && B>G && R-G>=2*noise && B-R>=2*noise && B-G>=noise){
strcpy(color, "Sky Blue");
}else if(R-G<=noise && G-B<=noise && B-G<=noise){
if(R>41 && R<=205){
strcpy(color, "Gray"); //calibration done
}else{
strcpy(color, "White"); //calibration done
}
}else{
strcpy(color, "unknown");
}
return color;
}
1/6/2026 7 PM - Updated the color detection logic
I was using ratio based logic to detect colors. It was brain breaking to create logic for new colors, lighting conditions affected the readings wildly, sometimes it felt that the code is making a guess. Then when I was thinking about the alternate, it clicked! I decided to used vector based distance measurements to true RGB codes and output colors whose readings came nearest to an RGB code. And I immediately scribbled that idea to my notebook. The next day(today), I implemented it in my code and it worked! Now it can even detect specific shades like teal, baby pink, cardboard brown, etc.
Here are the updated logic functions that I implemented today:
char* color_mapper(int R, int G, int B) { //passes color names for inputted RGB codes
static char color[50];
float minimum;
float red;
float green;
float blue;
float cyan;
float magenta;
float yellow;
float orange;
float kesari; //the Indian Saffron
float CBrown;
float Lgreen;
float indigo;
float brown;
float baby;
float pink;
float wheat;
float gold;
float turquoise;
float white;
float black;
float gray;
float teal;
float plotR[3] = {255-R, G, B};
avg_arr(plotR, red);
float plotG[3] = {R, 255-G, B};
avg_arr(plotG, green);
float plotB[3] = {R, G, 255-B};
avg_arr(plotB, blue);
float plotC[3] = {R, 255-G, 255-B};
avg_arr(plotC, cyan);
float plotM[3] = {255-R, G, 255-B};
avg_arr(plotM, magenta);
float plotY[3] = {255-R, 255-G, B};
avg_arr(plotY, yellow);
float plotO[3] = {255-R, abs(165-G), B};
avg_arr(plotO, orange);
float plotK[3] = {255-R, abs(119-G), abs(34-B)};
avg_arr(plotK, kesari);
float plotCB[3] = {abs(187-R), abs(142-G), abs(81-B)};
avg_arr(plotCB, CBrown);
float plotLG[3] = {abs(50-R), abs(205-G), abs(50-B)};
avg_arr(plotLG, Lgreen);
float plotI[3] = {abs(75-R), G, abs(130-B)};
avg_arr(plotI, indigo);
float plotBr[3] = {abs(150-R), abs(75-G), B};
avg_arr(plotBr, brown);
float plotBpink[3] = {abs(255-R), abs(183-G), abs(206-B)};
avg_arr(plotBpink, baby);
float plotpink[3] = {abs(251-R), abs(116-G), abs(168-B)};
avg_arr(plotpink, pink);
float plotwheat[3] = {abs(245-R), abs(222-G), abs(179-B)};
avg_arr(plotwheat, wheat);
float plotgold[3] = {abs(229-R), abs(184-G), abs(11-B)};
avg_arr(plotgold, gold);
float plottur[3] = {abs(4-R), abs(255-G), abs(247-B)};
avg_arr(plottur, turquoise);
float plotwhite[3] = {abs(255-R), abs(255-G), abs(255-B)};
avg_arr(plotwhite, white);
float plotblack[3] = {abs(0-R), abs(0-G), abs(0-B)};
avg_arr(plotblack, black);
float plotgray[3] = {abs(128-R), abs(128-G), abs(128-B)};
avg_arr(plotgray, gray);
float plotteal[3] = {R, abs(128-G), abs(128-B)};
avg_arr(plotteal, teal);
float val_array[21]= {red, blue, green, cyan, magenta, yellow, orange, kesari, CBrown, Lgreen, indigo, brown, baby, pink, wheat, gold, turquoise, white, black, gray, teal};
min_finder(val_array, 21, minimum);
if(minimum==cyan){ // CMY
strcpy(color, "Cyan");
}else if(minimum==magenta){
strcpy(color, "Magenta");
}else if(minimum==yellow){
strcpy(color, "Yellow");
}else if(minimum==red){ //RGB
strcpy(color, "Red");
}else if(minimum==green){
strcpy(color, "Green");
}else if(minimum==blue){
strcpy(color, "Blue");
}else if(minimum==orange){ //tirtiary colors and others
strcpy(color, "Orange");
}else if(minimum==kesari){
strcpy(color, "Kesari");
}else if(minimum==CBrown){
strcpy(color, "Cardboard Brown");
}else if(minimum==Lgreen){
strcpy(color, "Lime Green");
}else if(minimum==indigo){
strcpy(color, "Indigo");
}else if(minimum==brown){
strcpy(color, "Brown");
}else if(minimum==baby){
strcpy(color, "Baby Pink");
}else if(minimum==pink){
strcpy(color, "Pink");
}else if(minimum==wheat){
strcpy(color, "Wheat");
}else if(minimum==gold){
strcpy(color, "Gold");
}else if(minimum==turquoise){
strcpy(color, "Turquoise");
}else if(minimum==white){ //neutrals
strcpy(color, "White");
}else if(minimum==black){
strcpy(color, "Black");
}else if(minimum==gray){
strcpy(color, "Gray");
}else if(minimum==teal){
strcpy(color, "Teal");
}else{strcpy(color, "!error!");} //can only occur when logic fails silently
return color;
}
int avg_arr(float array_to_be_averaged[], float &avg_found){ //makes the average value for the color inputted
avg_found=0;
for(int j = 0; j<3; j++){
avg_found+=array_to_be_averaged[j];
}
avg_found=avg_found/3;
return avg_found;
}
int min_finder(float ARRAY[], int size, float &smallest){ //finds the minimum value stored in input array
smallest=255;
for(int k = 0; k<size; k++){
if(ARRAY[k]<smallest){
smallest=ARRAY[k];
}
}
return smallest;
}
The notes that i used to approach this logic(sorry for the handwriting):





1/6/2026 9 PM - Introduced Modes
Since many colors were conflicting with RGB and CMY, I introduced three modes, which may be used to give either RGB color output, CMY output, point to a specific color from the list in the function or all at once. Following is the output of readings of a dark blue/indigo plastic cap in serial monitor. The output correspond to following findings(in order):
- Red color code reading
- Green color code reading
- Blue color code reading
- color from RGB
- distance from actual RGB code
- color from CMY
- distance from actual RGB code
- color from specific list
- distance from actual RGB code
The updated function signature is
public char color_mapper(int mode, int R, int G, int B)where valid value of mode is 0, 1 or 2.
The corresponding code is:
char* color_mapper(int mode, int R, int G, int B) { //passes color names for inputted RGB codes
static char color[50];
//float minimum;
float red;
float green;
float blue;
float cyan;
float magenta;
float yellow;
float orange;
float kesari; //the Indian Saffron
float CBrown;
float Lgreen;
float indigo;
float saffron;
float pink;
float wheat;
float gold;
float turquoise;
float white;
float gray;
float teal;
float plotR[3] = {255-R, G, B};
avg_arr(plotR, red);
float plotG[3] = {R, 255-G, B};
avg_arr(plotG, green);
float plotB[3] = {R, G, 255-B};
avg_arr(plotB, blue);
float plotC[3] = {R, 255-G, 255-B};
avg_arr(plotC, cyan);
float plotM[3] = {255-R, G, 255-B};
avg_arr(plotM, magenta);
float plotY[3] = {255-R, 255-G, B};
avg_arr(plotY, yellow);
float plotO[3] = {255-R, abs(165-G), B};
avg_arr(plotO, orange);
float plotK[3] = {255-R, abs(119-G), abs(34-B)};
avg_arr(plotK, kesari);
float plotCB[3] = {abs(187-R), abs(142-G), abs(81-B)};
avg_arr(plotCB, CBrown);
float plotLG[3] = {abs(50-R), abs(205-G), abs(50-B)};
avg_arr(plotLG, Lgreen);
float plotI[3] = {abs(75-R), G, abs(130-B)};
avg_arr(plotI, indigo);
float plotSaffron[3] = {abs(255-R), abs(183-G), abs(206-B)};
avg_arr(plotSaffron, saffron);
float plotpink[3] = {abs(251-R), abs(116-G), abs(168-B)};
avg_arr(plotpink, pink);
float plotwheat[3] = {abs(245-R), abs(222-G), abs(179-B)};
avg_arr(plotwheat, wheat);
float plotgold[3] = {abs(229-R), abs(184-G), abs(11-B)};
avg_arr(plotgold, gold);
float plottur[3] = {abs(4-R), abs(255-G), abs(247-B)};
avg_arr(plottur, turquoise);
float plotwhite[3] = {abs(255-R), abs(255-G), abs(255-B)};
avg_arr(plotwhite, white);
float plotgray[3] = {abs(128-R), abs(128-G), abs(128-B)};
avg_arr(plotgray, gray);
float plotteal[3] = {R, abs(128-G), abs(128-B)};
avg_arr(plotteal, teal);
if (mode==0){
float val_array[3]= {red, blue, green};
min_finder(val_array, 3, minimum);
}
if (mode==1){
float val_array[3]= {cyan, magenta, yellow};
min_finder(val_array, 3, minimum);
}
if (mode==2){
float val_array[19]= {red, blue, green, cyan, magenta, yellow, orange, kesari, CBrown, Lgreen, indigo, saffron, pink, wheat, gold, turquoise, white, gray, teal};
min_finder(val_array, 19, minimum);
}
if(minimum==cyan){ // CMY
strcpy(color, "Cyan");
}else if(minimum==magenta){
strcpy(color, "Magenta");
}else if(minimum==yellow){
strcpy(color, "Yellow");
}else if(minimum==red){ //RGB
strcpy(color, "Red");
}else if(minimum==green){
strcpy(color, "Green");
}else if(minimum==blue){
strcpy(color, "Blue");
}else if(minimum==orange){ //tirtiary colors and others
strcpy(color, "Orange");
}else if(minimum==kesari){
strcpy(color, "Kesari");
}else if(minimum==CBrown){
strcpy(color, "Cardboard Brown");
}else if(minimum==Lgreen){
strcpy(color, "Lime Green");
}else if(minimum==indigo){
strcpy(color, "Indigo");
}else if(minimum==saffron){
strcpy(color, "Saffron");
}else if(minimum==pink){
strcpy(color, "Pink");
}else if(minimum==wheat){
strcpy(color, "Wheat");
}else if(minimum==gold){
strcpy(color, "Gold");
}else if(minimum==turquoise){
strcpy(color, "Turquoise");
}else if(minimum==white){ //neutrals
strcpy(color, "White");
}else if(minimum==gray){
strcpy(color, "Gray");
}else if(minimum==teal){
strcpy(color, "Teal");
}else{strcpy(color, "!error!");} //can only occur when logic fails silently
return color;
}
1/8/2026 11 AM - Created schematic and PCB gerber file!
I have created the schematic and PCB design for this device using EasyEDA software. The following screenshots show them.


I have introduced modes in the code to for RGB+CMY detection and Specific color detection, hence, a MODE button is added. The RGB lights each flash independently in reading mode(will be done by the READ button). The ILLUMINATE button sends signal to the LED to emit all RGB colors, ie white, to illuminate the surface. I have also decided to show the output on an OLED display.
The schematic is:
SchematicColor2026-01-07 (1)
1/8/2026 2 PM - Rerouted the PCB
I took a printout of PCB on A4 paper. The Pro mini was not sitting on the female headers...
I have Arduino Pro mini, hence I decided to insert female headers on the PCB to avoid the extra cost of an MCU board. The female headers were smaller than the pin spacing of Pro mini, hence, in real life, the Pro mini would never sit on female header. I removed the headers from pcb design and inserted 2.54mm headers. The new design is okay and Pro mini sits on printout. I have also generated the Gerber files for this design. A few more indication and instruction labellings were also made. Here are the PCB pictures.


1/9/2026 - Updated github repo
I updated my github repository to include CAD files and BOM. Also changed folder names and file locations for clear navigation. I designed and exported my 3D models from EasyEDA. I also exported the interactive BOM from EasyEDA which shows 3D render of my PCB with all the parts. The git repo is now in accordance with custom projects shipping guidelines. The 3D render of PCB screenshots:

and the interactive BOM
