MixMeasureBuddy's smart cocktail scale revolutionizes home bartending by offering accurate ingredient measurements and a vast library of cocktail recipes personalized to your liquor collection. Enjoy the art of mixology with ease and precision.
Discover the advanced features that set MixMeasureBuddy apart from traditional kitchen scales.
Discover the advanced features that set MixMeasureBuddy apart from traditional kitchen scales:
- Open-Source MixMeasureBuddy is 100% open-source and can be build using a few simple and cheap parts.
- Connectivity Features Enjoy seamless integration with a companion webapp for sharing personalized cocktail recipes with other MixMeasureBuddy owners.
- Offline Usage Recipes are stored offline and locally on the MixMeasureBerry and can be edited using a text editor
- User-Friendly Interface Easily navigate the scale's intuitive interface for effortless cocktail making.
- Customizable Recipes Access a plethora of cocktail recipes tailored to your liquor cabinet for endless mixing possibilities.
- Precision Measurements Eliminate the guesswork and achieve consistent results with exact ingredient measurements every time.
- 1x
ring.stl
- 1x
breadboard_mount.stl
- 1x
inner_cellplate.stl
- 1x
hinge.stl
- 1x
bottom.stl
- 1x
display_clamp.stl
- 1x
display_mount.stl
- 1x
encoder_clamp.stl
- 1x
cellplate_<style>.stl
The top plate exists in three different styles:
normal
- plain top platering
- with an 82-84mm diameter ring depression in the center to plate a rubber band in itplate
- with an 80mm diameter circular depression to place a rubber sheet in it
The ring
and plate
variants offer better grip of the glasses while standing on the platic top plate.
- Layer height:
0.2
-0.3
mm - Support:
Support on buildplate only
- Inflill:
20%
- 6x
Heat Inserts M3
- 6x
M3x10 BHCS
- 4x
M4x10 BHCS
- depends on load cell screw threads - 3x
Heat Inserts M4
- 3x
M4x10 BHCS
- 8x
Cylindrical Magnet D5mm H8mm
Super Glue
- to glue magnets intoinner_cellplate
andcellplate
- 1x
Raspberry Pi Pico
orRaspberry Pi Pico W
- 1x
LOAD CELL
with dimensions of60x12x12mm
and at least2kg
, for exampleYZC-131
- 1x
HX711
- 1x
1.3" I2C OLED SSH1306
- 1x
Encoder
e.g.KY-040
- 50cm
WS2812
strip - Jumperwires >12x
male-male
, >10xmale-female
- 1x Breadboard with dimensions of
83 x 55mm
, for exampleMini Breadboard 400 Pin
- [OPTIONAL]
SPI SD TF Karte Memory Card Shield Modul
- [OPTIONAL]
Battery Expansion Shield 18650 V3
Soldering Iron
Hotglue
The following diagram shows the internal wiring of the individual components of the MixMeasureBuddy
.
Only the Raspberry Pi Pico
, button and the HX711
are placed on the small breadboard.
The display, LEDs
and the encoder(-module) are connected with longer cables so that they can be attached to the 3D printed parts at the intended mounting locations.
The circuit diagram was created in the Fritzing software. The project can be found under documenation/schematic/
.
After the function of all parts has been tested, the connections on the breadboard should be fixed with hot glue so that they do not come loose during transportation.
To fix accuracy issues on several HX711
boards, two addional resistors are needed.
Please refer to this guide: HX711 – Auswahl und Beschaltung
It is possible to connect an additional SD card reader. This makes it possible to save the recipes on an SD card.
Please connect the SD card reader to the following pins on the Raspberry Pi Pico
:
Raspberry Pi Pico GPIO | SD CARD MODULE GPIO |
---|---|
VCC (Pin 36) | 3V3 (OUT) / VCC |
GND (Pin 18) | GND |
GP10 | SCK |
GP8 | MISO |
GP11 | MOSI |
GP9 | CS |
No further software changes are needed later on. The systems firmware detects the connected SD card automatically.
The Battery Expansion Shield 18650 V3
can be mounted on the breadboard_mount.stl
later on.
In oder to connect the Battery Expansion
to the Raspberry Pi Pico GPIO
please follow the wiring table below:
Raspberry Pi Pico GPIO | Battery Expansion |
---|---|
VBUS (Pin 40) | 5V OUT |
GND (Pin 38) | GND |
Please check the Releases
page of this repository for prebuild firmware archives.
To initially flash the software to the Raspberry Pi Pico
, the BOOT
button must first be held down when plugging in the USB
cable.
A new removable disk will then appear on the PC
. The firmware.uf2
is then copied to this.
The microcontroller will then restart and the MixMeasureBuddy logo should appear on the display.
If you want to build the software yourself from source or add modifications, please refer to the SOFTWARE DEVELOPMENT
chapter.
NOTE There is an firmware archive with older stable versions located in src/firmware/build/archive
.
If a old version of the firmwre was already installed on the Raspberry Pi Pico
, please flash the flash_nuke.uf2 first! This clears all user settings!
NOTE Please backup all your stored recipes first!
- 1x
Heat Inserts M3
- 1x
M3x10
- 1x
1.3" I2C OLED SSH1306
- 4x Jumperwires
male-female
- 1x
display_clamp.stl
- 1x
display_mount.stl
- Insert the
M3 Heat Insert
using a soldering iron intodisplay_mount
- Place the
1.3" I2C OLED SSH1306
display insidedisplay_mount
- Press the display into place using the
display_clamp
and oneM3x10
- Connect four
male-female
jumperwires onto the display header - Glue the jumperwire ends and the header together using a drop of hotgleue
- [OPTIONAL] Lock down the jumperwires using a small zip-tie
- 2x
Heat Inserts M3
- 1x
M3x10
- 4x Jumperwires
male-female
- 1x
Encoder
e.g.KY-040
- 1x
encoder_clamp.stl
- 1x
ring.stl
- Connect five
male-female
jumperwires onto the encoder pcb header - Insert the two
M3 Heat Inserts
using a soldering iron intoring
part - Insert the encoder pcb into
ring
shell - Use the
encoder_clamp
and oneM3x10
to press the encoder into thering
- 1x
M3x10
- 1x
hinge.stl
- Place the display assembly in the indentation
- Use the
hinge
and oneM3x10
to press the assembly into thering
-
4x
M4x10
- depends on load cell screw threads -
4x
Cylindrical Magnet D5mm H8mm
-
1x
LOAD CELL
-
1x
inner_cellplate.stl
- Insert the three
M4 Heat Inserts
using a soldering iron intoring
part - Screw the loadcell using two
M4x10
screws into thering
- Use the remaining
M4x10
screws to mount theinner_cellplate
on the load cell arm, in the center of thering
cutout - Glue four magnets into
inner_cellplate
NOTE The cables from the load cell are facing towards the shell of the ring
- 4x
Heat Inserts M3
- 4x
M3x10 BHCS
- 1x
ASSEMBLED BREADBOARD
- 1x
breadboard_mount.stl
- Insert the four
M3 Heat Inserts
using a soldering iron intobreadboard_mount
part - [OPTIONAL] Screw the
Battery Expansion Shield 18650 V3
into the side using twoM3x10
- Remove the protective film from the breadboard bottom side and place it ontop
breadboard_mount
stl` - Use the remaining two
M3x10
screws to fixbreadboard_mount
andring
together into place
NOTE Please connect all remaining components (Encoder
, Display
, ...) to the breadboard. See chapter SCHEMATIC
again.
NOTE Its a good time to test the electronics again! Please refer to the SOFTWARE INSTALLATION
chapter.
WS2812
strip- 3x
M4x10 SHCS
- 3x Jumperwire
male-male
- 1x
bottom.stl
- Solder three
male-male
jumperwires onto theWS2812
strip (VCC
,GND
,DIN
) - Remove the protective film from the
WS2812
strip and place them on the center ring of thebottom
- Screw the
bottom
using the threeM4x10
screws ontoring
assembly.
NOTE Make sure that the Micro USB
is going thought opening between bottom
and ring
.
NOTE If no Battery Expansion Shield 18650 V3
is installed, please insert a Micro USB
cable into the Raspberry Pi Pico
before attaching the bottom
.
- 4x
Cylindrical Magnet D5mm H8mm
- 1x
cellplate.stl
- Glue four magnets into
cellplate
- Place the
cellplate
on theinner_cellplate
NOTE Please ensure that the magnets in the two parts cellplate
, inner_cellplate
attract each other on opposite sides
Once the hardware has been set up and the software has been flashed to the microcontroller, the system can be set up for the first time. The basic operation and calibration of the system is described below.
After switching on the power supply, e.g. via a power bank, the main menu appears on the display immediately after the logo screen. Navigation in the system is very easy thanks to the rotary encoder. The right and left rotation of the encoder can be used to scroll through the separate menu entries. If you want to enter / activate a menu item, simply press the button on the encoder. To exit a menu/recipe, the encoder button must be held down for at least half a second.
If the scales menu or a recipe is now called up. The weighing results can deviate significantly from reality.
This is due to the installation direction of the load cell, its mounting and other factors.
The menu item CALIBRATION
is provided for this purpose.
For this process, the scale is measured with and without weight and the corresponding correction factors are determined.
A known weight with at least 50g
is therefore required. This can be a glass of water, for example, which has previously been measured with a kitchen scale.
After starting the calibration routine, the user is guided through the following steps one after the other:
- Measure empty scale
- User enters weight of calibration object
- User places calibration object on scale
- User removes calibration object if shown by display
NOTE If the scales light up red, please DONT MOVE OR TOUCH. NOTE The IKEA POKAL glass has a weight of 390g (empty) :)
A recipe in the context of the MixMeasureBuddy consists of a JSON object in a .recipe file. This contains meta data such as the name and description of the recipe as well as a series of instructions that the user should execute to complete the recipe. The following instruction types can be used for this purpose:
- SCALE - Weigh until x grams reached
- WAIT - Wait x seconds
- CONFIRM - Confirm that it has been done.
There are two ways to create a new recipe manually:
- Create a new empty folder
- Copy the
src/firmware/src/static_modules/recipe.py
into the new created folder - Create a new
MyRecipe.py
file with the following content Python script below - Change the name, description and steps according your imagination
# SEE example_recipes.py FOR FURTHER / DETAILED EXAMPLES
import recipe
example_recipe: recipe.recipe = recipe.recipe("Tequila Sunrise", "A nice Tequila Sunrise Cocktail", "1.0.0", ["Tequila"])
# ADD A SCALE STEP: ADD x g TO THE GLASS
example_recipe.add_step(recipe.recipe_step(_action=recipe.USER_INTERACTION_MODE.SCALE, _ingredient_name="White Tequila", _current_step_text="", _target_value = 10))
# ADD A SCALE STEP: ADD x g TO THE GLASS
example_recipe.add_step(recipe.recipe_step(_action=recipe.USER_INTERACTION_MODE.SCALE, _ingredient_name="Orange Juice", _current_step_text="", _target_value = 120))
# ADD CONFIRM STEP: ADD ICE AND CONFIRM WITH A BUTTON PRESS
example_recipe.add_step(recipe.recipe_step(_action=recipe.USER_INTERACTION_MODE.CONFIRM, _ingredient_name="Ice", _current_step_text="Add Ice", _target_value = -1))
# ADD A SCALE STEP: ADD x g TO THE GLASS
example_recipe.add_step(recipe.recipe_step(_action=recipe.USER_INTERACTION_MODE.SCALE, _ingredient_name="Grenadine", _current_step_text="", _target_value = 40))
# ADD A WAIT STEP: WAIT x SECONDS
example_recipe.add_step(recipe.recipe_step(_action=recipe.USER_INTERACTION_MODE.WAIT, _ingredient_name="", _current_step_text="Wait for settle down", _target_value = 10))
# EXPORT THE RECIPES AS JSON BASE .RECIPE FILE
with open("MyRecipe.recipe, "w") as file:
file.write(json.dumps(example_recipe.to_dict()))
# COPY OVER THE .RECIPE FILE TO THE SCALE
Run the
$ python MyRecipe.py
$ cat MyRecipe.recipe
This scripts created a new MyRecipe.recipe
file. The content is a JSON
strucutre which is compatible with the recipe system of the scale.
{
"filename": "Tequila_Sunrise.recipe",
"name": "Tequila Sunrise",
"steps": [{
"text": "",
"ingredient": "White Tequila",
"amount": 10,
"action": 0
}, {
"text": "",
"ingredient": "Orange Juice",
"amount": 120,
"action": 0
}, {
"text": "Add Ice",
"ingredient": "Ice",
"amount": -1,
"action": 1
}, {
"text": "",
"ingredient": "Grenadine",
"amount": 40,
"action": 0
}, {
"text": "Wait for settle down",
"ingredient": "",
"amount": 10,
"action": 2
}],
"description": "A nice Tequila Sunrise Cocktail",
"version": "1.0.0"
}
To access the files and the Micropython environment on the scale directly, its possible to use these two example programs below:
In this example, a previously created recipe file is to be copied to the MixMeasureBuddy
so that it can be used.
After connecting the flashed MixMeasureBuddy
to a host PC using a USB cable, run the following commands in order to transfer the .recipe
file.
For installation, please refer to the installation guid on the website INSTALL_THONNY.
The image shows the steps in order to connect the MixMeasureBuddy
and access its files:
- Select the board
- Connect to python repl running on the board
- Use the file explorer to add/edit/remove files
# INSTALL RSHELL
$ sudo pip3 install rshell
#TEST CONNECTION
$ rshell
# OR
$ rshell -p /dev/ttyUSB0
# COPY
$ rshell cp MyRecipe.recipe /data/MyRecipe.recipe
The recipe is now stored on the MixMeasureBuddy
. Powercycle the device to load the new recipes!
If a Raspberry Pi Pico W
is used, its possible to use the recipe editor and the recipe api update functions.
Here the wifi credentials needs to be set using the SETTINGS.json
file on the scale.
Please use the same procedure described above to edit files on the scale filesystem.
The microcontroller firmware of the Raspberry Pi Pico
was created in micropython and is automatically created with the pre-built image.
The source code files are located in the folder src/firmware_rp2040
and the Python source code files in the folder src/firmware_rp2040/src
.
The program Thonny
can be used to adapt the software directly on the scale.
The global hardware configuration (used display type, pin definitions and other settings) are stored in the Config.py
loacted in the virtual filesystem.
Its possbile to modify this file using the REPL
or Thonny
as described above.
The entry point of the software is in the main.py
, which is called by the custom pre-boot script boot.py
. The general configuration of the hardware (e.g. which pins the buttons are connected to) is done in the config.py
file.
The control of the hardware components is done in the files:
ui.py
- UI system + display controlledring.py
- LED effects for the LED ringScales.py
- readout of the HX711settings.py
- filesystem access for writing/reading recipe files and settings
All these classes can be easily called from all other scripts using the singleton pattern. This makes integration very simple and uniform:
import ledring
import ui
import Scale
# CLEAR DISPLAY
ui().clear()
# SET LED RING
ledring().set_neopixel_full_hsv(ledring().COLOR_PRESET_HSV_H__BLUE)
# GET CURRENT SCALE MEASUREMENT
ScaleInterface().get_current_weight()
The individual menus are designed as a plug-in system. This allows you to quickly create your own extensions.
The plugins are designated in the system with the prefix menu_entry_*.py
and the functions are derived from the base class menu_entry.py
.
This consists of an activate
, teardown
and update
function, which are called accordingly when the corresponding menu entry is called.
class menu_entry_MyPlugin(menu_entry.menu_entry):
def __init__(self):
super().__init__("MyPlugin", "My nice plugin")
# WILL BE CALLED IF USER SELECTS PLUGIN
def preview(self):
print("preview {}".format(self.name))
ui().show_recipe_information(self.name, self.description)
# WILL BE CALLED IF USER ACTIVATES PLUGIN
# E.G. DO SETUP STUFF
def activate(self):
print("activate {}".format(self.name))
ui().show_titlescreen()
# WILL BE CALLED IF USER EXISTS PLUGIN
# E.G. DELETE RESOURCES
def teardown(self):
print("teardown {}".format(self.name))
# WILL BE CALLED IF A USER PRESSES A BUTTON, EVERY SECOND OR IF THE WEIGHT ON LOAD CELL CHANGES
def update(self, _system_command: system_command.system_command):
if _system_command.type == system_command.system_command.COMMAND_TYPE_NAVIGATION:
if _system_command.action == system_command.system_command.NAVIGATION_ENTER: # OK BUTTON
pass
elif _system_command.action == system_command.system_command.NAVIGATION_LEFT: # NEXT BUTTON
pass
elif _system_command.action == system_command.system_command.NAVIGATION_RIGHT: # PREV BUTTON
pass
elif _system_command.type == system_command.system_command.COMMAND_TYPE_SCALE:
print("CURRENT WEIGHT: {}".format(_system_command.value))
elif _system_command.type == system_command.system_command.COMMAND_TYPE_TIMER_IRQ:
print("ELAPSED TIME SINCE LAST CALL: {}".format(_system_command.value))
To add the plugin, import the module in main.py
and add the class into the menu tree:
from menu_entry_MyPlugin import menu_entry_MyPlugin
menu_manager.menu_manager().add_subentries(menu_entry_MyPlugin.menu_entry_MyPlugin())
Its also possible to add sub-menus using the menu_entry_submenu
plugin:
# CREATE SUBMENU WITH CUSTOM TITLE
submenu: menu_entry_submenu.menu_entry_submenu = menu_entry_submenu.menu_entry_submenu("SYSTEM", "ACCESS SYSTEM SETTINGS")
# ADD MENU ENTRIES TO THE SUBMENU
submenu.add_subentries(menu_entry_calibration.menu_entry_calibration())
submenu.add_subentries(menu_entry_calibration.menu_entry_calibration())
submenu.add_subentries(menu_entry_calibration.menu_entry_calibration())
# ADD THE SUBMENU TO THE MAIN MENU
menu_manager.menu_manager().add_subentries(submenu)
To create a finished and complete firmware image, the folder src/firmware_rp2040
contains a bash script which creates the images using Docker
for the Raspberry Pi Pico
and Raspberry Pi Pico W
and the required boot configurations.
# INSTALL AND RUN DOCKER https://docs.docker.com/engine/install/
$ cd src/firmware_rp2040
$ bash ./build_firmware_docker.sh
# RESULTS ARE LOCATED IN THE build FOLDER
One problem was how to build and distribute the Micropython images automatically via e.g. GitHub. The way documented by Micropython does not allow the user to change the code and data after building the u2f, because they are permanently written to the flash (static/lib) fodlers. That's why the complicated build process was created using a Docker image that installs a pre application boot procedure, so that the user source code to the Python file system during the first boot. This way, functions can be easily added/modified by the user and at the same time the finished software can be easily distributed.