Index: firmware/App/Controllers/BalancingChamber.c =================================================================== diff -u -r095866efd8f10bdead490578e7584051d79292f0 -rc479bd8fae14f3bd7dc8574bbb3dc6c5e7f98588 --- firmware/App/Controllers/BalancingChamber.c (.../BalancingChamber.c) (revision 095866efd8f10bdead490578e7584051d79292f0) +++ firmware/App/Controllers/BalancingChamber.c (.../BalancingChamber.c) (revision c479bd8fae14f3bd7dc8574bbb3dc6c5e7f98588) @@ -7,14 +7,16 @@ * * @file BalancingChamber.c * -* @author (last) Santhosh Reddy -* @date (last) 06-Jul-2026 +* @author (last) Jashwant Gantyada +* @date (last) 29-Jul-2026 * * @author (original) Vinayakam Mani * @date (original) 28-Jan-2025 * ***************************************************************************/ +#include // For sqrtf + #include "BalancingChamber.h" #include "Conductivity.h" #include "ConcentratePumps.h" @@ -28,7 +30,6 @@ #include "MixingControl.h" #include "ModeGenDialysate.h" #include "ModeStandby.h" -#include "OperationModes.h" #include "Pressure.h" #include "TaskGeneral.h" #include "TDInterface.h" @@ -45,24 +46,29 @@ #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 fill complete pressure (PSI) for Diener 2000 pump. -#define SPENT_FILL_COMPLETE_PRES_QD_MID_PSIG 10.0F ///< Spent fill complete pressure (PSI) when Diener 1000 and 200 < Qd <= 400. -#define SPENT_FILL_COMPLETE_PRES_QD_HIGH_PSIG 20.0F ///< Spent fill complete pressure (PSI) when Diener 1000 and Qd > 400. -#define SPENT_FILL_COMPLETE_QD_SLOPE_MAX_MLPM 300.0F ///< For Qd <= 300 mL/min, spent fill completion uses pressure-rise slope detection. +#define BAL_CHAMBER_FILL_COMPLETE_MS ( 100 / TASK_GENERAL_INTERVAL ) ///< Time (ms/tasktime) to confirm the balancing chamber fill completed and pressure is within range +#define SPENT_FILL_COMPLETE_QD_LOW_RISE_MAX_MLPM 300.0F ///< For Qd <= 300 mL/min (Diener 1000). #define SPENT_FILL_COMPLETE_DP_RISE_PSIG 0.5F ///< Minimum 100 ms spent pressure rise (current minus n-2) to count as a rise hit at low Qd. -#define SPENT_FILL_SLOPE_SPENT_PRESSURE_HIGH_PSIG 2.0F ///< Above this absolute spent pressure, only one rise hit is required. +#define SPENT_FILL_COMPLETE_DP_RISE_HIGH_QD_PSIG 3.0F ///< Minimum spent pressure rise for Diener 2000 or Qd > 300 mL/min. +#define SPENT_FILL_SLOPE_SPENT_PRESSURE_HIGH_PSIG 2.0F ///< Above this spent pressure rise, only one rise hit is required. #define SPENT_FILL_SLOPE_MAX_RISE_HITS 2 ///< Rise hits required when Qd <= 150 in the low spent-pressure band. #define SPENT_FILL_DETECT_COUNT_TOL_PCT 1.0F ///< Allowed detection timing tolerance as a fraction of expected BC switching count. -#define QD_THRESHOLD_HIGH_MLPM 400.0F ///< Qd threshold (mL/min) below which mid pressure limit applies. #define SPENT_DIFF_COUNT_ZERO 0 ///< Zero count difference for spent side fill comparing 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 +#define NUMBER_OF_CYCLES_BASED_ON_FLOWRATE 4 ///< NUmber of Balancing Chamber cycles required to update the new Qd rate. #define BAL_CHAMBER_FILL_TIMEOUT_FACTOR 3.0F ///< Balancing Chamber fill timeout factor (300% of observed fill count) #define COMP_SLOPE -0.000376F ///< Balancing chamber temperature compensation slope factor #define COMP_INTERCEPT 1.007269F ///< Balancing chamber temperature compensation intercept factor +/// Bicarb (D10) pump speed from instantaneous mix volume: speed = 240 - sqrt(230400 - 96000*v)/2 +#define BC_BICARB_SPEED_OFFSET_MLPM 240.0F ///< Bicarb speed formula offset (mL/min). +#define BC_BICARB_SPEED_DISC_CONST 230400.0F ///< Bicarb speed formula discriminant constant. +#define BC_BICARB_SPEED_DISC_VOL_COEF 96000.0F ///< Bicarb speed formula volume coefficient. +#define BC_ACID_SPEED_OFFSET_MLPM 160.0F ///< Acid speed formula offset (mL/min). +#define BC_ACID_SPEED_DISC_CONSTANT 102400.0F ///< Acid speed formula discriminant constant. +#define BC_ACID_SPEED_DISC_VOL_COEF 96000.0F ///< Acid speed formula volume coefficient. /// Payload record structure for balancing chamber switch only request typedef struct @@ -99,6 +105,7 @@ static U32 balChamberFillTimeoutCount; ///< Timeout count (in task interval) to detect BC fill timeout. static S32 diffSpentFillCompleteCount; ///< Difference between spent target fill to actual fill count static BOOL isSpentFillComplete; ///< Flag indicating spent side fill complete. +static BOOL isFreshSideValvesClosed; ///< Flag indicating fresh-side BC valves are closed while spent side may still be filling. static F32 lastPrevSpentDialPressure; ///< Low-Qd slope: spent pressure last sample of dosing (tick before VALVES_CLOSE); then n-2 in VC. static F32 prevSpentDialPressure; ///< Spent pressure previous sample (n-1) during VALVES_CLOSE for low-Qd slope detection. static U32 spentFillRiseHitCount; ///< Low-Qd slope: counted rise hits (only incremented when in timing window). @@ -112,7 +119,7 @@ static F32 balancingError; ///< Balancing error that has been calculated during balancing chamber switching. //TODO: remove later once level sensor working -static U32 bicarbChamberPeriodicFillCounter; +static U32 bicarbChamberPeriodicFillCounter; // Balancing chamber state change tracker static BAL_CHAMBER_EXEC_STATE_T prevBalChamberState; ///< Balancing chamber Previous State tracking variable @@ -129,11 +136,17 @@ static void publishBalChamberData( void ); static U32 getBalChamberDataPublishInterval( void ); static void checkSpentFillComplete( F32 spentDialPressure ); -static BOOL isSpentFillCompleteByPressure( F32 spentDialPressure, F32 qdMlpm, F32 spentFillCompletePresPsig ); +static BOOL isSpentFillCompleteByPressure( F32 spentDialPressure, F32 qdMlpm ); static void applyBalChamberSwitchingPeriod( F32 tdDialysateFlowrate ); static BOOL isBalChamberTimeBasedSwitching( void ); static void scheduleFirstCycleRelaxAfterQdApply( void ); +static void valveControlForBCState1FreshSideClose( void ); +static void valveControlForBCState2FreshSideClose( void ); +static void closeFreshSideValvesWhenFillComplete( void ); static void calculateBalancingChamberError( void ); +static F32 getBicarbConcentratePumpSpeedMlMin( F32 bicarbVolumeMl ); +static F32 getAcidConcentratePumpSpeedMlMin( F32 acidVolumeMl ); +static void startBalChamberConcPumpDosing( F32 acidVolume, F32 bicarbVolume ); /*********************************************************************//** * @brief @@ -186,6 +199,7 @@ spentFillRiseHitCount = 0; spentFillRiseMissCounter = 0; isSpentFillComplete = FALSE; + isFreshSideValvesClosed = FALSE; isBalChamberSwitchingActive = FALSE; isBalChamberSwitchingOnRequested = FALSE; isBalChamberSwitchingOffRequested = FALSE; @@ -200,6 +214,97 @@ /*********************************************************************//** * @brief + * The getAcidConcentratePumpSpeedMlMin function computes D11 pump speed + * from the instantaneous acid mix volume: + * speed = 160 - sqrt(102400 - 96000*v) / 2 + * Result is clamped to [CONCENTRATE_PUMP_MIN_SPEED, CONCENTRATE_PUMP_MAX_SPEED]. + * @details \b Inputs: acidVolumeMl + * @details \b Outputs: none + * @param acidVolumeMl Instantaneous acid mix volume (mL) + * @return Acid concentrate pump speed (mL/min) + *************************************************************************/ +static F32 getAcidConcentratePumpSpeedMlMin( F32 acidVolumeMl ) +{ + F32 discriminant = BC_ACID_SPEED_DISC_CONSTANT - ( BC_ACID_SPEED_DISC_VOL_COEF * acidVolumeMl ); + F32 result = 0.0F; + + if ( discriminant < 0.0F ) + { + discriminant = 0.0F; + } + + result = BC_ACID_SPEED_OFFSET_MLPM - ( sqrtf( discriminant ) / 2.0F ); + + if ( result > CONCENTRATE_PUMP_MAX_SPEED ) + { + result = CONCENTRATE_PUMP_MAX_SPEED; + } + else if ( result < 0.0F ) + { + result = CONCENTRATE_PUMP_MIN_SPEED; + } + + return result; +} +/*********************************************************************//** + * @brief + * The getBicarbConcentratePumpSpeedMlMin function computes D10 pump speed + * from the instantaneous bicarb mix volume: + * speed = 240 - sqrt(230400 - 96000*v) / 2 + * Result is clamped to [CONCENTRATE_PUMP_MIN_SPEED, CONCENTRATE_PUMP_MAX_SPEED]. + * @details \b Inputs: bicarbVolumeMl + * @details \b Outputs: none + * @param bicarbVolumeMl Instantaneous bicarb mix volume (mL) + * @return Bicarb concentrate pump speed (mL/min) + *************************************************************************/ +static F32 getBicarbConcentratePumpSpeedMlMin( F32 bicarbVolumeMl ) +{ + F32 discriminant = BC_BICARB_SPEED_DISC_CONST - ( BC_BICARB_SPEED_DISC_VOL_COEF * bicarbVolumeMl ); + F32 result = 0.0F; + + if ( discriminant < 0.0F ) + { + discriminant = 0.0F; + } + + result = BC_BICARB_SPEED_OFFSET_MLPM - ( sqrtf( discriminant ) / 2.0F ); + + if ( result > CONCENTRATE_PUMP_MAX_SPEED ) + { + result = CONCENTRATE_PUMP_MAX_SPEED; + } + else if ( result < 0.0F ) + { + result = CONCENTRATE_PUMP_MIN_SPEED; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The startBalChamberConcPumpDosing function starts acid and bicarb dosing. + * Acid (D11) runs at fixed 100 mL/min. Bicarb (D10) speed is computed from + * the instantaneous bicarb mix volume on every BC dose start. + * @details \b Inputs: acidVolume, bicarbVolume + * @details \b Outputs: concentrate pumps + * @param acidVolume Acid dose volume (mL) + * @param bicarbVolume Bicarb dose volume (mL) + * @return none + *************************************************************************/ +static void startBalChamberConcPumpDosing( F32 acidVolume, F32 bicarbVolume ) +{ + F32 acidSpeed = getAcidConcentratePumpSpeedMlMin( acidVolume ); + F32 bicarbSpeed = getBicarbConcentratePumpSpeedMlMin( bicarbVolume ); + + setConcentratePumpTargetSpeed( D11_PUMP, acidSpeed, acidVolume ); + setConcentratePumpTargetSpeed( D10_PUMP, bicarbSpeed, bicarbVolume ); + requestConcentratePumpOn( D11_PUMP ); + requestConcentratePumpOn( D10_PUMP ); +} + +/*********************************************************************//** + * @brief * The transitionToBalChamberFill function prepares for transition to * balancing chamber fill/switching operations. * @details \b Inputs: none @@ -320,6 +425,7 @@ spentFillRiseHitCount = 0; spentFillRiseMissCounter = 0; isSpentFillComplete = FALSE; + isFreshSideValvesClosed = FALSE; spentDialPressure = getFilteredPressure( D51_PRES ); lastPrevSpentDialPressure = spentDialPressure; prevSpentDialPressure = spentDialPressure; @@ -343,7 +449,7 @@ *************************************************************************/ static void scheduleFirstCycleRelaxAfterQdApply( void ) { - bcSwitchingBasedOnClosedPeriodCounter = 2; + bcSwitchingBasedOnClosedPeriodCounter = NUMBER_OF_CYCLES_BASED_ON_FLOWRATE; } /*********************************************************************//** @@ -358,8 +464,10 @@ static BOOL isBalChamberTimeBasedSwitching( void ) { BOOL state = FALSE; - state = ( getD48PumpSpeed() <= BAL_CHAMBER_TIME_BASED_D48_SPEED_RPM ) ? TRUE : FALSE; + F32 dialFlowRate = getBalChamberActiveDialysateFlowrate(); + state = ( dialFlowRate <= BAL_CHAMBER_OPEN_LOOP_FLOW_RATE_BAND_4 )? TRUE : FALSE; + return state; } @@ -640,6 +748,82 @@ /*********************************************************************//** * @brief + * The valveControlForBCSpentSideOnlyOpenState function opens the spent side + * balancing chamber valves and closes the fresh side valves. + * @details \b Inputs: none + * @details \b Outputs: valve states + * @return none. + *************************************************************************/ +void valveControlForBCSpentSideOnlyOpenState( void ) +{ + // Open spent side valves D23,D24,D25,D26 + setValveState( D23_VALV, VALVE_STATE_OPEN ); + setValveState( D25_VALV, VALVE_STATE_OPEN ); + setValveState( D24_VALV, VALVE_STATE_OPEN ); + setValveState( D26_VALV, VALVE_STATE_OPEN ); + + // Close fresh balancing chamber valves D19,D20,D21,D22 + setValveState( D19_VALV, VALVE_STATE_CLOSED ); + setValveState( D20_VALV, VALVE_STATE_CLOSED ); + setValveState( D21_VALV, VALVE_STATE_CLOSED ); + setValveState( D22_VALV, VALVE_STATE_CLOSED ); +} + +/*********************************************************************//** + * @brief + * The valveControlForBCState1FreshSideClose function closes only the fresh-side + * balancing chamber valves opened during state 1 fill. + * @details \b Inputs: none + * @details \b Outputs: valve states + * @return none. + *************************************************************************/ +static void valveControlForBCState1FreshSideClose( void ) +{ + setValveState( D19_VALV, VALVE_STATE_CLOSED ); + setValveState( D24_VALV, VALVE_STATE_CLOSED ); +} + +/*********************************************************************//** + * @brief + * The valveControlForBCState2FreshSideClose function closes only the fresh-side + * balancing chamber valves opened during state 2 fill. + * @details \b Inputs: none + * @details \b Outputs: valve states + * @return none. + *************************************************************************/ +static void valveControlForBCState2FreshSideClose( void ) +{ + setValveState( D26_VALV, VALVE_STATE_CLOSED ); + setValveState( D21_VALV, VALVE_STATE_CLOSED ); +} + +/*********************************************************************//** + * @brief + * The closeFreshSideValvesWhenFillComplete function closes the fresh-side BC + * valves once fresh fill is detected, without waiting for spent fill. + * @details \b Inputs: balChamberSWState, isPressureStabilizedDuringFill, isFreshSideValvesClosed + * @details \b Outputs: isFreshSideValvesClosed, valve states + * @return none. + *************************************************************************/ +static void closeFreshSideValvesWhenFillComplete( void ) +{ + if ( ( TRUE == isPressureStabilizedDuringFill ) && ( FALSE == isFreshSideValvesClosed ) ) + { + if ( BAL_CHAMBER_SW_STATE1 == balChamberSWState ) + { + valveControlForBCState1FreshSideClose(); + } + else + { + valveControlForBCState2FreshSideClose(); + } + + isFreshSideValvesClosed = TRUE; + } +} + +/*********************************************************************//** + * @brief * The handleBalChamberState1FillStart function handles the balancing chamber * state 1 fill and time to fill chamber counter being updated. * @details \b Inputs: Pressure @@ -665,6 +849,7 @@ spentFillRiseHitCount = 0; spentFillRiseMissCounter = 0; isSpentFillComplete = FALSE; + isFreshSideValvesClosed = FALSE; if ( FALSE == getTestConfigStatus( TEST_CONFIG_DD_DISABLE_DRY_BICARB ) ) { @@ -704,11 +889,7 @@ // Deliver dosing during generate dialysate mode if ( TRUE != getBalChamberSwitchingOnlyStatus() ) { - // start acid and bicarb pump with the expected quantity - setConcentratePumpTargetSpeed( D11_PUMP, DOSING_CONCENTRATE_PUMP_SPEED, acidVolume ); - setConcentratePumpTargetSpeed( D10_PUMP, DOSING_CONCENTRATE_PUMP_SPEED, bicarbVolume ); - requestConcentratePumpOn( D11_PUMP ); - requestConcentratePumpOn( D10_PUMP ); + startBalChamberConcPumpDosing( acidVolume, bicarbVolume ); } state = BAL_CHAMBER_STATE1_BICARB_ACID_DOSING_CNTRL; @@ -795,7 +976,6 @@ static BAL_CHAMBER_EXEC_STATE_T handleBalChamberState1FillDetectComplete( void ) { BAL_CHAMBER_EXEC_STATE_T state = BAL_CHAMBER_STATE1_FILL_DETECT_COMPLETE; - BOOL isBothFillsComplete = FALSE; BOOL isFirstCycleNotDone = FALSE; BOOL isFillCompleteOrFirstCycle = FALSE; @@ -807,17 +987,14 @@ { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_BC_FILL_TIMEOUT_FAULT, currentBalChamberFillCounter, balChamberFillTimeoutCount ); + SEND_EVENT_WITH_2_U32_DATA( DD_EVENT_BC_FILL_TIMEOUT, isPressureStabilizedDuringFill, isSpentFillComplete ); + // Move to the idle state state = BAL_CHAMBER_STATE_IDLE; } - // If the dry bicarbonate is enabled then we ignore looking at d18 pressure to be in range. - if ( ( FALSE == getTestConfigStatus( TEST_CONFIG_DD_DISABLE_DRY_BICARB ) ) && ( TRUE == getBicarbChamberFillRequested() ) ) - { - isPressureStabilizedDuringFill = TRUE; - } // Check fresh dialysate pressure back in range to indicate fresh fill complete. - else if ( ( freshDialPressure >= FRESH_DIAL_PRESSURE_MIN_PSIG ) && ( freshDialPressure <= FRESH_DIAL_PRESSURE_MAX_PSIG ) ) + if ( ( freshDialPressure >= FRESH_DIAL_PRESSURE_MIN_PSIG ) && ( freshDialPressure <= FRESH_DIAL_PRESSURE_MAX_PSIG ) ) { if ( ++balChamberFillCompleteStablePressureCounter >= BAL_CHAMBER_FILL_COMPLETE_MS ) { @@ -826,16 +1003,19 @@ } } + // Close fresh side valves only upon fresh fill completion by detecting pressure rise. + closeFreshSideValvesWhenFillComplete(); + + // Check Spent side fill complete checkSpentFillComplete( spentDialPressure ); - // Check both spent and fresh side fill is complete - isBothFillsComplete = ( ( TRUE == isSpentFillComplete ) && ( TRUE == isPressureStabilizedDuringFill ) ) ? TRUE : FALSE; isFirstCycleNotDone = ( ( FALSE == isFirstCycleBCSwitchingCompleted ) && ( currentBalChamberSwitchingCounter >= balChamberValveClosePeriod ) ) ? TRUE : FALSE; if ( FALSE == getBalChamberSwitchingOnlyStatus() ) { - isFillCompleteOrFirstCycle = ( ( TRUE == isBothFillsComplete ) || ( TRUE == isFirstCycleNotDone ) ) ? TRUE : FALSE; + isFillCompleteOrFirstCycle = ( ( ( TRUE == isSpentFillComplete ) && ( TRUE == isFreshSideValvesClosed ) ) || + ( TRUE == isFirstCycleNotDone ) ) ? TRUE : FALSE; } if ( ( TRUE == isFillCompleteOrFirstCycle ) || @@ -983,6 +1163,7 @@ spentFillRiseHitCount = 0; spentFillRiseMissCounter = 0; isSpentFillComplete = FALSE; + isFreshSideValvesClosed = FALSE; if ( FALSE == getTestConfigStatus( TEST_CONFIG_DD_DISABLE_DRY_BICARB ) ) { @@ -1021,11 +1202,7 @@ // Deliver dosing during generate dialysate mode if ( TRUE != getBalChamberSwitchingOnlyStatus() ) { - // start acid and bicarb pump with the expected quantity - setConcentratePumpTargetSpeed( D11_PUMP, DOSING_CONCENTRATE_PUMP_SPEED, acidVolume ); - setConcentratePumpTargetSpeed( D10_PUMP, DOSING_CONCENTRATE_PUMP_SPEED, bicarbVolume ); - requestConcentratePumpOn( D11_PUMP ); - requestConcentratePumpOn( D10_PUMP ); + startBalChamberConcPumpDosing( acidVolume, bicarbVolume ); } state = BAL_CHAMBER_STATE2_BICARB_ACID_DOSING_CNTRL; @@ -1056,38 +1233,24 @@ static BAL_CHAMBER_EXEC_STATE_T handleBalChamberState2FillDetectComplete( void ) { BAL_CHAMBER_EXEC_STATE_T state = BAL_CHAMBER_STATE2_FILL_DETECT_COMPLETE; - BOOL isBothFillsComplete = FALSE; BOOL isFirstCycleNotDone = FALSE; BOOL isFillCompleteOrFirstCycle = FALSE; freshDialPressure = getFilteredPressure( D18_PRES ); spentDialPressure = getFilteredPressure( D51_PRES ); - // If the dry bicarbonate is enabled then we ignore looking at d18 pressure to be in range. - if ( ( FALSE == getTestConfigStatus( TEST_CONFIG_DD_DISABLE_DRY_BICARB ) ) && ( TRUE == getBicarbChamberFillRequested() ) ) - { - isPressureStabilizedDuringFill = TRUE; - } - // Check fresh dialysate pressure back in range to indicate fresh fill complete. - else if ( ( freshDialPressure >= FRESH_DIAL_PRESSURE_MIN_PSIG ) && ( freshDialPressure <= FRESH_DIAL_PRESSURE_MAX_PSIG ) ) - { - if ( ++balChamberFillCompleteStablePressureCounter >= BAL_CHAMBER_FILL_COMPLETE_MS ) - { - // stabilized pressure indicating fresh side fill is complete - isPressureStabilizedDuringFill = TRUE; - } - } - // After the first BC cycle, fault if fill never completes at the Qd-based timeout. if ( ( currentBalChamberFillCounter > balChamberFillTimeoutCount ) && ( TRUE != getTestConfigStatus( TEST_CONFIG_DD_DISABLE_BC_PRESSURE_ALARMS ) ) ) { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_BC_FILL_TIMEOUT_FAULT, currentBalChamberFillCounter, balChamberFillTimeoutCount ); + SEND_EVENT_WITH_2_U32_DATA( DD_EVENT_BC_FILL_TIMEOUT, isPressureStabilizedDuringFill, isSpentFillComplete ); + // Move to the idle state state = BAL_CHAMBER_STATE_IDLE; } - // Check fresh dialysate pressure back in range to indicate fill complete. + // 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 ) @@ -1097,16 +1260,19 @@ } } + // Close fresh side valves only upon fresh fill completion by detecting pressure rise. + closeFreshSideValvesWhenFillComplete(); + + // Check Spent side fill complete checkSpentFillComplete( spentDialPressure ); - // Check switching cycle time or pressure check for valve closure - isBothFillsComplete = ( ( TRUE == isSpentFillComplete ) && ( TRUE == isPressureStabilizedDuringFill ) ) ? TRUE : FALSE; isFirstCycleNotDone = ( ( FALSE == isFirstCycleBCSwitchingCompleted ) && ( currentBalChamberSwitchingCounter >= balChamberValveClosePeriod ) ) ? TRUE : FALSE; if ( FALSE == getBalChamberSwitchingOnlyStatus() ) { - isFillCompleteOrFirstCycle = ( ( TRUE == isBothFillsComplete ) || ( TRUE == isFirstCycleNotDone ) ) ? TRUE : FALSE; + isFillCompleteOrFirstCycle = ( ( ( TRUE == isSpentFillComplete ) && ( TRUE == isFreshSideValvesClosed ) ) || + ( TRUE == isFirstCycleNotDone ) ) ? TRUE : FALSE; } if ( ( TRUE == isFillCompleteOrFirstCycle ) || @@ -1284,73 +1450,65 @@ * @brief * The isSpentFillCompleteByPressure function evaluates whether the spent side of the * balancing chamber fill is complete from D51 pressure. - * @details \b Inputs: spentDialPressure, qdMlpm, spentFillCompletePresPsig, valve-close period, - * fill counter, Diener pump test config, and D51 slope history for low-Qd slope mode. + * @details \b Inputs: spentDialPressure, qdMlpm, valve-close period, fill counter, + * Diener pump test config, and D51 slope history. * @details \b Outputs: spentFillRiseHitCount, spentFillRiseMissCounter, - * lastPrevSpentDialPressure, prevSpentDialPressure (updated only in slope mode). + * lastPrevSpentDialPressure, prevSpentDialPressure. * @param spentDialPressure filtered spent dialysate pressure (PSIG). * @param qdMlpm dialysate flow rate (mL/min). - * @param spentFillCompletePresPsig spent fill complete pressure threshold for current Qd and pump. * @return TRUE if spent fill is detected this task period; FALSE otherwise. *************************************************************************/ -static BOOL isSpentFillCompleteByPressure( F32 spentDialPressure, F32 qdMlpm, F32 spentFillCompletePresPsig ) +static BOOL isSpentFillCompleteByPressure( F32 spentDialPressure, F32 qdMlpm ) { - BOOL useSlopeDetector = ( ( ( TRUE != getTestConfigStatus( TEST_CONFIG_DD_ENABLE_DIENER_2000_PUMP ) ) && - ( qdMlpm <= SPENT_FILL_COMPLETE_QD_SLOPE_MAX_MLPM ) ) ? TRUE : FALSE ); + BOOL useLowQdRiseThreshold = ( ( ( TRUE != getTestConfigStatus( TEST_CONFIG_DD_ENABLE_DIENER_2000_PUMP ) ) && + ( qdMlpm <= SPENT_FILL_COMPLETE_QD_LOW_RISE_MAX_MLPM ) ) ? TRUE : FALSE ); + F32 riseThresholdPsig = ( TRUE == useLowQdRiseThreshold ) ? SPENT_FILL_COMPLETE_DP_RISE_PSIG : + SPENT_FILL_COMPLETE_DP_RISE_HIGH_QD_PSIG; BOOL state; - if ( TRUE == useSlopeDetector ) + // Gate slope hits near expected valve-close count (current vs balChamberValveClosePeriod +/- tolerance). + U32 detectTolCount = (U32)( (F32)balChamberValveClosePeriod * SPENT_FILL_DETECT_COUNT_TOL_PCT ); + U32 maxDetectCount = balChamberValveClosePeriod + detectTolCount; + U32 minDetectCount = balChamberValveClosePeriod - detectTolCount; + BOOL isDetectionInCountWindow = ( ( currentBalChamberFillCounter >= minDetectCount ) && + ( currentBalChamberFillCounter <= maxDetectCount ) ) ? TRUE : FALSE; + F32 riseDeltaPsi = spentDialPressure - lastPrevSpentDialPressure; + BOOL riseHit = ( riseDeltaPsi > riseThresholdPsig ) ? TRUE : FALSE; + U32 requiredRiseCount; + + // Mid band between low/high spent pressure rise: require two rise hits. + if ( riseDeltaPsi > SPENT_FILL_SLOPE_SPENT_PRESSURE_HIGH_PSIG ) { - // Gate slope hits near expected valve-close count (current vs balChamberValveClosePeriod +/- tolerance). - U32 detectTolCount = (U32)( (F32)balChamberValveClosePeriod * SPENT_FILL_DETECT_COUNT_TOL_PCT ); - U32 maxDetectCount = balChamberValveClosePeriod + detectTolCount; - U32 minDetectCount = balChamberValveClosePeriod - detectTolCount; - BOOL isDetectionInCountWindow = ( ( currentBalChamberFillCounter >= minDetectCount ) && - ( currentBalChamberFillCounter <= maxDetectCount ) ) ? TRUE : FALSE; - F32 riseDeltaPsi = spentDialPressure - lastPrevSpentDialPressure; - BOOL riseHit = ( riseDeltaPsi > SPENT_FILL_COMPLETE_DP_RISE_PSIG ) ? TRUE : FALSE; - U32 requiredRiseCount; + requiredRiseCount = 1U; + } + else + { + requiredRiseCount = 2U; + } - // Mid band between low/high spent pressure: require two rise hits. - if ( riseDeltaPsi > SPENT_FILL_SLOPE_SPENT_PRESSURE_HIGH_PSIG ) + if ( ( TRUE == riseHit ) && ( TRUE == isDetectionInCountWindow ) ) + { + spentFillRiseHitCount += 1; + spentFillRiseMissCounter = 0; + } + else if ( spentFillRiseHitCount > 0 ) + { + if ( ( FALSE == riseHit ) && ( TRUE == isDetectionInCountWindow ) ) { - requiredRiseCount = 1U; - } - else - { - requiredRiseCount = 2U; - } + spentFillRiseMissCounter += 1; - if ( ( TRUE == riseHit ) && ( TRUE == isDetectionInCountWindow ) ) - { - spentFillRiseHitCount += 1; - spentFillRiseMissCounter = 0; - } - else if ( spentFillRiseHitCount > 0 ) - { - if ( ( FALSE == riseHit ) && ( TRUE == isDetectionInCountWindow ) ) + if ( spentFillRiseMissCounter >= 2 ) { - spentFillRiseMissCounter += 1; - - if ( spentFillRiseMissCounter >= 2 ) - { - spentFillRiseHitCount = 0; - spentFillRiseMissCounter = 0; - } + spentFillRiseHitCount = 0; + spentFillRiseMissCounter = 0; } } + } - lastPrevSpentDialPressure = prevSpentDialPressure; - prevSpentDialPressure = spentDialPressure; + lastPrevSpentDialPressure = prevSpentDialPressure; + prevSpentDialPressure = spentDialPressure; - // Complete on slope hits, or on absolute pressure at period only if Qd > low band (low Qd: slope-only, no absolute-pressure fallback). - state = ( spentFillRiseHitCount >= requiredRiseCount ) ? TRUE : FALSE; - } - else - { - // Not in slope mode (Diener 2000 pump or Qd > SPENT_FILL_COMPLETE_QD_SLOPE_MAX_MLPM): threshold-only spent fill complete. - state = ( spentDialPressure >= spentFillCompletePresPsig ) ? TRUE : FALSE; - } + state = ( spentFillRiseHitCount >= requiredRiseCount ) ? TRUE : FALSE; return state; } @@ -1375,7 +1533,6 @@ U32 d48SpeedPostRangeCheck = 0; F32 qdMlpm = getBalChamberActiveDialysateFlowrate(); BOOL result = FALSE; - F32 spentFillCompletePresPsig; BOOL isSpentFillCompleteDetected = FALSE; if ( TRUE == isBalChamberTimeBasedSwitching() ) @@ -1387,25 +1544,8 @@ } else { - // Diener 2000 pump: spent fill complete pressure use legacy 25 PSI. Other pump: depends on Qd. - if ( TRUE == getTestConfigStatus( TEST_CONFIG_DD_ENABLE_DIENER_2000_PUMP ) ) - { - spentFillCompletePresPsig = SPENT_FILL_COMPLETE_PRES; - } - else - { - if ( qdMlpm <= QD_THRESHOLD_HIGH_MLPM ) - { - spentFillCompletePresPsig = SPENT_FILL_COMPLETE_PRES_QD_MID_PSIG; - } - else - { - spentFillCompletePresPsig = SPENT_FILL_COMPLETE_PRES_QD_HIGH_PSIG; - } - } + isSpentFillCompleteDetected = isSpentFillCompleteByPressure( spentDialPressure, qdMlpm ); - isSpentFillCompleteDetected = isSpentFillCompleteByPressure( spentDialPressure, qdMlpm, spentFillCompletePresPsig ); - if ( ( TRUE == isSpentFillCompleteDetected ) && ( isSpentFillComplete != TRUE ) ) { // Check spent fill time against the balancing chamber closing period @@ -1479,6 +1619,66 @@ /*********************************************************************//** * @brief + * The getBalChamberFreshSideValvesClosedStatus function returns whether the + * fresh-side balancing chamber valves are closed for the current switch state. + * @details \b Inputs: isFreshSideValvesClosed + * @details \b Outputs: none + * @return TRUE when fresh-side BC valves are closed. + *************************************************************************/ +BOOL getBalChamberFreshSideValvesClosedStatus( void ) +{ + return isFreshSideValvesClosed; +} + +/*********************************************************************//** + * @brief + * The getBalChamberFreshFillCompleteStatus function returns whether fresh-side + * fill completed during the current BC switch cycle. + * @details \b Inputs: isPressureStabilizedDuringFill + * @details \b Outputs: none + * @return TRUE when fresh-side fill is complete. + *************************************************************************/ +BOOL getBalChamberFreshFillCompleteStatus( void ) +{ + return isPressureStabilizedDuringFill; +} + +/*********************************************************************//** + * @brief + * The getBalChamberSpentFillCompleteStatus function returns whether spent-side + * fill completed during the current BC switch cycle. + * @details \b Inputs: isSpentFillComplete + * @details \b Outputs: none + * @return TRUE when spent-side fill is complete. + *************************************************************************/ +BOOL getBalChamberSpentFillCompleteStatus( void ) +{ + return isSpentFillComplete; +} + +/*********************************************************************//** + * @brief + * The isBalChamberSwitchImminent function returns whether the balancing chamber + * is within the valve-close window (50 ms) before switching. + * @details \b Inputs: currentBalChamberSwitchingCounter, balChamberValveClosePeriod + * @details \b Outputs: none + * @return TRUE when BC switch is imminent. + *************************************************************************/ +BOOL isBalChamberSwitchImminent( void ) +{ + BOOL result = FALSE; + + if ( ( balChamberValveClosePeriod > 0 ) && + ( currentBalChamberSwitchingCounter >= balChamberSwitchingPeriod ) ) + { + result = TRUE; + } + + return result; +} + +/*********************************************************************//** + * @brief * The getBalChamberSwitchingFreq function gets the balancing chamber switching * frequency value. * @details \b Inputs: balChamberSwitchingFreq Index: firmware/App/Controllers/BalancingChamber.h =================================================================== diff -u -r095866efd8f10bdead490578e7584051d79292f0 -rc479bd8fae14f3bd7dc8574bbb3dc6c5e7f98588 --- firmware/App/Controllers/BalancingChamber.h (.../BalancingChamber.h) (revision 095866efd8f10bdead490578e7584051d79292f0) +++ firmware/App/Controllers/BalancingChamber.h (.../BalancingChamber.h) (revision c479bd8fae14f3bd7dc8574bbb3dc6c5e7f98588) @@ -39,6 +39,13 @@ #define BC_SPENT_FILL_PRESSURE_PSIG 29.0F ///< Spent side pressure (drop) during balancing chamber switching, indicates fill has been started / in progress. #define BAL_CHAMBER_FILL_VOLUME_ML 30.0F ///< Balancing chamber fill/drain volume per batch operation. #define BAL_CHAMBER_TIME_BASED_D48_SPEED_RPM 250 ///< Minimum D48 speed and fixed speed for time-based balancing chamber switching. +#define BAL_CHAMBER_OPEN_LOOP_FLOW_RATE_BAND_1 50.0F ///< Band 1 flowrate (50 mL/min) +#define BAL_CHAMBER_OPEN_LOOP_FLOW_RATE_BAND_2 60.0F ///< Band 2 flowrate (60 mL/min) +#define BAL_CHAMBER_OPEN_LOOP_FLOW_RATE_BAND_3 70.0F ///< Band 3 flowrate (70 mL/min) +#define BAL_CHAMBER_OPEN_LOOP_FLOW_RATE_BAND_4 80.0F ///< Band 4 flowrate (80 mL/min) +#define BAL_CHAMBER_TIME_BASED_BAND_1 250 ///< Speed at D48 pump runs when the flowrate is between 50 mL/min and 60 mL/min. +#define BAL_CHAMBER_TIME_BASED_BAND_2 290 ///< Speed at D48 pump runs when the flowrate is between 60 mL/min and 70 mL/min. +#define BAL_CHAMBER_TIME_BASED_BAND_3 330 ///< Speed at D48 pump runs when the flowrate is between 70 mL/min and 80 mL/min. /// balancing chamber data structure typedef struct @@ -69,6 +76,7 @@ void valveControlForBCClosedState( void ); // Closes all balancing chamber valves void valveControlForBCOpenState( void ); // Open all balancing chamber valves void valveControlForBCFreshSideOnlyOpenState( void ); // Open only fresh side balancing chamber valves +void valveControlForBCSpentSideOnlyOpenState( void ); // Open only spent side balancing chamber valves BOOL getBalChamberSwitchingOnlyStatus( void ); // Get balancing chamber switching only status void setBalChamberSwitchingOnlyStatus( BOOL OnOff ); // Set balancing chamber switching only status U32 getBalChamberSwitchingPeriod( void ); // Get balancing chamber switching period Index: firmware/App/Controllers/ConcentratePumps.h =================================================================== diff -u -r095866efd8f10bdead490578e7584051d79292f0 -rc479bd8fae14f3bd7dc8574bbb3dc6c5e7f98588 --- firmware/App/Controllers/ConcentratePumps.h (.../ConcentratePumps.h) (revision 095866efd8f10bdead490578e7584051d79292f0) +++ firmware/App/Controllers/ConcentratePumps.h (.../ConcentratePumps.h) (revision c479bd8fae14f3bd7dc8574bbb3dc6c5e7f98588) @@ -7,8 +7,8 @@ * * @file ConcentratePumps.h * -* @author (last) Sameer Kalliadan Poyil -* @date (last) 15-Apr-2026 +* @author (last) Jashwant Gantyada +* @date (last) 29-Jul-2026 * * @author (original) Vinayakam Mani * @date (original) 19-Sep-2024 @@ -19,7 +19,7 @@ #define __CONCENTRATE_PUMPS_H__ #include "DDCommon.h" -//#include "NVDataMgmt.h" +//#include "NVMgmt.h" /** * @defgroup ConcentratePumps ConcentratePumps @@ -32,11 +32,13 @@ // ********** public definitions ********** // For 150 RPM, Diener pump delivers 60ml/min #define CONCENTRATE_PUMP_MAX_SPEED 200.0F ///< Maximum Diener pump speed for concentrate pump in mL/min -#define DOSING_CONCENTRATE_PUMP_SPEED 100.0F ///< Diener pump speed for acid and bicarb dosing pump speed in mL/min +#define CONCENTRATE_PUMP_MIN_SPEED 20.0F ///< Minimum Diener pump speed for concentrate pump in mL/min +#define DOSING_ACID_CONCENTRATE_PUMP_SPEED 60.0F ///< Diener pump speed for acid pump +#define DOSING_CONCENTRATE_PUMP_SPEED 100.F ///< Dosing pump speed for spent chamber fill for acid and bicarb pumps #define DRAIN_BICART_PUMP_SPEED 200.0F ///< Diener pump speed for cartridge drains at maximum speed in mL/min #define DEFAULT_ACID_VOLUME_ML 0.666666667F ///< Acid concentrate volume in ml. -#define DEFAULT_BICARB_VOLUME_ML 1.146666667F ///< Bicarb concentrate volume in ml. +#define DEFAULT_BICARB_VOLUME_ML 0.95F ///< Bicarb concentrate volume in ml. #define DOSING_CONT_VOLUME 0xFFFFFFFF ///< Volume set to 0xFFFFFFFF enables continuous delivery based on the speed set. Index: firmware/App/Controllers/DryBiCart.c =================================================================== diff -u -r095866efd8f10bdead490578e7584051d79292f0 -rc479bd8fae14f3bd7dc8574bbb3dc6c5e7f98588 --- firmware/App/Controllers/DryBiCart.c (.../DryBiCart.c) (revision 095866efd8f10bdead490578e7584051d79292f0) +++ firmware/App/Controllers/DryBiCart.c (.../DryBiCart.c) (revision c479bd8fae14f3bd7dc8574bbb3dc6c5e7f98588) @@ -7,8 +7,8 @@ * * @file DryBiCart.c * -* @author (last) Sameer Kalliadan Poyil -* @date (last) 13-Jul-2026 +* @author (last) Jashwant Gantyada +* @date (last) 29-Jul-2026 * * @author (original) Vinayakam Mani * @date (original) 19-Nov-2025 @@ -46,18 +46,19 @@ #define DRY_BICART_DATA_PUBLISH_INTERVAL ( 250 / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) at which the drybicart data published. #define WCDI2_PRESSURE_OFFSET_PSI 7.0F ///< The difference between the actual pressure measured at bicart assembly and the measured D66 pressure based on the placement of the sensor for WCID1 #define WCID1_PRESSURE_OFFSET_PSI 1.0F ///< The difference between the actual pressure measured at bicart assembly and the measured D66 pressure based on the placement of the sensor for WCID2 -#define DRY_BICART_PERSISTENCE_DURATION_MS 200 ///< Fill persistence duration to overcome the pressure overshoot/ drop for beta 1.9 and above. +#define DRY_BICART_PERSISTENCE_DURATION_MS 500 ///< Fill persistence duration to overcome the pressure overshoot/ drop for beta 1.9 and above. +//#define DRY_BICART_FILL_PERSISTENCE_DURATION_MS ( 1 * MS_PER_SECOND ) ///< Fill persistence duration to overcome the pressure overshoot/ drop for beta 1.9 and above. // Dry Bicart Fill #define DRY_BICART_FILL_COMPLETE_TIME_MS ( 1 * MS_PER_SECOND ) ///< Wait time to reset the request flag after fill complete -#define DRY_BICART_WCID2_FILL_COMPLETE_PRESSURE_PSI ( 10.0F + WCDI2_PRESSURE_OFFSET_PSI ) ///< Maximum pressure reached to indicate the dry bicart fill being completed for WCID2 -#define DRY_BICART_WCID1_FILL_COMPLETE_PRESSURE_PSI ( 10.0F + WCID1_PRESSURE_OFFSET_PSI ) ///< Maximum pressure reached to indicate the dry bicart fill being completed for WCID1 +#define DRY_BICART_WCID2_FILL_COMPLETE_PRESSURE_PSI ( 3.0F + WCDI2_PRESSURE_OFFSET_PSI ) ///< Maximum pressure reached to indicate the dry bicart fill being completed for WCID2 +#define DRY_BICART_WCID1_FILL_COMPLETE_PRESSURE_PSI ( 10.0F + WCID1_PRESSURE_OFFSET_PSI ) ///< Maximum pressure reached to indicate the dry bicart fill being completed for WCID1. #define GET_FILL_COMPLETE_PRESSURE_PSI (getTestConfigStatus( TEST_CONFIG_DD_ENABLE_WCID_1 ) != TRUE ? \ DRY_BICART_WCID2_FILL_COMPLETE_PRESSURE_PSI : DRY_BICART_WCID1_FILL_COMPLETE_PRESSURE_PSI ) ///< Get fill complete pressure based on the WCID configuration #define DRY_BICART_FILL_INITIATE_PRESSURE_PSI 1.5F ///< Minimum pressure required to initiate the dry bicart fill process. #define DRY_BICART_FILL_COMPLETE_SUPPLY_PRESSURE_PSI 10.0F ///< Maximum pressure allowed at bicart fill during supply process/state -#define DRY_BICART_FILL_INITIATE_SUPPLY_PRESSURE_PSI 3.0F ///< Minimum pressure required to initiate the dry bicart fill during supply process/state +#define DRY_BICART_FILL_INITIATE_SUPPLY_PRESSURE_PSI 4.0F ///< Minimum pressure required to initiate the dry bicart fill during supply process/state #define DRY_BICART_MAX_FILL_CYCLE_CNT 10 ///< Max fill cycle allowed (by override) for dry bicart fill/mix with water. #define DRY_BICART_MINIMUM_FILL_CYCLE_CNT 3 ///< Minimum fill cycle count for filling dry bicart // Dry Bicart Fill Vent @@ -67,13 +68,15 @@ // Bicarb chamber fill/Supply #ifdef CONDUCTIVE_LEVEL_SENSOR_ENABLED -#define DRY_BICART_SUPPLY_VALVE_D80_OPEN_TIME_MS ( 10 * MS_PER_SECOND ) ///< Max time allowed for supply (opening D80 valve) during bicarb chamber (F) fill using level sensor +#define DRY_BICART_SUPPLY_VALVE_D80_OPEN_TIME_MS ( 60 * MS_PER_SECOND ) ///< Max time allowed for supply (opening D80 valve) during bicarb chamber (F) fill using level sensor #else #define DRY_BICART_SUPPLY_VALVE_D80_OPEN_TIME_MS ( 3 * MS_PER_SECOND ) ///< Max time allowed for supply (opening D80 valve) during bicarb chamber (F) fill time based fill #endif #define DRY_BICART_SUPPLY_VENT_TIME_MS ( 1 * MS_PER_SECOND ) ///< Wait time to vent dry bicart gas before actuating Bicarb chamber(F) venting. #define DRY_BICART_SUPPLY_VENT_MAX_TIME_MS ( 1 * MS_PER_SECOND ) ///< Max time to vent both dry bicart and Chamber F. #define DRY_BICART_SUPPLY_COMPLETE_TIME_MS ( 2 * MS_PER_SECOND ) ///< Wait time to close the D80 valve after D65 is closed +#define DRY_BICART_SUPPLY_DECAY_COMPLETE_PRESSURE_PSI 4.0F ///< Minimum decay pressure needed to end the supply process. +#define DRY_BICART_SUPPLY_VENT_CYCLE_TIME_MS ( 15 * SEC_PER_MIN * MS_PER_SECOND ) ///< Vent cycle time to initiate D85 opening. // Dry Bicart Drain #define LARGE_DRY_BICART_MAX_DRAIN_TIME_MS ( 8 * SEC_PER_MIN * MS_PER_SECOND ) ///< Max drain time for large dry bicart in ms. @@ -141,6 +144,8 @@ static U32 dryBiCartPersistenceStartTime; ///< Dry bicart Persistence time. static U32 drybicartPersistenceOnLowercartPressureStartTime; ///< Dry bicart Persistence on lower cart pressure start time. static BOOL dryBiCartPressureDecayStartTimeFlag; ///< Dry bicart d66 pressure decay start timer. +static BOOL dryBiCartVentCycleWaitTimeStartedFlag; ///< Flag indicating venting cycle timer started for opening D85. +static U32 dryBiCartVentCycleStartTime; ///< Dry bicart Vent cycle start time. static DRY_BICART_OPERATION_T dryBicartStartRequest; ///< Dry bicart operation, fill or supply or drain request static DRY_BICART_OPERATION_T prevDryBicartState; ///< Dry BiCart State Change Tracker @@ -164,6 +169,9 @@ static BICARB_CHAMBER_FILL_EXEC_STATE_T handleBicarbChamberPressureCheckState( void ); static BICARB_CHAMBER_FILL_EXEC_STATE_T handleBicarbChamberSupplyVentStartState(void); static BICARB_CHAMBER_FILL_EXEC_STATE_T handleBicarbChamberSupplyVentEndState(void); +static BICARB_CHAMBER_FILL_EXEC_STATE_T completeBicarbChamberFillToIdle( void ); +static BOOL isDryBicartChamberFillWindowOpen( void ); +static void closeD65IfBalChamberSwitchImminent( void ); // Drain static DRY_BICART_DRAIN_EXEC_STATE_T handleDryBicartDrainStartState( void ); static DRY_BICART_DRAIN_EXEC_STATE_T handleDryBicartFluidDrainState( void ); @@ -237,7 +245,9 @@ dryBiCartLowerCartPressure.override = OVERRIDE_RESET; dryBiCartPressureDecayStartTimeFlag = FALSE; + dryBiCartVentCycleWaitTimeStartedFlag = FALSE; dryBiCartFillVentTimeOut = FALSE; + dryBiCartVentCycleStartTime = 0; dryBiCartFillStartTime = 0; lastFillDurationInMS = 0; currentFillDurationInMS = 0; @@ -551,6 +561,46 @@ /*********************************************************************//** * @brief + * The isDryBicartChamberFillWindowOpen function checks whether D65 may be + * opened for dry bicarb refill: fresh-side BC valves closed and BC switch not + * within the 50 ms valve-close window. + * @details \b Inputs: getBalChamberFreshSideValvesClosedStatus, isBalChamberSwitchImminent + * @details \b Outputs: none + * @return TRUE when the BC fill is completed and allow D65 to fill Chamber F + *************************************************************************/ +static BOOL isDryBicartChamberFillWindowOpen( void ) +{ + BOOL result = FALSE; + + if ( ( TRUE == getBalChamberFreshSideValvesClosedStatus() ) && + ( FALSE == isBalChamberSwitchImminent() ) ) + { + result = TRUE; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The closeD65IfBalChamberSwitchImminent function closes D65 when the balancing + * chamber is within 100 ms of switching. + * @details \b Inputs: isBalChamberSwitchImminent + * @details \b Outputs: valve states + * @return none + *************************************************************************/ +static void closeD65IfBalChamberSwitchImminent( void ) +{ + if ( TRUE == isBalChamberSwitchImminent() ) + { + setValveState( D65_VALV, VALVE_STATE_CLOSED ); + // Open D80 only in supply state + setValveState( D80_VALV, VALVE_STATE_OPEN ); + } +} + +/*********************************************************************//** + * @brief * The setBicartFillRequested function sets the bicart fill request * flag value to be True. * @details \b Inputs: bicarbChamberFillRequested, dryBiCartDrainRequested, @@ -838,16 +888,16 @@ F32 d66Pressure = getFilteredPressure( D66_PRES ); F32 fillCompletePressure = GET_FILL_COMPLETE_PRESSURE_PSI; - // Close dry bicart inlet water if D66 pressure is 17 PSI + // Close dry bicart inlet water if D66 pressure is equal to fill complete pressure if ( d66Pressure >= fillCompletePressure ) { - // start the persistence timer once d66 reaches 17 PSI + // start the persistence timer once d66 reaches fill complete pressure if ( 0 == dryBiCartPersistenceStartTime ) { dryBiCartPersistenceStartTime = getMSTimerCount(); } - // 200 ms persistence on D66 pressure since pressure overshoot just after D65 opening + // defined persistence on D66 pressure since pressure overshoot just after D65 opening if ( TRUE == didTimeout( dryBiCartPersistenceStartTime, DRY_BICART_PERSISTENCE_DURATION_MS ) ) { setValveState( D65_VALV, VALVE_STATE_CLOSED ); @@ -914,15 +964,15 @@ //Vent chamber F to atmosphere setValveState( D64_VALV, VALVE_STATE_OPEN ); - // D66 pressure drops to below 1.5 PSI + // D66 pressure drops to below fill vent complete pressure if ( ( d66Pressure <= DRY_BICART_FILL_VENT_COMPLETE_PRESSURE_PSI ) ) { - // start the persistence timer once d66 reaches 1.5 PSI + // start the persistence timer once d66 reaches fill vent complete pressure if ( 0 == dryBiCartPersistenceStartTime ) { dryBiCartPersistenceStartTime = getMSTimerCount(); } - // 200 ms persistence on D66 pressure since pressure drop below 1.5 PSI after opening D64 + // defined persistence on D66 pressure since pressure drop below fill vent complete pressure after opening D64 if ( TRUE == didTimeout( dryBiCartPersistenceStartTime, DRY_BICART_PERSISTENCE_DURATION_MS ) ) { setValveState( D85_VALV, VALVE_STATE_CLOSED ); @@ -1046,8 +1096,9 @@ * @brief * The handleBicarbChamberStartState function wait for bicarb chamber fill request * @details \b Inputs: bicarbChamberFillRequested - * @details \b Outputs: none - * @return the next drybicart fluid drain state. + * @details \b Outputs: dryBiCartVentCycleWaitTimeStartedFlag, dryBiCartPressureDecayStartTimeFlag, + * dryBiCartVentCycleStartTime. + * @return the next drybicart fluid supply state. *************************************************************************/ static BICARB_CHAMBER_FILL_EXEC_STATE_T handleBicarbChamberStartState( void ) { @@ -1057,7 +1108,13 @@ if ( TRUE == getU32OverrideValue( &bicarbChamberFillRequested ) ) { - state = BICARB_CARTRIDGE_FILL_WATER_START_STATE; + state = BICARB_CHAMBER_SUPPLY_STATE; + + if ( FALSE == dryBiCartVentCycleWaitTimeStartedFlag ) + { + dryBiCartVentCycleWaitTimeStartedFlag = TRUE; + dryBiCartVentCycleStartTime = getMSTimerCount(); + } } return state; @@ -1071,27 +1128,32 @@ *************************************************************************/ static BICARB_CHAMBER_FILL_EXEC_STATE_T handleBicarbChamberCartridgeFillWaterStartState( void ) { - BICARB_CHAMBER_FILL_EXEC_STATE_T state = BICARB_CARTRIDGE_FILL_WATER_END_STATE; + BICARB_CHAMBER_FILL_EXEC_STATE_T state = BICARB_CARTRIDGE_FILL_WATER_START_STATE; - // Close vent valves and descaling valve - setValveState( D80_VALV, VALVE_STATE_CLOSED ); - setValveState( D81_VALV, VALVE_STATE_CLOSED ); - setValveState( D85_VALV, VALVE_STATE_CLOSED ); + if ( TRUE == isDryBicartChamberFillWindowOpen() ) + { + // Close vent valves and descaling valve + setValveState( D80_VALV, VALVE_STATE_CLOSED ); + setValveState( D81_VALV, VALVE_STATE_CLOSED ); + setValveState( D85_VALV, VALVE_STATE_CLOSED ); - // open inlet water to bicart - setValveState( D65_VALV, VALVE_STATE_OPEN ); - setValveState( D64_VALV, VALVE_STATE_OPEN ); + // open inlet water to bicart only when fresh-side BC valves are closed + setValveState( D65_VALV, VALVE_STATE_OPEN ); + setValveState( D64_VALV, VALVE_STATE_OPEN ); - dryBiCarbSupplyStartTime = getMSTimerCount(); - dryBiCartPersistenceStartTime = 0; + dryBiCarbSupplyStartTime = getMSTimerCount(); + dryBiCartPersistenceStartTime = 0; + state = BICARB_CARTRIDGE_FILL_WATER_END_STATE; + } + return state; } /*********************************************************************//** * @brief - * The handleBicarbChamberCartridgeFillWaterStartState function stop water in the dry bicart - * @details \b Inputs: D66 pressure, dryBiCartPersistenceStartTime + * The handleBicarbChamberCartridgeFillWaterStartState function stops the inlet water to cartridge. + * @details \b Inputs: dryBiCartPersistenceStartTime * @details \b Outputs: valve states * @return the next bicarb chamber check level state. *************************************************************************/ @@ -1100,8 +1162,18 @@ BICARB_CHAMBER_FILL_EXEC_STATE_T state = BICARB_CARTRIDGE_FILL_WATER_END_STATE; F32 d66Pressure = getFilteredPressure( D66_PRES ); F32 fillCompletePressure = GET_FILL_COMPLETE_PRESSURE_PSI; + LVL_STATE_T bicarbChamberLevel = getBicarbChamberLevelStatus(); - // check D66 pressure greater than or equal to 17 PSI + if ( FALSE == isDryBicartChamberFillWindowOpen() ) + { + setValveState( D65_VALV, VALVE_STATE_CLOSED ); + } + else if ( TRUE == isDryBicartChamberFillWindowOpen() ) + { + setValveState( D65_VALV, VALVE_STATE_OPEN ); + } + + // check D66 pressure greater than or equal to Fill complete pressure if ( d66Pressure >= fillCompletePressure ) { // start the persistence timer once d66 reaches 17 PSI @@ -1110,21 +1182,30 @@ dryBiCartPersistenceStartTime = getMSTimerCount(); } - // 200 ms persistence on D66 pressure since pressure overshoot just after D65 opening + // Defined persistence on D66 pressure since pressure overshoot just after D65 opening only for chamber fill if ( TRUE == didTimeout( dryBiCartPersistenceStartTime, DRY_BICART_PERSISTENCE_DURATION_MS ) ) { // Close water inlet valve as D66 pressure reaches 11 PSI after persistence setValveState( D65_VALV, VALVE_STATE_CLOSED ); dryBiCartPersistenceStartTime = 0; - state = BICARB_CHAMBER_SUPPLY_STATE; + // Return to idle + state = completeBicarbChamberFillToIdle(); + } } else { dryBiCartPersistenceStartTime = 0; } + //Additional code + if ( LVL_STATE_LOW == bicarbChamberLevel ) + { + // Force return to idle to allow next supply + state = completeBicarbChamberFillToIdle(); + } + return state; } @@ -1140,7 +1221,8 @@ BICARB_CHAMBER_FILL_EXEC_STATE_T state = BICARB_CHAMBER_PRESSURE_CHECK_STATE; // TODO: set supply in progress to inform BC control to disable alarm - setValveState( D64_VALV, VALVE_STATE_CLOSED ); + setValveState( D64_VALV, VALVE_STATE_OPEN ); + // Open the Bicarb chamber inlet valve setValveState( D80_VALV, VALVE_STATE_OPEN ); @@ -1156,10 +1238,9 @@ * @brief * The handleBicarbChamberPressureCheckStartState function actuates the vent valve * present in the Bicart assembly. - * @details \b Inputs: D66 Pressure, D63 level, dryBiCarbSupplyStartTime, - * dryBiCarbSypplyVentStartTime, dryBiCartPersistenceStartTime, bicarbChamberLevel, - * dryBiCartPressureDecayStartTimeFlag, drybicartPersistenceOnLowercartPressureStartTime - * @details \b Outputs: dryBiCarbSypplyVentStartTime + * @details \b Inputs: dryBiCarbSupplyStartTime, dryBiCarbSypplyVentStartTime, dryBiCartPersistenceStartTime, + * bicarbChamberLevel, dryBiCartPressureDecayStartTimeFlag, drybicartPersistenceOnLowercartPressureStartTime + * @details \b Outputs: dryBiCarbSypplyVentStartTime, valve states * @return the next bicarb chamber Supply Vent Start state. *************************************************************************/ static BICARB_CHAMBER_FILL_EXEC_STATE_T handleBicarbChamberPressureCheckState( void ) @@ -1168,32 +1249,45 @@ F32 d66Pressure = getFilteredPressure( D66_PRES ); LVL_STATE_T bicarbChamberLevel = getBicarbChamberLevelStatus(); + // Close D65 irrespective of pressure for balancing chamber fresh side fill to complete. + closeD65IfBalChamberSwitchImminent(); + // Once upper level reached high , close the valve, timeout is for safety in case level sensor didn't work(10 sec), or else its a time based filling (3 sec) if ( ( LVL_STATE_HIGH == bicarbChamberLevel ) || ( TRUE == didTimeout( dryBiCarbSupplyStartTime, DRY_BICART_SUPPLY_VALVE_D80_OPEN_TIME_MS ) ) ) { - setValveState( D65_VALV, VALVE_STATE_CLOSED ); - - // start the timer for d66 pressure decay - if( FALSE == dryBiCartPressureDecayStartTimeFlag ) + if ( TRUE == didTimeout( dryBiCartVentCycleStartTime, DRY_BICART_SUPPLY_VENT_CYCLE_TIME_MS ) ) { - dryBiCarbSupplyStartTime = getMSTimerCount(); - dryBiCartPressureDecayStartTimeFlag = TRUE; - } - // do not close the D80 valve, wait till d66 decays to 3 PSI - if ( TRUE == didTimeout( dryBiCarbSupplyStartTime, DRY_BICART_SUPPLY_COMPLETE_TIME_MS ) ) - { - if ( d66Pressure <= getDryBicartLowerCartPressure() ) + // start the timer for d66 pressure decay + if ( FALSE == dryBiCartPressureDecayStartTimeFlag ) { - setValveState( D80_VALV, VALVE_STATE_CLOSED ); - setValveState( D85_VALV, VALVE_STATE_OPEN ); - dryBiCarbSupplyStartTime = getMSTimerCount(); - dryBiCarbSypplyVentStartTime = getMSTimerCount(); + dryBiCartPressureDecayStartTimeFlag = TRUE; + } + // do not close the D80 valve, wait till d66 decays to supply decay complete pressure + if ( TRUE == didTimeout( dryBiCarbSupplyStartTime, DRY_BICART_SUPPLY_COMPLETE_TIME_MS ) ) + { + if ( d66Pressure <= DRY_BICART_SUPPLY_DECAY_COMPLETE_PRESSURE_PSI ) + { + setValveState( D80_VALV, VALVE_STATE_CLOSED ); + setValveState( D64_VALV, VALVE_STATE_CLOSED ); + setValveState( D85_VALV, VALVE_STATE_OPEN ); - state = BICARB_SUPPLY_VENT_START_STATE; + dryBiCarbSupplyStartTime = getMSTimerCount(); + dryBiCarbSypplyVentStartTime = getMSTimerCount(); + + state = BICARB_SUPPLY_VENT_START_STATE; + } } } + else + { + setValveState( D65_VALV, VALVE_STATE_CLOSED ); + setValveState( D80_VALV, VALVE_STATE_CLOSED ); + dryBiCarbSupplyStartTime = getMSTimerCount(); + + state = BICARB_CARTRIDGE_FILL_WATER_START_STATE; + } } else if ( d66Pressure <= getDryBicartLowerCartPressure() ) { @@ -1202,10 +1296,13 @@ { drybicartPersistenceOnLowercartPressureStartTime = getMSTimerCount(); } - // 200 ms persistence on D66 pressure - if ( TRUE == didTimeout( drybicartPersistenceOnLowercartPressureStartTime, DRY_BICART_PERSISTENCE_DURATION_MS ) ) + + // defined persistence on D66 pressure + if ( ( TRUE == didTimeout( drybicartPersistenceOnLowercartPressureStartTime, DRY_BICART_PERSISTENCE_DURATION_MS ) ) && + ( TRUE == isDryBicartChamberFillWindowOpen() ) ) { setValveState( D65_VALV, VALVE_STATE_OPEN ); + setValveState( D80_VALV, VALVE_STATE_CLOSED ); drybicartPersistenceOnLowercartPressureStartTime = 0; } } @@ -1217,11 +1314,12 @@ { dryBiCartPersistenceStartTime = getMSTimerCount(); } - - // 200 ms persistence on D66 pressure since pressure overshoot just after D65 opening + + // defined persistence on D66 pressure since pressure overshoot just after D65 opening if ( TRUE == didTimeout( dryBiCartPersistenceStartTime, DRY_BICART_PERSISTENCE_DURATION_MS ) ) { setValveState( D65_VALV, VALVE_STATE_CLOSED ); + setValveState( D80_VALV, VALVE_STATE_OPEN ); dryBiCartPersistenceStartTime = 0; } } @@ -1239,7 +1337,7 @@ * The handleBicarbChamberSupplyVentStartState function actuates the vent valve * present in the Bicart assembly. * @details \b Inputs: dryBiCarbSypplyVentStartTime, dryBiCartPersistenceStartTime - * @details \b Outputs: dryBiCarbSypplyVentStartTime + * @details \b Outputs: dryBiCarbSypplyVentStartTime, valve states * @return the next bicarb chamber Supply Vent End state. *************************************************************************/ static BICARB_CHAMBER_FILL_EXEC_STATE_T handleBicarbChamberSupplyVentStartState(void) @@ -1252,20 +1350,20 @@ //Vent chamber F to atmosphere setValveState( D64_VALV, VALVE_STATE_OPEN ); - // D66 pressure drops to below 1.5 PSI or time out + // D66 pressure drops to below fill vent complete pressure or time out if ( ( d66Pressure <= DRY_BICART_FILL_VENT_COMPLETE_PRESSURE_PSI ) ) { - // start the persistence timer once d66 reaches if it drops below 1.5 + // start the persistence timer once d66 reaches if it drops below fill vent complete pressure if ( 0 == dryBiCartPersistenceStartTime ) { dryBiCartPersistenceStartTime = getMSTimerCount(); } - // 200 ms persistence on D66 pressure since pressure overshoot just after D65 opening + // defined persistence on D66 pressure since pressure overshoot just after D65 opening if ( TRUE == didTimeout( dryBiCartPersistenceStartTime, DRY_BICART_PERSISTENCE_DURATION_MS ) ) { setValveState( D85_VALV, VALVE_STATE_CLOSED ); dryBiCartPersistenceStartTime = 0; - + dryBiCartVentCycleWaitTimeStartedFlag = FALSE; state = BICARB_SUPPLY_VENT_END_STATE; } } @@ -1274,6 +1372,7 @@ setValveState( D85_VALV, VALVE_STATE_CLOSED ); dryBiCartPersistenceStartTime = 0; dryBiCartFillVentTimeOut = TRUE; + dryBiCartVentCycleWaitTimeStartedFlag = FALSE; state = BICARB_SUPPLY_VENT_END_STATE; } @@ -1288,25 +1387,39 @@ /*********************************************************************//** * @brief - * The handleBicarbChamberSupplyVentEndState function actuates the vent valve - * present in the Bicart assembly. - * @details \b Inputs: dryBiCarbSypplyVentStartTime - * @details \b Outputs: bicarbChamberFillRequested - * @return the next bicarb chamber start state. + * The completeBicarbChamberFillToIdle function clears bicarb chamber fill + * request and returns the bicarb chamber fill state machine to start. + * @details \b Inputs: none + * @details \b Outputs: bicarbChamberFillRequested, dryBiCartPressureDecayStartTimeFlag, + * dryBicartStartRequest + * @return BICARB_CHAMBER_START_STATE *************************************************************************/ -static BICARB_CHAMBER_FILL_EXEC_STATE_T handleBicarbChamberSupplyVentEndState(void) +static BICARB_CHAMBER_FILL_EXEC_STATE_T completeBicarbChamberFillToIdle( void ) { - BICARB_CHAMBER_FILL_EXEC_STATE_T state = BICARB_SUPPLY_VENT_END_STATE; + BICARB_CHAMBER_FILL_EXEC_STATE_T state = BICARB_CHAMBER_START_STATE; - bicarbChamberFillRequested.data = FALSE; + bicarbChamberFillRequested.data = FALSE; // for this request override also cleared bicarbChamberFillRequested.ovData = FALSE; dryBiCartPressureDecayStartTimeFlag = FALSE; + dryBicartStartRequest = DRY_BICART_IDLE; - state = BICARB_CHAMBER_START_STATE; + return state; +} - dryBicartStartRequest = DRY_BICART_IDLE; +/*********************************************************************//** + * @brief + * The handleBicarbChamberSupplyVentEndState function completes bicarb chamber + * fill to idle. Kept for any residual VentEnd transitions; prefer calling + * completeBicarbChamberFillToIdle() directly from FillWater/Vent completion. + * @details \b Inputs: none + * @details \b Outputs: bicarbChamberFillRequested, dryBicartStartRequest + * @return the next bicarb chamber start (idle) state. + *************************************************************************/ +static BICARB_CHAMBER_FILL_EXEC_STATE_T handleBicarbChamberSupplyVentEndState(void) +{ + BICARB_CHAMBER_FILL_EXEC_STATE_T state = completeBicarbChamberFillToIdle(); return state; } @@ -1619,7 +1732,7 @@ * @details \b Outputs: dryBiCartDataPublishInterval * @param Override message from Dialin which includes the interval * (in ms) to override the DD dry bicart data publish interval to. - * @return TRUE if override successful, FALSE if not + * @return TRUE if override successful, FALSE if not. *************************************************************************/ BOOL testDryBiCartDataPublishIntervalOverride( MESSAGE_T *message ) { Index: firmware/App/Controllers/MixingControl.c =================================================================== diff -u -r095866efd8f10bdead490578e7584051d79292f0 -rc479bd8fae14f3bd7dc8574bbb3dc6c5e7f98588 --- firmware/App/Controllers/MixingControl.c (.../MixingControl.c) (revision 095866efd8f10bdead490578e7584051d79292f0) +++ firmware/App/Controllers/MixingControl.c (.../MixingControl.c) (revision c479bd8fae14f3bd7dc8574bbb3dc6c5e7f98588) @@ -81,7 +81,6 @@ #define STD_45X_CONC_MIX_BICARB_PART 1.72F ///< Bicarb portion of 45X concentration mix #define STD_45X_CONC_MIX_WATER_PART 42.28F ///< Water portion of 45X concentration mix - #define TOTAL_STD_45X_MIX_PART ( STD_45X_CONC_MIX_ACID_PART + STD_45X_CONC_MIX_BICARB_PART + \ STD_45X_CONC_MIX_WATER_PART ) ///< Total 45X mix @@ -121,6 +120,8 @@ #define FINAL_BICARB_MIX ( BICARB_MIX_VOLUME_ML * BICARB_PERCENT_CHANGE ) ///< Final bicarb mix volume +#define FINAL_BICARB_MIX_VOLUME_UPDATE ( FINAL_BICARB_MIX / 1.25 ) ///< Updated Final Bicarb mix volume + #define BICARB_CONDUCTIVITY_PRE ( STD_DILUTION_BICARB_POST_MIX_NA * BICARB_PERCENT_CHANGE * getTDBicarbConversionFactor() ) ///< Theoretical bicarb conductivity pre mix #define BICARB_CONDUCTIVITY_POST ( STD_DILUTION_BICARB_POST_MIX_CHO3 * BICARB_PERCENT_CHANGE * getTDBicarbConversionFactor() ) ///< Theoretical bicarb conductivity post mix Index: firmware/App/Drivers/TemperatureSensors.c =================================================================== diff -u -r095866efd8f10bdead490578e7584051d79292f0 -rc479bd8fae14f3bd7dc8574bbb3dc6c5e7f98588 --- firmware/App/Drivers/TemperatureSensors.c (.../TemperatureSensors.c) (revision 095866efd8f10bdead490578e7584051d79292f0) +++ firmware/App/Drivers/TemperatureSensors.c (.../TemperatureSensors.c) (revision c479bd8fae14f3bd7dc8574bbb3dc6c5e7f98588) @@ -7,8 +7,8 @@ * * @file TemperatureSensors.c * -* @author (last) Sameer Kalliadan Poyil -* @date (last) 16-Jun-2026 +* @author (last) Arpita Srivastava +* @date (last) 13-Jul-2026 * * @author (original) Vinayakam Mani * @date (original) 25-Sep-2024 @@ -20,7 +20,7 @@ #include "FpgaDD.h" #include "MessageSupport.h" #include "Messaging.h" -//#include "NVDataMgmt.h" +//#include "NVMgmt.h" #include "OperationModes.h" #include "PersistentAlarm.h" #include "TemperatureSensors.h" @@ -378,9 +378,10 @@ } } - //TODO: revert back once issue resolved + //TODO: Uncomment after further analysis of the alarm behavior //checkPersistentAlarm( ALARM_ID_DD_TEMPERATURE_SENSOR_OUT_OF_RANGE, isTemperatureOutOfRange, sensorInAlarm, alarmTemperature ); + //TODO: Uncomment after further analysis of the alarm behavior //check freshness of temperature read checkTemperatureSensors(); } Index: firmware/App/Monitors/Conductivity.c =================================================================== diff -u -r095866efd8f10bdead490578e7584051d79292f0 -rc479bd8fae14f3bd7dc8574bbb3dc6c5e7f98588 --- firmware/App/Monitors/Conductivity.c (.../Conductivity.c) (revision 095866efd8f10bdead490578e7584051d79292f0) +++ firmware/App/Monitors/Conductivity.c (.../Conductivity.c) (revision c479bd8fae14f3bd7dc8574bbb3dc6c5e7f98588) @@ -7,8 +7,8 @@ * * @file Conductivity.c * -* @author (last) Michael Garthwaite -* @date (last) 08-Jul-2026 +* @author (last) Jashwant Gantyada +* @date (last) 29-Jul-2026 * * @author (original) Vinayakam Mani * @date (original) 13-Sep-2024 @@ -17,12 +17,14 @@ #include // Used for calculating the polynomial calibration equation. #include // For memcpy +#include "BalancingChamber.h" #include "Conductivity.h" #include "MessageSupport.h" #include "Messaging.h" #include "ModeGenPermeate.h" #include "OperationModes.h" #include "TaskPriority.h" +#include "TDInterface.h" #include "Utilities.h" /** @@ -38,9 +40,9 @@ #define FP_CONDUCTIVITY_DATA_PUBLISH_COUNTER_START_COUNT 41 ///< FP Conductivity data publish counter start count. #define RO_DATA_PUBLISH_COUNTER_START_COUNT 42 ///< FP RO Data publish counter start count. #define RESISTANCE_DATA_PUBLISH_COUNTER_START_COUNT 43 ///< DD Resistance data publish counter start count. -#define CONDUCTIVITY_SAMPLE_FILTER_MS ( 210 ) ///< Filter conductivity data for given time. Currently set to have 5 samples over 3.5s ( 700ms sample rate ) -#define CONDUCTIVITY_TEMP_SAMPLE_FILTER_MS ( 30 ) ///< Filter conductivity temperature data for given time. Currently set to have 5 samples over 3.5s ( 700ms sample rate ) -#define SIZE_OF_COND_ROLLING_AVG ( CONDUCTIVITY_SAMPLE_FILTER_MS / TASK_PRIORITY_INTERVAL ) ///< Filtered conductivity moving average sample count. +#define CONDUCTIVITY_MAX_FILTER_SIZE_MULTIPLIER ( 252 ) ///< Maximum conductivity filter sample count (At 50qd, we switch once for every 36sec, so 36 * FILTER_SIZE_MULTIPLIER = 252). +#define CONDUCTIVITY_TEMP_SAMPLE_FILTER_MS ( 3 * TASK_PRIORITY_INTERVAL ) ///< Conductivity temperature filter scale (ms). Rolling avg size = FILTER_MS / TASK_PRIORITY_INTERVAL = 3 samples (3 * TASK_PRIORITY_INTERVAL(10) = 30). At 700 ms sensor update rate, 3 * 700 ms ≈ 2.1 s window. +#define FILTER_SIZE_MULTIPLIER ( 7 ) ///< Conductivity filter size multiplier. #define SIZE_OF_COND_TEMP_ROLLING_AVG ( CONDUCTIVITY_TEMP_SAMPLE_FILTER_MS / TASK_PRIORITY_INTERVAL ) ///< Filtered conductivity temprature moving average sample count. #define RO_RR_MOVING_AVG_NUM_OF_SAMPLES 30 ///< RO rejection ratio moving average number of samples. #define FRACTION_TO_PERCENT_CONVERSION_FACTOR 100.0F ///< RO rejection ratio factor to percentage conversion factor value @@ -49,7 +51,7 @@ /// Filter conductivity readings record. typedef struct { - F32 conductivityReadings[ SIZE_OF_COND_ROLLING_AVG ]; ///< Holds conductivity sample rolling average. + F32 conductivityReadings[ CONDUCTIVITY_MAX_FILTER_SIZE_MULTIPLIER ]; ///< Holds conductivity sample rolling average. U32 conductivityReadingsIdx; ///< Index for next sample in rolling average array. F32 conductivityReadingsTotal; ///< Rolling total - used to calc average. U32 conductivityReadingsCount; ///< Number of samples in rolling average buffer @@ -89,6 +91,7 @@ static U32 roRRCount; ///< RO rejection ratio Number of samples in average buffer. static F32 roRRTankFillAvg; ///< Average RO rejection ratio during permeate tank fill state. static U32 roRRSampleIntervalCounter; ///< RO rejection ratio sample collection timer counter. +static U32 conductivityFilterSize; ///< Active conductivity filter sample count. // ********** private function prototypes ********** @@ -98,6 +101,9 @@ static void filterConductivitySensorTemperatureReadings( void ); static void calcRORejectionRatio( void ); static void filterRORejectionRatioReadings( void ); +static void broadcastResistanceData( void ); +static U32 getConductivityFilterSize( void ); +static void resetConductivityFilterReadings( void ); /*********************************************************************//** * @brief @@ -127,6 +133,7 @@ roRRAvg.ovInitData = 0.0F; roRRAvg.override = OVERRIDE_RESET; roRRSampleIntervalCounter = 0; + conductivityFilterSize = FILTER_SIZE_MULTIPLIER; memset( &roRRSamples, 0, sizeof( roRRSamples ) ); @@ -289,9 +296,16 @@ { CONDUCTIVITY_SENSORS_T sensor; F32 calculatedConductivity = 0.0F; + U32 activeFilterSize = getConductivityFilterSize(); F32 uncompenstatedConductivity = 0.0F; BOOL freshData = FALSE; + if ( conductivityFilterSize != activeFilterSize ) + { + conductivityFilterSize = activeFilterSize; + resetConductivityFilterReadings(); + } + for ( sensor = FIRST_DD_COND_SENSOR; sensor < NUM_OF_CONDUCTIVITY_SENSORS; sensor++ ) { if ( getTestConfigStatus( TEST_CONFIG_DD_FP_ENABLE_BETA_1_9_HW ) == TRUE ) @@ -324,24 +338,24 @@ { freshData = FALSE; // TODO - calibrate - if ( filteredConductivityReadings[ sensor ].conductivityReadingsCount >= SIZE_OF_COND_ROLLING_AVG ) + if ( filteredConductivityReadings[ sensor ].conductivityReadingsCount >= conductivityFilterSize ) { filteredConductivityReadings[ sensor ].conductivityReadingsTotal -= filteredConductivityReadings[ sensor ].conductivityReadings[ filteredConductivityReadings[ sensor ].conductivityReadingsIdx ]; } filteredConductivityReadings[ sensor ].conductivityReadings[ filteredConductivityReadings[ sensor ].conductivityReadingsIdx ] = calculatedConductivity; filteredConductivityReadings[ sensor ].conductivityReadingsTotal += calculatedConductivity; - filteredConductivityReadings[ sensor ].conductivityReadingsIdx = INC_WRAP( filteredConductivityReadings[ sensor ].conductivityReadingsIdx, 0, SIZE_OF_COND_ROLLING_AVG - 1 ); - filteredConductivityReadings[ sensor ].conductivityReadingsCount = INC_CAP( filteredConductivityReadings[ sensor ].conductivityReadingsCount, SIZE_OF_COND_ROLLING_AVG ); + filteredConductivityReadings[ sensor ].conductivityReadingsIdx = INC_WRAP( filteredConductivityReadings[ sensor ].conductivityReadingsIdx, 0, conductivityFilterSize - 1 ); + filteredConductivityReadings[ sensor ].conductivityReadingsCount = INC_CAP( filteredConductivityReadings[ sensor ].conductivityReadingsCount, conductivityFilterSize ); filteredcurrentConductivityReadings[ sensor ].data = filteredConductivityReadings[ sensor ].conductivityReadingsTotal / (F32)filteredConductivityReadings[ sensor ].conductivityReadingsCount; - if ( filteredUConductivityReadings[ sensor ].conductivityReadingsCount >= SIZE_OF_COND_ROLLING_AVG ) + if ( filteredUConductivityReadings[ sensor ].conductivityReadingsCount >= conductivityFilterSize ) { filteredUConductivityReadings[ sensor ].conductivityReadingsTotal -= filteredUConductivityReadings[ sensor ].conductivityReadings[ filteredUConductivityReadings[ sensor ].conductivityReadingsIdx ]; } filteredUConductivityReadings[ sensor ].conductivityReadings[ filteredUConductivityReadings[ sensor ].conductivityReadingsIdx ] = uncompenstatedConductivity; filteredUConductivityReadings[ sensor ].conductivityReadingsTotal += uncompenstatedConductivity; - filteredUConductivityReadings[ sensor ].conductivityReadingsIdx = INC_WRAP( filteredUConductivityReadings[ sensor ].conductivityReadingsIdx, 0, SIZE_OF_COND_ROLLING_AVG - 1 ); - filteredUConductivityReadings[ sensor ].conductivityReadingsCount = INC_CAP( filteredUConductivityReadings[ sensor ].conductivityReadingsCount, SIZE_OF_COND_ROLLING_AVG ); + filteredUConductivityReadings[ sensor ].conductivityReadingsIdx = INC_WRAP( filteredUConductivityReadings[ sensor ].conductivityReadingsIdx, 0, conductivityFilterSize - 1 ); + filteredUConductivityReadings[ sensor ].conductivityReadingsCount = INC_CAP( filteredUConductivityReadings[ sensor ].conductivityReadingsCount, conductivityFilterSize ); filteredcurrentUConductivityReadings[ sensor ].data = filteredUConductivityReadings[ sensor ].conductivityReadingsTotal / (F32)filteredUConductivityReadings[ sensor ].conductivityReadingsCount; } @@ -350,6 +364,75 @@ /*********************************************************************//** * @brief + * The getConductivityFilterSize function gets the active conductivity filter + * sample count based on the Qd balancing chamber switching period. + * @details \b Inputs: TD dialysate flow rate + * @details \b Outputs: none + * @return active conductivity filter sample count. + *************************************************************************/ +static U32 getConductivityFilterSize( void ) +{ + F32 tdDialysateFlowRate = getTDDialysateFlowrate(); + U32 result = FILTER_SIZE_MULTIPLIER; + + if ( tdDialysateFlowRate > NEARLY_ZERO ) + { + F32 balChamberSwitchingFreq = tdDialysateFlowRate / BAL_CHAMBER_FILL_VOLUME_ML; + F32 balChamberSwitchingPeriod = (F32)SEC_PER_MIN / balChamberSwitchingFreq; + + result = (U32)( (F32)FILTER_SIZE_MULTIPLIER * balChamberSwitchingPeriod ); + } + + return result; +} + +/*********************************************************************//** + * @brief + * The resetConductivityFilterReadings function clears conductivity rolling + * average data when the active filter window changes. + * @details \b Inputs: filteredConductivityReadings + * @details \b Outputs: filteredConductivityReadings + * @return none + *************************************************************************/ +static void resetConductivityFilterReadings( void ) +{ + CONDUCTIVITY_SENSORS_T sensor; + + for ( sensor = FIRST_DD_COND_SENSOR; sensor < NUM_OF_CONDUCTIVITY_SENSORS; sensor++ ) + { + U32 readingIndex; + + F32 seedConductivity = filteredcurrentConductivityReadings[ sensor ].data; + BOOL hasPreviousSamples = ( filteredConductivityReadings[ sensor ].conductivityReadingsCount > 0 ); + + for ( readingIndex = 0; readingIndex < CONDUCTIVITY_MAX_FILTER_SIZE_MULTIPLIER; readingIndex++ ) + { + if ( ( TRUE == hasPreviousSamples ) && ( readingIndex < conductivityFilterSize ) ) + { + filteredConductivityReadings[ sensor ].conductivityReadings[ readingIndex ] = seedConductivity; + } + else + { + filteredConductivityReadings[ sensor ].conductivityReadings[ readingIndex ] = 0.0F; + } + } + + filteredConductivityReadings[ sensor ].conductivityReadingsIdx = 0; + if ( TRUE == hasPreviousSamples ) + { + filteredConductivityReadings[ sensor ].conductivityReadingsTotal = seedConductivity * (F32)conductivityFilterSize; + filteredConductivityReadings[ sensor ].conductivityReadingsCount = conductivityFilterSize; + } + else + { + filteredConductivityReadings[ sensor ].conductivityReadingsTotal = 0.0F; + filteredConductivityReadings[ sensor ].conductivityReadingsCount = 0; + } + } +} + +/*********************************************************************//** + * @brief * The getFilteredConductivitySensorTemperature function gets the filtered * temperature (in C) for a given conductivity sensor. * @details \b Alarm: ALARM_ID_DD_SOFTWARE_FAULT if given sensor is invalid.