Arduino-Based Force Sensor Calibration System with I2C LCD Display
View and Edit Project In Cirkit Designer
Circuit Documentation
Summary
This circuit is designed to measure force using a Force Sensor (FSR) and display the readings on a 16x2 I2C LCD. The Arduino UNO microcontroller processes the sensor data and manages the communication with the LCD. A resistor is used in conjunction with the force sensor to create a voltage divider, allowing the Arduino to read the sensor's output.
Component List
Force Sensor
- Description: A sensor that detects force or pressure applied to it.
- Pins:
- Pin 1: Connected to the VCC (5V)
- Pin 2: Connected to the analog input (A0) through a resistor
Arduino UNO
- Description: A microcontroller board based on the ATmega328P. It is used to read sensor data and control the LCD.
- Pins:
- 5V: Power supply for the Force Sensor
- GND: Common ground for the circuit
- A0: Analog input pin connected to the Force Sensor
- SDA: Data line for I2C communication with the LCD
- SCL: Clock line for I2C communication with the LCD
16x2 I2C LCD
- Description: A liquid crystal display that can show two lines of text, each containing 16 characters. It communicates with the Arduino via I2C.
- Pins:
- VCC: Power supply (connected to 5V)
- GND: Common ground (connected to GND)
- SDA: Data line for I2C communication (connected to Arduino SDA)
- SCL: Clock line for I2C communication (connected to Arduino SCL)
Resistor
- Description: A passive electrical component that limits the flow of current in the circuit.
- Pins:
- Pin 1: Connected to the Force Sensor (Pin 2)
- Pin 2: Connected to GND
Wiring Details
Force Sensor
- Pin 1: Connected to 5V from the Arduino
- Pin 2: Connected to A0 on the Arduino through a 10kΩ resistor to GND
Arduino UNO
- 5V: Supplies power to the Force Sensor
- GND: Common ground for the circuit, connected to the Resistor and LCD
- A0: Reads the output from the Force Sensor
- SDA: Connected to SDA on the 16x2 I2C LCD
- SCL: Connected to SCL on the 16x2 I2C LCD
16x2 I2C LCD
- VCC: Connected to 5V from the Arduino
- GND: Connected to GND from the Arduino
- SDA: Connected to SDA on the Arduino
- SCL: Connected to SCL on the Arduino
Resistor
- Pin 1: Connected to Pin 2 of the Force Sensor
- Pin 2: Connected to GND from the Arduino
Documented Code
// FSR guided calibration with EXACTLY 4 points (conductance fit)
// Wiring: 5V -> FSR -> A0 -> 10k -> GND
// LCD: I2C 16x2 at 0x27 (change to 0x3F if needed)
//
// Commands:
// tare (with no load)
// caln <N> (apply known force in Newtons; repeat exactly 4 times at different forces)
// status (show current readings and state)
// help (show next step)
// tare (during streaming) restarts calibration
//
// After 4 points are collected, the fit runs automatically and streaming starts.
// Model: F ≈ m * (1/Rfsr) + c using linear regression on the 4 points.
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);
const uint8_t FSR_PIN = A0;
const float R_FIXED = 10000.0f; // 10k to GND
const uint16_t ADC_MAX = 1023;
const uint8_t AVG_TARE = 12;
const uint8_t AVG_CAL = 12;
const uint8_t AVG_STREAM = 8;
const unsigned long STREAM_MS = 200;
enum Stage { WAIT_TARE, WAIT_POINTS, STREAMING };
Stage stage = WAIT_TARE;
// Exactly 4 calibration points: x = 1/R (Siemens), y = N
struct CalPoint { float x_invR; float F_N; };
const int REQ_PTS = 4;
CalPoint pts[REQ_PTS];
int nPts = 0;
long tare_adc = 0;
float x0 = 0.0f; // 1/R at tare (for sanity checks if needed)
float m = 0.0f, c = 0.0f;
unsigned long lastStream = 0;
String inLine;
int readADC(uint8_t samples) {
long sum = 0;
for (uint8_t i = 0; i < samples; i++) {
sum += analogRead(FSR_PIN);
delay(2);
}
return (int)(sum / samples);
}
float fsrResistanceFromADC(int adc) {
if (adc <= 0) return INFINITY; // very large R at no load is expected in this divider
if (adc >= ADC_MAX) return 0.0f; // unexpected (A0 near 5V)
return R_FIXED * (float(ADC_MAX) / adc - 1.0f);
}
void sayNext(const __FlashStringHelper* msg) { Serial.print(F("NEXT: ")); Serial.println(msg); }
void sayNow (const __FlashStringHelper* msg) { Serial.print(F("DO THIS: ")); Serial.println(msg); }
void showIntro() {
Serial.println(F("FSR 4-point calibration (conductance fit)"));
Serial.println(F("Commands: tare, caln <N>, status, help"));
Serial.println();
sayNow(F("Ensure NO load on the FSR."));
sayNext(F("Type: tare"));
lcd.clear();
lcd.setCursor(0,0); lcd.print("FSR Ready");
lcd.setCursor(0,1); lcd.print("Type: tare");
}
void setup() {
Serial.begin(115200); // Make sure your Serial Monitor is also set to 115200 and UTF-8/ASCII
while (!Serial) {}
Wire.begin();
lcd.init();
lcd.backlight();
analogReference(DEFAULT);
pinMode(FSR_PIN, INPUT);
showIntro();
}
void printStatus() {
int adc = readADC(6);
float R = fsrResistanceFromADC(adc);
Serial.print(F("stage="));
if (stage == WAIT_TARE) Serial.print(F("WAIT_TARE"));
else if (stage == WAIT_POINTS) Serial.print(F("WAIT_POINTS"));
else Serial.print(F("STREAMING"));
Serial.print(F(" pts=")); Serial.print(nPts); Serial.print(F("/4"));
Serial.print(F(" adc=")); Serial.print(adc);
Serial.print(F(" R(Ohm)=")); if (isfinite(R) && R > 0) Serial.print(R,0); else Serial.print(F("~INF"));
if (stage == STREAMING) { Serial.print(F(" m=")); Serial.print(m,6); Serial.print(F(" c=")); Serial.print(c,6); }
Serial.println();
}
void guideAfterTare() {
Serial.println(F("Tare complete. adc near 0 at no load is normal with this wiring."));
sayNow(F("Apply a known force and keep it steady."));
sayNext(F("Type: caln <N> (e.g., caln 4.90). Add 4 points total."));
lcd.clear();
lcd.setCursor(0,0); lcd.print("Tare OK");
lcd.setCursor(0,1); lcd.print("caln<N> x4");
}
void guideAfterPoint() {
Serial.print(F("Point added. Now pts=")); Serial.print(nPts); Serial.println(F("/4"));
if (nPts < REQ_PTS) {
sayNow(F("Change to a different force level and keep steady."));
sayNext(F("Type: caln <N> again"));
}
}
bool fitLinear(const CalPoint* p, int n, float &mOut, float &cOut) {
if (n < 2) return false;
double Sx=0, Sy=0, Sxx=0, Sxy=0;
for (int i=0;i<n;i++){ double x=p[i].x_invR, y=p[i].F_N; Sx+=x; Sy+=y; Sxx+=x*x; Sxy+=x*y; }
double den = n*Sxx - Sx*Sx;
if (fabs(den) <