Puerto serie

hola todos. prefiero arrancar que no soy experto en esto.
tengo el siguiente problema con este ejemplo...

void setup() {
  // initialize both serial ports:
  Serial.begin(9600);
  Serial1.begin(9600);
}

void loop() {
  // read from port 1, send to port 0:
  if (Serial1.available()) {
    int inByte = Serial1.read();
    Serial.write(inByte);
  }

  // read from port 0, send to port 1:
  if (Serial.available()) {
    int inByte = Serial.read();
    Serial1.write(inByte);
  }
}

el arduino que estoy usando es un mega... en el puerto 0 le insertos datos desde otro equipo y los reenvia al 1 que luego vuelven a entrar por el 1 y salen por el 0...

esto funciona perfectamente durante los primeros 30 segundos... luego se tilda... supongo que es por algun buffer lleno o algo así...

si me pueden ayudar sería buenisimo!!!

Esta constumbre de usar int en lugar de char me llama mucho la atención.

Los valores recibidos y transmitods son char no int.

void loop() {
  // read from port 1, send to port 0:
  if (Serial1.available()>0) {
    char inByte = Serial1.read();
    Serial.write(inByte);
    delay(5);                  // agrega pausas para no perder el flujo de la info
                                   // expermienta con el valor
  }

  // read from port 0, send to port 1:
  if (Serial.available()>0) {
    char inByte = Serial.read();
    Serial1.write(inByte);
    delay(5);                // agrega pausas para no perder el flujo de la info
                                 // expermienta con el valor
  }
}

prueba a ver si hay cambios.

La "costumbre" de usar int en lugar de char supongo que viene de que la función read devuelve un int, no un char (o unsigned char que es lo que espera el write). ¿Porqué, si lo que queremos que lea es un char? Pues porque si le pedimos que nos lea un dato y no hay nada disponible nos retorna un -1 (1111111111111111 en binario y complemento a dos), mientras que si había un dato disponible para leer nos lo devolverá con un valor entre 0 y 255 (entre 0000000000000000 y 0000000011111111 en binario). Por lo que el código "original", sin el available, podría quedar así:

int inByte = 0; // Es buena costumbre inicializar toda variable cuando se declara.

void setup() {
  // initialize both serial ports:
  Serial.begin(9600);
  Serial1.begin(9600);
}

void loop() {
  // read from port 1, send to port 0:
  inByte = Serial1.read();
  if (inByte >= 0) {
    Serial.write(inByte);
  }

  // read from port 0, send to port 1:
  inByte = Serial.read();
  if (inByte >= 0) {
    Serial1.write(inByte);
  }
}

¿Entonces qué utilidad tiene el available? Pues, por ejemplo, no tener que guardar el valor leído para saber si podemos usarlo o no. Antes de tenerlo podemos saber si está disponible y, de estarlo, lo podemos usar diréctamente porque ya nos ha dicho el available que el dato está (y no nos va a devolver un -1, que si se lo "pasamos" al write lo tomará como un 255, por eso de que espera un unsigned char). Por lo que el código "original" usando el available podría quedar así:

void setup() {
  // initialize both serial ports:
  Serial.begin(9600);
  Serial1.begin(9600);
}

void loop() {
  // read from port 1, send to port 0:
  if (Serial1.available()) {
    Serial.write(Serial1.read());
  }

  // read from port 0, send to port 1:
  if (Serial.available()) {
    Serial1.write(Serial.read());
  }
}

Por otro lado, creo que los delay no sirven para mejorar nada, sino todo lo contrario. Podrían hacer que se pierdan datos, amén de hacer la transmisión "un pelín" más lenta. Yo no descartaría algún problema con los cables, conexiones o algún tipo de interferencias. La verdad es que no se me ocurre otra cosa ya que no veo defectos en el programa.

Es lo que creo, y no descarto el estar equivocado.

IgnoranteAbsoluto:
¿Entonces qué utilidad tiene el available?

Hay una y muy buena: lectura "a bulto".
Eso es, para leer a un array de byte o char sin ciclos:

Serial.readBytes(buffer, sizeof(buffer) > Serial.available() ? Serial.available() : sizeof(buffer));

También se puede utilizar para recuperar cadenas completas de caracteres sin utilizar objetos String.

hola, espero estar en el foro correcto, soy principiante y con pocos conocimientos de programacion. He recibido una placa Arduino Mega 2560 que dispone de 3 puertos serie, en realidad 4 solo que uno lo dejo para comunicarme con el monitor arduino IDE, para recibir información y la tarea que me he propuesto, en principio, es recibir datos, en el puerto serie 1 (57600 baudios) de una IMU razor 9 DOF con 50hz de actualización y en el puerto serie 2 (115200 baudios) recibir datos de un gps venus, por ahora solo las palabras GPGGA, GPRMC y GPVTG, con 1hz (en un futuro otras palabras y hasta 20hz) y transmitirlos por un lado al monitor de arduino ide y por otro almacenarlos en un datalogger logomatic v2 que dispone de entrada serial y guarda los datos en microSD.
Ambos sensores IMU y GPS entregan la infromacion por puerto serie.
La IMU entrega la informacion en angulos Euler con el siguiente formato:

#YPR=13.10,0.35,2.43. guiñada (yaw en inglés), de cabeceo (pitch) y de alabeo (roll), ángulos de navegación por ejemplo de un avión.

El GPS sensor de posición global entrega su información NMEA en distintas palabras y su formato es:

$GPGGA,125402.899,3126.1165,S,06416.1804,W,1,04,3.3,0.0,M,14.2,M,,000055
$GPRMC,125402.899,A,3126.1165,S,06416.1804,W,000.0,000.0,130617,,,A
66
$GPVTG,000.0,T,,M,000.0,N,000.0,K,A*0D

El programa que utilizo es muy sencillo y si bien puedo leer los datos en el monitor, cuando aparece la palabra GPGGA del gps se mezclan los datos con los de la IMU, como podrán apreciar mas abajo.

Este es mi programa:

------------------------------------------------------------------------
*/

void setup() {
// initialize serial ports:
Serial.begin(115200);
Serial1.begin(57600);
Serial2.begin(115200);

}

void loop() {
// read from port 1, send to port 0:IMU 50Hz

 if (Serial1.available()) {                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          if (Serial1.available()) {
  int inByte = Serial1.read();
  Serial.write(inByte);
 }
 }

  // read from port 2, send to port 0:GPS solo GGA 1hz
  if (Serial2.available()) {
  int inByte = Serial2.read();
  Serial.write(inByte);
}
}

He probado los puertos por separado y funcionan correctamente pero cuando habilito ambos la información que recibo esta mezclada apareciendo YPR, aunque alterado dentro de las palabras del gps:

#YPR=13.10,0.34,2.44
#YPR=13.10,0.33,2.43
#YPR=13.10,0.32,2.47
#YPR=13.10,0.33,2.48
#YPR=13.09,0.35,2.44
#YPR=13.09,0.36,2.45
#YPR=13.09,0.34,2.44
#YPR=13.10,0.38,2.41
#YPR=13.10,0.36,2.40
#YPR=13.12,0.37,2.40
#YPR=13.11,0.39,2.44
$GPGGA,121603.928,3126.1524,S#Y,PR=0163.4121,60..307,82.7439
,W,1,10,0.9,503.8,M,14.2,M,,000051
$GPRMC,121603#.YP9R=2138.,1A1,,0.3381,22.369.
1524,S,06416.0879,W,000.0,265.0,130617,,,A
61
$G#PYVPRT=G1,3.210,605.3.7,02.,37T
,,M,000.0,N,000.0,K,A*0C
#YPR=13.10,0.33,2.39
#YPR=13.09,0.32,2.38
#YPR=13.09,0.31,2.38
#YPR=13.09,0.33,2.37
#YPR=13.10,0.34,2.34
#YPR=13.09,0.35,2.37
#YPR=13.10,0.37,2.36
#YPR=13.10,0.35,2.36
#YPR=13.10,0.34,2.38
#YPR=13.09,0.33,2.35
#YPR=13.09,0.35,2.35
#YPR=13.09,0.38,2.35

entiendo que probablemente la solución este en utilizar algún otro procedimiento como por ejemplo armando una trama o algo parecido pero no dispongo de los conocimientos aún. Agradecería me pudiesen indicar alguna linea de lectura (aunque he buscado en profundidad) o trabajo para solventar mi problema. gracias

Pero esa tontería es porque al final de la cadena no pones un LINE FEED o un Serial.println()

void loop() {
 // read from port 1, send to port 0:IMU 50Hz

  if (Serial1.available()) {                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          if (Serial1.available()) {
     int inByte = Serial1.read();
     Serial.write(inByte);
     if (inByte == '\r' || inByte == '\n')
        Serial.println();
  }

  // read from port 2, send to port 0:GPS solo GGA 1hz
  if (Serial2.available()) {
     int inByte = Serial2.read();
     Serial.write(inByte);
     if (inByte == '\r' || inByte == '\n')
        Serial.println();
  }
}

Ajusta lo que sea necesario

Hola @jro1952. El problema que estás teniendo es que los datos que te llegan pueden llegar en cualquier momento de cualquiera de los dos puertos series de los que lees. Y como no llegan los datos por bloques "cerrados" se te están mezclando los unos con los otros. Por otro lado no te puedes quedar esperando a leer una linea completa de uno y luego leer una linea de otro y así sucesivamente.

Para este caso en particular, y viendo que los "paquetes de datos" te vienen "por líneas", es decir, con un retorno de carro (\r) seguido de un avance de línea (\n), o al menos uno de los dos, al final de cada "paquete de datos". Lo que se puede hacer es leer y guardar en sus respectivos buffers lo que te llega por cada puerto serie, a la espera de que se complete el "paquete" cuando llegue el un retorno de carro o un salto de línea (nadie te asegura que se manden los dos, algunos dispositivos sólo manda uno de ellos). Entonces, si se ha recibido algún dato, se envían todos los datos del paquete recibido, seguido del \r\n del println(). Como no es seguro que se reciba "la pareja" de fin de línea, nos aseguramos de al menos encontrar uno de los dos y "retransmitir" la pareja con el println().

Para ello he creado una clase que controla un puerto serie de "origen" su buffer para guardar los datos recibidos del paquete y un puerto serie de "destino". Creamos dos objetos, un para mandar del Serial1 al Serial y otro para mandar del Serial2 al Serial.

En el loop() del sketch se llama al loop() de cada objeto, que se encarga de leer el SerialX correspondiente, guardar en el buffer y enviarlo al Serial cuando detecta "el final del paquete".

No sé hasta qué punto te podrá funcionar, porque los buffers de recepción y transmisión del propio sistema Arduino, que no son "infinitos". Y puede darse el caso de que al mandar todo el bloque junto la función write() bloquee el proceso durante cierto tiempo mientras transmite parte del paquete, a la vez que lleguen datos por el otro puerto y como el loop() está bloqueado por el write() no se puede leer los datos recibidos para guardarlos en nuestro buffer. Esto llevaría a que el buffer de recepción del sistema de Arduino se llene y se pierdan bytes. Para procurar que esto no pase, te recomiendo que el Serial lo pongas a la velocidad más alta posible (los 115200 baudios que tiene ahora), pero el Serial1 y el Serial2 lo bajes lo más que puedas, sin pasarse no sea que pierda eficiencia, si lo puedes poner a 9600 sería perfecto. Yo, porque no sé si puedes bajar las velocidades de Serial1 y Serial2, las he dejado como las tenías.

Por otro lado, si necesitas ahorrar memoria del Arduino, podrías bajar el valor de los dos 150 que he puesto de tamaño de buffer, pero ten en cuenta que el mínimo es el máximo tamaño que puedes esperar de los paquetes que se reciben.

Lo he medio probado con un Arduino UNO (medio probado es decir mucho ya que sólo lo he podido probar con el único puerto serie que trae. Podría probarlo con otro puerto serie por software, pero no tengo a mano nada que enchufarle). Así que no lo he probado con un MEGA. Pero, en teoría, debería de funcionar. Ya me dirás si funciona, si lo pruebas.

El código:

class SerieBufferSerie {
    public:
        SerieBufferSerie (Stream &origen, Stream &destino, size_t maximoBuffer) :
            origen(origen),
            destino(destino),
            bytesLeidos(0),
            maximoBuffer(maximoBuffer),
            buffer(0) {
                this->buffer = new uint8_t[maximoBuffer];
            }

        void loop() {
            if ( ! this->origen.available() ) {
                return;
            }
            int byteLeido = this->origen.read();
            if ( (byteLeido == '\r') || (byteLeido == '\n') ) {
                if (this->bytesLeidos == 0) {
                    return;
                }
                this->destino.write(this->buffer, this->bytesLeidos);
                this->destino.println();
                this->bytesLeidos = 0;
                return;
            }
            this->buffer[this->bytesLeidos++] = byteLeido;
            if (this->bytesLeidos >= this->maximoBuffer) {
                this->destino.write(this->buffer, this->bytesLeidos);
                this->bytesLeidos = 0;
            }
        }

        virtual ~SerieBufferSerie() {
            delete[] this->buffer;
        }

    private:
        Stream &origen;
        Stream &destino;
        size_t bytesLeidos;
        size_t maximoBuffer;
        uint8_t *buffer;
};

SerieBufferSerie serial1BufferSerial(Serial1, Serial, 150);
SerieBufferSerie serial2BufferSerial(Serial2, Serial, 150);

void setup() {
    Serial.begin(115200);
    Serial1.begin(57600);
    Serial2.begin(115200);
}

void loop() {
    serial1BufferSerial.loop();
    serial2BufferSerial.loop();
}

surbyte, muchas gracias por tu pronta respuesta.
En el futuro tratare de no molestarte con mis "Tonterias", pense que era un foro aducativo y No de denigración.
De cualquier manera la "tonteria" que has posteado no anda por mas que probado y ajustado, seguramente sera por una Tonteria mia en la implementación. un saludo

IgnoranteAbsoluto, muchas gracias por tu ayuda, implemente tu código y funciona perfecto, realmente una ayuda muy didáctica y expeditiva. gracias, un saludo

Mira porque digo que es una tonteria, por cierto busca en el diccionario, tontería no es humillante ni denigrante.
Tal vez en tu país lo sea, y si es el caso me disculpo, en argentina, tonteria es decir que es una pavada algo simple de resolver.

A ver si esto funciona mas alla que lo que te sugirieron es técnicamente superior.

Este es tu código,

if (Serial1.available()) {
     int inByte = Serial1.read();
     Serial.write(inByte);
     if (inByte == '\r' || inByte == '\n')
        Serial.println();
  }

  // read from port 2, send to port 0:GPS solo GGA 1hz
  if (Serial2.available()) {
     int inByte = Serial2.read();
     Serial.write(inByte);
     if (inByte == '\r' || inByte == '\n')
        Serial.println();
  }

En el (Y NO ME PERCATE) tienes dos seriales que esperan caracteres de dos entradas diferentes... claro.. nadie le dice que no intercale porque en lugar de poner un while y quedarse en una trama recibida en un dispositivo saltas de un caracter a otro intercalándose y de ahi mi comentario.. una cosa simple de resolver.

usa un while y resuelto

while (Serial1.available()>0) {                                                                                                         int inByte = Serial1.read();
            Serial.write(inByte);
            if (inByte == '\r' || inByte == '\n')
                Serial.println();
 }

 // read from port 2, send to port 0:GPS solo GGA 1hz
 while (Serial2.available()) {
    int inByte = Serial2.read();
    Serial.write(inByte);
    if (inByte == '\r' || inByte == '\n')
       Serial.println();
 }

muchas gracias por tu colaboración, aunque he probado el programa y el mismo no soluciona el problema de la mezcla de información, al menos para mi. De cualquier manera sirve para analizar nuevas alternativas y aprendizaje.
Aplique la solución aportada por un integrante del foro, "Ignorante", que por un lado funciona de maravillas y por otro me permitirá expandir soluciones ya que estoy incorporando un sensor de temperatura y un sensor de presión, anbos I2C. gracias

Perdona una vez mas, espero que te hayas dado cuenta. copio y pego tantas veces y creo que al editar corregí el segundo if ( y debia ser while tmb.

Ese código funciona, aunque el otro es 10mil veces mejor. Pero solo te lo muestro a nivel elemental, que es el que todos manejamos.
El que indica Ignorante es el de un licenciado programador, yo hago ingeniería y programo. Se entiende que no me comparo jamás con Ignorante, Lucario o Noter por citar algunos de los que veo sus respuestas a diario.

Igual, tmb tomé nota.