Has anyone ever set up a Teensy or similar to receive WS2812/Neopixel data for

Has anyone ever set up a Teensy or similar to receive WS2812/Neopixel data for debugging or manipulating the packets? Can anyone recommend code for doing so? I’ve tried running the following to basically print out whether the data signal is high or low each time the loop runs:

(see below this for results)


unsigned long previousMicros = 0; // last time update
long interval = 1; // interval at which to do something (microseconds)

int ledData = 6;

unsigned long startTime = 0;
unsigned long totalTime = 0;
unsigned long readCounter = 0;

uint8_t currState = 0;
uint8_t knownState = 0;

void printSec(void) {
Serial.println(“MARK”);
}

void setup() {

Serial.begin(115200);

pinMode(ledData, INPUT);
}

void loop() {

unsigned long currentMicros = micros();

if((currentMicros - previousMicros) > interval) {
previousMicros = currentMicros;
Serial.print(“MARK:”);
Serial.println(currentMicros);
}

currState = digitalReadFast(ledData);
if (currState != knownState) {
totalTime = readCounter - startTime;

knownState = currState;
startTime = 0;
readCounter = 0;

}

readCounter++;
Serial.print(currState);
Serial.print(":");
Serial.println(readCounter);
}


And the serial monitor prints out something like this:

MARK:6596112
1:1
MARK:6596140
1:2
MARK:6596161
1:3
MARK:6596186
1:4
MARK:6596210
0:1
MARK:6596231
0:2
MARK:6596255
1:1
MARK:6596276
0:1
MARK:6596304
0:2

Basically it’s telling me whether the data pin was reading high or low each time through the loop.

I’ve got a project where I need to receive and manipulate WS2812 data, so this test is the first part of that process. Any suggestions of a faster way of running code to the same effect is much appreciated.

Also, I’m trying to avoid dealing with direct port manipulation if possible. digitalReadFast on Teensy actully compiles to the same instructions as direct port addressing. I know the other code I’ve got in there slows things down too, but it’s a start :slight_smile:

Each bit in the ws2812 protocol takes ~1.25us - so you are printing mark at about every 2 bytes worth of data. You can’t use micros for counting this because the amount of time the line is held high for a 1 is just under a single microsecond (and for low it is about 300nanoseconds).

You have to count clock cycles - and for the most part you won’t be able to do much of anything else while reading the data.

You might be able to play games abusing reading serial data in a reverse of the hacks for writing wa2812 data using a UART (where each byte “read” represents a single bit).

If you’re able to dive deep into the complexities of timers & DMA, maybe this could be done by configuring 2 timer channels as input capture. Or course, set the timer to count from 0 to FFFF at 48 or 60 MHz and roll over. You’d connect the same signal to both inputs, so one captures the timer value at the rising edge and the other at the falling edge. Then configure 2 DMA channels to grab those timer capture numbers into 2 big buffers.