From 6dbe0f57c34676daf419e5440a27f43cc6cf8933 Mon Sep 17 00:00:00 2001 From: PaulZC Date: Mon, 1 Feb 2021 12:19:09 +0000 Subject: [PATCH] Adding some of the features of paulvha's version of the library --- .../Example5_GetSettings.ino | 129 ++++++++++++++++ keywords.txt | 20 ++- library.properties | 2 +- src/SparkFun_SCD30_Arduino_Library.cpp | 139 ++++++++++++++---- src/SparkFun_SCD30_Arduino_Library.h | 51 +++++-- 5 files changed, 289 insertions(+), 52 deletions(-) create mode 100644 examples/Example5_GetSettings/Example5_GetSettings.ino diff --git a/examples/Example5_GetSettings/Example5_GetSettings.ino b/examples/Example5_GetSettings/Example5_GetSettings.ino new file mode 100644 index 0000000..651ab83 --- /dev/null +++ b/examples/Example5_GetSettings/Example5_GetSettings.ino @@ -0,0 +1,129 @@ +/* + Reading CO2, humidity and temperature from the SCD30 + By: Nathan Seidle + SparkFun Electronics + Date: May 22nd, 2018 + License: MIT. See license file for more information but you can + basically do whatever you want with this code. + + Feel like supporting open source hardware? + Buy a board from SparkFun! https://www.sparkfun.com/products/15112 + + This example gets the SCD30's settings using the new getSettingValue function (thank you paulvha) + + Hardware Connections: + Attach RedBoard to computer using a USB cable. + Connect SCD30 to RedBoard using Qwiic cable. + Open Serial Monitor at 115200 baud. +*/ + +#include + +#include "SparkFun_SCD30_Arduino_Library.h" //Click here to get the library: http://librarymanager/All#SparkFun_SCD30 +SCD30 airSensor; + +void setup() +{ + Serial.begin(115200); + Serial.println("SCD30 Example"); + Wire.begin(); + + //Start sensor using the Wire port, but disable the auto-calibration + if (airSensor.begin(Wire, false) == false) + { + Serial.println("Air sensor not detected. Please check wiring. Freezing..."); + while (1) + ; + } + + uint16_t settingVal; // The settings will be returned in settingVal + + if (airSensor.getForcedRecalibration(&settingVal) == true) // Get the setting + { + Serial.print("Forced recalibration factor (ppm) is "); + Serial.println(settingVal); + } + else + { + Serial.print("getForcedRecalibration failed! Freezing..."); + while (1) + ; // Do nothing more + } + + if (airSensor.getMeasurementInterval(&settingVal) == true) // Get the setting + { + Serial.print("Measurement interval (s) is "); + Serial.println(settingVal); + } + else + { + Serial.print("getMeasurementInterval failed! Freezing..."); + while (1) + ; // Do nothing more + } + + if (airSensor.getTemperatureOffset(&settingVal) == true) // Get the setting + { + Serial.print("Temperature offfset (C) is "); + Serial.println(((float)settingVal) / 100.0, 2); + } + else + { + Serial.print("getTemperatureOffset failed! Freezing..."); + while (1) + ; // Do nothing more + } + + if (airSensor.getAltitudeCompensation(&settingVal) == true) // Get the setting + { + Serial.print("Altitude offset (m) is "); + Serial.println(settingVal); + } + else + { + Serial.print("getAltitudeCompensation failed! Freezing..."); + while (1) + ; // Do nothing more + } + + if (airSensor.getFirmwareVersion(&settingVal) == true) // Get the setting + { + Serial.print("Firmware version is 0x"); + Serial.println(settingVal, HEX); + } + else + { + Serial.print("getFirmwareVersion! Freezing..."); + while (1) + ; // Do nothing more + } + + Serial.print("Auto calibration set to "); + if (airSensor.getAutoSelfCalibration() == true) + Serial.println("true"); + else + Serial.println("false"); + + //The SCD30 has data ready every two seconds +} + +void loop() +{ + if (airSensor.dataAvailable()) + { + Serial.print("co2(ppm):"); + Serial.print(airSensor.getCO2()); + + Serial.print(" temp(C):"); + Serial.print(airSensor.getTemperature(), 1); + + Serial.print(" humidity(%):"); + Serial.print(airSensor.getHumidity(), 1); + + Serial.println(); + } + else + Serial.println("Waiting for new data"); + + delay(500); +} diff --git a/keywords.txt b/keywords.txt index 848105b..7dbe45d 100644 --- a/keywords.txt +++ b/keywords.txt @@ -14,26 +14,31 @@ SCD30 KEYWORD1 SCD30 KEYWORD2 begin KEYWORD2 +enableDebugging KEYWORD2 beginMeasuring KEYWORD2 +StopMeasurement KEYWORD2 +getSettingValue KEYWORD2 +getForcedRecalibration KEYWORD2 +getMeasurementInterval KEYWORD2 +getTemperatureOffset KEYWORD2 +getAltitudeCompensation KEYWORD2 +getFirmwareVersion KEYWORD2 getCO2 KEYWORD2 getHumidity KEYWORD2 getTemperature KEYWORD2 -getTemperatureOffset KEYWORD2 setMeasurementInterval KEYWORD2 setAmbientPressure KEYWORD2 setAltitudeCompensation KEYWORD2 -getAltitudeCompensation KEYWORD2 setAutoSelfCalibration KEYWORD2 -setTemperatureOffset KEYWORD2 setForcedRecalibrationFactor KEYWORD2 +setTemperatureOffset KEYWORD2 +getAutoSelfCalibration KEYWORD2 dataAvailable KEYWORD2 readMeasurement KEYWORD2 -sendCommand KEYWORD2 +reset KEYWORD2 sendCommand KEYWORD2 readRegister KEYWORD2 computeCRC8 KEYWORD2 -getAutoSelfCalibration KEYWORD2 -reset KEYWORD2 ####################################### # Constants (LITERAL1) @@ -48,3 +53,6 @@ COMMAND_AUTOMATIC_SELF_CALIBRATION LITERAL1 COMMAND_SET_FORCED_RECALIBRATION_FACTOR LITERAL1 COMMAND_SET_TEMPERATURE_OFFSET LITERAL1 COMMAND_SET_ALTITUDE_COMPENSATION LITERAL1 +COMMAND_RESET LITERAL1 +COMMAND_STOP_MEAS LITERAL1 +COMMAND_READ_FW_VER LITERAL1 diff --git a/library.properties b/library.properties index 5819048..a02d235 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=SparkFun SCD30 Arduino Library -version=1.0.10 +version=1.0.11 author=SparkFun Electronics maintainer=SparkFun Electronics sentence=Library for the Sensirion SCD30 CO2 Sensor diff --git a/src/SparkFun_SCD30_Arduino_Library.cpp b/src/SparkFun_SCD30_Arduino_Library.cpp index de738d6..2111fce 100644 --- a/src/SparkFun_SCD30_Arduino_Library.cpp +++ b/src/SparkFun_SCD30_Arduino_Library.cpp @@ -6,6 +6,11 @@ Written by Nathan Seidle @ SparkFun Electronics, May 22nd, 2018 + Updated February 1st 2021 to include some of the features of paulvha's version of the library + (while maintaining backward-compatibility): + https://github.com/paulvha/scd30 + Thank you Paul! + The SCD30 measures CO2 with accuracy of +/- 30ppm. This library handles the initialization of the SCD30 and outputs @@ -14,15 +19,10 @@ https://github.com/sparkfun/SparkFun_SCD30_Arduino_Library Development environment specifics: - Arduino IDE 1.8.5 - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + Arduino IDE 1.8.13 - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SparkFun code, firmware, and software is released under the MIT License. + Please see LICENSE.md for more details. */ #include "SparkFun_SCD30_Arduino_Library.h" @@ -34,9 +34,9 @@ SCD30::SCD30(void) //Initialize the Serial port #ifdef USE_TEENSY3_I2C_LIB -bool SCD30::begin(i2c_t3 &wirePort, bool autoCalibrate) +bool SCD30::begin(i2c_t3 &wirePort, bool autoCalibrate, bool measBegin) #else -bool SCD30::begin(TwoWire &wirePort, bool autoCalibrate) +bool SCD30::begin(TwoWire &wirePort, bool autoCalibrate, bool measBegin) #endif { _i2cPort = &wirePort; //Grab which port the user wants us to use @@ -50,7 +50,7 @@ bool SCD30::begin(TwoWire &wirePort, bool autoCalibrate) * * To set ClockStretchlimit() a check for ESP8266 boards has been added in the driver. * - * With setting to 20000, we set a max timeout of 20mS (> 20x the maximum measured) basically disabling the time-out + * With setting to 20000, we set a max timeout of 20mS (> 20x the maximum measured) basically disabling the time-out * and now wait for clock stretch to be controlled by the client. */ @@ -58,6 +58,19 @@ bool SCD30::begin(TwoWire &wirePort, bool autoCalibrate) _i2cPort->setClockStretchLimit(200000); #endif + uint16_t fwVer; + if (getFirmwareVersion(&fwVer) == false) // Read the firmware version. Return false if the CRC check fails. + return (false); + + if (_printDebug == true) + { + _debugPort->print("SCD30 begin: got firmware version 0x"); + _debugPort->println(fwVer, HEX); + } + + if (measBegin == false) // Exit now if measBegin is false + return (true); + //Check for device to respond correctly if (beginMeasuring() == true) //Start continuous measurements { @@ -70,6 +83,14 @@ bool SCD30::begin(TwoWire &wirePort, bool autoCalibrate) return (false); //Something went wrong } +//Calling this function with nothing sets the debug port to Serial +//You can also call it with other streams like Serial1, SerialUSB, etc. +void SCD30::enableDebugging(Stream &debugPort) +{ + _debugPort = &debugPort; + _printDebug = true; +} + //Returns the latest available CO2 level //If the current level has already been reported, trigger a new read uint16_t SCD30::getCO2(void) @@ -130,14 +151,19 @@ bool SCD30::setForcedRecalibrationFactor(uint16_t concentration) float SCD30::getTemperatureOffset(void) { uint16_t response = readRegister(COMMAND_SET_TEMPERATURE_OFFSET); - return (float)response / 100; + return (((float)response) / 100.0); } //Set the temperature offset. See 1.3.8. bool SCD30::setTemperatureOffset(float tempOffset) { - int16_t tickOffset = tempOffset * 100; - return sendCommand(COMMAND_SET_TEMPERATURE_OFFSET, tickOffset); + union + { + int16_t signed16; + uint16_t unsigned16; + } signedUnsigned; // Avoid any ambiguity casting int16_t to uint16_t + signedUnsigned.signed16 = tempOffset * 100; + return sendCommand(COMMAND_SET_TEMPERATURE_OFFSET, signedUnsigned.unsigned16); } //Get the altitude compenstation. See 1.3.9. @@ -167,7 +193,7 @@ bool SCD30::setAmbientPressure(uint16_t pressure_mbar) void SCD30::reset() { sendCommand(COMMAND_RESET); - + } // Get the current ASC setting @@ -179,7 +205,7 @@ bool SCD30::getAutoSelfCalibration() } else { return false; - } + } } //Begins continuous measurements @@ -198,6 +224,12 @@ bool SCD30::beginMeasuring(void) return (beginMeasuring(0)); } +// Stop continuous measurement +bool SCD30::StopMeasurement(void) +{ + return(sendCommand(COMMAND_STOP_MEAS)); +} + //Sets interval between measurements //2 seconds to 1800 seconds (30 minutes) bool SCD30::setMeasurementInterval(uint16_t interval) @@ -224,9 +256,9 @@ bool SCD30::readMeasurement() if (dataAvailable() == false) return (false); - uint32_t tempCO2 = 0; - uint32_t tempHumidity = 0; - uint32_t tempTemperature = 0; + ByteToFl tempCO2; tempCO2.value = 0; + ByteToFl tempHumidity; tempHumidity.value = 0; + ByteToFl tempTemperature; tempTemperature.value = 0; _i2cPort->beginTransmission(SCD30_ADDRESS); _i2cPort->write(COMMAND_READ_MEASUREMENT >> 8); //MSB @@ -249,32 +281,37 @@ bool SCD30::readMeasurement() case 1: case 3: case 4: - tempCO2 <<= 8; - tempCO2 |= incoming; + tempCO2.array[x < 3 ? 3-x : 4-x] = incoming; bytesToCrc[x % 3] = incoming; break; case 6: case 7: case 9: case 10: - tempTemperature <<= 8; - tempTemperature |= incoming; + tempTemperature.array[x < 9 ? 9-x : 10-x] = incoming; bytesToCrc[x % 3] = incoming; break; case 12: case 13: case 15: case 16: - tempHumidity <<= 8; - tempHumidity |= incoming; + tempHumidity.array[x < 15 ? 15-x : 16-x] = incoming; bytesToCrc[x % 3] = incoming; break; default: //Validate CRC - const uint8_t foundCrc = computeCRC8(bytesToCrc, 2); + uint8_t foundCrc = computeCRC8(bytesToCrc, 2); if (foundCrc != incoming) { - //Serial.printf("Found CRC in byte %u, expected %u, got %u\n", x, incoming, foundCrc); + if (_printDebug == true) + { + _debugPort->print(F("readMeasurement: found CRC in byte ")); + _debugPort->print(x); + _debugPort->print(F(", expected 0x")); + _debugPort->print(foundCrc, HEX); + _debugPort->print(F(", got 0x")); + _debugPort->println(incoming, HEX); + } error = true; } break; @@ -283,19 +320,25 @@ bool SCD30::readMeasurement() } else { - //Serial.printf("No SCD30 data found from I2C, i2c claims we should receive %u bytes\n", receivedBytes); + if (_printDebug == true) + { + _debugPort->print(F("readMeasurement: no SCD30 data found from I2C, i2c claims we should receive ")); + _debugPort->print(receivedBytes); + _debugPort->println(F(" bytes")); + } return false; } if (error) { - //Serial.println("Encountered error reading SCD30 data."); + if (_printDebug == true) + _debugPort->println("readMeasurement: encountered error reading SCD30 data."); return false; } //Now copy the uint32s into their associated floats - memcpy(&co2, &tempCO2, sizeof(co2)); - memcpy(&temperature, &tempTemperature, sizeof(temperature)); - memcpy(&humidity, &tempHumidity, sizeof(humidity)); + co2 = tempCO2.value; + temperature = tempTemperature.value; + humidity = tempHumidity.value; //Mark our global variables as fresh co2HasBeenReported = false; @@ -305,6 +348,38 @@ bool SCD30::readMeasurement() return (true); //Success! New data available in globals. } +//Gets a setting by reading the appropriate register. +//Returns true if the CRC is valid. +bool SCD30::getSettingValue(uint16_t registerAddress, uint16_t *val) +{ + _i2cPort->beginTransmission(SCD30_ADDRESS); + _i2cPort->write(registerAddress >> 8); //MSB + _i2cPort->write(registerAddress & 0xFF); //LSB + if (_i2cPort->endTransmission() != 0) + return (false); //Sensor did not ACK + + _i2cPort->requestFrom((uint8_t)SCD30_ADDRESS, (uint8_t)3); // Request data and CRC + if (_i2cPort->available()) + { + uint8_t data[2]; + data[0] = _i2cPort->read(); + data[1] = _i2cPort->read(); + uint8_t crc = _i2cPort->read(); + *val = (uint16_t)data[0] << 8 | data[1]; + uint8_t expectedCRC = computeCRC8(data, 2); + if (crc == expectedCRC) // Return true if CRC check is OK + return (true); + if (_printDebug == true) + { + _debugPort->print(F("getSettingValue: CRC fail: expected 0x")); + _debugPort->print(expectedCRC, HEX); + _debugPort->print(F(", got 0x")); + _debugPort->println(crc, HEX); + } + } + return (false); +} + //Gets two bytes from SCD30 uint16_t SCD30::readRegister(uint16_t registerAddress) { diff --git a/src/SparkFun_SCD30_Arduino_Library.h b/src/SparkFun_SCD30_Arduino_Library.h index 68b530f..3f39996 100644 --- a/src/SparkFun_SCD30_Arduino_Library.h +++ b/src/SparkFun_SCD30_Arduino_Library.h @@ -6,6 +6,11 @@ Written by Nathan Seidle @ SparkFun Electronics, May 22nd, 2018 + Updated February 1st 2021 to include some of the features of paulvha's version of the library + (while maintaining backward-compatibility): + https://github.com/paulvha/scd30 + Thank you Paul! + The SCD30 measures CO2 with accuracy of +/- 30ppm. This library handles the initialization of the SCD30 and outputs @@ -14,15 +19,10 @@ https://github.com/sparkfun/SparkFun_SCD30_Arduino_Library Development environment specifics: - Arduino IDE 1.8.5 - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + Arduino IDE 1.8.13 - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SparkFun code, firmware, and software is released under the MIT License. + Please see LICENSE.md for more details. */ #ifndef __SparkFun_SCD30_ARDUINO_LIBARARY_H__ @@ -53,21 +53,40 @@ #define COMMAND_SET_FORCED_RECALIBRATION_FACTOR 0x5204 #define COMMAND_SET_TEMPERATURE_OFFSET 0x5403 #define COMMAND_SET_ALTITUDE_COMPENSATION 0x5102 -#define COMMAND_RESET 0xD304 +#define COMMAND_RESET 0xD304 // Soft reset +#define COMMAND_STOP_MEAS 0x0104 +#define COMMAND_READ_FW_VER 0xD100 + +typedef union { + byte array[4]; + float value; +} ByteToFl; // paulvha class SCD30 { public: SCD30(void); - + bool begin(bool autoCalibrate) { return begin(Wire, autoCalibrate); } #ifdef USE_TEENSY3_I2C_LIB - bool begin(i2c_t3 &wirePort = Wire, bool autoCalibrate=true); //By default use Wire port + bool begin(i2c_t3 &wirePort = Wire, bool autoCalibrate=true, bool measBegin=true); //By default use Wire port #else - bool begin(TwoWire &wirePort = Wire, bool autoCalibrate=true); //By default use Wire port + bool begin(TwoWire &wirePort = Wire, bool autoCalibrate=true, bool measBegin=true); //By default use Wire port #endif + + void enableDebugging(Stream &debugPort = Serial); //Turn on debug printing. If user doesn't specify then Serial will be used. + bool beginMeasuring(uint16_t pressureOffset); bool beginMeasuring(void); + bool StopMeasurement(void); // paulvha + + // based on paulvha + bool getSettingValue(uint16_t registerAddress, uint16_t *val); + bool getForcedRecalibration(uint16_t *val) {return(getSettingValue(COMMAND_SET_FORCED_RECALIBRATION_FACTOR, val));} + bool getMeasurementInterval(uint16_t *val) {return(getSettingValue(COMMAND_SET_MEASUREMENT_INTERVAL, val));} + bool getTemperatureOffset(uint16_t *val) {return(getSettingValue(COMMAND_SET_TEMPERATURE_OFFSET, val));} + bool getAltitudeCompensation(uint16_t *val) {return(getSettingValue(COMMAND_SET_ALTITUDE_COMPENSATION, val));} + bool getFirmwareVersion(uint16_t *val) {return(getSettingValue(COMMAND_READ_FW_VER, val));} uint16_t getCO2(void); float getHumidity(void); @@ -85,7 +104,7 @@ class SCD30 bool dataAvailable(); bool readMeasurement(); - + void reset(); bool sendCommand(uint16_t command, uint16_t arguments); @@ -96,6 +115,7 @@ class SCD30 uint8_t computeCRC8(uint8_t data[], uint8_t len); private: + //Variables #ifdef USE_TEENSY3_I2C_LIB i2c_t3 *_i2cPort; //The generic connection to user's chosen I2C hardware @@ -112,5 +132,10 @@ class SCD30 bool co2HasBeenReported = true; bool humidityHasBeenReported = true; bool temperatureHasBeenReported = true; + + //Debug + Stream *_debugPort; //The stream to send debug messages to if enabled. Usually Serial. + boolean _printDebug = false; //Flag to print debugging variables + }; #endif