Bluetooth Serial no Raspberry Pi 2 com Windows 10 IoT Core: Guia Completo
Componentes e Materiais
| Componente | Quantidade |
|---|---|
| Raspberry Pi 2 Model B | 1 |
| Dongle Bluetooth USB Genérico | 1 |
| Arduino UNO | 1 |
| Módem Bluetooth SparkFun BlueSMiRF Silver | 1 |
Aplicativos e Serviços Online
- Microsoft Windows 10 IoT Core
- Microsoft Visual Studio 2015
Sobre o Projeto
Mais Detalhes
Este projeto é discutido em detalhes no embedded101.com: "Win 10 IoT-Core: Bluetooth Universal Windows Serial App".
Sobre
Este projeto é um Universal Windows App que conecta um dispositivo IoT Core (como Raspberry Pi 2) a um dispositivo Bluetooth embarcado via Bluetooth Serial Profile, usando um dongle Bluetooth USB genérico.
Contexto
O Bluetooth suporta rede peer-to-peer de curto alcance. Diferente de Wi-Fi, a conectividade Bluetooth é um-para-um. Dois dispositivos podem conectar e interagir. Por exemplo, dois dispositivos podem conectar de forma simétrica usando o perfil serial e trocar dados brutos. Geralmente, porém, a conectividade Bluetooth é assimétrica: uma ponta gera um formato de dados específico para ser consumido na outra. Exemplo: um celular implementa o perfil A2DP (Advanced Audio Distribution Profile) e transmite áudio para um headset que implementa o perfil HSP (Headset Profile). OBEX (Object Exchange) é uma conexão simétrica entre dois dispositivos que trocam dados entre si.
O projeto de exemplo Bluetooth da Microsoft para IoT Core usa o perfil GATT (Generic Attribute Profile), que fornece serviços de descoberta e descrição para o protocolo Bluetooth Low Energy. Com esse protocolo, dispositivos simples de baixa potência podem ser conectados em uma ponta gerando dados para serem consumidos por um processador complexo.
Este projeto é muito mais simples que o exemplo da Microsoft. Ele se preocupa apenas com o SPP (Serial Port Profile), baseado no perfil RFCOMM. Foi desenvolvido com um dongle Bluetooth USB muito antigo e deve funcionar com qualquer dongle Bluetooth.
O App IoT
Este app é um simples app de teste Bluetooth Serial Universal Windows (Windows 10). Ele começa enumerando (como lista) todos os dispositivos Bluetooth pareados ao dispositivo onde o app roda. Um é selecionado com duplo clique, iniciando a conexão. Texto a enviar é digitado em uma TextBox e enviado ao pressionar um botão. O app recebe automaticamente qualquer texto enviado a ele (e o exibe). Portanto, quando o endpoint (como acima) simplesmente ecoa o texto recebido, o texto enviado deve reaparecer na caixa de recepção no app UW.
O Endpoint
Para teste, usa-se um Arduino Uno com adaptador Bluetooth. Ele tem um shield simples que ecoa de volta qualquer caractere recebido via Bluetooth:
Configuração do Shield:
// App serial loop back
// Após setup
// Iniciado pelo sistema conectado
// Pode ser usado com Bluetooth onde RxTx do dispositivo BT
// estão conectados ao TxRx da placa Arduino.
// Precisa desconectar adaptador BT para programar.
void setup() {
// Meu adaptador está configurado para este BAUD 115200
// Esta é a taxa da porta Arduino para adaptador BT
// Definida quando adaptador BT está em modo comando.
// NÃO é a taxa BT que não pode ser definida.
Serial.begin(115200);
delay(333);
while (!Serial) ;
delay(1000);
Serial.println("The quick brown fox jumps over the lazy dog!");
}
Loop do Shield:
void loop() {
char ch = Serial.read();
while (255 == (byte) ch) {
ch = Serial.read();
}
Serial.print(ch);
}
Pareamento
Bluetooth é um cenário peer-to-peer. Antes de conectar via Bluetooth, os dispositivos devem ser pareados. Isso não é feito dentro do app. Pareamento com chave de acesso pode ser problemático entre dispositivos embarcados, pois são frequentemente headless e podem não suportar popups (como o IoT Core não suporta). Existe um portal web IoT Core para isso, bem como um utilitário de linha de comando no dispositivo que pode rodar via shell SSH. A versão mais recente do portal web do OS suporta pareamento com chave de acesso, que faltava na época do Windows 10 RTM. O artigo "Win 10 IoT-Core: Bluetooth Universal Windows Serial App" discute pareamento neste contexto em detalhes.
Iniciando o Projeto
O projeto do app é criado no Visual Studio 2015 usando o template Universal Windows App:
Novo Projeto → Visual C# → Windows → Blank App (Universal Windows)
Criando a UI do App
A UI é implementada em XAML. Crie a UI conforme o layout acima (ou similar):
- Todos os elementos de texto são TextBlocks, exceto SendText que é uma TextBox
- SendText aceita CarriageReturn (é Multiline)
- O texto Recvd tem WrapText habilitado (é Multiline)
- Elementos UI são organizados em linhas usando StackPanel com orientação horizontal
- Esses StackPanels são empilhados com um StackPanel de orientação vertical, junto com o ListBox
Você pode preferir layout com Relative StackPanels ou Grid rows/columns.
O ConnectDevices é um ListBox com Binding Source definido para PairedDevices (ver depois) e ItemTemplate consistindo de um TextBlock vinculado à propriedade Name de PairedDevices (ver depois).
<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>
Configuração do App
- O app não usa recursos do IoT Core, então não requer Referências de Extensão.
- O app precisará de capacidades específicas para Bluetooth e Serial adicionadas ao Package.appxmanifest.
- Infelizmente, a UI que você obtém clicando duas vezes no arquivo no Solution Explorer não suporta adicionar essas capacidades, então você precisa adicioná-las manualmente clicando com botão direito no arquivo no Solution Explorer e selecionando Open With → XML Editor.
Modifique a seção Capabilities (no final) para:
<Capabilities>
<Capability Name="internetClient" />
<DeviceCapability Name="bluetooth.rfcomm">
<Device Id="any">
<Function Type="name:serialPort"/>
</Device>
</DeviceCapability>
</Capabilities>
A capability internetClient já estará lá. Não é necessária para este app, então você pode removê-la. Eu deixo.
Informações do PairedDevice
No início do app, os dispositivos pareados são enumerados usando:
DeviceInformationCollection DeviceInfoCollection
= await DeviceInformation.FindAllAsync(RfcommDeviceService.GetDeviceSelector(RfcommServiceId.SerialPort));
Esta coleção é transformada em um ObservableCollection para uso no ListBox:
_pairedDevices = new ObservableCollection<PairedDeviceInfo>();
A DeviceInfoCollection é iterada criando um objeto PairedDeviceInfo para cada item, adicionado à coleção _pairedDevices.
PairedDeviceInfo é uma classe com propriedades:
Name <string>
Id <string>
DeviceInfo <DeviceInformation>
A classe tem construtor apropriado.
DeviceInformation é Windows.Devices.Enumeration.DeviceInformation.
No listbox de dispositivos pareados, o campo Name é exibido (via binding).
Quando um dispositivo é selecionado (duplo clique), o DeviceInformation do endpoint faz parte do item de menu selecionado e é usado para estabelecer a conexão.
Conexão
O DeviceInformation é usado para obter o serviço RFCOMM. A conexão é estabelecida como StreamSocket usando o HostName do Endpoint (na verdade seu endereço MAC Bluetooth) e a string ServiceName.
_service = await RfcommDeviceService.FromIdAsync(DeviceInfo.Id);
_socket = new StreamSocket();
await _socket.ConnectAsync(_service.ConnectionHostName, _service.ConnectionServiceName);
Se tudo correr bem, temos uma conexão — o socket será válido (não nulo). Claro, isso tem tratamento de erro (try-catch). Envio e recebimento de texto usam as propriedades InputStream e OutputStream do socket.
Receber Texto
O recebimento deve ser iniciado primeiro, pois roda em thread separada. Executa um await assíncrono para obter entrada serial, processa a entrada e executa outro receive assíncrono — tudo em loop. Este loop continua até uma cancelamento ser gerado.
private async void Listen() {
ReadCancellationTokenSource = new CancellationTokenSource();
if (_socket.InputStream != null) {
dataReaderObject = new DataReader(_socket.InputStream);
// continua lendo entrada serial
while (true) {
await ReadAsync(ReadCancellationTokenSource.Token);
}
}
}
DataReader dataReaderObject;
private CancellationTokenSource ReadCancellationTokenSource;
private async Task ReadAsync(CancellationToken cancellationToken) {
uint ReadBufferLength = 1024;
// Se cancelamento solicitado, atende
cancellationToken.ThrowIfCancellationRequested();
// Define InputStreamOptions para completar leitura assíncrona quando um ou mais bytes disponíveis
dataReaderObject.InputStreamOptions = InputStreamOptions.Partial;
// Cria task para aguardar dados no serialPort.InputStream
loadAsyncTask = dataReaderObject.LoadAsync(ReadBufferLength).AsTask(cancellationToken);
// Inicia task e aguarda
UInt32 bytesRead = await loadAsyncTask;
if (bytesRead > 0) {
string recvdtxt = dataReaderObject.ReadString(bytesRead);
}
}
Enviar Texto
Enquanto o texto recebido é assíncrono (chegada não determinística), o envio é acionado pela UI do app e é essencialmente síncrono (embora use await para liberar a UI).
private async void Send(string msg) {
Task storeAsyncTask;
DataWriter dataWriteObject = new DataWriter(_socket.OutputStream);
dataWriteObject.WriteString(msg);
// Inicia task assíncrona para completar operação de escrita
storeAsyncTask = dataWriteObject.StoreAsync().AsTask();
UInt32 bytesWritten = await storeAsyncTask;
}
Estados dos Botões
Os botões de comando são habilitados/desabilitados conforme o estado do app. Exemplo: botões Send e Start Recv desabilitados quando não conectado, habilitados quando conectado, etc.
Aproveite! :)
Processo de manufatura
- Estendendo GoPiGo v2 no Windows 10 IoT Core para Raspberry Pi 3
- Controle de Brilho de LED com PWM via Botão no Arduino Mega
- Dispositivo de Precisão para Medição de Pressão e Fluxo de Ar com Sensor Analógico
- Construa um Jogo em Tempo Real com Giroscópio: Arduino Nano + Sensor MPU-6050
- Câmera de Segurança com Detecção de Movimento ESP32-CAM para Discord
- Projeto DHT11: Sensor Temperatura/Umidade com LEDs Indicadores e Buzzer
- Unopad: Controlador MIDI Arduino Perfeito para Ableton Live
- Controle Remoto TV Personalizado com Arduino – Bateria e Gabinete 3D
- Controlar um LED via Bluetooth com Arduino – Guia DIY Simples
- Construa um Sensor de Obstáculos Simples com Arduino – Tutorial Fácil com LED IR e Fotodiodo