Teensy audio and FastLED SPI?

Status
Not open for further replies.

apleschu

Member
So here is my predicament: the project I am working on calls for about 2000 LEDS, triggered and modified by sound. Specifically it will be a wearable LED installation for burning Man. Everything starts to come nicely together, so far so good. Yesterday the audio shields arrived and so far I like it. There is a lot I can do with that. BUT there is one really nagging problem. It seems that the FastLED library and the teensy audio library are not best pals. What I mean is that if I use hardware spi to drive the LEDs (at around 5MHz) then the FFT library indicates that there is a lot of distortion, aka high frequency noise. this can be prevented by using bit-banging the LEDs, on different pins, but that of course means that the LEDs are being handled much slower, and with 2000 LEDs to handle that could be a problem. Is there anything I can do, tell the audio library to make those two peacefully co-exist on the same processor (but on different pins?)
 
Guessing from only what you've described (without posting code... the "Forum Rule"), this sounds like an analog signal problem, utterly unrelated to the libraries & software on Teensy.

I saw the same thing happening when using OctoWS2811 with the audio lib on an ADC pin. There's a huge amount of noise at the 800 kHz output frequency. It couples to the analog input and aliases into the audio band.

Keeping digital switching noise out of sensitive analog signals is tough. The small details of how you're powering and connecting everything matter greatly.

Of course, the very first step would be to really figure out if there is indeed a software compatibility problem. That's unlikely, since these 2 libraries don't overlap in hardware resources, unless you use the stuff in the library which access the SPI port. Still, some tests like whether the problems change when you're driving the LEDs versus just letting the pins toggle without any wires connected would really help, and let you focus your effort on the right area (and be able to post the right soft of info here... source code vs photos & wiring diagrams).
 
Guessing from only what you've described (without posting code... the "Forum Rule"), this sounds like an analog signal problem, utterly unrelated to the libraries & software on Teensy.

I saw the same thing happening when using OctoWS2811 with the audio lib on an ADC pin. There's a huge amount of noise at the 800 kHz output frequency. It couples to the analog input and aliases into the audio band.

Keeping digital switching noise out of sensitive analog signals is tough. The small details of how you're powering and connecting everything matter greatly.

Of course, the very first step would be to really figure out if there is indeed a software compatibility problem. That's unlikely, since these 2 libraries don't overlap in hardware resources, unless you use the stuff in the library which access the SPI port. Still, some tests like whether the problems change when you're driving the LEDs versus just letting the pins toggle without any wires connected would really help, and let you focus your effort on the right area (and be able to post the right soft of info here... source code vs photos & wiring diagrams).

I highly doubt this is an analog problem since the audio comes from the mic on the audio shield, but I see your problem in deciding what it could be. I was just on the iPad yesterday evening when posting the question and did not have everything together. So this post will be a longer one, showing the whole setup and the relevant portions of the code. (There is more code, but that is not involved and if you want to I am happy to post the rest as well)

And with that: here is the main loop

Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#include <FastLED.h>
#include <RunningAverage.h>
#include <EEPROM.h>
#include <FastCRC.h>

#include "definitions.h"

#define NUM_LEDS (MatrixWidth * MatrixHeight)
CRGB leds_plus_safety_pixel[ NUM_LEDS + 1];
CRGB leds2_plus_safety_pixel[ NUM_LEDS + 1];
CRGB* const leds( leds_plus_safety_pixel + 1);
CRGB* const leds2( leds_plus_safety_pixel + 1);
CRGBPalette16 firePalette;

float mgain1 = 1.0;
float mgain2 = 1.0;
//static uint8_t Debug = 1;

#include "AudioDefinitions.h"

RunningAverage avg(255);

#include "misc.h"
#include "setup.h"

#include "Effects.h"
#include "Fire.h"
#include "SpectrumAnalyzer.h"
#include "DiffBase.h"

typedef void (*FrameList[])();
FrameList drawFrame = {SpectrumAnalyzer, Fire, DiffBase };

#include "Effects.h"
#include "Fire.h"

void loop() { 
//  int vol = analogRead(A1) / 20;
//  sgtl5000.micGain(vol);   // vol is integer
//  if (Serial) Serial.println(vol);
 drawFrame[0]();
 FastLED.show();
}

definitions.h:
Code:
#define BRIGHTNESS  64
#define NoiseFloor  0.05
#define TargetAVG   1.0              // Target avg for AGC
#define GainAdjust  0.001            // AGC uses this value to increase or decrease gain each loop, bigger values make AGC react faster
#define MAXGAIN     1.0              // AGC will not set the mixer gain higher than this
#define MINGAIN     0.01              // AGC will not set mixer gain lower than this
#define LogCurve    3        
#define MatrixHeight 10
#define MatrixWidth 10

#define DATA_PIN  7
#define CLOCK_PIN 6
#define COLOR_ORDER BGR
#define CHIPSET     APA102
#define UpdateFrequencyMHZ  1

#define MATRIX_CENTRE_Y 5
#define MATRIX_CENTRE_X 5

const bool  MatrixSerpentineLayout = true;

AudioDefinitions.h:
Code:
// GUItool: begin automatically generated code
AudioInputI2S            i2s1;           //xy=115,244
AudioMixer4              mixer;          //xy=400,248
AudioAnalyzeFFT1024      fft;            //xy=649,152
AudioAnalyzePeak         peak;           //xy=653,380
AudioConnection          patchCord1(i2s1, 0, mixer, 0);
AudioConnection          patchCord2(mixer, fft);
AudioConnection          patchCord3(mixer, peak);
AudioControlSGTL5000     sgtl5000;       //xy=335,609
// GUItool: end automatically generated code

misc.h:
Code:
float cVal(int start, int end){
  double val = 0;                         // starting value
  double res = 0;

  val = fft.read(start,end);          // add 1 so that we get to the correct part of the log2 curve
  if ( val > NoiseFloor ) {
    res = ((log10(val) + 2) / 2);   // 0(Noisefloor) is at a value of approx 0.3
    return(res);
  } else {
    return((float)0.0);
  }
}

// This function will return the right 'led index number' for 
// a given set of X and Y coordinates on your matrix.  
// IT DOES NOT CHECK THE COORDINATE BOUNDARIES.  
// That's up to you.  Don't pass it bogus values.
//
uint16_t XY( uint8_t x, uint8_t y)
{
  uint16_t i;
  
  if( MatrixSerpentineLayout == false) {
    i = (y * MatrixWidth) + x;
  }

  if( MatrixSerpentineLayout == true) {
    if( y & 0x01) {
      // Odd rows run backwards
      uint8_t reverseX = (MatrixWidth - 1) - x;
      i = (y * MatrixWidth) + reverseX;
    } else {
      // Even rows run forwards
      i = (y * MatrixWidth) + x;
    }
  } 
  return i;
}

uint16_t xySave( uint8_t x, uint8_t y)
{
  if( x >= MatrixWidth) return -1;
  if( y >= MatrixHeight) return -1;
  return XY(x,y);
}

void cLed(int band, int start, int end){
  int displayVal = 0;
  float val = cVal(start,end);
  if (val > 1) val = 1;
  val = val * 100;                  // bring into usable are
  displayVal = map((long) val, 39,100,0, MatrixHeight);  // Number of LEDs  
  for ( int i = 0; i < displayVal; i++) {
    int index = xySave(i, band);
    leds[index] = CHSV(100-i*10, 255, 200);  
  }
}

setup.h:
Code:
void setup() {
  Serial.begin(9600);
//  while (!Serial);                    // wait for serial to start
  
  AudioMemory(10);
  sgtl5000.enable();
  sgtl5000.volume(0.5);
  sgtl5000.inputSelect(AUDIO_INPUT_MIC);
  sgtl5000.micGain(30);
  FastLED.addLeds<APA102,DATA_PIN,CLOCK_PIN,BGR,DATA_RATE_MHZ(UpdateFrequencyMHZ)>(leds, NUM_LEDS).setCorrection(TypicalSMD5050);
  FastLED.setBrightness( BRIGHTNESS );
  FastLED.setDither(0);
  FastLED.setMaxPowerInVoltsAndMilliamps(5,2000);
  for (int i = 0; i < NUM_LEDS; i++)
    leds[i] = 0x000000;
  FastLED.show();
  // Uncomment one these to try other window functions
  // fft1024_1.windowFunction(NULL);
  fft.windowFunction(AudioWindowBartlett1024);
  // fft.windowFunction(AudioWindowFlattop1024);
  avg.clear();
  mixer.gain(0, mgain1);
//  mixer.gain(1, mgain2);
  firePalette = CRGBPalette16( CRGB::Black, CRGB::DarkOrange, CRGB::Red, CRGB::White);
  pinMode(5, OUTPUT);
}

and finally the piece of the code that shows the problem:
SpectrumAnalyzer.h
Code:
void SpectrumAnalyzer() { 
 double val = 0;

  if (fft.available()) {
    blur2d( leds, MatrixWidth, MatrixHeight, 3);
    fadeToBlackBy(leds,NUM_LEDS, 30);
      
//    cLed(0,1,1);      // 43             // too much noise in this bin
    cLed(1,2,3);      // 86
    cLed(2,3,4);      // 172
    cLed(3,5,8);      // 344  
    cLed(4,9,16);     // 688
    cLed(5,17,32);    // 1376
    cLed(6,33,64);    // 2752
    cLed(7,65,128);   // 5504
    cLed(8,129,255);  // 11008
    cLed(9,256,511);  // rest

    val = fft.read(2,128);
    avg.addValue(val);
    double ravg = avg.getAverage();
 
    if (ravg > TargetAVG)
      mgain1 -= GainAdjust;
    else
      mgain1 += GainAdjust;

    if (mgain1 > MAXGAIN)
      mgain1 = MAXGAIN;         //lets not overdo it!
    if (mgain1 < MINGAIN)
      mgain1 = MINGAIN;         // same here
    mixer.gain(0, mgain1);
  }
}

The way I posted the code is the way it works. bitbanging the protocol on two arbitrary pins. I will include links to some short videos that do show the problem rather clearly.

Also, here is the code when it does not work: Everything stays the same, with the exception of
definitions.h:
Code:
#define BRIGHTNESS  64
#define NoiseFloor  0.05
#define TargetAVG   1.0              // Target avg for AGC
#define GainAdjust  0.001            // AGC uses this value to increase or decrease gain each loop, bigger values make AGC react faster
#define MAXGAIN     1.0              // AGC will not set the mixer gain higher than this
#define MINGAIN     0.01              // AGC will not set mixer gain lower than this
#define LogCurve    3        
#define MatrixHeight 10
#define MatrixWidth 10

#define DATA_PIN  7
#define CLOCK_PIN 14
#define COLOR_ORDER BGR
#define CHIPSET     APA102
#define UpdateFrequencyMHZ  1

#define MATRIX_CENTRE_Y 5
#define MATRIX_CENTRE_X 5

const bool  MatrixSerpentineLayout = true;

andy you can see that the only thing that changed is the clock pin moved to a pin that is really a clock pin for the UART and thus the FastLED library switches from bitbanging to using the hardware UART. The videos show this rather well. In addition, as you might be able to see from the videos the LED array has its own power supply, and the connection to the teensy is via 3 lines (GND, CLK, DATA)

The links in the next post ....
 
My MacBook decided it had to reboot/update right while I was writing the post, so here is the second part with all the documentation:

So, here is the setup that works. (The LEDs connected to 6 and 7 as per definitions.h)
IMG_0027.JPG

IMG_0028.JPG


And the video that shows how it is supposed to look like when it works with sound and without.
[video]http://www.ple.org/TeensyAudioProblem/IMG_0029.MOV[/video]

And the setup that doesn't work: With the LEDs connected to 7 and 14 and thus using hardware SPI)
IMG_0031.JPG


And the video that shows how it should not look like when it is quiet
[video]http://www.ple.org/TeensyAudioProblem/IMG_0032.MOV[/video]
 
Code:
/home/paul/teensy/sketch/fastled_issue/fastled_issue.ino:7:28: fatal error: RunningAverage.h: No such file or directory
#include <RunningAverage.h>
^
compilation terminated.

Edit: looks like Effects.h any others maybe also be missing. I can't run it here if stuff is missing!
 
Last edited:
Yes, this looks similar. Because it is the other side of the equation.

As for RunningAverage: https://github.com/RobTillaart/Arduino/tree/master/libraries/RunningAverage

Effects.h
Code:
/*
 * Aurora: https://github.com/pixelmatix/aurora
 * Copyright (c) 2014 Jason Coon
 *
 * Portions of this code are adapted from "Funky Clouds" by Stefan Petrick: https://gist.github.com/anonymous/876f908333cd95315c35
 * Portions of this code are adapted from "NoiseSmearing" by Stefan Petrick: https://gist.github.com/StefanPetrick/9ee2f677dbff64e3ba7a
 * Copyright (c) 2014 Stefan Petrick
 * http://www.stefan-petrick.de/wordpress_beta
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#ifndef Effects_H
#define Effects_H

#define MAX_COLOR_VALUE 255

#define SWAPint(X,Y) { \
        int temp = X ; \
        X = Y ; \
        Y = temp ; \
    }
    
uint8_t beatcos8(accum88 beats_per_minute, uint8_t lowest = 0, uint8_t highest = 255, uint32_t timebase = 0, uint8_t phase_offset = 0)
{
  uint8_t beat = beat8(beats_per_minute, timebase);
  uint8_t beatcos = cos8(beat + phase_offset);
  uint8_t rangewidth = highest - lowest;
  uint8_t scaledbeat = scale8(beatcos, rangewidth);
  uint8_t result = lowest + scaledbeat;
  return result;
}

uint8_t beattriwave8(accum88 beats_per_minute, uint8_t lowest = 0, uint8_t highest = 255, uint32_t timebase = 0, uint8_t phase_offset = 0)
{
  uint8_t beat = beat8(beats_per_minute, timebase);
  uint8_t beatcos = triwave8(beat + phase_offset);
  uint8_t rangewidth = highest - lowest;
  uint8_t scaledbeat = scale8(beatcos, rangewidth);
  uint8_t result = lowest + scaledbeat;
  return result;
}

uint8_t mapsin8(uint8_t theta, uint8_t lowest = 0, uint8_t highest = 255) {
  uint8_t beatsin = sin8(theta);
  uint8_t rangewidth = highest - lowest;
  uint8_t scaledbeat = scale8(beatsin, rangewidth);
  uint8_t result = lowest + scaledbeat;
  return result;
}

uint8_t mapcos8(uint8_t theta, uint8_t lowest = 0, uint8_t highest = 255) {
  uint8_t beatcos = cos8(theta);
  uint8_t rangewidth = highest - lowest;
  uint8_t scaledbeat = scale8(beatcos, rangewidth);
  uint8_t result = lowest + scaledbeat;
  return result;
}

// Array of temperature readings at each simulation cell
byte heat[MatrixWidth][MatrixHeight];

uint32_t noise_x;
uint32_t noise_y;
uint32_t noise_z;
uint32_t noise_scale_x;
uint32_t noise_scale_y;

uint8_t noise[MatrixWidth][MatrixHeight];

uint8_t noisesmoothing;

  // Oscillators and Emitters

  // the oscillators: linear ramps 0-255
  byte osci[6];

  // sin8(osci) swinging between 0 to MatrixWidth - 1
  byte p[6];

  // set the speeds (and by that ratios) of the oscillators here
  void MoveOscillators() {
    osci[0] = osci[0] + 5;
    osci[1] = osci[1] + 2;
    osci[2] = osci[2] + 3;
    osci[3] = osci[3] + 4;
    osci[4] = osci[4] + 1;
    if (osci[4] % 2 == 0)
      osci[5] = osci[5] + 1; // .5
    for (int i = 0; i < 4; i++) {
      p[i] = map8(sin8(osci[i]), 0, MatrixWidth - 1); //why? to keep the result in the range of 0-MatrixWidth (matrix size)
    }
  }



  // scale the brightness of the screenbuffer down
  void DimAll(byte value)
  {
    for (int i = 0; i < NUM_LEDS; i++)
    {
      leds[i].nscale8(value);
    }
  }

  // give it a linear tail to the right
  void StreamRight(byte scale, int fromX = 0, int toX = MatrixWidth, int fromY = 0, int toY = MatrixHeight)
  {
    for (int x = fromX + 1; x < toX; x++) {
      for (int y = fromY; y < toY; y++) {
        leds[XY(x, y)] += leds[XY(x - 1, y)];
        leds[XY(x, y)].nscale8(scale);
      }
    }
    for (int y = fromY; y < toY; y++)
      leds[XY(0, y)].nscale8(scale);
  }

  // give it a linear tail to the left
  void StreamLeft(byte scale, int fromX = MatrixWidth, int toX = 0, int fromY = 0, int toY = MatrixHeight)
  {
    for (int x = toX; x < fromX; x++) {
      for (int y = fromY; y < toY; y++) {
        leds[XY(x, y)] += leds[XY(x + 1, y)];
        leds[XY(x, y)].nscale8(scale);
      }
    }
    for (int y = fromY; y < toY; y++)
      leds[XY(0, y)].nscale8(scale);
  }

  // give it a linear tail downwards
  void StreamDown(byte scale)
  {
    for (int x = 0; x < MatrixWidth; x++) {
      for (int y = 1; y < MatrixHeight; y++) {
        leds[XY(x, y)] += leds[XY(x, y - 1)];
        leds[XY(x, y)].nscale8(scale);
      }
    }
    for (int x = 0; x < MatrixWidth; x++)
      leds[XY(x, 0)].nscale8(scale);
  }

  // give it a linear tail upwards
  void StreamUp(byte scale)
  {
    for (int x = 0; x < MatrixWidth; x++) {
      for (int y = MatrixHeight - 2; y >= 0; y--) {
        leds[XY(x, y)] += leds[XY(x, y + 1)];
        leds[XY(x, y)].nscale8(scale);
      }
    }
    for (int x = 0; x < MatrixWidth; x++)
      leds[XY(x, MatrixHeight - 1)].nscale8(scale);
  }

  // give it a linear tail up and to the left
  void StreamUpAndLeft(byte scale)
  {
    for (int x = 0; x < MatrixWidth - 1; x++) {
      for (int y = MatrixHeight - 2; y >= 0; y--) {
        leds[XY(x, y)] += leds[XY(x + 1, y + 1)];
        leds[XY(x, y)].nscale8(scale);
      }
    }
    for (int x = 0; x < MatrixWidth; x++)
      leds[XY(x, MatrixHeight - 1)].nscale8(scale);
    for (int y = 0; y < MatrixHeight; y++)
      leds[XY(MatrixWidth - 1, y)].nscale8(scale);
  }

  // give it a linear tail up and to the right
  void StreamUpAndRight(byte scale)
  {
    for (int x = 0; x < MatrixWidth - 1; x++) {
      for (int y = MatrixHeight - 2; y >= 0; y--) {
        leds[XY(x + 1, y)] += leds[XY(x, y + 1)];
        leds[XY(x, y)].nscale8(scale);
      }
    }
    // fade the bottom row
    for (int x = 0; x < MatrixWidth; x++)
      leds[XY(x, MatrixHeight - 1)].nscale8(scale);

    // fade the right column
    for (int y = 0; y < MatrixHeight; y++)
      leds[XY(MatrixWidth - 1, y)].nscale8(scale);
  }

  // just move everything one line down
  void MoveDown() {
    for (int y = MatrixHeight - 1; y > 0; y--) {
      for (int x = 0; x < MatrixWidth; x++) {
        leds[XY(x, y)] = leds[XY(x, y - 1)];
      }
    }
  }

  // just move everything one line down
  void VerticalMoveFrom(int start, int end) {
    for (int y = end; y > start; y--) {
      for (int x = 0; x < MatrixWidth; x++) {
        leds[XY(x, y)] = leds[XY(x, y - 1)];
      }
    }
  }


  void BresenhamLine(int x0, int y0, int x1, int y1, CRGB color)
  {
    int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
    int dy = -abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
    int err = dx + dy, e2;
    for (;;) {
      leds[XY(x0, y0)] += color;
      if (x0 == x1 && y0 == y1) break;
      e2 = 2 * err;
      if (e2 > dy) {
        err += dy;
        x0 += sx;
      }
      if (e2 < dx) {
        err += dx;
        y0 += sy;
      }
    }
  }


  CRGB HsvToRgb(uint8_t h, uint8_t s, uint8_t v) {
    CHSV hsv = CHSV(h, s, v);
    CRGB rgb;
    hsv2rgb_spectrum(hsv, rgb);
    return rgb;
  }

  void NoiseVariablesSetup() {
    noisesmoothing = 200;

    noise_x = random16();
    noise_y = random16();
    noise_z = random16();
    noise_scale_x = 6000;
    noise_scale_y = 6000;
  }

  void FillNoise() {
    for (uint8_t i = 0; i < MatrixWidth; i++) {
      uint32_t ioffset = noise_scale_x * (i - MATRIX_CENTRE_Y);

      for (uint8_t j = 0; j < MatrixHeight; j++) {
        uint32_t joffset = noise_scale_y * (j - MATRIX_CENTRE_Y);

  byte data = inoise16(noise_x + ioffset, noise_y + joffset, noise_z) >> 8;

        uint8_t olddata = noise[i][j];
        uint8_t newdata = scale8(olddata, noisesmoothing) + scale8(data, 256 - noisesmoothing);
        data = newdata;

        noise[i][j] = data;
      }
    }
  }

  void MoveX(byte delta) {
    for (int y = 0; y < MatrixHeight; y++) {
      for (int x = 0; x < MatrixWidth - delta; x++) {
        leds2[XY(x, y)] = leds[XY(x + delta, y)];
      }
      for (int x = MatrixWidth - delta; x < MatrixWidth; x++) {
        leds2[XY(x, y)] = leds[XY(x + delta - MatrixWidth, y)];
      }
    }

    // write back to leds
    for (uint8_t y = 0; y < MatrixHeight; y++) {
      for (uint8_t x = 0; x < MatrixWidth; x++) {
        leds[XY(x, y)] = leds2[XY(x, y)];
      }
    }
  }

  void MoveY(byte delta) {
    for (int x = 0; x < MatrixWidth; x++) {
      for (int y = 0; y < MatrixHeight - delta; y++) {
        leds2[XY(x, y)] = leds[XY(x, y + delta)];
      }
      for (int y = MatrixHeight - delta; y < MatrixHeight; y++) {
        leds2[XY(x, y)] = leds[XY(x, y + delta - MatrixHeight)];
      }
    }

    // write back to leds
    for (uint8_t y = 0; y < MatrixHeight; y++) {
      for (uint8_t x = 0; x < MatrixWidth; x++) {
        leds[XY(x, y)] = leds2[XY(x, y)];
      }
    }
  }

  void MoveFractionalNoiseX(byte amt = 16) {
    // move delta pixelwise
    for (int y = 0; y < MatrixHeight; y++) {
      uint16_t amount = noise[0][y] * amt;
      byte delta = 31 - (amount / 256);

      for (int x = 0; x < MatrixWidth - delta; x++) {
        leds2[XY(x, y)] = leds[XY(x + delta, y)];
      }
      for (int x = MatrixWidth - delta; x < MatrixWidth; x++) {
        leds2[XY(x, y)] = leds[XY(x + delta - MatrixWidth, y)];
      }
    }

    //move fractions
    CRGB PixelA;
    CRGB PixelB;

    for (uint8_t y = 0; y < MatrixHeight; y++) {
      uint16_t amount = noise[0][y] * amt;
      byte delta = 31 - (amount / 256);
      byte fractions = amount - (delta * 256);

      for (uint8_t x = 1; x < MatrixWidth; x++) {
        PixelA = leds2[XY(x, y)];
        PixelB = leds2[XY(x - 1, y)];

        PixelA %= 255 - fractions;
        PixelB %= fractions;

        leds[XY(x, y)] = PixelA + PixelB;
      }

      PixelA = leds2[XY(0, y)];
      PixelB = leds2[XY(MatrixWidth - 1, y)];

      PixelA %= 255 - fractions;
      PixelB %= fractions;

      leds[XY(0, y)] = PixelA + PixelB;
    }
  }

  void MoveFractionalNoiseY(byte amt = 16) {
    // move delta pixelwise
    for (int x = 0; x < MatrixWidth; x++) {
      uint16_t amount = noise[x][0] * amt;
      byte delta = 31 - (amount / 256);

      for (int y = 0; y < MatrixWidth - delta; y++) {
        leds2[XY(x, y)] = leds[XY(x, y + delta)];
      }
      for (int y = MatrixWidth - delta; y < MatrixWidth; y++) {
        leds2[XY(x, y)] = leds[XY(x, y + delta - MatrixWidth)];
      }
    }

    //move fractions
    CRGB PixelA;
    CRGB PixelB;

    for (uint8_t x = 0; x < MatrixHeight; x++) {
      uint16_t amount = noise[x][0] * amt;
      byte delta = 31 - (amount / 256);
      byte fractions = amount - (delta * 256);

      for (uint8_t y = 1; y < MatrixWidth; y++) {
        PixelA = leds2[XY(x, y)];
        PixelB = leds2[XY(x, y - 1)];

        PixelA %= 255 - fractions;
        PixelB %= fractions;

        leds[XY(x, y)] = PixelA + PixelB;
      }

      PixelA = leds2[XY(x, 0)];
      PixelB = leds2[XY(x, MatrixWidth - 1)];

      PixelA %= 255 - fractions;
      PixelB %= fractions;

      leds[XY(x, 0)] = PixelA + PixelB;
    }
  }

  void standardNoiseSmearing() {
    noise_x += 1000;
    noise_y += 1000;
    noise_scale_x = 4000;
    noise_scale_y = 4000;
    FillNoise();

    MoveX(3);
    MoveFractionalNoiseY(4);

    MoveY(3);
    MoveFractionalNoiseX(4);
  }

#endif
 
Now missing Fire.h.

Are you going to make me keep trying and asking for the missing files one-by-one? Any chance you could just put all the files into a single ZIP file and attach it here (click "Go Advanced" to get to the message posting where you can attach a zip file).
 
Or better yet, if there really is a compatibility problem between FastLED and Teensy Audio, is there any chance you could create a small program in just a single file which reproduces the problem?

Yeah, I know that requires some extra work. Either way, I do want to help you and I absolutely want to get to the bottom of any library conflicts. But you really could make this a little easier and less time consuming with a small, single-file example. That'd let me focus more time on help you and others too.
 
I apologize for the files missing, I thought the pertinent files are enough. The other files are "just" different effects.

I did a little bit more development on the project and the volume on the audio card selects the effects. The one that shows the problem is all the dialed down (volume at or near 0)

this one should compile, it is the whole folder as you requested, I will also work on on a single file and attach that in another post. I hope I can get this done today. But here is the whole project for now.

Edit: The problem is visible when the data and clock pins are changed to 7 and 14 (alternative SPI PINS) Where they are right now the problem does not show, but now the CPU is bit-banging the leds

Edit2: output is to a 10x10 APA102 Matrix
 

Attachments

  • foo.zip
    10 KB · Views: 140
Last edited:
As requested ... here is one file that shows the problem. Look for the definitions of CLOCK_PIN and DATA_PIN. if you change it to 7/14 you will see the problem in the FFT data as well as on the display (10x10 matrix), or on 0 and 1 no problem.

Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#include <FastLED.h>

#define BRIGHTNESS  32
#define NoiseFloor  0.05
#define TargetAVG   3.0               // Target avg for AGC
#define peakTargetAVG 0.4
#define GainAdjust  0.0001            // AGC uses this value to increase or decrease gain each loop, bigger values make AGC react faster
#define MAXGAIN     2.0              // AGC will not set the mixer gain higher than this
#define MINGAIN     0.01              // AGC will not set mixer gain lower than this
#define LogCurve    3        
#define MatrixHeight 10
#define MatrixWidth 10

//#define DATA_PIN  1
//#define CLOCK_PIN 0
#define DATA_PIN 7
#define CLOCK_PIN 14
#define COLOR_ORDER BGR
#define CHIPSET     APA102
#define UpdateFrequencyMHZ  1

#define MATRIX_CENTRE_Y 5
#define MATRIX_CENTRE_X 5

const bool  MatrixSerpentineLayout = true;

#define NUM_LEDS (MatrixWidth * MatrixHeight)
CRGB leds_plus_safety_pixel[ NUM_LEDS + 1];
CRGB leds2_plus_safety_pixel[ NUM_LEDS + 1];
CRGB* const leds( leds_plus_safety_pixel + 1);
CRGB* const leds2( leds_plus_safety_pixel + 1);

float mgain1 = 1.0;
float mgain2 = 1.0;


// GUItool: begin automatically generated code
AudioInputI2S            i2s1;           //xy=115,244
AudioMixer4              mixer;          //xy=400,248
AudioAnalyzeFFT1024      fft;            //xy=649,152
AudioAnalyzePeak         peak;           //xy=653,380
AudioConnection          patchCord1(i2s1, 0, mixer, 0);
AudioConnection          patchCord2(mixer, fft);
AudioConnection          patchCord3(mixer, peak);
AudioControlSGTL5000     sgtl5000;       //xy=335,609
// GUItool: end automatically generated code


float cVal(int start, int end){
  double val = 0;                         // starting value
  double res = 0;

  val = fft.read(start,end);          // add 1 so that we get to the correct part of the log2 curve
  if ( val > NoiseFloor ) {
    res = ((log10(val) + 2) / 2);   // 0(Noisefloor) is at a value of approx 0.3
    return(res);
  } else {
    return((float)0.0);
  }
}

// This function will return the right 'led index number' for 
// a given set of X and Y coordinates on your matrix.  
// IT DOES NOT CHECK THE COORDINATE BOUNDARIES.  
// That's up to you.  Don't pass it bogus values.
//
uint16_t XY( uint8_t x, uint8_t y)
{
  uint16_t i;
  
    if( y & 0x01) {
      // Odd rows run backwards
      uint8_t reverseX = (MatrixWidth - 1) - x;
      i = (y * MatrixWidth) + reverseX;
    } else {
      // Even rows run forwards
      i = (y * MatrixWidth) + x;
    }
 
  return i;
}

uint16_t xySave( uint8_t x, uint8_t y)
{
  if( x >= MatrixWidth) return -1;
  if( y >= MatrixHeight) return -1;
  return XY(x,y);
}

void cLed(int band, int start, int end){
  int displayVal = 0;
  float val = cVal(start,end);
  if (val > 1) val = 1;
  val = val * 100;                  // bring into usable are
  displayVal = map((long) val, 39,100,0, MatrixHeight);  // Number of LEDs  
  for ( int i = 0; i < displayVal; i++) {
    int index = xySave(i, band);
    leds[index] = CHSV(100-i*10, 255, 200);  
  }
}

void colorLed(int i, int start, int end){
  int displayVal = 0;
  float val = fft.read(start,end); 
  if (val > 1) val = 1;
  displayVal = (int) (val *255);
  leds[i] = CHSV(255 - displayVal, 200, displayVal);
}




void setup() {
  Serial.begin(9600);
//  while (!Serial);                    // wait for serial to start
  
  AudioMemory(10);
  sgtl5000.enable();
  sgtl5000.volume(0.5);
  sgtl5000.inputSelect(AUDIO_INPUT_MIC);
  sgtl5000.micGain(40);
  FastLED.addLeds<APA102,DATA_PIN,CLOCK_PIN,BGR,DATA_RATE_MHZ(UpdateFrequencyMHZ)>(leds, NUM_LEDS).setCorrection(TypicalSMD5050);
  FastLED.setBrightness( BRIGHTNESS );
  FastLED.setDither(0);
  FastLED.setMaxPowerInVoltsAndMilliamps(5,2000);
  for (int i = 0; i < NUM_LEDS; i++)
    leds[i] = 0x000000;
  FastLED.show();
  fft.windowFunction(AudioWindowBartlett1024);
  mixer.gain(0, mgain1);

}


void loop() {
   if (fft.available()) {
    blur2d( leds, MatrixWidth, MatrixHeight, 3);
    fadeToBlackBy(leds,NUM_LEDS, 30);

    cLed(0,1,1);      // 43             
    cLed(1,2,3);      // 86
    cLed(2,3,4);      // 172
    cLed(3,5,8);      // 344  
    cLed(4,9,16);     // 688
    cLed(5,17,32);    // 1376
    cLed(6,33,64);    // 2752
    cLed(7,65,128);   // 5504
    cLed(8,129,255);  // 11008
    cLed(9,256,511);  // rest
    
  }
  FastLED.show();
}
 
Last edited:
Which version of FastLED are you using?

When I compile this, I get:

Code:
'class CFastLED' has no member named 'setMaxPowerInVoltsAndMilliamps'

I have version 3.1.0.
 
Same version. I just tested, that command is not needed in the test version. That is more preparation for the real product, when it runs on battery and I will need to limit current draw.

That said, when I compile it uses, the version I downloaded yesterday from the FastLED.io web site:

Code:
multiple libraries were found for "FastLED.h"
 Used: /Users/andy/src/Arduino/libraries/FastLED
 Not used: /Applications/Arduino.app/Contents/Java/hardware/teensy/avr/libraries/FastLED

Since this is one of the libraries that I use in other projects where teensy is not the CPU, I rather have it in a common area.
 
Ok, I commented out that line and the program now compiles.

I'm about to start actually testing on a Teensy... but every indication is you have a different version of FastLED in /Users/andy/src/Arduino/libraries/FastLED. So if I can't reproduce the problem or find anything wrong, I guess this will be wasted effort and I'll end up redoing it again.

If you see this soon, maybe you could check which version you really have in /Users/andy/src/Arduino/libraries/FastLED? Maybe post a zip file with the *exact* copy of FastLED you're really using?
 
I'm testing now, with a strip of 144 LEDs. I edited the defines for a 12x12 size, but otherwise the code is the same. I'm using the newer copy of FastLED from #16.

When I run it with FastLED.setMaxPowerInVoltsAndMilliamps(5,2000), almost nothing seems to happen no matter what I do.

When I run it without the line, it appears to work. I'm getting very similar results with either pins 0/1 or 7/14. Maybe I'll see if I can shoot a very quick video clip....
 
The only thing I can see that's substantially different is your test has the signals going through a cable. We're both sending the 3.3V outputs from Teensy to the 5V APA102s. Maybe the signals are just barely able to drive such a cable, and the SPI output has slightly less drive capability?

Can you try doing your test again with relatively short wires, like I did in this video?


EDIT: as far as hardware differences, I can tell you the normal pinMode and digitalWrite configures the pin in high drive strength mode with slow slew rate limiting enabled. SPI on pin 14 uses fast slew rate and lower drive strength. Maybe the slew rate limited edges and stronger drive, and somewhat slower speed allow pins 0/1 to drive a longer cable than 7/14 can?
 
Last edited:
Hi Paul,

Thank you for the test ... I think I saw the problem in your video also, but it was not as pronounced as on mine, because you were talking and thus it seemed to overriding the fft ...

Could you run that test again, with little to no noise? I think we both agree that if there is little to no noise around the FFT should not bring back any values... The problem shows best when the surroundings are quiet.

Now as for the slew rate ... I am not sure. I have never had seen this behavior since I never had a teensy audio board before. The problem on my end also does not show when I am getting the audiosignal from an analogread rather than through the teensy audio shield
 
I did test in a fairly quiet room. When I wasn't talking or moving stuff around on the desk, all the LEDs were off. Hopefully that's visible in the video during the pauses when I wasn't speaking? This footage is right off my camcorder, without any editing, so the sound in the video is what the camcorder's mic heard (after Youtube's re-encoding). There wasn't any extra noise that's been edited out of the video. I could try to repeat this late at night when perhaps it's slightly quieter, but I don't have access to a studio quality room with extremely low noise level. This video is about as quiet as I can reasonably get the background noise.

I do not understand what more I should do to test. I also disconnected all this stuff and put it away, since I have very limited workbench space... and a lot of work to do on so many things. I'm willing to set it up again and try another test, if you're convinced it'll help, but I also need to clearly understand what I'm supposed to do. Right now, I don't.

Any chance you could try on your end first with short wires instead of a long cable?
 
Last edited:
I did test in a fairly quiet room. When I wasn't talking or moving stuff around on the desk, all the LEDs were off. Hopefully that's visible in the video during the pauses when I wasn't speaking? This footage is right off my camcorder, without any editing, so the sound in the video is what the camcorder's mic heard (after Youtube's re-encoding). There wasn't any extra noise that's been edited out of the video. I could try to repeat this late at night when perhaps it's slightly quieter, but I don't have access to a studio quality room with extremely low noise level. This video is about as quiet as I can reasonably get the background noise.

I do not understand what more I should do to test. I also disconnected all this stuff and put it away, since I have very limited workbench space... and a lot of work to do on so many things. I'm willing to set it up again and try another test, if you're convinced it'll help, but I also need to clearly understand what I'm supposed to do. Right now, I don't.

Any chance you could try on your end first with short wires instead of a long cable?
I'll try with shorter wires. Thank you for testing.
 
Any idea if the wire length/type made a difference? Might help to understand what really happened here, in case others run into similar issues.
 
Any idea if the wire length/type made a difference? Might help to understand what really happened here, in case others run into similar issues.

I apologize for not responding, thank you for pinging.

I shortened the wires to no more than 4 inches, but the effect is the same. If I use hardware spi on 7,14 I have a lot of noise in the higher bins of the FFT as shown in the beginning. If use software spi on 0,1 I don't have that noise.

Since I need more than 1 chain anyway and I have restrict the speed, it's not so much as big deal for me, but as you said it could be a problem for somebody else.
 
As a quick followup for anyone who finds this old thread (perhaps from the Youtube video), I'm pretty sure the problem with noise in the higher bins with fast SPI was due to this APA102 problem with fast clock speeds.

https://www.pjrc.com/why-apa102-leds-have-trouble-at-24-mhz/

Back in early 2016, nobody really understood this APA102 problem. Everybody believed APA102 had superior performance, partly due to comments Adafruit published about solving software limitations most boards have with WS2812, partly due to a blog article about their faster PWM rate for POV applications.

Now in 2018, the poor quality of APA102's signal regeneration is well known. Unlike WS2812 where the signal really is regenerated in a manner similar to digital copying, APA102 regenerates the signals similar to analog copying. After each LED, the signal quality gets slightly worse. Basically, it's a copy of a copy of a copy which loses timing margin. At higher clock speeds, those slight errors add up, causing corrupted data after 100 to 200 LEDs when using 24 MHz clock.

Since early 2016, Daniel changed the default APA102 clock speed to only 12 MHz, which solves this problem for most projects using only a few hundred LEDs or less.
 
Status
Not open for further replies.
Back
Top