Note: this is a revised version of the original. There was a problem with the way the response was put in the array that held the original message sent to the sensor. That wouldn’t be a problem if I hadn’t setup my code to retransmit that message over and over without setting it up each time. The response would corrupt the transmitted message causing all sorts of problems. This problem was pointed out to me by one of my very astute readers (I can’t find your original email, sorry). Thank you!
I2C is a very popular communication bus for connecting low speed peripherals to embedded systems. It will come to no surprise that the NXT is also capable of communicating via I2C to digital sensors. Out of the box, the NXT is capable of talking to up 4 sensors at once. However, I2C can do much more than that. This howto is not going to very deeply into the protocol specifications, those details can be found in more than one spot on the Internet. Instead it will focus on how make the NXT play nice with all manners of I2C capable sensors and peripherals. This installment will introduce the basic calls and variables needed to make I2C calls. The next installment will focus on error handling and other fun things!
Please note that I am using hexadecimal notation (0x..) for messages sent through I2C. This is a convention you’ll find used throughout most of the world of microcontrollers. I suggest you practise a little with it, it will come in very handy as you progress. You can easily convert between decimal and hexadecimal using the built-in Windows calculator (calc.exe).
What you’ll need:
- An NXT brick with the ROBOTC firmware installed on it.
- A copy of ROBOTC on your PC, I recommend 2.00 preview or whatever comes after that. You can download that here: [LINK].
- A digital sensor, I’m using HiTechnic Colour Sensor in this example but you can use something else, like a Mindsensors ACCL-NX, it doesn’t matter.
Step 1: Let’s get started
Open a new source file in ROBOTC and create a standard main task with the following:
This configures port S1 as a low speed I2C sensor. This is a speed that should work with any I2C sensor as it uses the standard Lego clock speed (9600 Hz). The program doesn’t do a whole lot yet. It mainly just loops around until the cows come home or the batteries run out, whichever comes first.
Step 2: Declaring an array
I2C works by sending messages. To be able to send a message, we’ll need something to hold the message we want to send a response. An array of bytes will do the trick very nicely (line 5 + 6).
Step 3: Addressing the slave
Each device on the I2C bus has a unique address. If you want to talk to a slave, it needs to know you’re talking to it and not one of the other possible 126 slaves on the bus. This is done by setting the address of the slave as the first byte you send (line 12).
Why am I setting the 2nd byte in the array as the I2C address (0x02) and not the first? We’ll get to that later, rest assured that it’s supposed to be that way.
Step 4: Selecting the read register
Most sensors made for the NXT use a standard register layout. “What, pray tell, is a register?” you might ask yourself. Well, think of registers as memory locations or drawers, if you will, where information is stored. In order to get the right information, you need to know what register you want to read from. Lucky for us, you don’t usually have to guess where the information is kept. The register, or address, that holds the latest sensor reading is almost always 0x42 in the case of NXT sensors. We’ll use that as the 2nd byte we’re sending to the slave (line 15).
Step 5: Specifying message size
We’re almost ready to send our message to the slave but first we need to do one last thing; specifying the message size. Remember I said I would get back the first byte in the array later? Well, that time has come. In order for ROBOTC to know how much data to send to the slave, you need to specify the size of the message. In this case, our message is only two bytes long; the slave address (0x02) and the register we want to read (0x42), so let’s put that in our program (line 18).
Step 6: Getting the message across
We’re good to go now. The ROBOTC call to send a message via I2C is sendI2CMsg(nPort, sendMsg, nReplySize). The first argument, nPort, is S1, S2, S3 or S4. The second argument, sendMsg, is a pointer to the the array containing the message. The third and last argument, nReplySize, specifies the number of bytes we want to read back from the sensor in response to our request. We’ll tell ROBOTC that we want to read back a single byte (line 23).
Step 7: Handling the slave response
The code in step 6 isn’t all that useful considering we’re not actually doing anything with the slave’s response. To retrieve the response from the slave, you must use readI2CReply(nPort, replyBytes, nBytesToRead). Once again, nPort is the port specifier, S1 to S4, replyBytes holds the slave’s response and nBytesToRead tells ROBOTC how many bytes we’re expecting from the slave. We’ll print the response to the screen, so we can see (lines 28, 29).
Compile the program and upload it to your NXT brick. Run it and look at the the number on the screen. If you have the HiTechnic Colour Sensor connected, observe how the number changes when you hold differently coloured objects in front of the sensor. If you’re using the Mindsensors ACCL-NX, you’ll notice the number changes as you change the orientation of the sensor.
The sensor updates the contents of register 0x42 every couple of milliseconds (ms). What you are seeing are constantly updated values as returned by the sensor. Congratulations on writing your first I2C driver for the NXT!
The code for this program can be downloaded here: [LINK]. In the next installment I will go over error handling and how to deal with more than one byte in a response.