kitesurfer1404 / WS2812FX

WS2812 FX Library for Arduino and ESP8266

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Adafruit Feather RP2040 SCORPIO - 8 Channel NeoPixel Driver Support ?

EherXtrem opened this issue · comments

Hello, I've been using this wonderful project for years now and I'm amazed every time. Good work. Most of the time I use ESP8266 (Wemos D1 Mini) and am very satisfied with it.

But now I've discovered the new Adafruit Feather RP2040 SCORPIO - 8 Channel NeoPixel Driver and I'm wondering if the WS2812FX library will work with it (exept the wifi-needed sketch like esp8266_webinterface etc.) ? Can a statement be made on this?

Thanks in advance.

Well, like all things software, "it depends". I read about the Adafruit Feather RP2040 SCORPIO when it was announced and thought it was a great idea. It includes a built-in 74HCT245 level shifter, a reset button, USB-C connector, some SPI Flash and even a LiPo battery charger, which are all great improvements over the original Pi Pico.

Let me first say that WS2812FX supports generic RP2040 boards already. The Adafruit_NeoPixel library (which WS2812FX extends) already includes a powerful driver for RP2040 boards, which uses the on-chip PIO processor to drive a single LED strip.

Of course the main benefit of the SCORPIO board is that it can drive up to EIGHT strips simultaneously, all while using the PIO processor so the main CPUs are free to do other things. To do that requires Adafruit's new Adafruit_NeoPXL8 library (which also extends their base Adafruit_NeoPixel library). And therein lies the problem. Since WS2812FX and Adafruit_NeoPXL8 both extend Adafruit_NeoPixel, they are not compatible. WS2812FX could probably be made to work by extending Adafruit_NeoPXL8 instead of Adafruit_NeoPixel, but that would involve some messy #if preprocessor directives in the code. Bleh!

But all is not lost! WS2812FX exposes the setCustomShow() function, which allows some flexibility in which library actually drives the pulses out to the LED strips. So it's possible to create a sketch with BOTH a ws2812fx instance and a Adafruit_NeoPXL8 instance, and have the ws2812fx instance generate the LED data, while the Adafruit_NeoPXL8 actually drives the LEDs. This is essentially what's happening in the ws2812fx_dma.ino example sketch.

I dragged out my trust Pi Pico and wired up two LED strips, and created this test sketch:

#include <WS2812FX.h>
#include <Adafruit_NeoPXL8.h>

#define LED_COUNT 64  // number of LEDs on each strip
#define NUM_PINS   2  // number of GPIO pins used (i.e. the number of LED strips)

int8_t pins[8] = { 22, 21, -1, -1, -1, -1, -1, -1 }; // GPIO pin numbers for each strip

WS2812FX ws2812fx = WS2812FX(LED_COUNT * NUM_PINS, pins[0], NEO_GRB + NEO_KHZ800);

Adafruit_NeoPXL8 neoPXL8(LED_COUNT, pins, NEO_GRB);

void setup() {
  Serial.begin(115200);

  // init both LED instances
  ws2812fx.init();
  neoPXL8.begin();

  // change the ws2812fx pixels array pointer to the neoPXL8 pixels array
  ws2812fx.setPixels(LED_COUNT * NUM_PINS, neoPXL8.getPixels());

  // set the custom show function
  ws2812fx.setCustomShow(myCustomShow);

  ws2812fx.setBrightness(32);

  ws2812fx.setSegment(0, 0,         LED_COUNT - 1,     FX_MODE_COMET, GREEN, 4000); // LED strip #1
  ws2812fx.setSegment(1, LED_COUNT, LED_COUNT * 2 - 1, FX_MODE_COMET, BLUE,  4000); // LED strip #2

  ws2812fx.start();
}

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

void myCustomShow(void) {
  if(neoPXL8.canShow()) {
    neoPXL8.show();
  }
}

It works well.

Hopefully that can get you started.

Many thanks for the valuable information. I will test and report as soon as the ordered hardware has arrived :)

Hello moose4lord, first of all a big thank you. Your code works fine - if all LED strips have the same number of LEDs. I'm currently trying to write an adjustment to any LED length across all 8 outputs, but it doesn't really want to work yet.

Hello, me again. I made a simple test for all 8 outputs. To keep it simple with the same number of LEDs on all outputs. Unfortunately, the following code does not work consistently on all outputs. Am I making a fundamental mistake? Who can help?

#include <WS2812FX.h>
#include <Adafruit_NeoPXL8.h>

#define LED_COUNT 4 // number of LEDs on each strip
#define NUM_PINS 8 // number of GPIO pins used (i.e. the number of LED strips)

int8_t pins[8] = { 23, 22, 21, 20, 19, 18, 17, 16 }; // GPIO pin numbers for each strip

WS2812FX ws2812fx = WS2812FX(LED_COUNT * NUM_PINS, pins[0], NEO_GRB + NEO_KHZ800);

Adafruit_NeoPXL8 neoPXL8(LED_COUNT, pins, NEO_GRB);

void setup() {
Serial.begin(115200);

// init both LED instances
ws2812fx.init();
neoPXL8.begin();

// change the ws2812fx pixels array pointer to the neoPXL8 pixels array
ws2812fx.setPixels(LED_COUNT * NUM_PINS, neoPXL8.getPixels());

// set the custom show function
ws2812fx.setCustomShow(myCustomShow);

ws2812fx.setBrightness(32);

ws2812fx.setSegment(0, 0, 4 - 1, FX_MODE_LARSON_SCANNER, GREEN, 1000); // LED strip #1
ws2812fx.setSegment(1, 4, 8 - 1, FX_MODE_LARSON_SCANNER, BLUE, 1000); // LED strip #2
ws2812fx.setSegment(2, 8, 12 - 1, FX_MODE_LARSON_SCANNER, RED, 1000); // LED strip #3
ws2812fx.setSegment(3, 12, 16 - 1, FX_MODE_RAINBOW_CYCLE, BLUE, 1000); // LED strip #4
ws2812fx.setSegment(4, 16, 20 - 1, FX_MODE_MULTI_STROBE, BLUE, 1000); // LED strip #5
ws2812fx.setSegment(5, 24, 28 - 1, FX_MODE_DUAL_SCAN, GREEN, 1000); // LED strip #6
ws2812fx.setSegment(6, 28, 32 - 1, FX_MODE_DUAL_SCAN, RED, 1000); // LED strip #7
ws2812fx.setSegment(7, 32, 36 - 1, FX_MODE_MULTI_STROBE, BLUE, 1000); // LED strip #8

ws2812fx.start();
}

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

void myCustomShow(void) {
if(neoPXL8.canShow()) {
neoPXL8.show();
}
}

The Adafruit_NeoPXL8 strandtest example sketch says to use
int8_t pins[8] = { 16, 17, 18, 19, 20, 21, 22, 23 };
for the RP2040 SCORPIO board. Not sure if the pin order makes a difference, but you may want to try that to see if it makes a difference.

There's also an error in the setSegment statements, where you forgot the segment that drives LEDs 20-23. The setSegment statements should be this:

ws2812fx.setSegment(0,  0,  4 - 1, FX_MODE_LARSON_SCANNER, GREEN, 1000); // LED strip #1
ws2812fx.setSegment(1,  4,  8 - 1, FX_MODE_LARSON_SCANNER, BLUE, 1000); // LED strip #2
ws2812fx.setSegment(2,  8, 12 - 1, FX_MODE_LARSON_SCANNER, RED, 1000); // LED strip #3
ws2812fx.setSegment(3, 12, 16 - 1, FX_MODE_RAINBOW_CYCLE, BLUE, 1000); // LED strip #4
ws2812fx.setSegment(4, 16, 20 - 1, FX_MODE_MULTI_STROBE, BLUE, 1000); // LED strip #5
ws2812fx.setSegment(5, 20, 24 - 1, FX_MODE_DUAL_SCAN, GREEN, 1000); // LED strip #6
ws2812fx.setSegment(6, 24, 28 - 1, FX_MODE_DUAL_SCAN, RED, 1000); // LED strip #7
ws2812fx.setSegment(7, 28, 32 - 1, FX_MODE_MULTI_STROBE, BLUE, 1000); // LED strip #8

Other than that, it looks OK to me. What do you mean "does not work consistently on all outputs"? What's it doing?

Oh, thanks for correcting the error. Too many numbers after a long day at work :)
Now all 8 outputs are working fine :)

Is there a way to individually define the number of pins per output and then just call in the segments (instead of typing in the exact numbers)?

#define LED_COUNT1 20
#define LED_COUNT2 30
#define LED_COUNT3 40
#define LED_COUNT4 50
#define LED_COUNT5 60
#define LED_COUNT6 10
#define LED_COUNT7 90
#define LED_COUNT8 50

The Adafruit_NeoPXL8 lib requires memory be allocated as if all the strips were the same length, probably to make it easier to program the PIO driver. But the WS2812FX segments should be able to easily program the strips as if their lengths were different. I think you're on the right track with the individual #defines for each segment. Something like this maybe:

ws2812fx.setSegment(0, LED_COUNT * 0, LED_COUNT * 0 + LED_COUNT1 - 1, FX_MODE_LARSON_SCANNER, GREEN, 1000); // LED strip #1
ws2812fx.setSegment(1, LED_COUNT * 1, LED_COUNT * 1 + LED_COUNT2 - 1, FX_MODE_LARSON_SCANNER, BLUE, 1000); // LED strip #2
ws2812fx.setSegment(2, LED_COUNT * 2, LED_COUNT * 2 + LED_COUNT3 - 1, FX_MODE_LARSON_SCANNER, RED, 1000); // LED strip #3
ws2812fx.setSegment(3, LED_COUNT * 3, LED_COUNT * 3 + LED_COUNT4 - 1, FX_MODE_RAINBOW_CYCLE, BLUE, 1000); // LED strip #4
ws2812fx.setSegment(4, LED_COUNT * 4, LED_COUNT * 4 + LED_COUNT5 - 1, FX_MODE_MULTI_STROBE, BLUE, 1000); // LED strip #5
ws2812fx.setSegment(5, LED_COUNT * 5, LED_COUNT * 5 + LED_COUNT6 - 1, FX_MODE_DUAL_SCAN, GREEN, 1000); // LED strip #6
ws2812fx.setSegment(6, LED_COUNT * 6, LED_COUNT * 6 + LED_COUNT7 - 1, FX_MODE_DUAL_SCAN, RED, 1000); // LED strip #7
ws2812fx.setSegment(7, LED_COUNT * 7, LED_COUNT * 7 + LED_COUNT8 - 1, FX_MODE_MULTI_STROBE, BLUE, 1000); // LED strip #8

Kind of messy, but the math works. :)

Wow! It seems to be working. I'll test it tomorrow and report back!

#include <WS2812FX.h>
#include <Adafruit_NeoPXL8.h>

#define LED_COUNT1 4 
#define LED_COUNT2 30
#define LED_COUNT3 40
#define LED_COUNT4 50 
#define LED_COUNT5 60
#define LED_COUNT6 10
#define LED_COUNT7 90
#define LED_COUNT8 4 

#define LED_COUNT 288 // sum of all LEDs in all segments
#define NUM_PINS   8  // number of GPIO pins used (i.e. the number of LED strips)

int8_t pins[8] = { 23, 22, 21, 20, 19, 18, 17, 16 }; // GPIO pin numbers for each strip

WS2812FX ws2812fx = WS2812FX(LED_COUNT * NUM_PINS, pins[0], NEO_GRB + NEO_KHZ800);

Adafruit_NeoPXL8 neoPXL8(LED_COUNT, pins, NEO_GRB);

void setup() {
  Serial.begin(115200);

  // init both LED instances
  ws2812fx.init();
  neoPXL8.begin();

  // change the ws2812fx pixels array pointer to the neoPXL8 pixels array
  ws2812fx.setPixels(LED_COUNT * NUM_PINS, neoPXL8.getPixels());

  // set the custom show function
  ws2812fx.setCustomShow(myCustomShow);

  ws2812fx.setBrightness(32);
// setSegment(segment index, start LED, stop LED, mode, color, speed, reverse);
ws2812fx.setSegment(0, LED_COUNT * 0, LED_COUNT * 0 + LED_COUNT1 - 1, FX_MODE_LARSON_SCANNER, GREEN, 1000); // LED strip #1
ws2812fx.setSegment(1, LED_COUNT * 1, LED_COUNT * 1 + LED_COUNT2 - 1, FX_MODE_LARSON_SCANNER, BLUE, 1000); // LED strip #2
ws2812fx.setSegment(2, LED_COUNT * 2, LED_COUNT * 2 + LED_COUNT3 - 1, FX_MODE_LARSON_SCANNER, RED, 1000); // LED strip #3
ws2812fx.setSegment(3, LED_COUNT * 3, LED_COUNT * 3 + LED_COUNT4 - 1, FX_MODE_RAINBOW_CYCLE, BLUE, 1000); // LED strip #4
ws2812fx.setSegment(4, LED_COUNT * 4, LED_COUNT * 4 + LED_COUNT5 - 1, FX_MODE_MULTI_STROBE, BLUE, 1000); // LED strip #5
ws2812fx.setSegment(5, LED_COUNT * 5, LED_COUNT * 5 + LED_COUNT6 - 1, FX_MODE_DUAL_SCAN, GREEN, 1000); // LED strip #6
ws2812fx.setSegment(6, LED_COUNT * 6, LED_COUNT * 6 + LED_COUNT7 - 1, FX_MODE_LARSON_SCANNER, BLUE, 1000); // LED strip #7
ws2812fx.setSegment(7, LED_COUNT * 7, LED_COUNT * 7 + LED_COUNT8 - 1, FX_MODE_LARSON_SCANNER, RED, 1000); // LED strip #8


  ws2812fx.start();
}

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

void myCustomShow(void) {
  if(neoPXL8.canShow()) {
    neoPXL8.show();
  }
}

Hey, that's great!

I'll mention your value for LED_COUNT doesn't seem to be correct. It should be the number of LEDs in your longest strip, which is 90 in your case.

#define LED_COUNT 90 // maximum number of LEDs per strip

It doesn't hurt to set it to 288, but the sketch is allocating more memory for the LEDs than it needs to.

  1. It works like a charm. Big THX :)
  2. moose4lord = WS2812FX-Master :)
  3. I will upload a YT-Video to show the magic soon :)
  4. Adafruit Feather RP2040 SCORPIO - 8 Channel NeoPixel and WS2812FX are friends now :)
#include <WS2812FX.h>
#include <Adafruit_NeoPXL8.h>

#define LED_COUNT1 4
#define LED_COUNT2 30
#define LED_COUNT3 32
#define LED_COUNT4 50
#define LED_COUNT5 60
#define LED_COUNT6 10
#define LED_COUNT7 90
#define LED_COUNT8 4

#define LED_COUNT 90  // maximum number of LEDs @ longest strip
#define NUM_PINS 8    // number of GPIO pins used (i.e. the number of LED strips)

int8_t pins[8] = { 23, 22, 21, 20, 19, 18, 17, 16 };  // GPIO pin numbers for each strip

WS2812FX ws2812fx = WS2812FX(LED_COUNT * NUM_PINS, pins[0], NEO_GRB + NEO_KHZ800);

Adafruit_NeoPXL8 neoPXL8(LED_COUNT, pins, NEO_GRB);

void setup() {
  Serial.begin(115200);

  // init both LED instances
  ws2812fx.init();
  neoPXL8.begin();

  // change the ws2812fx pixels array pointer to the neoPXL8 pixels array
  ws2812fx.setPixels(LED_COUNT * NUM_PINS, neoPXL8.getPixels());

  ws2812fx.setBrightness(32);
  // setSegment(segment index, start LED, stop LED, mode, color, speed, reverse);
  ws2812fx.setSegment(0, LED_COUNT * 0, LED_COUNT * 0 + LED_COUNT1 - 1, FX_MODE_BLINK, COLORS(ORANGE, PURPLE), 1000, false);            // LED strip #1
  ws2812fx.setSegment(1, LED_COUNT * 1, LED_COUNT * 1 + LED_COUNT2 - 1, FX_MODE_LARSON_SCANNER, BLUE, 1000, false);                     // LED strip #2
  ws2812fx.setSegment(2, LED_COUNT * 2, LED_COUNT * 2 + LED_COUNT3 - 1, FX_MODE_COMET, COLORS(YELLOW, DIM(GREEN)), 1000, FADE_XXSLOW);  // LED strip #3
  ws2812fx.setSegment(3, LED_COUNT * 3, LED_COUNT * 3 + LED_COUNT4 - 1, FX_MODE_RAINBOW_CYCLE, BLUE, 1000, false);                      // LED strip #4
  ws2812fx.setSegment(4, LED_COUNT * 4, LED_COUNT * 4 + LED_COUNT5 - 1, FX_MODE_MULTI_STROBE, BLUE, 1000, false);                       // LED strip #5
  ws2812fx.setSegment(5, LED_COUNT * 5, LED_COUNT * 5 + LED_COUNT6 - 1, FX_MODE_RAINBOW_CYCLE, BLUE, 1000, false);                      // LED strip #6
  ws2812fx.setSegment(6, LED_COUNT * 6, LED_COUNT * 6 + LED_COUNT7 - 1, FX_MODE_LARSON_SCANNER, BLUE, 1000, false);                     // LED strip #7
  ws2812fx.setSegment(7, LED_COUNT * 7, LED_COUNT * 7 + LED_COUNT8 - 1, FX_MODE_LARSON_SCANNER, RED, 1000, false);                      // LED strip #8


  ws2812fx.start();
}

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

void myCustomShow(void) {
  if (neoPXL8.canShow()) {
    neoPXL8.show();
  }
}

Click me: WS2812FX @ RP2040 Scorpio

Big THX @ moose4lord

PS: a native rp2040 scorpio support would be cool :)

Nice! Is this going to be some kind of costume when you're done?

Yes :) My wife is bodyartist :)

Click: Some Blink-Blink

Wow, that's fantastic! Thanks for sharing.

You are welcome. It's my pleasure.

Is there a thread with ws2812fx compartible hardware?

WS2812FX is based on the Adafruit_NeoPixel library. Here's a link to Adafruits supported hardware list:
https://github.com/adafruit/Adafruit_NeoPixel#supported-chipsets

Is there a realistic chance that the Feather RP2040 SCORPIO will get native support for WS2812Fx?

Unfortunately, I would say "no". The Scorpio and Adafruit_NeoPXL8 library are a powerful combination, but also pretty specific. It only works with the RP2040's PIO peripheral, and the assumption that all the LED strips are the same length is kind of a pain. If all the strips are NOT the same length, you have to do some pretty kludgy programming to workaround it, as you've seen.

If native support were added, what do you think the programming interface would be? Can you create a mock-up of what a sketch would look like?

Ideally, nothing would change from current programming via the Arduino IDE. Instead of an ESP8266 or ESP32, simply connect the RP2040 SCORPIO 8 Channel NeoPixel Driver and upload the standard sketches as usual. That would be wonderful and would combine the best of both worlds - the flexibility of the WS2812FX project and the hardware power of the 8 x 5V shifted RP2040 SCORPIO outputs without the "pretty kludgy programming". Ideal for beginners as well as professionals :)

Yeah, I hear what you're saying. Unfortunately, Adafruit didn't integrate the Scorpio features into their regular Adafruit_Neopixel library, instead building the Scorpio functionality into a new NeoPXL8 library (and their existing ZeroDMA library). I'm guessing they did not want to add complexity to Adafruit_Neopixel for such a special use case, and I can totally understand that reasoning.

If you're willing to take a stab at adding native support to WS2812FX, go for it. It is open source after all. But for me, it's just not worth the effort, especially since the customShow() workaround we've created seems to work pretty well without too much fuss.

Hardware for this great software couldn't be better. Such a pity :(