Teensy 4.0 with MAX98357A, Stumped on what could be wrong

Jinxvar

Member
Hi, I've made a simple setup with a Teensy 4.0, a MAX98357A board and a small speaker.
I've tried numerous things to get it to work, but even the simple ToneSweep does not seem to work. Searched the
forums, but either I missed what could be wrong, or a component is not working.

Tried 2 different speakers. They both give a short sound when powered, but no output after that. They both work
on a Teensy 3.2 with propshield output.
Anyone any ideas?

Code:
/* */

#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>

// GUItool: begin automatically generated code
AudioOutputI2S           i2s1;           //xy=1209,399
// GUItool: end automatically generated code

AudioSynthToneSweep myEffect;
// The tone sweep goes to left and right channels
AudioConnection c1(myEffect, 0, i2s1, 0);
AudioConnection c2(myEffect, 0, i2s1, 1);

float t_ampx = 0.8;
int t_lox = 10;
int t_hix = 22000;
// Length of time for the sweep in seconds
float t_timex = 10;

#define FRAMES_PER_SECOND 70

void setup() {
  delay(500); // sanity delay

  Serial.begin(6400);
  // wait up to 2 seconds for the Serial device to become available
  long unsigned debug_start = millis ();
  while (!Serial && ((millis () - debug_start) <= 3000))
    ;

  // Audio connections require memory to work.  For more
  // detailed information, see the MemoryAndCpuUsage example
  AudioMemory(2);

  if(!myEffect.play(t_ampx,t_lox,t_hix,t_timex)) {
    Serial.println("AudioSynthToneSweep - begin failed");
    while(1);
  }
  // wait for the sweep to end
  while(myEffect.isPlaying());

  // and now reverse the sweep
  if(!myEffect.play(t_ampx,t_hix,t_lox,t_timex)) {
    Serial.println("AudioSynthToneSweep - begin failed");
    while(1);
  }
  // wait for the sweep to end
  while(myEffect.isPlaying());
  Serial.println("Done");

}

void loop () {

}
 

Attachments

  • IMG_7978.jpg
    IMG_7978.jpg
    310.7 KB · Views: 15
  • IMG_7979.jpg
    IMG_7979.jpg
    295.5 KB · Views: 15
  • IMG_7977 2.jpg
    IMG_7977 2.jpg
    292.8 KB · Views: 14
Your program uses AudioOutputI2S. On Teensy 4.x, AudioOutputI2S transmits data on pin 7.

Like all audio library features, it's documented in the design tool's right side panel. Scroll down to "Hardware" for the details.


Other audio outputs do use pins 9 and 6. For example, AudioOutputI2SOct transmits 8 audio channels using 4 data pins.


As you can see in the docs, pin 9 transmits channel 5 & 6, and pin 6 transmits channels 7 & 8.
 
Hi Paul, thank you a lot! I've learned something new.
Reason why I am trying this is because I want to combine it with a propshield. I've read quite some posts, but I am still not sure
how I could combine these as pin 7 is (also) used for addressing the LED strip.
 
Maybe use AudioOutputI2S2, which transmits on pins 2, 3, 4, 33.

Hi Paul, that's also an option. However, I noticed that the Propshield uses pin2 IRQ for motion sensors (which I also use).
Is my conclusion correct that I can not make this setup work in this combination, being:
- Teensy 4.0
- Propshield (LED, motion, flashmemory)
- Max98357A for sound output through I2S (1 or 2) from Teensy 4.0
because it conflicts with either LED (pin 7) or Motion (pin 2).
Or am I missing a potential solution here?
 
Teensy 4.1 uses the same pins as Teensy 4.0.

You might need to remove 1 or more of the pins connecting between Teensy and the Prop Shield, so that Teensy pin can be connected to your I2S device. Maybe inconvenient, but far from impossible.
 
Removing pins wouldn't be an issue, but I thought that the combination would always give conflicting pins:
- Max98357A for sound output through I2S(1 or 2) from Teensy 4.0 always conflicts with either LED (pin 7) or Motion (pin 2) to communicatie with the propshield right?

How would I solve this conflict?
 
How would I solve this conflict?

One way would connect to I2S to pins 2, 3, 4 on Teensy, and remove the connection between pin 2 on Teensy and the Prop Shield.

Alternately, you could connect to the normal I2S pins and resolve the pin 7 conflict by not connecting between Teensy and Prop Shield for pin 7, and then solder a wire from Prop Shield pin 7 to some other unused pin on Teensy.
 
Thx again Paul, what a great help and support.

Ahh, so pin 2 is not needed for the motion detection from propshield? That would definitely work.
And if I would go the route of pin 7, would that mean that I have to redefine the LED communication to another pin on Teensy right?
 
Ahh, it's just a matter of defining a different pin in the script I guess. Will try that one, thx Paul!
Will report back if it has succeeded. Thank you for your patience and help, much appreciated. :)
 
Ok, got it kinda working on pin 8. As in, sound plays when I try combining sounds in the setup() section.
However, when I combine it in the loop() section, where everything comes together, I do not hear any sound.
This setup used to work with a Teensy 3.2 and Propshield sound out. So it must have something to do with the I2S output.

What I am trying to do is to keep the sound going when the led strip is on and off. But the moment the led is on, no sounds.
Does below code work for this?

Note: when I println to check if the sound is playing, it does say it is playing, but the position is always 0

Code:
/*

*/

#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SerialFlash.h>
#include <FastLED.h>
#include <NXPMotionSense.h>

// GUItool: begin automatically generated code
AudioPlaySerialflashRaw  playHUMraw;     //xy=674,347
AudioPlaySerialflashRaw  playSwingRaw;   //xy=677,431
AudioPlaySerialflashRaw  playFlashRaw1;  //xy=679,388
AudioMixer4              mixer1;         //xy=979,391
AudioOutputI2S           i2s1;           //xy=1209,399
AudioConnection          patchCord1(playHUMraw, 0, mixer1, 0);
AudioConnection          patchCord2(playSwingRaw, 0, mixer1, 2);
AudioConnection          patchCord3(playFlashRaw1, 0, mixer1, 1);
AudioConnection          patchCord4(mixer1, 0, i2s1, 0);
AudioConnection          patchCord5(mixer1, 0, i2s1, 1);
// GUItool: end automatically generated code

NXPMotionSense imu;
NXPSensorFusion filter;

int motionThreshold = 2;

float lastPitch = 0,  lastHeading = 0; // lastRoll = 0,

int buttonState = HIGH;
int bladeState = 0; //0 off, 1, fading up, 2 on, 3 fading down, 4 color change
int bladeUpDownDelay = 0; // 0 no delay, 1 wait for sound to load, before led shows, or more in case of blade down
bool flashRawPlaying = 0;
int fadeStep = 0;
int fadeStepSize = 2;
int selectedColor = 0;
int lastSelectedColor = 0;
bool isAnimating = 0;
bool bladeOn = 0;
uint32_t lastDebounceTime = 0;  // the last time the output pin was toggled
uint32_t debounceDelay = 50;
int lastButtonState = HIGH;
int pendingPress = 0;
int brightStep = 0; // used to dim brightness when color changes
int brightStepSize = 20;
bool isDimming = 0;
int brightGlitch = 0; // used to make the lightsaber glitch when switching colors

//BC = Blade Color
int BCbuttonState = HIGH;
uint32_t BClastDebounceTime = 0;  // the last time the output pin was toggled
uint32_t BCdebounceDelay = 50;
int BClastButtonState = HIGH;
int BCpendingPress = 0;
//int pendingColorPress = 0;

int BCbrightness = 180;

#define COLOR_BUTTON_PIN 1
#define POWER_BUTTON_PIN 0
#define COLOR_ORDER BGR
#define CHIPSET     APA102
#define NUM_LEDS   120 // actual amount
#define DATA_PIN 11
#define CLOCK_PIN 13
#define LED_ACCESS_PIN 8 //  8 ! because I2S1 output on 7, 20 and 21
#define FLASH_CHIP_SELECT 6

#define BRIGHTNESS  100
#define FRAMES_PER_SECOND 70

int outputValue = 0;

int presetColors[] = {CRGB::Blue, CRGB::Green};
int totalPresetColors = (sizeof(presetColors) / sizeof(int));
int selectedColorIndex = 0;

int swingSounds = 4;
int lastSwingSound = swingSounds;
int clashSounds = 0;

CRGB leds[NUM_LEDS];

void setup() {
  delay(500); // sanity delay

  FastLED.addLeds<APA102, DATA_PIN, CLOCK_PIN, BGR>(leds, NUM_LEDS);
  selectedColor = presetColors[selectedColorIndex];
  lastSelectedColor = selectedColor;

  // initiate motion sensor
  imu.begin();
  filter.begin(100);

  pinMode(POWER_BUTTON_PIN, INPUT_PULLUP);
  pinMode(COLOR_BUTTON_PIN, INPUT_PULLUP);
  pinMode(LED_ACCESS_PIN, OUTPUT);
  digitalWrite(LED_ACCESS_PIN, LOW);

  Serial.begin(6400);
  // wait up to 2 seconds for the Serial device to become available
  long unsigned debug_start = millis ();
  while (!Serial && ((millis () - debug_start) <= 3000))
    ;

  pinMode(LED_ACCESS_PIN, OUTPUT);
  digitalWrite(LED_ACCESS_PIN, HIGH);

  // Audio connections require memory to work.
  AudioMemory(12);

  // Set initial volume
  mixer1.gain(1, 1.5f); //other sounds
  mixer1.gain(0, 1.5f); //hum

  // Start SerialFlash
  if (!SerialFlash.begin(FLASH_CHIP_SELECT)) {
    while (1)
    {
      Serial.println ("Cannot access SPI Flash chip");
      delay (3000);
    }
  }

  // test sounds >> ALL WORK in setup
  playHUMraw.play("HUM.RAW");
  playFlashRaw1.play("OPEN.RAW");
  playSwingRaw.play("SWING.RAW");
  triggerSound("SPARK.RAW");

  // clear led strip
  FastLED.clear(true);
  delay(3000);
  powerUpBlade();

}

void powerUpBlade() {
  // animate UP
  isAnimating = 1;
  bladeState = 1;
  Serial.println("Turn on blade");
  bladeOn = 1;
  bladeUpDownDelay = 1;
  triggerSound("OPEN.RAW"); // does not work!
}

void powerDownBlade() {
  //animate DOWN
  fadeStep = NUM_LEDS;
  isAnimating = 1;
  bladeState = 3;
  Serial.println("Turn off blade");
  bladeOn = 0;
  // extra delay because close sound starts later
  bladeUpDownDelay = 40;
  triggerSound("CLOSE.RAW");
  stopHum();
}

void bladeIsOn() {
  // play hum when ON
  if (!playHUMraw.isPlaying()) {
    Serial.println("Hum reload");
    startHum();
  }
  //nothing needed here unless adding animation
}

void bladeIsAnimatingUp() {
  // part of loop animation UP
  Serial.println("bladeIsAnimatingUp");
  int midpoint = NUM_LEDS / 2;
 
  int newSection = fadeStep + fadeStepSize;
  for ( int j = fadeStep; j < newSection; j++) {
    leds[j] = selectedColor;
    leds[NUM_LEDS - j] = selectedColor;
  }
 
  fadeStep = newSection;
  if (fadeStep >= midpoint + fadeStepSize) {
    fadeStep = NUM_LEDS;
    isAnimating = 0;
    bladeState = 2;
    Serial.println("blade up complete");
    startHum();
  }
}

void bladeIsAnimatingDown() {
  // part of loop animation DOWN
  //Serial.println("bladeIsAnimatingDown");
  int midpoint = NUM_LEDS / 2;
  int newSection = fadeStep - fadeStepSize;
  for ( int j = fadeStep; j > newSection; j--) {
    leds[j - midpoint] = CRGB::Black;
    leds[midpoint + NUM_LEDS - j] = CRGB::Black;
  }
 
  fadeStep = newSection;
  if (fadeStep <= midpoint - fadeStepSize) {
    fadeStep = 0;
    isAnimating = 0;
    bladeState = 0;
  }
}


void bladeIsSwitchingColor() {
  // part of loop animation UP
  Serial.println("bladeIsSwitchingColor");

  if (isDimming) {
      // dimming
      if (brightStep > 0){
        brightStep = brightStep - brightStepSize - brightStepSize;
        if (brightStep < 0){
          brightStep = 0;
        }
      }else{
        brightStep = 0;
        // when reaching off, change color
        for ( int j = 0; j < NUM_LEDS; j++) {
          leds[j] = selectedColor;
        }
        // and set dimming to off to turn on brightness again
        isDimming = 0;
        brightGlitch = 13;
      }
  }else{
    if (brightGlitch > 0){
      brightGlitch = brightGlitch - 1;
      brightStep = random(20, 80) * (brightGlitch % 3);
      //Serial.println(brightStep);
    }else{
     
      // not dimming
      if (brightStep < BCbrightness){
        brightStep = brightStep + brightStepSize;
      }else{
        brightStep = BCbrightness; // back to standard brightness
        brightGlitch = 0;
        // done with color change
        bladeState = 2;
      }
    }
  } // end if else

  //strip.setBrightness(brightStep);   // Set global brightness 0-255
}


void detectMotion() {
  float ax, ay, az;
  float gx, gy, gz;
  float mx, my, mz;
  float pitch, heading; //roll,

  if (imu.available()) {
    // Read the motion sensors
    imu.readMotionSensor(ax, ay, az, gx, gy, gz, mx, my, mz);

    // Update the SensorFusion filter
    filter.update(gx, gy, gz, ax, ay, az, mx, my, mz);

    // print the heading, pitch and roll
    // roll = filter.getRoll();
    pitch = filter.getPitch();
    heading = filter.getYaw();

    float headingDiff = abs(lastHeading - heading);
    float pitchDiff = abs(lastPitch - pitch);
    if (lastHeading != 0) {
      if (pitchDiff > motionThreshold || headingDiff > motionThreshold) {
        //cyle through swing sounds
        lastSwingSound++;
        if (lastSwingSound > swingSounds) {
          lastSwingSound = 1;
        }
        String swingFile = "SWING.RAW";
        swingFile.replace("X", lastSwingSound);
        char charBuf[50];
        swingFile.toCharArray(charBuf, 50);
        triggerSwing(charBuf);  // needs sequence to iterate through
      }
    }
    lastHeading = heading;
    lastPitch = pitch;
  }

}

void loop() {
  // Add entropy to random number generator; we use a lot of it.
  // random16_add_entropy( random() );

  //handle color selector button
  int BCreading = digitalRead(COLOR_BUTTON_PIN);

  if (BCreading != BClastButtonState) {
    // reset the debouncing timer
    BClastDebounceTime = millis();
  }

  if ((millis() - BClastDebounceTime) > BCdebounceDelay) {
    if (BCreading != BCbuttonState) {
      BCbuttonState = BCreading;

      if (BCbuttonState == HIGH && bladeState != 4) {
        // only act when button is pressed and color change not active
        nextColor();
      }
    }
  }
  BClastButtonState = BCreading;

  selectedColor = presetColors[selectedColorIndex];
  if (selectedColor != lastSelectedColor && bladeState != 0) {
    Serial.println("COLOR CHANGE");
    //Serial.println(selectedColorIndex);
    //for ( int j = 0; j < NUMPIXELS; j++) {
    //  strip.setPixelColor(j, selectedColor);
    //}
    // color change
    isDimming = 1; // start dimming
    bladeState = 4;
  }
  lastSelectedColor = selectedColor;

  //handle blade on/off

  int reading = digitalRead(POWER_BUTTON_PIN);
  //debounce
  if (reading != lastButtonState) {
    // reset the debouncing timer
    lastDebounceTime = millis();
  }

  if ((millis() - lastDebounceTime) > debounceDelay) {
    if (reading != buttonState) {
      buttonState = reading;
    }
  }
  lastButtonState = reading;

  if ( pendingPress == 1 && buttonState != LOW) {
    buttonState = HIGH;
    pendingPress = 0;
    //Serial.print("execute pending blade press");

    if (bladeOn) {
      powerDownBlade();
    } else {
      powerUpBlade();
    }
  }

  if (buttonState == LOW && !isAnimating) {
    //Serial.println("BLADE BUTTON IS PRESSED");
    pendingPress = 1;
  }
  //Serial.println("before bladeState: ");
  switch (bladeState) {
    case 0:
      //blade is off
      break;
    case 1:
      //blade is animating up
      //check if sound is playing, only then can we animate
      if (bladeUpDownDelay == 1 && playFlashRaw1.isPlaying() == 1) {
        bladeUpDownDelay = 0;
      }

      if (bladeUpDownDelay == 0) {
        bladeIsAnimatingUp();
      }
      break;
    case 2:
      //blade is on
      bladeIsOn();
      break;
    case 3:
      //blade is animating down
      //check if sound is playing, only then can we animate
      flashRawPlaying = playFlashRaw1.isPlaying();

      if (bladeUpDownDelay > 1 && flashRawPlaying == 1) {
        bladeUpDownDelay = bladeUpDownDelay - 1;
      } else if (bladeUpDownDelay == 1 && flashRawPlaying == 1) {
        bladeUpDownDelay = 0;
      }

      if (bladeUpDownDelay == 0) {
        bladeIsAnimatingDown();
      }
      break;
    case 4:
      //blade is switching color
      bladeIsSwitchingColor();
      break;
  }
 
  SPI.beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0));
  digitalWrite(LED_ACCESS_PIN, HIGH);    // enable access to LEDs
  // Refresh strip
  FastLED.show();
  digitalWrite(LED_ACCESS_PIN, LOW);
  SPI.endTransaction();                // allow other libs to use SPI again

  delay(10);

  if (bladeState == 2) {
    detectMotion();
  }

}

void nextColor() {
  selectedColorIndex++;
  if (selectedColorIndex >= totalPresetColors) {
    selectedColorIndex = 0;
  }
  triggerSound("SPARK.RAW");
}
void triggerSound(const char *filename) {
  playFlashRaw1.play(filename);
}
void triggerSwing(const char *filename) {
  if (playSwingRaw.isPlaying() == 0) {
    playSwingRaw.play(filename);
    Serial.println("Start swing");
  } else {
    Serial.print("  pos: ");
    Serial.println(playSwingRaw.positionMillis());
  }
}
void startHum() {
  Serial.println("startHum");
  playHUMraw.play("HUM.RAW");
  Serial.println(playHUMraw.isPlaying());
}
void stopHum() {
  playHUMraw.stop();
}
 
Ok, did a few additional tests. If I run the loop with just playing the Hum sound over and over it works. So there must be some conflict with the other items being Motion or LED. Could it be because of the SPI perhaps?
 
Solved it eventually by skipping the use of HW SPI for the LED strips. Hooked them up to regular pins, used the DotStar library, declared the data and clock pin and all worked again.
Still find it strange it didn't work properly.

Note: also tried with soldering the LED strip to pin 26/27. It flickered just a few leds but nothing more. Also tried with slower MHz, didnt work.
If anyone still has a clue towards the future I might try it. For now it works.

Thx all!
 
Back
Top