kitesurfer1404 / WS2812FX

WS2812 FX Library for Arduino and ESP8266
MIT License
1.58k stars 343 forks source link

WS2812FX virtual strip and mixing GRB and BGR #345

Closed happy77fr closed 7 months ago

happy77fr commented 7 months ago

Hello,

I couldn't find a way to have a virtual strip mixing GRB and BGR led strips. The virtual strip works with only one color scheme.

Is there a way to solve this issue?

Here is the code:

`WS2812FX ws2812fx_v1 = WS2812FX(LED_COUNT_P1 + LED_COUNT_P2 + LED_COUNT_P3 + LED_COUNT_P4, LED_PIN_V1, NEO_GRB + NEO_KHZ800, 1, 1);

WS2812FX ws2812fx_p1 = WS2812FX(1, LED_PIN_P1, NEO_GRB + NEO_KHZ800, 1, 1); WS2812FX ws2812fx_p2 = WS2812FX(1, LED_PIN_P2, NEO_GRB + NEO_KHZ800, 1, 1); WS2812FX ws2812fx_p3 = WS2812FX(1, LED_PIN_P3, NEO_BGR + NEO_KHZ800, 1, 1); WS2812FX ws2812fx_p4 = WS2812FX(1, LED_PIN_P4, NEO_BGR + NEO_KHZ800, 1, 1);`

moose4lord commented 7 months ago

Unfortunately, the virtual strip example sketch doesn't do any translation between virtual and physical strips. It just maps the virtual data array directly to the physical strip's data array.

I played around with it a little and you can do a messy data copy between virtual and physical strips that handles different RGB types. This is what I came up with:

#include <WS2812FX.h>

#define LED_PIN_V1     22  // virtual digital pin used to drive the virtual LED strip
#define LED_PIN_P1     22  // physical digital pin used to drive the first physical LED strip
#define LED_PIN_P2     21  // physical digital pin used to drive the second physical LED strip
#define LED_PIN_P3     19  // physical digital pin used to drive the second physical LED strip
#define LED_PIN_P4     17  // physical digital pin used to drive the second physical LED strip

#define LED_COUNT_P1   16  // number of LEDs on the first physical strip
#define LED_COUNT_P2   16  // number of LEDs on the second physical strip
#define LED_COUNT_P3   16  // number of LEDs on the third physical strip
#define LED_COUNT_P4   16  // number of LEDs on the fourth physical strip

#define LED_TYPE_V1    NEO_GRB + NEO_KHZ800  // LED type of the virtual strip
#define LED_TYPE_P1    NEO_GRB + NEO_KHZ800  // LED type of the first physical strip
#define LED_TYPE_P2    NEO_GRB + NEO_KHZ800  // LED type of the second physical strip
#define LED_TYPE_P3    NEO_BGR + NEO_KHZ800  // LED type of the third physical strip
#define LED_TYPE_P4    NEO_BGR + NEO_KHZ800  // LED type of the fourth physical strip

#define DATA_PIN_MOSFET 23

// create an instance of one virtual strip and four physical strips.
// (Note the instances are created with support of only one segment and one
// segment_runtime, just so the sketch fits in an Arduino's limited SRAM.)
WS2812FX ws2812fx_v1 = WS2812FX(LED_COUNT_P1 + LED_COUNT_P2 + LED_COUNT_P3 + LED_COUNT_P4, LED_PIN_V1, LED_TYPE_V1, 1, 1);

WS2812FX ws2812fx_p1 = WS2812FX(LED_COUNT_P1, LED_PIN_P1, LED_TYPE_P1, 1, 1);
WS2812FX ws2812fx_p2 = WS2812FX(LED_COUNT_P2, LED_PIN_P2, LED_TYPE_P2, 1, 1);
WS2812FX ws2812fx_p3 = WS2812FX(LED_COUNT_P3, LED_PIN_P3, LED_TYPE_P3, 1, 1);
WS2812FX ws2812fx_p4 = WS2812FX(LED_COUNT_P4, LED_PIN_P4, LED_TYPE_P4, 1, 1);

void setup() {
#ifdef DATA_PIN_MOSFET
  pinMode(DATA_PIN_MOSFET, OUTPUT);    // MOSFET GPIO
  digitalWrite(DATA_PIN_MOSFET, LOW);  // MOSFET on
  delay(10);
#endif

  // initialize the virtual strip as you would any normal ws2812fx instance
  ws2812fx_v1.init();
  ws2812fx_v1.setBrightness(255);
  ws2812fx_v1.setSegment(0, 0, ws2812fx_v1.getLength()-1, FX_MODE_COMET, RED, 2000);
  ws2812fx_v1.start();

  // init the physical strip's GPIOs
  ws2812fx_p1.init();
  ws2812fx_p2.init();
  ws2812fx_p3.init();
  ws2812fx_p4.init();

  // config a custom show() function for the virtual strip, so pixel
  // data gets copied to the physical strip's data array
  ws2812fx_v1.setCustomShow(myCustomShow);
}

void loop() {
  // update the virtual strip's pixel data by calling service() as you normally would
  ws2812fx_v1.service();
}

void myCustomShow(void) {
  uint8_t *v_ptr = ws2812fx_v1.getPixels();

  // map first physical strip
  mapLEDs(ws2812fx_p1.getPixels(), v_ptr, ws2812fx_p1.getNumBytes(), LED_TYPE_P1, LED_TYPE_V1);

  // map second physical strip
  v_ptr += ws2812fx_p1.getNumBytes();
  mapLEDs(ws2812fx_p2.getPixels(), v_ptr, ws2812fx_p2.getNumBytes(), LED_TYPE_P2, LED_TYPE_V1);

  // map third physical strip
  v_ptr += ws2812fx_p2.getNumBytes();
  mapLEDs(ws2812fx_p3.getPixels(), v_ptr, ws2812fx_p3.getNumBytes(), LED_TYPE_P3, LED_TYPE_V1);

  // map fourth physical strip
  v_ptr += ws2812fx_p3.getNumBytes();
  mapLEDs(ws2812fx_p4.getPixels(), v_ptr, ws2812fx_p4.getNumBytes(), LED_TYPE_P4, LED_TYPE_V1);

  // Call the physical strip's show() function.
  // Note: the virtual strip's show() functions are never called.
  ws2812fx_p1.Adafruit_NeoPixel::show();
  ws2812fx_p2.Adafruit_NeoPixel::show();
  ws2812fx_p3.Adafruit_NeoPixel::show();
  ws2812fx_p4.Adafruit_NeoPixel::show();
}

// map the virtual strip data to the physical strip
void mapLEDs(uint8_t *p, uint8_t *v, uint16_t byteCnt, neoPixelType p_type, neoPixelType v_type) {
  if(p_type == v_type) {
    memmove(p, v, byteCnt); // if the v and p strips are the same type, just do a memcopy
  } else {
    uint8_t rOffset_p = (p_type >> 4) & 0b11; // types are different, so do the GRB to BGR mapping
    uint8_t gOffset_p = (p_type >> 2) & 0b11;
    uint8_t bOffset_p = p_type & 0b11;

    uint8_t rOffset_v = (v_type >> 4) & 0b11;
    uint8_t gOffset_v = (v_type >> 2) & 0b11;
    uint8_t bOffset_v = v_type & 0b11;

    for(int i=0; i < byteCnt; i += 3) {
      p[rOffset_p + i] = v[rOffset_v + i];
      p[gOffset_p + i] = v[gOffset_v + i];
      p[bOffset_p + i] = v[bOffset_v + i];
    }
  }
}

The Adafruit_NeoPixel lib doesn't expose the LED type parameter, so it's a bit of a hack to make it work.

happy77fr commented 7 months ago

Thank you! I couldn't test this tonight but I will come back soon

happy77fr commented 7 months ago

It works! Thank you