import CoreBluetooth import UIKit class ViewController: UIViewController, CBCentralManagerDelegate, CBPeripheralDelegate { var centralManager: CBCentralManager! var discoveredPeripheral: CBPeripheral? var ruuviServiceUUID = CBUUID(string: "8EC90001-F315-4F60-9FB8-838830DAEA50") override func viewDidLoad() { super.viewDidLoad() // Use a unique restoration identifier when initializing CBCentralManager centralManager = CBCentralManager(delegate: self, queue: nil, options: [CBCentralManagerOptionRestoreIdentifierKey: "myCentralManagerIdentifier"]) } // Check Bluetooth availability func centralManagerDidUpdateState(_ central: CBCentralManager) { if central.state == .poweredOn { // Start scanning for all peripherals (no UUID filter for background compatibility) centralManager.scanForPeripherals(withServices: [ruuviServiceUUID], options: [ CBCentralManagerScanOptionAllowDuplicatesKey: true ]) print("Started scanning for devices.") } else { print("Bluetooth is not available.") } } // Handle peripheral discovery func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String: Any], rssi RSSI: NSNumber) { let deviceName = advertisementData[CBAdvertisementDataLocalNameKey] as? String ?? "Unknown Device" print("Discovered \(deviceName) with RSSI: \(RSSI)") // Stop scanning once a peripheral is found centralManager.stopScan() discoveredPeripheral = peripheral discoveredPeripheral?.delegate = self // Connect to the peripheral centralManager.connect(peripheral, options: nil) } func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) { print("Connected to \(peripheral.name ?? "Unknown")") peripheral.discoverServices([ruuviServiceUUID]) // Discover services after connection } // Handle service discovery func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) { if let error = error { print("Error discovering services: \(error.localizedDescription)") return } guard let services = peripheral.services else { return } print("Discovered services: \(services)") // Iterate through discovered services and discover characteristics for the target service for service in services { if service.uuid == ruuviServiceUUID { print("Target service found: \(service.uuid)") // Discover all characteristics for this service peripheral.discoverCharacteristics(nil, for: service) } } } // Handle characteristic discovery func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) { if let error = error { print("Error discovering characteristics: \(error.localizedDescription)") return } guard let characteristics = service.characteristics else { return } print("Discovered characteristics for service \(service.uuid): \(characteristics)") // Iterate through discovered characteristics for characteristic in characteristics { print("Characteristic: \(characteristic.uuid), Properties: \(characteristic.properties)") // If the characteristic is readable, read its value if characteristic.properties.contains(.read) { peripheral.readValue(for: characteristic) } // If the characteristic is notifiable, subscribe to it if characteristic.properties.contains(.notify) { peripheral.setNotifyValue(true, for: characteristic) } } } // Handle reading characteristic value func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) { if let error = error { print("Error reading characteristic value: \(error.localizedDescription)") return } if let value = characteristic.value { print("Value for characteristic \(characteristic.uuid): \(value.hexEncodedString())") } } func centralManager(_ central: CBCentralManager, willRestoreState dict: [String: Any]) { if let restoredPeripherals = dict[CBCentralManagerRestoredStatePeripheralsKey] as? [CBPeripheral] { for peripheral in restoredPeripherals { print("Restored peripheral: \(peripheral)") peripheral.delegate = self if peripheral.state == .connected { // If already connected, discover services peripheral.discoverServices([ruuviServiceUUID]) } else { // Reconnect to peripheral central.connect(peripheral, options: nil) } } } } // Function to convert manufacturer data to readable format func getConvertedData(from manufacturerData: Data) -> [String: Any] { // Example: Parsing the RuuviTag V5 format (adjust according to the actual format you need) // You can decode temperature, humidity, pressure, etc. from the hex string here. var parsedData: [String: Any] = [:] // Temperature is in the first 2 bytes, signed integer (multiply by 0.005 to get in Celsius) let temperatureRaw = Int16(bitPattern: UInt16(manufacturerData[1]) << 8 | UInt16(manufacturerData[2])) let temperatureCelsius = Double(temperatureRaw) * 0.005 parsedData["Temperature (°C)"] = temperatureCelsius // Humidity is in the next 2 bytes, unsigned integer (multiply by 0.0025 to get percentage) let humidityRaw = UInt16(manufacturerData[3]) << 8 | UInt16(manufacturerData[4]) let humidityPercent = Double(humidityRaw) * 0.0025 parsedData["Humidity (%)"] = humidityPercent // Pressure is in the next 2 bytes, unsigned integer (add 50000 to get in Pa) let pressureRaw = UInt16(manufacturerData[5]) << 8 | UInt16(manufacturerData[6]) let pressurePa = Double(pressureRaw) + 50000 parsedData["Pressure (Pa)"] = pressurePa // Add other fields as necessary, depending on the sensor data format return parsedData } // Function to extract manufacturer UUID from the manufacturer data func extractManufacturerUUID(from manufacturerData: Data) -> String { // Assuming the first few bytes contain the manufacturer UUID return manufacturerData.subdata(in: 0..<2).hexEncodedString() // Modify the range based on the manufacturer data format } // Function to check if it's a Ruuvi device by UUID func isRuuviDevice(manufacturerUUID: String) -> Bool { let ruuviManufacturerUUID = "YourRuuviManufacturerUUIDHere" // Replace with the correct UUID return manufacturerUUID == ruuviManufacturerUUID } } // Extension for hex string encoding extension Data { func hexEncodedString() -> String { return map { String(format: "%02hhx", $0) }.joined() } }