3
\$\begingroup\$

I'm new to the world of electronics and could really use your expertise. I'm attempting to create an I/V converter for a NOSHOK series 653 pressure sensor, and you can find the datasheet at this link: https://www.farnell.com/datasheets/3932164.pdf

My sensor operates on a 4 mA to 20 mA 2-wire setup and is powered by 18V. I'm aiming to capture pressure information using an Arduino Nano, converting it to a range of 0 to 4.5V. To achieve this, I believe I need an operational amplifier (OPAMP). In my research, I came across the INA122 OPAMP, but I'm uncertain if it's the right choice for this project. Frankly, I'm completely lost when it comes to the circuit design.

As for the Arduino code I've developed:

const int sensorPin = A0; // Conversor analog pin
float voltage;
float mappedValue; // Value mapped to 0-4.5V range
unsigned long previousMillis = 0;
const long interval = 1000; // Reading interval in milliseconds (1second)

void setup() {
  Serial.begin(9600); // Initialization of serial communication
}

void loop() {
  unsigned long currentMillis = millis(); // Get the current time
  
  if (currentMillis - previousMillis >= interval) { // Check if the interval has passed
    previousMillis = currentMillis; // Update previous time

    int sensorValue = analogRead(sensorPin); // Analog pin reading
    
    // Convert the read value to voltage (0-5V)
    voltage = sensorValue * (5.0 / 1023.0);

    // Map the read value to the range of 0-4.5V
    mappedValue = map(voltage, 0, 5, 0, 4.5);
    //Print the values to the serial monitor
    Serial.print("Raw Sensor Value: ");
    Serial.print(sensorValue);
    Serial.print("\tVoltage: ");
    Serial.print(voltage);
    Serial.print("V\tMapped Voltage: ");
    Serial.print(mappedValue);
    Serial.println("V");
  }
}

If anyone could offer additional information, considerations, or examples that I should take into account, I'd greatly appreciate it. I'm eager to learn more.

\$\endgroup\$
2
  • 3
    \$\begingroup\$ You probably only need a resistor. R=V/I = 5 / 0.02 = 250ohms. The opamp can remove the 4mA offset which gives you more resolution. \$\endgroup\$
    – Kartman
    Nov 21, 2023 at 22:30
  • 1
    \$\begingroup\$ Added a complete design to my answer. Feel free to ask questions if you have any. \$\endgroup\$ Nov 22, 2023 at 12:39

2 Answers 2

4
\$\begingroup\$

For a simple setup, i would use this approach:

NOTE: I don't fully understand why you want a 0-4.5V signal range?

schematic

simulate this circuit – Schematic created using CircuitLab

NOTE: This circuit assumes, that one side of your current loop can be GND referenced and safely connected to the same potential as your PC-GND and therefore earth! The sensor you linked, can be used in such arrangement if you can connect your e.g. 24V supply to the same GND as your PC is on (e.g. in a Test-Setup)!

(1) The 250Ohm resistor transform the 4-20mA signal into a 1-5V signal. This signal can then be safely sampled by your arduino, as its max range is 0-5V.

(2) The code must (deduced from your example) print the current sensor value in a loop. I added a 0-100% and a 0-10.000psig output to reflect the 4-20mA range and the current pressure.

#define PINMAP_ADC A0           /* use ADC0 to sample */
#define INTERVALL_MS 1000       /* 1s between samples */
#define SENSOR_FSR 100          /* Pressure in [psig] per % of 4-20mA Range. Example is 10,000 psig FSR */

unsigned long previousMillis = 0;

void setup() {
    //Init Serial with 9k6 8n1
    Serial.begin(9600); 
    
    //Check if Sensor connected
    //Check time to ensure intervall
    //If expired, update current time
    while( (float)(analogRead(PINMAP_ADC)* (5.0 / 1023.0)) < 1.0 ){
        unsigned long currentMillis = millis(); 
        if (currentMillis - previousMillis >= INTERVALL_MS) { 
            previousMillis = currentMillis;
            Serial.println("Sensor not connected....");
        }
    }
    
    //Print the CSV header if sensor is ready
    Serial.println("Raw_ADC_Value[cnts,0-1023];Voltage[V,0-5];Current_FSR[%,0-100];Pressure[psig,0-10.000];SampleValid[bool,0-1]");
}

void loop() {
    //Check time to ensure intervall
    //If expired, update current time
    unsigned long currentMillis = millis(); 
    if (currentMillis - previousMillis >= INTERVALL_MS) { 
        previousMillis = currentMillis;

        //Read in Sensor data ranging 0...1023
        //Scale it to raw Voltage (0...5V)
        //Scale it to % (4-20mA/1-5V Range)
        //Scale it to [bar]
        int     rawAdcCnts = analogRead(PINMAP_ADC);            //0...1023
        float   rawAdcVoltage = rawAdcCnts * (5.0 / 1023.0);    //0...5
        float   scaledIPercen = ( rawAdcVoltage - 1 )*25.0;     //0...100
        float   scaledIBar    = scaledIPercen * SENSOR_FSR      //0...FSR

        //Check if sensor is connected
        //and does not have an error
        if ( rawAdcVoltage >= 1.0 ){
            //Print the values to the serial monitor
            Serial.print(rawAdcCnts);
            Serial.print(";");
            Serial.print(rawAdcVoltage);
            Serial.print(";");
            Serial.print(scaledIPercen);
            Serial.print(";");
            Serial.print(scaledIBar);
            Serial.print(";");
            Serial.println("1");
        } else {
            Serial.print(";");
            Serial.print(rawAdcVoltage);
            Serial.print(";;;");
            Serial.println("0");
        }
    }
    
    //Idle
}

NOTE: Due to the big 1000ms delay between samples this code is not very usefull for dynamic monitoring applications. You could add a min/max per sample intervall. Also, due to the low communication speed and the use of ASCII_String output, the current code is extremly limited in the maximum sample rate.

TIPP: If you want to do dynamic analysis as well (e.g. log data with high frequency to a csv and analyse it later on), you can increase the effective communcation bandwith by altering the baudrate and sending a structured data format, which can then be "interpreted" by a custom terminal application (Have a look at C#...Such applications with a minimum GUI are done in ~100lines of code).


As my morning lecture was boring, i designed a complete solution for your problem and simulated it.

enter image description here

(1) The 5V Rail provided by the arduino is LC-filtered to provide a VCC rail clean of digital noise.

(2) The input current is scaled using a 200Ohm resisots (See the part-number). After scaling, the input is TVS protected and buffered (U3). This makes the input more safe for industrial applications. Note: The input is not reverse polarity safe!

(3) This now buffered input is used to derive I>20mA and I<4mA digital outputs via U1/U2. These can be used for error detection but can also be removed from the design. Note: If you are using a Quad-OpAmp, i would include it.

(4) Then, U4 is used for the equation Vo=Nx(V1-V0). The 4-20mA input range is used to derive the output VOut = 0.3125V/mA x ([4-20mA]-4mA). Hence, the nominal 16mA FSR is projected onto a 5V FSR which can be sampled by the ADC directly.

Note: You should play with the Resistor values a little, as i just did some "in-my-head calculations". Also, you can add RC-Lowpass filters on the input or delays/hysteresis on the digital outputs.


As is said, here is a little guid for how to speed up your data transmission:

//Declare a struct prototype and
//the associated typedef to store the 
//sampling result. Also provide a memory
//instance for use.
struct DataStruct{
    int RawAdcCnts;
    float AdcVoltage;
    float ScaledIPercent;
    float ScaledIBar;
};
typedef struct DataStruct t_DataStruct;
t_DataStruct dataStructToUse;

void loop(){

    //Instead of directly printing the data
    //populate the struct first.
    //Then send the raw struct via UART.
    dataStructToUse.RawAdcCnts = ?;
    dataStructToUse.AdcVoltage = ?;
    dataStructToUse.ScaledIPercent = ?;
    dataStructToUse.ScaledIBar = ?;
    
    //Send data struct by casting the pointer
    Serial.write((char*)(&dataStructToUse), sizeof(dataStructToUse));
    
    //NOTE: Youll need a desktop application
    //of some sort to "recover" the binary data
    //and to write it to a CSV.
}

(1) Basically, you declare a struct (which is a "range of sequential memory slots" in your MCU) and populate it with the data sampled.

Instead of sending "String that say something: + Value" you'll send only the binary representation of your data contained in the struct.

(2) For the example above, this is 3x4Byte (float) + 2Bytes (int) = 14Bytes.

When your a sending a float with the value of 4.32458752 via the Serial.print(), each digit is send as a char - therefore as a single byte. So the number would use 10 bytes (9Digits plus the .).

So far less efficient.

(3) However, you'll need a desktop application (C# works well and is simple) to "decode" the binary data and print out a string into a CSV.

\$\endgroup\$
8
  • \$\begingroup\$ 0-4.5V range makes sense to be able to detect shorted sensor. Though for that case some input protection is necessary, too. \$\endgroup\$
    – jpa
    Nov 22, 2023 at 7:58
  • \$\begingroup\$ @jpa You mean, if more than 20mA are delivered? \$\endgroup\$ Nov 22, 2023 at 11:36
  • 1
    \$\begingroup\$ Yes. It's quite common in industrial stuff to have detection of current less than 4 mA or more than 20 mA and signal error state. \$\endgroup\$
    – jpa
    Nov 22, 2023 at 11:42
  • \$\begingroup\$ @jpa I was aware of the <4mA purpose, but never head of >20mA detection in PLC systems. Thank you for the info - learned something today =) \$\endgroup\$ Nov 22, 2023 at 11:45
  • \$\begingroup\$ Yeah, I guess broken wire detection is more common. \$\endgroup\$
    – jpa
    Nov 22, 2023 at 12:05
3
\$\begingroup\$

To convert current to voltage, we need to take a look at ohms law: V = I*R more specifically, I = V/R.

If you take your varying current value and run it through a resistor of known resistance, you can then calculate the current running through the resistor based on voltage drop across said resistor.

If you have a 100 ohm resistor in series with the current connection, you can measure the voltage drop across the resistor by connecting an ADC pin on each end. In software, you can take the value of the ADC pins (A0 and A1 in the example schematic below) and subtract the two values. With that said, the formula would be I = ( analogRead(A1) - analogRead(A0) ) / R where R is the resistance (use a 1% or better resistor, or measure the actual value with an ohm-meter to maintain accuracy. The device will only be as accurate as the largest tolerance).

Make sure the voltage at each end of the resistor is less than 5 volts! The arduino ADC pins are referenced to ground so even if the voltage across the resistor is 100mV, the voltage at each end could be 18V and 17.9V. It's easy to check the circuits functionality with a voltmeter/multimeter before an Arduino, so make sure to do so.

schematic

simulate this circuit – Schematic created using CircuitLab

For an application like this, an Op Amp isn't what you quite need. If this solution doesn't work for you, still using the series resistor, I suggest using an Instrumentation Amplifier that can handle the 18+ volts instead of an Op Amp.


Edit based on comment (22NOV2023)

Summary of comment: Circuit can involve an extra resistor with a diode.

I am assuming what is meant in the comment is the inclusion of a Zener diode like in one of the other answers on this thread.

Here is an example circuit: enter image description here

What is going on here, is the addition of a zener diode. The function of this zener is to essentially cap the voltage between those two connections to prevent any over-voltage situations like I mention above. The reason for the resistor is to limit current to the zener and create a high-impedance path to your analog inputs or measurement equipment (it can be lower to reduce voltage drop. I suggest a value between 1k ohms to 10k ohms).

You can implement a 5v (or 5.1v) zener diode to cap the voltage at 5 volts.

It isn't necessary, and in the simulation above you can see that it drops the voltage by about 2mV, but it is a good protective component. Play with the simulation here.

Considerations if this two wire transducer is connected as Wire 1 to 18V, wire 2 to 100 ohm resistor, then resistor to ground you do not need to use A1 and can set up the circuit like so:

enter image description here

Test Simulation Here

I don't think the zener diode is necessary, and being that it is a current output sensor I don't believe an over-voltage situation will occur. Reason: at 100 ohms to reach above 5V across the resistor you need 50mA from the transducer, which is more than twice the high end of its range.

\$\endgroup\$
4
  • \$\begingroup\$ +1 for the great idea to make a "software differential input". \$\endgroup\$ Nov 22, 2023 at 15:18
  • \$\begingroup\$ @Pxl Thank you, sounds good! I've been doing some research about your suggestion and seems like it usually involves an extra resistor with a diode. Do you have any guide or resource on that? Appreciate it a lot! \$\endgroup\$
    – Toralito
    Nov 22, 2023 at 22:13
  • \$\begingroup\$ @Toralito I have edited my answer, please take a look at the new additions. \$\endgroup\$
    – Pxl
    Nov 22, 2023 at 22:51
  • \$\begingroup\$ @Pxl Thank you so much, I've learn a lot. \$\endgroup\$
    – Toralito
    Nov 23, 2023 at 13:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.