Index: firmware/App/Controllers/BalancingChamber.c =================================================================== diff -u -rd748813399d38ef5b71d760e327e368cc82d7a38 -rff8dde1e0c78936fc2db1aa496c9dce2e498e03b --- firmware/App/Controllers/BalancingChamber.c (.../BalancingChamber.c) (revision d748813399d38ef5b71d760e327e368cc82d7a38) +++ firmware/App/Controllers/BalancingChamber.c (.../BalancingChamber.c) (revision ff8dde1e0c78936fc2db1aa496c9dce2e498e03b) @@ -7,8 +7,8 @@ * * @file BalancingChamber.c * -* @author (last) Vinayakam Mani -* @date (last) 11-Feb-2026 +* @author (last) Jashwant Gantyada +* @date (last) 05-Mar-2026 * * @author (original) Vinayakam Mani * @date (original) 28-Jan-2025 @@ -23,6 +23,7 @@ #include "FpgaDD.h" #include "Heaters.h" #include "ModeStandby.h" +#include "ModeGenDialysate.h" #include "Messaging.h" #include "OperationModes.h" #include "Pressure.h" @@ -39,11 +40,15 @@ // ********** private definitions ********** -#define BAL_CHAMBER_DATA_PUBLISH_INTERVAL ( 250 / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) at which the balancing chamber data published. -#define BAL_CHAMBER_FILL_PRES_DROP_MS ( 200 / TASK_GENERAL_INTERVAL ) ///< Time (ms/tasktime) to confirm the balancing chamber filling started and corresponding valves opened. -#define BAL_CHAMBER_FILL_COMPLETE_MS ( 300 / TASK_GENERAL_INTERVAL ) ///< Time (ms/tasktime) to confirm the balancing chamber fill completed and pressure is within range +#define BAL_CHAMBER_DATA_PUBLISH_INTERVAL ( 250 / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) at which the balancing chamber data published. +#define BAL_CHAMBER_FILL_PRES_DROP_MS ( 200 / TASK_GENERAL_INTERVAL ) ///< Time (ms/tasktime) to confirm the balancing chamber filling started and corrosponding valves opened. +#define BAL_CHAMBER_FILL_COMPLETE_MS ( 300 / TASK_GENERAL_INTERVAL ) ///< Time (ms/tasktime) to confirm the balancing chamber fill completed and pressure is within range +#define SPENT_FILL_COMPLETE_PRES 25.0F ///< Spent pressure that indicates fill is complete. +#define SPENT_DIFF_COUNT_ZERO 0 ///< Zero count difference for spent side fill comparing target count +#define SPENT_DIFF_COUNT_DEADBAND 1 ///< Stop changing D48 pump speed when fill difference reaches close to target count +#define D48_SPEED_ADJUST_FACTOR 0.5F ///< D48 speed adjustment factor ( 50% of speed adjustment = 0.5) +#define D48_SPEED_RANGE_LIMIT 0.25F ///< D48 speed adjustment range check limit ( D48 speed can vary +/-25% of initial calculated speed) #define BICARB_CHAMBER_PERIODIC_FILL_TIME ( 1 * SEC_PER_MIN * ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ) ///< Periodic bicarb chamber fill request 60 sec x 20 = 1200 - /// Payload record structure for balancing chamber switch only request typedef struct { @@ -73,9 +78,13 @@ static F32 lastTdDialysateFlowrate; ///< Previous TD dialysate flow rate static F32 freshDialPressure; ///< Fresh side dialysate pressure static F32 spentDialPressure; ///< Spent side dialysate pressure - +static F32 prevSpentDialPressure; ///< Previous spent side dialysate pressure +static F32 lastPrevSpentDialPressure; ///< Last previous spent side dialysate pressure +static U32 currentBalChamberFillCounter; ///< Counter (in task interval) to monitor the timing spent for the spent side fill operation. +static S32 diffSpentFillCompleteCount; ///< Difference between spent target fill to actual fill count +static BOOL isSpentFillComplete; //TODO: remove later once level sensor working -static U32 bicarbChamberPeriodicFillCounter; ///< Counter for checking the timeout for drybicart chamber fill request. +static U32 bicarbChamberPeriodicFillCounter; // ********** private function prototypes ********** @@ -88,6 +97,7 @@ static BAL_CHAMBER_EXEC_STATE_T handleBalChamberState2FillEnd(void); static void publishBalChamberData( void ); static U32 getBalChamberDataPublishInterval( void ); +static void checkSpentFillComplete( F32 spentDialPressure ); /*********************************************************************//** * @brief @@ -130,10 +140,14 @@ isPressureDroppedDuringFill = FALSE; freshDialPressure = 0.0F; spentDialPressure = 0.0F; - + prevSpentDialPressure = 0.0F; + lastPrevSpentDialPressure = 0.0F; + currentBalChamberFillCounter = 0; + diffSpentFillCompleteCount = 0; + isSpentFillComplete = FALSE; //TODO:remove once level sensor working bicarbChamberPeriodicFillCounter = 0; - } +} /*********************************************************************//** * @brief @@ -199,6 +213,7 @@ { // Increment counter indicating fill is in progress. currentBalChamberSwitchingCounter += 1; + currentBalChamberFillCounter += 1; if ( getTestConfigStatus( TEST_CONFIG_DD_ENABLE_DRY_BICARB ) == TRUE ) { @@ -269,43 +284,20 @@ /*********************************************************************//** * @brief - * The valveControlForBCClosedState function closes the all balancing - * chamber valves. + * The valveControlForBCState1FillStart function actuates the valve combination + * for state 1 fill/drain process. * @details \b Inputs: none * @details \b Outputs: valve states * @return none. *************************************************************************/ -void valveControlForBCClosedState( void ) +static void valveControlForBCState1FillStart( void ) { // Close balancing chamber valve combinations D23,D20 and D21,D26 setValveState( D23_VALV, VALVE_STATE_CLOSED ); setValveState( D20_VALV, VALVE_STATE_CLOSED ); setValveState( D21_VALV, VALVE_STATE_CLOSED ); setValveState( D26_VALV, VALVE_STATE_CLOSED ); - //Close balancing chamber valve combinations D19,D24 and D25,D22 - setValveState( D19_VALV, VALVE_STATE_CLOSED ); - setValveState( D24_VALV, VALVE_STATE_CLOSED ); - setValveState( D25_VALV, VALVE_STATE_CLOSED ); - setValveState( D22_VALV, VALVE_STATE_CLOSED ); -} - -/*********************************************************************//** - * @brief - * The valveControlForBCOpenState function opens all of the balancing - * chamber valves. - * @details \b Inputs: none - * @details \b Outputs: valve states - * @return none. - *************************************************************************/ -void valveControlForBCOpenState( void ) -{ - // Open balancing chamber valve combinations D23,D20 and D21,D26 - setValveState( D23_VALV, VALVE_STATE_OPEN ); - setValveState( D20_VALV, VALVE_STATE_OPEN ); - setValveState( D21_VALV, VALVE_STATE_OPEN ); - setValveState( D26_VALV, VALVE_STATE_OPEN ); - //Open balancing chamber valve combinations D19,D24 and D25,D22 setValveState( D19_VALV, VALVE_STATE_OPEN ); setValveState( D24_VALV, VALVE_STATE_OPEN ); @@ -315,115 +307,46 @@ /*********************************************************************//** * @brief - * The valveControlForBCFreshSideOnlyOpenState function opens the fresh side - * balancing chamber valves and closes the spent side valves. + * The valveControlForBCState1FillEnd function closes the valve opened + * for state 1 fill/drain process. * @details \b Inputs: none * @details \b Outputs: valve states * @return none. *************************************************************************/ -void valveControlForBCFreshSideOnlyOpenState( void ) +static void valveControlForBCState1FillEnd( void ) { - // Open fresh balancing chamber valves D19,D20,D21,D22 - setValveState( D19_VALV, VALVE_STATE_OPEN ); - setValveState( D20_VALV, VALVE_STATE_OPEN ); - setValveState( D21_VALV, VALVE_STATE_OPEN ); - setValveState( D22_VALV, VALVE_STATE_OPEN ); - - // Close spent side valves D23,D24,D25,D26 - setValveState( D23_VALV, VALVE_STATE_CLOSED ); - setValveState( D25_VALV, VALVE_STATE_CLOSED ); + //Close balancing chamber valve combinations D19,D24 and D25,D22 + setValveState( D19_VALV, VALVE_STATE_CLOSED ); setValveState( D24_VALV, VALVE_STATE_CLOSED ); - setValveState( D26_VALV, VALVE_STATE_CLOSED ); + setValveState( D25_VALV, VALVE_STATE_CLOSED ); + setValveState( D22_VALV, VALVE_STATE_CLOSED ); } /*********************************************************************//** * @brief - * The getCurrentBalancingChamberExecState function returns the current state - * of the balancing chamber. - * @details \b Inputs: balChamberExecState - * @details \b Outputs: none - * @return the current state of balancing chamber states. + * The valveControlForBCState2FillStart function actuates the valve combination + * for state 2 fill/drain process. + * @details \b Inputs: none + * @details \b Outputs: valve states + * @return none. *************************************************************************/ -BAL_CHAMBER_EXEC_STATE_T getCurrentBalancingChamberExecState( void ) +static void valveControlForBCState2FillStart( void ) { - return balChamberExecState; -} + //Close balancing chamber valve combinations D19,D24 and D25,D22 + setValveState( D19_VALV, VALVE_STATE_CLOSED ); + setValveState( D24_VALV, VALVE_STATE_CLOSED ); + setValveState( D25_VALV, VALVE_STATE_CLOSED ); + setValveState( D22_VALV, VALVE_STATE_CLOSED ); -/*********************************************************************//** - * @brief - * The getBalancingChamberFillinProgressStatus function returns the current - * balancing chamber fill in progress status. - * @details \b Inputs: isBalChamberFillInProgress - * @details \b Outputs: none - * @return the current balancing chamber fill in progress. - *************************************************************************/ -BOOL getBalancingChamberFillinProgressStatus( void ) -{ - return isBalChamberFillInProgress; + // Open balancing chamber valve combinations D23,D20 and D21,D26 + setValveState( D23_VALV, VALVE_STATE_OPEN ); + setValveState( D20_VALV, VALVE_STATE_OPEN ); + setValveState( D21_VALV, VALVE_STATE_OPEN ); + setValveState( D26_VALV, VALVE_STATE_OPEN ); } /*********************************************************************//** * @brief - * The getBalChamberSwitchingFreq function gets the balancing chamber switching - * frequency value. - * @details \b Inputs: balChamberSwitchingFreq - * @details \b Outputs: none - * @return balancing chamber switching frequency - *************************************************************************/ -F32 getBalChamberSwitchingFreq( void ) -{ - F32 result = balChamberSwitchingFreq.data; - - if ( OVERRIDE_KEY == balChamberSwitchingFreq.override ) - { - result = balChamberSwitchingFreq.ovData; - } - - return result; -} - -/*********************************************************************//** - * @brief - * The setBalChamberSwitchingOnlyStatus function sets the balancing chamber - * switching only On/Off status. - * @details \b Inputs: balanceChamberSwitchingOnly - * @details \b Outputs: none - * @return none - *************************************************************************/ -void setBalChamberSwitchingOnlyStatus( BOOL OnOff ) -{ - balanceChamberSwitchingOnly = OnOff; -} - -/*********************************************************************//** - * @brief - * The getBalChamberSwitchingOnlyStatus function gets the balancing chamber - * switching only status. - * @details \b Inputs: balanceChamberSwitchingOnly - * @details \b Outputs: none - * @return balancing chamber switching only without any pressure validation - * and dosing delivery - *************************************************************************/ -BOOL getBalChamberSwitchingOnlyStatus( void ) -{ - return balanceChamberSwitchingOnly; -} - -/*********************************************************************//** - * @brief - * The getBalChamberSwitchingPeriod function gets the balancing chamber - * switching period. - * @details \b Inputs: balChamberSwitchingPeriod - * @details \b Outputs: none - * @return balancing chamber switching period - *************************************************************************/ -U32 getBalChamberSwitchingPeriod( void ) -{ - return balChamberSwitchingPeriod; -} - -/*********************************************************************//** - * @brief * The valveControlForBCState2FillEnd function closes the valve opened * for state 2 fill/drain process. * @details \b Inputs: none @@ -441,65 +364,71 @@ /*********************************************************************//** * @brief - * The valveControlForBCState1FillStart function actuates the valve combination - * for state 1 fill/drain process. + * The valveControlForBCClosedState function closes the all balancing + * chamber valves. * @details \b Inputs: none * @details \b Outputs: valve states * @return none. *************************************************************************/ -static void valveControlForBCState1FillStart( void ) +void valveControlForBCClosedState( void ) { // Close balancing chamber valve combinations D23,D20 and D21,D26 setValveState( D23_VALV, VALVE_STATE_CLOSED ); setValveState( D20_VALV, VALVE_STATE_CLOSED ); setValveState( D21_VALV, VALVE_STATE_CLOSED ); setValveState( D26_VALV, VALVE_STATE_CLOSED ); - //Open balancing chamber valve combinations D19,D24 and D25,D22 - setValveState( D19_VALV, VALVE_STATE_OPEN ); - setValveState( D24_VALV, VALVE_STATE_OPEN ); - setValveState( D25_VALV, VALVE_STATE_OPEN ); - setValveState( D22_VALV, VALVE_STATE_OPEN ); + //Close balancing chamber valve combinations D19,D24 and D25,D22 + setValveState( D19_VALV, VALVE_STATE_CLOSED ); + setValveState( D24_VALV, VALVE_STATE_CLOSED ); + setValveState( D25_VALV, VALVE_STATE_CLOSED ); + setValveState( D22_VALV, VALVE_STATE_CLOSED ); } /*********************************************************************//** * @brief - * The valveControlForBCState1FillEnd function closes the valve opened - * for state 1 fill/drain process. + * The valveControlForBCOpenState function open the all balancing + * chamber valves. * @details \b Inputs: none * @details \b Outputs: valve states * @return none. *************************************************************************/ -static void valveControlForBCState1FillEnd( void ) +void valveControlForBCOpenState( void ) { - //Close balancing chamber valve combinations D19,D24 and D25,D22 - setValveState( D19_VALV, VALVE_STATE_CLOSED ); - setValveState( D24_VALV, VALVE_STATE_CLOSED ); - setValveState( D25_VALV, VALVE_STATE_CLOSED ); - setValveState( D22_VALV, VALVE_STATE_CLOSED ); + // Open balancing chamber valve combinations D23,D20 and D21,D26 + setValveState( D23_VALV, VALVE_STATE_OPEN ); + setValveState( D20_VALV, VALVE_STATE_OPEN ); + setValveState( D21_VALV, VALVE_STATE_OPEN ); + setValveState( D26_VALV, VALVE_STATE_OPEN ); + + //Open balancing chamber valve combinations D19,D24 and D25,D22 + setValveState( D19_VALV, VALVE_STATE_OPEN ); + setValveState( D24_VALV, VALVE_STATE_OPEN ); + setValveState( D25_VALV, VALVE_STATE_OPEN ); + setValveState( D22_VALV, VALVE_STATE_OPEN ); } /*********************************************************************//** * @brief - * The valveControlForBCState2FillStart function actuates the valve combination - * for state 2 fill/drain process. + * The valveControlForBCFreshSideOnlyOpenState function opens the fresh side + * balancing chamber valves and closes the spent side valves. * @details \b Inputs: none * @details \b Outputs: valve states * @return none. *************************************************************************/ -static void valveControlForBCState2FillStart( void ) +void valveControlForBCFreshSideOnlyOpenState( void ) { - //Close balancing chamber valve combinations D19,D24 and D25,D22 - setValveState( D19_VALV, VALVE_STATE_CLOSED ); - setValveState( D24_VALV, VALVE_STATE_CLOSED ); - setValveState( D25_VALV, VALVE_STATE_CLOSED ); - setValveState( D22_VALV, VALVE_STATE_CLOSED ); - - // Open balancing chamber valve combinations D23,D20 and D21,D26 - setValveState( D23_VALV, VALVE_STATE_OPEN ); + // Open fresh balancing chamber valves D19,D20,D21,D22 + setValveState( D19_VALV, VALVE_STATE_OPEN ); setValveState( D20_VALV, VALVE_STATE_OPEN ); setValveState( D21_VALV, VALVE_STATE_OPEN ); - setValveState( D26_VALV, VALVE_STATE_OPEN ); + setValveState( D22_VALV, VALVE_STATE_OPEN ); + + // Close spent side valves D23,D24,D25,D26 + setValveState( D23_VALV, VALVE_STATE_CLOSED ); + setValveState( D25_VALV, VALVE_STATE_CLOSED ); + setValveState( D24_VALV, VALVE_STATE_CLOSED ); + setValveState( D26_VALV, VALVE_STATE_CLOSED ); } /*********************************************************************//** @@ -509,33 +438,30 @@ * @details \b Inputs: Pressure * @details \b Outputs: valve states * @details \b Alarm: ALARM_ID_DD_BC_STATE1_FILL_START_PRESSURE_OUT_OF_RANGE - * when pressure is not in range during balacing chamber fill start. + * when pressure is not in range during balancing chamber fill start. * @return next balancing chamber state. *************************************************************************/ static BAL_CHAMBER_EXEC_STATE_T handleBalChamberState1FillStart( void ) { BAL_CHAMBER_EXEC_STATE_T state = BAL_CHAMBER_STATE1_FILL_START; currentBalChamberSwitchingCounter = 0; + balChamberSWState = BAL_CHAMBER_SW_STATE1; + currentBalChamberFillCounter = 0; isBalChamberFillInProgress = FALSE; isPressureStabilizedDuringFill = FALSE; isPressureDroppedDuringFill = FALSE; - balChamberSWState = BAL_CHAMBER_SW_STATE1; + isSpentFillComplete = FALSE; + lastPrevSpentDialPressure = 0.0F; + prevSpentDialPressure = 0.0F; F32 acidVolume = getF32OverrideValue( &acidDoseVolume ); F32 bicarbVolume = getF32OverrideValue( &bicarbDoseVolume ); freshDialPressure = getFilteredPressure( D18_PRES ); spentDialPressure = getFilteredPressure( D51_PRES ); - //Set Trimmer heater Target temp every BC cycle to catch up fresh dialysate temp - setHeaterTargetTemperature( D45_HEAT, getD4AverageTemperature() ); - - BOOL isFreshDialysatePressureInRange = ( freshDialPressure >= FRESH_DIAL_PRESSURE_MIN_PSIG ) && ( freshDialPressure <= FRESH_DIAL_PRESSURE_MAX_PSIG ); - BOOL isSpentDialysatePressureInRange = ( spentDialPressure >= SPENT_DIAL_PRESSURE_MIN_PSIG ) && ( spentDialPressure <= SPENT_DIAL_PRESSURE_MAX_PSIG ); - BOOL isBCSwitchOnlyStatus = ( TRUE == getBalChamberSwitchingOnlyStatus() ); - BOOL isDisableBCPressureAlarms = ( TRUE == getTestConfigStatus( TEST_CONFIG_DD_DISABLE_BC_PRESSURE_ALARMS ) ); - // Check fresh and spent dialysate pressure in range or BC switch only flag set - if ( ( TRUE == isFreshDialysatePressureInRange && TRUE == isSpentDialysatePressureInRange ) || ( TRUE == isBCSwitchOnlyStatus ) || ( TRUE == isDisableBCPressureAlarms ) ) + if ( ( ( freshDialPressure >= FRESH_DIAL_PRESSURE_MIN_PSIG ) && ( freshDialPressure <= FRESH_DIAL_PRESSURE_MAX_PSIG ) ) || + ( TRUE == getBalChamberSwitchingOnlyStatus() ) || ( TRUE == getTestConfigStatus( TEST_CONFIG_DD_DISABLE_BC_PRESSURE_ALARMS ) ) ) { //Valve control for state 1 fill valveControlForBCState1FillStart(); @@ -546,9 +472,9 @@ // Deliver dosing during generate dialysate mode if ( TRUE != getBalChamberSwitchingOnlyStatus() ) { - // start acid and bicarb pump with the expected rate, speed in mL/min - setConcentratePumpTargetSpeed( D11_PUMP, DOSING_CONCENTRATE_PUMP_SPEED, acidVolume ); - setConcentratePumpTargetSpeed( D10_PUMP, DOSING_CONCENTRATE_PUMP_SPEED, bicarbVolume ); + // start acid and bicarb pump with the expected quantity + setConcentratePumpTargetSpeed( D11_PUMP, CONCENTRATE_PUMP_MAX_SPEED, acidVolume ); + setConcentratePumpTargetSpeed( D10_PUMP, CONCENTRATE_PUMP_MAX_SPEED, bicarbVolume ); requestConcentratePumpOn( D11_PUMP ); requestConcentratePumpOn( D10_PUMP ); } @@ -570,7 +496,7 @@ /*********************************************************************//** * @brief * The handleBalChamberConcentrateControl function handles the Acid and Bicarb - * concentrate doisng and checks the conductivity of the dialysate for the treatment. + * concentrate dosing and checks the conductivity of the dialysate for the treatment. * @details \b Inputs: balChamberSWState , Concentrate volume * @details \b Outputs: isPressureDroppedDuringFill, state * @return next balancing chamber state. @@ -581,7 +507,7 @@ freshDialPressure = getFilteredPressure( D18_PRES ); spentDialPressure = getFilteredPressure( D51_PRES ); - if ( BAL_CHAMBER_SW_STATE1 == balChamberSWState ) + if ( BAL_CHAMBER_SW_STATE1 == balChamberSWState ) { state = BAL_CHAMBER_STATE1_BICARB_ACID_DOSING_CNTRL; } @@ -604,6 +530,10 @@ ( TRUE == isConcentratePumpDosingCompleted( D10_PUMP ) ) ) || ( TRUE == getBalChamberSwitchingOnlyStatus() ) ) { + //Collect first sample of spent pressure + lastPrevSpentDialPressure = prevSpentDialPressure; + prevSpentDialPressure = spentDialPressure; + if ( BAL_CHAMBER_SW_STATE1 == balChamberSWState ) { state = BAL_CHAMBER_STATE1_VALVES_CLOSE; @@ -628,14 +558,11 @@ static BAL_CHAMBER_EXEC_STATE_T handleBalChamberState1ValvesClose( void ) { BAL_CHAMBER_EXEC_STATE_T state = BAL_CHAMBER_STATE1_VALVES_CLOSE; - BOOL isFreshDialysatePressureInRange = ( freshDialPressure >= FRESH_DIAL_PRESSURE_MIN_PSIG ) && ( freshDialPressure <= FRESH_DIAL_PRESSURE_MAX_PSIG ); - BOOL isSpentDialysatePressureInRange = ( spentDialPressure >= SPENT_DIAL_PRESSURE_MIN_PSIG ) && ( spentDialPressure <= SPENT_DIAL_PRESSURE_MAX_PSIG ); + freshDialPressure = getFilteredPressure( D18_PRES ); + spentDialPressure = getFilteredPressure( D51_PRES ); - freshDialPressure = getFilteredPressure( D18_PRES ); - spentDialPressure = getFilteredPressure( D51_PRES ); - - // Check fresh and spent dialysate pressure back in range to indicate fill complete. - if ( TRUE == isFreshDialysatePressureInRange && TRUE == isSpentDialysatePressureInRange ) + // Check fresh dialysate pressure back in range to indicate fresh fill complete. + if ( ( freshDialPressure >= FRESH_DIAL_PRESSURE_MIN_PSIG ) && ( freshDialPressure <= FRESH_DIAL_PRESSURE_MAX_PSIG ) ) { if ( ++balChamberFillCompleteStablePressureCounter >= BAL_CHAMBER_FILL_COMPLETE_MS ) { @@ -644,15 +571,22 @@ } } - // Switching time met or pressure in range, close valves - if ( ( currentBalChamberSwitchingCounter >= balChamberValveClosePeriod ) || - ( TRUE == isPressureStabilizedDuringFill ) ) + // Spent side of balancing chamber fill is complete or not + checkSpentFillComplete( spentDialPressure ); + + // Check both spent and fresh side fill is complete + if ( ( TRUE == isSpentFillComplete ) && ( TRUE == isPressureStabilizedDuringFill ) || ( FALSE == isFirstCycleBCSwitchingCompleted ) ) { // close the state 1 opened valves valveControlForBCState1FillEnd(); isBalChamberFillInProgress = FALSE; + //setDialysatePumpTargetRPM( D48_PUMP, getD48PumpSpeedForBCFill(), TRUE ); + if ( TRUE == isFirstCycleBCSwitchingCompleted ) + { + setDialysatePumpTargetRPM( D48_PUMP, getD48PumpSpeedForBCFill(), TRUE ); + } - //Transition to next state + // Transition to next state state = BAL_CHAMBER_STATE1_FILL_END; } @@ -662,56 +596,52 @@ /*********************************************************************//** * @brief * The handleBalChamberState1FillEnd function check for the balancing chamber - * switching period and tranistion to next state switching. + * switching period and transition to next state switching. * @details \b Inputs: currentBalChamberSwitchingCounter, balChamberSwitchingPeriod * @details \b Outputs: balChamberFillPressureDropCounter,balChamberFillCompleteStablePressureCounter * @details \b Alarm: ALARM_ID_DD_BC_STATE1_FILL_PRESSURE_DROP_OUT_OF_RANGE - * when pressure drop is not in range during balacing chamber fill in progress. + * when pressure drop is not in range during balancing chamber fill in progress. * @details \b Alarm: ALARM_ID_DD_BC_STATE1_FILL_END_PRESSURE_OUT_OF_RANGE - * when pressure is not in range during balacing chamber fill complete. + * when pressure is not in range during balancing chamber fill complete. * @return next balancing chamber state. *************************************************************************/ static BAL_CHAMBER_EXEC_STATE_T handleBalChamberState1FillEnd( void ) { BAL_CHAMBER_EXEC_STATE_T state = BAL_CHAMBER_STATE1_FILL_END; - // On completion of cycle time, transition to next state - if ( currentBalChamberSwitchingCounter >= balChamberSwitchingPeriod ) - { - balChamberFillPressureDropCounter = 0; - balChamberFillCompleteStablePressureCounter = 0; + balChamberFillPressureDropCounter = 0; + balChamberFillCompleteStablePressureCounter = 0; - if ( TRUE != getBalChamberSwitchingOnlyStatus() ) + if ( TRUE != getBalChamberSwitchingOnlyStatus() ) + { + if ( getTestConfigStatus( TEST_CONFIG_DD_DISABLE_BC_PRESSURE_ALARMS ) != TRUE ) { - if ( getTestConfigStatus( TEST_CONFIG_DD_DISABLE_BC_PRESSURE_ALARMS ) != TRUE ) + if ( ( TRUE != isPressureDroppedDuringFill ) && ( TRUE == isFirstCycleBCSwitchingCompleted ) ) { - if ( ( TRUE != isPressureDroppedDuringFill ) && ( TRUE == isFirstCycleBCSwitchingCompleted ) ) - { - // When fill initiated, pressure is not dropped to the expected range, possible valve failures. - SET_ALARM_WITH_2_F32_DATA( ALARM_ID_DD_BC_STATE1_FILL_PRESSURE_DROP_OUT_OF_RANGE, freshDialPressure, spentDialPressure ); - } - else if ( TRUE != isPressureStabilizedDuringFill ) - { - // Alarm when switching time expired, but still pressure not in range which indicates fill is not yet completed. - SET_ALARM_WITH_2_F32_DATA( ALARM_ID_DD_BC_STATE1_FILL_END_PRESSURE_OUT_OF_RANGE, freshDialPressure, spentDialPressure ); - } - else - { - // Move to next state when pressure is in range. - state = BAL_CHAMBER_STATE2_FILL_START; - } + // When fill initiated, pressure is not dropped to the expected range, possible valve failures. + SET_ALARM_WITH_2_F32_DATA( ALARM_ID_DD_BC_STATE1_FILL_PRESSURE_DROP_OUT_OF_RANGE, freshDialPressure, spentDialPressure ); } + else if ( TRUE != isPressureStabilizedDuringFill ) + { + // Alarm when switching time expired, but still pressure not in range which indicates fill is not yet completed. + SET_ALARM_WITH_2_F32_DATA( ALARM_ID_DD_BC_STATE1_FILL_END_PRESSURE_OUT_OF_RANGE, freshDialPressure, spentDialPressure ); + } else { - // Allow to proceed next state even though pressure is not stabilized. + // Move to next state when pressure is in range. state = BAL_CHAMBER_STATE2_FILL_START; } } else { + // Allow to proceed next state even though pressure is not stabilized. state = BAL_CHAMBER_STATE2_FILL_START; } } + else + { + state = BAL_CHAMBER_STATE2_FILL_START; + } return state; } @@ -723,7 +653,7 @@ * @details \b Inputs: fresh and spent dialysate pressure * @details \b Outputs: valve states * @details \b Alarm: ALARM_ID_DD_BC_STATE2_FILL_START_PRESSURE_OUT_OF_RANGE - * when pressure is not in range during balacing chamber state 2 fill start. + * when pressure is not in range during balancing chamber state 2 fill start. * @return next balancing chamber state. *************************************************************************/ static BAL_CHAMBER_EXEC_STATE_T handleBalChamberState2FillStart( void ) @@ -734,22 +664,20 @@ isPressureStabilizedDuringFill = FALSE; isPressureDroppedDuringFill = FALSE; balChamberSWState = BAL_CHAMBER_SW_STATE2; + currentBalChamberFillCounter = 0; + isSpentFillComplete = FALSE; + lastPrevSpentDialPressure = 0.0F; + prevSpentDialPressure = 0.0F; F32 acidVolume = getF32OverrideValue( &acidDoseVolume ); F32 bicarbVolume = getF32OverrideValue( &bicarbDoseVolume ); freshDialPressure = getFilteredPressure( D18_PRES ); spentDialPressure = getFilteredPressure( D51_PRES ); - //Set Trimmer heater Target temp every BC cycle to catch up fresh dialysate temp - setHeaterTargetTemperature( D45_HEAT, getD4AverageTemperature() ); - - BOOL isFreshDialysatePressureInRange = ( freshDialPressure >= FRESH_DIAL_PRESSURE_MIN_PSIG ) && ( freshDialPressure <= FRESH_DIAL_PRESSURE_MAX_PSIG ); - BOOL isSpentDialysatePressureInRange = ( spentDialPressure >= SPENT_DIAL_PRESSURE_MIN_PSIG ) && ( spentDialPressure <= SPENT_DIAL_PRESSURE_MAX_PSIG ); - BOOL isBCSwitchOnlyStatus = ( TRUE == getBalChamberSwitchingOnlyStatus() ); - BOOL isDisableBCPressureAlarms = ( TRUE == getTestConfigStatus( TEST_CONFIG_DD_DISABLE_BC_PRESSURE_ALARMS ) ); - - // Check fresh and spent dialysate pressure in range - if ( ( TRUE == isFreshDialysatePressureInRange && TRUE == isSpentDialysatePressureInRange ) || ( TRUE == isBCSwitchOnlyStatus ) || ( TRUE == isDisableBCPressureAlarms ) ) + // Check fresh dialysate pressure in range + if ( ( ( freshDialPressure >= FRESH_DIAL_PRESSURE_MIN_PSIG ) && ( freshDialPressure <= FRESH_DIAL_PRESSURE_MAX_PSIG ) ) || + // ( ( spentDialPressure >= SPENT_DIAL_PRESSURE_MIN_PSIG ) && ( spentDialPressure <= SPENT_DIAL_PRESSURE_MAX_PSIG ) ) || + ( TRUE == getBalChamberSwitchingOnlyStatus() ) || ( TRUE == getTestConfigStatus( TEST_CONFIG_DD_DISABLE_BC_PRESSURE_ALARMS ) ) ) { // Valve control for state 2 fill valveControlForBCState2FillStart(); @@ -760,9 +688,9 @@ // Deliver dosing during generate dialysate mode if ( TRUE != getBalChamberSwitchingOnlyStatus() ) { - // start acid and bicarb pump with the expected rate, speed in mL/min - setConcentratePumpTargetSpeed( D11_PUMP, DOSING_CONCENTRATE_PUMP_SPEED, acidVolume ); - setConcentratePumpTargetSpeed( D10_PUMP, DOSING_CONCENTRATE_PUMP_SPEED, bicarbVolume ); + // start acid and bicarb pump with the expected quantity + setConcentratePumpTargetSpeed( D11_PUMP, CONCENTRATE_PUMP_MAX_SPEED, acidVolume ); + setConcentratePumpTargetSpeed( D10_PUMP, CONCENTRATE_PUMP_MAX_SPEED, bicarbVolume ); requestConcentratePumpOn( D11_PUMP ); requestConcentratePumpOn( D10_PUMP ); } @@ -777,6 +705,7 @@ SET_ALARM_WITH_2_F32_DATA( ALARM_ID_DD_BC_STATE2_FILL_START_PRESSURE_OUT_OF_RANGE, freshDialPressure, spentDialPressure ); } } + return state; } @@ -791,14 +720,12 @@ static BAL_CHAMBER_EXEC_STATE_T handleBalChamberState2ValvesClose( void ) { BAL_CHAMBER_EXEC_STATE_T state = BAL_CHAMBER_STATE2_VALVES_CLOSE; - BOOL isFreshDialysatePressureInRange = ( freshDialPressure >= FRESH_DIAL_PRESSURE_MIN_PSIG ) && ( freshDialPressure <= FRESH_DIAL_PRESSURE_MAX_PSIG ); - BOOL isSpentDialysatePressureInRange = ( spentDialPressure >= SPENT_DIAL_PRESSURE_MIN_PSIG ) && ( spentDialPressure <= SPENT_DIAL_PRESSURE_MAX_PSIG ); freshDialPressure = getFilteredPressure( D18_PRES ); spentDialPressure = getFilteredPressure( D51_PRES ); // Check fresh and spent dialysate pressure back in range to indicate fill complete. - if ( ( TRUE == isFreshDialysatePressureInRange ) && ( TRUE == isSpentDialysatePressureInRange ) ) + if ( ( freshDialPressure >= FRESH_DIAL_PRESSURE_MIN_PSIG ) && ( freshDialPressure <= FRESH_DIAL_PRESSURE_MAX_PSIG ) ) { if ( ++balChamberFillCompleteStablePressureCounter >= BAL_CHAMBER_FILL_COMPLETE_MS ) { @@ -807,13 +734,20 @@ } } + // Spent side of balancing chamber fill is complete or not + checkSpentFillComplete( spentDialPressure ); + // Check switching cycle time or pressure check for valve closure - if ( ( currentBalChamberSwitchingCounter >= balChamberValveClosePeriod ) || - ( TRUE == isPressureStabilizedDuringFill ) ) + if ( ( TRUE == isSpentFillComplete ) && ( TRUE == isPressureStabilizedDuringFill ) || ( FALSE == isFirstCycleBCSwitchingCompleted ) ) { // close the valves valveControlForBCState2FillEnd(); isBalChamberFillInProgress = FALSE; + //setDialysatePumpTargetRPM( D48_PUMP, getD48PumpSpeedForBCFill(), TRUE ); + if ( TRUE == isFirstCycleBCSwitchingCompleted ) + { + setDialysatePumpTargetRPM( D48_PUMP, getD48PumpSpeedForBCFill(), TRUE ); + } //Transition to next state state = BAL_CHAMBER_STATE2_FILL_END; @@ -838,11 +772,8 @@ { BAL_CHAMBER_EXEC_STATE_T state = BAL_CHAMBER_STATE2_FILL_END; - // On completion of cycle time, transition to next state - if ( currentBalChamberSwitchingCounter >= balChamberSwitchingPeriod ) - { - balChamberFillPressureDropCounter = 0; - balChamberFillCompleteStablePressureCounter = 0; + balChamberFillPressureDropCounter = 0; + balChamberFillCompleteStablePressureCounter = 0; // Pressure alarm check if ( TRUE != getBalChamberSwitchingOnlyStatus() ) @@ -863,12 +794,6 @@ { // Move to next state when pressure is in range. state = BAL_CHAMBER_STATE1_FILL_START; - - // Trigger pressure drop alarm after first cycle of balancing chamber switching complete - if ( FALSE == isFirstCycleBCSwitchingCompleted ) - { - isFirstCycleBCSwitchingCompleted = TRUE; - } } } else @@ -881,13 +806,162 @@ { state = BAL_CHAMBER_STATE1_FILL_START; } - } + // Trigger pressure drop alarm after first cycle of balancing chamber switching complete + if ( FALSE == isFirstCycleBCSwitchingCompleted ) + { + isFirstCycleBCSwitchingCompleted = TRUE; + } + return state; } /*********************************************************************//** * @brief + * The checkSpentFillComplete function checks the pressure difference for + * spent side fill completion and adjusts the D48 pump speed to align the + * fill timing close to the switching period time. + * @details \b Inputs: balChamberExecState + * @details \b Outputs: none + * @return the current state of balancing chamber states. + *************************************************************************/ +static void checkSpentFillComplete( F32 spentDialPressure ) +{ + F32 diffInSpentPressure = 0.0F; + U32 absDiffSpentFillCount = 0; + U32 adjustedSpeed = 0; + U32 spentDialPumpSpeed = getD48PumpSpeedForBCFill(); + U32 initialD48PumpSpeed = getCalculatedD48PumpSpeedForBCFill(); + U32 minD48Speed = initialD48PumpSpeed - ( initialD48PumpSpeed * D48_SPEED_RANGE_LIMIT ); + U32 maxD48Speed = initialD48PumpSpeed + ( initialD48PumpSpeed * D48_SPEED_RANGE_LIMIT ); + U32 d48SpeedPostRangeCheck = 0; + + // Collect the next sample of spent pressure + lastPrevSpentDialPressure = prevSpentDialPressure; + prevSpentDialPressure = spentDialPressure; + // find the pressure difference for pressure spike detection + diffInSpentPressure = fabs(spentDialPressure - lastPrevSpentDialPressure); + + if ( ( spentDialPressure >= SPENT_FILL_COMPLETE_PRES ) && ( isSpentFillComplete != TRUE ) ) + { + // Check spent fill time against the balancing chamber closing period + diffSpentFillCompleteCount = balChamberValveClosePeriod - currentBalChamberFillCounter; + absDiffSpentFillCount = abs(diffSpentFillCompleteCount); + adjustedSpeed = ( ( (F32)absDiffSpentFillCount / (F32)balChamberValveClosePeriod ) * spentDialPumpSpeed ) * D48_SPEED_ADJUST_FACTOR; + + //Not adjust the D48 pump speed if the fill counter is just 50ms difference from the closing period. + if ( absDiffSpentFillCount > SPENT_DIFF_COUNT_DEADBAND ) + { + if ( diffSpentFillCompleteCount < SPENT_DIFF_COUNT_ZERO ) + { + // Increase the D48 pump speed + spentDialPumpSpeed += adjustedSpeed; + } + else + { + //Decrease the D48 pump speed + spentDialPumpSpeed -= adjustedSpeed; + } + + d48SpeedPostRangeCheck = RANGE( spentDialPumpSpeed, minD48Speed, maxD48Speed ); + + // Update the D48 pump speed + setD48PumpSpeedForBCFill( d48SpeedPostRangeCheck ); + } + + //Update spent fill is complete + isSpentFillComplete = TRUE; + } +} + +/*********************************************************************//** + * @brief + * The getCurrentBalancingChamberExecState function returns the current state + * of the balancing chamber. + * @details \b Inputs: balChamberExecState + * @details \b Outputs: none + * @return the current state of balancing chamber states. + *************************************************************************/ +BAL_CHAMBER_EXEC_STATE_T getCurrentBalancingChamberExecState( void ) +{ + return balChamberExecState; +} + +/*********************************************************************//** + * @brief + * The getBalancingChamberFillinProgressStatus function returns the current + * balancing chamber fill in progress status. + * @details \b Inputs: isBalChamberFillInProgress + * @details \b Outputs: none + * @return the current balancing chamber fill in progress. + *************************************************************************/ +BOOL getBalancingChamberFillinProgressStatus( void ) +{ + return isBalChamberFillInProgress; +} + +/*********************************************************************//** + * @brief + * The getBalChamberSwitchingFreq function gets the balancing chamber switching + * frequency value. + * @details \b Inputs: balChamberSwitchingFreq + * @details \b Outputs: none + * @return balancing chamber switching frequency + *************************************************************************/ +F32 getBalChamberSwitchingFreq( void ) +{ + F32 result = balChamberSwitchingFreq.data; + + if ( OVERRIDE_KEY == balChamberSwitchingFreq.override ) + { + result = balChamberSwitchingFreq.ovData; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The setBalChamberSwitchingOnlyStatus function sets the balancing chamber + * switching only On/Off status. + * @details \b Inputs: balanceChamberSwitchingOnly + * @details \b Outputs: none + * @return none + *************************************************************************/ +void setBalChamberSwitchingOnlyStatus( BOOL OnOff ) +{ + balanceChamberSwitchingOnly = OnOff; +} + +/*********************************************************************//** + * @brief + * The getBalChamberSwitchingOnlyStatus function gets the balancing chamber + * switching only status. + * @details \b Inputs: balanceChamberSwitchingOnly + * @details \b Outputs: none + * @return balancing chamber switching only without any pressure validation + * and dosing delivery + *************************************************************************/ +BOOL getBalChamberSwitchingOnlyStatus( void ) +{ + return balanceChamberSwitchingOnly; +} + +/*********************************************************************//** + * @brief + * The getBalChamberSwitchingPeriod function gets the balancing chamber + * switching period. + * @details \b Inputs: balChamberSwitchingPeriod + * @details \b Outputs: none + * @return balancing chamber switching period + *************************************************************************/ +U32 getBalChamberSwitchingPeriod( void ) +{ + return balChamberSwitchingPeriod; +} + +/*********************************************************************//** + * @brief * The getBalChamberDataPublishInterval function gets the balancing chamber * data publish interval. * @details \b Inputs: balChamberDataPublishInterval @@ -925,9 +999,9 @@ data.balChamberExecState = (U32)balChamberExecState; data.balChamberSWState = (U32)balChamberSWState; data.balChamberSWFreq = getBalChamberSwitchingFreq(); - data.balChamberSwPeriod = balChamberSwitchingPeriod; - data.isBalChamberFillInProgress = isBalChamberFillInProgress; - data.currentBalChamberSwitchingCounter = currentBalChamberSwitchingCounter; + data.balChamberSwPeriod = getD48PumpSpeedForBCFill(); + data.isBalChamberFillInProgress = isSpentFillComplete; + data.currentBalChamberSwitchingCounter = currentBalChamberFillCounter; data.isPressureStabilizedDuringFill = isPressureStabilizedDuringFill; data.balChamberSWOnlyState = balanceChamberSwitchingOnly; Index: firmware/App/Controllers/BalancingChamber.h =================================================================== diff -u -rd748813399d38ef5b71d760e327e368cc82d7a38 -rff8dde1e0c78936fc2db1aa496c9dce2e498e03b --- firmware/App/Controllers/BalancingChamber.h (.../BalancingChamber.h) (revision d748813399d38ef5b71d760e327e368cc82d7a38) +++ firmware/App/Controllers/BalancingChamber.h (.../BalancingChamber.h) (revision ff8dde1e0c78936fc2db1aa496c9dce2e498e03b) @@ -7,8 +7,8 @@ * * @file BalancingChamber.h * -* @author (last) Vinayakam Mani -* @date (last) 04-Feb-2026 +* @author (last) Jashwant Gantyada +* @date (last) 05-Mar-2026 * * @author (original) Vinayakam Mani * @date (original) 28-Jan-2025 Index: firmware/App/Controllers/DialysatePumps.c =================================================================== diff -u -r830213bc6dcc1a684610caf78c79d55f2cb41e93 -rff8dde1e0c78936fc2db1aa496c9dce2e498e03b --- firmware/App/Controllers/DialysatePumps.c (.../DialysatePumps.c) (revision 830213bc6dcc1a684610caf78c79d55f2cb41e93) +++ firmware/App/Controllers/DialysatePumps.c (.../DialysatePumps.c) (revision ff8dde1e0c78936fc2db1aa496c9dce2e498e03b) @@ -30,6 +30,7 @@ #include "SafetyShutdown.h" #include "TaskGeneral.h" #include "TaskPriority.h" +#include "TestSupport.h" #include "Timers.h" #include "Utilities.h" @@ -156,6 +157,46 @@ /*********************************************************************//** * @brief + * The getD48MinPumpRPM function returns the minimum RPM limit for the D48 pump + * based on test configuration (Diener 1000 vs Diener 2000). + * @details \b Inputs: Test configuration TEST_CONFIG_DD_ENABLE_DIENER_1000_PUMP. + * @details \b Outputs: none + * @return Minimum RPM for D48 pump (134 when Diener 1000 enabled, 200 otherwise). + *************************************************************************/ +U32 getD48MinPumpRPM( void ) +{ + U32 minRpm = MIN_DIALYSATE_PUMP_RPM; + + if ( TRUE == getTestConfigStatus( TEST_CONFIG_DD_ENABLE_DIENER_1000_PUMP ) ) + { + minRpm = D48_DIENER_1000_MIN_RPM; + } + + return minRpm; +} + +/*********************************************************************//** + * @brief + * The getD48MaxPumpRPM function returns the maximum RPM limit for the D48 pump + * based on test configuration (Diener 1000 vs Diener 2000). + * @details \b Inputs: Test configuration TEST_CONFIG_DD_ENABLE_DIENER_1000_PUMP. + * @details \b Outputs: none + * @return Maximum RPM for D48 pump (2770 when Diener 1000 enabled, 2650 otherwise). + *************************************************************************/ +U32 getD48MaxPumpRPM( void ) +{ + U32 maxRpm = MAX_DIALYSATE_PUMP_RPM; + + if ( TRUE == getTestConfigStatus( TEST_CONFIG_DD_ENABLE_DIENER_1000_PUMP ) ) + { + maxRpm = D48_DIENER_1000_MAX_RPM; + } + + return maxRpm; +} + +/*********************************************************************//** + * @brief * The initDialysatePump function initializes the DialysatePumps unit. * @details \b Inputs: none * @details \b Outputs: Dialysate pump unit variables initialized. @@ -192,7 +233,14 @@ dialysatePumpMeasuredCurrentA[ pumpId ].ovInitData = 0.0F; dialysatePumpMeasuredCurrentA[ pumpId ].ovData = 0.0F; dialysatePumpMeasuredCurrentA[ pumpId ].override = OVERRIDE_RESET; - dialysatePumps[ pumpId ].currentPumpSpeed = MIN_DIALYSATE_PUMP_RPM; + if ( D48_PUMP == pumpId ) + { + dialysatePumps[ pumpId ].currentPumpSpeed = getD48MinPumpRPM(); + } + else + { + dialysatePumps[ pumpId ].currentPumpSpeed = MIN_DIALYSATE_PUMP_RPM; + } dialysatePumps[ pumpId ].prevPumpTargetSpeed = 0.0F; dialysatePumps[ pumpId ].control = DIALYSATE_PUMP_CONTROL_STOP; dialysatePumps[ pumpId ].directionErrorCount = 0; @@ -221,14 +269,14 @@ initializePIController( PI_CONTROLLER_ID_D12_PUMP, FRESH_DIAL_OPEN_LOOP_SPEED_RPM, D12_PUMP_P_COEFFICIENT, D12_PUMP_I_COEFFICIENT, MIN_DIALYSATE_PUMP_RPM, MAX_DIALYSATE_PUMP_RPM, FALSE, DIAL_PUMP_NO_FEED_FORWARD ); - // Initialize spent dialysate pump PI controller + // Initialize spent dialysate pump PI controller (use Diener 1000 range to support both pump types) initializePIController( PI_CONTROLLER_ID_D48_PUMP, SPENT_DIAL_OPEN_LOOP_SPEED_RPM, D48_PUMP_P_COEFFICIENT, D48_PUMP_I_COEFFICIENT, - MIN_DIALYSATE_PUMP_RPM, MAX_DIALYSATE_PUMP_RPM,FALSE, DIAL_PUMP_NO_FEED_FORWARD ); + D48_DIENER_1000_MIN_RPM, D48_DIENER_1000_MAX_RPM, FALSE, DIAL_PUMP_NO_FEED_FORWARD ); // Init the dialysate pump with valid PWM while motor is disabled. // when enable triggers, we dont want invlid RPM set that triggers alarm in motor controller. setFPGAD12PumpSpeed( MIN_DIALYSATE_PUMP_RPM ); - setFPGAD48PumpSpeed( MIN_DIALYSATE_PUMP_RPM ); + setFPGAD48PumpSpeed( getD48MinPumpRPM() ); #ifdef __PITEST__ for ( i = 0; i < NUM_OF_CONTROLLER_SIGNAL; i++ ) @@ -287,37 +335,50 @@ BOOL setDialysatePumpTargetRPM( DIALYSATE_PUMPS_T pumpId, U32 rpm, BOOL isOpenLoopControlEnabled ) { BOOL result = FALSE; + U32 minRPM; + U32 maxRPM; + if ( D48_PUMP == pumpId ) + { + minRPM = getD48MinPumpRPM(); + maxRPM = getD48MaxPumpRPM(); + } + else + { + minRPM = MIN_DIALYSATE_PUMP_RPM; + maxRPM = MAX_DIALYSATE_PUMP_RPM; + } + if ( pumpId < NUM_OF_DIALYSATE_PUMPS ) { - if ( ( MIN_DIALYSATE_PUMP_RPM <= rpm ) && ( rpm <= MAX_DIALYSATE_PUMP_RPM ) ) + if ( ( minRPM <= rpm ) && ( rpm <= maxRPM ) ) { pumpTargetSpeed[ pumpId ].data = rpm; } - else if ( rpm < MIN_DIALYSATE_PUMP_RPM ) + else if ( rpm < minRPM ) { - // Lets assign minimum speed to make sure, driver is not getting into fault mode - pumpTargetSpeed[ pumpId ].data = MIN_DIALYSATE_PUMP_RPM; + // Assign minimum speed so driver does not enter fault mode. + pumpTargetSpeed[ pumpId ].data = minRPM; } else { - pumpTargetSpeed[ pumpId ].data = MAX_DIALYSATE_PUMP_RPM; + pumpTargetSpeed[ pumpId ].data = maxRPM; } // Assign Open loop or close loop control isDialPumpOpenLoopEnabled[ pumpId ] = isOpenLoopControlEnabled; #ifdef __BARO_PRES_SENSOR__ - //Update Degas pump target speed + // Update degas pump target speed. calculateDegasPumpTargetPressure(); #endif - //handle target speed update when pump is running + // Handle target speed update when pump is running. if ( DIALYSATE_PUMP_CONTROL_TO_TARGET_STATE == dialysatePumps[ pumpId ].dialysatePumpState ) { F32 diffSpeed = fabs( getDialysatePumpTargetSpeed( pumpId ) - dialysatePumps[ pumpId ].prevPumpTargetSpeed ); if ( diffSpeed > PUMP_TRANS_TO_RAMP_SPEED_THRESHOLD ) { - // change to ramp state + // Change to ramp state. dialysatePumps[ pumpId ].dialysatePumpState = DIALYSATE_PUMP_RAMP_UP_STATE; } } @@ -344,10 +405,9 @@ *************************************************************************/ void signalDialysatePumpHardStop( DIALYSATE_PUMPS_T pumpId ) { - //Update control to stop the dialysate pump + // Update control to stop the dialysate pump. if ( D12_PUMP == pumpId ) { - // dialysate pump control run enable dialysatePumps[ pumpId ].control = DIALYSATE_PUMP_CONTROL_STOP; setFPGAD12PumpControl( dialysatePumps[ pumpId ].control ); setFPGAD12PumpSpeed( MIN_DIALYSATE_PUMP_RPM ); @@ -356,18 +416,25 @@ { dialysatePumps[ pumpId ].control = DIALYSATE_PUMP_CONTROL_STOP; setFPGAD48PumpControl( dialysatePumps[ pumpId ].control ); - setFPGAD48PumpSpeed( MIN_DIALYSATE_PUMP_RPM ); + setFPGAD48PumpSpeed( getD48MinPumpRPM() ); } - // Reset all the variables to stop mode - dialysatePumps[ pumpId ].currentPumpSpeed = MIN_DIALYSATE_PUMP_RPM; + // Reset all the variables to stop mode. + if ( D48_PUMP == pumpId ) + { + dialysatePumps[ pumpId ].currentPumpSpeed = getD48MinPumpRPM(); + } + else + { + dialysatePumps[ pumpId ].currentPumpSpeed = MIN_DIALYSATE_PUMP_RPM; + } pumpTargetSpeed[ pumpId ].data = 0.0F; dialysatePumps[ pumpId ].dialysatePumpState = DIALYSATE_PUMP_OFF_STATE; dialysatePumps[ pumpId ].controlTimerCounter = 0; dialysatePumps[ pumpId ].isDialPumpOn = FALSE; isDialPumpOpenLoopEnabled[ pumpId ] = FALSE; - //Reset PI Controller + // Reset PI controller. if ( D12_PUMP == pumpId ) { resetPIController( PI_CONTROLLER_ID_D12_PUMP, FRESH_DIAL_OPEN_LOOP_SPEED_RPM, DIAL_PUMP_NO_FEED_FORWARD ); @@ -660,12 +727,22 @@ { DIALYSATE_PUMP_STATE_T result = DIALYSATE_PUMP_OFF_STATE; F32 targetSpeed = getDialysatePumpTargetSpeed( pumpId ); + U32 minRPM; - if ( targetSpeed >= MIN_DIALYSATE_PUMP_RPM ) + if ( D48_PUMP == pumpId ) { + minRPM = getD48MinPumpRPM(); + } + else + { + minRPM = MIN_DIALYSATE_PUMP_RPM; + } + + if ( targetSpeed >= minRPM ) + { if ( D12_PUMP == pumpId ) { - // dialysate pump control run enable, forward run only. + // Dialysate pump control run enable, forward run only. dialysatePumps[ pumpId ].control = DIALYSATE_PUMP_CONTROL_RUN; setFPGAD12PumpControl( dialysatePumps[ pumpId ].control ); } @@ -729,6 +806,7 @@ F32 speedIncrease = 0.0F; BOOL hasTgtBeenReached = FALSE; F32 currentToTargetDiff = fabs( getDialysatePumpTargetSpeed( pumpId ) - dialysatePumps[ pumpId ].currentPumpSpeed ); + U32 minRPM; if ( currentToTargetDiff > ZERO_SPEED ) { @@ -742,7 +820,7 @@ hasTgtBeenReached = TRUE; } - // Subtract current speed when target speed is smaller + // Subtract current speed when target speed is smaller. if ( getDialysatePumpTargetSpeed( pumpId ) < dialysatePumps[ pumpId ].currentPumpSpeed ) { speedIncrease *= -1.0F; @@ -757,16 +835,25 @@ // } } - if ( dialysatePumps[ pumpId ].currentPumpSpeed >= MIN_DIALYSATE_PUMP_RPM ) + if ( D48_PUMP == pumpId ) { + minRPM = getD48MinPumpRPM(); + } + else + { + minRPM = MIN_DIALYSATE_PUMP_RPM; + } + + if ( dialysatePumps[ pumpId ].currentPumpSpeed >= minRPM ) + { if ( D12_PUMP == pumpId ) { - // Set fresh dialyate pump speed + // Set fresh dialysate pump speed. setFPGAD12PumpSpeed( (U16)dialysatePumps[ pumpId ].currentPumpSpeed ); } else { - // Set spent dialyate pump speed + // Set spent dialysate pump speed. setFPGAD48PumpSpeed( (U16)dialysatePumps[ pumpId ].currentPumpSpeed ); } } @@ -837,7 +924,23 @@ } } } + else if ( TRUE == isDialPumpOpenLoopEnabled[ pumpId ] ) + { + // Get current pump speed + dialysatePumps[ pumpId ].currentPumpSpeed = getDialysatePumpTargetSpeed( pumpId ); + if ( D12_PUMP == pumpId ) + { + //Set fresh dialyate pump speed + setFPGAD12PumpSpeed( (U16)dialysatePumps[ pumpId ].currentPumpSpeed ); + } + else + { + //Set spent dialyate pump speed + setFPGAD48PumpSpeed( (U16)dialysatePumps[ pumpId ].currentPumpSpeed ); + } + } + return state; } @@ -1162,15 +1265,29 @@ if ( (DIALYSATE_PUMPS_T)payload.pumpID < NUM_OF_DIALYSATE_PUMPS ) { - // Handle start command + U32 minRPM; + U32 maxRPM; + + if ( D48_PUMP == (DIALYSATE_PUMPS_T)payload.pumpID ) + { + minRPM = getD48MinPumpRPM(); + maxRPM = getD48MaxPumpRPM(); + } + else + { + minRPM = MIN_DIALYSATE_PUMP_RPM; + maxRPM = MAX_DIALYSATE_PUMP_RPM; + } + + // Handle start command. if ( ( TRUE == payload.startStop ) && - ( ( payload.rpm >= MIN_DIALYSATE_PUMP_RPM ) && ( payload.rpm <= MAX_DIALYSATE_PUMP_RPM ) ) ) + ( ( payload.rpm >= minRPM ) && ( payload.rpm <= maxRPM ) ) ) { setDialysatePumpTargetRPM( (DIALYSATE_PUMPS_T)payload.pumpID, payload.rpm, (BOOL)payload.pumpControl ); result = TRUE; } - //Handle stop command + // Handle stop command. if ( FALSE == payload.startStop ) { signalDialysatePumpHardStop( (DIALYSATE_PUMPS_T)payload.pumpID ); Index: firmware/App/Controllers/DialysatePumps.h =================================================================== diff -u -rd210786d6c7d75bb0b4d9e18efc40a01d85123fe -rff8dde1e0c78936fc2db1aa496c9dce2e498e03b --- firmware/App/Controllers/DialysatePumps.h (.../DialysatePumps.h) (revision d210786d6c7d75bb0b4d9e18efc40a01d85123fe) +++ firmware/App/Controllers/DialysatePumps.h (.../DialysatePumps.h) (revision ff8dde1e0c78936fc2db1aa496c9dce2e498e03b) @@ -31,13 +31,13 @@ */ // ********** public definitions ********** -#ifdef __MAXON_SPEED_UPDATE__ -#define MIN_DIALYSATE_PUMP_RPM 200 ///< Minimum RPM target for dialysate pump (though zero is allowed if turning pump off). -#else -#define MIN_DIALYSATE_PUMP_RPM 350 ///< Minimum RPM target for dialysate pump (though zero is allowed if turning pump off). -#endif -#define MAX_DIALYSATE_PUMP_RPM 2650 ///< Maximum RPM target for dialysate pump. +#define MIN_DIALYSATE_PUMP_RPM 200 ///< Minimum RPM target for D12 pump and D48 Diener 2000 pump. +#define MAX_DIALYSATE_PUMP_RPM 2650 ///< Maximum RPM target for D12 pump and D48 Diener 2000 pump. + +#define D48_DIENER_1000_MIN_RPM 134 ///< Minimum RPM for D48 Diener 1000 pump (test config enabled). +#define D48_DIENER_1000_MAX_RPM 2770 ///< Maximum RPM for D48 Diener 1000 pump (test config enabled). + #define DEGAS_PUMP_TARGET_PRES_ADJ_THRESHOLD -1.0F ///< Dialysate Pump(D12) target pressure threshold adjustment factor. #define MIN_DIALYSATE_FLOW_RATE ( 100.0F ) ///< Minimum dialysate flow rate @@ -84,6 +84,8 @@ void execDialysatePumpController( void ); SELF_TEST_STATUS_T execDialysatePumpSelfTest( void ); +U32 getD48MinPumpRPM( void ); +U32 getD48MaxPumpRPM( void ); F32 getDialysatePumpTargetSpeed( DIALYSATE_PUMPS_T pumpId ); F32 getDialysatePumpMeasuredSpeed( DIALYSATE_PUMPS_T pumpId ); F32 getDialysatePumpTargetPressure( DIALYSATE_PUMPS_T pumpId ); Index: firmware/App/DDCommon.h =================================================================== diff -u -r3bafab778c613a1abd0c421128320408db407ef8 -rff8dde1e0c78936fc2db1aa496c9dce2e498e03b --- firmware/App/DDCommon.h (.../DDCommon.h) (revision 3bafab778c613a1abd0c421128320408db407ef8) +++ firmware/App/DDCommon.h (.../DDCommon.h) (revision ff8dde1e0c78936fc2db1aa496c9dce2e498e03b) @@ -7,8 +7,8 @@ * * @file DDCommon.h * -* @author (last) Vinayakam Mani -* @date (last) 14-Nov-2025 +* @author (last) Sameer Kalliadan Poyil +* @date (last) 10-Feb-2026 * * @author (original) Vinayakam Mani * @date (original) 07-Aug-2024 @@ -25,7 +25,7 @@ #define DD_VERSION_MAJOR 0 #define DD_VERSION_MINOR 0 #define DD_VERSION_MICRO 0 -#define DD_VERSION_BUILD 36 +#define DD_VERSION_BUILD 50 // ********** development build switches ********** @@ -45,21 +45,22 @@ //Uncomment below once characterization/study completed //#define ENABLE_ALARM_2 -//Uncomment below for spent chamber filling -//#define __SPENT_CHAMBER_FILL__ 1 - //Uncomment below for bicarb chamber filling //#define __BICARB_CHAMBER_FILL__ 1 //Uncomment below for Maxon controller speed change -//#define __MAXON_SPEED_UPDATE__ 1 +#define __MAXON_SPEED_UPDATE__ 1 //Uncomment below to disable heaters debug message #define __HEATERS_DEBUG__ 1 //Uncomment below for new D48 pump changes //#define __NEW_D48_PUMP__ 1 +//Uncomment below to disable Teensy conductivity driver +#define __TEENSY_CONDUCTIVITY_DRIVER__ 1 + + #include #include #endif Index: firmware/App/Modes/ModeGenDialysate.c =================================================================== diff -u -rd748813399d38ef5b71d760e327e368cc82d7a38 -rff8dde1e0c78936fc2db1aa496c9dce2e498e03b --- firmware/App/Modes/ModeGenDialysate.c (.../ModeGenDialysate.c) (revision d748813399d38ef5b71d760e327e368cc82d7a38) +++ firmware/App/Modes/ModeGenDialysate.c (.../ModeGenDialysate.c) (revision ff8dde1e0c78936fc2db1aa496c9dce2e498e03b) @@ -33,6 +33,7 @@ #include "TaskGeneral.h" #include "TDInterface.h" #include "Temperature.h" +#include "TestSupport.h" #include "Timers.h" #include "Ultrafiltration.h" #include "Valves.h" @@ -62,7 +63,13 @@ #define LOW_DIAL_FLOW_RATE 150.0F ///< Dialysate flow rate lesser than 150 considered to be low Qds. #define ZERO_DIAL_FLOW_RATE 0.0F ///< Zero dialysate flow rate #define SPENT_CHAMBER_FILL_MAX_COUNT 10 ///< Total number of spent chamber fill allowed. +#define BICARB_CHAMBER_FILL_TIMEOUT ( 1 * MS_PER_SECOND ) ///< Bicarb chamber fill timeout. +#define PUMP_SPEED_SLOPE_FACTOR_DIENER_2000 1.24F ///< D48 Diener 2000 pump speed slope (y = 1.24x + 30). +#define PUMP_SPEED_INTERCEPT_FACTOR_DIENER_2000 30.0F ///< D48 Diener 2000 pump speed intercept. +#define PUMP_SPEED_SLOPE_FACTOR_DIENER_1000 2.869F ///< D48 Diener 1000 pump speed slope (y = 2.869x + 25.956). +#define PUMP_SPEED_INTERCEPT_FACTOR_DIENER_1000 25.956F ///< D48 Diener 1000 pump speed intercept. #define BICARB_CHAMBER_FILL_TIMEOUT ( 1 * MS_PER_SECOND ) ///< Bicarb chamber fill timeout. + //Testing #define DELAY_BC_SWITCHING_AT_START_UP ( 10 * MS_PER_SECOND ) ///< Provide a balancing chamber switching start up delay to stabilize pump speed etc., /// Payload record structure for Gen dialysate execution state set request @@ -88,6 +95,7 @@ static U32 bypassStateDelayStartTimeMS; ///< Delay balancing chamber switching for a second to preapre pump steady state. static BOOL delayBypassStateFlag; ///< To indicate change in treatment parameters static F32 dialysateToDialyzerFlowRate; ///< Current dialysate to dialyzer flow rate (ml/min) +static U32 d48PumpSpeed; ///< Initial D48 pump speed based on the Qd. // ********** private function prototypes ********** @@ -137,6 +145,7 @@ bicarbFillStartTimeMS = 0; pendingSpentChamberFill = FALSE; pendingBicarbChamberFill = FALSE; + d48PumpSpeed = getD48MinPumpRPM(); //Testing bypassStateDelayStartTimeMS = 0; delayBypassStateFlag = TRUE; @@ -160,8 +169,13 @@ *************************************************************************/ U32 transitionToGenDialysateMode( void ) { + U32 initialD48PumpSpeed = 0U; + initGenDialysateMode(); setCurrentSubState( NO_SUB_STATE ); + //calculateD48PumpSpeedForBCFill(); + initialD48PumpSpeed = getCalculatedD48PumpSpeedForBCFill(); + setD48PumpSpeedForBCFill( initialD48PumpSpeed ); transitionToUltrafiltration(); return genDialysateState; @@ -243,7 +257,7 @@ //setDialysatePumpTargetRPM( D12_PUMP, FRESH_DIAL_PUMP_INITIAL_RPM, FALSE ); setDialysatePumpTargetRPM( D12_PUMP, FRESH_DIAL_PUMP_INITIAL_RPM, TRUE ); //setDialysatePumpTargetRPM( D48_PUMP, SPENT_DIAL_PUMP_INITIAL_RPM, FALSE ); - setDialysatePumpTargetRPM( D48_PUMP, SPENT_DIAL_PUMP_INITIAL_RPM, TRUE ); + setDialysatePumpTargetRPM( D48_PUMP, d48PumpSpeed, TRUE ); //Rinse pump On setRinsePumpState( RINSE_PUMP_STATE_ON ); @@ -279,7 +293,7 @@ //setDialysatePumpTargetRPM( D12_PUMP, FRESH_DIAL_PUMP_INITIAL_RPM, FALSE ); setDialysatePumpTargetRPM( D12_PUMP, FRESH_DIAL_PUMP_INITIAL_RPM, TRUE ); //setDialysatePumpTargetRPM( D48_PUMP, SPENT_DIAL_PUMP_INITIAL_RPM, FALSE ); - setDialysatePumpTargetRPM( D48_PUMP, SPENT_DIAL_PUMP_INITIAL_RPM, TRUE ); + setDialysatePumpTargetRPM( D48_PUMP, d48PumpSpeed, TRUE ); //Rinse pump On setRinsePumpState( RINSE_PUMP_STATE_ON ); @@ -372,7 +386,7 @@ setRinsePumpState( RINSE_PUMP_STATE_ON ); setDialysatePumpTargetRPM( D12_PUMP, FRESH_DIAL_PUMP_INITIAL_RPM, TRUE ); - setDialysatePumpTargetRPM( D48_PUMP, SPENT_DIAL_PUMP_INITIAL_RPM, TRUE ); + setDialysatePumpTargetRPM( D48_PUMP, d48PumpSpeed, TRUE ); break; case DD_GEND_DIALYSATE_DELIVERY_PAUSE: @@ -544,6 +558,31 @@ /*********************************************************************//** * @brief + * The getD48PumpSpeedForBCFill function returns the calculated D48 pump speed + * @details \b Inputs: d48PumpSpeed + * @details \b Outputs: none + * @return D48 pump speed. + *************************************************************************/ +U32 getD48PumpSpeedForBCFill( void ) +{ + return d48PumpSpeed; +} + +/*********************************************************************//** + * @brief + * The setD48PumpSpeedForBCFill function sets the updated D48 pump speed. + * @details \b Inputs: none + * @details \b Outputs: d48PumpSpeed + * @param pumpSpeed Dialysate pump speed + * @return none. + *************************************************************************/ +void setD48PumpSpeedForBCFill( U32 pumpSpeed ) +{ + d48PumpSpeed = pumpSpeed; +} + +/*********************************************************************//** + * @brief * The monitorChamberLevelStatus function checks the spent chamber and bicarb * chamber level status and updates the corrosponding flags. * @details \b Inputs: Spent and bicarb chamber levels. @@ -582,7 +621,7 @@ if ( dialFlowrate >= LOW_DIAL_FLOW_RATE ) { - // linear releationship seen against high dialysate flowrate Vs DeltaTemp + // linear relationship seen against high dialysate flowrate Vs DeltaTemp // deltaTemp = (-0.0029 * Qd) + 3.47 deltaTemp = ( LINEAR_SLOPE_FACTOR * dialFlowrate ) + LINEAR_INTERCEPT_FACTOR; } @@ -599,6 +638,34 @@ /*********************************************************************//** * @brief + * The getCalculatedD48PumpSpeedForBCFill function returns the D48 pump speed + * calculated from dialysate flow rate (Qd) for continuous delivery. + * @details \b Inputs: Qd from TD, test config TEST_CONFIG_DD_ENABLE_DIENER_1000_PUMP. + * @details \b Outputs: none + * @return Calculated D48 pump speed in RPM (Diener 1000 or 2000 formula per test config). + *************************************************************************/ +U32 getCalculatedD48PumpSpeedForBCFill( void ) +{ + F32 dialFlowrate = getTDDialysateFlowrate(); + F32 slope; + F32 intercept; + + if ( TRUE == getTestConfigStatus( TEST_CONFIG_DD_ENABLE_DIENER_1000_PUMP ) ) + { + slope = PUMP_SPEED_SLOPE_FACTOR_DIENER_1000; + intercept = PUMP_SPEED_INTERCEPT_FACTOR_DIENER_1000; + } + else + { + slope = PUMP_SPEED_SLOPE_FACTOR_DIENER_2000; + intercept = PUMP_SPEED_INTERCEPT_FACTOR_DIENER_2000; + } + + return (U32)( ( slope * dialFlowrate ) + intercept ); +} + +/*********************************************************************//** + * @brief * The handleGenDDialysateIsolatedUFState function performs the * Isolated ultrafiltration operations. * @details \b Inputs: none. @@ -886,6 +953,8 @@ *************************************************************************/ void updateTreatmentSettings( void ) { + F32 initialPumpSpeed = 0.0F; + // Update any dynamic treatment parameter changes if ( TRUE == isTreatmentParamUpdated ) { @@ -904,6 +973,11 @@ //TODO: update others parameters setting as needed. signalUFRateUpdate(); + //Update D48 pump speed + //calculateD48PumpSpeedForBCFill(); + initialPumpSpeed = getCalculatedD48PumpSpeedForBCFill(); + setD48PumpSpeedForBCFill( initialPumpSpeed ); + //reset the flag isTreatmentParamUpdated = FALSE; } Index: firmware/App/Modes/ModeGenDialysate.h =================================================================== diff -u -r3bafab778c613a1abd0c421128320408db407ef8 -rff8dde1e0c78936fc2db1aa496c9dce2e498e03b --- firmware/App/Modes/ModeGenDialysate.h (.../ModeGenDialysate.h) (revision 3bafab778c613a1abd0c421128320408db407ef8) +++ firmware/App/Modes/ModeGenDialysate.h (.../ModeGenDialysate.h) (revision ff8dde1e0c78936fc2db1aa496c9dce2e498e03b) @@ -7,8 +7,8 @@ * * @file ModeGenDialysate.h * -* @author (last) Varshini Nagabooshanam -* @date (last) 12-Jan-2026 +* @author (last) Sameer Kalliadan Poyil +* @date (last) 03-Feb-2026 * * @author (original) Vinayakam Mani * @date (original) 06-Nov-2024 @@ -33,6 +33,7 @@ #define FRESH_DIAL_PUMP_INITIAL_RPM 2500 ///< Nominal RPM target for fresh dialysate pump to maintain required pressure. #define SPENT_DIAL_PUMP_INITIAL_RPM 2300 ///< Nominal RPM target for spent dialysate pump to maintain required pressure. +#define DIAL_PUMP_DRAIN_RPM 1000 ///< Nominal RPM target for dialysate pump to drain the dry bicart. #define SPENT_DIAL_PUMP_FILL_RPM 200 ///< Nominal RPM target for spent chamber fill operations. /// Generate dialysate mode data structure