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.

Table 1: LVDT Sensor Specifications (AC vs. DC-Conditioned)
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) -
Interfacing Tip: For 3.3V logic systems like the ESP32, specify a DC-conditioned LVDT with a 0.5V to 2.5V output range. This keeps the signal safely within the ESP32's ADC limits without requiring a precision voltage divider, which introduces resistor tolerance errors.

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.

Table 2: ESP32 to DC-Conditioned LVDT Pinout
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.

  1. 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.
  2. ADC Configuration: Assign GPIO 34 (an input-only ADC1 pin). Set the ADC attenuation to 11dB to allow the full 0-3.3V range.
  3. Software Oversampling: Read the ADC 64 times in rapid succession and average the results to smooth out high-frequency EMI noise.
  4. 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.