Skip to content

Commit 337ddb5

Browse files
committed
add hx711 driver
1 parent 228e57c commit 337ddb5

File tree

8 files changed

+1455
-2
lines changed

8 files changed

+1455
-2
lines changed

examples/hx711/main.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package main
2+
3+
import (
4+
"machine"
5+
"time"
6+
7+
"tinygo.org/x/drivers/hx711"
8+
)
9+
10+
const (
11+
clockOutPin = machine.D3
12+
dataInPin = machine.D2
13+
gainAndChannel = hx711.A128 // only the first channel A is used
14+
tickSleep = 1 * time.Microsecond // set it to zero for slow MCU's
15+
calibrationWait = 10 * time.Second
16+
cycleTime = 1 * time.Second
17+
)
18+
19+
// please adjust to your load used for calibration
20+
const (
21+
setLoad = 100 // used unit will equal the measured unit
22+
unit = "gram"
23+
)
24+
25+
func main() {
26+
time.Sleep(5 * time.Second) // wait for monitor connection
27+
28+
cfg := hx711.DefaultConfig
29+
cfg.TickSleep = tickSleep
30+
31+
clockOutPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
32+
dataInPin.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
33+
34+
clockOutPinSet := func(v bool) error { clockOutPin.Set(v); return nil }
35+
dataInPinGet := func() (bool, error) { return dataInPin.Get(), nil }
36+
37+
sensor := hx711.New(clockOutPinSet, dataInPinGet, gainAndChannel)
38+
if err := sensor.Configure(&cfg); err != nil {
39+
println("Configure failed")
40+
panic(err)
41+
}
42+
43+
println("Please remove the mass completely for zeroing within", calibrationWait.String())
44+
time.Sleep(calibrationWait)
45+
println("Zero starts")
46+
if err := sensor.Zero(false); err != nil {
47+
println("Zeroing failed")
48+
panic(err)
49+
}
50+
51+
println("Please apply the load (", setLoad, unit+" ) for calibration within", calibrationWait.String())
52+
time.Sleep(calibrationWait)
53+
println("Calibration starts")
54+
if err := sensor.Calibrate(setLoad, false); err != nil {
55+
println("Calibration failed")
56+
panic(err)
57+
}
58+
59+
offs, factor := sensor.OffsetAndCalibrationFactor(false)
60+
println("Calibration done completely, offset:", offs, "factor:", factor)
61+
62+
println("Measurement starts")
63+
for {
64+
if err := sensor.Update(0); err != nil {
65+
println("Sensor update failed", err.Error())
66+
}
67+
68+
v1, _ := sensor.Values()
69+
70+
println("Mass:", v1, unit)
71+
72+
time.Sleep(cycleTime)
73+
}
74+
}

fraction.go

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package drivers
2+
3+
import (
4+
"errors"
5+
"math"
6+
"math/big"
7+
)
8+
9+
// Float32Fractions calculates the biggest possible denominator "den" for a given floating point number "f" and
10+
// returns this denominator together with the resulting nominator "nom", so "f" can be reconstructed by "f = num / den".
11+
// All values are in the range MaxInt32: 2147483647, MinInt32: -2147483648. If the given "f" exceeds this range, it is
12+
// not possible anymore to represent "f" with "f = num / 1" and an error will be returned with the nearest values.
13+
// As an exception we define that "den" is always positive, so negative numbers "f" leads always to negative "num".
14+
//
15+
// Used formulas:
16+
// For "abs(f)=af < 1" applies the biggest denominator: "den = math.MaxInt32" and "num = af * den". For "af > 0" this
17+
// can be written more generalized when split integer part "ip" from fractional part "fp" with "af = ip + fp":
18+
// "den = math.MaxInt32/(ip + 1)"; "num = af * den"
19+
// very good accuracy can be reached, similar to calculating with "math/big.Rat", but 2-15 times faster:
20+
// max. epsilon = 1.9073486328125e-06 in test for "17459216/697177" on arm64, but better for this example on MCU,
21+
// nrf52840 12.207µs-14.496µs (independent of used base)
22+
//
23+
// Sign:
24+
// "abs(MaxInt32) > abs (MinInt32)", the sign can be applied to "den" or "nom", but we already defined "den" as positive
25+
//
26+
// Considered other options: see function in test file
27+
func Float32Fractions(f float32) (int32, int32, error) {
28+
const baseMax = math.MaxInt32
29+
//const baseMax = 1000000000 // 10 digits
30+
//const baseMax = 2000000000 // 10.5 digits
31+
ip, den, err := float32FractionsPreCheck(f)
32+
if den == 1 || err != nil {
33+
return ip, den, err
34+
}
35+
36+
if f < 0 {
37+
ip = -ip
38+
}
39+
40+
// see also "math.Modf()"
41+
den = baseMax / (ip + 1)
42+
if den == 0 {
43+
den = 1
44+
}
45+
46+
return int32(float32(den) * f), den, nil
47+
}
48+
49+
// Float32FractionsBigRat uses the big/math go library for splitting the given value in fractions. This function seems
50+
// to produce more accurate results, but is 5-8 times slower than Float32Fractions (depending on value).
51+
// nrf52840: 74.768µs-106.049µs
52+
func Float32FractionsBigRat(f float32) (int32, int32, error) {
53+
if ip, den, err := float32FractionsPreCheck(f); den == 1 || err != nil {
54+
return ip, den, err
55+
}
56+
57+
r := new(big.Rat).SetFloat64(float64(f))
58+
d := r.Denom().Int64() //Denom returns the denominator of x; it is always > 0.
59+
if d > math.MaxInt32 {
60+
d = math.MaxInt32
61+
}
62+
63+
n := f * float32(d)
64+
if n > math.MaxInt32 {
65+
println("n ex+", n)
66+
return math.MaxInt32, int32(float32(math.MaxInt32) / f), nil
67+
} else if n < math.MinInt32 {
68+
println("n ex-", n)
69+
return math.MinInt32, int32(float32(math.MinInt32) / f), nil
70+
}
71+
72+
return int32(n), int32(d), nil
73+
}
74+
75+
func float32FractionsPreCheck(f float32) (int32, int32, error) {
76+
if f > math.MaxInt32 {
77+
return math.MaxInt32, 1, errors.New("input value exceeds +int32 range")
78+
}
79+
80+
if f < math.MinInt32 {
81+
return math.MinInt32, 1, errors.New("input value exceeds -int32 range")
82+
}
83+
84+
ip := int32(f)
85+
den := int32(0)
86+
87+
if float32(ip) == f {
88+
// float is an integer
89+
den = 1
90+
}
91+
92+
return ip, den, nil
93+
}

0 commit comments

Comments
 (0)