Creating my own program with makefile

Am a newbie, learning to use this fantastic library in my own test program. And would like some help, please?

What I’ve done so far:

  1. I use a Raspberry Pi 4B, with DietPi.
  2. Have Waveshare 64x32 RGB panels; Chain=3, Parallel=3
  3. Have changed “hardware-mapping.c” file, swapping blue and green pins in ‘regular’, and have then done a remake on the rpi-rgb-led-matrix, examples-api-use and utils directories. All to stop having to use –led-rgb-sequence=rbg in the command-flags, successfully. Now all examples in those directories work correctly without having to use that particular flag!! :smiley:
  4. Am using VS Code to SSH in. And have copied the relevant lines of code within demo-main.cc that “ColourPulseGenerator” (or sudo ./demo -D4 ) uses in my own C++ file, called Colour_Changer_01.cc
  5. Within the directory /home/dietpi/, are the directories /rpi-rgb-led-matrix, and /My_RGB_Matrix_Code
  6. I have put Colour_Changer_01.cc in a subdirectory here; /home/dietpi/My_RGB_Matrix_Code/Code_01/
  7. I have copied the Makefile from /examples-api-use into this same directory. And altered it. But am having trouble getting it to make my C++ file.

I would like help, as the makefile is my stumbling block right now, I think.

Here’s what I’ve got, (and I know it’s wrong):

CFLAGS=-Wall -O3 -g -Wextra -Wno-unused-parameter
CXXFLAGS=$(CFLAGS)
OBJECTS=Colour_Changer_01.o
BINARIES=Colour_Changer_01

# Where our library resides. You mostly only need to change the
# RGB_LIB_DISTRIBUTION, this is where the library is checked out.
RGB_LIB_DISTRIBUTION= ../home/dietpi/rpi-rgb-led-matrix
RGB_INCDIR=$(RGB_LIB_DISTRIBUTION)/include
RGB_LIBDIR=$(RGB_LIB_DISTRIBUTION)/lib
RGB_LIBRARY_NAME=rgbmatrix
RGB_LIBRARY=$(RGB_LIBDIR)/lib$(RGB_LIBRARY_NAME).a
LDFLAGS+=-L$(RGB_LIBDIR) -l$(RGB_LIBRARY_NAME) -lrt -lm -lpthread

# To compile image-example
MAGICK_CXXFLAGS?=$(shell GraphicsMagick++-config --cppflags --cxxflags)
MAGICK_LDFLAGS?=$(shell GraphicsMagick++-config --ldflags --libs)

all : $(BINARIES)

$(RGB_LIBRARY): FORCE
	$(MAKE) -C $(RGB_LIBDIR)

Colour_Changer_01 : Colour_Changer_01.cc

# All the binaries that have the same name as the object file.q
% : %.o $(RGB_LIBRARY)
	$(CXX) $< -o $@ $(LDFLAGS)

image-example.o : image-example.cc
	$(CXX) -I$(RGB_INCDIR) $(CXXFLAGS) $(MAGICK_CXXFLAGS) -c -o $@ $<

image-example: image-example.o $(RGB_LIBRARY)
	$(CXX) $< -o $@ $(LDFLAGS) $(MAGICK_LDFLAGS)

# Since the C example uses the C++ library underneath, which depends on C++
# runtime stuff, you still have to also link -lstdc++
c-example : c-example.o $(RGB_LIBRARY)
	$(CC) $< -o $@ $(LDFLAGS) -lstdc++

%.o : %.cc
	$(CXX) -I$(RGB_INCDIR) $(CXXFLAGS) -c -o $@ $<

%.o : %.c
	$(CC) -I$(RGB_INCDIR) $(CFLAGS) -c -o $@ $<

clean:
	rm -f $(OBJECTS) $(BINARIES)

FORCE:
.PHONY: FORCE

When I run it, the error is that the “led-matrix.h” file isn’t found.

For reference, this is the C++ file I’m toying with and learning from (Colour_Changer_01.cc):

// Name: Colour_Changer_01.cc
// Date: 2025/08/18
// Raspberry Pi 4B, with Waveshare 64x32 rgb led matrix
// Command line flags usually used: --led-cols=64 --led-rows=32 --led-chain=3 --led-parallel=3 --led-slowdown-gpio=2 --led-gpio-mapping=regular
// Notes:
//          - Try to get the RGB LED Matrix to display colours, for a few seconds, then change to another colour, and loop infinitely
//          - Copying from demo-main.cc in /rpi-rgb-led-matrix/examples-api-use
//          - This should work without the usual command flags (see above) after the "-D4"
//          - To run this: sudo ./demo -D4


// Start of copying from demo-main.cc
#include "led-matrix.h"

#include "pixel-mapper.h"
#include "graphics.h"

#include <assert.h>
#include <getopt.h>
#include <limits.h>
#include <math.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <algorithm>

using std::min;
using std::max;

#define TERM_ERR  "\033[1;31m"
#define TERM_NORM "\033[0m"

using namespace rgb_matrix;

volatile bool interrupt_received = false;
static void InterruptHandler(int signo) {
  interrupt_received = true;
}

class DemoRunner {
protected:
  DemoRunner(Canvas *canvas) : canvas_(canvas) {}
  inline Canvas *canvas() { return canvas_; }

public:
  virtual ~DemoRunner() {}
  virtual void Run() = 0;

private:
  Canvas *const canvas_;
};

// Simple generator that pulses through RGB and White.
class ColorPulseGenerator : public DemoRunner {
public:
  ColorPulseGenerator(RGBMatrix *m) : DemoRunner(m), matrix_(m) {
    off_screen_canvas_ = m->CreateFrameCanvas();
  }
  void Run() override {
    uint32_t continuum = 0;
    while (!interrupt_received) {
      usleep(5 * 1000);
      continuum += 1;
      continuum %= 3 * 255;
      int r = 0, g = 0, b = 0;
      if (continuum <= 255) {
        int c = continuum;
        b = 255 - c;
        r = c;
      } else if (continuum > 255 && continuum <= 511) {
        int c = continuum - 256;
        r = 255 - c;
        g = c;
      } else {
        int c = continuum - 512;
        g = 255 - c;
        b = c;
      }
      off_screen_canvas_->Fill(r, g, b);
      off_screen_canvas_ = matrix_->SwapOnVSync(off_screen_canvas_);
    }
  }

private:
  RGBMatrix *const matrix_;
  FrameCanvas *off_screen_canvas_;
};

static int usage(const char *progname) {
  fprintf(stderr, "usage: %s <options> -D <demo-nr> [optional parameter]\n",
          progname);
  fprintf(stderr, "Options:\n");
  fprintf(stderr,
          "\t-D <demo-nr>              : Always needs to be set\n"
          );


  rgb_matrix::PrintMatrixFlags(stderr);

  fprintf(stderr, "Demos, choosen with -D\n");
  fprintf(stderr, "\t0  - some rotating square\n"
          "\t1  - forward scrolling an image (-m <scroll-ms>)\n"
          "\t2  - backward scrolling an image (-m <scroll-ms>)\n"
          "\t3  - test image: a square\n"
          "\t4  - Pulsing color\n"
          "\t5  - Grayscale Block\n"
          "\t6  - Abelian sandpile model (-m <time-step-ms>)\n"
          "\t7  - Conway's game of life (-m <time-step-ms>)\n"
          "\t8  - Langton's ant (-m <time-step-ms>)\n"
          "\t9  - Volume bars (-m <time-step-ms>)\n"
          "\t10 - Evolution of color (-m <time-step-ms>)\n"
          "\t11 - Brightness pulse generator\n");
  fprintf(stderr, "Example:\n\t%s -D 1 runtext.ppm\n"
          "Scrolls the runtext until Ctrl-C is pressed\n", progname);
  return 1;
}

int main(int argc, char *argv[]) {
  int demo = -1;
  int scroll_ms = 30;

  const char *demo_parameter = NULL;
  RGBMatrix::Options matrix_options;
  rgb_matrix::RuntimeOptions runtime_opt;

  // These are the defaults when no command-line flags are given.
  matrix_options.rows = 32;                 // Kept the same
  matrix_options.cols = 64;                 // Not originally included
  matrix_options.chain_length = 3;          // Originally = 1
  matrix_options.parallel = 3;              // Originally = 1
  matrix_options.slowdown.gpio = 2;         // Not originally included
  matrix_options.gpio.mapping = "regular";  // Not originally included

  // First things first: extract the command line flags that contain
  // relevant matrix options.
  if (!ParseOptionsFromFlags(&argc, &argv, &matrix_options, &runtime_opt)) {
    return usage(argv[0]);
  }

  int opt;
  while ((opt = getopt(argc, argv, "dD:r:P:c:p:b:m:LR:")) != -1) {
    switch (opt) {
    case 'D':
      demo = atoi(optarg);
      break;

    case 'm':
      scroll_ms = atoi(optarg);
      break;

    default: /* '?' */
      return usage(argv[0]);
    }
  }

  if (optind < argc) {
    demo_parameter = argv[optind];
  }

  if (demo < 0) {
    fprintf(stderr, TERM_ERR "Expected required option -D <demo>\n" TERM_NORM);
    return usage(argv[0]);
  }

  RGBMatrix *matrix = RGBMatrix::CreateFromOptions(matrix_options, runtime_opt);
  if (matrix == NULL)
    return 1;

  printf("Size: %dx%d. Hardware gpio mapping: %s\n",
         matrix->width(), matrix->height(), matrix_options.hardware_mapping);

  Canvas *canvas = matrix;

  // The DemoRunner objects are filling
  // the matrix continuously.
  DemoRunner *demo_runner = NULL;
  switch (demo) {
  case 0:
  case 1:
  case 2:
  case 3:
  case 4:
    demo_runner = new ColorPulseGenerator(matrix);
    break;

  case 5:
  case 6:
  case 7:
  case 8:
  case 9:
  case 10:
  case 11:
  }

  if (demo_runner == NULL)
    return usage(argv[0]);

  // Set up an interrupt handler to be able to stop animations while they go
  // on. Each demo tests for while (!interrupt_received) {},
  // so they exit as soon as they get a signal.
  signal(SIGTERM, InterruptHandler);
  signal(SIGINT, InterruptHandler);

  printf("Press <CTRL-C> to exit and reset LEDs\n");

  // Now, run our particular demo; it will exit when it sees interrupt_received.
  demo_runner->Run();

  delete demo_runner;
  delete canvas;

  printf("Received CTRL-C. Exiting.\n");
  return 0;
}
// End of copying from demo-main.cc


Any help on how to make the make file work would be great.

Sorry if this is an obvious thing, but am muddling through the learning process (with a lot of fun)

Thank you

Honestly, debugging Makefiles remotely is not reasonably doable. How about you put your code in the api-demo dir, edit that makefile to add one more target, your new code, and then things should work.

Once that works, you can expand more effort to move your code to a different directory if you really care.
All that said, if you get “led-matrix.h not found”, it means your include path, which you will see on your device, when you build, does not contain the directory where that file, actually is. Check the gcc command and check what it includes and does not include

Thank you for replying.

I think I’ll follow your advice, to add my experimental code efforts into the same directory as the api-demo’s, and just add a target to the existing makefile. It’ll be easier, I think. At least until I learn writing makefile’s properly, which I’ve been immersing myself in to try and get my head around them haha!

I’ll also check out the one I wrote with the suggestions you mentioned too.

Thanks again, much appreciate the help/pointers. :smiley: