Working with bluetooth on an ESP32, connecting with iOS

I have a server running on a Seeed Studio ESP32S3, the code I got from the Seeed Wikipedia.

//Server Code
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>

#define SERVICE_UUID        "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"

void setup() {
  Serial.begin(115200);
  Serial.println("Starting BLE work!");

  BLEDevice::init("XIAO_ESP32S3");
  BLEServer *pServer = BLEDevice::createServer();
  BLEService *pService = pServer->createService(SERVICE_UUID);
  BLECharacteristic *pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID,
  BLECharacteristic::PROPERTY_READ |
  BLECharacteristic::PROPERTY_WRITE
  );

  pCharacteristic->setValue("Hello World");
  pService->start();

  BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
  pAdvertising->addServiceUUID(SERVICE_UUID);
  pAdvertising->setScanResponse(true);
  pAdvertising->setMinPreferred(0x06);  // functions that help with iPhone connections issue
  pAdvertising->setMinPreferred(0x12);
  BLEDevice::startAdvertising();
  Serial.println("Characteristic defined! Now you can read it in your phone!");
}

void loop() {
// put your main code here, to run repeatedly:
  delay(2000);
}//Server Code

And I am trying to connect to it with an iOS device [an iPhone 13] that I got from the Apple website.

import Foundation
import CoreBluetooth

struct TransferService {

static let serviceUUID = CBUUID(string:"4fafc201-1fb5-459e-8fcc-c5c9c331914b")
static let characteristicUUID = CBUUID(string:"beb5483e-36e1-4688-b7f5-ea07361b26a8")
                                    
}

class CentralViewController: UIViewController {
// UIViewController overrides, properties specific to this class, private helper methods, etc.

  @IBOutlet var textView: UITextView!

  var centralManager: CBCentralManager!

  var discoveredPeripheral: CBPeripheral?
  var transferCharacteristic: CBCharacteristic?
  var writeIterationsComplete = 0
  var connectionIterationsComplete = 0

  let defaultIterations = 5     // change this value based on test usecase

  var data = Data()

  // MARK: - view lifecycle

  override func viewDidLoad() {
      centralManager = CBCentralManager(delegate: self, queue: nil, options: [CBCentralManagerOptionShowPowerAlertKey: true])
      super.viewDidLoad()

  }

  override func viewWillDisappear(_ animated: Bool) {
      // Don't keep it going while we're not showing.
      centralManager.stopScan()
      os_log("Scanning stopped")

      data.removeAll(keepingCapacity: false)
    
      super.viewWillDisappear(animated)
  }

  // MARK: - Helper Methods

  /*
   * We will first check if we are already connected to our counterpart
   * Otherwise, scan for peripherals - specifically for our service's 128bit CBUUID
  */

  private func retrieveConnectedPeripheral(forIdentifier uuid: String) -> CBPeripheral? {
        return centralManager
            .retrieveConnectedPeripherals(withServices: [TransferService.serviceUUID])
            .first
  }

  private func retrievePeripheral() {
    print("retrievePeripheral")
    let connectedPeripherals: [CBPeripheral] = (centralManager.retrieveConnectedPeripherals(withServices: [TransferService.serviceUUID]))
    
    os_log("Found connected Peripherals with transfer service: %d", connectedPeripherals.count)
    
    os_log("Connecting to peripheral %@", connectedPeripherals.first ?? "foo")
    
    if let connectedPeripheral = connectedPeripherals.last {
        os_log("Connecting to peripheral %@", connectedPeripheral)
        self.discoveredPeripheral = connectedPeripheral
        centralManager.connect(connectedPeripheral, options: nil)
    } else {
        // We were not connected to our counterpart, so start scanning
        os_log("foo Something went Wrong")
        centralManager.scanForPeripherals(withServices: [TransferService.serviceUUID],
                                           options: [CBCentralManagerScanOptionAllowDuplicatesKey: true])
    }
}

/*
 *  Call this when things either go wrong, or you're done with the connection.
 *  This cancels any subscriptions if there are any, or straight disconnects if not.
 *  (didUpdateNotificationStateForCharacteristic will cancel the connection if a subscription is involved)
 */
private func cleanup() {
    // Don't do anything if we're not connected
    guard let discoveredPeripheral = discoveredPeripheral,
        case .connected = discoveredPeripheral.state else { return }
    
    for service in (discoveredPeripheral.services ?? [] as [CBService]) {
        for characteristic in (service.characteristics ?? [] as [CBCharacteristic]) {
            if characteristic.uuid == TransferService.characteristicUUID && characteristic.isNotifying {
                // It is notifying, so unsubscribe
                self.discoveredPeripheral?.setNotifyValue(false, for: characteristic)
            }
        }
    }
    
    // If we've gotten this far, we're connected, but we're not subscribed, so we just disconnect
    centralManager.cancelPeripheralConnection(discoveredPeripheral)
}

/*
 *  Write some test data to peripheral
 */
private func writeData() {

    guard let discoveredPeripheral = discoveredPeripheral,
            let transferCharacteristic = transferCharacteristic
        else { return }
    
    // check to see if number of iterations completed and peripheral can accept more data
    while writeIterationsComplete < defaultIterations && discoveredPeripheral.canSendWriteWithoutResponse {
                
        let mtu = discoveredPeripheral.maximumWriteValueLength(for: .withoutResponse)
        var rawPacket = [UInt8]()
        
        let bytesToCopy: size_t = min(mtu, data.count)
        data.copyBytes(to: &rawPacket, count: bytesToCopy)
        let packetData = Data(bytes: &rawPacket, count: bytesToCopy)
        
        let stringFromData = String(data: packetData, encoding: .utf8)
        os_log("Writing %d bytes: %s", bytesToCopy, String(describing: stringFromData))
        
        discoveredPeripheral.writeValue(packetData, for: transferCharacteristic, type: .withoutResponse)
        
        writeIterationsComplete += 1
        
    }
    
    if writeIterationsComplete == defaultIterations {
        // Cancel our subscription to the characteristic
        discoveredPeripheral.setNotifyValue(false, for: transferCharacteristic)
    }
}

}

extension CentralViewController: CBCentralManagerDelegate {
// implementations of the CBCentralManagerDelegate methods

/*
 *  centralManagerDidUpdateState is a required protocol method.
 *  Usually, you'd check for other states to make sure the current device supports LE, is powered on, etc.
 *  In this instance, we're just using it to wait for CBCentralManagerStatePoweredOn, which indicates
 *  the Central is ready to be used.
 */
internal func centralManagerDidUpdateState(_ central: CBCentralManager) {

    switch central.state {
    case .poweredOn:
        // ... so start working with the peripheral
        os_log("CBManager is powered on")
        retrievePeripheral()
        //let foo = retrieveConnectedPeripheral(forIdentifier: "4fafc201-1fb5-459e-8fcc-c5c9c331914b")
        
    case .poweredOff:
        os_log("CBManager is not powered on")
        // In a real app, you'd deal with all the states accordingly
        return
    case .resetting:
        os_log("CBManager is resetting")
        // In a real app, you'd deal with all the states accordingly
        return
    case .unauthorized:
        // In a real app, you'd deal with all the states accordingly
        if #available(iOS 13.0, *) {
            switch central.authorization {
            case .denied:
                os_log("You are not authorized to use Bluetooth")
            case .restricted:
                os_log("Bluetooth is restricted")
            default:
                os_log("Unexpected authorization")
            }
        } else {
            // Fallback on earlier versions
        }
        return
    case .unknown:
        os_log("CBManager state is unknown")
        // In a real app, you'd deal with all the states accordingly
        return
    case .unsupported:
        os_log("Bluetooth is not supported on this device")
        // In a real app, you'd deal with all the states accordingly
        return
    @unknown default:
        os_log("A previously unknown central manager state occurred")
        // In a real app, you'd deal with yet unknown cases that might occur in the future
        return
    }
}

/*
 *  This callback comes whenever a peripheral that is advertising the transfer serviceUUID is discovered.
 *  We check the RSSI, to make sure it's close enough that we're interested in it, and if it is,
 *  we start the connection process
 */
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral,
                    advertisementData: [String: Any], rssi RSSI: NSNumber) {
    
    // Reject if the signal strength is too low to attempt data transfer.
    // Change the minimum RSSI value depending on your app’s use case.
    guard RSSI.intValue >= -50
        else {
            os_log("Discovered perhiperal not in expected range, at %d", RSSI.intValue)
            return
    }
    
    os_log("Discovered %s at %d", String(describing: peripheral.name), RSSI.intValue)
    
    // Device is in range - have we already seen it?
    if discoveredPeripheral != peripheral {
        
        // Save a local copy of the peripheral, so CoreBluetooth doesn't get rid of it.
        discoveredPeripheral = peripheral
        
        // And finally, connect to the peripheral.
        os_log("Connecting to perhiperal %@", peripheral)
        centralManager.connect(peripheral, options: nil)
    }
}

/*
 *  If the connection fails for whatever reason, we need to deal with it.
 */
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
    os_log("Failed to connect to %@. %s", peripheral, String(describing: error))
    cleanup()
}

/*
 *  We've connected to the peripheral, now we need to discover the services and characteristics to find the 'transfer' characteristic.
 */
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) 
{
    os_log("Peripheral Connected")
    
    // Stop scanning
    centralManager.stopScan()
    os_log("Scanning stopped")
    
    // set iteration info
    connectionIterationsComplete += 1
    writeIterationsComplete = 0
    
    // Clear the data that we may already have
    data.removeAll(keepingCapacity: false)
    
    // Make sure we get the discovery callbacks
    peripheral.delegate = self
    
    // Search only for services that match our UUID
    peripheral.discoverServices([TransferService.serviceUUID])
}

/*
 *  Once the disconnection happens, we need to clean up our local copy of the peripheral
 */
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
    os_log("Perhiperal Disconnected")
    discoveredPeripheral = nil
    
    // We're disconnected, so start scanning again
    if connectionIterationsComplete < defaultIterations {
        retrievePeripheral()
    } else {
        os_log("Connection iterations completed")
    }
}

}

extension CentralViewController: CBPeripheralDelegate {
// implementations of the CBPeripheralDelegate methods

/*
 *  The peripheral letting us know when services have been invalidated.
 */
func peripheral(_ peripheral: CBPeripheral, didModifyServices invalidatedServices: [CBService]) {
    print("func didModifyServices")
    for service in invalidatedServices where service.uuid == TransferService.serviceUUID {
        os_log("Transfer service is invalidated - rediscover services")
        peripheral.discoverServices([TransferService.serviceUUID])
    }
}

/*
 *  The Transfer Service was discovered
 */
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
    print("func didDiscoverServices")
    if let error = error {
        os_log("Error discovering services: %s", error.localizedDescription)
        cleanup()
        return
    }
    
    // Discover the characteristic we want...
    
    // Loop through the newly filled peripheral.services array, just in case there's more than one.
    guard let peripheralServices = peripheral.services else { return }
    for service in peripheralServices {
        peripheral.discoverCharacteristics([TransferService.characteristicUUID], for: service)
    }
    
}

/*
 *  The Transfer characteristic was discovered.
 *  Once this has been found, we want to subscribe to it, which lets the peripheral know we want the data it contains
 */
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
    // Deal with errors (if any).
    print("func didDiscoverCharacteristicsFor")
    if let error = error {
        os_log("Error discovering characteristics: %s", error.localizedDescription)
        cleanup()
        return
    }
    
    // Again, we loop through the array, just in case and check if it's the right one
    guard let serviceCharacteristics = service.characteristics else { return }
    for characteristic in serviceCharacteristics where characteristic.uuid == TransferService.characteristicUUID {
        // If it is, subscribe to it
        transferCharacteristic = characteristic
        peripheral.setNotifyValue(true, for: characteristic)
    }
    
    // Once this is complete, we just need to wait for the data to come in.
}

/*
 *   This callback lets us know more data has arrived via notification on the characteristic
 */
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
    // Deal with errors (if any)
    print("func didUpdateValueFor")
    if let error = error {
        os_log("Error discovering characteristics: %s", error.localizedDescription)
        cleanup()
        return
    }
    
    guard let characteristicData = characteristic.value,
        let stringFromData = String(data: characteristicData, encoding: .utf8) else { return }
    
    os_log("Received %d bytes: %s", characteristicData.count, stringFromData)
    
    // Have we received the end-of-message token?
    if stringFromData == "EOM" {
        // End-of-message case: show the data.
        // Dispatch the text view update to the main queue for updating the UI, because
        // we don't know which thread this method will be called back on.
        DispatchQueue.main.async() {
            self.textView.text = String(data: self.data, encoding: .utf8)
        }
        
        // Write test data
        writeData()
    } else {
        // Otherwise, just append the data to what we have previously received.
        data.append(characteristicData)
    }
}

/*
 *  The peripheral letting us know whether our subscribe/unsubscribe happened or not
 */
func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
    print("func didUpdateNotificationStateFor")
    // Deal with errors (if any)
    if let error = error {
        os_log("Error changing notification state: %s", error.localizedDescription)
        return
    }
    
    // Exit if it's not the transfer characteristic
    guard characteristic.uuid == TransferService.characteristicUUID else { return }
    
    if characteristic.isNotifying {
        // Notification has started
        os_log("Notification began on %@", characteristic)
    } else {
        // Notification has stopped, so disconnect from the peripheral
        os_log("Notification stopped on %@. Disconnecting", characteristic)
        cleanup()
    }
    
}

/*
 *  This is called when peripheral is ready to accept more data when using write without response
 */
func peripheralIsReady(toSendWriteWithoutResponse peripheral: CBPeripheral) {
    print("func toSendWriteWithoutResponse")
    os_log("Peripheral is ready, send data")
    writeData()
}

}

I run the server on the ESP, iOS sees it, connects but fails when it reaches func didUpdateNotificationStateFor with an error.

Error changing notification state: The request is not supported. So I fixed this by ignoring it, but for the life of me I am unable to find the value set by the ESP32 here “Hello World”

Looking in didDiscoverCharacteristicsFor after it has connected, but cannot find “Hello World” value set by ESP32

5

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật