Indoor air quality is one of the most overlooked factors in health, productivity, and cognitive function. Studies show that elevated CO2 levels — above 1000 ppm — impair decision-making by up to 21%, and levels above 2500 ppm cause significant cognitive decline. The problem is invisible: you cannot see, smell, or taste CO2 until levels become dangerous. This is where the SCD40 CO2 sensor comes in.
The Sensirion SCD40 is a miniaturised, photoacoustic carbon dioxide sensor that delivers highly accurate CO2 measurements alongside temperature and humidity readings, all via a simple I2C interface. In this comprehensive guide, we cover everything about the SCD40 — from how it works and wiring it to Arduino, to advanced calibration techniques and building a complete indoor air quality monitor.
1. Why Measure CO2 Indoors?
The outdoor atmosphere contains approximately 420 ppm (parts per million) of CO2. When people are present in enclosed spaces, CO2 levels rise rapidly as we exhale carbon dioxide. A closed classroom with 30 students can reach 3000 ppm within an hour. Home offices, bedrooms, and meeting rooms all accumulate CO2 when poorly ventilated.
Elevated indoor CO2 causes:
- Cognitive impairment: Measurable decline in focus, decision-making, and complex task performance above 1000 ppm
- Fatigue and drowsiness: The classic “afternoon slump” in offices is often CO2-driven
- Headaches and discomfort: Common above 2000 ppm
- Indicator of viral risk: CO2 level is a reliable proxy for air recirculation rate — high CO2 means poor dilution of potentially virus-laden air
CO2 monitoring became especially relevant post-pandemic as a way to assess ventilation adequacy in public spaces. In industrial settings, CO2 monitoring is used to detect leaks from compressed gas systems and dry ice storage. In agriculture, CO2 enrichment of greenhouse air (up to 1500 ppm) increases crop yields — requiring accurate CO2 measurement and control.
2. SCD40 Overview and Specifications
The Sensirion SCD40 represents a generational leap in compact CO2 sensing technology. It uses Sensirion’s proprietary PASens® (Photoacoustic Sensing) technology to miniaturise what was previously a large, power-hungry NDIR (Non-Dispersive Infrared) sensor into a tiny 2.4 × 2.9 × 1.4 mm package.
| Parameter | Specification |
|---|---|
| CO2 Measurement Range | 400 – 2000 ppm |
| CO2 Accuracy | ±50 ppm + 5% of reading |
| Temperature Range | -10°C to 60°C |
| Temperature Accuracy | ±0.8°C (typ) |
| Humidity Range | 0 – 100% RH |
| Humidity Accuracy | ±6% RH (typ) |
| Interface | I2C (address 0x62) |
| Supply Voltage | 2.4V – 5.5V |
| Average Current (5s interval) | 1.4 mA |
| Peak Current | 175 mA |
| Single-shot mode avg current | ~0.4 mA (1 per 5 min) |
| Warm-up Time | Immediate (photoacoustic, no warm-up) |
| Dimensions | 10.1 × 33.5 × 6.5 mm (breakout board) |
SCD40 vs SCD41
Sensirion also makes the SCD41, which extends the CO2 range to 400–5000 ppm — making it more suitable for greenhouse CO2 enrichment, storage monitoring, and industrial environments where levels can exceed 2000 ppm. The SCD40 is ideal for indoor air quality monitoring where 2000 ppm represents the extreme upper boundary of typical conditions.
3. How Photoacoustic Sensing Works
Traditional CO2 sensors use Non-Dispersive Infrared (NDIR) technology: infrared light passes through a sample chamber, and CO2 molecules absorb specific IR wavelengths. The reduction in transmitted IR intensity is proportional to CO2 concentration. This works well but requires a light source, a long optical path, and a photodetector — making traditional NDIR sensors relatively large and expensive.
Photoacoustic NDIR (PASens)
The SCD40 uses photoacoustic sensing, a variant of NDIR. Instead of measuring transmitted light, the sensor uses a modulated infrared light source that causes CO2 molecules to periodically absorb energy and heat up, then cool, expanding and contracting. This creates pressure waves — essentially sound — at the modulation frequency. A MEMS microphone inside the sensor detects these pressure waves.
Key advantages of photoacoustic sensing:
- No optical path length requirement — the sensor can be miniaturised to millimetre scale
- No reference channel needed — dramatically simplifies the optical design
- Self-calibrating for zero drift — the signal goes to zero when no CO2 is present, unlike NDIR where zero drift accumulates
- Faster response time compared to electrochemical CO2 sensors
4. SCD40 vs Other CO2 Sensors
| Sensor | Technology | Accuracy | Range | Cost |
|---|---|---|---|---|
| SCD40 | Photoacoustic NDIR | ±50 ppm | 400–2000 ppm | Medium-High |
| MH-Z19B | NDIR | ±50 ppm | 400–5000 ppm | Medium |
| CCS811 | MOX (eCO2) | ±15% (eCO2) | 400–8192 ppm | Low |
| MQ-135 | Electrochemical | Poor (±30%) | Variable | Very Low |
| SCD30 | NDIR (dual channel) | ±30 ppm | 400–10000 ppm | High |
Important note on CCS811 and MQ-135: These sensors do not actually measure CO2 directly. The CCS811 measures total VOC (volatile organic compounds) and derives an “equivalent CO2” estimate — it is not a substitute for true CO2 measurement and does not detect CO2 from human respiration accurately. The MQ-135 is similarly a multi-gas sensor with poor CO2 selectivity. For genuine CO2 monitoring, use NDIR or photoacoustic sensors like the SCD40.
MQ-135 Air Quality / Gas Detector Sensor Module
A versatile multi-gas sensor for general air quality indication — complements dedicated CO2 sensors by detecting ammonia, benzene, smoke, and other gases.
5. Wiring SCD40 to Arduino / ESP32
The SCD40 operates on I2C with a fixed address of 0x62. It accepts supply voltages from 2.4V to 5.5V, making it directly compatible with both 3.3V and 5V systems.
Wiring to Arduino Uno (5V)
| SCD40 Pin | Arduino Uno Pin |
|---|---|
| VDD | 5V |
| GND | GND |
| SDA | A4 (SDA) |
| SCL | A5 (SCL) |
| SEL | Leave floating or connect to GND (I2C mode) |
Wiring to ESP32 (3.3V)
| SCD40 Pin | ESP32 Pin |
|---|---|
| VDD | 3.3V |
| GND | GND |
| SDA | GPIO 21 |
| SCL | GPIO 22 |
Note on peak current: The SCD40 draws up to 175 mA during measurement pulses. Always power it from the main power supply, not from a microcontroller’s 3.3V regulator output (which is often limited to 50-100 mA). Add a 10 µF electrolytic + 100 nF ceramic decoupling capacitor near the SCD40’s VDD pin.
6. Library Installation
Sensirion provides an official Arduino library for the SCD40:
- Open Arduino IDE → Sketch → Include Library → Manage Libraries
- Search for
Sensirion I2C SCD4x - Install Sensirion I2C SCD4x by Sensirion
- Also install the dependency: Sensirion Core
Alternatively, for ESP32/ESP8266 using PlatformIO, add to platformio.ini:
lib_deps =
sensirion/Sensirion I2C SCD4x@^0.4.0
7. Basic Measurement Code
Continuous Measurement Mode
#include <Arduino.h>
#include <SensirionI2CScd4x.h>
#include <Wire.h>
SensirionI2CScd4x scd4x;
void setup() {
Serial.begin(115200);
Wire.begin();
uint16_t error;
char errorMessage[256];
scd4x.begin(Wire);
// Stop any previous measurement before starting
error = scd4x.stopPeriodicMeasurement();
if (error) {
errorToString(error, errorMessage, 256);
Serial.print("Error stopping measurement: ");
Serial.println(errorMessage);
}
// Start periodic measurement (every 5 seconds)
error = scd4x.startPeriodicMeasurement();
if (error) {
errorToString(error, errorMessage, 256);
Serial.print("Error starting measurement: ");
Serial.println(errorMessage);
}
Serial.println("Waiting for first measurement...");
delay(5000); // Wait for first sample
}
void loop() {
uint16_t error;
char errorMessage[256];
bool isDataReady = false;
// Check if new data is available
error = scd4x.getDataReadyFlag(isDataReady);
if (error) {
errorToString(error, errorMessage, 256);
Serial.print("Error checking data ready: ");
Serial.println(errorMessage);
return;
}
if (!isDataReady) {
delay(100);
return;
}
uint16_t co2;
float temperature, humidity;
error = scd4x.readMeasurement(co2, temperature, humidity);
if (error) {
errorToString(error, errorMessage, 256);
Serial.print("Error reading measurement: ");
Serial.println(errorMessage);
return;
}
if (co2 == 0) {
Serial.println("Invalid sample (co2 = 0)");
return;
}
Serial.print("CO2: "); Serial.print(co2); Serial.print(" ppmt");
Serial.print("Temp: "); Serial.print(temperature, 1); Serial.print(" °Ct");
Serial.print("RH: "); Serial.print(humidity, 1); Serial.println("%");
delay(100);
}
8. Sensor Calibration
The SCD40 includes two calibration mechanisms that are critical for accurate long-term measurements.
Automatic Self-Calibration (ASC)
The SCD40 continuously observes its own CO2 readings and assumes that the minimum value observed over any 7-day period is fresh outdoor air at approximately 400 ppm. It automatically adjusts its calibration accordingly. ASC is enabled by default and works well when:
- The sensor sees outdoor-level CO2 for at least 1 hour every day
- It operates for at least 7 consecutive days
Disable ASC if the sensor is permanently in a sealed environment (controlled atmosphere research, submarine, etc.) where it never experiences outdoor air:
scd4x.setAutomaticSelfCalibration(false);
Forced Recalibration (FRC)
Forced recalibration is used when you want immediate, precise calibration to a known CO2 reference:
// Step 1: Run sensor continuously for 3+ minutes in known CO2 atmosphere
// Step 2: Stop periodic measurement
scd4x.stopPeriodicMeasurement();
delay(500);
// Step 3: Execute FRC with known CO2 concentration (e.g., 400 ppm outdoor air)
uint16_t targetCO2 = 400; // ppm
uint16_t frcCorrection;
uint16_t error = scd4x.performForcedRecalibration(targetCO2, frcCorrection);
if (frcCorrection == 0xFFFF) {
Serial.println("FRC failed — ensure sensor ran for 3+ minutes first");
} else {
int16_t correctionPpm = (int16_t)frcCorrection - 0x8000;
Serial.print("FRC correction applied: ");
Serial.print(correctionPpm);
Serial.println(" ppm");
}
Temperature Offset
The SCD40 measures temperature internally. Heat from nearby components can bias the temperature (and consequently humidity) readings. Set a temperature offset to correct this:
// Set +4°C offset if the enclosure raises temp by 4°C
scd4x.setTemperatureOffset(4.0);
9. Building a Complete Air Quality Monitor
Here is a complete indoor air quality monitor using the SCD40 with colour-coded LED indication and OLED display:
#include <Wire.h>
#include <SensirionI2CScd4x.h>
#include <Adafruit_SSD1306.h>
SensirionI2CScd4x scd4x;
Adafruit_SSD1306 display(128, 64, &Wire, -1);
// RGB LED pins (common cathode)
const int LED_R = 9;
const int LED_G = 10;
const int LED_B = 11;
void setLED(int r, int g, int b) {
analogWrite(LED_R, r);
analogWrite(LED_G, g);
analogWrite(LED_B, b);
}
void updateAirQualityIndicator(uint16_t co2) {
if (co2 < 800) setLED(0, 255, 0); // Green: Good
else if (co2 < 1200) setLED(255, 200, 0); // Yellow: Moderate
else if (co2 < 2000) setLED(255, 80, 0); // Orange: Poor
else setLED(255, 0, 0); // Red: Very Poor
}
void setup() {
Wire.begin();
Serial.begin(115200);
pinMode(LED_R, OUTPUT);
pinMode(LED_G, OUTPUT);
pinMode(LED_B, OUTPUT);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
scd4x.begin(Wire);
scd4x.stopPeriodicMeasurement();
scd4x.startPeriodicMeasurement();
delay(5000);
}
void loop() {
bool dataReady = false;
scd4x.getDataReadyFlag(dataReady);
if (!dataReady) { delay(100); return; }
uint16_t co2;
float temp, hum;
scd4x.readMeasurement(co2, temp, hum);
// Update LED
updateAirQualityIndicator(co2);
// Update display
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0, 0);
display.println("Indoor Air Quality");
display.drawLine(0, 10, 128, 10, SSD1306_WHITE);
display.setTextSize(2);
display.setCursor(0, 14);
display.print(co2);
display.setTextSize(1);
display.println(" ppm CO2");
display.setTextSize(1);
display.setCursor(0, 42);
display.print("Temp: "); display.print(temp, 1); display.println(" C");
display.print("RH: "); display.print(hum, 0); display.println("%");
String quality = co2 < 800 ? "GOOD" : co2 < 1200 ? "MODERATE" : co2 < 2000 ? "POOR" : "VERY POOR";
display.setCursor(80, 42);
display.println(quality);
display.display();
delay(100);
}
10. Understanding CO2 Level Thresholds
| CO2 Level (ppm) | Classification | Health Impact | Action |
|---|---|---|---|
| < 400 | Outdoor Fresh Air | None | Ideal — no action needed |
| 400–800 | Excellent | None detectable | Well-ventilated space |
| 800–1200 | Good | Minor drowsiness possible | Consider increasing ventilation |
| 1200–2000 | Moderate–Poor | Cognitive impairment begins | Open windows, increase airflow |
| 2000–5000 | Very Poor | Headaches, fatigue, loss of focus | Ventilate immediately |
| > 5000 | Dangerous | OSHA workplace limit exceeded | Evacuate, investigate source |
11. Low-Power Operation
The SCD40 supports a single-shot measurement mode ideal for battery-powered IoT devices. Instead of continuously measuring every 5 seconds, the sensor wakes up, takes one measurement, and returns to sleep:
// Measure once then go idle (for deep-sleep IoT applications)
void takeSingleMeasurement() {
scd4x.stopPeriodicMeasurement();
delay(500);
scd4x.measureSingleShot();
delay(5000); // Wait for measurement
uint16_t co2;
float temp, hum;
scd4x.readMeasurement(co2, temp, hum);
// Transmit data via LoRa/MQTT/BLE
transmitData(co2, temp, hum);
// Power down ESP32 for 5 minutes
esp_sleep_enable_timer_wakeup(5 * 60 * 1000000ULL);
esp_deep_sleep_start();
}
12. Applications and Use Cases
School Classroom Air Quality Monitor
Install an SCD40 with a NodeMCU and a large LED display visible from the back of the classroom. Teachers can see at a glance when ventilation is needed. Studies in Denmark and elsewhere show that reducing classroom CO2 below 900 ppm improves student performance measurably.
Smart Home Ventilation Control
Integrate the SCD40 with Home Assistant or Node-RED. When CO2 exceeds 1000 ppm, automatically activate an exhaust fan or open smart ventilation dampers. The system can also integrate with air purifiers and provide occupancy estimates based on CO2 rise rate.
Greenhouse CO2 Enrichment
Use the SCD41 (extended range) to maintain optimal CO2 levels for plant growth in controlled-environment agriculture. Most crops reach peak photosynthesis between 1200–1500 ppm. The system controls a CO2 release valve to maintain the target concentration.
DHT11 Digital Relative Humidity and Temperature Sensor Module
Add a DHT11 as a secondary temperature/humidity sensor for cross-validation and redundancy alongside your SCD40 CO2 monitor.
GY-BME280-5V Temperature and Humidity Sensor
Combine the BME280 with your SCD40 for a comprehensive environmental monitoring station covering CO2, temperature, humidity, and atmospheric pressure.
Frequently Asked Questions
How long does the SCD40 take to warm up before giving accurate readings?
Unlike traditional NDIR sensors that require 3-5 minute warm-up times, the SCD40 gives its first measurement within 5 seconds of starting periodic measurement mode. The photoacoustic sensing technology used by Sensirion does not require a warm-up period. However, for the auto-calibration to stabilise after initial deployment, the sensor should run continuously for at least 7 days to establish a reliable calibration baseline.
Can the SCD40 be used outdoors?
The SCD40 can operate outdoors in terms of its measurement range and temperature tolerance (-10°C to 60°C). However, the sensor itself is not weatherproof — it must be protected from direct rain, condensation, and prolonged exposure to high humidity (above 95% RH). Mount it in a weather-resistant enclosure with a Gore-Tex membrane or similar breathable waterproof cover that allows air exchange while keeping moisture away from the electronics.
Why does my SCD40 read 400 ppm even in a closed room with people?
This is the automatic self-calibration (ASC) algorithm setting a floor at 400 ppm. If the sensor has not yet completed its initial 7-day calibration period, or if it is regularly placed in fresh outdoor air (or near a window), ASC may incorrectly calibrate to 400 ppm. To check, disable ASC with setAutomaticSelfCalibration(false) and perform a forced recalibration. Also verify the I2C communication is working correctly — check the error return values from readMeasurement().
Is the SCD40 the same as the SCD30?
No. The SCD30 is a larger, older sensor using dual-channel NDIR technology with ±30 ppm accuracy and a range up to 10,000 ppm. The SCD40 uses newer photoacoustic sensing, is much smaller (2.4 × 2.9 mm chip vs much larger SCD30 module), consumes less power, but has slightly lower accuracy (±50 ppm) and a narrower range (400-2000 ppm). The SCD40 is the modern successor optimised for miniaturised IoT and consumer applications.
Does altitude affect SCD40 CO2 readings?
Yes. At higher altitudes, lower atmospheric pressure affects photoacoustic sensing performance. The SCD40 supports ambient pressure compensation — you can set the current atmospheric pressure using setAmbientPressure() (in Pa). This is especially important at altitudes above 1000 m above sea level. In India, cities like Bangalore (920 m), Shimla (2206 m), and Leh (3524 m) would benefit from altitude compensation for accurate CO2 readings. Use a BMP280 pressure sensor to measure ambient pressure and feed it to the SCD40 for automatic compensation.
Monitor Your Indoor Air Quality Today
Find CO2 sensors, Arduino boards, and all components for your air quality monitoring project at Zbotic — trusted by makers across India.
Add comment