Monday, October 24, 2011

Wireless humidity and temperature sensor

This project is a wireless zigbee based wireless temperature and humidity sensor.

This is a writeup on how I created this project to wirelessly send data from a temperature and humidity sensor.
It's not pretty, but it works. Here is a picture of the end product:



From turtlebot-projects
Click to enlarge


It's not going to be pretty, but it is pretty easy to put together. It took me about 8 hours to think out, assemble, and program.

I used the following parts:
  1. 2 xbee series 2 modules. Like this one: http://www.sparkfun.com/products/10415
  2. 1 xbee usb breakout board http://www.robotshop.com/parallax-breakout-board-for-xbee-module.html?utm_source=google&utm_medium=base&utm_campaign=jos
  3. 1 arduino protoshield http://www.sparkfun.com/products/7914
  4. 1 3.3 volt arduino pro http://www.sparkfun.com/products/9221
  5. 1 Stackable header pins for the arduino pro http://www.sparkfun.com/products/10007
  6. tmp102 temp sensor http://www.sparkfun.com/products/9418
  7. hh10d humidity sensor http://www.sparkfun.com/products/10239
  8. 1 ftdi breakout to program the arduino with http://www.sparkfun.com/products/9873
  9. 1 3.3 volt 2x8 lcd http://www.newhavendisplay.com/index.php?main_page=product_info&cPath=2_80&products_id=1908
  10. some number of batteries. I with with three lipo batteries that fit well in my case, and they are something that I can charge off of 12 volts. I got mine off of ebay, and do not have a link
  11. 1 A arduino project enclosure http://www.sparkfun.com/products/10088
  12. 1 little button for the xbe reset pin http://www.sparkfun.com/products/97

The components get hooked up as follows:
Arduino:
Wired as follows:
Digital pins:
0 - to a jumper, then xbee pin 2
1 - to a jumper, then xbee pin 3
2 - LCD D6
3 - LCD D7
4 - nc
5 - hh10d output pin
6 - LCD D4
7 - LCD D5
8 - LCD Enable Pin
9 - LCD RS pin
Analog pins:
A4 - temp and humidity sensor SDA
A5 - temp and humidity sensor SCL

hh10d:
SDA - arduino pin a4
SDL - arduuino pin a5
FOUT - arduino pin 5

tmp102:
SDA - arduino pin a4
SDL - arduuino pin a5
ALT - nc
ADD0 - ground

xbee module:
1 - 3.3v
2 - first a jumpe, then arduino pin 0
3 - first a jumpe, then arduino pin 1
5 - to a 10k resistor to vcc and a button connected to ground
6 - input pin to the available led on the protoshield
10 - ground

I start by configuring the xbee modules.
I first gathered the addresses of my xbee modules. In my case, my values are:
router module:
0013A200 40627E7E
cordinator module:
0013A200 403045E1

Everything can be configured on the xbee modules via AT commands, but I already had a windows machine with a config utility on it, and I needed to upgrade the firmware on the modules, so I followed the following steps to configure my modules:
1. Download the config utility here:
http://www.digi.com/support/productdetl.jsp?pid=3352&osvid=57&s=316&tp=5&tp2=0

For the next steps, you will need the high and low addresses for your modules. Get them from the bottom of your modules. The bottom of the modules look like this:
From turtlebot-projects

In this case, the high and low address for this module are:
high - 0013A200
low - 40627E74

Program the xbee modules with these steps
1. plug the xbee module into the usb breakout board.
2. plug the breakout board into your computer
3. open x-ctu
4. select the correct serial port for your xbee module.
5. Select the "modem configuration" tab.
6. Read the modem parameters and firmware.
7. one of your modules is going to need to be a router, and one is going to need to be a coordinator.
Set up the router first. So, select "ZIGBEE ROUTER AT" from the "Function Set" dropdown
8. Set the pan ID to any number from 0 to FFFF will work. I like something small, like "21"
9. Place the high address of your coordinator into the "Destination High Address" field of this module.
10. Place the low address of your coordinator into the "Destination Low Address" field of this module.
11. Select the highest version number possible in the version number dropdown.
12. Program it.

Now, take this module and set it aside, we will be installing it in our sensor device later.

Repeat steps 1-12 for your coordinator module. Just change step 7 to add "ZIGBEE CORDINATOR AT" instead of the router at.
Also, step 9 and 10 will need you to put the router module's high and low address instead of the address from the coordinator.

great, so I mashed all of my components into my device like this:




From turtlebot-projects



I put my battries on the top under the lcd, with the lcd glued to the batteries.

You can see the rx and tx jumper header between the lcd and the zigbee module.

I put a 12 v coaxial connector in the case to allow for easy charging from 12 volts.

The code output looks like this:
offset is 7847
humidity reading device hh10d
Calibrating:
0  Freq: 0
Relitive humidity: 727
Converted temp is       27.69
Corrected temp is       22.69

Output is: Seconds since start, humidity, temp in Deg C,temp in Deg F
3.4,-6039,27.69,81.84
4.0,41,27.75,81.95
4.5,41,27.62,81.72
5.0,44,27.69,81.84
5.6,47,29.00,84.20



And here is all of the code, syntax highlighted by the cool little program found here:
http://www.palfrader.org/code2html/

// humidity and temp transmitter with a lcd output.

/*
 Michael Gregg
 
   Wired as follows:
   0 - to a jumper, then xbee pin 2
   1 - to a jumper, then xbee pin 3
   2 - LCD D6
   3 - LCD D7
   4 - nc
   5 - hh10d output pin
   6 - LCD D4
   7 - LCD D5
   8 - LCD Enable Pin
   9 - LCD RS pin
   A4 - temp and humidity sensor SDA
   A5 - temp and humidity sensor SCL
 
 Code based off of:
 http://tushev.org/articles/electronics/46-interfacing-hh10d-with-arduino
 http://www.arduino.cc/playground/Code/TMP102
 http://www.arduino.cc/en/Tutorial/LiquidCrystal
 */
#include <FreqCounter.h>
#include <Wire.h>
#include <LiquidCrystal.h>
#define TMP102_I2C_ADDRESS 72 

LiquidCrystal lcd(8, 9, 6, 7, 2, 3);

int freq, offset, sens, RH;
char buff[30];
float convertedtemp, tempf; /* We then need to multiply our two bytes by a scaling factor, mentioned in the datasheet. */
float correctedtemp;

unsigned long frq;
int cnt,seconds,partialseconds;
int pinLed=13;
int val; /* an int is capable of storing two bytes, this is where we "chuck" the two bytes together. */

// arrays used to build custom characters
byte UP[8] = // array to make an arrow pointing up
{
  B00000,
  B00100,
  B01110,
  B11111,
  B00100,
  B00100,
  B00100,
  B00000
};
byte Down[8] = // array to make an arrow pointing down
{
  B00000,
  B00100,
  B00100,
  B00100,
  B11111,
  B01110,
  B00100,
  B00000
};

void setup() {
  pinMode(pinLed, OUTPUT);
  lcd.createChar(0, UP);
  lcd.createChar(1, Down);

  lcd.begin(8, 2);
  lcd.setCursor(0, 0);
  lcd.print("hello   ");
  lcd.setCursor(0, 1);
  lcd.print("World   ");  
  Wire.begin();
  Serial.begin(9600);
  sens    =  i2cRead2bytes(81, 10); //Read sensitivity from EEPROM
  offset =  i2cRead2bytes(81, 12); //Same for offset
  sprintf(buff, "sensitivity is %d", sens);
  Serial.println(buff);
  sprintf(buff, "offset is %d", offset);
  Serial.println(buff);

  Serial.println("humidity reading device hh10d");
  Serial.println("Calibrating:");
  FreqCounter::f_comp=10;   // Cal Value / Calibrate with professional Freq Counter
  FreqCounter::start(500);  // 100 ms Gate Time
  FreqCounter::start(500);  // 100 ms Gate Time
  
  FreqCounter::f_comp=10;   // Cal Value / Calibrate with professional Freq Counter
  FreqCounter::start(500);  // 100 ms Gate Time

  while (FreqCounter::f_ready == 0) 

  frq=FreqCounter::f_freq;
  Serial.print(cnt++);
  Serial.print("  Freq: ");
  Serial.println(frq);
  delay(20);
  digitalWrite(pinLed,!digitalRead(pinLed));  // blink Led
  //Calculate RH
  //frq = frq / 2; // for 8 mhz devices sampeling at 1 hz
  RH =  (offset-frq)*sens/4096; //Sure, you can use int - depending on what do you need
 
  Serial.print("Relitive humidity: ");
  Serial.println(RH);
  getTemp102();
  Serial.print("Converted temp is ");
  Serial.print("\t");
  Serial.println(val*0.0625);
  Serial.print("Corrected temp is ");
  Serial.print("\t");
  Serial.println(correctedtemp);
  Serial.println();
  
  Serial.println("Output is: Seconds since start, humidity, temp in Deg C,temp in Deg F");
  
}

void loop() {

  // wait if any serial is going on
  FreqCounter::f_comp=10;   // Cal Value / Calibrate with professional Freq Counter
  FreqCounter::start(500);  // 100 ms Gate Time

  while (FreqCounter::f_ready == 0) 

  frq=FreqCounter::f_freq;
  //Serial.print(cnt++);
  //Serial.print("  Freq: ");
  //Serial.println(frq);
  delay(20);
  digitalWrite(pinLed,!digitalRead(pinLed));  // blink Led
  //Calculate RH
  //frq = frq / 2; // for 8 mhz devices sampeling at 1 hz
  RH =  (offset-frq)*sens/4096; //Sure, you can use int - depending on what do you need
 
  getTemp102();
  
  partialseconds = millis()/10;
  seconds=partialseconds / 10;
  partialseconds = partialseconds - ( seconds * 10 );
  sprintf(buff,"%d.%d,%d,", seconds, partialseconds, RH);
  Serial.print(buff);
  Serial.print(convertedtemp);
  Serial.print(",");
  Serial.println(tempf);
  //Serial.println(millis());
  lcd.setCursor(0, 0);
  lcd.print("Temp:");
  lcd.print(tempf);
  lcd.print("F");
  lcd.setCursor(0, 1);
  lcd.print("Humd:");
  lcd.print(RH);
  lcd.print("%");
}  

int i2cRead2bytes(int deviceaddress, byte address)  
{
 // SET ADDRESS
 Wire.beginTransmission(deviceaddress);
 Wire.send(address); // address for sensitivity
 Wire.endTransmission();

 // REQUEST RETURN VALUE
 Wire.requestFrom(deviceaddress, 2);
 // COLLECT RETURN VALUE
 int rv = 0;
 for (int c = 0; c < 2; c++ )
 if (Wire.available()) rv = rv * 256 + Wire.receive();
 return rv;
 }

void getTemp102()
{
  byte firstbyte, secondbyte; //these are the bytes we read from the TMP102 temperature registers

  /* Reset the register pointer (by default it is ready to read temperatures)
   You can alter it to a writeable register and alter some of the configuration -
   the sensor is capable of alerting you if the temperature is above or below a specified threshold. */

  Wire.beginTransmission(TMP102_I2C_ADDRESS); //Say hi to the sensor.
  //Serial.println("done with wire begin");
  Wire.send(0x00);
  Wire.endTransmission();
  //Serial.println("done with wire send");
  Wire.requestFrom(TMP102_I2C_ADDRESS, 2);
  Wire.endTransmission();

  //Serial.println("firstbyte");
  firstbyte      = (Wire.receive());
  /*read the TMP102 datasheet - here we read one byte from
   each of the temperature registers on the TMP102*/
  secondbyte     = (Wire.receive());
  /*The first byte contains the most significant bits, and
   the second the less significant */
  val = ((firstbyte) << 4);  
  /* MSB */
  val |= (secondbyte >> 4);    
  /* LSB is ORed into the second 4 bits of our byte.
   Bitwise maths is a bit funky, but there's a good tutorial on the playground*/
  convertedtemp = val*0.0625;
  correctedtemp = convertedtemp - 5;
  tempf = convertedtemp * 1.8 + 32;
  /* See the above note on overreading */
}


Tuesday, July 20, 2010