OpenWrt Forum Archive

Topic: Serial port cannot read anything, RX pin seems Vdd level?

The content of this topic has been archived on 11 Mar 2018. There are no obvious gaps in this topic, but there may still be some posts missing at the end.

I am trying to read serial data which is send by a uC using /dev/tts/0 on the WL-500gP but I don't get anything. I keep on receiving the EAGAIN error (device temporarily unavailable or something like that)

I've written a C program to acces the serial port. Writing to the device goes fine but I don't receive anything.

If I measure the RX line the voltage is nearing the Vdd level 2.9V. I made a levelshifter from 5V (uC) to 3.3V (router) but it seems the RX (input of the router) can't be pulled to gnd... ?! Anyone having the same problem?

I do set the CREAD flag by the way, and I tried several simple reads in the shell

... some other defines ...

#define DEVICE "/dev/tts/0"
#define BAUDRATE B9600

/* Global filediscriptor */
int fd=0;

int initport(void) {
    struct termios options;
    // Get the current options for the port...
    tcgetattr(fd, &options);
    // Enable the receiver and set local mode...
    options.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD; // 8N1
    options.c_iflag = ICRNL;
    options.c_oflag = 0;
    options.c_lflag = ICANON;
    tcflush(fd, TCIFLUSH);
    // Set the new options for the port...
    tcsetattr(fd, TCSANOW, &options);
    return 1;
}

int write_char(char *chars, int len) {
    chars[len] = 0x0d; // stick a <CR> after the command
    chars[len+1] = 0x00; // terminate the string properly
    int n = write(fd, chars, strlen(chars));
    if (n < 0) {
        fputs("write failed!\n", stderr);
        return 0;
    }
    printf("written:%s\n", chars);
    return 1;
}

int read_char(char *result) {
    int n = read(fd, result, 254);
    result[n-1] = 0x00;
    if (n<0) {
        if (errno==EAGAIN) {
            printf("SERIAL EAGAIN ERROR\n");
            return 0;
        }
        else {
            printf("SERIAL read error %d %s\n", errno, strerror(errno));
            return 0;
        }
    }
    return 1;
}

... some other functions ...

int main(int argc, char **argv) {
    
    /* Open device port with corresponding options */
    fd = open(DEVICE, O_RDWR | O_NOCTTY | O_NDELAY);
    if (fd==-1) {
        perror("Port: Unable to open\n");
        return 1;
    }
    else {
        fcntl(fd, F_SETFL, 0);
    }

    /* Initialize the serial port */
    initport();

    // Send things

    // Receive things

    // etc...

    return 0;

}

(Last edited by casio on 20 Aug 2007, 20:10)

I think, measuring the data lines with a voltmeter is useless, since it does display an average level over a more or less long period of time.
To see what's going on, you have to use an oscilloscope.
Do you measure the unconnected RX line on the router, or when it is connected?
On a regular RS-232C interface, logic zeros are positive and logic ones are negative voltage. If TTL levels behave similar, and logic zeros are 3V and logic ones are 0V, the data lines will normally be 3.3V or 5V, since it does represent a logic 0.

I did measured it with an oscilloscope, but the way I measure it makes no difference using a voltage meter or an scope. I measure the level of the RX0 pin (of the router) when it's not connected, floating. RX1 is showing the same result...

Serial TTL levels compared to RS232 Levels:
* low level = +5V...+15V for the RS232 output and 0...+0.4V for the TTL output
* high level = -5V...-15V for the RS232 output and +2.4V...+5V for the TTL output

But I would not put 5V (from the uC) at the input, it's only degrading its life time since the UART (of the router) works at 3.3V. I used a simple NPN and some resistors. An even more simple trick is to use 2 or 3 1N4148 in serie which have a minimum voltage drop of 0.6V to reduce the voltage level.

Back to the source code
Using this code

int n = read(fd, result, 254);

gives n=-1 (length) and the errno==EAGAIN error which means device temporarily unavailable according to the strerror(errno). I also used smaller values than 254 for instance 2 or even 1... but all the same result. It could also be that the UART expects an string termination or simething like that. Is there a flag to not wait for this. Before I read the port I use this code

fcntl(fd, F_SETFL, FNDELAY);

to not block the serial port from reading... If I don't use it before the read function the program waits forever...

Maybe I set the wrong flags to initialize the serial port in a wrong state??? But it seems ok to me since I can transmit correct... :-) Did anyone got the serial read working on a Wl-500gP?

I am using the serial input to send the touch screen coordinates from a uC to the router. I use the touch screen data to controle mpd but I can't do much knowing that the serial port can only send (at the moment) ;-)

(Last edited by casio on 21 Aug 2007, 09:51)

I found something about the EAGAIN error...

[EACCES] or [EAGAIN]
    The cmd argument is F_SETLK; the type of lock (l_type) is a shared (F_RDLCK) or exclusive (F_WRLCK) lock and the segment of a file to be locked is already exclusive-locked by another process, or the type is an exclusive lock and some portion of the segment of a file to be locked is already shared-locked or exclusive-locked by another process.

Can it be that some system log-ish process is claiming the device?

(Last edited by casio on 22 Aug 2007, 21:57)

edit /etc/inittab to remove the login process for the desired serial port

I removed the line where the /dev/tts/0 is used for logging in the /etc/inittab file... Still no success!? :-(

Did anyone successfully read data from the serial port which is present on the router (WL-500gP) so not using the USB to serial cable (FDTI or similar)?

Since by default the port works as a console you need some magic to get character input without waiting for an Enter Key.

here is a snippet that bypasses I/O buffer waiting for "enter key" and also does demo of non-blocking read, i.e. if nothing there skip...


#include <stdlib.h>
#include <stdio.h>
#include <sys/io.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/timeb.h>
#include <time.h>
// these make the serial port work!
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>

int serport;    //the device for I/O
int mybuff ='?';   //the input character from serial port

//setup serial port for character I/O
int initport (char *deviceName){
int fd;
    struct termios options;
    serport = open(deviceName, O_RDWR | O_NOCTTY | O_NDELAY);
#ifdef DESKTOP
    if (serport == -1) {
        perror("open_port fail: ");
        perror(DEVICE_NAME);
        return -1;
    }
#endif
    //Next line not liked by  gcc so we get existing one 
    //bzero(&options,sizeof(options));
    fd = serport;
    tcgetattr(fd,&options);
    options.c_cflag = (B2400 |CRTSCTS |CS8| CLOCAL |CREAD);
    // options.c_cflag &= ~PARENB;
    // options.c_cflag &= ~CSTOPB;
    // options.c_cflag &= ~CSIZE ;
    options.c_iflag |= IGNPAR ;
    options.c_oflag = 0;
    //options.c_oflag = options.c_iflag;
    options.c_lflag = 0 ;    //no echo, no buffer till ENTER key
    options.c_cc[VMIN] = 0;  // don't wait for any characters
    options.c_cc[VTIME] = 0; // no intercharacter delay
    tcflush(fd,TCIFLUSH);
    tcsetattr(fd,TCSANOW,&options);
    fcntl(fd,F_SETFL,FNDELAY);    //nonblocking
    cfsetispeed(&options, B2400);
    cfsetospeed(&options, B2400);
    return serport;
}    
// End initport

//readsch reads none, or one waiting characters
int readsch(int fd){
    int readError = 0;
    char portbuff[200];
     
    readError = read(fd,portbuff,1);
    readError-- ;    // so we can have nulls
    if (readError >= 0) 
        readError = portbuff[0];
    return readError;
}
// End readsch

int writesch(int fd, char chtosend){
    int writeError = 0;
    char portbuff[200]; 
    portbuff[0] = chtosend;
    writeError = write(fd,&portbuff,1);
    return writeError;
}
// End writesch

int writesstr(int fd, char * strtosend){
    int writeError = 0;
    int sendLen = strlen(strtosend); 
    if (strtosend[sendLen-1] =='\0')
        sendLen--;
    writeError = write(fd,strtosend,sendLen);
    return writeError;
}
// End writesstr


//now use the readsch in a timed loop somehow...

                 // Continue user entry
                mybuff = readsch(serport);
                if (mybuff < 0){
                    
                 // do whatever happens when there is nothing to read    
                } else {
                //some user entry
                    
                        // do stuff with contents of mybuff

                                  }

This code works on a Geode running Pyramid Linux and also on Ubuntu laptop.

(Last edited by wot4g on 18 Sep 2007, 18:34)

The discussion might have continued from here.