Published © MIT

Arduino Chilled Mirror Hygrometer

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

IntermediateFull instructions provided7,044
Arduino Chilled Mirror Hygrometer

Things used in this project

Hardware components

Arduino UNO
Arduino UNO
12V 10A power supply
12V 40mm high power fan
12V 40mm TEC
40mm heat sink
small mirror
Thermally conductive adhesive
Thermal paste
BTS7960 motor driver
3 mm LED: Yellow
3 mm LED: Yellow
OPT101 light sensor
IRF520 MOSFET module
DS18B20 Programmable Resolution 1-Wire Digital Thermometer
Maxim Integrated DS18B20 Programmable Resolution 1-Wire Digital Thermometer
SparkFun Humidity and Temperature Sensor Breakout - Si7021
SparkFun Humidity and Temperature Sensor Breakout - Si7021

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Hot glue gun (generic)
Hot glue gun (generic)


Read more


Chilled Mirror Hygrometer schematic


Chilled Mirror Hygrometer

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.




0 projects • 4 followers