Thursday, October 28, 2010

Computer interface to a low cost electronic kitchen scale (part 1)

On a few occasions I was looking for a low cost electronic weigh scales with a computer interface. It seems they don't exist. There are electronic kitchen scales for as little as €15, but as soon as you require a computer interface you're into specialized scientific/point-of-sale equipment and the starting price jumps to €400+.

I had a spare low cost kitchen scale lying around that was purchased in Maplin (model KS108) a few years ago. Opening it up one can see the weight/force (also known as a "load cell") sensor is a cantilever beam anchored to the base of the unit with strain gauges glued to the top and bottom of the beam. The strain gauges are covered in a white epoxy for environmental protection. A weight (force) applied to the business end of the beam causes the beam to bend slightly causing the strain gauges on top to stretch and those on the bottom to compress.


A commonly used configuration comprises 4 strain gauges wired in a "full bridge" Wheatstone bridge. All four resistors of the bridge (see schematic further down) are strain gauges. Two on the top (R1, R4) and two on the bottom (R2, R3). This configuration provides good sensitivity to bending, and automatically compensates for resistance variation due to temperature.


The interface to the sensor comprises 4 wires: the red and black wires are are the 'excite' (labeled E+ and E- on the PCB) and white and blue are the sense wires (labeled S+ and S-).

The changes in resistance of individual strain gauges are too small to measure accurately by direct measurement. However in a Wheatstone bridge configuration the tiny changes in resistance causes the bridge to unbalance and measurable potential difference is generated on on the sense wires.

I was hoping to piggy back my own sensor electronics without interfering with the operation of the scale. Unfortunately it seems the scale's electronics was interfering with my ability to measure anything. So I cut the sensor wires and attached a simple plug / socket to allow the sensor to be connected to my system but still have the option to revert back to the original configuration if required.

After disconnecting the sensor from the scale's PCB I was able to measure a small voltage difference on the sense wires. But only 1.4mV when 1kg is applied. This is far too small to be measured directly by the ADC (each step of a 0–5V 10 bit ADC is about 5mV).

The two sense wires are connected to an op amp in differential configuration (a ST Microelectronics TS954 dual rail-to-rail op amp) set at moderately high gain produces a signal useable by an ADC.
Normally the Rg resistor is set set to the same value as Rf. In this configuration Vo is 0V when no force is applied to the sensor and can swing up the the supply rail (5V).

If Rg is omitted, at rest (zero force) the output from the op amp (Vo) is about 2.5V. Depending on how the sense wires are connected to the op amp, Vo can swing from 2.5V to 5V or from 2.5V down to 0V (the ground rail). So it seems we lose half of the ADC's dynamic range. However we can put this situation to good use.

Let's call the voltage change due to the application of force on the sensor as ΔV. Instead of wiring the excite wires to the power/ground rail, two digital IO output lines (D0 and D1 in the schematic) are used to excite the sensor. With D0 set to logic high (5V) and D1 set to logic low (0V) a measurement is taken: a0 = 2.5V + ΔV. By toggling the IO lines the excitation polarity is reversed and another measurement is taken: a1 = 2.5V - ΔV. The difference (Δa = a0 - a1) is 2.5V + ΔV - 2.5V + ΔV = 2ΔV. The measuring resolution is doubled. Another advantage of this setup is that is helps eliminate common mode interference.

A word of caution: one must allow sufficient time for the signal to settle after changing the excitation polarity. As can be seen from the oscilloscope trace (taken at Vo where a grid square = 200µs x 1V) at least 500µs is required for the sense voltage to settle after a reverse in direction.



To illustrate this noise reduction in action, I deliberately introduced mains noise by pinching one of the sense wires with my fingers. I wrote a Arduino sketch to take 300 samples with alternating polarities (and allowing 750µs settling time). In the chart below, one can see a0 and a1 have 50Hz mains noise of amplitude ~ 15 ADC units, but the noise in Δa has an amplitude of only ~ 5 ADC units.


Calibration

Ideally a calibration weight kit (expensive!) is used for calibration. But this is not intended to be a super accurate scales. I am aiming for ±0.5g. For that resolution there is a calibration kit (literally) in your pocket: coins are minted to precise specifications. Freshly minted coins are best, but any clean coin in reasonable condition will suffice for this purpose.

The following calibration chart was obtained by mounting Euro 10c (4.10g), 20c (5.74g) and 50c (7.80g) coins on the scales.

Using Gnuplot to plot and curve fit the data to the equation of a line: Δa(x) = c + mx
where x is the mass in grams applied to the scale.

Gnuplot curve fit reports:
c               = 17.3111          +/- 0.09565      (0.5525%)
m               = 2.49864          +/- 0.0006569    (0.02629%)


As can be seen from the residuals plot there is excellent linearity (the residuals are random and ranging from -0.8 to +0.8 ADC units with a root-mean-square of just 0.376). The slope or sensitivity (m) has a value of 2.49864 (let's call that 2.5) which means that each gram added registers a Δa of 2.5 ADC units, or each ADC increment is 1/2.5 = 0.4g. Combined with the noise (the spread in residuals) and it seems we are safely in the ±0.5g area.

An Arduino was used for the initial prototyping. The following sketch (below) excites the sensor in both direction, takes a time averaged ADC of Δa and spits it out on the serial port.

In part 2 I will discuss the interfacing to a computer, the computer side software and possible applications.



/**
 * Take measurement from weigh scales sensor and output to serial
 * port. By reversing polarity of excite signal can reduce common
 * mode noise. Take 256 samples and get mean before outputing 
 * result -- this helps reduce noise and adds extra resolution.
 * Joe Desbonnet, 28 Oct 2010. jdesbonnet@gmail.com. 
 */
 
 // Define bridge excite digital IO lines
#define D0 3
#define D1 2

// Use ADC port 0
#define AN0 0
 
// Delain in microseconds to allow signal to settle 
// after excite polarity reversal
#define DELAY 800

void setup()   {
  
  Serial.begin(115200); 
  
  pinMode(D0, OUTPUT);
  pinMode(D1, OUTPUT);
}

void loop()                     
{
  
  // Get zero point ('tare') of scale
  int tare = measure ();
  
  while (true) {
     Serial.println ( measure() - tare );
     delay (1000);
  }
  
}

int measure () {
  int i,a0,a1;
  long s;
  for (i = 0; i < 256; i++) {
    digitalWrite (D0,HIGH);
    digitalWrite (D1,LOW);
    delayMicroseconds(DELAY);
    a0 = analogRead(AN0);
     
    // reverse polarity
    digitalWrite (D0,LOW);
    digitalWrite (D1,HIGH);
    delayMicroseconds(DELAY);
    a1 = analogRead(AN0);
    
    s += (long) (a0 - a1);
      
   } // next i
   
   // Don't care about polarity
   if ( s < 0) {
       s = -s;
   }
   
   // As there is a large number of samples being averaged we
   // can probably extract another bit or two from the ADC.
   // Going to be conservative and going for one extra bit.
   // So instead of dividing by 256, will divide by just 128
   // instead (ie right shift 7 bits).
   return s>>7;
}