En esta entrada aprenderemos de una forma sencilla a enviar información desde un Arduino Nano 33 IoT a un Arduino MEGA 2560 (u otro Arduino) mediante la comunicación Serial (UART). Cómo ejemplo, enviaremos el ángulo Roll obtenido del sensor IMU del Arduino Nano 33 IoT al Arduino MEGA 2560. Veremos cómo conectar correctamente los cables entre modelos de Arduino distintos. La idea es aprender a conectar dos modelos de arduino diferentes, comunicarlos entre ellos y enviar un float de un arduino Nano 33 IoT a un MEGA 2560. Este último Arduino puede ser substituido por un Arduino UNO u cualquier otro modelo.
La comunicación Serial es un método muy utilizado en el entorno Arduino. La comunicación Serial o UART (Universal Asynchronous Receiver/Transmitter), se trata de una comunicación asíncrona. Permite la comunicación entre uno o mas Arduino, un ordenador u otros dispositivos. Además, es por Serie que los datos de un Arduino se muestran en nuestro ordenador.
Todas las placas de Arduino tienen, como mínimo un puerto del tipo Serial. En el Arduino UNO, el pin 0 es empleado para la recepción de datos (RX) y el pin 1 para la emisión (TX). Ambos pines también se emplean por el convertidor USB-Serie para transmitir datos por el puerto USB cuando por ejemplo se instala un nuevo programa a la placa. Esto implica que estos conectores no se podrán utilizar mientras que la tarjeta Arduino esté conectada a un ordenador. Cuando estos pines se estén empleando para cualquier tipo de comunicación, no será posible emplearlos como pines digitales. Para lograr la comunicación entre dos arduinos distintos o un Arduino y otro dispositivo, es obligatorio cruzar estos pines, es decir, conectar el TX de un dispositivo al RX del otro.
Cada placa de Arduino tiene sus propios puertos de comunicación Serial. Es bueno siempre, mirar la web de Arduino para localizar y asegurar cuáles son los puertos. En el caso de Arduino Mega 2560, que tiene cuatro puertos serie el RX/TX0, RX/TX1, RX/TX2 y RX/TX3.
CARACTERISTICAS DE LA COMUNICACIÓN RX TX CON UN NANO 33 IOT
En el vídeo vemos que se emplean tres cables, el RX con el TX y a la inversa, además de los GND. Realmente con dos calles sería suficiente porque sólo enviamos datos desde el Arduino Nano al Mega. Si fuera al revés, es decir, del MEGA al NANO podríamos dañarlo porque estamos enviando señales de 5V a un dispositivo de 3.3V. Por lo que en ese caso necesitaríamos de un Level Shifter como el de la imagen:
Como hemos dicho UART es un protocolo de comunicación que se emplea tanto para la comunicación de dos dispositivos como para comunicar datos del Arduino al PC a través del cable USB. En algunas placas, los pines TX y RX y el cable USB están de alguna manera conectados lo que significa que al conectar cualquier cosa a estos pines puede interferir negativamente con la transmisión de datos, llegando a provocar cargas fallidas en la placa. Este no es el caso de las familias Nano o MKR, ya que éstas poseen dos canales separados, utilizando Serial para la comunicación con el ordenador y Serial1 para la comunicación con cualquier otro dispositivo a través de UART. Por lo que si queremos enviar un dato desde un Arduino a otro con el RX-TX debemos inicializar el código con el «Serial1.begin(9600)». Luego para mostrar datos en pantalla emplearemos el «Serial.begin(9600)» sin el 1.
La forma en que indicaremos a Arduino que se emplearan los pines RX-TX para la comunicación es mediante:
Serial1.begin(9600); // Comunicación RX-TX
Sin embargo, con esto no veremos nada en el Monitor Serie, para ello emplearemos como siempre:
Serial.begin(9600);//Comunicación Monitor Serie
Por lo que en el Void Setup tendremos lo siguiente:
void setup() { Serial.begin(9600);// Para la comunicación con el Monitor Serie Serial1.begin(9600); // Para la comunicación RX-TX }
Con el Serial1, sólo podemos trabajar con los pines RX/TX. Por lo que me ha parecido interesante la librería «wiring_private.h» porque con ella se puede emplear cualquier pin digital de emisor y receptor.
El Transmisor:
#include <Arduino.h> #include "wiring_private.h" #include <Arduino_LSM6DS3.h> #include <MadgwickAHRS.h> // initialize a Madgwick filter: Madgwick filter; // sensor's sample rate is fixed at 104 Hz: const float sensorRate = 104.00; char mystr[6] = "Hello"; //String data Uart mySerial (&sercom0, 5, 6, SERCOM_RX_PAD_1, UART_TX_PAD_0); void SERCOM0_Handler() { mySerial.IrqHandler(); } void setup() { if (!IMU.begin()) { Serial.println("Failed to initialize IMU"); // stop here if you can't access the IMU: while (true); } // start the filter to run at the sample rate: filter.begin(sensorRate); pinPeripheral(5, PIO_SERCOM_ALT); pinPeripheral(6, PIO_SERCOM_ALT); // Initialize "debug" serial port // The data rate must be much higher than the "link" serial port mySerial.begin(9600); while (!mySerial) continue; } void loop() { // values for acceleration & rotation: float xAcc, yAcc, zAcc; float xGyro, yGyro, zGyro; // values for orientation: float roll, pitch, heading; if (IMU.accelerationAvailable() && IMU.gyroscopeAvailable()) { // read accelerometer & gyrometer: IMU.readAcceleration(xAcc, yAcc, zAcc); IMU.readGyroscope(xGyro, yGyro, zGyro); // update the filter, which computes orientation: filter.updateIMU(xGyro, yGyro, zGyro, xAcc, yAcc, zAcc); // print the heading, pitch and roll roll = filter.getRoll(); pitch = filter.getPitch(); heading = filter.getYaw(); Serial.print("Orientation: "); Serial.print(heading); Serial.print("; "); Serial.print(pitch); Serial.print("; "); Serial.println(roll); mySerial.println(roll); } }
El Receptor:
void setup() { // Begin the Serial at 9600 Baud Serial.begin(9600); Serial1.begin(9600); while (!Serial) continue; while (!Serial1) continue; } void loop() { if (Serial1.available()) { float lectura1 = Serial1.parseFloat(SKIP_ALL); Serial.println(lectura1); //Print data on Serial Monitor } }
Este ejercicio es muy interesante por varias razones. La primera es que convertiremos el Arduino Nano 33 IoT en un sensor de ángulo. En este caso obtenemos el ángulo Roll y lo mandaremos por el puerto Serie. Por otro lado es importante saber que el Arduino Nano trabaja con 3.3V por lo que sólo admite 3.3 V para los pines de entrada y salida y no tolera los 5V como si sucede en muchísimas placas de Arduino. Como se ha indicado previamente, más de 3.3V podríamos dañar la placa.
Como solucionar problemas
A veces se producen errores y el código no funciona como desearíamos. Algunos problemas son muy comunes y fáciles de solucionar:
- Falta un corchete o un punto y coma.
- La placa de Arduino está conectada al puerto incorrecto.
- La conexión entre las placas de Arduino no es correcta. Comprueben si el RX está conectado al TX y al revés.
- Interrupción accidental de la conexión del cable.
- Cables excesivamente largos.
Enviar y recibir números según Nick Gammon
Nick Gammon en el siguiente enlace describe un protocolo para enviar y recibir valores entre dos Arduinos evitando posibles errores. Para ello emplea el siguiente ejemplo:
Para enviar un número (mayor de 0 a 9) de un Arduino a otro de manera fiable, debe hacer algunas cosas previamente.
- Enviar algún tipo de delimitador de «número de inicio» (en este ejemplo, el carácter «<«).
- «Imprimir» el número, que lo convierte en una cadena de caracteres ASCII.
- Enviar algún tipo de delimitador de «número final» (en este ejemplo, el carácter «>»).
Veamos con un ejemplo, como se envían 10 números aleatorios por el puerto serie:
Para el que transmite los datos:
// Example of sending numbers by Serial // Author: Nick Gammon // Date: 31 March 2012 const char startOfNumberDelimiter = '<'; const char endOfNumberDelimiter = '>'; void setup () { srand (42); Serial.begin (115200); } // end of setup void loop () { Serial.println ("Starting ..."); for (int i = 0; i < 10; i++) { Serial.print (startOfNumberDelimiter); Serial.print (rand ()); // send the number Serial.print (endOfNumberDelimiter); Serial.println (); } // end of for delay (5000); } // end of loop
Para recibir los números necesitamos detectar en nuestro bucle principal si ha llegado algo o no al puerto serie. Si es así, podemos comprobar los caracteres especiales de «inicio» y «finalización». Estos nos dicen cuando empieza un nuevo número y cuándo ha terminado de llegar.
// Example of receiving numbers by Serial // Author: Nick Gammon // Date: 31 March 2012 const char startOfNumberDelimiter = '<'; const char endOfNumberDelimiter = '>'; void setup () { Serial.begin (115200); Serial.println ("Starting ..."); } // end of setup void processNumber (const long n) { Serial.println (n); } // end of processNumber void processInput () { static long receivedNumber = 0; static boolean negative = false; byte c = Serial.read (); switch (c) { case endOfNumberDelimiter: if (negative) processNumber (- receivedNumber); else processNumber (receivedNumber); // fall through to start a new number case startOfNumberDelimiter: receivedNumber = 0; negative = false; break; case '0' ... '9': receivedNumber *= 10; receivedNumber += c - '0'; break; case '-': negative = true; break; } // end of switch } // end of processInput void loop () { while (Serial.available ()) processInput (); // do other stuff here } // end of loop
Nos podemos preguntar ¿porqué es interesante esto?. Bueno, a veces cuando la transmisión de datos tiene lugar dentro de otro proceso, puede que veamos cosas extrañas como que se aglomeren los números y cosas por el estilo. De esta forma se establece un pequeño protocolo que permite evitar este tipo de errores.
El anterior código de recepción no tiene ningún Delay. Funciona a toda velocidad y detecta los números entrantes lo más rápido que puede. Cuando llega un número completo, se procesa (in processNumber).
Puede elegir diferentes delimitadores, solo asegúrese de que tanto el transmisor como el receptor utilicen los mismos. El código de recepción está diseñado para que pueda omitir el delimitador inicial entre números, ya que se supone que un delimitador final inicia un nuevo número.
El código anterior no verifica que el signo menos esté al comienzo del número. Es posible que desee agregar una verificación para eso y si es posible comprobar la recepción de datos no válidos.
Además, para mayor solidez, es posible que desee agregar comprobaciones adicionales (como comprobaciones CRC) para detectar datos descartados o sustituidos.
El código anterior ilustra cómo recibir datos en serie y dividirlos en números discretos. No tiene ninguna comprobación de errores.
Resumen
En este sencillo tutorial aprendimos cómo conectar dos placas Arduino para que puedan comunicarse mediante la comunicación UART. Para más información consultar en Arduino Serial.