Project tutorial
Arduino Chilled Mirror Hygrometer

Arduino Chilled Mirror Hygrometer © MIT

Measure the dew point and humidity with an Arduino-based Chilled Mirror Hygrometer.

  • 31 respects

Components and supplies

Necessary tools and machines

Hy gluegun
Hot glue gun (generic)

Apps and online services

About this project


Measuring humidity can be done many different ways. The most popular method is to use a capacitive humidity sensor. Unfortunately these sensors quickly lose accuracy when operated continuously in a very humid environment. A Chilled Mirror Hygrometer doesn't suffer from this problem and is also much more accurate, especially in the high humidity range. Unfortunately a commercial Chilled Mirror Hygrometer cost literally thousands of dollars. The basic operation is quite simple though, so it is possible to make one yourself. It will not have the same 0.1 degree accuracy as a commercial device, but for most purposes it will be good enough, and certainly more accurate than a capacitive sensor.

This project is a proof of concept and by no means production ready. Many improvements can be made but it proves the fact that it works.

How it works

A Chilled Mirror Hygrometer uses a Peltier (TEC) to cool a reflective surface until condensation appears. The condensation is detected with a light source and an optical sensor. The mirror surface temperature is logged at the moment condensation appears. This temperature is equivalent to the dew point. The Relative Humidity can then be calculated using the dew point and the ambient temperature.


There are a few differences with this DIY Chilled Mirror Hygrometer compared to a commercial unit.

A commercial Chilled Mirror Hygrometer does not use a conventional mirror because that doesn't conduct and distribute heat very well, causing a reduction in accuracy and efficiency. These devices mostly use platinum or rhodium plated copper as a mirror. As that is neither easily obtainable nor cost effective, a regular mirror is used in this DIY Chilled Mirror Hygrometer. A small square mirror would be better than a round one but I could not find one. As an alternative, a polished slab of stainless steel would be even better.

A commercial Chilled Mirror Hygrometer uses a Platinum Resistance Thermometer (PRT) embedded underneath the mirror instead of an SMD thermometer mounted to the mirror surface. A PRT requires additional electronics and mounting it between the Peltier and the mirror while at the same time providing good thermal conductivity is problematic. The disadvantage of mounting a thermometer to the mirror surface is that is modifies the heat distribution properties, reducing the accuracy. But a surface mounted thermometer is much easier to build and is accurate enough.

A commercial Chilled Mirror Hygrometer uses a much smaller mirror of about 5 mm instead of 4 cm. as that requires less power and has a smaller form factor. However, a small mirror and matching Peltier is not as readily available as the larger variant. Additionally, a small Peltier requires a very low voltage, requiring a custom driver. It is also harder to mount an SMD thermometer to a small mirror surface while leaving enough space to reflect some light off.

The current version does not have a light blocking enclosure. This can be easily 3d printed though and is highly recommended in order to block out interference from outside light sources. It can also be used to hold the light source and light sensor in place. If you do make a 3d printed enclosure, make sure it is well ventilated. You can use the Peltier fan for ventilation, but make sure to draw air into the enclosure, not blow into it. The inside must also be easily accessible for mirror cleaning.

The current implementation cannot measure the frost point as that requires differentiating between ice particles and water droplets, which requires a second optical sensor to measure scattered light.

How to build

Take the Peltier, clean both surfaces, and place thermally conductive adhesive on the hot side. Make sure it is evenly distributed. If you don't have thermally conductive adhesive, you can use thermal paste with a few drops of super glue near the edges. This works equally well. Clean the heat sink surface and press the heat sink and Peltier together and let the glue dry.

After the heat sink is attached to the Peltier and the glue has dried, attach the mirror the same way, making sure to clean the bottom surface first. It is important that the thermal paste is evenly distributed and no air gaps are present, otherwise the heat distribution on the mirror will be non-uniform.

When all glue has dried, attach the fan. Make sure to use a fan powerful enough to cool the heat sink as that gets quite hot. How to attach the fan to the heat sink depends on the type of heat sink you use. I just glued the two together using a hot glue gun.

Power up the fan, then the Peltier to check if there are at least two areas on the mirror where the condensation appears at the same time. Do not power the Peltier too long because that will certainly create a uniform condensation profile. You want to see what it looks like when the condensation just becomes visible. If you are not happy with the result, try again with new Peltier, mirror, and heat sink as it is unlikely you can remove the parts and restore the smooth surface.

I did not distribute the thermal paste uniformly resulting in an irregular condensation profile as you can see in the photo above.

It is important to make a photo or mark the area where the condensation shows up at the same time because this is the area where you have to mount the mirror surface temperature sensor and measure the amount of light reflected off the mirror.

In this project I use a Si7021 to measure the mirror temperature because it gives a low noise output. Unfortunately the I2C address is hard coded so you can only use one of those sensors. For the ambient temperature I use a DS18B20 temperature sensor but it does not have very high accuracy. The only reason I use those sensors is because I happened to have them available.

To mount the Si7021 (or any PCB based temperature sensor) to the mirror, first place a large drop of non-electrically conductive thermal paste on the temperature sensor. The PCB should be covered with thermal paste too, in order to prevent condensation on the electronics. Then place 4 large blobs of hot glue on each corner of the PCB. You have to move fast. Press the sensor against the mirror at the location where condensation showed up uniformly. Make sure to place the sensor horizontally and press the sensor firmly against the mirror. The hot glue will solidify fast once it touches the mirror. If you need to re-position the sensor, you can easily scrape it off with a box cutter knife bit, peel off the glue, and try again. If you have condensation on the PCB, insulated it with hot glue or spray-on plastic.

The fan is controlled by an IRF520 MOSFET module.

The light sensor used is an OPT101, which is sensitive, has low output noise, and is easy to use.

For the lights source you can either use a low power laser diode or a regular LED. I tried both and they both work fine. The laser has the advantage of a better response but is harder to line up. The LED has a flatter response because more light is scattered around, but it is easier to align. If you use an LED, make sure it outputs a focused beam.

The LED and laser light output is likely too high, and using PWM to reduce the output is not an option because that will cause interference in the light sensor. Instead, just place a resistor in series with the LED or laser to reduce the output.

To mount the LED or laser and the OPT101 light sensor, I used some copper wire twisted into locking wire to give it more rigidity. Both ends were secured with hot glue. This is good for a proof of concept but it is also way too fragile to be used out of a lab (or basement) setting. It is easy to line up the sensor and the light source though. A better way would be to 3d print a holder for these devices, also because a 3d printed enclosure is a must to prevent any interference from outside light.

To control the Peltier, a BTS7960 motor driver is used. The most efficient way to drive a Peltier is to change the current, not to use PWM. However, Peltier controllers are not as readily available as the BTS7960 motor driver and for this proof of concept, power consumption is not a factor.

Once assembled, upload the code to the Arduino and adjust the OPT101 sensitivity range with a potentiometer. A higher resistance means a higher voltage output for the same amount of light. Refer to the OPT101 datasheet, Figure 3 - Voltage Responsivity vs Irradiance. For debugging, you can blow on the mirror to create condensation, or place an object in front of the sensor. If you have an air conditioner, try to switch it on (or off) and wait. You will be able to see the humidity change.

In the graph below you can see the temperature (blue), optical reading (red), and measured dew point (green). You can see the dew point rising as I just turned off the air conditioner.


Although the supplied code doesn't support this, the hardware can heat the mirror in addition to cool it. This is done by simply reverting the polarity on the Peltier. Mirror heating can be used to quickly get rid of condensation and improve the response time. In addition, flash evaporating condensation gets rid of small contaminants. However, this also poses a potential safety risk because the mirror surface does not have a heat sink. If the code is stuck heating the Peltier, at best it will melt the hot glue holding the thermometer in place and at worst it will start a fire due to melting wires causing a short circuit.


Because the measured dew point temperature is an absolute value, calibration is not as important as with a capacitive or resistive hygrometer. However, there will be at least some difference in temperature between the mirror surface temperature sensor area and the light sensing area. If you need to verify the accuracy of the readings, you can calibrate the device with a calibrated commercial Chilled Mirror Hygrometer.

As for mirror contamination, this is only partially an issue. The reflected light reading is not absolute but relative to the start of a cooling cycle. When a cooling cycle starts, the mirror is free of condensation. The reflected light amount is measured and used as a reference to detect condensation. If the mirror is contaminated and less light is reflected, it should not affect the condensation detection. However, some contaminants either lower or increase the temperature at which condensation occurs, so for best accuracy, clean the mirror surface from time to time.

The mirror and ambient temperature sensors don't need to have high calibrated accuracy, but the resolution must be high. For example, if the actual temperature is 24.0 degrees, but it measures, 24.5 degrees, that is fine as long as both the mirror and ambient thermometer also measures 24.5 (can be normalized) and the number only jitters with one decimal. Many thermometers have a jitter of 0.2 or 0.3 degrees. It would be better to use a TSYS01 temperature sensor for both mirror surface temperature and ambient temperature measurement as these sensors provide the same accuracy as a Platinum Resistance Thermometer of 0.1 degrees.

It is important that the temperature sensor makes good contact with the mirror surface. The usage of non-electrically conductive thermal paste is imperative.

Do not to cool the mirror faster than the response time of the thermometer, otherwise the sensed dew point will be inaccurate.

The temperature sensor must be placed on a location of the mirror where the condensation of the light sensing area shows up at the same time.

Mounting a temperature sensor on the mirror surface modifies the heat distribution, reducing the accuracy. It might be tempting to use an infrared thermometer unit as an alternative but unfortunately the mirror reflects some amount of thermal radiation so the measurement will be influenced by the surroundings.

Technically, a humidity reading also depends on the barometric pressure but the effect is very small in ambient environments. Any pressure change caused by slamming doors, and outside wind blowing causing differential pressure in a building is likely to cause more problems than it's worth.

The hot air from the Peltier heat sink should not be drawn over the mirror.

A slow temperature drop will give more accurate readings, but also lowers the response time. The response time can be improved by oscillating the temperature close to the dew point though.


Chilled Mirror HygrometerC/C++
Arduino code
#include <math.h>
#include <avr/wdt.h>  //Watchdog crash detection

//These are custom libraries.
#include "Si7021.h" //humidity sensor with heater
#include <OneWire.h> //DS18B20 temp sensor
#include <DallasTemperature.h> //DS18B20 temp sensor

//Timer library:
#include "timer.h"
#include "timerManager.h" 

//Define the hardware pins on the Arduino board.
#define coolingPWM 6
#define heatingPWM 5
#define coolingEnable 13
#define heatingEnable 12
#define tecFan 7
#define opticalSensor 0 //Analog in
#define oneWireBus A3 //DS18B20 temp sensor

//The state of the TEC.
#define COOLING 0
#define HEATING 1
#define OFF 2

Timer timerMainLoop;
Timer timerTecCooling;
Timer timerSampleNoise;

//Temperature sensor (humidity not used). 
Si7021 si7021;

//DS18B20 temp sensor
OneWire oneWire(oneWireBus); 
DallasTemperature sensors(&oneWire);

float humidity = 0;
float ambientTemp = 0;
float opticalDewpoint = 0;

//Set these to an initial higher value to get the Serial Plotter range correct.
float mirrorTemp = 30; 
float optical = 30; 
float dewPoint = 15; //initial value must be lower than the mirror temp.
float relativeHumidity = 30;

int tecState = OFF;
bool cooling = false;

int intervalTecCooling = 200; //How often the TEC timer is updated in ms.
float opticalThreshold = 0.5f; //0.5 //The amount of degrees C the optical reading has to drop below the reference in order to flag condensation detection. This must be a bigger number than the signal noise.
int pwmIncrement = 1; 
int startPwm = 100;
int maxPwm = 255;
int intervalMainLoop = 200;  
int tecPwm = 0;
int noiseSampleIndex = 0;
int noiseSampleAmount = 10;
float noiseSampleHighest = 0;
float noiseSampleLowest = 10000;
bool noiseSampling = false;

float calculateHumidity(float TD, float T){

  //The dew point cannot be higher than the temperature.
  if(TD > T){

    TD = T;

  //August-Roche-Magnus approximation. 
  float rh = 100*(exp((17.625*TD)/(243.04+TD))/exp((17.625*T)/(243.04+T)));

  return rh;

//Set the TEC to heating, cooling, or off.
void SetTEC(int state, int amount){

  tecState = state;

  //Note that for both heating and cooling, the heating AND cooling pin need to be set to high. Ask the PCB designer why.
  //Driver used to control the TEC: BTS7960 motor driver board. Note that PWM to drive a TEC is not efficient and it is better to use a variable current source. 
    case COOLING: 
      digitalWrite(heatingEnable, HIGH); 
      analogWrite(heatingPWM, 0);   
      digitalWrite(coolingEnable, HIGH);
      analogWrite(coolingPWM, amount); 
    case HEATING: 
      digitalWrite(coolingEnable, HIGH);
      analogWrite(coolingPWM, 0); 
      digitalWrite(heatingEnable, HIGH); 
      analogWrite(heatingPWM, amount);   

    case OFF: 
      digitalWrite(coolingEnable, LOW);
      analogWrite(coolingPWM, 0); 
      digitalWrite(heatingEnable, LOW); 
      analogWrite(heatingPWM, 0); 
      digitalWrite(coolingEnable, LOW);
      analogWrite(coolingPWM, 0); 
      digitalWrite(heatingEnable, LOW); 
      analogWrite(heatingPWM, 0);

void setup() {

  //Watchdog crash detection. This is for safety because you don't want the TEC to be stuck in heating mode.
  wdt_enable(WDTO_2S); //WDTO_500MS //WDTO_1S
  Serial.begin(9600); //9600 //57600

  pinMode(coolingPWM, OUTPUT);
  pinMode(heatingPWM, OUTPUT);
  pinMode(coolingEnable, OUTPUT);
  pinMode(heatingEnable, OUTPUT);
  pinMode(tecFan, OUTPUT);
  pinMode(opticalSensor, INPUT);

  //Setup the timers



  //si7021 temp sensor setup.
  uint64_t serialNumber = 0ULL;
  serialNumber = si7021.getSerialNumber();

  //DS18B20 onewire temperature sensor

  //Disable the temp sensor debug logging in order to get the graph to work correctly.
  Serial.print("Si7021 serial number: ");
  Serial.print((uint32_t)(serialNumber >> 32), HEX);
  Serial.println((uint32_t)(serialNumber), HEX);
  //Firware version
  Serial.print("Si7021 firmware version: ");
  Serial.println(si7021.getFirmwareVersion(), HEX);


//Get the optical sensor reading.
float getOptical(){

  int opt = analogRead(opticalSensor);
  float optFactored = (float)opt / 30.0f;

  return optFactored;

//Timer callback.
void tecCoolingCallback(){
  digitalWrite(tecFan, HIGH);

  //Slowly increase the power of the TEC.
  tecPwm += pwmIncrement;

  if(tecPwm > maxPwm){

    tecPwm = maxPwm;

  //Set the TEC cooling amount
  SetTEC(COOLING, tecPwm); 

  //Is condensation detected?
  if(optical <= (noiseSampleLowest - opticalThreshold)){

    //Log the dew point;
    dewPoint = mirrorTemp;
    opticalDewpoint = optical;


void startNoiseSampling(){

  noiseSampling = true;
  noiseSampleHighest = 0;
  noiseSampleLowest = 10000;

void sampleNoiseReset(){

  noiseSampleIndex = 0;
  noiseSampling = false;

void sampleNoiseCallback(){

  if(noiseSampleIndex > noiseSampleAmount){



    if(optical > noiseSampleHighest){

      noiseSampleHighest = optical;

    if(optical < noiseSampleLowest){

      noiseSampleLowest = optical;


void startTecCooling(){

  cooling = true;

  digitalWrite(tecFan, HIGH); 

  tecPwm = startPwm;

  //Start the TEC counter callback.

void stopTec(){

  cooling = false;

  //Turn the TEC fan off.
  digitalWrite(tecFan, LOW); 

  //No cooling, no heating
  SetTEC(OFF, 0);  

//Non blocking timer.
void mainLoop(){

  //DS18B20 temp sensor for ambient temperature.
  sensors.setResolution(10); //has to be done before each temperature measurement. Note that a higher resolution is slower.
  ambientTemp = sensors.getTempCByIndex(0);  

  //si7021 temps sensor for mirror.
  mirrorTemp = si7021.measureTemperature();

  //Get the optical sensor reading.
  optical = getOptical();

  relativeHumidity = calculateHumidity(dewPoint, ambientTemp);

 //Readable format
  Serial.print("dewPoint: ");
  Serial.println(dewPoint, 2);
  Serial.print("mirrorTemp: ");
  Serial.println(mirrorTemp, 2);
  Serial.print("ambientTemp: ");
  Serial.println(ambientTemp, 2);
  Serial.print("relativeHumidity: ");
  Serial.println(relativeHumidity, 2);


  //For the Serial Plotter
  Serial.print(mirrorTemp, 2);
  Serial.print(" ");
  Serial.print(optical, 2);
  Serial.print(" ");
  Serial.println(dewPoint, 2);
 // Serial.print(" ");
 // Serial.println(relativeHumidity, 2);

  //Wait for the condensation to disappear
  if(!cooling && !noiseSampling && (optical >= noiseSampleLowest)){

void loop() {

  //Watchdog crash detection

  //Update all timers.


Chilled Mirror Hygrometer schematic
Chilled mirror hygrometer schematic 4bbrvtwf5n


Similar projects you might like

Personal Weather Station (Arduino+ ESP8266 + Thingspeak)

Project tutorial by Jayraj Desai

  • 180 respects

Precision Agriculture – A Prototype Solution

Project in progress by Eric Hall

  • 74 respects

Arduino / ESP8266 RS485 MODBUS Anemometer

Project in progress by philippedc

  • 24 respects

ThingSpeak Arduino Weather Station

Project tutorial by neverofftheinternet

  • 17 respects

The Zeus Electronic Weather Station (ZeWS)

Project in progress by moisi

  • 1 comment
  • 23 respects

Periodic Temperature Reporter

Project tutorial by Kian Calnan

  • 9 respects
Add projectSign up / Login