GamOdroiD C0 : an Odroid based portable retrogaming
Yet another homemade portable gaming console ! This one is the sequel to the first one I built. On the first build, I used an Odroid-w (pi clone) and a brand new GameBoy case.
For this new project, I wanted something more powerfull to run N64, Dreamcast and PSX games, but also some native linux games… And there was (is ?) not a lot of low power consumption with sufficient CPU+GPU for that : I chose an Odroid C0.
Moreover, instead of using and transforming an existing case, I used a 3d printed one designed by myself : optimized dimensions and form factor 😉
I want to thanks the Odroid community, in particular Meveric for its debian distribution and Odroid optimized packages.
Used components
Here is a list of all components I used for this build.
Main parts:
- Odroid C0
- 8Gb Emmc module
- 128 Gb MicroSD XC (SanDisk Ultra, XC I, class 10)
- 3.5″ NTSC/PAL TFT Display
- A 4x6cm prototype PCB board (ebay)
Audio Parts :
- Stereo 2.8W Class D audio amp
- 2 PSP 2000/3000 speakers (ebay)
- A cheap USB sound card with a small USB cable
Battery Parts:
- 2 lipo batteries : keeppower 16650 3.7v 2500mahprotected (important !)
- 2 MOLEX connectors, 50079-8100 (Ebay)
- 2 MOLEX receptacle, 51021-0200 (Ebay)
Control parts :
- 12 Soft Tactile Buttons (8mm)
- 4 tactile button switches (6mm)
- 2 PSP 1000 Analog sticks (Ebay)
- 1 Analog multiplexer MC14051BCL (Ebay)
Cooling parts :
- 2 PS3 GPU copper heatsink (Ebay)
- 4 15x15mm copper heatsink (Ebay)
- Some 1mm Thermal Pad (Ebay)
- Some Silicon Thermal paste (ebay)
Various other electronic parts:
- A 3mm blue led (ebay)
- Some wires from an old IDE ribbon cable
- Some breadboard connection wires (ebay)
- 3 resistors
Decoration parts:
- Some Nail polish templates for colors (black, yellow, red, green, blue). Found on eBay.
- 200, 600 and 1200 sand paper
- XTC 3D
- White statin Spray paint
Anticipated power consumption
The main sources of power drain are the Odroid C0, the display and the audio system (soundcard + audio amp). Before starting, I measured the consumption of these 3 components :
- Odroid C0 : 200-400 mAh depending on CPU and GPU usage
- Audio system : 310 mAh
- Display : 420 mAh
It’s a total of 1130 mAh at 5v, so 5650 mAh / hour. The batteries I used are (at least) 3.7v x 5000 mAh : it’s 18500 mAh.
The console should (and I confirm, actually) last more than 3h in all cases.
Some early questions and answers:
Why this display with such poor resolution ?
Several reasons for that: low power, real 60 fps, easy wiring (cvbs, so only 1 wire), blurry like old TV which makes an cool hardware anti-aliasing 😉
Why using cylindrical batteries ?
It’s more a matter of space optimization regarding the capacity I wanted. Using a more classical flat battery would have forced to make a case deeper than 2cm. Thought, I have to admit that it was my first intention.
Why using a prototype board to mount additional components ?
The goal was to easily mount all the components as one unique motherboard… and I can actually say, it was usefull !
Why the need for an analog multiplexer ?
The Odroid C0 provides only 2 analog inputs, and one is already used to report the battery level (unfortunately, not that useful, see below). Thus only 1 analog input was available for a total of 4 analog axis (2 thumb sticks with 2 directions each). The only way of reading 4 analog axis with one analog input was a multiplexer. And fortunately, the Odroid C0 has enough digital pins to use 2 of them for analog channels switching.
Emmc module and MicroSD ?
The emmc is really fast regarding microSD. It allows the console to boot in few seconds even with Xorg, a window manager and emulationstation with lots of games.
I use the emmc for the OS and the microSD for the games and video previews.
3D model and printing for the case
Th console case has been modeled with Freecad. I designed it specifically for this project and the very specific size of the motherboard and all components. It was my first 3D model and first 3D print, so it may contains errors…
However, the Freecad files are available on github and STL files are freely distributed on thingverse.
Overview of all printed pieces:
Front internal (black points are marks to make holes for skewing):
Back internal. You can see batteries space at the bottom and some striations for CPU + GPU thermal dissipation:
The whole size is nearly the same as a Nintendo DS. It may not be obvious, but using dimensions of a well known console allow to find good and cheap protection cases. As you can see on photos later, I used a NDS case to protect my GamOdroid C0, which I found for a few euros (2 or 3 if I remember well).
To obtain a nice finish, I first used 600 and 1200 sand paper on all parts.
Then I used a product which name is XTC-3d. It’s awesome and give a nice brilliant finish… but still not a good enough finish for me ;). I used some sand paper again (1200) before using a white satin aerosol painting. This gave me the finish you can see on photos below.
For small parts like buttons and dpad, I used some nail polish. It’s very cheap and actually provided great brilliant finish. I finalized buttons and dpad with some transparent nail varnish to protect colors (buttons are the most used part of the console)
Finished result :
Hardware Assembly
My goal was to build a one piece motherboard in order to make it more robust and easier to put inside the case.
I also built small boards for buttons, DPAD and start + select buttons (sorry, photos are missing).
Display hack
The hack is roughly the same as the one I did for my Retroboy console. However, there was some differences on the connector side : V-in and composite output was reversed this time.
Here is the original display, as found on Adafruit website :
I first removed the white connector, then I wired the V-in directly to the voltage regulator output and added two wires for powering through one of the Odroid 5v pin:
Sound card
I chose a cheap USB sound card with a wire between the board and the USB connector. It was important because it was easier to unsolder.
I started to dismantle wires, connectors and then re-drilled holes. I prepared the Odroid board by adding pins to the first USB connector.
Finally, I soldered the sound card directly on the pins :
Extension board with USB port
I put the extension board just below the USB sound card. I first soldered a USB connector, then I wired it to the second Odroid USB connector through the extension board.
Note that I also soldered the extension board to the Odroid motherboard to make the whole thing more robust.
Finishing audio on the extension board
Having a sound card with analog output is nice, but with a 3.5 audio jack and a good amp to drive speakers is better !
That was exactly the next step : wiring and soldering components on the extension board.
Analog multiplexer wiring
The soldering of this small piece started to add a lot of wires and finished to fill the extension board. I had to use the following: Vdd (Vin), Vss (ground), x (analog output), x0, x1, x2, x3 (analog inputs), A, B (digital switches).
C was not needed as 2 switches were enough to switch the first 4 outputs.
Vee and INH has been wired to ground
Note that I made a voltage divider bridge between x (output) and the analog input of the Odroid. This is because the psp analog sticks and MC14051B operate in 5V whereas the Odroid C0 analog input accept a maximum of 1.8v.
Volume buttons
You may have noticed on the previous photo that there were also 2 push buttons on one edge of the extension board.
I just wired them to GPIO pins to control audio volume:
Start + Select
I used push buttons for start and select buttons. I mounted them on an small additional board together with blue led (for battery monitoring).
Batteries
As indicated before, I used a pair of protected cylindrical lipo batteries. I wired them in parallel to get 5000 mAh.
I had to solder some wires directly on batteries and add a Molex connector to be able to connect the two wired batteries to the Odroid C0 Lipo connector.
Mounting everything in the case
At this time, I had done everything on the hardware side. I started to mount in the front part of the case the display, analog sticks, d-pad, a-b-x-y boards and L1 + R1 buttons.
The display is not glued but maintained with two traversal bars. As you will see in the next photo, theses bars allowed me to also block and drive all wires.
Next step for the front part of the case : adding speakers, start+select buttons board and wiring everything with a common ground… yes at this point, it started to be a mess 😉
Final steps before closing: adding a heat-sink, putting L2+R2 buttons and the motherboard in the back part of the case, then soldering everything to GPIO (note also the yellow wire : this is the composite output of the Odroid that go to the display input 1)
Result of assembly
It was time to close the case and check the result;)
Software part
I did a script that construct 80% of the system including copy of specific config files. The other 20% are for roms and personal customization.
If someone wants to do the same, it should be quite easy to adapt / re-run the script.
Linux distrib and File system organization
Before starting to comment the install script, here are the base install steps I did :
- Deployment of Meveric’s minimal Debian Jessie image on the emmc
- Creation of two partitions on the 128 Gb micro sd: 4 Gb for save states and the rest for roms (they will be mounted has /mnt/states and /mnt/ressources). I did 2 partitions because I had the intention to create a read-only system excepted for states… but I finaly kept a full RW system (ext4 is robust enough).
- Creation of a GameOdroid folder in /root and copy the install script and its dependencies
Install Script
The install script and all dependencies can be found on github. It is organized with functions dedicated for each steps.
first step : prepare the system
The first executed function create custom mount points, copy custom fstab and activate tmpfs :
1 2 3 4 5 6 7 8 9 10 11 12 |
function fstab { echo "fstab and filesystem" mkdir -p /mnt/states mkdir -p /mnt/ressources cp /root/GameOdroid/fstab /etc/fstab sed -i "s/#RAMLOCK=yes/RAMLOCK=yes/" /etc/default/tmpfs sed -i "s/#RAMSHM=yes/RAMSHM=yes/" /etc/default/tmpfs } |
The custom fstab file allows to change mount options in order to optimize for speed (noatime, discard) and use a small tmpfs partition for /var/log
1 |
tmpfs /var/log tmpfs nodev,nosuid,noatime,size=20M 0 0 |
After this first function, the system is rebooted, then upgraded and rebooted again:
1 2 3 4 5 6 7 |
function uptodate { echo "update" apt-get update apt-get upgrade apt-get dist-upgrade } |
The final step of this stage is to install all needed base package (function syspackages). Nothing special here excepted two things :
- evilwm : I had to use a window manager because some native games can’t find the native screen resolution without it. I found that evilwm was a very good candidate for the console : very light and invisible with default settings.
- Antimicro-odroid : it’s a very nice piece of software I did not know about before. It allows to map any keyboard and mouse event to a joypad.
- Python package evdev : used to configure reicast input
- not a package, but important to notice : I used a Odroid C1/C0 specific xorg config file given by Meveric (http://oph.mdrjr.net/meveric/other/C1/xorg.conf)
Games
This part correspond to functions “emulators”, “emulators_glupen64_meveric” and nativegames.
Excepted for Dreamcast games for which I used reicast, all other emulators are part of retroarch :
- pcsx-rearmed (PSX)
- fbalpha (CPS2)
- gambatte (Gameboy color)
- gpsp (Gameboy advance)
- mednafen-pce-fast (Pc-Engine + Cdrom)
- nestopia (Nes)
- picodrive (Sega 32X, SegaCD)
- pocketnes (Snes)
- genesis-plus-gx (GameGear, Genesis, MasterSystem)
- mednafen-ngp (Neogeo pocket color)
For natives games, I selected those that was enjoyable with a gamepad and was running correctly on the Odroid C0 with a small screen:
- hurrican
- hcraft
- frogatto
- SuperMario War
- astromenace
- neverball
- shmupacabra
- aquaria
- Revolt
- Open JK3
- openjazz
- supertuxkart
- mars
- puzzlemoppet
- opentyrian
- pushover
Game launcher
This correspond to the function “userinterface”.
Initially, I wanted to use Attract mode. Unfortunately, the implementation of GLES on Odroid C0/C1 seems not to have implemented glBlendEquationSeparateOES() and glBlendFuncSeparateOES() functions… which are mandatory to compile libFSML… which in turn is mandatory to compile Attract mode.
Thus, I used the latest EmulationStation version with video preview support. As I wanted to change the default splash screen with a custom one, I had to replace “splash_svg.cpp” file in “EmulationStation/data/converted”. This file is a simple C array that contains the bytes of an SVG file.
Despite the classical configuration of systems, I create a specific one that list two scripts to change the display : internal screen or HDMI (see composite.sh and hdmi.sh scripts)
Specific tools
This correspond to the function “localtools”.
This is mainly to handle the custom GPIO gamepad. I had to wrote a small program in C that creates a gamepad through linux’s uinput and poll GPIO to generate events… yes, polling and not IRQ based because the SoC does not have enough IRQ to handle all the buttons.
I named this tool gpio_joypad and the source code is on github. It also handles the analog multiplexer to get left and right analog thumb sticks values.
Boot config file
This correspond to the function “bootini”.
This function consists in copying a customized boot.ini file to the boot partition. The important changes I made are :
- Keeping only two video modes : cvbs480 (activated by default) and vga (commented out).
- Disabled cec and vpu
- Modified kernel arguments:
- “cvbsmode=480cvbs” to get a 60Hz NTSC resolution instead of 50 Hz PAL
- “max_freq=1824” to overclock the SoC (needed for N64 and Dreamcast emulators)
- “quiet loglevel=3 rd.systemd.show_status=false udev.log-priority=3” to make the boot as quiet as possible
Initially, I wanted to display the splash screen early during the boot process. It is well documented on Odroid wiki. Unfortunately it works only for 720p resolutions 🙁
Launch everything at start
This correspond to the function “startup”
The automatic startup of X and Emulationstation at boot consisted in a custom tty1 service in systemd that launch agetty with autologin, a bash profile that launch X when tty variable = tty1 and finaly a xinitrc that start the window manager and Emulationstation.
The custom tty1 service (/etc/systemd/system/getty@tty1.service.d/override.conf):
1 2 3 4 5 |
[Service] ExecStart= ExecStart=-/sbin/agetty --autologin root --noclear %I $TERM |
The bash /root/.profile :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# ~/.profile: executed by Bourne-compatible login shells. if [ "$BASH" ]; then if [ -f ~/.bashrc ]; then . ~/.bashrc fi fi if [ "$(tty)" = "/dev/tty1" ] ; then /usr/local/bin/battery.sh & /usr/local/bin/gpio-joypad & startx -- -nocursor 2>&1 & fi mesg n |
And the /root/.xinitrc
1 2 3 4 5 6 7 8 9 10 |
# a WM is needed some software are correctly sized in full screen # e.g : emulationstation, rvgl evilwm & pid=$! emulationstation.sh & # this allows not to shutdown X when emulation is killed # We want that because we have to kill it after gamelaunch # else it does not reappear on screen (SDL_Createwindow() does never end) wait $pid |
Note that the bash profile start the joypad driver (gpio_joypad) and the battery monitoring script (battery.sh) before starting X.
The battery monitoring script is not very accurate, but I dit not found any way to make a better monitoring to switch on the led on low battery or when charging:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
#!/bin/bash PIN=75 GPIO=/sys/class/gpio ACCESS=$GPIO/gpio$PIN LOWBAT=780 CHARGING=1020 if [ ! -d $ACCESS ] ; then echo $PIN > $GPIO/export echo out > $ACCESS/direction echo 0 > $ACCESS/value fi while true do ADCVAL=$(cat /sys/class/saradc/saradc_ch0) # echo "value : $ADCVAL" # charging if [ $ADCVAL -gt $CHARGING ]; then echo 1 > $ACCESS/value else # low bat if [ $ADCVAL -lt $LOWBAT ]; then echo 1 > $ACCESS/value sleep 1 echo 0 > $ACCESS/value else echo 0 > $ACCESS/value fi fi sleep 2 done |
Finalize & clean up
This correspond to the function “optimize_system”.
In this function, the bash login message is hidden (to make the boot process as silent as possible) and packages cache is cleaned (apt-get clean).
There is also two configuration files that are deployed.
The custom journald.conf is here to write logs in ram instead of disk (better performance):
1 2 |
[Journal] Storage=volatile |
I also created a specific alsa configuration file to add latency and buffers, so most sound stutering are avoided for n64 and dreamcast games:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
pcm.!default { type plug slave.pcm "softvol" ttable.0.1 0.8 ttable.1.0 0.8 } pcm.dmixer { type dmix ipc_key 1024 slave { pcm "hw:1,0" period_time 0 period_size 2048 buffer_size 65536 rate 44100 } bindings { 0 0 1 1 } } pcm.dsnooper { type dsnoop ipc_key 1024 slave { pcm "hw:1,0" channels 2 period_time 0 period_size 2048 buffer_size 65536 rate 44100 } bindings { 0 0 1 1 } } pcm.softvol { type softvol slave { pcm "dmixer" } control { name "Master" card 1 } } ctl.!default { type hw card 1 } ctl.softvol { type hw card 1 } ctl.dmixer { type hw card 1 } |
Emulators specific settings
Global Retroarch configuration
Despite changing buttons and path, I had to adapt some videos parameters of retroarch (root/.config/retroarch/retroarch.cfg) to optimize performance and better suit the hardware.
1 2 3 4 5 6 7 |
video_refresh_rate = "59.950001" video_monitor_index = "0" video_fullscreen_x = "720" video_fullscreen_y = "480" video_vsync = "true" video_threaded = "true" video_force_aspect = "true" |
Core specific configuration
I also did some adjustements on few emulator’s cores.
Allowing 6 buttons for SegaCD and 32X:
1 |
picodrive_input1 = "6 button pad" |
Changing glupen64 parameters to optimize rendering on the Odroid SoC:
1 2 3 4 |
glupen64-cpucore = "dynamic_recompiler" glupen64-rspmode = "HLE" glupen64-43screensize = "320x240" glupen64-BilinearMode = "standard" |
Allowing PSX analog joypad support:
1 |
pcsx_rearmed_pad1type = "analog" |
Reicast
For the Dreamcast emulator, I used reicast-joyconfig to generate the gamepad config and copied the resulting file to /root/.config/reicast/joy.conf
I also changed the fullscreen resolution to adapt it to the CVBS display:
1 2 3 4 |
[x11] fullscreen = 1 height = 480 width = 720 |
Keyboard & mouse mapping for native games
Some native games work fine but require either a mouse or a keyboard (esc, enter, space, shift or up/down/left/right keys).
To map needed keys to the console gamepad I used antimicro. It’s a very nice and easy to use program to map any mouse and keyboard key to any gamepad buttons.
Scraping videos
Emulationstation has an integrated scraper for game informations and pictures, but not for videos.
Moreover, if video previews are supported depending on the chosen themes, they are played throught vlc… which is not accelerated on Odroid C0/C1 SoC. The consequence is that 320×240@30 fps in h.264 is the biggest playable size.
I wrote and used a custom script available on github: https://github.com/jit06/gamesdatabase_scraper
This script parse Emulationstation gamelist folder and scrap videos from www.gamesdatabase.org
Final result : videos & photos
Lessons learned
- No way to correctly monitor Battery on Odroid C0 (charging, charged, etc.)
- Too bad to have only 2 mali 450 on the SoC, even with overclock, it is still too slow for a lot of n64 and Dreamcast games
- Some crash that seems to be related to graphic driver (e.g emulationstation that do not exit properly, hurrican that does not start with the correct resolution from time to time)
- Not possible to use a proper interrupt based joypad driver, too few IRQ available on the soc
- Need for a window manager, else no fullscreen size for games nor emulationstation
- Reicast : seems to emulate the GDRom noise, but I actually find it pretty anoying…
Pingback: Existing RetroGaming handled console – My RetroGaming journey
Emula ps2?
No, ps 2 emulation does not even work on XU4. AFAIK, there is no ps2 emulator on ARM architecture
Pingback: Odroid C1 optimized retroarch
Pingback: GamOdroid C0 : my odroid C0 based handheld console has been updated !