Error 1 when trying to upload to board

Hello,

I just started using the PlatformIO to learn how to program WS2812B LEDs using a Heltec WiFi kit 32. I’ve gone through several tutorials without any problems and I’ve been able to make the lights do some cool rainbow effects using the Fastled Library. Now we are trying to do a simple marquee effect with the rainbow. The tutorial I’m following had us write a separate file for the marquee effect, we include that in our main code, and call a DrawMarquee() function for the effect. When I try to upload to the board I get this: *** [.pio\build\heltec_wifi_kit_32\src.main.cpp.o] Error 1

Here is the main.cpp:

#include <Arduino.h>
#include <U8g2lib.h>
#define FASTLED_INTERNAL
#include <FastLED.h>
 
#define OLED_CLOCK  15          // Pins for the OLED display
#define OLED_DATA   4
#define OLED_RESET  16
 
#define NUM_LEDS    111          // FastLED definitions
#define LED_PIN     5

#include "marquee.h"


CRGB g_LEDs[NUM_LEDS] = {0};    // Frame buffer for FastLED
 
U8G2_SSD1306_128X64_NONAME_F_HW_I2C g_OLED(U8G2_R2, OLED_RESET, OLED_CLOCK, OLED_DATA);
int g_lineHeight = 0;
int g_Brightness = 250;

// FramesPerSecond
//
// Tracks a weighted average to smooth out the values that it calcs as the simple reciprocal
// of the amount of time taken specified by the caller.  So 1/3 of a second is 3 fps, and it
// will take up to 10 frames or so to stabilize on that value.
 
double FramesPerSecond(double seconds)
{
  static double framesPerSecond; 
  framesPerSecond = (framesPerSecond * .9) + (1.0 / seconds * .1);
  return framesPerSecond;
}
 
void setup() 
{
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(LED_PIN, OUTPUT);
 
  Serial.begin(115200);
  while (!Serial) { }
  Serial.println("ESP32 Startup");
 
  g_OLED.begin();
  g_OLED.clear();
  g_OLED.setFont(u8g2_font_profont15_tf);
  g_lineHeight = g_OLED.getFontAscent() - g_OLED.getFontDescent();        // Descent is a negative number so we add it to the total
 
  FastLED.addLeds<WS2812B, LED_PIN, GRB>(g_LEDs, NUM_LEDS);               // Add our LED strip to the FastLED library
  FastLED.setBrightness(250);
}
 
void loop() 
{
  bool bLED = 0;
  double fps = 0;
 
  uint8_t initialHue = 0;
  const uint8_t deltaHue = 16;
  const uint8_t hueDensity = 8;
 
  for (;;)
  {
    bLED = !bLED;                                                         // Blink the LED off and on  
    digitalWrite(LED_BUILTIN, bLED);
 
    double dStart = millis() / 1000.0;                                    // Display a frame and calc how long it takes
 
    // Handle OLED drawing
 
    g_OLED.clearBuffer();
    g_OLED.setCursor(0, g_lineHeight);
    g_OLED.printf("FPS: %.1lf", fps);
    g_OLED.sendBuffer();
 
    // Handle LEDs


    DrawMarquee();

    FastLED.setBrightness(g_Brightness);
    FastLED.show();
 
    double dEnd = millis() / 1000.0;
    fps = FramesPerSecond(dEnd - dStart);
  }

}

Here is the marquee.h file:

#include <Arduino.h>
#define FASTLED_INTERNAL
#include <FastLED.h>

void DrawMarquee()
{
    static byte j = 0;
    j += 4;
    byte k = j;

    // The following is roughly the equivalent to fill_rainbow(g_LEDs, NUM_LEDS, j, 8)

    CRGB c;
    for (int i = 0; i < NUM_LEDS; i++)
        g_LEDs[i] = c.setHue(k+=8);

    static int scroll = 0;
    scroll++;

    for (int i=scroll % 5; i < NUM_LEDS - 1; i += 5)
        g_LEDs[i] = CRGB::Black;

    delay(50); 
}

Any help is greatly appreciated. I’ve found people having similar problems, but what has solved their problem has not solved mine. I just know it is a simple fix, but I just cannot figure it out.

Thank you.

This is a header file without any include guards. This might cause double-inclusion of the file. Use the mechanism

#ifndef MARQUEE_H
#define MARQUEE_H

/* actual header content */

#endif /* MARQUEE_H */

or, #pragma once at the start of the ehader file.

Second of all, you are implementing functions in a header file; While header-only implementation style libraries do exist, you might want to go the header file (with only function declarations) and a implementation file (.cpp) with the implementations, since it provides a better overview of the file when only looking at the header file.

This is just the error message saying “There was an error”. The juicy actual error detail is before that, which you didn’t show.

You have also not shown your platformio.ini.

I will try and guess the platformio.ini and reproduce the error with your files to see where the C/C++ error lies.

With a platformio.ini of

[env:heltec_wifi_kit_32]
platform = espressif32
board = heltec_wifi_kit_32
framework = arduino
lib_deps =
   olikraus/U8g2 @ ^2.28.8
   fastled/FastLED @ ^3.3.3

The error message for your program reads

Indexing .pio\build\heltec_wifi_kit_32\liba1b\libWire.a
In file included from src\main.cpp:13:0:
src\marquee.h: In function 'void DrawMarquee()':
src\marquee.h:15:9: error: 'g_LEDs' was not declared in this scope
         g_LEDs[i] = c.setHue(k+=8);
         ^
src\marquee.h:21:9: error: 'g_LEDs' was not declared in this scope
         g_LEDs[i] = CRGB::Black;
         ^
src\main.cpp: In function 'void loop()':
src\main.cpp:58:11: warning: unused variable 'initialHue' [-Wunused-variable]
   uint8_t initialHue = 0;
           ^
src\main.cpp:59:17: warning: unused variable 'deltaHue' [-Wunused-variable]
   const uint8_t deltaHue = 16;
                 ^
src\main.cpp:60:17: warning: unused variable 'hueDensity' [-Wunused-variable]
   const uint8_t hueDensity = 8;
                 ^

which tells you exactly what’s wrong: the global variable g_LEDs is unknown in the function DrawMarquee(). When you look at the code, you write:

So you first pull in the function code that uses g_LEDs and then you actually declare and create the g_LEDs variable. So, at the position in which DrawMarquee() is defined, there is no mention of the global variable g_LEDs before that.

An extremely cheap way to fix that is to simply declare the global variable before you use it, i.e. reverse the order of the #include line and the one below. Then you get

Building .pio\build\heltec_wifi_kit_32\firmware.bin
Checking size .pio\build\heltec_wifi_kit_32\firmware.elf
Advanced Memory Usage is available via "PlatformIO Home > Project Inspect"
RAM:   [=         ]   5.5% (used 18148 bytes from 327680 bytes)
Flash: [==        ]  20.8% (used 272662 bytes from 1310720 bytes)
esptool.py v2.6
========================= [SUCCESS] Took 17.16 seconds =========================

However I don’t consider that a proper fix, since global variable sharing accross files is not done according to best practices at all. This forum already has a lot of info on splitting source code files and the associated techinques with sharing global variables: Tutorial for creating multi cpp file arduino project

I can also give my reference solution for that problem. The basic technique is:

  • declare global variables as extern ... in a header file
  • split code into header file and implementation file

Example:

src\global_vars.h

#ifndef GLOBAL_VARS_H
#define GLOBAL_VARS_H

/* all shared global variables and config are declared here :) */
/* need type definitions from FastLED. */
#define FASTLED_INTERNAL
#include <FastLED.h> 

#define NUM_LEDS    111          // FastLED definitions

/* defined in main.cpp. declared here. */
extern CRGB g_LEDs[NUM_LEDS];

#endif /* GLOBAL_VARS_H */

src\marquee.h

#ifndef MARQUEE_H
#define MARQUEE_H

/* declare functions provided by this compilation unit */

void DrawMarquee();

#endif /* MARQUEE_H */

src\marquee.cpp

#include <Arduino.h>
#define FASTLED_INTERNAL
#include <FastLED.h>
#include <marquee.h>
/* we need to use some shared global variables to implement this */
#include <global_vars.h> 

void DrawMarquee()
{
    static byte j = 0;
    j += 4;
    byte k = j;

    // The following is roughly the equivalent to fill_rainbow(g_LEDs, NUM_LEDS, j, 8)

    CRGB c;
    for (int i = 0; i < NUM_LEDS; i++)
        g_LEDs[i] = c.setHue(k+=8);

    static int scroll = 0;
    scroll++;

    for (int i=scroll % 5; i < NUM_LEDS - 1; i += 5)
        g_LEDs[i] = CRGB::Black;

    delay(50); 
}

src\main.cpp

#include <Arduino.h>
#include <U8g2lib.h>
#define FASTLED_INTERNAL
#include <FastLED.h>
#include <global_vars.h>
 #include "marquee.h"

#define OLED_CLOCK  15          // Pins for the OLED display
#define OLED_DATA   4
#define OLED_RESET  16
 
#define LED_PIN     5

CRGB g_LEDs[NUM_LEDS] = {0};    // Frame buffer for FastLED

/* non-shared global variables. (for now?) */
U8G2_SSD1306_128X64_NONAME_F_HW_I2C g_OLED(U8G2_R2, OLED_RESET, OLED_CLOCK, OLED_DATA);
int g_lineHeight = 0;
int g_Brightness = 250;

// FramesPerSecond
//
// Tracks a weighted average to smooth out the values that it calcs as the simple reciprocal
// of the amount of time taken specified by the caller.  So 1/3 of a second is 3 fps, and it
// will take up to 10 frames or so to stabilize on that value.
 
double FramesPerSecond(double seconds)
{
  static double framesPerSecond; 
  framesPerSecond = (framesPerSecond * .9) + (1.0 / seconds * .1);
  return framesPerSecond;
}
 
void setup() 
{
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(LED_PIN, OUTPUT);
 
  Serial.begin(115200);
  while (!Serial) { }
  Serial.println("ESP32 Startup");
 
  g_OLED.begin();
  g_OLED.clear();
  g_OLED.setFont(u8g2_font_profont15_tf);
  g_lineHeight = g_OLED.getFontAscent() - g_OLED.getFontDescent();        // Descent is a negative number so we add it to the total
 
  FastLED.addLeds<WS2812B, LED_PIN, GRB>(g_LEDs, NUM_LEDS);               // Add our LED strip to the FastLED library
  FastLED.setBrightness(250);
}
 
void loop() 
{
  bool bLED = 0;
  double fps = 0;
 
  uint8_t initialHue = 0;
  const uint8_t deltaHue = 16;
  const uint8_t hueDensity = 8;
 
  for (;;)
  {
    bLED = !bLED;                                                         // Blink the LED off and on  
    digitalWrite(LED_BUILTIN, bLED);
 
    double dStart = millis() / 1000.0;                                    // Display a frame and calc how long it takes
 
    // Handle OLED drawing
 
    g_OLED.clearBuffer();
    g_OLED.setCursor(0, g_lineHeight);
    g_OLED.printf("FPS: %.1lf", fps);
    g_OLED.sendBuffer();
 
    // Handle LEDs


    DrawMarquee();

    FastLED.setBrightness(g_Brightness);
    FastLED.show();
 
    double dEnd = millis() / 1000.0;
    fps = FramesPerSecond(dEnd - dStart);
  }
}

Just a bit of code to give you some ideas.

1 Like

I knew it was something easy. I just had to swap the #include line with the one below like you said. Thanks so much for taking the time to help out with this, and for the examples, I will try them out! I think it will be a fun holiday season this year.