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!
Final words
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.
OK, this sounds great! While we’re at it, how do I save to files? And is there anything I can do to set up to bricks to do the same thing (like keep track of time and activate a light as an alarm clock), while simultaneously restarting each other in the event of a crashed brick? (For now, the software. Tomorrow, the world-oops, I mean hardware. Yeah, hardware…)
That sounds like a question to ask on the forums: http://www.robotc.net/forums/
I find it confusing that you seem to reuse the I2Cmessage array for both sending a request to the sensor and for storing its response. Either I expected you to create another array to store the sensor’s response, or that you used the end of the same array to store the single byte response.
Apparently you chose for the latter option since I2Cmessage is 4 bytes long, but shouldn’t you then indicate the proper offset for writing the response, i.e. readI2CReply(S1, I2Cmessage[3], 1) instead of readI2CReply(S1, I2Cmessage[0], 1)?
Anyway, thanks for this post and I’m looking forward to the follow up!
Arie,
I see what you mean. The response will overwrite the byte holding size which is undesirable. I will fix the code and update the screenshots.
Thanks,
Xander
Thanks for the great information. I have a couple of questions. How do you know what I2C slave address is for a particular sensor? If they are all addressed to 0x2, how do you access multiple sensors on one sensor port? Is there a better way to know that a reply has arrive other than waiting 20ms?
The address is something that will be in the documentation. You can only access multiple sensors on a single port IFF they have unique addresses -AND- only pair of pull-up resistors is present. Most NXT sensors already come with these pull-ups built-in and cannot be disabled. That means that you cannot use more than one of these. However, the Mindsensors sensors can be reconfigured to have a different address and can also check if pull-ups are already present and disable its own. You could also use a multiplexer like the one sold by HiTechnic and by pass this problem altogether.
The 20ms is arbitrary. You could sample more frequently than that, but the sensor’s value may not have changed in the mean time. It is very dependent on the sensor in question.
Thank you for your help. I have a Port Splitter for NXT from mindsensors.com (version 1). I was hoping to have a light sensor, color sensor, and touch sensor all on one port using I2C. However, it looks like my splitter does not support these sensors. It is still worth a try. It is very difficult to find any documentation containing their addresses but I’ll look into it. Thank you again.
Your explaination at the I2C testing page linked me here, but I still don’t fully get it… I’m not sure where to put the commands for turning different LEDs on and off (the 0xFF to 0x00) in the program…
thx,
DiMastero
Did you have a look at the driver suite that I wrote? The PCF8574 driver comes with an example program.
http://rdpartyrobotcdr.sourceforge.net/
– Xander
I looked at the PCF8574 example program, but I don’t get it (at all)… Could you explain it?
DiMastero,
Contact me at the email address at the top of the source code.
– Xander
You you should change the post name ROBOTC I2C Howto (Part I) I’d Rather Be Building Robots to something more specific for your content you create. I loved the the writing however.
What should I change it to then?
Hi, I get the next compiler error: **Error**:Undefined variable ‘sensorLowSpeed’. ‘short’ assumed.
Later versions of ROBOTC use “sensorI2CCustom” instead. Try that and see if it works.
[…] — краткие курсы можно найти здесь и здесь (на английском […]
In step 6 on line 23, I get an error. I looked at the online Wiki and also at the RobotCIntrinsics. Unfortunately my knowledge is not yet at a point I can crack this. The code for this line is ->
sendI2CMsg(S1, I2Cmessage[0], 1);
The error during compiling is
**Error**:Calling procedure ‘sendI2CMsg’. ‘*’ Indirection levels mismatch. Parameter: ‘unsigned const char * pSendMsg’. Expression: ‘I2Cmessage[0]’. Type: ‘sbyte’
I am running RobotC 3.51. I am trying use a kit to generate an analog output. I am working to develop a working knowledge of I2C. I see many applications for my project.
http://mindsensors.com/index.php?module=pagemaster&PAGE_user_op=view_page&PAGE_id=92
Any input on solving my compile error or my general approach is appreciated.
Cheers,
Blair
Hi there Blair,
Unfortunately, there are parts of the tutorial that are somewhat outdated. With the introduction of ROBOTC 3.5x, came pointers and this changed how many functions worked. I will try to find some time to update this stuff, but it may take some time. In the mean, you can simply add a & in front of the I2Cmessage[0] to turn it into a pointer, so like this: &I2Cmessage[0].
Also, I would recommend you update to ROBOTC 3.59.0 and stop using the version you are using, it’s too old. You can get it from here: http://botbench.com/blog/2013/02/26/robotc-3-59-0-available-now/
= Xander
Xander,
Thanks for the insight. I uplifted to version 3.54 and did some testing with the ‘&’. No success yet but I will looking further into the I2C help material and do some further reading.
Cheers,
Blair
Thanks for sharing all this info Xander. I notice that the images in the IC2 how to blog refer to a site that isi no longer active. Is it possible for you to share those images ?
Unfortunately, they were hosted on my previous site, which doesn’t exist anymore. I am guessing they were not migrated along at the time. The EV3 I2C stuff works very differently anyway. What were you looking to do?