diff --git a/libraries/HX711/CHANGELOG..md b/libraries/HX711/CHANGELOG..md new file mode 100644 index 0000000..a7e6b5d --- /dev/null +++ b/libraries/HX711/CHANGELOG..md @@ -0,0 +1,150 @@ +# Change Log HX711 +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/) +and this project adheres to [Semantic Versioning](http://semver.org/). + + +## [0.6.3] - 2025-09-16 +- fix #70, HX711 rate pin code +- fix #70, add a doReset parameter to begin() to improve start up time. +- update readme.md + - add details about start up time (related to RATE). + - add **isReady()** check in calibration + - add reference to ADAfruit breakout with RATE +- minor edits + +## [0.6.2] - 2025-09-06 +- fix #68, add bogde to the license, to give credit for the API +- implement experimental rate support. +- update keywords.txt +- update readme.md +- update the GitHub actions +- minor edits + +## [0.6.1] - 2025-06-19 +- fix #65, is_ready() => set dataPin to INPUT_PULLUP +- minor edits + +## [0.6.0] - 2025-04-10 +- fix #60, change parameter **void calibrate_scale(float weight, uint8_t times = 10)** +- update readme.md + +---- + +## [0.5.2] - 2024-11-18 +- fix #56, update readme.md +- add parameter default for **fastProcessor** +- add **last_time_read()** to replace **last_read()** in future. +- minor edits + + +## [0.5.1] - 2024-11-08 +- fix #54, calibration sketch should output int32_t for offset. +- update examples +- update readme.md +- minor edits + +## [0.5.0] - 2024-06-17 +- fix #50, bug in constructor (thanks Mathieu!) + - refactor constructor + +---- + +## [0.4.0] - 2024-03-02 +- add fastProcessor option in **begin()** (Thanks to palmerr23) +- updated license +- updated GitHub/actions to v4 + +---- + +## [0.3.9] - 2023-11-04 +- update readme.md +- minor edits + +## [0.3.8] - 2023-08-26 +- fix #41 #40 add example **HX_loadcell_array.ino** + - test support array of loadcells. +- update readme.md +- add issue-count badge +- add PlatformIO badge +- minor edits + +## [0.3.7] - 2023-06-27 +- add example to measure noise level +- moved code to .cpp +- reorder .cpp to match .h +- removed **callibrate_scale()** (typo ll) +- add scale == 0 in **set_scale(scale)** +- changed return type to **bool set_scale(scale)** +- add example is_ready +- add example pulse-length decoder (Morse) +- update readme.md + +## [0.3.6] - 2023-03-11 +- update readme.md to reference HX711_MP + +## [0.3.5] - 2023-03-10 +- update readme.md +- update GitHub actions +- update license 2023 +- add MulitMap example +- minor edits + +## [0.3.4] - 2022-11-11 +- simplified changelog +- add RP2040 to build-CI +- refactored **set_gain()** to return bool to confirm valid parameter. +- add forced flag for set_gain to force a dummy read call. Default false. +- add constants for **set_gain()** +- improved unit test for **set_gain()** +- add unit test for constants. +- add delayMicroseconds(64) to power_down to enforce long enough HIGH signal. +- add power_down() power_up() cycle in **reset()** +- updated readme.md +- updated keywords.txt + +## [0.3.3] - 2022-03-16 +- add HX711_RAW_MODE +- update documentation + +## [0.3.2] - 2022-03-16 +- add example HX_set_persistent.ino. (won't work for m4) +- add CHANGELOG.md +- minor edits + +## [0.3.1] - 2021-12-19 +- update library.json +- license +- minor edits + +## [0.3.0] - 2021-11-14 +- update build-CI +- readme.md incl. badges +- fix #11 shiftIn timing + +---- + +## [0.2.3] - 2021-05-26 +- add running_average() mode + +## [0.2.2] - 2021-05-10 +- add read_median() +- add mode operandi +- fix typo + +## [0.2.1] - 2020-12-28 +- add arduino-ci +- unit test + +## [0.2.0] - 2020-06-15 +- add price functions +- some refactor + +---- + +## [0.1.1] - 2019-09-09 +- change long to float (reduce footprint) + +## [0.1.0] - 2019-09-04 +- initial version diff --git a/libraries/HX711/HX711.cpp b/libraries/HX711/HX711.cpp new file mode 100644 index 0000000..e589c54 --- /dev/null +++ b/libraries/HX711/HX711.cpp @@ -0,0 +1,512 @@ +// +// FILE: HX711.cpp +// AUTHOR: Rob Tillaart +// VERSION: 0.6.3 +// PURPOSE: Library for load cells for UNO +// URL: https://github.com/RobTillaart/HX711_MP +// URL: https://github.com/RobTillaart/HX711 + + +#include "HX711.h" + + +HX711::HX711() +{ + _offset = 0; + _scale = 1; + _gain = HX711_CHANNEL_A_GAIN_128; + _lastTimeRead = 0; + _mode = HX711_AVERAGE_MODE; + _fastProcessor = false; + _price = 0; +} + + +HX711::~HX711() +{ +} + + +void HX711::begin(uint8_t dataPin, uint8_t clockPin, bool fastProcessor, bool doReset) +{ + _dataPin = dataPin; + _clockPin = clockPin; + _fastProcessor = fastProcessor; + + pinMode(_dataPin, INPUT_PULLUP); + pinMode(_clockPin, OUTPUT); + digitalWrite(_clockPin, LOW); + + if (doReset) + { + reset(); + } +} + + +void HX711::reset() +{ + power_down(); + power_up(); + _offset = 0; + _scale = 1; + _gain = HX711_CHANNEL_A_GAIN_128; + _lastTimeRead = 0; + _mode = HX711_AVERAGE_MODE; + _price = 0; +} + + +bool HX711::is_ready() +{ + return digitalRead(_dataPin) == LOW; +} + + +void HX711::wait_ready(uint32_t ms) +{ + while (!is_ready()) + { + delay(ms); + } +} + + +bool HX711::wait_ready_retry(uint8_t retries, uint32_t ms) +{ + while (retries--) + { + if (is_ready()) return true; + delay(ms); + } + return false; +} + + +bool HX711::wait_ready_timeout(uint32_t timeout, uint32_t ms) +{ + uint32_t start = millis(); + while (millis() - start < timeout) + { + if (is_ready()) return true; + delay(ms); + } + return false; +} + + +/////////////////////////////////////////////////////////////// +// +// READ +// +// From datasheet page 4 +// When output data is not ready for retrieval, +// digital output pin DOUT is HIGH. +// Serial clock input PD_SCK should be LOW. +// When DOUT goes to LOW, it indicates data is ready for retrieval. +// Blocking period can be long up to 400 ms in first read() call. +float HX711::read() +{ + // this BLOCKING wait takes most time... + while (digitalRead(_dataPin) == HIGH) + { + yield(); + } + + union + { + int32_t value = 0; + uint8_t data[4]; + } v; + + // blocking part ... + noInterrupts(); + + // Pulse the clock pin 24 times to read the data. + // v.data[2] = shiftIn(_dataPin, _clockPin, MSBFIRST); + // v.data[1] = shiftIn(_dataPin, _clockPin, MSBFIRST); + // v.data[0] = shiftIn(_dataPin, _clockPin, MSBFIRST); + v.data[2] = _shiftIn(); + v.data[1] = _shiftIn(); + v.data[0] = _shiftIn(); + + // TABLE 3 page 4 datasheet + // + // CLOCK CHANNEL GAIN m + // ------------------------------------ + // 25 A 128 1 // default + // 26 B 32 2 + // 27 A 64 3 + // + // only default 128 verified, + // selection goes through the set_gain(gain) + // + uint8_t m = 1; + if (_gain == HX711_CHANNEL_A_GAIN_128) m = 1; + else if (_gain == HX711_CHANNEL_A_GAIN_64) m = 3; + else if (_gain == HX711_CHANNEL_B_GAIN_32) m = 2; + + while (m > 0) + { + // delayMicroSeconds(1) is needed for fast processors + // T2 >= 0.2 us + digitalWrite(_clockPin, HIGH); + if (_fastProcessor) delayMicroseconds(1); + digitalWrite(_clockPin, LOW); + // keep duty cycle ~50% + if (_fastProcessor) delayMicroseconds(1); + m--; + } + + interrupts(); + // yield(); + + // SIGN extend + if (v.data[2] & 0x80) v.data[3] = 0xFF; + + _lastTimeRead = millis(); + return 1.0 * v.value; +} + + +float HX711::read_average(uint8_t times) +{ + if (times < 1) times = 1; + float sum = 0; + for (uint8_t i = 0; i < times; i++) + { + sum += read(); + yield(); + } + return sum / times; +} + + +float HX711::read_median(uint8_t times) +{ + if (times > 15) times = 15; + if (times < 3) times = 3; + float samples[15]; + for (uint8_t i = 0; i < times; i++) + { + samples[i] = read(); + yield(); + } + _insertSort(samples, times); + if (times & 0x01) return samples[times/2]; + return (samples[times/2] + samples[times/2 + 1]) / 2; +} + + +float HX711::read_medavg(uint8_t times) +{ + if (times > 15) times = 15; + if (times < 3) times = 3; + float samples[15]; + for (uint8_t i = 0; i < times; i++) + { + samples[i] = read(); + yield(); + } + _insertSort(samples, times); + float sum = 0; + // iterate over 1/4 to 3/4 of the array + uint8_t count = 0; + uint8_t first = (times + 2) / 4; + uint8_t last = times - first - 1; + for (uint8_t i = first; i <= last; i++) // !! include last one too + { + sum += samples[i]; + count++; + } + return sum / count; +} + + +float HX711::read_runavg(uint8_t times, float alpha) +{ + if (times < 1) times = 1; + if (alpha < 0) alpha = 0; + if (alpha > 1) alpha = 1; + float val = read(); + for (uint8_t i = 1; i < times; i++) + { + val += alpha * (read() - val); + yield(); + } + return val; +} + + +/////////////////////////////////////////////////////// +// +// MODE +// +void HX711::set_raw_mode() +{ + _mode = HX711_RAW_MODE; +} + + +void HX711::set_average_mode() +{ + _mode = HX711_AVERAGE_MODE; +} + + +void HX711::set_median_mode() +{ + _mode = HX711_MEDIAN_MODE; +} + + +void HX711::set_medavg_mode() +{ + _mode = HX711_MEDAVG_MODE; +} + + +// set_run_avg will use a default alpha of 0.5. +void HX711::set_runavg_mode() +{ + _mode = HX711_RUNAVG_MODE; +} + + +uint8_t HX711::get_mode() +{ + return _mode; +} + + +float HX711::get_value(uint8_t times) +{ + float raw; + switch(_mode) + { + case HX711_RAW_MODE: + raw = read(); + break; + case HX711_RUNAVG_MODE: + raw = read_runavg(times); + break; + case HX711_MEDAVG_MODE: + raw = read_medavg(times); + break; + case HX711_MEDIAN_MODE: + raw = read_median(times); + break; + case HX711_AVERAGE_MODE: + default: + raw = read_average(times); + break; + } + return raw - _offset; +} + + +float HX711::get_units(uint8_t times) +{ + float units = get_value(times) * _scale; + return units; +} + + +/////////////////////////////////////////////////////////////// +// +// GAIN +// +// note: if parameter gain == 0xFF40 some compilers +// will map that to 0x40 == HX711_CHANNEL_A_GAIN_64; +// solution: use uint32_t or larger parameters everywhere. +// note that changing gain/channel may take up to 400 ms (page 3) +bool HX711::set_gain(uint8_t gain, bool forced) +{ + if ( (not forced) && (_gain == gain)) return true; + switch(gain) + { + case HX711_CHANNEL_B_GAIN_32: + case HX711_CHANNEL_A_GAIN_64: + case HX711_CHANNEL_A_GAIN_128: + _gain = gain; + read(); // next user read() is from right channel / gain + return true; + } + return false; // unchanged, but incorrect value. +} + + +uint8_t HX711::get_gain() +{ + return _gain; +} + + +/////////////////////////////////////////////////////// +// +// TARE +// +void HX711::tare(uint8_t times) +{ + _offset = read_average(times); +} + + +float HX711::get_tare() +{ + return -_offset * _scale; +} + + +bool HX711::tare_set() +{ + return _offset != 0; +} + + +/////////////////////////////////////////////////////////////// +// +// CALIBRATION (tare see above) +// +bool HX711::set_scale(float scale) +{ + if (scale == 0) return false; + _scale = 1.0 / scale; + return true; +} + + +float HX711::get_scale() +{ + return 1.0 / _scale; +} + + +void HX711::set_offset(int32_t offset) +{ + _offset = offset; +} + + +int32_t HX711::get_offset() +{ + return _offset; +} + + +// assumes tare() has been set. +void HX711::calibrate_scale(float weight, uint8_t times) +{ + _scale = weight / (read_average(times) - _offset); +} + + +/////////////////////////////////////////////////////////////// +// +// POWER MANAGEMENT +// +void HX711::power_down() +{ + // at least 60 us HIGH + digitalWrite(_clockPin, HIGH); + delayMicroseconds(64); +} + + +void HX711::power_up() +{ + digitalWrite(_clockPin, LOW); +} + + +/////////////////////////////////////////////////////////////// +// +// RATE PIN - works only if rate pin is exposed. +// +void HX711::set_rate_pin(uint8_t pin) +{ + _ratePin = pin; + pinMode(_ratePin, OUTPUT); + set_rate_10SPS(); +} + +void HX711::set_rate_10SPS() +{ + _rate = 10; + digitalWrite(_ratePin, LOW); +} + +void HX711::set_rate_80SPS() +{ + _rate = 80; + digitalWrite(_ratePin, HIGH); +} + +uint8_t HX711::get_rate() +{ + return _rate; +} + + +/////////////////////////////////////////////////////////////// +// +// MISC +// +uint32_t HX711::last_time_read() +{ + return _lastTimeRead; +} + + +/////////////////////////////////////////////////////////////// +// +// PRIVATE +// +void HX711::_insertSort(float * array, uint8_t size) +{ + uint8_t t, z; + float temp; + for (t = 1; t < size; t++) + { + z = t; + temp = array[z]; + while( (z > 0) && (temp < array[z - 1] )) + { + array[z] = array[z - 1]; + z--; + } + array[z] = temp; + yield(); + } +} + + +// MSB_FIRST optimized shiftIn +// see datasheet page 5 for timing +uint8_t HX711::_shiftIn() +{ + // local variables are faster. + uint8_t clk = _clockPin; + uint8_t data = _dataPin; + uint8_t value = 0; + uint8_t mask = 0x80; + while (mask > 0) + { + digitalWrite(clk, HIGH); + // T2 >= 0.2 us + if(_fastProcessor) delayMicroseconds(1); + if (digitalRead(data) == HIGH) + { + value |= mask; + } + digitalWrite(clk, LOW); + // keep duty cycle ~50% + if(_fastProcessor) delayMicroseconds(1); + mask >>= 1; + } + return value; +} + + +// -- END OF FILE -- + diff --git a/libraries/HX711/HX711.h b/libraries/HX711/HX711.h new file mode 100644 index 0000000..05d3f3d --- /dev/null +++ b/libraries/HX711/HX711.h @@ -0,0 +1,215 @@ +#pragma once +// +// FILE: HX711.h +// AUTHOR: Rob Tillaart +// VERSION: 0.6.3 +// PURPOSE: Library for load cells for Arduino +// URL: https://github.com/RobTillaart/HX711_MP +// URL: https://github.com/RobTillaart/HX711 +// +// NOTES +// Superset of interface of HX711 class of Bogde +// uses float instead of long as float has 23 bits mantissa +// which almost perfectly matches the 24 bit ADC. + + +#include "Arduino.h" + +#define HX711_LIB_VERSION (F("0.6.3")) + + +const uint8_t HX711_AVERAGE_MODE = 0x00; +// in median mode only between 3 and 15 samples are allowed. +const uint8_t HX711_MEDIAN_MODE = 0x01; +// medavg = average of the middle "half" of sorted elements +// in medavg mode only between 3 and 15 samples are allowed. +const uint8_t HX711_MEDAVG_MODE = 0x02; +// runavg = running average +const uint8_t HX711_RUNAVG_MODE = 0x03; +// causes read() to be called only once! +const uint8_t HX711_RAW_MODE = 0x04; + + +// supported values for set_gain() +const uint8_t HX711_CHANNEL_A_GAIN_128 = 128; // default +const uint8_t HX711_CHANNEL_A_GAIN_64 = 64; +const uint8_t HX711_CHANNEL_B_GAIN_32 = 32; + + +class HX711 +{ +public: + HX711(); + ~HX711(); + + // fixed gain 128 for now + void begin(uint8_t dataPin, uint8_t clockPin, + bool fastProcessor = false, + bool doReset = true); + + void reset(); + + // checks if load cell is ready to read. + // use this to prevent blocking reads, esp at startup, 1st read. + bool is_ready(); + + // wait until ready, + // check every ms + void wait_ready(uint32_t ms = 0); + // max # retries + bool wait_ready_retry(uint8_t retries = 3, uint32_t ms = 0); + // max timeout + bool wait_ready_timeout(uint32_t timeout = 1000, uint32_t ms = 0); + + + /////////////////////////////////////////////////////////////// + // + // READ + // + // raw read, is blocking until device is ready to read(). + // this blocking period can be long up to 400 ms in first read() call. + float read(); + + // get average of multiple raw reads + // times = 1 or more + float read_average(uint8_t times = 10); + + // get median of multiple raw reads + // times = 3..15 - odd numbers preferred + float read_median(uint8_t times = 7); + + // get average of "middle half" of multiple raw reads. + // times = 3..15 - odd numbers preferred + float read_medavg(uint8_t times = 7); + + // get running average over times measurements. + // the weight alpha can be set to any value between 0 and 1 + // times = 1 or more. + float read_runavg(uint8_t times = 7, float alpha = 0.5); + + + /////////////////////////////////////////////////////////////// + // + // MODE + // + // get set mode for get_value() and indirect get_units(). + // in median and medavg mode only 3..15 samples are allowed. + void set_raw_mode(); + void set_average_mode(); + void set_median_mode(); + void set_medavg_mode(); + // set_run_avg will use a default alpha of 0.5. + void set_runavg_mode(); + uint8_t get_mode(); + + // corrected for offset. + // in HX711_RAW_MODE the parameter times will be ignored. + float get_value(uint8_t times = 1); + // converted to proper units, corrected for scale. + // in HX711_RAW_MODE the parameter times will be ignored. + float get_units(uint8_t times = 1); + + + /////////////////////////////////////////////////////////////// + // + // GAIN + // + // CORE "CONSTANTS" -> read datasheet + // CHANNEL GAIN notes + // ------------------------------------- + // A 128 default, tested + // A 64 + // B 32 + + // returns true ==> parameter gain is valid + // returns false ==> parameter gain is invalid ==> no change. + // note that changing gain/channel takes up to 400 ms (page 3) + // if forced == true, the gain will be forced set + // even it is already the right value + bool set_gain(uint8_t gain = HX711_CHANNEL_A_GAIN_128, bool forced = false); + uint8_t get_gain(); + + + /////////////////////////////////////////////////////////////// + // + // TARE + // call tare to calibrate zero + void tare(uint8_t times = 10); + float get_tare(); + bool tare_set(); + + + /////////////////////////////////////////////////////////////// + // + // CALIBRATION + // + // SCALE > 0 + // returns false if scale == 0; + bool set_scale(float scale = 1.0); + float get_scale(); + + // OFFSET > 0 + void set_offset(int32_t offset = 0); + int32_t get_offset(); + + // clear the scale + // call tare() to set the zero offset + // put a known weight on the scale + // call calibrate_scale(weight) + // scale is calculated. + void calibrate_scale(float weight, uint8_t times = 10); + + + /////////////////////////////////////////////////////////////// + // + // POWER MANAGEMENT + // + void power_down(); + void power_up(); + + + /////////////////////////////////////////////////////////////// + // + // EXPERIMENTAL + // RATE PIN - works only if rate pin is exposed. + // + void set_rate_pin(uint8_t pin); + void set_rate_10SPS(); + void set_rate_80SPS(); + uint8_t get_rate(); + + + // TIME OF LAST READ + uint32_t last_time_read(); + // obsolete in the future (0.7.0) + [[deprecated("Use last_time_read() instead.")]] + uint32_t last_read() { return last_time_read(); }; + + + // PRICING + float get_price(uint8_t times = 1) { return get_units(times) * _price; }; + void set_unit_price(float price = 1.0) { _price = price; }; + float get_unit_price() { return _price; }; + + +private: + uint8_t _dataPin; + uint8_t _clockPin; + + int32_t _offset; + float _scale; + uint8_t _gain; + uint32_t _lastTimeRead; + uint8_t _mode; + bool _fastProcessor; + uint8_t _ratePin = 255; + uint8_t _rate = 10; + float _price; + + void _insertSort(float * array, uint8_t size); + uint8_t _shiftIn(); +}; + + +// -- END OF FILE -- + diff --git a/libraries/HX711/keywords.txt b/libraries/HX711/keywords.txt new file mode 100644 index 0000000..16019ba --- /dev/null +++ b/libraries/HX711/keywords.txt @@ -0,0 +1,80 @@ +# Syntax Colouring Map For HX711 + +# Data types (KEYWORD1) +HX711 KEYWORD1 + + +# Methods and Functions (KEYWORD2) +begin KEYWORD2 +reset KEYWORD2 + +is_ready KEYWORD2 +wait_ready KEYWORD2 +wait_ready_retry KEYWORD2 +wait_ready_timeout KEYWORD2 + +read KEYWORD2 +read_average KEYWORD2 +read_median KEYWORD2 +read_medavg KEYWORD2 +read_runavg KEYWORD2 + +get_value KEYWORD2 +get_units KEYWORD2 + +set_raw_mode KEYWORD2 +set_average_mode KEYWORD2 +set_median_mode KEYWORD2 +set_medavg_mode KEYWORD2 +set_runavg_mode KEYWORD2 +get_mode KEYWORD2 + +tare KEYWORD2 +get_tare KEYWORD2 +tare_set KEYWORD2 + +set_gain KEYWORD2 +get_gain KEYWORD2 +# set_chanA_gain128 KEYWORD2 +# set_chanA_gain64 KEYWORD2 +# set_chanB_gain32 KEYWORD2 + +set_scale KEYWORD2 +get_scale KEYWORD2 + +set_offset KEYWORD2 +get_offset KEYWORD2 + +calibrate_scale KEYWORD2 + +power_down KEYWORD2 +power_up KEYWORD2 + +set_rate_pin KEYWORD2 +set_rate_10SPS KEYWORD2 +set_rate_80SPS KEYWORD2 +get_rate KEYWORD2 + +last_read KEYWORD2 + +get_price KEYWORD2 +set_unit_price KEYWORD2 +get_unit_price KEYWORD2 + + +# Instances (KEYWORD2) + + +# Constants (LITERAL1) +HX711_LIB_VERSION LITERAL1 + +HX711_RAW_MODE LITERAL1 +HX711_AVERAGE_MODE LITERAL1 +HX711_MEDIAN_MODE LITERAL1 +HX711_MEDAVG_MODE LITERAL1 +HX711_RUNAVG_MODE LITERAL1 + +HX711_CHANNEL_A_GAIN_128 LITERAL1 +HX711_CHANNEL_A_GAIN_64 LITERAL1 +HX711_CHANNEL_B_GAIN_32 LITERAL1 +