I've been tinkering, reading watching for weeks and been able only make little progress. First off I have zero knowledge about Python coding (did some ABAP 18 years ago). I'm trying to help my son to get a home build wind tunnel up and running. We have attached 2 load cells to capture drag and lift of an airfoil. After some primitive thinking and connecting I was able to understand better and the current interfacing looks like this:
- each load cell (one vertical, one horizontal) are connected to their own HX711 modules
- each HX711 module is interfaced with a Raspberry Pi B+ v1.2
- each HX711 module is connected to separate 3.3v pins
- one HX711 is connected to GPIOs 5 and 6; the other 17 and 27
- I use a pre-coded example.py for HX711
- I get error messages when debugging and whenever I place a weight on 1 load cell I get same values displayed (printed) for the other one
- from my little understanding from just looking at the codes is I would not need to modify HX711 which creates a class and thought I load it for each HX711 module with their respective GPIOs ?!
original code
>`#! /usr/bin/python2
import time
import sys
EMULATE_HX711=False
referenceUnit = 1
if not EMULATE_HX711:
import RPi.GPIO as GPIO
from hx711 import HX711
else:
from emulated_hx711 import HX711
def cleanAndExit():
print("Cleaning...")
if not EMULATE_HX711:
GPIO.cleanup()
print("Bye!")
sys.exit()
hx = HX711(5, 6)
'# I've found out that, for some reason, the order of the bytes is not always the same between versions of python, numpy and the hx711 itself.'
'# Still need to figure out why does it change.'
'# If you're experiencing super random values, change these values to MSB or LSB until to get more stable values.'
'# There is some code below to debug and log the order of the bits and the bytes.'
'# The first parameter is the order in which the bytes are used to build the "long" value.'
'# The second paramter is the order of the bits inside each byte.'
'# According to the HX711 Datasheet, the second parameter is MSB so you shouldn't need to modify it.'
hx.set_reading_format("MSB", "MSB")
'# HOW TO CALCULATE THE REFFERENCE UNIT'
'# To set the reference unit to 1. Put 1kg on your sensor or anything you have and know exactly how much it weights.'
'# In this case, 92 is 1 gram because, with 1 as a reference unit I got numbers near 0 without any weight'
'# and I got numbers around 184000 when I added 2kg. So, according to the rule of thirds:'
'# If 2000 grams is 184000 then 1000 grams is 184000 / 2000 = 92.'
hx.set_reference_unit(235)
hx.reset()
hx.tare()
print("Tare done! Add weight now...")
'# to use both channels, you'll need to tare them both'
#hx.tare_A()
#hx.tare_B()
while True:
try:
# These three lines are usefull to debug wether to use MSB or LSB in the reading formats
# for the first parameter of "hx.set_reading_format("LSB", "MSB")".
# Comment the two lines "val = hx.get_weight(5)" and "print val" and uncomment these three lines to see what it prints.
# np_arr8_string = hx.get_np_arr8_string()
# binary_string = hx.get_binary_string()
# print binary_string + " " + np_arr8_string
# Prints the weight. Comment if you're debbuging the MSB and LSB issue.
val = max(0, int (hx.get_weight(5)))
print(val)
# To get weight from both channels (if you have load cells hooked up
# to both channel A and B), do something like this
#val_A = hx.get_weight_A(5)
#val_B = hx.get_weight_B(5)
#print "A: %s B: %s" % ( val_A, val_B )
hx.power_down()
hx.power_up()
time.sleep(0.1)
except (KeyboardInterrupt, SystemExit):
cleanAndExit()
`
my version :)
>`#! /usr/bin/python2
import time
import sys
EMULATE_HX711=False
referenceUnit = 1
if not EMULATE_HX711:
import RPi.GPIO as GPIO
from hx711 import HX711
else:
from emulated_hx711 import HX711
def cleanAndExit():
print("Cleaning...")
if not EMULATE_HX711:
GPIO.cleanup()
print("Bye!")
sys.exit()
hx1 = HX711(5, 6)
hx2 = HX711(27, 17)
'# I've found out that, for some reason, the order of the bytes is not always the same between versions of python, numpy and the hx711 itself.'
'# Still need to figure out why does it change.'
'# If you're experiencing super random values, change these values to MSB or LSB until to get more stable values.'
'# There is some code below to debug and log the order of the bits and the bytes.'
'# The first parameter is the order in which the bytes are used to build the "long" value.'
'# The second paramter is the order of the bits inside each byte.'
'# According to the HX711 Datasheet, the second parameter is MSB so you shouldn't need to modify it.'
hx1.set_reading_format("MSB", "MSB")
hx2.set_reading_format("MSB", "MSB")
'# HOW TO CALCULATE THE REFFERENCE UNIT'
'# To set the reference unit to 1. Put 1kg on your sensor or anything you have and know exactly how much it weights.'
'# In this case, 92 is 1 gram because, with 1 as a reference unit I got numbers near 0 without any weight'
'# and I got numbers around 184000 when I added 2kg. So, according to the rule of thirds:'
'# If 2000 grams is 184000 then 1000 grams is 184000 / 2000 = 92.'
hx1.set_reference_unit(200)
hx2.set_reference_unit(6)
hx1.reset()
hx2.reset()
hx1.tare()
hx2.tare()
print("Tare done! Add weight now...")
'# to use both channels, you'll need to tare them both'
#hx.tare_A()
#hx.tare_B()
while True:
try:
# These three lines are usefull to debug wether to use MSB or LSB in the reading formats
# for the first parameter of "hx.set_reading_format("LSB", "MSB")".
# Comment the two lines "val = hx.get_weight(5)" and "print val" and uncomment these three lines to see what it prints.
#np_arr8_string = hx.get_np_arr8_string()
#binary_string = hx.get_binary_string()
#print (binary_string + " " + np_arr8_string)
# Prints the weight. Comment if you're debbuging the MSB and LSB issue.
val_hx1 = max(0, int (hx1.get_weight(5)))
val_hx2 = max(0, int (hx1.get_weight(27)))
print ("lift: %s drag: %s" % (val_hx1, val_hx2))
# To get weight from both channels (if you have load cells hooked up
# to both channel A and B), do something like this
#val_A = hx.get_weight_A(5)
#val_B = hx.get_weight_B(5)
#print "A: %s B: %s" % ( val_A, val_B )
hx1.power_down()
hx2.power_down()
hx1.power_up()
hx2.power_up()
time.sleep(0.1)
GPIO.cleanup()
except (KeyboardInterrupt, SystemExit):
cleanAndExit()
This a test and a 1kg weight is placed on one load cell Tare done! Add weight now...
- lift: 44 drag: 0
- lift: 0 drag: 0
- lift: 0 drag: 0
- lift: 0 drag: 0
- lift: 0 drag: 408
- lift: 1215 drag: 4414
- lift: 4987 drag: 6169
- lift: 6692 drag: 6669
- lift: 6410 drag: 6456
- lift: 6646 drag: 6800
- lift: 6621 drag: 6580
- lift: 6845 drag: 5338
^CCleaning... Bye!
This is a debug result in Thonny which doesn't say anything to me
%FastDebug example.py
Traceback (most recent call last):
File "/home/pi/Downloads/hx711py-master/example.py", line 25, in hx1 = HX711(5, 6)
File "/home/pi/Downloads/hx711py-master/hx711.py", line 42, in init self.set_gain(gain)
File "/home/pi/Downloads/hx711py-master/hx711.py", line 64, in set_gain GPIO.output(self.PD_SCK, False)
RuntimeError: Please set pin numbering mode using GPIO.setmode(GPIO.BOARD) or GPIO.setmode(GPIO.BCM)
Any tips how I can resolve this would be awesomeness great.
Also can I print results in addition on the screen to a csv file ?
(sorry got long and can't get the formatting right...)
EDIT: I followed the tutorial from here Build a digital Raspberry Pi Scale
load cell to HX711
- Red: E+
- Black: E-
- Green: A-
- White: A+
HX711 to Rpi
- VCC to Raspberry Pi Pin 1 (3.3V)
- GND to Raspberry Pi Pin 6 (GND)
- DT to Raspberry Pi Pin 29 (GPIO 5)
- SCK to Raspberry Pi Pin 31 (GPIO 6)
I changed only the voltage and adequately I connected the second HX711 to DT to GPIO 27 and SCK to GPIO 17.
HX711.py code
import RPi.GPIO as GPIO
import time
import threading
class HX711:
def __init__(self, dout, pd_sck, gain=128):
self.PD_SCK = pd_sck
self.DOUT = dout
# Mutex for reading from the HX711, in case multiple threads in client
# software try to access get values from the class at the same time.
self.readLock = threading.Lock()
GPIO.setmode(GPIO.BCM)
GPIO.setup(self.PD_SCK, GPIO.OUT)
GPIO.setup(self.DOUT, GPIO.IN)
self.GAIN = 0
# The value returned by the hx711 that corresponds to your reference
# unit AFTER dividing by the SCALE.
self.REFERENCE_UNIT = 1
self.REFERENCE_UNIT_B = 1
self.OFFSET = 1
self.OFFSET_B = 1
self.lastVal = int(0)
self.DEBUG_PRINTING = False
self.byte_format = 'MSB'
self.bit_format = 'MSB'
self.set_gain(gain)
# Think about whether this is necessary.
time.sleep(1)
def convertFromTwosComplement24bit(self, inputValue):
return -(inputValue & 0x800000) + (inputValue & 0x7fffff)
def is_ready(self):
return GPIO.input(self.DOUT) == 0
def set_gain(self, gain):
if gain is 128:
self.GAIN = 1
elif gain is 64:
self.GAIN = 3
elif gain is 32:
self.GAIN = 2
GPIO.output(self.PD_SCK, False)
# Read out a set of raw bytes and throw it away.
self.readRawBytes()
def get_gain(self):
if self.GAIN == 1:
return 128
if self.GAIN == 3:
return 64
if self.GAIN == 2:
return 32
# Shouldn't get here.
return 0
def readNextBit(self):
# Clock HX711 Digital Serial Clock (PD_SCK). DOUT will be
# ready 1us after PD_SCK rising edge, so we sample after
# lowering PD_SCL, when we know DOUT will be stable.
GPIO.output(self.PD_SCK, True)
GPIO.output(self.PD_SCK, False)
value = GPIO.input(self.DOUT)
# Convert Boolean to int and return it.
return int(value)
def readNextByte(self):
byteValue = 0
# Read bits and build the byte from top, or bottom, depending
# on whether we are in MSB or LSB bit mode.
for x in range(8):
if self.bit_format == 'MSB':
byteValue <<= 1
byteValue |= self.readNextBit()
else:
byteValue >>= 1
byteValue |= self.readNextBit() * 0x80
# Return the packed byte.
return byteValue
def readRawBytes(self):
# Wait for and get the Read Lock, incase another thread is already
# driving the HX711 serial interface.
self.readLock.acquire()
# Wait until HX711 is ready for us to read a sample.
while not self.is_ready():
pass
# Read three bytes of data from the HX711.
firstByte = self.readNextByte()
secondByte = self.readNextByte()
thirdByte = self.readNextByte()
# HX711 Channel and gain factor are set by number of bits read
# after 24 data bits.
for i in range(self.GAIN):
# Clock a bit out of the HX711 and throw it away.
self.readNextBit()
# Release the Read Lock, now that we've finished driving the HX711
# serial interface.
self.readLock.release()
# Depending on how we're configured, return an orderd list of raw byte
# values.
if self.byte_format == 'LSB':
return [thirdByte, secondByte, firstByte]
else:
return [firstByte, secondByte, thirdByte]
def read_long(self):
# Get a sample from the HX711 in the form of raw bytes.
dataBytes = self.readRawBytes()
if self.DEBUG_PRINTING:
print(dataBytes,)
# Join the raw bytes into a single 24bit 2s complement value.
twosComplementValue = ((dataBytes[0] << 16) |
(dataBytes[1] << 8) |
dataBytes[2])
if self.DEBUG_PRINTING:
print("Twos: 0x%06x" % twosComplementValue)
# Convert from 24bit twos-complement to a signed value.
signedIntValue = self.convertFromTwosComplement24bit(twosComplementValue)
# Record the latest sample value we've read.
self.lastVal = signedIntValue
# Return the sample value we've read from the HX711.
return int(signedIntValue)
def read_average(self, times=3):
# Make sure we've been asked to take a rational amount of samples.
if times <= 0:
raise ValueError("HX711()::read_average(): times must >= 1!!")
# If we're only average across one value, just read it and return it.
if times == 1:
return self.read_long()
# If we're averaging across a low amount of values, just take the
# median.
if times < 5:
return self.read_median(times)
# If we're taking a lot of samples, we'll collect them in a list, remove
# the outliers, then take the mean of the remaining set.
valueList = []
for x in range(times):
valueList += [self.read_long()]
valueList.sort()
# We'll be trimming 20% of outlier samples from top and bottom of collected set.
trimAmount = int(len(valueList) * 0.2)
# Trim the edge case values.
valueList = valueList[trimAmount:-trimAmount]
# Return the mean of remaining samples.
return sum(valueList) / len(valueList)
# A median-based read method, might help when getting random value spikes
# for unknown or CPU-related reasons
def read_median(self, times=3):
if times <= 0:
raise ValueError("HX711::read_median(): times must be greater than zero!")
# If times == 1, just return a single reading.
if times == 1:
return self.read_long()
valueList = []
for x in range(times):
valueList += [self.read_long()]
valueList.sort()
# If times is odd we can just take the centre value.
if (times & 0x1) == 0x1:
return valueList[len(valueList) // 2]
else:
# If times is even we have to take the arithmetic mean of
# the two middle values.
midpoint = len(valueList) / 2
return sum(valueList[midpoint:midpoint+2]) / 2.0
# Compatibility function, uses channel A version
def get_value(self, times=3):
return self.get_value_A(times)
def get_value_A(self, times=3):
return self.read_median(times) - self.get_offset_A()
def get_value_B(self, times=3):
# for channel B, we need to set_gain(32)
g = self.get_gain()
self.set_gain(32)
value = self.read_median(times) - self.get_offset_B()
self.set_gain(g)
return value
# Compatibility function, uses channel A version
def get_weight(self, times=3):
return self.get_weight_A(times)
def get_weight_A(self, times=3):
value = self.get_value_A(times)
value = value / self.REFERENCE_UNIT
return value
def get_weight_B(self, times=3):
value = self.get_value_B(times)
value = value / self.REFERENCE_UNIT_B
return value
# Sets tare for channel A for compatibility purposes
def tare(self, times=15):
self.tare_A(times)
def tare_A(self, times=15):
# Backup REFERENCE_UNIT value
backupReferenceUnit = self.get_reference_unit_A()
self.set_reference_unit_A(1)
value = self.read_average(times)
if self.DEBUG_PRINTING:
print("Tare A value:", value)
self.set_offset_A(value)
# Restore the reference unit, now that we've got our offset.
self.set_reference_unit_A(backupReferenceUnit)
return value
def tare_B(self, times=15):
# Backup REFERENCE_UNIT value
backupReferenceUnit = self.get_reference_unit_B()
self.set_reference_unit_B(1)
# for channel B, we need to set_gain(32)
backupGain = self.get_gain()
self.set_gain(32)
value = self.read_average(times)
if self.DEBUG_PRINTING:
print("Tare B value:", value)
self.set_offset_B(value)
# Restore gain/channel/reference unit settings.
self.set_gain(backupGain)
self.set_reference_unit_B(backupReferenceUnit)
return value
def set_reading_format(self, byte_format="LSB", bit_format="MSB"):
if byte_format == "LSB":
self.byte_format = byte_format
elif byte_format == "MSB":
self.byte_format = byte_format
else:
raise ValueError("Unrecognised byte_format: \"%s\"" % byte_format)
if bit_format == "LSB":
self.bit_format = bit_format
elif bit_format == "MSB":
self.bit_format = bit_format
else:
raise ValueError("Unrecognised bitformat: \"%s\"" % bit_format)
# sets offset for channel A for compatibility reasons
def set_offset(self, offset):
self.set_offset_A(offset)
def set_offset_A(self, offset):
self.OFFSET = offset
def set_offset_B(self, offset):
self.OFFSET_B = offset
def get_offset(self):
return self.get_offset_A()
def get_offset_A(self):
return self.OFFSET
def get_offset_B(self):
return self.OFFSET_B
def set_reference_unit(self, reference_unit):
self.set_reference_unit_A(reference_unit)
def set_reference_unit_A(self, reference_unit):
# Make sure we aren't asked to use an invalid reference unit.
if reference_unit == 0:
raise ValueError("HX711::set_reference_unit_A() can't accept 0 as a reference unit!")
return
self.REFERENCE_UNIT = reference_unit
def set_reference_unit_B(self, reference_unit):
# Make sure we aren't asked to use an invalid reference unit.
if reference_unit == 0:
raise ValueError("HX711::set_reference_unit_A() can't accept 0 as a reference unit!")
return
self.REFERENCE_UNIT_B = reference_unit
def get_reference_unit(self):
return get_reference_unit_A()
def get_reference_unit_A(self):
return self.REFERENCE_UNIT
def get_reference_unit_B(self):
return self.REFERENCE_UNIT_B
def power_down(self):
# Wait for and get the Read Lock, incase another thread is already
# driving the HX711 serial interface.
self.readLock.acquire()
# Cause a rising edge on HX711 Digital Serial Clock (PD_SCK). We then
# leave it held up and wait 100 us. After 60us the HX711 should be
# powered down.
GPIO.output(self.PD_SCK, False)
GPIO.output(self.PD_SCK, True)
time.sleep(0.0001)
# Release the Read Lock, now that we've finished driving the HX711
# serial interface.
self.readLock.release()
def power_up(self):
# Wait for and get the Read Lock, incase another thread is already
# driving the HX711 serial interface.
self.readLock.acquire()
# Lower the HX711 Digital Serial Clock (PD_SCK) line.
GPIO.output(self.PD_SCK, False)
# Wait 100 us for the HX711 to power back up.
time.sleep(0.0001)
# Release the Read Lock, now that we've finished driving the HX711
# serial interface.
self.readLock.release()
# HX711 will now be defaulted to Channel A with gain of 128. If this
# isn't what client software has requested from us, take a sample and
# throw it away, so that next sample from the HX711 will be from the
# correct channel/gain.
if self.get_gain() != 128:
self.readRawBytes()
def reset(self):
self.power_down()
self.power_up()
# EOF - hx711.py