├── .github ├── FUNDING.yml └── workflows │ ├── arduino-lint.yml │ ├── arduino_test_runner.yml │ └── jsoncheck.yml ├── examples ├── HX_performance2 │ ├── performance_0.2.3.txt │ ├── performance_0.3.0.txt │ ├── performance_0.3.6.txt │ └── HX_performance2.ino ├── HX_set_persistent │ ├── .arduino-ci.yml │ └── HX_set_persistent.ino ├── HX_performance │ ├── performance_0.3.1.txt │ ├── performance_0.3.2.txt │ ├── performance_0.3.4.txt │ └── HX_performance.ino ├── HX_get_noise_statistics │ ├── .arduino-ci.yml │ └── HX_get_noise_statistics.ino ├── HX_plotter_multimap │ ├── .arduino-ci.yml │ └── HX_plotter_multimap.ino ├── HX_is_ready │ └── HX_is_ready.ino ├── HX_plotter │ └── HX_plotter.ino ├── HX_loadcell_array │ └── HX_loadcell_array.ino ├── HX_read_median_average │ └── HX_read_median_average.ino ├── HX_read_median │ └── HX_read_median.ino ├── HX_delta_scale │ └── HX_delta_scale.ino ├── HX_morse_experimental │ └── HX_morse_experimental.ino ├── HX_grocery_scale │ └── HX_grocery_scale.ino ├── HX_kitchen_scale │ └── HX_kitchen_scale.ino ├── HX_calibration │ └── HX_calibration.ino └── HX_set_mode │ └── HX_set_mode.ino ├── library.properties ├── .arduino-ci.yml ├── library.json ├── LICENSE ├── keywords.txt ├── CHANGELOG..md ├── test └── unit_test_001.cpp ├── HX711.h ├── HX711.cpp └── README.md /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: RobTillaart 4 | custom: "https://www.paypal.me/robtillaart" 5 | -------------------------------------------------------------------------------- /examples/HX_performance2/performance_0.2.3.txt: -------------------------------------------------------------------------------- 1 | 2 | (dry run with no HW) 3 | 4 | LIBRARY VERSION: 0.2.3 5 | 6 | Counting get_units() calls for 1 minute... 7 | calls per minute: 149202 8 | calls per second: 2486.70 9 | -------------------------------------------------------------------------------- /examples/HX_performance2/performance_0.3.0.txt: -------------------------------------------------------------------------------- 1 | 2 | (dry run with no HW) 3 | 4 | LIBRARY VERSION: 0.3.0 5 | 6 | Counting get_units() calls for 1 minute... 7 | calls per minute: 149202 8 | calls per second: 2486.70 9 | -------------------------------------------------------------------------------- /examples/HX_set_persistent/.arduino-ci.yml: -------------------------------------------------------------------------------- 1 | compile: 2 | # Choosing to run compilation tests on 2 different Arduino platforms 3 | platforms: 4 | - uno 5 | # - due 6 | # - zero 7 | # - leonardo 8 | # - m4 9 | - esp32 10 | # - esp8266 11 | # - mega2560 12 | -------------------------------------------------------------------------------- /.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@v5 10 | - uses: arduino/arduino-lint-action@v2 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@v5 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 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=HX711 2 | version=0.6.3 3 | author=Rob Tillaart 4 | maintainer=Rob Tillaart 5 | sentence=Arduino library for HX711 load cell amplifier. 6 | paragraph=includes calibrate functions, reading median and median_average. 7 | category=Signal Input/Output 8 | url=https://github.com/RobTillaart/HX711 9 | architectures=* 10 | includes=HX711.h 11 | depends= 12 | -------------------------------------------------------------------------------- /examples/HX_performance2/performance_0.3.6.txt: -------------------------------------------------------------------------------- 1 | 2 | Run with no HW 3 | ---------------------- 4 | LIBRARY VERSION: 0.3.6 5 | 6 | Counting get_units() calls for 1 minute... 7 | calls per minute: 157918 8 | calls per second: 2631.97 9 | 10 | 11 | 12 | Run with HW 13 | ---------------------- 14 | LIBRARY VERSION: 0.3.6 15 | 16 | Counting get_units() calls for 1 minute... 17 | calls per minute: 636 18 | calls per second: 10.60 19 | -------------------------------------------------------------------------------- /.github/workflows/jsoncheck.yml: -------------------------------------------------------------------------------- 1 | name: JSON check 2 | 3 | on: 4 | push: 5 | paths: 6 | - '**.json' 7 | pull_request: 8 | paths: 9 | - '**.json' 10 | 11 | jobs: 12 | test: 13 | runs-on: ubuntu-latest 14 | timeout-minutes: 5 15 | steps: 16 | - uses: actions/checkout@v5 17 | - name: json-syntax-check 18 | uses: limitusus/json-syntax-check@v2 19 | with: 20 | pattern: "\\.json$" -------------------------------------------------------------------------------- /examples/HX_performance/performance_0.3.1.txt: -------------------------------------------------------------------------------- 1 | LIBRARY VERSION: 0.3.1 2 | 3 | 4 | UNCONFIGURED 5 | RAW: 0.00 6 | AVG: 0.00 7 | VALUE: 0.00 8 | UNITS: 0.0 9 | 10 | CONFIGURED 11 | RAW: 0.00 12 | AVG: 0.00 13 | VALUE: 0.00 14 | UNITS: 0.0 15 | 16 | SLEEP 17 | 18 | WAKE UP 19 | RAW: 0.00 20 | AVG: 0.00 21 | VALUE: 0.00 22 | UNITS: 0.0 23 | 24 | PERFORMANCE 25 | 100x get_units(1) = 41164 26 | VAL: 0.00 27 | 28 | PRECISSION 29 | VAL:0.0000 30 | -------------------------------------------------------------------------------- /examples/HX_performance/performance_0.3.2.txt: -------------------------------------------------------------------------------- 1 | (UNO) 2 | 3 | LIBRARY VERSION: 0.3.2 4 | 5 | 6 | UNCONFIGURED 7 | RAW: 0.00 8 | AVG: 0.00 9 | VALUE: 0.00 10 | UNITS: 0.0 11 | 12 | CONFIGURED 13 | RAW: 0.00 14 | AVG: 0.00 15 | VALUE: 0.00 16 | UNITS: 0.0 17 | 18 | SLEEP 19 | 20 | WAKE UP 21 | RAW: 0.00 22 | AVG: 0.00 23 | VALUE: 0.00 24 | UNITS: 0.0 25 | 26 | PERFORMANCE 27 | 100x get_units(1) = 40944 28 | VAL: 0.00 29 | 30 | PRECISSION 31 | VAL:0.0000 32 | -------------------------------------------------------------------------------- /examples/HX_performance/performance_0.3.4.txt: -------------------------------------------------------------------------------- 1 | BOARD: UNO 2 | IDE: 1.8.19 3 | SKETCH: HX_performance.ino 4 | 5 | LIBRARY VERSION: 0.3.4 6 | 7 | 8 | UNCONFIGURED 9 | RAW: 0.00 10 | AVG: 0.00 11 | VALUE: 0.00 12 | UNITS: 0.0 13 | 14 | CONFIGURED 15 | RAW: 0.00 16 | AVG: 0.00 17 | VALUE: 0.00 18 | UNITS: 0.0 19 | 20 | SLEEP 21 | 22 | WAKE UP 23 | RAW: 0.00 24 | AVG: 0.00 25 | VALUE: 0.00 26 | UNITS: 0.0 27 | 28 | PERFORMANCE 29 | 100x get_units(1) = 40904 30 | VAL: 0.00 31 | 32 | PRECISION 33 | VAL:0.0000 34 | 35 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "HX711", 3 | "keywords": "loadcell,weight,kilo,pound,gram,price,lbs,ounce,pound", 4 | "description": "Arduino library for HX711 load cell amplifier, includes calibrate functions, reading median and median_average.", 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" 17 | }, 18 | "version": "0.6.3", 19 | "license": "MIT", 20 | "frameworks": "*", 21 | "platforms": "*", 22 | "headers": "HX711.h" 23 | } 24 | -------------------------------------------------------------------------------- /examples/HX_get_noise_statistics/.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 | libraries: 29 | - "Statistic" -------------------------------------------------------------------------------- /examples/HX_plotter_multimap/.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 | libraries: 29 | - "MultiMap" 30 | -------------------------------------------------------------------------------- /examples/HX_is_ready/HX_is_ready.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: HX_is_ready.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: HX711 demo 5 | // URL: https://github.com/RobTillaart/HX711 6 | 7 | 8 | #include "HX711.h" 9 | 10 | HX711 scale; 11 | 12 | // adjust pins if needed 13 | uint8_t dataPin = 6; 14 | uint8_t clockPin = 7; 15 | 16 | 17 | void setup() 18 | { 19 | Serial.begin(115200); 20 | Serial.println(); 21 | Serial.println(__FILE__); 22 | Serial.print("HX711_LIB_VERSION: "); 23 | Serial.println(HX711_LIB_VERSION); 24 | Serial.println(); 25 | 26 | scale.begin(dataPin, clockPin); 27 | 28 | // TODO find a nice solution for this calibration.. 29 | // load cell factor 20 KG 30 | // scale.set_scale(127.15); 31 | 32 | // load cell factor 5 KG 33 | scale.set_scale(420.0983); // TODO you need to calibrate this yourself. 34 | // reset the scale to zero = 0 35 | scale.tare(20); 36 | } 37 | 38 | 39 | void loop() 40 | { 41 | if (scale.is_ready()) 42 | { 43 | Serial.println(scale.get_units(1)); 44 | } 45 | } 46 | 47 | 48 | // -- END OF FILE -- 49 | -------------------------------------------------------------------------------- /examples/HX_plotter/HX_plotter.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: HX_plotter.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: HX711 demo 5 | // URL: https://github.com/RobTillaart/HX711 6 | 7 | 8 | #include "HX711.h" 9 | 10 | HX711 scale; 11 | 12 | // adjust pins if needed 13 | uint8_t dataPin = 6; 14 | uint8_t clockPin = 7; 15 | 16 | float f; 17 | 18 | 19 | void setup() 20 | { 21 | Serial.begin(115200); 22 | // Serial.println(); 23 | // Serial.println(__FILE__); 24 | // Serial.print("HX711_LIB_VERSION: "); 25 | // Serial.println(HX711_LIB_VERSION); 26 | // Serial.println(); 27 | 28 | scale.begin(dataPin, clockPin); 29 | 30 | // TODO find a nice solution for this calibration.. 31 | // load cell factor 20 KG 32 | // scale.set_scale(127.15); 33 | // load cell factor 5 KG 34 | scale.set_scale(420.0983); // TODO you need to calibrate this yourself. 35 | // reset the scale to zero = 0 36 | scale.tare(); 37 | } 38 | 39 | 40 | void loop() 41 | { 42 | // continuous scale 4x per second 43 | f = scale.get_units(5); 44 | Serial.println(f); 45 | delay(250); 46 | } 47 | 48 | 49 | // -- END OF FILE -- 50 | 51 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2025 Rob Tillaart 4 | Copyright (c) 2018 Bogdan Necula 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /examples/HX_loadcell_array/HX_loadcell_array.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: HX_loadcell_array.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: HX711 demo 5 | // URL: https://github.com/RobTillaart/HX711 6 | // 7 | // see #40 8 | // see https://forum.arduino.cc/t/mega2560-with-multiple-hx711/1378510 16 cells 9 | 10 | 11 | #include "HX711.h" 12 | 13 | HX711 scale0; 14 | HX711 scale1; 15 | HX711 scale2; 16 | HX711 scale3; 17 | 18 | HX711 scales[4] = { scale0, scale1, scale2, scale3 }; 19 | 20 | // adjust pins if needed 21 | const uint8_t dataPin[4] = { 3, 4, 5, 6 }; 22 | const uint8_t clockPin = 7; 23 | 24 | // TODO you need to adjust to your calibrated scale values 25 | float calib[4] = { 420.0983, 421.365, 419.200, 410.236 }; 26 | 27 | uint32_t count = 0; 28 | 29 | void setup() 30 | { 31 | Serial.begin(115200); 32 | Serial.println(); 33 | Serial.println(__FILE__); 34 | Serial.print("HX711_LIB_VERSION: "); 35 | Serial.println(HX711_LIB_VERSION); 36 | Serial.println(); 37 | 38 | for (int i = 0; i < 4; i++) 39 | { 40 | scales[i].begin(dataPin[i], clockPin); 41 | scales[i].set_scale(calib[i]); 42 | // reset the scale to zero = 0 43 | scales[i].tare(); 44 | } 45 | } 46 | 47 | 48 | void loop() 49 | { 50 | count++; 51 | Serial.print(count); 52 | for (int i = 0; i < 4; i++) 53 | { 54 | Serial.print("\t"); 55 | Serial.print(scales[i].get_units(5)); 56 | } 57 | Serial.println(); 58 | delay(250); 59 | } 60 | 61 | 62 | // -- END OF FILE -- 63 | -------------------------------------------------------------------------------- /examples/HX_read_median_average/HX_read_median_average.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: HX_read_median_average.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: HX711 demo 5 | // URL: https://github.com/RobTillaart/HX711 6 | 7 | 8 | #include "HX711.h" 9 | 10 | HX711 scale; 11 | 12 | // adjust pins if needed 13 | uint8_t dataPin = 6; 14 | uint8_t clockPin = 7; 15 | 16 | uint32_t start, stop; 17 | volatile float f; 18 | 19 | 20 | void setup() 21 | { 22 | Serial.begin(115200); 23 | Serial.println(); 24 | Serial.println(__FILE__); 25 | Serial.print("HX711_LIB_VERSION: "); 26 | Serial.println(HX711_LIB_VERSION); 27 | Serial.println(); 28 | 29 | scale.begin(dataPin, clockPin); 30 | 31 | // TODO find a nice solution for this calibration.. 32 | // load cell factor 20 KG 33 | scale.set_scale(127.15); 34 | 35 | // load cell factor 5 KG 36 | // scale.set_scale(420.0983); 37 | // reset the scale to zero = 0 38 | scale.tare(); 39 | 40 | Serial.println("\nPERFORMANCE"); 41 | start = micros(); 42 | f = 0; 43 | for (int i = 0; i < 100; i++) 44 | { 45 | f = scale.read_medavg(7); 46 | } 47 | stop = micros(); 48 | Serial.print("100x read_medavg(7) = "); 49 | Serial.println(stop - start); 50 | Serial.print(" VAL: "); 51 | Serial.println(f, 2); 52 | } 53 | 54 | 55 | void loop() 56 | { 57 | // continuous scale once per second 58 | f = scale.read_medavg(7); 59 | Serial.println(f); 60 | delay(1000); 61 | } 62 | 63 | 64 | // -- END OF FILE -- 65 | 66 | -------------------------------------------------------------------------------- /examples/HX_read_median/HX_read_median.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: HX_read_median.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: HX711 demo 5 | // URL: https://github.com/RobTillaart/HX711 6 | 7 | 8 | 9 | #include "HX711.h" 10 | 11 | HX711 scale; 12 | 13 | // adjust pins if needed 14 | uint8_t dataPin = 6; 15 | uint8_t clockPin = 7; 16 | 17 | uint32_t start, stop; 18 | volatile float f; 19 | 20 | 21 | void setup() 22 | { 23 | Serial.begin(115200); 24 | Serial.println(); 25 | Serial.println(__FILE__); 26 | Serial.print("HX711_LIB_VERSION: "); 27 | Serial.println(HX711_LIB_VERSION); 28 | Serial.println(); 29 | 30 | scale.begin(dataPin, clockPin); 31 | 32 | // TODO find a nice solution for this calibration.. 33 | // load cell factor 20 KG 34 | scale.set_scale(127.15); // TODO you need to calibrate this yourself. 35 | 36 | // load cell factor 5 KG 37 | // scale.set_scale(420.0983); 38 | // reset the scale to zero = 0 39 | scale.tare(); 40 | 41 | Serial.println("\nPERFORMANCE"); 42 | start = micros(); 43 | f = 0; 44 | for (int i = 0; i < 100; i++) 45 | { 46 | f = scale.read_median(7); 47 | } 48 | stop = micros(); 49 | Serial.print("100x read_median(7) = "); 50 | Serial.println(stop - start); 51 | Serial.print(" VAL: "); 52 | Serial.println(f, 2); 53 | } 54 | 55 | 56 | void loop() 57 | { 58 | // continuous scale once per second 59 | f = scale.read_median(7); 60 | Serial.println(f); 61 | delay(1000); 62 | } 63 | 64 | 65 | // -- END OF FILE -- 66 | 67 | -------------------------------------------------------------------------------- /examples/HX_performance2/HX_performance2.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: HX_performance2.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: HX711 performance measurements 5 | // URL: https://github.com/RobTillaart/HX711 6 | 7 | 8 | #include "HX711.h" 9 | 10 | HX711 scale; 11 | 12 | // adjust pins if needed 13 | uint8_t dataPin = 6; 14 | uint8_t clockPin = 7; 15 | 16 | uint32_t start, stop; 17 | volatile float f; 18 | 19 | 20 | void setup() 21 | { 22 | Serial.begin(115200); 23 | Serial.println(); 24 | Serial.println(__FILE__); 25 | Serial.print("HX711_LIB_VERSION: "); 26 | Serial.println(HX711_LIB_VERSION); 27 | Serial.println(); 28 | 29 | scale.begin(dataPin, clockPin); 30 | 31 | // TODO find a nice solution for this calibration.. 32 | // load cell factor 20 KG 33 | // scale.set_scale(127.15); 34 | 35 | // load cell factor 5 KG 36 | scale.set_scale(420.0983); // TODO you need to calibrate this yourself. 37 | // reset the scale to zero = 0 38 | scale.tare(); 39 | 40 | measure(); 41 | } 42 | 43 | 44 | void loop() 45 | { 46 | } 47 | 48 | 49 | void measure() 50 | { 51 | Serial.println("Counting get_units() calls for 1 minute..."); 52 | delay(20); 53 | uint32_t count = 0; 54 | uint32_t start = millis(); 55 | while (millis() - start < 60000) 56 | { 57 | if (scale.is_ready()) 58 | { 59 | count++; 60 | scale.get_units(1); 61 | } 62 | } 63 | Serial.print("calls per minute: "); 64 | Serial.println(count); 65 | Serial.print("calls per second: "); 66 | Serial.println(count / 60.0); 67 | } 68 | 69 | 70 | // -- END OF FILE -- 71 | 72 | -------------------------------------------------------------------------------- /examples/HX_delta_scale/HX_delta_scale.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: HX_kitchen_scale.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: HX711 demo 5 | // URL: https://github.com/RobTillaart/HX711 6 | 7 | 8 | #include "HX711.h" 9 | 10 | HX711 scale; 11 | 12 | // adjust pins if needed 13 | uint8_t dataPin = 6; 14 | uint8_t clockPin = 7; 15 | 16 | float w1, w2, previous = 0; 17 | 18 | 19 | void setup() 20 | { 21 | Serial.begin(115200); 22 | Serial.println(); 23 | Serial.println(__FILE__); 24 | Serial.print("HX711_LIB_VERSION: "); 25 | Serial.println(HX711_LIB_VERSION); 26 | Serial.println(); 27 | 28 | scale.begin(dataPin, clockPin); 29 | 30 | Serial.print("UNITS: "); 31 | Serial.println(scale.get_units(10)); 32 | 33 | // load cell factor 20 KG 34 | // scale.set_scale(127.15); 35 | // load cell factor 5 KG 36 | scale.set_scale(420.0983); // TODO you need to calibrate this yourself. 37 | scale.tare(); 38 | 39 | Serial.print("UNITS: "); 40 | Serial.println(scale.get_units(10)); 41 | } 42 | 43 | 44 | void loop() 45 | { 46 | // read until stable 47 | w1 = scale.get_units(10); 48 | delay(100); 49 | w2 = scale.get_units(); 50 | while (abs(w1 - w2) > 10) 51 | { 52 | w1 = w2; 53 | w2 = scale.get_units(); 54 | delay(100); 55 | } 56 | 57 | Serial.print("UNITS: "); 58 | Serial.print(w1); 59 | if (w1 == 0) 60 | { 61 | Serial.println(); 62 | } 63 | else 64 | { 65 | Serial.print("\t\tDELTA: "); 66 | Serial.println(w1 - previous); 67 | previous = w1; 68 | } 69 | delay(100); 70 | } 71 | 72 | 73 | // -- END OF FILE -- 74 | 75 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | # Syntax Colouring Map For HX711 2 | 3 | # Data types (KEYWORD1) 4 | HX711 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 | tare KEYWORD2 33 | get_tare KEYWORD2 34 | tare_set KEYWORD2 35 | 36 | set_gain KEYWORD2 37 | get_gain KEYWORD2 38 | # set_chanA_gain128 KEYWORD2 39 | # set_chanA_gain64 KEYWORD2 40 | # set_chanB_gain32 KEYWORD2 41 | 42 | set_scale KEYWORD2 43 | get_scale KEYWORD2 44 | 45 | set_offset KEYWORD2 46 | get_offset KEYWORD2 47 | 48 | calibrate_scale KEYWORD2 49 | 50 | power_down KEYWORD2 51 | power_up KEYWORD2 52 | 53 | set_rate_pin KEYWORD2 54 | set_rate_10SPS KEYWORD2 55 | set_rate_80SPS KEYWORD2 56 | get_rate KEYWORD2 57 | 58 | last_read KEYWORD2 59 | 60 | get_price KEYWORD2 61 | set_unit_price KEYWORD2 62 | get_unit_price KEYWORD2 63 | 64 | 65 | # Instances (KEYWORD2) 66 | 67 | 68 | # Constants (LITERAL1) 69 | HX711_LIB_VERSION LITERAL1 70 | 71 | HX711_RAW_MODE LITERAL1 72 | HX711_AVERAGE_MODE LITERAL1 73 | HX711_MEDIAN_MODE LITERAL1 74 | HX711_MEDAVG_MODE LITERAL1 75 | HX711_RUNAVG_MODE LITERAL1 76 | 77 | HX711_CHANNEL_A_GAIN_128 LITERAL1 78 | HX711_CHANNEL_A_GAIN_64 LITERAL1 79 | HX711_CHANNEL_B_GAIN_32 LITERAL1 80 | 81 | -------------------------------------------------------------------------------- /examples/HX_morse_experimental/HX_morse_experimental.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: HX_morse_experimental.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: HX711 demo 5 | // URL: https://github.com/RobTillaart/HX711 6 | // 7 | // far from a morse decoder but a start to show a different 8 | // application for loadcells. 9 | // discriminate between long and short presses. 10 | 11 | 12 | #include "HX711.h" 13 | 14 | HX711 scale; 15 | 16 | uint8_t dataPin = 6; 17 | uint8_t clockPin = 7; 18 | 19 | uint32_t start = 0, duration = 0; 20 | 21 | 22 | void setup() 23 | { 24 | Serial.begin(115200); 25 | Serial.println(); 26 | Serial.println(__FILE__); 27 | Serial.print("HX711_LIB_VERSION: "); 28 | Serial.println(HX711_LIB_VERSION); 29 | Serial.println(); 30 | 31 | scale.begin(dataPin, clockPin); 32 | 33 | // TODO find a nice solution for this calibration.. 34 | // load cell factor 20 KG 35 | // scale.set_scale(127.15); 36 | 37 | // load cell factor 5 KG 38 | scale.set_scale(420.0983); // TODO you need to calibrate this yourself. 39 | // reset the scale to zero = 0 40 | scale.tare(20); 41 | } 42 | 43 | 44 | void loop() 45 | { 46 | if (scale.is_ready()) 47 | { 48 | float val = scale.get_units(1); 49 | if (start == 0 && val > 10) 50 | { 51 | start = millis(); 52 | } 53 | if (start > 0 && val < 1) 54 | { 55 | duration = millis() - start; 56 | start = 0; 57 | 58 | Serial.print(duration); 59 | Serial.print("\t"); 60 | if (duration < 700) Serial.println("."); 61 | else if (duration < 1400) Serial.println("-"); 62 | else Serial.println("---"); 63 | } 64 | } 65 | } 66 | 67 | 68 | // -- END OF FILE -- 69 | 70 | -------------------------------------------------------------------------------- /examples/HX_grocery_scale/HX_grocery_scale.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: HX_grocery_scale.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: HX711 demo 5 | // URL: https://github.com/RobTillaart/HX711 6 | 7 | 8 | #include "HX711.h" 9 | 10 | HX711 scale; 11 | 12 | // adjust pins if needed 13 | uint8_t dataPin = 6; 14 | uint8_t clockPin = 7; 15 | 16 | 17 | void setup() 18 | { 19 | Serial.begin(115200); 20 | Serial.println(); 21 | Serial.println(__FILE__); 22 | Serial.print("HX711_LIB_VERSION: "); 23 | Serial.println(HX711_LIB_VERSION); 24 | Serial.println(); 25 | 26 | scale.begin(dataPin, clockPin); 27 | 28 | Serial.print("UNITS: "); 29 | Serial.println(scale.get_units(10)); 30 | 31 | Serial.println("\nEmpty the scale, press a key to continue"); 32 | while(!Serial.available()); 33 | while(Serial.available()) Serial.read(); 34 | 35 | scale.tare(20); 36 | Serial.print("UNITS: "); 37 | Serial.println(scale.get_units(10)); 38 | 39 | 40 | Serial.println("\nPut 1000 gr in the scale, press a key to continue"); 41 | while(!Serial.available()); 42 | while(Serial.available()) Serial.read(); 43 | 44 | scale.calibrate_scale(1000, 5); 45 | Serial.print("UNITS: "); 46 | Serial.println(scale.get_units(10)); 47 | 48 | Serial.println("\nScale is calibrated, press a key to continue"); 49 | while(!Serial.available()); 50 | while(Serial.available()) Serial.read(); 51 | 52 | scale.set_unit_price(0.031415); // we only have one price 53 | } 54 | 55 | 56 | void loop() 57 | { 58 | Serial.print("UNITS: "); 59 | Serial.print(scale.get_units(5)); 60 | Serial.print("\t\tPRICE: "); 61 | Serial.println(scale.get_price(5)); 62 | delay(1000); 63 | } 64 | 65 | 66 | // -- END OF FILE -- 67 | -------------------------------------------------------------------------------- /examples/HX_kitchen_scale/HX_kitchen_scale.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: HX_kitchen_scale.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: HX711 demo 5 | // URL: https://github.com/RobTillaart/HX711 6 | 7 | 8 | #include "HX711.h" 9 | 10 | HX711 scale; 11 | 12 | // adjust pins if needed 13 | // uint8_t dataPin = 6; 14 | // uint8_t clockPin = 7; 15 | uint8_t dataPin = 19; // for ESP32 16 | uint8_t clockPin = 18; // for ESP32 17 | 18 | void setup() 19 | { 20 | Serial.begin(115200); 21 | Serial.println(); 22 | Serial.println(__FILE__); 23 | Serial.print("HX711_LIB_VERSION: "); 24 | Serial.println(HX711_LIB_VERSION); 25 | Serial.println(); 26 | 27 | scale.begin(dataPin, clockPin); 28 | 29 | Serial.print("UNITS: "); 30 | Serial.println(scale.get_units(10)); 31 | 32 | Serial.println("\nEmpty the scale, press a key to continue"); 33 | while(!Serial.available()); 34 | while(Serial.available()) Serial.read(); 35 | 36 | scale.tare(); 37 | Serial.print("UNITS: "); 38 | Serial.println(scale.get_units(10)); 39 | 40 | 41 | Serial.println("\nPut 1000 gram in the scale, press a key to continue"); 42 | while(!Serial.available()); 43 | while(Serial.available()) Serial.read(); 44 | 45 | scale.calibrate_scale(1000, 5); 46 | Serial.print("UNITS: "); 47 | Serial.println(scale.get_units(10)); 48 | 49 | Serial.println("\nScale is calibrated, press a key to continue"); 50 | // Serial.println(scale.get_scale()); 51 | // Serial.println(scale.get_offset()); 52 | while(!Serial.available()); 53 | while(Serial.available()) Serial.read(); 54 | } 55 | 56 | 57 | void loop() 58 | { 59 | Serial.print("UNITS: "); 60 | Serial.println(scale.get_units(10)); 61 | delay(250); 62 | } 63 | 64 | 65 | // -- END OF FILE -- 66 | 67 | -------------------------------------------------------------------------------- /examples/HX_plotter_multimap/HX_plotter_multimap.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: HX_plotter_multimap.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: HX711 demo 5 | // URL: https://github.com/RobTillaart/HX711 6 | 7 | 8 | 9 | #include "MultiMap.h" 10 | 11 | // inkg[] holds the weights from the linear calibrated HX711 sensor 12 | float inkg[] = { 0, 6.25, 11.50, 14.50, 30.00, 34.10, 38.20, 44.50, 50.00}; 13 | 14 | // outkg[] holds the corrected weight 15 | float outkg[] = { 0, 5, 10, 15, 30, 35, 40, 45, 50}; 16 | 17 | 18 | #include "HX711.h" 19 | 20 | HX711 scale; 21 | 22 | // adjust pins if needed 23 | uint8_t dataPin = 6; 24 | uint8_t clockPin = 7; 25 | 26 | uint32_t start, stop; 27 | 28 | float raw, corrected; 29 | 30 | 31 | void setup() 32 | { 33 | Serial.begin(115200); 34 | // Serial.println(); 35 | // Serial.println(__FILE__); 36 | // Serial.print("HX711_LIB_VERSION: "); 37 | // Serial.println(HX711_LIB_VERSION); 38 | Serial.println(); 39 | 40 | scale.begin(dataPin, clockPin); 41 | 42 | // TODO find a nice solution for this calibration.. 43 | // load cell factor 20 KG 44 | // scale.set_scale(127.15); 45 | // load cell factor 5 KG 46 | scale.set_scale(420.0983); // TODO you need to calibrate this yourself. 47 | // reset the scale to zero = 0 48 | scale.tare(); 49 | 50 | // dump whole range 0..50 kg 51 | for (float w = 0; w <= 50; w += 0.1) 52 | { 53 | Serial.print(w, 3); 54 | Serial.print('\t'); 55 | corrected = multiMap(w, inkg, outkg, 9); // 9 is the size of the arrays! 56 | Serial.println(corrected, 3); 57 | } 58 | 59 | delay(50000); 60 | 61 | } 62 | 63 | 64 | void loop() 65 | { 66 | raw = scale.get_units(5); 67 | corrected = multiMap(raw, inkg, outkg, 9); // 9 is the size of the arrays! 68 | 69 | Serial.print(raw, 3); 70 | Serial.print('\t'); 71 | Serial.println(corrected, 3); 72 | delay(250); 73 | } 74 | 75 | 76 | // -- END OF FILE -- 77 | -------------------------------------------------------------------------------- /examples/HX_calibration/HX_calibration.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: HX_calibration.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: HX711 calibration finder for offset and scale 5 | // URL: https://github.com/RobTillaart/HX711 6 | 7 | 8 | #include "HX711.h" 9 | 10 | HX711 myScale; 11 | 12 | // adjust pins if needed. 13 | uint8_t dataPin = 6; 14 | uint8_t clockPin = 7; 15 | 16 | 17 | void setup() 18 | { 19 | Serial.begin(115200); 20 | Serial.println(); 21 | Serial.println(__FILE__); 22 | Serial.print("HX711_LIB_VERSION: "); 23 | Serial.println(HX711_LIB_VERSION); 24 | Serial.println(); 25 | 26 | myScale.begin(dataPin, clockPin); 27 | } 28 | 29 | void loop() 30 | { 31 | calibrate(); 32 | } 33 | 34 | 35 | 36 | void calibrate() 37 | { 38 | Serial.println("\n\nCALIBRATION\n==========="); 39 | Serial.println("remove all weight from the loadcell"); 40 | // flush Serial input 41 | while (Serial.available()) Serial.read(); 42 | 43 | Serial.println("and press enter\n"); 44 | while (Serial.available() == 0); 45 | 46 | Serial.println("Determine zero weight offset"); 47 | // average 20 measurements. 48 | myScale.tare(20); 49 | int32_t offset = myScale.get_offset(); 50 | 51 | Serial.print("OFFSET: "); 52 | Serial.println(offset); 53 | Serial.println(); 54 | 55 | 56 | Serial.println("place a weight on the loadcell"); 57 | // flush Serial input 58 | while (Serial.available()) Serial.read(); 59 | 60 | Serial.println("enter the weight in (whole) grams and press enter"); 61 | uint32_t weight = 0; 62 | while (Serial.peek() != '\n') 63 | { 64 | if (Serial.available()) 65 | { 66 | char ch = Serial.read(); 67 | if (isdigit(ch)) 68 | { 69 | weight *= 10; 70 | weight = weight + (ch - '0'); 71 | } 72 | } 73 | } 74 | Serial.print("WEIGHT: "); 75 | Serial.println(weight); 76 | myScale.calibrate_scale(weight, 20); 77 | float scale = myScale.get_scale(); 78 | 79 | Serial.print("SCALE: "); 80 | Serial.println(scale, 6); 81 | 82 | Serial.print("\nuse scale.set_offset("); 83 | Serial.print(offset); 84 | Serial.print("); and scale.set_scale("); 85 | Serial.print(scale, 6); 86 | Serial.print(");\n"); 87 | Serial.println("in the setup of your project"); 88 | 89 | Serial.println("\n\n"); 90 | } 91 | 92 | 93 | // -- END OF FILE -- 94 | -------------------------------------------------------------------------------- /examples/HX_performance/HX_performance.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: HX_performance.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: HX711 performance measurements 5 | // URL: https://github.com/RobTillaart/HX711 6 | 7 | 8 | #include "HX711.h" 9 | 10 | HX711 scale; 11 | 12 | // adjust pins if needed 13 | uint8_t dataPin = 6; 14 | uint8_t clockPin = 7; 15 | 16 | uint32_t start, stop; 17 | volatile float f; 18 | 19 | 20 | void setup() 21 | { 22 | Serial.begin(115200); 23 | Serial.println(); 24 | Serial.println(__FILE__); 25 | Serial.print("HX711_LIB_VERSION: "); 26 | Serial.println(HX711_LIB_VERSION); 27 | Serial.println(); 28 | 29 | scale.begin(dataPin, clockPin); 30 | 31 | Serial.println("\nUNCONFIGURED"); 32 | delay(1000); 33 | measure(10); 34 | 35 | // TODO find a nice solution for this calibration.. 36 | // load cell factor 20 KG 37 | // scale.set_scale(127.15); 38 | 39 | // load cell factor 5 KG 40 | scale.set_scale(420.0983); // TODO you need to calibrate this yourself. 41 | // reset the scale to zero = 0 42 | scale.tare(); 43 | 44 | Serial.println("\nCONFIGURED"); 45 | delay(1000); 46 | measure(10); 47 | 48 | Serial.println("\nSLEEP"); 49 | scale.power_down(); 50 | delay(2000); 51 | scale.power_up(); 52 | Serial.println("\nWAKE UP"); 53 | delay(1000); 54 | measure(10); 55 | 56 | /* 57 | * PERFORMANCE 58 | * 100x get_units(1) = 9404352 (UNO) 59 | * VAL: 0.05 60 | */ 61 | Serial.println("\nPERFORMANCE"); 62 | delay(10); 63 | start = micros(); 64 | f = 0; 65 | for (int i = 0; i < 100; i++) 66 | { 67 | f = scale.get_units(1); 68 | } 69 | stop = micros(); 70 | Serial.print("100x get_units(1) = "); 71 | Serial.println(stop - start); 72 | Serial.print(" VAL: "); 73 | Serial.println(f, 2); 74 | 75 | Serial.println("\nPRECISION"); 76 | f = 0; 77 | for (int i = 0; i < 100; i++) 78 | { 79 | f += scale.get_units(1); 80 | } 81 | Serial.print(" VAL:"); 82 | Serial.println(f * 0.01, 4); 83 | } 84 | 85 | 86 | void loop() 87 | { 88 | // continuous scale 4x per second 89 | // f = scale.get_units(5); 90 | // Serial.println(f); 91 | // delay(250); 92 | } 93 | 94 | 95 | void measure(uint8_t cnt) 96 | { 97 | Serial.print(" RAW: "); 98 | Serial.println(scale.read()); 99 | Serial.print(" AVG: "); 100 | Serial.println(scale.read_average(cnt)); 101 | Serial.print("VALUE: "); 102 | Serial.println(scale.get_value(cnt)); 103 | Serial.print("UNITS: "); 104 | Serial.println(scale.get_units(cnt), 1); 105 | } 106 | 107 | 108 | // -- END OF FILE -- 109 | -------------------------------------------------------------------------------- /examples/HX_set_mode/HX_set_mode.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: HX_set_mode.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: HX711 demo 5 | // URL: https://github.com/RobTillaart/HX711 6 | 7 | 8 | #include "HX711.h" 9 | 10 | HX711 scale; 11 | 12 | // adjust pins if needed 13 | uint8_t dataPin = 6; 14 | uint8_t clockPin = 7; 15 | 16 | uint32_t start, stop; 17 | volatile float f; 18 | 19 | 20 | void setup() 21 | { 22 | Serial.begin(115200); 23 | Serial.println(); 24 | Serial.println(__FILE__); 25 | Serial.print("HX711_LIB_VERSION: "); 26 | Serial.println(HX711_LIB_VERSION); 27 | Serial.println(); 28 | 29 | scale.begin(dataPin, clockPin); 30 | 31 | // TODO find a nice solution for this calibration.. 32 | // loadcell factor 20 KG 33 | scale.set_scale(127.15); 34 | 35 | // loadcell factor 5 KG 36 | // scale.set_scale(420.0983); 37 | // reset the scale to zero = 0 38 | scale.tare(); 39 | 40 | 41 | Serial.println(); 42 | scale.set_average_mode(); 43 | Serial.print(scale.get_mode()); 44 | Serial.println("\tPERFORMANCE set_average_mode"); 45 | delay(10); 46 | start = micros(); 47 | f = 0; 48 | for (int i = 0; i < 100; i++) 49 | { 50 | f = scale.get_value(7); 51 | } 52 | stop = micros(); 53 | Serial.print("100x set_average_mode = "); 54 | Serial.println(stop - start); 55 | Serial.print(" VAL: "); 56 | Serial.println(f, 2); 57 | 58 | 59 | Serial.println(); 60 | scale.set_median_mode(); 61 | Serial.print(scale.get_mode()); 62 | Serial.println("\tPERFORMANCE set_median_mode"); 63 | delay(10); 64 | start = micros(); 65 | f = 0; 66 | for (int i = 0; i < 100; i++) 67 | { 68 | f = scale.get_value(7); 69 | } 70 | stop = micros(); 71 | Serial.print("100x set_median_mode = "); 72 | Serial.println(stop - start); 73 | Serial.print(" VAL: "); 74 | Serial.println(f, 2); 75 | 76 | 77 | Serial.println(); 78 | scale.set_medavg_mode(); 79 | Serial.print(scale.get_mode()); 80 | Serial.println("\tPERFORMANCE set_medavg_mode"); 81 | delay(10); 82 | start = micros(); 83 | f = 0; 84 | for (int i = 0; i < 100; i++) 85 | { 86 | f = scale.get_value(7); 87 | } 88 | stop = micros(); 89 | Serial.print("100x set_medavg_mode = "); 90 | Serial.println(stop - start); 91 | Serial.print(" VAL: "); 92 | Serial.println(f, 2); 93 | 94 | 95 | Serial.println(); 96 | scale.set_raw_mode(); 97 | Serial.print(scale.get_mode()); 98 | Serial.println("\tPERFORMANCE set_raw_mode"); 99 | delay(10); 100 | start = micros(); 101 | f = 0; 102 | for (int i = 0; i < 100; i++) 103 | { 104 | f = scale.get_value(7); // note 7 will be ignored 105 | } 106 | stop = micros(); 107 | Serial.print("100x set_raw_mode = "); 108 | Serial.println(stop - start); 109 | Serial.print(" VAL: "); 110 | Serial.println(f, 2); 111 | 112 | 113 | Serial.println("\ndone..."); 114 | } 115 | 116 | 117 | void loop() 118 | { 119 | } 120 | 121 | 122 | // -- END OF FILE -- 123 | -------------------------------------------------------------------------------- /CHANGELOG..md: -------------------------------------------------------------------------------- 1 | # Change Log HX711 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.6.3] - 2025-09-16 9 | - fix #70, HX711 rate pin code 10 | - fix #70, add a doReset parameter to begin() to improve start up time. 11 | - update readme.md 12 | - add details about start up time (related to RATE). 13 | - add **isReady()** check in calibration 14 | - add reference to ADAfruit breakout with RATE 15 | - minor edits 16 | 17 | ## [0.6.2] - 2025-09-06 18 | - fix #68, add bogde to the license, to give credit for the API 19 | - implement experimental rate support. 20 | - update keywords.txt 21 | - update readme.md 22 | - update the GitHub actions 23 | - minor edits 24 | 25 | ## [0.6.1] - 2025-06-19 26 | - fix #65, is_ready() => set dataPin to INPUT_PULLUP 27 | - minor edits 28 | 29 | ## [0.6.0] - 2025-04-10 30 | - fix #60, change parameter **void calibrate_scale(float weight, uint8_t times = 10)** 31 | - update readme.md 32 | 33 | ---- 34 | 35 | ## [0.5.2] - 2024-11-18 36 | - fix #56, update readme.md 37 | - add parameter default for **fastProcessor** 38 | - add **last_time_read()** to replace **last_read()** in future. 39 | - minor edits 40 | 41 | 42 | ## [0.5.1] - 2024-11-08 43 | - fix #54, calibration sketch should output int32_t for offset. 44 | - update examples 45 | - update readme.md 46 | - minor edits 47 | 48 | ## [0.5.0] - 2024-06-17 49 | - fix #50, bug in constructor (thanks Mathieu!) 50 | - refactor constructor 51 | 52 | ---- 53 | 54 | ## [0.4.0] - 2024-03-02 55 | - add fastProcessor option in **begin()** (Thanks to palmerr23) 56 | - updated license 57 | - updated GitHub/actions to v4 58 | 59 | ---- 60 | 61 | ## [0.3.9] - 2023-11-04 62 | - update readme.md 63 | - minor edits 64 | 65 | ## [0.3.8] - 2023-08-26 66 | - fix #41 #40 add example **HX_loadcell_array.ino** 67 | - test support array of loadcells. 68 | - update readme.md 69 | - add issue-count badge 70 | - add PlatformIO badge 71 | - minor edits 72 | 73 | ## [0.3.7] - 2023-06-27 74 | - add example to measure noise level 75 | - moved code to .cpp 76 | - reorder .cpp to match .h 77 | - removed **callibrate_scale()** (typo ll) 78 | - add scale == 0 in **set_scale(scale)** 79 | - changed return type to **bool set_scale(scale)** 80 | - add example is_ready 81 | - add example pulse-length decoder (Morse) 82 | - update readme.md 83 | 84 | ## [0.3.6] - 2023-03-11 85 | - update readme.md to reference HX711_MP 86 | 87 | ## [0.3.5] - 2023-03-10 88 | - update readme.md 89 | - update GitHub actions 90 | - update license 2023 91 | - add MulitMap example 92 | - minor edits 93 | 94 | ## [0.3.4] - 2022-11-11 95 | - simplified changelog 96 | - add RP2040 to build-CI 97 | - refactored **set_gain()** to return bool to confirm valid parameter. 98 | - add forced flag for set_gain to force a dummy read call. Default false. 99 | - add constants for **set_gain()** 100 | - improved unit test for **set_gain()** 101 | - add unit test for constants. 102 | - add delayMicroseconds(64) to power_down to enforce long enough HIGH signal. 103 | - add power_down() power_up() cycle in **reset()** 104 | - updated readme.md 105 | - updated keywords.txt 106 | 107 | ## [0.3.3] - 2022-03-16 108 | - add HX711_RAW_MODE 109 | - update documentation 110 | 111 | ## [0.3.2] - 2022-03-16 112 | - add example HX_set_persistent.ino. (won't work for m4) 113 | - add CHANGELOG.md 114 | - minor edits 115 | 116 | ## [0.3.1] - 2021-12-19 117 | - update library.json 118 | - license 119 | - minor edits 120 | 121 | ## [0.3.0] - 2021-11-14 122 | - update build-CI 123 | - readme.md incl. badges 124 | - fix #11 shiftIn timing 125 | 126 | ---- 127 | 128 | ## [0.2.3] - 2021-05-26 129 | - add running_average() mode 130 | 131 | ## [0.2.2] - 2021-05-10 132 | - add read_median() 133 | - add mode operandi 134 | - fix typo 135 | 136 | ## [0.2.1] - 2020-12-28 137 | - add arduino-ci 138 | - unit test 139 | 140 | ## [0.2.0] - 2020-06-15 141 | - add price functions 142 | - some refactor 143 | 144 | ---- 145 | 146 | ## [0.1.1] - 2019-09-09 147 | - change long to float (reduce footprint) 148 | 149 | ## [0.1.0] - 2019-09-04 150 | - initial version 151 | -------------------------------------------------------------------------------- /examples/HX_set_persistent/HX_set_persistent.ino: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // FILE: HX_set_persistent.ino 4 | // AUTHOR: Felix Moli Grao la base de Rob Tillaart 5 | // PURPOSE: HX711 demo 6 | // URL: https://github.com/RobTillaart/HX711 7 | 8 | //scale.set_offset(-186985); 9 | //scale.set_scale(14.18); 10 | 11 | 12 | #include "HX711.h" 13 | #include 14 | 15 | 16 | int eeAddress = 0; 17 | #define EEPROM_SIZE 100 18 | 19 | 20 | bool forced = false; // indicates that we want to enter calibration mode 21 | HX711 scale; 22 | byte buttonTare= 2; 23 | 24 | // adjust pins if needed 25 | uint8_t dataPin = 6; 26 | uint8_t clockPin = 7; 27 | // uint8_t dataPin = 19; // for ESP32 28 | // uint8_t clockPin = 18; // for ESP32 29 | 30 | // scale.set_offset(-181815); 31 | // scale.set_scale(13.79); 32 | struct Bascula { 33 | float scala; 34 | long offSet; 35 | }; 36 | 37 | Bascula bascula; 38 | 39 | void SaveStruct(int eeAddress, Bascula bascula) { 40 | EEPROM.put(eeAddress, bascula); 41 | Serial.println( "Save custom object to EEPROM: " ); 42 | Serial.println( bascula.scala ); 43 | Serial.println( bascula.offSet ); 44 | } 45 | 46 | Bascula LoadStruct(int eeAddress) { 47 | EEPROM.get( eeAddress, bascula ); 48 | Serial.println( "Read custom object from EEPROM: " ); 49 | Serial.print("scale: ");Serial.println( bascula.scala ); 50 | Serial.print("offset: ");Serial.println( bascula.offSet ); 51 | return bascula; 52 | } 53 | 54 | 55 | void setup() 56 | { 57 | Serial.begin(115200); 58 | Serial.println(); 59 | Serial.println(__FILE__); 60 | Serial.print("HX711_LIB_VERSION: "); 61 | Serial.println(HX711_LIB_VERSION); 62 | Serial.println(); 63 | 64 | pinMode(buttonTare, INPUT_PULLUP); 65 | bascula = LoadStruct(0);//load off eeprom 66 | 67 | scale.set_scale(bascula.scala); //read scale from eeprom position 0 68 | scale.set_offset(bascula.offSet); //read offSet from eeprom position 100 69 | scale.begin(dataPin, clockPin);// initiate communication 70 | 71 | if ((bascula.scala == 0.00) || (bascula.offSet == 0) || (forced == true)) { 72 | 73 | Serial.print("UNITS: "); 74 | Serial.println(scale.get_units(10)); 75 | 76 | Serial.println("\nEmpty the scale, press a key to continue"); 77 | while (!Serial.available()); 78 | while (Serial.available()) Serial.read(); 79 | 80 | scale.tare(); 81 | Serial.print("UNITS: "); 82 | bascula.offSet=scale.get_units(10); 83 | Serial.println(bascula.offSet); 84 | 85 | 86 | Serial.println("\nPut 1000 gram in the scale, press a key to continue"); 87 | while (!Serial.available()); 88 | while (Serial.available()) Serial.read(); 89 | 90 | scale.calibrate_scale(1000, 5); 91 | Serial.print("UNITS: "); 92 | bascula.scala=scale.get_units(10); 93 | Serial.println(bascula.scala); 94 | 95 | Serial.println("\nScale is calibrated, your calibration values:"); 96 | 97 | long scaleOffset = scale.get_offset(); 98 | Serial.print("\nOffset \t"); 99 | Serial.println(scaleOffset); 100 | 101 | float scaleFactor = scale.get_scale(); 102 | Serial.print("Scale \t"); 103 | Serial.println(scaleFactor); 104 | 105 | Serial.println("\nUse this code for setting zero and calibration factor permanently:"); 106 | 107 | Serial.print("\nscale.set_offset("); 108 | Serial.print(scaleOffset); 109 | Serial.println(");"); 110 | Serial.print("scale.set_scale("); 111 | Serial.print(scaleFactor); 112 | Serial.println(");"); 113 | 114 | Serial.println("\nPress a key to continue"); 115 | while (!Serial.available()); 116 | while (Serial.available()) Serial.read(); 117 | 118 | //scale.set_offset(-88627); 119 | //scale.set_scale(101.05); 120 | 121 | bascula.scala=scaleFactor; 122 | bascula.offSet=scaleOffset; 123 | SaveStruct( 0, bascula);//Save to eeprom 124 | 125 | } else { 126 | Serial.println("The scale is calibrated... press to continue"); 127 | while (!Serial.available()); 128 | while (Serial.available()) Serial.read(); 129 | scale.set_offset(bascula.offSet); 130 | scale.set_scale(bascula.scala); 131 | } 132 | } 133 | 134 | 135 | void loop() 136 | { 137 | if (digitalRead(buttonTare)==false){ 138 | scale.tare(); 139 | delay(500); 140 | } 141 | 142 | Serial.print("UNITS: "); 143 | Serial.println(scale.get_units(15)); 144 | delay(250); 145 | } 146 | -------------------------------------------------------------------------------- /examples/HX_get_noise_statistics/HX_get_noise_statistics.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: HX_get_noise_statistics.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: HX711 noise meansurement 5 | // URL: https://github.com/RobTillaart/HX711 6 | // 7 | // This example uses - https://github.com/RobTillaart/Statistic 8 | // 9 | // The purpse of this sketch is to measure the noise of your system. 10 | // This is done by making 1000 measurements. 11 | // From these some basic statistics are caculated so one can see 12 | // how much digits are significant and how much noise there is. 13 | // 14 | // First one need to calibrate the loadcell and fill in the data 15 | // use - HX_calibration.ino to get the numbers. 16 | // Then run this program. 17 | // 18 | // example output 19 | // 5 KG loadcell 20 | // 21 | // COUNT MIN MAX AVG VAR PDEV UDEV 22 | // ... 23 | // 1000 -0.7148 1.1891 -0.0158 0.2187 0.4676 0.4678 24 | // 25 | // the scale.tare is averaged over 100 measurements (first loop) 26 | // so on a scale from 0..5000 gram 27 | // the average zero point drift = -0.0158 gram 28 | // the stddev is below 0.5 gram 29 | // MAX - MIN => ~1.9 gram 30 | // so accurate in order of 1 gram (about two stddev) 31 | 32 | 33 | #include "HX711.h" 34 | HX711 scale; 35 | 36 | // adjust pins if needed 37 | uint8_t dataPin = 6; 38 | uint8_t clockPin = 7; 39 | 40 | 41 | #include "Statistic.h" 42 | statistic::Statistic myStats; 43 | 44 | 45 | void setup() 46 | { 47 | Serial.begin(115200); 48 | Serial.println(); 49 | Serial.println(__FILE__); 50 | Serial.print("HX711_LIB_VERSION:\t"); 51 | Serial.println(HX711_LIB_VERSION); 52 | Serial.print("STATISTIC_LIB_VERSION:\t"); 53 | Serial.println(STATISTIC_LIB_VERSION); 54 | Serial.println(); 55 | 56 | scale.begin(dataPin, clockPin); 57 | 58 | // statistics on raw data 59 | myStats.clear(); 60 | scale.set_raw_mode(); 61 | Serial.println(); 62 | Serial.println("\tCOUNT \tMIN \tMAX \tAVG \tVAR \tPDEV \tUDEV"); 63 | for (uint32_t i = 1; i <= 100; i++) 64 | { 65 | float value = scale.read(); 66 | myStats.add(value); 67 | if (i % 10 == 0) 68 | { 69 | Serial.print("\t"); 70 | Serial.print(myStats.count()); 71 | Serial.print("\t"); 72 | Serial.print(myStats.minimum(), 4); 73 | Serial.print("\t"); 74 | Serial.print(myStats.maximum(), 4); 75 | Serial.print("\t"); 76 | Serial.print(myStats.average(), 4); 77 | Serial.print("\t"); 78 | Serial.print(myStats.variance(), 4); 79 | Serial.print("\t"); 80 | Serial.print(myStats.pop_stdev(), 4); 81 | Serial.print("\t"); 82 | Serial.print(myStats.unbiased_stdev(), 4); 83 | Serial.println(); 84 | } 85 | } 86 | 87 | 88 | // TODO find an in-sketch solution for this calibration. 89 | // use HX_calibration.ino for now 90 | // load cell factor 20 KG 91 | // scale.set_scale(127.15); // TODO you need to calibrate this yourself. 92 | 93 | // load cell factor 5 KG 94 | scale.set_scale(449.076354); 95 | 96 | // reset the scale to zero = 0 normally call scale.tare(30); 97 | // use the average from previous loop as tare offset 98 | scale.set_offset(myStats.average()); 99 | 100 | // for which weight measure the noise? 101 | while (Serial.available()) Serial.read(); 102 | Serial.println("\nApply weight and press enter\n"); 103 | while (!Serial.available()); 104 | Serial.read(); 105 | 106 | 107 | 108 | // statistics on average data 109 | myStats.clear(); 110 | scale.set_average_mode(); 111 | Serial.println(); 112 | Serial.println("\tCOUNT \tMIN \tMAX \tAVG \tVAR \tPDEV \tUDEV"); 113 | for (uint32_t i = 1; i <= 1000; i++) 114 | { 115 | float value = scale.get_units(1); 116 | myStats.add(value); 117 | if (i % 100 == 0) 118 | { 119 | Serial.print("\t"); 120 | Serial.print(myStats.count()); 121 | Serial.print("\t"); 122 | Serial.print(myStats.minimum(), 4); 123 | Serial.print("\t"); 124 | Serial.print(myStats.maximum(), 4); 125 | Serial.print("\t"); 126 | Serial.print(myStats.average(), 4); 127 | Serial.print("\t"); 128 | Serial.print(myStats.variance(), 4); 129 | Serial.print("\t"); 130 | Serial.print(myStats.pop_stdev(), 4); 131 | Serial.print("\t"); 132 | Serial.print(myStats.unbiased_stdev(), 4); 133 | Serial.println(); 134 | } 135 | } 136 | } 137 | 138 | 139 | void loop() 140 | { 141 | } 142 | 143 | 144 | // -- END OF FILE -- 145 | -------------------------------------------------------------------------------- /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.h" 29 | 30 | 31 | uint8_t dataPin = 6; 32 | uint8_t clockPin = 7; 33 | 34 | 35 | unittest_setup() 36 | { 37 | fprintf(stderr, "HX711_LIB_VERSION: %s\n", (char *) HX711_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 scale; 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_time_read()); 68 | } 69 | 70 | 71 | unittest(test_gain) 72 | { 73 | HX711 scale; 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 | unittest(test_scale) 108 | { 109 | HX711 scale; 110 | scale.begin(dataPin, clockPin); 111 | 112 | // default 113 | assertEqualFloat(1.0, scale.get_scale(), 0.001); 114 | 115 | for (float sc = 0.1; sc < 2.0; sc += 0.1) 116 | { 117 | scale.set_scale(sc); 118 | assertEqualFloat(sc, scale.get_scale(), 0.001); 119 | } 120 | scale.set_scale(); 121 | assertEqualFloat(1.0, scale.get_scale(), 0.001); 122 | } 123 | 124 | 125 | unittest(test_offset) 126 | { 127 | HX711 scale; 128 | scale.begin(dataPin, clockPin); 129 | 130 | // default offset 131 | assertEqual(0, scale.get_offset()); 132 | 133 | for (long of = -100; of < 100; of += 13) 134 | { 135 | scale.set_offset(of); 136 | assertEqual(of, scale.get_offset() ); 137 | } 138 | scale.set_offset(); 139 | assertEqual(0, scale.get_offset()); 140 | } 141 | 142 | 143 | unittest(test_tare) 144 | { 145 | HX711 scale; 146 | scale.begin(dataPin, clockPin); 147 | 148 | // default tare 149 | assertEqual(0, scale.get_tare()); 150 | assertFalse(scale.tare_set()); 151 | 152 | scale.set_offset(123); 153 | assertTrue(scale.tare_set()); 154 | } 155 | 156 | 157 | unittest(test_unit_price) 158 | { 159 | HX711 scale; 160 | scale.begin(dataPin, clockPin); 161 | 162 | assertEqual(0, scale.get_unit_price()); 163 | 164 | for (float up = 0.10; up < 10; up += 1.23) 165 | { 166 | scale.set_unit_price(up); 167 | assertEqualFloat(up, scale.get_unit_price(), 0.001); 168 | } 169 | scale.set_unit_price(); 170 | assertEqualFloat(1.0, scale.get_unit_price(), 0.001); 171 | } 172 | 173 | 174 | unittest(test_operational_mode) 175 | { 176 | HX711 scale; 177 | scale.begin(dataPin, clockPin); 178 | 179 | assertEqual(0x00, scale.get_mode()); 180 | scale.set_medavg_mode(); 181 | assertEqual(0x02, scale.get_mode()); 182 | scale.set_median_mode(); 183 | assertEqual(0x01, scale.get_mode()); 184 | scale.set_average_mode(); 185 | assertEqual(0x00, scale.get_mode()); 186 | } 187 | 188 | 189 | unittest_main() 190 | 191 | 192 | // -- END OF FILE -- 193 | 194 | -------------------------------------------------------------------------------- /HX711.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | // 3 | // FILE: HX711.h 4 | // AUTHOR: Rob Tillaart 5 | // VERSION: 0.6.3 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_LIB_VERSION (F("0.6.3")) 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 | 39 | class HX711 40 | { 41 | public: 42 | HX711(); 43 | ~HX711(); 44 | 45 | // fixed gain 128 for now 46 | void begin(uint8_t dataPin, uint8_t clockPin, 47 | bool fastProcessor = false, 48 | bool doReset = true); 49 | 50 | void reset(); 51 | 52 | // checks if load cell is ready to read. 53 | // use this to prevent blocking reads, esp at startup, 1st 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, is blocking until device is ready to read(). 70 | // this blocking period can be long up to 400 ms in first read() call. 71 | float read(); 72 | 73 | // get average of multiple raw reads 74 | // times = 1 or more 75 | float read_average(uint8_t times = 10); 76 | 77 | // get median of multiple raw reads 78 | // times = 3..15 - odd numbers preferred 79 | float read_median(uint8_t times = 7); 80 | 81 | // get average of "middle half" of multiple raw reads. 82 | // times = 3..15 - odd numbers preferred 83 | float read_medavg(uint8_t times = 7); 84 | 85 | // get running average over times measurements. 86 | // the weight alpha can be set to any value between 0 and 1 87 | // times = 1 or more. 88 | float read_runavg(uint8_t times = 7, float alpha = 0.5); 89 | 90 | 91 | /////////////////////////////////////////////////////////////// 92 | // 93 | // MODE 94 | // 95 | // get set mode for get_value() and indirect get_units(). 96 | // in median and medavg mode only 3..15 samples are allowed. 97 | void set_raw_mode(); 98 | void set_average_mode(); 99 | void set_median_mode(); 100 | void set_medavg_mode(); 101 | // set_run_avg will use a default alpha of 0.5. 102 | void set_runavg_mode(); 103 | uint8_t get_mode(); 104 | 105 | // corrected for offset. 106 | // in HX711_RAW_MODE the parameter times will be ignored. 107 | float get_value(uint8_t times = 1); 108 | // converted to proper units, corrected for scale. 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 | // TARE 136 | // call tare to calibrate zero 137 | void tare(uint8_t times = 10); 138 | float get_tare(); 139 | bool tare_set(); 140 | 141 | 142 | /////////////////////////////////////////////////////////////// 143 | // 144 | // CALIBRATION 145 | // 146 | // SCALE > 0 147 | // returns false if scale == 0; 148 | bool set_scale(float scale = 1.0); 149 | float get_scale(); 150 | 151 | // OFFSET > 0 152 | void set_offset(int32_t offset = 0); 153 | int32_t get_offset(); 154 | 155 | // clear the scale 156 | // call tare() to set the zero offset 157 | // put a known weight on the scale 158 | // call calibrate_scale(weight) 159 | // scale is calculated. 160 | void calibrate_scale(float weight, uint8_t times = 10); 161 | 162 | 163 | /////////////////////////////////////////////////////////////// 164 | // 165 | // POWER MANAGEMENT 166 | // 167 | void power_down(); 168 | void power_up(); 169 | 170 | 171 | /////////////////////////////////////////////////////////////// 172 | // 173 | // EXPERIMENTAL 174 | // RATE PIN - works only if rate pin is exposed. 175 | // 176 | void set_rate_pin(uint8_t pin); 177 | void set_rate_10SPS(); 178 | void set_rate_80SPS(); 179 | uint8_t get_rate(); 180 | 181 | 182 | // TIME OF LAST READ 183 | uint32_t last_time_read(); 184 | // obsolete in the future (0.7.0) 185 | [[deprecated("Use last_time_read() instead.")]] 186 | uint32_t last_read() { return last_time_read(); }; 187 | 188 | 189 | // PRICING 190 | float get_price(uint8_t times = 1) { return get_units(times) * _price; }; 191 | void set_unit_price(float price = 1.0) { _price = price; }; 192 | float get_unit_price() { return _price; }; 193 | 194 | 195 | private: 196 | uint8_t _dataPin; 197 | uint8_t _clockPin; 198 | 199 | int32_t _offset; 200 | float _scale; 201 | uint8_t _gain; 202 | uint32_t _lastTimeRead; 203 | uint8_t _mode; 204 | bool _fastProcessor; 205 | uint8_t _ratePin = 255; 206 | uint8_t _rate = 10; 207 | float _price; 208 | 209 | void _insertSort(float * array, uint8_t size); 210 | uint8_t _shiftIn(); 211 | }; 212 | 213 | 214 | // -- END OF FILE -- 215 | 216 | -------------------------------------------------------------------------------- /HX711.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: HX711.cpp 3 | // AUTHOR: Rob Tillaart 4 | // VERSION: 0.6.3 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.h" 11 | 12 | 13 | HX711::HX711() 14 | { 15 | _offset = 0; 16 | _scale = 1; 17 | _gain = HX711_CHANNEL_A_GAIN_128; 18 | _lastTimeRead = 0; 19 | _mode = HX711_AVERAGE_MODE; 20 | _fastProcessor = false; 21 | _price = 0; 22 | } 23 | 24 | 25 | HX711::~HX711() 26 | { 27 | } 28 | 29 | 30 | void HX711::begin(uint8_t dataPin, uint8_t clockPin, bool fastProcessor, bool doReset) 31 | { 32 | _dataPin = dataPin; 33 | _clockPin = clockPin; 34 | _fastProcessor = fastProcessor; 35 | 36 | pinMode(_dataPin, INPUT_PULLUP); 37 | pinMode(_clockPin, OUTPUT); 38 | digitalWrite(_clockPin, LOW); 39 | 40 | if (doReset) 41 | { 42 | reset(); 43 | } 44 | } 45 | 46 | 47 | void HX711::reset() 48 | { 49 | power_down(); 50 | power_up(); 51 | _offset = 0; 52 | _scale = 1; 53 | _gain = HX711_CHANNEL_A_GAIN_128; 54 | _lastTimeRead = 0; 55 | _mode = HX711_AVERAGE_MODE; 56 | _price = 0; 57 | } 58 | 59 | 60 | bool HX711::is_ready() 61 | { 62 | return digitalRead(_dataPin) == LOW; 63 | } 64 | 65 | 66 | void HX711::wait_ready(uint32_t ms) 67 | { 68 | while (!is_ready()) 69 | { 70 | delay(ms); 71 | } 72 | } 73 | 74 | 75 | bool HX711::wait_ready_retry(uint8_t retries, uint32_t ms) 76 | { 77 | while (retries--) 78 | { 79 | if (is_ready()) return true; 80 | delay(ms); 81 | } 82 | return false; 83 | } 84 | 85 | 86 | bool HX711::wait_ready_timeout(uint32_t timeout, uint32_t ms) 87 | { 88 | uint32_t start = millis(); 89 | while (millis() - start < timeout) 90 | { 91 | if (is_ready()) return true; 92 | delay(ms); 93 | } 94 | return false; 95 | } 96 | 97 | 98 | /////////////////////////////////////////////////////////////// 99 | // 100 | // READ 101 | // 102 | // From datasheet page 4 103 | // When output data is not ready for retrieval, 104 | // digital output pin DOUT is HIGH. 105 | // Serial clock input PD_SCK should be LOW. 106 | // When DOUT goes to LOW, it indicates data is ready for retrieval. 107 | // Blocking period can be long up to 400 ms in first read() call. 108 | float HX711::read() 109 | { 110 | // this BLOCKING wait takes most time... 111 | while (digitalRead(_dataPin) == HIGH) 112 | { 113 | yield(); 114 | } 115 | 116 | union 117 | { 118 | int32_t value = 0; 119 | uint8_t data[4]; 120 | } v; 121 | 122 | // blocking part ... 123 | noInterrupts(); 124 | 125 | // Pulse the clock pin 24 times to read the data. 126 | // v.data[2] = shiftIn(_dataPin, _clockPin, MSBFIRST); 127 | // v.data[1] = shiftIn(_dataPin, _clockPin, MSBFIRST); 128 | // v.data[0] = shiftIn(_dataPin, _clockPin, MSBFIRST); 129 | v.data[2] = _shiftIn(); 130 | v.data[1] = _shiftIn(); 131 | v.data[0] = _shiftIn(); 132 | 133 | // TABLE 3 page 4 datasheet 134 | // 135 | // CLOCK CHANNEL GAIN m 136 | // ------------------------------------ 137 | // 25 A 128 1 // default 138 | // 26 B 32 2 139 | // 27 A 64 3 140 | // 141 | // only default 128 verified, 142 | // selection goes through the set_gain(gain) 143 | // 144 | uint8_t m = 1; 145 | if (_gain == HX711_CHANNEL_A_GAIN_128) m = 1; 146 | else if (_gain == HX711_CHANNEL_A_GAIN_64) m = 3; 147 | else if (_gain == HX711_CHANNEL_B_GAIN_32) m = 2; 148 | 149 | while (m > 0) 150 | { 151 | // delayMicroSeconds(1) is needed for fast processors 152 | // T2 >= 0.2 us 153 | digitalWrite(_clockPin, HIGH); 154 | if (_fastProcessor) delayMicroseconds(1); 155 | digitalWrite(_clockPin, LOW); 156 | // keep duty cycle ~50% 157 | if (_fastProcessor) delayMicroseconds(1); 158 | m--; 159 | } 160 | 161 | interrupts(); 162 | // yield(); 163 | 164 | // SIGN extend 165 | if (v.data[2] & 0x80) v.data[3] = 0xFF; 166 | 167 | _lastTimeRead = millis(); 168 | return 1.0 * v.value; 169 | } 170 | 171 | 172 | float HX711::read_average(uint8_t times) 173 | { 174 | if (times < 1) times = 1; 175 | float sum = 0; 176 | for (uint8_t i = 0; i < times; i++) 177 | { 178 | sum += read(); 179 | yield(); 180 | } 181 | return sum / times; 182 | } 183 | 184 | 185 | float HX711::read_median(uint8_t times) 186 | { 187 | if (times > 15) times = 15; 188 | if (times < 3) times = 3; 189 | float samples[15]; 190 | for (uint8_t i = 0; i < times; i++) 191 | { 192 | samples[i] = read(); 193 | yield(); 194 | } 195 | _insertSort(samples, times); 196 | if (times & 0x01) return samples[times/2]; 197 | return (samples[times/2] + samples[times/2 + 1]) / 2; 198 | } 199 | 200 | 201 | float HX711::read_medavg(uint8_t times) 202 | { 203 | if (times > 15) times = 15; 204 | if (times < 3) times = 3; 205 | float samples[15]; 206 | for (uint8_t i = 0; i < times; i++) 207 | { 208 | samples[i] = read(); 209 | yield(); 210 | } 211 | _insertSort(samples, times); 212 | float sum = 0; 213 | // iterate over 1/4 to 3/4 of the array 214 | uint8_t count = 0; 215 | uint8_t first = (times + 2) / 4; 216 | uint8_t last = times - first - 1; 217 | for (uint8_t i = first; i <= last; i++) // !! include last one too 218 | { 219 | sum += samples[i]; 220 | count++; 221 | } 222 | return sum / count; 223 | } 224 | 225 | 226 | float HX711::read_runavg(uint8_t times, float alpha) 227 | { 228 | if (times < 1) times = 1; 229 | if (alpha < 0) alpha = 0; 230 | if (alpha > 1) alpha = 1; 231 | float val = read(); 232 | for (uint8_t i = 1; i < times; i++) 233 | { 234 | val += alpha * (read() - val); 235 | yield(); 236 | } 237 | return val; 238 | } 239 | 240 | 241 | /////////////////////////////////////////////////////// 242 | // 243 | // MODE 244 | // 245 | void HX711::set_raw_mode() 246 | { 247 | _mode = HX711_RAW_MODE; 248 | } 249 | 250 | 251 | void HX711::set_average_mode() 252 | { 253 | _mode = HX711_AVERAGE_MODE; 254 | } 255 | 256 | 257 | void HX711::set_median_mode() 258 | { 259 | _mode = HX711_MEDIAN_MODE; 260 | } 261 | 262 | 263 | void HX711::set_medavg_mode() 264 | { 265 | _mode = HX711_MEDAVG_MODE; 266 | } 267 | 268 | 269 | // set_run_avg will use a default alpha of 0.5. 270 | void HX711::set_runavg_mode() 271 | { 272 | _mode = HX711_RUNAVG_MODE; 273 | } 274 | 275 | 276 | uint8_t HX711::get_mode() 277 | { 278 | return _mode; 279 | } 280 | 281 | 282 | float HX711::get_value(uint8_t times) 283 | { 284 | float raw; 285 | switch(_mode) 286 | { 287 | case HX711_RAW_MODE: 288 | raw = read(); 289 | break; 290 | case HX711_RUNAVG_MODE: 291 | raw = read_runavg(times); 292 | break; 293 | case HX711_MEDAVG_MODE: 294 | raw = read_medavg(times); 295 | break; 296 | case HX711_MEDIAN_MODE: 297 | raw = read_median(times); 298 | break; 299 | case HX711_AVERAGE_MODE: 300 | default: 301 | raw = read_average(times); 302 | break; 303 | } 304 | return raw - _offset; 305 | } 306 | 307 | 308 | float HX711::get_units(uint8_t times) 309 | { 310 | float units = get_value(times) * _scale; 311 | return units; 312 | } 313 | 314 | 315 | /////////////////////////////////////////////////////////////// 316 | // 317 | // GAIN 318 | // 319 | // note: if parameter gain == 0xFF40 some compilers 320 | // will map that to 0x40 == HX711_CHANNEL_A_GAIN_64; 321 | // solution: use uint32_t or larger parameters everywhere. 322 | // note that changing gain/channel may take up to 400 ms (page 3) 323 | bool HX711::set_gain(uint8_t gain, bool forced) 324 | { 325 | if ( (not forced) && (_gain == gain)) return true; 326 | switch(gain) 327 | { 328 | case HX711_CHANNEL_B_GAIN_32: 329 | case HX711_CHANNEL_A_GAIN_64: 330 | case HX711_CHANNEL_A_GAIN_128: 331 | _gain = gain; 332 | read(); // next user read() is from right channel / gain 333 | return true; 334 | } 335 | return false; // unchanged, but incorrect value. 336 | } 337 | 338 | 339 | uint8_t HX711::get_gain() 340 | { 341 | return _gain; 342 | } 343 | 344 | 345 | /////////////////////////////////////////////////////// 346 | // 347 | // TARE 348 | // 349 | void HX711::tare(uint8_t times) 350 | { 351 | _offset = read_average(times); 352 | } 353 | 354 | 355 | float HX711::get_tare() 356 | { 357 | return -_offset * _scale; 358 | } 359 | 360 | 361 | bool HX711::tare_set() 362 | { 363 | return _offset != 0; 364 | } 365 | 366 | 367 | /////////////////////////////////////////////////////////////// 368 | // 369 | // CALIBRATION (tare see above) 370 | // 371 | bool HX711::set_scale(float scale) 372 | { 373 | if (scale == 0) return false; 374 | _scale = 1.0 / scale; 375 | return true; 376 | } 377 | 378 | 379 | float HX711::get_scale() 380 | { 381 | return 1.0 / _scale; 382 | } 383 | 384 | 385 | void HX711::set_offset(int32_t offset) 386 | { 387 | _offset = offset; 388 | } 389 | 390 | 391 | int32_t HX711::get_offset() 392 | { 393 | return _offset; 394 | } 395 | 396 | 397 | // assumes tare() has been set. 398 | void HX711::calibrate_scale(float weight, uint8_t times) 399 | { 400 | _scale = weight / (read_average(times) - _offset); 401 | } 402 | 403 | 404 | /////////////////////////////////////////////////////////////// 405 | // 406 | // POWER MANAGEMENT 407 | // 408 | void HX711::power_down() 409 | { 410 | // at least 60 us HIGH 411 | digitalWrite(_clockPin, HIGH); 412 | delayMicroseconds(64); 413 | } 414 | 415 | 416 | void HX711::power_up() 417 | { 418 | digitalWrite(_clockPin, LOW); 419 | } 420 | 421 | 422 | /////////////////////////////////////////////////////////////// 423 | // 424 | // RATE PIN - works only if rate pin is exposed. 425 | // 426 | void HX711::set_rate_pin(uint8_t pin) 427 | { 428 | _ratePin = pin; 429 | pinMode(_ratePin, OUTPUT); 430 | set_rate_10SPS(); 431 | } 432 | 433 | void HX711::set_rate_10SPS() 434 | { 435 | _rate = 10; 436 | digitalWrite(_ratePin, LOW); 437 | } 438 | 439 | void HX711::set_rate_80SPS() 440 | { 441 | _rate = 80; 442 | digitalWrite(_ratePin, HIGH); 443 | } 444 | 445 | uint8_t HX711::get_rate() 446 | { 447 | return _rate; 448 | } 449 | 450 | 451 | /////////////////////////////////////////////////////////////// 452 | // 453 | // MISC 454 | // 455 | uint32_t HX711::last_time_read() 456 | { 457 | return _lastTimeRead; 458 | } 459 | 460 | 461 | /////////////////////////////////////////////////////////////// 462 | // 463 | // PRIVATE 464 | // 465 | void HX711::_insertSort(float * array, uint8_t size) 466 | { 467 | uint8_t t, z; 468 | float temp; 469 | for (t = 1; t < size; t++) 470 | { 471 | z = t; 472 | temp = array[z]; 473 | while( (z > 0) && (temp < array[z - 1] )) 474 | { 475 | array[z] = array[z - 1]; 476 | z--; 477 | } 478 | array[z] = temp; 479 | yield(); 480 | } 481 | } 482 | 483 | 484 | // MSB_FIRST optimized shiftIn 485 | // see datasheet page 5 for timing 486 | uint8_t HX711::_shiftIn() 487 | { 488 | // local variables are faster. 489 | uint8_t clk = _clockPin; 490 | uint8_t data = _dataPin; 491 | uint8_t value = 0; 492 | uint8_t mask = 0x80; 493 | while (mask > 0) 494 | { 495 | digitalWrite(clk, HIGH); 496 | // T2 >= 0.2 us 497 | if(_fastProcessor) delayMicroseconds(1); 498 | if (digitalRead(data) == HIGH) 499 | { 500 | value |= mask; 501 | } 502 | digitalWrite(clk, LOW); 503 | // keep duty cycle ~50% 504 | if(_fastProcessor) delayMicroseconds(1); 505 | mask >>= 1; 506 | } 507 | return value; 508 | } 509 | 510 | 511 | // -- END OF FILE -- 512 | 513 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [![Arduino CI](https://github.com/RobTillaart/HX711/workflows/Arduino%20CI/badge.svg)](https://github.com/marketplace/actions/arduino_ci) 3 | [![Arduino-lint](https://github.com/RobTillaart/HX711/actions/workflows/arduino-lint.yml/badge.svg)](https://github.com/RobTillaart/HX711/actions/workflows/arduino-lint.yml) 4 | [![JSON check](https://github.com/RobTillaart/HX711/actions/workflows/jsoncheck.yml/badge.svg)](https://github.com/RobTillaart/HX711/actions/workflows/jsoncheck.yml) 5 | [![GitHub issues](https://img.shields.io/github/issues/RobTillaart/HX711.svg)](https://github.com/RobTillaart/HX711/issues) 6 | 7 | [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/RobTillaart/HX711/blob/master/LICENSE) 8 | [![GitHub release](https://img.shields.io/github/release/RobTillaart/HX711.svg?maxAge=3600)](https://github.com/RobTillaart/HX711/releases) 9 | [![PlatformIO Registry](https://badges.registry.platformio.org/packages/robtillaart/library/HX711.svg)](https://registry.platformio.org/libraries/robtillaart/HX711) 10 | 11 | 12 | # HX711 13 | 14 | Arduino library for HX711 24 bit ADC used for load cells and scales. 15 | 16 | 17 | ## Description 18 | 19 | This HX711 library has a public interface (API) which is a superset of the 20 | public interface of the HX711 library, version 0.7.3, by [Bogde](https://github.com/bogde/HX711). 21 | The API is reused to be compatible on the basic interaction level, so credits 22 | for the reused API go to Bogde. 23 | 24 | The implementation of both libraries differ from the beginning. 25 | The HX711 by Bogde is in several ways more sophisticated than this HX711 library. 26 | In this HX711 library, missing functions, modes and more were added to get more 27 | info from the library. 28 | So you can choose the library that fit your needs most. 29 | 30 | Another important difference is that this HX711 library uses floats. 31 | The 23 bits mantissa of the IEEE754 float matches the 24 bit ADC very well. 32 | Furthermore, using floats gave a smaller footprint on the Arduino UNO R3. 33 | 34 | Note that the 24 bits of the HX711 contains some noise so depending on setup, 35 | load etc. only 16 to 20 of the bits are expected significant in practice. 36 | This translates roughly to 4 or max 5 significant digits in a single measurement 37 | That's why it is advised to average (median) multiple measurements to reduce the noise. 38 | 39 | 40 | ### Breaking change 0.4.0 41 | 42 | The **begin()** function has a new parameter **bool fastProcessor** which is default false. 43 | It is used to slow down the internal **shiftIn()** to keep clock pulses within the 44 | specification. For most processors the internal code is "slow enough". 45 | If the processor can set an IO pin faster than 0.4 us (spec datasheet == 0.2 us), 46 | the parameter **fastProcessor** must be set to true. 47 | 48 | 49 | ### Breaking change 0.3.0 50 | 51 | In issue #11 it became clear that the timing of the default **shiftIn()** function to 52 | read the value of the internal ADC was too fast on some processor boards for the HX711. 53 | This resulted in missing the first (= sign) bit or the value read could be a factor two 54 | higher than it should. If one calibrated the sensor this would be compensated with the 55 | factor that is derived in the calibration process. 56 | 57 | In 0.3.0 a dedicated **shiftIn()** function is added into this library that uses hard 58 | coded delayMicroseconds to keep the timing of the clock within HX711 datasheet parameters. 59 | This should guarantee that the sign bit is always read correctly on all platforms. 60 | Drawback is that reading the HX711 takes an extra 50-55 microseconds. 61 | How much this affects performance is to be investigated. 62 | 63 | 64 | ### HX711_MP 65 | 66 | - https://github.com/RobTillaart/HX711_MP 67 | 68 | The library HX711_MP is derived from this HX711 library version 0.3.5. 69 | 70 | The HX711_MP is not compatible, see readme.md of HX711_MP 71 | 72 | The main difference is that the HX711_MP uses a multi-point calibration 73 | which replaces the "offset + scale" concept. 74 | Functions related to this concept are removed in HX711_MP and replaced by 75 | its own set of calibration functions. 76 | 77 | This multi-point calibration allows to compensate for non-linear behaviour 78 | in the sensor readings. 79 | 80 | 81 | ### 10 or 80 SPS 82 | 83 | The datasheet mentions that the HX711 can run at 80 samples per second (SPS). 84 | To select this mode connect the **RATE** pin(15) of the chip to VCC (HIGH). 85 | Connecting **RATE** to GND (LOW) gives 10 SPS. 86 | 87 | Having the RATE set to 10 or 80 SPS also changes the time to start up. 88 | At 10 SPS it takes 400 milliseconds, at 80 SPS it takes 50 milliseconds. 89 | 90 | All breakout boards I tested have **RATE** connected to GND and offer no 91 | pin to control this from the outside. 92 | Adafruit however has a breakout board with **RATE** exposed. 93 | See https://www.adafruit.com/product/5974 94 | There might be more. 95 | 96 | If you have the schema of your board you should be able to expose the **RATE** 97 | pin, e.g. by removing the pull down resistor to GND. 98 | 99 | This library provide experimental means to control the **RATE**, see below. 100 | 101 | If you need more SPS you could consider using the HX71708 device. 102 | This is a close "relative" of the HX711 that allows to set the SPS to 103 | 10, 20, 80, or 320 Hz. 104 | - https://github.com/beniseman/HX71708 105 | 106 | 107 | ### Related 108 | 109 | - https://github.com/bogde/HX711 110 | - https://github.com/RobTillaart/weight (conversions kg <> stone etc.) 111 | - https://github.com/RobTillaart/HX710AB 112 | - https://github.com/RobTillaart/HX711 113 | - https://github.com/RobTillaart/HX711_MP multipoint calibration version. 114 | 115 | Discussion about resolution of the ADC 116 | - https://forum.arduino.cc/t/scale-from-50-kg-to-5000kg-what-adc/1139710 117 | 118 | Support for the HX71708 device (close related) 119 | - https://github.com/beniseman/HX71708 allows to set the SPS to 10, 20, 80, or 320 Hz 120 | 121 | Load cells go to very high weights, this side sells them up to 200 ton. 122 | Never seen one and cannot tell if it will work with this library. 123 | - https://stekon.nl/load-cells 124 | 125 | Breakout with RATE exposed by ADAfruit 126 | - https://www.adafruit.com/product/5974 127 | 128 | 129 | ### Faulty boards 130 | 131 | - https://forum.arduino.cc/t/load-cell-amplifier-hx711-wrong-ground/1046075 132 | 133 | 134 | ## Main flow 135 | 136 | First action is to call **begin(dataPin, clockPin)** to make connection to the **HX711**. 137 | 138 | Second step is to check **isReady()** to wait until the device is ready for measurements. 139 | 140 | Next step is calibration for which a number of functions exist. 141 | - **tare()** measures the offset of the zero point. 142 | - **set_scale(factor)** set a known conversion factor e.g. from EEPROM. 143 | - **calibrate_scale(weight, times)** determines the scale factor based upon a known weight e.g. 1 Kg. 144 | The weight is typical in grams, however any unit can be used, depending on the 145 | load cell used. 146 | 147 | Steps to take for calibration 148 | 1. clear the scale. 149 | 1. wait until **isReady()** returns true. 150 | 1. call **tare()** to determine and set the zero weight offset. 151 | 1. put a known weight on the scale. 152 | 1. call **calibrate_scale(float weight)**, weight typical in grams, however any unit can be used. 153 | 1. scale factor is calculated. 154 | 1. save the offset and scale for later use e.g. EEPROM. 155 | 156 | Note that the units used in **calibrate_scale()** will be returned by **get_units()**. 157 | 158 | 159 | ## Interface 160 | 161 | ```cpp 162 | #include "HX711.h" 163 | ``` 164 | 165 | ### Constructor 166 | 167 | - **HX711()** constructor. 168 | - **~HX711()** destructor. 169 | - **void begin(uint8_t dataPin, uint8_t clockPin, bool fastProcessor = false, bool doReset = true)** sets a fixed gain 128 for now. 170 | - The parameter fastProcessor adds a 1 uS delay for each clock half-cycle to keep the time greater than 200 nS. 171 | - The parameter doReset is experimental in 0.6.3. 172 | It defaults to true (== backwards compatible) causing a call to reset(), taking extra time 173 | before the device is ready to make new measurements. See reset() below. 174 | Note that not calling reset() leaves the ADC in the previous or even an undefined state, 175 | so use with care. (needs testing) 176 | - **void reset()** set internal state to the start condition. 177 | Reset() also does a power_down() / power_up() cycle. 178 | This cycle adds a delay of 400 (RATE = 10 SPS) or 50 (RATE = 80 SPS) milliseconds. 179 | 180 | 181 | ### isReady 182 | 183 | There are different ways to wait for a new measurement. 184 | 185 | - **bool is_ready()** checks if load cell is ready to read. 186 | - **void wait_ready(uint32_t ms = 0)** wait until ready, check every ms. 187 | - **bool wait_ready_retry(uint8_t retries = 3, uint32_t ms = 0)** wait max retries. 188 | - **bool wait_ready_timeout(uint32_t timeout = 1000, uint32_t ms = 0)** wait max timeout milliseconds. 189 | 190 | 191 | ### Read 192 | 193 | Warning: the read calls are blocking calls, which can take up to 400 ms in the first read() call. 194 | Best practice is to check with isReady() before calling read(). 195 | 196 | - **float read()** get a raw read. 197 | - **float read_average(uint8_t times = 10)** get average of times raw reads. times = 1 or more. 198 | - **float read_median(uint8_t times = 7)** get median of multiple raw reads. 199 | times = 3..15 - odd numbers preferred. 200 | - **float read_medavg(uint8_t times = 7)** get average of "middle half" of multiple raw reads. 201 | times = 3..15 - odd numbers preferred. 202 | - **float read_runavg(uint8_t times = 7, float alpha = 0.5)** get running average over times measurements. 203 | The weight alpha can be set to any value between 0 and 1, times >= 1. 204 | - **uint32_t last_read()** returns timestamp in milliseconds of last read. 205 | 206 | 207 | ### Gain + channel 208 | 209 | Use with care as it is not 100% reliable - see issue #27. (solutions welcome). 210 | 211 | Read datasheet before use. 212 | 213 | Constants (see .h file) 214 | 215 | - **HX711_CHANNEL_A_GAIN_128 = 128** This is the default in the constructor. 216 | - **HX711_CHANNEL_A_GAIN_64 = 64** 217 | - **HX711_CHANNEL_B_GAIN_32 = 32** Note fixed gain for channel B. 218 | 219 | The selection of channels + gain is in theory straightforward. 220 | 221 | - **bool set_gain(uint8_t gain = 128, bool forced = false)** values: 128 (default), 64 or 32. 222 | If one uses an invalid value for the parameter gain, the channel and gain are not changed. 223 | If forced == false it will not set the new gain if the library "thinks" it 224 | already has the right value. 225 | If forced == true, it will explicitly try to set the gain/channel again. 226 | This includes a dummy **read()** so the next "user" **read()** will give the right info. 227 | - **uint8_t get_gain()** returns set gain (128, 64 or 32). 228 | 229 | By setting the gain to one of the three constants the gain and the channel is selected. 230 | The **set_gain()** does a dummy read if gain has changed (or forced == true) so the 231 | next call to **read()** will return info from the selected channel/gain. 232 | 233 | According to the datasheet the gain/channel change may take up to 400ms (table page 3). 234 | 235 | Warning 1: if you use **set_gain()** in your program the HX711 can be in different states. 236 | If there is an expected or unexpected reboot of the MCU, this could lead 237 | to an unknown state at the reboot of the code. 238 | So in such case it is strongly advised to call **set_gain()** explicitly in **setup()** 239 | so the device is in a known state. 240 | 241 | Warning 2: In practice it seems harder to get the channel and gain selection as reliable 242 | as the datasheet states it should be. So use with care. (feedback welcome) 243 | See discussion #27 HX711. 244 | 245 | 246 | ### Read mode 247 | 248 | Get and set the operational mode for **get_value()** and indirect **get_units()**. 249 | 250 | Constants (see .h file) 251 | 252 | - **HX711_RAW_MODE** 253 | - **HX711_AVERAGE_MODE** 254 | - **HX711_MEDIAN_MODE** 255 | - **HX711_MEDAVG_MODE** 256 | - **HX711_RUNAVG_MODE** 257 | 258 | 259 | In **HX711_MEDIAN_MODE** and **HX711_MEDAVG_MODE** mode only 3..15 samples are allowed 260 | to keep memory footprint relative low. 261 | 262 | - **void set_raw_mode()** will cause **read()** to be called only once! 263 | - **void set_average_mode()** take the average of n measurements. 264 | - **void set_median_mode()** take the median of n measurements. 265 | - **void set_medavg_mode()** take the average of n/2 median measurements. 266 | - **void set_runavg_mode()** default alpha = 0.5. 267 | - **uint8_t get_mode()** returns current set mode. Default is **HX711_AVERAGE_MODE**. 268 | 269 | 270 | ### Get values 271 | 272 | Get values from the HX711 corrected for offset and scale. 273 | Note that in **HX711_RAW_MODE** the times parameter will be ignored => just call **read()** once. 274 | 275 | - **float get_value(uint8_t times = 1)** read value, corrected for offset. 276 | - **float get_units(uint8_t times = 1)** read value, converted to proper units. 277 | - **bool set_scale(float scale = 1.0)** set scale factor which is normally a positive 278 | number larger than 50. Depends on load-cell used. 279 | Returns false if scale == 0. 280 | Note that for some specific applications, scale might be negative. 281 | - **float get_scale()** returns set scale factor. 282 | - **void set_offset(int32_t offset = 0)** idem. 283 | - **int32_t get_offset()** idem. 284 | 285 | 286 | ### Tare & calibration I 287 | 288 | Steps to take for calibration 289 | 1. clear the scale. 290 | 1. call **tare()** to determine and set the zero weight offset. 291 | 1. put a known weight on the scale. 292 | 1. call **calibrate_scale(float weight)**, weight typical in grams, however any unit can be used. 293 | 1. scale factor is calculated. 294 | 1. save the offset and scale for later use e.g. EEPROM. 295 | 296 | Note that the units used in **calibrate_scale()** will be returned by **get_units()**. 297 | 298 | - **void tare(uint8_t times = 10)** call tare to determine the offset 299 | to calibrate the zero (reference) level. See below. 300 | - **float get_tare()** returns the offset \* scale. 301 | Note this differs after calls to **calibrate_scale()**. 302 | Use **get_offset()** to get only the offset. 303 | - **bool tare_set()** checks if a tare has been set. 304 | Assumes offset is not zero, which is true for all load cells tested. 305 | - **void calibrate_scale(float weight, uint8_t times = 10)** 306 | The calibration weight averages times measurements to improve accuracy. 307 | Weight is typical in grams, however any unit can be used. 308 | Be aware this unit will also be returned by **get_units()**. 309 | 310 | Since 0.6.0 the weight is defined as float which allows easier calibration in 311 | other units e.g. define the weight as 2.5 kg instead of 2500 gram. 312 | The function **GetUnits()** will then return its value in kg too. 313 | 314 | Also by using a float the range of calibration weights is substantially increased. 315 | One can now define 250 gram as 250000 milligram, where before the value was max 316 | 65535 units (theoretical increase of precision from 4.8 to 6.9 digits). 317 | This allows the calibration of superheavy load cells, e.g 500 kg and use a 318 | defined weight of 100000 gram. 319 | Finally the use of floats allow the use of decimals e.g. a calibration weight 320 | of 125.014 kg or 306.4 gram. 321 | 322 | Note: calibrate_scale() uses averaging and does not use the mode set. 323 | 324 | Note: calibrate_scale() can have a negative value as weight e.g. force of a balloon. 325 | 326 | 327 | ### Tare & calibration II 328 | 329 | A load cell + HX711 module without weight gives a raw value, mostly not equal to zero. 330 | The function **get_tare()** is used to measure this raw value and allows the user 331 | to define this value as a zero weight (force) point. 332 | This zero point is normally without any load, however it is possible to define 333 | a zero point with a "fixed" load e.g. a cup, a dish, even a spring or whatever. 334 | This allows the system to automatically subtract the weight / force of the cup etc. 335 | 336 | **Warning**: The user must be aware that the "fixed" load together with the 337 | "variable" load does not exceed the specifications of the load cell. 338 | 339 | E.g. a load cell which can handle 1000 grams with a cup of 300 grams should not 340 | be calibrated with a weight of more than 700 grams. 341 | In fact it is better to calibrate with a weight in the order of 80 to 90% of 342 | the maximum load so in this example a weight of 500 to 600 grams. 343 | That would make the total 800-900 grams == 80/90% of the max load. 344 | 345 | Another point to consider when calibrating is to use a weight that is 346 | in the range you want to make your measurements. 347 | E.g. if you want to measure coffee beans in portions of 250 grams, use 348 | a weight in the range 200-300 grams. Could just save an extra bit. 349 | 350 | Furthermore it is also important to do the calibration at the temperature you 351 | expect to do the weight measurements. See temperature section below. 352 | 353 | 354 | ### Inner formula 355 | 356 | Weight = **get_scale()** x raw + **get_tare()**. 357 | 358 | With the two parameters one can interpolate the inner formula. 359 | This can be used e.g to make an ideal graph of the conversion. 360 | This can be compared with actual values to get an indication 361 | of the accuracy of the load cell. 362 | 363 | 364 | ### Power management 365 | 366 | - **void power_down()** idem. Explicitly blocks for 64 microseconds. 367 | (See Page 5 datasheet). 368 | - **void power_up()** wakes up the HX711. 369 | It should reset the HX711 to defaults but this is not always seen. 370 | See discussion issue #27 GitHub. Needs more testing. 371 | 372 | Note: Having the RATE set to 10 or 80 SPS changes the time to start up. 373 | At 10 SPS it takes 400 milliseconds, at 80 SPS it takes 50 milliseconds. 374 | (See datasheet, Output settling time on page 3) 375 | 376 | 377 | ### Rate 378 | 379 | **Experimental** 380 | 381 | See section "10 or 80 SPS" above. 382 | 383 | Note this only works if the **RATE** pin is exposed and connected to 384 | the IO pin configured in set_rate_pin(). 385 | If not configured the other functions won't work. 386 | 387 | - **void set_rate_pin(uint8_t pin)** sets the IO pin for SPS selection. 388 | - **void set_rate_10SPS()** sets rate to 10 SPS. 389 | - **void set_rate_80SPS()** sets rate to 80 SPS. 390 | - **uint8_t get_rate()** returns 10 d(default) or 80. 391 | 392 | 393 | ### Pricing 394 | 395 | Some price functions were added to make it easy to use this library 396 | for pricing goods or for educational purposes. 397 | These functions are under discussion if they will stay in the library. 398 | For weight conversion functions see https://github.com/RobTillaart/weight 399 | 400 | - **float get_price(uint8_t times = 1)** idem. 401 | - **void set_unit_price(float price = 1.0)** idem. 402 | - **float get_unit_price()** idem. 403 | 404 | 405 | ### Millivolts 406 | 407 | **Experimental** 408 | 409 | To be verified in a test setup. 410 | 411 | In issue #53, a question was asked to convert the input of the HX711 to millivolts. 412 | Thinking about this question resulted in a simple and elegant idea: 413 | 414 | - Apply 0.000 mV to the system. 415 | - Call **tare(times)** to calibrate the zero point. 416 | - Then apply 20.000 mV to the system. 417 | - Call **calibrate_scale(20000)** to map the raw reading to 20000 µV = 20 mV. 418 | 419 | Assuming the scale is linear, the HX711 now works like a millivolt meter. 420 | And the **float get_units(uint8_t times = 1)** will return microvolts. 421 | 422 | In fact, one could map any linear unit this way, e.g. if the voltage applied 423 | is linear with temperature, humidity or wind speed one can map this directly. 424 | 425 | 426 | ## Notes 427 | 428 | 429 | ### Scale values for load cells 430 | 431 | These scale values worked pretty well with a set of load cells I have, 432 | Use calibrate to find your favourite values. 433 | 434 | - 5 KG load cell scale.set_scale(420.52); 435 | - 20 KG load cell scale.set_scale(127.15); 436 | 437 | 438 | ### Connections HX711 439 | 440 | - A+/A- uses gain of 128 or 64 441 | - B+/B- uses gain of 32 442 | 443 | Colour scheme wires of two devices. 444 | 445 | | HX711 Pin | Colour dev 1 | Colour dev 2 | 446 | |:-----------:|:---------------:|:---------------:| 447 | | E+ | red | red | 448 | | E- | black | black | 449 | | A- | white | blue | 450 | | A+ | green | white | 451 | | B- | not connected | not connected | 452 | | B+ | not connected | not connected | 453 | 454 | 455 | ### Temperature 456 | 457 | Load cells do have a temperature related error. (see datasheet load cell) 458 | This can be reduced by doing the calibration and take the tare 459 | at the operational temperature one uses for the measurements. 460 | 461 | Another way to handle this is to add a good temperature sensor 462 | (e.g. DS18B20, SHT85) and compensate for the temperature 463 | differences in your code. 464 | 465 | 466 | ## Multiple HX711 467 | 468 | 469 | ### Separate lines 470 | 471 | Simplest way to control multiple HX711's is to have a separate **DOUT** and **CLK** 472 | line for every HX711 connected. 473 | 474 | 475 | ### Multiplexer 476 | 477 | Alternative one could use a multiplexer like the https://github.com/RobTillaart/HC4052 478 | or possibly an https://github.com/RobTillaart/TCA9548. 479 | Although to control the multiplexer one need some extra lines and code. 480 | 481 | 482 | ### Share CLOCK line 483 | 484 | See **HX_loadcell_array.ino** 485 | 486 | Another way to control multiple HX711's is to share the **CLK** line. 487 | This has a few side effects which might be acceptable or not. 488 | 489 | Known side effects - page 4 and 5 datasheet. 490 | 491 | - The **CLK** is used to select channel and to select gain for the NEXT sample. 492 | - The **CLK** is used for power down. 493 | - After wake up after power down all HX711's will reset to channel A and gain 128. 494 | **WARNING:** if one of the objects does a **powerDown()** or **reset()** it resets its internal states. 495 | The other objects however won't reset their internal state, so a mismatch can occur. 496 | 497 | So in short, sharing the **CLK** line causes all HX711 modules share the same state. 498 | This can introduce extra complexity if one uses mixed gains or channels. 499 | If all HX711's use the same settings it should work, however extra care is needed for 500 | **powerDown()** and **reset()**. 501 | 502 | **WARNING: Sharing the data lines is NOT possible as it could cause short circuit.** 503 | 504 | See https://github.com/RobTillaart/HX711/issues/40 505 | 506 | 507 | ## Future 508 | 509 | 510 | #### Must 511 | 512 | - update documentation 513 | - keep in sync with HX711_MP, HX710AB library. 514 | 515 | #### Should 516 | 517 | - test 518 | - different load cells. 519 | - B channel explicitly. 520 | - test reset and reboot behaviours. 521 | - test and verify the proper working of the rate functions. 522 | - investigate read() 523 | - investigate the need of yield after interrupts 524 | - investigate blocking loop at begin => less yield() calls ? 525 | - add performance figures 526 | 527 | #### Could 528 | 529 | - add error handling? 530 | - optimize fastProcessor code (possible better performance) 531 | - injecting 2 micro delays is not always needed. 532 | - int flag instead of bool. 533 | - make enum of the MODE's 534 | - add examples 535 | - example the adding scale 536 | - void weight_clr(), void weight_add(), float weight_get() - adding scale 537 | - example for using rate functions. 538 | - investigate temperature compensation. 539 | - decide pricing keep/not => move to .cpp 540 | 541 | #### Wont 542 | 543 | - why store the gain as \_gain while the iterations m = 1..3 is used most 544 | - read() less code 545 | - **changes from explanatory code to vague** 546 | - very small performance gain. 547 | - code moves to both get/set_gain() so footprint might rise. 548 | 549 | 550 | ## Support 551 | 552 | If you appreciate my libraries, you can support the development and maintenance. 553 | Improve the quality of the libraries by providing issues and Pull Requests, or 554 | donate through PayPal or GitHub sponsors. 555 | 556 | Thank you, 557 | 558 | --------------------------------------------------------------------------------