MCU learning, Intermediate-Level, Part 1, I2C communication

Andrew C
7 min readNov 30, 2020

Background:

Once you understand most of function in MCU, you will start trying to connect it to other modules for more application. Once you did it, you will start to find that IOs number on MCU is always not enough.

Communication interface such as I2C will help you to increase capability of connecting more modules. In this tutorial, I will show you how to access a combined environment sensor which is I2C communication interface.

Source: https://unsplash.com/photos/gR8QukPoAvQ

Again, let’s talk about who should read “Intermediate-Level” series. First, if you already have basic knowledge of C language and MCU, you should have the ability to complete the exercise. If you are a noob, this series could be too challenging for you. Still welcome follows the instruction to complete the exercise.

What do you need for this exercise?

  1. Particle Photon(LINK)
  2. A Computer/Laptop with a USB port(Windows is ideal)
  3. Sparkfun Si7021(Official link)

Why I select Si7021?

There are thousands of methods to measure temperature and humidity. The easiest way is to measure a Thermistor via ADC function from Photon. But soon, you will find that measuring something is easy, but accurately measuring something is difficult.

I had tried to measure a PT1000 sensor before. It requires an extra amplifier circuit which will increase the cost and complexity. For sure, using MAX31865 you can achieve it as well, but you still need a PT1000/PT100 which could be expansive. I will try to demo it later.

Si7021 not only has a temperature sensor but also has a humidity sensor as well. It will be ideal if you are designing a project which needs both temperature and humidity, such as a weather monitoring station. The measuring range and working temperature of the Si7021 IC are pretty enough for an amateur project as well. So, why not?

Have a look at Si7021 main IC datasheet:

There are several important items that I will check first every time I receive a datasheet.

The first one is the feature and application list. The feature list tells you about the most important function and advantages of this IC. The application list will give you an idea about the field of application.

Some numbers I will always check, the Operating temperature, Storage temperature, operating voltage…etc. Since I am working in the automotive industry, I will also care about automotive qualification, such as AEC-Q, for the company project.

One more thing I would like to mention: NEVER 100% trust the datasheet.

Reference:

Actually, I do not begin this exercise from ground zero since I am not expert in the I2C communication protocol. After searching on the internet, I found a guy who is Stefan Linke already did a great job to provide a fully functional library and document. https://github.com/particleflux/Si7021

I was using this library to access the Si7021 to make sure that it is functional and also learn the idea behind it.

However, it could be a little bit tricky especially if you have NO I2C experience like me. So I decided to break it down and implement a more manageable but less function library. After all, we just need to use I2C to read temperature and humidity.

Let’s coding!

By reading the datasheet of Si7021 and the example code from Stefan Linke, I summarized that to read the temperature and humidity of Si7021 takes 4 steps only.

When Si7021 measures the humidity, it also measures the temperature at the same time. Instead of measuring temperature again, we can let it read out previous temperature result.

In our case, we also looking for using “No Hold Master Mode”. In this mode, the master can do other communication and come back later for the result.

We also assume that you receive a brand-new sensor which setting is not modified.

Step 0: Initialize I2C function in Particle Photon

Add the following code in “setup()”. I2C library in Particle library is also referred to as “Wire”. If there is no input argument, it will set the I2C clock speed into default setting which is 100KHz.

Official reference document: Particle I2C LINK

/* Setup I2C interface*/
Wire.begin();

Step 1: Request Si7021 perform humidity measure

void send_measure_humd_cmd()
{
Wire.beginTransmission(SI7021_DEFAULT_ADDRESS);
Wire.write(SI7021_CMD_MEASRH_NOHOLD);
Wire.endTransmission();

return ;
}

SI7021_DEFAULT_ADDRESS & SI7021_CMD_MEASRH_NOHOL are defined in “si7021.h”. This information is from the datasheet of Si7021.

SI7021_DEFAULT_ADDRESS is the default slave address in our case. Particle Photon acts as Master and Si7021 acts like a slave. Master will send the commands to the slave for a specific task. SI7021_CMD_MEASRH_NOHOL is the command in this case which is defined in the command table as well.

How does Master know who is talking to? Slave address! That is why it should be unique in the same I2C bus. Then, how does the Master tell the Slave what to do? Command code!

Then, remember to close the transmission process and release the I2C bus using “ Wire.endTransmission();”. Or this part of code will hold the I2C bus for a while.

Step 2: Retrieve humidity reading

float send_read_humidity_cmd()
{
float humidity;
uint8_t buffer[2] = {0};
Wire.requestFrom(SI7021_DEFAULT_ADDRESS, 2); for(int i = 0; i < 2; ++i)
{
buffer[i] = Wire.read();
}
humidity = ((float)(((uint16_t) buffer[0] << 8) | buffer[1]) * 125) / 65536 - 6;// clamp values to 0-100% range
// it is in some cases possible that the values are outside of this range, according to spec
if (humidity < 0)
{
humidity = 0.0;
}
else if (humidity > 100.0)
{
humidity = 100.0;
}
else
{
/* Do nothing here. */
}
return humidity;
}

In retrieve reading process, we have to tell the Slave module how many bytes we need via “Wire.requestFrom();” Then, we use “Wire.read()” to read each byte until the end.

The relation between the conversion of bytes and actual humidity is also mentioned in the datasheet:

It mentioned that the measured value due to error and mathematical calculation, it could be out of 0 and 100 which is impossible. So make sure we clamp the value between 0 and 100.

Step 3: Request Si7021 read out previous temperature reading.

void send_measure_humd_cmd()  
{
Wire.beginTransmission(SI7021_DEFAULT_ADDRESS);
Wire.write(SI7021_CMD_READPREVTEMP);
Wire.endTransmission();
return ;
}

Basically, it is almost the same as humidity measure command. And using “SI7021_CMD_READPREVTEMP” will no require sensor perform temperature measurement again.

Step 4: Retrieve temperature reading

float send_read_tmp_cmd()
{
float TemperatureLocal;
uint8_t buffer[2] = {0};

Wire.requestFrom(SI7021_DEFAULT_ADDRESS, 2);
for(int i = 0; i < 2; ++i)
{
buffer[i] = Wire.read();
}
TemperatureLocal = ((float) (((uint16_t) buffer[0] << 8) | buffer[1]) * 175.72) / 65536 - 46.85; return TemperatureLocal;
}

Temperature reading retrieve process is the same as humidity, but we do not need to claim the value.

Pull all together:

Since we need step1 to step 4 executing in sequence, we use a “switch” statement. And each time “void sensor_cmd_cycle()” is called, it will execute one step ONLY.

In the original library, the command is predefined. We also add 2 functions for other software to get the temperature and humidity value.

After all…USING GLOBAL VARIABLE IS DANGEROUS!!

If you take a look at datasheet carefully, you will also find that each measurement takes several milliseconds. So make sure you wait long enough before you request Si7021 return the measured values.

In our case, we will place “sensor_cmd_cycle()” in a 100 ms time loop, so each command will have at least 100-millisecond gap which is much longer than measurement time.

And we also place the serial print function in a 1-second loop so we can read it easily.

Following code is how we are using it:

Results:

Compile -> Flash -> Serial monitor:

You should see the serial monitor print temperature and humidity each second.

Conclusion:

I2C is a very common communication interface. However, each OEM may have a different way to implement it. Furthermore, there are some features we missed, such as heating function and CRC.

--

--

Andrew C

Software engineer, Movie lover, Karate beginner.