The LVDT Sensor: Sensing Principle and Output Types
An LVDT (Linear Variable Differential Transformer) sensor operates on the principle of mutual inductance to measure linear displacement. It consists of one primary coil and two secondary coils wound symmetrically on a hollow cylindrical form. A movable ferromagnetic core slides inside this bore; when an AC excitation signal drives the primary coil, it induces voltages in the secondary coils proportional to the core's exact physical position.
The raw output of a bare LVDT is an analog AC differential voltage, which requires complex phase-sensitive demodulation to interpret direction and magnitude. For microcontroller interfacing, you will almost always use a DC-conditioned LVDT (which contains internal electronics to output a proportional 0-5V, 0-10V, or 4-20mA signal) or pair a raw AC LVDT with a dedicated signal conditioning IC like the Analog Devices AD698. The digital value you read on your ESP32 is the result of the microcontroller's ADC sampling this conditioned DC voltage.
Hardware Specifications and Wiring Pinout
Before writing any code, you must identify whether your LVDT is raw AC or DC-conditioned. Pushing a raw AC signal into an ESP32 GPIO will not yield usable displacement data and risks damaging the pin if the excitation voltage exceeds 3.3V. The table below contrasts a standard raw AC model with a DC-conditioned model suitable for direct embedded integration.
| Parameter | Raw AC LVDT (e.g., TE Connectivity) | DC-Conditioned LVDT (e.g., RDP DCTH) | Unit |
|---|---|---|---|
| Stroke Range | ±25.0 | ±10.0 | mm |
| Supply / Excitation Voltage | 3.0V RMS @ 2.5 kHz (AC) | 10 to 28 (DC) | V |
| Output Signal | AC Differential (mV/V/mm) | 0.5 to 2.5 (DC) | V |
| Linearity Error | ±0.25% of Full Scale | ±0.10% of Full Scale | % FS |
| Repeatability | Infinite (non-contact) | Infinite (non-contact) | - |
DC-Conditioned LVDT Wiring Pinout
Assuming a standard 4-pin DC-conditioned LVDT with a 0.5V–2.5V output, use the following wiring schema. Always use shielded twisted pair (STP) cable for the signal lines.
| LVDT Wire Color | Function | ESP32 / Power Supply Pin | Notes |
|---|---|---|---|
| Red | Supply (+) | External 12V or 24V PSU | Do not power from ESP32 3V3 pin |
| Black | Supply (-) / GND | PSU GND & ESP32 GND | Must share common ground with ESP32 |
| White | Signal Output (+) | GPIO 34 (ADC1_CH6) | Use ADC1 pins; ADC2 conflicts with WiFi |
| Bare/Braid | Cable Shield | ESP32 GND (One end only) | Ground shield at MCU end to prevent loops |
Signal Math: Converting ADC Raw Readings to Millimeters
Unlike a simple potentiometer, a bidirectional LVDT outputs a voltage centered around a zero-offset. For our 0.5V to 2.5V sensor with a ±10mm stroke, the core at the exact mechanical center outputs 1.5V (1500 mV). Moving the core +10mm yields 2.5V, while -10mm yields 0.5V.
The sensitivity (scaling factor) is calculated as:
Sensitivity = (V_max - V_min) / Total_Stroke = (2.5V - 0.5V) / 20mm = 0.1 V/mm (or 100 mV/mm).
To convert the ESP32's ADC reading into physical displacement, we use the analogReadMilliVolts() function introduced in recent ESP32 Arduino cores. This function utilizes the chip's internal eFuse calibration data to return a linearized millivolt value, bypassing the notorious non-linearity of the ESP32's raw 12-bit ADC at the voltage rails.
The raw-to-unit math formula is:
Displacement (mm) = (ADC_mV - Zero_Offset_mV) / Sensitivity_mV_per_mm
Substituting our specific sensor values:
Displacement (mm) = (ADC_mV - 1500) / 100.0
ESP32 Interfacing: Step-by-Step Implementation
Follow these steps to read, filter, and scale the LVDT signal. We implement a simple oversampling filter because LVDTs are highly sensitive to high-frequency electrical noise, and the ESP32 ADC can exhibit ±30mV of jitter on a quiet bench.
- Hardware Check: Verify the LVDT power supply is active and the shared ground between the PSU and ESP32 is secure. Measure the white signal wire with a multimeter; it should read ~1.5V with the core centered.
- ADC Configuration: Assign GPIO 34 (an input-only ADC1 pin). Set the ADC attenuation to 11dB to allow the full 0-3.3V range.
- Software Oversampling: Read the ADC 64 times in rapid succession and average the results to smooth out high-frequency EMI noise.
- Apply Math: Convert the averaged millivolt reading to millimeters using the sensitivity formula.
// LVDT Sensor Interfacing for ESP32 (Arduino Core)
// Target: DC-Conditioned LVDT (0.5V - 2.5V, ±10mm stroke)
const int lvdtPin = 34; // ADC1_CH6 (GPIO 34)
const int SAMPLE_COUNT = 64; // Oversampling factor for noise reduction
// Sensor Calibration Constants (Adjust based on your specific datasheet)
const float ZERO_OFFSET_MV = 1500.0;
const float SENSITIVITY_MV_MM = 100.0;
void setup() {
Serial.begin(115200);
analogReadResolution(12); // Ensure 12-bit resolution (0-4095)
analogSetPinAttenuation(lvdtPin, ADC_11db); // Full 0-3.3V range
// Allow ADC to stabilize
delay(100);
for(int i=0; i<10; i++) analogRead(lvdtPin);
}
void loop() {
long total_mV = 0;
// Oversampling to mitigate EMI and ADC jitter
for (int i = 0; i < SAMPLE_COUNT; i++) {
total_mV += analogReadMilliVolts(lvdtPin);
delayMicroseconds(50); // Small delay between samples
}
float avg_mV = (float)total_mV / SAMPLE_COUNT;
// Raw-to-Unit Math
float displacement_mm = (avg_mV - ZERO_OFFSET_MV) / SENSITIVITY_MV_MM;
// Clamp to physical limits to handle noise at extreme ends
displacement_mm = constrain(displacement_mm, -10.0, 10.0);
Serial.print("Avg mV: ");
Serial.print(avg_mV, 1);
Serial.print(" | Displacement: ");
Serial.print(displacement_mm, 2);
Serial.println(" mm");
delay(100); // 10Hz update rate
}
Troubleshooting Common Interference and Calibration Drift
LVDTs themselves are virtually immune to environmental factors because the core is non-contact and the coils are sealed. However, the signal conditioning and wiring are highly susceptible to industrial interference. If your serial monitor shows erratic jumping or a steady offset, check the following failure modes.
1. Ground Loops and 50/60Hz Hum
Symptom: The ADC reading oscillates by 10–50mV at a steady 50Hz or 60Hz rhythm, causing a ±0.5mm jitter in the final output.
Cause: The cable shield is grounded at both the sensor housing and the ESP32, creating a ground loop that picks up ambient AC mains magnetic fields.
Fix: Disconnect the cable shield from the LVDT housing. Ground the shield only at the ESP32 GND pin. As noted in Omega Engineering's LVDT guidelines, single-point grounding is mandatory for low-level analog signals.
2. Thermal Zero-Shift
Symptom: The sensor reads 0.0mm perfectly at room temperature, but drifts to +0.2mm after the machine warms up, even though the core hasn't moved.
Cause: Thermal expansion of the mechanical linkage pushing the core, or temperature drift in the LVDT's internal signal conditioning resistors.
Fix: Perform a software tare. Add a physical "home" limit switch. On boot, drive the mechanism to the home position, read the LVDT, and dynamically update the ZERO_OFFSET_MV variable in your code to compensate for thermal expansion.
3. ESP32 ADC2 WiFi Conflict
Symptom: The LVDT reads perfectly until you connect the ESP32 to WiFi or use MQTT, at which point the ADC returns 0 or random noise.
Cause: You wired the LVDT to an ADC2 pin (e.g., GPIO 25, 26, 27). The ESP32 hardware disables ADC2 when the WiFi radio is active.
Fix: Rewire the signal to an ADC1 pin (GPIO 32, 33, 34, 35, 36, or 39). Refer to the Espressif ADC API documentation for pin mapping constraints.
4. Mechanical Binding and Side-Loading
Symptom: The output is non-linear; it sticks at certain points in the stroke or shows hysteresis (reads differently when extending vs. retracting).
Cause: Side-loading on the push-rod. LVDTs measure purely linear motion; lateral forces cause the core to rub against the coil bore, creating friction and altering the magnetic flux path.
Fix: Use a spring-loaded LVDT with a spherical tip, or decouple the push-rod using a flexible linkage. Never rigidly clamp both ends of an LVDT push-rod across two separate mechanical structures that might flex independently.






