DEV Community

張旭豐
張旭豐

Posted on

From Feeling to Firmware: Build Your First Haptic Feedback Device (10 Minutes)

From Feeling to Firmware: Build Your First Haptic Feedback Device (10 Minutes)

Subtitle: Stop asking "what motor should I use." Start with "what do I want this to feel like."


The Moment Your Device Misses the Point

You built it. The sensor detects. The motor spins. The code compiles clean.

But when you hold it — the vibration is technically there, but it's emotionally absent.

You didn't build the wrong thing. You built the right parts in the wrong order.

Most tutorials start with components. You started with components too. That's not the failure — the failure is that you never stepped back and asked:

"Before I pick a motor, what feeling am I actually trying to create?"

The designers of the products you use every day — your phone, your game controller, your smartwatch — all asked that question first. That's why their devices feel intentional. Yours felt like a parts list that happened to run code.

Today we're going to do it the other way around.


PS5 DualSense controller with haptic feedback — you already know what intentional haptic feedback feels like from your hands every day

Fig 1: You already know what intentional haptic feedback feels like — the question is how to design it deliberately


What We're Building: "The Noticeable Nudge"

Not a vibration. Not an alert. Not a rumble.

A tactile acknowledgment — the digital equivalent of a brief nod from someone who noticed you walk into the room.

This is not...

  • no an alarm
  • no a gaming rumble
  • no a buzzy confirmation
  • no a long pulse

This is...

  • yes a brief tactile acknowledgment
  • yes a "yes, I noticed you" in physical form
  • yes a socially readable moment — like a tap on the shoulder

You feel it in your hand, it registers immediately, and it's gone before it becomes annoying. That's our haptic target. Everything that follows — the spec, the motor, the circuit, the code — exists to serve this one sentence.


What you'll actually learn:

  • How to define a feeling as a measurable engineering spec
  • How to reverse-engineer component choices from that spec
  • How to wire a driver circuit that preserves the feeling you designed
  • How to write code that doesn't kill the crispness you worked so hard to create

Time: 10-15 minutes


Step 1: Define the Feeling Before You Touch Anything

Close the component catalog. Open a blank sheet of paper.

Write this sentence — and don't let anyone tell you it's too vague:

"I want someone to feel a short, warm tap. Like a finger saying 'I notice you.' Not aggressive. Not startling. A crisp, friendly nudge."

That's your haptic target. Every decision that follows is a test: does this serve that sentence, or am I drifting?

If a decision makes the feeling fuzzy — you stop. If it sharpens the feeling — you keep going.


Step 2: Translate the Feeling Into Numbers

Feelings can't be selected from a menu. Specs can be selected from a menu.

Ask yourself: "What are the physical dimensions of 'short, warm tap'?"

Haptic specification table: Duration, Intensity, Profile, Frequency — each feeling word translated into measurable engineering specs

Fig 2: The Feeling to Spec Translation. Each feeling word has a measurable engineering equivalent.

These numbers aren't arbitrary. They're the engineering translation of "crisp, friendly nudge."

If your target changes — if you want a heartbeat pulse, or a sustained alert — your numbers change too. But the process is the same: feeling to spec to component.


Step 3: Reverse-Engineer the Component

Now we have a spec. Now we match the component to it.

ERM vs LRA vs Piezo motor comparison — why LRA is the right choice for a crisp, clean tap

Fig 3: ERM vs LRA vs Piezo — each motor type produces a different tactile feel. Only LRA matches our "crisp tap" spec.

Motor Type What It Feels Like Matches Our Spec?
ERM (Eccentric Rotating Motor) Diffuse rumble. Hard to control precisely. no Too coarse for "crisp tap"
LRA (Linear Resonant Actuator) Clean tap, sharp pulse, precise onset yes Matches all four attributes
Piezo Micro-tap at very high frequency no Overkill for this project

Our pick: 12mm LRA module. Compact, 3V-5V compatible, and critically — its linear motion produces the sharp onset and clean cutoff our spec demands. An ERM can never give you that because its motion is rotational, not linear.


FAQ: "What if I want it stronger — just use a bigger motor?"

A bigger ERM motor at full speed doesn't give you stronger. It gives you rougher. Like revving an engine in first gear.

What you actually want is precision. You want exactly the right amount of force, delivered exactly the way you designed it. That's not a motor size problem — it's a control problem. Which is why the spec comes first.


FAQ: "I have ERM parts lying around — can I use them?"

Yes, but the sensation won't match your spec. ERM at full speed is buzzy. You can PWM it down, but the control is coarse. For a "friendly tap," ERM will always feel like a compromise. If your spec says "crisp tap," trust the spec and use LRA.


Step 4: The Circuit — Why Each Component Exists

Here's the complete schematic:

Complete circuit diagram: Arduino Pin 3 to 330 Ohm resistor to 2N2222 NPN transistor base, collector drives 12mm LRA from external 5V supply with 1N4148 flyback diode protection

Fig 4: Complete schematic — every component exists to preserve the "crisp, friendly nudge" feeling


And the pin-by-pin connection:

Arduino Pin 3  ->  330 Ohm resistor  ->  Transistor Base (B)
Arduino GND    ->  Transistor Emitter (E)
5V external    ->  Diode band (anode) / LRA (+)
Transistor Collector (C)  ->  LRA (+)
LRA (-)  ->  GND rail
Enter fullscreen mode Exit fullscreen mode

Why transistor, flyback diode, and external 5V — the feeling-preservation logic behind each component choice

Fig 5: Why each component exists in feeling terms — not just "what it does" but "why it preserves the tap quality over time"


Why This Exact Circuit — In Feeling Terms

"Why the transistor?"

If you wire the LRA directly to an Arduino pin: the Arduino can source about 40mA. The LRA needs 100-200mA to produce a satisfying tap. With direct wiring, the Arduino pin strangles the LRA — you get a weak, mushy pulse that feels nothing like your spec.

The transistor lets the Arduino control a switch, not power the motor directly. The Arduino signal says "on" or "off." The external 5V supply delivers the current. Result: a clean, strong tap that matches your intensity spec.

"Why the flyback diode?"

When you cut power to a motor, its magnetic field collapses and sends a voltage spike back into the circuit. Without the diode, that spike travels back to the transistor — and each spike gradually degrades the transistor's performance. With the diode, the spike is clamped safely back to the 5V rail.

In feeling terms: the diode keeps the tap consistent over time. Without it, after a few hundred taps, the transistor starts to behave unpredictably — and the crisp feeling you designed degrades.

"Why external 5V, not the Arduino's 5V pin?"

The Arduino's onboard regulator can't supply enough stable current for consistent LRA performance. With a phone charger or USB power bank on the external input, the tap stays strong and consistent — every single time.


Breadboard wiring diagram — pin-by-pin physical connection guide

Fig 6: Breadboard wiring — follow the color paths to connect each component


FAQ: The LRA doesn't vibrate

Debug in order:

  1. Is the transistor's emitter connected to Arduino GND? (Shared ground is required — this is the #1 mistake)
  2. Is the LRA's negative terminal on the collector side of the transistor?
  3. Is the external 5V supply connected and providing 1A or more?
  4. Is the diode's band facing toward +5V?

FAQ: The tap feels too weak

Try in order:

  1. Check your LRA voltage rating. If it's 3V, 5V might be too much. If it's 5V-rated, increase from 3V to 5V.
  2. Increase tap duration in code from 80ms to 120ms.
  3. Add a 10uF capacitor across the LRA terminals for sharper onset.

Step 5: The Code — Written for Feeling

const int hapticPin = 3;

void setup() {
  pinMode(hapticPin, OUTPUT);
  digitalWrite(hapticPin, LOW);  // Starts off — no stray vibration
}

void loop() {
  // "The Noticeable Nudge":
  // Short tap -> immediate acknowledgment
  // Then pause -> so it's responsive, not annoying

  tap(100);    // 100ms = "I noticed you"
  delay(600);  // Quiet window before next possible tap
}

// The tap() function is the feeling delivery mechanism.
// Tune duration to sharpen or soften the tap.
void tap(int duration) {
  digitalWrite(hapticPin, HIGH);  // Tap begins — sharp onset
  delay(duration);                 // Hold for this long
  digitalWrite(hapticPin, LOW);   // Tap ends — clean cutoff
  delay(30);                       // <- This is the secret
                                   //    30ms pause before next tap
                                   //    gives the LRA physical time
                                   //    to stop. Without this, you get
                                   //    a double-pulse that feels mushy.
}
Enter fullscreen mode Exit fullscreen mode

The 30ms delay(30) is the feeling guardian.

The LRA is a physical mass on a spring. When you cut power, the mass doesn't stop instantly — it oscillates. That oscillation creates a second, weaker pulse that your hand feels as mushiness.

The 30ms pause gives the mass time to settle before the next signal arrives. Result: each tap is clean, distinct, and intentional. Matches your "sharp attack, fast decay" spec.


Connect it to a touch sensor:

const int hapticPin = 3;
const int touchPin = 2;  // Touch sensor or ESP32 capacitive touch

void setup() {
  pinMode(hapticPin, OUTPUT);
  pinMode(touchPin, INPUT);
}

void loop() {
  if (digitalRead(touchPin) == HIGH) {
    tap(80);     // Confirm the touch
    delay(200);  // Debounce — prevent rapid-fire taps
  }
}

void tap(int duration) {
  digitalWrite(hapticPin, HIGH);
  delay(duration);
  digitalWrite(hapticPin, LOW);
  delay(30);  // Physical settling time
}
Enter fullscreen mode Exit fullscreen mode

Now your device responds to touch with "I noticed you." Every time.


Transistor driver close-up — the precise control point where the feeling gets delivered

Fig 7: Transistor driver zone — where the Arduino's low-current signal controls the LRA's high-current tap


What You Actually Learned

You didn't learn how to wire an LRA motor.

You learned the reverse-engineering process:

Feeling -> Spec -> Component -> Circuit -> Code
    ^                            |
    <-<-<-<-<-<-<-<-<-<-<-<-<-<-<-<-<-<-<-<-<-<-<
         (each step tests the feeling)
Enter fullscreen mode Exit fullscreen mode

The Arduino code, the transistor circuit, the wiring order — these are all in service of your feeling target. Change the feeling target, and every one of those decisions changes too.

That's the skill that transfers to your next project. Not the LRA — the thinking process.


The Question Your Next Project Will Face

You just completed "The Noticeable Nudge" — a case where you knew the feeling you wanted.

Your next project will be harder: you'll start with a vague intention — something like "I want it to feel responsive" or "I want the user to feel in control."

How do you turn vague intention into a precise spec?

That's the question the Project Direction Worksheet is built to answer. It's not a parts list. It's a thinking tool:

  • It walks you through defining your feeling target clearly
  • It checks whether your current component choices actually deliver that feeling
  • It identifies gaps between your vision and your build — before you spend weeks on it

-> Get the Project Direction Worksheet ($6)

This article gave you one case. The worksheet gives you the method to define your next case.


Parts Reference

Not a shopping list — a spec confirmation. Match these to your feeling target.

Component Spec to Match Notes
Arduino Nano (or equivalent) Digital pin output Any board with a 3.3V or 5V logic pin works
12mm LRA module 3V-5V, 200mA or less Linear motion gives you the crisp tap. ERM cannot.
2N2222 NPN transistor Current gain 100 or more Switches the LRA on/off without straining the Arduino pin
1N4148 flyback diode Reverse voltage 75V or more Protects the transistor from voltage spikes
330 Ohm base resistor Limits base current Keeps the Arduino pin safe (40mA max)
External 5V supply 1A or more, stable Phone charger or USB power bank. Not the Arduino's 5V pin.

Tags: #hapticfeedback #Arduino #interactiondesign #LRA #haptics #makermovement #prototype


This is the free prototype guide. The Project Direction Worksheet gives you the method to design your next one.

Top comments (0)