ESP32 UNO - GUVA-S12SD UV Sensor

Is it time for more sunscreen? Is that cheap UV flashlight really giving out UV? A GUVA-S12SD module can answer both, and the ESP32 Uno form board reads it with three jumper wires and no extra parts. The sensor gives a small voltage. The board turns that voltage into millivolts, and a simple rule turns millivolts into a UV index you already know from the weather report.

What this page covers:

  1. What is inside the purple module and what its SIG, GND and VCC pins do
  2. Which header holes to use on the ESP32 Uno, and one thing to know about A0
  3. A first sketch that prints the ADC count and the SIG voltage side by side
  4. A second sketch that averages the input and prints a UV index from Low to Extreme
ESP32 Uno GUVA-S12SD UV sensor

Components You Need

1×ESP32 Uno-form Board
1×USB Cable Type-A to Type-C (for USB-A PC)
1×USB Cable Type-C to Type-C (for USB-C PC)
1×GUVA-S12SD UV Sensor Module
1×Jumper Wires

Or you can buy the following kits:

1×DIYables Sensor Kit (18 sensors/displays)
Disclosure: Some of the links provided in this section are Amazon affiliate links. We may receive a commission for any purchases made through these links at no additional cost to you.
Additionally, some of these links are for products from our own brand, DIYables .

The GUVA-S12SD UV Sensor Module

This part explains what the module measures, so the numbers on the Serial Monitor make sense later.

The board is small and purple, about 11mm x 27mm, with two holes for screws. Look for the tiny white square: that is the GUVA-S12SD photodiode. When UV light hits it, a very small current flows. That current is too weak for any ADC, so the VLM358 op-amp next to it changes it into a voltage on SIG. More UV means a higher voltage. The DIYables module comes with the header already soldered, two modules per pack.

GUVA-S12SD UV sensor module pinout
image source: diyables.io
Pin What It Does ESP32 Uno Connection
SIG Analog output, about 0V with no UV and around 1V in strong sun A0 (GPIO2)
GND Ground GND
VCC Power, 2.5V to 5V DC 3.3V

Some facts to keep in mind:

  • It reacts to UV-A and UV-B, from 240nm to 370nm.
  • It looks out over a wide 130 degree cone, so it does not need to point straight at the sun.
  • Normal lamps and window light give almost no UV. At your desk the output stays near 0V. That is correct, not a fault.

From voltage to UV index

Most makers use one easy rule: UV index ≈ SIG voltage / 0.1. So 0.3V is about UV index 3, and 0.7V is about 7. The result is good enough for a hobby project, but it is not a lab meter. Here is the World Health Organization scale the second sketch uses:

UV Index Level What It Means Outside
0 to 2 Low Safe for most people
3 to 5 Moderate Use shade at noon
6 to 7 High Sunscreen, hat and shade
8 to 10 Very High Stay out of the midday sun
11 and above Extreme Avoid the sun if you can

ESP32 Uno Pinout

The sketches use the number 2 for the sensor pin. The diagram shows why: the hole printed A0 on the Uno-style header is GPIO2 on the ESP32 chip.

ESP32 Uno pinout diagram

How to Connect

Only three wires are needed. Plug them from the module header into the board header.

The wiring diagram between ESP32 Uno GUVA-S12SD UV sensor

This image is created using Fritzing. Click to enlarge image

GUVA-S12SD Pin ESP32 Uno Pin
SIG A0 (GPIO2)
GND GND
VCC 3.3V

WARNING

Keep VCC on the 3.3V pin. The ESP32 Uno pins take 3.3V at most, and the module works fine at 3.3V.

Good to know: GPIO2 is an ADC2 pin, and ESP32 cannot read ADC2 while WiFi is on; that is no problem here, because these sketches do not use WiFi. GPIO2 is also a boot strapping pin. If an upload ever fails with the sensor plugged in, pull out the SIG wire, upload, then put it back.

ESP32 Uno Code - Read the ADC Count and SIG Voltage

This first sketch prints two numbers every 0.8 seconds. One is the plain 12-bit count from analogRead(). The other is the pin voltage from analogReadMilliVolts(), which uses the calibration data stored in each ESP32 chip at the factory. That makes the voltage more accurate than a simple "count x 3.3 / 4095" sum.

/* * This ESP32 Uno code was developed by newbiely.com * * This ESP32 Uno code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/esp32-uno/esp32-uno-guva-s12sd-uv-sensor */ /* * ESP32 Uno sketch: read the SIG pin of the GUVA-S12SD UV sensor module. * Each line shows the raw 12-bit ADC count and the calibrated * pin voltage from analogReadMilliVolts(). */ #define UV_PIN 2 // A0 on the Uno-style header is GPIO2 on the ESP32 chip void setup() { Serial.begin(9600); analogSetAttenuation(ADC_11db); // let the pin read from 0V up to about 3.1V } void loop() { int adc_count = analogRead(UV_PIN); // 0 to 4095, not corrected int sig_mv = analogReadMilliVolts(UV_PIN); // millivolts, uses the factory calibration Serial.print("ADC count: "); Serial.print(adc_count); Serial.print(" SIG: "); Serial.print(sig_mv / 1000.0, 3); // print in volts with three decimals Serial.println(" V"); delay(800); }

Upload and Test

  1. New to this board? Install the ESP32 core with ESP32 Uno - Getting Started first.
  2. Make the three connections from the table above.
  3. Plug the board in with the USB Type-C cable.
  4. Pick the board in Arduino IDE: ESP32 Dev Module and the CH340 COM port.
  5. Paste the sketch into a new window and click Upload.
  6. Open the Serial Monitor at 9600 baud.
  7. Watch it indoors first, then carry the laptop to a sunny window with the window open, or go outside. A long USB cable or a power bank helps. The log looks like this:
∞
Newbiely | Arduino IDE 2.3.8
──
☐
✕
File
Edit
Sketch
Tools
Help
ESP32 Dev Module
Newbiely.ino
···
8 Serial.println("Hello World!");
Output
Serial Monitor
Message (Enter to send message to 'ESP32 Dev Module' on 'COM15')
New Line
9600 baud
ADC count: 0 SIG: 0.074 V ADC count: 0 SIG: 0.074 V ADC count: 0 SIG: 0.075 V ADC count: 268 SIG: 0.352 V ADC count: 401 SIG: 0.463 V ADC count: 577 SIG: 0.604 V ADC count: 612 SIG: 0.632 V
Ln 11, Col 1
ESP32 Dev Module on COM15
2
  1. Pro tip: no sun today? A 365nm or 395nm UV flashlight held 5 cm above the white square also lifts the reading.

Why does the voltage show 0.07V when the count is 0? The classic ESP32 ADC cannot tell apart voltages under roughly 0.1V. A count of 0 just means "at the bottom", and the small millivolt value is the calibration offset, not real UV. The second sketch handles this case.

ESP32 Uno Code - UV Index with Level Names

Now the sketch takes 20 quick readings and averages them, so one noisy sample does not jump the result. It divides the voltage by 0.1 to get the UV index, then looks up the WHO level in a small table.

/* * This ESP32 Uno code was developed by newbiely.com * * This ESP32 Uno code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/esp32-uno/esp32-uno-guva-s12sd-uv-sensor */ /* * ESP32 Uno sketch: turn the GUVA-S12SD SIG voltage into a rough UV index. * Rule of thumb for hobby use: about 0.1V on SIG = 1 UV index point. * This is an estimate, not a calibrated UV meter. */ #define UV_PIN 2 // A0 (GPIO2) is wired to SIG #define SAMPLES 20 // readings taken for one averaged result // upper limit of each WHO level, and its name const float LEVEL_TOP[] = {3.0, 6.0, 8.0, 11.0}; const char* LEVEL_NAME[] = {"Low", "Moderate", "High", "Very High", "Extreme"}; void setup() { Serial.begin(9600); analogSetAttenuation(ADC_11db); // full range, up to about 3.1V } void loop() { long total_mv = 0; long total_raw = 0; // take several readings 2 ms apart and add them up for (int i = 0; i < SAMPLES; i++) { total_raw += analogRead(UV_PIN); total_mv += analogReadMilliVolts(UV_PIN); delay(2); } float sig_volt = (total_mv / (float)SAMPLES) / 1000.0; // a raw count of 0 means the pin is at the very bottom of the ADC range, // so ignore the small calibration offset and call it 0V if (total_raw == 0) sig_volt = 0.0; float uv_index = sig_volt / 0.1; // every 0.1V is about one UV index point // walk up the level table until the UV index fits int level = 0; while (level < 4 && uv_index >= LEVEL_TOP[level]) level++; Serial.print("SIG "); Serial.print(sig_volt, 2); Serial.print(" V | UV index "); Serial.print(uv_index, 1); Serial.print(" | "); Serial.println(LEVEL_NAME[level]); delay(1000); }

Upload and Test

  1. Keep the wiring from the first sketch.
  2. Upload this sketch in place of the old one.
  3. Open the Serial Monitor at 9600 baud again.
  4. Start in the room, then move into the sun. Here is a run from a clear afternoon:
∞
Newbiely | Arduino IDE 2.3.8
──
☐
✕
File
Edit
Sketch
Tools
Help
ESP32 Dev Module
Newbiely.ino
···
8 Serial.println("Hello World!");
Output
Serial Monitor
Message (Enter to send message to 'ESP32 Dev Module' on 'COM15')
New Line
9600 baud
SIG 0.00 V | UV index 0.0 | Low SIG 0.00 V | UV index 0.0 | Low SIG 0.18 V | UV index 1.8 | Low SIG 0.47 V | UV index 4.7 | Moderate SIG 0.63 V | UV index 6.3 | High SIG 0.66 V | UV index 6.6 | High SIG 0.41 V | UV index 4.1 | Moderate
Ln 11, Col 1
ESP32 Dev Module on COM15
2
  1. Pro tip: compare the number with the UV index in a weather app at the same time. If yours is always a bit higher or lower, change 0.1 in the code to fit your module.

The 1.8 line was taken in the shade of a tree, and the 4.1 line came when a cloud passed. Glass blocks most UV-B, so behind a closed window the index drops a lot.

Video Tutorial

See the ESP32 Uno build in action in the video below.

Code Explanation

Code Part What It Does
#define UV_PIN 2 Uses GPIO2, which is the A0 hole on the header
analogSetAttenuation(ADC_11db) Opens the input range to about 3.1V, more than the module ever gives
analogRead(UV_PIN) Gives a count from 0 to 4095 with no correction
analogReadMilliVolts(UV_PIN) Gives the pin voltage in mV, corrected by the chip's own calibration
for (int i = 0; i < SAMPLES; i++) Adds 20 readings so the average is calm
if (total_raw == 0) Treats a reading at the very bottom of the range as 0V
sig_volt / 0.1 The rule of thumb, 0.1V per UV index point
LEVEL_TOP[] and LEVEL_NAME[] Upper limit and name of each WHO level
while (level < 4 && ...) Moves up the table until the UV index fits

Project Ideas

  • Sunscreen reminder: count the minutes spent at UV index 3 or more and print "Put on sunscreen again" after two hours.
  • Balcony UV station: add a DHT22 sensor and show UV, temperature and humidity on an OLED. See also the ESP32 Uno - Smart Weather Station.
  • UV lamp tester: check if a nail lamp or a UV LED strip still gives out UV after months of use.
  • Smart blind: close a blind with a servo when the UV index goes above 6.
  • Phone view: show the UV index on a Web Analog Gauge or a Bluetooth Plotter. For the WiFi version, move SIG to A2 (GPIO35) and set UV_PIN to 35, because A0 is on ADC2. Bluetooth works with A0.

Troubleshooting

Problem Possible Cause Solution
Count is always 0, even outside No UV reaches the sensor, or SIG is loose Go out in direct sun with no glass in between, and check the SIG wire on A0
Count is always 0 with good light The code reads the wrong pin Use 2 in the code, since A0 is GPIO2
Count stays near 4095 SIG touches VCC, or pins swapped Follow the SIG, GND, VCC labels on the module
UV index looks too low behind a window Glass blocks most UV Open the window or test outdoors
Reading stops changing after adding WiFi A0 is ADC2 and WiFi blocks it Move SIG to A2 (GPIO35) and use 35 in the code
Upload fails with the sensor plugged in GPIO2 is a boot strapping pin Unplug SIG during upload, or hold BOOT as the upload starts
No COM port in Arduino IDE CH340 driver missing or charge-only cable Install the CH340 driver and use a data cable
Serial Monitor shows odd symbols Baud rate mismatch Select 9600 baud

FAQ

Why does the sensor read 0 in my bright room?

Indoor lamps and LED bulbs give almost no UV, and window glass blocks a lot of it. The GUVA-S12SD only reacts to light from 240nm to 370nm, so a dark-looking reading indoors is normal.

Can I power the module from 5V on the ESP32 Uno?

The module accepts 5V, but keep it on 3.3V. The ESP32 Uno pins are 3.3V only, and the module works just as well at 3.3V.

Why use analogReadMilliVolts() and not analogRead()?

Every ESP32 chip has a slightly different ADC. analogReadMilliVolts() uses calibration data stored in the chip, so it gives a truer voltage. Since the UV index comes straight from the voltage, this matters.

How exact is the UV index from this module?

It is a good estimate, often within one or two points of a weather app. Dust on the sensor, the angle to the sun and the module itself all change it a little. For a medical or lab result, use a calibrated UV meter.

Can I use another analog pin?

Yes. A1 (GPIO4) also works without WiFi. A2 to A5 (GPIO35, 34, 36, 39) and GPIO32/33 are on ADC1, so they keep working when WiFi is on. Change UV_PIN to the GPIO number.

Does the sensor wear out in the sun?

No. It is made to sit in sunlight. If you place it outside, put it under a clear cover that lets UV pass, such as quartz or a UV-transparent acrylic. Normal glass and most plastics block UV.

※ OUR MESSAGES

  • As freelancers, We are AVAILABLE for HIRE. See how to outsource your project to us
  • Please feel free to share the link of this tutorial. However, Please do not use our content on any other websites. We invested a lot of effort and time to create the content, please respect our work!