Problem sending data from slave to master - analogic sensor 4-20 mA

Hi Everyone.

I work on a project which aim is to get data from two 4-20 mA flow meters.

For that, an Arduino Uno slave first makes measurement on analogic ports (A0, A1) and get a tension (0-1023) (sensorValue and sensorValue2).

The slave send data to the master (Arduino Uno) directly from the integer used to get the tension from 0 to 1023.

The master does not get the proper integer send by the slave (there are some variations but that's not the true ones). I verified the data send, it seems to be good (integer from 0 to 1023).

The problem is that if I defined manually the value of the sensors values like (sensorValue=247 and sensorValue2=153) then the master receives the proper values.

What can be the problem?

Here is the code

SLAVE :

int sensorValue2 = analogRead(A1);
int sensorValue = analogRead(A0);

uint8_t buffer[2];
buffer[0] = sensorValue >> 8;
buffer[1] = sensorValue & 0xff;
Wire.write(buffer, 2);

buffer[0] = sensorValue2 >> 8;
buffer[1] = sensorValue2 & 0xff;
Wire.write(buffer, 2);

MASTER :

// Request data from slave.
  Wire.beginTransmission(SlaveDeviceId);
  int available = Wire.requestFrom(SlaveDeviceId, (uint8_t)4);

  if (available == 4)
  {


    int receivedValue = Wire.read() << 8 | Wire.read();
    Serial.println(receivedValue);

    delay(10);

    int receivedValue2 = Wire.read() << 8 | Wire.read();
    Serial.println(receivedValue2);
  }
  else
  {
    Serial.print("Unexpected number of bytes received: ");
    Serial.println(available);
  }

  int result = Wire.endTransmission();
  if (result)
  {
    Serial.print("Unexpected endTransmission result: ");
    Serial.println(result);
  }

Thank you

You might want to look into filtering your data.

Thank you Idahowalker.

That's not a problem of filtering.

If I get 202 from the analogic A0, the master receives 250.
If I code : sensorValue=202, then the master receives 202.

I can't understand why. I verified that sensorValue get from A0 is a true integer (it is).
I also verify the values send in the array "buffer" and the value of sensorValue : all are good before going to the master.

The I2C bus is not a serial bus where you put data on one end and it comes out at the other end. It is not that kind of bus.

Is it possible to do your project with a single Arduino Uno ? That is easier (a lot easier, really a lot easier).

The next option is a Serial bus, preferably with a Arduino board that has a spare hardware Serial port.

If you want to continue with the I2C bus, then you have to do everything just right. Your project might fail because of the I2C bus between two Arduino boards, because some project like that fail and are never finished. Therefor your goal should be a project that you can finish.

Thank you Koepel.

I had some projects in the past with slave and master on I2C bus (rain gauge) and it was working properly.

When I send a defined integer (as I said like sensorValue=202) the master receives the proper value. When the 202 come from the analogic port, the value received by the master is not the good one.

I can't believe the problem comes from I2C because I can send good data if it is defined "manually".

Also, at the step of the project, I can't go back to one Arduino Uno and no Slave (all are mounted on a PCB).

Can you show two minimal working sketches for the Master and the Slave that has the problem ?

It is easier if you use an array of integers:

Master board

int available = Wire.requestFrom(SlaveDeviceId, 4);
if (available == 4)
{
  int16_t value[2];       // two signed integers of 16 bit, total 4 bytes
  Wire.readBytes(value, sizeof(value));
}

Slave board

The onRequest handler:

int16_t value[2];        // two signed integers of 16 bit, total 4 bytes
value[0] = analogRead(A1);
value[1] = analogRead(A2);
Wire.write( value, sizeof(value));

Please don't use Wire.beginTransmission() and Wire.endTransmission() around the Wire.requestFrom(). Please don't use a delay after a Wire.requestFrom().
See my alternative explanation of the Wire library and the common mistakes.

Thank you for your answer Koepel.

It seems that int16_t can't be used with Wire.write

Message error :

no matching function for call to 'TwoWire::write(int16_t [2], unsigned int)'

Wire.write can only be used with uint8_t

I think that it should be casted it to a byte pointer:

Wire.write( (byte *) value, sizeof(value));

The common way is to use a 'struct'. I think that a pointer to the struct has to be casted as well.

That worked for the Wire.write and Wire.read.

Nothing received by the master but I checked the value array that is good before sending to the master.

Here is the SLAVE code :

#include <Wire.h>

const byte SlaveDeviceId = 3;

void setup()
{
  Serial.begin(9600);

  // Start I²C bus as a slave
  Wire.begin(SlaveDeviceId);

  // Set the callback to call when data is requested.
  Wire.onRequest(requestCallback);
}



void loop()
{

}



void requestCallback()
{
  int16_t value[2];        // two signed integers of 16 bit, total 4 bytes
  value[0] = analogRead(A0);
  value[1] = analogRead(A1);
  Wire.write( (byte *) value, sizeof(value));

//  Serial.println("value ");
//  for (int i = 0; i < sizeof(value); i++) {
//    Serial.print("i=");
//    Serial.println(i);
//    Serial.print("valeur=");
//    Serial.println(value[i], HEX);
//  }
//  Serial.println();


}

Here is the MASTER Code :

#include <Wire.h>

const byte SlaveDeviceId = 3;

void setup()
{
  // Start I²C bus as master
  Wire.begin();

  // For demonstration purposes.
  Serial.begin(9600);
}

void loop()
{

int available = Wire.requestFrom(SlaveDeviceId, 4);
if (available == 4)
{
  int16_t value[2];       // two signed integers of 16 bit, total 4 bytes
  Wire.readBytes((byte *) value, sizeof(value));
}

  delay(10000);
}

I think that should work.
How do you know that nothing is received ?
If the 4 bytes are not received by the Master, then your I2C bus is not working.
What else is connected to the I2C bus ? Do you have pullup resistors ?

Can you choose Slave address 0x20 or so ? A value of 3 is too low, and I read that even 8 could cause a problem.

Sorry you were true just forgot to Serial Print the value[0] and value[1].

Same problem as previously : value from sensor checked with the slave is 202 and the master receives 250 ! I can't understand why !

I also tested different address : same problem. The fact is that if I code value[0]=202 and send it the master can properly receives the good value.

Can you show both sketches that have the problem ?

Master code


#include <Wire.h>

const byte SlaveDeviceId = 0x20;

void setup()
{
  // Start I²C bus as master
  Wire.begin();

  // For demonstration purposes.
  Serial.begin(9600);
}

void loop()
{

  int available = Wire.requestFrom(SlaveDeviceId, 4);
  if (available == 4)
  {
    int16_t value[2];       // two signed integers of 16 bit, total 4 bytes
    Wire.readBytes((byte *) value, sizeof(value));
    Serial.println(value[1]);
    Serial.println(value[0]);
  }

  else
  {
    Serial.print("Erreur");
  }

  delay(10000);

Slave code

#include <Wire.h>

const byte SlaveDeviceId = 0x20;

void setup()
{
  Serial.begin(9600);

  // Start I²C bus as a slave
  Wire.begin(SlaveDeviceId);

  // Set the callback to call when data is requested.
  Wire.onRequest(requestCallback);
}



void loop()
{

}



void requestCallback()
{
  int16_t value[2];        // two signed integers of 16 bit, total 4 bytes
  value[0] = analogRead(A0);
  value[1] = analogRead(A1);
  Wire.write( (byte *) value, sizeof(value));

  //  Serial.println("value ");
  //  for (int i = 0; i < sizeof(value); i++) {
  //    Serial.print("i=");
  //    Serial.println(i);
  //    Serial.print("valeur=");
  //    Serial.println(value[i], HEX);
  //  }
  //  Serial.println();


}

The analogRead() may cause a timeout of the current transmission.

The master can not know how many bytes it received, that's a known problem of the I2C protocol. Only 0 bytes signals an general bus error with this slave.

Thank you DrDiettrich

Trying to make the readings of analogic entries before any transmission could solve the problem?

What could you advice after your observations?

What I can't understand is that if I specify the value directly, the communiction is good between slave and master !

What circuitry and programming are you using on the slave to convert 4mA to 0 and 20mA to 1023?

There is a 250 Ohm resistance like in the schema below to get a signal from 0-24V to be 0-5V (0-1023 in the analogic A0/A1). The 4 and the 20 mA can be programmed with the flow we want (0-35 L/min).

With one arduino, everything is ok in terms of values. The only problem we have is getting 250 instead of 202 from slave to master.

There are 2 flow meters with scheme like the picture below, one distance ultrasonic sensor (analogic with a voltage diviser) and 7 moisture sensor (SDI-12) repeated in 6 areas.

The only problem we have is with the flow meters and that's why I isolate only 1 flow meter to test the circuitry and code.

In the schema, 24V +/- refers to 24V power voltage and the flow meter (+/-) in on the left side of the 24V alimentation.

I'd try this:
Transmit a dummy byte (command...) to the slave first. Insert a delay that allows the slave to collect the current analog values in/after its onReceive event for subsequent transmission in onRequest.

If you really used that Master and Slave sketch in your reply #13, and the Master shows 250, then the analogRead() has returned 250. You do not show the value of the Slave, so it is 250.
Sorry for being so strict, but we have to be precise and accurate because something weird is going on.

I don't care if that is not possible, that is what you get. As soon as you use the I2C bus, then it is 250.

Your circuit needs a protection resistor in the signal path to A0. For example 1k (1k to 10k). It is possible to damage the analog circuitry inside the chip, while the rest of the chip still runs. It could be possible that analogRead() no longer reads the analog input correctly.

Do you have a schematic or a layout of your PCB ?
Do you have another Arduino board ? Can you do more tests ? for example with a potentiometer to the analog input, or exchange the Master and the Slave board. Can you show a photo ? Maybe you use a breadboard (they have bad contacts). Maybe a GND is missing between the Arduino boards.

Hi Koepel, I will try all you have mentioned (the potentiometer is really a good idea) and send some pictures.

Note that when I said it returned 250 instead of the value measured by AO, when I just ask the slave to read with the same circuitry, I have the good value. That's why the problem of I2C communication can be one the problem source.

I am going to check if the value returned by the master is proportional to the true value and that's why potentiometer is nice !

When you talk about protection, do you mean just a resistance like in the picture below?

Thank you for your help