SGP40 VOC Sensor with an ESP32 DevKit V1
The Sensirion SGP40 is an I2C metal-oxide gas sensor that reports a VOC Index (1-500) for total volatile organic compounds in indoor air.

On the ESP32 DevKit V1 the SGP40 breakout runs from 3V3 and joins the ESP32's default I2C pins, SDA on GPIO21 and SCL on GPIO22, which Wire uses when no pins are given. Its 10 kΩ pull-ups are tied to VIN, so powering VIN from 3V3 keeps the whole bus at the ESP32's 3.3 V logic level.
Wiring the SGP40 VOC Sensor to the ESP32 DevKit V1
Pin by pin.
| SGP40 VOC Sensor | ESP32 DevKit V1 |
|---|---|
| VIN | 3V3 |
| GND | GND |
| SCL | GPIO22 (D22) |
| SDA | GPIO21 (D21) |
What to know on the ESP32 DevKit V1
- Keep VIN on 3V3 rather than the DevKit's 5 V VIN pin: the breakout's pull-ups would otherwise hold GPIO21 and GPIO22 at 5 V, past the VDD + 0.3 V an ESP32 input is rated for.Adafruit SGP40 guide, Pinouts; Espressif ESP32 Series Datasheet v5.3, p. 52
- Adafruit rates VIN from 3 V, and the SGP40 itself runs from 1.7 V to 3.6 V, drawing about 2.6 mA when measuring continuously at 3.3 V, so the DevKit's 3V3 pin powers it with plenty to spare.Adafruit SGP40 guide, Pinouts; Sensirion SGP40 datasheet v1.2, p. 5
- The VOC algorithm keeps its baseline in RAM and adapts it slowly (a 12-hour time constant), so deep sleep between readings wipes it: every wake starts again with about 45 samples of 0. For a VOC Index, leave the ESP32 awake and call measureVocIndex() about once a second.Adafruit_SGP40 library, sensirion_voc_algorithm.h (VocAlgorithm_INITIAL_BLACKOUT, TAU_MEAN_VARIANCE_HOURS); Sensirion SGP40 datasheet v1.2, p. 9
- measureVocIndex(25.0, 50.0) sends the datasheet's default humidity and temperature, which keeps on-chip compensation off. A BME280 or SHT4x sharing GPIO21 and GPIO22 can supply real values; pass temperature first, then humidity.Sensirion SGP40 datasheet v1.2, pp. 9, 13; Adafruit_SGP40 library, Adafruit_SGP40.h
SGP40 VOC Sensor code for the ESP32 DevKit V1
Compiled for esp32:esp32:esp32 on 2026-10-30.
// SGP40 VOC Sensor on ESP32 DevKit V1: wiring and a free simulation at https://www.tinkered.ai/components/sgp40-voc-sensor/esp32
#include <Wire.h>
#include "Adafruit_SGP40.h"
Adafruit_SGP40 sgp;
// The SGP40 compensates for humidity and temperature. With no sensor for them, pass the
// library's defaults, 25 °C and 50 %RH; with an SHT4x or a BME280 beside it, pass its readings.
const float TEMPERATURE_C = 25.0;
const float HUMIDITY_RH = 50.0;
void setup() {
Serial.begin(115200);
if (!sgp.begin()) { // fixed I2C address 0x59
Serial.println("SGP40 not found");
while (1) delay(10);
}
Serial.println("SGP40 ready: the VOC Index reads 0 for its first 45 s");
}
uint32_t lastSample = 0;
void loop() {
// Sensirion's VOC algorithm expects one sample a second. The library waits about 250 ms
// for each measurement, so time the loop with millis() rather than delay(1000).
if (millis() - lastSample < 1000) return;
lastSample = millis();
// Both calls take (temperature, humidity), in that order. measureVocIndex() measures and runs
// the algorithm: 100 is this air's recent average, higher means more VOCs than usual, lower fewer.
int32_t vocIndex = sgp.measureVocIndex(TEMPERATURE_C, HUMIDITY_RH);
Serial.print("VOC Index: ");
Serial.println(vocIndex);
}
SGP40 VOC Sensor on the ESP32 DevKit V1: questions
Which ESP32 pins does the SGP40 use?
SDA to GPIO21 and SCL to GPIO22, the default Wire pins of arduino-esp32's ESP32 variant (variants/esp32/pins_arduino.h). VIN goes to 3V3 and GND to GND.
Why does the SGP40 print SGP40 not found on my ESP32?
begin() reads the serial number at 0x59 and then runs the chip's self-test, which must answer 0xD400 (Adafruit_SGP40.cpp). Check that SDA and SCL are not swapped and that VIN is on 3V3.
Why does the VOC Index sit near 100 most of the time?
That is how the index works: 100 is the average of the air the sensor has been seeing, and values move above it when VOCs rise and below it when the air gets cleaner (Sensirion datasheet, p. 9).
Keep going
This guide as plain Markdown. /components/sgp40-voc-sensor/esp32.md