Arduino HM-10 Bluetooth with iOS : Connected, but it doesn't send values
Tags: arduino, ios, swift, swiftui
Published: 2023-07-24 08:24:06

I'm trying to make an iOS (SwiftUI based) app to connect the arduino and send the current latitude value of ios to arduino. I successfully made the view that allow user to select the bluetooth device, and the UUID/name of the device is successfully displayed. Also, the arduino device detects connection (I used LiquidCrystal to display serial and it says "OK+CONN" and "OK+LOST" if connection was lost.), but when I click "fetch location" button, nothing happens on arduino side.

Also, when I click select device, it automatically navigates to mainview(), and I can't figure why.. (This is less important than the bluetooth problem...)

For your information, I'll attach my frontend and backend codes.

**ConnectView.swift**
```swift
import SwiftUI
import CoreBluetooth
import Awesome

struct ConnectView: View {
    @State private var isDeviceSelectionPresented = false
    @State private var selectedDevice: CBPeripheral?
    @State private var isSendingData = false
    private var writeType: CBCharacteristicWriteType = .withoutResponse
    
    @StateObject private var bleManager = BLEManager()
    @StateObject private var locationDelegate = LocationDelegate()
    
    private var peripherals: [CBPeripheral] { bleManager.peripherals }
    private var txCharacteristic: CBCharacteristic?
    
    weak var writeCharacteristic: CBCharacteristic?
    
    var body: some View {
        NavigationView {
            VStack(spacing: 20) {
                HeadView(headTitle: "Cydial 장치 연결하기")
                Button(action: {
                    isDeviceSelectionPresented = true
                }) {
                    ZStack{
                        RoundedRectangle(cornerRadius: 8)
                            .stroke(Color.accentColor, lineWidth:2)
                            .frame(height:40)
                        HStack(spacing:10){
                            Image(systemName:"checkmark")
                            Text("Select Device")
                                .fontWeight(.semibold)
                        }
                    }
                }
                .sheet(isPresented: $isDeviceSelectionPresented) {
                    DeviceSelectionView(selectedDevice: $selectedDevice, isPresented: $isDeviceSelectionPresented)
                        .environmentObject(bleManager)
                }
                
                if let connectedDevice = bleManager.connectedPeripheral {
                    VStack(alignment:.leading ,spacing: 5) {
                        HStack{
                            Image(systemName:"checkmark.circle")
                            Text("Device Connected")
                        }
                        .font(.title3)
                        .fontWeight(.semibold)
                        Text("Device Name: \(connectedDevice.name ?? "Unknown")")
                        Text("UUID: \(connectedDevice.identifier.uuidString)")
                    }
                    .padding(.top, 10)
                }
                HStack{
                    Image(systemName:"location.circle")
                    Text("Current Latitude: \(locationDelegate.latitudeValue)")
                    Spacer()
                }
                
                Button(action: {
//                    sendMessageToDevice("\(locationDelegate.latitudeValue)")
                    sendDataToArduino()
                }) {
                    ZStack{
                        RoundedRectangle(cornerRadius: 8)
                            .stroke(Color.accentColor, lineWidth:2)
                            .frame(height:40)
                        HStack(spacing:10){
                            Image(systemName:"rectangle.portrait.and.arrow.forward")
                            Text("Fetch Location")
                                .fontWeight(.semibold)
                        }
                    }
                }
                Spacer()
            }
            .padding()
        }
    }
    
    func sendDataToArduino() {
        guard let peripheral = selectedDevice,
              let txCharacteristic = txCharacteristic,
              let dataToSend = "\(locationDelegate.latitudeValue)".data(using: .utf8) else {
            print("Error: Bluetooth is not ready.")
            return
        }
        
        isSendingData = true
        selectedDevice?.writeValue(dataToSend, for: txCharacteristic, type: writeType)
    }
    
    func sendMessageToDevice(_ message: String) {
        if let data = message.data(using: String.Encoding.utf8) {
            selectedDevice!.writeValue(data, for: writeCharacteristic!, type: writeType)
        }
    }
    
//    func sendBytesToDevice(_ bytes: [UInt8]) {
//        let data = Data(bytes: UnsafePointer<UInt8>(bytes), count: bytes.count)
//        selectedDevice!.writeValue(data, for: writeCharacteristic!, type: writeType)
//    }
//    func floatToUInt8(_ value: Float) -> UInt8? {
//        // Check if the value is within the valid range for UInt8
//        let max = (UInt32(UInt16.max) + 1) * UInt32(UInt32(UInt8.max) + 1) - 1
//                let int = UInt32(((self / maxRange + 1.0) / 2.0 * Float(max)).rounded())
//                let a = int.quotientAndRemainder(dividingBy: UInt32(UInt16.max) + 1)
//                let b = a.remainder.quotientAndRemainder(dividingBy: UInt32(UInt8.max) + 1)
//                return [UInt8(a.quotient), UInt8(b.quotient), UInt8(b.remainder), 0]
//    }

}

struct ConnectView_Previews: PreviewProvider {
    static var previews: some View {
        ConnectView()
    }
}

```

**DeviceSelectionView.swift**
```swift
import SwiftUI
import CoreBluetooth

struct DeviceSelectionView: View {
    @Binding var selectedDevice: CBPeripheral?
    @EnvironmentObject var bleManager: BLEManager
    @Binding var isPresented: Bool
    
    var body: some View {
        List(bleManager.peripherals, id: \.self) { peripheral in
            Button(action: {
                bleManager.connectToDevice(peripheral)
                isPresented = false
            }) {
                Text(peripheral.name ?? "Unknown Device")
            }
        }
        .onAppear {
            bleManager.startScanning()
        }
        .onDisappear {
            bleManager.stopScanning()
        }
        .onChange(of: bleManager.connectedPeripheral) { connectedPeripheral in
            if let selectedDevice = selectedDevice, selectedDevice == connectedPeripheral {
                isPresented = false
            }
        }
    }
}

struct DeviceSelectionView_Previews: PreviewProvider {
    static var previews: some View {
        DeviceSelectionView(selectedDevice: .constant(nil), isPresented: .constant(true))
            .environmentObject(BLEManager())
    }
}

```


**BLEManager.swift**
```swift
import Foundation
import CoreBluetooth
import CoreLocation

class LocationDelegate: NSObject, ObservableObject, CLLocationManagerDelegate {
    @Published var latitudeValue: Float = 0.0
    private let locationManager = CLLocationManager()
    
    override init() {
        super.init()
        locationManager.delegate = self
        locationManager.requestWhenInUseAuthorization()
        locationManager.startUpdatingLocation()
    }
    
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        if let location = locations.last {
            latitudeValue = Float(location.coordinate.latitude)
        }
    }
}

class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeripheralDelegate {
    var centralManager: CBCentralManager!
    @Published var peripherals: [CBPeripheral] = []
    @Published var connectedPeripheral: CBPeripheral?
    @Published var txCharacteristic: CBCharacteristic?
    
    override init() {
        super.init()
        centralManager = CBCentralManager(delegate: self, queue: nil)
    }
    
    func startScanning() {
        centralManager.scanForPeripherals(withServices: nil, options: nil)
    }
    
    func stopScanning() {
        centralManager.stopScan()
    }
    
    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        if central.state == .poweredOn {
            startScanning()
        } else {
            print("Error: Bluetooth is not available.")
        }
    }
    
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
        if !peripherals.contains(peripheral) {
            peripherals.append(peripheral)
        }
    }
    
    func connectToDevice(_ peripheral: CBPeripheral) {
        connectedPeripheral = peripheral
        centralManager.connect(peripheral, options: nil)
    }
    
    func disconnectFromDevice() {
        if let peripheral = connectedPeripheral {
            centralManager.cancelPeripheralConnection(peripheral)
            connectedPeripheral = nil
        }
    }
    
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
        peripheral.delegate = self
        peripheral.discoverServices(nil)
    }
    
    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
        if let services = peripheral.services {
            for service in services {
                peripheral.discoverCharacteristics(nil, for: service)
            }
        }
    }
    
    func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
        if let characteristics = service.characteristics {
            for characteristic in characteristics {
                if characteristic.properties.contains(.write) {
                    txCharacteristic = characteristic
                }
            }
        }
    }
}

```

I tried to port UIKit based example project to this, but it doesn't work: [Question](https://stackoverflow.com/questions/76752453/integrate-uikit-based-app-into-swiftui-based-app-multiple-commands-produce)