Skip to content

Commit c6025ca

Browse files
author
GatCode
committed
Improve reading stability and implement an abort option
1 parent be30e54 commit c6025ca

File tree

7 files changed

+68
-71
lines changed

7 files changed

+68
-71
lines changed

README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ Since the usage of load cells for accurate weight measurement is always correlat
1515

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

18+
Below you can see an example how the ScaleStabilizer can improve the reading stability in a real-world project.
19+
20+
![](assets/ScaleStabilizer.gif)
21+
1822
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.
1923

2024
## Basic principle
@@ -41,8 +45,8 @@ const int SCK_PIN = 22;
4145
HX711 scale;
4246

4347
// Scale Stabilizer
44-
const int WINDOW_SIZE = 10;
45-
const double WEIGHT_THRESHOLD = 0.01;
48+
const int WINDOW_SIZE = 12;
49+
const double WEIGHT_THRESHOLD = 0.05;
4650
ScaleStabilizer stabilizer;
4751

4852
void setup() {

assets/ScaleStabilizer.gif

2.09 MB
Loading

examples/basic/basic.ino

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ const int SCK_PIN = 22;
77
HX711 scale;
88

99
// Scale Stabilizer
10-
const int WINDOW_SIZE = 10;
11-
const double WEIGHT_THRESHOLD = 0.01;
10+
const int WINDOW_SIZE = 12;
11+
const double WEIGHT_THRESHOLD = 0.05;
1212
ScaleStabilizer stabilizer;
1313

1414
void setup() {
@@ -25,4 +25,4 @@ void loop() {
2525
double adcReading = scale.get_units();
2626
stabilizer.add(adcReading);
2727
Serial.println(stabilizer.getStablilizedReading());
28-
}
28+
}

library.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ScaleStabilizer",
3-
"version": "1.0.2",
3+
"version": "1.1.0",
44
"description": "A library to stabilize load cell readings",
55
"keywords": "scale, load cell, HC711",
66
"repository":

library.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name=ScaleStabilizer
2-
version=1.0.2
2+
version=1.1.0
33
author=GatCode <gatcode@wdw.one>
44
maintainer=GatCode <gatcode@wdw.one>
55
sentence=A library to stabilize load cell readings.

src/ScaleStabilizer.cpp

Lines changed: 53 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -14,33 +14,41 @@ ScaleStabilizer::~ScaleStabilizer()
1414
}
1515

1616
/*!
17-
* @brief simple average calculation
17+
* @brief M-2 average calculation
1818
*/
1919
double ScaleStabilizer::getAvg()
2020
{
21-
double sum = 0;
22-
21+
// find min and max values
22+
int minIdx = 0;
23+
double minReading = _window[minIdx];
24+
int maxIdx = 0;
25+
double maxReading = _window[maxIdx];
2326
for (int i = 0; i < _windowSize; i++)
2427
{
25-
sum += _window[i];
28+
if (_window[i] < minReading)
29+
{
30+
minIdx = i;
31+
minReading = _window[i];
32+
}
33+
if (_window[i] > maxReading)
34+
{
35+
maxIdx = i;
36+
maxReading = _window[i];
37+
}
2638
}
2739

28-
return sum / _windowSize;
29-
}
30-
31-
/*!
32-
* @brief checks if all the elements in the moving _window are the same
33-
*/
34-
bool ScaleStabilizer::isBufferMonotone()
35-
{
40+
// remove min and max from measurements
41+
double sum = 0;
3642
for (int i = 0; i < _windowSize; i++)
3743
{
38-
if (_window[i] != _window[0])
44+
if (i == minIdx || i == maxIdx)
3945
{
40-
return false;
46+
continue;
4147
}
48+
sum += _window[i];
4249
}
43-
return true;
50+
51+
return sum / (_windowSize - 2);
4452
}
4553

4654
/*!
@@ -54,6 +62,12 @@ void ScaleStabilizer::begin(int windowSize, double weightThreshold)
5462
_forceOverwrite = _windowSize;
5563
_currentReadingIdx = 0;
5664
_lastOutputValue = 0.0;
65+
_abortCounter = _windowSize / 2;
66+
67+
for (int i = 0; i < _windowSize; i++)
68+
{
69+
_window[i] = 0.0;
70+
}
5771
}
5872

5973
/*!
@@ -62,7 +76,7 @@ void ScaleStabilizer::begin(int windowSize, double weightThreshold)
6276
*/
6377
void ScaleStabilizer::add(double reading)
6478
{
65-
if (_forceOverwrite > 0 || isBufferMonotone())
79+
if (_forceOverwrite > 0)
6680
{
6781
int oldestReadingIdx = (_currentReadingIdx + 1) % _windowSize;
6882
_window[oldestReadingIdx] = reading;
@@ -76,6 +90,7 @@ void ScaleStabilizer::add(double reading)
7690
int oldestReadingIdx = (_currentReadingIdx + 1) % _windowSize;
7791
_window[oldestReadingIdx] = reading;
7892
_currentReadingIdx = oldestReadingIdx;
93+
_abortCounter = _abortCounter < _windowSize / 2 ? _abortCounter++ : _abortCounter;
7994
return;
8095
}
8196

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

85100
if (abs(_window[nextToLastReadingIdx] - getAvg()) > _weightThreshold)
86101
{
87-
// replace 75% of the data in the filter _window with fresh readings
88-
_forceOverwrite = _windowSize * 0.75;
102+
// replace all the data in the filter _window with fresh readings for the next six cycles
103+
_forceOverwrite = _windowSize * 6;
104+
return;
89105
}
90106

91-
// everything else is probably just noise
107+
// everything else is probably just noise but abort if stuck
108+
if (_abortCounter <= 0)
109+
{
110+
_forceOverwrite = _windowSize;
111+
_abortCounter = _windowSize / 2;
112+
}
113+
_abortCounter--;
92114
}
93115

94116
/*!
95117
* @brief returns the stabilized average reading from the moving _window
96118
*/
97119
double ScaleStabilizer::getStablilizedReading(double displayResolution, int decimalPlaces)
98120
{
99-
// find min and max values
100-
int minIdx = 0;
101-
int maxIdx = 0;
102-
for (int i = 0; i < _windowSize; i++)
103-
{
104-
if (_window[i] < minIdx)
105-
{
106-
minIdx = i;
107-
}
108-
if (_window[i] > maxIdx)
109-
{
110-
maxIdx = i;
111-
}
112-
}
113-
114-
// remove min and max from measurements
115-
double sum = 0;
116-
for (int i = 0; i < _windowSize; i++)
117-
{
118-
if (i == minIdx || i == maxIdx)
119-
{
120-
continue;
121-
}
122-
sum += _window[i];
123-
}
124-
125-
// calc cleaned average
126-
double cleanedAverage = sum / (_windowSize - 2);
121+
double codeValue = getAvg();
122+
double outputValue = codeValue;
127123

128124
// remove negative numbers and set to 0 if close to 0
129-
if (cleanedAverage < 0.5)
125+
if (outputValue < 0.5)
130126
{
131-
cleanedAverage = 0.0;
127+
outputValue = 0.0;
132128
}
133129

134130
// round to given decimal places
135131
float scale = pow(10, decimalPlaces);
136-
cleanedAverage = round(cleanedAverage * scale) / scale;
132+
outputValue = round(outputValue * scale) / scale;
137133

138134
// perform the output flicker reduction
139-
if (_lastOutputValue == cleanedAverage)
135+
if (_lastOutputValue == outputValue)
140136
{
141-
return cleanedAverage;
137+
_lastCodeValue = codeValue;
138+
return _lastOutputValue;
142139
}
143140

144-
if (abs(cleanedAverage - _lastOutputValue) < displayResolution)
141+
if (abs(codeValue - _lastCodeValue) > displayResolution)
145142
{
146-
return _lastOutputValue; // probably was just noise - return the old measurement
143+
_lastCodeValue = codeValue;
144+
_lastOutputValue = outputValue;
147145
}
148146

149-
_lastOutputValue = cleanedAverage;
150-
return cleanedAverage;
147+
return _lastOutputValue;
151148
}

src/ScaleStabilizer.h

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,15 @@ class ScaleStabilizer
1212
int _forceOverwrite;
1313
int _currentReadingIdx;
1414
double _lastOutputValue;
15+
double _lastCodeValue;
16+
int _abortCounter;
1517

1618
/*!
17-
* @brief Returns the average
18-
* @return Returns the average of the moving window
19+
* @brief Returns the M-2 average
20+
* @return Returns the M-2 average of the moving window
1921
*/
2022
double getAvg();
2123

22-
/*!
23-
* @brief Returns true if moving window contains monotone elements
24-
* @return True if all elements have the same value
25-
*/
26-
bool isBufferMonotone();
27-
2824
public:
2925
/*!
3026
* @brief Construct a new Scale Stabilizer object

0 commit comments

Comments
 (0)