Télécommande intelligente

De Wiki LOGre
Aller à : navigation, rechercher
Langue : Français  • English

Projet réalisé par fma38.

Abandonné

Présentation

Réalisation d'une télécommande intelligente pour piloter un hexapode, à base de joysticks.

L'idée est de faire un système où tout peut être fait sans enlever les mains des joysticks.

La partie électronique se fait via une carte Teensy++ (plein d'I/O, émulation clavier/souris/joystick si besoin...)

Cahier des charges

  • 2 joysticks, chacun ayant :
    • 3 voies analogiques
    • 1 hat 4 directions
    • 4 boutons
  • autres commandes (boutons, switchs, potars...) si besoin
  • retour d'info :
    • leds
    • écran lcd
    • vibreur
    • visu caméra (FPV)
  • communication BT

Réalisation

Cette télécommande est basée sur l'utilisation de 2 manches de joystick Saitek Cyborg Digital 3D. À l'origine, ce joystick se branchait sur un port joystick/midi (sub-D 15) ; une version série puis usb a été fabriquée par la suite. Pour ce projet, n'importe quelle version peut être utilisée, mais vu que l'électronique ne sera pas utilisée, autant acheter la version la moins chère. En fait, n'importe quel joystick peut être utilisé, à condition qu'il ne soit pas à lecture optique, comme les Sidewinder de Microsoft.

À noter que le Cyborg peut être configuré en gaucher ; c'est indispensable ici !

Saitek cybrog digital.jpg

Seule la partie joystick est conservée. On commence par couper les fils qui sortent du manche lui-même en les gardant les plus longs possible :

IMG 20160301 174538.jpg

Puis on coupe la base au niveau du cylindre et on fixe les 2 manches sur une planche à l'aide d'une bride imprimée :

IMG 20160301 174654.jpg IMG 20160223 122655.jpg

La connectique vers le Teensy++ :

IMG 20160301 180827.jpg IMG 20160301 180516.jpg

Câblage

Tableau de correspondance des fils du joystick :

rouge : ground potar RZ
      : signal potar RZ
      : ref.   potar RZ

noir  : button common
      : hat N
      : hat S
      : hat E
      : hat W
      : button left
      : button middle
      : button right
      : button trigger

Câblage retenu sur le Teensy++, incluant le pilotage futur d'un écran LCD graphique type xxx :

A0 :  X 0
A1 :  Y 0
A2 : RZ 0
A3 :  X 1
A4 :  Y 1
A5 : RZ 1
A6 : free
A7 : free

 0 : free
 1 : free
 2 : RX BT
 3 : TX BT
 4 : free
 5 : free
 6 : free (led)
 7 : free
 8 : CE LCD
 9 : CS LCD
10 : hat N 0
11 : hat S 0
12 : hat E 0
13 : hat W 0
14 : button left 0
15 : button middle 0
16 : button right 0
17 : button trigger 0
18 : RD LCD
19 : WR LCD
20 : hat N 1
21 : hat S 1
22 : hat E 1
23 : hat W 1
24 : button left 1
25 : button middle 1
26 : button right 1
27 : button trigger 1
28 : D0 LCD
29 : D1 LCD
30 : D2 LCD
31 : D3 LCD
32 : D4 LCD
33 : D5 LCD
34 : D6 LCD
35 : D7 LCD

Soft

Exemple de code émulant 1 joystick USB avec 6 axes analogiques et 16 boutons :

#include <Bounce2.h>
 
const uint8_t DEBOUNCE_DELAY = 10; // 10ms is appropriate for most mechanical pushbuttons
 
const uint8_t AXIS[] = {
    0,   // X,  joystick right - mapped to ABS_X        (0x00)
    1,   // Y,  joystick right - mapped to ABS_Y        (0x01)
    2,   // RZ, joystick right - mapped to ABS_Z        (0x02)
    3,   // X,  joystick left  - mapped to ABS_THROTTLE (0x06)
    4,   // Y,  joystick left  - mapped to ABS_RUDDER   (0x07)
    5    // RZ, joystick left  - mapped to ABS_RZ       (0x05)
};
 
const uint8_t BUTTONS[] = {
 
    // Joystick right
    10,   // hat N   - mapped to BTN_JOYSTICK/BTN_TRIGGER    (0x120)
    11,   // hat S   - mapped to BTN_THUMB                   (0x121)
    12,   // hat E   - mapped to BTN_THUMB2                  (0x122)
    13,   // hat W   - mapped to BTN_TOP                     (0x123)
    14,   // left    - mapped to BTN_TOP2                    (0x124)
    15,   // middle  - mapped to BTN_PINKIE                  (0x125)
    16,   // right   - mapped to BTN_BASE                    (0x126)
    17,   // trigger - mapped to BTN_BASE2                   (0x127)
 
    // Joystick left
    20,   // hat N   - mapped to BTN_BASE3                   (0x128)
    21,   // hat S   - mapped to BTN_BASE4                   (0x129)
    22,   // hat E   - mapped to BTN_BASE5                   (0x12a)
    23,   // hat W   - mapped to BTN_BASE6                   (0x12b)
    24,   // left    - mapped to BTN_DEAD                    (0x12f)
    25,   // middle  - mapped to BTN_GAMEPAD/BTN_SOUTH/BTN_A (0x130)
    26,   // right   - mapped to BTN_EAST/BTN_B              (0x131)
    27,   // trigger - mapped to BTN_C                       (0x132)
};
 
const uint8_t SAMPLES = 10;
uint8_t rawAxis[6][SAMPLES];
uint16_t axis[6];
uint8_t prevAxis[6];
uint8_t buttons[32], prevButtons[32];
uint8_t index = 0;
 
Bounce *bouncer;
 
 
void setup()
{
    // Init Joystick
    Joystick.useManualSend(true);
 
    // Init axis readings
    for (uint8_t i=0; i<sizeof(AXIS); i++) {
        axis[i] = 0;
        prevAxis[i] = 0;
        for (uint8_t j=0; j<SAMPLES; j++) {
            rawAxis[i][j] = 0;
        }
    }
 
    bouncer = (Bounce *)malloc(sizeof(BUTTONS) * sizeof(Bounce));
    for (uint8_t i=0; i<sizeof(BUTTONS); i++) {
 
        // Init digital inputs (buttons)
        pinMode(BUTTONS[i], INPUT_PULLUP);
        buttons[i] = 0;
        prevButtons[i] = 0;
 
        // Create bounce objects
        bouncer[i] = Bounce();
        bouncer[i].attach(BUTTONS[i]);
        bouncer[i].interval(DEBOUNCE_DELAY);
    }
}
 
 
void loop()
{
 
    // Read up to 6 analog inputs and use them as axis
    for (uint8_t i=0; i<sizeof(AXIS); i++) {
        axis[i] = axis[i] - rawAxis[i][index];
        rawAxis[i][index] = analogRead(AXIS[i]) >> 2;  // remove noisy bits
        axis[i] = axis[i] + rawAxis[i][index];
 
        switch (i) {
            case 0:
                Joystick.X(axis[i] / SAMPLES);
                break;
 
            case 1:
                Joystick.Y(axis[i] / SAMPLES);
                break;
 
            case 2:
                Joystick.Z(axis[i] / SAMPLES);
                break;
 
            case 3:
                Joystick.sliderLeft(axis[i] / SAMPLES);
                break;
 
            case 4:
                Joystick.sliderRight(axis[i] / SAMPLES);
                break;
 
            case 5:
                Joystick.Zrotate(axis[i] / SAMPLES);
                break;
 
            default:
                break;
        }
    }
    index++;
    if (index >= SAMPLES) {
        index = 0;
    }
 
    // Read up to 32 digital pins and use them as buttons
    uint8_t index = 1;
    for (uint8_t i=0; i<sizeof(BUTTONS); i++) {
 
        // Update buttons
        bouncer[i].update();
        if (bouncer[i].read()) {
            buttons[i] = 0;
        }
        else {
            buttons[i] = 1;
        }
        Joystick.button(index++, buttons[i]);
    }
 
    // Complete USB joystick buttons (up to 32)
    while (index <= 32) {
        Joystick.button(index++, 0);
    }
 
    // check to see if any axis/button changed since last time
    boolean anyChange = false;
    for (uint8_t i=0; i<sizeof(AXIS); i++) {
        if (axis[i] != prevAxis[i]) {
            anyChange = true;
        }
        prevAxis[i] = axis[i];
    }
    for (uint8_t i=0; i<sizeof(BUTTONS); i++) {
        if (buttons[i] != prevButtons[i]) {
            anyChange = true;
        }
        prevButtons[i] = buttons[i];
    }
 
    // If any axis/button changed, update USB/BT
    if (anyChange) {
 
        // Send datas to USB joystick channel
        Joystick.send_now();
}