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.
- 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] |
+----+--------+------------------------+------------+---------+
| | | |+3.3V | GND | SDA | SCL |
Rail | Rail | Line | Line |
=======+========+========================+============+==========
| | | || | +--------------------+ || | | +-----------------------------+| | | | | |+--+--------+---+---+--+ | |
| VCC GND SDA SCL | | || VEML6075 (0x10) | | |+----------------------+ | |
| | | |+--+--------+---+---+--+ | |
| VCC GND SDA SCL |<------------+ || MLX90614 (0x5A) | |+----------------------+ |
| | |+--+--------+---+---+--+ |
| 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() {
}
``
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.
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.
#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.
Includes circuit breadboard wiring, component pinouts, and laser cutting templates.
Screen-share or show your breadboard to M. F. Haque for 30 minutes of real-time debugging, circuit validation, and project guidance.
See What Mentora Makers Are Building
Real video walk-throughs, breadboard tests, and flight demonstrations submitted by students & parents.
Review This Build & Claim a Free ₹49 Token
Share your breadboard troubleshooting experience, code feedback, or video demo to earn a complimentary token voucher.
Have a Reward Voucher?
Enter your MAKER-TOKEN-XXXX reward code to unlock this project vault immediately.
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!
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).
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.