How do you feel about this article? Help us to provide better content for you.
Thank you! Your feedback has been received.
There was a problem submitting your feedback, please try again later.
What do you think of this article?
The new robot with twin rangefinders and light sensors. Both rangefinder modules share the same I2C communication channel, but on different addresses. Hence the E0 and E2 markings on their ultrasonic transducers.
This is the (almost) final part of my series on serial communication: Catching a Bus. The subject is a practical project which uses UART, I2C and SPI serial busses to link various ‘Off-the-Shelf’ (OTS) modules together, forming the ‘brains’ of a wireless/autonomous mobile robot. Since the previous article, the Parallax Boe-Bot based mobile robot has had an extra SRF08 rangefinder module added, and the light sensors on both now activated.
The robot’s electronics is based on the same MikroElektronika dsPIC33 Clicker 2 MCU board and Click peripheral modules used in the remote-control unit, with the exception of the input devices: the Click Thumbsticks being replaced by ultrasonic rangefinder units (Fig.1). I used the SRF08 devices instead of Click equivalents because:
- I happened to have them in my spares box.
- They are completely self-contained: send a ‘ping’ command via the I2C bus then read back the range in inches, centimetres or microseconds.
- They feature a light sensor which returns a light-level value between 0 and 255 when requested.
- The I2C address is easily reprogrammed allowing up to 16 devices to be controlled on one channel.
- They can be mounted upright without an extra adapter board.
- I happened to have them in my spares box.
The choice of mobile robot chassis was also determined by what was lying around on the bench, in this case, a Parallax chassis with an Arduino interface Shield. The previous processor board had been an Arduino-format dsPIC33 board obtained from Hong Kong via ebay. I retained the Shield when the ‘Arduino’ was upgraded to a Clicker 2 because it contained useful switchgear, sockets and 3.3/5 volt voltage regulators. The adoption of the dsPIC33 Clicker board necessitated the addition of an extra layer on the PCB stack to mount it on. The barrel jack plug on the end of the battery pack cable needed a new home as its socket had disappeared with the old processor board. A barrel socket was duly mounted on the new stripboard layer and linked to the Shield’s Vin and Gnd header sockets. The Parallax Shield doesn’t have a lot on it, but what it does contain is very useful in the development phase of a project. For instance, the power slide-switch has three positions: All power off, Electronics on but Drive Servos off, and All power on.
Parallax BoE Robotics Shield Kit
MikroElektronika Clicker 2 for dsPIC33E MIKROE-2567
Ultrasonic rangefinder (2) SRF08
nRF T Click mikroBUS MIKROE-1305
USB-UART 3 Click mikroBUS MIKROE-3063
The I2C Bus: Some practical aspects
The basic operating principles of I2C were covered in Part 4 of this series and now I’ll talk about creating a working link – and point out some common pitfalls encountered by developers. There are two basic components in any digital communications link: hardware and software. To an embedded system designer I2C hardware is trivial. Even a complex multi-Slave project just requires a pair of PCB tracks for clock (SCL) and data (SDA) signals. And of course, those all-important pull-up resistors without which nothing will work (Fig.1). This brings me to:
Pitfall One: Building a system from OTS modules and not noticing the lack of pull-ups in the final design. There are no ‘standards’ to determine whether a commercial MCU host board or an I2C-compatible peripheral module should be supplied with the necessary resistors fitted. For example, the Raspberry Pi does have them on-board, but the MikroElektronika Clicker 2 does not. Click modules all have the resistors permanently installed which could be problematic if many Slaves are linked causing the overall pull-up resistance value to fall too low for the open-drain drivers to handle. The Pmod range of I2C expansion modules is rather well designed as ‘daisy-chaining’ connectors are provided and pull-ups can be added by installing jumper links as required. Of course, if you are building a custom embedded board from individual chips, then the resistors are unlikely to be overlooked!
You’ve built your prototype system using modules from different manufacturers and by careful inspection of datasheets avoided Pitfall One. Now for the software. The MCU board will come with some sort of development software along with a library of drivers for the peripheral hardware provided on the chip. This will include I2C communications. The Arduino in its various forms is a popular choice, for example. The user will create applications using its Integrated Development Environment (IDE) software which runs on a PC. Now this leads on to:
Pitfall Two: Getting confused with 7- and 8-bit device addressing. Didn’t I say last time that 7-bit I2C device addresses were standard? (Actually, some implementations allow 10-bit addressing but let’s not get into that.) Confusion arises because the first byte after the Start sequence of a message uses the seven most-significant bits for the device address and the least-significant bit for the Read/Write flag (See Fig.3 in Part 4). Some products such as the Arduino IDE expect an I2C address in your code to be presented as a 7-bit number. Meanwhile, documentation for the SRF08 rangefinder for instance, provides its address in 8-bit format:
Documented SRF08 device 8-bit address = E0h = 1110 0000b
Arduino IDE expects it in 7-bit format = 70h = 0111 0000b
The IDE shifts the number left by 1 bit and this becomes the address byte following Start = E0h. If you use E0h in your code, it gets shifted left and becomes C0h. The SRF08 won’t respond to this incorrect address. This mismatch is probably the single biggest reason for an unresponsive Slave module. With a lack of pull-up resistors a close second.
The Robot Code
The basic function of the controller program is to capture ‘Thumbstick’ position data from the wireless channel, then send it to the PWM logic controlling the speed and direction of the robot’s two continuous-rotation servomotors (Fig.2). The original project aim was to develop a wireless remote-control system for a mobile robot based on mechanical joysticks enabling precise manual control. This has been achieved and now the objective is to make a semi-autonomous vehicle whose movements are determined by sensor data – until there is a manual intervention.
A listing for the Forth source code can be obtained by clicking on the link in the Downloads section below. As usual, this blog post is getting too long, so a more detailed description of the wireless link will have to wait until next time.
Rangefinders and light sensors on the I2C bus
The original design featured only one SRF08 module, but as I happened to have a spare, I added a second (Fig.3) to provide:
- Twin light sensors for light source tracking.
- ‘Stereo’ ultrasonics for experimenting with more precise object location.
- An opportunity to try out multi-Slave I2C bus operation.
The phototransistor light sensors replaced a pair of Light-Dependent Resistors and eliminated the need to use ADC channels on the host dsPIC33 MCU. Now light data can be read via the I2C bus and is updated every time the rangefinder is ‘pinged’.
The ‘beams’ of the two rangefinders overlap, but each module is activated in turn so as to avoid confusion between the returning echoes.
More than one Slave sharing the bus involves no extra hardware – unlike an SPI system. In Part 4 I described how chips designed for I2C usually have pins which can be pulled up or down to fix the low bit(s) of their address. This allows multiple devices to be connected on the same channel. The SRF08 can be programmed by its host to respond to an address other than the default E0h. In fact, up to 16 modules may share the same bus with addresses E0h, E2h, E4H, up to FEh. Notice that they are all given as even-numbered 8-bit addresses – bit 0 is reserved for the Read/Write flag. However, an Arduino IDE would expect them to be entered as 70h, 71h, 72h up to 7Fh. A small piece of code is run on the host that sends a command sequence to a lone module, changing its address; no others may be connected during this exercise.
Pitfall Three: Forgetting to ensure devices sharing the same I2C channel have different addresses.
I2C has one final trick up its sleeve: address 00h is reserved as a General Broadcast address. If the host transmits a message to address 00h, all the Slaves on the bus will respond. Imagine a mobile robot with an SRF08 on each of its four sides. All the devices can be triggered to transmit the ranging pulse at the same time by sending one message with the GB address. The received data messages can then be read back using the individual device addresses.
The I2C bus may seem slow and cumbersome when compared with the fast and simple SPI, but it conforms to a strict standard with many robust features. This conformance makes life so much easier for the developer, not having to design custom hardware and driver firmware. It’s so well thought out that the drivers I’ve written for two different MCUs in recent years have both worked perfectly first time, in spite of their apparent complexity. And that rarely happens! Failure to work is usually down to one or more of three pitfalls I’ve described, but thanks to the simple Slave acknowledgement feature of the protocol, the host MCU will detect the fault every time.
Next time I'll finally get around to talking about the wireless channel for my robot.
If you're stuck for something to do, follow my posts on Twitter. I link to interesting articles on new electronics and related technologies, retweeting posts I spot about robots, space exploration and other issues.