/*
 * THIS FILE IS AUTOMATICALLY GENERATED
 *
 * Generator:     sensirion-driver-generator 1.0.1
 * Product:       sen66
 * Model-Version: 1.3.0
 */
/*
 * Copyright (c) 2024, Sensirion AG
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 *
 * * 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.
 *
 * * Neither the name of Sensirion AG 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.
 */

#ifndef SENSIRIONI2CSEN66_H
#define SENSIRIONI2CSEN66_H

#include <SensirionCore.h>
#include <Wire.h>

#define SEN66_I2C_ADDR_6B 0x6b

typedef enum {
    SEN66_START_CONTINUOUS_MEASUREMENT_CMD_ID = 0x21,
    SEN66_STOP_MEASUREMENT_CMD_ID = 0x104,
    SEN66_GET_DATA_READY_CMD_ID = 0x202,
    SEN66_READ_MEASURED_VALUES_AS_INTEGERS_CMD_ID = 0x300,
    SEN66_READ_NUMBER_CONCENTRATION_VALUES_AS_INTEGERS_CMD_ID = 0x316,
    SEN66_READ_MEASURED_RAW_VALUES_CMD_ID = 0x405,
    SEN66_START_FAN_CLEANING_CMD_ID = 0x5607,
    SEN66_SET_TEMPERATURE_OFFSET_PARAMETERS_CMD_ID = 0x60b2,
    SEN66_SET_VOC_ALGORITHM_TUNING_PARAMETERS_CMD_ID = 0x60d0,
    SEN66_GET_VOC_ALGORITHM_TUNING_PARAMETERS_CMD_ID = 0x60d0,
    SEN66_SET_NOX_ALGORITHM_TUNING_PARAMETERS_CMD_ID = 0x60e1,
    SEN66_GET_NOX_ALGORITHM_TUNING_PARAMETERS_CMD_ID = 0x60e1,
    SEN66_SET_TEMPERATURE_ACCELERATION_PARAMETERS_CMD_ID = 0x6100,
    SEN66_SET_VOC_ALGORITHM_STATE_CMD_ID = 0x6181,
    SEN66_GET_VOC_ALGORITHM_STATE_CMD_ID = 0x6181,
    SEN66_PERFORM_FORCED_CO2_RECALIBRATION_CMD_ID = 0x6707,
    SEN66_SET_CO2_SENSOR_AUTOMATIC_SELF_CALIBRATION_CMD_ID = 0x6711,
    SEN66_GET_CO2_SENSOR_AUTOMATIC_SELF_CALIBRATION_CMD_ID = 0x6711,
    SEN66_SET_AMBIENT_PRESSURE_CMD_ID = 0x6720,
    SEN66_GET_AMBIENT_PRESSURE_CMD_ID = 0x6720,
    SEN66_SET_SENSOR_ALTITUDE_CMD_ID = 0x6736,
    SEN66_GET_SENSOR_ALTITUDE_CMD_ID = 0x6736,
    SEN66_ACTIVATE_SHT_HEATER_CMD_ID = 0x6765,
    SEN66_GET_PRODUCT_NAME_CMD_ID = 0xd014,
    SEN66_GET_SERIAL_NUMBER_CMD_ID = 0xd033,
    SEN66_READ_DEVICE_STATUS_CMD_ID = 0xd206,
    SEN66_READ_AND_CLEAR_DEVICE_STATUS_CMD_ID = 0xd210,
    SEN66_DEVICE_RESET_CMD_ID = 0xd304,
} SEN66CmdId;

typedef union {
    struct {
        uint32_t reserved1 : 4;
        uint32_t fanError : 1;
        uint32_t reserved2 : 1;
        uint32_t rhtError : 1;
        uint32_t gasError : 1;
        uint32_t reserved3 : 1;
        uint32_t co22Error : 1;
        uint32_t reserved4 : 1;
        uint32_t pmError : 1;
        uint32_t reserved5 : 1;
        uint32_t reserved6 : 8;
        uint32_t fanSpeedWarning : 1;
    };
    uint32_t value;
} SEN66DeviceStatus;

class SensirionI2cSen66 {
  public:
    SensirionI2cSen66();
    /**
     * @brief Initializes the SEN66 class.
     *
     * @param i2cBus Arduino stream object to be used for communication.
     */
    void begin(TwoWire& i2cBus, uint8_t i2cAddress);

    /**
     * @brief Read measured values and apply scaling as defined in datasheet.
     *
     * @param[out] massConcentrationPm1p0 Mass concentration for particles
     * smaller than 1.0 μm.
     * @param[out] massConcentrationPm2p5 Mass concentration for particles
     * smaller than 2.5 μm.
     * @param[out] massConcentrationPm4p0 Mass concentration for particles
     * smaller than 4.0 μm.
     * @param[out] massConcentrationPm10p0 Mass concentration for particles
     * smaller than 10.0 μm.
     * @param[out] humidity Measured humidity in %RH.
     * @param[out] temperature Measured temperature in degrees celsius.
     * @param[out] vocIndex Measured VOC Index between 0 and 500.
     * @param[out] noxIndex Measured NOx Index between 0 and 500.
     * @param[out] co2 Measured CO2 concentration in ppm.
     *
     * @return error_code 0 on success, an error code otherwise.
     */
    int16_t readMeasuredValues(float& massConcentrationPm1p0,
                               float& massConcentrationPm2p5,
                               float& massConcentrationPm4p0,
                               float& massConcentrationPm10p0, float& humidity,
                               float& temperature, float& vocIndex,
                               float& noxIndex, uint16_t& co2);

    /**
     * @brief Read measured number concentration values and apply scaling as
     * defined in datasheet.
     *
     * @param[out] massConcentrationPm0p5 Mass concentration for particles
     * smaller than 1.0 μm.
     * @param[out] massConcentrationPm1p0 Mass concentration for particles
     * smaller than 1.0 μm.
     * @param[out] massConcentrationPm2p5 Mass concentration for particles
     * smaller than 2.5 μm.
     * @param[out] massConcentrationPm4p0 Mass concentration for particles
     * smaller than 4.0 μm.
     * @param[out] massConcentrationPm10p0 Mass concentration for particles
     * smaller than 10.0 μm.
     *
     * @return error_code 0 on success, an error code otherwise.
     */
    int16_t readNumberConcentrationValues(float& massConcentrationPm0p5,
                                          float& massConcentrationPm1p0,
                                          float& massConcentrationPm2p5,
                                          float& massConcentrationPm4p0,
                                          float& massConcentrationPm10p0);

    /**
     * @brief Start a continuous measurement (interval 1s)
     *
     * Starts a continuous measurement. After starting the measurement, it takes
     * some time (~1.1s) until the first measurement results are available. You
     * could poll with the command "Get Data Ready" to check when the results
     * are ready to read. This command is only available in idle mode. If the
     * device is already in any measure mode, this command has no effect.
     *
     * @return error_code 0 on success, an error code otherwise.
     */
    int16_t startContinuousMeasurement();

    /**
     * @brief Stop the continuous measurement
     *
     * Stops the measurement and returns to idle mode. After sending this
     * command, wait at least 1000 ms before starting a new measurement. If the
     * device is already in idle mode, this command has no effect.
     *
     * @return error_code 0 on success, an error code otherwise.
     */
    int16_t stopMeasurement();

    /**
     * @brief Check if data is ready to be read out from the sensor
     *
     * This command can be used to check if new measurement results are ready to
     * read. The data ready flag is automatically reset after reading the
     * measurement values.
     *
     * @param[out] padding Padding byte, always 0x00.
     * @param[out] dataReady True (0x01) if data is ready, False (0x00) if not.
     * When no measurement is running, False will be returned.
     *
     * @return error_code 0 on success, an error code otherwise.
     */
    int16_t getDataReady(uint8_t& padding, bool& dataReady);

    /**
     * @brief read measured values as integers.
     *
     * Returns the measured values. The command "Get Data Ready" can be used to
     * check if new data is available since the last read operation. If no new
     * data is available, the previous values will be returned again. If no data
     * is available at all (e.g. measurement not running for at least one
     * second), all values will be at their upper limit (0xFFFF for uint16,
     * 0x7FFF for int16).
     *
     * @param[out] massConcentrationPm1p0 Value is scaled with factor 10: PM1.0
     * [µg/m³] = value / 10 *Note: If this value is unknown, 0xFFFF is
     * returned.*
     * @param[out] massConcentrationPm2p5 Value is scaled with factor 10: PM2.5
     * [µg/m³] = value / 10 *Note: If this value is unknown, 0xFFFF is
     * returned.*
     * @param[out] massConcentrationPm4p0 Value is scaled with factor 10: PM4.0
     * [µg/m³] = value / 10 *Note: If this value is unknown, 0xFFFF is
     * returned.*
     * @param[out] massConcentrationPm10p0 Value is scaled with factor 10:
     * PM10.0 [µg/m³] = value / 10 *Note: If this value is unknown, 0xFFFF is
     * returned.*
     * @param[out] ambientHumidity Value is scaled with factor 100: RH [%] =
     * value / 100 *Note: If this value is unknown, 0x7FFF is returned.*
     * @param[out] ambientTemperature Value is scaled with factor 200: T [°C] =
     * value / 200 *Note: If this value is unknown, 0x7FFF is returned.*
     * @param[out] vOCIndex Value is scaled with factor 10: VOC Index = value /
     * 10 *Note: If this value is unknown, 0x7FFF is returned.*
     * @param[out] nOxIndex Value is scaled with factor 10: NOx Index = value /
     * 10 *Note: If this value is unknown, 0x7FFF is returned. During the
     * first 10..11 seconds after power-on or device reset, this value will be
     * 0x7FFF as well.*
     * @param[out] cO2 CO₂ concentration [ppm] *Note: If this value is unknown,
     * 0xFFFF is returned. During the first 5..6 seconds after power-on or
     * device reset, this value will be 0xFFFF as well.*
     *
     * @return error_code 0 on success, an error code otherwise.
     */
    int16_t readMeasuredValuesAsIntegers(
        uint16_t& massConcentrationPm1p0, uint16_t& massConcentrationPm2p5,
        uint16_t& massConcentrationPm4p0, uint16_t& massConcentrationPm10p0,
        int16_t& ambientHumidity, int16_t& ambientTemperature,
        int16_t& vOCIndex, int16_t& nOxIndex, uint16_t& cO2);

    /**
     * @brief readNumberConcentrationValuesAsIntegers
     *
     * Returns the measured number concentration values. The command "Get Data
     * Ready" can be used to check if new data is available since the last read
     * operation. If no new data is available, the previous values will be
     * returned again. If no data is available at all (e.g. measurement not
     * running for at least one second), all values will be at their upper limit
     * (0xFFFF for uint16).
     *
     * @param[out] numberConcentrationPm0p5 Value is scaled with factor 10:
     * PM0.5 [particles/cm³] = value / 10 *Note: If this value is unknown,
     * 0xFFFF is returned.*
     * @param[out] numberConcentrationPm1p0 Value is scaled with factor 10:
     * PM1.0 [particles/cm³] = value / 10 *Note: If this value is unknown,
     * 0xFFFF is returned.*
     * @param[out] numberConcentrationPm2p5 Value is scaled with factor 10:
     * PM2.5 [particles/cm³] = value / 10 *Note: If this value is unknown,
     * 0xFFFF is returned.*
     * @param[out] numberConcentrationPm4p0 Value is scaled with factor 10:
     * PM4.0 [particles/cm³] = value / 10 *Note: If this value is unknown,
     * 0xFFFF is returned.*
     * @param[out] numberConcentrationPm10p0 Value is scaled with factor 10:
     * PM10.0 [particles/cm³] = value / 10 *Note: If this value is unknown,
     * 0xFFFF is returned.*
     *
     * @return error_code 0 on success, an error code otherwise.
     */
    int16_t readNumberConcentrationValuesAsIntegers(
        uint16_t& numberConcentrationPm0p5, uint16_t& numberConcentrationPm1p0,
        uint16_t& numberConcentrationPm2p5, uint16_t& numberConcentrationPm4p0,
        uint16_t& numberConcentrationPm10p0);

    /**
     * @brief readMeasuredRawValues
     *
     * Returns the measured raw values. The command "Get Data Ready" can be used
     * to check if new data is available since the last read operation. If no
     * new data is available, the previous values will be returned again. If no
     * data is available at all (e.g. measurement not running for at least one
     * second), all values will be at their upper limit (0xFFFF for uint16,
     * 0x7FFF for int16).
     *
     * @param[out] rawHumidity Value is scaled with factor 100: RH [%] = value /
     * 100 *Note: If this value is unknown, 0x7FFF is returned.*
     * @param[out] rawTemperature Value is scaled with factor 200: T [°C] =
     * value / 200 *Note: If this value is unknown, 0x7FFF is returned.*
     * @param[out] rawVOC Raw measured VOC ticks without scale factor. *Note: If
     * this value is unknown, 0xFFFF is returned.*
     * @param[out] rawNOx Raw measured NOx ticks without scale factor. *Note: If
     * this value is unknown, 0xFFFF is returned. During the first 10..11
     * seconds after power-on or device reset, this value will be 0xFFFF as
     * well.*
     * @param[out] rawCO2 Not interpolated CO₂ concentration [ppm] updated every
     * five seconds. *Note: If this value is unknown, 0xFFFF is returned. During
     * the first 5..6 seconds after power-on or device reset, this value will be
     * 0xFFFF as well.*
     *
     * @return error_code 0 on success, an error code otherwise.
     */
    int16_t readMeasuredRawValues(int16_t& rawHumidity, int16_t& rawTemperature,
                                  uint16_t& rawVOC, uint16_t& rawNOx,
                                  uint16_t& rawCO2);

    /**
     * @brief startFanCleaning
     *
     * This command triggers fan cleaning. The fan is set to the maximum speed
     * for 10 seconds and then automatically stopped. Wait at least 10s after
     * this command before starting a measurement.
     *
     * @note This command is only available in idle mode.
     *
     * @return error_code 0 on success, an error code otherwise.
     */
    int16_t startFanCleaning();

    /**
     * @brief setTemperatureOffsetParameters
     *
     * This command allows to compensate temperature effects of the design-in at
     * customer side by applying custom temperature offsets to the ambient
     * temperature. The compensated ambient temperature is calculated as
     * follows: T_Ambient_Compensated = T_Ambient + (slope * T_Ambient) + offset
     * Where slope and offset are the values set with this command, smoothed
     * with the specified time constant. All temperatures
     * (T_Ambient_Compensated, T_Ambient and offset) are represented in °C.
     * There are 5 temperature offset slots available that all contribute
     * additively to T_Ambient_Compensated. The default values for the
     * temperature offset parameters are all zero, meaning that
     * T_Ambient_Compensated is equal to T_Ambient by default. The parameters
     * can be changed in any state of the device, i.e. both in idle mode and in
     * measure mode.
     *
     * @param[in] offset Constant temperature offset scaled with factor 200 (T
     * [°C] = value / 200).
     * @param[in] slope Normalized temperature offset slope scaled with factor
     * 10000 (applied factor = value / 10000).
     * @param[in] timeConstant The time constant determines how fast the new
     * slope and offset will be applied. After the specified value in seconds,
     * 63% of the new slope and offset are applied. A time constant of zero
     * means the new values will be applied immediately (within the next measure
     * interval of 1 second).
     * @param[in] slot The temperature offset slot to be modified. Valid values
     * are 0 .. 4. If the value is outside this range, the parameters will not
     * be applied.
     *
     * @note This configuration is volatile, i.e. the parameters will be
     * reverted to their default value of zero after a device reset.
     *
     * @return error_code 0 on success, an error code otherwise.
     *
     * Example:
     * --------
     *
     * @code{.cpp}
     *
     *     int16_t localError = 0;
     *     localError = sensor.setTemperatureOffsetParameters(1, 10, 1, 0);
     *     if (localError != NO_ERROR) {
     *         return;
     *     }
     *
     * @endcode
     *
     */
    int16_t setTemperatureOffsetParameters(int16_t offset, int16_t slope,
                                           uint16_t timeConstant,
                                           uint16_t slot);

    /**
     * @brief setVocAlgorithmTuningParameters
     *
     * Sets the parameters to customize the VOC algorithm. This configuration is
     * volatile, i.e. the parameters will be reverted to their default values
     * after a device reset.
     *
     * @param[in] indexOffset VOC index representing typical (average)
     * conditions. Allowed values are in range 1..250. The default value is 100.
     * @param[in] learningTimeOffsetHours Time constant to estimate the VOC
     * algorithm offset from the history in hours. Past events will be forgotten
     * after about twice the learning time. Allowed values are in range 1..1000.
     * The default value is 12 hours.
     * @param[in] learningTimeGainHours Time constant to estimate the VOC
     * algorithm gain from the history in hours. Past events will be forgotten
     * after about twice the learning time. Allowed values are in range 1..1000.
     * The default value is 12 hours.
     * @param[in] gatingMaxDurationMinutes Maximum duration of gating in minutes
     * (freeze of estimator during high VOC index signal). Set to zero to
     * disable the gating. Allowed values are in range 0..3000. The default
     * value is 180 minutes
     * @param[in] stdInitial Initial estimate for standard deviation. Lower
     * value boosts events during initial learning period, but may result in
     * larger device-to-device variations. Allowed values are in range 10..5000.
     * The default value is 50.
     * @param[in] gainFactor Gain factor to amplify or to attenuate the VOC
     * index output. Allowed values are in range 1..1000. The default value is
     * 230.
     *
     * @note This command is available only in idle mode. In measure mode, this
     * command has no effect. In addition, it has no effect if at least one
     * parameter is outside the specified range.
     *
     * @return error_code 0 on success, an error code otherwise.
     *
     * Example:
     * --------
     *
     * @code{.cpp}
     *
     *     int16_t localError = 0;
     *     localError = sensor.setVocAlgorithmTuningParameters(100, 12, 12, 180,
     * 50, 230); if (localError != NO_ERROR) { return;
     *     }
     *
     * @endcode
     *
     */
    int16_t setVocAlgorithmTuningParameters(int16_t indexOffset,
                                            int16_t learningTimeOffsetHours,
                                            int16_t learningTimeGainHours,
                                            int16_t gatingMaxDurationMinutes,
                                            int16_t stdInitial,
                                            int16_t gainFactor);

    /**
     * @brief getVocAlgorithmTuningParameters
     *
     * Gets the parameters to customize the VOC algorithm.
     *
     * @param[out] indexOffset VOC index representing typical (average)
     * conditions.
     * @param[out] learningTimeOffsetHours Time constant to estimate the VOC
     * algorithm offset from the history in hours. Past events will be forgotten
     * after about twice the learning time.
     * @param[out] learningTimeGainHours Time constant to estimate the VOC
     * algorithm gain from the history in hours. Past events will be forgotten
     * after about twice the learning time.
     * @param[out] gatingMaxDurationMinutes Maximum duration of gating in
     * minutes (freeze of estimator during high VOC index signal). Zero disables
     * the gating.
     * @param[out] stdInitial Initial estimate for standard deviation. Lower
     * value boosts events during initial learning period, but may result in
     * larger device-to-device variations.
     * @param[out] gainFactor Gain factor to amplify or to attenuate the VOC
     * index output.
     *
     * @note This command is only available in idle mode.
     *
     * @return error_code 0 on success, an error code otherwise.
     */
    int16_t getVocAlgorithmTuningParameters(int16_t& indexOffset,
                                            int16_t& learningTimeOffsetHours,
                                            int16_t& learningTimeGainHours,
                                            int16_t& gatingMaxDurationMinutes,
                                            int16_t& stdInitial,
                                            int16_t& gainFactor);

    /**
     * @brief setNoxAlgorithmTuningParameters
     *
     * Sets the parameters to customize the NOx algorithm. This configuration is
     * volatile, i.e. the parameters will be reverted to their default values
     * after a device reset.
     *
     * @param[in] indexOffset NOx index representing typical (average)
     * conditions. Allowed values are in range 1..250. The default value is 1.
     * @param[in] learningTimeOffsetHours Time constant to estimate the NOx
     * algorithm offset from the history in hours. Past events will be forgotten
     * after about twice the learning time. Allowed values are in range 1..1000.
     * The default value is 12 hours.
     * @param[in] learningTimeGainHours The time constant to estimate the NOx
     * algorithm gain from the history has no impact for NOx. This parameter is
     * still in place for consistency reasons with the VOC tuning parameters
     * command. This parameter must always be set to 12 hours.
     * @param[in] gatingMaxDurationMinutes Maximum duration of gating in minutes
     * (freeze of estimator during high NOx index signal). Set to zero to
     * disable the gating. Allowed values are in range 0..3000. The default
     * value is 720 minutes.
     * @param[in] stdInitial The initial estimate for standard deviation
     * parameter has no impact for NOx. This parameter is still in place for
     * consistency reasons with the VOC tuning parameters command. This
     * parameter must always be set to 50.
     * @param[in] gainFactor Gain factor to amplify or to attenuate the NOx
     * index output. Allowed values are in range 1..1000. The default value is
     * 230.
     *
     * @note This command is available only in idle mode. In measure mode, this
     * command has no effect. In addition, it has no effect if at least one
     * parameter is outside the specified range.
     *
     * @return error_code 0 on success, an error code otherwise.
     *
     * Example:
     * --------
     *
     * @code{.cpp}
     *
     *     int16_t localError = 0;
     *     localError = sensor.setNoxAlgorithmTuningParameters(1, 12, 12, 720,
     * 50, 230); if (localError != NO_ERROR) { return;
     *     }
     *
     * @endcode
     *
     */
    int16_t setNoxAlgorithmTuningParameters(int16_t indexOffset,
                                            int16_t learningTimeOffsetHours,
                                            int16_t learningTimeGainHours,
                                            int16_t gatingMaxDurationMinutes,
                                            int16_t stdInitial,
                                            int16_t gainFactor);

    /**
     * @brief getNoxAlgorithmTuningParameters
     *
     * Gets the parameters to customize the NOx algorithm.
     *
     * @param[out] indexOffset NOx index representing typical (average)
     * conditions.
     * @param[out] learningTimeOffsetHours Time constant to estimate the NOx
     * algorithm offset from the history in hours. Past events will be forgotten
     * after about twice the learning time.
     * @param[out] learningTimeGainHours The time constant to estimate the NOx
     * algorithm gain from the history has no impact for NOx. This parameter is
     * still in place for consistency reasons with the VOC tuning parameters
     * command.
     * @param[out] gatingMaxDurationMinutes Maximum duration of gating in
     * minutes (freeze of estimator during high NOx index signal). Zero disables
     * the gating.
     * @param[out] stdInitial The initial estimate for standard deviation has no
     * impact for NOx. This parameter is still in place for consistency reasons
     * with the VOC tuning parameters command.
     * @param[out] gainFactor Gain factor to amplify or to attenuate the NOx
     * index output.
     *
     * @note This command is available only in idle mode.
     *
     * @return error_code 0 on success, an error code otherwise.
     */
    int16_t getNoxAlgorithmTuningParameters(int16_t& indexOffset,
                                            int16_t& learningTimeOffsetHours,
                                            int16_t& learningTimeGainHours,
                                            int16_t& gatingMaxDurationMinutes,
                                            int16_t& stdInitial,
                                            int16_t& gainFactor);

    /**
     * @brief setTemperatureAccelerationParameters
     *
     * This command allows to set custom temperature acceleration parameters of
     * the RH/T engine. It overwrites the default temperature acceleration
     * parameters of the RH/T engine with custom values. This configuration is
     * volatile, i.e. the parameters will be reverted to their default values
     * after a device reset.
     *
     * @param[in] k Filter constant K scaled with factor 10 (K = value / 10).
     * @param[in] p Filter constant P scaled with factor 10 (P = value / 10).
     * @param[in] t1 Time constant T1 scaled with factor 10 (T1 [s] = value /
     * 10).
     * @param[in] t2 Time constant T2 scaled with factor 10 (T2 [s] = value /
     * 10).
     *
     * @note The command is only available in idle mode.
     *
     * @return error_code 0 on success, an error code otherwise.
     *
     * Example:
     * --------
     *
     * @code{.cpp}
     *
     *     int16_t localError = 0;
     *     localError = sensor.setTemperatureAccelerationParameters(1, 1, 1, 1);
     *     if (localError != NO_ERROR) {
     *         return;
     *     }
     *
     * @endcode
     *
     */
    int16_t setTemperatureAccelerationParameters(uint16_t k, uint16_t p,
                                                 uint16_t t1, uint16_t t2);

    /**
     * @brief setVocAlgorithmState
     *
     * Sets the VOC algorithm state previously received with "Get VOC Algorithm
     * State" command. Allows restoration of the VOC algorithm state to resume
     * operation after a power cycle or device reset, skipping initial learning
     * phase. By default, the VOC Engine is reset, and the algorithm state is
     * retained if a measurement is stopped and started again. If the VOC
     * algorithm state shall be reset, a device reset, or a power cycle can be
     * executed.
     *
     * @param[in] state VOC algorithm state to restore.
     *
     * @note This command is only available in idle mode and the state will be
     * applied only once when starting the next measurement. In measure mode,
     * this command has no effect.
     *
     * @return error_code 0 on success, an error code otherwise.
     */
    int16_t setVocAlgorithmState(const uint8_t state[], uint16_t stateSize);

    /**
     * @brief getVocAlgorithmState
     *
     * Gets the current VOC algorithm state. Allows to backup and restore the
     * VOC algorithm state to resume operation after a power cycle or device
     * reset, skipping initial learning phase. By default, the VOC Engine is
     * reset and the algorithm state  is retained if a measurement is stopped
     * and started again. If the VOC algorithm state shall be reset, a device
     * reset or a power cycle can be executed.
     *
     * @param[out] state Current VOC algorithm state.
     *
     * @note This command can be used either in measure mode or in idle mode
     * (which will then return the state at the time when the measurement was
     * stopped). In measure mode, the state can be read each measure interval to
     * always have the latest state available, even in case of a sudden power
     * loss.
     *
     * @return error_code 0 on success, an error code otherwise.
     */
    int16_t getVocAlgorithmState(uint8_t state[], uint16_t stateSize);

    /**
     * @brief Perform Forced CO₂ Recalibration
     *
     * Execute the forced recalibration (FRC) of the CO₂. See the datasheet of
     * the SCD4x sensor for details how the forced recalibration shall be used.
     *
     * @param[in] targetCo2Concentration Target CO₂ concentration [ppm] of the
     * test setup.
     * @param[out] correction Correction value as received from the SCD [ppm
     * CO₂]. FRC correction [ppm CO₂] is calculated as follows: FRC =
     * return_value - 0x8000 If the recalibration has failed this returned value
     * is 0xFFFF.
     *
     * @note After power-on wait at least 1000 ms and after stopping a
     * measurement 600 ms before sending this command. This command is not
     * available in measure mode. The recalibration procedure will take about
     * 500 ms to complete, during which time no other functions can be executed.
     *
     * @return error_code 0 on success, an error code otherwise.
     */
    int16_t performForcedCo2Recalibration(uint16_t targetCo2Concentration,
                                          uint16_t& correction);

    /**
     * @brief setCo2SensorAutomaticSelfCalibration
     *
     * Sets the status of the CO₂ sensor automatic self-calibration (ASC). The
     * CO₂ sensor supports automatic self calibration (ASC) for long-term
     * stability of the CO₂ output. This feature can be enabled or disabled. By
     * default it is enabled. This configuration is volatile, i.e. the parameter
     * will be reverted to its default value after a device restart.
     *
     * @param[in] status Set to true (0x0001) to enable or false (0x0000) to
     * disable the automatic CO₂ measurement self calibration feature. High byte
     * of uint16 is padding and always 0x00.
     *
     * @note This command is only available in idle mode.
     *
     * @return error_code 0 on success, an error code otherwise.
     *
     * Example:
     * --------
     *
     * @code{.cpp}
     *
     *     int16_t localError = 0;
     *     localError = sensor.setCo2SensorAutomaticSelfCalibration(0);
     *     if (localError != NO_ERROR) {
     *         return;
     *     }
     *
     * @endcode
     *
     */
    int16_t setCo2SensorAutomaticSelfCalibration(uint16_t status);

    /**
     * @brief getCo2SensorAutomaticSelfCalibration
     *
     * The CO₂ sensor supports automatic self calibration (ASC) for long-term
     * stability of the CO₂ output. This feature can be enabled or disabled. By
     * default it is enabled. This configuration is volatile, i.e. the parameter
     * will be reverted to its default value after a device restart.
     *
     * @param[out] padding Padding byte, always 0x00.
     * @param[out] status Is set true (0x01) if the automatic self calibration
     * is enabled or false (0x00) if the automatic self calibration is disabled.
     *
     * @note This command is only available in idle mode.
     *
     * @return error_code 0 on success, an error code otherwise.
     */
    int16_t getCo2SensorAutomaticSelfCalibration(uint8_t& padding,
                                                 bool& status);

    /**
     * @brief setAmbientPressure
     *
     * The ambient pressure can be used for pressure compensation in the CO₂
     * sensor. Setting an ambient pressure overrides any pressure compensation
     * based on a previously set sensor altitude. Use of this command is
     * recommended for applications experiencing significant ambient pressure
     * changes to ensure CO₂ sensor accuracy. Valid input values are between 700
     * to 1'200 hPa. The default value is 1013 hPa. This configuration is
     * volatile, i.e. the parameter will be reverted to its default value after
     * a device restart.
     *
     * @param[in] ambientPressure Ambient pressure [hPa] to be used for pressure
     * compensation.
     *
     * @note This command can be used in any state of the device, i.e. both in
     * idle mode and in measure mode.
     *
     * @return error_code 0 on success, an error code otherwise.
     *
     * Example:
     * --------
     *
     * @code{.cpp}
     *
     *     int16_t localError = 0;
     *     localError = sensor.setAmbientPressure(1013);
     *     if (localError != NO_ERROR) {
     *         return;
     *     }
     *
     * @endcode
     *
     */
    int16_t setAmbientPressure(uint16_t ambientPressure);

    /**
     * @brief getAmbientPressure
     *
     * Gets the ambient pressure value. The ambient pressure can be used for
     * pressure compensation in the CO₂ sensor.
     *
     * @param[out] ambientPressure Currently used ambient pressure [hPa] for
     * pressure compensation.
     *
     * @note This command can be used in any state of the device, i.e. both in
     * idle mode and in measure mode.
     *
     * @return error_code 0 on success, an error code otherwise.
     */
    int16_t getAmbientPressure(uint16_t& ambientPressure);

    /**
     * @brief setSensorAltitude
     *
     * The sensor altitude can be used for pressure compensation in the CO₂
     * sensor. The default sensor altitude value is set to 0 meters above sea
     * level. Valid input values are between 0 and 3000m. This configuration is
     * volatile, i.e. the parameter will be reverted to its default value after
     * a device reset.
     *
     * @param[in] altitude Sensor altitude [m], valid input between 0 and 3000m.
     *
     * @return error_code 0 on success, an error code otherwise.
     *
     * Example:
     * --------
     *
     * @code{.cpp}
     *
     *     int16_t localError = 0;
     *     localError = sensor.setSensorAltitude(0);
     *     if (localError != NO_ERROR) {
     *         return;
     *     }
     *
     * @endcode
     *
     */
    int16_t setSensorAltitude(uint16_t altitude);

    /**
     * @brief getSensorAltitude
     *
     * Gets the current sensor altitude. The sensor altitude can be used for
     * pressure compensation in the CO₂ sensor.
     *
     * @param[out] altitude Current sensor altitude [m].
     *
     * @note This command is only available in idle mode.
     *
     * @return error_code 0 on success, an error code otherwise.
     */
    int16_t getSensorAltitude(uint16_t& altitude);

    /**
     * @brief Activate SHT Heater
     *
     * This command allows to use the inbuilt heater in SHT sensor to reverse
     * creep at high humidity. This command activates the SHT sensor heater with
     * 200mW for 1s. The heater is then automatically deactivated again. Wait at
     * least 20s after this command before starting a measurement to get
     * coherent temperature values (heating consequence to disappear).
     *
     * @note This command is only available in idle mode.
     *
     * @return error_code 0 on success, an error code otherwise.
     */
    int16_t activateShtHeater();

    /**
     * @brief getProductName
     *
     * Gets the product name from the device.
     *
     * @param[out] productName Null-terminated ASCII string containing the
     * product name. Up to 32 characters can be read from the device.
     *
     * @return error_code 0 on success, an error code otherwise.
     */
    int16_t getProductName(uint8_t productName[], uint16_t productNameSize);

    /**
     * @brief getSerialNumber
     *
     * Gets the serial number from the device.
     *
     * @param[out] serialNumber Null-terminated ASCII string containing the
     * serial number. Up to 32 characters can be read from the device.
     *
     * @return error_code 0 on success, an error code otherwise.
     */
    int16_t getSerialNumber(uint8_t serialNumber[], uint16_t serialNumberSize);

    /**
     * @brief readDeviceStatus
     *
     * Reads the current device status. Use this command to get detailed
     * information about the device status. The device status is encoded in
     * flags. Each device status flag represents a single bit in a 32-bit
     * integer value. If more than one error is present, the device status
     * register value is the sum of the corresponding flag values. For details
     * about the available flags, refer to the device status flags documentation
     * in the data sheet.
     *
     * @param[out] deviceStatus Device status (32 flags as an integer value).
     * For details, please refer to the device status flags documentation in the
     * datasheet.
     *
     * @note The status flags of type "Error" are sticky, i.e. they are not
     * cleared automatically even if the error condition no longer exists. So
     * they can only be cleared manually with the command "Read And Clear Device
     * Status" or with a device reset. All other flags are not sticky, i.e. they
     * are cleared automatically if the trigger condition disappears.
     *
     * @return error_code 0 on success, an error code otherwise.
     */
    int16_t readDeviceStatus(SEN66DeviceStatus& deviceStatus);

    /**
     * @brief readAndClearDeviceStatus
     *
     * Reads the current device status (like command 0xD206 "Read Device
     * Status") and afterwards clears all flags.
     *
     * @param[out] deviceStatus Device status (32 flags as an integer value)
     * **before** clearing it. For details, please refer to the device status
     * flags documentation.
     *
     * @return error_code 0 on success, an error code otherwise.
     */
    int16_t readAndClearDeviceStatus(SEN66DeviceStatus& deviceStatus);

    /**
     * @brief deviceReset
     *
     * Executes a reset on the device. This has the same effect as a power
     * cycle.
     *
     * @return error_code 0 on success, an error code otherwise.
     */
    int16_t deviceReset();

    /**
     * @brief signalMassConcentrationPm0p5
     *
     * @param[in] massConcentrationPm0p5Raw
     *
     * @return Mass concentration for particles smaller than 0.5 μm
     */
    static float
    signalMassConcentrationPm0p5(uint16_t massConcentrationPm0p5Raw);

    /**
     * @brief signalMassConcentrationPm1p0
     *
     * @param[in] massConcentrationPm1p0Raw
     *
     * @return Mass concentration for particles smaller than 1.0 μm
     */
    static float
    signalMassConcentrationPm1p0(uint16_t massConcentrationPm1p0Raw);

    /**
     * @brief signalMassConcentrationPm2p5
     *
     * @param[in] massConcentrationPm2p5Raw
     *
     * @return Mass concentration for particles smaller than 2.5 μm
     */
    static float
    signalMassConcentrationPm2p5(uint16_t massConcentrationPm2p5Raw);

    /**
     * @brief signalMassConcentrationPm4p0
     *
     * @param[in] massConcentrationPm4p0Raw
     *
     * @return Mass concentration for particles smaller than 4.0 μm
     */
    static float
    signalMassConcentrationPm4p0(uint16_t massConcentrationPm4p0Raw);

    /**
     * @brief signalMassConcentrationPm10p0
     *
     * @param[in] massConcentrationPm10p0Raw
     *
     * @return Mass concentration for particles smaller than 10.0 μm
     */
    static float
    signalMassConcentrationPm10p0(uint16_t massConcentrationPm10p0Raw);

    /**
     * @brief signalTemperature
     *
     * @param[in] temperatureRaw
     *
     * @return Measured temperature in degrees celsius. The raw value is scaled
     * appropriately.
     */
    static float signalTemperature(int16_t temperatureRaw);

    /**
     * @brief signalHumidity
     *
     * @param[in] humidityRaw
     *
     * @return Measured humidity in %RH. The raw value is scaled appropriately.
     */
    static float signalHumidity(int16_t humidityRaw);

    /**
     * @brief signalVocIndex
     *
     * @param[in] vocIndexRaw
     *
     * @return Measured VOC Index ticks.
     */
    static float signalVocIndex(int16_t vocIndexRaw);

    /**
     * @brief signalNoxIndex
     *
     * @param[in] noxIndexRaw
     *
     * @return Measured NOx Index ticks.
     */
    static float signalNoxIndex(int16_t noxIndexRaw);

    /**
     * @brief signalCo2
     *
     * @param[in] co2Raw
     *
     * @return Measured CO2 in ppm.
     */
    static uint16_t signalCo2(uint16_t co2Raw);

  private:
    TwoWire* _i2cBus = nullptr;
    uint8_t _i2cAddress = 0;
};

#endif  // SENSIRIONI2CSEN66_H