Index: firmware/App/Controllers/ConductivitySensors.c =================================================================== diff -u -rd4f40c48a728c866c24bf44a59ff8ddd1e244ca1 -rffdb4e555df649ae73c30005e19b90c17c8aad2e --- firmware/App/Controllers/ConductivitySensors.c (.../ConductivitySensors.c) (revision d4f40c48a728c866c24bf44a59ff8ddd1e244ca1) +++ firmware/App/Controllers/ConductivitySensors.c (.../ConductivitySensors.c) (revision ffdb4e555df649ae73c30005e19b90c17c8aad2e) @@ -1,20 +1,21 @@ /************************************************************************** * -* Copyright (c) 2020-2023 Diality Inc. - All Rights Reserved. +* Copyright (c) 2020-2025 Diality Inc. - All Rights Reserved. * * THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN * WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. * * @file ConductivitySensors.c * * @author (last) Dara Navaei -* @date (last) 11-May-2023 +* @date (last) 28-May-2025 * * @author (original) Quang Nguyen * @date (original) 13-Jul-2020 * ***************************************************************************/ -#include // Used for calculating the polynomial calibration equation. +#include // Used for calculating the polynomial calibration equation. +#include // For memcpy #include "ConductivitySensors.h" #include "FPGA.h" @@ -23,6 +24,7 @@ #include "ModeFill.h" #include "OperationModes.h" #include "PersistentAlarm.h" +#include "Reservoirs.h" #include "SystemCommMessages.h" #include "TaskGeneral.h" #include "TaskPriority.h" @@ -43,15 +45,13 @@ #define COND_CPO_SENSOR_PROBE_TYPE 10 ///< 0.1 K cell constant conductivity probe. #define COND_SENSOR_DECIMAL_CONVERSION 100.0F ///< Conductivity value from FPGA has two decimal place. -#define COND_SENSOR_TEMPERATURE_COEF 0.02F ///< Linear temperature coefficient of variation at 25 Celcius for fresh water. #define COND_SENSOR_REFERENCE_TEMPERATURE 25.0F ///< Reference temperature for conductivity sensor. #define COND_SENSOR_REPORT_PERIOD ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) ///< Broadcast conductivity values message every second. #define MAX_COND_SENSOR_CPI_WARNING_HIGH_US_PER_CM 2000.0F ///< Maximum allowed high conductivity value in uS/cm. #define MIN_COND_SENSOR_CPI_WARNING_HIGH_US_PER_CM 1990.0F ///< Minimum allowed high conductivity value in uS/cm. -#define MAX_COND_SENSOR_CPI_WARNING_LOW_US_PER_CM 200.0F ///< Maximum allowed low conductivity value in uS/cm. -#define MIN_COND_SENSOR_CPI_WARNING_LOW_US_PER_CM 220.0F ///< Minimum allowed low conductivity value in uS/cm. +#define MIN_CPI_INLET_ALARM_RECOVERY_OFFSET_US_PER_CM 10.0F ///< Minimum inlet water conductivity recovery offset value in uS/cm. #define MAX_RO_ONLY_COND_SENSOR_CPI_HIGH_US_PER_CM 100.0F ///< Maximum RO only mode high conductivity value in uS/cm. #define MIN_RO_ONLY_COND_SENSOR_CPI_HIGH_US_PER_CM 90.0F ///< Minimum RO only mode high conductivity value in uS/cm. @@ -85,6 +85,8 @@ #define COND_SENSORS_FPGA_ERROR_TIMEOUT_MS ( 2 * MS_PER_SECOND ) ///< Conductivity sensors FPGA error timeout in milliseconds. #define COND_SENSORS_BAD_CHAR_TIME_OUT_MS ( 2 * MS_PER_SECOND ) ///< Conductivity sensor bad received character timeout in milliseconds. +#define MIN_CPI_CPO_COND_VALUES_AFTER_CALS_US_PER_CM 20.0F ///< Minimum allowed CPi and CPo conductivity values after calculations in uS/cm. + #pragma pack(push,1) /// Emstat pico measurement data package structure typedef struct @@ -144,11 +146,13 @@ static EMSTAT_READ_T emstatBoardRead[ NUM_OF_EMSTAT_BOARDS ]; ///< EMSTAT board read. static COND_SENSOR_STATUS_T condSensorStatus[ NUM_OF_CONDUCTIVITY_SENSORS ]; ///< Conductivity sensors status. static DG_COND_SENSORS_CAL_RECORD_T condSensorsCalRecord; ///< Conductivity sensors' calibration record. +static DG_COND_SENSORS_TEMP_COMP_CAL_RECORD_T condSensorsTempCompCalRecord; ///< Conductivity sensors' temperature compensation calibration record. static CAL_DATA_DG_COND_SENSORS_T condSensorCalTable[ NUM_OF_CONDUCTIVITY_SENSORS ]; ///< Conductivity sensors calibration table. +static F32 minInletWaterCondAlarmLimitUSPCM; ///< Min inlet water conductivity alarm limit in uS/cm. // ********** private function prototypes ********** -static F32 calcCompensatedConductivity( F32 conductivity, F32 temperature ); +static F32 calcCompensatedConductivity( U32 sensorID, F32 conductivity, F32 temperature ); static void calcRORejectionRatio( void ); static void processCPiCPoSensorRead( U32 sensorId, U08 emstatBoardSensorIndex, U32 fgpaRead, U08 fpgaReadCount, U08 fpgaErrorCount, U08 fpgaSensorFault ); @@ -168,8 +172,9 @@ void initConductivitySensors( void ) { U08 i; - roRejectionRatio = 0.0F; - condDataPublishCounter = DATA_PUBLISH_COUNTER_START_COUNT; + roRejectionRatio = 0.0F; + condDataPublishCounter = DATA_PUBLISH_COUNTER_START_COUNT; + minInletWaterCondAlarmLimitUSPCM = 0.0F; for ( i = 0; i < NUM_OF_CONDUCTIVITY_SENSORS; i++ ) { @@ -234,8 +239,8 @@ initTimeWindowedCount( TIME_WINDOWED_COUNT_FPGA_CONDUCTIVITY_SENSOR_ERROR, MAX_CONDUCTIVITY_SENSOR_FAILURES, MAX_CONDUCTIVITY_SENSOR_FAILURE_WINDOW_MS ); initPersistentAlarm( ALARM_ID_DG_INLET_WATER_CONDUCTIVITY_IN_HIGH_RANGE, INLET_WATER_COND_SENSOR_OUT_OF_RANGE_CLEAR_MS, INLET_WATER_COND_SENSOR_OUT_OF_RANGE_TIMEOUT_MS ); initPersistentAlarm( ALARM_ID_DG_INLET_WATER_CONDUCTIVITY_IN_LOW_RANGE, INLET_WATER_COND_SENSOR_OUT_OF_RANGE_CLEAR_MS, INLET_WATER_COND_SENSOR_OUT_OF_RANGE_TIMEOUT_MS ); - initPersistentAlarm( ALARM_ID_DG_CLEANING_MODE_INLET_WATER_COND_TOO_HIGH, INLET_WATER_COND_SENSOR_OUT_OF_RANGE_TIMEOUT_MS, INLET_WATER_COND_SENSOR_OUT_OF_RANGE_TIMEOUT_MS ); - initPersistentAlarm( ALARM_ID_DG_CLEANING_MODE_INLET_WATER_COND_TOO_LOW, INLET_WATER_COND_SENSOR_OUT_OF_RANGE_TIMEOUT_MS, INLET_WATER_COND_SENSOR_OUT_OF_RANGE_TIMEOUT_MS ); + initPersistentAlarm( ALARM_ID_DG_CLEANING_MODE_INLET_WATER_COND_TOO_HIGH, 0, INLET_WATER_COND_SENSOR_OUT_OF_RANGE_TIMEOUT_MS ); + initPersistentAlarm( ALARM_ID_DG_CLEANING_MODE_INLET_WATER_COND_TOO_LOW, 0, INLET_WATER_COND_SENSOR_OUT_OF_RANGE_TIMEOUT_MS ); // Initialize the conductivity sensors' FPGA alarms initFPGAPersistentAlarm( FPGA_PERS_ERROR_CPI_CPO_COND_SENSORS, ALARM_ID_DG_CPI_CPO_SENSORS_FPGA_FAULT, COND_SENSORS_FPGA_ERROR_TIMEOUT_MS, COND_SENSORS_FPGA_ERROR_TIMEOUT_MS ); @@ -258,6 +263,9 @@ { getNVRecord2Driver( GET_CAL_CONDUCTIVITY_SENSORS, (U08*)&condSensorsCalRecord, sizeof( condSensorsCalRecord ), NUM_OF_CAL_DATA_COND_SENSORS, ALARM_ID_DG_COND_SENSORS_INVALID_CAL_RECORD ); + + getNVRecord2Driver( GET_CAL_CONDUCTIVITY_SENSORS_TEMP_COMP, (U08*)&condSensorsTempCompCalRecord, sizeof( DG_COND_SENSORS_TEMP_COMP_CAL_RECORD_T ), + NUM_OF_CAL_DATA_COND_SENSORS_TEMP_COMP, ALARM_ID_DG_COND_SENSORS_INVALID_TEMP_COMP_CAL_RECORD ); } #ifndef _RELEASE_ @@ -306,10 +314,16 @@ *************************************************************************/ SELF_TEST_STATUS_T execConductivitySensorsSelfTest( void ) { - BOOL calStatus = getNVRecord2Driver( GET_CAL_CONDUCTIVITY_SENSORS, (U08*)&condSensorsCalRecord, sizeof( condSensorsCalRecord ), - NUM_OF_CAL_DATA_COND_SENSORS, ALARM_ID_DG_COND_SENSORS_INVALID_CAL_RECORD ); - SELF_TEST_STATUS_T result = ( TRUE == calStatus ? SELF_TEST_STATUS_PASSED : SELF_TEST_STATUS_FAILED ); + SELF_TEST_STATUS_T result = SELF_TEST_STATUS_IN_PROGRESS; + BOOL calStatus = FALSE; + calStatus |= getNVRecord2Driver( GET_CAL_CONDUCTIVITY_SENSORS, (U08*)&condSensorsCalRecord, sizeof( DG_COND_SENSORS_CAL_RECORD_T ), + NUM_OF_CAL_DATA_COND_SENSORS, ALARM_ID_DG_COND_SENSORS_INVALID_CAL_RECORD ); + calStatus |= getNVRecord2Driver( GET_CAL_CONDUCTIVITY_SENSORS_TEMP_COMP, (U08*)&condSensorsTempCompCalRecord, sizeof( DG_COND_SENSORS_TEMP_COMP_CAL_RECORD_T ), + NUM_OF_CAL_DATA_COND_SENSORS_TEMP_COMP, ALARM_ID_DG_COND_SENSORS_INVALID_TEMP_COMP_CAL_RECORD ); + + result = ( TRUE == calStatus ? SELF_TEST_STATUS_PASSED : SELF_TEST_STATUS_FAILED ); + return result; } @@ -323,75 +337,88 @@ *************************************************************************/ void checkInletWaterConductivity( void ) { + F32 conductivity = getConductivityValue( CONDUCTIVITYSENSORS_CPI_SENSOR ); + HD_MODE_SUB_MODE_T opMode; + getHDOperationMode( &opMode ); + + + if ( VALVE_STATE_OPEN == getValveStateName( VPI ) && ( opMode.hdMode <= MODE_TREA ) ) + { #ifndef _RELEASE_ - if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_WATER_QUALITY_CHECK ) != SW_CONFIG_ENABLE_VALUE ) + if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_WATER_QUALITY_CHECK ) != SW_CONFIG_ENABLE_VALUE ) #endif - { - DG_OP_MODE_T opMode = getCurrentOperationMode(); - F32 conductivity = getConductivityValue( CONDUCTIVITYSENSORS_CPI_SENSOR ); - BOOL isConductTooLow = FALSE; - BOOL isConductTooHigh = FALSE; - - if ( FALSE == isROOnlyModeEnabled() ) { - isConductTooLow = ( conductivity < MAX_COND_SENSOR_CPI_WARNING_LOW_US_PER_CM ? TRUE : FALSE ); - isConductTooHigh = ( conductivity > MAX_COND_SENSOR_CPI_WARNING_HIGH_US_PER_CM ? TRUE : FALSE ); - } - else - { - isConductTooLow = FALSE; - isConductTooHigh = ( conductivity > MAX_RO_ONLY_COND_SENSOR_CPI_HIGH_US_PER_CM ? TRUE : FALSE ); - } + DG_OP_MODE_T opMode = getCurrentOperationMode(); + BOOL isConductTooLow = FALSE; + BOOL isConductTooHigh = FALSE; - switch( opMode ) - { - case DG_MODE_GENE: - case DG_MODE_FILL: - case DG_MODE_DRAI: - case DG_MODE_STAN: - if ( TRUE == isAlarmActive( ALARM_ID_DG_INLET_WATER_CONDUCTIVITY_IN_LOW_RANGE ) ) - { - isConductTooLow = ( conductivity >= MIN_COND_SENSOR_CPI_WARNING_LOW_US_PER_CM ? FALSE : TRUE ); - } + if ( FALSE == isROOnlyModeEnabled() ) + { + isConductTooLow = ( conductivity < minInletWaterCondAlarmLimitUSPCM ? TRUE : FALSE ); + isConductTooHigh = ( conductivity > MAX_COND_SENSOR_CPI_WARNING_HIGH_US_PER_CM ? TRUE : FALSE ); + } + else + { + isConductTooLow = FALSE; + isConductTooHigh = ( conductivity > MAX_RO_ONLY_COND_SENSOR_CPI_HIGH_US_PER_CM ? TRUE : FALSE ); + } - // Per PRS 403 - checkPersistentAlarm( ALARM_ID_DG_INLET_WATER_CONDUCTIVITY_IN_LOW_RANGE, isConductTooLow, conductivity, MAX_COND_SENSOR_CPI_WARNING_LOW_US_PER_CM ); - - if ( TRUE == isAlarmActive( ALARM_ID_DG_INLET_WATER_CONDUCTIVITY_IN_HIGH_RANGE ) ) - { - if ( FALSE == isROOnlyModeEnabled() ) + switch( opMode ) + { + case DG_MODE_GENE: + case DG_MODE_FILL: + case DG_MODE_DRAI: + case DG_MODE_STAN: + if ( TRUE == isAlarmActive( ALARM_ID_DG_INLET_WATER_CONDUCTIVITY_IN_LOW_RANGE ) ) { - isConductTooHigh = ( conductivity <= MIN_COND_SENSOR_CPI_WARNING_HIGH_US_PER_CM ? FALSE : TRUE ); + isConductTooLow = ( conductivity >= ( minInletWaterCondAlarmLimitUSPCM + MIN_CPI_INLET_ALARM_RECOVERY_OFFSET_US_PER_CM ) ? FALSE : TRUE ); } - else + + // Per PRS 403 + checkPersistentAlarm( ALARM_ID_DG_INLET_WATER_CONDUCTIVITY_IN_LOW_RANGE, isConductTooLow, conductivity, minInletWaterCondAlarmLimitUSPCM ); + + if ( TRUE == isAlarmActive( ALARM_ID_DG_INLET_WATER_CONDUCTIVITY_IN_HIGH_RANGE ) ) { - isConductTooHigh = ( conductivity <= MIN_RO_ONLY_COND_SENSOR_CPI_HIGH_US_PER_CM ? FALSE : TRUE ); + if ( FALSE == isROOnlyModeEnabled() ) + { + isConductTooHigh = ( conductivity <= MIN_COND_SENSOR_CPI_WARNING_HIGH_US_PER_CM ? FALSE : TRUE ); + } + else + { + isConductTooHigh = ( conductivity <= MIN_RO_ONLY_COND_SENSOR_CPI_HIGH_US_PER_CM ? FALSE : TRUE ); + } } - } - // Per PRS 404 - checkPersistentAlarm( ALARM_ID_DG_INLET_WATER_CONDUCTIVITY_IN_HIGH_RANGE, isConductTooHigh, conductivity, MAX_COND_SENSOR_CPI_WARNING_HIGH_US_PER_CM ); - break; + // Per PRS 404 + checkPersistentAlarm( ALARM_ID_DG_INLET_WATER_CONDUCTIVITY_IN_HIGH_RANGE, isConductTooHigh, conductivity, MAX_COND_SENSOR_CPI_WARNING_HIGH_US_PER_CM ); + break; - case DG_MODE_FLUS: - case DG_MODE_HEAT: - case DG_MODE_HCOL: - case DG_MODE_CHEM: - case DG_MODE_CHFL: - if ( VALVE_STATE_OPEN == getValveStateName( VPI ) ) - { + case DG_MODE_FLUS: + case DG_MODE_HEAT: + case DG_MODE_HCOL: + case DG_MODE_CHEM: + case DG_MODE_CHFL: + case DG_MODE_ROPS: // Per PRS 403 checkPersistentAlarm( ALARM_ID_DG_CLEANING_MODE_INLET_WATER_COND_TOO_HIGH, isConductTooHigh, conductivity, MAX_COND_SENSOR_CPI_WARNING_HIGH_US_PER_CM ); // Per PRS 404 - checkPersistentAlarm( ALARM_ID_DG_CLEANING_MODE_INLET_WATER_COND_TOO_LOW, isConductTooLow, conductivity, MAX_COND_SENSOR_CPI_WARNING_LOW_US_PER_CM ); - } - break; + checkPersistentAlarm( ALARM_ID_DG_CLEANING_MODE_INLET_WATER_COND_TOO_LOW, isConductTooLow, conductivity, minInletWaterCondAlarmLimitUSPCM ); + break; - default: - // NOTE: Do nothing for the rest of the modes - break; + default: + // NOTE: Do nothing for the rest of the modes + break; + } } } + else + { + // VPI is closed - clear all alarms + checkPersistentAlarm( ALARM_ID_DG_INLET_WATER_CONDUCTIVITY_IN_LOW_RANGE, FALSE, conductivity, minInletWaterCondAlarmLimitUSPCM ); + checkPersistentAlarm( ALARM_ID_DG_INLET_WATER_CONDUCTIVITY_IN_HIGH_RANGE, FALSE, conductivity, MAX_COND_SENSOR_CPI_WARNING_HIGH_US_PER_CM ); + checkPersistentAlarm( ALARM_ID_DG_CLEANING_MODE_INLET_WATER_COND_TOO_HIGH, FALSE, conductivity, MAX_COND_SENSOR_CPI_WARNING_HIGH_US_PER_CM ); + checkPersistentAlarm( ALARM_ID_DG_CLEANING_MODE_INLET_WATER_COND_TOO_LOW, FALSE, conductivity, minInletWaterCondAlarmLimitUSPCM ); + } } /*********************************************************************//** @@ -426,6 +453,21 @@ /*********************************************************************//** * @brief + * The setMinInletWaterConductivityAlarmLimitUSPCM function sets the min + * inlet water conductivity alarm limit in uS/cm. + * @details Inputs: none + * @details Outputs: minInletWaterConductivityAlarmLimitUSPCM + * @param conductivity value + * @return none + *************************************************************************/ +void setMinInletWaterConductivityAlarmLimitUSPCM( F32 valueUSPM ) +{ + minInletWaterCondAlarmLimitUSPCM = valueUSPM; + SEND_EVENT_WITH_2_F32_DATA( DG_EVENT_MIN_INLET_WATER_COND_ALARM_FROM_HD_INSTIT_RECORD, minInletWaterCondAlarmLimitUSPCM, 0.0F ) +} + +/*********************************************************************//** + * @brief * The getConductivityValue function gets the compensated conductivity * value for a given conductivity sensor id. * @details Inputs: compensatedConductivityValues[] @@ -459,14 +501,19 @@ * reference temperature of 25 degree Celsius. * @details Inputs: conductivity, temperature * @details Outputs: none + * @param sensorID the ID of the conductivity sensor * @param conductivity conductivity value * @param temperature temperature to compensate conductivity with * @return compensated conductivity based on temperature *************************************************************************/ -static F32 calcCompensatedConductivity( F32 conductivity, F32 temperature ) +static F32 calcCompensatedConductivity( U32 sensorID, F32 conductivity, F32 temperature ) { - // EC = EC_25 * (1 + temp_coef * ( temperature - 25 )) - F32 compensatedCoef = ( 1.0F + ( COND_SENSOR_TEMPERATURE_COEF * ( temperature - COND_SENSOR_REFERENCE_TEMPERATURE ) ) ); + F32 gain = condSensorsTempCompCalRecord.condSensorsTempComp[ (CAL_DATA_DG_COND_SENSORS_TEMP_COMP_T)sensorID ].gain; + F32 coeff = condSensorsTempCompCalRecord.condSensorsTempComp[ (CAL_DATA_DG_COND_SENSORS_TEMP_COMP_T)sensorID ].coefficient; + F32 offset = condSensorsTempCompCalRecord.condSensorsTempComp[ (CAL_DATA_DG_COND_SENSORS_TEMP_COMP_T)sensorID ].offset; + F32 compensation = ( gain * coeff ) + offset; + // EC = EC_25 * (1 + compensation * ( temperature - 25 )) + F32 compensatedCoef = ( 1.0F + ( compensation * ( temperature - COND_SENSOR_REFERENCE_TEMPERATURE ) ) ); return conductivity / compensatedCoef; } @@ -517,7 +564,7 @@ // EMSTAT sensors will be the permanent sensors from DVT onward. F32 temperature = getTemperatureValue( emstatBoardRead[ EMSTAT_CPI_CPO_BOARD ].sensors[ emstatBoardSensorIndex ].condSnsrTempSnsr ); F32 conductivity = ( (F32)( fgpaRead ) / COND_SENSOR_DECIMAL_CONVERSION ); - F32 compensatedCond = calcCompensatedConductivity( conductivity, temperature ); + F32 compensatedCond = calcCompensatedConductivity( sensorId, conductivity, temperature ); condSensorStatus[ sensorId ].readCount = fpgaReadCount; condSensorStatus[ sensorId ].internalErrorCount = 0; @@ -702,10 +749,20 @@ F32 resistance = ( ( F32 )( condSensorStatus[ sensorId ].rawEmstatCondValue - EMSTAT_PICO_MEASUREMENT_OFFSET ) / prefix ); F32 temperature = getTemperatureValue( readPackage->sensors[ boardSensorIndex ].condSnsrTempSnsr ); F32 conductivity = ( 1.0F / resistance ) * SIEMENS_TO_MICROSIEMENS_CONVERSION; - F32 compensatedCond = calcCompensatedConductivity( conductivity, temperature ); + F32 compensatedCond = calcCompensatedConductivity( sensorId, conductivity, temperature ); + F32 calAppliedCond = getCalibrationAppliedConductivityValue( sensorId, compensatedCond ); + if ( ( CONDUCTIVITYSENSORS_CPI_SENSOR == sensorId ) || ( CONDUCTIVITYSENSORS_CPO_SENSOR == sensorId ) ) + { + // If the CPi or CPo sensors values < 20 uS/cm after compensation and calibration, they are set to 20 uS/cm. + if ( calAppliedCond < MIN_CPI_CPO_COND_VALUES_AFTER_CALS_US_PER_CM ) + { + calAppliedCond = MIN_CPI_CPO_COND_VALUES_AFTER_CALS_US_PER_CM; + } + } + condSensorStatus[ sensorId ].internalErrorCount = 0; - condSensorStatus[ sensorId ].compensatedCondValue.data = getCalibrationAppliedConductivityValue( sensorId, compensatedCond ); + condSensorStatus[ sensorId ].compensatedCondValue.data = calAppliedCond; condSensorStatus[ sensorId ].rawCondValue = conductivity; }