Asignar un nombre fijo al puerto serie USB de Arduino

publicado en: Portada | 4

Un uso relativamente frecuente para una placa Arduino, que se trató en el artículo cobre la configuración de las comunicaciones serie entre Arduino y Python, es servir de intermediario para una batería de sensores y actuadores con los que adquirir datos, como temperatura, humedad, presión, movimiento… y controlar dispositivos desde una aplicación que los analice.

Mientras que no representa especial inconveniente seleccionar la placa Arduino en la fase de desarrollo porque seguramente no habrá muchas conectadas o el usuario se puede tomar la molestia de elegirla entre unas pocas deduciendo el puerto serie al que se conecta, puede ser una molestia tener que demorar el inicio de una aplicación en producción a la selección de la conexión serie por parte de un operador.

Lo más práctico es poder elegir automáticamente el puerto serie identificando la placa Arduino. La manera más directa de hacerlo en Linux es creando un enlace al dispositivo concreto en una fase previa de configuración que se podrá almacenar de manera estable para toda la vida productiva del Arduino.

Identificar cada placa Arduino.

Para crear el enlace al dispositivo concreto en primer lugar es necesario identificarlo de una forma unívoca. Seguramente la manera eficaz de hacerlo, y que se puede utilizar en el caso de Arduino, es referirse a su número de serie.

La orden dmesg muestra un histórico de los dispositivos que se han ido detectando o desconectando a lo largo del tiempo. Si volvemos a conectar la placa Arduino a un puerto USB la información correspondiente aparecerá al final y podrá verse algo como lo mostrado en la captura de pantalla de abajo.

Información con dmesg del último dispositivo (Arduino) conectado

En las últimas líneas que muestra dmesg se puede encontrar toda la información necesaria. En concreto habrá que recabar el identificador del producto, el identificador del fabricante y el número de serie. Estos datos aparecen, respectivamente, como idProduct, idVendor y SerialNumber (el último de los dos que aparece y el más largo)

Si el dispositivo ya está en uso se puede conocer el puerto serie real al que está conectado. Por ejemplo, el IDE más reciente de Arduino muestra el nombre del modelo de la placa conectada a los puertos serie encontrados.

Identificando el puerto serie de la placa Arduino desde el IDE

En la captura de pantalla anterior puede verse que el IDE de Arduino ha detectado dos placas, una conectada a /dev/ttyACM0 y otra a /dev/ttyACM1, la primera un Arduino Leonardo y la segunda un Arduino Uno.

Estos datos son muy importantes. Buscar esa información entre los nodos de dispositivos habría sido bastante incómodo y tendría el riesgo añadido de tener que distinguir entre los diferentes dispositivos serie conectados.

Listado de los dispositivos (nodos) en /dev

Una vez que se conoce el puerto serie al que está conectada la placa Arduino, se pueden obtener individualmente los datos que muestra dmesg con la orden udevadm. El inconveniente es que hay demasiada información y es laborioso encontrar lo necesario; es preferible ir filtrando con grep la salida para buscar los tres datos necesarios: fabricante, producto y número de serie con la siguiente secuencia de órdenes:

Información del fabricante con udevadm info
Información del producto con udevadm info
Información del número de serie con udevadm info

De las tres cadenas de datos obtenidas con las tres órdenes anteriores la información necesaria es la mostrada en primer lugar (la primera línea) que corresponde con el dispositivo último, la placa Arduino, en este caso.

Al llegar hasta aquí, sea usando dmesg, sea usando udevadm info, ya se saben los datos que identifican a la placa Arduino cuyo puerto serie se quiere distinguir automáticamente.
En este ejemplo son:

  • ATTRS{idVendor}=="2341"
  • ATTRS{idProduct}=="0043"
  • ATTRS{serial}=="A4139373630351A05011"

Crear reglas para los nodos /dev de dispositivos concretos.

Con la información obtenida en el apartado anterior, los tres datos que identifican el dispositivo, es posible crear reglas udev para que los nodos que se crean en /dev tengan un comportamiento concreto. Para referirse por un nombre predecible a la placa Arduino lo más práctico es darle un nombre creando un alias (enlace simbólico) al puerto serie real al que se conecte, que podrá variar.

Las reglas se definen como documentos de texto dentro de la carpeta /etc/udev/rules.d y es un convenio aceptado preceder el nombre con un número de orden y usar la extensión .rules, aunque no es imprescindible. Se puede crear un documento por cada regla o agrupar varias en un mismo documento.

Los documentos deben crearse con privilegios de administrador. Por ejemplo, desde la consola se puede editar con sudo nano /etc/udev/rules.d/arduinas.rules suponiendo, lógicamente, que el nombre del documento en el que se van a definir las reglas para las placas Arduino es «arduinas.rules» En mi caso, voy a crearlo con Kate desde KDE con kdesudo kate /etc/udev/rules.d/arduinas.rules

Editando reglas udev con Kate en KDE

En un escritorio con GTK, por ejemplo Cinnamon, para editar las reglas con Geany podría usarse la orden gksudo geany /etc/udev/rules.d/arduinas.rules

Editar las reglas udev con gksudo geany para establecer nombres fijos para la conexión serie USB de Arduino

Cada línea del documento arduinas.rules debe contener la definición de una regla, en este caso la identificación de una placa Arduino y la información sobre qué hacer cuando se conecte. Como decía antes, la acción que debe llevarse a cabo al conectar una placa Arduino al sistema consiste en crear un alias que enlace el puerto serie que asigne el sistema al conector USB a un nombre conocido que se usará para comunicar con Arduino en una aplicación como se explica en la configuración de las comunicaciones serie entre Arduino y Python.

Los parámetros que deben incluirse en cada línea de definición de una regla, separándolos por comas, son:

  • SUBSYSTEM=="usb" Para expresar que se trata de un dispositivo USB
  • ATTRS{idVendor}=="2341" Para referirse al identificador del fabricante
  • ATTRS{idProduct}=="0043" Para referirse al identificador del producto
  • ATTRS{serial}=="A4139373630351A05011" Indicando el número de serie de la placa
  • SYMLINK+="nombre" Siendo «nombre» el del enlace al que se hará referencia en lugar del puerto serie real
  • GROUP="dialout" Para otrogar permisos al grupo que corresponda (dialout en el ejemplo)
  • MODE="0664" Para gestionar los permisos del propietario, del grupo y de otros usuarios del dispositivo

El contenido del documento arduinas.rules quedaría, por ejemplo, con el siguiente texto:

Además de indicar la acción (crear un enlace) y la identificación de la placa por el tipo de dispositivo y el número de serie, parece evidente que hay que usar un nombre diferente para el enlace que se crea para cada placa («nombre» en el ejemplo) así como especificar el tipo de dispositivo (USB) que corresponde.

Al conectar una de las placas reconocidas, además del puerto serie correspondiente, puede verse que se crea el enlace en la carpeta /dev como si fuera un nodo más. En el ejemplo anterior, al conectar la placa cuyo número de serie corresponde con el nombre «carmela» aparecería un enlace como el de la siguiente captura de pantalla:

Enlace simbólico (alias) a una placa Arduino asingnándole un nombre fijo por el número de serie

El último de los parámetros sirve para que el nuevo dispositivo que se añade como nodo a /dev pueda ser usado por los miembros de cierto grupo («dialout» en el ejemplo) El usuario de la aplicación que usa este puerto serie debe pertenecer a dicho grupo para usar los permisos que asigna de esta forma la regla.

El usuario debe pertenecer al grupo indicado por la regla udev en la que se asigna nombre fijo al puerto serie de la placa Arduino

Se supone que una vez definidas las reglas, udev es capaz de detectar los cambios en los documentos automáticamente y usarlos cuando se conecta un nuevo dispositivo. La realidad es que esto no siempre ocurre, para asegurarse de que se conocen las nuevas reglas se puede forzar que se carguen inmediatamente con udevadm control --reload-rules y obligar a que se activen con udevadm trigger

Recargar las reglas udev con udevadm control --reload-rules y udevadm trigger

Una forma un poco radical, pero efectiva, de hacer lo anterior y olvidarse de lo que sea que haga falta para reiniciar las reglas es re-lanzar (reiniciar) el script de inicio de udev. Con la distribución Debian que uso en mi escritorio, lo hago con sudo /etc/init.d/udev restart como puede verse en la siguiente captura de pantalla.

Recargar las reglas udev en Debian con /etc/init.d/udev restart

Víctor Ventura

Desarrollando aplicaciones para la web conocí el potencial de internet de las cosas, encontré la excusa perfecta para satisfacer la inquietud de aprender electrónica que había tenido desde siempre. Ahora puedo darme el gusto de programar las cosas que yo mismo diseño y fabrico.

Más entradas - Página web

Sígueme:
TwitterLinkedIn

Seguir Víctor Ventura:

Programador multimedia y web + IoT. Mejor con software libre.

Desarrollando aplicaciones para la web conocí el potencial de internet de las cosas, encontré la excusa perfecta para satisfacer la inquietud de aprender electrónica que había tenido desde siempre. Ahora puedo darme el gusto de programar las cosas que yo mismo diseño y fabrico.

4 Respuestas

  1. Manolo

    Buenas, información muy útil.
    Una consulta, es posible una vez conectados dos arduinos a la pc utilizar un solo codigo python para recibir datos de ambos? Por ejemplo

    # Abrir Directorio
    path = «/dev/serial/by-id/»
    dirs = os.listdir( path )
    for lista in dirs:
    arduino = serial.Serial(‘/dev/serial/by-id/’+lista,9600)
    print arduino.readline()
    arduino.close()

    Saludos

    • Víctor Ventura

      Hola, Manolo.

      Sí es posible. Como es lógico tienes que hacer algún tipo de procesado para ordenar el lío que se va a organizar, pero poderse, se puede; de hecho tu ejemplo funciona.

      Reescribo tu código, si no te importa, que al ponerlo en el comentario sale un poco raro y a lo peor puede confundir a alguien.

      ¡Gracias por tu comentario!

  2. Eduardo

    Buenos días Víctor,

    Quiero agradecerte que hayas compartido tu conocimiento en esta página. Sin gente como vosotros el mundo iría más despacio.

    El reto al que me enfrento es el siguiente. He desarrollado una aplicación en python que hace uso de múltiples dispositivos USB con los que interactúa. Uno de estos dispositivos es Arduino.

    Gracias a tu información consigo que todos los dispositivos tengan una referencia coherente (vía enlace simbólico) independientemente de la que les asigne linux en el arranque. Pretendo que la aplicación sea a prueba de usuarios manazas y así detecte si el usuario no conectó Arduino o lo desconectó a mitad de transacción. Esto funciona y cada vez que reconecto arduino tengo asignado el mismo dispositivo simbolico. Pero la comunicación me falla. Arduino no me responde a mis peticiones de información desde Python.

    He comprobado que si hago que mi aplicación funcione enviando y recibiendo información del arduino y simultaneamente arranco el monitor serie del IDE de arduino, este último se hace con el control del puerto USB correspondiente al arduino y monopoliza la comunicación con el arduino.

    ¿Cómo hace esto el IDE para poder recuperar el control del puerto cada vez que reconecto el Arduino?

    PD: La inicialización del puerto USB se hace correctamente, pues si no le hago perrerías, funciona perfectamente.
    El caso es que cuando desconecto Arduino al volver a reconectar

    .

    • Víctor Ventura

      Hola, Eduardo.

      Me alegro de que el artículo te haya resultado útil.

      No termino de entender muy bien el problema que tienes ¿Sería posible que facilitaras algún código con el que reproducir el comportamiento? A lo mejor eso nos ayuda a buscar alguna solución.

      Gracias por tus elogios y por participar en polaridad.es 🙂

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *