Autor: Alberto García Hierro <skyhusker EN handhelds PUNTO org>
Fecha: 2 de mayo de 2005
El objetivo de este trabajo es explicar el proceso de desarrollo de software para un PDA usando Linux tanto en la estación de trabajo como en el propio PDA.
Desarrollar software para un PDA es un proceso algo diferente a desarrollar aplicaciones para una estación de trabajo. Debido a que la potencia de estos dispositivos es bastante pequeña, normalmente no se compila en ellos. En su lugar, usaremos la estación de trabajo para llevar a cabo lo que se denomina compilación cruzada, que consiste en realizar una compilación para una arquitectura distinta a la máquina donde se lleva a cabo. Otra dificultad añadida es que vamos a usar Linux y el PDA viene de fábrica con Pocket PC, de manera que también será necesario instalar un nuevo sistema operativo en él.
Para llevar a cabo todo el proceso usaremos un PDA HP Ipaq H5550 y un iBook G4 corriendo Gentoo Linux con un kernel 2.6. El software que necesitamos en la estación de trabajo es el siguiente:
Comenzaremos eliminando el sistema operativo propietario que se encuentra instalado en el PDA, ya que la aplicación a desarrollar se ejecutará bajo Linux. Para ello lo primero que tenemos que hacer es sutituir el cargador del sistema operativo por otro que nos permita reprogramar la ROM. Se trata de BootBlaster y lo podemos obtener en la página web del proyecto Handhelds.org. Una vez descargado, simplemente lo ejecutamos y seleccionamos la opción "Program" . Al cabo de unos segundos la aplicación nos avisará de que ha termindo su trabajo, de manera que ya podemos resetear el dispositivo y continuar con la instalación.
Para el siguiente paso necesitamos haber descargado una imagen de Familiar, que puede obtenerse en http://familiar.handhelds.org. Una vez terminada la descarga, es el momento de conectarse al PDA a través del puerto de serie para subir la nueva imagen del S.O. y flashear la ROM con ella. La colocamos en su base, lanzamos minicom en la estación de trabajo y lo configuramos para que utilice el puerto de serie donde se conecta el dispositivo, poniendo la velocidad a 115200 y la paridad a None así como desactivando Flow Control, Hardware Handshaking y la cadena de inicialización. Una vez que la comunicación se haya establecido, veremos un prompt donde debemos ejecutar el comando load root. En ese momento, el cargador esperará por la imagen del sistema operativo. Pulsamos la secuencia Control+A, S, seleccionamos el protocolo ymodem y le indicamos el fichero de imagen que previamente hemos descargado. Debido a la baja velocidad del puerto de serie, el proceso de enviar la imagen al PDA llevará alrededor de 45 minutos. Una vez haya terminado, el cargador se encargará de reprogramar la ROM, instalando nuestro flamante sistema operativo libre. Para arrancar usaremos el comando boot y ya podremos ver un sistema Linux totalmente funcional.
Ahora que ya tenemos Linux instalado en el PDA, vamos a preparar el entorno de compilación cruzada. Usaremos este proceso porque es mucho más rápido y versátil que la compilación nativa en el PDA.
Para esta labor, usaremos OpenEmbedded. Consta de un núcleo, llamado bitbake y una serie de ficheros que indican a este núcleo como compilar el software. Comenzamos descargando el núcleo desde su repositorio subversion e instalándolo:
$ svn co svn://svn.berlios.de/bitbake/trunk/bitbake
$ cd bitbake
$ ./setup.py install --prefix=/usr/local --install-data=/usr/local/share
A continuación, descargamos los ficheros bb usando BitKeeper:
$ bk clone bk://openembedded.bkbits.net/openembedded
A continuación, creamos el directorio donde llevaremos a cabo la compilación (por ejemplo, build), y a su vez un subdirectorio dentro de este llamado conf, donde copiaremos el fichero openembedded/conf/local.conf. En él, solo tenemos que cambiar los valores de las siguientes variables:
BBFILES := "/home/fiam/src/oe/openembedded/packages/*/*.bb"
#Ojo, cambiar por la ruta apropiada
MACHINE = "h3900"
TARGET_OS = "linux"
DISTRO = "familiar"
Antes de comenzar a compilar, debemos de preparar el entorno para bitbake:
$ export BBPATH=$(pwd)/build:($pwd)/openembedded
Ahora ya podemos comenzar a construir nuestro entorno de compilación cruzada. Este proceso tardará unas cuantas horas, así que una vez llegados a este punto es recomendable tomarse un descanso ;).
$ cd build
$ bitbake qte libpcap openssl sqlite3 libpcap libetpan
OPIE es el entorno gráfico que servirá de soporte a nuestra aplicación. Este entorno tiene la ventaja de basarse en la versión embebida de QT, y por lo tanto, no necesita de un servidor X sino que pinta directamente sobre el framebuffer. Para descargarlo usaremos cvs:
$ export CVSROOT=:pserver:anoncvs@cvs.handhelds.org:/home/cvs
$ cvs login
#Introducimos anoncvs como contraseña
$ cvs -z3 co opie
Preparar la compilación de OPIE es muy sencillo. Usa un sistema de construcción muy similar al del kernel. Vamos a configurarlo usando el interfaz ncurses:
$ cd opie
$ export OPIEDIR=$(pwd)
$ make menuconfig
En este menú debemos seleccionar al menos todas las librerías de OPIE y el gestor de ventanas. También se pueden seleccionar tantas aplicaciones como se desee, siempre que se satisfagan las dependencias. En cuanto a la única opción relevante para la compilación cruzada es la máquina de destino. Simplemente se debe escoger la opción OpenEmbedded e indicar el directorio donde lo hemos construido (en este caso, /home/fiam/src/oe/build/tmp. Una vez configurado, salimos del interfaz de configuración escogiendo guardar los cambios y ejecutamos make para que la compilación se lleve a cabo.
Ya tenemos todos los binarios necesarios construidos, pero tenemos que configurar nuestra estación de trabajo para que el PDA pueda acceder a ellos a través de la red. Vamos a utilizar NFS, ya que es el sistema más simple para realizar esta tarea. Suponiendo que el servidor NFS ya está instalado sólo necesitamos añadir una línea similar a esta en /etc/exports:
/home 192.168.5.2 (rw,no_root_squash)
#Ojo, no hacer esto sin saber los riesgos que puede conllevar.
#Básicamente, no debería utilizarse esta configuración a menos
#que la red donde resida la máquina sea totalmente confiable.
Es necesario reiniciar nfsd para que los cambios tengan efecto:
$ /etc/init.d/nfs restart
#En otras distribuciones probablemente será diferente.
Para conectar el PDA en red, vamos a usar la conexión USB a través de la base. En el PDA no es necesario instalar nada, pero en la estación de trabajo necesitamos el módulo del kernel usbnet. Una vez que tengamos el módulo cargado y ambos dispositivos conectados a través de la base, en los dos aparecerá una nueva interfaz de red llamada usb0 en la estación de trabajo y usbf en el PDA. Con asignarles direcciones de la misma red ya tendremos la conexión lista.
#En la estación de trabajo
$ ifconfig usb0 192.168.5.1
#En el PDA
$ ifconfig usbf 192.168.5.2
Para poder acceder a los ficheros compartidos, es necesario montar el volumen NFS:
$ mount 192.168.5.1:/home /media/net
El PDA ya cuenta con su propia instalación de OPIE, y será la que ejecute por defecto. Para que ejecute la que acabamos de compilar, es necesario indicarlo explícitamente a través de las variables de entorno PATH y LD_LIBRARY_PATH:
$ export STAGING=/media/net/fiam/src/oe/build/tmp/staging/arm-linux/
$ export OPIEDIR=/media/net/fiam/src/oe/opie
$ export PATH=${OPIEDIR}/bin:${STAGING}/bin:${PATH}
$ export LD_LIBRARY_PATH=${OPIEDIR}/lib:${STAGING}/lib:${LD_LIBRARY_PATH}
Para lanzar la nueva versión de OPIE es necesario reiniciarlo con el comando:
$ /etc/init.d/opie restart
Ahora crearemos una original aplicación de ejemplo: un botón con el texto "¡Hola Mundo!". La forma más sencilla de compilarla es colocándola dentro del directorio de OPIE. Para este ejemplo, usaremos noncore/apps/hola:
$ mkdir noncore/apps/hola
$ cd noncore/apps/hola
Ahora abrimos nuestro editor favorito y creamos el fichero hola.cpp:
#include <qpe/qpeapplication.h>
#include <qpushbutton.h>
int main( int argc, char **argv )
{
QPEApplication app( argc, argv );
QPushButton hola( "¡Hola Mundo!", 0 );
hola.resize( 100, 30 );
app.setMainWidget( &hola );
hola.show();
return app.exec();
}
El siguiente paso es crear un fichero que indique como construir la aplicación, llamado hola.pro:
TEMPLATE = app
CONFIG += qt warn_on release
DESTDIR = $(OPIEDIR)/bin
HEADERS =
SOURCES = hola.cpp
TARGET = hola
INCLUDEPATH += $(OPIEDIR)/include
DEPENDPATH += $(OPIEDIR)/include
LIBS += -lqpe
include ( $(OPIEDIR)/include.pro )
También es necesario un fichero que indique al menú de configuración las dependencias de la aplicación, así como si debe estar seleccionada por defecto. Este fichero debe llamarse obligatoriamente config.in.
config HOLA
boolean "opie-myhello (really cool application which says 'Hello' to the World)"
default "y"
depends ( LIBQPE || LIBQPE-X11 ) && LIBOPIE2CORE
El último paso para añadir es colocar una línea como esta en el fichero packages, situado en el directorio raíz de OPIE:
CONFIG_HOLA noncore/apps/hola hola.pro
El sistema de construcción genera varios ficheros de configuración y dependencias durante la construcción. Para que se tenga en cuenta la nueva aplicación debemos eliminar los existentes, de manera que en la próxima compilación se reconstruyan. Para ello ejecutamos el comando make clean-configs en el directorio de OPIE.
Una vez realizados todos los pasos, ejecutamos make y la nueva aplicación se construirá. Para ejecutarla simplemente habría que lanzarla usando su nombre, en este caso hola.
Siguiendo los mismos pasos que para el ejemplo, sería posible desarrollar prácticamente cualquier aplicación. El único cambio necesario en cuanto al sistema de construcción, sería ajustar las dependencias acorde lo que necesite. Evidentemente, también habría que cambiar el código fuente ;).
A modo de ejemplo, aquí podemos ver una captura de una aplicación que he estado desarrollando en las últimas semanas:
Esta aplicación, llamada OpieStumbler, detecta redes inalámbricas de forma activa, basándose en las extensiones wireless del kernel. Su código fuente está disponible bajo la licencia GPL y se encuentra alojado en el CVS de OPIE, más concretamente en el directorio noncore/net/opiestumbler.