Serial 8-servo controller and Linux

Hi,

I try to make a code in C Linux for the servo Controller.
Ma hard Linux is a FOXBOARD (http://www.acmesystems.it/).
But controller don’t work
My code:

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

struct termios stdin_saved_attributes;
struct termios tty_saved_attributes;
int tty_fd;

int tty_open(char* tty_dev) {
	struct termios new_attributes;

	//tty_fd = open(tty_dev,O_RDWR| O_NOCTTY | O_NONBLOCK);
        tty_fd = open(tty_dev,O_RDWR| O_NOCTTY);
  if (tty_fd<0) {
  	return -1;
  } else {
		tcgetattr(tty_fd,&tty_saved_attributes);
		tcgetattr(tty_fd,&new_attributes);

		// Set the new attributes for the serial port
		// http://linux.about.com/library/cmd/blcmdl3_termios.htm
		// http://www.gnu.org/software/libc/manual/html_node/Low_002dLevel-I_002fO.html#Low_002dLevel-I_002fO

		// c_cflag
//		new_attributes.c_cflag |= CREAD;                  // Enable receiver
		new_attributes.c_cflag |= (CLOCAL | CREAD); 	  // Enable receiver
  	        new_attributes.c_cflag |= B9600; 	          // Set baud rate
//              new_attributes.c_cflag |= CS8;	 	          // 8 data bit
                new_attributes.c_cflag &= ~PARENB;                /* 8N1 */
                new_attributes.c_cflag &= ~CSTOPB;
                new_attributes.c_cflag &= ~CSIZE;
                new_attributes.c_cflag |= CS8;
                new_attributes.c_iflag |= CRTSCTS;                /* RTS/CTS */
                new_attributes.c_iflag |= (IXON | IXOFF | IXANY); /* Xon/Xoff */

             /*
		// c_iflag
  	        new_attributes.c_iflag |= IGNPAR; 	          // Ignore framing errors and parity errors.

		// c_lflag
               new_attributes.c_lflag &= ~(ICANON);               // DISABLE canonical mode.
  																				// Disables the special characters EOF, EOL, EOL2,
  																				// ERASE, KILL, LNEXT, REPRINT, STATUS, and WERASE, and buffers by lines.
               new_attributes.c_lflag &= ~(ECHO);	          // DISABLE this: Echo input characters.
  	       new_attributes.c_lflag &= ~(ECHOE);	          // DISABLE this: If ICANON is also set, the ERASE character erases the preceding input
  																				// character, and WERASE erases the preceding word.
  	       new_attributes.c_lflag &= ~(ISIG);	          // DISABLE this: When any of the characters INTR, QUIT, SUSP,
  	*/																			// or DSUSP are received, generate the corresponding signal.
              //new_attributes.c_cc[VMIN]=1;			  // Minimum number of characters for non-canonical read.
	      //new_attributes.c_cc[VTIME]=0;			  // Timeout in deciseconds for non-canonical read.

        (void)cfsetispeed(&new_attributes,  B9600);
        (void)cfsetospeed(&new_attributes,  B9600);

    tcsetattr(tty_fd, TCSANOW, &new_attributes);
	}
  return tty_fd;
}

// Serial version of printf

void tty_printf(char *format, ...) {
  va_list argptr;
  char buffer[256] = {0};

  va_start(argptr,format);
  vsprintf(buffer,format,argptr);
  va_end(argptr);

  write(tty_fd,buffer,strlen(buffer));
}

void termination_handler (int signum) {
	tcsetattr(STDIN_FILENO,TCSANOW,&stdin_saved_attributes);
	if (tty_fd>0) tcsetattr (tty_fd,TCSANOW,&tty_saved_attributes);
	close(tty_fd);
	printf("Exit\n");
	exit(0);
}

int stdin_init(void) {
  struct termios tattr;

  // Make sure stdin is a terminal
	if (!isatty (STDIN_FILENO)) {
		fprintf (stderr,"stdin is not a terminal\n");
	  return -1;
	}

	// Save the terminal attributes so we can restore them later.
	tcgetattr (STDIN_FILENO, &stdin_saved_attributes);

  // Set the funny terminal modes.
  tcgetattr (STDIN_FILENO, &tattr);
  tattr.c_lflag &= ~(ICANON | ECHO); /* Clear ICANON and ECHO. */
  tattr.c_cc[VMIN] = 0;
  tattr.c_cc[VTIME] = 0;
  tcsetattr (STDIN_FILENO, TCSAFLUSH, &tattr);
  return 0;
}

int main(int argc, char *argv[]) {

  char rxChar;
  char txChar;

  printf("Pololu Test (press ctrl-c to exit)\n");

  if (tty_open("/dev/ttyS3")<0) {
  	fprintf (stderr,"tty open error %s\n", strerror(errno));
	  exit(EXIT_FAILURE);
  }

  if (stdin_init()<0) {
  	printf("stdin init error %s\n", strerror(errno));
	  exit(EXIT_FAILURE);
  }

  if (signal (SIGINT, termination_handler) == SIG_IGN) signal (SIGINT, SIG_IGN);
  if (signal (SIGHUP, termination_handler) == SIG_IGN) signal (SIGHUP, SIG_IGN);
  if (signal (SIGTERM, termination_handler) == SIG_IGN) signal (SIGTERM, SIG_IGN);

  unsigned char buf[3];

  buf[0]=0x80;
  buf[1]=0x02;
  buf[2]=0x00;
  write(tty_fd,buf,3);
  //fflush(stdout);


  while (1) {


               buf[0]=0xFF;
               buf[1]=0x01;
               buf[2]=0xFE;
               write(tty_fd,buf,3);


       }
        return EXIT_SUCCESS;
}

Can you help please ?

Thank you
Olivier

Hello.

Can you provide some details about what happens when you run your program? For example, what feedback are you getting from the controller’s LEDs?

- Ben

Hi,

The Yellow Led go to off.
That’all.

Olivier

Hello,

I try directely on my UBUNTU, but i have the same result: No activity on Servo Controller…

A idea ?

I don’t found …

Hello,

I have remove the DTR/RTS jumper and now the servo controler have a activity.
I have modify my code with the function:
thread: Understanding commands?

void put(int servo, int angle){
   unsigned char buff[6];


   unsigned short int temp;
   unsigned char pos_hi,pos_low;

   temp=angle&0x1f80;
   pos_hi=temp>>7;
   pos_low=angle & 0x7f;

   buff[0]=0x80;//start byte
   buff[1]=0x01;//device id
   buff[2]=0x04;//command number
   buff[3]=servo;//servo number
   buff[4]=pos_hi;//data1
   buff[5]=pos_low;//data2
   write(tty_fd, &buff, 6);

And i have insert in while:
put(0, 90);

but all the servo move (left/right)

Can you help me ?

Thank you

Hello,

It looks like your code sends a position of 90, which is outside the valid range (500-5500, and even that range is intended to be outside what a servo can actually achieve). Is the yellow light turning back on to indicate an out-of-range warning?

You might also try the simpler Mini SSC II protocol, where you just have to send 3 bytes: 255, the servo number, and the servo position.

- Jan

Hi Jan,

I try in miniSSCII mode with this function:

void mini(int servomini,int posmini) {

  unsigned char buf[3];

  buf[0]=255;
  buf[1]=servomini;
  buf[2]=posmini;
  write(tty_fd,buf,3);
  printf("Servo %d Set to %d\n", servomini, posmini);
}

and send mini(0,127) or mini(0,254)
and my servo 0 runs a little…and my servo 1 also

Thank you for help

It would help if you did some more tests and provided additional details about what is happening. Saying “servo 0 runs a little…and my servo 1 also” doesn’t tell us much. Does servo 0 respond to every command? Does servo 1 do exactly the same thing as servo 0? What happens when you send commands to servo 1? What about the other servos? And so on.

- Jan

Hi Jan,
When i send mini(0,127) all the servo move but not in position 127. It’s random position.
Perhaps it’s my serial configuration ?

Thank you for advance

You are continuing to give us one data point at a time. In Mini SSC II mode, all servos should go to their neutral position after you send the first byte, so it’s normal for them to all move after the first command. When you say the servos go to random positions, do you mean that they go to different positions every time you try? What happens when you send additional commands?

- Jan

Hi Jan,

Yes when i send mini(0,127) more than once, the servo has not the same position.

I m testing with a loops for, and the servo move randomly and after le red led is on.

127, it’s a middle position ?

Thank you

If the red LED is turning on, you have a communications error. If you are getting non-deterministic results, you might have noise problems, too. Do you have a way of verifying your serial signal (e.g. an oscilloscope)?

I’m also skeptical of your use of loops. Are you making sure to account for whatever timing necessary to see the results on your servos? When things don’t work, I’m in favor of small, incremental changes where you send a few choice bytes. For instance, you might find that an error consistently happens on the ninth byte; if you are sending hundreds or thousands of bytes per second, and you just know that some error occurs, it’s harder to pin down.

- Jan

Hi,

Yes i see.
The controller and the cable it’s Ok with ssc-tester.

I don’t have a oscilloscope.

Do you have an code example?
when i send 255,01,127, th servo go in middle positon, It is this good?

Thnak you and good year

Happy New Year.

Yes, 127 is the middle position since the range is 0-254, and it’s good if your servo goes to the middle in response to that command. Earlier, you said that you were getting random results in response to the same command, so have you changed something?

Sample project 3 and customer project 3 on our sample projects page (https://www.pololu.com/projects/) have some code examples.

- Jan

Hi Jan,

Yes i have again a randomly position.
I try to modified my code but no change.

I have already read the sample N°3.
I modify the serial parameter with this exemple.

I don’t understand this:
options.c_iflag |= (IXON | IXOFF | IXANY); /* Xon/Xoff /
options.c_iflag |= CRTSCTS; /
RTS/CTS */
It’s ok if i have removed the jumper DTR/RTS ?

I search on my power; i have link the power of servo on the power of VIN. It’s correct ?
It’s Ok with the “pololu_controller.exe” on Windows but no with the ssc-tester on Cygwin.

Thank you for your help.

Those two lines you say you don’t understand should not be there. They enable flow control, which we do not want for the servo controllers. (Some serial ports don’t work well without it, and it did not matter for the 16-servo controller, which does not transmit anything back or use the DTR/RTS lines. The newer 8-servo controller has a full RS-232 transceiver that allows the servo controller outputs (just echoes of the input) to go back to your computer, and, in the case of software flow control, some special characters could cause the serial port to stop sending data.)

You haven’t really specified the power supply you’re using, so I cannot comment on it. When you say the servo controller works with “pololu_controller.exe”, do you mean you have complete operation as you expect it to work? In that case, your power supply would be okay, and you should just look at your code for problems.

- Jan

Hi Jan,

I don’t found my problem…
Can you test my code on your computer ?

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

struct termios stdin_saved_attributes;
struct termios tty_saved_attributes;
int tty_fd;

int tty_open(char* tty_dev) {
	struct termios new_attributes;
    tty_fd = open("/dev/ttyS1",O_RDWR| O_NOCTTY);

  if (tty_fd<0) {
  	return -1;
  } else fcntl(tty_fd, F_SETFL, 0);
  {
		tcgetattr(tty_fd,&new_attributes);

        /* go to 9600 baud */
        cfsetispeed(&new_attributes, B9600);
        cfsetospeed(&new_attributes, B9600);
        //new_attributes.c_cflag |= B9600 /*B9600*/;
        new_attributes.c_cflag |= (CLOCAL | CREAD); /* enable */

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

       new_attributes.c_iflag |= (IXON | IXOFF | IXANY); /* Xon/Xoff */
       new_attributes.c_iflag |= CRTSCTS; /* RTS/CTS */

//        (void)cfsetispeed(&new_attributes,  B9600);
//        (void)cfsetospeed(&new_attributes,  B9600);
    /* set all of the options */
    tcsetattr(tty_fd, TCSANOW, &new_attributes);
	}
  return tty_fd;
}

// MiniSSCII Mode
void mini(int servomini,int posmini) {

  unsigned char buf[3];
  int err = 0;
  buf[0]=0xFF;
  buf[1]=servomini;
  buf[2]=posmini;
  //write(tty_fd,buf,3);

  err = write(tty_fd,buf,3);
  if(err < 0)
        printf("Erreur d'ecriture\n");
  else
  printf("%d caracteres ecrit\n", err);
  printf("Servo %d Set to %d\n", servomini, posmini);
  printf("Buf0: %d ,Buf1: %d ,Buf2: %d \n", buf[0], buf[1],buf[2]);
}

int main(int argc, char *argv[]) {
  printf("Pololu Test (press ctrl-c to exit)\n");

  if (tty_open("/dev/ttyS1")<0) {
  	fprintf (stderr,"tty open error %s\n", strerror(errno));
	  exit(EXIT_FAILURE);
  }


mini(7,127);

  while (1) {
        }
  return EXIT_SUCCESS;



 }

thank you

Sorry, I don’t have a set up here to test your program.

- Jan