Here’s the code I changed to get the Output Expander to work with the WS2814s. @wizard, this is a good reference for adding official support for them, as it’s tested and works. I made these changes in app.c. I’m only including the handleIncoming() function here. For the rest go to Github (see above)
static inline void handleIncomming() {
uartResetCrc();
//look for the 4 byte magic header sequence
if (uartGetc() == 'U' && uartGetc() == 'P' && uartGetc() == 'X' && uartGetc() == 'L') {
uint8_t channel = uartGetc();
uint8_t recordType = uartGetc();
lastDataMs = ms; //notice that we see some data
switch (recordType) {
case SET_CHANNEL_WS2812: {
//read in the header
PBWS2812Channel ch;
uartRead(&ch, sizeof(PBWS2812Channel));
if (ch.numElements < 3 || ch.numElements > 4)
return;
if (ch.pixels * ch.numElements > BYTES_PER_CHANNEL)
return;
//check that it's addressed to us and remap
channel = remapFrameChannel(channel);
// Original code
// uint8_t or = ch.or;
// uint8_t og = ch.og;
// uint8_t ob = ch.ob;
// uint8_t ow = ch.ow;
/// Original code
// Scott Mauer's hack for WS2814s
// Set your PB and Output Expander for RGBW
// This will re-arrange the colors to be correct
uint8_t or = ch.og;
uint8_t og = ch.ob;
uint8_t ob = ch.ow;
uint8_t ow = ch.or;
uint8_t elements[4];
uint32_t * dst = bitBuffer;
int stride = 2*ch.numElements;
for (int i = 0; i < ch.pixels; i++) {
elements[or] = uartGetc();
elements[og] = uartGetc();
elements[ob] = uartGetc();
if (ch.numElements == 4) {
elements[ow] = uartGetc();
}
//this will ignore channel > 7
bitConverter(dst, channel, elements, ch.numElements);
dst += stride;
}
volatile uint32_t crcExpected = uartGetCrc();
volatile uint32_t crcRead;
uartRead((void *) &crcRead, sizeof(crcRead));
if (channel < 8) { //check that it was addressed to us (not 0xff)
int blocksToZero;
if (crcExpected == crcRead) {
if (channels[channel].type == SET_CHANNEL_WS2812
&& (ch.pixels * ch.numElements >=
channels[channel].ws2812Channel.pixels * channels[channel].ws2812Channel.numElements)
) {
blocksToZero = 0;
} else {
//we need to zero out previous data if the data received was less than last time
blocksToZero = BYTES_PER_CHANNEL - ch.numElements * ch.pixels;
}
channels[channel].type = SET_CHANNEL_WS2812;
channels[channel].ws2812Channel = ch;
lastValidChannelMs = ms;
} else {
//garbage data, disable the channel, zero everything.
//its better to let the LEDs keep the previous values than draw garbage.
debugStats.crcErrors++;
channels[channel].type = SET_CHANNEL_WS2812;
memset(&channels[channel].ws2812Channel, 0, sizeof(channels[0].ws2812Channel));
blocksToZero = BYTES_PER_CHANNEL;
}
//zero out any remaining data in the buffer for this channel
if (blocksToZero > 0)
bitSetZeros(bitBuffer + (BYTES_PER_CHANNEL - blocksToZero)*2, channel, blocksToZero);
}
break;
}
case DRAW_ALL: {
uint32_t crcExpected = uartGetCrc();
uint32_t crcRead;
uartRead(&crcRead, sizeof(crcRead));
if (crcExpected == crcRead) {
startDrawingChannles();
} else {
debugStats.crcErrors++;
}
break;
}
case SET_CHANNEL_APA102_DATA: {
//read in the header
PBAPA102DataChannel ch;
uartRead(&ch, sizeof(ch));
if (ch.frequency == 0)
return;
//make sure we're not getting more data than we can handle
if ((ch.pixels+2) * 4 > BYTES_PER_CHANNEL)
return;
//check that it's addressed to us and remap
channel = remapFrameChannel(channel);
uint8_t or = ch.or;
uint8_t og = ch.og;
uint8_t ob = ch.ob;
uint8_t elements[4] = {0,0,0,0};
uint32_t * dst = bitBuffer;
//start frame
bitConverter(dst, channel, elements, 4);
dst += 8;
for (int i = 0; i < ch.pixels; i++) {
elements[or+1] = uartGetc();
elements[og+1] = uartGetc();
elements[ob+1] = uartGetc();
elements[0] = uartGetc() | 0xe0;
bitConverter(dst, channel, elements, 4);
dst += 8;
}
//end frame
elements[0] = 0xff;
elements[1] = elements[2] = elements[3] = 0;
bitConverter(dst, channel, elements, 4);
volatile uint32_t crcExpected = uartGetCrc();
volatile uint32_t crcRead;
uartRead((void *) &crcRead, sizeof(crcRead));
if (channel < 8) { //check that it was addressed to us (not 0xff)
int blocksToZero;
if (crcExpected == crcRead) {
if (channels[channel].type == SET_CHANNEL_APA102_DATA
&& (ch.pixels >= channels[channel].apa102DataChannel.pixels)
) {
blocksToZero = 0;
} else {
//we need to zero out previous data if the data received was less than last time
blocksToZero = BYTES_PER_CHANNEL - ch.pixels * 4;
}
channels[channel].type = SET_CHANNEL_APA102_DATA;
channels[channel].apa102DataChannel = ch;
lastValidChannelMs = ms;
} else {
//garbage data, disable the channel, zero everything.
//its better to let the LEDs keep the previous values than draw garbage.
debugStats.crcErrors++;
channels[channel].type = SET_CHANNEL_APA102_DATA;
memset(&channels[channel].apa102DataChannel, 0, sizeof(channels[0].apa102DataChannel));
blocksToZero = BYTES_PER_CHANNEL;
}
//zero out any remaining data in the buffer for this channel
//TODO FIXME apa102 zeros will cause a start frame or maybe draw black? might not be what we want. set to all ones instead?
if (blocksToZero > 0)
bitSetZeros(bitBuffer + (BYTES_PER_CHANNEL - blocksToZero)*2, channel, blocksToZero);
}
break;
}
case SET_CHANNEL_APA102_CLOCK: {
//read in the header
PBAPA102ClockChannel ch;
uartRead(&ch, sizeof(ch));
if (ch.frequency == 0)
return;
//check that it's addressed to us and remap
channel = remapFrameChannel(channel);
volatile uint32_t crcExpected = uartGetCrc();
volatile uint32_t crcRead;
uartRead((void *) &crcRead, sizeof(crcRead));
if (channel < 8) { //check that it was addressed to us (not 0xff)
int blocksToZero;
if (crcExpected == crcRead) {
if (channels[channel].type == SET_CHANNEL_APA102_CLOCK) {
blocksToZero = 0;
} else {
//we need to zero out previous data
blocksToZero = BYTES_PER_CHANNEL;
}
channels[channel].type = SET_CHANNEL_APA102_CLOCK;
channels[channel].apa102ClockChannel = ch;
lastValidChannelMs = ms;
} else {
//garbage data, disable the channel, zero everything. Some apa102 channels could be without clock, so should remain unchanged
debugStats.crcErrors++;
channels[channel].type = SET_CHANNEL_APA102_CLOCK;
memset(&channels[channel].apa102ClockChannel, 0, sizeof(channels[0].apa102ClockChannel));
blocksToZero = BYTES_PER_CHANNEL;
}
//zero out any remaining data in the buffer for this channel
if (blocksToZero > 0)
bitSetZeros(bitBuffer + (BYTES_PER_CHANNEL - blocksToZero)*2, channel, blocksToZero);
}
break;
}
default:
//unsupported frame type or garbage frame, just wait for the next one
break;
}
} else {
debugStats.frameMisses++;
}
}