Naadloze seriële Bluetooth op Raspberry Pi 2 met Windows 10 IoT Core
Onderdelen en benodigdheden
| Raspberry Pi 2 Model B | × | 1 |
| Generieke Bluetooth USB-dongle | × | 1 |
| Arduino UNO | × | 1 |
| SparkFun Bluetooth Modem - BlueSMiRF Silver | × | 1 |
Apps en online diensten
- Microsoft Windows 10 IoT Core
- Microsoft Visual Studio 2015
Over dit project
Dit project wordt gedetailleerd besproken op embedded101.com: "Win 10 IoT-Core: Bluetooth Universal Windows Serial App".
Over
Dit is een Universal Windows App die een IoT-Core-apparaat (zoals een Raspberry Pi 2) verbindt met een ingesloten Bluetooth-apparaat via het Bluetooth Serial Port Profile (SPP), gebruikmakend van een generieke Bluetooth USB-dongle.
Achtergrond
Bluetooth ondersteunt peer-to-peer networking over korte afstand. In tegenstelling tot Wi-Fi is Bluetooth-connectiviteit één-op-één. Twee apparaten kunnen symmetrisch (bijv. seriëel profiel voor raw data) of asymmetrisch (bijv. A2DP voor audio streaming) communiceren. OBEX is een symmetrisch profiel voor datauitwisseling.
De Microsoft IoT-Core Bluetooth-voorbeeldcode gebruikt GATT (Generic Attribute Profile) voor Bluetooth Low Energy. Dit project is eenvoudiger: het richt zich puur op SPP (Serial Port Profile), gebaseerd op RFCOMM. Het werd ontwikkeld met een oude USB Bluetooth-dongle en zou met elke dongle moeten werken.
De IoT-app
De app is een simpele Bluetooth Serial Universal Windows test-app. Het werkt als volgt:
- Opsommen van alle gebande Bluetooth-apparaten (lijst).
- Dubbelklik op een apparaat om te verbinden.
- Tekst invoeren in een TextBox en versturen via knop.
- Ontvangen tekst wordt automatisch getoond.
Het eindpunt (Arduino)
Voor testen wordt een Arduino Uno met Bluetooth-adapter gebruikt. De sketch echo't elk ontvangen teken terug:
Shield Setup
// Seriële loopback-app
// Opstart: wacht op verbinding
// Let op: BT-adapter Rx/Tx aan Arduino Tx/Rx
// BT-adapter loskoppelen voor programmeren!
void setup() {
Serial.begin(115200); // Baudrate van mijn adapter (ingesteld in command mode)
delay(333);
while (!Serial);
delay(1000);
Serial.println("The quick brown fox jumps over the lazy dog!");
}
Shield Loop
void loop() {
char ch = Serial.read();
while (255 == (byte)ch) {
ch = Serial.read();
}
Serial.print(ch);
}
Koppelen (Pairing)
Bluetooth is peer-to-peer; apparaten moeten eerst gekoppeld worden. Dit gebeurt niet in de app. Passkey-koppeling kan lastig zijn bij headless embedded devices (geen popup-ondersteuning). Er is een IoT-Core webportal en een commandline-tool (via SSH) voor koppeling. De nieuwste OS-webportal ondersteunt passkey-koppeling (ontbrak rond Windows 10 RTM). Zie de embedded101-artikelen voor details.
Project starten
Maak in Visual Studio 2015 een nieuw project: New Project → Visual C# → Windows → Blank App (Universal Windows).
App UI maken (XAML)
De UI is eenvoudig:
- Alle tekstelementen zijn TextBlocks, behalve SendText (TextBox).
- SendText accepteert CarriageReturn (Multiline).
- Recvd tekst heeft WrapText enabled (Multiline).
- UI-elementen in rijen via horizontale StackPanels, die weer in een verticale StackPanel zitten (samen met de ListBox).
- Alternatief: RelativePanel of Grid.
De ListBox ConnectDevices heeft een Binding Source PairedDevices en een ItemTemplate met TextBlock gebonden aan de Name-property:
<ListBox x:Name="ConnectDevices" ItemsSource="{Binding Source={StaticResource PairedDevices}}" Margin="10" DoubleTapped="ConnectDevices_DoubleTapped" Width="Auto" Height="Auto">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
App-configuratie
- Geen IoT-Core-extensies nodig.
- Wel specifieke capabilities voor Bluetooth en Serial in
Package.appxmanifest. - De UI-editor (dubbelklik) ondersteunt dit niet; open met XML Editor (rechtsklik → Open With → XML Editor).
Pas de <Capabilities>-sectie aan:
<Capabilities>
<Capability Name="internetClient" />
<DeviceCapability Name="bluetooth.rfcomm">
<Device Id="any">
<Function Type="name:serialPort"/>
</Device>
</DeviceCapability>
</Capabilities>
internetClient is standaard aanwezig; niet nodig voor deze app, maar mag blijven.
Gekoppelde apparaatinformatie
Bij opstart worden gekoppelde apparaten opgevraagd:
DeviceInformationCollection DeviceInfoCollection
= await DeviceInformation.FindAllAsync(RfcommDeviceService.GetDeviceSelector(RfcommServiceId.SerialPort));
Deze collectie wordt omgezet naar een ObservableCollection voor de ListBox:
_pairedDevices = new ObservableCollection<PairedDeviceInfo>();
Elk item uit DeviceInfoCollection krijgt een PairedDeviceInfo-object met properties:
Name(string)Id(string)DeviceInfo(Windows.Devices.Enumeration.DeviceInformation)
In de ListBox wordt het Name-veld getoond. Bij selectie (dubbelklik) is het DeviceInformation-object beschikbaar voor de verbinding.
Verbinding opzetten
Via DeviceInformation het RFCOMM-service opvragen, daarna een StreamSocket openen:
_service = await RfcommDeviceService.FromIdAsync(DeviceInfo.Id);
_socket = new StreamSocket();
await _socket.ConnectAsync(_service.ConnectionHostName, _service.ConnectionServiceName);
Bij succes is de socket geldig (niet null). Foutafhandeling via try-catch. Verzenden/ontvangen gebruikt de InputStream en OutputStream van de socket.
Tekst ontvangen (asynchroon)
Ontvangen loopt in een aparte thread, oneindige lus met async wait:
private async void Listen() {
ReadCancellationTokenSource = new CancellationTokenSource();
if (_socket.InputStream != null) {
dataReaderObject = new DataReader(_socket.InputStream);
while (true) {
await ReadAsync(ReadCancellationTokenSource.Token);
}
}
}
DataReader dataReaderObject;
private CancellationTokenSource ReadCancellationTokenSource;
private async Task ReadAsync(CancellationToken cancellationToken) {
uint ReadBufferLength = 1024;
cancellationToken.ThrowIfCancellationRequested();
dataReaderObject.InputStreamOptions = InputStreamOptions.Partial;
var loadAsyncTask = dataReaderObject.LoadAsync(ReadBufferLength).AsTask(cancellationToken);
UInt32 bytesRead = await loadAsyncTask;
if (bytesRead > 0) {
string recvdtxt = dataReaderObject.ReadString(bytesRead);
// verwerken/tonen...
}
}
Tekst verzenden (synchroon)
Verzenden wordt getriggerd vanuit de UI, dus essentieel synchroon (met await voor UI-vrijheid):
private async void Send(string msg) {
Task storeAsyncTask;
DataWriter dataWriteObject = new DataWriter(_socket.OutputStream);
dataWriteObject.WriteString(msg);
storeAsyncTask = dataWriteObject.StoreAsync().AsTask();
UInt32 bytesWritten = await storeAsyncTask;
}
Knopstatussen
Knoppen worden in/uitgeschakeld afhankelijk van app-state (bijv. Send/Start Recv pas actief na verbinding).
Genieten! :)
De volledige broncode is beschikbaar op GitHub en (mirror) Codeplex.
Productieproces
- GoPiGo v2 Uitbreiden op Windows 10 IoT Core voor Raspberry Pi 3
- LED helderheid regelen met PWM via drukknop op Arduino Mega
- Precisie druk- en luchtstroommeting met analoge sensor en Arduino
- Bouw een real-time gyroscoop-spel met Arduino Nano & MPU-6050 sensor
- ESP32-CAM bewegingdetecterende beveiligingscamera voor Discord
- DHT11 Temperatuur- & Vochtigheidsensor Project met LED-indicatoren en Piezo-luidspreker
- Unopad: Naadloze Arduino MIDI-controller voor Ableton Live
- Zelfgemaakte Arduino TV-afstandsbediening – Batterij, 3D-geprinte behuizing
- Een LED bedienen via Bluetooth met Arduino – Eenvoudige DIY Handleiding
- Een eenvoudige obstakelsensor bouwen met Arduino – IR LED & Fotodiodo Tutorial
- Spraakgestuurde huishoudapparaten: Bluetooth-besturing met Google Assistant