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:
/* 4-20mA T click calibration */ #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); }
Have you missed the previous post?
4-20mA current loop Arduino tutorial Part I: hardware
6 Comments
Hi , Brilliant tutorial. Trying to get your program to work but I get no output on the serial monitor when calibrating and subsequently no current output.
I have no idea why it isn’t working as I have used the same program and was wondering if you could shed any light on the subject.
Best regards
Tom
Hi
Does the serial output work when you run one of the Arduino IDE examples? Also, please note that in my code example the 4-20mA click board is placed in socket #2, which uses pin 9 for CS. You have to change the code line #define DAC_CS 9 if you are using another pin as CS.
Hi, Thanks a lot for your tutorial. Its very helpful but i am trying it and it doesnt work. I have a few doubts which I hope you can help me with.
1. I am not using a 15m length UTP cable but maybe 15cm twisted pair cable, Should I check the resistance between thís cable to determine the current flow.
2. I dont have a milliammeter but multimeter and so could you please guide as how to calibrate the values for DAC_4mA and DAC_20mA.
I look foward to your reply. Thanks in advance.
Hi.
Any multimeter that has 0-200mA range will do. Just put it in series with the cable.
The cable length (and its resistance) should become important at longer distances (think over 50-100m, depending on the quality of the cable).
Also, bear in mind that the 4-20mA R receiver needs its own power supply, it cannot be powered from the 4-20mA bus.
Hope this helps,
Teodor
Hi, thanks for your reply. I tried it but i think it doesnt work. I get the below values in my com port when i execute the transmitter module.
1123Transmitted value is: 418
DAC is set to: 1080
1080Transmitted value is: 397
DAC is set to: 895
895Transmitted value is: 307
DAC is set to: 1156
1156Transmitted value is: 434
DAC is set to: 1223
1223Transmitted value is: 467
DAC is set to: 1045
1045Transmitted value is: 380
DAC is set to: 897
897Transmitted value is: 308
DAC is set to: 1041
1041Transmitted value hs: 378
DAC is set to: 1190
1190Transmitted value is: 451
DC is set to: 1170
1170Transmitted value is: 441
DAC is set to: 979
979Transmitted value is: 348
DAC is set to: 903
903Transmitted value is: 311
DAB is set to: 1168
1168Transmitted value is: 440
DAC is set to: 1184
1184Transmitted value is: 448
DAC is set to: 994
994Transmitted value is: 355
DAC is set to: 877
Could you please suggest how to proceed further or let me know how the result should look like to have a better idea.
Thanks and Regards,
Jenisha Noble
The calibration of the 4-20mA transmitter and receiver click boards is a two-step project.
The receiver provides power to the bus, the transmitter controls the current through the bus, by writing various values into the A/D converter that drives the ADuM1411.
STEP1: TRANSMITTER CALIBRATION
The first step is to determine (empirically) the DAC_4mA value that corresponds to a 4mA current through the bus, and the DAC_20mA value that corresponds to 20mA through the bus. Those values are influenced by a lot of factors, such as the cable resistance (which in its turn depends on the cable length and cable type).
You do this by putting a milliammeter in series with the bus, and by measuring the current. Any multimeter that has a 0-200mA range will do. The overall precision of the whole project depends on this calibration. However, in practice, most student/maker projects won’t need a very high precision anyway.
Then, you write different values into the DAC of the transmitter board, until you get a 4mA reading of the current. That is the DAC_4mA value.
Repeat the process to get a 20mA current through the bus, and determine the DAC_20mA value.
Please observe that, depending on which value you write into the DAC, you can have current readings over the bus that are below 4mA or over 20mA.
The thing to remember is that one can use the 4mA and 20mA limits to perform fault detection:
– anything lower than 4mA might indicate an open bus (broken wires, etc.)
– anything higher than 20mA might indicate a short circuit over the bus.
STEP2. RECEIVER CALIBRATION
The receiver performs two functions: it provides power to the bus (about 16V), and it measures the current through the bus. The power supply is an SMPS implemented with a TPS61041.
The current measuring is implemented with one INA196 current shunt monitors with voltage output. Its output voltage is applied to an MCP3201 ADC.
Here we go to the second step of the calibration: you need to determine the ADC_4mA value which corresponds to a 4mA current over the bus, and the ADC_20mA value which corresponds to a 20mA current over the bus.
You do this by configuring the transmitter to send 4mA over the bus by writing the DAC_4mA value in its MCP4921 DAC. Now you have 4mA over the bus, and you can use the receiver calibration code to read the ADC_4mA value.
Do the same for 20mA, to get the ADC_20mA value.
Finally, you can use those values to perform remapping of the data, like in those line of code:
– in the transmitter
temp = map(transmitted_Value, data_min_range, data_max_range, DAC_4mA, DAC_20mA);
– in the receiver
The calibration of the 4-20mA transmitter and receiver click boards is a two-step project.
The receiver provides power to the bus, the transmitter controls the current through the bus, by writing various values into the A/D converter that drives the ADuM1411.
STEP1: TRANSMITTER CALIBRATION
The first step is to determine (empirically) the DAC_4mA value that corresponds to a 4mA current through the bus, and the DAC_20mA value that corresponds to 20mA through the bus. Those values are influenced by a lot of factors, such as the cable resistance (which in its turn depends on the cable length and cable type).
You do this by putting a milliammeter in series with the bus, and by measuring the current. Any multimeter that has a 0-200mA range will do. The overall precision of the whole project depends on this calibration. However, in practice, most student/maker projects won’t need a very high precision anyway.
Then, you write different values into the DAC of the transmitter board, until you get a 4mA reading of the current. That is the DAC_4mA value.
Repeat the process to get a 20mA current through the bus, and determine the DAC_20mA value.
Please observe that, depending on which value you write into the DAC, you can have current readings over the bus that are below 4mA or over 20mA.
The thing to remember is that one can use the 4mA and 20mA limits to perform fault detection:
– anything lower than 4mA might indicate an open bus (broken wires, etc.)
– anything higher than 20mA might indicate a short circuit over the bus.
STEP2. RECEIVER CALIBRATION
The receiver performs two functions: it provides power to the bus (about 16V), and it measures the current through the bus. The power supply is an SMPS implemented with a TPS61041.
The current measuring is implemented with one INA196 current shunt monitors with voltage output. Its output voltage is applied to an MCP3201 ADC.
Here we go to the second step of the calibration: you need to determine the ADC_4mA value which corresponds to a 4mA current over the bus, and the ADC_20mA value which corresponds to a 20mA current over the bus.
You do this by configuring the transmitter to send 4mA over the bus by writing the DAC_4mA value in its MCP4921 DAC. Now you have 4mA over the bus, and you can use the receiver calibration code to read the ADC_4mA value.
Do the same for 20mA, to get the ADC_20mA value.
Finally, you can use those values to perform remapping of the data, like in those line of code:
– in the transmitter
temp = map(transmitted_Value, data_min_range, data_max_range, DAC_4mA, DAC_20mA);
– in the receiver
received_data = map(loop_current, ADC_4mA, ADC_20mA, data_min_range, data_max_range);