More Sensor Examples


Hall Effect RPM Counter 


 What it is called:  RPM Counter Using A Hall Effect Counter
 What it does:  Calculates RPM
 What it takes:  Arduino (5V), Hall Effect Sensor, 10K Resistor, Magnets
 What pins are used:  Hall Effect Sensor uses analog 0, 16x2 LCD uses digital 7, 8, 9, 10, 11, 12
 What libraries:  LiquidCrystal

Parts:

Boarduino (any 5V arduino)
10K resistor, 10K pot for LCD contrast, optional capacitors (for noisy environments)
Housing for Magnets (you make it for your application).

The housing I used. The part slips on to the shaft, and  uses four 3mm magnets stacked 2 and 2.  The sensor needs to be within 1/4 inch of the magnets.  The magnets must be stacked to provide a north and south pole.  This particular Hall Effect sensor flips states on N and S (two times per revolution).  Not all Hall Effect sensors work that way!  Read the data sheet for your sensor.



Wiring:



Display Example:


Comments:


Adafruit has some great tutorials to help with this project.  First, hooking up the LCD is shown here in fantastic detail.  The description of the Hall Effect sensor is not as detailed:

Runs at 3.5V up to 24V. To use connect power to pin 1 (all the way to the left), ground to pin 2 (middle) and then a 10K pull up resistor from pin 3 to power. Then listen on pin 3, when the south pole of a magnet is near the front of the sensor, pin 3 will go down to 0V. Otherwise it will stay at whatever the pullup resistor is connected to. Nothing occurs if a magnet's north pole is nearby (unipolar). 

But, it gets the basics across.

Explanation & Code:


This particular project samples at 1000 ms intervals, and it averages samples over that period.  Change sampleTime for faster rates.  The simple bargraph uses maxRPM for a 100% spread, so change as needed.

/*
  RPM meter code (tested on device rotating at 10,000 rpm) 
  rev.001
  * using a hall effect sensor
  http://www.adafruit.com/index.php?main_page=product_info&cPath=35&products_id=158
  * using a parallel LCD with
  LiquidCrystal Library
  http://www.ladyada.net/learn/lcd/charlcd.html
  http://www.arduino.cc/en/Tutorial/LiquidCrystal
  with pins 7,8,9,10,11,12
*/

// include the library code:
#include <LiquidCrystal.h>

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(7,8,9,10,11,12);
// read the hall effect sensor on pin 2
const int hallPin=2;
const unsigned long sampleTime=1000;
const int maxRPM = 10200; 

void setup() 
{
  pinMode(hallPin,INPUT);
  Serial.begin(9600);
  // set up the LCD's number of rows and columns: 
  lcd.begin(16, 2);
  // Print a message to the LCD.
  lcd.print("initializing");
  delay(1000);
  lcd.clear();
}

void loop() 
{
  delay(100);
  int rpm=getRPM();
  lcd.clear();
  displayRPM(rpm);
  displayBar(rpm);
}
 
int getRPM()
{
  // sample for sampleTime in millisecs
  int kount=0;
  boolean kflag=LOW;
  unsigned long currentTime=0;
  unsigned long startTime=millis();
  while (currentTime<=sampleTime)
  {
    if (digitalRead(hallPin)==HIGH)
    {
      kflag=HIGH;
    }
    if (digitalRead(hallPin)==LOW && kflag==HIGH)
    {
      kount++;
      kflag=LOW;
    }
    currentTime=millis()-startTime;
  }
  int kount2rpm = int(60000./float(sampleTime))*kount;
  return kount2rpm;
}
    
void displayRPM(int rpm) 
{
  lcd.clear();
  // set the cursor to column 0, line 1
  lcd.setCursor(0, 0); 
  // print the number of seconds since reset:
  lcd.print(rpm,DEC);
  lcd.setCursor(7,0);
  lcd.print("RPM");
}

void displayBar(int rpm)
{
  int numOfBars=map(rpm,0,maxRPM,0,15);
  lcd.setCursor(0,1);
  if (rpm!=0)
  {
  for (int i=0; i<=numOfBars; i++)
   {
        lcd.setCursor(i,1);
        lcd.write(1023);
      }
  }

HSM-20G Humidity and Temperature Mini-board


 What it is called: HSM-20G Humidity and Temperature Sensor 
 What it does: Measures % Relative Humidity and Temperature  
 What it takes: Arduino, HSM-20G Sensor 
 What pins are used:  +5, Gnd, plus two analog pins (0,1 in this example) 
 What libraries are needed: None 


Parts:


Arduino (any 5 volt, or possibly a 3.3 volt with external 5 volt supply)
HSM-20G
(1) 10K, (1) 100K resistors
(1) 47 uF capacitor


Wiring: 




Display Example (serial monitor):


34 %RH
25.8 deg C
78.4 deg F


Comments:


The spec sheet provides the following curve data:

Humidity    
Volts,Relative Humidity in %
0.74,10
0.95,20
1.31,30
1.68,40
2.02,50
2.37,60
2.69,70
2.99,80
3.19,90

The curve fit for this data is:
RH=31*volts-12 

So, assuming a 5 volt reference, the Arduin code would be:
RH=(31*analogRead(aPin)*5/1023)-12 or, RH= 0.1515*analogRead(aPin)-12.0

Temperature
Resistance in Kohms,Temperature in C
163,81,0
97.10,10
59.42,20
47.0,25
37.43,30
24.19,40
16.01,50
10.83,60

The curve fit for this data is:
Temperature in C = 281.583*1.0230^(1.0/R)*R^-0.1227 -150.6614, or in Arduino speak, T = 281.583*pow(1.0230,(1.0/R))*pow(R,-0.1227) -150.6614

So we have Temperature given an RTD value.  But what is the RTD value?  This is a little trickier.  We have a voltage reading at the junction of the  RTD and the 10K resistor.  We have to assume that the voltage across the RTD and 10K series resistor is 5 volts (pull the resistor and you'll get a reading of 1022 on the pin).  So, let's do an example:

At 20 degrees C, the RTD resistance is 59.42 Kohms.   Current (i), is analogvoltage divided by 10Kohms.  The same current flows through the RTD, so we know RTD in Kohms = (5 volts - analogvoltage)/i, so
RTD in Kohms=(5-analogvoltage)*10K/analogvoltage.  So, we get the analogvoltage, calculate RTD and use a curve fit to give us temperature in C.  The Arduino code is:

int Vraw=analogRead(aPin);
float V=(float) Vraw*5.0/1023.0;
float R=(5.0-V)*10.0/V;  //drop the 1000 (K)
float T = 281.583*pow(1.0230,(1.0/R))*pow(R,-0.1227) -150.6614               

Code:


/*
  HGM20G Rev.1
  http://sites.google.com/site/measuringstuff

 */
const byte nsum=10;

int humidityPin=1;
int Thgm20Pin=2;
unsigned int sensorValue2 = 0;  // variable to store the value coming from the sensor
unsigned int sensorValue3 = 0;  // variable to store the value coming from the sensor

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

void loop() {
  for (byte i=0;i<nsum;i++)
  {    
    sensorValue2 += analogRead(humidityPin);    
    sensorValue3 += analogRead(Thgm20Pin); 
  }    
  int sensorValue2Avg=sensorValue2/nsum;
  float RH= 0.1515*sensorValue2Avg-12.0;
  
  int sensorValue3Avg=sensorValue3/nsum;
  float Vt=(float) sensorValue3Avg*5.0/1023.0;
  float R=(5.0-Vt)*10.0/Vt;
  float TinC=281.583*pow(1.0230,(1.0/R))*pow(R,-0.1227)-150.6614; 
  float TinF=TinC*(9.0/5.0)+32; 
  
  Serial.print(RH,0);
  Serial.println(" %RH");
  Serial.print(TinC,1);
  Serial.println(" deg C");
  Serial.print(TinF,1);
  Serial.println(" deg F");
  
  sensorValue2=0;
  sensorValue3=0;
  delay(5000);
}

500 gram Load Cell 


 What it is called: Load Cell, 500 gram
 What it does: Weight measurement
 What it takes: load cell, op amp, arduino
 What pins are used: digital 3, digital 4, analog 0
 What libraries are needed: optional serial LCD library


Parts:

100K resistors (2)
optional serial LCD 

Wiring:








Display Example:


200 grams

Comments:

Resistor values may need to be reduced (85K/85K instead of 100K/100K) to get the amplified voltage range inside the 0 to 1.1 volt analog reference.  You need the reduced reference to give you the best resolution.  Zeroing is done with an interrupt attached to pin 3 (interrupt 1).  

Code:

/*
load cell Rev.1
http://sites.google.com/site/measuringstuff


*/
#include "SparkSoftLCD.h"
// curve fit for analog volts as int (0-1023) to grams
const float slope=0.8319;
const float constant=-113.27;
//number of samples to take for average
const byte avgNum=100;
// serial rec line here
const byte lcdTxPin=4;
// analog read pin
const byte aPin= 0;
// check for zeroing routine request
// int 1 is tied to pin 3
const byte zeroChkPin=3;
const byte aRefVolts=1.1;

SparkSoftLCD Lcd = SparkSoftLCD(lcdTxPin);

float offset=0;
long vRawWeight=0;
int grams=0;
float g=0;
byte zeroPinStatus=0;
byte zeroRoutineFlagSet=0;

void setup()
{  
Serial.begin(9600);
pinMode(lcdTxPin, OUTPUT);
pinMode(zeroChkPin,INPUT);
attachInterrupt(1,zeroRoutineSet,LOW);
Lcd.begin(9600);
// switch to 1.1 internal reference voltage and stabilize
analogReference(INTERNAL);
vRawWeight=analogRead(aPin);
delay(10);
vRawWeight=analogRead(aPin);
// zero the display
zeroRoutine();
delay(500);
}

void loop()
{   
calcGrams();
// uncomment this line for calibration
// Serial.println(vRawWeight,DEC);
Lcd.clear();
Lcd.print(grams,DEC);
Lcd.cursorTo(1,5);
Lcd.print("grams");
// check for zero request 
if (zeroRoutineFlagSet==1)
{
  zeroRoutine();
  zeroRoutineFlagSet=0;
}
}

void calcGrams()
{
  vRawWeight=0;
  // take avgNum samples
  for (int i=0;i<avgNum;i++)
  {
    int avalue=analogRead(aPin);
    vRawWeight=vRawWeight+avalue;
    delay(5);
  }
  vRawWeight=vRawWeight/avgNum;
  // calculate grams and round upward if necessary
  g=slope*(float)vRawWeight+constant+offset;
  grams=(int)g;
  float remainder=g-(float)grams;
  if (remainder > 0.5) grams=grams+1;
}

void zeroRoutine()
{
  offset=0;
  Lcd.clear();
  Lcd.print("zeroing...");
  // check zero and offset if needed
  calcGrams();
  if (grams!=0) offset=-g;
}

void zeroRoutineSet()
{
  zeroRoutineFlagSet=1;
}


I2C-7SEGMENT Display (okay, its not a sensor)


 What it is called: i2c 7 segment display 
 What it does: provides a 4 digit numerical display
 What it takes: display and Arduino with i2c breakout
 What pins are used: analog 4 (sda), analog 5 (sdl) on Duemilanove
 What libraries are needed: the code has not been placed in a library (yet)


Parts:

i2c-7seg

Wiring:

Wiring is easy.  Plus 5v to pin 1 (left), ground to pin 2, analog 4 to pin 3 and analog 5 to pin 4.


Comments:

It's not a sensor but it runs on the i2c bus like many sensors do, and so here it is.  

This is a very bright display that can be seen from some distance, and that is really about the only reason to use it instead of a serial LCD display, especially since it cost about the same.  You are also limited to mostly numbers with a few cobbled together letters. Can't really see much use for it except where you are measuring voltages, or some numeric value and want a large, well illuminated output.  If this thing sold for $10 I'd be drooling; $23 is just too much.

Code:

The first code set is directly from the Gravitech website.  

/**********************************************************

Example program I2C-7SEG interface with Arduino.



SETUP:    I2C-7SEG => Arduino

          PIN1 => +5V PIN2 => GND, PIN3 => A4, PIN4 => A5

Note:     The program is written for address 0x70 (Arduino address 0x38).

          This program was tested using Arduino Nano

Document: SAA1064 datasheet

Updated:  July 30, 2008

E-mail:   support@gravitech.us

          Gravitech

(C) Copyright 2008 All Rights Reserved

**********************************************************/



#include <Wire.h> 

 

void setup() 


  Wire.begin();        // join i2c bus (address optional for master) 

  delay(500);


 

void loop() 


  const int lookup[10] = {0x3F,0x06,0x5B,0x4F,0x66,

                          0x6D,0x7D,0x07,0x7F,0x6F};

  int Count, Thousands, Hundreds, Tens, Base;

  

  Wire.beginTransmission(0x38);

  Wire.send(0);

  Wire.send(B01000111);

  Wire.endTransmission();

  

  for (Count=0;Count<=9999;Count++)

  {

    Wire.beginTransmission(0x38);

    Wire.send(1);

    Thousands = Count/1000;

    Hundreds = (Count-(Thousands*1000))/100;

    Tens = (Count-((Thousands*1000)+(Hundreds*100)))/10;

    Base = Count-((Thousands*1000)+(Hundreds*100)+(Tens*10));

    Wire.send(lookup[Base]);

    Wire.send(lookup[Tens]);

    Wire.send(lookup[Hundreds]);

    Wire.send(lookup[Thousands]);

    Wire.endTransmission();

    delay(10);

  }


Gravitech also makes a 7 segment shield, and the code for it has some additional clues about decimal points and such. So, I took their more complete shield example code, added my own temperature sensor, the wonderful TM36 (hooked to analog 0), and created a temperature display. The inner workings of the display are as follows:

0 is all segments off, and the following numbers control each segment:

         1
       ------
32  | 64 | 2 
       ------
16  |       | 4
       ------        o 128 (the decimal point)
         8

Therefore if I want a the first LED segment to display a 1, I send:

Wire.send(1);              // something is coming
Wire.send((2+4));      // I want segments 2 and 4 turned on
Wire.endTransmission();

The numbers are: 0=63 (1+2+4+8+16+32), 1=6 (2+4), 2=91 (1+2+64+16+8), 3=79, 4=102, 5=109, 6=125, 7=7, 8=127, 9=111

If I want to display 3.145 I would send:

Wire.send(1);  // it's coming
Wire.send(109);
Wire.send(102);
Wire.send(6);
Wire.send((79+128));
Wire.endTransmission();

There is no way to skip over a digit (update digit 3 without chnaging digit 2).  You must send the entire string.

/**********************************************************

Example program I2C-7SEG interface with Arduino.
SETUP:    I2C-7SEG => Arduino
          PIN1 => +5V PIN2 => GND, PIN3 => A4, PIN4 => A5
Note:     The program is written for address 0x70 (Arduino address 0x38).
          This program was tested using Arduino Nano
Document: SAA1064 datasheet
Updated:  July 30, 2008
E-mail:   support@gravitech.us
          Gravitech
(C) Copyright 2008 All Rights Reserved

9/26/10 - http://sites.google.com/site/measuringstuff
Added code for an LM36 temperature sensor read by analog 0
Displays temperature after counting to 999
**********************************************************/

#include <Wire.h> 

#define BAUD (9600)    /* Serial baud define */
#define _7SEG (0x38)   /* I2C address for 7-Segment */
// place other i2c addresses here e.g.
// #define EEP (0x50)     /* I2C address for some type of EEPROM */

const byte tPin=0;
// segments are from top clockwise 1,2,4,8,16,32, and 64 is the middle segment
// codes for numbers 0-9, for a decimal point add 128
const byte lookup[10] = {63,6,91,79,102,109,125,7,127,111};
int Count, Thousands, Hundreds, Tens, Ones;

void setup() 
  //Serial.begin(9600);
  Wire.begin();  // join i2c bus (address optional for master) 
  delay(500);
  initialize();
  delay(500);
  test7segment();
  delay(500);
  clearDisplay();
  delay(500);

void loop() 
    int rawV=analogRead(tPin);
    float v=(float)rawV*5.0/1023.0;
    float tC=(100*v)-50;
    int tF=(9*tC/5)+32;
    sendTempInF(tF);
    // Serial.println(tF,DEC);
    delay(5000); 
 
void initialize()
{
  // Configure 7-Segment to 12mA segment output current, 
  // Dynamic mode, and Digits 1, 2, 3 AND 4 are NOT blanked
  Wire.beginTransmission(_7SEG);
  Wire.send(0);
  Wire.send(B01000111);
  Wire.endTransmission();
  clearDisplay();  
}

void clearDisplay()
{
  Wire.beginTransmission(_7SEG);
  Wire.send(1);
  Wire.send(0);
  Wire.send(0);
  Wire.send(0);
  Wire.send(0);
  Wire.endTransmission();
}

void test7segment()
{
  // Test 7-Segment
  for (byte counter=0; counter<8; counter++)
  {
    Wire.beginTransmission(_7SEG);
    Wire.send(1);
    for (byte counter2=0; counter2<4; counter2++)
    {
      Wire.send(1<<counter);
    }
    Wire.endTransmission();
    delay (250);
  }
}

void sendTempInF(int number)
{
  Wire.beginTransmission(_7SEG);
  Wire.send(1);
  Count=number;
  Thousands = Count/1000;
  Hundreds = (Count-(Thousands*1000))/100;
  Tens = (Count-((Thousands*1000)+(Hundreds*100)))/10;
  Ones = Count-((Thousands*1000)+(Hundreds*100)+(Tens*10));
  // 113 is the code for F (fahrenheit)
  // c would be 64+16+8
  Wire.send(113);
  Wire.send(lookup[Ones]);
  Wire.send(lookup[Tens]);
  if (Hundreds==0) Wire.send(0); 
  else Wire.send(lookup[Hundreds]);
  // Wire.send(lookup[Thousands]);
  Wire.endTransmission();
  delay(10);
}

Works great!  Display costs too much.
Comments