1

I'm trying to get data from a sensor, but don't see how I can get data back. I'm looking at the data on the I2C bus with a logic analyzer, and I'm sending what I expect, but I seem to cut off the slave before he can respond. My understanding from the datasheet is that I need to send this command to wake it up and request a measurement:

0xD8 0x30 0x0A

and then I need to send this command to get the data:

0xD8 0x06 0xD9

#define HEARTBEAT_PIN            (13)

#define 7BIT_ADDR                (0x6C)
#define ADDR_WRITE               (0xD8)
#define ADDR_READ                (0xD9)

#define MAX_NUM_RX_BYTES         (8)
#define MAX_NUM_TX_BYTES         (8)


static uint8_t tx_buffer[MAX_NUM_TX_BYTES];
static uint8_t rx_buffer[MAX_NUM_RX_BYTES];
static uint8_t new_rx_data;

void setup(void) {
  // Clear any module memory
  memset(tx_buffer, 0, MAX_NUM_TX_BYTES);
  memset(rx_buffer, 0, MAX_NUM_RX_BYTES);
  new_rx_data = false;

  Wire.begin();  
}

void loop(void) {
  RequestMeasurement();
  delay(10);
  GetData();
  delay(500);
}

void RequestMeasurement(void) {
  // MR: 0xD8 0x30 0x0A
  // Internal (to IC) register address?
  tx_buffer[0] = 0x30;
  // Request-measurement command
  tx_buffer[1] = 0x0A;
  Wire.beginTransmission(7BIT_ADDR);
  Wire.write(tx_buffer, 2);
  Wire.endTransmission(true);
}

void GetData(void) {
  // GD: 0xD8 0x06 0xD9 0x?? 0x?? 0x?? 0x?? 0x?? (0x?? => slave data)
  // Slave address + 'write' indication bit
  // Internal (to IC) register address?
  tx_buffer[0] = 0x06;
  // Slave address + 'read' indication bit
  tx_buffer[1] = ADDR_READ;

  Wire.beginTransmission(7BIT_ADDR);
  Wire.write(tx_buffer, 2);
  Wire.endTransmission(true);

  // HOW DO I LEAVE THE BUS OPEN LONG ENOUGH
  // FOR THE SLAVE DEVICE TO RESPOND WITH THE DATA?
  //Wire.requestFrom(7BIT_ADDR, 3, true); 
}

3 Answers 3

3

First off; when talking about I2C bus, an important point to clarify: did you connect pullup resistors to both SDA and SCL lines (A4 and A5 respectively)? Without those, the I2C bus will not work properly.

Next, I2C protocol is quite low-level and a bit complex; but fortunately, the Wire library that comes with Arduino helps a lot and hides some of this complexity.

When using Wire library, there is a first point to keep in mind:

The device address is 7 bits only, the 8th bit is used to define the direction of data (read or write). You don't need to care about this direction bit as Wire deals with it for you.

In your situation, the device address is 0x6C, ie 1101100 in binary.

The commands you mentioned that you want to send to the device, 0xD8 0x30 0x0A and 0xD8 0x06 0xD9 already include the device address and the write bit (0):

0xD8 is 1101 1000 ie 1101100 plus the 0 direction bit at the end.

Please note that 0xD8 0x06 0xD9 is made of 2 commands actually:

  • 0xD8 0x06 is a write command to tell the device hich register we want to read
  • 0xD9 is just the device address followed by the read bit (1): this is the way the I2C master tells a slave that the former will now wait for data sent from the latter.

It is important to split this command in 2 because each will use a specific API from Wire library.

Writing to your device with the Wire library will be done this way:

// First command: start measurement
Wire.beginTransmission(0x6C);
Wire.write(0x30);
Wire.write(0x0A);
Wire.endTransmission();

// Second command: tell the device what register we want to read
Wire.beginTransmission(0x6C);
Wire.write(0x0A);
Wire.endTransmission();

Then you can start reading from your device with a different API:

Wire.requestFrom(0x6C, 3);
byte b1 = Wire.read();
byte b2 = Wire.read();
byte b3 = Wire.read();
// Now rebuild value from the 3 bytes just read...

Note that Wire.requestFrom(0x6C, 3); will send the 0xD9 that is calculated from the device address 0x6C and the read bit. SO you don't have (and you should not) send 0xD9 yourself, Wire doesit for you.

Please note that I use Wire.endTransmission() without the stop argument. Not sure if this is mandatory, but it worked fine on my I2C experiments.

2

In the diagram there is a S after sending 0x60. You first write to the device, letting it you want to read, and then you start reading.

I think you need to do something like

void GetData(void) {
  Wire.beginTransmission(7BIT_ADDR);
  Wire.write(0x06);
  Wire.endTransmission(false);//don't terminate connection

  Wire.requestFrom(7BIT_ADDR, 3);//this will send the 0xD9
  pressure = ( (uint32_t) i2cread() << 16) | ( i2cread() << 8 ) | i2cread();//or something 

}

But I've never used I2C.

0

Ensure pullups (see datasheet for min acceptable resistance) are on I2C lines.

What I didn't understand at first was where the Wire library would bit shift and add read/write indication bits. And those start/stop booleans. After some testing and a closer read of the docs, I got this to work:

#include "Wire.h"

#define BAR_7BIT_ADDR         (0x6C)
#define MAX_NUM_RX_BYTES      (5)
#define MAX_NUM_TX_BYTES      (3)
#define FIRST_TEMP_BYTE_INDEX (3)

uint8_t new_rx_data;
uint8_t tx_buffer[MAX_NUM_TX_BYTES];
uint8_t rx_buffer[MAX_NUM_RX_BYTES];

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

void loop(void) {
  // If new data is available
  if (new_rx_data) {
    new_rx_data = false;

    // Process the data in the background
    PrintRxData();
  }

  BAR_RequestMeasurement();
  BAR_GetData();

  // This is a bit of a hack to get this function to execute.
  // After attempting to register this function as a callback with
  // the Wire.onReceive() method the function wouldn't execute.
  BAR_OnDataReceived(0);

  // Wait a tit, then do it again
  delay(500);
}

void RequestMeasurement(void) {
  // MR: 0xD8 0x30 0x0A
  // Internal (to IC) register address
  tx_buffer[0] = 0x30;
  // Request-measurement command
  tx_buffer[1] = 0x0A;
  Wire.beginTransmission(BAR_7BIT_ADDR);
  Wire.write(tx_buffer, 2);
  Wire.endTransmission(true);
}

void GetData(void) {
  // GD: 0xD8 0x06 0xD9 response from slave
  // Internal (to IC) register address
  tx_buffer[0] = 0x06;
  Wire.beginTransmission(BAR_7BIT_ADDR);
  Wire.write(tx_buffer, 1);
  Wire.endTransmission(false);
  Wire.requestFrom(BAR_7BIT_ADDR, 5, true);
}

void OnDataReceived(int num_bytes) {  
  uint8_t i = 0;
  while (Wire.available() && i < MAX_NUM_RX_BYTES) {
    rx_buffer[i++] = Wire.read();
  }

  // Indicate to the background that there is new rx data
  new_rx_data = true;
}

void PrintRxData(void) {
  Serial.print("Pressure [kPa]: ");
  Serial.print(ToKpa(rx_buffer));
  Serial.print("\r\n");

  Serial.print("Temperature [C]: ");
  Serial.print(ToCelsius(&rx_buffer[FIRST_TEMP_BYTE_INDEX]));
  Serial.print("\r\n");
}

// Returns the pressure in kPa given raw binary data
float ToKpa(uint8_t* raw_data) {
  // example: raw_data[3] = {0x63, 0x6C, 0xBB}; => 101.81kPa
  // return ((float)((((uint32_t)raw_data[0]) * 65536) |
  //                  (((uint32_t)raw_data[1]) * 256)  |
  //                  (((uint32_t)raw_data[2]) * 1))) / 64.0 / 1000.0;
  return ((float)((
                  (((uint32_t)raw_data[0]) << 16) |
                  (((uint32_t)raw_data[1]) << 8)  |
                  (((uint32_t)raw_data[2]) << 0)
                  ) >> 6)) / 1000.0;
}

// Returns the temperature in Celsius given raw binary data
float ToCelsius(uint8_t* raw_data) {
  // example: raw_data[2] = {0x18, 0xE6}; => 24.90C
  // return ((float)(((uint32_t)raw_data[0]) * 256) + (((uint32_t)raw_data[1]) * 1)) / 256.0;
  return ((float)(
                 (((uint32_t)raw_data[0]) << 8) |
                 (((uint32_t)raw_data[1]) << 0)
                 )) / 256.0;
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.