Latest update · Jul 3 Beginner Projects — 5 Arduino starter builds with full parts, wiring & code
Palden

Hi, I'm Palden. I started engineering only two years ago, and since then I've won two hackathons — including one of the largest hardware hackathons in the world. I've served as the electrical lead for my school's Mars Rover club, worked as a machine-learning research assistant in brain-computer interfaces, and grown a following of over 300,000 in the past year.

How did I do it? Honestly, I've been in catch-up mode for the past two years. I started late, and I got tired of getting mogged all the time — whether in school by my cracked friends, or online, where every time I opened LinkedIn I'd see some MIT engineer building something insane or landing an internship at SpaceX.

A lot of people look at these cracked people and think, "They probably grew up in a STEM household," or "They're lucky they started early." I looked at it differently. I thought, "Okay, they're smart, and they have the advantage of starting early. But I have AI on my side. If I work hard enough, I can use AI to shorten my learning curve and eventually catch up."

So for the past two years I've taken only one real weekend off, and that was to celebrate my cousin graduating. Most of my weekend nights went into studying, building projects, or growing my engineering pages — which, if you've followed me for a while, you've probably seen on my Instagram. And BTW, this isn't a flex — it's just what I felt I had to do to catch up.

Anyway, now I feel like I've somewhat caught up — or at least I'm not as far behind as I used to think. So I built this page to share the resources, lessons, personal thoughts, and open-source projects that helped me level up. My goal is simple: help you shorten your learning curve, avoid some of the mistakes I made, and catch up faster too.

I hope this page helps. And I hope you use engineering to help humanity solve its greatest problems!

Beginner Projects

Simple starter builds — the ones I'd point a complete beginner to first. Work top to bottom: each build adds one new skill, and every guide opens with the full parts list, wiring, and code.

01 Level 1

Arduino Piano

Three buttons, three notes. Each key lights its own LED and plays C, D, or E on a piezo buzzer — inputs and outputs in their simplest, most satisfying form.

digitalRead()INPUT_PULLUPtone()
Build guide
Parts
  • Arduino Uno
  • 3 push buttons
  • 3 LEDs
  • 3 × 220 Ω resistors
  • 1 piezo buzzer
  • Breadboard + 6 male-to-male jumpers
Wiring
  • Buttons → D2 (E), D3 (D), D4 (C) — other leg to GND (internal pull-ups, no resistors needed)
  • LEDs → D5, D6, D7, each through a 220 Ω resistor to GND
  • Piezo buzzer → D8 and GND
piano.ino
const int button1 = 2; // E
const int button2 = 3; // D
const int button3 = 4; // C

const int led1 = 5;
const int led2 = 6;
const int led3 = 7;

const int piezo = 8;

// Correct note frequencies
const int NOTE_C4 = 262;
const int NOTE_D4 = 294;
const int NOTE_E4 = 330;

void setup() {
  pinMode(button1, INPUT_PULLUP);
  pinMode(button2, INPUT_PULLUP);
  pinMode(button3, INPUT_PULLUP);

  pinMode(led1, OUTPUT);
  pinMode(led2, OUTPUT);
  pinMode(led3, OUTPUT);

  // pinMode(piezo, OUTPUT); // not required for tone()
}

void loop() {
  bool b1 = (digitalRead(button1) == LOW);
  bool b2 = (digitalRead(button2) == LOW);
  bool b3 = (digitalRead(button3) == LOW);

  // LEDs follow button presses
  digitalWrite(led1, b1);
  digitalWrite(led2, b2);
  digitalWrite(led3, b3);

  // Priority if multiple buttons pressed at once: 1 > 2 > 3
  if (b1) {
    tone(piezo, NOTE_E4);
  } else if (b2) {
    tone(piezo, NOTE_D4);
  } else if (b3) {
    tone(piezo, NOTE_C4);
  } else {
    noTone(piezo);
  }
}
02 Level 1

Joystick LED Controller

Tilt the stick, steer the light. Four LEDs mark up, down, left and right and follow the joystick — your first step from on/off buttons to reading real analog voltages.

analogRead()thresholdsSerial Monitor
Build guide
Parts
  • Arduino Uno
  • 2-axis analog joystick module
  • 4 LEDs
  • 4 × 220 Ω resistors
  • Breadboard + male-to-male jumpers
Wiring
  • Joystick VCC5V, GNDGND
  • VRxA0, VRyA1
  • LEDs: left D4, right D5, up D6, down D7 — each through 220 Ω to GND

Open the Serial Monitor at 9600 baud to watch the raw X/Y values while you move the stick.

joystick_leds.ino
// Joystick pins
const int joyX = A0;
const int joyY = A1;

// LED pins
const int ledLeft  = 4;
const int ledRight = 5;
const int ledUp    = 6;
const int ledDown  = 7;

void setup() {
  pinMode(ledLeft, OUTPUT);
  pinMode(ledRight, OUTPUT);
  pinMode(ledUp, OUTPUT);
  pinMode(ledDown, OUTPUT);

  Serial.begin(9600);
}

void loop() {
  int x = analogRead(joyX);
  int y = analogRead(joyY);

  // Turn all LEDs off
  digitalWrite(ledLeft, LOW);
  digitalWrite(ledRight, LOW);
  digitalWrite(ledUp, LOW);
  digitalWrite(ledDown, LOW);

  // Horizontal movement
  if (x < 300) {
    digitalWrite(ledLeft, HIGH);
  }
  else if (x > 700) {
    digitalWrite(ledRight, HIGH);
  }

  // Vertical movement
  if (y < 300) {
    digitalWrite(ledDown, HIGH);
  }
  else if (y > 700) {
    digitalWrite(ledUp, HIGH);
  }

  // Debug values (optional)
  Serial.print("X: ");
  Serial.print(x);
  Serial.print("  Y: ");
  Serial.println(y);

  delay(50);
}
03 Level 2

Ultrasonic Radar Scanner

A sonar sweep for your desk. An HC-SR04 rides a servo across 180°, pinging distance at every angle and streaming it over serial to a glowing green radar display built in Processing.

Servo librarypulseIn()serial data
Build guide
Parts
  • Arduino Uno
  • HC-SR04 ultrasonic sensor
  • SG90 micro servo
  • Breadboard + jumper wires
Wiring
  • HC-SR04: VCC5V, GNDGND, TRIGD9, ECHOD10
  • Servo: red → 5V, brown → GND, yellow (signal) → D6

The radar display runs in Processing (free). Set your serial port near the top of radar_display.pdeCOM3-style on Windows, /dev/cu.usbmodem… on Mac.

radar.ino
#include <Servo.h>

Servo radarServo;

// Pins
const int servoPin = 6;
const int trigPin  = 9;
const int echoPin  = 10;

// Sweep settings
int angle = 0;
int stepDir = 1;                 // +1 goes right, -1 goes left
const int minAngle = 0;
const int maxAngle = 180;

const unsigned long servoIntervalMs = 20;  // 20ms = smooth (50 Hz-ish)
unsigned long lastServoMove = 0;

long getDistanceCM() {
  // Trigger pulse
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);

  // Read echo (timeout prevents freezing if no echo)
  unsigned long duration = pulseIn(echoPin, HIGH, 30000UL); // 30ms timeout

  if (duration == 0) return 999; // out of range / no reading

  // Speed of sound ~0.0343 cm/us, divide by 2 for round trip
  return (long)(duration * 0.0343 / 2.0);
}

void setup() {
  Serial.begin(9600);

  radarServo.attach(servoPin);
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);

  radarServo.write(angle);
}

void loop() {
  unsigned long now = millis();

  // Move servo at a steady interval (smooth back-and-forth)
  if (now - lastServoMove >= servoIntervalMs) {
    lastServoMove = now;

    radarServo.write(angle);

    // Take distance reading
    long distance = getDistanceCM();

    // Send to PC: "angle,distance"
    Serial.print(angle);
    Serial.print(",");
    Serial.println(distance);

    // Update angle and bounce at ends
    angle += stepDir;
    if (angle >= maxAngle) {
      angle = maxAngle;
      stepDir = -stepDir;
    } else if (angle <= minAngle) {
      angle = minAngle;
      stepDir = -stepDir;
    }
  }
}
radar_display.pde
import processing.serial.*;
import java.util.ArrayList;

Serial myPort;
String inData = "";

float angleDeg = 0;
float distanceCm = 0;

int maxCm = 150;
float scale = 3.0;

// ----- trail storage -----
class Hit {
  float angle;
  float dist;
  int born;          // millis() when added
  Hit(float a, float d) { angle = a; dist = d; born = millis(); }
}
ArrayList<Hit> hits = new ArrayList<Hit>();

int trailMs = 1500;       // how long hits stay (ms)
int maxHits = 400;        // max stored hits

void setup() {
  size(900, 600);
  smooth();
  pixelDensity(1);

  println(Serial.list());
  // CHANGE THIS to your port: "COM3" on Windows, "/dev/cu.usbmodem..." on Mac
  myPort = new Serial(this, "/dev/cu.usbmodem1101", 9600);
  myPort.bufferUntil('\n');
}

void draw() {
  background(0);

  translate(width/2, height*0.9);
  drawRadarGrid();

  // Draw sweep line
  float theta = radians(angleDeg);
  float rMax = maxCm * scale;

  stroke(0, 255, 0);
  strokeWeight(2);
  line(0, 0, -rMax * cos(theta), -rMax * sin(theta));

  // Draw hit trail (older hits fade out)
  int now = millis();
  for (int i = hits.size() - 1; i >= 0; i--) {
    Hit h = hits.get(i);
    int age = now - h.born;

    if (age > trailMs) {
      hits.remove(i);
      continue;
    }

    float life = 1.0 - (float)age / trailMs;   // 1 -> 0
    float r = min(h.dist, maxCm) * scale;
    float t = radians(h.angle);

    // fade alpha
    stroke(255, 0, 0, 255 * life);
    strokeWeight(6 * life + 1);
    point(-r * cos(t), -r * sin(t));
  }

  // HUD
  resetMatrix();
  fill(0, 255, 0);
  textSize(18);
  text("Angle: " + nf(angleDeg, 0, 0) + "\u00b0", 20, 30);
  text("Distance: " + nf(distanceCm, 0, 1) + " cm", 20, 55);
}

void drawRadarGrid() {
  stroke(0, 255, 0);
  strokeWeight(1);
  noFill();

  for (int cm = 25; cm <= maxCm; cm += 25) {
    float rr = cm * scale;
    arc(0, 0, rr*2, rr*2, PI, TWO_PI);
  }

  for (int deg = 0; deg <= 180; deg += 15) {
    float t = radians(deg);
    float x = -(maxCm * scale) * cos(t);
    float y = -(maxCm * scale) * sin(t);
    line(0, 0, x, y);
  }

  line(-maxCm*scale, 0, maxCm*scale, 0);
  float outer = maxCm * scale;
  arc(0, 0, outer*2, outer*2, PI, TWO_PI);
}

void serialEvent(Serial myPort) {
  inData = myPort.readStringUntil('\n');
  if (inData == null) return;

  inData = trim(inData);
  String[] parts = split(inData, ',');

  if (parts.length == 2) {
    try {
      angleDeg = float(parts[0]);
      distanceCm = float(parts[1]);

      // Add to trail only if valid
      if (distanceCm > 0 && distanceCm < maxCm) {
        hits.add(new Hit(angleDeg, distanceCm));
        if (hits.size() > maxHits) hits.remove(0);
      }
    } catch (Exception e) {
      // ignore bad lines
    }
  }
}
04 Level 3

Reaction Time Tester

How fast are you, in milliseconds? After a random wait one of three LEDs fires — hit the matching button and the LCD prints your reaction time. Press early and it calls you out.

LiquidCrystalmillis()random()
Build guide
Parts
  • Arduino Uno
  • 16×2 LCD
  • 10 kΩ potentiometer (contrast)
  • 4 push buttons — 1 start + 3 reaction
  • 3 LEDs
  • 4 × 220 Ω resistors (3 LEDs + LCD backlight)
  • Breadboard + male-to-male jumpers
Wiring
  • LCD: RSD7, ED8, D4–D7D9–D12; VSS, RW, KGND; VDD5V; A5V via 220 Ω
  • LCD VO → potentiometer middle pin (outer pins to 5V and GND) for contrast
  • LEDs → D2, D3, D4, each through 220 Ω to GND
  • Buttons: start → A0, reaction → A1–A3 — other leg to GND (internal pull-ups)

Install the LiquidCrystal library from the Arduino IDE’s Library Manager before uploading.

reaction_test.ino
#include <LiquidCrystal.h>

LiquidCrystal lcd(7, 8, 9, 10, 11, 12);

// LED pins
const int leds[3] = {2, 3, 4};

// Button pins
const int startButton = A0;
const int buttons[3] = {A1, A2, A3};

unsigned long startTime;
int activeLED = -1;

void setup() {
  lcd.begin(16, 2);

  // LEDs
  for (int i = 0; i < 3; i++) {
    pinMode(leds[i], OUTPUT);
    digitalWrite(leds[i], LOW);
  }

  // Buttons (internal pullups)
  pinMode(startButton, INPUT_PULLUP);
  for (int i = 0; i < 3; i++) {
    pinMode(buttons[i], INPUT_PULLUP);
  }

  showStartScreen();
}

void loop() {
  // Wait for START button
  if (digitalRead(startButton) == LOW) {
    delay(300); // debounce
    runReactionTest();
    showStartScreen();
  }
}

void showStartScreen() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Reaction Test");
  lcd.setCursor(0, 1);
  lcd.print("Press START");
}

void runReactionTest() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Get Ready...");

  delay(random(2000, 5000)); // random wait

  activeLED = random(0, 3);
  digitalWrite(leds[activeLED], HIGH);

  startTime = millis();

  while (true) {
    // Check reaction buttons
    for (int i = 0; i < 3; i++) {
      if (digitalRead(buttons[i]) == LOW) {
        unsigned long reactionTime = millis() - startTime;
        digitalWrite(leds[activeLED], LOW);

        lcd.clear();

        if (i == activeLED) {
          lcd.setCursor(0, 0);
          lcd.print("Reaction Time:");
          lcd.setCursor(0, 1);
          lcd.print(reactionTime);
          lcd.print(" ms");
        } else {
          lcd.setCursor(0, 0);
          lcd.print("Wrong Button!");
          lcd.setCursor(0, 1);
          lcd.print("Try Again");
        }

        delay(3000);
        return;
      }
    }

    // Prevent early press before LED
    if (digitalRead(startButton) == LOW) {
      digitalWrite(leds[activeLED], LOW);
      lcd.clear();
      lcd.print("Too Early!");
      delay(2000);
      return;
    }
  }
}
05 Level 3

Pomodoro Timer

A desktop focus timer: 25-minute work blocks, 5-minute breaks, start / pause / reset buttons and a buzzer between phases. Debouncing, millis() timing and a real state machine — the fundamentals behind every bigger build.

state machinedebouncingmillis()
Build guide
Parts
  • Arduino Uno
  • 16×2 LCD
  • 10 kΩ potentiometer (contrast)
  • 3 push buttons
  • 1 buzzer
  • 1 × 220 Ω resistor
  • Breadboard + jumper wires
Wiring
  • LCD: RSD12, ED11, D4–D7D5, D4, D3, D2; VSS, RW, KGND; VDD5V; A5V via 220 Ω
  • LCD VO → potentiometer middle pin for contrast
  • Buttons: start → D8, pause → D9, reset → D10 — other leg to GND (internal pull-ups)
  • Buzzer → D6 and GND

Uses the LiquidCrystal library — install it from the Library Manager first.

pomodoro.ino
#include <LiquidCrystal.h>

// LCD: RS, E, D4, D5, D6, D7
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

// Buttons
const int BTN_PLAY  = 8;   // Start
const int BTN_PAUSE = 9;   // Pause
const int BTN_RESET = 10;  // Reset

// Optional buzzer
const int BUZZ_PIN = 6;

// Pomodoro times (seconds)
const long FOCUS_TIME_SEC = 25L * 60L;  // 25 minutes
const long BREAK_TIME_SEC = 5L  * 60L;  // 5 minutes

// State
enum Phase { FOCUS, BREAK };
Phase phase = FOCUS;

bool running = false;
long remainingSec = FOCUS_TIME_SEC;
unsigned long lastTickMs = 0;

// Debounce struct
struct Btn {
  int pin;
  bool lastStable;
  bool lastRead;
  unsigned long lastChangeMs;
};

Btn bPlay  {BTN_PLAY,  HIGH, HIGH, 0};
Btn bPause {BTN_PAUSE, HIGH, HIGH, 0};
Btn bReset {BTN_RESET, HIGH, HIGH, 0};

const unsigned long DEBOUNCE_MS = 35;

bool pressed(Btn &b) {
  bool r = digitalRead(b.pin);
  unsigned long now = millis();

  if (r != b.lastRead) {
    b.lastRead = r;
    b.lastChangeMs = now;
  }

  if ((now - b.lastChangeMs) > DEBOUNCE_MS && b.lastStable != r) {
    b.lastStable = r;
    if (b.lastStable == LOW) return true; // pressed
  }
  return false;
}

void beep(int times) {
  for (int i = 0; i < times; i++) {
    tone(BUZZ_PIN, 2000);
    delay(100);
    noTone(BUZZ_PIN);
    delay(100);
  }
}

void setPhase(Phase p) {
  phase = p;
  running = false;
  remainingSec = (p == FOCUS) ? FOCUS_TIME_SEC : BREAK_TIME_SEC;
  lastTickMs = millis();
}

void drawLCD() {
  // Top line: FOCUS or BREAK
  lcd.setCursor(0, 0);
  lcd.print("                ");
  lcd.setCursor(0, 0);
  lcd.print(phase == FOCUS ? "FOCUS" : "BREAK");

  // Bottom line: MM:SS
  int mm = remainingSec / 60;
  int ss = remainingSec % 60;

  lcd.setCursor(0, 1);
  lcd.print("                ");
  lcd.setCursor(0, 1);

  if (mm < 10) lcd.print("0");
  lcd.print(mm);
  lcd.print(":");
  if (ss < 10) lcd.print("0");
  lcd.print(ss);
}

void resetPomodoro() {
  setPhase(FOCUS);
}

void setup() {
  pinMode(BTN_PLAY,  INPUT_PULLUP);
  pinMode(BTN_PAUSE, INPUT_PULLUP);
  pinMode(BTN_RESET, INPUT_PULLUP);

  pinMode(BUZZ_PIN, OUTPUT);
  noTone(BUZZ_PIN);

  lcd.begin(16, 2);
  lcd.clear();

  resetPomodoro();
  drawLCD();
}

void loop() {
  unsigned long now = millis();

  // Buttons
  if (pressed(bPlay)) {
    if (!running) {
      running = true;
      lastTickMs = now;
      beep(1);
    }
  }

  if (pressed(bPause)) {
    if (running) {
      running = false;
      beep(1);
    }
  }

  if (pressed(bReset)) {
    resetPomodoro();
    beep(2);
  }

  // Countdown
  if (running && (now - lastTickMs) >= 1000) {
    lastTickMs += 1000;

    if (remainingSec > 0) remainingSec--;

    if (remainingSec == 0) {
      beep(3);
      if (phase == FOCUS) setPhase(BREAK);
      else setPhase(FOCUS);
    }
  }

  // Refresh LCD
  static unsigned long lastDraw = 0;
  if (now - lastDraw > 200) {
    lastDraw = now;
    drawLCD();
  }
}
Stuck on a build? DM me on Instagram @engineeringwithpalden — end your message with “arduino” so it doesn’t get lost in my inbox.

Current Projects

What I'm building right now — and exactly where each one is at.

Self-balancing robot — CAD render Click to zoom
Project 01In progress

Self-Balancing PID Robot

A two-wheel robot that stays upright on its own. An IMU measures how far it's tipping and a PID loop drives the motors to catch the fall — hundreds of corrections a second.

Phase III of III · tuning2 of 3 phases done
  1. CAD & chassis — full mechanical design finished and the frame built.
  2. Electronics wired — motors, driver, IMU, and microcontroller all connected.
  3. Tune the PID loop — dialing in the gains so it balances and rejects pushes.
Right now

Blew one of the motors mid-tuning — waiting on a replacement before I can finish dialing in the PID loop.

Replacement motor~ Jun 25
Target finishJun 29
Want to build one? Everything I have so far — CAD, firmware, and wiring — is on GitHub.
8-bit breadboard computer — modules labeled Click to zoom
Project 02In progress

8-Bit Computer

A working computer built from scratch on breadboards, one logic chip at a time — clock, registers, ALU, RAM, and control logic, following the classic SAP-1 architecture.

Phase III of VI2 of 6 phases done
  1. Research & plan the build — study the SAP-1 architecture, source the parts, and map out every module.
  2. Clock module — adjustable speed with single-step for debugging.
  3. Registers & ALU — A and B registers on the bus, plus add and subtract.
  4. RAM & program counter — memory with an address register, and instruction sequencing.
  5. Output & control logic — the 7-segment display, instruction register, and microcode.
  6. Program it — write machine-code programs, load them into RAM, and run them.

Latest · June 2026 — Registers moving data cleanly across the bus; building the ALU next.

Want to build one? All my schematics, build notes, and the parts list are on GitHub.
Self-rising RL robot — servo-driven build Click to zoom
Project 03In progress

Simple RL Robot

A small servo robot that learns to move on its own. I train a reinforcement-learning policy in simulation, then deploy it straight to the hardware — no hand-coded gait.

Phase I of Vjust getting started
  1. Research & learning RL — getting the fundamentals of reinforcement learning down before building.
  2. Build the robot — servos, frame, and electronics assembled.
  3. Match it in sim — recreate the robot in a MuJoCo simulation.
  4. Train the policy — run PPO until it moves reliably in simulation.
  5. Sim-to-real — deploy the trained policy onto the physical robot.

Latest · June 2026 — Learning the fundamentals of reinforcement learning before the build.

Repo coming soon. For now, this build draws inspiration from homemadegarbage's SelfRisingRobot project.

Extra

A little more of the thinking behind the building — and the things that help me do it.

Writings

Thoughts, lessons, and deeper breakdowns from the things I build — the stuff that doesn't fit in a 30-second reel.

Coming soon

Personal Goals

What I'm working toward — the short-term targets and the bigger picture I'm chasing in engineering and beyond.

Coming soon

Resources

The tools, parts, and references I keep coming back to — collected here so you can skip the searching I didn't.

View resources

Contact

The best way to reach me: