Strange Issue with MIDI Library and Neopixels on Teensy LC

Status
Not open for further replies.

NewLinuxFan

Well-known member
My sketch works for a period of time, then stops working. The amount of time that it works depends on how quickly I repeatedly press keys on the keyboard (musical) and also the number of LED's enabled in the code. At 11 or 12 lights it works with infrequent key pressing but stops working when I press the keys rapidly. 1 light, 8 lights, and 10 lights seem to work indefinitely, even with rapid pressing of keys.

Originally, I was working on multi-tab code that is longer than anybody here wants to read, but now I've been trying this shorter code for debugging. There's no millis() used, and the problem still happens.

Code:
#include <MIDI.h>

MIDI_CREATE_DEFAULT_INSTANCE();

#include <FastLED.h>

#define NUM_LEDS 12

#define DATA_PIN 17

CRGB leds[NUM_LEDS];

unsigned int Fadeoff;

void handleNoteOn(byte channel, byte pitch, byte velocity)
{
  Fadeoff = 4095;
  for (int i = 0; i < NUM_LEDS; i++)
  {
    leds[i].setRGB(Fadeoff >> 4, Fadeoff >> 6, 0);
  }
  FastLED.show();
  digitalWriteFast(13, HIGH);
}

void setup() 
{

  MIDI.begin(MIDI_CHANNEL_OMNI);

  MIDI.setHandleNoteOn(handleNoteOn);

  pinMode(13, OUTPUT);  // Indicator LED on Teensy LC.

  FastLED.addLeds<WS2812, DATA_PIN, RGB>(leds, NUM_LEDS);

  digitalWriteFast(13, LOW);
  for (int i = 0; i < NUM_LEDS; i++)
  {
    leds[i] = CRGB::Black;
  }
  FastLED.show();

}

void loop() 
{

  while (1) 
  {

    MIDI.read();

    if (Fadeoff)
    { Fadeoff -= ((Fadeoff >> 8) + 2);
      if (Fadeoff < 0) {
        Fadeoff = 0;
      }
    }
    
    else {
      digitalWriteFast(13, LOW);
    };

    for (int i = 0; i < NUM_LEDS; i++)
    {
      leds[i].setRGB(Fadeoff >> 4, Fadeoff >> 6, 0);
    }
    FastLED.show();

  }

}

It's not the polyfuse because the LED's are powered by a different power supply, and if I comment out this line near the bottom:

Code:
digitalWriteFast(13, LOW);

the indicator LED will remain on even after the Neopixels stop responding to MIDI input. And with my original sketch, I noticed the same problem without any Neopixels connected, just watching an indicator LED that stopped turning on.

Maybe it could be a RAM issue, but when it compiles it says global variables only use 30% of dynamic memory, 2472 out of 8192 bytes. It's hard to imagine that the MIDI library and 12 Neopixels and a simple sketch could max out the RAM.

The same problem happens with the Neopixel library, but the strand test example works fine without issues. Colorpalette test example works fine with FastLED library.

The problem also happens if I don't use callbacks and use the MIDI library the other way.

Any ideas about what's going on?
 
It really looks like this is the explanation:
https://github.com/FastLED/FastLED/wiki/Interrupt-problems
Sending data for 11 or 12 LED's is approximately the time of one MIDI byte. Apparently this is a non-issue for Teensy 3.x but is an issue for Teensy LC.

This looks like a guaranteed fix: (last example)
https://github.com/FastLED/FastLED/wiki/Multiple-Controller-Examples

But I'm wondering if I re-wire the MIDI input to RX3, the only FIFO buffer, would that make a difference? Teensy 3.x has FIFO for all serials.
 
Last edited:
This is a well known problem, where WS2812 LED update disables interrupts, interfering with serial communication and other libraries requiring interrupts.

WS2812Serial is probably the answer. To see how to use it with FastLED, click File > Examples > WS2812Serial > FastLED_Cylon.

On Teensy LC, unfortunately WS2812Serial only supports transmitting on Serial1, so you can't use Serial1 for other purposes. The LEDs need to connect to pins 1, 4, 5, or 24 (the TX1 capable pins). Serial2 & Serial3 on LC don't have configurable uart oversample ratio, so those ports are unable to run at the correct speed for WS2812.

If you're using MIDI or other serial stuff, you'd need to connect them to Serial2 or Serial3 on Teensy LC. Fortunately both of those work fine for MIDI.

On Teensy 3.x, the FIFOs can help slightly if a library disables interrupts. But WS2812Serial or OctoWS2811 are still the best solution, since they don't disable interrupts. MIDI at 31250 is slow enough that the FIFOs make very little improvement, assuming no library like Adafruit_NeoPixel or FastLED with the normal WS2812 driver are shutting off interrupts.

WS2812Serial never uses the FIFO, even if used on Serial1 or Serial2 on Teensy 3.x. If you have regular serial stuff you want to run efficiently on Teensy 3.2, use Serial1 or Serial2 and let WS2812Serial use Serial3. WS2812Serial can work on any of the ports of Teensy 3.2. Only Teensy LC has the limit where WS2812Serial can't use Serial2 & Serial3. The ports are Teensy 3.2 are more capable in many ways, not just the FIFOs.
 
Last edited:
Thanks! A lot of good info. I'll read up on that and try some things.

To use WS2812Serial on Serial1 and use the on-board level-converter, I'm guessing I can connect a resistor between pins 1 and 17, then set 17 to INPUT.
 
Yes, that's right, a wire from pin 1 to 17 will do it. Or a wire from pin 24 to 17 would also work, if you want use pin 1 for non-serial stuff.

Setting pin 17 to INPUT mode is optional. The pins default to a low power disabled state, so just leaving it unconfigured is also perfectly fine. Just make sure you don't use pin 17 as an output or analogWrite.
 
It worked great! I wired a 1K resistor from pin 1 to pin 17 because I'm making this for a friend who is new to Arduino/Teensy, and it would be a bummer if he fried some pins while learning to code, so I didn't use a wire. :) I was able to get the BasicTest program to work but not the FastLED_Cylon, so I wrote this function to convert 8-bit RGB channels in decimal format into the web color hexadecimal format:

Code:
void setRGB(byte i, unsigned long red, unsigned long green, unsigned long blue)
{
  leds.setPixel(i, (red << 16) + (green << 8) + blue);
}

And this test code works with MIDI and Neopixels:

Code:
#include <WS2812Serial.h>

const int NUM_LEDS = 12;
const int DATA_PIN = 1;

byte drawingMemory[NUM_LEDS*3];         //  3 bytes per LED
DMAMEM byte displayMemory[NUM_LEDS*12]; // 12 bytes per LED

WS2812Serial leds(NUM_LEDS, displayMemory, drawingMemory, DATA_PIN, WS2812_RGB);

#include <MIDI.h>

struct MySettings : public midi::DefaultSettings
{
 // No changes needed.
};

MIDI_CREATE_CUSTOM_INSTANCE(HardwareSerial, Serial3, MIDI, MySettings);

unsigned int Fadeoff;

void handleNoteOn(byte channel, byte pitch, byte velocity)
{
  Fadeoff = 4095;
  digitalWriteFast(13, HIGH);
}

void setup() 
{

  leds.begin();

  MIDI.begin(MIDI_CHANNEL_OMNI);
  MIDI.setHandleNoteOn(handleNoteOn);

  pinMode(13, OUTPUT);  // Indicator LED on Teensy LC.
  digitalWriteFast(13, LOW);
  
  for (int i = 0; i < NUM_LEDS; i++)
  {
    setRGB(i, 0, 0, 0);
  }
  leds.show();

}

void loop() 
{

  while (1) 
  {

    MIDI.read();

    for (int i = 0; i < NUM_LEDS; i++)
    {
      setRGB(i, Fadeoff >> 4, Fadeoff >> 6, 0);
    }
    leds.show();

    if (Fadeoff)
    { Fadeoff -= ((Fadeoff >> 8) + 2);
      if (Fadeoff < 0) {
        Fadeoff = 0;
      }
    }
    
    else {
      digitalWriteFast(13, LOW);
    }; // Oddly, it still works despite this extra semicolon that should give an error message. :)

  }

}

void setRGB(byte i, unsigned long red, unsigned long green, unsigned long blue)
{
  leds.setPixel(i, (red << 16) + (green << 8) + blue);
}

wiring.jpg

mini_light_system.jpg
 
Status
Not open for further replies.
Back
Top