A flex sensor is essentially a variable resistor that increases its resistance as it is bent. When integrating flex sensors with Arduino boards for projects like robotic gloves, animatronics, or MIDI controllers, the most common mistake is wiring the sensor directly to an analog pin and expecting a clean 0-1023 reading. Because the Arduino's ADC (Analog-to-Digital Converter) measures voltage, not resistance, you must use a voltage divider circuit to translate the changing resistance into a readable voltage swing.
Flat, a standard 2.2-inch flex sensor measures roughly 10kΩ. Bent at 90 degrees, it climbs to 30kΩ–40kΩ. This guide covers the exact hardware wiring, the voltage divider math, a production-ready C++ sketch with error handling, and the specific debugging steps for when your serial monitor gets stuck at 1023.
Flex Sensor Specifications & Selection
Not all flex sensors are created equal. The resistance curve, physical durability, and minimum bend radius dictate which model you should buy. The most common commercial variants are manufactured by Spectra Symbol and sold through distributors like Adafruit and SparkFun.
| Model / Variant | Flat Resistance | 90° Bend Resistance | Min. Bend Radius | Approx. Price |
|---|---|---|---|---|
| Spectra Symbol 2.2" (FLX-01-2.2) | ~10,000 Ω | 20kΩ - 30kΩ | 6.35 mm | $7.95 |
| Spectra Symbol 4.5" (FLX-01-4.5) | ~10,000 Ω | 35kΩ - 45kΩ | 6.35 mm | $11.95 |
| Spectra Symbol 5.5" (FLX-01-5.5) | ~10,000 Ω | 40kΩ - 60kΩ | 6.35 mm | $14.95 |
| Generic Carbon Nanotube (CNT) Flex | ~1,000 Ω | 3kΩ - 5kΩ | 2.00 mm | $18.50 |
The Math: To read the 2.2" sensor, we pair it with a fixed 10kΩ pull-down resistor. Using the voltage divider formula Vout = Vin * (R_fixed / (R_flex + R_fixed)), a flat sensor (10kΩ) yields 2.5V (ADC ~512). Bent to 30kΩ, it yields 1.25V (ADC ~255). This gives you a usable swing of roughly 250 ADC steps, which is plenty for finger-tracking resolution.
Hardware Build: Parts List & Pin Mapping
This build targets the Arduino Uno R3 or the newer Arduino Uno R4 Minima. Both utilize a 10-bit ADC (0-1023 range) on the analog pins, making the code universally compatible across the AVR and ARM architectures.
Required Components
- Microcontroller: Arduino Uno R3 or R4 Minima
- Sensor: 2.2" Flex Sensor (e.g., Adafruit Product 182)
- Resistor: 10kΩ 1/4W Carbon Film (for 2.2" sensor) or 47kΩ (for 4.5" sensor)
- Connector: 0.1" Female Header (2-pin) or alligator clips
- Filter Capacitor (Optional but recommended): 100nF (0.1µF) ceramic capacitor
- Breadboard and jumper wires
Pin Mapping & Wiring Steps
| Component Pin | Destination | Notes |
|---|---|---|
| Flex Sensor Pin 1 | Arduino 5V | Polarity does not matter on standard flex sensors. |
| Flex Sensor Pin 2 | Arduino Pin A0 & Resistor Leg 1 | This is the analog sense node. |
| Resistor Leg 2 | Arduino GND | Acts as the pull-down to complete the divider. |
| 100nF Capacitor | Between A0 and GND | Hardware low-pass filter to eliminate ADC jitter. |
- Insert the 10kΩ resistor into the breadboard. Connect one leg to the Arduino's GND rail.
- Connect the other leg of the resistor to the Arduino's A0 pin via a jumper wire.
- Insert the flex sensor pins into the female header. Connect one flex pin to the 5V rail.
- Connect the second flex pin to the same breadboard row where the resistor and A0 jumper meet.
- Place the 100nF capacitor across the A0 row and the GND rail to stabilize the voltage reading.
Complete Arduino Code with Calibration & Error Handling
Raw analog readings from flex sensors are notoriously noisy due to the high impedance of the voltage divider and environmental EMI. The code below implements a moving average filter, maps the raw ADC values to a usable 0-100% bend scale, and includes a health-check function to catch disconnected wires.
Target Board: Arduino Uno R3 / R4 Minima (10-bit ADC)
// Flex Sensor Robotic Glove Code
// Target: Arduino Uno R3 / R4 Minima
const int FLEX_PIN = A0; // Analog pin connected to the voltage divider
const int NUM_READINGS = 10; // Size of the moving average filter array
const int MIN_RAW = 250; // Raw ADC value when fully bent (calibrate this)
const int MAX_RAW = 520; // Raw ADC value when perfectly flat (calibrate this)
int readings[NUM_READINGS]; // Array to store sensor readings
int readIndex = 0; // Current index in the array
long total = 0; // Running total for averaging
int stuckCounter = 0; // Tracks consecutive identical reads for error handling
int lastRead = -1; // Stores the previous read value
void setup() {
Serial.begin(115200);
pinMode(FLEX_PIN, INPUT);
// Initialize the readings array
for (int i = 0; i < NUM_READINGS; i++) {
readings[i] = 0;
}
Serial.println("Flex Sensor Initialized. Keep flat for baseline...");
delay(1000);
}
void loop() {
int rawValue = analogRead(FLEX_PIN);
// Error Handling: Check for stuck pin (disconnected wire or short)
checkSensorHealth(rawValue);
// Moving Average Filter
total = total - readings[readIndex];
readings[readIndex] = rawValue;
total = total + readings[readIndex];
readIndex = (readIndex + 1) % NUM_READINGS;
int averageRaw = total / NUM_READINGS;
// Constrain and map to a 0-100% bend percentage
averageRaw = constrain(averageRaw, MIN_RAW, MAX_RAW);
int bendPercent = map(averageRaw, MAX_RAW, MIN_RAW, 0, 100);
// Note: MAX_RAW maps to 0% (flat), MIN_RAW maps to 100% (bent)
Serial.print("Raw: ");
Serial.print(averageRaw);
Serial.print(" | Bend: ");
Serial.print(bendPercent);
Serial.println("%");
delay(20); // 50Hz sampling rate
}
void checkSensorHealth(int currentRead) {
if (currentRead == lastRead) {
stuckCounter++;
} else {
stuckCounter = 0;
}
lastRead = currentRead;
// If the value hasn't changed in 100 reads, flag a hardware error
if (stuckCounter > 100) {
if (currentRead >= 1020) {
Serial.println("ERROR: Sensor stuck at rail (1023). Check pull-down resistor to GND.");
} else if (currentRead <= 5) {
Serial.println("ERROR: Sensor reading 0. Check 5V connection or sensor continuity.");
} else {
Serial.println("WARNING: Sensor value static. Possible trace fracture.");
}
stuckCounter = 0; // Reset to avoid spamming the serial monitor
}
}
Debugging: Analog Read Stuck at 1023 or 0
When prototyping flex circuits, the most frequent failure mode is a frozen serial monitor output. If your serial monitor prints ERROR: Sensor stuck at rail (1023). Check pull-down resistor to GND. or simply outputs a flat 1023 regardless of how much you bend the sensor, follow this diagnostic path.
The First Three Things to Check
- Resistor Placement: Verify that your fixed resistor is actually bridging the analog sense node (A0) and GND. If the pull-down resistor is missing or disconnected, the ADC pin floats, and the internal parasitic capacitance will pull the reading up to the 5V rail (1023).
- Sensor Continuity: Use a multimeter in continuity/resistance mode. Probe the two silver tabs on the flex sensor. It should read ~10kΩ flat. If it reads 'OL' (Open Loop), you have cracked the internal carbon trace by bending it past its 6.35mm minimum radius.
- ADC Pin Damage: If you accidentally shorted 5V directly to A0 without a current-limiting resistor in a previous iteration, you may have damaged the internal multiplexer of the ATmega328P or the Renesas RA4M1 on the R4. Test A1 with a known good potentiometer to rule out a dead ADC channel.
Ranked Causes for Erratic Jitter (Not Stuck, but Noisy)
If the sensor isn't stuck, but the values are jumping wildly (e.g., 400, 415, 380, 450), the issue is high-impedance noise. The Arduino ADC expects a source impedance of 10kΩ or less. A bent flex sensor (30kΩ) combined with a 10kΩ pull-down creates a Thevenin equivalent resistance that exceeds the ADC's sample-and-hold capacitor charging threshold.
- Cause 1 (Most Likely): Missing hardware low-pass filter. Fix: Add the 100nF capacitor across A0 and GND as shown in the wiring steps.
- Cause 2: USB power noise. Fix: Power the Arduino via the barrel jack with a regulated 9V supply, or use an external 5V LDO regulator for the sensor's voltage rail.
- Cause 3: Wire crosstalk. Fix: Keep the analog jumper wires away from the breadboard's power rails and any PWM/servo signal wires.
Extending and Simplifying the Build
Once you have a single finger tracking reliably, you will likely want to scale up to a full five-finger robotic glove or improve the resolution for delicate MIDI control.
How to Simplify
If you want to skip the breadboard voltage divider and hardware filtering, purchase a Flex Sensor Breakout Board. These small PCBs include the pull-down resistor, an operational amplifier (op-amp) configured as a voltage follower to drop the output impedance to near-zero, and a low-pass filter. They output a clean 0-3.3V or 0-5V signal directly to your analog pin, eliminating the need for software smoothing arrays.
How to Extend (High-Resolution I2C ADC)
The Uno's internal 10-bit ADC gives you roughly 250 usable steps across the flex sensor's range. For high-precision animatronics, this can result in visible 'stair-stepping' in servo movements. To fix this, bypass the internal ADC entirely and use an external 16-bit I2C ADC like the Texas Instruments ADS1115.
The ADS1115 provides 65,536 steps of resolution and includes an internal programmable gain amplifier (PGA). By wiring up to four flex sensors to the ADS1115's A0-A3 pins and communicating via I2C, you free up the Arduino's analog pins and achieve sub-millimeter bend tracking accuracy. When mapping 16-bit values in your code, remember to adjust your MIN_RAW and MAX_RAW constants to reflect the 0-65535 range.






