SGP30
A Sensirion metal-oxide gas sensor breakout that reports total VOC (TVOC) in ppb and an equivalent CO2 (eCO2) in ppm over I2C, for indoor air quality monitors.
- Chip supplyVDD and VDDH 1.62 V to 1.98 V (1.8 V typical)
- Absolute maximum-0.3 V to 2.16 V on VDD and VDDH
- Current48.8 mA in measurement mode
- I2C address0x58, fixed

The SGP30 is Sensirion's multi-pixel metal-oxide gas sensor: it outputs TVOC from 0 to 60,000 ppb and an equivalent CO2 (CO2eq) from 400 to 60,000 ppm once per second, calculated from its ethanol and H2 signals (Sensirion SGP30 datasheet v1.0, pp. 3 to 4). It answers at fixed I2C address 0x58 (p. 8). The chip needs 1.62 V to 1.98 V and draws 48.8 mA while measuring (p. 5), so use a breakout like Adafruit's with a 1.8 V regulator and level shifting (VIN 3 V to 5 V).
The SGP30 heats a small metal-oxide hotplate and measures how its resistance changes with reducing gases. From the raw ethanol and H2 signals, on-chip algorithms compute TVOC in ppb and CO2eq in ppm (Sensirion datasheet v1.0, p. 3). CO2eq is an estimate from VOCs, which track human presence; it is not an infrared CO2 measurement.
The air quality signals cover 0 to 60,000 ppb TVOC and 400 to 60,000 ppm CO2eq with resolution from 1 ppb and 1 ppm at the low end (p. 4). For the first 15 s after initialisation the sensor returns fixed values of 400 ppm and 0 ppb, and the dynamic baseline algorithm expects a measurement every second (p. 9). The baseline can be read out, stored and restored after a power cycle so readings do not restart from scratch (p. 9).
Electrically it is a 1.8 V part (1.62 V to 1.98 V, 2.16 V absolute maximum) whose hotplate draws 48.8 mA while measuring (pp. 5 to 6). Adafruit's breakout adds a 1.8 V regulator and level shifting so 3.3 V and 5 V boards can use it directly (Adafruit guide, pp. 5, 7).
SGP30 pinout
| Pin | Function | Type |
|---|---|---|
| VIN | Vin3 V to 5 V, equal to your board's logic level | power in |
| 1V8 | 1V81.8 V | power out |
| GND | GND | power in |
| SCL | I2C SCL3 V or 5 V logic | input |
| SDA | I2C SDA3 V or 5 V logic | bidirectional |
How to wire the SGP30
Pin by pin, for the board you have.
| SGP30 | Arduino Uno | Note |
|---|---|---|
| VIN | 5V | |
| GND | GND | |
| SCL | A5 | |
| SDA | A4 |
| SGP30 | ESP32 DevKit V1 | Note |
|---|---|---|
| VIN | 3V3 | |
| GND | GND | |
| SCL | GPIO22 (D22) | |
| SDA | GPIO21 (D21) |
- Power VIN from 3V3 so the level-shifted bus sits at 3.3 V; the hotplate's 48.8 mA (datasheet, p. 5) is well within the DevKit's 3.3 V regulator.
| SGP30 | Raspberry Pi Pico | Note |
|---|---|---|
| VIN | 3V3(OUT) | |
| GND | GND | |
| SCL | GP5 | |
| SDA | GP4 |
- Power VIN from 3V3(OUT), not VBUS.
SGP30 code
Every example here compiles for its board.
// SGP30 on Arduino Uno: wiring and a free simulation at https://www.tinkered.ai/components/sgp30
// SGP30 on an Arduino Uno: VIN 5V, SDA A4, SCL A5, address 0x58
#include <Wire.h>
#include <Adafruit_SGP30.h>
Adafruit_SGP30 sgp;
int counter = 0;
void setup() {
Serial.begin(115200);
Wire.begin();
if (!sgp.begin(&Wire)) {
Serial.println("SGP30 not found at 0x58. Check wiring.");
while (true) delay(10);
}
Serial.println("SGP30 ready; the first 15 s read 400 ppm and 0 ppb");
}
void loop() {
// the baseline algorithm expects a measurement every second
if (!sgp.IAQmeasure()) {
Serial.println("Measurement failed");
} else {
Serial.print("TVOC: ");
Serial.print(sgp.TVOC);
Serial.print(" ppb eCO2: ");
Serial.print(sgp.eCO2);
Serial.println(" ppm");
}
if (++counter >= 60) { // once a minute, show the baseline to save and restore later
counter = 0;
uint16_t eco2Base, tvocBase;
if (sgp.getIAQBaseline(&eco2Base, &tvocBase)) {
Serial.print("Baseline eCO2: 0x");
Serial.print(eco2Base, HEX);
Serial.print(" TVOC: 0x");
Serial.println(tvocBase, HEX);
}
}
delay(1000);
}
Compiled for arduino:avr:uno on 2026-10-30// SGP30 on ESP32 DevKit V1: wiring and a free simulation at https://www.tinkered.ai/components/sgp30
// SGP30 on an ESP32 DevKit V1: VIN 3V3, SDA GPIO21, SCL GPIO22, address 0x58
#include <Wire.h>
#include <Adafruit_SGP30.h>
Adafruit_SGP30 sgp;
int counter = 0;
void setup() {
Serial.begin(115200);
Wire.begin(21, 22); // SDA, SCL
if (!sgp.begin(&Wire)) {
Serial.println("SGP30 not found at 0x58. Check wiring.");
while (true) delay(10);
}
Serial.println("SGP30 ready; the first 15 s read 400 ppm and 0 ppb");
}
void loop() {
// the baseline algorithm expects a measurement every second
if (!sgp.IAQmeasure()) {
Serial.println("Measurement failed");
} else {
Serial.print("TVOC: ");
Serial.print(sgp.TVOC);
Serial.print(" ppb eCO2: ");
Serial.print(sgp.eCO2);
Serial.println(" ppm");
}
if (++counter >= 60) { // once a minute, show the baseline to save and restore later
counter = 0;
uint16_t eco2Base, tvocBase;
if (sgp.getIAQBaseline(&eco2Base, &tvocBase)) {
Serial.print("Baseline eCO2: 0x");
Serial.print(eco2Base, HEX);
Serial.print(" TVOC: 0x");
Serial.println(tvocBase, HEX);
}
}
delay(1000);
}
Compiled for esp32:esp32:esp32 on 2026-10-30// SGP30 on Raspberry Pi Pico: wiring and a free simulation at https://www.tinkered.ai/components/sgp30
// SGP30 on a Raspberry Pi Pico (arduino-pico): VIN 3V3(OUT), SDA GP4, SCL GP5, address 0x58
#include <Wire.h>
#include <Adafruit_SGP30.h>
Adafruit_SGP30 sgp;
int counter = 0;
void setup() {
Serial.begin(115200);
Wire.setSDA(4);
Wire.setSCL(5);
Wire.begin();
if (!sgp.begin(&Wire)) {
Serial.println("SGP30 not found at 0x58. Check wiring.");
while (true) delay(10);
}
Serial.println("SGP30 ready; the first 15 s read 400 ppm and 0 ppb");
}
void loop() {
// the baseline algorithm expects a measurement every second
if (!sgp.IAQmeasure()) {
Serial.println("Measurement failed");
} else {
Serial.print("TVOC: ");
Serial.print(sgp.TVOC);
Serial.print(" ppb eCO2: ");
Serial.print(sgp.eCO2);
Serial.println(" ppm");
}
if (++counter >= 60) { // once a minute, show the baseline to save and restore later
counter = 0;
uint16_t eco2Base, tvocBase;
if (sgp.getIAQBaseline(&eco2Base, &tvocBase)) {
Serial.print("Baseline eCO2: 0x");
Serial.print(eco2Base, HEX);
Serial.print(" TVOC: 0x");
Serial.println(tvocBase, HEX);
}
}
delay(1000);
}
Compiled for rp2040:rp2040:rpipico on 2026-10-30Before you wire it
- Call IAQmeasure() every second: the dynamic baseline algorithm depends on that rhythm (datasheet, p. 9).
- Ignore the first 15 s after start: the sensor returns 400 ppm and 0 ppb while it initialises (p. 9).
- Save the baseline regularly and restore it with setIAQBaseline() after power-up (p. 9).
- Use a regulated, level-shifted breakout: the chip is a 1.8 V part with a 2.16 V absolute maximum (pp. 5 to 6).
- Never treat eCO2 as a true CO2 reading; it is computed from VOC signals (p. 3). Use an NDIR sensor for CO2.
- Never put a bare SGP30 on 3.3 V; that is above its 2.16 V absolute maximum (p. 6).
- Never place it where silicone vapour or cleaning sprays collect without expecting the readings to swing.
- Humidity compensation improves accuracy: compute absolute humidity in mg/m³ from an SHT31 or BME280 and pass it to setHumidity().
- The hotplate makes the breakout warm; keep temperature and humidity sensors a few centimetres away.
- The 0x58 address is fixed (p. 8): one SGP30 per bus.
- Its 48.8 mA draw (p. 5) is too much for coin cells; plan on USB or a Li-ion cell.
Specifications
- Chip supply
- VDD and VDDH 1.62 V to 1.98 V (1.8 V typical)Sensirion SGP30 datasheet v1.0 (May 2020), p. 5
- Absolute maximum
- -0.3 V to 2.16 V on VDD and VDDHSensirion SGP30 datasheet v1.0 (May 2020), p. 6
- Current
- 48.8 mA in measurement modeSensirion SGP30 datasheet v1.0 (May 2020), p. 5
- I2C address
- 0x58, fixedSensirion SGP30 datasheet v1.0 (May 2020), p. 8
- TVOC
- 0 to 60,000 ppb; 1 ppb resolution up to 2008 ppbSensirion SGP30 datasheet v1.0 (May 2020), p. 4
- CO2eq
- 400 to 60,000 ppm; 1 ppm resolution up to 1479 ppmSensirion SGP30 datasheet v1.0 (May 2020), p. 4
- Sampling
- 1 Hz; first 15 s after init read 400 ppm and 0 ppbSensirion SGP30 datasheet v1.0 (May 2020), pp. 4, 9
- Operating conditions
- Recommended 5 °C to 55 °C and 4 to 30 g/m³ absolute humiditySensirion SGP30 datasheet v1.0 (May 2020), p. 4
- Breakout
- 1.8 V regulator and level shifting; VIN 3 V to 5 V; 1V8 output up to 50 mAAdafruit SGP30 guide (PDF), pp. 5, 7
From the SGP30 datasheet v1.0, published by Sensirion.
Common mistakes
eCO2 stuck at 400 and TVOC at 0
That is the 15 s initialisation phase (datasheet, p. 9), or a sketch that reinitialises the sensor in loop(). Call begin() once and IAQmeasure() every second.
Readings drift for days
The baseline algorithm needs time and a steady 1 Hz rhythm. Store the baseline once it settles and restore it at start-up (p. 9).
Comparing eCO2 with an NDIR meter
eCO2 is inferred from VOCs (p. 3). It rises with people in a room but does not follow real CO2 when VOC sources change.
Sharing the bus at 5 V without the breakout
Bare SGP30 boards without level shifting are 1.8 V parts. Use Adafruit's or another regulated board on 3.3 V and 5 V boards.
SGP30 questions
What is the SGP30's I2C address?
0x58, and it cannot be changed (Sensirion SGP30 datasheet v1.0, p. 8; Adafruit guide, p. 7).
Does the SGP30 measure real CO2?
No. CO2eq is calculated from ethanol and H2 signals (p. 3). For real CO2 use an NDIR sensor such as the SCD41 or MH-Z19B.
Why does it read 400 ppm and 0 ppb at start?
For the first 15 s after initialisation the sensor returns those fixed values (p. 9). Real readings follow, and the baseline takes longer to settle.
SGP30 or SGP40?
The SGP30 outputs TVOC and eCO2 numbers directly; the SGP40 outputs a raw signal that Sensirion's algorithm turns into a VOC index from 0 to 500. The SGP40 is the newer part.
Can it run on 3.3 V?
The chip runs at 1.8 V (1.62 V to 1.98 V, p. 5). The Adafruit breakout's regulator and level shifting let you power it from 3 V to 5 V (guide, p. 7).
Build something with the SGP30
- Build this
Indoor air quality display
Show TVOC and eCO2 with a colour bar.
- Build this
Smart vent trigger
Switch a fan when TVOC climbs.
- Build this
Baseline-saving logger
Store the SGP30 baseline in flash and restore it at boot.
This page as plain Markdown, for ChatGPT, Claude or your own agent. /components/sgp30.md