4 Bit Display, Rotary und USB HID (Arduino Uno), Menü auswählen und USB HID senden

Pendant vieleicht nicht aber Leonardo ist ein Atmega32u und hat ein natives USB. Als China Nachbau wird was ähnliches als "Arduino Micro" beworben, kommt dann aber nicht von Arduino.cc

Na Klasse, das ist das, wovon @my_xy_projekt schon in #9 schrieb, warum ich Deine Frage nicht verstanden habe und weshalb ich den UNO für sowas nicht einem Anfänger empfehlen wollte. Aber lieber spät als nie :slightly_smiling_face:

Der Teensy kann mit einer Erweiterung der Arduino-IDE genauso wie ein Arduino programmiert werden, taktet schneller, hat mehr Speicher und kann seriellen Monitor und Tastaturemulation gleichzeitig. Wäre eine Option, wenn es später mal mit dem UNO irgendwie klemmen sollte.

Was siehst Du mit {20, 26, 8, 21, 23, 28, 29, 40} ?

Ja da hast Du recht, ich wusste nicht dass das eine spezielle Firmware ist und man nicht direk über USB senden kann.
Der Nachteil über SPI war für mich kein Nachteil, weil ich ja schon ein Codefragment hatte welches tat.
Ich hoffe man kann verstehen warum ich den UNO weiter verwenden wollte.

Genau selbes Verhalten.

Ich bestelle dann den Teensy 3.2 und es wird einfacher :slight_smile:

Du bekommst nicht "qwertzy" oder "qwertyz"?

Dann zeig' Deinem UNO mal die Zeichenbelegung der Tastatur!

Immer die Ruhe!

Nein ich sehe kein Querz oder Querty.
Sag mal kann das sein, dass der USB Code im Atmega16U2 die Befehle überhaupt nicht nativ durchlässt wenn er mit den Befehlen von Post 38 Arbeitet?

Erstens weiß ich nicht wie das geht, und macht das überhaupt Sinn, wenn im Code des Atmega was "übersetzt" wird?

Schade.

Das könnte sein, aber da für mich immer noch geheim ist, was Du mit dem Atmega16U2 gemacht hast, weiß ich das nicht. Das übertragene Feld ist acht Byte lang mit vielen Nullen, das könnte eine Bedeutung haben.

Das war nur Spaß, was bin ich heute wieder für ein Schelm! Heinz Erhardt

Na so geheim ist das nicht:

Aber ob man damit was anfangen kann steht auf einem anderen Blatt - ich nicht :frowning:

Also sowas... :grinning_face_with_smiling_eyes:

Das war die Anleitung von dem Type, die ich schon im Post 38 präsentierte:

Das ist je verrückt der flasht sogar den Arduino Mega ADK mit der Firmware, obwohl der einen USB Host drin hat?? Ich verstehe das einfach nicht, ich dachte wenn man einen USB Host hat, dann muss man die Firmware nicht mehr flashen, das aber nur nebenbei..

Auf meinem Blatt steht "Du mußt die Taste auch loslassen!".

Es werden wohl immer acht Byte übertragen, Position 0 sind die Modifiers wie Shift, Position 2 ist der Buchstabe.

Eventuell so (ungetestet):

const byte encoderPinA = 2;
const byte encoderPinB = 3;
const byte encoderPinSW = 10;
//-------------------------
#include <LiquidCrystal.h>
const int rs = 4, en = 5, d4 = 6, d5 = 7, d6 = 8, d7 = 9;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
//-------------------------

volatile int8_t encoderPos = 0;     // a counter for the dial
int8_t lastReportedPos = -1;        // change management
bool rotating = false;              // debounce management
bool neuePosition = false;

// interrupt service routine vars
boolean A_set = false;
boolean B_set = false;


void setup() {
  //---------------------------
  lcd.begin(20, 2);
  //---------------------------
  lcd.setCursor(0, 0);
  lcd.print("12345678901234567890");

  pinMode(encoderPinA, INPUT_PULLUP);
  pinMode(encoderPinB, INPUT_PULLUP);
  pinMode(encoderPinSW, INPUT_PULLUP);

  attachInterrupt(0, doEncoderA, CHANGE); // encoder pin on interrupt 0 (pin 2)
  attachInterrupt(1, doEncoderB, CHANGE); // encoder pin on interrupt 1 (pin 3)

  Serial.begin(9600);  // output
}

void loop()
{
  const char * text[5] = {"e", "r", "t", "z", "y"};
  const uint8_t code[5][8] = {
    {0, 0,  8, 0, 0, 0, 0, 0},
    {0, 0, 21, 0, 0, 0, 0, 0},
    {0, 0, 23, 0, 0, 0, 0, 0},
    {0, 0, 28, 0, 0, 0, 0, 0},
    {0, 0, 29, 0, 0, 0, 0, 0}
  };
  rotating = true;  // reset the debouncer

  if (lastReportedPos != encoderPos)
  {
    lastReportedPos = encoderPos;
    lcd.setCursor(0, 1);
    lcd.print("                    ");
    lcd.setCursor(7, 1);
    if (encoderPos < 0) encoderPos = 0;
    if (encoderPos > 4) encoderPos = 4;
    lcd.print(text[encoderPos]);
    neuePosition = true;
  }
  if ( neuePosition && !digitalRead(encoderPinSW) )  // nur beim Tastendruck des Encoders werden die Zeichen verschickt
  {
    neuePosition = false;
    Serial.write(code[encoderPos], 8);
    releaseKey();
  }
}

void releaseKey() {
  uint8_t keyNone[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
  Serial.write(keyNone, 8); // Send Release key
}

// Interrupt on A changing state
void doEncoderA()
{
  if ( rotating ) delay (1);  // wait a little until the bouncing is done
  if ( digitalRead(encoderPinA) != A_set ) { // debounce once more
    A_set = !A_set;
    // adjust counter + if A leads B
    if ( A_set && !B_set )
      encoderPos += 1;
    rotating = false;  // no more debouncing until loop() hits again
  }
}

// Interrupt on B changing state, same as A above
void doEncoderB() {
  if ( rotating ) delay (1);
  if ( digitalRead(encoderPinB) != B_set ) {
    B_set = !B_set;
    //  adjust counter - 1 if B leads A
    if ( B_set && !A_set )
      encoderPos -= 1;
    rotating = false;
  }
}

:+1:

Und das funktioniert!

Vielen herzlichen Dank!!!!!

Jetzt wo ich das weiß, kann ich auch den Code von der Firmware erkennen: man kann noch weitere Zeichen übertragen, in meinem Fall ein Enter, die 40 aus dem Tastaurlayout Post #38

{0, 0,  8, 40, 0, 0, 0, 0}

Jetzt muss nur noch das Display nach 10sec ausgehen, und wenn der Rotary gedreht wird, dann wieder angehen, ich glaube das bekommt man mit:

lcd.print("                    ");

und einem Timer hin.
Das muss ich dann mal übermorgen probieren, vorher habe ich keine Zeit mehr an der Schaltung..

Bitte gerne :crazy_face:

Wenn Deine Bibliothek sowas wie

  lcd.backlight();                     // turn on backlight

hat, kannst Du damit die stromfressende Beleuchtung ein/aus schalten.

Ja das versuche ich mal, wenn nicht geht das andere auch, weil ich ja ein OLED habe, das ist dunkel ohne Zeichen :slight_smile:

Dann reicht ein clear mit einem retriggerbaren Monoflop:

const byte encoderPinA = 2;
const byte encoderPinB = 3;
const byte encoderPinSW = 7;
//-------------------------
/*
  OLED - UNO
  GND - GND
  VDD - 5V
  SCX - 13 (HW SPI)
  SDA - 11 (HW SPI)
  RES -  8
  DC -   9
  CS -  10

  Hardware SPI Pins:
    Arduino Uno    sclk=13, data=11
    Arduino Due   sclk=76, data=75
    Arduino Mega  sclk=52, data=51
*/
#include <SPI.h>
#include "Ucglib.h" // "ucglib" im Bibliotheksverwalter
Ucglib_SSD1331_18x96x64_UNIVISION_HWSPI ucg(/*cd=*/ 9, /*cs=*/ 10, /*reset=*/ 8);
//-------------------------

volatile int8_t encoderPos = 0;     // a counter for the dial
int8_t lastReportedPos = -1;        // change management
bool rotating = false;              // debounce management
bool neuePosition = false;

// interrupt service routine vars
boolean A_set = false;
boolean B_set = false;


void setup() {
  ucg.begin(UCG_FONT_MODE_SOLID);
  //ucg.setRotate90();
  ucg.clearScreen();
  ucg.setFont(ucg_font_ncenR12_hr);
  ucg.setColor(255, 255, 255);

  pinMode(encoderPinA, INPUT_PULLUP);
  pinMode(encoderPinB, INPUT_PULLUP);
  pinMode(encoderPinSW, INPUT_PULLUP);

  attachInterrupt(0, doEncoderA, CHANGE); // encoder pin on interrupt 0 (pin 2)
  attachInterrupt(1, doEncoderB, CHANGE); // encoder pin on interrupt 1 (pin 3)

  Serial.begin(9600);  // output
}

void loop()
{
  const char * text[5] = {"e", "r", "t", "z", "y"};
  const uint8_t code[5][8] = {
    {0, 0,  8, 0, 0, 0, 0, 0},
    {0, 0, 21, 0, 0, 0, 0, 0},
    {0, 0, 23, 0, 0, 0, 0, 0},
    {0, 0, 28, 0, 0, 0, 0, 0},
    {0, 0, 29, 0, 0, 0, 0, 0}
  };
  rotating = true;  // reset the debouncer
  uint32_t jetzt = millis();
  static uint32_t vorhin = jetzt;
  const uint32_t intervall = 10000;  // Abschaltzeit nach Inaktivität in ms

  if (lastReportedPos != encoderPos)
  {
    lastReportedPos = encoderPos;
    vorhin = jetzt; // Monoflop retriggern
    ucg.setPrintPos(0, 12);
    ucg.print("0123456789");
    ucg.setPrintPos(0, 25);
    ucg.print("        ");
    ucg.setPrintPos(10, 25);
    if (encoderPos < 0) encoderPos = 0;
    if (encoderPos > 4) encoderPos = 4;
    ucg.print(text[encoderPos]);
    neuePosition = true;
  }
  if ( neuePosition && !digitalRead(encoderPinSW) )  // nur beim Tastendruck des Encoders werden die Zeichen verschickt
  {
    neuePosition = false;
    Serial.write(code[encoderPos], 8);
    releaseKey();
  }
  if (jetzt - vorhin >= intervall)
  {
    ucg.clearScreen();
  }
}

void releaseKey() {
  uint8_t keyNone[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
  Serial.write(keyNone, 8); // Send Release key
}

// Interrupt on A changing state
void doEncoderA()
{
  if ( rotating ) delay (1);  // wait a little until the bouncing is done
  if ( digitalRead(encoderPinA) != A_set ) { // debounce once more
    A_set = !A_set;
    // adjust counter + if A leads B
    if ( A_set && !B_set )
      encoderPos += 1;
    rotating = false;  // no more debouncing until loop() hits again
  }
}

// Interrupt on B changing state, same as A above
void doEncoderB() {
  if ( rotating ) delay (1);
  if ( digitalRead(encoderPinB) != B_set ) {
    B_set = !B_set;
    //  adjust counter - 1 if B leads A
    if ( B_set && !A_set )
      encoderPos -= 1;
    rotating = false;
  }
}

Meine OLEDs habe normalerweise I²C, nur ein kleines RGB-OLED hat SPI. Aber zum Probieren reicht es :slightly_smiling_face:

Ich hab kein SPI mehr, nur noch 4 Bit, oder meinst ich soll das Display wieder auf SPI umklemmen?
Aber ob die Lib dann passt...

ugc steuert ja das SPI Display, richtig?

Ich glaube ich kopiere den Monoflopcode in meinen rüber, ich denke mal das war von Dir auch so gedacht, oder?

Ja, denn SPI stand ursprünglich bei Dir so im Thema.

Verfahre bitte so, wie es für Dich paßt.

Ich habe nun mal das Monoflop in den 4Bit Modus von mir eingebaut:

const byte encoderPinA = 2;
const byte encoderPinB = 3;
const byte encoderPinSW = 10;
//-------------------------
#include <LiquidCrystal.h>
const int rs = 4, en = 5, d4 = 6, d5 = 7, d6 = 8, d7 = 9;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
//-------------------------

volatile int8_t encoderPos = 0;     // a counter for the dial
int8_t lastReportedPos = -1;        // change management
bool rotating = false;              // debounce management
bool neuePosition = false;

// interrupt service routine vars
boolean A_set = false;
boolean B_set = false;


void setup() {
  lcd.begin(20, 2);

  pinMode(encoderPinA, INPUT_PULLUP);
  pinMode(encoderPinB, INPUT_PULLUP);
  pinMode(encoderPinSW, INPUT_PULLUP);

  attachInterrupt(0, doEncoderA, CHANGE); // encoder pin on interrupt 0 (pin 2)
  attachInterrupt(1, doEncoderB, CHANGE); // encoder pin on interrupt 1 (pin 3)

  Serial.begin(9600);  // output
}

void loop()
{
  const char * text[5] = {"Filter 1", "Filter 2", "Filter 3", "Filter 4", "  Mute"};
  const uint8_t code[5][8] = {
    {0, 0,  8, 40, 0, 0, 0, 0},
    {0, 0, 21, 40, 0, 0, 0, 0},
    {0, 0, 23, 40, 0, 0, 0, 0},
    {0, 0, 28, 40, 0, 0, 0, 0},
    {0, 0, 29, 40, 0, 0, 0, 0}
  };
  rotating = true;  // reset the debouncer
  uint32_t jetzt = millis();
  static uint32_t vorhin = jetzt;
  const uint32_t intervall = 5000;  // Abschaltzeit nach Inaktivität in ms

  if (lastReportedPos != encoderPos)
  {
    lastReportedPos = encoderPos;
    vorhin = jetzt; // Monoflop retriggern
    lcd.setCursor(0, 0);
    lcd.print("12345678901234567890");
    lcd.setCursor(0, 1);
    lcd.print("                    ");
    lcd.setCursor(6, 1);
    if (encoderPos < 0) encoderPos = 0;
    if (encoderPos > 4) encoderPos = 4;
    lcd.print(text[encoderPos]);
    neuePosition = true;
  }
  if ( neuePosition && !digitalRead(encoderPinSW) )  // nur beim Tastendruck des Encoders werden die Zeichen verschickt
  {
    neuePosition = false;
    Serial.write(code[encoderPos], 8);
    releaseKey();

  }
  if (jetzt - vorhin >= intervall)
  {
    lcd.setCursor(0, 1);
    lcd.print("                    ");
    lcd.setCursor(0, 0);
    lcd.print("                    ");
  }
}
void releaseKey() {
  uint8_t keyNone[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
  Serial.write(keyNone, 8); // Send Release key
}

// Interrupt on A changing state
void doEncoderA()
{
  if ( rotating ) delay (1);  // wait a little until the bouncing is done
  if ( digitalRead(encoderPinA) != A_set ) { // debounce once more
    A_set = !A_set;
    // adjust counter + if A leads B
    if ( A_set && !B_set )
      encoderPos += 1;
    rotating = false;  // no more debouncing until loop() hits again
  }
}

// Interrupt on B changing state, same as A above
void doEncoderB() {
  if ( rotating ) delay (1);
  if ( digitalRead(encoderPinB) != B_set ) {
    B_set = !B_set;
    //  adjust counter - 1 if B leads A
    if ( B_set && !A_set )
      encoderPos -= 1;
    rotating = false;
  }
}

Jetzt bin ich noch an den kosmetischen Dingen :slight_smile:
Wie schaffe ich es, dass "Filter" auf den Display nicht immer so aufblitzt, es springen ab und zu benachbarte Pixel mit an, wenn man dreht, wenn dann das Wort geschrieben wurde, dann ist alles ruhig..
Ich hoffe man kann verstehen was ich meine.
Beim Wechsel von Mute auf Filter4 oder Filter4 auf Mute ist das nicht so.
Ich würde jetzt versuchen "Filter" nur einmal zu schreiben und nur noch die Ziffer dann zu ändern.
Springe ich aber auf "Mute" dann muss das Wort "Filter" ja überschrieben werden, genau so wenn ich von "Mute" auf "Filter" zurückspringe.
Und genau da ist mein Problem, wie kann man das bewerkstelligen? -Falls das überhaupt die Lösung ist, könnte natürlich auch sein, dass es brauchbarere Lösungen gibt-

Das Problem ist gelöst:

  const char * text[5] = {"Filter 1", "Filter 2", "Filter 3", "Filter 4", "  Mute  "};
  const uint8_t code[5][8] = {
    {0, 0,  8, 40, 0, 0, 0, 0},
    {0, 0, 21, 40, 0, 0, 0, 0},
    {0, 0, 23, 40, 0, 0, 0, 0},
    {0, 0, 28, 40, 0, 0, 0, 0},
    {0, 0, 29, 40, 0, 0, 0, 0}
  };
  rotating = true;  // reset the debouncer
  uint32_t jetzt = millis();
  static uint32_t vorhin = jetzt;
  const uint32_t intervall = 5000;  // Abschaltzeit nach Inaktivität in ms

  if (lastReportedPos != encoderPos)
  {
    lastReportedPos = encoderPos;
    vorhin = jetzt; // Monoflop retriggern
    lcd.setCursor(0, 0);
    lcd.print("12345678901234567890");
//    lcd.setCursor(6, 1);
//    lcd.print("                    ");
    lcd.setCursor(6, 1);
    if (encoderPos < 0) encoderPos = 0;
    if (encoderPos > 4) encoderPos = 4;
    lcd.print(text[encoderPos]);
    neuePosition = true;

Ich habe den Teil mal auskommentiert, es müssen die Zeilen nicht auf "keinZeichen" geschrieben werden, wenn man schon jedes mal den kompletten Text schreibt :slight_smile:

Nun kommt schon das nächste kosmetische Problem...
Wenn Mute ausgewählt wurde, dann soll der Text von Mute zu Mute* sich ändern, der aktuell komplette Code:

#include <LiquidCrystal.h>
const int rs = 4, en = 5, d4 = 6, d5 = 7, d6 = 8, d7 = 9;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

const byte encoderPinA = 2;
const byte encoderPinB = 3;
const byte encoderPinSW = 10;
volatile int8_t encoderPos = 0; // a counter for the dial
int8_t lastReportedPos = -1;    // change management
bool rotating = false;          // debounce management
bool neuePosition = false;
boolean A_set = false;          // interrupt service routine vars
boolean B_set = false;          // interrupt service routine vars

void setup() {
  lcd.begin(20, 2);

  pinMode(encoderPinA, INPUT_PULLUP);
  pinMode(encoderPinB, INPUT_PULLUP);
  pinMode(encoderPinSW, INPUT_PULLUP);

  attachInterrupt(0, doEncoderA, CHANGE); // encoder pin on interrupt 0 (pin 2)
  attachInterrupt(1, doEncoderB, CHANGE); // encoder pin on interrupt 1 (pin 3)

  Serial.begin(9600);  // output
}

void loop()
{
  const char * text[5] = {"Filter 1", "Filter 2", "Filter 3", "Filter 4", "  Mute  "};
  const uint8_t code[5][8] = {
    {0, 0,  8, 40, 0, 0, 0, 0},
    {0, 0, 21, 40, 0, 0, 0, 0},
    {0, 0, 23, 40, 0, 0, 0, 0},
    {0, 0, 28, 40, 0, 0, 0, 0},
    {0, 0, 29, 40, 0, 0, 0, 0}
  };
  rotating = true;  // reset the debouncer
  uint32_t jetzt = millis();
  static uint32_t vorhin = jetzt;
  const uint32_t intervall = 5000;  // Abschaltzeit nach Inaktivität in ms

  if (lastReportedPos != encoderPos)
  {
    lastReportedPos = encoderPos;
    vorhin = jetzt; // Monoflop retriggern
    lcd.setCursor(0, 0);
    lcd.print("12345678901234567890");
//    lcd.setCursor(6, 1);
//    lcd.print("                    ");
    lcd.setCursor(6, 1);
    if (encoderPos < 0) encoderPos = 0;
    if (encoderPos > 4) encoderPos = 4;
    lcd.print(text[encoderPos]);
    neuePosition = true;
  }
  if ( neuePosition && !digitalRead(encoderPinSW) )  // nur beim Tastendruck des Encoders werden die Zeichen verschickt
  {
    neuePosition = false;
    Serial.write(code[encoderPos], 8);
    releaseKey();

  }
  if (encoderPos <= 3 && jetzt - vorhin >= intervall)
  {
    lcd.setCursor(0, 1);
    lcd.print("                    ");
    lcd.setCursor(0, 0);
    lcd.print("                    ");
  }
  if (encoderPos == 4 && !digitalRead(encoderPinSW))
  {
    lcd.setCursor(6, 1);
    lcd.print("  Mute*  ");
  }
}
void releaseKey() {
  uint8_t keyNone[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
  Serial.write(keyNone, 8); // Send Release key
}

// Interrupt on A changing state
void doEncoderA()
{
  if ( rotating ) delay (1);  // wait a little until the bouncing is done
  if ( digitalRead(encoderPinA) != A_set ) { // debounce once more
    A_set = !A_set;
    // adjust counter + if A leads B
    if ( A_set && !B_set )
      encoderPos += 1;
    rotating = false;  // no more debouncing until loop() hits again
  }
}

// Interrupt on B changing state, same as A above
void doEncoderB() {
  if ( rotating ) delay (1);
  if ( digitalRead(encoderPinB) != B_set ) {
    B_set = !B_set;
    //  adjust counter - 1 if B leads A
    if ( B_set && !A_set )
      encoderPos -= 1;
    rotating = false;
  }
}

Das habe ich hiermit (nur der Relevante Teil vom kompletten Code oben)

  if (encoderPos <= 3 && jetzt - vorhin >= intervall)
  {
    lcd.setCursor(0, 1);
    lcd.print("                    ");
    lcd.setCursor(0, 0);
    lcd.print("                    ");
  }
  if (encoderPos == 4 && !digitalRead(encoderPinSW))
  {
    lcd.setCursor(6, 1);
    lcd.print("  Mute*  ");
  }

hinbekommen.
Aber wie komme ich es hin, dass Mute* wieder bei erneutem drücken auf Mute zurückspringt?
(mal ganz davon abgesehen, dass Position 4 (Mute) aus der Variable "neuePosition" rausgenommen werden muss)

Gar nicht.

    if (encoderPos > 4) encoderPos = 4;

Das ist unübersichtlich und gestückelt geworden ;(
Und ja, das geht schon - aber das passt irgendwie nicht sinnvoll da rein.

Ja ok, das war mir schon bewusst dass das so werden würde, aber jetzt hab ich fast alles schon zusammen, jetzt wollte ich auch nicht aufgeben...
Notfalls muss man eben teile vom Code ändern, damit es tut.
Ich könnte zwar jetzt ein Workaround machen und sagen ich mache einen Menüpunkt extra, eben Mute und Mute*, aber das muss auch anders gehen.

Ich komme gerade nicht drauf, was Du mir damit sagen möchtest..., eventuell dass das die Sackgasse ist?