Bring old toys back to life with Arduino: Part II

Lead Image © Anton Brand, 123RF.com

I, Robot

Robotics are all the rage, and those old high-tech toys you might have lying around are ideal candidates for the Frankenstein/Arduino treatment.

In the previous issue, I showed how to upgrade a Robosapien toy with an Arduino brain [1]. Just to quickly recap, I chose a Robosapien robot because it is a great toy to hack an Arduino into: It was wildly popular about 10 years ago and sold millions of units, so, even if you don't have one yourself, there are a lot of second-hand ones around, and you can find one in perfect working order on the cheap on eBay. The toy is easily pulled apart without damaging its components, and the information printed on its control board makes it easy to figure out which wire controls which part of the Robosapien. Best of all: It has a lots of potential for cool hacks.

Movement is a big part of Robosapien, so in the last installment you learned how to implement an H-bridge that lets you easily control both speed and direction of a DC motor (the Robosapien uses DC motors, like those on electric cars, not servos) and you got hold of some L293Ds – chips that give you two H-bridges, one per side.

However, I left off in issue 9 with the realization that, despite the Arduino's many good points, it falls short when you need to control a large number of motors, such as those that move Robosapien's [2] limbs. The Robosapien has seven motors and each must be spun forward and backward to walk, move his arms, and open and close his claws. That means each motor needs the input from two PWM pins. That's 14 pins.

Most Arduinos have only 13 pins (plus five analog pins, but it's a waste to use all those to control motors), and you would still need to get feedback from Robo's sensors or control his LEDs and speaker to have a truly completely functional walking, talking robot. Simply put: An Arduino Uno and its ilk do not have enough pins to go around.

Pins in a Haystack

You could buy an Arduino Mega, which provides 54 I/O pins, but again, why bother? When you started on your maker adventure, you probably went for the most basic Arduino – the Uno, or maybe the Leonardo if you were feeling frisky – and that's what you've got kicking around at home. Believe me, that is all you need. Well, that and an MCP23017 port expander (Figure 1).

Figure 1: The MCP23017 port expander adds 16 more ports to your Arduino (or Raspberry Pi), and they can all be controlled by just two analog pins.

As with the L293Ds, you can buy these chips online cheaply: These babies go for a little over a dollar each. As its name implies, the MCP23017 expands the number of ports by giving you eight more on each side, 16 in total. That's plenty to control Robosapien's seven motors and then some.

To understand how the digital port expander works, you must be familiar with binary numbers. Take a look at Figure 2. The MCP's 16 ports are the top eight ports on the right (known as the A ports, pins GPA7 to GPA0) and the top eight ports on the left (the B ports, pins GPB0 to GPB7). You use two analog pins on your Arduino to control each of the two sets of ports. Suppose you use the A4 pin to control the A ports and A5 to control the B ports. You connect the A4 pin on your Arduino to the SCL pin on the MCP23017 and the A5 pin to the MCP's SDA pin. (Hooking up this chip to the Arduino is more complicated than that, but I'll get to a complete setup shortly).

Figure 2: The MCP23017 layout. The ports are the top eight pins on the right (GPA7 … GPA0) and left (GPB0 … GPB7) of the diagram.

Say you now want to push a HIGH value to pins GPA0, GPA3, and GPA4: You would send the binary number 00011001 over the Arduino's A4 pin, as shown in Listing 1. The first bit from the left of the binary number above sets GPA0 to HIGH, the fourth bit does the same thing for GPA3, as does the fifth bit for GPA4.

Listing 1

Send a Byte to MCP23017

01 ...
02 Wire.beginTransmission(0x20);
03   Wire.write(A4);
04   Wire.write(B00011001);
05 Wire.endTransmission();
06 ...

I'll come back to this and show the snippet in context a bit later on. First, however, I'll set up all the wiring between the Arduino and the MCP23017 and show a practical example.

Binary Converter

Probably the simplest project you can build to test an MCP23017 is to have a row of eight LEDs hooked up to the port expander and then use them to represent 8-bit binary numbers. The setup would look like Figure 3. The wiring is as shown in Figure 4.

Figure 3: A row of LEDs connected to a port expander used to show a visual representation of binary numbers.
Figure 4: Setting up eight LEDs to test your MCP23017.

I'll start with the simple stuff: All the LEDs must be grounded. No surprises there. Their anodes (long legs) are connected to pins GPB0 through GPB7. As explained previously, you then run a cable from pin A4 on the Arduino to the SDA pin on the MCP23017 and another from A5 to SCL. Even if you're only using the left side of the port expander, you're going to need both of those connections for this to work.

For this example, you can ground the MCP23017's A0, A1, and A2 pins. As an I2C device, these pins establish the address the chip will use to identify itself to the Arduino. Grounding all three of the Ax pins gives an address of B00100000 in binary (or 0x20 in hexadecimal). If you ground A2 and A1, but connect A0 to the Arduino's 5V pin, the MCP23017 becomes B00100001 (or 0x21 in hex). You can probably see where this is going, but check the MCP23017 address table shown in Table 1 if you're unsure. You will need to change the MCP23017's address if it coincides with that of another I2C device connected to your Arduino.

Table 1

MCP23017 Address Table

A2

A1

A0

Address (Binary)

Address (Hex)

GND

GND

GND

B0010000

0x20

GND

GND

5V

B0010001

0x21

GND

5V

GND

B0010010

0x22

GND

5V

5V

B0010011

0x23

5V

GND

GND

B0010100

0x24

5V

GND

5V

B0010101

0x25

5V

5V

GND

B0010110

0x26

5V

5V

5V

B0010111

0x27

Getting back to your wiring, you next need to ground the Vss pin from the MCP23017 and then connect its RESET and Vdd pins to the Arduino's 5V pin.

Finally, you pull up two 4.7K resistors [3] on each of the connectors coming from the Arduino's A4 and A5 pins. This step makes sure they are at a defined logical voltage level.

Listing 2 shows the type of code you could use with this type of setup. On line 1, you bring in the Wire library, which allows you to communicate with I2C devices. On lines 3 and 4, you define which Arduino analog ports you're going to use to send instructions to the port expander chip. In this case, as seen in the hardware stage of the setup, you'll be using ports A4 to control the A ports, and A5 to control the B ports. On line 5, you tell the Arduino what address the MCP23017 will be using. Because the chip's A0, A1, and A2 pins are all connected to ground (they are all equal to 0), its address is B00100000, or 0x20.

Listing 2

Binary Display

01 #include "Wire.h"
02
03 const byte a_Ports = A4;
04 const byte b_Ports = A5;
05 const byte mcp_Add = 0x20;
06
07 void setup() {
08   Wire.begin();
09
10   Wire.beginTransmission(mcp_Add);
11     Wire.write(0x01);
12     Wire.write(0x00);
13   Wire.endTransmission();
14
15   Wire.beginTransmission(mcp_Add);
16     Wire.write(b_Ports);
17     Wire.write(0);
18   Wire.endTransmission();
19
20   Serial.begin(9600);
21 }
22
23 void loop() {
24   if(Serial.available()){
25     Wire.beginTransmission(mcp_Add);
26       Wire.write(b_Ports);
27       Wire.write(Serial.parseInt());
28     Wire.endTransmission();
29   }
30 }

In the setup() section, you initiate the Wire library and have the Arduino join the I2C bus as master (line 8). On line 10, you start the transmission to the MCP23017 and then you prepare the B bank of ports (0x01 on line 11) for output (0x00 on line 12).

The code that starts on line 15 and ends on line 18 simply sends a 0 over the wire to the MCP23017 to make sure all the LEDs are off. Line 20 opens the serial line so you can input numbers and have the Arduino relay them on to the port expander.

The loop() function is short and sweet. You check whether the user has written anything into the serial monitor on line 24; and, if so, you pass it on to the MCP23017 on lines 26 and 27. The Serial.parseInt() on line 27 is a very useful little function that converts inputs from 0 to 255 into their numeric equivalents. If you don't use parseInt(), the Wire class will parse the input of the first character of the string and show the ASCII code of that instead.

Next, upload the sketch to your Arduino, open the Serial Monitor from your Arduino IDE, and type in a number from 0 to 255. After a brief pause, a combination of LEDs will light up corresponding to the 1s in the number in its binary form.

Buy this article as PDF

Express-Checkout as PDF

Pages: 8

Price $2.95
(incl. VAT)

Buy Raspberry Pi Geek

SINGLE ISSUES
 
SUBSCRIPTIONS
 
TABLET & SMARTPHONE APPS
Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content