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.
- Unit Testing - These should always be written anyways but it can be difficult to mock out the serial port in some languages.
- Building Hardware - Don't bother... just buy a refurbished bill acceptor and outsource the really hard stuff.
- 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.
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.
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
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
The Object
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