Newsgroup: it.comp.os.linux.development
Titolo: programmazione della porta seriale

Salve a tutti,
premetto che sono abbastanza principiante.
Mi trovo a dover controllare un dispositivo (irobot roomba) tramite
porta seriale. Se qualcuno fosse interessato, le specifiche del
protocollo che utilizza sono disponibili qui:
http://www.irobot.com/images/consumer/hacker/Roomba_SCI_Spec_Manual.pdf
Da qualche giorno provo a guardare tutorial e howto sulla
programmazione della seriale, ma non sono riuscito ancora ad ottenere
niente...
Al momento attuale, il mio obiettivo è realizzare un programma (in C o
in C++), che mi permetta di spedire alla seriale ttyS0 la seguente
sequenza di valori:
128
130
132
131
135
I valori che ho elencato sono decimali; i corrispondenti valori
esadecimali (se serve...) sono 80, 82, 84, 83, 87.
Per quanto riguarda la comunicazione seriale:
baud rate: 57600
data bit: 8
parity: none
stop bits: 1
flow control: none
Qualcuno riesce a darmi una mano?
grazie
L

Ti posso dire le risorse che mi sono state utili quando ho dovuto affrontare 
questo problema un po' di tempo fa (tra l'altro puoi googolare le mie 
domande proprio su questo ng, penso circa un anno fa):
- 
http://www.easysw.com/~mike/serial/serial.html
 : parecchie info utili e 
pezzetti di codice (c'e' molto ma non tutto)
- il codice sorgente di cutecom: e' in C++, ma la parte di settaggi della 
seriale e' C, un buon esempio di come suare tutte quelle funzioni arcane
- Stevens, "Advanced programming in the UNIX environment": qui c'e' _tutto_ 
, in ogni minimo dettaglio di tutte le opzioni: per me e' stata la bibbia, 
quando ogni altra risorsa falliva lui aveva sempre la risposta giusta
Se hai ancora problemi potrei recuperare qualche pezzo di codice che ho 
scritto un anno fa (non l'ho piu' toccatod a allora perche' ho avuto altro 
da fare... ma allora funzionicchiava :)
Pleg

 questo problema un po' di tempo fa
grazie mille delle dritte!
allora, ho iniziato a scrivere il codice.
Purtroppo, però, non ottengo il risultato desiderato...
Ecco il codice che ho scritto:
###########################
#include <stdio.h>   /* Standard input/output definitions */
#include <string.h>  /* String function definitions */
#include <unistd.h>  /* UNIX standard function definitions */
#include <fcntl.h>   /* File control definitions */
#include <errno.h>   /* Error number definitions */
#include <termios.h> /* POSIX terminal control definitions */
int
open_port(void)
{
	int fd; /* File descriptor for the port */
	fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY);
	if (fd == -1)
	{
		/*
		 * Could not open the port.
		 */
		perror("open_port: Unable to open /dev/ttyS0 - ");
	}
	else
		fcntl(fd, F_SETFL, 0);
	/* BAUD RATE */
	struct termios options;
    /*
     * Get the current options for the port...
     */
    tcgetattr(fd, &options);
    /*
     * Set the baud rates to 19200...
     */
    cfsetispeed(&options, B57600);
    cfsetospeed(&options, B57600);
    /*
     * Enable the receiver and set local mode...
     */
    options.c_cflag |= (CLOCAL | CREAD);
    /*
     * Set the new options for the port...
     */
    tcsetattr(fd, TCSANOW, &options);
	/* NO PARITY */
	options.c_cflag &= ~PARENB;
	options.c_cflag &= ~CSTOPB;
	options.c_cflag &= ~CSIZE;
	options.c_cflag |= CS8; /*8 bit*/
	return (fd);
}
void delay()
{
	float j;
	int i,k,l;
	for (i = 0; i<1000; i++)
	{
		for (k = 0; k<1000; k++)
		{
			for (l = 0; l<1000; l++)
			{
				j = (i*i)/2;
			}
		}
	}
}
main()
{
	int fd = open_port();
	delay();
	int n = write(fd, "0x80", 2);
	delay();
	n = write(fd, "0x82", 2);
	delay();
	n = write(fd, "0x84", 2);
	delay();
	n = write(fd, "0x83", 2);
	delay();
	n = write(fd, "0x88", 2);
	delay();
	close(fd);
}
##########################################
Allora, quello che ottengo è che il dispositivo attaccato alla seriale
fa il wakeup appena apro la porta, ma poi non ha nessuna reazione ai
comandi.
Ho inserito la funzione delay() perchè, senza di essa, non avveniva
nemmeno questo wakeup.
Il fatto è che non capisco se sbaglio il codice C o se sbaglio a
utilizzare il protocollo di comunicazione del dispositivo....
Qualche suggerimento?
grazie
Lorenzo

 
non lo stai impostando a 19200, contrariamente a quanto commenti..
-- 
SF
"Breaking something which works as intended in order to solve a problem 
existing in delusion-space is not usually called 'a workaround'."

 non lo stai impostando a 19200, contrariamente a quanto commenti..
sì, quel commento è perchè ho fatto copia e incolla dal sito che mi è
stato segnalato nella risposta precedente...
Il baud rate corretto è 57600
grazie
Lorenzo

 
non č che devi smanettare su RTS e/o CTS?



mh... se non ho capito male, sono parametri che riguardano il flow
control, giusto?
Il protocollo del mio dispositivo dice: Flow control NONE
grazie
Lorenzo

L ha scritto:

Già. Allora prova a guardare la funzione cfmakeraw(), da chiamare prima 
di settare il baudrate.


 di settare il baudrate.
ciao,
innanzi tutto grazie mille per la disponibilità!
Allora, ho provato così: nel codice che ho scritto nel mio messaggio
precedente, ho inserito cfmakeraw nella posizione seguente:
**********************
/* BAUD RATE */
	struct termios options;
	cfmakeraw(&options);
    /*
     * Get the current options for the port...
     */
    tcgetattr(fd, &options);
***********************
è corretto così? Comunque continuo a non ottenere risposte dal
sistema... ma intanto voglio capire se sto usando la comunicazione
seriale nel modo corretto...........

 seriale nel modo corretto...........
no:
struct termios options;
cfmakeraw( &options );
cfsetispeed( &options, B57600 );
cfsetospeed( &options, B57600 );
int err = tcsetattr( fd, TCSANOW, &options );
assert( !err );


 wrote:
[...]
chiedo scusa se sono duro...
Allora, il mio codice adesso è questo:
************************************************
#include <stdio.h>   /* Standard input/output definitions */
#include <string.h>  /* String function definitions */
#include <unistd.h>  /* UNIX standard function definitions */
#include <fcntl.h>   /* File control definitions */
#include <errno.h>   /* Error number definitions */
#include <termios.h> /* POSIX terminal control definitions */
#include <assert.h>
int
open_port(void)
{
	int fd; /* File descriptor for the port */
	fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY);
	if (fd == -1)
	{
		/*
		 * Could not open the port.
		 */
		perror("open_port: Unable to open /dev/ttyS0 - ");
	}
	else
		fcntl(fd, F_SETFL, 0);
	/* BAUD RATE */
/*	struct termios options;
	cfmakeraw(&options);*/
	struct termios options;
	cfmakeraw( &options );
	cfsetispeed( &options, B57600 );
	cfsetospeed( &options, B57600 );
	int err = tcsetattr( fd, TCSANOW, &options );
	assert( !err );

	return (fd);
}

}





***********************************************
è corretto, così?
Anche così, comunque, continuo ad ottenere solo il wakeup del
dispositivo....
grazie mille
Lorenzo

         options.c_cflag |= CS8; /*8 bit*/
Se può aiutare a capire, ho provato per curiosità ad aggiungere
printf("%d",err);
e in output ottengo sempre 0...
ciao
Lorenzo

 è corretto, così?
a occhio direi di sì, ma non guardi i valori di n dopo le chiamate a 
write().


 write().
ho provato ora, è in tutti i casi il valore di n è 2, quindi se non
sbaglio significa che entrambi i byte sono stati trasmessi. Quindi in
teoria la comunicazione è andata a buon fine...
Boh, proverò a studiarmi meglio il protocollo... anche se non è che ci
sia molto altro, in realtà....
grazie
Lorenzo

 
hai tenuto conto di questo pezzo?
"Roomba will not respond to any SCI commands when it is asleep. Users 
can wake up Roomba by setting the state of the Device Detect pin low for
500ms."


 
La funzione write come secondo parametro vuole un puntatore e tu 
correttamente lo stai passando in quanto le passi il puntatore 
all'inizio delle varie stringhe "0x80", "0x82" etc. poi come tezo 
parametro indichi 2 quindi le dici di prendere i primi 2 caratteri.
Attenzione poi perche' stai dicendo di prendere gli ASCII dei caratteri 
'0' e 'x' quindi questo e' un secondo errore se il robottino accetta 
solo comandi composti da un solo carattere.
Ciao
UB
-- 
U'll neva get 2 heaven if ur scared of gettin high

 500ms."
sì, certo...
In tutti i casi, come ti dicevo, l'unica cosa che ottengo è
esattamente il wakeup... nel momento in cui apro la connessione
seriale....
mah!
grazie
Lorenzo

UBoss ha scritto:

eggià, non me ne ero proprio accorto! sto diventando obsoleto :-/


 solo comandi composti da un solo carattere.
probabilmente è proprio questo il problema!
Quello che io devo mandare, quando scrivo ad esempio n = write(fd,
"0x80", 2);
sono due byte che corrispondono al valore esadecimale 80, che in
binario sarebbe 10000000.
Ho proprio scritto una cavolata, nel programma! :-)
ehm..... come si fa a fare quello che voglio fare io?
grazie
Lorenzo

 ehm..... come si fa a fare quello che voglio fare io?
Calma non facciamo errori su errori.
0x80 esadecimale corrisponde si a 10000000 binario e a 127 decimale 
pero' e' un solo byte. Un byte equivale ad 8 bit.
adesso il suggerimento arruginito di un vecchio programmatore un po 
tarlato e con le ragnatele.
main()
{
	char *pCH={0x80,0x82,0x84,0x83,0x88};
	int fd=open_port();
	delay();
	write(fd,pCH[0],1);
	delay();
	write(fd,pCH[1],1);
	delay();
	write(fd,pCH[2],1);
	delay();
	write(fd,pCH[3],1);
	delay();
	write(fd,pCH[4],1);
	delay();
	close(fd);
}
non l'ho provata, potrebbe anche non andare comunque i byte da scrivere 
sono 1 alla volta e non 2.


 }
quando provo a compilare, ottengo:
prova.c:125: warning: passing argument 2 of ‘write’ makes pointer from
integer without a cast
prova.c:127: warning: passing argument 2 of ‘write’ makes pointer from
integer without a cast
prova.c:129: warning: passing argument 2 of ‘write’ makes pointer from
integer without a cast
prova.c:131: warning: passing argument 2 of ‘write’ makes pointer from
integer without a cast
prova.c:133: warning: passing argument 2 of ‘write’ makes pointer from
integer without a cast
e, se provo a far partire il programma, ottengo segmentation
fault..........................
comunque grazie mille a tutti per l'aiuto che mi state dando!
ciao
Lorenzo

 
mi sto un po' sulle balle ad intervenire ora :-), però la versione 
corretta di quanto sopra è:
main() {
	char pCH[] ={0x80,0x82,0x84,0x83,0x88};
	int fd=open_port();
	delay();
	write(fd, pCH, 1);
	delay();
	write(fd, pCH+1, 1);
	delay();
	write(fd, pCH+2, 1);
	delay();
	write(fd, pCH+3, 1);
	delay();
	write(fd, pCH+4, 1);
	delay();
	close(fd);



Confermo purtroppo l'ho gia detto sono un po' arruginito anch'io.
Ciao.


 	write(fd, pCH+1, 1);
Scusate l'OT e la "cazzoneria", ma grazie a tutti e due per avermi 
ricordato per quale motivo (puntatori) continuo a cercare alternative di 
livello piu` alto al C. :)
Bye.


così in effetti la compilazione va a buon fine.............. anche se
comunque ottengo sempre e solo il wakeup.............. :-/
vi ringrazio comunque molto.... grazie anche a questa discussione sto
pian piano capendo un po' come funziona il tutto.... grazie mille!
ciao
Lorenzo


Caspita ragazzi mi avete fatto tornare la pelle d'oca, ho riscritto 
qualche riga di C/C++ che erano anni che non toccavo e mi e' venuto un 
brivido su tutto il corpo. Saranno comodi gli altri linguaggi di 
programmazione, ma la violenza pura la apprezzi solo in un modo. Come 
dire esistono le Car, le Super Car e poi ci sono le Extreme Car. Con il 
C/C++ sei al Extreme. Il resto e' bello.
Una volta vidi in un negozio di auto una Mercedes 600 Sl una Ferrari 
Testarossa ed una Lamborghini Diablo affiancate. Niente da dire 3 bei 12 
cilindri affiancati. Una Gnocca, una Super Gnocca e la Gnocca Estrema.
La Gnocca estrema tutto il resto e' bello.
Ciao
UB
Scusate lo sfogo.
Ma ad un vecchietto e' permesso ;-)



Mi spiace contraddire ancora una volta ma la versione esatta e':
main() {
     char pCH[] ={0x80,0x82,0x84,0x83,0x88};
     int fd=open_port();
     delay();
     write(fd, pCH[0], 1);
     delay();
     write(fd, pCH[1], 1);
     delay();
     write(fd, pCH[2], 1);
     delay();
     write(fd, pCH[3], 1);
     delay();
     write(fd, pCH[4], 1);
     delay();
     close(fd);
}
Questa dovrebbe essere definitiva.


 
Manca l'ultima forma che equivale un po' alla mia precedente e a quella 
di Simone:
main() {
     char pCH[] ={0x80,0x82,0x84,0x83,0x88};
     int fd=open_port();
     delay();
     write(fd, *pCH, 1);
     delay();
     write(fd, *(pCH+1), 1);
     delay();
     write(fd, *(pCH+2), 1);
     delay();
     write(fd, *(pCH+3), 1);
     delay();
     write(fd, *(pCH+4), 1);
     delay();
     close(fd);
}
Evviva i puntatori !!!!!! ;-)



in realtà, entrambe queste due ultime versioni forniscono il seguente
output, in fase di compilazione:
prova.c:142: warning: passing argument 2 of ‘write’ makes pointer from
integer without a cast
prova.c:144: warning: passing argument 2 of ‘write’ makes pointer from
integer without a cast
prova.c:146: warning: passing argument 2 of ‘write’ makes pointer from
integer without a cast
prova.c:148: warning: passing argument 2 of ‘write’ makes pointer from
integer without a cast
prova.c:150: warning: passing argument 2 of ‘write’ makes pointer from
integer without a cast
ciao
Lorenzo

Array di char:
char pCH[] ={0x80,0x82,0x84,0x83,0x88};
pCH[1] : il valore del secondo elemento dell'array (0x82)
*(pCH+1) : il valore del secondo elemento dell'array (0x82), uguale a sopra 
(stai dereferenziando il puntatore al secondo elemento)
&pCH[1] : puntatore al secondo elemento dell'array
pCH+1 : puntatore al secondo elemento dell'array (uguale a sopra)
Pleg 


Lo sapevo che gli anni arrugginiscono c...o !!!
Hai pienamente ragione.
Cilicio ai fianchi, cenere in testa, chiedere scusa e ... tornare a 
scrivere codice.


 scrivere codice.
Eddeche? :D
Su un ng parente sono io che sto chiedendo info da niubbio a destra e a 
manca... :)
Pleg 

Approp: prova a comunicare col tuo robot ma senza usare il tuo programma, 
usa cutecom o qualsiasi altro terminale seriale "gia' fatto". Cosi' puoi 
vedere subito se il problema sta nel tuo prog o da qualche altra parte.
Pleg 

 
eh no, pCH[0] è un char, non il char* che richiede la write()..



Per prima cosa occhio in open_port. Se open ritorna -1 (errore) non
puoi usare altre funzioni su questo valore (tipo fcntl(fd, ...))
quindi ti conviene interrompere
ritornando subito il valore dalla funzione `if(error) { perror(bla
bla); return fd; }
Poi per quanto riguarda la write io proverei anche a vedere il suo
valore di ritorno:
#include <errno.h>
#include <stdlib.h>
int main()
{
  char pCH[] = {0x80,0x82,0x84,0x83,0x88};
  char i;
  int fd;
  if((fd = open_port()) == -1)
    return EXIT_FAILURE;
  for(i=0; i < sizeof(pCH); delay(), i++)
    if((write(fd, pCH + i, 1) != 1)
      {
        perror("write error");
        return EXIT_FAILURE;
      }
  /* CHIUSURA */
  return EXIT_SUCCESS;
}
Christian.

Dimenticavo... :)
Al posto della funzione delay() che hai creato tu (e immagino faccia
impennare la cpu) perchè non usi la sleep() ?
PS: Un buon testo in italiano per chi vuole programmare con GNU/linux
è ``Guida Alla Programmazione Linux'' 
http://gapil.truelite.it/gapil.pdf

 č ``Guida Alla Programmazione Linux'' 
http://gapil.truelite.it/gapil.pdf
Proprio li' c'era un errore su... non mi ricordo cosa, forse il 
comportamento della select(). Mi ricordo che stavo seguendo quanto diceva 
quella guida, e non riuscivo a far funzionare il mio codice. Poi ho letto la 
sezione relativa sullo Stevens, che come ogni bibbia che si rispetti aveva 
un paragrafo dedicato *esattamente* al problema che mi affliggeva, con la 
soluzione corretta.
Pleg 

 Pleg
Mai provati gli esempi... :)