Mobile Retro Gaming Console
Retro gaming console that is connected to the network anywhere you go!
Check out the rest of my projects on www.amikers.com!
Supplies
Prepare SD Card With Ubuntu OS Image
Download Ubuntu 18.04 minimal image from here (retropie docs link) and write .img file into the micro SD card using balenaEtcher software. You can download balenaEtcher here. Make sure your micro SD card is inserted into your computer.
Going through the steps to write the image via balenaEtcher is pretty self explanatory. Just follow the steps after you run the software. Just make sure to select the micro SD card you inserted! You do not want to write the image to your computer hard drive (or solid state drive, whichever one is running on your computer).
Power Up Odroid XU4
Insert the micro SD card into Odroid XU4 after .img file is written on it, then power it up using the power supply.
If you are not using the official Odroid power supply, make sure the rating is 5V 4A. Note that the only way to power up the Odroid XU4 is via a barrel jack on the board.
The SD card slot is right next to this barrel jack connector.
After you power it up, your monitor should show a login prompt in terminal interface.
Install RetroPie on Odroid XU4 (part 1)
The instructions below are from the retropie docs (link provided in previous step). For simplicity, I pasted the instructions in this step. I'd advise to go through the steps below as root user.
Note 1: login info to root is username: root, password: odroid. Follow the steps below logged in as root for convenience.
Note 2: To save and exit visudo, ctrl+k then ctrl+x.
Note 3: Here is a detailed Youtube video of how to set up retropie on Odroid XU4 for reference
.
Install RetroPie:
Preliminary steps:
- As the fresh install of Ubuntu Minimal includes only the root user (password: odroid) a new user has to be created:
adduser NameOfYourChoice
- Add new created user to the sudo group:
usermod -a -G sudo NameOfYourChoice
- Upgrade system:
sudo apt update
sudo apt upgrade
sudo apt full-upgrade
shutdown -r now
- Set the locale settings. Below you can find an example for American English:
apt install language-pack-en-base
update-locale LC_ALL="en_US.UTF-8"
update-locale LANG="en_US.UTF-8"
update-locale LANGUAGE="en_US.UTF-8"
shutdown -r now
dpkg-reconfigure locales
- Check if all locale variables were correctly set by using the "locale" command. Below you will find an exemplary output:
LANG=en_US.UTF-8
LANGUAGE=en_US.UTF-8
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=en_US.UTF-8
- Update visudo editor:
sudo update-alternatives --config editor
- Enable NOPASSWD for the new created user:
sudo visudo
This will open up /etc/sudoers file in editor you configured in first step. Add the following line to the end of the file:
NameOfYourChoice ALL=(ALL) NOPASSWD:ALL
This tells sudo that the NameOfYourChoice user doesn't need a password. Save and quit.
- Remove password for the new created user:
sudo passwd -d NameOfYourChoice
Test it all worked:
sudo date
Should give you the date without asking for a password.
- Edit the startup of our tty1 (replace with any tty of choice):
sudo systemctl edit getty@tty1
Add the following text, save and exit
[Service]
ExecStart=
ExecStart=-/sbin/agetty -a NameOfYourChoice --noclear %I $TERM
Finally, restart the whole mess:
sudo systemctl restart getty@tty1
- Disable Screen Blanking in Console and set the CPU Governor Setup:
Edit /media/boot/boot.ini with your editor of choice and add before "setenv bootargs":
setenv RetroPie "no_console_suspend consoleblank=0"
# --- CPU Governor Setup ---
# Uncomment only one line. New governor is set after 90secs after boot.
# ------------------------------------------
# - Performance (Keep all the CPU's at Maximum frequency)
# setenv governor "performance"
# ------------------------------------------
# - Ondemand (recommended)
setenv governor "ondemand"
and add the value of the new created variable to "setenv bootargs":
setenv bootargs "${bootrootfs} ${videoconfig} ${hdmi_phy_control} ${hud_quirks} smsc95xx.macaddr=${macaddr} ${external_watchdog} governor=${governor} ${RetroPie}"
- Reboot and log in as the new created user (NameOfYourChoice):
sudo reboot
- Install libsdl2-dev and xorg
sudo apt install libsdl2-dev xorg
- Install the RetroPie Setup Script (logged in as NameOfYourChoice)
cd
sudo apt install git
git clone --depth=1 https://github.com/RetroPie/RetroPie-Setup.git
- Run the Setup Script:
cd RetroPie-Setup
sudo ./retropie_setup.sh
Note if you have issues while compiling modules and it freezes up on you, then you need to tell it to only compile with one core by running the setup script with this:
sudo MAKEFLAGS="-j1" ./retropie_setup.sh
Install RetroPie on Odroid XU4 (Part 2)
Running the Setup Script will take you to a menu with list of options you can choose. Choose "Basic Install" and press enter.
After it's done going through the basic installation, hover over to "Update" and press enter (just to make sure everything has been updated).
Install RetroPie on Odroid XU4 (Part 3)
Let's make it start on boot up for convenience. If you do not want to do this, feel free to skip this step.
- Hover over "Configuration / tools" and press enter.
- Hover over "autostart - Auto-start Emulation Station / Kodi on boot" and press enter.
- Hover over "Start Emulation Station at boot" and press enter.
- Go back to the main menu, and select "Perform reboot".
After reboot, Emulation Station will launch automatically. Your RetroPie is all set up!
Upgrade to Python 3.8
- Install ppa
sudo add-apt-repository ppa:deadsnakes/ppa
- Update packages
sudo apt-get update
- Install Python3.7 and Python3.8
sudo apt-get install python3.7
sudo apt-get install python3.8
- Install PIP
sudo apt install python3-pip
- Set priority
sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.6 1
sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.7 2
sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.8 3
Note: The number assigned at the end of each line sets the priority. The highest number has the highest priority. For this case, Python3.8 is set to 3, which is the highest out of the 3, which means that the system will default to Python3.8.
Update .bashrc Script to Make "python" Refer to "python3" Automatically
Open .bashrc script with nano:
sudo nano .bashrc
Add at the end of this script:
alias python=python3
alias pip=pip3
Exit out of this script by pressing ctrl+x, then y, then enter.
Restart the .bashrc script to reflect the changes:
source .bashrc
Upgrade PIP
Upgrade PIP:
python -m pip install -U pip
Upgrade Setuptool
Upgrade setuptool:
pip install --upgrade setuptools
Note: If running into error: "pip is being invoked by an old script wrapper":
sudo nano ~/.profile
Add at the end:
export PATH="$HOME/.local/bin:$PATH"
Exit out of this script by pressing ctrl+x, then y, then enter.
Restart the ~/.profile script to reflect the changes:
source ~/.profile
Reinstall PIP and upgrade setuptools:
python -m pip install --upgrade --force-reinstall pip
pip install --upgrade setuptools
Setting Up Cloud Connection Via AWS (part 1)
Now it's time to set up cloud connection so that the games can be pulled from your cloud storage whenever you want and wherever you are.
I called it "Store", but you are free to call it whatever name you want.
Note: "NameOfYourChoice" is the username you set up while setting up the OS at the beginning. In the code below, simply replace "NameOfYourChoice" with your username.
Open the es_systems.cfg file:
sudo nano /etc/emulationstation/es_systems.cfg
Add at the end but before the "</systemList>":
<system>
<name>store</name>
<fullname>Cloud Store</fullname>
<path>/home/NameOfYourChoice/RetroPie/roms/store</path>
<extension>.py</extension>
<command>python3 %ROM%</command>
<theme>store</theme>
</system>
Create directories in /home/NameOfYourChoice/RetroPie/roms
cd /home/NameOfYourChoice/RetroPie/roms
mkdir store
cd store
mkdir nes
mkdir snes
mkdir psx
Note: You can create more directories as you see in the /home/NameOfYourChoice/RetroPie/roms directory. I only wanted to play NES, SNES, and PS games, so I only created directories for those. Who knows? Maybe I might want to play some Sega games later :).
Install boto3 (AWS S3 library for python)
pip install boto3
Note: The reason why I installed Python3.8 was because of boto3. Boto3 does not work on Python3.6 or below anymore.
Setting Up Cloud Connection Via AWS (Part 2)
This part of the tutorial will focus on setting up your AWS account to get access to S3, which is their cloud storage platform.
Go to www.aws.com, and make an account if you don't already have one.
After logging in, on the top search bar, type "s3". You should see "S3" pop up. Click on it, then follow the steps below:
- Create a bucket
- Give it a bucket name, choose your AWS region (closer to you the better, typically), and uncheck "Block all public access". Everything else, you can leave.it the way it is, then click "create bucket" at the bottom.
- After creating the bucket, click the bucket you just created, then click "Create folder".
- Name it "nes", then click "create folder" at the bottom. For "server-side encryption", keep it at "disabled".
- Do this 2 more times to create folders called "snes" and "psx". You can create more folders as you need.
- Once the folders are created, go into each folder and upload ROMs. Make sure the ROMs are in .zip format for NES and SNES games. Make sure the ROMs are in .7z format for PSX games. If you don't want to stick to these formats, make sure to alter the code in the Python file below in Step 12, accordingly.
Next, back on the top search bar, type "iam". You should see "IAM" pop up. Click on it, then follow the steps below.
- Click on "users" at the left.
- If you don't already have a user, create one by clicking on "Add users", then go through the process of creating one.
- Create a username.
- You can just click on "Access key - Programmatic access" for now.
- For permissions, click on "Attach existing policies directly", and then from the list, select "AmazonS3ReadOnlyAccess".
- Note: You can change these settings as you see fit. The steps above are just the minimal requirements to get this project up and running.
- No need to add any tags.
- Finish creating new user
- Click on the username you just created or an existing one.
- Click on "Security credentials".
- Click on "Create access key", and it will give you an Access Key and Secret Key. Make sure you note the Secret Key as this will be the only time you see it.
Now the AWS is set up and ready to go!
Connecting Your Odroid XU4 to the AWS Cloud
To have the Odroid XU4 communicate with the AWS S3 you just set up above, we will create a python script in the /home/<NameOfYourChoice> directory (your default directory).
You can create the Python script from Odroid XU4 via the terminal interface, but this doesn't exactly come with the ease of writing code as if you were to write code using VS Code, IDLE, etc., so I'd advise writing the code in a another computer with VS Code or other coding software, and then transferring it over to the Odroid using a USB stick. But if you are ok with writing code in the terminal interface, please feel free. Refer to Appendix Step below for how to mount USB stick to access.
The Python code is as below:
import boto3
from botocore.exceptions import ClientError
import logging
import time
import subprocess, sys, os
aws_access_key = 'XXXXXXXXXXXXXXX' # Your AWS access key
aws_secret_key = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' # Your AWS secret key
bucket = 'xxxxxxxx' # Your bucket name
prefix_snes = 'snes'
prefix_psx = 'psx'
prefix_nes = 'nes'
s3 = boto3.client(
's3',
aws_access_key_id=aws_access_key,
aws_secret_access_key=aws_secret_key
)
def list_objects(bucket_name, prefix_name):
"""list all objects from an Amazon S3 bucket"""
try:
response = s3.list_objects(Bucket=bucket_name, Prefix=prefix_name)
except ClientError as e:
logging.error(e)
return None
return response
# MAKE SURE YOUR ROM FILE DOES NOT HAVE A PERIOD IN THE MIDDLE OF THE FILE NAME.e.g. if name is "shooter.jets.zip", change it to "shooter jets.zip". A period should only exist right before the file extension.
def list_snes():
# List games for SNES
files_snes = list_objects(bucket, prefix_snes)
files_snes = files_snes.get("Contents")[1:]
for file in files_snes:
store_displayName = str(file['Key']).split('.',1)[0]
#print(store_displayName)
# Make sure to insert your Odroid username below
with open('/home/<NameOfYourChoice>/RetroPie/roms/store/' + store_displayName + '.py', 'w') as f:
f.write(
'import boto3\n'
'from botocore.exceptions import ClientError\n'
'import logging\n'
'import subprocess, sys, os\n'
'import time\n'
'\n'
'store_displayName = str(os.path.basename(__file__)).split(\'.\',1)[0]\n'
# Make sure to insert your Odroid username below
'outputName = \'/home/<NameOfYourChoice>/RetroPie/roms/snes/\' + store_displayName + \'.zip\'\n'
'aws_access_key = \'XXXXXXXXXXXXXXX\'\n' # Your AWS access key
'aws_secret_key = \'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\'\n' # Your AWS secret key
'bucket = \'xxxxxxxx\'\n' # Your bucket name
'key = \'snes/\' + store_displayName + \'.zip\'\n'
's3 = boto3.client(\'s3\', aws_access_key_id=aws_access_key, aws_secret_access_key=aws_secret_key)\n'
's3.download_file(bucket, key, outputName)\n'
'print("Success! Game saved in Super Nintendo.")\n'
'proc = subprocess.Popen([\'killall\', \'emulationstation\'], shell=False)\n'
'print("Restarting Emulationstation...")\n'
'time.sleep(2)\n'
'proc = subprocess.Popen([\'emulationstation\'], shell=False)\n'
)
# Make sure to insert your Odroid username below
args = ['chmod', '+x', ('/home/<NameOfYourChoice>/RetroPie/roms/store/' + store_displayName + '.py')]
proc = subprocess.Popen(args, shell=False)
def list_psx():
# List games for PSX
files_psx = list_objects(bucket, prefix_psx)
files_psx = files_psx.get("Contents")[1:]
for file in files_psx:
store_displayName = str(file['Key']).split('.',1)[0]
#print(store_displayName)
# Make sure to insert your Odroid username below
with open('/home/<NameOfYourChoice>/RetroPie/roms/store/' + store_displayName + '.py', 'w') as f:
f.write(
'import boto3\n'
'from botocore.exceptions import ClientError\n'
'import logging\n'
'import subprocess, sys, os\n'
'import time\n'
'\n'
'store_displayName = str(os.path.basename(__file__)).split(\'.\',1)[0]\n'
# Make sure to insert your Odroid username below
'outputName = \'/home/<NameOfYourChoice>/RetroPie/roms/psx/\' + store_displayName + \'.7z\'\n'
'aws_access_key = \'XXXXXXXXXXXXXXX\'\n' # Your AWS access key
'aws_secret_key = \'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\'\n' # Your AWS secret key
'bucket = \'xxxxxxxx\'\n' # Your bucket name
'key = \'psx/\' + store_displayName + \'.7z\'\n'
's3 = boto3.client(\'s3\', aws_access_key_id=aws_access_key, aws_secret_access_key=aws_secret_key)\n'
's3.download_file(bucket, key, outputName)\n'
'print("Success! Game saved in Playstation.")\n'
'proc = subprocess.Popen([\'killall\', \'emulationstation\'], shell=False)\n'
'print("Restarting Emulationstation...")\n'
'time.sleep(2)\n'
'proc = subprocess.Popen([\'emulationstation\'], shell=False)\n'
)
# Make sure to insert your Odroid username below
args = ['chmod', '+x', ('/home/<NameOfYourChoice>/RetroPie/roms/store/' + store_displayName + '.py')]
proc = subprocess.Popen(args, shell=False)
def list_nes():
# List games for NES
files_nes = list_objects(bucket, prefix_nes)
files_nes = files_nes.get("Contents")[1:]
for file in files_nes:
store_displayName = str(file['Key']).split('.',1)[0]
#print(store_displayName)
# Make sure to insert your Odroid username below
with open('/home/<NameOfYourChoice>/RetroPie/roms/store/' + store_displayName + '.py', 'w') as f:
f.write(
'import boto3\n'
'from botocore.exceptions import ClientError\n'
'import logging\n'
'import subprocess, sys, os\n'
'import time\n'
'\n'
'store_displayName = str(os.path.basename(__file__)).split(\'.\',1)[0]\n'
# Make sure to insert your Odroid username below
'outputName = \'/home/<NameOfYourChoice>/RetroPie/roms/nes/\' + store_displayName + \'.zip\'\n'
'aws_access_key = \'XXXXXXXXXXXXXXX\'\n' # Your AWS access key
'aws_secret_key = \'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\'\n' # Your AWS secret key
'bucket = \'xxxxxxxx\'\n' # Your bucket name
'key = \'nes/\' + store_displayName + \'.zip\'\n'
's3 = boto3.client(\'s3\', aws_access_key_id=aws_access_key, aws_secret_access_key=aws_secret_key)\n'
's3.download_file(bucket, key, outputName)\n'
'print("Success! Game saved in Playstation.")\n'
'proc = subprocess.Popen([\'killall\', \'emulationstation\'], shell=False)\n'
'print("Restarting Emulationstation...")\n'
'time.sleep(2)\n'
'proc = subprocess.Popen([\'emulationstation\'], shell=False)\n'
)
# Make sure to insert your Odroid username below
args = ['chmod', '+x', ('/home/<NameOfYourChoice>/RetroPie/roms/store/' + store_displayName + '.py')]
proc = subprocess.Popen(args, shell=False)
while True:
list_snes()
list_psx()
list_nes()
time.sleep(10) # Update from AWS cloud every 10 seconds
For reference, you can refer to the boto3 docs here.
Downloads
Autostart Cloud Connection
Now that the basics are set up, you'd probably want the cloud connection to automatically start with the Emulation Station.
To do this, we need to edit the crontab file:
crontab -e
Make sure to select the /bin/nano option.
At the end of the crontab script, add the following:
@reboot nohup python3 /home/NameOfYourChoice/cloud_retropie_instructables.py &
Save and exit the file using ctrl+x, y, then enter.
With this in place, the Python script will launch on boot up and automatically connect to AWS cloud.
Set Up USB Speaker
To set up sound, the steps are as below:
sudo apt update
sudo apt install gnome-alsamixer
sudo apt install alsa-utils
Edit the asound.conf script:
sudo nano /etc/asound.conf
Delete all existing text and replace it with:
pcm.!default{
type hw card 1
}
ctl.!default{
type hw card 1
}
Test speaker with command:
speaker-test -c2 hw:Set,0
This will turn on white noise from speaker. Ctrl+c to stop.
To adjust volume:
alsamixer
You will have a graphical interface to control the volume using keyboard.
Set Up LEMBAS LTE/GNSS USB Modem
You can establish internet connectivity using WiFi, but to make it truly mobile, you will need cellular connectivity. This is where the LEMBAS USB Modem comes in. Set this up per the instructions manual that comes with the device.
If you are too lazy to read the instructions manual, follow the simple steps below to set up LEMBAS:
- Go to https://lembas.luner.io to register and purchase a data plan. SIM number is printed on the back of the device.
- Connect the LEMBAS to one of the USB ports of the Odroid XU4 and mount the USB drive of the LEMBAS and run installation script:
cd /media
mkdir NameOfYourChoice
cd NameOfYourChoice
mkdir tedongle
sudo mount /dev/sda1 /media/NameOfYourChoice/tedongle
cd tedongle
cd LEMBAS_SW_Package_V1.0
./te_install.sh
- The script will prompt you to reboot the device. Press enter to reboot.
Now the LEMBAS is set up as plug-and-play, so if you want to deactivate cellular, simply plug it out.
Note: It does state that the best way to unplug the device is to either shut down Odroid first before unplugging or press and hold the small button on the side with a SIM ejector pin or a paper clip to shut it down first before unplugging, but I will leave that up to you.
Besides the high speed LTE connectivity, the LEMBAS also has outdoor GPS functionality. The GPS coordinate are stored in /opt/GnssDataShare in decimal degrees format. You can enter these coordinates on Google maps to visually see where the device is located.
nano /opt/GnssDataShare
I haven't integrated the GPS feature to this project, yet, but I think maybe I will add some kind of cloud tracking capability in the future. Not sure where this capability might be useful, but maybe it can come in handy for some situations.
Finally!
Reboot Odroid XU4 with the LEMBAS plugged in, and now you have a cloud connected retro gaming console! I call this the ultimate mobile gaming machine! What's the use of a portable gaming machine if you can only connect to WiFi at home? I always thought that this limitation defeats the purpose of a portable gaming console, but this is just my opinion.
However, one caveat I'd like to mention is that because Odroid XU4 needs 5V 4A to operate, finding a battery that has this rating might not be readily available. How to get around this is to use a Raspberry Pi, instead. The LEMBAS works very nicely with the Raspberry Pi, as well, so no issues there. Sound set-up and other things might be a bit different from the steps above.
After reboot, you will see that you can navigate to Store from the menu and download any games you have in your AWS cloud. After downloading, Emulation Station is restarted to refresh the data, and you will see the game under "Nintendo", "Super Nintendo", and/or "Playstation". Again, you can always modify the Python script and add a new folder in AWS S3 to play on more emulators that RetroPie offers.
I hope you enjoyed this tutorial!
Appendix: Mounting USB Sticks to Access USB Storage in Ubuntu Minimal
If using USB stick to transfer the Python script to the Odroid, you have to make sure to mount the drive first to access the USB storage. In case you are not familiar, below are the steps to mount the USB drive:
- Before connecting the USB stick to Odroid, type the command:
sudo fdisk -l
- Then, connect the USB stick to Odroid, and type the same command again:
sudo fdisk -l
- Note the drive that was not there initially and then appeared after connecting the USB stick. That is the name of your USB stick. For most cases, it will be /dev/sda1.
- After verifying, change directory to /media:
cd /media
- Create new directory and name it as your username:
mkdir NameOfYourChoice
- Change directory to NameOfYourChoice:
cd NameOfYourChoice
- Create new directory and name it anything e.g. SANDISK (my USB stick was a Sandisk).
mkdir USBNameOfYourChoice
- Mount the USB drive to this directory you created above:
sudo mount /dev/sda1 /media/NameOfYourChoice/USBNameOfYourChoice
- Change directory to the USB drive:
cd USBNameOfYourChoice