Problem with creating global variables in IO

I’m creating app to control addresable led strip. Every effect is different class. When i try create GRADIENT_PALLETE i get error:

.pio\build\esp32dev\src\main.cpp.o:(.rodata.nether1+0x0): multiple definition of `nether1'
.pio\build\esp32dev\src\GradientMoving.cpp.o:(.rodata.nether1+0x0): first defined here

nether1 is defined only in file with class. This file is included only in main.

#ifndef GRADIENTMOVING_H
#define GRADIENTMOVING_H

#include "Effect.cpp"
#include <FastLED.h>
//problematic fragment
DEFINE_GRADIENT_PALETTE(sunshine1) {
    195, 14, 32,
    24,  11, 36
};


class GradientMovint : public Effect
{
    CRGB* leds;
    const int NUM_LEDS;
    int gradient;
    int paletteIndex;

public:
    GradientMovint(int brightness, int speed, int gradient, CRGB* leds, int num_leds)
        : NUM_LEDS(num_leds)
        , gradient(gradient)
    {
        srand( time( NULL ) );
        this->speed = speed;
        this->leds = leds;
        this->brightness = brightness;
        color = color;
        paletteIndex = 0;
    };

    virtual void updateAndShow()
    {
        switch (gradient)
        {
        case 1:
            //fill_palette(leds, NUM_LEDS, paletteIndex, 255 / NUM_LEDS, nether1, brightness, LINEARBLEND);
            break;
        
        default:
            break;
        }
        paletteIndex++;
        delay(speed);
        FastLED.show();
    }

    void setSpeed(int speed)
    {
        this->speed = speed;
    }

    virtual void changeBrightness(int b) {
        brightness = b;
    }

    virtual void changeSpeed(int s) {
        speed = s;
    }
    virtual void changeColor(CRGB a) {
        this->color = a;
    }
};

#endif

and fragment of main:

#include "Bouncing.cpp"
#include "Effect.cpp"
#include "Rainbow.cpp"
#include "RainbowFull.cpp"
#include "StaticColor.cpp"
#include "Train.cpp"
#include "TrainsWithManyColors.cpp"
#include "Rain.cpp"
#include "Fire.cpp"
#include "Disable.cpp"
#include "Stroboscop.cpp"
#include "GradientMoving.cpp"

#include <Arduino.h> //podstawowa biblioteka
#include <ESPmDNS.h> //dns połączenie
#include <FastLED.h> //obsługa ledów !niekoniecznie musi być tutaj
#include <String>
#include <WebServer.h> //serwer webowy !musi być tutaj na ten moment
#include <WebSocketsServer.h> //web socket    !musi być tutaj na ten moment
#include <WiFi.h> //połączenie wifi !musi być tutaj na ten moment
#include <cstdlib>
#include <iostream>
#define LED_PIN 2
#define NUM_LEDS 297

int speed = 50;
int brightness = 150;
// tworzenie serwera, efektu działającego i ledów
CRGB leds[NUM_LEDS];
WebServer server;
WebSocketsServer webSocket = WebSocketsServer(81);
Effect* effect;

I had similar problem when i tried to create separate file just to hold cstring with html and js script. When it is in main there is no problem, but when its in different file and its included then i get multiple definitions

Please show the code where nether1 is defined. And if it is defined in a header file, please describe which files include it.

1 Like

Multiple definitions come from multiple .c/.cpp files creating the same global variables. If you have written the creation of the nether1 in a header file that is included by 2 or more .cpp files, that’s wrong and causes the error. You only declare it to be extern <type> <name>; in the header and use <type> <name> = initial_value; in one .cpp file.

this is a code that inplement nether1:

#ifndef GRADIENTMOVING_H
#define GRADIENTMOVING_H

#include "Effect.cpp"
#include <FastLED.h>
//problematic fragment
DEFINE_GRADIENT_PALETTE(nether1) {
    195, 14, 32,
    24,  11, 36
};


class GradientMovint : public Effect
{
    CRGB* leds;
    const int NUM_LEDS;
    int gradient;
    int paletteIndex;

public:
    GradientMovint(int brightness, int speed, int gradient, CRGB* leds, int num_leds)
        : NUM_LEDS(num_leds)
        , gradient(gradient)
    {
        srand( time( NULL ) );
        this->speed = speed;
        this->leds = leds;
        this->brightness = brightness;
        color = color;
        paletteIndex = 0;
    };

    virtual void updateAndShow()
    {
        switch (gradient)
        {
        case 1:
            //fill_palette(leds, NUM_LEDS, paletteIndex, 255 / NUM_LEDS, nether1, brightness, LINEARBLEND);
            break;
        
        default:
            break;
        }
        paletteIndex++;
        delay(speed);
        FastLED.show();
    }

    void setSpeed(int speed)
    {
        this->speed = speed;
    }

    virtual void changeBrightness(int b) {
        brightness = b;
    }

    virtual void changeSpeed(int s) {
        speed = s;
    }
    virtual void changeColor(CRGB a) {
        this->color = a;
    }
};

#endif

this file is included only in main

code of main:

// includy efektów
#include "Bouncing.cpp"
#include "Effect.cpp"
#include "Rainbow.cpp"
#include "RainbowFull.cpp"
#include "StaticColor.cpp"
#include "Train.cpp"
#include "TrainsWithManyColors.cpp"
#include "Rain.cpp"
#include "Fire.cpp"
#include "Disable.cpp"
#include "Stroboscop.cpp"
#include "GradientMoving.cpp"
#include "ChangeFullColors.cpp"

#include <Arduino.h> //podstawowa biblioteka
#include <ESPmDNS.h> //dns połączenie
#include <FastLED.h> //obsługa ledów !niekoniecznie musi być tutaj
#include <String>
#include <WebServer.h> //serwer webowy !musi być tutaj na ten moment
#include <WebSocketsServer.h> //web socket    !musi być tutaj na ten moment
#include <WiFi.h> //połączenie wifi !musi być tutaj na ten moment
#include <cstdlib>
#include <iostream>
#define LED_PIN 2
#define NUM_LEDS 297

int speed = 50;
int brightness = 150;
// tworzenie serwera, efektu działającego i ledów
CRGB leds[NUM_LEDS];
WebServer server;
WebSocketsServer webSocket = WebSocketsServer(81);
Effect* effect;

// plik html bo nie wiem jak go wsadzić gdziekolwiek indziej xDD
char webpage123[] = R"=====(
<html>
<head>
  <script>
    var Socket;
    function init() {
      Socket = new WebSocket('ws://' + window.location.hostname + ':81/');
      Socket.onmessage = function(event){
         console.log(event.data);
      }
    }

    function sendBrightness() {
      console.log("jasność costam");
      Socket.send("VB" + document.getElementById("brightness").value);
    }
    function chooseDisable() {
      Socket.send("Edisable");
    }
    function chooseTrain() {
      console.log("pociag costam");
      Socket.send("Etrain");
    }
    function chooseRainbow() {
      Socket.send("Erainbow");
    }
    function chooseRainbow2() {
      Socket.send("Erainbow2");
    }
    function chooseBounce() {
      Socket.send("Ebounce");
    }
    function chooseTrain2() {
      Socket.send("Etrain2");
    }
    function chooseStaticColor() {
      Socket.send("Estatic");
    }
    function chooseRain() {
      Socket.send("Erain");
    }
    function chooseFire() {
      Socket.send("Efire");
    }
    function chooseStroboscop() {
      Socket.send("Estroboscop");
    }
    function chooseGradientMoving() {
      Socket.send("Egradientmoving");
    }
    function chooseFullColor() {
      Socket.send("Efullcolor");
    }
    function chooseFullColor2() {
      Socket.send("Efullcolor2");
    }
    function sendSpeed() {
      Socket.send("VS" + document.getElementById("speed").value);
    }
    function ChangeBounceColor() {
      Socket.send("VCB" + document.getElementById("favcolor").value);
    }
    function ChangeStaticColor() {
      Socket.send("VCS" + document.getElementById("favcolor").value);
    }
  </script>
</head>
<body onload="javascript:init()">
  <div>
    <input type="button" value="Disable" id="disable" onclick="chooseDisable();" />
  </div>
  <hr/>
  <div>
    <input type="button" value="Train" id="rainbow" onclick="chooseTrain();" />
  </div>
  <hr/>
  <div>
    <input type="button" value="Rainbow" id="rainbow" onclick="chooseRainbow()" />
  </div>  
  <hr/>
  <div>
    <input type="button" value="Bounce" id="bounce" onclick="chooseBounce()" />
  </div>  
    <hr/>
  <div>
    <input type="button" value="Train 2" id="train2" onclick="chooseTrain2()" />
  </div>
  <hr/>
  <div>
    <input type="button" value="StaticColor" id="staticColor" onclick="chooseStaticColor()" />
  </div>
  <hr/>
  <div>
    <input type="button" value="Rainbow2" id="rainbow2" onclick="chooseRainbow2()" />
  </div>
  <hr/>
  <div>
    <input type="button" value="Rain" id="rain" onclick="chooseRain()" />
  </div>
  <hr/>
  <div>
    <input type="button" value="Fire" id="fire" onclick="chooseFire()" />
  </div>
  <hr/>
  <div>
    <input type="button" value="Stroboscop" id="stroboscop" onclick="chooseStroboscop()" />
  </div>
  <hr/>
  <div>
    <input type="button" value="GradientMoving" id="GradientMoving" onclick="chooseGradientMoving()" />
  </div>
  <hr/>
  <div>
    <input type="button" value="FullColor" id="fullColor" onclick="chooseFullColor()" />
  </div>
  <hr/>
  <div>
    <input type="button" value="FullColor2" id="fullColor2" onclick="chooseFullColor2()" />
  </div>    



  <hr/>
  <div>
    <input type="range" min="1" max="255" value="123" id="brightness" onchange="sendBrightness()" />
  </div>
  <hr/>
  <div>
    <input type="range" min="0" max="100" value="50" id="speed" onchange="sendSpeed()" />
  </div>
  <hr/>
  <div>
    <input type="color" id="favcolor" name="favcolor" value="#ff0000" />
  </div>
  <hr/>
  <div>
    <input type="button" value="Submit" id="sumbit" onclick="ChangeBounceColor()" />
  </div>
  <hr/>
  <div>
    <input type="button" value="Submit2" id="sumbit2" onclick="ChangeStaticColor()" />
  </div>
</body>
</html>
)=====";

// obsługa komunikacji z klientem, w stronę; odbieranie danych z przeglądarki; komunikacja klient->serwer
void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length)
{
    if (type == WStype_TEXT) {
        Serial.println(brightness);
        String str = (char*)payload;
        Serial.println(str);
        if (str[0] == 'E') {
            // Serial.print("2");
            String substr = str.substring(1);
            
            if (substr == "train") {
                delete effect;
                Train* t = new Train(speed, brightness, 6, leds, NUM_LEDS);
                t->addColor(CRGB(150, 150, 0));
                t->addColor(CRGB(0, 150, 150));
                effect = t;
            } else if (substr == "rainbow") {
                delete effect;
                effect = new Rainbow(speed, brightness, 1, leds, NUM_LEDS);
            } else if (substr == "rainbow2") {
                delete effect;
                effect = new RainbowFull(speed, brightness, leds, NUM_LEDS);
            } else if (substr == "bounce") {
                delete effect;
                effect = new Bouncing(speed, brightness, CRGB(150, 150, 0), leds, NUM_LEDS, 6);
            } else if (substr == "train2") {
                delete effect;
                TrainsWithManyColors* t = new TrainsWithManyColors(speed, brightness, 10, leds, NUM_LEDS);
                
                t->addColor(CRGB(255, 0, 0));
                t->addColor(CRGB(0, 0, 255));
                effect = t;
            } else if (substr == "static") {
                delete effect;
                effect = new StaticColor(brightness, leds, NUM_LEDS);
            } else if (substr == "rain") {
                delete effect;
                effect = new Rain(brightness, speed, CRGB::Green, leds, NUM_LEDS);
            } else if (substr == "fire") {
                delete effect;
                effect = new Fire(brightness, speed, leds, NUM_LEDS);
            } else if (substr == "disable") {
                delete effect;
                effect = new Disable(leds, NUM_LEDS);
            } else if (substr == "stroboscop") {
                delete effect;
                effect = new Stroboscop(brightness, speed, CRGB::White, leds, NUM_LEDS);
            } else if (substr == "gradientmoving") {
                delete effect;
                effect = new GradientMovint(brightness, speed, 1, leds, NUM_LEDS);
            } else if (substr == "fullcolor") {
                delete effect;
                ChangeFullColors* t = new ChangeFullColors(speed, brightness, leds, NUM_LEDS);
                t->addColor(CRGB(255, 0, 0));
                t->addColor(CRGB(0, 0, 255));
                effect = t;
            } else if (substr == "fullcolor2") {
                delete effect;
                ChangeFullColors* t = new ChangeFullColors(speed, brightness, leds, NUM_LEDS);
                t->addColor(CRGB(255, 0, 0));
                t->addColor(CRGB(255, 255, 0));
                t->addColor(CRGB(0, 255, 255));
                t->addColor(CRGB(0, 0, 255));
                t->addColor(CRGB(255, 0, 255));
                effect = t;
            }
        } else if (str[0] == 'V') {
            if (str[1] == 'B') {
                brightness = (int)strtol((const char*)&payload[2], NULL, 10);
                Serial.println(brightness);
                effect->changeBrightness(brightness);
            } else if (str[1] == 'S') {
                speed = (int)strtol((const char*)&payload[2], NULL, 10);
                effect->changeSpeed(speed);
                Serial.println(speed);
            } else if (str[1] == 'C') {
                String substr = str.substring(4);
                char* p;
                int color = strtol(substr.c_str(), &p, 16);
                if (*p != 0) {
                    Serial.println("not a color");
                }
                Serial.println(color);
                CRGB a = color;
                effect->changeColor(a);
            }
        }
    }
    // Serial.print("end");
}

// konfiguracja serwera, websocketa, dnsa
void setupServer(std::string ssid, std::string password)
{
    WiFi.begin(ssid.c_str(), password.c_str());

    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
    }

    Serial.print("connected\n");
    Serial.print("IP address: ");
    Serial.print(WiFi.localIP());

    if (!MDNS.begin("esp32")) {
        Serial.println("Error setting up MDNS responder!");
        while (1) {
            delay(1000);
        }
    }
    Serial.println("mDNS responder started");
    server.on("/", []() {
        server.send_P(200, "text/html", webpage123);
    });
    server.begin();
    webSocket.begin();
    webSocket.onEvent(webSocketEvent);
}

void setup()
{
    Serial.begin(115200);
    FastLED.addLeds<WS2812, LED_PIN, GRB>(leds, NUM_LEDS);
    // server.start("Dom", "Kabanos1", NUM_LEDS, leds);
    setupServer("UPC3356958", "m3sdBthjwfus");
    effect = new Rainbow(speed, brightness, 10, leds, NUM_LEDS);
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void loop()
{
    webSocket.loop();
    server.handleClient();
    effect->updateAndShow();
}

file gradientmoving.cpp is only place that create struct/variable with this name. Its also only place where i use this type of struct.

I tried to create this as static in class but then i get this error

#define DEFINE_GRADIENT_PALETTE(X) FL_ALIGN_PROGMEM extern const TProgmemRGBGradientPalette_byte X[] FL_PROGMEM =
Jest rozwijane do:

FL_ALIGN_PROGMEM extern const TProgmemRGBGradientPalette_byte nether1[] FL_PROGMEM =
more than one storage class C / ++ (81)

Little update.
I tried to create ceparate file for global variables named Gradients.cpp

#ifndef GRADIENT_H
#define GRADIENT_H

#include <FastLED.h>

DEFINE_GRADIENT_PALETTE( heatmap_gp ) {
  0,     0,  0,  0,   //black
128,   255,  0,  0,   //red
224,   255,255,  0,   //bright yellow
255,   255,255,255 }; //full white

#endif

Its included only in Gradient Moving.cpp

#ifndef GRADIENTMOVING_H
#define GRADIENTMOVING_H

#include "Effect.cpp"
#include <FastLED.h>
#include "Gradients.cpp"

class GradientMovint : public Effect
{
    
    CRGB* leds;
    const int NUM_LEDS;
    int gradient;
    int paletteIndex;

public:
    GradientMovint(int brightness, int speed, int gradient, CRGB* leds, int num_leds)
        : NUM_LEDS(num_leds)
        , gradient(gradient)
    {
        srand( time( NULL ) );
        this->speed = speed;
        this->leds = leds;
        this->brightness = brightness;
        color = color;
        paletteIndex = 0;
    };

    virtual void updateAndShow()
    {
        switch (gradient)
        {
        case 1:
            //fill_palette(leds, NUM_LEDS, paletteIndex, 255 / NUM_LEDS, nether1, brightness, LINEARBLEND);
            break;
        
        default:
            break;
        }
        paletteIndex++;
        delay(speed);
        FastLED.show();
    }

    void setSpeed(int speed)
    {
        this->speed = speed;
    }

    virtual void changeBrightness(int b) {
        brightness = b;
    }

    virtual void changeSpeed(int s) {
        speed = s;
    }
    virtual void changeColor(CRGB a) {
        this->color = a;
    }
};

#endif

and now i get even weirder error

.pio\build\esp32dev\src\Gradients.cpp.o:(.rodata.heatmap_gp+0x0): multiple definition of `heatmap_gp'
.pio\build\esp32dev\src\GradientMoving.cpp.o:(.rodata.heatmap_gp+0x0): first defined here

it looks like it first include Gradient.cpp into second file and then check for multiple definitions in whole file.

No. Please use

properly. The definition only belongs in one .cpp file. In the header you need a declaration.

// in header
DECLARE_GRADIENT_PALETTE(heatmap_gp);
// in one cpp file
DEFINE_GRADIENT_PALETTE( heatmap_gp ) {
  0,     0,  0,  0,   //black
128,   255,  0,  0,   //red
224,   255,255,  0,   //bright yellow
255,   255,255,255 }; //full white

ok, this worked. Thanks

I got another weird problem with colors.

DEFINE_GRADIENT_PALETTE( heatmap_gp ) {
  0,     0,  0,  0,   //black
128,   255,  0,  0,   //red
224,   255,255,  0,   //bright yellow
255,   255,255,255 }; //full white

this for example give me black, red, yellow, green. It doesnt light blue leds.

DEFINE_GRADIENT_PALETTE( sunset_pallete) {
0, 255, 112, 154,
127, 254, 225, 64,
255, 255, 112, 154
};

this give me white with a little bit of yellow.