Sketch in anderen Sketch integrieren

Hallo,
ich hab mal wieder ein Problem. Die Vorgeschichte:
Ich habe einen LED-Würfel nach Der bannende Zauberwürfel – Bansalor's Projektseite
nachgebaut. Daran etliche Veränderungen nach meinem Geschmack durchgeführt.
Alles funktioniert so wie es soll. Ich habe es als zip angehängt, sind immerhin
625 Zeilen.
Nun wollte ich ein weiteres Programm zu den fünf vorhandenen hinzufügen.
Einen Lichtwechsel, beruhend auf dem Beispiel-Sketch “NeoPixelFunRandomChange”
der Makuna- NeoPixelBus-Library. Nach klenen Änderungen sieht der jetzt so aus:

// NeoPixelFunRandomChange
// This example will randomly select a number pixels and then
// start an animation to blend them from their current color to
// randomly selected a color
// 
#include <NeoPixelBus.h>
#include <NeoPixelAnimator.h>
const uint16_t PixelCount = 45;
const uint8_t PixelPin = 3;
NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod> strip(PixelCount, PixelPin);
NeoPixelAnimator animations(PixelCount);
// what is stored for state is specific to the need, in this case, the colors.
// Basically what ever you need inside the animation update function
struct MyAnimationState {
    RgbColor StartingColor;
    RgbColor EndingColor;
};
// one entry per pixel to match the animation timing manager
MyAnimationState animationState[PixelCount];

void setup(){
    strip.Begin();
    strip.Show();
    SetRandomSeed();
}
void loop(){
    if (animations.IsAnimating()) {
        animations.UpdateAnimations();
        strip.Show();
    }
    else
    {  // no animations runnning, start some 
        PickRandom(0.3f); // 0.0 = black, 0.25 is normal, 0.5 is bright
    }
}
void SetRandomSeed(){
    uint32_t seed;
    seed = analogRead(0);
    delay(1);
    for (int shifts = 3; shifts < 31; shifts += 3) {
        seed ^= analogRead(0) << shifts;
        delay(1);
    }
    randomSeed(seed);
}
void BlendAnimUpdate(const AnimationParam& param){ // simple blend function
    // this gets called for each animation on every time step
    // progress will start at 0.0 and end at 1.0
    // we use the blend function on the RgbColor to mix
    // color based on the progress given to us in the animation
    RgbColor updatedColor = RgbColor::LinearBlend(
        animationState[param.index].StartingColor,
        animationState[param.index].EndingColor,
        param.progress);
    // apply the color to the strip
    strip.SetPixelColor(param.index, updatedColor);
}
void PickRandom(float luminance){ // pick random count of pixels to animate
    uint16_t count = random(PixelCount);
    while (count > 0){       
        uint16_t pixel = random(PixelCount);  // pick a random pixel
        uint16_t time = random(100, 400);     // pick random time 
        // we use HslColor object as it allows us to easily pick a color
        // with the same saturation and luminance 
        animationState[pixel].StartingColor = strip.GetPixelColor(pixel);
        animationState[pixel].EndingColor = HslColor(random(360) / 360.0f, 1.0f, luminance);
        animations.StartAnimation(pixel, time, BlendAnimUpdate);
        count--;
    }
}

Nun habe ich versucht, beide zu vereinen. Das ging aber schief. Ich bekomme folgende Fehlermeldungen:

D:\Eigenbauten\Rubik-RGBW-Cube\CUBE_V2\rgbcubev2\rgbcubev2.ino: In function ‘void loop()’:
D:\Eigenbauten\Rubik-RGBW-Cube\CUBE_V2\rgbcubev2\rgbcubev2.ino:102:18: warning: unused variable ‘animationState’ [-Wunused-variable]
MyAnimationState animationState[PixelCount];
^~~~~~~~~~~~~~

D:\Eigenbauten\Rubik-RGBW-Cube\CUBE_V2\rgbcubev2\rgbcubev2.ino: In function ‘void SetSparklingLights()’:
rgbcubev2:236:11: error: ‘animations’ was not declared in this scope
if (animations.IsAnimating()) {
^~~~~~~~~~
D:\Eigenbauten\Rubik-RGBW-Cube\CUBE_V2\rgbcubev2\rgbcubev2.ino:236:11: note: suggested alternative: ‘AnimationParam’
if (animations.IsAnimating()) {
^~~~~~~~~~
AnimationParam

D:\Eigenbauten\Rubik-RGBW-Cube\CUBE_V2\rgbcubev2\rgbcubev2.ino: In function ‘void PickRandom(float)’:
rgbcubev2:262:9: error: ‘animationState’ was not declared in this scope
animationState[pixel].StartingColor = strip.GetPixelColor(pixel);
^~~~~~~~~~~~~~
D:\Eigenbauten\Rubik-RGBW-Cube\CUBE_V2\rgbcubev2\rgbcubev2.ino:262:9: note: suggested alternative: ‘AnimationState’
animationState[pixel].StartingColor = strip.GetPixelColor(pixel);
^~~~~~~~~~~~~~
AnimationState

rgbcubev2:264:9: error: ‘animations’ was not declared in this scope
animations.StartAnimation(pixel, time, BlendAnimUpdate);
^~~~~~~~~~
D:\Eigenbauten\Rubik-RGBW-Cube\CUBE_V2\rgbcubev2\rgbcubev2.ino:264:9: note: suggested alternative: ‘AnimationParam’
animations.StartAnimation(pixel, time, BlendAnimUpdate);
^~~~~~~~~~
AnimationParam

D:\Eigenbauten\Rubik-RGBW-Cube\CUBE_V2\rgbcubev2\rgbcubev2.ino: In function ‘void BlendAnimUpdate(const AnimationParam&)’:
rgbcubev2:274:9: error: ‘animationState’ was not declared in this scope
animationState[param.index].StartingColor,
^~~~~~~~~~~~~~
D:\Eigenbauten\Rubik-RGBW-Cube\CUBE_V2\rgbcubev2\rgbcubev2.ino:274:9: note: suggested alternative: ‘AnimationState’
animationState[param.index].StartingColor,
^~~~~~~~~~~~~~
AnimationState

Die mich zu dem Schluß kommen lassen, das der Struct-Teil Schuld ist:

// what is stored for state is specific to the need, in this case, the colors.
// Basically what ever you need inside the animation update function
struct MyAnimationState {
    RgbColor StartingColor;
    RgbColor EndingColor;
};
// one entry per pixel to match the animation timing manager
MyAnimationState animationState[PixelCount];

Hat jemand einen Tip für mich?
Edit: Das neue Programm hat die Nummer5.

rgbcubev2.zip (4.77 KB)

rgbcube120.zip (3.97 KB)

warning: unused variable 'animationState' [-Wunused-variable]

Wenn eine Variable namens animationState nicht verwendet wird, und gleichzeitig eine Variable namens AnimationState fehlt, ist die Sache vermutlich einfach.

Was mit animations los ist, solltest du genauer klären. Ob der erhaltene Korrekturvorschlag
suggested alternative: 'AnimationParam' richtig ist.

translate.google.com liefert übrigens "vorgeschlagene Alternative". Das sollte eigentlich verständlich sein, oder ?

Bin wieder zurück...
Ahem, die Variable animationState wird sehr wohl verwendet, zwei mal.
Die vorgeschlagenen Ersetzungen erzeugen nur neue Fehlermeldungen.

Was mich irritiert ist, das es als einzelnes Programm funktioniert, aber nicht eingebunden in das große Programm.

Und das struct beinhaltet die Farbfunktionen Startfarbe und Endfarbe. Diese werden in beiden Funktionen verwendet, in "pickRandom" und in "BlendAnimUpdate".

Kann es sein, das die Einbindung des struct in eine Funktion nicht so einfach geht?

Ich bin trotz gewisser Fortschritte immernoch Anfänger, bemühe mich aber, das alles zu verstehen.

Offensichtlich habe ich bei der Integration des Sketches irgendwo einen Fehler gemacht.
Und diesen sehe ich nicht.

D:\Eigenbauten\Rubik-RGBW-Cube\CUBE_V2\rgbcubev2\rgbcubev2.ino: In function 'void loop()':
D:\Eigenbauten\Rubik-RGBW-Cube\CUBE_V2\rgbcubev2\rgbcubev2.ino:102:18: warning: unused variable 'animationState' [-Wunused-variable]

Wenn du uns die Funktion loop() zeigen würdest, und freundlicherweise rgbcubev2.ino Zeile 102 markierst, kann man dir evtl. helfen.
(Geraten globale und lokale Variablen durcheinander?)

Ich tue alles, was Dir hilft, mir zu helfen :wink:

void loop() {
  if(progActive == 1){		//Prog 1 Alles aus
       resetRubik();
       allOff();
  }
  if(progActive == 2){		//Prog 2 NACHTLICHT
      nightlight= EEPROM.read(0);
      setNightlight();
    if (ProgCounter<=PixelCount){
      strip.SetBrightness(nightlight);
      strip.SetPixelColor(ProgCounter, RgbColor(255,200,64));
      ProgCounter+=1;
      }
    if (ProgCounter>PixelCount){
      ProgCounter = 0;
      strip.Show();
      }
  }
  if(progActive == 3){		//Prog 3 Regenbogen mit allen LED's gleich
      strip.SetBrightness(lightness);      
      rainbow(PixelCount,wait*100);
  }
  if(progActive == 4){		//Prog 4 Regenbogen mit allen unterschiedlich
      strip.SetBrightness(lightness);      
      rotRainbow(PixelCount,wait*100);
  } //----------------------------------------------------------------------------------------------------------
  if(progActive == 5){    //Prog 5 Sparkling Lights
// what is stored for state is specific to the need, in this case, the colors.
// Basically what ever you need inside the animation update function
struct MyAnimationState {
    RgbColor StartingColor;
    RgbColor EndingColor;
};
// one entry per pixel to match the animation timing manager
MyAnimationState animationState[PixelCount]; //Zeile 102!*******************************

    SetRandomSeed();            
    SetSparklingLights(); 
  }  //----------------------------------------------------------------------------------------------------------
  if(progActive == 6){		//Prog 6 Rubik Cube
      strip.SetBrightness(lightness);
  if(!digitalRead(Multi)){//Drehungen erzeugen einen ungeordneten Würfel
          rotateRubik(7,3);
          delay(wait);
          rotateRubik(1,3);
          delay(wait);
          rotateRubik(7,1);
          delay(wait);
          rotateRubik(8,2);
          delay(wait);
          rotateRubik(2,3);
          delay(wait);
          rotateRubik(3,2);
          delay(wait);
          rotateRubik(7,1);
          delay(wait);
          rotateRubik(9,1);
          delay(wait);          
          rotateRubik(8,3);
          delay(wait);
          rotateRubik(7,3);
          delay(wait);
          rotateRubik(4,2);
          delay(wait);
          rotateRubik(5,1);
          delay(wait);
          rotateRubik(6,2);
          delay(wait);                    
          rotateRubik(2,1);
          delay(wait);
          rotateRubik(6,3);
          delay(wait);
          rotateRubik(3,1);
          delay(wait);
          rotateRubik(4,3);
          delay(wait);
          rotateRubik(1,3);
          delay(wait);
          rotateRubik(6,1);
          delay(wait);
          rotateRubik(1,1);
          delay(wait);
          rotateRubik(4,2);
          delay(wait);
          rotateRubik(1,2);
    }
//function:1= X_Right,2= X_Middle,3= X_Left,4= Y_Front,
//5= Y_Middle,6= Y_Back,7= Z_Top,8= Z_Middle,9= Z_Bottom
// turns = 1= nach oben/ links, 3= nach unten/ rechts
    if(!digitalRead(X_Right)){
          rotateRubik(1,1);
          delay(wait*4);
          while(!digitalRead(X_Right)){
            //delay damit nur ein Schritt gemacht wird
          }
    }
    if(!digitalRead(X_Middle)){
          rotateRubik(2,1);
          delay(wait*4);
          while(!digitalRead(X_Middle)){
          }
    }
    if(!digitalRead(X_Left)){
          rotateRubik(3,1);
          delay(wait*4);
          while(!digitalRead(X_Left)){
          }
    }
    if(!digitalRead(Y_Front)){
          rotateRubik(4,1);
          delay(wait*4);
          while(!digitalRead(Y_Front)){
          }
    }
    if(!digitalRead(Y_Middle)){
          rotateRubik(5,1);
          delay(wait*4);
          while(!digitalRead(Y_Middle)){
          }
    }
    if(!digitalRead(Y_Back)){
          rotateRubik(6,1);
          delay(wait*4);
          while(!digitalRead(Y_Back)){
          }
    }
    if(!digitalRead(Z_Top)){
          rotateRubik(7,1);
          delay(wait*4);
          while(!digitalRead(Z_Top)){
          }
    }
    if(!digitalRead(Z_Middle)){
          rotateRubik(8,1);
          delay(wait*4);
          while(!digitalRead(Z_Middle)){
          }
    }
    if(!digitalRead(Z_Bottom)){
          rotateRubik(9,1);
          delay(wait*4);
          while(!digitalRead(Z_Bottom)){
          }
    }
      setRubikColors();
   }
}

Hier sind noch die anderen zugehörigen Funktionen:

void SetSparklingLights() {  //Start Animation
      if (animations.IsAnimating()) {
        animations.UpdateAnimations();
        strip.Show();
    }
    else
    {  // no animations runnning, start some 
        PickRandom(0.3f); // 0.0 = black, 0.25 is normal, 0.5 is bright
    }
}
void SetRandomSeed(){
    uint32_t seed;
    seed = analogRead(0);
    delay(1);
    for (int shifts = 3; shifts < 31; shifts += 3) {
        seed ^= analogRead(0) << shifts;
        delay(1);
    }
    randomSeed(seed);
}
void PickRandom(float luminance){ // pick random count of pixels to animate
    uint16_t count = random(PixelCount);
    while (count > 0){       
        uint16_t pixel = random(PixelCount);  // pick a random pixel
        uint16_t time = random(100, 400);     // pick random time 
        // we use HslColor object as it allows us to easily pick a color
        // with the same saturation and luminance 
        animationState[pixel].StartingColor = strip.GetPixelColor(pixel);
        animationState[pixel].EndingColor = HslColor(random(360) / 360.0f, 1.0f, luminance);
        animations.StartAnimation(pixel, time, BlendAnimUpdate);
        count--;
    }
}
void BlendAnimUpdate(const AnimationParam& param){ // simple blend function
    // this gets called for each animation on every time step
    // progress will start at 0.0 and end at 1.0
    // we use the blend function on the RgbColor to mix
    // color based on the progress given to us in the animation
    RgbColor updatedColor = RgbColor::LinearBlend(
        animationState[param.index].StartingColor,
        animationState[param.index].EndingColor,
        param.progress);
    // apply the color to the strip
    strip.SetPixelColor(param.index, updatedColor);
}

Wenn du in loop nach

  if(progActive == 5){    //Prog 5 Sparkling Lights

richtig eingerückt hättest, würdest du sehen, dass dort eine lokale Variable

MyAnimationState animationState[PixelCount];

definiert wird, die nirgends sonst zu sehen ist, und daher auch nicht verwendet wird (werden kann).

void loop() {
  ... 
  if(progActive == 5){    //Prog 5 Sparkling Lights
    SetRandomSeed();           
    SetSparklingLights();
  }
  ... 
}

ist also dasselbe, nur ohne Warnung.

Jetzt geht aber vermutlich erst die Arbeit los...

Ich hab mal die Einrückungen etwas verändert:

 if(progActive == 5)   //Prog 5 Sparkling Lights
  { 
  struct MyAnimationState
     {
      RgbColor StartingColor;
      RgbColor EndingColor;
     };
     MyAnimationState animationState[PixelCount]; //Zeile 102!***********
     SetRandomSeed();            
     SetSparklingLights(); 
  }

Also für mich ist das "MyAnimationState" der struct - Titel.
Und dem wird "animationState[PixelCount]" zugewiesen.
Und das folgt direkt auf das struct. Das kann doch nicht falsch sein?
Es war ja im (funktionierenden) Original genauso.
Sollte ich vielleicht das ganze struct unter"void SetSparklingLight" einordnen?

Ich habs jetzt einfach mal gemacht:
Keine Änderung an den Fehlermeldungen, es ist also ziemlich egal wo sich das struct befindet.

Der nächste Versuch war:
Das ganze struct komplett nach oben verschoben, direkt vor das setup.
Und siehe da, die Fehler beim "animationState" sind weg!
Jetzt kommen nur noch zwei Fehlermeldungen:

D:\Eigenbauten\Rubik-RGBW-Cube\CUBE_V2\rgbcubev2\rgbcubev2.ino: In function 'void loop()':
rgbcubev2:115:11: error: 'animations' was not declared in this scope
       if (animations.IsAnimating())
           ^~~~~~~~~~

D:\Eigenbauten\Rubik-RGBW-Cube\CUBE_V2\rgbcubev2\rgbcubev2.ino: In function 'void PickRandom(float)':
rgbcubev2:283:5: error: 'animations' was not declared in this scope
     animations.StartAnimation(pixel, time, BlendAnimUpdate);
     ^~~~~~~~~~

Damit weiß ich garnicht weiter. Das "animations." ist wohl eine Funktion aus der Library (NeoPixelAnimator.h).

Ein weiteres Auftreten in Zeile 117 "animations.UpdateAnimations()" wird nicht bemängelt.

Also für mich ist das "MyAnimationState" der struct - Titel.
Und dem wird "animationState[PixelCount]" zugewiesen.
Und das folgt direkt auf das struct. Das kann doch nicht falsch sein?

Nicht direkt falsch, aber völlig sinnlos. Daher auch die Warnung "unused"

Deine Funktion SetSparklingLights verwendet eine Variable namens animations und strip.
Ausserdem, via PickRandom, animationState
Diese müssen global definiert sein, damit die Funktion sie kennt.

animations , vom Datentyp NeoPixelAnimator
und animationState hast du im Schnipsel definiert, der mit

// NeoPixelFunRandomChange

anfängt. Ist das eine andere Datei?

Zum Übersetzen werden alle .ino zuzammenkopiert, zuerst, die Haupt-Datei, die genauso heisst wie das Verzeichnis, danach alle anderen .ino dieses Verzeichnisses in alphabetischer Reihenfolge.
Wenn dein "NeoPixelFunRandomChange" später kommt, sind vorher die globalen Variablen nicht definiert.
Da gibt es mehrere Abhilfe-Möglichkeiten, aber das Einfachste ist wohl, die Dateien zusammenzufassen oder umzubenennen, oder diese globalen Variablen und deren Strukturen am Anfang der Haupt-Datei vor

void setup() {

zu definieren.

Es funktioniert!!

DAS war der entscheidende Hinweis:

"diese globalen Variablen und deren Strukturen am Anfang der Haupt-Datei vor void setup() {zu definieren."

Das habe ich getan und schon sind alle Probleme gelöst.

Dieses "NeoPixelFunRandomChange" ist der Name der originalen Beispieldatei aus der Lib.

Ich bedanke mich für die schnelle und kompetente Hilfe, ohne diese hätte ich das Ganze wohl aufgegeben. Jetzt kann ich dieses Projekt abschließen und was neues anfangen :wink:

rgbcubev2.zip (4.63 KB)

Sehr schön, freut mich. Danke für die Rückmeldung.

Schönes Foto von einem schönen Projekt übrigens.

Fehlt noch, dass man “richtige” Dreheffekte machen kann und das Würfel-Puzzle manuell oder automatisch lösen kann. :wink:
Aber mehr als 6 Farben ist natürlich auch schön.

Die originale Würfelfunktion ist natürlich auch drin! Die Lösung dauert zwar etwas länger, aber es geht.
Für jede Ebene des Würfels ist eine Taste auf dem Sockel.
Dieses Blinki-Bunti ist ja nur eine Zusatzfunktion.

Die originale Würfelfunktion ist natürlich auch drin

Und die Unterseite des Würfels sieht man auch irgendwie? Muss ich mir tatsächlich die .zip Datei mal ansehen :slight_smile:

Die untere Schicht ist nur virtuell vorhanden. Das erhöht beträchtlich den Schwierigkeitsgrad :wink:
Das Original von https://bansaloris.de/der-bannende-zauberwuerfel/ ist etwas einfacher gehalten, dafür
ist aber noch mehr Schnickschnack drin.
Das größte Problem waren die Lötarbeiten, RGB-LED SK6812 in 3535, ohne Lötfahnen, sehr dicht beieinander.
Ein NANO treibt das alles an und ist ca. 50% damit ausgelastet.