Testing Bluetooth functionality requires both the app be running on a physical device but also a physical Bluetooth device to communicate with. Once you have an app to test, using the SensorTag allows you to test the functionality in a real-world situation.
- [Instructor] So now we're going to add some additional features to our app. We're going to allow the user to tap the buttons on the physical device, our Bluetooth LE device. That will update our app that they pressed the keys, and then we're going to send back some commands to turn on and off the lights and the buzzer on the sensor tag. To do this, we're going to need to use the IO service provided by the sensor tag. There's two characteristics, the data and the config, and the first thing we need to do is to store the characteristic for the data so that we can write to it in the later parts of the execution of the app.
We also need to enable the remote mode on the configuration to allow the sensor tag to be remotely controlled for the lights and the buzzer. We'll have to write a function to toggle the lights and the buzzer by writing to the characteristic, and in the didUpdateValue where we capture the keys being pressed, we'll call that logic to do the toggling. So what we see is that as the user presses the buttons on either side of the sensor tag, the data's sent to the iPhone app.
The app determines in its logic, in our case, the toggleIO function, what value to write back to the characteristic that the sensor tag then uses to toggle on and off the lights and the buzzer. So, the first thing we want to do is to head back over to our tear down of the GATT table. Look for the IO service. Here's the server's UUID. We can also see that the IO Data UUID is AA65, and the config is AA66.
So we can create those constants like we have for other services and characteristics. We can also see that to enable the remote control, we need to set the characteristic to one. This will allow the app to control the lights. In writing the data to turn the lights on and off, we see that bit zero is red, bit one is green, and bit two is the buzzer. This will allow us to change the value that we write to the device to turn on and off the various characteristics.
So, back in the code, let's create out constants for the service. So on line 16, I'm going to add let svcIO, and set that equal to CBUUID and initialize it with the string. This is the string that I just copied from the tear down. I also need to create the two characteristics, so I'll copy and paste line 16 twice. We need a characteristic for the config and a characteristic for the data.
So I'll change line 17 to charIOConfig and line 18 to the characteristic data. The data was the AA65, and the config was the AA66. Now when we discover the service, we can discover its characteristics and handle them accordingly. So in didDiscoverServices, we want to make sure that we discover the characteristics when we find this service. So in line 87, I'll add an else for the service UUID to be compared to the svcIO.
When that's the case, we want to discover the characteristics, so I'll just copy line 85 into my if clause at line 88. So now we're discovering the characteristics. When the characteristics are discovered, we want to handle them accordingly, too. We want to enable the characteristic just like we did for the light sensor, so I'll copy lines 98 to 105 and paste them just below it. On line 106, I'll change this to be an else if, and I'll change the characteristic we're looking for to be the IO config.
For the characteristic for the IO data, I'll add another else down at line 114. For if char.uuid == charIOData, we want to store this. So we need to create a property in our class. We'll call it charIO and set it equal to this characteristic. So back at the top, I want to create that property.
So on line 25, I'll add the variable property charIO. That's a CBCharacteristic. I'll make that an optional. So at this point, we should be discovering our service and characteristic and even enabling it for remote control. However, we're not doing anything with it. We need to have a value to write to the characteristic to turn the lights and buzzer on and off. I'm going to create another property called ioValue or ioVal, and that's of type UInt8.
And I'll give that a default value of the hex value zero. This is what we'll use to send to the characteristic to update the light state of on, off, and the buzzer. So now we want to write the function that actually controls this. I'm going to put that down by the function we have for didUpdateValueForCharacteristic. I'm going to call this function toggleIO. So on line 162, I'll write func toggleIO, and this is where I'm going to increment the property that we just created.
ioVal += the hex value of one. I want to make sure it doesn't get too high. We know that we're only using the first three bits for the values for the lights and the buzzer, so if our value goes above that, I want to cycle it around back to zero. So on line 164, I'm going to check if ioVal equal to hex value of eight, set it back to zero.
Now we should cycle through all the possible values for the lights and the buzzer and back around again. Just to see what that value is, I'm going to print it out. Next, we need to write that to the characteristic, so I'll create a data instance that represents our ioVal to be sent. I'll use the init function and pass in our ioVal as an array. So now we have the data to be sent to the characteristic, we need to actually write it.
So, if our charIO characteristic that we stored contains the property for writeWithoutResponse, we'll use that in our parameters, just as we've done before. We check to make sure that it contains writeWithoutResponse and then call it accordingly. So on line 168, I'll use my property for the peripheral to write the val to the characteristic using the data we just defined.
The characteristic we're writing to is the charIO that we saved, and the final parameter is to be with or without response. We check to make sure that we do have writeWithoutResponse, so that's the one that we'll use. I'll create an if statement at line 170 for the case where it doesn't support the writeWithoutResponse. I'll change the last parameter to the CBCharacteristicWriteType of withResponse.
Since our charIO is an optional, we need to unwrap that. Fortunately, Xcode helps us out here. So now we have our toggle that increments our ioVal, cycles it around when it gets too high, and writes it to the characteristic. So now all we need to do is actually call this function when they key is pressed. We only want to toggle the lights and the buzzer when the button is pressed down. That's when the characteristic value is non-zero. If we look back at our Simple Key's definition in the wiki, we see that bit zero is the left key.
Bit one is the right key. When there's zero, that means there's no keys being pressed, but we'll still get an update, because that is a value change to the characteristic. So we want to check to make sure that we only toggle when the key is pressed down. So we have the value, and on line 183, we'll check to make sure that it's not equal to a data that's initialized as zero. We have to pass this in as an array. We'll make it an array of one item that's zero.
So, if it's non-zero, we know that one of the keys was pressed down, not up, and on line 184, we'll add the body of this if to call the toggle. The last thing we want to do before we run this app is make sure that our initial state is all cleared out in the characteristic. So back up where we discovered the characteristic, we'll toggle it for the first time. So on line 120, I'll add the toggleIO call to our function. Now, we know that the toggle increments the IO variable and flips it when it's eight, and we want it to start at zero.
We'll set the variable to seven. When it's incremented in toggle, it'll be eight, flip to zero, and then the initial state of the characteristic will start at zero for us. Now, as we tap the keys, it'll increment up, starting from zero. So, if we run the app and turn on the sensor tag, it should discover the characteristics, initialize it to zero, enable the configuration, and as I press the keys, it should cycle through the lights, red, green, both, the buzzer, (device beeps) the buzzer with red, green, both, and back to off.
So we had to store our characteristic before we could write to it, and then we were able to write to it for the rest of the execution. We also had to enable the configuration, and as we catch the button presses, we call the toggle method to cycle through the various light and buzzer combinations.
- Bluetooth Low Energy (BLE) basics
- Core Bluetooth key classes
- Scanning for peripherals
- Discovering services and characteristics
- Writing to a characteristic
- Reconnecting to peripherals
- Apps as peripherals