Emulating a Bill Validator

Emulating a Bill Validator


Emulating a Bill Validator in Software

We recently had an excellent question presented to us on another topic so we thought we'd dig in and deliver an answer that should help out a lot of other developers. 

The original questions was posted here. Like everything else, there is literally a 100 ways we could solve this problem, each with their own benefits and drawbacks.

  1. Unit Testing - These should always be written anyways but it can be difficult to mock out the serial port in some languages.
  2. Building Hardware - Don't bother... just buy a refurbished bill acceptor and outsource the really hard stuff.
  3. Rolling your Own - Relatively quick and easy if you understand the protocol and know a least one programming language with good serial port support.

Because I love software, have an overabundance of bill acceptors, and don't really want to write unit tests today, we are going to roll our own. Maybe I'll write up some unit tests next week :)

Before we get too far along, please make sure that you are familiar with the RS-232 bill validator protocol.

Since we're running in software, presumably both slave and master on the same computer, we will need a solution to bridge our serial ports.

 

Windows - I use com0com and have never bothered looking for another solution because it just works so darn well.

 

Linux - socat appears to be the answer though I have not yet tried it. Post back if you find a better solution!

 

The concept is pretty simple:

  • Start a background thread to run our virtual bill acceptor as Idle
  • Listen to the keyboard for commands to pass to our virtual bill acceptor
    • Such as events, bill values, etc
    • This will trigger state machine changes e.g. Accepting->Escrow->Stacking->Stacked
  • Provide on screen directions for commands

A slave device sends a message similar to this:

[STX] [length] [ack]  [state] [event] [data] [reserved] [model] [revision] [ETX] [checksum]

 0x02,  0x0B,  0x20,    0x01,  0x10,   0x00,     0x00,    0x01,   0x01,    0x03,   0x3A

 

I am particularly fond of tables so enjoy these beauties :) This data was mostly scraped from our RS-232 manual if you prefere to get to the horse of the source.

RS-232 Slave Table

RS-232 Slave Message Structure

Name Value Description
STX 0x02 Start transmission, always 0x02
Length 0x0B Length of entire message, always 11 for slave
ACK 0x20 Upper nibble indicates mast|slave, lower nibble is message number
State 0x01 Bits are set according to the state table
Event 0x10 Events are set according to the event table
Data 0x00 Extra events and bill value bits
Reserved 0x00 Always set to 0x00 to avoid undefined behavior
Model 0x01 Your BA's model (00-7FH) as hex value
Revision 0x01 Your BA's revision (00-7FH) as hex value
ETX 0x03 End transmission, always 0x03
Checksum 0x3A XOR of each byte length through revision (omit STX, ETX)

State Table

Name Bit Definition
Idling 0 Bill acceptor is awaiting note, operating normally
Accepting 1 Note is currently being transported through bill acceptor, not yet validated
Escrowed 2 Note is valid, awaiting stack or return message from master
Stacking 3 Note is being moved to cash box
Stacked 4 Note has been successfully stacked
Returning 5 Note is being returned to patron
Returned 6 Note has been successfully returned

 

 

Event Table

Name Bit Definition
Cheated 0 Acceptor suspects cheating (e.g. fishing, pullback)
Bill Rejected 1 Acceptor rejected note (bad feed, unidentified note, disabled note)
Bill Jammed 2 Bill is jammed and acceptor cannot recover
Stacker Full 3 Acceptor cannot accept anymore currency, cashbox is full
Bill Cassette Present 4 Set when present, clear when missing
Reserved 5 Set to 0, avoid undefined behavior
Reserved 6 Set to 0, avoid undefined behavior

Extra Events and Bill Values

Name Bit Definition
Power Up 0 Set when BA is initializing, not ready to accept currency
Invalid Command 1 An invalid command was received
Failure 2 The acceptor has failed and cannot recover itself (e.g. motor failure)
Bill value LSB 3  Bits 3-5 are set as the bill numbers, 0-7, where 0 is None/unknown bill
  4 e.g. 010 (binary 2) is the 2nd note, $2 in USA
Bill value MSB 5 e.g. 110 (binary 6) is the 6th note, $50 in USA
Reserved 6 Set to 0, avoid undefined behavior

 

 

So now we know what is going on with the slave. We have some bits to set that tell our master what we're up to and how we're doing. In turn, the master will set some bits to control how we traverse our possible states. The master is another beast entirely but we're not going to worry about today so please...


Just assume our master is perfect and sane!


Now that we know our basics, let's dig into some code!  Since we like to get things done quickly, we'll be working with Python and pyserial. We'll start with defining an Acceptor class that will hold the state of our virtual BA (bill acceptor).

We're going to want to track the following values:
  • State
  • Event(s)
  • Bill Value
  • RS-232 Fields
farnsworth

 

The Object

soft_bill_1

Code doesn't look great on here so I'll just be linking to my Github Soft Bill Project and then explaining what is going on. We'll start with the Acceptor class definition. On a sidenote, there are two things that I really dislike in code: flags as globals and unnecessary multidimensional arrays. Here we track our internal state in a single object to avoid having a whole bunch of globals to track. This object is then passed by the main method into the worker thread so we are sitting pretty.
</div> </div>

Threads!

That's right, we are working with a background thread so we can leave our virtual BA's serial TxRx alone while we arbitrarily modify its state and observe how the master behaves. For now we are not going to worry about race conditions and other multithreading headaches. The thread magic is super simple. Spin up a non-daemon thread, pass the proper arguments, then enter our STDIN loop. Please be sure to set the daemon mode to false so as to avoid leaving the serial port resource open. See the code for a link to the quoted documentation explaining why.

Keyboard Input

So now we have a background thread and STDIN (keyboard input) thread. How do we get our input into the virtual BAs? We've defined a command parser method to handle the interpretation and state modification for us. The last portion is to actually use the data, build our packet, and send it on its way. All the while listening to our master of course. This is all handled in the method we passed into our background thread called serial_runner.


Eventually we will want to incorporate this state machine with out fancy little emulator.</p>

Idling->Accepting->Escrowed->{Stacking, Returning}->{Stacked, Returned}


Wrapping Up

That's basically it! This is still a work in progress and you may have noticed that our slave does not do much besides report events. There is still a system that needs to be built for mocking the bill movement timing as well as compensation for bad/damaged/missing packets. Feel free to ask questions, share your code, or poke holes in anything that I have said. 

 

Happy coding!
The Pyramid Software Team

 

Comments Section

Feel free to comment on the post but keep it clean and on topic.

blog comments powered by Disqus