icon

Unihiker Scale using Gravity HX711 i2c Pressure Sensor

This project details using the Unihiker as a scale along with a DFRobot Gravity series HX711 pressure sensor. I previously converted the library to work with circuit python for the scale's i2c interface so I opted to do the same here with the pinpong library for the unihiker. Overall the conversion process was straightforward and I was able to get the scale running without too much effort.

 

Note: the grams move a bit on a decimal level as the scale functions. I think it has to do with the hx711 needing an excitation voltage of around 5V or it will vary (from what I've seen folks discuss online). I may try level shifting in the future here to see if it improves but it works for my needs with the small variation.

STEP 1
Add python code to the Unihiker
STEP 2
Attach i2c wire for Gravity hx711 scale to the Unihiker one of the i2c pinouts
STEP 3
Optionally print the stand for use with the scale
STEP 4
Attach an 8 LED ring light (use one with 28MM spacing betwen screws) to the pin 22
STEP 5
Turn on the program and operate the scale

The above steps cover the gist of the steps to get this project up and running. You'll need to upload both the unihiker scale code and the associate i2c library I've provided here with this project.

 

For my purposes I placed both files in the root directory and accessed it via the run programs flow. You can access the getting started documentation for the Unihiker to see more about that process.

 

For the stand there is a printables link with access to the model: https://www.printables.com/model/894852-unihiker-stand

 

I soldered a jst 3 pin connector to my LED ring to allow me to connect directly to the unihiker using the provided cables. As I'm only using 20 in each channel for the LED brightness the current draw should be minimal (the lights are plenty bright).

 

The LED ring is used as part of a night light feature I included to utilize the Unihiker's light sensor to determine whether or not to enable. 

 

The scale provides two buttons to further utilize it:

 

The top button on the unihiker activates the calibration mode which allows a weight to be used to calibrate the scale (it can be configured in the script with an ENV variable)

 

The bottom button on the unihiker activates the peel functionality resetting the scale back to 0

HARDWARE LIST
1 DFRobot Unihiker (Affiliate Link)
HARDWARE LIST
1 DFRobot Gravity HX711 Pressure Sensor (Affiliate Link)
HARDWARE LIST
1 Generic 8 LED ring
CODE
# dfrobot_unihiker_scale.py
#!/usr/bin/python3
import time
from unihiker import GUI
from pinpong.board import Board, I2C, Pin, NeoPixel
from pinpong.extension.unihiker import *
from dfrobot_i2c_hx711 import DFRobot_HX711_I2C
import matplotlib.pyplot as plt
from datetime import datetime
import matplotlib.dates as mdates

# Weight value
CALIBRATION_WEIGHT_VALUE=50

# Arbitrary value for light sensor to trigger LED ring light
LIGHT_ENABLE_THRESHOLD = 300

Board("UNIHIKER").begin()
NEOPIXEL_PIN = Pin(22)
PIXELS_NUM = 8
neopixel = NeoPixel(NEOPIXEL_PIN, PIXELS_NUM)

# Initialize GUI and variables
gui = GUI()
lightEnabled = None
hx711_sensor = DFRobot_HX711_I2C()

# Initialize the HX711 sensor
hx711_sensor.begin()

# Page header
gui.draw_text(text="Weight Sensor", x=30, y=20, font_size=20, color="teal")
state = gui.draw_text(text="Current State: Weighing", x=30, y=80, font_size=10, color="blue")

# Weight display
weightText = gui.draw_text(text="0.00 g", x=20, y=180, font_size=34, color="teal")

def handleNightMode():
    global lightEnabled
    lightValue = light.read()
    if 0 <= lightValue < LIGHT_ENABLE_THRESHOLD:
        lightEnabled = True
        for i in range(PIXELS_NUM):
            neopixel[i] = (20, 20, 20)
    elif lightEnabled:
        lightEnabled = False
        for i in range(PIXELS_NUM):
            neopixel[i] = (0, 0, 0)

# Track the time when the state was last changed
last_state_change_time = time.time()
initial_state_set = False

while True:
    handleNightMode()

    # Check if button A is pressed for calibration
    if button_a.is_pressed() == True:
        state.config(text="Current State: Calibrating {}g".format(CALIBRATION_WEIGHT_VALUE))
        last_state_change_time = time.time()
        hx711_sensor.setTriggerWeight(CALIBRATION_WEIGHT_VALUE)
        hx711_sensor.setCalThreshold(1)
        hx711_sensor.enableCalibration()
        i = 0
        while hx711_sensor.peelFlag() != 2:
            time.sleep(1)
            i += 1
            if i >= 7:
                break
        calibration = hx711_sensor.calibration()
        state.config(text="Calibrated: {:.2f}".format(calibration))
        hx711_sensor.setCalibration(calibration)
        last_state_change_time = time.time()
        initial_state_set = False

    # Check if button B is pressed for tare (peel)
    if button_b.is_pressed() == True:
        state.config(text="Current State: Peeled")
        last_state_change_time = time.time()
        hx711_sensor.peel()
        initial_state_set = False

    # Update the weight display
    weight = hx711_sensor.weight(10)
    weightText.config(text="{:.2f} g".format(weight))

    # Check and update the state to "Current State: Weighing" if needed
    if not initial_state_set and (time.time() - last_state_change_time) >= 5:
        state.config(text="Current State: Weighing")
        initial_state_set = True

    time.sleep(0.1)
CODE
# dfrobot_i2c_hx711.py
import time
import struct
from pinpong.board import gboard, I2C

# Define constants
I2C_ADDR = 0x64
REG_CLEAR_REG_STATE = 0x65
REG_DATA_GET_RAM_DATA = 0x66
REG_DATA_GET_CALIBRATION = 0x67
REG_DATA_GET_PEEL_FLAG = 0x69
REG_DATA_INIT_SENSOR = 0x70
REG_SET_CAL_THRESHOLD = 0x71
REG_SET_TRIGGER_WEIGHT = 0x72
REG_CLICK_RST = 0x73
REG_CLICK_CAL = 0x74

class DFRobot_HX711_I2C:
    def __init__(self, board=None, i2c_addr=I2C_ADDR, bus_num=0):
        if isinstance(board, int):
            i2c_addr = board
            board = gboard
        elif board is None:
            board = gboard
        self.i2c_addr = i2c_addr
        self._i2c = I2C(bus_num)
        self.buffer = bytearray(3)
        self.rxbuf = bytearray(16)
        self._offset = 0
        self._calibration = 1752.60

    def begin(self):
        time.sleep(0.03)
        self.buffer[0] = REG_DATA_INIT_SENSOR
        self._write_register()
        time.sleep(0.03)
        self.buffer[0] = REG_CLEAR_REG_STATE
        self._write_register()
        time.sleep(0.03)
        self._offset = self.average(1)
        self.setCalibration(self._calibration)

    def average(self, times=1):
        sum = 0
        for _ in range(times):
            sum += self.raw_weight()
        return sum / times

    def weight(self, times=1):
        weight = self.average(times)
        time.sleep(0.05)
        peel_flag = self.peelFlag()
        if peel_flag == 1:
            self._offset = self.average(times)
        elif peel_flag == 2:
            self._calibration = self.calibration()
        return (weight - self._offset) / self._calibration

    def raw_weight(self):
        self.buffer[0] = REG_DATA_GET_RAM_DATA
        self._write_register()
        time.sleep(0.03)
        self._read_register(4)
        return ((self.rxbuf[0] << 24) | (self.rxbuf[1] << 16) | (self.rxbuf[2] << 8) | self.rxbuf[3])

    def peel(self):
        self._offset = self.average(1)
        self.buffer[0] = REG_CLICK_RST
        self._write_register()
        time.sleep(0.03)

    def peelFlag(self):
        self.buffer[0] = REG_DATA_GET_PEEL_FLAG
        self._write_register()
        time.sleep(0.03)
        self._read_register(1)
        return self.rxbuf[0]

    def calibration(self):
        self.buffer[0] = REG_DATA_GET_CALIBRATION
        self._write_register()
        time.sleep(0.03)
        self._read_register(4)
        value = (self.rxbuf[0] << 24) | (self.rxbuf[1] << 16) | (self.rxbuf[2] << 8) | self.rxbuf[3]
        return struct.unpack('>f', struct.pack('>I', value))[0]

    def enableCalibration(self):
        self.buffer[0] = REG_CLICK_CAL
        self._write_register()
        time.sleep(0.03)

    def setCalibration(self, cal):
        self._calibration = cal

    def setCalThreshold(self, cal):
        self.buffer[0] = REG_SET_CAL_THRESHOLD
        tx_data = struct.pack('>H', cal)
        self.buffer[1:3] = tx_data
        self._write_register()
        time.sleep(0.03)

    def setTriggerWeight(self, weight):
        self.buffer[0] = REG_SET_TRIGGER_WEIGHT
        tx_data = struct.pack('>H', weight)
        self.buffer[1:3] = tx_data
        self._write_register()
        time.sleep(0.03)

    def _write_register(self):
        self._i2c.writeto(self.i2c_addr, self.buffer)

    def _read_register(self, length):
        self.rxbuf = self._i2c.readfrom(self.i2c_addr, length)
License
All Rights
Reserved
licensBg
0