Project 6.03 Read the I2C Temperature Sensor
Project 6.03 Read the I2C Temperature Sensor In the previous project, we “discovered” the temperature sensor on the I2C bus. In this project, we actually read the temperature from the sensor. There are a lot of I2C devices and they are setup in different ways. As you move into more complex projects, you will need to look at the datasheets for your I2C devices. Datasheets are easy to find on the web. The datasheets will tell you how to setup the device in different ways and how to read and write to the device.
Most I2C devices will have multiple registers. A register is a one or more bytes of memory that can be read, written, or both. On the TCN75A, register 0 holds the temperature reading. This is a read-only register. Each time the device makes a reading, it loads the results to register 0. Register 1 holds configuration information. The device can be setup to encode the temperature with different numbers of bits. In this project, we set register 1 to encode with the maximum of 12 bits. This gives us the maximum available resolution.
It’s fun to look at the datasheets for different devices to learn about all of their functions. The devices are often quite sophisticated. There are two additional register in the TCN75A where you can set “alarm” values. These can be configured to trigger an alarm when the temperature is higher or lower that the values set in the registers. We don’t use the alarm functions with the Arno since we did not connect the IC’s alarm pin to one of the I/O pins on the microcontroller. You might decide to use the alarm when you design your own circuits.
If you haven’t done project 6.01, you should do it now to become familiar with bit shifting. We use bit shifting to read the temperature sensor in this project.
Concepts: I2C communication, Wire library
Circuits:
Most I2C devices will have multiple registers. A register is a one or more bytes of memory that can be read, written, or both. On the TCN75A, register 0 holds the temperature reading. This is a read-only register. Each time the device makes a reading, it loads the results to register 0. Register 1 holds configuration information. The device can be setup to encode the temperature with different numbers of bits. In this project, we set register 1 to encode with the maximum of 12 bits. This gives us the maximum available resolution.
It’s fun to look at the datasheets for different devices to learn about all of their functions. The devices are often quite sophisticated. There are two additional register in the TCN75A where you can set “alarm” values. These can be configured to trigger an alarm when the temperature is higher or lower that the values set in the registers. We don’t use the alarm functions with the Arno since we did not connect the IC’s alarm pin to one of the I/O pins on the microcontroller. You might decide to use the alarm when you design your own circuits.
If you haven’t done project 6.01, you should do it now to become familiar with bit shifting. We use bit shifting to read the temperature sensor in this project.
Concepts: I2C communication, Wire library
Circuits:
Since we want to use I2C communication, we need to include the Wire library in the program:
#include <Wire.h>
The reading from the sensor will initially be stored as int but later converted to a float:
int tempreg = 0;
float temperature = 0;
The IC address needs to be encoded as a single byte:
byte address = 72;
In the loop() block, we create an instance of a Wire object:
Wire.begin();
Now we setup the sensor’s resolution by writing to register 1. We start the communication session with the wire.beginTransmission statement with the IC’s address:
// Set the resolution of the measurement
Wire.beginTransmission(address);
The next communication that the sensor expects is a pointer to a register. We point to the configuration register using the hexadecimal value for the register:
// point to Configuration Register
Wire.write(0x01);
We now write a value to the register. The value represents 8 bits. Each of these bits does something different. Only bits 6 and 5 set the temperature resolution. The other bits set up other parameters. We won’t get into all of the parameters here, but it’s not really complicated. You can look at the datasheet and decide whether each bit of the register should be a 1 or 0 based on the configuration that you want. You can then enter the bits into an online binary-to-hexadecimal converter to find the equivalent hexadecimal value. In our case it’s 0x60.
// set the resolution
Wire.write(0x60);
With the resolution set, we end the transmission:
// ends the command
Wire.endTransmission();
Before the end of the setup() block, we set a pointer to register 0. Register 0 holds the temperature reading. This sets us up to read the temperature register in the loop() block:
// points to the Temperature Register
Wire.beginTransmission(address);
Wire.write(0x00);
Wire.endTransmission();
In the loop() block, we begin to read the temperature sensor. The Wire.requestFrom function has two arguments. The first argument is the device’s address. The second argument if the number of bytes requested. This argument needs to be a byte value, so we cast the number ‘2’ to a byte variable within the function call. The device knows that we want the value of register 1 since we pointed to it in our previous transmission:
// Receives data from the Temperature Register
Wire.requestFrom(address,byte(2));
The two bytes are now stored in a buffer in the microcontroller. The Wire.read() function reads one byte and loads it into the variable tempreg:
tempreg = Wire.read();
Remember tempreg is a 16-bit int variable. The temperature value is encoded as a 12 bit number. The first byte includes the 8 left-most (highest) bits of the temperature value. We need to move these all the way to the left-hand side of the bits that make up tempreg:
tempreg= tempreg << 8;
We use the or operator, |, to load the remaining bits:
tempreg |= Wire.read();
We’ve now loaded 16 bits into tempreg. But remember that the temperature value is only coded in 12 bits. The second byte we read only contained information in the 4 left-most bits. The rest were zeros. We don’t need them, but they will mess up our temperature value since the bits we do need are shifted too far to the left. Now we need to shift all of the bits back right:
tempreg = tempreg >> 4;
The right-shit operator is a little different than the left-shift operator. The left-most bit of an int is the sign bit. It indicates whether the value is positive or negative. The right-shift operator is smart enough to leave that bit in its place and shift the rest.
Now for some math. We have a 12-bit integer value that represents a temperature between -40 and +125 °C. The datasheet provides the conversion equation. The resulting value is cast to a float to retain the decimal part:
temperature =( float ) tempreg / 16;
We’re almost there! Now we just need to output our results in both Fahrenheit and Celsius. The second argument in some of the Serial.print statements below sets the number of decimals to include:
// Display the temperature in the Serial Monitor
Serial.print("Temp F = ");
Serial.print(temperature*9/5 + 32,2);
Serial.print(" ");
Serial.print("Temp C = ");
Serial.println(temperature, 2);
This project is a little more complicated than our others. It takes some work to figure out how to use different I2C devices. A good place to start is by doing an internet search to see if someone has already figured out the device that you want to use. One great thing about being part of the Arduino community is building on the experience of other community members. Just remember to share your code when you figure out something new!
#include <Wire.h>
The reading from the sensor will initially be stored as int but later converted to a float:
int tempreg = 0;
float temperature = 0;
The IC address needs to be encoded as a single byte:
byte address = 72;
In the loop() block, we create an instance of a Wire object:
Wire.begin();
Now we setup the sensor’s resolution by writing to register 1. We start the communication session with the wire.beginTransmission statement with the IC’s address:
// Set the resolution of the measurement
Wire.beginTransmission(address);
The next communication that the sensor expects is a pointer to a register. We point to the configuration register using the hexadecimal value for the register:
// point to Configuration Register
Wire.write(0x01);
We now write a value to the register. The value represents 8 bits. Each of these bits does something different. Only bits 6 and 5 set the temperature resolution. The other bits set up other parameters. We won’t get into all of the parameters here, but it’s not really complicated. You can look at the datasheet and decide whether each bit of the register should be a 1 or 0 based on the configuration that you want. You can then enter the bits into an online binary-to-hexadecimal converter to find the equivalent hexadecimal value. In our case it’s 0x60.
// set the resolution
Wire.write(0x60);
With the resolution set, we end the transmission:
// ends the command
Wire.endTransmission();
Before the end of the setup() block, we set a pointer to register 0. Register 0 holds the temperature reading. This sets us up to read the temperature register in the loop() block:
// points to the Temperature Register
Wire.beginTransmission(address);
Wire.write(0x00);
Wire.endTransmission();
In the loop() block, we begin to read the temperature sensor. The Wire.requestFrom function has two arguments. The first argument is the device’s address. The second argument if the number of bytes requested. This argument needs to be a byte value, so we cast the number ‘2’ to a byte variable within the function call. The device knows that we want the value of register 1 since we pointed to it in our previous transmission:
// Receives data from the Temperature Register
Wire.requestFrom(address,byte(2));
The two bytes are now stored in a buffer in the microcontroller. The Wire.read() function reads one byte and loads it into the variable tempreg:
tempreg = Wire.read();
Remember tempreg is a 16-bit int variable. The temperature value is encoded as a 12 bit number. The first byte includes the 8 left-most (highest) bits of the temperature value. We need to move these all the way to the left-hand side of the bits that make up tempreg:
tempreg= tempreg << 8;
We use the or operator, |, to load the remaining bits:
tempreg |= Wire.read();
We’ve now loaded 16 bits into tempreg. But remember that the temperature value is only coded in 12 bits. The second byte we read only contained information in the 4 left-most bits. The rest were zeros. We don’t need them, but they will mess up our temperature value since the bits we do need are shifted too far to the left. Now we need to shift all of the bits back right:
tempreg = tempreg >> 4;
The right-shit operator is a little different than the left-shift operator. The left-most bit of an int is the sign bit. It indicates whether the value is positive or negative. The right-shift operator is smart enough to leave that bit in its place and shift the rest.
Now for some math. We have a 12-bit integer value that represents a temperature between -40 and +125 °C. The datasheet provides the conversion equation. The resulting value is cast to a float to retain the decimal part:
temperature =( float ) tempreg / 16;
We’re almost there! Now we just need to output our results in both Fahrenheit and Celsius. The second argument in some of the Serial.print statements below sets the number of decimals to include:
// Display the temperature in the Serial Monitor
Serial.print("Temp F = ");
Serial.print(temperature*9/5 + 32,2);
Serial.print(" ");
Serial.print("Temp C = ");
Serial.println(temperature, 2);
This project is a little more complicated than our others. It takes some work to figure out how to use different I2C devices. A good place to start is by doing an internet search to see if someone has already figured out the device that you want to use. One great thing about being part of the Arduino community is building on the experience of other community members. Just remember to share your code when you figure out something new!