Environmental Sensor Calibration and Long-Term Drift: BME280, SCD40, and Getting Accurate Readings Over Time
Wiring up a BME280 or an SCD40 and reading numbers over I2C is the easy part — that's covered in the general air quality and sensor wiring content on this site. What isn't covered there is what happens six months later, when your "accurate" sensor is reading humidity 8% low, or your CO2 sensor has drifted so far that it never reports below 800ppm even in fresh outdoor air. Every low-cost gas and humidity sensor drifts, and if you're building anything you actually trust — a greenhouse controller, a workshop air quality monitor, a whole-home CO2 tracker — you need a calibration routine, not just a wiring diagram.
Why Environmental Sensors Drift
- Humidity sensors (capacitive polymer elements, as used in the BME280/BME680) drift because the hygroscopic polymer layer that changes capacitance with moisture slowly ages, especially with exposure to contaminants, high humidity extremes, or long dry storage before first use.
- NDIR CO2 sensors (the SCD40/SCD41 and similar) drift because the infrared source's intensity degrades slowly with heat and age, and because dust or condensation inside the optical chamber changes the light path. This is a slow, gradual bias, not a sudden failure.
- Gas sensors generally (MOx-type VOC sensors especially) are the worst offenders — they can shift meaningfully within days of first power-on as the sensing element "burns in," and continue drifting with temperature and humidity cross-sensitivity for the sensor's whole life.
Automatic Self-Calibration (ASC) — and Why You Might Want to Turn It Off
The SCD40/SCD41 ships with Automatic Self-Calibration enabled by default. It works by assuming that, over a rolling multi-day window, the sensor will see at least one period of fresh outdoor-equivalent air (~420ppm) — typically overnight in a well-ventilated home — and it quietly recalibrates its baseline to that assumption. This works well for a bedroom or living room with normal ventilation. It works badly for:
- Enclosed spaces that never see fresh air — a sealed grow tent, a server closet, a workshop that's always closed up — where ASC will slowly recalibrate the sensor to whatever the "low" reading actually is in that space, hiding a real, sustained CO2 problem instead of measuring it.
- Greenhouses that are deliberately CO2-enriched above ambient for plant growth, where you don't want the sensor "correcting" toward 420ppm at all.
In these cases, disable ASC via the sensor's I2C command set (set_automatic_self_calibration(0) in most ESP32/Arduino libraries) and instead calibrate manually with a known reference — see below.
Manual Calibration Methods by Sensor Type
SensorMethodReference NeededFrequency BME280/BME680 (humidity)Salt calibration (75% RH) or side-by-side comparison against a calibrated hygrometerSaturated NaCl solution in a sealed container, or a known-good reference sensorEvery 6–12 months SCD40/SCD41 (CO2)Forced Recalibration (FRC) command with the sensor exposed to fresh outdoor air (~420ppm) for at least 3 minutes of stable readings firstConfirmed fresh outdoor air, or a certified reference gasEvery 6 months, or after any suspected drift event MOx VOC sensors (SGP30, CCS811, etc.)Baseline resistance capture after 48-hour burn-in, stored and reloaded on every bootClean air environment for burn-inBaseline should be reloaded every boot; full recalibration every 3–6 monthsThe Salt Calibration Method for Humidity (Detailed)
- Make a saturated solution of plain table salt (NaCl) in a small amount of distilled water in a sealed container — enough salt that undissolved crystals remain visible at the bottom.
- Place the sensor inside the sealed container, suspended above the solution (not touching it), and seal it up.
- Let it equilibrate for at least 6 hours, ideally overnight, at a stable room temperature.
- A saturated NaCl solution produces a highly repeatable 75.3% RH environment at 25°C. Compare your sensor's reading against that reference value.
- Apply the offset in software — most BME280 libraries let you apply a calibration offset in the driver layer rather than modifying firmware calibration registers directly.
Building Drift Compensation Into the Firmware
Rather than treating calibration as a one-time event, a more robust design bakes drift handling into the firmware itself:
- Store a calibration timestamp and offset in non-volatile storage (ESP32's NVS or LittleFS) so the device can log when it was last calibrated and flag itself as "calibration due" after a set interval.
- Cross-check sensors where possible. If you have both a BME280 and an SCD40 in the same enclosure, their temperature readings should roughly agree — a growing disagreement between them is often the first sign one of the two is drifting.
- Log raw values alongside calibrated ones if you're pushing data to Home Assistant, InfluxDB, or Grafana — this lets you spot a slow drift trend in the historical data long before it becomes an obviously wrong reading.
- Don't trust FRC blindly. Forced recalibration on the SCD40 assumes the reference condition was actually correct — running FRC against air that wasn't really at 420ppm (a closed room, a breath too close to the sensor) will make the calibration worse, not better, and there's no built-in sanity check against that mistake.
None of this turns a $10 sensor into a laboratory instrument, and it shouldn't need to — the goal is knowing roughly how much to trust a reading and catching the failure mode where a sensor drifts quietly for months while your dashboard keeps reporting numbers with false confidence. A simple 6-month calibration reminder, whether it's a recurring note or a firmware flag, is enough to keep any of the environmental monitoring projects on this site honest over the long run.
Related Guides
- Air Quality Monitoring with ESP32: Choosing and Wiring PM2.5, CO2, and VOC Sensors
- How to Program Addressable LED Strips: WS2812B Patterns, Effects, and Power Design
- I2C vs SPI vs UART: How to Choose and Use Serial Communication Protocols
- How to Use Sensors with Arduino and ESP32: Temperature, Distance, Load, Current, and Hall Effect
- How to Control Motors with Arduino and ESP32: Stepper, DC, and Servo Drivers
- How to Hack Wi-Fi and Bluetooth with the Flipper Zero and Wi-Fi Marauder
- ESP32: Setting Up for Arduino IDE
- Reading Sensors over I2C with Raspberry Pi