Maker Pro
Arduino

An Arduino based weigh scale controller for HX711 modules.

November 12, 2023 by Adrian Smith
Share
banner

A digital Arduino based weigh scale controller for the commonly available HX711 modules and a load cell. The weight is displayed on a 5 digit display capable of indicating up to 99.999Kg. It uses a TPIC6B595 to drive the LED displays with multiplexing for lower component count. Can be assembled on Veroboard.

Story

This project started as a side project of a larger commercial project which was to make a weighing scale controller connected to 4 scales that could be placed under each wheel of a race / track car. This is so you could balance out the weight of the car for better handling and performance. There were two possible ways of doing this; using HX711 modules or an analog differential amplifier with a gain of x100 to amplify the voltage from the load cell and input it to an Arduino. This is ultimately the design we went for in the end which led to this project made from one of the spare HX711 modules.

The project

So with the HX711 module I decided to use it to make my own weighing scale to weigh items up to 10Kg. I had a spare microcontroller and other parts and set about designing a circuit in KiCad. I went for a simple 7 segment display with 0.56" digits and used a TPIC6B595 shift register to drive the segments and PNP transistors for the digits using multiplexing. This reduced the number of components so I could assemble a prototype on Veroboard.


Next was to write the code which I used the Arduino IDE but when I came to powering on for the first time I found that some digits were dimmer than others. To cut a long story short this was due to the digitalwrite Arduino functions taking up too much processor cycles so I used direct port manipulation using AVR C++ functions. This cured the problem. To calibrate the code for my particular setup I followed the instructions for the HX711 library (link down below) and found that in comparison to a kitchen scale it was very accurate to within 10 grams.


The display has a built in error detection so if the reading goes "negative" - the load cell is bent the wrong way it will show "Error" on the display. It was assembled into a plastic case which to be honest didn't work quite well as it bent when heavier loads were placed on it. The load cell needs to be fixed to a strong surface such as thick steel plate so it does not flex. Other than that it worked well.


I decided to design a PCB for it too but didn't get it manufactured as I didn't see much point as a) you can buy a controller on AliExpress for a few pounds / dollars and b) I had no need to make any more. However I will release the design files and code so you can modify and adapt my design for yourself if you wanted to make something. Or simply use my design as an idea for something else such as how to multiplex a 7 segment display from an Arduino.


One thing to watch out for is power dissipation in the TPIC6B595; my displays have a forward voltage of 2.2v and with a current of 60mA x8 segments it's close to the limit of the 500mA absolute maximum. So if you use higher efficiency LED displays the current limiting resistors should be 56 or 68 ohms. 60mA x5 digits gives an average forward current of 12mA per segment. Most modern displays will work with 5 to 8mA.


The PCB design passes all design rule checks but I have NOT got it manufactured - this project was made on veroboard. I will make the gerber files available for download should you want to try it.


Link to the HX711 library on GitHub. https://github.com/olkal/HX711_ADC/tree/master

// Weigh scale up 10KG

#include <avr/pgmspace.h> 
#include <avr/io.h>
#include <SPI.h>
#include <Wire.h>
#include <HX711_ADC.h>

const int tare_sw = 9; 
long int offset = 0;
long int calfactor = 0;
int digit = 0;

const int clk = 13; // SRCK
const int latch = 10; // RCK
const int data = 11; // DIN
const int OE = 14; // output enable
const int digit0 = 2;
const int digit1 = 3;
const int digit2 = 4;
const int digit3 = 5;
const int digit4 = 6;

HX711_ADC LoadCell(8, 7); // parameters: dt pin, sck pin

// 7-segment digits 0-9
// {Q0, Q1, Q2, Q3, Q4, Q5, Q6, Q7} --> {g, f, e, d, c, b, a, DP} 
byte ssddigits[10] = // array without decimal points on
{
  B01111110,  // 0
  B00001100,  // 1
  B10110110,  // 2
  B10011110,  // 3
  B11001100,  // 4
  B11011010,  // 5
  B11111010,  // 6
  B01001110,  // 7
  B11111110,  // 8
  B11011110,  // 9
};

byte ssddigitsDP[10] = // array with decimal points on
{
  B01111111,  // 0
  B00001101,  // 1
  B10110111,  // 2
  B10011111,  // 3
  B11001101,  // 4
  B11011011,  // 5
  B11111011,  // 6
  B01001111,  // 7
  B11111111,  // 8
  B11011111,  // 9
};

void setup()
{
    pinMode (tare_sw, INPUT_PULLUP);
    pinMode (clk, OUTPUT);
    pinMode (latch, OUTPUT);
    pinMode (data, OUTPUT);
    pinMode (OE, OUTPUT); // use external pull-up 
    pinMode (digit0, OUTPUT);
    pinMode (digit1, OUTPUT);
    pinMode (digit2, OUTPUT);
    pinMode (digit3, OUTPUT);
    pinMode (digit4, OUTPUT);
    //Serial.begin(9600); // for testing. Serial not connected in final version
    LoadCell.begin(); // start connection to HX711
    LoadCell.start(2000); // load cells gets 2000ms of time to stabilize
    //calfactor = analogRead(1);
    //LoadCell.setCalFactor(calfactor);
    LoadCell.setCalFactor(201.4); // calibration factor for load cell => strongly dependent on your individual setup
    LoadCell.tareNoDelay(); // cancel out weight of the plate attached to the sensor
    digitalWrite(OE,LOW); // turn LED display on. OE / G is pulled up to +5V externally by 4.7K resistor.
    //This prevents LED burnout if at power on / bootup random segments are lit (multiplexing code will not be running)
}

void loop()
{
    LoadCell.update(); // retrieves data from the load cell
    long int grams = LoadCell.getData() -offset; // get output value
    //long int grams = 12345; // for testing
    //Serial.println(grams);
       
    static uint16_t btndbc = 0;
    btndbc=(btndbc<<1) | digitalRead(tare_sw) | 0xe000; // debounce button
     if (btndbc==0xf000)  
          
     {
        delay(1); // wait 1 millisecond
        offset = LoadCell.getData(); // read initial HX711 value and store it as offset  
     } 

    // get digit data. Digit 0 is leftmost
    int dig4 = (grams % 10); // ones 
    int dig3 = (grams / 10) % 10; // tens     
    int dig2 = (grams / 100) % 10; // hundreds  
    int dig1 = (grams / 1000) % 10; // thousands
    int dig0 = (grams / 10000) %10; // ten thousands
   
    delay(1);

    // turn all digits off by setting digit pin high on each loop through. 
    PORTD |= (1<<PORTD2);
    PORTD |= (1<<PORTD3);
    PORTD |= (1<<PORTD4);
    PORTD |= (1<<PORTD5);
    PORTD |= (1<<PORTD6);

   if (grams <-9 || grams >10010) // show error if minus figure or scale is overloaded

    {
     switch (digit)
     {
      case 0:
        SPI.beginTransaction(SPISettings(8000000, LSBFIRST, SPI_MODE0));
        PORTB &= ~(1<<PORTB2); // set latch LOW 
        SPI.transfer(B11110010); //E
        PORTB |= (1<<PORTB2); // set latch HIGH
        SPI.endTransaction();
        PORTD &= ~(1<<PORTD2); // set digit pin 0 LOW
        delay(2);
        break;
 
      case 1:
        SPI.beginTransaction(SPISettings(8000000, LSBFIRST, SPI_MODE0));
        PORTB &= ~(1<<PORTB2); // set latch LOW 
        SPI.transfer(B10100000); // r
        PORTB |= (1<<PORTB2); // set latch HIGH
        SPI.endTransaction();
        PORTD &= ~(1<<PORTD3);
        delay(2);
        break;    
 
      case 2:
        SPI.beginTransaction(SPISettings(8000000, LSBFIRST, SPI_MODE0));
        PORTB &= ~(1<<PORTB2); // set latch LOW 
        SPI.transfer(B10100000); //r
        PORTB |= (1<<PORTB2); // set latch HIGH
        SPI.endTransaction();
        PORTD &= ~(1<<PORTD4);
        delay(2);
        break; 
 
      case 3:
        SPI.beginTransaction(SPISettings(8000000, LSBFIRST, SPI_MODE0));
        PORTB &= ~(1<<PORTB2); // set latch LOW 
        SPI.transfer(B10111000); // o
        PORTB |= (1<<PORTB2); // set latch HIGH
        SPI.endTransaction();
        PORTD &= ~(1<<PORTD5);
        delay(2);
        break; 

      case 4:
        SPI.beginTransaction(SPISettings(8000000, LSBFIRST, SPI_MODE0));
        PORTB &= ~(1<<PORTB2); // set latch LOW 
        SPI.transfer(B10100000); //r
        PORTB |= (1<<PORTB2); // set latch HIGH
        SPI.endTransaction();
        PORTD &= ~(1<<PORTD6);
        delay(2);
        break;
      }
 
        digit++;
    
    if (digit == 5)
      {
        digit = 0;
      }
   }
    
    else // display normally
    {
      switch (digit)
      {
        case 0:
          SPI.beginTransaction(SPISettings(8000000, LSBFIRST, SPI_MODE0));
          PORTB &= ~(1<<PORTB2); // set latch LOW 
          SPI.transfer(ssddigits[dig0]);
          PORTB |= (1<<PORTB2); // set latch HIGH
          SPI.endTransaction();
          PORTD &= ~(1<<PORTD2); // set digit pin 0 LOW
          delay(2);
          break;
 
        case 1:
          SPI.beginTransaction(SPISettings(8000000, LSBFIRST, SPI_MODE0));
          PORTB &= ~(1<<PORTB2); // set latch LOW 
          SPI.transfer(ssddigitsDP[dig1]); // put a decimal point here. HX711 output is in grams and display will show in KG.
          PORTB |= (1<<PORTB2); // set latch HIGH
          SPI.endTransaction();
          PORTD &= ~(1<<PORTD3);
          delay(2);
          break;    
 
        case 2:
          SPI.beginTransaction(SPISettings(8000000, LSBFIRST, SPI_MODE0));
          PORTB &= ~(1<<PORTB2); // set latch LOW 
          SPI.transfer(ssddigits[dig2]);
          PORTB |= (1<<PORTB2); // set latch HIGH
          SPI.endTransaction();
          PORTD &= ~(1<<PORTD4);
          delay(2);
          break; 
 
        case 3:
          SPI.beginTransaction(SPISettings(8000000, LSBFIRST, SPI_MODE0));
          PORTB &= ~(1<<PORTB2); // set latch LOW 
          SPI.transfer(ssddigits[dig3]);
          PORTB |= (1<<PORTB2); // set latch HIGH
          SPI.endTransaction();
          PORTD &= ~(1<<PORTD5);
          delay(2);
          break; 

          case 4:
          SPI.beginTransaction(SPISettings(8000000, LSBFIRST, SPI_MODE0));
          PORTB &= ~(1<<PORTB2); // set latch LOW 
          SPI.transfer(ssddigits[dig4]);
          PORTB |= (1<<PORTB2); // set latch HIGH
          SPI.endTransaction();
          PORTD &= ~(1<<PORTD6);
          delay(2);
          break;
      }
 
     digit++;
    
    if (digit == 5)
      {
        digit = 0;
      }
   }
}

Related Content

Comments


You May Also Like