Rolf Jethon
Published © CC BY-SA

Design Kitchen Scale Based on Arduino

3D printable design with all resources - free to download under the Creative commons License CC BY-SA.

IntermediateFull instructions providedOver 1 day561
Design Kitchen Scale Based on Arduino

Story

Read more

Custom parts and enclosures

Thingiverse

https://www.thingiverse.com/thing:4551860

Schematics

Wiring

Wiring of the scale including zero power circuit

Code

Arduino sketch

Arduino
Sketch for the kitchen scale
/*  This sketch has been written by Rolf Jethon and is licensed under: 

                Creative commons License CC BY-SA
          
    The purpose of this sketch is to read a Weight cell using an HX711 Weight controller and show the weight on
    a HD44780 16 x 2 LCD display. 
    The following functions are implemented:
    - A tara  function - pressing S1 initiates a tara calibration.
    - A weight add function is implemented: Pressing S2 calculates a second tara. This is for example interesting
      for kitchen scales if you want to add a new ingredient to the existing ingredients. You see then both,
      the weight of the additional ingredient and the total weight. 
     -A calibration procedure that asks for a series of calibration weights in increasing order. The weight way
      points can be defined as desired, also the number of way points may be changed. More way points result in
      a better accuracy, though most sensors are quite linear. A calibration can be started by double klicking 
      onto S1 followed by double clicking onto S2
    - Manual shutdown function - doing a long press onto S2 switches off the scale - the power consumption is
      then zero - if the proposed hold circuit is in use.
    - For debug purposes (doubleclick S1 followed by long S1) the raw weight values will be shown on the display  
    Rolf Jethon
    Mail: rolf@jethon.de
    Web:  bechele.de
 */

//----------------------------------------------------------------------------
//--------------------- Configure below here ---------------------------------
// Scale configuration:
const byte scale_avg = 10;        // iteration reads of weight cell for operation mode
const byte cal_avg = 10;          // iteration reads of weight cell for calibration mode
const char weightunit[]="g";      // use grams in this case - you may use any unit you like unless the same unit is used during calibration
const float calib_weights[5] {0,100,1000,2000,5000}; // calibration steps for the weight - this are grams, but may be any other weight unit
                                                   // note that the number of weight calibration points and also the weights may be changed 
const byte aftercolon=1;          // number of digits after the colon to be displayed on the LCD
const long autooff=180000;        // time to automatically switch off, if no change happens - time in ms - 3 min
const int zerotolerance=40;       // raw values - if raw weight gets below tara - this value -9999.99 is displayed instead of 0 to indicate a bad tara
const int autooff_tolerance=100;  // tolerance raw weight that causes the switch off timer (offtimer) to reset - means changing the weigt for this
                                  // amount causes autooff to be delayed. This value depends on the scale measurment fluctuation !!
                                  
//-------------------------- do not change below here ------------------------
//----------------------------------------------------------------------------
#include <Wire.h>
#include <hd44780.h>                       // main hd44780 header
#include <hd44780ioClass/hd44780_I2Cexp.h> // i2c expander i/o class header
#include <Q2HX711.h>                       // load the weight cell library
#include <EEPROM.h>
#include <MD_KeySwitch.h>
#include <neotimer.h>
//HX711 scale;

hd44780_I2Cexp lcd; // declare lcd object: auto locate & auto config expander chip

// LCD geometry
const int LCD_COLS = 16;
const int LCD_ROWS = 2;

// HX711 pin assignment and setting:
const byte LOADCELL_DOUT_PIN = 4;
const byte LOADCELL_SCK_PIN = 5;
Q2HX711 hx711(LOADCELL_DOUT_PIN, LOADCELL_SCK_PIN);

// MD keyswitch pin assignments and settings:
const uint8_t Button1 = 7 ;
const uint8_t Button2 = 8 ;
const uint8_t SWITCH_ACTIVE= LOW; 
MD_KeySwitch S1(Button1,SWITCH_ACTIVE);
MD_KeySwitch S2(Button2,SWITCH_ACTIVE);

// power self holding pin assignment
const int Hold = 9 ;                 // pin for the power self holding - if pin gets 0 the scale switches off

const int a_elements=sizeof(calib_weights)/sizeof(float);        // calculates the number of weight calibration way points

//--------------------------global variable definition -----------------------
float tara1raw;                   // Rawweigt value after tara button
float tara2raw;                   // rawweight value after tara2  button
float rawweight;                  // raw measurement number from sensor
float old_read;                   // keeps the weight from the previous measurement  
bool tara_done;                   // indicates that a tara calib has jus been done to be able to cancel current measurement
bool tara2_inuse=false;           // controls, if the add weigt will be shown
char lcdbuf1[17];                 // LCD text buffer line 1
char lcdbuf2[17];                 // LCD text buffer line 2

// shutdown timer init
Neotimer offtimer= Neotimer(autooff);
//----------------------------------------------------------------------------
void setup()
{
  pinMode(Hold,OUTPUT);
  pinMode(Button1,INPUT_PULLUP);
  pinMode(Button2,INPUT_PULLUP);
  int status;
  status = lcd.begin(LCD_COLS, LCD_ROWS);
  if(status) // non zero status means it was unsuccesful
  {
    // hd44780 has a fatalError() routine that blinks an led if possible
    // begin() failed so blink error code using the onboard LED if possible
    hd44780::fatalError(status); // does not return
  }
  // initalization was successful, the backlight should be on now
  
  digitalWrite(Hold,HIGH);                        // keep battery power on (self holding)

  // initial settining of the key press library
  S1.begin();                                     // S1 single tara common + reset
  S1.enableLongPress(true);                       // S1 long = Info about s2 long (switch off)
  S1.enableDoublePress(true);                     // S1 double + S2 double = Kalib. Only S1 double - info regardin calib (+S2)
  S1.enableRepeat(false);
  S2.begin();                                     // S2 single = tara 2 (add weight function)
  S2.enableLongPress(true);                       // S2 long = switch scale off
  S2.enableDoublePress(true);                     // S2 double + S1 double= Kalib Only S2 double - info regarding kalib (+S1)
  S2.enableRepeat(false);
  S1.setLongPressTime(2000);
  S2.setLongPressTime(2000);
  S1.setDoublePressTime(500);
  S2.setDoublePressTime(500);
  S1.setDebounceTime(15);
  S2.setDebounceTime(15);
  // prepare the scale for display 
  read_avg(2,false);                              // tweak off initial inaccuracies
  tara();                                         // run tara on init once
  offtimer.start();                               // count down time
}
//------------------------------------------------------------------------------
//-------------- main loop - showing the weight values on the display ----------
void loop() {
  float addweight;
  float ee_calib[a_elements];
  float weight1;
  float weight2=0;
  float tara1_offset;
  float tara2_offset;
  char strbuf[14];
  bool below_zero;
  for(;;){ 
    if (offtimer.done()) {shut_down();}          // shutsown if the timer reaches the end
    EEPROM.get(0,ee_calib);                      // read the raw calibration values from eeprom
    tara1_offset=ee_calib[0]-tara1raw;        
    tara2_offset=ee_calib[0]-tara2raw;
    read_avg(scale_avg,true);                    // result in rawweight - read the scale 
    if(old_read+autooff_tolerance < rawweight||old_read-autooff_tolerance > rawweight){ // V V V
      reset_timer();                             // as long as values change (scale is in operation) keep it on
    }
    old_read=rawweight;
    rawweight+= tara1_offset;
    addweight = rawweight-tara1_offset+tara2_offset;
//------------ calculate the weight --------------------------------------------
    if(tara_done==false) {                       // do not continue an averaging calculation loop after a tara calibration
      for (byte i=0;i<a_elements;i++){
        if ( i==0 && rawweight < ee_calib[i]) {weight1=0; below_zero=false;}
        if ( i==0 && rawweight+zerotolerance < ee_calib[i]) {below_zero=true;}
        if ( rawweight >= (ee_calib[i]) && rawweight <= (ee_calib[i+1])){
          weight1=(rawweight - ee_calib[i])*(calib_weights[i+1]-calib_weights[i])/(ee_calib[i+1]-ee_calib[i])+calib_weights[i];
          below_zero=false;
        }     
        if ( addweight < ee_calib[0]) {weight2=0;}   
        if ( addweight >= (ee_calib[i]) && addweight <= (ee_calib[i+1])){
          weight2=(addweight - ee_calib[i])*(calib_weights[i+1]-calib_weights[i])/(ee_calib[i+1]-ee_calib[i])+calib_weights[i];
        }
      } 
//----------- display the weight on the LCD ------------------------------------     
      dtostrf(weight1,1,aftercolon,strbuf);
      strcpy(lcdbuf1,"Total: ");
      if (below_zero){strcat(lcdbuf1,"---");} else {strcat(lcdbuf1,strbuf);}
      strcat(lcdbuf1," ");
      strcat(lcdbuf1,weightunit);
      if (tara2_inuse==true) {
        dtostrf(weight2,1,aftercolon,strbuf);
        strcpy(lcdbuf2,"Add: ");
        strcat(lcdbuf2,strbuf);
         strcat(lcdbuf2," ");
        strcat(lcdbuf2,weightunit);       
      } else { strcpy(lcdbuf2,"");}
      lcdprint(lcdbuf1,lcdbuf2);
    } else { tara_done=false; }                 // enable weight measurement - because we start over after a tara calibration
  }   
}
//------------------ start function depending on key pressed ----------------
void key_check() {
  static boolean double_S2=false;
  static boolean double_S1=false;
  boolean long_S1=false;
  switch(S2.read())
  {
    case MD_KeySwitch::KS_NULL:       break;
    case MD_KeySwitch::KS_PRESS:      tara2_inuse=true; tara2raw=rawweight; double_S1=false; double_S2=false; reset_timer(); break; // 2nd tara for chain weigh
    case MD_KeySwitch::KS_DPRESS:     double_S2=true; reset_timer(); break;
    case MD_KeySwitch::KS_LONGPRESS:  shut_down();   break;
    default:                          break;
  }
  switch(S1.read())
  {
    case MD_KeySwitch::KS_NULL:       break;
    case MD_KeySwitch::KS_PRESS:      tara2_inuse=false; tara(); double_S1=false; double_S2=false; reset_timer(); break; // first tara resets all
    case MD_KeySwitch::KS_DPRESS:     double_S1=true; reset_timer(); break;
    case MD_KeySwitch::KS_LONGPRESS:  if(double_S1==false){note_shut();} long_S1=true; double_S2=false; reset_timer(); break;
    default:                          break;
  }
  if ( double_S1 && double_S2 ) {      // If both buttons got a double press, run the calibration
    double_S1=false; double_S2=false;
    calib();                     
  }
  if ( double_S1 && long_S1 ) {        // after a double S1, followed by a S1 long press, show the raw values
    show_raw();
  }
}
//---------------------- resetting the switch off timer ---------------------
void reset_timer() {
  offtimer.reset();
  offtimer.start();
}

//---------------------- Determine the raw tara value -----------------------
void tara() { 
  float avg;
  lcdprint("Calibrating","tara .....");
  read_avg(cal_avg,false);
  tara1raw=rawweight;
  tara_done=true;                       // breaks a incomplete weight measurement
}

//------------------- calibrate the scale -----------------------------------
void calib(void) {
  float cal_val[a_elements];            // space for the calibration results
  char buf[11];
  for (int i=0;i <a_elements;i++) {
    strcpy(lcdbuf1,"!! Calibstep ");
    itoa(i,buf,10);
    strcat(lcdbuf1,buf);
    strcpy(lcdbuf2,"put ");
    itoa(calib_weights[i],buf,10);
    strcat(lcdbuf2,buf);
    strcat(lcdbuf2,weightunit);
    strcat(lcdbuf2," -> S1");
    lcdprint(lcdbuf1,lcdbuf2);
    if (await_Sx() == false){
      lcdprint("Calib aborted","");
      delay(2000);
      reset_timer();
      return;
    }
    lcdprint("Measuring ...","");
    read_avg(cal_avg,false);
    cal_val[i]=rawweight;               // save the raw calibration point in an array
  }  
  EEPROM.put(0,cal_val);                // write the raw calibration steps to the EEprom
  lcdprint("Calib saved","");
  delay(2000);
  reset_timer();
  return;
}
//---------------------------------------------------------------------------
//----------------- Wait for keypress ---------------------------------------
bool await_Sx(void){
  for (;;){                            // run until S1 or S2 is pressed
    switch(S1.read())
    {
      case MD_KeySwitch::KS_PRESS:      return true;
    }
    switch(S2.read())
    {
      case MD_KeySwitch::KS_PRESS:      return false;
    }
  }
}
//---------------------------------------------------------------------------
//----------- shutdown procedure --------------------------------------------
void shut_down(void) {
  lcdprint("Bye !!","");
  delay(2000);
  digitalWrite(Hold,LOW);                        // switch battery power off
  delay(500);
  lcdprint("poweroff failed","remove battery !");
  while (1){}                                    // stay here and wait
}
//---------------------------------------------------------------------------
//----------- in case the user presses S2 long by fault ---------------------
void note_shut(void) {
  lcdprint("Long press S2","to shut down");  
  delay(2000);
}
//---------------------------------------------------------------------------
//------------ show the raw weigt until shutdown -------------------------
void show_raw(void){
  char strbuf[12];
  while (true){
    read_avg(scale_avg,false);  
    dtostrf(rawweight,1,aftercolon,strbuf);
    lcdprint("Raw Weight: ",strbuf);
    key_check();
    tara_done=false;
  }
}  
//---------------------------------------------------------------------------
//--- reads the weight cell and places an averaged raw value in rawweight----
void read_avg(byte num_avg,bool do_keycheck){
  while ( hx711.readyToSend() == false) {}
  rawweight = hx711.read();
  for (int i=0;i<num_avg;){
    if (do_keycheck) {key_check();}                   // Keycheck only in the operation mode, not during calibration
    if (tara_done==true){return 0;}                   // exit, because this is a continuation after a tara calibration
    if (hx711.readyToSend()) {
      i++;
      float reading = hx711.read();
      rawweight = (rawweight/num_avg*(num_avg-1)+reading/num_avg);     // creeping average
    }
  }
}
//---------------------------------------------------------------------------
//---------- output two strings to the LCD ----------------------------------
void lcdprint (char str1_pt[],char str2_pt[]) {
    lcd.home();
    lcd.print(str1_pt);
    lcd.print("                ");                   // fill with spaces - this works because internally the 44780 has 40 chars per line
    lcd.setCursor(0, 1); // bottom left
    lcd.print(str2_pt);
    lcd.print("                ");                   // this works only for the 16x2 display 
}

Credits

Rolf Jethon
2 projects • 0 followers
I'm in the mid 50s and build things since I remember mainly for my own purposes.

Comments