/* $modname$ $version$ $date$ $time$ REVISION HISTORY: $log$ */ /* ************************************************************* * * * WOODS HOLE OCEANOGRAPHIC INSTITUTION * * UPPER OCEAN PROCESSES GROUP * * PHYSICAL OCEANOGRAPHY DEPT. * * WOODS HOLE, MASSACHUSETTS USA 02543 * * * * File: serial.c * * * * Function: remote serial port setup routines * * * Project: * * * Programmer: G.A. * * * * Copyright (c) 2004 Woods Hole Oceanographic Institution * * * ************************************************************* */ #include #include #include #include #include #include #include #include "equates.h" #include "globals.h" #define uchar unsigned char #define _POSIX_SOURCE 1 /* POSIX compliant source */ const char *port[6] = {"/dev/ttyS0", "/dev/ttyS1", "/dev/ttyS32", "/dev/ttyS33", "/dev/ttyS34", "/dev/ttyS35" }; void flush_serial(int); /* Initialize a serial port - call with index of desired ttyS? device in port[] array i.e. to init /dev/ttyS32 in device array above, call init_serial(2) see these man pages for info about what's going on here: man 3 termios man 2 open */ int init_serial(char portnum) { /* Open serial (modem) device for reading and writing and not as controlling tty because we don't want to get killed if linenoise sends CTRL-C. */ fd_comms[portnum] = open(port[portnum], O_RDWR | O_NOCTTY | O_NONBLOCK); if (fd_comms[portnum] < 0) { perror(port[portnum]); exit(-1); } /* save current serial (modem) settings */ // tcgetattr(fd_comms,&oldtio); /* Set bps rate and hardware flow control and 8n1 (8bit,no parity,1 stopbit). Also don't hangup automatically and ignore modem status. Finally enable receiving characters. */ // newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD; newtio.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD; /* Ignore bytes with parity errors and make terminal raw and dumb. */ newtio.c_iflag = IGNPAR; /* Raw output. */ newtio.c_oflag = 0; /* Don't echo characters because if you connect to a host it or your modem will echo characters for you. Don't generate signals. */ newtio.c_lflag = 0; /* blocking read until 1 char arrives */ // newtio.c_cc[VMIN]=1; newtio.c_cc[VMIN]=0; // non-blocking read newtio.c_cc[VTIME]=0; /* now clean the modem line and activate the settings for modem */ tcflush(fd_comms[portnum], TCIFLUSH); tcsetattr(fd_comms[portnum],TCSANOW,&newtio); return(0); } /* NOTE: UNUSED - haven't needed this.... Check if there is IO pending. fd1 is file descriptor, tmout is time out in milliseconds, *buf is pointer to buffer to put stuff into, term is terminating character (so we know when we're done), *buflen is a pointer to int to hold length of received message */ int check_io(int fd1, int tmout, char *buf, char term, int *buflen) { int n = 0, i; char c; int done; struct timeval tv; fd_set fds; /* set timeout */ tv.tv_sec = tmout / 1000; tv.tv_usec = (tmout % 1000) * 1000L; /* zero counter and flag */ i = 0; done = 0; /* clear file descriptor set (do I need this?) */ FD_ZERO(&fds); /* watch fd1 (for read char availability) */ if (fd1 >= 0) FD_SET(fd1, &fds); else fd1 = 0; /* stay here til terminating char or timeout */ while (!done) { /* See if there is data to read */ if (select(fd1+1, &fds, NULL, NULL, &tv) > 0) { /* OK, put it in the buffer */ read(fd1, &c, 1); /* quit on ? */ if (c == term) // terminating character; i.e. CR, LF, ETX { done = 1; buf[i] = 0; } else { buf[i] = c; i++; } } else { buf[i] = 0; done = 1; } } /* end while */ /* return length of received message */ if (buflen) *buflen = i; /* unused */ return(n); } /* send a command; *bufptr is a pointer to the message to send */ short send_command(char *bufptr) { size_t count; /* figure out how many char in message */ count = strlen(bufptr); /* flush any garbage before new command */ usleep(100000); // insures we don't run into RS485 turnaround flush_serial(fd_comms[CONTROL_PORT]); /* send the message */ // write(fd_comms,bufptr,256); write(fd_comms[CONTROL_PORT],bufptr,count); /* OK */ return(0); } /* read the response, if any; *bufptr is a pointer to the buffer to hold the message, tmout is time out in milliseconds, term is terminating character (so we know when we're done), returns 0 if OK, -1 on timeout note: strips out \r and always inserts a \n at end of line */ short get_response(char *bufptr, char term, int tmout) { char c; int done; /* zero flag and global counter */ done = 0; msg_len = 0; /* check for I/O or timeout */ while (!done) { /* get a character */ if (read(fd_comms[CONTROL_PORT], &c, 1) > 0) { if (c == term) { /* we're done - first store \n */ *bufptr = '\n'; /* bump pointer */ bufptr++; /* bump length */ msg_len++; /* and terminate string */ *bufptr = 0; /* bump the message length to include terminator in count */ msg_len++; /* and set flag */ done = 1; } else if (c != '\r') { /* store the character (ignore CR) */ *bufptr = c; /* bump pointer */ bufptr++; /* bump length */ msg_len++; /* addition by fb: reset time-out counter */ tmout = 500; } } else { if (!tmout) { /* timeout...stuff a terminator just in case */ *bufptr = 0; /* and return a -1 */ return(-1); } else { /* sleep a msec */ usleep(200); /* and decrement timeout timer */ tmout--; } } } /* return OK */ return(0); } /* clear any remaining characters - this will not wait; just clears whatever is already there (if anything), then returns */ void flush_serial(int fd) { char c; /* clear it out */ while (read(fd, &c, 1)) ; } /* NOTE: this was used on Ed Sholkovitz's aerosol sampler project - keep as example command to remote device (pass thru maincpu board) port = main cpu comm port (INL, SMP, MET, XRF, XPC, WND, RFL) command = remote device command string delay = wait in msec for response to command term = terminator character (i.e. \r or \n or whatever; use 0 to just timeout after additional 1000 msec bufptr = buffer to store returned message returns: 0 on success, 1 on NO RESPONSE from remote device, 2 on NO RESPONSE from Main CPU */ short passthru(char *port, char *command, short delay, char term, char *bufptr) { char cmndbuf[256]; /* wait ... */ usleep(250000); /* make up the full command and send it */ sprintf(cmndbuf,"\rS%s%s\r",port,command); send_command(cmndbuf); /* get the acknowledgement response CRLF */ get_response(bufptr,'\n',1000); /* wait 'delay' msec for response from remote device */ usleep(delay * 1000); /* request whatever is in the 'response' buffer */ send_command("\rRMSG\r"); /* make sure input buffer is "string clear" */ *bufptr = 0; /* get the response and put into the working input buffer */ if (get_response(bufptr,term,1000)) /* no response from Main CPU */ return(2); else { /* is the response a 'NO RESPONSE' message from the Main CPU? i.e. no response from remote device */ if (*bufptr == 'N' && *(bufptr + 1) == 'O') /* remote device didn't respond... */ return(1); else /* got something - OK to continue... */ return(0); } }