Measuring the length of a WS2812 string

Recently, I encountered an interesting problem: How do you measure the length of a WS2812 programmable RGB-LED string electronically? That is, just using signals that are already there. This can be useful if you want a LED controller to adapt its pattern according to the string that is connected to it, or simply for diagnostic reasons.

WS2812 strings are usually controlled by using one serial output signal only. The LEDs are daisy chained and you simply push out data for all LEDs without any feedback. If there are fewer LEDs on the string, the data for the last LEDs is ignored.

An intuitive approach to counting the number of LEDs is to feed the output of the last LED of the string back into the microcontroller. According to the WS2812 protocol, the output of LED number n is low until data for n+1 LEDs has been pushed to the string. To count the number of LEDs, data is fed to the string until a rising transition is detected on the output. A relatively easy way to implement this is by using a pin change interrupt on the AVR, or a similar feature on other microcontrollers.

While this works nicely, it has the disadvantage of requiring another wire. Can we do it without introducing any additional connections?

I accidentally stumbled upon another way: When the WS2812 LEDS are set to the brightest level of white (255,255,255) they draw around 60 mA of current. This leads to a measurable voltage drop on the power rails. So we can get a good idea of the number of LEDs connected to a power source by setting it to a known pattern and measuring the resulting voltage drop.

Many microcontrollers actually allow using their internal ADC to measure the supply voltage. If the MCU is connected to the same power supply as the LED string, it can determine the current draw of the WS2812 LEDs without any additional hardware.

I tried this on an ATtiny841. The code below uses the internal ADC to measure the band gap voltage (1.1 V) using VCC as reference. From this, it is possible to calculate the actual supply voltage. It is necessary to average a number of measurements due to noise introduced into the power rail by the WS2812 pulse width modulation.

/*
Measures the supply voltage internally using the ADC.

Returns the ADC-value of the 1.1 band gap input measured 
with VCC as reference voltage. This value can be converted 
to the VCC voltage by using this equation:

VCC=1024*1.1/ADC;

cpldcpu@gmail.com - Nov 23, 2013
*/

uint16_t MeasureVCC(void)
{
  uint16_t sum=0;
  uint8_t i;

  PRR &=~_BV(PRADC); // ADC power on
  ADCSRA =_BV(ADEN)|_BV(ADPS2)|_BV(ADPS1)|_BV(ADPS0); 
  ADMUXB=0; // Reference is Vcc
  ADMUXA=0x0d; // Measure internal 1.1v reference
  for (i=0; i<32; i++) {
    ADCSRA |=_BV(ADSC); // Start conversion
    while (!(ADCSRA&_BV(ADIF))); //~100 us
    ADCSRA |=_BV(ADIF); // Clear ADIF
    sum+=ADC;
  }

  return sum;
}

I used a loop to turn on one led after another, measure the power supply voltage after each step and print the number of LEDs and ADC output:

uint8_t i,k;

for(i=0; i<stringlen;i++) {
  for (k=0; k<stringlen; k++) {
    if (k<i) {
      LEDs[k]=cwhite;
    } else {
      LEDs[k]=cblack;
    }
  }

  ws2812_setleds(LEDs,32);
  
  uint16_t in;
  in=MeasureVCC();
  printf("%i\t%i\n",i,in);
}

The image below shows the output for a WS2812 string after converting the ADC output to a voltage. Both the LED string and the microcontroller were connected to a USB port as power supply.

vcctrace
It is easily noticeable that there are only 16 LEDs in the string as no further drop of the supply voltage occurs after this point. Interestingly, this information can also be used to calculate the effective internal impedance of the power supply. In this example it is 0.5 Ohm, assuming that every LED draws 60 mA. The main contributor to the series resistance is probably a poly fuse that is used to protect the USB port power rails.

vccmultitraces
The image above compares the behavior of several USB power sources. The USB battery has the lowest internal resistance, as indicated by the flat slope. In this case it becomes more difficult to resolve the contribution of individual LEDs and it is only possible to get an estimate of the string lengths.

In conclusion, even though it is a bit hacky, measuring the supply voltage allows to get a good estimate of the length of a WS2812 string without any additional connections.

10 thoughts on “Measuring the length of a WS2812 string

  1. Tim,

    Nice write up. This effort is an excellent example of thinking about using additional information that you can gather about your circuit. Perhaps you can apply a little more math and make the test slower but more robust with the battery.

    I suggest toggling the first LED 5 (tweak to optimize) times and getting an average voltage delta for the on vs off state. Use a fraction of this delta as your test to determine if there if the next LED is present (we only want to know is there an actual delta that correlates with the next LED’s on state). The turn the first LED off and toggle the 2nd LED 5 times. If there was a 2nd LED, uses a fraction of that voltage delta as your test value for your next LED. Obviously there should be no delta for an address with no LED. In essence we want to change from an analog mapping measurement into a digital comparison test with a shifting window level for the comparison.

    Please let me know if you get a chance to try it. Great blog.

    Shane

    • Hi Shane,

      thank your for your comment. Yes you are right, there are several ways to make this more efficient.

      By integrating more measurements it would be possible to get a higher resolution.

      I agree that just using a single LED to “probe” could be more efficient, especially if there as a danger of overloading the power source. One could use the first LED to get the voltage delta, as you described, and then use a binary search pattern to speed up the process.

      Regards,
      Tim

  2. measuring the power consumption is a good idea. you could get the exact number of LEDs this way:
    * turn the first LED on
    * shift the lit LED through the chain (so, keep shifting zeros in the chain)
    if i recall the protocoll correctly, you need to reset the chain first, set x LEDs to off, and send one lit LED
    * drop in powerconsumption at the end of the chain, when the last LED turns off

    of course, this is a little bit slower than your method.

  3. This (or one of the single-led-at-a-time variants) should be fairly reliable for short strings powered by a relatively high effective resistance power supply.

    It may be harder for a series of 150 or 300 LEDs. First off, they are going to require a much lower impedance power supply, and secondly the delta current (one lit LED plus N dark currents, vs no lit LEDs plus N dark currents) is going to get less dramatic (lower S/N). (and its worse still if you light the first K pixels rather than just one).

    One could partially counter the latter effect by lighting M LEDs at a time rather than just 1, albeit one would get a less precise count that way (eg: lighting pixels 95-100 drops the voltage vs non lit, but lighting pixels 101-105 does not – so there are around 100 pixels).

    Still, it’s clever and and quite useful in some circumstances, thanks for the tip.

    • Another thought – if one wanted, one could have a series resistor between the power supply and the load, which could be switched in or out of the circuit – eg: using a relay to short across it when operating normally (ie: many LEDs lit at once which would drop too much across the resistor)..

      Rather than measuring the uC’s own power, one could use a resistive voltage divider (after the series resistor in the power line) to bring the measured voltage down to less than 1.1v and do the ADC against the 1.1v reference. This would allow finding the last pixel of even longer strings, and could work when the uC is using a separate supply or using onboard regulation from a higher Vin, and when the LEDs are running at 12v, etc – at the cost of 3 resistors and a relay.

      Again, that’s a slightly different niche than your no-external-components approach. Like a control box into which different strings might be plugged. And if you wanted to be fancier than that, you could use a current sensor chip for more accuracy (and no need for a relay).

  4. It may be harder for a series of 150 or 300 LEDs. First off, they are going to require a much lower impedance power supply, and secondly the delta current (one lit LED plus N dark currents, vs no lit LEDs plus N dark currents) is going to get less dramatic (lower S/N). (and its worse still if you light the first K pixels rather than just one).

    One could partially counter the latter effect by lighting M LEDs at a time rather than just 1, albeit one would get a less precise count that way (eg: lighting pixels 95-100 drops the voltage vs non lit, but lighting pixels 101-105 does not – so there are around 100 pixels).

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s