0

I'm working on an led matrix project where I'm running a series of patterns made of bitmap frames.

Here's an example: https://vimeo.com/564184465

Right now I'm using a series of method calls named per pattern to run the animation:

void CSMatrix::runPattern(PATTERNS pattern, uint8_t totalRuns)
{
  for (uint8_t i = 0; i < totalRuns; i++)
  {
    switch (pattern)
    {
    case PATTERN_ZIG_ZAG:
      runPatternSpacedStripes();
      break;
    case PATTERN_ARROW_UP:
      runPatternArrowUp();
      break;
...
    }
  }
}

void CSMatrix::runPatternSpacedStripes()
{
  for (uint8_t i = 0; i < SPACED_STRIPES_LEN; i++)
  {
    renderFrame(SPACED_STRIPES[i]);
    FastLED.show();
    delay(100);
  }
}

void CSMatrix::runPatternArrowUp()
{
  for (uint8_t i = 0; i < ARROW_UP_LEN; i++)
  {
    renderFrame(ARROW_UP[i]);
    FastLED.show();
    delay(100);
  }
}

...

And the bitmaps used to control the matrix pixel states look like this (the functions beneath renderFrame basically say "if it's a 1, use this color, if it's a 0, use that color":

const byte SPACED_STRIPES[][8] = {
    {0b11000110,
     0b10001100,
     0b00011000,
     0b00110001,
     0b01100011,
     0b11000110,
     0b10001100,
     0b00011000},
    {0b01100011,
     0b11000110,
     0b10001100,
     0b00011000,
     0b00110001,
     0b01100011,
     0b11000110,
     0b10001100},
...    
};
const int SPACED_STRIPES_LEN = sizeof(SPACED_STRIPES) / 8;

This is working fine, but having a bunch of identical methods to run each pattern is not great.

So, with this working I decided to refactor to make a common runTwoColorPattern method that could accept the pattern frames and the length to run the given pattern:

void CSMatrix::runTwoColorPattern(const byte *frames, const int length, int delayDuration)
{
  for (uint8_t i = 0; i < length; i++)
  {
    renderFrame(frames[i]);
    FastLED.show();
    delay(delayDuration);
  }
}

The problem I'm running into is figuring out how to properly pass the two dimensional array to the function.

I tried updating my switch statement by passing the pointer of SPACED_STRIPES into the runTwoColorPattern:

    switch (pattern)
    {
    case PATTERN_ZIG_ZAG:
      runTwoColorPattern(SPACED_STRIPES, SPACED_STRIPES_LEN, 100);
      //runPatternSpacedStripes();
      break;

But when I do I get the error

argument of type "const byte (*)[8]" is incompatible with parameter of type "const byte *"C/C++(167)

error

I've found that I can correct this by dereferencing the first item of the multidimensional array, (which would mean I'm passing the memory address to the first pointer element of the multi-dimensional array (I think)), but when I do this the pattern runs backwards and is jittery :|

https://vimeo.com/564184624

I'm not totally sure how to correct this. I'm still fairly new to pointers so I'm sure there's something I'm misunderstanding with passing a pointer for a multi-dimensional array, but I thought what I was passing was basically an address of where to start for the pointer, so I'm not sure why I'm hitting these snags or how to fix it.

Any suggestions?

4
  • You really don't need a 2D array for this - a simple 1D one will do and just use multiple-of-8 offsets to read each frame.
    – Majenko
    Jun 17, 2021 at 14:38
  • Aaaaaah that's true! I was running with this format b/c it was easy to visually parse and adjust the bitmaps by hand after generating them from an online calculator, but ultimately that format isn't needed. So to restate to make sure I understand: I can make a 1d array where each element is a single number representation of the frame and those numbers can be parsed 8 bits at a time to get the rows within the frame, right? Jun 17, 2021 at 15:24
  • No, you'd just have a big "blob" of 8 bit values. Exactly as you have now, but without the },{ between each frame. Then frame 0 starts at offset 0. Frame 1 at offset 8. Frame 2 at offset 16, etc. Basically &frames[i * 8]
    – Majenko
    Jun 17, 2021 at 15:30
  • ah, even simpler. I'll give it a try and when i have it working I'll post an answer. Thanks for the help!! Jun 17, 2021 at 15:38

2 Answers 2

1

A 2D array would be passed as const byte frames[][8]. However you really don't need the complexity of a 2D array - you can do it with a 1D array.

Your array would look like:

const byte SPACED_STRIPES[] = {
     0b11000110,
     0b10001100,
     0b00011000,
     0b00110001,
     0b01100011,
     0b11000110,
     0b10001100,
     0b00011000,

     0b01100011,
     0b11000110,
     0b10001100,
     0b00011000,
     0b00110001,
     0b01100011,
     0b11000110,
     0b10001100,
...    
};

And you reference frames within it as a multiple of 8 of the index. Using your existing code it would look like:

void CSMatrix::runTwoColorPattern(const byte *frames, const int length, int delayDuration)
{
  for (uint8_t i = 0; i < length; i++)
  {
    renderFrame(&frames[i * 8]); // Take the address of the first value of the frame at a multiple of 8
    FastLED.show();
    delay(delayDuration);
  }
}
7
  • Awesome, this worked! I updated my pattern and the method and now the animations are running as expected. Implementing the changes also helped me understand exactly what you were going for; the structure of the multi-dimensional array helped me visually, but isn't necessary for the parsing. The other added benefit of doing it this way is that now that it's a flat array I can send through arbitrary sized blobs for different sized matricies (16x16, 32x32 ...) and just parameterize the multiplier for grabbing the pointer index. Thanks again for the help! Jun 17, 2021 at 17:49
  • Re “const byte *frames[8]”: you mean const byte (*frames)[8]. Jun 17, 2021 at 19:21
  • @EdgarBonet I have never seen that format before in my life. I actually meant frames[][8]. Is (*frames)[8] some newer C++ syntax that didn't exist when I was taught C at uni?
    – Majenko
    Jun 18, 2021 at 10:12
  • AFAIK, (*frames)[8] is ANSI C, maybe even K&R C. Jun 18, 2021 at 10:39
  • Well, I have only ever come across brackets like that when working with function pointers.
    – Majenko
    Jun 18, 2021 at 10:45
2

The error message is quite explicit:

argument of type "const byte (*)[8]" is incompatible with parameter of type "const byte *"

If you want to pass SPACED_STRIPES to the method, it is going to be passed as const byte (*)[8], i.e. pointer to arrays of 8 byte. You can just set the parameter type accordingly:

void CSMatrix::runTwoColorPattern(
    const byte (*frames)[8], const int length, int delayDuration)
{
    // body unchanged
}
1
  • Thanks for this answer. I ended up going with the suggestion to simplify the array, but this answer helps me understand the error I was getting. I didn't realize I could write the method signature with the parens around the pointer. Jun 17, 2021 at 17:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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