Arduino struct, on three lamps that blink at their own rates
Or see every lesson.
- 455parts on the bench
- 43boards running now
- 1.00xreal time, on every board
A struct gives one name to several values that belong together. struct Lamp { int pin; unsigned long everyMs; bool on; }; makes a new type called Lamp, and every Lamp you make carries its own pin, rate and state. Read or set one member with a dot, as in red.pin or red.on = true, and keep a whole list of them in one array of structs.
The circuit above is three lamps on an Uno, each blinking at its own rate. Press Run. The serial monitor prints "lamp on pin 13 every 250 ms", then the same for pins 12 and 11 at 400 and 650 ms, and "sizeof(Lamp) = 11". The sketch keeps each lamp’s pin, rate, last change and state in one struct, and loop() walks the array.
Below is how to define and fill a struct, return two values from a function, pass one by reference, how big one is, saving one to EEPROM and the errors people meet first, each run on an Uno with what it printed.
Want the step-by-step version? The lesson "Things that belong together" walks through this with checkpoints.
Open the lessonDefine it, then make some
Here is the struct in the sketch running above, and the array that makes three of them. The values in each pair of braces fill the members in the order they are written: the pin, the rate, the last time it changed and whether it is on. So lamps[1].everyMs is 400 and lamps[2].pin is 11.
Adding a fourth lamp is one more line in the list. loop() counts the array with sizeof(lamps) / sizeof(lamps[0]), so it picks the new lamp up without another change. Six loose arrays of pins, rates and states would need an edit in six places.
A struct definition ends with a semicolon after its closing brace. Leave it out and the build stops: clang says expected ';' after struct, and GCC says expected ';' after struct definition.
struct Lamp {
int pin;
unsigned long everyMs;
unsigned long last;
bool on;
};
Lamp lamps[] = {
{LAMP_A, 250, 0, false},
{LAMP_B, 400, 0, false},
{LAMP_C, 650, 0, false},
};Return two values from one function
A function returns one value, but that value can be a struct, which is the usual answer to "how do I return two values from a function". The sketch below finds the lowest and highest of five readings in one pass and hands both back in a Range. It printed "low 471, high 530".
struct Range {
int low;
int high;
};
Range rangeOf(const int list[], int count) {
Range r = {list[0], list[0]};
for (int i = 1; i < count; i++) {
if (list[i] < r.low) r.low = list[i];
if (list[i] > r.high) r.high = list[i];
}
return r;
}
void setup() {
Serial.begin(9600);
int readings[] = {512, 498, 530, 471, 505};
Range r = rangeOf(readings, 5);
Serial.print("low ");
Serial.print(r.low);
Serial.print(", high ");
Serial.println(r.high);
}
void loop() {
}Pass it by reference to change it
A struct handed to a function is copied, like an int. toggleCopy() below flips its own copy and the caller’s lamp stays off, so the first line printed 0. Put & after the type and the function gets the caller’s lamp itself: toggle() printed 1.
A reference also saves copying a large struct, which matters with 2 KB of RAM. When the function only reads the struct, say so with const: void show(const Lamp &lamp). With a pointer to a struct, use an arrow instead of the dot: Lamp *p = &lamps[1]; p->on = false;.
struct Lamp {
int pin;
bool on;
};
void toggleCopy(Lamp lamp) {
lamp.on = !lamp.on;
}
void toggle(Lamp &lamp) {
lamp.on = !lamp.on;
}
void setup() {
Serial.begin(9600);
Lamp red = {13, false};
toggleCopy(red);
Serial.println(red.on);
toggle(red);
Serial.println(red.on);
}
void loop() {
}How big is a struct?
sizeof(Lamp) printed 11 on the Uno: 2 bytes for the int, 4 each for the two unsigned longs and 1 for the bool. The Uno’s chip packs members one after another with no gaps. The same struct printed 16 on an ESP32-C3, where an int is 4 bytes and members are lined up on 4-byte boundaries. Count the bytes before sending a struct from one board to another.
Save a struct to EEPROM
EEPROM.put() writes a whole struct, byte by byte, and EEPROM.get() reads it back into a variable of the same type. The sketch below saved two settings and read them back as "brightness 180, mode 2". The struct is 3 bytes, so the next thing saved can start at address 3. On a real board those bytes survive a power cut, which makes a struct the tidy way to keep settings.
#include <EEPROM.h>
struct Settings {
int brightness;
byte mode;
};
void setup() {
Serial.begin(9600);
Settings saved = {180, 2};
EEPROM.put(0, saved);
Settings back;
EEPROM.get(0, back);
Serial.print("brightness ");
Serial.print(back.brightness);
Serial.print(", mode ");
Serial.println(back.mode);
Serial.println(sizeof(Settings));
}
void loop() {
}The errors people meet first
Too many values: a Lamp given five values when it has four members stopped the build with excess elements in struct initializer. GCC says too many initializers for 'Lamp'.
Used before it exists: a struct has to be defined above the first line that uses it. With struct Reading written under setup(), the line in setup() that made one stopped with unknown type name 'Reading', and GCC says 'Reading' was not declared in this scope. Move the definition to the top of the sketch, under the #include lines.
Comparing two: if (a == b) with two Lamps stopped with invalid operands to binary expression ('Lamp' and 'Lamp'); GCC says no match for 'operator=='. C++ does not compare structs for you. Compare the members you care about, a.pin == b.pin && a.on == b.on, or put that line in a function.
Questions
What is a struct in Arduino?
A type you make yourself that groups several values under one name, such as a pin, a rate and a state for one lamp. It is plain C++, so it works on every board Arduino supports, and each variable of that type has its own copy of every member.
How do I make an array of structs?
Define the struct, then declare an array of it with one pair of braces per element: Lamp lamps[] = {{13, 250, 0, false}, {12, 400, 0, false}}; Read a member with lamps[i].pin inside a for loop.
How do I return two values from an Arduino function?
Return a struct that holds both, as rangeOf() does with a low and a high here. Or pass the variables in by reference, with an & after each type, and let the function set them.
What is the difference between a struct and a class?
Only who can see the members by default. A struct’s members are public unless you say otherwise and a class’s are private. Both can hold functions as well as values; Arduino code tends to use struct for plain groups of values.
Why does my sketch say the struct was not declared?
The struct is defined below the line that first uses it. Move the definition to the top of the sketch, above setup() and above any function that takes or returns one.
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.