diff --git a/library.properties b/library.properties index 66f28cb0..251c9c3d 100644 --- a/library.properties +++ b/library.properties @@ -7,4 +7,4 @@ paragraph=Arduino application for Adafruit.io WipperSnapper category=Communication url=https://github.com/adafruit/Adafruit_Wippersnapper_Arduino architectures=* -depends=Adafruit NeoPixel, Adafruit SPIFlash, ArduinoJson, Adafruit DotStar, Adafruit HDC302x, Adafruit INA219, Adafruit LTR329 and LTR303, Adafruit LTR390 Library, Adafruit MCP3421, Adafruit NAU7802 Library, Adafruit SleepyDog Library, Adafruit TMP117, Adafruit TinyUSB Library, Adafruit AHTX0, Adafruit BME280 Library, Adafruit BMP280 Library, Adafruit BMP3XX Library, Adafruit DPS310, Adafruit DS248x, Adafruit SCD30, Adafruit SGP30 Sensor, Adafruit SGP40 Sensor, Sensirion I2C SCD4x, Sensirion I2C SEN5X, arduino-sht, Adafruit Si7021 Library, Adafruit MQTT Library, Adafruit MS8607, Adafruit MCP9808 Library, Adafruit MCP9600 Library, Adafruit MPL115A2, Adafruit MPRLS Library, Adafruit TSL2591 Library, Adafruit_VL53L0X, Adafruit VL53L1X, STM32duino VL53L4CD, STM32duino VL53L4CX, Adafruit_VL6180X, Adafruit PM25 AQI Sensor, Adafruit VCNL4020 Library, Adafruit VCNL4040, Adafruit VCNL4200 Library, Adafruit VEML7700 Library, Adafruit LC709203F, Adafruit LPS2X, Adafruit LPS35HW, Adafruit seesaw Library, Adafruit BME680 Library, Adafruit MAX1704X, Adafruit ADT7410 Library, Adafruit HTS221, Adafruit HTU21DF Library, Adafruit HTU31D Library, Adafruit PCT2075, hp_BH1750, ENS160 - Adafruit Fork +depends=Adafruit NeoPixel, Adafruit SPIFlash, ArduinoJson, Adafruit DotStar, Adafruit HDC302x, Adafruit INA219, Adafruit LTR329 and LTR303, Adafruit LTR390 Library, Adafruit MCP3421, Adafruit NAU7802 Library, Adafruit SleepyDog Library, Adafruit TMP117, Adafruit TinyUSB Library, Adafruit AHTX0, Adafruit BME280 Library, Adafruit BMP280 Library, Adafruit BMP3XX Library, Adafruit DPS310, Adafruit DS248x, Adafruit SCD30, Adafruit SGP30 Sensor, Adafruit SGP40 Sensor, Sensirion I2C SCD4x, Sensirion I2C SEN5X, Sensirion I2C SEN66, arduino-sht, Adafruit Si7021 Library, Adafruit MQTT Library, Adafruit MS8607, Adafruit MCP9808 Library, Adafruit MCP9600 Library, Adafruit MPL115A2, Adafruit MPRLS Library, Adafruit TSL2591 Library, Adafruit_VL53L0X, Adafruit VL53L1X, STM32duino VL53L4CD, STM32duino VL53L4CX, Adafruit_VL6180X, Adafruit PM25 AQI Sensor, Adafruit VCNL4020 Library, Adafruit VCNL4040, Adafruit VCNL4200 Library, Adafruit VEML7700 Library, Adafruit LC709203F, Adafruit LPS2X, Adafruit LPS35HW, Adafruit seesaw Library, Adafruit BME680 Library, Adafruit MAX1704X, Adafruit ADT7410 Library, Adafruit HTS221, Adafruit HTU21DF Library, Adafruit HTU31D Library, Adafruit PCT2075, hp_BH1750, ENS160 - Adafruit Fork diff --git a/platformio.ini b/platformio.ini index fd5b7453..f6b7f0e1 100644 --- a/platformio.ini +++ b/platformio.ini @@ -85,6 +85,7 @@ lib_deps = https://github.com/Sensirion/arduino-sht.git https://github.com/Sensirion/arduino-i2c-scd4x.git https://github.com/Sensirion/arduino-i2c-sen5x.git + https://github.com/Sensirion/arduino-i2c-sen66.git https://github.com/adafruit/WiFiNINA.git https://github.com/Starmbi/hp_BH1750.git https://github.com/adafruit/Adafruit_TinyUSB_Arduino.git diff --git a/src/components/i2c/WipperSnapper_I2C.cpp b/src/components/i2c/WipperSnapper_I2C.cpp index 173e8fa1..dfae4db4 100644 --- a/src/components/i2c/WipperSnapper_I2C.cpp +++ b/src/components/i2c/WipperSnapper_I2C.cpp @@ -600,6 +600,22 @@ bool WipperSnapper_Component_I2C::initI2CDevice( _sen5x->configureDriver(msgDeviceInitReq); drivers.push_back(_sen5x); WS_DEBUG_PRINTLN("SEN5X Initialized Successfully!"); + } else if ((strcmp("sen6x", msgDeviceInitReq->i2c_device_name) == 0) || + (strcmp("sen60", msgDeviceInitReq->i2c_device_name) == 0) || + (strcmp("sen63C", msgDeviceInitReq->i2c_device_name) == 0) || + (strcmp("sen65", msgDeviceInitReq->i2c_device_name) == 0) || + (strcmp("sen66", msgDeviceInitReq->i2c_device_name) == 0) || + (strcmp("sen68", msgDeviceInitReq->i2c_device_name) == 0)) { + _sen6x = new WipperSnapper_I2C_Driver_SEN6X(this->_i2c, i2cAddress); + if (!_sen6x->begin()) { + WS_DEBUG_PRINTLN("ERROR: Failed to initialize SEN6X!"); + _busStatusResponse = + wippersnapper_i2c_v1_BusResponse_BUS_RESPONSE_DEVICE_INIT_FAIL; + return false; + } + _sen6x->configureDriver(msgDeviceInitReq); + drivers.push_back(_sen6x); + WS_DEBUG_PRINTLN("SEN6X Initialized Successfully!"); } else if ((strcmp("sht40", msgDeviceInitReq->i2c_device_name) == 0) || (strcmp("sht41", msgDeviceInitReq->i2c_device_name) == 0) || (strcmp("sht45", msgDeviceInitReq->i2c_device_name) == 0)) { diff --git a/src/components/i2c/WipperSnapper_I2C.h b/src/components/i2c/WipperSnapper_I2C.h index e992f275..0c514b81 100644 --- a/src/components/i2c/WipperSnapper_I2C.h +++ b/src/components/i2c/WipperSnapper_I2C.h @@ -53,6 +53,7 @@ #include "drivers/WipperSnapper_I2C_Driver_SCD30.h" #include "drivers/WipperSnapper_I2C_Driver_SCD4X.h" #include "drivers/WipperSnapper_I2C_Driver_SEN5X.h" +#include "drivers/WipperSnapper_I2C_Driver_SEN6X.h" #include "drivers/WipperSnapper_I2C_Driver_SGP30.h" #include "drivers/WipperSnapper_I2C_Driver_SGP40.h" #include "drivers/WipperSnapper_I2C_Driver_SHT3X.h" @@ -164,6 +165,7 @@ class WipperSnapper_Component_I2C { WipperSnapper_I2C_Driver_VEML7700 *_veml7700 = nullptr; WipperSnapper_I2C_Driver_SCD4X *_scd40 = nullptr; WipperSnapper_I2C_Driver_SEN5X *_sen5x = nullptr; + WipperSnapper_I2C_Driver_SEN6X *_sen6x = nullptr; WipperSnapper_I2C_Driver_SGP30 *_sgp30 = nullptr; WipperSnapper_I2C_Driver_SGP40 *_sgp40 = nullptr; WipperSnapper_I2C_Driver_PCT2075 *_pct2075 = nullptr; diff --git a/src/components/i2c/drivers/WipperSnapper_I2C_Driver_SEN6X.h b/src/components/i2c/drivers/WipperSnapper_I2C_Driver_SEN6X.h new file mode 100644 index 00000000..9999de5c --- /dev/null +++ b/src/components/i2c/drivers/WipperSnapper_I2C_Driver_SEN6X.h @@ -0,0 +1,340 @@ +/*! + * @file WipperSnapper_I2C_Driver_SEN6X.h + * + * Device driver for the SEN66 Particulate Matter, Temperature, Humidity, VOC, NOX, and CO2 sensor. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Marni Brewster 2022 for Adafruit Industries. + * Modified (c) by Martin Ebner 2024 https://github.com/MartinEbnerSensirion + * + * MIT license, all text here must be included in any redistribution. + * + */ + +#ifndef WipperSnapper_I2C_Driver_SEN6X_H +#define WipperSnapper_I2C_Driver_SEN6X_H + +#include "WipperSnapper_I2C_Driver.h" +#include +#include + +/**************************************************************************/ +/*! + @brief Class that provides a driver interface for the SEN6X sensor. +*/ +/**************************************************************************/ +class WipperSnapper_I2C_Driver_SEN6X : public WipperSnapper_I2C_Driver { + + const float OVERFLOW_SEN6X = (0xFFFF / 10); // maxes out at u_int16 / 10 + +public: + /*******************************************************************************/ + /*! + @brief Constructor for a SEN6X sensor. + @param i2c + The I2C interface. + @param sensorAddress + 7-bit device address. + */ + /*******************************************************************************/ + WipperSnapper_I2C_Driver_SEN6X(TwoWire *i2c, uint16_t sensorAddress) + : WipperSnapper_I2C_Driver(i2c, sensorAddress) { + _i2c = i2c; + _sensorAddress = sensorAddress; + } + + /*******************************************************************************/ + /*! + @brief Initializes the SEN6X sensor and begins I2C. + @returns True if initialized successfully, False otherwise. + */ + /*******************************************************************************/ + bool begin() { + _sen = new SensirionI2cSen66(); + _sen->begin(*_i2c, (uint8_t)_sensorAddress); + u_int16_t error_stop = _sen->deviceReset(); + if (error_stop != 0) { + return false; + } + // Wait 1 second for sensors to start recording + 100ms for reset command + delay(1100); + u_int16_t error_start = _sen->startContinuousMeasurement(); + if (error_start != 0) { + return false; + } + + return true; + } + + /*******************************************************************************/ + /*! + @brief Gets the SEN6X's current temperature. + @param tempEvent + Pointer to an Adafruit_Sensor event. + @returns True if the temperature was obtained successfully, False + otherwise. + */ + /*******************************************************************************/ + bool getEventAmbientTemp(sensors_event_t *tempEvent) { + float massConcentrationPm1p0, massConcentrationPm2p5, + massConcentrationPm4p0, massConcentrationPm10p0, ambientHumidity, + ambientTemperature, vocIndex, noxIndex; + uint16_t co2; + uint16_t error; + + error = _sen->readMeasuredValues( + massConcentrationPm1p0, massConcentrationPm2p5, massConcentrationPm4p0, + massConcentrationPm10p0, ambientHumidity, ambientTemperature, vocIndex, + noxIndex, co2); + if ((_tempSensorPeriod != 0 && error != 0) || ambientTemperature == NAN) { + return false; + } + + tempEvent->temperature = ambientTemperature; + return true; + } + + /*******************************************************************************/ + /*! + @brief Gets the SEN6X's current relative humidity reading. + @param humidEvent + Pointer to an Adafruit_Sensor event. + @returns True if the humidity was obtained successfully, False + otherwise. + */ + /*******************************************************************************/ + bool getEventRelativeHumidity(sensors_event_t *humidEvent) { + float massConcentrationPm1p0, massConcentrationPm2p5, + massConcentrationPm4p0, massConcentrationPm10p0, ambientHumidity, + ambientTemperature, vocIndex, noxIndex; + uint16_t co2; + uint16_t error; + + error = _sen->readMeasuredValues( + massConcentrationPm1p0, massConcentrationPm2p5, massConcentrationPm4p0, + massConcentrationPm10p0, ambientHumidity, ambientTemperature, vocIndex, + noxIndex, co2); + if ((_humidSensorPeriod != 0 && error != 0) || ambientHumidity == NAN) { + return false; + } + + humidEvent->relative_humidity = ambientHumidity; + return true; + } + + /*******************************************************************************/ + /*! + @brief Gets the SEN6X's current NOX reading. + Note: If this value is unknown, which is true for SEN54, + NAN is returned. During the first 10..11 seconds after + power-on or device reset, this value will be NAN as well. + @param noxIndexEvent + Adafruit Sensor event for NOx Index (0-500, 1 is normal) + @returns True if the sensor value was obtained successfully, False + otherwise. + */ + /*******************************************************************************/ + bool getEventNOxIndex(sensors_event_t *noxIndexEvent) { + float massConcentrationPm1p0, massConcentrationPm2p5, + massConcentrationPm4p0, massConcentrationPm10p0, ambientHumidity, + ambientTemperature, vocIndex, noxIndex; + uint16_t co2; + uint16_t error; + + error = _sen->readMeasuredValues( + massConcentrationPm1p0, massConcentrationPm2p5, massConcentrationPm4p0, + massConcentrationPm10p0, ambientHumidity, ambientTemperature, vocIndex, + noxIndex, co2); + if ((_NOxIndexPeriod != 0 && error != 0) || noxIndex == NAN) { + return false; + } + + noxIndexEvent->nox_index = noxIndex; + return true; + } + + /*******************************************************************************/ + /*! + @brief Gets the SEN6X's current VOC reading. + @param vocIndexEvent + Adafruit Sensor event for VOC Index (1-500, 100 is normal) + @returns True if the sensor value was obtained successfully, False + otherwise. + */ + /*******************************************************************************/ + bool getEventVOCIndex(sensors_event_t *vocIndexEvent) { + float massConcentrationPm1p0, massConcentrationPm2p5, + massConcentrationPm4p0, massConcentrationPm10p0, ambientHumidity, + ambientTemperature, vocIndex, noxIndex; + uint16_t co2; + uint16_t error; + + error = _sen->readMeasuredValues( + massConcentrationPm1p0, massConcentrationPm2p5, massConcentrationPm4p0, + massConcentrationPm10p0, ambientHumidity, ambientTemperature, vocIndex, + noxIndex, co2); + if ((_VOCIndexPeriod != 0 && error != 0) || vocIndex == NAN) { + return false; + } + + vocIndexEvent->voc_index = vocIndex; + return true; + } + + /*******************************************************************************/ + /*! + @brief Gets the SEN6X sensor's PM1.0 STD reading. + @param pm10StdEvent + Adafruit Sensor event for PM1.0 + @returns True if the sensor value was obtained successfully, False + otherwise. + */ + /*******************************************************************************/ + bool getEventPM10_STD(sensors_event_t *pm10StdEvent) { + float massConcentrationPm1p0, massConcentrationPm2p5, + massConcentrationPm4p0, massConcentrationPm10p0, ambientHumidity, + ambientTemperature, vocIndex, noxIndex; + uint16_t co2; + uint16_t error; + + error = _sen->readMeasuredValues( + massConcentrationPm1p0, massConcentrationPm2p5, massConcentrationPm4p0, + massConcentrationPm10p0, ambientHumidity, ambientTemperature, vocIndex, + noxIndex, co2); + if ((_PM10SensorPeriod != 0 && error != 0) || + massConcentrationPm1p0 == NAN || + massConcentrationPm1p0 == OVERFLOW_SEN6X) { + return false; + } + + pm10StdEvent->pm10_std = massConcentrationPm1p0; + return true; + } + + /*******************************************************************************/ + /*! + @brief Gets the SEN6X sensor's PM2.5 STD reading. + @param pm25StdEvent + Adafruit Sensor event for PM2.5 + @returns True if the sensor value was obtained successfully, False + otherwise. + */ + /*******************************************************************************/ + bool getEventPM25_STD(sensors_event_t *pm25StdEvent) { + float massConcentrationPm1p0, massConcentrationPm2p5, + massConcentrationPm4p0, massConcentrationPm10p0, ambientHumidity, + ambientTemperature, vocIndex, noxIndex; + uint16_t co2; + uint16_t error; + + error = _sen->readMeasuredValues( + massConcentrationPm1p0, massConcentrationPm2p5, massConcentrationPm4p0, + massConcentrationPm10p0, ambientHumidity, ambientTemperature, vocIndex, + noxIndex, co2); + if ((_PM25SensorPeriod != 0 && error != 0) || + massConcentrationPm2p5 == NAN || + massConcentrationPm2p5 == OVERFLOW_SEN6X) { + return false; + } + + pm25StdEvent->pm25_std = massConcentrationPm2p5; + return true; + } + + /*******************************************************************************/ + /*! + @brief Gets the SEN6X sensor's PM4.0 STD reading. + @param pm40StdEvent + Adafruit Sensor event for PM4.0 + @returns True if the sensor value was obtained successfully, False + otherwise. + */ + /*******************************************************************************/ + bool getEventPM40_STD(sensors_event_t *pm40StdEvent) { + float massConcentrationPm1p0, massConcentrationPm2p5, + massConcentrationPm4p0, massConcentrationPm10p0, ambientHumidity, + ambientTemperature, vocIndex, noxIndex; + uint16_t co2; + uint16_t error; + + error = _sen->readMeasuredValues( + massConcentrationPm1p0, massConcentrationPm2p5, massConcentrationPm4p0, + massConcentrationPm10p0, ambientHumidity, ambientTemperature, vocIndex, + noxIndex, co2); + if ((_PM25SensorPeriod != 0 && error != 0) || + massConcentrationPm4p0 == NAN || + massConcentrationPm4p0 == OVERFLOW_SEN6X) { + return false; + } + + pm40StdEvent->data[0] = massConcentrationPm4p0; + return true; + } + + /*******************************************************************************/ + /*! + @brief Gets the SEN6X sensor's PM10.0 STD reading. + @param pm100StdEvent + Adafruit Sensor event for PM10.0 + @returns True if the sensor value was obtained successfully, False + otherwise. + */ + /*******************************************************************************/ + bool getEventPM100_STD(sensors_event_t *pm100StdEvent) { + float massConcentrationPm1p0, massConcentrationPm2p5, + massConcentrationPm4p0, massConcentrationPm10p0, ambientHumidity, + ambientTemperature, vocIndex, noxIndex; + uint16_t co2; + uint16_t error; + + error = _sen->readMeasuredValues( + massConcentrationPm1p0, massConcentrationPm2p5, massConcentrationPm4p0, + massConcentrationPm10p0, ambientHumidity, ambientTemperature, vocIndex, + noxIndex, co2); + if ((_PM100SensorPeriod != 0 && error != 0) || + massConcentrationPm10p0 == NAN || + massConcentrationPm10p0 == OVERFLOW_SEN6X) { + return false; + } + + pm100StdEvent->pm100_std = massConcentrationPm10p0; + return true; + } + + /*******************************************************************************/ + /*! + @brief Gets the SEN6X sensor's CO2 reading. + @param co2Event + Adafruit Sensor event for CO2 + @returns True if the sensor value was obtained successfully, False + otherwise. + */ + /*******************************************************************************/ + bool getEventCO2(sensors_event_t *co2Event) { + float massConcentrationPm1p0, massConcentrationPm2p5, + massConcentrationPm4p0, massConcentrationPm10p0, ambientHumidity, + ambientTemperature, vocIndex, noxIndex; + uint16_t co2; + uint16_t error; + + error = _sen->readMeasuredValues( + massConcentrationPm1p0, massConcentrationPm2p5, massConcentrationPm4p0, + massConcentrationPm10p0, ambientHumidity, ambientTemperature, vocIndex, + noxIndex, co2); + + if ((_CO2SensorPeriod != 0 && error != 0) || co2 == NAN) { + return false; + } + + co2Event->CO2 = co2; + return true; + } + +protected: + SensirionI2cSen66 *_sen; ///< SEN6X driver object +}; + +#endif // WipperSnapper_I2C_Driver_SEN6X