Index: firmware/App/Controllers/LoadCell.c =================================================================== diff -u -r7b6b599a6f98241bc51bbee65b736b92cb881f01 -r2fff37fa585181917705645494549b5fd4a4d522 --- firmware/App/Controllers/LoadCell.c (.../LoadCell.c) (revision 7b6b599a6f98241bc51bbee65b736b92cb881f01) +++ firmware/App/Controllers/LoadCell.c (.../LoadCell.c) (revision 2fff37fa585181917705645494549b5fd4a4d522) @@ -34,32 +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_ZERO_OFFSET -1500.0 ///< Zero offset (in grams). TODO - right now, this is empty reservoir weight. -#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 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 - F32 autoCalOffset; ///< Load cell auto-calibration offset - F32 loadCellVelocity_g_min; ///< Velocity (in g/min) of load cell. + 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 - 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 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 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 + 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. } LOADCELL_T; // ********** private data ********** @@ -74,8 +75,6 @@ static U32 largeReadingsIdx; ///< Index for next sample in load cell large rolling average sample array. static DG_LOAD_CELLS_CAL_RECORD_T loadCellsCalRecord; ///< Load cells calibration record. -// TODO - gain and offset for load cells should be read from NV Data calibration record. - // ********** private function prototypes ********** static U32 getLoadCellDataPublishInterval( void ); @@ -174,17 +173,39 @@ SET_ALARM_WITH_1_U32_DATA( ALARM_ID_DG_LOAD_CELL_ADC_ERROR, ( a1 | a2 | b1 | b2 ) ) } + // Check if a new calibration is available + if ( isNewCalibrationRecordAvailable() == TRUE ) + { + // Get the new calibration data and check its validity + processCalibrationData(); + + // Zero the current tare values when new calibration data is available + loadcells[ LOAD_CELL_RESERVOIR_1_PRIMARY ].autoCalOffset = 0.0; + loadcells[ LOAD_CELL_RESERVOIR_1_BACKUP ].autoCalOffset = 0.0; + loadcells[ LOAD_CELL_RESERVOIR_2_PRIMARY ].autoCalOffset = 0.0; + loadcells[ LOAD_CELL_RESERVOIR_2_BACKUP ].autoCalOffset = 0.0; + } + // Rolling average of last 100 raw samples in small filter for ( ii = 0; ii < NUM_OF_LOAD_CELLS; ++ii ) { - loadcells[ ii ].weight.data = (F32)loadcells[ ii ].rawReading * ADC2GRAM + LOAD_CELL_ZERO_OFFSET - loadcells[ ii ].autoCalOffset; + loadcells[ ii ].weight.data = (F32)loadcells[ ii ].rawReading * ADC2GRAM; + // Apply the calibration factors to the data. + // load_cell_weight = fourth_order_coeff * (load_cell^4) + third_order_coeff * (load_cell^3) + second_order_coeff * (load_cell^2) + gain * load_cell + offset + loadcells[ ii ].weight.data = pow(loadcells[ ii ].weight.data, 4) * loadCellsCalRecord.loadCells[ (CAL_DATA_DG_LOAD_CELLS_T)ii ].fourthOrderCoeff + + pow(loadcells[ ii ].weight.data, 3) * loadCellsCalRecord.loadCells[ (CAL_DATA_DG_LOAD_CELLS_T)ii ].thirdOrderCoeff + + pow(loadcells[ ii ].weight.data, 2) * loadCellsCalRecord.loadCells[ (CAL_DATA_DG_LOAD_CELLS_T)ii ].secondOrderCoeff + + loadcells[ ii ].weight.data * loadCellsCalRecord.loadCells[ (CAL_DATA_DG_LOAD_CELLS_T)ii ].gain + + loadCellsCalRecord.loadCells[ (CAL_DATA_DG_LOAD_CELLS_T)ii ].offset; + 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 loadcells[ ii ].smallFilterTotal -= loadcells[ ii ].smallFilterReadings[ smallReadingsIdx ]; loadcells[ ii ].smallFilterReadings[ smallReadingsIdx ] = getLoadCellWeight( (LOAD_CELL_ID_T)ii ); loadcells[ ii ].smallFilterTotal += getLoadCellWeight( (LOAD_CELL_ID_T)ii ); - loadcells[ ii ].smallFilteredWeight = loadcells[ ii ].smallFilterTotal / (F32)SIZE_OF_SMALL_LOAD_CELL_AVG; + loadcells[ ii ].smallFilteredWeight = (F32)( loadcells[ ii ].smallFilterTotal / (F64)SIZE_OF_SMALL_LOAD_CELL_AVG ); } smallReadingsIdx = INC_WRAP( smallReadingsIdx, 0, SIZE_OF_SMALL_LOAD_CELL_AVG - 1 ); @@ -198,7 +219,7 @@ loadcells[ ii ].largeFilterTotal -= loadcells[ ii ].largeFilterReadings[ largeReadingsIdx ]; loadcells[ ii ].largeFilterReadings[ largeReadingsIdx ] = loadcells[ ii ].smallFilteredWeight; loadcells[ ii ].largeFilterTotal += loadcells[ ii ].smallFilteredWeight; - loadcells[ ii ].largeFilteredWeight = loadcells[ ii ].largeFilterTotal / (F32)SIZE_OF_LARGE_LOAD_CELL_AVG; + loadcells[ ii ].largeFilteredWeight = (F32)( loadcells[ ii ].largeFilterTotal / (F64)SIZE_OF_LARGE_LOAD_CELL_AVG ); } loadCellFilterTimerCount = 0; @@ -255,8 +276,32 @@ *************************************************************************/ void tareLoadCell( LOAD_CELL_ID_T loadCellID ) { - // Add old auto calibration offset to get back to actual weight value - loadcells[ loadCellID ].autoCalOffset = ( loadcells[ loadCellID ].smallFilteredWeight + loadcells[ loadCellID ].autoCalOffset ); + BOOL isWeightOutOfRange = FALSE; + + F32 weight = getLoadCellSmallFilteredWeight( loadCellID ); + + // Check if the load cell is being tared for the first time + if ( fabs(loadcells[ loadCellID ].autoCalOffset) < NEARLY_ZERO ) + { + // For the first tare, the weight of the reservoir should be considered + // The current weight of the load cell should not be greater than the weight of the reservoir + the extra weight + F32 deltaWeight = fabs( weight - EMPTY_RESERVOIR_WEIGHT_GRAMS ); + isWeightOutOfRange = ( deltaWeight > MAX_ALLOWED_EXTRA_WEIGHT_BEFORE_TARE_GRAMS ? TRUE : FALSE ); + } + else + { + isWeightOutOfRange = ( fabs(weight) > MAX_ALLOWED_EXTRA_WEIGHT_BEFORE_TARE_GRAMS ? TRUE : FALSE ); + } + + if ( FALSE == isWeightOutOfRange ) + { + // Add old auto calibration offset to get back to actual weight value + loadcells[ loadCellID ].autoCalOffset = ( loadcells[ loadCellID ].smallFilteredWeight + loadcells[ loadCellID ].autoCalOffset ); + } + else + { + SET_ALARM_WITH_1_F32_DATA( ALARM_ID_DG_LOAD_CELLS_TARE_WEIGHT_OUT_OF_RANGE, weight ) + } } /*********************************************************************//**