/*
 * THIS FILE IS AUTOMATICALLY GENERATED
 *
 * Generator:     sensirion-driver-generator 1.7.0
 * Product:       sen63c
 * Model-Version: 1.3.1
 */
/*
 * Copyright (c) 2026, 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.
 */

#include "SensirionI2cSen63c.h"
#include <Arduino.h>

// make sure that we use the proper definition of NO_ERROR
#ifdef NO_ERROR
#undef NO_ERROR
#endif
#define NO_ERROR 0

static uint8_t communication_buffer[48] = {0};

SensirionI2cSen63c::SensirionI2cSen63c() {
}

float SensirionI2cSen63c::signalDividedBy10Uint16(uint16_t scaledIntegerValue) {
    float dividedBy10Uint16 = 0.0;

    dividedBy10Uint16 = scaledIntegerValue / 10.0;
    return dividedBy10Uint16;
}

float SensirionI2cSen63c::signalTemperature(int16_t temperatureRaw) {
    float temperature = 0.0;

    temperature = temperatureRaw / 200.0;
    return temperature;
}

float SensirionI2cSen63c::signalHumidity(int16_t humidityRaw) {
    float humidity = 0.0;

    humidity = humidityRaw / 100.0;
    return humidity;
}

int16_t SensirionI2cSen63c::readMeasuredValues(
    float& massConcentrationPm1p0, float& massConcentrationPm2p5,
    float& massConcentrationPm4p0, float& massConcentrationPm10p0,
    float& humidity, float& temperature, int16_t& co2) {
    uint16_t massConcentrationPm1p0Raw = 0;
    uint16_t massConcentrationPm2p5Raw = 0;
    uint16_t massConcentrationPm4p0Raw = 0;
    uint16_t massConcentrationPm10p0Raw = 0;
    int16_t humidityRaw = 0;
    int16_t temperatureRaw = 0;
    int16_t co2Raw = 0;
    int16_t localError = 0;

    localError = readMeasuredValuesAsIntegers(
        massConcentrationPm1p0Raw, massConcentrationPm2p5Raw,
        massConcentrationPm4p0Raw, massConcentrationPm10p0Raw, humidityRaw,
        temperatureRaw, co2Raw);
    if (localError != NO_ERROR) {
        return localError;
    }
    massConcentrationPm1p0 =
        SensirionI2cSen63c::signalDividedBy10Uint16(massConcentrationPm1p0Raw);
    massConcentrationPm2p5 =
        SensirionI2cSen63c::signalDividedBy10Uint16(massConcentrationPm2p5Raw);
    massConcentrationPm4p0 =
        SensirionI2cSen63c::signalDividedBy10Uint16(massConcentrationPm4p0Raw);
    massConcentrationPm10p0 =
        SensirionI2cSen63c::signalDividedBy10Uint16(massConcentrationPm10p0Raw);
    humidity = SensirionI2cSen63c::signalHumidity(humidityRaw);
    temperature = SensirionI2cSen63c::signalTemperature(temperatureRaw);
    co2 = co2Raw;
    return localError;
}

int16_t SensirionI2cSen63c::readNumberConcentrationValues(
    float& numberConcentrationPm0p5, float& numberConcentrationPm1p0,
    float& numberConcentrationPm2p5, float& numberConcentrationPm4p0,
    float& numberConcentrationPm10p0) {
    uint16_t numberConcentrationPm0p5Raw = 0;
    uint16_t numberConcentrationPm1p0Raw = 0;
    uint16_t numberConcentrationPm2p5Raw = 0;
    uint16_t numberConcentrationPm4p0Raw = 0;
    uint16_t numberConcentrationPm10p0Raw = 0;
    int16_t localError = 0;

    localError = readNumberConcentrationValuesAsIntegers(
        numberConcentrationPm0p5Raw, numberConcentrationPm1p0Raw,
        numberConcentrationPm2p5Raw, numberConcentrationPm4p0Raw,
        numberConcentrationPm10p0Raw);
    if (localError != NO_ERROR) {
        return localError;
    }
    numberConcentrationPm0p5 = SensirionI2cSen63c::signalDividedBy10Uint16(
        numberConcentrationPm0p5Raw);
    numberConcentrationPm1p0 = SensirionI2cSen63c::signalDividedBy10Uint16(
        numberConcentrationPm1p0Raw);
    numberConcentrationPm2p5 = SensirionI2cSen63c::signalDividedBy10Uint16(
        numberConcentrationPm2p5Raw);
    numberConcentrationPm4p0 = SensirionI2cSen63c::signalDividedBy10Uint16(
        numberConcentrationPm4p0Raw);
    numberConcentrationPm10p0 = SensirionI2cSen63c::signalDividedBy10Uint16(
        numberConcentrationPm10p0Raw);
    return localError;
}

int16_t SensirionI2cSen63c::performForcedCo2Recalibration(
    uint16_t targetCo2Concentration, uint16_t& correction) {
    int16_t localError = NO_ERROR;
    uint8_t* buffer_ptr = communication_buffer;

    SensirionI2CTxFrame txFrame = SensirionI2CTxFrame::createWithUInt16Command(
        SEN63C_PERFORM_FORCED_CO2_RECALIBRATION_CMD_ID, buffer_ptr, 5);
    localError |= txFrame.addUInt16(targetCo2Concentration);
    if (localError != NO_ERROR) {
        return localError;
    }
    localError =
        SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
    if (localError != NO_ERROR) {
        return localError;
    }
    delay(500);
    SensirionI2CRxFrame rxFrame(buffer_ptr, 5);
    localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 3,
                                                         rxFrame, *_i2cBus);
    if (localError != NO_ERROR) {
        return localError;
    }
    localError |= rxFrame.getUInt16(correction);
    return localError;
}

int16_t SensirionI2cSen63c::performCo2SensorFactoryReset(void) {
    int16_t localError = NO_ERROR;
    uint8_t* buffer_ptr = communication_buffer;

    SensirionI2CTxFrame txFrame = SensirionI2CTxFrame::createWithUInt16Command(
        SEN63C_PERFORM_CO2_SENSOR_FACTORY_RESET_CMD_ID, buffer_ptr, 2);
    localError =
        SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
    if (localError != NO_ERROR) {
        return localError;
    }
    delay(1400);
    return localError;
}

int16_t
SensirionI2cSen63c::getCo2SensorAutomaticSelfCalibration(uint8_t& padding,
                                                         bool& status) {
    int16_t localError = NO_ERROR;
    uint8_t* buffer_ptr = communication_buffer;

    SensirionI2CTxFrame txFrame = SensirionI2CTxFrame::createWithUInt16Command(
        SEN63C_GET_CO2_SENSOR_AUTOMATIC_SELF_CALIBRATION_CMD_ID, buffer_ptr, 3);
    localError =
        SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
    if (localError != NO_ERROR) {
        return localError;
    }
    delay(20);
    SensirionI2CRxFrame rxFrame(buffer_ptr, 3);
    localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 3,
                                                         rxFrame, *_i2cBus);
    if (localError != NO_ERROR) {
        return localError;
    }
    localError |= rxFrame.getUInt8(padding);
    localError |= rxFrame.getBool(status);
    return localError;
}

int16_t
SensirionI2cSen63c::setCo2SensorAutomaticSelfCalibration(uint16_t status) {
    int16_t localError = NO_ERROR;
    uint8_t* buffer_ptr = communication_buffer;

    SensirionI2CTxFrame txFrame = SensirionI2CTxFrame::createWithUInt16Command(
        SEN63C_SET_CO2_SENSOR_AUTOMATIC_SELF_CALIBRATION_CMD_ID, buffer_ptr, 5);
    localError |= txFrame.addUInt16(status);
    if (localError != NO_ERROR) {
        return localError;
    }
    localError =
        SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
    if (localError != NO_ERROR) {
        return localError;
    }
    delay(20);
    return localError;
}

int16_t SensirionI2cSen63c::getAmbientPressure(uint16_t& ambientPressure) {
    int16_t localError = NO_ERROR;
    uint8_t* buffer_ptr = communication_buffer;

    SensirionI2CTxFrame txFrame = SensirionI2CTxFrame::createWithUInt16Command(
        SEN63C_GET_AMBIENT_PRESSURE_CMD_ID, buffer_ptr, 3);
    localError =
        SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
    if (localError != NO_ERROR) {
        return localError;
    }
    delay(20);
    SensirionI2CRxFrame rxFrame(buffer_ptr, 3);
    localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 3,
                                                         rxFrame, *_i2cBus);
    if (localError != NO_ERROR) {
        return localError;
    }
    localError |= rxFrame.getUInt16(ambientPressure);
    return localError;
}

int16_t SensirionI2cSen63c::setAmbientPressure(uint16_t ambientPressure) {
    int16_t localError = NO_ERROR;
    uint8_t* buffer_ptr = communication_buffer;

    SensirionI2CTxFrame txFrame = SensirionI2CTxFrame::createWithUInt16Command(
        SEN63C_SET_AMBIENT_PRESSURE_CMD_ID, buffer_ptr, 5);
    localError |= txFrame.addUInt16(ambientPressure);
    if (localError != NO_ERROR) {
        return localError;
    }
    localError =
        SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
    if (localError != NO_ERROR) {
        return localError;
    }
    delay(20);
    return localError;
}

int16_t SensirionI2cSen63c::getSensorAltitude(uint16_t& altitude) {
    int16_t localError = NO_ERROR;
    uint8_t* buffer_ptr = communication_buffer;

    SensirionI2CTxFrame txFrame = SensirionI2CTxFrame::createWithUInt16Command(
        SEN63C_GET_SENSOR_ALTITUDE_CMD_ID, buffer_ptr, 3);
    localError =
        SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
    if (localError != NO_ERROR) {
        return localError;
    }
    delay(20);
    SensirionI2CRxFrame rxFrame(buffer_ptr, 3);
    localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 3,
                                                         rxFrame, *_i2cBus);
    if (localError != NO_ERROR) {
        return localError;
    }
    localError |= rxFrame.getUInt16(altitude);
    return localError;
}

int16_t SensirionI2cSen63c::setSensorAltitude(uint16_t altitude) {
    int16_t localError = NO_ERROR;
    uint8_t* buffer_ptr = communication_buffer;

    SensirionI2CTxFrame txFrame = SensirionI2CTxFrame::createWithUInt16Command(
        SEN63C_SET_SENSOR_ALTITUDE_CMD_ID, buffer_ptr, 5);
    localError |= txFrame.addUInt16(altitude);
    if (localError != NO_ERROR) {
        return localError;
    }
    localError =
        SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
    if (localError != NO_ERROR) {
        return localError;
    }
    delay(20);
    return localError;
}

int16_t SensirionI2cSen63c::startContinuousMeasurement(void) {
    int16_t localError = NO_ERROR;
    uint8_t* buffer_ptr = communication_buffer;

    SensirionI2CTxFrame txFrame = SensirionI2CTxFrame::createWithUInt16Command(
        SEN63C_START_CONTINUOUS_MEASUREMENT_CMD_ID, buffer_ptr, 2);
    localError =
        SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
    if (localError != NO_ERROR) {
        return localError;
    }
    delay(50);
    return localError;
}

int16_t SensirionI2cSen63c::stopMeasurement(void) {
    int16_t localError = NO_ERROR;
    uint8_t* buffer_ptr = communication_buffer;

    SensirionI2CTxFrame txFrame = SensirionI2CTxFrame::createWithUInt16Command(
        SEN63C_STOP_MEASUREMENT_CMD_ID, buffer_ptr, 2);
    localError =
        SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
    if (localError != NO_ERROR) {
        return localError;
    }
    delay(1400);
    return localError;
}

int16_t SensirionI2cSen63c::getDataReady(uint8_t& padding, bool& dataReady) {
    int16_t localError = NO_ERROR;
    uint8_t* buffer_ptr = communication_buffer;

    SensirionI2CTxFrame txFrame = SensirionI2CTxFrame::createWithUInt16Command(
        SEN63C_GET_DATA_READY_CMD_ID, buffer_ptr, 3);
    localError =
        SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
    if (localError != NO_ERROR) {
        return localError;
    }
    delay(20);
    SensirionI2CRxFrame rxFrame(buffer_ptr, 3);
    localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 3,
                                                         rxFrame, *_i2cBus);
    if (localError != NO_ERROR) {
        return localError;
    }
    localError |= rxFrame.getUInt8(padding);
    localError |= rxFrame.getBool(dataReady);
    return localError;
}

int16_t SensirionI2cSen63c::readNumberConcentrationValuesAsIntegers(
    uint16_t& numberConcentrationPm0p5, uint16_t& numberConcentrationPm1p0,
    uint16_t& numberConcentrationPm2p5, uint16_t& numberConcentrationPm4p0,
    uint16_t& numberConcentrationPm10p0) {
    int16_t localError = NO_ERROR;
    uint8_t* buffer_ptr = communication_buffer;

    SensirionI2CTxFrame txFrame = SensirionI2CTxFrame::createWithUInt16Command(
        SEN63C_READ_NUMBER_CONCENTRATION_VALUES_AS_INTEGERS_CMD_ID, buffer_ptr,
        15);
    localError =
        SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
    if (localError != NO_ERROR) {
        return localError;
    }
    delay(20);
    SensirionI2CRxFrame rxFrame(buffer_ptr, 15);
    localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 15,
                                                         rxFrame, *_i2cBus);
    if (localError != NO_ERROR) {
        return localError;
    }
    localError |= rxFrame.getUInt16(numberConcentrationPm0p5);
    localError |= rxFrame.getUInt16(numberConcentrationPm1p0);
    localError |= rxFrame.getUInt16(numberConcentrationPm2p5);
    localError |= rxFrame.getUInt16(numberConcentrationPm4p0);
    localError |= rxFrame.getUInt16(numberConcentrationPm10p0);
    return localError;
}

int16_t SensirionI2cSen63c::setTemperatureOffsetParameters(
    int16_t offset, int16_t slope, uint16_t timeConstant, uint16_t slot) {
    int16_t localError = NO_ERROR;
    uint8_t* buffer_ptr = communication_buffer;

    SensirionI2CTxFrame txFrame = SensirionI2CTxFrame::createWithUInt16Command(
        SEN63C_SET_TEMPERATURE_OFFSET_PARAMETERS_CMD_ID, buffer_ptr, 14);
    localError |= txFrame.addInt16(offset);
    if (localError != NO_ERROR) {
        return localError;
    }
    localError |= txFrame.addInt16(slope);
    if (localError != NO_ERROR) {
        return localError;
    }
    localError |= txFrame.addUInt16(timeConstant);
    if (localError != NO_ERROR) {
        return localError;
    }
    localError |= txFrame.addUInt16(slot);
    if (localError != NO_ERROR) {
        return localError;
    }
    localError =
        SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
    if (localError != NO_ERROR) {
        return localError;
    }
    delay(20);
    return localError;
}

int16_t SensirionI2cSen63c::setTemperatureAccelerationParameters(uint16_t k,
                                                                 uint16_t p,
                                                                 uint16_t t1,
                                                                 uint16_t t2) {
    int16_t localError = NO_ERROR;
    uint8_t* buffer_ptr = communication_buffer;

    SensirionI2CTxFrame txFrame = SensirionI2CTxFrame::createWithUInt16Command(
        SEN63C_SET_TEMPERATURE_ACCELERATION_PARAMETERS_CMD_ID, buffer_ptr, 14);
    localError |= txFrame.addUInt16(k);
    if (localError != NO_ERROR) {
        return localError;
    }
    localError |= txFrame.addUInt16(p);
    if (localError != NO_ERROR) {
        return localError;
    }
    localError |= txFrame.addUInt16(t1);
    if (localError != NO_ERROR) {
        return localError;
    }
    localError |= txFrame.addUInt16(t2);
    if (localError != NO_ERROR) {
        return localError;
    }
    localError =
        SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
    if (localError != NO_ERROR) {
        return localError;
    }
    delay(20);
    return localError;
}

int16_t SensirionI2cSen63c::getProductType(int8_t productType[],
                                           uint16_t productTypeSize) {
    int16_t localError = NO_ERROR;
    uint8_t* buffer_ptr = communication_buffer;

    SensirionI2CTxFrame txFrame = SensirionI2CTxFrame::createWithUInt16Command(
        SEN63C_GET_PRODUCT_TYPE_CMD_ID, buffer_ptr, 48);
    localError =
        SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
    if (localError != NO_ERROR) {
        return localError;
    }
    delay(20);
    SensirionI2CRxFrame rxFrame(buffer_ptr, 48);
    localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 48,
                                                         rxFrame, *_i2cBus);
    if (localError != NO_ERROR) {
        return localError;
    }
    localError |= rxFrame.getBytes((uint8_t*)productType, productTypeSize);
    return localError;
}

int16_t SensirionI2cSen63c::getProductName(int8_t productName[],
                                           uint16_t productNameSize) {
    int16_t localError = NO_ERROR;
    uint8_t* buffer_ptr = communication_buffer;

    SensirionI2CTxFrame txFrame = SensirionI2CTxFrame::createWithUInt16Command(
        SEN63C_GET_PRODUCT_NAME_CMD_ID, buffer_ptr, 48);
    localError =
        SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
    if (localError != NO_ERROR) {
        return localError;
    }
    delay(20);
    SensirionI2CRxFrame rxFrame(buffer_ptr, 48);
    localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 48,
                                                         rxFrame, *_i2cBus);
    if (localError != NO_ERROR) {
        return localError;
    }
    localError |= rxFrame.getBytes((uint8_t*)productName, productNameSize);
    return localError;
}

int16_t SensirionI2cSen63c::getSerialNumber(int8_t serialNumber[],
                                            uint16_t serialNumberSize) {
    int16_t localError = NO_ERROR;
    uint8_t* buffer_ptr = communication_buffer;

    SensirionI2CTxFrame txFrame = SensirionI2CTxFrame::createWithUInt16Command(
        SEN63C_GET_SERIAL_NUMBER_CMD_ID, buffer_ptr, 48);
    localError =
        SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
    if (localError != NO_ERROR) {
        return localError;
    }
    delay(20);
    SensirionI2CRxFrame rxFrame(buffer_ptr, 48);
    localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 48,
                                                         rxFrame, *_i2cBus);
    if (localError != NO_ERROR) {
        return localError;
    }
    localError |= rxFrame.getBytes((uint8_t*)serialNumber, serialNumberSize);
    return localError;
}

int16_t SensirionI2cSen63c::readDeviceStatus(SEN63CDeviceStatus& deviceStatus) {
    int16_t localError = NO_ERROR;
    uint8_t* buffer_ptr = communication_buffer;

    SensirionI2CTxFrame txFrame = SensirionI2CTxFrame::createWithUInt16Command(
        SEN63C_READ_DEVICE_STATUS_CMD_ID, buffer_ptr, 6);
    localError =
        SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
    if (localError != NO_ERROR) {
        return localError;
    }
    delay(20);
    SensirionI2CRxFrame rxFrame(buffer_ptr, 6);
    localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 6,
                                                         rxFrame, *_i2cBus);
    if (localError != NO_ERROR) {
        return localError;
    }
    localError |= rxFrame.getUInt32(deviceStatus.value);
    return localError;
}

int16_t
SensirionI2cSen63c::readAndClearDeviceStatus(SEN63CDeviceStatus& deviceStatus) {
    int16_t localError = NO_ERROR;
    uint8_t* buffer_ptr = communication_buffer;

    SensirionI2CTxFrame txFrame = SensirionI2CTxFrame::createWithUInt16Command(
        SEN63C_READ_AND_CLEAR_DEVICE_STATUS_CMD_ID, buffer_ptr, 6);
    localError =
        SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
    if (localError != NO_ERROR) {
        return localError;
    }
    delay(20);
    SensirionI2CRxFrame rxFrame(buffer_ptr, 6);
    localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 6,
                                                         rxFrame, *_i2cBus);
    if (localError != NO_ERROR) {
        return localError;
    }
    localError |= rxFrame.getUInt32(deviceStatus.value);
    return localError;
}

int16_t SensirionI2cSen63c::getVersion(uint8_t& firmwareMajor,
                                       uint8_t& firmwareMinor) {
    int16_t localError = NO_ERROR;
    uint8_t* buffer_ptr = communication_buffer;

    SensirionI2CTxFrame txFrame = SensirionI2CTxFrame::createWithUInt16Command(
        SEN63C_GET_VERSION_CMD_ID, buffer_ptr, 3);
    localError =
        SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
    if (localError != NO_ERROR) {
        return localError;
    }
    delay(20);
    SensirionI2CRxFrame rxFrame(buffer_ptr, 3);
    localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 3,
                                                         rxFrame, *_i2cBus);
    if (localError != NO_ERROR) {
        return localError;
    }
    localError |= rxFrame.getUInt8(firmwareMajor);
    localError |= rxFrame.getUInt8(firmwareMinor);
    return localError;
}

int16_t SensirionI2cSen63c::deviceReset(void) {
    int16_t localError = NO_ERROR;
    uint8_t* buffer_ptr = communication_buffer;

    SensirionI2CTxFrame txFrame = SensirionI2CTxFrame::createWithUInt16Command(
        SEN63C_DEVICE_RESET_CMD_ID, buffer_ptr, 2);
    localError =
        SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
    if (localError != NO_ERROR) {
        return localError;
    }
    delay(1200);
    return localError;
}

int16_t SensirionI2cSen63c::startFanCleaning(void) {
    int16_t localError = NO_ERROR;
    uint8_t* buffer_ptr = communication_buffer;

    SensirionI2CTxFrame txFrame = SensirionI2CTxFrame::createWithUInt16Command(
        SEN63C_START_FAN_CLEANING_CMD_ID, buffer_ptr, 2);
    localError =
        SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
    if (localError != NO_ERROR) {
        return localError;
    }
    delay(20);
    return localError;
}

int16_t SensirionI2cSen63c::activateShtHeater(void) {
    int16_t localError = NO_ERROR;
    uint8_t* buffer_ptr = communication_buffer;

    SensirionI2CTxFrame txFrame = SensirionI2CTxFrame::createWithUInt16Command(
        SEN63C_ACTIVATE_SHT_HEATER_CMD_ID, buffer_ptr, 2);
    localError =
        SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
    if (localError != NO_ERROR) {
        return localError;
    }
    delay(20);
    return localError;
}

int16_t
SensirionI2cSen63c::getShtHeaterMeasurements(int16_t& shtRelativeHumidity,
                                             int16_t& shtTemperature) {
    int16_t localError = NO_ERROR;
    uint8_t* buffer_ptr = communication_buffer;

    SensirionI2CTxFrame txFrame = SensirionI2CTxFrame::createWithUInt16Command(
        SEN63C_GET_SHT_HEATER_MEASUREMENTS_CMD_ID, buffer_ptr, 6);
    localError =
        SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
    if (localError != NO_ERROR) {
        return localError;
    }
    delay(20);
    SensirionI2CRxFrame rxFrame(buffer_ptr, 6);
    localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 6,
                                                         rxFrame, *_i2cBus);
    if (localError != NO_ERROR) {
        return localError;
    }
    localError |= rxFrame.getInt16(shtRelativeHumidity);
    localError |= rxFrame.getInt16(shtTemperature);
    return localError;
}

int16_t SensirionI2cSen63c::readMeasuredValuesAsIntegers(
    uint16_t& massConcentrationPm1p0, uint16_t& massConcentrationPm2p5,
    uint16_t& massConcentrationPm4p0, uint16_t& massConcentrationPm10p0,
    int16_t& ambientHumidity, int16_t& ambientTemperature, int16_t& co2) {
    int16_t localError = NO_ERROR;
    uint8_t* buffer_ptr = communication_buffer;

    SensirionI2CTxFrame txFrame = SensirionI2CTxFrame::createWithUInt16Command(
        SEN63C_READ_MEASURED_VALUES_AS_INTEGERS_CMD_ID, buffer_ptr, 21);
    localError =
        SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
    if (localError != NO_ERROR) {
        return localError;
    }
    delay(20);
    SensirionI2CRxFrame rxFrame(buffer_ptr, 21);
    localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 21,
                                                         rxFrame, *_i2cBus);
    if (localError != NO_ERROR) {
        return localError;
    }
    localError |= rxFrame.getUInt16(massConcentrationPm1p0);
    localError |= rxFrame.getUInt16(massConcentrationPm2p5);
    localError |= rxFrame.getUInt16(massConcentrationPm4p0);
    localError |= rxFrame.getUInt16(massConcentrationPm10p0);
    localError |= rxFrame.getInt16(ambientHumidity);
    localError |= rxFrame.getInt16(ambientTemperature);
    localError |= rxFrame.getInt16(co2);
    return localError;
}

int16_t SensirionI2cSen63c::readMeasuredRawValues(int16_t& rawHumidity,
                                                  int16_t& rawTemperature) {
    int16_t localError = NO_ERROR;
    uint8_t* buffer_ptr = communication_buffer;

    SensirionI2CTxFrame txFrame = SensirionI2CTxFrame::createWithUInt16Command(
        SEN63C_READ_MEASURED_RAW_VALUES_CMD_ID, buffer_ptr, 6);
    localError =
        SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
    if (localError != NO_ERROR) {
        return localError;
    }
    delay(20);
    SensirionI2CRxFrame rxFrame(buffer_ptr, 6);
    localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 6,
                                                         rxFrame, *_i2cBus);
    if (localError != NO_ERROR) {
        return localError;
    }
    localError |= rxFrame.getInt16(rawHumidity);
    localError |= rxFrame.getInt16(rawTemperature);
    return localError;
}

void SensirionI2cSen63c::begin(TwoWire& i2cBus, uint8_t i2cAddress) {
    _i2cBus = &i2cBus;
    _i2cAddress = i2cAddress;
}
