Index: firmware/App/Controllers/MixingControl.c =================================================================== diff -u -r145fd716a856f864f39fb0f9884865f6e45b9256 -rc1557cc79c8f0b0c358963c99d83015bd3b37e42 --- firmware/App/Controllers/MixingControl.c (.../MixingControl.c) (revision 145fd716a856f864f39fb0f9884865f6e45b9256) +++ firmware/App/Controllers/MixingControl.c (.../MixingControl.c) (revision c1557cc79c8f0b0c358963c99d83015bd3b37e42) @@ -8,7 +8,7 @@ * @file MixingControl.c * * @author (last) Sameer Kalliadan Poyil -* @date (last) 16-Jun-2026 +* @date (last) 10-Jul-2026 * * @author (original) Sameer Kalliadan Poyil * @date (original) 15-Jun-2026 @@ -46,9 +46,9 @@ #define MIXING_CONTROL_DATA_PUBLISH_INTERVAL ( 250 / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) at which the mixing control data published. // drybicarb mixing -#define BICARB_VOL_CONTROL_P_COEFFICIENT ( 0.00008484F * 2 ) ///< Bicarb proportional gain (kp) -#define BICARB_VOL_CONTROL_I_COEFFICIENT ( 0.00033936F / 4 ) ///< Bicarb integral gain. (ki) -#define MIN_BICARB_VOLUME_ML 0.868686869 ///< Minimum bicarb volume in mL +#define BICARB_VOL_CONTROL_P_COEFFICIENT ( 0.00008484F ) ///< Bicarb proportional gain (kp) +#define BICARB_VOL_CONTROL_I_COEFFICIENT ( 0.00033936F / 8 ) ///< Bicarb integral gain. (ki) +#define MIN_BICARB_VOLUME_ML 0.4F ///< Minimum bicarb volume in mL #define MAX_BICARB_VOLUME_ML 1.8F ///< Maximum bicarb volume in mL #define ACID_VOL_CONTROL_P_COEFFICIENT ( 0.00000997F / 2 ) ///< Acid proportional gain (kp) @@ -59,7 +59,7 @@ #define BICARB_TARGET_CONDUCTIVITY 2714.0F ///< Target bicarb conductivity #define BICARB_DELTA_CONDUCTIVITY 500.0F ///< Delta bicarb conductivity -#define DIALYSATE_TARGET_CONDUCTIVITY 3734.87F ///< Target dialysate conductivity +#define DIALYSATE_TARGET_CONDUCTIVITY 13613.8F ///< Target dialysate conductivity for 1K #define DIALYSATE_DELTA_CONDUCTIVITY 700.0F ///< Delta dialysate conductivity // this is for reference only , it can be removed later @@ -69,19 +69,18 @@ #define MIX_NO_FEED_FORWARD 0.0F ///< Feed forward term for dialysate closed loop control -#define BICARB_MIX_CONTROL_INTERVAL ( 15 * MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) at which the bicarb mix is controlled. -#define ACID_MIX_CONTROL_INTERVAL ( 9 * MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) at which the dialysate mix is controlled. +#define BICARB_MIX_CONTROL_INTERVAL_MULTIPLIER ( 3 * MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) at which the bicarb mix is controlled. +#define ACID_MIX_CONTROL_INTERVAL_MULTIPLIER ( 5 * MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) at which the dialysate mix is controlled. -#define BICARB_DEADBAND_CONTROL 15.0F ///< Bicarb dead band control -#define ACID_DEADBAND_CONTROL 50.0F ///< Acid dead band control +#define BICARB_DEADBAND_CONTROL 30.0F ///< Bicarb dead band control +#define ACID_DEADBAND_CONTROL 70.0F ///< Acid dead band control // Proportioning Ratios: 1 Part Acid : 1.72 Parts Bicarb : 42.28 Parts Water // Total parts = 1 + 1.72 + 42.28 = 45.0 #define STD_45X_CONC_MIX_ACID_PART 1.0F ///< Acid portion of 45X concentration mix #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 @@ -97,8 +96,8 @@ #define STD_DILUTION_BICARB_POST_MIX_NA 37.0F ///< Bicarb post mix Sodium #define STD_DILUTION_BICARB_POST_MIX_CHO3 33.0F ///< Post mix bicarbonate -//#define DIALYSATE_TARGET_STD_NA_SETTINGS 137.0F ///< Standard target sodium -//#define DIALYSATE_TARGET_STD_BICARB_SETTING 33.0F ///< Standard bicarb +#define MILLISIEMENS_TO_MICROSIEMENS(x) ( ( ( F32 )( x ) ) * 1000.0F ) ///< Milli siemense to micro siemense convertion factor + #define DIALYSATE_TARGET_USER_ADJ_SODIUM_SETTING getTDSodiumConcentration() ///< User Sodium #define DIALYSATE_TARGET_USER_ADJ_BICARB_SETTING getTDBicarbonateConcentration() ///< User bicarb @@ -115,12 +114,14 @@ #define FINAL_NA_ADJ_MIX ( ACID_TARGET_CONCENTRATE_NA + BICARB_PERCENT_CHANGE_NA ) ///< Final adjusted sodium -#define FINAL_NAHCO3_ADJ_MIX ( BICARB_PERCENT_CHANGE_NAHCO3 ) +#define FINAL_NAHCO3_ADJ_MIX ( BICARB_PERCENT_CHANGE_NAHCO3 ) ///< Final Bicarbonate part -#define FINAL_ACID_MIX ( ACID_MIX_VOLUME_ML * ACID_PERCENT_CHANGE ) ///< Final Acid Mix +#define FINAL_ACID_MIX ( ACID_MIX_VOLUME_ML * ACID_PERCENT_CHANGE ) ///< Final Acid Mix volume -#define FINAL_BICARB_MIX ( BICARB_MIX_VOLUME_ML * BICARB_PERCENT_CHANGE ) ///< Final bicarb mix +#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 @@ -160,8 +161,8 @@ // publish static OVERRIDE_U32_T mixingControlDataPublishInterval; ///< Mixing Control data publish interval. static U32 mixingControlDataPublicationTimerCounter; ///< Used to schedule mixingControl data publication to CAN bus. -static F32 lastBicarbMixVolume; ///< Deadband bicarb mix volume -static F32 lastAcidMixVolume; ///< Deadband acid mix volume +static F32 mixingControlLastBicarbMixVolume; ///< Previously stored value, if bicarb control in dead band +static F32 mixingControlLastAcidMixVolume; ///< Previously stored value, if acid control in dead band // mixing static OVERRIDE_F32_T mixingControlAcidVolume; ///< Acid concentrate volume in ml ( overrideable). @@ -201,6 +202,7 @@ static void setBicarbMixVol( F32 targetValue ); static void setAcidMixVol( F32 targetValue ); +static void updatedCMixingControlInterval( void ); static F32 getBicarbDeltaConductivity( void ); static F32 getBicarbTargetConductivity( void ); @@ -247,13 +249,13 @@ mixingControlDataPublishInterval.ovInitData = 0; mixingControlDataPublishInterval.override = OVERRIDE_RESET; - mixingControlAcidVolume.data = DEFAULT_ACID_VOLUME_ML; - mixingControlAcidVolume.ovData = DEFAULT_ACID_VOLUME_ML; + mixingControlAcidVolume.data = FINAL_ACID_MIX; + mixingControlAcidVolume.ovData = FINAL_ACID_MIX; mixingControlAcidVolume.ovInitData = 0.0F; mixingControlAcidVolume.override = OVERRIDE_RESET; - mixingControlBicarbVolume.data = DEFAULT_BICARB_VOLUME_ML; - mixingControlBicarbVolume.ovData = DEFAULT_BICARB_VOLUME_ML; + mixingControlBicarbVolume.data = FINAL_BICARB_MIX_VOLUME_UPDATE; + mixingControlBicarbVolume.ovData = FINAL_BICARB_MIX_VOLUME_UPDATE; mixingControlBicarbVolume.ovInitData = 0.0F; mixingControlBicarbVolume.override = OVERRIDE_RESET; @@ -277,8 +279,8 @@ mixingControlBicarbVolumeKiGain.ovInitData = 0.0F; mixingControlBicarbVolumeKiGain.override = OVERRIDE_RESET; - mixingControlBicarbTargetConductivity.data = BICARB_TARGET_CONDUCTIVITY; - mixingControlBicarbTargetConductivity.ovData = BICARB_TARGET_CONDUCTIVITY; + mixingControlBicarbTargetConductivity.data = getBicarbConductivityPre(); + mixingControlBicarbTargetConductivity.ovData = getBicarbConductivityPre(); mixingControlBicarbTargetConductivity.ovInitData = 0.0F; mixingControlBicarbTargetConductivity.override = OVERRIDE_RESET; @@ -287,8 +289,8 @@ mixingControlBicarbDeltaConductivity.ovInitData = 0.0F; mixingControlBicarbDeltaConductivity.override = OVERRIDE_RESET; - mixingControlDialysateTargetConductivity.data = DIALYSATE_TARGET_CONDUCTIVITY; - mixingControlDialysateTargetConductivity.ovData = DIALYSATE_TARGET_CONDUCTIVITY; + mixingControlDialysateTargetConductivity.data = getTotalConductivity(); + mixingControlDialysateTargetConductivity.ovData = getTotalConductivity(); mixingControlDialysateTargetConductivity.ovInitData = 0.0F; mixingControlDialysateTargetConductivity.override = OVERRIDE_RESET; @@ -312,18 +314,28 @@ mixingControlTargetAdjBicarbSettings.ovInitData = 0.0F; mixingControlTargetAdjBicarbSettings.override = OVERRIDE_RESET; - mixingControlBicarbControlInterval.data = BICARB_MIX_CONTROL_INTERVAL; - mixingControlBicarbControlInterval.ovData = BICARB_MIX_CONTROL_INTERVAL; + mixingControlBicarbControlInterval.data = BICARB_MIX_CONTROL_INTERVAL_MULTIPLIER; + mixingControlBicarbControlInterval.ovData = BICARB_MIX_CONTROL_INTERVAL_MULTIPLIER; mixingControlBicarbControlInterval.ovInitData = 0; mixingControlBicarbControlInterval.override = OVERRIDE_RESET; - mixingControlAcidControlInterval.data = ACID_MIX_CONTROL_INTERVAL; - mixingControlAcidControlInterval.ovData = ACID_MIX_CONTROL_INTERVAL; + mixingControlAcidControlInterval.data = ACID_MIX_CONTROL_INTERVAL_MULTIPLIER; + mixingControlAcidControlInterval.ovData = ACID_MIX_CONTROL_INTERVAL_MULTIPLIER; mixingControlAcidControlInterval.ovInitData = 0; mixingControlAcidControlInterval.override = OVERRIDE_RESET; mixingControlDataPublicationTimerCounter = 0; + mixingControlLastBicarbMixVolume = 0.0F; + mixingControlLastAcidMixVolume = 0.0F; + initializePIController( PI_CONTROLLER_ID_BICARB_VOL, 0.0F,\ + getBicarbKpGainCoefficient(), getBicarbKiGainCoefficient(),\ + MIN_BICARB_VOLUME_ML, MAX_BICARB_VOLUME_ML, FALSE, MIX_NO_FEED_FORWARD ); + + initializePIController( PI_CONTROLLER_ID_ACID_VOL, 0.0F, \ + getAcidKpGainCoefficient(), getAcidKiGainCoefficient(),\ + MIN_ACID_VOLUME_ML, MAX_ACID_VOLUME_ML, FALSE, MIX_NO_FEED_FORWARD ); + } /*********************************************************************//** @@ -349,8 +361,10 @@ *************************************************************************/ void execMixingControl( void ) { - if( getCurrentBalancingChamberExecState() > BAL_CHAMBER_STATE_IDLE ) + if ( getCurrentBalancingChamberExecState() > BAL_CHAMBER_STATE_IDLE ) { + updatedCMixingControlInterval(); + // closed loop bicarb and acid mixing controller handleDialysateCompositionMixing(); } @@ -417,6 +431,36 @@ /*********************************************************************//** * @brief + * The updatedCMixingControlInterval function sets both acid and bicarb + * Mix volume provided by the controller + * @details \b Inputs: none + * @details \b Outputs: mixingControlBicarbMixVolume + * @param targetValue bicarb Mix volume + * @return TRUE if successful, FALSE if not. + *************************************************************************/ +static void updatedCMixingControlInterval( void ) +{ + F32 tdDialysateFlowRate = 0.0F; + F32 balChamberSwitchingFreq = 0.0F; + U32 balChamberSwitchingPeriod = 0; + + tdDialysateFlowRate = getTDDialysateFlowrate(); + + balChamberSwitchingFreq = tdDialysateFlowRate / 30.0; + balChamberSwitchingPeriod = ( SEC_PER_MIN ) / (U32)balChamberSwitchingFreq; + + // For acid control period, we are using 3 cycles of balancing chamber switching period (for ex: At Qd = 600, switching period = 3sec, so acid control interval = 3*3 = 9sec + // For bicarb control period, we are using 5 cycles of balancing chamber switching period (for ex: At Qd = 600, switching period = 3sec, so bicarb control interval = 3*5 = 15sec + + if ( balChamberSwitchingPeriod > 0) + { + mixingControlAcidControlInterval.data = ACID_MIX_CONTROL_INTERVAL_MULTIPLIER * balChamberSwitchingPeriod; + mixingControlBicarbControlInterval.data = BICARB_MIX_CONTROL_INTERVAL_MULTIPLIER * balChamberSwitchingPeriod; + } +} + +/*********************************************************************//** + * @brief * The getBicarbDeltaConductivity function gets the delta target conductivity * @details \b Inputs: mixingControlBicarbDeltaConductivity * @details \b Outputs: none @@ -564,47 +608,56 @@ * The getTotalConductivity function gets the total conductivity of bicarb and acid * @details \b Inputs: none * @details \b Outputs: none - * @return conductivity + * @return totalConductivity(Dialysate conductivity) in uS/cm *************************************************************************/ static F32 getTotalConductivity( void ) { - return TOTAL_CONDUCTIVITY; + F32 totalConductivity = TOTAL_CONDUCTIVITY; + // convert milli siemense to micro siemense + return MILLISIEMENS_TO_MICROSIEMENS( totalConductivity ); } /*********************************************************************//** * @brief * The getAcidConducivityPost function gets the acid conductivity post * @details \b Inputs: none * @details \b Outputs: none - * @return conductivity + * @return acidConductivityPost in uS/cm *************************************************************************/ static F32 getAcidConducivityPost( void ) { - return ACID_CONDUCTIVITY_POST; + F32 acidConductivityPost = ACID_CONDUCTIVITY_POST; + // convert milli siemense to micro siemense + return MILLISIEMENS_TO_MICROSIEMENS( acidConductivityPost ); } /*********************************************************************//** * @brief - * The getBicarbConductivityPost function gets the bicarb conductivity post + * The getBicarbConductivityPost function gets the bicarb conductivity after mix * @details \b Inputs: none * @details \b Outputs: none - * @return conductivity + * @return bicarbConductivityPost in uS/cm *************************************************************************/ static F32 getBicarbConductivityPost( void ) { - return BICARB_CONDUCTIVITY_PRE; + // right now post mix value is same as BICARB_CONDUCTIVITY_PRE + F32 bicarbConductivityPost = BICARB_CONDUCTIVITY_PRE; + // convert milli siemense to micro siemense + return MILLISIEMENS_TO_MICROSIEMENS( bicarbConductivityPost ); } /*********************************************************************//** * @brief - * The getBicarbConductivityPre function gets the bicarb conductivity pre + * The getBicarbConductivityPre function gets the bicarb conductivity before mix * @details \b Inputs: none * @details \b Outputs: none - * @return conductivity + * @return bicarbConductivityPre in uS/cm *************************************************************************/ static F32 getBicarbConductivityPre( void ) { - return BICARB_CONDUCTIVITY_PRE; + F32 bicarbConductivityPre = BICARB_CONDUCTIVITY_PRE; + // convert milli siemense to micro siemense + return MILLISIEMENS_TO_MICROSIEMENS( bicarbConductivityPre ); } /*********************************************************************//** @@ -637,8 +690,8 @@ if ( BICARB_MIX_ID == mixId ) { - //TODO: right now using the fixed conductivity , later use the calculated version API F32 measuredBicarbConductivity = getFilteredConductivity( D17_COND ); + // reads getBicarbConductivityPre() F32 bicarbConductivity = getBicarbTargetConductivity(); F32 bicarbDeltaConductivity = getBicarbDeltaConductivity(); @@ -653,10 +706,10 @@ } else if ( ACID_MIX_ID == mixId ) { - //TODO: right now using the fixed conductivity , later use the calculated version API F32 measuredAcidBicarbMixConductivity = getFilteredConductivity( D27_COND ); + // reads getTotalConductivity(); F32 DialysateConductivity = getDialysateTargetConductivity(); - F32 DialysateDeltaConductivity = getDialysateTargetConductivity(); + F32 DialysateDeltaConductivity = getDialysateDeltaConductivity(); if ( TRUE != getTestConfigStatus( TEST_CONFIG_DD_ENABLE_DOSING_OPEN_LOOP_CONTROL ) ) { @@ -724,18 +777,20 @@ // Control based on the measured and target conductivity PI_CONTROLLER_SIGNALS_DATA debugBicarbControl; F32 measuredBicarbConductivity = getFilteredConductivity( D17_COND ); - F32 bicarbConductivity = getBicarbTargetConductivity();//getBicarbConductivityPre(); + F32 bicarbConductivity = getBicarbConductivityPre(); + //TODO: ALARM if measuredDialysateConductivity or bicarbConductivity is or both 0 if ( fabs ( bicarbConductivity - measuredBicarbConductivity ) > BICARB_DEADBAND_CONTROL ) { - bicarbMixVol = runPIController( PI_CONTROLLER_ID_BICARB_VOL, getBicarbConductivityPre() /*getBicarbTargetConductivity()*/, measuredBicarbConductivity ); - //Set bicarb mix vol only if its above dead band threshold, else use the previous set vlaue + bicarbMixVol = runPIController( PI_CONTROLLER_ID_BICARB_VOL, bicarbConductivity, measuredBicarbConductivity ); + //Set bicarb mix vol only if its above dead band threshold, else use the previous set value + // Note: bicarb volume will impact the D10 pump , if volume is set to 0 , then D10 doesn't run setBicarbMixVol( bicarbMixVol ); } else { - //use the previous value - lastBicarbMixVolume = getBicarbMixVol(); + //use the previous value, this is for debug + mixingControlLastBicarbMixVolume = getBicarbMixVol(); } debugBicarbControl = getDebugPIControllerSignals( PI_CONTROLLER_ID_BICARB_VOL ); @@ -755,19 +810,21 @@ // TODO: use the calculated API F32 measuredDialysateConductivity = getFilteredConductivity( D27_COND ); - F32 DialysateConductivity = getDialysateTargetConductivity();//getTotalConductivity(); + F32 DialysateConductivity = getTotalConductivity(); PI_CONTROLLER_SIGNALS_DATA debugAcidControl; + //TODO: ALARM if measuredDialysateConductivity or DialysateConductivity is or both 0 if ( fabs ( DialysateConductivity - measuredDialysateConductivity ) > ACID_DEADBAND_CONTROL ) { acidMixVol = runPIController( PI_CONTROLLER_ID_ACID_VOL, DialysateConductivity, measuredDialysateConductivity ); // set acid mix volume if its above dead band threshold , else use previous set value + // Note: if acid volume is set as 0, then D11 pump doesn't run setAcidMixVol( acidMixVol ); } else { - // use the previous value - lastAcidMixVolume = getAcidMixVol(); + // use the previous value, this is for debug + mixingControlLastAcidMixVolume = getAcidMixVol(); } debugAcidControl = getDebugPIControllerSignals( PI_CONTROLLER_ID_ACID_VOL ); @@ -839,11 +896,15 @@ data.acidControlInterval = (U32)dialysateMix[ ACID_MIX_ID ].controlTimerCounter; data.currentBicarbMixVolume = getBicarbMixVol(); - data.lastBicarbMixVolume = lastBicarbMixVolume; + data.lastBicarbMixVolume = mixingControlLastBicarbMixVolume; data.currentAcidMixVolume = getAcidMixVol(); - data.currentAcidMixVolume = lastAcidMixVolume; + data.lastAcidMixVolume = mixingControlLastAcidMixVolume; - //TODO :broadcast theretical pre and post conductivity + data.bicarbConductivityPreMix = getBicarbConductivityPre(); + data.bicarbConductivityPostMix = getBicarbConductivityPost(); + data.acidConductivityPostMix = getAcidConducivityPost(); + // acid+bicarb conductivity or Dialysate conductivity + data.totalConductivity = getTotalConductivity(); // TODO: remove after feature testing memcpy( &data.bicarbData, &bicarbControlSignals, sizeof( PI_CONTROLLER_SIGNALS_DATA ) ); @@ -1087,7 +1148,7 @@ *************************************************************************/ BOOL testMixingControlBicarbControlIntervalOverride( MESSAGE_T *message ) { - BOOL result = u32Override( message, &mixingControlBicarbControlInterval, 0, BICARB_MIX_CONTROL_INTERVAL ); + BOOL result = u32Override( message, &mixingControlBicarbControlInterval, 0, BICARB_MIX_CONTROL_INTERVAL_MULTIPLIER ); return result; } @@ -1104,7 +1165,7 @@ *************************************************************************/ BOOL testMixingControlAcidControlIntervalOverride( MESSAGE_T *message ) { - BOOL result = u32Override( message, &mixingControlAcidControlInterval, 0, BICARB_MIX_CONTROL_INTERVAL ); + BOOL result = u32Override( message, &mixingControlAcidControlInterval, 0, ACID_MIX_CONTROL_INTERVAL_MULTIPLIER ); return result; }