Electronic Money Check Unit
Minimize 

The electronic money checking unit was requested by a customer. He had a puppet that when a button was pressed it spits water to the people. As a result childrens staying the whole day in front of the puppet and pressing the button again and again. The idea was to replace this button by a control unit where people spent 50cent and can press up to 3 times. So he asked me if I can build something. ~Naturely~ no problem for me - I did a similar project using coin controled game devices some times ago for another customer. So I took a NRI-G13 money checking device and a PIC processor with some relays, buttons, opto-couplers and some other stuff together.
I soldered everything together and programmed the PIC 16C73 processor.
It had enought IO pins for controlling the EMP unit, buttons, LEDs, and the output contact.
Electronic Money Checking

The whole electronic with the NRI device is mounted in a steel case - nobody can break this!

EMC rear view
Front... Rear...

 You can see that the coins are falling in a small case. On the right side you can see the power regulator and the PCB mounted.

Below is the Sourcecode for this device. 

  
Sourcecode
Minimize 

The Sourcecode for the money checking unit is here. It's not too complex and written using the PIC-Wiz C compiler.
It's a nice tool that makes it easy to create coftware for PIC processors.

#pragma chip PIC16F73A 
 
// The Play Game Button / Starts the Game Impulse
#pragma bit IN_PLAYBUTTON @ PORTB.7
 
 
// The Fregame Key Switch / Enables / Disables the Freegame mode
// if turned on longer then 5 secs,
// or simply gives a free game
#pragma bit IN_FREEGAMESWITCH @ PORTA.2
 
// The outputs from electronic money checking unit
#pragma bit IN_EMP0 @ PORTB.4 // 50 cent coin
#pragma bit IN_EMP1 @ PORTB.5 // 1 Euro coin
#pragma bit IN_EMP2 @ PORTB.6 // 2 Euro coin
 
// Locks the money checking unit
#pragma bit OUT_EMPLOCK @ PORTC.6
 
// Status LED output
#pragma bit OUT_STATUS @ PORTC.5
#pragma bit OUT_IMPULSE @ PORTC.4
#pragma bit OUT_LEDLATCH @ PORTC.7
 
#define FALSE 0
#define TRUE 1
 
 
#include "int16CXX.H"
 
 
// constants for impulse times
#define IMPULSE_ON_TIME   500
#define IMPULSE_OFF_TIME 10000
 
// constants for LED flash times
#define STATUS_FLASH_TIME_FAST   300
#define STATUS_FLASH_TIME_SLOW      700
 
#define WATCHDOG_COUNTER_SECS     160
 
// staus variables contrling the flow of operation
bit _EmpEnabled ;
 
// variable to remember the state of the LED
bit _StatusLightOn;
 
// Status values for programmflow
bit _Emp0Low;
bit _Emp1Low;
bit _Emp2Low;
 
bit _FreeGameSwitch0;
bit _FreeGameMode;
 
bit _PlayButton0;
 
 
// counts how long the freegameswitch was turned on
uns16 _FreeGameSwitchCounter;
 
// a counter used for status flashing
uns16 _FlashTime;
 
// how many credits where inserted
uns8 _Credits ;
uns8 _GamesPerCredit;
 
// how many games does teh user have ?
uns8 _Games;
 
// variables used for timing
uns16 _Timer0;
uns16 _Timer1;
uns16 _Timer2;
 
// variable used to indicate games / credits
uns8 _LedValue;
 
// the no one does anything time counter
uns16 _WatchDogCounter;
uns16 _WatchDogCounterSecs;
 
// a state variable indicating, that a game is currently running
bit _IsPlayingGame;
 
uns16 _ImpulseOnTime;
uns16 _ImpulseOffTime;
 
 
// here starts the interrupt service handler
#pragma origin 4
 
// the ISR
interrupt InterruptService( void)
{
    int_save_registers    // W, STATUS (and PCLATH)
 
    if ( T0IF) {
        /* TMR0 overflow interrupt */
        TMR0 = -45;
        T0IF = 0; /* reset flag */
    }
 
    if ( INTF) {
        /* INT interrupt */
        INTF = 0; /* reset flag */
    }
 
    if ( RBIF) {
        /* RB port change interrupt */
        W = PORTB; /* clear mismatch */
        RBIF = 0; /* reset flag */
    }
 
 if ( TMR1IF == 1 ) {
 TMR1IF = 0;
 }
 
   /*
     NOTE: GIE is AUTOMATICALLY cleared on interrupt entry and set
           to 1 on exit (by RETFIE). Setting GIE to 1 inside the
           interrupt service routine will cause nested interrupts
           if an interrupt is pending. Too deep nesting may crash
           the program !
   */
 
    int_restore_registers // W, STATUS (and PCLATH)
}
 
// this method can be used to delay n milliseconds
void Delay1ms( uns16 millisec)
// Delays a multiple of 1 milliseconds at 4 MHz
// using the TMR0 timer
{
    char next = 0;
 
    OPTION = 2; // prescaler divide TMR0 rate by 8
    TMR0 = 2; // deduct 2*8 fixed instruction cycles delay
    do {
        next += 125;
        clrwdt(); // needed only if watchdog is enabled
        while (TMR0 != next)   // 125 * 8 = 1000 (= 1 ms)
            ;
    } while ( -- millisec != 0);
}
 
 
// reset the watchdog values
void ResetWatchDog(void)
{
 _WatchDogCounter = 0;
    _WatchDogCounterSecs = 0;
}
 
void Initialize(void){
 
    INTCON = 0;
 
    OPTION = 0; /* prescaler divide by 2 */
    TMR0 = -45;   /* 45 * 2 = 90 periods */
    T0IE = 0;    /* enabledisable TMR0 interrupt */
 
 TMR1IE = 0;    /* enable / disable 16bit timer 1 interrupt */
 
 ADCON1 = 0x8E; /* set PORTA analog / digital input */
 
    PORTA = 0;
    TRISA = 0x4F; /* Set PORTA input / output Mode */
 
    PORTB = 0xFF;
 TRISB = 0xFF; /* Set PORTB in input mode */
 
    PORTC = 0;
 TRISC = 0x00; /* Set PORTC in output mode */
 
    GIE = 0;     /* interrupts enabled / disabled */
 
}
 
 
// enables / disables the emp lock
void EnableEmp( bit enabled )
{
 // remember status
 _EmpEnabled = enabled;
 
 // set hardware
 if (enabled)
 {
 OUT_EMPLOCK = FALSE;
 }
 else
 {
 OUT_EMPLOCK = TRUE;
 }
}
 
 
/// displays a value from 0 - 9 on LED Display
void DisplayValue(int value){
 OUT_LEDLATCH = TRUE;
 
 // get the upper bits from port C into a temp value
 int tempPortC = PORTC & 0xF0;
 
 // get lower 4 bits from value
 int tempValue = value & 0x0F;
 
 // display the value
 PORTC = tempPortC | tempValue;
 
 OUT_LEDLATCH = FALSE;
}
 
// adds credit for the user
void AddCredits(uns8 credits){
 ResetWatchDog();
 
 do {
 // increase _Credits value
 _Credits ++;
 
 // how many credits for coin
 // -> read bcd value from port b
 _GamesPerCredit = PORTB & 0x0F;
 
 // set a minimumvalue for the user
 if (_GamesPerCredit < 1){
    _GamesPerCredit = 1;
 }
 
 // add value to credits
 _Games += _GamesPerCredit;
 }
 while (--credits != 0);
 
 DisplayValue(_Games);
 EnableEmp(FALSE);
 
}
 
 
// reads the number of games per credit from PORTB
void GetGamesPerCredit(){
 // how many credits for coin
 // -> read bcd value from port b
 _GamesPerCredit = PORTB & 0x0F;
 
 // set a minimumvalue for the user
 if (_GamesPerCredit < 1) {
   _GamesPerCredit = 1;
 }
}
 
 
 
 
 
// checks - if a coin was inserted and adds credits if so
void CheckEmpInput( void ) {
 // we check for money input only if EMP is enabled
 if (_EmpEnabled){
 // check for 50 Cent
 if (IN_EMP0 == FALSE){
   // set state flag
   _Emp0Low = TRUE;
 } else {
   if (_Emp0Low == TRUE){
    // after low impulse we add a credit
    _Emp0Low = FALSE;
    AddCredits(1);
   }
 }
 // check for 1 Euro coin
 if (IN_EMP1 == FALSE){
   // set state flag
   _Emp1Low = TRUE;
 } else {
   if (_Emp1Low == TRUE) {
    // after low impulse we add a credit
    _Emp1Low = FALSE;
    AddCredits(2);
   }
 }
 // check for 2 Euro coin
 if (IN_EMP2 == FALSE){
   // set state flag
   _Emp2Low = TRUE;
 } else {
   if (_Emp2Low == TRUE){
    // after low impulse we add a credit
    _Emp2Low = FALSE;
    AddCredits(4);
   }
 }
 }
}
 
 
 
// Enables a game impulse
void PlayGame() {
 // set current state to playing
 if (!_IsPlayingGame) {
 _IsPlayingGame = TRUE;
 _ImpulseOnTime = 0;
 _ImpulseOffTime = 0;
 
 // reset watchdog counter
 ResetWatchDog();
 }
}
 
 
// checks the state of the user play button
void CheckPlayButtonInput() {
 // if the user hasn't Games -> exit
 if (_Games < 1 ) return;
 
 // if already playing game, wait until finished
 if (_IsPlayingGame) return;
 
 // check button state
 if(IN_PLAYBUTTON == FALSE) {
 // remember state
 _PlayButton0 = TRUE;
 } else {
 // check if button was released
 if (_PlayButton0) {
   _PlayButton0 = FALSE;
   PlayGame();
 }
 }
}
 
// checks, if game impulse is enabled and pulses output
void CheckGameImpulse() {
 // when game is running
 if (_IsPlayingGame) {
 if (_ImpulseOnTime < IMPULSE_ON_TIME) {
   _ImpulseOnTime ++;
   OUT_IMPULSE = TRUE;
 } else {
   OUT_IMPULSE = FALSE;
   if (_ImpulseOffTime < IMPULSE_OFF_TIME) {
    _ImpulseOffTime ++;
   } else {
    // decrease gamecount
    if (_Games > 0) {
     _Games --;
     DisplayValue(_Games);
    }
    _IsPlayingGame = FALSE;
   }
 }
 } else {
 // disable impulse output
 OUT_IMPULSE = FALSE;
 }
}
 
 
// resets the application values and hardware to defaults
void SoftReset(void) {
 // disable impulse output
 OUT_IMPULSE = FALSE;
 
 // reset its state variables
 _IsPlayingGame = FALSE;
 _ImpulseOnTime = 0;
 _ImpulseOffTime = 0;
 
 // reset FreeGame modus
 _FreeGameMode = FALSE;
 
 // reset the watchdog
 _WatchDogCounter = 0;
    _WatchDogCounterSecs = 0;
 
 // reset credits and game values
 _Credits = 0;
 _Games = 0;
 
 // reset LED counter on PCB
 _LedValue = 0;
 
 // Inserting money is possible now
 EnableEmp(TRUE);
 
 // display number of credits on LED
 DisplayValue(_Credits);
}
 
 
// checks the state of the freegame switch
void CheckFreeGameInputSwitch() {
 // check the input state
 // off state means high
 if ( IN_FREEGAMESWITCH == 0) {
 // is it first time check ?
 if (_FreeGameSwitch0) {
   // we count the time the switch was turned on
   _FreeGameSwitchCounter ++;
 } else {
 
   // remember state
   _FreeGameSwitch0 = TRUE;
   // reset counter
            _FreeGameSwitchCounter = 0;
 }
 } else {
 // check if switch was turned off
 if (_FreeGameSwitch0) {
   // reset flag
   _FreeGameSwitch0 = FALSE;
  
   // if we where in free game mode -> reset
 
   // check the time how long the switch was turned on
   if (_FreeGameSwitchCounter < 5000) {
    if (_Games > 0) {
     _FreeGameMode = FALSE;
     SoftReset();
    } else {
     AddCredits(1);
    }
   } else {
    _FreeGameMode = TRUE;
    AddCredits(2000);
   }
 }
 }
}
 
// enables / disables statuslight
void SetStatusLight(bit enabled) {
 _StatusLightOn = enabled;
 OUT_STATUS = enabled;
}
 
// enables / disables the led status light, depending on programmstate
void CheckStatusLight(void) {
 // when playimpulse is active, flash LED fast
 if (_IsPlayingGame) {
 if(_FlashTime < STATUS_FLASH_TIME_FAST) {
   _FlashTime ++;
 } else {
   // Toggle the state of the LED
   SetStatusLight(!_StatusLightOn);
 
   // reset counter
   _FlashTime = 0;
 }
 } else {
 // when Games -> LED On
 if (_Games > 0) {
   SetStatusLight(TRUE);
 } else {
   // No Games, waiting for money or freegame
   if(_FlashTime < STATUS_FLASH_TIME_SLOW) {
    _FlashTime ++;
   } else {
    // Toggle the state of the LED
    SetStatusLight(!_StatusLightOn);
 
    // reset counter
    _FlashTime = 0;
   }
 }
 }
}
 
 
 
// check the WatchdogCOunter value,
// if nothing has happened for a long time
// automatically play a game
void CheckWatchdogCounter(void) {
      if (_WatchDogCounter < 1000) {
            _WatchDogCounter ++;
            return;
      }
 
      _WatchDogCounter = 0;
 
      if (_WatchDogCounterSecs < WATCHDOG_COUNTER_SECS) {
            _WatchDogCounterSecs ++;
            return;
      }
 
      _WatchDogCounterSecs = 0;
 
      if (!_FreeGameMode) {
            _Games = 1;
      }
      PlayGame();
}
 
 
// the main application loop
void main(void) {
 
 // initialising the ports, setting defaults and enables interrupts, etc.
 Initialize();
 
 // reset to default software state
 SoftReset();
 
 while(1) {
       // check, if someone has inserted money,
       // pressed the playbutton,
       // or enabled / disabled freegame mode
       CheckFreeGameInputSwitch();
 
       // check, in what state we are
       if (_FreeGameMode == FALSE) {
         CheckEmpInput();
       }
 
       // Disable / Enable Emp
       if (_Games > 0) {
         EnableEmp(FALSE);
         CheckPlayButtonInput();
       } else {
         EnableEmp(TRUE);
       }
 
       // check if game is in playing state
       CheckGameImpulse();
 
       // set statuslights
       CheckStatusLight();
 
       // check watchdog, if we play a game automatically
       CheckWatchdogCounter();
 
       // delay 1 millisecond
       Delay1ms(1);
    }
}