- Home
- PLC Solution Library
- Tools
- 4-20 mA calculator
PLC Solution Library · Engineering tools
4-20 mA calculator: scaling, 0-10 V shunt and PLC input check
To read a 4-20 mA signal on a 0-10 V PLC input, put a 500 Ω resistor between the input and 0 V: 4 mA gives 2 V and 20 mA gives 10 V.Scale it with value = min + (mA - 4) / 16 x (max - min). At 3.6 mA or less, treat it as a fault: broken wire, no power or a low fault signal.
The calculator uses the analog input data of the M-Duino, Ardbox, ESP32 PLC and Raspberry PLC datasheets. Free and with no sign-up: the result is always on screen.
Result
Valid measurement
- Resistor in parallel with the input
- 493.7 Ω
- Input impedance
- 39 kΩ
- Input at full scale (10 V) at
- 20.26 mA
- Reading at 4 mA · at 20 mA
- 202 · 1010
- Useful steps · effective bits
- 808 · 9.7 bit
- One step is
- 19.8 µA · 0.012 bar
- Sees saturation · sees NE 43 high fault
- no · no
- Resistor power at 20 mA · 21 mA
- 0.200 W · 0.221 W
- Error from the tolerance, at 20 mA
- ± 0.125 bar
- Loop
- 14.1 V left at 20 mA (minimum 12.0 V)
A shunt resistor is enough
- With this resistor the input reaches full scale at 20.26 mA: the PLC cannot tell saturation or the NAMUR NE 43 high fault (21 mA or more). If you need them, use 477 Ω or less, for example 470 Ω.
- For 500 Ω there is the IS.ACANTO10-0.5k DIN rail terminal, with the resistor at ±1 %.
The loop current flows from the transmitter into the resistor on input I0.7. The PLC GND, the resistor and the 0 V of the supply share one point.
Code for your PLC · loop420.ino
// 4-20 mA transmitter on M-Duino 21+ input I0.7 (M-Duino: 0-10 Vdc, 10 bit)
// Shunt resistor of 500 ohm between I0.7 and GND turns the loop current into a voltage.
// Generated by the Industrial Shields 4-20 mA calculator: https://www.industrialshields.com/plc-solution-library/tools/4-20ma-calculator
// Check the values on your hardware with a loop calibrator before you use them.
// Arduino IDE with the Industrial Shields AVR boards package. Select your M-Duino model.
const int AI_PIN = I0_7; // analog input used for the loop
const float ADC_MAX = 1023.0; // 10 bit, from the M-Duino Family datasheet
const float AI_RANGE = 10.0; // input range in volts
const float R_SHUNT = 500.0; // shunt resistor in ohm
const float R_INPUT = 39000.0; // input impedance in ohm (0 = not known, treated as ideal)
const float R_EQ = (R_INPUT > 0.0) ? R_SHUNT * R_INPUT / (R_SHUNT + R_INPUT) : R_SHUNT;
const float PV_MIN = 0.000; // value at 4 mA (bar)
const float PV_MAX = 10.000; // value at 20 mA (bar)
// NAMUR NE 43 limits in mA
const float MA_FAULT_LOW = 3.6; // 3.6 mA or less: broken wire or low fault signal
const float MA_RANGE_LOW = 3.8; // 3.6 to 3.8 mA: under range
const float MA_RANGE_HIGH = 20.5; // 20.5 to 21 mA: over range (saturated)
const float MA_FAULT_HIGH = 21.0; // 21 mA or more: high fault signal
const int N_SAMPLES = 16;
float readCounts() {
uint32_t sum = 0;
for (int i = 0; i < N_SAMPLES; i++) {
sum += analogRead(AI_PIN);
}
return (float)sum / N_SAMPLES;
}
float countsToMilliamps(float counts) {
float volts = counts * AI_RANGE / ADC_MAX;
return volts / R_EQ * 1000.0;
}
float milliampsToValue(float mA) {
float pv = PV_MIN + (mA - 4.0) * (PV_MAX - PV_MIN) / 16.0;
if (PV_MIN < PV_MAX) {
if (pv < PV_MIN) pv = PV_MIN;
if (pv > PV_MAX) pv = PV_MAX;
}
return pv;
}
void setup() {
Serial.begin(115200);
pinMode(AI_PIN, INPUT);
}
void loop() {
float counts = readCounts();
float mA = countsToMilliamps(counts);
float pv = milliampsToValue(mA);
Serial.print(mA, 2);
Serial.print(" mA ");
if (MA_FAULT_LOW >= mA) {
Serial.println("LOOP FAULT: broken wire or low fault signal");
} else if (mA >= MA_FAULT_HIGH) {
Serial.println("HIGH FAULT SIGNAL from the transmitter");
} else if (counts >= ADC_MAX - 0.5) {
Serial.print(pv, 2);
Serial.println(" bar (input at full scale: the current can be higher)");
} else {
Serial.print(pv, 2);
Serial.print(" bar");
if (MA_RANGE_LOW > mA) Serial.println(" (under range)");
else if (mA > MA_RANGE_HIGH) Serial.println(" (over range, saturated)");
else Serial.println();
}
delay(500);
}
It reads 16 samples, converts them to mA with the resistor and the input impedance, checks the NAMUR NE 43 limits and scales the value. M-Duino, Ardbox and ESP32 PLC: Arduino IDE with the Industrial Shields boards package. Raspberry PLC: C++ with librpiplc. Comments are in English.
Send me the calculation and the code
We send this configuration, the code and the wiring to your email, and an engineer reviews them for your project.
How the 4-20 mA calculation works
A 4-20 mA transmitter sets a current between 4 and 20 mA in proportion to what it measures. The analog inputs of the M-Duino, Ardbox, ESP32 PLC and Raspberry PLC families measure 0 to 10 V, so a resistor turns the current into a voltage. These are the formulas the calculator uses, in this order.
- Resistor seen by the loop. The input impedance sits in parallel with the shunt:
R_eq = R x Z_in / (R + Z_in) - Voltage at the input. Ohm's law:
V = I x R_eq - PLC reading. With an input of 0 to 10 V and N bits:
reading = V / 10 x (2^N - 1) - Back to mA in the program.
I = reading x 10 / (2^N - 1) / R_eq - Engineering units.
value = min + (I - 4) / 16 x (max - min) - Full scale. The input saturates at
I_fs = 10 V / R_eq. Above it, every current reads the same. - Resistor power.
P = I² x R - Loop voltage, 2-wire. What is left for the transmitter, with R_cable = 2 x length x 0.0175 / section (copper):
V_tx = V_supply - I x (R_eq + R_cable)
Analog inputs of Industrial Shields PLCs for 4-20 mA
All four families have 0-10 Vdc analog inputs. What changes is the resolution and, on the M-Duino and the Ardbox, the 39 kΩ input impedance given in the datasheet. Values with a 500 Ω shunt:
| Family | Resolution | Input impedance | Reading at 4 mA · 20 mA | Full scale at |
|---|---|---|---|---|
| M-Duino | Resolution: 10 bit (0 to 1023) | Input impedance: 39 kΩ | Reading at 4 mA · 20 mA: 202 · 1010 | Full scale at: 20.26 mA |
| Ardbox Analog | Resolution: 10 bit (0 to 1023) | Input impedance: 39 kΩ | Reading at 4 mA · 20 mA: 202 · 1010 | Full scale at: 20.26 mA |
| ESP32 PLC | Resolution: 11 bit (0 to 2047) | Input impedance: Not given (ideal) | Reading at 4 mA · 20 mA: 409 · 2047 | Full scale at: 20.00 mA |
| Raspberry PLC | Resolution: 12 bit (0 to 4095) | Input impedance: Not given (ideal) | Reading at 4 mA · 20 mA: 819 · 4095 | Full scale at: 20.00 mA |
ESP32 PLC 14: inputs AI0.0 and AI0.1 read 4-20 mA directly in 4-20 mA mode, with no shunt. This calculator covers the 0-10 V inputs.
On the M-Duino 21+, the ESP32 PLC 21+ and the Raspberry PLC 21, the analog inputs are I0.7 to I0.12; larger models add more zones with the same pattern. On the Ardbox Analog they are I0.2 to I0.9.
With an ideal input and 500 Ω, 20 mA lands exactly on 10 V, so the ESP32 PLC and the Raspberry PLC have no margin above 20 mA: 470 Ω gives that margin.
Worked examples
| Case | Input | Result |
|---|---|---|
| Pressure 0 to 10 bar, M-Duino, 500 Ω | Input: 12 mA | Result: 5.92 V, reading 606, 5.00 bar |
| Level 0 to 4 m, ESP32 PLC, 500 Ω | Input: reading 1000 | Result: 9.77 mA, 1.44 m |
| Temperature -50 to 150 °C, Raspberry PLC, 470 Ω | Input: 21 mA | Result: 9.87 V, reading 4042: NAMUR NE 43 high fault. With 500 Ω it would read 4095, full scale, and the fault would look like 150 °C. |
| Broken wire, Ardbox Analog, 500 Ω | Input: 0 mA | Result: 0 V, reading 0: fault, below 3.6 mA |
| 250 Ω on an M-Duino, 0 to 10 bar | Input: 20 mA | Result: 4.97 V, reading 508: half of the range, 0.025 bar per step instead of 0.012 bar with 500 Ω |
| 2-wire loop at 24 V, 300 m of 0.5 mm², ESP32 PLC, 500 Ω | Input: 20 mA | Result: Cable 21 Ω, 13.58 V left for the transmitter |
How to connect a 4-20 mA transmitter to a 0-10 V PLC input
- Choose the resistor. 500 Ω for a 0-10 V input, or 470 Ω if you need to see the NAMUR NE 43 high fault. Use 1 % or better and at least 0.5 W.
- Wire the loop. 2-wire: +24 V to the + of the transmitter, the - of the transmitter to the analog input, and the resistor between the input and 0 V. 3-wire and 4-wire: the output of the transmitter to the input, its 0 V to the common 0 V.
- Check the loop voltage. At 20 mA the supply must still leave the minimum voltage of the transmitter after the resistor and the cable.
- Scale and check faults in the program. Convert the reading to mA, check the NAMUR NE 43 limits, then scale to units. Check 4, 12 and 20 mA with a loop calibrator.
Shunt resistor or isolated converter?
A shunt resistor is enough when the transmitter and the PLC share the same 0 V, the loop has only one grounded receiver and the cable stays in a clean cabinet. The IS.ACANTO10-0.5k is that shunt in DIN rail format: a feed-through terminal with a 500 Ω resistor at ±1 %.
Use an isolated 4-20 mA to 0-10 V signal converter when:
- the transmitter or its supply has another ground reference, such as a 4-wire instrument fed from another panel;
- another device with a grounded input, such as a second controller or a recorder, reads the same loop;
- the cable runs between buildings or next to drives and power cables, and the readings move.
The datasheets of the M-Duino, the Ardbox, the ESP32 PLC and the Raspberry PLC say that the equipment has no galvanic isolation between the grounds of different systems, and recommend galvanic isolators when the grounds are different.
Broken wire and saturation: NAMUR NE 43 limits
| Current | Meaning | What the program does |
|---|---|---|
| 3.6 mA or less | Fault: broken wire, no power or low fault signal | Program: Alarm, no value |
| 3.6 to 3.8 mA | Under range | Program: Value at the bottom of the scale, with a flag |
| 3.8 to 20.5 mA | Valid measurement | Program: Scaled value |
| 20.5 to 21 mA | Over range: the transmitter is saturated | Program: Value at the top of the scale, with a flag |
| 21 mA or more | Fault: high fault signal | Program: Alarm, no value |
To see the last two rows the input must not saturate before 21 mA. With 500 Ω it saturates at 20.0 to 20.3 mA; with 470 Ω, at 21.3 to 21.5 mA.
Example code: 4-20 mA on an M-Duino
This is the code the calculator gives for a 0 to 10 bar transmitter on input I0.7 of an M-Duino with 500 Ω. Change the values above and copy your version from the calculator.
// 4-20 mA transmitter on M-Duino 21+ input I0.7 (M-Duino: 0-10 Vdc, 10 bit)
// Shunt resistor of 500 ohm between I0.7 and GND turns the loop current into a voltage.
// Generated by the Industrial Shields 4-20 mA calculator: https://www.industrialshields.com/plc-solution-library/tools/4-20ma-calculator
// Check the values on your hardware with a loop calibrator before you use them.
// Arduino IDE with the Industrial Shields AVR boards package. Select your M-Duino model.
const int AI_PIN = I0_7; // analog input used for the loop
const float ADC_MAX = 1023.0; // 10 bit, from the M-Duino Family datasheet
const float AI_RANGE = 10.0; // input range in volts
const float R_SHUNT = 500.0; // shunt resistor in ohm
const float R_INPUT = 39000.0; // input impedance in ohm (0 = not known, treated as ideal)
const float R_EQ = (R_INPUT > 0.0) ? R_SHUNT * R_INPUT / (R_SHUNT + R_INPUT) : R_SHUNT;
const float PV_MIN = 0.000; // value at 4 mA (bar)
const float PV_MAX = 10.000; // value at 20 mA (bar)
// NAMUR NE 43 limits in mA
const float MA_FAULT_LOW = 3.6; // 3.6 mA or less: broken wire or low fault signal
const float MA_RANGE_LOW = 3.8; // 3.6 to 3.8 mA: under range
const float MA_RANGE_HIGH = 20.5; // 20.5 to 21 mA: over range (saturated)
const float MA_FAULT_HIGH = 21.0; // 21 mA or more: high fault signal
const int N_SAMPLES = 16;
float readCounts() {
uint32_t sum = 0;
for (int i = 0; i < N_SAMPLES; i++) {
sum += analogRead(AI_PIN);
}
return (float)sum / N_SAMPLES;
}
float countsToMilliamps(float counts) {
float volts = counts * AI_RANGE / ADC_MAX;
return volts / R_EQ * 1000.0;
}
float milliampsToValue(float mA) {
float pv = PV_MIN + (mA - 4.0) * (PV_MAX - PV_MIN) / 16.0;
if (PV_MIN < PV_MAX) {
if (pv < PV_MIN) pv = PV_MIN;
if (pv > PV_MAX) pv = PV_MAX;
}
return pv;
}
void setup() {
Serial.begin(115200);
pinMode(AI_PIN, INPUT);
}
void loop() {
float counts = readCounts();
float mA = countsToMilliamps(counts);
float pv = milliampsToValue(mA);
Serial.print(mA, 2);
Serial.print(" mA ");
if (MA_FAULT_LOW >= mA) {
Serial.println("LOOP FAULT: broken wire or low fault signal");
} else if (mA >= MA_FAULT_HIGH) {
Serial.println("HIGH FAULT SIGNAL from the transmitter");
} else if (counts >= ADC_MAX - 0.5) {
Serial.print(pv, 2);
Serial.println(" bar (input at full scale: the current can be higher)");
} else {
Serial.print(pv, 2);
Serial.print(" bar");
if (MA_RANGE_LOW > mA) Serial.println(" (under range)");
else if (mA > MA_RANGE_HIGH) Serial.println(" (over range, saturated)");
else Serial.println();
}
delay(500);
}
Related guides, cases and simulators
- How to convert a 4-20 mA signal to 0-10 V in an Arduino PLC: the full guide, with the wiring photos.
- IS.ACANTO10-0.5k: DIN rail terminal with a 500 Ω resistor for 4-20 mA to 0-10 V.
- 4-20 mA level sensor reading with an ESP32 PLC, in the PLC Solution Library.
- Steam boiler automation: a pressure transmitter on an ESP32 PLC with a 500 Ω shunt.
- Water treatment: pH and chlorine sensors on the analog inputs of an ESP32 PLC 42.
- Small hydro plant: plant signals on a Raspberry PLC 21, with 4-20 mA transmitters through a shunt.
- Oil well remote monitoring: an ESP32 PLC 21 at a remote site, and when to use 4-20 mA wiring instead of wireless sensors.
- Process plant simulator: start a small hydro unit or a pumping station and watch the analog values.
- Remote site telemetry simulator: pressures and levels of a water network, with alarms.
- All PLC simulators
Questions about 4-20 mA signals
Which resistor do I need to read 4-20 mA on a 0-10 V PLC input?
Use 500 Ω for an M-Duino or an Ardbox: with their 39 kΩ input impedance in parallel, 500 Ω gives 493.7 Ω, so 4 mA reads 1.97 V and 20 mA reads 9.87 V.
On an input with an ideal impedance, such as the ESP32 PLC and the Raspberry PLC in the calculator, 500 Ω puts 20 mA exactly at 10 V, with no margin: 470 Ω gives margin and lets you see the NAMUR NE 43 high fault.
Choose 1 % or better. Full guide: How to convert a 4-20 mA signal to 0-10 V in an Arduino PLC.
How do I scale a 4-20 mA signal to engineering units?
Use value = min + (mA - 4) / 16 x (max - min), where min is the value at 4 mA and max the value at 20 mA.
For a 0 to 10 bar transmitter, 12 mA is 0 + 8 / 16 x 10 = 5 bar. The reverse is mA = 4 + 16 x (value - min) / (max - min).
How does a PLC detect a broken wire on a 4-20 mA loop?
A broken wire drops the current to 0 mA, below the live zero of 4 mA.
NAMUR NE 43 treats 3.6 mA or less as a fault, 3.6 to 3.8 mA as under range, 3.8 to 20.5 mA as a valid measurement, 20.5 to 21 mA as over range and 21 mA or more as a high fault. The program checks the current against these limits before it scales the value.
Why can the PLC not see 21 mA with a 500 Ω resistor?
Because 500 Ω turns 20 mA into 10 V, the top of a 0-10 V input. Anything above that reads as full scale, so the PLC cannot tell over range from a high fault.
If you need the NAMUR NE 43 high fault, use 470 Ω: the full scale moves to 21.28 mA with an ideal input, or 21.53 mA on an M-Duino or an Ardbox, and you lose about 6 % of the resolution.
What power rating does the shunt resistor need?
P = I² x R. A 500 Ω resistor dissipates 0.2 W at 20 mA and 0.22 W at 21 mA. A rating of at least twice that, 0.5 W, keeps it cool and stable. The calculator gives the figure for your resistor.
When do I need an isolated converter instead of a shunt resistor?
A shunt is enough when the transmitter and the PLC share the same 0 V and nothing else with a grounded input sits on the loop.
Use an isolated 4-20 mA to 0-10 V converter when the transmitter or its supply has another ground reference, when another grounded device reads the same loop, or when a long cable picks up noise.
The datasheets of the M-Duino, Ardbox, ESP32 PLC and Raspberry PLC say the equipment has no galvanic isolation between the grounds of different systems.
How do Industrial Shields PLCs read a 4-20 mA signal?
The ESP32 PLC 14 reads it directly: its analog inputs AI0.0 and AI0.1 have a 4-20 mA mode that you select in software.
The M-Duino, Ardbox, ESP32 PLC and Raspberry PLC families have 0-10 Vdc analog inputs (10 bit on the M-Duino and the Ardbox, 11 bit on the ESP32 PLC, 12 bit on the Raspberry PLC).
On these, a 4-20 mA transmitter connects through a 500 Ω shunt, such as the IS.ACANTO10-0.5k DIN rail terminal, or through an isolated converter.
Send me the calculation and the code
The form sends your current settings with it. We reply by email with the calculation, the code for your PLC and the wiring, and an engineer reviews them for your project.