0

Good day to everyone,

I am just starting to program for my research which needs to use a traverse mechanism (similar to a robotic arm which can only move in 3 axis X-Y-Z) connected by Serial Port (USB to RS232, using /dev/ttyUSB0) to the Computer. The OS of the computer is Linux 14.04 and for kernel reasons I can't use a more advance version, also I am using C language on the program for this traverse arm.

This code that I have been using has been developed since the year 2000, but on recent years this program hasn't been used. Ever since I started with this project I have encounter several issues, but in the end I always crash with the same wall, that is an eternal loop caused inside the program because the read() function of C can't proceed properly to read the data coming from the Serial Port.

/* For 232cOUT() and 232cIN() */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include "traverse.h"

rs232cIN(mojiin,status)
unsigned char *mojiin;
unsigned char *status;{
     int fdi,c,res,j,w;
     struct termios oldtio,newtio;
     struct termios oldscr;
     printf("ok0i\n");
     fdi = open(MODEMDEVICE, O_RDWR | O_NOCTTY ); 
     if (fdi <0) {perror(MODEMDEVICE); exit(-1); }

    tcgetattr(fdi,&oldtio); /* save current port settings --> For tty device */
    tcgetattr(1,&oldscr);  /* save current port settings --> For display device */
    printf("fdi=%d\n",fdi);
    printf("ok1i\n");
    bzero(&newtio, sizeof(newtio));
    newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD | CSTOPB;
    newtio.c_iflag = IGNPAR | ICRNL;  
    newtio.c_oflag = 0;
    printf("ok2i\n");
    /* set input mode (non-canonical, no echo,...) */
    newtio.c_lflag = 0;

    newtio.c_cc[VTIME]    = 5;   /* inter-character timer unused */
    newtio.c_cc[VMIN]     = 0;   /* blocking read until 5 chars received */

    tcflush(fdi, TCIFLUSH);
    tcsetattr(fdi,TCSANOW,&newtio);
    tcsetattr(1,TCSANOW,&newtio); /* stdout settings like modem settings */

    printf("ok3i\n");
    res = read(fdi,mojiin,256);   /* return after 5 chars have been input */        
    printf("res=%d\n",res);
     printf("ok4i\n");
    mojiin[res]=0;              /* so we can printf... */
    #if 1
         printf("Data import Good!!\n\r");
         printf("Imported-MOJIRETSU:%c\n\r",mojiin[0]);   
         printf("Imported-MOJISU:%d\n\r", res);   
     #endif
     status[0]=mojiin[0];       
     //sleep(1);       /*  this command is very important */
     usleep(SLEEPUSEC2);
     tcsetattr(fdi,TCSANOW,&oldtio);
     tcsetattr(1,TCSANOW,&oldscr);

     close(fdi); 
     }

Maybe this code is similar to others I have found on the web, the Open Port is also similar to this one but instead of read() function is with write().

The problem comes here at the read function, where using the current code I can't read any value coming from the RS232. The computer can send an ASCII code to the RS232 but when it need to receive, there is no return value, as an example of the program running is this code, The program runs several loops but the program gets stuck in loopin1.

The results of the read are res, MOJIRETSU and MOJISU. If you check the fdi=3 which means that it is reading something from the RS232 port but when it get to res, the value is 0 and MOJIRETSU doesn't show any value and MOJISU it is equal to 0. From MOJISU it creates the Status that later generates the Error on the Status flag.

==========  [Y_AXIS] Traversing START!! ========== 
Delta(Traverse) -->0.000000 
loopin1ok0o 
ok1o 
ok2o 
MOJISU=4 
ok3o 
     ZR 

MOJIOUT=ZR 
Sending OK 

ok4o 
ok0i 
fdi=3 
ok1i 
ok2i 
ok3i 
    res=0 
         ok4i 
             Data import Good!! 
Imported-MOJIRETSU: 
Imported-MOJISU:0 
status=0 
STATUS ERROR 0x0 !!!! 
STATUS FLAG=-4 
ok0o 
ok1o 
ok2o 
MOJISU=4 
ok3o 
     ZR 

MOJIOUT=ZR 
Sending OK 

ok4o 
ok0i 
fdi=3 
ok1i 
ok2i 
ok3i 
    res=0 
         ok4i 
             Data import Good!! 
Imported-MOJIRETSU: 
Imported-MOJISU:0 
status=0 
STATUS ERROR 0x0 !!!! 
STATUS FLAG=-4 
^C 

As you can see from the Loop, there is an error message that is the one causing that the same section repeats and repeats itself all the time. The weirdest thing is that if I nullify this error, it means that I just ignore the loop the error generates modifying other codes:The signal continues to other loops until it finally gets to move the traverse mechanism! Even though it still sends the exact same error as the loop is showing.

You may ask then, work without the loop, but the problem is that the error messages are the flags that indicate if the traverse mechanism is reaching a limit on the rail that it stands, and if it doesn't have the warning I might break the traverse mechanism! So definitely I need this feedback loop for its operation.

The system works in Non-canonical way and I have adjusted the VTIME to several values and doesn't change the status of the out come. If I change the VMIN then the program get stuck and I can only stop it if I close the terminal.

It is really strange that sending the error even without the loop, the traverse mechanism works. So really need advice if someone know how to operate the read() function so I can actually get values for res, MOJIRETSU y MOJISU and so operate the traverse mechanism without removing this feedback loop.

  • 1
    How old is this code? It's still using K&R function definition syntax! – Barmar Nov 08 '18 at 03:46
  • 1
    You said 2000, but that's like 1980's. – Barmar Nov 08 '18 at 03:48
  • What's causing the indentation before `res=0`, `ok4i`, and `Data import Good!!`? I don't see anything printing that whitespace. – Barmar Nov 08 '18 at 03:51
  • That function signature is very, very old school. What insight does a debugger have to offer? Have you tried the old method of spiking in a bunch of `printf` statements to inspect and log intermediate state? – tadman Nov 08 '18 at 04:24
  • Hi Barmar, I don't really understan how those spaces are generated in the results related with the `res=0` `ok4i`, and `Data import Good!!` I think it is not really important to the issue that I am having. – Gustavo Navas Nov 08 '18 at 04:34
  • And also I have searched for codes and I have seen it in some other webpages too... So it if it is older I don't really know. I think it can be one of the issues – Gustavo Navas Nov 08 '18 at 04:35
  • Hi tadman! Yes I have done `printf` in some of the parts like on the `tcsetattr(fdi,TCSANOW,&newtio);` section and also on the `res = read(fdi,mojiin,256);` and after this line is when it throws me the error message. So I am for sure that the problem is at the read() function. – Gustavo Navas Nov 08 '18 at 04:38
  • *"Maybe this code is similar to others I have found on the web"* -- If it is, then it's all bad code. Zeroing out the termios struct is bad coding practice. See [Setting Terminal Modes Properly](http://www.chemie.fu-berlin.de/chemnet/use/info/libc/libc_12.html#SEC237). Your assignments & comments for VMIN and VTIME seem reversed. As coded, it performs a pure timed read (that is returning 0 bytes) rather than *"return after 5 chars have been input"*. See https://stackoverflow.com/questions/25996171/linux-blocking-vs-non-blocking-serial-read/26006680#26006680 – sawdust Nov 08 '18 at 10:15
  • Hi Sawdust, thanks for you comment. I placed it as VMIN and VTIME reversed just as the last test I applied. Actually if I put a value on VMIN the program completely blocks as it doesn't read any bit. Respect on the first part, what do you refer with Zeroing the termios structure? Also the code use to work some years ago, because the traverse mechanism was used in previous research, more recently it hasn't been able to move with this code. – Gustavo Navas Nov 09 '18 at 00:54

1 Answers1

0

Your serial terminal initialization uses poor coding practices. Zeroing out the termios struct is not POSIX compliant.

what do you refer with Zeroing the termios structure?

Simply search for "zero" in your code, and you'll find the offending statement:

    bzero(&newtio, sizeof(newtio));

Refer to Setting Terminal Modes Properly and Serial Programming Guide for POSIX Operating Systems for proper methods of initializing a serial terminal.

Also the code use to work some years ago, because the traverse mechanism was used in previous research, more recently it hasn't been able to move with this code.

That's the problem with poorly written code. It's not portable, and may not work on a different system or a different day.


The problem comes here at the read function, where using the current code I can't read any value coming from the RS232.

Analysis is somewhat hampered by code that does not match the comments or your text descriptions.
But three problems with your code are obvious.

As already mentioned, the serial terminal initialization is not POSIX compliant.

The log indicates that you're cycling between some mystery output routine(s) and the input routine that you did post for review.
Each execution of the input routine performs an open(), initialization, and close() sequence on the serial terminal. Presumably the output routine (which has not been posted) performs a similar open(), initialization, and close() sequence on the serial terminal.

This repeated sequence of opening, initializing, and closing of the serial terminal is horribly inefficient and completely unnecessary with proper program design.
That's the second problem.

The third problem is a direct consequence of the previous problem.
As part of the initialization for reading, you perform an explicit discarding of any data that may be in the receive buffers:

    tcflush(fdi, TCIFLUSH);

Your subsequent read() (regardless how you configure VMIN and VTIME) can only return data that is received after the flush operation.
Lost to your program is any and all data that is received while the serial terminal is not open and/or before the flush operation.

If your program opened and initialized the serial terminal just once at its start for both reading and writing, this third problem would disappear.

sawdust
  • 16,103
  • 3
  • 40
  • 50