├── .arduino-ci.yml ├── .github ├── FUNDING.yml └── workflows │ ├── arduino-lint.yml │ ├── arduino_test_runner.yml │ └── jsoncheck.yml ├── CHANGELOG..md ├── HX711_MP.cpp ├── HX711_MP.h ├── LICENSE ├── README.md ├── examples ├── HX_MP_calibrate │ └── HX_MP_calibrate.ino ├── HX_MP_performance │ └── HX_MP_performance.ino ├── HX_MP_performance2 │ └── HX_MP_performance2.ino ├── HX_MP_plotter │ └── HX_MP_plotter.ino └── HX_MP_runtime_calibration │ └── HX_MP_runtime_calibration.ino ├── keywords.txt ├── library.json ├── library.properties └── test └── unit_test_001.cpp /.arduino-ci.yml: -------------------------------------------------------------------------------- 1 | platforms: 2 | rpipico: 3 | board: rp2040:rp2040:rpipico 4 | package: rp2040:rp2040 5 | gcc: 6 | features: 7 | defines: 8 | - ARDUINO_ARCH_RP2040 9 | warnings: 10 | flags: 11 | 12 | packages: 13 | rp2040:rp2040: 14 | url: https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json 15 | 16 | compile: 17 | # Choosing to run compilation tests on 2 different Arduino platforms 18 | platforms: 19 | - uno 20 | # - due 21 | # - zero 22 | # - leonardo 23 | - m4 24 | - esp32 25 | # - esp8266 26 | # - mega2560 27 | - rpipico 28 | 29 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: RobTillaart 4 | custom: "https://www.paypal.me/robtillaart" 5 | -------------------------------------------------------------------------------- /.github/workflows/arduino-lint.yml: -------------------------------------------------------------------------------- 1 | name: Arduino-lint 2 | 3 | on: [push, pull_request] 4 | jobs: 5 | lint: 6 | runs-on: ubuntu-latest 7 | timeout-minutes: 5 8 | steps: 9 | - uses: actions/checkout@v4 10 | - uses: arduino/arduino-lint-action@v1 11 | with: 12 | library-manager: update 13 | compliance: strict -------------------------------------------------------------------------------- /.github/workflows/arduino_test_runner.yml: -------------------------------------------------------------------------------- 1 | name: Arduino CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | runTest: 7 | runs-on: ubuntu-latest 8 | timeout-minutes: 20 9 | 10 | steps: 11 | - uses: actions/checkout@v4 12 | - uses: ruby/setup-ruby@v1 13 | with: 14 | ruby-version: 2.6 15 | - run: | 16 | gem install arduino_ci 17 | arduino_ci.rb 18 | -------------------------------------------------------------------------------- /.github/workflows/jsoncheck.yml: -------------------------------------------------------------------------------- 1 | name: JSON check 2 | 3 | on: 4 | push: 5 | paths: 6 | - '**.json' 7 | pull_request: 8 | 9 | jobs: 10 | test: 11 | runs-on: ubuntu-latest 12 | timeout-minutes: 5 13 | steps: 14 | - uses: actions/checkout@v4 15 | - name: json-syntax-check 16 | uses: limitusus/json-syntax-check@v2 17 | with: 18 | pattern: "\\.json$" -------------------------------------------------------------------------------- /CHANGELOG..md: -------------------------------------------------------------------------------- 1 | # Change Log HX711_MP 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/). 6 | 7 | 8 | ## [0.3.1] - 2024-11-08 9 | - fix #7, sync with HX711 0.5.1 10 | - clean up examples a bit 11 | - minor edits 12 | 13 | ## [0.3.0] - 2024-06-17 14 | - fix #5, reset bug 15 | - refactor constructor 16 | - move code to .cpp file (to match HX711) 17 | 18 | ---- 19 | 20 | ## [0.2.0] - 2024-03-02 21 | - add fastProcessor option in **begin()** (Thanks to palmerr23) 22 | - updated license 23 | - updated GitHub/actions to v4 24 | 25 | ---- 26 | 27 | ## [0.1.1] - 2023-11-04 28 | - update readme.md 29 | - minor edits 30 | 31 | ## [0.1.0] - 2023-03-10 32 | - initial release 33 | - first version derived from 0.3.5 https://github.com/RobTillaart/HX711 34 | - minimized examples. 35 | - interface is different from HX711 library as calibration is not a scale and offset any more. 36 | - see discussion https://github.com/RobTillaart/HX711/issues/29 37 | - see readme.md 38 | 39 | 40 | -------------------------------------------------------------------------------- /HX711_MP.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: HX711_MP.cpp 3 | // AUTHOR: Rob Tillaart 4 | // VERSION: 0.3.1 5 | // PURPOSE: Library for load cells for UNO 6 | // URL: https://github.com/RobTillaart/HX711_MP 7 | // URL: https://github.com/RobTillaart/HX711 8 | 9 | 10 | #include "HX711_MP.h" 11 | 12 | 13 | HX711_MP::HX711_MP(uint8_t size) 14 | { 15 | _size = size; 16 | if (_size >= HX711_MP_MAX_SIZE) 17 | { 18 | _size = HX711_MP_MAX_SIZE; 19 | } 20 | else if (_size < 2) 21 | { 22 | _size = 2; // hard coded minimum!! 23 | } 24 | _gain = HX711_CHANNEL_A_GAIN_128; 25 | _lastRead = 0; 26 | _mode = HX711_AVERAGE_MODE; 27 | } 28 | 29 | 30 | HX711_MP::~HX711_MP() 31 | { 32 | } 33 | 34 | 35 | void HX711_MP::begin(uint8_t dataPin, uint8_t clockPin, bool fastProcessor ) 36 | { 37 | _dataPin = dataPin; 38 | _clockPin = clockPin; 39 | _fastProcessor = fastProcessor; 40 | 41 | pinMode(_dataPin, INPUT); 42 | pinMode(_clockPin, OUTPUT); 43 | digitalWrite(_clockPin, LOW); 44 | 45 | reset(); 46 | } 47 | 48 | 49 | void HX711_MP::reset() 50 | { 51 | power_down(); 52 | power_up(); 53 | _gain = HX711_CHANNEL_A_GAIN_128; 54 | _lastRead = 0; 55 | _mode = HX711_AVERAGE_MODE; 56 | } 57 | 58 | 59 | bool HX711_MP::is_ready() 60 | { 61 | return digitalRead(_dataPin) == LOW; 62 | } 63 | 64 | 65 | void HX711_MP::wait_ready(uint32_t ms) 66 | { 67 | while (!is_ready()) 68 | { 69 | delay(ms); 70 | } 71 | } 72 | 73 | 74 | bool HX711_MP::wait_ready_retry(uint8_t retries, uint32_t ms) 75 | { 76 | while (retries--) 77 | { 78 | if (is_ready()) return true; 79 | delay(ms); 80 | } 81 | return false; 82 | } 83 | 84 | 85 | bool HX711_MP::wait_ready_timeout(uint32_t timeout, uint32_t ms) 86 | { 87 | uint32_t start = millis(); 88 | while (millis() - start < timeout) 89 | { 90 | if (is_ready()) return true; 91 | delay(ms); 92 | } 93 | return false; 94 | } 95 | 96 | 97 | /////////////////////////////////////////////////////////////// 98 | // 99 | // READ 100 | // 101 | // From datasheet page 4 102 | // When output data is not ready for retrieval, 103 | // digital output pin DOUT is HIGH. 104 | // Serial clock input PD_SCK should be LOW. 105 | // When DOUT goes to LOW, it indicates data is ready for retrieval. 106 | float HX711_MP::read() 107 | { 108 | // this BLOCKING wait takes most time... 109 | while (digitalRead(_dataPin) == HIGH) yield(); 110 | 111 | union 112 | { 113 | int32_t value = 0; 114 | uint8_t data[4]; 115 | } v; 116 | 117 | // blocking part ... 118 | noInterrupts(); 119 | 120 | // Pulse the clock pin 24 times to read the data. 121 | // v.data[2] = shiftIn(_dataPin, _clockPin, MSBFIRST); 122 | // v.data[1] = shiftIn(_dataPin, _clockPin, MSBFIRST); 123 | // v.data[0] = shiftIn(_dataPin, _clockPin, MSBFIRST); 124 | v.data[2] = _shiftIn(); 125 | v.data[1] = _shiftIn(); 126 | v.data[0] = _shiftIn(); 127 | 128 | // TABLE 3 page 4 datasheet 129 | // 130 | // CLOCK CHANNEL GAIN m 131 | // ------------------------------------ 132 | // 25 A 128 1 // default 133 | // 26 B 32 2 134 | // 27 A 64 3 135 | // 136 | // only default 128 verified, 137 | // selection goes through the set_gain(gain) 138 | // 139 | uint8_t m = 1; 140 | if (_gain == HX711_CHANNEL_A_GAIN_128) m = 1; 141 | else if (_gain == HX711_CHANNEL_A_GAIN_64) m = 3; 142 | else if (_gain == HX711_CHANNEL_B_GAIN_32) m = 2; 143 | 144 | while (m > 0) 145 | { 146 | // delayMicroSeconds(1) needed for fast processors? 147 | digitalWrite(_clockPin, HIGH); 148 | if (_fastProcessor) 149 | delayMicroseconds(1); 150 | digitalWrite(_clockPin, LOW); 151 | if (_fastProcessor) 152 | delayMicroseconds(1); 153 | m--; 154 | } 155 | 156 | interrupts(); 157 | // yield(); 158 | 159 | // SIGN extend 160 | if (v.data[2] & 0x80) v.data[3] = 0xFF; 161 | 162 | _lastRead = millis(); 163 | return 1.0 * v.value; 164 | } 165 | 166 | 167 | float HX711_MP::read_average(uint8_t times) 168 | { 169 | if (times < 1) times = 1; 170 | float sum = 0; 171 | for (uint8_t i = 0; i < times; i++) 172 | { 173 | sum += read(); 174 | yield(); 175 | } 176 | return sum / times; 177 | } 178 | 179 | 180 | float HX711_MP::read_median(uint8_t times) 181 | { 182 | if (times > 15) times = 15; 183 | if (times < 3) times = 3; 184 | float samples[15]; 185 | for (uint8_t i = 0; i < times; i++) 186 | { 187 | samples[i] = read(); 188 | yield(); 189 | } 190 | _insertSort(samples, times); 191 | if (times & 0x01) return samples[times/2]; 192 | return (samples[times/2] + samples[times/2 + 1]) / 2; 193 | } 194 | 195 | 196 | float HX711_MP::read_medavg(uint8_t times) 197 | { 198 | if (times > 15) times = 15; 199 | if (times < 3) times = 3; 200 | float samples[15]; 201 | for (uint8_t i = 0; i < times; i++) 202 | { 203 | samples[i] = read(); 204 | yield(); 205 | } 206 | _insertSort(samples, times); 207 | float sum = 0; 208 | // iterate over 1/4 to 3/4 of the array 209 | uint8_t count = 0; 210 | uint8_t first = (times + 2) / 4; 211 | uint8_t last = times - first - 1; 212 | for (uint8_t i = first; i <= last; i++) // !! include last one too 213 | { 214 | sum += samples[i]; 215 | count++; 216 | } 217 | return sum / count; 218 | } 219 | 220 | 221 | float HX711_MP::read_runavg(uint8_t times, float alpha) 222 | { 223 | if (times < 1) times = 1; 224 | if (alpha < 0) alpha = 0; 225 | if (alpha > 1) alpha = 1; 226 | float val = read(); 227 | for (uint8_t i = 1; i < times; i++) 228 | { 229 | val += alpha * (read() - val); 230 | yield(); 231 | } 232 | return val; 233 | } 234 | 235 | 236 | /////////////////////////////////////////////////////// 237 | // 238 | // MODE 239 | // 240 | void HX711_MP::set_raw_mode() 241 | { 242 | _mode = HX711_RAW_MODE; 243 | } 244 | 245 | 246 | void HX711_MP::set_average_mode() 247 | { 248 | _mode = HX711_AVERAGE_MODE; 249 | } 250 | 251 | 252 | void HX711_MP::set_median_mode() 253 | { 254 | _mode = HX711_MEDIAN_MODE; 255 | } 256 | 257 | 258 | void HX711_MP::set_medavg_mode() 259 | { 260 | _mode = HX711_MEDAVG_MODE; 261 | } 262 | 263 | 264 | // set_run_avg will use a default alpha of 0.5. 265 | void HX711_MP::set_runavg_mode() 266 | { 267 | _mode = HX711_RUNAVG_MODE; 268 | } 269 | 270 | 271 | uint8_t HX711_MP::get_mode() 272 | { 273 | return _mode; 274 | } 275 | 276 | 277 | float HX711_MP::get_value(uint8_t times) 278 | { 279 | float raw; 280 | switch(_mode) 281 | { 282 | case HX711_RAW_MODE: 283 | raw = read(); 284 | break; 285 | case HX711_RUNAVG_MODE: 286 | raw = read_runavg(times); 287 | break; 288 | case HX711_MEDAVG_MODE: 289 | raw = read_medavg(times); 290 | break; 291 | case HX711_MEDIAN_MODE: 292 | raw = read_median(times); 293 | break; 294 | case HX711_AVERAGE_MODE: 295 | default: 296 | raw = read_average(times); 297 | break; 298 | } 299 | return raw; 300 | } 301 | 302 | 303 | float HX711_MP::get_units(uint8_t times) 304 | { 305 | return _multiMap(get_value(times)); 306 | } 307 | 308 | 309 | /////////////////////////////////////////////////////////////// 310 | // 311 | // GAIN 312 | // 313 | // note: if parameter gain == 0xFF40 some compilers 314 | // will map that to 0x40 == HX711_CHANNEL_A_GAIN_64; 315 | // solution: use uint32_t or larger parameters everywhere. 316 | // note that changing gain/channel may take up to 400 ms (page 3) 317 | bool HX711_MP::set_gain(uint8_t gain, bool forced) 318 | { 319 | if ( (not forced) && (_gain == gain)) return true; 320 | switch(gain) 321 | { 322 | case HX711_CHANNEL_B_GAIN_32: 323 | case HX711_CHANNEL_A_GAIN_64: 324 | case HX711_CHANNEL_A_GAIN_128: 325 | _gain = gain; 326 | read(); // next user read() is from right channel / gain 327 | return true; 328 | } 329 | return false; // unchanged, but incorrect value. 330 | } 331 | 332 | 333 | uint8_t HX711_MP::get_gain() 334 | { 335 | return _gain; 336 | } 337 | 338 | 339 | /////////////////////////////////////////////////////////////// 340 | // 341 | // CALIBRATION 342 | // 343 | bool HX711_MP::setCalibrate(uint8_t index, float raw, float weight) 344 | { 345 | if (index >= _size) return false; 346 | _in[index] = raw; 347 | _out[index] = weight; 348 | return true; 349 | } 350 | 351 | 352 | uint8_t HX711_MP::getCalibrateSize() 353 | { 354 | return _size; 355 | } 356 | 357 | 358 | float HX711_MP::getCalibrateRaw(uint8_t index) 359 | { 360 | if (index >= _size) return 0; // NaN 361 | return _in[index]; 362 | } 363 | 364 | 365 | float HX711_MP::adjustCalibrateRaw(uint8_t index, float amount) 366 | { 367 | if (index >= _size) return 0; // NaN 368 | _in[index] += amount; 369 | return _in[index]; 370 | } 371 | 372 | 373 | float HX711_MP::getCalibrateWeight(uint8_t index) 374 | { 375 | if (index >= _size) return 0; // NaN 376 | return _out[index]; 377 | } 378 | 379 | 380 | /////////////////////////////////////////////////////////////// 381 | // 382 | // POWER MANAGEMENT 383 | // 384 | void HX711_MP::power_down() 385 | { 386 | // at least 60 us HIGH 387 | digitalWrite(_clockPin, HIGH); 388 | delayMicroseconds(64); 389 | } 390 | 391 | 392 | void HX711_MP::power_up() 393 | { 394 | digitalWrite(_clockPin, LOW); 395 | } 396 | 397 | 398 | /////////////////////////////////////////////////////////////// 399 | // 400 | // MISC 401 | // 402 | uint32_t HX711_MP::last_read() 403 | { 404 | return _lastRead; 405 | } 406 | 407 | 408 | /////////////////////////////////////////////////////////////// 409 | // 410 | // PRIVATE 411 | // 412 | void HX711_MP::_insertSort(float * array, uint8_t size) 413 | { 414 | uint8_t t, z; 415 | float temp; 416 | for (t = 1; t < size; t++) 417 | { 418 | z = t; 419 | temp = array[z]; 420 | while( (z > 0) && (temp < array[z - 1] )) 421 | { 422 | array[z] = array[z - 1]; 423 | z--; 424 | } 425 | array[z] = temp; 426 | yield(); 427 | } 428 | } 429 | 430 | 431 | // MSB_FIRST optimized shiftIn 432 | // see datasheet page 5 for timing 433 | uint8_t HX711_MP::_shiftIn() 434 | { 435 | // local variables are faster. 436 | uint8_t clk = _clockPin; 437 | uint8_t data = _dataPin; 438 | uint8_t value = 0; 439 | uint8_t mask = 0x80; 440 | while (mask > 0) 441 | { 442 | digitalWrite(clk, HIGH); 443 | if(_fastProcessor) // T2 >= 0.2 us 444 | delayMicroseconds(1); 445 | if (digitalRead(data) == HIGH) 446 | { 447 | value |= mask; 448 | } 449 | digitalWrite(clk, LOW); 450 | if(_fastProcessor) 451 | delayMicroseconds(1); // keep duty cycle ~50% 452 | mask >>= 1; 453 | } 454 | return value; 455 | } 456 | 457 | 458 | float HX711_MP::_multiMap(float val) 459 | { 460 | // take care the value is within range 461 | // val = constrain(val, _in[0], _in[_size-1]); 462 | if (val <= _in[0]) return _out[0]; 463 | if (val >= _in[_size-1]) return _out[_size-1]; 464 | 465 | // search right interval 466 | uint8_t pos = 1; // _in[0] already tested 467 | while(val > _in[pos]) pos++; 468 | 469 | // this will handle all exact "points" in the _in array 470 | if (val == _in[pos]) return _out[pos]; 471 | 472 | // interpolate in the right segment for the rest 473 | return (val - _in[pos-1]) * (_out[pos] - _out[pos-1]) / (_in[pos] - _in[pos-1]) + _out[pos-1]; 474 | } 475 | 476 | 477 | 478 | // -- END OF FILE -- 479 | 480 | -------------------------------------------------------------------------------- /HX711_MP.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | // 3 | // FILE: HX711_MP.h 4 | // AUTHOR: Rob Tillaart 5 | // VERSION: 0.3.1 6 | // PURPOSE: Library for load cells for Arduino 7 | // URL: https://github.com/RobTillaart/HX711_MP 8 | // URL: https://github.com/RobTillaart/HX711 9 | // 10 | // NOTES 11 | // Superset of interface of HX711 class of Bogde 12 | // uses float instead of long as float has 23 bits mantissa 13 | // which almost perfectly matches the 24 bit ADC. 14 | 15 | 16 | #include "Arduino.h" 17 | 18 | #define HX711_MP_LIB_VERSION (F("0.3.1")) 19 | 20 | 21 | const uint8_t HX711_AVERAGE_MODE = 0x00; 22 | // in median mode only between 3 and 15 samples are allowed. 23 | const uint8_t HX711_MEDIAN_MODE = 0x01; 24 | // medavg = average of the middle "half" of sorted elements 25 | // in medavg mode only between 3 and 15 samples are allowed. 26 | const uint8_t HX711_MEDAVG_MODE = 0x02; 27 | // runavg = running average 28 | const uint8_t HX711_RUNAVG_MODE = 0x03; 29 | // causes read() to be called only once! 30 | const uint8_t HX711_RAW_MODE = 0x04; 31 | 32 | 33 | // supported values for set_gain() 34 | const uint8_t HX711_CHANNEL_A_GAIN_128 = 128; // default 35 | const uint8_t HX711_CHANNEL_A_GAIN_64 = 64; 36 | const uint8_t HX711_CHANNEL_B_GAIN_32 = 32; 37 | 38 | // maximum size for internal mapping array 39 | const uint8_t HX711_MP_MAX_SIZE = 10; 40 | 41 | 42 | class HX711_MP 43 | { 44 | public: 45 | HX711_MP(uint8_t size); 46 | ~HX711_MP(); 47 | 48 | // fixed gain 128 for now 49 | void begin(uint8_t dataPin, uint8_t clockPin, bool fastProcessor = false); 50 | 51 | void reset(); 52 | 53 | // checks if load cell is ready to read. 54 | bool is_ready(); 55 | 56 | // wait until ready, 57 | // check every ms 58 | void wait_ready(uint32_t ms = 0); 59 | // max # retries 60 | bool wait_ready_retry(uint8_t retries = 3, uint32_t ms = 0); 61 | // max timeout 62 | bool wait_ready_timeout(uint32_t timeout = 1000, uint32_t ms = 0); 63 | 64 | 65 | /////////////////////////////////////////////////////////////// 66 | // 67 | // READ 68 | // 69 | // raw read 70 | float read(); 71 | 72 | // get average of multiple raw reads 73 | // times = 1 or more 74 | float read_average(uint8_t times = 10); 75 | 76 | // get median of multiple raw reads 77 | // times = 3..15 - odd numbers preferred 78 | float read_median(uint8_t times = 7); 79 | 80 | // get average of "middle half" of multiple raw reads. 81 | // times = 3..15 - odd numbers preferred 82 | float read_medavg(uint8_t times = 7); 83 | 84 | // get running average over times measurements. 85 | // the weight alpha can be set to any value between 0 and 1 86 | // times = 1 or more. 87 | float read_runavg(uint8_t times = 7, float alpha = 0.5); 88 | 89 | 90 | /////////////////////////////////////////////////////////////// 91 | // 92 | // MODE 93 | // 94 | // get set mode for get_value() and indirect get_units(). 95 | // in median and medavg mode only 3..15 samples are allowed. 96 | void set_raw_mode(); 97 | void set_average_mode(); 98 | void set_median_mode(); 99 | void set_medavg_mode(); 100 | // set_run_avg will use a default alpha of 0.5. 101 | void set_runavg_mode(); 102 | uint8_t get_mode(); 103 | 104 | 105 | // primary user functions 106 | // in HX711_RAW_MODE the parameter times will be ignored. 107 | float get_value(uint8_t times = 1); 108 | // converted to proper units. 109 | // in HX711_RAW_MODE the parameter times will be ignored. 110 | float get_units(uint8_t times = 1); 111 | 112 | 113 | /////////////////////////////////////////////////////////////// 114 | // 115 | // GAIN 116 | // 117 | // CORE "CONSTANTS" -> read datasheet 118 | // CHANNEL GAIN notes 119 | // ------------------------------------- 120 | // A 128 default, tested 121 | // A 64 122 | // B 32 123 | 124 | // returns true ==> parameter gain is valid 125 | // returns false ==> parameter gain is invalid ==> no change. 126 | // note that changing gain/channel takes up to 400 ms (page 3) 127 | // if forced == true, the gain will be forced set 128 | // even it is already the right value 129 | bool set_gain(uint8_t gain = HX711_CHANNEL_A_GAIN_128, bool forced = false); 130 | uint8_t get_gain(); 131 | 132 | 133 | /////////////////////////////////////////////////////////////// 134 | // 135 | // CALIBRATION 136 | // 137 | // clear the scale 138 | // call tare() to set the zero offset (index 0) 139 | // 140 | // put a series of SIZE known weights on the scale in increasing order 141 | // thereby skipping 0 (as that is the tare) 142 | // call calibrate_scale(index, raw, weight) 143 | // index = 0..size -1 144 | // raw = raw read = getValue() made in any mode 145 | // weight = weight in units 146 | // setCalibrate() can be adjusted runtime. 147 | bool setCalibrate(uint8_t index, float raw, float weight); 148 | uint8_t getCalibrateSize(); 149 | float getCalibrateRaw(uint8_t index); 150 | float adjustCalibrateRaw(uint8_t index, float amount); 151 | float getCalibrateWeight(uint8_t index); 152 | 153 | 154 | /////////////////////////////////////////////////////////////// 155 | // 156 | // POWER MANAGEMENT 157 | // 158 | void power_down(); 159 | void power_up(); 160 | 161 | 162 | // TIME OF LAST READ 163 | uint32_t last_read(); 164 | 165 | 166 | /////////////////////////////////////////////////////////////// 167 | // 168 | // DEBUG 169 | // 170 | float testCalibration(float raw) 171 | { 172 | return _multiMap(raw); 173 | } 174 | 175 | 176 | private: 177 | uint8_t _dataPin; 178 | uint8_t _clockPin; 179 | 180 | uint8_t _gain; 181 | uint32_t _lastRead; 182 | uint8_t _mode; 183 | bool _fastProcessor; 184 | 185 | void _insertSort(float * array, uint8_t size); 186 | uint8_t _shiftIn(); 187 | 188 | // calibration arrays. 189 | float _in[HX711_MP_MAX_SIZE]; 190 | float _out[HX711_MP_MAX_SIZE]; 191 | uint8_t _size = HX711_MP_MAX_SIZE; 192 | float _multiMap(float val); 193 | }; 194 | 195 | 196 | // -- END OF FILE -- 197 | 198 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2024 Rob Tillaart 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [![Arduino CI](https://github.com/RobTillaart/HX711_MP/workflows/Arduino%20CI/badge.svg)](https://github.com/marketplace/actions/arduino_ci) 3 | [![Arduino-lint](https://github.com/RobTillaart/HX711_MP/actions/workflows/arduino-lint.yml/badge.svg)](https://github.com/RobTillaart/HX711_MP/actions/workflows/arduino-lint.yml) 4 | [![JSON check](https://github.com/RobTillaart/HX711_MP/actions/workflows/jsoncheck.yml/badge.svg)](https://github.com/RobTillaart/HX711_MP/actions/workflows/jsoncheck.yml) 5 | [![GitHub issues](https://img.shields.io/github/issues/RobTillaart/HX711_MP.svg)](https://github.com/RobTillaart/HX711_MP/issues) 6 | 7 | [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/RobTillaart/HX711_MP/blob/master/LICENSE) 8 | [![GitHub release](https://img.shields.io/github/release/RobTillaart/HX711_MP.svg?maxAge=3600)](https://github.com/RobTillaart/HX711_MP/releases) 9 | [![PlatformIO Registry](https://badges.registry.platformio.org/packages/robtillaart/library/HX711_MP.svg)](https://registry.platformio.org/libraries/robtillaart/HX711_MP) 10 | 11 | 12 | # HX711_MP 13 | 14 | Arduino library for HX711 24 bit ADC used for load cells. 15 | Has a multipoint calibration (MP) to compensate for non-linear sensor readings. 16 | 17 | 18 | ## Description 19 | 20 | This https://github.com/RobTillaart/HX711_MP library is derived from https://github.com/RobTillaart/HX711 21 | version 0.3.5. Although related and partly similar interface there are not compatible. 22 | 23 | Currently HX711_MP is experimental and need to be tested more. 24 | Good news is that it is based upon tested code, so no big problems are expected. 25 | 26 | The original HX711 library uses a linear relation between raw measurements and weights. 27 | This improved library uses multi point calibration - up to 10 points for now. 28 | This allows to compensate for non-linearities in the readings of the sensor. 29 | Between these calibration points interpolation is still linear. 30 | 31 | **WARNING: An important difference is that HX711_MP (version now) does not 32 | interpolate beyond the calibration data points.** 33 | 34 | If problems occur or there are questions, please open an issue at GitHub. 35 | 36 | 37 | 38 | ### 10 or 80 SPS 39 | 40 | The datasheet mentions that the HX711 can run at 80 samples per second SPS. 41 | To select this mode connect the **RATE** pin(15) of the chip to VCC (HIGH). 42 | Connecting **RATE** to GND (LOW) gives 10 SPS. 43 | 44 | All breakout boards I tested have **RATE** connected to GND and offer no 45 | pin to control this from the outside. 46 | 47 | This library does not provide means to control the **RATE** yet. 48 | If there is a need (issue) I will implement this in the library. 49 | For now one can add an IOpin for this and use **digitalWrite()**. 50 | 51 | 52 | ### Related 53 | 54 | - https://github.com/bogde/HX711 55 | - https://github.com/RobTillaart/weight (conversions kg <> stone etc.) 56 | - https://github.com/RobTillaart/HX711 57 | - https://github.com/RobTillaart/HX711_MP multipoint calibration version. 58 | 59 | 60 | ### Faulty boards 61 | 62 | - https://forum.arduino.cc/t/load-cell-amplifier-hx711-wrong-ground/1046075 63 | 64 | 65 | #### Differences HX711 66 | 67 | Although the library is derived from the HX711 library they are not compatible. 68 | Almost is the right word here. 69 | 70 | Due to the different way of calibration the default **tare()** function is not 71 | supported any more. 72 | This function used to calculate the offset in the raw data to get the zero point. 73 | As in the multi point calibration there are up to 10 points that can indicate 74 | the zero point the whole concept of offset and scale has "left the building". 75 | 76 | In practice the return value of **get_value()**, **read()** functions et al differs 77 | that original raw-zero offset from the HX711 library. 78 | This means the libraries are not 1 to 1 interchangeable, even a HX711_MP with only 79 | two points will behave slightly differently on this point. 80 | This is even more true as the zero point does not need to be the lowest possible value. 81 | Due to the support of non linear negative weights / forces the zero point can be at 82 | any point in the array. 83 | 84 | The performance is not tested yet, expect a slightly slower **get_units()** as there 85 | is more math involved for converting raw data to weights. 86 | 87 | 88 | ## Interface 89 | 90 | ```cpp 91 | #include "HX711_MP.h" 92 | ``` 93 | 94 | ### Constructor 95 | 96 | - **HX711_MP(uint8_t size)** constructor. 97 | Parameter sets the size of for the calibration arrays. 98 | Allowed range for size is 2..10. 99 | - **~HX711_MP()** 100 | - **void begin(uint8_t dataPin, uint8_t clockPin, bool fastProcessor)** sets a fixed gain 128 for now. 101 | The fastProcessor option adds a 1 uS delay for each clock half-cycle to keep the time greater than 200 nS. 102 | - **void reset()** set internal state to start condition. 103 | Reset also does a power down / up cycle. 104 | It does not reset the calibration data. 105 | 106 | 107 | ### Read 108 | 109 | - **bool is_ready()** checks if load cell is ready to read. 110 | - **void wait_ready(uint32_t ms = 0)** wait until ready, check every ms. 111 | - **bool wait_ready_retry(uint8_t retries = 3, uint32_t ms = 0)** wait max retries. 112 | - **bool wait_ready_timeout(uint32_t timeout = 1000, uint32_t ms = 0)** wait max timeout milliseconds. 113 | - **float read()** raw read. 114 | - **float read_average(uint8_t times = 10)** get average of times raw reads. times = 1 or more. 115 | - **float read_median(uint8_t times = 7)** get median of multiple raw reads. 116 | times = 3..15 - odd numbers preferred. 117 | - **float read_medavg(uint8_t times = 7)** get average of "middle half" of multiple raw reads. 118 | times = 3..15 - odd numbers preferred. 119 | - **float read_runavg(uint8_t times = 7, float alpha = 0.5)** get running average over times measurements. 120 | The weight alpha can be set to any value between 0 and 1, times >= 1. 121 | - **uint32_t last_read()** returns timestamp in milliseconds of last read. 122 | 123 | 124 | ### Gain + channel 125 | 126 | Use with care as it is not 100% reliable - see issue #27 (HX711 lib). (solutions welcome). 127 | 128 | Read datasheet before use. 129 | 130 | Constants (see .h file) 131 | 132 | - **HX711_CHANNEL_A_GAIN_128 = 128** This is the default in the constructor. 133 | - **HX711_CHANNEL_A_GAIN_64 = 64** 134 | - **HX711_CHANNEL_B_GAIN_32 = 32** Note fixed gain for channel B. 135 | 136 | The selection of channels + gain is in theory straightforward. 137 | 138 | - **bool set_gain(uint8_t gain = 128, bool forced = false)** values: 128 (default), 64 or 32. 139 | If one uses an invalid value for the parameter gain, the channel and gain are not changed. 140 | If forced == false it will not set the new gain if the library "thinks" it 141 | already has the right value. 142 | If forced == true, it will explicitly try to set the gain/channel again. 143 | This includes a dummy **read()** so the next "user" **read()** will give the right info. 144 | - **uint8_t get_gain()** returns set gain (128, 64 or 32). 145 | 146 | By setting the gain to one of the three constants the gain and the channel is selected. 147 | The **set_gain()** does a dummy read if gain has changed (or forced == true) so the 148 | next call to **read()** will return info from the selected channel/gain. 149 | 150 | According to the datasheet the gain/channel change may take up to 400ms (table page 3). 151 | 152 | Warning 1: if you use **set_gain()** in your program the HX711 can be in different states. 153 | If there is an expected or unexpected reboot of the MCU, this could lead 154 | to an unknown state at the reboot of the code. 155 | So in such case it is strongly advised to call **set_gain()** explicitly in **setup()** 156 | so the device is in a known state. 157 | 158 | Warning 2: In practice it seems harder to get the channel and gain selection as reliable 159 | as the datasheet states it should be. So use with care. (feedback welcome) 160 | See discussion #27 HX711. 161 | 162 | 163 | ### Read mode 164 | 165 | Get and set the operational mode for **get_value()** and indirect **get_units()**. 166 | 167 | Constants (see .h file) 168 | 169 | - **HX711_RAW_MODE** 170 | - **HX711_AVERAGE_MODE** 171 | - **HX711_MEDIAN_MODE** 172 | - **HX711_MEDAVG_MODE** 173 | - **HX711_RUNAVG_MODE** 174 | 175 | 176 | In **HX711_MEDIAN_MODE** and **HX711_MEDAVG_MODE** mode only 3..15 samples are allowed 177 | to keep memory footprint relative low. 178 | 179 | - **void set_raw_mode()** will cause **read()** to be called only once! 180 | - **void set_average_mode()** take the average of n measurements. 181 | - **void set_median_mode()** take the median of n measurements. 182 | - **void set_medavg_mode()** take the average of n/2 median measurements. 183 | - **void set_runavg_mode()** default alpha = 0.5. 184 | - **uint8_t get_mode()** returns current set mode. Default is **HX711_AVERAGE_MODE**. 185 | 186 | 187 | ### Get values 188 | 189 | Get values from the HX711. 190 | 191 | Note that in **HX711_RAW_MODE** the times parameter will be ignored => just call **read()** once. 192 | 193 | - **float get_value(uint8_t times = 1)** return raw value, optional averaged etc. 194 | - **float get_units(uint8_t times = 1)** return units, typical grams. 195 | 196 | 197 | ### Tare & calibration I 198 | 199 | The multipoint calibration array is based upon https://github.com/RobTillaart/multiMap. 200 | One can compensate for a non linear sensor by interpolating linear over multiple points. 201 | It does not use a smooth nth degree function to interpolate. 202 | 203 | The user has to measure a number of raw weights, typical including a zero weight. 204 | These numbers are set into the object with **setCalibrate(index, raw, weight)**. 205 | Note: Increasing indices must have an increasing raw number. 206 | 207 | Typical use is to hardcode earlier found values in the setup() phase. 208 | 209 | - **bool setCalibrate(uint8_t index, float raw, float weight)** maps a raw measurement 210 | to a certain weight. 211 | Note the index is zero based so a size of 10 uses index 0..9. 212 | - **float getCalibrateSize()** returns the size of the internal array, typical 2..10 213 | - **float getCalibrateRaw(uint8_t index)** get the raw value for the calibration point at index. 214 | Returns 0 is index is out of range. 215 | - **float adjustCalibrateRaw(uint8_t index, float amount)** changes the raw value at the array. 216 | Returns 0 is index is out of range. 217 | Used for run time calibration. 218 | - **float getCalibrateWeight(uint8_t index)** get the mapped weight for the calibration point at index. 219 | Returns 0 is index is out of range. 220 | 221 | This way of calibration allows: 222 | - to compensate for a non linear sensor by interpolating linear between two adjacent points. 223 | - to adjust runtime the values in the array, adjusting the mapping. 224 | - to use of negative weights (forces) as the 225 | 226 | 227 | ### Power management 228 | 229 | - **void power_down()** idem. Explicitly blocks for 64 microseconds. 230 | (See Page 5 datasheet). 231 | - **void power_up()** wakes up the HX711. 232 | It should reset the HX711 to defaults but this is not always seen. 233 | See discussion issue #27 GitHub. Needs more testing. 234 | 235 | 236 | ## Notes 237 | 238 | 239 | ### Connections HX711 240 | 241 | - A+/A- uses gain of 128 or 64 242 | - B+/B- uses gain of 32 243 | 244 | Colour scheme wires of two devices. 245 | 246 | | HX711 Pin | Colour dev 1 | Colour dev 2 | 247 | |:-----------:|:---------------:|:---------------:| 248 | | E+ | red | red | 249 | | E- | black | black | 250 | | A- | white | blue | 251 | | A+ | green | white | 252 | | B- | not connected | not connected | 253 | | B+ | not connected | not connected | 254 | 255 | 256 | ### Temperature 257 | 258 | Load cells do have a temperature related error. (see datasheet load cell) 259 | This can be reduced by doing the calibration and take the tare 260 | at the operational temperature one uses for the measurements. 261 | 262 | Another way to handle this is to add a good temperature sensor 263 | (e.g. DS18B20, SHT85) and compensate for the temperature 264 | differences in your code. 265 | 266 | 267 | ## Future 268 | 269 | Points from HX711 are not repeated here 270 | 271 | 272 | #### Must 273 | 274 | - keep in sync with HX711 library where relevant. 275 | - update documentation 276 | 277 | #### Should 278 | 279 | - test a lot 280 | - different load cells. 281 | - investigate interpolation beyond calibration range. 282 | - add examples 283 | - runtime changing of the mapping. 284 | - investigate malloc/free for the mapping arrays 285 | - add performance figures 286 | - Calibration 287 | - Returns 0 is index is out of range ==> NaN ? 288 | 289 | #### Could 290 | 291 | - add error handling? 292 | - HX711_INDEX_OUT_OF_RANGE 293 | - ?? 294 | - investigate temperature compensation. 295 | 296 | #### Wont 297 | 298 | 299 | ## Support 300 | 301 | If you appreciate my libraries, you can support the development and maintenance. 302 | Improve the quality of the libraries by providing issues and Pull Requests, or 303 | donate through PayPal or GitHub sponsors. 304 | 305 | Thank you, 306 | 307 | -------------------------------------------------------------------------------- /examples/HX_MP_calibrate/HX_MP_calibrate.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: HX_MP_calibrate.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: HX711_MP sketch to help calibration. 5 | // URL: https://github.com/RobTillaart/HX711_MP 6 | 7 | 8 | #include "HX711_MP.h" 9 | 10 | 11 | // 2 calibration points 12 | // but not needed as we only read raw data here 13 | HX711_MP scale(2); 14 | 15 | 16 | // adjust pins to your setup. 17 | uint8_t dataPin = 16; 18 | uint8_t clockPin = 17; 19 | 20 | 21 | float f; 22 | 23 | 24 | void setup() 25 | { 26 | Serial.begin(115200); 27 | Serial.println(__FILE__); 28 | Serial.print("HX711_MP_LIB_VERSION: "); 29 | Serial.println(HX711_MP_LIB_VERSION); 30 | Serial.println(); 31 | Serial.println("Get raw values for the calibration points."); 32 | Serial.println("Put weight on and wait for raw value to stabilize."); 33 | Serial.println("Write down the raw value and the used weight.."); 34 | Serial.println(); 35 | 36 | scale.begin(dataPin, clockPin); 37 | scale.set_average_mode(); 38 | } 39 | 40 | 41 | void loop() 42 | { 43 | f = scale.get_value(20); 44 | Serial.println(f); 45 | delay(1000); 46 | } 47 | 48 | 49 | // -- END OF FILE -- 50 | -------------------------------------------------------------------------------- /examples/HX_MP_performance/HX_MP_performance.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: HX_MP_performance.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: HX711_MP demo 5 | // URL: https://github.com/RobTillaart/HX711 6 | 7 | 8 | #include "HX711_MP.h" 9 | 10 | 11 | // 10 calibration points 12 | // all user defined. 13 | HX711_MP scale(10); 14 | 15 | // adjust pins to your setup. 16 | uint8_t dataPin = 16; 17 | uint8_t clockPin = 17; 18 | 19 | 20 | uint32_t start, stop; 21 | volatile float f; 22 | 23 | 24 | void setup() 25 | { 26 | Serial.begin(115200); 27 | Serial.println(__FILE__); 28 | Serial.print("HX711_MP_LIB_VERSION: "); 29 | Serial.println(HX711_MP_LIB_VERSION); 30 | Serial.println(); 31 | 32 | scale.begin(dataPin, clockPin); 33 | 34 | Serial.println("\nUNCALIBRATED"); 35 | delay(1000); 36 | 37 | measure(10); 38 | 39 | 40 | scale.begin(dataPin, clockPin); 41 | 42 | // Calibration 43 | // adjust the calibration data to your sensor 44 | // setCalibrate(index, rawRead, weight); 45 | scale.setCalibrate(0, 1000, -10000); 46 | scale.setCalibrate(1, 1300, 0); 47 | scale.setCalibrate(2, 2000, 20000); 48 | scale.setCalibrate(3, 4000, 30000); 49 | scale.setCalibrate(4, 5000, 40000); 50 | scale.setCalibrate(5, 5200, 50000); 51 | scale.setCalibrate(6, 6000, 60000); 52 | scale.setCalibrate(7, 6500, 70000); 53 | scale.setCalibrate(8, 6750, 80000); 54 | scale.setCalibrate(9, 6900, 90000); 55 | delay(10); 56 | 57 | Serial.println("\nCALIBRATED"); 58 | delay(1000); 59 | measure(10); 60 | 61 | Serial.println("\nSLEEP"); 62 | scale.power_down(); 63 | delay(2000); 64 | scale.power_up(); 65 | Serial.println("\nWAKE UP"); 66 | delay(1000); 67 | measure(10); 68 | 69 | // 70 | // PERFORMANCE 71 | // 100x get_units(1) = 9404352 (UNO) 72 | // VAL: 0.05 73 | // 74 | Serial.println("\nPERFORMANCE"); 75 | delay(10); 76 | start = micros(); 77 | f = 0; 78 | for (int i = 0; i < 100; i++) 79 | { 80 | f = scale.get_units(1); 81 | } 82 | stop = micros(); 83 | Serial.print("100x get_units(1) = "); 84 | Serial.println(stop - start); 85 | Serial.print(" VAL: "); 86 | Serial.println(f, 2); 87 | 88 | Serial.println("\nPRECISION"); 89 | f = 0; 90 | for (int i = 0; i < 100; i++) 91 | { 92 | f += scale.get_units(1); 93 | } 94 | Serial.print(" VAL:"); 95 | Serial.println(f * 0.01, 4); 96 | } 97 | 98 | 99 | void loop() 100 | { 101 | // continuous scale 4x per second 102 | // f = scale.get_units(5); 103 | // Serial.println(f); 104 | // delay(250); 105 | } 106 | 107 | 108 | void measure(uint8_t cnt) 109 | { 110 | Serial.print(" RAW: "); 111 | Serial.println(scale.read()); 112 | Serial.print(" AVG: "); 113 | Serial.println(scale.read_average(cnt)); 114 | Serial.print("VALUE: "); 115 | Serial.println(scale.get_value(cnt)); 116 | Serial.print("UNITS: "); 117 | Serial.println(scale.get_units(cnt), 1); 118 | } 119 | 120 | 121 | // -- END OF FILE -- 122 | -------------------------------------------------------------------------------- /examples/HX_MP_performance2/HX_MP_performance2.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: HX_MP_performance2.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: HX711_MP demo 5 | // URL: https://github.com/RobTillaart/HX711_MP 6 | 7 | 8 | #include "HX711_MP.h" 9 | 10 | 11 | // 10 calibration points 12 | // all user defined. 13 | HX711_MP scale(10); 14 | 15 | // adjust pins to your setup. 16 | uint8_t dataPin = 16; 17 | uint8_t clockPin = 17; 18 | 19 | 20 | uint32_t start, stop; 21 | volatile float f; 22 | 23 | 24 | void setup() 25 | { 26 | Serial.begin(115200); 27 | Serial.println(__FILE__); 28 | Serial.print("HX711_MP_LIB_VERSION: "); 29 | Serial.println(HX711_MP_LIB_VERSION); 30 | Serial.println(); 31 | 32 | scale.begin(dataPin, clockPin); 33 | 34 | // Calibration 35 | // adjust the calibration data to your sensor 36 | // setCalibrate(index, rawRead, weight); 37 | scale.setCalibrate(0, 1000, -10000); 38 | scale.setCalibrate(1, 1300, 0); 39 | scale.setCalibrate(2, 2000, 20000); 40 | scale.setCalibrate(3, 4000, 30000); 41 | scale.setCalibrate(4, 5000, 40000); 42 | scale.setCalibrate(5, 5200, 50000); 43 | scale.setCalibrate(6, 6000, 60000); 44 | scale.setCalibrate(7, 6500, 70000); 45 | scale.setCalibrate(8, 6750, 80000); 46 | scale.setCalibrate(9, 6900, 90000); 47 | delay(10); 48 | 49 | measure(); 50 | 51 | Serial.println("Done..."); 52 | } 53 | 54 | 55 | void loop() 56 | { 57 | } 58 | 59 | 60 | void measure() 61 | { 62 | Serial.println("Counting get_units() calls for 1 minute..."); 63 | delay(10); 64 | 65 | uint32_t count = 0; 66 | uint32_t start = millis(); 67 | while (millis() - start < 60000) 68 | { 69 | if (scale.is_ready()) 70 | { 71 | count++; 72 | scale.get_units(1); 73 | } 74 | } 75 | Serial.print("calls per minute: "); 76 | Serial.println(count); 77 | Serial.print("calls per second: "); 78 | Serial.println(count / 60.0); 79 | } 80 | 81 | 82 | // -- END OF FILE -- 83 | -------------------------------------------------------------------------------- /examples/HX_MP_plotter/HX_MP_plotter.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: HX_MP_plotter.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: HX711 demo 5 | // URL: https://github.com/RobTillaart/HX711_MP 6 | 7 | 8 | #include "HX711_MP.h" 9 | 10 | 11 | // 10 calibration points 12 | // all user defined. 13 | HX711_MP scale(10); 14 | 15 | 16 | // adjust pins to your setup. 17 | uint8_t dataPin = 16; 18 | uint8_t clockPin = 17; 19 | 20 | 21 | float f; 22 | 23 | 24 | void setup() 25 | { 26 | Serial.begin(115200); 27 | // Serial.println(__FILE__); 28 | // Serial.print("HX711_MP_LIB_VERSION: "); 29 | // Serial.println(HX711_MP_LIB_VERSION); 30 | // Serial.println(); 31 | 32 | scale.begin(dataPin, clockPin); 33 | 34 | // Calibration 35 | // adjust the data to your measurements 36 | // setCalibrate(index, rawRead, weight); 37 | scale.setCalibrate(0, 1000, -10000); 38 | scale.setCalibrate(1, 1300, 0); 39 | scale.setCalibrate(2, 2000, 20000); 40 | scale.setCalibrate(3, 4000, 30000); 41 | scale.setCalibrate(4, 5000, 40000); 42 | scale.setCalibrate(5, 5200, 50000); 43 | scale.setCalibrate(6, 6000, 60000); 44 | scale.setCalibrate(7, 6500, 70000); 45 | scale.setCalibrate(8, 6750, 80000); 46 | scale.setCalibrate(9, 6900, 90000); 47 | 48 | for (uint32_t raw = 0; raw <= 7000; raw += 20) 49 | { 50 | Serial.print(raw); 51 | Serial.print("\t"); 52 | Serial.println(scale.testCalibration(raw)); 53 | } 54 | delay(5000); 55 | } 56 | 57 | 58 | void loop() 59 | { 60 | // continuous scale 4x per second 61 | f = scale.get_units(5); 62 | Serial.println(f); 63 | delay(250); 64 | } 65 | 66 | 67 | // -- END OF FILE -- 68 | -------------------------------------------------------------------------------- /examples/HX_MP_runtime_calibration/HX_MP_runtime_calibration.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: HX_MP_runtime_calibration.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: HX711 demo 5 | // URL: https://github.com/RobTillaart/HX711_MP 6 | // 7 | // Note: this example is not fool proof, just to get the idea. 8 | 9 | #include "HX711_MP.h" 10 | 11 | 12 | // 3 calibration points 13 | // all user defined. 14 | HX711_MP scale(3); 15 | 16 | 17 | // adjust pins to your setup. 18 | uint8_t dataPin = 16; 19 | uint8_t clockPin = 17; 20 | uint8_t PIN_UP = 18; 21 | uint8_t PIN_DOWN = 19; 22 | 23 | 24 | void setup() 25 | { 26 | Serial.begin(115200); 27 | Serial.println(__FILE__); 28 | Serial.print("HX711_MP_LIB_VERSION: "); 29 | Serial.println(HX711_MP_LIB_VERSION); 30 | Serial.println(); 31 | 32 | scale.begin(dataPin, clockPin); 33 | 34 | // Calibration 35 | // adjust the data to your measurements 36 | // setCalibrate(index, rawRead, weight); 37 | scale.setCalibrate(0, 1000, 0); 38 | scale.setCalibrate(1, 1500, 250); 39 | scale.setCalibrate(2, 2000, 500); 40 | 41 | pinMode(PIN_UP, INPUT_PULLUP); 42 | pinMode(PIN_DOWN, INPUT_PULLUP); 43 | delay(5000); 44 | } 45 | 46 | 47 | void loop() 48 | { 49 | Serial.print("WEIGHT:\t"); 50 | Serial.println(scale.get_units(5)); 51 | 52 | // adjustment for index 1 (hard coded) 53 | if (digitalRead(PIN_UP) == LOW) 54 | { 55 | float raw = scale.adjustCalibrateRaw(1, +10); 56 | Serial.print("RAW:\t"); 57 | Serial.println(raw); 58 | } 59 | if (digitalRead(PIN_DOWN) == LOW) 60 | { 61 | float raw = scale.adjustCalibrateRaw(1, -10); 62 | Serial.print("RAW:\t"); 63 | Serial.println(raw); 64 | } 65 | 66 | delay(250); 67 | } 68 | 69 | 70 | // -- END OF FILE -- 71 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | # Syntax Colouring Map For HX711_MP 2 | 3 | # Data types (KEYWORD1) 4 | HX711_MP KEYWORD1 5 | 6 | 7 | # Methods and Functions (KEYWORD2) 8 | begin KEYWORD2 9 | reset KEYWORD2 10 | 11 | is_ready KEYWORD2 12 | wait_ready KEYWORD2 13 | wait_ready_retry KEYWORD2 14 | wait_ready_timeout KEYWORD2 15 | 16 | read KEYWORD2 17 | read_average KEYWORD2 18 | read_median KEYWORD2 19 | read_medavg KEYWORD2 20 | read_runavg KEYWORD2 21 | 22 | get_value KEYWORD2 23 | get_units KEYWORD2 24 | 25 | set_raw_mode KEYWORD2 26 | set_average_mode KEYWORD2 27 | set_median_mode KEYWORD2 28 | set_medavg_mode KEYWORD2 29 | set_runavg_mode KEYWORD2 30 | get_mode KEYWORD2 31 | 32 | set_gain KEYWORD2 33 | get_gain KEYWORD2 34 | 35 | setCalibrate KEYWORD2 36 | getCalibrateSize KEYWORD2 37 | getCalibrateRaw KEYWORD2 38 | adjustCalibrateRaw KEYWORD2 39 | getCalibrateWeight KEYWORD2 40 | 41 | power_down KEYWORD2 42 | power_up KEYWORD2 43 | last_read KEYWORD2 44 | 45 | 46 | # Instances (KEYWORD2) 47 | 48 | 49 | # Constants (LITERAL1) 50 | HX711_MP_LIB_VERSION LITERAL1 51 | 52 | HX711_RAW_MODE LITERAL1 53 | HX711_AVERAGE_MODE LITERAL1 54 | HX711_MEDIAN_MODE LITERAL1 55 | HX711_MEDAVG_MODE LITERAL1 56 | HX711_RUNAVG_MODE LITERAL1 57 | 58 | HX711_CHANNEL_A_GAIN_128 LITERAL1 59 | HX711_CHANNEL_A_GAIN_64 LITERAL1 60 | HX711_CHANNEL_B_GAIN_32 LITERAL1 61 | 62 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "HX711_MP", 3 | "keywords": "loadcell,weight,kilo,pound,gram,price,lbs,ounce,pound", 4 | "description": "Arduino library for HX711 load cell amplifier. Multipoint calibration version.", 5 | "authors": 6 | [ 7 | { 8 | "name": "Rob Tillaart", 9 | "email": "Rob.Tillaart@gmail.com", 10 | "maintainer": true 11 | } 12 | ], 13 | "repository": 14 | { 15 | "type": "git", 16 | "url": "https://github.com/RobTillaart/HX711_MP" 17 | }, 18 | "version": "0.3.1", 19 | "license": "MIT", 20 | "frameworks": "*", 21 | "platforms": "*", 22 | "headers": "HX711_MP.h" 23 | } 24 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=HX711_MP 2 | version=0.3.1 3 | author=Rob Tillaart 4 | maintainer=Rob Tillaart 5 | sentence=Arduino library for HX711 load cell amplifier. 6 | paragraph=Multipoint calibration version. 7 | category=Signal Input/Output 8 | url=https://github.com/RobTillaart/HX711_MP 9 | architectures=* 10 | includes=HX711_MP.h 11 | depends= 12 | -------------------------------------------------------------------------------- /test/unit_test_001.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: unit_test_001.cpp 3 | // AUTHOR: Rob Tillaart 4 | // DATE: 2020-12-28 5 | // PURPOSE: unit tests for the HX711 library (24 bit ADC for loadcells) 6 | // https://github.com/RobTillaart/HX711 7 | // https://github.com/Arduino-CI/arduino_ci/blob/master/REFERENCE.md 8 | // 9 | 10 | // supported assertions 11 | // https://github.com/Arduino-CI/arduino_ci/blob/master/cpp/unittest/Assertion.h#L33-L42 12 | // ---------------------------- 13 | // assertEqual(expected, actual) 14 | // assertNotEqual(expected, actual) 15 | // assertLess(expected, actual) 16 | // assertMore(expected, actual) 17 | // assertLessOrEqual(expected, actual) 18 | // assertMoreOrEqual(expected, actual) 19 | // assertTrue(actual) 20 | // assertFalse(actual) 21 | // assertNull(actual) 22 | // assertNotNull(actual) 23 | 24 | #include 25 | 26 | 27 | #include "Arduino.h" 28 | #include "HX711_MP.h" 29 | 30 | 31 | uint8_t dataPin = 6; 32 | uint8_t clockPin = 7; 33 | 34 | 35 | unittest_setup() 36 | { 37 | fprintf(stderr, "HX711_MP_LIB_VERSION: %s\n", (char *) HX711_MP_LIB_VERSION); 38 | } 39 | 40 | unittest_teardown() 41 | { 42 | } 43 | 44 | 45 | unittest(test_constants) 46 | { 47 | assertEqual(0x00, HX711_AVERAGE_MODE); 48 | assertEqual(0x01, HX711_MEDIAN_MODE); 49 | assertEqual(0x02, HX711_MEDAVG_MODE); 50 | assertEqual(0x03, HX711_RUNAVG_MODE); 51 | assertEqual(0x04, HX711_RAW_MODE); 52 | 53 | assertEqual(128, HX711_CHANNEL_A_GAIN_128); 54 | assertEqual(64, HX711_CHANNEL_A_GAIN_64); 55 | assertEqual(32, HX711_CHANNEL_B_GAIN_32); 56 | } 57 | 58 | 59 | unittest(test_constructor) 60 | { 61 | HX711_MP scale(5); 62 | scale.begin(dataPin, clockPin); 63 | 64 | // pins are default LOW apparently. 65 | assertTrue(scale.is_ready()); 66 | // default not read 67 | assertEqual(0, scale.last_read()); 68 | } 69 | 70 | 71 | unittest(test_gain) 72 | { 73 | HX711_MP scale(5); 74 | scale.begin(dataPin, clockPin); 75 | 76 | // rewrite with constants? 77 | // HX711_CHANNEL_A_GAIN_128 78 | // HX711_CHANNEL_A_GAIN_64 79 | // HX711_CHANNEL_B_GAIN_32 80 | 81 | // default 82 | assertEqual(128, scale.get_gain()); 83 | 84 | assertTrue(scale.set_gain(32)); 85 | assertEqual(32, scale.get_gain()); 86 | 87 | assertTrue(scale.set_gain()); 88 | assertEqual(128, scale.get_gain()); 89 | 90 | assertTrue(scale.set_gain(64)); 91 | assertEqual(64, scale.get_gain()); 92 | 93 | assertTrue(scale.set_gain(128)); 94 | assertEqual(128, scale.get_gain()); 95 | 96 | // failing invalid parameter 97 | assertFalse(scale.set_gain(100)); 98 | assertEqual(128, scale.get_gain()); 99 | 100 | // failing invalid parameter 101 | // 0x40 == 64 so it will fail to fail. 102 | // assertFalse(scale.set_gain(0xFF40)); 103 | // assertEqual(128, scale.get_gain()); 104 | } 105 | 106 | 107 | 108 | unittest(test_operational_mode) 109 | { 110 | HX711_MP scale(5); 111 | scale.begin(dataPin, clockPin); 112 | 113 | assertEqual(0x00, scale.get_mode()); 114 | scale.set_medavg_mode(); 115 | assertEqual(0x02, scale.get_mode()); 116 | scale.set_median_mode(); 117 | assertEqual(0x01, scale.get_mode()); 118 | scale.set_average_mode(); 119 | assertEqual(0x00, scale.get_mode()); 120 | } 121 | 122 | 123 | 124 | unittest(test_calibration) 125 | { 126 | HX711_MP scale(5); 127 | HX711_MP scale1(11); 128 | HX711_MP scale2(1); 129 | 130 | // check getCalibrateSize 131 | assertEqual(5, scale.getCalibrateSize()); 132 | assertEqual(10, scale1.getCalibrateSize()); 133 | assertEqual(2, scale2.getCalibrateSize()); 134 | 135 | // check setCalibrate() 136 | assertTrue(scale.setCalibrate(0, 1000, -10000)); 137 | assertTrue(scale.setCalibrate(1, 1300, 0)); 138 | assertTrue(scale.setCalibrate(2, 2000, 20000)); 139 | assertTrue(scale.setCalibrate(3, 4000, 30000)); 140 | assertTrue(scale.setCalibrate(4, 5000, 40000)); 141 | assertFalse(scale.setCalibrate(5, 6000, 50000)); 142 | 143 | // check getCalibrateRaw() 144 | assertEqual(1000, scale.getCalibrateRaw(0)); 145 | assertEqual(1300, scale.getCalibrateRaw(1)); 146 | assertEqual(2000, scale.getCalibrateRaw(2)); 147 | assertEqual(4000, scale.getCalibrateRaw(3)); 148 | assertEqual(5000, scale.getCalibrateRaw(4)); 149 | assertEqual(0, scale.getCalibrateRaw(5)); 150 | 151 | // check getCalibrateWeight 152 | assertEqual(-10000, scale.getCalibrateWeight(0)); 153 | assertEqual(000000, scale.getCalibrateWeight(1)); 154 | assertEqual( 20000, scale.getCalibrateWeight(2)); 155 | assertEqual( 30000, scale.getCalibrateWeight(3)); 156 | assertEqual( 40000, scale.getCalibrateWeight(4)); 157 | assertEqual(0, scale.getCalibrateWeight(5)); 158 | } 159 | 160 | 161 | unittest_main() 162 | 163 | 164 | // -- END OF FILE -- 165 | 166 | --------------------------------------------------------------------------------