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.

Dave G
  • 155
  • 1
  • 6

5 Answers5

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.

Chris Stratton
  • 5,411
  • 20
  • 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.

Craig
  • 2,120
  • 10
  • 11
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.)

James Waldby - jwpat7
  • 8,920
  • 3
  • 21
  • 33
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.

Nick Gammon
  • 38,901
  • 13
  • 69
  • 125
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.
Nick Gammon
  • 38,901
  • 13
  • 69
  • 125