Logo Pastebin.fr
Pastebin

Retrouvez, créez et partagez vos snippets en temps réel.

CO2 sensor Arduino Nano

// ----------------------------------
#include <SSD1306.h>
#include "SparkFun_SCD4x_Arduino_Library.h"
#include <Wire.h>
#include <avr/sleep.h>
#include <avr/wdt.h>

// ==================== I2C POWER CONTROL ====================

// Disable TWI/I2C MODULE + pull-ups
inline void i2cOff() {
  // Disable internal pullups on SDA/SCL
  pinMode(A4, INPUT);          // SDA
  digitalWrite(A4, LOW);       // pull-up off
  pinMode(A5, INPUT);          // SCL
  digitalWrite(A5, LOW);       // pull-up off

  // Disable TWI hardware
  TWCR &= ~(1 << TWEN);        // disable TWI module
}

// Re-enable TWI/I2C module + internal pullups
inline void i2cOn() {
  // Re-enable internal pullups on SDA/SCL (optional)
  pinMode(A4, INPUT_PULLUP);   // SDA
  pinMode(A5, INPUT_PULLUP);   // SCL

  // Re-enable TWI hardware
  TWCR |= (1 << TWEN);
  
  // Re-start Wire library
  Wire.begin();
}

// ================

// ----------------------------------
//          WATCHDOG TIMER
// ----------------------------------

volatile uint32_t elapsedSeconds = 0;  // <-- replaces millis()

void setupWatchdogTimer() {
  // Enable watchdog configuration changes
  WDTCSR = (1 << WDCE) | (1 << WDE);

  // Set interrupt mode (NO reset) + 5 second period
  // WDP3 + WDP2 = 5 seconds
  WDTCSR = (1 << WDIE) | (1 << WDP3) | (1 << WDP2);
}

ISR(WDT_vect) {
  elapsedSeconds += 5;   // WDT fires every 5 seconds
}

inline void adcOff() {
  ADCSRA &= ~(1 << ADEN);  // disable ADC
}

inline void adcOn() {
  ADCSRA |= (1 << ADEN);   // enable ADC
}

void disableBOD() {
  MCUCR |= (1 << BODSE) | (1 << BODS);
  MCUCR = (MCUCR & ~(1 << BODSE)) | (1 << BODS);
}

void goToSleep() {
  i2cOff();
  adcOff();

  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  sleep_enable();
  noInterrupts();
  disableBOD();
  interrupts();
  sleep_cpu();  
  sleep_disable();
}

// ----------------------------------
//          ORIGINAL CODE
// ----------------------------------

SCD4x mySensor(SCD4x_SENSOR_SCD41);
SSD1306 display;

// --- BATTERY ---
const int BAT_PIN = A0;

// Voltage thresholds for 2S LiPo
const float FULL_VOLTAGE = 8.40;
const float EMPTY_VOLTAGE = 6.00;

// intervals converted to SECONDS:
#define MEASURE_INTERVAL_S         10      // 10 seconds
#define AVERAGE_PERIOD_S           (30UL * 60UL)   // 1800 seconds
#define LIVE_DISPLAY_DURATION_S    30      // 30 seconds
#define HISTORY_DISPLAY_DURATION_S 15      // 15 seconds

#define HISTORY_SIZE 10

float co2History[HISTORY_SIZE];
int historyCount = 0;
int historyIndex = 0;

uint32_t lastMeasure = 0;
uint32_t lastAverage = 0;
uint32_t lastDisplaySwitch = 0;

float co2Sum = 0;
int co2Samples = 0;

uint16_t currentCO2 = 0;
float currentTemp = 0;
float currentHum = 0;
float batteryJauge = 0;

bool showLive = true;


// ======================================================================
//                              SETUP
// ======================================================================

void setup() {
  Serial.begin(9600);
  Serial.println(F("SCD41 CO2 Logger"));
  Wire.begin();

  setupWatchdogTimer();  // now WDT is your master clock

  if (!mySensor.begin(false, true, false)) {
    Serial.println(F("Sensor not detected. Check wiring. Freezing..."));
    while (1);
  }

  mySensor.measureSingleShot();

  display.init();
  display.clear();
  display.setScale(2);
  display.setCaret(17, 0);
  display.setText(F("Trafichou"));
  display.setCaret(47, 25);
  display.setText(F("CO2"));
  display.setCaret(35, 55);
  display.setScale(1);
  display.setText(F("Starting..."));
  display.update();
  delay(2000);
  display.clear();

  lastMeasure = elapsedSeconds;
  lastAverage = elapsedSeconds;
  lastDisplaySwitch = elapsedSeconds;
}


// ======================================================================
//                               LOOP
// ======================================================================

void loop() {
  i2cOn();

  uint32_t now = elapsedSeconds;

  // ---- MEASUREMENT ----
  if (now - lastMeasure >= MEASURE_INTERVAL_S) {
    lastMeasure = now;

    if (mySensor.readMeasurement()) {
      currentCO2 = mySensor.getCO2();
      currentTemp = mySensor.getTemperature();
      currentHum = mySensor.getHumidity();

      Serial.print(F("CO2: "));
      Serial.println(currentCO2);

      co2Sum += currentCO2;
      co2Samples++;

      mySensor.measureSingleShot();
    }

    batteryJauge = calculateBatteryJauge();
  }

  // ---- 30 MIN AVERAGE ----
  if (now - lastAverage >= AVERAGE_PERIOD_S) {
    lastAverage = now;

    if (co2Samples > 0) {
      float avg = co2Sum / co2Samples;
      co2Sum = 0;
      co2Samples = 0;

      co2History[historyIndex] = avg;
      historyIndex = (historyIndex + 1) % HISTORY_SIZE;
      if (historyCount < HISTORY_SIZE) historyCount++;

      Serial.print(F("Stored 30-min avg: "));
      Serial.println(avg);
    }
  }

  // ---- SWITCH DISPLAY ----
  uint32_t duration = showLive ? LIVE_DISPLAY_DURATION_S : HISTORY_DISPLAY_DURATION_S;
  if (now - lastDisplaySwitch >= duration) {
    showLive = !showLive;
    lastDisplaySwitch = now;
    display.clear();
  }

  // ---- UPDATE DISPLAY ----
  if (showLive) showLiveScreen();
  else showHistoryScreen();

  // ---- ENTER DEEP SLEEP ----
  Serial.println(F("Going to sleep..."));
  goToSleep();
}



// ======================================================================
//                       DISPLAY FUNCTIONS
// ======================================================================

void showLiveScreen() {
  display.clear();
  display.setScale(1);
  display.setText(F("Battery:"));
  display.setText((int)batteryJauge);
  display.setText(F("%"));
  display.setCaret(0, 15);
  display.setScale(3);
  display.setText(F("CO2:"));
  display.setText((int)currentCO2);
  display.setCaret(0, 45);
  display.setScale(1);
  display.setText(F("T:"));
  display.setText(currentTemp);
  display.setText(F(" degres"));
  display.setCaret(0, 55);
  display.setText(F("H:"));
  display.setText(currentHum);
  display.setText(F("%"));
  display.update();
}

void showHistoryScreen() {
  display.clear();
  display.setScale(1);
  display.setCaret(0, 0);
  display.setText(F("30min avg CO2 ppm:"));

  const int perLine = 2;
  const int lineHeight = 10;
  const int colWidth = 65;

  for (int i = 0; i < historyCount; i += perLine) {
    display.setCaret(0, 10 + (i / perLine) * lineHeight);

    for (int j = 0; j < perLine; j++) {
      int idx = i + j;
      if (idx >= historyCount) break;

      int offset = historyCount - 1 - idx;
      float hoursAgo = offset * 0.5;

      int histIdx = (historyIndex - historyCount + idx + HISTORY_SIZE) % HISTORY_SIZE;

      int xPos = j * colWidth;
      display.setCaret(xPos, 10 + (i / perLine) * lineHeight);
      display.setText(hoursAgo, 1);
      display.setText(F("h: "));
      display.setText((int)co2History[histIdx]);
    }
  }

  display.update();
}


// ======================================================================
//                       BATTERY FUNCTIONS
// ======================================================================

float calculateBatteryJauge() {
  adcOn();
  delay(5);

  int raw = analogRead(BAT_PIN);

  float vOut = (raw / 1023.0) * 5.0;
  float batteryVoltage = vOut * 2.0;

  Serial.print(F("Measure battery: "));
  Serial.println(batteryVoltage);

  return mapBatteryPercent(batteryVoltage);
}

float mapBatteryPercent(float voltage) {
  if (voltage > FULL_VOLTAGE) voltage = FULL_VOLTAGE;
  if (voltage < EMPTY_VOLTAGE) voltage = EMPTY_VOLTAGE;

  return ((voltage - EMPTY_VOLTAGE) / (FULL_VOLTAGE - EMPTY_VOLTAGE)) * 100.0;
}

Créé il y a 2 mois.

Rechercher un Pastebin

Aucun paste trouvé.