The Sensing Principle and 4-20mA Output Standard

Industrial pressure transducers typically use a piezoresistive Wheatstone bridge embedded in a 316L stainless steel diaphragm. As fluid pressure deflects the diaphragm, the strain gauges change resistance, unbalancing the bridge and producing a raw millivolt signal proportional to the applied mechanical force. This raw signal is highly susceptible to noise and temperature drift, so the sensor's internal ASIC immediately amplifies and conditions it.

Instead of outputting a fragile 0-5V analog signal that degrades over long wire runs due to cable resistance, the sensor conditions the signal into a 4-20mA current loop sensor output. In a current loop, the transmitter actively varies its internal resistance to maintain a precise current flow through the entire circuit, regardless of wire length or voltage drop (up to the power supply's compliance limit). 4mA represents 0 PSI (allowing the system to detect a broken wire at 0mA), and 20mA represents the full-scale pressure, typically 100 PSI or 200 PSI depending on the model.

Hardware Specs and Wiring the Current Loop

To interface this industrial current output with a 3.3V microcontroller like the ESP32, we must convert the current back into a voltage using a precision shunt resistor. While a standard 250Ω shunt yields 1-5V (standard for industrial PLCs), that 5V maximum will fry the ESP32's GPIO pins. By dropping to a 150Ω precision shunt, we scale the output to 0.6V–3.0V, safely maximizing the ESP32's 12-bit ADC resolution without risking silicon damage.

Table 1: Component Specifications and Operating Ranges
Component / Parameter Specification / Value Notes & Constraints
Pressure Transducer 0-100 PSI, 4-20mA, 316L SS Generic industrial (e.g., Hilitchi, US Sensor Corp). Cost: ~$18-$25.
Sensor Supply Voltage 9V to 32V DC Use a dedicated 12V or 24V DC switching supply. Do not share with ESP32 5V rail.
Shunt Resistor 150Ω, 0.1% tolerance, 1/4W Metal film precision. Yields 0.6V at 4mA and 3.0V at 20mA.
ESP32 ADC Input GPIO 34 (ADC1_CH6) Input-only pin. Max safe voltage 3.3V. Requires 11dB attenuation.
Loop Compliance Voltage V_supply - V_shunt With 24V supply and 3V shunt drop, 21V remains for the sensor (well within 9-32V range).

Wiring Pinout and Connections

The shunt resistor must be placed on the low side (ground-referenced) of the loop. If you place it on the high side, the ESP32 will attempt to read a voltage referenced to 24V, instantly destroying the microcontroller.

Table 2: Wiring Map for ESP32 DevKit V1
Source Destination Wire Color / Gauge
24V PSU Positive (+) Sensor Red Wire (V+) Red, 22 AWG
Sensor Black Wire (Signal+) Shunt Resistor Leg 1 Black, 22 AWG
Shunt Resistor Leg 1 ESP32 GPIO 34 Yellow, 22 AWG (Keep short, < 6 inches)
Shunt Resistor Leg 2 24V PSU Negative (-) AND ESP32 GND Blue, 22 AWG (Common ground point)
Bench Tip: Always connect the ESP32 GND and the 24V PSU Negative to the exact same physical terminal block or breadboard rail. A ground potential difference of even 0.2V between the two power supplies will introduce a massive offset error in your pressure readings.

Converting Raw ADC Reads to Physical Units

The math to convert the sensor output from a raw ADC reading to PSI requires three steps: converting the ADC register to millivolts, converting millivolts to milliamps via Ohm's Law, and mapping the 4-20mA range to the 0-100 PSI physical range.

The Math:
1. Voltage (V): V_shunt = ADC_mV / 1000.0
2. Current (mA): I_loop = (V_shunt / 150.0) * 1000.0
3. Pressure (PSI): PSI = ((I_loop - 4.0) / 16.0) * 100.0

Historically, hobbyists used analogRead() and mapped the 0-4095 raw value. However, the ESP32's ADC is notoriously non-linear at the extremes (below 0.1V and above 3.1V). Modern ESP32 Arduino Core (v2.x and v3.x) includes analogReadMilliVolts(), which uses the chip's factory-calibrated eFuse data to return a highly accurate millivolt reading, bypassing the worst of the non-linearity.

// ESP32 4-20mA Pressure Sensor Interfacing Code
// Tested on ESP32 DevKit V1, Arduino Core v3.0.x

const int SENSOR_PIN = 34;      // ADC1_CH6, input only
const float SHUNT_OHMS = 150.0; // Precision shunt resistor value
const float MAX_PSI = 100.0;    // Sensor full-scale rating
const float ADC_MIN_MV = 600.0; // 4mA * 150 ohms
const float ADC_MAX_MV = 3000.0;// 20mA * 150 ohms

void setup() {
  Serial.begin(115200);
  analogSetAttenuation(ADC_11db); // Required for 0-3.1V range
  pinMode(SENSOR_PIN, INPUT);
  Serial.println("System Initialized. Waiting for stable loop...");
  delay(1000);
}

void loop() {
  // Read voltage in millivolts using factory calibration
  int adc_mV = analogReadMilliVolts(SENSOR_PIN);
  
  // Convert mV to Loop Current (mA)
  float loop_mA = (float)adc_mV / SHUNT_OHMS;
  
  // Check for broken wire or short circuit (Outside 3.8mA - 21mA range)
  if (loop_mA < 3.8) {
    Serial.println("ERROR: Broken wire or sensor unpowered (< 4mA)");
  } else if (loop_mA > 21.0) {
    Serial.println("ERROR: Sensor shorted or over-pressurized (> 20mA)");
  } else {
    // Constrain to valid 4-20mA window to prevent negative PSI math errors
    loop_mA = constrain(loop_mA, 4.0, 20.0);
    
    // Map 4-20mA to 0-100 PSI
    float pressure_PSI = ((loop_mA - 4.0) / 16.0) * MAX_PSI;
    
    Serial.print("Loop Current: ");
    Serial.print(loop_mA, 2);
    Serial.print(" mA | Pressure: ");
    Serial.print(pressure_PSI, 1);
    Serial.println(" PSI");
  }
  
  delay(500); // 2Hz sampling rate
}

Calibration and Scaling in the Real World

Even with analogReadMilliVolts(), you may see a 1-2% offset due to the shunt resistor's actual tolerance and minor ground bounce. For precision applications, perform a 2-point calibration. Connect a calibrated digital multimeter in series with the loop. Read the actual mA at 0 PSI (should be ~4.00mA) and at a known pressure (e.g., 50 PSI, should be ~12.00mA). Update the ADC_MIN_MV and ADC_MAX_MV constants in the code to match your multimeter's exact measurements multiplied by 150.

Troubleshooting Interference and Signal Dropout

While 4-20mA loops are inherently immune to voltage drop over long distances, they are not immune to Electromagnetic Interference (EMI). When routing sensor output cables near Variable Frequency Drives (VFDs), large contactors, or AC mains, the cables can act as antennas, inducing high-frequency common-mode noise that the ESP32's ADC will interpret as pressure spikes.

Table 3: Common Interference Sources and Mitigation Strategies
Interference Source Symptom on ESP32 Hardware / Software Mitigation
VFDs and AC Motors Erratic, high-frequency jitter (±5 PSI swings) Use Shielded Twisted Pair (STP) cable. Ground the shield at the ESP32 end ONLY to prevent ground loops.
Switching Power Supplies Constant high-frequency ripple on ADC Solder a 100nF (0.1µF) ceramic capacitor directly across the 150Ω shunt resistor legs to form a low-pass filter.
Long Wire Runs (>50ft) Readings consistently 1-2 PSI lower than expected Verify PSU compliance voltage. If the 24V sags below 12V under load, the sensor's internal ASIC will starve and drop the current output.
ADC Quantization Noise Last decimal digit flickers randomly Implement software oversampling: read the ADC 16 times in a tight loop, average the results, then calculate PSI.

According to Texas Instruments' application notes on current loop design, the physical twisting of the signal wires is what rejects common-mode magnetic interference, while the shield handles capacitive electric field coupling. Never use untwisted ribbon cable or loose jumper wires for the final installation in an industrial enclosure.

Finally, if your ESP32 is resetting randomly when the sensor is connected, you likely have a ground loop injecting noise directly into the microcontroller's reset pin. In these stubborn cases, abandon the direct shunt method and use an isolated 4-20mA receiver module (like the XTR115 or a cheap opto-isolated ADC board) to physically break the galvanic connection between the 24V industrial ground and your 3.3V logic ground.