From 10d3c36c7a0bcab74bd48fab123a6c80e8c5d428 Mon Sep 17 00:00:00 2001 From: GOB Date: Tue, 21 Jan 2025 16:50:24 +0900 Subject: [PATCH 1/8] Fixes M5UnitUnified API calls --- src/unit/unit_SCD40.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/unit/unit_SCD40.cpp b/src/unit/unit_SCD40.cpp index ca1ab3e..b5371a8 100644 --- a/src/unit/unit_SCD40.cpp +++ b/src/unit/unit_SCD40.cpp @@ -169,7 +169,7 @@ bool UnitSCD40::readTemperatureOffset(float& offset) } uint16_t u16{}; - auto ret = readRegister16(GET_TEMPERATURE_OFFSET, u16, GET_TEMPERATURE_OFFSET_DURATION); + auto ret = readRegister16BE(GET_TEMPERATURE_OFFSET, u16, GET_TEMPERATURE_OFFSET_DURATION); offset = Temperature::toFloat(u16); return ret; } @@ -198,7 +198,7 @@ bool UnitSCD40::readSensorAltitude(uint16_t& altitude) M5_LIB_LOGD("Periodic measurements are running"); return false; } - return readRegister16(GET_SENSOR_ALTITUDE, altitude, GET_SENSOR_ALTITUDE_DURATION); + return readRegister16BE(GET_SENSOR_ALTITUDE, altitude, GET_SENSOR_ALTITUDE_DURATION); } bool UnitSCD40::writeAmbientPressure(const float pressure, const uint32_t duration) @@ -278,7 +278,7 @@ bool UnitSCD40::readAutomaticSelfCalibrationEnabled(bool& enabled) return false; } uint16_t u16{}; - if (readRegister16(GET_AUTOMATIC_SELF_CALIBRATION_ENABLED, u16, 1)) { + if (readRegister16BE(GET_AUTOMATIC_SELF_CALIBRATION_ENABLED, u16, 1)) { enabled = (u16 == 0x0001); return true; } @@ -288,7 +288,7 @@ bool UnitSCD40::readAutomaticSelfCalibrationEnabled(bool& enabled) bool UnitSCD40::read_data_ready_status() { uint16_t res{}; - return readRegister16(GET_DATA_READY_STATUS, res, GET_DATA_READY_STATUS_DURATION) ? (res & 0x07FF) != 0 : false; + return readRegister16BE(GET_DATA_READY_STATUS, res, GET_DATA_READY_STATUS_DURATION) ? (res & 0x07FF) != 0 : false; } bool UnitSCD40::writePersistSettings(const uint32_t duration) @@ -353,7 +353,7 @@ bool UnitSCD40::performSelfTest(bool& malfunction) } uint16_t response{}; - if (readRegister16(PERFORM_SELF_TEST, response, PERFORM_SELF_TEST_DURATION)) { + if (readRegister16BE(PERFORM_SELF_TEST, response, PERFORM_SELF_TEST_DURATION)) { malfunction = (response != 0); return true; } From 797322e0ebae3e11cd2ed8d6ac7eae016989e8a2 Mon Sep 17 00:00:00 2001 From: GOB Date: Tue, 21 Jan 2025 16:51:32 +0900 Subject: [PATCH 2/8] Fixes NanoC6 env and versions of dependent libraries --- platformio.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platformio.ini b/platformio.ini index 162d185..64cd223 100644 --- a/platformio.ini +++ b/platformio.ini @@ -11,7 +11,7 @@ lib_ldf_mode = deep test_framework = googletest test_build_src = true lib_deps=m5stack/M5Unified - m5stack/M5UnitUnified + m5stack/M5UnitUnified@^0.0.5 https://github.com/boschsensortec/Bosch-BME68x-Library.git @ 1.2.40408 ; -------------------------------- @@ -91,7 +91,7 @@ extends = m5base board = m5stack-nanoc6 platform = https://github.com/platformio/platform-espressif32.git platform_packages = - platformio/framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git + platformio/framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#idf-release/v5.1 platformio/framework-arduinoespressif32-libs @ https://github.com/espressif/esp32-arduino-libs.git#idf-release/v5.1 board_build.partitions = default.csv lib_deps = ${env.lib_deps} From b855b2e90f97137855073e20fceff21daccbac22 Mon Sep 17 00:00:00 2001 From: GOB Date: Tue, 21 Jan 2025 16:52:22 +0900 Subject: [PATCH 3/8] Fixes versions of dependent libraries --- library.json | 8 ++++++-- library.properties | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/library.json b/library.json index a05ed01..b1ee806 100644 --- a/library.json +++ b/library.json @@ -11,7 +11,7 @@ "url": "https://github.com/m5stack/M5Unit-ENV.git" }, "dependencies": { - "M5UnitUnified": "https://github.com/m5stack/M5UnitUnified.git", + "M5UnitUnified": "https://github.com/m5stack/M5UnitUnified.git@^0.0.5", "BME68x Sensor library": "https://github.com/boschsensortec/Bosch-BME68x-Library.git", "bsec2": "https://github.com/boschsensortec/Bosch-BSEC2-Library" }, @@ -19,6 +19,10 @@ "frameworks": [ "arduino" ], + "headers": [ + "M5UnitENV.h", + "M5UnitUnifiedENV.h" + ] , "platforms": "espressif32", "license": "MIT", "export": { @@ -26,4 +30,4 @@ "docs/html" ] } -} \ No newline at end of file +} diff --git a/library.properties b/library.properties index 58d8f06..6524b7c 100644 --- a/library.properties +++ b/library.properties @@ -8,4 +8,4 @@ category=Device Control url=https://github.com/m5stack/M5Unit-ENV architectures=esp32 includes=M5UnitENV.h, M5UnitUnifiedENV.h -depends=BME68x Sensor library,M5UnitUnified,M5Utility,M5HAL,bsec2 +depends=BME68x Sensor library,M5UnitUnified(>=0.0.5),M5Utility,M5HAL,bsec2 From 101ad5251702e01d804ed112e1eaa95850fbd74d Mon Sep 17 00:00:00 2001 From: GOB Date: Tue, 21 Jan 2025 16:52:59 +0900 Subject: [PATCH 4/8] Raise version --- library.json | 6 +++--- library.properties | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/library.json b/library.json index b1ee806..e97331d 100644 --- a/library.json +++ b/library.json @@ -15,14 +15,14 @@ "BME68x Sensor library": "https://github.com/boschsensortec/Bosch-BME68x-Library.git", "bsec2": "https://github.com/boschsensortec/Bosch-BSEC2-Library" }, - "version": "1.2.0", + "version": "1.2.1", "frameworks": [ "arduino" ], "headers": [ "M5UnitENV.h", "M5UnitUnifiedENV.h" - ] , + ], "platforms": "espressif32", "license": "MIT", "export": { @@ -30,4 +30,4 @@ "docs/html" ] } -} +} \ No newline at end of file diff --git a/library.properties b/library.properties index 6524b7c..d74564a 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=M5Unit-ENV -version=1.2.0 +version=1.2.1 author=M5Stack maintainer=M5Stack sentence=Library for M5Stack UNIT ENV From 46fa76d2cd29c8ee9b8a69942602ba250fe1ec75 Mon Sep 17 00:00:00 2001 From: GOB Date: Fri, 31 Jan 2025 17:25:19 +0900 Subject: [PATCH 5/8] Add support for UnitCO2L(SCD41) in M5UnitUnified --- .../workflows/arduino-esp-v2-build-check.yml | 1 + .../workflows/arduino-esp-v3-build-check.yml | 1 + .github/workflows/arduino-m5-build-check.yml | 1 + .github/workflows/platformio-build-check.yml | 1 + README.md | 2 + .../UnitCO2L/PlotToSerial/PlotToSerial.ino | 11 + .../PlotToSerial/main/PlotToSerial.cpp | 94 +++++ src/M5UnitUnifiedENV.hpp | 1 + src/unit/unit_SCD40.cpp | 11 +- src/unit/unit_SCD41.cpp | 24 +- src/unit/unit_SCD41.hpp | 7 +- test/embedded/scd4x_test.inl | 286 ++++++++++++++ test/embedded/test_scd40/scd40_test.cpp | 369 +----------------- test/embedded/test_scd41/scd41_test.cpp | 105 +++++ unit_co2_env.ini | 168 ++++++++ 15 files changed, 722 insertions(+), 360 deletions(-) create mode 100644 examples/UnitUnified/UnitCO2L/PlotToSerial/PlotToSerial.ino create mode 100644 examples/UnitUnified/UnitCO2L/PlotToSerial/main/PlotToSerial.cpp create mode 100644 test/embedded/scd4x_test.inl create mode 100644 test/embedded/test_scd41/scd41_test.cpp diff --git a/.github/workflows/arduino-esp-v2-build-check.yml b/.github/workflows/arduino-esp-v2-build-check.yml index 971a61e..40b1891 100644 --- a/.github/workflows/arduino-esp-v2-build-check.yml +++ b/.github/workflows/arduino-esp-v2-build-check.yml @@ -61,6 +61,7 @@ jobs: unit: - UnitCO2 + - UnitCO2L - UnitENVIII - UnitENVIV - UnitTVOC diff --git a/.github/workflows/arduino-esp-v3-build-check.yml b/.github/workflows/arduino-esp-v3-build-check.yml index 755487e..ac9b58e 100644 --- a/.github/workflows/arduino-esp-v3-build-check.yml +++ b/.github/workflows/arduino-esp-v3-build-check.yml @@ -61,6 +61,7 @@ jobs: unit: - UnitCO2 + - UnitCO2L - UnitENVIII - UnitENVIV - UnitTVOC diff --git a/.github/workflows/arduino-m5-build-check.yml b/.github/workflows/arduino-m5-build-check.yml index d6be848..09a1551 100644 --- a/.github/workflows/arduino-m5-build-check.yml +++ b/.github/workflows/arduino-m5-build-check.yml @@ -61,6 +61,7 @@ jobs: unit: - UnitCO2 + - UnitCO2L - UnitENVIII - UnitENVIV - UnitTVOC diff --git a/.github/workflows/platformio-build-check.yml b/.github/workflows/platformio-build-check.yml index 6be49f7..a353d17 100644 --- a/.github/workflows/platformio-build-check.yml +++ b/.github/workflows/platformio-build-check.yml @@ -57,6 +57,7 @@ jobs: unit: - UnitCO2 + - UnitCO2L - UnitENVIII - UnitENVIV - UnitTVOC diff --git a/README.md b/README.md index 4818aaf..fa45baa 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,9 @@ See also examples using conventional methods here. ``` ### Supported units - Unit CO2 +- Unit CO2L - Unit ENVIII +- Unit ENVIV - Unit ENVPro - Unit TVOC diff --git a/examples/UnitUnified/UnitCO2L/PlotToSerial/PlotToSerial.ino b/examples/UnitUnified/UnitCO2L/PlotToSerial/PlotToSerial.ino new file mode 100644 index 0000000..15b19df --- /dev/null +++ b/examples/UnitUnified/UnitCO2L/PlotToSerial/PlotToSerial.ino @@ -0,0 +1,11 @@ +/* + * SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +/* + Example using M5UnitUnified for UnitCO2L + Required + - M5Unified: https://github.com/m5stack/M5Unified +*/ +#include "main/PlotToSerial.cpp" diff --git a/examples/UnitUnified/UnitCO2L/PlotToSerial/main/PlotToSerial.cpp b/examples/UnitUnified/UnitCO2L/PlotToSerial/main/PlotToSerial.cpp new file mode 100644 index 0000000..27dce39 --- /dev/null +++ b/examples/UnitUnified/UnitCO2L/PlotToSerial/main/PlotToSerial.cpp @@ -0,0 +1,94 @@ +/* + * SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +/* + Example using M5UnitUnified for UnitCO2L +*/ +// #define USING_M5HAL // When using M5HAL +#include +#include +#include + +namespace { +auto& lcd = M5.Display; +m5::unit::UnitUnified Units; +// m5::unit::UnitCO2 unit; +m5::unit::UnitCO2L unit; + +} // namespace + +using namespace m5::unit::scd4x; + +void setup() +{ + M5.begin(); + + auto pin_num_sda = M5.getPin(m5::pin_name_t::port_a_sda); + auto pin_num_scl = M5.getPin(m5::pin_name_t::port_a_scl); + M5_LOGI("getPin: SDA:%u SCL:%u", pin_num_sda, pin_num_scl); + +#if defined(USING_M5HAL) +#pragma message "Using M5HAL" + // Using M5HAL + m5::hal::bus::I2CBusConfig i2c_cfg; + i2c_cfg.pin_sda = m5::hal::gpio::getPin(pin_num_sda); + i2c_cfg.pin_scl = m5::hal::gpio::getPin(pin_num_scl); + auto i2c_bus = m5::hal::bus::i2c::getBus(i2c_cfg); + if (!Units.add(unit, i2c_bus ? i2c_bus.value() : nullptr) || !Units.begin()) { + M5_LOGE("Failed to begin"); + lcd.clear(TFT_RED); + while (true) { + m5::utility::delay(10000); + } + } +#else +#pragma message "Using Wire" + // Using TwoWire + Wire.begin(pin_num_sda, pin_num_scl, 400000U); + if (!Units.add(unit, Wire) || !Units.begin()) { + M5_LOGE("Failed to begin"); + lcd.clear(TFT_RED); + while (true) { + m5::utility::delay(10000); + } + } +#endif + M5_LOGI("M5UnitUnified has been begun"); + M5_LOGI("%s", Units.debugInfo().c_str()); + + lcd.clear(TFT_DARKGREEN); +} + +void loop() +{ + M5.update(); + auto touch = M5.Touch.getDetail(); + + // Periodic + Units.update(); + if (unit.updated()) { + // Can be checked e.g. by serial plotters + M5_LOGI("\n>CO2:%u\n>Temperature:%2.2f\n>Humidity:%2.2f", unit.co2(), unit.temperature(), unit.humidity()); + } + + // Single + if (M5.BtnA.wasClicked() || touch.wasClicked()) { + static bool all{}; // false: RHT only + all = !all; + M5_LOGI("Try single shot %u, waiting measurement...", all); + unit.stopPeriodicMeasurement(); + Data d{}; + if (all) { + if (unit.measureSingleshot(d)) { + M5_LOGI("SingleAll: %u/%2.2f/%2.2f", d.co2(), d.temperature(), d.humidity()); + } + } else { + if (unit.measureSingleshotRHT(d)) { + M5_LOGI("SingleRHT: %2.2f/%2.2f", d.temperature(), d.humidity()); + } + } + unit.startPeriodicMeasurement(); + } +} diff --git a/src/M5UnitUnifiedENV.hpp b/src/M5UnitUnifiedENV.hpp index ff251dd..2b2e14f 100644 --- a/src/M5UnitUnifiedENV.hpp +++ b/src/M5UnitUnifiedENV.hpp @@ -46,6 +46,7 @@ namespace m5 { namespace unit { using UnitCO2 = m5::unit::UnitSCD40; +using UnitCO2L = m5::unit::UnitSCD41; using UnitENVPro = m5::unit::UnitBME688; using UnitTVOC = m5::unit::UnitSGP30; diff --git a/src/unit/unit_SCD40.cpp b/src/unit/unit_SCD40.cpp index b5371a8..9ee8c08 100644 --- a/src/unit/unit_SCD40.cpp +++ b/src/unit/unit_SCD40.cpp @@ -102,13 +102,13 @@ void UnitSCD40::update(const bool force) { _updated = false; if (inPeriodic()) { - unsigned long at{m5::utility::millis()}; + auto at = m5::utility::millis(); if (force || !_latest || at >= _latest + _interval) { Data d{}; _updated = read_measurement(d); if (_updated) { + _latest = m5::utility::millis(); // Data acquisition takes time, so acquire again _data->push_back(d); - _latest = at; } } } @@ -397,6 +397,13 @@ bool UnitSCD40::read_measurement(Data& d, const bool all) if (!readRegister(READ_MEASUREMENT, d.raw.data(), d.raw.size(), READ_MEASUREMENT_DURATION)) { return false; } + + // For RHT only, previous Co2 data may be obtained and should be dismissed + if (!all) { + d.raw[0] = d.raw[1] = d.raw[2] = 0; + } + + // Check CRC m5::utility::CRC8_Checksum crc{}; for (uint_fast8_t i = all ? 0 : 1; i < 3; ++i) { if (crc.range(d.raw.data() + i * 3, 2U) != d.raw[i * 3 + 2]) { diff --git a/src/unit/unit_SCD41.cpp b/src/unit/unit_SCD41.cpp index 914ee80..1525add 100644 --- a/src/unit/unit_SCD41.cpp +++ b/src/unit/unit_SCD41.cpp @@ -17,6 +17,12 @@ using namespace m5::unit::scd4x::command; using namespace m5::unit::scd41; using namespace m5::unit::scd41::command; +namespace { +// Max command duration(ms) +constexpr uint16_t MEASURE_SINGLE_SHOT_DURATION{5000}; +constexpr uint16_t MEASURE_SINGLE_SHOT_RHT_ONLY_DURATION{50}; +} // namespace + namespace m5 { namespace unit { // class UnitSCD41 @@ -26,20 +32,34 @@ const types::uid_t UnitSCD41::attr{0}; bool UnitSCD41::measureSingleshot(Data& d) { + d = Data{}; + if (inPeriodic()) { M5_LIB_LOGD("Periodic measurements are running"); return false; } - return writeRegister(MEASURE_SINGLE_SHOT) && read_measurement(d); + + if (writeRegister(MEASURE_SINGLE_SHOT)) { + m5::utility::delay(MEASURE_SINGLE_SHOT_DURATION); + return read_measurement(d); + } + return false; } bool UnitSCD41::measureSingleshotRHT(Data& d) { + d = Data{}; + if (inPeriodic()) { M5_LIB_LOGD("Periodic measurements are running"); return false; } - return writeRegister(MEASURE_SINGLE_SHOT_RHT_ONLY) && read_measurement(d, false); + + if (writeRegister(MEASURE_SINGLE_SHOT_RHT_ONLY)) { + m5::utility::delay(MEASURE_SINGLE_SHOT_RHT_ONLY_DURATION); + return read_measurement(d, false); + } + return false; } } // namespace unit diff --git a/src/unit/unit_SCD41.hpp b/src/unit/unit_SCD41.hpp index 9bbad30..704381c 100644 --- a/src/unit/unit_SCD41.hpp +++ b/src/unit/unit_SCD41.hpp @@ -35,7 +35,7 @@ class UnitSCD41 : public UnitSCD40 { /*! @brief Request a single measurement @return True if successful - @note Values are updated at 5000 ms interval + @note Blocked until measurement results are acquired (5000 ms) @warning During periodic detection runs, an error is returned */ bool measureSingleshot(scd4x::Data &d); @@ -43,6 +43,7 @@ class UnitSCD41 : public UnitSCD40 { @brief Request a single measurement temperature and humidity @return True if successful @note Values are updated at 50 ms interval + @note Blocked until measurement results are acquired (50 ms) @warning Information on CO2 is invalid. @warning During periodic detection runs, an error is returned */ @@ -56,10 +57,6 @@ class UnitSCD41 : public UnitSCD40 { */ namespace scd41 { ///@cond -// Max command duration(ms) -constexpr uint16_t MEASURE_SINGLE_SHOT_DURATION{5000}; -constexpr uint16_t MEASURE_SINGLE_SHOT_RHT_ONLY_DURATION{50}; - namespace command { // Low power single shot - SCD41 only constexpr uint16_t MEASURE_SINGLE_SHOT{0x219d}; diff --git a/test/embedded/scd4x_test.inl b/test/embedded/scd4x_test.inl new file mode 100644 index 0000000..d654f50 --- /dev/null +++ b/test/embedded/scd4x_test.inl @@ -0,0 +1,286 @@ +/* + * SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +/* + Common parts of SCD40/41 test + */ + +namespace { +template +elapsed_time_t test_periodic(U* unit, const uint32_t times, const uint32_t measure_duration = 0) +{ + auto tm = unit->interval(); + auto timeout_at = m5::utility::millis() + 10 * 1000; + + do { + unit->update(); + if (unit->updated()) { + break; + } + std::this_thread::yield(); + } while (!unit->updated() && m5::utility::millis() <= timeout_at); + // timeout + if (!unit->updated()) { + return 0; + } + + // + uint32_t measured{}; + auto start_at = m5::utility::millis(); + timeout_at = start_at + (times * (tm + measure_duration) * 2); + + do { + unit->update(); + measured += unit->updated() ? 1 : 0; + if (measured >= times) { + break; + } + m5::utility::delay(1); + + } while (measured < times && m5::utility::millis() <= timeout_at); + return (measured == times) ? m5::utility::millis() - start_at : 0; +} +} // namespace + +TEST_P(TestSCD4x, BasicCommand) +{ + SCOPED_TRACE(ustr); + + EXPECT_FALSE(unit->inPeriodic()); + + for (auto&& m : mode_table) { + auto s = m5::utility::formatString("Mode:%u", m); + SCOPED_TRACE(s); + + EXPECT_TRUE(unit->stopPeriodicMeasurement()); + EXPECT_FALSE(unit->inPeriodic()); + + EXPECT_TRUE(unit->startPeriodicMeasurement(m)); + // Return False if already started + EXPECT_FALSE(unit->startPeriodicMeasurement(m)); + + EXPECT_TRUE(unit->inPeriodic()); + + // These APIs result in an error during periodic detection + { + EXPECT_FALSE(unit->writeTemperatureOffset(0)); + float offset{}; + EXPECT_FALSE(unit->readTemperatureOffset(offset)); + + EXPECT_FALSE(unit->writeSensorAltitude(0)); + uint16_t altitude{}; + EXPECT_FALSE(unit->readSensorAltitude(altitude)); + + int16_t correction{}; + EXPECT_FALSE(unit->performForcedRecalibration(0, correction)); + + EXPECT_FALSE(unit->writeAutomaticSelfCalibrationEnabled(true)); + bool enabled{}; + EXPECT_FALSE(unit->readAutomaticSelfCalibrationEnabled(enabled)); + + EXPECT_FALSE(unit->startLowPowerPeriodicMeasurement()); + + EXPECT_FALSE(unit->writePersistSettings()); + + uint64_t sno{}; + EXPECT_FALSE(unit->readSerialNumber(sno)); + + bool malfunction{}; + EXPECT_FALSE(unit->performSelfTest(malfunction)); + + EXPECT_FALSE(unit->performFactoryReset()); + + EXPECT_FALSE(unit->reInit()); + } + // These APIs can be used during periodic detection + EXPECT_TRUE(unit->writeAmbientPressure(0.0f)); + } +} + +TEST_P(TestSCD4x, Periodic) +{ + SCOPED_TRACE(ustr); + + uint32_t idx{}; + for (auto&& m : mode_table) { + auto s = m5::utility::formatString("Mode:%u", m); + SCOPED_TRACE(s); + + EXPECT_FALSE(unit->inPeriodic()); + + EXPECT_TRUE(unit->startPeriodicMeasurement(m)); + EXPECT_TRUE(unit->inPeriodic()); + EXPECT_EQ(unit->updatedMillis(), 0); + + auto it = interval_table[idx]; + auto elapsed = test_periodic(unit.get(), STORED_SIZE, it); + + EXPECT_TRUE(unit->stopPeriodicMeasurement()); + EXPECT_FALSE(unit->inPeriodic()); + + EXPECT_NE(elapsed, 0); + EXPECT_GE(elapsed, STORED_SIZE * it); + + // + EXPECT_EQ(unit->available(), STORED_SIZE); + EXPECT_FALSE(unit->empty()); + EXPECT_TRUE(unit->full()); + + uint32_t cnt{STORED_SIZE / 2}; + while (cnt-- && unit->available()) { + EXPECT_NE(unit->co2(), 0); + EXPECT_TRUE(std::isfinite(unit->celsius())); + EXPECT_TRUE(std::isfinite(unit->fahrenheit())); + EXPECT_TRUE(std::isfinite(unit->humidity())); + + EXPECT_EQ(unit->co2(), unit->oldest().co2()); + EXPECT_FLOAT_EQ(unit->celsius(), unit->oldest().celsius()); + EXPECT_FLOAT_EQ(unit->fahrenheit(), unit->oldest().fahrenheit()); + EXPECT_FLOAT_EQ(unit->humidity(), unit->oldest().humidity()); + + EXPECT_FALSE(unit->empty()); + unit->discard(); + } + EXPECT_EQ(unit->available(), STORED_SIZE / 2); + EXPECT_FALSE(unit->empty()); + EXPECT_FALSE(unit->full()); + + unit->flush(); + EXPECT_EQ(unit->available(), 0); + EXPECT_TRUE(unit->empty()); + EXPECT_FALSE(unit->full()); + + EXPECT_EQ(unit->co2(), 0); + EXPECT_FALSE(std::isfinite(unit->celsius())); + EXPECT_FALSE(std::isfinite(unit->fahrenheit())); + EXPECT_FALSE(std::isfinite(unit->humidity())); + } +} + +TEST_P(TestSCD4x, OnChipOutputSignalCompensation) +{ + SCOPED_TRACE(ustr); + + { + constexpr float OFFSET{1.234f}; + EXPECT_TRUE(unit->writeTemperatureOffset(OFFSET)); + float offset{}; + EXPECT_TRUE(unit->readTemperatureOffset(offset)); + EXPECT_EQ(float_to_uint16(offset), float_to_uint16(OFFSET)) << "offset:" << offset << " OFFSET:" << OFFSET; + } + + { + constexpr uint16_t ALTITUDE{3776}; + EXPECT_TRUE(unit->writeSensorAltitude(ALTITUDE)); + uint16_t altitude{}; + EXPECT_TRUE(unit->readSensorAltitude(altitude)); + EXPECT_EQ(altitude, ALTITUDE); + } +} + +TEST_P(TestSCD4x, FieldCalibration) +{ + SCOPED_TRACE(ustr); + + { + int16_t correction{}; + EXPECT_TRUE(unit->performForcedRecalibration(1234, correction)); + } + + { + EXPECT_TRUE(unit->writeAutomaticSelfCalibrationEnabled(false)); + bool enabled{}; + EXPECT_TRUE(unit->readAutomaticSelfCalibrationEnabled(enabled)); + EXPECT_FALSE(enabled); + + EXPECT_TRUE(unit->writeAutomaticSelfCalibrationEnabled(true)); + EXPECT_TRUE(unit->readAutomaticSelfCalibrationEnabled(enabled)); + EXPECT_TRUE(enabled); + } +} + +TEST_P(TestSCD4x, AdvancedFeatures) +{ + SCOPED_TRACE(ustr); + + { + // Read direct [MSB] SNB_3, SNB_2, CRC, SNB_1, SNB_0, CRC [LSB] + std::array rbuf{}; + EXPECT_TRUE(unit->readRegister(m5::unit::scd4x::command::GET_SERIAL_NUMBER, rbuf.data(), rbuf.size(), 1)); + + // M5_LOGI("%02x%02x%02x%02x%02x%02x", rbuf[0], rbuf[1], rbuf[3], + // rbuf[4], + // rbuf[6], rbuf[7]); + + m5::types::big_uint16_t w0(rbuf[0], rbuf[1]); + m5::types::big_uint16_t w1(rbuf[3], rbuf[4]); + m5::types::big_uint16_t w2(rbuf[6], rbuf[7]); + uint64_t d_sno = (((uint64_t)w0.get()) << 32) | (((uint64_t)w1.get()) << 16) | ((uint64_t)w2.get()); + + // M5_LOGI("d_sno[%llX]", d_sno); + + // + uint64_t sno{}; + char ssno[13]{}; + EXPECT_TRUE(unit->readSerialNumber(sno)); + EXPECT_TRUE(unit->readSerialNumber(ssno)); + + // M5_LOGI("s:[%s] uint64:[%x]", ssno, sno); + + EXPECT_EQ(sno, d_sno); + + std::stringstream stream; + stream << std::uppercase << std::setw(12) << std::hex << std::setfill('0') << sno; + std::string s(stream.str()); + EXPECT_STREQ(s.c_str(), ssno); + } + + // Set + constexpr float OFFSET{1.234f}; + EXPECT_TRUE(unit->writeTemperatureOffset(OFFSET)); + constexpr uint16_t ALTITUDE{3776}; + EXPECT_TRUE(unit->writeSensorAltitude(ALTITUDE)); + EXPECT_TRUE(unit->writeAutomaticSelfCalibrationEnabled(false)); + + EXPECT_TRUE(unit->writePersistSettings()); // Save EEPROM + + // Overwrite settings + EXPECT_TRUE(unit->writeTemperatureOffset(OFFSET * 2)); + EXPECT_TRUE(unit->writeSensorAltitude(ALTITUDE * 2)); + EXPECT_TRUE(unit->writeAutomaticSelfCalibrationEnabled(true)); + + float off{}; + uint16_t alt{}; + bool enabled{}; + + EXPECT_TRUE(unit->readTemperatureOffset(off)); + EXPECT_TRUE(unit->readSensorAltitude(alt)); + EXPECT_TRUE(unit->readAutomaticSelfCalibrationEnabled(enabled)); + EXPECT_EQ(float_to_uint16(off), float_to_uint16(OFFSET * 2)); + EXPECT_EQ(alt, ALTITUDE * 2); + EXPECT_TRUE(enabled); + + EXPECT_TRUE(unit->reInit()); // Load EEPROM + + // Check saved settings + EXPECT_TRUE(unit->readTemperatureOffset(off)); + EXPECT_TRUE(unit->readSensorAltitude(alt)); + EXPECT_TRUE(unit->readAutomaticSelfCalibrationEnabled(enabled)); + EXPECT_EQ(float_to_uint16(off), float_to_uint16(OFFSET)); + EXPECT_EQ(alt, ALTITUDE); + EXPECT_FALSE(enabled); + + bool malfunction{}; + EXPECT_TRUE(unit->performSelfTest(malfunction)); + + EXPECT_TRUE(unit->performFactoryReset()); // Reset EEPROM + + EXPECT_TRUE(unit->readTemperatureOffset(off)); + EXPECT_TRUE(unit->readSensorAltitude(alt)); + EXPECT_TRUE(unit->readAutomaticSelfCalibrationEnabled(enabled)); + EXPECT_NE(float_to_uint16(off), float_to_uint16(OFFSET)); + EXPECT_NE(alt, ALTITUDE); + EXPECT_TRUE(enabled); +} diff --git a/test/embedded/test_scd40/scd40_test.cpp b/test/embedded/test_scd40/scd40_test.cpp index c59c318..acdc7d8 100644 --- a/test/embedded/test_scd40/scd40_test.cpp +++ b/test/embedded/test_scd40/scd40_test.cpp @@ -4,7 +4,7 @@ * SPDX-License-Identifier: MIT */ /* - UnitTest for UnitSCD40/41 + UnitTest for UnitSCD40 */ #include #include @@ -13,49 +13,26 @@ #include #include #include -#include #include #include -// #define UNIT_TEST_SCD41 - using namespace m5::unit::googletest; using namespace m5::unit; using namespace m5::unit::scd4x; +using m5::unit::types::elapsed_time_t; const ::testing::Environment* global_fixture = ::testing::AddGlobalTestEnvironment(new GlobalFixture<400000U>()); -class TestSCD40 : public ComponentTestBase { -protected: - virtual UnitSCD40* get_instance() override - { -#if defined(UNIT_TEST_SCD41) - auto ptr = new m5::unit::UnitSCD41(); -#else - auto ptr = new m5::unit::UnitSCD40(); -#endif - if (ptr) { - auto ccfg = ptr->component_config(); - ccfg.stored_size = 3; - ptr->component_config(ccfg); +constexpr uint32_t STORED_SIZE{4}; - auto cfg = ptr->config(); - cfg.start_periodic = false; - ptr->config(cfg); - } - return ptr; - } - virtual bool is_using_hal() const override - { - return GetParam(); - }; -}; - -class TestSCD41 : public ComponentTestBase { +class TestSCD4x : public ComponentTestBase { protected: - virtual UnitSCD41* get_instance() override + virtual UnitSCD40* get_instance() override { - auto ptr = new m5::unit::UnitSCD41(0x62); + auto ptr = new m5::unit::UnitSCD40(); + auto ccfg = ptr->component_config(); + ccfg.stored_size = STORED_SIZE; + ptr->component_config(ccfg); auto cfg = ptr->config(); cfg.start_periodic = false; ptr->config(cfg); @@ -67,333 +44,23 @@ class TestSCD41 : public ComponentTestBase { }; }; -// INSTANTIATE_TEST_SUITE_P(ParamValues, TestSCD40, -// ::testing::Values(false, true)); -// INSTANTIATE_TEST_SUITE_P(ParamValues, TestSCD40, ::testing::Values(true)); -INSTANTIATE_TEST_SUITE_P(ParamValues, TestSCD40, ::testing::Values(false)); - -#if defined(UNIT_TEST_SCD41) -// INSTANTIATE_TEST_SUITE_P(ParamValues, TestSCD41, -// ::testing::Values(false, true)); -// INSTANTIATE_TEST_SUITE_P(ParamValues, TestSCD41, ::testing::Values(true)); -INSTANTIATE_TEST_SUITE_P(ParamValues, TestSCD41, ::testing::Values(false)); -#endif +// INSTANTIATE_TEST_SUITE_P(ParamValues, TestSCD4x, ::testing::Values(false, true)); +// INSTANTIATE_TEST_SUITE_P(ParamValues, TestSCD4x, ::testing::Values(true)); +INSTANTIATE_TEST_SUITE_P(ParamValues, TestSCD4x, ::testing::Values(false)); namespace { -// flot t uu int16 (temperature) +// float t uu int16 (temperature) same as library constexpr uint16_t float_to_uint16(const float f) { return f * 65536 / 175; } -struct ModeParams { - const char* s; - Mode mode; - uint32_t tolerance; -}; -constexpr ModeParams mode_table[] = { - // {"Normal", Mode::Normal, 1}, - {"LowPower", Mode::LowPower, 500}, +constexpr Mode mode_table[] = {Mode::Normal, Mode::LowPower}; +constexpr uint32_t interval_table[] = { + 5 * 1000, + 30 * 1000, }; -void check_measurement_values(UnitSCD40* u) -{ - EXPECT_NE(u->co2(), 0); - EXPECT_TRUE(std::isfinite(u->latest().celsius())); - EXPECT_TRUE(std::isfinite(u->latest().fahrenheit())); - EXPECT_TRUE(std::isfinite(u->latest().humidity())); -} } // namespace -TEST_P(TestSCD40, BasicCommand) -{ - SCOPED_TRACE(ustr); - - EXPECT_FALSE(unit->inPeriodic()); - - for (auto&& m : mode_table) { - SCOPED_TRACE(m.s); - EXPECT_TRUE(unit->stopPeriodicMeasurement()); - EXPECT_FALSE(unit->inPeriodic()); - - EXPECT_TRUE(unit->startPeriodicMeasurement(m.mode)); - // Return False if already started - EXPECT_FALSE(unit->startPeriodicMeasurement(m.mode)); - - EXPECT_TRUE(unit->inPeriodic()); - - // These APIs result in an error during periodic detection - { - EXPECT_FALSE(unit->writeTemperatureOffset(0)); - float offset{}; - EXPECT_FALSE(unit->readTemperatureOffset(offset)); - - EXPECT_FALSE(unit->writeSensorAltitude(0)); - uint16_t altitude{}; - EXPECT_FALSE(unit->readSensorAltitude(altitude)); - - int16_t correction{}; - EXPECT_FALSE(unit->performForcedRecalibration(0, correction)); - - EXPECT_FALSE(unit->writeAutomaticSelfCalibrationEnabled(true)); - bool enabled{}; - EXPECT_FALSE(unit->readAutomaticSelfCalibrationEnabled(enabled)); - - EXPECT_FALSE(unit->startLowPowerPeriodicMeasurement()); - - EXPECT_FALSE(unit->writePersistSettings()); - - uint64_t sno{}; - EXPECT_FALSE(unit->readSerialNumber(sno)); - - bool malfunction{}; - EXPECT_FALSE(unit->performSelfTest(malfunction)); - - EXPECT_FALSE(unit->performFactoryReset()); - - EXPECT_FALSE(unit->reInit()); - } - // These APIs can be used during periodic detection - EXPECT_TRUE(unit->writeAmbientPressure(0.0f)); - } -} - -TEST_P(TestSCD40, Periodic) -{ - SCOPED_TRACE(ustr); - - // Empty - EXPECT_EQ(unit->co2(), 0U); - EXPECT_FALSE(std::isfinite(unit->temperature())); - EXPECT_FALSE(std::isfinite(unit->celsius())); - EXPECT_FALSE(std::isfinite(unit->fahrenheit())); - EXPECT_FALSE(std::isfinite(unit->humidity())); - - // - uint32_t idx{}; - for (auto&& m : mode_table) { - auto s = m5::utility::formatString("Mode:%s", m.s); - SCOPED_TRACE(s.c_str()); - - EXPECT_TRUE(unit->startPeriodicMeasurement(m.mode)); - EXPECT_TRUE(unit->inPeriodic()); - EXPECT_EQ(unit->updatedMillis(), 0); - test_periodic_measurement(unit.get(), 3, m.tolerance, check_measurement_values); - EXPECT_TRUE(unit->stopPeriodicMeasurement()); - - EXPECT_EQ(unit->available(), 3); - EXPECT_FALSE(unit->empty()); - EXPECT_TRUE(unit->full()); - - if (idx & 1) { - uint32_t cnt{}; - while (unit->available()) { - ++cnt; - - EXPECT_NE(unit->co2(), 0U); - EXPECT_TRUE(std::isfinite(unit->celsius())); - EXPECT_TRUE(std::isfinite(unit->fahrenheit())); - EXPECT_TRUE(std::isfinite(unit->humidity())); - EXPECT_EQ(unit->co2(), unit->oldest().co2()); - EXPECT_FLOAT_EQ(unit->celsius(), unit->oldest().celsius()); - EXPECT_FLOAT_EQ(unit->fahrenheit(), unit->oldest().fahrenheit()); - EXPECT_FLOAT_EQ(unit->humidity(), unit->oldest().humidity()); - - unit->discard(); - } - EXPECT_EQ(cnt, 2); - - EXPECT_EQ(unit->co2(), 0U); - EXPECT_TRUE(std::isnan(unit->celsius())); - EXPECT_TRUE(std::isnan(unit->fahrenheit())); - EXPECT_TRUE(std::isnan(unit->humidity())); - EXPECT_EQ(unit->available(), 0); - EXPECT_TRUE(unit->empty()); - EXPECT_FALSE(unit->full()); - } else { - EXPECT_NE(unit->co2(), 0U); - EXPECT_TRUE(std::isfinite(unit->celsius())); - EXPECT_TRUE(std::isfinite(unit->fahrenheit())); - EXPECT_TRUE(std::isfinite(unit->humidity())); - EXPECT_EQ(unit->co2(), unit->oldest().co2()); - EXPECT_FLOAT_EQ(unit->celsius(), unit->oldest().celsius()); - EXPECT_FLOAT_EQ(unit->fahrenheit(), unit->oldest().fahrenheit()); - EXPECT_FLOAT_EQ(unit->humidity(), unit->oldest().humidity()); - - unit->flush(); - EXPECT_EQ(unit->co2(), 0U); - EXPECT_TRUE(std::isnan(unit->celsius())); - EXPECT_TRUE(std::isnan(unit->fahrenheit())); - EXPECT_TRUE(std::isnan(unit->humidity())); - EXPECT_EQ(unit->available(), 0); - EXPECT_TRUE(unit->empty()); - EXPECT_FALSE(unit->full()); - } - ++idx; - } -} - -TEST_P(TestSCD40, OnChipOutputSignalCompensation) -{ - SCOPED_TRACE(ustr); - - { - constexpr float OFFSET{1.234f}; - EXPECT_TRUE(unit->writeTemperatureOffset(OFFSET)); - float offset{}; - EXPECT_TRUE(unit->readTemperatureOffset(offset)); - EXPECT_EQ(float_to_uint16(offset), float_to_uint16(OFFSET)) << "offset:" << offset << " OFFSET:" << OFFSET; - } - - { - constexpr uint16_t ALTITUDE{3776}; - EXPECT_TRUE(unit->writeSensorAltitude(ALTITUDE)); - uint16_t altitude{}; - EXPECT_TRUE(unit->readSensorAltitude(altitude)); - EXPECT_EQ(altitude, ALTITUDE); - } -} - -TEST_P(TestSCD40, FieldCalibration) -{ - SCOPED_TRACE(ustr); - - { - int16_t correction{}; - EXPECT_TRUE(unit->performForcedRecalibration(1234, correction)); - } - - { - EXPECT_TRUE(unit->writeAutomaticSelfCalibrationEnabled(false)); - bool enabled{}; - EXPECT_TRUE(unit->readAutomaticSelfCalibrationEnabled(enabled)); - EXPECT_FALSE(enabled); - - EXPECT_TRUE(unit->writeAutomaticSelfCalibrationEnabled(true)); - EXPECT_TRUE(unit->readAutomaticSelfCalibrationEnabled(enabled)); - EXPECT_TRUE(enabled); - } -} - -TEST_P(TestSCD40, AdvancedFeatures) -{ - SCOPED_TRACE(ustr); - - { - // Read direct [MSB] SNB_3, SNB_2, CRC, SNB_1, SNB_0, CRC [LSB] - std::array rbuf{}; - EXPECT_TRUE(unit->readRegister(m5::unit::scd4x::command::GET_SERIAL_NUMBER, rbuf.data(), rbuf.size(), 1)); - - // M5_LOGI("%02x%02x%02x%02x%02x%02x", rbuf[0], rbuf[1], rbuf[3], - // rbuf[4], - // rbuf[6], rbuf[7]); - - m5::types::big_uint16_t w0(rbuf[0], rbuf[1]); - m5::types::big_uint16_t w1(rbuf[3], rbuf[4]); - m5::types::big_uint16_t w2(rbuf[6], rbuf[7]); - uint64_t d_sno = (((uint64_t)w0.get()) << 32) | (((uint64_t)w1.get()) << 16) | ((uint64_t)w2.get()); - - // M5_LOGI("d_sno[%llX]", d_sno); - - // - uint64_t sno{}; - char ssno[13]{}; - EXPECT_TRUE(unit->readSerialNumber(sno)); - EXPECT_TRUE(unit->readSerialNumber(ssno)); - - // M5_LOGI("s:[%s] uint64:[%x]", ssno, sno); - - EXPECT_EQ(sno, d_sno); - - std::stringstream stream; - stream << std::uppercase << std::setw(12) << std::hex << std::setfill('0') << sno; - std::string s(stream.str()); - EXPECT_STREQ(s.c_str(), ssno); - } - - // Set - constexpr float OFFSET{1.234f}; - EXPECT_TRUE(unit->writeTemperatureOffset(OFFSET)); - constexpr uint16_t ALTITUDE{3776}; - EXPECT_TRUE(unit->writeSensorAltitude(ALTITUDE)); - EXPECT_TRUE(unit->writeAutomaticSelfCalibrationEnabled(false)); - - EXPECT_TRUE(unit->writePersistSettings()); // Save EEPROM - - // Overwrite settings - EXPECT_TRUE(unit->writeTemperatureOffset(OFFSET * 2)); - EXPECT_TRUE(unit->writeSensorAltitude(ALTITUDE * 2)); - EXPECT_TRUE(unit->writeAutomaticSelfCalibrationEnabled(true)); - - float off{}; - uint16_t alt{}; - bool enabled{}; - - EXPECT_TRUE(unit->readTemperatureOffset(off)); - EXPECT_TRUE(unit->readSensorAltitude(alt)); - EXPECT_TRUE(unit->readAutomaticSelfCalibrationEnabled(enabled)); - EXPECT_EQ(float_to_uint16(off), float_to_uint16(OFFSET * 2)); - EXPECT_EQ(alt, ALTITUDE * 2); - EXPECT_TRUE(enabled); - - EXPECT_TRUE(unit->reInit()); // Load EEPROM - - // Check saved settings - EXPECT_TRUE(unit->readTemperatureOffset(off)); - EXPECT_TRUE(unit->readSensorAltitude(alt)); - EXPECT_TRUE(unit->readAutomaticSelfCalibrationEnabled(enabled)); - EXPECT_EQ(float_to_uint16(off), float_to_uint16(OFFSET)); - EXPECT_EQ(alt, ALTITUDE); - EXPECT_FALSE(enabled); - - bool malfunction{}; - EXPECT_TRUE(unit->performSelfTest(malfunction)); - - EXPECT_TRUE(unit->performFactoryReset()); // Reset EEPROM - - EXPECT_TRUE(unit->readTemperatureOffset(off)); - EXPECT_TRUE(unit->readSensorAltitude(alt)); - EXPECT_TRUE(unit->readAutomaticSelfCalibrationEnabled(enabled)); - EXPECT_NE(float_to_uint16(off), float_to_uint16(OFFSET)); - EXPECT_NE(alt, ALTITUDE); - EXPECT_TRUE(enabled); -} - -#if defined(UNIT_TEST_SCD41) -TEST_P(TestSCD41, LowPowerSingleshot) -{ - SCOPED_TRACE(ustr); - - { - UnitSCD40::Data d{}; - EXPECT_TRUE(unit->measureSingleshot(d)); - EXPECT_NE(d.co2(), 0); - EXPECT_TRUE(std::isfinite(d.temperature())); - EXPECT_TRUE(std::isfinite(d.humidity())); - - EXPECT_FALSE(unit->measureSingleshot(d)); - m5::utility::delay(5000); - - EXPECT_TRUE(unit->measureSingleshot(d)); - EXPECT_NE(d.co2(), 0); - EXPECT_TRUE(std::isfinite(d.temperature())); - EXPECT_TRUE(std::isfinite(d.humidity())); - } - - { - UnitSCD40::Data d{}; - EXPECT_TRUE(unit->measureSingleshotRHT(d)); - EXPECT_EQ(d.co2(), 0); - EXPECT_TRUE(std::isfinite(d.temperature())); - EXPECT_TRUE(std::isfinite(d.humidity())); - - EXPECT_FALSE(unit->measureSingleshotRHT(d)); - m5::utility::delay(50); - - EXPECT_TRUE(unit->measureSingleshotRHT(d)); - EXPECT_EQ(d.co2(), 0); - EXPECT_TRUE(std::isfinite(d.temperature())); - EXPECT_TRUE(std::isfinite(d.humidity())); - } -} -#endif +#include "../scd4x_test.inl" diff --git a/test/embedded/test_scd41/scd41_test.cpp b/test/embedded/test_scd41/scd41_test.cpp new file mode 100644 index 0000000..b291298 --- /dev/null +++ b/test/embedded/test_scd41/scd41_test.cpp @@ -0,0 +1,105 @@ +/* + * SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +/* + UnitTest for UnitSCD41 +*/ +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace m5::unit::googletest; +using namespace m5::unit; +using namespace m5::unit::scd4x; +using m5::unit::types::elapsed_time_t; + +const ::testing::Environment* global_fixture = ::testing::AddGlobalTestEnvironment(new GlobalFixture<400000U>()); + +constexpr uint32_t STORED_SIZE{4}; + +class TestSCD4x : public ComponentTestBase { +protected: + virtual UnitSCD41* get_instance() override + { + auto ptr = new m5::unit::UnitSCD41(); + auto ccfg = ptr->component_config(); + ccfg.stored_size = STORED_SIZE; + ptr->component_config(ccfg); + auto cfg = ptr->config(); + cfg.start_periodic = false; + ptr->config(cfg); + return ptr; + } + virtual bool is_using_hal() const override + { + return GetParam(); + }; +}; + +// INSTANTIATE_TEST_SUITE_P(ParamValues, TestSCD4x, ::testing::Values(false, true)); +// INSTANTIATE_TEST_SUITE_P(ParamValues, TestSCD4x, ::testing::Values(true)); +INSTANTIATE_TEST_SUITE_P(ParamValues, TestSCD4x, ::testing::Values(false)); + +namespace { +// float t uu int16 (temperature) same as library +constexpr uint16_t float_to_uint16(const float f) +{ + return f * 65536 / 175; +} + +constexpr Mode mode_table[] = {Mode::Normal, Mode::LowPower}; +constexpr uint32_t interval_table[] = { + 5 * 1000, + 30 * 1000, +}; +} // namespace + +#include "../scd4x_test.inl" + +TEST_P(TestSCD4x, Singleshot) +{ + SCOPED_TRACE(ustr); + { + Data d{}; + EXPECT_FALSE(unit->inPeriodic()); + EXPECT_TRUE(unit->measureSingleshot(d)); + EXPECT_NE(d.co2(), 0); + EXPECT_TRUE(std::isfinite(d.temperature())); + EXPECT_TRUE(std::isfinite(d.humidity())); + + EXPECT_TRUE(unit->startPeriodicMeasurement()); + + EXPECT_TRUE(unit->inPeriodic()); + EXPECT_FALSE(unit->measureSingleshot(d)); + EXPECT_EQ(d.co2(), 0); + EXPECT_FLOAT_EQ(d.temperature(), -45.f); + EXPECT_FLOAT_EQ(d.humidity(), 0.0f); + + EXPECT_TRUE(unit->stopPeriodicMeasurement()); + } + { + Data d{}; + EXPECT_FALSE(unit->inPeriodic()); + EXPECT_TRUE(unit->measureSingleshotRHT(d)); + EXPECT_EQ(d.co2(), 0); + EXPECT_TRUE(std::isfinite(d.temperature())); + EXPECT_TRUE(std::isfinite(d.humidity())); + + EXPECT_TRUE(unit->startPeriodicMeasurement()); + + EXPECT_TRUE(unit->inPeriodic()); + EXPECT_FALSE(unit->measureSingleshotRHT(d)); + EXPECT_EQ(d.co2(), 0); + EXPECT_FLOAT_EQ(d.temperature(), -45.f); + EXPECT_FLOAT_EQ(d.humidity(), 0.0f); + + EXPECT_TRUE(unit->stopPeriodicMeasurement()); + } +} diff --git a/unit_co2_env.ini b/unit_co2_env.ini index 9e88391..429e069 100644 --- a/unit_co2_env.ini +++ b/unit_co2_env.ini @@ -87,7 +87,94 @@ lib_deps = ${CoreInk.lib_deps} ${test_fw.lib_deps} test_filter= embedded/test_scd40 +; SCD41 +[env:test_SCD41_Core] +extends=Core, option_release, arduino_latest +lib_deps = ${Core.lib_deps} + ${test_fw.lib_deps} +test_filter= embedded/test_scd41 + +[env:test_SCD41_Core2] +extends=Core2, option_release, arduino_latest +lib_deps = ${Core2.lib_deps} + ${test_fw.lib_deps} +test_filter= embedded/test_scd41 + +[env:test_SCD41_CoreS3] +extends=CoreS3, option_release, arduino_latest +lib_deps = ${CoreS3.lib_deps} + ${test_fw.lib_deps} +test_filter= embedded/test_scd41 + +[env:test_SCD41_Fire] +extends=Fire, option_release, arduino_latest +lib_deps = ${Fire.lib_deps} + ${test_fw.lib_deps} +test_filter= embedded/test_scd41 + +[env:test_SCD41_StampS3] +extends=StampS3, option_release, arduino_latest +lib_deps = ${StampS3.lib_deps} + ${test_fw.lib_deps} +test_filter= embedded/test_scd41 + +[env:test_SCD41_Dial] +extends=StampS3, option_release, arduino_latest +lib_deps = ${StampS3.lib_deps} + ${test_fw.lib_deps} + m5stack/M5Dial +test_filter= embedded/test_scd41 + +[env:test_SCD41_AtomMatrix] +extends=AtomMatrix, option_release, arduino_latest +lib_deps = ${AtomMatrix.lib_deps} + ${test_fw.lib_deps} +test_filter= embedded/test_scd41 + +[env:test_SCD41_AtomS3] +extends=AtomS3, option_release, arduino_latest +lib_deps = ${AtomS3.lib_deps} + ${test_fw.lib_deps} +test_filter= embedded/test_scd41 + +[env:test_SCD41_AtomS3R] +extends=AtomS3R, option_release, arduino_latest +lib_deps = ${AtomS3R.lib_deps} + ${test_fw.lib_deps} +test_filter= embedded/test_scd41 + +[env:test_SCD41_NanoC6] +extends=NanoC6, option_release, arduino_latest +lib_deps = ${NanoC6.lib_deps} + ${test_fw.lib_deps} +test_filter= embedded/test_scd41 + +[env:test_SCD41_StickCPlus] +extends=StickCPlus, option_release, arduino_latest +lib_deps = ${StickCPlus.lib_deps} + ${test_fw.lib_deps} +test_filter= embedded/test_scd41 + +[env:test_SCD41_StickCPlus2] +extends=StickCPlus2, option_release, arduino_latest +lib_deps = ${StickCPlus2.lib_deps} + ${test_fw.lib_deps} +test_filter= embedded/test_scd41 + +[env:test_SCD41_Paper] +extends=Paper, option_release, arduino_latest +lib_deps = ${Paper.lib_deps} + ${test_fw.lib_deps} +test_filter= embedded/test_scd41 + +[env:test_SCD41_CoreInk] +extends=CoreInk, option_release, arduino_latest +lib_deps = ${CoreInk.lib_deps} + ${test_fw.lib_deps} +test_filter= embedded/test_scd41 + ;Examples +; UnitCO2 [env:UnitCO2_PlotToSerial_Core_Arduino_latest] extends=Core, option_release, arduino_latest build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitCO2/PlotToSerial> @@ -167,3 +254,84 @@ build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitCO2/Plot [env:UnitCO2_PlotToSerial_Fire_Arduino_4_4_0] extends=Fire, option_release, arduino_4_4_0 build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitCO2/PlotToSerial> + +; UnitCO2L +[env:UnitCO2L_PlotToSerial_Core_Arduino_latest] +extends=Core, option_release, arduino_latest +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitCO2L/PlotToSerial> + +[env:UnitCO2L_PlotToSerial_Core_Arduino_5_4_0] +extends=Core, option_release, arduino_5_4_0 +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitCO2L/PlotToSerial> + +[env:UnitCO2L_PlotToSerial_Core_Arduino_4_4_0] +extends=Core, option_release, arduino_4_4_0 +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitCO2L/PlotToSerial> + +[env:UnitCO2L_PlotToSerial_Core2_Arduino_latest] +extends=Core2, option_release, arduino_latest +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitCO2L/PlotToSerial> + +[env:UnitCO2L_PlotToSerial_Core2_Arduino_5_4_0] +extends=Core2, option_release, arduino_5_4_0 +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitCO2L/PlotToSerial> + +[env:UnitCO2L_PlotToSerial_Core2_Arduino_4_4_0] +extends=Core2, option_release, arduino_4_4_0 +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitCO2L/PlotToSerial> + +[env:UnitCO2L_PlotToSerial_CoreS3_Arduino_latest] +extends=CoreS3, option_release, arduino_latest +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitCO2L/PlotToSerial> + +[env:UnitCO2L_PlotToSerial_StampS3_Arduino_latest] +extends=StampS3, option_release, arduino_latest +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitCO2L/PlotToSerial> + +[env:UnitCO2L_PlotToSerial_AtomMatrix_Arduino_latest] +extends=AtomMatrix, option_release, arduino_latest +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitCO2L/PlotToSerial> + +[env:UnitCO2L_PlotToSerial_AtomS3_Arduino_latest] +extends=AtomS3, option_release, arduino_latest +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitCO2L/PlotToSerial> + +[env:UnitCO2L_PlotToSerial_AtomS3R_Arduino_latest] +extends=AtomS3R, option_release, arduino_latest +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitCO2L/PlotToSerial> + +[env:UnitCO2L_PlotToSerial_Dial_Arduino_latest] +extends=Dial, option_release, arduino_latest +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitCO2L/PlotToSerial> + +[env:UnitCO2L_PlotToSerial_NanoC6_Arduino_latest] +extends=NanoC6, option_release, arduino_latest +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitCO2L/PlotToSerial> + +[env:UnitCO2L_PlotToSerial_StickCPlus_Arduino_latest] +extends=StickCPlus, option_release, arduino_latest +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitCO2L/PlotToSerial> + +[env:UnitCO2L_PlotToSerial_StickCPlus2_Arduino_latest] +extends=StickCPlus2, option_release, arduino_latest +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitCO2L/PlotToSerial> + +[env:UnitCO2L_PlotToSerial_Paper_Arduino_latest] +extends=Paper, option_release, arduino_latest +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitCO2L/PlotToSerial> + +[env:UnitCO2L_PlotToSerial_CoreInk_Arduino_latest] +extends=CoreInk, option_release, arduino_latest +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitCO2L/PlotToSerial> + +[env:UnitCO2L_PlotToSerial_Fire_Arduino_latest] +extends=Fire, option_release, arduino_latest +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitCO2L/PlotToSerial> + +[env:UnitCO2L_PlotToSerial_Fire_Arduino_5_4_0] +extends=Fire, option_release, arduino_5_4_0 +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitCO2L/PlotToSerial> + +[env:UnitCO2L_PlotToSerial_Fire_Arduino_4_4_0] +extends=Fire, option_release, arduino_4_4_0 +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitCO2L/PlotToSerial> From 2ac6b3e3e5a0978c1b17d265bdf8ebe982de6b7e Mon Sep 17 00:00:00 2001 From: GOB Date: Fri, 31 Jan 2025 19:54:07 +0900 Subject: [PATCH 6/8] Fixes Arduino Lint error --- library.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.properties b/library.properties index d74564a..af0709d 100644 --- a/library.properties +++ b/library.properties @@ -8,4 +8,4 @@ category=Device Control url=https://github.com/m5stack/M5Unit-ENV architectures=esp32 includes=M5UnitENV.h, M5UnitUnifiedENV.h -depends=BME68x Sensor library,M5UnitUnified(>=0.0.5),M5Utility,M5HAL,bsec2 +depends=M5UnitUnified (>=0.0.5),M5Utility,M5HAL,bsec2,BME68x Sensor library From b5541cd3d716cb0f550955d3e3a0744eb3002c3d Mon Sep 17 00:00:00 2001 From: GOB Date: Sat, 1 Feb 2025 14:45:43 +0900 Subject: [PATCH 7/8] Update README --- README.md | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index fa45baa..bd42d66 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,13 @@ CO2 is a photoacoustic Carbon Dioxide (CO2) Unit that will tell you the CO2 PPM Library for Unit ENV using [M5UnitUnified](https://github.com/m5stack/M5UnitUnified). M5UnitUnified is a library for unified handling of various M5 units products. -M5UnitUnified currently supports the following units SKU:U001-C, SKU:U103, SKU:U169, and SKU:U088. +### Supported units +- Unit CO2 (SKU:U103) +- Unit CO2L (SKU:U104) +- Unit ENVIII (SKU:SKU:U001-C) +- Unit ENVIV (SKU:SKU:U001-D) +- Unit ENVPro (SKU:U169) +- Unit TVOC (SKU:U088) ### SKU:U088 @@ -67,14 +73,6 @@ See also examples using conventional methods here. #include // For UnitUnified //#include // When using M5UnitUnified, do not use it at the same time as conventional libraries ``` -### Supported units -- Unit CO2 -- Unit CO2L -- Unit ENVIII -- Unit ENVIV -- Unit ENVPro -- Unit TVOC - Supported units will be added in the future. ### Required Libraries: From cc3d100746d491808d64f5dd74ac26f8946b84b4 Mon Sep 17 00:00:00 2001 From: GOB Date: Sat, 1 Feb 2025 14:47:10 +0900 Subject: [PATCH 8/8] Update README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bd42d66..feb26b4 100644 --- a/README.md +++ b/README.md @@ -47,8 +47,8 @@ M5UnitUnified is a library for unified handling of various M5 units products. ### Supported units - Unit CO2 (SKU:U103) - Unit CO2L (SKU:U104) -- Unit ENVIII (SKU:SKU:U001-C) -- Unit ENVIV (SKU:SKU:U001-D) +- Unit ENVIII (SKU:U001-C) +- Unit ENVIV (SKU:U001-D) - Unit ENVPro (SKU:U169) - Unit TVOC (SKU:U088)