Saturday, November 5, 2016

Arduino 4-20 mA input

This is a quick discussion of how to feed a 4-20 mA signal into an Arduino. Looking for 4-20 mA output from an Arduino? See here.

I bought a pressure transmitter off of ebay that outputs a 4-20 mA current signal, because I like to live it up. The 4-20 mA current is very common in industrial control systems, so it's relatively easy to find various transmitters with this output. However, an Arduino can't natively read a current signal with its analog input pins - they measure voltage. We need to convert this current signal to a voltage. Fortunately, this is a piece of cake to do with a simple circuit.


Precious peppy pressure


The transmitter I bought is a Setra 209 model (datasheet here). It takes a 9-30V input, and acts as a current controller to put out a 4 to 20 milliamp signal proportional to the pressure it senses, with a span of zero to 100 psig. Plotting it out:



We can use a simple resistor circuit to generate a voltage readable to the Arduino that is proportional to the current. The Arduino AnalogRead function gives a 0-1023 value output proportional to the measured voltage on the analog input pin, with 0 corresponding to 0V or Ground (duh) and 1023 corresponding to the reference voltage (5V for the plain jane Arduino if nothing's connected to the Aref pin).


That's really it. I don't think electronics gets any easier than this.

The standard Arduino's analog input pins can read zero to 5 volts. If 5 volts is our maximum signal at 20 milliamps, that requires a resistance of 250 ohms:


R = V/I

R = 5 V/ 0.02 A = 250 ohms

If this were instead a 3.3V microcontroller, we'd require a 165 ohm resistor to read the full range output by the same math. Whichever reference voltage, you can use a lower resistor, but it cuts down the readable span.

We can now convert the measured voltage back to whatever units our signal is in. To do this, we just need a little algebra! We can plot an x-y chart, where the x-axis is the measured current/voltage/AnalogRead output and the y-axis is the process variable (pressure in this case). 

I live my life by the principles of slope intercept form

Usually, a precision resistor is used for the reference resistor, with a low temperature coefficient (change in resistance with temperature), but for my purposes, an ordinary resistor suffices. I had a nominally 220 ohm resistor I measured as 217 ohms actual resistance, and a 33 ohm resistor that measured true, which combined in series gave 250 ohms. We don't need exactly the correct resistance, though - we can compensate in code. Here's everything breadboarded up - not much to it:


The transmitter worked like a champ, with the measured signal tracking the manual gauge I had connected to it as I varied the pressure.


One other nice feature of the 4-20 mA signal scheme is that the low end of the signal output is 4 milliamps, not zero. This makes it easy to detect errors, as happened on the above data logging run when I bumped the wire around 10,000 milliseconds in. The raw reading went to zero, when it should have bottomed out at about 200 if it were a real zero signal. This tells me that something is up with the wiring at this point, and the data can't be trusted. If I wrote a program to use this transmitter reading as an input, I would know to handle any zero signal readings as an error, not as a true data point.


Here's the code: 
float PressureReading;     
float ResistorValue = 250;  //reference resistor resistance
float Pmax = 100;           //Max of process variable range
float Pmin = 0;             //Min of process variable range
float Vref = 5;             //Reference voltage for AnalogRead


void setup() {
    Serial.begin(9600);
    Serial.println("Time, Raw reading, Corrected reading");
}

void loop() {
  Serial.print(millis());
  Serial.print(",");

  //Read the voltage across the reference resistor
  PressureReading = analogRead(A3);
  
  Serial.print(PressureReading);
  Serial.print(",");

  //Convert the voltage signal to a pressure reading
  PressureReading =(Pmax-Pmin)*Vref/(1023*ResistorValue*(0.02-0.004))*(PressureReading-1023*0.004*ResistorValue/Vref);

  Serial.println(PressureReading);
  
  delay(500);
  
}

No comments:

Post a Comment