If you are building a DIY CNC router, a motorized linear actuator, or a hydraulic press controller, you need exact feedback on where your mechanical parts are in space. The direct answer for most industrial-grade embedded builds is to use a 0-10V analog linear position sensor paired with a microcontroller like the ESP32. Because the ESP32 operates at 3.3V logic, you cannot wire a 10V industrial transducer directly to its GPIO pins; you must use a precision voltage divider and apply a specific scaling algorithm to convert raw ADC readings into physical millimeters.

The Sensing Principle: How Industrial Linear Transducers Work

A linear position sensor translates physical displacement along a single axis into a proportional electrical signal. In basic resistive types (linear potentiometers), a physical wiper slides along a carbon or cermet track, changing the resistance ratio. However, in industrial magnetostrictive or Hall-effect transducers—like the Gefran PK-M or Novotechnik LWH series—a magnet moves along the sensor body, and internal signal-conditioning electronics convert the magnetic field variation into a standardized, low-impedance analog voltage (typically 0-10V) or current (4-20mA) loop.

For embedded DIY builds, the 0-10V voltage output is the most practical. Unlike raw 3-wire potentiometers that suffer from wiper contact noise, track wear, and susceptibility to supply voltage sag, a 0-10V industrial linear position sensor contains active internal amplification. This means the output is an active voltage source that scales linearly from 0V at the mechanical zero point to exactly 10V at full stroke, remaining entirely immune to the input supply fluctuations that plague raw resistive pots.

Decoding the Output Signal and Voltage Requirements

The output of this sensor is strictly an analog DC voltage ranging from 0.0V to 10.0V. It is not a digital protocol (like I2C or SPI), nor is it a raw resistance value. The sensor requires an external DC power supply, typically ranging from 10VDC to 30VDC, to power its internal op-amps and Hall/magnetostrictive elements. A standard 24VDC industrial power supply is the ideal choice here.

⚠️ Hardware Warning: The ESP32’s ADC pins are strictly limited to 3.3V. Feeding a 10V signal directly into GPIO 34, 35, 36, or 39 will instantly and permanently destroy the microcontroller's internal ADC multiplexer. A hardware voltage divider is mandatory.

Wiring Pinout and Voltage Divider Design

To safely step down the 0-10V signal to a 0-3.125V signal (safely within the ESP32's 3.3V limit), we use a resistor voltage divider. For precision, you must use 1% tolerance metal film resistors, not standard 5% carbon film, or your position readings will drift by several millimeters across the stroke.

Sensor Wire / Component Function ESP32 DevKit Pin Notes & Specifications
Brown Wire (Pin 1) Supply Voltage (+) 24VDC PSU (+) Sensor supply range: 10-30VDC. Do not use the ESP32 VIN.
Blue Wire (Pin 2) Signal Output (0-10V) Voltage Divider Input Connect to the junction of R1 and R2.
Black Wire (Pin 3) Supply Ground (-) 24VDC PSU (-) & ESP32 GND Crucial: Sensor ground and ESP32 ground MUST be bonded together.
Resistor R1 (22kΩ) Divider Top Leg N/A 22kΩ 1% Metal Film. Connects between Sensor Blue and ESP32 GPIO.
Resistor R2 (10kΩ) Divider Bottom Leg ESP32 GND 10kΩ 1% Metal Film. Connects between ESP32 GPIO and GND.
ESP32 GPIO 34 ADC Input Channel 6 GPIO 34 Input only pin. No internal pull-up/pull-down resistors.

Using the standard voltage divider formula ($V_{out} = V_{in} \times \frac{R2}{R1 + R2}$), a 10V maximum input yields: $10V \times \frac{10k}{22k + 10k} = 3.125V$. This leaves a 0.175V safety margin below the ESP32’s 3.3V absolute maximum rating.

Raw-to-Unit Math and ESP32 Calibration Code

Converting the raw ADC reading into physical millimeters requires a three-step mathematical chain. First, we convert the raw 12-bit ADC value (0-4095) into millivolts. Second, we reverse the voltage divider math to find the actual sensor voltage. Third, we scale that voltage against the sensor's physical stroke length.

Note on ESP32 ADC Non-Linearity: The ESP32's ADC is notoriously non-linear near the 0V and 3.3V rails. To bypass this hardware flaw, modern ESP32 Arduino cores (v2.x and above) include the analogReadMilliVolts() function, which applies an internal factory-calibrated lookup table (LUT) to return a highly accurate millivolt reading. Always use this instead of the raw analogRead(). For deeper architectural details, refer to the Espressif ADC Oneshot Driver Documentation.

// ESP32 Linear Position Sensor Scaling Code (Arduino Framework)
// Assumes: 500mm stroke sensor, 0-10V output, 22k/10k divider on GPIO 34

const int SENSOR_PIN = 34;
const float STROKE_MM = 500.0;      // Physical full-scale stroke of your sensor
const float SENSOR_V_MAX = 10.0;    // Sensor max output voltage
const float R1 = 22000.0;           // Top resistor in ohms
const float R2 = 10000.0;           // Bottom resistor in ohms
const float DIVIDER_RATIO = (R1 + R2) / R2; // 3.2

// Two-point calibration offsets (determined during physical bench test)
const float CAL_OFFSET_MM = 2.5;    // Mechanical zero-point error
const float CAL_SCALE_FACTOR = 1.01;// Span linearity correction

void setup() {
  Serial.begin(115200);
  analogSetAttenuation(ADC_11db);   // Required for 0-3.3V full-scale reading
  pinMode(SENSOR_PIN, INPUT);
}

void loop() {
  // Step 1: Get accurate millivolts using ESP32 internal LUT
  int adc_mV = analogReadMilliVolts(SENSOR_PIN);
  
  // Step 2: Calculate actual voltage at the sensor output pin
  float voltage_at_pin = adc_mV / 1000.0;
  float sensor_voltage = voltage_at_pin * DIVIDER_RATIO;
  
  // Step 3: Scale to physical units (mm) and apply calibration
  float raw_position_mm = (sensor_voltage / SENSOR_V_MAX) * STROKE_MM;
  float final_position_mm = (raw_position_mm * CAL_SCALE_FACTOR) + CAL_OFFSET_MM;
  
  // Clamp to physical limits to prevent negative/over-travel readouts
  final_position_mm = constrain(final_position_mm, 0.0, STROKE_MM);
  
  Serial.print("Sensor Voltage: ");
  Serial.print(sensor_voltage, 2);
  Serial.print("V | Position: ");
  Serial.print(final_position_mm, 1);
  Serial.println(" mm");
  
  delay(100); // 10Hz sampling rate is sufficient for most mechanical actuators
}

Mitigating EMI and Ground Loop Interference

Industrial environments and DIY CNC builds are electrically noisy. Stepper motor drivers, spindle VFDs (Variable Frequency Drives), and relay coils generate massive electromagnetic interference (EMI) that will couple into your sensor's analog signal wire, causing your position readings to jump erratically.

💡 Pro-Tip for Cable Routing: Never run the linear position sensor signal cable parallel to stepper motor power cables. If they must cross, force them to cross at a strict 90-degree angle to minimize inductive coupling.

To defeat interference, implement these three hardware-level defenses:

  1. Use Shielded Twisted Pair (STP): Use a cable like Belden 8760. Twist the signal and ground wires together, and connect the copper drain wire to the chassis ground at one end only (usually the control panel side) to prevent ground loops.
  2. Add a Hardware Low-Pass Filter: Solder a 100nF (0.1µF) ceramic capacitor directly across the ESP32 ADC input pin (GPIO 34) and GND. This forms an RC filter with your 10kΩ divider resistor, cutting off high-frequency PWM noise from motor drivers above ~160Hz.
  3. Star Grounding: Ensure the 24VDC sensor ground, the ESP32 ground, and the motor driver ground all meet at a single physical brass grounding busbar. If they daisy-chain through the breadboard, high motor currents will create a voltage differential across the ground wire, which the ADC will read as false position data.

Linear Position Sensor FAQ

Why is my linear position sensor reading jumping around on the ESP32?

If your serial monitor shows the position jumping by 2-5mm randomly while the actuator is stationary, you are experiencing ADC noise or EMI. First, verify you are using analogReadMilliVolts() instead of analogRead(), as the ESP32's raw ADC has an inherent noise floor of about ±30 LSBs. Second, check your physical wiring: ensure you have a 100nF bypass capacitor on the ADC pin, and verify that your sensor's shielded cable drain wire is grounded at the PSU, not floating. Finally, implement a software moving-average filter (averaging the last 10 readings) in your code to smooth out transient electrical spikes.

Can I wire a 4-20mA linear position sensor directly to an Arduino analog pin?

No. A 4-20mA sensor outputs a regulated current loop, not a voltage. An Arduino or ESP32 analog pin measures voltage. To interface a 4-20mA linear position sensor, you must place a precision shunt resistor across the signal and ground wires to convert the current into a voltage (Ohm's Law). A 250Ω 1% shunt resistor will convert the 4-20mA loop into a 1-5V signal, which is safe for a 5V Arduino Uno. For a 3.3V ESP32, use a 150Ω resistor to yield a 0.6V to 3.0V signal.

How do I protect the microcontroller if the linear position sensor cable shorts to 24V?

In heavy machinery, cable chafing can cause the 24V supply wire to short into the 0-10V signal wire. If this happens, 24V will hit your voltage divider. While the 22kΩ/10kΩ divider will drop 24V down to 7.5V, this will still instantly fry the ESP32's 3.3V ADC pin. To protect against this, wire a 3.3V Zener diode (like the 1N5226) in reverse bias directly across the ESP32 ADC pin and ground. If a short occurs, the Zener diode will clamp the voltage at 3.3V and safely shunt the excess current to ground, sacrificing itself to save your microcontroller.