Using wire library to read 16-bit I2C sensor

Hi.

I have a confusion to use the wire library to read data from this sensor

I started by finding the address of the sensor using the example in wire library, the address is 0x13

and i found this example online to read a 16-bit I2C register using Arduino Wire

The confusing part for me is the SLAVE_I2C_ADDRESS used in the example, i don't seem to know how to get that address from the datasheet of the sensor.
Basically, i need to read from the register that has the ambient light value
I just want to read the value of the ambiant light
Can anyone help me in that or give me some guidance on how to get that address ?

Do you mean the slave address 0x13, described in the datasheet?

0x13 is the sensor address that i got when i used this code:

// --------------------------------------
// i2c_scanner
//
// Version 1
//    This program (or code that looks like it)
//    can be found in many places.
//    For example on the Arduino.cc forum.
//    The original author is not known.
// Version 2, Juni 2012, Using Arduino 1.0.1
//     Adapted to be as simple as possible by Arduino.cc user Krodal
// Version 3, Feb 26  2013
//    V3 by louarnold
// Version 4, March 3, 2013, Using Arduino 1.0.3
//    by Arduino.cc user Krodal.
//    Changes by louarnold removed.
//    Scanning addresses changed from 0...127 to 1...119,
//    according to the i2c scanner by Nick Gammon
//    https://www.gammon.com.au/forum/?id=10896
// Version 5, March 28, 2013
//    As version 4, but address scans now to 127.
//    A sensor seems to use address 120.
// Version 6, November 27, 2015.
//    Added waiting for the Leonardo serial communication.
//
//
// This sketch tests the standard 7-bit addresses
// Devices with higher bit address might not be seen properly.
//

#include <Wire.h>

void setup() {
  Wire.begin();

  Serial.begin(9600);
  while (!Serial); // Leonardo: wait for Serial Monitor
  Serial.println("\nI2C Scanner");
}

void loop() {
  int nDevices = 0;

  Serial.println("Scanning...");

  for (byte address = 1; address < 127; ++address) {
    // The i2c_scanner uses the return value of
    // the Wire.endTransmission to see if
    // a device did acknowledge to the address.
    Wire.beginTransmission(address);
    byte 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
}

Apparently, i need to get the register address but the datasheet is so confusing for me and i'm from a different background so i can't understand electronic this much

Second paragraph, page 6.

Do you mean this ?

The predefined 7 bit I2C bus address is set to 0010 011 = 13h. The least
significant bit (LSB) defines read or write mode. Accordingly, the bus address is set to 0010 011x = 26h for write, 27h for read.

I don't get it, do i simply use 0010 011 as register address? what about all these other paragraphs and tables talking about different registers? I just want to read the value of ambiant light and i'm not sure how to do that

As @anon73444976 noted, this is the required protocol:

image

You first Write the desired register address to the device then Read back the data byte from that register. Because ambient light is a 16-bit value, you must do this sequence twice. First for Register #5 then for Register #6.

1 Like

Because register addresses start at 0x80 and because Register #5 (address 0x85) is the high-order byte, I think the code would look something like this:

const byte DeviceAddress = 0x13;

   Wire.beginTransmission(DeviceAddress);
   Wire.write(0x85);  // Register #5 address
   Wire.endTransmission();
   Wire.requestFrom(DeviceAddress, 1);
   uint16_t value = Wire.read();
   value <<= 8;  // Move to upper byte

   Wire.beginTransmission(DeviceAddress);
   Wire.write(0x86);  // Register #6 address
   Wire.endTransmision();
   Wire.requestFrom(DeviceAddress, 1);
   value |= Wire.read();
1 Like

@khamis98 , here's some more important information you can find simply by reading the datasheet:

1 Like

So you can write it this way (compiles, untested):

 #include <Wire.h>
void setup() {
  const uint8_t DeviceAddress = 0x13;

  Wire.beginTransmission(DeviceAddress);
  Wire.write(0x85);  // Register #5 address
  Wire.endTransmission(false);
  Wire.requestFrom(DeviceAddress, static_cast<uint8_t>(2));
  uint16_t value = Wire.read() & 0xFF;
  value <<= 8;  // Move to upper byte
  value |= Wire.read() & 0xFF;
}

void loop() {
}

The cast is needed to suppress compiler warnings (complaints) about using the constant '2' (defaults to int) as argument to function looking for a uint8_t.

1 Like

The R/W bit at the end of the address byte is handled by the Wire library. You just supply the 7-bit address, and beginTransmission() shifts it one bit to the left and adds a 0-bit which means write. Similarly, the requestFrom() does the same, but adds a 1-bit which means read.

Device address is 0x13 regardless of operation!

1 Like

So what I understood so far is the following:
The device address is: 0x13
The value of the ambient light measurement is stored in registers #5 and #6
And to read from a certain register I need to write its address first

So the sequence of the code should be like this:

1- Send a START condition using Wire.beginTransmission to the device address
2- Write the register #5 address
3- Read the data in that register using Wire.read(); after executing the Wire.requestFrom(); to the device address
4- then A repeated START condition as highlighted by @gfvalvo
5- Then write the register #6 address
6- then read from it using Wire.read(); after executing the Wire.requestFrom(); to the device address
7- Then i can send the STOP condition using Wire.end() to the device address

All this should allow me to read the value in the register every time i loop back into it
@johnwasser your code really helped me thank you so much, but i don't understand why did you repeat this twice?

isn't it okay to do two starts without a stop in the middle? Also, this Wire.endTransmission(); is basically just the Wire.end(), corrct? or tha't a different function

What does this mean? aren't we supposed to 0x86 ?

but what about register #0 though, aren't i supposed to write to the device to initiate the measurement?
I understand that other registers are used to change some settings, and i can choose not to change them for now. But the first register though, isn't it supposed to be called and written to at least once?

can I simply print the value of value using Serial.print(value); ? or i need to use another way to display that number?

Yes. You can also do math on the number before you print it if you want to change units.

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.