SparkFun Forums 

Where electronics enthusiasts find answers.

Have questions about a SparkFun product or board? This is the place to be.
By stix
#199779
Hi guys, my first post here and apologies if I ask too many questions and get a few things wrong. 8-)

I've got the Openscale connected to a load cell, done the basic setup and it all seems to be working well enough. However, I'd like to be able to format the output data my way. There doesn't seem to be a great deal of information when searching the web.

What I need is for the data to be output without any formatting.

This is what I'm getting: 1.000,kg,
What I need is: 1000
Meaning 1000 grams with no delimiters etc. I can't see that I can do this using the basic commands provided.

I've written some code before for Arduino, and from what I understand code can be uploaded to the Openscale using the Arduino IDE. Also there's the issue of it not remembering TARE, (although it will remember calibration). I did look at the bogde/HX711 library code but I got errors in the Arduino IDE.

I don't need or want the user to be able to change anything (I will do the setup/calibration), I just want it to spit out data in grams at 80Hz (which I believe is the limitation.

I have a similar device using an Arduino Uno attached to an analog load cell amplifier and load cell. The code for this is about 10 lines long - very simple. The device is used with software I have already written in Xojo.

The Openscale seems more appealing than the Arduino+Amp method I'm already using because it's all in the one unit - very nice.

Your thoughts much appreciated.
Cheers.
User avatar
By languer
#199802
I don't have the OpenScale system but after reading the info from SFE it seems the system is based on the Load Cell Amplifier HX711. The HX711 SFE library seems pretty straight forward to use. Look at the SFE examples from it and I would start by using them. Also, if you look at the original HX711 library you can get a good idea on how they intended to use the library. Hope this helps.
User avatar
By languer
#199816
I think the code below should get you going (from a simplicity point of view). It is taken almost verbatim from SFE's HX711 examples. The Openscale system is more elaborate and has eeprom to store things, user interface, temp sensor, etc. The code below is just an attempt to simplify things. I don't really have an Openscale to try this on but I think it should work.
Code: Select all
/*
 Example HX711 code using OpenScale 

 This example demonstrates basic scale output. See the calibration sketch to get the calibration_factor for your
 specific load cell setup.
 
 Arduino pin 
 3 -> HX711 CLK (modified to work with open scale, use pin 2 for HX711 breakout)
 2 -> DAT (modified to work with open scale, use pin 3 for HX711 breakout)
 5V -> VCC
 GND -> GND
  
*/

#include "HX711.h"

#define calibration_factor -7050.0 //This value is just a guess for this example

#define UNITS_KG  0
#define UNITS_LBS 1

#define DOUT  3
#define CLK  2

HX711 scale(DOUT, CLK);

void setup() {
  Serial.begin(9600);
  Serial.println("OpenScale demo");

  scale.set_scale(calibration_factor); // This value was the gues from before
  scale.tare();	//Assuming there is no weight on the scale at start up, reset the scale to 0

  Serial.println("Readings:");
}

void loop() {
  Serial.print("Reading: ");
  Serial.print(scale.get_units(), UNITS_LBS); //scale.get_units() returns a floatl units will be in LBS
  Serial.print(" lbs"); //You can change this to kg but you'll need to refactor the calibration_factor, or this can be omitted alltogether
  Serial.println();
}
By stix
#199853
Thanks Languer, I couldn't get that code to work - all I get is zero's.

However, I was able to get very close to what I need by editing some firmware code (Nathan Seidle 2014, using the bogde library). I was able to remove all the formatting, then I multiplied currentReading by 100 to convert kg readings to grams:
Code: Select all
Serial.print((currentReading*100), setting_decimal_places);
Now that the readings are in grams, I am able to use my software to record the force coming in from the load cell:

Image

You can see that 1kg is applied over a period of approx. 1 minute. Unfortunately there are a few "random glitches" as can be seen in the graph. The random readings only last for one sample, some negative 27 kilos to positive 28 kilos. This is unacceptable. I can't see how this is a code problem, but hopefully I'm wrong.

Perhaps something to do with gain? but I can't find much details about changing it.

Anyone with any ideas as to why the "random glitches" appear?

Cheers.
User avatar
By languer
#199860
The simple program I provided works. I had a mistake on the #define (which I corrected on the comment section, but missed on the actual #define). Now that I got an OpenScale I was able to try it.
you can see it working by changing
Code: Select all
#define DOUT  3
#define CLK  2
to
Code: Select all
#define DOUT  2
#define CLK  3
By stix
#199866
I confirm that changing the code to:
Code: Select all
#define DOUT  2
#define CLK  3
Now it works, and the code posted above by Languer now shows a valid reading.

So, I went back and looked at some other code examples that I downloaded that didn't work either, and the SAME error was there. Amazing!!

The link to the code below (I believe) is the original code that does work (sort of). Is it the code uploaded as "factory settings"?... If not, what is it, or where do I find it?
https://github.com/sparkfun/OpenScale/b ... nScale.ino

What I did was start from scratch using the basic code that at least reads the data (Languer). I then reviewed and copied some bits that I needed from the code in the link above (Nathan Seidle/Bogde) and finally got to a point that I now have the device recording at 80Hz with NO glitches, and a 5min recording test is pretty much perfect!! and the data is spilling out in grams :)

It's also worth pointing out that the instructions on to how to "cut the link" to convert to 80Hz sample rate is sadly inadequate. DO NOT attempt this with a hobby knife otherwise you can easily ruin the board. Use your soldering iron (you do have one don't you?) with a de-soldering braid/wick to remove the solder and you are left with two clean pads as below (you can always re-solder to get back to 10Hz).

Oh yeah, also I think the smaller HX711 breakout board has the pad links running vertical, so forget the knife and de-solder just in case.

Image

A pedestal magnifier with led lights is a great help when dealing with surface mount components (especially with poor eyesight) as you do when you are 55+.

Cheers.
User avatar
By languer
#199868
Glad you have it working. Below is the code I played with over the weekend.
Code: Select all
/*
  Example HX711 code using OpenScale
  @languer 2018

  This example demonstrates basic scale output. See the calibration sketch to get the calibration_factor for your
  specific load cell setup.

  Arduino pin
  3 -> HX711 CLK (modified to work with open scale, use pin 2 for HX711 breakout)
  2 -> DAT (modified to work with open scale, use pin 3 for HX711 breakout)
  5V -> VCC
  GND -> GND

    Board: Arduino/Genuino Uno
    Programmer: AVR ISP
*/

/************************* HX711 Setup *********************************/
#include "HX711.h"
#define RESOLUTION  6     //decimal places of HX711 reading
#define AVG_FACT 8        //averagin factor of HX711 reading
#define DOUT  2           //data IO of HX711
#define CLK  3            //clock input of HX711
HX711 scale(DOUT, CLK);

/************************* EEPROM Setup *********************************/
#include <EEPROM.h>
#define EEP_CAL_FLAG_ADDR  0      //eeprom calibration flag address, flag occupies 1 byte
#define EEP_CAL_DATA_ADDR  1      //eeprom calibration data address; data occupies 4 bytes
byte eepCalFlag = 0;
long calibrationFactor_l = 535; //initial guess (for 0-5kg uxcell cell)


/************************* SETUP *********************************/
void setup() {
  Serial.begin(9600);
  Serial.println("OpenScale demo");
  calibrateOpenScale();  // calibrate scale
  Serial.println("Take OpenScale Readings");  // read scale
  delay(2500);
}


/************************* MAIN *********************************/
void loop() {
  readOpenScale();
}


/************************* HX711 CALIBRATION *********************************/
void calibrateOpenScale() {
  Serial.println("OpenScale calibration sketch");
  Serial.println("Remove all weight from scale");
  delay(5000);
  Serial.println("After readings begin, place known weight on scale");
  Serial.println("Press a to increase calibration factor by 10");
  Serial.println("Press d to increase calibration factor by 100");
  Serial.println("Press g to increase calibration factor by 1000");
  Serial.println("Press z to decrease calibration factor by 10");
  Serial.println("Press c to decrease calibration factor by 100");
  Serial.println("Press b to decrease calibration factor by 1000");
  Serial.println("Press x to exit calibration");
  delay(5000);

  //initialize calibration
  /* --- START UNTESTED ---
  eepCalFlag = EEPROM.read(EEP_CAL_FLAG_ADDR);
  //get cal value from eeprom if exists
  if (eepCalFlag == 128) {
    EEPROM.get(EEP_CAL_DATA_ADDR, calibrationFactor_l);
  }
  --- END UNTESTED ---*/
  float calibrationFactor_f = calibrationFactor_l;

  scale.set_scale();  //reset scale to default (0)
  scale.tare();	//reset the scale to 0
  long zero_factor = scale.read_average(); //Get a baseline reading

  Serial.print("Zero factor: "); //This can be used to remove the need to tare the scale. Useful in permanent scale projects.
  Serial.println(zero_factor);

  boolean calibrateFlag = true;
  while (calibrateFlag) {
    scale.set_scale(calibrationFactor_f);  //Adjust to this calibration factor
    Serial.print("Reading (grams, raw, cal factor): ");
    Serial.print(scale.get_units(4), 3);
    Serial.print(" , ");
    Serial.print(scale.read_average());
    Serial.print(" , ");
    Serial.print(calibrationFactor_f);
    Serial.println();

    if (Serial.available()) {
      char temp = Serial.read();
      if (temp == 'a') {
        calibrationFactor_f += 10;
      }
      else if (temp == 'd') {
        calibrationFactor_f += 100;
      }
      else if (temp == 'g') {
        calibrationFactor_f += 1000;
      }
      else if (temp == 'z') {
        calibrationFactor_f -= 10;
      }
      else if (temp == 'c') {
        calibrationFactor_f -= 100;
      }
      else if (temp == 'b') {
        calibrationFactor_f -= 1000;
      }
      else if (temp == 'x') {
        calibrateFlag = false;
      }
    }
  }

  //exit calibration and store calibration in EEPROM
  {
  /* --- START UNTESTED ---
    eepCalFlag = 128;
    EEPROM.write(EEP_CAL_FLAG_ADDR, eepCalFlag);
    calibrationFactor_l = calibrationFactor_f;
    EEPROM.put(EEP_CAL_DATA_ADDR, calibrationFactor_l);
  --- END UNTESTED --- */

    Serial.println("Exiting calibration sequence");
  }
}


/************************* READ HX711 *********************************/
void readOpenScale() {
  Serial.print("Reading: ");
  Serial.print(scale.get_units(AVG_FACT), RESOLUTION); //scale.get_units() returns a float
  Serial.print(" grams");  //grams or kilograms depending on load cell
  Serial.println();
  delay(1000);
}
User avatar
By languer
#199898
Final example code (stores the calibration in EEPROM).
Code: Select all
/*
  Example HX711 code using OpenScale
  @languer 2018

  This example demonstrates basic scale output. See the calibration sketch to get the calibration_factor for your
  specific load cell setup.

  Arduino pin
  3 -> HX711 CLK (modified to work with open scale, use pin 2 for HX711 breakout)
  2 -> DAT (modified to work with open scale, use pin 3 for HX711 breakout)
  5V -> VCC
  GND -> GND

    Board: Arduino/Genuino Uno
    Programmer: AVR ISP
*/

/************************* HX711 Setup *********************************/
#include "HX711.h"
#define RESOLUTION  6     //decimal places of HX711 reading
#define AVG_FACT 8        //averagin factor of HX711 reading
#define DOUT  2           //data IO of HX711
#define CLK  3            //clock input of HX711
HX711 scale(DOUT, CLK);

/************************* EEPROM Setup *********************************/
#include <EEPROM.h>
#define EEP_CAL_FLAG_ADDR  0      //eeprom calibration flag address, flag occupies 1 byte
#define EEP_CAL_DATA_ADDR  1      //eeprom calibration data address; data occupies 4 bytes
byte eepCalFlag = 0;
long calibrationFactor_l = 535; //initial guess (for 0-5kg uxcell cell)


/************************* SETUP *********************************/
void setup() {
  Serial.begin(9600);
  Serial.println("OpenScale demo");
  calibrateOpenScale();  // calibrate scale
  Serial.println("Take OpenScale Readings");  // read scale
  delay(2500);
}


/************************* MAIN *********************************/
void loop() {
  readOpenScale();
}


/************************* HX711 CALIBRATION *********************************/
void calibrateOpenScale() {
  Serial.println("OpenScale calibration sketch");
  Serial.println("Remove all weight from scale");
  delay(5000);
  Serial.println("After readings begin, place known weight on scale");
  Serial.println("Press a to increase calibration factor by 10");
  Serial.println("Press d to increase calibration factor by 100");
  Serial.println("Press g to increase calibration factor by 1000");
  Serial.println("Press z to decrease calibration factor by 10");
  Serial.println("Press c to decrease calibration factor by 100");
  Serial.println("Press b to decrease calibration factor by 1000");
  Serial.println("Press x to exit calibration");
  delay(5000);

  //initialize calibration
  eepCalFlag = EEPROM.read(EEP_CAL_FLAG_ADDR);
  //get cal value from eeprom if exists
  if (eepCalFlag == 128) {
    EEPROM.get(EEP_CAL_DATA_ADDR, calibrationFactor_l);
  }
  float calibrationFactor_f = calibrationFactor_l;

  scale.set_scale();  //reset scale to default (0)
  scale.tare();	//reset the scale to 0
  long zero_factor = scale.read_average(); //Get a baseline reading

  Serial.print("Zero factor: "); //This can be used to remove the need to tare the scale. Useful in permanent scale projects.
  Serial.println(zero_factor);

  boolean calibrateFlag = true;
  while (calibrateFlag) {
    scale.set_scale(calibrationFactor_f);  //Adjust to this calibration factor
    Serial.print("Reading (grams, raw, cal factor): ");
    Serial.print(scale.get_units(4), 3);
    Serial.print(" , ");
    Serial.print(scale.read_average());
    Serial.print(" , ");
    Serial.print(calibrationFactor_f);
    Serial.println();

    if (Serial.available()) {
      char temp = Serial.read();
      if (temp == 'a') {
        calibrationFactor_f += 10;
      }
      else if (temp == 'd') {
        calibrationFactor_f += 100;
      }
      else if (temp == 'g') {
        calibrationFactor_f += 1000;
      }
      else if (temp == 'z') {
        calibrationFactor_f -= 10;
      }
      else if (temp == 'c') {
        calibrationFactor_f -= 100;
      }
      else if (temp == 'b') {
        calibrationFactor_f -= 1000;
      }
      else if (temp == 'x') {
        calibrateFlag = false;
      }
    }
  }

  //exit calibration and store calibration in EEPROM
  {
    eepCalFlag = 128;
    EEPROM.write(EEP_CAL_FLAG_ADDR, eepCalFlag);
    calibrationFactor_l = calibrationFactor_f;
    EEPROM.put(EEP_CAL_DATA_ADDR, calibrationFactor_l);

    Serial.println("Exiting calibration sequence");
  }
}


/************************* READ HX711 *********************************/
void readOpenScale() {
  Serial.print("Reading: ");
  Serial.print(scale.get_units(AVG_FACT), RESOLUTION); //scale.get_units() returns a float
  Serial.print(" grams");  //grams or kilograms depending on load cell
  Serial.println();
  delay(1000);
}


Paired it with the following uXcell load cell sensor: https://www.amazon.com/dp/B006W2IDUO/re ... _ttl_sol_0
By stix
#199899
Thanks Languer.

If I wanted to use an external clock with your code, how would the code know where the clock source comes from? I imagine it comes from defining "CLK" but I haven't read any other info that shows hows it's done.

I don't expect you to "spoon feed" me the solution, but can you point me in the right direction for me to look at.

Cheers.
User avatar
By languer
#199914
If you look at the datasheet of the HX711 (sh 4, under Clock Source Options) you see you can drive this externally.
from HX711 datasheet:
By connecting pin XI to Ground, the on-chip oscillator is activated. The nominal output data rate when using the internal oscillator is 10 (RATE=0) or 80SPS (RATE=1). If accurate output data rate is needed, crystal or external reference clock can be used. A crystal can be directly connected across XI and XO pins. An external clock can be connected to XI pin, through a 20pF ac coupled capacitor. This external clock is not required to be a square wave. It can come directly from the crystal output pin of the MCU chip, with amplitude as low as 150 mV.
The output data rate is set by the equations on sh 3 of the same datasheet. In the case of the higher sampling/output rate: Fclk/138,240. For a 100Hz output rate, this comes out to an external clock of 13.824 MHz. That is not a normal value you typically find, so using a value of 16MHz is probably the easiest. If you look at the OpenScale schematic you see the ATMEGA MCU already uses 16MHz clock. Probably the simplest thing is to replace that crystal with a clock (this one is an example) and feed that clock to both the ATMEGA and the HX711. Another option (not sure how likely it is) would be to try to split the ATMEGA oscillator to also feed the HX711 clock input. Another option may be to drive the HX711 clock input with some clock output from the ATMEGA (a quick search on the ATMEGA did not immediately show an ATMEGA clk output pin so I am not sure how likely this option is).
User avatar
By languer
#199937
languer wrote: Wed Aug 01, 2018 11:59 pm Another option (not sure how likely it is) would be to try to split the ATMEGA oscillator to also feed the HX711 clock input.
Turns out the ATMEGA328 has the capability to output the system clock into pin PB0. To do this you have to burn a new bootloader with the CLKOUT fuse set. Then you need to connect pin PB0 on the ATMEGA328 to the XI input on the HX711 through a 20pF cap ("An external clock can be connected to XI pin, through a 20pF ac coupled capacitor"). The XI pin is currently grounded so you will need to lift it. Totally doable, but nothing ready to just plug and play. Some guidance is found on the following link: https://forum.arduino.cc/index.php?topi ... #msg915490
By stix
#199951
Thanks Langue. At this point I've had to make some priority decisions, therefore I've gone back to working on my overall software solution. I'm currently writing a help system which is taking waaaay.... too long, (2 months so far) and I'm about 1/3rd of the way through it!!!

When that is finalized, I'll re-look at this because it would be great to get 100Hz out of the OpenScale. I'll need to get my brother involved as he is pretty good with electronics. For the most part, my area is with the software side of things.

Thanks for all your help and I'll get back to this when I can.

Cheers, Steve.
By stix
#200422
Hi Langue, it's been a while.

Nice work on the calibration code.

How would you go about writing the new calibration factor to the EEPROM permanently without using the Arduino IDE.? I've had a bit of a fiddle and everything works fine until the openscale is disconnected, then it starts up with the original calibration.

What I'm asking is can you use an external app to write to the EEPROM?
User avatar
By languer
#200437
I think you are asking if you can write to the Arduino EEP (i.e. the ATMEGA 328P EEP) externally. The short answer is that there is no connection between the external world and the internal EEP of the ATMEGA IC. Having said that, it seems you could "build" your interface within the Arduino code and perhaps that would work for what you need. Following are a few options that may help:

option 1)
On the input terminal screen you can build your self the option to calibrate or to skip calibration. This is, once the device is calibrated the user can choose to skip calibration. The user would have to choose this every time the openscale is powered-up though.

option 2)
You can assign a discrete input which is checked upon power up. If the discrete input is low (or high - depending how you want to configure it), the calibration sequence is run, otherwise the calibration sequence is skipped.

option 3)
You can add a timing sequence on power-up that scans the serial input for a calibration command. If the command is received within your predetermined time then the calibration sequence is engage, otherwise normal program operation continues.

These are just a few examples that come to mind. Note that the "calibration sequence" I reference above can be an actual calibration sequence, or if you just want to store a set predetermined cal factors you can also do that (option 2 and option 3 would be better suited for the latter).