¿Cómo funciona el SensorTag?
Un video introductorio acerca de cómo funciona el SensorTag puede ser visto aquí.
Video 2: Cómo diseñar aplicaciones.
Video 3: Recolectar datos de un cohete.
Un proyecto intersante es el que desarrolló ByteWorks, que se trata de recolectar información de un cohete mediante el uso de los datos del SensorTag, en el siguiente link se pueden ver todos los detalles del proyecto e indicaciones para emularlo. >> Click aquí
Escribiendo programas Bluetooth Low Energy
A continuación se presenta un tutorial resumido de cómo escribir programas BLE para el SensorTag con el uso de la aplicación techBASIC para iOS. Para efectos de mejor comprensión y para que no se presenten ambiguedades, se presenta el tutorial tal cual fue escrito por ByteWorks en idioma inglés.
With the hardware in hand, it's time to write the software. The complete, fully commented program is available as a download at the end of the article, so you don't need to retype what you see here.
Before jumping into the code, though, let's stop and get an overview of how Bluetooth low energy devices are designed. Bluetooth low energy devices package information in services. In our program, we will use six of these services, one for each sensor. This article will show how to connect to one of them, the accelerometer, but the software download handles all six. The sensors themselves, and the details on how to use them, are covered in the six follow-on blogs.
Other devices might offer a heart rate service or a servo control service. Each service has characteristics, which work more or less like variables. Some can only be read, some can only be written, and some can be both read and written. There can be more than one characteristic for a service. For example, the accelerometer has three characteristics, one to start or stop the accelerometer, one to set the rate at which it will return data, and one to read the actual data. Services can also have other sub-services imbedded in them; these are called included services. Characteristics can have descriptors with additional information about the characteristic. The figure shows the services and characteristics we'll use on the SensorTag. This is a simplification of the complete specification for the communication protocol for the SensorTag. The full specification is called the GATT profile, available for download from the TI web site here. There is also a lot of supplemental information on each sensor in the wiki. You don't need the GATT profile or wiki to follow along, but learning to read these documents will help if you have to figure out another Bluetooth low energy device.
Almost all Bluetooth low energy calls are asynchronous, since it may take some time for the operating system to communicate with the device to carry out an operation. Your program can use that time to make new requests, handle information passed back from old requests, or simply to do something else while waiting for the results of a call. From the program's perspective, the program begins by making a call, then moves on. At some point in the future the operating system will call a subroutine in the program to report the results. You will see this pattern over and over in the SensorTag program.
Let's get going with the program. The first step in connecting to a Bluetooth low energy device is to start the Bluetooth low energy service with the call
BLE.startBLE
Next we begin scanning for devices. This allows our program to look for any Bluetooth low energy device in the area and connect to the one—or ones—that have the information or capabilities we want. In general, you should already know the kind of service you are looking for. Each service has a 16 or 128 bit identifier for the service, called the service UUID. The shorter 16 bit identifiers are supposed to be assigned by the Bluetooth standards committee. You can find a list of the standard services at http://developer.bluetooth.org/gatt/services/Pages/ServicesHome.aspx. Anyone is free to create a service using a 128 bit UUID.
The values shown in the figure are actually only part of the 128 bit UUID for the characteristics and services. These values make up the second and third byte of a much longer UUID. The complete UUID for the accelerometer service looks like this:
F000AA00-0451-4000-B000-000000000000
The UUID string for other services and characteristics are formed by replacing the fourth through eighth digits with the appropriate value form the table, so the calibration UUID for the barometer looks like this:
F000AA43-0451-4000-B000-000000000000
In our case, we're going to scan for any Bluetooth low energy peripheral in the area by passing an empty array of UUIDs to the startScan method.
DIM uuid(0) AS STRING |
At this point, the iPhone or iPad starts looking around for any Bluetooth low energy device in the area. Bluetooth low energy devices advertise their presence with short, infrequent radio signals that our program is scanning for. As the iOS device finds Bluetooth low energy devices, it calls a subroutine in your program called BLEDiscoveredPeripheral. Here's the implementation from our program.
! Set up variables to hold the peripheral and the characteristics
! for the battery and buzzer.
DIM sensorTag AS BLEPeripheral
! Start the BLE service and begin scanning for devices.
debug = 1
BLE.startBLE
DIM uuid(0) AS STRING
BLE.startScan(uuid)
! Called when a peripheral is found. If it is a Sensor Tag, we
! initiate a connection to it and stop scanning for peripherals.
!
! Parameters:
! time - The time when the peripheral was discovered.
! peripheral - The peripheral that was discovered.
! services - List of services offered by the device.
! advertisements - Advertisements (information provided by the
! device without the need to read a service/characteristic)
! rssi - Received Signal Strength Indicator
!
SUB BLEDiscoveredPeripheral (time AS DOUBLE, peripheral AS BLEPeripheral, services() AS STRING, advertisements(,) AS STRING, rssi)
IF peripheral.bleName = "TI BLE Sensor Tag" THEN
sensorTag = peripheral
BLE.connect(sensorTag)
BLE.stopScan
IF debug THEN PRINT "Discovered SensorTag."
END IF
END SUB
Since we're more interested in a kind of device rather than a kind of service, we check here to see if we've found the device we're looking for by looking at the name of the peripheral the iPhone or iPad found. If it matches TI BLE Sensor Tag, we found what we are looking for. Ideally, things like the name of the peripheral and the peripheral UUID will be in the documentation, but in practice, you may have to write just this much of the program and print the name of any peripheral you find to figure out the name to use.
Once we find the device, we save the peripheral in a global variable. This is important—it keeps the memory manager from disposing of the peripheral's object when the subroutine ends, which would tell the operating system we're not interested in this peripheral. Next we attempt to connect to the peripheral using BLE.connect. The last step is to stop scanning for other Bluetooth low energy devices, which can drain the battery of the iOS device and the Bluetooth low energy devices. We do this with BLE.stopScan.
This tiny program is actually a complete, working program to find and connect to a SensorTag. To see it in action, run the program, then press the pairing button on the left side of the SensorTag. It will take a second or two for the iPhone and SensorTag to set up communications, then you should see Discovered SensorTag.printed to the console.
Connecting to the device does not happen right away. The operating system asks for access and, once it gets a response, calls another subroutine called BLEPeripheralInfo.
! Called to report information about the connection status of the
! peripheral or to report that services have been discovered.
!
! Parameters:
! time - The time when the information was received.
! peripheral - The peripheral.
! kind - The kind of call. One of
! 1 - Connection completed
! 2 - Connection failed
! 3 - Connection lost
! 4 - Services discovered
! message - For errors, a human-readable error message.
! err - If there was an error, the Apple error number. If there
! was no error, this value is 0.
!
SUB BLEPeripheralInfo (time AS DOUBLE, peripheral AS BLEPeripheral, kind AS INTEGER, message AS STRING, err AS LONG)
IF kind = 1 THEN
! The connection was established. Look for available services.
IF debug THEN PRINT "Connection made."
peripheral.discoverServices(uuid)
ELSE IF kind = 2 OR kind = 3 THEN
IF debug THEN PRINT "Connection lost: "; kind
BLE.connect(sensorTag)
ELSE IF kind = 4 THEN
! Services were found. If it is one of the ones we are interested
! in, begin discovery of its characteristics.
DIM availableServices(1) AS BLEService
availableServices = peripheral.services
FOR s = 1 to UBOUND(services, 1)
FOR a = 1 TO UBOUND(availableServices, 1)
IF services(s) = availableServices(a).uuid THEN
IF debug THEN PRINT "Discovering characteristics for "; services(s)
peripheral.discoverCharacteristics(uuid, availableServices(a))
END IF
NEXT
NEXT
END IF
END SUB
We also need to add some lines at the start of the program. The new lines are shown in blue.
! Set up variables to hold the peripheral and the characteristics
! for the battery and buzzer.
DIM sensorTag AS BLEPeripheral
! We will look for these services.
DIM servicesHeader AS STRING, services(1) AS STRING
servicesHeader = "-0451-4000-B000-000000000000"
services(1) = "F000AA10" & servicesHeader : ! Accelerometer
accel% = 1
! Start the BLE service and begin scanning for devices.
debug = 0
BLE.startBLE
DIM uuid(0) AS STRING
BLE.startScan(uuid)
This subroutine can get called for a variety of reasons. After a BLE.connect call, we can get back the "connection complete" response or be told that the connection failed. Later, the connection might be lost—perhaps we wondered too far away from the peripheral. The kind parameter tells us why the call was made. Our program asks the peripheral for a list of any services it provides by calling the peripheral's discoverServices method when a connection is made, and tries to reconnect if a connection is lost. As the peripheral reports back on any services, the subroutine is called again with kind set to 4. Since we've enabled the debug output, the program will print a list of the available services.
Eventually we'll expand the array to hold all six services, but for now we'll restrict our attention to the accelerometer, so the array only has one element.
The code in BLEPeripheralInfo checks to see if the service reported by the device is the accelerometer and, if so, asks the service for a list of the available characteristics using the peripheral's discoverCharacteristics method.
By this time, you probably have a good idea what will happen next. As with the services, the characteristics are reported to the program by calling a subroutine. In this case, the subroutine is BLEServiceInfo. The name might seem odd, but the information is about a service, not the characteristic. The operating system is telling us the service has information. Here's the implementation.
! Called to report information about a characteristic or included
! services for a service. If it is one we are interested in, start
! handling it.
!
! Parameters:
! time - The time when the information was received.
! peripheral - The peripheral.
! service - The service whose characteristic or included
! service was found.
! kind - The kind of call. One of
! 1 - Characteristics found
! 2 - Included services found
! message - For errors, a human-readable error message.
! err - If there was an error, the Apple error number. If there
! was no error, this value is 0.
!
SUB BLEServiceInfo (time AS DOUBLE, peripheral AS BLEPeripheral, service AS BLEService, kind AS INTEGER, message AS STRING, err AS LONG)
IF kind = 1 THEN
! Get the characteristics.
DIM characteristics(1) AS BLECharacteristic
characteristics = service.characteristics
FOR i = 1 TO UBOUND(characteristics, 1)
IF service.uuid = services(accel%) THEN
! Found the accelerometer.
SELECT CASE characteristics(i).uuid
CASE "F000AA11" & servicesHeader
! Tell the accelerometer to begin sending data.
IF debug THEN PRINT "Start accelerometer."
DIM value(2) as INTEGER
value = [0, 1]
peripheral.writeCharacteristic(characteristics(i), value, 0)
peripheral.setNotify(characteristics(i), 1)
CASE "F000AA12" & servicesHeader
! Turn the accelerometer sensor on.
IF debug THEN PRINT "Accelerometer on."
DIM value(1) as INTEGER
value(1) = 1
peripheral.writeCharacteristic(characteristics(i), value, 1)
CASE "F000AA13" & servicesHeader
! Set the sample rate to 100ms.
DIM value(1) as INTEGER
value(1) = 100
IF debug THEN PRINT "Setting accelerometer sample rate to "; value(1)
peripheral.writeCharacteristic(characteristics(i), value, 1)
END SELECT
END IF
NEXT
END IF
END SUB
We're only interested in the first kind of call, where the operating system is telling us the service has updated its list of characteristics. If kind is 1, we get the characteristics for the service with a call to the service's characteristics method and loop over them, looking for characteristics we're interested in. There's only one so far, but the list will grow in the final program.
But wait: It looks like the characteristics are part of the service, which we knew after the call to BLEPeripheralInfo. Why go to all this trouble? The reason is that the operating system doesn't ask the device for a list of characteristics until the discoverCharacteristics call, since it doesn't want to waste battery power asking for information unless it is really needed. You can call the service's characteristics method in BLEPeripheralInfo, but it will return an empty array.
There is more than one way to read a value from the device. The accelerometer uses a method called notification, where the device notifies the iPhone each time a new value is available. We turn notifications on and start them with the calls
DIM value(2) as INTEGER
value = [0, 1]
peripheral.writeCharacteristic(characteristics(i), value, 0)
peripheral.setNotify(characteristics(i), 1)
The first two lines set up the value to write to the peripheral. The accelerometer expects two bytes, a 0 and a 1, to turn on. The next line writes these values to the characteristic, telling it to report information using notifications. The last line actually starts the notifications.
The other method would look similar. If the accelerometer supported individual read commands, you could use the readCharacteristics call, like this:
peripheral.readCharacteristic(characteristics(i))
The BLECharacteristicInfo subroutine gets called with either notifications or reads, the difference being that it will only be called once after a read, and will be called every time a new value is available for notifications.
The next two characteristics are used to turn the accelerometer on and set the sampling rate. They work pretty much like the write used earlier to tell the device to report information using notifications. It may seem odd to send information to the command using an array with only one element, but that's because the software making the call to the device really has no idea how many bytes to send, so it sends all of the bytes in any array you pass. In these cases, the device only expects a single byte. For the AA12 characteristic, a 1 turns the accelerometer on and a 0 turns it off. For the AA13 characteristic, the value is the number of tens of milliseconds between samples, so passing 100 tells the device to send back an acceleration once a second.
The operating system calls the BLECharacteristicInfo subroutine when the device reports a change to a characteristic. Here's a simplified version of the subroutine that appears in the complete program.
! Called to return information from a characteristic.
!
! Parameters:
! time - The time when the information was received.
! peripheral - The peripheral.
! characteristic - The characteristic whose information
! changed.
! kind - The kind of call. One of
! 1 - Called after a discoverDescriptors call.
! 2 - Called after a readCharacteristics call.
! 3 - Called to report status after a writeCharacteristics
! call.
! message - For errors, a human-readable error message.
! err - If there was an error, the Apple error number. If there
! was no error, this value is 0.
!
SUB BLECharacteristicInfo (time AS DOUBLE, peripheral AS BLEPeripheral, characteristic AS BLECharacteristic, kind AS INTEGER, message AS STRING, err AS LONG)
IF kind = 2 THEN
DIM value(1) AS INTEGER
value = characteristic.value
SELECT CASE characteristic.uuid
CASE "F000AA11" & servicesHeader
! Update the accelerometer.
c = 64.0
p% = value(1)
IF p% BITAND $0080 THEN p% = p% BITOR $FF00
lastAccelX = p%/c
p% = value(2)
IF p% BITAND $0080 THEN p% = p% BITOR $FF00
lastAccelY = p%/c
p% = value(3)
IF p% BITAND $0080 THEN p% = p% BITOR $FF00
lastAccelZ = p%/c
PRINT lastAccelX, lastAccelY, lastAccelZ
END SELECT
ELSE IF kind = 3 AND err <> 0 THEN
PRINT "Error writing "; characteristic.uuid; ": ("; err; ") "; message
END IF
END SUB
This subroutine is called with a kind of 2 when the accelerometer reports back with a new value. The value it returns is packed into a three-byte array, where each byte is the acceleration value along one axis. These are signed values with a range of -128 to 127, so we can't just use them as-is. Instead, the program checks to see if the value is negative and, if so, extends the sign bits to form a two-byte signed integer.
Run the program. It may connect to the peripheral right away if you have already run an earlier version of the program. If not, push the pairing button on the SensorTag to establish a connection. After a second or two, you should start to see a spew of accelerometer data on the console telling you the G forces experienced by the SensorTag. Twist it around slowly to verify that the value changes.
Bluetooth es una especificación que posibilita la transmisión de voz y datos entre diferentes dispositivos mediante un enlace por radiofrecuencia.Mas »
BLE es la primera tecnología abierta de comunicación inalámbrica, que ofrece comunicación entre dispositivos móviles o computadores y otros dispositivos más pequeños (de pila de botón). Mas»
Sensortag es un dispositivo construido por Texas Instrument diseñado para aminorar el tiempo de diseño para aplicaciones Bluetooth de meses a sólo horas. Mas»
Copyleft © 2013 - Ningún derecho reservado - Nicolás Oneto | Gonzalo Rojas | Gustavo Sazo
UTFSM, Redes de Computadores II