利用NFC标签建立Android手机与Raspberry Pi的BLE连接
嘿,这个跨设备联动的场景挺实用的!我之前做过类似的项目,给你一步步拆解实现步骤,保证能跑通:
一、NFC标签写入与读取(获取RPi的MAC地址)
首先得把树莓派的BLE MAC地址写入NFC标签,推荐用NDEF格式,方便Android直接解析:
- 写入标签:可以用现成的NFC工具APP(比如NFC Tools),选择「文本记录」,输入树莓派的BLE MAC地址(格式要严格遵循
AA:BB:CC:DD:EE:FF),写入到标签中。 - Android端读取NFC:在你的Activity里注册NFC过滤逻辑,捕获
NDEF_DISCOVEREDintent并解析MAC地址:
override fun onNewIntent(intent: Intent?) { super.onNewIntent(intent) if (NfcAdapter.ACTION_NDEF_DISCOVERED == intent?.action) { val rawMessages = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES) rawMessages?.let { messages -> val ndefMessage = messages[0] as NdefMessage val record = ndefMessage.records[0] val piMacAddress = String(record.payload, Charsets.UTF_8).trim() // 拿到MAC地址后直接触发BLE连接逻辑 connectToPiViaBle(piMacAddress) } } }
注意:别忘了在AndroidManifest里添加NFC权限和intent filter,确保APP能捕获NFC触发的事件
二、Android端BLE连接核心实现
BLE连接需要处理权限、适配器初始化、连接回调这几个关键环节:
- 权限配置(AndroidManifest.xml):
<uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <!-- Android 6+必须,否则无法扫描BLE设备 --> <uses-feature android:name="android.hardware.bluetooth_le" android:required="true" /> <!-- Android 12+ 额外需要以下权限 --> <uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" /> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
- 初始化蓝牙适配器并发起连接:
private fun connectToPiViaBle(macAddress: String) { val bluetoothManager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager val bluetoothAdapter = bluetoothManager.adapter // 检查蓝牙是否开启,未开启则引导用户打开 if (!bluetoothAdapter.isEnabled) { val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE) startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT) return } // 获取树莓派的BLE设备实例 val piDevice = bluetoothAdapter.getRemoteDevice(macAddress) // 定义BLE连接回调,处理连接状态和服务发现 val gattCallback = object : BluetoothGattCallback() { override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) { super.onConnectionStateChange(gatt, status, newState) when (newState) { BluetoothProfile.STATE_CONNECTED -> { runOnUiThread { Toast.makeText(this@MainActivity, "已连接到树莓派", Toast.LENGTH_SHORT).show() } gatt?.discoverServices() // 连接成功后立即发现服务,用于后续通信 } BluetoothProfile.STATE_DISCONNECTED -> { runOnUiThread { Toast.makeText(this@MainActivity, "连接已断开", Toast.LENGTH_SHORT).show() } } } } override fun onServicesDiscovered(gatt: BluetoothGatt?, status: Int) { super.onServicesDiscovered(gatt, status) if (status == BluetoothGatt.GATT_SUCCESS) { // 找到树莓派上定义的BLE服务和特征,发送播放指令 val playService = gatt?.getService(UUID.fromString("你的自定义服务UUID")) val playCharacteristic = playService?.getCharacteristic(UUID.fromString("你的自定义特征UUID")) playCharacteristic?.value = "play_video:demo.mp4".toByteArray() gatt?.writeCharacteristic(playCharacteristic) } } } // 发起BLE连接(第三个参数为false表示不自动重连,可根据需求调整) piDevice.connectGatt(this, false, gattCallback) }
三、树莓派端BLE服务配置
树莓派这边用BlueZ工具链实现BLE外围设备,步骤如下:
- 安装BlueZ依赖:
sudo apt-get update sudo apt-get install bluez bluez-tools python3-pip pip3 install bluepy
- 编写BLE服务脚本(用bluepy库实现):
from bluepy.btle import Peripheral, UUID, Characteristic, Service, Advertiser import subprocess # 自定义服务和特征的UUID(可以在线生成随机UUID) SERVICE_UUID = UUID("12345678-1234-5678-1234-567812345678") PLAY_CHAR_UUID = UUID("87654321-4321-8765-4321-876543218765") class VideoPlayService(Service): def __init__(self, peripheral): super().__init__(peripheral, SERVICE_UUID, True) self.addCharacteristic(VideoPlayCharacteristic(self)) class VideoPlayCharacteristic(Characteristic): def __init__(self, service): super().__init__(service, PLAY_CHAR_UUID, Characteristic.PROP_WRITE) def writeValue(self, value, offset): # 解析Android端发送的指令 command = value.decode('utf-8') if command.startswith("play_video:"): video_path = command.split(":")[1] # 调用omxplayer播放视频到HDMI接口(电视) subprocess.run(["omxplayer", "-o", "hdmi", f"/home/pi/videos/{video_path}"]) # 启动BLE外围设备 peripheral = Peripheral() peripheral.addService(VideoPlayService(peripheral)) peripheral.setAdvertiseData(Advertiser(name="PiVideoPlayer")) peripheral.startAdvertising() try: print("等待Android端连接...") while True: peripheral.waitForNotifications(1.0) except KeyboardInterrupt: peripheral.stopAdvertising() print("服务已停止")
- 配置树莓派蓝牙状态:
sudo bluetoothctl power on discoverable on pairable on exit
四、关键注意事项与故障排查
- MAC地址格式:必须严格遵循
XX:XX:XX:XX:XX:XX格式,大小写不影响,但Android的getRemoteDevice方法会校验格式。 - BLE连接稳定性:如果连接频繁断开,检查树莓派的蓝牙信号强度,或在Android端添加重连逻辑。
- 权限问题:Android端一定要在运行时申请位置权限(6+)和蓝牙相关权限(12+),否则无法扫描或连接BLE设备。
- 树莓派服务启动:确保BlueZ服务正常运行,用
sudo systemctl status bluetooth查看状态,异常则重启服务sudo systemctl restart bluetooth。
内容的提问来源于stack exchange,提问作者Xflkekw
相关产品推荐
相关产品推荐

