Virtual Pinball con Arduino ... nudge analogico inside!!

Area dedicata alle guide tecniche,consigli e suggerimenti.
Rispondi
Avatar utente
Barito
Extreme Arcader
Extreme Arcader
Messaggi: 1856
Iscritto il: 08/12/2015, 19:18
Reputation: 500

Virtual Pinball con Arduino ... nudge analogico inside!!

Messaggio da Barito » 20/09/2016, 19:18

Sappiamo tutti cosa sia un flipper virtuale/virtual pinball dato che ogni appassionato di arcade prima o poi ci ha fatto un pensierino. Quello che spesso chi si è fermato al "pensierino" non sa è che "le vie del flipper virtuale sono infinite" e basta trascurare un aspetto importante come la scelta adeguata del monitor (occhio al lag!) o l'integrazione di un sistema di nudge analogico (la possibilità cioè di interagire con la palla tramite "colpi" al tavolo) per ritrovarsi fra le mani un costoso quanto "inutile" giocattolone.

Non è fine di questa guida affrontare il problema "lag", ma vedremo come creare una interfaccia semplice da costruire e programmare che permetta non solo di controllare le palette e tasti vari del nostro flipper virtuale (ormai banale per noi [coolio.gif] ) ma anche di utilizzare il nudge analogico.

Esistono diverse soluzioni che cercano di fare fronte al problema degli scossoni (nudge): in rete si trovano progetti di flipper virtuali con tasti laterali extra per il nudge digitale, altri che sfruttano obsoleti switch al mercurio, altri con cosiddetti "potenziometri a pendolo" (un peso attaccato ad un pot a due assi messo "a testa in giù") per il nudge analogico o, ancora più spesso, tavoli che non prevedono proprio una gestione del nudge. Non voglio citare soluzioni a pagamento VERGOGNOSE (ma lo faccio, tipo il "pinana 1" per gli amici "pinanal" visto che ti chiedono 90 euro per un hardware a base arduino che a loro costa meno di 20 euro e su cui è caricato un software come il nostro ... [angry.gif] ).

Fino ad oggi l'unica soluzione veramente funzionante era il progetto "Pinscape Controller" di un certo M.J. Roberts, il quale si basa su una economica schedina a microcontrollore KL25Z; il progetto è eccellente, l'unico "difetto" è che se non sei pratico di programmazione C++ difficilmente riuscirai ad apportare delle modifiche al sorgente.
Credo che la bontà del progetto Pinscape sia la "causa" della mancanza di alternative basate su altre schedine a microcontrollore, tipo Arduino; siccome a me piace smanettare con l'IDE di Arduino, ho deciso di provare a produrre una valida alternativa a Pinscape... e di seguito te la racconto ;)

Useremo una scheda arduino a microcontrollore ATmega32u4 (es. Leonardo, Micro, o pro-micro), la stessa già vista nella giuda introduttiva ad Arduino e in altri progetti presentati qui su arcademania. Inoltre, sfrutteremo le mirabilanti capacità di un comune modulo giroscopico GY521 per il nudge analogico!

Ci verranno in aiuto come sempre librerie già presenti e moduli ad-hoc che semplificheranno enormemente il lavoro. In particolare, ci verranno in aiuto la libreria "keyboard" e la libreria "joystick" (in particolare, la versione 1.0 della libreria). Come spiegato nella nostra introduzione ad Arduino, la prima è già presente fra le librerie default di Arduino, mentre la seconda deve essere "installata" ("installata" è una parola grossa dato che è un semplice copia-incolla).
EDIT: quando ho scritto questa guida, esisteva solo la versione 1 della libreria joystick. Lo sketch che trovi qui sotto funzionerà solo con la versione 1 della libreria, che puoi trovare >>QUI<<. Nel caso volessi uno sketch compatibile con la libreria joystick versione 2 (e successive) l'ho compilato, ma mai testato... se ti interessa provarlo, chiedi pure che te lo giro [icon_wink.gif]
Se non hai mai sentito parlare di arduino prima d'ora, prima di proseguire leggi bene la nostra guida introduttiva.

Il software su cui ho testato lo sketch è Visual Pinball 9.9.1, ma non ho dubbi che possa funzionare anche sul più recente VP10 o su altri software tipo Future Pinball.

PROCEDIAMO CAZZO! [w00t-anim.gif]

HARDWARE

Il progettino d'esempio che ti mostro qui è un semplice controller a quattro tasti (i due flipper laterali, pulsante START e pulsante tirapalle/plunger). Giusto per capirci, questa roba qui
SAM_2621.JPG
Io ho usato una vecchia scatola in legno che girava per casa, ma va benissimo di qualsiasi materiale... pensa che il primissimo prototipo l'ho fatto con una scatola per scarpe e funzionava a meraviglia :D

Lo sketch che dopo vedremo funziona comunque anche su un flipper virtuale a dimensioni reali (o scalate)!

I collegamenti da effettuare sono pochissimi:

- fra arduino e modulo giroscopico ci sono quattro collegamenti (di dominio comune e ben documentati ovunque in rete);

- fra arduino e pulsante, due collegamenti (anche qui: niente di nuovo!).

Il collegamento dei pulsanti è lo stesso già visto nella nostra introduzione ad Arduino: un pin del pulsante a massa, l'altro (Normally Open, cioè "NO") su un pin digitale di Arduino.

Immagine

Il collegamento del modulo giroscopico è il seguente...
collegamenti1.bmp
In foto ho messo un Arduino pro micro; i pin SCL e SDA vanno ai pin 3 e 2 rispettivamente di pro micro; in caso di Arduino Leonardo o Micro il collegamento è lo stesso. Nota che NON PUOI scegliere altri pin di pro micro o leonardo o micro per il collegamento al giroscopio, o non funzionerà.

Questo l'accrocchio che ho messo su io: il solito casino [wallbash.gif] ; ci sono tre potenziometri che a me sono serviti per i vari test, ma nella tua versione non serviranno [icon_wink.gif]
SAM_2623.JPG
SAM_2624.JPG

SKETCH


Passiamo allo sketch! Questo è un tipico caso in cui la natura open source di Arduino, dei suoi tool, e la presenza di una community attiva e propositiva tornano utili: non sarebbe stato affatto facile scrivere la porzione di codice per rendere possibile il colloquio fra arduino e il modulo giroscopico... non per me, almeno. Fortunatamente, in rete c'è già un sacco di materiale da cui prendere spunto!!

Codice: Seleziona tutto

/*Controller per Virtual Pinball, tilt/nudge analogico.
  funziona solo con microcontrollore ATmega32u4.
  il pinout dei pulsanti è il seguente (modificabile):
   4 - flipper sinistro
   5 - flipper destro
   6 - tasto start
   7 - plunger
  il collegamento a modulo GY521 prevede il seguente pinout (NON modificabile)
  GND - GND
  Vcc - Vcc/5V
  2 - SDA
  3 - SCL
  
  Compatibile con libreria joystick versione 1.
  NON compatibile con libreria joystick versione 2.

 by Barito 2016
 */

// LIBRERIE
#include <Wire.h>
#include <Joystick.h>
#include <Keyboard.h>

// COSTANTI
 //I2C address of the MPU-6050
 const int MPU=0x68; 
 //Pinball buttons
 //2 - SDA, 3- SCL
 const byte LbuttonPin = 4; // flipper sinistro  
 const byte RbuttonPin = 5; // flipper destro 
 const byte startPin = 6;   //Start
 const byte plungerPin = 7; //tirapalle/Plunger
 //tipo di flipper virtuale...
 const byte fullSizeVirtualPinball = 0;// <<IMPOSTARE 
 const byte delayTime = 30;
 
//VARIABILI 
 int16_t AcX, AcY; //valori x, y correnti, 16 bit
 //Accelerometer zeroes 
 int16_t xCenter; //valore di "X" a riposo
 int16_t yCenter; //valore di "Y" a riposo
 int16_t prevAcX, prevAcY;
 int16_t deltaAcX, deltaAcY; 
 //buttons states
 boolean statoPrecedenteStart = HIGH; //START
 boolean statoAttualeStart = HIGH; 
 boolean statoPrecedentePlunger = HIGH; //plunger - INVIO
 boolean statoAttualePlunger = HIGH;
 boolean statoPrecedenteLeft = HIGH; //flipper sinistro
 boolean statoAttualeLeft = HIGH;
 boolean statoPrecedenteRight = HIGH; //flipper destro
 boolean statoAttualeRight = HIGH;
 //grossValue determina l'intensità del colpo per raggiungere il Tilt; è un valore empirico.
 //se ti sembra che il tavolo risponda poco ai tuoi colpi, cala il valore di grossValue. 
 int grossValue=1000; //<< IMPOSTABILE
 int RTNtoCenter;
 float ForceX,ForceY;  
 //times
 long timeBTWnudgeX=0;
 long timeBTWnudgeY=0;
 long debounceStart = 0;
 long debouncePlunger = 0;
 long debounceLeft = 0;
 long debounceRight = 0;

  //SETUP
void setup(){
   
 //initialise Buttons
 pinMode(LbuttonPin, INPUT_PULLUP);
 pinMode(RbuttonPin, INPUT_PULLUP);
 pinMode(startPin, INPUT_PULLUP);
 pinMode(plungerPin, INPUT_PULLUP);

//RTNtoCenter determina il range di scostamento entro il quale il controller ridefinisce l'inclinazione di riposo.
 if (fullSizeVirtualPinball == 1) {RTNtoCenter = 0;}
 else {RTNtoCenter = 200;}//<< IMPOSTABILE. Aumenta il valore se il controller ti sembra troppo sensibile ai tuoi movimenti da fermo.
  
 //Begin Wire communication
 Wire.begin(); 
 Wire.beginTransmission(MPU);
 Wire.write(0x6B); //PWR_MGMT_1 register
 Wire.write(0);//wakes up the MPU-6050
 Wire.endTransmission(true);
  
 //Begin joystick emulation
 Joystick.begin(); 
 //Begin keyboard emulation
 Keyboard.begin();

 } 

 //LOOP
void loop(){
  
 Wire.beginTransmission(MPU);
 Wire.write(0x3B); // starting with register 0x3B (ACCEL_XOUT_H)
 Wire.endTransmission(false);
 Wire.requestFrom(MPU, 4, true);

 AcX = Wire.read()<<8|Wire.read();//0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L) 
 AcY = Wire.read()<<8|Wire.read();//0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
 
 //stati pin digitali
 statoAttualeRight = digitalRead(RbuttonPin);
 statoAttualeLeft = digitalRead(LbuttonPin);
 statoAttualeStart = digitalRead(startPin);
 statoAttualePlunger = digitalRead(plungerPin);

 //inizializzazione zeri
 if(fullSizeVirtualPinball == 0){XYinit();}
 else if (fullSizeVirtualPinball == 1 && millis()<1000){
   xCenter = AcX;
   yCenter = AcY;
 }

 //controlla se ci sono colpi e muovi il tavolo
 analogNudge(); 

 prevAcX = AcX;
 prevAcY = AcY;
 
 simpleInputHandling();
 specialInputHandling();

}//loop end

//FUNZIONI

void XYinit(){
 if (AcX>=prevAcX-RTNtoCenter && AcX<=prevAcX+RTNtoCenter){
  xCenter=AcX;}
 if (AcY>=prevAcY-RTNtoCenter && AcY<=prevAcY+RTNtoCenter){
  yCenter=AcY;}
 }

void analogNudge(){  
deltaAcY = AcY-yCenter; 
deltaAcX = AcX-xCenter;
ForceY = map(deltaAcY, -grossValue, grossValue, -127, 127);
ForceX = map(deltaAcX, -grossValue, grossValue, -127, 127);
if (ForceY>127){ForceY=127;}
else if (ForceY<-127){ForceY=-127;}
if (ForceX>127){ForceX=127;}
else if (ForceX<-127){ForceX=-127;}
Joystick.setYAxis(ForceY);
Joystick.setXAxis(ForceX);}

void simpleInputHandling(){

 if (statoAttualeLeft==HIGH && statoAttualeRight==HIGH){  
  if (millis() - debounceStart > delayTime && statoAttualeStart !=  statoPrecedenteStart){ //start - 1
      debounceStart=millis();
      if (statoAttualeStart == LOW){Keyboard.press(49);}
      else {Keyboard.release(49);}
      statoPrecedenteStart = statoAttualeStart;}
  if (millis() - debouncePlunger > delayTime && statoAttualePlunger !=  statoPrecedentePlunger){ //plunger -invio
      debouncePlunger=millis();
      if (statoAttualePlunger == LOW){Keyboard.press(224);}
      else {Keyboard.release(224);}
      statoPrecedentePlunger = statoAttualePlunger;}
}
 
 if (millis() - debounceLeft > delayTime && statoAttualeLeft !=  statoPrecedenteLeft){ //left flipper
      debounceLeft=millis();
      if (statoAttualeLeft == LOW){Keyboard.press(105);}
      else {Keyboard.release(105);}// i
      statoPrecedenteLeft = statoAttualeLeft;}
 if (millis() - debounceRight > delayTime && statoAttualeRight !=  statoPrecedenteRight){ //right flipper
      debounceRight=millis();
      if (statoAttualeRight == LOW){Keyboard.press(106);}
      else {Keyboard.release(106);}// j
      statoPrecedenteRight = statoAttualeRight;}
   
}// void end

void specialInputHandling(){
 // SPECIAL INPUTS (BUTTONS COMBINATIONS)
if (statoAttualeStart==LOW && statoAttualePlunger==HIGH && statoAttualeLeft==LOW && statoAttualeRight==HIGH){
  keypress(53);}//'5' (COIN)    
else if (statoAttualeStart==LOW && statoAttualePlunger==HIGH && statoAttualeLeft==HIGH && statoAttualeRight==LOW){
  keypress(198);} //F5 (runs the table in Visual Pinbal 8/9)  
else if (statoAttualeStart==HIGH && statoAttualePlunger==LOW && statoAttualeLeft==LOW && statoAttualeRight==HIGH){
  keypress(213);} //key end (DOOR OPEN)   
else if (statoAttualeStart==HIGH && statoAttualePlunger==LOW && statoAttualeLeft==HIGH && statoAttualeRight==LOW){
  keypress(57);} //'9' (volume +)   
else if (statoAttualeStart==LOW && statoAttualePlunger==LOW && statoAttualeLeft==LOW && statoAttualeRight==LOW){ 
  //Keyboard.releaseAll();
  keypress(177);} //ESC    
}

void keypress(int kp){
  Keyboard.press(kp);    
  delay(50);
  Keyboard.release(kp);
  delay(800);}

Ciò che lo sketch fa è tradurre le variazioni di inclinazione del modulo giroscopico in movimenti di un joystick.

Inoltre, tiene conto del fatto che durante le fasi di gioco un flipper virtuale a dimensioni reali mantiene nello spazio sostanzialmente sempre la medesima inclinazione; nel caso di controller da banco, invece, questo è soggetto a continui cambi di inclinazione (si pensi ad esempio al caso di gioco tenendo il controller sulle ginocchia): da qui la necessità di un "filtro" per distinguere quelli che sono gli scossoni al tavolo da quelli che sono i normali movimenti del giocatore.

Impostando a "1" la variabile "fullSizeVirtualPinball" si dice alla schedina "ho un virtual pinball di grosse dimensioni (Full Size), non fare continuamente azzeramenti di inclinazione"; ponendola a "0", invece, gli si dice "Ho un controller piccolino, aggiusta la sua inclinazione".

Ci sono poi altri due parametri modificabili spiegati nello sketch, ma ora non voglio tirarla troppo per le lunghe: all'inizio , setta solo "fullSizeVirtualPinball" e vai di default con gli altri.

I quattro tasti e i pin di Arduino corrispondenti sono:

4 - flipper sinistro
5 - flipper destro
6 - tasto start
7 - plunger

Naturalmente questi puoi modificarli a piacimento (evita se puoi i pin "0" e "1"); inoltre, puoi tranquillamente utilizzare i pin non in uso per collegare altri tasti o farci tutto ciò che ti passa per la testa (es. gestire un plunger analogico... eh già!).
Discorso diverso invece per i pin usati dal modulo giroscopico ("2" e "3"): quelli sono pin speciali e non possono essere modificati.

Nello sketch ho previsto anche alcune combinazioni di tasti che ho trovato utili con Visual Pinball, in particolare:

- premendo contemporaneamente flipper sinistro e tasto start, inserisci un credito;
- premendo contemporaneamente flipper destro e tasto start, simula la pressione del tasto F5 (fa partire un tavolo in VP);
- premendo contemporaneamente flipper sinistro e tirapalle, simula l'apertura della gettoniera (utile per settare il volume in alcuni tavoli Williams);
- premendo contemporaneamente flipper destro e tirapalle, alza il volume (simula "9");
- premendo contemporaneamente tutti e quattro i tasti, simula "ESC".

SETUP

Come detto, questo progetto è stato testato su VP9.9.1. Non escludo che possa funzionare anche su altri flipper per PC; alla peggio, piccole modifiche allo sketch saranno certamente sufficienti!

- prima di giocare, assegna i tasti nel menu "keys". Per assegnare i tasti, basta premere con il mouse il riquadro corrispondente sul menu (es. Left Flipper) e successivamente premere il tasto relativo al flipper sinistro sul flipper virtuale. Se tutto è andato a buon fine, subito sotto il riquadro del pulsante comparirà il tasto di assegnazione (nel nostro sketch Left Flipper è assegnato a “i”, Right Flipper è assegnato a "j", e così via).
La stessa procedura andrà ripetuta per tutti i tasti del flipper virtuale che si intende utilizzare.

- ricorda di spuntare l'opzione "enable analog nudge" nel menu keys, o il tilt analogico non potrà funzionare. Inoltre, ti consiglio di spuntare anche "enable nudge filter" visto che ho testato tutto con quel filtro attivo (ed è molto migliore del nudge analogico senza filtro).

- in funzione alla orientazione di montaggio del giroscopio, potrebbe essere necessario invertire gli assi "X" e "Y" fra loro, oppure invertire l'orientamento degli assi. Per agevolare questo passaggio ho buttato giù due banali tavoli VP9 a pendenza zero, uno orizzontale e uno verticale; scegli quello adeguato al tuo pinball, carica lo sketch impostando l'opzione "fullSizeVirtualPinball" a "1", muovi il tavolo e vedi la palla come si comporta. Oh, ricorda poi di settare l'opzione adeguatamente per il tuo tipo di flipper virtuale e ricarica lo sketch sulla schedina!
Per i curiosi, questo l'aspetto del tavolo base base base che trovi allegato
AMTavoloTest.png
tavoli zero slope.zip
[up.gif]

Chissà se qualcuno nei prossimi mesi/anni lo renderà ancora più bello ... [book6.gif]

Questo un breve video che dimostra il funzionamento dello sketch per quanto riguarda il nudge analogico in fase di test degli assi: su un tavolo ad inclinazione nulla, la palla segue l'inclinazione del controller, come atteso



- se a virtual pinball o controller immobile la pallina dovesse muoversi nel tavolo a zero pendenza, setta un deadzone fra 3 e 10 (se non ricordo male io ho impostato 7...).

NOTE FINALI

Non voglio gettare acqua sul fuoco che ho appena contribuito ad alimentare, ma prima di buttarti sull'impresa "Virtual Pinball" tieni presente che NON PUO' E NON POTRA' MAI sostituire un flipper vero. Avere per le mani decine di tavoli virtuali non è comparabile al singolo tavolo reale. Ergo, spendere per la realizzazione di un flipper virtuale tanto quanto per l'acquisto di un flipper non ha senso.

Diverso naturalmente il discorso se i costi rimangono contenuti...

Il maggiore pregio di un flipper virtuale è di consentire di prendere confidenza con le regole dei tavoli in modo divertente e veloce, ma non sperare di diventare un buon giocatore di flipper dal lato tecnico.

Te lo dice uno che ormai una ideuzza piuttosto chiara a riguardo sell'è fatta ... [icon_wink.gif]
Non hai i permessi necessari per visualizzare i file allegati in questo messaggio.
Non é vero che siamo quello che mangiamo: siamo quello che non dimentichiamo.


Avatar utente
bisus
Arcade Maniac
Arcade Maniac
Messaggi: 2924
Iscritto il: 03/11/2015, 2:16
Reputation: 486
Località: Firenze
Contatta:

Re: Virtual Pinball con Arduino ... nudge analogico inside!!

Messaggio da bisus » 20/09/2016, 21:24

allora e' ufficiale sei matto davvero
Bravissimo Barito
Segnalazioni problemi Forum
Non esitate a contattarmi in privato se doveste avere problemi sul forum http://www.arcademania.eu
grazie


Avatar utente
giuppo
Site Admin
Site Admin
Messaggi: 3314
Iscritto il: 03/11/2015, 7:46
Medaglie: 27
Reputation: 529
Località: San Salvo
Nazione: Italia
:
Medaglia d'oro Medaglia d'argento Medaglia di bronzo Medaglia di legno I - Team Battle Maestro Yoda

Re: Virtual Pinball con Arduino ... nudge analogico inside!!

Messaggio da giuppo » 20/09/2016, 23:05

Standing ovation please [groupwave1.gif] [groupwave1.gif]
Arcademania the forum around you


Avatar utente
Il Dela
Arcader
Arcader
Messaggi: 281
Iscritto il: 24/08/2016, 15:25
Reputation: 59
Località: Valle Camonica
Nazione: IT

Re: Virtual Pinball con Arduino ... nudge analogico inside!!

Messaggio da Il Dela » 21/09/2016, 0:07

Gray ottimo direi :)

Inviato dal mio Redmi Note 3 utilizzando Tapatalk


Avatar utente
Moket
Arcade Maniac
Arcade Maniac
Messaggi: 2777
Iscritto il: 03/11/2015, 9:41
Medaglie: 4
Reputation: 473
Località: Roma
:
Medaglia d'oro Medaglia di bronzo Medaglia di legno

Re: Virtual Pinball con Arduino ... nudge analogico inside!!

Messaggio da Moket » 21/09/2016, 7:45

Bravo Barito. Davvero un ottimo lavoro...
<< Sono il padre dei famigerati Bacheca Boys >>


Avatar utente
b4nd1t0
Extreme Arcader
Extreme Arcader
Messaggi: 2206
Iscritto il: 06/06/2016, 17:34
Medaglie: 8
Reputation: 526
Località: Roma
Nazione: Italia
:
Medaglia d'oro Medaglia di bronzo Medaglia di legno
Contatta:

Re: Virtual Pinball con Arduino ... nudge analogico inside!!

Messaggio da b4nd1t0 » 21/09/2016, 9:19

Quello che mancava in pratica, bravo!!!
mame-wolfmame-hbmame cab friendly ed altri emu compilati da me -> vai quì

in bacheca uso wolfmame 0.167 (e mo è rimasto solo masez! [on_eek.gif] )


Rispondi

Chi c’è in linea

Visitano il forum: Nessuno e 2 ospiti