Good idea!
(I'm doing the same thing right now...)
Here's a snippet of the code I'm using on an 8MHz Tiny85 (nb. it won't work at other clock frequencies - WS2811 is all about timing):
// Where the WS2811 is attached (pin B4)
#define LED_BIT (1<<4)
#define LED_DDR DDRB
#define LED_PORT PORTB
#define LED_PIN PINB
#define NOP __asm__("nop\n\t")
class WS2811 {
public:
static void init() {
LED_PORT &= ~LED_BIT;
LED_DDR |= LED_BIT;
}
};
WS2811 ws2811;
// A single LED in the string
class LED {
byte r_,g_,b_;
static void sendByte(byte b) {
byte mask = 0x80;
while (mask!=0) {
if ((b&mask)==0) {
// Send a '0'
LED_PIN = LED_BIT; // Hi (start)
NOP; // Hi
LED_PIN = LED_BIT; // Lo (250ns)
NOP; // Lo
NOP; // Lo (500ns)
NOP; // Lo (data bit here!)
NOP; // Lo (750ns)
NOP; // Lo (875ns)
}
else {
// Send a '1'
LED_PIN = LED_BIT; // Hi (start)
NOP; // Hi
NOP; // Hi (250ns)
NOP; // Hi
NOP; // Hi (500ns)
NOP; // Hi (data bit here!)
NOP; // Hi (750ns)
LED_PIN = LED_BIT; // Lo (875ns)
}
mask >>= 1; // Lo (1000ns)
}
}
public:
// Set my color
LED& setColor(byte r, byte g, byte b) {
r_ = r;
g_ = g;
b_ = b;
}
// Send me to the LED
void send() const {
cli(); // Interrupts have to be off while we do this as they cause timing glitches
sendByte(g_);
sendByte(b_);
sendByte(r_);
sei();
}
};
LED led;
void setup()
{
ws2811.init();
}
void loop()
{
byte b = 128;
led.setColor(b,0,b);
led.send();
delay(300);
led.setColor(0,b,0);
led.send();
delay(300);
}
The only thing you have to watch out for is not to delay for too long between sending of each LED. The WS2811 interprets transmission delays of 50us or more as a signal that you've finished sending (in that code it's the 'delay(300)' that sets the LED color).
8MHz is the minimum CPU speed you need. If you want 16MHz you'll have to put more NOPs in.
There's a bunch of info on the WS2811 here: https://www.insomnialighting.com/products/rgbpxws2811.html
It's possible, yes...