Index: firmware/App/Controllers/BalancingChamber.c =================================================================== diff -u -rb2e7c9194acd84783d2bbad64c5720410493e199 -r4cdcb589ad34efa472c94180e6de938132cbc89b --- firmware/App/Controllers/BalancingChamber.c (.../BalancingChamber.c) (revision b2e7c9194acd84783d2bbad64c5720410493e199) +++ firmware/App/Controllers/BalancingChamber.c (.../BalancingChamber.c) (revision 4cdcb589ad34efa472c94180e6de938132cbc89b) @@ -8,7 +8,7 @@ * @file BalancingChamber.c * * @author (last) Jashwant Gantyada -* @date (last) 13-Mar-2026 +* @date (last) 10-Apr-2026 * * @author (original) Vinayakam Mani * @date (original) 28-Jan-2025 @@ -19,6 +19,7 @@ #include "Conductivity.h" #include "ConcentratePumps.h" #include "DialysatePumps.h" +#include "DryBiCart.h" #include "FpgaDD.h" #include "Heaters.h" #include "ModeGenDialysate.h" @@ -43,14 +44,20 @@ #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_LOW_PSIG 3.5F ///< Spent fill complete pressure (PSI) when Diener 1000 and Qd <= 200. #define SPENT_FILL_COMPLETE_PRES_QD_MID_PSIG 15.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 QD_THRESHOLD_LOW_MLPM 200.0F ///< Qd threshold (mL/min) below which low pressure limit applies. +#define SPENT_FILL_COMPLETE_QD_SLOPE_MAX_MLPM 300.0F ///< For Qd <= 300 mL/min, spent fill completion uses pressure-rise slope detection. +#define SPENT_FILL_COMPLETE_QD_TWO_HIT_MAX_MLPM 150.0F ///< For Qd <= 150 mL/min, require two rise detections; above this and <=300, one rise detection is enough. +#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_SLOPE_SPENT_PRESSURE_LOW_PSIG 0.5F ///< Below this absolute spent pressure, use Qd-based required rise hit count. +#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 0.20F ///< 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 BAL_CHAMBER_FILL_TIMEOUT_FACTOR 1.5 ///< Balancing Chamber fill timeout factor (150% of observed fill count) /// Payload record structure for balancing chamber switch only request @@ -68,12 +75,14 @@ static U32 balChamberValveClosePeriod; ///< Close balancing chamber valves with the defined time prior switching state. static U32 currentBalChamberSwitchingCounter; ///< Counter (in task interval) to monitor the timing spent during balancing chamber fill/drain operation. static BOOL isBalChamberFillInProgress; ///< Flag indicating balancing chamber fill/drain is in progress. -static BOOL isPressureStabilizedDuringFill; ///< Flag indicating that the pressure is stablized due to fill complete. +static BOOL isPressureStabilizedDuringFill; ///< Flag indicating that the pressure is stabilized due to fill complete. static BAL_CHAMBER_SW_STATE_T balChamberSWState; ///< Current balancing chamber switching state ( state 1 or state 2). static U32 balChamberDataPublicationTimerCounter; ///< Used to schedule balancing chamber data publication to CAN bus. static U32 balChamberFillPressureDropCounter; ///< Counter to check balancing chamber valves opened and there by pressure drop is seen. static OVERRIDE_U32_T balChamberDataPublishInterval; ///< Balancing chamber data publish interval. -static BOOL balanceChamberSwitchingOnly; ///< Balancing chamber switching without any pressure check and dosing delivery. +static BOOL balanceChamberSwitchingOnly; ///< Balancing chamber switching without any pressure check and dosing delivery +static BOOL balanceChamberSwitchingOnlyOnRequested; ///< Flag indicating that a request was made to activate balancing chamber switching only +static BOOL balanceChamberSwitchingOnlyOffRequested; ///< Flag indicating that a request was made to deactivate balancing chamber switching only static BOOL isPressureDroppedDuringFill; ///< Flag indicating that the pressure is dropped due to BC fill (state 1 or state 2) in progress condition. static BOOL isFirstCycleBCSwitchingCompleted; ///< Flag indicating that first time balancing chamber swithcing is done to trigger alarms from next cycle onwards. static U32 balChamberFillCompleteStablePressureCounter; ///< Counter to check balancing chamber fill complete and stable pressure is met. @@ -86,7 +95,17 @@ 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 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). +static U32 spentFillRiseMissCounter; ///< Missed samples after a rise hit before resetting hit count. +static BOOL isBalChamberSwitchingActive; ///< Flag indicating balancing chamber switching is active or not. +static BOOL isBalChamberSwitchingOnRequested; ///< Flag indicating that a request was made to activate balancing chamber switching. +static BOOL isBalChamberSwitchingOffRequested; ///< Flag indicating that a request was made to deactivate balancing chamber switching. +//TODO: remove later once level sensor working +static U32 bicarbChamberPeriodicFillCounter; + // ********** private function prototypes ********** static BAL_CHAMBER_EXEC_STATE_T handleBalChamberState1FillStart( void ); @@ -96,9 +115,11 @@ static BAL_CHAMBER_EXEC_STATE_T handleBalChamberState2FillStart( void ); static BAL_CHAMBER_EXEC_STATE_T handleBalChamberState2ValvesClose( void ); static BAL_CHAMBER_EXEC_STATE_T handleBalChamberState2FillEnd(void); +static BAL_CHAMBER_EXEC_STATE_T handleBalChamberStateIdle( void ); static void publishBalChamberData( void ); static U32 getBalChamberDataPublishInterval( void ); static void checkSpentFillComplete( F32 spentDialPressure ); +static BOOL isSpentFillCompleteByPressure( F32 spentDialPressure, F32 qdMlpm, F32 spentFillCompletePresPsig ); /*********************************************************************//** * @brief @@ -109,7 +130,7 @@ *************************************************************************/ void initBalanceChamber( void ) { - balChamberExecState = BAL_CHAMBER_STATE_START; + balChamberExecState = BAL_CHAMBER_STATE_IDLE; balChamberSWState = BAL_CHAMBER_SW_STATE1; balChamberSwitchingFreq.data = 0.0F; balChamberSwitchingFreq.ovData = 0.0F; @@ -128,10 +149,11 @@ bicarbDoseVolume.ovInitData = 0.0F; bicarbDoseVolume.override = OVERRIDE_RESET; balanceChamberSwitchingOnly = FALSE; + balanceChamberSwitchingOnlyOnRequested = FALSE; + balanceChamberSwitchingOnlyOffRequested = FALSE; balChamberSwitchingPeriod = 0; balChamberValveClosePeriod = 0; isBalChamberFillInProgress = FALSE; - currentBalChamberSwitchingCounter = 0; isPressureStabilizedDuringFill = FALSE; lastTdDialysateFlowrate = 0.0F; balChamberDataPublicationTimerCounter = 0; @@ -141,10 +163,20 @@ isPressureDroppedDuringFill = FALSE; freshDialPressure = 0.0F; spentDialPressure = 0.0F; - currentBalChamberFillCounter = 0; balChamberFillTimeoutCount = 0; diffSpentFillCompleteCount = 0; + lastPrevSpentDialPressure = 0.0F; + prevSpentDialPressure = 0.0F; + currentBalChamberSwitchingCounter = 0; + currentBalChamberFillCounter = 0; + spentFillRiseHitCount = 0; + spentFillRiseMissCounter = 0; isSpentFillComplete = FALSE; + isBalChamberSwitchingActive = FALSE; + isBalChamberSwitchingOnRequested = FALSE; + isBalChamberSwitchingOffRequested = FALSE; + //TODO:remove once level sensor working + bicarbChamberPeriodicFillCounter = 0; } /*********************************************************************//** @@ -199,6 +231,15 @@ //Testing balChamberValveClosePeriod = balChamberSwitchingPeriod; balChamberValveClosePeriod -= 1; // Close valves prior 50 msecond for testing + + currentBalChamberFillCounter = 0; + currentBalChamberSwitchingCounter = 0; + spentFillRiseHitCount = 0; + spentFillRiseMissCounter = 0; + isSpentFillComplete = FALSE; + spentDialPressure = getFilteredPressure( D51_PRES ); + lastPrevSpentDialPressure = spentDialPressure; + prevSpentDialPressure = spentDialPressure; } } @@ -212,15 +253,33 @@ *************************************************************************/ U32 execBalancingChamberControl( void ) { + updateBalChamberSwitchingPeriod(); + // Increment counter indicating fill is in progress. currentBalChamberSwitchingCounter += 1; currentBalChamberFillCounter += 1; - // execute current balancing chamber exec state + if ( getTestConfigStatus( TEST_CONFIG_DD_ENABLE_DRY_BICARB ) == TRUE ) + { + // Increment counter for dry bicarb chamber fill + bicarbChamberPeriodicFillCounter += 1; + // Fill bicarb chamber once every 60secs. + if ( bicarbChamberPeriodicFillCounter >= BICARB_CHAMBER_PERIODIC_FILL_TIME ) + { + if ( FALSE == setBicarbChamberFillRequested() ) + { + // TODO + //drybicart state machines are combined and so this is not an issue in future + //set alarm ? + } + bicarbChamberPeriodicFillCounter = 0; + } + } + switch ( balChamberExecState ) { - case BAL_CHAMBER_STATE_START: - balChamberExecState = BAL_CHAMBER_STATE1_FILL_START; + case BAL_CHAMBER_STATE_IDLE: + balChamberExecState = handleBalChamberStateIdle(); break; case BAL_CHAMBER_STATE1_FILL_START: @@ -257,7 +316,7 @@ default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_BAL_CHAMBER_INVALID_EXEC_STATE, balChamberExecState ) - balChamberExecState = BAL_CHAMBER_STATE_START; + balChamberExecState = BAL_CHAMBER_STATE_IDLE; break; } @@ -269,6 +328,26 @@ /*********************************************************************//** * @brief + * The requestBalChamberSwitching function activates or deactivates balancing chamber switching. + * @details \b Inputs: none + * @details \b Outputs: isBalChamberSwitchingOnRequested, isBalChamberSwitchingOffRequested + * @param activate - TRUE to activate and FALSE to deactivate. + * @return none. + *************************************************************************/ +void requestBalChamberSwitching( BOOL activate ) +{ + if ( TRUE == activate ) + { + isBalChamberSwitchingOnRequested = TRUE; + } + else + { + isBalChamberSwitchingOffRequested = TRUE; + } +} + +/*********************************************************************//** + * @brief * The valveControlForBCState1FillStart function actuates the valve combination * for state 1 fill/drain process. * @details \b Inputs: none @@ -429,20 +508,35 @@ 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; balChamberFillTimeoutCount = 0; isBalChamberFillInProgress = FALSE; isPressureStabilizedDuringFill = FALSE; isPressureDroppedDuringFill = FALSE; + lastPrevSpentDialPressure = 0.0F; + prevSpentDialPressure = 0.0F; + currentBalChamberSwitchingCounter = 0; + currentBalChamberFillCounter = 0; + spentFillRiseHitCount = 0; + spentFillRiseMissCounter = 0; isSpentFillComplete = FALSE; - F32 acidVolume = getF32OverrideValue( &acidDoseVolume ); - F32 bicarbVolume = getF32OverrideValue( &bicarbDoseVolume ); - freshDialPressure = getFilteredPressure( D18_PRES ); - spentDialPressure = getFilteredPressure( D51_PRES ); + F32 acidVolume = getF32OverrideValue( &acidDoseVolume ); + F32 bicarbVolume = getF32OverrideValue( &bicarbDoseVolume ); + freshDialPressure = getFilteredPressure( D18_PRES ); + spentDialPressure = getFilteredPressure( D51_PRES ); + if ( getTestConfigStatus( TEST_CONFIG_DD_FP_ENABLE_BETA_1_0_HW ) == TRUE ) + { + //Set Trimmer heater Target temp every BC cycle to catch up fresh dialysate temp + setHeaterTargetTemperature( D45_HEAT, getFilteredTemperatureValue( D4_TEMP ) ); + } + else + { + //Set Trimmer heater Target temp every BC cycle to catch up fresh dialysate temp + setHeaterTargetTemperature( D45_HEAT, getFilteredTemperatureValue( D99_TEMP ) ); + } + // Check fresh dialysate pressure in range or BC switch only flag set or BC pressure alarms are disabled if ( ( ( freshDialPressure >= FRESH_DIAL_PRESSURE_MIN_PSIG ) && ( freshDialPressure <= FRESH_DIAL_PRESSURE_MAX_PSIG ) ) || ( TRUE == getBalChamberSwitchingOnlyStatus() ) || ( TRUE == getTestConfigStatus( TEST_CONFIG_DD_DISABLE_BC_PRESSURE_ALARMS ) ) ) @@ -471,6 +565,9 @@ { //Alarm when pressure is not in range SET_ALARM_WITH_2_F32_DATA( ALARM_ID_DD_BC_STATE1_FILL_START_PRESSURE_OUT_OF_RANGE, freshDialPressure, spentDialPressure ); + + // Move to the idle state + state = BAL_CHAMBER_STATE_IDLE; } } @@ -488,8 +585,10 @@ static BAL_CHAMBER_EXEC_STATE_T handleBalChamberConcentrateControl( void ) { BAL_CHAMBER_EXEC_STATE_T state; - freshDialPressure = getFilteredPressure( D18_PRES ); - spentDialPressure = getFilteredPressure( D51_PRES ); + freshDialPressure = getFilteredPressure( D18_PRES ); + spentDialPressure = getFilteredPressure( D51_PRES ); + lastPrevSpentDialPressure = prevSpentDialPressure; + prevSpentDialPressure = spentDialPressure; if ( BAL_CHAMBER_SW_STATE1 == balChamberSWState ) { @@ -517,11 +616,14 @@ if ( BAL_CHAMBER_SW_STATE1 == balChamberSWState ) { - state = BAL_CHAMBER_STATE1_VALVES_CLOSE; + // Low-Qd spent slope: baseline = spent pressure on last dosing tick (this task period) before VALVES_CLOSE. + prevSpentDialPressure = spentDialPressure; + state = BAL_CHAMBER_STATE1_VALVES_CLOSE; } else { - state = BAL_CHAMBER_STATE2_VALVES_CLOSE; + prevSpentDialPressure = spentDialPressure; + state = BAL_CHAMBER_STATE2_VALVES_CLOSE; } } @@ -549,6 +651,9 @@ if ( ( balChamberFillTimeoutCount > 0 ) && ( currentBalChamberFillCounter > balChamberFillTimeoutCount ) ) { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_BC_FILL_TIMEOUT_FAULT, currentBalChamberFillCounter, balChamberFillTimeoutCount ); + + // Move to the idle state + state = BAL_CHAMBER_STATE_IDLE; } // Check fresh dialysate pressure back in range to indicate fresh fill complete. @@ -567,18 +672,19 @@ // Check both spent and fresh side fill is complete isBothFillsComplete = ( TRUE == isSpentFillComplete ) && ( TRUE == isPressureStabilizedDuringFill ); isFirstCycleNotDone = ( FALSE == isFirstCycleBCSwitchingCompleted ); - isFillCompleteOrFirstCycle = isBothFillsComplete || isFirstCycleNotDone; - if ( TRUE == isFillCompleteOrFirstCycle ) + if ( FALSE == getBalChamberSwitchingOnlyStatus() ) { + isFillCompleteOrFirstCycle = isBothFillsComplete || isFirstCycleNotDone; + } + + if ( ( TRUE == isFillCompleteOrFirstCycle ) || + ( ( currentBalChamberSwitchingCounter >= balChamberValveClosePeriod ) && ( TRUE == getBalChamberSwitchingOnlyStatus() ) ) ) + { // close the state 1 opened valves valveControlForBCState1FillEnd(); isBalChamberFillInProgress = FALSE; - //setDialysatePumpTargetRPM( D48_PUMP, getD48PumpSpeedForBCFill(), TRUE ); - if ( TRUE == isFirstCycleBCSwitchingCompleted ) - { - setDialysatePumpTargetRPM( D48_PUMP, getD48PumpSpeedForBCFill(), TRUE ); - } + setDialysatePumpTargetRPM( D48_PUMP, getD48PumpSpeedForBCFill(), TRUE ); // Transition to next state state = BAL_CHAMBER_STATE1_FILL_END; @@ -591,8 +697,11 @@ * @brief * The handleBalChamberState1FillEnd function check for the balancing chamber * switching period and transition to next state switching. - * @details \b Inputs: currentBalChamberSwitchingCounter, balChamberSwitchingPeriod + * @details \b Inputs: currentBalChamberSwitchingCounter, balChamberSwitchingPeriod, + * isBalChamberSwitchingOffRequested, balanceChamberSwitchingOnlyOffRequested * @details \b Outputs: balChamberFillPressureDropCounter,balChamberFillCompleteStablePressureCounter + * isBalChamberSwitchingOnRequested, isBalChamberSwitchingOffRequested, isBalChamberSwitchingActive, + * balanceChamberSwitchingOnlyOffRequested * @details \b Alarm: ALARM_ID_DD_BC_STATE1_FILL_PRESSURE_DROP_OUT_OF_RANGE * 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 @@ -614,11 +723,17 @@ { // 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 ); + + // Move to the idle state + state = BAL_CHAMBER_STATE_IDLE; } 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 ); + + // Move to the idle state + state = BAL_CHAMBER_STATE_IDLE; } else { @@ -633,10 +748,42 @@ } } else + { // For Balancing Chamber Switch Only command, change state as per the switching period + if ( currentBalChamberSwitchingCounter >= balChamberSwitchingPeriod ) + { + state = BAL_CHAMBER_STATE2_FILL_START; + } + } + + // Clear the switching only on request flag. + balanceChamberSwitchingOnlyOnRequested = FALSE; + + // Check if switching only off was requested + if ( TRUE == balanceChamberSwitchingOnlyOffRequested ) { - state = BAL_CHAMBER_STATE2_FILL_START; + // Clear the Switch only off request flag + balanceChamberSwitchingOnlyOffRequested = FALSE; + + //Clear the switch only flag + setBalChamberSwitchingOnlyStatus( FALSE ); + + // Move to the idle state + state = BAL_CHAMBER_STATE_IDLE; } + // Keep Clearing the On request flag in case an On request was made while the switching is active + isBalChamberSwitchingOnRequested = FALSE; + + // Check if a request made was to deactivate the balancing chamber switching. + if ( TRUE == isBalChamberSwitchingOffRequested ) + { + //Clear the request flag to indicate that the request was processed. + isBalChamberSwitchingOffRequested = FALSE; + + // Move to the idle state + state = BAL_CHAMBER_STATE_IDLE; + } + return state; } @@ -653,20 +800,35 @@ static BAL_CHAMBER_EXEC_STATE_T handleBalChamberState2FillStart( void ) { BAL_CHAMBER_EXEC_STATE_T state = BAL_CHAMBER_STATE2_FILL_START; - currentBalChamberSwitchingCounter = 0; isBalChamberFillInProgress = FALSE; isPressureStabilizedDuringFill = FALSE; isPressureDroppedDuringFill = FALSE; balChamberSWState = BAL_CHAMBER_SW_STATE2; - currentBalChamberFillCounter = 0; balChamberFillTimeoutCount = 0; + lastPrevSpentDialPressure = 0.0F; + prevSpentDialPressure = 0.0F; + currentBalChamberSwitchingCounter = 0; + currentBalChamberFillCounter = 0; + spentFillRiseHitCount = 0; + spentFillRiseMissCounter = 0; isSpentFillComplete = FALSE; - F32 acidVolume = getF32OverrideValue( &acidDoseVolume ); - F32 bicarbVolume = getF32OverrideValue( &bicarbDoseVolume ); - freshDialPressure = getFilteredPressure( D18_PRES ); - spentDialPressure = getFilteredPressure( D51_PRES ); + F32 acidVolume = getF32OverrideValue( &acidDoseVolume ); + F32 bicarbVolume = getF32OverrideValue( &bicarbDoseVolume ); + freshDialPressure = getFilteredPressure( D18_PRES ); + spentDialPressure = getFilteredPressure( D51_PRES ); + if ( getTestConfigStatus( TEST_CONFIG_DD_FP_ENABLE_BETA_1_0_HW ) == TRUE ) + { + //Set Trimmer heater Target temp every BC cycle to catch up fresh dialysate temp + setHeaterTargetTemperature( D45_HEAT, getFilteredTemperatureValue( D4_TEMP ) ); + } + else + { + //Set Trimmer heater Target temp every BC cycle to catch up fresh dialysate temp + setHeaterTargetTemperature( D45_HEAT, getFilteredTemperatureValue( D99_TEMP ) ); + } + // Check fresh dialysate pressure in range if ( ( ( freshDialPressure >= FRESH_DIAL_PRESSURE_MIN_PSIG ) && ( freshDialPressure <= FRESH_DIAL_PRESSURE_MAX_PSIG ) ) || ( TRUE == getBalChamberSwitchingOnlyStatus() ) || ( TRUE == getTestConfigStatus( TEST_CONFIG_DD_DISABLE_BC_PRESSURE_ALARMS ) ) ) @@ -695,6 +857,9 @@ { //Alarm when pressure is not in range SET_ALARM_WITH_2_F32_DATA( ALARM_ID_DD_BC_STATE2_FILL_START_PRESSURE_OUT_OF_RANGE, freshDialPressure, spentDialPressure ); + + // Move to the idle state + state = BAL_CHAMBER_STATE_IDLE; } } @@ -732,6 +897,9 @@ if ( ( balChamberFillTimeoutCount > 0 ) && ( currentBalChamberFillCounter > balChamberFillTimeoutCount ) ) { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_BC_FILL_TIMEOUT_FAULT, currentBalChamberFillCounter, balChamberFillTimeoutCount ); + + // Move to the idle state + state = BAL_CHAMBER_STATE_IDLE; } // Spent side of balancing chamber fill is complete or not @@ -740,18 +908,19 @@ // Check switching cycle time or pressure check for valve closure isBothFillsComplete = ( TRUE == isSpentFillComplete ) && ( TRUE == isPressureStabilizedDuringFill ); isFirstCycleNotDone = ( FALSE == isFirstCycleBCSwitchingCompleted ); - isFillCompleteOrFirstCycle = isBothFillsComplete || isFirstCycleNotDone; - if ( TRUE == isFillCompleteOrFirstCycle ) + if ( FALSE == getBalChamberSwitchingOnlyStatus() ) { + isFillCompleteOrFirstCycle = isBothFillsComplete || isFirstCycleNotDone; + } + + if ( ( TRUE == isFillCompleteOrFirstCycle ) || + ( ( currentBalChamberSwitchingCounter >= balChamberValveClosePeriod ) && ( TRUE == getBalChamberSwitchingOnlyStatus() ) ) ) + { // close the valves valveControlForBCState2FillEnd(); isBalChamberFillInProgress = FALSE; - //setDialysatePumpTargetRPM( D48_PUMP, getD48PumpSpeedForBCFill(), TRUE ); - if ( TRUE == isFirstCycleBCSwitchingCompleted ) - { - setDialysatePumpTargetRPM( D48_PUMP, getD48PumpSpeedForBCFill(), TRUE ); - } + setDialysatePumpTargetRPM( D48_PUMP, getD48PumpSpeedForBCFill(), TRUE ); //Transition to next state state = BAL_CHAMBER_STATE2_FILL_END; @@ -764,8 +933,11 @@ * @brief * The handleBalChamberState2FillEnd function check for the balancing chamber * switching period complete and transition to next state. - * @details \b Inputs: currentBalChamberSwitchingCounter, balChamberSwitchingPeriod - * @details \b Outputs: isPressureStalbilizedDuringFill,isBalChamberFillInProgress + * @details \b Inputs: currentBalChamberSwitchingCounter, balChamberSwitchingPeriod, + * isBalChamberSwitchingOffRequested, balanceChamberSwitchingOnlyOffRequested + * @details \b Outputs: isPressureStabilizedDuringFill,isBalChamberFillInProgress, + * isBalChamberSwitchingOnRequested, isBalChamberSwitchingOffRequested, isBalChamberSwitchingActive, + * balanceChamberSwitchingOnlyOffRequested * @details \b Alarm: ALARM_ID_DD_BC_STATE2_FILL_PRESSURE_DROP_OUT_OF_RANGE * when pressure is not in range during balacing chamber state 2 fill in progress. * @details \b Alarm: ALARM_ID_DD_BC_STATE2_FILL_END_PRESSURE_OUT_OF_RANGE @@ -788,11 +960,17 @@ { // When fill initiated, pressure is not dropped to the expected range, possible valve failures. SET_ALARM_WITH_2_F32_DATA( ALARM_ID_DD_BC_STATE2_FILL_PRESSURE_DROP_OUT_OF_RANGE, freshDialPressure, spentDialPressure ); + + // Move to the idle state + state = BAL_CHAMBER_STATE_IDLE; } else if ( TRUE != isPressureStabilizedDuringFill ) { // Alarm when switching time expired, but still pressure not in range which indicates fill is not completed. SET_ALARM_WITH_2_F32_DATA( ALARM_ID_DD_BC_STATE2_FILL_END_PRESSURE_OUT_OF_RANGE, freshDialPressure, spentDialPressure ); + + // Move to the idle state + state = BAL_CHAMBER_STATE_IDLE; } else { @@ -808,7 +986,11 @@ } else { - state = BAL_CHAMBER_STATE1_FILL_START; + // For Balancing Chamber Switch Only command, change state as per the switching period + if ( currentBalChamberSwitchingCounter >= balChamberSwitchingPeriod ) + { + state = BAL_CHAMBER_STATE1_FILL_START; + } } // Trigger pressure drop alarm after first cycle of balancing chamber switching complete @@ -817,11 +999,171 @@ isFirstCycleBCSwitchingCompleted = TRUE; } + // Clear the switching only on request flag. + balanceChamberSwitchingOnlyOnRequested = FALSE; + + // Check if switching only off was requested + if ( TRUE == balanceChamberSwitchingOnlyOffRequested ) + { + // Clear the Switch only off request flag + balanceChamberSwitchingOnlyOffRequested = FALSE; + + //Clear the switch only flag + setBalChamberSwitchingOnlyStatus( FALSE ); + + // Move to the idle state + state = BAL_CHAMBER_STATE_IDLE; + } + + // Keep Clearing the On request flag in case an On request was made while the switching is active + isBalChamberSwitchingOnRequested = FALSE; + + // Check if a request made was to deactivate the balancing chamber switching. + if ( TRUE == isBalChamberSwitchingOffRequested ) + { + // Clear the request flag to indicate that the request was processed. + isBalChamberSwitchingOffRequested = FALSE; + + // Move to the idle state + state = BAL_CHAMBER_STATE_IDLE; + } + return state; } /*********************************************************************//** * @brief + * The handleBalChamberStateIdle function handles balancing chamber idle state. + * @details \b Inputs: isBalChamberSwitchingOnRequested + * @details \b Outputs: isBalChamberSwitchingOnRequested, isBalChamberSwitchingOffRequested, + * isBalChamberSwitchingActive + * @return next balancing chamber state. + *************************************************************************/ +static BAL_CHAMBER_EXEC_STATE_T handleBalChamberStateIdle( void ) +{ + BAL_CHAMBER_EXEC_STATE_T state = BAL_CHAMBER_STATE_IDLE; + + // Clear the switching only off request flag. + balanceChamberSwitchingOnlyOffRequested = FALSE; + + // Check if the switching only on was requested + if ( TRUE == balanceChamberSwitchingOnlyOnRequested ) + { + // Clear the switching only on request flag. + balanceChamberSwitchingOnlyOnRequested = FALSE; + + // then set BC switching only flag to ignore pressure check and dosing. + setBalChamberSwitchingOnlyStatus( TRUE ); + + // Set flag to indicate that balancing chamber switching is active + isBalChamberSwitchingActive = TRUE; + + // Move to the start state1 + state = BAL_CHAMBER_STATE1_FILL_START; + } + + // Keep Clearing the off request flag in case an off request was made while we were already in the idle state + isBalChamberSwitchingOffRequested = FALSE; + + // Clear flag to indicate that balancing chamber switching is inactive + isBalChamberSwitchingActive = FALSE; + + // Check if a request made was to activate the balancing chamber switching. + if ( TRUE == isBalChamberSwitchingOnRequested ) + { + //Clear the request flag to indicate that the request was processed. + isBalChamberSwitchingOnRequested = FALSE; + + // Set flag to indicate that balancing chamber switching is active + isBalChamberSwitchingActive = TRUE; + + // Move to the start state1 + state = BAL_CHAMBER_STATE1_FILL_START; + } + + return state; +} + +/*********************************************************************//** + * @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 Outputs: spentFillRiseHitCount, spentFillRiseMissCounter, + * lastPrevSpentDialPressure, prevSpentDialPressure (updated only in slope mode). + * @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 ) +{ + BOOL useSlopeDetector = ( ( ( TRUE != getTestConfigStatus( TEST_CONFIG_DD_ENABLE_DIENER_2000_PUMP ) ) && + ( qdMlpm <= SPENT_FILL_COMPLETE_QD_SLOPE_MAX_MLPM ) ) ? TRUE : FALSE ); + 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 > SPENT_FILL_COMPLETE_DP_RISE_PSIG ) ? TRUE : FALSE; + U32 requiredRiseCount; + + // Mid band between low/high spent pressure: require two rise hits. + if ( riseDeltaPsi > SPENT_FILL_SLOPE_SPENT_PRESSURE_HIGH_PSIG ) + { + requiredRiseCount = 1U; + } + else if ( riseDeltaPsi < SPENT_FILL_SLOPE_SPENT_PRESSURE_LOW_PSIG ) + { + requiredRiseCount = ( qdMlpm <= SPENT_FILL_COMPLETE_QD_TWO_HIT_MAX_MLPM ) ? SPENT_FILL_SLOPE_MAX_RISE_HITS : 1U; + } + else + { + requiredRiseCount = 2U; + } + + if ( ( TRUE == riseHit ) && ( TRUE == isDetectionInCountWindow ) ) + { + spentFillRiseHitCount += 1; + spentFillRiseMissCounter = 0; + } + else if ( spentFillRiseHitCount > 0 ) + { + if ( ( FALSE == riseHit ) && ( TRUE == isDetectionInCountWindow ) ) + { + spentFillRiseMissCounter += 1; + + if ( spentFillRiseMissCounter >= 2 ) + { + spentFillRiseHitCount = 0; + spentFillRiseMissCounter = 0; + } + } + } + + 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; + } + 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. @@ -842,6 +1184,7 @@ F32 qdMlpm = getTDDialysateFlowrate(); BOOL result = FALSE; F32 spentFillCompletePresPsig; + BOOL isSpentFillCompleteDetected = FALSE; // 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 ) ) @@ -850,12 +1193,8 @@ } else { - if ( qdMlpm <= QD_THRESHOLD_LOW_MLPM ) + if ( qdMlpm <= QD_THRESHOLD_HIGH_MLPM ) { - spentFillCompletePresPsig = SPENT_FILL_COMPLETE_PRES_QD_LOW_PSIG; - } - else if ( qdMlpm <= QD_THRESHOLD_HIGH_MLPM ) - { spentFillCompletePresPsig = SPENT_FILL_COMPLETE_PRES_QD_MID_PSIG; } else @@ -864,32 +1203,42 @@ } } - if ( ( spentDialPressure >= spentFillCompletePresPsig ) && ( isSpentFillComplete != TRUE ) ) + isSpentFillCompleteDetected = isSpentFillCompleteByPressure( spentDialPressure, qdMlpm, spentFillCompletePresPsig ); + + if ( ( TRUE == isSpentFillCompleteDetected ) && ( 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; - - result = ( ( diffSpentFillCompleteCount <= 0 ) && ( diffSpentFillCompleteCount >= -2 ) ) ? TRUE : FALSE; - //Not adjust the D48 pump speed if the fill counter is just 50ms difference from the closing period (considering only positive DEADBAND as negative deadband might indicate under fill of BC). - if ( FALSE == result ) + if ( balChamberValveClosePeriod > 0 ) { - if ( diffSpentFillCompleteCount < SPENT_DIFF_COUNT_ZERO ) + diffSpentFillCompleteCount = balChamberValveClosePeriod - currentBalChamberFillCounter; + absDiffSpentFillCount = abs(diffSpentFillCompleteCount); + adjustedSpeed = ( ( (F32)absDiffSpentFillCount / (F32)balChamberValveClosePeriod ) * spentDialPumpSpeed ) * D48_SPEED_ADJUST_FACTOR; + + // Skip D48 trim in deadband: diff 0..-2 counts (0..100 ms at 50 ms/task); positive band only (larger negative may mean under-fill). + result = ( ( diffSpentFillCompleteCount <= 0 ) && ( diffSpentFillCompleteCount >= -2 ) ) ? TRUE : FALSE; + + if ( FALSE == result ) { - // Increase the D48 pump speed - spentDialPumpSpeed += adjustedSpeed; - } - else - { - //Decrease the D48 pump speed - spentDialPumpSpeed -= adjustedSpeed; - } + 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 ); + d48SpeedPostRangeCheck = RANGE( spentDialPumpSpeed, minD48Speed, maxD48Speed ); - // Update the D48 pump speed - setD48PumpSpeedForBCFill( d48SpeedPostRangeCheck ); + // Do not turn on the pump if the switching only is enabled in the standby mode. + if ( FALSE == getBalChamberSwitchingOnlyStatus() ) + { + // Update the D48 pump speed + setD48PumpSpeedForBCFill( d48SpeedPostRangeCheck ); + } + } } //Update spent fill is complete @@ -1028,6 +1377,7 @@ data.currentBalChamberSwitchingCounter = currentBalChamberFillCounter; data.isPressureStabilizedDuringFill = isPressureStabilizedDuringFill; data.balChamberSWOnlyState = balanceChamberSwitchingOnly; + data.isBalChamberSwitchingActive = isBalChamberSwitchingActive; broadcastData( MSG_ID_DD_BAL_CHAMBER_DATA, COMM_BUFFER_OUT_CAN_DD_BROADCAST, (U08*)&data, sizeof( BAL_CHAMBER_DATA_T ) ); @@ -1115,7 +1465,6 @@ * chamber switching only without dosing delivery and pressure check condition. * @details \b Inputs: tester logged in * @details \b Outputs: tdDialysateFlowrate,balanceChamberSwitchingOnly - * pendingBalanceChamberSwOnlyRequest * @param message set message from Dialin which includes the balancing chamber * to start/stop switching and update switching rate based on the flow rate. * @return TRUE if set request is successful, FALSE if not @@ -1124,7 +1473,7 @@ { BOOL result = FALSE; - // Verify tester has logged in with TD + // Verify tester has logged in with DD if ( TRUE == isTestingActivated() ) { // Verify payload length is valid @@ -1142,17 +1491,18 @@ setTDDialysateFlowrate( payload.flowrate ); // update switching rate transitionToBalChamberFill(); - // then set BC switching only flag to ignore pressure check and dosing. - setBalChamberSwitchingOnlyStatus( TRUE ); + // Set the flag to indicate that a request was made to start the switching only + balanceChamberSwitchingOnlyOnRequested = TRUE; // Now, if the current operating mode is standby mode idle state, execute the balancing chamber switching only control result = requestBCSwitchingOnlyStart(); } //Handle stop command if ( FALSE == payload.startStop ) { - //Reset the flag - setBalChamberSwitchingOnlyStatus( FALSE ); + // Set the flag to indicate that a request was made to stop the switching only + balanceChamberSwitchingOnlyOffRequested = TRUE; + //Stop the BC switching execution only control requestBCSwitchingOnlyStop(); result = TRUE; Index: firmware/App/Controllers/Heaters.c =================================================================== diff -u -rb689a4627e86e263cb69e83b91e80802bf411c92 -r4cdcb589ad34efa472c94180e6de938132cbc89b --- firmware/App/Controllers/Heaters.c (.../Heaters.c) (revision b689a4627e86e263cb69e83b91e80802bf411c92) +++ firmware/App/Controllers/Heaters.c (.../Heaters.c) (revision 4cdcb589ad34efa472c94180e6de938132cbc89b) @@ -7,8 +7,8 @@ * * @file Heaters.c * -* @author (last) Vinayakam Mani -* @date (last) 03-Mar-2026 +* @author (last) Michael Garthwaite +* @date (last) 30-Mar-2026 * * @author (original) Vinayakam Mani * @date (original) 11-Oct-2024 @@ -18,6 +18,7 @@ #include // Used for mathematical operations #include "Conductivity.h" +#include "ConductivityTeensy.h" #include "FpgaDD.h" #include "Heaters.h" #include "Level.h" @@ -518,11 +519,17 @@ if ( ++primaryTargetTempAdjCounter >= d5OuterLoopControlInterval ) { F32 targetTempfromTD = getTDTargetDialysateTemperature(); -#ifdef __TEENSY_CONDUCTIVITY_DRIVER__ - F32 measuredTempAtDialyzer = getTeensyConductivityTemperatureValue( D27_COND ); -#else - F32 measuredTempAtDialyzer = getFilteredConductivitySensorTemperature( D27_COND ); -#endif + F32 measuredTempAtDialyzer = 0.0F; + + if ( getTestConfigStatus( TEST_CONFIG_DD_FP_ENABLE_BETA_2_0_HW ) != TRUE ) + { + measuredTempAtDialyzer = getTeensyConductivityTemperatureValue( D27_COND ); + } + else + { + measuredTempAtDialyzer = getConductivityTemperature( D27_COND ); + } + F32 calcTargetTemp = getHeaterTargetTemperature( D5_HEAT ); F32 dialysateFlowrate = getTDDialysateFlowrate(); F32 deltaTempC = targetTempfromTD - measuredTempAtDialyzer; Index: firmware/App/Controllers/RinsePump.c =================================================================== diff -u -r60db0506b1a90ed00b0e83159f6e7510ab6e4b7b -r4cdcb589ad34efa472c94180e6de938132cbc89b --- firmware/App/Controllers/RinsePump.c (.../RinsePump.c) (revision 60db0506b1a90ed00b0e83159f6e7510ab6e4b7b) +++ firmware/App/Controllers/RinsePump.c (.../RinsePump.c) (revision 4cdcb589ad34efa472c94180e6de938132cbc89b) @@ -8,7 +8,7 @@ * @file RinsePump.c * * @author (last) Jashwant Gantyada -* @date (last) 20-Feb-2026 +* @date (last) 02-Apr-2026 * * @author (original) Vinayakam Mani * @date (original) 02-Oct-2025 @@ -64,6 +64,7 @@ static RINSE_PUMP_STATE_T handleRinsePumpOffState( void ); static RINSE_PUMP_STATE_T handleRinsePumpOnState ( void ); +static void monitorRinsePumpSpeed( void ); static void publishRinsePumpData( void ); static void setRinsePumpPwmCount( RINSE_PUMP_ID_T pumpId, U32 pwmCount ); Index: firmware/App/Controllers/Ultrafiltration.c =================================================================== diff -u -r623587d96bf6c0bafd3ff94d47f8c783b4b999f0 -r4cdcb589ad34efa472c94180e6de938132cbc89b --- firmware/App/Controllers/Ultrafiltration.c (.../Ultrafiltration.c) (revision 623587d96bf6c0bafd3ff94d47f8c783b4b999f0) +++ firmware/App/Controllers/Ultrafiltration.c (.../Ultrafiltration.c) (revision 4cdcb589ad34efa472c94180e6de938132cbc89b) @@ -249,13 +249,13 @@ // Fresh side dialysate density if ( getTestConfigStatus( TEST_CONFIG_DD_FP_ENABLE_BETA_1_0_HW ) == TRUE ) { - freshDensity = ( COMP_SLOPE * getD4AverageTemperature() ) + COMP_INTERCEPT; + freshDensity = ( COMP_SLOPE * getFilteredTemperatureValue( D4_TEMP ) ) + COMP_INTERCEPT; } else { - freshDensity = ( COMP_SLOPE * getD99AverageTemperature() ) + COMP_INTERCEPT; + freshDensity = ( COMP_SLOPE * getFilteredTemperatureValue( D99_TEMP ) ) + COMP_INTERCEPT; } - spentDensity = ( COMP_SLOPE * getD50AverageTemperature() ) + COMP_INTERCEPT; // spent side dialysate density + spentDensity = ( COMP_SLOPE * getFilteredTemperatureValue( D50_TEMP ) ) + COMP_INTERCEPT; // spent side dialysate density compFreshFlowrate = getTDDialysateFlowrate() * freshDensity; // Qd * fresh density compSpentFlowrate = getTDDialysateFlowrate() * spentDensity; // Qd * spent density balancingError = compFreshFlowrate - compSpentFlowrate; // Error in g/min Index: firmware/App/DDCommon.h =================================================================== diff -u -r60db0506b1a90ed00b0e83159f6e7510ab6e4b7b -r4cdcb589ad34efa472c94180e6de938132cbc89b --- firmware/App/DDCommon.h (.../DDCommon.h) (revision 60db0506b1a90ed00b0e83159f6e7510ab6e4b7b) +++ firmware/App/DDCommon.h (.../DDCommon.h) (revision 4cdcb589ad34efa472c94180e6de938132cbc89b) @@ -7,8 +7,8 @@ * * @file DDCommon.h * -* @author (last) Jashwant Gantyada -* @date (last) 13-Mar-2026 +* @author (last) Michael Garthwaite +* @date (last) 14-Apr-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 58 +#define DD_VERSION_BUILD 70 // ********** development build switches ********** @@ -51,9 +51,6 @@ //Uncomment below to disable heaters debug message #define __HEATERS_DEBUG__ 1 -//Uncomment below to disable Teensy conductivity driver -#define __TEENSY_CONDUCTIVITY_DRIVER__ 1 - //Uncomment below to disable revised heater model #define __REVISED_HEATER_MODEL__ 1 Index: firmware/App/Modes/ModeGenDialysate.c =================================================================== diff -u -ra569e34b86e389ce6d6f4c971ea86d2690dbaaed -r4cdcb589ad34efa472c94180e6de938132cbc89b --- firmware/App/Modes/ModeGenDialysate.c (.../ModeGenDialysate.c) (revision a569e34b86e389ce6d6f4c971ea86d2690dbaaed) +++ firmware/App/Modes/ModeGenDialysate.c (.../ModeGenDialysate.c) (revision 4cdcb589ad34efa472c94180e6de938132cbc89b) @@ -14,6 +14,7 @@ * @date (original) 06-Nov-2024 * ***************************************************************************/ + #include // For ceilf #include "BalancingChamber.h" @@ -56,15 +57,10 @@ #define DIALYSATE_TEMP_UPPER_SAFETY_LIMIT_C 42.0F ///< Dialysate upper bound safety temperature limit in C. #define DIALYSATE_TEMP_LOWER_SAFETY_LIMIT_C 33.0F ///< Dialysate lower bound safety temperature limit in C. #define DIALYSATE_TEMP_CLEAR_TIMEOUT_MS ( 10 * MS_PER_SECOND ) ///< Dialysate temperature clear persistence timeout. -#define QUAD_FIRST_COEFFICIENT 0.0006F ///< First coefficient used in adjusted dialysate temperature quadratic calculation for low Qds -#define QUAD_SECOND_COEFFICIENT -0.1743F ///< Second coefficient used in adjusted dialysate temperature quadratic calculation for low Qds -#define QUAD_THIRD_COEFFICIENT 17.3F ///< Third coefficient used in adjusted dialysate temperature quadratic calculation for low Qds -#define LINEAR_SLOPE_FACTOR -0.0029F ///< Slope factor used in adjusted dialysate temperature linear calculation for high Qds -#define LINEAR_INTERCEPT_FACTOR 3.47F ///< Intercept factor used in adjusted dialysate temperature linear calculation for high Qds -#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). @@ -108,7 +104,6 @@ static DD_GEND_MODE_STATE_T handleGenDBicarbChamberFillState( void ); static DD_GEND_MODE_STATE_T handleGenDIsolatedUFState( void ); static F32 getGenDialysateTargetTemperature( void ); -static void calculateTargetDialysateTemp( void ); static void updateDialysateToDialyzerFlowRate( void ); static void checkDialysateTemperature( void ); static void monitorChamberLevelStatus( void ); @@ -276,11 +271,11 @@ //Turn on Trimmer heater if ( getTestConfigStatus( TEST_CONFIG_DD_FP_ENABLE_BETA_1_0_HW ) == TRUE ) { - setHeaterTargetTemperature( D45_HEAT, getD4AverageTemperature() ); + setHeaterTargetTemperature( D45_HEAT, getFilteredTemperatureValue( D4_TEMP ) ); } else { - setHeaterTargetTemperature( D45_HEAT, getD99AverageTemperature() ); + setHeaterTargetTemperature( D45_HEAT, getFilteredTemperatureValue( D99_TEMP ) ); } startHeater( D45_HEAT ); @@ -325,11 +320,11 @@ //Turn on Trimmer heater if ( getTestConfigStatus( TEST_CONFIG_DD_FP_ENABLE_BETA_1_0_HW ) == TRUE ) { - setHeaterTargetTemperature( D45_HEAT, getD4AverageTemperature() ); + setHeaterTargetTemperature( D45_HEAT, getFilteredTemperatureValue( D4_TEMP ) ); } else { - setHeaterTargetTemperature( D45_HEAT, getD99AverageTemperature() ); + setHeaterTargetTemperature( D45_HEAT, getFilteredTemperatureValue( D99_TEMP ) ); } startHeater( D45_HEAT ); @@ -660,37 +655,6 @@ /*********************************************************************//** * @brief - * The calculateTargetDialysateTemp function calculate the delta temperature - * required for dialysate temperature to meet the set temperature at dialyzer. - * @details \b Inputs: Qd and target temperature. - * @details \b Outputs: Adjusted Target temperature - * @return none. - *************************************************************************/ -static void calculateTargetDialysateTemp( void ) -{ - // Get the dialysate flow rate from TD - F32 dialFlowrate = getTDDialysateFlowrate(); - F32 deltaTemp = 0.0F; - - if ( dialFlowrate >= LOW_DIAL_FLOW_RATE ) - { - // linear relationship seen against high dialysate flowrate Vs DeltaTemp - // deltaTemp = (-0.0029 * Qd) + 3.47 - deltaTemp = ( LINEAR_SLOPE_FACTOR * dialFlowrate ) + LINEAR_INTERCEPT_FACTOR; - } - else - { - // deltaTemp = (0.0006 * Qd * Qd)-(0.1743*Qd) + 17.3 - deltaTemp = ( QUAD_FIRST_COEFFICIENT * dialFlowrate * dialFlowrate ) + - ( QUAD_SECOND_COEFFICIENT * dialFlowrate ) + QUAD_THIRD_COEFFICIENT; - } - - // Adjust the D4 target temperature - targetHydChamberFluidTemp.data = getTDTargetDialysateTemperature() + deltaTemp; -} - -/*********************************************************************//** - * @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. @@ -1003,6 +967,20 @@ /*********************************************************************//** * @brief + * The setTargetHydChamberTemp function updates the target hydraulics temperature + * for D5 heater control + * @details \b Inputs: none + * @details \b Outputs: targetHydChamberFluidTemp + * @param temperature The target temperature for D5 heater control + * @return none + *************************************************************************/ +void setTargetHydChamberTemp( F32 temperature ) +{ + targetHydChamberFluidTemp.data = temperature; +} + +/*********************************************************************//** + * @brief * The updateTreatmentSettings function updates the switching rate post the * treatement parameters updated during treatement. * @details \b Inputs: isTreatmentParamUpdated @@ -1236,8 +1214,8 @@ * @brief * The testDialDeliveryInProgressOverride function sets the override value * of the dialysate delivery In progress flag. - * @details Inputs: isDialDeliveryInProgress - * @details Outputs: isDialDeliveryInProgress + * @details \b Inputs: isDialDeliveryInProgress + * @details \b Outputs: isDialDeliveryInProgress * @param message Override message from Dialin which includes the override * value to override the dialysate delivery in progress flag. * @return TRUE if override successful, FALSE if not @@ -1253,8 +1231,8 @@ * @brief * The testDialGoodToDeliverStatusOverride function sets the override value * of the dialysate good to deliver status flag. - * @details Inputs: isDialysateGoodtoDeliver - * @details Outputs: isDialysateGoodtoDeliver + * @details \b Inputs: isDialysateGoodtoDeliver + * @details \b Outputs: isDialysateGoodtoDeliver * @param message Override message from Dialin which includes the override * value to override the dialysate delivery in progress flag. * @return TRUE if override successful, FALSE if not Index: firmware/App/Modes/ModeGenDialysate.h =================================================================== diff -u -r1bea59c13158a871b80e76d75a2a1a579ae67434 -r4cdcb589ad34efa472c94180e6de938132cbc89b --- firmware/App/Modes/ModeGenDialysate.h (.../ModeGenDialysate.h) (revision 1bea59c13158a871b80e76d75a2a1a579ae67434) +++ firmware/App/Modes/ModeGenDialysate.h (.../ModeGenDialysate.h) (revision 4cdcb589ad34efa472c94180e6de938132cbc89b) @@ -8,7 +8,7 @@ * @file ModeGenDialysate.h * * @author (last) Jashwant Gantyada -* @date (last) 13-Mar-2026 +* @date (last) 06-Apr-2026 * * @author (original) Vinayakam Mani * @date (original) 06-Nov-2024 @@ -62,7 +62,6 @@ BOOL requestDDGenDialyasteStop( void ); // Stop generate dialysate U32 getD48PumpSpeedForBCFill( void ); // Get D48 pump speed void setD48PumpSpeedForBCFill( U32 pumpSpeed ); // Set D48 pump speed -U32 calculateD48PumpSpeedForBCFill( void ); // Calculate and store initial D48 pump speed U32 getCalculatedD48PumpSpeedForBCFill( void ); // Get nominal D48 speed from formula only BOOL testDDGenDialysateDataPublishIntervalOverride( MESSAGE_T *message ); // GenD Mode data publish interval override Index: firmware/App/Monitors/Temperature.c =================================================================== diff -u -rb689a4627e86e263cb69e83b91e80802bf411c92 -r4cdcb589ad34efa472c94180e6de938132cbc89b --- firmware/App/Monitors/Temperature.c (.../Temperature.c) (revision b689a4627e86e263cb69e83b91e80802bf411c92) +++ firmware/App/Monitors/Temperature.c (.../Temperature.c) (revision 4cdcb589ad34efa472c94180e6de938132cbc89b) @@ -8,19 +8,14 @@ * @file Temperature.c * * @author (last) Michael Garthwaite -* @date (last) 06-Mar-2026 +* @date (last) 07-Apr-2026 * * @author (original) Vinayakam Mani * @date (original) 25-Sep-2024 * ***************************************************************************/ #include "BalancingChamber.h" -#ifdef __TEENSY_CONDUCTIVITY_DRIVER__ -#include "ConductivityTeensy.h" -#else -#include "ConductivitySensors.h" -#endif #include "Conductivity.h" #include "Flow.h" #include "Messaging.h" @@ -340,6 +335,8 @@ { DIAL_TEMPERATURE_SENSORS_T i; F32 temperatureC = 0.0F; + U32 currentIndex = 0; + F32 prevSampleToRemoveC = 0.0f; for ( i = DIAL_TEMP_FIRST; i < NUM_OF_DIAL_TEMPS; i++ ) { @@ -352,22 +349,18 @@ { CONDUCTIVITY_SENSORS_T sensor = ( DIAL_TEMP_D28 == i ? D27_COND : D29_COND ); -#ifdef __TEENSY_CONDUCTIVITY_DRIVER__ - - if (sensor != D74_COND) + if ( getTestConfigStatus( TEST_CONFIG_DD_FP_ENABLE_BETA_2_0_HW ) != TRUE ) { temperatureC = getTeensyConductivityTemperatureValue( sensor ); } else { - temperatureC = getConductivityTemperatureValue( sensor ); + temperatureC = getConductivityTemperature( sensor ); } -#else - temperatureC = getConductivityTemperatureValue( sensor ); -#endif - U32 currentIndex = dialTempMovingAvgData[ i ].dialTempSamplesNextIndex; - F32 prevSampleToRemoveC = dialTempMovingAvgData[ i ].dialTempSamplesC[ currentIndex ]; + currentIndex = dialTempMovingAvgData[ i ].dialTempSamplesNextIndex; + prevSampleToRemoveC = dialTempMovingAvgData[ i ].dialTempSamplesC[ currentIndex ]; + dialTempMovingAvgData[ i ].dialTempDataColStartTimeMS = getMSTimerCount(); dialTempMovingAvgData[ i ].dialTempColHasTimerBeenSet = TRUE; dialTempMovingAvgData[ i ].dialTempSamplesC[ currentIndex ] = temperatureC; @@ -483,19 +476,24 @@ data.d50Temp = getTemperatureValue( D50_TEMP ); data.d99Temp = getTemperatureValue( D99_TEMP ); data.boardTemp = getTemperatureValue( BRD_TEMP ); -#ifdef __TEENSY_CONDUCTIVITY_DRIVER__ - data.d16CondTemp = getTeensyConductivityTemperatureValue( D17_COND ); - data.d28CondTemp = getTeensyConductivityTemperatureValue( D27_COND ); - data.d30CondTemp = getTeensyConductivityTemperatureValue( D29_COND ); - data.d44CondTemp = getTeensyConductivityTemperatureValue( D43_COND ); - data.d75CondTemp = getConductivityTemperatureValue( D74_COND ); -#else - data.d16CondTemp = getConductivityTemperatureValue( D17_COND ); - data.d28CondTemp = getConductivityTemperatureValue( D27_COND ); - data.d30CondTemp = getConductivityTemperatureValue( D29_COND ); - data.d44CondTemp = getConductivityTemperatureValue( D43_COND ); - data.d75CondTemp = getConductivityTemperatureValue( D74_COND ); -#endif + + if ( getTestConfigStatus( TEST_CONFIG_DD_FP_ENABLE_BETA_2_0_HW ) != TRUE ) + { + data.d16CondTemp = getTeensyConductivityTemperatureValue( D17_COND ); + data.d28CondTemp = getTeensyConductivityTemperatureValue( D27_COND ); + data.d30CondTemp = getTeensyConductivityTemperatureValue( D29_COND ); + data.d44CondTemp = getTeensyConductivityTemperatureValue( D43_COND ); + data.d75CondTemp = getConductivityTemperature( D74_COND ); + } + else + { + data.d16CondTemp = getConductivityTemperature( D17_COND ); + data.d28CondTemp = getConductivityTemperature( D27_COND ); + data.d30CondTemp = getConductivityTemperature( D29_COND ); + data.d44CondTemp = getConductivityTemperature( D43_COND ); + data.d75CondTemp = getConductivityTemperature( D74_COND ); + } + data.d4AvgTemp = getFilteredTemperatureValue( D4_TEMP ); data.d50AvgTemp = getFilteredTemperatureValue( D50_TEMP ); data.d99AvgTemp = getFilteredTemperatureValue( D99_TEMP ); Index: firmware/App/Monitors/Temperature.h =================================================================== diff -u -r623587d96bf6c0bafd3ff94d47f8c783b4b999f0 -r4cdcb589ad34efa472c94180e6de938132cbc89b --- firmware/App/Monitors/Temperature.h (.../Temperature.h) (revision 623587d96bf6c0bafd3ff94d47f8c783b4b999f0) +++ firmware/App/Monitors/Temperature.h (.../Temperature.h) (revision 4cdcb589ad34efa472c94180e6de938132cbc89b) @@ -50,6 +50,7 @@ F32 d99AvgTemp; ///< D99 moving average temperature value F32 d28AvgTemp; ///< D28 moving average temperature value F32 d30AvgTemp; ///< D30 moving average temperature value + F32 d78AvgTemp; ///< D78 moving average temperature value F32 d9PresTemp; ///< Hydraulics outlet pressure temperature F32 d66PresTemp; ///< Bicarb bag temperature F32 d51PresTemp; ///< Spent Dialysate temperature @@ -72,21 +73,19 @@ F32 p16Temp; ///< Temperature RO outlet F32 p7InternalTemp; ///< P7 internal Temperature F32 p16InternalTemp; ///< P16 internal Temperature - } TEMPERATURE_SENSORS_FP_DATA_T; // ********** public function prototypes ********** void initTemperature( void ); SELF_TEST_STATUS_T execTemperatureSensorsSelfTest( void ); void execTemperatureSensors( void ); -F32 getD4AverageTemperature( void ); -F32 getD50AverageTemperature( void ); -F32 getD99AverageTemperature( void ); +F32 getFilteredTemperatureValue( TEMPERATURE_SENSORS_T sensor ); void checkDialysateTemperatureSensors( void ); BOOL testDDTemperatureSensorsDataPublishIntervalOverride( MESSAGE_T *message ); BOOL testIOFPTemperatureSensorsDataPublishIntervalOverride( MESSAGE_T *message ); +BOOL testDDTemperatureSensorFilteredReadingsOverride( MESSAGE_T *message ); /**@}*/ Index: firmware/App/Services/AlarmMgmtSWFaults.h =================================================================== diff -u -rb04db69f541f245e543df343257bcbdb73fbbc3d -r4cdcb589ad34efa472c94180e6de938132cbc89b --- firmware/App/Services/AlarmMgmtSWFaults.h (.../AlarmMgmtSWFaults.h) (revision b04db69f541f245e543df343257bcbdb73fbbc3d) +++ firmware/App/Services/AlarmMgmtSWFaults.h (.../AlarmMgmtSWFaults.h) (revision 4cdcb589ad34efa472c94180e6de938132cbc89b) @@ -120,7 +120,7 @@ SW_FAULT_ID_TEMPERATURE_SENSORS_EXEC_INVALID_STATE = 89, SW_FAULT_ID_PI_CTRL_INVALID_CONTROLLER = 90, SW_FAULT_ID_PI_CTRL_INVALID_SIGNAL = 91, - SW_FAULT_ID_PI_CTRL_INVALID_STEP_LIMIT = 92, + SW_FAULT_ID_AVAILABALE_1 = 92, SW_FAULT_ID_DIALYSATE_PUMP_INVALID_RPM_SELECTED = 93, SW_FAULT_ID_DIALYSATE_PUMP_EXEC_INVALID_STATE = 94, SW_FAULT_ID_DIALYSATE_PUMP_INVALID_PUMP_ID = 95, @@ -154,6 +154,8 @@ SW_FAULT_ID_DRY_BICART_DRAIN_INVALID_EXEC_STATE = 123, SW_FAULT_ID_INVALID_RINSE_PUMP = 124, SW_FAULT_ID_CONDUCTIVITY_SENSOR_CAL_CHECK = 125, + SW_FAULT_ID_PI_CTRL_INVALID_FEED_FORWARD_LIMIT = 126, + SW_FAULT_ID_INVALID_TEMPERATURE_SENSOR_SELECTED3 = 127, NUM_OF_SW_FAULT_IDS } DD_SW_FAULT_ID_T; Index: firmware/App/Services/Messaging.c =================================================================== diff -u -rb04db69f541f245e543df343257bcbdb73fbbc3d -r4cdcb589ad34efa472c94180e6de938132cbc89b --- firmware/App/Services/Messaging.c (.../Messaging.c) (revision b04db69f541f245e543df343257bcbdb73fbbc3d) +++ firmware/App/Services/Messaging.c (.../Messaging.c) (revision 4cdcb589ad34efa472c94180e6de938132cbc89b) @@ -279,6 +279,7 @@ { MSG_ID_FP_BOOST_PUMP_INSTALL_STATUS_REQUEST, &testGetFPBoostPumpInstallStatus }, { MSG_ID_DD_CONDUCTIVITY_SENSOR_VERSION_REQUEST, &testHandleConductivitySensorVersionRequest }, { MSG_ID_FP_CONDUCTIVITY_SENSOR_VERSION_REQUEST, &testHandleConductivitySensorVersionRequest }, + { MSG_ID_DD_TEMPERATURE_SENSOR_FILTERED_TEMP_OVERRIDE_REQUEST, &testDDTemperatureSensorFilteredReadingsOverride }, }; /// Calculation for number of entries in the incoming message function handler look-up table.