Snacks for Bitcoin

A Vending Machine Retrofit

Executive Summary

This project is a retrofit of a Crane National 145 Snacktron 1 vending machine, circa 1988, to accept payments and return change in bitcoins. The retrofit takes the form of a Raspberry Pi single-board computer running a software application that communicates with the Bitcoin network over Wi-Fi and with the vending machine via a custom hardware interface module. Bitcoin amounts are translated to and from fiat currency using multiple Internet-accessible price sources.

Hardware Interface

The hardware interface module enables the Raspberry Pi to communicate with the vending machine controller and coin mechanism.

The Raspberry Pi physically and electrically interfaces with the vending machine via a custom hardware module, comprising these parts:

The vending machine's internal digital-logic signals use 5 volts as the “high” logic level and 0 volts as the “low” logic level. Unfortunately, the Raspberry Pi's GPIO pins cannot tolerate 5-volt inputs, and they produce only 3.3-volt outputs. As the Pi's GPIO pins have a constant input resistance, it would be a simple matter to construct a voltage divider to bring the VMC's 5-volt signals down to 3.3 volts, but this strategy would not work in the opposite direction (from the Pi's 3.3-volt levels to the VMC's 5-volt levels). Amplifying the signals requires transistors. The 74244 series of integrated circuits provides a convenient solution.

The 74244 series is an eight-bit, tri-stateable buffer and line driver. The common 74HC244 requires a VCC (supply voltage) of 5 volts and has a VIH (high-level input voltage) threshold of 3.15 volts at VCC = 4.5V. As this input threshold is dangerously close to 3.3 volts and would be even closer at VCC = 5V, this is too close for comfort. The 74AHCT244 integrated circuit solves this dilemma. Its supply requirements are similar to those of the 74HC244, but despite its VOH (high-level output voltage) being 5 volts, its VIH is only 2 volts, which is safely below the 3.3-volt level of the Pi's GPIO outputs. Thus, the 74AHCT244 is ideal for translating the 3.3-volt signals of the Pi to the 5-volt signals of the vending machine.

Going in the other direction — translating signals from the 5-volt levels of the vending machine to the 3.3-volt levels of the Pi — is achieved conveniently using another variant of the 74244. The 74LVC244A requires a VCC in the range of 1.65 to 3.6 volts, so the 3.3-volt supply on the Pi is suitable. Crucially, the LVC accepts input voltages up to 5.5 volts while its outputs remain clamped at VCC. Thus, powered by the Pi's 3.3-volt supply, this chip efficiently translates from the vending machine's 5-volt signals to the 3.3-volt signals required by the Pi's GPIO inputs.

A custom printed circuit board accommodates the two ICs, routing eight 5-volt signals from one connector grouping to six IC inputs and two IC outputs and another eight 5-volt signals from a second connector grouping to two IC inputs and six IC outputs. The corresponding 3.3-volt IC pins connect to header pins, arranged to mate to the expansion header of the Raspberry Pi. While most of the Pi's GPIO pins can be configured for either input or output, special care is taken to ensure that the Pi's TXD and RXD pins, which expose the transmitter and receiver of the Pi's internal UART, are routed respectively to an IC input and IC output, as these two pins are not bi-directional when they are connected to the UART.

The hardware interface module connects the 5-volt supply line from the VMC to the Raspberry Pi's 5-volt supply bus, thereby powering the Pi directly from the vending machine's internal power supply.

Signaling Protocol

The Snacktron implements the MicroMech protocol for interfacing with a coin mechanism. This protocol predates the Multi-Drop Bus (MDB) protocol that is in ubiquitous use today, and details of the MicroMech specification are scant. An extremely poor-quality scan of a pin-out diagram is available in the Coinco 9300-L Operation and Service Manual provided on Coinco's web site. The table of connector pin assignments is reproduced here:

Pin # Function
1 5 VDC Supply Positive
2 5 VDC Supply Return
3 Send (0-Volts Active)
4 Interrupt (0-Volts Active)
5 Data (0-Volts Active)
6 Accept Enable (0-Volts Active)
7 $.25 Dispense (0-Volts Active)
8 $.10 Dispense (0-Volts Active)
9 $.05 Dispense (0-Volts Active)
10 117 VDC Supply Return
11 Reset (+5VDC Active)
12 117 VDC Supply Positive (rectified unfiltered)

The hardware interface module positions the Raspberry Pi in-line between the vending machine controller and the coin mechanism. An upstream interface connects the Pi to the VMC, and a downstream interface connects the Pi to the coin mechanism. Ten of the twelve MicroMech signals are present in each of the connector groupings on the interface module. Pins 10 and 12 are absent, as they carry dangerously high voltages. Instead, these pins are connected directly by wire between the upstream and downstream connectors.

The MicroMech signaling protocol is straightforward:

Coin status bytes follow a prescribed format:

D7 D6 D5 D4 D3 D2 D1 D0
0 CVH CVL /MT05 /MT10 /MT25 0 I
CVH CVL Coin Value
0 0 100 cents (dollar)
0 1 25 cents (quarter)
1 0 10 cents (dime)
1 1 5 cents (nickel)
/MT05 Nickel Tube Status
0 Tube contains fewer than 7 nickels
1 Tube contains no fewer than 7 nickels
/MT10 Dime Tube Status
0 Tube contains fewer than 9 dimes
1 Tube contains no fewer than 9 dimes
/MT25 Quarter Tube Status
0 Tube contains fewer than 7 quarters
1 Tube contains no fewer than 7 quarters
I Coin Routing
0 Cash box
1 Inventory tube

Non-coin status bytes take one of a set of prescribed values:

A dollar coin was detected but rejected because of prevailing conditions in the acceptor.
Two coins are presented to the coin acceptor in rapid succession. The acceptor was unable to evaluate either coin. Both coins shall return via the reject slot.
A coin is detected by the coin acceptor as being lodged between the acceptance gates.
The coin acceptor has just powered up, or has been RESET, and is ready for coin acceptance.
The acceptor has sensed a failed coin tube sensor.
An invalid coin has been detected and returned to the customer via the reject slot.
Indicates the depression of the escrow return lever on the acceptor.
A valid coin has been recognized but not detected passing through to the inventory tubes or to the coin box.
The information in this table is extracted from patent publication WO1993007594 A1.

Each of the values in the above table shall have 0x10 added to it if the coin mechanism is configured to retain no more than 22 coins in the quarter tube.

Software Application

The Raspberry Pi runs a stock Linux kernel and a privileged daemon in userspace. The daemon monitors and manipulates the Pi's GPIO pins via the standard Linux GPIO Sysfs Interface. Input signal edges are detected in hardware and relayed to userspace via the poll(2) system call. The daemon is written in C++ and depends on these external libraries:

The daemon includes a lightweight Bitcoin node implementation that connects to an instance of Bitcoin Core running on any network-accessible machine. Communication is via the Bitcoin P2P protocol, so the Bitcoin Core instance does not need its RPC server enabled. Network traffic is minimal, as the daemon utilizes Bitcoin's Bloom-filter mechanism to filter out almost all transactions except those destined for its own address.

The daemon monitors the bitcoin address associated with the private key supplied to it at startup. When the daemon receives a transaction paying this address, it consults six bitcoin price sources in parallel via public APIs:

If fewer than three of the price sources return a valid response, then the request is retried periodically using a progressive back-off algorithm until at least three of the price sources return valid responses. The daemon respects the Expires response header and will not request pricing information from a given source while any previously received response from that source remains unexpired.

Once valid pricing data has been acquired, the daemon uses the median price to translate the received bitcoin amount into U.S. cents. It then engages the MicroMech signaling protocol to report to the VMC the sequential insertions of an optimal (fewest-coins) mix of nickels, dimes, quarters, and dollar coins. These coin insertions are reported at a rate of approximately 14 coins per second, limited primarily by the slow baud rate of the data transmissions and by the necessity to wait for the VMC to process each transmission before it signals readiness to accept the next.

After the customer makes a selection using the keypad, the VMC may assert signals to dispense change. The daemon monitors the /DISPENSE5, /DISPENSE10, and /DISPENSE25 lines and tallies up the total value of change to return. Once all dispense lines have quiesced for 1 second, the daemon consults the price sources again (respecting any Expires headers from any previously received responses) and converts the signaled amount of U.S. cents back into an amount of bitcoins. It then builds a bitcoin transaction to pay this amount of bitcoins to the customer, signs the transaction, and transmits it to the connected Bitcoin Core node for dissemination to the Bitcoin network.

The determination of the customer's bitcoin address follows somewhat complex rules:

  1. If the received payment pays exactly two distinct addresses, one of which necessarily belongs to the daemon, then the other address is assumed to be a change address controlled by the customer, and the daemon will pay any change to this address.
  2. Otherwise, if the received payment contains an input script that appears to be in the standard form — a DER-encoded ECDSA signature followed by an EC public key — then the public key in the first such input script is assumed to belong to the customer, and the daemon will pay any change to the corresponding P2PKH address.
  3. Otherwise, the customer's address is undetermined, and the daemon will not pay out any change.

The daemon always constructs change transactions so that they redeem the outputs by which the customer originally paid. This guards against a possible double-spend attack in which an adversary would pay a large amount to the machine while double-spending the coins to himself and would then press the escrow return lever to cause the machine to pay out fresh bitcoins. Because the change payment always depends on the original payment, the change payment will only confirm if the original payment confirms. The customer could still potentially defraud the machine by double-spending to get free snacks, but this is both less likely and less severe.

Since the VMC has a 5-cent resolution on credited amounts, the daemon always rounds incoming payments down to the next 5-cent increment. Thus, some residual value paid by the customer may not be credited to the machine. The daemon remembers this residual value and will add it back in when paying out change.

The daemon supports receiving multiple bitcoin payments for one snack purchase. Change is paid up to the amount of each individual payment output in LIFO order. When constructing change transactions, the daemon pays a mining fee of 1000 satoshis per kibibyte of transaction data (or fraction thereof), and it avoids generating outputs of less than 5460 satoshis (the “dust” threshold).

Building the Daemon

  1. Install the prerequisites on your Raspberry Pi and on your build machine.
  2. On your build machine, check out the sources:
    $ git clone
    $ cd vendingpi
    $ git submodule update --init
  3. If you have not built the hardware interface module, you may omit the hardware interface code from the compilation:
  4. Build the daemon:
    $ make RPI_REV=2 CHOST=armv6j-hardfloat-linux-gnueabi
    • If you are using an original Raspberry Pi (the model with 256 MiB of RAM), set RPI_REV=1 instead.
    • If you are compiling on the Raspberry Pi itself, you may omit the CHOST variable assignment. Otherwise, adjust it to the cross-compiler prefix appropriate to your system.
    • If you are omitting the hardware interface code, then you can build the daemon for any ordinary computer (e.g., x86) by compiling on that computer and omitting the CHOST.
  5. Copy out/armv6j-hardfloat-linux-gnueabi/vending to your Raspberry Pi.
  6. Start the daemon:
    $ ./vending --testnet ${BITCOIND_HOST} ${BITCOIND_PORT} ${PRIVKEY}
    • Substitute appropriate values for BITCOIND_HOST, BITCOIND_PORT, and PRIVKEY.
    • Omit --testnet in production.

For demonstration and testing purposes, the daemon supports an extremely rudimentary command interface on standard input:

Future Work