diff --git a/TODO.md b/TODO.md index 8930c517..09d96d8f 100644 --- a/TODO.md +++ b/TODO.md @@ -1,4 +1,8 @@ # MuMo SW V3 + +## known bugs +when unplugging the USB, sometimes the display is not updated immediately, but only at the next RTCtick ??? + ## ToDo's 2. make SNR work both in devStatusReq and on display diff --git a/lib/application/maincontroller.cpp b/lib/application/maincontroller.cpp index 74c9667e..3f4b429e 100644 --- a/lib/application/maincontroller.cpp +++ b/lib/application/maincontroller.cpp @@ -315,7 +315,7 @@ void mainController::goTo(mainState newState) { } void mainController::runSleep() { - if (!power::hasUsbPower() && !debugPort::isDebugProbePresent()) { + if (power::noUsbPowerDelayed() && !debugPort::isDebugProbePresent()) { prepareSleep(); goSleep(); wakeUp(); diff --git a/lib/power/power.cpp b/lib/power/power.cpp index 3b776009..7bf237cd 100644 --- a/lib/power/power.cpp +++ b/lib/power/power.cpp @@ -13,11 +13,19 @@ bool power::mockUsbPower{false}; #endif bool power::usbPower{false}; +uint32_t power::usbPowerOffCount{usbPowerOffCountMax}; bool power::hasUsbPower() { #ifndef generic - bool pinState = (GPIO_PIN_SET == HAL_GPIO_ReadPin(GPIOB, usbPowerPresent_Pin)); - return pinState; + bool result = (GPIO_PIN_SET == HAL_GPIO_ReadPin(GPIOB, usbPowerPresent_Pin)); + if (result) { + usbPowerOffCount = usbPowerOffCountMax; + } else { + if (usbPowerOffCount > 0) { + usbPowerOffCount--; + } + } + return result; #else return mockUsbPower; #endif @@ -42,4 +50,3 @@ bool power::isUsbRemoved() { return false; } } - diff --git a/lib/power/power.hpp b/lib/power/power.hpp index 1058fb2e..a01b6493 100644 --- a/lib/power/power.hpp +++ b/lib/power/power.hpp @@ -10,12 +10,15 @@ class power { static bool isUsbConnected(); static bool isUsbRemoved(); static bool hasUsbPower(); + static bool noUsbPowerDelayed() { return (usbPowerOffCount == 0); }; #ifndef unitTesting private: #endif static bool usbPower; + static uint32_t usbPowerOffCount; + static constexpr uint32_t usbPowerOffCountMax{16U}; #ifdef generic static bool mockUsbPower; #endif diff --git a/lib/sensors/bme680.cpp b/lib/sensors/bme680.cpp index 97b1b816..c07e866b 100644 --- a/lib/sensors/bme680.cpp +++ b/lib/sensors/bme680.cpp @@ -133,8 +133,6 @@ void bme680::run() { } void bme680::startSampling() { - state = sensorDeviceState::sampling; - // run a ADC conversion cycle for Temp Hum and Presure, and when ready, read and store all raw ADC results writeRegister(bme680::registers::ctrl_hum, 0b00000001); writeRegister(bme680::registers::ctrl_meas, 0b00100101); state = sensorDeviceState::sampling; diff --git a/lib/sensors/bme680.hpp b/lib/sensors/bme680.hpp index ceae86fd..de5672b4 100644 --- a/lib/sensors/bme680.hpp +++ b/lib/sensors/bme680.hpp @@ -85,8 +85,7 @@ class bme680 { static uint32_t rawDataBarometricPressure; static uint32_t rawDataRelativeHumidity; - static bool awake; - + // Calibration data static float calibrationCoefficientTemperature1; static float calibrationCoefficientTemperature2; @@ -113,87 +112,4 @@ class bme680 { static float calibrationCoefficientHumidity7; friend class sensorDeviceCollection; -}; - -/* -Initialization -Write I2C : register = [E0], data[1] = [B6 ] -Read I2C : register = [D0], data[1] = [61 ] -Read I2C : register = [F0], data[1] = [00 ] -Read I2C : register = [8A], data[23] = [62 67 03 10 8F 90 68 D7 58 00 38 22 62 FF 2C 1E 00 00 71 F4 5B F6 1E ] -Read I2C : register = [E1], data[14] = [3D BD 37 00 2D 14 78 9C B6 65 AB DC FB 12 ] -Read I2C : register = [00], data[5] = [28 AA 16 4C 03 ] - -Configuration -Read I2C : register = [74], data[1] = [00 ] -Read I2C : register = [74], data[1] = [00 ] -Read I2C : register = [71], data[5] = [00 00 00 00 00 ] -Write I2C : register = [71], data[9] = [80 72 05 73 00 74 44 75 00 ] -Set Heater Config -Read I2C : register = [74], data[1] = [44 ] -Write I2C : register = [5A], data[1] = [78 ] -Write I2C : register = [64], data[1] = [59 ] -Read I2C : register = [70], data[2] = [00 80 ] -Write I2C : register = [70], data[3] = [08 71 80 ] - -Set Operation Mode -Read I2C : register = [74], data[1] = [44 ] -Write I2C : register = [74], data[1] = [45 ] - -Read Data -Read I2C : register = [1D], data[17] = [80 FF 48 92 00 75 F8 00 62 20 80 00 00 00 04 00 04 ] -Read I2C : register = [5A], data[1] = [78 ] -Read I2C : register = [50], data[1] = [00 ] -Read I2C : register = [64], data[1] = [59 ] - - -par_h1 uint16_t 893 -par_h2 uint16_t 987 -par_h3 int8_t 0 '\0' -par_h4 int8_t 45 '-' -par_h5 int8_t 20 '\024' -par_h6 uint8_t 120 'x' -par_h7 int8_t -100 '\234' -par_gh1 int8_t -5 'รป' -par_gh2 int16_t -9045 -par_gh3 int8_t 18 '\022' - -par_t1 uint16_t 26038 -par_t2 int16_t 26466 -par_t3 int8_t 3 - -par_p1 uint16_t 37007 -par_p2 int16_t -10392 -par_p3 int8_t 88 'X' -par_p4 int16_t 8760 -par_p5 int16_t -158 -par_p6 int8_t 30 '\036' -par_p7 int8_t 44 ',' -par_p8 int16_t -2959 -par_p9 int16_t -2469 -par_p10 uint8_t 30 '\036' -t_fine float 107711.469 -res_heat_range uint8_t 1 '\001' -res_heat_val int8_t 40 '(' -range_sw_err int8_t 0 '\0' - -adc_temp uint32_t 484376 - -mine : -rawData1 uint8_t 16 '\020' -rawData2 uint8_t 143 '\217' -rawData3 uint8_t 144 '\220' -rawData uint32_t 67833 - theirs - buff[5] uint8_t 119 'w' -buff[6] uint8_t 16 '\020' -buff[7] uint8_t 128 '\200' - -My READ -Read I2C : register = [8A], data[23] = [62 67 03 10 8F 90 68 D7 58 00 38 22 62 FF 2C 1E 00 00 71 F4 5B F6 1E ] -Read I2C : register = [E1], data[14] = [3D BD 37 00 2D 14 78 9C B6 65 AB DC FB 12 ] -Read I2C : register = [00], data[5] = [28 AA 16 4C 03 ] - - - -*/ \ No newline at end of file +}; \ No newline at end of file diff --git a/lib/sensors/scd40.cpp b/lib/sensors/scd40.cpp new file mode 100644 index 00000000..3471c8f6 --- /dev/null +++ b/lib/sensors/scd40.cpp @@ -0,0 +1,148 @@ +// ###################################################################################### +// ### Author : Pascal Roobrouck - https://github.com/Strooom ### +// ### License : CC 4.0 BY-NC-SA - https://creativecommons.org/licenses/by-nc-sa/4.0/ ### +// ###################################################################################### + +#include +#include +#include + +#ifndef generic +#include "main.h" +extern I2C_HandleTypeDef hi2c2; +#else +uint8_t mockSCD40Registers[256]; +bool mockSCD40Present{false}; +#include +#endif + +sensorDeviceState scd40::state{sensorDeviceState::unknown}; +sensorChannel scd40::channels[nmbrChannels] = { + {1, "temperature", "~C"}, + {0, "relativeHumidity", "%RH"}, + {0, "CO2", "ppm"}, +}; + +uint32_t scd40::rawDataTemperature; +uint32_t scd40::rawDataRelativeHumidity; +uint32_t scd40::rawCo2; + +bool scd40::isPresent() { + return (testI2cAddress(i2cAddress)); +} + +void scd40::initialize() { + for (uint32_t channelIndex = 0; channelIndex < nmbrChannels; channelIndex++) { + channels[channelIndex].set(0, 0); + } + writeCommand(scd40::commands::stopPeriodicMeasurement); // stop any previous measurement +#ifndef generic + HAL_Delay(500); // stopping requires 500ms processing +#endif + writeCommand(scd40::commands::startLowPowerPeriodicMeasurement); // now start the low power periodic measurement = 1 sample / 30 seconds + state = sensorDeviceState::sleeping; +} + +void scd40::run() { + if ((state == sensorDeviceState::sampling) && samplingIsReady()) { + readSample(); + + if (channels[temperature].needsSampling()) { + float scd40Temperature = calculateTemperature(rawDataTemperature); + channels[temperature].addSample(scd40Temperature); + } + if (channels[relativeHumidity].needsSampling()) { + float scd40RelativeHumidity = calculateRelativeHumidity(rawDataRelativeHumidity); + channels[relativeHumidity].addSample(scd40RelativeHumidity); + } + if (channels[co2].needsSampling()) { + float scd40CO2 = calculateCO2(rawCo2); + channels[co2].addSample(scd40CO2); + } + state = sensorDeviceState::sleeping; + } +} + +void scd40::startSampling() { + state = sensorDeviceState::sampling; +} + +bool scd40::samplingIsReady() { + uint16_t tmpResult; + writeCommand(scd40::commands::getDataReadyStatus); +#ifndef generic + HAL_Delay(2); +#endif + readData(&tmpResult, 1U); + return (!((tmpResult & 0x07FF) == 0)); // datasheet 3.8.2 : 11 least significant bits are zero -> data not ready +} + +void scd40::readSample() { + uint16_t tmpResult[3]; + writeCommand(scd40::commands::readMeasurement); +#ifndef generic + HAL_Delay(2); +#endif + readData(tmpResult, 3U); + rawCo2 = tmpResult[0]; + rawDataTemperature = tmpResult[1]; + rawDataRelativeHumidity = tmpResult[2]; +} + +float scd40::calculateTemperature(uint32_t rawData) { + float result{0.0F}; + result = -45.0F + ((175.0F * static_cast(rawData)) / 65535.0F); + return result; +} + +float scd40::calculateRelativeHumidity(uint32_t rawData) { + float result{0.0F}; + result = (100.0F * static_cast(rawData)) / 65535.0F; + return result; +} + +float scd40::calculateCO2(uint32_t rawData) { + return static_cast(rawData); +} + +bool scd40::testI2cAddress(const uint8_t addressToTest) { +#ifndef generic + return (HAL_OK == HAL_I2C_IsDeviceReady(&hi2c2, static_cast(addressToTest << 1), halTrials, halTimeout)); +#else + return mockSCD40Present; +#endif +} + +void scd40::writeCommand(const scd40::commands aCommand) { + uint16_t command = static_cast(aCommand); + uint8_t commandAsBytes[2]; + commandAsBytes[0] = static_cast(command >> 8); + commandAsBytes[1] = static_cast(command & 0x00FF); +#ifndef generic + for (uint32_t trials = 0; trials < halTrials; trials++) { + HAL_StatusTypeDef result; + result = HAL_I2C_Master_Transmit(&hi2c2, static_cast(i2cAddress << 1), commandAsBytes, 2U, halTimeout); + if (result == HAL_OK) { + return; + } + HAL_Delay(halTimeout); + } +#endif +} + +void scd40::readData(uint16_t* data, const uint32_t dataLength) { + uint8_t tmpData[dataLength * 3]{}; +#ifndef generic + for (uint32_t trials = 0; trials < halTrials; trials++) { + HAL_StatusTypeDef result; + result = HAL_I2C_Master_Receive(&hi2c2, static_cast(i2cAddress << 1), tmpData, static_cast(dataLength * 3), halTimeout); + if (result == HAL_OK) { + break; + } + HAL_Delay(halTimeout); + } +#endif + for (uint32_t index = 0; index < dataLength; index++) { + data[index] = sensirion::asUint16(&tmpData[index * 3]); + } +} diff --git a/lib/sensors/scd40.cppx b/lib/sensors/scd40.cppx deleted file mode 100644 index 2081795c..00000000 --- a/lib/sensors/scd40.cppx +++ /dev/null @@ -1,240 +0,0 @@ -// ###################################################################################### -// ### Author : Pascal Roobrouck - https://github.com/Strooom ### -// ### License : CC 4.0 BY-NC-SA - https://creativecommons.org/licenses/by-nc-sa/4.0/ ### -// ###################################################################################### - -#include -#include -#include - -#ifndef generic -#include "main.h" -extern I2C_HandleTypeDef hi2c2; -#else -uint8_t mockSCD40Registers[256]; -#include - -#endif - -sensorDeviceState scd40::state{sensorDeviceState::unknown}; -sensorChannel scd40::channels[nmbrChannels] = { - {1, "temperature", "~C"}, - {0, "relativeHumidity", "%RH"}, - {0, "CO2", "ppm"}, -}; - -uint32_t scd40::rawDataTemperature; -uint32_t scd40::rawDataRelativeHumidity; - -bool scd40::isPresent() { - // 1. Check if something is connected to the I2C bus at the address of the scd40 - if (!testI2cAddress(i2cAddress)) { - return false; - } // - // 2. Check if it is a BME680 by reading the chip id register/value - uint8_t chipidValue = readRegister(scd40::registers::chipId); - return (scd40::chipIdValue == chipidValue); -} - -void scd40::initialize() { - // TODO : need to read the sensorChannel settins from EEPROM and restore them - channels[temperature].set(0, 1, 0, 1); - channels[relativeHumidity].set(0, 1, 0, 1); - - uint8_t registerData[42]{}; - readRegisters(0x8A, 23, registerData); // read all calibration data from the sensorChannel and convert to proper coefficients - readRegisters(0xE1, 14, registerData + 23); // - readRegisters(0x00, 5, registerData + 37); // - - state = sensorDeviceState::sleeping; -} - -bool scd40::hasNewMeasurement() { - return (channels[temperature].hasNewValue || channels[relativeHumidity].hasNewValue || channels[co2].hasNewValue); -} - -void scd40::clearNewMeasurements() { - channels[temperature].hasNewValue = false; - channels[relativeHumidity].hasNewValue = false; -} - -float scd40::valueAsFloat(uint32_t index) { - return channels[index].getOutput(); -} - -void scd40::tick() { - if (state != sensorDeviceState::sleeping) { - adjustAllCounters(); - return; - } - - if (anyChannelNeedsSampling()) { - clearNewMeasurements(); - startSampling(); - state = sensorDeviceState::sampling; - } else { - adjustAllCounters(); - } -} - -void scd40::run() { - if ((state == sensorDeviceState::sampling) && samplingIsReady()) { - readSample(); - - if (channels[temperature].needsSampling()) { - float scd40Temperature = calculateTemperature(); - channels[temperature].addSample(scd40Temperature); - if (channels[temperature].hasOutput()) { - channels[temperature].hasNewValue = true; - } - } - - if (channels[relativeHumidity].needsSampling()) { - float scd40RelativeHumidity = calculateRelativeHumidity(); - channels[relativeHumidity].addSample(scd40RelativeHumidity); - if (channels[relativeHumidity].hasOutput()) { - channels[relativeHumidity].hasNewValue = true; - } - } - - if (channels[co2].needsSampling()) { - float scd40BarometricPressure = calculateBarometricPressure(); - channels[co2].addSample(scd40BarometricPressure); - if (channels[co2].hasOutput()) { - channels[co2].hasNewValue = true; - } - } - - state = sensorDeviceState::sleeping; - adjustAllCounters(); - } -} - -bool scd40::anyChannelNeedsSampling() { - return (channels[temperature].needsSampling() || - channels[relativeHumidity].needsSampling() || -} - -void scd40::adjustAllCounters() { - channels[temperature].adjustCounters(); - channels[relativeHumidity].adjustCounters(); -} - -void scd40::startSampling() { - state = sensorDeviceState::sampling; - // run a ADC conversion cycle for Temp Hum and Presure, and when ready, read and store all raw ADC results - // writeRegister(scd40::registers::ctrl_hum, 0b00000001); - // writeRegister(scd40::registers::ctrl_meas, 0b00100101); - state = sensorDeviceState::sampling; -} - -bool scd40::samplingIsReady() { - // uint8_t status = readRegister(scd40::registers::meas_status); - uint8_t statusBit = status & 0x80; - bool noNewData = statusBit == 0; - return !noNewData; -} - -void scd40::readSample() { - constexpr uint32_t nmbrRegisters{8}; - uint8_t registerData[nmbrRegisters]; - readRegisters(0x1F, nmbrRegisters, registerData); // reads 8 registers, from 0x1F up to 0x26, they contain the raw ADC results for temperature, relativeHumidity and pressure - rawDataTemperature = ((static_cast(registerData[3]) << 12) | (static_cast(registerData[4]) << 4) | (static_cast(registerData[5]) >> 4)); - rawDataRelativeHumidity = ((static_cast(registerData[6]) << 8) | (static_cast(registerData[7]))); - rawCo2 = ((static_cast(registerData[0]) << 12) | (static_cast(registerData[1]) << 4) | (static_cast(registerData[2]) >> 4)); -} - -float scd40::calculateTemperature() { - // float var1 = ((((float)rawDataTemperature / 16384.0f) - (calibrationCoefficientTemperature1 / 1024.0f)) * (calibrationCoefficientTemperature2)); - // float var2 = (((((float)rawDataTemperature / 131072.0f) - (calibrationCoefficientTemperature1 / 8192.0f)) * (((float)rawDataTemperature / 131072.0f) - (calibrationCoefficientTemperature1 / 8192.0f))) * (calibrationCoefficientTemperature3 * 16.0f)); - // calibrationCoefficientTemperature4 = var1 + var2; - // return (calibrationCoefficientTemperature4 / 5120.0f); -} - -float scd40::calculateRelativeHumidity() { - // float calc_hum; - - // float temp_comp = ((calibrationCoefficientTemperature4) / 5120.0f); - // float var1 = static_cast(rawDataRelativeHumidity) - (calibrationCoefficientHumidity1 * 16.0f) + ((calibrationCoefficientHumidity3 / 2.0f) * temp_comp); - // float var2 = var1 * (calibrationCoefficientHumidity2 / 262144.0f) * (1.0f + ((calibrationCoefficientHumidity4 / 16384.0f) * temp_comp) + ((calibrationCoefficientHumidity5 / 1048576.0f) * temp_comp * temp_comp)); - // float var3 = calibrationCoefficientHumidity6 / 16384.0f; - // float var4 = calibrationCoefficientHumidity7 / 2097152.0f; - // calc_hum = var2 + ((var3 + (var4 * temp_comp)) * var2 * var2); - // if (calc_hum > 100.0f) { - // calc_hum = 100.0f; - // } else if (calc_hum < 0.0f) { - // calc_hum = 0.0f; - // } - - // return calc_hum; -} - -float scd40::calculateBarometricPressure() { - // float var1 = ((calibrationCoefficientTemperature4 / 2.0f) - 64000.0f); - // float var2 = var1 * var1 * ((calibrationCoefficientPressure6) / (131072.0f)); - // var2 = var2 + (var1 * (calibrationCoefficientPressure5) * 2.0f); - // var2 = (var2 / 4.0f) + ((calibrationCoefficientPressure4) * 65536.0f); - // var1 = ((((calibrationCoefficientPressure3 * var1 * var1) / 16384.0f) + (calibrationCoefficientPressure2 * var1)) / 524288.0f); - // var1 = ((1.0f + (var1 / 32768.0f)) * ((float)calibrationCoefficientPressure1)); - // float result = (1048576.0f - ((float)rawCo2)); - - // if ((int)var1 != 0) { - // result = (((result - (var2 / 4096.0f)) * 6250.0f) / var1); - // var1 = ((calibrationCoefficientPressure9)*result * result) / 2147483648.0f; - // var2 = result * ((calibrationCoefficientPressure8) / 32768.0f); - // float var3 = ((result / 256.0f) * (result / 256.0f) * (result / 256.0f) * (calibrationCoefficientPressure10 / 131072.0f)); - // result = (result + (var1 + var2 + var3 + (calibrationCoefficientPressure7 * 128.0f)) / 16.0f); - // } else { - // result = 0; - // } - - // result = result / 100.0F; // use hPa as unit - - // return result; -} - -bool scd40::testI2cAddress(uint8_t addressToTest) { -#ifndef generic - return (HAL_OK == HAL_I2C_IsDeviceReady(&hi2c2, addressToTest << 1, halTrials, halTimeout)); -#else - return true; -#endif -} - -uint8_t scd40::readRegister(registers registerAddress) { - uint8_t result; -#ifndef generic - HAL_I2C_Mem_Read(&hi2c2, i2cAddress << 1, static_cast(registerAddress), I2C_MEMADD_SIZE_8BIT, &result, 1, halTimeout); -#else - result = mockSCD40Registers[static_cast(registerAddress)]; -#endif - return result; -} - -void scd40::writeRegister(registers registerAddress, uint8_t value) { -#ifndef generic - HAL_I2C_Mem_Write(&hi2c2, i2cAddress << 1, static_cast(registerAddress), I2C_MEMADD_SIZE_8BIT, &value, 1, halTimeout); -#else - mockSCD40Registers[static_cast(registerAddress)] = value; -#endif -} - -void scd40::readRegisters(uint16_t startAddress, uint16_t length, uint8_t* destination) { -#ifndef generic - HAL_I2C_Mem_Read(&hi2c2, i2cAddress << 1, startAddress, I2C_MEMADD_SIZE_8BIT, destination, length, halTimeout); -#else - memcpy(destination, mockSCD40Registers + startAddress, length); -#endif -} - -void scd40::log() { - if (channels[temperature].hasNewValue) { - logging::snprintf(logging::source::sensorData, "%s = %.2f *C\n", channels[temperature].name, channels[temperature].getOutput()); - } - if (channels[relativeHumidity].hasNewValue) { - logging::snprintf(logging::source::sensorData, "%s = %.0f %s\n", channels[relativeHumidity].name, channels[relativeHumidity].getOutput(), channels[relativeHumidity].unit); - } - if (channels[co2].hasNewValue) { - logging::snprintf(logging::source::sensorData, "%s = %.0f %s\n", channels[co2].name, channels[co2].getOutput(), channels[co2].unit); - } -} diff --git a/lib/sensors/scd40.hpp b/lib/sensors/scd40.hpp index 365fba6f..14463a12 100644 --- a/lib/sensors/scd40.hpp +++ b/lib/sensors/scd40.hpp @@ -14,66 +14,48 @@ class scd40 { scd40() = delete; static bool isPresent(); static void initialize(); - static sensorDeviceState getState() { return state; }; - static bool hasNewMeasurement(); - static float valueAsFloat(uint32_t channelIndex); - static const char* channelName(uint32_t channelIndex); - static const char* channelUnit(uint32_t channelIndex); - - static void tick(); static void run(); - static void log(); + static sensorDeviceState getState() { return state; }; static constexpr uint32_t nmbrChannels{3}; static constexpr uint32_t temperature{0}; static constexpr uint32_t relativeHumidity{1}; static constexpr uint32_t co2{2}; static sensorChannel channels[nmbrChannels]; - #ifndef unitTesting private: #endif static sensorDeviceState state; - static bool anyChannelNeedsSampling(); - static void adjustAllCounters(); static void startSampling(); static bool samplingIsReady(); static void readSample(); - static float calculateTemperature(); - static float calculateRelativeHumidity(); - static float calculateBarometricPressure(); + static float calculateTemperature(uint32_t rawData); + static float calculateRelativeHumidity(uint32_t rawData); + static float calculateCO2(uint32_t rawData); - static void clearNewMeasurements(); - - static constexpr uint8_t i2cAddress{0x44}; + static constexpr uint8_t i2cAddress{0x62}; static constexpr uint8_t halTrials{0x03}; // ST HAL requires a 'retry' parameters - static constexpr uint8_t halTimeout{0x10}; // ST HAL requires a 'timeout' in ms - - // Registers - enum class registers : uint8_t { - - }; + static constexpr uint8_t halTimeout{0x20}; // ST HAL requires a 'timeout' in ms // Commands - enum class commands : uint8_t { + enum class commands : uint16_t { + startPeriodicMeasurement = 0x21B1, + startLowPowerPeriodicMeasurement = 0x21AC, + getDataReadyStatus = 0xE4B8, + readMeasurement = 0xEC05, + stopPeriodicMeasurement = 0x3F86, }; - // Other - static constexpr uint8_t chipIdValue{0x61}; // value to expect at the chipIdregister, this allows to discover/recognize the BME68x - - static bool testI2cAddress(uint8_t addressToTest); // - static uint8_t readRegister(registers aRegister); // read a single register - static void readRegisters(uint16_t startAddress, uint16_t length, uint8_t* destination); // read a range of registers into a buffer - static void writeRegister(registers aRegister, const uint8_t value); // write a single register + static bool testI2cAddress(const uint8_t addressToTest); + static void writeCommand(const scd40::commands aCommand); + static void readData(uint16_t* data, const uint32_t dataLength); static uint32_t rawDataTemperature; static uint32_t rawDataRelativeHumidity; - static uint32_t rawCo2; - - static bool awake; + static uint32_t rawCo2; + friend class sensorDeviceCollection; }; - diff --git a/lib/sensors/sensordevicecollection.cpp b/lib/sensors/sensordevicecollection.cpp index 43289c1f..672c95f8 100644 --- a/lib/sensors/sensordevicecollection.cpp +++ b/lib/sensors/sensordevicecollection.cpp @@ -5,6 +5,7 @@ #include #include #include +#include // All known sensordevices' include files are to be added here // #include #include // only needed for logging.. can be removed later @@ -41,6 +42,12 @@ void sensorDeviceCollection::discover() { if (present[static_cast(sensorDeviceType::tsl2591)]) { tsl2591::initialize(); } + + present[static_cast(sensorDeviceType::scd40)] = scd40::isPresent(); + if (present[static_cast(sensorDeviceType::scd40)]) { + scd40::initialize(); + } + // Add more types of sensors here } @@ -67,6 +74,9 @@ void sensorDeviceCollection::startSampling() { case sensorDeviceType::tsl2591: tsl2591::startSampling(); break; + case sensorDeviceType::scd40: + scd40::startSampling(); + break; // Add more types of sensors here default: break; @@ -92,6 +102,9 @@ void sensorDeviceCollection::run() { case sensorDeviceType::tsl2591: tsl2591::run(); break; + case sensorDeviceType::scd40: + scd40::run(); + break; // Add more types of sensors here default: break; @@ -124,6 +137,10 @@ bool sensorDeviceCollection::isSamplingReady() { return false; } break; + case sensorDeviceType::scd40: + if (scd40::getState() != sensorDeviceState::sleeping) { + return false; + } // Add more types of sensors here default: break; @@ -313,7 +330,6 @@ void sensorDeviceCollection::collectNewMeasurements(uint32_t deviceIndex) { } } - uint32_t sensorDeviceCollection::nmbrOfChannels(uint32_t deviceIndex) { if (isValid(deviceIndex)) { switch (static_cast(deviceIndex)) { @@ -329,6 +345,9 @@ uint32_t sensorDeviceCollection::nmbrOfChannels(uint32_t deviceIndex) { case sensorDeviceType::tsl2591: return tsl2591::nmbrChannels; break; + case sensorDeviceType::scd40: + return scd40::nmbrChannels; + break; // Add more types of sensors here default: return 0; @@ -352,6 +371,9 @@ sensorChannel& sensorDeviceCollection::channel(uint32_t deviceIndex, uint32_t ch case sensorDeviceType::tsl2591: return tsl2591::channels[channelIndex]; break; + case sensorDeviceType::scd40: + return scd40::channels[channelIndex]; + break; // Add more types of sensors here default: return dummy; // in case of an invalid index, we return this dummy channel diff --git a/lib/sensors/sht40.cpp b/lib/sensors/sht40.cpp index 753e4a5c..a69d950f 100644 --- a/lib/sensors/sht40.cpp +++ b/lib/sensors/sht40.cpp @@ -122,7 +122,7 @@ void sht40::write(command aCommand) { #ifndef generic HAL_I2C_Master_Transmit(&hi2c2, static_cast(i2cAddress << 1), &pCommand, 1, halTimeout); #else -// TODO add mock for generic Unit testing + #endif } diff --git a/platformio.ini b/platformio.ini index 5b9b5ad4..73f8546f 100644 --- a/platformio.ini +++ b/platformio.ini @@ -218,10 +218,11 @@ test_filter = ;target_no_jigs/test_eeprom ;target_no_jigs/test_tsl2591 ;target_no_jigs/test_sht40 + target_no_jigs/test_scd40 ;target_no_jigs/test_eeprom_pagewrite ;tools/test_initialize_eeprom ;tools/test_initialize_mac - target_no_jigs/test_display + ;target_no_jigs/test_display ;target_no_jigs/test_screen ;target_no_jigs/test_rtc ;test_semihosting @@ -242,7 +243,7 @@ test_filter = ;target_no_jigs/test_HAL_timeout ;target_no_jigs/test_measurementgroupcollection -debug_test = target_no_jigs/test_HAL_timeout +debug_test = target_no_jigs/test_scd40 ; ############################################################################# diff --git a/test/generic/test_scd40/test.cpp b/test/generic/test_scd40/test.cpp index 47e8e146..d9bd2714 100644 --- a/test/generic/test_scd40/test.cpp +++ b/test/generic/test_scd40/test.cpp @@ -1,44 +1,34 @@ #include -//#include +#include #include -void setUp(void) { // before each test -} -void tearDown(void) { // after each test -} +void setUp(void) {} +void tearDown(void) {} -void test_isPresent() { -// TEST_ASSERT_TRUE(scd40::isPresent()); +void test_initialize() { + TEST_ASSERT_EQUAL(sensorDeviceState::unknown, scd40::getState()); + scd40::initialize(); + TEST_ASSERT_EQUAL(sensorDeviceState::sleeping, scd40::getState()); } -void test_signature() { - // TEST_ASSERT_EQUAL(sensorDeviceState::unknown, scd40::state); - // scd40::initialize(); - // TEST_ASSERT_EQUAL(sensorDeviceState::sleeping, scd40::state); +void test_calculateTemperature() { + TEST_ASSERT_FLOAT_WITHIN(0.1F, 25.0F, scd40::calculateTemperature(0x6667)); } -// void test_sample() { -// scd40::sample(); -// TEST_ASSERT_EQUAL_UINT32(0, scd40::rawDataTemperature); -// TEST_ASSERT_EQUAL_UINT32(0, scd40::rawDataRelativeHumidity); -// TEST_ASSERT_EQUAL_UINT32(0, scd40::rawDataBarometricPressure); -// } - -// void test_measurements() { -// scd40::sample(); -// TEST_IGNORE_MESSAGE("TODO: test calculating measurements from raw data"); -// TEST_ASSERT_EQUAL_FLOAT(0.0F, scd40::getTemperature()); -// TEST_ASSERT_EQUAL_FLOAT(0.0F, scd40::getRelativeHumidity()); -// TEST_ASSERT_EQUAL_FLOAT(0.0F, scd40::getBarometricPressure()); -// } +void test_calculateRelativeHumidity() { + TEST_ASSERT_FLOAT_WITHIN(0.1F, 37.0F, scd40::calculateRelativeHumidity(0x5eb9)); +} +void test_calculateCO2() { + TEST_ASSERT_FLOAT_WITHIN(0.1F, 500.0F, scd40::calculateCO2(0x01f4)); +} int main(int argc, char **argv) { UNITY_BEGIN(); - RUN_TEST(test_isPresent); - RUN_TEST(test_signature); - // RUN_TEST(test_sample); - // RUN_TEST(test_measurements); + RUN_TEST(test_initialize); + RUN_TEST(test_calculateTemperature); + RUN_TEST(test_calculateRelativeHumidity); + RUN_TEST(test_calculateCO2); UNITY_END(); } diff --git a/test/target_no_jigs/test_scd40/hwsetup.md b/test/target_no_jigs/test_scd40/hwsetup.md new file mode 100644 index 00000000..445c8da5 --- /dev/null +++ b/test/target_no_jigs/test_scd40/hwsetup.md @@ -0,0 +1,4 @@ +In order to run this target unit tests, you need to take following HW setup : +* testPort = UART1 = ST-Link + +Of course this test only works on a PCB with an SCD40 connected to the I2C port (QWIIC or GROVE) diff --git a/test/target_no_jigs/test_scd40/test.cpp b/test/target_no_jigs/test_scd40/test.cpp new file mode 100644 index 00000000..422de998 --- /dev/null +++ b/test/target_no_jigs/test_scd40/test.cpp @@ -0,0 +1,74 @@ +#include +#include "main.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +circularBuffer applicationEventBuffer; + +void setUp(void) {} +void tearDown(void) {} + +void test_isPresent() { + TEST_ASSERT_TRUE(scd40::isPresent()); +} + +void test_initialize() { + TEST_ASSERT_EQUAL(sensorDeviceState::unknown, scd40::getState()); + scd40::initialize(); + // HAL_Delay(10); + TEST_ASSERT_EQUAL(sensorDeviceState::sleeping, scd40::getState()); +} + +void test_samplingReady() { + if (scd40::samplingIsReady()) { + scd40::readSample(); + } + TEST_ASSERT_FALSE(scd40::samplingIsReady()); +} + +void test_run() { + scd40::startSampling(); + TEST_ASSERT_EQUAL(sensorDeviceState::sampling, scd40::getState()); + + while (true) { + if ((scd40::getState() == sensorDeviceState::sampling) && scd40::samplingIsReady()) { + scd40::readSample(); + break; + } + } + float scd40Temperature = scd40::calculateTemperature(scd40::rawDataTemperature); + float scd40RelativeHumidity = scd40::calculateRelativeHumidity(scd40::rawDataRelativeHumidity); + float scd40CO2 = scd40::calculateCO2(scd40::rawCo2); + + TEST_ASSERT_FLOAT_WITHIN(3.0F, 25.0F, scd40Temperature); + TEST_ASSERT_FLOAT_WITHIN(10.0F, 60.0F, scd40RelativeHumidity); + TEST_ASSERT_FLOAT_WITHIN(100.0F, 400.0F, scd40CO2); +} + +int main(int argc, char **argv) { + HAL_Init(); + SystemClock_Config(); + HAL_Delay(1000); + + gpio::initialize(); + uart1::initialize(); + i2c::wakeUp(); + + UNITY_BEGIN(); + RUN_TEST(test_isPresent); + RUN_TEST(test_initialize); + RUN_TEST(test_samplingReady); + for (uint32_t runIndex = 0; runIndex < 10; runIndex++) { + RUN_TEST(test_run); + HAL_Delay(100); + } + UNITY_END(); +} diff --git a/test/target_no_jigs/test_scd40/unity_config.cpp b/test/target_no_jigs/test_scd40/unity_config.cpp new file mode 100644 index 00000000..5115535a --- /dev/null +++ b/test/target_no_jigs/test_scd40/unity_config.cpp @@ -0,0 +1,27 @@ +#include "unity_config.h" +#include "main.h" + +#ifdef TARGET_TEST_PORT_UART1 +extern UART_HandleTypeDef huart1; +void MX_USART1_UART_Init(void); +void unityOutputStart() { + MX_USART1_UART_Init(); +} +void unityOutputChar(char c) { + HAL_UART_Transmit(&huart1, (uint8_t *)(&c), 1, 1000); +} +#endif +#ifdef TARGET_TEST_PORT_UART2 +extern UART_HandleTypeDef huart2; +void MX_USART2_UART_Init(void); +void unityOutputStart() { + MX_USART2_UART_Init(); +} +void unityOutputChar(char c) { + HAL_UART_Transmit(&huart2, (uint8_t *)(&c), 1, 1000); +} +#endif + +void unityOutputFlush() {} + +void unityOutputComplete() {} \ No newline at end of file diff --git a/test/target_no_jigs/test_scd40/unity_config.h b/test/target_no_jigs/test_scd40/unity_config.h new file mode 100644 index 00000000..ec16a945 --- /dev/null +++ b/test/target_no_jigs/test_scd40/unity_config.h @@ -0,0 +1,28 @@ +#pragma once + +#ifndef NULL +#ifndef __cplusplus +#define NULL (void*)0 +#else +#define NULL 0 +#endif +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +void unityOutputStart(); +void unityOutputChar(char); +void unityOutputFlush(); +void unityOutputComplete(); + +#define UNITY_OUTPUT_START() unityOutputStart() +#define UNITY_OUTPUT_CHAR(c) unityOutputChar(c) +#define UNITY_OUTPUT_FLUSH() unityOutputFlush() +#define UNITY_OUTPUT_COMPLETE() unityOutputComplete() + +#ifdef __cplusplus +} +#endif /* extern "C" */