4

I'have been looking at the AdaFruit NeoPixels recently and am trying to understand how the addressing scheme works.

All of the examples I've seen explicitly identify the length of the string (number of neopixel units) and are typically set using an array of 24-bit values to control the color of a particular neopixel.

My question is two fold:

  1. How do the neopixels know where they are in a string to allow for addressing?
  2. If the neopixels know where they are in the string, would it not be possible to potentially feed the output of the last neoxpixel back to a microcontroller to determine the number of neopixels in the string?

OP Note (this is my interpretation and may be suspect, but it is more or less my take away)

Thank you for all the answers on this - it has been very enlightening from ALL of the answers. General take away, the NeoPixel is not "directly" addressable in the sense that you can pick pixel "X" of "n" pixels. The string of NeoPixels acts essentially as a gigantic shift-register (as stated in one of the answers) so to "address" a particular pixel you have to update a buffer that represents the state of the entire string and then shift it down the wire. This is why micro-controllers, such as an Arduino, would have limitations on how many NeoPixels it could control. This is also why it is more practical to define the number of pixels in the string and allocate memory to represent that state.

3

As noted in Chris Stratton's answer, “Each pixel "peels off" the first data word before passing on the rest, so the payload gets shorter and shorter as you move down the chain”.

This behavior is spelled out clearly in WS2812.pdf and WS2812B.pdf datasheets from http://www.world-semi.com :

The data transfer protocol use single NZR communication mode. After the pixel power-on reset, the DIN port receive data from controller, the first pixel collect initial 24bit data then sent to the internal data latch, the other data which reshaping by the internal signal reshaping amplification circuit sent to the next cascade pixel through the DO port. After transmission for each pixel, the signal to reduce 24bit. pixel adopt auto resha-ping transmit technology, making the pixel cascade number is not limited the signal transmission, only depend on the speed of signal transmission.

This means that each WS2812 unit in a chain picks off the first 24-bit data packet, latches that data into an internal register, and passes the rest of the data on out, through reshaping circuitry. Hence, if you put m packets of data into a chain and k packets come out, the chain length is m-k or more. (The chain length may be more than m-k when k is zero.)

4

Each pixel "peels off" the first data word before passing on the rest, so the payload gets shorter and shorter as you move down the chain. Although the data wire may look continuous it isn't - the signal is electrically re-driven from each unit to the next.

For the RGB ones, they are logically three monochrome pixels of different colors, so they peel off three words rather than just one.

At any point in the chain you can determine how many logical pixels potentially remain to be updated, by counting the words you are passing on. You can't of course tell if they are actually there, or if there are further pixels for which no data is being provided. And you can't tell how many pixels preceded, other than to bound it by the amount of gap time from the previous update you received.

  • So that more or less answers my understanding in that each neopixel peels off the data it knows about (the "head" of the data set) and then passes the rest. But doesn't that mean I could feed the end of the last pixel back to the microcontroller and have a routine to "watch" for pulses to come back? If no pulses were received then we are either at or below the length of the chain. The first group of pulses that we receive after sending would indicate the number of pixels-1 yes? – Dave G Feb 24 '16 at 5:45
  • Yes, if you have access to both ends of the chain and don't know how many elements you have, you can measure it that way. It may get a little tricky in practice as the transition edges you get will probably be delayed from those you sent, at the least by electrical delay and probably by many register clock-to-out delays as well, and for a long enough chain could make receiving them in software a little challenging. For some of these signalling schemes (would have to look again to see if this one) you can persuade a sufficiently flexible UART peripheral to help, for others you cannot. – Chris Stratton Feb 24 '16 at 6:19
  • I guess a simpler method than truly receiving the data could be to configure a pin change interrupt on the tail-of-chain return, and then send frames with increasing numbers of data words in each into the head end until you finally see something make it out that tail end, or do so in a pattern more like a binary search. – Chris Stratton Feb 24 '16 at 6:23
  • Looks like I finally arrived at the same conclusion you did, Chris. However the binary search idea may have a speed penalty. What number would you start with? Too small and you miss a long string, too large and it would take a long time to send all those pixels. Still this guy drove 1000+ pixels at 30 FPS, so maybe it is a pretty reasonable idea at that. Maybe start with 1000 pixels and work your way down. :) – Nick Gammon Feb 24 '16 at 21:39
  • Or maybe send in increments of 100 until you overshoot, then do a binary search on that last amount. – Nick Gammon Feb 24 '16 at 21:40
4

It is possible to do this by feeding the end of the strip back into the arduino. bunnie Huang blogged about doing this a few years ago: http://www.bunniestudios.com/blog/?p=3345

He has schematics and firmware which uses a modified neopixel library that can measure the length of the strip.

2

As far as I can make out (eg. NeoPixels Revealed: How to (not need to) generate precisely timed signals) the NeoPixels are not in fact addressed, but are like a long shift register. Thus if you have 10 pixels, you have to send out 10 lots of RGB colours and then latch them. This is why you need to know how many pixels you have, so that you send out the right number of RGB bytes.

I don't think it would be particularly practical to try to detect how many pixels are in the chain, because the individual ones "reshape" the timing pulses.

  • 1
    What about feeding the far end of the data line back into an arduino input pin? – Wirewrap Feb 24 '16 at 8:32
  • What would that tell you? Because of the reshaping done by individual pixels you don't know how long each bit will be, exactly. Thus whilst you might know how long it took for a bit to arrive at the other end, without knowing the length of each bit, you don't know what to divide that time by, to arrive at a count. – Nick Gammon Feb 24 '16 at 19:41
  • 1
    I suppose, since each pixel "peels off" one RGB colour, you might try sending increasing numbers of pixels out, and waiting until one arrives at the other end. That might work. – Nick Gammon Feb 24 '16 at 19:42
1

This is certainly possible as I have now confirmed.

The code below will detect the length of a string of NeoPixels up to 10000 long.

/*
 * 
 * Find length of NeoPixel string.
 * Author: Nick Gammon
 * Date:   11 March 2016
 */

#include <SPI.h>

const unsigned long LIMIT = 10000;  // maximum pixels to test

// Note: Connect NeoPixels to MOSI (pin D11 on a Uno, pin D51 on a Mega)
//       Connect the other end of the NeoPixels to D2 (External Interrupt 0)

void sendByte (byte b)
  {
    // send one byte to the Neopixels - note that the "off" gap is partly handled by the loop overhead
    //   gaps measured empirically to be 1.7 µs to 2 µs, so we don't need to add any more of our own
    for (byte bit = 0; bit < 8; bit++)  
      {
      if (b & 0x80) // is high-order bit set?
        SPI.transfer (0b11111100);  // 1 bit - 750 ns on + 250 ns off (acceptable "on" range 550 ns to 850 ns)
      else
        SPI.transfer (0b11100000);  // 0 bit - 375 ns on + 625 ns off (acceptable "on" range 200 ns to 500 ns)
      b <<= 1; // shift next bit into high-order position
    } // end of for each bit
} // end of sendByte

void ledsetup() 
{
  SPI.begin ();
  SPI.setClockDivider (SPI_CLOCK_DIV2);
  SPI.setBitOrder (MSBFIRST);
  SPI.setDataMode (SPI_MODE1);   // MOSI normally low.
} // end of ledsetup

void sendPixel (const byte r, const byte g, const byte b) 
  {
  sendByte (g);       
  sendByte (r);
  sendByte (b);
  } // end of sendPixel

void show() 
  {
  delayMicroseconds (7);
  } // end of show

volatile bool triggered;

void myISR ()
  {
  triggered = true;
  } // end of myISR

void setup() 
  {
  ledsetup();
  Serial.begin (115200);
  Serial.println (F("Starting ..."));
  attachInterrupt (0, myISR, RISING);
  } // end of setup

unsigned int increment = 1000;
unsigned int counter = increment;

void loop() 
  {
  noInterrupts ();
  triggered = false;
  EIFR = bit (INTF0);  // clear flag for interrupt 0

  for (unsigned int i = 0; i < counter; i++)
    sendPixel (5, 0, 0);
  TIMSK0 = bit (TOIE0);  // throw away timer overflow interrupt
  interrupts ();
  show ();  // latch the colours - handle the interrupt meantime

  if (triggered)
    {
    if (increment <= 1)
      {
      Serial.print (F("There are "));
      Serial.print (counter - 1);
      Serial.println (F(" NeoPixels in the string."));
      Serial.flush ();
      exit (0);
      }
    else
      {  // binary search
      counter -= increment;
      increment /= 2;
      } // end of if 
    } // end of if triggered

  counter += increment;

  if (counter > LIMIT)
    {
    Serial.print (F("Could not find any NeoPixels after trying "));
    Serial.print (LIMIT);
    Serial.println (F(" pixels."));
    Serial.flush ();
    exit (0);
    }

  } // end of loop

It works by sending out increasingly long strings of 24 bits, and waiting for one to "shuffle off" the other end of the string.

Connect the Data In of the NeoPixels to MOSI (pin D11 on a Uno, pin D51 on a Mega) as this sketch uses SPI to generate the timed sequences. Connect the far end of the NeoPixels (Data Out) to pin D2 which is External Interrupt 0.

The sketch pumps out batches of pixels, and waits for an external interrupt. When that occurs it indicates that the last pixel in the chain was reached, and passed the pixel data out Data Out to the non-existent pixel past the end of the chain.

The number of pixels needed to do this is then noted.

The sketch does a "binary search" to speed things up for long strings of pixels. The worst-case scenario (where you have either 10000 pixels, or none at all) takes 6 seconds to execute. With less pixels it will be faster. I had under a second execution time for 3 pixels in my test string.

The sketch outputs a dull red (intensity 5) so you should see the pixels light up in red if all is well. If you wanted to do it unobtrusively, you could output black (as that still results in bits being sent down the string).

Example output:

Starting ...
There are 3 NeoPixels in the string.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.