首页 > 解决方案 > 如何让 iBeacon 到在 iPhone 上运行的 Raspbberry PI?

问题描述

我有一个带有 Cambridge Scientific USB 适配器的 Raspberry PI,在该适配器上我有以下内容:

sudo  hciconfig hci0 up
sudo  hciconfig hci0 leadv 3
sudo hcitool -i hci0 cmd 0x08 0x0008 1E 02 01 06 1A FF 4C 00 02 15 C7 C1 A1 BF BB 00 4C AD 87 04 9F 2D 29 17 DE D2 00 00 00 00 C8 00

我编写了一个 MacOS Cocoa App,如下所示:

import Cocoa
import CoreBluetooth
class ViewController: NSViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        BeaconScanner(type: BeaconType.iBeacon).scanEverySecond()
    }
}
enum BeaconType {
    case iBeacon
}
struct Beacon {
    var uuid: NSUUID!
    var major: UInt16!
    var minor: UInt16!
    var power: Int8!
    var peripheral: CBPeripheral!
    var advertisementData: NSDictionary!
    var RSSI: NSNumber!
    init(peripheral: CBPeripheral, advertisementData: NSDictionary, RSSI: NSNumber) {
        self.peripheral = peripheral
        self.advertisementData = advertisementData
        self.RSSI = RSSI
    }
    private func getBytes(data: Any?, from: Int, count: Int) -> [CUnsignedChar] {
        var bytes = [CUnsignedChar](repeating: 0, count: count)
        (data! as AnyObject).getBytes(&bytes, range: NSMakeRange(from, count))
        return bytes
    }
    private func getBytesAsUshort(data: Any?, from: Int) -> ushort {
        var bytes = ushort()
        (data! as AnyObject).getBytes(&bytes, range: NSMakeRange(from, 2))
        return NSSwapShort(bytes)
    }
    private func getBytesAsInt8(data: Any?, from: Int) -> Int8 {
        var bytes = Int8()
        (data! as AnyObject).getBytes(&bytes, range: NSMakeRange(from, 1))
        return bytes
    }
    func checkAdvertisementDataForPossibleiBeaconData() {
        let beaconStandard: [CUnsignedChar] = [0x4c, 0x00, 0x02, 0x15]
        var data = advertisementData.value(forKey: "kCBAdvDataManufacturerData")
        if(data != nil) {
            data = data as! Data
            var beaconStandardBytes = [CUnsignedChar](repeating: 0, count: 4)
            (data! as AnyObject).getBytes(&beaconStandardBytes, range: NSMakeRange(0,4))
            if((data! as AnyObject).length >= 25 && beaconStandardBytes.elementsEqual(beaconStandard)) {
                let uuid = NSUUID.init(uuidBytes: getBytes(data: data, from: 4, count: 16)) as UUID
                let major = getBytesAsUshort(data: data, from: 20)
                let minor = getBytesAsUshort(data: data, from: 22)
                let power = getBytesAsInt8(data: data, from: 24)
                let distance = calculateBeaconDistance(Int(power), RSSI: Int(truncating: RSSI))
                print("BeaconStandard=\(beaconStandard), UUID=\(uuid.uuidString), major=\(major), minor=\(minor), power=\(power), RSSI=\(String(describing: RSSI)), Distance=\(String(format: "%.2f", distance))m")
            }
        }
    }
    func calculateBeaconDistance(_ power: Int, RSSI: Int) -> Double {
        let ratio = Double(RSSI) * 1.0 / Double(power)
        return ratio < 1.0 ? pow(ratio, 10.0) : round(10*((0.89976) * pow(ratio, 7.7095) + 0.111)) / 10
    }
}
class BeaconScanner: NSObject, CBCentralManagerDelegate {
    var cbManager: CBCentralManager!
    var timer: Timer!
    var type: BeaconType!
    init(type: BeaconType) {
        self.type = type
    }
    func scanEverySecond() {
        cbManager = CBCentralManager(delegate: self, queue: nil)
        self.timer = Timer.scheduledTimer(timeInterval: TimeInterval(1), target: self, selector: #selector(scan), userInfo: nil, repeats: true)
    }
    @objc func scan() {
        if(cbManager != nil && cbManager.state.rawValue==CBManagerState.poweredOn.rawValue) {
            cbManager.scanForPeripherals(withServices: nil, options: nil)
        }
    }
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
        Beacon(peripheral: peripheral, advertisementData: advertisementData as NSDictionary, RSSI: RSSI).checkAdvertisementDataForPossibleiBeaconData()
    }
    func centralManagerDidUpdateState(_ central: CBCentralManager) {

        if(!(central.state.rawValue == CBManagerState.poweredOn.rawValue)) {
            print("In order to user this tool, Bluetooth must be switched on\n")
            exit(EXIT_FAILURE)
        }
    }
}

这似乎工作得很好。我得到以下信息:

BeaconStandard=[76, 0, 2, 21], UUID=C7C1A1BF-BB00-4CAD-8704-9F2D2917DED2, major=0, minor=0, power=-56, RSSI=Optional(-74), Distance=7.80m
BeaconStandard=[76, 0, 2, 21], UUID=C7C1A1BF-BB00-4CAD-8704-9F2D2917DED2, major=0, minor=0, power=-56, RSSI=Optional(-76), Distance=9.60m

然后我构建了一个 iOS 应用程序,只需复制 ViewController 代码替换

import Cocoa
import CoreBluetooth
class ViewController: NSViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        BeaconScanner(type: BeaconType.iBeacon).scanEverySecond()
    }
}

import UIKit
import CoreBluetooth
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        BeaconScanner(type: BeaconType.iBeacon).scanEverySecond()
    }
}

我在 iOS 上运行了大约 5 分钟的代码,但没有成功。

我添加了一些调试代码,我确实看到了来自其他蓝牙设备的点击,但没有来自 PI。

MacOS 正在运行 10.14.6。iOS 运行 12.3.1,XCode 运行 10.3。

这是硬件限制吗?我知道 CSR 蓝牙很便宜。还是我的代码有问题?

标签: bluetoothraspberry-pixcode10ios12

解决方案


推荐阅读