Temco Pong

De wikijoan
(dif) ← Versió més antiga | Versió actual (dif) | Versió més nova → (dif)
Salta a la navegació Salta a la cerca

Introducció

Es tracta de fer un moble de fusta (tauleta d'estar) per jugar al pong, amb dos mandos rotatoris i un botó, amb un look retro. El joc també es pot integrar a les màquines bartop, posant dos mandos rotatoris.

Aquesta versió del Pong està basat en la versió de Temco a què jugava amb els meus germans quan era petit, i que encara conservem. Es tracta d'emular totes les funcions i sons d'aquest joc, intentant que l'emulador s'assembli al màxim al joc real.

El joc ha de constar d'un front-end (temco_pong_frontend) i del joc en si (temco_pong). El frontend llença el joc amb les diferents opcions escollides en el frontend.

Per jugar només ha de caldre un botó, a part dels dos mandos rotatoris.

Projectes de pong amb C++ i SDL

He trobat dos projectes desenvolupats amb C++ i SDL:

Projecte 1 (SDL2)

Descarrego pong-master.zip

$ cd pong-master
$ make
...
g++ -MM src/main.cpp src/pong.cpp src/ball.cpp src/paddle.cpp src/utilities.cpp -Wall -c -std=c++0x -g `sdl2-config --cflags` > ./.depend;
/bin/sh: 1: sdl2-config: not found
...

sdl2-config  - script to get information about the installed version of
       SDL

Jo tinc

$ sdl-config --version
1.2.14

que és la versiói disponible des dels repositoris. Jo tinc instal.lada la versió 1.2 de SDL, i aquest programa treballa amb la versió SDL 2.

Llegeixo en el projecte:

Ubuntu
You will need to download and install:
    SDL 2.0
    SDL Image 2.0
    SDL Mixer 2.0
    SDL TTF 2.0

Per instal.lar la versió 2 de SDL:

$ hg clone http://hg.libsdl.org/SDL
cd SDL
./configure
make
sudo make install

$ sdl2-config --version
2.0.4

Primer de tot miraré de compilar amb la versió 1.2 de SDL.

Em falta la llibreria SDL-mixer:

$ sudo apt-get install libsdl-mixer1.2-dev

g++ -MM src/main.cpp src/pong.cpp src/ball.cpp src/paddle.cpp src/utilities.cpp -Wall -c -std=c++0x -g `sdl-config --cflags`

No es pot compilar contra 1.2. Hi ha massa canvis de la versió 1.2 a 2 de SDL (https://wiki.libsdl.org/MigrationGuide).

Per tant, he de compilar contra 2.0, i el primer que em falta és SDL Mixer 2.0.

$ cd ~/SDL2_mixer-2.0.0
$ ./configure
$ make
$ sudo make install

Finalment compilo contra SDL2.0 excepte SDL_TTF i SDL_image.

I això implica que a pong.hpp:

#include <SDL/SDL_ttf.h>

i a utilities.hpp:

#include <SDL/SDL_ttf.h>
#include <SDL/SDL_image.h>

i un petit canvi en el Makefile. Ara sí que compila:

$ make
rm -f ./.depend
g++ -MM src/main.cpp src/pong.cpp src/ball.cpp src/paddle.cpp src/utilities.cpp -Wall -c -std=c++0x -g `sdl2-config --cflags` > ./.depend;
g++ -c src/main.cpp -o main.o -Wall -c -std=c++0x -g `sdl2-config --cflags`
g++ -c src/pong.cpp -o pong.o -Wall -c -std=c++0x -g `sdl2-config --cflags`
g++ -c src/ball.cpp -o ball.o -Wall -c -std=c++0x -g `sdl2-config --cflags`
g++ -c src/paddle.cpp -o paddle.o -Wall -c -std=c++0x -g `sdl2-config --cflags`
g++ -c src/utilities.cpp -o utilities.o -Wall -c -std=c++0x -g `sdl2-config --cflags`
g++ main.o pong.o ball.o paddle.o utilities.o -o pong -Wall -g -lSDL_ttf -lSDL2_mixer `sdl2-config --libs`

Dóna un problema amb les fonts, en la línia 74 de pong.cpp

font_image_launch = renderText("Press SPACE to start", font_name, font_color, 18, renderer);

deu ser que he d'instal.lar realment SDL2_TTF

$ cd ~/SDL2_ttf-2.0.12
$ ./configure
$ make
$ sudo make install

i finalment ara sí que compilar i funciona:

$ ./pong

Per fer fullscreen, F11. La pala es controla amb el mouse. Només és un jugador (contra la màquina).

Projecte 2

No està basat en la versió 2 de SDL, sinó en la versió que ja disposava.

$ cd SDL-Pong-master/

$ make
g++ -c keyboard.cpp -O3 -Wall -pedantic -Werror
g++ -c paddle.cpp -O3 -Wall -pedantic -Werror
g++ -c ball.cpp -O3 -Wall -pedantic -Werror
g++ main.cpp keyboard.o paddle.o ball.o -lSDL -lSDL_ttf  -O3 -Wall -pedantic -Werror -o pong

Compila bé i implementa dos jugadors. Es juga amb les fletxes dalt i baix; i w i s. Sembla un codi prou fàcil per estudiar i adaptar.


creat per Joan Quintana Compte, desembre 2014

Projecte 3 (basat en C)

És més senzill que els altres dos, però hi ha unes explicacions que estan bé.

$ gcc pong.c -lSDL -lSDL_ttf -O3 -o pong
$ ./pong

Altres projectes

Altres projectes dels quals extreure idees o trossos de codi:

temco_pong_frontend

A part dels dos mandos rotatoris, la idea només és necessitar un botó. Les diferents opcions del frontend serien:

  • Part esquerre: escollir el joc
    • tenis
    • hockey
  • squash
    • Practice
  • Part dreta: (funciona com a toggle)
    • Pad size (short/large)
    • Speed (fast/slow)
    • Angle (20º / 20º-40º)
    • Volume (aquesta opció no hi era en el temco). La idea és controlar el volum per software (amb alsamixer)
    • Auto/Manual. En principi aquesta opció que hi havia al Temco no s'implementarà, doncs no la faig servir. La pilota arrenca automàticament.

Amb el mando rotatori s'ha d'anar recorrent les opcions, i amb el botó se selecciona l'opció i queda emmarcada.

Partida (temco_pong): Tinc dos mandos. Per començar la partida clico en el botó. Quan s'acaba la partida, perquè s'ha arribat a 15, s'ha de tornar a clicar en el botó. Sempre que es vol començar la partida s'ha de clicar en el botó. Doble click: per tornar al front-end.

Com que hi ha un sol botó, per apagar la màquina serà un doble click en el botó. (Tres clics seria per anar a la consola (service mode)).

temco_pong

(TBD)

Instal.lació a la RPi A+

Parteixo del model A+ (256MB, sense port ethernet) i d'una targeta amb Raspbian (set'14).

  • overclock Medium (900 MHz)

teclat català:

Com que no disposa de port Ethernet, el primer de tot és configurar el USB wireless.

Com que encara no he instal.lat el joe i no ho puc fer perquè de moment no tinc connexió a Internet, l'editor que faig servir és el nano, que sí que ve instal.lat per defecte amb la Raspbian.

Trobar la IP:

$ nmap -sP 192.168.1.1-255

Problema: No sé què passa amb el meu USB HUB que el teclat usb no el puc fer servir. El teclat l'he de fer servir sense el USB HUB. Això vol dir que no puc utilitzar el mateix temps el teclat usb i el wireless usb. El wireless USB sí que va bé amb el USB HUB (de fet s'ha de fer servir obligatòriament amb el USB HUB). Bé, la qüestió és que ja tinc xarxa i ja em puc connectar via SSH:

$ ssh pi@192.168.1.35

$ sudo apt-get update
$ sudo apt-get install joe
$ sudo apt-get install libsdl-image1.2-dev libsdl-ttf2.0-0 libsdl-ttf2.0-dev libsdl-mixer1.2-dev

Configuració del so (ALSA)

$ sudo joe /etc/modules
snd-bcm2835

$ joe /home/pi/.bashrc
amixer cset numid=3 1

Instal.lació de pikeyd per tal de controlar els botons des dels pins GPIO:

$ sudo modprobe uinput

Fer-ho permanent fent:

$ sudo joe /etc/modules
uinput
i2c-dev
sudo joe /etc/modprobe.d/raspi-blacklist.conf
#blacklist i2c-bcm2708
$ wget https://github.com/mmoller2k/pikeyd/archive/master.zip
$ unzip master.zip
$ cd pikeyd-master
$ make

Fico la configuració correcta del fitxer de configuració. En el projecte TEMCO només utilitzaré un botó (el UP que es correspons al GPIO=4)

$ joe pikeyd.conf

$ sudo cp pikeyd.conf /etc

$ sudo ./pikeyd -d (necessari executar-lo com a sudo)
pikeyd: Daemon starting

Millor copiar-ho a /usr/local/bin

Then you need to run it as root, with perhaps the -d switch to make it ran as a daemon. Adding the line '/usr/local/bin/pikeyd -d' to /etc/rc.local makes it run as root at boot time.

Per tant:

$ sudo cp pikeyd /usr/local/bin
$ sudo joe /etc/rc.local

afegim al final: (abans de exit 0)

/usr/local/bin/pikeyd -d

Instal.lació i configuració per poder utilitzar el ADC MCP3008

$ sudo joe /etc/modprobe.d/raspi-blacklist.conf
#blacklist spi-bcm2708
$ sudo reboot
ls /dev/spidev*
/dev/spidev0.0  /dev/spidev0.1

provo que funcioni el projecte original d'on he tret el codi:

$ g++ -Wall -o OutBin  mcp3008Spi.cpp mcp3008SpiTest.cpp
$ sudo ./OutBin 
The Result is: 1
...
The Result is: 48
The Result is: 312
The Result is: 433

Instal.lació del projecte temco_pong:

$ mkdir temco_pong

Des del portàtil:

$ cd temco_pong
$ scp -r * pi@192.168.1.35:/home/pi/temco_pong

todo:

  • que arrenqui el frontend en l'inici (/home/pi/.profile). A la RPi s'ha d'executar com a sudo per tal de poder llegir el ADC MCP3008.
  • eliminar els serveis que no necessito, com ara vs_ftp

Construcció del moble

Televisor CRT

Original scart.png
Arcade2tv17.gif

s'ha de comentar:

#hdmi_force_hotplug=1
#hdmi_drive=2

hdmi_drive chooses between HDMI and DVI modes. El seu significat és:

  • hdmi_drive=1 Normal DVI mode (No sound)
  • hdmi_drive=2 Normal HDMI mode (Sound will be sent if supported and enabled)

add the following into the file (choose from your own standard, PAL/NTSC/etc):

sdtv_mode defines the TV standard for composite output (default=0)
sdtv_mode=0    Normal NTSC
sdtv_mode=1    Japanese version of NTSC – no pedestal
sdtv_mode=2    Normal PAL
sdtv_mode=3    Brazilian version of PAL – 525/60 rather than 625/50, different subcarrier
sdtv_aspect defines the aspect ratio for composite output (default=1)
sdtv_aspect=1  4:3
sdtv_aspect=2  14:9
sdtv_aspect=3  16:9

El més senzill per convertir el senyal DVI a SCART és tenir el següent aparell:

De fet, el senyal no s'ha de convertir, és passiu. Senzillament s'ha de tenir en compte com són els pins del connector SCART

Per tant, en el meu cas hauria de tenir una cosa similar a:

#hdmi_force_hotplug=1
#hdmi_drive=2
sdtv_mode=2
sdtv_aspect=1 

Per fer les connexions del senyal AV al SCART tinc en compte:

Però compte que no és el mateix cas. En aquest article es vol connectar una placa JAMMA a una tele analògica, i la placa JAMMA fa servir el sistema RGB (un pin per cada senyal). En el meu cas, la sortida RPi que m'imteressa és AV, i per tant deixaré sense connexió el pin16, doncs el mode AV és el mode per defecte.

D'altra banda el pin8 l'he de connectar a 12V, i d'aquesta manera la tele commuta directament al mode AV i així no cal utilitzar el mando (que no s'hauria d'utilitzar).

Si vull utiltizar els altaveus i l'amplificador de so de la tele, hauré d'utilitzar els pins 2, 6 i 4. El terra hauria de quedar com el de la imatge, amb varis pins connectats.

SOLUCIÓ. La prova la faig amb una font d'alimentació dual que em dona 12V, 5V i GND. La RPi (model B, connector RCA) l'alimento amb la font d'alimentació (5V i GND). Els 12 V els necessitaré per connectar el pin8 (que força el commutament a mode AV, i així no necessito el mando).

video:

  • pin 20: compsite video input/sync input. (connector RCA)
  • pin 18: compsite video input ground. (connector RCA)
  • pin 21: common ground (la massa de terra a la carcassa). (font d'alimentació, GND)
  • massa (cable pelat). També va connectat al pin 21. Això és important per eliminar soroll fregit del senyal de video. (font d'alimentació, GND)
  • pin 8: 12 V: (font d'alimentació, 12V)

audio (TBD):

  • pins 2 i 6: audio input left i right (connector jack 3.5)
  • pin 4: audio ground (connector jack 3.5)

Model B+ (connector jack 3.5 de 4 pols) (TBD).

La idea és la mateixa. El connector RCA i jack de 3.5 de pols que trobem en el model B es converteixen en jack de 3.5 i 4 pols en el model B+. L'ordre dels pols és (http://www.raspberrypi-spy.co.uk/wp-content/uploads/2014/07/Model-B-Plus-Audio-Video-Jack-Diagram.png):

  • pol 4 (interior, tocant el cable): video
  • pol 3: massa
  • pol 2: right
  • pol 1 (exterior): left

Spinner per jugar al pong (potenciòmetre)

La primera idea és amb un potenciòmetre poder llegir el valor analògic de la resistència i llegir el valor a la RPi (tot i que no té pins analògics). Realment el temco tenia potenciòmetres. Però hi ha altres possibilitats a considerar, com es comenta a:

Ara bé, aquestes dues possibilitats que es comenten una és cara, i l'altra fa servir la roda del ratolí, que va bé doncs l'emulador MAME va bé per configurar una de les entrades com a rodeta del ratolí (és com s'ha de configurar per exemple el Arkanoid). Com que en aquest projecte vull fer un software des de 0 que no depèn d'emuladors com el MAME, la solució d'un potenciòmetre i conversor ADC és vàlida.

D'entrada disposo del ADC0804LCN, però compte que no és recomanadble doncs funciona a 5V i els pins GPIO van a 3,5V, i no és recomanable ficar-lis 5V. Per tant, és més recomanable el ADC MCP3008 o MCP3004 (8 entrades i 4 entrades analògiques), que sí que funcionen a 3,5V en el seu marge.

Està explicat en varis llocs de com connectar el ADC MCP3008 a la RPi, per ex:

Ara bé, en aquest exemple anterior hi ha un script amb Python, i jo ho he d'adaptar a llenguatge C/C++, doncs en aquest llenguatge serà la meva aplicació.

Amb llenguatge C:

Segueixo el següent enllaç i codi:

L'exemple funciona correctament. Recordar que s'ha d'habilitar a la RPi, i que s'ha d'executar com l'aplicatiu d'exemple com a sudo:

$ sudo joe /etc/modprobe.d/raspi-blacklist.conf
#blacklist spi-bcm2708

reboot

ls /dev/spidev*
/dev/spidev0.0  /dev/spidev0.1
$ g++ -Wall -o OutBin  mcp3008Spi.cpp mcp3008SpiTest.cpp
$ sudo ./OutBin 
The Result is: 1
...
The Result is: 48
The Result is: 312
The Result is: 433
The Result is: 578
The Result is: 750
The Result is: 876
The Result is: 1022

Faig una petita modificació al codi per incloure dos potenciòmetres: mcp3008SpiTest_v2.cpp: (El xip mcp3008 disposa de 8 canals. Amb dos potenciòmetres faig servir dos dels canals.). D'altra banda, la RPi disposa de dos dispositius, que vol dir que podria connectar 2 xips, i per tant el número màxim de senyals analògiques que podria llegir seria de 16.

/***********************************************************************
 * mcp3008SpiTest_v2.cpp. Sample program that tests the mcp3008Spi class.
 * an mcp3008Spi class object (a2d) is created. the a2d object is instantiated
 * using the overloaded constructor. which opens the spidev0.0 device with
 * SPI_MODE_0 (MODE 0) (defined in linux/spi/spidev.h), speed = 1MHz &
 * bitsPerWord=8.
 *
 * call the spiWriteRead function on the a2d object 20 times. Each time make sure
 * that conversion is configured for single ended conversion on CH0
 * i.e. transmit ->  byte1 = 0b00000001 (start bit)
 *                   byte2 = 0b1000000  (SGL/DIF = 1, D2=D1=D0=0)
 *                   byte3 = 0b00000000  (Don't care)
 *      receive  ->  byte1 = junk
 *                   byte2 = junk + b8 + b9
 *                   byte3 = b7 - b0
 *    
 * after conversion must merge data[1] and data[2] to get final result
 *
 *
 *
 * *********************************************************************/
#include "mcp3008Spi.h"
 
using namespace std;
 
int main(void)
{
    int i = 20;

    mcp3008Spi a2d_0("/dev/spidev0.0", SPI_MODE_0, 1000000, 8);
    int a2dVal_0 = 0;
    int a2dChannel_0 = 0;
    unsigned char data_0[3];

    mcp3008Spi a2d_1("/dev/spidev0.0", SPI_MODE_0, 1000000, 8);
    int a2dVal_1 = 0;
    int a2dChannel_1 = 1;
    unsigned char data_1[3];

    while(i > 0)
    {
        data_0[0] = 1;  //  first byte transmitted -> start bit
        data_0[1] = 0b10000000 |( ((a2dChannel_0 & 7) << 4)); // second byte transmitted -> (SGL/DIF = 1, D2=D1=D0=0)
        data_0[2] = 0; // third byte transmitted....don't care
 
        data_1[0] = 1;  //  first byte transmitted -> start bit
        data_1[1] = 0b10000000 |( ((a2dChannel_1 & 7) << 4)); // second byte transmitted -> (SGL/DIF = 1, D2=D1=D0=0)
        data_1[2] = 0; // third byte transmitted....don't care

        a2d_0.spiWriteRead(data_0, sizeof(data_0) );
        a2d_1.spiWriteRead(data_1, sizeof(data_1) );

        a2dVal_0 = 0;
        a2dVal_0 = (data_0[1]<< 8) & 0b1100000000; //merge data[1] & data[2] to get result
        a2dVal_0 |=  (data_0[2] & 0xff);

        a2dVal_1 = 0;
        a2dVal_1 = (data_1[1]<< 8) & 0b1100000000; //merge data[1] & data[2] to get result
        a2dVal_1 |=  (data_1[2] & 0xff);

        sleep(1);
        cout << "Pot 0: " << a2dVal_0 << endl;
        cout << "Pot 1: " << a2dVal_1 << endl;
        i--;
    }
    return 0;
}

NOTA. Amb un MCP3008 disposo de 8 canals (8 ADC). He utilitzat el CE1 (chip select, chip enabled) de la RPi per seleccionar aquest xip MCP3008 (que alhora també té el seu pin CS). Però a la RPi hi ha el CE1 i el CE2, que significa que puc utlitzar dos xips MCP3008. Si necessito més de 16 ADCs puc utilitzar un multiplexor (74HCT139), i així poder triar entre més de 2 xips MCP3008. Aquesta és una de les possibilitats a fer en la recreativa de dards, on es necessiten molts inputs.