Skip to content

Instantly share code, notes, and snippets.

@indeedhat
Created December 22, 2025 17:23
Show Gist options
  • Select an option

  • Save indeedhat/f4fb558ac92cae2fd0de2a30ffdc0788 to your computer and use it in GitHub Desktop.

Select an option

Save indeedhat/f4fb558ac92cae2fd0de2a30ffdc0788 to your computer and use it in GitHub Desktop.
read weight in grams from load cell (hx711 - periph.io)
package main
import (
"fmt"
"log"
"time"
"periph.io/x/conn/v3/gpio"
"periph.io/x/host/v3"
"periph.io/x/host/v3/rpi"
)
func main() {
log.Print("init host")
if _, err := host.Init(); err != nil {
log.Fatalf("failed to init periph.io %s", err)
}
log.Print("enable 3.3v power")
powerPin := rpi.P1_7
log.Print(powerPin.Out(gpio.High))
defer func() {
powerPin.Out(gpio.Low)
powerPin.Halt()
}()
time.Sleep(5 * time.Second)
log.Print("trigger weigh")
weigh(0, 17700)
log.Print("post weigh")
}
func weigh(zero, adjustScale float64) {
scale, err := NewScale(zero, adjustScale)
if err != nil {
log.Fatalf("failed to init scale: %s", err)
}
defer scale.Close()
log.Print("created scale instance")
ticker := time.NewTicker(time.Second)
log.Print("auto zero")
scale.AutoZero()
log.Print("auto zero done")
for range ticker.C {
w, _ := scale.Weigh()
log.Printf("weight: %0.1f", w)
}
}
package main
import (
"context"
"log"
"sort"
"sync"
"time"
"periph.io/x/devices/v3/hx711"
"periph.io/x/host/v3/rpi"
)
const readTimeout = time.Millisecond * 500
type ringBuffer struct {
data []int
i int
full bool
mux sync.Mutex
}
func newRingBuffer() *ringBuffer {
return &ringBuffer{data: make([]int, 11)}
}
func (b *ringBuffer) Set(v int32) {
b.mux.Lock()
defer b.mux.Unlock()
b.i++
if b.i == 10 {
b.full = true
}
i := b.i % 10
b.data[i] = int(v)
}
func (b *ringBuffer) Data() []int {
b.mux.Lock()
defer b.mux.Unlock()
out := make([]int, 11)
copy(out, b.data)
return out
}
type scale struct {
hx *hx711.Dev
buf *ringBuffer
cancel context.CancelFunc
Zero float64
Scale float64
}
func NewScale(zero, adjust float64) (*scale, error) {
hx, err := hx711.New(
rpi.P1_29,
rpi.P1_31,
)
if err != nil {
return nil, err
}
ctx, cancel := context.WithCancel(context.Background())
if adjust == 0 {
adjust = 1
}
scale := &scale{
hx: hx,
buf: newRingBuffer(),
cancel: cancel,
Zero: zero,
Scale: adjust,
}
go func() {
readings := scale.hx.ReadContinuous()
for {
select {
case <-ctx.Done():
return
case r := <-readings:
log.Print(r.Raw)
if r.Raw == -1 {
continue
}
scale.buf.Set(r.Raw)
}
}
}()
return scale, nil
}
func (s *scale) AutoZero() error {
_, _ = s.Weigh()
adjustScale := s.Scale
s.Scale = 1
s.Zero = 0
time.Sleep(time.Second * 2)
w, err := s.Weigh()
if err != nil {
return err
}
s.Scale = adjustScale
s.Zero = w
return nil
}
func (s *scale) Await(ctx context.Context, weight float64) <-chan struct{} {
var done chan struct{}
go func() {
ticker := time.NewTicker(readTimeout)
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
if !s.buf.full {
continue
}
w, _ := s.Weigh()
if w >= weight {
done <- struct{}{}
ticker.Stop()
return
}
}
}
}()
return done
}
func (s *scale) Weigh() (float64, error) {
for !s.buf.full {
time.Sleep(time.Millisecond * 100)
}
data := s.buf.Data()
sort.Ints(data)
log.Print(data[len(data)/2])
return (float64(data[len(data)/2]) - s.Zero) / s.Scale, nil
}
func (s *scale) Close() {
s.hx.Halt()
s.cancel()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment