Teensy 3.2, OctoWS2811, and large Serial.readBytes hang

Status
Not open for further replies.
I'm using a Teensy 3.2 with the OctoWS2811 Adaptor, connected via USB Serial to a Raspberry Pi 4 running Falcon Player v3.3 (Raspbian based) to control 2378 WS2813 LEDs being updated at 25ms intervals. The Teensy & OctoWS2811 library are configured to run the longest string of 517 LEDs, so it is having to push bits as if there were 4136 LEDs. With this configuration, I'm expecting a 2 byte header plus 12408 bytes of data being sent over USB Serial, in 64 byte packets, from the Pi to the Teensy, every 25ms.

I'm using Serial.readBytes() to get the data, then hand it off to setPixel in the OctoWS2811 library. However, when I try to use Serial.readBytes() for a "large" number of bytes, the Teensy appears to hang or at least never return from the Serial.readBytes(). This is based on my observation that the built-in LED stops blinking with the code below.

Condensed version:
Code:
// Created by Nick Anderson 7/2/2017
#include <OctoWS2811.h>
#define LEDPIN 13 // Teensy 3.2 LED Pin
#define qBlink() {GPIOC_PTOR=32;} // Fast way to toggle LED on and off
#define MAX_PIXELS_PER_STRIP 517

DMAMEM int displayMemory[MAX_PIXELS_PER_STRIP * 6];
int drawingMemory[MAX_PIXELS_PER_STRIP * 6];

OctoWS2811 leds(MAX_PIXELS_PER_STRIP, displayMemory, drawingMemory, WS2811_RGB | WS2813_800kHz);

void setup() {
  Serial.begin(115200);
  Serial.setTimeout(20);
  
  leds.begin();
  leds.show();

  pinMode(LEDPIN, OUTPUT);
  qBlink();
}

char megaBuffer[MAX_PIXELS_PER_STRIP * 8 * 3]; // 12408 currently
int curItem = 0;
unsigned long millisSinceBlink;

void loop() {
  if (Serial.readBytes(megaBuffer, 2) == 2) {
    if (megaBuffer[0] == '<' && megaBuffer[1] == '>') {
      curItem = 0;
      while (curItem < 24) {
        Serial.readBytes(&megaBuffer[curItem * MAX_PIXELS_PER_STRIP], MAX_PIXELS_PER_STRIP);
        curItem++;
      }
      
      if (millis() - millisSinceBlink > 25) {
        millisSinceBlink = millis();
        qBlink();
      }
      
      curItem = 0;
      while (curItem < (MAX_PIXELS_PER_STRIP * 8)) {
        leds.setPixel(curItem, megaBuffer[curItem * 3], megaBuffer[curItem * 3 + 1], megaBuffer[curItem * 3 + 2]);
        curItem++;
      }
      leds.show();
    }
  }
}
Full code, with comments, is here https://github.com/ShadowLight8/fpp...fpp-to-teensy-serial/fpp-to-teensy-serial.ino

With the default NUM_USB_BUFFERS in usb_desc.h of 12 for USB_SERIAL, attempting 517 byte reads does not work and the built-in LED does not blink.
If I increase the NUM_USB_BUFFERS to 30, 517 byte reads to work. The built-in LED continues to blink and the WS2813 LEDs light up as expected.

Another non-working attempt was to read all 12408 bytes I was expecting by doing:
Code:
Serial.readBytes(megaBuffer, MAX_PIXELS_PER_STRIP * 8 * 3);
Ultimately, this single 12408 byte Serial.readBytes() is what I'd like to get working.

I also tried the following code to handle timeouts and use the led to indicate that they were occurring, but it also did not work:
Code:
curItem = 0;
while (curItem < MAX_PIXELS_PER_STRIP * 8 * 3) {
  n = Serial.readBytes(megaBuffer + curItem, MAX_PIXELS_PER_STRIP * 8 * 3 - curItem);
  if (millis() - millisSinceBlink > 25) {
    millisSinceBlink = millis();
    qBlink();
  }
  curItem += n;
}

I have looked at the OctoWS2811 VideoDisplay code, which appears to do "large" 7500 byte Serial.readBytes(). https://github.com/PaulStoffregen/OctoWS2811/blob/master/examples/VideoDisplay/VideoDisplay.ino

I've attempted to read through the teensy3/core (usb_serial.h readBytes; usb_serial.c usb_serial_read; usb_dev.c usb_rx & usb_rx_memory; usb_mem.c usb_free) to understand if I'm missing something in my code, but my C/C++ kung-fu might not be up to the task yet.

Any assistance or suggestions of how to dig into this issue is sincerely appreciated. Thank you in advance!

-Nick Anderson
 
Does this work any better where it replaces what was tried in the second code loop

Code:
char megaBuffer[MAX_PIXELS_PER_STRIP * 8 * 3]; // 12408 currently
int curItem = 0, n;
unsigned long millisSinceBlink;
void loop() {
  // put your main code here, to run repeatedly:
  curItem = 0;
  while (curItem < MAX_PIXELS_PER_STRIP * 8 * 3) {
    n = 0;
    if ( Serial.available() )
      n = Serial.readBytes(megaBuffer + curItem, Serial.available() );
    if (millis() - millisSinceBlink > 25) {
      millisSinceBlink = millis();
      qBlink();
    }
    curItem += n;
    if ( 0 != n ) Serial.println( n );
  }
  Serial.println( "LOOP" );
}
 
Thanks for the reply defragster!

I gave your suggestion a try and still encountered more or less the same result, but it did get me to find a simpler repo and it might have provided, what I think is, a clue. While trying this out, I would typically see values of 1, 64, 65, or 128 being returned, which seemed reasonable. However, during one test, it printed out that Serial.Available() had returned over 5000 bytes, which at a glance seems out of bounds. I wouldn't expect to see anything over 768 (NUM_USB_BUFFERS at 12 * 64 bytes). I started to look back over the USB code stack to understand how that could be.

I'm a bit out of my depth, but here goes what I found:
Serial.Available(), in usb_serial.c, gets counts from usb_rx_byte_count and the current rx_packet. usb_rx_byte_count, in usb_dev.h, effectively is usb_rx_byte_count_data which is only used in usb_dev.c. I only found one place where usb_rx_byte_count_data is increased. It's in the receive part of the usb_isr. https://github.com/PaulStoffregen/c...9939b4cb6d360ed464d0bd/teensy3/usb_dev.c#L997

Here's the condensed code with serial output comments removed:
Code:
			} else { // receive
				packet->len = b->desc >> 16;
				if (packet->len > 0) {
					packet->index = 0;
					packet->next = NULL;
					if (rx_first[endpoint] == NULL) {
						rx_first[endpoint] = packet;
					} else {
						rx_last[endpoint]->next = packet;
					}
					rx_last[endpoint] = packet;
					usb_rx_byte_count_data[endpoint] += packet->len;
					packet = usb_malloc();
					if (packet) {
						b->addr = packet->buf;
						b->desc = BDT_DESC(64,
							((uint32_t)b & 8) ? DATA1 : DATA0);
					} else {
						b->desc = 0;
						usb_rx_memory_needed++;
					}
				} else {
					b->desc = BDT_DESC(64, ((uint32_t)b & 8) ? DATA1 : DATA0);
				}
			}
usb_rx_byte_count_data is increased first, then usb_malloc(). If usb_malloc fails because it is out of buffers, does usb_rx_byte_count_data become overstated?

Just to see what would happen, I did try moving the usb_rx_byte_count_data increase into the "if (packet)" block and updated my script to do a single Serial.readBytes for 12408. It worked for at least one update, maybe two.

I feel like I'm stabbing in the dark, so any guidance would be welcomed.


Here's the simpler repo I ended up with:
On the Raspberry Pi 4, in one SSH window run
cat > /dev/ttyACM0

In another, run the following
Code:
#!/bin/bash
for (( c=1; c<=10; c++ ))
do
        cat test-frame.txt > /dev/ttyACM0
done
The test-frame.txt file is exactly 12410 bytes long. The first two characters are the header, <>. The rest is "~ " or 7e 20 20 repeating.

When I ran with the code below on the Teensy, I would see 12408 returned twice before things appeared to lock up. At least the WS2813 LEDs would light up as expected.

Code:
void loop() {
  if (Serial.readBytes(megaBuffer, 2) == 2) {
    if (megaBuffer[0] == '<' && megaBuffer[1] == '>') {
      Serial.println( Serial.readBytes(megaBuffer, MAX_PIXELS_PER_STRIP * 8 * 3) );

      curItem = 0;
      while (curItem < (MAX_PIXELS_PER_STRIP * 8)) {
        leds.setPixel(curItem, megaBuffer[curItem * 3], megaBuffer[curItem * 3 + 1], megaBuffer[curItem * 3 + 2]);
        curItem++;
      }
      leds.show();
    }
  }
}
 
Mainly tested on Teensy 4 - did just run on okay a T_3.1 (about 10 times with 11,200 bytes) - because I was expecting it to have an issue with the USB stack being a work in progress - and there is a known issue with Serial.available() and internal counts/updates.

But finding it to work with variation of the above code using a file send from TyCommander of sizes : 28, 280, 2800, 5600, 11200 bytes.

@Paul - this is with Latest Github/CORES changes from 11? days back. - looking goof for use of :: Serial.parseInt() and Serial.available()

Also tried it with a single FULL read request and with reading available() count until either ALL gone - or until the last counted byte would be pulled in:
Code:
    int cntN = Serial.available();
    if ( cntN ) {
      if ( cntN + curItem > rCnt ) cntN = rCnt - curItem;
      n = Serial.readBytes(megaBuffer + curItem, rCnt );
      // n = Serial.readBytes(megaBuffer + curItem, cntN );
      // n = Serial.readBytes(megaBuffer + curItem, Serial.available() );
    }

When the code starts it waits for entry of the Number of bytes to read, then loop() does above reads to as indicated to get those bytes, then exits and repeats.

This is a stand alone sketch - with the test text files. Would be interesting if it works on T_3.2
Code:
[ATTACH]18159._xfImport[/ATTACH]

Slow 200ms blink - enter expected read size
Quick 100ms blink - top of loop() waiting data start
FAST - waiting for data read of indicated bytes.
 
defragster,
I did some testing using your code. The only modification I made was to setup() where I commented out Serial lines after qBlink and just set rCnt to 12408. I also switch to n = Serial.readBytes(megaBuffer + curItem, cntN ); in the loop.

In one SSH window, I sent a single test-frame.txt to /dev/ttyACM0
Code:
cat test-frame.txt > /dev/ttyACM0

In the other SSH window, I watched the returning data. I keep the original formatting, so you'll have to scroll over to see the n and curItem value
Code:
fpp@FPP:~ $ cat < /dev/ttyACM0
768 ==n and sum curItem== 768

<>~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~705 ==n and sum curItem== 1473

  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  768 ==n and sum curItem== 2241

  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  705 ==n and sum curItem== 2946

  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  768 ==n and sum curItem== 3714

  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  705 ==n and sum curItem== 4419

  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  705 ==n and sum curItem== 5124

  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  768 ==n and sum curItem== 5892

  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  705 ==n and sum curItem== 6597

  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  768 ==n and sum curItem== 7365

  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  705 ==n and sum curItem== 8070

  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  768 ==n and sum curItem== 8838

  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  705 ==n and sum curItem== 9543

  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~ 705 ==n and sum curItem== 10248

  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~768 ==n and sum curItem== 11016

  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~515 ==n and sum curItem== 11531

  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~ 390 ==n and sum curItem== 11921

 ~  ~   ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~

----------------------64 ==n and sum curItem== 11985

  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~

----------------------64 ==n and sum curItem== 12049

 ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~

----------------------64 ==n and sum curItem== 12113

~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~

----------------------64 ==n and sum curItem== 12177

  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~231 ==n and sum curItem== 12408

 ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  20 ==n and sum curItem== 20

 ~  ~  ~    ~~  ~ ~

----------------------3 ==n and sum curItem== 23

 ~

----------------------1 ==n and sum curItem== 24



----------------------1 ==n and sum curItem== 25

~

----------------------1 ==n and sum curItem== 26

~

----------------------1 ==n and sum curItem== 27

I'm not sure where the " <<< end LOOP\n" lines went, but the code must have exited the loop since curItem was reset from 12408. At this point, there is an ongoing stream of returning data that doesn't appear to come from the test-frame.txt data. Scanning through the returning data, it's looking like it ran past a buffer somewhere since it spelled out "curItem== 102" over multiple readBytes.

Code:
----------------------1 ==n and sum curItem== 358

c

----------------------1 ==n and sum curItem== 359

u

----------------------1 ==n and sum curItem== 360

r

----------------------1 ==n and sum curItem== 361

I

----------------------1 ==n and sum curItem== 362

t

----------------------1 ==n and sum curItem== 363

e

----------------------1 ==n and sum curItem== 364

m

----------------------1 ==n and sum curItem== 365

=

----------------------1 ==n and sum curItem== 366

=

----------------------1 ==n and sum curItem== 367



----------------------1 ==n and sum curItem== 368

1

----------------------1 ==n and sum curItem== 369

0

----------------------1 ==n and sum curItem== 370

2

----------------------2 ==n and sum curItem== 372


Edit:
I realized I should have set rCnt to 12410 to match the amount of data being sent. Ended up with similar results, but did capture the "end LOOP" this time:
Code:
  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~76  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~

----------------------64 ==n and sum curItem== 11659

 ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~ 751 ==n and sum curItem== 12410

(▒iX▒

---------------------->>>>>>>>>>>>> sum curItem== 12410 <<< end LOOP


7 ==n and sum curItem== 7

     ~

----------------------1 ==n and sum curItem== 8



----------------------1 ==n and sum curItem== 9
 
Last edited:
I've been able to pull my hardware (RPi4 & Teensy 3.2) onto my bench and setup hardware serial out to get some insight of what is going on when trying to do a large serial.readBytes. The short version is that, after much debugging, I think I'm running into a SRAM_L to SRAM_U boundary fault. I'm in need of some help to confirm if this is the case and how to best avoid it.

I uncommented a lot of the USB stack's serial_print messages and added some additional ones to see where the Teensy 3.2 was stopping. Eventually, I determined that the memcpy in usb_serial.c's usb_serial_read was where things stopped.
Code:
memcpy(p, rx_packet->buf + rx_packet->index, qty);
@ https://github.com/PaulStoffregen/cores/blob/master/teensy3/usb_serial.c#L114
I added serial_print's to list out each variable just before the memcpy and the last values seen are "p:1FFFFFD2 - rx->buf:1FFFB6E4 - rx->idx:0000 - qty:00000040"

Searching around lead me to the K20 Sub-Family Reference Manual, where I saw that this memcpy would be crossing the SRAM_L and SRAM_U boundary. Which in turn lead me to multiple discussion about crossing this boundary.

I've got everything setup to test and I plan to keep it that way for a while, so I should be able to try out ideas and provide results quickly. Thanks in advance!!

In case it helps, the code I added to output the memcpy variables:
Code:
serial_print("p:");
serial_phex32(p);
serial_print(" - rx->buf:");
serial_phex32(rx_packet->buf);
serial_print(" - rx->idx:");
serial_phex16(rx_packet->index);
serial_print(" - qty:");
serial_phex32(qty);
serial_print("\n");
 
I think I've been able to confirm the issue is related to the SRAM_L / SRAM_U boundary. In the sym file, it showed the megaBuffer was being placed just before the boundary and it's length would go beyond it. Adding a 5k dummy array cause the megaBuffer to move past the SRAM_U boundary at 0x20000000. Maybe not a final solution, but a useful work around to prove the issue.

What other solutions might be better that allocating additional memory to fill up SRAM_L to force the entire megaBuffer to be in SRAM_U?

This was something I didn't know was an issue/limitation/feature with the Freescale chips, but have found quite a few posts and pages about it. Here are some of links I found helpful:
https://community.pixelmatix.com/t/unable-to-load-12288-bytes-from-serial-into-backgroundlayer/148
https://forum.pjrc.com/threads/25256-Teensy-3-hard-fault-due-to-SRAM_L-and-SRAM_U-boundary
https://community.nxp.com/thread/320347
https://eleccelerator.com/kinetis-microcontroller-sram-region-hard-faults/
https://community.nxp.com/thread/437160

Code that is working:
Code:
// Created by Nick Anderson 7/2/2017

#include <OctoWS2811.h>
#define qBlink() (digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN))) // Fast way to toggle LED on and off

#define MAX_PIXELS_PER_STRIP 517

DMAMEM char dummyBuffer[5000];

DMAMEM int displayMemory[MAX_PIXELS_PER_STRIP * 6];
int drawingMemory[MAX_PIXELS_PER_STRIP * 6];

OctoWS2811 leds(MAX_PIXELS_PER_STRIP, displayMemory, drawingMemory, WS2811_RGB | WS2813_800kHz);

void setup() {
  dummyBuffer[0] = 'a';
  Serial.begin(115200);

  leds.begin();
  leds.show();

  pinMode(LED_BUILTIN, OUTPUT);
  qBlink();
  // Leave led on by default, easier to see the Teensy is running. Ideally, should always be on. If it goes on and off, it indicates the Serial Header is not where it is expected to be. Check channel/output counts.
}

int curItem = 0;
unsigned long millisSinceBlink;

char megaBuffer[MAX_PIXELS_PER_STRIP * 8 * 3];

void loop() {
  // Try to read 2 bytes or hit the timeout, loop around and try again
  if (Serial.readBytes(megaBuffer, 2) == 2) {
    if (megaBuffer[0] == '<' && megaBuffer[1] == '>') {
      Serial.readBytes(megaBuffer, MAX_PIXELS_PER_STRIP * 8 * 3);
      
      curItem = 0;
      while (curItem < (MAX_PIXELS_PER_STRIP * 8)) {
        leds.setPixel(curItem, megaBuffer[curItem * 3], megaBuffer[curItem * 3 + 1], megaBuffer[curItem * 3 + 2]);
        curItem++;
      }
      leds.show();
      qBlink();
    }
    else {
      // Toggle LED to indicate data that isn't a header.
      if (millis() - millisSinceBlink > 250) {
         millisSinceBlink = millis();
       qBlink();
      }
    }
  }
}
 
Last edited:
I found that adding "__attribute__((aligned(2048)))" to the megaBuffer declaration did a good enough job of coercing the linker to keeping from placing it across the SRAM_L / SRAM_U boundary. With that fixed and some optimizations based on the movie2serial.pde code, I've been successfully running my leds at ~60 fps.

https://github.com/ShadowLight8/fpp-to-teensy-serial-ws2813

Code:
// Created by Nick Anderson 7/2/2017
// Updated for Halloween 2018 Show 10/8/2018
//  Updated MAX_PIXELS_PER_STRIP to 517
//  Removed color order changing, will use Vixen/xLight to manage
// Added comments from forum posts to help with setting up to use this code 12/19/2018
// Updated after Halloween 2019 11/5/2019
//  Major refactor to use Serial.readBytes
//  Improved performance and reduced glitches seen
//  Build with Teensy running at 120Mhz and Fastest optimizations
// Ongoing effort to figure out why a single large Serial.readBytes hangs 3/10/2020
//  Ultimately, the issue was memory writes across the SRAM_L / SRAM_U ram segments
//  Link to post on the PJRC forums: https://forum.pjrc.com/threads/58326-Teensy-3-2-OctoWS2811-and-large-Serial-readBytes-hang?p=232994&viewfull=1#post232994
//  Added the alignment option to the megaBuffer to help the linker to avoid the SRAM_L / SRAM_U boundary 7/9/2020
// Switched to method of building drawingMemory directly based on movie2serial.pde: https://github.com/PaulStoffregen/OctoWS2811/blob/master/extras/VideoDisplay/Processing/movie2serial.pde
//  With a RPi4 running FPP 4.0.1, MAX_PIXELS_PER_STRIP set to 517, CPU Speed @ 120 Mhz, and Fastest with LTO set, able to run at 17ms per frame or about ~58.8 fps 7/15/2020
//  Updated when/how the Teensy blinks to make it easier to see when a potential issue exists
//  Final clean up and testing 8/26/2020

// Other interesting ideas:
//   Thread looking at how to quickly setup the drawingMemory: https://forum.pjrc.com/threads/28115-Trouble-wrapping-my-head-around-some-parts-of-movie2serial-PDE
//   Link to another conversion method: https://github.com/bitogre/OctoWS2811/commit/18e3c93fac3aa7546c152969c9f9485c153a6ad7

// ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- -----
#include <OctoWS2811.h>
#define qBlink() (digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN))) // Fast way to toggle LED on and off

// This code is designed to get the serial data stream into the OctoWS2811's drawingMemory buffer as fast as possible.

// The OctoWS2811 library is designed for 8 equal length led strips, define the max number of pixels on the longest, single strip.
// For example, if you plan to run 3 strips of lengths 48, 50, and 75, set the value to 75.
// See the LED Address & Different Strip Lengths section of https://www.pjrc.com/teensy/td_libs_OctoWS2811.html for more detail.

// When you change the MAX_PIXELS_PER_STRIP, be sure to update Vixen/xLight/FPP/etc with the new channel/output count of MAX_PIXELS_PER_STRIP * 8 * 3
#define MAX_PIXELS_PER_STRIP 517

// Within the sequencing software such as Vixen or Falcon Pi Player (FPP), the serial connection should be configured for MAX_PIXELS_PER_STRIP * 8 * 3 outputs and to send a header of <>
// 8 for the number of strips and 3 for each of the R, G, and B channels. The mapping of the channels to align with each of the 8 output the OctoWS2811 library should be within the sequencing software.
// Color order must be configured in the sequencing software.

// When you patch up the controller in Vixen (and likely other programs), you'll end up with unconnected gaps in the outputs from the last used channel of one strip to the Starting Output of the next strip.
// These gaps are critical to the Teensy code since all it's really doing it taking a stream of data and feeding it to the OctoWS2811 library. I know this adds an extra headache when setting up the channel outputs.
// You can calculate a strip's Starting Output with ((Strip_Number - 1) * MAX_PIXELS_PER_STRIP * 3) + 1. If you had MAX_PIXELS_PER_STRIP as 444, the start of Strip 3 would be ((3 - 1) * 444 * 3) + 1 = 2665.
// When connecting the outputs in Vixen for Strip 3, you'd start with channel 2665 on your Generic Serial / Teensy controller.

// Under the Arduino Tools menu, make sure the "USB Type" is set to just "Serial". Testing with CPU speed @ 120 Mhz and Optimizations as Fastest with LTO worked well.

DMAMEM int displayMemory[MAX_PIXELS_PER_STRIP * 6];
DMAMEM byte drawingMemory[MAX_PIXELS_PER_STRIP * 8 * 3] __attribute__((aligned(32))); // Ensure 32 byte boundary because of Byte type doesn't require one

// Using an aligned attribute to force the linker to place megaBuffer such that it doesn't cross over the SRAM_L and SRAM_U boundary at 0x20000000.
// The memory placement can be verified in the fpp-to-teensy-serial.ino.sym file located in C:\Users\<username>\AppData\Local\Temp\arduino_build_<rnd_number>
// Search for megaBuffer - Location should be typically be 20000000 or higher, but at the least, the location + size should not cross 20000000
// attribute aligned value must be a power of 2
char megaBuffer[MAX_PIXELS_PER_STRIP * 8 * 3] __attribute__((aligned(2048)));

OctoWS2811 leds(MAX_PIXELS_PER_STRIP, displayMemory, drawingMemory, WS2811_RGB | WS2813_800kHz);

void setup() {
  Serial.begin(115200);

  leds.begin();
  leds.show();

  // Leave led on by default, easier to see the Teensy is running. Ideally, should always be on. If it goes on and off, it indicates an issue. See bottom of the code for more info.
  pinMode(LED_BUILTIN, OUTPUT);
  qBlink();
}

int offset, curItem, i, mask;
int pixel[8];
byte b;
unsigned long millisSinceBlink = 0;

void loop() {
  // Quickly check if next bytes are the header
  if (Serial.read() == '<' && Serial.read() == '>') {
    Serial.readBytes(megaBuffer, MAX_PIXELS_PER_STRIP * 8 * 3);

    // Original, but slower, method for populating drawingMemory
    // curItem = 0;
    // while (curItem < (MAX_PIXELS_PER_STRIP * 8)) {
    //   leds.setPixel(curItem, megaBuffer[curItem * 3], megaBuffer[curItem * 3 + 1], megaBuffer[curItem * 3 + 2]);
    //   curItem++;
    // }

    // Code is based on movie2serial.pde
    // While loop runs through one strip worth - Number of pixels * 3 channels R,G,B
    // First For loop gets each strip's nth (i) pixel's color data as an int
    // Second For loop set which bit from each pixel we process
    //  Inner For loop gets that bit from each pixel and accumulates them into a byte (b)
    offset = 0;
    curItem = 0;
    while (curItem < MAX_PIXELS_PER_STRIP * 3) {

      for (i = 0; i < 8; i++) {
        // Color order should be managed by the Sequencer (like Vixen/xLights) configuration
        pixel[i] = (megaBuffer[curItem + i * MAX_PIXELS_PER_STRIP * 3] << 16) | (megaBuffer[curItem + 1 + i * MAX_PIXELS_PER_STRIP * 3] << 8) | megaBuffer[curItem + 2 + i * MAX_PIXELS_PER_STRIP * 3];
      }

      for (mask = 0x800000; mask != 0; mask >>= 1) {
        b = 0;
        for (i = 0; i < 8; i++) {
          if ((pixel[i] & mask) != 0) b |= (1 << i);
        }
        // Load data directly into OctoWS2811's drawingMemory
        drawingMemory[offset++] = b;
      }

      curItem += 3;
    }

    leds.show();
  } else {
    // Is there USB Serial data that isn't a header? If so, flash LED every 750ms
    // Flashing can indicate the following:
    //    Serial header of <> is missing (Lights most likely aren't working) - Check FPP settings that the header is set
    //    Serial header of <> isn't where it is expected (Lights might be working) - Verify MAX_PIXELS_PER_STRIP * 8 * 3 matches the number of channels
    //    Serial data not being processed fast enough - Sequence refresh rate should be slowed down until flashing stops (Tested with MAX_PIXELS_PER_STRIP = 517 with 17ms sequence timing)
    if (Serial.peek() != -1 && Serial.peek() != '<' && millis() - millisSinceBlink > 750) {
      qBlink();
      millisSinceBlink = millis();
    } else if (millis() - millisSinceBlink > 3000) // Turn LED back on in case it was left off, but no more data has come in
      digitalWriteFast(LED_BUILTIN, 1);
  }
}
 
Status
Not open for further replies.
Back
Top