8 Arduino coding habits that separate hobby sketches from real products

calendar_todayschedule3 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.


If you want to go deeper on the jump from hobby projects to real embedded products, that's what we write about over at Make-it.ai. Tools for hardware design, project breakdowns, and lessons from makers actually shipping things in the wild.

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.

3 Comments

2 votes
0
1
🔥 Join developers growing publicly
Share your knowledge, build in public, and grow your developer presence with a global community.

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
181 Points5 Badges
3Posts
1Comments
1Connections
Co-founder of Make-it (make-it.ai). Building the Canva for smart physical devices. Turning ideas int... Show more

Related Jobs

View all jobs →

Commenters (This Week)

1 comment
1 comment
1 comment

Contribute meaningful comments to climb the leaderboard and earn badges!