Skip to content

Commit bae3422

Browse files
authored
Miscellaneous improvements in HX711 (#451)
1 parent 78dfbda commit bae3422

File tree

3 files changed

+95
-38
lines changed

3 files changed

+95
-38
lines changed

devices/Hx711/GainLevel.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,16 @@ public enum GainLevel : byte
2020
/// <summary>
2121
/// Gain factor of 32. Channel B.
2222
/// </summary>
23-
Gain32 = 0b1010_0000,
23+
GainB32 = 0b1010_0000,
2424

2525
/// <summary>
2626
/// Gain factor of 64. Channel A.
2727
/// </summary>
28-
Gain64 = 0b1010_1000,
28+
GainA64 = 0b1010_1000,
2929

3030
/// <summary>
3131
/// Gain factor of 128. Channel A.
3232
/// </summary>
33-
Gain128 = 0b1000_0000
33+
GainA128 = 0b1000_0000
3434
}
3535
}

devices/Hx711/Hx711.Sample/Program.cs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,16 @@
1111
// When connecting to an ESP32 device, need to configure the SPI GPIOs
1212
// The following mapping is used in order to connect the WEIGTH module
1313
// to a M5Core device using the Grove port A
14-
// MOSI: connects to Grove pin 1
15-
// MISO: connects to Grove pin 2
14+
// MOSI: connects to Grove pin 1 (PD_SCK)
15+
// MISO: connects to Grove pin 2 (DOUT)
1616
// CLOCK: connect to any free port as it's not used at all
1717
Configuration.SetPinFunction(21, DeviceFunction.SPI1_MOSI);
1818
Configuration.SetPinFunction(22, DeviceFunction.SPI1_MISO);
1919
Configuration.SetPinFunction(23, DeviceFunction.SPI1_CLOCK);
2020

2121
// setup SPI connection settings
2222
// the clock value was adjusted in order to get the typical duration expected by the PD_SCK ~1us
23-
var spisettings = new SpiConnectionSettings(2, 19)
23+
var spisettings = new SpiConnectionSettings(1, 19)
2424
{
2525
ClockFrequency = Scale.DefaultClockFrequency
2626
};
@@ -32,17 +32,27 @@
3232
var scale = new Scale(spidev);
3333

3434
// power up WEIGHT module
35-
scale.PowerUp();
35+
// select channel A with gain 64
36+
scale.PowerUp(GainLevel.GainA64);
37+
// select channel A with gain 128 for 2 times better scale resolution but more noise too
38+
//scale.PowerUp(GainLevel.GainA64);
3639

40+
// set avaraging to 10 samples for tare
41+
scale.SampleAveraging = 10;
3742
// set scale tare to get accurate readings
3843
scale.Tare();
3944

45+
// set avaraging to 3 samples for loop sampling
46+
scale.SampleAveraging = 3;
47+
48+
//example gramm value to convert measurments to actual weight
49+
double gramm_unit = 220.23;
4050
// loop forever outputting the current reading
4151
while (true)
4252
{
4353
var reading = scale.Read();
4454

45-
Console.WriteLine($"Weight: {reading}");
55+
Console.WriteLine($"Read value: {reading} Weight: {reading / gramm_unit} gramm");
4656

4757
Thread.Sleep(2_000);
4858
}

devices/Hx711/Hx711.cs

Lines changed: 77 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ public class Scale
1616
// pulse train required to read a sample and setup gain factor for next reading
1717
private readonly byte[] _readSamplePulseTrain;
1818

19+
// sample buffer to hold data read from DOUT
20+
private readonly byte[] _readSampleBuffer;
21+
22+
//setup Dout wait buffers
23+
private readonly byte[] _clkWaitDoutBuffer;
24+
private readonly byte[] _doutWaitBuffer;
25+
1926
private readonly SpiDevice _spiDevice;
2027

2128
/// <summary>
@@ -26,13 +33,13 @@ public class Scale
2633
/// <summary>
2734
/// Gets or sets the value that's subtracted from the actual reading.
2835
/// </summary>
29-
public int Offset { get; set; }
36+
public double Offset { get; set; } = 0;
3037

3138
/// <summary>
3239
/// Gets or sets the gain factor that the Hx711 uses when sampling.
3340
/// </summary>
3441
/// <remarks>
35-
/// The default value is <see cref="GainLevel.Gain128"/>.
42+
/// The default value is <see cref="GainLevel.GainA128"/>.
3643
/// </remarks>
3744
public GainLevel Gain { get; set; }
3845

@@ -53,12 +60,18 @@ public class Scale
5360
/// Initializes a new instance of the <see cref="Scale"/> class.
5461
/// </summary>
5562
/// <param name="spiDevice">The <see cref="SpiDevice"/> that is used as channel to communicate with the Hx711.</param>
56-
/// <param name="gain"><see cref="GainLevel"/> that will be used for the scale. If not provided, the default is <see cref="GainLevel.Gain128"/>.</param>
63+
/// <param name="gain"><see cref="GainLevel"/> that will be used for the scale. If not provided, the default is <see cref="GainLevel.GainA128"/>.</param>
64+
/// <exception cref="ArgumentOutOfRangeException"> <see cref="GainLevel.None"/>.</exception>
5765
public Scale(
5866
SpiDevice spiDevice,
59-
GainLevel gain = GainLevel.Gain128
67+
GainLevel gain = GainLevel.GainA128
6068
)
6169
{
70+
if (gain == GainLevel.None)
71+
{
72+
throw new ArgumentOutOfRangeException();
73+
}
74+
6275
_spiDevice = spiDevice;
6376
Gain = gain;
6477

@@ -75,6 +88,13 @@ public Scale(
7588
0b1010_1010,
7689
(byte)Gain
7790
};
91+
92+
// setup buffer to hold data read from DOUT
93+
_readSampleBuffer = new byte[7];
94+
95+
// setup wait DOUT buffers
96+
_clkWaitDoutBuffer = new byte[] { 0x00 };
97+
_doutWaitBuffer = new byte[1];
7898
}
7999

80100
/// <summary>
@@ -105,36 +125,40 @@ public void Tare()
105125
/// </summary>
106126
public void PowerDown()
107127
{
108-
// transition of CLK signal low > high with 60us
109-
_spiDevice.Write(new ushort[] { 0xFFFF, 0xFFFF, 0xFFFF });
128+
// transition of CLK signal low > high with 60us
129+
// 1 bit ~= 1.5uS, 16*4 *1.5uS = 96uS
130+
_spiDevice.Write(new ushort[] { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF });
110131
}
111132

112133
/// <summary>
113-
/// Wakes up the device from power down mode.
134+
/// Wakes up and resets the device. Optional set gain level and channel.
114135
/// </summary>
115-
public void PowerUp()
136+
/// <param name="gain"><see cref="GainLevel"/> that will be used for the scale. If not provided, the default is <see cref="GainLevel.GainA128"/>.</param>
137+
public void PowerUp(GainLevel gain = GainLevel.None)
116138
{
117-
// only required if the device is in power down mode
118-
var currentDout = _spiDevice.ReadByte();
119-
120-
if (currentDout != 0)
139+
// PowerDown then PowerUP to activate on-chip power on rest circuitry
140+
PowerDown();
141+
//set PD_CLK low to awake and reset to default mode GainA128
142+
//Wait for DOUT low means HX711 ready to accept new commands
143+
WaitForConversion();
144+
//switch to another channel mode if it is specified
145+
if (gain != GainLevel.None)
121146
{
122-
// transition of CLK signal high > low to wake-up device
123-
124-
// set gain factor
147+
Gain = gain;
148+
_readSamplePulseTrain[_readSamplePulseTrain.Length - 1] = (byte)Gain;
125149
SetChannelAndGainFactor();
126150
}
127151
}
128152

129-
private int ReadValue()
153+
private double ReadValue()
130154
{
131155
Debug.WriteLine("INFO: Reading sample.");
132156

133157
// setup buffer to drive PD_SCK
134158
SpanByte clkTrain = new(_readSamplePulseTrain);
135159

136160
// setup buffer to hold data read from DOUT
137-
SpanByte readBuffer = new(new byte[7]);
161+
SpanByte readBuffer = new(_readSampleBuffer);
138162

139163
// setup array to hold readings for averaging
140164
int[] values = new int[SampleAveraging];
@@ -158,12 +182,17 @@ private bool WaitForConversion()
158182
{
159183
Debug.WriteLine("INFO: Setup sampling to detect that a sample is ready");
160184

161-
var currentDout = _spiDevice.ReadByte();
185+
//send it in full duplex mode to be platform independent to not let spi send FF's by default
186+
SpanByte clkWaitDoutBuffer = new(_clkWaitDoutBuffer);
187+
SpanByte doutWaitBuffer = new(_doutWaitBuffer);
188+
_spiDevice.TransferFullDuplex(clkWaitDoutBuffer, doutWaitBuffer);
189+
var currentDout = doutWaitBuffer[0];
162190

163191
while (currentDout != 0)
164192
{
165193
Thread.Sleep(10);
166-
currentDout = _spiDevice.ReadByte();
194+
_spiDevice.TransferFullDuplex(clkWaitDoutBuffer, doutWaitBuffer);
195+
currentDout = doutWaitBuffer[0];
167196
}
168197

169198
return true;
@@ -173,12 +202,24 @@ private void SetChannelAndGainFactor()
173202
{
174203
// send N clock pulses according to the set gain factor
175204
// 1 clock pulse per gain factor
176-
_spiDevice.WriteByte((byte)Gain);
205+
Debug.WriteLine("INFO: Setting channel and gain.");
206+
207+
// setup buffer to drive PD_SCK
208+
SpanByte clkTrain = new(_readSamplePulseTrain);
209+
210+
// setup buffer to hold data read from DOUT
211+
SpanByte readBuffer = new(_readSampleBuffer);
212+
213+
if (WaitForConversion())
214+
{
215+
// perform SPI transaction
216+
_spiDevice.TransferFullDuplex(clkTrain, readBuffer);
217+
}
177218
}
178219

179220
private int ParseRawData(SpanByte readBuffer)
180221
{
181-
int value = 0;
222+
uint value24bit = 0;
182223
int rotationFactor = 20;
183224

184225
// raw data is received in as 24 bits in 2’s complement format. MSB first.
@@ -188,37 +229,43 @@ private int ParseRawData(SpanByte readBuffer)
188229
// don't care about the last position (it's the setting of gain factor)
189230
for (int i = 0; i < readBuffer.Length - 1; i++, rotationFactor -= 4)
190231
{
191-
value |= ParseNibble(readBuffer[i]) << rotationFactor;
232+
value24bit |= (uint)ParseNibble(readBuffer[i]) << rotationFactor;
192233
}
193234

194-
return value;
235+
return ConvertFrom24BitTwosComplement(value24bit);
195236
}
196237

197238
private byte ParseNibble(byte value)
198239
{
199240
// take the even bits
200241
// because we can be sure that on the second half of the clock cycle the value in the bit it's the one output from the device
201-
value = (byte)(value & 0b01010101);
202242

203243
int finalValue = 0;
204-
int mask = 0b1100_0000;
244+
int mask = 0b0100_0000;
205245

206246
for (int i = 3; i >= 0; i--)
207247
{
208248
// capture bit value at mask position
209249
// add bit to nibble
210-
if ((value & mask) > 0)
250+
finalValue <<= 1;
251+
if ((value & mask) != 0)
211252
{
212-
finalValue |= 1 << i;
253+
finalValue++;
213254
}
214255

215-
mask = mask >> 2;
256+
mask >>= 2;
216257
}
217258

218259
return (byte)finalValue;
219260
}
220261

221-
private int ComputeAverage(int[] values)
262+
private int ConvertFrom24BitTwosComplement(uint twosComp)
263+
{
264+
//convert from 2's cpmplement 24 bit to int (32 bit)
265+
int NormalValue = ((twosComp & 0x800000) != 0) ? (0 - (int)((twosComp ^ 0xffffff) + 1)) : (int)twosComp;
266+
return NormalValue;
267+
}
268+
private double ComputeAverage(int[] values)
222269
{
223270
double value = 0;
224271

@@ -227,7 +274,7 @@ private int ComputeAverage(int[] values)
227274
value += values[i];
228275
}
229276

230-
return (int)(value / values.Length);
277+
return (value / values.Length);
231278
}
232279
}
233280
}

0 commit comments

Comments
 (0)