In this tutorial we are building a Raspberry Pi smart food scale with Hexabitz modules
Things used in this project
Hardware components
Software apps and online services
STMicroelectronics STM32CUBEPROG
Story
In this tutorial we are building a Raspberry Pi kitchen scale, whose precision is amazingly accurate. In addition, It can calculate energy in calories, display this info on the LCD, and you can transfer the weight information remotely.
Calories are essential for human health. The key is consuming the right amount thus, many weight loss programs center around reducing the intake of calories so counting calories is your first step to weight management.
How I build it 🛠️
The most important thing to build your own scale is a “load cell”, which is a metal bar with a hole in the center. This is available for different weight classes (up to 1kg, up to 5kg, up to 50kg, etc.). Even though some have a different form, all are provided with four cables.
A load cell is a transducer which converts force into a measurable electrical output. Although there are many varieties of force sensors, strain gauge load cells are the most commonly used type.
A load cell consists of a metal core and a set of electrical resistances that transform when a force is applied to it. But after the force is removed, it returns to its original state. The reversibility of this material determines the quality and accuracy of the load cell. The equivalent electrical circuit of a load cell is as follows:
Our load cell has 4 wires:
🔴 Red for Excitation+
⚫ Black for Excitation-
🟡 Yellow for Output+
🟢 Green for Output-
Be careful about the side of the load cell when you’re putting a weigh on it. Usually, there is an arrow on the module that shows the force direction. With the help of this arrow, you can place the weight and the load cell correctly.
To read out the values, the weight sensor module is also required.
Load Cell Sensor (H26R0x) : H26R0x is a load cell (strain gauge) sensor interface module based on Avia Semiconductor HX711 Wheatstone bridge ADC and STM32F0 MCU. This module is a precision 24-bit analog-to-digital converter (ADC) designed for weigh scales and industrial control applications. It interfaces to load cells (strain gauges) in a Wheatstone bridge configuration. The module accepts a full bridge (5-pin connection) as well as a secondary sensor connection (2 pins).
To use a load cell module, first you need to calibrate it. Then stream strain gauge channel (ch) in gram to a port in remote module (HF1R0 module) with specified period and timeout. To do this add the following APIs to your Hexabitz load cell module code.
Module_Status Calibration(uint16_t full_scale, float cell_output, float cell_drift)
//Set load cell calibration values (full scale, cell output, cell
drift)
Module_Status ZeroCal(uint8_t ch) // Perform zero weight calibration on the load cell
Module_Status StreamGramToPort(uint8_t ch,uint8_t port, uint8_t module, uint32_t period, uint32_t timeout);
Raspberry Pi Interface (HF1R0x):HF1R0x Is Hexabitz Raspberry Pi interface module. It interfaces seamlessly to Raspberry Pi 3B/3B+/4B single-board computers and enables you to connect and control your favorite Hexabitz modules from the Raspberry Pi operating system and build your own Raspberry Pi Hat. You can use C++ Hexabitz Interface library on Raspbian, Ubuntu or other compatible Linux distributions from within your C++ or Python applications.
Notes:
Connect the H26R0 module to HF1R0 module.
Make sure to connect your Hexabitz module at the right spot or left spot then put the Jumper JP1 and JP2 at right position.
Be sure to download the firmware on the module before soldering.
https://hexabitz.com/docs/how-to/update-module-firmware/
Code Explanation 🖥️ 🧐 ⚖️
Python Tkinter
Tkinter is the standard GUI library for Python. Python when combined with Tkinter provides a fast and easy way to create GUI applications. Tkinter provides a powerful object-oriented interface to the Tk GUI toolkit. Creating a GUI application using Tkinter is an easy task.
All you need to do is perform the following steps :
-Import the Tkinter module.
-Create the GUI application main window.
-Add one or more of the above-mentioned widgets to the GUI application.
-Enter the main event loop to take action against each event triggered by the user.
· You can start Python3 from the command line (with python3) then
import tkinter
· If you get an error message, then exit out of Python3 and call this command to install it.
sudo apt-get install python3-tk
"master" represents the parent window, where the entry widget should be placed. Like other widgets, it's possible to further influence the rendering of the widget by using options. The comma separated list of options can be empty.
The Button widget is a standard Tkinter widget, which is used for various kinds of buttons. A button is a widget which is designed for the user to interact with, i.e. if the button is pressed by mouse click some action might be started. They can also contain text and images like labels.
Python pyserial
This module encapsulates the access for the serial port. It provides backends for Python running on Windows, OSX, Linux, BSD (possibly any POSIX compliant system) and IronPython. The module named “serial” automatically selects the appropriate backend.
Depending on the version of python, there are several commands to install the package pyserial.
sudo apt-get install python-serial python3-serial
Note :
There are several ways to determine the USB port to which the device is connected. The fastest is to connect the unit to the USB port then to immediately run the command dmesg -s 1024.
You’ll get directly the tty port on which it is connected.
Python code to read the serial port:
This section of code primarily instantiates the serial class, setting it up with all the various bits of information that it needs to make the connection with.
port – This defines the serial port that the object should try and do read and writes over.
baudrate – This is the rate at which information is transferred over a communication channel.
parity – Sets whether we should be doing parity checking, this is for ensuring accurate data transmission between nodes during communication.
stopbits – This is the pattern of bits to expect which indicates the end of a character or the data transmission.
bytesize – This is the number of data bits.
timeout – This is the amount of time that serial commands should wait for before timing out.
import serial
ser = serial.Serial(
port='/dev/ttyUSB0',
baudrate = 921600,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS,
timeout=1
)
struct Module
This module performs conversions between Python values and C structs represented as Python bytes objects. This can be used in handling binary data stored in files or from network connections, among other sources. It uses Format Strings as compact descriptions of the layout of the C structs and the intended conversion to/from Python values.
We’ll kick off with learning the functions used in this project:
Byte to float function:
import serial
import struct
ser = serial.Serial(port='/dev/ttyS0',baudrate = 921600,parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,bytesize=serial.EIGHTBITS,timeout=1)
print(ser.name)
def byte_to_float(b):
x = struct.unpack('f', b)
return x
def stream():
global w
s = ser.read(4)
print(s)
[w,] = byte_to_float(s)
w = int(w)
print(w)
return w
In our main program we define the tk window with the Tk() as the root variable. Then we create the title of the window.
from tkinter import *
root=Tk()
root.title("Hexabitz Scale")
The stop button:
button=Button(root,text='Stop',bg ='orange',width=50,command=root.destroy)
button.pack()
You can use food calorie calculator to know the values for our GUI code.
The sample button:
def start():
global w
w = stream()
print(w)
lable= Label (root,width=24,fg="yellow",bg="black")
lable.config(font=("Courier",20))
lable.config(text=str(w)+ ' g')
lable.pack()
Bsample=Button(root,text='Sample weight(g)', bg ='gold',width=50,command=start)
Bsample.pack()
FoodName label and option menu:
🍏🍎🍌🍐🍊🍋🍆🍒🫑🥥🍍🍪🍬🍞🌽🥕
OPTIONS = [
"Apples", "Pears" ,"Raspberries","Bread","Sugar"] #etc
L1 = Label(root, text = "Food Name")
L1.pack()
variable = StringVar(root)
variable.set(OPTIONS[0]) # default value
w = OptionMenu(root, variable, *OPTIONS)
w.pack()
The calculationbutton:
def Calculate_calories():
q =variable.get()
w1 = int(w)
if (q == "Apples"):
x= 0.48
z = x*w1
print(z)
elif(q == "Pears"):
x= 0.57
z = x*w1
print(z)
elif(q == "Bread"):
x= 3.8
z = x*w1
print(z)
elif(q == "Sugar"):
x= 5.8
z = x*w1
print(z)
else:
x= 0.85
z = x*w1
print(z)
lable= Label (root,width=24,fg="yellow",bg="black")
lable.config(font=("Courier",20))
lable.config(text = str(z) + " calories")
lable.pack()
buttonc=Button(root,text='Calculate calories', bg ='light green',width=50,command=Calculate_calories)
buttonc.pack()
We end by a root.mainloop() function to keep events on the main window active and all other widgets interactive.
root.mainloop()
Test the System 😃 ⚖️ 🍏
Schematics
Raspberry Pi Weight Scale Schematic
CODE
# -*- coding: utf-8 -*-
"""
Created on Fri Sep 3 19:44:22 2021
@author: Aula_J
"""
from tkinter import *
import time
#import numpy as np
import serial
import struct
ser = serial.Serial(
port='/dev/ttyS0',
baudrate = 921600,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS,
timeout=1
)
print(ser.name)
def byte_to_float(b):
x = struct.unpack('f', b)
return x
def stream():
global w
s = ser.read(4) #4byte
print(s)
[w,] = byte_to_float(s)
w = int(w)
print(w)
return w
root=Tk()
root.title("Hexabitz Scale")
OPTIONS = [
"Apples", "Pears" ,"Raspberries",
"Bread",
"Sugar"
] #etc
def start():
global w
w = stream()
print(w)
lable= Label (root,width=24,fg="yellow",bg="black")
lable.config(font=("Courier",20))
lable.config(text=str(w)+ ' g')
lable.pack()
button=Button(root,text='Stop',bg ='orange',width=50,command=root.destroy)
button.pack()
Bsample=Button(root,text='Sample weight(g)', bg ='gold',width=50,command=start)
Bsample.pack()
L1 = Label(root, text = " Food Name")
L1.pack()
variable = StringVar(root)
variable.set(OPTIONS[0]) # default value
w = OptionMenu(root, variable, *OPTIONS)
w.pack()
def Calculate_calories():
q =variable.get()
global w
#w = stream()
w1 =int(w)
if (q == "Apples"):
x= 0.48
z = x*w1
print(z)
elif(q == "Pears"):
x= 0.57
z = x*w1
print(z)
elif(q == "Bread"):
x= 3.8
z = x*w1
print(z)
elif(q == "Sugar"):
x= 5.8
z = x*w1
print(z)
else:
x= 0.85
z = x*w1
print(z)
lable= Label (root,width=24,fg="yellow",bg="black")
lable.config(font=("Courier",20))
lable.config(text = str(z) + " calories")
lable.pack()
buttonc=Button(root,text='Calculate calories', bg ='light green',width=50,command=Calculate_calories)
buttonc.pack()
root.mainloop()
HF1R0x-Firmware 0.2.0
https://github.com/HexabitzPlatform/HF1R0x-Firmware/tree/0.2.0
The article was first published in hackster, September 5, 2021
cr: https://www.hackster.io/aula-jazmati/raspberry-pi-smart-food-scale-with-hexabitz-modules-e6cbaf
author: Aula Jazmati, Najib Kassab