Photoakustischer C0₂ Sensor SCD40
Der SCD40 Sensor dient zum Messen von CO₂ mit einer recht guten Genauigkeit zwischen 400 und 2000 ppm. Zur internen Kompensation von Umwelteinflüssen sind im Sensor auch ein Temperatur- und ein Luftfeuchtigkeitssensor verbaut deren Messergebnisse ebenfalls über die I2C Schnittstelle ausgelesen werden können.
Das Messprinzip des Sensors beruht auf der Eigenschaft, dass CO₂ Moleküle Wärmestrahlung (IR-Light) absorbieren (gleicher Effekt wie bei de Klimaerwärmung in dem reflektiertes Sonnelicht (Wärmestrahlung) von CO₂ Molekülen zurückgehalten wird).
Durch die Energieabsorption beginnen die CO₂ Moleküle zu schwingen. Werden die Moleküle nun periodischen mit IR Licht bestahlt beginnen sie sich so zu bewegen, dass dies mit einem Mikrofon detektiert werden kann. Je Höher die CO₂ Konzentration innerhalb der Kammer, desto höher der Mikrofonausschlag. Diesen Effekt nennt man Photoakustischer Effekt.
| Pinbelegung | |
|---|---|
| VDD | +3,3V (oder 5V) Spannungsversorgung |
| GND | - Spannungsversorgung |
| SCL | I2C - Serial Clock |
| SDA | I2C - Serial Data |
| Die I2C Slave Adresse des Sensors ist 0x62 und der SCL-Takt max. 400kHz | |
Ansteuerung des Sensors
Der SCD50 Sensor lässt sich mit I2C über die feste Adresse 0x62
ansteuern. Es können Befehle gesendet (send Command), Einstellungen verändert (Write) und Sensorwerte ausgelesen
werden (Read).
Sämtliche Befehle (und noch einige mehr) sind im Datenblatt detailliert beschrieben.
Send Commands
- Start Periodic Measurement - Automatische Messung alle 5 Sekunden
- Start Low Power Periodic Measurement - Automatische Messung alle 30 Sekunden
- Stop Periodic Measurement - Beendet die automatische Messung
Hinweis: Um Einstellungen zu verändern muss die automatische Messung abgeschaltet sein
"Send Command" Funktionen
void startPeriodicMeasurement() {
Wire.beginTransmission(0x62); // I2C Sensoradresse lt. Datenblatt
Wire.write(0x21); // MSB Befehl
Wire.write(0xb1); // LSB Befehl
Wire.endTransmission(); // beendet die Übertragung mit dem Stop-Bit
}
void startLowPowerPeriodicMeasurement(){
Wire.beginTransmission(0x62); // I2C Sensoradresse lt. Datenblatt
Wire.write(0x21); // MSB Befehl
Wire.write(0xac); // LSB Befehl
Wire.endTransmission(); // beendet die Übertragung mit dem Stop-Bit
}
void stopPeriodicMeasurement(){
Wire.beginTransmission(0x62); // I2C Sensoradresse lt. Datenblatt
Wire.write(0x3F); // MSB Befehl
Wire.write(0x86); // LSB Befehl
Wire.endTransmission(); // beendet die Übertragung mit dem Stop-Bit
delay(500); // Zeit bis zur Ausführung lt. Datenblatt
}
Sensor Write
- Set Temperature Offset - Abhängig von der Einbausituation kann ein Offset eingestellt werden
- Set Sensor Altitude - Zur Verbesserung der CO₂ Messgenauigkeit kann die Betriebshöhe eingestellt werden
- Set Ambient Pressure - Alternativ zur Höhe kann auch eine Druckdifferenz eingestellt werden
Hinweis: Höhe und Druck beeinflussen sich gegenseitig. Entweder macht man die Korrektur über die Höhe oder die Druckdifferenz
"Sensor Write" Funktionen
void setTemperatureOffset(float tOffset) { // tOffset = 0-20°C
unsigned int word = tOffset * 65535 / 175;
byte crc = calculateCRC(word);
// Senden des Befehls "Set Temperature Offset"
Wire.beginTransmission(0x62); // I2C Sensoradresse lt. Datenblatt
Wire.write(0x24); // MSB Command
Wire.write(0x1d); // LSB Command
// Übertragen der Daten inkl. CRC-Checksumme
Wire.write(word>>8); // MSB
Wire.write(word); // LSB
Wire.write(crc); // CRC
Wire.endTransmission(); // beendet die Übertragung mit dem Stop-Bit
}
void setSensorAltitude(unsigned int altitude) { // altitude = 0-3000m
unsigned int word = altitude;
byte crc = calculateCRC(word);
// Senden des Befehls "Set Sensor Altitude"
Wire.beginTransmission(0x62); // I2C Sensoradresse lt. Datenblatt
Wire.write(0x24); // MSB Command
Wire.write(0x27); // LSB Command
// Übertragen der Daten inkl. CRC-Checksumme
Wire.write(word>>8); // MSB
Wire.write(word); // LSB
Wire.write(crc); // CRC
Wire.endTransmission(); // beendet die Übertragung mit dem Stop-Bit
}
void setAmbientPressure(unsigned long pressure) { // pressure = 70000-120000 Pa
unsigned int word = pressure / 100;
Serial.println(word);
byte crc = calculateCRC(word);
// Senden des Befehls "Set Ambient Pressure"
Wire.beginTransmission(0x62); // I2C Sensoradresse lt. Datenblatt
Wire.write(0xE0); // MSB Command
Wire.write(0x00); // LSB Command
// Übertragen der Daten inkl. CRC-Checksumme
Wire.write(word>>8); // MSB
Wire.write(word); // LSB
Wire.write(crc); // CRC
Wire.endTransmission(); // beendet die Übertragung mit dem Stop-Bit
}
// Berechnung der CSC Prüfsumme lt. Datenblatt
byte calculateCRC(unsigned int value) {
const unsigned char polynom = 0x31;
unsigned char crc = 0xff; // Init-Wert 0xff
unsigned char bitPosition;
crc = crc ^ value >> 8;
for (bitPosition = 8; bitPosition > 0; bitPosition--) {
if (crc & 0x80) // 0x80 = 0b10000000
crc = (crc << 1) ^ polynom;
else
crc = (crc << 1);
}
crc = crc ^ value & 0b11111111;
for (bitPosition = 8; bitPosition > 0; bitPosition--) {
if (crc & 0x80) // 0x80 = 0b10000000
crc = (crc << 1) ^ polynom;
else
crc = (crc << 1);
}
return crc;
}
Sensor Read
- Read Measurement - Liest die aktuellen Messwerte aus (CO₂, Temperatur, Luftfeuchtigkeit)
Hinweis: Abgeholte Messwerte werden im Sensor gelöscht. Aktualisierte Messwerte sind erst nach 5 oder 30 Sekunden vorhanden. - Get Temperature Offset - Zeigt die Einstellwerte für das Temperaturoffset (Werkseinstellung 4°C)
- Get Sensor Altitude - Zeigt die Einstellwerte für das Betriebshöhenoffset (Werkseinstellung 0m)
- Get Ambient Pressure - Zeigt die Einstellwerte des Druckoffsets (Werkseinstellung 101300Pa)
Hinweis: Höhe und Druck beeinflussen sich gegenseitig.
"Sensor Read" Funktionen
void readMeasurement(){
// Senden des Befehls "Read Measurement"
Wire.beginTransmission(0x62); // I2C Sensoradresse lt. Datenblatt
Wire.write(0xec); // MSB Befehl
Wire.write(0x05); // LSB Befehl
Wire.endTransmission(); // beendet die Übertragung mit dem Stop-Bit
delay(1); // Verarbeitungszeit lt. Datenblatt
// Daten anfordern
Wire.requestFrom(0x62, 9); // 9 Bytes werden von der Slave Adresse 0x62 angefordert
byte i = 0;
while (Wire.available()) {
sensorWerte[i] = Wire.read(); // 'sensorWerte[]' ist eine globale Variable
i++;
}
}
float getTemperatureOffset() {
// Senden des Befehls "Get Temperature Offset"
Wire.beginTransmission(0x62); // I2C Sensoradresse lt. Datenblatt
Wire.write(0x23); // MSB Befehl
Wire.write(0x18); // LSB Befehl
Wire.endTransmission(); // beendet die Übertragung mit dem Stop-Bit
delay(1); // Verarbeitungszeit lt. Datenblatt
// Daten anfordern
Wire.requestFrom(0x62, 3); // 3 Bytes werden von der Slave Adresse 0x62 angefordert
byte receivedBytes[3];
byte i = 0;
while (Wire.available()) {
receivedBytes[i] = Wire.read();
i++;
}
return uint16_t(receivedBytes[0] << 8 | receivedBytes[1]) / 65535.0 * 175.0;
}
unsigned int getSensorAltitude() {
// Senden des Befehls "Get Sensor Altitude"
Wire.beginTransmission(0x62); // I2C Sensoradresse lt. Datenblatt
Wire.write(0x23); // MSB Befehl
Wire.write(0x22); // LSB Befehl
Wire.endTransmission(); // beendet die Übertragung mit dem Stop-Bit
delay(1); // Verarbeitungszeit lt. Datenblatt
// Daten anfordern
Wire.requestFrom(0x62, 3); // 3 Bytes werden von der Slave Adresse 0x62 angefordert
byte receivedBytes[3];
byte i = 0;
while (Wire.available()) {
receivedBytes[i] = Wire.read();
i++;
}
return (receivedBytes[0] << 8 | receivedBytes[1]);
}
unsigned long getAmbientPressure() {
// Senden des Befehls "Get Ambient Pressure"
Wire.beginTransmission(0x62); // I2C Sensoradresse lt. Datenblatt
Wire.write(0xE0); // MSB Befehl
Wire.write(0x00); // LSB Befehl
Wire.endTransmission(); // beendet die Übertragung mit dem Stop-Bit
delay(1); // Verarbeitungszeit lt. Datenblatt
// Daten anfordern
Wire.requestFrom(0x62, 3); // 3 Bytes werden von der Slave Adresse 0x62 angefordert
byte receivedBytes[3];
byte i = 0;
while (Wire.available()) {
receivedBytes[i] = Wire.read();
i++;
}
return uint32_t((receivedBytes[0] << 8 | receivedBytes[1])) * 100;
}