In einem früheren Lernprogramm haben Sie gelernt, wie Sie einer Xenon-Anwendung Bluetooth hinzufügen. Auf diese Weise können Sie die integrierte RGB-LED über eine Test-App wie nRF Connect oder Light Blue Explorer steuern.
In diesem Beitrag gehen wir noch einen Schritt weiter. Wir werden eine Swift-App entwickeln, um eine Partikel-Mesh-RGB-LED zu steuern. Wenn alles gut geht, sollten Sie in ca. 20 Minuten eine funktionierende App haben!
Lass uns anfangen.
Sie haben gerade keine Zeit, den ganzen Artikel zu lesen?
Laden Sie hier die PDF-Version herunter.
Einrichten
- Installieren Sie Xcode. Sie können es hier aus dem App Store herunterladen.
- Sie benötigen außerdem einen Apple-Login. Ich benutze meine iCloud-E-Mail. Sie können ein neues Konto in Xcode erstellen, wenn Sie noch kein Konto haben.
- Installieren Sie den RGB-Beispielcode auf einer Partikelmaschenplatine.
Erstellen Sie das Projekt
Sobald alles installiert ist, kommen wir zu den lustigen Dingen!
Öffnen Sie Xcode und gehen Sie zu Datei → Neues Projekt.

Wählen Sie Single View App.

Aktualisieren Sie dann den Projektnamen nach Ihren Wünschen. Ich habe auch meine Organisationskennung in geändert com.jaredwolff
. Ändern Sie es nach Belieben!
Wählen Sie einen Speicherort aus, um ihn zu speichern.
Als nächstes finden Sie Ihre Info.plist.

Update info.plist
durch HinzufügenPrivacy - Bluetooth Peripheral Usage Description
Die Beschreibung, die ich letztendlich verwendet habe, war App uses Bluetooth to connect to the Particle Xenon RGB Example
Auf diese Weise können Sie Bluetooth in Ihrer App verwenden, wenn Sie es jemals freigeben möchten.
Lassen Sie uns jetzt alles minimal funktionsfähig machen!
Minimal funktionell

Als Nächstes erhalten wir eine App mit minimalen Funktionen, mit der Sie eine Verbindung herstellen und eine Serviceerkennung durchführen können. Der größte Teil der Aktion wird in der ViewController.swift
.
Lässt zuerst importieren CoreBluetooth
import CoreBluetooth
Auf diese Weise können wir die Bluetooth Low Energy-Funktionalität in iOS steuern. Dann fügen wir der Klasse sowohl das CBPeripheralDelegate
als auch hinzu .CBCentralManagerDelegate
ViewController
class ViewController: UIViewController, CBPeripheralDelegate, CBCentralManagerDelegate {
Erstellen wir nun lokale private Variablen, um den eigentlichen zentralen Manager und das Peripheriegerät zu speichern. Wir werden sie für einen Moment weiter einrichten.
// Properties private var centralManager: CBCentralManager! private var peripheral: CBPeripheral!
viewDidLoad
Lassen Sie uns in Ihrer Funktion diecentralManager
centralManager = CBCentralManager(delegate: self, queue: nil)
Die Einstellung delegate: self
ist wichtig. Andernfalls ändert sich der Zentralzustand beim Start nie.
Bevor wir fortfahren, erstellen wir eine separate Datei und rufen sie auf ParticlePeripheral.swift
. Es kann überall platziert werden, aber ich habe es für später in einer separaten 'Gruppe' namens Models platziert .
Im Inneren erstellen wir einige öffentliche Variablen, die die UUIDs für unsere Spanplatte enthalten. Sie sollten vertraut aussehen!
import UIKit import CoreBluetooth class ParticlePeripheral: NSObject { /// MARK: - Particle LED services and charcteristics Identifiers public static let particleLEDServiceUUID = CBUUID.init(string: "b4250400-fb4b-4746-b2b0-93f0e61122c6") public static let redLEDCharacteristicUUID = CBUUID.init(string: "b4250401-fb4b-4746-b2b0-93f0e61122c6") public static let greenLEDCharacteristicUUID = CBUUID.init(string: "b4250402-fb4b-4746-b2b0-93f0e61122c6") public static let blueLEDCharacteristicUUID = CBUUID.init(string: "b4250403-fb4b-4746-b2b0-93f0e61122c6") }
Zurück in ViewController.swift
lasst uns die Bluetooth-Bits zusammensetzen.
Bluetooth-Bits

Alles, was mit Bluetooth zu tun hat, ist ereignisbasiert. Wir werden verschiedene Funktionen definieren, die diese Ereignisse behandeln. Hier sind die wichtigsten:
centralManagerDidUpdateState
wird aktualisiert, wenn das Bluetooth-Peripheriegerät ein- oder ausgeschaltet wird. Es wird ausgelöst, wenn eine App zum ersten Mal gestartet wird, damit Sie den Status von Bluetooth kennen. Hier beginnen wir auch mit dem Scannen.
Das centralManager
didDiscover
Ereignis tritt auf, wenn Sie Scanergebnisse erhalten. Wir werden dies verwenden, um eine Verbindung herzustellen.
Das centralManager
didConnect
Ereignis wird ausgelöst, sobald das Gerät angeschlossen ist. Wir werden hier mit der Geräteerkennung beginnen. Hinweis: Mit der Geräteerkennung bestimmen wir, welche Dienste und Merkmale verfügbar sind. Dies ist eine gute Möglichkeit, um zu bestätigen, mit welchem Gerätetyp wir verbunden sind.
Das peripheral
didDiscoverServices
Ereignis zuerst, sobald alle Dienste entdeckt wurden. Beachten Sie, dass wir von jetzt centralManager
zu peripheral
jetzt gewechselt sind , wo wir verbunden sind. Wir werden hier mit der charakteristischen Entdeckung beginnen. Wir werden die RGB-Dienst-UUID als Ziel verwenden.
Die peripheral
didDiscoverCharacteristicsFor
Veranstaltung bietet alle Merkmale unter Verwendung der bereitgestellten Dienst-UUID. Dies ist der letzte Schritt in der Kette einer vollständigen Geräteerkennung. Es ist haarig, muss aber während der Verbindungsphase nur einmal gemacht werden!
Definieren aller Bluetooth-Funktionen.
Jetzt wissen wir, welche Funktionsereignisse ausgelöst werden. Wir definieren sie in der logischen Reihenfolge, in der sie während eines Verbindungszyklus auftreten.
First, we'll define centralManagerDidUpdateState
to start scanning for a device with our Particle RGB LED Service. If Bluetooth is not enabled, it will not do anything.
// If we're powered on, start scanning func centralManagerDidUpdateState(_ central: CBCentralManager) { print("Central state update") if central.state != .poweredOn { print("Central is not powered on") } else { print("Central scanning for", ParticlePeripheral.particleLEDServiceUUID); centralManager.scanForPeripherals(withServices: [ParticlePeripheral.particleLEDServiceUUID], options: [CBCentralManagerScanOptionAllowDuplicatesKey : true]) } }
Defining the centralManager
didDiscover
is our next step in the process. We know we've found a device if this event has occurred.
// Handles the result of the scan func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) { // We've found it so stop scan self.centralManager.stopScan() // Copy the peripheral instance self.peripheral = peripheral self.peripheral.delegate = self // Connect! self.centralManager.connect(self.peripheral, options: nil) }
So, we stop scanning using self.centralManager.stopScan()
. We set the peripheral
so it persists through the app. Then we connect to that device using self.centralManager.connect
Once connected, we need to double check if we're working with the right device.
// The handler if we do connect succesfully func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) { if peripheral == self.peripheral { print("Connected to your Particle Board") peripheral.discoverServices([ParticlePeripheral.particleLEDServiceUUID]) } }
By comparing the two peripherals we'll know its the device we found earlier. We'll kick off a services discovery using peripheral.discoverService
. We can use ParticlePeripheral.particleLEDServiceUUID
as a parameter. That way we don't pick up any services we don't care about.
Once we finish the discovering services, we'll get a didDiscoverServices
event. We iterate through all the "available" services. (Though there will only be one!)
// Handles discovery event func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) { if let services = peripheral.services { for service in services { if service.uuid == ParticlePeripheral.particleLEDServiceUUID { print("LED service found") //Now kick off discovery of characteristics peripheral.discoverCharacteristics([ParticlePeripheral.redLEDCharacteristicUUID, ParticlePeripheral.greenLEDCharacteristicUUID, ParticlePeripheral.blueLEDCharacteristicUUID], for: service) return } } } }
By this point this is the third time we're checking to make sure we have the correct service. This becomes more handy later when there are many characteristics and many services.
We call peripheral.discoverCharacteristics
with an array of UUIDs for the characteristics we're looking for. They're all the UUIDs that we defined in ParticlePeripheral.swift
.
Finally, we handle the didDiscoverCharacteriscsFor
event. We iterate through all the available characteristics. As we iterate we compare with the ones we're looking for.
// Handling discovery of characteristics func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) { if let characteristics = service.characteristics { for characteristic in characteristics { if characteristic.uuid == ParticlePeripheral.redLEDCharacteristicUUID { print("Red LED characteristic found") } else if characteristic.uuid == ParticlePeripheral.greenLEDCharacteristicUUID { print("Green LED characteristic found") } else if characteristic.uuid == ParticlePeripheral.blueLEDCharacteristicUUID { print("Blue LED characteristic found"); } } } }
At this point we're ready to do a full device discovery of our Particle Mesh device. In the next section we'll test what we have to make sure things are working ok.
Testing our minimal example

Before we get started, if you run into trouble I've put some troubleshooting steps in the footnotes.
To test, you'll have to have an iPhone with Bluetooth Low Energy. Most modern iPhones have it. The last iPhone not to have it I believe was either the iPhone 4 or 3Gs. (so you're likely good)
First, plug it into your computer.
Go to the top by the play and stop buttons. Select your target device. In my case I chose my phone (Jared's iPhone). You can also use an iPad.

Then you can hit Command + R or hit that Play button to load the app to your phone.
Make sure you have your log tab open. Enable it by clicking the bottom pane button in the top right corner.

Make sure you have a mesh device setup and running the example code. You can go to this post to get it. Remember your Particle Mesh board needs to be running device OS 1.3.0 or greater for Bluetooth to work!
Once both the firmware and app is loaded, let's check the log output.
It should look something like this:
View loaded Central state update Central scanning for B4250400-FB4B-4746-B2B0-93F0E61122C6 Connected to your Particle Board LED service found Red LED characteristic found Green LED characteristic found Blue LED characteristic found
This means that your Phone has connected, found the LED service! The characteristics also being discovered is important here. Without those we wouldn't be able to send data to the mesh device.
Next step is to create some sliders so we can update the RGB values on the fly.
Slide to the left. Slide to the right.
Next we're going to add some elements to our Main.storyboard
. Open Main.storyboard
and click on the View underneath View Controller.

Then click on the Library button. (It looks like the old art Apple used for the home button)

You'll get a pop-up with all the choices that you can insert into your app.

Drag three Labels and copy three Sliders to your view.

You can double click on the labels and rename them as you go.

If you click and hold, some handy alignment tools will popup. They'll even snap to center!

You can also select them all and move them together. We'll align them vertically and horizontally.
In order for them to stay in the middle, let's remove the autoresizing property. Click the Ruler icon on the top right. Then click each of the red bars. This will ensure that your labels and sliders stay on the screen!

Next let's click the Show Assistant Editor button. (Looks like a Venn diagram)

Note: make sure that ViewController.swift is open in your Assistant Editor.

Then underneath the /properties
section, Control-click and dragthe Red Slider into your code.

Repeat with all the other ones. Make sure you name them something different. Your code should look like this when you're done:
// Properties private var centralManager: CBCentralManager! private var peripheral: CBPeripheral! // Sliders @IBOutlet weak var redSlider: UISlider! @IBOutlet weak var greenSlider: UISlider! @IBOutlet weak var blueSlider: UISlider!
This allow us to access the value of the sliders.
Next, let's attach the Value Changed event to each of the sliders. Right click on the Red Slider in the folder view.

It should give you some options for events. Click and drag the Value Changed event to your code. Make sure you name it something that makes sense. I used RedSliderChanged for the Red Slider.
Repeat two more times. Your code should look like this at the end of this step:
@IBAction func RedSliderChanged(_ sender: Any) { } @IBAction func GreenSliderChanged(_ sender: Any) { } @IBAction func BlueSliderChanged(_ sender: Any) { }
I've also selected each of the sliders to and un-checked Enabled. That way you can't move them. We'll enable them later on in code.

Also, this is a great time to change the maximum value to 255. Also set the default value from 0.5 to 0.

Back at the top of the file. Let's create some local variables for each of the characteristics. We'll use these so we can write the slider variables to the Particle Mesh board.
// Characteristics private var redChar: CBCharacteristic? private var greenChar: CBCharacteristic? private var blueChar: CBCharacteristic?
Now, let's tie everything together!
In the didDiscoverCharacteristicsFor
callback function. Let's assign those characteristics. For example
if characteristic.uuid == ParticlePeripheral.redLEDCharacteristicUUID { print("Red LED characteristic found") redChar = characteristic
As we find each characteristic, we can also enable each of the sliders in the same spot.
// Unmask red slider redSlider.isEnabled = true
In the end your didDiscoverCharacteristicsFor
should look like:
// Handling discovery of characteristics func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) { if let characteristics = service.characteristics { for characteristic in characteristics { if characteristic.uuid == ParticlePeripheral.redLEDCharacteristicUUID { print("Red LED characteristic found") redChar = characteristic redSlider.isEnabled = true } else if characteristic.uuid == ParticlePeripheral.greenLEDCharacteristicUUID { print("Green LED characteristic found") greenChar = characteristic greenSlider.isEnabled = true } else if characteristic.uuid == ParticlePeripheral.blueLEDCharacteristicUUID { print("Blue LED characteristic found"); blueChar = characteristic blueSlider.isEnabled = true } } } }
Now, let's update the RedSliderChanged
GreenSliderChanged
and BlueSliderChanged
functions. What we want to do here is update the characteristic associated with the Changed
function. I created a separate function called writeLEDValueToChar
. We'll pass in the characteristic and the data.
private func writeLEDValueToChar( withCharacteristic characteristic: CBCharacteristic, withValue value: Data) { // Check if it has the write property if characteristic.properties.contains(.writeWithoutResponse) && peripheral != nil { peripheral.writeValue(value, for: characteristic, type: .withoutResponse) } }
Now add a call to writeLEDValueToChar
to each of the Changed
functions. You will have to cast the value to a Uint8
. (The Particle Mesh device expects an unsigned 8-bit number.)
@IBAction func RedSliderChanged(_ sender: Any) { print("red:",redSlider.value); let slider:UInt8 = UInt8(redSlider.value) writeLEDValueToChar( withCharacteristic: redChar!, withValue: Data([slider])) }
Repeat this for GreenSliderChanged
and BlueSliderChanged
. Make sure you changed red
to green
and blue
for each!
Finally, to keep things clean, i've also added a function that handles Bluetooth disconnects.
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
Inside, we should reset the state of the sliders to 0 and disable them.
if peripheral == self.peripheral { print("Disconnected") redSlider.isEnabled = false greenSlider.isEnabled = false blueSlider.isEnabled = false redSlider.value = 0 greenSlider.value = 0 blueSlider.value = 0
It's a good idea to reset self.peripheral
to nil that way we're not ever trying to write to a phantom device.
self.peripheral = nil
Finally, because we've disconnected, start scanning again!
// Start scanning again print("Central scanning for", ParticlePeripheral.particleLEDServiceUUID); centralManager.scanForPeripherals(withServices: [ParticlePeripheral.particleLEDServiceUUID], options: [CBCentralManagerScanOptionAllowDuplicatesKey : true]) }
Alright! We just about ready to test. Let's move on to the next (and final) step.
Test the sliders.

The hard work is done. Now it's time to play!
The easiest way to test everything is to click the Play button in the top left or the Command + R keyboard shortcut. Xcode will load the app to your phone. You should see a white screen proceeded by a screen with your sliders!
The sliders should stay greyed out until connected to your Particle Mesh board. You can check your log output if the connection has been established.
View loaded Central state update Central scanning for B4250400-FB4B-4746-B2B0-93F0E61122C6 Connected to your Particle Board LED service found Red LED characteristic found Green LED characteristic found Blue LED characteristic found
(Look familiar? We're connected!)
If you followed everything perfectly, you should be able to move the sliders. Better yet, the RGB LED on the Particle Mesh board should change color.

Conclusion
In diesem Artikel haben Sie gelernt, wie Sie Ihr Partikel-Mesh-Board und Ihr iOS-Gerät über Bluetooth verbinden. Wir haben gelernt, wie Sie sich mit den verfügbaren Merkmalen verbinden. Erstellen Sie außerdem eine saubere Oberfläche, um alles zu erledigen.
Wie Sie sich vorstellen können, können Sie mit Bluetooth unter iOS das Kaninchenloch hinuntergehen. In meinem kommenden Leitfaden wird es noch mehr geben: Der ultimative Leitfaden für Partikelgitter. Abonnenten meiner Liste erhalten Zugriff auf Inhalte vor dem Start und einen Rabatt, wenn diese veröffentlicht werden! Klicken Sie hier, um sich anzumelden.
Code
Der vollständige Quellcode ist auf Github verfügbar. Wenn Sie es nützlich finden, drücken Sie die Stern-Taste. ⭐️