#271308 - 02/12/2005 17:40
Serial Port Programming Question
|
veteran
Registered: 21/01/2002
Posts: 1380
Loc: Erie, CO
|
I'm writing the serial port interface for Empire for use with a BT-Serial port adapter, and I'm running into a problem receiving the data on the Empeg side. I've got the Palm part running, the Palm sends data in sets of 8 characters, it looks like this: Code:
BEG__ENQ 000001B4 00000E92 00000E93 00000E94 END__ENQ
I send the data using sockets (over Bluetooth) but it is straightforward. The data is copied into a buffer using StrCopy: Code:
if(btBeamPosition == -1) { switch(palantirSave->uploadMode) { case UPLOAD_MODE_INSERT: StrNCopy(btData, "BEG__INS", 8); break; case UPLOAD_MODE_APPEND: StrNCopy(btData, "BEG__APP", 8); break; case UPLOAD_MODE_REPLACE: StrNCopy(btData, "BEG__REP", 8); break; case UPLOAD_MODE_ENQUEUE: StrNCopy(btData, "BEG__ENQ", 8); break; } } else if(btBeamPosition == playlistCount) { switch(palantirSave->uploadMode) { case UPLOAD_MODE_INSERT: StrNCopy(btData, "END__INS", 8); break; case UPLOAD_MODE_APPEND: StrNCopy(btData, "END__APP", 8); break; case UPLOAD_MODE_REPLACE: StrNCopy(btData, "END__REP", 8); break; case UPLOAD_MODE_ENQUEUE: StrNCopy(btData, "END__ENQ", 8); break; } } else { tItem = DmQueryRecord(palantirDB, playlist[btBeamPosition]); tRecPacked = (TrackRecordPacked *) MemHandleLock(tItem); MemHandleUnlock(tItem); StrIToH(btData, tRecPacked->fid); } btData[8] = chrLineFeed; //0x0A //btData[8] = chrCarriageReturn; //0x0C btData[9] = chrNull; //0x00 if((err = BtLibSocketSend(btLibRefNum, btSocket, (UInt8*)btData, 10)) != btLibErrPending) FrmCustomAlert(BtErrorAlert, "BtBeamPlaylist()\n", "Error on data send\n", StrIToA(charIntDebug, err));
The code that I am using to receive (serialtest.c) is attached, important is the open: Code:
if((serial_fd = open("/dev/ttyS1", O_NOCTTY | O_RDWR | O_NONBLOCK)) > -1) { //install the signal handler before making the device asynchronous saio.sa_flags = 0; saio.sa_restorer = NULL; saio.sa_handler = signal_handler_IO; sigemptyset(&saio.sa_mask); sigaction(SIGIO, &saio, NULL);
//allow the process to receive SIGIO fcntl(serial_fd, F_SETOWN, getpid()); fcntl(serial_fd, F_SETFL, FASYNC);
bzero(&termios, sizeof(termios)); termios.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD; termios.c_iflag = ICRNL; termios.c_oflag = 0; termios.c_lflag = ICANON; termios.c_cc[VMIN] = 1; termios.c_cc[VTIME] = 0; tcflush(serial_fd, TCIFLUSH); if(tcsetattr(serial_fd, TCSANOW, &termios) > -1) ret = 1; }
And the code to read: Code:
void BT_HandleInput(void) { int len; char buffer[255]; if(wait_flag == 0) { len = read(serial_fd, buffer, 255); buffer[len] = 0x00; fprintf(stderr, "line[%d]: %s", len, buffer); wait_flag = 1; /* wait for new input */ } }
Now, everything works fine if I run serialtest on the Empeg, connect to the Bluetooth adapter via my laptop and use Hyperterminal to send lines over. The output comes out fine. The output is also coming across ok if I cat /dev/ttyS1 on the Empeg and use my Palm to beam over a playlist. I get the entire list printed out on the screen. What isn't working correctly is if I use my Palm to beam a playlist and read it with serialtest. I get the following: Code:
sig:sig:line[9]: BEG__ENQ
Which makes me wonder.. I am getting two signals for input on the serial port? Why don't I get the next line? If I send slowly (by pausing a bit between sends on the Palm side) I get something different: Code:
sig:line[9]: BEG__ENQ sig:line[10]: sig:line[10]: sig:line[10]: sig:line[10]: sig:line[10]:
Help?
Attachments
271304-serialtest.c (435 downloads)
|
Top
|
|
|
|
#271309 - 02/12/2005 17:48
Re: Serial Port Programming Question
[Re: cushman]
|
carpal tunnel
Registered: 29/08/2000
Posts: 14493
Loc: Canada
|
What is producing the "sig:" strings ?
|
Top
|
|
|
|
#271310 - 02/12/2005 18:50
Re: Serial Port Programming Question
[Re: mlord]
|
veteran
Registered: 21/01/2002
Posts: 1380
Loc: Erie, CO
|
I'm using signals to let me know when data is available on the serial port, I register a signal handler like this: Code:
saio.sa_flags = 0; saio.sa_restorer = NULL; saio.sa_handler = signal_handler_IO; sigemptyset(&saio.sa_mask); sigaction(SIGIO, &saio, NULL);
And the signal handler is this: Code:
void signal_handler_IO(int status) { fprintf(stderr, "sig:"); wait_flag = 0; }
The full source to the listening program is attached above. I want to make sure I'm actually SENDING things right first, the sending code looks OK, doesn't it? 8 bytes of text followed by a 0x0D (LF) character then a 0x00 (NULL) to terminate the string? If that is right, I must be doing something wrong on the reading side, and I can concentrate on that part of it. I think I have the serial port options all correct, but I'm obviously missing something here.
|
Top
|
|
|
|
#271311 - 03/12/2005 02:51
Re: Serial Port Programming Question
[Re: cushman]
|
veteran
Registered: 21/01/2002
Posts: 1380
Loc: Erie, CO
|
I've stripped down the serial port "echo" program I wrote to this: Code:
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <termios.h>
#define BAUDRATE B115200 #define PORT "/dev/ttyS1"
int main(int argc, char **argv) { int serial_fd; //serial port file descriptor int len; char buffer[255]; struct termios termios; if((serial_fd = open("/dev/ttyS1", O_NOCTTY | O_RDWR)) > -1) { bzero(&termios, sizeof(termios)); termios.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD; termios.c_iflag = ICRNL; termios.c_oflag = 0; termios.c_lflag = ICANON; termios.c_cc[VMIN] = 1; termios.c_cc[VTIME] = 0; tcflush(serial_fd, TCIFLUSH); if(tcsetattr(serial_fd, TCSANOW, &termios) > -1) { while(1) { len = read(serial_fd, buffer, 255); buffer[len] = 0x00; fprintf(stderr, "line[%d]: %s", len, buffer); } } else { fprintf(stderr, "error setting termios"); } } return 0; }
And I still get the same output when beaming from my Palm program.
|
Top
|
|
|
|
#271312 - 03/12/2005 02:54
Re: Serial Port Programming Question
[Re: cushman]
|
veteran
Registered: 21/01/2002
Posts: 1380
Loc: Erie, CO
|
The output is this: Code:
empeg:/# serialtest line[9]: BEG__ENQ line[10]: line[10]: line[10]: line[10]: line[10]: empeg:/#
Again, this works normally if I either cat /dev/ttyS1 and send the playlist from the Palm or if I type characters via a Hyperterminal to the above program. It's just beaming to the program above that is somehow "missing" the LFs?
|
Top
|
|
|
|
#271313 - 04/12/2005 00:26
Re: Serial Port Programming Question
[Re: cushman]
|
veteran
Registered: 21/01/2002
Posts: 1380
Loc: Erie, CO
|
I modified my program a bit to read using a non-canonical method, one byte at a time. I made it print out each character and the hex value, so I know now that my Palm program is outputting the correct sequence of characters, I just can't figure out why I can't use a canonical input method to read the same data. Here is the output now: Code:
char: B hex: 42 char: E hex: 45 char: G hex: 47 char: _ hex: 5f char: _ hex: 5f char: E hex: 45 char: N hex: 4e char: Q hex: 51 char: hex: a char: hex: 0 char: 0 hex: 30 char: 0 hex: 30 char: 0 hex: 30 char: 0 hex: 30 char: 0 hex: 30 char: 1 hex: 31 char: B hex: 42 char: 4 hex: 34 char: hex: a char: hex: 0 char: E hex: 45 char: N hex: 4e char: D hex: 44 char: _ hex: 5f char: _ hex: 5f char: E hex: 45 char: N hex: 4e char: Q hex: 51 char: hex: a char: hex: 0
So this proves that my output from the Palm is OK, I think. I can code my program to read the input in a non-canonical way, character by character, but this seems like a bad way to write it if I could do line-based processing instead. Any ideas as to how to make this program process the input line-by-line instead of by character?
|
Top
|
|
|
|
#271314 - 04/12/2005 01:03
Re: Serial Port Programming Question
[Re: cushman]
|
carpal tunnel
Registered: 29/08/2000
Posts: 14493
Loc: Canada
|
unsigned char buf[128], c, i = 0;
while (i < (sizeof(buf)-1) && 1 == read(fd, &buf+i, 1) && buf[i++] != '\n');
|
Top
|
|
|
|
#271315 - 04/12/2005 01:16
Re: Serial Port Programming Question
[Re: mlord]
|
veteran
Registered: 21/01/2002
Posts: 1380
Loc: Erie, CO
|
Hehe, I guess what I was asking is if there was a way to force the serial port open into canonical mode processing, but I guess I'll do it char by char. I probably would have done something similar to your code there, but in 20 lines instead of 1. A design question: Currently I have Empire running as a daemon, listening to the IrDA port for input. When it detects input on the IrDA port, it reads the IrDA input into a buffer, then forks to allow a thread to process that input while the main process keeps listening to the IrDA port for more input. This allows the forked process to take it's time inserting tunes into the playlist because the player crashes if you try to add too many tunes to the playlist at one time. Now that I must listen not only to the IrDA port but the serial port for input too, should I fork a new main process to listen to the serial port for input, or should I try to integrate the serial port listener in with the IrDA listener? The IrDA listener portion blocks for a fraction of a second, so I'd have to do some gymnastics to get around it, but it could be done. The easiest way would be to fork a serial port listening thread, and have it fork it's own sub-processes to handle input when it deems the time is right (it has gotten a complete serial port message).
|
Top
|
|
|
|
#271316 - 04/12/2005 01:23
Re: Serial Port Programming Question
[Re: cushman]
|
carpal tunnel
Registered: 29/08/2000
Posts: 14493
Loc: Canada
|
Your choice. The kernel will buffer input from either device until your app reads it, so no worries about dropping chars unless it gets *really far* behind.
Most people would probably use select() (or poll()) to wait on both fds simultaneously from a single thread.
Cheers
|
Top
|
|
|
|
#271317 - 04/12/2005 01:29
Re: Serial Port Programming Question
[Re: mlord]
|
veteran
Registered: 21/01/2002
Posts: 1380
Loc: Erie, CO
|
Quote: Most people would probably use select() (or poll()) to wait on both fds simultaneously from a single thread.
Thanks, Mark. I would use select, but I'm using OpenOBEX libraries to get the data from the IrDA port - no fd to listen to there. I'll integrate it into one thread and see how the performance is, then go from there. Thanks for the help!
|
Top
|
|
|
|
#271318 - 04/12/2005 01:41
Re: Serial Port Programming Question
[Re: cushman]
|
carpal tunnel
Registered: 29/08/2000
Posts: 14493
Loc: Canada
|
Quote: I'm using OpenOBEX libraries to get the data from the IrDA port - no fd to listen to there
Sure there is. There MUST be an fd in there somewhere, as it's the only way to do a read(), and read() is the only way to get IrDA data.
Cheers
|
Top
|
|
|
|
#271319 - 04/12/2005 01:59
Re: Serial Port Programming Question
[Re: mlord]
|
veteran
Registered: 21/01/2002
Posts: 1380
Loc: Erie, CO
|
Buried in the OpenOBEX library, though. I loop on a function called OBEX_HandleInput, I could rewrite it to do what I want I guess. I'll do quick and dirty first (hack), then clean it up if I can.
|
Top
|
|
|
|
|
|