1. C#蓝牙通讯开发概述
在物联网和智能设备快速发展的当下,蓝牙技术已成为短距离无线通信的重要解决方案。作为一名长期从事C#开发的工程师,我发现蓝牙通讯在工业控制、智能家居、医疗设备和可穿戴设备等领域有着广泛应用。C#凭借其强大的类库支持和简洁的语法,成为实现蓝牙通讯的理想选择。
传统蓝牙(Classic Bluetooth)和低功耗蓝牙(BLE)是两种主要的技术标准。传统蓝牙适合大数据量传输,如音频流和文件传输;而BLE则专为低功耗设计,适用于传感器数据采集等场景。在Windows平台上,我们可以通过32feet.NET库进行传统蓝牙开发,使用Windows Runtime API进行BLE开发。
2. 开发环境准备
2.1 硬件和软件要求
进行C#蓝牙开发前,需要确保开发环境满足以下条件:
- 操作系统:Windows 7及以上版本。如需进行BLE开发,强烈推荐Windows 10或更高版本,因为其对BLE的支持更完善。
- 开发工具:Visual Studio 2019或2022,安装时需要勾选".NET桌面开发"工作负载。
- 硬件设备:内置或外接蓝牙适配器。我建议使用支持蓝牙4.0以上的适配器,以获得更好的兼容性。
注意:部分外接蓝牙适配器需要安装专用驱动。我曾遇到过某品牌USB蓝牙模块无法被识别的问题,安装官方驱动后才解决。
2.2 .NET框架选择
根据项目需求选择合适的.NET框架版本:
- 传统蓝牙开发:推荐.NET Framework 4.6.1或更高版本
- BLE开发:需要.NET Framework 4.6.1+配合Windows SDK
在实际项目中,我发现.NET 4.6在Windows 7上存在蓝牙功能不稳定的问题,升级到4.6.1后问题解决。
3. 传统蓝牙开发实战
3.1 设备扫描与发现
使用32feet.NET库可以方便地实现蓝牙设备扫描。以下是核心代码示例:
csharp复制public List<BluetoothDeviceModel> ScanDevices(int scanTimeout = 5000)
{
var deviceList = new List<BluetoothDeviceModel>();
try
{
BluetoothRadio radio = BluetoothRadio.PrimaryRadio;
if (radio == null) throw new Exception("未检测到蓝牙适配器");
radio.Mode = RadioMode.Connectable;
using (var client = new BluetoothClient())
{
BluetoothDeviceInfo[] devices = client.DiscoverDevices(scanTimeout);
foreach (var device in devices)
{
deviceList.Add(new BluetoothDeviceModel
{
DeviceName = device.DeviceName ?? "未知设备",
BluetoothAddress = device.DeviceAddress.ToString(),
IsPaired = device.Authenticated,
DeviceType = device.DeviceClass.ToString()
});
}
}
}
catch (Exception ex)
{
Console.WriteLine($"扫描失败: {ex.Message}");
}
return deviceList;
}
优化建议:
- 扫描超时设置为3-5秒,平衡完整性和响应速度
- 处理设备名称为空的情况,提升用户体验
- 在UI应用中,建议使用异步扫描避免界面卡顿
3.2 设备配对与连接
配对是蓝牙通讯的关键步骤。对于传统蓝牙设备,通常需要指定服务UUID:
csharp复制public BluetoothClient ConnectToDevice(BluetoothDeviceModel device)
{
if (device == null) return null;
try
{
var address = BluetoothAddress.Parse(device.BluetoothAddress);
Guid serviceUuid = Guid.Parse("00001101-0000-1000-8000-00805F9B34FB"); // 串口服务UUID
var client = new BluetoothClient();
client.Connect(address, serviceUuid);
if (client.Connected)
{
Console.WriteLine($"成功连接: {device.DeviceName}");
return client;
}
}
catch (Exception ex)
{
Console.WriteLine($"连接失败: {ex.Message}");
}
return null;
}
常见问题处理:
- 蓝牙地址格式必须为XX:XX:XX:XX:XX:XX
- 蓝牙2.x/3.x设备可能需要设置PIN码:
device.SetPin(new byte[] {0x31, 0x32, 0x33, 0x34}); - 连接失败时检查设备是否处于可发现模式
3.3 数据收发实现
建立连接后,可以通过NetworkStream进行数据传输:
csharp复制// 发送数据
public bool SendData(BluetoothClient client, string data)
{
if (!client.Connected) return false;
try
{
using (var stream = client.GetStream())
{
byte[] bytes = Encoding.UTF8.GetBytes(data);
stream.Write(bytes, 0, bytes.Length);
stream.Flush();
return true;
}
}
catch (Exception ex)
{
Console.WriteLine($"发送失败: {ex.Message}");
return false;
}
}
// 接收数据
public string ReceiveData(BluetoothClient client)
{
if (!client.Connected) return null;
try
{
using (var stream = client.GetStream())
{
byte[] buffer = new byte[1024];
int bytesRead = stream.Read(buffer, 0, buffer.Length);
return Encoding.UTF8.GetString(buffer, 0, bytesRead).Trim('\0');
}
}
catch (Exception ex)
{
Console.WriteLine($"接收失败: {ex.Message}");
return null;
}
}
注意事项:
- 发送和接收使用相同的编码格式(推荐UTF-8)
- 接收数据时使用Trim('\0')去除空字符
- 大数据传输需要实现分段机制
4. 低功耗蓝牙(BLE)开发
4.1 BLE设备扫描优化
BLE扫描默认较慢,可以通过配置主动扫描模式提升速度:
csharp复制public class BLEScanner
{
private BluetoothLEAdvertisementWatcher _watcher;
public BLEScanner()
{
_watcher = new BluetoothLEAdvertisementWatcher
{
ScanningMode = BluetoothLEScanningMode.Active,
SignalStrengthFilter = {
InRangeThresholdInDBm = -80,
OutOfRangeThresholdInDBm = -90,
OutOfRangeTimeout = TimeSpan.FromMilliseconds(500)
}
};
_watcher.Received += OnAdvertisementReceived;
}
private void OnAdvertisementReceived(BluetoothLEAdvertisementWatcher sender,
BluetoothLEAdvertisementReceivedEventArgs args)
{
// 处理扫描到的设备
}
public void StartScan() => _watcher.Start();
public void StopScan() => _watcher.Stop();
}
优化点:
- 主动扫描模式(Active)比被动模式快3-5倍
- 信号强度过滤可排除距离过远的设备
- 建议扫描超时设置为3秒
4.2 GATT服务与特征操作
BLE通讯基于GATT协议,需要先发现服务和特征:
csharp复制public async Task<bool> DiscoverServices(BluetoothLEDevice device)
{
try
{
var servicesResult = await device.GetGattServicesAsync(BluetoothCacheMode.Uncached);
if (servicesResult.Status != GattCommunicationStatus.Success) return false;
foreach (var service in servicesResult.Services)
{
Console.WriteLine($"发现服务: {service.Uuid}");
var charsResult = await service.GetCharacteristicsAsync(BluetoothCacheMode.Uncached);
if (charsResult.Status != GattCommunicationStatus.Success) continue;
foreach (var characteristic in charsResult.Characteristics)
{
Console.WriteLine($"发现特征: {characteristic.Uuid}, 属性: {characteristic.CharacteristicProperties}");
}
}
return true;
}
catch (Exception ex)
{
Console.WriteLine($"发现服务失败: {ex.Message}");
return false;
}
}
关键点:
- 使用Uncached模式获取最新服务列表
- 根据特征属性(Read/Write/Notify)进行相应操作
- 特征UUID需要参考设备文档
4.3 数据读写实现
发现特征后可以进行数据读写:
csharp复制// 读取特征值
public async Task<string> ReadCharacteristic(GattCharacteristic characteristic)
{
try
{
var result = await characteristic.ReadValueAsync();
if (result.Status != GattCommunicationStatus.Success) return null;
var reader = DataReader.FromBuffer(result.Value);
byte[] data = new byte[reader.UnconsumedBufferLength];
reader.ReadBytes(data);
return Encoding.UTF8.GetString(data);
}
catch (Exception ex)
{
Console.WriteLine($"读取失败: {ex.Message}");
return null;
}
}
// 写入特征值
public async Task<bool> WriteCharacteristic(GattCharacteristic characteristic, string data)
{
try
{
var writer = new DataWriter();
writer.WriteString(data);
var status = await characteristic.WriteValueAsync(writer.DetachBuffer(),
(characteristic.CharacteristicProperties & GattCharacteristicProperties.WriteWithoutResponse) != 0
? GattWriteOption.WriteWithoutResponse
: GattWriteOption.WriteWithResponse);
return status == GattCommunicationStatus.Success;
}
catch (Exception ex)
{
Console.WriteLine($"写入失败: {ex.Message}");
return false;
}
}
注意事项:
- 写入方式根据特征属性选择
- WriteWithoutResponse效率高但不保证送达
- 数据长度不能超过特征值限制(通常20字节)
5. 高级技巧与优化
5.1 异步编程实践
蓝牙操作应全部使用async/await模式:
csharp复制public async Task<List<BluetoothDeviceModel>> ScanDevicesAsync(int timeout = 5000)
{
return await Task.Run(() =>
{
var devices = new List<BluetoothDeviceModel>();
// 同步扫描代码
return devices;
});
}
5.2 大数据传输处理
对于超过MTU的数据,需要分段传输:
csharp复制public async Task SendLargeData(BluetoothClient client, byte[] data, int chunkSize = 1024)
{
using (var stream = client.GetStream())
{
int offset = 0;
while (offset < data.Length)
{
int size = Math.Min(chunkSize, data.Length - offset);
await stream.WriteAsync(data, offset, size);
offset += size;
}
}
}
5.3 连接稳定性优化
- 实现自动重连机制
- 监控信号强度,低于-90dBm时预警
- 添加心跳包检测连接状态
6. 常见问题解决
6.1 设备无法发现
可能原因:
- 设备未处于可发现模式
- 蓝牙适配器驱动问题
- 设备距离过远或有障碍物
解决方案:
- 确认设备处于配对模式
- 更新蓝牙适配器驱动
- 缩短设备距离,移除障碍物
6.2 连接频繁断开
可能原因:
- 信号强度弱
- 电源管理导致适配器休眠
- 设备资源不足
解决方案:
- 优化设备摆放位置
- 禁用蓝牙适配器的电源节能选项
- 检查设备负载情况
6.3 数据传输不完整
可能原因:
- 未处理数据分段
- 缓冲区大小不足
- 编码格式不一致
解决方案:
- 实现数据分片和重组逻辑
- 增大接收缓冲区
- 统一使用UTF-8编码
在实际项目中,我发现蓝牙开发最大的挑战不是技术实现,而是各种兼容性和稳定性问题。建议在开发初期就做好充分的设备兼容性测试,并建立完善的错误处理机制。
