I am writing this tutorial in response to a Google Search query that landed someone on this blog. Now I am pretty sure there isn’t one of those tutorials on my blog, so I am not sure what brought that person here. To make sure the next person who lands here does get a satisfactory answer, I’ve put something up.
So, you want to learn more about I2C and how to play with digital sensors? ROBOTC allows you to talk to them directory from inside a very useful utility. Let’s get started.
Talking to the Sensor
All digital sensors that want to connect to the NXT use a protocol called I2C. It’s a very simple protocol that allows a controller, also known as the master, to talk to sensors or other devices, known as slaves. The NXT is always the master and only allows one master to be on the same connection, or bus. So how does a slave know when the master wants something from it? It uses a 7-bit address, which can range from 0 to 127. There are some special addresses in the I2C range but they’re outside the scope of this tutorial. Below is an I2C address byte:
|Bit 7||Bit 6||Bit 5||Bit 4||Bit 3||Bit 2||Bit 1||Bit 0|
So what’s bit 0 (R/W) all about then? That’s the Read/Write bit. An I2C message can be either a read or write request. At the start of each message, the master will send the 7 bit address, followed by a 0 or 1, depending on whether it is a write or a read request. This is where things get a little confusing. LEGO, in its infinite wisdom, decided that instead of using the normal 7 bit addressing notation, it would instead use the 7 bit address + write bit (0). That means instead of using an address like 0x01 (that’s the hexadecimal notation for 1), it would use 0x02. It makes more sense if you look at it in binary format:
|Bit 7||Bit 6||Bit 5||Bit 4||Bit 3||Bit 2||Bit 1||Bit 0|
It’s basically the 0x01 (b0000_0001) address left-shifted by one to make 0x02 (b0000_0010). 0x02 is the address chosen by LEGO as the default address for digital sensors. Allowing only address 0x02 for sensors also prevents people from using more than one sensor on a sensor port, since you can’t have duplicate slave addresses on a single bus. There are exceptions to the 0x02 address rule, most notably most of Mindsensors’ sensors and the HiTechnic Sensor MUX and a few others. For clarity’s sake, all I2C addresses mentioned from now on are in the “standard” LEGO 8 bit notation, that is, the 7 bit slave address followed by the write bit (0).
LEGO’s Hardware Development Kit (HDK) has a lot of information on how a standard NXT sensor ought to behave. One of the features a sensor should support is a thing called “registers”. The I2C standard doesn’t really define these, but think of them as memory addr esses which can be accessed directly through I2C. Register addresses are also noted in hexadecimal. To request the contents of a particular register, the NXT would send the slave’s address, followed by the register address.
Some common register addresses are:
|0x00 – 0x07||Version of sensor’s firmware, e.g. “V1.0” or “V2.5”|
|0x08 – 0x0F||Product ID, like “LEGO” or “HiTechnc”|
|0x10 – 0x17||Sensor type, e.g. “SONAR”|
|0x42||Current sensor reading, e.g. 17 or 255|
There are others, too. For more information on the various registers and their function, I recommend you download the HDK and read through those documents.
Please note that I left out a couple of details regarding the I2C protocol. These pertain to the START/STOP conditions, ACK and NACK bits that get sent back and forth between the master and slave. Unless you are writing your own firmware, this is not something you will ever need to deal with when using an NXT. If you are curious about those details, I can recommend you take a peek at the official I2C specification. Be warned though, it can be a little overwhelming.
The examples below make the following assumptions:
- You are using ROBOTC 2.00 or higher;
- You have a LEGO UltraSound sensor connected to port S1;
- No other sensors are connected to any of the other sensor ports.
Opening a connection to your brick
Starting the I2C Utility
You can find the I2C Test Utility in the NXT Brick sub menu in the Robot menu item. Click on Robot->NXT Brick->Test I2C.
Configuring the Ports
Make sure “Port” is set S1. In the “Output Message” box, enter the sensor’s address, 0x02, and the register we’re interested in reading, 0x42 – the current sensor value. The slave will not respond with any bytes unless we tell it to. Set the “Reply Len” to 1. Make sure the “Select” tick box is marked.
Now hit the “Once” button to right of “Select” tick box. A reply should appear in the “Reply” text box. This will be a hexadecimal number representing the current distance the sensor is measuring. The value in brackets behind it is the ASCII representation of that number. Instead of hitting the “Once” button all the time, press the “Send Continuous” button. This will send all the selected messages to the specified sensors. Play around with this a little bit; hold your hand in front of the sensor and vary the distance. The number in the “Reply” text box will change accordingly.
It probably has not escaped your attention that the “Count” value increases very quickly when in continuous send mode, that’s the number of messages that been sent to the sensor. While you have the utility in continuous send mode, disconnect the sensor. You’ll notice the “Bus Errs” value changes. This means that the NXT cannot communicate with the sensor. This error will also happen if you specify the wrong I2C slave address. You can test that by changing 0x02 to 0x04, for example. This will try to address the slave at address 0x04.
What if we want to read more than a single byte? How about we try and read the sensor type. This information is held in registers 0x10 to 0x17. We could read it one by one but that would tedious and very inefficient.
Again, make sure S1 is selected from the “Port” drop down menu. In the “Output “Message” field, fill in “0x02 0x10”. This time set the “Reply" Len” to 8, the number of bytes we wish to read. Ensure the “Select” box is ticked and hit the “Once” button. The “Reply” field should now read “53 6F 6E 61 72 00 FF FF [Sonar...]”. Try the same with registers 0x00 to 0x07 (the version) and 0x08-0x0F (Product ID).
Writing to a Sensor
Writing data to a sensor is not all that different from what we’ve done so far. Instead of two values, the I2C address and register, we specify a third (or more) value. This value will be written to the specified register. For this part I used a HiTechnic Colour Sensor because it doesn’t work with the LEGO US sensor. However, any other HiTechnic digital sensor will do the trick as will most Mindsensors sensors. You may need to adjust the slave address, just refer to the sensor’s manual for additional information.
We’ll send two messages this time. Once to write out a value (0x66) to the register (0x80) and another to read that register back. You know the drill, configure the messages as specified above. Make sure that both messages have their “Select” box ticked. Optionally, you can turn off “Verify Write”, it kind of spoils the whole thing. Hit the “Once” button and “66” will magically appear in the “Reply” text box of the second message.
So what happened here? In the first message we’re telling the sensor to store the value 0x66 in register 0x80 and in the second message we’re asking it to return the value stored in 0x80. No more, no less.
Non-NXT I2C Slaves
So you bought yourself one of the Mindsensors Magic Wands and you’re wondering how to make it work with this utility. It’s actually a lot easier than you might think. The datasheet for the PCF8574A (the IC on the Magic Wand) specifies the default address as 0x38. However, remember that this is using the 7 bit notation, that means that you will need to specify 0x70 in the I2C test utility.
The PCF8574A does not use registers at all. That means that the data written to it is used directly to configure the state of the I/O pins. You write a 1 to it and the port will go high, write a 0 and it will go low. There’s a bit of a problem with the PCF8574A; while it’s a great current sink, it is a terrible current source. That means that if we want to connect LEDs to it, we need to reverse how they work. When the port goes low, the LED will switch on, when it goes high, the LED will switch off. That means that when we write 0x00 (all I/Os low), all the LEDs will switch on. Reversely, if we write 0xFF to the PCF8574A, the LEDs will all switch on.
All LEDs off (0xFF):
All LEDs on (0x00):
Alternating LEDs off/on (0xAA):
So there you have it. Play with the utility a little. It’s truly the best way to learn how to use it. Don’t be afraid, you won’t mess anything up. Leg Godt!