Skip to content
Browse files

Wireless Arduino temperature sensor

  • Loading branch information...
0 parents commit d32203d6cb3b42f7e14293f1d0fe73cbcf1142b4 @drbrain committed Aug 30, 2011
Showing with 622 additions and 0 deletions.
  1. +1 −0 .gitignore
  2. +243 −0 DHT22.cpp
  3. +37 −0 DHT22.h
  4. +42 −0 README.rdoc
  5. +71 −0 temperature_monitor.pde
  6. +12 −0 watch.rb
  7. +216 −0 xbee_modem_setup.rb
1 .gitignore
@@ -0,0 +1 @@
+*.swp
243 DHT22.cpp
@@ -0,0 +1,243 @@
+/*
+ DHT22.cpp - DHT22 sensor library
+ Developed by Ben Adams - 2011
+ Original source: https://github.com/nethoncho/Arduino-DHT22
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+Humidity and Temperature Sensor DHT22 info found at
+http://www.sparkfun.com/products/10167
+
+Version 0.4: 24-Jan-2011 by Ben Adams
+Added return code constants to keywords.txt
+Returns DHT_ERROR_CHECKSUM on check sum mismatch
+
+Version 0.3: 17-Jan-2011 by Ben Adams
+This version reads data
+Needs check sum code added at the end of readData
+
+Version 0.2: 16-Jan-2011 by Ben Adams
+Changed coding style to match other Arduino libraries.
+This version will not read data either!
+
+Version 0.1: 10-Jan-2011 by Ben Adams nethoncho AT gmail.com
+First Version is a skeleton. This version will not read data!
+Code adapted from the following sources:
+The Arduino OneWire lib
+http://sheepdogguides.com/arduino/ar3ne1humDHT11.htm
+
+*/
+
+#include "DHT22.h"
+#include "pins_arduino.h"
+
+extern "C" {
+#include "WConstants.h"
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <avr/pgmspace.h>
+}
+
+#define DIRECT_READ(base, mask) (((*(base)) & (mask)) ? 1 : 0)
+#define DIRECT_MODE_INPUT(base, mask) ((*(base+1)) &= ~(mask))
+#define DIRECT_MODE_OUTPUT(base, mask) ((*(base+1)) |= (mask))
+#define DIRECT_WRITE_LOW(base, mask) ((*(base+2)) &= ~(mask))
+//#define DIRECT_WRITE_HIGH(base, mask) ((*(base+2)) |= (mask))
+
+// This should be 40, but the sensor is adding an extra bit at the start
+#define DHT22_DATA_BIT_COUNT 41
+
+DHT22::DHT22(uint8_t pin)
+{
+ _bitmask = digitalPinToBitMask(pin);
+ _baseReg = portInputRegister(digitalPinToPort(pin));
+ _lastReadTime = millis();
+ _lastHumidity = DHT22_ERROR_VALUE;
+ _lastTemperature = DHT22_ERROR_VALUE;
+}
+
+//
+// Read the 40 bit data stream from the DHT 22
+// Store the results in private member data to be read by public member functions
+//
+DHT22_ERROR_t DHT22::readData()
+{
+ uint8_t bitmask = _bitmask;
+ volatile uint8_t *reg asm("r30") = _baseReg;
+ uint8_t retryCount;
+ uint8_t bitTimes[DHT22_DATA_BIT_COUNT];
+ int currentHumidity;
+ int currentTemperature;
+ uint8_t checkSum, csPart1, csPart2, csPart3, csPart4;
+ unsigned long currentTime;
+ int i;
+
+ currentHumidity = 0;
+ currentTemperature = 0;
+ checkSum = 0;
+ currentTime = millis();
+ for(i = 0; i < DHT22_DATA_BIT_COUNT; i++)
+ {
+ bitTimes[i] = 0;
+ }
+
+ if(currentTime - _lastReadTime < 2000)
+ {
+ // Caller needs to wait 2 seconds between each call to readData
+ return DHT_ERROR_TOOQUICK;
+ }
+ _lastReadTime = currentTime;
+
+ // Pin needs to start HIGH, wait until it is HIGH with a timeout
+ cli();
+ DIRECT_MODE_INPUT(reg, bitmask);
+ sei();
+ retryCount = 0;
+ do
+ {
+ if (retryCount > 125)
+ {
+ return DHT_BUS_HUNG;
+ }
+ retryCount++;
+ delayMicroseconds(2);
+ } while(!DIRECT_READ(reg, bitmask));
+ // Send the activate pulse
+ cli();
+ DIRECT_WRITE_LOW(reg, bitmask);
+ DIRECT_MODE_OUTPUT(reg, bitmask); // Output Low
+ sei();
+ delayMicroseconds(1100); // 1.1 ms
+ cli();
+ DIRECT_MODE_INPUT(reg, bitmask); // Switch back to input so pin can float
+ sei();
+ // Find the start of the ACK Pulse
+ retryCount = 0;
+ do
+ {
+ if (retryCount > 25) //(Spec is 20 to 40 us, 25*2 == 50 us)
+ {
+ return DHT_ERROR_NOT_PRESENT;
+ }
+ retryCount++;
+ delayMicroseconds(2);
+ } while(!DIRECT_READ(reg, bitmask));
+ // Find the end of the ACK Pulse
+ retryCount = 0;
+ do
+ {
+ if (retryCount > 50) //(Spec is 80 us, 50*2 == 100 us)
+ {
+ return DHT_ERROR_ACK_TOO_LONG;
+ }
+ retryCount++;
+ delayMicroseconds(2);
+ } while(DIRECT_READ(reg, bitmask));
+ // Read the 40 bit data stream
+ for(i = 0; i < DHT22_DATA_BIT_COUNT; i++)
+ {
+ // Find the start of the sync pulse
+ retryCount = 0;
+ do
+ {
+ if (retryCount > 35) //(Spec is 50 us, 35*2 == 70 us)
+ {
+ return DHT_ERROR_SYNC_TIMEOUT;
+ }
+ retryCount++;
+ delayMicroseconds(2);
+ } while(!DIRECT_READ(reg, bitmask));
+ // Measure the width of the data pulse
+ retryCount = 0;
+ do
+ {
+ if (retryCount > 50) //(Spec is 80 us, 50*2 == 100 us)
+ {
+ return DHT_ERROR_DATA_TIMEOUT;
+ }
+ retryCount++;
+ delayMicroseconds(2);
+ } while(DIRECT_READ(reg, bitmask));
+ bitTimes[i] = retryCount;
+ }
+ // Now bitTimes have the number of retries (us *2)
+ // that were needed to find the end of each data bit
+ // Spec: 0 is 26 to 28 us
+ // Spec: 1 is 70 us
+ // bitTimes[x] <= 11 is a 0
+ // bitTimes[x] > 11 is a 1
+ // Note: the bits are offset by one from the data sheet, not sure why
+ for(i = 0; i < 16; i++)
+ {
+ if(bitTimes[i + 1] > 11)
+ {
+ currentHumidity |= (1 << (15 - i));
+ }
+ }
+ for(i = 0; i < 16; i++)
+ {
+ if(bitTimes[i + 17] > 11)
+ {
+ currentTemperature |= (1 << (15 - i));
+ }
+ }
+ for(i = 0; i < 8; i++)
+ {
+ if(bitTimes[i + 33] > 11)
+ {
+ checkSum |= (1 << (7 - i));
+ }
+ }
+
+ _lastHumidity = currentHumidity & 0x7FFF;
+ if(currentTemperature & 0x8000)
+ {
+ // Below zero, non standard way of encoding negative numbers!
+ currentTemperature &= 0x7FFF;
+ _lastTemperature = currentTemperature * -1;
+ }
+ else
+ {
+ _lastTemperature = currentTemperature;
+ }
+
+ csPart1 = currentHumidity >> 8;
+ csPart2 = currentHumidity & 0xFF;
+ csPart3 = currentTemperature >> 8;
+ csPart4 = currentTemperature & 0xFF;
+ if(checkSum == ((csPart1 + csPart2 + csPart3 + csPart4) & 0xFF))
+ {
+ return DHT_ERROR_NONE;
+ }
+ return DHT_ERROR_CHECKSUM;
+}
+
+int DHT22::getHumidity()
+{
+ return _lastHumidity;
+}
+
+int DHT22::getTemperatureC()
+{
+ return _lastTemperature;
+}
+
+//
+// This is used when the millis clock rolls over to zero
+//
+void DHT22::clockReset()
+{
+ _lastReadTime = millis();
+}
37 DHT22.h
@@ -0,0 +1,37 @@
+#ifndef _DHT22_H_
+#define _DHT22_H_
+
+#include <inttypes.h>
+
+#define DHT22_ERROR_VALUE -99.5
+
+typedef enum
+{
+ DHT_ERROR_NONE = 0,
+ DHT_BUS_HUNG,
+ DHT_ERROR_NOT_PRESENT,
+ DHT_ERROR_ACK_TOO_LONG,
+ DHT_ERROR_SYNC_TIMEOUT,
+ DHT_ERROR_DATA_TIMEOUT,
+ DHT_ERROR_CHECKSUM,
+ DHT_ERROR_TOOQUICK
+} DHT22_ERROR_t;
+
+class DHT22
+{
+ private:
+ uint8_t _bitmask;
+ volatile uint8_t *_baseReg;
+ unsigned long _lastReadTime;
+ double _lastHumidity;
+ double _lastTemperature;
+
+ public:
+ DHT22(uint8_t pin);
+ DHT22_ERROR_t readData(void);
+ int getHumidity();
+ int getTemperatureC();
+ void clockReset();
+};
+
+#endif /*_DHT22_H_*/
42 README.rdoc
@@ -0,0 +1,42 @@
+= Arduino + DHT22 + XBee Temperature Monitor
+
+This project allows you to monitor the temperature remotely. The hardware used
+in this project includes:
+
+* {Arduino Uno}[http://www.sparkfun.com/products/10356]
+* {DHT22 Humidity & Temperature Sensor}[http://www.sparkfun.com/products/10167]
+* Two {XBees}[http://www.sparkfun.com/products/8664]
+* One {XBee Explorer}[http://www.sparkfun.com/products/8687]
+* One {XBee Adapter Kit}[http://www.adafruit.com/products/126]
+* {Jumper wires}[http://www.sparkfun.com/products/9387]
+* One {Breadboard}[http://www.sparkfun.com/products/137]
+
+The XBee Adapter Kit was my first soldering attempt and I'm told I did a great
+job. A PanaVise Junior and a Third Hand helped immensely, along with choosing
+a 1/64 inch soldering iron tip over the 1/32" tip that came with my soldering
+iron.
+
+I also cut three jumper wires, stripped them and soldered them to the DHT22.
+This was more difficult, but placing the sensor in the vise and using the Third
+Hand to hold the wire with a folded up piece of paper to protect the insulation
+helped line everything up.
+
+I attached the XBee Adapter to the breadboard and wired up 5V power and ground.
+I attached the TX pin of the adapter to digital pin 2 of the Arduino and the RX
+pin of the adapter to digital pin 3 of the Arduino. I attached the sensor's
+data pin to port 7 (from the {DHT22
+source}[https://github.com/nethoncho/Arduino-DHT22]) and the ground and power
+to the proper pins on the breadboard.
+
+The xbee_mode_setup.rb script was used to set up the to XBees to communicate with each other. Be sure to use the Arduino XBee's serial number as the destination address of the XBee Explorer and vice versa.
+
+The watch.rb script can be used to monitor the temperature the Arduino reads.
+
+Excluding the cost of the soldering equipment, this project cost under $150.
+
+Most of the software used in this project was already written by third-parties
+that I adapted to my needs. The DHT22 library was changed from returning float
+to int (that I can decode on the ruby side). xbee_mode_setup.rb was updated to
+work with a newer serialport gem for ruby and the documentation was
+reformatted.
+
71 temperature_monitor.pde
@@ -0,0 +1,71 @@
+#include <SoftwareSerial.h>
+#include "DHT22.h"
+
+#define XBee_rx_PIN 2
+#define XBee_tx_PIN 3
+#define DHT22_PIN 7
+#define interval 2
+
+DHT22 temp_sensor(DHT22_PIN);
+SoftwareSerial XBee(XBee_rx_PIN, XBee_tx_PIN);
+
+void setup(void)
+{
+ pinMode(XBee_rx_PIN, INPUT);
+ pinMode(XBee_tx_PIN, OUTPUT);
+
+ XBee.begin(9600);
+
+ Serial.begin(9600);
+}
+
+void loop(void)
+{
+ DHT22_ERROR_t errorCode;
+ char out[40] = "";
+
+ delay(interval * 1000);
+ errorCode = temp_sensor.readData();
+ switch(errorCode)
+ {
+ case DHT_ERROR_NONE:
+ snprintf(out, 40, "%5iC %5i%%",
+ temp_sensor.getTemperatureC(), temp_sensor.getHumidity());
+
+ Serial.println(out);
+ XBee.println(out);
+ break;
+ case DHT_ERROR_CHECKSUM:
+ snprintf(out, 40, "check sum error %5iC %5i%%",
+ temp_sensor.getTemperatureC(), temp_sensor.getHumidity());
+
+ Serial.println(out);
+ XBee.println(out);
+ break;
+ case DHT_BUS_HUNG:
+ Serial.println("BUS Hung");
+ XBee.println("BUS Hung");
+ break;
+ case DHT_ERROR_NOT_PRESENT:
+ Serial.println("Not Present");
+ XBee.println("Not Present");
+ break;
+ case DHT_ERROR_ACK_TOO_LONG:
+ Serial.println("ACK timeout");
+ XBee.println("ACK timeout");
+ break;
+ case DHT_ERROR_SYNC_TIMEOUT:
+ Serial.println("Sync Timeout");
+ XBee.println("Sync Timeout");
+ break;
+ case DHT_ERROR_DATA_TIMEOUT:
+ Serial.println("Data Timeout");
+ XBee.println("Data Timeout");
+ break;
+ case DHT_ERROR_TOOQUICK:
+ Serial.println("Polled too quick");
+ XBee.println("Polled too quick");
+ break;
+ }
+}
+
12 watch.rb
@@ -0,0 +1,12 @@
+require 'rubygems'
+require 'serialport'
+
+device = Dir['/dev/tty.usbserial*'].first
+
+puts "listening on #{device}"
+xbee = SerialPort.new device, 9600, 8, 1, SerialPort::NONE
+
+loop do
+ puts xbee.gets
+end
+
216 xbee_modem_setup.rb
@@ -0,0 +1,216 @@
+# jd barnhart (c) 2008 - jd at jdbarnhart dot com -
+# https://github.com/madrona/xbee-modem-setup
+#
+# = Setup Xbee Modems
+#
+# Xbee modems ship in router configuration, but can be configured as routers
+# or coordinators with the digi X-CTU software (windows only). A coordinator
+# is necessary to configure a mesh, however for direct communication between
+# modems a coordinator is unnecessary. So for two Xbee modems, the as shipped
+# router firmware works fine.
+#
+# The purpose of this script:
+#
+# An Xbee module will only communicate with other modules having the same
+# channel (CH parameter), PAN ID (ID parameter) and destination address
+# (DH + DL parameters). Reading and setting these parameters is the purpose
+# of this script
+#
+# This could be thought of as 3 layers: Channel, Pan ID and Destination
+# Address low and high: DL/DH
+#
+# Note: when a constant is not set, that constant is not written, only read
+#
+# == Step 1
+#
+# Install ruby serialport gem:
+#
+# gem install serialport
+#
+# == Step 2
+#
+# Add an xbee shield with xbee shield with both jumpers in usb position
+# (nearest the board edge) to the arduino connect the arduino to your computer
+# run this script the result should look similar to this:
+#
+# using tty.usbserial-A60048pt
+#
+# Connected to: serial port
+# cmd: ATVR, result: 1220 OK
+# cmd: ATCH, result: 16 OK
+# cmd: ATID, result: 555 OK
+# cmd: ATSL, result: 4052D736 OK
+# cmd: ATSH, result: 13A200 OK
+# cmd: ATDL, result: 4052DAF7 OK
+# cmd: ATDH, result: 13A200 OK
+# cmd: ATNI, result: VISCONTI OK
+# serial port closed
+#
+# What is all this?
+#
+# ATVR:: firmware version
+# ATCH:: channel (from 1-16)
+# ATID:: pan id, or Personal Area Network ID
+# ATSL:: serial number low (record this)
+# ATSH:: serial number high (record this)
+# ATDL:: destination low (assign the serial number low of the target modem to
+# this)
+# ATDH:: destination high (assign the serial number high of the target modem
+# to this)
+# ATNI:: networking identification (assign a human readable name... not
+# necessary but very handy)
+#
+# Take a note of the serial low and high, since these will be assigned to
+# destination high and low of the other (destination) modem
+#
+# == Step Three
+#
+# Enter your configuration and run the script
+#
+# Set the CH, ID, DL, DH and NI parameters below.
+#
+# == Step Four
+#
+# run the script and then repeat for your other modem.
+#
+# == Reference
+#
+# ID::
+# The network ID of the Xbee module. 0 - 0xFFFF Default: 3332
+# CH::
+# The channel of the Xbee module. 0x0B - 0x1A Default: 0X0C
+# SH, SL::
+# The serial number of the Xbee module (SH gives the high 32 bits, SL the
+# low 32 bits). Read-only. 0 - 0xFFFFFFFF (for both SH and SL) Default:
+# different for each module
+# MY::
+# The 16-bit address of the module. 0 - 0xFFFF Default: 0
+# DH, DL::
+# The destination address for wireless communication (DH is the high 32
+# bits, DL the low 32). 0 - 0xFFFFFFFF (for both DH and DL) 0 (for both
+# DH and DL)
+# BD::
+# The baud rate used for serial communication with the Arduino board or
+# computer. Default: 3 (9600)
+# 0:: 1200 bps
+# 1:: 2400 bps
+# 2:: 4800 bps
+# 3:: 9600 bps
+# 4:: 19200 bps
+# 5:: 38400 bps
+# 6:: 57600 bps
+# 7:: 115200 bps
+
+# Channel
+CH = "" # 11 - 26 for XBee modules and 12 - 23 for XBee Pro modules.
+
+# Pan ID
+ID = "3332" # 0 - 65535
+
+# Destination Address Low -- should be the serial low of the modem you wish to
+# send to... run this script to find out
+DL = "406A7078"
+
+# Destination Address High -- should be the serial low of the modem you wish
+# to send to....run this script to find out
+DH = "13A200"
+
+# Networking Identification
+NI = "segment7" # anything memorable up to 20 characters
+
+# determine the usb address
+
+USB = Dir["/dev/tty.usbserial*"].first
+puts "using #{USB}"
+puts
+
+commands = [
+ { :ATVR => "" }, # firmware version
+ { :ATCH => CH }, # channel
+ { :ATID => ID }, # pan id
+ { :ATSL => "" }, # serial number low (cannot be changed)
+ { :ATSH => "" }, # serial number high (cannot be changed)
+ { :ATDL => DL }, # destination address low
+ { :ATDH => DH }, # destination address high
+ { :ATNI => NI } #
+]
+
+require 'rubygems'
+require 'serialport'
+
+class Xbee
+
+ def initialize(options = {})
+ begin
+ # dev/tty will most likely be different for you
+ port = options[:port] || USB
+ baud = options[:baud] || 9600
+ bits = options[:bits] || 8
+ stop = options[:stop] || 1
+
+ @port = SerialPort.new(port, baud, bits, stop, SerialPort::NONE)
+ @debug = options[:debug]
+
+ # timeout is touchy
+ @port.read_timeout = 100
+ rescue Errno::EBUSY
+ raise "Cannot connect to the serial port is busy or unavailable."
+ end
+
+ if @port.nil?
+ $stderr.puts "Cannot connect to usbserial"
+ else
+ puts "Connected to: serial port" if @debug
+ end
+ setup
+ end
+
+ def close
+ @port.close
+ end
+
+ ##
+ # enter AT Command Mode
+
+ def setup
+ @port.write("+++")
+ sleep 1.2
+ end
+
+ ##
+ # Write each command
+
+ def cmd(cmd)
+ @port.write(cmd + "\r")
+ verify(cmd)
+ end
+
+ ##
+ # Verify each command
+
+ def verify(cmd)
+ result = @port.read
+ print_result(cmd, result)
+ end
+
+ ##
+ # Print each result
+
+ def print_result(cmd, result)
+ puts "cmd: #{cmd.chomp} result: #{result}"
+ end
+
+end
+
+xb = Xbee.new(:debug => true)
+
+commands.each do |command|
+ command.each do |k, v|
+ xb.cmd("#{k}#{v}, \r")
+ xb.cmd("#{k},WR \r") if v != ""
+ end
+end
+
+xb.close
+puts "serial port closed"
+

0 comments on commit d32203d

Please sign in to comment.
Something went wrong with that request. Please try again.