Skip to content

Embedded Thinking Cheatsheet

A one-page reference for embedded systems concepts. Keep this open during labs.


1. Execution Models

Understanding how your code runs is fundamental to embedded engineering.

Model Description When to Use Limitation
Polling (Round-Robin) Main loop checks everything sequentially Simple systems, few tasks Slow response, wastes CPU
Event-Driven Code runs in response to events Medium complexity Still single-threaded
Interrupt-Driven Hardware triggers immediate response Time-critical events ISR must be short
State Machine Explicit states + transitions Complex behavior More code structure
RTOS Multiple tasks, scheduler decides Many concurrent tasks Overhead, complexity
COMPLEXITY SPECTRUM:
Polling ──▶ Events ──▶ Interrupts ──▶ FSM ──▶ RTOS
 simple                                       complex
 slow response                          fast response
 easy to debug                       harder to debug

2. Time Vocabulary

These terms appear constantly in embedded systems. Know them precisely.

Term Definition Typical Range Example
Latency Time from event to response µs to ms Button press to LED on
Jitter Variation in timing µs to ms PWM period varying ±5µs
Deadline Maximum allowed latency Defined by application Motor update every 10ms
Throughput Events processed per second Hz or events/sec 1000 sensor readings/sec
Period Time between repetitions ms to s Control loop runs every 20ms
Duty Cycle ON time / total period 0-100% 50% PWM = half power

Time Budget Example

Main loop iteration: 20ms deadline
├── Read sensors:      5ms
├── Process data:      3ms
├── Update motors:     2ms
├── Update display:   8ms
└── Margin:            2ms  ← ALWAYS leave margin!
                      ────
                      20ms

3. The Golden Rules

ISR Rules (Interrupt Service Routines)

  1. Keep it SHORT - Set a flag, update a counter, return
  2. No blocking - Never use sleep(), delay(), or wait loops
  3. No I/O - Never use print(), UART, or file operations
  4. Volatile variables - Shared data must be marked volatile
  5. Atomic access - Multi-byte variables need protection
# GOOD ISR pattern
button_pressed = False  # Should be volatile in C

def button_isr(pin):
    global button_pressed
    button_pressed = True  # Just set flag, return immediately

# Main loop handles the work
while True:
    if button_pressed:
        button_pressed = False
        handle_button()  # Complex logic here, not in ISR

Measurement Rules

  1. Measure FIRST - Before tuning, understand the system
  2. Multiple samples - One reading is anecdote, 100 is data
  3. Quantify uncertainty - "25 ± 1.5 cm" not just "25 cm"
  4. Log everything - Data explains behavior better than observation
  5. Validate claims - "It works" needs evidence

Design Rules

  1. Non-blocking - Never sleep() in the main loop if you can avoid it
  2. State machines - Make behavior explicit and testable
  3. Timing budgets - Know how long each operation takes
  4. Fail safely - What happens when sensors fail?
  5. Document assumptions - Future you will thank present you

4. Shared Resources

Embedded systems have limited resources. Know what's shared.

Resource Shared By Danger Solution
CPU Main loop, ISRs, (tasks) Starvation, jitter Time budgets, priorities
Memory (RAM) All code, stack, heap Overflow, corruption Static allocation, monitoring
Peripherals Multiple code paths Race conditions Locks, careful design
I/O Pins Hardware, software Conflicts Clear ownership
Communication Multiple messages Corruption, loss Protocols, error checking

5. Common Patterns

Pattern: Debounce

# Problem: Button bounces cause multiple triggers
# Solution: Ignore changes for debounce period

last_press_time = 0
DEBOUNCE_MS = 50

def button_handler():
    global last_press_time
    now = time.ticks_ms()
    if time.ticks_diff(now, last_press_time) > DEBOUNCE_MS:
        last_press_time = now
        # Handle the press

Pattern: Non-Blocking Timing

# Problem: sleep() blocks everything
# Solution: Check elapsed time

last_update = time.ticks_ms()
UPDATE_INTERVAL = 100  # ms

while True:
    now = time.ticks_ms()
    if time.ticks_diff(now, last_update) >= UPDATE_INTERVAL:
        last_update = now
        do_periodic_task()

    # Other tasks can run here!
    do_other_stuff()

Pattern: State Machine

# States
STATE_IDLE = 0
STATE_MOVING = 1
STATE_STOPPED = 2

state = STATE_IDLE

while True:
    if state == STATE_IDLE:
        if start_button_pressed():
            start_motors()
            state = STATE_MOVING

    elif state == STATE_MOVING:
        if obstacle_detected():
            stop_motors()
            state = STATE_STOPPED
        elif goal_reached():
            stop_motors()
            state = STATE_IDLE

    elif state == STATE_STOPPED:
        if obstacle_cleared():
            start_motors()
            state = STATE_MOVING

6. Quick Reference: MicroPython

Timing

import time

time.ticks_ms()      # Current time in ms
time.ticks_us()      # Current time in µs
time.ticks_diff(a,b) # Difference (handles overflow)
time.sleep(1)        # Sleep 1 second (BLOCKING!)
time.sleep_ms(100)   # Sleep 100ms (BLOCKING!)

GPIO

from machine import Pin

# Output (onboard LED)
led = Pin("LED", Pin.OUT)
led.value(1)  # HIGH
led.value(0)  # LOW
led.toggle()

# Input with pull-up
button = Pin(14, Pin.IN, Pin.PULL_UP)
if button.value() == 0:  # Pressed (active low)
    pass

# Interrupt
button.irq(trigger=Pin.IRQ_FALLING, handler=callback)

PWM

from machine import Pin, PWM

# PWM on buzzer (GPIO 15) - frequency controls pitch
buzzer = PWM(Pin(15))
buzzer.freq(440)     # 440 Hz = A4 note
buzzer.duty_u16(32768)  # 50% duty for clear tone
buzzer.duty_u16(0)   # Silence

ADC

from machine import ADC

adc = ADC(26)  # GPIO26 = ADC0
raw = adc.read_u16()  # 0-65535
voltage = raw * 3.3 / 65535

7. Debugging Checklist

When something doesn't work:

  • [ ] Power - Is the device powered? Battery charged?
  • [ ] Connections - Wires connected? Correct pins?
  • [ ] Code uploaded - Latest version on device?
  • [ ] Print statements - Add them to trace execution
  • [ ] Timing - Is something blocking? Check with timestamps
  • [ ] Scope/Analyzer - Look at actual signals if available
  • [ ] Simplify - Remove code until it works, add back incrementally

8. The Engineering Mindset

Before ES101:  "It works!"

After ES101:   "It works, and here's the data that proves it,
                explains why, and predicts when it won't."

Questions to Always Ask

  1. How do I know it works? (Not "I tested it once")
  2. Under what conditions? (Temperature, battery, surface...)
  3. What's the uncertainty? (±X, confidence interval)
  4. What could fail? (Edge cases, environmental factors)
  5. How would I detect failure? (Monitoring, validation)

Cross-Reference

  • [[Reference/extras/mental-models-map|Mental Models Map]]
  • [[Tutorials/robot-unboxing|Tutorial: Robot Unboxing]]
  • [[Tutorials/hardware-abstraction|Hardware Abstraction tutorial]]