Die Kombinierung eines Arduino Leonardo mit einer Joystick bibliothek und 2 bis 3 MCP23017

Hallo, ich habe für ein Projekt eine Grundsätzliche Frage.

Ich möchte mir mit einem Arduino Leonardo ein Controler aufbauen der über 2 Achsen und 32 Buttons verfügt.

Dazu will ich die Joysticklibary von MHeironimus verwenden.

Phase 1
Buttons und achsen direkt von den Pins an den PC zu senden ist gelungen. leider verfügt der Leonardo aber nur insgesammt 20 Pins.

Pase 2
Für die gewünschten 32 Buttons ist entweder ein Multiplexer oder eine Binärcodierung in der Pinverdrahrung nötig.

In beiden fällen muss ich den Buttonstatus für die setButton Funktion aus Variablen oder einer Arrey ablesen stat des einfachen digitalRead.

Leider weiß ich nicht wie ich das machen muss. (Also die Joystick.h dazu zu bringen die Variabelen einzulesen und an den PC zu senden, definieren kann ich sie schon)

schau dir das Breakoutboard vom SX1509 und die Library von Sparkfun an.
Das ist ein I2C IC mit dem du bis zu 64 Buttons aus der Matrix einlesen kannst.

https://learn.sparkfun.com/tutorials/sx1509-io-expander-breakout-hookup-guide

entweder original von Sparkfun oder von Aliexpress

ist imho einfacher, als mit MCPs selber zu multiplexen, wobei du wenn du mal die Suche bemühst, hier auch etwas finden würdest.

Oh sorry, ich habe meine frag falsch formuliert. ein paar multiplex sketches habe ich schon getestet.

Mein problem ist das verbinden vom multiplex mit der joystic.h
// Read pin values for (int index = 0; index < 14; index++) { int currentButtonState = !digitalRead(index + pinToButtonMap); if (currentButtonState != lastButtonState[index]) { Joystick.setButton(index, currentButtonState); lastButtonState[index] = currentButtonState; }

Diese Zeile funktioniert ja nur mit de ArduinoPins

int currentButtonState = !digitalRead(index + pinToButtonMap);

Das Du einen int benutzt wo Du nur einen bool brauchst...
Dann ist das mit dem zuweisen nicht viel anders.

Schau Dir das INPUT-Beispiel an.
https://wolles-elektronikkiste.de/portexpander-mcp23017

currentButtonState ist dann pinStatus - oder anders rum.

Ist halt schon in den Beispielen der Lib so ... :roll_eyes:

@tebraan : Zum einen könntest Du mal deinen Sketch zeigen ( oder hast Du bisher nur das Beispiel aus der Lib probiert/angeschaut?

Im Prinzip must Du deine Buttons über die Port-Extender z.B. in ein Array lesen, und dann beim Übergeben an die Joystick-Lib die Statuswerte aus dem Array nehmen, statt das digitalRead zu nutzen.

Das habe ich auch vermutet, ich weiß allerdings nicht wie ich das schreiben soll, also das array statt digital read.
Ich hab dafür nirgendwo etwas gefunden.
Nur wie ich alles mögliche in ein array einlese.

Zu den Scatches

#include "Joystick.h"

Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID,JOYSTICK_TYPE_GAMEPAD,
14, 0, // Button Count, Hat Switch Count
false, false, true, // X and Y, but no Z Axis
true, true, true, // Rx, Ry, or Rz
false, true, // rudder or throttle
true, false, false); // accelerator, brake, or steering

// declare variables
int axe1Value;
int axe2Value;
int axe3Value;
int axe4Value;
int axe5Value;
int axe6Value;


// init joystick libary
void setup() {
// Initialize Button Pins
pinMode(0, INPUT_PULLUP);
pinMode(1, INPUT_PULLUP);
pinMode(2, INPUT_PULLUP);
pinMode(3, INPUT_PULLUP);
pinMode(4, INPUT_PULLUP);
pinMode(5, INPUT_PULLUP);
pinMode(6, INPUT_PULLUP);
pinMode(7, INPUT_PULLUP);
pinMode(8, INPUT_PULLUP);
pinMode(9, INPUT_PULLUP);
pinMode(10, INPUT_PULLUP);
pinMode(11, INPUT_PULLUP);
pinMode(12, INPUT_PULLUP);
pinMode(13, INPUT_PULLUP);
pinMode(A0, INPUT_PULLUP);
pinMode(A1, INPUT_PULLUP);
pinMode(A2, INPUT_PULLUP);
pinMode(A3, INPUT_PULLUP);
pinMode(A4, INPUT_PULLUP);
pinMode(A5, INPUT_PULLUP);

// Initialize Joystick Library
Joystick.begin();
}

// Constant that maps the phyical pin to the joystick button.
const int pinToButtonMap = 0;

// Last state of the button
int lastButtonState[14] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0};

void loop() {
// Axe 1
axe1Value = analogRead(A0);
Joystick.setThrottle(axe1Value);
delay(1);

// Axe 2
axe2Value = analogRead(A1);
Joystick.setZAxis(axe2Value);
delay(1);

// Axe 3
axe3Value = analogRead(A2);
Joystick.setRxAxis(axe3Value);
delay(1);

// Axe 4
axe4Value = analogRead(A3);
Joystick.setRyAxis(axe4Value);
delay(1);

// Axe 5
axe5Value = analogRead(A4);
Joystick.setRzAxis(axe5Value);
delay(1);

// Axe 6
axe6Value = analogRead(A5);
Joystick.setAccelerator(axe6Value);
delay(1);


// Read pin values
for (int index = 0; index < 14; index++)
{
int currentButtonState = !digitalRead(index + pinToButtonMap);
if (currentButtonState != lastButtonState[index])
{
Joystick.setButton(index, currentButtonState);
lastButtonState[index] = currentButtonState;
}
}

delay(50);
}

Den habe ich getestet und er funktioniert. beim Spielen mit diesem sketch habe ich es auch hinbekommen die Achsen zu verringern und die buttons zu erhöhen.

Basierend auf einigen Beispielen hab ich mal ein gedankenspiel eingetipt.
2 mcp für 32 Buttons und 2 weitere um jede Buttonbetätigung durch LED anzeigen zu lassen.
ist natürlich noch nicht ganz fertig.

Da der scetch so keine Fehlermeldung ausgibt, vermute ich, dass man auch mit der indexzeile was machen kann um jeden mcp in eine solche for schleife zu bearbeiten.

Ist aber ehrlich gesagt nur geraten, da ich diesen abschnitt einfach nicht verstehe^^

#include <Wire.h>
#include <Adafruit_MCP23017.h>

#include "Joystick.h"

Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID,JOYSTICK_TYPE_GAMEPAD,
32, 0, // Button Count, Hat Switch Count
false, false, true, // X and Y, but no Z Axis
true, true, true, // Rx, Ry, or Rz
false, true, // rudder or throttle
true, false, false); // accelerator, brake, or steering

// declare variables
int axe1Value;
int axe2Value;
int axe3Value;
int axe4Value;
int axe5Value;
int axe6Value;

Adafruit_MCP23017 mcp1; // Create MCP 1
Adafruit_MCP23017 mcp2; // Create MCP 2
Adafruit_MCP23017 mcp3; // Create MCP 3
Adafruit_MCP23017 mcp4; // Create MCP 4

const uint8_t addr1 = 0; // Adresse 0x20 / 0
const uint8_t addr2 = 1; // Adresse 0x21 / 1
const uint8_t addr3 = 2; // Adresse 0x22 / 2
const uint8_t addr4 = 3; // Adresse 0x23 / 3

void setup() {  
  mcp1.begin(addr1);      // Start MCP 1
  mcp2.begin(addr2);      // Start MCP 2
  mcp3.begin(addr3);      // Start MCP 3
  mcp4.begin(addr4);      // Start MCP 4

//Definiere Inputs mit Pullup

  mcp1.pinMode(0, INPUT); // Define GPA0 on MCP1 as input
  mcp1.pullUp(0, HIGH);  // Activate Internal Pull-Up Resistor 
  mcp1.pinMode(1, INPUT); // Define GPA1 on MCP1 as input
  mcp1.pullUp(1, HIGH);  // Activate Internal Pull-Up Resistor
  mcp1.pinMode(2, INPUT); // Define GPA2 on MCP1 as input
  mcp1.pullUp(2, HIGH);  // Activate Internal Pull-Up Resistor 
  mcp1.pinMode(3, INPUT); // Define GPA3 on MCP1 as input
  mcp1.pullUp(3, HIGH);  // Activate Internal Pull-Up Resistor
  mcp1.pinMode(4, INPUT); // Define GPA4 on MCP1 as input
  mcp1.pullUp(4, HIGH);  // Activate Internal Pull-Up Resistor 
  mcp1.pinMode(5, INPUT); // Define GPA5 on MCP1 as input
  mcp1.pullUp(5, HIGH);  // Activate Internal Pull-Up Resistor
  mcp1.pinMode(6, INPUT); // Define GPA6 on MCP1 as input
  mcp1.pullUp(6, HIGH);  // Activate Internal Pull-Up Resistor 
  mcp1.pinMode(7, INPUT); // Define GPA7 on MCP1 as input
  mcp1.pullUp(7, HIGH);  // Activate Internal Pull-Up Resistor

  mcp1.pinMode(8, INPUT); // Define GPA0 on MCP1 as input
  mcp1.pullUp(8, HIGH);  // Activate Internal Pull-Up Resistor 
  mcp1.pinMode(9, INPUT); // Define GPA1 on MCP1 as input
  mcp1.pullUp(9, HIGH);  // Activate Internal Pull-Up Resistor
  mcp1.pinMode(10, INPUT); // Define GPA2 on MCP1 as input
  mcp1.pullUp(10, HIGH);  // Activate Internal Pull-Up Resistor 
  mcp1.pinMode(11, INPUT); // Define GPA3 on MCP1 as input
  mcp1.pullUp(11, HIGH);  // Activate Internal Pull-Up Resistor
  mcp1.pinMode(12, INPUT); // Define GPA4 on MCP1 as input
  mcp1.pullUp(12, HIGH);  // Activate Internal Pull-Up Resistor 
  mcp1.pinMode(13, INPUT); // Define GPA5 on MCP1 as input
  mcp1.pullUp(13, HIGH);  // Activate Internal Pull-Up Resistor
  mcp1.pinMode(14, INPUT); // Define GPA6 on MCP1 as input
  mcp1.pullUp(14, HIGH);  // Activate Internal Pull-Up Resistor 
  mcp1.pinMode(15, INPUT); // Define GPA7 on MCP1 as input
  mcp1.pullUp(15, HIGH);  // Activate Internal Pull-Up Resistor

  mcp2.pinMode(0, INPUT); // Define GPA0 on MCP2 as input
  mcp2.pullUp(0, HIGH);  // Activate Internal Pull-Up Resistor 
  mcp2.pinMode(1, INPUT); // Define GPA1 on MCP1 as input
  mcp2.pullUp(1, HIGH);  // Activate Internal Pull-Up Resistor
  mcp2.pinMode(2, INPUT); // Define GPA2 on MCP2 as input
  mcp2.pullUp(2, HIGH);  // Activate Internal Pull-Up Resistor 
  mcp2.pinMode(3, INPUT); // Define GPA3 on MCP2 as input
  mcp2.pullUp(3, HIGH);  // Activate Internal Pull-Up Resistor
  mcp2.pinMode(4, INPUT); // Define GPA4 on MCP2 as input
  mcp2.pullUp(4, HIGH);  // Activate Internal Pull-Up Resistor 
  mcp2.pinMode(5, INPUT); // Define GPA5 on MCP2 as input
  mcp2.pullUp(5, HIGH);  // Activate Internal Pull-Up Resistor
  mcp2.pinMode(6, INPUT); // Define GPA6 on MCP2 as input
  mcp2.pullUp(6, HIGH);  // Activate Internal Pull-Up Resistor 
  mcp2.pinMode(7, INPUT); // Define GPA7 on MCP2 as input
  mcp2.pullUp(7, HIGH);  // Activate Internal Pull-Up Resistor

  mcp2.pinMode(8, INPUT); // Define GPB0 on MCP1 as input
  mcp2.pullUp(8, HIGH);  // Activate Internal Pull-Up Resistor 
  mcp2.pinMode(9, INPUT); // Define GPB1 on MCP1 as input
  mcp2.pullUp(9, HIGH);  // Activate Internal Pull-Up Resistor
  mcp2.pinMode(10, INPUT); // Define GPB2 on MCP1 as input
  mcp2.pullUp(10, HIGH);  // Activate Internal Pull-Up Resistor 
  mcp2.pinMode(11, INPUT); // Define GPB3 on MCP1 as input
  mcp2.pullUp(11, HIGH);  // Activate Internal Pull-Up Resistor
  mcp2.pinMode(12, INPUT); // Define GPB4 on MCP1 as input
  mcp2.pullUp(12, HIGH);  // Activate Internal Pull-Up Resistor 
  mcp2.pinMode(13, INPUT); // Define GPB5 on MCP1 as input
  mcp2.pullUp(13, HIGH);  // Activate Internal Pull-Up Resistor
  mcp2.pinMode(14, INPUT); // Define GPB6 on MCP1 as input
  mcp2.pullUp(14, HIGH);  // Activate Internal Pull-Up Resistor 
  mcp2.pinMode(15, INPUT); // Define GPB7 on MCP1 as input
  mcp2.pullUp(15, HIGH);  // Activate Internal Pull-Up Resistor


//Definiere Outputs

   mcp3.pinMode(0, OUTPUT); // Define GPA0 on MCP3 as Output
   mcp3.pinMode(1, OUTPUT); // Define GPA1 on MCP3 as Output
   mcp3.pinMode(2, OUTPUT); // Define GPA0 on MCP3 as Output
   mcp3.pinMode(3, OUTPUT); // Define GPA1 on MCP3 as Output
   mcp3.pinMode(4, OUTPUT); // Define GPA0 on MCP3 as Output
   mcp3.pinMode(5, OUTPUT); // Define GPA1 on MCP3 as Output
   mcp3.pinMode(6, OUTPUT); // Define GPA0 on MCP3 as Output
   mcp3.pinMode(7, OUTPUT); // Define GPA1 on MCP3 as Output

   mcp3.pinMode(8, OUTPUT); // Define GPB0 on MCP3 as Output
   mcp3.pinMode(9, OUTPUT); // Define GPB0 on MCP3 as Output
   mcp3.pinMode(10, OUTPUT); // Define GPB0 on MCP3 as Output
   mcp3.pinMode(11, OUTPUT); // Define GPB0 on MCP3 as Output
   mcp3.pinMode(12, OUTPUT); // Define GPB0 on MCP3 as Output
   mcp3.pinMode(13, OUTPUT); // Define GPB0 on MCP3 as Output
   mcp3.pinMode(14, OUTPUT); // Define GPB0 on MCP3 as Output
   mcp3.pinMode(15, OUTPUT); // Define GPB0 on MCP3 as Output

      mcp3.pinMode(0, OUTPUT); // Define GPA0 on MCP3 as Output
   mcp4.pinMode(1, OUTPUT); // Define GPA1 on MCP3 as Output
   mcp4.pinMode(2, OUTPUT); // Define GPA0 on MCP3 as Output
   mcp4.pinMode(3, OUTPUT); // Define GPA1 on MCP3 as Output
   mcp4.pinMode(4, OUTPUT); // Define GPA0 on MCP3 as Output
   mcp4.pinMode(5, OUTPUT); // Define GPA1 on MCP3 as Output
   mcp4.pinMode(6, OUTPUT); // Define GPA0 on MCP3 as Output
   mcp4.pinMode(7, OUTPUT); // Define GPA1 on MCP3 as Output

   mcp4.pinMode(8, OUTPUT); // Define GPB0 on MCP3 as Output
   mcp4.pinMode(9, OUTPUT); // Define GPB0 on MCP3 as Output
   mcp4.pinMode(10, OUTPUT); // Define GPB0 on MCP3 as Output
   mcp4.pinMode(11, OUTPUT); // Define GPB0 on MCP3 as Output
   mcp4.pinMode(12, OUTPUT); // Define GPB0 on MCP3 as Output
   mcp4.pinMode(13, OUTPUT); // Define GPB0 on MCP3 as Output
   mcp4.pinMode(14, OUTPUT); // Define GPB0 on MCP3 as Output
   mcp4.pinMode(15, OUTPUT); // Define GPB0 on MCP3 as Output


   

// Initialize Joystick Library
Joystick.begin();
} // End Setup

// Constant that maps the phyical pin to the joystick button.
const int pinToButtonMap = 0;

// Last state of the button
int lastButtonState[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};


void loop() {
 // The LED on on MCP3u.4 will 'echo' the button on MCP3u.4

if (mcp1.digitalRead(0) == LOW)
  {
    mcp3.digitalWrite(0, HIGH); 
  }
  else
  {
    mcp3.digitalWrite(0, LOW);
  }
if (mcp1.digitalRead(1) == LOW)
  {
    mcp3.digitalWrite(1, HIGH); 
  }
  else
  {
    mcp3.digitalWrite(1, LOW);  
  }
if (mcp1.digitalRead(2) == LOW)
  {
    mcp3.digitalWrite(2, HIGH);
  }
  else
  {
    mcp3.digitalWrite(2, LOW);
  }
if (mcp1.digitalRead(3) == LOW)
  {
    mcp3.digitalWrite(3, HIGH);
  }
  else
  {
    mcp3.digitalWrite(3, LOW);
  }
if (mcp1.digitalRead(4) == LOW)
  {
    mcp3.digitalWrite(4, HIGH);
  }
  else
  {
    mcp3.digitalWrite(4, LOW);
  }
if (mcp1.digitalRead(5) == LOW)
  {
    mcp3.digitalWrite(5, HIGH);
  }
  else
  {
    mcp3.digitalWrite(5, LOW);
  } 

  //Joystick
  // Axe 1
axe1Value = analogRead(A0);
Joystick.setThrottle(axe1Value);
delay(1);

// Axe 2
axe2Value = analogRead(A1);
Joystick.setZAxis(axe2Value);
delay(1);

// Axe 3
axe3Value = analogRead(A2);
Joystick.setRxAxis(axe3Value);
delay(1);

// Axe 4
axe4Value = analogRead(A3);
Joystick.setRyAxis(axe4Value);
delay(1);

// Axe 5
axe5Value = analogRead(A4);
Joystick.setRzAxis(axe5Value);
delay(1);

// Axe 6
axe6Value = analogRead(A5);
Joystick.setAccelerator(axe6Value);
delay(1);


// Read pin values
for (int index = 0; index < 16; index++)
{
int currentButtonState = !mcp1.digitalRead(index + pinToButtonMap);
if (currentButtonState != lastButtonState[index])
{
Joystick.setButton(index, currentButtonState);
lastButtonState[index] = currentButtonState;
}
}
for (int index = 0; index < 16; index++)
{
int currentButtonState = !mcp2.digitalRead(index + pinToButtonMap);
if (currentButtonState != lastButtonState[index])
{
Joystick.setButton(index, currentButtonState);
lastButtonState[index] = currentButtonState;
}
}

delay(50);
}
 // End loop

Als nachsatz zu mir ist noch zusagen. Bin elektroniker und hatte vor sehr sehr langer zeit in meiner Ausbildung das Thema programmieren angeschnitten.
hatte vor inzwischen auch schon 10 jahren einen simatik kurs.
ist aber alles recht weit weg von C++ und arduino

was die Ausrüstung angeht ich habe hier einen Leonardo liegen aber noch keine MCP23017. wenn ich keinen Ansatz finde. kaufe ich mir lieber nen 2. Leonardo um meine kontrolpanels umzusetzen.

Einen Leonardo habe ich nicht und die Arduino Joystick Library von MHeironimus kenne ich nicht, daher sind meine Ausführungen rein theoretisch.

Bei der Joystick Library finde ich das Beispiel JoystickButton, und dort die Zeile

Joystick.setButton(index, currentButtonState);

Bei 32 Tasten müßte 'index' Werte von 0 bis 31 annehmen, also

Joystick.setButton(0, buttonState0);
...
Joystick.setButton(31, buttonState31);

Für den MCP23017 empfehle ich Dir die Adafruit_MCP23017_Arduino_Library. Die 32 Taster könntest Du an zwei ICs anschließen:

#include <Joystick.h>
Joystick_ Joystick;

#include <Wire.h>
#include <Adafruit_MCP23017.h>
Adafruit_MCP23017 mcp[2];

void mcpMode(const uint8_t pin, const uint8_t modus)
{
  uint8_t ic = pin / 16;
  if (modus == INPUT_PULLUP)
  {
    mcp[ic].pinMode(0x0F & pin, INPUT);
    mcp[ic].pullUp(0x0F & pin, HIGH);
  } else {
    mcp[ic].pinMode(0x0F & pin, modus);
  }
}

bool mcpRead(const uint8_t pin)
{
  return mcp[pin / 16].digitalRead(0x0F & pin);
}

void setup() {
  mcp[0].begin( );  // Adresse 0x20
  mcp[1].begin(1);  // Adresse 0x21
  for (byte pin = 0; pin < 32; pin++)
  {
    mcpMode(pin, INPUT_PULLUP);
  }
}

const byte ANZAHLTASTER = 32;
bool lastButtonState[ANZAHLTASTER] = {0};

void loop() {

  // Read pin values
  for (byte index = 0; index < ANZAHLTASTER; index++)
  {
    bool currentButtonState = mcpRead(index);
    if (currentButtonState != lastButtonState[index])
    {
      Joystick.setButton(index, currentButtonState);
      lastButtonState[index] = currentButtonState;
    }
  }

  delay(50);
}

Ohne Gewähr, ungetestet als Anregung.

Du könntest auch MCP23S17 für SPI in Erwägung ziehen.

Ich danke schon mal dafür, dann werde ich mal ein paar teile bestellen und testen.