Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Need help with custom effect #357

Open
Bouni opened this issue Jul 17, 2024 · 5 comments
Open

Need help with custom effect #357

Bouni opened this issue Jul 17, 2024 · 5 comments

Comments

@Bouni
Copy link

Bouni commented Jul 17, 2024

Hi,

I want to create a custom effect that looks like one of those fancy nw car blinkers where the length fills up with orange and then altogether turns off. Its almsot what the color_whipe effect does but that "empties" the length instead of turning it of at once.

I looked into the source of the color whipe effect, here: https://github.com/kitesurfer1404/WS2812FX/blob/master/src/modes_funcs.cpp#L84-L106

I also looked at a lot of custom effects and came up with this:

#include <WS2812FX.h>

#define LED_COUNT 30
#define LED_PIN 3

#define LEFT 0
#define CENTER 1
#define RIGHT 2

WS2812FX ws2812fx = WS2812FX(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

uint16_t CarBlinkerEffect(void) { 
  WS2812FX::Segment* seg = ws2812fx.getSegment(); // get the current segment
  WS2812FX::Segment_runtime* segrt = ws2812fx.getSegmentRuntime(); // get current segment runtime
  int seglen = seg->stop - seg->start + 1;

  if(segrt->counter_mode_step < seglen) { // Fill up the segment one by one
    ws2812fx.setPixelColor(segrt->counter_mode_step, ORANGE);
    segrt->counter_mode_step = (segrt->counter_mode_step + 1) % seglen;
  }
  else { // If segment is full, turn all black again
    for(uint16_t i=seg->stop; i>seg->start; i--) {
      ws2812fx.setPixelColor(i, BLACK);
    }
    segrt->counter_mode_step = 0;
  }
  return seg->speed; 
}

void setup() {
  Serial.begin(115200);
  ws2812fx.init();
  ws2812fx.setCustomMode(CarBlinkerEffect);
  ws2812fx.setSegment(LEFT,  0,  9, FX_MODE_CUSTOM, BLACK, 50, false); // segment 0 is leds 0 - 9
  ws2812fx.setSegment(CENTER, 10, 19, FX_MODE_STATIC,  BLACK, 1000, false); // segment 1 is leds 10 - 19
  ws2812fx.setSegment(RIGHT, 20, 29, FX_MODE_STATIC, BLACK, 1000, false);  // segment 2 is leds 20 - 29
  ws2812fx.start();
}

void loop() {
  ws2812fx.service();
}

The segment of 10 LEDs fills up as expected, but then nothing more happens and I don't understand why.

Can somebody help me figuring out where I made a mistake?

@Bouni
Copy link
Author

Bouni commented Jul 17, 2024

I figured it out 🥳

uint16_t CarBlinkerEffect(void) { // random chase
  WS2812FX::Segment* seg = ws2812fx.getSegment(); // get the current segment
  WS2812FX::Segment_runtime* segrt = ws2812fx.getSegmentRuntime(); // get current segment runtime
  int seglen = seg->stop - seg->start + 1;
  bool isReverse = (seg->options & REVERSE) == REVERSE;

  if(segrt->counter_mode_step < seglen-1) {
    if(isReverse) {
      ws2812fx.setPixelColor(segrt->counter_mode_step, ORANGE);
    } else {
      ws2812fx.setPixelColor(seg->stop - segrt->counter_mode_step, ORANGE);
    }
    segrt->counter_mode_step = (segrt->counter_mode_step + 1) % seglen;
  }
  else {
    ws2812fx.fill(BLACK, seg->start, seglen);
    segrt->counter_mode_step = 0;
  }
  if(segrt->counter_mode_step == 0) ws2812fx.setCycle();
  return seg->speed; 
}

@moose4lord
Copy link
Collaborator

Yay! Good for you. Nice debug work.

However, I did notice that the first LED (or the last LED if REVERSE is enabled) does not illuminate. I think one more tweak is needed. Maybe this:

uint16_t CarBlinkerEffect(void) { // random chase
  WS2812FX::Segment* seg = ws2812fx.getSegment(); // get the current segment
  WS2812FX::Segment_runtime* segrt = ws2812fx.getSegmentRuntime(); // get current segment runtime
  int seglen = seg->stop - seg->start + 1;
  bool isReverse = (seg->options & REVERSE) == REVERSE;

//if(segrt->counter_mode_step < seglen - 1) {
  if(segrt->counter_mode_step < seglen) {
    if(isReverse) {
      ws2812fx.setPixelColor(segrt->counter_mode_step, ORANGE);
    } else {
      ws2812fx.setPixelColor(seg->stop - segrt->counter_mode_step, ORANGE);
    }
//  segrt->counter_mode_step = (segrt->counter_mode_step + 1) % seglen;
    segrt->counter_mode_step++;
  } else {
    ws2812fx.fill(BLACK, seg->start, seglen);
    segrt->counter_mode_step = 0;
  }
  if(segrt->counter_mode_step == 0) ws2812fx.setCycle();
  return seg->speed; 
}

But you probably already figured it out. :)

@Bouni
Copy link
Author

Bouni commented Jul 18, 2024

Actually I didn't even notice 😅

But I added your tweaks anyway as they are probably correct.

I have one more question, heres my complete code:

#include <WS2812FX.h>

#define LED_COUNT 300
#define LED_PIN 3

#define LEFT 0
#define CENTER 1
#define RIGHT 2

WS2812FX ws2812fx = WS2812FX(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

uint16_t CarBlinkerEffect(void) { // Car blinker effect (Audi & co)
  WS2812FX::Segment* seg = ws2812fx.getSegment(); 
  WS2812FX::Segment_runtime* segrt = ws2812fx.getSegmentRuntime(); 
  int seglen = seg->stop - seg->start + 1;
  bool isReverse = (seg->options & REVERSE) == REVERSE;

  if(segrt->counter_mode_step < seglen) {
    if(isReverse) {
      ws2812fx.setPixelColor(seg->start + segrt->counter_mode_step, ORANGE);
    } else {
      ws2812fx.setPixelColor(seg->stop - segrt->counter_mode_step, ORANGE);
    }
    segrt->counter_mode_step++;
  }
  else {
    ws2812fx.fill(BLACK, seg->start, seglen);
    segrt->counter_mode_step = 0;
  }
  if(segrt->counter_mode_step == 0) ws2812fx.setCycle();
  return seg->speed; 
}

void blink_left() {
  ws2812fx.resetSegments();
  ws2812fx.resetSegmentRuntimes();
  ws2812fx.strip_off();
  ws2812fx.setSegment(0,  0,  9, FX_MODE_CUSTOM, BLACK, 60, false);
}

void blink_right() {
  ws2812fx.resetSegments();
  ws2812fx.resetSegmentRuntimes();
  ws2812fx.strip_off();
  ws2812fx.setSegment(0,  20,  29, FX_MODE_CUSTOM, BLACK, 60, true);
}

void full_color(int color) {
  ws2812fx.resetSegments();
  ws2812fx.resetSegmentRuntimes();
  ws2812fx.strip_off();
  ws2812fx.setSegment(0,  0,  29, FX_MODE_STATIC, color, 60, false);
}

void full_blink(int color) {
  ws2812fx.resetSegments();
  ws2812fx.resetSegmentRuntimes();
  ws2812fx.strip_off();
  ws2812fx.setSegment(0,  0,  29, FX_MODE_BLINK, color, 1000, false);
}

void off() {
  ws2812fx.resetSegments();
  ws2812fx.resetSegmentRuntimes();
  ws2812fx.strip_off();
}

void setup() {
  Serial.begin(115200);
  ws2812fx.init();
  ws2812fx.setCustomMode(CarBlinkerEffect);
  ws2812fx.start();
  off();
}

void loop() {
  while (Serial.available() > 0) {
    String cmd;
    cmd = Serial.readString();
    cmd.trim();
    if(cmd == "bll") {
        blink_left();
        Serial.println("Start Left");
    }
    if(cmd == "blr") {
        blink_right();
        Serial.println("Start Right");
    }
    if(cmd == "g") {
        full_color(GREEN);
        Serial.println("Green");
    }
    if(cmd == "r") {
        full_color(RED);
        Serial.println("Red");
    }
    if(cmd == "b") {
        full_color(BLUE);
        Serial.println("Blue");
    }
    if(cmd == "y") {
        full_color(YELLOW);
        Serial.println("Yellow");
    }
    if(cmd == "bg") {
        full_blink(GREEN);
        Serial.println("Green");
    }
    if(cmd == "br") {
        full_blink(RED);
        Serial.println("Red");
    }
    if(cmd == "bb") {
        full_blink(BLUE);
        Serial.println("Blue");
    }
    if(cmd == "by") {
        full_blink(YELLOW);
        Serial.println("Yellow");
    }
    if(cmd == "0") {
        off();
        Serial.println("Off");
    }
  }
  ws2812fx.service();
}

This works as expected but maybe can be done better.
The only thing that bothers me is that when I switch between the effects, it takes like half a scond until the next effect starts.

Is there something I can do about that?

@Bouni
Copy link
Author

Bouni commented Jul 18, 2024

I'm an idiot. After writing my answer I realized that readString amkes use of the Serial timeout which defaults to 1000ms.
Setting that to 100ms with Serial.setTimeout(100); makes it respond as expected 🤦🏽‍♂️

@moose4lord
Copy link
Collaborator

Yeah, those blocking Serial I/O statements are a pain.
Setting the Serial timeout to 100 works, but it still blocks for 100ms. To up your Arduino mojo, the Serial Input Basics tutorial has some nice examples of non-blocking Serial I/O.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants