Conectando IOS + SWIFT + BLE Sensor Tag + Acelerómetro

Standard

Buen día, hoy veremos un tema que tuvimos que atacar recientemente.

Por requerimientos de un cliente necesitamos conectar una aplicación hecha en Swift para IOS con un dispositivo BLE 4.0 para poder obtener los datos del acelerómetro.

 

Antes de empezar pondré el link de la página donde saqué la biblioteca para convertir los datos:

https://github.com/StevenRudenko/BleSensorTag

Ahora sí comencemos.

Lo primero es crear un proyecto IOS.

newproject

(Como podemos observar mi xcode está en inglés, como recomendación usen todo en inglés incluyendo el teclado, por experiencia recomiendo el teclado en inglés)

 

Ahora utilizaremos esta info para nuestro proyecto de prueba, pueden cambiar la información que gusten, estos datos son los de su proyecto.

 

newproject2

Después de hacer click en “next”, seleccionamos el lugar para guardar el proyecto y listo, tendremos nuestro proyecto de prueba preparado.

Ahora lo que haremos será crear dentro de nuestro proyecto un archivo swift donde pondremos todo nuestro código para este ejemplo lo he llamado “ExampleBLE.swift”.

newfile

Como vemos nuestro archivo sólo tiene una línea de código que vamos a eliminar y reemplazar con lo siguiente:

Archivo: ExampleBLE.swift

 

//
//  ExampleBLE.swift
//  simplebletest
//
//  Created by hunabsys on 8/3/15.
//  Copyright (c) 2015 Hunabsys. All rights reserved.
//

//Clases base necesarias para el trabajo con bluetooth
import CoreBluetooth

//Nuestra clase necesita extender de NSObject, CBCentralManagerDelegate y CBPeripheralDelegate
//La primera NSObject es requerida por las otras 2.
//CBCentralManagerDelegate y CBPeripheralDelegate nos permiten utilizar eventos necesarios
//para nuestra funcionalidad
class ExampleBLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate {
    
    //Este objeto nos permitirá conectarnos y buscar nuestro dispositivo
    var central : CBCentralManager!
    //Variable que guardará la referencia a nuestro dispositivo
    var disp : CBPeripheral!
    
    //Constante con el nombre de nuestro dispositivo
    let dispName = "Nombre Dispositivo"
    
    //El identificador del servicio que necesitamos, en este caso Acelerómetro
    let ACCEL_SERVICE = "F000AA10-0451-4000-B000-000000000000"
    //Caracteristica de velocidad de refresco
    let REFRESH_RATE_CHARACTERISTIC = "F000AA12-0451-4000-B000-000000000000"
    //caracteristica de valores de acelerómetro
    let ACCEL_VALUE_CHARACTERISTIC = "F000AA11-0451-4000-B000-000000000000"
    
    //En este constructor simplemente se inicializa nuestro central manager, recordemos
    //que este objeto será el encargado de buscar y conectar a nuestro dispositivo.
    override init(){
        super.init()
        central = CBCentralManager(delegate: self, queue: nil, options: nil)
        
    }
    
    
    func centralManagerDidUpdateState(central: CBCentralManager!) {
        if central.state != .PoweredOn {
            //Manejamos de alguna manera que no esté encendido el bluetooth
            //por ahora lo dejo simplemente en blanco y no se hace nada
            return
        }
        //Iniciamos la búsqueda de dispositivos
        central.scanForPeripheralsWithServices(nil,options:nil)
        
    }
    //Este método es simplemente para ver si nuestro dispositivo se ha desconectado, en una app real debería
    //enviar alguna notificación al usuario.
    func centralManager(central: CBCentralManager!, didDisconnectPeripheral peripheral: CBPeripheral!, error: NSError!) {
        println("Disconnected")
    }
    //En caso de no poderse conectar al dispositivo manejamos el error. al igual que el método anterior, debiera
    //notificarle al usuario.
    func centralManager(central: CBCentralManager!, didFailToConnectPeripheral peripheral: CBPeripheral!, error: NSError!) {
        println(error)
    }
    //En este método en caso de encontrar dispositivos disponibles validamos que sea el que buscamos mediante el "name"
    //y en caso de encontrarlo guardamos la referencia y nos conectamos al dispositivo.
    func centralManager(central: CBCentralManager!, didDiscoverPeripheral peripheral: CBPeripheral!, advertisementData: [NSObject : AnyObject]!, RSSI: NSNumber!) {
        println(peripheral.name)
        if (peripheral.name == dispName){
            println("Found")
            println(peripheral.description)
            self.disp = peripheral
            central.connectPeripheral(disp, options: nil)
            
        }
    }
    //En caso de que nos hayamos podido conectar al dispositivo, le decimos que somos el delegado del mismo, esto
    //permitirá utilizar los métodos que inician con "peripheral", por último buscamos los servicios
    //que el dispositivo tiene disponibles.
    func centralManager(central: CBCentralManager!, didConnectPeripheral peripheral: CBPeripheral!) {
        println("Connected")
        disp.delegate = self
        disp.discoverServices(nil)
        
    }
    
    //En caso de que se encuentren servicios provistos por el dispositivos, se va a ejecutar este método
    //lo que hacemos en este ejemplo es simplemente ver si alguno de los servicios es el que nos interesa
    //aquí buscamos el servicio de acelerómetro. Si alguno de ellos coincide, pasamos a descubrir las
    //características de dicho servicio.
    func peripheral(peripheral: CBPeripheral!, didDiscoverServices error: NSError!) {
        for service in peripheral.services {
            println(service.UUID)
            if service.UUID == CBUUID(string: ACCEL_SERVICE){
                println("Looking for characteristics")
                self.disp.discoverCharacteristics(nil, forService: service as! CBService)
                break;
            }
        }
    }
    //Una vez encontradas las caracteristicas del servicio que buscamos llegaremos a este método y así como lo hicimos
    //con los servicios vamos a buscar dentro de las caracteristicas las que nos interesan.
    func peripheral(peripheral: CBPeripheral!, didDiscoverCharacteristicsForService service: CBService!, error: NSError!) {
        for characteristic in service.characteristics as! [CBCharacteristic]{
            println(characteristic)
            //En caso de que la característica sea la de REFRESH_RATE vamos a asignar un valor (Nota, este valor me lo
            //proporcionó el desarrollador del dispositivo y me comentó que era necesario para que el dispositivo me
            //envíe los valores, y en efecto sin este cambio no me envia información el dispositivo.
            if characteristic.UUID == CBUUID(string: REFRESH_RATE_CHARACTERISTIC){
                println("Receive Values Activated")
                self.disp.writeValue(NSData(bytes: [0x01], length: 1), forCharacteristic: characteristic, type: CBCharacteristicWriteType.WithResponse)
            }else if characteristic.UUID == CBUUID(string: ACCEL_VALUE_CHARACTERISTIC){
                //En caso de que la característica sea la de acelerómetro vamos a decirle que cada vez que
                //se actualice el valor del acelerómetro se nos notifique.
                self.disp.setNotifyValue(true, forCharacteristic: characteristic)
            }
        }
    }
    //Por último, cada vez que recibamos un valor del acelerometro se ejecutará este código.
    //Aquí utilizamos la biblioteca proporcionada en el link al final del código para convertir los valores
    //en bruto enviados por la característica a algo entendible que en este caso son los valores de x, y y z.
    func peripheral(peripheral: CBPeripheral!, didUpdateValueForCharacteristic characteristic: CBCharacteristic!, error: NSError!) {
        println("DATA: @", characteristic.value)
        let allValues = SensorTag.getAccelerometerData(characteristic.value)
        let accelerometerX = allValues[0]
        let accelerometerY = allValues[1]
        let accelerometerZ = allValues[2]
        println("X: ", accelerometerX)
        println("Y: ", accelerometerY)
        println("Z: ", accelerometerZ)
        
    }
    //Con este método evaluamos que el cambio a la característica se haya hecho correctamente.
    func peripheral(peripheral: CBPeripheral!, didWriteValueForCharacteristic characteristic: CBCharacteristic!, error: NSError!) {
        println("Updated")
        println(error)
    }

    
}

 

(Una disculpa por el formato del código, estamos buscando un plugin que nos permita solucionar este detalle.)

Lo siguiente es agregar este archivo a nuestro proyecto:

SensorTag.swift

Como notarán este es el archivo que hará la “magia” de convertir los datos obtenidos en bruto a nuestros valores x, y & z.

 

Lo agregamos a nuestro proyecto y listo, en teoría todo debiera funcionar como seda.

Eso es todo por ahora, en caso de cualquier duda o comentario no duden en hacerlos, espero y a alguien le sirva esta información.

 

PD.- Otra recomendación además de la del teclado en inglés sería el usar sólo inglés para programar y el nombrado de variables, inclusive los comentarios. Para fines explicativos estamos mezclando español e inglés lo cual es PÉSIMO, pero bueno, después de todo es un blog en español.

 

Que tengan un buen día.

Leave a Reply

Your email address will not be published. Required fields are marked *