0

I'm trying to make an OO state machine based program to play music and lights at the same time. It uses the FastLED library and a 13 pixel neopixel strip. I've confirmed that with the currently wired setup that I can separately play music and also separately run the FastLED Cylon example code. However when I upload the code below it just kinda hangs out and doesn't do anything and doesn't even print the statements in the setup statement. The weird thing is that if I comment out the Lights lightstrip1() instantiation line and the lightstrip.Update() line, it does play the melody part ok.

#include "pitches.h"
#include "FastLED.h"

class Melody{

// notes in the melody Minuet in G.
int melody[126] = {
  NOTE_D4, NOTE_G3, NOTE_A3, NOTE_B3, NOTE_C4, 
  NOTE_D4, NOTE_G3, NOTE_G3, 
  NOTE_E4, NOTE_C4, NOTE_D4, NOTE_E4, NOTE_FS4,
  NOTE_G4, NOTE_G3, NOTE_G3,

  NOTE_C4, NOTE_D4, NOTE_C4, NOTE_B3, NOTE_A3,
  NOTE_B3, NOTE_C4, NOTE_B3, NOTE_A3, NOTE_G3,
  NOTE_FS3,NOTE_G3, NOTE_A3, NOTE_B3, NOTE_G3,
  NOTE_A3,

  NOTE_D4, NOTE_G3, NOTE_A3, NOTE_B3, NOTE_C4, 
  NOTE_D4, NOTE_G3, NOTE_G3, 
  NOTE_E4, NOTE_C4, NOTE_D4, NOTE_E4, NOTE_FS4,
  NOTE_G4, NOTE_G3, NOTE_G3,

  NOTE_C4, NOTE_D4, NOTE_C4, NOTE_B3, NOTE_A3,
  NOTE_B3, NOTE_C4, NOTE_B3, NOTE_A3, NOTE_G3,
  NOTE_A3, NOTE_B3, NOTE_A3, NOTE_G3, NOTE_FS3,
  NOTE_G3,

 // Second part
  NOTE_B4, NOTE_G4, NOTE_A4, NOTE_B4, NOTE_G4,
  NOTE_A4, NOTE_D4, NOTE_E4, NOTE_FS4, NOTE_D4,
  NOTE_G4, NOTE_E4, NOTE_FS4, NOTE_G4, NOTE_D4,
  NOTE_CS4, NOTE_B3, NOTE_CS4, NOTE_A3,
  NOTE_A3, NOTE_B3, NOTE_CS4, NOTE_D4, NOTE_E4, NOTE_FS4,
  NOTE_G4, NOTE_FS4, NOTE_E4,
  NOTE_FS4, NOTE_A3, NOTE_CS4,
  NOTE_D4,

  // BACK TO G MAJOR!!!!
  NOTE_D4, NOTE_G3, NOTE_FS3, NOTE_G3,
  NOTE_E4, NOTE_G3, NOTE_FS3, NOTE_G3,
  NOTE_D4, NOTE_C4, NOTE_B3,
  NOTE_A3, NOTE_G3, NOTE_FS3, NOTE_G3, NOTE_A3,
  NOTE_D3, NOTE_E3, NOTE_FS3, NOTE_G3, NOTE_A3, NOTE_B3,
  NOTE_C4, NOTE_B3, NOTE_A3,
  NOTE_B3, NOTE_D4, NOTE_G3, NOTE_FS3,
  NOTE_G3



  };

// note durations: 4 = quarter note, 8 = eighth note, etc.:
float noteDurations[126] = {
  4, 8,8,8,8,
  4, 4, 4,
  4, 8,8,8,8,
  4, 4, 4,
  4, 8,8,8,8,
  4, 8,8,8,8,
  4, 8,8,8,8,
  1.5,

  4, 8,8,8,8,
  4, 4, 4,
  4, 8,8,8,8,
  4, 4, 4,
  4, 8,8,8,8,
  4, 8,8,8,8,
  4, 8,8,8,8,
  1.5,

  // Second part

  4, 8,8,8,8,
  4, 8,8,8,8,
  4, 8,8,8,8,
  4, 8,8, 4,
  8,8,8,8,8,8,
  4, 4, 4,
  4, 4, 4,
  1.5,
  4, 8,8, 4,
  4, 8,8, 4,
  4, 4, 4,
  8,8,8,8, 4,
  8,8,8,8,8,8,
  4,4,4,
  8,8,4,4,
  1.5
  };


  unsigned long OnTime;  // This will be the time in millis to turn on a note.
  unsigned long OffTime; // This will be the time in millis to turn off a note.

  int ToneState;
  unsigned long previousMillis;

  int speakerPin;
  int noteNumber;
  int maxNoteNumber;

  public:
  Melody(int pin) {
     speakerPin = pin;
     pinMode(speakerPin, OUTPUT);
     previousMillis = 0;  
     OnTime = previousMillis;
     ToneState = 0; // Off
     noteNumber = 0; // Start at the first note.
//     maxNoteNumber = sizeof(noteDurations); // This turns out to 504 for some reason.
     maxNoteNumber = 125;

  }


  void Update() {
     unsigned long currentMillis = millis();

     if (ToneState == 0 && currentMillis >= OnTime) {
       tone(speakerPin, melody[noteNumber]);
       OffTime = currentMillis + (1000 / noteDurations[noteNumber]);
       ToneState = 1;
       Serial.print(noteNumber);
       Serial.print("/");
       Serial.println(maxNoteNumber);

     } else if (ToneState == 1 && currentMillis >= OffTime) {
       noTone(speakerPin);
       OnTime = currentMillis + ((1000 / noteDurations[noteNumber]) * 0.30);
       ToneState = 0;
       if (noteNumber < maxNoteNumber) {
         noteNumber += 1;
       } else {
         Serial.println("RESET");
         noteNumber = 0; // Reset to the begining of the song.
       }
     } 
  }
};

class Lights {
    int ledPin;
        int ledCount;
    unsigned long NextOn;
        int led_R;
        int led_G;
        int led_B;

        CRGB leds[12];

    int ledOn;
        int lightdelay;

    public:
    Lights(int pin, int ledcount, int led_r, int led_g, int led_b) {
      ledPin = pin;
      pinMode(ledPin, OUTPUT);
      digitalWrite(ledPin, LOW);
      ledCount = ledcount;
      led_R = led_r;
      led_G = led_g;
      led_B = led_b;
      ledOn = 0;
      lightdelay = 100;
      NextOn = 0;
      FastLED.addLeds<NEOPIXEL, 7>(leds, ledCount); // It complains if I try to use a variable for the 7. It has to be a constant.
      for (int i = 0; i < ledCount; i++) {
        leds[i] = CRGB::White;
      }
      FastLED.show();
    }

    void Update() {
      unsigned long currentMillis2 = millis();

      if (currentMillis2 > NextOn) {
         if (ledOn >= ledCount) {
           leds[ledOn] = CRGB::Black;
           ledOn = 0;
           leds[ledOn] = CRGB::Red;
         } else if (ledOn == 0) {
           leds[ledCount - 1] = CRGB::Black;
           leds[ledOn] = CRGB::Red; // Need to put RGB to HSV converter in later.
         } else {
           leds[ledOn - 1] = CRGB::Black;
           leds[ledOn] = CRGB::Red; // Need to put RGB to HSV converter in later.
         }
         Serial.print("LED: ");
         Serial.println(ledOn);
         Serial.println(NextOn);
         FastLED.show();
         NextOn = currentMillis2 + lightdelay;
         ledOn++;
      } 
    }

};


Melody melody1(2);

Lights lightstrip1(7, 12, 250, 0, 0);

void setup() {
  Serial.begin(115200);
  Serial.println("Started");
}

void loop() {
   melody1.Update();
   lightstrip1.Update();
}

Any ideas?

7
  • Are you sure the program is uploading properly and you haven't run out of memory or something daft like that? (Of course that wouldn't explain why it runs when you comment things out, unless the compiler is optimising things away). Jun 7 '16 at 9:57
  • Well, I'm not 100% sure, but its not erroring out and it says the program is only using about 5K (out of 32K) of memory. This is on an Arduino Uno by the way.
    – deltaray
    Jun 7 '16 at 10:14
  • 2
    You could try your luck with Adafruits' NeoPixel library. I also highly advise to use const unsigned char as the type of noteDurations (and simply multiply by 2. This will save you quite some room and avoids floating point math. And depending on the type of a single note int melody[126] could better be const unsigned char or const unsigned int.
    – Paul
    Jun 7 '16 at 15:50
  • 1
    What type of Arduino do you have? 32K SRAM is pretty nice? You're probably confusing SRAM and PROGRAM/Flash memory. An Arduino Uno only has 2kbyte of SRAM (so you can save only 2.000 bytes of variables).
    – Paul
    Jun 7 '16 at 15:54
  • 1
    sizeof(noteDurations); // This turns out to 504 for some reason. Yes. sizeof gives you the length, in bytes. So only the note durations already use 0,5KB of your precious 2KB RAM. An float is 4 bytes.
    – Paul
    Jun 7 '16 at 16:04
1

The WS2812 device requires precise timing down to +/- 150 x 10-9 seconds. For details look at this chip specification. As such, it is likely the FastLED library uses processor resources that can not be shared. Look through the FastLED library's code to verify this.

It appears your sketch plays a note, changes the LED pattern then repeats this cycle. Assuming the resources for FastLED are initialized in the constructor of the library, you might consider destroying and re-instantiating the FastLED class for each of these cycles.

added later...

Comments about memory space and large amounts of static data in the sketch have merit. Consider moving your static (unchanging) data from limited RAM to FLASH memory. This resource explains how this is accomplished.

3
  • Hmmm, understand what you are saying but I would think if this were the problem that commenting out the melody1.Update line would still allow the lights to update, but it doesnt. It's almost like the object instantiation of the lights object is crashing the Arduino. I say this because it doesn't print the lines on serial in the setup statement if I have the lights enabled.
    – deltaray
    Jun 7 '16 at 15:18
  • I think to understand what is happening and how to fix it, you would have to analyze the FastLED library, the built in millis() function and the Atmel chip specification. Although the comments about limited RAM space has merit. You said you can control the LEDs if you remove all other code. But what happens when you put back (only) the large arrays? Will the LEDs still work?
    – st2000
    Jun 8 '16 at 2:16
  • You have a good idea. When I have time, I think I will build a version that doesn't have the melody part in it at all to see if the FastLED library can handle being updated from an object. I'm beginning to think this is the problem.
    – deltaray
    Jun 8 '16 at 11:58
0

I asked on the FastLED library github page and they provided me with the correct instantiation for the object:

Lights *lightstrip1;

void setup() {
  lightstrip1 = new Lights(7,13, 250, 0, 0);
}

void loop() {
  lightstrip1->Update();
}

It now works fine and plays the music and the lights simultaneously without issue.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.