Sunday, August 28, 2011

DHT22 Temperature/Humidity Sensor

Here's a little something I cobbled together from a DHT22 Sensor, an Adafruit DC BoArduino, and a 3-digit LED display.
More to come!




Here is a schematic of the external hardware:

Figure 1: External Hardware

======================================================
ARDUINO SKETCH FOLLOWS BELOW
======================================================

A few changes:
I added the means to change the brightness and scroll-rate using external analog signals.  Two ordinary pots wired between 0 and +5V (variable voltage divider), with the wiper going to the respective analog pin is all you need.  These parts are not shown in the above schematic.

The code for both is present, however, only the Brightness Control is activated. The scroll rate code is currently commented out, with a fixed scroll rate of 8 char/sec.  Please find the relevant section of code (shown below) in the sketch, and follow the directions.


/* ---------------------------------------------------------------------
      To use an external analog signal controls scroll rate, 4 to 20 char/sec:
      t1Preset = map(analogRead(scrollSpeed),0,1023,250,50);
      (Comment out the line above, "t1Preset = 125;"
      ------------------------------------------------------------------------*/




/*


 DHT22 SCROLLING TEMPERATURE AND HUMIDITY DISPLAY

 ---------------------------------------------
 Author: EasternStarGeek, Johnson City, TN
 Date: 27 August, 2011
 ---------------------------------------------

 This sketch reads a DHT22 Temperature and Humidity Sensor and displays the
 readings on a 3-digit, 7-segment display.

 The three readings scroll by, "Marquee" fashion: Temperature in Degrees F, Temp. in Deg. C, and Percent relative Humidity.

 Normal Display Format:
 (+/-)XXX.y *F  (+/-)XXX.y *C   XX.XH

 (If the senor returns invalid data, all values will show "-999.9")

 Readings repeat in between display updates.


 Hardware:
 Controller: Adafruit DC Boarduino

 Sensor: DHT22
 - 10K, 1/4W Resistor

 Display:
 - 7-Segment LED, 3-Digit, Common Cathode
 - 74HC595 Shift Register
 - 220 ohm, 1/4W resistors (8)
 - 2N2222 Digit Drivers (3)
 - 10K, 1/4W Resistors (3)

 For a complete schematic of th external hardware, please see details in my blog:
 http://easternstargeek.blogspot.com/2011/08/dht22-temperaturehumidity-sensor.html

 This software uses the Adafruit DHT Sensor Library:
 https://github.com/adafruit/DHT-sensor-library

 */

// Include Library:
#include "DHT.h"  // DHT Sensor Library 

// Hardware Connections to I/O Pins:
#define DHTPIN A0     //DHT22 Data Pin
// Note: The sensor data is digital.  Any I/O Pin can be used.

const int dataPin = 11;   // 74HC595 Shift Register Data, IC Pin 14
const int clockPin = 12;  // 74HC595 Shift Register Data Clock, IC Pin 11
const int latchPin = 8;   // 74HC595 Shift Register Latch Clock, IC Pin 12

// Arduino pins for Digit Driver Transistors: 1(MSD), 2, 3(LSD)
const int digitEnable[3] = {
  2,3,4}; // Array holding pin numbers for Digit Driver transistors

const int brightLevel = A5;  // Analog pin for Brightness Control
const int scrollSpeed = A4;  // Analog pin for Scroll Rate Control

// Global Declarations:
#define DHTTYPE DHT22   // DHT 22  (AM2302)
DHT dht(DHTPIN, DHTTYPE);  // Declare Sensor Class

// Lookup Table containing all segment patterns used in this project:
// Character Set:
// 0,1,2,3,4,5,6,7,8,9,<null>,H,t,C,F, -, _, =, <deg symbol>

// Mapping of Display Segments by bit: (MSB) dp,g,f,e,d,c,b,a (LSB)
const byte LUT_Chars[20] = {
  0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,
  0x00,0x76,0x78,0x39,0x71,0x40,0x08,0x48,0x63};

// Proxies for non-numeric characters:
const int chr_nul = 10;  // Pointer to pattern for null (blank) character
const int chr_H = 11;    // Pointer to pattern for 'H'
const int chr_t = 12;    // Pointer to pattern for 't'
const int chr_C = 13;    // Pointer to patterrn for 'C'
const int chr_F = 14;    // Pointer to pattern for 'F'
const int chr_min = 15;  // Pointer to pattern for dash (minus sign)
const int chr_usc = 16;  // Pointer to pattern for underscore
const int chr_equ = 17;  // Pointer to pattern for equal sign
const int chr_deg = 18;  // Pointer to pattern for degree symbol

int valLength;  // Length of returned numerical substring after conversion
int numFieldChars[5]; // Global array hilding converted numerical subsrting
int CharBuffer[3];  // Buffer holding segment patterns for currently displayed digits

int Brightness = 255;  // Brightness Level, 1 to 255
unsigned int T_On;  // LED ON Time  // For display brightness control
unsigned int T_Off; // LED OFF Time  // For display brighness control

int pointerScroll;  // Index to Character Buffer (1=MSD, 3=LSD)
int SCR = 10;  // Sequence Control Register, used w/ Switch/Case
int DisplayLine[47];  //Array holding segment patterns
// for the entire string to be displayed, pre-scroll

int pointerDL;
float h;  //Raw sensor data, Percent Relative Humidity
float tc;  //Raw sensor data, Temperature, Degrees Celsius

/* Timers:
 This project uses a Timer Services that run on every scan, making use of the millis()
 function.  This allows the code to scan continuously with minimal wait-states
 */
// Declare Timer 1 Variables
boolean t1Bit;   // Timer 1 Control Bit
boolean t1Edge;  // Timer 1 Edge Bit
long t1Preset;     // Timer 1 Preset
long t1Temp;     // Timer 1 Temporary Value
long t1Remain;     // Timer 1 Word
// End Timer 1 Variables

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

  dht.begin();  // Initialize Sensor Functions
  // I/O Pin Modes::
  pinMode (dataPin, OUTPUT);
  pinMode (clockPin, OUTPUT);
  pinMode (latchPin, OUTPUT);

  for (int i=0; i<3; i++)  {
    pinMode(digitEnable[i], OUTPUT);
  }

  pinMode (brightLevel, INPUT);
  pinMode (scrollSpeed, INPUT);
}

void loop()
{

  // Switch/Case used to control delayed sequential processes without wait-states
  switch (SCR)
  {

  case 10:
    // ======== Get Raw Sensor Data ==============================
    h = dht.readHumidity();    //Get the Humidity Data
    tc = dht.readTemperature();  // Temperature in deg C (Float)

    // check if returned sensor data is invalid, put in dummy data so
    // that all temp and humidity values will read "-999.9"
    if (isnan(h) || isnan(tc))
    {
      h = -999.9;
      tc = -999.9;
    }

    // ======== Build the Display String ============================
    /* Containing the 7-segment patterns for the entire
     assembled message, pre-scroll:
     */

    pointerDL = 0;  // Initialize the Display Line Pointer
    // Start the Display Line with three null characters
    for (int n=0; n<4; n++)  {  // Add four blanks to the Display Line
      DisplayLine[pointerDL] = LUT_Chars[chr_nul];
      pointerDL +=1;  // Increment the Pointer
    }


    //Convert the Temperature Data into Fahrenheit, and convert it to a 7-segment pattern:
    valLength = float_to_SevenSegment((tc * 1.8)+ 32);
    // Copy the Temperature Substring to the Display Line
    for (int i=0; i < valLength; i++) {
      DisplayLine[pointerDL + i] = numFieldChars[i];
    }
    pointerDL += valLength;
    // Add the Degree Symbol character, the 'F' character, and three null (blank) characters
    DisplayLine[pointerDL] = LUT_Chars[chr_deg];
    pointerDL +=1;  // Increment the Pointer
    DisplayLine[pointerDL] = LUT_Chars[chr_F];
    pointerDL +=1;  // Increment the Pointer
    for (int n=0; n<4; n++)  {  // Add four blanks to the Display Line
      DisplayLine[pointerDL] = LUT_Chars[chr_nul];
      pointerDL +=1;  // Increment the Pointer
    }

    // Convert the Celsius Temperature to a 7-segment pattern:
    valLength = float_to_SevenSegment(tc);
    for (int i=0; i < valLength; i++) {
      DisplayLine[pointerDL + i] = numFieldChars[i];
    }
    pointerDL += valLength;
    // Add the Degree Symbol character, the 'C' character, and three null (blank) characters
    DisplayLine[pointerDL] = LUT_Chars[chr_deg];
    pointerDL +=1;  // Increment the Pointer
    DisplayLine[pointerDL] = LUT_Chars[chr_C];
    pointerDL +=1;  // Increment the Pointer
    for (int n=0; n<4; n++)  {  // Add four blanks to the Display Line
      DisplayLine[pointerDL] = LUT_Chars[chr_nul];
      pointerDL +=1;  // Increment the Pointer
    }

    //Get the Humidity Data, and convert it to a 7-segment pattern:
    valLength = float_to_SevenSegment(h);
    // Copy the Humidity Substring to the Display Line
    for (int i=0; i < valLength; i++) {
      DisplayLine[pointerDL + i] = numFieldChars[i];
    }
    pointerDL += valLength;
    // Add the 'H' character and three null (blank) characters
    DisplayLine[pointerDL] = LUT_Chars[chr_H];
    pointerDL +=1;  // Increment the Pointer
    for (int n=0; n<3; n++)  {  // Add three blanks to the Display Line
      DisplayLine[pointerDL] = LUT_Chars[chr_nul];
      pointerDL +=1;  // Increment the Pointer
    }

    /* Diagnostics:
     Serial.print("Humidity: ");
     Serial.print(h);
     Serial.print(" %   ");
     Serial.print("Temp: ");
     Serial.print(tc);
     Serial.print(" *C   ");
     Serial.print((tc * 1.8)+ 32);
     Serial.println(" *F");
    
     // Print out contents of Display "String"
     for (int j=0; j<pointerDL; j++)
     {
     Serial.print(DisplayLine[j],HEX);
     Serial.print(" ");
     }
     Serial.println();
     */
    pointerScroll = 0;  // Initialize the Marquee Pointer
    SCR = 20;
    break;

    // ======= Scroll the 3-character Display over the Display Line =========
  case 20:
    // Update the contents of the three LED Digits
    if (pointerScroll <= (pointerDL - 3))
    {
      CharBuffer[0] = DisplayLine[pointerScroll];
      CharBuffer[1] = DisplayLine[pointerScroll + 1];
      CharBuffer[2] = DisplayLine[pointerScroll + 2];
      pointerScroll +=1;
      // Set Timer to 125mS for scroll delay
      t1Preset = 125; 
         
      /* ---------------------------------------------------------------------
      To use an external analog signal controls scroll rate, 4 to 20 char/sec:
      t1Preset = map(analogRead(scrollSpeed),0,1023,250,50);
      (Comment out the line above, "t1Preset = 125;"
      ------------------------------------------------------------------------*/
    
      t1Bit = 1; 
      SCR = 30;
    }
    else
    {
      SCR = 10;  // Get another Reading
    }
    break;
  case 30:
    if (!t1Bit)  // When Timer expires, scroll the next character
      SCR = 20;
    break;
  }  // End of Switch/Case statement for SCR

  // ################################################################
  // ################################################################

  // ###################################################
  // ################## SERVICES #######################
  // ###################################################
  // Multiplex 3-digit 7-segment display
/*  Here is a line of code that allows you to control the
    display brightness with an external analog voltage: */
   
  Brightness = map(analogRead(brightLevel),0,1023,1,255);
 
//  If not using this feature, substitute a number between 1 and 255

  Brightness = constrain (Brightness,1,255);  // Traps bad Brightness Level Data
  T_On = Brightness * 20;
  T_Off = (256 - Brightness) * 20;
  // Scan the three digits once
  for (int i=0; i < 3; i++)
  {
    // Transmit Segment Data for current digit to the Shift Register:
    digitalWrite(latchPin, LOW);
    shiftOut (dataPin, clockPin, MSBFIRST, CharBuffer[i]);  // Shift out  Byte
    digitalWrite(latchPin, HIGH);
    // Energize Digit Driver for current Digit
    digitalWrite(digitEnable[i], HIGH);
    // Leave energized, according to Brighness Level
    delayMicroseconds(T_On);
    // Blank Digit Outputs according to Brighness Level
    digitalWrite(digitEnable[0], LOW);
    digitalWrite(digitEnable[1], LOW);
    digitalWrite(digitEnable[2], LOW);
    delayMicroseconds(T_Off); 
  }
  // ### END Display Multiplexing ###

  // ********** Pulse Timer Service **********
  /* This timer runs as a service, allowing delayed sequences to be built without hanging-up
   the rest of the project code
  
   When the sequence wants to use the timer, it must specify the Time Delay (t1Preset)in milliseconds,
   and then set the Timer Bit (t1Bit = HIGH).
   From that point forward, the Timer Service will keep track of the elapsed time (via the millis() function).
   When the time interval expires, the Timer Service will reset the Timer Bit (t1Bit) which can be checked by
   code running elsewhere.
  
   */
  // arm the timer
  if (t1Bit && !t1Edge)
  {
    t1Edge = HIGH;
    t1Temp = millis();
    t1Remain = t1Preset;
  }
  // decrement the Timer Word as long as the timer is active 
  if ( t1Edge ) 
  {
    t1Remain = t1Remain - abs(millis() - t1Temp);
    t1Temp = millis();
  }
  // check for timer expiration (Timer can also be cancelled by forcing t1Bit to LOW)
  if ( (t1Remain <= 0) || !t1Bit ) 
  {
    t1Bit = LOW;
    t1Edge = LOW;
    t1Remain = 0;
  }
  // *** END Pulse Timer 1 ***     
} // Loop Procedure End

// #######################################################################
// #######################################################################
/*
*****************
 *** FUNCTIONS ***
 *****************
 */
/* CONVERT FLOATING POINT VALUE TO 7-SEGMENT CHARACTER ARRAY
 Display Format: -999.9 TO 999.9, with one fixed decimal place
 Return Value = "String Length"
 (The "string" is a series of 1-byte segment patterns, not an actual ASCII string)

 Notes:
 a. The decimal point does not occupy a character position, but
 is incorporated into the units digit.

 b. Numbers between -0.9 and 0.9 are always displayed with a leading zero.
 c. Over/under range values are displayed as "999.9" or "-999.9" respectively

 */
int float_to_SevenSegment (float inputVal)  {
  // Declare local function variables
  int numFieldVal[5];
  // Constrain input value
  inputVal = constrain (inputVal, -999.9, 999.9);
  // Convert constrained Float to Word * 10, display as 1 Fixed decimal place
  int  inputInt = word(inputVal*10); 

  // Clear Array
  for (int i=0; i<5; i++) {
    numFieldVal[i] = chr_nul;
  }
  int charPos = 0; //Initialize Character Position Pointer
  // Is Input Value Negative?
  if (inputVal < 0)
  {
    inputInt = inputInt * -1;  // Negate Input Integer
    numFieldVal[charPos] = chr_min; // Insert a Minus Sign
    charPos +=1;  // Increment the Character Position Counter
  }
  // Round-off Integer value
  if ((inputVal*10) - inputInt >= 0.5)
    inputInt +=1;
  // if Input value is less than 100.0
  if (inputInt < 1000)
  {
    // if Input is less than 10.0
    if (inputInt < 100)
    {
      numFieldVal[charPos] = inputInt /10 ;
      numFieldVal[charPos+1] = inputInt % 10;
      charPos +=2;  // Increment the Character Poaition Output by 2
    }
    // if Input is greater than 10.0
    else
    {
      numFieldVal[charPos] = inputInt / 100;
      numFieldVal[charPos+1] = (inputInt - numFieldVal[charPos]*100)/10 ;
      numFieldVal[charPos+2] = (inputInt - numFieldVal[charPos]*100) % 10;
      charPos +=3;  // Increment the Character Poaition Output by 3
    }
  }
  // if Input is greater than or equal to 100.0
  else
  {
    numFieldVal[charPos] = (inputInt) / 1000;
    numFieldVal[charPos+1] = (inputInt - numFieldVal[charPos]*1000) /100 ;
    numFieldVal[charPos+2] = (inputInt - numFieldVal[charPos]*1000 - numFieldVal[charPos+1]*100)/10 ;
    numFieldVal[charPos+3] = (inputInt - numFieldVal[charPos]*1000 - numFieldVal[charPos+1]*100)%10 ;
    charPos +=4;  // Increment the Character Poaition Output by 4
  }
  // Fetch the Segment Patterns
  for (int i=0; i<=charPos; i++) {
    numFieldChars[i] = LUT_Chars[numFieldVal[i]];
    if (i == charPos-2)  //is the current digit the units digit?
      numFieldChars[i] += 0x80; //set bit 7 (dp)
  }
  return charPos;
}

1 comment: