Archive

Archive for the ‘Made using Processing’ Category

Using the Linear Sensor Array TSL201R with Arduino

March 26, 2014 28 comments

Dealing with linear Sensor Arrays is not easy. After some research in the web, found some experiments with TSL1401, TSL202R and TSL201R, many of them without a happy ending.

My intent is to find out how to do simple image processing of Linear Sensor Arrays using the Arduino.

This will open the path to develop some interesting experiments and applications:

-Line Follower Robots.

-Optical Flow Sensor (to sense movement in robots and autonomous vehicles).

-Laser Range Finders.

-Imagen Scanners (Barcode, character recognition etc.)

-Position Sensors.

-Objet Detection and Objet Counting.

-Objet Shape Identification and Analysis.

-Light Spectrum Analyzers.

-Etc…

After reading the (64×1) linear Sensor Arrays TSL201R manual…

tsl201r-fingertip

…decided to buy some DIP-8 versions (package discontinued) in Aliexpress.com, (breadboard friendly).

AMS produces the SMT version,  The TSL201CL.

This is the wiring diagram:

TSL201R_Wiring

Arduino NANO, solid wires (AWG#22) and an adequate breadboard  for the job.

TSL201R_Test_01_noFilter

The Arduino is relatively slow to scan and read this sensor. To solve this limitation the integration time is increased by reducing the incoming light. A pinhole approach is implemented with a tiny black box (a relay case).

Pinhole cameras using  PCB Relay’s cases:

tsl201r-test-panels

Because the small hole, more integration time is needed.

The projected image field of view can be estimated using geometry…

pinhole-image-size-3


 

In the Sketch only one pixel is readed in each scan. The image is completed after 1+64 scans.(first is discarded)
First scan is repeated to permit a complete integration period.
The Pixel integration Period is adjustable using a potentiometer in the analog channel 1.

/*TSL201R_Scanning.ino Arduining 26 MAR 2014

Linear Array Sensor TSL201R (TAOS) is now AMS:
  -64 x 1 Linear Sensor Array 200 DPI

-Sketch used with the Arduino NANO.
  Data is Send at 115200 bauds (ASCII messages ending with CR and LF)
  One frame (64x1 image) has the followinf format:
      >5890    Indicates the integration period in microseconds.
      843      this value is discarded.
      234      value of the pixel 1
      242      value of the pixel 2
       .         .
       .         .
      245      value of the pixel 64

-Reading only one pixel in each scan.
-The image is completed after 1+64 scans.(first is discarded)
-First scan is repeated to permit a complete integration period). 
-Pixel integration Period is adjustable (potentiometer in analog channel 1).

*/

#define CLK     2
#define SI      3
#define VOUT    0   //pixel intensity in the analog channel 0
#define INTVAL  1   //integration time adjust in the analog channel 1.
#define PIXELS  64

int intDelay;      //Integration Period = (intDelay + 535 ) microseconds.
int Value;         //pixel intensity value.

void setup(){
  pinMode(CLK, OUTPUT);
  pinMode(SI, OUTPUT);
  digitalWrite(CLK, LOW);
  digitalWrite(SI, LOW);

//  Serial.begin(9600);
  Serial.begin(115200);
  Serial.flush();
}



void loop(){
  intDelay=1+analogRead(INTVAL)*10;  //read integration time from potentiometer.
  Serial.print(">");                //Mark the start of a new frame
  Serial.println(intDelay + 535);   //Send Integration Period in microseconds.
//  delay(8);     // used with 9600 bauds
  delay(2);     // used with 115200 bauds

  readPixel(0);                     //the first reading will be discarded.
  delayMicroseconds(intDelay);

  for(int i=0;i<PIXELS;i++){
    readPixel(i);
    delayMicroseconds(intDelay);    //Delay added to the integration period.
  }
}

//------------------ Send the intensity of the pixel serially -----------------
void readPixel(int pixel){
  digitalWrite(CLK, LOW);
  digitalWrite(SI, HIGH);         
  digitalWrite(CLK, HIGH);
  digitalWrite(SI, LOW);
 

  for(int i=0;i<pixel; i++){    //Clock pulses before pixel reading
    digitalWrite(CLK, LOW);
    digitalWrite(CLK, HIGH);    //Select next pixel and reset integrator of actual pixel.
  }

  Value = analogRead(VOUT);
  Serial.println(Value);
//  delay(8);       //used with 9600 bauds
  delay(1);         //used with 115200 bauds
  for(int i=0;i<=(PIXELS-pixel); i++){    //Clock pulses after pixel reading.
    digitalWrite(CLK, LOW);
    digitalWrite(CLK, HIGH);    //Select next pixel and reset integrator of actual pixel.
  }
}


 

The application in Progressing shows the illuminance of each pixel sent by the arduino.

Integration time and frames per second (FPS) are also presented.

TSL201RView_06e

Here is the Processing Code:

Pro_TSL201RViewer.pde    28 AUG 2015

Processing Viewer for the Linear Array Sensor TSL201R (AMS):

 


</pre>
/*Pro_TSL201RViewer_01.pde    Arduning.com  28 AUG 2015

Processing Viewer for the Linear Array Sensor TSL201R (AMS):
  64 x 1 sensors (200 DPI)

Data is received at 115200 bauds (ASCII messages ending with CR and LF)
One frame (64x1 image) has the followinf format:
      >5890    Indicates the integration period in microseconds.
      543      this value must be discarded.
      234      value of the pixel 1
      242      value of the pixel 2
       .         .
       .         .
      245      value of the pixel 64

Works with the Sketch:TSL201R_Scanning.ino in the Arduino.    

------------------------------------------------------------------------------*/
//---------------- Labels -----------------------------------------------------
String    heading = "TSL201R Viewer";
String    credit  = "Arduining.com";
//------------------------- Graph dimensions (constants) ----------------------
//System variables width and height are defined with size(width,height) in setup().

int       leftMargin=   80;   //from Graph to the left margin of the window
int       rightMargin=  80;   //from Graph to the right margin of the window
int       topMargin=    70;   //from Graph to the top margin of the window.
int       bottonMargin= 70;   //from Graph to the botton margin of the window.
//-------------------- Colors -------------------------------------------------
final  color  frameColor =  color(0,0,255);    // Main window background color.(Blue)
final  color  graphBack =   color(0,0,128);    // Graphing window background color. (Dark black)
final  color  borderColor=  color(128);        // Border of the graphing window.(grey)
final  color  barColor=     color(255,255,0);  // Vertical bars color. (yellow)
final  color  titleColor=   color(255,255,0);  // Title color. (yellow)
final  color  textColor=    color(200);        // Text values color. (grey)
//-------------------- Serial port library and variables ----------------------
import    processing.serial.*;

Serial    myPort;
boolean   COMOPENED = false;      // to flag that the COM port is opened.
final  char  LINE_FEED=10;        //mark the end of the frame.
final  int  numRecords=3;         //Number of data records per frame.
String[]  vals;                   //array to receibe data.
String    dataIn= null;           //string received
int       pixelCount= 64;
int       lightIntensity;
int       Integration= 0;         //Integration period in milliseconds
int       timeStamp;              // used to calculate frames per second (FPS).
int       lastTimeStamp;          // used to calculate frames per second (FPS).
float     framePeriod=0;          // used to calculate frames per second (FPS).
PFont     fontArial;
PFont     boldArial;
int       graphBase,graphWidth,graphHeight;

//-------- Variables to allocate the parameters in "settings.txt" --------------
String    comPortName;
float     Gain= 1;

//-------- Default content of "settings.txt" (one parameter per line) ----------
String[] lines = {
                 "Port:COM4",      //Serial Port
                 "Gain:1"};        //Gain (data amplification)

//------------------------------------------------------------------------------
//  setup
//------------------------------------------------------------------------------
void setup(){
//  String[] fontList = PFont.list();
//  println(fontList);
//  exit();

  size(800,600);        // setting system variables width and height.

  FillParams();         //Read "settings.txt" an fill the values.
  PortManager();        //Deal with the port selection and opening.

  graphBase= height-bottonMargin;
  graphHeight= height-bottonMargin-topMargin;
  graphWidth= width-leftMargin-rightMargin;

  fontArial = createFont("Arial",12);
  boldArial = createFont("Arial Bold Italic",12);

  timeStamp= millis();
  lastTimeStamp = timeStamp;

  background(frameColor);     // Main window background color.
  clearGrahp();
  showLabels();
  showParams();               // Display variables (usefull for debugging)
}

//==============================================================================
//  draw (main loop)
//==============================================================================
void draw(){

  while (myPort.available() > 0) {
    dataIn = myPort.readStringUntil(LINE_FEED);
    if (dataIn != null) {
      print(dataIn);                //show dataIn for debugging
      dataIn= trim(dataIn);         //removes spaces, CR and LF.
      PlotData();
      showParams();
    }
  }
}

//------------------------------------------------------------------------------
//Plot the incoming data in the Graph.
//------------------------------------------------------------------------------
void PlotData(){

  if (dataIn.charAt(0)=='>'){
    Integration= int(dataIn.substring(1,dataIn.length())); // removes '>'.
    timeStamp= millis();             //calculate period between frames.
    framePeriod= 1000/float(timeStamp - lastTimeStamp);
    lastTimeStamp= timeStamp;
    println("         (" + Integration +")");   // print Integration period.
    pixelCount=-2;            //reset pixel counter (-2 to skip first reading).
  }

  else if((pixelCount >= 0) && (pixelCount < 64)){
    lightIntensity= int(dataIn)/2;
    println("light= " + lightIntensity);

// ------- Clear the pixel region. -------------------
    noStroke();                 //no border
    fill(graphBack);            //Dark blue background
    rect(1+leftMargin +(graphWidth/64)*pixelCount, graphBase, (graphWidth/64)-2, 1-graphHeight);

// ------- Draw pixel intensity value as a vertical bar. -------
//    strokeWeight(1);
//    stroke(0);                //black border
    noStroke();
    fill(barColor);              //Color of the vertical bars
    rect(1+leftMargin +graphWidth*pixelCount/64, graphBase,(graphWidth/64)-2, -constrain(lightIntensity*Gain, 0, graphHeight-1));
  }
  pixelCount++;       //pointer to draw next pixel.
  println("--------" + pixelCount + "-"); //show pixel for debugging
}

//------------------------------------------------------------------------------
//Display diferent values for debugging.
//------------------------------------------------------------------------------
void showParams(){
  noStroke();                 //no border
  fill(frameColor);           //Blue background
  rect(1,height, width-250, 1-bottonMargin);  //Clean parameters region.

  textFont(fontArial,18);
  textAlign(LEFT,BOTTOM);    //Horizontal and vertical alignment.
  fill(textColor);

//---------- Show COM Port -----------------------------
  if (COMOPENED){
    text(lines[0],20, height-15);                          //Show the COM port.
    text("Gain: "+ Gain ,150, height-15);                  //Show Vertical Gain.
    text("Integration: " + Integration,270, height-15);    //Show Integration Period.
    text("FPS: "+ nf(framePeriod, 1, 1) ,450, height-15);  //Show frames Per Second (FPS)
  }
  else {
    fill(255,0,0);
    text(lines[0]+" not available",20, height-15);   //COMM port not available.
  }
}

//------------------------------------------------------------------------------
//Clear graph area.
//------------------------------------------------------------------------------
void clearGrahp(){

  stroke(borderColor);        //Graph border color.(gray)
  fill(graphBack);            //Dark Blue background
  rect(leftMargin, graphBase,graphWidth,-graphHeight);
}

//------------------------------------------------------------------------------
//Write the labels.
//------------------------------------------------------------------------------
void showLabels(){
  textAlign(CENTER,CENTER);
  textFont(fontArial,36);
  fill(titleColor);
  text(heading,width/2,topMargin/2);     //Draw graph title.
  textAlign(RIGHT,BOTTOM);
  textFont(boldArial,24);
  fill(textColor);
  text(credit,width-25,height-15);      //Draw credits.
}

//------------------------------------------------------------------------------
// Look for the values in "settings.txt" and set the variables accordingly.
//------------------------------------------------------------------------------
void  FillParams(){
  String splitLine[];
//-------- If "settings.txt" doesn't exist, create it.-------------
  if (loadStrings("settings.txt")== null){
    saveStrings("settings.txt", lines);         //each string is a line.
  }
  else{  //Reading "settings.txt" and update variables:
    lines = loadStrings("settings.txt");        //lines is a string array
    splitLine = split(lines[0], ':');           //split line[0].
    comPortName= splitLine[1];                  //the substring after '='
    splitLine = split(lines[1], ':');           //split line[1].
    Gain= Float.parseFloat(splitLine[1]);       //the substring after '='
  }  

//---------- Show the content of "settings.txt" in the the console -------------
  println("there are " + lines.length + " lines in \"settings.tx\" file");
  for (int i=0; i < lines.length; i++) {
      println(lines[i]);                        //Send to console.
  }

  saveStrings("settings.txt", lines);           //Save the settings.
}

//------------------------------------------------------------------------------
//Try to open the port specified in "settings.txt".
//------------------------------------------------------------------------------
void PortManager(){

//------------- List the available COM ports in this system --------------------
  String[] portlist = Serial.list();  //Create a list of available ports.
  println("There are " + portlist.length + " COM ports:");
  println(portlist);

//----------- If COM port in "settings.txt" is available, open it  -------------
  for (int i=0; i < portlist.length; i++) {
      if(comPortName.equals(portlist[i]) == true){
          myPort = new Serial(this, portlist[i], 115200); //open serial port.
          COMOPENED = true;
          println(portlist[i]+" opened");
      }
  }

//-------- Procedure when COM port is not available --------------------
  if(!COMOPENED){
      println(comPortName + " is not available");    //Anounce the problem
//      SelComPort();         //Procedure to select another port...
  }
}
<pre> 

An example of how to use the sensor:  Measure the position of a floating ball.

20140417_120633

Another  possible application, a laser ranger:

laser-ranger-concept

Working to increase the frames per second (FPS)…

 

 

(in twitter you can follow my progress before it be posted here ): @arduining

 

Arduino and Processing ( Graph Example )

August 5, 2013 21 comments

Graph_Example

This example was uploaded to Youtube last year.

It is based in the code at http://arduino.cc/en/Tutorial/Graph

The Processing code was modified as is explained in the comments.

// Pro_Graph2.pde
/*
 Used in the Youtube video "Arduino and Processing ( Graph Example )"
 Based in the Tom Igoe example.
 Mofified by Arduining 17 Mar 2012:
  -A wider line was used. strokeWeight(4);
  -Continuous line instead of vertical lines.
  -Bigger Window size 600x400.
-------------------------------------------------------------------------------
This program takes ASCII-encoded strings
from the serial port at 9600 baud and graphs them. It expects values in the
range 0 to 1023, followed by a newline, or newline and carriage return

Created 20 Apr 2005
Updated 18 Jan 2008
by Tom Igoe
This example code is in the public domain.
*/

import processing.serial.*;

Serial myPort;        // The serial port
int xPos = 1;         // horizontal position of the graph 

//Variables to draw a continuous line.
int lastxPos=1;
int lastheight=0;

void setup () {
  // set the window size:
  size(600, 400);        

  // List all the available serial ports
  println(Serial.list());
  // Check the listed serial ports in your machine
  // and use the correct index number in Serial.list()[].

  myPort = new Serial(this, Serial.list()[0], 9600);  //

  // A serialEvent() is generated when a newline character is received :
  myPort.bufferUntil('\n');
  background(0);      // set inital background:
}
void draw () {
  // everything happens in the serialEvent()
}

void serialEvent (Serial myPort) {
  // get the ASCII string:
  String inString = myPort.readStringUntil('\n');
  if (inString != null) {
    inString = trim(inString);                // trim off whitespaces.
    float inByte = float(inString);           // convert to a number.
    inByte = map(inByte, 0, 1023, 0, height); //map to the screen height.

    //Drawing a line from Last inByte to the new one.
    stroke(127,34,255);     //stroke color
    strokeWeight(4);        //stroke wider
    line(lastxPos, lastheight, xPos, height - inByte); 
    lastxPos= xPos;
    lastheight= int(height-inByte);

    // at the edge of the window, go back to the beginning:
    if (xPos >= width) {
      xPos = 0;
      lastxPos= 0;
      background(0);  //Clear the screen.
    } 
    else {
      // increment the horizontal position:
      xPos++;
    }
  }
}




Important Note:
Remember to replace the index in Serial.list()[0] to open the Serial Port where the Arduino is connected.
Run the program in Processing and check the Console window to see the available ports.

insertPort


Categories: Made using Processing

Single Pixel Camera with Arduino Nano

July 16, 2013 3 comments

The idea of a camera using a single light detector always atracted my atention.
There are sophisticated applications using Compressive Imaging and single pixel cameras, but my objetive was more simple to implement.
Inspired by the project “A Low-Cost Single-Pixel Thermographic Camera”.
Using the following material:
-Pan and Tilt servo assebly from ServoCity.com (A smaller one would be enough)
-TEPT4400 light sensor from VISHAY.
-Arduino NANO.
-120nF polyester capacitor
-Breadboars, jumpers etc..

At first, some experiments to sense very low light intensity (we will use a pinhole to catch a very small area of the image).

TEPT4400_sch2

Using the Oscilloscope to observe the charge of the capacitor:

Charging

The following technic was implemented in the Arduino software:

Technique

This method is useful to increase the range of the light measured, but in this project only the gray area was used because the low light intensity and the short time to take the sample. Using a current amplifier to charge the capacitor may solve the problem (things to do).

The One Pixel Camera:

Camera_04Camera_05

The full Assembly:

Pic_01

The Arduino Nano is reading the light intensity and controlling the servos, a separated breadboard is used to feed the servos with an external 5V power adapter.

Capture_02

The 40×30 pixels  image is generated in Processing, the aplication is very simple. The Arduino sends  x ,y coordinates and the the light intensity of each position , Processing just paint each point of the image using a square of 8×8 (screen pixels) with a grey value in the range 0-255.

I want to share the simple code used in Processing:

/*Pro_Single_Pixel_01.pde
Arduining 7 JUL 2013
This Processing application plot the incoming value (0-255)
in the specified coordinates with a gray scale 8x8 pixels square.
The incoming characters at 9600 bauds have the following format:
xPos,yPos,value CR
xPos = x coordinate (1-40)
yPos = y coordinate (1-30)
value = gray scale value (0-255)
CR  ending with a carriage return character.
*/
import processing.serial.*;

Serial port; 

int xPos;
int yPos;
int value;

void setup(){
  println(Serial.list());               // Check COM port number and edit...
  String portName = Serial.list()[0];   // ...the index in Serial.list[]
  port = new Serial(this, portName, 9600);
  port.bufferUntil(10); 
  size(320,240);
  background(255);
}

void draw(){

}

void serialEvent(Serial _port){
  String input = port.readString(); 
  input = trim(input);
  String[] vars = split(input, ",");
  xPos = int(vars[0]);
  yPos = int(vars[1]);
  value = int(vars[2]);

  println(value);         // To see the received values.
  noStroke();
  fill(value);
  rect(xPos*8, yPos*8, 8, 8);
}

And this is the sketch used in the Arduino, the macros for direct port manipulation can be avoided in the final version but was useful to experiment with the charge-discharge circuit.

/*Ard_SinglePixelCam.ino

Arduining.com    06 JUL 2013  
Sketch used in the YouTube video "Single Pixel Camera with Arduino"
This code controls two servos to produce the scanning of the
single pixel camera.
The x and y position is sent with the corresponding light intensity.
Characters are sent at 9600 bauds with the following format:
xPos,yPos,value CR
xPos = x coordinate (1-40)
yPos = y coordinate (1-30)
value = gray scale value (0-255)
CR  ending with a carriage return character.
*/

#include <Servo.h> 

#define TILT_SERVO  7
#define PAN_SERVO   8

#define H_SIZE      40   // Horizontal picture elements.
#define V_SIZE      30   // Vertical picture elements.
#define SENSOR      0    // Light intensity sensor.
#define Xcenter     140  // X image center (Servo position in degrees)
#define Ycenter     35   // Y image center (Servo position un degrees)

//=====================================================================
//Definitions and variables used by ReadPixel() funtion
#define AnalogIn    0
#define Tcharge1    5000    //Delay microseconds before first Adquisition.
#define Tcharge2    20000   //Delay microseconds before second Adquisition.

//Macros to control the Light Sensor Circuit:
#define Discharge  {DDRD &= B00000011; DDRD |= B00001000; PORTD |= B00000000;}  //D3 output LOW.
#define Sense      {DDRD &= B00000011; DDRD |= B00000000; PORTD |= B00000000;}  //D3 as input.

//Variables
int first,second;

//=====================================================================
Servo servo_tilt;
Servo servo_pan;

//============== functions ============================================
//---------------------------------------------------------------------
void SendPixel(int xPos, int yPos, int value){
   Serial.print(xPos); Serial.print(","); Serial.print(yPos); Serial.print(","); Serial.println(value); 
}

//--------------------------------------------------------------------- 
void goto_origin(){
  servo_tilt.write( V_SIZE/2 + Ycenter ); // Tilt servo at top.
  servo_pan.write( Xcenter - H_SIZE/2 );  // Pan servo to left.
  delay(1000);                            // time to reach origin.
}

//--------------------------------------------------------------------- 
int ReadPixel(){
Sense
delayMicroseconds(Tcharge1);    //Delay to take the first value.
first= analogRead(AnalogIn);    //first value is taken.
delayMicroseconds(Tcharge2);    //Delay to take the second value.
second= analogRead(AnalogIn);   //second value is taken.
Discharge
if (first<200)first= second;    //if first is under 200, second will be under 1024.
else first = first * 5 ;        //if first is over 200, first*5 will be used.
first = map(first, 0, 60, 0, 255); //map was adjusted for the light conditions.
return first;
}

//=============== setup ================================================
void setup() 
{ 
  servo_tilt.attach(TILT_SERVO); 
  servo_pan.attach(PAN_SERVO);
  Serial.begin(9600);

  ReadPixel();      //Read and discard it (Discharge for next read).//================= main loop =========================================
void loop(){

  goto_origin();

  for (int y=0 ; y<V_SIZE ; y++) { 

    servo_tilt.write(Ycenter + V_SIZE/2 -y );
    delay(50);                              // time to reach position.

     for ( int x=0 ; x<H_SIZE ; x++) {      // Scan from Left to Right.
      servo_pan.write(Xcenter - H_SIZE/2 + x);
      delay(50);                            // time to reach position.

      SendPixel(x, y, ReadPixel());      
    }
    y++;
    servo_tilt.write(Ycenter + V_SIZE/2 - y);
    delay(50);                              // time to reach position.

      for ( int x=H_SIZE-1 ; x>=0 ; x--) {  // Scan from Right to Left.
      servo_pan.write(Xcenter - H_SIZE/2 + x);     
      delay(50);                            // time to reach position.

      SendPixel(x, y, ReadPixel());
    }

  }
}

Next steps:

-Increase the resolution and speed using stepper motors with gear reduction.

-Increase the sensibility and reduce the adquisition time using an Operational Amplifier as current amplifier in the charge circuit.

-Improve the resolution of the image using a smaller hole and a small box instead of a tube to capture the light (this is to eliminate the light reflections inside the cylinder).

-Hope to receive more ideas about this project, a well done Single Pixel Camera open the possibilities of experimenting with image processing algorithms.

The Youtube video shows the camera in action: