ESP32 UNO - Force Sensor

How hard is someone pressing? An ESP32 Uno force sensor circuit answers that with one analog pin and a few lines of Arduino code. Hook a force sensitive resistor (FSR) up to the ESP32 Uno form board, and every press turns into a number from 0 to 4095 plus a plain label such as "light touch" or "big squeeze" on your computer screen.

By the end, you can:

  1. Explain how a force sensing resistor changes with pressure
  2. Wire an FSR and a 10 kΩ pull-down resistor to an analog pin
  3. Turn each press into a number using analogRead()
  4. Sort raw readings into five pressure levels
ESP32 Uno force sensor

Components You Need

1×ESP32 Uno-form Board
1×USB Cable Type-A to Type-C (for USB-A PC)
1×USB Cable Type-C to Type-C (for USB-C PC)
1×Force Sensor
1×10 kΩ Resistor
1×Breadboard
1×Jumper Wires

Or you can buy the following kits:

1×DIYables Sensor Kit (18 sensors/displays)
Disclosure: Some of the links provided in this section are Amazon affiliate links. We may receive a commission for any purchases made through these links at no additional cost to you.
Additionally, some of these links are for products from our own brand, DIYables .

Force Sensor Basics

After this part, you know what an FSR reports and where it fits.

Force sensor pinout
Property Detail
Other names Force sensing resistor, force sensitive resistor, FSR
Output type Resistance that drops as pressure rises
Resting resistance Very high (almost an open circuit)
Strong at Spotting a tap, a press or a squeeze
Weak at Exact weight in grams or pounds
Price and effort Low cost, no library needed
Found in E-drum pads, phones, handheld game pads, small gadgets

Good to know: treat an FSR as a "how hard" sensor, not a scale. Two sensors from the same bag can give quite different numbers for the same press.

Pinout

The part has just two legs. Like any plain resistor it has no polarity, so either leg can go to either side of the circuit.

How It Works

Pressure squeezes the conductive layers inside the round pad closer together, so current finds an easier path.

Pressure on the pad FSR resistance Voltage at A0
None Very high About 0V
Light High Low
Medium Medium Middle
Hard Low Close to 3.3V

A fixed 10 kΩ resistor is placed in series with the FSR. The pair forms a voltage divider, and the joint between them feeds the analog pin. Less FSR resistance means a higher voltage at that joint.

ESP32 Uno Pinout

The labels printed on the Uno headers (A0, 3V3, GND) are not the numbers your sketch uses. Find each label's GPIO number in the diagram below before you plug in a jumper wire.

ESP32 Uno pinout diagram

How to Connect

At the end of this step, the FSR and resistor form a divider between 3.3V and GND, with the middle point on A0.

The wiring diagram between ESP32 Uno Force

This image is created using Fritzing. Click to enlarge image

Component Pin ESP32 Uno Pin
Force sensor leg 1 3.3V
Force sensor leg 2 + one end of 10 kΩ resistor A0 (GPIO2)
Other 10 kΩ resistor leg GND

Good to know: the ESP32 Uno runs on 3.3V logic, so the top of the divider must be the 3.3V pin.

WARNING

Do not feed the FSR from 5V. A firm press pushes A0 almost up to the supply voltage, and ESP32 Uno pins are not 5V tolerant. 5V on A0 can kill the pin.

WARNING

A0 is GPIO2, a boot strapping pin. The 10 kΩ resistor keeps it LOW at startup, which is safe. If the board refuses to boot or upload, check that nothing pulls A0 HIGH during reset (for example, someone pressing the sensor while you hit upload).

Reading Pressure in Code

This part shows how a voltage becomes a number your sketch can test.

Item ESP32 Uno value
Analog pins on the Uno header A0 to A5
ADC resolution 12-bit
Value range 0 to 4095
Input voltage range (11 dB attenuation) 0V to about 3.3V
Function used analogRead()

Because the divider joint goes to A0, a single call to analogRead() on that pin tells you the pressure. Bigger numbers mean a harder press.

Good to know: A0 (GPIO2) is an ADC2 pin. It reads fine in this sketch, but ADC2 stops working once WiFi is turned on.

ESP32 Uno Code

This sketch samples A0 (GPIO2) once a second, prints the raw number, and adds a pressure label.

/* * This ESP32 Uno code was developed by newbiely.com * * This ESP32 Uno code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/esp32-uno/esp32-uno-force-sensor */ #define FORCE_SENSOR_PIN 2 // A0 (GPIO2) on the ESP32 Uno reads the FSR, with a 10K resistor to GND void setup() { Serial.begin(9600); // set the ADC attenuation to 11 dB (up to ~3.3V input) analogSetAttenuation(ADC_11db); } void loop() { int analogReading = analogRead(FORCE_SENSOR_PIN); Serial.print("Force sensor reading = "); Serial.print(analogReading); // raw 12-bit value, 0 to 4095 if (analogReading < 40) // 0 - 39 Serial.println(" -> no pressure"); else if (analogReading < 800) // 40 - 799 Serial.println(" -> light touch"); else if (analogReading < 2000) // 800 - 1999 Serial.println(" -> light squeeze"); else if (analogReading < 3200) // 2000 - 3199 Serial.println(" -> medium squeeze"); else // 3200 - 4095 Serial.println(" -> big squeeze"); delay(1000); }
Code Part What It Does
#define FORCE_SENSOR_PIN 2 Points the sketch at A0, which is GPIO2
analogSetAttenuation(ADC_11db) Widens the ADC input range to about 3.3V
analogRead(FORCE_SENSOR_PIN) Returns the divider voltage as 0 to 4095
if / else if chain Compares the value with 40, 800, 2000 and 3200
delay(1000) Waits one second between readings

The result is one line per second, each showing the reading and a pressure label.

Good to know: analogSetAttenuation(ADC_11db) lets the ESP32 Uno analog pin cover the full span from 0V up to about 3.3V. Leave it out and the reading slams into 4095 after a gentle press, so the labels stop making sense.

The four limits are sized for the 12-bit range. Your FSR may behave a bit differently, so adjust them after a few test presses.

Upload and Test

  1. First time with this board? Set it up with ESP32 Uno - Getting Started before you continue.
  2. Build the circuit by following the wiring table above.
  3. Plug in the USB Type-C cable between the ESP32 Uno and your computer.
  4. Select the board in Arduino IDE: pick ESP32 Dev Module and the COM port of the board (CH340).
  5. Paste the sketch into a new Arduino IDE window.
  6. Press Upload (Upload) and wait for "Done uploading".
How to upload ESP32 Uno code on Arduino IDE
  1. Open Serial Monitor at 9600 baud.
  2. Push on the pad softly first, then harder.
  3. Watch the labels change in the Serial Monitor.
∞
Newbiely | Arduino IDE 2.3.8
──
☐
✕
File
Edit
Sketch
Tools
Help
ESP32 Dev Module
Newbiely.ino
···
8 Serial.println("Hello World!");
Output
Serial Monitor
Message (Enter to send message to 'ESP32 Dev Module' on 'COM15')
New Line
9600 baud
Force sensor reading = 0 -> no pressure Force sensor reading = 12 -> no pressure Force sensor reading = 315 -> light touch Force sensor reading = 702 -> light touch Force sensor reading = 1143 -> light squeeze Force sensor reading = 1866 -> light squeeze Force sensor reading = 2271 -> medium squeeze Force sensor reading = 2985 -> medium squeeze Force sensor reading = 3412 -> big squeeze Force sensor reading = 3907 -> big squeeze Force sensor reading = 5 -> no pressure
Ln 11, Col 1
ESP32 Dev Module on COM15
2
  1. Pro tip: tape the FSR flat to a stiff card before testing. A bent or wobbly pad gives lower, jumpier numbers than a flat one.

Video Tutorial

See the ESP32 Uno build in action in the video below.

The video project adds two outputs: a row of 8 LEDs on D2 to D9 that light up like a level meter, and a battery-style bar on an SSD1306 OLED wired to the SDA (GPIO21) and SCL (GPIO22) header pins. Here is the matching ESP32 Uno code:

/* * This ESP32 Uno code was developed by newbiely.com * * This ESP32 Uno code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/esp32-uno/esp32-uno-force-sensor */ #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> // SSD1306 screen size and reset setting #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define OLED_RESET -1 Adafruit_SSD1306 display( SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET ); // A0 (GPIO2) reads the FSR voltage divider #define FORCE_SENSOR_PIN 2 // eight LEDs for the force bar: D2, D3, D4, D5, D6, D7, D8, D9 const int ledPins[8] = { 26, 25, 17, 16, 27, 14, 12, 13 }; // expected FSR range on the 12-bit ADC (0-4095) const int FORCE_MIN = 0; const int FORCE_MAX = 4000; // smoothing filter (moving average with weight alpha) float smoothValue = 0; const float alpha = 0.3; // setup() prepares the LEDs, the OLED and the filter void setup() { Serial.begin(115200); // set the ADC attenuation to 11 dB (up to ~3.3V input) analogSetAttenuation(ADC_11db); // make every LED pin an output and switch it off for (int i = 0; i < 8; i++) { pinMode(ledPins[i], OUTPUT); digitalWrite(ledPins[i], LOW); } // start the OLED on I2C (SDA GPIO21, SCL GPIO22), address 0x3C if (!display.begin( SSD1306_SWITCHCAPVCC, 0x3C )) { Serial.println("OLED failed!"); // no screen found, so halt here while (1); } display.clearDisplay(); display.setTextColor(SSD1306_WHITE); // short splash screen display.setTextSize(2); display.setCursor(20, 25); display.println("DIYables"); display.display(); delay(1500); // seed the filter with a real reading // so the first values do not ramp up from zero smoothValue = analogRead(FORCE_SENSOR_PIN); } void loop() { // take a fresh FSR sample int rawValue = analogRead(FORCE_SENSOR_PIN); // blend the new sample with the old value // to hide small jumps and noise smoothValue = alpha * rawValue + (1.0 - alpha) * smoothValue; int forceValue = (int)smoothValue; // scale the value to a level from 0 to 8 int level = map( forceValue, FORCE_MIN, FORCE_MAX, 0, 8 ); level = constrain(level, 0, 8); // light one LED per level, the rest stay off for (int i = 0; i < 8; i++) { if (i < level) { digitalWrite(ledPins[i], HIGH); } else { digitalWrite(ledPins[i], LOW); } } // print the value and level for tuning Serial.print("FSR = "); Serial.print(forceValue); Serial.print(" | Level = "); Serial.println(level); // start a new OLED frame display.clearDisplay(); // heading text display.setTextSize(2); display.setCursor(50, 0); display.println("FSR"); // size and place of the battery-style bar int barX = 10; int barY = 25; int barWidth = 108; int barHeight = 18; // bar outline display.drawRect( barX, barY, barWidth, barHeight, SSD1306_WHITE ); // small cap on the right end display.fillRect( barX + barWidth, barY + 5, 4, 8, SSD1306_WHITE ); // inner fill grows with the level int fillWidth = map( level, 0, 8, 0, barWidth - 4 ); if (fillWidth > 0) { display.fillRect( barX + 2, barY + 2, fillWidth, barHeight - 4, SSD1306_WHITE ); } // level as text, e.g. 5/8 display.setTextSize(1); display.setCursor(5, 50); display.print("LEVEL: "); display.print(level); display.print("/8"); // brand name in the corner display.setCursor(78, 50); display.print("DIYables"); display.display(); // brief pause between frames delay(30); }
LED Uno Pin GPIO in code
LED 1 D2 26
LED 2 D3 25
LED 3 D4 17
LED 4 D5 16
LED 5 D6 27
LED 6 D7 14
LED 7 D8 12
LED 8 D9 13

WARNING

LED 7 sits on D8 (GPIO12), a boot strapping pin that must be LOW when the board starts. An LED with its resistor to GND normally keeps it LOW, but if the board will not boot or upload, unplug the D8 LED, upload, and plug it back in.

Code Explanation

This part walks through the demo sketch so you can change it with confidence.

Code Part What It Does
ledPins[8] Holds GPIO 26, 25, 17, 16, 27, 14, 12, 13 for D2 to D9
smoothValue and alpha Moving-average filter, 30% new sample and 70% old value
FORCE_MAX = 4000 Top of the expected 12-bit range
map(...) + constrain(...) Turns the smoothed value into a level from 0 to 8
LED for loop Turns on one LED per level
drawRect / fillRect Draws the battery outline and a fill that grows with the level

The result is a fast, smooth 8-step meter on both the LEDs and the OLED, refreshed about every 30 ms.

ESP32 Uno vs UNO R4 for This Project

This table lists what changes when this FSR project moves from the UNO R4 to the ESP32 Uno.

Item Arduino UNO R4 ESP32 Uno
Logic voltage 5V 3.3V
Sensor supply 5V 3.3V pin
Sensor pin in code A0 2 (A0 = GPIO2)
ADC range 0 to 1023 0 to 4095
Extra setup line None analogSetAttenuation(ADC_11db)
Pressure limits 10, 200, 500, 800 40, 800, 2000, 3200
Arduino IDE board Arduino UNO R4 WiFi ESP32 Dev Module

The wiring stays on the same header positions. The only hardware change is taking the top of the divider from 3.3V instead of 5V, and the only code changes are the pin number, the attenuation line and the four limits.

Go Wireless: Pressure Gauge on Your Phone

With a few extra lines, the press strength shows up on your phone instead of the Serial Monitor.

The ESP32 Uno has WiFi and Bluetooth built in. Send the FSR value to a web gauge or a live plotter, and you can watch a grip, a step or a door push from across the room. For a WiFi project, move the FSR to an ADC1 pin first (A2 to A5, or GPIO32/33), because A0 cannot be read while WiFi is on. Over Bluetooth Classic, A0 keeps working.

Troubleshooting

Problem Possible Cause Solution
No COM port appears in Arduino IDE CH340 driver not installed or a charge-only cable Install the CH340 driver and swap to a USB data cable
Upload stops at "Failed to connect" or "Waiting for download" Board did not enter download mode Hold the BOOT button while the upload starts, then release it
Board does not boot or upload with the circuit connected A0 (GPIO2) or D8 (GPIO12) is a boot strapping pin pulled the wrong way Unplug the sensor or the D8 LED while uploading, or check the 10 kΩ resistor goes to GND
Value stays at 0 when you press hard FSR leg not on 3.3V or A0 jumper in the wrong breadboard row Trace both legs and the A0 jumper row by row
Random numbers with nothing touching the pad Pull-down resistor missing or loose Seat the 10 kΩ resistor firmly between A0 and GND
Gentle tap already gives 4095 analogSetAttenuation line removed Put analogSetAttenuation(ADC_11db) back in setup()
Label is always "no pressure" Limits copied from 10-bit code Use the 12-bit limits 40, 800, 2000 and 3200
Unreadable symbols in Serial Monitor Baud rate mismatch Pick 9600 for the main sketch and 115200 for the video demo
OLED stays dark in the demo OLED wired to A4/A5 or wrong address Move it to the SDA (GPIO21) and SCL (GPIO22) pins and check address 0x3C
Reading freezes after adding WiFi A0 is on ADC2 Move the FSR to A2 to A5 or GPIO32/33

FAQ

Is an FSR accurate enough to weigh things?

No. It changes a lot from part to part and drifts with time and temperature. It is great for "pressed or not" and "soft or hard". For real weight, pick a load cell with an HX711 board.

What does the 10 kΩ resistor do in this circuit?

It is the lower half of the voltage divider and it also pulls A0 to 0V when the pad is idle. Without it, A0 floats and shows random values. A bigger resistor makes light presses easier to see; a smaller one gives more room for hard presses.

Does it matter which leg of the FSR goes to 3.3V?

No. An FSR has no polarity, just like a normal resistor. Flip it either way and the readings stay the same.

How do I read two force sensors at once?

Give each FSR its own 10 kΩ resistor and its own analog pin, such as A0 and A2. Call analogRead() on each pin and print both values. Every sensor needs its own divider.

Why are my ESP32 Uno readings about four times bigger than on other Arduino boards?

Its converter has 12 bits, which means 4096 steps (0 to 4095), while a 10-bit board stops at 1023. Code written for a 10-bit board needs its limits multiplied by about 4. The sketch on this page already uses the bigger limits.

What is analogSetAttenuation(ADC_11db) for in this FSR sketch?

It sets the ESP32 Uno ADC to accept about 0V to 3.3V, so the whole FSR swing maps to 0 to 4095. With a narrower range, the value saturates after a light press. The UNO R4 does not use this line; only ESP32 Uno code needs it. Keep the A0 voltage at 3.3V or less.

Can I keep the FSR on A0 if I add WiFi to the project?

Not if you want readings. A0 (GPIO2) and A1 (GPIO4) are ADC2 pins, and ADC2 cannot be read while WiFi is running. Move the sensor to A2 to A5 (GPIO35, 34, 36, 39) or GPIO32/33 and change FORCE_SENSOR_PIN to match.

Do I need a library to use the force sensor with the ESP32 Uno?

No. analogRead() is part of the esp32 by Espressif Systems core. Only the video demo needs extra libraries: Adafruit SSD1306 and Adafruit GFX for the OLED.

Will UNO shields work on the ESP32 Uno with this sensor?

Most shields fit the headers. Check that the shield runs on 3.3V signals, does not need I2C on A4/A5, and does not drive A0 or D8 at boot.

My FSR project needs more inputs or outputs than the Uno headers offer. What can I use?

The ESP32 Uno has a few extra holes near the power header. GPIO32 and GPIO33 offer I/O, PWM and analog on ADC1, so they still read while WiFi is on. GPIO15 offers I/O, PWM and analog on ADC2. Add pin headers to those holes, then use the plain GPIO number in your sketch. For an extra FSR in a WiFi build, use GPIO32 or GPIO33. Two warnings: (1) GPIO15 and GPIO0 (the IO0 pin at the unused Uno NC spot) are boot strapping pins, and a wrong connection can stop the board from booting or uploading code, so use GPIO32/33 first; (2) never use the six holes under the digital header (SD0 to SD3, CMD, CLK = GPIO6 to GPIO11), because they connect to the flash chip and using them crashes the board.

Can the ESP32 Uno trigger something when the pressure passes a limit?

Yes. Add an if test on the reading and drive an LED, buzzer or relay on a free output pin such as D7 (GPIO14). Avoid A2 to A5 for this, because they are input only.

Learn More

※ OUR MESSAGES

  • As freelancers, We are AVAILABLE for HIRE. See how to outsource your project to us
  • Please feel free to share the link of this tutorial. However, Please do not use our content on any other websites. We invested a lot of effort and time to create the content, please respect our work!