swiftbluetooth-lowenergyesp32core-bluetooth

BLE sent String between ESP32 and Iphone


I need to send text from an Iphone via BLE to an ESP32. The phone App connects to the ESP32 and stays connected successfully. But when I press send the console says the sending was successful. But on der Serial Monitor of the ESP32 appeares nothing. With an BLE scanner App all works fine.

Here is the code for the ESP32:

#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>

#define SERVICE_UUID        "8c2a81f7-f8b8-4b31-89b4-6b5d98a822db"
#define CHARACTERISTIC_UUID "e8bd8c82-2506-4fae-b5f2-9bbbf4ab5b0e"

BLECharacteristic *characteristic;

class MyServerCallbacks : public BLEServerCallbacks {
    void onConnect(BLEServer *pServer) {
        // Verbindung hergestellt
    }

    void onDisconnect(BLEServer *pServer) {
        // Verbindung getrennt
    }
};

class MyCallbacks : public BLECharacteristicCallbacks {
    void onWrite(BLECharacteristic *pCharacteristic) {
        std::string value = pCharacteristic->getValue();
        if (value.length() > 0) {
            Serial.println("Empfangene Daten:");
            Serial.println(value.c_str());
        }
    }
};

void setup() {
    Serial.begin(115200);

    BLEDevice::init("ESP32");
    BLEServer *pServer = BLEDevice::createServer();
    pServer->setCallbacks(new MyServerCallbacks());

    BLEService *pService = pServer->createService(SERVICE_UUID);
    characteristic = pService->createCharacteristic(
        CHARACTERISTIC_UUID,
        BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE
    );
    characteristic->setCallbacks(new MyCallbacks());
    pService->start();

    BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
    pAdvertising->addServiceUUID(SERVICE_UUID);
    pAdvertising->setScanResponse(false);
    pAdvertising->setMinPreferred(0x06);
    pAdvertising->setMinPreferred(0x12);
    BLEDevice::startAdvertising();
}

void loop() {
    // Hier können weitere Aktionen im Hintergrund durchgeführt werden
}

And here from the App:

import SwiftUI
import CoreBluetooth

class BluetoothManager: NSObject, CBCentralManagerDelegate, ObservableObject {
    let esp32ServiceUUID = CBUUID(string: "8c2a81f7-f8b8-4b31-89b4-6b5d98a822db")
    let esp32CharacteristicUUID = CBUUID(string: "e8bd8c82-2506-4fae-b5f2-9bbbf4ab5b0e")
    let centralManager: CBCentralManager
    var peripheral: CBPeripheral?
    var characteristic: CBCharacteristic?
    
    @Published var isConnected = false
    
    override init() {
        centralManager = CBCentralManager(delegate: nil, queue: nil)
        super.init()
        centralManager.delegate = self
    }
    
    func startScanning() {
        centralManager.scanForPeripherals(withServices: [esp32ServiceUUID])
        print("Started scanning for peripherals")
    }
    
    func sendNumberToESP32(text: String) {
        guard let peripheral = peripheral, let characteristic = characteristic else { return }
        
        guard let data = text.data(using: .utf8) else { return }
        peripheral.writeValue(data, for: characteristic, type: .withoutResponse)
        print("Sent data to ESP32: \(text)")
    }
    
    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        if central.state == .poweredOn {
            startScanning()
        } else {
            // Bluetooth ist nicht aktiviert oder nicht verfügbar
            print("Bluetooth not powered on or unavailable")
        }
    }
    
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral,
                        advertisementData: [String : Any], rssi RSSI: NSNumber) {
        if peripheral.name == "ESP32" {
            centralManager.stopScan()
            self.peripheral = peripheral
            self.peripheral?.delegate = self
            centralManager.connect(peripheral)
            print("Discovered ESP32 peripheral")
        }
    }
    
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
        isConnected = true
        print("Connected to peripheral")
        peripheral.discoverServices([esp32ServiceUUID])
    }
    
    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
        isConnected = false
        print("Disconnected from peripheral")
        centralManager.scanForPeripherals(withServices: [esp32ServiceUUID])
        print("Started scanning for peripherals again")
    }
}

extension BluetoothManager: CBPeripheralDelegate {
    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
        if let service = peripheral.services?.first(where: { $0.uuid == esp32ServiceUUID }) {
            peripheral.discoverCharacteristics([esp32CharacteristicUUID], for: service)
        }
    }
    
    func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
        if let characteristic = service.characteristics?.first(where: { $0.uuid == esp32CharacteristicUUID }) {
            self.characteristic = characteristic
        }
    }
}

struct ContentView: View {
    @State private var text: String = ""
    @StateObject private var bluetoothManager = BluetoothManager()
    
    var body: some View {
        VStack {
            Text(bluetoothManager.isConnected ? "Verbunden" : "Getrennt")
                .padding()
            
            TextField("Zahl eingeben",text: $text)
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .padding()
            
            Button(action: {
                bluetoothManager.sendNumberToESP32(text: text)
            }) {
                Text("Senden")
                    .padding()
                    .background(Color.blue)
                    .foregroundColor(.white)
                    .cornerRadius(10)
            }
        }
        .onAppear {
            bluetoothManager.startScanning()
        }
        .onChange(of: bluetoothManager.isConnected) { isConnected in
            print("Verbindungsstatus geändert: \(isConnected)")
        }
    }
}


Thanks for helping

I´ve asked ChatGPT but it has no idea, and I searched in the internet


Solution

  • You're not checking for errors here, so it's hard to be certain what's happening. Typically you should implement peripheral(_:didWriteValueFor:error:) to see that the value was actually written. I expect the problem is here:

        peripheral.writeValue(data, for: characteristic, type: .withoutResponse)
    

    You didn't configure the characteristic to allow writing without response (PROPERTY_WRITE_NR). You generally don't want that in any case. You should be using .withResponse here.