Index: firmware/App/Controllers/ConcentratePumps.h =================================================================== diff -u -r3cc784af591b14e5d292f92c796db70bcbd5a40b -rafb05eebd86337012af4f6a9d21cb9a1e7f5f16a --- firmware/App/Controllers/ConcentratePumps.h (.../ConcentratePumps.h) (revision 3cc784af591b14e5d292f92c796db70bcbd5a40b) +++ firmware/App/Controllers/ConcentratePumps.h (.../ConcentratePumps.h) (revision afb05eebd86337012af4f6a9d21cb9a1e7f5f16a) @@ -34,8 +34,10 @@ // For 100 RPM, Diener pump delivers 40ml/min,so restricing 300 RPM for now to maintain accuracy. We may allow up to 500 RPM // that yields 200 ml/min, but as speed increases, accuracy may drop. #define CONCENTRATE_PUMP_MAX_SPEED 120.0F ///< Optimal speed for concentrate pump in mL/min +#define UF_PUMP_SPEED 100.0F ///< 100 ml/min, runs at 250 RPM #else #define CONCENTRATE_PUMP_MAX_SPEED 48.0F ///< Maximum speed for concentrate pump in mL/min +#define UF_PUMP_SPEED 40.0F ///< 40 ml/min, runs at 400 RPM #endif #define DEFAULT_ACID_VOLUME_ML 0.67F ///< Acid concentrate volume in ml. #define DEFAULT_BICARB_VOLUME_ML 1.15F ///< Bicarb concentrate volume in ml. Index: firmware/App/Controllers/Ultrafilteration.c =================================================================== diff -u --- firmware/App/Controllers/Ultrafilteration.c (revision 0) +++ firmware/App/Controllers/Ultrafilteration.c (revision afb05eebd86337012af4f6a9d21cb9a1e7f5f16a) @@ -0,0 +1,292 @@ +/************************************************************************** +* +* Copyright (c) 2024-2025 Diality Inc. - All Rights Reserved. +* +* THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN +* WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. +* +* @file Ultrafilteration.c +* +* @author (last) Vinayakam Mani +* @date (last) 23-Jul-2025 +* +* @author (original) Vinayakam Mani +* @date (original) 23-Jul-2025 +* +***************************************************************************/ + +#include "ConcentratePumps.h" +#include "Messaging.h" +#include "OperationModes.h" +#include "TaskGeneral.h" +#include "TDInterface.h" +#include "TestSupport.h" +#include "Ultrafilteration.h" + + +/** + * @addtogroup Ultrafilteration + * @{ + */ + +// ********** private definitions ********** + +#define UF_DATA_PUBLISH_INTERVAL ( 1000 / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) at which the ultrafilteration data published. +#define UF_RUN_PERIOD ( 5 * MS_PER_SECOND ) ///< Interval at which the ultrafilteration initiated. +#define ZERO_RATE 0.0F ///< Zero value. +#define UF_RUN_INTERVAL ( UF_RUN_PERIOD / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) at which the peroidic ultrafilteration initiated . + +// ********** private data ********** + +static UF_EXEC_STATE_T ufExecState; ///< Current ultrafilteration executive state. +static BOOL isUltrafilterationRequested; ///< Flag indicating ultrafilteration request. +static U32 currentUFRunningCounter; ///< Counter (in task interval) to initiate the periodic ultrafilteration +static F32 ufVolumeperIteration; ///< ultrafilteration volume per defined interval +static U32 ufDataPublicationTimerCounter; ///< Used to schedule ultrafilteration data publication to CAN bus. +static OVERRIDE_U32_T ufDataPublishInterval; ///< Ultrafilteration data publish interval. + +// ********** private function prototypes ********** + +static UF_EXEC_STATE_T handleUFRunningState( void ); +static UF_EXEC_STATE_T handleUFPausedState( void ); +static void updateUFRequest( void ); +static void publishUltrafiltrationData( void ); +static U32 getUFDataPublishInterval( void ); + +/*********************************************************************//** + * @brief + * The initUltrafilteration function initializes the ultrafilteration unit. + * @details \b Inputs: none + * @details \b Outputs: unit variables initialized + * @return none + *************************************************************************/ +void initUltrafilteration( void ) +{ + ufExecState = DD_UF_PAUSED; + ufDataPublishInterval.data = UF_DATA_PUBLISH_INTERVAL; + ufDataPublishInterval.ovData = UF_DATA_PUBLISH_INTERVAL; + ufDataPublishInterval.ovInitData = 0; + ufDataPublishInterval.override = OVERRIDE_RESET; + isUltrafilterationRequested = FALSE; + currentUFRunningCounter = 0; + ufVolumeperIteration = 0.0F; + ufDataPublicationTimerCounter = 0; +} + +/*********************************************************************//** + * @brief + * The transitionToUltrafilteration function prepares for transition to + * ultrafilteration. + * @details \b Inputs: none + * @details \b Outputs: isUltrafilterationRequested,ufDataPublicationTimerCounter + * @return none + *************************************************************************/ +void transitionToUltrafilteration( void ) +{ + initUltrafilteration(); +} + +/*********************************************************************//** + * @brief + * The execUFControl function executes the ultrafilteration state machine. + * @details \b Inputs: ufExecState + * @details \b Outputs: ufExecState + * @details \b Alarm: ALARM_ID_DD_SOFTWARE_FAULT when wrong ultrafilteration + * state invoked. + * @return current state. + *************************************************************************/ +U32 execUFControl( void ) +{ + // Calculate UF volume and determine UF pause/run + updateUFRequest(); + + // execute current ultrafilteration exec state + switch ( ufExecState ) + { + case DD_UF_PAUSED: + ufExecState = handleUFPausedState(); + break; + + case DD_UF_RUNNING: + ufExecState = handleUFRunningState(); + break; + + default: + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_UF_INVALID_EXEC_STATE, ufExecState ) + ufExecState = DD_UF_PAUSED; + break; + } + + //Publish ultrafilteration data + publishUltrafiltrationData(); + + return ufExecState; +} + +/*********************************************************************//** + * @brief + * The handleUFPausedState function handles the ultrafilteration + * paused state. + * @details \b Inputs:isUltrafilterationRequested + * @details \b Outputs: UF state + * @return next UF state. + *************************************************************************/ +static UF_EXEC_STATE_T handleUFPausedState( void ) +{ + UF_EXEC_STATE_T state = DD_UF_PAUSED; + + if ( TRUE == isUltrafilterationRequested ) + { + setConcentratePumpTargetSpeed( D76_PUMP, UF_PUMP_SPEED, ufVolumeperIteration ); + requestConcentratePumpOn( D76_PUMP ); + + //Tranistion to run state + state = DD_UF_RUNNING; + } + else + { + requestConcentratePumpOff( D76_PUMP, FALSE ); + } + + //Reset counter + currentUFRunningCounter = 0; + + return state; +} + +/*********************************************************************//** + * @brief + * The handleUFPausedState function handles the ultrafilteration + * running state. + * @details \b Inputs:isUltrafilterationRequested, currentUFRunningCounter + * @details \b Outputs: UF state, currentUFRunningCounter + * @return next UF state. + *************************************************************************/ +static UF_EXEC_STATE_T handleUFRunningState( void ) +{ + UF_EXEC_STATE_T state = DD_UF_RUNNING; + + if ( TRUE != isUltrafilterationRequested ) + { + state = DD_UF_PAUSED; + } + else if ( ++currentUFRunningCounter >= UF_RUN_INTERVAL ) + { + setConcentratePumpTargetSpeed( D76_PUMP, UF_PUMP_SPEED, ufVolumeperIteration ); + requestConcentratePumpOn( D76_PUMP ); + + currentUFRunningCounter = 0; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The updateUFRequest function updates the ultrafilteration rate per iteration + * and of the ultrafilteration. + * @details \b Inputs: TD Uf rate, TD Dialysate flow rate and bypass flag + * @details \b Outputs: ufVolumeperIteration , isUltrafilterationRequested + * @return none. + *************************************************************************/ +static void updateUFRequest( void ) +{ + // Calculate UF volume per iteration ( ml/ second) based on TD UF rate ( ml/minute) + ufVolumeperIteration = ( getTDUFRate() / SEC_PER_MIN ) * ( UF_RUN_PERIOD / MS_PER_SECOND ); + + // update latest UF run/pause request + if ( ( getTDUFRate() > ZERO_RATE ) && ( TRUE != getTDDialyzerBypass() ) && + ( getTDDialysateFlowrate() > ZERO_RATE ) ) + { + isUltrafilterationRequested = TRUE; + } + else + { + isUltrafilterationRequested = FALSE; + } +} + +/*********************************************************************//** + * @brief + * The getCurrentUFExecState function returns the current state + * of the ultrafilteration. + * @details \b Inputs: ufExecState + * @details \b Outputs: none + * @return the current state of UF execution state. + *************************************************************************/ +UF_EXEC_STATE_T getCurrentUFExecState( void ) +{ + return ufExecState; +} + +/*********************************************************************//** + * @brief + * The getUFDataPublishInterval function gets the ultrafilteration + * data publish interval. + * @details \b Inputs: balChamberDataPublishInterval + * @details \b Outputs: none + * @return the interval at UF data being published. + *************************************************************************/ +static U32 getUFDataPublishInterval( void ) +{ + U32 result = ufDataPublishInterval.data; + + if ( OVERRIDE_KEY == ufDataPublishInterval.override ) + { + result = ufDataPublishInterval.ovData; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The publishUltrafiltrationData function broadcasts the ultrafilteration + * data at defined interval. + * @details \b Inputs: ufDataPublicationTimerCounter + * @details \b Outputs: DD ultrafilteration data broadcast message sent + * @details \b Message \Sent: MSG_ID_DD_UF_DATA to publish the ultrafilteration + * data. + * @return none + *************************************************************************/ +static void publishUltrafiltrationData( void ) +{ + if ( ++ufDataPublicationTimerCounter >= getUFDataPublishInterval() ) + { + UF_DATA_T data; + + data.ufExecState = (U32)ufExecState; + data.ufRate = getTDUFRate(); + data.isUFRequested = (U32)isUltrafilterationRequested; + data.ufVolumeperIteration = ufVolumeperIteration; + + broadcastData( MSG_ID_DD_UF_DATA, COMM_BUFFER_OUT_CAN_DD_BROADCAST, (U08*)&data, sizeof( UF_DATA_T ) ); + + ufDataPublicationTimerCounter = 0; + } +} + + +/************************************************************************* + * TEST SUPPORT FUNCTIONS + *************************************************************************/ + + +/*********************************************************************//** + * @brief + * The testDDUFDataPublishIntervalOverride function overrides the + * DD ultrafilteration data publish interval. + * @details \b Inputs: ufDataPublishInterval + * @details \b Outputs: ufDataPublishInterval + * @param Override message from Dialin which includes the interval + * (in ms) to override the DD ultrafilteration data publish interval to. + * @return TRUE if override successful, FALSE if not + *************************************************************************/ +BOOL testDDUFDataPublishIntervalOverride( MESSAGE_T *message ) +{ + BOOL result = u32BroadcastIntervalOverride( message, &ufDataPublishInterval, TASK_GENERAL_INTERVAL ); + + return result; +} + +/**@}*/ Index: firmware/App/Controllers/Ultrafilteration.h =================================================================== diff -u --- firmware/App/Controllers/Ultrafilteration.h (revision 0) +++ firmware/App/Controllers/Ultrafilteration.h (revision afb05eebd86337012af4f6a9d21cb9a1e7f5f16a) @@ -0,0 +1,54 @@ +/************************************************************************** +* +* Copyright (c) 2024-2025 Diality Inc. - All Rights Reserved. +* +* THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN +* WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. +* +* @file Ultrafilteration.h +* +* @author (last) Vinayakam Mani +* @date (last) 23-Jul-2025 +* +* @author (original) Vinayakam Mani +* @date (original) 23-Jul-2025 +* +***************************************************************************/ + +#ifndef __ULTRA_FILTERATION_H__ +#define __ULTRA_FILTERATION_H__ + +#include "DDCommon.h" +#include "DDDefs.h" + +/** + * @defgroup Ultrafilteration Ultrafilteration + * @brief Ultrafilteration control unit. Performs ultrafilteration functions via a state machine. + * + * @addtogroup Ultrafilteration + * @{ + */ + +// ********** public definitions ********** + +/// ultrafilteration data structure +typedef struct +{ + U32 ufExecState; ///< Ultrafilteration execution state + F32 ufRate; ///< Ultrafilteration rate from TD + U32 isUFRequested; ///< Ultrafilteration run or puase request + F32 ufVolumeperIteration; ///< Ultrafilteration volume per iteration +} UF_DATA_T; + +// ********** public function prototypes ********** + +void initUltrafilteration( void ); // Initialize ultrafilteration unit +void transitionToUltrafilteration( void ); // Prepares for transition to ultrafilteration execution +U32 execUFControl( void ); // Execute the ultrafilteration state machine +UF_EXEC_STATE_T getCurrentUFExecState( void ); // Get the current state of the balancing chamber execution + +BOOL testDDUFDataPublishIntervalOverride( MESSAGE_T *message ); // To override the UF data publish interval + +/**@}*/ + +#endif Index: firmware/App/DDCommon.h =================================================================== diff -u -r5a038d2812c51d60b025dce1c66da40a8e91d22e -rafb05eebd86337012af4f6a9d21cb9a1e7f5f16a --- firmware/App/DDCommon.h (.../DDCommon.h) (revision 5a038d2812c51d60b025dce1c66da40a8e91d22e) +++ firmware/App/DDCommon.h (.../DDCommon.h) (revision afb05eebd86337012af4f6a9d21cb9a1e7f5f16a) @@ -45,6 +45,9 @@ //Uncomment below if diener concentrate pump used #define __DIENER_CONC_PUMP__ 1 +//Uncomment below if beta hardware is used +#define __BETA_HW_VER__ 1 + //Uncomment below if barometric pressure sensor reading available //#define __BARO_PRES_SENSOR__ 1 Index: firmware/App/Drivers/PressureSensor.c =================================================================== diff -u -r322747d530c1b8205be257557e53dcfe9caad50a -rafb05eebd86337012af4f6a9d21cb9a1e7f5f16a --- firmware/App/Drivers/PressureSensor.c (.../PressureSensor.c) (revision 322747d530c1b8205be257557e53dcfe9caad50a) +++ firmware/App/Drivers/PressureSensor.c (.../PressureSensor.c) (revision afb05eebd86337012af4f6a9d21cb9a1e7f5f16a) @@ -32,7 +32,11 @@ #define PRES_SENSORS_COUNT_ERROR_TIMEOUT_MS ( 2 * MS_PER_SECOND ) ///< Pressure sensors read and error count timeout in milliseconds. #define HIGH_PRES_MAX_PSI 145.038F ///< Convert pressure to PSI for 10 bar pressure sensor #define LOW_PRES_MAX_PSI 50.7632F ///< Convert pressure to PSI for 3.5 bar pressure sensor +#ifndef __BETA_HW_VER__ #define PRES_MIN_PSI 0.0F ///< Minimum value for PSI conversion +#else +#define PRES_MIN_PSI -14.5038F ///< Minimum value for PSI conversion +#endif #define ONE_BAR_TO_MILLI_BAR 1000 ///< 1 bar to milli-bar conversion. #define COUNTS_TO_MILLI_BAR 100 ///< Counts to milli-bar conversion. #define BAR_TO_MMHG ( 750.062F ) ///< Conversion factor for converting bar to mmHg. @@ -142,9 +146,15 @@ void readPressureSensors( void ) { // Update and convert raw pressures to mmHg +#ifndef __BETA_HW_VER__ currentPressureReadings[ M1_PRES ].data = convertPressureReading( getFPGAM1PresRawPressure(), PRES_MIN_PSI, HIGH_PRES_MAX_PSI ); currentPressureReadings[ M3_PRES ].data = convertPressureReading( getFPGAM3PresRawPressure(),PRES_MIN_PSI, HIGH_PRES_MAX_PSI ); currentPressureReadings[ D9_PRES ].data = convertPressureReading( getFPGAD9PresRawPressure(),PRES_MIN_PSI, HIGH_PRES_MAX_PSI ); +#else + currentPressureReadings[ M1_PRES ].data = convertPressureReading( getFPGAM1PresRawPressure(), PRES_MIN_PSI, LOW_PRES_MAX_PSI ); + currentPressureReadings[ M3_PRES ].data = convertPressureReading( getFPGAM3PresRawPressure(),PRES_MIN_PSI, LOW_PRES_MAX_PSI ); + currentPressureReadings[ D9_PRES ].data = convertPressureReading( getFPGAD9PresRawPressure(),PRES_MIN_PSI, LOW_PRES_MAX_PSI ); +#endif currentPressureReadings[ D66_PRES ].data = convertPressureReading( getFPGAD66PresRawPressure(), PRES_MIN_PSI, LOW_PRES_MAX_PSI ); currentPressureReadings[ D51_PRES ].data = convertPressureReading( getFPGAD51PresRawPressure(), PRES_MIN_PSI, LOW_PRES_MAX_PSI ); currentPressureReadings[ D18_PRES ].data = convertPressureReading( getFPGAD18PresRawPressure(), PRES_MIN_PSI, LOW_PRES_MAX_PSI ); Index: firmware/App/Modes/ModeGenDialysate.c =================================================================== diff -u -r1781335a8c1833fad17b275bf44c7f6675e68423 -rafb05eebd86337012af4f6a9d21cb9a1e7f5f16a --- firmware/App/Modes/ModeGenDialysate.c (.../ModeGenDialysate.c) (revision 1781335a8c1833fad17b275bf44c7f6675e68423) +++ firmware/App/Modes/ModeGenDialysate.c (.../ModeGenDialysate.c) (revision afb05eebd86337012af4f6a9d21cb9a1e7f5f16a) @@ -32,6 +32,7 @@ #include "TDInterface.h" #include "Temperature.h" #include "Timers.h" +#include "Ultrafilteration.h" #include "Valves.h" /** @@ -138,6 +139,7 @@ { initGenDialysateMode(); setCurrentSubState( NO_SUB_STATE ); + transitionToUltrafilteration(); return genDialysateState; } @@ -440,8 +442,11 @@ //Execute balancing chamber execBalancingChamberControl(); + //Execute ultrafilteration + execUFControl(); + // if TD asks for bypass or dialysate is not good to deliver - //transition to produce dialystate state + //transition to bypass dialystate state if ( ( FALSE == getDialGoodToDeliverStatus() ) || ( TRUE == getTDDialyzerBypass() ) ) { setModeGenDStateTransition( DD_GEND_DIALYSATE_BYPASS_STATE ); Index: firmware/App/Services/AlarmMgmtSWFaults.h =================================================================== diff -u -rf6022e86136c821709ce24b61e11e8e2bdf0b11e -rafb05eebd86337012af4f6a9d21cb9a1e7f5f16a --- firmware/App/Services/AlarmMgmtSWFaults.h (.../AlarmMgmtSWFaults.h) (revision f6022e86136c821709ce24b61e11e8e2bdf0b11e) +++ firmware/App/Services/AlarmMgmtSWFaults.h (.../AlarmMgmtSWFaults.h) (revision afb05eebd86337012af4f6a9d21cb9a1e7f5f16a) @@ -137,6 +137,7 @@ SW_FAULT_ID_POST_GEND_MODE_INVALID_EXEC_STATE = 106, SW_FAULT_ID_PRE_GEND_WET_SELF_TEST_INVALID_EXEC_STATE = 107, SW_FAULT_ID_PRE_GEND_WET_SELF_TEST_INVALID_EXEC_STATE1 = 108, + SW_FAULT_ID_UF_INVALID_EXEC_STATE = 109, #ifdef __PUMPTEST__ // Assigning high value to separate from actual fault id from test fault ids. SW_FAULT_ID_PISTON_PUMP_EXEC_INVALID_STATE = 9000, Index: firmware/App/Services/Messaging.c =================================================================== diff -u -rff90c5d3a21b9b4eecc4f85a4cfeb15bff28d371 -rafb05eebd86337012af4f6a9d21cb9a1e7f5f16a --- firmware/App/Services/Messaging.c (.../Messaging.c) (revision ff90c5d3a21b9b4eecc4f85a4cfeb15bff28d371) +++ firmware/App/Services/Messaging.c (.../Messaging.c) (revision afb05eebd86337012af4f6a9d21cb9a1e7f5f16a) @@ -43,6 +43,7 @@ #include "Temperature.h" #include "TDInterface.h" #include "Utilities.h" +#include "Ultrafilteration.h" #include "Valves.h" @@ -161,6 +162,7 @@ { MSG_ID_DD_PRE_GEN_DIALYSATE_REQUEST_DATA, &handlePreGenDialysateRequestMsg }, { MSG_ID_FW_VERSIONS_REQUEST, &handleVersionRequestMessage }, { MSG_ID_DD_SAFETY_SHUTDOWN_OVERRIDE_REQUEST, &testSetResetSafetyShutdownOverride }, + { MSG_ID_DD_UF_DATA_PUBLISH_OVERRIDE_REQUEST, &testDDUFDataPublishIntervalOverride }, #ifdef __PUMPTEST__ { MSG_ID_DD_PISTON_PUMP_DATA_PUBLISH_OVERRIDE_REQUEST, &testDDPistonPumpControlDataPublishIntervalOverride }, { MSG_ID_DD_PISTON_PUMP_START_STOP_OVERRIDE_REQUEST, &testDDPistonPumpStartStopOverride }, Index: firmware/source/sys_main.c =================================================================== diff -u -rf8bb43e62a4d10b64d8b197e372a56adfcfb88b9 -rafb05eebd86337012af4f6a9d21cb9a1e7f5f16a --- firmware/source/sys_main.c (.../sys_main.c) (revision f8bb43e62a4d10b64d8b197e372a56adfcfb88b9) +++ firmware/source/sys_main.c (.../sys_main.c) (revision afb05eebd86337012af4f6a9d21cb9a1e7f5f16a) @@ -84,6 +84,7 @@ #include "Timers.h" #include "Temperature.h" #include "Utilities.h" +#include "Ultrafilteration.h" #include "Valves.h" #include "WatchdogMgmt.h" @@ -182,6 +183,7 @@ initTestConfigs(); initTDInterface(); initROInterface(); + initUltrafilteration(); #ifdef __PUMPTEST__ initPistonPump(); #endif