8 Arduino coding habits that separate hobby sketches from real products

posted 2 min read

Most Arduino tutorials teach you to blink an LED and stop there. The jump from "it works on my desk" to "it ran for 6 months without crashing" is where most hobby projects die. Here are eight habits that get you across that gap - most of them I learned the hard way.

1. Stop using delay()

delay(1000) halts your entire program for a full second. Nothing else runs - not your buttons, not your sensor reads, not your serial output. Use millis() instead:

unsigned long lastBlink = 0;
const unsigned long BLINK_INTERVAL = 1000;

void loop() {
  if (millis() - lastBlink >= BLINK_INTERVAL) {
    lastBlink = millis();
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
  }
  // your other code keeps running here
}

Once you internalize this pattern, every project gets better.

2. Avoid the String class on AVR boards

String fragments the tiny heap on Uno/Nano/Mega. Your sketch runs fine for an hour, then crashes mysteriously at 3 AM. Use fixed-size char buffers:

char buf[32];
snprintf(buf, sizeof(buf), "Temp: %d C", temperature);
Serial.println(buf);

On ESP32 with megabytes of RAM, String is fine. On a 2KB AVR, it's a timebomb.

3. Wrap string literals in F()

Every Serial.println("Hello world") copies that string into precious RAM at boot. The F() macro keeps it in flash:

Serial.println(F("System ready"));  // costs 0 bytes of RAM

On a 2KB Uno, this single change can free hundreds of bytes.

4. Be explicit about integer sizes

int is 16 bits on AVR and 32 bits on ESP32/SAMD. Code that works on an Uno silently breaks on an ESP32 (or vice versa) because of overflow. Use <stdint.h> types:

uint8_t  pin = 7;
uint16_t adcValue;
uint32_t timestamp;
int16_t  signedTemp;

Your future self porting between boards will thank you.

5. Use INPUT_PULLUP instead of external resistors

You almost never need a physical pull-up resistor for a button. The AVR/ESP32 has them built in:

pinMode(BUTTON_PIN, INPUT_PULLUP);
// button pressed = LOW, released = HIGH

Less wiring, fewer parts, fewer things to debug.

6. Keep ISRs tiny and mark shared variables volatile

Interrupt service routines should set a flag and exit. Do the real work in loop():

volatile bool buttonPressed = false;

void IRAM_ATTR onButton() {   // IRAM_ATTR for ESP32; omit on AVR
  buttonPressed = true;
}

void loop() {
  if (buttonPressed) {
    buttonPressed = false;
    handleButton();
  }
}

Calling Serial.print() inside an ISR is a common rookie mistake - it can deadlock the whole board.

7. Model state machines explicitly

If your project has modes - idle, recording, transmitting, error - don't nest five if statements. Use an enum:

enum class State { Idle, Recording, Transmitting, Error };
State state = State::Idle;

void loop() {
  switch (state) {
    case State::Idle:         handleIdle(); break;
    case State::Recording:    handleRecording(); break;
    case State::Transmitting: handleTransmitting(); break;
    case State::Error:        handleError(); break;
  }
}

This one habit makes debugging an order of magnitude easier.

8. Enable the watchdog for anything deployed

A device sitting in your garage with no debugger needs to recover from its own bugs. On AVR:

#include <avr/wdt.h>
void setup() { wdt_enable(WDTO_8S); }
void loop()  { wdt_reset(); /* your code */ }

If loop() hangs for more than 8 seconds, the chip reboots itself. Beats driving across town to power-cycle.

A bonus: setup() and loop() are just main()

The Arduino IDE hides it, but underneath it's plain C++. You can - and should - define classes, split code across .h/.cpp files, and use namespaces. The .ino file is just convention.


What's the Arduino habit you wish you'd picked up earlier? Drop it in the comments - I'm curious which of these resonate and which ones people disagree with.

More Posts

Tuesday Coding Tip 06 - Explicit template instantiation

Jakub Neruda - Apr 7

Tuesday Coding Tip 02 - Template with type-specific API

Jakub Neruda - Mar 10

5 Web Dev Pitfalls That Are Silently Killing Your Projects (With Real Fixes)

Dharanidharan - Mar 3

Tuesday Coding Tip 08 — Explicit template specialization

Jakub Neruda - Apr 21

Tuesday Coding Tip 05 - Object initialization in C++

Jakub Neruda - Mar 31
chevron_left

Related Jobs

View all jobs →

Commenters (This Week)

1 comment
1 comment
1 comment

Contribute meaningful comments to climb the leaderboard and earn badges!