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.