Community project

Je Veux Cr Er Un Objet Qui Absorbe

Studio Horizon Digital

Published July 22, 2026

ESP328 components5 assembly steps
Remix this project
Photo of Je Veux Cr Er Un Objet Qui Absorbe

This CO2 monitoring and fan control system uses an ESP32 to read air quality data from an MH-Z19B sensor and automatically activate a ventilation fan when carbon dioxide levels exceed safe thresholds. The system displays real-time CO2 measurements on an SSD1306 OLED screen and uses an RGB LED to provide visual feedback about air quality status—green for good air, yellow for warning, and red for poor conditions.

The guide includes a complete wiring diagram, parts list, and step-by-step assembly instructions for connecting the CO2 sensor, OLED display, RGB LED with current-limiting resistors, and relay-controlled fan to the ESP32. Firmware is provided to handle sensor readings, threshold-based fan automation, display updates, and color-coded LED indicators, making this an ideal project for monitoring indoor air quality in offices, classrooms, or living spaces.

Wiring diagram

Interactive · read-only
Wiring diagram for Je Veux Cr Er Un Objet Qui Absorbe

Pan and zoom to explore the wiring. Remix the project to edit it in your own workspace.

Parts list

Bill of materials
ComponentQtyNotes
MH-Z19B CO2 Sensor1NDIR CO2 sensor, 400–5000 ppm range, UART interface at 9600 baud, 5V powered, 3.3V-compatible TX/RX logic
SSD1306 OLED10.96 inch 128x64 OLED display with I2C interface
Relay Module1Single channel relay module
Rgb Led1Discrete four-pin common-cathode RGB LED with separate red, green, and blue anodes. Each colour channel needs its own current-limit resistor and can be PWM-dimmed from separate MCU GPIO pins.
Resistor 100Ω100 Ω1100 ohm current-limiting resistor for RGB LED Red channel
Resistor 100Ω100 Ω1100 ohm current-limiting resistor for RGB LED Green channel
Resistor 100Ω100 Ω1100 ohm current-limiting resistor for RGB LED Blue channel
5V DC Fan1Small 5V DC brushless fan (40mm or 50mm). Switched via relay. Pulls CO2-rich air through the soda lime capture cartridge.

Assembly

5 steps
  1. Power off everything

    Make sure the ESP32 is unplugged from USB before wiring anything.

    • Never wire components while the board is powered.
  2. Wire the MH-Z19B CO₂ sensor

    Connect MH-Z19B VIN → ESP32 5V, GND → GND, TXD → GPIO16, RXD → GPIO17. The sensor UART logic is 3.3V — no level shifter needed for RX/TX.

    • Tip: Allow 3 minutes warm-up after first power-on before readings are valid.
  3. Wire the SSD1306 OLED display

    Connect OLED VCC → ESP32 3.3V, GND → GND, SDA → GPIO21, SCL → GPIO22.

  4. Wire the RGB LED with resistors

    Insert three 100Ω resistors in series on each color channel: GPIO25 → R1 → LED-R, GPIO27 → R2 → LED-G, GPIO14 → R3 → LED-B. Connect LED GND (common cathode) to GND.

    • Tip: The long leg of each color pin is the anode; the single short leg is the common cathode.
  5. Wire the relay and fan

    Connect relay VCC → 5V, GND → GND, IN → GPIO26. Then wire the 5V fan's positive lead to the relay's COM terminal and the relay's NO (Normally Open) terminal to 5V. Fan negative → GND. When GPIO26 goes LOW, the relay closes and the fan runs.

    • Keep relay wiring away from the breadboard signal area.
    • The fan must be 5V rated — do not use a 12V fan here.

Pin assignments

Board wiring reference
PinConnectionType
5Vco2_sensor VINpower
GNDco2_sensor GNDground
GPIO 16co2_sensor TXuart
GPIO 17co2_sensor RXuart
3V3oled VCCpower
GNDoled GNDground
GPIO 21oled SDAi2c
GPIO 22oled SCLi2c
5Vrelay VCCpower
GNDrelay GNDground
GPIO 26relay INdigital
GPIO 25res_r Adata
EXTres_r BRgb Led Rdata
GPIO 27res_g Adata
EXTres_g BRgb Led Gdata
GPIO 14res_b Adata
EXTres_b BRgb Led Bdata
GNDrgb_led GNDground
EXTfan VCCRelay Module VCCpower
GNDfan GNDground

Firmware

ESP32
main.cppDeploy to device
#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <MHZ19.h>
#include <HardwareSerial.h>

// Pin definitions
#define CO2_RX_PIN   16   // ESP32 RX2 ← MH-Z19B TX
#define CO2_TX_PIN   17   // ESP32 TX2 → MH-Z19B RX
#define RELAY_PIN    26   // Relay IN (LOW = fan ON)
#define LED_R_PIN    25
#define LED_G_PIN    27
#define LED_B_PIN    14

// Thresholds (ppm)
#define CO2_GOOD     800
#define CO2_WARN    1200
#define CO2_BAD     1600

// OLED
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET   -1

// Forward declarations
void setLED(uint8_t r, uint8_t g, uint8_t b);
void updateDisplay(int co2ppm, bool fan);

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// CO2 sensor
HardwareSerial co2Serial(2);
MHZ19 mhz19;

// State
int  lastCO2     = -1;
bool fanOn       = false;
unsigned long lastRead = 0;
const unsigned long READ_INTERVAL = 5000; // ms

void setLED(uint8_t r, uint8_t g, uint8_t b) {
  analogWrite(LED_R_PIN, r);
  analogWrite(LED_G_PIN, g);
  analogWrite(LED_B_PIN, b);
}

void updateDisplay(int co2ppm, bool fan) {
  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);

  // Title
  display.setTextSize(1);
  display.setCursor(0, 0);
  display.print("CO2 Capture System");
  display.drawLine(0, 10, 127, 10, SSD1306_WHITE);

  // CO2 value
  display.setTextSize(2);
  display.setCursor(0, 16);
  if (co2ppm > 0) {
    display.print(co2ppm);
    display.setTextSize(1);
    display.print(" ppm");
  } else {
    display.print("Warmup...");
  }

  // Status
  display.setTextSize(1);
  display.setCursor(0, 40);
  if (co2ppm <= 0)        display.print("Status: WARMING UP");
  else if (co2ppm < CO2_GOOD)  display.print("Status: GOOD  ");
  else if (co2ppm < CO2_WARN)  display.print("Status: MODERATE");
  else if (co2ppm < CO2_BAD)   display.print("Status: HIGH   ");
  else                         display.print("Status: DANGER !");

  // Fan
  display.setCursor(0, 52);
  display.print("Fan: ");
  display.print(fan ? "ON  (capturing)" : "OFF");

  display.display();
}

void setup() {
  Serial.begin(115200);

  // LED pins
  pinMode(LED_R_PIN, OUTPUT);
  pinMode(LED_G_PIN, OUTPUT);
  pinMode(LED_B_PIN, OUTPUT);

  // Relay
  pinMode(RELAY_PIN, OUTPUT);
  digitalWrite(RELAY_PIN, HIGH); // relay inactive (active LOW module)

  // OLED
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println("SSD1306 not found");
  }
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(10, 20);
  display.print("CO2 Capture System");
  display.setCursor(20, 36);
  display.print("Warming up 3 min");
  display.display();

  // CO2 sensor on UART2
  co2Serial.begin(9600, SERIAL_8N1, CO2_RX_PIN, CO2_TX_PIN);
  mhz19.begin(co2Serial);
  mhz19.autoCalibration(false);

  setLED(0, 0, 30); // dim blue during warmup
  Serial.println("MH-Z19B warming up (3 minutes needed)...");
}

void loop() {
  unsigned long now = millis();

  if (now - lastRead >= READ_INTERVAL) {
    lastRead = now;
    int co2 = mhz19.getCO2();

    if (co2 > 0) {
      // Fan logic
      bool shouldFanOn = (co2 >= CO2_WARN);
      if (shouldFanOn != fanOn) {
        fanOn = shouldFanOn;
        digitalWrite(RELAY_PIN, fanOn ? LOW : HIGH);
      }

      // LED color
      if (co2 < CO2_GOOD)       setLED(0, 255, 0);    // green
      else if (co2 < CO2_WARN)  setLED(255, 165, 0);  // orange
      else if (co2 < CO2_BAD)   setLED(255, 50, 0);   // red-orange
      else                      setLED(255, 0, 0);     // red

      if (co2 != lastCO2) {
        lastCO2 = co2;
        updateDisplay(co2, fanOn);
        Serial.printf("CO2: %d ppm  Fan: %s\n", co2, fanOn ? "ON" : "OFF");
      }
    } else {
      // Still warming up
      updateDisplay(-1, false);
    }
  }
}

“Deploy to device” opens this project in Schematik, where you can flash it to your board over USB.

Remix this project

Make it yours in one click

Open a full copy of this project in your own Schematik workspace — diagram, code, parts, and assembly steps included. Swap the sensor, add features, or redesign the whole thing with AI. The author's original stays untouched.

Open in Schematik