Weihnachtbaum mit Display

Hallo,
Bei meinem Projekt "Weihnachtbaum mit Display" habe ich bei der Darstellung ein Problem.
mit Hardware: Arduino clon bzw Arduino Every getestet
Display:1,3 Oled
LED: 24 Stück Typ WS2811

Auf dem Display wird in 2 Zeilen der Text "Happy Weihnachten" dauernd gezeigt, und es sollen Schneeflocken von oben nach unten rieseln.
Die Led´s werden mit dem Sketch "Twinkle" gesteuert.

Mein Problem: der Programmteil Schrift und Schneeflocken werden ohne die Led Steuerung einwandfrei wiedergegeben(die Schneeflocken fallen einwandfrei).
Aber im Zusammenspiel mit der Twinkle Steuerung der LED´s ruckeln die Flocken bzw blinken beim fallen, die Schrift bleibt stabil.

Meine Vermutung ist, die Hardware kann meine Anforderung LED blinken und Schneeflockenfall zeitlich nicht steuern. (LED und Schneeflocken werden mit Zufallszahlen gesteuert.)

Für eine Hilfe bzw Tipps würde ich mich freuen.


```cpp
/// @file    TwinkleFox.ino
/// @brief   Twinkling "holiday" lights that fade in and out.
/// @example TwinkleFox.ino

#include "FastLED.h"
#include <U8g2lib.h>

#define NUM_LEDS      24
#define LED_TYPE   WS2811
#define COLOR_ORDER   GRB
#define Data_PIN       2
//#define CLK_PIN       4
#define VOLTS          5
#define MAX_MA       500
#define BRIGHTNESS 50  //LED Helligkeit

U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
// Define snowflake pattern (16x16) in XBM format
const static unsigned char snowflakes_bits[] PROGMEM = {
    0x00, 0x00, 0xa0, 0x05, 0xc0, 0x03, 0x88, 0x11, 0x9e, 0x79, 0x9c, 0x39,
   0xf6, 0x6f, 0xc0, 0x03, 0xc0, 0x03, 0xf6, 0x6f, 0x9c, 0x39, 0x9e, 0x79,
   0x88, 0x11, 0xc8, 0x03, 0xa0, 0x05, 0x00, 0x00
};
// Array to hold the position of each snowflake
#define MAX_SNOWFLAKES 10
int snowflakesX[MAX_SNOWFLAKES];  // X positions of snowflakes
int snowflakesY[MAX_SNOWFLAKES];  // Y positions of snowflakes
uint8_t snowflakeSpeeds[MAX_SNOWFLAKES]; // Falling speeds of the snowflakes


CRGBArray<NUM_LEDS> leds;

// Overall twinkle speed.
// 0 (VERY slow) to 8 (VERY fast).  
// 4, 5, and 6 are recommended, default is 4.
#define TWINKLE_SPEED 3

// Overall twinkle density.
// 0 (NONE lit) to 8 (ALL lit at once).  
// Default is 5.
#define TWINKLE_DENSITY 7

// How often to change color palettes.
#define SECONDS_PER_PALETTE  20
// Also: toward the bottom of the file is an array 
// called "ActivePaletteList" which controls which color
// palettes are used; you can add or remove color palettes
// from there freely.

// Background color for 'unlit' pixels
// Can be set to CRGB::Black if desired.
CRGB gBackgroundColor = CRGB::Black; 
// Example of dim incandescent fairy light background color
// CRGB gBackgroundColor = CRGB(CRGB::FairyLight).nscale8_video(16);

// If AUTO_SELECT_BACKGROUND_COLOR is set to 1,
// then for any palette where the first two entries 
// are the same, a dimmed version of that color will
// automatically be used as the background color.
#define AUTO_SELECT_BACKGROUND_COLOR 0

// If COOL_LIKE_INCANDESCENT is set to 1, colors will 
// fade out slighted 'reddened', similar to how
// incandescent bulbs change color as they get dim down.
#define COOL_LIKE_INCANDESCENT 0


CRGBPalette16 gCurrentPalette;
CRGBPalette16 gTargetPalette;

void setup()
 {
  delay( 1000 ); //safety startup delay
  FastLED.setMaxPowerInVoltsAndMilliamps( VOLTS, MAX_MA);
  FastLED.addLeds<LED_TYPE,Data_PIN,COLOR_ORDER>(leds, NUM_LEDS)
    .setCorrection(TypicalLEDStrip);
  FastLED.setBrightness( BRIGHTNESS );  // LED helligkeit
  u8g2.begin();

  //chooseNextColorPalette(gTargetPalette);
 // Initialize display
  u8g2.begin();
  randomSeed(analogRead(0));
     // Initialize snowflakes' random positions
  for (int i = 0; i < MAX_SNOWFLAKES; i++) {
    snowflakesX[i] = random(0, 112);  // Random X position
    snowflakesY[i] = random(-112, 0); // Start snowflakes from the top
    snowflakeSpeeds[i] = random(1,3); // Random speed for each snowflake
  }

}

void loop()
{
u8g2.clearBuffer(); 
  // Draw each snowflake
  for (int i = 0; i < MAX_SNOWFLAKES; i++) {
    u8g2.drawXBMP(snowflakesX[i], snowflakesY[i], 16,16, snowflakes_bits); // Draw snowflake
       snowflakesY[i] += snowflakeSpeeds[i];  // Update snowflake position
     
    // If the snowflake goes below the screen, reset it to the top
    if (snowflakesY[i] > 112) {
      snowflakesY[i] = -3;  // Reset snowflake to the top, slightly off-screen
      snowflakesX[i] = random(0, 112);  // New random X position
      snowflakeSpeeds[i] = random(1,3); // Random speed for new snowflake


  //u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_ncenR14_tf );
  u8g2.drawStr(30, 25, " Happy");
  u8g2.drawStr(12, 50, " Christmas!");
  u8g2.sendBuffer();
  //delay(1000);


  EVERY_N_SECONDS( SECONDS_PER_PALETTE ) { 
    chooseNextColorPalette( gTargetPalette ); 
  }
  
  EVERY_N_MILLISECONDS( 10 ) {
    nblendPaletteTowardPalette( gCurrentPalette, gTargetPalette,  12);
  }

  drawTwinkles( leds);
 
  FastLED.show();
   
 delay(30);  // Control the falling speed of snowflakes
  }
  }
}

//  This function loops over each pixel, calculates the 
//  adjusted 'clock' that this pixel should use, and calls 
//  "CalculateOneTwinkle" on each pixel.  It then displays
//  either the twinkle color of the background color, 
//  whichever is brighter.

void drawTwinkles( CRGBSet& L)
{
  // "PRNG16" is the pseudorandom number generator
  // It MUST be reset to the same starting value each time
  // this function is called, so that the sequence of 'random'
  // numbers that it generates is (paradoxically) stable.
  uint16_t PRNG16 = 11337;
  
  uint32_t clock32 = millis();

  // Set up the background color, "bg".
  // if AUTO_SELECT_BACKGROUND_COLOR == 1, and the first two colors of
  // the current palette are identical, then a deeply faded version of
  // that color is used for the background color
  CRGB bg;
  if( (AUTO_SELECT_BACKGROUND_COLOR == 1) &&
      (gCurrentPalette[0] == gCurrentPalette[1] )) {
    bg = gCurrentPalette[0];
    uint8_t bglight = bg.getAverageLight();
    if( bglight > 64) {
      bg.nscale8_video( 16); // very bright, so scale to 1/16th
    } else if( bglight > 16) {
      bg.nscale8_video( 64); // not that bright, so scale to 1/4th
    } else {
      bg.nscale8_video( 86); // dim, scale to 1/3rd.
    }
  } else {
    bg = gBackgroundColor; // just use the explicitly defined background color
  }

  uint8_t backgroundBrightness = bg.getAverageLight();
  
  for( CRGB& pixel: L) {
    PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; // next 'random' number
    uint16_t myclockoffset16= PRNG16; // use that number as clock offset
    PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; // next 'random' number
    // use that number as clock speed adjustment factor (in 8ths, from 8/8ths to 23/8ths)
    uint8_t myspeedmultiplierQ5_3 =  ((((PRNG16 & 0xFF)>>4) + (PRNG16 & 0x0F)) & 0x0F) + 0x08;
    uint32_t myclock30 = (uint32_t)((clock32 * myspeedmultiplierQ5_3) >> 3) + myclockoffset16;
    uint8_t  myunique8 = PRNG16 >> 8; // get 'salt' value for this pixel

    // We now have the adjusted 'clock' for this pixel, now we call
    // the function that computes what color the pixel should be based
    // on the "brightness = f( time )" idea.
    CRGB c = computeOneTwinkle( myclock30, myunique8);

    uint8_t cbright = c.getAverageLight();
    int16_t deltabright = cbright - backgroundBrightness;
    if( deltabright >= 32 || (!bg)) {
      // If the new pixel is significantly brighter than the background color, 
      // use the new color.
      pixel = c;
    } else if( deltabright > 0 ) {
      // If the new pixel is just slightly brighter than the background color,
      // mix a blend of the new color and the background color
      pixel = blend( bg, c, deltabright * 8);
    } else { 
      // if the new pixel is not at all brighter than the background color,
      // just use the background color.
      pixel = bg;
    }
  }
}


//  This function takes a time in pseudo-milliseconds,
//  figures out brightness = f( time ), and also hue = f( time )
//  The 'low digits' of the millisecond time are used as 
//  input to the brightness wave function.  
//  The 'high digits' are used to select a color, so that the color
//  does not change over the course of the fade-in, fade-out
//  of one cycle of the brightness wave function.
//  The 'high digits' are also used to determine whether this pixel
//  should light at all during this cycle, based on the TWINKLE_DENSITY.
CRGB computeOneTwinkle( uint32_t ms, uint8_t salt)
{
  uint16_t ticks = ms >> (8-TWINKLE_SPEED);
  uint8_t fastcycle8 = ticks;
  uint16_t slowcycle16 = (ticks >> 8) + salt;
  slowcycle16 += sin8( slowcycle16);
  slowcycle16 =  (slowcycle16 * 2053) + 1384;
  uint8_t slowcycle8 = (slowcycle16 & 0xFF) + (slowcycle16 >> 8);
  
  uint8_t bright = 0;
  if( ((slowcycle8 & 0x0E)/2) < TWINKLE_DENSITY) {
    bright = attackDecayWave8( fastcycle8);
  }

  uint8_t hue = slowcycle8 - salt;
  CRGB c;
  if( bright > 0) {
    c = ColorFromPalette( gCurrentPalette, hue, bright, NOBLEND);
    if( COOL_LIKE_INCANDESCENT == 1 ) {
      coolLikeIncandescent( c, fastcycle8);
    }
  } else {
    c = CRGB::Black;
  }
  return c;
}


/* This function is like 'triwave8', which produces a 
// symmetrical up-and-down triangle sawtooth waveform, except that this
// function produces a triangle wave with a faster attack and a slower decay:

     / \ 
   /     \ 
   /         \ 
  /             \ 
*/

uint8_t attackDecayWave8( uint8_t i)
{
  if( i < 86) {
    return i * 3;
  } else {
    i -= 86;
    return 255 - (i + (i/2));
  }
}

// This function takes a pixel, and if its in the 'fading down'
// part of the cycle, it adjusts the color a little bit like the 
// way that incandescent bulbs fade toward 'red' as they dim.
void coolLikeIncandescent( CRGB& c, uint8_t phase)
{
  if( phase < 128) return;

  uint8_t cooling = (phase - 128) >> 4;
  c.g = qsub8( c.g, cooling);
  c.b = qsub8( c.b, cooling * 2);
}

// A palette reminiscent of large 'old-school' C9-size tree lights
// in the five classic colors: red, orange, green, blue, and white.
#define C9_Red    0xB80400
#define C9_Orange 0x902C02
#define C9_Green  0x046002
#define C9_Blue   0x070758
#define C9_White  0x606820
const TProgmemRGBPalette16 RetroC9_p FL_PROGMEM =
{  C9_Red,    C9_Orange, C9_Red,    C9_Orange,
   C9_Orange, C9_Red,    C9_Orange, C9_Red,
   C9_Green,  C9_Green,  C9_Green,  C9_Green,
   C9_Blue,   C9_Blue,   C9_Blue,
   C9_White
};

// Add or remove palette names from this list to control which color
// palettes are used, and in what order.
const TProgmemRGBPalette16* ActivePaletteList[] = {
  &RetroC9_p,
  
};

// Advance to the next color palette in the list (above).
void chooseNextColorPalette( CRGBPalette16& pal)
{
  const uint8_t numberOfPalettes = sizeof(ActivePaletteList) / sizeof(ActivePaletteList[0]);
  static uint8_t whichPalette = -1; 
  whichPalette = addmod8( whichPalette, 1, numberOfPalettes);

  pal = *(ActivePaletteList[whichPalette]);
}

Wofür soll das delay nach show gut sein?

Gruß Tommy

Wenn du mehrere Sachen "gleichzeitig" machen willst darfst du keine for schleife in der loop haben, die ein delay enthält (bzw. mehr als ein paar ms Zeit braucht)

2 Likes

Noch ein Tipp: Formatiere mal den Sketch richtig (Strg-T in der IDE). Viellleicht siehst du dann ja noch mehr Probleme.

...oder um es noch deutlicher aufzuzeigen:
diese besagte Schleife umschließt fast den gesamten loop und darin werden
(a) jede Schneeflocke einzeln "geupdatet" (was ja auch richtig ist)
(b) 10x der Text aufs Display geschrieben
(c) 10x die Daten auf den LED-strip übertragen
(d) 10x auf ein delay gewartet

da geht viel Zeit verloren.. :innocent:

Tipps zum Beschleunigen:
(a) nach der Neupositionierung der Schneeflocken die Schleife beenden
(b) danach den Text aufs Display schreiben
Jede Neupositionierung der Schneeflocken und den Text nur alle X ms aufrufen und nicht bei jedem loop-Durchlauf (lieber millis() statt delay verwenden.. mehr Infos dazu hier - "BlinkWithoutDelay")
(c) die Daten an den LED-Strip genauso nur alle Y ms senden (gleiches Prinzip wie oben, eventuell reicht es auch schon drawTwinkles(leds); FastLED.show(); in die EVERY_N_MILLISECONDS(10)-Funktion zu verschieben..?)
(d) das delay, das dir den ganzen Programmablauf blockiert, entfällt damit.

Da geht bestimmt noch mehr, um es schneller zu bekommen, aber ich glaube, das wäre schonmal ein erster Schritt in die richtige Richtung :slight_smile:

Edit:
Oje... if (snowflakesY[i] > 112) { wird ja auch erst am Ende des Loops geschlossen.. das auch noch korrigieren :innocent:

Hallö Danke für die schnellen Antworten. Werde das mal testen.
Ich bin ein Neuling , das kann also etwas dauern bis ich mich wieder melde.
Ich wünsche Euch schon mal ein schönesWeihnachtsfest.
Siggi

Das ist ein Teil aus dem Entwurf für die Schneeflocken.
Der Sketch ist ein Entwurf von Chatgpt :grin:

Da sieht man wieder mal das das Teil keine Ahnung hat.
Wirf die delay raus und nutze millis() wie in BlinkWithoutDelay um Zeitsteuerungen zu machen. Du kannst Dir auch die MoBaTools anschauen.

Gruß Tommy

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.