diff --git a/examples/Example9_SensorType/Example9_SensorType.ino b/examples/Example9_SensorType/Example9_SensorType.ino new file mode 100644 index 0000000..4f300ef --- /dev/null +++ b/examples/Example9_SensorType/Example9_SensorType.ino @@ -0,0 +1,65 @@ +/* + SCD4x Sensor Type Test + By: Alex Brudner + Based on earlier code by: Nathan Seidle and Paul Clark + SparkFun Electronics + Date: + 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! + SparkFun CO2 Humidity and Temperature Sensor - SCD40: + - https://www.sparkfun.com/products/22395 + SparkFun CO2 Humidity and Temperature Sensor - SCD41: + - https://www.sparkfun.com/products/22396 + + This example reads the serial number from the device, determines the device type, then prints the device type to serial before spinning. + + Hardware Connections: + Attach RedBoard to computer using a USB cable. + Connect SCD40/41 to RedBoard using Qwiic cable. + Open Serial Monitor at 115200 baud. +*/ + +#include + +#include "SparkFun_SCD4x_Arduino_Library.h" //Click here to get the library: http://librarymanager/All#SparkFun_SCD4x + +SCD4x mySensor; + +void setup() +{ + Serial.begin(115200); + Serial.println(F("Sensor Type test")); + Wire.begin(); + + // mySensor.enableDebugging(Serial); // Uncomment this line to get helpful debug messages on Serial + + if (mySensor.begin(false, true, false, true) == false) // Do not start periodic measurements + //measBegin_________/ | | | + //autoCalibrate__________/ | | + //skipStopPeriodicMeasurements_/ / + //pollAndSetDeviceType______________/ + { + Serial.println(F("Sensor not detected. Please check wiring. Freezing...")); + while (1) + ; + } + + scd4x_sensor_type_e* sensorType; + char serialNumber[13]; + bool success = mySensor.getSerialNumber(serialNumber); + Serial.print(F("Serial Number is: ")); + Serial.println(serialNumber); + + Serial.print(F("Getting the sensor type: SCD4")); + Serial.println(mySensor.getSensorType()); + +} + +void loop() { + // put your main code here, to run repeatedly: + Serial.println("Entering loop, spinning..."); + while(1); +} diff --git a/keywords.txt b/keywords.txt index 924536f..c734059 100644 --- a/keywords.txt +++ b/keywords.txt @@ -38,6 +38,8 @@ measureSingleShotRHTOnly KEYWORD2 sendCommand KEYWORD2 readRegister KEYWORD2 computeCRC8 KEYWORD2 +determineSensorType KEYWORD2 +getSensorType KEYWORD2 ####################################### # Constants (LITERAL1) @@ -45,6 +47,7 @@ computeCRC8 KEYWORD2 SCD4x_SENSOR_SCD40 LITERAL1 SCD4x_SENSOR_SCD41 LITERAL1 +SCD4x_SENSOR_INVALID LITERAL1 SCD4x_ADDRESS LITERAL1 SCD4x_COMMAND_START_PERIODIC_MEASUREMENT LITERAL1 SCD4x_COMMAND_READ_MEASUREMENT LITERAL1 diff --git a/library.properties b/library.properties index dc6aaca..6c0cc53 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=SparkFun SCD4x Arduino Library -version=1.0.4 +version=1.1.0 author=SparkFun Electronics maintainer=SparkFun Electronics sentence=Library for the Sensirion SCD4x family of CO2 Sensors (SCD40 and SCD41) diff --git a/src/SparkFun_SCD4x_Arduino_Library.cpp b/src/SparkFun_SCD4x_Arduino_Library.cpp index f1f7536..89afac2 100644 --- a/src/SparkFun_SCD4x_Arduino_Library.cpp +++ b/src/SparkFun_SCD4x_Arduino_Library.cpp @@ -30,9 +30,9 @@ SCD4x::SCD4x(scd4x_sensor_type_e sensorType) //Initialize the Serial port #ifdef USE_TEENSY3_I2C_LIB -bool SCD4x::begin(i2c_t3 &wirePort, bool measBegin, bool autoCalibrate, bool skipStopPeriodicMeasurements) +bool SCD4x::begin(i2c_t3 &wirePort, bool measBegin, bool autoCalibrate, bool skipStopPeriodicMeasurements, bool pollAndSetDeviceType) #else -bool SCD4x::begin(TwoWire &wirePort, bool measBegin, bool autoCalibrate, bool skipStopPeriodicMeasurements) +bool SCD4x::begin(TwoWire &wirePort, bool measBegin, bool autoCalibrate, bool skipStopPeriodicMeasurements, bool pollAndSetDeviceType) #endif { _i2cPort = &wirePort; //Grab which port the user wants us to use @@ -58,6 +58,24 @@ bool SCD4x::begin(TwoWire &wirePort, bool measBegin, bool autoCalibrate, bool sk } #endif // if SCD4x_ENABLE_DEBUGLOG + if (pollAndSetDeviceType == true) + { + scd4x_sensor_type_e* sensorType; + success &= determineSensorType(sensorType, serialNumber); + if (success == false) + return (false); + + setSensorType(*sensorType); + + #if SCD4x_ENABLE_DEBUGLOG + if (_printDebug == true) + { + _debugPort->print(F("SCD40::begin: Sensor is of type SCD4")); + _debugPort->println(_sensorType); + } + #endif // if SCD4x_ENABLE_DEBUGLOG + } + if (autoCalibrate == true) // Must be done before periodic measurements are started { success &= setAutomaticSelfCalibrationEnabled(true); @@ -856,6 +874,27 @@ char SCD4x::convertHexToASCII(uint8_t digit) return (char(digit + 0x41 - 10)); // Use upper case for A-F } +void SCD4x::convertASCIIToHex(const char *hexstr, uint16_t *integers) +{ + for (int i = 0; i < 3; i++ ) { + uint16_t val = 0; + for (int j = 0; j < 4; j++ ) { + char c = hexstr[i * 4 + j]; + val <<= 4; + if (c >= '0' && c <= '9') { + val += c - '0'; + } + else if (c >= 'A' && c <= 'F') { + val += c - 'A' + 10; + } + else if (c >= 'a' && c <= 'f') { + val += c - 'a' + 10; + } + } + integers[i] = val; + } +} + //Perform self test. Takes 10 seconds to complete. See 3.9.3 //The perform_self_test feature can be used as an end-of-line test to check sensor functionality //and the customer power supply to the sensor. @@ -1026,6 +1065,135 @@ bool SCD4x::measureSingleShotRHTOnly(void) return (success); } +// Determine Sensor type from the serial number +bool SCD4x::determineSensorType(scd4x_sensor_type_e *sensorType, char *serialNumber) +{ + bool success = true; + + if(strlen(serialNumber) != 12) + { + #if SCD4x_ENABLE_DEBUGLOG + if (_printDebug == true) + { + _debugPort->println(F("SCD40::getSensorType: S/N is not 13 bytes.")); + _debugPort->print(F("SCD40::getSensorType: Num Bytes: ")); + _debugPort->println(strlen(serialNumber)); + } + #endif // if SCD4x_ENABLE_DEBUGLOG + success = false; + return (success); + } + + uint16_t serNumInt[3]; + convertASCIIToHex(serialNumber, serNumInt); + + #if SCD4x_ENABLE_DEBUGLOG + if (_printDebug == true) + { + _debugPort->print("SCD40::getSensorType: Word 1: "); + _debugPort->println(serNumInt[0], HEX); + _debugPort->print("SCD40::getSensorType: Word 2: "); + _debugPort->println(serNumInt[1], HEX); + _debugPort->print("SCD40::getSensorType: Word 3: "); + _debugPort->println(serNumInt[2], HEX); + } + #endif // if SCD4x_ENABLE_DEBUGLOG + + *sensorType = extractMaskedSensorType(serNumInt); + + if (*sensorType == SCD4x_SENSOR_INVALID) + { + #if SCD4x_ENABLE_DEBUGLOG + if (_printDebug == true) + { + _debugPort->println("SCD40::getSensorType: Invalid Sensor Type Determined."); + } + #endif // if SCD4x_ENABLE_DEBUGLOG + *sensorType = SCD4x_SENSOR_SCD40; // Pick a default so we don't break things. + success = false; + return (success); + } + + return (success); +} + +scd4x_sensor_type_e SCD4x::getSensorType() +{ + return _sensorType; +} + +void SCD4x::setSensorType(scd4x_sensor_type_e sensorType) +{ + _sensorType = sensorType; +} + +// Extract the Sensor type (SCD40 vs SCD41) from the Serial Number of the connected device. +scd4x_sensor_type_e SCD4x::extractMaskedSensorType(uint16_t *serialNumberArray) +{ + // Of the 48-bit serial number, the Type is identified by bits 24-37 + // 0bxxxx'xxxx'xx11'1111'1111'1111'xxxx'xxxx'xxxx'xxxx'xxxx'xxxx + uint16_t masks[] = {0x003F, 0xFF00}; + + uint16_t maskedValues[2]; + uint16_t combinedValue = 0; + + for (int i = 0; i < 2; i++) { + maskedValues[i] = serialNumberArray[i] & masks[i]; + #if SCD4x_ENABLE_DEBUGLOG + if(_printDebug == true) { + _debugPort->print("SCD40::extractMaskedSensorType: i: "); + _debugPort->println(i); + _debugPort->print("SCD40::extractMaskedSensorType: serialNumberArray[i]: "); + _debugPort->println(serialNumberArray[i], HEX); + _debugPort->print("SCD40::extractMaskedSensorType: masks[i]: "); + _debugPort->println(masks[i], HEX); + _debugPort->print("SCD40::extractMaskedSensorType: maskedValues[i]: "); + _debugPort->println(maskedValues[i], HEX); + } + #endif // if SCD4x_ENABLE_DEBUGLOG + } + + combinedValue = (maskedValues[0] << 8) | (maskedValues[1] >> 8); + + #if SCD4x_ENABLE_DEBUGLOG + if (_printDebug == true) + { + _debugPort->print("SCD40::extractMaskedSensorType: Combined Value: "); + _debugPort->println(combinedValue, HEX); + } + #endif // if SCD4x_ENABLE_DEBUGLOG + + if (combinedValue == 0x376F) + { + #if SCD4x_ENABLE_DEBUGLOG + if (_printDebug == true) + { + _debugPort->println("SCD40::extractMaskedSensorType: Picked SCD41"); + } + #endif // if SCD4x_ENABLE_DEBUGLOG + return SCD4x_SENSOR_SCD41; + } + else if (combinedValue == 0x2397) + { + #if SCD4x_ENABLE_DEBUGLOG + if (_printDebug == true) + { + _debugPort->println("SCD40::extractMaskedSensorType: Picked SCD40"); + } + #endif // if SCD4x_ENABLE_DEBUGLOG + return SCD4x_SENSOR_SCD40; + } + else + { + #if SCD4x_ENABLE_DEBUGLOG + if (_printDebug == true) { + _debugPort->println("SCD40::extractMaskedSensorType: Something's seriously wrong here..."); + } + #endif // if SCD4x_ENABLE_DEBUGLOG + return SCD4x_SENSOR_INVALID; + } +} + //Sends a command along with arguments and CRC bool SCD4x::sendCommand(uint16_t command, uint16_t arguments) { diff --git a/src/SparkFun_SCD4x_Arduino_Library.h b/src/SparkFun_SCD4x_Arduino_Library.h index 8b2366b..d25f9d5 100644 --- a/src/SparkFun_SCD4x_Arduino_Library.h +++ b/src/SparkFun_SCD4x_Arduino_Library.h @@ -5,6 +5,7 @@ https://www.sparkfun.com/products/18365 Written by Paul Clark @ SparkFun Electronics, June 2nd, 2021 + Revision by Alex Brudner @ SparkFun Electronics The SCD41 measures CO2 from 400ppm to 5000ppm with an accuracy of +/- 40ppm + 5% of reading @@ -14,7 +15,7 @@ https://github.com/sparkfun/SparkFun_SCD4x_Arduino_Library Development environment specifics: - Arduino IDE 1.8.13 + Arduino IDE 1.8.13 and 2.1.0 SparkFun code, firmware, and software is released under the MIT License. Please see LICENSE.md for more details. @@ -101,7 +102,8 @@ typedef union typedef enum { SCD4x_SENSOR_SCD40 = 0, - SCD4x_SENSOR_SCD41 + SCD4x_SENSOR_SCD41, + SCD4x_SENSOR_INVALID } scd4x_sensor_type_e; class SCD4x @@ -113,10 +115,11 @@ class SCD4x bool begin(bool measBegin) { return begin(Wire, measBegin); } bool begin(bool measBegin, bool autoCalibrate) { return begin(Wire, measBegin, autoCalibrate); } bool begin(bool measBegin, bool autoCalibrate, bool skipStopPeriodicMeasurements) { return begin(Wire, measBegin, autoCalibrate, skipStopPeriodicMeasurements); } + bool begin(bool measBegin, bool autoCalibrate, bool skipStopPeriodicMeasurements, bool pollAndSetDeviceType) { return begin(Wire, measBegin, autoCalibrate, skipStopPeriodicMeasurements, pollAndSetDeviceType); } #ifdef USE_TEENSY3_I2C_LIB - bool begin(i2c_t3 &wirePort = Wire, bool measBegin = true, bool autoCalibrate = true, bool skipStopPeriodicMeasurements = false); //By default use Wire port + bool begin(i2c_t3 &wirePort = Wire, bool measBegin = true, bool autoCalibrate = true, bool skipStopPeriodicMeasurements = false, bool pollAndSetDeviceType = true); //By default use Wire port #else - bool begin(TwoWire &wirePort = Wire, bool measBegin = true, bool autoCalibrate = true, bool skipStopPeriodicMeasurements = false); //By default use Wire port + bool begin(TwoWire &wirePort = Wire, bool measBegin = true, bool autoCalibrate = true, bool skipStopPeriodicMeasurements = false, bool pollAndSetDeviceType = 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 @@ -181,6 +184,9 @@ class SCD4x uint8_t computeCRC8(uint8_t data[], uint8_t len); + bool determineSensorType(scd4x_sensor_type_e *sensorType, char *serialNumber); // Determine sensor type from serial number. + scd4x_sensor_type_e getSensorType(); // Get the sensor type stored in the struct. + private: //Variables #ifdef USE_TEENSY3_I2C_LIB @@ -191,7 +197,7 @@ class SCD4x //Sensor type scd4x_sensor_type_e _sensorType; - + //Global main datums float co2 = 0; float temperature = 0; @@ -209,6 +215,14 @@ class SCD4x //Convert serial number digit to ASCII char convertHexToASCII(uint8_t digit); + // Convert serial number string to hex digits. + void convertASCIIToHex(const char *hexstr, uint16_t *integers); + + // Helper function to compare the serial number to known IDs. + scd4x_sensor_type_e extractMaskedSensorType(uint16_t *serialNumberArray); + + void setSensorType(scd4x_sensor_type_e sensorType); // Set the sensor type for the device. + #if SCD4x_ENABLE_DEBUGLOG //Debug Stream *_debugPort; //The stream to send debug messages to if enabled. Usually Serial.