PDA

View Full Version : Leggere dati binari da porta seriale


hwmaniac
01-12-2005, 14:23
Sto lavorando per la mia tesi con un ricevitore GPS garmin il quale lavora con un formato proprietario (non NMEA).
So come decodificare i dati ma ho qualche problema con i dati che ricevo tramite seriale (checksum errati)... probabilmente c'è qualcosa di sbagliato nel programma di acquisizione (scritto in C).

Qualcuno potrebbe dirmi che comando o programma posso usare sotto Linux per acquisire su un file i dati binari (l'intero stream di dati) che il GPS mi invia tramite porta seriale?
I settaggi della porta dovrebbero essere 9600 8N1 controllo di flusso hardware.
Ho provato a smanettare con Minicom ma non sembra fare al caso mio (il file che mi crea è vuoto)

Qualche anima pia potrebbe aiutarmi?

ilsensine
01-12-2005, 14:53
minicom non credo consenta lo scambio di dati generici, ma supporta alcuni protocolli (ad es. xmodem, zmodem ecc.). Quindi il programma custom è la soluzione più pratica. Se puoi allegare la parte rilevante ci do una occhiata.

hwmaniac
01-12-2005, 15:00
minicom non credo consenta lo scambio di dati generici, ma supporta alcuni protocolli (ad es. xmodem, zmodem ecc.). Quindi il programma custom è la soluzione più pratica. Se puoi allegare la parte rilevante ci do una occhiata.

Grazie per l'aiuto :)

Di seguito trovi parte del programma che si occupa dell'acquisizione.

Considera che non l'ho scritto io e che a livello di programmazione C (soprattutto programmazione su seriale) sono una pippa.

int acquire(int argc, char *argv[])
{
char *ComPort;
char c;
int fd;
char FILENAME[40]="";
char STRINGDATE[40]="";


ComPort="/dev/ttyS0";
fd=initCOM(ComPort);//set the serial port for binary com.with Garmin receiver

if (argc<3)
{
printf("Inserisci ID STAZIONE E CODICE STRUMENTO\n");
printf("ESEMPIO: ./acquire 01 G\n");
exit(1);
}

if (strlen(argv[1])!=2)
{
printf("L'ID STAZIONE deve essere di DUE CIFRE!\n");
exit(1);
}
if (strlen(argv[2])!=1)
{
printf("Il CODICE STRUMENTO è un solo CARATTERE\n");
exit(1);
}

do
{
t1=time(NULL);
ptr=gmtime(&t1);

stpcpy(STRINGDATE,"");
stpcpy(FILENAME,"");
strftime(STRINGDATE,40,"%d%m%y%H%M.sv",ptr);
strcat(FILENAME,"./");
strcat(FILENAME,argv[1]);
strcat(FILENAME,argv[2]);
strcat(FILENAME,STRINGDATE);

lg=fopen(FILENAME,"w+");
do
{
read(fd,&c,sizeof(c));
fwrite(&c,1,1,lg);
//printf("%d ",c);
t2=time(NULL);
}
while((difftime(t2,t1))<3600); //legge dalla porta seriale per un ora(3600 secondi)
fclose(lg);

}
while(1);

close(fd);

return(0);
}

//******************************************************************************************************
//******************************************************************************************************

int initCOM(char *comName)
{
int fd1;
int n=0;

fd1=open(comName,O_RDWR | O_NOCTTY | O_NONBLOCK);

if (fd1==-1)
{
printf("I CAN'T OPEN THE PORT\n\n");

exit(1);
}
else
{
printf("I OPEN THE PORT\n\n");
printf("TRANSFERT ACTIVE, i'm logging.....\n\n");
printf("per terminare CTRL+C\n");
fcntl (fd1, F_SETFL, 0);
setCOM(fd1);
ioctl(fd1, FIONREAD , &n);
}
return(fd1);
}

//******************************************************************************************************

void setCOM(int fd)
{
tcgetattr(fd, &oldoptions); //retrieves the serial port setup

// baud = 9600 bps
// dati,parita',bit_stop = 8,n,1
// no handshake
// flow control: hardware (RTS-CTS)

cfsetispeed(&oldoptions,B9600); //input baud rate 9600bps
cfsetospeed(&oldoptions,B9600); //output baud rate 9600bps
oldoptions.c_cflag |=(CLOCAL|CREAD); //programma non master della COM
oldoptions.c_cflag &= ~CSIZE; //maschera per modificare i bit
oldoptions.c_cflag |= CS8; //8 data bits
oldoptions.c_cflag &= ~PARENB; //no parity
oldoptions.c_cflag &= ~CSTOPB; //1 stop bit
oldoptions.c_cflag |= CRTSCTS; //flow-control RTS-CTS (hardware)
oldoptions.c_cc[VMIN]=15; //it is active after 15 char
oldoptions.c_lflag &= ~(ICANON | ECHO | ISIG ); //raw input mode
oldoptions.c_oflag &= ~ OPOST; //output processed in hardware mode
tcsetattr(fd,TCSANOW, &oldoptions); //saves setup
}

Riesco ad acquisire i dati e anche a decodificarli, il problema è che il checksum non è corretto (fai conto cmq che i dati una volta decodificati rimangono plausibili)

Non c'è un comando sotto linux per prendere tutto quello che viene inviato da un dispositivo tramite seriale e redirigerlo su un file?

ilsensine
01-12-2005, 15:30
Cambia qualcosa se modifichi il ciclo di acquisizione in questa maniera?

do {
struct pollfd pfd;
pfd.fd = fd;
pfd.events = POLLIN;
if(poll(&pfd, 1, -1)!=1) abort();
if(read(fd,&c,sizeof(c))!=sizeof(c)) abort();
fwrite(&c,1,1,lg);
//printf("%d ",c);
t2=time(NULL);
} while((difftime(t2,t1))<3600);

nb devi includere anche <sys/poll.h> all'inizio del file.

hwmaniac
01-12-2005, 16:59
Cambia qualcosa se modifichi il ciclo di acquisizione in questa maniera?

do {
struct pollfd pfd;
pfd.fd = fd;
pfd.events = POLLIN;
if(poll(&pfd, 1, -1)!=1) abort();
if(read(fd,&c,sizeof(c))!=sizeof(c)) abort();
fwrite(&c,1,1,lg);
//printf("%d ",c);
t2=time(NULL);
} while((difftime(t2,t1))<3600);

nb devi includere anche <sys/poll.h> all'inizio del file.

Riesco ad acquisire i dati ugualmente e il file viene creato come prima.
apparentemente non cambia nulla quindi credo che le nuove condizioni siano soddisfatte (anche se non so a cosa servano di preciso).

ilsensine
01-12-2005, 17:09
Il programma originale ha un sospetto bug sulla read, in quanto il device è aperto come non bloccante (e la read ritorna immediatamente se non ci sono dati). Ho semplicemente messo una condizione per attendere l'arrivo effettivo di dati.

A parte questo, il processo di lettura è grosso modo corretto, credo proprio che leggi quello che viene ricevuto.

hwmaniac
01-12-2005, 17:24
Il programma originale ha un sospetto bug sulla read, in quanto il device è aperto come non bloccante (e la read ritorna immediatamente se non ci sono dati). Ho semplicemente messo una condizione per attendere l'arrivo effettivo di dati.

A parte questo, il processo di lettura è grosso modo corretto, credo proprio che leggi quello che viene ricevuto.

grazie comunque :)

vorrei averne conferma utilizzando un altro sistema o programma per memorizzare quello che ricevo sulla seriale. Sai cosa posso usare sotto linux?

ilsensine
01-12-2005, 17:28
Se la seriale è impostata correttamente (ad es. con minicom, chiudendolo senza effettuare il reset della porta), un semplice "cat /dev/ttyS0 > file.bin" dovrebbe bastare. Ho il dubbio che non funzioni con crtscts attivato, ma provare non costa nulla.

hwmaniac
01-12-2005, 17:48
Se la seriale è impostata correttamente (ad es. con minicom, chiudendolo senza effettuare il reset della porta), un semplice "cat /dev/ttyS0 > file.bin" dovrebbe bastare. Ho il dubbio che non funzioni con crtscts attivato, ma provare non costa nulla.

funziona e ottengo gli stessi dati (con checksum errato).

Possibile a questo punto che dipenda dal fatto che imposto male la seriale?

ilsensine
01-12-2005, 17:52
Dubito...

hwmaniac
01-12-2005, 18:16
Se la seriale è impostata correttamente (ad es. con minicom, chiudendolo senza effettuare il reset della porta), un semplice "cat /dev/ttyS0 > file.bin" dovrebbe bastare. Ho il dubbio che non funzioni con crtscts attivato, ma provare non costa nulla.

E se invece volessi inviare una stringa alla seriale come la stringa "$PGRMC1,,2,,,,,,<CR><LF>" oppure una stringa esadecimale come "10 0A 02 26 00 CE 10 03" che comando dovrei usare?

ilsensine
02-12-2005, 08:18
echo -n <stringa> > /dev/ttyS1
Per i caratteri speciali (cr, lf) oppure per i codici binari diretti, puoi usare echo -n -e utilizzando i caratteri di escape o i codici in ottale. man echo spiega la sintassi.