Post

Replies

Boosts

Views

Activity

CPU Saturation with Charts real time plotting from data received from Bluetooth
Hello everyone, I am new to Swift, it is my first project and I am a PhD Electrical Engineer student. I am designing an iOS app for a device that we are designing that is capable of reading electrical brain data and sending them via BLE with a sampling frequency of 2400 Hz. I created a Bluetooth service for the Swift app that every time it receives new data, processes it to split the different channels and add the new data to the Charts data arrays. Here is the code I've designed: func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) { if characteristic.uuid == Nordic_UART_TX_CHAR_UUID { guard error == nil, let data = characteristic.value else { print("[Bluetooth] Error receiving data or no data: \(error?.localizedDescription ?? "Unknown Error")") return } DispatchQueue.global(qos: .background).async { self.processReceivedData(data) } } } func processReceivedData(_ data: Data) { var batch = [(Int, Int)]() for i in stride(from: 0, to: data.count - 4, by: 4) { let channel = Int(data[i] & 0xFF) let value = Int((Int(data[i + 3] & 0xFF) << 16) | (Int(data[i + 2] & 0xFF) << 8) | (Int(data[i + 1] & 0xFF))) - 8388608 batch.append((channel, value)) } DispatchQueue.main.async { for (channel, value) in batch { let nowTime = (Date().timeIntervalSince1970 - self.dataGraphService.startTime) let newDataPoint = DataGraphService.VoltagePerTime(time: nowTime, voltage: Double(value)/8388608, channel: "Channel \(channel - 15)") if channel == 16 { self.dataGraphService.lastX1 = nowTime self.dataGraphService.dataCh1.append(newDataPoint) } else if channel == 17 { self.dataGraphService.lastX2 = nowTime self.dataGraphService.dataCh2.append(newDataPoint) } else if channel == 18 { self.dataGraphService.lastX3 = nowTime self.dataGraphService.dataCh3.append(newDataPoint) } else if channel == 19 { self.dataGraphService.lastX4 = nowTime self.dataGraphService.dataCh4.append(newDataPoint) } } } } // DataGraphService.swift struct VoltagePerTime { var time: Double var voltage: Double var channel: String } @Published var dataCh1: [VoltagePerTime] = [] @Published var dataCh2: [VoltagePerTime] = [] @Published var dataCh3: [VoltagePerTime] = [] @Published var dataCh4: [VoltagePerTime] = [] @Published var windowSize: Double = 2.0 @Published var lastX1: Double = 0 @Published var lastX2: Double = 0 @Published var lastX3: Double = 0 @Published var lastX4: Double = 0 I also created a View that shows the real-time data from the different channels. ChartView( data: dataGraphService.dataCh1.filter { dataGraphService.getXAxisRange(for: dataGraphService.dataCh1, windowSize: dataGraphService.windowSize).contains($0.time) }, xAxisRange: dataGraphService.getXAxisRange(for: dataGraphService.dataCh1, windowSize: dataGraphService.windowSize), channel: "Channel 1", windowSize: dataGraphService.windowSize ) // ChartView.swift import SwiftUI import Charts struct ChartView: View { var data: [DataGraphService.VoltagePerTime] var xAxisRange: ClosedRange<Double> var channel: String var windowSize: Double var body: some View { RoundedRectangle(cornerRadius: 10) .fill(Color.gray.opacity(0.1)) .overlay( VStack{ Text("\(channel)") .foregroundColor(Color.gray) .font(.system(size: 16, weight: .semibold)) Chart(data, id: \.time) { item in LineMark( x: .value("Time [s]", item.time), y: .value("Voltage [V]", item.voltage) ) } .chartYAxisLabel(position: .leading) { Text("Voltage [V]") } .chartYScale(domain: [-1.6, 1.6]) .chartYAxis { AxisMarks(position: .leading, values: [-1.6, -0.8, 0, 0.8, 1.6]) AxisMarks(values: [-1.6, -1.2, -0.8, -0.4, 0, 0.4, 0.8, 1.2, 1.6]) { AxisGridLine() } } .chartXAxisLabel(position: .bottom, alignment: .center) { Text("Time [s]") } .chartXScale(domain: xAxisRange) .chartXAxis { AxisMarks(values: .automatic(desiredCount: Int(windowSize)*2)) AxisMarks(values: .automatic(desiredCount: 4*Int(windowSize)-2)) { AxisGridLine() } } .padding(5) } ) .padding(2.5) .padding([.leading, .trailing], 5) } } With these code I can receive and plot the data in real-time but after some time the CPU of the iPhone gets saturated and the app stop working. I have the guess that the code is designed in a way that the functions are called one inside the other one in a very fast speed that the CPU cannot handle. My doubt is if there is any other way to code this real-time plotting actions without make the iPhone's CPU power hungry. Thank you very much for your help!
1
1
609
Jan ’24