Index: firmware/App/Modes/ModeHeatDisinfect.c =================================================================== diff -u -r81769c6bea6ed32d70698fb03ad5823de1814b27 -r5c430c3ac17fc8ad836fd70b8a3b8a12af44319e --- firmware/App/Modes/ModeHeatDisinfect.c (.../ModeHeatDisinfect.c) (revision 81769c6bea6ed32d70698fb03ad5823de1814b27) +++ firmware/App/Modes/ModeHeatDisinfect.c (.../ModeHeatDisinfect.c) (revision 5c430c3ac17fc8ad836fd70b8a3b8a12af44319e) @@ -1,6 +1,6 @@ /************************************************************************** * -* Copyright (c) 2020-2022 Diality Inc. - All Rights Reserved. +* Copyright (c) 2020-2023 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. @@ -58,8 +58,6 @@ // Flush drain path state defines #define FLUSH_DRAIN_WAIT_TIME_MS ( 2 * SEC_PER_MIN * MS_PER_SECOND ) ///< Flush Drain path wait time in milliseconds. -#define MIN_INLET_TEMPERATURE_C 24.0F ///< Minimum water inlet temperature in C. -#define MAX_INLET_CONDUCTIVITY_US_PER_CM 2000.0F ///< Maximum water inlet conductivity in us/cm // Flush circulation path state defines #define RO_PUMP_TARGET_FLUSH_FILL_FLOW_RATE_LPM 0.8F ///< RO pump target flow rate during flush/fill in L/min. @@ -82,21 +80,21 @@ // Fill and heat water #define HEAT_DISINFECT_TARGET_TEMPERATURE_C 82.0F ///< Heat disinfect target water temperature in C. -#define HEAT_DISINFECT_START_TEMPERATURE_C 81.00F ///< Heat disinfect minimum acceptable temperature in C. +#define HEAT_DISINFECT_START_TEMPERATURE_C 81.0F ///< Heat disinfect minimum acceptable temperature in C. // R1 to R2 & R2 to R1 heat disinfect circulation #define HEAT_DISINFECT_TARGET_RO_FLOW_LPM 1.3F ///< Heat disinfect target RO flow rate in L/min. #define HEAT_DISINFECT_TARGET_RO_FLOW_TRANSFER_LPM 0.8F ///< Heat disinfect target RO flow rate in L/min when transferring between reservoirs. #define HEAT_DISINFECT_MAX_RO_PRESSURE_PSI 30 ///< Heat disinfect maximum RO pressure in psi. -#define HEAT_DISINFECT_TARGET_DRAIN_PRES_PSI 12.0F ///< Heat disinfect target drain outlet pressure in psi. -#define HEAT_DISINFECT_TARGET_DRAIN_FILL_R2_PSI 9.0F ///< Heat disinfect target drain R2 fill outplet pressure in PSI #define HEAT_DISINFECT_TIME_MS ( 10 * SEC_PER_MIN * MS_PER_SECOND ) ///< Heat disinfect time for each section in milliseconds. #define HEAT_DISINFECT_START_TEMP_TIMOUT_MS ( 4 * MIN_PER_HOUR * SEC_PER_MIN * MS_PER_SECOND ) ///< Heat disinfect reaching to minimum temperature timeout in milliseconds. #define RSRVRS_TARGET_VOL_OUT_TIMEOUT_MS ( 0.5F * SEC_PER_MIN * MS_PER_SECOND ) ///< Reservoirs 1 & 2 maximum volume out of range timeout during heat disinfect. TODO change this to 5 seconds #define RSRVRS_MAX_TARGET_VOL_CHANGE_ML 250.0F // TODO temporary change. Change back to 100 ///< Reservoirs 1 & 2 maximum allowed volume change when full during heat disinfect. #define POST_HEAT_DISINFECT_WAIT_TIME_MS ( 3 * SEC_PER_MIN * MS_PER_SECOND ) ///< Heat disinfect final wait time before flushing the system in milliseconds. #define HEAT_DISINFECT_MAX_TEMP_GRADIENT_C 15.0F ///< Heat disinfect maximum allowed temperature gradient in between hottest and coldest sensors. #define HEAT_DISINFECT_TEMP_GRAD_OUT_RANGE_TIME_MS ( 0.16 * SEC_PER_MIN * MS_PER_SECOND ) ///< Heat disinfect temperature gradient out of range timeout in milliseconds. +#define HEAT_DISINFECT_TARGET_RO_PUMP_DC 0.4F ///< Heat disinfect target RO pump duty cycle. +#define HEAT_DISINFECT_REF_RSRVR_TIMEOUT_MS ( 5 * MS_PER_SECOND ) ///< Heat disinfect getting reference reservoirs value timeout in milliseconds. // Mix drain R1 and R2 #define RSRVRS_MIX_DRAIN_TIMEOUT_MS ( 5 * SEC_PER_MIN * MS_PER_SECOND ) ///< Reservoirs 1 & 2 mix drain timeout in ms. @@ -145,8 +143,8 @@ static U32 concentratePumpsPrimeTimer; ///< Concentrate pumps prime timer. static DG_RESERVOIR_STATUS_T rsrvr1Status; ///< Reservoir 1 status. static DG_RESERVOIR_STATUS_T rsrvr2Status; ///< Reservoir 2 status. -static F32 R1HeatDisinfectVol; ///< Reservoir 1 full volume during heat disinfect. -static F32 R2HeatDisinfectVol; ///< Reservoir 2 full volume during heat disinfect. +static F32 rsrvr1RefVolML; ///< Reservoir 1 reference volume in heat disinfect in milliliters. +static F32 rsrvr2RefVolML; ///< Reservoir 2 reference volume in heat disinfect in milliliters. static U32 heatDisinfectTimer; ///< Heat disinfect timer. static BOOL isPartialDisinfectInProgress; ///< Heat disinfect partial complete/in progess flag. static U32 rsrvrsVolMonitorTimer; ///< Reservoir 1 & 2 volume monitor timers during heat disinfect. @@ -198,7 +196,7 @@ * @details Inputs: none * @details Outputs: heatDisinfectState, stateTimer, * stateTrialCounter, areTempSensorsInRange, rsrvr1Status, rsrvr2Status, - * R1HeatDisinfectVol, R2HeatDisinfectVol, overallHeatDisinfectTimer, + * rsrvr1RefVolML, rsrvr2RefVolML, overallHeatDisinfectTimer, * cancellationMode, rsrvrFillStableTimeCounter, prevHeatDisinfectState * isPartialDisinfectInProgress, isDrainPumpOnInMixDrain, heatDisinfectTimer * hasROFCirculationBeenStarted, ROFCirculationTimer, targetDisinfectTime @@ -217,8 +215,8 @@ areTempSensorsInRange = FALSE; rsrvr1Status = NUM_OF_DG_RESERVOIR_STATUS; rsrvr2Status = NUM_OF_DG_RESERVOIR_STATUS; - R1HeatDisinfectVol = 0.0F; - R2HeatDisinfectVol = 0.0F; + rsrvr1RefVolML = 0.0F; + rsrvr2RefVolML = 0.0F; overallHeatDisinfectTimer = 0; cancellationMode = CANCELLATION_MODE_NONE; rsrvrFillStableTimeCounter = 0; @@ -608,7 +606,7 @@ // State failed. Cancel heat disinfect mode else { - alarmDetectedPendingTrigger = ALARM_ID_DG_TEMP_SENSORS_DIFF_OUT_OF_RANGE; + alarmDetectedPendingTrigger = ALARM_ID_DG_CLEANING_MODE_TEMP_SENSORS_DIFF_OUT_OF_RANGE; prevHeatDisinfectState = state; state = DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH; } @@ -930,7 +928,6 @@ { DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_FILL_WITH_WATER; - // Set the heat disinfect UI state heatDisinfectUIState = HEAT_DISINFECT_UI_STATE_HEAT_UP_WATER; // First reservoir 1 must be full @@ -953,37 +950,28 @@ setValveState( VPD, VALVE_STATE_DRAIN_C_TO_NO ); setValveState( VDR, VALVE_STATE_RECIRC_C_TO_NC ); setValveState( VRC, VALVE_STATE_RECIRC_C_TO_NC ); - + signalROPumpHardStop(); // Set the drain pump to control mode - setDrainPumpTargetOutletPressure( HEAT_DISINFECT_TARGET_DRAIN_PRES_PSI ); + setDrainPumpTargetOutletFlowLPM( HEAT_DISINFECT_TARGET_RO_FLOW_LPM ); - // Set the RO flow to maximum pressure of 30psi since it is the maximum pressure on the RO filter - // at inlet temperature > 45 C - setROPumpTargetFlowRateLPM( HEAT_DISINFECT_TARGET_RO_FLOW_LPM, HEAT_DISINFECT_MAX_RO_PRESSURE_PSI ); - // Start the trimmer heater since we are recirculating water and there is flow in the shunt line setHeaterTargetTemperature( DG_TRIMMER_HEATER, HEAT_DISINFECT_TARGET_TEMPERATURE_C ); startHeater( DG_TRIMMER_HEATER ); - // Get the current volumes of R1 & R2. These values will be used to make sure the reservoirs' - // volume does not change more than a certain amount during the actual heat disinfect cycle - R1HeatDisinfectVol = getLoadCellLargeFilteredWeight( LOAD_CELL_RESERVOIR_1_PRIMARY ); - R2HeatDisinfectVol = getLoadCellLargeFilteredWeight( LOAD_CELL_RESERVOIR_2_PRIMARY ); - - stateTimer = getMSTimerCount(); + stateTimer = getMSTimerCount(); rsrvrsVolMonitorTimer = getMSTimerCount(); - state = DG_HEAT_DISINFECT_STATE_DISINFECT_R1_TO_R2; + state = DG_HEAT_DISINFECT_STATE_DISINFECT_R1_TO_R2; } else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr2Status ) { prevHeatDisinfectState = state; - state = DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH; + state = DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH; } } else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr1Status ) { prevHeatDisinfectState = state; - state = DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH; + state = DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH; } return state; @@ -1006,6 +994,22 @@ DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_DISINFECT_R1_TO_R2; HEAT_DISINFECT_STATUS_T status = getHeatDisinfectStatus(); + if ( FALSE == isROPumpRunning() ) + { + setROPumpTargetDutyCycle( HEAT_DISINFECT_TARGET_RO_PUMP_DC ); + } + + if ( TRUE == didTimeout( stateTimer, HEAT_DISINFECT_REF_RSRVR_TIMEOUT_MS ) ) + { + if ( ( rsrvr1RefVolML < NEARLY_ZERO ) && ( rsrvr2RefVolML < NEARLY_ZERO ) ) + { + // Get the current volumes of R1 & R2. These values will be used to make sure the reservoirs' + // volume does not change more than a certain amount during the actual heat disinfect cycle + rsrvr1RefVolML = getLoadCellLargeFilteredWeight( LOAD_CELL_RESERVOIR_1_PRIMARY ); + rsrvr2RefVolML = getLoadCellLargeFilteredWeight( LOAD_CELL_RESERVOIR_2_PRIMARY ); + } + } + switch ( status ) { case HEAT_DISINFECT_RSRVRS_LEAK_TIMEOUT: @@ -1028,12 +1032,8 @@ setValveState( VRI, VALVE_STATE_R1_C_TO_NO ); setValveState( VRF, VALVE_STATE_R2_C_TO_NO ); - // Set the RO flow to maximum pressure of 30psi since it is the maximum pressure on the RO filter - // at inlet temperature > 45 C - setROPumpTargetFlowRateLPM( HEAT_DISINFECT_TARGET_RO_FLOW_TRANSFER_LPM, HEAT_DISINFECT_MAX_RO_PRESSURE_PSI ); - // Set the drain pump to control mode - setDrainPumpTargetOutletPressure( HEAT_DISINFECT_TARGET_DRAIN_FILL_R2_PSI ); + setDrainPumpTargetOutletFlowLPM( HEAT_DISINFECT_TARGET_RO_FLOW_TRANSFER_LPM ); // Turn off trimmer heater for transition stopHeater( DG_TRIMMER_HEATER ); @@ -1043,6 +1043,8 @@ rsrvr2Status = DG_RESERVOIR_BELOW_TARGET; rsrvr1Status = DG_RESERVOIR_BELOW_TARGET; stateTimer = getMSTimerCount(); + rsrvr1RefVolML = 0.0F; + rsrvr2RefVolML = 0.0F; // Reset the timer for the next disinfect state tempGradOutOfRangeTimer = 0; state = DG_HEAT_DISINFECT_STATE_FILL_R2_WITH_HOT_WATER; @@ -1074,7 +1076,6 @@ { DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_FILL_R2_WITH_HOT_WATER; - // Set the heat disinfect UI state heatDisinfectUIState = HEAT_DISINFECT_UI_STATE_TRANSITION_HOT_WATER; // First reservoir 1 must be partially full @@ -1088,33 +1089,26 @@ if ( DG_RESERVOIR_REACHED_TARGET == rsrvr2Status ) { - // Get the current volumes to be monitored during R2 to R1 heat disinfect state - R1HeatDisinfectVol = getLoadCellLargeFilteredWeight( LOAD_CELL_RESERVOIR_1_PRIMARY ); - R2HeatDisinfectVol = getLoadCellLargeFilteredWeight( LOAD_CELL_RESERVOIR_2_PRIMARY ); - // Set the drain pump to control mode - setDrainPumpTargetOutletPressure( HEAT_DISINFECT_TARGET_DRAIN_PRES_PSI ); + setDrainPumpTargetOutletFlowLPM( HEAT_DISINFECT_TARGET_RO_FLOW_LPM ); - // Set the RO flow to maximum pressure of 30psi since it is the maximum pressure on the RO filter - // at inlet temperature > 45 C - setROPumpTargetFlowRateLPM( HEAT_DISINFECT_TARGET_RO_FLOW_LPM, HEAT_DISINFECT_MAX_RO_PRESSURE_PSI ); - // Start the trimmer heater since we are recirculating water and there is flow in the shunt line setHeaterTargetTemperature( DG_TRIMMER_HEATER, HEAT_DISINFECT_TARGET_TEMPERATURE_C ); startHeater( DG_TRIMMER_HEATER ); - state = DG_HEAT_DISINFECT_STATE_DISINFECT_R2_TO_R1; + stateTimer = getMSTimerCount(); + state = DG_HEAT_DISINFECT_STATE_DISINFECT_R2_TO_R1; } else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr2Status ) { prevHeatDisinfectState = state; - state = DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH; + state = DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH; } } else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr1Status ) { prevHeatDisinfectState = state; - state = DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH; + state = DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH; } return state; @@ -1135,8 +1129,20 @@ { DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_DISINFECT_R2_TO_R1; HEAT_DISINFECT_STATUS_T status = getHeatDisinfectStatus(); - heatDisinfectUIState = HEAT_DISINFECT_UI_STATE_DISINFECT_RESERVOIR_2; + heatDisinfectUIState = HEAT_DISINFECT_UI_STATE_DISINFECT_RESERVOIR_2; + + if ( TRUE == didTimeout( stateTimer, HEAT_DISINFECT_REF_RSRVR_TIMEOUT_MS ) ) + { + if ( ( rsrvr1RefVolML < NEARLY_ZERO ) && ( rsrvr2RefVolML < NEARLY_ZERO ) ) + { + // Get the current volumes of R1 & R2. These values will be used to make sure the reservoirs' + // volume does not change more than a certain amount during the actual heat disinfect cycle + rsrvr1RefVolML = getLoadCellLargeFilteredWeight( LOAD_CELL_RESERVOIR_1_PRIMARY ); + rsrvr2RefVolML = getLoadCellLargeFilteredWeight( LOAD_CELL_RESERVOIR_2_PRIMARY ); + } + } + switch ( status ) { case HEAT_DISINFECT_RSRVRS_LEAK_TIMEOUT: @@ -1176,8 +1182,9 @@ static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectCoolDownHeatersState( void ) { DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_COOL_DOWN_HEATERS; - heatDisinfectUIState = HEAT_DISINFECT_UI_STATE_COOL_DOWN_DEVICE; + heatDisinfectUIState = HEAT_DISINFECT_UI_STATE_COOL_DOWN_DEVICE; + writeDisinfectDataToNV( USAGE_INFO_HEAT_DIS ); if ( TRUE == didTimeout( stateTimer, POST_HEAT_DISINFECT_WAIT_TIME_MS ) ) @@ -1216,8 +1223,9 @@ static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectMixDrainR1State( void ) { DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_MIX_DRAIN_R1; - heatDisinfectUIState = HEAT_DISINFECT_UI_STATE_FLUSH_AFTER_DISINFECT; + heatDisinfectUIState = HEAT_DISINFECT_UI_STATE_FLUSH_AFTER_DISINFECT; + if ( ( TRUE == didTimeout( stateTimer, DRAIN_PUMP_START_TIME_IN_MIX_DRAIN_MS ) ) && ( FALSE == isDrainPumpInMixDrainOn ) ) { isDrainPumpInMixDrainOn = TRUE; @@ -1334,7 +1342,7 @@ // Set the heat disinfect UI state heatDisinfectUIState = HEAT_DISINFECT_UI_STATE_CANCEL_DISINFECT; - U32 drainTimeoutMS = RSRVRS_INITIAL_DRAIN_TIME_OUT_MS; + U32 drainTimeoutMS = ( CANCELLATION_MODE_COLD == cancellationMode ? RSRVRS_INITIAL_DRAIN_TIME_OUT_MS : RSRVRS_MIX_DRAIN_TIMEOUT_MS ); if ( CANCELLATION_MODE_NONE == cancellationMode ) { @@ -1439,7 +1447,6 @@ { DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_COMPLETE; - // Set the heat disinfect UI state heatDisinfectUIState = HEAT_DISINFECT_UI_STATE_COMPLETE; requestNewOperationMode( DG_MODE_HCOL ); @@ -1609,8 +1616,8 @@ F32 ThdTemp = getTemperatureValue( TEMPSENSORS_HEAT_DISINFECT ); F32 loadCellA1 = getLoadCellSmallFilteredWeight( LOAD_CELL_RESERVOIR_1_PRIMARY ); F32 loadCellB1 = getLoadCellSmallFilteredWeight( LOAD_CELL_RESERVOIR_2_PRIMARY ); - BOOL isR1OutOfRange = ( fabs( loadCellA1 - R1HeatDisinfectVol ) > RSRVRS_MAX_TARGET_VOL_CHANGE_ML ? TRUE : FALSE ); - BOOL isR2OutOfRange = ( fabs( loadCellB1 - R2HeatDisinfectVol ) > RSRVRS_MAX_TARGET_VOL_CHANGE_ML ? TRUE : FALSE ); + BOOL isR1OutOfRange = ( fabs( loadCellA1 - rsrvr1RefVolML ) > RSRVRS_MAX_TARGET_VOL_CHANGE_ML ? TRUE : FALSE ); + BOOL isR2OutOfRange = ( fabs( loadCellB1 - rsrvr2RefVolML ) > RSRVRS_MAX_TARGET_VOL_CHANGE_ML ? TRUE : FALSE ); BOOL isGradientOutOfRange = ( fabs( TPoTemp - ThdTemp ) > HEAT_DISINFECT_MAX_TEMP_GRADIENT_C ? TRUE : FALSE ); // Perform check if no pending alarm @@ -1634,22 +1641,25 @@ // Perform check if no pending alarm if ( ALARM_ID_NO_ALARM == alarmDetectedPendingTrigger ) { - // Check if either reservoir 1 or reservoir 2 are losing volume more than allowed volume - if ( ( TRUE == isR1OutOfRange ) || ( TRUE == isR2OutOfRange ) ) + if ( ( rsrvr1RefVolML > NEARLY_ZERO ) && ( rsrvr2RefVolML > NEARLY_ZERO ) ) { - // If the leak is the first time after a while, set the flag and start the timer - if ( FALSE == areRsrvrsLeaking ) + // Check if either reservoir 1 or reservoir 2 are losing volume more than allowed volume + if ( ( TRUE == isR1OutOfRange ) || ( TRUE == isR2OutOfRange ) ) { - areRsrvrsLeaking = TRUE; - rsrvrsVolMonitorTimer = getMSTimerCount(); + // If the leak is the first time after a while, set the flag and start the timer + if ( FALSE == areRsrvrsLeaking ) + { + areRsrvrsLeaking = TRUE; + rsrvrsVolMonitorTimer = getMSTimerCount(); + } + // If the volume is out of range and it has timed out, exit + else if ( TRUE == didTimeout( rsrvrsVolMonitorTimer, RSRVRS_TARGET_VOL_OUT_TIMEOUT_MS ) ) + { + areRsrvrsLeaking = FALSE; + alarmDetectedPendingTrigger = ALARM_ID_DG_RESERVOIR_LEAK_TIMEOUT; + status = HEAT_DISINFECT_RSRVRS_LEAK_TIMEOUT; + } } - // If the volume is out of range and it has timed out, exit - else if ( TRUE == didTimeout( rsrvrsVolMonitorTimer, RSRVRS_TARGET_VOL_OUT_TIMEOUT_MS ) ) - { - areRsrvrsLeaking = FALSE; - alarmDetectedPendingTrigger = ALARM_ID_DG_RESERVOIR_LEAK_TIMEOUT; - status = HEAT_DISINFECT_RSRVRS_LEAK_TIMEOUT; - } } // Reservoirs are in range else @@ -1745,8 +1755,8 @@ uiData.heatDisinfectTargetTime = targetDisinfectTime; uiData.heatDisinfectCountdownTime = countDown / 1000; // The count down is converted into seconds since the UI does not work with milliseconds - data.R1FillLevel = R1HeatDisinfectVol; - data.R2FillLevel = R2HeatDisinfectVol; + data.R1FillLevel = rsrvr1RefVolML; + data.R2FillLevel = rsrvr2RefVolML; } else { @@ -1815,6 +1825,8 @@ * non-volatile memory. * @details Inputs: disinfectNVOps * @details Outputs: disinfectNVOps + * @param info the type disinfect data to write to the memory (i.e. heat + * disinfect start time) * @return: none *************************************************************************/ static void writeDisinfectDataToNV( DG_USAGE_INFO_ITEMS_T info )