Index: firmware/App/Controllers/Heaters.c =================================================================== diff -u -r9c3c817b09c3c7e0d9b23a397493f6fc16ca5d62 -ref511196d29f6b0de6ade9ea113d91164d938c88 --- firmware/App/Controllers/Heaters.c (.../Heaters.c) (revision 9c3c817b09c3c7e0d9b23a397493f6fc16ca5d62) +++ firmware/App/Controllers/Heaters.c (.../Heaters.c) (revision ef511196d29f6b0de6ade9ea113d91164d938c88) @@ -8,7 +8,7 @@ * @file Heaters.c * * @author (last) Dara Navaei -* @date (last) 17-May-2023 +* @date (last) 20-May-2023 * * @author (original) Dara Navaei * @date (original) 23-Apr-2020 @@ -99,6 +99,7 @@ U32 controlIntervalCounter; ///< Heater control interval counter. BOOL isThisFirstControl; ///< Heater is this first control interval. BOOL useLastDutyCycle; ///< Heater has use previous duty cycle been requested flag. + F32 prevDiaTargetFlowLPM; ///< Heater previous target dialysate flow in L/min. } HEATER_STATUS_T; static HEATER_STATUS_T heatersStatus[ NUM_OF_DG_HEATERS ]; ///< Heaters status. @@ -156,6 +157,7 @@ heatersStatus[ heater ].controlIntervalCounter = 0; heatersStatus[ heater ].isThisFirstControl = TRUE; heatersStatus[ heater ].useLastDutyCycle = FALSE; + heatersStatus[ heater ].prevDiaTargetFlowLPM = 0.0F; } // Initialize the persistent alarms @@ -692,13 +694,12 @@ // Update the calculated target temperature // Reset the duty cycle since the reservoir has been switched + // Cap the minimum duty cycle. So if it is calculated to negative, set it to 0 heatersStatus[ heater ].calculatedTemperatureC = currentTemperature; heatersStatus[ heater ].inactiveRsrvr = getInactiveReservoir(); heatersStatus[ heater ].targetFlowLPM = targetFlowLPM; heatersStatus[ heater ].isThisFirstControl = TRUE; - - // Cap the minimum duty cycle. So if it is calculated to negative, set it to 0 - heatersStatus[ heater ].dutyCycle.data = MAX( dutyCycle, HEATERS_MIN_DUTY_CYCLE ); + heatersStatus[ heater ].dutyCycle.data = MAX( dutyCycle, HEATERS_MIN_DUTY_CYCLE ); setHeaterDutyCycle( heater ); return state; @@ -717,35 +718,50 @@ HEATERS_STATE_T state = HEATER_EXEC_STATE_TRIMMER_CONTROL_TO_TARGET; F32 tempDutyCycle = 0.0F; DG_HEATERS_T heater = DG_TRIMMER_HEATER; + F32 targetFlowLPM = getTargetDialysateFlowLPM(); U32 controlInterval = ( TRUE == heatersStatus[ heater ].isThisFirstControl ? TRIMMER_HEATER_INITIAL_CONTROL_INTERVAL_COUNT : TRIMMER_HEATER_CONTROL_INTERVAL_COUNT ); - // If the inactive reservoir has changed from the last run transition to ramp state to recalculate the + // If the inactive reservoir has changed from the last run, transition to ramp state to recalculate the // duty cycle for the next delivery if ( heatersStatus[ heater ].inactiveRsrvr != getInactiveReservoir() ) { state = HEATER_EXEC_STATE_TRIMMER_RAMP_TO_TARGET; } - else if ( ++heatersStatus[ heater ].controlIntervalCounter > controlInterval ) + else if ( ( ++heatersStatus[ heater ].controlIntervalCounter > controlInterval ) || + ( fabs( heatersStatus[ heater ].prevDiaTargetFlowLPM - targetFlowLPM ) > NEARLY_ZERO ) ) { - // Reset the control counter + // Check if it is time for another control interval or the current target flow is different from the previous flow and + // we need to reset the control counter heatersStatus[ heater ].controlIntervalCounter = 0; heatersStatus[ heater ].isThisFirstControl = FALSE; // When the trimmer heater is on, its duty cycle is adjusted at the control interval. For this control check, // dialysate inlet temperature sensor is used rather than the theoretical calculations. F32 dialysateInletTemperature = getTemperatureValue( TEMPSENSORS_OUTLET_REDUNDANT ); F32 targetTemperature = heatersStatus[ heater ].targetTempC; - F32 targetFlowLPM = getTargetDialysateFlowLPM(); F32 dutyCycle = calculateTrimmerHeaterDutyCycle( targetTemperature, dialysateInletTemperature, targetFlowLPM, TRUE ); - tempDutyCycle = heatersStatus[ heater ].dutyCycle.data + dutyCycle; + if ( fabs( heatersStatus[ heater ].prevDiaTargetFlowLPM - targetFlowLPM ) > NEARLY_ZERO ) + { + // If the current flow is different from the previous flow, restart the duty cycle as the controller is restarting itself + // Set it as it is the first control so the control interval is shorter since it is the first guess with the new dialysate flow + tempDutyCycle = dutyCycle; + heatersStatus[ heater ].isThisFirstControl = TRUE; + } + else + { + tempDutyCycle = heatersStatus[ heater ].dutyCycle.data + dutyCycle; + } + tempDutyCycle = MIN( tempDutyCycle, HEATERS_MAX_DUTY_CYCLE ); tempDutyCycle = MAX( tempDutyCycle, HEATERS_MIN_DUTY_CYCLE ); heatersStatus[ heater ].dutyCycle.data = tempDutyCycle; setHeaterDutyCycle( heater ); } + heatersStatus[ heater ].prevDiaTargetFlowLPM = targetFlowLPM; + return state; } @@ -974,6 +990,8 @@ data.primaryCalcTargetTemp = heatersStatus[ DG_PRIMARY_HEATER ].calculatedTemperatureC; data.trimmerCalcCurrentTemp = heatersStatus[ DG_TRIMMER_HEATER ].calculatedTemperatureC; data.trimmerUseLastDC = (U32)heatersStatus[ DG_TRIMMER_HEATER ].useLastDutyCycle; + data.previsouFlow = heatersStatus[ DG_TRIMMER_HEATER ].prevDiaTargetFlowLPM; + data.controlCounter = heatersStatus[ DG_TRIMMER_HEATER ].controlIntervalCounter; dataPublicationTimerCounter = 0; broadcastData( MSG_ID_DG_HEATERS_DATA, COMM_BUFFER_OUT_CAN_DG_BROADCAST, (U08*)&data, sizeof( HEATERS_DATA_T ) );