CO₂ Sensor Project Using Sensirion SCD30

Written by Dikshanya Ramaswamy, Nicholas Ricci and Ao Ruan

1. Introduction

This project focuses on the integration and testing of the Sensirion SCD30 sensor module for measuring carbon dioxide (CO₂) concentration, temperature, and humidity. The SCD30 is a digital sensor that communicates over the I²C protocol and is factory-calibrated for accurate measurements under controlled environmental conditions. It provides continuous and reliable data, making it well-suited for environmental monitoring applications where real-time readings are crucial. The sensor delivers processed digital outputs to minimize on-device computation and offers flexibility for users to conduct their own post-processing and calibration adjustments as needed. Careful handling is required, particularly during the sensor's initial power-up period, during which readings may momentarily fluctuate before stabilizing. Preliminary lab testing (in a stable indoor environment) has shown CO₂ readings ranging from approximately 1000 ppm to over 3000 ppm under conditions of forced air exposure. Temperature and humidity measurements have remained consistent, typically around 25 °C and 45–50% relative humidity, respectively. At present, the sensor interfacing and data logging are combined within a unified system design. Future work will aim to modularize the codebase, separating the SCD30 interface into a standalone library for easier integration into broader data collection frameworks.

2. Hardware Setup

The sensor communicates via I²C and requires 3.3V power. Below is the circuit diagram showing how the SCD30 is connected.

Circuit Diagram for SCD30 setup

3. Software Implementation

For the software implementation the below table shows all the I2C commands required to communicate with the sensor.
Command / Action Read/Write Notes
SCAN Read Scan devices on the I2C bus
CONTINUOUS MEASUREMENT Write Start continuous CO₂ measurement
SET MEASUREMENT INTERVAL Write Set measurement interval (2s)
AUTOMATIC SELF CALIBRATION Write Enable automatic self-calibration
READY SIGNAL Read Check if data is ready (RDY pin high)
READ_MEASUREMENT Write - Read Tell sensor to prepare measurement data and reads it

  • Python Code:
  • Fully working, reads CO₂, temperature, and humidity.

    This project uses a MicroPython script running on the Raspberry Pi Pico to read data from the Sensirion SCD30 sensor over I²C. Below is a breakdown of the code logic and key commands used:

    
            # Import required modules
            from machine import Pin, I2C     # Access GPIO pins and I2C
            import struct                   # For binary data unpacking
            
            # Unpack 4 bytes into a float (big endian)
            def unpack_scd30_float(b0, b1, b2, b3):
                tmp = bytearray(4)
                tmp[0], tmp[1], tmp[2], tmp[3] = b0, b1, b2, b3
                return struct.unpack('>f', tmp)   # Returns a float from 4 bytes
            
            # Initialize I2C on GP12 (SDA) and GP13 (SCL) at 100 kHz
            i2c = I2C(0, scl=Pin(13), sda=Pin(12), freq=100000)
            i2c.scan()   # Scan for devices on the bus (for debugging)
            
            # RDY pin to check when data is ready
            rdy = Pin(7, Pin.IN)
            
            # SCD30 I2C address (7-bit)
            SCD30_ADR = 97
            
            # Command bytes (based on SCD30 datasheet)
            COMMAND_CONTINUOUS_MEASUREMENT = b'\x00\x10\x00\x00\x00'
            COMMAND_SET_MEASUREMENT_INTERVAL = b'\x46\x00\x00\x02\x00'  # Set 2s interval
            COMMAND_AUTOMATIC_SELF_CALIBRATION = b'\x53\x06\x00\x01\x00'
            COMMAND_READ_MEASUREMENT = b'\x03\x00'
            
            # Initialize sensor with above commands
            i2c.writeto(SCD30_ADR, COMMAND_CONTINUOUS_MEASUREMENT)
            i2c.writeto(SCD30_ADR, COMMAND_SET_MEASUREMENT_INTERVAL)
            i2c.writeto(SCD30_ADR, COMMAND_AUTOMATIC_SELF_CALIBRATION)
            
            # Main data reading loop
            while True:
                if rdy.value() == 1:  # If RDY pin is high
                    i2c.writeto(SCD30_ADR, COMMAND_READ_MEASUREMENT)
                    buf = i2c.readfrom(SCD30_ADR, 18)  # Read 18 bytes of data
                    
                    # Extract float values from buffer
                    co2 = unpack_scd30_float(buf[0], buf[1], buf[3], buf[4])
                    temp = unpack_scd30_float(buf[6], buf[7], buf[9], buf[10])
                    humi = unpack_scd30_float(buf[12], buf[13], buf[15], buf[16])
                    
                    # Print values (you can format these further)
                    print("CO2:", co2[0], "ppm")
                    print("Temp:", temp[0], "°C")
                    print("Humidity:", humi[0], "%RH")
            

    The above code is based on Original github code
    You can find the full working script on GitHub: https://github.com/Dikshanya-Ram/Sensors

  • C Code:
  • *Partially working, still under debugging for data parsing.

    Sensor Initialization and Configuration

    Data Handling and Processing

    Main Function (main)

    https://github.com/Dikshanya-Ram/Sensors

    4. Results

    Reading CO₂ (ppm) Temperature (°C) Humidity (%)
    1 (closed lab) 1056.30 25.00 47.53
    2 (closed lab) 1054.00 24.99 47.78
    3 (closed lab) 1044.93 24.99 46.58
    4 (when tested with blown air) 2534.34 25.20 50.10
    5 (continuation) 3450.12 25.12 49.59

    5. Conclusion/Future Work

    The SCD30 provides accurate and reliable environmental data. With working Python code and progress on C implementation, this setup is a solid base for real-time air quality monitoring. We also would like to integrate all the electronic components and fabricate the PCB design shown in the image below. We would then have to provide the battery backed power integration to this system and the motor system developed.

    PCB Design for the entire sysem

    6. References