sdc41 with bmp280 issue – wrong ppm value – zero ppm

  • Today got to know why my sdc41 sensor was showing bad data earlier it used to show 500 in day and 600-800ppm in night when door was closed. #sdc41 #esp32
    • It was paired with bmp280 and it was using pressure to Compensate the co2. but bmp 280 was showing bad value randomly about -149 hpa
    • As it was using BMP280 value it was started showing double and after few days it started showing 0 ppm
    • Now i ran simple code to just get value of sdc41 it was showing 23000 which more than current limit of sdc41 which is 5000
    • Ran frc 420 to self calibrate the sensor with current value to 420 reference
  • Ran auto calibration , everything looks good now.
#include <Arduino.h>
#include <Wire.h>
#include <SparkFun_SCD4x_Arduino_Library.h>

// NodeMCU/ESP8266 I2C pins: D2 = SDA, D1 = SCL
static const uint8_t SDA_PIN = D2;
static const uint8_t SCL_PIN = D1;

static const uint16_t MAX_VALID_CO2_PPM = 5000;
static const uint16_t DEFAULT_FRC_REFERENCE_PPM = 420; // Typical fresh-air reference
static const uint32_t FRC_STABILIZE_MS = 180000;       // >= 3 minutes per datasheet
static const float DEFAULT_AMBIENT_PRESSURE_HPA = 1006.0f;

SCD4x scd4x(SCD4x_SENSOR_SCD41);

bool applyAmbientPressureCompensation(float pressureHpa, bool persist = true) {
  if (pressureHpa < 700.0f || pressureHpa > 1200.0f) {
    Serial.println("ERROR: pressure out of expected range (700..1200 hPa).");
    return false;
  }

  const float pressurePa = pressureHpa * 100.0f;

  scd4x.stopPeriodicMeasurement();
  delay(500);

  if (!scd4x.setAmbientPressure(pressurePa)) {
    Serial.println("ERROR: Failed to set ambient pressure compensation.");
    scd4x.startPeriodicMeasurement();
    return false;
  }

  if (persist && !scd4x.persistSettings()) {
    Serial.println("WARNING: Ambient pressure set but persistSettings failed.");
  }

  if (!scd4x.startPeriodicMeasurement()) {
    Serial.println("ERROR: Failed to restart periodic measurement after pressure update.");
    return false;
  }

  Serial.print("Ambient pressure compensation set to ");
  Serial.print(pressureHpa, 1);
  Serial.println(" hPa");
  return true;
}

bool configureAutoSelfCalibration(bool enabled) {
  scd4x.stopPeriodicMeasurement();
  delay(500);

  if (!scd4x.setAutomaticSelfCalibrationEnabled(enabled)) {
    Serial.println("ERROR: Failed to set ASC state.");
    return false;
  }

  if (!scd4x.persistSettings()) {
    Serial.println("WARNING: ASC updated but persistSettings failed.");
  }

  if (!scd4x.startPeriodicMeasurement()) {
    Serial.println("ERROR: Failed to restart periodic measurement after ASC config.");
    return false;
  }

  Serial.print("ASC is now ");
  Serial.println(enabled ? "ENABLED" : "DISABLED");
  return true;
}

bool runForcedRecalibration(uint16_t referencePpm) {
  if (referencePpm < 350 || referencePpm > 2000) {
    Serial.println("ERROR: Reference ppm out of expected FRC range (350..2000).");
    return false;
  }

  Serial.println();
  Serial.println("Starting Forced Recalibration (FRC)...");
  Serial.println("Keep sensor in stable, known CO2 environment.");
  Serial.print("Reference CO2 (ppm): ");
  Serial.println(referencePpm);

  scd4x.stopPeriodicMeasurement();
  delay(500);
  if (!scd4x.startPeriodicMeasurement()) {
    Serial.println("ERROR: Could not start periodic measurement for FRC preconditioning.");
    return false;
  }

  // Datasheet requirement: run in normal mode for at least 3 minutes before FRC.
  const uint32_t startMs = millis();
  while (millis() - startMs < FRC_STABILIZE_MS) {
    const uint32_t remaining = (FRC_STABILIZE_MS - (millis() - startMs)) / 1000;
    Serial.print("FRC preconditioning... ");
    Serial.print(remaining);
    Serial.println(" s remaining");

    if (scd4x.readMeasurement()) {
      Serial.print("  sample CO2(ppm): ");
      Serial.println(scd4x.getCO2());
    }

    delay(5000);
  }

  scd4x.stopPeriodicMeasurement();
  delay(500);

  float correction = 0.0f;
  if (!scd4x.performForcedRecalibration(referencePpm, &correction)) {
    Serial.println("ERROR: FRC failed.");
    scd4x.startPeriodicMeasurement();
    return false;
  }

  Serial.print("FRC correction applied (ppm): ");
  Serial.println(correction, 1);

  if (!scd4x.persistSettings()) {
    Serial.println("WARNING: FRC succeeded but persistSettings failed.");
  }

  if (!scd4x.reInit()) {
    Serial.println("WARNING: reInit failed after FRC.");
  }

  if (!scd4x.startPeriodicMeasurement()) {
    Serial.println("ERROR: Could not restart periodic measurement after FRC.");
    return false;
  }

  Serial.println("FRC complete. Sensor restarted in periodic mode.");
  return true;
}

void handleSerialCommands() {
  if (!Serial.available()) {
    return;
  }

  String cmd = Serial.readStringUntil('\n');
  cmd.trim();
  cmd.toLowerCase();

  if (cmd == "help") {
    Serial.println("Commands:");
    Serial.println("  help         -> show commands");
    Serial.println("  asc on       -> enable automatic self-calibration");
    Serial.println("  asc off      -> disable automatic self-calibration");
    Serial.println("  frc          -> run forced recalibration with 420 ppm reference");
    Serial.println("  frc <ppm>    -> run forced recalibration with custom reference");
    Serial.println("  pressure     -> set pressure compensation to 1006 hPa");
    return;
  }

  if (cmd == "asc on") {
    configureAutoSelfCalibration(true);
    return;
  }

  if (cmd == "asc off") {
    configureAutoSelfCalibration(false);
    return;
  }

  if (cmd == "frc") {
    runForcedRecalibration(DEFAULT_FRC_REFERENCE_PPM);
    return;
  }

  if (cmd.startsWith("frc ")) {
    int ppm = cmd.substring(4).toInt();
    if (ppm <= 0) {
      Serial.println("ERROR: Invalid ppm. Example: frc 420");
      return;
    }
    runForcedRecalibration((uint16_t)ppm);
    return;
  }

  if (cmd == "pressure") {
    applyAmbientPressureCompensation(DEFAULT_AMBIENT_PRESSURE_HPA, true);
    return;
  }

  Serial.print("Unknown command: ");
  Serial.println(cmd);
  Serial.println("Type 'help' for available commands.");
}

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

  Serial.println();
  Serial.println("SCD41 minimal CO2 test starting...");

  Wire.begin(SDA_PIN, SCL_PIN);
  Wire.setClock(100000);

  if (!scd4x.begin(true, true, false)) {
    Serial.println("ERROR: SCD41 init failed. Check wiring, power, and I2C address.");
    while (true) {
      delay(1000);
    }
  }

  // Ensure periodic mode starts from a clean state.
  scd4x.stopPeriodicMeasurement();
  delay(500);

  if (!scd4x.startPeriodicMeasurement()) {
    Serial.println("ERROR: Could not start low-power periodic measurement.");
    while (true) {
      delay(1000);
    }
  }

  // Recommended for SCD41 long-term stability in real deployments.
  configureAutoSelfCalibration(true);

  // Overwrite any previously saved bad pressure setting (e.g. from a faulty upstream sensor)
  // and persist the correct local pressure compensation.
  applyAmbientPressureCompensation(DEFAULT_AMBIENT_PRESSURE_HPA, true);

  Serial.println("SCD41 initialized. First reading can take ~30s.");
  Serial.println("Type 'help' in Serial Monitor for calibration commands.");
}

void loop() {
  handleSerialCommands();

  if (!scd4x.readMeasurement()) {
    Serial.println("Measurement not ready yet...");
    delay(5000);
    return;
  }

  const uint16_t co2 = scd4x.getCO2();
  const float temperature = scd4x.getTemperature();
  const float humidity = scd4x.getHumidity();

  Serial.print("CO2(ppm): ");
  Serial.print(co2);
  Serial.print("  Temp(C): ");
  Serial.print(temperature, 2);
  Serial.print("  RH(%): ");
  Serial.println(humidity, 2);

  if (co2 == 0) {
    Serial.println("WARNING: CO2 is 0 ppm (invalid on SCD41). Sensor may still be stabilizing or faulty.");
  } else if (co2 > MAX_VALID_CO2_PPM) {
    Serial.println("WARNING: CO2 is above SCD41 range (5000 ppm). Reading likely invalid; run 'frc 420' in fresh air.");
  }

  delay(5000);
}
FRC preconditioning... 24 s remaining
  sample CO2(ppm): 23076
FRC preconditioning... 19 s remaining
  sample CO2(ppm): 23076
FRC preconditioning... 14 s remaining
  sample CO2(ppm): 23077
FRC preconditioning... 9 s remaining
  sample CO2(ppm): 23075
FRC preconditioning... 4 s remaining
  sample CO2(ppm): 23078
FRC correction applied (ppm): -22518.0
FRC complete. Sensor restarted in periodic mode.
Measurement not ready yet...
CO2(ppm): 471  Temp(C): 30.83  RH(%): 73.06
CO2(ppm): 472  Temp(C): 30.39  RH(%): 74.56
CO2(ppm): 488  Temp(C): 30.14  RH(%): 75.75
CO2(ppm): 486  Temp(C): 29.92  RH(%): 76.85
CO2(ppm): 469  Temp(C): 29.68  RH(%): 77.88

Published by

Leave a Reply

Your email address will not be published. Required fields are marked *