亿迅智能制造网
工业4.0先进制造技术信息网站!
首页 | 制造技术 | 制造设备 | 工业物联网 | 工业材料 | 设备保养维修 | 工业编程 |
home  MfgRobots >> 亿迅智能制造网 >  >> Manufacturing Technology >> 制造工艺

树莓派 2 上实现无缝串口蓝牙:基于 Windows 10 IoT Core

组件与物料

组件数量
树莓派 2 Model B1
通用蓝牙 USB 适配器1
Arduino UNO1
SparkFun BlueSMiRF Silver 蓝牙模块1

应用与在线服务

项目详解

延伸阅读

本项目在 embedded101.com 有详细讨论:
"Win 10 IoT-Core: Bluetooth Universal Windows Serial App"

概述

这是一个通用 Windows 应用(UWP),将运行 IoT Core 的设备(如树莓派 2)通过通用蓝牙 USB 适配器,基于蓝牙串口配置文件(SPP)与嵌入式蓝牙设备连接。

背景知识

蓝牙支持短距离点对点网络。与 Wi-Fi 不同,蓝牙连接是一对一的。两设备可对称连接,通过串口配置文件传输原始数据。通常连接是不对称的:一端生成特定数据格式供另一端消费。例如手机实现 A2DP 向耳机推流音频;OBEX 是双向对称数据交换。

微软 IoT Core 官方蓝牙示例使用 GATT(通用属性配置文件),为蓝牙低功耗协议提供配置文件发现和描述服务,适合低功耗设备向复杂处理器发送数据。

本项目更简单,仅关注基于 RFCOMM 的通用 SPP(串口配置文件)。使用非常老旧的 USB 蓝牙适配器开发,因此应兼容任何蓝牙适配器。

IoT 应用

这是一个简单的蓝牙串口 UWP 测试应用。启动时枚举所有已配对的蓝牙设备列表,双击某项发起连接。在文本框输入要发送的文本,按按钮发送。应用自动接收并显示收到的文本。因此当终端设备(如下文 Arduino)只是回显收到的字符时,发送的文本会重现在 UWP 应用的接收框中。

树莓派 2 上实现无缝串口蓝牙:基于 Windows 10 IoT Core

终端设备

测试用 Arduino Uno + 蓝牙适配器。一个简单的扩展板,回显通过蓝牙收到的任何字符:

扩展板初始化代码:

// 串口回环应用
// 连接建立后由对端发起
// 注:蓝牙设备的 Rx/Tx 接 Arduino 的 Tx/Rx
// 编程时需断开蓝牙适配器
void setup() {
  // 适配器波特率 115200(AT 命令模式下设置)
  // 这是 Arduino 到蓝牙适配器的端口速率,非蓝牙空中速率
  Serial.begin(115200);
  delay(333);
  while (!Serial);
  delay(1000);
  Serial.println("The quick brown fox jumps over the lazy dog!");
}

扩展板主循环:

void loop() {
  char ch = Serial.read();
  while (255 == (byte)ch) {
    ch = Serial.read();
  }
  Serial.print(ch);
}

配对

蓝牙是点对点场景,连接前必须配对。配对不在应用内完成。嵌入式设备间用密钥配对可能有麻烦,因为它们常无头且不支持弹窗(IoT Core 也不支持)。可通过 IoT Core Web 门户或设备上的命令行工具(通过 SSH 运行)配对。最新版 OS Web 门户支持密钥配对(Win 10 RTM 时期缺失)。详见 "Win 10 IoT-Core: Bluetooth Universal Windows Serial App"。

创建项目

在 Visual Studio 2015 中使用通用 Windows 应用模板创建:

新建项目 → Visual C# → Windows → 空白应用(通用 Windows)

创建应用 UI

UI 用 XAML 实现,按上图布局创建:

ConnectDevices 是 ListBox,ItemsSource 绑定到 PairedDevices(见后),ItemTemplate 包含绑定到 PairedDevices Name 属性的 TextBlock。

<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>

应用配置

修改底部 Capabilities 节为:

<Capabilities>
  <Capability Name="internetClient" />
  <DeviceCapability Name="bluetooth.rfcomm">
    <Device Id="any">
      <Function Type="name:serialPort"/>
    </Device>
  </DeviceCapability>
</Capabilities>

internetClient 功能默认已有,本应用不需要可移除,我保留了它。

已配对设备信息

应用启动时枚举已配对设备:

DeviceInformationCollection DeviceInfoCollection
  = await DeviceInformation.FindAllAsync(RfcommDeviceService.GetDeviceSelector(RfcommServiceId.SerialPort));

将集合转为 ObservableCollection 供 ListBox 绑定:

_pairedDevices = new ObservableCollection<PairedDeviceInfo>();

遍历 DeviceInfoCollection,为每项创建 PairedDeviceInfo 对象加入 _pairedDevices。

PairedDeviceInfo 类属性:

类有相应构造函数。DeviceInformation 即 Windows.Devices.Enumeration.DeviceInformation。

ListBox 显示绑定的 Name 字段。双击选中设备时,该项实际包含终端的 DeviceInformation,用于建立连接。

连接建立

用 DeviceInformation 获取 RFCOMM 服务,通过终端主机名(实际是蓝牙 MAC 地址)和服务名字符串建立 StreamSocket 连接:

_service = await RfcommDeviceService.FromIdAsync(DeviceInfo.Id);
_socket = new StreamSocket();
await _socket.ConnectAsync(_service.ConnectionHostName, _service.ConnectionServiceName);

若成功,socket 有意义(非 null)。当然有 try-catch 错误处理。

发送和接收文本使用 socket 的 InputStream 和 OutputStream 属性。

接收文本

接收文本应先启动,因为它作为独立线程运行。循环执行异步等待获取串口输入,处理后再次异步接收,直到取消令牌触发。

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;
  Task loadAsyncTask = dataReaderObject.LoadAsync(ReadBufferLength).AsTask(cancellationToken);
  UInt32 bytesRead = await loadAsyncTask;
  if (bytesRead > 0) {
    string recvdtxt = dataReaderObject.ReadString(bytesRead);
  }
}

发送文本

接收是异步的(到达时机不确定),发送由 UI 触发,本质同步(虽用 await 释放 UI)。

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;
}

按钮状态

命令按钮根据应用状态启用/禁用。例如未连接时 Send 和 Start Recv 禁用,连接后启用等。

Enjoy! :)

制造工艺

  1. 在树莓派 3 上扩展 GoPiGo v2 的 Windows 10 IoT Core 应用
  2. Arduino Mega 按键 PWM 调光:三阶段渐变 LED 实战教程
  3. 基于模拟传感器的高精度压力与气流测量装置
  4. Arduino Nano 与 MPU-6050 构建实时陀螺仪游戏
  5. ESP32-CAM 运动检测安防相机,推送照片到 Discord
  6. DHT11 温湿度传感器项目:LED 指示与压电蜂鸣器报警
  7. Unopad:基于 Arduino 的无缝 Ableton Live MIDI 控制器
  8. 自制 Arduino 电视遥控器:3D 打印外壳、电池供电、支持三大品牌协议
  9. Arduino + HC-05 蓝牙控制 LED:MIT App Inventor 手机 App 开发全攻略
  10. 自制简易红外避障传感器:IR LED + 光电二极管 + Arduino
  11. 语音控制家电:Google Assistant + 蓝牙 + Arduino UNO
  12. Windows 10 Cortana 蓝牙控制 Arduino LED:UWP 应用开发全流程