Wicked Makers
Published © GPL3+

Beautifully Finished Humidity and Temperature Sensor

We built a stylish and useful humidity and temperature sensor for our home workshop that uses color to alert us to big swings in humidity.

IntermediateFull instructions provided30,321
Beautifully Finished Humidity and Temperature Sensor

Things used in this project

Hardware components

Arduino UNO
Arduino UNO
×1
NeoPixel Ring: WS2812 5050 RGB LED
Adafruit NeoPixel Ring: WS2812 5050 RGB LED
×1
DHT22 Temperature Sensor
DHT22 Temperature Sensor
×1
Adafruit 1.44" Color TFT LCD Display with MicroSD Card breakout - ST7735R
×1
Breadboard (generic)
Breadboard (generic)
×1
4xAA battery holder
4xAA battery holder
×1
Jumper wires (generic)
Jumper wires (generic)
×1

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Hot glue gun (generic)
Hot glue gun (generic)

Story

Read more

Custom parts and enclosures

Arduino Work Shop Sensor Plans

Schematics

Schematic

Fritzing didn't have most of the actual components so I did my best to represent the flow of things.

Picture of Actual Setup

Code

Arduino Work Shop Sensor Code

Arduino
#include <Adafruit_NeoPixel.h>
#include <Adafruit_ST7735.h>
#include <Adafruit_GFX.h>
#include <FastLED.h>
#include <SPI.h>
#include <SD.h>
#include <cactus_io_DHT22.h> // There's a bunch of DHTXX libraries you can find online. They all have varying levels of features.

// Define Pins
#define TFT_CS    10
#define TFT_RST   9
#define TFT_DC    8
#define DHT_PIN   2
#define PIXEL_PIN 6
#define PIXEL_NUM 24
#define SD_PIN    4
#define BMP_BUF   20  

// Global Variables
int currentColor = 9999; //(Numbers correspond to chart. See below.)
CRGB leds[PIXEL_NUM];
Adafruit_ST7735   tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);
DHT22 dht(DHT_PIN);
int loopDelay = 2000; //ms

// SETUP
void setup() {
 
  Serial.begin(9600);
  Serial.println("Starting up.");

  // Begin DHT
  dht.begin();

  // Initialize LCD Screen
  tft.initR(INITR_144GREENTAB);

  // Initialize SD Card
  Serial.print("Initializing SD card.");
  if (!SD.begin(SD_PIN)) {
    Serial.println("Failed!");
    return;
  }

  // Set Screen Background to Black
  tft.fillScreen(ST7735_BLACK);

  // Initialize LEDs
  FastLED.addLeds<NEOPIXEL, PIXEL_PIN>(leds, PIXEL_NUM);

  // Let everything catch up before starting loop
  delay(500);

}


// LOOP
void loop() {

  // Get Sensor Data
  dht.readTemperature();
  dht.readHumidity();
  int f = dht.temperature_F; // Degrees in Fahrenheit
  int h = dht.humidity; // Percentage

  // Draw Degrees Icon
  tft.fillCircle(102, 42, 6, ST7735_WHITE);
  tft.fillCircle(102, 42, 4, ST7735_BLACK);

  // Draw Temperature on LCD Screen
  drawTemperature(f);

  // Humidity
  setHumidityColors(h);
  
  //Print Data to Serial Monitor
  Serial.print("Temperature: ");
  Serial.println(f);
  Serial.print("Humidity: ");
  Serial.print(h);
  Serial.println("%");

  // Delay
  delay(1000);
  
}


// Draw Bitmap - This is for the LCD Screen
void bmpDraw(char *filename, uint8_t x, uint8_t y) {

  File     bmpFile;
  int      bmpWidth, bmpHeight;   // W+H in pixels
  uint8_t  bmpDepth;              // Bit depth (currently must be 24)
  uint32_t bmpImageoffset;        // Start of image data in file
  uint32_t rowSize;               // Not always = bmpWidth; may have padding
  uint8_t  sdbuffer[3*BMP_BUF];   // pixel buffer (R+G+B per pixel)
  uint8_t  buffidx = sizeof(sdbuffer); // Current position in sdbuffer
  boolean  goodBmp = false;       // Set to true on valid header parse
  boolean  flip    = true;        // BMP is stored bottom-to-top
  int      w, h, row, col;
  uint8_t  r, g, b;
  uint32_t pos = 0, startTime = millis();

  if((x >= tft.width()) || (y >= tft.height())) return;

//  Serial.println();
//  Serial.print("Loading Bitmap Image: '");
//  Serial.print(filename);
//  Serial.println('\'');

  // Open requested file on SD card
  if ((bmpFile = SD.open(filename)) == NULL) {
    Serial.print("Bitmap file not found!");
    return;
  }

  // Parse BMP header
  if(read16(bmpFile) == 0x4D42) {
    read32(bmpFile);
    (void)read32(bmpFile);
    bmpImageoffset = read32(bmpFile);
    //Serial.println());
    read32(bmpFile);
    bmpWidth  = read32(bmpFile);
    bmpHeight = read32(bmpFile);
    if(read16(bmpFile) == 1) {
      bmpDepth = read16(bmpFile);
      //Serial.print("Bit Depth: ");
      if((bmpDepth == 24) && (read32(bmpFile) == 0)) {

        goodBmp = true;

        // BMP rows are padded (if needed) to 4-byte boundary
        rowSize = (bmpWidth * 3 + 3) & ~3;

        // If bmpHeight is negative, image is in top-down order.
        // This is not canon but has been observed in the wild.
        if(bmpHeight < 0) {
          bmpHeight = -bmpHeight;
          flip      = false;
        }

        // Crop area to be loaded
        w = bmpWidth;
        h = bmpHeight;
        if((x+w-1) >= tft.width())  w = tft.width()  - x;
        if((y+h-1) >= tft.height()) h = tft.height() - y;

        // Set TFT address window to clipped image bounds
        tft.setAddrWindow(x, y, x+w-1, y+h-1);

        for (row=0; row<h; row++) { // For each scanline...
          if(flip) {
            pos = bmpImageoffset + (bmpHeight - 1 - row) * rowSize;
          } else {
            pos = bmpImageoffset + row * rowSize;
          }
          if(bmpFile.position() != pos) {
            bmpFile.seek(pos);
            buffidx = sizeof(sdbuffer);
          }

          for (col=0; col<w; col++) {
            if (buffidx >= sizeof(sdbuffer)) {
              bmpFile.read(sdbuffer, sizeof(sdbuffer));
              buffidx = 0;
            }

            // Convert pixel from BMP to TFT format, push to display
            b = sdbuffer[buffidx++];
            g = sdbuffer[buffidx++];
            r = sdbuffer[buffidx++];
            tft.pushColor(tft.Color565(r,g,b));
          }
        }
      }
    }
  }

  bmpFile.close();
  if(!goodBmp) Serial.println("BMP format not recognized.");

}

// This is for the Bitmap/LCD Screen
uint16_t read16(File f) {
  uint16_t result;
  ((uint8_t *)&result)[0] = f.read(); // LSB
  ((uint8_t *)&result)[1] = f.read(); // MSB
  return result;
}

// This is for the Bitmap/LCD Screen
uint32_t read32(File f) {
  uint32_t result;
  ((uint8_t *)&result)[0] = f.read(); // LSB
  ((uint8_t *)&result)[1] = f.read();
  ((uint8_t *)&result)[2] = f.read();
  ((uint8_t *)&result)[3] = f.read(); // MSB
  return result;
}


// Draw Temperature on LCD Screen
void drawTemperature(int temp) {

  // Get Number of Digits in the Temperature
  int digits = numDigits(temp);
 
  // Define Cursor Positions to Draw Bitmaps
  int x1_2 = 62;
  int x2_2 = 32;
  int x1_3 = 1;
  int x2_3 = 1;
  int x3_3 = 1;
  int y = 38;

  char digit1[12];
  char digit2[12];
  char digit3[12];
  char digitStr1[24];
  char digitStr2[24];
  char digitStr3[24];
  
  // Get First Digit
  itoa(temp % 10, digit1,10); 
  //Serial.println(temp % 10);
  strcpy(digitStr1, "");
  strcat(digitStr1, digit1);
  strcat(digitStr1, ".bmp");

  // Get Second Digit
  if(digits == 2){
    itoa((temp / 10) % 10, digit2,10);
    strcpy(digitStr2, "");
    strcat(digitStr2, digit2);
    strcat(digitStr2, ".bmp");
  }

  // Get Third Digit
  if(digits == 3){
    itoa((temp / 100) % 10, digit3,10);
    strcpy(digitStr3, "");
    strcat(digitStr3, digit3);
    strcat(digitStr3, ".bmp");
  }  
  
  if(digits > 2){
    bmpDraw(digitStr1,x1_3,y);
    bmpDraw(digitStr2,x2_3,y);
    bmpDraw(digitStr3,x3_3,y);
  } else {
    bmpDraw(digitStr1,x1_2,y);
    bmpDraw(digitStr2,x2_2,y);
  }

}


// Get number of digits in temperature to determine placement on screen
int numDigits(int number){

  int valLen = 0;  
  if(number > 99)
    valLen = 3;
  else
    valLen = 2;
  return valLen;

}


// Set LED Colors
void setColors(int r, int g, int b){

  for(int i = 0; i < PIXEL_NUM; i++){
    
    leds[i].setRGB( r, g, b);
    leds[i].fadeLightBy(125);
    FastLED.show();    
    delay(100);
  
  }
  
}


// Determine which colors to use based on Humidity level
void setHumidityColors(int humidity){

  // Humidity Color Chart - This is just something we came up with.
  // Red - 0-17%
  // Orange - 18-34%
  // Yellow - 35-51%
  // Green - 52-68%
  // Blue - 69-85%
  // Purple - 86-100%

  // Define Colors
  int cRed[3] = {255,0,0};
  int cOrange[3] = {255,90,0};
  int cYellow[3] = {255,255,0};
  int cGreen[3] = {0,255,0};
  int cBlue[3] = {0,175,255};
  int cPurple[3] = {255,0,255};

  int range = map(humidity, 0, 100, 0, 6);
  switch (range) {
    case 0:    // Red
      if(currentColor == 0){
        break;
      }
      else {
        setColors(cRed[0], cRed[1], cRed[2]);
        currentColor = 0;
      }
      break;
    case 1:    // Orange
      if(currentColor == 1){
        break;
      }
      else {
        setColors(cOrange[0], cOrange[1], cOrange[2]);
        currentColor = 1;
      }
      break;
    case 2:    // Yellow
      if(currentColor == 2){
        break;
      }
      else {
        setColors(cYellow[0], cYellow[1], cYellow[2]);
        currentColor = 2;
      }
      break;
    case 3:    // Green
      if(currentColor == 3){
        break;
      }
      else {
        setColors(cGreen[0], cGreen[1], cGreen[2]);
        currentColor = 3;
      }
      break;
    case 4:    // Blue
      if(currentColor == 4){
        break;
      }
      else {
        setColors(cBlue[0], cBlue[1], cBlue[2]);
        currentColor = 4;
      }
      break;
    case 5:    // Purple
      if(currentColor == 5){
        break;
      }
      else {
        setColors(cPurple[0], cPurple[1], cPurple[2]);
        currentColor = 5;
      }      
      break;
  }
}

Credits

Wicked Makers

Wicked Makers

0 projects • 55 followers

Comments