CONECTAR ARDUINO A UNITY ANDROID POR USB. MODIFICAR EL ANDROIDMANIFEST.XML DE UNITY PARA ANDROID E INCLUIR ARCHIVOS DE RESOURCES MEDIANTE SCRIPTS.

Vamos a ver como comunicar Arduino y Unity por Usb. En esta parte aprenderemos a conectar el arduino como Host y como Otg y que el dispositivo Android lo detecte y arranque la aplicación Unity.

En este artículo aprenderemos a hacerlo modificando el AndroidManifest.xml de Unity para android mediante scripts. Y por último, en otro artículo, veremos como programar una libreria de Android para realizar la comunicación entre los dos dispositivos.

1. Android Usb Accessories y Usb Host.

   Cuando conectamos Android por Usb a otro dispositivo, podemos hacerlo de dos formas: como accesorio o como host. La diferencia radica en cual de los dispositivos actúa como host, que es el que aporta la corriente al bus Usb y enumera los dispositivos Usb.

   Si usamos el puerto del arduino que usamos para conectarlo al PC entonces necesitamos un adaptador OTG conectado al dispositivo android. En este caso este último es el que aporta la corriente de alimentación al bus y a nuestro arduino.
1. Conexión mediante OTG.

   En el caso de que el arduino actúe como Host, necesitaremos usar un shield usb para arduino o bién alguna de las placas que ya tienen integrado un puerto usb host, como es el caso del MEGA ADK.
2. Conexión por puerto Usb Host de Arduino.

   Ambas formas de conexión tienen su correspondiente Api en la parte android y su correspondiente librería en la parte de arduino.

2. Apis android


   El modo USB Accessory está disponible en android a partir de la versión 3.1. Está diseñado para dispositivos que cumplan con las especificaciones del protocolo de accesorios de android (Android Development Kit o ADK). Es lo que también se conoce como Android Open Accessories o AOA. En el caso de arduino tenemos librerías para proveer estas especificaciones como son la adk.h y la androidaccessories.h que podemos encontrar en varias versiones en GitHub.

   El modo USB Host el dispositivo Android es el que alimenta el bus y enumera los dispositivos conectados. Es lo que comunmente llamamos conexión por OTG. En este caso el arduino se comunica con el dispositivo android mediante el puerto USB que usamos para conectar con el PC y bastará con usar la comunicación serie habitual.

   En los dos casos android provee las clases necesarias para establecer la comunicación. Principalmente UsbManager, UsbAccessory y UsbDevice, aparte de algunas más. Esto lo veremos más tarde cuando nos ocupemos de como establecer la comunicación.

   Para que podamos usar el accesorio o el dispositivo Usb es necesario que incluyamos algunas cosas en el AndroidManifest de nuestra aplicación. Son las siguientes:

   - Elemento <uses-feature> para declarar que la aplicación usa la característica android.hardware.usb.accessory o la android.hardware.usb.host.
   - Hay que establecer el SDK mínimo de la aplicación por encima de la versión 12. En las inferiores a esta no se pueden usar ninguno de los dos tipos de dispositivos usb.
   - Si queremos recibir notificaciones de que se ha conectado un dispositivo hay que especificar el correspondiente elementos <intent-filter> y <meta-data> para los intent android.hardware.usb.action.USB_ACCESSORY_ATTACHED o para el android.hardware.usb.action.USB_DEVICE_ATTACHED. El elemento <meta-data> apuntará a un fichero de recursos XML externo en el que declararemos la información de identificación del dispositivo que conectemos. Estos ficheros hay que guardarlos en el directorio res/xml/. El nombre del fichero debe ser igual que el que hemos consignado en el elemento <meta-data>. En el caso de un accessory declararemos un elemento <usb-accesory> que podrá tener los siguientes atributos:

        - manufacturer
        - model
        - version

   En el caso de un Usb Device usaremos un elemento <usb-device> que podrá contener los atributos:

        - vendor-id
        - product-id
        - class
        - subclass
        - protocol (device o interface)

3. Ejemplo de archivos Manifest y recursos.         

   En el caso de un accessory hay que modificar el AndroidManifest incluyendo las siguiente líneas:


   En el caso de un dispositivo quedaría así:


   El fichero de recursos sería  res/xml/accessory_filter.xml y los atributos se especificarían de la forma:

   Lo que pone entre comillas en model y manufacturer lo modificaremos a nuestro gusto. Eso si, tienen que ser los mismos que pongamos en el sketch de Arduino.

   Y para un dispositivo res/xml/device_filter.xml de la siguiente forma:

   He puesto algunos de los id de los arduinos que tengo. Puedes añadir todos los que quieras o dejar solo uno. Puedes consultar estos datos en el mismo IDE de Arduino pidiendo información de la placa que tienes conectada, como por ejemplo:


   También podemos discriminar por el número de serie del dispositivo.

   Se usará un ejemplo u otro según como queramos conectar nuestro arduino, pero también se pueden declarar los dos tipos en la misma aplicación, que es lo que voy a hacer en el ejemplo práctico que vamos a ver.

4. Arduino ADK y Usb Host.


   En la parte del Arduino, tenemos dos casos también. Si conectamos mediante OTG por el puerto USB habitual que llevan por ejemplo el Arduino Uno (imagen 1.) Nos bastará con usar los comando de comunicación serie que usamos para comunicarnos con el PC. El sistema Android detectará que se trata de un arduino por los datos que vamos a declarar en el archivo device_filter.xml.

   Si conectamos como Accesorio tenemos que usar una de las librerías que ya existen para controlar el puerto Usb Host que hemos añadido como shield o que ya está integrado en la placa.
3. Puertos USB del Arduino Mega ADK y USB Host Shield para Arduino UNO.

   Si hacemos una busqueda en GitHub de Arduino y Usb podemos encontrar varias versiones. Por ejemplo esta:

https://github.com/felis/USB_Host_Shield_2.0

 
   Un ejemplo de sketch para arduino sería este:



   Como veis los datos que he puesto de ejemplo son los mismos que en el fichero accessory_filter.xml y con esto lo que conseguimos es que al conectar los dispositivos como en la imagen 2, la app que hemos creado con los datos anteriores en el AndroidManifest.xml, arranque automaticamente. Si no encuentra ninguna aplicación que tenga declarado un accesorio con estos datos nos preguntará si queremos ir a la dirección que hemos puesto como URI. Esta puede ser directamente un repositorio de software, el acceso a la app en Google Play, una página web con información sobre el dispositivo, etc.

   Si por el contrario conectamos el arduino al adaptador OTG, tal y como podemos ver en la imagen 1, y si coincide el id de dispositivo con el que hemos declarado en el archivo device_filter.xml, tendremos el mismo efecto. En este caso no hay que hacer nada en la programación de arduino para que lo reconozca.


   Como se puede ver en el vídeo, podemos hacer que al conectar un arduino, ya sea como host o como accesorio, arranque una aplicación de Unity3d. Esto es lo que hacemos en las declaraciones descritas anteriormente del AndroidManifest.xml y los ficheros de recursos. Luego nos quedaría la parte de programación que vendrán en artículos posteriores. Tendremos que escribir código cuando nos lleguen los avisos de que el dispositivo ha sido desconectado y para la comunicación serie por el puerto Usb. Ahora vamos a ver como hacer todo esto desde Unity.

5. Configuración de la aplicación de Unity3d. Modificación del AndroidManifest.

   Voy a usar la versión 2019.1.0f2 de Unity que es la que tengo instalada en el momento de escribir este artículo. Cuando generamos un archivo .apk desde Unity para Android, este nos genera los archivos necesarios como el AndroidManifest. En versiones anteriores a la 2018.2 si queríamos hacer modificaciones a este archivo bastaba con incluir un archivo manifest en la carpeta plugins/android de nuestro proyecto y Unity lo "mezclaba" con el suyo propio, además de hacer lo mismo con los que incluían los plugins que pudieramos estar usando. También podíamos incluir en esa carpeta los archivos de recursos xml dentro de la carpeta /res. Ahora esto no es posible. Si nos vamos a la documentación de Unity al respecto (los links a toda la información están al final del artículo) nos dice que lo que hace es general el manifest principal y lo mezcla con los de los plugins que estemos usando. Si queremos tener un control sobre el manifest tenemos que generar un manifest y ponerlo en la ruta Assets/Plugins/Android/AndroidManifest.xml. Unity toma este como principal y lo mezcla con los que tienen los plugins. La forma que nos indica que usemos para introducir nuestros cambios es que los pongamos en un plugin. O bien que exportemos nuestro proyecto a Android Studio, realicemos las modificaciones y añadamos los archivos de recursos y luego lo compilemos ahí.

   Después de muchas pruebas os voy a contar las dos formas, una manual y otra por scripting, que he encontrado para hacer las modificaciones que necesitamos para que funciona la detección de los dispositivos usb y que podamos usarlos en nuestra aplicación de Unity.

   Vamos ha realizar las modificaciones de forma manual primero y os diré los inconvenientes que hay.

   El fichero de manifiesto generado por Unity se encuentra en la ruta Temp/StagingArea/AndroidManifest.xml pero solo después de realizar un build del proyecto. Lo que vamos a hacer es:

   1. Iniciamos un proyecto nuevo en Unity.
   2. En build settings cambiamos la plataforma a Android.
   3. En el Player Settings modificamos el Package Name a uno válido.
   4. Si queremos ponemos algún objeto en la escena pero no es necesario. La grabamos y la incluimos con Add Open Scenes en la lista Scenes In Build.
   5. Hacemos Build del proyecto.
   6. Cuando termine nos vamos a la carpeta Temp/StagingArea/ de nuestro proyecto y pasamos el archivo AndroidManifest.xml a la carpeta Assets/Plugins/Android/.
   7. Editamos el archivo e introducimos los cambios que hemos descrito antes. Lo guardamos en la ruta Assets/Plugins/Android/AndroidManifest.xml para que Unity lo use como manifest principal. También grabaremos nuestros dos archivos  res/xml/accessory_filter.xml y/o el res/xml/device_filter.xml
manteniendo esta ruta y dentro de la carpeta Android/.

   Con esto ya podemos ejecutarlo en nuestro dispositivo Android y probarlo. Esto tiene el inconveniente de que tenemos que hacer los cambios manualmente y que si modificamos configuraciones de nuestra aplicación que afecten al archivo de manifiesto tendremos que estar constantemente haciéndolo. También puede dar problemas a la hora de que Unity mezcle el archivo de manifiesto con los que llevan los plugins si estamos usando alguno.

   Para solucionar esto Unity ha creado el interface IPostGenerateGradleAndroidProject con el método OnPostGenerateGradleAndroidProject. Por el nombre podéis imaginar que este método es llamado por el editor después de generar el proyecto android y podemos usarlo para modificar el AndroidManifest.xml. Encontré un script en GitHub que he adaptado añadiendo algunos métodos. El original está en "este enlace" y mi versión es la siguiente:



   Esto generará los cambios justo antes de generar la apk. Podemos comprobarlo si después de hacer el build de la app miramos el archivo de manifiesto que ha generado Unity.

   Por otro lado quedan los archivos de resources. Podemos ponerlos directamente en el directorio res como indico en el punto 7 anterior, pero Unity nos dirá que esto está obsoleto (deprecated). Por eso he escrito un script también para ser ejecutado por el editor y que copia los archivos en la carpeta res de proyecto generado por Unity usando la misma técnica anterior:



   Para que funcione tenemos que poner los archivos dentro de nuestra carpeta Assets en la ruta \AndroidUsb\res\xml. Se puede modificar el script para poner una ruta diferente. Lo que hace es copiar todos los archivos .xml que encuentra dentro de esta ruta y respetendo la estructura de carpetas dentro del directorio res cuando genera el proyecto android.

   Si tenéis prisa por probar, solo tenéis que descargar los archivos del siguiente repositorio de GitHub: https://github.com/Lamolda/LMDSmart-Unity-Android-Usb

   Este y el resto de enlaces de interés están al final del artículo. Encontraréis el sketch de arduino y también el archivo de Unity con los Scripts y los ficheros necesarios que solo tenéis que arrastrar y soltar en un proyecto cualquiera de Unity. También está una versión de la apk.

   Solo queda hacer el plugin para comunicar los dispositivos. Esto será en un nuevo artículo. Espero vuestros comentarios. Gracias.


Enlaces de interés:

 - https://developer.android.com/guide/topics/connectivity/usb/accessory   - https://source.android.com/devices/accessories/protocol
 - https://developer.android.com/guide/topics/connectivity/usb/host
 - https://docs.unity3d.com/es/current/Manual/android-manifest.html
 - https://github.com/felis/USB_Host_Shield_2.0
 - https://youtu.be/SKXBfDuzWUo
 - https://github.com/Lamolda/LMDSmart-Unity-Android-Usb
 - https://docs.unity3d.com/ScriptReference/Android.IPostGenerateGradleAndroidProject.html

Comentarios

  1. hola como estas, estas escribiendo un excelente articulo, cuando saldra la segunda parte? Para concluir con los mensajes seriales al arduino? Se te agradece por la informacion dada hasta el momento

    ResponderEliminar
    Respuestas
    1. Hola. Me alegra que te guste. Me estoy retrasando con los siguientes artículos por falta de tiempo. Pero estoy en ello. Espero tenerlos listos pronto. Gracias.

      Eliminar
  2. Hola, muy buen articulo ¿Que paso con la segunda parte?

    ResponderEliminar
  3. What androidAccessory library do you use? Your AndroidAccessory.ino compiled with errors, and in LMDSmartUsbTest.apk I see empty scene.Thanks.

    ResponderEliminar

Publicar un comentario