USB 16-servo Controller linux

Ok so I am at my wits end here, I can’t seem to get this controller to move my servos. There is power hooked up to the servos and I am in Mini SSC II Mode.

Here is the code I am using (the problem is most likely in here, but I know little about interfacing to a serial port). Whenever I run the program the green transfer LED flashes briefly so I’m assuming its receiving the data.

int main(){
	struct termios newtio;
	int fd;

	fd = open("/dev/ttyUSB1", O_RDWR | O_NOCTTY);

	newtio.c_cflag = BAUD | CRTSCTS | DATABITS | STOPBITS | PARITYON | PARITY | CLOCAL | CREAD;
      	newtio.c_iflag = IGNPAR;
     	newtio.c_oflag = 0;
      	newtio.c_lflag = 0;
      	newtio.c_cc[VMIN]=1;
      	newtio.c_cc[VTIME]=0;
      	tcflush(fd, TCIFLUSH);
      	tcsetattr(fd,TCSANOW,&newtio);

	
	char Key = (char)255;
	write(fd, &Key, 1);
	Key = (char)18;
	write(fd, &Key, 1);
	Key = (char)254;
	write(fd, &Key, 1);
	

	close(fd);
}

Another thing that I think is odd is that when I first plug the controller into the USB of my computer it appears to run through its start up sequence fine. I pull out my volt-meter to test connectivity and all the + pins are connected to + input for the servo controller and likewise for the - pins (which is fine). However when I run the above program all the pins (signal/+/-) become connected to eachother…is this supposed to happen?

Thanks for any help.

EDIT:
Forgot to mention that I am not using a servo from Pololu. It is a Hitech HS-422 servo.

-Brian Peasley

Hello,

I’m sorry that you’re have trouble getting our servo controller to work under linux. Most likely, the problem comes from configuring the serial port incorrectly - those cflags and the other settings are tricky to get right. Take a look at Section 4 of our serial port tutorial to see example serial port settings that have worked for me; I’d suggest that you try those and try to understand what you are doing differently. In particular, I don’t understand what part of your code is setting the baud rate. Have you been able to connect to other devices with the serial port using similar code?

-Paul

Thanks for the reply. The Baudrate/parity/etc is being set above int main
Baud = B9600
Data = CS8
Stop = 1
Parity = 0
Parityon = 0

And like I said I have little experience with programming serial devices so no I haven’t had this code work with any other device. I will work through the tutorial you provided me sometime tommorow and I’ll let you know how it goes, I don’t know why I didn’t see it before.

-Brian Peasley

Hello,

If you don’t have any red or yellow LEDs turning on after you see green flicker, the servo controller should be sending out control pulses (in Mini SSC II mode only). Can you verify that by seeing if the servos are resisting being moved?

The connectivity test probably doesn’t mean that much since there are various signals on the lines, and we don’t know exactly what your meter is using to decide that two nodes are connected.

- Jan

So there is some code in your program that wasn’t shown in what you pasted? Could you simplify your code to the minimum possible complete program that should work but doesn’t, and then paste the entire file in here so we can have a good look at it? Thanks,
-Paul

Hey Jan, yes the servos are resiting movement when the code is run. They go to one position and move back to that position if I move them.

Paul, that is the minimal program that should work. The only thing that isn’t seen is where the flags use to set newtio.c_flag are defined. I will paste that part of the code when I get back to my office.

-Brian Peasley

Ok here is all the code

#include <termios.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/signal.h>
#include <sys/types.h>


long BAUD = B9600;                     
long DATABITS = CS8;
long STOPBITS = 1;
long PARITYON = 0;
long PARITY = 0;

int main(){
	struct termios newtio;
	int fd;

	fd = open("/dev/ttyUSB1", O_RDWR | O_NOCTTY);

	newtio.c_cflag = BAUD | CRTSCTS | DATABITS | STOPBITS | PARITYON | PARITY | CLOCAL | CREAD;
      	newtio.c_iflag = IGNPAR;
     	newtio.c_oflag = 0;
      	newtio.c_lflag = 0;
      	newtio.c_cc[VMIN]=1;
      	newtio.c_cc[VTIME]=0;
      	tcflush(fd, TCIFLUSH);
      	tcsetattr(fd,TCSANOW,&newtio);

	
	char Key = (char)255;
	write(fd, &Key, 1);
	Key = (char)18;
	write(fd, &Key, 1);
	Key = (char)254;
	write(fd, &Key, 1);
	

	close(fd);
}

Ok so I used the code in ssc.c and ssc.h but cutting out the Tcl/Tk GUI stuff since I don’t need or want a GUI. Here is the code for that and I only changed two things:
1.) added int main(){;}
2.) added an argument to the connectSsc function so it is now int connectSsc(ssc *s, char *port);

Am I using it these functions right? I am getting the exact same behavior as when I used my code. ( I put it in Pololu mode for this code )

/* functions for communicating with the pololu ssc over a serial port */

#include <stdio.h>   /* Standard input/output definitions */
#include <string.h>  /* String function definitions */
#include <unistd.h>  /* UNIX standard function definitions */
#include <fcntl.h>   /* File control definitions */
#include <errno.h>   /* Error number definitions */
#include <termios.h> /* POSIX terminal control definitions */
#include <sys/select.h>
#include "ssc.h"

int main(){
	ssc s;
	char portName[100];
	sprintf(portName, "/dev/ttyUSB0");

	connectSsc(&s,portName);
	sendCommand(&s, (char)0x01, (char)2, (char)2, (char)90);
}

int connectSsc(ssc *s, char *port) {
  struct termios options;
  int count,ret;

  s->connected=0; /* reset in case we fail to connect */

  s->fd = open(port, O_RDWR | O_NOCTTY);
  if (s->fd == -1) return 0;
  else fcntl(s->fd, F_SETFL, 0);

  tcgetattr(s->fd, &options);

  /* go to 9600 baud */
  cfsetispeed(&options, B9600);
  cfsetospeed(&options, B9600);

  options.c_cflag |= (CLOCAL | CREAD); /* enable */

  options.c_cflag &= ~PARENB; /* 8N1 */
  options.c_cflag &= ~CSTOPB;
  options.c_cflag &= ~CSIZE;
  options.c_cflag |= CS8;

  /* set all of the options */
  tcsetattr(s->fd, TCSANOW, &options);

  s->connected=1;
  return 1;
}

/* write a message to the SSC */
void sendCommand(ssc *s, char id, char cmd, char servo, char data) {
  char buf[5];
  snprintf(buf,5,"%c%c%c%c%c",0x80,id,cmd,servo,data);
  write(s->fd,buf,5);
}

void sendCommand6(ssc *s, char controller, char cmd, char servo, char data1, char data2) {
  char buf[6];
  servo += controller*16;
  snprintf(buf,6,"%c%c%c%c%c%c",0x80,0x01,cmd,servo,data1,data2);
  write(s->fd,buf,6);
}

int enable(ssc *s, int controller, int servo, int enabled) {
  if(!s->connected) { return 0; }

  if(enabled) {
    sendCommand(s,(char)controller,0,(char)servo,(char)0x40);
    printf("enabled %i\n",servo); fflush(stdout);
  } else {
    sendCommand(s,(char)controller,0,(char)servo,0x00);
    printf("disabled %i\n",servo); fflush(stdout);
  }

}

int stepTo(ssc *s, int controller, int servo, int pos) {
  if(!s->connected) { return 0; }

  sendCommand6(s,(char)controller,4,(char)servo,(char)(pos/128),(char)(pos%128));

  return 1;
}

void endSession(ssc *s) {
  if(!s->connected) return;
  close(s->fd);
}



const char *getId(ssc *s) {
  return s->idstring;
}

If your servos are moving to a position and holding it, it seems like the servo controller is working. If you just send one command, you won’t see much motion since the servo would just hold that position.

Also, you should see at least a small difference between the Mini SSC II mode and Pololu mode. In Mini SSC II mode, there is no concept of a servo being “off”, so all servos will be sent to neutral as soon as the first byte is received. In Pololu mode, each servo stays off until it is individually commanded to a position or explicitly turned on.

If you get one command to go through without causing an error, you could try moving on to a loop that sends all 128 possible servo numbers to a new position, and see if that does something. Otherwise, you might be sending the command to a servo number that the servo controller is not controlling.

- Jan

ok I got it working all I had to do was

while(1)
stepTo(&s,controller,servo,pos);

So does that mean it’s all working fine now? Do you have an idea of what you did wrong before?

When you get a chance, it would be nice to see the completed, working code.

-Paul

Yea it is all working fine now. Here is the code I’m using.

/* functions for communicating with the pololu ssc over a serial port */

#include <stdio.h>   /* Standard input/output definitions */
#include <stdlib.h>
#include <string.h>  /* String function definitions */
#include <unistd.h>  /* UNIX standard function definitions */
#include <fcntl.h>   /* File control definitions */
#include <errno.h>   /* Error number definitions */
#include <termios.h> /* POSIX terminal control definitions */
#include <sys/select.h>
#include "ssc.h"

int main(int argc, char **argv){
	ssc s;
	char portName[100];
	int controller = 0;
	int servo = 1;
	int pos = atoi(argv[1]);
	sprintf(portName, "/dev/ttyUSB0");

	connectSsc(&s,portName);
	enable(&s, 1, 1, 1); //controller, servo, enabled

	
	while(1)
		stepTo(&s,controller,servo,pos);
	
	
	endSession(&s);
}

int connectSsc(ssc *s, char *port) {
  struct termios options;
  int count,ret;

  s->connected=0; /* reset in case we fail to connect */

  s->fd = open(port, O_RDWR | O_NOCTTY);
  if (s->fd == -1) return 0;
  else fcntl(s->fd, F_SETFL, 0);

  tcgetattr(s->fd, &options);

  /* go to 9600 baud */
  cfsetispeed(&options, B9600);
  cfsetospeed(&options, B9600);

  options.c_cflag |= (CLOCAL | CREAD); /* enable */

  options.c_cflag &= ~PARENB; /* 8N1 */
  options.c_cflag &= ~CSTOPB;
  options.c_cflag &= ~CSIZE;
  options.c_cflag |= CS8;

  /* set all of the options */
  tcsetattr(s->fd, TCSANOW, &options);

  s->connected=1;
  return 1;
}

/* write a message to the SSC */
void sendCommand(ssc *s, char id, char cmd, char servo, char data) {
  char buf[5];
  snprintf(buf,5,"%c%c%c%c%c",0x80,id,cmd,servo,data);
  write(s->fd,buf,5);
}

void sendCommand6(ssc *s, char controller, char cmd, char servo, char data1, char data2) {
  char buf[6];
  servo += controller*16;
  snprintf(buf,6,"%c%c%c%c%c%c",0x80,0x01,cmd,servo,data1,data2);
  write(s->fd,buf,6);
}

int enable(ssc *s, int controller, int servo, int enabled) {
  if(!s->connected) { return 0; }

  if(enabled) {
    sendCommand(s,(char)controller,0,(char)servo,(char)0x40);
    printf("enabled %i\n",servo); fflush(stdout);
  } else {
    sendCommand(s,(char)controller,0,(char)servo,0x00);
    printf("disabled %i\n",servo); fflush(stdout);
  }

}

int stepTo(ssc *s, int controller, int servo, int pos) {
  if(!s->connected) { return 0; }

  sendCommand6(s,(char)controller,4,(char)servo,(char)(pos/128),(char)(pos%128));

  return 1;
}

void endSession(ssc *s) {
  if(!s->connected) return;
  close(s->fd);
}



const char *getId(ssc *s) {
  return s->idstring;
}

I’ll just use signals or something to change pos, so I won’t ever really need to leave that while loop.

Another question I have, is it possible to send the USB controller one bit at a time?

It’s not possible to send one bit at a time. Why would you want to do that?

(By the way, being a serial interface, you’re always sending just one bit at a time. But the protocol is such that you have to send a whole byte every time you want to send something.)

- Jan

yea I am aware you send one bit at a time. the reason why I was asking because I was trying to also interface this thing with a Ping))) ultrasonic range finder. To make it pulse you need to send a 0-1-0 (with about a 5 microsecond delay in between each). I have sense decided to not to try and interface with the sonar…unless you guys know of a way to do this.