A Reflection on summer of 2018

Since it’s almost a few days until the end of summer 2018, it’s prime time to do some reflecting!

All in all, the past 4 months was pretty great. I got a summer position working on an industrial project as a Research Assistant, at the University Robotics/Computer Vision (CV) Lab. This has greatly widened my understanding for CV. Though I have still got far to go and much to learn, having the opportunity to develop software within such a great team has been a wonderful experience. I’m truly thankful for this opportunity and for all the help that I have received.

The industrial project I worked on aims at solving the particle detection, classification and tracking problem to improve oil sands process yield. In detection, the particles within the image should be detected, measured and distinguished from the background. In classification, the particles should be classified into 1 of the 4 categories: sand, bitumen, bubbles and unknown based on the output of a CNN classifier. In tracking, the particles should be tracked across frames in a video. Since this project has been on-going for 2 years before I joined, there is a lot of materials to pick up on – database, webUI tools, in-house libraries, folder setup and software pipeline. Quite overwhelming at start, but gradually familiarize with the project as I worked more on it.


I worked on resolving the incorrect particle size distribution (PSD) during detection. When a dataset with known ground truth PSD is input into the gold standard – current software pipeline for detection, an incorrect PSD histogram plot is generated (see example plot below). 2 observations can be made about the PSD plot:

  • The PSD is shifted right (peak x is offset towards a higher value).
  • The PSD has extended tails (larger span).

Particle diameter vs. particle count for a 300 – 335 um ground truth dataset.

The Deblur Method:

The first method tried, to adjust and align with the ground truth PSD, is deblurring. Because in the particle images, there appears to be a ring of blur surrounding the particle. This can result in an overestimate of the diameter measurement. The deblur is performed by deconvolution using the Richardson Lucy algorithm. Briefly, this is an iterative algorithm that recomputes a correction factor for each pixel and multiplies each pixel with the correction factor. The correction factor approaches 1 as the desired, deconvolved image is obtained. Naturally, the Guassian kernel is used. There are 3 parameters to tune and define in using this algorithm: kernel size, kernel standard deviation and number of iterations. I investigated that selecting different kernel sizes and standard deviations can generate ringing artifacts at the border, while different number of iterations don’t seem to generate significant effect on deblur. Below shows the deblur results after integrating the algorithm with the rest of the software pipeline. The deblur effect is more obvious in particle image than the frame image.


Though the particle is deblurred, the PSD histogram remains the same.


Also, to be extra sure that our algorithm is performing correctly, I also conducted the following 2 experiments:

  • Experiment 1: Generate synthetic deblur particle > apply Gaussian blur > apply RL deblur algorithm.
  • Experiment 2: Input real particle image > apply Gaussian blur > apply RL deblur algorithm.

In both experiments, I wanted to make sure the end result matched the input result, which it did. Therefore, deblur is not the problem for the incorrect PSD histogram.

The Particle Density and OOC Analysis:

In order to find another approach to tackle the PSD problem, I needed to investigate the problem more. The question we want to answer here is: “what is the relation between frame density (number of particles in frame) and the total number of out-of-control (OOC) particles?”

The database is used to query all article count per frame for the x axis, and all OCC particles (particles in that frame with diameter outside the ground truth range) for the y axis (see example plots below). Overall, there exists a linear relation (y=x) between density and OCC. This shows that the OCC particles can be due to the fusing of glass bead particles (clumping), manufacture defects and partial occlusion of particles. Not just clumping. If clumping was the only problem, then the plot should have a downward sloping curve as density increases.


150-180 um (top left), another 150-180 um (top right), 300-335 um (bottom left), 300-335 um (bottom right) experiments.

To further understand OOC particles, I created a dataset from 100 hand labeled circular particles, from a 300-335 um experiment. However, its histogram shows that these well-formed circular particles also fall outside the ground truth range of 300 – 335 um.

ooc1 Also, I created a different dataset from 100 hand labeled clumped particles, from a 300-335 um experiment. However, its histogram shows that these deformed particles also fall inside the ground truth range of 300 – 335 um.


These histogram results re-emphasize that well-formed AND deformed particles can reside both inside and outside of the ground truth range. 😕

The takeaway from this analysis is that a “perfect” detection algorithm would correct the occlusion and clumping problem, but the underlying manufacture defects would not result in a ground truth PSD.

The LDA and Neural Networks Method:

Following the above analysis results, the second method tried, to adjust and align with the ground truth PSD, is LDA Classifier. Because the PSD has 2 incorrect properties, as mentioned above: shifted right and extended tails, I wanted to see if training a classifier to discern the well-formed and deformed particles would truncate the extended tails and possibly shift the histogram peak leftwards. Briefly, LDA is a supervised algorithm that can be used as a classifier to project data onto a linear decision boundary that maximizes the separation between classes. LDA was the first choice mainly because of its simplicity to use – no hyperparameters to tune, and fast computation time. It is used as a first pass approach to solve the classification problem, if LDA can classify with high accuracy than this might not be a hard problem. Also, traditional techniques should always be investigated first before implementing any neural network (NN) techniques.

I started by building a training dataset of 20 valid (circular) and 20 invalid (anything noncircular) for each experiment (include all diameter ranges). A total of +200 particle images were hand-labeled. Then I bootstrap the LDA to create a larger dataset of 4651 particles. After training, I investigated the effect of the number of attributes input into a classifier on PSD and accuracy, in which the performance of a 9 multi-class attribute is on par with a 2 class attributes with solidity and eccentricity (2 most weighted attributes). I also performed an ablation study to determine the input size, in which accuracy converged when number of particles is 3500.


This plot shows the accuracy per class performance, weights per feature and ablation study.


At first, the LDA accuracy is only 92.02 %. I did a failure case analysis by gathering the 7.98 % of images that LDA labeled incorrectly. From the images, by visual inspection,  almost half of the images have been incorrectly labeled. This prompted a refine of the training dataset. But instead of re-labelling the entire 4651 images, I passed in the entire dataset for testing and re-labeled the 7.98 % or 372 failure case images. With this new dataset, the LDA achieved an accuracy of 95.39 %. This can be further improved by improving the input dataset. See below for a comparison of PSD histogram before and after LDA classifier.


Before applying LDA with 40 particle included in histogram (left) and after apply LDA (right).


From LDA classifier.

Next, I tried several other classification methods. I trained a SVM classifier using the same training dataset above. SVM is selected for its nonlinear decision boundary characteristic. I selected a degree 6 polynomial kernel and achieved an accuracy of 92.7%. Also, I input the 9 multi-class attribute values to a MLP Network with: 4 hidden layers, selu activation, alpha dropout of 0.2, Adam optimizer and 20 epochs. The accuracy = 93.21 %. Then, I trained a CNN with: 4 hidden layers, selu activation, alpha dropout of 0.2, Adam optimizer, locally connected layer 64 and 100 epochs. The accuracy = 94.29 %. However, LDA still wins with 95.39% accuracy.

The takeaway from the LDA classifier is that it can perform classification with high accuracy. The LDA truncates the extended tails and provides a calibration constant to shift the PSD peak closer to the ground truth PSD peak. Therefore, LDA is a feasible method to resolve the incorrect PSD problem.

All in all, I have learned lots. And I’m excited for new semester to come, where I will be learning more on software development and OS concepts. 😀


Inductance and capacitance measurement circuit

Since we made an inductance and capacitance measurement PCB in the previous post, lets talk a little more about it in this post.
The question I want to answer in this post is: “How does it work?”
Short Answer: It measures capacitance through RC charge time, and inductance through LC oscillation frequency.

Long Answer: First, it requires an input square wave. This square wave is generated by a Schmitt trigger circuit via an astable process. The duty cycle of the square wave depends on the resistor and capacitor values, which determines the charge and discharge rate, hence the on and off time of the square wave. Second, a RC charging circuit is used to determine the unknown capacitance value. A capacitor will require a certain time delay to elapse before it is fully charged. This time delay is known as τ, and for a RC charging circuit τ=RC. The capacitor will charge up to 63 % of the input voltage when τ has elapsed, and charge up to 100% of the input voltage when 5τ has elapsed. Therefore, based on τ=RC and on when the capacitor has charged up to 63% of the input voltage, the unknown capacitance value can then be deduced. Finally, a LC tank circuit is used to determine the unknown inductance value. Because the reactance of a capacitor decreases with frequency while the reactance of an inductor increases with frequency, there exists 1 frequency where the 2 reactances are equal. This is the oscillation frequency fr = 1/(2π(LC)½). Therefore, based on the oscillation frequency, the unknown inductance value can then be deduced.




The RC charging circuit is used to determine the unknown capacitance value based on the time delay τ.


The LC tank circuit is used to determine the unknown inductance value based on the oscillation frequency.


Make Your Own PCBs

This post has its origin from :

Supervisor: “Can you setup a PCB work station for the lab?” 🙂

There are lots of resources on this topic, which can be found on Google by searching: “Make your own PCB station.” However, the amount of information can be overwhelming. After some trial and error, I found the following components most economical and accessible (in Canada), and that the following steps worked best for me.

For those that don’t know what PCBs are, it stands for Printed Circuit Board. It provides mechanical and electrical support for the electronic components using conductive tracks, pads and other features etched from the copper substrate. The breadboard prototyping wires are replaced by conductive tracks, and the components are connected by soldering. The design of a PCB is all about tradeoffs between factors such as signal integrity, routability, size, cost of manufacturing etc. This post will not go through the design aspects, as a stand alone post dedicated to this topic is required.


Lets delve into the physical layers of a PCB:

  • Substrate layer: This provides dielectric isolation. For most low-frequency applications FR-4 substrate is used. This is because FR4 dielectric constant stability varies from PCB to PCB, and over frequency range. Furthermore, FR-4 is considered a higher loss board compared to other RF PCB. With that said, signal loss is proportional to trace length. So with short tracks, some HF signals can be designed using FR4 boards without signal integrity issues. The thickness of the substrate is selected based on compactness, connection configuration, impedance matching, flexibility, component compatibility and weight. The connection configuration refers to edge connectors, which may require the right thickness to physically fit the connector. The impedance matching refers to the capacitor behavior between traces on adjacent layers, where the thickness of the board defines the thickness of the dielectric, thus the value of the capacitance. The component compatibility refers to through-hole components. Overall, FR4 boards are the most common for their low cost and durability.
  • Copper layer (top and/or bottom): This provides conductivity for the traces. A thin copper foil is laminated to the PCB with heat and adhesive. The copper thickness can vary and is specified by weight, in ounces per square foot. Most PCB have 1 ounce of copper per square foot, but some PCB handling high power may use up to 2 or 3 ounces of copper per square foot.
  • Soldermask (top and/or bottom): This helps to insulate the copper traces from accidental contact with other metal solder or solder jumpers. This layer gives the PCB its green (most of the time) color. It is overlaid onto most of the PCB covering the tracks, but leaving the pads exposed so the components can be soldered on.
  • Silkscreen (top and/or bottom): This provides labelling of letters, number and symbols on the PCB.


Popular PCB layout and design software are:

  • Cadsoft Eagle. (Popular and free)
  • Ki-CAD.
  • Altium.

PCB Workstation:

  1. Thin glossy paper (HP Premium presentation laser paper from Staples).
  2. Laser printer (Print with mono color setting).
  3. Isopropyl alcohol 99% or acetone or fine sand paper (SandWet Ultra Fine-600 grit from Home Depot).
  4. Laminator (Swingline Inspire Staples).
  5. Copper board (Rogers).
  6. Ammonia persulfate (NH)4S2O8 (410-1 kg MG-chemicals).
  7. Kitchen scrubs.
  8. Gloves.
  9. Etching system (Circuit specialists ET20).


PCB Etching:

  1. Print the board layout on a glossy thin paper using the laser printer. Make sure to mirror the PCB layout for front/back designs. Try not to touch the printed layout as oil from the finger can prevent the transfer of the toner onto the copper board.
  2. Important Step: Clean the copper board with either isopropyl alcohol, acetone or fine sand paper. I find that fine sand paper works best.
  3. Tape the glossy paper (ink face down) to the cleaned copper board.
  4. Turn on the laminator, wait until it has reached its operating temperature. This is usually indicated by a LED. I found laminator works much better than an iron.
  5. Important Step: Continuously feed the board into the laminator for ~10 minutes. This worked well for a ~5×5 cm board. Longer time maybe required for larger and thicker boards.
  6. Gently rinse the board under cold water and wash away the glossy paper. The ink portion with the board layout should stick onto the copper.
  7. Check the ink transfer. Insufficient ink transfer can be applied with an etch resist pen or black sharpie or duct tape (for larger areas). If a ground layer is desired, cover the copper with duct tape to prevent etching.
  8. Set up the etching system in a ventilated area. The tank requires ~2 L of water in order to fully immerse the heater (see the indication line on the immersion heater and this is specific to the tank) to prevent the temperature difference from breaking the heater glass. Then, 440 g of ammonia persulfate with 2 L of water is able to etch 120 g of copper, which translates to a copper area of 1913 cm2 (mass = density * volume). Where the copper density is 8.96 g/cm3, and a copper standard thickness of 35 um is assumed. Therefore, depending on the PCB size and copper thickness, the amount of ammonia persulfate can be varied. The etching solution can be re-used; however, the efficiency of the etching solution degrades as the etching solution slowly decomposes. Therefore, the time required and uniformity of the etch will degrade over time.
  9. Stir the solution. This helps with the uniformity of the etch.
  10. Plug in the water heater, and turn the temperature knob to 40 °C. This helps to speed up the etching process. Note that if the etching solution is crystallised at the bottom of the tank, lower the temperature.
  11. Plug in the 120 V agitating air pump, and immerse its tube into the solution. This also helps to speed up the etching process and uniformity of the etch.
  12. Let the heater warm for ~5 min.
  13. Clip the PCB onto the copper wire and fully immerse it into the solution, at about mid-way through the tank.
  14. Depending on the size of the board and the quality of the etching solution, the etch can take up to 5 – 15 min. The etching process is exponential. This means at first nothing seems toe be etched, and once a certain time has passed it etches in exponentially fast.
  15. When the copper is removed, it reacts with the ammonia persulfate to create copper sulfate CuSO4, which gives a deep blue color. The more the copper is removed, a deeper blue color appears. This solution is a hazardous waste and needs to be disposed appropriately in a safe manner (NOT down the drain).


I made an inductance and capacitance measurement circuit to test out the PCB station.

Step 1) Prepare PCB layout.


Cadsoft Eagle schematic.


CadSoft Eagle PCB layout overall (left), top layer mirrored (mid) and bottom layer (right).

Step 2) Clean the board.


Step 3-5) Tape to board and feed through laminator.


Step 6-7) Rinse board under water and check ink transfer.


Step 8-15) Setup etching station and the etching process.


Completed PCB.


Arduino and ATtiny Serial Communication

The question I want to answer in this post is: “How to debug the ATtiny?”
Short Answer: One way to debug is to directly send and view the output of the pin via SoftwareSerial in Arduino IDE.

Long Answer: The Serial.print function is not available with the Arduino IDE when it is configured as ISP. The only commands supported by Arduino IDE when it is configured as ISP to program the ATtiny are:

  • pinMode()
  • digitalWrite()
  • digitalRead()
  • analogRead()
  • analogWrite()
  • shiftOut()
  • pulseIn()
  • millis()
  • micros()
  • delay()
  • delaymicroseconds()
  • SoftwareSerial

Furthermore, the ATtiny does not have a built in hardware UART thus is unable to provide direct hardware support for serial communication. The ATtiny has an USI that can be used to facilitate I2C and SPI communication, which are commonly used to link additional chips when there are insufficient I/O pins. Therefore, the Serial object is unavailable. Luckily, there are still several ways to achieve Serial communication between the ATtiny and Arduino IDE:

  1. TinyDebugSerial.
  2. TinyISP.
  3. SoftwareSerial.

This project will show how to achieve Serial communication between the ATtiny 85 and Arduino IDE using the SoftwareSerial method. It emulates the UART by allowing serial communication on the digital pins 0 (Rx) and 1 (Tx) of the Arduino, and using software to replicate the UART functionality (hence the name Software Serial).

Compared to the TinyDebugSerial, the SoftwareSerial method occupies more memory and may require TinyTuner to tune the internal oscillator. This is because, the internal oscillator of an ATtiny from the factory can be off by as much as 10%. This inaccuracy in the clock period can skew the rate of the data sent to and from the board, thus creates strange characters in the serial monitor. Compared to the TinyISP, the SoftwareSerial does not require the purchase of an additional hardware (if Arduino is readily available). The TinyISP is a replacement for the ArduinoISP. With TinyISP a serial/usb converter is already built onto the chip to provide direct serial communication.

It is important to note that prior to using the SoftwareSerial library, the Arduino needs to already be configured as ISP, the correct chip is selected, the correct internal oscillator frequency is selected and the burn bootloader command has been executed.



  • The diagram provided is only an initial circuit illustration. Follow the steps below for a complete circuit implementation.
  • Resistor 220 Ω:  between the Arduino Tx pin and the ATtiny Tx pin. This provides isolation between the serial lines. A larger value resistor will also work.

Config Serial Communication:

  1. Make sure the Arduino Tx and Rx pins are not connected. The only wires connected between from Arduino to ATtiny are from Digital pins 10, 11, 12, 13, Reset to capacitor, power and ground. Since the Arduino IDE uploads the code on the same Tx and Rx lines, the Tx 1 and Rx 0 pins can not be connected to the ATtiny while uploading the sketch.With serial, only one device can be communicating on the line at a time.
  2. Upload the Arduino code.
  3. Connect Reset directly to ground (not through a capacitor). This forces the Arduino into reset to perform serial communication with the ATtiny.
  4. Connect the Tx pin declared in the Arduino code to the Rx pin on the Arduino (Digital pin 0 Rx).
  5. Connect the Tx pin on the Arduino (Digital pin 1 Tx).
  6. Open the Arduino serial monitor. Make sure the baud rate is set correctly as per the code.
  7. Repeat steps 2-7 every time the Arduino is re-connected.

Arduino Script:


const int LED = 0;
const int Rx = -1; //unused Rx
const int Tx = 3;
const int inputPin = 2; //wire act like antenna
SoftwareSerial mySerial(Rx, Tx);
const float VCC = 4.98;
int val = 0;

void setup()
pinMode(Rx, INPUT);
pinMode(Tx, OUTPUT); //initialize internal pull-up resistor
pinMode(LED, OUTPUT);
pinMode(inputPin, INPUT);

void loop()
val = analogRead(inputPin);


The Arduino is reading the values from the ATtiny wire and displaying it in the serial monitor with 9600 baud rate.



Arduino serial monitor.


Arduino and ATtiny

Lets play with the ATtiny! An IC from the family of Atmel’s AVR microcontroller. This will be a start to the many exciting projects to come, as microcontrollers are, without a doubt, amazing little things. Tricky to program, but are versatile, powerful and extremely tiny. We will start by showing how to use the Arduino to load programs onto the ATtiny.

The Arduino we have uses an ATmega 328 AVR microcontroller to provide a great and easy to use, hardware and software platform. But for projects that require compactness, low-cost and stand-alone characteristics, a microcontroller is the way to go. Unlike Arduino that comes with a USB port for “plug-and-play” capabilities, the ATtiny is just an IC by itself. It requires a piece of programmer hardware with USB port to interface with the laptop. Fortunately, the Arduino can be configured as an ISP (in-system programmer) to load programs onto the ATtiny. In fact, the ATtiny 44/45/84/85 can all be programmed with the Arduino (Atmega 328). However, there are some limitations to be noted:

  1. Number of I/O pins: The Atmega 328 is a 28 pin chip, the ATtiny 45 and 85 are 8 pin chips, and the ATtiny 44 and 84 are 14 pin chips. Therefore, this can limit the complexity of the hardware.
  2. Flash Memory: The Atmega 328 has 32 KB, the ATtiny 45 has 4 kB, and the ATtiny 85 has 8 KB. Therefore, this can limit the size of the software.
  3. RAM: The Atmega 328 has 2 KB, the ATtiny 45 has 256 bytes, and the ATtiny 85 has 512 bytes. Therefore, this can limit the size of the data received.
  4. Arduino Functions Available: There are only a limited commands supported, see here.





  • The diagram provided is only an initial circuit illustration. Additional components (i.e.: capacitor and LED) will be appended.
  • 10 uF capacitor: between reset and ground pin of the Arduino. This prevents resetting, which starts the bootloader. The capacitor ensures the Arduino IDE talks to the ArduinoISP, not the bootloader, during the upload of sketches.
  • LED and resistor: between ATtiny pin 0 and ground.

Config Arduino as ISP:

  1. Make sure the correct Arduino and COM port are correctly selected, and indicated on the display in the bottom right of the Arduino window.
  2. Arduino > File > Example > Open Arduino as ISP sketch > Upload sketch.
  3. Place filter capacitor between Arduino Reset pin and ground pin.
  4. Download the attiny45_85.zip files here.
  5. Arduino > File > Preferences > Change sketchbook location to where the downloaded ATtiny files will be saved.
  6. In the sketchbook folder > Create new folder called Hardware > Unzip attiny45_85.zip.
  7. Close and restart Arduino.
  8. Arduino > Tools > Board > ATtiny 85 > Clock: Internal 8 MHz > Arduino as ISP .
  9. Place resistor and LED at ATtiny pin 0 to ground.
  10. Arduino > Example > Basics > Blink > Change all Pin 13 to Pin 0 > Upload.
  11. The LED should blink at the rate it is set at.


The Arduino is now ready to program the ATtiny. The ATtiny is used to blink 2 single LEDS


Arduino temperature and FSR sensor

Here, we discover the use of other sensors with Arduino. The FSR and temperature sensors are used in conjunction to capture both pressure and temperature information. The temperature sensor used here is a solid-state non-contact type of sensor, which measures the radiation of a heat source through changes in the base-emitter junction of the transistor. Therefore, the amplitude of the signal is proportional to the changes in the temperature. The FSR (Force Sensitive Resistor) is a passive contact type of sensor, which measures the weight through physically asserting pressure to change the resistive value. Therefore, the resistance is inversely proportional to the force applied to the FSR.



Arduino Script:

const int FSR_PIN = A1;
const float VCC = 4.98;
const float R_DIV = 1000.00;

const int tempPIN = A0;
const int LEDR = 8;
const int LEDG = 10;

void setup() {
 pinMode(FSR_PIN, INPUT);
 pinMode(tempPIN, INPUT);
 pinMode(LEDR, OUTPUT);
 pinMode(LEDG, OUTPUT);

void loop() {
 int fsrADC = analogRead(FSR_PIN);
 int tempADC = analogRead(tempPIN);

 if (fsrADC !=0 & tempADC != 0)
 float fsrV = fsrADC * VCC / 1023; //10 bit ADC
 float fsrR = R_DIV *((VCC - fsrV) / fsrV); //from voltage divide equation

 float tempV = tempADC * VCC / 1023; // 10 bit
 float degC = tempV / 0.01; // LM35 transfer function Vout = 10 mV/C * Temp

 if (degC = 23)
 digitalWrite(LEDR, HIGH);
 digitalWrite(LEDR, LOW);
 digitalWrite(LEDG, HIGH);
 digitalWrite(LEDG, LOW);
 Serial.print("Reistance: " + String(fsrR) + " ohms");
 Serial.println(" Deg: " + String(degC) + " C ");

Python plot script:

import serial #Library for serial comm
import numpy as np #Library for array
import matplotlib.pyplot as plt #Library for plotting
from drawnow import * 

arduinoData = serial.Serial('com3', 9600)
Tarray = [] #empty array for arduino data
Rarray = []
plt.ion() #Tell matplotlib you want interactive mode to plot live data
cnt = 0

def makeFig(): #create function to make plot
 plt.ylim(20, 30)
 plt.title('My real-time sensor data')
 plt.ylabel('Temp C')
 plt.xlabel('N point')
 plt.plot(Tarray,'ro-', label='Degrees C')
 plt.legend(loc='upper left')

 plt2 = plt.twinx() #create twin of plt
 plt2.plot(Rarray, 'b^-', label='Resistance ohm')
 plt2.set_ylabel('Resistance ohms')
 plt2.legend(loc='upper right')
 plt2.ticklabel_format(useOffset = False) #Force matplotlib to not autoscale yaxis.

while True: #While loop that loops forever
 while(arduinoData.inWaiting() == 0): #Wait until there is data
 pass #do nothing
 arduinoString = arduinoData.readline() #read the serial data
 dataArray = arduinoString.split(',') #split into array
 R = float(dataArray[0])
 T = float(dataArray[1])
 Tarray.append(T) #build array
 drawnow(makeFig) #call function
 plt.pause(0.000001) #pause drawnow for short time
 cnt = cnt + 1
 if (cnt = 50):


  • Measure the temperature from temperature sensor and the resistance from the FSR. We are unable to convert to resistance to force because we don’t have weights to perform the calibration.
  • The temperature is relatively stable at 25 C, while the resistance has a higher fluctuation.



Arduino Color Sensor

Color sensors can be found in various applications from environmental sensing to general robotics. For example, color sensors can be used to monitor the growth of algae, to sort items according to their color, or to determine light absorption through a medium. Color is not inherent to objects. Rather, the surface and material of an object reflect some light and absorb all others, in which the reflected light defines the color we see. In other words, color is different wavelengths of light reflected off an object. The reflection of the different combinations of wavelength, due to the material of the object, results in different colors.


The range of frequency visible to the human eye is known as the visible spectrum, which ranges from about 400 nm to 700 nm, and defines the spectral of colors we see. However, the primary colors are Red, Green and Blue (RGB). By varying the amount of RGB light, all of the colors in the visible spectrum can be produced.


Here, a simple color sensor is built and follows the project shown here.


* A RGB 4 pin common cathode LED is used here.


Circuit schematic (D = digital, A = analog pins).

Arduino Script:

//Define color sensor LED pins
int ledArray[] = {2,3,4};

//boolean to know if the balance is set
boolean balanceSet = false;

//place holders for color detected
int red =0;
int green =0;
int blue =0;

//floats to hold color arrays
float colourArray[] = {0,0,0};
float whiteArray[] = {0,0,0};
float blackArray[] = {0,0,0};

//place holder for average
int avgRead;

void setup(){


void loop(){


void checkBalance(){
if(balanceSet == false){

void setBalance(){
//set white balance
for(int i=0; i=2; i++){
getReading(5); //function defined later
whiteArray[i] = avgRead; //defined in getReading()
//set black balance
for(int i=0; i=2; i++){

//iterate thru RGB to find the color reflectivity
void checkColour(){
for(int i=0; i=2; i++){
colourArray[i] = avgRead;
float greyDiff = whiteArray[i] - blackArray[i];
colourArray[i] = (colourArray[i] - blackArray[i])/(greyDiff)*255;
digitalWrite(ledArray[i], HIGH);

void getReading(int times){
int reading;
int tally=0;
for(int i=0; i=times; i++){
reading = analogRead(0);
tally = reading + tally;
avgRead = (tally)/times;

void printColour(){

Processing Script:

Download the Processing software, which can be found here. This software allows coding within the context of visual arts. This code reads the serial output from the Arduino, and visually displays the corresponding color.

import processing.serial.*;

String buff = "";
int val = 0;
int wRed, wGreen, wBlue;

Serial port;

void setup(){
port = new Serial(this, "COM3", 9600); //Replace COM port

void draw(){
// check for serial, and process
while (port.available(), 0) {

void serialEvent(int serial) {
if(serial != '\n') {
buff += char(serial);
else {
int cRed = buff.indexOf("R");
int cGreen = buff.indexOf("G");
int cBlue = buff.indexOf("B");

if(cRed =0){
String val = buff.substring(cRed+3);
wRed = Integer.parseInt(val.trim());
String val = buff.substring(cGreen+3);
wGreen = Integer.parseInt(val.trim());
if(cBlue =0){
String val = buff.substring(cBlue+3);
wBlue = Integer.parseInt(val.trim());
buff = "";


  • Balance the sensor with Arduino code: The first time running the program, or resetting the Arduino, or restarting the Arduino, or starting Arduino serial monitor, or running the Processing sketch. the color sensor needs to be re-balanced. The code allows 5 sec during startup to bring a white paper over the sensor to generate a whiteArray balanced data, then it will turn-off the LED for 5 seconds to allow a black paper to be prepared and placed over the sensor to generate a blackArray balanced data. Try to place both white and black colored papers at the same height to achieve good balancing. Any white and/or black colored surface can be used.
  • Color change with Arduino: Open the serial monitor to observe the changes in RGB values, and test with different colored objects.
  • Color change with Processing: Open the Processing sketch, make sure the serial monitor from Arduino is closed, run the sketch. This requires a balancing of the sensor again. Test with different colored objects.
  • Improve color accuracy: Dimming the surrounding light or enclosing the circuit can help block external visible light from offsetting the accuracy of the color sensor.



RGB Color display from Processing.