// A fast-track guide for developers coming from other languages
The Arduino Uno (and its R3/R4 variants) uses a microcontroller — the ATmega328P (Uno R3) or Renesas RA4M1 (Uno R4). Unlike Python or JavaScript that run in a managed runtime, C++ compiles directly to native machine code, giving you total control of hardware with zero overhead.
16 MHz · 32KB Flash · 2KB SRAM · 1KB EEPROM · 14 digital pins · 6 analog pins
48 MHz · 256KB Flash · 32KB SRAM · 14 digital · 6 analog · USB · CAN
avr-g++ compiler · AVR-Libc · Arduino framework wraps hardware registers in readable APIs
No OS, no heap GC, no STL (Uno R3), limited SRAM — code must be lean and deterministic
The Arduino IDE uses a subset of C++14. On the Uno R3, the STL (vector, map, etc.) is unavailable. Dynamic memory (new/delete) works but causes heap fragmentation on 2KB SRAM — avoid it. Exceptions and RTTI are disabled.
Every Arduino sketch is a .ino file (or a .cpp file in a library). The IDE auto-generates a wrapper that calls your two mandatory functions.
// 1. Preprocessor directives — processed before compilation #include <Arduino.h> // usually auto-included by IDE #define LED_PIN 13 // compile-time constant (no memory used) #define BAUD 9600 // 2. Global variables — live for the entire program lifetime int counter = 0; // 3. Function declarations (prototypes) — optional in .ino; required in .cpp void blinkLED(int times); // ────────────────────────────────────────────── // setup() — runs ONCE when the board powers on // ────────────────────────────────────────────── void setup() { Serial.begin(BAUD); // start serial monitor at 9600 baud pinMode(LED_PIN, OUTPUT); // configure pin 13 as output Serial.println("Arduino ready!"); } // ────────────────────────────────────────────── // loop() — runs FOREVER after setup() completes // ────────────────────────────────────────────── void loop() { counter++; blinkLED(3); delay(1000); // pause 1000 ms } // 4. User-defined function void blinkLED(int times) { for (int i = 0; i < times; i++) { digitalWrite(LED_PIN, HIGH); delay(200); digitalWrite(LED_PIN, LOW); delay(200); } }
C++ is compiled, not interpreted. Every variable needs a declared type. Curly braces {} define scope. Statements end with ;. There is no garbage collector — you manage memory.
| Type | Arduino Size | Range | Use Case |
|---|---|---|---|
| bool | 1 byte | true / false | Flags, on/off state |
| byte | 1 byte | 0 – 255 | Raw pin values, buffers |
| char | 1 byte | -128 – 127 | ASCII characters |
| unsigned char | 1 byte | 0 – 255 | Same as byte |
| int | 2 bytes (AVR) | -32,768 – 32,767 | General integers ⚠ small! |
| unsigned int | 2 bytes | 0 – 65,535 | Counts, indices |
| long | 4 bytes | ±2.1 billion | millis(), large counts |
| unsigned long | 4 bytes | 0 – 4.29 billion | Timestamps, millis() |
| float | 4 bytes | ±3.4×10³⁸ | Sensor math, 6-7 sig digits |
| double | 4 bytes (AVR!) | same as float | On Uno R3, identical to float |
| String | object | heap-based | Convenient but use cautiously |
| char[] | static array | fixed length | Preferred for strings (no heap) |
// Declaration and initialization int count = 0; unsigned long t = 0UL; // UL suffix = unsigned long literal float voltage = 3.14f; // f suffix = float literal bool isOn = false; char letter = 'A'; // single quotes for char const int PIN = 7; // const: value cannot change // Storage class qualifiers static int runCount = 0; // persists between function calls volatile int isr_flag = 0; // used in interrupt service routines // Type casting int raw = analogRead(A0); // 0 – 1023 float v = (float)raw * 5.0 / 1023.0; // C-style cast // PROGMEM — store constant data in Flash instead of SRAM #include <avr/pgmspace.h> const char msg[] PROGMEM = "Hello from Flash!"; // saves precious SRAM
Unlike desktop C++ where int is 4 bytes, on the ATmega328P it's only 2 bytes. Always use long when working with millis() (which overflows after ~49 days). Use uint8_t, int16_t etc. for explicit sizes.
| Category | Operator | Example | Note |
|---|---|---|---|
| Arithmetic | + - * / % | x = a + b; | Integer division truncates |
| Increment | ++ -- | i++; ++i; | Post vs pre increment matters in expressions |
| Compound assign | += -= *= /= %= | x += 5; | Shorthand for x = x + 5 |
| Comparison | == != < > <= >= | if (x == 10) | Returns bool |
| Logical | && || ! | if (a && !b) | Short-circuit evaluation |
| Bitwise | & | ^ ~ << >> | x &= 0x0F; | Essential for register manipulation |
| Ternary | ? : | y = x > 0 ? 1 : 0; | Inline if/else |
// Bitwise operations are critical in embedded programming byte reg = 0b10110000; // binary literal reg |= (1 << 2); // SET bit 2 → 10110100 reg &= ~(1 << 7); // CLEAR bit 7 → 00110100 reg ^= (1 << 4); // TOGGLE bit 4→ 00100100 bool bit4set = (reg >> 4) & 0x01; // READ bit 4
// ── IF / ELSE IF / ELSE ────────────────────── int temp = 25; if (temp < 0) { Serial.println("Freezing"); } else if (temp < 20) { Serial.println("Cold"); } else { Serial.println("Warm"); } // ── SWITCH ─────────────────────────────────── byte mode = 2; switch (mode) { case 1: Serial.println("Mode: Blink"); break; case 2: Serial.println("Mode: Fade"); break; case 3: Serial.println("Mode: Pulse"); break; default: Serial.println("Mode: Off"); } // ── FOR LOOP ───────────────────────────────── for (int i = 0; i < 10; i++) { Serial.println(i); } // ── WHILE LOOP ─────────────────────────────── int count = 0; while (count < 5) { count++; } // ── DO-WHILE — executes at least once ──────── do { Serial.println("Always runs once"); } while (false); // ── BREAK & CONTINUE ───────────────────────── for (int i = 0; i < 10; i++) { if (i == 5) break; // exit loop if (i % 2 == 0) continue; // skip even numbers Serial.println(i); } // ── NON-BLOCKING TIMING (preferred over delay) unsigned long previousMillis = 0; const unsigned long INTERVAL = 1000; void loop() { unsigned long now = millis(); if (now - previousMillis >= INTERVAL) { previousMillis = now; // do periodic work here without blocking } }
// ── Basic function ─────────────────────────── // return_type name(param_type param) { body } int add(int a, int b) { return a + b; } // ── void function (no return value) ────────── void printSeparator() { Serial.println("──────────────"); } // ── Default parameters ──────────────────────── void blink(int pin, int ms = 500) { digitalWrite(pin, HIGH); delay(ms); digitalWrite(pin, LOW); delay(ms); } // Usage: blink(13); — uses default 500ms // Usage: blink(13, 100); — uses 100ms // ── Pass by value vs by reference ──────────── void byValue(int x) { x = 99; } // original unchanged void byRef (int &x) { x = 99; } // modifies original (& = reference) void byPtr (int *x) { *x = 99; } // pointer dereference // ── Overloaded functions (same name, diff params) float mapVal(int v, int lo, int hi) { return (float)v / (hi - lo); } float mapVal(float v, float lo, float hi) { return (v - lo) / (hi - lo); } // ── Recursion — use sparingly (limited stack!) long factorial(int n) { if (n <= 1) return 1; return n * factorial(n - 1); } // ── Interrupt Service Routine (ISR) ────────── volatile bool buttonPressed = false; void IRAM_ATTR onButtonPress() { // runs on hardware interrupt buttonPressed = true; } // In setup(): attachInterrupt(digitalPinToInterrupt(2), onButtonPress, FALLING);
// ── Arrays ──────────────────────────────────── int pins[4] = {9, 10, 11, 12}; // fixed size at compile time int len = sizeof(pins) / sizeof(pins[0]); // = 4 for (int i = 0; i < len; i++) { pinMode(pins[i], OUTPUT); } // ── 2D Arrays ───────────────────────────────── byte matrix[3][3] = { {1, 0, 1}, {0, 1, 0}, {1, 0, 1} }; // ── C-style strings (char arrays) ───────────── char greeting[] = "Hello"; // auto-sized: 6 bytes (incl. \0) char buf[32]; // buffer for building strings sprintf(buf, "Temp: %d C", 25); // printf-style formatting Serial.println(buf); // String functions from <string.h> int len2 = strlen(greeting); // length without null strcpy(buf, greeting); // copy string strcat(buf, " World"); // concatenate int cmp = strcmp("abc", "abc"); // 0 if equal // ── Arduino String object ───────────────────── String s1 = "Sensor: "; String s2 = s1 + String(42); // "Sensor: 42" — heap allocation! Serial.println(s2); // ⚠ Avoid String in loops — causes heap fragmentation on R3
Pointers are addresses of memory locations. They unlock direct hardware register access — essential in embedded programming.
int value = 42; int *ptr = &value; // ptr holds the ADDRESS of value Serial.println(*ptr); // dereference: prints 42 *ptr = 100; // modifies value through pointer Serial.println(value); // prints 100 // ── Pointers and arrays ─────────────────────── int arr[] = {10, 20, 30}; int *p = arr; // array name IS a pointer to first element Serial.println(*(p + 1)); // prints 20 (pointer arithmetic) // ── Passing array to function ───────────────── void printArray(int *data, int length) { for (int i = 0; i < length; i++) { Serial.println(data[i]); } } // ── References (C++ only, not in C) ────────── void swap(int &a, int &b) { // & makes it a reference param int tmp = a; a = b; b = tmp; } // swap(x, y); — no & needed at call site // ── Function pointers — useful for callbacks ── void doWork(void (*callback)()) { // ... do something ... callback(); // call whatever function was passed in } void myAction() { Serial.println("Action!"); } // doWork(myAction);
C++ adds OOP on top of C. Classes bundle data (member variables) with behavior (member functions/methods). On Arduino this is used extensively — every library is a class (e.g., Serial, Wire, Servo).
// ── Class Definition (usually in a .h header file) ── class LED { private: // accessible only inside the class int _pin; bool _state; public: // accessible from anywhere // Constructor — called when object is created LED(int pin) : _pin(pin), _state(false) { pinMode(_pin, OUTPUT); } // Destructor — called when object goes out of scope ~LED() { digitalWrite(_pin, LOW); } void on() { _state = true; digitalWrite(_pin, HIGH); } void off() { _state = false; digitalWrite(_pin, LOW); } void toggle() { _state ? off() : on(); } // Getter — const means it doesn't modify the object bool isOn() const { return _state; } void blink(int times, int ms = 200) { for (int i = 0; i < times; i++) { on(); delay(ms); off(); delay(ms); } } }; // ── Inheritance ─────────────────────────────── class FadingLED : public LED { // inherits everything from LED private: int _brightness; public: FadingLED(int pin) : LED(pin), _brightness(0) {} void fadeTo(int brightness) { _brightness = brightness; analogWrite(_pin, brightness); // 0-255 PWM } }; // ── Usage in sketch ─────────────────────────── LED statusLED(13); // creates object, calls constructor FadingLED rgbLED(9); void setup() { statusLED.blink(3); // object.method() rgbLED.fadeTo(128); }
C++ supports virtual methods for runtime polymorphism. On Arduino this works but adds a vtable overhead (~2–6 bytes/object). Use it in libraries, but avoid in tight loops on the Uno R3.
| Function | Purpose | Example |
|---|---|---|
| pinMode(pin, mode) | Set pin as INPUT, OUTPUT, or INPUT_PULLUP | pinMode(7, INPUT_PULLUP); |
| digitalWrite(pin, val) | Write HIGH or LOW to digital pin | digitalWrite(13, HIGH); |
| digitalRead(pin) | Read digital pin → HIGH or LOW | int v = digitalRead(2); |
| analogRead(pin) | Read analog pin → 0–1023 (10-bit ADC) | int v = analogRead(A0); |
| analogWrite(pin, val) | PWM output 0–255 on PWM pins (3,5,6,9,10,11) | analogWrite(9, 128); |
| delay(ms) | Block for N milliseconds | delay(1000); |
| delayMicroseconds(us) | Block for N microseconds | delayMicroseconds(50); |
| millis() | Milliseconds since boot (unsigned long) | unsigned long t = millis(); |
| micros() | Microseconds since boot | unsigned long t = micros(); |
| map(v, f1, t1, f2, t2) | Re-map a value from one range to another | map(val, 0, 1023, 0, 255) |
| constrain(v, lo, hi) | Clamp value to range | constrain(x, 0, 100) |
| random(min, max) | Pseudo-random integer | random(0, 256) |
| Serial.begin(baud) | Init serial @ baud rate | Serial.begin(9600); |
| Serial.print/println() | Send data to serial monitor | Serial.println(3.14); |
| Serial.available() | Bytes waiting to be read | if (Serial.available()) |
| Serial.read() | Read one byte from serial | char c = Serial.read(); |
| attachInterrupt(pin, ISR, mode) | Hardware interrupt on pin | attachInterrupt(0, isr, FALLING); |
| tone(pin, freq, dur) | Generate square wave (buzzer) | tone(8, 440, 500); |
| noTone(pin) | Stop tone | noTone(8); |
On a microcontroller there's no terminal, so "Hello World" means blinking an LED and printing to the Serial Monitor.
/* * Hello World — Arduino Uno * Blinks the built-in LED on pin 13 * and prints a message to the Serial Monitor. * * Hardware: No extra components needed. * Open: Tools → Serial Monitor, set baud to 9600 */ const int LED = 13; // built-in LED on all Uno boards int blinkCount = 0; void setup() { Serial.begin(9600); pinMode(LED, OUTPUT); Serial.println("Hello, Arduino World!"); } void loop() { digitalWrite(LED, HIGH); // LED on delay(500); digitalWrite(LED, LOW); // LED off delay(500); blinkCount++; Serial.print("Blink #"); Serial.println(blinkCount); }
This single sketch demonstrates virtually every core C++ and Arduino concept in one cohesive program. It simulates a "smart LED control station" with serial commands, multiple modes, a class, timers, arrays, and more.
Arduino Uno · 3× LEDs (pins 9, 10, 11) · 3× 220Ω resistors · 1× pushbutton (pin 2) · USB cable
Classes · Arrays · Enums · Pointers · ISR · Serial I/O · Timers · Functions · Bitwise ops · Control flow
Upload sketch → Open Serial Monitor at 9600 baud → Type commands: 1 (blink), 2 (chase), 3 (pulse), 0 (off), ? (help)
/* * ╔══════════════════════════════════════════════════════╗ * ║ SMART LED STATION — C++ Arduino Project ║ * ║ Demonstrates core C++ and Arduino concepts in one ║ * ║ cohesive beginner program. ║ * ╚══════════════════════════════════════════════════════╝ * * CIRCUIT: * Pins 9, 10, 11 → LED anode → 220Ω resistor → GND * Pin 2 → one leg of pushbutton → GND * (use INPUT_PULLUP so no external pull-up resistor needed) * * CONCEPTS DEMONSTRATED: * #defines and const Enumerations (enum) * Data types and casting Arrays and sizeof * Classes and objects Inheritance * Pointers and references Function overloading * Default parameters ISR / hardware interrupts * Non-blocking timing Serial communication * switch/case, for, while Bitwise operations * PROGMEM (Flash strings) Static variables */ #include <avr/pgmspace.h> // PROGMEM macro // ════════════════════════════════════════════════ // 1. CONSTANTS & DEFINES // ════════════════════════════════════════════════ #define BAUD_RATE 9600 #define BTN_PIN 2 // interrupt-capable pin #define NUM_LEDS 3 const int LED_PINS[NUM_LEDS] = {9, 10, 11}; // PWM-capable const unsigned long TICK_MS = 50; // main timer tick // ════════════════════════════════════════════════ // 2. ENUMERATION — named integer constants // Cleaner than bare #defines for related states // ════════════════════════════════════════════════ enum Mode { MODE_OFF = 0, MODE_BLINK = 1, MODE_CHASE = 2, MODE_PULSE = 3 }; // ════════════════════════════════════════════════ // 3. CLASS — encapsulates a single LED with state // ════════════════════════════════════════════════ class SmartLED { private: uint8_t _pin; // explicit 8-bit unsigned type uint8_t _brightness; // 0–255 bool _isOn; public: // Constructor using member initializer list SmartLED(uint8_t pin) : _pin(pin), _brightness(0), _isOn(false) { pinMode(_pin, OUTPUT); off(); } void on(uint8_t brightness = 255) { // default param _brightness = brightness; _isOn = true; analogWrite(_pin, _brightness); } void off() { _isOn = false; _brightness = 0; analogWrite(_pin, 0); } void toggle() { _isOn ? off() : on(); } // Fade using pointer-to-brightness for demo void setFromPtr(uint8_t *brightnessPtr) { on(*brightnessPtr); // dereference pointer } // Getters (const methods — promise not to modify object) bool isOn() const { return _isOn; } uint8_t brightness() const { return _brightness; } uint8_t pin() const { return _pin; } }; // ════════════════════════════════════════════════ // 4. GLOBAL VARIABLES & OBJECTS // ════════════════════════════════════════════════ SmartLED leds[NUM_LEDS] = { SmartLED(LED_PINS[0]), // pin 9 SmartLED(LED_PINS[1]), // pin 10 SmartLED(LED_PINS[2]) // pin 11 }; Mode currentMode = MODE_OFF; unsigned long lastTick = 0; int tickCount = 0; volatile bool btnEvent = false; // volatile: changed in ISR // PROGMEM string — stored in Flash, not SRAM const char HELP_STR[] PROGMEM = "\r\n=== Smart LED Station ===\r\n" " 0 = OFF\r\n" " 1 = Blink all LEDs\r\n" " 2 = Chase pattern\r\n" " 3 = Pulse / fade\r\n" " ? = Show this help\r\n" " s = Print status\r\n" "========================\r\n"; // ════════════════════════════════════════════════ // 5. INTERRUPT SERVICE ROUTINE // Called automatically by hardware when button // is pressed (pin 2 goes LOW = FALLING edge) // ════════════════════════════════════════════════ void onButtonPress() { // ISR must be fast, no delay() btnEvent = true; // set flag; handle in loop() } // ════════════════════════════════════════════════ // 6. HELPER FUNCTIONS // ════════════════════════════════════════════════ // Turn off all LEDs — passing array by pointer void allOff(SmartLED *ledArray, int count) { for (int i = 0; i < count; i++) { ledArray[i].off(); } } // Function overloading — same name, different params void printStatus() { Serial.print("[STATUS] Mode="); Serial.print((int)currentMode); Serial.print(" Tick="); Serial.print(tickCount); Serial.print(" LEDs: "); for (int i = 0; i < NUM_LEDS; i++) { Serial.print(leds[i].isOn() ? "ON " : "off "); } Serial.println(); } void printStatus(const char *label) { // overload with label Serial.print(label); Serial.print(": "); printStatus(); } // Map mode enum to name — demonstrates switch const char* modeName(Mode m) { switch (m) { case MODE_OFF: return "OFF"; case MODE_BLINK: return "BLINK"; case MODE_CHASE: return "CHASE"; case MODE_PULSE: return "PULSE"; default: return "UNKNOWN"; } } // Cycle to next mode (demonstrates static variable) void cycleMode() { currentMode = (Mode)(((int)currentMode + 1) % 4); // 0→1→2→3→0 allOff(leds, NUM_LEDS); Serial.print("Button → Mode: "); Serial.println(modeName(currentMode)); } // ════════════════════════════════════════════════ // 7. MODE UPDATE FUNCTIONS (called each tick) // ════════════════════════════════════════════════ // MODE 1: Blink all LEDs together void updateBlink() { bool ledState = (tickCount / 10) % 2 == 0; // toggle every 10 ticks for (int i = 0; i < NUM_LEDS; i++) { ledState ? leds[i].on() : leds[i].off(); } } // MODE 2: LED chase / Knight Rider pattern void updateChase() { int active = (tickCount / 8) % NUM_LEDS; // cycles 0→1→2→0 for (int i = 0; i < NUM_LEDS; i++) { (i == active) ? leds[i].on() : leds[i].off(); } } // MODE 3: PWM pulse / breathing effect // Uses a sine-like triangle wave for smooth fade // Demonstrates bitwise ops and uint8_t arithmetic void updatePulse() { uint8_t phase = (uint8_t)(tickCount * 4); // 0–255 cycling // Triangle wave: up 0→255 then down 255→0 uint8_t brightness; if (phase < 128) { brightness = phase * 2; // ramp up } else { brightness = 255 - (phase - 128) * 2; // ramp down } // Use a pointer to brightness to demo setFromPtr() uint8_t *bPtr = &brightness; for (int i = 0; i < NUM_LEDS; i++) { // Each LED has a different phase offset using bitwise shift uint8_t offset = brightness + (uint8_t)(i * 85); // 85 = 255/3 leds[i].setFromPtr(&offset); } } // ════════════════════════════════════════════════ // 8. SERIAL COMMAND PARSER // Demonstrates while loop, char handling, Serial // ════════════════════════════════════════════════ void handleSerial() { while (Serial.available() > 0) { char cmd = (char)Serial.read(); // cast int→char allOff(leds, NUM_LEDS); tickCount = 0; switch (cmd) { case '0': currentMode = MODE_OFF; Serial.println("Mode: OFF"); break; case '1': currentMode = MODE_BLINK; Serial.println("Mode: BLINK"); break; case '2': currentMode = MODE_CHASE; Serial.println("Mode: CHASE"); break; case '3': currentMode = MODE_PULSE; Serial.println("Mode: PULSE"); break; case '?': Serial.println((__FlashStringHelper*)HELP_STR); break; case 's': printStatus("Manual"); break; // overloaded version default: Serial.println("Unknown cmd. Type ? for help"); } } } // ════════════════════════════════════════════════ // 9. SETUP — runs once // ════════════════════════════════════════════════ void setup() { Serial.begin(BAUD_RATE); // Configure button with internal pull-up resistor // Pin reads HIGH normally, LOW when pressed pinMode(BTN_PIN, INPUT_PULLUP); // Attach hardware interrupt on pin 2 // digitalPinToInterrupt() converts pin# to interrupt# // FALLING = trigger when signal goes HIGH→LOW attachInterrupt(digitalPinToInterrupt(BTN_PIN), onButtonPress, FALLING); // Startup sequence — demonstrates for loop & array access Serial.println((__FlashStringHelper*)HELP_STR); for (int i = 0; i < NUM_LEDS; i++) { leds[i].on(255); delay(150); leds[i].off(); } unsigned long uptime = millis(); // record startup time Serial.print("Ready in "); Serial.print(uptime); Serial.println("ms"); } // ════════════════════════════════════════════════ // 10. LOOP — runs forever // Uses NON-BLOCKING timing (millis) — not delay // ════════════════════════════════════════════════ void loop() { // ── Handle hardware interrupt flag ─────────── if (btnEvent) { btnEvent = false; // clear flag FIRST (re-entrant safety) static unsigned long lastPress = 0; // static persists between calls! unsigned long now = millis(); if (now - lastPress > 200) { // debounce: ignore if <200ms ago lastPress = now; cycleMode(); } } // ── Non-blocking timer — tick every TICK_MS ── unsigned long now = millis(); if (now - lastTick >= TICK_MS) { lastTick = now; tickCount++; // increment global tick counter // ── Dispatch to mode update function ───────── switch (currentMode) { case MODE_OFF: allOff(leds, NUM_LEDS); break; case MODE_BLINK: updateBlink(); break; case MODE_CHASE: updateChase(); break; case MODE_PULSE: updatePulse(); break; } // ── Print status every 100 ticks (5 sec) ───── if (tickCount % 100 == 0) { printStatus(); // no-arg overload } } // ── Check serial commands (runs every loop) ─── handleSerial(); // ── Demonstrate do-while: read & discard overflow // (no busy-wait; this just drains any extra bytes) }
| Line / Block | Concept | Why It Matters |
|---|---|---|
| enum Mode {...} | Enumeration | Named constants improve readability vs magic numbers |
| class SmartLED | OOP / Encapsulation | Pin state stays private; public interface is clean |
| : _pin(pin), _brightness(0) | Member initializer list | Efficient constructor — initializes before body runs |
| void on(uint8_t br = 255) | Default parameters | Flexible API with sensible defaults |
| volatile bool btnEvent | volatile keyword | Tells compiler variable can change outside normal flow (ISR) |
| PROGMEM | Flash storage | Saves precious 2KB SRAM by putting strings in 32KB Flash |
| attachInterrupt(...) | Hardware ISR | Button handled instantly regardless of what loop() is doing |
| static unsigned long lastPress | Static local variable | Persists between calls without being global |
| now - lastTick >= TICK_MS | Non-blocking timing | Loop stays responsive while timing events |
| void setFromPtr(uint8_t *bPtr) | Pointers | Passes address; called with &offset to demo dereferencing |
| void printStatus() + overload | Function overloading | Same function name, different signatures — compiler picks right one |
| uint8_t, int16_t | Explicit-size types | Portable, avoids platform size surprises |
Use F("string") or PROGMEM for string literals. Use uint8_t, int16_t etc. for explicit sizes. Use non-blocking timing with millis(). Declare arrays with const size. Keep ISRs short.
Avoid String objects in loops (heap fragmentation). Avoid delay() in serious programs. Avoid new/delete on R3. Never call delay(), Serial.print() inside an ISR.
In Arduino IDE: Sketch → Export Compiled Binary, then check the output for flash/SRAM usage. Or use freeMemory() from MemoryFree library at runtime.
Use Serial.print(F("msg")) — the F() macro stores the string in Flash just like PROGMEM but with simpler syntax. Saves SRAM on every debug line.
// ✅ Use F() macro to keep string literals in Flash Serial.println(F("This string stays in Flash, not SRAM")); // ✅ Non-blocking blink pattern unsigned long prev = 0; bool state = false; void loop() { if (millis() - prev >= 500) { prev = millis(); state = !state; digitalWrite(13, state); } // other code runs here — not blocked! } // ✅ Debounce button manually bool readButton(int pin) { if (digitalRead(pin) == LOW) { delay(20); // short delay OK in debounce return digitalRead(pin) == LOW; } return false; } // ✅ Map analog reading to voltage float toVolts(int raw) { return raw * (5.0f / 1023.0f); } // ✅ Constrain sensor readings before use int raw = analogRead(A0); int safe = constrain(raw, 0, 1023); int mapped = map(safe, 0, 1023, 0, 255);
After mastering this guide, explore: the Wire library (I²C sensors), SPI library, Servo library, writing your own library (.h + .cpp), and the Arduino Uno R4 which adds USB HID, CAN bus, and a real-time clock out of the box.