Arduino Mikrocontroller Tutorials - Module

Photoakustischer C0₂ Sensor SCD40

Bild Sensirion SCD40
Funktion Photoacustic Sensor

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

"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

"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

"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; }

Mehr Infos