Help 4-20mA Convert Data Code and Adjust Readings

Hello again. So I have tried to read all the forum search results on this topic but got stuck on the converting values as I am using a very simple code from github. This is an ADS1115 4-20ma converter, 1 channel.

I have tried to find examples on converting the 4-20mA readings to know values of my sensor, and also how to calibrate so I get true 4-20mA instead of a lower number.

For calibration.
I should show a 4 reading as 4mA (for a flow rate of 0 cfm), and 20 as 20mA (for a flow rate of 200 cfm). But what I actually see on my console is a flow of 0 = 3.827mA through 3.93mA and a flow of 200 as 19.67 to 19.678.
How do I enter calibration code to fix this variable and or round it to two decimals, IE 4.00 or 20.00?

For Converting the data.
My flow meter will measure 0 to 200 CFM. So I need to add code that will change the mA values to read and measure the date in between if a flow of 0 = 4mA and flow of 200 CFM = 20mA. Ane everywhere in between.

Thank you in advance for your time. I really tried to find the solution online on all the forums but was unsuccessful.

Here is a screenshot of my particle console. I will post my code for the .ino file and the ads1115.cpp separately.


datalogger.ino :

/*

Project DataLoggerMac
Description: Flow Meter Datta logger application
Author:
Date: 4/28/18
*/
// setup() runs once, when the device is first turned on.
#include "ads1115.h"

ADS1115 inputBoard;

float ma = 0.0;

void setup() {
inputBoard.setAddress(0);
}

void loop() {

//Input 1
int16_t input_1 = inputBoard.readInput(1);
ma = (input_1*4.00)/2130.00;

Serial.printf("Input 1 reading: %f \n", ma);


delay(1000);


Particle.publish("Input 1 reading", String(ma) + " mA"); delay(2000);
}

ads1115.cpp :

#include "ads1115.h"

bool ADS1115::setAddress(int addressJumper){
if(addressJumper == 1){
address = address | 1;
}
}

int ADS1115::readInput(int channel){
// byte writeData[2] = {(byte)readInputSingleEnded[channel -1], (byte)(readInputSingleEnded[channel - 1]>>8)};
if(!wireWrite(address, 0x01, readInputSingleEnded[channel -1], 2)){
Serial.println("WireWrite failed");
}
delay(50);
if(!wireRead(address, 0x00, localReadBuf, 2)){
Serial.println("WireRead failed");
}

uint16_t raw_adc = (localReadBuf[0] << 8) | localReadBuf[1];

return raw_adc;
}

bool ADS1115::wireWrite(int addr, int reg, uint16_t value, int len){
if (!Wire.isEnabled()) {
Wire.begin();
}
Wire.beginTransmission(addr);
Wire.write((uint8_t)reg);
Wire.write((uint8_t)(value>>8));
Wire.write((uint8_t)(value & 0xFF));
byte status = Wire.endTransmission();
if(status == 0){
return true;
}else{
return false;
}
}

bool ADS1115::wireRead(int addr, int reg, int* readBuff, int returnLen){
int buf[returnLen];
if (!Wire.isEnabled()) {
Wire.begin();
}
Wire.beginTransmission(addr);
Wire.write(reg);
byte status = Wire.endTransmission();
if(status != 0){
return false;
}
Wire.requestFrom(addr, returnLen);
unsigned long startTime = millis();
while(Wire.available() != returnLen && millis()<startTime+timeout);
if(Wire.available() != returnLen){
return false;
}
for(int i = 0; i < returnLen; i++){
readBuff[i] = Wire.read();
// Serial.printf("read: %i \n", readBuff[i]);
}
return true;
}

Did you look at the map() function in the docs? What you’re trying to do is just what it does.

2 Likes

Thank you for the suggestion. I tried mapping the number but as I have discovered mapping a floating number is beyond my capability. I need the fraction so I have to keep it as a float. The only solution was to use the formula to calculate the conversion using the formula listed here.

The readings vary a small degree for some reason but I will have to round them after I post them to google sheets.

If anyone in interested here is part of my code after using the formula.

#include "ads1115.h"
ADS1115 inputBoard;

float ma = 0.0;
float flow = 0.0; //TWC ADDED 5/1/18 To calculate ADC to FLow SEE FORMULA BELOW
        //int flow = 0.0; // GIVES WHOLE NUMBERS NO Decmial Point. 

void setup() {
    inputBoard.setAddress(0);
    
}

void loop() {

int16_t input_1 = inputBoard.readInput(1);
    //ma = (input_1*4.00)/2130.00;
ma=(input_1); //ACTUALLY THIS IS ADC VALUE NOT mA

Serial.printf("Input 1 reading: %i\n", input_1);
    //CONVERSION FORMULA
flow=(0.0236742*ma)-48.295368; // Formula from https://ncd.io/how-to-convert-4-20ma-current-loop-to-voltage/ 
    
Particle.publish("Input 1 reading", String(flow)); delay(2000); // WORKED TWC ADDED 5/1/18
    
}

What system version are you targeting?

There is a map() function that does that for floating point variables too.

1 Like

Good day ScruffR.

I am using the particle proton. Compiling on the web version of the build.particle.io

Every time I tried to map a floating point I got an error saying unable to map floating variable.

I may just do something in the google sheet that I am posting to. That seems to be the easier solution.

Thank you for your efforts.

Very best,

Could it be that the exact message was complaining about ambiguity of the call?
The exact error message would be helpful to show there.

But if it was the ambiguity issue I suspect, then the reason for it could be that you were mixing floating point and integral datatypes in the call.
If you want to map a floating point value, you need to provide all the parameters as floating point like this

  float raw = 12.3456;
  float mapped = map(raw, 4.0, 20.0, 0.0, 100.0);

(note the decimal places to indicate the constants are to be taken as floating point)

2 Likes

Oh yea. That was the reason. Thank you so much for taking the time to answer what I assume is a rookie mistake. :slight_smile:

I really appreciate it.

Cheers

2 Likes