I2C communication in BeagleBoneBlack

A bit of a background.

I wanted to log temperature and humidity in my room for no reason and I wanted to do it on my BBB. My plan is to post this data to a web application as an experiment, because I have never ever done web apps in my life.

I’m going to use the Si7021 module which I bought from core electronics (here is the link)

Adafruit Si7021 Temperature & Humidity Sensor Breakout Board ADA3251 Adafruit in Australia - Express Delivery Australia Wide (Feature image)
https://core-electronics.com.au/media/catalog/product/cache/1/image/650×650/fe1bcd18654db18f328c2faaaf3c690a/3/2/3251-00.jpg

What are the pins for I2C?

There are so many ways to find out what pins are used for I2C. Simplest is a google search. However, for the sake of completeness I’ll let you know how to find it properly. I’m going to refer to the following documents.

  1. Beaglebone System Reference Manual
  2. AM335x Sitara Processor Datasheet
  3. Beaglebone Schematic

Basically you open the link 1 and figure out which processor the device is using. Then you can open the link 2 which is the processor datasheet and search for I2C. Then you’ll see a table (Device Comparison). There you can see that the processor has 3 I2C modules. Finally, you can refer to link 3 and take a look at how this is wired in hardware.

I2CProcessor PinWhere it is connected?
I2C0Pin C16 and C17HDMI controller – TPS19988
EEPROM – 24LC32A
Power management IC – TPS65217C
I2C1Pin B16 and A16Female header – P9.17, P9.18
I2C2Pin D18 and D17Female header – P9.19, P9.20
I2C modules in BBB

I think it is possible to use either I2C1 or I2C2 for this job. In my case, I picked I2C2 because it is the module used by BBB-Capes. If you want to check which I2C modules are configured, execute the following command.

debian@beaglebone:~$ i2cdetect -l
i2c-1 i2c OMAP I2C adapter I2C adapter
i2c-2 i2c OMAP I2C adapter I2C adapter
i2c-0 i2c OMAP I2C adapter I2C adapter

My device has all these modules enabled and they can be seen in /dev directory.

How to wire the sensor?

Simple,

  1. power off the BBB
  2. Connect SCL to SCL (P9.19)
  3. Connect SDA to SDA (P9.20)
  4. Connect Vin to 3V3
  5. Connect GND to GND.

How do I know that it is working?

This is the beauty of linux, we can use ‘i2cdetect’ to avoid all the anxiety. Execute the following command.

debian@beaglebone:~$ i2cdetect -r 2
undefined

This simply talks with entire I2C2 address range and lists who acknowledges. As you can see 0x40 acknowledged. If you look at Si7021 datasheet you can find out that the address of the chip is 0x40. That means it is properly connected. If this didn’t list your device, there is some issue and you’ll have to check whether it is properly connected and powered.

How to write a code to read I2C registers?

The next step I’m going to do might look dumb, however, I just wanted to do it that way. I’m going to write a C application to read humidity and temperature and log it in a file. This can be done using linux tools like i2cget, i2cset or using other high level programming languages like javascript and python. (javascript is supported by beaglebone black through cloud9 ide)

The latest code for the tool I developed is published here.

I’ll explain the initialization and data transfer blocks here, but I feel that it is self explanatory.

Initialization:

SI7021_RET_VAL si7021_init()
{
        SI7021_RET_VAL ret = SI7021_SUCCESS;
        const char filename[] = "/dev/i2c-2";
        const int addr = 0x40;

        file_handle = open(filename, O_RDWR);

        if (file_handle < 0)
        {
                ret = SI7021_FAIL;
        }
        else
        {
                if (ioctl(file_handle, I2C_SLAVE, addr) < 0)
                {
                        si7021_deinit();
                        ret = SI7021_FAIL;
                }
        }

        // i2c initialization is successful
        if (ret == SI7021_SUCCESS)
        {
                ret = verify_ic();
        }

        return ret;
}
  1. Open /dev/i2c-2 for read and write (Remember I2C2)
  2. Set the I2C_SLAVE address (0x40 in our case)

Data transfer:

This is tied to how Si7021 implements I2C communication. Please refer to the datasheet. The main idea is, if we want to read a register, first write the register address and then read the content. It has two steps, read and write. In this tool I’m going to treate the driver as a SYSFS file.

SI7021_RET_VAL i2c_transfer(const char* cmd, const size_t cmd_length, char* read_buf, const size_t read_length)
{
        size_t transfer_length = write(file_handle, cmd, cmd_length);

        if (transfer_length == cmd_length)
        {
                transfer_length = read(file_handle, read_buf, read_length);

                if (transfer_length == read_length)
                {
                        return SI7021_SUCCESS;
                }
        }

        return SI7021_FAIL;
}
  1. Write the register address first
  2. Read the register content

To understand how I do the temperature conversion and write it to the file, please look at the projec repo.

Ultimately, I have built an executable which reads temperature and humidity from the sensor and writes it to a file.

How to execute the tool periodically?

I used crontab to run the executable every 5 minutes. My executable is saved in /home/debian/bin/log_vitals. You can execute the following command and edit the crontab file.

debian@beaglebone:~$ crontab -e
...
Add the following line 
*/5 * * * * /home/debian/bin/log_vitals

Now the OS will run the tool every 5 minutes. I hope this makes sense!

Thanks!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s