Photon, FastLED and a rabbit hole of errors

Good evening all. Thanks to the help of this board’s users, I was able to successfully convert an arduino uno controlled project to a Photon project, at least in the hardware department. Now I struggle with the software side. I’m utilizing the FastLED library for my light display and what I had hoped to be a copy and paste kinduva night has turned into one failed compile after another. I’ve tried using other people’s samples and then tried adapting but failed there to. I’ve pared things down to what I consider my simplest display, with a focus on color palettes with the occasional twinkle on a WS2812b strip of LEDs:

    //This #include statement was automatically added by the Particle IDE.
    #include <FastLED.h>
    #include "Particle.h"

    FASTLED_USING_NAMESPACE;

    #define LED_PIN     4
    #define COLOR_ORDER GRB
    #define CHIPSET     WS2811
    #define NUM_LEDS    300 //88 for testing, 300 for prodction
    #define BRIGHTNESS  150
    #define FRAMES_PER_SECOND 30

    bool gReverseDirection = false;
    unsigned long previousMillis = 0;
    unsigned long currentMillis;
    unsigned long twinkleMillis = 0;
    int mode;
    long totalModes = 3;

    NSFastLED::CRGB leds[NUM_LEDS];
    CRGBPalette16 gPal;

    void setup() {
      delay(3000); // sanity delay
      FastLED.addLeds<CHIPSET, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
      FastLED.setBrightness( BRIGHTNESS );
      //FastLED.setMaxPowerInVoltsAndMilliamps(5, 1500);
      randomSeed(analogRead(0));
      //Serial.begin(9600);
      mode = random(totalModes);
    }


    extern const TProgmemRGBGradientPalettePtr gGradientPalettes[];

    void loop()
    {
      currentMillis = millis();

      switch (mode) {

        case 0:
          runRainbow();
          twinkle();
          break;

        case 1:
          runPalette(leds, NUM_LEDS,  gGradientPalettes[0]);
          twinkle();
          break;

        case 2:
          runPalette(leds, NUM_LEDS,  gGradientPalettes[1]);
          twinkle();
          break;
      }
    }


    void runColor(NSFastLED::CRGB color) {
      if (currentMillis - previousMillis >= 200) {
        previousMillis = currentMillis;
        fill_solid(leds, NUM_LEDS, color); //
        FastLED.show();
      }
    }

    void runRainbow() {
      if (currentMillis - previousMillis >= 30) {
        previousMillis = currentMillis;
        static uint8_t hue = 0;
        fill_rainbow(leds, NUM_LEDS, hue++);
        FastLED.show();
      }
    }

    void twinkle() {
      //create random twinkle
      int rp = random(500,2000);
      if (currentMillis - twinkleMillis >= rp) {
        twinkleMillis = currentMillis;
        int pixel = random(NUM_LEDS);
        leds[random(NUM_LEDS)] = CRGB::White;
        FastLED.show();
      }
    }


    void runPalette(NSFastLED::CRGB* ledarray, uint16_t numleds, const CRGBPalette16& gCurrentPalette)
    {
      if (currentMillis - previousMillis >= 30) {
        previousMillis = currentMillis;
        static uint8_t startindex = 0;
        startindex--;
        fill_palette( ledarray, numleds, startindex, (256 / NUM_LEDS) + 1, gCurrentPalette, 255, LINEARBLEND);
        FastLED.show();
      }
    }

    DEFINE_GRADIENT_PALETTE( holly_gp) {
      0,    0,  255,  0,  //green
      48,   0,  255,  0,  //green
      49,   255,  0,  0,  //red
      64,   255,  0,  0,  //red
      65,   0,  255,  0,  //green
      114,   0,  255,  0,  //green
      115,   255,  0,  0,  //red
      118,   255,  0,  0,  //red
      119,   0,  255,  0,  //green
      168,  0,  255,  0,  //green
      169,  255,  0,  0,  //red
      184,  255,  0,  0,  //red
      185,  0,  255,  0,  //green
      234,  0,  255,  0,  //green
      235,  255,  0,  0,  //red
      255,  255,  0,  0   //red
    };

    DEFINE_GRADIENT_PALETTE( candycane_gp) {
      0 , 128, 128, 128,  //white
      32 , 128, 128, 128,  //white
      33 , 255, 0, 0,  //red
      66 , 255, 0, 0,  //red
      67 , 128, 128, 128,  //white
      100 , 128, 128, 128,  //white
      101 , 255, 0, 0,  //red
      134 , 255, 0, 0,  //red
      135 , 128, 128, 128,  //white
      168 , 128, 128, 128,  //white
      169 , 255, 0, 0,  //red
      202 , 255, 0, 0,  //red
      203 , 128, 128, 128,  //white
      236 , 128, 128, 128,  //white
      237 , 255, 0, 0,  //red
      255 , 255, 0, 0  //red
    };

    DEFINE_GRADIENT_PALETTE( snowynight_gp) {
      0, 163, 182, 199,
      41,  188, 192, 200,
      117,  117, 157, 240,
      204,  117, 224, 240,
      255,  163, 182, 199
    };

    DEFINE_GRADIENT_PALETTE( silvergold_gp) {
      46, 191, 201, 224,
      127, 255, 236, 133,
      204, 191, 201, 224
    };

    const TProgmemRGBGradientPalettePtr gGradientPalettes[] = {
      holly_gp,
      candycane_gp,
      snowynight_gp,
      silvergold_gp
    };

This particular sketch gives me one error: ‘runPalette’ was not declared in this scope. The line it references occurs in the loop(); If I comment out the runPalette calls, it compiles fine.

I suspect this boils down to a lack of understanding in the syntax department from the arduino to the Photon. Other quicky tests I’ve run break whenever a function with dynamic variables are used.

So, am I just writing this wrong? Please help.

Since there several possible reasons for such an error posting the exact error message with its context may provide valuable hints.

One thing I'd assume from your description is that your project is not based on an .ino file but rather a .cpp and hence you'd need to provide a function prototype

    void runPalette(NSFastLED::CRGB* ledarray, uint16_t numleds, const CRGBPalette16& gCurrentPalette);

before you first use the function - the same goes for all other functions that are implemented after their first use.

Another possible reason might be a mismatch between function signature and the parameter list of your call - hence the full context of the error message would be helpful.

runPalette(leds, NUM_LEDS,  gGradientPalettes[0]);

What do you consider "dynamic variables"?

Here’s everything in the RAW error log after all the build text:

#warning FastLED version 3001000  (Not really a warning, just telling you here.)
  ^
christmaslights.ino: In function 'void setup()':
christmaslights.ino:28:38: error: 'runPalette' was not declared in this scope
christmaslights.ino: In function 'void loop()':
christmaslights.ino:50:55: error: 'runPalette' was not declared in this scope
christmaslights.ino: In function 'void twinkle()':
christmaslights.ino:82:40: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
christmaslights.ino:84:9: warning: unused variable 'pixel' [-Wunused-variable]
../build/module.mk:267: recipe for target '../build/target/user/platform-6-msrc/christmaslights.o' failed

I guess I don’t really mean “dynamic variables”, but more like any function that uses variables in parentheses. If I try to invoke the runColor function specifying a color from switch case inside the loop, I get the same failure, only swapping out the function name.

And as far as file type, it’s an INO. It is in my original Arduino sketch and, according to the Particle IDE’s tab, it’s here too.

These variables are called parameters.

As I assumed and hence my hint for this case does apply

If "prototype" doesn't really mean anything to you yet, here we go

// function prototypes
void runColor(NSFastLED::CRGB color);
void runRainbow();
void twinkle();
void runPalette(NSFastLED::CRGB* ledarray, uint16_t numleds, const CRGBPalette16& gCurrentPalette);

void setup() {
  ...
}
2 Likes

+1

I've noticed that from time to time things go pear-shaped particularly when trying to use a struct as a parameter to a function (vs. primitives). I can't say what causes it but I've resolved to just declaring every function as @ScruffR showed when using Particle DEV or the CLI to compile.

1 Like

Parameters, yes, of course. I feel like I should know this stuff.

So, if I understand correctly, whenever I want to create a function outside the loop (to be called inside of it), I need to create a prototype first by calling it out ahead of time and THEN writing the function to do what it needs to do? That seems really redundant. That’s like pulling into a gas station and yelling “I’m pumping gas” before pumping it. If dems da rules though, I guess I can’t argue cuz the sketch is working like a charm.

Thanks a heap!

Nope.
As I said, if you intend to call a function before its implementation, you need to tell the compiler ahead of time that such a function will be implemented later but it will look like this prototype.
This is not redundant but a C imperative. In order to check if your syntax is correct, the compiler needs to be told what an acceptable function call (valid function signature) would look like.
Your illustration with the petrol station does not apply here, since you are missing out the compiler in the scene.
Imagine you drive with someone who always keeps an eye on where you drive - and doesn't hesitate to tell you when you are "going wrong" -, imagine what happens if you just pull over into a petrol station and telling, you want to buy a new laptop :wink:

The only reason why you don't need to do this on Arduino (and for that reason when using an .ino instead of .cpp as project file) is that a dedicated Wiring preprocessor will add these function prototypes for you, just in case you forgot to adhere to the C rules.

A "workaround" is to actually implement the whole function before its first call - that way the compiler already knows the whole story before it'll hit an instruction it otherwise would not know what to do with.

I see. So if I would have actually just written out the function above the loop, I could have avoided all this or would it have still required the prototype call?

You’ll have to forgive me. My programming background is javascript and PHP.

Exactly