Home > Exploring Ideas, Hardware testing, Made using Processing > Using the Linear Sensor Array TSL201R with Arduino

Using the Linear Sensor Array TSL201R with Arduino

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

 

Advertisements
  1. Kishore Garapati
    July 30, 2014 at 12:31 PM

    Hi, I awaiting for a new posts in blog from many days… loved this blog.. thanks for awesome posts..

  2. September 22, 2014 at 3:07 PM

    Hello,

    I’m trying to do the same montage as you… what are your raw readings ? I’ve doubts about my montage, thanks !

  3. September 23, 2014 at 6:22 AM

    Hello,

    what are the example of values you get from your sensor ?

    I don’t know how to validate if the sensor is correctly wired.

    thanks !

  4. September 23, 2014 at 3:21 PM

    Hello,

    I’ve tried the parallax code and adapted to this one, always ones…

    http://forums.parallax.com/showthread.php/125594-TSL1401-and-Arduino

    • November 26, 2014 at 7:19 PM

      The TSL1401 works different than the TSL201.
      In the TSL1401 the reset, charge and hold of sensor capacitors occurs simultaneously.
      In the TSL201 it occurs sequentially.

  5. Müslüm İncedal
    August 9, 2015 at 1:40 PM

    potentiometer = ? ohm

  6. Müslüm İncedal
    August 10, 2015 at 11:14 AM

    Dear friend; Are you possible to share pc software.

  7. xpeace
    August 20, 2015 at 9:21 AM

    processing code please 🙂

    • April 6, 2016 at 3:51 PM

      Processing code included.

  8. xpeace
    August 29, 2015 at 5:05 PM

    I currently reach 300 FPS with the Arduino & TSL1401R.

    Some hints:
    digitalWrite is damn slow and will mess up your Exposure Time setting …

    // Reset Camera
    digitalWriteFast (CLKpin, LOW);
    digitalWriteFast (SIpin, HIGH);
    delayMicroseconds (1); // required when using port manipulation or fastwrite
    digitalWriteFast (CLKpin, HIGH);
    digitalWriteFast (SIpin, LOW);
    delayMicroseconds (1);
    for (i = 0; i < 128; i++) {
    digitalWriteFast (CLKpin, LOW);
    delayMicroseconds (1);
    digitalWriteFast (CLKpin, HIGH);
    }

    I created a custom optic combined with a 3D printed case.
    My main task is a fast lasertriangulation device (0-3m).

    • Scott
      February 22, 2016 at 4:01 PM

      Hi Xpeace,

      I’m wondering if you have a typo for your 300 fps frame rate statement.

      I am using a “micro” which has an effective baud rate of about 100 kbps through the USB connection. Just sending 128 readings in binary (256 characters) takes about 14 ms which is an equivalent rate of just over 70 fps. Add clocking the detector and taking A/D samples, and its hard to imagine frame rates above 30 or 40 fps. Please help me understand.

      Thanks,
      Scott

    • Scott
      February 23, 2016 at 12:17 PM

      Hi All and xpeace,

      I am wondering if you have an error in your frame rate number 300 fps?

      The reason I ask is this: I have measured the USB serial transfer rate using a Arduino “micro” to be about 100 KBAUD or about 100 us/character. Even sending sensor data in binary as 256 bytes, that’s nearly 26 ms to send the frame. That works out to be under 40 FPS. Add in clocking the detector and ADC conversions, well, you get the idea. Can you explain how you’re getting that high of a rate?

      Thanks,
      Scott

  9. xpeace
    August 29, 2015 at 5:20 PM

    Oh, and I nearly forgot – you could also enable faster AD readout on the Arduino

    #define FASTADC 1
    // defines for setting and clearing register bits
    #ifndef cbi
    #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
    #endif
    #ifndef sbi
    #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
    #endif

    void setup(void)
    {
    .
    .
    .
    .
    #if FASTADC // 1MHz ADC
    // set prescaler to 16
    sbi(ADCSRA,ADPS2);
    cbi(ADCSRA,ADPS1);
    cbi(ADCSRA,ADPS0);
    #endif
    .
    .
    .
    .
    Serial.begin (115200);
    }

  10. August 29, 2015 at 8:52 PM

    Thanks xpeace, very useful ideas.
    Posting the Processing Code (adjusted to run it in Processing 3.0)

  11. December 8, 2015 at 6:30 AM

    processing code please…

    • April 6, 2016 at 3:50 PM

      Finally… the Processing code included.

  12. rahul
    January 20, 2016 at 2:40 AM

    Can u help me out How many 7 segments digits can this sensors scan sucessfully?

    • February 27, 2016 at 7:23 AM

      Hi rahul, I was planning to read 4 digit 7-segment led display. Because the linear array, a servomotor can be used for scanning. Usually the 7-segments led displays are multiplexed.
      The problem is the multiplexing frequency and the scanning frequency implemented with the TSL201. If the reading of the display is stable (like the one in my Radon Monitor) may be possible to integrate the readings to identify the On or Off state of each display segments.

  13. April 14, 2016 at 3:07 AM

    How do I connect oled display

  14. April 14, 2016 at 3:13 AM

    computer software which language c# ? vb.net ?

    • April 14, 2016 at 11:53 PM

      Processing

      • April 15, 2016 at 7:06 AM

        What is this Processing ? computer software required please at me

  15. April 16, 2016 at 3:13 AM
  16. April 16, 2016 at 10:27 AM

    &amp;amp;amp;gt –> This section gives an error

  17. April 16, 2016 at 10:39 AM

    Processing file save add github.

  18. Müslüm İncedal
    April 25, 2016 at 3:05 AM

    It gives error codes. Processing file save add github.

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: