
Beautifully Finished Humidity and Temperature Sensor © GPL3+
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.
- 23,408 views
- 23 comments
- 156 respects
Components and supplies
Necessary tools and machines
![]() |
| |||
![]() |
|
About this project
Learn how we built a walnut dovetailed case and used easy-to-find Arduino components to build a stylish and useful humidity and temperature monitor for the wood shop. This DIY project showcases a bit of woodworking with walnut dovetails and beginner electronics using an Arduino Uno and some excellent parts from Adafruit.
We live in Central Texas where we get massive swings in Humidity and Temperature in the Spring, which can be disastrous for certain woodworking projects. This cool project helps alert us when the humidity is changing or starts to get high so we can take precautions like moving our wood projects into the house, or not doing any milling during the high humidity weather. It also looks awesome and has our sweet logo!
We are new to Arduino and Electronics so we learned as we went when making this. We're very excited to make more projects now that we know how easy it is to make great things.
Watch the Detailed Build Video!Code
#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;
}
}
Custom parts and enclosures
Arduino Work Shop Sensor Plans

Schematics
Comments
Author
Published on
March 26, 2018Members who respect this project
you might like