Arduino and an I2C temperature sensor
by Naneau
Introduction
(skip this if you just want to know about TC74’s and Arduinos)
The first post of the season will be about my new love. I’ve been hacking software for a looong time now, but the last time I did any serious hardware stuff was back in high school.
And I’m glad to say that I forgot almost everything about it. I seem to remember some guy named Ohm being important, but that’s about it. But, lack of knowledge aside, writing code that, at best, changes a few pixels on a screen is a somewhat limited domain. I wanted to make something cool, something that moves, something interesting.
Arduino to the rescue! are awesome little things that let you play with electronics in a fun and easy way. At it’s heart lies a programmable chip (an ATmega168 to be precise), which you can attach stuff to. All kinds of stuff! There are dozens of examples of stuff being attached to arduinos in order to make them flash, vibrate, move, dance and sing to be found on the web.
And I’m not going to write about flashing leds or controlling servos. I’m going to do something a little bit more complex. I’ve been playing with my arduino for a couple of weeks now (mostly during weekends) and I’ve built a little robot around it (which I’m sure I’ll write about another time). One of the problems you start facing when building large, complicated things, is that you are going to run out of ports to connect stuff to. The currently “standard” Arduino Duemilanove has 13 digital I/O ports, and 5 Analog IN ports. This is plenty for basic sensor input, driving a dc motor through a controller and a few servos on the side (in short, plenty for a robot). But if you want to control a few dozen leds independently you’ll be out of luck.
That’s where buses come in. They allow communication in a standardized form over a smaller number of wires than you would need to attach the components on the bus to your controller separately. I had of course read about them, in some computer science class a couple of years ago, but never used a bus directly. Abstraction layers make your life easier, and I am lazy. I work at least levels above bus level usually.
I2C
The TC74 temperature sensor I hooked up today uses an I2C bus. This bus has been designed here, in the Netherlands, by Philips. It requires only two wires, and has a 7 bit addressing space (if that doesn’t mean much to you, think: “you can hook up a lot of things to just 2 ports on your Arduino”).
Starting out with low-level bus communication for a person like me is… Difficult at best. I spent 2 hours just hooking up a single digital sensor, which would have taken me 5 minutes were it a “normal” analog sensor. You’ll need skills like Binary-To-Hexadecimal conversion and a hacker attitude. You will have to pay closer attention to datasheets than usual, because it’s harder to troubleshoot things when they go wrong. But don’t let that scare you away, there are a lot of benefits to using serial communication with your components, most of all that you need two wires to hook up a whole bunch of stuff.
Arduino has a library for I2C communication, which you can include in your sketch like:
1 2 | #include "Wire.h" //wire library |
There is a reference page for the Wire library, but don’t expect too much from it. Wire (why didn’t they just call it I2C?) takes care of some of the communication details, but you will still need to know intimate details of the communication protocol, as it differs from part to part.

Connecting the sensor to the relevant ports is easy enough. There are only 4 wires that matter, there is the standard +5V and GND connectors, which go to the +5V and GND connectors on your arduino (durrr), and then there’s two wires for I2C. One is a clock line (SCL, sometimes written as SCLK), which is controlled by the master (your Arduino), and can be thought of as the rhythm section of an orchestra. Communication is a series (hence “serial communication”) of 1s and 0s, and they are “measured” on each “beat” of the clock line. When using the Wire library, your Analog Input 5 is automagically set up as the SCL line. On a TC74, the second wire from the left is SCL. The other wire in the I2C setup is the data wire (SDA), which is the wire that is either high for 1, or low for 0, synced with the clock.
The difficult part about bus communication is that you need to specify specific addresses for each component added to the bus. These addresses can sometimes be set with control pins, but for the TC74 they are set in the factory. An address is 7 bits long, and for the TC74 the first 4 bits are always 1001. The most common TC74 sensor, the TC74A5 has the address of 1001101 (101 is binary for 5). There’s a whole series of TC74’s, from TC74A0 (address 1001000) to tc74A7 (1001111). Do you see the connection? Well I didn’t, at first. I didn’t realize that my sensor was an A7 model, which led to extreme frustration as I tried to communicate with a non-existent component on the bus.
So how does this communication actually work? Well to start you’ll have to tell the Wire library to set your Arduino up as a bus master:
1 2 3 4 5 6 | void setup() { Wire.begin(); //the begin() method accepts an address parameter //but that would make your arduino into a slave } |
Communication is set up into blocks. A master initializes it by writing the address (that’s 7 bits, remember?) of the component it wants to talk to on the SDA line, with the last bit being either 0 for read, or 1 for write. Making it into a block of one byte long. Don’t worry about the read/write stuff, we’ll get to that. In Wire code, beginning a conversation with your TC74 sensor would look like:
1 2 3 4 5 6 7 8 9 10 11 | #define address 0x4F //the address of the sensor, in hex, it's the equivalent of 1001111 //which is the address found in the datasheet of my sensor //this may seem complicated if you've never seen hex before //for a TC74A5 it would be 0x4D void setup() { Wire.beginTransmission(address); //start the transmission } |
Now, in the datasheet for your component you’ll find a timing diagram. It specifies what the component expects at certain points during communication. There will be references to “registers” and “commands”. Usually you’ll issue a command to either read or write something from a register. What the component does with the value of a register is of course dependent on what kind of component it is. For our TC74 temperature sensor, you can read a register that contains… The current temperature.
The command to “read” the register is 0×00 (that’s hex), which is found in the datasheet. After that, the datasheet says, you have to repeat the address of the slave in read mode. This means that you put the 7 bits of the address on the SDA line, but with a 1 following it, and not a 0 (which was how beginTransmission() put it on the line). After that we can read the value, which is a single byte with the temperature in Celsius:
1 2 3 4 5 6 7 8 9 10 11 12 | Wire.beginTransmission(address); //start the transmission by writing the slave address Wire.send(0x00); //send the command to read the temperature Wire.requestFrom(address, 1); //read 1 byte from address //this re-broadcasts the address in read-mode int temperature; if (Wire.available()) { //check if we can read temperature = Wire.receive(); } |
Now our integer “temperature” holds the current temperature. In theory. If something went wrong it’ll hold 0, which is a valid temperature (a little too cold to be working with small electronic components though
). It would be best to add an else-clause to the if-clause and do something meaningful.
A full sketch that reads the temperature and puts it on the serial line looks like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | #include "Wire.h" //wire library #define address 0x4F //address of the temperature sensor #define delayC 1000 //delay count in ms #define baudrate 9600 //baudrate for communication void setup() { Wire.begin(); Serial.begin(baudrate); } void loop() { Serial.print("temperature in Celsius: "); //let's signal we're about to do something int temperature; //temperature in a byte Wire.beginTransmission(address); //start the transmission Wire.send(0x00); Wire.requestFrom(address, 1); if (Wire.available()) { temperature = Wire.receive(); Serial.println(temperature); } else { Serial.println("---"); } Wire.endTransmission(); //end the transmission delay(delayC); } |
I hope this was a little helpful to you. I found it a bit hard to start with I2C because of a lack of documents on the web written for noobs like me. If you want to read more, I would suggest the following resources that detail specifically with Arduino’s I2C support:
- Some guy named Keith’s Arduino I2C Expansion I/O
- Near Future’s Laboratory’s Arduino and the Two-Wire Interface (TWI/I2C) Including A Short Didactic Parenthetical On Making TWI Work On An Arduino Mini
Comments
hey there, i was looking information about the I2c trasmission protocol with arduino, because i bougth a line follower and i canĀ“t get it work. i would like to know if you can help us… the name of the component is I2C LINE FOLLOWER
thnks
[...] to be very easy to work with but for me as a lay person it’s still complicated. Paul found a tutorial on the web for connecting it to the Arduino. Everything works fine except that the temperature is [...]
By the way, the address doesn’t have to be in hexadecimal, we just converted it to decimal and it works fine. Regards, Danielle.
Decimal notation is something most non-technical people are more familiar with. They are just representations of the same number, so you can usually use them interchangeably with a little conversion here and there. Hex notation is much more common in technical documentation and datasheets though, and by copying it you may reduce the risk of (conversion) errors in your code.
This was a great I2C article. I’ve been looking at so many and I just couldn’t get my project to work until I stumbled on this page. I’m interfacing a RFID I2C circuit to my Arduino and the manufacture’s datasheet was nonexistant. 3 days later, and finding this page, I finally have it working!
My problem was I was running the beginTransmission, send, endTransmission and then the receiveFrom. That’s what all the articles I found did… your’s was the only one that ran the receiveFrom before the endTransmission. And it works. Thanks!
[...] Arduino and an I2C temperature sensor : Naneau [...]