4–20mA T current loop Arduino tutorial Part II: transmitter

Aug 19, 2016 0 comments


In my previous post I wrote a bit of the hardware aspects of using a pair of 4–20mA R and 4–20mA T click boards from MikroElektronika in an Arduino environment. Today I will make a further progress in this tutorial and I will describe how to implement the transmitter side, using a pair of 4–20mA receiver and transmitter click boards, Arduino Uno boards and Arduino Uno click shields.

As the transmitter implements galvanic separation, I can’t power the transmitter from the loop itself. This is a bit different from what you’ll find in industrial environments, where the transmitter can be energized either by by the current loop itself, or by using some 3-wire or 4-wire setups. You can read more about these topologies in this guide from Libelium.

Anyway, in my setup I must provide separate power sources for the transmitter and the receiver. Furthermore, as the loop side of the transmitter is powered from the loop, the receiver must be on for the transmitter to work.

So, I used two Arduino Uno boards, two Arduino Uno Click shields, one 4–20mA T click and one 4–20mA R click. Each Arduino Uno is powered from its own supply. Both 4–20mA click boards were reconfigured for 5V operation by moving the SMD jumpers into the 5V position.

With the current loop powered the next step to perform is calibration of the transmitter. That is, I have to know what to write to the DAC registers to set the current through the loop to 20mA and what to write in order to set the current to 4mA.

The calibration routine I propose looks like this:

#include <SPI.h>

// Arduino UNO with Mikroe Arduino Uno Click shield
// 4-20mA is placed in socket #2
// CS   is pin 9
// SCK  is pin 13
// MISO is pin 12
// MOSI is pin 11
#define DAC_CS 9

// Used to receive data from serial port
int received;

void setup() {
  // initialize serial
  Serial.begin(9600);
  // initialize SPI
  SPI.begin();
  pinMode(DAC_CS, OUTPUT);
}

void loop() {
  // if there's any serial available, read it:
  while (Serial.available() > 0) {
    // look for the next valid integer in the incoming serial stream:
    int received = Serial.parseInt();
    // look for the newline. That's the end of your
    // sentence:
    if (Serial.read() == '\n') {
      // constrain the values to 0 - 255 and invert
      // if you're using a common-cathode LED, just use "constrain(color, 0, 255);"
      received = constrain(received, 0, 4095);

      Serial.print("DAC is set to value : ");
      Serial.println(received);

      // now send it to the DAC
      set_DAC(received);
    }
  }
}


/*******************************************************************************
* Function set_DAC(int set_value)
* ******************************************************************************

DAC works on SPI
We must send 16 bits
byte1 is [WR, BUF, /GA, /SHDN, data11, data10, data9, data8]
byte2 is [data7, data6, data5, data4, data3, data2, data1, data0]
Write code
WR    0 - write to DAC register
      1 - Ignore this command
VREF Input Buffer Control bit
BUF   0 - Unbuffered
      1 - Buffered
Output Gain Selection bit
/GA   0 - 1x (VOUT = VREF * D/4096)
      1 - 0 = 2x (VOUT = 2 * VREF * D/4096)
Output Shutdown Control bit
/SHDN 0 - Shutdown the device. Analog output is not available.
      1 - Active mode operation. VOUT is available

WR has to be 0 to write to DAC registers
BUF is set to 0 (unbuffered)
GAIN MUST BE SET TO 1. We can't output more than the Vcc!!!
SHDN also set to 1 to have DAC active.
00110000 = 0x30
*/

void set_DAC(int set_value){
  byte first_byte;
  byte second_byte;

  first_byte = (set_value >> 8) & 0x0F;
  first_byte = first_byte | 0x30;
  second_byte = set_value & 0xFF;

  SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));
  digitalWrite(DAC_CS, 0);
  SPI.transfer(first_byte);
  SPI.transfer(second_byte);
  digitalWrite(DAC_CS, 1);
  SPI.endTransaction();
} 

To calibrate the transmitter I used the serial monitor to send values to the DAC. Valid values are from 0 to 4095. Anything outside this range will be mapped to one of the extremes.

The values sent through the serial monitor are written to the DAC registers, and as result the current through the loop changes. An miliammeter is used to measure this current, and I recorded the DAC_4mA and the DAC_20mA values. Those values are then used in the transmitter routine.

In my tutorial, for a length of about 15m of (good quality) UTP cable to get 4mA through the loop I recorded DAC_40mA = 801 and to get 20mA I’ve got DAC_20mA = 3994.

4–20mA T Arduino IDE code for the transmitter

With the above values I can write the transmitter code. The code in this tutorial reads the value on the A0 pin, maps the result to the [DAC_4mA,…, DAC_20mA] range and updates the DAC registers accordingly:

/*
  4-20mA T click transmitter code

  This code reads the voltage on pin A0
  and sends the result on 4-20mA current loop

 */

#include <SPI.h>

// Arduino UNO with Mikroe Arduino Uno Click shield
// 4-20mA is placed in socket #2
// CS   is pin 9
// SCK  is pin 13
// MISO is pin 12
// MOSI is pin 11
#define DAC_CS 9

// Calibration data obtained by running the calibration code
const int DAC_4mA  = 796;
const int DAC_20mA = 3982;

// Data min and max range
const int data_min_range = 0;
const int data_max_range = 1023;


// Read from A0 pin
int analog_value;

// Debug mode? (1 - debug, 0 - no debug);
bool debug_mode = 1;

void setup() {

  // Are we in debug mode
  if (debug_mode == 1){
     // Initialize serial
     Serial.begin(9600);
  }

  // Initialize SPI
  pinMode(DAC_CS, OUTPUT);
  digitalWrite(DAC_CS, 1);
  SPI.begin();

}

void loop() {

  // Read the input on analog pin 0:
  analog_value = analogRead(A0);

  // Transmit data
  SendTo420mA(analog_value);

  // Are we in debug mode
  if (debug_mode == 1){
     // Print information
     Serial.print("Transmitted value is: ");
     Serial.println(analog_value);
  }
  
  // Don't update too often, it doesn't make sense
  delay(500);

}

void set_DAC(int set_value){
/*
DAC works on SPI
We must send 16 bits
byte1 is [WR, BUF, /GA, /SHDN, data11, data10, data9, data8]
byte2 is [data7, data6, data5, data4, data3, data2, data1, data0]
Write code
WR    0 - write to DAC register
      1 - Ignore this command
VREF Input Buffer Control bit
BUF   0 - Unbuffered
      1 - Buffered
Output Gain Selection bit
/GA   0 - 1x (VOUT = VREF * D/4096)
      1 - 0 = 2x (VOUT = 2 * VREF * D/4096)
Output Shutdown Control bit
/SHDN 0 - Shutdown the device. Analog output is not available.
      1 - Active mode operation. VOUT is available

WR has to be 0 to write to DAC registers
BUF is set to 0 (unbuffered)
GAIN MUST BE SET TO 1. We can't output more than the Vcc!!!
SHDN also set to 1 to have DAC active.
00110000 = 0x30
*/

  byte first_byte;
  byte second_byte;

  first_byte = (set_value >> 8) & 0x0F;
  first_byte = first_byte | 0x30;
  second_byte = set_value & 0xFF;

  SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));
  digitalWrite(DAC_CS, 0);
  SPI.transfer(first_byte);
  SPI.transfer(second_byte);
  digitalWrite(DAC_CS, 1);
  SPI.endTransaction();

  // Are we in debug mode
  if (debug_mode == 1){
    // Print information
    Serial.print("DAC is set to: ");
    Serial.println(set_value);
    Serial.println();
  }
}

void SendTo420mA(unsigned int transmitted_Value)
{
  // Map the data to be sent into DAC values
  // that result in a loop current between 4 and 20mA
  int temp = map(transmitted_Value, data_min_range, data_max_range, DAC_4mA, DAC_20mA);
  // update the current loop
  set_DAC(temp);
}

Comments

Related Posts

{{posts[0].title}}

{{posts[0].date}} {{posts[0].commentsNum}} {{messages_comments}}

{{posts[1].title}}

{{posts[1].date}} {{posts[1].commentsNum}} {{messages_comments}}

{{posts[2].title}}

{{posts[2].date}} {{posts[2].commentsNum}} {{messages_comments}}

{{posts[3].title}}

{{posts[3].date}} {{posts[3].commentsNum}} {{messages_comments}}

Recent Comments

Contact Form