Reorganizing code w/ Wire.h library

Hello! I am working on an ESP32-based project for school. I had all my functioning code in a single .ino file, but to organize it I wanted to break it into multiple files. Most everything with my includes and header files are working, but Wire (it seems to be wire) is causing me and my compiler issues. The actual code hasn't changed other than the location, and the includes are largely the same as well. All the searching I have done on this problem has told me that I "simply need to change my file from .c to .cpp". I did this, and tried many different combinations of .hpp instead of .h and got nowhere. For the sake of completeness for you kind folk, I will paste all of my files and console output.

my_gpio.h

#ifndef MY_GPIO_H
#define MY_GPIO_H

#include <Arduino.h>
#include <Wire.h>
#include "driver/i2c.h"
#include "driver/gpio.h"
#include <pas-co2-ino.hpp>
#include <Adafruit_Sensor.h>
#include "Adafruit_BMP3XX.h"
#include <SensirionI2CSen5x.h>

  /***************************************************************************
  This is a library for the BMP3XX temperature & pressure sensor

  Designed specifically to work with the Adafruit BMP388 Breakout
  ----> http://www.adafruit.com/products/3966

  These sensors use I2C or SPI to communicate, 2 or 4 pins are required
  to interface.

  Adafruit invests time and resources providing this open source code,
  please support Adafruit and open-source hardware by purchasing products
  from Adafruit!

  Written by Limor Fried & Kevin Townsend for Adafruit Industries.
  BSD license, all text above must be included in any redistribution
 ***************************************************************************/

  /*
 * I2C-Generator: 0.3.0
 * Yaml Version: 2.1.3
 * Template Version: 0.7.0-112-g190ecaa
 */
  /*
 * Copyright (c) 2021, 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.
 */


#define I2C_FREQ_HZ 400000
#define I2C_SDA_PIN 21
#define I2C_SCL_PIN 22
#define PERIODIC_MEAS_INTERVAL_IN_SECONDS 10
#define PRESSURE_REFERENCE 900
// Standard GPIO Pin definitions
#define LED_OUT_3 A3
#define LED_OUT_2 A2
#define LED_OUT_1 A1
#define NGPin A0
#define COPin A1

  void setup_GPIO(void);
  void read_all_sensors(float *ret_array, uint16_t array_size);

#endif

my_gpio.cpp

#include "my_gpio.h"


// Sensor object definitions
PASCO2Ino co2Sensor;
int16_t co2PPM;
Error_t co2Error;
Adafruit_BMP3XX bmp;
SensirionI2CSen5x sen5x;



void setupSENSensor() {
  Serial.println("Setting up SEN...");
  sen5x.begin(Wire);
  uint16_t error;
  char errorMessage[256];
  error = sen5x.deviceReset();
  if (error) {
    Serial.print("Error trying to execute deviceReset(): ");
    errorToString(error, errorMessage, 256);
    Serial.println(errorMessage);
  }
  error = sen5x.setTemperatureOffsetSimple(0);  // No temp offset
  error = sen5x.startMeasurement();
  Serial.println("SEN Setup!");
}

void setupBMPSensor() {
  while (!bmp.begin_I2C()) {  // hardware I2C mode, can pass in address & alt Wire
    Serial.println("Could not find a valid BMP3 sensor, check wiring!");
  }

  // Set up oversampling and filter initialization
  bmp.setTemperatureOversampling(BMP3_OVERSAMPLING_8X);
  bmp.setPressureOversampling(BMP3_OVERSAMPLING_4X);
  bmp.setIIRFilterCoeff(BMP3_IIR_FILTER_COEFF_3);
  bmp.setOutputDataRate(BMP3_ODR_50_HZ);
}

void setupCO2Sensor(Error_t errorPtr, PASCO2Ino CO2SensorPtr) {
  errorPtr = co2Sensor.begin();
  if (XENSIV_PASCO2_OK != errorPtr) {
    Serial.print("PAS CO2: initialization error: ");
    Serial.println(errorPtr);
  }
  /* We can set the reference pressure before starting 
     * the measure 
     */
  errorPtr = co2Sensor.setPressRef(PRESSURE_REFERENCE);
  if (XENSIV_PASCO2_OK != errorPtr) {
    Serial.print("PAS CO2: pressure reference error: ");
    Serial.println(errorPtr);
  }
  errorPtr = co2Sensor.startMeasure(5);
  if (XENSIV_PASCO2_OK != errorPtr) {
    Serial.print("PAS CO2: startmeasure error: ");
    Serial.println(errorPtr);
  }

  if (errorPtr == XENSIV_PASCO2_OK)
    Serial.println("PAS CO2: Set up sensor successfully.");
}

float readNGSensor() {
  float reading = (3.3 * analogRead(NGPin)) / 4095;
  return 10.938 * exp(1.7742 * (reading * 3.3 / 4095));
}

float readCOSensor() {
  float sensorVoltage = (3.3 * analogRead(COPin)) / 4095;
  return 3.027 * exp(1.0698 * sensorVoltage);
}

void readSENSensor(float *retArray, uint8_t arraySize) {
  // Read Measurement
  // Serial.println("Reading SEN...");
  char errorMessage[256];
  // float massConcentrationPm1p0;
  // float massConcentrationPm2p5;
  // float massConcentrationPm4p0;
  // float massConcentrationPm10p0;
  // float ambientHumidity;
  // float ambientTemperature;
  // float vocIndex;
  // float noxIndex;
  uint16_t error;

  // error = sen5x.readMeasuredValues(
  //   massConcentrationPm1p0, massConcentrationPm2p5, massConcentrationPm4p0,
  //   massConcentrationPm10p0, ambientHumidity, ambientTemperature, vocIndex,
  //   noxIndex);  // Need to copy print statement and figure out how to pass stuff along
  // // Also need to figure out how to sort files in this thang
  error = sen5x.readMeasuredValues(
    retArray[0], retArray[1], retArray[2],
    retArray[3], retArray[4], retArray[5], retArray[6],
    retArray[7]);

  if (error) {
    Serial.print("Error trying to execute readMeasuredValues(): ");
    errorToString(error, errorMessage, 256);
    Serial.println(errorMessage);
    // } else {
    //   Serial.print("MassConcentrationPm1p0:");
    //   Serial.print(retArray[0]);
    //   Serial.print("\t");
    //   Serial.print("MassConcentrationPm2p5:");
    //   Serial.print(retArray[1]);
    //   Serial.print("\n");
    //   Serial.print("MassConcentrationPm4p0:");
    //   Serial.print(retArray[2]);
    //   Serial.print("\t");
    //   Serial.print("MassConcentrationPm10p0:");
    //   Serial.print(retArray[3]);
    //   Serial.print("\n");
    //   Serial.print("AmbientHumidity:");
    //   if (isnan(retArray[4])) {
    //     Serial.print("n/a");
    //   } else {
    //     Serial.print(retArray[4]);
    //   }
    //   Serial.print("\t");
    //   Serial.print("AmbientTemperature:");
    //   if (isnan(retArray[5])) {
    //     Serial.print("n/a");
    //   } else {
    //     Serial.print(retArray[5]);
    //   }
    //   Serial.print("\t");
    //   Serial.print("VocIndex:");
    //   if (isnan(retArray[6])) {
    //     Serial.print("n/a");
    //   } else {
    //     Serial.print(retArray[6]);
    //   }
    //   Serial.print("\t");
    //   Serial.print("NoxIndex:");
    //   if (isnan(retArray[7])) {
    //     Serial.println("n/a");
    //   } else {
    //     Serial.println(retArray[7]);
    //   }
  }
}

uint16_t readCO2PPM(Error_t errorPtr, PASCO2Ino CO2SensorPtr) {
  co2PPM = 0;
  do {
    errorPtr = co2Sensor.getCO2(co2PPM);
    Serial.print("Error reading CO2 w/ error code:");
    Serial.println(errorPtr);
    // Serial.print("CO2 ppm: ");
    // Serial.println(co2PPM);
    delay(1000);
  } while (co2PPM == 0);
  return co2PPM;
}

void read_all_sensors(float *ret_array, uint16_t array_size) {
  // Reads all sensors and outputs into array
  /*
  0: CO2 PPM - PASCO2
  1: Pressure - BMP390
  2: PPM 1.0 - SEN 
  3: PPM 2.5 - SEN
  4: PPM 4.0 - SEN
  5: PPM 10.0 - SEN
  6: Humidity - SEN
  7: Temperature - SEN
  8: VOCs - SEN
  9: CO - MQ7
  10: NG - MQ4
  */
  // CO2
  float co2_ppm_return = readCO2PPM(co2Error, co2Sensor);
  String co2ppmString = String(co2_ppm_return);
  // Serial.print("CO2: ");
  // Serial.println(co2ppmString);
  ret_array[0] = co2_ppm_return;
  // Pressure
  // while (!bmp.performReading()) {
  //   Serial.println("Failed to perform BMP390 reading :(");
  // }
  // Serial.print("BMP390 Temperature = ");
  // Serial.print(bmp.temperature);
  // Serial.println(" *C");

  // Serial.print("BMP390 Pressure = ");
  // Serial.print(bmp.pressure / 100.0);
  // Serial.println(" hPa");
  // SEN
  // Uses ret_array[2] through ret_array[8]
  readSENSensor(&ret_array[2], 7);
  // CO
  float COReading = readCOSensor();
  ret_array[9] = COReading;
  // Serial.print("CO ppm: ");
  // Serial.println(ret_array[9]);
  // NG
  float NGReading = readNGSensor();
  ret_array[10] = NGReading;
  // Serial.print("NG ppm: ");
  // Serial.println(ret_array[10]);
}

void setup_GPIO() {
  // Setup I2C
  // Wire.begin(); // For breadboard QT-PY ESP32
  Serial.println("Setting up GPIO...");
  Wire.begin(21, 22);          // Used for Sparkfun Thing
  Wire.setClock(I2C_FREQ_HZ);  // 400KHz

  setupCO2Sensor(co2Error, co2Sensor);  // Setup PASCO2 Sensor
  // setupBMPSensor();
  setupSENSensor();
  Serial.println("Successfully set up GPIO.");
}

ESP32_FreeRTOS_HelloWorld.ino

#if CONFIG_FREERTOS_UNICORE
#define ARDUINO_RUNNING_CORE 0
#else
#define ARDUINO_PRIMARY_CORE 1
#define ARDUINO_AUX_CORE 0
#endif

#include "helloworld.h"


// Task handle definitions
TaskHandle_t hello_task_handle;
TaskHandle_t blink_task_handle;
TaskHandle_t blink_task_handle1;

// Preferences object creation
Preferences preferences;

void setupPreferences()
{
  // Preferences is good for single KVP storage.
  // We want to use SPIFF for large storage
  bool status = preferences.begin("my_app", false);
}

void loop() {}
void setup() {
  Serial.begin(115200);
  Serial.write("Setting up...");
  setup_GPIO();
  setupPreferences();

  // pinMode(LED_PIN_0, OUTPUT);

  xTaskCreatePinnedToCore(hello_task,            /*Function to call*/
                          "hello_task",          /*Task name*/
                          10000,                 /*Stack size*/
                          NULL,                  /*Function parameters*/
                          1,                     /*Priority*/
                          &hello_task_handle,    /*ptr to global TaskHandle_t*/
                          ARDUINO_PRIMARY_CORE); /*Core ID*/
  // xTaskCreatePinnedToCore(blinky, "blink_task_handle", 10000, NULL, 1, &blink_task_handle, 1);
  // xTaskCreatePinnedToCore(blink1, "blink1_task_handle", 10000, NULL, 1, &blink_task_handle1, 1);
}
void hello_task(void *pvParameter) {
  uint16_t arraySize = 11;
  float dataArray[arraySize];
  uint16_t taskIteration = 0;
  while (1) {
    Serial.print("Hello world!\n");
    Serial.print(taskIteration++);
    Serial.println();
    taskIteration %= arraySize;
    // dataArray[0] = readCO2PPM(co2Error, co2Sensor);
    read_all_sensors(&dataArray[0], arraySize);
    for(int i=0;i<arraySize;i++)
      Serial.println(dataArray[i]);
    
    vTaskDelay(5000 / portTICK_RATE_MS);
  }
}
void blinky(void *pvParameter) {
  // pinMode(LED_OUT_3, OUTPUT);
  int count = 0;
  while (1) {
    /* Blink off (output low) */
    count++;
    if (count >= 255) {
      count = 0;
    }
    // analogWrite(LED_OUT_3, count);
    vTaskDelay(10);
  }
}

void blink1(void *pvParameter) {
  // pinMode(LED_OUT_2, OUTPUT);
  int count = 0;
  while (1) {
    /* Blink off (output low) */
    count++;
    if (count >= 255) {
      count = 0;
    }
    // analogWrite(LED_OUT_2, count);
    vTaskDelay(15);
  }
}
In file included from C:\Users\Zane\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.14\cores\esp32/Print.h:27,
                 from C:\Users\Zane\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.14\cores\esp32/Stream.h:26,
                 from C:\Users\Zane\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.14\libraries\Wire\src/Wire.h:35,
                 from C:\Users\Zane\Documents\Arduino\ESP32_FreeRTOS_HelloWorld\my_gpio.h:5,
                 from C:\Users\Zane\Documents\Arduino\ESP32_FreeRTOS_HelloWorld\my_gpio.c:1:
C:\Users\Zane\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.14\cores\esp32/Printable.h:25:1: error: unknown type name 'class'
 class Print;
 ^~~~~
C:\Users\Zane\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.14\cores\esp32/Printable.h:33:1: error: unknown type name 'class'
 class Printable
 ^~~~~
C:\Users\Zane\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.14\cores\esp32/Printable.h:34:1: error: expected '=', ',', ';', 'asm' or '__attribute__' before '{' token
 {
 ^
In file included from C:\Users\Zane\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.14\cores\esp32/Stream.h:26,
                 from C:\Users\Zane\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.14\libraries\Wire\src/Wire.h:35,
                 from C:\Users\Zane\Documents\Arduino\ESP32_FreeRTOS_HelloWorld\my_gpio.h:5,
                 from C:\Users\Zane\Documents\Arduino\ESP32_FreeRTOS_HelloWorld\my_gpio.c:1:
C:\Users\Zane\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.14\cores\esp32/Print.h:34:1: error: unknown type name 'class'
 class Print
 ^~~~~
C:\Users\Zane\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.14\cores\esp32/Print.h:35:1: error: expected '=', ',', ';', 'asm' or '__attribute__' before '{' token
 {
 ^
In file included from C:\Users\Zane\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.14\libraries\Wire\src/Wire.h:35,
                 from C:\Users\Zane\Documents\Arduino\ESP32_FreeRTOS_HelloWorld\my_gpio.h:5,
                 from C:\Users\Zane\Documents\Arduino\ESP32_FreeRTOS_HelloWorld\my_gpio.c:1:
C:\Users\Zane\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.14\cores\esp32/Stream.h:38:1: error: unknown type name 'class'
 class Stream: public Print
 ^~~~~
C:\Users\Zane\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.14\cores\esp32/Stream.h:38:13: error: expected '=', ',', ';', 'asm' or '__attribute__' before ':' token
 class Stream: public Print
             ^
In file included from C:\Users\Zane\Documents\Arduino\ESP32_FreeRTOS_HelloWorld\my_gpio.h:5,
                 from C:\Users\Zane\Documents\Arduino\ESP32_FreeRTOS_HelloWorld\my_gpio.c:1:
C:\Users\Zane\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.14\libraries\Wire\src/Wire.h:48:1: error: unknown type name 'class'
 class TwoWire: public Stream
 ^~~~~
C:\Users\Zane\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.14\libraries\Wire\src/Wire.h:48:14: error: expected '=', ',', ';', 'asm' or '__attribute__' before ':' token
 class TwoWire: public Stream
              ^
C:\Users\Zane\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.14\libraries\Wire\src/Wire.h:162:8: error: unknown type name 'TwoWire'
 extern TwoWire Wire;
        ^~~~~~~
C:\Users\Zane\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.14\libraries\Wire\src/Wire.h:163:8: error: unknown type name 'TwoWire'
 extern TwoWire Wire1;
        ^~~~~~~
In file included from c:\Users\Zane\Documents\Arduino\libraries\XENSIV_PAS_CO2\src/pas-co2-ino.hpp:14,
                 from C:\Users\Zane\Documents\Arduino\ESP32_FreeRTOS_HelloWorld\my_gpio.h:8,
                 from C:\Users\Zane\Documents\Arduino\ESP32_FreeRTOS_HelloWorld\my_gpio.c:1:
C:\Users\Zane\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.14\cores\esp32/HardwareSerial.h:49:10: fatal error: functional: No such file or directory
 #include <functional>
          ^~~~~~~~~~~~
compilation terminated.

I will be very active on this topic so please let me know if you'd like more information. One of the biggest things that is throwing me off about my error is that it's claiming it's stemming from my_gpio.c, a file that doesn't exist (it's my_gpio.cpp!)

A "flush" issue? Calling @ptillisch

Please, mention --
1. Name and type of Sensor (s) you have connected with ESP32.
2. Name and type of IO devices (Button, LED etc.) you have connected with ESP32.
3. Name and type of input variables to the ESP32.
4. Name and type of output variables that will be delivered by ESP32 to Serial Monitor/Output Devices.

  1. I am using the SEN54-SDN-T, Infineon PAS CO2, DFRobot BMP-390, MQ4, and MQ7 gas sensors. All but the MQ sensors are on the same I2C bus, and the MQs are wired to individual analog inputs. In my previous sketch everything was working great.

  2. No other IO connected by me, but I am using the Sparkfun Thing Plus C ESP32 Devboard. I am generating this error at compile-time.

  3. Analog reads on my analog pins + using the built-in libraries for all these sensors. They depend on Wire.h for I2C communication. This is the PAS CO2 sensor header I am currently using and gets referenced by many error messages:

 * @file        pas-co2-ino.hpp
 * @brief       XENSIV™ PAS CO2 Arduino API
 * @copyright   Copyright (c) 2020-2021 Infineon Technologies AG
 *              
 * SPDX-License-Identifier: MIT
 */

#ifndef PAS_CO2_INO_HPP_
#define PAS_CO2_INO_HPP_

#include <Arduino.h>
#include <Wire.h>
#include <HardwareSerial.h>
#include "pas-co2-platf-ino.hpp"
#include "xensiv_pasco2.h"

/**
 * @addtogroup co2inoapi
 * @{
 */

typedef int32_t Error_t;
typedef xensiv_pasco2_status_t Diag_t;
typedef xensiv_pasco2_boc_cfg_t ABOC_t;

class PASCO2Ino
{
    public:

        static constexpr uint8_t       unusedPin = 0xFFU; /**< Unused pin */        

                PASCO2Ino (TwoWire * wire = &Wire, uint8_t intPin = unusedPin);
                PASCO2Ino (HardwareSerial * serial, uint8_t intPin = unusedPin);
                ~PASCO2Ino();
        Error_t begin           ();
        Error_t end             ();
        Error_t startMeasure    (int16_t  periodInSec = 0, int16_t alarmTh = 0, void (*cback) (void *) = nullptr, bool earlyNotification = false);
        Error_t stopMeasure     ();
        Error_t getCO2          (int16_t & CO2PPM);
        Error_t getDiagnosis    (Diag_t & diagnosis);
        Error_t setABOC         (ABOC_t aboc, int16_t abocRef);
        Error_t setPressRef     (uint16_t pressRef);
        Error_t performForcedCompensation(uint16_t co2Ref);
        Error_t clearForcedCompensation  ();
        Error_t reset           ();
        Error_t getDeviceID     (uint8_t & prodID, uint8_t & revID);

        Error_t getRegister     (uint8_t regAddr, uint8_t * data, uint8_t len);
        Error_t setRegister     (uint8_t regAddr, const uint8_t * data, uint8_t len);

    private:

        TwoWire         * i2c;          /**< I2C interface*/
        HardwareSerial  * uart;         /**< UART interface */   
        uint8_t           intPin;       /**< Interrupt pin */

        static constexpr uint16_t baudrateBps = 9600;      /**< UART baud rate in bps */
        static constexpr uint32_t freqHz      = 100000;    /**< I2C frequency in Hz*/

        xensiv_pasco2_t   dev;          /**< XENSIV™ PAS CO2 corelib object */
};

/** @} */

#endif /** PAS_CO2_INO_HPP_ **/
  1. Finally I am using the built-in Serial.println("") in my_gpio.cpp to print the current values from the sensors to the standard output.

I feel most of these could've been explained by the code I provided, but perhaps I am mistaken. Let me know if this information is sufficient and gets us a step closer towards the solution. :slight_smile:

1 Like

Thank you for providing the texual description as the programming codes are very private and most of the times, it is difficult for some readers o extract physical meanings.

Please, post the composite sketch in which all the sensors were working correctly. If possible, post a copy of the output from Serial Monitor.

Here is my working .ino that compiles.

crazily_named.ino

#if CONFIG_FREERTOS_UNICORE
#define ARDUINO_RUNNING_CORE 0
#else
#define ARDUINO_PRIMARY_CORE 1
#define ARDUINO_AUX_CORE 0
#endif

#include "helloworld.h"

// Standard GPIO Pin definitions
#define LED_OUT_3 A3
#define LED_OUT_2 A2
#define LED_OUT_1 A1
#define NGPin A0
#define COPin A1
// Task handle definitions
TaskHandle_t hello_task_handle;
TaskHandle_t blink_task_handle;
TaskHandle_t blink_task_handle1;
// Sensor object definitions
PASCO2Ino co2Sensor;
int16_t co2PPM;
Error_t co2Error;
Adafruit_BMP3XX bmp;
SensirionI2CSen5x sen5x;
// Preferences object creation
Preferences preferences;


void setupGPIO() {
  // Setup I2C
  // Wire.begin(); // For breadboard QT-PY ESP32
  Serial.println("Setting up GPIO...");
  Wire.begin(21, 22);          // Used for Sparkfun Thing
  Wire.setClock(I2C_FREQ_HZ);  // 400KHz

  setupCO2Sensor(co2Error, co2Sensor);  // Setup PASCO2 Sensor
  // setupBMPSensor();
  setupSENSensor();
  Serial.println("Successfully set up GPIO.");
}

void setupPreferences()
{
  // Preferences is good for single KVP storage.
  // We want to use SPIFF for large storage
  bool status = preferences.begin("my_app", false);
}

void read_all_sensors(float *ret_array, uint16_t array_size) {
  // Reads all sensors and outputs into array
  /*
  0: CO2 PPM - PASCO2
  1: Pressure - BMP390
  2: PPM 1.0
  3: PPM 2.5
  4: PPM 4.0
  5: PPM 10.0
  6: Humidity
  7: Temperature
  8: VOCs
  9: CO
  10: NG
  */
  // CO2
  float co2_ppm_return = readCO2PPM(co2Error, co2Sensor);
  String co2ppmString = String(co2_ppm_return);
  // Serial.print("CO2: ");
  // Serial.println(co2ppmString);
  ret_array[0] = co2_ppm_return;
  // Pressure
  // while (!bmp.performReading()) {
  //   Serial.println("Failed to perform BMP390 reading :(");
  // }
  // Serial.print("BMP390 Temperature = ");
  // Serial.print(bmp.temperature);
  // Serial.println(" *C");

  // Serial.print("BMP390 Pressure = ");
  // Serial.print(bmp.pressure / 100.0);
  // Serial.println(" hPa");
  // SEN
  // Uses ret_array[2] through ret_array[8]
  readSENSensor(&ret_array[2], 7);
  // CO
  float COReading = readCOSensor();
  ret_array[9] = COReading;
  // Serial.print("CO ppm: ");
  // Serial.println(ret_array[9]);
  // NG
  float NGReading = readNGSensor();
  ret_array[10] = NGReading;
  // Serial.print("NG ppm: ");
  // Serial.println(ret_array[10]);
}
void setupSENSensor() {
  Serial.println("Setting up SEN...");
  sen5x.begin(Wire);
  uint16_t error;
  char errorMessage[256];
  error = sen5x.deviceReset();
  if (error) {
    Serial.print("Error trying to execute deviceReset(): ");
    errorToString(error, errorMessage, 256);
    Serial.println(errorMessage);
  }
  error = sen5x.setTemperatureOffsetSimple(0);  // No temp offset
  error = sen5x.startMeasurement();
  Serial.println("SEN Setup!");
}
void setupBMPSensor() {
  while (!bmp.begin_I2C()) {  // hardware I2C mode, can pass in address & alt Wire
    Serial.println("Could not find a valid BMP3 sensor, check wiring!");
  }

  // Set up oversampling and filter initialization
  bmp.setTemperatureOversampling(BMP3_OVERSAMPLING_8X);
  bmp.setPressureOversampling(BMP3_OVERSAMPLING_4X);
  bmp.setIIRFilterCoeff(BMP3_IIR_FILTER_COEFF_3);
  bmp.setOutputDataRate(BMP3_ODR_50_HZ);
}

void setupCO2Sensor(Error_t errorPtr, PASCO2Ino CO2SensorPtr) {
  errorPtr = co2Sensor.begin();
  if (XENSIV_PASCO2_OK != errorPtr) {
    Serial.print("PAS CO2: initialization error: ");
    Serial.println(errorPtr);
  }
  /* We can set the reference pressure before starting 
     * the measure 
     */
  errorPtr = co2Sensor.setPressRef(PRESSURE_REFERENCE);
  if (XENSIV_PASCO2_OK != errorPtr) {
    Serial.print("PAS CO2: pressure reference error: ");
    Serial.println(errorPtr);
  }
  errorPtr = co2Sensor.startMeasure(5);
  if (XENSIV_PASCO2_OK != errorPtr) {
    Serial.print("PAS CO2: startmeasure error: ");
    Serial.println(errorPtr);
  }

  if (errorPtr == XENSIV_PASCO2_OK)
    Serial.println("PAS CO2: Set up sensor successfully.");
}

float readNGSensor() {
  float reading = (3.3 * analogRead(NGPin))/4095;
  return 10.938 * exp(1.7742 * (reading * 3.3 / 4095));
}

float readCOSensor() {
  float sensorVoltage =  (3.3 * analogRead(COPin))/4095;
  return 3.027*exp(1.0698*sensorVoltage);
}

void readSENSensor(float *retArray, uint8_t arraySize) {
  // Read Measurement
  // Serial.println("Reading SEN...");
  char errorMessage[256];
  // float massConcentrationPm1p0;
  // float massConcentrationPm2p5;
  // float massConcentrationPm4p0;
  // float massConcentrationPm10p0;
  // float ambientHumidity;
  // float ambientTemperature;
  // float vocIndex;
  // float noxIndex;
  uint16_t error;

  // error = sen5x.readMeasuredValues(
  //   massConcentrationPm1p0, massConcentrationPm2p5, massConcentrationPm4p0,
  //   massConcentrationPm10p0, ambientHumidity, ambientTemperature, vocIndex,
  //   noxIndex);  // Need to copy print statement and figure out how to pass stuff along
  // // Also need to figure out how to sort files in this thang
  error = sen5x.readMeasuredValues(
    retArray[0], retArray[1], retArray[2],
    retArray[3], retArray[4], retArray[5], retArray[6],
    retArray[7]);

  if (error) {
    Serial.print("Error trying to execute readMeasuredValues(): ");
    errorToString(error, errorMessage, 256);
    Serial.println(errorMessage);
  // } else {
  //   Serial.print("MassConcentrationPm1p0:");
  //   Serial.print(retArray[0]);
  //   Serial.print("\t");
  //   Serial.print("MassConcentrationPm2p5:");
  //   Serial.print(retArray[1]);
  //   Serial.print("\n");
  //   Serial.print("MassConcentrationPm4p0:");
  //   Serial.print(retArray[2]);
  //   Serial.print("\t");
  //   Serial.print("MassConcentrationPm10p0:");
  //   Serial.print(retArray[3]);
  //   Serial.print("\n");
  //   Serial.print("AmbientHumidity:");
  //   if (isnan(retArray[4])) {
  //     Serial.print("n/a");
  //   } else {
  //     Serial.print(retArray[4]);
  //   }
  //   Serial.print("\t");
  //   Serial.print("AmbientTemperature:");
  //   if (isnan(retArray[5])) {
  //     Serial.print("n/a");
  //   } else {
  //     Serial.print(retArray[5]);
  //   }
  //   Serial.print("\t");
  //   Serial.print("VocIndex:");
  //   if (isnan(retArray[6])) {
  //     Serial.print("n/a");
  //   } else {
  //     Serial.print(retArray[6]);
  //   }
  //   Serial.print("\t");
  //   Serial.print("NoxIndex:");
  //   if (isnan(retArray[7])) {
  //     Serial.println("n/a");
  //   } else {
  //     Serial.println(retArray[7]);
  //   }
  }
}
uint16_t readCO2PPM(Error_t errorPtr, PASCO2Ino CO2SensorPtr) {
  co2PPM = 0;
  do {
    errorPtr = co2Sensor.getCO2(co2PPM);
    Serial.print("Error reading CO2 w/ error code:");
    Serial.println(errorPtr);
    // Serial.print("CO2 ppm: ");
    // Serial.println(co2PPM);
    delay(1000);
  } while (co2PPM == 0);
  return co2PPM;
}

void loop() {}
void setup() {
  Serial.begin(115200);
  Serial.write("Setting up...");
  setupGPIO();
  setupPreferences();

  // pinMode(LED_PIN_0, OUTPUT);

  xTaskCreatePinnedToCore(hello_task,            /*Function to call*/
                          "hello_task",          /*Task name*/
                          10000,                 /*Stack size*/
                          NULL,                  /*Function parameters*/
                          1,                     /*Priority*/
                          &hello_task_handle,    /*ptr to global TaskHandle_t*/
                          ARDUINO_PRIMARY_CORE); /*Core ID*/
  // xTaskCreatePinnedToCore(blinky, "blink_task_handle", 10000, NULL, 1, &blink_task_handle, 1);
  // xTaskCreatePinnedToCore(blink1, "blink1_task_handle", 10000, NULL, 1, &blink_task_handle1, 1);
}
void hello_task(void *pvParameter) {
  uint16_t arraySize = 11;
  float dataArray[arraySize];
  uint16_t taskIteration = 0;
  while (1) {
    Serial.print("Hello world!\n");
    Serial.print(taskIteration++);
    Serial.println();
    taskIteration %= arraySize;
    // dataArray[0] = readCO2PPM(co2Error, co2Sensor);
    read_all_sensors(&dataArray[0], arraySize);
    for(int i=0;i<arraySize;i++)
      Serial.println(dataArray[i]);
    
    vTaskDelay(5000 / portTICK_RATE_MS);
  }
}
void blinky(void *pvParameter) {
  // pinMode(LED_OUT_3, OUTPUT);
  int count = 0;
  while (1) {
    /* Blink off (output low) */
    count++;
    if (count >= 255) {
      count = 0;
    }
    // analogWrite(LED_OUT_3, count);
    vTaskDelay(10);
  }
}

void blink1(void *pvParameter) {
  // pinMode(LED_OUT_2, OUTPUT);
  int count = 0;
  while (1) {
    /* Blink off (output low) */
    count++;
    if (count >= 255) {
      count = 0;
    }
    // analogWrite(LED_OUT_2, count);
    vTaskDelay(15);
  }
}

hello_world.h

#include "my_gpio.h"
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include <Preferences.h>


typedef enum{
  CO2,
  PRESSURE,
  PPM1,
  PPM25,
  PPM40,
  PPM10,
  HUMIDITY,
  TEMP,
  VOC,
  CO, 
  NG
}sensor_map;

#define LED_PIN_0 A3
#define LED_PIN_1 A1

my_gpio.h (Only used for sorting includes in this version of the project. The associated .cpp/.c is all commented and unused.)

#include "driver/i2c.h"
#include "driver/gpio.h"
#include "Wire.h"
#include <pas-co2-ino.hpp>
#include <Adafruit_Sensor.h>
#include "Adafruit_BMP3XX.h"
#include <SensirionI2CSen5x.h>

/***************************************************************************
  This is a library for the BMP3XX temperature & pressure sensor

  Designed specifically to work with the Adafruit BMP388 Breakout
  ----> http://www.adafruit.com/products/3966

  These sensors use I2C or SPI to communicate, 2 or 4 pins are required
  to interface.

  Adafruit invests time and resources providing this open source code,
  please support Adafruit and open-source hardware by purchasing products
  from Adafruit!

  Written by Limor Fried & Kevin Townsend for Adafruit Industries.
  BSD license, all text above must be included in any redistribution
 ***************************************************************************/

/*
 * I2C-Generator: 0.3.0
 * Yaml Version: 2.1.3
 * Template Version: 0.7.0-112-g190ecaa
 */
/*
 * Copyright (c) 2021, 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.
 */


#define I2C_FREQ_HZ  400000                     
#define PERIODIC_MEAS_INTERVAL_IN_SECONDS  10 
#define PRESSURE_REFERENCE  900

And finally, the successful compile messages (Hopefully it's in-depth enough and what you're looking for. The total output is absolutely massive because of the includes to the ESP32 system. If you'd like the entire thing, let me know.)

esptool.py v4.5.1
Creating esp32 image...
Merged 2 ELF sections
Successfully created esp32 image.
"C:\\Users\\Zane\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\2.0.14/tools/gen_esp32part.exe" -q "C:\\Users\\Zane\\AppData\\Local\\Temp\\arduino\\sketches\\B6044D5C7D072F5C144F4BA191EE9BE8/partitions.csv" "C:\\Users\\Zane\\AppData\\Local\\Temp\\arduino\\sketches\\B6044D5C7D072F5C144F4BA191EE9BE8/ESP32_FreeRTOS_HelloWorld.ino.partitions.bin"
cmd /c if exist "C:\\Users\\Zane\\AppData\\Local\\Temp\\arduino\\sketches\\B6044D5C7D072F5C144F4BA191EE9BE8\\libraries\\Insights" "C:\\Users\\Zane\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\2.0.14/tools/gen_insights_package.exe" "C:\\Users\\Zane\\AppData\\Local\\Temp\\arduino\\sketches\\B6044D5C7D072F5C144F4BA191EE9BE8" ESP32_FreeRTOS_HelloWorld.ino "C:\\Users\\Zane\\Documents\\Arduino\\ESP32_FreeRTOS_HelloWorld"
cmd /c IF 0==1 COPY /y "C:\\Users\\Zane\\AppData\\Local\\Arduino15\\packages\\esp32\\tools\\openocd-esp32\\v0.12.0-esp32-20230419\\share\\openocd\\scripts\\board\\esp32-wrover-kit-3.3v.cfg" "C:\\Users\\Zane\\Documents\\Arduino\\ESP32_FreeRTOS_HelloWorld\\debug.cfg"
cmd /c IF 0==1 COPY /y "C:\\Users\\Zane\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\2.0.14\\tools\\ide-debug\\esp32.json" "C:\\Users\\Zane\\Documents\\Arduino\\ESP32_FreeRTOS_HelloWorld\\debug_custom.json"
cmd /c IF 0==1 COPY /y "C:\\Users\\Zane\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\2.0.14\\tools\\ide-debug\\svd\\esp32.svd" "C:\\Users\\Zane\\Documents\\Arduino\\ESP32_FreeRTOS_HelloWorld\\debug.svd"

Using library Wire at version 2.0.0 in folder: C:\Users\Zane\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.14\libraries\Wire 
Using library XENSIV PAS CO2 at version 3.1.0 in folder: C:\Users\Zane\Documents\Arduino\libraries\XENSIV_PAS_CO2 
Using library Adafruit Unified Sensor at version 1.1.12 in folder: C:\Users\Zane\Documents\Arduino\libraries\Adafruit_Unified_Sensor 
Using library Adafruit BMP3XX Library at version 2.1.2 in folder: C:\Users\Zane\Documents\Arduino\libraries\Adafruit_BMP3XX_Library 
Using library Adafruit BusIO at version 1.14.5 in folder: C:\Users\Zane\Documents\Arduino\libraries\Adafruit_BusIO 
Using library SPI at version 2.0.0 in folder: C:\Users\Zane\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.14\libraries\SPI 
Using library Sensirion I2C SEN5X at version 0.3.0 in folder: C:\Users\Zane\Documents\Arduino\libraries\Sensirion_I2C_SEN5X 
Using library Sensirion Core at version 0.6.0 in folder: C:\Users\Zane\Documents\Arduino\libraries\Sensirion_Core 
Using library Preferences at version 2.0.0 in folder: C:\Users\Zane\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.14\libraries\Preferences 
"C:\\Users\\Zane\\AppData\\Local\\Arduino15\\packages\\esp32\\tools\\xtensa-esp32-elf-gcc\\esp-2021r2-patch5-8.4.0/bin/xtensa-esp32-elf-size" -A "C:\\Users\\Zane\\AppData\\Local\\Temp\\arduino\\sketches\\B6044D5C7D072F5C144F4BA191EE9BE8/ESP32_FreeRTOS_HelloWorld.ino.elf"
Sketch uses 304445 bytes (6%) of program storage space. Maximum is 4718592 bytes.
Global variables use 22716 bytes (6%) of dynamic memory, leaving 304964 bytes for local variables. Maximum is 327680 bytes.

A: You have --
1. SEN54-SDN-T sensor to measure particulate matter, VOC, humidity, and temperature.
2. PAS CO2 sensor to measure CO2 concentration.
3. BMP390 sensor to measure pressure and temperature.
4. MQ4 sensor to measure methane gas concentration.
5. MQ7 sesnor to measure CO concentration.

B: My suggestion would be:
1. Use an Arduino UNO to operate all sensors in a sketch using non-class codes. You may start with the following basic structure:

#include <Wire.h>
#include "xensiv_pasco2.h"  //download and include 
#include "xensiv_pasco2_platform.h"
#include <Adafruit_Sensor.h>
#include "Adafruit_BMP3XX.h"
#include<SensirionCore.h>
#include <SensirionI2CSen5x.h>

void setup()
{

}

void loop()
{

}

2. Conver the above sketch into class-based codes in Arduino UNO Platform.
3. Excute the Sketches of Step-B1 and B2 using ESP32 Platform.
4. Reformat the sketch of Step-B3 to create five tasks so that they can be concurrently executed using ESP32.

Are you using a Library for the operation of PASCO2 sensor? If yes, attach it in the next post.

Yes, I am using a library for the PAS CO2, as well as the SEN54.

pas-co2-ino.hpp

/** 
 * @file        pas-co2-ino.hpp
 * @brief       XENSIV™ PAS CO2 Arduino API
 * @copyright   Copyright (c) 2020-2021 Infineon Technologies AG
 *              
 * SPDX-License-Identifier: MIT
 */

#ifndef PAS_CO2_INO_HPP_
#define PAS_CO2_INO_HPP_

#include <Arduino.h>
#include <Wire.h>
#include <HardwareSerial.h>
#include "pas-co2-platf-ino.hpp"
#include "xensiv_pasco2.h"

/**
 * @addtogroup co2inoapi
 * @{
 */

typedef int32_t Error_t;
typedef xensiv_pasco2_status_t Diag_t;
typedef xensiv_pasco2_boc_cfg_t ABOC_t;

class PASCO2Ino
{
    public:

        static constexpr uint8_t       unusedPin = 0xFFU; /**< Unused pin */        

                PASCO2Ino (TwoWire * wire = &Wire, uint8_t intPin = unusedPin);
                PASCO2Ino (HardwareSerial * serial, uint8_t intPin = unusedPin);
                ~PASCO2Ino();
        Error_t begin           ();
        Error_t end             ();
        Error_t startMeasure    (int16_t  periodInSec = 0, int16_t alarmTh = 0, void (*cback) (void *) = nullptr, bool earlyNotification = false);
        Error_t stopMeasure     ();
        Error_t getCO2          (int16_t & CO2PPM);
        Error_t getDiagnosis    (Diag_t & diagnosis);
        Error_t setABOC         (ABOC_t aboc, int16_t abocRef);
        Error_t setPressRef     (uint16_t pressRef);
        Error_t performForcedCompensation(uint16_t co2Ref);
        Error_t clearForcedCompensation  ();
        Error_t reset           ();
        Error_t getDeviceID     (uint8_t & prodID, uint8_t & revID);

        Error_t getRegister     (uint8_t regAddr, uint8_t * data, uint8_t len);
        Error_t setRegister     (uint8_t regAddr, const uint8_t * data, uint8_t len);

    private:

        TwoWire         * i2c;          /**< I2C interface*/
        HardwareSerial  * uart;         /**< UART interface */   
        uint8_t           intPin;       /**< Interrupt pin */

        static constexpr uint16_t baudrateBps = 9600;      /**< UART baud rate in bps */
        static constexpr uint32_t freqHz      = 100000;    /**< I2C frequency in Hz*/

        xensiv_pasco2_t   dev;          /**< XENSIV™ PAS CO2 corelib object */
};

/** @} */

#endif /** PAS_CO2_INO_HPP_ **/

sen_5x.h

/*
 * THIS FILE IS AUTOMATICALLY GENERATED
 *
 * I2C-Generator: 0.3.0
 * Yaml Version: 2.1.3
 * Template Version: 0.7.0-112-g190ecaa
 */
/*
 * Copyright (c) 2021, 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 SENSIRIONI2CSEN5X_H
#define SENSIRIONI2CSEN5X_H

#include <Wire.h>

#include <SensirionCore.h>

class SensirionI2CSen5x {

  public:
    SensirionI2CSen5x();
    /**
     * begin() - Initializes the SensirionI2CSen5x class.
     *
     * @param serial Arduino stream object to be communicated with.
     *
     */
    void begin(TwoWire& i2cBus);

    /**
     * startMeasurement() - Starts a continuous measurement.

     * After starting the measurement, it takes some time (~1s) until the first
     * measurement results are available. You could poll with the command
     * 0x0202 \"Read 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 0 on success, an error code otherwise
     */
    uint16_t startMeasurement(void);

    /**
     * startMeasurementWithoutPm() - Starts a continuous measurement without PM.
     * Only humidity, temperature, VOC and NOx are available in this mode. Laser
     * and fan are switched off to keep power consumption low.
     *
     * After starting the measurement, it takes some time (~1s) until the first
     * measurement results are available. You could poll with the command
     * 0x0202 \"Read 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.
     *
     * Supported sensors: SEN54, SEN55
     *
     * @return 0 on success, an error code otherwise
     */
    uint16_t startMeasurementWithoutPm(void);

    /**
     * stopMeasurement() - Stops the measurement and returns to idle mode.
     *
     * If the device is already in idle mode, this command has no effect.
     *
     * @return 0 on success, an error code otherwise
     */
    uint16_t stopMeasurement(void);

    /**
     * readDataReady() - 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 with the 0x03.. \"Read Measured
     * Values\" commands.
     *
     * @note During fan (auto-)cleaning, no measurement data is available for
     * several seconds and thus this flag will not be set until cleaning has
     * finished. So please expect gaps of several seconds at any time if fan
     * auto-cleaning is enabled.
     *
     * @param padding Padding byte, always 0x00.
     *
     * @param dataReady True (0x01) if data is ready, False (0x00) if not. When
     * no measurement is running, False will be returned.
     *
     * @return 0 on success, an error code otherwise
     */
    uint16_t readDataReady(bool& dataReady);

    /**
     * readMeasuredValues() - Returns the measured values.
     *
     * The command 0x0202 \"Read 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 NAN.
     *
     * @param massConcentrationPm1p0  PM1.0 [µg/m³]
     * Note: If this value is unknown, NAN is returned.*
     *
     * @param massConcentrationPm2p5  PM2.5 [µg/m³]
     * Note: If this value is unknown, NAN is returned.*
     *
     * @param massConcentrationPm4p0  PM4.0 [µg/m³]
     * Note: If this value is unknown, NAN is returned.*
     *
     * @param massConcentrationPm10p0  PM10.0 [µg/m³]
     * Note: If this value is unknown, NAN is returned.*
     *
     * @param ambientHumidity RH [%]
     * Note: If this value is unknown, NAN is returned.*
     *
     * @param ambientTemperature T [°C]
     * Note: If this value is unknown, NAN is returned.*
     *
     * @param vocIndex  VOC Index
     * Note: If this value is unknown, NAN is returned.*
     *
     * @param noxIndex  NOx Index
     * 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.*
     *
     * @return 0 on success, an error code otherwise
     */
    uint16_t readMeasuredValues(float& massConcentrationPm1p0,
                                float& massConcentrationPm2p5,
                                float& massConcentrationPm4p0,
                                float& massConcentrationPm10p0,
                                float& ambientHumidity,
                                float& ambientTemperature, float& vocIndex,
                                float& noxIndex);

    /**
     * readMeasuredValuesAsIntegers() - Returns the measured values
     * without scaling factors applied.
     *
     * The command 0x0202 \"Read 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 massConcentrationPm1p0 Value is scaled with factor 10:
     *                               PM1.0 [µg/m³] = value / 10
     * Note: If this value is unknown, 0xFFFF is returned.*
     *
     * @param massConcentrationPm2p5 Value is scaled with factor 10:
     *                               PM2.5 [µg/m³] = value / 10
     * Note: If this value is unknown, 0xFFFF is returned.*
     *
     * @param massConcentrationPm4p0 Value is scaled with factor 10:
     *                               PM4.0 [µg/m³] = value / 10
     * Note: If this value is unknown, 0xFFFF is returned.*
     *
     * @param massConcentrationPm10p0 Value is scaled with factor 10:
     *                                PM10.0 [µg/m³] = value / 10
     * Note: If this value is unknown, 0xFFFF is returned.*
     *
     * @param ambientHumidity Value is scaled with factor 100:
     *                        RH [%] = value /100
     * Note: If this value is unknown, 0x7FFF is returned.*
     *
     * @param ambientTemperature Value is scaled with factor 200:
     *                           T [°C] = value / 200
     * Note: If this value is unknown, 0x7FFF is returned.*
     *
     * @param vocIndex Value is scaled with factor 10: VOC Index = value / 10
     * Note: If this value is unknown, 0x7FFF is returned.*
     *
     * @param noxIndex Value is scaled with factor 10: NOx Index = value / 10
     * Note: If this value is unknown, which is the case for SEN54,
     * 0x7FFF is returned. During the first 10..11 seconds after power-on
     * or device reset, this value will be 0x7FFF as well.*
     *
     * @return 0 on success, an error code otherwise
     */
    uint16_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);

    /**
     * readMeasuredRawValues() - Returns the measured raw values.
     *
     * The command 0x0202 \"Read 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`).
     *
     * Supported sensors: SEN54 (no NOx), SEN55
     *
     * @param rawHumidity Value is scaled with factor 100: RH [%] = value / 100
     * Note: If this value is unknown, 0x7FFF is returned.*
     *
     * @param rawTemperature Value is scaled with factor 200:
     *                       T [°C] = value / 200
     * Note: If this value is unknown, 0x7FFF is returned.*
     *
     * @param rawVoc Raw measured VOC ticks without scale factor.
     * Note: If this value is unknown, 0xFFFF is returned.*
     *
     * @param rawNox Raw measured NOx ticks without scale factor.
     * Note: If this value is unknown, which is the case for SEN54,
     * 0x7FFF is returned. During the first 10..11 seconds after power-on
     * or device reset, this value will be 0x7FFF as well.*
     *
     * @return 0 on success, an error code otherwise
     */
    uint16_t readMeasuredRawValues(int16_t& rawHumidity,
                                   int16_t& rawTemperature, uint16_t& rawVoc,
                                   uint16_t& rawNox);

    /**
     * readMeasuredValuesSen50() - Returns the measured values for SEN50.
     *
     * The command 0x0202 \"Read 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 NAN.
     *
     * @param massConcentrationPm1p0  PM1.0 [µg/m³]
     * Note: If this value is unknown, NAN is returned.*
     *
     * @param massConcentrationPm2p5  PM2.5 [µg/m³]
     * Note: If this value is unknown, NAN is returned.*
     *
     * @param massConcentrationPm4p0  PM4.0 [µg/m³]
     * Note: If this value is unknown, NAN is returned.*
     *
     * @param massConcentrationPm10p0  PM10.0 [µg/m³]
     * Note: If this value is unknown, NAN is returned.*
     *
     * @return 0 on success, an error code otherwise
     */
    uint16_t readMeasuredValuesSen50(float& massConcentrationPm1p0,
                                     float& massConcentrationPm2p5,
                                     float& massConcentrationPm4p0,
                                     float& massConcentrationPm10p0);

    /**
     * readMeasuredPmValues() - Returns the measured particulate matter values.
     *
     * The command 0x0202 \"Read 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 NAN.
     *
     * @param massConcentrationPm1p0  PM1.0 [µg/m³]
     * Note: If this value is unknown, NAN is returned.*
     *
     * @param massConcentrationPm2p5  PM2.5 [µg/m³]
     * Note: If this value is unknown, NAN is returned.*
     *
     * @param massConcentrationPm4p0  PM4.0 [µg/m³]
     * Note: If this value is unknown, NAN is returned.*
     *
     * @param massConcentrationPm10p0  PM10.0 [µg/m³]
     * Note: If this value is unknown, NAN is returned.*
     *
     * @param numberConcentrationPm0p5  PM0.5 [#/cm³]
     * Note: If this value is unknown, NAN is returned.*
     *
     * @param numberConcentrationPm1p0  PM1.0 [#/cm³]
     * Note: If this value is unknown, NAN is returned.*
     *
     * @param numberConcentrationPm2p5  PM2.5 [#/cm³]
     * Note: If this value is unknown, NAN is returned.*
     *
     * @param numberConcentrationPm4p0  PM4.0 [#/cm³]
     * Note: If this value is unknown, NAN is returned.*
     *
     * @param numberConcentrationPm10p0  PM10.0 [#/cm³]
     * Note: If this value is unknown, NAN is returned.*
     *
     * @param typicalParticleSize Size [µm]
     * Note: If this value is unknown, NAN is returned.*
     *
     * @return 0 on success, an error code otherwise
     */
    uint16_t readMeasuredPmValues(
        float& massConcentrationPm1p0, float& massConcentrationPm2p5,
        float& massConcentrationPm4p0, float& massConcentrationPm10p0,
        float& numberConcentrationPm0p5, float& numberConcentrationPm1p0,
        float& numberConcentrationPm2p5, float& numberConcentrationPm4p0,
        float& numberConcentrationPm10p0, float& typicalParticleSize);

    /**
     * readMeasuredPmValuesAsIntegers() - Returns the measured particulate
     * matter values.
     *
     * The command 0x0202 \"Read 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 0xFFFF.
     *
     * @param massConcentrationPm1p0 Value is scaled with factor 10:
     *                               PM1.0 [µg/m³] = value / 10
     * Note: If this value is unknown, 0xFFFF is returned.*
     *
     * @param massConcentrationPm2p5 Value is scaled with factor 10:
     *                               PM2.5 [µg/m³] = value / 10
     * Note: If this value is unknown, 0xFFFF is returned.*
     *
     * @param massConcentrationPm4p0 Value is scaled with factor 10:
     *                               PM4.0 [µg/m³] = value / 10
     * Note: If this value is unknown, 0xFFFF is returned.*
     *
     * @param massConcentrationPm10p0 Value is scaled with factor 10:
     *                                PM10.0 [µg/m³] = value / 10
     * Note: If this value is unknown, 0xFFFF is returned.*
     *
     * @param numberConcentrationPm0p5 Value is scaled with factor 10:
     *                                 PM0.5 [#/cm³] = value / 10
     * Note: If this value is unknown, 0xFFFF is returned.*
     *
     * @param numberConcentrationPm1p0 Value is scaled with factor 10:
     *                                 PM1.0 [#/cm³] = value / 10
     * Note: If this value is unknown, 0xFFFF is returned.*
     *
     * @param numberConcentrationPm2p5 Value is scaled with factor 10:
     *                                 PM2.5 [#/cm³] = value / 10
     * Note: If this value is unknown, 0xFFFF is returned.*
     *
     * @param numberConcentrationPm4p0 Value is scaled with factor 10:
     *                                 PM4.0 [#/cm³] = value / 10
     * Note: If this value is unknown, 0xFFFF is returned.*
     *
     * @param numberConcentrationPm10p0 Value is scaled with factor 10:
     *                                  PM10.0 [#/cm³] = value / 10
     * Note: If this value is unknown, 0xFFFF is returned.*
     *
     * @param typicalParticleSize Value is scaled with factor 1000:
     *                            Size [µm] = value / 1000
     * Note: If this value is unknown, 0xFFFF is returned.*
     *
     * @return 0 on success, an error code otherwise
     */
    uint16_t readMeasuredPmValuesAsIntegers(
        uint16_t& massConcentrationPm1p0, uint16_t& massConcentrationPm2p5,
        uint16_t& massConcentrationPm4p0, uint16_t& massConcentrationPm10p0,
        uint16_t& numberConcentrationPm0p5, uint16_t& numberConcentrationPm1p0,
        uint16_t& numberConcentrationPm2p5, uint16_t& numberConcentrationPm4p0,
        uint16_t& numberConcentrationPm10p0, uint16_t& typicalParticleSize);

    /**
     * startFanCleaning() - Starts the fan cleaning manually. The \"data
     * ready\"-flag will be cleared immediately and during the next few seconds,
     * no new measurement results will be available (old values will be
     * returned). Once the cleaning is finished, the \"data ready\"-flag will be
     * set and new measurement results will be available.
     *
     * When executing this command while cleaning is already active, the
     * command does nothing.
     *
     * If you stop the measurement while fan cleaning is active, the cleaning
     * will be aborted immediately.
     *
     * @note This command is only available in measure mode with PM measurement
     * enabled, i.e. only if the fan is already running. In any other state,
     * this command does nothing.
     *
     * @return 0 on success, an error code otherwise
     */
    uint16_t startFanCleaning(void);

    /**
     * setTemperatureOffsetSimple() - Sets the temperature offset parameter
     * in degrees celsius for the device, while leaving the other parameters at
     * their default setting.
     *
     * Supported sensors: SEN54, SEN55
     *
     * @param tempOffset Constant temperature offset in degrees celsius.
     * The default value is 0.
     *
     * @return 0 on success, an error code otherwise
     */
    uint16_t setTemperatureOffsetSimple(float tempOffset);

    /**
     * getTemperatureOffsetSimple() - Gets the temperature offset parameter
     * in degrees celsius from the device.
     * @note The other parameters, such as slope and time constant may differ
     * from the default values, if they were previously set using
     * `setTemperatureOffsetParameters`.
     *
     * Supported sensors: SEN54, SEN55
     *
     * @param tempOffset Constant temperature offset in degrees celsius.
     *
     * @return 0 on success, an error code otherwise
     */
    uint16_t getTemperatureOffsetSimple(float& tempOffset);

    /**
     * setTemperatureOffsetParameters() - Sets the temperature offset parameters
     * for the device.
     *
     * Supported sensors: SEN54, SEN55
     *
     * @param tempOffset Constant temperature offset scaled with factor 200 (T
     * [°C] = value / 200). The default value is 0.
     *
     * @param slope Normalized temperature offset slope scaled with factor 10000
     * (applied factor = value / 10000). The default value is 0.
     *
     * @param timeConstant Time constant [s] 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).
     *
     * @return 0 on success, an error code otherwise
     */
    uint16_t setTemperatureOffsetParameters(int16_t tempOffset, int16_t slope,
                                            uint16_t timeConstant);

    /**
     * getTemperatureOffsetParameters() - Gets the temperature offset parameters
     * from the device.
     *
     * Supported sensors: SEN54, SEN55
     *
     * @param tempOffset Constant temperature offset scaled with factor 200 (T
     * [°C] = value / 200).
     *
     * @param slope Normalized temperature offset slope scaled with factor 10000
     * (applied factor = value / 10000).
     *
     * @param timeConstant Time constant [s] how fast the slope and offset are
     * applied. After the specified value in seconds, 63% of the new slope and
     * offset are applied.
     *
     * @return 0 on success, an error code otherwise
     */
    uint16_t getTemperatureOffsetParameters(int16_t& tempOffset, int16_t& slope,
                                            uint16_t& timeConstant);

    /**
     * setWarmStartParameter() - Sets the warm start parameter for the device.
     *
     * Supported sensors: SEN54, SEN55
     *
     * @note This parameter can be changed in any state of the device (and the
     * getter immediately returns the new value), but it is applied only the
     * next time starting a measurement, i.e. when sending a \"Start
     * Measurement\" command! So the parameter needs to be set *before* a
     * warm-start measurement is started.
     *
     * @param warmStart Warm start behavior as a value in the range from 0 (cold
     * start) to 65535 (warm start). The default value is 0.
     *
     * @return 0 on success, an error code otherwise
     */
    uint16_t setWarmStartParameter(uint16_t warmStart);

    /**
     * getWarmStartParameter() - Gets the warm start parameter from the device.
     *
     * Supported sensors: SEN54, SEN55
     *
     * @param warmStart Warm start behavior as a value in the range from 0 (cold
     * start) to 65535 (warm start).
     *
     * @return 0 on success, an error code otherwise
     */
    uint16_t getWarmStartParameter(uint16_t& warmStart);

    /**
     * setVocAlgorithmTuningParameters() - Sets the tuning parameters of the VOC
     * algorithm.
     *
     * Supported sensors: SEN54, SEN55
     *
     * @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.
     *
     * @param indexOffset VOC index representing typical (average) conditions.
     * Allowed values are in range 1..250. The default value is 100.
     *
     * @param 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 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 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 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 gainFactor Gain factor to amplify or to attenuate the VOC index
     * output. Allowed values are in range 1..1000. The default value is 230.
     *
     * @return 0 on success, an error code otherwise
     */
    uint16_t setVocAlgorithmTuningParameters(int16_t indexOffset,
                                             int16_t learningTimeOffsetHours,
                                             int16_t learningTimeGainHours,
                                             int16_t gatingMaxDurationMinutes,
                                             int16_t stdInitial,
                                             int16_t gainFactor);

    /**
     * getVocAlgorithmTuningParameters() - Gets the currently set tuning
     * parameters of the VOC algorithm.
     *
     * Supported sensors: SEN54, SEN55
     *
     * @param indexOffset VOC index representing typical (average) conditions.
     *
     * @param 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 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 gatingMaxDurationMinutes Maximum duration of gating in minutes
     * (freeze of estimator during high VOC index signal). Zero disables the
     * gating.
     *
     * @param stdInitial Initial estimate for standard deviation. Lower value
     * boosts events during initial learning period, but may result in larger
     * device-to-device variations.
     *
     * @param gainFactor Gain factor to amplify or to attenuate the VOC index
     * output.
     *
     * @return 0 on success, an error code otherwise
     */
    uint16_t getVocAlgorithmTuningParameters(int16_t& indexOffset,
                                             int16_t& learningTimeOffsetHours,
                                             int16_t& learningTimeGainHours,
                                             int16_t& gatingMaxDurationMinutes,
                                             int16_t& stdInitial,
                                             int16_t& gainFactor);

    /**
     * setNoxAlgorithmTuningParameters() - Sets the tuning parameters of the NOx
     * algorithm.
     *
     * Supported sensors: SEN55
     *
     * @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.
     *
     * @param indexOffset NOx index representing typical (average) conditions.
     * Allowed values are in range 1..250. The default value is 1.
     *
     * @param 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 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 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 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 gainFactor Gain factor to amplify or to attenuate the NOx index
     * output. Allowed values are in range 1..1000. The default value is 230.
     *
     * @return 0 on success, an error code otherwise
     */
    uint16_t setNoxAlgorithmTuningParameters(int16_t indexOffset,
                                             int16_t learningTimeOffsetHours,
                                             int16_t learningTimeGainHours,
                                             int16_t gatingMaxDurationMinutes,
                                             int16_t stdInitial,
                                             int16_t gainFactor);

    /**
     * getNoxAlgorithmTuningParameters() - Gets the currently set tuning
     * parameters of the NOx algorithm.
     *
     * Supported sensors: SEN55
     *
     * @param indexOffset NOx index representing typical (average) conditions.
     *
     * @param 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 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 gatingMaxDurationMinutes Maximum duration of gating in minutes
     * (freeze of estimator during high NOx index signal). Zero disables the
     * gating.
     *
     * @param 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 gainFactor Gain factor to amplify or to attenuate the NOx index
     * output.
     *
     * @return 0 on success, an error code otherwise
     */
    uint16_t getNoxAlgorithmTuningParameters(int16_t& indexOffset,
                                             int16_t& learningTimeOffsetHours,
                                             int16_t& learningTimeGainHours,
                                             int16_t& gatingMaxDurationMinutes,
                                             int16_t& stdInitial,
                                             int16_t& gainFactor);

    /**
     * setRhtAccelerationMode() - Sets the RH/T acceleration mode.
     *
     * Supported sensors: SEN54, SEN55
     *
     * @note This parameter can be changed in any state of the device (and the
     * getter immediately returns the new value), but it is applied only the
     * next time starting a measurement, i.e. when sending a \"Start
     * Measurement\" command. So the parameter needs to be set *before* a new
     * measurement is started.
     *
     * @param mode The new RH/T acceleration mode.
     *
     * @return 0 on success, an error code otherwise
     */
    uint16_t setRhtAccelerationMode(uint16_t mode);

    /**
     * getRhtAccelerationMode() - Gets the RH/T acceleration mode.
     *
     * Supported sensors: SEN54, SEN55
     *
     * @param mode The current RH/T acceleration mode.
     *
     * @return 0 on success, an error code otherwise
     */
    uint16_t getRhtAccelerationMode(uint16_t& mode);

    /**
     * setVocAlgorithmState() - Sets the VOC algorithm state previously received
     * with the \"Get VOC Algorithm State\" command.
     *
     * Supported sensors: SEN54, SEN55
     *
     * @note This command is only available in idle mode and the state will be
     * applied only once when starting the next measurement. Any further
     * measurements (i.e. when stopping and restarting the measure mode) will
     * reset the state to initial values. In measure mode, this command has no
     * effect.
     *
     * @param state VOC algorithm state to restore.
     *
     * @return 0 on success, an error code otherwise
     */
    uint16_t setVocAlgorithmState(const uint8_t state[], uint8_t stateSize);

    /**
     * getVocAlgorithmState() - Gets the current VOC algorithm state. This data
     * can be used to restore the state with the \"Set VOC Algorithm State\"
     * command after a short power cycle or device reset.
     *
     * 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.
     *
     * Supported sensors: SEN54, SEN55
     *
     * @param state Current VOC algorithm state.
     *
     * @return 0 on success, an error code otherwise
     */
    uint16_t getVocAlgorithmState(uint8_t state[], uint8_t stateSize);

    /**
     * setFanAutoCleaningInterval() - Sets the fan auto cleaning interval for
     * the device.
     *
     * @param interval Fan auto cleaning interval [s]. Set to zero to disable
     * auto cleaning.
     *
     * @return 0 on success, an error code otherwise
     */
    uint16_t setFanAutoCleaningInterval(uint32_t interval);

    /**
     * getFanAutoCleaningInterval() - Gets the fan auto cleaning interval from
     * the device.
     *
     * @param interval Fan auto cleaning interval [s]. Zero means auto cleaning
     * is disabled.
     *
     * @return 0 on success, an error code otherwise
     */
    uint16_t getFanAutoCleaningInterval(uint32_t& interval);

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

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

    /**
     * getVersion() - Gets the version information for the hardware, firmware
     * and communication protocol.
     *
     * @param firmwareMajor Firmware major version number.
     *
     * @param firmwareMinor Firmware minor version number.
     *
     * @param firmwareDebug Firmware debug state. If the debug state is set, the
     * firmware is in development.
     *
     * @param hardwareMajor Hardware major version number.
     *
     * @param hardwareMinor Hardware minor version number.
     *
     * @param protocolMajor Protocol major version number.
     *
     * @param protocolMinor Protocol minor version number.
     *
     * @param padding Padding byte, ignore this.
     *
     * @return 0 on success, an error code otherwise
     */
    uint16_t getVersion(uint8_t& firmwareMajor, uint8_t& firmwareMinor,
                        bool& firmwareDebug, uint8_t& hardwareMajor,
                        uint8_t& hardwareMinor, uint8_t& protocolMajor,
                        uint8_t& protocolMinor);

    /**
     * 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.
     *
     * @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 0xD210 \"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.
     *
     * @param deviceStatus Device status (32 flags as an integer value). For
     * details, please refer to the device status flags documentation.
     *
     * @return 0 on success, an error code otherwise
     */
    uint16_t readDeviceStatus(uint32_t& deviceStatus);

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

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

  private:
    TwoWire* _i2cBus = nullptr;
};

#endif /* SENSIRIONI2CSEN5X_H */

To perform your suggestion of using non-class codes, that'd require me to re-write the libraries to change their implementation, correct? Part of the project scope is that I am not really meant to modify the libraries I am using to keep the codebase as open-source and standard as possible.

Again, the code in a single file is working perfectly. All the sensors work as expected, print to the serial output, and ultimately could stay in that .ino for the duration of the project, but I just want my files to be sorted with functions in files.

No!
I have difficulties to follow the codes that you have posted/provided. I have given you a basic structure which you may follow to make your project working and then you migrate to class-based codes and then to ESP32 Platform.

You just connect one sensor at a time. Make it functional and then connect the next sensor and so on. Serach the net to find the required Library. The Libraries should contain examples to help you in coding.

Do you have/purchased all those sensors? Do you have an UNO and an ESP32?

I have already done that. My composite sketch is functional, and came from me doing one sensor at a time. It compiles, and it works. My issue is when I break it into multiple files, and seemingly when I include Wire.h, the project fails to compile. All the current posts on this forum and other places say it's a problem with .c files attempting to include .cpp ones. I have remedied that issue, or at least I have attempted to, and I am still encountering the same issue.

Thank you and wish your good luck!

Amazingly your wish of good luck seemed to have made the stars align in my favor.

I started by commenting my include to the pas-co2 libaray and all references to that object so I could compile, and to my surprise, it compiled! So that narrowed the issue down to that library. I started commenting block-by-block and eventually I was compiling with the same code I started with. I don't know why, but this process of commenting and essentially uncommenting my code has it working. Something that may have fixed it was removing the BMP390 from the code altogether. We've decided for the project to remove it anyways, so this was me cleaning up after myself.

To those that follow: just try some stupid stuff. my_gpio.cpp is a cpp file, and the header is still just .h. I am not sure what changed between it working or not, but my project is back to running with some proper file organization.

my final, working my_gpio.h

#ifndef MY_GPIO_H
#define MY_GPIO_H

#include <Arduino.h>
#include "driver/i2c.h"
#include "driver/gpio.h"
#include <pas-co2-ino.hpp>
#include <Adafruit_Sensor.h>
#include <SensirionI2CSen5x.h>

#define I2C_FREQ_HZ 400000
#define I2C_SDA_PIN 21
#define I2C_SCL_PIN 22
#define PERIODIC_MEAS_INTERVAL_IN_SECONDS 10
#define PRESSURE_REFERENCE 900
// Standard GPIO Pin definitions
#define LED_OUT_3 A3
#define LED_OUT_2 A2
#define LED_OUT_1 A1
#define NGPin A0
#define COPin A1

  void setup_GPIO(void);
  void read_all_sensors(float *ret_array, uint16_t array_size);

#endif

my_gpio.cpp

#include "my_gpio.h"


// Sensor object definitions
PASCO2Ino co2Sensor;
int16_t co2PPM;
Error_t co2Error;
SensirionI2CSen5x sen5x;

void setupSENSensor() {
  Serial.println("Setting up SEN...");
  sen5x.begin(Wire);
  uint16_t error;
  char errorMessage[256];
  error = sen5x.deviceReset();
  if (error) {
    Serial.print("Error trying to execute deviceReset(): ");
    errorToString(error, errorMessage, 256);
    Serial.println(errorMessage);
  }
  error = sen5x.setTemperatureOffsetSimple(0);  // No temp offset
  error = sen5x.startMeasurement();
  Serial.println("SEN Setup!");
}

void setupCO2Sensor(Error_t errorPtr, PASCO2Ino CO2SensorPtr) {
  errorPtr = co2Sensor.begin();
  Serial.println("CO2 Sensor setup");
  if (XENSIV_PASCO2_OK != errorPtr) {
    Serial.print("PAS CO2: initialization error: ");
    Serial.println(errorPtr);
  }
  /* We can set the reference pressure before starting 
     * the measure 
     */
  errorPtr = co2Sensor.setPressRef(PRESSURE_REFERENCE);
  if (XENSIV_PASCO2_OK != errorPtr) {
    Serial.print("PAS CO2: pressure reference error: ");
    Serial.println(errorPtr);
  }
  errorPtr = co2Sensor.startMeasure(5);
  if (XENSIV_PASCO2_OK != errorPtr) {
    Serial.print("PAS CO2: startmeasure error: ");
    Serial.println(errorPtr);
  }

  if (errorPtr == XENSIV_PASCO2_OK)
    Serial.println("PAS CO2: Set up sensor successfully.");
}

float readNGSensor() {
  float reading = (3.3 * analogRead(NGPin)) / 4095;
  return 10.938 * exp(1.7742 * (reading * 3.3 / 4095));
}

float readCOSensor() {
  float sensorVoltage = (3.3 * analogRead(COPin)) / 4095;
  return 3.027 * exp(1.0698 * sensorVoltage);
}

void readSENSensor(float *retArray, uint8_t arraySize) {
  // Read Measurement
  // Serial.println("Reading SEN...");
  char errorMessage[256];
  uint16_t error;

  // error = sen5x.readMeasuredValues(
  //   massConcentrationPm1p0, massConcentrationPm2p5, massConcentrationPm4p0,
  //   massConcentrationPm10p0, ambientHumidity, ambientTemperature, vocIndex,
  //   noxIndex);  // Need to copy print statement and figure out how to pass stuff along
  // // Also need to figure out how to sort files in this thang
  error = sen5x.readMeasuredValues(
    retArray[0], retArray[1], retArray[2],
    retArray[3], retArray[4], retArray[5], retArray[6],
    retArray[7]);

  if (error) {
    Serial.print("Error trying to execute readMeasuredValues(): ");
    errorToString(error, errorMessage, 256);
    Serial.println(errorMessage);
  }
}

uint16_t readCO2PPM(Error_t errorPtr, PASCO2Ino CO2SensorPtr) {
  co2PPM = 0;
  do {
    errorPtr = co2Sensor.getCO2(co2PPM);
    Serial.print("Error reading CO2 w/ error code:");
    Serial.println(errorPtr);
    delay(1000);
  } while (co2PPM == 0);
  return co2PPM;
}

void read_all_sensors(float *ret_array, uint16_t array_size) {
  // Reads all sensors and outputs into array
  /*
  0: CO2 PPM - PASCO2
  1: PPM 1.0 - SEN 
  2: PPM 2.5 - SEN
  3: PPM 4.0 - SEN
  4: PPM 10.0 - SEN
  5: Humidity - SEN
  6: Temperature - SEN
  7: VOCs - SEN
  8: CO - MQ7
  9: NG - MQ4
  */
  // CO2
  float co2_ppm_return = readCO2PPM(co2Error, co2Sensor);
  String co2ppmString = String(co2_ppm_return);
  Serial.print("CO2: ");
  Serial.println(co2ppmString);
  ret_array[0] = co2_ppm_return;
  // SEN
  // Uses ret_array[2] through ret_array[8]
  readSENSensor(&ret_array[2], 7);
  // CO
  float COReading = readCOSensor();
  ret_array[9] = COReading;
  // Serial.print("CO ppm: ");
  // Serial.println(ret_array[9]);
  // NG
  float NGReading = readNGSensor();
  ret_array[10] = NGReading;
  // Serial.print("NG ppm: ");
  // Serial.println(ret_array[10]);
}

void setup_GPIO() {
  // Setup I2C
  // Wire.begin(); // For breadboard QT-PY ESP32
  Serial.begin(115200);
  Serial.println("Setting up GPIO...");
  Wire.begin(21, 22);          // Used for Sparkfun Thing
  Wire.setClock(I2C_FREQ_HZ);  // 400KHz

  setupCO2Sensor(co2Error, co2Sensor);  // Setup PASCO2 Sensor
  setupSENSensor();
  Serial.println("Successfully set up GPIO.");
}

Hi @zanemcmorris

This is caused by a bug in Arduino IDE that causes the previous file to remain in the temporary build folder after you renamed it in your sketch:

The workaround is to manually delete the temporary build folder where the leftover file is.

Since you report the error is no longer occurring, it seems you won't need to perform that workaround. I guess the temporary build folder ended up being cleared or refreshing as a matter of course while you were investigating the cause of the problem.

But if you do encounter it again, you should be able to restore the sketch to functionality through that workaround. I'll be happy to provide assistance if you do end up needing that.

Ah this is beautiful. Thanks for answering this most puzzling question! I am sure I'll be encountering this same issue or a related one sometime in the next few months. You guys have been quite helpful. Thanks!

You are welcome. I'm glad if I was able to be of some assistance.

Regards,
Per

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.