Midi Controller Encoder an IO Expander und OLED Display

Hallo miteinander,

ich möchte mir einen Midi Controller bauen, und habe dafür mehrere Rotary Encoder hier, einen PCF8574A Expander mit Breakoutboard, und ein kleines OLED SSD1306 Display.
Leider beschränken sich meine Programmierfähigkeiten auf ein absolutes Anfängerniveau, und ich versuche mir deshalb aus verschiedenen Bibliotheken und Codeschnipseln bedarfsmäßig zu behelfen.

Hier ein Code aus Control Surface Library mit Hilfe ich ohne Expander einen Encoder ansteuern konnte:

/** 
 * This is an example that demonstrates the use of a rotary encoder that can 
 * be used for scrolling through a song list, tuning effects, etc.
 *
 * ### Connections
 * 
 *   - 2: pin A of the rotary encoder
 *   - 3: pin B of the rotary encoder
 * 
 * The internal pull-up resistors will be enabled automatically.  
 * Connect the common (C) pin of the rotary encoder to ground.
 * 
 * ### Behavior
 * 
 * If the encoder is rotated clockwise, a MIDI CC increment message is sent, 
 * if the encoder is rotated counter-clockwise, a MIDI CC decrement message is 
 * sent.  
 * The controller number `MCU::V_POT_1` will be used.  
 * 
 * To change the encoding of the MIDI CC increment and decrement messages, use
 * `RelativeCCSender::setMode`.
 * 
 * Map the Arduino as a Mackie Control Universal (MCU) or equivalent in your
 * audio software.
 * 
 * Written by PieterP, 2019-02-16  
 * https://github.com/tttapa/Control-Surface
 */
#include <Encoder.h> // Include the encoder library
// This has to be done before including the Control Surface library
#include <Control_Surface.h> // Include the Control Surface library

// Instantiate a MIDI over USB interface.
USBMIDI_Interface midi;

// Instantiate a CCRotaryEncoder object that reads a rotary encoder,
// connected to pins 2 and 3.
// The CC controller is MCU::V_POT_1, the multiplier is 1,
// and the encoder sends out 4 pulses per step/click.
CCRotaryEncoder enc = {
    {2, 3},
    MCU::V_POT_1,
    1,
    4,
};

void setup() {
    // Use the Mackie Control protocol for sending relative MIDI CC messages.
    RelativeCCSender::setMode(MACKIE_CONTROL_RELATIVE);
    Control_Surface.begin(); // Initialize Control Surface
}

void loop() {
    // Update the control surface
    Control_Surface.loop();
}

Hier ein Code mit welchem ich erfolgreich mit Expander 2 Glühbirnen blinken ließ:

#include "Arduino.h"
#include <Wire.h>

void setup()
{
  // Serial Window (debugging)
  Serial.begin(9600);

  // I2C Two Wire initialisation
  Wire.begin();

  // PCF8574N is 'reverse' logic inasmuch it SINKS current
  // so HIGH is OFF and LOW is ON (will we remember this?)
  // Turn OFF all pins by sending a high byte (1 bit per byte)
  Wire.beginTransmission(0x027);
  Wire.write(0xF);
  Wire.endTransmission();
}


void loop()
{
  // Addressing the PCF8574N via I2C is easy:
  /*
   *    I 7 6 5 4 3 2 1 0
   *    | | | | | | | | |
   *    | P7              P0
   *      Interrupt
   *
   *     So to make P0 HIGH we set the bit to LOW (yes, confusing)
   *     eg 0b1111110 where 0b means we're specify a binary value
   */

  //Simple LED blink! Turn ON P0 bit by setting LOW (to zero)
  Wire.beginTransmission(0x027);
  Wire.write(0b11111110);
  Wire.endTransmission();

  delay (500);


  Wire.beginTransmission(0x038);
  Wire.write(0b11111101);
  Wire.endTransmission();

  delay (500);
}

Jetzt würde ich gerne verstehen, wie ich diese beiden Codes zusammenbringe.

Erste versuche haben leider über nicht so richtig funktioniert..

#include "Arduino.h"
#include <Wire.h>

#include <Encoder.h> // Include the encoder library
// This has to be done before including the Control Surface library
#include <Control_Surface.h> // Include the Control Surface library
// Instantiate a MIDI over USB interface.
USBMIDI_Interface midi;

// Instantiate a CCRotaryEncoder object that reads a rotary encoder,
// connected to pins 2 and 3.
// The CC controller is MCU::V_POT_1, the multiplier is 1,
// and the encoder sends out 4 pulses per step/click.
CCRotaryEncoder enc = {
    {0b11111110, 0b11111101},
    MCU::V_POT_1,
  1,
  4, 
};

void setup()
{
  // Serial Window (debugging)
  Serial.begin(9600);

  // I2C Two Wire initialisation
  Wire.begin();

  // PCF8574N is 'reverse' logic inasmuch it SINKS current
  // so HIGH is OFF and LOW is ON (will we remember this?)
  // Turn OFF all pins by sending a high byte (1 bit per byte)
  Wire.beginTransmission(0x038);
  Wire.write(0xF);
  Wire.endTransmission();

  Wire.requestFrom(0b11111110, 0b11111101);    // request 6 bytes from slave device #2
  
  while(Wire.available())    // slave may send less than requested
  {
    char c = Wire.read();    // receive a byte as character
    Serial.print(c);         // print the character
  }

  delay(500);

      // Use the Mackie Control protocol for sending relative MIDI CC messages.
    RelativeCCSender::setMode(MACKIE_CONTROL_RELATIVE);
    Control_Surface.begin(); // Initialize Control Surface
}


void loop()
{

    // Update the control surface
    Control_Surface.loop();
}

Freue mich über jede hilfreiche Unterstützung, bzw einen Ratschlag, falls ein ähnliches Projekt schon irgendwo realisiert wurde.

Danke und viele Grüße raus!

Und warum zeigst du uns nicht deine Versuche ?
Da könnte man doch drauf aufbauen.

HotSystems:
Und warum zeigst du uns nicht deine Versuche ?
Da könnte man doch drauf aufbauen.

Das wäre dann der dritte Code, den hier gepostet habe :slight_smile:
Quasi ein Gemisch aus den ersten beiden.

Echt Niemand, nichtmal einen Tip?

Na gut, ein Tip:

  Wire.requestFrom(0b11111110, 0b11111101);    // request 6 bytes from slave device #2

Da verstehe ich nicht

  • wie Code und Kommentar zusammenpassen sollen
  • warum du das nur einmal in setup machst
  • was das mit deinen beiden vorigen Einzel-Versuchen zu tun hat

michael_x:
Na gut, ein Tip:

Da verstehe ich nicht

  • wie Code und Kommentar zusammenpassen sollen
  • warum du das nur einmal in setup machst
  • was das mit deinen beiden vorigen Einzel-Versuchen zu tun hat

Passt nicht, ja. Hab die bits ersetzt und an die Anzahl der in/outs des Expanders angepasst.
Wie oft soll ich das denn im Setup machen?

Also Ziel war es die beiden Versuche zusammen zu bringen. Der erste spricht den Expander an, der zweite den rotary encoder. Dachte ich verknüpfe die irgendwie miteinander.

Der erste spricht den Expander an, der zweite den rotary encoder

Und was haben die mit "Midi Controller ... und OLED Display" zu tun?

Deine Frage ist vermutlich, wie du den Encoder über einen I2C Port-Expander (PCF8574N) erfasssen kannst statt direkt?

Die erste Gegenfrage wäre dann, ob das mit deiner CCRotaryEncoder Library überhaupt vorgesehen ist?

michael_x:
Und was haben die mit "Midi Controller ... und OLED Display" zu tun?

Deine Frage ist vermutlich, wie du den Encoder über einen I2C Port-Expander (PCF8574N) erfasssen kannst statt direkt?

Die erste Gegenfrage wäre dann, ob das mit deiner CCRotaryEncoder Library überhaupt vorgesehen ist?

Das Display ist erstmal zweitrangig, ansonsten genau, wie du schreibst.

Ich bezweifle, dass dies mit der Lib vorgesehen ist. Deshalb war ja der Versuch das erste mit dem zweiten irgendwie zusammenzubringen.

Hi

Also erst Mal vor den Problemen weg rennen oder ignorieren, bis wir so viel zusammen gebastelt haben, daß ein Umbau quasi ein kompletter Neubau werden wird?

Auch ein Weg ... frage mich nur, ob Masochismus der Normalzustand ist und ich einfach nur nicht normal bin ...

Du kannst den PortExpander befehligen und abfragen - und genau Das machst Du - Du fragst die Pins von dem Ding zyklisch ab - nennt man pollen.
Dann kannst Du aus dem Alt-Zustand und dem Neu-Zustand eine Drehrichtung erkennen - ab jetzt merkst Du Dir den neuen Zustand als Alt-Zustand - das Spiel beginnt von Neuem.
Klappt NUR bei Hand-Dreh-Encodern, die üblichen Alps von eBay sollten damit gut funktionieren.

MfG

postmaster-ino:
Hi

Also erst Mal vor den Problemen weg rennen oder ignorieren, bis wir so viel zusammen gebastelt haben, daß ein Umbau quasi ein kompletter Neubau werden wird?

Auch ein Weg ... frage mich nur, ob Masochismus der Normalzustand ist und ich einfach nur nicht normal bin ...

Du kannst den PortExpander befehligen und abfragen - und genau Das machst Du - Du fragst die Pins von dem Ding zyklisch ab - nennt man pollen.
Dann kannst Du aus dem Alt-Zustand und dem Neu-Zustand eine Drehrichtung erkennen - ab jetzt merkst Du Dir den neuen Zustand als Alt-Zustand - das Spiel beginnt von Neuem.
Klappt NUR bei Hand-Dreh-Encodern, die üblichen Alps von eBay sollten damit gut funktionieren.

MfG

Wie schon im Eingangspfosten erwähnt fehlen mir die Skills, um deine Anleitung mal eben 1:1 umsetzen, deshalb meine Versuche aus dem Vorhandenen möglicherweise zum ziel zu gelangen.

Soll ich jetzt deshalb alles hinschmeissen, statt es zumindest hier zu versuchen? Vielleicht gibt es ja schon ähnliche Projekte an denen ich mich orientieren und lernen könnte.

Hi

Genau - Du sollst NICHT Alles hinschmeißen (müssen) und Dich dem Problem stellen.
Die fehlenden Skills werden Dir nicht helfen, wenn Diese bei mir vorhanden sind.

Wir waren so weit, daß wir den PortExpander befehligen und auslesen können?
Dann frage den Expander ab, erkenne die Veränderungen an den Pins des Dreh-Encoder.
Dazu solltest Du das delay(500); aber vll. entfernen - so langsam bekommst Du den Encoder nicht gedreht, daß Das nicht stören wird.

Sinnvoll ist's hier, den Encoder GND schalten zu lassen, die Pins am Expander werden auf HIGH (Stromquelle << 1mA) gesetzt.
Beim Abfragen sind nun die Pins auf LOW, weil der Strom des Expander nach GND abfließt, wo Dieser Diesen Das aufzwingt (ich liebe Deutsch :slight_smile: ).
Aus diesem Bitmuster kannst Du ableiten, ob und in welche Richtung der Dreh-Encoder gedreht wird.

Etwas mehr als 'ich kann Das nicht' solltest Du schon probiert haben.

MfG

euphor:
Soll ich jetzt deshalb alles hinschmeissen, statt es zumindest hier zu versuchen?

Eine Suchmaschine liefert mir Rotary encoder with non-polling (interrupt based) switches from PCF8574 mit einer Bibliothek, die Dich möglicherweise Deinem Zeil näher bringt. Ausprobiert habe ich es nicht.

agmue:
Eine Suchmaschine liefert mir Rotary encoder with non-polling (interrupt based) switches from PCF8574 mit einer Bibliothek, die Dich möglicherweise Deinem Zeil näher bringt. Ausprobiert habe ich es nicht.

Das lese ich mich mal aufmerksam durch und versuche daraus was zu machen, danke!