SensorTechnics i2c Pressure Sensor

Hello All,

I'm trying to interface a SensorTechnics pressure sensor to a Mega. The device is a SSIB001GU9AH5 0-1bar stainless steel housed pressure sensor, seemingly ideal (if a little expensive @£80) for what I need.
It's i2c capable, but also has a analogue voltage out. I'd like to us the i2c function as (if I can get it working) because ultimately I'd like an array of the units.
Attached are the datasheets that I've managed to track down. In one of these there is the code snippet below, which I'm guessing is C ??, but as a Arduio beginner I'm finding it hard to follow/translate this into something I can use on the Arduino.

The datasheet also specifies that the device needs 1.5 K Ohm pullup resistors on from the 5v supply as well as 240 Ohm resistors on the SDA\SCL lines

My question is two fold:

  1. Can anyone help me to understand this code and how to get it working?
  2. Does the Arduino already have these pullup resistors on the SDA & SCL lines?

I wondered if this might be a device that could be added to the Arduino library....

Many thanks,

byte byte_msb, byte_lsb; // 8bit values
int16 pressure; // 16bit value
// Set I2C unit to I2C master mode, clock speed 100 kHz and 7 bit addressing
configureI2C (I2C_MASTER | CLK_SPEED_100KHZ | ADDRESSING_7BIT);
// Set the target address of the sensor (0x78 = 120dec)
I2C_set_target(0x78);
// Send start condition for reading from sensor (slave)
I2C_send_start_read();
// Read first (MSB) data byte and answer with ACK (continue communication)
I2C_read (&byte_msb, SEND_ACK);
// Read second (LSB) data byte and answer with NACK (end communication)
I2C_read (&byte_lsb, SEND_NACK);
// Send Stop condition
I2C_send_stop();
// Put both values together
pressure = ((int16)byte_msb << 8) | byte_lsb;

AN_I2C-Bus-HDI-HCLA-HCA-SSI_E_11155.pdf (54 KB)

DS_Standard-SSI_E_11671.pdf (116 KB)

The datasheet also specifies that the device needs 1.5 K Ohm pullup resistors on from the 5v supply as well as 240 Ohm resistors on the SDA\SCL lines

Use 4k7 pull-ups (1k5 @5V is outside the bus specification) and I don't see a reason for the 240 ? resistors in the lines (maybe prevent short circuit in case of wrong wiring).

Does the Arduino already have these pullup resistors on the SDA & SCL lines?

It does but they are too weak for most cases.

Can anyone help me to understand this code and how to get it working?

Translated to Arduino-C:

byte byte_msb, byte_lsb; // 8bit values
int16_t pressure; // 16bit value
// Set I2C unit to I2C master mode, clock speed 100 kHz and 7 bit addressing
// ### configureI2C (I2C_MASTER | CLK_SPEED_100KHZ | ADDRESSING_7BIT);
Wire.begin();
// Set the target address of the sensor (0x78 = 120dec)
// ### I2C_set_target(0x78);
// Send start condition for reading from sensor (slave)
// ### I2C_send_start_read();
Wire.requestFrom(0x78, 2); // request two bytes from I2C slave address 0x78
// Read first (MSB) data byte and answer with ACK (continue communication)
// ### I2C_read (&byte_msb, SEND_ACK);
byte_msb = Wire.read();
// Read second (LSB) data byte and answer with NACK (end communication)
// ### I2C_read (&byte_lsb, SEND_NACK);
byte_lsb = Wire.read();
// Send Stop condition
// ### I2C_send_stop();
// Put both values together
pressure = ((int16_t)byte_msb << 8) | byte_lsb;

Thanks very much for taking the time to reply Pylon,

The code makes sense now, but I cant get the device to be recognised on the i2C bus with the 4k7 pullup resistors installed.

I've tried the following i2c scanner with a HMC5883L connected onto the bus, which is detected, but not so the SSI device so I guess its a wiring\pull up resistor\address issue?

I might have to resort to the analogue pin after all..... :frowning:

#include <Wire.h>
void setup()
{
  Wire.begin();
  Serial.begin(115200);
  Serial.println("\nI2C Scanner");
}
void loop()
{
  byte error, address;
  int nDevices;
  Serial.println("Scanning...");
  nDevices = 0;
  for(address = 1; address < 120; address++ ) 
  {
    // The i2c_scanner uses the return value of
    // the Write.endTransmisstion to see if
    // a device did acknowledge to the address.
    Wire.beginTransmission(address);
    error = Wire.endTransmission();
    if (error == 0)
    {
      Serial.print("I2C device found at address 0x");
      if (address<16) 
        Serial.print("0");
      Serial.print(address,HEX);
      Serial.println("  !");
      nDevices++;
    }
    else if (error==4) 
    {
      Serial.print("Unknown error at address 0x");
      if (address<16) 
        Serial.print("0");
      Serial.println(address,HEX);
    }    
  }
  if (nDevices == 0)
    Serial.println("No I2C devices found\n");
  else
    Serial.println("done\n");
  delay(5000);           // wait 5 seconds for next scan
}
  for(address = 1; address < 120; address++ )

This code isn't scanning the sensors address which is 120 (0x78). Might that be a reason for it?

Well spotted Pylon, but unfortunately increasing it to 150 as below has made no difference..... :frowning:

for(address = 1; address < 150; address++ )

In the datasheet I cannot find any reference that the sensor supports an I2C write. The I2C scan code does a write and that maybe is the problem. What does the code I wrote for you return? You may also check the return value of Wire.requestFrom(), it should return 2 if the sensor really returned data.

Pylon,

Thanks for your help, you've helped me solve it! Thank you. I found that it was a voltage dip in the 5v supply from the Arduino board. I measured it at just less than 4.3v and running the Mega off separate power supply rather than the USB power the sensor came to life.

Attached is an image of the results (me blowing air into the sensor), the values are in counts of course.

For reference (in case I lose it!), here's the final code I used:

//Code to read a SensorTronics SSIB001GU9AH5 Pressure Sensor
//Thanks to 'Pylon' on Arduino Forum.

#include <Wire.h> //For I2C comms
byte byte_msb, byte_lsb; // 8bit values
int16_t pressure; // 16bit value

void setup(void) { 
// Set I2C unit to I2C master mode, clock speed 100 kHz and 7 bit addressing
// ### configureI2C (I2C_MASTER | CLK_SPEED_100KHZ | ADDRESSING_7BIT);
Serial.begin(115200); // Initialize the serial port.
Wire.begin();
}

void loop(void) { 
// Set the target address of the sensor (0x78 = 120dec)
// ### I2C_set_target(0x78);
// Send start condition for reading from sensor (slave)
// ### I2C_send_start_read();
Wire.requestFrom(0x78, 2); // request two bytes from I2C slave address 0x78
// Read first (MSB) data byte and answer with ACK (continue communication)
// ### I2C_read (&byte_msb, SEND_ACK);
byte_msb = Wire.read();
// Read second (LSB) data byte and answer with NACK (end communication)
// ### I2C_read (&byte_lsb, SEND_NACK);
byte_lsb = Wire.read();
// Send Stop condition
// ### I2C_send_stop();
// Put both values together
pressure = ((int16_t)byte_msb << 8) | byte_lsb;
//Serial.print("P=");
Serial.println (pressure);
delay(50);
}

Pressure.JPG

If you aspire to having "an array" of sensors, then I2C may be an issue, because you can usually only have 2 different addresses.

Hi there,

as I am working with Sensortechnics diff. pressure sensors at the moment I thought I share my code which is working as intended. Please note that the HDI-sensors I used, are capable of delivering a non-calibrated temperature signal. This feature however, has to be enabled when programming the adress into each sensor (using a eval-board from Sensortechnics). The Temperature can then be obtained as 3rd and 4th byte using the exact sheme as used in the following code.

By the way I am using the I2c-library created by Wayne Truchsess (DssCircuits.com is for sale | HugeDomains). However, using the wire library should work just fine.

Using a running average might be a good idea to use on the acquired sensor data. Here is a link to the RunningAverage library from Rob Tillaart:

Best regards,
Jan

/*******************************************************
SUBMODULE

This program will initialize communication with I2c
pressure sensors. Additionally it delivers the calculated 
value from these sensors.

 The circuit:
 * Digital pin 20 (SDA) is used as the communication I2c-bus line
 * Digital pin 21 (SCL) is used as the common clock for the  I2c-bus
 * PullUp Resistors of 1,5 kOhm need to be attached to SDA and SCL Lines
 * As per Datasheet the SDA and SCL lines are equiped with a 220 Ohm Resistor each 
********************************************************/

// Sensor used: HDIM010DUF8P5 [Differential | 10mbar | DIP | 5V]
// Define the I2C-Adresses for the Sensors
#define HDIDiff 0x14      //Adress for the differential pressure sensor | DEC: 20

// Define the calculation parameters
// for each sensor.
// See SensorTechnics-Datasheet: DS_Standard-HDI_E_11650.pdf.
// See SensorTechnics I2C Application Note: AN_I2C-Bus_HMI_HDI_HCLA_HCA_SSI_E_11155.pdf
#define P_min_diff 0       // min. for the differential pressure sensor
#define P_max_diff 10     // max. for the differential pressure sensor
#define T_min -20             // min. temperature for the sensor (same for both sensors)
#define T_max 50              // max. temperature for the sensor (same for both sensors)
/* DIGITAL PERFORMANCE CHARACTERISTIC */
#define Out_min 3277     // min. counts for the sensors (same for both sensors)
#define Out_max 29490 // max. counts for the sensors (same for both sensors)

/* Variables for "getDifferentialPressure" */
  float sensitivity_p_diff = 0; // set the differential pressure sensors sensitivity to zero
  float pressure_diff = 0;      // set the differential pressure to zero
  byte P_counts_diff_msb, P_counts_diff_lsb; // set pressure counts to zero
  int16_t P_counts_diff;        // 16bit value

float getDifferentialPressure()
{
  I2c.read(HDIDiff,2);                // read 2 bytes (we skip temperature [would be byte 3 and 4] for now) from the differential pressure sensor
  P_counts_diff_msb = I2c.receive();  // read the most significant byte from data bytes containing the pressure value
  P_counts_diff_lsb = I2c.receive();  // read the least significant byte from data bytes containing the pressure value
  P_counts_diff = ((int16_t)P_counts_diff_msb << 8) | P_counts_diff_lsb; // combibe msb and lsb
  sensitivity_p_diff = (Out_max - Out_min)/(P_max_diff - P_min_diff); // calculate the differential pressure sensors sensitivity
  pressure_diff = (P_counts_diff - Out_min)/sensitivity_p_diff + P_min_diff;   // calculate the differential pressure in [mbar]
  return pressure_diff;
}

Jan,

Thanks very much for taking the effort to post your code for this. I'd given up using the SSI sensor in I2C mode because of the address issue as I have 3 sensors in my application and I believe (rightly or wrongly?) you can only have a maximum of two and so have been using them in analogue mode.

I've another application where I'll be using only one sensor so I'll try going back to I2C mode on that one and so I'm very grateful for you being kind enough to share your code,

Steve

Hi Steve,

using more than 2 Sensortechnics I2C Sensors is no problem at all. But it is necessary to programm the adresses before you solder your sensors to your board. Sensortechnics has a evaluation board (which i cannot find a datasheet for on their homepage) which can be used to program the adresses into the sensors. Thus, the full range of 127 adresses can be exploited. Consequently, you could use 127 Sensors on one I2C-bus. However, SDA and SCL line capacitance will prevent you from doing so.
Now I suspect you could ask Sensortechnics to program your sensors with different adresses when you order from them directly. I've never done this, though. I allways programmed the adresses myself with an eval-board I have available. They are quite expensive none the less (~400 Euro).

Hope these information will be helpful at some point. Using the I2c interface on these sensors has still some advantages. You get a build in measuring amplifier, filtering capabilities and a somewhat cripled temperature readout. Additionally you do not have to bother about ADC, as that would significantly largen the footprint of your PCB. Presupposed you need more than the 10 bit provided by the arduino.

Best regards,
Jan

I have been using the code by SheepRustler. I found that it sometimes breaks when I run on the arduino uno then open a serial connection to it. My best guess is that the reset routine does not handle pending interrupts properly. I found the simple solution was to use another board (arduino micro) that does not reset when I open a serial connection to it.

My best guess is that the reset routine does not handle pending interrupts properly.

No, opening a serial connection (over USB) to the UNO resets it.

I found the simple solution was to use another board (arduino micro) that does not reset when I open a serial connection to it.

Because the Micro has the USB code internally (ATmega32U4) it is able to distinguish and upload from a standard access to the serial console.

You can have your UNO react the same if you disable the reset by pulling reset constantly high: Arduino Playground - DisablingAutoResetOnSerialConnection. Please note that with the resistor in place your won't be able to upload new sketches the usual way.

I should have written more about the process I followed to get to my conclusion I did.

Initially I noticed that roughly 1/3 of the time the code on my uno stopped working when I connected it over serial. After poking around in the Wire library I realised that although the user facing requestFrom method is a blocking call it is built upon interrupts under the hood. I knew that the uno is designed to reset on the serial connection being opened and I though that I might be able to fix the issue by writing a lower level program that talked directly to the registers. I spent a couple of days reading the manual on the 328 chip so that I could implement a smarter I2C that explicitly used interrupts. Unfortunately a reset interrupt could come at any time in the main loop and writing a smarter I2C library was too much work.

I also noticed that If I put this in the setup routine:

    Serial.begin(115200); // Initialize the serial port
    Serial.println('"abcdefghijklmnopqrstvwxyz");

The message would often not all be printed. This made me suspect that when the arduino runs the reset routine it does not properly write over the registers that control the inturupts/I2C. I considered digging more deeply but I decided that it was much simpler to avoid the restart. I knew about how to modify the uno hardware to avoid the reset but I decided that I would prefer to solve the problem by using the arduino micro. (I liked the smaller board)

Here is my current code, it runs correctly on the micro..

// Code to read a SensorTronics Pressure Sensor
// This has been tested with a pressure sensor owned by C2M Design LTD. Part number: HMIB010UZ7H5 

#include <Wire.h> //For I2C comms

void setup(void) { 
  Serial.begin(115200); // Initialize the serial port.
  Wire.begin();
}

void loop(void) { 
  Wire.requestFrom(0x78, 2); // request two bytes from I2C slave address 0x78
  // I then print the two bytes I got back
  Serial.print((char)Wire.read());
  Serial.print((char)Wire.read());
  // I then print a known byte to allow the my code on the PC to sync up with the stream
  Serial.print('\xFF');
}

I am trying to capture events that last tens of milliseconds so I am deliberately writing very fast, simple code. The computer on the other end of the serial connection need to be smart about syncing up with the streaming data.

I am currently getting 820 pressure readings per second. Does anyone know how to go faster?

I am currently getting 820 pressure readings per second. Does anyone know how to go faster?

Theoretically you can switch to 400kHz for the I2C and increase the serial speed to 500000. But that's outside the specification of the sensor which should have a pause of 500µs between reads.