// // ViewController.swift // ble_esp32_sesam // // Created by Werner on 24/03/2024. // import UIKit import CoreBluetooth class ViewController: UIViewController, CBCentralManagerDelegate, CBPeripheralDelegate { private var centralQueue: DispatchQueue? private let serviceUUID = CBUUID(string: "186eaa96-ced3-43dc-bf61-3925b32308c9") private let inputCharUUID = CBUUID(string: "8fbc5b21-61ed-4244-8cde-7287b4aa35ec") private var inputChar: CBCharacteristic? private let outputCharUUID = CBUUID(string: "f1921465-7ce5-4994-a201-8494e068cbb7") private var outputChar: CBCharacteristic? // service and peripheral objects private var centralManager: CBCentralManager? private var connectedPeripheral: CBPeripheral? @Published var output = "Disconnected" // current text to display in the output field @Published var connected = false // true when BLE connection is active private var operands:[UInt8] = [0x01, 0x00, 0x00] // operand1, operand2, operation private lazy var loginButton: UIButton = { let button = UIButton() button.setTitle("Sesam Open Port", for: .normal) button.setTitleColor(.white, for: .normal) button.layer.cornerRadius = 10 button.layer.masksToBounds = true button.backgroundColor = .darkGray button.titleLabel?.font = UIFont.systemFont(ofSize: 18, weight: .semibold) button.addTarget(self, action: #selector(handleLoginButtonTapped), for: .touchUpInside) button.translatesAutoresizingMaskIntoConstraints = false return button }() override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. let notificationCenter = NotificationCenter.default notificationCenter.addObserver(self, selector: #selector(appMovedToBackground), name: UIApplication.willResignActiveNotification, object: nil) notificationCenter.addObserver(self, selector: #selector(appBecomeActive), name: UIApplication.didBecomeActiveNotification, object: nil) initialSetup() centralManager = CBCentralManager(delegate: self, queue: nil) //connectCalculator() } private func initialSetup() { // basic setup view.backgroundColor = .white navigationItem.title = "UIButton" // adding the constraints to login button view.addSubview(loginButton) loginButton.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true loginButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true loginButton.heightAnchor.constraint(equalToConstant: 50).isActive = true loginButton.widthAnchor.constraint(equalToConstant: 280).isActive = true } /*ectCalculator() { output = "Connecting..." centralQueue = DispatchQueue(label: "test.discovery") centralManager = CBCentralManager(delegate: self, queue: centralQueue) }*/ func disconnectCalculator() { guard let manager = centralManager, let peripheral = connectedPeripheral else { return } manager.cancelPeripheralConnection(peripheral) } @objc func appMovedToBackground() { print("App moved to background!") if self.connected{ disconnectCalculator() } } @objc func appBecomeActive() { loginButton.isEnabled = false print("App become active") if !self.connected{ centralManager = CBCentralManager(delegate: self, queue: nil) } } @objc private func handleLoginButtonTapped() { print("Open Door button tapped...") if self.connected{ print("OK Connected...") guard let peripheral = connectedPeripheral,let inputChar = inputChar else { output = "Connection error" return } self.operands[0] = 0x01 peripheral.writeValue(Data(operands), for: inputChar, type: .withoutResponse) self.showToastFaded(message: "Portblik aktiv") } else { print("NOT Connected...") centralManager = CBCentralManager(delegate: self, queue: nil) } } // This method monitors the Bluetooth radios state func centralManagerDidUpdateState(_ central: CBCentralManager) { print("Central Manager state changed: \(central.state)") if central.state == .poweredOn { central.scanForPeripherals(withServices: [serviceUUID], options: nil) } } // Called for each peripheral found that advertises the serviceUUID // This test program assumes only one peripheral will be powered up func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) { print("Discovered \(peripheral.name ?? "UNKNOWN")") central.stopScan() connectedPeripheral = peripheral central.connect(peripheral, options: nil) } // After BLE connection to peripheral, enumerate its services func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) { print("Connected to \(peripheral.name ?? "UNKNOWN")") peripheral.delegate = self peripheral.discoverServices(nil) } // After BLE connection, cleanup func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) { print("Disconnected from \(peripheral.name ?? "UNKNOWN")") centralManager = nil DispatchQueue.main.async { self.connected = false self.output = "Disconnected" } } func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) { print("Discovered services for \(peripheral.name ?? "UNKNOWN")") guard let services = peripheral.services else { return } for service in services { peripheral.discoverCharacteristics(nil, for: service) } } func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) { print("Discovered characteristics for \(peripheral.name ?? "UNKNOWN")") guard let characteristics = service.characteristics else { return } for ch in characteristics { switch ch.uuid { case inputCharUUID: inputChar = ch case outputCharUUID: outputChar = ch // subscribe to notification events for the output characteristic peripheral.setNotifyValue(true, for: ch) default: break } } DispatchQueue.main.async { self.connected = true self.output = "Connected." self.loginButton.isEnabled = true } } func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) { print("Notification state changed to \(characteristic.isNotifying)") } func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) { print("Characteristic updated: \(characteristic.uuid)") if characteristic.uuid == outputCharUUID, let data = characteristic.value { let bytes:[UInt8] = data.map {$0} if bytes.first != nil { DispatchQueue.main.async { //self.output = "\(self.operands[0]) \(self.operatorSymbol) \(self.operands[1]) = \(answer)" // Clear inputs self.operands[0] = 0x00 self.operands[1] = 0x00 } } } } func showToastFaded(message : String) { let toastLabel = UILabel(frame: CGRect(x: self.view.frame.size.width/2 - 125, y: self.view.frame.size.height-100, width: 250, height: 35)) toastLabel.numberOfLines = 0 toastLabel.backgroundColor = UIColor.black.withAlphaComponent(0.6) toastLabel.textColor = UIColor.white toastLabel.textAlignment = .center; toastLabel.text = message toastLabel.alpha = 1.0 toastLabel.layer.cornerRadius = 10; toastLabel.clipsToBounds = true toastLabel.sizeToFit() toastLabel.frame = CGRect( x: toastLabel.frame.minX, y: toastLabel.frame.minY,width: toastLabel.frame.width + 20, height: toastLabel.frame.height + 8) self.view.addSubview(toastLabel) UIView.animate(withDuration: 4.0, delay: 0.1, options: .curveEaseOut, animations: { toastLabel.alpha = 0.0 }, completion: {(isCompleted) in toastLabel.removeFromSuperview() }) } }