Results 1 to 5 of 5

Thread: Talking Serial to Renards

  1. #1
    Join Date
    Dec 2010
    Location
    Oceanside, CA
    Posts
    2,345
    Post Thanks / Like

    Default Talking Serial to Renards

    I'm writing some linux code to talk serial to the renard boards and am having troubles. I'm certain it's not a problem with the renard code, but somebody who knows more about serial communication should be able to help me.

    I have written some linux test code after reading Serial Programming Guide for POSIX Operating Systems. The renard seems to act on my output only some of the time. I'm not exactly sure.

    The pseudo code:
    Code:
    open the usb->serial device
    get the termios
    configure the termios structure (things like baud rate and 8 bits, no parity, 1 stop bit)
    set the termios
    flush
    loop, writing data for an 8 channel block from 0 to 255
    flush
    close
    The problem:
    Multiple runs cause the renard SS8 to only act about 80% of the time. That other 20%, the code will run just fine, but the renard does nothing for the entire stream of data. How can I "reset" the serial line at the beginning of every run so that it works 100% of the time like the renard plugin.

    The real code:
    Code:
    #include <fcntl.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <termios.h>
    
    #define DATA_SIZE 10
    
    int main()
    {
    	const char *device = "/dev/ttyUSB0";
    	int fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY);
    	if(fd == -1) {
    		printf( "failed to open port\n" );
    	}
    
    	struct termios config;
    	if(!isatty(fd)) { printf("Failed tty check\n"); exit(1); }
    	if(tcgetattr(fd, &config) < 0) { printf("Failed getting term config\n"); exit(1); }
    
    	// The following block sets 8N1
    	config.c_cflag &= ~PARENB; // No partity bit
    	config.c_cflag &= ~CSTOPB; // Unset 2 stop bits, which defaults to 1
    	config.c_cflag &= ~CSIZE;  // unmask all bit sizes
    	config.c_cflag |= CS8;     // set data to 8 bits
    
    	// Turn off all software flow control... maybe I want this?
    	config.c_iflag &= ~(IXON | IXOFF | IXANY);
    
    	// Call posix function to set raw (can also be done by unsetting the OPOST bit for c_oflag)
    	cfmakeraw( &config );
    
    	// Flush... what exactly am I flushing here?
    	tcflush( fd, TCOFLUSH );
    
    	// Set both input and output baud rates to 57600 since that's what my renard
    	// PICs are programmed to receive.  Don't really need to set input baud rate,
    	// but what the heck?
    	if ((cfsetispeed(&config, B57600) < 0) || (cfsetospeed(&config, B57600) < 0)) {
    		printf("Error setting tty speed to 57600\n"); exit(1);
    	}
    
    	// Set all the stuff I configured using FLUSH constant instead of "NOW"
    	if(tcsetattr(fd, TCSAFLUSH, &config) < 0) { printf("Failed to set our new config\n"); exit(1); }
    
    	// loop to ramp up
    	int i, j;
    	unsigned char * buff = malloc(DATA_SIZE);
    	for (i = 0; i <= 255; ++i)
    	{
    		printf("writing %d\n", i);
    
    		// Clear our buffer
    		memset(buff, 0, DATA_SIZE);
    
    		// Fill the buffer
    		buff[0] = 0x7E; //sync byte
    		buff[1] = 0x80; //address 0x80
    		for (j = 0; j < 8; ++j)
    		{
    			// TERRIBLE TERRIBLE HACK...
    			// If we're one of the special characters, send something
    			// else instead.  (Barely noticable in dimming curve test code
    			//
    			// TODO - Make this prettier: realloc
    			if (( i != 0x7D ) && ( i != 0x7E ) && ( i != 0x7F ))
    				buff[j+2] = i; // Set our dimming value
    			else
    				buff[j+2] = i-4; // Special Character, set something else (vew values lower)
    		}
    		// Send the data!
    		write(fd, buff, DATA_SIZE);
    		// Sleep for a tiny little bit (10ms)
    		usleep(10000);
    	}
    
    	// Flush before closing (I read this somewhere... not sure what exactly I'm flushing here)
    	tcflush(fd, TCOFLUSH);
    	// Close the serial port and call it done
    	close(fd);
    }
    To compile and test run, simply run:
    Code:
    $ gcc -o test_renard test_renard.c
    $ ./test_renard
    Last edited by Materdaddy; 07-25-2011 at 11:39 PM.
    [url]http://christmasonquiethills.com/[/url]
    [url]http://diychristmas.org/vb1/index.php[/url]

  2. #2
    Join Date
    May 2007
    Posts
    4,743
    Post Thanks / Like

    Default Re: Talking Serial to Renards

    Have you tried using a real serial port, rather than a USB port? I'm always suspicious that the baud rate coming from a USB dongle might not be that accurate.
    Phil

  3. #3
    Join Date
    Jan 2010
    Location
    New Zealand
    Posts
    178
    Post Thanks / Like

    Default Re: Talking Serial to Renards

    Try this... it works for me.

    Code:
    // gcc com.c -o com
    // gcc -Wall com.c /usr/lib/libm.a -o com
    #include <stdlib.h>
    #include <string.h>
    #include <termios.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <sys/signal.h>
    #include <sys/types.h>
    #include <time.h>
    #include <math.h>
    
    char devicename[] = "/dev/ttyS0";
    volatile int READY = 1;
    int serial;
      struct termios oldtio, newtio;       //place for old and new port settings for serial port
      struct sigaction saio;               //definition of signal action
    
    #define MAX_CHANS 48
    #define SEQ_LEN 1000
    struct state {
      char ch[MAX_CHANS];
    };
    
    struct state event[SEQ_LEN];
    
    /***************************************************************************
    * Incoming signal handler... called when previous block has been sent      *
    ***************************************************************************/
    
    void signal_handler_IO (int status)
    {
      READY = 1;
    //    printf("received SIGIO signal.\n");
    }
    
    void ser_puts(char *x)
    {
      int i;
      char buf[MAX_CHANS*2+2] = {0x7e, 0x80};        // Packet header SYNC byte plus address/control byte
      char *dest = buf+2;
      // Append dimmer byte values to the packet header
      // Bytes with special meaning 0x7d (Pad), 0x7e (Sync), 0x7f (Escape) are escaped by preceding with 0x7f
      for (i = 0; i < MAX_CHANS; i++) {
        switch (x[i]) {
        case 0x7f: *dest++ = 0x7f; *dest++ = 0x31; break;
        case 0x7e: *dest++ = 0x7f; *dest++ = 0x30; break;
        case 0x7d: *dest++ = 0x7f; *dest++ = 0x2f; break;
        default: *dest++ = x[i];
        }
      }
      while(!READY) ;                          // Wait until previous buffer has been sent
      READY = 0;
      write(serial, buf, dest - buf);          //write bytes to the port
    }
    
    void wait(unsigned msecs)
    {
     struct timespec req, rem;
      req.tv_sec = msecs/1000;
      req.tv_nsec = (msecs % 1000) * 1000000;
      while (nanosleep(&req, &rem) == -1) {
        if ((rem.tv_sec == 0) && (rem.tv_nsec < 1000)) return;
        req = rem;
      }
    }
    
    int min(int x, int y)
    {
      if (x < y) return x;
      return y;
    }
    
    
    void init_serial(void)
    {
      //open the device(com port) to be non-blocking (read will return immediately)
      serial = open(devicename, O_RDWR | O_NOCTTY | O_NONBLOCK);
      if (serial < 0) {
        perror(devicename);
        exit(-1);
      }
    
      //install the serial handler before making the device asynchronous
      saio.sa_handler = signal_handler_IO;
      sigemptyset(&saio.sa_mask);   //saio.sa_mask = 0;
      saio.sa_flags = 0;
      saio.sa_restorer = NULL;
      sigaction(SIGIO,&saio,NULL);
    
      // allow the process to receive SIGIO
      fcntl(serial, F_SETOWN, getpid());
      // Make the file descriptor asynchronous (the manual page says only
      // O_APPEND and O_NONBLOCK, will work with F_SETFL...)
      fcntl(serial, F_SETFL, FASYNC);
    
      tcgetattr(serial, &oldtio); // save current port settings 
      // set new port settings for canonical input processing 
      newtio.c_cflag = B57600 | CS8 | CLOCAL;
      newtio.c_iflag = IGNPAR;
      newtio.c_oflag = 0;
      newtio.c_lflag = 0;       //ICANON;
      newtio.c_cc[VMIN]=1;
      newtio.c_cc[VTIME]=0;
      tcflush(serial, TCIFLUSH);
      tcsetattr(serial, TCSANOW, &newtio);
    }
    
    void board_test(char *ch, int i)
    {
    
        // LEDs on first board
        ch[1]  = i;                       // Channel 1 -- sawtooth /|
        ch[4]  = 0xff - i;                // Channel 4 -- sawtooth |\ x
        ch[5] =  (i & 0x10) ? 0xff : 0;   // Channel 5 -- square wave 50/50 duty cycle 5Hz
        ch[6] =  (i & 0x30) ? 0 : 0xff;   // Channel 6 -- square wave 25/75 duty cycle 2.5Hz
    
        // LEDs on 2nd board do the same as first board
        ch[25]  = i;
        ch[28]  = 0xff - i;
        ch[29] =  (i & 0x10) ? 0xff : 0;
        ch[30] =  (i & 0x10) ? 0 : 0xff;
    
        // Test all PICs
        ch[0]  = (i & 0x10) ? 0xff : 0;   // Channel 0 of PIC 0 -- square wave 50/50 duty cycle 5Hz
        ch[8]  = (i & 0x20) ? 0xff : 0;   // Channel 0 of PIC 1 -- square wave 50/50 duty cycle 2.5Hz
        ch[16] = (i & 0x30) ? i : 0;      // Channel 0 of PIC 2 -- sawtooth + square wave 50/50 duty cycle 5Hz
        ch[9]  = 0xff - i;
        ch[17] =  (i & 0x10) ? 0xff : 0;
        ch[18] =  (i & 0x10) ? 0 : 0xff;
    }
    
    void blink_all(char *ch, int i)
    {
      int x;
    
      for (x = 0; x < 12; x++) {
        ch[x] = (i & 0x20) ? 0 : 0xff;
      }
    }
    
    
    void sequence_on_off(char *ch, int i)
    {
      ch[0]  = ch[1]  = (((i >> 2) % 6) != 0) ? 0 : 0xff;
      ch[2]  = ch[3]  = (((i >> 2) % 6) != 1) ? 0 : 0xff;
      ch[4]  = ch[5]  = (((i >> 2) % 6) != 2) ? 0 : 0xff;
      ch[6]  = ch[7]  = (((i >> 2) % 6) != 3) ? 0 : 0xff;
      ch[8]  = ch[9]  = (((i >> 2) % 6) != 4) ? 0 : 0xff;
      ch[10] = ch[11] = (((i >> 2) % 6) != 5) ? 0 : 0xff;
    }
    
    
    
    void sequence_pole_1_of_4(char *ch, int i)
    {
      ch[12] = (((i >> 2) % 4) != 0) ? 0 : 0xff;
      ch[13] = (((i >> 2) % 4) != 1) ? 0 : 0xff;
      ch[14] = (((i >> 2) % 4) != 2) ? 0 : 0xff;
      ch[15] = (((i >> 2) % 4) != 3) ? 0 : 0xff;
    }
    
    void sequence_pole_3_of_4(char *ch, int i)
    {
      ch[12] = (((i >> 2) % 4) == 0) ? 0 : 0xff;
      ch[13] = (((i >> 2) % 4) == 1) ? 0 : 0xff;
      ch[14] = (((i >> 2) % 4) == 2) ? 0 : 0xff;
      ch[15] = (((i >> 2) % 4) == 3) ? 0 : 0xff;
    }
    
    
    void sequence_white(char *ch, int i)
    {
      ch[16] = (((i >> 2) % 6) != 0) ? 0 : 0xff;
      ch[17] = (((i >> 2) % 6) != 1) ? 0 : 0xff;
      ch[18] = (((i >> 2) % 6) != 2) ? 0 : 0xff;
      ch[19] = (((i >> 2) % 6) != 3) ? 0 : 0xff;
      ch[20] = (((i >> 2) % 6) != 4) ? 0 : 0xff;
      ch[21] = (((i >> 2) % 6) != 5) ? 0 : 0xff;
    }
    
    
    
    void twinkle_white(char *ch, int x)
    {
      int i;
      for (i = 16; i < 22; i++) {
        if (rand() < RAND_MAX / 4) continue;
        ch[i] = rand() * (100.0 / RAND_MAX);
      }
    }
    
    
    void sequence_fade(char *ch, int deg)
    {
      int i;
      for (i = 0; i < 6; i++) {
        int deg_diff = min(abs(i * 60 - deg)*1.5, 90);
        int brightness = cos(deg_diff * M_PI / 180) * 0xff;
        ch[i*2] = ch[i*2+1] = brightness;
      } 
    }
    
    
    void binary_by_strand(char *ch, int i)
    {
      ch[0]  = ch[1]  = (i & 0x04) ? 0 : 0xff;
      ch[2]  = ch[3]  = (i & 0x08) ? 0 : 0xff;
      ch[4]  = ch[5]  = (i & 0x10) ? 0 : 0xff;
      ch[6]  = ch[7]  = (i & 0x20) ? 0 : 0xff;
      ch[8]  = ch[9]  = (i & 0x40) ? 0 : 0xff;
      ch[10] = ch[11] = (i & 0x80) ? 0 : 0xff;
    }
    
    void blue_green(char *ch, int i)
    {
      ch[0]  = 0xff;
      ch[2]  = 0xff;
      ch[4]  = 0xff;
      ch[6]  = 0xff;
      ch[8]  = 0xff;
      ch[10] = 0xff;
    
      ch[1]  = 0;
      ch[3]  = 0;
      ch[5]  = 0;
      ch[7]  = 0;
      ch[9]  = 0;
      ch[11] = 0;
    
    }
    
    void red_yellow(char *ch, int i)
    {
      ch[0]  = 0;
      ch[2]  = 0;
      ch[4]  = 0;
      ch[6]  = 0;
      ch[8]  = 0;
      ch[10] = 0;
    
      ch[1]  = 0xff;
      ch[3]  = 0xff;
      ch[5]  = 0xff;
      ch[7]  = 0xff;
      ch[9]  = 0xff;
      ch[11] = 0xff;
    }
    
    int main(int argc, char *argv[])
    {
      int i;
      char ch[MAX_CHANS];
    
      init_serial();
      memset(ch, 0, sizeof(ch));
      srand(time(0));
    while(1) {
      for (i = 0; i < 360; i += 10) {
        printf("%3d\r", i); fflush(stdout);
    
        blink_all(ch, i);
        sequence_pole_3_of_4(ch, i);
        twinkle_white(ch, i);
        ser_puts(ch);
        wait(100);
      }
    }
      printf("\nDone\n");
      memset(ch, 0, sizeof(ch));
      ser_puts(ch);
      wait(250);
    
      // restore old port settings
      tcsetattr(serial, TCSANOW,&oldtio);
      close(serial);        //close the com port
    
      return 0;
    }  //end of main

  4. #4
    Join Date
    Jul 2009
    Location
    Kansas City, KS
    Posts
    192
    Post Thanks / Like

    Default Re: Talking Serial to Renards

    I tried out the code with my SS8 and a strand of lights and it seems to work for me so far.

    Used a shell script to loop it over-and-over for about 20 minutes for a test,
    and the SS8 was 100% responsive with each start of the program, and followed the fade perfectly each time.

    That definitely sounds odd though.

    Mike.

  5. #5
    Join Date
    Dec 2010
    Location
    Oceanside, CA
    Posts
    2,345
    Post Thanks / Like

    Default Re: Talking Serial to Renards

    Quote Originally Posted by P. Short View Post
    Have you tried using a real serial port, rather than a USB port? I'm always suspicious that the baud rate coming from a USB dongle might not be that accurate.
    Great idea! Unfortunately for me my computers don't have real serial ports. Since you mentioned the USB dongles can be inaccurate, I decided to see if everything seemed fine with the USB->Serial device so I ran dmesg. It's a Belkin branded device which I always thought worked better than the knock-off usb dongles I have.

    dmesg shows many ugly things like this:
    Code:
    [1045042.980090] usb 2-6.4.1: Set LINE CTRL 0x7 failed (error = -110)
    [1046728.680064] mct_u232 ttyUSB0: Sending USB device request code 12 failed (error = -110)
    [1046971.168167] usb 2-6.4.1: Get MODEM STATus failed (error = -110)
    [1047289.168179] mct_u232 ttyUSB0: Sending USB device request code 11 failed (error = -110)
    [1048586.064110] usb 2-6.4.1: Set LINE CTRL 0x7 failed (error = -110)
    [1048623.428128] usb 2-6.4.1: Set LINE CTRL 0x7 failed (error = -110)
    [1049243.696140] mct_u232 ttyUSB0: Sending USB device request code 12 failed (error = -110)
    [1049356.704069] mct_u232 ttyUSB0: Sending USB device request code 11 failed (error = -110)
    [1049840.516149] mct_u232 ttyUSB0: Sending USB device request code 12 failed (error = -110)
    [1050295.128094] mct_u232 ttyUSB0: Sending USB device request code 11 failed (error = -110)
    Quote Originally Posted by frankv View Post
    Try this... it works for me.
    Thanks for that code. It seems to have about the same success rate (about 80% of the time the Renard SS8 works). I'm leaning toward usb->serial problems.

    Quote Originally Posted by mheuer View Post
    I tried out the code with my SS8 and a strand of lights and it seems to work for me so far.

    Used a shell script to loop it over-and-over for about 20 minutes for a test,
    and the SS8 was 100% responsive with each start of the program, and followed the fade perfectly each time.

    That definitely sounds odd though.

    Mike.
    Thanks for trying and letting me know it was working for you. I was also running a quick shell loop to see it run constantly:
    Code:
    while true; do ./test_renard; done
    After Phil's idea of the usb->serial port having a bad baud rate and then seeing the logs in dmesg, as well as seeing the same thing with frankv's code, and Mike saying it worked for him, I tried another usb->serial adapter I have (which is a knock-off from China), it seems to work fine.

    The Belkin one is identified as "ID 050d:0109 Belkin Components F5U109/F5U409 PDA Adapter" which uses the mct_u232 module. This was having troubles with reliability, and producing error logs.

    The working one is identified as "ID 067b:2303 Prolific Technology, Inc. PL2303 Serial Port" which uses the pl2303 module, and works great without error messages (over quite a long test run).

    So, it looks like there may be an issue with the mct_u232 module, or the dongle itself. Needless to say, my proof-of-concept test code is running with a 100% success rate now.

    Thanks to everybody for the help!
    [url]http://christmasonquiethills.com/[/url]
    [url]http://diychristmas.org/vb1/index.php[/url]

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •