Serial Linux

Bonjour,
J'ai un comportement très différent de Serial.available() sous Linux (toujours>0, même après un flush) et Windows (qui se comporte comme attendu).
Quelqu'un a-t-il déjà rencontré ce problème?
Merci.

Yep Patsol !

J'utilise Debian Linux et je n'ai pas le comportement que tu indiques.

void setup() {
  Serial.begin(9600);
}

void loop() {
  if (Serial.available() > 0) {
    Serial.println("DATA?");
    Serial.flush();
  }
  delay(2000);
}

Tu as peut être des pertubations exterieures ? mauvais cable Usb ?

@+

Zoroastre.

Bonjour,

Comme ça au vu des "symptômes" je pencherai plus vers un probléme de drivers.

Sinon par rapport à Serial.flush() depuis arduino 1.0 il n'as pas le même comportement.
Avant 1.0 flush() vidé tout le contenu du buffer série, depuis 1.0 flush() ne fait que replacer le curseur à la 1er "case" du buffer série.

Est tu sous arduino 0022/0023 ou sous arduino 1.0 ?

Merci!
Je suis sous arduino1.0 (depuis peu!)
En effet, je n'avais pas vu que

Serial.flush() depuis arduino 1.0 il n'as pas le même comportement.

Et comme sous linux je faisais un cat sur /dev/ttyACM0....

Pourtant dans arduino-1.0/reference/serial.html, on lit encore:

Flushes the buffer of incoming serial data. Any call to Serial.read() or Serial.available() will return only data received after all the most recent call to Serial.flush().

Il y a-t-il une doc plus à jour?
Existe-til un équivalant à l'ancien Serial.available()?

patsol:
Pourtant dans arduino-1.0/reference/serial.html, on lit encore:

Flushes the buffer of incoming serial data. Any call to Serial.read() or Serial.available() will return only data received after all the most recent call to Serial.flush().

Il y a-t-il une doc plus à jour?
Existe-til un équivalant à l'ancien Serial.available()?

Non pour le moment il n'y a pas de doc sur .flush() ... et c'et bien dommage car flush() dans la version 1.0 n'as pas du tout la même fonction qu'avant !
cf :

http://code.google.com/p/arduino/issues/detail?id=593&can=1&q=serial.flush

La version 1.0 est pour moi une catastrophe logistique de la part de la team arduino, trop de changements imposé, en une seul fois, mal documenté (voir pas du tout) et qui ne respecte aucun standard de nommage (flush() a maintenant un comportement équivalent à une fonction wait() ... et l'ancien flush() n'existe plus ... etc).
Pour le moment 0023 est la meilleur version disponible ...

On essaiera de s'y faire.
Merci.

skywodd:
Sinon par rapport à Serial.flush() depuis arduino 1.0 il n'as pas le même comportement.
Avant 1.0 flush() vidé tout le contenu du buffer série, depuis 1.0 flush() ne fait que replacer le curseur à la 1er "case" du buffer série.

En fait c'est l'ancien flush qui ne fesait que placer le curseur à la "dernière case", l'emplacement ou l'on peux écrire (head) ou lire (tail) du buffer de réception, les mettre à égalité veux dire que tout à été lu et plus rien à écrire donc libre si j'ai bien compris.

Avant:

void HardwareSerial::flush()
{
  _rx_buffer->head = _rx_buffer->tail;
}

Maintenant il ne joue plus avec le buffer de réception mais celui d'émission, on rempli le buffer avec write et on force à le vider via flush (c'est comme en c pour vider le buffer sur la sortie stdin en applis console si je me souviens bien, idem en java avec out).

void HardwareSerial::flush()
{
  while (_tx_buffer->head != _tx_buffer->tail)
    ;
}

La nouveauté dans la version 1.0 est qu'il utilise également un buffer pour l'émission et le fait d'utiliser un buffer en transmission évite d'envoyer des "morceau" d'une trame à des temps différents.

Avant:

#if (RAMEND < 1000)
  #define RX_BUFFER_SIZE 32
#else
  #define RX_BUFFER_SIZE 128
#endif

struct ring_buffer
{
  unsigned char buffer[RX_BUFFER_SIZE];
  int head;
  int tail;
};

ring_buffer rx_buffer  =  { { 0 }, 0, 0 };

Après:

#if (RAMEND < 1000)
  #define SERIAL_BUFFER_SIZE 16
#else
  #define SERIAL_BUFFER_SIZE 64
#endif

struct ring_buffer
{
  unsigned char buffer[RX_BUFFER_SIZE];
  int head;
  int tail;
};

  ring_buffer rx_buffer = { { 0 }, 0, 0};
  ring_buffer tx_buffer = { { 0 }, 0, 0};

Nouveau buffer de transmission et entre () on peux remarqué qu'ils ont séparé le buffer en deux, pour la réception et l'émission.

Interuption sur UDRE qui signale si le buffer UDR (c'est le même pour rx et tx) est prèt à recevoir de nouvelles données.
if (tx_buffer.head == tx_buffer.tail) si le buffer est "vide" on désactive cette interruption "cbi(UCSR1B, UDRIE);" (réactivé dans write).
Sinon on envoie les données "UDR = c;" .

ISR(USART1_UDRE_vect)
{
  if (tx_buffer1.head == tx_buffer1.tail) {
	// Buffer empty, so disable interrupts
    cbi(UCSR1B, UDRIE1);
  }
  else {
    // There is more data in the output buffer. Send the next byte
    unsigned char c = tx_buffer1.buffer[tx_buffer1.tail];
    tx_buffer1.tail = (tx_buffer1.tail + 1) % SERIAL_BUFFER_SIZE;
	
    UDR1 = c;
  }
}

Nouveau write qui ce contente de remplir le buffer et activé l'interruption UDRIE sur le flag UDRE de UDR :sweat_smile:.

size_t HardwareSerial::write(uint8_t c)
{
  int i = (_tx_buffer->head + 1) % SERIAL_BUFFER_SIZE;
	
  // If the output buffer is full, there's nothing for it other than to 
  // wait for the interrupt handler to empty it a bit
  // ???: return 0 here instead?
  while (i == _tx_buffer->tail)
    ;
	
  _tx_buffer->buffer[_tx_buffer->head] = c;
  _tx_buffer->head = i;
	
  sbi(*_ucsrb, _udrie);
  
  return 1;
}

Avant pour la transmission on se contentais de ça:

void HardwareSerial::write(uint8_t c)
{
  while (!((*_ucsra) & (1 << _udre)))
    ;

  *_udr = c;
}

Par contre available n'a pas changé, la méthode de réception est toujours la même.

int HardwareSerial::available(void)
{
  return (unsigned int)(SERIAL_BUFFER_SIZE + _rx_buffer->head - _rx_buffer->tail) % SERIAL_BUFFER_SIZE;
}

Même la macro d’interruption SIGNAL() qui apparemment est dépréciée et remplacer par ISR(), pourtant ils l'ont mis sur la nouvelle pour a transmission et pas en réception :cold_sweat: (ou je me trompe ?)

En fait on dirais juste une mise à la norme, mais qui arrive bien tard ce qui engendre la confusion nouvelle.
Donc pour moi il faut surtout ne pas oublier un petit flush après un ou des write successif (donc également print(), println(), ... ) sinon une interruption sur UDRE peux bloqué l'autre je suppose ?

Ce sont ses deux morceau de code provenant de write et de l’interruption qui peuvent être la cause de blocage en réception je crois ?

    sbi(*_ucsrb, _udrie);
  if (tx_buffer1.head == tx_buffer1.tail) {
	// Buffer empty, so disable interrupts
    cbi(UCSR1B, UDRIE1);
  }

d'où le forcing dans flush.

  while (_tx_buffer->head != _tx_buffer->tail)
    ;

Entre () quand je vois des (I think) ou de (???) dans les commentaire du code ça fais quand même peur :~

// Define constants and variables for buffering incoming serial data.  We're
// using a ring buffer (I think), in which head is the index of the location
// to which to write the next incoming character and tail is the index of the
// location from which to read.

J'ai également remarqué que si on supprimais l'inclusion de HardwareSerial de WProgram.h si on veux faire ça propre gestion du port série ou simplement le retiré comme il est inclus par défaut, be il manque l'inclusion de Stream.h dans la lib Ethernet ... :~ pas facile d'y toucher au core arduino.

'tain! J'avais pas vu le double buffer circulaire pour le port série Tx / Rx !
Mais c'est une total hérésie :stuck_out_tongue_closed_eyes: on se retrouve avec 2x fois moins de RAM qu'avant pour faire un pathétique "bulk transfert" ...

Et oui les commentaires de certains fichiers font très peur ... faudrait que je retrouve où, mais dans un fichier du core il y a carrément un commentaire signalant qu'une fonction bug si on modifie un define, mais que le dev savait pas pourquoi :fearful:

Un truc intelligent aurait était de prendre exemple sur MHVLib qui est un core "arduino like" méga optimisé ...
Pour être passé sur du "c pure" avec avr-gcc certain morceau du core arduino me donne qu'une envie, c'est de faire un fork du code source et de tout reprendre ...
Mais bon pour le moment il faudrait déja que j'arrive à faire un script pour convertir les librairies arduino pour 1.0 (avec patch des valeurs de retour & co) ...

'tain! J'avais pas vu le double buffer circulaire pour le port série Tx / Rx !
Mais c'est une total hérésie on se retrouve avec 2x fois moins de RAM qu'avant pour faire un pathétique "bulk transfert" ...

Non, il y a 2 buffers mais ils sont 2 fois plus courts qu'avant. Donc on consomme la même quantité de RAM.
Le buffer en réception est indispensable pour ne pas rater des caractères.
Le buffer en émission a lui aussi un sens, il évite les blocages de programme lorsqu'il y a des messages un peu longs à émettre (par exemple lorsqu'on met des Serial.print pour débugger) cela perturbe moins le "temps réel" du programme à tester qui a ainsi un déroulement plus régulier.

fdufnews:

'tain! J'avais pas vu le double buffer circulaire pour le port série Tx / Rx !
Mais c'est une total hérésie on se retrouve avec 2x fois moins de RAM qu'avant pour faire un pathétique "bulk transfert" ...

Non, il y a 2 buffers mais ils sont 2 fois plus courts qu'avant. Donc on consomme la même quantité de RAM.

C'est ma faute, mauvais copier coller dans mon post précédent, j'avais laissé les deux taille identique :sleeping:.

C'est bien :

#if (RAMEND < 1000)
  #define RX_BUFFER_SIZE 32
#else
  #define RX_BUFFER_SIZE 128
#endif
#if (RAMEND < 1000)
  #define SERIAL_BUFFER_SIZE 16
#else
  #define SERIAL_BUFFER_SIZE 64
#endif

En tout cas c'est bien ce qu'il me semblait quand je regarde la lib optimisée que tu présente sky, la grosse différence est que le core arduino est prévu pour rendre transparent l'utilisation des registre, etc au débutant aux prix de quelque include ou ligne de plus dans le main je sais pas si ça vaut la peine de prémaché le code à ce point là :cold_sweat: ...

osaka:
C'est ma faute, mauvais copier coller dans mon post précédent, j'avais laissé les deux taille identique :sleeping:.

J'ai vu après coup :*

osaka:
En tout cas c'est bien ce qu'il me semblait quand je regarde la lib optimisée que tu présente sky, la grosse différence est que le core arduino est prévu pour rendre transparent l'utilisation des registre, etc au débutant aux prix de quelque include ou ligne de plus dans le main je sais pas si ça vaut la peine de prémaché le code à ce point là :cold_sweat: ...

Il y a quand même un limite entre rendre les choses transparente et faire une usine à gaz, l'api arduino est trés simpliste, des personnes ont porté les fonctions de base sur d'autre µc avec un seul .h et le préprocesseur ... alors quand on voit un digitalWrite qui consomme 430octets de flash ...

skywodd:
Il y a quand même un limite entre rendre les choses transparente et faire une usine à gaz, l'api arduino est trés simpliste, des personnes ont porté les fonctions de base sur d'autre µc avec un seul .h et le préprocesseur ... alors quand on voit un digitalWrite qui consomme 430octets de flash ...

C'est clair et je suis pas certain que ce soit rendre service que de cacher à outrance ce qui ce passe au plus bas niveau, surtout vu le cout mémoire programme, ram et processus qu'il en résulte ...
Maintenant que j'ai un peux plus avancé dans l'utilisation de l'arduino et ses microcontrôleurs , dès que je peux me passer de certaine partie du core je le fais (comme digitalWrite par exemple).

Autre chose à propos de HardwareSerial, je viens seulement de voire ça :

  void serialEvent1() __attribute__((weak));
  void serialEvent1() {}

void serialEventRun(void)
{
  if (Serial.available()) serialEvent();
}

je vais voir sur arduino.cc pour une petite explication voir ce qui a changer et je tombe sur ça serialEvent() - Arduino Reference

Description

Called when data is available. Use Serial.read() to capture this data. The serialEvent() can be set with Serial.buffer() to only trigger after a certain number of data elements are read and can be set with Serial.bufferUntil() to only trigger after a specific character is read.

Mais dans le code ou autre je ne trouve aucune trace de méthode buffer() ou bufferUntil() et serialEvent() ou serialEventRun(void) ici plus haut ne retourne rien et ne font rien, que du vide ...
Alors code incomplet, pas encore implémenté (mais documenté), prévu pour une version 1.0 sortie trop tôt ???