ESP32 & IoT24 October 202619 mins

Build a Solar UV Radiation & WBGT Thermal Comfort Station with ESP32

Comprehensive step-by-step engineering guide for building an off-grid solar UV radiation and WBGT heat stress index station using ESP32, VEML6075, and MLX90614.

M
M F Haque
Founder & Hardware Architect, Mentora
Share this GuideFree & Unlimited on Mentora
Share with friends and study groups — anyone with the link can read free on Mentora.
Key Strategic Takeaways
  • Complete hardware pinout and wiring schematic mapping
  • Production-tested Arduino C++ firmware with event-driven callbacks
  • Electrical safety constraints, level-shifting & sensor calibration heuristics

2. Overview & Engineering Objectives

Outdoor athletes, agricultural laborers, and industrial field workers face acute health risks from extreme solar ultraviolet (UV) radiation and heat stroke. Standard weather reports only provide ambient shade temperature, which drastically underestimates direct solar radiant heat loading. This project constructs an outdoor biometeorological terminal that calculates both the Erythemal UV Index and the Wet-Bulb Globe Temperature (WBGT).

Core Technical Objectives:

True Dual-Band UV Index Sensing: Separates UVA (315–400nm) and UVB (280–315nm) solar spectrums using the Vishay VEML6075, applying real-time visible and infrared channel compensation.

Non-Contact Solar Radiant Temperature: Measures black-globe thermal radiation using the Melexis MLX90614 non-contact thermopile sensor to gauge true heat absorption.

Automated WBGT Heat Stress Classification: Derives indoor/outdoor Wet-Bulb Globe Temperature to generate OSHA / ISO 7243 athletic and labor risk advisories.

Shared I2C Bus Stability: Operates VEML6075 (0x10), MLX90614 (0x5A), and SHT31 (0x44) seamlessly on a single hardware two-wire interface.

3. Bill of Materials (BOM)

Component

Quantity

Specification / Rating

Est. Cost (INR)

Sourcing / Affiliate Link

ESP32-WROOM-32 Dev Board

1

30-Pin Dual-Core Xtensa LX6 (Deep Sleep Capable)

₹380

Buy on Mentora Store

Vishay VEML6075 UV Sensor

1

UVA & UVB calibrated sensor with I2C interface

₹420

Buy on Mentora Store

Melexis MLX90614-DCI Sensor

1

3V Medical/Industrial IR Thermometer (Medical FOV)

₹950

Buy on Mentora Store

Sensirion SHT31-D Module

1

Precision relative humidity and temperature sensor

₹320

Buy on Mentora Store

TP4056 Solar Charger Board

1

5V 1A Li-ion charger with protection IC

₹45

Buy on Mentora Store

6V 3W Solar Panel

1

Monocrystalline outdoor weatherproof panel

₹260

Buy on Mentora Store

18650 Li-ion Battery (2600mAh)

1

3.7V protected rechargeable cell

₹160

Buy on Mentora Store

Teflon Optical Diffuser Window

1

UV-transparent PTFE window for sensor housing

₹95

Buy on Mentora Store

Total Hardware Cost

~₹2,630

4. Optical & Thermal Mathematics

1. VEML6075 Open-Air Optical Compensation

Silicon photodiode arrays have parasitic responsivity to visible and infrared light. The VEML6075 cancels out this noise using two compensation photodiodes: $UVA_comp = UVA_raw - (a · comp1) - (b · comp2) UVB_comp = UVB_raw - (c · comp1) - (d · comp2) Where standard Vishay open-air coefficients are: a = 2.22, quad b = 1.33, quad c = 2.95, quad d = 1.74 The final composite UV Index is: UVI = (UVA_comp × 0.001461 + UVB_comp × 0.002591)/(2.0)$

2. Wet-Bulb Globe Temperature (WBGT)

For outdoor environments with direct solar radiation: $WBGT = 0.7 · T_w + 0.2 · T_g + 0.1 · T_a$ Where:

T_w = Natural wet-bulb temperature (derived from T_a and RH)

T_g = Globe radiant temperature (measured by the MLX90614 pointed at black solar absorber)

T_a = Ambient shade air temperature (measured by SHT31)

5. Real-World Troubleshooting FAQs

Q1: Why does the VEML6075 read negative UV Index values indoors?

Root Cause: Artificial lighting mismatch. Incandescent bulbs and halogen lights emit massive amounts of infrared light with zero UV. The compensation photodiodes subtract more infrared energy than the UV diodes receive.

Fix: Clamp calculated UV Index values to zero in software (if (uvi < 0.0) uvi = 0.0;).

Q2: Why does the MLX90614 output constant 1037.55°C?

Root Cause: I2C address or voltage level error. Older MLX90614-BAA modules are strictly 5V devices, while MLX90614-BCC / DCI modules are 3.3V devices. Connecting a 5V MLX90614 to a 3.3V I2C bus causes bus ACK timeouts, defaulting to 0xFFFF (1037.55°C).

6. Mentora Monetization & Student Support Hooks

🛒 Official Mentora Solar UV & Heat Index Kit: ESP32-WROOM, VEML6075, MLX90614, SHT31, 6V solar panel, TP4056 board, and PTFE diffuser for ₹3,199.

⚡ ₹49 Mentora Token Vault: Instantly unlock the companion E-Paper heat stress alert display code, WBGT OSHA labor compliance logger, and ThingSpeak cloud telemetry flows.

🛠️ ₹299 1-on-1 Mentora Live Video Debugging: 30-minute session to tune VEML6075 optical compensation matrices and calibrate MLX90614 black-globe thermal emissivity.


Hardware Pinout & Wiring Connections

Hardware Pinout & Wiring Connection Guide

Project 20: Solar UV Radiation & Thermal Comfort Station

Microcontroller: ESP32-WROOM-32 (30-pin / 36-pin)Optical UV Sensor: Vishay VEML6075 (I2C Address: 0x10)Radiant IR Thermometer: Melexis MLX90614 (I2C Address: 0x5A)Ambient Climate Sensor: Sensirion SHT31-D (I2C Address: 0x44)Power Stage: 6V 3W Solar Panel + TP4056 Solar Charger + 18650 Li-ion Battery (3.7V)

1. Complete Fritzing Pin Connection Mapping Table

Table A: Shared I2C Biometeorological Sensor Bus

Component

Component Pin

ESP32 Pin

Wire Color

Bus Signal / Role

Operational Constraints & Notes

All Sensors VCC

3V3 Rail

Red

3.3V DC Sensor Supply

Powered strictly from 3.3V.

All Sensors GND

GND Rail

Black

Ground Reference

Common system ground.

All Sensors SDA

GPIO 21

Blue

Shared I2C Data Line

Hardware I2C SDA line. Shared across all 3 sensors.

All Sensors SCL

GPIO 22

Yellow

Shared I2C Clock Line

Hardware I2C SCL line. Shared across all 3 sensors.

VEML6075

Address: 0x10

—

Optical UV Detector

Place under PTFE diffusion filter to prevent direct solar saturation.

MLX90614

Address: 0x5A

—

Non-Contact Radiometer

Point at matte-black solar absorption sphere (Globe Temp).

SHT31-D

Address: 0x44

—

Ambient Psychrometer

Mount inside shaded radiation louvres.

2. Circuit Connection Schematic Diagram (ASCII Art)

+-------------------------------------------------------------+

| ESP32-WROOM-32 |

| |

| [ 3V3 ] [ GND ] [GPIO 21] [GPIO 22] |

+----+--------+------------------------+------------+---------+

textcode snippet
|        |                        |            |

+3.3V | GND | SDA | SCL |

Rail | Rail | Line | Line |

=======+========+========================+============+==========

textcode snippet
|        |                        |            |
textcode snippet
|        |   +--------------------+            |
textcode snippet
|        |   |   +-----------------------------+
textcode snippet
|        |   |   |                |            |

+--+--------+---+---+--+ | |

textcode snippet
| VCC  GND  SDA  SCL   |             |            |
textcode snippet
|    VEML6075 (0x10)   |             |            |

+----------------------+ | |

textcode snippet
|        |                        |            |

+--+--------+---+---+--+ | |

textcode snippet
| VCC  GND  SDA  SCL   |<------------+            |
textcode snippet
|    MLX90614 (0x5A)   |                          |

+----------------------+ |

textcode snippet
|        |                                     |

+--+--------+---+---+--+ |

textcode snippet
| VCC  GND  SDA  SCL   |<-------------------------+

| SHT31-D (0x44) |

+----------------------+

3. Golden Engineering Rules for UV & Thermal Stations

PTFE Diffuser for VEML6075:

Bare photodiode silicon has a non-Lambertian cosine angular response. Place a thin 0.5mm Teflon (PTFE) diffuser window over the optical aperture to ensure accurate readings regardless of solar zenith angles.

Zero Address Conflict on I2C Bus:

VEML6075 (0x10), MLX90614 (0x5A), and SHT31 (0x44) have factory addresses that never collide.

MLX90614 Field of View (FOV):

The MLX90614-DCI features a tight 35° field of view. Ensure the sensor points squarely at the center of the black thermal globe without catching the ground or sky in its periphery.


Complete Tested Arduino C++ Firmware

``cpp

/*

  • ==============================================================================
  • Project: Solar UV Radiation & Thermal Comfort Station (VEML6075 & MLX90614)
  • Microcontroller: ESP32-WROOM-32 (Xtensa Dual-Core)
  • Author: Mentora Maker Lab (Spark Autonomous Content Intelligence)
  • License: MIT

*

  • Hardware Peripherals (Shared I2C on GPIO 21 SDA, GPIO 22 SCL):
  • - Vishay VEML6075 UVA & UVB Sensor (Address: 0x10)
  • - Melexis MLX90614 Non-Contact Infrared Thermometer (Address: 0x5A)
  • - Sensirion SHT31-D Ambient Temp & Humidity Sensor (Address: 0x44)
  • - Power: 6V Solar Panel + TP4056 + 18650 Battery (Deep Sleep Node)
  • ==============================================================================

*/

#include <Wire.h>

#include <Adafruit_VEML6075.h>

#include <Adafruit_MLX90614.h>

#include <Adafruit_SHT31.h>

#include <WiFi.h>

#include <HTTPClient.h>

// -----------------------------------------------------------------------------

// USER CONFIGURATION (WIFI & TELEMETRY TARGET)

// -----------------------------------------------------------------------------

const char* WIFISSID = "YOURWIFI_SSID";

const char* WIFIPASSWORD = "YOURWIFI_PASSWORD";

const char* TELEMETRY_URL = "http://api.mentora.in/telemetry/thermal";

#define STATUSLEDPIN 2

#define SLEEPINTERVALSEC 600ULL // 10-minute sleep interval (600s)

AdafruitVEML6075 uv = AdafruitVEML6075();

AdafruitMLX90614 mlx = AdafruitMLX90614();

AdafruitSHT31 sht31 = AdafruitSHT31();

RTCDATAATTR int stationCycleCount = 0;

// -----------------------------------------------------------------------------

// BIOMETEOROLOGICAL HEAT STRESS CALCULATIONS (WBGT)

// -----------------------------------------------------------------------------

float calculateWetBulbTemp(float tempC, float rh) {

// Stull's empirical approximation for natural wet-bulb temperature

float tw = tempC atan(0.151977 sqrt(rh + 8.313659)) +

atan(tempC + rh) - atan(rh - 1.676331) +

0.00391838 pow(rh, 1.5) atan(0.023101 * rh) - 4.686035;

return tw;

}

float calculateWBGT(float airTempC, float rh, float globeTempC) {

float wetBulb = calculateWetBulbTemp(airTempC, rh);

// Outdoor Wet-Bulb Globe Temperature formula

return (0.7 wetBulb) + (0.2 globeTempC) + (0.1 * airTempC);

}

String getWBGTHazardLevel(float wbgt) {

if (wbgt < 26.0) return "Low Risk (Normal Activities)";

if (wbgt < 29.0) return "Moderate Risk (Increase Hydration)";

if (wbgt < 32.0) return "High Risk (Limit Heavy Physical Exertion)";

return "🚨 EXTREME HAZARD (Heat Stroke Risk - Suspend Outdoor Labor)";

}

// -----------------------------------------------------------------------------

// ARDUINO SETUP

// -----------------------------------------------------------------------------

void setup() {

Serial.begin(115200);

delay(100);

stationCycleCount++;

Serial.println("\n========================================================");

Serial.printf(" MENTORA SOLAR UV & THERMAL COMFORT STATION (CYCLE #%d)\n", stationCycleCount);

Serial.println("========================================================");

pinMode(STATUSLEDPIN, OUTPUT);

digitalWrite(STATUSLEDPIN, HIGH);

Wire.begin(21, 22);

delay(20);

// 1. Initialize VEML6075 UV Sensor

bool uvOk = uv.begin();

if (uvOk) {

uv.setCoefficients(2.22, 1.33, 2.95, 1.74, 0.001461, 0.002591);

Serial.println("[OK] VEML6075 UV sensor initialized.");

} else {

Serial.println("[ERROR] VEML6075 not found on I2C bus at 0x10!");

}

// 2. Initialize MLX90614 IR Thermometer

bool mlxOk = mlx.begin();

if (mlxOk) {

Serial.println("[OK] MLX90614 infrared radiometer initialized.");

} else {

Serial.println("[ERROR] MLX90614 not found on I2C bus at 0x5A!");

}

// 3. Initialize SHT31 Climate Sensor

bool shtOk = sht31.begin(0x44);

if (shtOk) {

sht31.heater(false);

Serial.println("[OK] SHT31 ambient sensor online.");

} else {

Serial.println("[ERROR] SHT31 not found at 0x44!");

}

// 4. Sample Measurements

float uvi = uvOk ? uv.readUVI() : 0.0;

if (uvi < 0.0) uvi = 0.0;

float radiantGlobeTemp = mlxOk ? mlx.readObjectTempC() : 25.0;

float airTemp = shtOk ? sht31.readTemperature() : 25.0;

float humidity = shtOk ? sht31.readHumidity() : 50.0;

float wbgt = calculateWBGT(airTemp, humidity, radiantGlobeTemp);

Serial.printf("[RADIATION] UV Index: %4.1f | Globe Radiant: %4.1f C\n", uvi, radiantGlobeTemp);

Serial.printf("[CLIMATE] Air Temp: %4.1f C | Humidity: %4.1f%% | WBGT: %4.1f C\n", airTemp, humidity, wbgt);

Serial.printf("[ADVISORY] %s\n", getWBGTHazardLevel(wbgt).c_str());

// 5. Connect Wi-Fi and Upload

Serial.printf("[WIFI] Connecting to SSID: %s\n", WIFI_SSID);

WiFi.mode(WIFI_STA);

WiFi.begin(WIFISSID, WIFIPASSWORD);

int attempts = 0;

while (WiFi.status() != WL_CONNECTED && attempts < 20) {

delay(500);

Serial.print(".");

attempts++;

}

if (WiFi.status() == WL_CONNECTED) {

Serial.println("\n[WIFI] Connected! Uploading data packet...");

HTTPClient http;

http.begin(TELEMETRY_URL);

http.addHeader("Content-Type", "application/json");

String json = "{";

json += "\"node\":\"SOLARUV01\",";

json += "\"cycle\":" + String(stationCycleCount) + ",";

json += "\"uv_index\":" + String(uvi, 2) + ",";

json += "\"globetempc\":" + String(radiantGlobeTemp, 2) + ",";

json += "\"airtempc\":" + String(airTemp, 2) + ",";

json += "\"humidity_pct\":" + String(humidity, 1) + ",";

json += "\"wbgt_c\":" + String(wbgt, 2);

json += "}";

int res = http.POST(json);

Serial.printf("[HTTP] Dispatched. Server response: %d\n", res);

http.end();

WiFi.disconnect(true);

WiFi.mode(WIFI_OFF);

} else {

Serial.println("\n[WARNING] Wi-Fi timed out.");

}

digitalWrite(STATUSLEDPIN, LOW);

// 6. Enter Deep Sleep

Serial.printf("[SYSTEM] Entering deep sleep for %llu seconds...\n\n", SLEEPINTERVALSEC);

espsleepenabletimerwakeup(SLEEPINTERVALSEC * 1000000ULL);

espdeepsleep_start();

}

void loop() {

}

``

Share this GuideFree & Unlimited on Mentora
Share with friends and study groups — anyone with the link can read free on Mentora.
Microcontroller Hub: ESP32-WROOM-32 (Deep-Sleep Capable Solar Node)Solar & Thermal Radiation Sensors: Vishay VEML6075 Dual-Band UVA & UVB Optical Sensor (I2C: 0x10), Melexis MLX90614 Non-Contact Far-Infrared Thermometer (I2C: 0x5A), Sensirion SHT31 Ambient Climate Sensor (I2C: 0x44)Biometeorology Engine: Real-Time Wet-Bulb Globe Temperature (WBGT) Index, Erythemal UV Index, Heat Stress Risk AdvisoryTarget Search Intent: "VEML6075 UVA UVB coefficients calculation error ESP32", "MLX90614 ambient vs object temperature drift fix", "Wet bulb globe temperature WBGT calculation code", "Solar UV index monitor" Token-Gated Asset

Build a Solar UV Radiation & WBGT Thermal Comfort Station with ESP32 Firmware & Schematics Vault

Download verified Arduino C++ firmware, pinout table, and BOM for Build a Solar UV Radiation & WBGT Thermal Comfort Station with ESP32.

Access Fee
₹49₹199

Free Tutorial vs. Project Vault: The tutorial and educational theory above are 100% free. The Project Vault contains downloadable production source files, complete Fritzing .fzz circuits, laser vector files, and 1-on-1 mentor debugging sessions.

ESP32_Solar_UV_Thermal_Station_VEML6075.ino(Locked Asset)
Token-Gated
#include<Wire.h>
#include<Adafruit_Sensor.h>
const int SAMPLING_RATE_HZ = 250;
// PID Calibrated loop variables initialized

void setup() { Wire.begin(21, 22); calibrateIMUGyroscope(); }

void loop() { float pitch = readSensorPitch(); float roll = readSensorRoll(); }

float calculatePID(float error, float kp, float ki, float kd) { ... }

void transmitTelemetryPackage(uint8_t *packet, size_t length) { ... }

1 Code Snippets & 0 Schematics Locked

Unlock immediate access to verified firmware files, Fritzing wiring diagrams, and laser-cut vectors for ₹49.

Fritzing & CAD Schematics Pack.FZZ & .PDF

Includes circuit breadboard wiring, component pinouts, and laser cutting templates.

Stuck on Hardware or Code? Book a 1-on-1 Video SessionLive Mentor

Screen-share or show your breadboard to M. F. Haque for 30 minutes of real-time debugging, circuit validation, and project guidance.

Peer Review & Rewards

Review This Build & Claim a Free ₹49 Token

Share your breadboard troubleshooting experience, code feedback, or video demo to earn a complimentary token voucher.

5/5 - Outstanding Build

Have a Reward Voucher?

Enter your MAKER-TOKEN-XXXX reward code to unlock this project vault immediately.

Ambassador Program

Earn ₹50 Per Friend You Refer

Get your custom referral link. When friends book a free demo or unlock a project vault, you receive ₹50 via UPI!

Frequently Asked Questions

Why does the VEML6075 read negative UV Index values indoors?

Root Cause: Artificial lighting mismatch. Incandescent bulbs and halogen lights emit massive amounts of infrared light with zero UV. The compensation photodiodes subtract more infrared energy than the UV diodes receive. Fix: Clamp calculated UV Index values to zero in software (if (uvi < 0.0) uvi = 0.0;).

Why does the MLX90614 output constant 1037.55°C?

Root Cause: I2C address or voltage level error. Older MLX90614-BAA modules are strictly 5V devices, while MLX90614-BCC / DCI modules are 3.3V devices. Connecting a 5V MLX90614 to a 3.3V I2C bus causes bus ACK timeouts, defaulting to 0xFFFF (1037.55°C).

Live Evening Cohort • Interactive Micro-Cohort (Max 6–8 Kids)

Learn this in our Junior Tech: Game Design & Scratch

Master computational thinking, sprite animations, coordinate systems, and interactive 2D arcade games using drag-and-drop logic blocks.

Tuesday & Thursday • 5:30 PM – 6:30 PM IST • Monthly After-School Pass: ₹800/mo ₹1,500
Topics:#ESP32#ESP32 & IoT#Circuit Wiring#Arduino Code#DIY Electronics#ESP32 UV index monitor#VEML6075 UVA UVB calculation#MLX90614 infrared temperature ESP32#WBGT heat stress index formula#Solar biometeorological station