Ignacio Rosell
Published © GPL3+

miniAstroHeat Project powered by Blynk

minAstroHeat is an automatic 2-Channel dew heater controller for (astro)photography, controlled with an app powered by Blynk

BeginnerFull instructions provided3 hours442
miniAstroHeat Project powered by Blynk

Things used in this project

Hardware components

ESP32 DevKit4
×1
DS18B20 Temperature Sensor
×2
DS18B20 Terminal Adapter Module
×2
DHT11 Ambient Sensor Module
×1
MOSFET IRF520N module
×2
DC-DC Step-Down Converter
×1
Telescope Lens Dew Heater
Telescope lens dew heaters must be purchased fitting your telescope's lens dimensions
×2
ESP32 WiFi Antenna
×1
Waterproof Cable Gland
×1
Plastic Enclosure
×1

Software apps and online services

Blynk
Blynk
Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Breadboard and jumper wires
12V DC 6A Power Source
Digital Multimeter

Story

Read more

Schematics

miniAstroHeat 2-Channel DIY schematics

Follow this schematics to build your own miniAstroHeat. No soldering skills required.

Code

miniAstroHeat 2-Channel DIY solution

Arduino
Welcome to the miniAstroHeat firmware - the 2-Channel solution
for controlling the temperature and humidity of your
astrophotography and outdoor photography equipment. This code
can automatically and manually control and monitor two different
lenses or zone of your equipment.
The DHT11 sensor measures the ambient temperature and humidity,
while the DS18B20 measures the temperature of your lenses or
equipment. Using this data, it calculates the dew point and
delivers the right power to your heaters, ensuring that moisture
never becomes an issue.
Build your own Blynk app, powered by Blynk, so you can easily
connect with you miniAstroHeat and control your gear from anywhere
at any time.
Simply follow our step-by-step guidelines at www.astroheat.biz
or at https://www.hackster.io/ignaciorosell/miniastroheat-project-powered-by-blynk-b0868a
to know how to activate your device and set up your miniAstroHeat app with Blynk.
Get started today and take your astrophotography to the next level!
/*************************************************************
Welcome to the miniAstroHeat firmware - the 2-Channel solution
for controlling the temperature and humidity of your
astrophotography and outdoor photography equipment. This code
can automatically and manually control and monitor two different
lenses or zone of your equipment.
The DHT11 sensor measures the ambient temperature and humidity,
while the DS18B20 measures the temperature of your lenses or
equipment. Using this data, it calculates the dew point and
delivers the right power to your heaters, ensuring that moisture
never becomes an issue.
Build your own Blynk app, powered by Blynk, so you can easily
connect with you miniAstroHeat and control your gear from anywhere
at any time.
Simply follow our step-by-step guidelines at www.astroheat.biz
or at https://www.hackster.io/ignaciorosell/miniastroheat-project-powered-by-blynk-b0868a
to know how to activate your device and set up your miniAstroHeat app with Blynk.
Get started today and take your astrophotography to the next level!
 *************************************************************/

// Template ID, Device Name and Auth Token are provided by the Blynk.Cloud
// See the Device Info tab, or Template settings
#define BLYNK_TEMPLATE_ID "your_template_ID"
#define BLYNK_DEVICE_NAME "your_device_name"
#define BLYNK_AUTH_TOKEN "your_auth_token"

// Comment this out to disable prints and save space
#define BLYNK_PRINT Serial

#include <WiFi.h>
#include <WiFiClient.h>
#include <BlynkSimpleEsp32.h>

char auth[] = BLYNK_AUTH_TOKEN;

// Your WiFi credentials.
// Set password to "" for open networks.
char ssid[] = "your_wifi_ssid";  
char pass[] = "your_wifi_pass"; 

float       temperatureAmbient;
float       humidityAmbient;
float       logEx;
float       dewPointAmbient;
boolean     errorAmbientSensor = 0;

int powerMode = 0; // 0 = Dew Auto, 1 = Manual Power

float       aboveDewPoint[2];
float       aboveDewPointConstrain[2];
float       tempSens[2];
boolean     errorTempSens[] = {0, 0}; // 0 = no error; 1 = sensor error

int defSet[] = {0, 0};
float displayPower[] = {0, 0};
float commdPower[] = {0, 0};

int         dewPointThreshold = 8; //The bigger the value the sooner the heater switches on with a smoother reaction
int         cutoffTemp = 28; //Safety cutoff Temperature. Above this temperature the heaters go off
const int heaterPin[]  = {6, 7};     // MOSFET heater output channels on PWM pins - GPIOs 6 & 7
const int DS18B20pin[] = {13, 14};    // heater temp sensor pins - GPIOs 13 & 14

#include <DHT.h>
#define DHTTYPE DHT11
#define DHTPIN 25         //DHT11 Ambient Sensor GPIO 25
DHT dht(DHTPIN, DHTTYPE);

#include <OneWire.h>
#include <DallasTemperature.h>

OneWire oneWire0(DS18B20pin[0]);
OneWire oneWire1(DS18B20pin[1]);
DallasTemperature sensor0(&oneWire0);
DallasTemperature sensor1(&oneWire1);

BlynkTimer timer;

BLYNK_WRITE(V9)               //The system reads the commanded maual power from the app for heater 1 - Virtual Pin 9
{
  defSet[0] = param.asInt();
}

BLYNK_WRITE(V10)              //The system reads the commanded maual power from the app for heater 2 - Virtual Pin 10
{
  defSet[1] = param.asInt();
}

BLYNK_WRITE(V1)               //The system reads the commanded power mode from the app - Virtual Pin 1
{
  powerMode = param.asInt();
}     

void astroHeatTimer()   //Blynk Timer - This function is excuted every 2sec as specified in the Setup function
{
  // read ambient temp and humidity - and calculates dewPointAmbient
  humidityAmbient = dht.readHumidity();
  temperatureAmbient = dht.readTemperature();
  sensor0.requestTemperatures();
  sensor1.requestTemperatures();
  tempSens[0] = sensor0.getTempCByIndex(0);
  tempSens[1] = sensor1.getTempCByIndex(0);
  delay(100);
  
// Check status temperature sensors
  for (int i = 0; i < 2; i++) { //Check temperature sensors
     if (tempSens[i] == -127.00) { 
        errorTempSens[i] = 1;
      }
     else {
        errorTempSens[i] = 0; //
      }
     }

//Check status ambient sensor. If error then powerMode set automatically to Manual Power Mode only
  if (isnan(temperatureAmbient) || isnan(humidityAmbient)) {   
    errorAmbientSensor = 1;
    powerMode = 1;          
    }
  else {
    errorAmbientSensor = 0;
    logEx = 0.66077 + 7.5 * temperatureAmbient / (237.3 + temperatureAmbient) + (log10(humidityAmbient) - 2);
    dewPointAmbient = (logEx - 0.66077) * 237.3 / (0.66077 + 7.5 - logEx);       
    }

   switch (powerMode) {
          
     case 0: //Dew Auto heating mode
       
       Blynk.setProperty(V9, "label", "Dew Auto C");  //It writes in the Slider widget the label associated to the selected heating mode, in this dew auto
       Blynk.setProperty(V10, "label", "Dew Auto C");
       
       for (int i = 0; i < 2; i++) {
         if (errorTempSens[i] == 1 || tempSens[i] >= cutoffTemp) {
          commdPower[i] = 0;
         }
         else { 
         aboveDewPoint[i] = tempSens[i] - dewPointAmbient;
         aboveDewPointConstrain[i] = constrain( aboveDewPoint[i], 0, dewPointThreshold);
         commdPower[i] = 255 * (( dewPointThreshold - aboveDewPointConstrain[i] ) / dewPointThreshold);
         }
        defSet[i] = dewPointAmbient + dewPointThreshold;
        displayPower[i] = commdPower[i] / 2.55; //display power in %
        analogWrite(heaterPin[i], commdPower[i]);
        }
         
        Blynk.virtualWrite(V9, defSet[0]); //It writes in the Slider widget the calculated target temperature for Sensor 1 in auto dew heating mode 
        Blynk.virtualWrite(V10, defSet[1]); //It writes in the Slider widget the calculated target temperature for Sensor 2 in auto dew heating mode  

        break;
          
        case 1: //Manual Power heating mode

         Blynk.setProperty(V9, "label", "Power Mode %"); //It writes in the Slider widget the label associated to the selected heating mode, in this manual power
         Blynk.setProperty(V10, "label", "Power Mode %");
         
         for (int i = 0; i < 2; i++) {
           if (errorTempSens[i] == 1 || tempSens[i] >= cutoffTemp) {
             commdPower[i] = 0;
           }
           else { 
            commdPower[i] = 2.55 * defSet[i]; //in case 1 target power is defSet1 that goes from 0 to 100%
           }
          displayPower[i] = commdPower[i] / 2.55; //display power in %
          analogWrite(heaterPin[i], commdPower[i]);    
          }
          
          Blynk.virtualWrite(V9, defSet[0]);
          Blynk.virtualWrite(V10, defSet[1]);
          
          break;
    }     
        
    Blynk.virtualWrite(V1, powerMode);
    
    if (errorAmbientSensor ==1) {
    Blynk.setProperty(V16, "label", "Error-Amb.Hum.");    // If Ambient Sensor fails, the app will show and error message in the labels
    Blynk.setProperty(V17, "label", "Error-Amb.Temp.");
    Blynk.setProperty(V18, "label", "Error-Dew.Pt.");
    Blynk.virtualWrite(V16, humidityAmbient);
    Blynk.virtualWrite(V17, temperatureAmbient);
    Blynk.virtualWrite(V18, dewPointAmbient);  
    }
    else {
    Blynk.setProperty(V16, "label", "Ambient Humidity");
    Blynk.setProperty(V17, "label", "Ambient Temperature");
    Blynk.setProperty(V18, "label", "Dew Point");
    Blynk.virtualWrite(V16, humidityAmbient);
    Blynk.virtualWrite(V17, temperatureAmbient);
    Blynk.virtualWrite(V18, dewPointAmbient);  
    }
    
    if (errorTempSens[0] == 1) {
    Blynk.setProperty(V19, "label", "ErrorSensor1");    // If Temperature Sensor 1 fails, the app will show and error message in the labels
    Blynk.virtualWrite(V19, tempSens[0]);
    }
    else {
    Blynk.setProperty(V19, "label", "Sensor 1 Temp");
    Blynk.virtualWrite(V19, tempSens[0]);
    }
    if (errorTempSens[1] == 1) {
    Blynk.setProperty(V20, "label", "ErrorSensor2");    // If Temperature Sensor 2 fails, the app will show and error message in the labels
    Blynk.virtualWrite(V20, tempSens[1]);
    }
    else {
    Blynk.setProperty(V20, "label", "Sensor 2 Temp");
    Blynk.virtualWrite(V20, tempSens[1]);
    }
    
    Blynk.virtualWrite(V5, displayPower[0]);
    Blynk.virtualWrite(V6, displayPower[1]);
}

void setup()
{
  Serial.begin(115200);
  delay(100);
  timer.setInterval(2000L, astroHeatTimer);   //astroHeatTimer functions is executed every 2 seconds

  Blynk.begin(auth, ssid, pass);
}

void loop()                                   
{
  Blynk.run();
  timer.run();
}

Credits

Ignacio Rosell

Ignacio Rosell

1 project • 1 follower

Comments