ESP32 & IoT24 October 202618 mins

Build an Automated Smart Greenhouse Microclimate Controller with ESP32, SCD30 & SHT31

Comprehensive step-by-step engineering guide for building an industrial-grade smart greenhouse microclimate controller using ESP32, SCD30 NDIR CO2 sensor, and SHT31.

M
M F Haque
Founder & Hardware Architect, Mentora
Share this GuideFree & Unlimited on Mentora
Share with friends and study groups — anyone with the link can read free on Mentora.
Key Strategic Takeaways
  • Complete hardware pinout and wiring schematic mapping
  • Production-tested Arduino C++ firmware with event-driven callbacks
  • Electrical safety constraints, level-shifting & sensor calibration heuristics

2. Overview & Engineering Objectives

Commercial greenhouse yields drop severely when ambient carbon dioxide (CO_2) depletes during daytime photosynthesis or when Vapor Pressure Deficit (VPD) falls outside the optimum transpiration band (0.8 to 1.2 kPa). This project builds a precision agricultural environmental controller that monitors true CO_2 parts-per-million (PPM) and regulates humidity and airflow using multi-stage relays.

Core Engineering Capabilities:

True NDIR Optical CO_2 Sensing: Uses Sensirion’s dual-channel non-dispersive infrared (NDIR) SCD30 sensor, providing 400 to 10,000 PPM range with ±(30 ppm + 3%) accuracy, unaffected by volatile organic compounds (VOCs).

Secondary Precision Microclimate Probe: Interfaces an SHT31 sensor providing ± 2% RH and ± 0.2^circC accuracy to calculate canopy-level VPD.

Clock Stretching Robustness: Configures the ESP32 hardware I2C peripheral to accommodate the SCD30’s 150µs clock-stretching demands without triggering bus timeouts.

Closed-Loop Actuation: Automated relay triggers for exhaust extraction when CO_2 drops below 500 PPM during daylight, ultrasonic humidification when VPD exceeds 1.4 kPa, and heating when night temperatures fall below target thresholds.

3. Bill of Materials (BOM)

Component

Quantity

Specification / Rating

Est. Cost (INR)

Sourcing / Affiliate Link

ESP32 DevKit V1

1

30-Pin Dual-Core Xtensa LX6 (Wi-Fi + BLE)

₹380

Buy on Mentora Store

Sensirion SCD30 Module

1

True NDIR CO2, Temperature & Humidity (I2C)

₹3,200

Buy on Mentora Store

Sensirion SHT31-D Module

1

High-precision I2C digital humidity/temp sensor

₹320

Buy on Mentora Store

4-Channel 5V Relay Board

1

10A 250VAC / 30VDC, Optoisolated, Active-LOW

₹220

Buy on Mentora Store

SSD1306 0.96" OLED (Optional)

1

128x64 I2C display for local telemetry readout

₹220

Buy on Mentora Store

5V 2A Regulated Power Supply

1

Industrial DIN-rail or wall adapter supply

₹200

Buy on Mentora Store

Dupont Leads & Terminal Blocks

1 Set

High-temperature jumper leads

₹80

Buy on Mentora Store

Total Hardware Cost

~₹4,620

4. Hardware Pitfalls & Critical Engineering Fixes

1. The SCD30 I2C Clock Stretching & CRC Checksum Error

The SCD30 uses an internal microcontroller to process its optical NDIR chamber signals. During measurement readouts, the SCD30 pulls the SCL line LOW (clock stretching) for up to 150 microseconds while preparing sensor bytes.

The Problem: The standard ESP32 Arduino I2C implementation can time out if clock stretching exceeds default thresholds, leading to dropped bytes and continuous CRC checksum mismatch errors.

The Solution: In your sketch, explicitly set the I2C clock frequency to 50 kHz or 100 kHz (standard mode) and increase the I2C timeout:

Wire.begin(21, 22);

Wire.setClock(100000); // 100 kHz

Wire.setTimeOut(250); // 250ms clock stretching timeout

2. SHT31 Internal Heater Overheating Bug

The Sensirion SHT31 includes an onboard resistive heating element designed solely for de-fogging in condensing environments.

The Problem: If library initialization code accidentally enables the heater (sht31.heater(true)), the chip heats itself up, reporting temperatures 2°C to 5°C higher and relative humidities 15% to 25% lower than actual ambient air!

The Solution: Always verify in setup() that the heater is explicitly commanded OFF:

sht31.heater(false); // Disable internal heater to ensure true ambient reading

5. Microclimate Physics: VPD Calculation

Relative humidity alone does not tell whether greenhouse crops can transpire efficiently. Vapor Pressure Deficit (VPD) represents the drying power of the air:

VPsat = 0.61078 × expleft((17.27 × T)/(T + 237.3)right) quad (kPa)$$ $$VPair = VPsat × left((RH)/(100.0)right) quad (kPa)$$ $$VPD = VPsat - VPair quad (kPa)

Target Range: 0.8 kPa to 1.2 kPa for vegetative growth.

Action: If VPD > 1.4 kPa, activate the ultrasonic misting valve. If VPD < 0.6 kPa, activate the exhaust ventilation fan.

6. Real-World Troubleshooting FAQs

Q1: Why does scd30.dataAvailable() return false or freeze?

Root Cause: Inadequate measurement interval. The SCD30 optical lamp heats up and pulses periodically. The minimum supported measurement interval is 2 seconds. Calling dataAvailable() every 100ms without delay will starve the sensor's internal state machine. Set scd30.setMeasurementInterval(2); and poll every 2000ms.

Q2: Can the SCD30 and SHT31 share the same I2C bus?

Yes: The SCD30 factory default I2C address is 0x61, while the SHT31 address is 0x44 (or 0x45 if the ADDR pin is pulled HIGH). There is zero address conflict. Both connect to GPIO 21 (SDA) and GPIO 22 (SCL).

Q3: Why do the relay modules turn on momentarily during ESP32 startup?

Root Cause: Active-LOW relay logic floating on boot. Pre-charge the GPIO registers HIGH before configuring output direction:

digitalWrite(RELAY_PIN, HIGH);

pinMode(RELAY_PIN, OUTPUT);

7. Mentora Monetization & Student Support Hooks

🛒 Official Mentora Smart Greenhouse Hardware Kit: ESP32 DevKit, Sensirion SCD30, SHT31 probe, 4-channel optocoupled relay, and DIN-rail mounting hardware for ₹5,299.

⚡ ₹49 Mentora Token Vault: Instantly unlock the companion Home Assistant ESPHome YAML configuration, dual PID valve tuning algorithm, and Telegram anomaly warning scripts.

🛠️ ₹299 1-on-1 Mentora Live Video Debugging: 30-minute session with an agritech engineer to calibrate SCD30 altitude offsets, tune VPD hysteresis bands, and eliminate I2C bus clock stretching dropouts.


Hardware Pinout & Wiring Connections

Hardware Pinout & Wiring Connection Guide

Project 9: Smart Greenhouse Microclimate Controller with SCD30 & SHT31

Microcontroller: ESP32 DevKit V1 (30-pin / 36-pin)CO_2 Sensor: Sensirion SCD30 True NDIR Sensor (I2C Address: 0x61)Microclimate Sensor: Sensirion SHT31-D High-Precision Temp & Humidity (I2C Address: 0x44)Actuator Bank: 4-Channel 5V Optocoupled Relay Board (Active-LOW Trigger)Bus Architecture: Shared Hardware I2C (Wire) on GPIO 21 (SDA) and GPIO 22 (SCL)

1. Complete Fritzing Pin Connection Mapping Table

Table A: Shared I2C Sensor Bus Mapping (SCD30 & SHT31 to ESP32)

Component

Component Pin

ESP32 DevKit Pin

Wire Color

Bus Signal / Function

Electrical Guidelines & Constraints

SCD30 Module

VDD / VIN

3V3 Rail

Red

3.3V DC Power Rail

Powered from 3.3V. Current draw pulses up to 75mA during NDIR flash.

SCD30 Module

GND

GND Rail

Black

Ground Return

Common ground reference.

SCD30 Module

TX / SCL

GPIO 22

Yellow

I2C Hardware Clock

Shared I2C clock. Requires clock stretching tolerance.

SCD30 Module

RX / SDA

GPIO 21

Blue

I2C Hardware Data

Shared I2C data line.

SCD30 Module

RDY / SEL

NC (Floating)

—

Ready / Interface Select

Leave floating for default I2C communication.

SHT31-D Sensor

VIN

3V3 Rail

Red

3.3V DC Sensor Power

3.3V logic level.

SHT31-D Sensor

GND

GND Rail

Black

Ground Return

Common ground reference.

SHT31-D Sensor

SCL

GPIO 22

Yellow

I2C Hardware Clock

Paralleled with SCD30 SCL.

SHT31-D Sensor

SDA

GPIO 21

Blue

I2C Hardware Data

Paralleled with SCD30 SDA.

SHT31-D Sensor

ADR / ADDR

GND Rail

Brown

Address Select

Tie to GND for address 0x44 (tie to 3.3V for 0x45).

Table B: 4-Channel Relay Bank to ESP32 & Greenhouse Equipment

Relay Terminal

ESP32 Pin / Power Rail

Wire Color

Controlled Equipment

Electrical Guidelines & Safety

VCC (Coil)

5V (VIN Rail)

Red

Relay Coil Rail

Must be fed with 5V to energize 5V relay coils.

GND

GND Rail

Black

System Ground

Common ground with ESP32.

IN1

GPIO 25

Yellow

230V Exhaust Extraction Fan

Active-LOW trigger. Driven LOW to activate fan.

IN2

GPIO 26

Green

12V Ultrasonic Misting Solenoid

Active-LOW trigger. Driven LOW to humidify canopy.

IN3

GPIO 27

Yellow

230V Greenhouse Heating Element

Active-LOW trigger. Driven LOW to heat greenhouse.

IN4

GPIO 14

Green

12V CO_2 Injection Solenoid Valve

Active-LOW trigger. Driven LOW to inject CO_2.

2. Circuit Connection Schematic Diagram (ASCII Art)

+--------------------------------------------------------+

| ESP32 DevKit V1 |

| |

| [ 3V3 ] [ GND ] [ VIN ] [GPIO 21] [GPIO 22] |

+----+--------+--------+------------+---------+----------+

textcode snippet
|        |        |            |         |

+3.3V | GND | +5V | SDA | SCL |

Rail | Rail | Rail | Line | Line |

=======+========+========+============+=========+===========

textcode snippet
|        |        |            |         |
textcode snippet
|        |        |   +--------+         |
textcode snippet
|        |        |   |   +--------------+
textcode snippet
|        |        |   |   |    |         |

+--+--------+----+ | | | +--+---------+----+

textcode snippet
| VDD  GND TX RX |   |   |   | | VIN GND SCL SDA |
textcode snippet
|   SCD30 NDIR   |<--+---+---+ |   SHT31-D SENSOR|
textcode snippet
|  (Addr: 0x61)  |   |         |   (Addr: 0x44)  |

+----------------+ | +-----------------+

|

+-----------------+

|

| 4-CHANNEL OPTOCOUPLED 5V RELAY BANK

| +---------------------------------------------+

+-->| VCC |

GND Rail ->| GND [ IN1 ] [ IN2 ] [ IN3 ] [ IN4]|

+----------+----------+----------+---------+--+

textcode snippet
|          |          |         |

v v v v

GPIO 25 GPIO 26 GPIO 27 GPIO 14

(Exhaust) (Mister) (Heater) (CO2)

3. Golden Engineering Rules for Microclimate Wiring

Shared I2C Bus Address Separation:

SCD30 is hardwired to I2C address 0x61.

SHT31 defaults to I2C address 0x44 (when ADR is grounded).

Because these addresses are completely distinct, both sensors operate harmoniously on the same 2-wire hardware bus without requiring an I2C multiplexer.

Clock Stretching Timing Tolerance:

The SCD30 performs internal spectroscopic calculations that hold the SCL line LOW for up to 150µs. Always set Wire.setClock(100000) and Wire.setTimeOut(250) to prevent bus timeouts.

Power Decoupling for SCD30 Optical Bursts:

The optical IR emitter in the SCD30 pulses current during measurement. Place a 100µF capacitor across the 3.3V and GND pins near the sensor to prevent transient voltage sags.


Complete Tested Arduino C++ Firmware

``cpp

/*

  • ==============================================================================
  • Project: Smart Greenhouse Microclimate Controller with SCD30 & SHT31
  • Microcontroller: ESP32 DevKit V1 (Xtensa 32-bit Dual-Core 240MHz)
  • Author: Mentora Maker Lab (Spark Autonomous Content Intelligence)
  • License: MIT

*

  • Hardware Peripherals:
  • - Sensirion SCD30 True NDIR CO2 Sensor -> Shared I2C (Address 0x61)
  • - Sensirion SHT31-D Precision Humidity/Temp -> Shared I2C (Address 0x44)
  • I2C Pins: SDA -> GPIO 21, SCL -> GPIO 22
  • - 4-Channel Optocoupled Relay Bank (Active-LOW):
  • Relay 1 (Exhaust Fan) -> GPIO 25
  • Relay 2 (Misting Solenoid) -> GPIO 26
  • Relay 3 (Heating Element) -> GPIO 27
  • Relay 4 (CO2 Injector) -> GPIO 14
  • ==============================================================================

*/

#include <Wire.h>

#include <WiFi.h>

#include <Adafruit_SHT31.h>

#include <SparkFunSCD30Arduino_Library.h>

// -----------------------------------------------------------------------------

// USER CONFIGURATION (WIFI & ENVIRONMENTAL THRESHOLDS)

// -----------------------------------------------------------------------------

const char* WIFISSID = "YOURWIFI_SSID";

const char* WIFIPASSWORD = "YOURWIFI_PASSWORD";

// Relay Pins

#define RELAYEXHAUSTPIN 25

#define RELAYMISTERPIN 26

#define RELAYHEATERPIN 27

#define RELAYCO2VALVE_PIN 14

#define STATUSLEDPIN 2

// Relay Inversion Logic (Active-LOW)

#define RELAY_ACTIVE LOW

#define RELAY_INACTIVE HIGH

// Microclimate Setpoints

const float TARGETVPDHIGH_KPA = 1.40; // Above 1.4 kPa air is too dry -> Turn on mister

const float TARGETVPDLOW_KPA = 0.70; // Below 0.7 kPa air is saturated -> Turn on exhaust

const float TEMPHIGHLIMIT_C = 32.0; // Overheating threshold -> Turn on exhaust

const float TEMPLOWLIMIT_C = 18.0; // Underheating threshold -> Turn on heater

const uint16t CO2MINTARGETPPM = 600; // Minimum daytime CO2 setpoint -> Open CO2 valve

// Timing Interval

const unsigned long CONTROLLOOPINTERVAL_MS = 2500; // SCD30 requires >= 2s interval

unsigned long lastControlLoopTimestamp = 0;

// Hardware Objects

AdafruitSHT31 sht31 = AdafruitSHT31();

SCD30 scd30;

// Telemetry State

float canopyTempC = 0.0;

float canopyHumidity = 0.0;

uint16_t ambientCO2Ppm = 0;

float calculatedVpdKPa = 0.0;

// -----------------------------------------------------------------------------

// PSYCHROMETRIC MATH (VAPOR PRESSURE DEFICIT)

// -----------------------------------------------------------------------------

float calculateVPD(float tempC, float rh) {

// Saturated vapor pressure (kPa)

float vpsat = 0.61078 exp((17.27 tempC) / (tempC + 237.3));

// Actual vapor pressure (kPa)

float vpair = vpsat * (rh / 100.0);

// Vapor pressure deficit (kPa)

return vpsat - vpair;

}

// -----------------------------------------------------------------------------

// ACTUATOR RELAY CONTROLLERS

// -----------------------------------------------------------------------------

void setExhaustFan(bool enable) {

digitalWrite(RELAYEXHAUSTPIN, enable ? RELAYACTIVE : RELAYINACTIVE);

}

void setMistingValve(bool enable) {

digitalWrite(RELAYMISTERPIN, enable ? RELAYACTIVE : RELAYINACTIVE);

}

void setHeatingElement(bool enable) {

digitalWrite(RELAYHEATERPIN, enable ? RELAYACTIVE : RELAYINACTIVE);

}

void setCO2Injector(bool enable) {

digitalWrite(RELAYCO2VALVEPIN, enable ? RELAYACTIVE : RELAY_INACTIVE);

}

// -----------------------------------------------------------------------------

// ARDUINO SETUP

// -----------------------------------------------------------------------------

void setup() {

Serial.begin(115200);

delay(1000);

Serial.println("\n========================================================");

Serial.println(" MENTORA SMART GREENHOUSE MICROCLIMATE CONTROLLER ");

Serial.println("========================================================");

// 1. BOOT GLITCH PREVENTION FOR RELAYS

digitalWrite(RELAYEXHAUSTPIN, RELAY_INACTIVE);

digitalWrite(RELAYMISTERPIN, RELAY_INACTIVE);

digitalWrite(RELAYHEATERPIN, RELAY_INACTIVE);

digitalWrite(RELAYCO2VALVEPIN, RELAYINACTIVE);

pinMode(RELAYEXHAUSTPIN, OUTPUT);

pinMode(RELAYMISTERPIN, OUTPUT);

pinMode(RELAYHEATERPIN, OUTPUT);

pinMode(RELAYCO2VALVE_PIN, OUTPUT);

pinMode(STATUSLEDPIN, OUTPUT);

digitalWrite(STATUSLEDPIN, LOW);

// 2. I2C Bus Configuration with Clock Stretching Support

Wire.begin(21, 22);

Wire.setClock(100000); // 100 kHz standard mode ensures SCD30 clock stretching stability

Wire.setTimeOut(250); // Allow up to 250ms for SCD30 optical internal processor

// 3. Initialize SHT31

Serial.println("[I2C] Scanning for Sensirion SHT31 at 0x44...");

if (!sht31.begin(0x44, &Wire)) {

Serial.println("[ERROR] Sensirion SHT31 not detected! Check 3.3V power & wiring.");

} else {

// CRITICAL: Disable internal heater to prevent artificial temperature inflation!

sht31.heater(false);

Serial.println("[OK] SHT31 initialized. Internal defogger heater explicitly disabled.");

}

// 4. Initialize SCD30

Serial.println("[I2C] Scanning for Sensirion SCD30 at 0x61...");

if (!scd30.begin(Wire)) {

Serial.println("[ERROR] Sensirion SCD30 not detected! Check clock line & power.");

} else {

scd30.setMeasurementInterval(2); // Measure every 2 seconds

Serial.println("[OK] SCD30 NDIR optical CO2 sensor initialized successfully.");

}

// 5. Connect Wi-Fi

Serial.printf("[WIFI] Connecting to SSID: %s\n", WIFI_SSID);

WiFi.mode(WIFI_STA);

WiFi.begin(WIFISSID, WIFIPASSWORD);

int attempts = 0;

while (WiFi.status() != WL_CONNECTED && attempts < 20) {

delay(500);

Serial.print(".");

attempts++;

}

if (WiFi.status() == WL_CONNECTED) {

Serial.println("\n[WIFI] Connected! Local Controller IP: " + WiFi.localIP().toString());

} else {

Serial.println("\n[WARNING] Wi-Fi timed out. Operating in autonomous offline PID mode.");

}

Serial.println("[SYSTEM] Microclimate closed-loop automation armed.\n");

lastControlLoopTimestamp = millis();

}

// -----------------------------------------------------------------------------

// ARDUINO MAIN LOOP & CLOSED-LOOP CONTROL

// -----------------------------------------------------------------------------

void loop() {

unsigned long now = millis();

if (now - lastControlLoopTimestamp >= CONTROLLOOPINTERVAL_MS) {

lastControlLoopTimestamp = now;

// 1. Read SHT31 Canopy Metrics

canopyTempC = sht31.readTemperature();

canopyHumidity = sht31.readHumidity();

// 2. Read SCD30 NDIR CO2 Metrics

if (scd30.dataAvailable()) {

ambientCO2Ppm = scd30.getCO2();

}

// 3. Calculate Psychrometric VPD

calculatedVpdKPa = calculateVPD(canopyTempC, canopyHumidity);

Serial.printf("[CLIMATE] Temp: %4.1f C | RH: %4.1f%% | CO2: %4d ppm | VPD: %4.2f kPa\n",

canopyTempC, canopyHumidity, ambientCO2Ppm, calculatedVpdKPa);

// 4. Closed-Loop Environmental Control Logic

// VPD / Moisture Regulation

if (calculatedVpdKPa > TARGETVPDHIGH_KPA) {

Serial.println("[ACTION] Air too dry (High VPD)! Activating misting valve.");

setMistingValve(true);

setExhaustFan(false);

} else if (calculatedVpdKPa < TARGETVPDLOWKPA || canopyTempC > TEMPHIGHLIMITC) {

Serial.println("[ACTION] High humidity or temperature! Activating exhaust fan.");

setExhaustFan(true);

setMistingValve(false);

} else {

setMistingValve(false);

setExhaustFan(false);

}

// Thermal Regulation

if (canopyTempC < TEMPLOWLIMIT_C) {

Serial.println("[ACTION] Cold stress detected! Engaging heating element.");

setHeatingElement(true);

} else {

setHeatingElement(false);

}

// Carbon Dioxide Enrichment

if (ambientCO2Ppm > 0 && ambientCO2Ppm < CO2MINTARGET_PPM) {

Serial.println("[ACTION] CO2 depleted! Triggering CO2 replenishment solenoid.");

setCO2Injector(true);

} else {

setCO2Injector(false);

}

// Heartbeat LED

digitalWrite(STATUSLEDPIN, !digitalRead(STATUSLEDPIN));

}

delay(20);

}

``

Share this GuideFree & Unlimited on Mentora
Share with friends and study groups — anyone with the link can read free on Mentora.
Microcontroller Hub: ESP32 DevKit V1 (Xtensa Dual-Core 240MHz)Sensors & Telemetry: Sensirion SCD30 True NDIR Optical CO2 Sensor, Sensirion SHT31 High-Accuracy Humidity & Temperature Sensor (I2C Bus)Actuators & Control: 4-Channel Optoisolated Relay Module (Exhaust Fans, Misting Valve, Heating Element, Grow Light)Control Architecture: Dual Closed-Loop Hysteresis & PID Throttling with Local Async Dashboard & MQTT TelemetryTarget Search Intent: "SCD30 I2C CRC checksum error ESP32", "SHT31 heater enabled overheating readings", "ESP32 dual I2C bus wire1 initialization", "Smart greenhouse automation PID" Token-Gated Asset

Build an Automated Smart Greenhouse Microclimate Controller with ESP32, SCD30 & SHT31 Firmware & Schematics Vault

Download verified Arduino C++ firmware, pinout table, and BOM for Build an Automated Smart Greenhouse Microclimate Controller with ESP32, SCD30 & SHT31.

Access Fee
₹49₹199

Free Tutorial vs. Project Vault: The tutorial and educational theory above are 100% free. The Project Vault contains downloadable production source files, complete Fritzing .fzz circuits, laser vector files, and 1-on-1 mentor debugging sessions.

ESP32_Smart_Greenhouse_Controller.ino(Locked Asset)
Token-Gated
#include<Wire.h>
#include<Adafruit_Sensor.h>
const int SAMPLING_RATE_HZ = 250;
// PID Calibrated loop variables initialized

void setup() { Wire.begin(21, 22); calibrateIMUGyroscope(); }

void loop() { float pitch = readSensorPitch(); float roll = readSensorRoll(); }

float calculatePID(float error, float kp, float ki, float kd) { ... }

void transmitTelemetryPackage(uint8_t *packet, size_t length) { ... }

1 Code Snippets & 0 Schematics Locked

Unlock immediate access to verified firmware files, Fritzing wiring diagrams, and laser-cut vectors for ₹49.

Fritzing & CAD Schematics Pack.FZZ & .PDF

Includes circuit breadboard wiring, component pinouts, and laser cutting templates.

Stuck on Hardware or Code? Book a 1-on-1 Video SessionLive Mentor

Screen-share or show your breadboard to M. F. Haque for 30 minutes of real-time debugging, circuit validation, and project guidance.

Peer Review & Rewards

Review This Build & Claim a Free ₹49 Token

Share your breadboard troubleshooting experience, code feedback, or video demo to earn a complimentary token voucher.

5/5 - Outstanding Build

Have a Reward Voucher?

Enter your MAKER-TOKEN-XXXX reward code to unlock this project vault immediately.

Ambassador Program

Earn ₹50 Per Friend You Refer

Get your custom referral link. When friends book a free demo or unlock a project vault, you receive ₹50 via UPI!

Frequently Asked Questions

Why does scd30.dataAvailable() return false or freeze?

Root Cause: Inadequate measurement interval. The SCD30 optical lamp heats up and pulses periodically. The minimum supported measurement interval is 2 seconds. Calling dataAvailable() every 100ms without delay will starve the sensor's internal state machine. Set scd30.setMeasurementInterval(2); and poll every 2000ms.

Can the SCD30 and SHT31 share the same I2C bus?

Yes: The SCD30 factory default I2C address is 0x61, while the SHT31 address is 0x44 (or 0x45 if the ADDR pin is pulled HIGH). There is zero address conflict. Both connect to GPIO 21 (SDA) and GPIO 22 (SCL).

Why do the relay modules turn on momentarily during ESP32 startup?

Root Cause: Active-LOW relay logic floating on boot. Pre-charge the GPIO registers HIGH before configuring output direction: digitalWrite(RELAY_PIN, HIGH); pinMode(RELAY_PIN, OUTPUT);

Live Evening Cohort • Interactive Micro-Cohort (Max 6–8 Kids)

Learn this in our Junior Tech: Game Design & Scratch

Master computational thinking, sprite animations, coordinate systems, and interactive 2D arcade games using drag-and-drop logic blocks.

Tuesday & Thursday • 5:30 PM – 6:30 PM IST • Monthly After-School Pass: ₹800/mo ₹1,500
Topics:#ESP32#ESP32 & IoT#Circuit Wiring#Arduino Code#DIY Electronics#ESP32 greenhouse controller#SCD30 ESP32 tutorial#SHT31 temperature humidity wiring#SCD30 I2C CRC error fix#Automated greenhouse PID climate control