Skip to content

Commit

Permalink
Merge pull request #123 from hiveeyes/spring-cleaning
Browse files Browse the repository at this point in the history
Spring cleaning with multiarch support
  • Loading branch information
bogde authored Feb 20, 2019
2 parents 899dc7c + 83c9f51 commit c7a7336
Show file tree
Hide file tree
Showing 16 changed files with 759 additions and 118 deletions.
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,10 @@
*.lai
*.la
*.a

# IDE metadata
/.idea

# PlatformIO virtualenv and pioenv
/.venv*
/.pioenvs
8 changes: 3 additions & 5 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,10 @@ cache:
directories:
- "~/.platformio"

env:
- PLATFORMIO_CI_SRC=examples/HX711Serial
- PLATFORMIO_CI_SRC=examples/HX711SerialBegin

install:
- pip install -U platformio

script:
- platformio ci --board=megaatmega2560 --lib="."
- platformio ci --board=megaatmega2560 --lib="." examples/HX711_full_example
- platformio ci --board=megaatmega2560 --lib="." examples/HX711_timeout_example
- platformio run
20 changes: 20 additions & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# HX711 library contributors

Listed in the order of appearance.

- Weihong Guan: First steps
- Bogdan Necula: Making it real
- Zachary J. Fields: Performance improvements on AVR. Simplify read logic.
- Rodrigo Wirth: Support to read the current `get_offset` and `get_scale`
- Ulrich Wolf: Move pin definition out of constructor
- Alexander Wilms: Improve documentation
- David Holland-Moritz: Improve interrupt safety on AVR
- Geert Roumen et al.: ESP32 support
- Thomas O Fredericks: Support for Teensy 3.2 and non-blocking readings
- Ahmad Elbadri: Improve ESP8266 stability
- Andreas Motl: Bookkeeping, multiarch support
- The Hiveeyes Developers: Spring-cleaning 2019
- Many bits and pieces by countless people from the community,
see also "doc/backlog.rst" in the repository.

Thanks a bunch!
177 changes: 156 additions & 21 deletions HX711.cpp
Original file line number Diff line number Diff line change
@@ -1,19 +1,66 @@
/**
*
* HX711 library for Arduino
* https://github.com/bogde/HX711
*
* MIT License
* (c) 2018 Bogdan Necula
*
**/
#include <Arduino.h>
#include <HX711.h>

#ifndef ESP8266
#if ARDUINO_VERSION <= 106
// "yield" is not implemented as noop in older Arduino Core releases, so let's define it.
// See also: https://stackoverflow.com/questions/34497758/what-is-the-secret-of-the-arduino-yieldfunction/34498165#34498165
void yield(void) {};
#endif
// TEENSYDUINO has a port of Dean Camera's ATOMIC_BLOCK macros for AVR to ARM Cortex M3.
#define HAS_ATOMIC_BLOCK (defined(ARDUINO_ARCH_AVR) || defined(TEENSYDUINO))

// Whether we are running on either the ESP8266 or the ESP32.
#define ARCH_ESPRESSIF (defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32))

// Whether we are actually running on FreeRTOS.
#define IS_FREE_RTOS defined(ARDUINO_ARCH_ESP32)

// Define macro designating whether we're running on a reasonable
// fast CPU and so should slow down sampling from GPIO.
#define FAST_CPU \
( \
ARCH_ESPRESSIF || \
defined(ARDUINO_ARCH_SAM) || defined(ARDUINO_ARCH_SAMD) || \
defined(ARDUINO_ARCH_STM32) || defined(TEENSYDUINO) \
)

#if HAS_ATOMIC_BLOCK
// Acquire AVR-specific ATOMIC_BLOCK(ATOMIC_RESTORESTATE) macro.
#include <util/atomic.h>
#endif

HX711::HX711(byte dout, byte pd_sck, byte gain) {
#ifndef ESP8266
begin(dout, pd_sck, gain);
#endif
#if FAST_CPU
// Make shiftIn() be aware of clockspeed for
// faster CPUs like ESP32, Teensy 3.x and friends.
// See also:
// - https://github.com/bogde/HX711/issues/75
// - https://github.com/arduino/Arduino/issues/6561
// - https://community.hiveeyes.org/t/using-bogdans-canonical-hx711-library-on-the-esp32/539
uint8_t shiftInSlow(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder) {
uint8_t value = 0;
uint8_t i;

for(i = 0; i < 8; ++i) {
digitalWrite(clockPin, HIGH);
delayMicroseconds(1);
if(bitOrder == LSBFIRST)
value |= digitalRead(dataPin) << i;
else
value |= digitalRead(dataPin) << (7 - i);
digitalWrite(clockPin, LOW);
delayMicroseconds(1);
}
return value;
}
#define SHIFTIN_WITH_SPEED_SUPPORT(data,clock,order) shiftInSlow(data,clock,order)
#else
#define SHIFTIN_WITH_SPEED_SUPPORT(data,clock,order) shiftIn(data,clock,order)
#endif


HX711::HX711() {
}
Expand Down Expand Up @@ -53,27 +100,74 @@ void HX711::set_gain(byte gain) {
}

long HX711::read() {
// wait for the chip to become ready
while (!is_ready()) {
// Will do nothing on Arduino but prevent resets of ESP8266 (Watchdog Issue)
yield();
}

// Wait for the chip to become ready.
wait_ready();

// Define structures for reading data into.
unsigned long value = 0;
uint8_t data[3] = { 0 };
uint8_t filler = 0x00;

// pulse the clock pin 24 times to read the data
data[2] = shiftIn(DOUT, PD_SCK, MSBFIRST);
data[1] = shiftIn(DOUT, PD_SCK, MSBFIRST);
data[0] = shiftIn(DOUT, PD_SCK, MSBFIRST);
// Protect the read sequence from system interrupts. If an interrupt occurs during
// the time the PD_SCK signal is high it will stretch the length of the clock pulse.
// If the total pulse time exceeds 60 uSec this will cause the HX711 to enter
// power down mode during the middle of the read sequence. While the device will
// wake up when PD_SCK goes low again, the reset starts a new conversion cycle which
// forces DOUT high until that cycle is completed.
//
// The result is that all subsequent bits read by shiftIn() will read back as 1,
// corrupting the value returned by read(). The ATOMIC_BLOCK macro disables
// interrupts during the sequence and then restores the interrupt mask to its previous
// state after the sequence completes, insuring that the entire read-and-gain-set
// sequence is not interrupted. The macro has a few minor advantages over bracketing
// the sequence between `noInterrupts()` and `interrupts()` calls.
#if HAS_ATOMIC_BLOCK
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {

#elif IS_FREE_RTOS
// Begin of critical section.
// Critical sections are used as a valid protection method
// against simultaneous access in vanilla FreeRTOS.
// Disable the scheduler and call portDISABLE_INTERRUPTS. This prevents
// context switches and servicing of ISRs during a critical section.
portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;
portENTER_CRITICAL(&mux);

#else
// Disable interrupts.
noInterrupts();
#endif

// Pulse the clock pin 24 times to read the data.
data[2] = SHIFTIN_WITH_SPEED_SUPPORT(DOUT, PD_SCK, MSBFIRST);
data[1] = SHIFTIN_WITH_SPEED_SUPPORT(DOUT, PD_SCK, MSBFIRST);
data[0] = SHIFTIN_WITH_SPEED_SUPPORT(DOUT, PD_SCK, MSBFIRST);

// set the channel and the gain factor for the next reading using the clock pin
// Set the channel and the gain factor for the next reading using the clock pin.
for (unsigned int i = 0; i < GAIN; i++) {
digitalWrite(PD_SCK, HIGH);
#if ARCH_ESPRESSIF
delayMicroseconds(1);
#endif
digitalWrite(PD_SCK, LOW);
#if ARCH_ESPRESSIF
delayMicroseconds(1);
#endif
}

#if IS_FREE_RTOS
// End of critical section.
portEXIT_CRITICAL(&mux);

#elif HAS_ATOMIC_BLOCK
}

#else
// Enable interrupts again.
interrupts();
#endif

// Replicate the most significant bit to pad out a 32-bit signed integer
if (data[2] & 0x80) {
filler = 0xFF;
Expand All @@ -90,11 +184,52 @@ long HX711::read() {
return static_cast<long>(value);
}

void HX711::wait_ready(unsigned long delay_ms) {
// Wait for the chip to become ready.
// This is a blocking implementation and will
// halt the sketch until a load cell is connected.
while (!is_ready()) {
// Probably will do no harm on AVR but will feed the Watchdog Timer (WDT) on ESP.
// https://github.com/bogde/HX711/issues/73
delay(delay_ms);
}
}

bool HX711::wait_ready_retry(int retries, unsigned long delay_ms) {
// Wait for the chip to become ready by
// retrying for a specified amount of attempts.
// https://github.com/bogde/HX711/issues/76
int count = 0;
while (count < retries) {
if (is_ready()) {
return true;
}
delay(delay_ms);
count++;
}
return false;
}

bool HX711::wait_ready_timeout(unsigned long timeout, unsigned long delay_ms) {
// Wait for the chip to become ready until timeout.
// https://github.com/bogde/HX711/pull/96
unsigned long millisStarted = millis();
while (millis() - millisStarted < timeout) {
if (is_ready()) {
return true;
}
delay(delay_ms);
}
return false;
}

long HX711::read_average(byte times) {
long sum = 0;
for (byte i = 0; i < times; i++) {
sum += read();
yield();
// Probably will do no harm on AVR but will feed the Watchdog Timer (WDT) on ESP.
// https://github.com/bogde/HX711/issues/73
delay(0);
}
return sum / times;
}
Expand Down
26 changes: 20 additions & 6 deletions HX711.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
/**
*
* HX711 library for Arduino
* https://github.com/bogde/HX711
*
* MIT License
* (c) 2018 Bogdan Necula
*
**/
#ifndef HX711_h
#define HX711_h

Expand All @@ -17,23 +26,28 @@ class HX711
float SCALE = 1; // used to return weight in grams, kg, ounces, whatever

public:
// define clock and data pin, channel, and gain factor
// channel selection is made by passing the appropriate gain: 128 or 64 for channel A, 32 for channel B
// gain: 128 or 64 for channel A; channel B works with 32 gain factor only
HX711(byte dout, byte pd_sck, byte gain = 128);

HX711();

virtual ~HX711();

// Allows to set the pins and gain later than in the constructor
// Initialize library with data output pin, clock input pin and gain factor.
// Channel selection is made by passing the appropriate gain:
// - With a gain factor of 64 or 128, channel A is selected
// - With a gain factor of 32, channel B is selected
// The library default is "128" (Channel A).
void begin(byte dout, byte pd_sck, byte gain = 128);

// check if HX711 is ready
// Check if HX711 is ready
// from the datasheet: 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.
bool is_ready();

// Wait for the HX711 to become ready
void wait_ready(unsigned long delay_ms = 0);
bool wait_ready_retry(int retries = 3, unsigned long delay_ms = 0);
bool wait_ready_timeout(unsigned long timeout = 1000, unsigned long delay_ms = 0);

// set the gain factor; takes effect only after a call to read()
// channel A can be set for a 128 or 64 gain; channel B has a fixed 32 gain
// depending on the parameter, the channel is also set to either A or B
Expand Down
62 changes: 62 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# ============
# Main targets
# ============


# -------------
# Configuration
# -------------

$(eval venvpath := .venv2)
$(eval pip := $(venvpath)/bin/pip)
$(eval python := $(venvpath)/bin/python)
$(eval platformio := $(venvpath)/bin/platformio)

# Setup Python virtualenv
setup-virtualenv:
@test -e $(python) || `command -v virtualenv` --python=python2 --no-site-packages $(venvpath)


# ----------
# PlatformIO
# ----------

install-platformio: setup-virtualenv
@$(pip) install platformio --quiet

build-all: install-platformio
@$(platformio) run

build-env: install-platformio
@$(platformio) run --environment $(environment)


# Note: This are legacy build targets, the new ones are defined through `platformio.ini`.

ci-all: install-platformio
# atmelavr
$(platformio) ci --board=megaatmega2560 --lib="." examples/HX711_basic_example
$(platformio) ci --board=megaatmega2560 --lib="." examples/HX711_timeout_example
$(platformio) ci --board=megaatmega2560 --lib="." examples/HX711_full_example

# atmelavr
$(MAKE) ci-basic board=feather328p

# espressif8266
$(MAKE) ci-basic board=huzzah

# espressif32
$(MAKE) ci-basic board=lopy4

# atmelsam
$(MAKE) ci-basic board=adafruit_feather_m0
$(MAKE) ci-basic board=adafruit_feather_m4

# bluepill
$(MAKE) ci-basic board=bluepill_f103c8

ci-basic:
$(platformio) ci --board=$(board) --lib="." examples/HX711_basic_example --verbose

clean:
platformio run -t clean
Loading

0 comments on commit c7a7336

Please sign in to comment.