ccTalk - Part 2 - Coin acceptor handling
Published on 19 August 2013
Coin acceptor
A coin acceptor is a device that can recognize various types of coins based on its weight, shape, size and more (all depends on the device and the manufacturer). Usually, all the different coins it can recognize are separated in what are called validation channels, which means one coin type is assigned an ID.
Coin acceptors usually can recognize up to 16 different coin values, and each of them is assigned one of those validation channels. These devices also have at least two sorter paths, which define the path the coin will take based on the fact that the coin has been recognized or not. The two main paths are the "good coin" path (generally inside the machine) and the "error" path, which normally gives the coin back to the customer.
Data request
To actually get the data from the coin acceptor, the controller must issue a request with header 229 - Read buffered credit or error codes. The coin acceptor will respond with eleven bytes, containing the following information :
[Counter ]
[Result1A] [Result1B]
[Result2A] [Result2B]
[Result3A] [Result3B]
[Result4A] [Result4B]
[Result5A] [Result5B]
- Counter is an event counter. Each event will increase this value.
- Result is stored in two bytes. Usually, the first byte contains the validation channel and the second contains the error code (OK, bad coin, mechanical error, ...) I said usually, because some acceptors invert the two result bytes, resulting in many errors when trying to figure out what happens on those devices. When in doubt, refer to the product documentation.
Since the coin acceptor will return the validation channel value, the controller will need to know which coin type is associated with each ID. It is then mandatory to know this association to be able to process different coins properly.
Interfacing with a coin acceptor
In the previous article, we saw how to create ccTalk packets and requests. Now, let's interface a coin acceptor, handle its returned data and process it !
Initialization
First of all, You have to make sure that the coin acceptor is online by sending a sample poll packet and get the response.
<cctalk src=1 dst=2 length=0 header=254> : 020001feff
<cctalk src=2 dst=1 length=0 header=0> : 01000200fd
Coin acceptor setup
The coin acceptors need to be initialized prior accepting coins. The two main things to do are to set the inhibit status, which define which validation channels are enabled on the coin acceptor. This can be done using the header 231 - Modify inhibit status with two bytes of data. Each bit in the bytes represent a validation channel (little-endian).
<cctalk src=1 dst=2 length=2 header=231 data=ffff> : 020201e7ffff16
<cctalk src=2 dst=1 length=0 header=0> : 01000200fd
The second thing is to enable the coin acceptor, which is often not done by default. To do this, we need to send a request with header 228 - Modify master inhibit status and 0x01 as the data byte - which means enabled.
<cctalk src=1 dst=2 length=1 header=228 data=01> : 020101e40117
<cctalk src=2 dst=1 length=0 header=0> : 01000200fd
Data request and processing
Now that the coin acceptor is ready, it needs to be polled regularly (specs say every 200 milliseconds, but it can be more) and the returned data needs to be parsed and processed.
The counter starts at zero on initialization, and increments up to 255. It will then loop from 1 to 255 and so on. Processing the data is quite simple once you know which coin is what :
Send a request with header 229
Get the response data
- Check the counter value.
If the value is greater than the previous one, check the corresponding number of results
Depending on the error code returned, process the coin ID that has been accepted
GOTO 1
Example code
The following Python code uses the ccTalk library presented in last post It shows a basic coin acceptor initialization and polling, and can be enhanced to support other functionalities :
import serial
import time
from ccTalk import *
ser=serial.Serial('/dev/ttyUSB0', 9600, timeout=1)
def sendMessage(header, data='', source=1, destination=2):
request = ccTalkMessage(header=header, payload=data, source=source,
destination=destination)
request.setPayload(header, data)
ser.write(request)
data = ser.read(50)
messages = parseMessages(data)
for message in messages:
print message
init = ccTalkMessage()
init.setPayload(254)
ok = False
#Wait for device to be initiated
while ok!=True:
ser.write(init)
data = ser.read(50)
try:
messages = parseMessages(data)
response = messages[-1]
print response
except:
continue
if response.payload.header==0:
ok = True
else:
print response.payload.header
#Set inhibit status to allow all
sendMessage(231,'\xff\xff')
#Set master inhibit status to enable device
sendMessage(228,'\x01')
#Read buffered credit or error codes
event = 0
while True:
try:
request = ccTalkMessage()
request.setPayload(229)
ser.write(request)
data = ser.read(50)
messages = parseMessages(data)
for message in messages:
if message.payload.header==0:
data = message.payload.data
if ord(data[0])>event:
event = ord(data[0])
print "Counter : " + str(ord(data[0]))
print "Credit 1 : " + str(ord(data[1])),
print "Error 1 : " + str(ord(data[2]))
print "Credit 2 : " + str(ord(data[3])),
print "Error 2 : " + str(ord(data[4]))
print "Credit 3 : " + str(ord(data[5])),
print "Error 3 : " + str(ord(data[6]))
print "Credit 4 : " + str(ord(data[7])),
print "Error 4 : " + str(ord(data[8]))
print "Credit 5 : " + str(ord(data[9])),
print "Error 5 : " + str(ord(data[10]))
time.sleep(0.2)
except KeyboardInterrupt, e:
print "Quitting..."
break
ser.close()
Teensy implementation
I also created a simple ccTalk controller that can be used on an Arduino or a Teensy device. The code available here polls a coin acceptor and will send the corresponding amount of keystrokes to the host computer. The purpose of this was to add a coin acceptor on my MAMEcab to add a more realistic feeling when playing.
Here is a (crappy) demo of it in action. You can actually see the credits change when I insert a new coin :
In the next post, we'll start messing with a ccTalk bus by injecting data and see what can be done once you have a physical access to the bus.