Здравствуйте! Это ретро игровая приставка на базе Raspberry Pi. В картридже от игровой приставки NES. Для игры может использоваться интегрированный экран. При выводе же видео по HDMI он показывает изображение обложки игры, а также температуру ядра, см. фото. Предлагаю немного понастальгировать и почитать про сборку и настройку такого устройства. Осторожно, далее много трафика и фото.

Ностальгия


В детстве мы, кому сейчас за 25-35, любили играть в приставки. Тогда не было новомодного слова консоли, поэтому буду называть их так. Мое знакомство с приставками началось с Денди (клон NES). Super Mario Bros, Contra, Ducktales, Battletoads это лишь часть хитов того времени. Затем всем двором собирались поиграть у моего друга в Сегу (Sega Mega Drive), в такие хиты как Sonic, Ultimate Mortal Combat 3, Contra Hard Corps и другое. А уж когда вышла Play Station, надо сказать, она добралась к нам не сразу, то серия Resident Evil вызывала какой-то по истине пугающий восторг.


Смахнуть слезу ностальгии мне помогла эта картинка. Надо сказать, я не первый, кто придумал вставить Raspberry Pi в корпус от картриджа NES. Да, это не мое устройство. И этот картридж подходит для этих целей практически идеально, он маленький и красивый. А внутри много места. Судите сами.


Внутри Raspberry Pi Zero, USB хаб, и пара удлинителей. Все палки посажены на гов термоклей.

Сборка


Захотелось собрать нечто тоже самое или похожее. Был выбран такой же картридж от NES. Другие варианты либо совсем маленькие, либо выглядят не очень. В качестве пламенного сердца ретро-машины выступил Raspberry Pi 3B. Были закуплены на али, также, USB разъемы с удобными выводами для пайки. Вот это вот всё.


Ах да, будете повторять этот проект, не берите хитовые картриджи. Их и так мало осталось. Возьмите что-нибудь более распространенное или менее известное. Есть также вариант взять картридж с али, но оригинал, он душу греет. Мой 1986 года выпуска.

Также вам понадобится специальная отвертка nes screwdriver, если картридж закручивается на три шурупа. Есть также ранние варианты картриджей с пятью шурупами, они откручиваются обычной плоской отверткой. У меня как раз такой.

С картриджа канцелярским ножом и полотном от ножовки по металлу срезаны и спилены все мешающие части.


С Raspberry Pi были спаяны выступающие разъемы. Знатоки говорят, что можно обойтись и без паяльной станции, если сначала аккуратненько разобрать разъемы кусачками, а потом выпаять по одной ножке. Всё наклеено на термоклей. Между разъемами USB и малинкой проложил изолятор из неплавящегося пластика. Не забывайте соединять GND Raspberry Pi и корпус USB разъема (на картинке выше не показано). Иначе некоторые джойстики не будут работать.


На малинку был установлен последний Retropie. Поначалу стоял Recalbox, но я от него отказался, поскольку у него не поддерживается вибрация джойстиков на приставках Playstation 1. Но надо сказать, Recalbox чуть удобнее, в плане того, что работает из коробки и не надо ничего настраивать. В Retropie куча настроек, настраивать можно бесконечно. Этим он мне и понравился.
Вот что получилось. Подключаются любые джойстики с xinput.



Экран


Аппетит приходит во время еды. Как насчет встроить экранчик в картридж? Для игры или вывода изображения обложки игры. Был заказан такой экран. Это 800*480 3,5 дюймовый экран с параллельным интерфейсом DPI, который использует почти все GPIO Raspberry Pi. У меня он работает в режиме 120 Hz (но рендер все равно в 60 Hz), отклик мгновенный. Единственный минус — это 6 битная матрица. Всего 262 144 цвета. Хотя, в общем-то, это особо не заметно, судите сами.

Скриншот из игры Comix Zone (Кликабельно)

Все ножки с экрана и Raspberry Pi были спаяны. Экран был припаян двумя шлейфами с раздербаненного старенького кабеля IDE. Под экран установлены закладные в 5мм, он не касается нижней стенки картриджа. По периметру дисплея наклеена изолента, чтобы не было боковых засветов. Все приклеено на термоклей. На верхней крышке я прорезал окно и вклеил стекло толщиной 2мм.


Для того, чтобы запустить вывод изображения на этот экран, надо поправить конфиг /boot/config.txt. Рекомендую для обладателей Windows программу WinSCP, которая соединяясь с Raspberry Pi по SSH, дает представление файлов как в Total commander'e. Очень удобно. Особенно, если запустить ее в режиме shell sudo su -, которая дает доступ ко всем файлам.

К сожалению, когда включен DPI экран, вывод видео по HDMI не работает. Поэтому я создал в папке /boot/ два конфига, один config_hdmi.txt, второй config_dpi.txt. В конфиге config_hdmi.txt выставлено разрешение 1080p60Hz и убран overscan. config_dpi.txt содержит настройки DPI экрана.

config_dpi.txt
# uncomment if you get no picture on HDMI for a default "safe" mode
#hdmi_safe=1

disable_overscan=1

# uncomment to force a specific HDMI mode (this will force VGA)
#hdmi_group=1
#hdmi_mode=1

# Sound output. Set to 0 or comment for autodetect, 1 for DVI, 2 to force HDMI.
#hdmi_drive=2

# Using /etc/modules is deprecated and no longer supported on 4.4 kernel
# So manually enable audio
dtparam=audio=on

config_hdmi_boost=0

# force hdmi while the tv can take time before sending the signal on the hdmi output
hdmi_force_hotplug=1

# uncomment for composite PAL
#sdtv_mode=2

# uncomment for lirc-rpi
#dtoverlay=lirc-rpi

#3.5 HD tft screen 800x480
dtoverlay=dpi24
overscan_left=0
overscan_right=0
overscan_top=0
overscan_bottom=0

#Banggood
framebuffer_width=800
framebuffer_height=480
dtparam=spi=off
dtparam=i2c_arm=off
enable_dpi_lcd=1
display_default_lcd=1
dpi_output_format=0x6f015
dpi_group=2
dpi_mode=87
hdmi_timings=480 0 16 16 24 800 0 4 2 2 0 0 0 120 0 46080000 6
display_rotate=3


# if you plug your tv at the same time as your rpi and that the rpi switches from the hdmi or give a low resolution because tv had no enough time to initialize it
boot_delay=3

# uncomment if you don't want the rainbow at startup
#disable_splash=1

# default CEC name
#cec_osd_name=recalbox

dtparam=spi=off

# Overclock
gpu_mem_256=128
gpu_mem_512=256
gpu_mem_1024=256
overscan_scale=1
gpu_mem=256
start_x=0
enable_uart=0

avoid_safe_mode=1

kernel=zImage

config_hdmi.txt

# For more options and information see
# http://rpf.io/configtxt
# Some settings may impact device functionality. See link above for details

# uncomment if you get no picture on HDMI for a default "safe" mode
#hdmi_safe=1

# uncomment this if your display has a black border of unused pixels visible
# and your display can output without overscan
disable_overscan=1

# uncomment the following to adjust overscan. Use positive numbers if console
# goes off screen, and negative if there is too much border
#overscan_left=16
#overscan_right=16
#overscan_top=16
#overscan_bottom=16

# uncomment to force a console size. By default it will be display's size minus
# overscan.
#framebuffer_width=1280
#framebuffer_height=720

# uncomment if hdmi display is not detected and composite is being output
hdmi_force_hotplug=1

# uncomment to force a specific HDMI mode (this will force VGA)
hdmi_group=1

#1080p60fps
hdmi_mode=16

# uncomment to force a HDMI mode rather than DVI. This can make audio work in
# DMT (computer monitor) modes
#hdmi_drive=2

# uncomment to increase signal to HDMI, if you have interference, blanking, or
# no display
#config_hdmi_boost=4

# uncomment for composite PAL
#sdtv_mode=2

#uncomment to overclock the arm. 700 MHz is the default.
#arm_freq=800

# Uncomment some or all of these to enable the optional hardware interfaces
#dtparam=i2c_arm=on
#dtparam=i2s=on
dtparam=spi=off

# Uncomment this to enable the lirc-rpi module
#dtoverlay=lirc-rpi

# Additional overlays and parameters are documented /boot/overlays/README

# Enable audio (loads snd_bcm2835)
dtparam=audio=on
gpu_mem_256=128
gpu_mem_512=256
gpu_mem_1024=256
overscan_scale=1
gpu_mem=256
start_x=0
enable_uart=0


Переключать конфиги будет наш автоскрипт который доступен по сети
\\192.168.x.xxx\configs\all\autostart.sh или в папке на устройстве /opt/retropie/configs/all/autostart.sh

autostart.sh
# Check to see if display is not connected
_NOHDMI=$(tvservice -n ) || true
# Check to make sure it's not already in LCD mode
_ISLCD=$(tvservice -s | grep "LCD") || true
# HDMI is connected - turn off backlight LCD
_HDMI=$(tvservice -s | grep "0x12000a") || true

if [ -z "$_NOHDMI" ]; then
  if [ "$_ISLCD" ]; then
    printf "NO HDMI connected, LCD DPI display config already active\n"
    #do nothing
  else
    printf "NO HDMI connected, Switching to LCD DPI display\n"
    #change config to Hyperpixel and reboot since no display detected

    sudo cp /boot/config_dpi.txt /boot/config.txt
    sudo reboot now

  fi
elif [ "$_NOHDMI" ]; then
  if [ "$_ISLCD" ]; then
    printf "HDMI is connected, but LCD DPI config is being used\n"
    #we need to switch to HDMI display config and reboot

    sudo cp /boot/config_hdmi.txt /boot/config.txt
    sudo reboot now

  elif [ "$_HDMI" ]; then
    printf "HDMI is connected, HDMI config detected, so turning off LCD BL\n"
    #we need to shut off the backlight on the Hyperpixel display since we aren't using it
    #don't panic, a reboot automatically resets this - it isn't a persistent value
    #also to do this the config.txt file needs to load the backlight module on startup
    #to control it even if you aren't loading the overlay driver for the display

    # this comment

    #echo 1 | sudo tee /sys/class/backlight/rpi_backlight/bl_power

  else
    #debugging catchall - shouldn't happen since we expect 0x12000a - but you never know...
    printf "HDMI is connected, HDMI config detected, your tv might not like 1920x1080 resolution\n"
  fi
else
  #do nothing - based on previous if statements - you either have HDMI or you don't have it...
  printf "Debug catchall point - sorry I can't help you out - I haven't run into the error state yet!\n"
fi

emulationstation #auto


Также был написан скрипт switchscreen.sh, который надо положить в /home/pi/RetroPie/retropiemenu/. Этот скрипт переключает конфиги вручную и доступен из главного меню настроек Retropie. Надо после его запуска вытащить кабель HDMI, а то после перезагрузки всё переключится обратно автоматически. Не забудьте скрипту прописать

chmod +x /home/pi/RetroPie/retropiemenu/switchscreen.sh

switchscreen.sh
#!/bin/bash
# Check to see if display is dpi
_DPI=$(grep dpi /boot/config.txt) || true

if [ "$_DPI" ]; then
  printf "Switch to HDMI\n"
  sudo cp /boot/config_hdmi.txt /boot/config.txt
  sudo reboot now

else
  printf "Switch to DPI LCD\n"
  sudo cp /boot/config_dpi.txt /boot/config.txt
  sudo reboot now
fi
exit 0


В autostart.sh видны следы кода, отключающие подсветку DPI экрана, когда включен HDMI. Этот скрипт я скопипастил в интернете, так что на нашем экране это не работает. Можно было бы заморочиться с транзисторами, чтобы отключать питание экрана, но зачем, если нам надо выводить картинку обложки в данный момент запущенной игры. Надо только разобраться, что такое DPI.

Parallel Display Interface (DPI)


DPI, как было сказано, это параллельный интерфейс для экранов. Информации по этому интерфейсу раз, два и обчелся. Ну а теперь еще и здесь будет, на русском.

Все попытки включить DPI консольными командами во время работы HDMI результатов не дали. Возможно делал что-то не то. Был принят к реализации план Б: выводить изображение на малый экран, дергая программно за ножки GPIO.

DPI характеризуется тем, что один пиксель передается дисплею за один такт сигнала clock. Поскольку у нас матрица 6-битная, то это 18 выводов для трех цветов, а также присутствуют выводы display enable (обозначает валидную дату), h_sync (устанавливает нулевой адрес горизонтальной строки в контроллере дисплея), v_sync (устанавливает нулевой адрес вертикальной строки в контроллере дисплея). Защелкивание данных происходит по заднему фронту сигнала clock. Это известно, если расшифровать строчку «dpi_output_format=0x6f015» из нашего config_dpi.txt согласно этой терминологии.

dpi_output_format
output_format          = (dpi_output_format >>  0) & 0xf;
rgb_order              = (dpi_output_format >>  4) & 0xf;

output_enable_mode     = (dpi_output_format >>  8) & 0x1;
invert_pixel_clock     = (dpi_output_format >>  9) & 0x1;

hsync_disable          = (dpi_output_format >> 12) & 0x1;
vsync_disable          = (dpi_output_format >> 13) & 0x1;
output_enable_disable  = (dpi_output_format >> 14) & 0x1;

hsync_polarity         = (dpi_output_format >> 16) & 0x1;
vsync_polarity         = (dpi_output_format >> 17) & 0x1;
output_enable_polarity = (dpi_output_format >> 18) & 0x1;

hsync_phase            = (dpi_output_format >> 20) & 0x1;
vsync_phase            = (dpi_output_format >> 21) & 0x1;
output_enable_phase    = (dpi_output_format >> 22) & 0x1;

output_format:
   1: DPI_OUTPUT_FORMAT_9BIT_666
   2: DPI_OUTPUT_FORMAT_16BIT_565_CFG1
   3: DPI_OUTPUT_FORMAT_16BIT_565_CFG2
   4: DPI_OUTPUT_FORMAT_16BIT_565_CFG3
   5: DPI_OUTPUT_FORMAT_18BIT_666_CFG1
   6: DPI_OUTPUT_FORMAT_18BIT_666_CFG2
   7: DPI_OUTPUT_FORMAT_24BIT_888

rgb_order:
   1: DPI_RGB_ORDER_RGB
   2: DPI_RGB_ORDER_BGR
   3: DPI_RGB_ORDER_GRB
   4: DPI_RGB_ORDER_BRG

output_enable_mode:
   0: DPI_OUTPUT_ENABLE_MODE_DATA_VALID
   1: DPI_OUTPUT_ENABLE_MODE_COMBINED_SYNCS

invert_pixel_clock:
   0: RGB Data changes on rising edge and is stable at falling edge
   1: RGB Data changes on falling edge and is stable at rising edge.

hsync/vsync/output_enable_polarity:
   0: default for HDMI mode
   1: inverted

hsync/vsync/oe phases:
   0: DPI_PHASE_POSEDGE
   1: DPI_PHASE_NEGEDGE




Последовательность битов приведена на рисунке выше. На каждую строчку, начинающуюся с VSYNC, приходится полный цикл тактов строчки HSYNC. Это на один кадр. Back и Front porch это так называемые отступы у дисплея, которые есть в памяти контроллера, но их нет на экране.

Теперь расшифруем строку «hdmi_timings=480 0 16 16 24 800 0 4 2 2 0 0 0 120 0 46080000 6» из нашего config_dpi.txt. Здесь ничего сложного нет, просто записываем параметры по порядку.


Какие GPIO ноги используются для передачи цветов, показано на этой картинке. У нас Mode 5. Внимание, это не ноги по порядку, а именно обозначения GPIO! Остальные сигналы Clock — GPIO 0, DE — GPIO 1, VSYNC — GPIO 2, HSYNC — GPIO 3.


Быстренько был установлен питон, необходимая GPIO библиотека и написан hello-скрипт хотя бы окрашивающий экран в синий цвет. А еще у меня был когнитивный диссонанс, когда Notepad++ на Windows не показывал отступы, там где они были в nano, при этом при переносе строк делал табуляцию, а не пробелы. Кстати, VSCode тем же самым грешит, как поправить, не нашел.

Python script
from gpiozero import LED
from time import sleep

sleep_time = 0.0

clock = LED(0)
de = LED(1)
vsync = LED(2)
hsync = LED(3)

red2 = LED(16)
red3 = LED(17)
red4 = LED(18)
red5 = LED(19)
red6 = LED(20)
red7 = LED(21)

green2 = LED(10)
green3 = LED(11)
green4 = LED(12)
green5 = LED(13)
green6 = LED(14)
green7 = LED(15)

blue2 = LED(4)
blue3 = LED(5)
blue4 = LED(6)
blue5 = LED(7)
blue6 = LED(8)
blue7 = LED(9)


def v_sync(frame):
    vsync.on()
    for n in range(frame):
        clock.on()
        #sleep(sleep_time)
        clock.off()
        #sleep(sleep_time)
        #print("vsync")
    vsync.off()
    pass

def h_sync(frame):
    hsync.on()
    for n in range(frame):
        clock.on()
        #sleep(sleep_time)
        clock.off()
        #sleep(sleep_time)
        #print("hsync")
    hsync.off()
    pass

def clc(frame):
    for n in range(frame):
        clock.on()
        #sleep(sleep_time)
        clock.off()
        #sleep(sleep_time)
    pass


red2.off()
red3.off()
red4.off()
red5.off()
red6.off()
red7.off()

green2.off()
green3.off()
green4.off()
green5.off()
green6.off()
green7.off()

blue2.on()
blue3.on()
blue4.on()
blue5.on()
blue6.on()
blue7.on()

clock.off()

de.off()
#de.on()

sleep(sleep_time)

v_sync(2)
clc(2)

for row in range(5):
    v_sync(2)
    # v_back_porch(2)
    clc(2)
    for column in range(800):
        h_sync(16)
        # h_back_porch(16)
        clc(16)
        # LCD column
        de.on()
        clc(480)
        de.off()
        # h_front_porch(24)
        clc(24)
    # v_front_porch(4)
    clc(4)
de.off()


red2.off()
red3.off()
red4.off()
red5.off()
red6.off()
red7.off()

green2.off()
green3.off()
green4.off()
green5.off()
green6.off()
green7.off()

blue2.off()
blue3.off()
blue4.off()
blue5.off()
blue6.off()
blue7.off()




Слишком ме-е-едлено, Python. Для меня было неожиданностью то, что уже отрисованные пиксели исчезают. Хотя, чего я ждал, это же очевидно.

Принято решение переписать всё на С. Работает резвее, примерно несколько кадров в секунду. Экран рябит как на старых ЭЛТ мониторах или радарах. То что надо. Ретро! Код выложил на GitHub.


Доступ к GPIO осуществляется напрямую, через регистры SoC чипа BCM2837. Примеры кода брал здесь.

Началось всё с совсем крохотной программки, но теперь на дисплей выводятся еще загрузка процессора и памяти в виде маленьких скролл-баров. Температура процессора рисуется вот таким шрифтом.


Чтобы запустить код, надо скопировать содержимое репозитория в папку /home/pi/lcd_screen/ (Кто знает как на C под nix написать путь относительно бинарника, а не рабочей папки? Поэтому пока только в эту папку) и выполнить следующие команды.

gcc ./lcd_screen/lcd.c -o ./lcd_screen/lcd
chmod +x ./lcd_screen/lcd

Либо скачать сразу бинарник из релизов. Чтобы запустить, надо набрать следующее. SUDO требуется для доступа к GPIO.

sudo ./lcd_screen/lcd path/file.bmp usec_per_frame

Где «path/file.bmp» — путь до файла изображения, работает только с BMP форматом, «usec_per_frame» — задержка между кадрами в микросекундах, чтобы напрасно не грузить процессор.

Автоматизируем запуск нашей программки lcd, чтобы она выводила нужную обложку, когда игра запущена. Конечно же, для загрузки обложек в Retropie используется встроенный scrapер.
Установим конвертер изображений. Он будет конвертить все форматы в bmp, поддерживаемый программой. Заодно и изменять размер, чтобы изображения были на весь экран.

sudo apt-get install imagemagick

В директории /opt/retropie/configs/all/ создадим два файла: runcommand-onstart.sh и runcommand-onend.sh. Эти скрипты Retropie автоматически выполняет при загрузке/завершении любой игры. Не забудьте прописать chmod +x каждому файлу.

runcommand-onstart.sh
#!/bin/sh

system="$1"
emulator="$2"
romname="$3" #with path

#echo $system

# LCD programm here: 
#find current config: dpi or hdmi
_DPI=$(grep dpi /boot/config.txt) || true
if ! [ "$_DPI" ]; #if hdmi 
 then
  sudo killall lcd 
  # remove path
  filename=$(basename "$romname")
  # remove extension
  filename="${filename%.*}"
  # add image path
  file_png="/home/pi/.emulationstation/downloaded_images/$system/$filename-image.png"
  file_jpg="/home/pi/.emulationstation/downloaded_images/$system/$filename-image.jpg"
  #if system snes and n64 then rotate image 270 degree
  if [ "$system" = "snes" ] || [ "$system" = "n64" ]
  then
	if test -f "$file_png"; #fing file
	then  
		convert "$file_png" -resize '800x480' -type TrueColor -alpha Remove -rotate 270 /home/pi/lcd_screen/work.bmp
	elif test -f "$file_jpg"; 
	then 
		convert "$file_jpg" -resize '800x480' -type TrueColor -alpha Remove -rotate 270 /home/pi/lcd_screen/work.bmp 
	else
		convert "/opt/retropie/configs/$system/launching.png" -resize '480x800' -type TrueColor -alpha Remove -rotate 180 /home/pi/lcd_screen/work.bmp 	
	fi	
  else
  	if test -f "$file_png"; 
	then  
		convert "$file_png" -resize '480x800' -type TrueColor -alpha Remove -rotate 180 /home/pi/lcd_screen/work.bmp
	elif test -f "$file_jpg"; 
	then 
		convert "$file_jpg" -resize '480x800' -type TrueColor -alpha Remove -rotate 180 /home/pi/lcd_screen/work.bmp 
	else
		convert "/opt/retropie/configs/$system/launching.png" -resize '480x800' -type TrueColor -alpha Remove -rotate 180 /home/pi/lcd_screen/work.bmp 	
	fi	
  fi
  #launch my lcd project
  sudo /home/pi/lcd_screen/lcd /home/pi/lcd_screen/work.bmp 400000 &
 fi

runcommand-onend.sh
#!/bin/sh

_DPI=$(sudo grep dpi /boot/config.txt) || true
if ! [ "$_DPI" ]; 
then
  sudo killall lcd
  sudo /home/pi/lcd_screen/lcd /home/pi/lcd_screen/retropie.bmp 50000 &
#  cp /home/pi/lcd_screen/retropie.bmp /home/pi/lcd_screen/work.bmp
fi


В стартовом скрипте сначала проверяется наличие DPI соединения, затем ищется изображение с форматами jpeg или png, поворачивается на нужный угол, изменяется его размер и конвертится в формат bmp, потом запускается моя программа с необходимыми параметрами.

Охлаждение


Raspberry Pi 3B мощнее, чем Zero, и ему необходимо хорошее охлаждение. Особенно в таком закрытом корпусе. С небольшим радиатором, видимым на фото, через полчаса игры на любом эмуляторе температура ядра достигала 80 градусов и начинался тротлинг.


Был заказан центробежный вентилятор 40*30*10 мм, но пока он не приехал, я просто попробовал выпилить из 3 мм алюминия небольшую пластину, размером 80*30 мм. К тому времени все пины GPIO я выпаял, поэтому они уже не мешались установке пластины. Дополнительно еще выпилил кусочек 20*10мм для контакта с чипом и сделал из пластин бутерброд. Это превзошло все мои ожидания, максимум 65 градусов на ядре.


Так это выглядит внутри.



В итоге что


Как это играется? Отлично! С криками, битьем геймпадов, взаимными упреками. Прямо как когда-то.

Никаких задержек и лагов замечено не было.

Экранчик, конечно, маловат, всего три с половиной дюйма. Разбаловали нас смартфоны с пятью дюймами, но играть можно. Текст читается, даже мелкий. Предпочитаю HDMI.

Можно прийти к другу, поиграть, портативность позволяет. Сопутствующие аксессуары: шлейфы, кабели, джойстики, зарядка занимают больше места, чем сама приставка.

Звук


К сожалению, штатный звук Raspberry Pi оставляет желать лучшего. В качестве звуковой системы применена ШИМ модуляция с пассивной фильтрацией. Поэтому был закуплен USB ЦАП на PCM2704. Главным образом, из-за своих размеров.

Очень не хватает вывода звука при игре на маленьком LCD экранчике, поэтому куплен также микроусилитель 3W на NS8002 и подобран динамик от планшета на 8 Ом.

USB ЦАП и NS8002



Всё это поместится под LCD экраном. Приедет, буду ставить.

Подпиливаем Retropie


Сборка окончена. Далее приведу некоторые менее интересные для развлекательного чтения особенности настроек Retropie.

Шейдеры
Большинство приставок, которые способен эмулировать Raspberry Pi, выдает разрешение 320x240 пикселей. По сегодняшним меркам это очень мало. Все игры приобретают ярко выраженную пикселизацию на современных экранах.


Создатели Retroarch, который входит в состав Retropie, озаботились этим вопросом и добавили поддержку шейдеров. Например, мне очень нравится предустановленный xbr-lv1-noblend.glslp. Он, буквально, вытягивает детали из картинки. См. ниже.

Скриншот из игры Castlevania: Symphony of the Night, советую в нее сыграть

Чтобы установить шейдер, зайдите в меню Retropie -> Configuration editor. Выберите Configure basic libretro emulator options, после выберите Configure default options for all libretro emulators. Затем установите Video Shader Enable в «true» и Video Shader File на желаемый шейдер. Установите также Video Smoth в «false», поскольку он только смазывает картинку. Можно даже выбрать что-то типа scanline шейдера, который эмитирует работу CRT телевизоров. Но это на любителя.


Отрисовка шейдеров довольно затратная операция. При разрешении равном и выше 720p на RPi 3B появляются довольно заметные подлагивания. Поэтому в этом же меню выберите Render resolution «800x600». Не волнуйтесь, если вы до этого делали изменения в файле /boot/config.txt, как в этой статье чуть выше, то разрешение экрана по кабелю HDMI будет 1080p, но эмулятор будет рендерить картинку в 800x600 и растягивать на весь экран. И делает он это не плохо. По крайне мере, с шейдером лучше, чем без него, да и рендер в 1080p бесполезен, поскольку видеоданные игры всё равно нарисованы для разрешения 320x240.

Настройка джойстиков
Если у вас есть любой xinput геймпад (стандарт Micro$oft), он подойдет. Настраивается он при первом же запуске Retropie, либо через кнопку старт — Configure Input. Но и тут есть головная боль. Ушлые заокеанские ребята, чтобы не попадать на патентные судебные иски поменяли местами кнопки с A и B, X и Y.


Если у вас геймпад от xbox или аналогичный, то настраивать надо с перевернутыми кнопками, как на NES. Затем зайти в Retropie -> Retropie Setup, выбрать Configuration / tools, затем emulationstation, поставить в Swap A/B Buttons in ES «Swapped». Потом заново переконфигурируйте джойстик.

Также, мне не нравится, как разработчики Retropie распорядились кнопочным фондом. Где комбинация кнопок сделать скриншот? Зачем мне кнопка reset, если я на нее попадаю каждый раз? Где кнопки перемотки и ускорения времени?

Чтобы это исправить, нужно подредактировать файл с названием вашего джойстика в /opt/retropie/configs/all/retroarch-joypads/. Также он доступен по сети \\192.168.x.xxx\configs\all\retroarch-joypads\. После каждого обновления Retropie через апдейтер, нумерация привязки слетает, поэтому я не буду выкладывать весь конфиг. Напишу, что изменил лишь эти кнопки. Они выполняются при нажатии одновременно с Hotkey-м, обычно это кнопка с логотипом, посередине джойстика.

Button config
input_screenshot_btn = "0"
input_rewind_btn = "h0left"
input_hold_fast_forward_btn = "h0right"
input_state_slot_increase_btn = "h0up"
input_state_slot_decrease_btn = "h0down"


Здесь кнопка скриншотов назначена вместо reset. Fast forvard и rewind (перемотка назад) настроены на кнопки крестовины: вперед и назад. Кнопки крестовины вверх и вниз изменяют слот сохранения.

Rewind надо включить в конфиге /opt/retropie/configs/all/retroarch.cfg. Только не включайте Rewind на эмуляторе psx, всё начинает дико тормозить. Перемотка назад, по-умолчанию, отключена в конфиге psx.

Еще хочется добавить про Bluetooth. Сам я беспроводные джойстики не люблю и играю всегда с кабелем. Старая школа еще. Для любителей беспроводных джойстиков надо приобрести Bluetooth донгл и вставить в USB порт. Так будет лучше и никаких проблем не будет. Сам я не проверял, но знающие люди пишут.

Xboxdrv


Но и это еще не всё. В Retropie Setup в менеджере пакетов можно поставить различные порты, например Openttd, Doom или вообще DOS эмулятор Dosbox. Но управлять в них джойстиком не получится. Только клавиатурой и мышкой. Чтобы это исправить, нужно поставить в менеджере пакетов драйвер xboxdrv, который умеет эмулировать нажатия клавиатуры. Просто поставить из исходников, автозапуск делать не нужно, он не подходит под все джойстики.

Скрипт запуска напишем сами под наш геймпад. Добавим в /opt/retropie/configs/all/runcommand-onstart.sh следующие строки в конец файла.

runcommand-onstart.sh
# xboxdrv
sudo killall > /dev/null 2>&1 xboxdrv
if [ "$1" = "pc" ] || [ "$1" = "openttd" ] || [ "$1" = "doom" ] || [ "$1" = "kodi" ];
then
sudo /opt/retropie/supplementary/xboxdrv/bin/xboxdrv > /dev/shm/runcommand.log 2>&1     --evdev /dev/input/event0 	--silent 	--detach-kernel-driver 	--force-feedback 	--mimic-xpad 	--trigger-as-button 	--evdev-no-grab 	--evdev-absmap ABS_X=x1,ABS_Y=y1,ABS_RX=x2,ABS_RY=y2,ABS_HAT0X=dpad_x,ABS_HAT0Y=dpad_y 	--evdev-keymap KEY_#304=a,KEY_#305=b,KEY_#307=x,KEY_#308=y,KEY_#312=lt,KEY_#313=rt,KEY_#310=lb,KEY_#311=rb,KEY_#317=tl,KEY_#318=tr,KEY_#314=back,KEY_#315=start,KEY_#316=guide 	--axismap -Y1^deadzone:3000=Y1,-Y2^deadzone:3000=Y2 	--ui-axismap x2^deadzone:3000=KEY_LEFT:KEY_RIGHT,y2=KEY_UP:KEY_DOWN 	--ui-axismap x1^deadzone:3000=REL_X:06,y1=REL_Y:06 	--ui-axismap dpad_x^deadzone:0=KEY_LEFT:KEY_RIGHT,dpad_y^deadzone:0=KEY_UP:KEY_DOWN 	--ui-buttonmap rt=BTN_LEFT,lt=BTN_RIGHT,start=KEY_ENTER,back=KEY_ESC,tl=KEY_4,tr=KEY_5,lb=KEY_2,rb=KEY_3,a=KEY_SPACE,b=KEY_M,x=KEY_3,y=KEY_LEFTCTRL,guide=KEY_Y &
fi


Здесь $1 — наша целевая система, при которой запускается xboxdrv. Можно добавлять по аналогии. Строки --evdev-absmap --evdev-keymap отвечают за привязку осей и кнопок к вашему джойстику. Скрипт выше дан для контроллера Xbox One S. Чтобы узнать какая ось и кнопка соответствуют номеру или названию вашего джойстика, нужно в консоли набрать команду evtest. Заодно можно посмотреть какой event соответствует нашему джойстику. У меня это /dev/input/event0 который стоит в параметре --evdev.

Параметры --ui-axismap эмулируют оси мыши, а --ui-buttonmap кнопки клавиатуры. В данном примере курки — это кнопки мыши, левый стик — мышь, правый стик — курсорные клавиши. Старт, селект — enter и esc, соответственно. На остальные кнопки привязаны цифры клавиатуры. Задействовать крестовину под кнопки клавиатуры, к сожалению, не удалось. Почему-то на джойстике Xbox One S отказывается работать.

С таким конфигом отлично играются стратегии, наподобие Theme Hospital, OpenTTD и пр. Чуть хуже играется в шутеры, но вы можете создать аналогичные скрипты запуска под шутеры в этом же файле, меняя там параметр $1.

Чтобы обеспечить нормальную работу джойстика в emulationstation, нужно завершить xboxdrv после выхода из игры. Для этого в конец файла /opt/retropie/configs/all/runcommand-onend.sh добавляем следующее.

runcommand-onend.sh
sudo killall > /dev/null 2>&1 xboxdrv



Если при обновлении Retropie что-то пошло не так
Однажды, я запустил обновление Retropie и при распаковке ядра отвалился встроенный Wi-Fi. Устройство загружалось, но не реагировало ни на какие кнопки, в том числе на подключенной клавиатуре. SSH нет, так как нет Wi-Fi, на клавиатуру не реагирует, а SD карточку не достать…

Заметил, что клавиатура работает при загрузке. Понажимал много раз ctrl+c и, о, консоль! Но как теперь восстановить Wi-Fi? Интерфейс wlan0 отсутствовал от слова совсем. Запуск апдейтера не помог, поскольку он подтягивает файлы с интернета.

Первым делом в /opt/retropie/configs/all/autostart.sh закомментировал запуск emulationstation и перезагрузился. После перезапуска загружается консоль, клавиатура работает. Дальнейшее гугление, как вернуть wlan дало вот это. Команда переконфигурирует все установленные пакеты. Помогло.

sudo dpkg --configure -a


Чтобы Retropie выглядел не как Linux станция
При загрузке приставки мне не хочется смотреть на лог загрузки Linux. Он портит весь дух приставок девяностых.

Чтобы убрать лог загрузки Linux
Чтобы убрать лог загрузки Linux, нужно привести файл /boot/cmdline.txt к такому виду. Всё в одну строку.

cmdline.txt
dwc_otg.lpm_enable=0 console=tty3 root=PARTUUID=f2d3cb4f-02 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait loglevel=3 consoleblank=0 plymouth.enable=0 quiet vt.global_cursor_default=0 plymouth.enable=0 plymouth.ignore-serial-consoles splash


Кратко, конфиг убирает лого, выводит сообщения в другую консоль, убирает мигающий курсор.
Затем убираем весь текст в motd.

nano /etc/motd

Убираем автологин текст.

sudo nano /etc/systemd/system/autologin@.service

Нужно в этом файле изменить строку

ExecStart=-/sbin/agetty --autologin pi --noclear %I $TERM

На

ExecStart=-/sbin/agetty --skip-login --noclear --noissue --login-options "-f pi" %I $TERM

Более подробно про это написано здесь.

Также, при загрузке системы можно вывести свое фото — splashscreen. Можно и несколько, они будут сменять друг друга со временем. Нужно положить свои картинки в папку в сети \\192.168.x.xxx\splashscreens, и выбрать их в меню Retropie -> SPLASH SCREEN -> Append Splashscreen to list.

Мне, например, безумно нравится вот эта картинка.


Также в Retropie установлен runcommand, который при запуске игры выводит консольный обрезок с предложением нажатия клавиши. Этот экран можно заменить на милые картиночки игровых приставок.


Подробная инструкция, как это сделать, написана здесь.

Что еще можно сделать


Улучшать можно бесконечно. Приведу еще пару ссылок, что можно сделать. Пишите еще интересные штуки, которые можно добавить. Спасибо за внимание.


Он того стоит.

Комментарии (18)


  1. Sdima1357
    13.09.2018 20:37

    Мне кажется, что второй экран явно лишний за его цену. Лучше уже взять HDMI 7 дюймов IPS практически за те же деньги и запускать от батарейки. Или вообще его не ставить и пользоваться только телевизором. И есть недорогие экраны c SPI интерфейсом и экранным буфером, правда RetroPie умеет только на HDMI или TVout. Впрочем есть программа 'FBCP' копирующая из GPU на SPI framebuffer около 20 FPS


    1. 8street Автор
      13.09.2018 20:56

      Можно было и не ставить. Цена на экран да, почти сравнима со стоимостью самого RPi. И доллар, к сожалению, не в нашу пользу.
      Можно было и 7 дюймов взять, можно и матрицу от ноута с переходниками, но форм-фактор был бы уже не тот.

      SPI экраны изначально не рассматривал. При 20 fps играть в платформеры это такое.

      А телевизор и HDMI не везде есть. Например, приставку можно в поезд взять, нужен еще джойстик и, в качестве БП, зарядка от телефона.


      1. Sdima1357
        13.09.2018 21:30

        А телевизор и HDMI не везде есть.

        Это правильно, только на вашем DPI экране игры то и не работают,RetroPie под GPU заточен. А под форм фактор есть что то вроде вот этого www.ebay.com/itm/5-inch-HDMI-Touch-Screen-TFT-LCD-Panel-Module-Shield-800X480-For-Raspberry-Pi/311872272215?epid=2282981307&hash=item489d093757:g:S7cAAOSw2ENW63YF


        1. 8street Автор
          14.09.2018 06:53

          Нет, экран работает в играх. Вся возня с кодом в статье — попытка запустить вывод на маленький экран, совместно с hdmi.


  1. tormozedison
    13.09.2018 22:13

    Круто! Dosbox — вообще первое, что я бы туда прикрутил.


    1. Singrana
      13.09.2018 22:43

      Хм, dosbox и xcom…


  1. immaculate
    14.09.2018 05:11

    Проблему табов и переносов во всех современных редакторах решает EditorConfig.


    Очень удобно — забрасываешь его в корень проекта, и вся команда начинает форматировать код более или менее единообразно.


    Хотя в вашем случае, конечно, можно и поискать настройки в VS Code.


  1. Antern
    14.09.2018 09:01

    Как-то загорелся ретро-тематикой: собрал на Raspberry Pi 2 бартоп, но финальный билд разочаровал. Всё же не хватает производительности малинки для полноценной станции эмуляции.
    1) На большинство аркадных игр не хватает производительности.
    2) На консольных играх задержка ввода в 8 кадров ( с напильником ~5 ).
    Для сувенирного устройства ( как в статье ) или пошаговых игр подходит идеально, но в том же супер-марио — уже серьёзные проблемы. Так же отпадают игры со световым пистолетом, даже при наличии crt-дисплея.

    Своего рода дисклеймер: для компактного стационарного эмулятора лучше всё-таки использовать старый x86-ноутбук, mac mini или пк в форм-факторе mini-itx. На них — linux с emulationstation без иксов. Это так же даёт возможность включить frame_advance: добиться меньшего времени отклика, чем на оригинальном железе, за счёт спекулятивного ввода.


    1. 8street Автор
      14.09.2018 09:05
      +1

      На RPi 3 производительности хватает. Я в статье под спойлером Шейдеры написал как лучше настроить, и какое разрешение поставить. Также, не нужно использовать встроенный блютуз, если играете с беспроводными джойстиками. Используйте внешний USB донгл блютуз.


      1. Antern
        16.09.2018 00:18

        Не хватает производительности. Можно винить слабое железо или странную реализацию, по вот по ссылке графики: github.com/RetroPie/RetroPie-Setup/wiki/Input-Lag.
        Чистая задержка эмуляции в 5-9 фреймов — это без учёта контроллеров и дисплея: блютуз в данном конкретном месте не при чём.

        Еще по повду производительности: наверное, просто так для большинства аркадных игр используют старенький lr-mame-2003 с менее точной эмуляцией, а не из-за проблем с перформансом малинки?


        1. 8street Автор
          16.09.2018 08:10

          Вы путаете понятие input lag и производительность. В любом случае, задержка если и есть, то на мой взгляд незаметная. Input lag в этом случае зависит только от лага HDMI монитора/ТВ. Мне действительно попадался телек, играть с которого невозможно. Но в большинстве случаев все нормально. А так, у ПК, кажись, тоже в 3d режиме 3 кадра пререндерятся.


          1. Antern
            16.09.2018 09:32

            Это Вы что-то путаете. Input lag — это время между нажатием кнопки на контроллере и реакцией на дисплее. ТВ добавляет в эту цепочку 10-40мсек, столько же можно ожидать от беспроводного котроллера, но даже исключив данные показатели — малинка сама_по_себе даёт задержку в 5+ кадров ( ~90мсек ) даже на простых эмуляторах ( NES ). Вы счастливый человек, если её не замечаете и она Вам не мешает, но с моей стороны пруфы и тесты, с Вашей — «на мой взгляд незаметная».

            Про ПК с его трея кадрами… В KMS под линуксом на стареньком железе мне удалось добиться двух кадров задержки довольно быстро (без учета дисплея/геймпада). Поусердствовав еще немного с настройками и драйвером можно до одного снизить ( как минимум, за счёт очень прожорливого спекулятивного рендеринга на ввод )


            1. 8street Автор
              16.09.2018 13:00

              А вы чувствуете эти 5 кадров?

              По ПК гугление выдало вот это displaylag.com/reduce-input-lag-in-pc-games-the-definitive-guide В среднем такие же цифры, если использовать V-Sync и я бы не назвал, что это проблема всемирного масштаба. Большинство, в том числе и я, об этом не задумывается и не видит никакой задержки. За исключением, если монитор настолько плох, что создает задержку 10-20 и более кадров.

              Кроме того, у самого человека время реакции на внешний раздражитель около 0,2 сек (у самых натренерованных 0,16 с). В это время также входит задержка восприятия, пока сигнал от зрительного нерва дойдет до мозга и там обработается. Она составляет около 0,1 сек или чуть меньше. За 0,1 сек при 60fps пройдет 6 кадров. Поэтому такая задержка особо не ощущается.


  1. vyacheslavteplyakov
    14.09.2018 15:48

    Ретропай хорош когда надо вывернуть что-то экзотическое, сэмулировать редкие игровые автоматы к примеру. А 99% людей отлично подойдёт recalbox, который из коробки не показывает консоль при загрузке, вместо неё реанимированная заставка, имеет приятный интерфейс из коробки и прикольное музыкальное сопровождение в фоне. Он без проблем запустит всё то, что обычно запускают люди когда хотят ретро игр, все сеги, денди и нинтенды на нем отлично работают.
    По поводу досбокса. С ним есть фундаментальный косяк, он запускается как бы в своем окружении и он не знает что такое джойстики. Поиграть тем же геймпадом сконфигурированном в ретропае, в дум не получится, нужно цеплять клавиатуру. Да можно там как-то настроить эмуляцию геймпадов которые цеплялись в геймпорт, но это задача не тривиальная.


    1. 8street Автор
      14.09.2018 16:13

      Почитайте статью, загляните под спойлеры. В статье написано как использовать xboxdrv для эмуляции клавиатуры и мыши джойстиком, в том числе и в dosbox. А также написано почему retropie и как убрать консоль.


      1. vyacheslavteplyakov
        14.09.2018 16:24

        Прочитал и поэтому дал совет для тех кому ехать, а не шашечки.
        Зачем убирать консоль, когда есть версия эмулятора, где все это сделано за тебя, красиво и сразу из коробки.


        1. 8street Автор
          14.09.2018 16:40

          Не спорю, что многое там сразу из коробки, но для меня существенным недостатком стало отсутствие вибрации геймпадов.


          1. vyacheslavteplyakov
            14.09.2018 16:55

            Я покдлючал от Х360 и вроде как все вибрирует. Я там другую засаду нашел. Он не умет на zero w подключать джойстики от PS3 черед встроенный Bluetooth, а ретропай без проблем, хотя это немного и ломает нормальную работу BT. Иных недостатков не нашел. Сейчас делаю настольный мини автомат, будет сделан на нём.