Introduction: 4-20ma Generator/Tester Using Arduino

4-20mA generators are available on ebay, but I for one love the DIY part of things and using parts I have laying around.

I wanted to test our PLC’s analog inputs to verify our scada readings and to test the output of 4-20mA instruments. There are loads of current to voltage converters and voltage to current converters for arduino on ebay, they need calibration though. I can use this to calibrate any of those converters found on ebay and the likes.

I decided that I will DIY a generator and tester. At this point in time it is still a work in progress and a prototype.

I had an old 2.1 sound system that was not being used (small speakers). So I used one of the speaker boxes as an enclosure. I also had an amplifier that died because of lightning, I removed the speaker terminal from that amplifier to make connecting up a breeze. I intend to make a PCB in the future and a better enclosure.


Parts List.

LCD // 20x4 (adapt code if yours is smaller)

LM7808 // 8volt regulator

LED // Any type or size

Resistor for LED // Appropriate for the LED type and 8volt

100 ohm resistor + 47 ohm resistor in series // Will be used as shunt resistor

10K resistor // Arduino analog in protection against high voltage

22K resistor // To stop A0 from floating

Trimpot 100 ohm + 47 ohm resistor in series // PT100 simulator

35 volt capacitor // I used 470uF, just to keep supply voltage fluctuations down

RTD (PT100 transducer) //Span does not matter (range)

DIODE (for polarity protection)



Step 1:

Following the schematic should get you started on where to add the parts and wire them.

The LM7808 allows a maximum of 25volts input which is fine for PLC systems, they generally use 24volt power supplies. Add a heatsink to the regulator and do not use it for extended periods. Dropping 16volts causes the regulator to generate a lot of heat.

The input supply feeds the regulator and connects to the INA219 VIN, in this configuration the INA219 will also be able to measure the correct supply voltage minus the voltage drop from the diode. You should measure your diode voltage drop and add it to the code so that you get the correct supply voltage readout.

From the INA219 VOUT to the RTD+ powers up the RTD. RTD- to ground completes the circuit.

To test a PLC analog card you would connect RTD- to the input on the analog card and the ground from the card to arduino ground. (Make sure to disconnect any instrument attached to the channel being tested).

R5 and LED1, indicating system is powered up.

The regulator feeds into the arduino VIN (arduino has built in regulator to 5volts).

Arduino 5V pin goes to INA219 to power the on-board chip. INA219 GND to arduino ground.

Trim pot wiper to RTD PIN1 and Trim pot pin 3 to RTD pin 2 will simulate a PT100 connection. (Swap the wires if turning the trim pot clockwise does not increase the mA).

Step 2: Instrument Output Test

To test instrument output extra parts is needed, like a shunt resistor. Normal 0.25W resistors will do the job just fine. You can leave the shunt resistor and add a second INA219 to test instrument output. I only had one left so I used a resistor instead.

Testing using a shunt can only be done on the device’s negative side. If you use the positive side you will supply your arduino with more than 4 times the allowed voltage and let the smoke out.

Add the shunt resistor in series with negative wire of the instrument. The side of the shunt closest to the device will become the positive analog for arduino. The other side of the shunt closest to the power supply will become the arduino ground completing the analog input circuit.

150 ohm shunt resistor is the absolute maximum that should be used when using an arduino. The resistor has a voltage drop linear to the mA flowing through it. The greater the mA the greater the voltage.

At 20mA current # 150ohm*0.02A = 3volt to arduino.

At 4mA current # 150ohm*0.004A = 0.6volt to arduino.

Now you might want the voltage to be closer to 5 volts so that you can us the full ADC range of the arduino. (Not a good idea).

RTD’s can reach 30.2mA output (Mine does). 150ohm*0.03A = 4.8volts. That is as close as I would like to be.

Another website indicated to use a 250ohm resistor.

At 20mA current # 250ohm*0.02A = 5volt to arduino.

At 30mA current # 250ohm*0.03A = 7.5volt to arduino.

You risk burning your ADC and arduino.

To test an instrument out in the field, take a 12volt battery with you and connect it to the supply input. Using an external power source will not influence the current PLC setup.

To test an analog input card in the field, take a 12volt battery with you. Disconnect the instrument + from the circuit. Connect ground to instrument ground and the RTD- to the disconnected instrument wire.

Step 3: Calibration

To calibrate your shunt resistor reading, wire RTD- to the shunt Analog in. Set your trim pot so that the generated mA is 4mA. If your device mA is not equal then modify the first value in the code at line 84. Increasing this value will lower the mA readout.

Then set your trim pot to generate 20mA. If your device mA is not equal then modify the second value in the code at line 84.

So your 4-20mA will now become 0.6-3volts (theoretical). More than enough range. Using the library from eRCaGuy, oversampling will give you a better and stable readout.

Hopefully you read this. This is my first instructable, so please take it easy if I have made a mistake somewhere or left something out.

This project is probably not the best way to go about it, but it works for me and was fun doing it.

Some ideas I have extra…

Add a servo to rotate the trim pot inside the box.

Add push buttons to rotate servo left or right.

Add a digital temperature sensor to the regulator heatsink to warn for dangerous heat.

Step 4: Programming Arduino

#include <Wire.h>

// #include <LiquidCrystal_SR.h> //Uncomment if you use an LCD with a shift register.

#include <LiquidCrystal.h>

#include <TimeLib.h>

#include <Adafruit_INA219.h>

#include <eRCaGuy_NewAnalogRead.h>

//A4 = (SDA)

//A5 = (SCL)

Adafruit_INA219 ina219;

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

// LiquidCrystal_SR lcd(3, 4, 2); //Uncomment if you use an LCD with a shift register.

// | | |____ Latch Pin

// | \______ Clock Pin

// \________ Data/Enable Pin

byte bitsOfResolution = 12; //commanded oversampled resolution

unsigned long numSamplesToAvg = 20; //number of samples AT THE OVERSAMPLED RESOLUTION that you want to take and average

ADC_prescaler_t ADCSpeed = ADC_DEFAULT;

unsigned long previousMillis = 0;

float shuntvoltage = 0.0; //From INA219

float busvoltage = 0.0; //From INA219

float current_mA = 0.0; //From INA219

float loadvoltage = 0.0; //From INA219

float arduinovoltage = 0.0; //Voltage calculation from A0 pin

Unsigned long A0analogReading = 0;

byte analogIn = A0;

float ma_mapped = 0.0; //Map voltage from A0 to 4-20mA

void setup() {




uint32_t currentFrequency;


ina219.setCalibration_32V_30mA(); //Modified library for more precision on mA

lcd.begin(20, 4); // initialize the LCD


lcd.home (); // go home





void loop() {

unsigned long currentMillis = millis();

const long interval = 100;


Read I2C devices at intervals and do some calculations &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&

if (currentMillis - previousMillis >= interval) {

previousMillis = currentMillis;



Print_To_LCD(); // I probably don’t need to update the LCD this fast and can be moved to below Interval()



Interval() {

shuntvoltage = ina219.getShuntVoltage_mV();

busvoltage = ina219.getBusVoltage_V();

current_mA = ina219.getCurrent_mA();

loadvoltage = (busvoltage + (shuntvoltage / 1000)) + 0.71; // +0.71 is my diode voltage drop

A0analogReading = adc.newAnalogRead(analogIn);

arduinovoltage = (5.0 * A0analogReading); //Calculated to mV

ma_mapped = map(arduinovoltage, 752, 8459, 30, 220) / 10.0; //Map cannot use floats. Add a 0 behind the mapped value and divide by 10 to get float readout.

//Mapping from voltage calculation gives more stable readout then using the raw adc reading.

if (shuntvoltage >= -0.10 && shuntvoltage <= -0.01 ) //With no load the INA219 tends to read below -0.01, well mine does.


current_mA = 0;

busvoltage = 0;

loadvoltage = 0;

shuntvoltage = 0;




Print_To_LCD() {

lcd.setCursor(0, 0);

if (ma_mapped < 1.25) { //With no current this is my mA reading, so I just chuck it away.

lcd.print("* 4-20mA Generator *");


else {

lcd.print("** Analog Tester **");


lcd.setCursor(0, 1);

lcd.print("Device :");

lcd.setCursor(10, 1);

if (ma_mapped < 1.25) {

lcd.print("no device ");


else {



lcd.print("mA ");

lcd.setCursor(0, 2);


lcd.setCursor(10, 2);


lcd.print("mA ");

lcd.setCursor(0, 3);

lcd.print("Supply :");

lcd.setCursor(10, 3);


lcd.print("V ");


Step 5: Some More Photos

Amplifier speaker terminal. LED driven by the current generator (RTD). Analog card wiring will replace the LED.

Terminal on the far left is for supply input. Terminals on the right are for instrument input.

Step 6: Fitting In

Everything seems to fit. I used silicone to temporarily hold some stuff together. The trim pot is siliconed to the top right. A small hole was predrilled. I can adjust the current from the top of the box.

Step 7: Just Photos

Step 8: Final Words

I have tested this device's output with an Allan Bradley PLC. The results was very good. I got full range.
I have also tested this device with 4-20mA pressure sensor that has a built in LCD display. Again results was very good. My readings was off by a couple of decimals.

I write my arduino code in tabs. In PLC's they are called sub routines. Makes debugging easier for my.

Attached are text files of those tabs.