I2C communication with SFM3000 series flow sensor from Sensirion

Hi there

I'm trying to communicate with a flow sensor, but I'm having problem understanding how to do the code.

Here is the sensor: https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/5_Mass_Flow_Meters/Sensirion_Mass_Flow_Meters_SFM3000_Datasheet_V2.2.pdf

Here is the I2C notes on the sensor:
https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/5_Mass_Flow_Meters/Sensirion_Mass_Flow_Meters_SFM3000_I2C_Functional_Description_V1.1.pdf

Here is my code:

#include <Wire.h>


void setup()
{
  Wire.begin();
  Serial.begin(9600);
  int a = 0;
  int b = 0;
  int c = 0;  
  
  delay(1000);

  Wire.beginTransmission(byte(0x40)); // transmit to device #064 (0x40)
  Wire.write(byte(0x10));      // 
  Wire.write(byte(0x00));      // 
  Wire.endTransmission();  

  delay(5);

  Wire.requestFrom(0x81, 3); // 
  a = Wire.read(); // first received byte stored here
  b = Wire.read(); // second received byte stored here
  c = Wire.read(); // third received byte stored here
  Serial.print(a);
  Serial.print(b);
  Serial.println(c);

  delay(5);
  
  Wire.requestFrom(0x81, 3); // 
  a = Wire.read(); // first received byte stored here
  b = Wire.read(); // second received byte stored here
  c = Wire.read(); // third received byte stored here
  Serial.print(a);
  Serial.print(b);
  Serial.println(c);

  delay(5);
}

void loop()
{
  Wire.requestFrom(0x81, 2); // 
  a = Wire.read(); // first received byte stored here
  b = Wire.read(); // second received byte stored here
  Serial.println(a, b);

  delay(1000);

}

Arduino IDE will not even accept the code and returns the errror "exit status 1
'a' was not declared in this scope"

You use two different addresses to access the sensor. According to the datasheet 0x40 is correct, 0x81 is wrong. I guess you interpreted the description in the note incorrectly that added the read/write bit to the address. The 0x81 is to be used if you don't use a library for the I2C communication but transfer the bytes directly. But in the Arduino world the Wire library take care of this stuff and you always have to provide the device address and must not calculate low level bytes yourself.

To get around the compile error, define the variable a in the loop too:

void loop()
{
  Wire.requestFrom(0x40, 3); //
  int a = Wire.read(); // first received byte stored here
  int b = Wire.read(); // second received byte stored here
  int crc = Wire.read(); // CRC value
  Serial.println(a, b);

  delay(1000);

}

Thanks pylon. I can now communicate with the sensor, but I only get gibberish on the serial monitor. See below.

175
1331
}
³
³
A5
³
A5
}

So I get "data". The "data" changes when the sensor is affected, and it updates every second. My loop looks like this.

void loop()
{
  Wire.requestFrom(0x40, 3); // 
  int a = Wire.read(); // first received byte stored here
  int b = Wire.read(); // second received byte stored here
  int crc = Wire.read(); // crc value stored here
  Wire.endTransmission();
  Serial.println(a, b);

  delay(1000);

}

In the I2C functional description is this "Bit: 15:0", "#Bits: 16", "Description/Coding: Flow measurement result. Bit<1:0> is always zero"

I'm not even getting numbers out. What is my problem?

I guess this is probably what you wanted to achieve:

uint8_t crc8(const uint8_t data, uint8_t crc) {
  crc ^= data;

  for ( uint8_t i = 8; i; --i ) {
    crc = ( crc & 0x80 )
      ? (crc << 1) ^ 0x31
      : (crc << 1);
  }
  return crc;
}

void loop() {
  Wire.requestFrom(0x40, 3); //
  uint16_t a = Wire.read(); // first received byte stored here
  uint8_t b = Wire.read(); // second received byte stored here
  uint8_t crc = Wire.read(); // crc value stored here
  uint8_t mycrc = 0xFF;
  mycrc = crc8(a, mycrc);
  mycrc = crc8(b, mycrc);
  if (mycrc != crc) {
    Serial.prinln("Error: wrong CRC");
  }
  a = (a << 8) | b;
  a >>= 2;
  Serial.println(a);

  delay(1000);
}

This prints out the flow value once a second.

Thanks for the help, but the code will not compile.

It is nice to just get code, don't get me wrong, but I really can't understand what it all does, and I would like to.

Excuse me there was a typo in the code. The line

Serial.prinln("Error: wrong CRC");

should be

Serial.println("Error: wrong CRC");

To get a complete sketch, you have to keep the include line and the setup() routine from your code.

It is nice to just get code, don't get me wrong, but I really can't understand what it all does, and I would like to.

I got the impression that you don't, but here is a commented version:

void loop() {
  Wire.requestFrom(0x40, 3); // read 3 bytes from device with address 0x40
  uint16_t a = Wire.read(); // first received byte stored here
  uint8_t b = Wire.read(); // second received byte stored here
  uint8_t crc = Wire.read(); // crc value stored here
  uint8_t mycrc = 0xFF; // initialize crc variable
  mycrc = crc8(a, mycrc); // let first byte through CRC calculation
  mycrc = crc8(b, mycrc); // and the second byte too
  if (mycrc != crc) { // check if the calculated and the received CRC byte matches
    Serial.prinln("Error: wrong CRC");
  }
  a = (a << 8) | b; // combine the two received bytes to a 16bit integer value
  a >>= 2; // remove the two least significant bits
  Serial.println(a); // print the value to the serial interface

  delay(1000);
}

Ok. Just to close this so others reading this thread can use the code.

I enden up not reporting the CRC check as I cant get it to work. The readings from the sensor are fine and have been verified with another flowmeter. In the code is an offset so flows in one way are reported as positive numbers and the other way is negative. There is also a scale factor which is dependent on the gas being measured.

#include <Wire.h>


void setup()
{
  Wire.begin();
  Serial.begin(9600);
  int a = 0;
  int b = 0;
  int c = 0;  
  
  delay(1000);

  Wire.beginTransmission(byte(0x40)); // transmit to device #064 (0x40)
  Wire.write(byte(0x10));      // 
  Wire.write(byte(0x00));      // 
  Wire.endTransmission();  

  delay(5);

  Wire.requestFrom(0x40, 3); // 
  a = Wire.read(); // first received byte stored here
  b = Wire.read(); // second received byte stored here
  c = Wire.read(); // third received byte stored here
  Wire.endTransmission();
  Serial.print(a);
  Serial.print(b);
  Serial.println(c);

  delay(5);
  
  Wire.requestFrom(0x40, 3); // 
  a = Wire.read(); // first received byte stored here
  b = Wire.read(); // second received byte stored here
  c = Wire.read(); // third received byte stored here
  Wire.endTransmission();
  Serial.print(a);
  Serial.print(b);
  Serial.println(c);

  delay(5);
}

uint8_t crc8(const uint8_t data, uint8_t crc) {
  crc ^= data;

  for ( uint8_t i = 8; i; --i ) {
    crc = ( crc & 0x80 )
      ? (crc << 1) ^ 0x31
      : (crc << 1);
  }
  return crc;
}

void loop() {

  int offset = 32000; // Offset for the sensor
  float scale = 140.0; // Scale factor for Air and N2 is 140.0, O2 is 142.8

  
  Wire.requestFrom(0x40, 3); // read 3 bytes from device with address 0x40
  uint16_t a = Wire.read(); // first received byte stored here. The variable "uint16_t" can hold 2 bytes, this will be relevant later
  uint8_t b = Wire.read(); // second received byte stored here
  uint8_t crc = Wire.read(); // crc value stored here
  uint8_t mycrc = 0xFF; // initialize crc variable
  mycrc = crc8(a, mycrc); // let first byte through CRC calculation
  mycrc = crc8(b, mycrc); // and the second byte too
  if (mycrc != crc) { // check if the calculated and the received CRC byte matches
    //Serial.println("Error: wrong CRC");
  }
  a = (a << 8) | b; // combine the two received bytes to a 16bit integer value
  // a >>= 2; // remove the two least significant bits
  float Flow = ((float)a - offset) / scale; 
  //Serial.println(a); // print the raw data from the sensor to the serial interface
  Serial.println(Flow); // print the calculated flow to the serial interface

  delay(25);
}
1 Like

Good morning
I need a 0-5V analogue output, I wanted to use
the MCP4725 module, I would like to know how to do it.
I state that I am not very experienced it is the first time I use Arduino.
You can help me.
Thank you
Achille

Don't hijack another thread, start a new one as your setup is different.

Hello Pylon
Thanks for the reply.
You are very experienced, I was counting on you that you could give me
a help.
Thank you anyway
Achilles

This code helped me a lot, but it doesn't read correctly the data because we skipped info. from the manufacturers Datasheet. This one should work.

#include <Wire.h>
  int offset = 32000; // Offset for the sensor
  float scale = 140.0; // Scale factor for Air and N2 is 140.0, O2 is 142.8 

void setup()
{
  Wire.begin();
  Serial.begin(9600);
  int a = 0;
  int b = 0;
  int c = 0; 

 /*Wire.requestFrom(0x40, 3); //
  a = Wire.read(); // first received byte stored here
  b = Wire.read(); // second received byte stored here
  c = Wire.read(); // third received byte stored here
  Wire.endTransmission();
  delay(5);
 
  Wire.requestFrom(0x40, 3); //
  a = Wire.read(); // first received byte stored here
  b = Wire.read(); // second received byte stored here
  c = Wire.read(); // third received byte stored here
  Wire.endTransmission();
  delay(5);*/
}

uint8_t crc8(const uint8_t data, uint8_t crc) {
  crc ^= data;

  for ( uint8_t i = 8; i; --i ) {
    crc = ( crc & 0x80 )
      ? (crc << 1) ^ 0x31
      : (crc << 1);
  }
  return crc;
}

void loop() {
  delay(500);
  Wire.beginTransmission(byte(0x40)); // transmit to device #064 (0x40)
  Wire.write(byte(0x10));      //
  Wire.write(byte(0x00));      //
  Wire.endTransmission();
  Wire.requestFrom(0x40, 3); // read 3 bytes from device with address 0x40
  while (Wire.available()) { // slave may send less than requested
  uint16_t a = Wire.read(); // first received byte stored here. The variable "uint16_t" can hold 2 bytes, this will be relevant later
  uint8_t b = Wire.read(); // second received byte stored here
  uint8_t crc = Wire.read(); // crc value stored here
  uint8_t mycrc = 0xFF; // initialize crc variable
  mycrc = crc8(a, mycrc); // let first byte through CRC calculation
  mycrc = crc8(b, mycrc); // and the second byte too
  if (mycrc != crc) { // check if the calculated and the received CRC byte matches
  Serial.println("Error: wrong CRC");
  }
  Serial.println('p');
  Serial.println(a);
  Serial.println(b);
  Serial.println(crc);
  Serial.println('h');
  a = (a << 8) | b; // combine the two received bytes to a 16bit integer value
  // a >>= 2; // remove the two least significant bits
  int Flow = (a - offset) / scale;
  //Serial.println(a); // print the raw data from the sensor to the serial interface
  Serial.println(Flow); // print the calculated flow to the serial interface
   }
}

Forse:
Ok. Just to close this so others reading this thread can use the code.

I enden up not reporting the CRC check as I cant get it to work. The readings from the sensor are fine and have been verified with another flowmeter. In the code is an offset so flows in one way are reported as positive numbers and the other way is negative. There is also a scale factor which is dependent on the gas being measured.

#include <Wire.h>

void setup()
{
  Wire.begin();
  Serial.begin(9600);
  int a = 0;
  int b = 0;
  int c = 0; 
 
  delay(1000);

Wire.beginTransmission(byte(0x40)); // transmit to device #064 (0x40)
  Wire.write(byte(0x10));      //
  Wire.write(byte(0x00));      //
  Wire.endTransmission();

delay(5);

Wire.requestFrom(0x40, 3); //
  a = Wire.read(); // first received byte stored here
  b = Wire.read(); // second received byte stored here
  c = Wire.read(); // third received byte stored here
  Wire.endTransmission();
  Serial.print(a);
  Serial.print(b);
  Serial.println(c);

delay(5);
 
  Wire.requestFrom(0x40, 3); //
  a = Wire.read(); // first received byte stored here
  b = Wire.read(); // second received byte stored here
  c = Wire.read(); // third received byte stored here
  Wire.endTransmission();
  Serial.print(a);
  Serial.print(b);
  Serial.println(c);

delay(5);
}

uint8_t crc8(const uint8_t data, uint8_t crc) {
  crc ^= data;

for ( uint8_t i = 8; i; --i ) {
    crc = ( crc & 0x80 )
      ? (crc << 1) ^ 0x31
      : (crc << 1);
  }
  return crc;
}

void loop() {

int offset = 32000; // Offset for the sensor
  float scale = 140.0; // Scale factor for Air and N2 is 140.0, O2 is 142.8

Wire.requestFrom(0x40, 3); // read 3 bytes from device with address 0x40
  uint16_t a = Wire.read(); // first received byte stored here. The variable "uint16_t" can hold 2 bytes, this will be relevant later
  uint8_t b = Wire.read(); // second received byte stored here
  uint8_t crc = Wire.read(); // crc value stored here
  uint8_t mycrc = 0xFF; // initialize crc variable
  mycrc = crc8(a, mycrc); // let first byte through CRC calculation
  mycrc = crc8(b, mycrc); // and the second byte too
  if (mycrc != crc) { // check if the calculated and the received CRC byte matches
    //Serial.println("Error: wrong CRC");
  }
  a = (a << 8) | b; // combine the two received bytes to a 16bit integer value
  // a >>= 2; // remove the two least significant bits
  float Flow = ((float)a - offset) / scale;
  //Serial.println(a); // print the raw data from the sensor to the serial interface
  Serial.println(Flow); // print the calculated flow to the serial interface

delay(25);
}