Tipps für ws2812 Leds mit LCD Keypad gesteuert gesucht

Hallo erst mal ist mein erstes Post hier.
Habe gestern mein erstes Arduino Projekt begonnen, Ich möchte mit einem Arduino Nano unterschiedliche Lichteffekte auf WS2812 Ledstreifen Zaubern. Bedient soll dies alles von einem LCD Keypad werden (5 Tasten + Reset). Bei der Elektronik habe ich keinerlei Probleme damit habe ich schon länger zu tun.
Beim Programmieren bin aber noch sehr schwach, mein konkretes Problem ist ich habe jetzt schon das Menü fertig finde aber nicht so recht einen Ansatz wie daraus dann auf sketches die die Leds steuern zugreifen soll.
Das Menü hab ich jetzt einmal mit https://lcd-menu-bulder.cohesivecomputing.co.uk/ gebaut (ob es noch erweitert wird hängt vom Speicher am Nano ab).
Bei den Effekten werde ich mich wohl auch vorerst bei schon programmiertem bediene.

Ich bräuchte jetzt nur einen Tipps wie ich am besten aus dem Menü für das LCD Keypad am besten die Effekte steuere. Hab grad keinen Ansatz und finde keinen Hilfreichen Link.
Das Menü ist jetzt so gebastelt:

#include <LiquidCrystal.h>
#include "LcdKeypad.h"
#include "MenuData.h"

enum AppModeValues
{
  APP_NORMAL_MODE,
  APP_MENU_MODE,
  APP_PROCESS_MENU_CMD
};

byte appMode = APP_NORMAL_MODE;

MenuManager Menu1(sampleMenu_Root, menuCount(sampleMenu_Root));

char strbuf[LCD_COLS + 1]; // one line of lcd display
byte btn;

// initialize the library with the numbers of the interface pins
const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);


void refreshMenuDisplay (byte refreshMode);
byte getNavAction();


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

  backLightOn();
  // set up the LCD's number of columns and rows:
  lcd.begin(LCD_COLS, LCD_ROWS);

  // fall in to menu mode by default.
  appMode = APP_MENU_MODE;
  refreshMenuDisplay(REFRESH_DESCEND);

  // Use soft PWM for backlight, as hardware PWM must be avoided for some LCD shields.
  // piggy back on to timer0, which is already set to approx 1khz.
  OCR0A = 0xAF;
  TIMSK0 |= _BV(OCIE0A);
  
  setBacklightBrightness(1);
}

SIGNAL(TIMER0_COMPA_vect)
{
  lcdBacklightISR();
}

void loop()
{ 
  btn = getButton();

  switch (appMode)
  {
    case APP_NORMAL_MODE :
      if (btn == BUTTON_UP_LONG_PRESSED)
      {
        appMode = APP_MENU_MODE;
        refreshMenuDisplay(REFRESH_DESCEND);
      }
      break;
    case APP_MENU_MODE :
    {
      byte menuMode = Menu1.handleNavigation(getNavAction, refreshMenuDisplay);

      if (menuMode == MENU_EXIT)
      {
        lcd.clear();
        lcd.print("Hold UP for menu");
        appMode = APP_NORMAL_MODE;
      }
      else if (menuMode == MENU_INVOKE_ITEM)
      {
        appMode = APP_PROCESS_MENU_CMD;

        // Indicate selected item.
        if (Menu1.getCurrentItemCmdId())
        {
          lcd.setCursor(0, 1);
          strbuf[0] = 0b01111110; // forward arrow representing input prompt.
          strbuf[1] = 0;
          lcd.print(strbuf);
        }
      }
      break;
    }
    case APP_PROCESS_MENU_CMD :
    {
      byte processingComplete = processMenuCommand(Menu1.getCurrentItemCmdId());

      if (processingComplete)
      {
        appMode = APP_MENU_MODE;
        // clear forward arrow
        lcd.setCursor(0, 1);
        strbuf[0] = ' '; // clear forward arrow
        strbuf[1] = 0;
        lcd.print(strbuf);
      }
      break;
    }
  }
}

//----------------------------------------------------------------------
// Addition or removal of menu items in MenuData.h will require this method
// to be modified accordingly. 
byte processMenuCommand(byte cmdId)
{
  byte complete = false;  // set to true when menu command processing complete.

  if (btn == BUTTON_SELECT_PRESSED)
  {
    complete = true;
  }

  switch (cmdId)
  {
    // TODO Process menu commands here:
      default:
    break;
  }

  return complete;
}


//----------------------------------------------------------------------
// Callback to convert button press to navigation action.
byte getNavAction()
{
  byte navAction = 0;
  byte currentItemHasChildren = Menu1.currentItemHasChildren();
  
  if (btn == BUTTON_UP_PRESSED || btn == BUTTON_UP_LONG_PRESSED) navAction = MENU_ITEM_PREV;
  else if (btn == BUTTON_DOWN_PRESSED || btn == BUTTON_DOWN_LONG_PRESSED) navAction = MENU_ITEM_NEXT;
  else if (btn == BUTTON_SELECT_PRESSED || (btn == BUTTON_RIGHT_PRESSED && currentItemHasChildren)) navAction = MENU_ITEM_SELECT;
  //else if (btn == BUTTON_LEFT_PRESSED) navAction = MENU_BACK;
  return navAction;
}


//----------------------------------------------------------------------
const char EmptyStr[] = "";

// Callback to refresh display during menu navigation, using parameter of type enum DisplayRefreshMode.
void refreshMenuDisplay (byte refreshMode)
{
  char nameBuf[LCD_COLS+1];

/*
  if (refreshMode == REFRESH_DESCEND || refreshMode == REFRESH_ASCEND)
  {
    byte menuCount = Menu1.getMenuItemCount();
    
    // uncomment below code to output menus to serial monitor
    if (Menu1.currentMenuHasParent())
    {
      Serial.print("Parent menu: ");
      Serial.println(Menu1.getParentItemName(nameBuf));
    }
    else
    {
      Serial.println("Main menu:");
    }
    
    for (int i=0; i<menuCount; i++)
    {
      Serial.print(Menu1.getItemName(nameBuf, i));

      if (Menu1.itemHasChildren(i))
      {
        Serial.println("->");
      }
      else
      {
        Serial.println();
      }
    }
  }
*/

  lcd.setCursor(0, 0);
  if (Menu1.currentItemHasChildren())
  {
    rpad(strbuf, Menu1.getCurrentItemName(nameBuf));
    strbuf[LCD_COLS-1] = 0b01111110;            // Display forward arrow if this menu item has children.
    lcd.print(strbuf);
    lcd.setCursor(0, 1);
    lcd.print(rpad(strbuf, EmptyStr));          // Clear config value in display
  }
  else
  {
    byte cmdId;
    rpad(strbuf, Menu1.getCurrentItemName(nameBuf));
    
    if ((cmdId = Menu1.getCurrentItemCmdId()) == 0)
    {
      strbuf[LCD_COLS-1] = 0b01111111;          // Display back arrow if this menu item ascends to parent.
      lcd.print(strbuf);
      lcd.setCursor(0, 1);
      lcd.print(rpad(strbuf, EmptyStr));        // Clear config value in display.
    }
    else
    {
      lcd.print(strbuf);
      lcd.setCursor(0, 1);
      lcd.print(" ");
      
      // TODO Display config value.
    }
  }
}

und

#ifndef _sampleMenu_
#define _sampleMenu_
#include "MenuManager.h"
#include <avr/pgmspace.h>

/*

Generated using LCD Menu Builder at https://lcd-menu-bulder.cohesivecomputing.co.uk/
For more information, visit https://www.cohesivecomputing.co.uk/hackatronics/arduino-lcd-menu-library/

All our hackatronics projects are free for personal use. If you find them helpful or useful, please consider
making a small donation to our hackatronics fund using the donate buttons on our web pages. Thank you.
    
*/

enum sampleMenuCommandId
{
  mnuCmdBack = 0,
  mnuCmdTopItem1,
  mnuCmdTopItem2,
  mnuCmdTopItem,
  mnuCmdSubItemA,
  mnuCmdSubItemA1,
  mnuCmdSubItemA2,
  mnuCmdSubItemA3,
  mnuCmdSubItemB,
  mnuCmdSubItemB1,
  mnuCmdSubItemB2,
  mnuCmdSubItemB3,
  mnuCmdSubItemB3A,
  mnuCmdSubItemB3B,
  mnuCmdSubItemC,
  mnuCmdSubItemC1,
  mnuCmdSubItemC2,
  mnuCmdSubItemC3,
  mnuCmdResetToDefaults
};

PROGMEM const char sampleMenu_back[] = "Back";
PROGMEM const char sampleMenu_exit[] = "Exit";

PROGMEM const char sampleMenu_3_2_3_1[] = "on";
PROGMEM const char sampleMenu_3_2_3_2[] = "off";
PROGMEM const MenuItem sampleMenu_List_3_2_3[] = {{mnuCmdSubItemB3A, sampleMenu_3_2_3_1}, {mnuCmdSubItemB3B, sampleMenu_3_2_3_2}, {mnuCmdBack, sampleMenu_back}};

PROGMEM const char sampleMenu_3_1_1[] = "Rot";
PROGMEM const char sampleMenu_3_1_2[] = "Grün";
PROGMEM const char sampleMenu_3_1_3[] = "Blau";
PROGMEM const MenuItem sampleMenu_List_3_1[] = {{mnuCmdSubItemA1, sampleMenu_3_1_1}, {mnuCmdSubItemA2, sampleMenu_3_1_2}, {mnuCmdSubItemA3, sampleMenu_3_1_3}, {mnuCmdBack, sampleMenu_back}};

PROGMEM const char sampleMenu_3_2_1[] = "Slide";
PROGMEM const char sampleMenu_3_2_2[] = "Blink";
PROGMEM const char sampleMenu_3_2_3[] = "Pixel";
PROGMEM const MenuItem sampleMenu_List_3_2[] = {{mnuCmdSubItemB1, sampleMenu_3_2_1}, {mnuCmdSubItemB2, sampleMenu_3_2_2}, {mnuCmdSubItemB3, sampleMenu_3_2_3, sampleMenu_List_3_2_3, menuCount(sampleMenu_List_3_2_3)}, {mnuCmdBack, sampleMenu_back}};

PROGMEM const char sampleMenu_3_3_1[] = "Länge";
PROGMEM const char sampleMenu_3_3_2[] = "Lauf";
PROGMEM const char sampleMenu_3_3_3[] = "Farbraum";
PROGMEM const MenuItem sampleMenu_List_3_3[] = {{mnuCmdSubItemC1, sampleMenu_3_3_1}, {mnuCmdSubItemC2, sampleMenu_3_3_2}, {mnuCmdSubItemC3, sampleMenu_3_3_3}, {mnuCmdBack, sampleMenu_back}};

PROGMEM const char sampleMenu_3_1[] = "Farbe";
PROGMEM const char sampleMenu_3_2[] = "Lauflicht";
PROGMEM const char sampleMenu_3_3[] = "Regenbogen";
PROGMEM const MenuItem sampleMenu_List_3[] = {{mnuCmdSubItemA, sampleMenu_3_1, sampleMenu_List_3_1, menuCount(sampleMenu_List_3_1)}, {mnuCmdSubItemB, sampleMenu_3_2, sampleMenu_List_3_2, menuCount(sampleMenu_List_3_2)}, {mnuCmdSubItemC, sampleMenu_3_3, sampleMenu_List_3_3, menuCount(sampleMenu_List_3_3)}, {mnuCmdBack, sampleMenu_back}};

PROGMEM const char sampleMenu_1[] = "Dimm";
PROGMEM const char sampleMenu_2[] = "Speed";
PROGMEM const char sampleMenu_3[] = "Programme";
PROGMEM const char sampleMenu_4[] = "Reset";
PROGMEM const MenuItem sampleMenu_Root[] = {{mnuCmdTopItem1, sampleMenu_1}, {mnuCmdTopItem2, sampleMenu_2}, {mnuCmdTopItem, sampleMenu_3, sampleMenu_List_3, menuCount(sampleMenu_List_3)}, {mnuCmdResetToDefaults, sampleMenu_4}, {mnuCmdBack, sampleMenu_exit}};

/*
case mnuCmdTopItem1 :
  break;
case mnuCmdTopItem2 :
  break;
case mnuCmdSubItemA1 :
  break;
case mnuCmdSubItemA2 :
  break;
case mnuCmdSubItemA3 :
  break;
case mnuCmdSubItemB1 :
  break;
case mnuCmdSubItemB2 :
  break;
case mnuCmdSubItemB3A :
  break;
case mnuCmdSubItemB3B :
  break;
case mnuCmdSubItemC1 :
  break;
case mnuCmdSubItemC2 :
  break;
case mnuCmdSubItemC3 :
  break;
case mnuCmdResetToDefaults :
  break;
*/
#endif

die anderen Tabs kann ich euch auch noch posten wenn es hilft!

Da klemmt es schon.

Frage: Was willst Du? möchtest Du zusammenkopieren für ein Ding, oder willst Du auch verstehen, was da passiert und warum?

Und nein, die Frage ist ernst gemeint.

Ich glaub nicht das ich mit zusammen kopieren alleine Erfolg haben werde, also ich werde dabei schon auf genug Probleme stoßen die ich nicht werde lösen können wenn ich nicht auch verstehen lerne.
Klar ist das nicht der beste Zugang zu einer Sache, aber bei dem Projekt dachte ich mir zumindest das ich bei dem Menü auf dem Display ruhig auf etwas fertiges zurückgreifen kann. (hab ja auch verlinkt woher ich es habe). Bei den Lichteffekten werde ich dann schon wesentlich mehr selber machen, aber natürlich werde ich da auch nicht das Rad neu erfinden und mir codeblöcke von fertigen Dingen ausborgen.

Ich will das nicht bestreiten; es solche Fälle auch geben.

Aber wenn dann schon beim Code in der ersten Zeile Fehler (durch fehlerhaftes kopieren) auftauchen, dann kann das ganz schnell schief gehen, wenn sich das wiederholt.
Nicht nur von Dir zu hier sondern auch von dort zu dir.

Und ja, ein Menu zu bauen ist das Eine, das inhaltlich zu bestücken das Andere.
Genau da liegt meine Frage.

Warum machst Du das?
Nimm Deine 2812 und bau Dir erstmal einzelne Funktionen, mit denen Du die Dinger ansteuerst.
An Stelle des Menus ein einfaches auslesen des Keypad oder gar der Serielle Monitor zur Eingabe nutzen.
Damit Du erstmal weisst, was der Stripe kann, macht und wie Du Dir die Funktionen zusammenstellst.

DANN das Menu bauen. Mit dem vorher erarbeiteten Inhalt.
Das wäre meine Herangehensweise.
Aber da gibt es bestimmt noch weitere Wortmeldungen :wink:

Danke ups der Fehler ist nur beim hier her kopieren passiert danke! Und ja an Effekten für die LEDs arbeite ich eh schon, bzw. bastel ich vorhandenes um.
Wie der Stripe arbeitet habe ich im Grunde auch schon ganz gut verstanden (ist jetzt keine Raketenwissenschaft), klar wäre es ohne Menü leichter!
Nur finde ich ein Menü auch ganz gut um generell Struktur in den Code zu bringen, einzelne Effekte teilen sich ja auch Funktionen im Menü.
PS: ich bin auch schon weiter also ich habe raus gefunden das ich wohl hier

// Addition or removal of menu items in MenuData.h will require this method
// to be modified accordingly. 
byte processMenuCommand(byte cmdId)
{
  byte complete = false;  // set to true when menu command processing complete.

  if (btn == BUTTON_SELECT_PRESSED)
  {
    complete = true;
  }

  switch (cmdId)
  {
    // TODO Process menu commands here:
      default:
    break;
  }

rein muss um aus dem Menü heraus was anderes bedienen zu können.

Genau.
Jeder Menuepunkt bekommt eine cmdId.
Diese cmdId musst Du hier verwenden.

also

case 1:
machwas();
machwasanderes();
break;
case 2:
dunkel();
break;
case 3:
hell();
break;

Je nach Belieben.
Halte Dir den Bereich zwischen case und break so kurz wie möglich, damit Du das auch noch später überblicken kannst, was Du da machen willst.

1 Like

Allgemein formuliert setzt Du im Menü einen Merker, nennen wir ihn animationsNummer, den Du im Menü mit einem Wert füllst. In einer von loop ständig aufgerufenen Funktion führst Du mittels switch(animationsNummer) die gewünschte Animation aus. Beim Umschalten der Animationen mußt Du auf ein sauberes Verlassen und Starten der Animationen achten. Da kann ein if(animationsNummer != animationsNummerAlt) helfen.

1 Like

Danke euch!
Versuche mit kleinen Effekten laufen schon, ging jetzt einfacher als gedacht. Ich setze das mal auf erledigt, auch wenn ich mich jetzt mal erst ums steuern aus dem Menü der Effekte selbst kümmern muss.