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)
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 :|
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?
2 Answers 2
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);
}
}
-
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!Chris Schmitz– Chris Schmitz2021年06月17日 17:49:29 +00:00Commented Jun 17, 2021 at 17:49
-
Re "
const byte *frames[8]
": you meanconst byte (*frames)[8]
.Edgar Bonet– Edgar Bonet2021年06月17日 19:21:50 +00:00Commented 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– Majenko2021年06月18日 10:12:39 +00:00Commented Jun 18, 2021 at 10:12 -
AFAIK,
(*frames)[8]
is ANSI C, maybe even K&R C.Edgar Bonet– Edgar Bonet2021年06月18日 10:39:26 +00:00Commented Jun 18, 2021 at 10:39 -
Well, I have only ever come across brackets like that when working with function pointers.Majenko– Majenko2021年06月18日 10:45:33 +00:00Commented Jun 18, 2021 at 10:45
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
}
-
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.Chris Schmitz– Chris Schmitz2021年06月17日 17:52:39 +00:00Commented Jun 17, 2021 at 17:52
},{
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]