1 / 6
Description
This kitchen scale can be printed on an ordinary 3d (FDM) printer. The project consists of 3D printed mechanics, electronics and software. Here you can see the software features of the scale:
Wiring diagrams,software,3d print data and instructions can be downloaded for free: Software wirings and build instructions on: https://bechele.de/?page_id=435 3D construction data and 3D STL files on: https://www.thingiverse.com/thing:4551860 The complete project is published under the following License: Creative commons License CC BY-SA
1 / 8
/* 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
}
2 projects • 0 followers
I'm in the mid 50s and build things since I remember mainly for my own purposes.






Comments