Skip to content

Commit

Permalink
Improve reading stability and implement an abort option
Browse files Browse the repository at this point in the history
  • Loading branch information
GatCode committed Apr 28, 2021
1 parent be30e54 commit c6025ca
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 71 deletions.
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ Since the usage of load cells for accurate weight measurement is always correlat

This library provides an easy way to accommodate for these errors without sacrificing measurement speed or the implementation of accurate timing measurements.

Below you can see an example how the ScaleStabilizer can improve the reading stability in a real-world project.

![](assets/ScaleStabilizer.gif)

The idea for the two basic algorithms behind this library were borrowed from *Colm Slattery* *and Mariah Nie's* [A Reference Design for High-Performance, Low-Cost Weigh Scales | Analog Devices](https://www.analog.com/en/analog-dialogue/articles/a-reference-design-for-weigh-scales.html), which describes an excellent way to solve these problems.

## Basic principle
Expand All @@ -41,8 +45,8 @@ const int SCK_PIN = 22;
HX711 scale;

// Scale Stabilizer
const int WINDOW_SIZE = 10;
const double WEIGHT_THRESHOLD = 0.01;
const int WINDOW_SIZE = 12;
const double WEIGHT_THRESHOLD = 0.05;
ScaleStabilizer stabilizer;

void setup() {
Expand Down
Binary file added assets/ScaleStabilizer.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions examples/basic/basic.ino
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ const int SCK_PIN = 22;
HX711 scale;

// Scale Stabilizer
const int WINDOW_SIZE = 10;
const double WEIGHT_THRESHOLD = 0.01;
const int WINDOW_SIZE = 12;
const double WEIGHT_THRESHOLD = 0.05;
ScaleStabilizer stabilizer;

void setup() {
Expand All @@ -25,4 +25,4 @@ void loop() {
double adcReading = scale.get_units();
stabilizer.add(adcReading);
Serial.println(stabilizer.getStablilizedReading());
}
}
2 changes: 1 addition & 1 deletion library.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ScaleStabilizer",
"version": "1.0.2",
"version": "1.1.0",
"description": "A library to stabilize load cell readings",
"keywords": "scale, load cell, HC711",
"repository":
Expand Down
2 changes: 1 addition & 1 deletion library.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name=ScaleStabilizer
version=1.0.2
version=1.1.0
author=GatCode <[email protected]>
maintainer=GatCode <[email protected]>
sentence=A library to stabilize load cell readings.
Expand Down
109 changes: 53 additions & 56 deletions src/ScaleStabilizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,33 +14,41 @@ ScaleStabilizer::~ScaleStabilizer()
}

/*!
* @brief simple average calculation
* @brief M-2 average calculation
*/
double ScaleStabilizer::getAvg()
{
double sum = 0;

// find min and max values
int minIdx = 0;
double minReading = _window[minIdx];
int maxIdx = 0;
double maxReading = _window[maxIdx];
for (int i = 0; i < _windowSize; i++)
{
sum += _window[i];
if (_window[i] < minReading)
{
minIdx = i;
minReading = _window[i];
}
if (_window[i] > maxReading)
{
maxIdx = i;
maxReading = _window[i];
}
}

return sum / _windowSize;
}

/*!
* @brief checks if all the elements in the moving _window are the same
*/
bool ScaleStabilizer::isBufferMonotone()
{
// remove min and max from measurements
double sum = 0;
for (int i = 0; i < _windowSize; i++)
{
if (_window[i] != _window[0])
if (i == minIdx || i == maxIdx)
{
return false;
continue;
}
sum += _window[i];
}
return true;

return sum / (_windowSize - 2);
}

/*!
Expand All @@ -54,6 +62,12 @@ void ScaleStabilizer::begin(int windowSize, double weightThreshold)
_forceOverwrite = _windowSize;
_currentReadingIdx = 0;
_lastOutputValue = 0.0;
_abortCounter = _windowSize / 2;

for (int i = 0; i < _windowSize; i++)
{
_window[i] = 0.0;
}
}

/*!
Expand All @@ -62,7 +76,7 @@ void ScaleStabilizer::begin(int windowSize, double weightThreshold)
*/
void ScaleStabilizer::add(double reading)
{
if (_forceOverwrite > 0 || isBufferMonotone())
if (_forceOverwrite > 0)
{
int oldestReadingIdx = (_currentReadingIdx + 1) % _windowSize;
_window[oldestReadingIdx] = reading;
Expand All @@ -76,6 +90,7 @@ void ScaleStabilizer::add(double reading)
int oldestReadingIdx = (_currentReadingIdx + 1) % _windowSize;
_window[oldestReadingIdx] = reading;
_currentReadingIdx = oldestReadingIdx;
_abortCounter = _abortCounter < _windowSize / 2 ? _abortCounter++ : _abortCounter;
return;
}

Expand All @@ -84,68 +99,50 @@ void ScaleStabilizer::add(double reading)

if (abs(_window[nextToLastReadingIdx] - getAvg()) > _weightThreshold)
{
// replace 75% of the data in the filter _window with fresh readings
_forceOverwrite = _windowSize * 0.75;
// replace all the data in the filter _window with fresh readings for the next six cycles
_forceOverwrite = _windowSize * 6;
return;
}

// everything else is probably just noise
// everything else is probably just noise but abort if stuck
if (_abortCounter <= 0)
{
_forceOverwrite = _windowSize;
_abortCounter = _windowSize / 2;
}
_abortCounter--;
}

/*!
* @brief returns the stabilized average reading from the moving _window
*/
double ScaleStabilizer::getStablilizedReading(double displayResolution, int decimalPlaces)
{
// find min and max values
int minIdx = 0;
int maxIdx = 0;
for (int i = 0; i < _windowSize; i++)
{
if (_window[i] < minIdx)
{
minIdx = i;
}
if (_window[i] > maxIdx)
{
maxIdx = i;
}
}

// remove min and max from measurements
double sum = 0;
for (int i = 0; i < _windowSize; i++)
{
if (i == minIdx || i == maxIdx)
{
continue;
}
sum += _window[i];
}

// calc cleaned average
double cleanedAverage = sum / (_windowSize - 2);
double codeValue = getAvg();
double outputValue = codeValue;

// remove negative numbers and set to 0 if close to 0
if (cleanedAverage < 0.5)
if (outputValue < 0.5)
{
cleanedAverage = 0.0;
outputValue = 0.0;
}

// round to given decimal places
float scale = pow(10, decimalPlaces);
cleanedAverage = round(cleanedAverage * scale) / scale;
outputValue = round(outputValue * scale) / scale;

// perform the output flicker reduction
if (_lastOutputValue == cleanedAverage)
if (_lastOutputValue == outputValue)
{
return cleanedAverage;
_lastCodeValue = codeValue;
return _lastOutputValue;
}

if (abs(cleanedAverage - _lastOutputValue) < displayResolution)
if (abs(codeValue - _lastCodeValue) > displayResolution)
{
return _lastOutputValue; // probably was just noise - return the old measurement
_lastCodeValue = codeValue;
_lastOutputValue = outputValue;
}

_lastOutputValue = cleanedAverage;
return cleanedAverage;
return _lastOutputValue;
}
12 changes: 4 additions & 8 deletions src/ScaleStabilizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,15 @@ class ScaleStabilizer
int _forceOverwrite;
int _currentReadingIdx;
double _lastOutputValue;
double _lastCodeValue;
int _abortCounter;

/*!
* @brief Returns the average
* @return Returns the average of the moving window
* @brief Returns the M-2 average
* @return Returns the M-2 average of the moving window
*/
double getAvg();

/*!
* @brief Returns true if moving window contains monotone elements
* @return True if all elements have the same value
*/
bool isBufferMonotone();

public:
/*!
* @brief Construct a new Scale Stabilizer object
Expand Down

0 comments on commit c6025ca

Please sign in to comment.