kitesurfer1404 / WS2812FX

WS2812 FX Library for Arduino and ESP8266
MIT License
1.6k stars 346 forks source link

Virtual Strip Example - Customize with Reverse #239

Closed henmel closed 4 years ago

henmel commented 4 years ago

I am trying to customize ws2812fx_virtual_strip example to reverse one of the strips, so the effects appear continuous from one strip to the other, as they are both wired from the same end. I have attached a modified version of the example, but it only appears to work with some effects, but not others. Can you please take a look and see what I am doing wrong? ws2812fx_virtual_strip_testreverse-200807a.zip

Thank you for a great library...

moose4lord commented 4 years ago

You're on the right track, but I think you need to create a separate buffer for the "reverse" data. The data in the virtual strip is always in "forward" order, and you can't really muck with that order or it will mess up the effect. So I would create a separate buffer for the second physical strip:

WS2812FX ws2812fx_p2 = WS2812FX(LED_COUNT_P2, LED_PIN_P2, NEO_GRB + NEO_KHZ800, 1, 1);

And remove the code that reassigns the second physical strip's buffer to the virtual strip:

ws2812fx_p2.setPixels(LED_COUNT_P2, ws2812fx_v1.getPixels() + ... // remove this line

Then create the custom show() function that copies the virtual strip's pixel data to the second physical strip in reverse order. This is what I came up with:

void myCustomShow(void) {
  ws2812fx_p1.Adafruit_NeoPixel::show(); // show zone 1

  // copy the pixel data from the virtual buffer to the second physical buffer in reverse order
  uint8_t bytesPerPixel = ws2812fx_p2.getNumBytesPerPixel(); // 3=RGB, 4=RGBW
  uint8_t *fromPtr = ws2812fx_v1.getPixels() + (LED_COUNT_P1 * bytesPerPixel);
  uint8_t *toPtr = ws2812fx_p2.getPixels();
  copyPixelsRev(fromPtr, toPtr, LED_COUNT_P2, bytesPerPixel);

  ws2812fx_p2.Adafruit_NeoPixel::show(); // show zone 2
}

void copyPixelsRev(uint8_t *fromPtr, uint8_t *toPtr, uint16_t numPixels, uint8_t bytesPerPixel) {
  fromPtr += (numPixels - 1) * bytesPerPixel; // update fromPtr to point to the last pixel's data
  for(uint16_t i=0; i<numPixels; i++) {
    for(uint16_t j=0; j<bytesPerPixel; j++) {
      toPtr[j] = fromPtr[j];
    }
    toPtr += bytesPerPixel;
    fromPtr -= bytesPerPixel;
  }
}
henmel commented 4 years ago

Thank you very much! Just the bit I needed. I actually changed it a bit and removed the: ws2812fx_p1.setPixels(LED_COUNT_P1, ws2812fx_v1.getPixels()); and used memmove for both of the zones. Not sure if this is the best way to do this, but seems to work ok.

void copyPixelData() {
    uint8_t reversePixelData[LED_COUNT_Z2 * 3]; // assumes RGB LEDs, multiply by 4 for RGBW LEDs
    uint8_t numBytesPerPixel = ws2812fx_z2.getNumBytesPerPixel();
    uint16_t numBytesPerSegment = LED_COUNT_Z2 * numBytesPerPixel;

    // pre-calculate the reverse pixel data for zone 1 segment 0
    uint8_t *forwardPtr = ws2812fx.getPixels();
    uint8_t *reversePtr = reversePixelData + ((LED_COUNT_Z2 - 1) * numBytesPerPixel);
    for(uint16_t i=0; i < numBytesPerSegment; i+=numBytesPerPixel) {
      memmove(reversePtr - i, forwardPtr + i, numBytesPerPixel);
    }

    // copy virtual segment data to zone 1
    memmove(ws2812fx_z1.getPixels(), ws2812fx.getPixels() + (numBytesPerSegment), LED_COUNT_Z1 * ws2812fx_z1.getNumBytesPerPixel());
    ws2812fx_z1.Adafruit_NeoPixel::show(); // reshow zone 1

    // copy virtual segment data to reverse zone 2
    memmove(ws2812fx_z2.getPixels(), reversePixelData, numBytesPerSegment);
    ws2812fx_z2.Adafruit_NeoPixel::show(); // reshow zone 2

}

Thanks again for the great library and your quick help.

henmel commented 4 years ago

I have a follow up question. I have been trying to copy effects from one strip to another, but with an offset. For example, I have two strips, 30 LEDs each. I would like to copy the effects from strip 1 to strip 2 with a 10 pixel offset. I tried a number of different ways to do the wrapping from strip 1, but did not have any luck. I was able to do this, but with two memmove steps. copying data before and after the offset, like this:

void myCustomShow(void) {
  ws2812fx.Adafruit_NeoPixel::show(); // show zone 1
  uint16_t pix_offset = 10;
  memmove(ws2812fx_z2.getPixels(), ws2812fx.getPixels()+(pix_offset*ws2812fx.getNumBytesPerPixel()), (LED2_COUNT-pix_offset)*ws2812fx_z2.getNumBytesPerPixel());
  memmove(ws2812fx_z2.getPixels()+(((LED2_COUNT-pix_offset)*ws2812fx_z2.getNumBytesPerPixel())), ws2812fx.getPixels(), (pix_offset)*ws2812fx_z2.getNumBytesPerPixel());
  ws2812fx_z2.Adafruit_NeoPixel::show(); // show zone 2
}

Is there another, better way of dong this?

Again, thanks for all of your help...

moose4lord commented 4 years ago

That seems like a good way to do it to me. memmoves are very fast and more efficient than a fancy for loop.