Retro Arcade Console

Created Date:

28 May 2019

Last Updated / Completion Date:

Status:

Complete

Introduction

In the late 80’s and early 90’s, we had a video game arcade up the road from where we lived, and I used to go there often and play arcade games.

While gaming technology has moved on, there is still a place for those old classics. The old Nintendo NES console was also popular around the time, and I have a lot of good memories playing games with my friends.

Having purchased some Raspberry PIs a few years ago, I discovered back then that one of the popular projects to use one of these brilliant single board computers for, was to build a retro games console. I stumbled upon the RetroPie platform, which is essentially all the software you need (aside from the ROMs).

We all have large TVs now, and I didn’t feel the need to build a large fully featured console, but rather wanted something portable which I could connect to my TV.  What was important to me was to was to recreate the same playing experience, by using similar controls to the large arcade consoles.

So all I needed was a box large enough to allow me to use the controls as well as house all the electronics and the Raspberry PI.

Parts Used

I bought a Sanwa JLF-TP-8YT Ball Top joystick from eBay as well as some mechanical switch pushbuttons, both of which are very similar to what the old arcade machines used.

I realised it would be beneficial to expose the joystick and button functionality as a USB output so that I can also use it with my PC emulator and other games. I found a board that was built for this purpose: the MiniPAC. This board is essentially a programmable keyboard emulator, although it has additional functionality. You connect all the buttons / switches up to it, connect it to USB and then program which keys are activated for the buttons / joystick.

And then of course I used a Raspberry PI 3B+ (which was relatively new at the time), with an official 5.1V power supply.

I made two circuits, one for powering up and shutting down the Raspberry PI with the push of a button (shutdown controller), and the other for switching the USB signal from the Raspberry PI USB port to an external USB socket for plugging the MiniPAC directly into the PC (USB switcher).

The shutdown controller sends signals to and from the Raspberry PI to orchestrate a graceful shutdown, and simply latches power to it to initiate startup, with a Python script on the Raspberry PI handing the events accordingly.

With the USB switcher, in the absence of power to the Raspberry PI the relay is off, and the MiniPAC USB signal is routed to an output on the back of the unit. When the powered up, the relay energizes and switches the USB signal from the output to the Raspberry PI, so that the MiniPAC is connected directly to the Raspberry PI. This essentially gives the controls to the Raspberry PI and cuts off the rear USB output.

Finally I bought some USB and HDMI panel mount cabled sockets, and an SD card extension, for exposing outputs on the back. I had a mains connector in stock.

Shutdown Controller

The Raspberry PI cannot simply be switched off. It needs to be powered down gracefully to avoid corruption to the operating system file structure. To make it easy to shutdown the Raspberry PI, I created a circuit to allow a pushbutton to initiate the shutdown sequence, and then power off the Raspberry PI when it was complete.

I created a simple program for a PIC16F84 microcontroller to listen for the button push, and send a signal to a GPIO pin on the Raspberry PI. A Python script on the Raspberry PI (that launches at startup) listens for the signal, and then commences shutdown. Once complete, an overlay configuration sets a GPIO pin high, which then causes the PIC16F84 to switch off the power relay, removing power from the Raspberry PI.

Pressing the power button again simply causes the PIC16F84 to switch the relay on again, latching power to the Raspberry PI, starting the boot sequence normally.

Shutdown controller circuit diagram:

Veroboard layout:

Completed circuit:

PIC16F84 Assembly code:
ASM
ASM;
;   Title:   RPI Power & Shutdown Controller for Retropie Arcade Joystick Console
;   Author:  Russell Carter
;   Date:    18 July 2019
;   
    List     P=16F84,F=inhx8m
;   #include p16F84.inc  ; processor specific variable definitions
;
;===========Register Files=======================================================
;
INDF        equ   00
TMR0        equ   01
PCL         equ   02
STATUS      equ   03
FSR         equ   04
PORTA       equ   05
PORTB       equ   06
EEDATA      equ   08
EEADR       equ   09
PCLATH      equ   0A
INTCON      equ   0B
OPTION_REG  equ   81
TRISA       equ   85      ; A "1" sets the relevant port bit as input
TRISB       equ   86      ; A "0" sets the relevant port bit as output
EECON1      equ   88
EECON2      equ   89
;
;===========Status Bits==========================================================
;
IRP         equ   07      ; Not Used
RP1         equ   06      ; Not Used
RP0         equ   05      ; 0 = Bank0, 1 = Bank1
NOT_TO      equ   04      ; WDT Time-out Bit*
NOT_PD      equ   03      ; Power Down Bit*
Z           equ   02      ; 1 = Result is 0
DC          equ   01      ; Digit Carry/Not-Borrow*
C           equ   00      ; 1 = Carry-out from MSB
; 
;===========Intcon Bits==========================================================
;
GIE         equ   07      ; 0 = Disables all interrupts
EEIE        equ   06      ; 1 = Enables EE Write complete int
T0IE        equ   05      ; 1 = Enables TMR0 int
INTE        equ   04      ; 1 = Enables RB0/INT int
RBIE        equ   03      ; 1 = Enables RB port change int
T0IF        equ   02      ; 1 = TMR0 has overflowed
INTF        equ   01      ; 1 = RB0/INT int occured
RBIF        equ   00      ; 1 = An RB port pin changed state
;
;===========Option_Reg Bits======================================================
;
NOT_RBPU    equ   07      ; 0 = PortB Pull-ups enabled
INTEDG      equ   06      ; 1 = Int. on rising edge
TOCS        equ   05      ; TMR0 Clk 1 = External: T0CKI
T0SE        equ   04      ; 1 = Inc on falling edge of T0CKI
PSA         equ   03      ; 1 = Prescaler on WDT, 0 = Psa on TMR0
PS2         equ   02      ; Prescaler rate select bits*
PS1         equ   01      ; Prescaler rate select bits*
PS0         equ   00      ; Prescaler rate select bits*
;
;===========Eecon1 Bits==========================================================
;
EEIF        equ   04      ; 1 = Write op. complete (must be cleared)
WRERR       equ   03      ; 1 = Write op. terminated prematurely
WREN        equ   02      ; 1 = Allows write cycles
WR          equ   01      ; 1 = Initiate write (clears automatically)
RD          equ   00      ; 1 = Initiate EEPROM read (clears auto.)
;
;===========Custom Defined Registers (68)========================================
;
;   Usable range: 0Ch to 4Fh
;
COUNT_MS_1  equ   0C      ; 100ms Delay counter
COUNT_MS_2  equ   0D      ; 100ms Delay counter
COUNT_3S    equ   0E      ; 3 Second Delay counter
COUNT_30S   equ   0F      ; 30 Second Delay counter
PRESS_COUNT equ   10      ; Button press counter
TEMPA       equ   11      ; Temp PORTA storage
STATE       equ   12      ; Store the state of the controller
;
;===========Custom Bits==========================================================
;
POWER_ON    equ   00      ; STATE - Status of RPI: 0 = Power off; 1 = Power On
BUTTON      equ   00      ; PORTB - RB0 - Pushbutton: 0 = pressed or contact bounce; 1 = not pressed
SD_STATUS   equ   01      ; PORTB - RB1 - Status from Rasberry PI. 0 = running; 1 (afer booted) = Shutdown
RELAY       equ   00      ; PORTA - RA0 - Relay: 0 = off; 1 = on
LED         equ   01      ; PORTA - RA1 - LED: 0 = off; 1 = on
SHTDWN_CMD  equ   02      ; PORTA - RA2 - Shutdown Command: 0 = off; 1 = on
;
;===========Information==========================================================
;
;   Data format:   Binary   b'MSB...LSB', (Bit7...Bit0)
;   Hex   0x00
;   Decimal   .000
;
;   We need to store PORTA's value in a temp register and perform all operations in that register. 
;   When done we write the result to PORTA. This is because a read-modify-write operation on
;   pin RA4 (open collector) will always read 0 if the output is driving an NPN transistor to ground.
;   The voltage in this case on pin RA4 will be the base-emitter voltage, i.e 0.5-0,7v.
;   Wiring: Resistor from +V to base and RA4, emitter to ground.
;
;===========Beginning of Program=================================================
;
   org      0
   goto     INIT                ; Start Vector
;                               
INIT                            
   clrf     PORTA               ; Ensure all port A bits are off
   bsf      STATUS, RP0         ; Set bank 1
   clrf     TRISA               ; Set port A to outputs
   movlw    b'11111111'         
   movwf    TRISB               ; Set port B to inputs
   bcf      STATUS, RP0         ; Set bank 0
   clrf     PORTA               ; Ensure all port A bits are off
   clrf     TEMPA               ; Clear temp PORTA reg
   clrf     STATE               ; Clear the state reg
;                               
MAIN                            
   btfss    PORTB, BUTTON       ; Test bit RB0 - Power button
   goto     BUTTON_PRESS        ; RB0 is low, so the button is current pressed or in contact bounce
   goto     MAIN                ; Loop
;
BUTTON_PRESS
   btfss    STATE, POWER_ON     ; Check if the RPI is on. If not urn on.
   goto     TURN_ON
   btfsc    STATE, POWER_ON     ; Check if the RPI is on. If so, check if button is held for 3 seconds
   goto     LONG_PRESS
   goto     MAIN                ; Go back to waiting for button press
;
TURN_ON
   bsf      TEMPA, RELAY        ; Switch on the relay
   bsf      TEMPA, LED          ; Switch on the LED
   call     WRITE               ; Commit the values to port A
   bsf      STATE, POWER_ON     ; Set the power on state to true
   call     LOCKOUT_DELAY       ; Wait for the RPI to boot properly before allowing shutdown.
   goto     MAIN                ; Resume monitoring the switch
;
LONG_PRESS
   movlw    .30   
   movwf    PRESS_COUNT         ; Set the value to be counted down - 30 x 100ms = 3 seconds
   goto     MONITOR_BUTTON_1
;
MONITOR_BUTTON_1
   decfsz   PRESS_COUNT         ; Decrement the counter and 
   goto     MONITOR_BUTTON_2    ; Proceed with button monitoring
   goto     SHUTDOWN            ; Counter is down to zero, shutdown the RPI.
;   
MONITOR_BUTTON_2
   call     MONITOR_DELAY       ; 100ms Delay
   btfss    PORTB, BUTTON       ; Is the button still pressed?
   goto     MONITOR_BUTTON_1    ; Yes, loop
   goto     MAIN                ; No, go back to monitoring for button press.
;
SHUTDOWN
   bsf      TEMPA, SHTDWN_CMD   ; Send the shutdown command to the RPI
   call     WRITE
   call     WAIT_FOR_SHUTDOWN   ; Wait for RPI to signal that is has shut down
   bcf      TEMPA, RELAY        ; Turn off relay
   bcf      TEMPA, LED          ; Turn off LED
   bcf      TEMPA, SHTDWN_CMD   ; Turn off shutdown command
   call     WRITE
   bcf      STATE, POWER_ON     ; Set state to turned off
   goto     MAIN
;
WAIT_FOR_SHUTDOWN
   movlw    b'00000010'
   xorwf    TEMPA               ; Toggle RA1 - LED
   call     WRITE
   call     SHUTDOWN_DELAY
   btfss    PORTB, SD_STATUS    ; Check if the RPI has shutdown yet
   goto     WAIT_FOR_SHUTDOWN
   return
;
LOCKOUT_DELAY                   ; 30 Second delay after starting up to allow RPI to boot properly
   btfss    PORTB, BUTTON
   goto     LOCKOUT_DELAY
   call     DELAY_30_SEC
   return
;
MONITOR_DELAY                   ; 3 Second delay while holding button down, before initiating shutdown
   call     DELAY_100MS
   return
;
SHUTDOWN_DELAY                  ; 100Ms delay, which is the time between toggling the flashing LED, 
                                ; waiting for shutdown to complete
   call     DELAY_100MS
   return
;
WRITE                           ; Write the value to Port A
   movf     TEMPA, 0
   movwf    PORTA
   return
;
DELAY_30_SEC                    ; 30 Second delay
   movlw    .10
   movwf    COUNT_30S
DELAY_30_SEC_LOOP
   call     DELAY_3_SEC
   decfsz   COUNT_30S
   goto     DELAY_30_SEC_LOOP
   return
;
DELAY_3_SEC                     ; 3 Second delay - currently UNUSED
   movlw    .30
   movwf    COUNT_3S
DELAY_3_SEC_LOOP
   call     DELAY_100MS
   decfsz   COUNT_3S
   goto     DELAY_3_SEC_LOOP
   return
;
DELAY_100MS                     ; 100Ms delay
   movlw    .30
   movwf    COUNT_MS_1
   movlw    .79
   movwf    COUNT_MS_2
DELAY_100MS_LOOP
   decfsz   COUNT_MS_1
   goto     $+2
   decfsz   COUNT_MS_2
   goto     DELAY_100MS_LOOP
   nop
   nop
   nop
   return
;
   end

PIC16F84 Firmware Flowchart:

Python script:

Python
import RPi.GPIO as GPIO
import time
import subprocess

GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(20, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

while True:
    if GPIO.input(20) == GPIO.HIGH:
        print("Pushed")
        subprocess.call("sudo shutdown -h now", shell=True)
    time.sleep(0.5)

To ensure this runs on startup I added a line to the /etc/rc.local file as follows:

Bash
sudo python /home/pi/gpio_shutdown.py &

For the overlay configuration, edit /boot/config.txt and add:

Bash
dtoverlay=gpio-poweroff,gpiopin=21

Box Construction

For the construction of the box I used an old solid oak cupboard door.

It was not my intention to create a woodworking masterpiece, so I assembled the unit using aluminium brackets to hold all the sides together.

I did want it to look good however, so I gave the playing surface a nice rounded edge and sanded it down to a smooth finish, before applying 3 coats of wood varnish.

I designed the layout on the PC and printed it to scale, and stuck it onto the surface to act as a guide for drilling the holes.

Forstner bits were used to drill the holes to keep them as neat as possible.

Internal Construction & Wiring

The joystick and buttons were fitted and used to test and program the MiniPAC:

Next I mounted the Raspberry PI and it’s power supply, and started creating a wire harness to keep the wiring neat and tidy. I made a metal bracket to hold the Raspberry PI power supply, and mounted the external cabled sockets to the 3mm aluminium back plate:

After that I mounted and wired up the shutdown controller circuit and MiniPAC:

Assembling the console on my electronics bench:

Insertion of the USB switcher board and wiring up of the external cabled sockets:

All cables neatly stuffed in, ready for closing. I used another 3mm aluminium plate for the bottom, with four large rubber feet fitted:

Final Product

The rear panel exposes the following conections:

  • 240V Input
  • Printer style Type B USB socket for connecting the MiniPAC to the PC
  • 2 USB-A 2.0 ports for a keyboard and mouse – connect directly to the Raspberry PI
  • HDMI Output
  • SD Card socket

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *