Hey Rudi,
I took a dive into this, and discovered the fix is simple, but in the interests of education (give a man a fish / teach a man to fish) I figured Iâll post up my whole adventure.
Hopefully it will help you better understand how to problem solve these issues, and also seeing some of the rabbit holes I went down based on assumptions I made to fill in information your posts didnât provide will help you to write good questions on a forum (which can be a bit of an art in itself).
Iâve never used either of these devices before. I first noticed you were using A4 and A5 for your HX711, so I assumed both the HX711 and LCD had I2C interfaces.
To start debugging this, letâs open up the waveshare lcd header (.h) and .cpp files and take a look at what happens when you call lcd.init()
void Waveshare_LCD1602_RGB::init()
{
Wire.begin();
_showfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS;
begin(_cols, _rows);
}
Thereâs really not much in the lcd.init() function. You can see all it really does is initialises the i2C interface. Ok, maybe when the HX711 initialises I2C it passes some special arguments (say for clock frequency or something). I know LCDs are often quite timing sensitive.
And assuming itâs the standard Arduino HX711 library: HX711 Arduino Library - Arduino Reference
Itâs a little more complex:
void HX711::begin(byte dout, byte pd_sck, byte gain) {
PD_SCK = pd_sck;
DOUT = dout;
pinMode(PD_SCK, OUTPUT);
pinMode(DOUT, DOUT_MODE);
set_gain(gain);
}
You can see itâs using the pins you feed it as the Data and Clock lines for the HX711 - but it doesnât call wire.begin()
Maybe itâs called somewhere else. Itâs a bit atypical to not call it in a setup function, but itâs definitely not a fundamental issue if the commands are intended to be called in a specific order, or if it just has to be set by the person programming. Since itâs also called in the LCD library, the fact it isnât called probably wonât break anything here.
It is odd itâs configuring the pins - wire.begin() does that, but anyway, letâs assume the library works and keep moving.
Letâs see what that set_gain function isâŚ
void HX711::set_gain(byte gain) {
switch (gain) {
case 128: // channel A, gain factor 128
GAIN = 1;
break;
case 64: // channel A, gain factor 64
GAIN = 3;
break;
case 32: // channel B, gain factor 32
GAIN = 2;
break;
}
}
Hmm, nothing really jumps out at me here. Looks fairly simple. Thatâs the bottom of this rabbit hole. Letâs see what the next HX711 command in your program - set_scale()
- does:
void HX711::set_scale(float scale) {
SCALE = scale;
}
Well ok then. Nothing much there either. Just sets a global variable. The next HX711 library function you use is Tare()
so lets look at that one. I do a ctrl+F for âtareâ, and thereâs no Tare()
function, but there is a tare()
function. Iâll just check the header (.h) file to see what thatâs about (since variables and function names are case sensitive). Hmm, not here either. Ok, lets install this library and see what the compiler saysâŚ
'error: HX711_LIB_VERSION' was not declared in this scope
Oook. Double check, and yep - itâs not declared anywhere. Since Bryce said it compiled without errors, we must be looking at different libraries
- sure enough there are 7 different HX711 libraries in the Arduino library manager.
PS. this is why itâs important to provide a direct link to the exact library youâre using when seeking help.
A google for that exact command leads me to here:
Ok, so itâs the Rob Tillart HX711 library. Back to square one. Delete that old HX711 library and install the correct one.
Hmm, still no Tare()
function in the library. Oh wait. Youâve made your own Tare()
function that calls tare()
.
again. Ok, slightly confusing, but now Iâm with it.
Now that Iâm confident Iâve got the right library, letâs go back and investigate those initialisation functions in this library.
void HX711::begin(uint8_t dataPin, uint8_t clockPin)
{
_dataPin = dataPin;
_clockPin = clockPin;
pinMode(_dataPin, INPUT);
pinMode(_clockPin, OUTPUT);
digitalWrite(_clockPin, LOW);
reset();
}
This one is almost identical to the one in the other library. What does reset do?
void HX711::reset()
{
_offset = 0;
_scale = 1;
_gain = 128;
_lastRead = 0;
_mode = HX711_AVERAGE_MODE;
}
Ok, just sets some global variables to default values. Letâs try set_scale()
.Hmm nothing in the cpp. check the header file:
void set_scale(float scale = 1.0) { _scale = 1.0 / scale; };
Well thatâs about the simplest function possible. Canât be an issue there. Time to look at your Tare()
function.
void Tare()
{
//digitalWrite(Led ,HIGH);
Serial.print("TARE UNITS: ");
Serial.println(scale.get_units(10));
Serial.println("Taring");
while(Serial.available());
while(Serial.available()) Serial.read();
scale.tare();
Serial.print("NET UNITS: ");
Serial.println(scale.get_units(10));
//digitalWrite(Led, LOW);
Serial.println("Tared");
}
while(Serial.available());
- Ok, unusual. Good chance it will get stuck in a loop here. Because you never read anything, the serial buffer will never decrease so your program will freeze here if your Arduino receives a serial command while starting up. HOWEVER, because thatâs unlikely, and itâs only called during setup itâs probably not the issue, so Iâm going to keep going.
You do need to delete that line though.
while(Serial.available()) Serial.read();
- This line is fine because Serial.read()
removes a byte from the serial buffer, thus decreasing the value returned by Serial.available()
until it reaches 0, at which point the while(condition)
evaluates to false, and the program moves on.
Youâre not actually doing anything with the data youâre reading, but if your intention is just to make sure the serial buffer is empty, this is a common way to do it.
scale.tare();
- ok, letâs go back to the HX711 library and see what this does.
void tare(uint8_t times = 10) { _offset = read_average(times); };
Ok, another fairlyinnocuous looking function. Letâs see what read_average()
does.
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;
}
Ok, what it says on the tin. What does read()
doâŚ
float HX711::read()
{
// this BLOCKING wait takes most time...
while (digitalRead(_dataPin) == HIGH) yield();
union
{
long 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
// only default verified, so other values not supported yet
uint8_t m = 1; // default gain == 128
if (_gain == 64) m = 3;
if (_gain == 32) m = 2;
while (m > 0)
{
digitalWrite(_clockPin, HIGH);
digitalWrite(_clockPin, LOW);
m--;
}
interrupts();
// yield();
// SIGN extend
if (v.data[2] & 0x80) v.data[3] = 0xFF;
_lastRead = millis();
return 1.0 * v.value;
}
// Pulse the clock pin 24 times to read the data.
Ahhhhh
Now I get it. The HX711 is not an I2C device! This is bit banging a protocol!
I first noticed you were using A4 and A5 for your HX711, so I assumed they were both I2C sensors.
Well there goes 90 minutes of my life on a silly assumption.
Again.
Sure enough, double check the datasheet and no mention of I2C or TWI anywhere:
So the problem here is that youâre using pins A4 and A5 for your HX711 which is not an I2C device, while also trying to use those two pins for your LCD - which is an I2C device. In general, you should only ever connect a single device to a pin.
Connecting multiple I2C devices is a special case - itâs whatâs called a Bus or Bussed protocol. The only reason you can only connect multiple I2C devices on the I2C bus, is because I2C has been carefully designed to handle that. For most electronics, thatâs not the case and attaching something that doesnât respect the manners and protocol of the very polite I2C devices is like putting a bull in a china shop - everything breaks.
Ben Eaterâs video on Bus Architecture for his 8 bit breadboard computer explains it very well.
This is a good video on I2C specifically:
As an FYI, on the Uno, A4 and A5 are also directly connected to the I2C pins SDA and SCL, respectively. I think itâs a flaw in the design personally - breaking out the same pins twice has lead to a lot of confusion over the years.
So, all you should need to do (after fixing that infinite loop in your Tare()
function), is leave your LCD connected to A4 and A5, and move your HX711 to 2 other pins. Any 2 other digital or analog pins should work.