diffential pressure and temp. devices with specific i2c data format

Moderators: grovkillen, Stuntteam, TD-er

Post Reply
Message
Author
analogworld
Normal user
Posts: 16
Joined: 04 Nov 2019, 11:31

diffential pressure and temp. devices with specific i2c data format

#1 Post by analogworld » 13 Dec 2019, 16:22

Dear All,

I need to use a differential pressure sensor to monitor the static pressure at the output /plenum of my ceiling air conditioner. The sensor has also the good idea to include temperature according to this data format:

https://ibb.co/ZJfWmXm

How to "parse" the 32 bits code in order to get the pressure and the temperature separately into something that I can send to domoticz, or something that I can send to ther easpeasy devices using p2p protocol ?

I love the easpeasy firmware so much I don't want to use anything else, your help is greatly appreciated.

TD-er
Core team member
Posts: 8643
Joined: 01 Sep 2017, 22:13
Location: the Netherlands
Contact:

Re: diffential pressure and temp. devices with specific i2c data format

#2 Post by TD-er » 13 Dec 2019, 16:39

Hmm, that's a tricky one, since most data from a sensor is converted to/from a float.
And float values do not have a lot of bits resolution, so you will loose quite a bit (pun intended) of least significant bits.

How do you fetch this value?
Are you writing a new plugin for it, or do you get it from MQTT import, or some other route?

I think it would be the 'easiest' to have a plugin for it, since then you can do whatever you like with the digital data and splitting an unsigned int into separate values is not really rocket science.

analogworld
Normal user
Posts: 16
Joined: 04 Nov 2019, 11:31

Re: diffential pressure and temp. devices with specific i2c data format

#3 Post by analogworld » 13 Dec 2019, 17:44

Hello TD-er,

Thank you for your insighful comments as usual.
I understand complely the matter regarding the floating number vs accuracy.
I haven't bought the sensor yet, let's then drop this one if it is too complex and do otherwise:

--> What should look like a data format returned by an I2c sensor that cover both temperature and differential pressure ? (including how many bits).

I believe that the output format should be something close to the one of bmpX80 right ? The simplest way would be rewrite a plugin similar to the BMPX80 I guess...

Thank you and best regards,

TD-er
Core team member
Posts: 8643
Joined: 01 Sep 2017, 22:13
Location: the Netherlands
Contact:

Re: diffential pressure and temp. devices with specific i2c data format

#4 Post by TD-er » 13 Dec 2019, 20:59

If the data is read as a single 32 bit value, then it is best to go for a new plugin.

If you would start from an existing plugin, better have a look at the BME280, as that one is using the more recent insights like support for multiple instances of the same plugin and rescheduling itself when a value is ready to be read.
It also uses newly added functions to read a range of registers all in a single I2C transfer.
This is needed for the BME280 to make sure you get the registers in the same state and not some registers already changed for another reading.

analogworld
Normal user
Posts: 16
Joined: 04 Nov 2019, 11:31

Re: diffential pressure and temp. devices with specific i2c data format

#5 Post by analogworld » 14 Dec 2019, 23:35

Hello TD-er,

all the sensor that I came across do not have a small adressable memory like in the bmp280. Instead, you get a stream of bytes of data (the 2 first are for pressure, the 2 next are for compensated temperature).

I would move on for the TruStability sensor from Honeywell, since there is an available library for arduino : https://playground.arduino.cc/Main/Hone ... reSensors/

From there, I assume it should be possible to build a plugin because I've seen that some plugin are directly inspired from some arduino code...

any comment/objections on this would be welcomed.

TD-er
Core team member
Posts: 8643
Joined: 01 Sep 2017, 22:13
Location: the Netherlands
Contact:

Re: diffential pressure and temp. devices with specific i2c data format

#6 Post by TD-er » 15 Dec 2019, 00:52

all the sensor that I came across do not have a small adressable memory like in the bmp280. Instead, you get a stream of bytes of data (the 2 first are for pressure, the 2 next are for compensated temperature).
Still you need to read them all at once and since I2C devices do not initiate data transfers, you have to ask them for it.
Addressing a register is asking for a response, but there can also be queries which deliver just the standard "final" reply instead of setting a register, waiting for data, setting another register, etc.

So it is not that different.

Personally I don't like to use external libraries, partly because most also do stuff you don't want, like waiting for the data using calls to delay.

And like I said splitting a 32 bit int into several values is really really simple.

analogworld
Normal user
Posts: 16
Joined: 04 Nov 2019, 11:31

Re: diffential pressure and temp. devices with specific i2c data format

#7 Post by analogworld » 17 Dec 2019, 09:10

Hello TD-er,

understood, I will take this guy then (that happens to be much cheaper):

https://eu.mouser.com/datasheet/2/589/S ... 391765.pdf

Where I can read the MSB's and LSB's with 2 register readings at 2 different address. it doesn't have temperature reading but I get it through over sensors. I will use the analog output to amplify it and zoom it to get some region between 0 and 150 Pa.

Thanks for your comments on the matter so far.

TD-er
Core team member
Posts: 8643
Joined: 01 Sep 2017, 22:13
Location: the Netherlands
Contact:

Re: diffential pressure and temp. devices with specific i2c data format

#8 Post by TD-er » 18 Dec 2019, 09:11

analogworld wrote: 17 Dec 2019, 09:10 [...]
I will use the analog output to amplify it [...]
Still staying faithful to your user name ;)

analogworld
Normal user
Posts: 16
Joined: 04 Nov 2019, 11:31

Re: diffential pressure and temp. devices with specific i2c data format

#9 Post by analogworld » 07 Jul 2020, 22:55

Well, the analog output was killed by the adc analog input of the wemos beeing at 12V. don't know how.
So now, my SMI5852 pressure sensor only works on I2c.

Below is a flat arduino "flat" code that does not uses a library, how easy do you think it could be for beginner to write a plugin for this ?
(Because I'm electronician with limited knowledge in programming)
Believe me this pressure is nice piece of hardware using advanced sigma-delta analog to digital conversion, I believe it is worth it.

Code: Select all

*/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include "Wire.h"
//DO NOT CHANGE THESE VALUES (THESE ARE THE SENSOR ACCESS ADDRESSES FOR I2C DATA)
#define SENSOR_I2C_ADDR         0x5f //
#define SENSOR_REG_PRESSURE_LSB 0x80 //
#define SENSOR_REG_PRESSURE_MSB 0x81 //
#define SENSOR_REG_TEMP_LSB     0x82 //
#define SENSOR_REG_TEMP_MSB     0x83 //
////////////////////////////////////////////////////////////////////////////////////////////
//ADJUST THESE VALUES FOR THE TYPE OF SENSOR,THE DESIRED UNIT OF MAESURE, THE READABLE PRESSURE RANGE,AND THE ZERO VALUE. EACH INDIVIDUAL SENSOR HAS ITSS OWN ZERO VAL. MY ZERO VALUE IS 1960, BUT YOURS WILL BE DIFFERENT.
////////////////////////////////////////////////////////////////////////////////////////////
String SENSOR_TYPE             = ("DIFFERENTIAL"); // DIFFERENTIAL OR GAUGE ///////////////////////////////////
String UNIT_OF_MEASURE         = ("inWC");         // INWC, PSI, PA  "inches h2o, lbs/in^2, Pascals"               //
double SENSOR_MINIMUM          = -1.5;             // LOWEST RATED PRESSURE FOR UINT of MEASURE SELECTED                 //
uint16_t SENSOR_ZERO_READING   = 1960;             // RAW READING WHEN THE SENSOR IS READING ZERO(its different for every sensor)  //  ADJUST THESE TO THE MODEL YOU HAVE
double SENSOR_MAXIMUM          = 1.5;              // MAXIMUM RATED PRESSURE FOR UNIT SELECTEED                          //
uint16_t TIME_BETWEEN_READINGS = 1000;             // READING INTERVAL IN MILLISECONDS(time between reads)   //    //
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//DO NOT ADJUST ANYTHING HERE.
uint8_t  pressLSB;
uint8_t  pressMSB;
uint16_t pressRaw;
uint8_t  tempLSB;
uint8_t  tempMSB;
uint16_t tempRaw;
double pressure;                         // ACTUAL MAESURED PRESSURE
double temperature;                      // ACTUAL MAESURED TEMPERATURE

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


void loop()

// READ THE PRESSURE
{
  Wire.beginTransmission(SENSOR_I2C_ADDR);  //Send the address of device
  Wire.write(SENSOR_REG_PRESSURE_MSB);      //send the address of the MSB
  Wire.endTransmission(false);              //Send a repeated start
  Wire.requestFrom(SENSOR_I2C_ADDR, 1);     //Request 1 byte of data

  if (Wire.available() == 1)
  {
    pressMSB = Wire.read();                      // Read the MSB register
  }
  else                                      //If more or less than 1 byte have been received something isn't right.
  {
    while (Wire.available())                //Clear the buffer
      Wire.read();
    Serial.println("I2C transaction error");
  }
  Wire.beginTransmission(SENSOR_I2C_ADDR);  // call the device address again
  Wire.write(SENSOR_REG_PRESSURE_LSB);      // call the LSB register address
  Wire.endTransmission(false);              // repeated start
  Wire.requestFrom(SENSOR_I2C_ADDR, 1);     // Get the 1 byte of data from the LSB register

  if (Wire.available() == 1)
  { // byte contains the 6 least significant bits of the LSB
    pressLSB = Wire.read();
  }
  else                                      //If more or less than 1 byte have been received something isn't right.
  {
    while (Wire.available())                //Clear the buffer
      Wire.read();
    Serial.println("I2C transaction error");
  }
  // Build the raw pressure from the two bytes received according to the release notes
  pressRaw = ((dataMSB << 8)|(dataLSB << 2)) >> 2;

  pressRaw = pressMSB << 8;                   // Bit manipulation to move the data to correct positions
  pressLSB = pressLSB << 2;
  pressRaw = pressRaw | pressLSB;
  pressRaw = pressRaw >> 2;



// READ THE TEMPERATURE
  Wire.beginTransmission(SENSOR_I2C_ADDR);  //Send the address of device
  Wire.write(SENSOR_REG_TEMP_MSB);      //send the address of the MSB
  Wire.endTransmission(false);              //Send a repeated start
  Wire.requestFrom(SENSOR_I2C_ADDR, 1);     //Request 1 byte of data

  if (Wire.available() == 1)
  {
    tempMSB = Wire.read();                      // Read the MSB register
  }
  else                                      //If more or less than 1 byte have been received something isn't right.
  {
    while (Wire.available())                //Clear the buffer
      Wire.read();
    Serial.println("I2C transaction error");
  }

  Wire.beginTransmission(SENSOR_I2C_ADDR);  // call the device address again
  Wire.write(SENSOR_REG_TEMP_LSB);      // call the LSB register address
  Wire.endTransmission(false);              // repeated start
  Wire.requestFrom(SENSOR_I2C_ADDR, 1);     // Get the 1 byte of data from the LSB register

  if (Wire.available() == 1)
  { // byte contains the 6 least significant bits of the LSB
    tempLSB = Wire.read();
  }
  else                                      //If more or less than 1 byte have been received something isn't right.
  {
    while (Wire.available())                //Clear the buffer
      Wire.read();
    Serial.println("I2C transaction error");
  }

  tempRaw = tempMSB << 8;                   // Bit manipulation to move the relevant data to correct positions
  tempLSB = tempLSB << 2;
  tempRaw = tempRaw | tempLSB;
  tempRaw = tempRaw >> 2;
  temperature = mapfloat(tempRaw, 2770, 2100, 15, 75);
   

// PARSE THE DATA INTO SOMETHING MEANINGFUL
  if (SENSOR_TYPE == "GAUGE") {
    pressure = mapfloat(pressRaw, SENSOR_ZERO_READING, 3891, 0.0, SENSOR_MAXIMUM);
  }

  if (SENSOR_TYPE == "DIFFERENTIAL") {
    if (pressRaw == SENSOR_ZERO_READING) {
      pressure = 0.0;
    }
    if (pressRaw > SENSOR_ZERO_READING) {
      pressure = mapfloat(pressRaw, SENSOR_ZERO_READING, 3891, 0.0, SENSOR_MAXIMUM);
    }
    if (pressRaw < SENSOR_ZERO_READING) {
      pressure = mapfloat(pressRaw, 205, SENSOR_ZERO_READING, SENSOR_MINIMUM, 0.0);
    }
  }

// PRINT THE READINGS TO THE SERIAL MONITOR
  Serial.print(SENSOR_TYPE);
  Serial.print(" ");
  Serial.print(pressure, 3);
  Serial.print(" ");
  Serial.println(UNIT_OF_MEASURE);
  Serial.print("Raw Pressure Value: ");
  Serial.println(pressRaw);
  Serial.print("TEMPERATURE: ");
  Serial.print(temperature);
  Serial.println("*C");
  Serial.print("Raw Temperature Value: ");
  Serial.println(tempRaw);
  Serial.println();
  Serial.println();
  delay(TIME_BETWEEN_READINGS);
}

// Below is the math I needed to get the actual reading from the pressure sensor.I MODIFIED THE "MAP" function to accept and return floating point numerals.
float mapfloat(float x, float in_min, float in_max, float out_min, float out_max)
{
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

TD-er
Core team member
Posts: 8643
Joined: 01 Sep 2017, 22:13
Location: the Netherlands
Contact:

Re: diffential pressure and temp. devices with specific i2c data format

#10 Post by TD-er » 07 Jul 2020, 23:45

Well that doesn't look too hard, as most of the code you gave would be placed in the PLUGIN_READ part of a plugin.

Maybe have a loop at the P006_BMP085.ino file to get an idea of what's needed.

analogworld
Normal user
Posts: 16
Joined: 04 Nov 2019, 11:31

Re: diffential pressure and temp. devices with specific i2c data format

#11 Post by analogworld » 08 Jul 2020, 00:26

Hello TD-er,

thanks for your suggestions. I will start working on it. There are some few steps on the way that I will try to overcome.

I'll be back.

analogworld
Normal user
Posts: 16
Joined: 04 Nov 2019, 11:31

Re: diffential pressure and temp. devices with specific i2c data format

#12 Post by analogworld » 08 Jul 2020, 01:32

By the way, there are many pre-releases. Is there a recommended release to start from ?
Thanks.

TD-er
Core team member
Posts: 8643
Joined: 01 Sep 2017, 22:13
Location: the Netherlands
Contact:

Re: diffential pressure and temp. devices with specific i2c data format

#13 Post by TD-er » 08 Jul 2020, 10:45

Just base it on the current 'mega' branch.

analogworld
Normal user
Posts: 16
Joined: 04 Nov 2019, 11:31

Re: diffential pressure and temp. devices with specific i2c data format

#14 Post by analogworld » 08 Jul 2020, 13:48

Fine, thank you very much.

analogworld
Normal user
Posts: 16
Joined: 04 Nov 2019, 11:31

Re: diffential pressure and temp. devices with specific i2c data format

#15 Post by analogworld » 10 Jul 2020, 22:29

My deep thanks to TD-Er, you're a true source of inspiration.

I don't remember the last time I've coded in C, but this time I managed to get my plugin working, can't believe it.

Code: Select all

#ifdef USES_P777
//#######################################################################################################
//######################## Plugin 777 SM5852 I2C Pressure Sensor and Temperature indication  ###########
//#######################################################################################################

#include "_Plugin_Helper.h"
#include "Wire.h"
#include "math.h"
#define PLUGIN_777
#define PLUGIN_ID_777        6
#define PLUGIN_NAME_777       "Environment - SM5852"
#define PLUGIN_VALUENAME1_777 "Temperature"
#define PLUGIN_VALUENAME2_777 "Pressure"


// TODO this will not work if we have more than one of this task!
boolean Plugin_777_init = false;

boolean Plugin_777(byte function, struct EventStruct *event, String& string)
{
  boolean success = false;

  switch (function)
  {
    case PLUGIN_DEVICE_ADD:
      {
        Device[++deviceCount].Number = PLUGIN_ID_777;
        Device[deviceCount].Type = DEVICE_TYPE_I2C;
        Device[deviceCount].VType = SENSOR_TYPE_TEMP_BARO;
        Device[deviceCount].Ports = 0;
        Device[deviceCount].PullUpOption = false;
        Device[deviceCount].InverseLogicOption = false;
        Device[deviceCount].FormulaOption = true;
        Device[deviceCount].ValueCount = 2;
        Device[deviceCount].SendDataOption = true;
        Device[deviceCount].TimerOption = true;
        Device[deviceCount].GlobalSyncOption = true;
        break;
      }

    case PLUGIN_GET_DEVICENAME:
      {
        string = F(PLUGIN_NAME_777);
        break;
      }

    case PLUGIN_GET_DEVICEVALUENAMES:
      {
        strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_777));
        strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_777));
        break;
      }

    case PLUGIN_READ:
      {
          UserVar[event->BaseVarIndex] = Plugin_777_SM5852_readTemperature();
		  //Collect raw pressure at 0 using the command below and report it in Plugin_777_SM5852_readPressure
          //UserVar[event->BaseVarIndex + 1] = Plugin_777_SM5852_readRawPressure();
		  UserVar[event->BaseVarIndex + 1] = Plugin_777_SM5852_readPressure();
          String log = F("SM5852  : Temperature: ");
          log += UserVar[event->BaseVarIndex];
          addLog(LOG_LEVEL_INFO, log);
          log = F("SM5852  : Pressure: ");
          log += UserVar[event->BaseVarIndex + 1];
          addLog(LOG_LEVEL_INFO, log);
          success = true;
        
        break;
      }

  }
  return success;
}

#define SENSOR_I2C_ADDR         0x5f //
#define SENSOR_REG_PRESSURE_LSB 0x80 //
#define SENSOR_REG_PRESSURE_MSB 0x81 //
#define SENSOR_REG_TEMP_LSB     0x82 //
#define SENSOR_REG_TEMP_MSB     0x83 //

/*********************************************************************/
boolean Plugin_777_SM5852_begin()
/*********************************************************************/
{
  if (I2C_read8_reg(SENSOR_I2C_ADDR, 0xD0) != 0x55) return false;


  return(true);
}

/*********************************************************************/
uint16_t Plugin_777_SM5852_readRawTemperature(void)
/*********************************************************************/
{
  uint16_t tempMSB;
  uint8_t tempLSB;
  uint16_t tempRaw;

  Wire.beginTransmission(SENSOR_I2C_ADDR);  //Send the address of device
  Wire.write(SENSOR_REG_TEMP_MSB);      //send the address of the MSB
  Wire.endTransmission(false);              //Send a repeated start
  Wire.requestFrom(SENSOR_I2C_ADDR, 1);     //Request 1 byte of data

  if (Wire.available() == 1)
  {
    tempMSB = Wire.read();                      // Read the MSB register
  }
  else                                      //If more or less than 1 byte have been received something isn't right.
  {
    while (Wire.available())                //Clear the buffer
      Wire.read();
    Serial.println("I2C transaction error");
  }
  Wire.beginTransmission(SENSOR_I2C_ADDR);  // call the device address again
  Wire.write(SENSOR_REG_TEMP_LSB);      // call the LSB register address
  Wire.endTransmission(false);              // repeated start
  Wire.requestFrom(SENSOR_I2C_ADDR, 1);     // Get the 1 byte of data from the LSB register

  if (Wire.available() == 1)
  { // byte contains the 6 least significant bits of the LSB
    tempLSB = Wire.read();
  }
  else                                      //If more or less than 1 byte have been received something isn't right.
  {
    while (Wire.available())                //Clear the buffer
      Wire.read();
    Serial.println("I2C transaction error");
  }
  // Build the raw tempure from the two bytes received according to the release notes
  //tempRaw = ((dataMSB << 8)|(dataLSB << 2)) >> 2;

  tempRaw = tempMSB * 256;                   // Bit manipulation to move the data to correct positions
  tempLSB = tempLSB * 4;
  tempRaw = (tempRaw + tempLSB)/4;
  
  return tempRaw;
}

/*********************************************************************/
uint16_t Plugin_777_SM5852_readRawPressure(void)
/*********************************************************************/
{
  uint16_t pressMSB;
  uint8_t pressLSB;
  uint16_t pressRaw;

  Wire.beginTransmission(SENSOR_I2C_ADDR);  //Send the address of device
  Wire.write(SENSOR_REG_PRESSURE_MSB);      //send the address of the MSB
  Wire.endTransmission(false);              //Send a repeated start
  Wire.requestFrom(SENSOR_I2C_ADDR, 1);     //Request 1 byte of data

  if (Wire.available() == 1)
  {
    pressMSB = Wire.read();                      // Read the MSB register
  }
  else                                      //If more or less than 1 byte have been received something isn't right.
  {
    while (Wire.available())                //Clear the buffer
      Wire.read();
    Serial.println("I2C transaction error");
  }
  Wire.beginTransmission(SENSOR_I2C_ADDR);  // call the device address again
  Wire.write(SENSOR_REG_PRESSURE_LSB);      // call the LSB register address
  Wire.endTransmission(false);              // repeated start
  Wire.requestFrom(SENSOR_I2C_ADDR, 1);     // Get the 1 byte of data from the LSB register

  if (Wire.available() == 1)
  { // byte contains the 6 least significant bits of the LSB
    pressLSB = Wire.read();
  }
  else                                      //If more or less than 1 byte have been received something isn't right.
  {
    while (Wire.available())                //Clear the buffer
      Wire.read();
    Serial.println("I2C transaction error");
  }
  // Build the raw pressure from the two bytes received according to the release notes
  //pressRaw = ((dataMSB << 8)|(dataLSB << 2)) >> 2;

  pressRaw = pressMSB * 256;                   // Bit manipulation to move the data to correct positions
  pressLSB = pressLSB * 4;
  pressRaw = (pressRaw + pressLSB)/4;
  
  return pressRaw;
}

/*********************************************************************/
float Plugin_777_SM5852_readTemperature(void)
/*********************************************************************/
{
  int16_t tempRaw;
  float temp;

  tempRaw = Plugin_777_SM5852_readRawTemperature();
  temp=Plugin_777_mapfloat(tempRaw, 2770, 2100, 15, 75);

  return temp;
}


/*********************************************************************************************/
float Plugin_777_mapfloat(float x, float in_min, float in_max, float out_min, float out_max)
/*********************************************************************************************/
{
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

/*********************************************************************/
float Plugin_777_SM5852_readPressure(void)
/*********************************************************************/
{
  double SENSOR_MINIMUM          = -300;             // LOWEST RATED PRESSURE FOR UINT of MEASURE SELECTED                 //
  uint16_t SENSOR_ZERO_READING   = 2193;             // RAW READING WHEN THE SENSOR IS READING ZERO(its different for every sensor)  //  ADJUST THESE TO THE MODEL YOU HAVE
  double SENSOR_MAXIMUM          = 300;              // MAXIMUM RATED PRESSURE FOR UNIT SELECTEED                          //
  
  int16_t pressRaw;
  float pressure;

  pressRaw = Plugin_777_SM5852_readRawPressure();
  if (pressRaw == SENSOR_ZERO_READING) {
      pressure = 0.0;
    }
  if (pressRaw > SENSOR_ZERO_READING) {
      pressure = Plugin_777_mapfloat(pressRaw, SENSOR_ZERO_READING, 3891, 0.0, SENSOR_MAXIMUM);
    }
  if (pressRaw < SENSOR_ZERO_READING) {
      pressure = Plugin_777_mapfloat(pressRaw, 205, SENSOR_ZERO_READING, SENSOR_MINIMUM, 0.0);
    }

  return pressure;
}


#endif // USES_P777

TD-er
Core team member
Posts: 8643
Joined: 01 Sep 2017, 22:13
Location: the Netherlands
Contact:

Re: diffential pressure and temp. devices with specific i2c data format

#16 Post by TD-er » 10 Jul 2020, 22:35

Great!

Looking forward to see your pull request on GitHub :)

If you're not too familiar with Git, I strongly suggest to have a look at GitKraken as a GUI tool for Git.
At least it made clear to me how Git worked, even though I had been using it for a few years in my day job... (every time I messed up, walking over to the one Git guru asking to help me out :) )

Post Reply

Who is online

Users browsing this forum: No registered users and 31 guests