Introduction: Thermo-hygrometer

There are several instructables out there on how to build an infrared thermometer. It occurred to me that it could be interesting to add more versatility by adding a hygrometer sensor. This way, through code it could be possible to determine the dew temperature.

On average use, determining dew temperature is not really necessary, however if for example some paint job needs to be done, determining the dew temperature is important. The surface temperature should be some 3ºC (5ºF) above dew temperature to avoid the formation of a moisture film between the coating and the base that could compromise the quality of the paint job. For this, in a single instrument it is posible to sense the surface temperature contactless, and at the same time determine if the site conditions are appropriate for the job.

Apart from determining dew point temperature, the device can also display other parameters that the usual infrared thermometer can't, such as relative humidity and apparent temperature (indoors).

Supplies

The supplies and tools needed are indicated in the images above.

Step 1: Circuit Design and Test

In order to fit the circuit in a not too large holder, it is best to use a small processor. For this case I used the Arduino Nano. I strongly recommend the Arduino Nano V3 based on the FT232 chip and not the one with the generic CH340 chip.

I initially bought the latter one and I had several issues communicating with the Arduino application. I searched for solutions but never really got to solve the problem. Some times closing and opening the app again solved the issue; other times I had to restart the computer. Since I was constantly modifying and enhancing the code I had to upload regularly the sketch to the Board. Eventually something happened to the Arduino board and ceased to synchronize completely. Probably the bootloader got corrupted, so I went for the Arduino with the FT232 chip. It's slightly more expensive but definitely saves a headache.

Another advise, if you can get a DHT22 instead of a DHT11 it would be even better. Both are very similar but the DHT22 is more accurate and has a greater sensing range.

Aside from that, the first step once all de supplies and tools are gathered is to assemble the circuit on a breadboard. This will guarantee that all the components are working well before the final assembly. I usually test continuity even for each wire independently ever since I got a dupont wire damaged. Once the circuit is assembled it will not allow us to fully test all the components until the program is loaded to the board.

The device was conceived in such a way that the user can switch between two modes: an object temperature mode, and a hygrometer mode. The device has two push buttons. One of them (the one connected to pin D7) will allow the user to switch between modes, while the other push button (the one connected to pin D5) enables the infrared sensor. Only while the button is pressed will de divise display the sensed temperature. If the button is released the display will freeze the last measurement taken. In addition to that, whenever the device is in the object temperature mode and if the corresponding push button is pressed, a LED lights up (in my case I chose a white LED connected to pin D12). Whenever the mode is switched, another LED lights up (in my case I chose a blue LED connected to pin D10). In the hygrometer mode however it is not necessary to keep any push button pressed. The device will update measurements every quarter of a second and the LED will remain on.

Another feature added to the apparatus is a battery level indicator. To achieve this, a voltage divider was added. From the above image, Z1 corresponds to a 2.7 kOhm resistor and Z2 corresponds to a 5 kOhm trimmer set at 2.98 kOhm (carefully calibrated with a multimeter). The voltage divider proportionally drops down the voltage of the 9V battery (with real charge up to 9.5V) to a scale of 0-5V readable by the analog pins of the board (Vout in the image is connected to pin A1 and Vin directly to the battery). Within the code a scale is set up which will in turn draw a battery icon on the display, showing the level of the battery.

Step 2: Psychrometric Theory for the Code

As stated before, one of the uses of the device is to calculate and display the dew temperature. This parameter can be expressed as a function of the dry bulb temperature and the relative humidity, which are determined by the MLX90614 and DHT11 sensor respectively. Although the DHT11 can sense dry bulb temperature, the MLX90614 offers a function for reading the ambient temperature in addition to a point source temperature, and is capable of achieving more accurate readings than the DHT11.

The image shown corresponds to the Psychrometric chart of air at 2680 meters above sea level. This chart correlates thermodynamic properties of "wet air" which we all experience regardless where we live. The chart shows how to determine the dew point temperature given the dry bulb temperature and relative humidity. For the case shown, a reading is taken indicating a dry bulb temperature of 25ºC and a relative humidity of 60%. Finding the intersection of this point with the 100% relative humidity curve along a constant humidity ratio and then projecting the intersection to dry bulb temperature scale indicates what the dew temperature is. For this case approx. 16ºC. This means that for the given condition, any surface bellow 16ºC will condensate the moisture of air.

The chart helps in understanding the relationship between the parameters, but creating an algorithm to code this procedure seems tough. Instead programming the equations that govern the thermodynamics of air seems simpler.

The equations required are:

Es = 6.108 * (e^((17.27 * T) / (237.3 + T)))

B = (ln(E / 6.108)) / 17.27

RH = 100 * (E / Es)

D = (237.3 * B) / (1 - B)

where T = Air Temperature (Dry Bulb) in Centigrade (C) degrees, Es = Saturation Vapor Pressure at Dry Bulb (mb), E = Actual Vapor Pressure (mb), B = intermediate value (no units), RH = Relative Humidity in percent (%), and D = Dewpoint in Centigrade (C) degrees. Rearranging terms, with T and RH we can solve for D.

For more on this visit Dewpoint Formulas.

In addition to calculating dew temperature, the code also includes the equations for calculating the apparent temperature; although this parameter is additionally a function of wind speed and solar radiation, since the apparatus can't sense these variables, only indoors apparent temperature is calculated as follows:

AT = Ta+ 0.348 * e

e = (rh)/100)*6.105*e^((17.27 *Ta) / (237.7+Ta))

where T = Apparent Temperature, Ta = Dry Bulb Temperature, e = Water Vapour Pressure, and RH = Relative Humidity in percent (%). Notice the similarity with the former equations for dew point. For more on this visit Calculate Apparent Temperature.

Step 3: Implementing Code

The code for the Arduino board that was implemented includes some interesting features, specially oriented to the display. It's necessary to load the Adafruit_SSD1306.h library to work with the display.

An animated mercury thermometer was included for the object temperature mode. The code for this was borrowed from an instructable created by AnnaMai. Also a display icon was added upon start up of the device. This icon consists of a bit map array, obtained from an uploaded image, available online at image2cpp. The array is inserted directly into the code and called through the function drawBitmap() to render the icon.

As indicated before, the code also draws a battery level indicator. The battery is drawn with the display.drawRect() and display.fillRect() functions (more on SSD1306 functions can be found here). Five steps where included in such way that the battery icon is completely full when the voltage is greater or equal to 9V, and empty when the voltage is less than 7.5V. Arduino requires at least a 5V supply to operate.

Another thing to take into account is that for both modes a loading screen was included. Additionally, the screen that displays hygrometer data holds a lot of information. The full 8 rows of the 128 x 64 screen were taken. It was necessary to add some scrolling text; but because of the nature of the scroll, the inbuilt functions in the SSD1306 library did not work. The code was custom configured in such way that two long lines scroll to the end, one after the other and then reset to their original position.

Update: in the images of the hygrometer mode, it was indicated a dependency of barometric pressure or altitude for the display of the dew temperature. This was corrected since the theory applied is approximated. As it can be seen from the equations, in this approximation, determining dew temperature from dry bulb temperature and humidity does not involve barometric pressure.

This is the complete code:

// ARDUINO PROGRAM FOR THERMO - HYGROMETER
//Using Arduino Nano
//by juanjogo

//Sensor library
#include <Adafruit_MLX90614.h> //GY-906 infrared temperature sensor
#include <DHT.h> //Thermo hygrometer sensor
#include <DHT_U.h> //Thermo hygrometer sensor
#include <Adafruit_SSD1306.h> //OLED 0.96" screen
#include <Adafruit_GFX.h>

//Additional libraries
#include <Wire.h> 
#include <AsyncDelay.h>
#include <SoftWire.h>
#include <splash.h>
#include <SPI.h>
#include <gfxfont.h>

struct Temps{
  float tr;
  float ta;
};
int pinLedAzul = 10; //Output for blue LED 
int pinLedBlanco = 12;//Output for white LED
int pinSDA = A4; //Input signal SDA
int pinSCL = A5; //Input signal SCL
int pinSwitch = 7; //Button for switching between infrared thermometer and y Thermo hygrometer
int pinTrigger = 5; //trigger button for infrared measurent
int pinDHT = 2; //Input signal KY-015
int pinNivBat = A1; //Input battery level voltage 
int medicion = 1; //Wildcard for switching between infrared thermometer and y Thermo hygrometer
float r1 = 2.7; //Value for R1 in voltage divider [kOhm]
float r2 = 3.0; //Value for R2 in voltage divider [kOhm]
int temp = 0; //Temperature for ruler display
bool load = true; //Load screens wildcard
char texto1[] = "Temperatura o punto de rocio:"; //Scrolling text 1
char texto2[] = "Temperatura aparente interior:"; //Scrolling text 2
int x1; //Scrolling coordinate text 1
int x2; //Scrolling coordinate text 2
int xLong1; //pixels over edge text 1
int xLong2; //pixels over edge text 2
bool hold;
bool scroll;
int j = 0;

//Further parameters for OLED screen
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET     -1 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// Custom logo generated from <a href="https://diyusthad.com/image2cpp" rel="nofollow"> <a href="https://diyusthad.com/image2cpp" rel="nofollow"> https://diyusthad.com/image2cpp
</a>
</a>
const unsigned char Logo [] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x1f, 0xc0, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x01, 0x80, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x0c, 0x7f, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x81, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xf0, 0x03, 0xf8, 0x00, 0x00, 0x02, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0x00, 0x3f, 0xff, 0x00, 0x00, 0x06, 0x67, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xfc, 0x03, 0xff, 0xff, 0xe0, 0x00, 0x0e, 0x77, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x0e, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x1e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x1e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x3e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x83, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9f, 0xff, 0x8f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f, 0xff, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0xcf, 0xff, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0xc3, 0xff, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xfc, 0x00, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xcf, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xcf, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xef, 0x81, 0xf3, 0xb2, 0x01, 0xf8, 0xf1, 0xe1, 0xde, 0xd0, 0x7b, 0xc7, 0xff, 0xff, 
  0xff, 0xff, 0xef, 0x9d, 0xc9, 0x9e, 0x4c, 0xde, 0x6f, 0xdd, 0x9e, 0xde, 0x7b, 0x7f, 0xff, 0xff, 
  0xff, 0xff, 0xef, 0xbd, 0x9d, 0xbe, 0xee, 0xbf, 0x4b, 0xfc, 0xbe, 0xde, 0x7b, 0x0f, 0xff, 0xff, 
  0xff, 0xff, 0xef, 0xbd, 0xbf, 0xbe, 0xee, 0xbf, 0x70, 0xfc, 0xbe, 0xde, 0x7b, 0x87, 0xff, 0xff, 
  0xff, 0xff, 0xef, 0xbd, 0xbf, 0xbe, 0xee, 0xbf, 0x7c, 0xbe, 0xbe, 0xde, 0x7b, 0xf7, 0xff, 0xff, 
  0xff, 0xff, 0xef, 0xbd, 0x9e, 0xbe, 0xec, 0x9e, 0xdd, 0xbc, 0x9c, 0xde, 0x72, 0xf7, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xbd, 0xe7, 0xbe, 0xfd, 0xc3, 0xe3, 0x8d, 0xc6, 0xff, 0x1f, 0x1f, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

//Further parameters for KY-015 (DHT11 sensor)
#define DHTTYPE DHT11
DHT dht(pinDHT,DHTTYPE);   

//Further parameters for thermometer GY-906
Adafruit_MLX90614 mlx = Adafruit_MLX90614();  

void setup() {
  pinMode(pinLedAzul,OUTPUT);
  pinMode(pinLedBlanco,OUTPUT);
  pinMode(pinSwitch,INPUT_PULLUP);
  pinMode(pinTrigger,INPUT_PULLUP);
  //Serial.begin(9600);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // Initialize display with the I2C address of 0x3C
  //if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
    //Serial.println(F("SSD1306 allocation failed"));
    //for(;;);
  //}
  dht.begin();
  mlx.begin();
  display.clearDisplay();
  display.drawBitmap(0,0,Logo,128,64,WHITE);
  display.display();
  delay(3000);
  //Serial.println("Inicio del programa");
  xLong1 = -6*strlen(texto1) + 128;
  xLong2 = -6*strlen(texto2) + 128;
}

void loop() {
  //Serial.print("Variable medicion = "); 
  //Serial.println(medicion);
  if (digitalRead(pinSwitch)==LOW){
    medicion=medicion * (-1);
    load=true;
  }
  
  if (medicion==1){
    //Read infrared thermometer signal GY-906
    digitalWrite(pinLedAzul,LOW);
    if (load==true){
      display.clearDisplay();
      diplayTempLoad();
      NivBat();
      delay(1000); 
    }
    //Read only while trigger pressed
    if (digitalRead(pinTrigger)==LOW){
      display.clearDisplay();
      digitalWrite(pinLedBlanco,HIGH);
      float t1 = mlx.readObjectTempC();
      displayTemp(t1);
      NivBat();
      delay(500);
      load=false;
    } else{
      //Freeze last reading when trigger released
      digitalWrite(pinLedBlanco,LOW);  
    }
  } else{
    //Read thermo hygrometer KY-015
    digitalWrite(pinLedAzul,HIGH);
    digitalWrite(pinLedBlanco,LOW);
    float h = dht.readHumidity(); 
    float t2 = mlx.readAmbientTempC(); 
    //float t2 = dht.readTemperature(); Alternative for measuring temp. with DHT11
    if (load==true){
      display.clearDisplay();
      NivBat();
      displayDHTLoad();
      delay(2000);
      x1 = 0;
      x2 = 0; 
      hold = true;
      scroll = true;
      load = false;
    }
    //float d = tr(t2, h);
    Temps d = tOut(t2, h);
    display.clearDisplay();
    displayDHT(h, t2, d.tr, d.ta, x1, x2);
    NivBat();
    delay(200);
    if (x1==0){
      delay(500);
    }
    if (scroll == true){
      if (x1 > xLong1){
        x1 = x1-4;
      }else{
        if (hold==true){
          delay(500);
        }
        x2 = x2-4;
        hold = false;  
      }
      if (x2 < xLong2){
        scroll = false; 
      } 
    }
    j+=1;
    if (j==40){
      x1 = 0;
      x2 = 0; 
      scroll = true;
      j=0;      
    }
  }
}

//Function for updating and displaying battery level
void NivBat(){
  int anaBat = analogRead(pinNivBat); 
  float volPinBat = float(anaBat) *(5.0/1023.0);
  float volBat = (volPinBat * (r1+r2))/r2;
  int x = 2;
  if (volBat>=9){
    x=10;  
  } else if (volBat<9 && volBat>=8.5){
    x=8;
  } else if (volBat<8.5 && volBat>=8){
    x=6;
  } else if (volBat<8 && volBat>=7.5){
    x=4;
  } else if (volBat<7.5){
    x=2;
  }
  display.drawRect(112,1,12,8,WHITE);
  display.drawRect(123,3,4,4,WHITE);
  display.fillRect(113,2,x,6,WHITE);
  display.display();
  //Serial.println("Analog A1");
  //Serial.println(anaBat);
  //Serial.println("Voltaje A1");
  //Serial.println(volPinBat);
  //Serial.println("Voltaje bateria");
  //Serial.println(volBat);
  //Serial.println("ancho rectangulo");
  //Serial.println(x);
}

//Function for displaying thermo hygrometer readings
void displayDHT(float H, float T, float D, float A, int X1, int X2){
  display.setTextColor(WHITE, BLACK);
  display.setTextSize(1);
  display.setTextWrap(false);
  display.setCursor(0,0);
  display.println("Temperatura:"); 
  display.print(T);
  display.println(" C"); 
  display.println("Humedad. Relativa:"); 
  display.print(H);
  display.println(" %");
  display.setCursor(X1,32);
  display.print(texto1);
  display.setCursor(0,40);
  display.print(D); 
  display.println(" C");
  display.setCursor(X2,48);
  display.print(texto2);
  display.setCursor(0,56);
  display.print(A);
  display.print(" C");
  display.display();
 
}

//Function for loading screen of thermo hygrometer readings
void displayDHTLoad(){
  display.setTextSize(1);
  display.setTextColor(BLACK, WHITE); // 'inverted' text
  display.setCursor(10,2);
  display.println("Modo");
  display.setCursor(10,14);
  display.println("Termo-Hygrometro");
  display.display();
  //Serial.println("Modo");
  //Serial.println("Termo-Hygro"); 
}

//Function for displaying infrared thermometer readings
void displayTemp(float T){
  String salida;
  float valor;
  int largo;
  display.setTextColor(WHITE, BLACK); // 'inverted' text
  display.setTextSize(1);
  display.setCursor(30,2);
  display.println("TEMPERATURA");
  valor = DecimalRound(T,1);
  salida = String(valor);
  largo = int(salida.length())-1;
  salida.remove(largo);
  if (salida.indexOf('.') <= 2){
    display.setTextSize(3);
    display.setCursor(18,16);
  } else{
    display.setTextSize(2);
    display.setCursor(18,20);
  }
  display.print(salida); 
  display.print(" C"); 
  display.fillCircle(8, 55, 7, WHITE);  // Draw filled circle (x,y,radius,color). X and Y are the coordinates for the center point
  display.drawRoundRect(6, 3, 5, 49, 2, WHITE);  // Draw rounded rectangle (x,y,width,height,radius,color)                                             
  for (int i = 6; i<=45; i=i+3){
    display.drawLine(11, i, 12, i, WHITE);  // Draw line (x0,y0,x1,y1,color)
  }
  temp = T*0.43; //ratio for show
  display.drawLine(8, 46, 8, 46-temp, WHITE);  // Draw temperature line (x0,y0,x1,y1,color)
  display.display();
  //Serial.println("TEMPERATURA");
  //Serial.print(T); 
  //Serial.print(" C"); 
}

//Function for loading screen of infrared thermometer readings
void diplayTempLoad(){
  display.setTextSize(1);
  display.setTextColor(BLACK, WHITE); // 'inverted' text
  display.setCursor(25,10);
  display.println("Modo");
  display.setCursor(25,22);
  display.println("Temp. objeto");
  display.drawRoundRect(6, 3, 5, 49, 2, WHITE);  // Draw rounded rectangle (x,y,width,height,radius,color)                                             
  display.drawCircle(8, 55, 7, WHITE);  // Draw circle (x,y,radius,color). X and Y are the coordinates for the center point
  display.fillCircle(8, 55, 6, BLACK);  // Draw black circle (x,y,radius,color). X and Y are the coordinates for the center point
  for (int i = 6; i<=45; i=i+3){
    display.drawLine(11, i, 12, i, WHITE);  // Draw line (x0,y0,x1,y1,color)
  }
  display.display();
  //Serial.println("Modo");
  //Serial.println("Temp. objeto");
}

//Function for calculating Dew Point at 2680 MASL and indoor precieved temperature
Temps tOut(float T, float RH){
  double h = 2680;
  double es = 6.108*exp((17.27*T)/(237.3+T));
  double e = (RH*es)/100.0;
  double b = (1/17.27)*(log(e/6.108));
  Temps t;
  t.tr = (273.0*b)/(1-b);
  t.ta = T+0.348*e-4.25;
  return t;
}

//Round function
float DecimalRound(float input, int decimals)
{
  float scale=pow(10,decimals);
  return round(input*scale)/scale;
}<br>

Step 4: Printing the Holder

Once the code is uploaded, the full components can be tested on the breadboard. If everything works fine it's time to build the holder that will contain all the parts.

The holder was designed in Autodesk Fusion 360. It consists of 6 parts as follows:

  • Handle and battery holder (PLA)
  • Battery lid with snapping joint with handle (PLA)
  • Mid section above handle. Holds the buttons and power switch (PLA)
  • Main section. Holds every other electronic component and sensors (PLA)
  • Top cover (PLA)
  • Outer cover for handle for better grip (TPU)

All parts were designed to fit with snapping joints, but in the end it was preferible to glue them together except for the battery lid and top cover. The snapping joints did not work well mainly because the bending components were printed in the Z direction (layer deposition direction). This should be avoided since the bending moment along the X or Y axis generates the stress on the direction of the layers, providing less resistance and more fragility. The Battery lid instead was printed with the snapping joints along the X direction. It's flexible and works perfectly.

Generally, the device is better suited for right handed people. For left handed users it would be preferable to print a mirror of the parts along the axis of the handle. The reason for this is the arrangement of the buttons. The button for taking object temperature readings is located at the front, in the position of the trigger. This is not an issue, but the button for switching modes is located at the left side of the handle so that it can be pressed with the thumb comfortably by right handed folks.

Another characteristic to notice is the container for the LEDs and sensors. The top cover has an opening for the blue LED which indicates the device is in hygrometer mode. There's also an opening for the hygrometer sensor of the DHT11. The main section holds the container for the white LED. The container was meant to have an elongated tubular shape so that when the LED was powered the light would encompass an angle similar to the reading angle of the infrared sensor (90º). In theory it should light up a 2cm diameter when being held 1cm away from the surface. In practice it fell short by half. The idea was that the user could get an estimate of the area sensed. Some infrared thermometers are equipped with a laser pointer but I find this unnecessary unless the version of infrared sensor includes a lens for reducing the sensing angle to a couple of degrees.

Attached you can find the .stl files for the 6 parts.

Step 5: Migrate the Components to the Holder

The length of the wires was almost an issue. I recommend short dupont wires. Some of the ones I used were unnecessarily long and ended up taking most bulk space in the main section. In the end they had to be stuffed, maybe risking contact among unsoldered connections, so that the top cover could close.

The migration of components should start from bottom to top, starting by the battery conector, then the buttons and switch in the mid section. At this point some soldering has to be done on the switch. The buttons instead were connected with dupont wires. Afterwards comes the main section. For this part some additional soldering has to be made: For the common ground I used a 3 cm naked wire segment, wrapped all the grounds around and soldered the piece. Some other soldering is required for the LED resistors. The rest of the connections were made with dupont terminals.

Originally the main section included a support for the Arduino board and a support for the DHT11 sensor, but because of all the wiring, the support of the board ended up being useless and instead taking up space. The support for the DHT11 instead did help to keep the sensor in place.

Step 6: Test and Measure the Temperature of Everything!

Once all the wires and components are stuffed into the holder is time for a final test to make sure that contact on all connections remains.