[DIY] Spinner pour Batocera (ou Recalbox, Mister ?)

Vous voulez parler de système d'arcade, de borne d'arcade, de joystick, de hardware console. Vous voulez des infos sur un point technique, c'est ici. 8292
Message
Auteur
Avatar de l’utilisateur
Sebbeug
stick d'argent
Messages : 551
Inscription : 29 nov. 2017, 23:50

[DIY] Spinner pour Batocera (ou Recalbox, Mister ?)

#1 Message par Sebbeug »

Il y a quelques mois, j'avais acheté quelques composants pour faire mon propre spinner. J'avais trouvé un tuto sur le net et ça fonctionnait notamment sous Recalbox. J'avais pour idée de me faire un panel spécifique, puis je suis passé à autre chose.
Ce week-end, mon batocera ayant faim de nouveaux ajouts. Je décidais de me ressortir les composants et de finaliser une version qui fonctionnerait sous mon OS du moment et en y ajoutant quelques spec supplémentaires, comme le réglage de la vitesse à la volée. (car oui, entre puchi carat et arkanoid returns par exemple, les sensibilités par défaut ne sont pas les mêmes)

Donc, pour ce tuto (GyuGyu a dû faire une vidéo du même style), on ressort nos composants cheap :

- Arduino Pro Micro à 4 euros sur Aliexpress
- Rotary encoder 600 P/R à 8 euros (600 impulsions par tour)
- 2 boutons Sanwa qui trainaient
- Un petit passage sous mon imprimante 3D pour faire un boitier (source trouvée sur le net et adaptée)

Donc, je suis parti du code Arduino 3 boutons, et j'ai adapté pour simuler une souris 2 boutons, donc un spinner avec 2 boutons seulement.
J'ai ajouté la gestion de la vitesse avec 3 vitesses possibles : 1- Par défaut, 2- Plus lent, 3- Encore plus lent. Lorsqu'on reboote, il retient la dernière position.
Pour changer la vitesse en jeu, rien de plus simple, appui long sur les 3 boutons pendant 2 secondes et la LED d'activité du Pro Micro affiche la position.

Image

Dernière modification par Sebbeug le 13 janv. 2026, 23:47, modifié 1 fois.
WIP de ma TAITO Canary
https://sebbeug.fr/canary

Avatar de l’utilisateur
Sebbeug
stick d'argent
Messages : 551
Inscription : 29 nov. 2017, 23:50

Re: [DIY] Spinner pour Batocera (ou Recalbox, Mister ?)

#2 Message par Sebbeug »

1ère étape : Code Arduino

Installer Arduino IDE, sélectionnez votre Board, et voici le code Arduino à coller :

Code : Tout sélectionner

/*   Arcade Spinner for Batocera
*    
*    by Sebbeug - 2025
*          
*    based on spinner code from Craig B  - (craigbspinner@gmail.com)
*    
*    Features:
*    - 3 sensitivity levels (hold both buttons 2 sec to change)
*    - LED blinks to show current level (1, 2 or 3 blinks)
*    - Settings saved to EEPROM (persists after reboot)
*    
*    This program is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*    GNU General Public License for more details.
*
*    You should have received a copy of the GNU General Public License
*    along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

#include "Mouse.h"
#include <EEPROM.h>

/*   Batocera mod - Arduino IDE -> Tools, Get Board Info
*    VID: 2341 (Arduino)
*    PID: 8037 (Arduino Micro)
*/

// Port Bit/Pin layout   
#define xPD1 0b00000010 //Digital Pin 2  - Micro/PRO Micro - SDA, INT0
#define xPD0 0b00000001 //Digital Pin 3  - Micro/PRO Micro - SCL, INT1
#define xPD_10 (xPD1 | xPD0)
#define xPD4 0b00010000 //Digital Pin 4  - Micro/PRO Micro
#define xPD7 0b10000000 //Digital Pin 6  - Micro/PRO Micro
#define xPE6 0b01000000 //Digital Pin 7  - Micro/PRO Micro

#define pinA 2
#define pinB 3
#define maxBut 3
#define axisFlip 2

// Sensitivity settings
#define EEPROM_ADDR 0
#define LONG_PRESS_MS 2000
#define DEFAULT_SENSITIVITY 1

// Sensitivity levels: 1 = ÷4 (normal), 2 = ÷8 (slow), 3 = ÷16 (very slow)
volatile int sensitivityLevel = DEFAULT_SENSITIVITY;
volatile int rotPositionX = 0;
volatile int rotMultiX = 0;
volatile int prevQuadratureX = 0;

#ifdef axisFlip
volatile int xAxis = 1; 
volatile int yAxis = 0; 
int lastButtonState[maxBut] = {1,1,1};
#else
int lastButtonState[maxBut] = {1,1,0};
#endif

// Long press detection
unsigned long bothButtonsPressed = 0;
bool longPressTriggered = false;

void setup() {
  // Disable TX LED auto-activity (TXLED1 = off on Arduino Micro)
  TXLED1;
  
  // Setup ports
  PORTD = 0b10010011;
  PORTE = 0b01000000;

  // Load sensitivity from EEPROM
  int savedLevel = EEPROM.read(EEPROM_ADDR);
  if (savedLevel >= 1 && savedLevel <= 3) {
    sensitivityLevel = savedLevel;
  } else {
    sensitivityLevel = DEFAULT_SENSITIVITY;
    EEPROM.write(EEPROM_ADDR, sensitivityLevel);
  }

  // Setup interrupts
  attachInterrupt(digitalPinToInterrupt(pinA), pinChangeX, CHANGE); 
  attachInterrupt(digitalPinToInterrupt(pinB), pinChangeX, CHANGE);

  // Start mouse
  Mouse.begin();
  
  // Blink current level at startup
  delay(500);
  blinkLevel(sensitivityLevel);
}

void pinChangeX() {
  int currQuadratureX = PIND & xPD_10;
  int comboQuadratureX = (prevQuadratureX << 2) | currQuadratureX;

  if(comboQuadratureX == 0b0010 || comboQuadratureX == 0b1011 ||
     comboQuadratureX == 0b1101 || comboQuadratureX == 0b0100) {
    rotPositionX--;
  }

  if(comboQuadratureX == 0b0001 || comboQuadratureX == 0b0111 ||
     comboQuadratureX == 0b1110 || comboQuadratureX == 0b1000) {
    rotPositionX++;
  }

  prevQuadratureX = currQuadratureX;
}

void blinkLevel(int level) {
  for (int i = 0; i < level; i++) {
    TXLED0;  // ON
    delay(200);
    TXLED1;  // OFF
    delay(200);
  }
}

void changeSensitivity() {
  sensitivityLevel++;
  if (sensitivityLevel > 3) {
    sensitivityLevel = 1;
  }
  EEPROM.write(EEPROM_ADDR, sensitivityLevel);
  blinkLevel(sensitivityLevel);
}

int getThreshold() {
  switch (sensitivityLevel) {
    case 1: return 4;   // ÷4 (normal)
    case 2: return 8;   // ÷8 (slow)
    case 3: return 16;  // ÷16 (very slow)
    default: return 4;
  }
}

int getShift() {
  switch (sensitivityLevel) {
    case 1: return 2;  // >> 2 = ÷4
    case 2: return 3;  // >> 3 = ÷8
    case 3: return 4;  // >> 4 = ÷16
    default: return 2;
  }
}

void loop() { 
  int currentButtonState;
  int threshold = getThreshold();
  int shift = getShift();

  // Mouse movement with dynamic sensitivity
  if (rotPositionX >= threshold || rotPositionX <= -threshold) {
    rotMultiX = rotPositionX >> shift;
    #ifdef axisFlip
      int x = (xAxis) ? rotMultiX : 0;
      int y = (yAxis) ? rotMultiX : 0;
      Mouse.move(x, y, 0);
    #else 
      Mouse.move(rotMultiX, 0, 0);
    #endif 
    rotPositionX -= (rotMultiX << shift);
  }

  // Read button states
  int btn0 = (PIND & xPD4) >> 4;  // Left button
  int btn1 = (PIND & xPD7) >> 7;  // Right button
  
  // Long press detection (both buttons)
  if (btn0 == 0 && btn1 == 0) {
    if (bothButtonsPressed == 0) {
      bothButtonsPressed = millis();
      longPressTriggered = false;
    } else if (!longPressTriggered && (millis() - bothButtonsPressed >= LONG_PRESS_MS)) {
      changeSensitivity();
      longPressTriggered = true;
    }
  } else {
    bothButtonsPressed = 0;
    longPressTriggered = false;
  }

  // Button handling
  int button = 0;
  do {
    switch (button) {
      case 0:
        currentButtonState = btn0;
        break;
      case 1:
        currentButtonState = btn1;
        break;
      #ifdef axisFlip
      case 2:
        currentButtonState = (PINE & xPE6) >> 6;
        break;
      #endif
      default:
        currentButtonState = 0b00000000;
        break;
    }
    
    if (currentButtonState != lastButtonState[button]) {
      // Don't trigger click if long press was just triggered
      if (!longPressTriggered) {
        switch (button) {
          case 0:
            if (currentButtonState == 1) {
              Mouse.release(MOUSE_LEFT);
            } else {
              Mouse.press(MOUSE_LEFT);
            }        
            break;
          case 1:
            if (currentButtonState == 1) {
              Mouse.release(MOUSE_RIGHT);
            } else {
              Mouse.press(MOUSE_RIGHT);
            }        
            break;
          #ifdef axisFlip
          case 2:
            if (currentButtonState == 0) {
              xAxis = !xAxis;
              yAxis = !yAxis;
            } 
            break;
          #endif
        }
      }
    }

    lastButtonState[button] = currentButtonState;
    ++button;
  } while (button < maxBut);

  // Force TX LED off (USB activity turns it on automatically)
  TXLED1;
}

2ème étape, on soude tout, c'est classique, c'est comme sur tous les autres tutos avec ces composants, rien de nouveau :

ROTARY Encoder -> Pro Micro :
Fil noir -> GND
Fil rouge -> VCC
Fil blanc -> D2
Fil vert -> D3

Bouton Sanwa 1 -> D4
Bouton Sanwa 2 -> D6
Et bien entendu, le GND en chaine
WIP de ma TAITO Canary
https://sebbeug.fr/canary

Avatar de l’utilisateur
Sebbeug
stick d'argent
Messages : 551
Inscription : 29 nov. 2017, 23:50

Re: [DIY] Spinner pour Batocera (ou Recalbox, Mister ?)

#3 Message par Sebbeug »

3ème étape : Impression 3D

Donc pour un boitier 2 boutons :

Image

>> Fichiers STL pour impression
WIP de ma TAITO Canary
https://sebbeug.fr/canary

Avatar de l’utilisateur
Sebbeug
stick d'argent
Messages : 551
Inscription : 29 nov. 2017, 23:50

Re: [DIY] Spinner pour Batocera (ou Recalbox, Mister ?)

#4 Message par Sebbeug »

Finalement, le plus cher dans ce délire, c'est le bouton (knob) en 44x22x6 que j'ai pris en alu, pour une belle finition et un bon grip (et sans inscription)... 10 balles ici
WIP de ma TAITO Canary
https://sebbeug.fr/canary

Avatar de l’utilisateur
Sebbeug
stick d'argent
Messages : 551
Inscription : 29 nov. 2017, 23:50

Re: [DIY] Spinner pour Batocera (ou Recalbox, Mister ?)

#5 Message par Sebbeug »

Oups, j'ai oublié le principal pour Batocera (car par défaut, il n'est pas reconnu), j'ai ce script qui s'execute au lancement d'un jeu, c'est pas dingue comme méthode, mais ça marche.

A coller ici : /userdata/system/configs/emulationstation/scripts/game-start/

Fichier spinner.sh à coller dans le rep ci-dessus :

Code : Tout sélectionner

#!/bin/bash

# Tuer l'ancien evsieve s'il existe
pkill -f "Arduino Spinner Mouse" 2>/dev/null

# Trouver et configurer l'Arduino
for f in /sys/class/input/event*/device/name; do
    if grep -q "Arduino LLC Arduino Micro" "$f" 2>/dev/null; then
        EVENT=$(echo "$f" | sed 's|.*/\(event[0-9]*\)/.*|\1|')
        evsieve --input /dev/input/$EVENT grab --output name="Arduino Spinner Mouse" &
        break
    fi
done
Enfin, pour Puchi Carat par exemple, lancez le via Mame et dans la config du jeu (sous emulationstation), activez la souris !
Dernière modification par Sebbeug le 14 janv. 2026, 18:29, modifié 3 fois.
WIP de ma TAITO Canary
https://sebbeug.fr/canary

Avatar de l’utilisateur
ZeV
stick d'argent
Messages : 592
Inscription : 28 mars 2018, 14:16
Localisation : Alsace

Re: [DIY] Spinner pour Batocera (ou Recalbox, Mister ?)

#6 Message par ZeV »

Merci pour ce tuto Sebbeug 👍
Image
Sans tabouret, cendrier ni carton SEGA... Mais avec Batocera !!
Et un lave-linge Electrolux 10Kg 1600 trs/min...

Avatar de l’utilisateur
wildpumpk1n
stick de zinc
Messages : 272
Inscription : 21 sept. 2019, 11:03
Localisation : Paris

Re: [DIY] Spinner pour Batocera (ou Recalbox, Mister ?)

#7 Message par wildpumpk1n »

Merci pour le partage :)

Auber1083
stick de zinc
Messages : 365
Inscription : 27 nov. 2016, 13:05
Localisation : 57

Re: [DIY] Spinner pour Batocera (ou Recalbox, Mister ?)

#8 Message par Auber1083 »

Très intéressant ce bricolage et le résultat est très propre.
Ce genre de trucs me permettrait de doser le training sur Arkanoid avec save state.

Avatar de l’utilisateur
Romano
Lèche moi le stick
Messages : 2758
Inscription : 10 févr. 2009, 01:13
Localisation : Metz / Lux

Re: [DIY] Spinner pour Batocera (ou Recalbox, Mister ?)

#9 Message par Romano »

Merci !!
D’abord on teste, après on cause

Ay0r
stick de zinc
Messages : 304
Inscription : 12 févr. 2017, 18:23

Re: [DIY] Spinner pour Batocera (ou Recalbox, Mister ?)

#10 Message par Ay0r »

Nickel ce petit post. Je me disais justement qu'un spinner pour les qq jeux qui l'utilisent serait pas mal.
Si je veux le mettre sur rb, dual ou jamma, y'a quelque chose de particulier à savoir ?

Merci pour ce partage en tout cas. :p:

Ay0r
stick de zinc
Messages : 304
Inscription : 12 févr. 2017, 18:23

Re: [DIY] Spinner pour Batocera (ou Recalbox, Mister ?)

#11 Message par Ay0r »

Salut,

Quand je veux televerser le code j'ai une tonne de message d'erreur et ça finit par txled n'est pas déclaré...
Y'a t il quelque chose qui m'échappe ?

Édit : j'avais choisi la mauvaise version d'arduino. En prenant arduino Leonardo, ça passe tranquille. Plus qu'à recevoir la partie spinner et c'est tipar.

Ay0r
stick de zinc
Messages : 304
Inscription : 12 févr. 2017, 18:23

Re: [DIY] Spinner pour Batocera (ou Recalbox, Mister ?)

#12 Message par Ay0r »

Resalut,

Petite question pour la fixation de l'arduino, tu as fait comment ? Parce que là à part mettre une calme en mousse collée, je ne vois pas trop pour que ça ne bouge pas.

Merci par avance.

Avatar de l’utilisateur
Sebbeug
stick d'argent
Messages : 551
Inscription : 29 nov. 2017, 23:50

Re: [DIY] Spinner pour Batocera (ou Recalbox, Mister ?)

#13 Message par Sebbeug »

J'ai mis de la colle chaude...
WIP de ma TAITO Canary
https://sebbeug.fr/canary

Ay0r
stick de zinc
Messages : 304
Inscription : 12 févr. 2017, 18:23

Re: [DIY] Spinner pour Batocera (ou Recalbox, Mister ?)

#14 Message par Ay0r »

Hello,

Alors j'ai tout fait tout ce qu'il faut niveau soudure et tout et tout.
Quand je reste appuyé sur les deux boutons, y'a bien le changement qui se fait via le clignotement de la Led.

Les boutons sont reconnus sous rb par contre la spinner, walou :lol: j'ai pas encore essayer de mettre le sh cependant.

Edit : après pas mal de tests, ça viendrait ptet de la carte arduino. En fait quand je tourne le spinner il ne se passe rien mais quand je joue avec les fils de contact sur les pins ça bouge tout seul. Y'a quelque chose qui couille entre l'arduino et le rotary encoder...

Ay0r
stick de zinc
Messages : 304
Inscription : 12 févr. 2017, 18:23

Re: [DIY] Spinner pour Batocera (ou Recalbox, Mister ?)

#15 Message par Ay0r »

Re,

Est-ce que tu pourrais donner le modèle de rotary que tu as pris ? Comme ça je peux comparer avec celui que j'ai pris.

Avatar de l’utilisateur
Sebbeug
stick d'argent
Messages : 551
Inscription : 29 nov. 2017, 23:50

Re: [DIY] Spinner pour Batocera (ou Recalbox, Mister ?)

#16 Message par Sebbeug »

Désolé pour le délai,

voici la ref des miens :

MODEL :
E38S6G5-600B-G24N
WIP de ma TAITO Canary
https://sebbeug.fr/canary

Avatar de l’utilisateur
Sebbeug
stick d'argent
Messages : 551
Inscription : 29 nov. 2017, 23:50

Re: [DIY] Spinner pour Batocera (ou Recalbox, Mister ?)

#17 Message par Sebbeug »

Par contre, j'y pense...
Essaye sous Mame... Parce qu'avec FBNeo, ça ne fonctionne pas en plug'n play.
WIP de ma TAITO Canary
https://sebbeug.fr/canary

Ay0r
stick de zinc
Messages : 304
Inscription : 12 févr. 2017, 18:23

Re: [DIY] Spinner pour Batocera (ou Recalbox, Mister ?)

#18 Message par Ay0r »

Sebbeug a écrit : 19 mars 2026, 11:03 Par contre, j'y pense...
Essaye sous Mame... Parce qu'avec FBNeo, ça ne fonctionne pas en plug'n play.
Pas de souci pour le délai de réponse.
Je vais tester sous mame. Sous FBNeo, les boutons sont bien reconnus et dans rb direct aussi. J'ai plus l'impression que le problème vient de la comm entre le rotary et l'arduino.

Je viens de regarder et j'ai exactement la même référence que toi. Je vais refaire mes connexions sur l'arduino pour check mais en tout cas j'ai le même matos que ce que tu as pris donc ça me rassure.

Mkazoo
stick de plastique
Messages : 6
Inscription : 02 févr. 2014, 12:17

Re: [DIY] Spinner pour Batocera (ou Recalbox, Mister ?)

#19 Message par Mkazoo »

Bonjour,
Je suis en train d'essayer de faire un spinner pour recalbox mais je ne sais pas si je peux utiliser ce type d'arduino : https://amzn.eu/d/09C3tpdp
Et autre question, au niveau de la soudure, je ne sais pas quoi faire de la tresse qui entoure les câbles du rotary. Je le met à la masse ou je n'en fait rien? Merci

https://ibb.co/TM80dhR3

Avatar de l’utilisateur
Sebbeug
stick d'argent
Messages : 551
Inscription : 29 nov. 2017, 23:50

Re: [DIY] Spinner pour Batocera (ou Recalbox, Mister ?)

#20 Message par Sebbeug »

Je ne suis pas certain que ton arduino soit 100% compatible, il faudra sûrement ajuster le code (un IA peut te faire ça rapidos).

Concernant la tresse, tu coupes à ras, pas besoin de raccorder.
WIP de ma TAITO Canary
https://sebbeug.fr/canary

Mkazoo
stick de plastique
Messages : 6
Inscription : 02 févr. 2014, 12:17

Re: [DIY] Spinner pour Batocera (ou Recalbox, Mister ?)

#21 Message par Mkazoo »

Merci pour ta réponse!

Mkazoo
stick de plastique
Messages : 6
Inscription : 02 févr. 2014, 12:17

Re: [DIY] Spinner pour Batocera (ou Recalbox, Mister ?)

#22 Message par Mkazoo »

J'ai un souci car je n"ai pas utilisé les mêmes pin que toi pour les sourdures (pinA 2 mais pinB 5 et pour les boutons, c'est sur 8 et 9) mais je ne sais pas comment le changer dans ton code.
j'imagine que c'est ici :
#define xPD1 0b00000010 //Digital Pin 2 - Micro/PRO Micro - SDA, INT0
#define xPD0 0b00000001 //Digital Pin 3 - Micro/PRO Micro - SCL, INT1
#define xPD_10 (xPD1 | xPD0)
#define xPD4 0b00010000 //Digital Pin 4 - Micro/PRO Micro
#define xPD7 0b10000000 //Digital Pin 6 - Micro/PRO Micro
#define xPE6 0b01000000 //Digital Pin 7 - Micro/PRO Micro

mais je ne sais par quoi le changer.
merci