After performing a demonstration of the version 0.9 code and hardware to the hackerspace a few months back, I finally got around to finishing up the major features, dressing it and calling it done (for now).
I admit to some lazyness, but the winter holidays played some part in my delay.
The other major delay was that I got sidetracked on writing a time clock library for the RTC8564 (more on that in another post). Suffice it to say, it took me a few days to hash out exactly how I wanted that library to work, and then had to reintegrate the time functionality to the code of the membership box.
Anyway, the code (as well as the hardware design files) can be found on my github account, here.
Rather than a full rundown of every aspect of the code (as I would likely do in a book or tutorial format), I will just explain the major features and functions, for anyone who may need to maintain the project in the future after me:
The operation of the box is simple enough:
There are a number of reusable strings associated with printing receipts, which store their text strings in flash memory to save operating ram.
Sets up all the hardware. Also verifies the clock status on power up. If clock was running (the backup battery kept the clock active and accurate), the routine does nothing. If it finds the clock had stopped, it sets it to the published date and time of the code (set at the top, near the version number).
Setup also configures the interrupt pin tied to the bill acceptor.
The sound a manual cash register makes when the cash drawer is opened. Also the name of the function which handles the interrupt from the bill acceptor. Each pulse on the interrupt pin adds 1000 yen to the cashIn. There is a milis window which momentarily locks the acceptor until the pulse counts catch up. This MAY NOT be necessary?
caChing ends by calling the cashIn function.
The main line of the program. In fact, many times, when it may APPEAR as though the machine is in a particular mode, it really just cascading through the mainline with particular states set, which determines the actions it may take (or present to the user). It is this look which checks if the balance is zero (which triggers the attract mode), more than zero (which enables donations), more than 5000 yen (enabling a membership payment) and responds to button presses (or not, when the button is not available to the user).
This loop also checks for the audit button inside the machine, which enables viewing and clearing audit data.
When the audit button is pressed, we display the number of donations (in 1000 yen counts) and memberships (in 5000 yen counts) paid into the machine since the audits were last cleared. Pressing the audit button again will exit. Pressing the Membership button will clear the audits. The audit button is only accessable from the inside, necessitating a key. There are only three keys in existance to the box, making it 'marginally' secure. :)
This is the only function that does not cascade through the main loop. Instead, it relies on holdingPattern (a variable) which forces the code to wait here for an appropriate button press (which is debounced via typical millis routines).
holdingPattern will eventually drop back to the mainline, leaving the audits in changed if enough time elapses.
The function which actually overwrites the audit data in flash.
Thanks the user, updates the audit data, and subtracts 1000 yen from the available balance. Ends by returning to the mainline. The function to print a receipt for each donation is avialable by uncommenting one line (a call to printDonation function). But I was concerned about the time taken for each receipt, plus the paper costs. Before exiting to the mainline, processes a cashIn.
Disables the acceptor and ignores button presses until the function completes. Informs the user that receipts are about to be printed. Updates the audit data in flash. Forces a clock time fetch. Then sends out to two print functions, one for THS, followed by one for the member. Upon returning, processes a cashIn, then returns to the mainline.
The function we see when we step up to the machine. When mainline finds no cash remaining in the balance, it fires this routine, which waits for an insert of cash. The bill acceptor is enabled, button lights are off. The top line of the LCD is rotating through several attract messages, designed to encourage people to make donations or pay membership. Each time the message is refreshed, the routine fetches the current time from the clock and displays it on the bottom line of the LCD.
Note that this routine does not actually notice the cash input!
When cash is inserted, regardless of when, the acceptor triggers pulses on the interrupt pin. For each interrupt pulse, the Arduino jumps out of whatever it is doing and increments a timer (by calling caChing), then returns to wherever it was. In the case of idling, when it finishes its run, it drops back to the mainline, which NOW recognizes that there is cash in the machine, and takes a new route.
The function that actually calculates and displays to the user how much money they have available on the machine. cashIn is called anytime the cashCount variable is updated (when caChing registers new cash inserted, or the donate and membership processes deduct cash from the balance).
After updating the user, cashIn then calls setLEDs, followed by some flash eeprom writing to update the audit data. It is through this update that we have some margin of power safety. If power is lost while money is still in the machine, upon powering back up, the mainline code will read the flash, see that the audit byte still is not zero, and will return to a state showing a balance still on the machine. No one can claim lost money due to an inadvertant power cycle.
This function simply sets the button illumination LEDs, based on the current balance on the machine.
This function prints the receipt for the member after a membership is paid for. It uses the adafruit thermal printer library. It also throws up some messages to the user on the LCD indicating what is going on. This function is called by membershipProcess, which had forced a clock update before calling the print functions. Momentarily, the clock data in the Arduino is not being updated, so time is "frozen" although the clock is still running as normal. This way, both receipts will have the same time data, even though they are printed a few moments later.
This receipt tells the member to keep this copy for their records.
The same as the printMembership function, except this receipt asks the member to write their name on it and insert it in the side box. We use this receipt to update the membership records at the end of the month.
The same as the other print functions, except this one is triggered by the donationProcess function, and is formatted like the membership copy. It is for the records of the user. This function is available in code, but the code in the donationProcess function to call this function is commented out.