Writing Robust Sketches
Good Arduino code is predictable under constrained resources. The single most impactful habit is avoiding blocking calls like delay() in favor of non-blocking timing with millis(), so your loop() can service multiple tasks — reading a sensor, blinking an LED, and checking a button — without freezing. A blocked loop means missed button presses and stalled communication. Structure your sketch so loop() runs quickly and repeatedly, using state variables and timestamps rather than long delays that hold the processor hostage.
Cricket analogy: Using millis() instead of delay() is like a wicketkeeper staying alert every ball rather than napping between overs — a blocking delay is the keeper who looks away and misses the stumping chance.
// Non-blocking blink and sensor read using millis()
const uint8_t LED_PIN = 13;
const unsigned long BLINK_INTERVAL = 500; // ms
unsigned long lastBlink = 0;
bool ledState = false;
void setup() {
pinMode(LED_PIN, OUTPUT);
Serial.begin(9600);
}
void loop() {
unsigned long now = millis();
if (now - lastBlink >= BLINK_INTERVAL) {
lastBlink = now; // store timestamp first
ledState = !ledState;
digitalWrite(LED_PIN, ledState);
}
// loop() stays free to do other work here, e.g. read a button
}Memory and Type Discipline
On a 2 KB-SRAM board, memory hygiene is non-negotiable. Store constant strings in flash with the F() macro — Serial.println(F("Ready")) keeps the literal out of SRAM. Prefer fixed char buffers over the dynamic String class, which fragments the tiny heap and can cause mysterious crashes. Use the smallest correct integer type: uint8_t for a pin, uint16_t for an analog reading, and add the const or PROGMEM qualifier for lookup tables. These choices are cumulative; each one buys headroom that keeps a long-running sketch stable.
Cricket analogy: Using the F() macro is like rotating strike to keep the scoreboard ticking without risk — small, disciplined singles (byte savings) add up to a match-winning total, unlike a reckless String that gets you out.
Wrap constant string literals in the F() macro whenever you print them: Serial.println(F("Sensor error")). Without F(), every literal is copied into SRAM at startup; with it, the string stays in flash and is streamed directly, often reclaiming hundreds of precious bytes.
Wiring, Power, and Debugging Discipline
Hardware habits prevent the bugs software cannot fix. Always share a common ground between the Arduino and external circuits, or signals float unpredictably. Use INPUT_PULLUP for buttons instead of leaving pins floating, and add a decoupling capacitor near power-hungry modules. Never power servos or motor drivers from the Arduino's 5V regulator alone — a stalled motor can draw enough current to brown-out and reset the board; use a separate supply and tie grounds together. For debugging, print with a consistent Serial baud, guard verbose output behind a DEBUG flag, and confirm the correct board and port are selected before blaming your code.
Cricket analogy: Sharing a common ground is like the batters calling loudly and running to the same end — without that shared reference, a mix-up causes a run-out just as a floating ground causes garbage signals.
Do not drive motors or servos directly from the Arduino's onboard 5V pin under load. A stalled or starting motor can spike current well beyond the regulator's limit, causing brown-out resets or permanent damage. Use a dedicated external supply, drive motors through a transistor or motor-driver IC, and always connect the two grounds together.
- Replace delay() with millis()-based timing so loop() can service multiple tasks without freezing.
- Store string literals in flash with the F() macro to conserve SRAM.
- Prefer fixed char buffers over the String class to avoid heap fragmentation and crashes.
- Use the smallest correct integer types and const/PROGMEM for lookup tables.
- Share a common ground and use INPUT_PULLUP to avoid floating, noisy signals.
- Power motors/servos from a separate supply to prevent brown-out resets.
- Gate verbose Serial output behind a DEBUG flag and verify board/port before debugging code.
Practice what you learned
1. Why is millis()-based timing preferred over delay() in most sketches?
2. What does wrapping a literal in the F() macro accomplish?
3. Why is the String class discouraged on small AVR boards?
4. What is the correct way to power a servo that stalls under load?
5. What does INPUT_PULLUP do for a button pin?
Was this page helpful?
You May Also Like
Choosing the Right Arduino Board
How to pick the correct Arduino board for a project by matching microcontroller, I/O count, memory, connectivity, and voltage to your requirements.
Building a Temperature Monitor Project
A complete walkthrough of building an Arduino temperature monitor: wiring a sensor, reading it in code, adding thresholds, and displaying or logging results.
Arduino Quick Reference
A condensed reference of the essential Arduino functions, data types, pin modes, and communication calls for fast lookup while coding.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics