diff --git a/.github/workflows/platformio.yml b/.github/workflows/platformio.yml index c8ade514..3ba1e533 100644 --- a/.github/workflows/platformio.yml +++ b/.github/workflows/platformio.yml @@ -34,12 +34,16 @@ jobs: - name: Basic Arduino IDE example test run: | cd examples/advanced_sensirion - pio run -s - - name: PlatformIO registry test (M5CoreInk project) + pio run + - name: PlatformIO registry backward (M5CoreInk project) run: | git clone https://github.com/hpsaturn/co2_m5coreink.git cd co2_m5coreink - pio run -s + pio run + - name: PlatformIO registry lastest (M5Atom project) + run: | + cd examples/m5atom + pio run - name: All archictures tests run: | pio run diff --git a/examples/m5airq/platformio.ini b/examples/m5airq/platformio.ini new file mode 100644 index 00000000..e7785bd0 --- /dev/null +++ b/examples/m5airq/platformio.ini @@ -0,0 +1,32 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[platformio] +src_dir = . +lib_dir = ../.. +extra_configs = ../../unified-lib-deps.ini + +[env] +framework = arduino +upload_speed = 1500000 +monitor_speed = 115200 +build_flags = + -D CORE_DEBUG_LEVEL=0 + -D ARDUINO_USB_CDC_ON_BOOT=1 + -D ARDUINO_ESP32_DEV=1 + -D M5AIRQ=1 ; in your implementation you NEED it (it will improved in the future) +lib_deps = + ${commonlibs.lib_deps} + +[env:m5airq] +extends = env +platform = espressif32 +board = esp32-s3-devkitc-1 +board_build.filesystem = littlefs ; compatibility with original demo firmware diff --git a/examples/m5airq/src/main.cpp b/examples/m5airq/src/main.cpp new file mode 100644 index 00000000..91be28f3 --- /dev/null +++ b/examples/m5airq/src/main.cpp @@ -0,0 +1,96 @@ +/** + * @file main.cpp + * @author Antonio Vanegas @hpsaturn + * @date June 2018 - 2024 + * @brief CanAirIO M5AirQ test + * @license GPL3 + * + * Full documentation: + * https://github.com/kike-canaries/canairio_sensorlib#canairio-air-quality-sensors-library + * + * Full implementation for WiFi and Bluetooth Air Quality fixed and mobile station: + * https://github.com/kike-canaries/canairio_firmware#canairio-firmware + * + * CanAirIO project documentation: + * https://canair.io/docs + */ + +#include + +#include + +#define POWER_HOLD 46 // M5AirQ main board +#define SEN55_POWER_EN 10 + +#define GROVE_SDA 13 +#define GROVE_SCL 15 + +#define I2C1_SDA_PIN 11 +#define I2C1_SCL_PIN 12 + +void printSensorsDetected() { + uint16_t sensors_count = sensors.getSensorsRegisteredCount(); + uint16_t units_count = sensors.getUnitsRegisteredCount(); + Serial.println("-->[MAIN] Sensors detected \t: " + String(sensors_count)); + Serial.println("-->[MAIN] Sensors units count\t: " + String(units_count)); + Serial.print("-->[MAIN] Sensors devices names\t: "); + int i = 0; + while (sensors.getSensorsRegistered()[i++] != 0) { + Serial.print(sensors.getSensorName((SENSORS)sensors.getSensorsRegistered()[i - 1])); + Serial.print(","); + } + Serial.println(); +} + +void printSensorsValues() { + Serial.println("-->[MAIN] Preview sensor values :"); + UNIT unit = sensors.getNextUnit(); + while (unit != UNIT::NUNIT) { + String uName = sensors.getUnitName(unit); + float uValue = sensors.getUnitValue(unit); + String uSymb = sensors.getUnitSymbol(unit); + Serial.printf("-->[MAIN] %6s:\t%02.1f\t%s\n", uName.c_str(), uValue, uSymb.c_str()); + unit = sensors.getNextUnit(); + } +} + +void onSensorDataOk() { + Serial.println("======= E X A M P L E T E S T ========="); + printSensorsDetected(); + printSensorsValues(); +} + +void onSensorDataError(const char* msg) {} +/****************************************************************************** + * M A I N + ******************************************************************************/ + +void powerEnableSensors() { + Serial.println("-->[POWR] == enable sensors =="); + pinMode(POWER_HOLD, OUTPUT); + digitalWrite(POWER_HOLD, HIGH); + pinMode(SEN55_POWER_EN, OUTPUT); + digitalWrite(SEN55_POWER_EN, LOW); +} + +void setup() { + Serial.begin(115200); + delay(2000); // Only for debugging + powerEnableSensors(); // M5AirQ enable sensors + + delay(100); + Serial.println("\n== Sensor test setup ==\n"); + Serial.println("-->[SETUP] Detecting sensors.."); + + sensors.setSampleTime(10); // config sensors sample time interval + sensors.setOnDataCallBack(&onSensorDataOk); // all data read callback + sensors.setDebugMode(false); // [optional] debug mode + sensors.detectI2COnly(true); // not force to only i2c sensors + sensors.setTemperatureUnit(TEMPUNIT::CELSIUS); // comment for Celsius or set Fahrenheit + sensors.init(); // Auto detection (UART and i2c sensors) + delay(1000); +} + +void loop() { + sensors.loop(); // read sensor data and showed it +} diff --git a/examples/m5atom/platformio.ini b/examples/m5atom/platformio.ini index cdd8b02a..a5430515 100644 --- a/examples/m5atom/platformio.ini +++ b/examples/m5atom/platformio.ini @@ -10,8 +10,6 @@ [platformio] src_dir = . -lib_dir = ../.. -extra_configs = ../../unified-lib-deps.ini [env:esp32dev] platform = espressif32 @@ -23,6 +21,6 @@ build_flags = -D CORE_DEBUG_LEVEL=0 -D M5ATOM lib_deps = + hpsaturn/CanAirIO Air Quality Sensors Library @ 0.7.4 fastled/FastLED@^3.5.0 m5stack/M5Atom@^0.0.7 - ${commonlibs.lib_deps} \ No newline at end of file diff --git a/src/Sensors.cpp b/src/Sensors.cpp index d76287bb..d156d88b 100644 --- a/src/Sensors.cpp +++ b/src/Sensors.cpp @@ -66,7 +66,6 @@ bool Sensors::readAllSensors() { } enableWire1(); - sen5xRead(); CO2scd30Read(); GCJA5Read(); sps30Read(); @@ -353,9 +352,23 @@ void Sensors::tempRegister(bool isCO2temp) { } } +/** + * @brief Initialize internal temperature offset to be used on startup + * + * Positive value for offset to be subtracetd to the temperature. + * Mush be called before the initialization of the sensors. + */ +void Sensors::initTOffset(float offset) { toffset = offset; } + +/** + * @brief Get sensorlib actual internal temperature offset + * @return float with the temperature offset. + * Positive value for offset to be subtracetd to the temperature. + */ +float Sensors::getTOffset() { return toffset; } + /** * @brief Set temperature offset for all temperature sensors - * @param offset temperature offset in °C (default 0). * * Positive value for offset to be subtracetd to the temperature. */ @@ -366,6 +379,22 @@ void Sensors::setTempOffset(float offset) { setsen5xTempOffset(toffset); } +/** + * @brief Get temperature offset for Sensirion sensors (from internal sensor in SCD4x and SCD30) + * @return float with the temperature offset. + * Positive value for offset to be subtracetd to the temperature. + */ +float Sensors::getTempOffset() { + float toffset = 0.0; + if (isSensorRegistered(SENSORS::SSCD30)) { + toffset = getSCD30TempOffset(); + } + if (isSensorRegistered(SENSORS::SSCD4X)) { + toffset = getSCD4xTempOffset(); + } + return toffset; +} + /// get Gas resistance value of BMP680 sensor float Sensors::getGas() { return gas; } @@ -1038,12 +1067,17 @@ void Sensors::sen5xRead() { massConcentrationPm1p0, massConcentrationPm2p5, massConcentrationPm4p0, massConcentrationPm10p0, ambientHumidity, ambientTemperature, vocIndex, noxIndex); - if (error) return; + if (error) { + DEBUG("[E][SLIB] SEN5x read error!"); + return; + } - pm1 = massConcentrationPm1p0; - pm25 = massConcentrationPm2p5; - pm4 = massConcentrationPm4p0; - pm10 = massConcentrationPm4p0; + pm1 = (u_int16_t)massConcentrationPm1p0; + pm25 = (u_int16_t)massConcentrationPm2p5; + pm4 = (u_int16_t)massConcentrationPm4p0; + pm10 = (u_int16_t)massConcentrationPm4p0; + voci = vocIndex; + noxi = noxIndex; temp = ambientTemperature; humi = ambientHumidity; dataReady = true; @@ -1054,6 +1088,8 @@ void Sensors::sen5xRead() { unitRegister(UNIT::PM10); unitRegister(UNIT::TEMP); unitRegister(UNIT::HUM); + unitRegister(UNIT::VOCI); + unitRegister(UNIT::NOXI); } void Sensors::GCJA5Read() { @@ -1586,6 +1622,8 @@ void Sensors::CO2scd30Init() { #endif delay(10); + sensorRegister(SENSORS::SSCD30); + DEBUG("-->[SLIB] SCD30 Temp offset\t:", String(scd30.getTemperatureOffset()).c_str()); DEBUG("-->[SLIB] SCD30 Altitude offset\t:", String(scd30.getAltitudeOffset()).c_str()); @@ -1595,11 +1633,11 @@ void Sensors::CO2scd30Init() { delay(10); } - if (uint16_t((scd30.getTemperatureOffset() * 100)) != (uint16_t(toffset * 100))) { + if (uint16_t((scd30.getTemperatureOffset())) != (uint16_t(toffset * 100))) { + DEBUG("-->[SLIB] SCD30 Temp offset to\t:", String(toffset).c_str()); setSCD30TempOffset(toffset); delay(10); } - sensorRegister(SENSORS::SSCD30); } /// set SCD30 temperature compensation @@ -1610,6 +1648,16 @@ void Sensors::setSCD30TempOffset(float offset) { } } +/// get SCD30 temperature compensation +float Sensors::getSCD30TempOffset() { + float offset = 0.0; + if (isSensorRegistered(SENSORS::SSCD30)) { + offset = scd30.getTemperatureOffset() / 100.0; + Serial.println("-->[SLIB] SCD30 get temp offset\t: " + String(offset)); + } + return offset; +} + /// set SCD30 altitude compensation void Sensors::setSCD30AltitudeOffset(float offset) { if (isSensorRegistered(SENSORS::SSCD30)) { @@ -1627,6 +1675,7 @@ void Sensors::CO2scd4xInit() { scd4x.begin(Wire); error = scd4x.stopPeriodicMeasurement(); if (error) return; + sensorRegister(SENSORS::SSCD4X); scd4x.getTemperatureOffset(tTemperatureOffset); scd4x.getSensorAltitude(tSensorAltitude); DEBUG("-->[SLIB] SCD4x Temp offset\t:", String(tTemperatureOffset).c_str()); @@ -1637,12 +1686,11 @@ void Sensors::CO2scd4xInit() { offsetDifference = abs((toffset * 100) - (tTemperatureOffset * 100)); if (offsetDifference > 0.5) { // Accounts for SCD4x conversion rounding errors in temperature offset - Serial.println("-->[SLIB] SCD4x new offset\t: Temp offset to" + String(toffset)); + Serial.println("-->[SLIB] SCD4x new offset\t: Temp offset to " + String(toffset)); setSCD4xTempOffset(toffset); } error = scd4x.startPeriodicMeasurement(); if (error) DEBUG("[W][SLIB] SCD4x periodic measure\t: starting error:", String(error).c_str()); - sensorRegister(SENSORS::SSCD4X); } /// set SCD4x temperature compensation @@ -1656,6 +1704,32 @@ void Sensors::setSCD4xTempOffset(float offset) { } } +/// get SCD4x temperature compensation +float Sensors::getSCD4xTempOffset() { + float offset = 0.0; + uint16_t error; + if (isSensorRegistered(SENSORS::SSCD4X)) { + scd4x.stopPeriodicMeasurement(); + if (error) { + DEBUG("[SLIB] SCD4x stopPeriodicMeasurement()\t: error:", String(error).c_str()); + return 0.0; + } else { + DEBUG("[SLIB] SCD4x stopPeriodicMeasurement()\t: done!"); + } + error = scd4x.getTemperatureOffset(offset); + if (error) { + DEBUG("[SLIB] SCD4x get temp offset\t: error:", String(error).c_str()); + return 0.0; + } + error = scd4x.startPeriodicMeasurement(); + if (error) { + DEBUG("[SLIB] SCD4x startPeriodicMeasurement()\t: error:", String(error).c_str()); + return 0.0; + } + } + return offset; +} + /// set SCD4x altitude compensation void Sensors::setSCD4xAltitudeOffset(float offset) { if (isSensorRegistered(SENSORS::SSCD4X)) { @@ -1670,21 +1744,22 @@ void Sensors::setSCD4xAltitudeOffset(float offset) { /// Panasonic SEN5X sensor init void Sensors::sen5xInit() { sensorAnnounce(SENSORS::SSEN5X); -#ifndef Wire1 sen5x.begin(Wire); -#else - sen5x.begin(Wire1); -#endif uint16_t error; error = sen5x.deviceReset(); if (error) return; float tempOffset = 0.0; - DEBUG("-->[SLIB] SEN5X Temp offset\t:", - String(sen5x.getTemperatureOffsetSimple(tempOffset)).c_str()); + sen5x.getTemperatureOffsetSimple(tempOffset); + DEBUG("-->[SLIB] SEN5X Temp offset\t:", String(tempOffset).c_str()); if (uint16_t((tempOffset * 100)) != (uint16_t(toffset * 100))) { sen5x.setTemperatureOffsetSimple(toffset); delay(10); } + error = sen5x.startMeasurement(); + if (error) { + DEBUG("[E][SLIB] Error trying to execute startMeasurement():"); + return; + } sensorRegister(SENSORS::SSEN5X); } @@ -1888,13 +1963,13 @@ void Sensors::startI2C() { #ifdef M5ATOM enableWire1(); #endif -#if not defined(M5STICKCPLUS) && not defined(M5COREINK) && not defined(M5ATOM) && \ - not defined(ESP32C3) - Wire.begin(); -#endif #ifdef ESP32C3 Wire.begin(19, 18); #endif +#ifdef M5AIRQ + Wire.begin(I2C1_SDA_PIN, I2C1_SCL_PIN); + enableWire1(); +#endif } void Sensors::enableWire1() { @@ -1910,6 +1985,10 @@ void Sensors::enableWire1() { Wire1.flush(); Wire1.begin(26, 32); // M5CoreInk Ext port (default for all sensors) #endif +#ifdef M5AIRQ + Wire1.flush(); + Wire1.begin(GROVE_SDA, GROVE_SCL); +#endif } void Sensors::disableWire1() { diff --git a/src/Sensors.hpp b/src/Sensors.hpp index cb048029..e55937df 100644 --- a/src/Sensors.hpp +++ b/src/Sensors.hpp @@ -81,6 +81,13 @@ #define EXT_I2C_SDA 32 #define EXT_I2C_SCL 33 +#ifdef M5AIRQ +#define GROVE_SDA 13 +#define GROVE_SCL 15 +#define I2C1_SDA_PIN 11 +#define I2C1_SCL_PIN 12 +#endif + // Read UART sensor retry. #define SENSOR_RETRY 1000 // Max Serial characters @@ -111,6 +118,8 @@ X(NH3, "ppm", "NH3") \ X(CO, "ppm", "CO") \ X(NO2, "ppm", "NO2") \ + X(NOXI, "noxi", "NOXI") \ + X(VOCI, "voci", "VOCI") \ X(UCOUNT, "COUNT", "UCOUNT") #define X(unit, symbol, name) unit, @@ -304,8 +313,14 @@ class Sensors { float getGeigerMicroSievertHour(void); + void initTOffset(float offset); + + float getTOffset(); + void setTempOffset(float offset); + float getTempOffset(); + void setCO2AltitudeOffset(float altitude); void setSeaLevelPressure(float hpa); @@ -385,7 +400,9 @@ class Sensors { float temp = 0.0; // Temperature (°C) float pres = 0.0; // Pressure float alt = 0.0; - float gas = 0.0; // + float gas = 0.0; + float voci = 0.0; + float noxi = 0.0; // temperature unit (C,K,F) TEMPUNIT temp_unit = TEMPUNIT::CELSIUS; @@ -419,6 +436,7 @@ class Sensors { void CO2scd30Init(); void CO2scd30Read(); void setSCD30TempOffset(float offset); + float getSCD30TempOffset(); void setSCD30AltitudeOffset(float offset); void CO2correctionAlt(); float hpaCalculation(float altitude); @@ -426,6 +444,7 @@ class Sensors { void CO2scd4xInit(); void CO2scd4xRead(); void setSCD4xTempOffset(float offset); + float getSCD4xTempOffset(); void setSCD4xAltitudeOffset(float offset); void sen5xInit();