22 Nov

hooking up with an SD21

I bought some bits and pieces for the robot project. After a lot of consideration, I chose to go the servo route instead of the DC motor route. The difference is that with DC motors, you just turn them on and off they go, but with servos, you can tell the motors how far to go, and at what speed. I like the idea of having that fine control (which would probably be needed if I’m picking up twigs etc).

Unfortunately, the LPT port is pretty rare these days. In the first incarnation of my robot, I used the LPT port to directly turn motors on or off, using a home-built h-bridge to tell the motors to go forward or backward depending on what pins of the LPT I turned on.

I opted instead to use USB and I2C. I2C is a cool little standard – it lets you chain a load of devices into one connection. There is no direct I2C port on the outside of a laptop though, so i bought a USB-I2C convertor.

I found a shop online that sold all the necessary parts. Robot Electronics appears to be the shop-front for Devantech, who make a load of brilliant gadgets such as ultra-sonic rangers (not related to power rangers), on-chip compasses, and what I was interested in – servo controllers.

I bought the SD21 servo controller (controls up to 21 servos), a USB-I2C interface module so I could talk to the SD21, and 2 HS-55 Micro Lite servos so the other stuff actually did something, as well as some leads to connect the interface module to the motor controller.

By the way, the word ‘micro’ is a very apt description – they’re tiny! The image on the right shows the two of them with my mouse in the background.

The stuff finally arrived. Next was the tricky part – figuring out how they all go together.

Using the tech-specs for the usb-i2c and the sd21, it was easy to figure out. The only tricky part was that the USB-I2C module has 5 pins on it, although I2C actually only uses 4. in my photo above-left, the white cable is not used at all (it’s a test pin used by Devantech). So hook the USB-I2C and SD21 together connecting to one of the I2C connectors on the board.

in my case, I got a 5-way and a 4-way cable and just stuck the ends together. Connect black to black, red to red. But the blue in each cable connects to the yellow in the other. Then stick them onto the boards.

Power is provided by hooking 7.2v into the green block. I used 6 1.2v rechargeable batteries. Actually, 4 of them are 1.25v, but the board didn’t complain much. Be very careful that you hook them into the SD21 the right way around! I put the power in the wrong way once and had to yank them apart when I smelled burning parts! Luckily, there wasn’t anything wrong afterwards, but beware!

After all that, the hard part was figuring out how to actually talk to the thing. It took me some reading to figure out the obvious… you don’t speak directly to the SD21. You speak to the USB-I2C and it then talks to the SD21.

Here’s a small C program that turns the motors first in one direction, and then in the other. BTW: it’s been a century since I wrote C, so if I’ve done anything obviously wrong (especially that int to bytes conversion bit…) please tell me.

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>

struct termios options;

int open_port(void) {
  // this function taken from http://www.robot-electronics.co.uk/forum/viewtopic.php?f=5&t=165
  int fd; // File descriptor for the port
  fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY);
  if (fd == -1) {
    perror("open_port: Unable to open /dev/ttyUSB0 - "); // Could not open the port.
  }
  else {
    fcntl(fd, F_SETFL, 0);

    // Get the current options for the port...
    tcgetattr(fd, &options);

    // Set the baud rates to 19200...
    cfsetispeed(&options, B19200);

    // Enable the receiver and set local mode...
    options.c_cflag |= (CLOCAL | CREAD);

    // Set no parity bit
    options.c_cflag &= ~PARENB;

    // Set 2 stop bits
    options.c_cflag &= ~CSTOPB;

    // Set the character size
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8;

    // Set the new options for the port...
    tcsetattr(fd, TCSANOW, &options);

    fcntl(fd, F_SETFL, FNDELAY);
  }
  return (fd);
}

void set_servo(unsigned char reg, int pos) {
  // sets servo 'reg' to position 'pos'
  unsigned char sbuf[7];
  unsigned char lbyte, hbyte;
  int fd;

  hbyte=(unsigned char) (pos >> 8);
  lbyte=(unsigned char) (pos);

  sbuf[0] = 0x55;  // ftdi mode (multibyte device)
  sbuf[1] = 0xC2;  // address of the i2c device
  sbuf[2] = reg*3; // each servo has three registers
  sbuf[3] = 0x03;  // bytes to send
  sbuf[4] = 0x00;  // set to full speed
  sbuf[5] = lbyte; // position low byte
  sbuf[6] = hbyte; // position high byte

  fd = open_port();
  write(fd, sbuf, 7);
  close(fd);
}

int main() {
  // take a step to the left
  set_servo(0, 800);
  set_servo(1, 800);

  sleep(2);

  // and then a jump to the right
  set_servo(0, 2200);
  set_servo(1, 2200);

  return 0;
}