Arduino 2D arrays, on a die drawn on an 8x8 matrix
Intermediate
Or see every lesson.
- 455parts on the bench
- 43boards running now
- 1.00xreal time, on every board
A two-dimensional array is a table. int grid[3][4]; is 3 rows of 4 numbers each, and grid[1][2] is row 1, column 2, both counted from 0. To go over the whole table, put one for loop inside another: the outer one for the rows and the inner one for the columns.
The circuit above is a die on an 8x8 LED matrix on an Uno. Its sketch keeps all six faces in const byte FACES[6][8]: six rows, one per face, of eight bytes, one per line of LEDs. Press Run and click the tilt switch a few times to shake it. The face rolls, slows and stops, the RGB LED turns green, and the serial monitor prints "rolled a" and the number.
Below is the dice table, making and filling a grid, counting rows and columns, passing a table to a function, a table of words, and what a big table costs on an Uno, each run with what it printed.
Six faces, eight rows each
Here is the table from the sketch running above and the function that draws a face. FACES[n - 1] is the whole face for the number n, and FACES[n - 1][row] is one line of it, a byte whose bits are the eight LEDs across. The face for 1 is all zeros but rows 3 and 4, where 0x18 lights the two middle LEDs: the single pip.
const byte FACES[6][8] = {
{0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00},
{0x00, 0x06, 0x06, 0x00, 0x00, 0x60, 0x60, 0x00},
{0xC0, 0xC0, 0x00, 0x18, 0x18, 0x00, 0x03, 0x03},
{0x00, 0x66, 0x66, 0x00, 0x00, 0x66, 0x66, 0x00},
{0xC3, 0xC3, 0x00, 0x18, 0x18, 0x00, 0xC3, 0xC3},
{0x66, 0x66, 0x00, 0x66, 0x66, 0x00, 0x66, 0x66},
};Make one, fill it, print it
Each inner pair of braces is one row. The sketch below prints the grid as a table, one row per line with a tab between the numbers, and then grid[1][2], which printed 7: row 1 is the second row, and column 2 is its third number.
Writing works the same way as reading: grid[row][col] = value; inside the same two loops fills the whole table, row by row. Keep the rows on the outside and the columns on the inside, because that is the order the numbers sit in memory, and use names like row and col rather than i and j, so a swapped pair of indexes stands out when you read it back.
int grid[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12},
};
void setup() {
Serial.begin(9600);
for (int row = 0; row < 3; row++) {
for (int col = 0; col < 4; col++) {
Serial.print(grid[row][col]);
Serial.print('\t');
}
Serial.println();
}
Serial.println(grid[1][2]);
}
void loop() {
}How many rows and columns
sizeof(grid) is the size of the whole table in bytes: 24 for 12 ints of 2 bytes. Divide by the size of one row, sizeof(grid[0]), for the number of rows, which printed 3, and divide one row by one element, sizeof(grid[0]) / sizeof(grid[0][0]), for the columns, which printed 4. Loops written with these keep working when the table grows.
The numbers sit in memory one row after another. Read past the end of a row and you get the start of the next: grid[1][4] printed 9, the first number of row 2, with only a warning, array index 4 is past the end of the array (that has type 'int[4]'). Past the end of the last row there is no next row, just whatever else is in memory.
Passing a table to a function
A function that takes a 2D array has to be told how long a row is, so it can find row 2. Only the number of rows may be left out. The sketch below adds up the grid and printed 78. Write int g[][] with both sizes left out and the build stops with array has incomplete element type 'int[]'; GCC says declaration of 'g' as multidimensional array must have bounds for all dimensions except the first.
const int COLS = 4;
int total(int g[][COLS], int rows) {
int sum = 0;
for (int r = 0; r < rows; r++) {
for (int c = 0; c < COLS; c++) {
sum += g[r][c];
}
}
return sum;
}
int grid[3][COLS] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
void setup() {
Serial.begin(9600);
Serial.println(total(grid, 3));
}
void loop() {
}A table of words
Text can be stored two ways. char words[3][6] is a real 2D array: three rows of six characters, each word padded out, 18 bytes in all, and words[1][0] is the letter g. const char *names[] is an array of pointers, each to a word of its own length. Both printed "green" and "blue" the same way.
A row must have room for the longest word and its zero byte. With rows of 5, "green" did not fit: initializer-string for char array is too long, array size is 5 but initializer has size 6 (including the null terminating character). GCC says initializer-string for 'char [5]' is too long.
const char *names[] = {"red", "green", "blue"};
char words[3][6] = {"red", "green", "blue"};
void setup() {
Serial.begin(9600);
Serial.println(names[1]);
Serial.println(words[2]);
Serial.println(words[1][0]);
Serial.println(sizeof(words));
}
void loop() {
}Big tables and 2 KB of RAM
An Uno has 2048 bytes of RAM for every variable in the sketch. int big[30][40] wants 2400 of them, and the build stopped: global variables do not fit in the 2 KB SRAM, overflowed by 538 bytes, counting the other variables a sketch always has. A table that never changes, like the dice faces or a font, can live in flash instead: add PROGMEM and read it with pgm_read_byte(&FACES[1][2]), which printed 6 from a small table. Flash has 32 KB.
Questions
How do I declare a 2D array in Arduino?
Give the rows, then the columns: int grid[3][4]; or fill it as you declare it, one pair of braces per row. The row count may be left out when every row is written out.
How do I loop through a 2D array?
Put a for loop over the columns inside a for loop over the rows and read grid[row][col]. Work the counts out with sizeof so the loops follow the table if it changes.
How do I pass a 2D array to a function?
Write the column count in the parameter, int g[][4], and pass the row count as a second parameter. Only the first size may be left empty.
How do I make an array of strings on Arduino?
Use const char *names[] = {"red", "green"}; for fixed words, or a char array with one row per word, char words[3][6], when the words have to change. Each row needs one extra byte for the zero at the end.
How big can an array be on an Arduino Uno?
Everything in RAM has to fit in 2048 bytes, with room left for the stack. A constant table can go in the 32 KB of flash with PROGMEM.
Keep going
Where the facts on this page come from
Build this for real
Open the editor, change a value and watch the number move with it. Nothing to install, and no account needed.