/*
Name: DigitalScaleAutoCal.ino
Created: 11/15/2018 4:49:12 PM
Author: FRANKWIN10\Frank
This program is a modification of the November 2016 circuits4you.com program for interfacing
with the HX711 Module coupled with a typical load cell. It's purpose is to automatically
calibrate the combination to accurately display weight in Kg.
The process is as follows:
Step1: User opts to calibrate by entering 'Y' at the prompt. Entering any other key (or
just letting the question time out) will skip the calibration process
Step2: User establishes 'tare' weight physical configuration with any non-measured weight
attached (in my case, the bucket used to hold measured amounts of water used for
weight calibration), and then presses 'T' to zero out the 'tare weight'
Step3: The user establishes a calibration weight condition (in my case, 1.8 liters of water)
and enters the weight value (in Kg). The program then calculates the scale factor
needed to calibrate the scale for the entered weight and then automatically
drops into measurement mode
Step5: The user enters 'q' to quit measurements.
*/
/*
* circuits4you.com
* 2016 November 25
* Load Cell HX711 Module Interface with Arduino to measure weight in Kgs
Arduino
pin
2 -> HX711 CLK
3 -> DOUT
5V -> VCC
GND -> GND
Most any pin on the Arduino Uno will be compatible with DOUT/CLK.
The HX711 board can be powered from 2.7V to 5V so the Arduino 5V power should be fine.
*/
#include "HX711.h" //You must have this library in your arduino library folder
#include <EEPROM.h>
#include<EEPROMAnything.h> //so I can store the last calibration value in EEPROM
#include <PrintEx.h> //allows printf-style printout syntax
#include <elapsedMillis.h>
StreamEx mySerial = Serial; //added 03/18/18 for printf-style printing
elapsedMillis sinceLastWaitMsg;
const int WAITINGMSGINTERVALMSEC = 1000; //1-sec intervals
const int MAX_WAITING_INTERVALS = 10; //how long to wait before starting measurement
int numWaitIntervals = 0;
char inbuf[20]; //buffer for user input
#define DOUT A1
#define CLK A0
HX711 scale(DOUT, CLK);
//Change this calibration factor as per your load cell once it is found. You many need to vary it in thousands
long calibration_factor = -96650; //-106600 worked for my 40Kg max scale setup
long tare_offset = 0;
bool bDoneMeasuring = false;
const int LED_PIN = 13;
const char* waitstr = "Waiting...Measurements will start in ";
const int CAL_FACTOR_EEPROM_ADDR = 0;
const int TARE_OFFSET_EEPROM_ADDR = CAL_FACTOR_EEPROM_ADDR + sizeof(calibration_factor);
void setup()
{
delay(2000); //required to wait for Pro Micro to switch from programming to output serial port assignments
Serial.begin(115200);
pinMode(LED_BUILTIN, OUTPUT);
memset(inbuf, 0, sizeof(inbuf));
mySerial.printf("Digital Scale Program\n");
mySerial.printf("Program to automatically calibrate HX711/Load Cell to measure weight in Kg\n");
EEPROM_readAnything(CAL_FACTOR_EEPROM_ADDR, calibration_factor); //read current cal scale from EEPROM
mySerial.printf("The current stored scale calibration factor is %li\n", calibration_factor);
if (isnan(calibration_factor))
{
mySerial.printf("invalid cal factor: Setting to -96650\n");
calibration_factor = -96650;
}
scale.set_scale(calibration_factor);
EEPROM_readAnything(TARE_OFFSET_EEPROM_ADDR, tare_offset); //read current tare offset from EEPROM
mySerial.printf("The current stored tare offset factor is %li\n", tare_offset);
if (isnan(tare_offset))
{
mySerial.printf("invalid cal factor: Setting to zero\n");
tare_offset = 0;
}
scale.set_offset(tare_offset);
mySerial.printf("Enter 'Y' to start the calibration, any other key to skip\n");
mySerial.printf("Calibrate (Y/N) (Y)?\n");
if (GetUserInputStr(inbuf, 5)) //returns true if strlen(inbuf) > 0
{
mySerial.printf("\nReceived "); Serial.print(inbuf); Serial.println(" from user");
if (strncasecmp("Y", inbuf, 1) == 0) //compare only 1st chars, as user input may have CR/LF appended
{
//calibrate with known weights
Serial.println("HX711 Calibration");
Serial.println("Remove all non-fixture weight from scale, and press 't' to zero the scale");
Serial.println("Or any other character to skip this step");
memset(inbuf, 0, sizeof(inbuf));
if (GetUserInputStr(inbuf, 10)) //returns true if strlen(inbuf) > 0
{
mySerial.printf("\nReceived "); Serial.print(inbuf); Serial.println(" from user");
if (strncasecmp("T", inbuf, 1) == 0) //compare only 1st chars, as user input may have CR/LF appended
{
tare_offset = scale.read_average(); //Get a baseline reading
mySerial.printf("Applying offset %li as tare adjustment\n", tare_offset);
scale.set_offset(tare_offset); //this value will be subtracted from all future measurements
mySerial.printf("Writing offset %li to EEPROM\n", tare_offset);
EEPROM_writeAnything(TARE_OFFSET_EEPROM_ADDR, tare_offset);
}
}
Serial.println("Apply a known weight (the higher the better) and enter its value in Kg");
Serial.println("Enter 0 to skip this step");
memset(inbuf, 0, sizeof(inbuf));
if (GetUserInputStr(inbuf, 30)) //returns true if strlen(inbuf) > 0
{
mySerial.printf("\nReceived "); Serial.print(inbuf); Serial.println(" from user");
//calculate scale factor resulting in user-input wt
//wt(Kg) = (read_avg - OFFSET)/Scale
//Scale = (read_avg - OFFSET)/wt(Kg)
double wtKg = atof(inbuf);
mySerial.printf("User weight input was %4.2f\n", wtKg);
long read_avg = scale.read_average();
long offset = scale.get_offset();
calibration_factor = (double)(read_avg - offset) / wtKg;
scale.set_scale(calibration_factor); //Adjust to this calibration factor
mySerial.printf("User weight input was %4.2f, read_avg = %li, offset = %li, cal = ",
wtKg, read_avg, offset);
Serial.println(calibration_factor); //PrintEx won't print the cal factor value
EEPROM_writeAnything(CAL_FACTOR_EEPROM_ADDR, calibration_factor);
Serial.print("Wrote cal factor "); Serial.print(calibration_factor); //mySerial.printf doesn't work for cal factor
mySerial.printf(" to EEPROM address %d\n",CAL_FACTOR_EEPROM_ADDR);
}
}
}
mySerial.printf("\n\nCalibration Complete... Starting Measurements\n\n");
mySerial.printf("\nTo quit measuring, enter 'Q'\n");
mySerial.printf("Time(mSec)\t\Wt(Kg)\n");
bDoneMeasuring = false;
while (!bDoneMeasuring)
{
mySerial.printf("%lu\t%3.2f\n", millis(), scale.get_units(3));
delay(250);
if (Serial.available())
{
char temp = Serial.read();
mySerial.printf("got %c\n", temp);
if (temp == 'q')
{
bDoneMeasuring = true; //all done
}
}
}
mySerial.printf("Measurements Completed - Have a nice day!\n");
}
void loop()
{
}
bool GetUserInputStr(char* inbuf, int numWaitIntervals)
{
//Purpose: Get user input
//Inputs:
// inbuf = pointer to char[10] array to receive user input
//Outputs:
// inbuf = String object containing user input. If inbuf.length() == 0, then the routine timed out
// returns True if user input was received, False if the routine timed out
//Plan:
// Wait for user input or timeout whichever occurs first
int intervalCount = 0;
while (strlen(inbuf) <= 0 && intervalCount < numWaitIntervals)
{
Serial.readBytesUntil('\n', inbuf, 10);
if (sinceLastWaitMsg >= WAITINGMSGINTERVALMSEC)
{
sinceLastWaitMsg -= WAITINGMSGINTERVALMSEC;
intervalCount++;
mySerial.printf("%s %d\n", waitstr, numWaitIntervals - intervalCount);
}
delay(250);
}
return (strlen(inbuf) > 0);
}