Send float to 4-20mA current loop transmitter

Hello,

I am using an Arduino mega 2560 + 4-20mA current loop transmitter.

I am trying to write a float value to DAC via I2C.

I casted the float value into 4 bytes, as you can see in the code below:

float p= 9.2047

union {
byte p Inbytes[4];
}myunion

void setup(){

wire.begin ();
}

void loop(){

wire.beginTransmission(0x60);    //DAC address

for (int i=0;i<4;i++){
wire.write(myunion.Inbytes[i])
println(myunion.Inbytes[i],HEX);
}

End Transmission();
}

I am a little bit stuck in integrating my code to the this example code:

Do you have any suggestion?

You don't send a float to the DAC, you need to send the DAC count.

A DAC is the opposite of an ADC. But they both work based on count value.

How many bits is your DAC? What is the range of your analog value?

You will need to scale your float to be between 0 and (2^n)-1, then send that result to the DAC.

I am controlling a pressure sensor that measures from 0 to 20bar and I want to the send the pressure measurements to the DAC of this board:

where there is a 12-bit MCP 4725.

You will need to scale your float to be between 0 and (2^n)-1, then send that result to the DAC.

I have never worked with DAC, so I was wondering which was the right approach, if, for example, the float value=9.2040

Just like a ADC only in reverse.

12 bit = 4096 there fore scale 0 to 20 bar between 0 and 4095.

Output value = setpoint /20 * 4095

where setpoint is the 9.2040 in your example and the output value is the value to send to the DAC.

Ok, thanks...now it is clear for me.

From the formula I obtain:

Output value =1887.3

Which is the rounded correct value that represents exactly to 9.2040?

Convert to an Int and send it.

The value to write to DAC comes from this:

int DAC_value;
DAC_value= int ((Output value)+0.5);

Should be correct to add 0.5 to round the value and avoid loosing information?

If you do that, it will never output 0.

I understand, I will remove 0.5.

Which is the best way to have highest accuracy in the conversion?

adwsystems:

int DAC_value;

DAC_value= int ((Output value)+0.5);

If you do that, it will never output 0.

Yes it will. int() cast truncates towards zero, it doesn't round, that's what round() does.

My bad. I can never remember which it does in C. Other languages work differently.

No comment on "add 0.5 to round the value and avoid loosing information".

RUE:
I am controlling a pressure sensor that measures from 0 to 20bar and I want to the send the pressure measurements to the DAC of this board:

Interfacing Isolated 4-20mA Current Loop Transmitter Arduino - NCD.io

where there is a 12-bit MCP 4725.

not sure I I understood well what u trying to do... are using using only the MCP4725 or the interface boad on that link?

you are doing 2 actions here:

(1)set pressure
(2) output current

The second is pretty straight forward especially if you get round trying the current transmitter example that's on the webpage.

For (1) I'm assuming that the arduino sets the pressure measurement as a 4-20mA current... so for example with pressure range of 0-20bars, based on the current transmitter example, to get the 4-20mA range, 0bar == 290 and 20bar == 1500.

You can map for voltage range using the 'map' function OR write your own y=mx + c function.

RUE:
I casted the float value into 4 bytes, as you can see in the code below:

float p= 9.2047

union {
byte p Inbytes[4];
}myunion

Not with that code you didn’t. The float variable ‘p’ is totally distinct from the byte array ‘p’ in the union. No connection whatsoever. You’re also missing a ‘;’ after ‘myunion’.

Ok, Thank you for the input.

Sherzaad,

You have well understood what I am trying to do.

I am still a little bit confused so I'll try to summarize the steps and please, correct me if I made some mistake.

1)Measure pressure from 0 to 20bar(float values)
2)Map the pressure values(0-20bar) to voltage value (0-5V). Could I do this with float?
3) Write the value to DAC
4)Obtain the value in mA

RUE:
Ok, Thank you for the input.

Sherzaad,

You have well understood what I am trying to do.

I am still a little bit confused so I'll try to summarize the steps and please, correct me if I made some mistake.

1)Measure pressure from 0 to 20bar(float values)
2)Map the pressure values(0-20bar) to voltage value (0-5V). Could I do this with float?
3) Write the value to DAC
4)Obtain the value in mA

Ok... I'm afraid I'm gonna have to reply with more questions! :slight_smile:

1)Measure pressure from 0 to 20bar(float values) -> HOW is your are arduino reading pressure (analog voltage, pulses, SPI...)? surely not by pressing on it!

2)Map the pressure values(0-20bar) to voltage value (0-5V). Could I do this with float? -> why are you so focused on this point; yes you can using y=mx+c but its irrelevant for now... (1) will really dictate how to get the data to the DAC

for (3) and (4) are mentioned there is already an example on that link you posted explain what values to send to the DAC to get a 4-20mA output

No problem, for me it is important to clearly understand :wink:

sherzaad:
1)Measure pressure from 0 to 20bar(float values) -> HOW is your are arduino reading pressure (analog voltage, pulses, SPI...)? surely not by pressing on it!

Sorry, I measure the pressure using a pressure sensor.
The communication between Arduino and the sensor is via RS485 half duplex and the protocol is Modbus RTU. I read directly the pressure values from the UART.

2)Map the pressure values(0-20bar) to voltage value (0-5V). Could I do this with float? -> why are you so focused on this point; yes you can using y=mx+c but its irrelevant for now... (1) will really dictate how to get the data to the DAC

My sensor measures from 0-20bar. How can I pass the sensor output to the DAC and get the final mA signal?

now we're getting somewhere! :slight_smile:

So you read the the pressure sensor with UART... good... next question if for example the pressure is 1.23bar, what is the UART data read? what does the datasheet say about the data stucture of the UART data; is it a float number that you receive as 4 bytes? if it an integer value? is it ASCII characters?

sorry to be a pain but asking all those qestions coz as I said that will dictate know to convert/pass that value to the 4-20mA current transmitter.

I read from sensor two information pressure and temperature.
Each info is 4bytes stored in an array of [4].

Pfinal is the info to pass to the DAC.

Here you are the code I am using to read sensor.

#define baud 9600
#define timeout 200                                                                                                                                                                                                    
#define polling 1000 // the scan rate
#define retry_count 10


#define TxEnablePin 8 
#define LED 9
#define TOTAL_NO_OF_REGISTERS 2


enum
{
  PACKET1,
  TOTAL_NO_OF_PACKETS // leave this last entry
};

// Create an array of Packets to be configured
Packet packets[TOTAL_NO_OF_PACKETS];

// Masters register array
unsigned int readRegs[4];   //Data read from the slave will be stored in this array 


float p;
float Thead;
float pfinal;

//calibration parameters:
const float c=0.015;    
const float d=0.052;
const float e=0.622;
const float f=0.431;

void setup()
{
  
  // Initialize each packet
  Serial.begin(9600);
  Serial1.begin(9600);
  modbus_construct(&packets[PACKET1], 250, READ_HOLDING_REGISTERS, 0x100, 4, 0);
   
  modbus_configure(&Serial1, baud, SERIAL_8N1, timeout, polling, retry_count, TxEnablePin, packets, TOTAL_NO_OF_PACKETS,readRegs);   // Initialize the Modbus Finite State Machine
                                                                                                                          
  pinMode(LED,OUTPUT); 
}


void loop()
{
  delay(1000); 
  
  modbus_update();
  
  digitalWrite(LED, HIGH);  
  delay(100);   
  digitalWrite(LED, LOW);
  
  //Shift and Cast pressure to float   
  unsigned long temp1 = (unsigned long) readRegs[0] << 16 | readRegs[1];   
  p = *(float*)&temp1;
  
   
  digitalWrite(LED, HIGH); 
  delay(100); 
  digitalWrite(LED, LOW);
  
 
  //Shift and Cast value tempretaure to float   
  unsigned long temp2 = (unsigned long) readRegs[2] << 16 | readRegs[3];
  Thead = *(float*)&temp2;
  
  p2=pow(p,2);
  p3=pow(p,3);

  pfinal=p3*a+b*p2+c*p+d;
  
  Serial.print("\nRequests: ");
  Serial.println(packets[PACKET1].requests);  
  Serial.print("Successful requests: ");    
  Serial.println(packets[PACKET1].successful_requests);
  Serial.print("Failed requests: ");  
  Serial.println(packets[PACKET1].failed_requests);  
  Serial.print("Reg[0]: ");   
  Serial.println(readRegs[0]);  
  Serial.print("Reg[1]: ");  
  Serial.println(readRegs[1]); 
  Serial.print("pressure:");  
  Serial.println (p,4);   
  Serial.println("");   // new line 
  Serial.print("Reg[2]: ");   
  Serial.println(readRegs[2]);  
  Serial.print("Reg[2]: ");  
  Serial.println(readRegs[3]); 
  Serial.print("Thead:");  
  Serial.println (Thead,4);   
  Serial.println("");   // new line
  Serial.print("pfinal:");  
  Serial.println (pfinal,4);   
  Serial.println("");   // new line
  
}

I really can't figure out how to pass pfinal to DAC of the board transmitter and get the mA transformation.

Which part are you having issues with? Sending the value to the DAC or scaling to the DAC value?

How to send the value is in the example you posted already.

As for scaling, think in terms of percent of range. What percent of the pressure range is you desired setpoint? The range is 0-20 bar, your setpoint 9.02. 9.02/20.0= 45.1%. From the example you linked for the device, the range is 290-1500 or 1210. So 45.1% of 1210 is 545. Since the range starts at 290, add 290 to that result, 835. Send 835 to the DAC (see the example you linked already).

The actual complete formula is

DAC Output = (Setpoint - Setpoint minimum)/(Setpoint maximum - Setpoint minimum) * (Output Maximum - Output Minimum) + Output Minimum

For your example that becomes:
(9.02 - 0) / (20 - 0) * (1500 - 290) + 290 = 835

RUE:
I read from sensor two information pressure and temperature.
Each info is 4bytes stored in an array of [4].

Pfinal is the info to pass to the DAC.

Here you are the code I am using to read sensor.

  //Shift and Cast pressure to float   

unsigned long temp1 = (unsigned long) readRegs[0] << 16 | readRegs[1];  
 p = (float)&temp1;




I really can't figure out how to pass pfinal to DAC of the board transmitter and get the mA transformation.

What adwsystems is what I have told you right at the very start in more words and added an example for your understanding.

I am however now concerned about how you are retrieving the float value for pressure and temperature; did you test that code to see whether or not you are getting the RIGHT values?
(using one of those sensors by any chance? DCT 531 Modbus RTU RS485 Pressure Sensor)

If you are getting the right values ignore what I say next! :wink:

coz "Each info is 4bytes stored in an array of [4]" NOT TRUE! Each info2 (temp1 and temp2) is 2 bytes long ie they are float16 (2 bytes) not the typical float which is 4 bytes(float32).
I dont expect that you are getting the right pressure value with this casting: "p = (float)&temp1;"

you need to get you input readings right before applying what both I and adwsystems said on how to use that value to set your 4-20mA transmitter