When I connect chain of LED strips to power from both ends, do I need to make the power line disconnect in the middle?

If I wanted a continuous run of neopixel strips about 30 meters long, what is the best way to power this? Should I get a bunch of 5v10A power supplies and power the LEDs every couple meters? Or would it be better to get a much larger power supply to power more at the same time?

Another question, would having a program control that many LEDs at the same time really slow down the performance of the software?

A beefy one, and then distribute power every meter or so. Much longer and you'll notice color differences.

Thanks @Moors7

Do you have a recommendation for a beefy power supply?

Kind of depends on how many actual LEDs youā€™re planning on driving. Then multiply that by 60mA. Add a safety margin, and find one that suits your needs :wink:

1 Like

I guess the most pressing question is if anyone knows if Particle Photon will be releasing a faster chip?

Right now, my animation really slows down when I add more LED lights I want to run by a single software/single Photon. What is the best way to control 2,000 LEDs without making the animation slow down?

The speed of the controller is secondary since the timing for the update is set by the NeoPixel protocol - it takes as long as it takes.
You may be able to employ multithreading, but that will probably mess up the NeoPixel timing again.

On the other hand, you could use AP102 LEDs. These are much faster, but also way more expensive :wink:

1 Like

I see.

I actually want to run my comet animation along 30 meters of continuous LEDs (144 LEDs/meter).

Would APA102 LEDs work for this?

You can do the math.

  • 32 bit per LED
  • 30 x 144 LEDs
  • one extra clock pulse per two LEDs to fully shift the data forward

adds up 140400 SPI clock cycles. With 2MHz SPI speed the whole data transfer would take less than 100ms.

2 Likes

Iā€™m new to electrical engineering. It seems like APA102 LEDs would be fine for my purposes if it would take less than 100ms for a come to run the entire 30 meters.

Iā€™m looking into Adafruitā€™s DotStar LEDs (seems these are their APA102). However, it doesnā€™t look like they have RGBW versions. I thought the white color looked really good on the RGBW neopixels I have. Do you have RGBW APA102 LEDs you would recommend?

Also, Iā€™m worried about porting code. Are DotStar LEDs easily portable from the code I wrote for Neopixels?

I guess there are RGBW LEDs that use a similarly fast protocol as the AP102s but I have no experience with them.

Or something like this with alternating RGB+W AP102
http://www.rgbledcolor.com/sale-8995198-apa102-rgbw-led-strip.html

Hmā€¦ seems that if speed is the most important thing to me, I should just go with DotStar LEDs.

What would you rate as the difficulty level of porting my code from neopixel library to a DotStar library for the Particle Photon?

My neopixel code:

#include <Particle.h>
#include <neopixel.h>

SYSTEM_MODE(AUTOMATIC)

const int PIXEL_COUNT = 1728;
const int PIXEL_PIN   = D1;
const int PIXEL_TYPE  = SK6812RGBW;

Adafruit_NeoPixel strip(PIXEL_COUNT, PIXEL_PIN, PIXEL_TYPE);

struct ANIMATION_DATA
{
  uint8_t g;
  uint8_t r;
  uint8_t b;
  int     pos;
  int     len;
};

// circular buffer with 100 slots will overwrite unfinished animations when full
const int      maxAnim  = 100;
int            animHead = 0;
int            animTail = 0;
ANIMATION_DATA anim[maxAnim];

const uint8_t  whiteAnim[] = 
{ 255, 200, 175, 160, 155, 150, 145, 140, 135, 130
, 125, 120, 115, 110, 105, 100,  95,  90,  85,  80
,  75,  70,  65,  60,  55,  50,  45,  40,  35,  30
,  25,  20,  15,  10,   5,   0,   0,   0,   0,   0
,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0
,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0
,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0
,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0
,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0
,   0,   0,   0,   0,   0,   0,   0 
};
const uint8_t  brightAnim[] = 
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
, 255, 255, 255, 255, 255, 255, 255, 255, 245, 240
, 240, 240, 240, 235, 230, 230, 230, 230, 225, 220
, 220, 220, 220, 215, 210, 210, 210, 210, 205, 200
, 200, 200, 200, 200, 195, 195, 190, 190, 190, 190
, 190, 195, 180, 180, 180, 175, 170, 165, 160, 155
, 150, 145, 140, 135, 130, 125, 120, 115, 110, 105
, 100,  95,  90,  85,  80,  75,  70 
};
const int      cometLen = sizeof(whiteAnim) / sizeof(whiteAnim[0]);
const uint32_t msRefresh = 0;
const int stepSpeed = 10;
static int animationStep = 0;

void setup()
{
  memset(anim, 0, sizeof(anim));

  strip.begin();
  strip.show(); // Initialize all pixels to 'off'
  Particle.function("led", addComet);
  //strip.setBrightness(30);
}

void loop()
{
  static uint32_t msDelay = 0;
  if (millis() - msDelay < msRefresh) return;
  msDelay = millis();
  
  static bool sparkleState = false;
  static int  sparklePixel = 0;

  if (animHead != animTail)
  {
    ANIMATION_DATA *a;
    for (int animationStep = animTail; animationStep != animHead; ) 
    {
      a = &anim[animationStep];
      if (a->len)
      {
        int px;

        //Draw the Comet
        for (int p = a->len - 1; p >= 0; p--)
        {
          px = constrain(a->pos - p, 0, PIXEL_COUNT - 1);
          strip.setColorDimmed(px, a->g, a->r, a->b, whiteAnim[p], brightAnim[p]);
        }

        //Erase the pixels behind comet as it moves forward  
        if (a->pos - a->len >= 0)
          for (int k = 0; k < stepSpeed; k++)
            strip.setPixelColor(a->pos - k - a->len, 0);

        //Make comet jump forward stepSpeed pixels at a time to make comet run seemingly faster
        if (a->pos < PIXEL_COUNT + stepSpeed)
          a->pos += stepSpeed;
        else
          a->len -= stepSpeed;

        if (a->len <= 0)
        {
          *a = { 0, 0, 0, 0, 0 };
          animTail++;
          animTail %= maxAnim;
        }
      }

      animationStep++;
      animationStep %= maxAnim;
    }
  }

  sparkleState = !sparkleState;  // flip state
  if (sparkleState)
  {
    sparklePixel = random(PIXEL_COUNT);
    strip.setPixelColor(sparklePixel, random(255), random(255), random(255), 100);
  }
  else
    strip.setPixelColor(sparklePixel, 0, 0, 0, 0);

  strip.show();
}

struct topic {
  char    title[16];
  uint8_t rgb[3];
};

const topic topicList[] = 
{ { "login"  , { 200, 200, 200 } }  // white
, { "idea"   , { 255, 255,   0 } }  // yellow
, { "comment", {   0,   0, 255 } }  // blue
, { "outcome", { 255,   0,   0 } }  // green
, { "project", { 255,   0,   0 } }  // green
, { "status" , { 100, 255,   0 } }  // orange
, { "step"   , {   0,  75, 255 } }  // purple
, { "vote"   , {   0, 255,   0 } }  // red
, { "view"   , { 105, 255, 200 } }  // pink
};
const int   topics = sizeof(topicList)/sizeof(topicList[0]);

int addComet(String command)
{
  int retVal = -1;

  for (int i = 0; i < topics; i++)
  {
    if (strcmp((const char*)command, topicList[i].title) == 0)
    {
      anim[animHead] = { topicList[i].rgb[0], topicList[i].rgb[1], topicList[i].rgb[2], 0, cometLen }; // purple
      Particle.publish(command);
      animHead++;
      animHead %= maxAnim;
      retVal = i;
      break;
    }
  }
  return retVal;
}

The AP102 data transfer is trivial, I donā€™t use any library but only SPI DMA transfer. So adapting your code should be trivial too.

1 Like

What do you mean by ā€œSPI DMA transferā€?

Would I not use the DotStar library for Particle Photon for the DotStar LEDs?

Nope. The library might offer some extra features for pixel manipulation and other conveniences, but as I said the protocol is that simple that you just need to fill an array with the data for your entire strip and then call SPI.transfer(yourArray, NULL, yourLEDcount); to have the hardware SPI push out the data.
If you also supply a callback function the whole thing will happen in the background without blocking your application code at all - thatā€™s possible due to DMA (Direct Memory Access)

volatile bool spiTransferReady = false;
void spiCallback(void) {
  spiTransferReady = true;
}

...
  SPI.transfer(yourArray, NULL, yourLEDcount, spiCallback);

https://docs.particle.io/reference/firmware/photon/#transfer-void-void-size_t-std-function-

Okay, I just received my DotStar LED strip.
I canā€™t tell which wire is the clock and which is the Data. Itā€™s not labeled.

Which is which?

And is this correct setup with photon microcontroller:
5v => VIN
GND => GND
Clock => A3
Data => A5

Nothing underneath either?
The strips I have have the CLK near the Vcc raild and MOSI near GND.

However, you can just try. swapping CLK and MOSI wonā€™t damage the LEDs you will just not get the light pattern you expect and hence know itā€™s wrong :wink:

Iā€™m trying to translate this comet code to my dotstar LED strip.

Is there a ā€˜setcolordimmedā€™ function for DotStar?

#include <Particle.h>
#include <neopixel.h>

SYSTEM_MODE(AUTOMATIC)

const int PIXEL_COUNT = 1728;
const int PIXEL_PIN   = D1;
const int PIXEL_TYPE  = SK6812RGBW;

Adafruit_NeoPixel strip(PIXEL_COUNT, PIXEL_PIN, PIXEL_TYPE);

struct ANIMATION_DATA
{
  uint8_t g;
  uint8_t r;
  uint8_t b;
  int     pos;
  int     len;
};

// circular buffer with 100 slots will overwrite unfinished animations when full
const int      maxAnim  = 100;
int            animHead = 0;
int            animTail = 0;
ANIMATION_DATA anim[maxAnim];

const uint8_t  whiteAnim[] = 
{ 255, 200, 175, 160, 155, 150, 145, 140, 135, 130
, 125, 120, 115, 110, 105, 100,  95,  90,  85,  80
,  75,  70,  65,  60,  55,  50,  45,  40,  35,  30
,  25,  20,  15,  10,   5,   0,   0,   0,   0,   0
,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0
,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0
,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0
,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0
,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0
,   0,   0,   0,   0,   0,   0,   0 
};
const uint8_t  brightAnim[] = 
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
, 255, 255, 255, 255, 255, 255, 255, 255, 245, 240
, 240, 240, 240, 235, 230, 230, 230, 230, 225, 220
, 220, 220, 220, 215, 210, 210, 210, 210, 205, 200
, 200, 200, 200, 200, 195, 195, 190, 190, 190, 190
, 190, 195, 180, 180, 180, 175, 170, 165, 160, 155
, 150, 145, 140, 135, 130, 125, 120, 115, 110, 105
, 100,  95,  90,  85,  80,  75,  70 
};
const int      cometLen = sizeof(whiteAnim) / sizeof(whiteAnim[0]);
const uint32_t msRefresh = 0;
const int stepSpeed = 10;
static int animationStep = 0;

void setup()
{
  memset(anim, 0, sizeof(anim));

  strip.begin();
  strip.show(); // Initialize all pixels to 'off'
  Particle.function("led", addComet);
  //strip.setBrightness(30);
}

void loop()
{
  static uint32_t msDelay = 0;
  if (millis() - msDelay < msRefresh) return;
  msDelay = millis();
  
  static bool sparkleState = false;
  static int  sparklePixel = 0;

  if (animHead != animTail)
  {
    ANIMATION_DATA *a;
    for (int animationStep = animTail; animationStep != animHead; ) 
    {
      a = &anim[animationStep];
      if (a->len)
      {
        int px;

        //Draw the Comet
        for (int p = a->len - 1; p >= 0; p--)
        {
          px = constrain(a->pos - p, 0, PIXEL_COUNT - 1);
          strip.setColorDimmed(px, a->g, a->r, a->b, whiteAnim[p], brightAnim[p]);
        }

        //Erase the pixels behind comet as it moves forward  
        if (a->pos - a->len >= 0)
          for (int k = 0; k < stepSpeed; k++)
            strip.setPixelColor(a->pos - k - a->len, 0);

        //Make comet jump forward stepSpeed pixels at a time to make comet run seemingly faster
        if (a->pos < PIXEL_COUNT + stepSpeed)
          a->pos += stepSpeed;
        else
          a->len -= stepSpeed;

        if (a->len <= 0)
        {
          *a = { 0, 0, 0, 0, 0 };
          animTail++;
          animTail %= maxAnim;
        }
      }

      animationStep++;
      animationStep %= maxAnim;
    }
  }

  sparkleState = !sparkleState;  // flip state
  if (sparkleState)
  {
    sparklePixel = random(PIXEL_COUNT);
    strip.setPixelColor(sparklePixel, random(255), random(255), random(255), 100);
  }
  else
    strip.setPixelColor(sparklePixel, 0, 0, 0, 0);

  strip.show();
}

struct topic {
  char    title[16];
  uint8_t rgb[3];
};

const topic topicList[] = 
{ { "login"  , { 200, 200, 200 } }  // white
, { "idea"   , { 255, 255,   0 } }  // yellow
, { "comment", {   0,   0, 255 } }  // blue
, { "outcome", { 255,   0,   0 } }  // green
, { "project", { 255,   0,   0 } }  // green
, { "status" , { 100, 255,   0 } }  // orange
, { "step"   , {   0,  75, 255 } }  // purple
, { "vote"   , {   0, 255,   0 } }  // red
, { "view"   , { 105, 255, 200 } }  // pink
};
const int   topics = sizeof(topicList)/sizeof(topicList[0]);

int addComet(String command)
{
  int retVal = -1;

  for (int i = 0; i < topics; i++)
  {
    if (strcmp((const char*)command, topicList[i].title) == 0)
    {
      anim[animHead] = { topicList[i].rgb[0], topicList[i].rgb[1], topicList[i].rgb[2], 0, cometLen }; // purple
      Particle.publish(command);
      animHead++;
      animHead %= maxAnim;
      retVal = i;
      break;
    }
  }
  return retVal;
}

The bottom 5 bits in the leading byte in the 32 bit parameter you send to the LEDs is the brightness of the individual LED (32 possible levels).
So you can set the brightness per LED in your pixel buffer.

Sorry, I donā€™t know what that means.

What do you mean by ā€˜the bottom 5 bitsā€™?
Right now, Iā€™m setting the color of an individual LED with ā€˜strip.setPixelColor(px, a->g, a->r, a->b)ā€™.

And that does nothing else then combining the RGB components into on 32bit number and placing that integer in a pixel buffer at position px.
Just like this

#define LEDCOUNT 300
uint32_t pxBuffer[LEDCOUNT];
// actually for the APA102 protocoll you need some extra bytes for leadin/leadout
//uint32_t pxBuffer[1 + LEDCOUNT + LEDCOUNT/16 + 1]; // element 0 (leadin) ... LEDs ... 1 leadout clock per 2 LEDs (ceil)

bool needRefresh;
volatile bool canRefresh = true;

uint32_t setAPA102Color(int px, uint8_t r, uint8_t g, uint8_t b, uint8_t brightness = 31) {
  uint32_t color = 0;
  
  // due to big endianness in revers order 
  color |= (brighntess <<   0) | 0xE0;  //top 3 bits must be 111
  color |= (r          <<   8);
  color |= (g          <<  16);
  color |= (b          <<  24);

  pxBuffer[px] = color;

  return color;
}

void refreshDone() {
  canRefresh = true;
}

void setup() {
  SPI.begin();
  SPI.setClockSpeed(8, MHZ);
  //SPI.setDataMode(0);
  SPI.setBitOrder(MSBFIRST);
}

void loop() {
  // setup your pixels 

  if (needRefresh && canRefresh) {
    canRefresh = 
    needRefresh = false;
    SPI.transfer(pxBuffer, NULL, sizeof(pxBuffer), refreshDone);
  }
}

This is all you need to work with APA102 strips - actually it's already a bit more to also illustrate use of DMA and guard against messing up data while still being sent out in the background.

1 Like