Line Sensing. QRE1113 Reflectance Sensor + Arduino

Monday, June 27 th , 2011

The QRE1113 is a common reflectance sensor often used in robotic line followers. The sensor works by shining an IR LED down and seeing how much of that light bounces back using a phototransistor. Because dark colors will bounce back less of the light, the sensor can be used to tell the difference between white and black areas. So an array of these can be used to help a robot determine where a dark line is on the ground so it can follow it. But they can also be used to determine proximity under an inch.

Both Pololu and Sparkfun sell digital and analog models of a QRE1113 breakout board. The analog version is very simple, it just outputs an analog voltage on the signal pin relative to how much light was reflected. And you might think the digital version, especially being called a line sensor by sparkfun, would just output HIGH when it sees a line, and LOW when it does not. But this is not the case.

The digital version is designed for times you do not have an analog input on your microcontroller but still need an analog reading of how much light was reflected. It does this by allowing you to charge a capacitor on the board, and then timing how long it takes to discharge. The more light that is reflected, the less time it takes to discharge the capacitor. In my tests, the times ranged between 10 microseconds (10/million) to 2.5 milliseconds, so this is all done very quickly and wont put much of a delay in your code.

Hooking Them Up

Hooking the QRE1113 to your Arduino is very simple. It just needs power (5V), ground, and an analog or digital pin depending on what version you have.

Code

The method of reading the values from the analog and digital version is very different, so we have 2 code examples for you.

Analog Version

//Code for the QRE1113 Analog board
//Outputs via the serial terminal - Lower numbers mean more reflected
int QRE1113_Pin = 0; //connected to analog 0

void setup(){
  Serial.begin(9600);
}


void loop(){

  int QRE_Value = analogRead(QRE1113_Pin);
  Serial.println(QRE_Value); 

}
Unless otherwise stated, this code is released under the MIT License – Please use, change and share it.

Digital Version

//Code for the QRE1113 Digital board
//Outputs via the serial terminal - Lower numbers mean more reflected
//3000 or more means nothing was reflected.

int QRE1113_Pin = 2; //connected to digital 2

void setup(){
  Serial.begin(9600);
}


void loop(){

  int QRE_Value = readQD();
  Serial.println(QRE_Value); 

}


int readQD(){
  //Returns value from the QRE1113 
  //Lower numbers mean more refleacive
  //More than 3000 means nothing was reflected.
  pinMode( QRE1113_Pin, OUTPUT );
  digitalWrite( QRE1113_Pin, HIGH );  
  delayMicroseconds(10);
  pinMode( QRE1113_Pin, INPUT );

  long time = micros();

  //time how long the input is HIGH, but quit after 3ms as nothing happens after that
  while (digitalRead(QRE1113_Pin) == HIGH && micros() - time < 3000); 
  int diff = micros() - time;

  return diff;
}
Unless otherwise stated, this code is released under the MIT License – Please use, change and share it.

A Slow Display… E-Paper + Arduino

Monday, June 20 th , 2011

Most notable for its inclusion in the Kindel and other E-Readers, E-Paper has recently become very popular. But until very recently been out of reach to being used in personal projects. Luckily for us, SparkFun started selling and E-Paper display, and breakout board finally bringing this great technology to a place where we can slap it on the back of our Arduinos.

If you are wondering, E-Ink is a brand of E-Paper, and this display is not of that brand so we will be calling it E-Paper.

E-paper has some properties that make it unlike almost any other display. Unlike a typical LCD screen, E-paper only needs power when it is changing what is being displayed. Think of it as an electrical etch-a-sketch – The image is there until you change it.

The E-paper actually does this by drawing the image by flipping over tiny balls in the display that are black on one side, and white on the other. When an electrical charge is applied to portions of the display it forces the balls in that area to all flip to one side, changing the color of that part of the display. Once these balls have been set into place, they stay there until a new instruction takes place.

E-paper also is not backlit like many LCDs and is more like actual paper where reading it is dependent on available light where you are. And, many people report E-Paper as being easier on the eyes and more like reading actual paper than a screen.

This particular display is a 20 character, 2 line, 16-segment display. So you wont be able to display graphics on it, and the type size is set as well. But… it is still very cool.

Hooking It Up

I wouldn’t try hooking up this display without the breakout board, so im not going to cover that at all (the break outboard bumps the 3.3 or 5[v] source to the 35V needed for the display) and there are like 400 connections on the 2 ribbon cables. Also, even with the breakout board it can be a little tricky.

The ribbon cables of the display need to slide into the connectors of the breakout board, and I have read some people having issue with this. To do this you need to slide out the sides of the connector, then slide the ribbon cable into it, and slide the sides back in, locking it into place.

You also need to make sure you are plugging the right cable into the right connector. – Look at the illustration. In the top left of the back of the display are some square / line shapes. This side needs to plug into “input 2″ of the breakout-board. Im not 100% that it MUST be orientated this way, but this is how I did it, and how it is shown on SparkFun, and it worked for us. It is possible that it would just be upside-down if reversed.

Now just hook everything up as shown in the illustration.

Code

This code is based on the code example on SparkFun’s site written by Jim Lindblom. With his permission, we started a library for this display that makes it very easy to write anything to the display. It has some bugs, but works quite well.

All you need to do after the library is initiated, is this:

To display a number up to 2147483647
epaper.writeNumberTop(2147483647);
epaper.writeNumberBottom(2147483647);
epaper.writeDisplay();

Or for text
epaper.writeTop(” bildr “);
epaper.writeBottom(” example “);
epaper.writeDisplay();

To make this code work, before you load the code, or even open the Arduino program, we need to place both the “ePaper” folder into your Arduino Library. If you don’t know where that is by default, Look to the right.

If you click the download button to the right of “Arduino” you can download the whole thing as a zip, so you dont need to copy all the files.

Default Library Folder Location

On your Mac:: In (home directory)/Documents/Arduino/libraries
On your PC:: My Documents -> Arduino -> libraries
On your Linux box:: (home directory)/sketchbook/libraries

Help expand the library

We would love help expanding the library if you are interested. We would like to add to support for other characters, capital and non capitol letters, left and right justify etc. The screen may also support black on white as well as black on white (not 100% sure). If you are interested in helping, let us know in the forum.

Video

Sensing Barometric Pressure | BMP085 + Arduino

Thursday, June 16 th , 2011

Light, location, temperature… What’s next? Well, how about Barometric pressure? You know.. that thing that determines so much of our weather. Well the BMP085 Barometric Pressure sensor, available at SparkFun is a great little sensor capable of sensing such small changes in barometric pressure it can be used as a pretty precise altimeter as well. And, because no Barometric pressure sensor would be complete without a temperature reading, the BMP085 has an imbedded thermometer. It also looks cool!

For this example, the way we will be calculating altitude is imprecise and does not compensate for temperature or many other things that can contribute to differences. But, if you use it as a comparison against itself during a hour long period (barometric pressure also fluctuates throughout the day), you can get a pretty accurate difference reading.

The reading differs from my weather report

Im not going to get into why this is, but to make comparisons easier, your weather report uses a sea-level compensated reading, not an actual reading. This is an actual reading.

Hooking it up

Hooking it up to your Arduino is pretty simple, the BMP085 is an I2C device. I2C is a 2-wire serial connection, so you just need to connect the BMP085 to power (3.3v) and ground, then the SDA (Data) and SCL (Clock) lines to your Arduino for communication. On your Arduino (everything but the mega) SDA is on analog pin 4, and SCL is on analog pin 5. On an Arduino Mega, SDA is digital 20, and SCL is digital 21.

Code

The code for this is largely copied and based off of Jim Lindblom’s example from SparkFun (Thanks Jim!). I changed a few things so it is just easier for you to use the readings in calculations, and I added the altitude reading as well. If you know of a better way to calculate altitude, please let us know so we can include it!

The code simply outputs the temperature, pressure, and altitude in the serial terminal.

//Arduino 1.0+ Only
//Arduino 1.0+ Only

/*Based largely on code by  Jim Lindblom

 Get pressure, altitude, and temperature from the BMP085.
 Serial.print it out at 9600 baud to serial monitor.
 */

#include <Wire.h>

#define BMP085_ADDRESS 0x77  // I2C address of BMP085

const unsigned char OSS = 0;  // Oversampling Setting

// Calibration values
int ac1;
int ac2;
int ac3;
unsigned int ac4;
unsigned int ac5;
unsigned int ac6;
int b1;
int b2;
int mb;
int mc;
int md;

// b5 is calculated in bmp085GetTemperature(...), this variable is also used in bmp085GetPressure(...)
// so ...Temperature(...) must be called before ...Pressure(...).
long b5; 

void setup(){
  Serial.begin(9600);
  Wire.begin();

  bmp085Calibration();
}

void loop()
{
  float temperature = bmp085GetTemperature(bmp085ReadUT()); //MUST be called first
  float pressure = bmp085GetPressure(bmp085ReadUP());
  float atm = pressure / 101325; // "standard atmosphere"
  float altitude = calcAltitude(pressure); //Uncompensated caculation - in Meters 

  Serial.print("Temperature: ");
  Serial.print(temperature, 2); //display 2 decimal places
  Serial.println("deg C");

  Serial.print("Pressure: ");
  Serial.print(pressure, 0); //whole number only.
  Serial.println(" Pa");

  Serial.print("Standard Atmosphere: ");
  Serial.println(atm, 4); //display 4 decimal places

  Serial.print("Altitude: ");
  Serial.print(altitude, 2); //display 2 decimal places
  Serial.println(" M");

  Serial.println();//line break

  delay(1000); //wait a second and get values again.
}

// Stores all of the bmp085's calibration values into global variables
// Calibration values are required to calculate temp and pressure
// This function should be called at the beginning of the program
void bmp085Calibration()
{
  ac1 = bmp085ReadInt(0xAA);
  ac2 = bmp085ReadInt(0xAC);
  ac3 = bmp085ReadInt(0xAE);
  ac4 = bmp085ReadInt(0xB0);
  ac5 = bmp085ReadInt(0xB2);
  ac6 = bmp085ReadInt(0xB4);
  b1 = bmp085ReadInt(0xB6);
  b2 = bmp085ReadInt(0xB8);
  mb = bmp085ReadInt(0xBA);
  mc = bmp085ReadInt(0xBC);
  md = bmp085ReadInt(0xBE);
}

// Calculate temperature in deg C
float bmp085GetTemperature(unsigned int ut){
  long x1, x2;

  x1 = (((long)ut - (long)ac6)*(long)ac5) >> 15;
  x2 = ((long)mc << 11)/(x1 + md);
  b5 = x1 + x2;

  float temp = ((b5 + 8)>>4);
  temp = temp /10;

  return temp;
}

// Calculate pressure given up
// calibration values must be known
// b5 is also required so bmp085GetTemperature(...) must be called first.
// Value returned will be pressure in units of Pa.
long bmp085GetPressure(unsigned long up){
  long x1, x2, x3, b3, b6, p;
  unsigned long b4, b7;

  b6 = b5 - 4000;
  // Calculate B3
  x1 = (b2 * (b6 * b6)>>12)>>11;
  x2 = (ac2 * b6)>>11;
  x3 = x1 + x2;
  b3 = (((((long)ac1)*4 + x3)<<OSS) + 2)>>2;

  // Calculate B4
  x1 = (ac3 * b6)>>13;
  x2 = (b1 * ((b6 * b6)>>12))>>16;
  x3 = ((x1 + x2) + 2)>>2;
  b4 = (ac4 * (unsigned long)(x3 + 32768))>>15;

  b7 = ((unsigned long)(up - b3) * (50000>>OSS));
  if (b7 < 0x80000000)
    p = (b7<<1)/b4;
  else
    p = (b7/b4)<<1;

  x1 = (p>>8) * (p>>8);
  x1 = (x1 * 3038)>>16;
  x2 = (-7357 * p)>>16;
  p += (x1 + x2 + 3791)>>4;

  long temp = p;
  return temp;
}

// Read 1 byte from the BMP085 at 'address'
char bmp085Read(unsigned char address)
{
  unsigned char data;

  Wire.beginTransmission(BMP085_ADDRESS);
  Wire.write(address);
  Wire.endTransmission();

  Wire.requestFrom(BMP085_ADDRESS, 1);
  while(!Wire.available())
    ;

  return Wire.read();
}

// Read 2 bytes from the BMP085
// First byte will be from 'address'
// Second byte will be from 'address'+1
int bmp085ReadInt(unsigned char address)
{
  unsigned char msb, lsb;

  Wire.beginTransmission(BMP085_ADDRESS);
  Wire.write(address);
  Wire.endTransmission();

  Wire.requestFrom(BMP085_ADDRESS, 2);
  while(Wire.available()<2)
    ;
  msb = Wire.read();
  lsb = Wire.read();

  return (int) msb<<8 | lsb;
}

// Read the uncompensated temperature value
unsigned int bmp085ReadUT(){
  unsigned int ut;

  // Write 0x2E into Register 0xF4
  // This requests a temperature reading
  Wire.beginTransmission(BMP085_ADDRESS);
  Wire.write(0xF4);
  Wire.write(0x2E);
  Wire.endTransmission();

  // Wait at least 4.5ms
  delay(5);

  // Read two bytes from registers 0xF6 and 0xF7
  ut = bmp085ReadInt(0xF6);
  return ut;
}

// Read the uncompensated pressure value
unsigned long bmp085ReadUP(){

  unsigned char msb, lsb, xlsb;
  unsigned long up = 0;

  // Write 0x34+(OSS<<6) into register 0xF4
  // Request a pressure reading w/ oversampling setting
  Wire.beginTransmission(BMP085_ADDRESS);
  Wire.write(0xF4);
  Wire.write(0x34 + (OSS<<6));
  Wire.endTransmission();

  // Wait for conversion, delay time dependent on OSS
  delay(2 + (3<<OSS));

  // Read register 0xF6 (MSB), 0xF7 (LSB), and 0xF8 (XLSB)
  msb = bmp085Read(0xF6);
  lsb = bmp085Read(0xF7);
  xlsb = bmp085Read(0xF8);

  up = (((unsigned long) msb << 16) | ((unsigned long) lsb << 8) | (unsigned long) xlsb) >> (8-OSS);

  return up;
}

void writeRegister(int deviceAddress, byte address, byte val) {
  Wire.beginTransmission(deviceAddress); // start transmission to device 
  Wire.write(address);       // send register address
  Wire.write(val);         // send value to write
  Wire.endTransmission();     // end transmission
}

int readRegister(int deviceAddress, byte address){

  int v;
  Wire.beginTransmission(deviceAddress);
  Wire.write(address); // register to read
  Wire.endTransmission();

  Wire.requestFrom(deviceAddress, 1); // read a byte

  while(!Wire.available()) {
    // waiting
  }

  v = Wire.read();
  return v;
}

float calcAltitude(float pressure){

  float A = pressure/101325;
  float B = 1/5.25588;
  float C = pow(A,B);
  C = 1 - C;
  C = C /0.0000225577;

  return C;
}
Unless otherwise stated, this code is released under the MIT License – Please use, change and share it.

« Older Entries