NFC en la Raspberry Pi

Hace tiempo que no escribo en el blog debido a insertar-excusa-que-no-haya-usado-antes-aquí, pero me apetecía compartir el resultado de pasar unas pocas de tardes buscando una buena alternativa para utilizar NFC en la Raspberry Pi.

Las primeras semanas me centré en hacer funcionar el lector NFC ACR122u de ACS, lector que ya había utilizado en la empresa en la que trabajo para hacer una serie de investigaciones con Linux y que arrojaron un resultado aceptable. Pobre de mi, pensaba que con la Raspberry Pi y la misma librería automágica nfcpy que había utilizado en el pasado, encontrar una alternativa estable iba a ser pan comido. No fue así. La Raspberry Pi es un Linux, eso es cierto, concretamente una Debian. Pero la arquitectura no es i386, es ARM (más propia de pequeños dispositivos como smartphones y tablets). A pesar de funcionar más o menos regularmente, el lector ACR122u fallaba de manera aleatoria y sin explicación. Tal y como me comenta un experto en la materia como es Stephen Tiedemann (creador de nfcpy) en este hilo, además de que el lector es de lo peor para estas cosas, el subsistema USB de la Raspberry se muestra muy inestable en cuanto a periféricos más concretos como este (ratones, teclados y demás material básico funciona sin problemas).

El lector NFC ACR122U

El lector NFC ACR122U

Con el primer golpe en la frente, decido no hacerle caso a Stephen que me recomienda probar con una placa PN532 conectable vía serie en plan Arduino, usando electrónica, placas y demás. En su lugar, me tomé la libertad de realizar una pequeña inversión en la placa EXPLORE-NFC NXP PN512 que no tiene soporte por parte de nfcpy pero tiene programas de ejemplo ya escritos en C puro. Esta placa se engancha al puerto de expansión GPIO como si de un conector USB se tratase.

La placa explore-nfc

La placa explore-nfc

Hay programas escritos para los 3 modos de operación de NFC: tag, card emulation y P2P. Un handicap es que el modo P2P sólo tiene escrito un programa para la operación PUT (recibir de Raspberry) pero no para GET (que la Raspberry obtenga del móvil). He escrito un programa Python/GTK como interfaz gráfica hacia este programa P2P ya existente, para probar la operación PUT. El resultado lo podéis ver en el vídeo que adjunto, donde también pruebo la demo del modo Card Emulation, que recoge el valor que asignemos al tag 14443 y lo escribe en el muro de Facebook de mi cuenta dummy.

Enlaces de interés

* Código de ejemplo EXPLORE-NFC:
http://www.element14.com/community/docs/DOC-65447/l/explore-nfc-software-and-project?ICID=knode-devkitnfc-quick

* Comprar EXPLORE-NFC (marcar Business en la ventana modal, aunque seas un particular):
http://es.farnell.com/jsp/displayProduct.jsp?sku=2366201&CMP=KNC-GES-FES-GEN-SKU-MDC

* Código de demo gráfica personal Python/GTK:
https://bitbucket.org/jialvarez/raspberry-mpos

A 1 persona le gusta esta entrada

Conectar programas C/C++ con aplicaciones Android

En esta entrada voy a describir la manera de comunicar aplicaciones escritas en Android con programas escritos en C/C++ vía JNI utilizando el NDK de Android.

El NDK es un conjunto de herramientas que permite incorporar los componentes que hacen uso de código nativo en las aplicaciones de Android. Permite implementar parte de tus aplicaciones usando código nativo con lenguajes como C y C ++. Esto puede proporcionar beneficios a ciertas clases de aplicaciones, en la medida que se puede reutilizar el código existente y en algunos casos obtener un aumento de la velocidad.

Java Native Interface (JNI) es un framework de programación que permite que un programa escrito en Java ejecutado en la máquina virtual java (JVM) pueda interactuar con programas escritos en otros lenguajes como C, C++ y ensamblador.

La estructura de un proyecto Android que utilice JNI y NDK para permitir la intercomunicación entre aplicaciones C/C++ y Android es la siguiente:
carpeta raíz del proyecto/

  • jni/
  • libs/
  • res/
  • src/
  • AndroidManifest.xml
  • default.properties
  • … otros ficheros …

Descripción de los ficheros:

  • La carpeta src contiene el código Java de la aplicación Android
  • La carpeta res contiene los recursos de la aplicación (imágenes, archivos XML que describen las capas de interfaz, etc)
  • La carpeta libs *contendrá* las librerías nativas *después* de una construcción exitosa
  • La carpeta jni *contiene* el código C/C++ de la aplicación con la que queremos comunicarnos, además de dos scripts importantes: Android.mk y Application.mk. Estos scripts son dos Makefiles típicos que controlan el proceso de construcción de la aplicación C++.

Veamos los pasos para ejecutar una sencilla aplicación Android que se comunique con un programa escrito en C++.

  • Descargar y descomprimir NDK
    Sé que hay versiones mucho más actuales, pero aún así, utilizo esta de Crystax que está bastante optimizada.
  1. wget http://www.crystax.net/en/download/android-ndk-r4-linux-x86-crystax-4.tar.bz2 -O /tmp/android-ndk-r4-linux-x86-crystax-4.tar.bz2
  2. cd ~ && tar xvjf /tmp/android-ndk-r4-linux-x86-crystax-4.tar.bz2

  • Crear la carpeta libs en el proyecto
    Pulsamos el botón derecho sobre el proyecto, y escogemos la opción New -> Folder. Escribimos libs como nombre de carpeta.
  • Crear la carpeta jni en el proyecto
    Pulsamos el botón derecho sobre el proyecto, y escogemos la opción New -> Folder. Escribimos jni como nombre de carpeta.
  • Crear el programa C++ con el que queremos comunicarnos
    Pulsamos el botón derecho sobre la carpeta JNI, y escogemos la opción New -> File. Escribimos code.cpp como nombre de fichero. Hacemos doble clic sobre el fichero y escribimos el siguiente código:
  1. #include <jni.h>
  2. #include <iostream>
  3. #include <cstdlib>
  4. #include <getopt.h>
  5. #include <string>
  6. #include <cassert>
  7. #include <stdexcept>
  8.  
  9. using namespace std;
  10.  
  11. extern "C"
  12. {
  13. JNIEXPORT jstring JNICALL Java_blog_neonigma_AndroidActivity_SayHello(JNIEnv *env, jobject thiz, jstring name)
  14. {
  15.   try
  16.   {
  17.     const char *myName = env->GetStringUTFChars(name, 0);
  18.     std::string nameCPP(myName);
  19.     nameCPP = "Hello: " + nameCPP;
  20.     return env->NewStringUTF(nameCPP.c_str());
  21.   }
  22.   catch (exception &ex)
  23.   {
  24.     const char *error = "Failed";
  25.     return env->NewStringUTF(error);
  26.   }
  27. }
  28. }

  • Consideraciones sobre el código:
    • El bloque extern «C» indica que el programa está escrito en C++. Sí, en C++. Si queremos escribirlo en C, eliminamos el bloque extern.
    • La declaración de la función nativa tiene que coincidir con el namespace + nombre de actividad Android + nombre de función en el código Java/Android. En este caso, como nuestra actividad es AndroidActivity y se encuentra en el namespace blog.neonigma, la función debe nombrarse como Java_blog_neonigma_AndroidActivity_SayHello, siendo SayHello el nombre que damos a la función y «Java_» un texto estático.
    • La función siempre recibe un puntero al entorno (JNIEnv *env) y un objeto manejador (jobject thiz)
    • Para convertir const char * en jstring, usamos la función NewStringUTF del entorno
    • Más información en la API oficial de JNI

  • Crear los Makefiles para NDK
    Pulsamos el botón derecho sobre el proyecto, y escogemos la opción New -> File. Escribimos Android.mk como nombre de fichero. El código para este fichero sería el siguiente:
  1. LOCAL_PATH := $(call my-dir)
  2.  
  3. include $(CLEAR_VARS)
  4.  
  5. LOCAL_MODULE    := mixed_sample
  6. LOCAL_SRC_FILES := code.cpp
  7. LOCAL_LDLIBS +=  -llog -ldl
  8.  
  9. include $(BUILD_SHARED_LIBRARY)

Los valores más importantes son code.cpp, que indica el fichero de código con el que queremos comunicar y mixed_sample, que es el nombre que damos a la librería que se va generar vía JNI.

Repetimos el proceso y creamos un fichero Application.mk.

  1. APP_STL := gnustl_static
  2. APP_CPPFLAGS := -frtti -fexceptions
  3. APP_ABI := armeabi
  4. APP_PROJECT_PATH := ~/workspace/blog-jni-c

En este caso, los valores más importantes son la arquitectura armeabi (puede cambiarse por la más moderna armeabi-v7a) y el path del proyecto, donde hay que indicar el path al workspace actual.

  • Crear un builder para la compilación
    Debe generarse un builder en Eclipse para la compilación del código C/C++ con NDK.

    1. Pulsamos en projects -> properties y escogemos la opción Builders. Pulsamos el botón New.
    2. En la ventana que aparece, escogemos la opción Program (sin escribir nada), y pulsamos OK.
    3. En la siguiente ventana configuramos el Builder en sí. En la pestaña Main, escribimos en el campo Location la ruta absoluta al programa ndk-build, en mi caso /home/neonigma/android-ndk-r4-crystax/ndk-build En el campo Working directory, pulsamos en el botón Browse workspace y escogemos el proyecto en el que estamos trabajando.
    4. Ahora vamos a la pestaña Refresh y marcamos Refresh upon completion, Specific resources y Recursively include sub-folders.

      Pulsamos en el botón Specify resources y en la ventana que aparece, escogemos el recurso libs.
    5. Por último, vamos a la pestaña Build Options, dejamos las casillas ya marcadas y marcamos During auto builds y Specify working set of relevant resources.

      Pulsamos en el botón Specify resources y escogemos el recurso jni de nuestro proyecto.
  • Escribir el código de la actividad Android
    Para enviar datos al programa en C++ y recogerlos, necesitamos escribir el código de la actividad principal de la siguiente manera:
  1. package blog.neonigma;
  2.  
  3. import src.blog.neonigma.R;
  4. import android.app.Activity;
  5. import android.os.Bundle;
  6. import android.widget.Toast;
  7.  
  8. public class AndroidActivity extends Activity {
  9.     @Override
  10.     public void onCreate(Bundle savedInstanceState) {
  11.         super.onCreate(savedInstanceState);
  12.         setContentView(R.layout.main);
  13.        
  14.         String greeting = SayHello("neonigma");
  15.         Toast.makeText(this, greeting, Toast.LENGTH_LONG).show();
  16.     }
  17.    
  18.     public native String SayHello(String name);
  19.    
  20.     static {
  21.         System.loadLibrary("mixed_sample");
  22.     }

  • Consideraciones sobre el código
    • La llamada a función nativa se define como public native.
    • String se equipara a jstring, e int a jint, a la hora de enviar y recibir parámetros.
    • Debe definirse como estática la carga del módulo de la librería generada y que especificamos en el código del Makefile Android.mk.

Referencias:
http://opencv.itseez.com/doc/tutorials/introduction/android_binary_package/android_binary_package_using_with_NDK.html#android-binary-package-with-ndk
http://zsoluciones.com/datos/?p=246

El código del ejemplo puede descargarse desde el repositorio creado en BitBucket.

A 1 persona le gusta esta entrada