Trouble using HSC pressure transducer

I'm working on a project using a honeywell HSC pressure transducer hooked up on the i2c bus. This project also has a DS1307 RTC, a BMP085, and an 8-ch 12-bit i2C ADC. The hex address of the HSC is 56, and I don't think there's any overlap there.

I'm writing everything to an SD card, and my numbers seem to be correct, except for the HSC.

I get a repeating value of 100010110111, which according to their datasheet (http://sensing.honeywell.com/i2c%20comms%20tech%20note_008201-1-en_final_28jan10.pdf) means that; 1. The data is 'Stale Data," which they say means data that has not been updated since the last time I got it (the first two bits are the status bit which are 10 in this case,) and, 2. That it's currently got -5.55047 psig applied to it (The transfer function says p = (counts-countsmin)*(pmax-pmin)/(countsmax-countsmin), so counts = 183, and it's a 0-50psig with a 10-90% calibration.)

Unless I'm reading the datasheet completely wrong that is. It's currently open to atmosphere so I find it hard to believe it's got 5 pounds of negative pressure on it, but that's just me.

Here's the function where I get the values,

String getLinePressure() 
{
  String dataString = "";
  byte pvalHigh, pvalLow;
  
  Wire.requestFrom(PRESSURE_ADDRESS, 2);
  while (Wire.available()) {
    pvalHigh = Wire.receive();
    pvalLow = Wire.receive();
  }
  dataString += ",";
  dataString += String(pvalHigh, BIN);
  dataString += String(pvalLow, BIN);
  return dataString;
}

And I call it once per second in my main(), which tells me I've really got something screwed up because once a second should be more than okay for the pressure transfer, or do I need a wait in the actual function?

Also, I wrote a bit of code to just grab the bits and if you examine them independently the high value is 1000 and the low value is 10110111, so apparently I'm losing four bits from the high value too.

Any help you guys could give me would be appreciated, thanks.

-Ian

I'm working on a project using a honeywell HSC pressure transducer hooked up on the i2c bus. This project also has a DS1307 RTC, a BMP085, and an 8-ch 12-bit i2C ADC. The hex address of the HSC is 56, and I don't think there's any overlap there.

Without all that other stuff connected or coded, do you have the same problem?

And I call it once per second in my main()

You need to post ALL of your code, then. Your sketch should NOT have a main() function.

PaulS:
Without all that other stuff connected or coded, do you have the same problem?

Yes, the problem does not go away if the pressure transducer is by itself.

#include <Wire.h>
#include <SD.h>
#include <RTClib.h>
#include <BMP085.h>

#define CHIP_SELECT          4    // SD chip select pin
#define LOG_INTERVAL      1000    // logging interval
#define CHANNELS             8    // define number of channels to log
#define ON_BOARD_ANALOGS     0    // set to 1 if using onboard digital to analog pins
#define PRESSURE_ADDRESS    40    // address of Honeywell pressure sensor

RTC_DS1307 RTC;    // define real-time clock instance
BMP085 BMP;        // define barometric pressure/temperature sensor instance

unsigned int logDelay = 0;

void setup()
{
  Serial.begin(9600);
  Wire.begin();
  BMP.begin();
  RTC.begin();
  pinMode(10, OUTPUT);
  if (!SD.begin(CHIP_SELECT)) {         // see if card is present and can be initialized
    Serial.println("Card failed, or not present");
    return;
  }
  if (! RTC.isrunning()) {                      // if the RTC isn't set,
    RTC.adjust(DateTime(__DATE__, __TIME__));   // set it now.
  }
  delay(1000);
  unsigned int logDelay = millis();
}

String getAnalogValues (int ADC_address)
{
  String dataString = "";
  const byte DAT[8] = {0x84,0xC4,0x94,0xD4,0xA4,0xE4,0xB4,0xF4};
                                 // Constant configuration data
  byte advalHigh, advalLow;      // Store A/D (high byte, low byte)
  byte i;
  for (i=0; i<=CHANNELS-1; i++) {
    Wire.beginTransmission(ADC_address);
    Wire.send(DAT[i]);
    Wire.endTransmission();
    delay(1);
      
    Wire.requestFrom(ADC_address, 2);
    while(Wire.available()) {
      advalHigh = Wire.receive();
      advalLow = Wire.receive();
    }
    dataString += String(advalHigh, DEC);
    if (advalLow <= 0X0f) {
      dataString += String(0);
    }
    dataString += String(advalLow, DEC);
    if (i < (CHANNELS - 1)) {
      dataString += ",";
    }
  }
  return dataString;
}

String getTempAndPressure() 
{
  String dataString = "";
  
  int temp = ((BMP.readTemperature() * 1.8) + 32) * 100;
  int baro = (BMP.readPressure() * .000296) * 100;
  dataString += ",";
  dataString += String(temp);
  dataString += ",";
  dataString += String(baro);
  dataString += ",";
  return dataString;
}

String getLinePressure() 
{
  String dataString = "";
  byte pvalHigh, pvalLow;
  
  Wire.requestFrom(PRESSURE_ADDRESS, 2);
  while (Wire.available()) {
    pvalHigh = Wire.receive();
    pvalLow = Wire.receive();
  }
  dataString += ",";
  dataString += String(pvalHigh, BIN);
  dataString += String(pvalLow, BIN);
  return dataString;
}
  

void loop()
{
  String ADCString = "";
  String tempString = "";
  String pressString = "";
  const int ADC_address = 0x48;    // I2C write address for 12-bit ADC  
  DateTime now = RTC.now();
  
  if (millis() - logDelay > LOG_INTERVAL) {     // create data string and write to a file
    ADCString = getAnalogValues(ADC_address);   // if LOG_INTERVAL has passed, get analog 
    tempString = getTempAndPressure();          // values, ambient temperature and baro 
    pressString = getLinePressure();            // pressure values, and line pressure
    logDelay = millis();                        // values, and reset the log timer
    File dataFile = SD.open("datalog.txt", FILE_WRITE);  // write the data string to a file
    if (dataFile) {
      dataFile.print(now.unixtime());                    // beginning with a timestamp
      dataFile.print(pressString);
      dataFile.print(tempString);
      dataFile.println(ADCString);
      dataFile.close();
    }
    else {
      Serial.println("error opening datalog.txt");
    }
  }
}

Sorry, loop() not main(). Doing this, learning Python, and hacking my way on a C project. Sometimes they all run together. My apologies.

-Ian

Yes, the problem does not go away if the pressure transducer is by itself.

The idea was to write a short sketch that JUST deals with the pressure transducer.

  unsigned int logDelay = millis();

A local variable that immediately goes out of scope. How useful is that?

  for (i=0; i<=CHANNELS-1; i++) {
    Wire.beginTransmission(ADC_address);
    Wire.send(DAT[i]);
    Wire.endTransmission();
    delay(1);

This is not the way to send multiple bytes. The Wire.send() method with two arguments should be used.

String getAnalogValues (int ADC_address)
{
  String dataString = "";
  const byte DAT[8] = {0x84,0xC4,0x94,0xD4,0xA4,0xE4,0xB4,0xF4};
                                 // Constant configuration data
  byte advalHigh, advalLow;      // Store A/D (high byte, low byte)
  byte i;
  for (i=0; i<=CHANNELS-1; i++) {
    Wire.beginTransmission(ADC_address);
    Wire.send(DAT[i]);
    Wire.endTransmission();
    delay(1);
      
    Wire.requestFrom(ADC_address, 2);
    while(Wire.available()) {
      advalHigh = Wire.receive();
      advalLow = Wire.receive();
    }
    dataString += String(advalHigh, DEC);
    if (advalLow <= 0X0f) {
      dataString += String(0);
    }
    dataString += String(advalLow, DEC);
    if (i < (CHANNELS - 1)) {
      dataString += ",";
    }
  }
  return dataString;
}

Why are you writing the low order and high order bytes to the text file separately? Seems to me that you would want to recombine the high order and low order bytes back into an int, and then write the int as a string.

PaulS:
The idea was to write a short sketch that JUST deals with the pressure transducer.

Here’s that sketch, and it gives me the exact same response.

#include <Wire.h>

#define PRESSURE_ADDRESS 40

unsigned int logDelay = 0;

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

String getLinePressure() 
{
  String dataString = "";
  byte pvalHigh, pvalLow;
  
  Wire.requestFrom(PRESSURE_ADDRESS, 2);
  while (Wire.available()) {
    pvalHigh = Wire.receive();
    pvalLow = Wire.receive();
  }
  Serial.println(pvalHigh, BIN);
  Serial.println(pvalLow, BIN);
  dataString += ",";
  dataString += String(pvalHigh, BIN);
  dataString += String(pvalHigh, BIN);
  return dataString;
}

void loop()
{
  String pressString = "";
  
  if (millis() - logDelay > 1000){
    pressString = getLinePressure();
    Serial.println(pressString);
    logDelay = millis();
  }
}

PaulS:
A local variable that immediately goes out of scope. How useful is that?

Fixed. When moving things around trying to figure out the issue it got pasted into the wrong spot. You can see above it where it gets initialized as a global.

PaulS:
This is not the way to send multiple bytes. The Wire.send() method with two arguments should be used.

I’m not sending multiple bytes at once. It sends the channel info and command for that channel to begin an acquisition, than waits and gets the available info from that channel. Then it iterates to the next channel. Most of the ADC code came from http://www.gravitech.us/i2c128anco.html and this http://www.ti.com/product/ads7828#technicaldocuments.

PaulS:
Why are you writing the low order and high order bytes to the text file separately? Seems to me that you would want to recombine the high order and low order bytes back into an int, and then write the int as a string.

The example code was written that way and it works, so I didn’t change it. It’s the reason I write the byte values the way I do for this piece of code here for the pressure transducer,

String getLinePressure() 
{
  String dataString = "";
  byte pvalHigh, pvalLow;
  
  Wire.requestFrom(PRESSURE_ADDRESS, 2);
  while (Wire.available()) {
    pvalHigh = Wire.receive();
    pvalLow = Wire.receive();
  }
  Serial.println(pvalHigh, BIN);
  Serial.println(pvalLow, BIN);
  dataString += ",";
  dataString += String(pvalHigh, BIN);
  dataString += String("_");
  dataString += String(pvalHigh, BIN);
  return dataString;
}

So I can get a real view of what values I’m getting. Plus, I can handle the decoding when I look at the data later, why complicate the code doing the parsing on the Arduino if I don’t have to?

-Ian

  byte pvalHigh, pvalLow;

Local variables are not initialized. Since these variables may, or may not be assigned a value - depending on whether the device responds in a timely fashion - you MUST assign them default values.

  Wire.requestFrom(PRESSURE_ADDRESS, 2);
  while (Wire.available()) {
    pvalHigh = Wire.receive();
    pvalLow = Wire.receive();
  }

You are expecting that the device has generated a response immediately. I'm not sure that that is a valid assumption. You are also testing that Wire.available() returns at least one, and then reading two bytes. Again, not the correct way to do this.

I would execute a while loop, waiting for two bytes to be available OR for some time to have elapsed. Only when there are two or more bytes would I try to read two bytes. If two bytes do not become available within a reasonable time, then bail out WITHOUT adding to the data string.

Well, it changed. Here’s the updated code. I wrote a while loop that should loop for 1000 mills unless it sees two bytes on Wire.receive.

#include <Wire.h>

#define PRESSURE_ADDRESS 40

unsigned int logDelay = 0;

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

String getLinePressure() 
{
  unsigned int delayTime = 0;
  String dataString = "";
  byte pvalHigh = 0;
  byte pvalLow = 0;
  
  Wire.requestFrom(PRESSURE_ADDRESS, 2);
  unsigned int timerHold = millis();
  while (delayTime < 1000 ) {
    if (Wire.available() == 2) {
      pvalHigh = Wire.receive();
      pvalLow = Wire.receive();
      break;
    }
    delayTime += millis() - timerHold;
  }
  Serial.println(pvalHigh, BIN);
  Serial.println(pvalLow, BIN);
  dataString += ",";
  dataString += String(pvalHigh, BIN);
  dataString += String("_");
  dataString += String(pvalLow, BIN);
  return dataString;
}

void loop()
{
  String pressString = "";
  
  if (millis() - logDelay > 1000){
    pressString = getLinePressure();
    Serial.println(pressString);
    logDelay = millis();
  }
}

I also picked up the Saleae Logic Analyzer, and here’s what I’m seeing when the sketch runs.

I’m not 100 percent sure of what to make of it, so any help there would be appreciated. I can obviously see that there’s some communication so my wires are correct and the address is correct and the pull-ups are fine. But I’m not sure how to figure the rest of it out.

-Ian

Ooops, and the output. High byte is 110, Low byte is 1101100. Still missing a few bits there.

  Wire.requestFrom(PRESSURE_ADDRESS, 2);

This function returns a value. What is it?

  Serial.println(pvalHigh, BIN);
  Serial.println(pvalLow, BIN);
  dataString += ",";
  dataString += String(pvalHigh, BIN);
  dataString += String("_");
  dataString += String(pvalLow, BIN);

Move these inside the block where the data is read. It makes no sense to print (or do) anything if nothing was read.

Ooops, and the output. High byte is 110, Low byte is 1101100. Still missing a few bits there.

What do you mean that there are bits missing? Leading 0s are not printed.

PaulS:
This function returns a value. What is it?

Well, according to this;

http://www.arduino.cc/en/Reference/WireRequestFrom

it returns none. But if you dig down into the class…

/* 
 * Function twi_readFrom
 * Desc     attempts to become twi bus master and read a
 *          series of bytes from a device on the bus
 * Input    address: 7bit i2c device address
 *          data: pointer to byte array
 *          length: number of bytes to read into array
 * Output   number of bytes read
 */
uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length)
{
  uint8_t i;

  // ensure data will fit into buffer
  if(TWI_BUFFER_LENGTH < length){
    return 0;
  }

  // wait until twi is ready, become master receiver
  while(TWI_READY != twi_state){
    continue;
  }
  twi_state = TWI_MRX;
  // reset error state (0xFF.. no error occured)
  twi_error = 0xFF;

  // initialize buffer iteration vars
  twi_masterBufferIndex = 0;
  twi_masterBufferLength = length-1;  // This is not intuitive, read on...
  // On receive, the previously configured ACK/NACK setting is transmitted in
  // response to the received byte before the interrupt is signalled. 
  // Therefor we must actually set NACK when the _next_ to last byte is
  // received, causing that NACK to be sent in response to receiving the last
  // expected byte of data.

  // build sla+w, slave device address + w bit
  twi_slarw = TW_READ;
  twi_slarw |= address << 1;

  // send start condition
  TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA);

  // wait for read operation to complete
  while(TWI_MRX == twi_state){
    continue;
  }

  if (twi_masterBufferIndex < length)
    length = twi_masterBufferIndex;

  // copy twi buffer to data
  for(i = 0; i < length; ++i){
    data[i] = twi_masterBuffer[i];
  }
	
  return length;
}

…it returns the length of the previously set buffer, which should be the number of bytes it received.

So, if I’m reading this right Wire.requestFrom returns the number of bytes actually received and Wire.available only returns the number of bytes left in the buffer, right?

Are you suggesting I use the return from requestFrom as my test? Does it matter?

-Ian

PaulS: What do you mean that there are bits missing? Leading 0s are not printed.

So, if I replace all my assumptions with knowns, i.e. don't assume there's two bytes there, just don't do anything unless I know there's two bytes there, and the high byte returns 110 and the low byte returns 1101100, then knowing that's it's dropped the leading zeros I can interpret that 16-bit number as 0000011001101100. Correct?

So the first two bits are my status bits, 00, which means everything's good, and the last 14 are my counts which comes to 1644 and if I pop that in the equation I get 0.01373312 psig, a believable enough reading all things considered, and definitely accurate enough for what I'm doing. Not to mention well within the accuracy claimed on the data sheet which is .25% of full-scale, or about .075 psig.

-Ian

-Ian

Are you suggesting I use the return from requestFrom as my test? Does it matter?

Not specifically. I was interested in knowing that the function was doing what it was supposed to. That is verified by capturing/printing the return value. If it is what is expected, then it can be assumed that the function worked.

So the first two bits are my status bits, 00, which means everything's good, and the last 14 are my counts which comes to 1644 and if I pop that in the equation I get 0.01373312 psig, a believable enough reading all things considered, and definitely accurate enough for what I'm doing. Not to mention well within the accuracy claimed on the data sheet which is .25% of full-scale, or about .075 psig.

Excellent!

The next thing is to change the actual pressure that it is reading, and verify that you get a different/good reading.

Excellent!

The next thing is to change the actual pressure that it is reading, and verify that you get a different/good reading.

Yep, adding this bit of code back into the main code and then loading it up and seeing how it does.

I'll post results tonight or tomorrow maybe.

-Ian

PaulS: The next thing is to change the actual pressure that it is reading, and verify that you get a different/good reading.

Success...

Got the data writing to a log and changing and looking very close to what the gauge on my little pressure pump shows.

One weird thing though, when it writes to the log file, every once in a while I get a garbled line. For example;

1325881096,00000110,01101000,7178,2896,15255,15255,15255,15255,15255,15255,15255,15255
1325881097,00000110,01101010,7178,2896,15255,15255,15255,15255,15255,15255,15255,15255
1325881098,00000110,01101010,7178,2896,15255,15255,15255,15255,15255,15255,15255,15255
1325881099,a01,ÛÛ«,7178,2896,15255,15255,15255,15255,15255,8015,15255,15255
1325881101,00000111,11110100,7214,2896,15255,15255,15255,15255,15255,15255,15255,15255
1325881102,00000111,10111111,7232,2896,15255,15255,15255,15255,15255,15255,15255,15255

You can see in the line in the middle where the reading from the pressure sensor is weird. I guess if I did the binary conversion and scaling on the processor, that may help, than I could eliminate weird character readings.

-Ian

Could you post the final code please? I obtain only zeros.

cheers.