El GPS es el caballo de batalla de los servicios basados en localización. Sin embargo, existen casos de uso en los cuales probablemente desees evitar el costo y el consumo de energía que suponen el hardware GPS o la localización de dispositivos en sitios en los que estos sistemas carezcan de precisión; por ejemplo, en entornos urbanos o edificios.

Recientemente, hemos observado un aumento en el número de aplicaciones relacionadas con la Internet de las cosas (IoT) que usan la Google Maps Geolocation API en lugar de GPS para el seguimiento de recursos, la prevención de robos, la optimización en el uso y el mantenimiento de recursos, entre otros aspectos. Como parte de mi proyecto relacionado con la propuesta del 20 por ciento del tiempo, en el área de soluciones industriales, creé un prototipo de dispositivo de IoT que puede localizarse a sí mismo a través de redes WiFi circundantes y de la Google Maps Geolocation API. En esta entrada, trataré algunas funciones de implementación interesantes y señalaré la forma en que tú mismo puedes crear el prototipo.

Creé un dispositivo que realiza un análisis en busca de redes WiFi locales y registra, mediante escritura, los resultados (los hotspots WiFi y la intensidad de su señal) en una base de datos en tiempo real de Firebase. Un servicio de backend luego lee estos datos y usa la Google Maps Geolocation API para convertirlos en una ubicación real que puede representarse en un mapa.

Configurar el dispositivo y realizar operaciones de escritura a nivel local

Para esta prueba de concepto, usé el módulo Intel Edison como plataforma de cálculo basada en Linux y lo expandí con bloques Sparkfun para Edison. Para crear el dispositivo, necesitarás un módulo Intel Edison, un bloque básico un bloque de batería y un paquete de hardware.

Realizar desarrollos para Edison es sencillo con el IDE de Intel XDK. Crearemos una aplicación Node.js simple en JavaScript. Me basé en 3 bibliotecas: Firebase para la conexión con la base de datos, wireless-tools/iwlist para captar redes WiFi y macaddress para captar la dirección MAC del dispositivo. Las instrucciones de instalación pueden hallarse en las páginas vinculadas.

Paso 1: obtener la dirección MAC del dispositivo y establecer la conexión con Firebase.
function initialize() {
    macaddress.one('wlan0', function (err, mac) {
        mac_address = mac;
        if (mac === null) {
            console.log('exiting due to null mac Address');
            process.exit(1);
        }
        firebase.initializeApp({
            serviceAccount: '/node_app_slot/<service-account-key>.json',
            databaseURL: 'https://<project-id>.firebaseio.com/'
        });
        var db = firebase.database();
        ref_samples = db.ref('/samples');
        locationSample();
    });
}
El código anterior contiene dos marcadores de posición:

  1. service-account-key es una clave privada que se crea en la consola de Firebase. Usa el ícono de ajustes de la parte superior izquierda de la consola, selecciona “Settings” y haz clic en Generate New Private Key. Dispón esta clave en tu Edison, dentro del directorio /node_app_slot/. Para obtener más información, consulta esta documentación de Firebase.
  2. project-id, en la URL de la base de datos, se encuentra en la página de la base de datos de la consola de Firebase una vez que vinculas tu proyecto de Google con Firebase.


Paso 2: buscar redes WiFi cada 10 segundos y realizar operaciones de escritura a nivel local.
function locationSample() {
    var t = new Date();
    iwlist.scan('wlan0', function(err, networks) {
        if(err === null) {
            ref_samples.push({
                mac: mac_address,
                t_usec: t.getTime(),
                t_locale_string: t.toLocaleString(),
                networks: networks,
            });
        } else {
            console.log(err);
        }        
    });
    setTimeout(locationSample, 10000);
}

Realizar operaciones de escritura en la nube

La función locationSample() de arriba registra, mediante escritura, redes WiFi detectables en una base de datos de Firebase que se sincroniza con la nube al establecerse una conexión con una red.

Advertencia: Para establecer derechos de acceso y la autenticación de Firebase, se configura el dispositivo como “servidor”. En el sitio web de Firebase, se encuentran disponibles instrucciones para esta configuración. Para esta prueba de concepto, supuse que el dispositivo era suficientemente seguro como para alojar nuestras credenciales. Si esto no se aplica a tu implementación, como alternativa debes seguir las instrucciones para configurar el JavaScript SDK de cliente.

La base de datos usa 3 colas para administrar la carga de trabajo: una de ejemplos de WiFi, una de resultados de ubicación geográfica y una de datos de visualización. El flujo de trabajo será el siguiente: los ejemplos del dispositivo van en una cola de ejemplos, que se consume para producir ubicaciones geográficas dispuestas en una cola de ubicación geográfica. Las ubicaciones geográficas se consumen, reciben formato para su presentación y se organizan por dispositivo, y el resultado se almacena en un depósito de visualizaciones para su uso a través del sitio web de front end.

A continuación, se ofrecen un ejemplo, una ubicación geográfica, y los datos de visualización escritos por el dispositivo y observados en la consola de la base de datos de Firebase.

Procesamiento de los datos con Google App Engine

Para ejecutar el procesamiento de los datos de ejemplo, usé un módulo de backend de Google App Engine de ejecución prolongada y una versión personalizada del Java Client for Google Maps Services.

Advertencia: Para usar Firebase con App Engine, debes aplicar escalamiento manual. Firebase usa subprocesos en segundo plano para detectar cambios y App Engine solo permite subprocesos en segundo plano de larga duración en instancias de backend de escalamiento manual.

Java Client for Google Maps Services se encarga de una gran parte del código de comunicaciones necesario para usar las Maps API y aplica nuestras prácticas recomendadas publicadas para las estrategias de manejo de errores y reintentos que respetan límites de tasa. La función GeolocateWifiSample(), a continuación, se registra como un detector de eventos con Firebase. Realiza ciclos en cada red indicada por el dispositivo y la incorpora a la solicitud de ubicación geográfica.
private void GeolocateWifiSample(DataSnapshot sample,  Firebase db_geolocations, Firebase db_errors) {
    // initalize the context and request
    GeoApiContext context = new GeoApiContext(new GaeRequestHandler()).setApiKey("");
    GeolocationApiRequest request = GeolocationApi.newRequest(context)
            .ConsiderIp(false);
    // for every network that was reported in this sample...
    for (DataSnapshot wap : sample.child("networks").getChildren()) {
        // extract the network data from the database so it’s easier to work with
        String wapMac = wap.child("address").getValue(String.class);
        int wapSignalToNoise = wap.child("quality").getValue(int.class);
        int wapStrength = wap.child("signal").getValue(int.class);
        // include this network in our request
        request.AddWifiAccessPoint(new WifiAccessPoint.WifiAccessPointBuilder()
                .MacAddress(wapMac)
                .SignalStrength(wapStrength)
                .SignalToNoiseRatio(wapSignalToNoise)
                .createWifiAccessPoint());
    }
    ...
    try {
        // call the api
        GeolocationResult result = request.CreatePayload().await();
        ...
        // write results to the database and remove the original sample
    } catch (final NotFoundException e) {
        ...
    } catch (final Throwable e) {
        ...
    }
}
Registra la función GeolocateWifiSample() como un controlador de eventos. Los demás receptores que procesan resultados de ubicación geográfica y crean los datos de visualización se crean con un patrón similar.
ChildEventListener samplesListener = new ChildEventListener() {
    @Override
    public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
        // geolocate and write to new location
        GeolocateWifiSample(dataSnapshot, db_geolocations, db_errors);
    }
    ...
};
db_samples.addChildEventListener(samplesListener);

Visualizar los datos

Para visualizar las ubicaciones de dispositivos, usé Google App Engine a fin de proporcionar datos almacenados de Firebase y de la Google Maps JavaScript API, con los cuales creé una página web simple en la que se muestran los resultados. La página index.html contiene un <div> vacío con ID “map”. Inicialicé este <div> para contener el proyecto de Google Map. También agregué los controladores de eventos “child_added” y “child_removed” para actualizar el mapa a medida que los datos cambien con el paso del tiempo.
function initMap() {
    // attach listeners
    firebase.database().ref('/visualization').on('child_added', function(data) {
        ...
        data.ref.on('child_added', function(vizData) {
            circles[vizData.key]= new CircleRoyale(map,
                                vizData.val().lat,
                                vizData.val().lng,
                                vizData.val().accuracy,
                                color);
          set_latest_position(data.key, vizData.val().lat, vizData.val().lng);
        });
        data.ref.on('child_removed', function(data) {
            circles[data.key].removeFromMap();
        });
    });
    // create the map
    map = new google.maps.Map(document.getElementById('map'), {
      center: get_next_device(),
      zoom: 20,
      scaleControl: true,
    });
    ...
}
Debido a que la API no solo muestra una ubicación sino también un indicio de precisión, creé un indicador personalizado con un radio de pulso para indicar el componente de precisión.
Dos dispositivos (rojo y azul) y las últimas cinco posiciones conocidas de estos.

Lo que viene

En esta entrada, indiqué la manera en que puedes crear un dispositivo de IoT que use la Google Maps Geolocation API para realizar un seguimiento de cualquier dispositivo conectado a Internet, desde los robóticos hasta los wearables. El módulo de procesamiento de App Engine puede expandirse para usar otros servicios web de Google Maps API que proporcionen datos geográficos, como las indicaciones, la elevación, los lugares o la zona horaria. ¡A disfrutar de la codificación!

Como alternativa, puedes lograr una solución similar usando Google Cloud Platform como sustituto de Firebase; en este artículo se muestra la forma de hacerlo.

author image
Acerca de Ken: Ken es jefe del equipo de soluciones industriales de Google. Trabaja junto a los clientes para aportar soluciones innovadoras al mercado.