ESP32 MicroPython SCD40 CO2 Sensor
This tutorial instructs you how to use the SCD40 CO2 sensor with the ESP32 and MicroPython. In detail, we will learn:
- How to connect the SCD40 CO2 sensor module to a ESP32 over I2C.
- How to write MicroPython code for the ESP32 to read the CO2 level in ppm, using only the built-in machine.I2C (no extra library to install).
- How to tell if the air in your room is good or stuffy from the CO2 value.
- How to read the temperature and humidity that the same sensor also gives.

Hardware Preparation
Or you can buy the following kits:
| 1 | × | DIYables ESP32 Starter Kit (ESP32 included) | |
| 1 | × | DIYables ESP32 S3 Starter Kit (ESP32 S3 included) | |
| 1 | × | DIYables Sensor Kit (18 sensors/displays) |
Additionally, some of these links are for products from our own brand, DIYables .
Overview of SCD40 CO2 Sensor
When people stay in a closed room, the CO2 level climbs. High CO2 is a clear sign that the room needs fresh air. The SCD40 from Sensirion measures this gas for real. It does not guess from other gases, like the "eCO2" value of an MQ135 or a CCS811. How does it work? A tiny infrared light pulses inside a small chamber. CO2 takes in part of that light and makes a very small pressure wave, a bit like a sound. A tiny microphone picks it up. The more CO2 there is, the stronger the signal. This method is called photoacoustic NDIR.
| Item | SCD40 | |
|---|---|---|
| Supply voltage | 2.4V to 5.5V | |
| Interface | I2C, address 0x62 | |
| CO2 output range | 0 to 40,000 ppm | |
| CO2 accuracy | ±(50 ppm + 5% of reading), from 400 to 2,000 ppm | |
| New reading | every 5 seconds (low-power mode | every 30 seconds) |
| Temperature accuracy | ±0.8°C (15°C to 35°C) | |
| Humidity accuracy | ±6 %RH (15°C to 35°C, 20 to 65 %RH) | |
| Average current | about 15 mA at 3.3V |
Two things are good to know before you start:
- The first few readings after power-up can be less accurate. Give the sensor a little time.
- Automatic self-calibration (ASC) is ON by default. It thinks that the sensor sees fresh, outdoor-level air (about 400 ppm) at least once a week, best for an hour or more. So, now and then, open a window or put the sensor near one. If it never sees fresh air, its readings can slowly drift.
Pinout

The SCD40 module has four pins:
- VCC pin (also printed VIN): connect to 3.3V of the ESP32 (2.4 volts to 5.5 volts are accepted).
- GND pin: connect to GND (0 volts).
- SDA pin: the I2C data line. Connect to GPIO21 of the ESP32.
- SCL pin: the I2C clock line. Connect to GPIO22 of the ESP32.
Most SCD40 modules already have I2C pull-up resistors on board, so you do not need to add any.
What Does the CO2 Number Mean?
CO2 is shown in ppm (parts per million). Use this guide to read the value:
| CO2 level | Air quality | What to do |
|---|---|---|
| about 400 ppm | Outdoor air | Nothing, this is fresh air |
| 400 to 1000 ppm | Good indoor air | Nothing |
| 1000 to 2000 ppm | Stuffy | Open a window |
| above 2000 ppm | Poor | Get fresh air now, you may feel sleepy or get a headache |
Wiring Diagram
The ESP32 runs at 3.3 volts, and the SCD40 works fine at 3.3 volts too. We power it from the 3.3V pin, so the I2C lines also stay at 3.3 volts.
| SCD40 Pin | ESP32 Pin |
|---|---|
| VCC | 3.3V |
| GND | GND |
| SDA | GPIO21 |
| SCL | GPIO22 |
- How to connect ESP32 and SCD40 CO2 sensor using breadboard

This image is created using Fritzing. Click to enlarge image
- How to connect ESP32 and SCD40 CO2 sensor using screw terminal block breakout board

GPIO21 and GPIO22 are the usual I2C pins of the ESP32. You can move them to other GPIOs. If you do, change SDA_PIN and SCL_PIN at the top of the code.
ESP32 MicroPython Code - SCD40 CO2 Sensor
There is no SCD40 driver built into MicroPython, and we do not want to depend on a random file from the internet. The good news: the sensor needs only a few short commands, so the script talks to it directly with machine.I2C.
What the script does:
- It sends "stop periodic measurement" first. If an older script left the sensor running, this gives a clean start. Then it waits 500 ms.
- It sends "start periodic measurement". From now on, the sensor makes a new reading every 5 seconds.
- Every second, it asks "is new data ready?". If not, it waits again.
- When data is ready, it reads 9 bytes: CO2, temperature and humidity. Each value has 2 data bytes and 1 check byte (CRC). If a check byte is wrong, the reading is skipped.
- It prints the CO2 value with a short air-quality word: Good, Moderate or Poor.
Detailed Instructions
Here's instructions on how to set up and run your MicroPython code on the ESP32 using Thonny IDE:
- Make sure Thonny IDE is installed on your computer.
- Confirm that MicroPython firmware is loaded on your ESP32 board.
- If you have never used MicroPython on the ESP32 before, follow the ESP32 MicroPython Getting Started guide.
- Connect the SCD40 module to the ESP32 board according to the provided diagram.
- Connect the ESP32 board to your computer with a USB cable.
- Open Thonny IDE on your computer.
- In Thonny, go to Tools Options, open the Interpreter tab, and choose MicroPython (ESP32) from the dropdown.
- Copy the provided MicroPython code and paste it into Thonny's editor.
- Save the code to your ESP32 by:
- Clicking the Save button or pressing Ctrl+S.
- Choosing MicroPython device in the save dialog.
- Typing main.py as the file name.
- Click the green Run button (or press F5) to execute the script.
- Wait about 5 seconds for the first value. Then breathe gently toward the sensor a few times and watch the number go up.
- Check out the message in the Shell at the bottom of Thonny.
The value falls back slowly after you stop breathing on it, because the CO2 must leave the small chamber first.
Got OSError: [Errno 116] ETIMEDOUT or [Errno 19] ENODEV? The ESP32 cannot find the sensor. Check the four wires, and make sure SDA goes to GPIO21 and SCL goes to GPIO22, not the other way round.
Reading Temperature and Humidity from the SCD40
The SCD40 also has a temperature and humidity sensor inside. It uses them to correct the CO2 value, but you can read them too. In fact, the first script already receives them. They are the second and third values in the same 9 bytes. It just does not print them.
To turn the raw numbers into real units, use these formulas:
- Temperature (°C) = -45 + 175 × raw value ÷ 65535
- Humidity (%) = 100 × raw value ÷ 65535
The script below is the same as before, but it adds these two formulas and prints all three values on one line.
ESP32 MicroPython Code - CO2, Temperature and Humidity
Detailed Instructions
- Copy the code above and paste it into Thonny's editor.
- Save it to your ESP32 as main.py.
- Click the green Run button (or press F5).
- Check out the message in the Shell at the bottom of Thonny.
If you name your script main.py and save it to the MicroPython device, it will run automatically every time the ESP32 is powered on.
The temperature reads a bit higher than your room thermometer? The sensor warms itself a little while it runs, and so does a nearby ESP32. Keep a few centimeters of space between the two boards for a better value.