Index: firmware/App/Controllers/ConductivitySensors.c =================================================================== diff -u -r6f1f269cd7d91f41c51797d17a85a7ea249e21f3 -r58d631f8ce26bf420a3fa087d4d1d86833aba002 --- firmware/App/Controllers/ConductivitySensors.c (.../ConductivitySensors.c) (revision 6f1f269cd7d91f41c51797d17a85a7ea249e21f3) +++ firmware/App/Controllers/ConductivitySensors.c (.../ConductivitySensors.c) (revision 58d631f8ce26bf420a3fa087d4d1d86833aba002) @@ -45,17 +45,6 @@ #define COND_SENSOR_CPI_MAX_VALUE 2000 ///< Maximum inlet water conductivity. #define COND_SENSOR_CPI_MIN_VALUE 100 ///< Minimum inlet water conductivity. -#define MIN_ACID_CONCENTRATE_CONDUCTIVITY 11000 ///< Minimum conductivity for acid concentrate. -#define MAX_ACID_CONCENTRATE_CONDUCTIVITY 11850 ///< Maximum conductivity for acid concentrate. - -#define MIN_BICARB_CONCENTRATE_CONDUCTIVITY 2000 ///< Minimum conductivity for bicarbonate concentrate. -#define MAX_BICARB_CONCENTRATE_CONDUCTIVITY 3000 ///< Maximum conductivity for bicarbonate concentrate. - -///< Minimum conductivity value for dialysate solution. -#define MIN_DIALYSATE_CONDUCTIVITY ( MIN_ACID_CONCENTRATE_CONDUCTIVITY + MIN_BICARB_CONCENTRATE_CONDUCTIVITY ) -///< Maximum conductivity value for dialysate solution. -#define MAX_DIALYSATE_CONDUCTIVITY ( MAX_ACID_CONCENTRATE_CONDUCTIVITY + MAX_BICARB_CONCENTRATE_CONDUCTIVITY ) - #define MAX_ALLOWED_UNCHANGED_CONDUCTIVITY_READS ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) ///< New reading every 800 ms, expect to get valid new reading in 1s. #define MAX_CONDUCTIVITY_SENSOR_FAILURES 5 ///< maximum number of conductivity sensor errors within window period before alarm. #define MAX_CONDUCTIVITY_SENSOR_FAILURE_WINDOW_MS ( 60 * MS_PER_SECOND ) ///< Conductivity sensor error window. @@ -318,7 +307,7 @@ } else { - activateAlarmNoData( ALARM_ID_DG_SOFTWARE_FAULT ); + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_CONDUCTIVITY_SENSOR_ID, sensorId ); } return result; Index: firmware/App/Controllers/LoadCell.c =================================================================== diff -u -r6f1f269cd7d91f41c51797d17a85a7ea249e21f3 -r58d631f8ce26bf420a3fa087d4d1d86833aba002 --- firmware/App/Controllers/LoadCell.c (.../LoadCell.c) (revision 6f1f269cd7d91f41c51797d17a85a7ea249e21f3) +++ firmware/App/Controllers/LoadCell.c (.../LoadCell.c) (revision 58d631f8ce26bf420a3fa087d4d1d86833aba002) @@ -34,33 +34,33 @@ // TODO check the maximum weight on the load cells in tare. There was 1500 grams limit // but it has been removed. Check the load cells data sheet. -#define LOAD_CELL_REPORT_PERIOD (100 / TASK_PRIORITY_INTERVAL) ///< Broadcast load cell values message every 100 ms. +#define LOAD_CELL_REPORT_PERIOD (100 / TASK_PRIORITY_INTERVAL) ///< Broadcast load cell values message every 100 ms. /// Conversion factor from ADC counts to grams. -static const F32 ADC2GRAM = (0.0894 * 1.1338); -#define LOAD_CELL_FILTER_ALPHA 0.05 ///< Alpha factor for the alpha filter used on load cell readings. +static const F32 ADC2GRAM = (0.0894 * 1.1338); +#define LOAD_CELL_FILTER_ALPHA 0.05 ///< Alpha factor for the alpha filter used on load cell readings. -#define SIZE_OF_SMALL_LOAD_CELL_AVG 100 ///< Small load cell moving average has 100 raw samples @ 10ms intervals (1-second). -#define SIZE_OF_LARGE_LOAD_CELL_AVG 40 ///< Large load cell moving average has 40 samples from small filter @ 100ms intervals (4-second). +#define SIZE_OF_SMALL_LOAD_CELL_AVG 100 ///< Small load cell moving average has 100 raw samples @ 10ms intervals (1-second). +#define SIZE_OF_LARGE_LOAD_CELL_AVG 40 ///< Large load cell moving average has 40 samples from small filter @ 100ms intervals (4-second). -#define LOAD_CELL_ADC_ERROR_PERSISTENCE 500 ///< Alarm persistence period (in ms) for load cell ADC errors. -#define EMPTY_RESERVOIR_WEIGHT_GRAMS 1600 ///< Reservoirs empty weight in grams. -#define MAX_ALLOWED_EXTRA_WEIGHT_BEFORE_TARE_GRAMS 300 ///< Max allowed extra weight before tare in grams. +#define LOAD_CELL_ADC_ERROR_PERSISTENCE 500 ///< Alarm persistence period (in ms) for load cell ADC errors. +#define EMPTY_RESERVOIR_WEIGHT_GRAMS 1600 ///< Reservoirs empty weight in grams. +#define MAX_ALLOWED_EXTRA_WEIGHT_BEFORE_TARE_GRAMS 300 ///< Max allowed extra weight before tare in grams. /// Load cell data structure. typedef struct { - U32 rawReading; ///< Latest raw load cell reading - OVERRIDE_F32_T weight; ///< Latest load cell weight + U32 rawReading; ///< Latest raw load cell reading. + OVERRIDE_F32_T weight; ///< Latest load cell weight. F32 autoCalOffset; ///< Load cell auto-calibration offset F32 loadCellVelocity_g_min; ///< Velocity (in g/min) of load cell. - F32 smallFilterReadings[ SIZE_OF_SMALL_LOAD_CELL_AVG ]; ///< Load cell samples for small load cell moving average. - F64 smallFilterTotal; ///< Small filter rolling total - used to calc small load cell moving average. - F32 smallFilteredWeight; ///< Load cell small filtered (100 100Hz raw sample) weight. + F32 smallFilterReadings[ SIZE_OF_SMALL_LOAD_CELL_AVG ]; ///< Load cell samples for small load cell moving average. + F32 smallFilterTotal; ///< Small filter rolling total - used to calc small load cell moving average. + F32 smallFilteredWeight; ///< Load cell small filtered (100 100Hz raw sample) weight. - F32 largeFilterReadings[ SIZE_OF_LARGE_LOAD_CELL_AVG ]; ///< Load cell samples for large load cell moving average. - F64 largeFilterTotal; ///< Large filter rolling total - used to calc small load cell moving average. - F32 largeFilteredWeight; ///< Load cell large filtered (40 10Hz filtered sample) weight. + F32 largeFilterReadings[ SIZE_OF_LARGE_LOAD_CELL_AVG ]; ///< Load cell samples for large load cell moving average. + F32 largeFilterTotal; ///< Large filter rolling total - used to calc small load cell moving average. + F32 largeFilteredWeight; ///< Load cell large filtered (40 10Hz filtered sample) weight. } LOADCELL_T; // ********** private data ********** @@ -192,7 +192,6 @@ loadcells[ ii ].weight.data = (F32)loadcells[ ii ].rawReading * ADC2GRAM; loadcells[ ii ].weight.data = loadcells[ ii ].weight.data - loadcells[ ii ].autoCalOffset; - loadcells[ ii ].loadCellVelocity_g_min = ( getLoadCellWeight( (LOAD_CELL_ID_T)ii ) - loadcells[ ii ].smallFilterReadings[ smallReadingsIdx ] ) * (F32)SEC_PER_MIN; // Update small filter with new weight sample Index: firmware/App/Controllers/Pressures.c =================================================================== diff -u -r5a36a768d11cc597a36b894c1fb3a5e5590130f1 -r58d631f8ce26bf420a3fa087d4d1d86833aba002 --- firmware/App/Controllers/Pressures.c (.../Pressures.c) (revision 5a36a768d11cc597a36b894c1fb3a5e5590130f1) +++ firmware/App/Controllers/Pressures.c (.../Pressures.c) (revision 58d631f8ce26bf420a3fa087d4d1d86833aba002) @@ -14,10 +14,12 @@ * @date (original) 04-Apr-2020 * ***************************************************************************/ +#include #include "AlarmMgmt.h" #include "FPGA.h" -#include "InternalADC.h" +#include "InternalADC.h" +#include "NVDataMgmt.h" #include "OperationModes.h" #include "PersistentAlarm.h" #include "Pressures.h" @@ -74,14 +76,16 @@ static U32 pressureFilterCounter = 0; ///< Used to schedule pressure sensor filtering. static PRESSURE_SELF_TEST_STATE_T pressuresSelfTestState; ///< Current pressure self-test state. -static SELF_TEST_STATUS_T pressuresSelfTestResult; ///< Self-test result of the Pressures module. +static SELF_TEST_STATUS_T pressuresSelfTestResult; ///< Self-test result of the Pressures module. +static DG_PRES_SENSORS_CAL_RECORD_T pressuresCalRecord; ///< Pressures calibration record. // ********** private function prototypes ********** static PRESSURE_STATE_T handlePressuresInitState( void ); static PRESSURE_STATE_T handlePressuresContReadState( void ); static void publishPressuresData( void ); -static U32 getPublishPressuresDataInterval( void ); +static U32 getPublishPressuresDataInterval( void ); +static BOOL processCalibrationData( void ); static SELF_TEST_STATUS_T handleSelfTestADCCheck( void ); @@ -94,7 +98,7 @@ *************************************************************************/ void initPressures( void ) { - U32 i; + U32 i; for ( i = 0; i < NUM_OF_PRESSURE_SENSORS; i++ ) { @@ -111,8 +115,84 @@ pressuresDataPublicationTimerCounter = 0; initPersistentAlarm( ALARM_ID_INLET_WATER_LOW_PRESSURE, INLET_WATER_PRESSURE_PERSISTENCE_PERIOD, INLET_WATER_PRESSURE_PERSISTENCE_PERIOD ); - initPersistentAlarm( ALARM_ID_INLET_WATER_PRESSURE_FAULT, INLET_WATER_PRESSURE_PERSISTENCE_PERIOD, INLET_WATER_PRESSURE_PERSISTENCE_PERIOD ); + initPersistentAlarm( ALARM_ID_INLET_WATER_PRESSURE_FAULT, INLET_WATER_PRESSURE_PERSISTENCE_PERIOD, INLET_WATER_PRESSURE_PERSISTENCE_PERIOD ); + + // TODO remove, temporary code + for ( i = 0; i < NUM_OF_CAL_DATA_PRES_SENSORS; i++ ) + { + pressuresCalRecord.pressureSensors[ i ].fourthOrderCoeff = 0.0; + pressuresCalRecord.pressureSensors[ i ].thirdOrderCoeff = 0.0; + pressuresCalRecord.pressureSensors[ i ].secondOrderCoeff = 0.0; + pressuresCalRecord.pressureSensors[ i ].gain = 1.0; + pressuresCalRecord.pressureSensors[ i ].offset = 0.0; + } + // TODO remove, temporary code } + +/*********************************************************************//** + * @brief + * The checkInletPressure function checks inlet water pressure value + * and triggers an alarm when pressure value is out of allowed range. + * @details Inputs: RO pump inlet pressure sensor value + * @details Outputs: Triggers low pressure persistent alarm + * @return none + *************************************************************************/ +void checkInletPressure( void ) +{ +#ifndef DISABLE_WATER_QUALITY_CHECK + F32 const pressure = getMeasuredDGPressure( PRESSURE_SENSOR_RO_PUMP_INLET ); + BOOL const isPressureTooLow = ( pressure < MIN_INLET_WATER_PRESSURE ); + + checkPersistentAlarm( ALARM_ID_INLET_WATER_LOW_PRESSURE, isPressureTooLow, pressure, MIN_INLET_WATER_PRESSURE ); +#endif +} + +/*********************************************************************//** + * @brief + * The checkInletPressureFault function checks inlet water pressure value + * and triggers a machine fault when pressure value is out of allowed range. + * @details Inputs: RO pump inlet pressure sensor value + * @details Outputs: Triggers pressure fault persistent alarm + * @return none + *************************************************************************/ +void checkInletPressureFault( void ) +{ + F32 const pressure = getMeasuredDGPressure( PRESSURE_SENSOR_RO_PUMP_INLET ); + BOOL const isPressureTooLow = ( pressure < MIN_INLET_WATER_PRESSURE ); + + checkPersistentAlarm( ALARM_ID_INLET_WATER_PRESSURE_FAULT, isPressureTooLow, pressure, MIN_INLET_WATER_PRESSURE ); +} + +/*********************************************************************//** + * @brief + * The getMeasuredArterialPressure function gets the current arterial pressure. + * @details Inputs: arterialPressure + * @details Outputs: none + * @param pressureID pressure sensor ID + * @return the current arterial pressure (in mmHg). + *************************************************************************/ +F32 getMeasuredDGPressure( U32 pressureID ) +{ + F32 result = 0.0; + + if ( pressureID < NUM_OF_PRESSURE_SENSORS ) + { + if ( OVERRIDE_KEY == pressures[ pressureID ].override ) + { + result = pressures[ pressureID ].ovData; + } + else + { + result = pressures[ pressureID ].data; + } + } + else + { + activateAlarmNoData( ALARM_ID_DG_SOFTWARE_FAULT ); + } + + return result; +} /*********************************************************************//** * @brief @@ -143,6 +223,39 @@ // publish pressure/occlusion data on interval publishPressuresData(); +} + +/*********************************************************************//** + * @brief + * The execPressureSelfTest function executes the pressures self-test's state machine. + * @details Inputs: pressuresSelfTestState + * @details Outputs: pressuresSelfTestState + * @return PressuresSelfTestResult (SELF_TEST_STATUS_T) + *************************************************************************/ +SELF_TEST_STATUS_T execPressureSelfTest( void ) +{ + switch ( pressuresSelfTestState ) + { + case PRESSURE_SELF_TEST_STATE_START: + pressuresSelfTestState = PRESSURE_TEST_STATE_IN_PROGRESS; + pressuresSelfTestResult = SELF_TEST_STATUS_IN_PROGRESS; + break; + + case PRESSURE_TEST_STATE_IN_PROGRESS: + pressuresSelfTestResult = handleSelfTestADCCheck(); + pressuresSelfTestState = PRESSURE_TEST_STATE_COMPLETE; + break; + + case PRESSURE_TEST_STATE_COMPLETE: + // Done with self-test + break; + + default: + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_TEMPERATURE_SENSORS_INVALID_SELF_TEST_STATE, pressuresSelfTestState ); + break; + } + + return pressuresSelfTestResult; } /*********************************************************************//** @@ -182,40 +295,62 @@ measuredPressureReadingsSum[ PRESSURE_SENSOR_RO_PUMP_INLET ] += measuredPressureReadingsRaw[ PRESSURE_SENSOR_RO_PUMP_INLET ]; measuredPressureReadingsSum[ PRESSURE_SENSOR_RO_PUMP_OUTLET ] += measuredPressureReadingsRaw[ PRESSURE_SENSOR_RO_PUMP_OUTLET ]; measuredPressureReadingsSum[ PRESSURE_SENSOR_DRAIN_PUMP_INLET ] += measuredPressureReadingsRaw[ PRESSURE_SENSOR_DRAIN_PUMP_INLET ]; - measuredPressureReadingsSum[ PRESSURE_SENSOR_DRAIN_PUMP_OUTLET ] += measuredPressureReadingsRaw[ PRESSURE_SENSOR_DRAIN_PUMP_OUTLET ]; + measuredPressureReadingsSum[ PRESSURE_SENSOR_DRAIN_PUMP_OUTLET ] += measuredPressureReadingsRaw[ PRESSURE_SENSOR_DRAIN_PUMP_OUTLET ]; + + // Check if a new calibration is available + if ( TRUE == isNewCalibrationRecordAvailable() ) + { + // Get the new calibration data and check its validity + processCalibrationData(); + } // filter every 200ms if ( ++pressureFilterCounter >= PRESSURE_SAMPLES_TO_AVERAGE ) - { // calculate average pressures + { + U32 sensor; + F32 pressuresBeforeCal[ NUM_OF_PRESSURE_SENSORS ]; + + // Calculate average pressures F32 avgRoIn = (F32)measuredPressureReadingsSum[ PRESSURE_SENSOR_RO_PUMP_INLET ] * PRESSURE_AVERAGE_MULTIPLIER; F32 avgRoOut = (F32)measuredPressureReadingsSum[ PRESSURE_SENSOR_RO_PUMP_OUTLET ] * PRESSURE_AVERAGE_MULTIPLIER; F32 avgDrnIn = (F32)measuredPressureReadingsSum[ PRESSURE_SENSOR_DRAIN_PUMP_INLET ] * PRESSURE_AVERAGE_MULTIPLIER; - F32 avgDrnOut = (F32)measuredPressureReadingsSum[ PRESSURE_SENSOR_DRAIN_PUMP_OUTLET ] * PRESSURE_AVERAGE_MULTIPLIER; + F32 avgDrnOut = (F32)measuredPressureReadingsSum[ PRESSURE_SENSOR_DRAIN_PUMP_OUTLET ] * PRESSURE_AVERAGE_MULTIPLIER; + + // Convert average pressure readings to PSI + pressuresBeforeCal[ PRESSURE_SENSOR_RO_PUMP_INLET ] = avgRoIn * PUMP_PRESSURE_PSIA_PER_COUNT - PUMP_PRESSURE_PSIA_TO_PSI_OFFSET; + pressuresBeforeCal[ PRESSURE_SENSOR_RO_PUMP_OUTLET ] = avgRoOut * PUMP_PRESSURE_PSIA_PER_COUNT - PUMP_PRESSURE_PSIA_TO_PSI_OFFSET; + pressuresBeforeCal[ PRESSURE_SENSOR_DRAIN_PUMP_INLET ] = avgDrnIn * PUMP_PRESSURE_PSIA_PER_COUNT - PUMP_PRESSURE_PSIA_TO_PSI_OFFSET; + pressuresBeforeCal[ PRESSURE_SENSOR_DRAIN_PUMP_OUTLET ] = avgDrnOut * PUMP_PRESSURE_PSIA_PER_COUNT - PUMP_PRESSURE_PSIA_TO_PSI_OFFSET; - // reset average counter - pressureFilterCounter = 0; + // Reset average counter + pressureFilterCounter = 0; + + // Apply calibration to the pressure values prior to storing them + for ( sensor = 0; sensor < NUM_OF_PRESSURE_SENSORS; sensor++ ) + { + pressures[ sensor ].data = pow(pressuresBeforeCal[ sensor ], 4) * pressuresCalRecord.pressureSensors[ sensor ].fourthOrderCoeff + + pow(pressuresBeforeCal[ sensor ], 3) * pressuresCalRecord.pressureSensors[ sensor ].thirdOrderCoeff + + pow(pressuresBeforeCal[ sensor ], 2) * pressuresCalRecord.pressureSensors[ sensor ].secondOrderCoeff + + pressuresBeforeCal[ sensor ] * pressuresCalRecord.pressureSensors[ sensor ].gain + + pressuresCalRecord.pressureSensors[ sensor ].offset; + } - // convert average pressure readings to PSI - pressures[ PRESSURE_SENSOR_RO_PUMP_INLET ].data = avgRoIn * PUMP_PRESSURE_PSIA_PER_COUNT - PUMP_PRESSURE_PSIA_TO_PSI_OFFSET; - pressures[ PRESSURE_SENSOR_RO_PUMP_OUTLET ].data = avgRoOut * PUMP_PRESSURE_PSIA_PER_COUNT - PUMP_PRESSURE_PSIA_TO_PSI_OFFSET; - pressures[ PRESSURE_SENSOR_DRAIN_PUMP_INLET ].data = avgDrnIn * PUMP_PRESSURE_PSIA_PER_COUNT - PUMP_PRESSURE_PSIA_TO_PSI_OFFSET; - pressures[ PRESSURE_SENSOR_DRAIN_PUMP_OUTLET ].data = avgDrnOut * PUMP_PRESSURE_PSIA_PER_COUNT - PUMP_PRESSURE_PSIA_TO_PSI_OFFSET; - // reset sums for next averaging measuredPressureReadingsSum[ PRESSURE_SENSOR_RO_PUMP_INLET ] = 0; measuredPressureReadingsSum[ PRESSURE_SENSOR_RO_PUMP_OUTLET ] = 0; measuredPressureReadingsSum[ PRESSURE_SENSOR_DRAIN_PUMP_INLET ] = 0; measuredPressureReadingsSum[ PRESSURE_SENSOR_DRAIN_PUMP_OUTLET ] = 0; - } - + } + // TODO - any other checks return result; } /*********************************************************************//** * @brief - * The getPublishPressuresDataInterval function gets the pressure data publish internval. + * The getPublishPressuresDataInterval function gets the pressure data + * publish interval. * @details Inputs: pressuresDataPublishInterval * @details Outputs: none * @return the current pressures data publication interval (in task intervals). @@ -230,75 +365,53 @@ } return result; -} +} /*********************************************************************//** * @brief - * The checkInletPressure function checks inlet water pressure value - * and triggers an alarm when pressure value is out of allowed range. - * @details Inputs: RO pump inlet pressure sensor value - * @details Outputs: Triggers low pressure persistent alarm - * @return none + * The processCalibrationData function gets the calibration data and makes + * sure it is valid by checking the calibration date. The calibration date + * should not be 0. + * @details Inputs: none + * @details Outputs: loadCellsCalRecord + * @return TRUE if the calibration record is valid, otherwise FALSE *************************************************************************/ -void checkInletPressure( void ) +static BOOL processCalibrationData( void ) { -#ifndef DISABLE_WATER_QUALITY_CHECK - F32 const pressure = getMeasuredDGPressure( PRESSURE_SENSOR_RO_PUMP_INLET ); - BOOL const isPressureTooLow = ( pressure < MIN_INLET_WATER_PRESSURE ); + BOOL status = TRUE; + U32 sensor; - checkPersistentAlarm( ALARM_ID_INLET_WATER_LOW_PRESSURE, isPressureTooLow, pressure, MIN_INLET_WATER_PRESSURE ); + // Get the calibration record from NVDataMgmt + DG_PRES_SENSORS_CAL_RECORD_T calData = getDGPressureSensorsCalibrationRecord(); + + for ( sensor = 0; sensor < NUM_OF_CAL_DATA_PRES_SENSORS; sensor++ ) + { + // Check if the calibration data that was received from NVDataMgmt is legitimate + // The calibration date item should not be zero. If the calibration date is 0, + // then the data is not stored in the NV memory or it was corrupted. + if ( 0 == calData.pressureSensors[ sensor ].calibrationTime ) + { +#ifndef DISABLE_CAL_CHECK + SET_ALARM_WITH_1_U32_DATA( ALARM_ID_DG_PRESSURE_SENSORS_INVALID_CALIBRATION, (U32)sensor ); #endif -} + status = FALSE; + } + else + { + // The calibration data was valid, update the local copy + pressuresCalRecord.pressureSensors[ sensor ].fourthOrderCoeff = calData.pressureSensors[ sensor ].fourthOrderCoeff; + pressuresCalRecord.pressureSensors[ sensor ].thirdOrderCoeff = calData.pressureSensors[ sensor ].thirdOrderCoeff; + pressuresCalRecord.pressureSensors[ sensor ].secondOrderCoeff = calData.pressureSensors[ sensor ].secondOrderCoeff; + pressuresCalRecord.pressureSensors[ sensor ].gain = calData.pressureSensors[ sensor ].gain; + pressuresCalRecord.pressureSensors[ sensor ].offset = calData.pressureSensors[ sensor ].offset; + } + } -/*********************************************************************//** - * @brief - * The checkInletPressureFault function checks inlet water pressure value - * and triggers a machine fault when pressure value is out of allowed range. - * @details Inputs: RO pump inlet pressure sensor value - * @details Outputs: Triggers pressure fault persistent alarm - * @return none - *************************************************************************/ -void checkInletPressureFault( void ) -{ - F32 const pressure = getMeasuredDGPressure( PRESSURE_SENSOR_RO_PUMP_INLET ); - BOOL const isPressureTooLow = ( pressure < MIN_INLET_WATER_PRESSURE ); - - checkPersistentAlarm( ALARM_ID_INLET_WATER_PRESSURE_FAULT, isPressureTooLow, pressure, MIN_INLET_WATER_PRESSURE ); + return status; } /*********************************************************************//** * @brief - * The getMeasuredArterialPressure function gets the current arterial pressure. - * @details Inputs: arterialPressure - * @details Outputs: none - * @param pressureID pressure sensor ID - * @return the current arterial pressure (in mmHg). - *************************************************************************/ -F32 getMeasuredDGPressure( U32 pressureID ) -{ - F32 result = 0.0; - - if ( pressureID < NUM_OF_PRESSURE_SENSORS ) - { - if ( OVERRIDE_KEY == pressures[ pressureID ].override ) - { - result = pressures[ pressureID ].ovData; - } - else - { - result = pressures[ pressureID ].data; - } - } - else - { - SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_PRESSURE_SENSOR_ID, pressureID ); - } - - return result; -} - -/*********************************************************************//** - * @brief * The publishPressuresData function publishes DG pressures data at a set interval. * @details Inputs: pressuresDataPublicationTimerCounter * @details Outputs: Pressures data are published to CAN bus @@ -319,39 +432,6 @@ } } -/*********************************************************************//** - * @brief - * The execPressureSelfTest function executes the pressures self-test's state machine. - * @details Inputs: pressuresSelfTestState - * @details Outputs: pressuresSelfTestState - * @return PressuresSelfTestResult (SELF_TEST_STATUS_T) - *************************************************************************/ -SELF_TEST_STATUS_T execPressureSelfTest( void ) -{ - switch ( pressuresSelfTestState ) - { - case PRESSURE_SELF_TEST_STATE_START: - pressuresSelfTestState = PRESSURE_TEST_STATE_IN_PROGRESS; - pressuresSelfTestResult = SELF_TEST_STATUS_IN_PROGRESS; - break; - - case PRESSURE_TEST_STATE_IN_PROGRESS: - pressuresSelfTestResult = handleSelfTestADCCheck(); - pressuresSelfTestState = PRESSURE_TEST_STATE_COMPLETE; - break; - - case PRESSURE_TEST_STATE_COMPLETE: - // Done with self-test - break; - - default: - SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_TEMPERATURE_SENSORS_INVALID_SELF_TEST_STATE, pressuresSelfTestState ); - break; - } - - return pressuresSelfTestResult; -} - /*********************************************************************//** * @brief * The handleSelfTestADCCheck function checks whether the ADC reads and @@ -366,7 +446,7 @@ SELF_TEST_STATUS_T result = SELF_TEST_STATUS_PASSED; U16 const inletPressureADCReading = getIntADCReading( INT_ADC_RO_PUMP_INLET_PRESSURE ); - if ( ( inletPressureADCReading == 0 ) || ( inletPressureADCReading >= INT_ADC_FULL_SCALE_BITS ) ) + if ( ( 0 == inletPressureADCReading ) || ( inletPressureADCReading >= INT_ADC_FULL_SCALE_BITS ) ) { result = SELF_TEST_STATUS_FAILED; SET_ALARM_WITH_1_U32_DATA( ALARM_ID_PRESSURE_SENSOR_FAULT, inletPressureADCReading ); Index: firmware/App/Controllers/ROPump.c =================================================================== diff -u -r5a36a768d11cc597a36b894c1fb3a5e5590130f1 -r58d631f8ce26bf420a3fa087d4d1d86833aba002 --- firmware/App/Controllers/ROPump.c (.../ROPump.c) (revision 5a36a768d11cc597a36b894c1fb3a5e5590130f1) +++ firmware/App/Controllers/ROPump.c (.../ROPump.c) (revision 58d631f8ce26bf420a3fa087d4d1d86833aba002) @@ -21,9 +21,10 @@ #include "mibspi.h" #include "FPGA.h" +#include "NVDataMgmt.h" #include "OperationModes.h" -#include "PIControllers.h" #include "PersistentAlarm.h" +#include "PIControllers.h" #include "Pressures.h" #include "ROPump.h" #include "SafetyShutdown.h" @@ -131,6 +132,7 @@ static S32 measuredFlowReadingsSum = 0; ///< Raw flow reading sums for averaging. static U32 flowFilterCounter = 0; ///< Flow filtering counter. +static DG_FLOW_SENSORS_CAL_RECORD_T flowSensorsCalRecord; ///< Flow sensors calibration record. // ********** private function prototypes ********** @@ -145,6 +147,7 @@ static void stopROPump( void ); static void publishROPumpData( void ); static U32 getPublishROPumpDataInterval( void ); +static BOOL processCalibrationData( void ); /*********************************************************************//** * @brief @@ -188,6 +191,15 @@ roPumpState = RO_PUMP_OFF_STATE; roPumpControlMode = NUM_OF_PUMP_CONTROL_MODES; isROPumpOn = FALSE; + + // TODO temporary initialization + // Reset the calibration variables + flowSensorsCalRecord.flowSensors[ CAL_DATA_RO_PUMP_FLOW_SENSOR ].fourthOrderCoeff = 0.0; + flowSensorsCalRecord.flowSensors[ CAL_DATA_RO_PUMP_FLOW_SENSOR ].thirdOrderCoeff = 0.0; + flowSensorsCalRecord.flowSensors[ CAL_DATA_RO_PUMP_FLOW_SENSOR ].secondOrderCoeff = 0.0; + flowSensorsCalRecord.flowSensors[ CAL_DATA_RO_PUMP_FLOW_SENSOR ].gain = 1.0; + flowSensorsCalRecord.flowSensors[ CAL_DATA_RO_PUMP_FLOW_SENSOR ].offset = 0.0; + // TODO temporary initialization, remove } /*********************************************************************//** @@ -331,20 +343,38 @@ BOOL isPressureMax = actualPressure >= MAX_ALLOWED_MEASURED_PRESSURE_PSI; checkPersistentAlarm( ALARM_ID_RO_PUMP_PRESSURE_OUT_OF_RANGE, isPressureMax, actualPressure, MAX_ALLOWED_MEASURED_PRESSURE_PSI ); + // Check if a new calibration is available + if ( TRUE == isNewCalibrationRecordAvailable() ) + { + // Get the new calibration data and check its validity + processCalibrationData(); + } + // Read flow at the control set if ( ++flowFilterCounter >= FLOW_SAMPLES_TO_AVERAGE ) { - F32 avgROFlow = (F32)measuredFlowReadingsSum * FLOW_AVERAGE_MULTIPLIER; + U32 sensor; + F32 flow = RO_FLOW_ADC_TO_LPM_FACTOR / ( (F32)measuredFlowReadingsSum * FLOW_AVERAGE_MULTIPLIER ); + // If the flow is less than a certain value, FPGA will return 0xFFFF meaning that // the flow is 0. Otherwise, convert the count to flow rate in mL/min - if ( ( roFlowReading == FLOW_SENSOR_ZERO_READING ) || ( roFlowReading == 0 ) ) + if ( ( FLOW_SENSOR_ZERO_READING == roFlowReading ) || ( 0 == roFlowReading ) ) { measuredROFlowRateLPM.data = 0.0; } else { - measuredROFlowRateLPM.data = RO_FLOW_ADC_TO_LPM_FACTOR / avgROFlow; + // Right now there is only one flow sensor but a for loop is used here to be able to automatically accommodate + // the future flow sensors + for( sensor = 0; sensor < NUM_OF_CAL_DATA_FLOW_SENSORS; sensor++ ) + { + measuredROFlowRateLPM.data = pow(flow, 4) * flowSensorsCalRecord.flowSensors[ sensor ].fourthOrderCoeff + + pow(flow, 3) * flowSensorsCalRecord.flowSensors[ sensor ].thirdOrderCoeff + + pow(flow, 2) * flowSensorsCalRecord.flowSensors[ sensor ].secondOrderCoeff + + flow * flowSensorsCalRecord.flowSensors[ sensor ].gain + + flowSensorsCalRecord.flowSensors[ sensor ].offset; + } } measuredFlowReadingsSum = 0; @@ -462,6 +492,51 @@ /*********************************************************************//** * @brief + * The getTargetROPumpFlowRate function gets the current target RO pump + * flow rate. + * @details Inputs: targetROPumpFlowRate + * @details Outputs: targetROPumpFlowRate + * @return the current target RO flow rate (in L/min). + *************************************************************************/ +F32 getTargetROPumpFlowRate( void ) +{ + return targetROPumpFlowRate; +} + +/*********************************************************************//** + * @brief + * The getMeasuredROFlowRate function gets the measured RO pump flow rate. + * @details Inputs: measuredROFlowRateLPM + * @details Outputs: measuredROFlowRateLPM + * @return the current RO pump flow rate (in L/min). + *************************************************************************/ +F32 getMeasuredROFlowRate( void ) +{ + F32 result = measuredROFlowRateLPM.data; + + if ( OVERRIDE_KEY == measuredROFlowRateLPM.override ) + { + result = measuredROFlowRateLPM.ovData; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The getTargetROPumpPressure function gets the current target RO pump + * pressure. + * @details Inputs: targetROPumpPressure + * @details Outputs: targetROPumpPressure + * @return the current target RO targetROPumpPressure in psi. + *************************************************************************/ +F32 getTargetROPumpPressure( void ) +{ + return targetROPumpMaxPressure; +} + +/*********************************************************************//** + * @brief * The handleROPumpOffState function handles the RO pump off state of the * controller state machine. * @details Inputs: roPumpControlMode, roPumpPWMDutyCyclePctSet, @@ -717,51 +792,49 @@ /*********************************************************************//** * @brief - * The getTargetROPumpFlowRate function gets the current target RO pump - * flow rate. - * @details Inputs: targetROPumpFlowRate - * @details Outputs: targetROPumpFlowRate - * @return the current target RO flow rate (in L/min). + * The processCalibrationData function gets the calibration data and makes + * sure it is valid by checking the calibration date. The calibration date + * should not be 0. + * @details Inputs: none + * @details Outputs: flowSensorsCalRecord + * @return TRUE if the calibration record is valid, otherwise FALSE *************************************************************************/ -F32 getTargetROPumpFlowRate( void ) +static BOOL processCalibrationData( void ) { - return targetROPumpFlowRate; -} + BOOL status = TRUE; + U32 sensor; -/*********************************************************************//** - * @brief - * The getMeasuredROFlowRate function gets the measured RO pump flow rate. - * @details Inputs: measuredROFlowRateLPM - * @details Outputs: measuredROFlowRateLPM - * @return the current RO pump flow rate (in L/min). - *************************************************************************/ -F32 getMeasuredROFlowRate( void ) -{ - F32 result = measuredROFlowRateLPM.data; + // Get the calibration record from NVDataMgmt + DG_FLOW_SENSORS_CAL_RECORD_T calData = getDGFlowSensorsCalibrationRecord(); - if ( OVERRIDE_KEY == measuredROFlowRateLPM.override ) + for ( sensor = 0; sensor < NUM_OF_CAL_DATA_FLOW_SENSORS; sensor++ ) { - result = measuredROFlowRateLPM.ovData; + // Check if the calibration data that was received from NVDataMgmt is legitimate + // The calibration date item should not be zero. If the calibration date is 0, + // then the flow sensors data is not stored in the NV memory or it was corrupted. + if ( 0 == calData.flowSensors[ sensor ].calibrationTime ) + { +#ifndef DISABLE_CAL_CHECK + SET_ALARM_WITH_1_U32_DATA( ALARM_ID_DG_FLOW_SENSORS_INVALID_CALIBRATION, (U32)sensor ); +#endif + status = FALSE; + } + else + { + // The calibration data was valid, update the local copy + flowSensorsCalRecord.flowSensors[ sensor ].fourthOrderCoeff = calData.flowSensors[ sensor ].fourthOrderCoeff; + flowSensorsCalRecord.flowSensors[ sensor ].thirdOrderCoeff = calData.flowSensors[ sensor ].thirdOrderCoeff; + flowSensorsCalRecord.flowSensors[ sensor ].secondOrderCoeff = calData.flowSensors[ sensor ].secondOrderCoeff; + flowSensorsCalRecord.flowSensors[ sensor ].gain = calData.flowSensors[ sensor ].gain; + flowSensorsCalRecord.flowSensors[ sensor ].offset = calData.flowSensors[ sensor ].offset; + } } - return result; + return status; } /*********************************************************************//** * @brief - * The getTargetROPumpPressure function gets the current target RO pump - * pressure. - * @details Inputs: targetROPumpPressure - * @details Outputs: targetROPumpPressure - * @return the current target RO targetROPumpPressure in psi. - *************************************************************************/ -F32 getTargetROPumpPressure( void ) -{ - return targetROPumpMaxPressure; -} - -/*********************************************************************//** - * @brief * The publishROPumpData function publishes RO pump data at the set interval. * @details Inputs: roPumpDataPublicationTimerCounter * @details Outputs: roPumpDataPublicationTimerCounter @@ -785,6 +858,7 @@ } } + /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ Index: firmware/App/DGCommon.h =================================================================== diff -u -r6f1f269cd7d91f41c51797d17a85a7ea249e21f3 -r58d631f8ce26bf420a3fa087d4d1d86833aba002 --- firmware/App/DGCommon.h (.../DGCommon.h) (revision 6f1f269cd7d91f41c51797d17a85a7ea249e21f3) +++ firmware/App/DGCommon.h (.../DGCommon.h) (revision 58d631f8ce26bf420a3fa087d4d1d86833aba002) @@ -23,9 +23,9 @@ // ********** version ********** #define DG_VERSION_MAJOR 0 -#define DG_VERSION_MINOR 5 +#define DG_VERSION_MINOR 6 #define DG_VERSION_MICRO 0 -#define DG_VERSION_BUILD 15 +#define DG_VERSION_BUILD 18 // ********** build switches ********** @@ -37,8 +37,8 @@ // #define SIMULATE_UI 1 // #define TASK_TIMING_OUTPUT_ENABLED 1 // re-purposes drain pump enable pin for task timing // #define DISABLE_HEATERS_AND_TEMPS 1 -// #define DISABLE_ACCELS 1 -// #define SKIP_POST 1 + #define DISABLE_ACCELS 1 + #define SKIP_POST 1 #define DISABLE_CAL_CHECK 1 // #define ENABLE_DIP_SWITCHES 1 // #define EMC_TEST_BUILD 1 @@ -49,12 +49,16 @@ // #define IGNORE_DRAIN_PUMP_MONITOR 1 // #define IGNORE_HEATERS_MONITOR 1 #define IGNORE_RO_PUMP_MONITOR 1 -// #define DISABLE_RO_RATIO_CHECK 1 + #define IGNORE_DISINFECT_RSRVR_TIMEOUT 1 + #define DISABLE_RO_RATIO_CHECK 1 #define DISABLE_COND_SENSOR_CHECK 1 #define DISABLE_MIXING 1 #define DISABLE_WATER_QUALITY_CHECK 1 #define DISABLE_RTC_CONFIG 1 - //#define V_2_SYSTEM 1 +// #define V_2_SYSTEM 1 + #define DISABLE_UV_REACTOR_MONITOR 1 + #define IGNORE_HEAT_DISINFECT_RSRVR_TIMEOUT 1 + #define THD_USING_TRO_CONNECTOR 1 #define IGNORE_CONC_PUMP_IN_HEAT_DISINFECT 1 #include Index: firmware/App/Modes/ModeFlush.c =================================================================== diff -u -r6f1f269cd7d91f41c51797d17a85a7ea249e21f3 -r58d631f8ce26bf420a3fa087d4d1d86833aba002 --- firmware/App/Modes/ModeFlush.c (.../ModeFlush.c) (revision 6f1f269cd7d91f41c51797d17a85a7ea249e21f3) +++ firmware/App/Modes/ModeFlush.c (.../ModeFlush.c) (revision 58d631f8ce26bf420a3fa087d4d1d86833aba002) @@ -662,7 +662,7 @@ } } // Once reservoir 2 is full, reservoir 1 must be partially full - else if( DG_RESERVOIR_REACHED_TARGET == rsrvr2Status ) + else if ( DG_RESERVOIR_REACHED_TARGET == rsrvr2Status ) { rsrvr1Status = getRsrvrFillStatus( DG_RESERVOIR_1, RSRVRS_PARTIAL_FILL_VOL_ML, RSRVRS_PARTIAL_FILL_TIMEOUT_MS ); @@ -690,7 +690,7 @@ } } // Check if reservoir 2 fill time out - else if( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr2Status ) + else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr2Status ) { state = DG_FLUSH_STATE_CANCEL_WATER_PATH; } Index: firmware/App/Modes/ModeHeatDisinfect.c =================================================================== diff -u -r6f1f269cd7d91f41c51797d17a85a7ea249e21f3 -r58d631f8ce26bf420a3fa087d4d1d86833aba002 --- firmware/App/Modes/ModeHeatDisinfect.c (.../ModeHeatDisinfect.c) (revision 6f1f269cd7d91f41c51797d17a85a7ea249e21f3) +++ firmware/App/Modes/ModeHeatDisinfect.c (.../ModeHeatDisinfect.c) (revision 58d631f8ce26bf420a3fa087d4d1d86833aba002) @@ -682,7 +682,7 @@ BOOL isTD2Out = ( fabs( TD2Temp - avgTemp ) > MAX_FLUSH_CIRC_TEMP_SENSOR_DIFF_C ? TRUE : FALSE ); // Check if any of the temperature sensors are out of tolerance - if( ( TRUE == isThdOut ) || ( TRUE == isTPoOut ) || ( TRUE == isTD1Out ) || ( TRUE == isTD2Out ) ) + if ( ( TRUE == isThdOut ) || ( TRUE == isTPoOut ) || ( TRUE == isTD1Out ) || ( TRUE == isTD2Out ) ) { // Check if we have exceeded the number of trials. If not, try another time if ( ++stateTrialCounter < MAX_ALLOWED_STATE_TRIALS ) @@ -770,7 +770,7 @@ } } // Once R1 is full, keep monitoring for R2 level and timeout - else if( DG_RESERVOIR_REACHED_TARGET == rsrvr1Status ) + else if ( DG_RESERVOIR_REACHED_TARGET == rsrvr1Status ) { rsrvr2Status = getRsrvrFillStatus( DG_RESERVOIR_2, RSRVRS_PARTIAL_FILL_VOL_ML, RSRVRS_500ML_FILL_UP_TIMEOUT_MS ); @@ -867,7 +867,7 @@ } } // Once R2 is full, R1 must be partially full - else if( DG_RESERVOIR_REACHED_TARGET == rsrvr2Status ) + else if ( DG_RESERVOIR_REACHED_TARGET == rsrvr2Status ) { rsrvr1Status = getRsrvrFillStatus( DG_RESERVOIR_1, RSRVRS_PARTIAL_FILL_VOL_ML, RSRVRS_500ML_FILL_UP_TIMEOUT_MS ); Index: firmware/App/Modes/ModeRecirculate.c =================================================================== diff -u -r6f1f269cd7d91f41c51797d17a85a7ea249e21f3 -r58d631f8ce26bf420a3fa087d4d1d86833aba002 --- firmware/App/Modes/ModeRecirculate.c (.../ModeRecirculate.c) (revision 6f1f269cd7d91f41c51797d17a85a7ea249e21f3) +++ firmware/App/Modes/ModeRecirculate.c (.../ModeRecirculate.c) (revision 58d631f8ce26bf420a3fa087d4d1d86833aba002) @@ -99,8 +99,13 @@ setValveState( VRC, VALVE_STATE_DRAIN_C_TO_NO ); setValveState( VDR, VALVE_STATE_DRAIN_C_TO_NO ); setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); +#ifndef V_2_SYSTEM + setValveStateDelayed( VRD1, VALVE_STATE_CLOSED, 0 ); + setValveStateDelayed( VRD2, VALVE_STATE_CLOSED, 0 ); +#endif signalDrainPumpHardStop(); + stopPrimaryHeater(); requestConcentratePumpsOff( CONCENTRATEPUMPS_CP1_ACID ); requestConcentratePumpsOff( CONCENTRATEPUMPS_CP2_BICARB ); Index: firmware/App/Services/Reservoirs.c =================================================================== diff -u -r6f1f269cd7d91f41c51797d17a85a7ea249e21f3 -r58d631f8ce26bf420a3fa087d4d1d86833aba002 --- firmware/App/Services/Reservoirs.c (.../Reservoirs.c) (revision 6f1f269cd7d91f41c51797d17a85a7ea249e21f3) +++ firmware/App/Services/Reservoirs.c (.../Reservoirs.c) (revision 58d631f8ce26bf420a3fa087d4d1d86833aba002) @@ -139,27 +139,31 @@ { case DG_RESERVOIR_1: activeReservoir.data = (U32)resID; - cmdResponse.rejected = FALSE; - setValveState( VRF, VALVE_STATE_R2_C_TO_NO ); -#ifdef V_2_SYSTEM + cmdResponse.rejected = FALSE; + setValveState( VRF, VALVE_STATE_R2_C_TO_NO ); +#ifndef V_2_SYSTEM + setValveState( VRD1, VALVE_STATE_CLOSED ); +#else setValveState( VRD, VALVE_STATE_R2_C_TO_NO ); #endif - setValveState( VRO, VALVE_STATE_R1_C_TO_NO ); - setValveState( VRI, VALVE_STATE_R1_C_TO_NO ); - break; - - case DG_RESERVOIR_2: + setValveState( VRO, VALVE_STATE_R1_C_TO_NO ); + setValveState( VRI, VALVE_STATE_R1_C_TO_NO ); + break; + + case DG_RESERVOIR_2: activeReservoir.data = (U32)resID; - cmdResponse.rejected = FALSE; + cmdResponse.rejected = FALSE; setValveState( VRF, VALVE_STATE_R1_C_TO_NC ); -#ifdef V_2_SYSTEM +#ifndef V_2_SYSTEM + setValveState( VRD2, VALVE_STATE_CLOSED ); +#else setValveState( VRD, VALVE_STATE_R1_C_TO_NC ); #endif - setValveState( VRO, VALVE_STATE_R2_C_TO_NC ); - setValveState( VRI, VALVE_STATE_R2_C_TO_NC ); - break; - - default: + setValveState( VRO, VALVE_STATE_R2_C_TO_NC ); + setValveState( VRI, VALVE_STATE_R2_C_TO_NC ); + break; + + default: // invalid reservoir given - cmd will be NAK'd w/ false result. cmdResponse.rejectCode = DG_CMD_REQUEST_REJECT_REASON_INVALID_PARAMETER; break;