fastLED beatsin16 just one way

Here is part of my sketch. The moving dot (and tail) goes out and then comes back. I would like it to simply go out.
I have searched and searched - I must be using the wrong search terms.
Must beatsin16 oscillate? If not, what parameter must i changed, or how to rewrite my code?
Thank you -

  void movingDotred() {
    uint16_t sinBeat = beatsin16(20, 0, NUM_LEDS - 1, 0, 0);

    leds[sinBeat] = CRGB::Red;
    fadeToBlackBy(leds, NUM_LEDS, 10);
    addGlitter(30);   //changing this variable will increase the chance of a "star" popping up
    FastLED.show();
  }

Sorry we need to see it all, along with a schematic, hand drawn is fine Fritzing is not.

You might want to look at this How to get the best from this from this Forum before you proceed any further.

Ok, I'll work on that.
In the meantime, I asked the wrong question.
I know the beatsin oscillates, along the y-axis. When it gets to NUM-LEDS-1, which essentially can be thought of as the x-axis, I want it to stop, not return.
Can this be done with beatsin, or by definition must it be reflected?

#include <FastLED.h>

#define NUM_LEDS  180
#define LED_PIN   3
//CRGB leds[NUM_LEDS];
CRGBArray <NUM_LEDS> leds;
uint8_t data[ NUM_LEDS];


void setup() {
  delay(3000);
  FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
  FastLED.setBrightness(100);
  FastLED.setMaxPowerInVoltsAndMilliamps(5, 1000);
  randomSeed(analogRead(A0));

}

void loop() {
  
  int seqVar = random(0, 2);

  //uint32_t period2 = .1 * 60000L;       // X minutes
    uint32_t period2 = random(10,20) * 1000L;       // X seconds
      for ( uint32_t tStart = millis();  (millis() - tStart) < period2; )
      
  switch (seqVar) {
      
    case 0:
      //uint32_t period2 = .1 * 60000L;       // X minutes
      //for ( uint32_t tStart = millis();  (millis() - tStart) < period2;  )
      movingDotred();
      break;

    case 1:
      //uint32_t period2 = .2 * 60000L;       // X minutes
      //for ( uint32_t tStart = millis();  (millis() - tStart) < period2;  )
      movingDotwhite();
      break;

    case 2:
      //uint32_t period2 = .2 * 60000L;       // X minutes
      //for ( uint32_t tStart = millis();  (millis() - tStart) < period2;  )
      movingDotblue();
      break;

    case 3:
      //uint32_t period3 = .3 * 60000L;       // X minutes
      //for ( uint32_t tStart = millis();  (millis() - tStart) < period2;  )
      phaseBeat();
      break;

    case 4:
      //uint32_t period3 = .3 * 60000L;       // X minutes
      //for ( uint32_t tStart = millis();  (millis() - tStart) < period2;  )
      redwhitebluestripeswithglitter();
      break;

    case 5:
      //uint32_t period3 = .3 * 60000L;       // X minutes
      //for ( uint32_t tStart = millis();  (millis() - tStart) < period2;  )
      RWBFadeInandOut();
      break;

    case 6:
      //uint32_t period3 = .3 * 60000L;       // X minutes
      //for ( uint32_t tStart = millis();  (millis() - tStart) < period2;  )
      NewKITT(0xff, 0, 0, 35, .1, 50);
      NewKITT(0, 0, 0xff, 35, .1, 50);
      NewKITT(0xff, 0xff, 0xff, 35, .1, 50);
      break;

    case 7:
      //uint32_t period3 = .3 * 60000L;       // X minutes
      //for ( uint32_t tStart = millis();  (millis() - tStart) < period2;  )
      theaterChase(0xff, 0, 0, 70);
      theaterChase(0xff, 0xff, 0xff, 70);
      theaterChase(0, 0, 0xff, 70); 
      break;

     case 8:
     outsideinmeteors();
      break;

      default:
      ;
 
      
  }
}
void movingDotred() {
  uint16_t sinBeat = beatsin16(20, 0, NUM_LEDS - 1, 0, 0);

  leds[sinBeat] = CRGB::Red;
  fadeToBlackBy(leds, NUM_LEDS, 10);
  FastLED.show();
}

void movingDotwhite() {
  uint16_t sinBeat = beatsin16(20, 0, NUM_LEDS - 1, 0, 0);

  leds[sinBeat] = CRGB::White;
  fadeToBlackBy(leds, NUM_LEDS, 10);
  FastLED.show();
}
void movingDotblue() {
  uint16_t sinBeat = beatsin16(20, 0, NUM_LEDS - 1, 0, 0);

  leds[sinBeat] = CRGB::Blue;
  fadeToBlackBy(leds, NUM_LEDS, 10);
  FastLED.show();
}



void phaseBeat() {
  //  uint8_t sinBeat   = beatsin8(15, 0, NUM_LEDS - 1, 0, 0);
  //  uint8_t sinBeat2  = beatsin8(15, 0, NUM_LEDS - 1, 0, 85);
  //  uint8_t sinBeat3  = beatsin8(15, 0, NUM_LEDS - 1, 0, 170);

  // If you notice that your pattern is missing out certain LEDs, you
  // will need to use the higher resolution beatsin16 instead. In this
  // case remove the 3 lines above and replace them with the following:
  uint16_t sinBeat   = beatsin16(15, 0, NUM_LEDS - 1, 0, 0);
  uint16_t sinBeat2  = beatsin16(15, 0, NUM_LEDS - 1, 0, 21845);
  uint16_t sinBeat3  = beatsin16(15, 0, NUM_LEDS - 1, 0, 43690);

  leds[sinBeat]   = CRGB::Blue;
  leds[sinBeat2]  = CRGB::Red;
  leds[sinBeat3]  = CRGB::White;

  fadeToBlackBy(leds, NUM_LEDS, 10);
  FastLED.show();
}

void redwhitebluestripeswithglitter() {
  fill_data_array();
  render_data_with_palette();
  add_glitter();

  FastLED.show();
  FastLED.delay(20);
}

void fill_data_array()
{
  static uint8_t startValue = 0;
  startValue = startValue + 2;

  uint8_t value = startValue;
  for ( int i = 0; i < NUM_LEDS; i++) {
    data[i] = triwave8( value); // convert value to an up-and-down wave
    value += 3;
  }
}


CRGBPalette16 gPalette (
  CRGB::Black, CRGB::Black,
  CRGB::Red,   CRGB::Red,  CRGB::Red,  CRGB::Red,
  CRGB::Gray,  CRGB::Gray, CRGB::Gray, CRGB::Gray,
  CRGB::Blue,  CRGB::Blue, CRGB::Blue, CRGB::Blue,
  CRGB::Black, CRGB::Black
);

void render_data_with_palette()
{
  for ( int i = 0; i < NUM_LEDS; i++) {
    leds[i] = ColorFromPalette( gPalette, data[i], 128, LINEARBLEND);
  }
}

void add_glitter()
{
  int chance_of_glitter =  10; // percent of the time that we add glitter
  int number_of_glitters = 10; // number of glitter sparkles to add

  int r = random8(100);
  if ( r < chance_of_glitter ) {
    for ( int j = 0; j < number_of_glitters; j++) {
      int pos = random16( NUM_LEDS);
      leds[pos] = CRGB::White; // very bright glitter
    }
  }
}

void RWBFadeInandOut() {
  FadeInOut(0xff, 0x00, 0x00); // red
  FadeInOut(0xff, 0xff, 0xff); // white
  FadeInOut(0x00, 0x00, 0xff); // blue
}
void setAll(byte red, byte white, byte blue) {
  for (int i = 0; i < NUM_LEDS; i++ ) {
    setPixel(i, red, white, blue);
  }
  FastLED.show();
}

void setPixel(int Pixel, byte red, byte white, byte blue) {

  leds[Pixel].r = red;
  leds[Pixel].g = white;
  leds[Pixel].b = blue;

}
void FadeInOut(byte red, byte green, byte blue) {
  float r, g, b;

  for (int k = 0; k < 256; k = k + 5) {
    r = (k / 256.0) * red;
    g = (k / 256.0) * green;
    b = (k / 256.0) * blue;
    setAll(r, g, b);
    FastLED.show();
  }

  for (int k = 255; k >= 0; k = k - 5) {
    r = (k / 256.0) * red;
    g = (k / 256.0) * green;
    b = (k / 256.0) * blue;
    setAll(r, g, b);
    FastLED.show();
  }
}

void NewKITT(byte red, byte green, byte blue, int EyeSize, int SpeedDelay, int ReturnDelay) {
  RightToLeft(red, green, blue, EyeSize, SpeedDelay, ReturnDelay);
  LeftToRight(red, green, blue, EyeSize, SpeedDelay, ReturnDelay);
  OutsideToCenter(red, green, blue, EyeSize, SpeedDelay, ReturnDelay);
  CenterToOutside(red, green, blue, EyeSize, SpeedDelay, ReturnDelay);
  //  LeftToRight(red, green, blue, EyeSize, SpeedDelay, ReturnDelay);
  //  RightToLeft(red, green, blue, EyeSize, SpeedDelay, ReturnDelay);
  //  OutsideToCenter(red, green, blue, EyeSize, SpeedDelay, ReturnDelay);
  //  CenterToOutside(red, green, blue, EyeSize, SpeedDelay, ReturnDelay);
}

void CenterToOutside(byte red, byte green, byte blue, int EyeSize, int SpeedDelay, int ReturnDelay) {
  for (int i = ((NUM_LEDS - EyeSize) / 2); i >= 0; i--) {
    setAll(0, 0, 0);

    setPixel(i, red / 10, green / 10, blue / 10);
    for (int j = 1; j <= EyeSize; j++) {
      setPixel(i + j, red, green, blue);
    }
    setPixel(i + EyeSize + 1, red / 10, green / 10, blue / 10);

    setPixel(NUM_LEDS - i, red / 10, green / 10, blue / 10);
    for (int j = 1; j <= EyeSize; j++) {
      setPixel(NUM_LEDS - i - j, red, green, blue);
    }
    setPixel(NUM_LEDS - i - EyeSize - 1, red / 10, green / 10, blue / 10);

    FastLED.show();
    delay(SpeedDelay);
  }
  delay(ReturnDelay);
}

void OutsideToCenter(byte red, byte green, byte blue, int EyeSize, int SpeedDelay, int ReturnDelay) {
  for (int i = 0; i <= ((NUM_LEDS - EyeSize) / 2); i++) {
    setAll(0, 0, 0);

    setPixel(i, red / 10, green / 10, blue / 10);
    for (int j = 1; j <= EyeSize; j++) {
      setPixel(i + j, red, green, blue);
    }
    setPixel(i + EyeSize + 1, red / 10, green / 10, blue / 10);

    setPixel(NUM_LEDS - i, red / 10, green / 10, blue / 10);
    for (int j = 1; j <= EyeSize; j++) {
      setPixel(NUM_LEDS - i - j, red, green, blue);
    }
    setPixel(NUM_LEDS - i - EyeSize - 1, red / 10, green / 10, blue / 10);

    FastLED.show();
    delay(SpeedDelay);
  }
  delay(ReturnDelay);
}

void LeftToRight(byte red, byte green, byte blue, int EyeSize, int SpeedDelay, int ReturnDelay) {
  for (int i = 0; i < NUM_LEDS - EyeSize - 2; i++) {
    setAll(0, 0, 0);
    setPixel(i, red / 10, green / 10, blue / 10);
    for (int j = 1; j <= EyeSize; j++) {
      setPixel(i + j, red, green, blue);
    }
    setPixel(i + EyeSize + 1, red / 10, green / 10, blue / 10);
    FastLED.show();
    delay(SpeedDelay);
  }
  delay(ReturnDelay);
}

void RightToLeft(byte red, byte green, byte blue, int EyeSize, int SpeedDelay, int ReturnDelay) {
  for (int i = NUM_LEDS - EyeSize - 2; i > 0; i--) {
    setAll(0, 0, 0);
    setPixel(i, red / 10, green / 10, blue / 10);
    for (int j = 1; j <= EyeSize; j++) {
      setPixel(i + j, red, green, blue);
    }
    setPixel(i + EyeSize + 1, red / 10, green / 10, blue / 10);
    FastLED.show();
    delay(SpeedDelay);
  }
  delay(ReturnDelay);
}

void theaterChase(byte red, byte green, byte blue, int SpeedDelay) {
  for (int j = 0; j < 10; j++) { //do 10 cycles of chasing
    for (int q = 0; q < 3; q++) {
      for (int i = 0; i < NUM_LEDS; i = i + 3) {
        setPixel(i + q, red, green, blue);  //turn every third pixel on
      }
      FastLED.show();

      delay(SpeedDelay);

      for (int i = 0; i < NUM_LEDS; i = i + 3) {
        setPixel(i + q, 0, 0, 0);    //turn every third pixel off
      }
    }
  }
}

void outsideinmeteors(){
  int myVar=random(0,3);
  switch (myVar) {
  case 0:
  red();
  break;

  case 1:
  blue();
  break;

  case 2:
  white();
}
}
void red(){ 
  //static uint8_t hue;
  for(int i = 0; i < NUM_LEDS/2; i++) {   
    // fade everything out
    leds.fadeToBlackBy(20);

    // let's set an led value
    leds[i] = CRGB(250,0,0);

    // now, let's first 20 leds to the top 20 leds, 
    leds(NUM_LEDS/2,NUM_LEDS-1) = leds(NUM_LEDS/2 - 1 ,0);
    FastLED.delay(random (2,30));
  }  
}

void blue(){ 
  //static uint8_t hue;
  for(int i = 0; i < NUM_LEDS/2; i++) {   
    // fade everything out
    leds.fadeToBlackBy(20);

    // let's set an led value
    leds[i] = CRGB(0,0,250);

    // now, let's first 20 leds to the top 20 leds, 
    leds(NUM_LEDS/2,NUM_LEDS-1) = leds(NUM_LEDS/2 - 1 ,0);
    FastLED.delay(random(2,30));
  } 
}

void white(){ 
  //static uint8_t hue;
  for(int i = 0; i < NUM_LEDS/2; i++) {   
    // fade everything out
    leds.fadeToBlackBy(20);

    // let's set an led value
    leds[i] = CRGB(250,250,250);

    // now, let's first 20 leds to the top 20 leds, 
    leds(NUM_LEDS/2,NUM_LEDS-1) = leds(NUM_LEDS/2 - 1 ,0);
    FastLED.delay(random(2,30));
  } 
}

So how come you can ever get a case 3 for your

?
Or in fact any of the other cases 4 to 8?

Have you posted the correct code?

I blocked those out so I could concentrate on the moving dot red. I don't want to sit through all cases when working on just a few.

Some docs for those that don't know the library:

https://fastled.io/docs/3.1/group__lib8tion.html#gaa46e5de1c4c27833359e7a97a18c839b -- dead

https://fastled.io/docs/group___beat_generators.html#gaa46e5de1c4c27833359e7a97a18c839b

By this do you mean it does half of a cycle of sine curve and you want only a quarter cycle?

If so, I'd say you have a choice to slow it down by using half the frequency/BPM, or keep the same speed and only call it for half of the length of time you are now calling it.

I think it is doing a full cycle, I can't visually on the strip tell the difference between a half and a full. And when I plot sinBeat it seems to be many complete cycles.
I want it to do a quarter cycle.

Well, I think I'm going to call it done and simply use beat8, the sawtooth wave.
It's not as smooth as the sin wave but it will do for my purposes.
Thank you for your attention.

Oh, I see. You are calling it repeatedly, continuously for a random interval. I missed that bit and mistakenly thought your other code was just calling it for a definite half-cycle. Your usage here cycles the bright red LED back and forth along NUM_LEDS at 20BPM (60s/20=3s) in phase with millis().

If you just want a quarter cycle, (0.75s), you need some logic to reset every quarter cycle. You could do it with some logic to change the timebase or phase parameter every 7500ms.

I don't have your hardware, but something like this untested code might work:

void movingDotred() {
  const uint32_t interval = 1500UL; // half a 20BPM beat
  static unsigned long lastOffsetTime = 0;
  static uint32_t t0 = 0; // timebase
  static uint16_t phase = 1*32768/2; // which segment of sine
  if(millis() - lastOffsetTime >= interval){
    lastOffsetTime += interval;
    t0 = millis() ; // update timebase
  }
  
  uint16_t sinBeat = beatsin16(20, 0, NUM_LEDS - 1, t0, phase);

  leds[sinBeat] = CRGB::Red;
  fadeToBlackBy(leds, NUM_LEDS, 1);
  FastLED.show();
}

This resets the timebase every half-beat of 20 BPM. The phase term selects where along the sine curve to start, with 90° or Pi/2 being the peak. At the 90° peak it changes it to a cosine curve, starting slow from maximum speeding up towards the midpoint, and stopping at the minimum. If you want to change the direction, start at the trough: 270°, 3Pi/2, or phase = 332768/2.

Wow. It looks like you really put some thought into this.
I'll try it out and see what happens.

I just put your code into Wokwi and fiddled with it a bit. I was curious about the timestamp and phase parameters.

If you don't care about time and cycle continuously, the sine and cosine behavior isn't any different, but if you want to catch the peaks and scan from one extreme to the other, the timing matters. From the top to bottom of a sine curve, you need to go from an angle of 90° to 270°, which is half a cycle and 90° out of phase. My code uses a millis() timer to do half of a cycle, stores the millis() timestamp for use in the timestamp parameter, and uses a 90° phase to shift to the descending part of the sine curve.

1 Like

That's slick.
I thought I just wanted 1/4 cycle, 0 degrees to 90. Isn't a full cycle, on a strip, out(0-90), back(90-180), out(180-270), then back(270-360)?
Why did you use a ring vs strip? Just to match the trig functions, frequently associated with a circle and polar coordinates? Or because Wokwi doesn't have a strip?

Sine from 0-360° goes from -1 to 1, with the first 90° starting at 0 moving fast towards 1 in in the middle, then stops and heads down to -1. Since beatsin() maps the y=[-1,1] to [0,NUM_LEDS], you likely want to start at one of the extremes, y=-1 or +1, which is the [90,270] range or the [-90,+90] range.

I used the ring because the ring was easier to find and show lots of pixels compactly. I was only lighting half of the leds on it (NUM_LEDS = 40, versus the diagram.json file's "attrs": { "pixels": "80" }) because if you use the full ring, the ends butt together and it looks more like planned circular motion rather than a one-way sawtooth-ish motion.

ETA: The strip is called "wokwi-neopixel-canvas" and I think you can't just drag and drop it, you have to edit it into the diagram.json file. (per NeoPixel strip, e.g. · Issue #237 · wokwi/wokwi-features · GitHub)

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