From b3ae34e730a57808f2669177b98cdc72412a77f2 Mon Sep 17 00:00:00 2001 From: Stefan Brand Date: Wed, 31 Mar 2021 16:07:14 +0200 Subject: [PATCH] Initial SCD30 Support --- lib/SCD30/SCD30.cpp | 155 ++++++++++++++++++++++++++++++++++++++++++++ lib/SCD30/SCD30.h | 69 ++++++++++++++++++++ src/main.cpp | 11 +++- 3 files changed, 233 insertions(+), 2 deletions(-) create mode 100644 lib/SCD30/SCD30.cpp create mode 100644 lib/SCD30/SCD30.h diff --git a/lib/SCD30/SCD30.cpp b/lib/SCD30/SCD30.cpp new file mode 100644 index 0000000..14fe0a8 --- /dev/null +++ b/lib/SCD30/SCD30.cpp @@ -0,0 +1,155 @@ +/* + SCD30.cpp - Sensirion SCD30 CO2-Sensor Library + Copyright (c) 2020-2021, Stefan Brand + All rights reserved. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include "SCD30.h" + +// Default Constructor +SCD30::SCD30() {}; + +// Constructor with Measuring Interval and Autocalibration Status +SCD30::SCD30(uint8_t interval, bool selfcalib) { + this->interval = interval; + this->selfcalib = selfcalib; +} + +// Initialize the Sensor, Enable Constant Measurement, Set Autocalibration and Interval +void SCD30::initialize(void) { + uint8_t fw[3]; + // Soft Reset + reset(); + // Send Get Firmware, Workaround to Avoid First Real Command to be Ignored + getBytes(SCD30_GET_FW_VER, fw, 3); + delay(10); + // Start Continous Measurement + sendCmd(SCD30_CONT_MEASURE, 0); + // Enable/Disable Autocalibration + if (selfcalib) { + sendCmd(SCD30_AUTOCAL, 1); + } else { + sendCmd(SCD30_AUTOCAL, 0); + } + delay(10); + // Measurement Interval + sendCmd(SCD30_SET_INTERVAL, interval); +} + +// Read Data From Sensor and Put Them Into the Payload Array +uint8_t SCD30::getSensorData(char *payload, uint8_t startbyte) { + uint8_t ready[2] = {0}; + uint8_t data[18] = {0}; + // Check if Sensor Data is Available + getBytes(SCD30_DATA_READY, ready, 2); + if (ready[1] == 1){ + // Get All Measurements (18 Bytes) + getBytes(SCD30_GET_MEASURE, data, 18); + + // CO2 PPM + int16_t value = (int16_t)(byteToFloat(data[0], data[1], data[3], data[4])); + int16ToPayload(value, payload, startbyte); + + // Temperature + value = (int16_t)(byteToFloat(data[6], data[7], data[9], data[10])*100); + int16ToPayload(value, payload, startbyte+2); + + // Humidity + value = (int16_t)(byteToFloat(data[12], data[13], data[15], data[16])*100); + int16ToPayload(value, payload, startbyte+4); + } + return startbyte+6; +} + +// Calibrate the Sensor to 400ppm (Outside Level) +void SCD30::calibrate() { + sendCmd(SCD30_SET_RECALIB, 400); +} + +// Sensor Soft Reset +void SCD30::reset(void) { + sendCmd(SCD30_RESET); + delay(2000); // Bootup Time +} + +// Send a Command with Argument +void SCD30::sendCmd(uint16_t cmd, uint16_t arg) { + uint8_t args[2], crc; + args[0] = (arg >> 8); + args[1] = (arg & 0xFF); + crc = calcCrc(args, 2); + + Wire.beginTransmission(SCD30_ADDR); + Wire.write(cmd >> 8); + Wire.write(cmd & 0xFF); + Wire.write(args[0]); + Wire.write(args[1]); + Wire.write(crc); + Wire.endTransmission(); +} + +// Send only a Command +void SCD30::sendCmd(uint16_t cmd) { + Wire.beginTransmission(SCD30_ADDR); + Wire.write(cmd >> 8); + Wire.write(cmd & 0xFF); + Wire.endTransmission(); +} + +// Read len Number of Bytes from Register reg into Array bytes +void SCD30::getBytes(uint16_t reg, uint8_t bytes[], uint8_t len) { + sendCmd(reg); + delay(3); + Wire.requestFrom((uint8_t)SCD30_ADDR, (uint8_t)len); + if (Wire.available()) { + for (uint8_t i = 0; i #endif +#ifdef HAS_SCD30 + #include +#endif #ifdef HAS_BME280 #include #endif @@ -301,12 +304,16 @@ void setup() sensors[i] = new SENSAIRS8(); i++; #endif - #ifdef HAS_BME280 + #ifdef HAS_SCD30 + sensors[i] = new SCD30(); + i++; + #endif + #ifdef HAS_BME280 sensors[i] = new BME280(); i++; #endif #ifdef HAS_SHT21 - sensors[i] = new SHT21() + sensors[i] = new SHT21(); i++; #endif #ifdef HAS_DS18B20