A Force Sensitive Resistor (FSR) is a piezoresistive sensor whose electrical resistance drops as physical pressure increases. When pairing an FSR with Arduino, you are essentially building a variable voltage divider. The microcontroller's 10-bit ADC (Analog-to-Digital Converter) reads the changing voltage and translates it into a 0–1023 integer. While the concept is simple, the non-linear resistance curve and fragile sensor tails make real-world implementation prone to specific wiring and code errors.

This guide targets the Arduino Uno R3 (ATmega328P) running at 5V logic. Below, you will find exact force-to-voltage data, a complete pin mapping, compilable C++ code with built-in error handling, and a diagnostic framework for the most common analog read failures.

FSR Specifications and Force-to-Voltage Data

Unlike load cells, FSRs are highly non-linear. They are excellent for detecting if a surface is pressed and roughly how hard, but they are not precision weighing instruments. The most common hobbyist variants are the Interlink Electronics 402 (0.5" square sensing area) and the Adafruit 166 (1.5" round). Both share a similar resistance curve, ranging from >1MΩ (unpressed) down to ~200Ω (max pressed at ~10kg).

To read this with an Arduino, we use a 10kΩ pulldown resistor. This value is chosen because it sits near the geometric mean of the sensor's active range, maximizing voltage resolution in the 100g to 2kg zone where most human-interaction projects operate.

Table 1: Interlink 402 / Adafruit FSR Force vs. Resistance vs. Voltage (5V VCC, 10kΩ Pulldown)
Applied Force (g) Typical Resistance (Ω) Voltage at Analog Pin (V) Arduino ADC Value (0-1023) State Description
0 (Unpressed) > 1,000,000 0.00 - 0.05 0 - 10 Idle / Open Circuit
100 (Light Touch) ~ 100,000 0.45 ~ 92 Threshold Trigger
500 (Firm Press) ~ 10,000 2.50 ~ 512 Mid-Range Active
2,000 (Hard Squeeze) ~ 2,000 4.16 ~ 853 High Force
10,000 (Max Rated) ~ 200 4.90 ~ 1004 Saturation

Note: FSRs have a tolerance of ±25%. If your project requires exact gram measurements, you must use a strain gauge load cell with an HX711 amplifier instead.

Hardware Parts List and Pin Mapping

Before writing code, verify your bench inventory. Using the wrong pulldown resistor value will skew your ADC readings heavily toward the extremes.

  • Microcontroller: Arduino Uno R3 (or Nano v3 / Mega 2560 with 5V logic)
  • Sensor: Interlink 402 FSR or Adafruit Round FSR (1.5")
  • Pulldown Resistor: 10kΩ, 1/4W, 5% tolerance (Brown-Black-Orange-Gold)
  • Wiring: 22 AWG solid core hookup wire, plus 2x alligator clips (highly recommended for FSR tails)
  • Prototyping: 830-tie-point solderless breadboard
Bench Tip: The silver traces on an FSR tail are printed on a fragile polymer substrate. Pushing the tail directly into a breadboard often fails to make contact or cracks the traces. Use alligator clips to bridge the FSR tail to standard jumper wires, and apply Kapton tape over the clip connection for strain relief.
Table 2: FSR to Arduino Uno R3 Pin Mapping
Component Pin / Leg Connects To Notes
FSR Leg 1 (Arbitrary) Arduino 5V Pin FSRs are non-polarized; either leg can be VCC.
FSR Leg 2 (Arbitrary) Breadboard Node X This node forms the center of the voltage divider.
10kΩ Resistor Leg 1 Breadboard Node X Connects to the same node as FSR Leg 2.
10kΩ Resistor Leg 2 Arduino GND Pin Completes the pulldown circuit.
Jumper Wire Breadboard Node X Arduino Pin A0 Carries the divided voltage to the ADC.

Complete Arduino Code with Smoothing and Error Handling

The following C++ code targets the Arduino Uno R3. It implements a moving average filter to smooth out the electrical noise inherent in high-impedance voltage dividers, calculates the approximate force in grams using a logarithmic interpolation, and includes explicit serial error flags for debugging.

/*
 * FSR with Arduino - Smoothed Analog Read & Force Estimation
 * Target Board: Arduino Uno R3 (ATmega328P, 5V, 10-bit ADC)
 * Hardware: Interlink 402 FSR + 10kΩ pulldown on A0
 */

// --- Pin Definitions ---
const int FSR_PIN = A0;

// --- Configuration Constants ---
const int SAMPLE_WINDOW = 10;       // Number of samples for moving average
const int NOISE_THRESHOLD = 15;     // ADC value below which we consider 'unpressed'
const float VCC = 5.0;              // Board logic voltage
const float PULLDOWN_R = 10000.0;   // 10k ohm pulldown resistor

// Smoothing buffer
int readings[SAMPLE_WINDOW];
int readIndex = 0;
long total = 0;

void setup() {
  Serial.begin(115200);
  
  // Initialize ADC pin (optional for analogRead, but good practice)
  pinMode(FSR_PIN, INPUT);
  
  // Clear smoothing buffer
  for (int i = 0; i < SAMPLE_WINDOW; i++) {
    readings[i] = 0;
  }
  
  Serial.println("FSR Sensor Initialized. Press to measure force.");
}

void loop() {
  // 1. Read raw ADC and apply moving average filter
  total = total - readings[readIndex];
  readings[readIndex] = analogRead(FSR_PIN);
  total = total + readings[readIndex];
  readIndex = (readIndex + 1) % SAMPLE_WINDOW;
  
  int smoothedADC = total / SAMPLE_WINDOW;
  
  // 2. Error Handling & Edge Case Detection
  if (smoothedADC >= 1020) {
    Serial.println("ERROR: FSR_SATURATION_1023 - Check pulldown resistor wiring or sensor short.");
    delay(500);
    return;
  }
  
  if (smoothedADC <= NOISE_THRESHOLD) {
    // Unpressed state, avoid math errors with log(0)
    Serial.println("State: Unpressed | Force: 0g | ADC: " + String(smoothedADC));
    delay(100);
    return;
  }
  
  // 3. Calculate Voltage and FSR Resistance
  float voltage = (smoothedADC * VCC) / 1023.0;
  float fsrResistance = (PULLDOWN_R * (VCC - voltage)) / voltage;
  
  // 4. Estimate Force (Logarithmic approximation for Interlink 402)
  // Formula derived from datasheet log-log plot: Force(g) ≈ 10^((log10(R) - 5.5) / -0.8)
  float forceGrams = 0;
  if (fsrResistance > 0) {
    float logR = log10(fsrResistance);
    float logF = (logR - 5.8) / -0.85; // Tuned constants for 402 variant
    forceGrams = pow(10, logF);
  }
  
  // 5. Output Data
  Serial.print("ADC: "); Serial.print(smoothedADC);
  Serial.print(" | V: "); Serial.print(voltage, 2);
  Serial.print(" | R: "); Serial.print(fsrResistance, 0); Serial.print("Ω");
  Serial.print(" | Est. Force: "); Serial.print(forceGrams, 1); Serial.println("g");
  
  delay(50); // 20Hz update rate
}

Debugging: Readings Stuck at 1023 or 0

When wiring an FSR with Arduino, the most common failure mode is a misconfigured voltage divider or a broken sensor tail. If your Serial Monitor is outputting one of the exact error strings below, follow the ranked diagnostic steps.

Symptom 1: Serial outputs "ERROR: FSR_SATURATION_1023"

This means the analog pin is seeing a full 5V (or very close to it) regardless of how hard you press the sensor. The ADC is maxed out.

  1. Check the Pulldown Resistor Connection: The 10kΩ resistor must be connected between the analog pin (Node X) and GND. If it is accidentally connected to 5V, or if the GND wire is loose, the pin floats high.
  2. Verify Voltage Divider Order: The FSR must be on the "top" (connected to 5V), and the 10kΩ resistor on the "bottom" (connected to GND). If you swap them, pressing the FSR drops the voltage to 0V instead of raising it to 5V, and an unpressed FSR reads 1023.
  3. Test for an Internal Short: Disconnect the FSR. Use a multimeter in continuity mode across the FSR legs. If it beeps (near 0Ω) while unpressed, the polymer layers inside the sensor have fused together due to over-pressing or heat damage. Replace the sensor.

Symptom 2: Serial outputs "State: Unpressed | Force: 0g" (Even when pressing hard)

The analog pin is reading 0V. The circuit is either open or shorted to ground.

  1. Inspect the FSR Tail Contact: This is the #1 physical failure. The 0.5mm pitch silver traces on the tail rarely make reliable contact with standard breadboard spring clips. Squeeze the tail into the breadboard with a small binder clip, or use alligator clips to bypass the breadboard entirely.
  2. Check for a Broken Tail Trace: Bend the FSR tail gently while watching the multimeter resistance. If the resistance spikes to infinite (OL), the silver trace has cracked at the neck (where the sensing area meets the tail). You cannot solder directly to this polymer; you must use a ZIF connector or conductive epoxy to repair it.
  3. Verify the 5V Feed: Ensure the top leg of the FSR is actually receiving 5V from the Arduino. Measure it with a multimeter relative to GND. If it reads 0V, your breadboard's power rail is disconnected.
Multimeter Verification: Before plugging the analog pin into the Arduino, put your multimeter in DC Voltage mode. Probe the center node (Node X) of your breadboard divider. Unpressed, it should read < 0.1V. Pressed hard, it should smoothly sweep up to ~4.5V. If it does not sweep smoothly, your hardware is faulty.

Extending or Simplifying the Build

Depending on your end goal, you may need to scale this project up for a MIDI drum pad, or scale it down to a simple digital button replacement.

How to Extend: Multiplexing for MIDI Drum Pads

The Arduino Uno only has 6 analog pins. If you are building an electronic drum kit or a foot-pedal matrix requiring 16 FSRs, you cannot wire them directly to the board.
The Fix: Use a CD74HC4067 16-Channel Analog Multiplexer. You wire the 16 FSRs to the mux's input channels, and the mux's single SIG pin to Arduino A0. You then use 4 digital pins to toggle the mux's address lines, reading all 16 sensors sequentially in about 2 milliseconds. Ensure you add a 10kΩ pulldown on the mux SIG pin, not on each individual FSR, to save components.

How to Simplify: Digital Thresholding

If you only need to know if a seat is occupied or if a button was pushed—and you don't care about the exact gram force—drop the logarithmic math and the serial plotting.
The Fix: Replace the complex loop with a simple digital threshold. Define a `PRESS_THRESHOLD = 150`. If `smoothedADC > PRESS_THRESHOLD`, set a digital pin HIGH to trigger a relay or a keyboard emulator. This reduces CPU overhead and eliminates the need for floating-point math, which is useful if you are migrating the code to a smaller, resource-constrained board like an ATtiny85.

For deeper integration details, refer to the official Arduino analogRead() documentation regarding ADC prescalers, and consult the Adafruit FSR integration guide for mechanical mounting best practices to prevent creep and hysteresis errors.