Comunicación por UART entre dos Arduino Leonardo

Hola

Tengo dos arduino Leonardo que estoy tratando de comunicar por UART. He comprobado que la placa emisora sí es capaz de enviar datos (he hecho la prueba conectándola a un STM32L4), pero la placa receptora es incapaz de recibirlos.

He utilizado la librería SoftwareSerial y los cables de los pines RX y TX están cruzados entre ambos dispositivos. Los GND de ambos Arduinos están conectados, y cada Arduino está conectado a un ordenador distinto.

  • Código del Arduino emisor:
#include "SoftwareSerial.h"

#define rxPin 2
#define txPin 3

SoftwareSerial miSerial(rxPin, txPin); 

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

  pinMode(rxPin, INPUT);
  pinMode(txPin, OUTPUT);
}

void loop () {

miSerial.write("a");
delay(100);

}
  • Código del Arduino receptor:
#include "SoftwareSerial.h"

#define rxPin 2
#define txPin 3

SoftwareSerial miSerial(rxPin, txPin);

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

  pinMode(rxPin, INPUT);
  pinMode(txPin, OUTPUT);
}

void loop() {

  char sMsg = miSerial.read();
  int iMsg = miSerial.read();

  Serial.println(sMsg);
  Serial.println(iMsg);

  delay(100);
}

Lo que se imprime por pantalla es:

-1

Y lo que me desconcierta más es que el led "TX" del Arduino receptor siempre está encendido, pero ninguno en el emisor.

Agradecería cualquier tipo de ayuda. Gracias por adelantado.

Así estás leyendo aunque no reciba nada, prueba así.

while (miSerial.available() > 0) {
  char sMsg = miSerial.read();
  Serial.println(sMsg);
}

Puedes probar si funciona el puerto haciendo un puente entre RX y TX del mismo arduino y enviando cualquier caracter a ver si lo lees.

1 Like

Eso no es necesario. La librería SoftwareSerial se encarga de ese manejo y configuración.
Retira esas dos líneas del setup.
El código es muy escueto o pobre en el loop.

Usa esto en ambos con os pines adecuados

include <SoftwareSerial.h>
#define rxPin 2
#define txPin 3
SoftwareSerial miSerial(rxPin, txPin);
char caracter;
void setup() {
     Serial.begin(9600);
     miSerial.begin(9600);
}

void loop() {
       if (miSerial.available()) { //si hay caracteres disponibles en el puerto serial por software
           caracter = miSerial.read();//leer carecteres en puerto serial por software
           Serial.print(caracter);//Mostras los caracteres obtenidos
       }
       if (Serial.available()) { //Si hay caracteres en puerto serial por hardware
            caracter = Serial.read();//leer carecteres en puerto serial por hardware
            miSerial.print(caracter);//Mostras los caracteres obtenidos
       }
 }
1 Like

Hola, en primer lugar, ¡gracias por responder!

Ahora bien, he probado a puentear los pines RX y TX en una sóla Arduino y he cargado el siguiente código:

#include <SoftwareSerial.h>
#define rxPin 2
#define txPin 3
SoftwareSerial miSerial(rxPin, txPin);

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

void loop() {

  miSerial.write('a');

  while (miSerial.available() > 0) {
    char sMsg = miSerial.read();
    Serial.println(sMsg);
  }
}

La salida por pantalla es:

aaaaaaaaaa....

Si fuese capaz de leer un dato, habría un salto de línea, pero no es el caso. He probado a utilizar los pines nativos 0 y 1 para UART con el siguiente código:

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

void loop() {

  Serial.write('a');

  char sMsg = Serial.read();
  if (strcmp(sMsg, 'a') == 0) {
    Serial.println("dato leido");
  }
}

Y obtengo la misma salida... He probado ambos códigos en una Leonardo a estrenar, la saqué de su caja para esto mismo, e incluso hice otra prueba con una Arduino Uno...

Siempre obtengo el mismo resultado, así que descarto que se trate de un problema de hardware.

Hola, gracias por responder.

He usado el código que me proporcionas pero no tengo ningún tipo de salida impresa en pantallla. Tengo la sospecha de que miSerial.available() y Serial.available() están siempre a FALSE.

A pesar de que, si omito esta instrucción, soy capaz de imprimir datos por el puerto serie...

Y esa salida es más que correcta.
No hay salto de línea porque no lo envías, solo envías 'a'.
Cambia

miSerial.write('a');

por

miSerial.write('a');
miSerial.write('\n'); // envía un salto de línea

Saludos

He hecho prueba con un ESP32 y me funciona correctamente.
Puente entre RX2 y TX2, si lo quito deja de llegar el 1 en consola.

#include <SoftwareSerial.h>
SoftwareSerial miSerial(16, 17);
char caracter;
unsigned long m = 0;
void setup() {
  Serial.begin(9600);
  miSerial.begin(9600);
}

void loop() {
  if (millis() - m  > 500) {
    miSerial.write("1");
    m = millis();
  }
  if (miSerial.available()) { //si hay caracteres disponibles en el puerto serial por software
    caracter = miSerial.read();//leer carecteres en puerto serial por software
    Serial.print(caracter);//Mostras los caracteres obtenidos
  }
  if (Serial.available()) { //Si hay caracteres en puerto serial por hardware
    caracter = Serial.read();//leer carecteres en puerto serial por hardware
    miSerial.print(caracter);//Mostras los caracteres obtenidos
  }
}
1 Like

Como siempre, si no sabes lo que estas haciendo entonces simplemente solo copias y pegas y no analizas lo que uno te escribe.
Te dije, verifica los pines de tus dos arduinos. Los dos usan 2 y 3.
Ahora si usas un Leonardo , estos tardan en reaccionar, a menos que pongas al comienzo en cada setup esto

Serial.begin(9600);

   // while the serial stream is not open, do nothing:
   while (!Serial) ;

Y eso no lo veo, en ninguno de los dos códigos y es básico en un Leonardo porque tiene USB nativo pero funciona de este modo.

También he probado a utilizar while(!Serial); dentro de la función setup() de mi sketch, pero obtengo los mismos resultados.

De hecho, he consultado el código fuente de la librería SoftwareSerial y he encontrado las siguientes líneas de código en la función SoftwareSerial::begin(unsigned long speed):

if (port) {
    port->begin(speed);
  } else {
    cycles_per_bit = (uint32_t)(F_CPU + speed / 2) / speed;
    ARM_DEMCR |= ARM_DEMCR_TRCENA;
    ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
  }

El puerto serie se inicializa en el fichero wiring_pulse.s de la biblioteca ArduinoCore. En caso de que no esté disponible, se calcula el número de ciclos por bit, y en la línea de código ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA; se habilita el contador de ciclos.

Tal y como lo veo, estas líneas auxiliares deberían ser suficientes para habilitar la comunicación por UART sin necesidad de una espera adicional.

Te agradezco la ayuda que me estás proporcionando, pero yo creo que sería bueno que el código que me hayas proporcionado, al menos no tuviera errores de compilación.

Sí, en la primera línea has escrito un include <SoftwareSerial.h>, y puestos a ser puristas, eso es un error.

Si te fijas en mis códigos, utilizo comillas dobles en los archivos de cabecera en vez de corchetes angulares, ya que la diferencia está en que al utilizar los <> el precompilador buscará los headers en en los directorios especificados en la variable de entorno INCLUDE_PATH. Si no estuvieran definidos en esta carpeta, daría un error.

He probado el código que me has facilitado, pero sigo teniendo el mismo problema.

Así que he descargado el código fuente de la librería SoftwareSerial y lo he modificado ligeramente para comprobar unas cosas.

En el miembro available() he comentado lo que tenía y he puesto un return port; para que me devolviese el número de puerto que utiliza; en vez de return port->available();.

Y en el miembro read(); también he comentado lo que traía y he escrito return port->read();. Sino, me devuelve un -1.

He usado este código para testear:

//He acortado aquí la ruta por privacidad
#include "/../xxxxSerial.h"    // <=== Editado
//He renombrado la "nueva" librería con un poco de humor
LindoSerial miSerial(10, 11); 

void setup() {

Serial.begin(9600);

miSerial.begin(9600);

}

void loop() {

//Modificado en la librería para obtener el número de puerto interno
int puerto = miSerial.available();

Serial.println(puerto);

miSerial.write("1");

//Devuelve en crudo lo que está leyendo
int lectura = miSerial.read();

Serial.println(lectura);

}

Y lo que imprime por pantalla es:

0
-1

Por lo que he estado leyendo, en el microcontrolador de Arduino hay tres grupos de puertos: B, C y D; de ocho pines I/O cada uno. En el caso del grupo B, los pines van del 8 al 13, por eso he cambiado los puertos; pero los he puenteado como me habías recomendado para el testing en el comentario anterior.

La idea detrás de usar estos pines, es porque el puerto que me devuelve es el 0, que corresponde con el grupo B.

En resumidas cuentas, la configuración del puerto, he visto que viene dado por el fichero wiring_pulse.s, que se encarga de la medición de los pulsos en los GPIO.

En fin, creo que debe haber algún problema en las librerías "principales" de mi Arduino IDE, ya que mi problema, en apariencia sencillo, tiene origen muy profundo.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.