February 21, 2018 · javascript raspberry pi vue

Building a pouring game with a RPi, two scales and Vue

I built a pouring competition game for the people that joined CocktailCity, as part of Bucharest Street Food Festival 2017. The game ran on a Raspberry Pi, connected to two scales via USB (nothing crazy).

Note: Cocktail City photos © 2017

How I’ve come to do this

A while ago I was introduced to Ionut Ivanov of Exquisite Bar Solutions by a common friend. As we’re coming from very different fields of work, we naturally started to think about how we could build something together by merging our passions.

Ionut told me that there’s an upcoming event they will attend, that it will require various activities, and it would be a good place to experiment something cool.

Pour Challenge

The idea — “Pour Challenge”

Pouring a specific amount of liquid using just your hand and your brain is a valuable skill for bartenders. We thought we’ll make a battle game out of this.

The point of the game would be to have two persons pour water in two containers, with a target quantity of liquid in mind. The winner would be whoever got closer.

DYMO scales with USB support

Apparently scales that actually spit data on USB, and don’t just use it for power, are pretty rare. After some research we settled for the DYMO M5 5KG postal scale. We bought two of them from a store in Bucharest called Sanco Grup — horrible customer experience, avoid them. They were the only store to have two of them in-stock.

Connecting to the scales and reading the data was easy using node-hid:

// Vendor ID for the DYMO scales
const vendorId = 2338
// Get all devices and filter by vendorID
let scales = hid.devices().filter(device => device.vendorId === vendorId)
scales.forEach((scale, index) => {
  // Establish a connection with each connected scale
  let connection = new hid.HID(scale.path)
  connection.on('data', (data) => {
    handleDeviceData(index, data)
  connection.on('error', (err) => {
    handleDeviceError(index, err)

function handleDeviceData (index, data) {
  let scaleValue = data[4] + 256 * data[5]
  console.log('Received scale value:', scaleValue)

Raspberry Pi

Making the game server

I wrote the game server using Node.js and it ran on my Raspberry Pi. It’s a simple express app that holds a state of the game, broadcasts state changes via websockets and exposes a configuration and control API.

One more very important thing the server does is that it connects to the two USB scales and reads data from them.

Sample game state:

const gameState = {
  // Scales
  scaleCount: 2,
  // Current game configuration
  config: {
    target: 40,
    seconds: 60,
  // Current game
  running: true,
  secondsLeft: 60,
  // Player information
  players: [{
    name: 'Andrei',
    value: 32
  }, {
    name: 'Ionut',
    value: 40
  // Previous game info
  previousGame: {
    winner: 'George',
    target: 20,
    value: 18

Game screen

Making the game client with Vue.js

Vue.js has been favourite web framework for a while, and I’m lucky enough to play with it daily at my job.

The client connects to the websockets server, and receives the game state broadcasts. Then it just renders the corresponding view according to the state.

There are 3 “views” for the client:

  • A game is configured, but not started yet
  • A game is currently running
  • A game has ended

If the game is not configured yet, the game displays the same waiting screen as the “game configured but not started” one.

Scale table setup

Putting it all together

Here’s how all the different parts came together.

  • The TV was connected to the Raspberry via HDMI.
  • The Raspberry Pi ran the game server and displayed the game client in a fullscreen Chromium instance.
  • The DYMO scales were connected to the Raspberry via USB.

Setup schema

The end

I didn’t make this pretty or extensible and it’s not “production grade” by any means, but it worked well for what we needed. At the end of the day I delivered, it was good enough, I learned stuff in the process, and that’s all that matters.

Thanks a lot for the challenge.

Comments powered by Disqus