HX711 & LCD1602 merge

Hi Bryce - as discussed - the merged code for HC711 & LCD1602.
lcd.init();// initialize is the problem problem that stops HX711 code from working.


// 04/02/'22
// FILE:LCD_3
// AUTHOR: Rudi Broekhuizen
// Hardware Arduino NANO, LCD1602_RGB, HX711

#include <HX711.h>
HX711 scale;
float calibration_factor = -104950  ;
uint8_t dataPin = A4;
uint8_t clockPin = A5;

#include "Waveshare_LCD1602_RGB.h"
#include <Wire.h>
#include <stdio.h>
#include <stdlib.h>
//#define LCD_ADDRESS  (0x70)   //(0x7c)
//#define RGB_ADDRESS     (0xc0)

  Waveshare_LCD1602_RGB lcd(16,2);  //16 characters and 2 lines of show
  int r,g,b,t=0;
  int i;
  char buffer [5];
  int Load=0;
  
void setup() {

  scale.begin(dataPin, clockPin);
  scale.set_scale(calibration_factor);
  Serial.begin(9600);
  Serial.println(__FILE__);
  Serial.print("LIBRARY VERSION: ");
  Serial.println(HX711_LIB_VERSION);
  Serial.println();
 Tare();

    lcd.init();// initialize ####problem
    //r=90; //set colour
    //g=0;
    //b=0;
    
    //lcd.stopBlink();
    //lcd.setRGB(r,g,b);
    //lcd.setCursor(0,0);
    //lcd.send_string("Nett");
    //lcd.setCursor(10,0);
   // lcd.send_string("Kg.");
   // lcd.setCursor(0,1);
   // lcd.send_string("Tared");
}

void loop() {

    //i=Load;
    //scanf ("%d",&i);
    //itoa (i,buffer,11);
    //printf ("decimal: %s\n",buffer);
        
    //Serial.println(buffer);
    //lcd.setCursor(6,0);
    //lcd.send_string(buffer);

    Serial.print("calibration_factor: ");
    Serial.println(calibration_factor);
    Serial.print("Load: ");
    Serial.print(scale.get_units(10));
    Serial.println(" Kg");
    delay(200);
    
    }
    
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");
}

Regards Rudi.

4 Likes

Hi Rudi, what’s the error message you’re getting? The compiler warnings and erros usually give pretty good clues about the issue, especially if you turn on verbose output.

Arduino verbose errors

5 Likes

Hi Rudi,

Just double-checking that it is certainly compiling for you? I included the HX711 library and after downloading the zip and adding it as a .zip library in my IDE (and as such switching the “” to <>) it appears that I can compile and upload this to a board without running into any issues:

Waveshare-LCD1602-RGB-master.zip (3.2 KB)

// 04/02/'22
// FILE:LCD_3
// AUTHOR: Rudi Broekhuizen
// Hardware Arduino NANO, LCD1602_RGB, HX711

#include <HX711.h>
HX711 scale;
float calibration_factor = -104950  ;
uint8_t dataPin = A4;
uint8_t clockPin = A5;

#include <Waveshare_LCD1602_RGB.h>
#include <Wire.h>
#include <stdio.h>
#include <stdlib.h>
//#define LCD_ADDRESS  (0x70)   //(0x7c)
//#define RGB_ADDRESS     (0xc0)

  Waveshare_LCD1602_RGB lcd(16,2);  //16 characters and 2 lines of show
  int r,g,b,t=0;
  int i;
  char buffer [5];
  int Load=0;
  
void setup() {

  scale.begin(dataPin, clockPin);
  scale.set_scale(calibration_factor);
  Serial.begin(9600);
  Serial.println(__FILE__);
  Serial.print("LIBRARY VERSION: ");
  Serial.println(HX711_LIB_VERSION);
  Serial.println();
 Tare();

    lcd.init();// initialize ####problem
    //r=90; //set colour
    //g=0;
    //b=0;
    
    //lcd.stopBlink();
    //lcd.setRGB(r,g,b);
    //lcd.setCursor(0,0);
    //lcd.send_string("Nett");
    //lcd.setCursor(10,0);
   // lcd.send_string("Kg.");
   // lcd.setCursor(0,1);
   // lcd.send_string("Tared");
}

void loop() {

    //i=Load;
    //scanf ("%d",&i);
    //itoa (i,buffer,11);
    //printf ("decimal: %s\n",buffer);
        
    //Serial.println(buffer);
    //lcd.setCursor(6,0);
    //lcd.send_string(buffer);

    Serial.print("calibration_factor: ");
    Serial.println(calibration_factor);
    Serial.print("Load: ");
    Serial.print(scale.get_units(10));
    Serial.println(" Kg");
    delay(200);
    
    }
    
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");
}
3 Likes

Bryce,

That is correct the program compiles and runs but does not work with that statement in the program.

Regards

Rudi Broekhuizen

2 Likes

Hi,

No errors at all.

Both programs work ok independently but now after merging them the HX711 part does not work

The LCD1602 I have not investigated as yet

~WRD000.jpg

image001.jpg

2 Likes

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() :thinking: 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 :man_facepalming: - 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(). :man_facepalming: 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 :bulb: 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. :man_facepalming: 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.

7 Likes

Thanks Oliver,
Yes you are very correct - I assumed they were both I2C devices.
Much appreciate your help with this matter
Regards
Rudi

5 Likes

Thanks Bryce
I assumed they were both I2C devices.
Much appreciate your help with this matter
Regards
Rudi

5 Likes