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






