Gamoover

[move]Vous aimez le couscous-boulettes, le chorizo, la Force 4, et la mimolette ? Alors soyez les bienvenus sur Gamoover ! [/move]

Substitution d'un PCB Pole Position par un PC sous MAME

Démarré par Little_Rabbit, Lundi 06 Avril 2020, 18:36:44 PM

SDF

#16
Pour ton probleme de joystick, je dirai que la barre est vide et se remplie de rouge en appuyant. Sinon, tu sais que pour un prix derisoire, en programmant un arduino, tu peux le transformer en joystick ana et que windows le gere tres bien  ;)

Little_Rabbit

Salut,

@Nene2K10 : oui bien sûr, et c'est précisément ce que je disais en introduction et à la fin de mon dernier épisode du WIP ;). J'aborderai le sujet très prochainement :).

A+
Recherche bornes dédiées ou PCB originaux: Miss Pacman, Dig Dug, Galaga, Mappy, Asteroids, Battlezone, Missile Command, Tempest, Star Wars, Donkey Kong (+ Jr), Mario Bros, Moon Patrol, Defender, Joust, Frogger, Gyruss, Pooyan, Space Tactics, Zaxxon, etc. Flip : Gottlieb des années 80 (Spirit, Amazon Hunt, ...), Baby Pac Man. Divers :  Ice Cold Beer => Trois fois rien quoi ! :D
Ma séance sur le divan : c'est grave Docteur ? :-\
Ma gaming room, ma storage room

Iro

non mais ce qu'on veut te dire, c'est d'oublier ton interface moisie (même si on a bien compris que tu es têtu :D )
"Jet set 2, c'est avec Robert Garcia ?" Kaneda, Lapsus de sac Vol.1
Peter Shou Owner' Club

WIPs : Naomi - SEGA Rally - AB Cop - Lethal Enforcers - COMPUMI - Terminator 2 - Space Invaders - Artworks pour Boitiers K7 Naomi CF - Ma collec' de panels

LES TUTOS DE GAMO   

Little_Rabbit

#19
Salut,

Citation de: Iro le Jeudi 09 Avril 2020, 11:26:15 AMnon mais ce qu'on veut te dire, c'est d'oublier ton interface moisie (même si on a bien compris que tu es têtu :D )

Oui, je sais bien que je n'ai pas besoin de m'accrocher à cette carte, mais j'aime bien comprendre pourquoi un appareil censé faire un truc ne fonctionne pas ! ;)

D'ailleurs, en parallèle de tout ça, j'avais ainsi sollicité lipide512 qui avait rédigé un très bon tuto sur des contrôleurs analogiques à base d'Arduino : ici

Lipide512 nous avait expliqué que depuis il valait mieux partir sur un Leonardo qui simplifiait la manip (en préservant notamment le port COM du Leonardo, sans devoir gérer les modes programmation/exploitation qu'imposait l'Arduino).

Je me suis donc équipé :



Et là, s'ouvrait à moi un nouvel univers : la programmation des Arduino / Leonardo !

J'ai copieusement pataugé au début, puis compris des bribes de trucs. J'étais parvenu à programmer un Leonardo en épurant les exemples pour ne garder qu'un potard et quelques boutons.

Ça c'était en décembre dernier. Je ne m'y suis pas recollé que tout récemment :-\...

Et dernièrement, dans le sujet d'aganyte sur le Pijamma, supernono a évoqué un bout de code gérant spinner et potentiomètre. Suite à l'intérêt que j'ai témoigné pour son travail, par mp il m'a très gentiment proposé son aide : un grand merci à lui ! :-*

Mais étant resté bloqué sur la pédale d'accélération, je n'ai pas encore pris le temps de mettre à profit ses suggestions : j'espère pouvoir le faire prochainement.

Revenons-en à la gestion de notre pédale d'accélération via un Leonardo.

Input Analogique à base d'Arduino/Leonardo

À l'occasion, peut-être que j'essayerai de faire un tuto « Leonardo pour les nuls », ça pourrait servir à d'autres comme moi qui peuvent être un peu perdu quand on découvre un sujet aussi vaste que le monde des Arduinos !...

Je ne vais pas vous détailler toutes les étapes par lesquelles je suis passé, mais j'ai tout de même un peu galéré...

Je suis parti sur la bibliothèque «ArduinoJoystickLibrary-version-2.0 » de  Matthew Heironimus que vous pouvez télécharger ici. Son auteur explique son travail sur cette page de son blog.


Je me suis inspiré de plusieurs sources des exemples, modifié à ma sauce pour ne garder que 2 potards analogiques et 4 boutons.

À partir du panneau de contrôle, ça fonctionnait correctement sous Windows : si je tournais un potentiomètre, j'avais bien une progression régulière du curseur correspondant.

Par contre, dès que sous Windows XP j'allais sous MAME, dans le menu service de Pole Position, et bah j'avais exactement le même résultat qu'avec la carte XimoTek !  >:(

À savoir :
- La valeur de l'accélérateur restait un bon moment à 0 en début de course
- puis passait subitement à FF
- puis en continuant toujours la même rotation du potard, ça se mettait à décroître doucement jusqu'à la valeur 70

C'était à s'arracher les cheveux !

J'ai fini par tester le même montage, mais cette fois sous Windows 7, avec un MameUI32 que j'avais installé il y a des années, version 0.123 de février 2008 !...

Et oh surprise, cela fonctionnait correctement : l'accélération partait bien de 0, et au fur et à mesure allait jusqu'à 90.

Ce maxi à 90 m'a d'abord surpris car sur une vraie borne Pole Position, on va au-delà  : 98 sur la mienne quand j'ai fait le test (il faut regarder les chiffres après "ACCEL") :

(car il me reste un PCB dont le mode service fonctionne)

Sur une vraie borne le potentiomètre n'est pas utilisé sur toute sa course : une partie de la piste carbone n'est jamais parcourue par le curseur du potard car la butée mécanique de la pédale l'interdit. Si on l'utilisait sur toute sa course, on peut imaginer qu'au lieu de 98, on irait sans doute jusqu'à FF.

Cette limitation à 90 m'a d'abord laissé perplexe et me faisait penser que j'avais un bug, mais j'imagine que MAME bride la valeur maxi à 90, car cela correspond déjà à la vitesse maximale dans le jeu.

On remarque aussi, si vous tendez l'oreille, qu'il y a un retard entre la butée physique de la pédale en fin de course, et l'établissement de la valeur maxi affichée à l'écran. J'ai d'abord cru que ce retard était dû au fait que sur un PCB original de Pole Position, entre la pédale et l'entrée du convertisseur analogique/numérique, il y a un circuit RC (résistance + condensateur) :



Pour trouver la constante de temps d'un tel montage, il faut utiliser la formule T = R x C.

Or ici, R = 1K ohms, C = 0,1 µF, cela nous donne => 1000 x 0,0000001 = 0,0001 sec, soit 0,1 ms, alors qu'on constate un retard de plus d'une seconde... Je ne sais pas alors à quoi est dû ce retard (peut-être simplement la façon dont le soft du PCB gère l'incrément de la valeur de l'accélérateur...).


Bon OK, ça marche sous Windows 7 et ce vieux MAME, mais pourquoi sous XP sous MAME 0.155 cela déconnait-il ? J'ai essayé d'effacer mes fichiers de config, mais cela ne changeait rien.

J'ai fini par re-télécharger MAME 0.155, tout réinstaller, et cette fois ça fonctionnait !  ;D

J'ai pensé que dans mes essais précédents j'avais fini par foirer la configuration. Pour en avoir le cœur net, je reteste avec mes MAME précédemment installés, et ça marche aussi ! C'est à n'y rien comprendre !  ::)

Et dernier essai, j'ai remis la carte XimoTek, et là par contre ça merdait bien comme au tout début.

Vous n'imaginez pas le temps que j'ai perdu avec ces résultats incohérents !...  )::(
(d'autant que ça commençait à me gonfler sérieux, et que j'étais à deux doigts de tout envoyer balader !  :D)

Je vous livre ici mon source sans son état actuel, qui semble fonctionner :

// Program used to test the USB Joystick object on the
// Arduino Leonardo or Arduino Micro.
//
// Matthew Heironimus
// 2015-03-28 - Original Version
// 2015-11-18 - Updated to use the new Joystick library
//              written for Arduino IDE Version 1.6.6 and
//              above.
// 2016-05-13   Updated to use new dynamic Joystick library
//              that can be customized.
//------------------------------------------------------------

#include "Joystick.h"

#define MaxButtonQty 4
#define FirstButtonPinNumber 4
#define Frein A0
#define Accelerateur A1



// Create Joystick
Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID, JOYSTICK_TYPE_JOYSTICK,
  MaxButtonQty, 0,       // 4 boutons, pas de Hat Switch
  true, true, false,     // Conserve un axe X et Y, mais les laisse en position neutre, pas d'axe Z
  false, false, false,   // Pas de Rx, Ry, ou Rz
  false, true,           // Pas de rudder, mais throttle en guise d'accélérateur
  false, true, false);    // seulement un frein, pas de volant (ni accélérateur car ne semble pas fonctionner...)


 
// Set to true to test "Auto Send" mode or false to test "Manual Send" mode.
//const bool testAutoSendMode = true;
const bool testAutoSendMode = false;

//const bool DebugMode = true;
const bool DebugMode = false;



void setup() {

  //Start serial communication with the computer
  if (DebugMode)
  {
    Serial.begin(9600);
  }
 
  // Set Range Values
  Joystick.setXAxisRange(-127, 127);
  Joystick.setYAxisRange(-127, 127);
  Joystick.setXAxis(0);
  Joystick.setYAxis(0);
 
   
  Joystick.setThrottleRange(0, 1023);
  Joystick.setThrottle(0);
  /*Joystick.setAcceleratorRange(0, 1023);
  Joystick.setAccelerator(0);*/
  Joystick.setBrakeRange(0, 1023);
  Joystick.setBrake(0);
 
 
 // Les boutons du panel sont attribués aux pin 4 à 7
  pinMode(FirstButtonPinNumber, INPUT_PULLUP);
  pinMode(FirstButtonPinNumber + 1, INPUT_PULLUP);
  pinMode(FirstButtonPinNumber + 2, INPUT_PULLUP);
  pinMode(FirstButtonPinNumber + 3, INPUT_PULLUP);

  // l'accélérateur et le frein sont associée aux entrées analogiques A0 et A1
  // A0 = frein
  // A1 = accélérateur
  // les axes X et Y ne sont que virtuels, en position neutre, donc pas d'entrées analogiques pour eux
  pinMode(A0, INPUT_PULLUP);
  pinMode(A1, INPUT_PULLUP);



 
  if (testAutoSendMode)
  {
    Joystick.begin();
  }
  else
  {
    Joystick.begin(false);
  }
 
  // pour pouvour faire clignoter la LED du Leonardo
  pinMode(13, OUTPUT);
}


// État précédent des boutons
int lastButtonState[MaxButtonQty] = {0,0,0,0};
// Valeurs précédentes de l'accélérateur et du frein
int lastAcceleratorValue = 0;
int lastBrakeValue = 0;




void loop() {
 
int index;
int currentButtonState;
int currentAnalogValue;
String DebugMsg;
String Msg1;
String Msg2;
String Msg3;
bool ButtonChange;

  Msg1 = String("Gas     = ");
  Msg2 = String("Brake   = ");
  Msg3 = String("Buttons = ");
  // remet à 0 la chaîne utilisée pour les messages de debug
  DebugMsg = String();

  // Turn indicator light on.
  digitalWrite(13, 1);
 
  // lit les valeurs analogiques
  // l'accélérateur
  currentAnalogValue = analogRead(Accelerateur);
  if (currentAnalogValue != lastAcceleratorValue)
  {
    lastAcceleratorValue = currentAnalogValue;
    Joystick.setAccelerator (lastAcceleratorValue);
    Joystick.setThrottle (currentAnalogValue);
    if (DebugMode)
    {
      DebugMsg = Msg1 + lastAcceleratorValue;
      Serial.println(DebugMsg);
    }
  }

  // puis le frein
  currentAnalogValue = analogRead(Frein);
  if (currentAnalogValue != lastBrakeValue)
  {
    lastBrakeValue = currentAnalogValue;
    Joystick.setBrake (currentAnalogValue);
    if (DebugMode)
    {     
      DebugMsg = Msg2 + lastBrakeValue;
      Serial.println(DebugMsg);
    }
  }

  // Lit les boutons
  ButtonChange = false;
  for (index = 0; index < MaxButtonQty; index++)
  {
    currentButtonState = !digitalRead(index + FirstButtonPinNumber);
    if (currentButtonState != lastButtonState[index])
    {
      Joystick.setButton(index, currentButtonState);
      lastButtonState[index] = currentButtonState;
      ButtonChange = true;
    }
  }
  if (DebugMode)
  {
    if (ButtonChange == true)
    {
      DebugMsg = Msg3 + lastButtonState[0] + " " + lastButtonState[1] + " " + lastButtonState[2] + " " + lastButtonState[3];
      Serial.println(DebugMsg);
    }
  }
   
  if (testAutoSendMode == false)
  {
    Joystick.sendState();
  }

  //Attend 15 ms avant de boucler
  delay(15);
 
}

Parmi vos périphériques, vous devez alors avoir une manette de jeu qui vous affiche ces paramètres :



À noter que parmi les types d'axes disponibles sur le joystick analogique que le Leonardo gère ici, on est censé avoir Throttle (manette de gaz sur un avion) et Accelerator (pour un accélérateur de voiture). Sauf que Accelerator n'a jamais fonctionné, il n'apparaît pas quand on regarde les paramètres de la manette ! J'ai donc pris Throttle à la place.

Au sujet du mode « testAutoSendMode », je n'ai pas vraiment vu de différence selon qu'il est activé ou pas. De ce que j'ai compris, le Leonardo envoie à la demande ou automatiquement les infos mises à jour du joystick, c'est ça ?

Maintenant que le panel fonctionne, avec son accélérateur et le frein, je vais me pencher sur la gestion du spinner pour le volant ! Le source que supernono m'a donné devrait fortement m'aider, mais encore faut-il que je comprenne comment cela fonctionne ! :D

C'est ce que nous verrons la prochaine fois ! :)

A+
Recherche bornes dédiées ou PCB originaux: Miss Pacman, Dig Dug, Galaga, Mappy, Asteroids, Battlezone, Missile Command, Tempest, Star Wars, Donkey Kong (+ Jr), Mario Bros, Moon Patrol, Defender, Joust, Frogger, Gyruss, Pooyan, Space Tactics, Zaxxon, etc. Flip : Gottlieb des années 80 (Spirit, Amazon Hunt, ...), Baby Pac Man. Divers :  Ice Cold Beer => Trois fois rien quoi ! :D
Ma séance sur le divan : c'est grave Docteur ? :-\
Ma gaming room, ma storage room

Little_Rabbit

#20
Salut,

J'aborde à présent la gestion du volant : sur Pole Position, il s'agit d'une roue codeuse associée à un opto-coupleur, constitué d'une paire de LED infrarouge/Phototransistors :





Cela revient donc pour le PC à le faire voir comme un spinner, ou encore comme l'axe X d'une souris.

Là encore le Leonardo est idéal puisqu'il est conçu pour se substituer à un clavier souris !

Supernono m'a été d'un grand secours puisqu'il m'a mis le pied à l'étrier en me fournissant son source Arduino.

Mais voyons d'abord en quoi consiste le « coupleur » d'une borne Pole Position au niveau électronique :



En plus de son bloc pédale, mon frère m'avait filé le coupleur de son volant : parfait, je vais pouvoir utiliser celui-là pour mes tests... Sauf qu'au moment de chercher son brochage pour savoir comment le brancher, je me suis rendu compte que le sien n'est pas un coupleur Pole Position, et qu'il avait été bricolé !



=> 2 résistances ajoutées, fils soudés à même le connecteur, et une piste coupée par un perçage, et son fil de substitution mis en place... :-\

C'est bien un coupleur ATARI, mais il porte la référence A043397 (le copyright 1986 figurant dessus m'avait d'ailleurs intrigué pour une borne de 1981 !...).

Une rapide recherche sur Google m'apprit que ce coupleur venait très certainement d'une Road Blaster ! Voici son schéma :



D'un point de vue fonctionnel, il est identique à celui de Pole Position, et surtout son brochage est identique (si ce n'est que DIR1 et CLK1 sont inversés, mais cela n'a je pense aucune incidence) !

Je bricole 2 équerres avec des bouts de Mecanno pour mettre la roue codeuse au milieu de l'opto-coupleur :



et ensuite j'ai remis ça au propre en virant les résistances et en faisant un connecteur propre.

Que remarque-t-on sur les schémas ci-dessus ? Que les sorties CLK et DIR de ces cartes coupleurs sont en collecteurs ouverts PNP sur Road Blaster, et émetteur « flottant » sur Pole Position : il nous faudra donc dans les 2 cas une résistance de pull-down. C'est d'ailleurs ce à quoi servaient les 2 résistances de 330 Ω que j'ai enlevées. Vous me direz peut-être que j'aurais mieux fait de les laisser ? Mais non car mon but est toujours de faire quelque chose « plug & play » sur une borne non modifiée ;). Je mettrai donc ces résistances de pull down sur mon câblage vers le Leonardo :).

Avec le montage suivant, j'ai pu l'alimenter et vérifier si tout fonctionnait normalement :



C'est bien le cas, comme l'illustrent ces 2 relevés à l'analyseur logique :


=> quand on tourne la roue codeuse dans un sens


=> quand on tourne la roue codeuse dans l'autre sens

Le temps écoulé entre deux impulsions va permettre au programme de connaître la vitesse à laquelle on tourne la roue (ou encore la distance parcourue par une boule de souris). Et le 1er front montant survenant entre les 2 canaux donnera le sens de déplacement.

On voit que dans le premier cas, c'est sur le canal 1 que survient en 1er un front montant, et quand on tourne la roue dans l'autre sens, c'est sur le canal 2 que survient en 1er un front montant.


Maintenant que la roue codeuse et sont opto-coupleur sont prêts, passons à son interfaçage avec le Leonardo !

Spinner à base d'Arduino Leonardo :

Ce qui est génial avec les Arduino, c'est qu'il existe énormément de bibliothèques toutes faites pour des tas de problématiques divers et variées, et que donc, d'autres ont fait le travail avant vous ! :)

En l'occurrence, supernono m'avait transmis son travail qui s'appuyait sur la bibliothèque « Encoder » de Paul Stoffregen.

Voici le source de supernono (il gère l'axe X et Y d'une souris, sa molette de défilement et deux boutons)

/* For Leonardo , Due , Pro micro boards only.

 Controls the mouse from 3 quadrature encoder on an Arduino Leonardo, Due or Pro Micro.

 Hardware:
 * 2 pushbuttons attached to D8, D9
 * 1 X axis encoder attached to D 2,D 4
 * 1 Y axis encoder attached to D 3,D 5
 * 1 Z axis encoder attached to D 0,D 1

 by Supernono

 */

//#define ENCODER_USE_INTERRUPTS
#define ENCODER_OPTIMIZE_INTERRUPTS

#include "Encoder.h"
#include "Mouse.h"
//#include "Keyboard.h" pour utiliser un clavier

// Change these pin numbers to the pins connected to your encoder.
//   Best Performance: both pins have interrupt capability
//   Good Performance: only the first pin has interrupt capability
//   Low Performance:  neither pin has interrupt capability
Encoder axeX(2, 4);
Encoder axeY(3, 5);
Encoder axeZ(0, 1);
//   avoid using pins with LEDs attached

const int rightButton = 8;
const int leftButton = 9;
const int RXLED = 17;
int range = 2;              // output range of X or Y movement; affects movement speed
int responseDelay = 10;     // response delay of the mouse, in ms


void setup() {
  // initialize the buttons' inputs:
  pinMode(2, INPUT);
  pinMode(3, INPUT);
  pinMode(4, INPUT);
  pinMode(5, INPUT);
  pinMode(6, INPUT); //_PULLUP);
  pinMode(7, INPUT); //_PULLUP);
  pinMode(rightButton, INPUT_PULLUP);
  pinMode(leftButton, INPUT_PULLUP);
  pinMode(RXLED,OUTPUT);
  // initialize mouse control:
  //Mouse.begin();
  //Serial.begin(9600);
}

long positionX  = -999;
long positionY = -999;
long positionZ = -999;

void loop() {
  // read the axes:
  //long newX, newY;
  long newX = axeX.read();
  long newY = axeY.read();
  long newZ = axeZ.read();
  // read the buttons:
  int rightState = digitalRead(rightButton);
  int leftState  = digitalRead(leftButton);

  if (newX != positionX || newY != positionY || newZ !=positionZ) {
   
    // calculate the movement distance based on the
    int  distanceX = (newX - positionX) * range;
    int  distanceY = (newY - positionY) * range;
    int  distanceZ = (newZ - positionZ) * 0.2;
   
    // if X or Y is non-zero, move:
    if ((distanceX != 0) || (distanceY != 0) || (distanceZ != 0)) {
      Mouse.move(distanceX, distanceY, distanceZ);
    }
   
    positionX = newX;
    positionY = newY;
    positionZ = newZ;
  }


   // if the mouse button is pressed:
  if (leftState == LOW) {
    // if the mouse is not pressed, press it:
    if (!Mouse.isPressed(MOUSE_LEFT)) {
      Mouse.press(MOUSE_LEFT);
      Serial.println("Basic Encoder Test:");
    }
  }
  // else the mouse button is not pressed:
  else {
    // if the mouse is pressed, release it:
    if (Mouse.isPressed(MOUSE_LEFT)) {
      Mouse.release(MOUSE_LEFT);
    }
  }

   // if the mouse button is pressed:
  if (rightState == LOW) {
    // if the mouse is not pressed, press it:
    if (!Mouse.isPressed(MOUSE_RIGHT)) {
      Mouse.press(MOUSE_RIGHT);
    }
  }
  // else the mouse button is not pressed:
  else {
    // if the mouse is pressed, release it:
    if (Mouse.isPressed(MOUSE_RIGHT)) {
      Mouse.release(MOUSE_RIGHT);
    }
  }

  // a delay so the mouse doesn't move too fast:
  delay(responseDelay);
}

Concrètement pour pouvoir en faire un « croquis » Arduino, vous allez devoir :

- ajouter la bibliothèque « mouse » (=> Croquis/Inclure une bibliothèque/Mouse)
- ajouter la bibliothèque « encoder », détaillée sur cette page => Outils/Gérer les bibliothèques... => chercher « Encoder » et sélectionner celle de Paul Stoffregen :



Vous pouvez à présent compiler le source et le téléverser dans l'Arduino.

Toutefois, il faut convenir des broches du Leonardo auxquelles on raccorde le coupleur Atari.

Pour fonctionner au mieux, l'auteur précise que les 2 signaux de l'optocoupleur doivent être raccordés à des entrées de l'Arduino supportant les interruptions. Une interruption pour un micro-processeur est sa capacité d'exécuter une petite tâche quand survient un événement particulier. Il interrompt l'exécution du programme en cours, et traite immédiatement la routine d'interruption. Quand cette routine s'est exécutée, le processeur revient là où il en était. Dans notre cas, l'interruption peut se produire par exemple lorsque survient un front montant sur une de ses entrées.

D'après ce que j'ai lu sur Internet, le Leonardo supporte les interruptions sur les entrées numériques suivantes :

D0, D1, D2, D3 et D7

Je choisis de câbler mon spinner sur les entrées D2 et D3. Rappelons-nous qu'il faut les résistances de pull-down sur ces deux entrées.

Cela nous donne le câblage suivant :



Quant au code source, il est d'une simplicité extraordinaire : comme on ne garde que l'axe X pour notre spinner, qu'on n'a pas besoin de boutons, il n'y a presque rien à faire ! (j'ai ajouté des sorties sur le moniteur série pour le mode Debug, afin de vérifier que cela fonctionne).

/* For Leonardo , Due , Pro micro boards only.

 Controls the mouse from 3 quadrature encoder on an Arduino Leonardo, Due or Pro Micro.

 Hardware:
 * 1 X axis encoder attached to D 2,D 3

 by Supernono, modified by Little_Rabbit

 */

#include "Encoder.h"
#include "Mouse.h"


//à mettre en commentaire pour ne pas avoir les messages de debug sur le moniteur série
#define DEBUG

//#define ENCODER_USE_INTERRUPTS
#define ENCODER_OPTIMIZE_INTERRUPTS


// Change these pin numbers to the pins connected to your encoder.
//   Best Performance: both pins have interrupt capability
//   Good Performance: only the first pin has interrupt capability
//   Low Performance:  neither pin has interrupt capability
Encoder axeX(3, 2);
//   avoid using pins with LEDs attached


// Mettre 1 ou -1 selon le sens de déplacement souhaité pour le curseur de souris
const int SensRotationX = 1;


int range = 2;                    // output range of X movement; affects movement speed
const int responseDelay = 10;     // response delay of the mouse, in ms




void setup() {

  // pour les messages de debug sur moniteur série
  #ifdef DEBUG
      Serial.begin(9600);
  #endif
 
  // initialize the buttons' inputs:
  // les broches 2 et 3 supportent les interruptions sur Leonardo
  pinMode(2, INPUT);
  pinMode(3, INPUT);
 
  // initialize mouse control:
  Mouse.begin();
}

long positionX  = -999;

#ifdef DEBUG
String DebugMsg = String(); // déclare et remet à 0 la chaîne utilisée pour les messages de debug
String Msg1 = String("Curseur X : ");
String Msg2 = String(" => distance X : ");
#endif

void loop() {
 
  // read the axes:
  long newX = axeX.read() * SensRotationX;

  if (newX != positionX) {
   
    // calculate the movement distance based on the new position
    int  distanceX = (newX - positionX) * range;
   
    // if X is non-zero, move:
    if (distanceX != 0) {
      #ifdef DEBUG
        DebugMsg = Msg1 + positionX + Msg2 + distanceX;
        Serial.println(DebugMsg);
      #endif

      // seul l'axe des X évolue pour un spinner
      Mouse.move(distanceX, 0, 0);
    }
   
    positionX = newX;
  }

  // Délais pour ne pas surcharger le PC avec sans cesse de nouveaux déplacements du curseur:
  delay(responseDelay);
}

Résultat : cela a fonctionné du premier coup !  :-)=

Par contre, sur mon poste Windows 7, cela n'avait aucun effet sur le curseur de la souris ! :-\

En fait, il a fallu que je réinstalle le driver Arduino pour que tout rentre dans l'ordre :).

Sur le poste sous Windows XP, le curseur de la souris bougeait bien, mais à chaque branchement du Leonardo, Windows me gratifiait d'un message me demandant d'installer le driver : pour régler ce problème, téléchargez l'IDE Arduino complet, et si vous ouvrez le .exe avec WinZip, vous trouverez un dossier « drivers » => désignez à Windows ce dossier et laissez le faire l'installation : cela fonctionne nickel.

Au premier essai sous MAME, cela ne fonctionnait pas :-\. Même une souris de base n'avait aucun effet sur le paramètre « steering » du jeu. J'ai questionné Chelnov qui m'a donné la solution : dans le fichier .ini de MAME, il faut changer un des paramètres :

Par défaut on a :

[tt]mouse   0[/tt]   qu'il faut changer en    [tt]mouse   1[/tt]

Autre détail pour les newbies comme moi en matière de MAME : je n'avais de fichier .ini ! :D

Pour cela, il faut en ligne de commande exécuter la commande suivante :

[tt]mame –cc[/tt]

Cela génère le fichier mame.ini, dans lequel vous pourrez mettre « mouse » à 1 ! ^-

Une fois que tout est bien paramétré, voilà le résultat dans le mode service de Pole Position :


 :-)=

Il me faut à présent regrouper les sources de la gestion de la pédale et du spinner pour n'en faire qu'un : cela devrait être très simple :).

A+
Recherche bornes dédiées ou PCB originaux: Miss Pacman, Dig Dug, Galaga, Mappy, Asteroids, Battlezone, Missile Command, Tempest, Star Wars, Donkey Kong (+ Jr), Mario Bros, Moon Patrol, Defender, Joust, Frogger, Gyruss, Pooyan, Space Tactics, Zaxxon, etc. Flip : Gottlieb des années 80 (Spirit, Amazon Hunt, ...), Baby Pac Man. Divers :  Ice Cold Beer => Trois fois rien quoi ! :D
Ma séance sur le divan : c'est grave Docteur ? :-\
Ma gaming room, ma storage room

SDF

T'es monté en compétence là  :D
Ca avance bien en tout cas.  ^-

Little_Rabbit

#22
Salut,

@Nene2k10 : oui, j'ai un peu progressé en Arduino :). Je serais bien incapable de pondre une bibliothèque, mais en exploiter des toutes faites me paraît plus clair à présent.

Comme je vous le disais la dernière fois, il me faut à présent tenter de rassembler la gestion du joystick analogique, et la gestion du spinner dans un seul programme.

Un source Arduino étant essentiellement constitué d'une fonction d'initialisation « setup », et une boucle infinie de fonctionnement « loop », il devrait me suffire de regrouper les deux initialisation et les deux boucles.

Il faut bien sûr aussi regrouper les déclarations de variables, constantes et objets, ainsi que les inclusions de fichiers .h

Mais avant de se lancer dans la fusion des deux sources, il convient de faire le bilan de l'exploitation des différentes broches du Leonardo. J'y avais un peu réfléchi avant de me mettre à la programmation du spinner, donc je n'ai aucun conflit ;). Voici le schéma à présent complet :



Et pour le source, après fusion des deux parties, cela donne ça :

#include "Encoder.h"
#include "Mouse.h"
#include "Joystick.h"

//à mettre en commentaire pour ne pas avoir les messages de debug sur le moniteur série
//#define DEBUG


#define MaxButtonQty 4
#define FirstButtonPinNumber 4
#define Frein A0
#define Accelerateur A1
#define AccelerateurEtalonnage 1.4


// Create Joystick
Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID, JOYSTICK_TYPE_JOYSTICK,
  MaxButtonQty, 0,       // 4 boutons, pas de Hat Switch
  true, true, false,     // Conserve un axe X et Y, mais les laisse en position neutre, pas d'axe Z
  false, false, false,   // Pas de Rx, Ry, ou Rz
  false, true,           // Pas de rudder, mais throttle en guise d'accélérateur
  false, true, false);    // seulement un frein, pas de volant (ni accélérateur car ne semble pas fonctionner...)

 
// Set to true to test "Auto Send" mode or false to test "Manual Send" mode.
//const bool testAutoSendMode = true;
const bool testAutoSendMode = false;


//#define ENCODER_USE_INTERRUPTS
#define ENCODER_OPTIMIZE_INTERRUPTS


// Change these pin numbers to the pins connected to your encoder.
//   Best Performance: both pins have interrupt capability
//   Good Performance: only the first pin has interrupt capability
//   Low Performance:  neither pin has interrupt capability
Encoder axeX(3, 2);
//   avoid using pins with LEDs attached


// Mettre 1 ou -1 selon le sens de déplacement souhaité pour le curseur de souris
const int SensRotationX = 1;

int range = 2;                    // output range of X movement; affects movement speed
const int responseDelay = 10;     // response delay of the mouse, in ms



void setup() {
  //**************************************************
  // Intialisation, partie spinner
  //************************************************** 
  // pour les messages de debug sur moniteur série
  #ifdef DEBUG
      Serial.begin(9600);
  #endif
 
  // initialize the buttons' inputs:
  // les broches 2 et 3 supportent les interruptions sur Leonardo
  pinMode(2, INPUT);
  pinMode(3, INPUT);
 
  // initialize mouse control:
  Mouse.begin();


  //**************************************************
  // Intialisation, partie joystick analogique
  //**************************************************
   
  // Set Range Values
  Joystick.setXAxisRange(-127, 127);
  Joystick.setYAxisRange(-127, 127);
  Joystick.setXAxis(0);
  Joystick.setYAxis(0);
 
   
  Joystick.setThrottleRange(0, 1023);
  Joystick.setThrottle(0);
  /*Joystick.setAcceleratorRange(0, 1023);
  Joystick.setAccelerator(0);*/
  Joystick.setBrakeRange(0, 1023);
  Joystick.setBrake(0);
 
 // Les boutons du panel sont attribués aux pin 4 à 7
  pinMode(FirstButtonPinNumber, INPUT_PULLUP);
  pinMode(FirstButtonPinNumber + 1, INPUT_PULLUP);
  pinMode(FirstButtonPinNumber + 2, INPUT_PULLUP);
  pinMode(FirstButtonPinNumber + 3, INPUT_PULLUP);

  // l'accélérateur et le frein sont associée aux entrées analogiques A0 et A1
  // A0 = frein
  // A1 = accélérateur
  // les axes X et Y ne sont que virtuels, en position neutre, donc pas d'entrées analogiques pour eux
  pinMode(A0, INPUT_PULLUP);
  pinMode(A1, INPUT_PULLUP);

  if (testAutoSendMode)
  {
    Joystick.begin();
  }
  else
  {
    Joystick.begin(false);
  }
 
  // pour pouvour faire clignoter la LED du Leonardo
  //pinMode(13, OUTPUT);
 
}

/******************************
 * Variables globales
 ******************************/

// État précédent des boutons
int lastButtonState[MaxButtonQty] = {0,0,0,0};
// Valeurs précédentes de l'accélérateur et du frein
int lastAcceleratorValue = 0;
int lastBrakeValue = 0;


long positionX  = -999;

#ifdef DEBUG
String DebugMsg = String(); // déclare et remet à 0 la chaîne utilisée pour les messages de debug
String Msg1 = String("Curseur X : ");
String Msg2 = String(" => distance X : ");
#endif

void loop() {

int index;
int currentButtonState;
int currentAnalogValue;
bool ButtonChange;
float CalibratedValue;

#ifdef DEBUG
String DebugMsg;
String Msg1;
String Msg2;
String Msg3;

  Msg1 = String("Gas     = ");
  Msg2 = String("Brake   = ");
  Msg3 = String("Buttons = ");
  // remet à 0 la chaîne utilisée pour les messages de debug
  DebugMsg = String();
#endif
 
  /***************************************
  * partie spinner de la boucle principale
  ****************************************/
 
  // read the axes:
  long newX = axeX.read() * SensRotationX;

  if (newX != positionX) {
   
    // calculate the movement distance based on the new position
    int  distanceX = (newX - positionX) * range;
   
    // if X is non-zero, move:
    if (distanceX != 0) {
      #ifdef DEBUG
        DebugMsg = Msg1 + positionX + Msg2 + distanceX;
        Serial.println(DebugMsg);
      #endif

      // seul l'axe des X évolue pour un spinner
      Mouse.move(distanceX, 0, 0);
    }
   
    positionX = newX;
  }

  /****************************************************
  * partie joystick analogique de la boucle principale
  *****************************************************/

  // Turn indicator light on.
  digitalWrite(13, 1);
 
  // lit les valeurs analogiques
  // l'accélérateur
  currentAnalogValue = analogRead(Accelerateur);
  if (currentAnalogValue != lastAcceleratorValue)
  {
    lastAcceleratorValue = currentAnalogValue;
    Joystick.setAccelerator (lastAcceleratorValue);
    CalibratedValue = (float) lastAcceleratorValue;
    CalibratedValue = CalibratedValue * AccelerateurEtalonnage;
    //Joystick.setThrottle (currentAnalogValue);
    Joystick.setThrottle ((int)CalibratedValue);
    #ifdef DEBUG
      DebugMsg = Msg1 + lastAcceleratorValue;
      Serial.println(DebugMsg);
    #endif
  }

  // puis le frein
  currentAnalogValue = analogRead(Frein);
  if (currentAnalogValue != lastBrakeValue)
  {
    lastBrakeValue = currentAnalogValue;
    Joystick.setBrake (currentAnalogValue);
    #ifdef DEBUG
      DebugMsg = Msg2 + lastBrakeValue;
      Serial.println(DebugMsg);
    #endif
  }
  //Joystick.setBrake (0);
 
  // Lit les boutons
  ButtonChange = false;
  for (index = 0; index < MaxButtonQty; index++)
  {
    currentButtonState = !digitalRead(index + FirstButtonPinNumber);
    if (currentButtonState != lastButtonState[index])
    {
      Joystick.setButton(index, currentButtonState);
      lastButtonState[index] = currentButtonState;
      ButtonChange = true;
    }
  }
  #ifdef DEBUG
    if (ButtonChange == true)
    {
      DebugMsg = Msg3 + lastButtonState[0] + " " + lastButtonState[1] + " " + lastButtonState[2] + " " + lastButtonState[3];
      Serial.println(DebugMsg);
    }
  #endif
   
  if (testAutoSendMode == false)
  {
    Joystick.sendState();
  }


  /*******************************************************************************************
   * Délais pour ne pas surcharger le PC avec sans cesse de nouveaux déplacements du curseur:
   *******************************************************************************************/
  delay(responseDelay);
}

Compilation, téléversement, et... le PC mouline un peu au 1er lancement ! Il met même pas mal de temps à reconnaître mon Leonardo comme un périphérique USB composite, mais il finit par y arriver !



On voit qu'il est bien reconnu à la fois comme une souris et un joystick analogique ! Les fois suivantes Windows le reconnaît quasiment instantanément.

Premier test : à partir du panneau de contrôle, la partie joystick analogique fonctionne normalement. Le curseur de la souris par contre se meut péniblement, et que dans un sens ! :-\ Je vérifie le câblage, le source, et ne vois pas d'erreur. J'essaye encore, et je me rends compte que c'est simplement le soleil qui vient parasiter le capteur infrarouge du coupleur ! :D J'interpose un livre mis debout pour faire écran, et ça fonctionne parfaitement : le curseur de la souris se déplace de façon fluide dans les deux sens ! ^-^

Un dernier point à adresser pour la gestion du panel, c'est le fonctionnement du levier de vitesse, ou « shifter ».

En effet, sur une borne Pole Position, le levier de vitesse est un simple interrupteur, pas un inverseur.

- interrupteur ouvert -> Gear Low
- interrupteur fermé -> Gear High
(ou l'inverse, mais peu importe)

Si vous avez déjà joué à Pole Position sur MAME, vous aurez remarqué qu'une touche sert à passer alternativement de Low à High. Cette touche, vous ne restez pas le doigt dessus tant que vous voulez rester en High, non, vous la relâcher sitôt la vitesse passé : c'est le mode standard de MAME, ou mode « toggle ». Pour Pole Position, il faut à l'inverse maintenir le contact tant qu'on veut rester en vitesse High.

Heureusement, les développeurs de MAME on introduit cette fonctionnalité.

C'est détaillé sur cette page.

Et c'est ce que nous montre cette très bonne vidéo :


(merci à Chelnov qui m'en a fourni le lien !)

Toutefois il faut que votre version de MAME ne soit pas trop ancienne : autrefois, les options de config étaient enregistrées dans un fichier binaire, impossible à modifier aisément. À présent, il s'agit d'un fichier XML éditable avec n'importe quel éditeur de texte.

En résumé, il faut changer le fichier config du jeu, et modifier la règle qui gère l'input du levier de vitesse, en ajoutant :

Toggle ="no"

Dans mon cas, cela donne la config suivante :

[tt]<port tag=":IN0L" type="P1_BUTTON1" mask="2" defvalue="2" value="0" toggle="no">
                <newseq type="standard">
                    JOYCODE_1_BUTTON1
                </newseq>
            </port>
[/tt]

Mais à présent que le Leonardo est censé regrouper la gestion du volant, de la pédale et des boutons du panel, comment tester ça dans de bonnes conditions ?

Le précédent panel était tout naze et partiel : il était temps de se retrousser les manches et de constituer un panel de test digne de ce nom !

J'ai dû faire appel à mes lointains souvenirs de construction mécanique, il y a près de 40 quand j'étais au lycée :) !

Veillons à avoir des solutions isostatiques et bannissons tout ce qui peut être hyperstatique ! Révisons nos jeux de fonctionnement : un ajustement H7g6 devrait convenir à l'axe du volant... des paliers en bronze seraient-ils bienvenus ? Un ajustement plus serré, de type H7h6 serait par contre préférable pour le levier de vitesse pour qu'il reste dans l'une des deux positions (et pour ce levier, j'avoue avoir été inspiré par le WIP de Spectro sur son Outrun Deluxe !...)... Et puis mettons-y un contacteur à micro-switch, car ma précédente solution à base d'interrupteur Legrand n'avait que trop duré !  Je n'ai pas été jusqu'à calculé mes chaînes de cotes, car mes capacités d'usinage ne permettent pas de toutes façons une précision suffisante...

Je vous laisse découvrir le résultat dans cette petite vidéo :









 =:))

Comment ça vous vous attendiez à autre chose ?? :D

Il est super mon panel ! ;) Pour l'anecdote, ce volant en Meccano, j'ai dû le faire il y a presque 30 ans, quand est sorti Vroom sur ATARI ST ! J'avais trouvé ce jeu tellement génial que j'avais fait un hack souris : coupée en deux, l'un des axe me servait sur ce volant, l'autre avait été mis à profit pour la pédale d'accélération, faite en Lego technique ! ^-^



Je savais bien que ce « volant » me resservirait un jour ! ;)



La pince à linge va définitivement détrôner la ficelle à rôti :D. En ces temps de confinement, il convient de faire avec ce qu'on a sous la main : ce curieux interrupteur m'a été donné par Chelnov dans un lot de vieux composants ! :)



L'ensemble du panel a été fait avec des trucs de récup divers et variés : j'ai mis un connecteur qui me permet de débrancher la pédale d'accélération, ce qui facilite le transport ou le déplacement de l'ensemble (là encore système débrouille : j'ai découpé et percé un vieux bout de circuit imprimé pour pouvoir y souder le connecteur mâle.

Quoiqu'il en soit, le tout fonctionne à merveille et je suis très content du résultat. Je reviendrai sur la configuration des boutons et pédale sous MAME, qui réserve quelques subtilités.

Va maintenant falloir que je m'attaque à MAME en 15 kHz : je n'y connais rien, cela ne va donc pas être une mince affaire !

A+
Recherche bornes dédiées ou PCB originaux: Miss Pacman, Dig Dug, Galaga, Mappy, Asteroids, Battlezone, Missile Command, Tempest, Star Wars, Donkey Kong (+ Jr), Mario Bros, Moon Patrol, Defender, Joust, Frogger, Gyruss, Pooyan, Space Tactics, Zaxxon, etc. Flip : Gottlieb des années 80 (Spirit, Amazon Hunt, ...), Baby Pac Man. Divers :  Ice Cold Beer => Trois fois rien quoi ! :D
Ma séance sur le divan : c'est grave Docteur ? :-\
Ma gaming room, ma storage room

-fab-

C'est pour ça que j'adore ce forum , ton banc de test est génial  ^-
Gottlieb Diamond Lady | Gottlieb Génésis | Williams Aces & Kings | Séga Blast City | Occulus Rift

Iro

le volant est une piéce de musée soviétique !!  =:))
C'est trés ingénieux !

Merci pour toutes ces infos !!
On attends ton retour sur le réglage dans MAME car il me semble en effet qu'il y a quelques réglages de précision
"Jet set 2, c'est avec Robert Garcia ?" Kaneda, Lapsus de sac Vol.1
Peter Shou Owner' Club

WIPs : Naomi - SEGA Rally - AB Cop - Lethal Enforcers - COMPUMI - Terminator 2 - Space Invaders - Artworks pour Boitiers K7 Naomi CF - Ma collec' de panels

LES TUTOS DE GAMO   

th_vador

je ne sais pas ce qui est le plus incroyable : le volant en mécano ou le fait de garder ce genre de bricolage dans un carton pendant 30 ans! Pourtant le nombre d'occasion de "faire de la place" n'a pas dû manquer... Chapeau! ^-^

Et la pince à linge... que dire à part que MacGyver n'a qu'à bien se tenir! 8)
De l'arcade, de la console, des conneries, du Giga Wing 2. TomTom's Lair quoi!
Recherche : Altered Beast (Master System) | Streets of Rage (Megadrive) | Une borne DDR | Un flipper

AsPiC

C'est... comment dire..?
Ah oui je sais : Digne de ce forum :-* =:))

Vieille_Loutre

Oui la c'est clair qu'on peut aller se rhabiller! la photo du volant a fini de me tuer, j'étais pas prêt pour la pince a linge derrière... chapeau!

Darth Nuno

Nice  8)

De mémoire il me semble qu'à l'époque de ma conversion polpo, mes APAC et autres analog devices avaient du mal à lire les info de l'opto de la carte originale... du coup j'ai trouvé sur un site arcade US une petite pcb plus moderne qui fait le taf farpaitement :D
Je n'ai plus ses références (c'était il y a + de 10 ans) mais la voilà montée :





Cela doit être encore trouvable...
 

Little_Rabbit

#29
Salut,

Ahh !! Je suis content de constater que mon panel de test Pole Position, comment dire... ne vous laisse pas indifférent ! :D

Citation de: th_vador le Vendredi 22 Mai 2020, 07:48:31 AMje ne sais pas ce qui est le plus incroyable : le volant en mécano ou le fait de garder ce genre de bricolage dans un carton pendant 30 ans! Pourtant le nombre d'occasion de "faire de la place" n'a pas dû manquer... Chapeau! ^-^

Ça c'est mon côté punk !...


:D

Bah disons que lorsqu'on est collectionneur compulsif comme moi, on a l'habitude d'entasser les cartons... En l'occurence, j'ai la chance d'avoir presque toujours eu un "local" pour y stocker tout mon bordel, en dehors de mon logement.

Autrefois, quand j'étais en région Parisienne :





(local de 60 m² situé à Chatou dans le 78, à présent vide et du reste à vendre, MP pour les intéressés ! ;)) :

Ou comme à présent où je squatte une partie de la réserve au bureau :


(il y en a autant de l'autre côté du rack... ce ne sont que des consoles et vieux ordi)

Alors t'imagine, un petit volant en Mecanno, ça ne prend pas de place ! :)

A+
Recherche bornes dédiées ou PCB originaux: Miss Pacman, Dig Dug, Galaga, Mappy, Asteroids, Battlezone, Missile Command, Tempest, Star Wars, Donkey Kong (+ Jr), Mario Bros, Moon Patrol, Defender, Joust, Frogger, Gyruss, Pooyan, Space Tactics, Zaxxon, etc. Flip : Gottlieb des années 80 (Spirit, Amazon Hunt, ...), Baby Pac Man. Divers :  Ice Cold Beer => Trois fois rien quoi ! :D
Ma séance sur le divan : c'est grave Docteur ? :-\
Ma gaming room, ma storage room

Vieille_Loutre

Tranquille , le gyromite big box en vrac comme si de rien n'étais !

kidicarus

Magnifique ce post que je découvre à l'instant , c'est top félicitations!! ^-
Je cherche une playchoice 10 et une pcb super system nintendo un playfield skateball ; evel knievel