Interfacing Onion Omega with Industrial 4-20mA Sensors

Interfacing Onion Omega with Industrial 4-20mA Sensors

The Onion Omega 2 is one of my favorite SBCs. It’s small and comes with a ton of power. There are so many neat and interesting projects that can be done with
this computer. In this guide, I will show you how to use the Onion Omega with 4-20mA industrial sensors.

About 4-20mA Sensors

4-20mA sensors are a powerful communication standard, commonly found in industrial applications.  4-20mA sensors use current variations to send data.  Current variations can travel long distance because current does not decay over distance.

Why is 4-20mA the Standard Current Range?

I have seen this question asked so many times, but there is a really easy answer for this.  Put simply, a current sensor needs to use 4mA for a Zero reading rather than 0mA.  If 0mA were used for the zero value, there would be no way to detect a sensor malfunction vs. a broken wire.  Using the standard 4-20mA current range, a broken wire will indicate 0mA.  A working sensor will load the current on the line to 4mA (minimum), indicating the sensor is active and ready to go to work.  This has been an industry standard for many years, making it easy to troubleshoot a long-distance wiring problem vs. a remote sensor problem.  The 4-20mA standard really has nothing to do with 420, it’s just a just a “happy” coincidence!

There are many industrial sensors which come with 4-20mA output.  The most common are distance sensors, pressure sensors, temperature sensors, flow meters, weight sensors, and much more. In this tutorial, we will learn how we can read all these complex industrial sensors using the Onion Omega and a NCD 4-Channel 4-20mA current receiver board.

Hardware Grocery List

You are going to need a few items to complete this project, here’s a grocery list of the base necessities (we are assuming you already have some 4-20mA sensors):

All NCD hardware is plug-and-play, so you won’t need any wiring or soldering, all you will need to do is insert the Onion Omega 2 (1) into the IoT adapter (2), and stack these boards into in the 4-20mA board (3).

Setting up the Onion Omega

The 4-20mA receiver has an MCP3428 to convert analog values into digital values using the MCP3428.  The MCP3428 will communicate with Onion Omega using I2C communications.  To use I2C communications, we will need to install the Python I2C driver in the Onion Omega using the following commands:

opkg update
opkg install python-light pyOnionI2C

Our 4-20mA receiver is very easy to use since it’s based on a common Analog-to-Digital converter.  This device has 4 input channels with on-board amplifier for signal conditioning. To read the 4-20mA signals, we will need to setup the following parameters:

a. Set the MCP3428 Gain to 2

b. Set the MCP3428 Bit Resolution to 12 (it supports up to 16 bits if you need better resolution)

Why 12-bit is Better

The MCP3428 comes with three resolutions 12, 14, and 16 bits. In 12-bit mode, the MCP3428 takes 240 samples per second to calculate the ADC value.   In 14-bit mode, the MCP3428 takes 60 samples per second. Finally, in 16-bit mode, the MCP3428 slows down significantly to just 15 samples per second.  The MCP3428 measures the discharge time of integrated capacitors to read analog values.   Higher resolutions finalize readings after a longer discharge cycle of the internal capacitors.  Using 12-bit mode, the MCP3428 doesn’t have to wait for the caps to discharge all the way, greatly improving speed. Should you require better resolution (and time is not an issue), then you could use 16-bit mode.  However, the MCP3428 datasheet should be consulted carefully in 16-bit mode, as fast reads will result in errant data.  The MCP3428 includes readable flags, letting you know if data is valid.  These flags must be utilized in 16-bit mode.

Python Library to Read 4-20mA Inputs

Below, we have included our Python library.  This library is also available on github as well: Onion Omega 4-20mA Python Library

# Distributed with a free-will license.
# Use it any way you want, profit or free, provided it fits in the licenses of its associated works.
# MCP3428
# This code is designed to work with the MCP3428_I2CADC I2C Mini Module available from ControlEverything.com.
# https://shop.controleverything.com/collections/4-20-ma-current-loop-input
import smbus
import time
DL = 0.01
DL1 = 0.01
# Get I2C bus
address = 0x68
bus = smbus.SMBus(1)

# MCP3428 address, 0x68(104)
# Send configuration command
# 0x11(17)Continuous conversion mode, Channel-1, 12-bit Resolution,gain2

while True:
        bus.write_byte(address, 0x11)
        time.sleep(DL1)
# MCP3428 address, 0x68(104)
# Read data back from 0x00(0), 2 bytes
# raw_adc MSB, raw_adc LSB
        data = bus.read_i2c_block_data(address, 0x00, 2)

# Convert the data to 12-bits
        raw_adc = (data[0] & 0x0F) * 256 + data[1]
        if raw_adc > 2047 :
                raw_adc -= 4095
        current = (raw_adc * 0.01109)
# Output data to screen
        print "Current Output channel 1 : %.2f" %current
        time.sleep(DL)

####### channel 2
        bus.write_byte(address, 0x31)
        time.sleep(DL1)
# MCP3428 address, 0x68(104)
# Read data back from 0x00(0), 2 bytes
# raw_adc MSB, raw_adc LSB
        data = bus.read_i2c_block_data(address, 0x00, 2)

# Convert the data to 12-bits
        raw_adc = (data[0] & 0x0F) * 256 + data[1]
        if raw_adc > 2047 :
                raw_adc -= 4095
        current = (raw_adc * 0.01109)
# Output data to screen
        print "Current Output channel 2 : %.2f" %current
        time.sleep(DL)
############ channel 3
        bus.write_byte(address, 0x51)
        time.sleep(DL1)
# MCP3428 address, 0x68(104)
# Read data back from 0x00(0), 2 bytes
# raw_adc MSB, raw_adc LSB
        data = bus.read_i2c_block_data(address, 0x00, 2)

# Convert the data to 12-bits
        raw_adc = (data[0] & 0x0F) * 256 + data[1]
        if raw_adc > 2047 :
                raw_adc -= 4095
        current = (raw_adc * 0.01101)
# Output data to screen
        print "Current Output channel 3 : %.2f" %current
        time.sleep(DL)
####### channel 4
        bus.write_byte(address, 0x71)
        time.sleep(DL1)
# MCP3428 address, 0x68(104)
# Read data back from 0x00(0), 2 bytes
# raw_adc MSB, raw_adc LSB
        data = bus.read_i2c_block_data(address, 0x00, 2)

# Convert the data to 12-bits
        raw_adc = (data[0] & 0x0F) * 256 + data[1]
        if raw_adc > 2047 :
                raw_adc -= 4095
        current = (raw_adc * 0.01094)
# Output data to screen
        print "Current Output channel 4 : %.2f" %current
        time.sleep(DL)

 

from OmegaExpansion import onionI2C
import time
# Get I2C bus
i2c = onionI2C.OnionI2C()

The above sample is used to import the Onion Omega I2C Module

long MCP3426::readADC()
{

    raw_adc = 0;

    while(CheckConversion() == 1);

    switch (SPS)
    {
  
        case 12:
                raw_adc = data[0];
                raw_adc &= 0b00001111;
                raw_adc = raw_adc << 8;
                raw_adc |= data[1];

                if(raw_adc > 2047)
                {
                    raw_adc = raw_adc - 4096;
                }
        
                // raw_adc = raw_adc * LSB(1 mV)/PGA for PGA = 1;
        
                break;
    
        case 14:
                raw_adc = data[0];
                raw_adc &= 0b00111111;
                raw_adc = raw_adc << 8;
                raw_adc |= data[1];

                if(raw_adc > 8191)
                {
                    raw_adc = raw_adc - 16384;
                }
        
                // raw_adc = raw_adc * LSB(250 µV)/PGA for PGA = 1;
       
                break;
    
        case 16:

                raw_adc = data[0];
                raw_adc = raw_adc << 8;
                raw_adc |= data[1];

                if(raw_adc > 32767)
                {
                    raw_adc = raw_adc - 65536;
                }
            
                // raw_adc = raw_adc * LSB(62.5 µV)/PGA for PGA = 1;
      
                break;
    }
    return raw_adc;
}

The above section is used to setup the MCP3428 to read channel one with 12-bit resolution and a gain of two (very important).  Once we write the setup bytes, we introduce a small delay to give channel one some time to setup and take enough samples to calculate the ADC value. If the delay is too short, fluctuations in the readings will occur. If you are using the MCP3428 in the 16-bit mode, you will need to setup this delay to a higher value (around 1 second).  Please check the MCP3428 datasheet for more information.

MCP3428 Addressing

The NCD 4-20mA Receiver board has 2 address jumpers with floating address support. Up to eight 4-channel 4-20mA receiver boards can be used with one master. To learn more about the address setup, please see the wiring diagram below:

 

 

 

MCP3428 Configuration Bits

MCP3428 configuration register is used to setup the channel selection, conversion mode, sample rate and gain setting. This register has 8-bits, which can be changed to obtain a certain setting. The most common bits you need to know about are

  1. Channel Selection — bit 6 and bit 5 of the configuration register
  2. Conversion Mode — bit 4, keep this bit set to use MCP3428 in continuous mode
  3. Sample Rate — bit 3 and bit 2 to select the sample rate
  4. Gain — bit 1 and bit 0, keep these two bits to 0,1 to read 4-20mA input

 

Let’s say we want to read channel one with 12-bit resolution, in that case, our bits will look like this:

bit 7 — 0, bit 6 and 5 — 00, bit 4 — 1, bit 3 and bit 2 — 00, bit 1 and bit 0 — 01 ====== 00010001 === 0x11

 

Let’s say we want to read channel 2 with 12-bit resolution, in that case out config register will look like this:

bit 7 — 0, bit 6 and 5 — 01, bit 4 — 1, bit 3 and bit 2 — 00, bit 1 and bit 0 — 01 ====== 00110001 === 0x31

 

Let’s say we want to read channel 3 with 12-bit resolution, in that case out config register will look like this:

bit 7 — 0, bit 6 and 5 — 10, bit 4 — 1, bit 3 and bit 2 — 00, bit 1 and bit 0 — 01 ====== 01010001 === 0x51

 

Let’s say we want to read channel 4 with 12-bit resolution, in that case out config register will look like this:

bit 7 — 0, bit 6 and 5 — 11, bit 4 — 1, bit 3 and bit 2 — 00, bit 1 and bit 0 — 01 ====== 01110001 === 0x71

 

Let’s say we want to read channel one with 16-bit resolution, in that case, our bits will look like this:

bit 7 — 0, bit 6 and 5 — 00, bit 4 — 1, bit 3 and bit 2 — 10, bit 1 and bit 0 — 01 ====== 00011001 === 0x19

 

Please see configuration bits in the datasheet below:

 

 

Once all this is setup, run the python library.  You should see readings for all four 4-20mA channels, one by one.  When when no input is connected to the 4-20mA input, the code output will look like this:

Readings with no sensor connected:

 

Readings with 4mA sensor connected: