         // The offset for subsequent Read Blob Requests is the next octet that has yet to be read.
         // The Read Blob Request is repeated until the Read Blob Response’s Part Attribute Value parameter is shorter than (ATT_MTU-1).
         
-        let pdu = ATTReadBlobRequest(handle: operation.handle,
-                                     offset: operation.offset)
-        
-        send(pdu) { [unowned self] in self.readBlobResponse($0, operation: operation) }
-    }
-    
-    private func writeAttribute(_ handle: UInt16,
-                                data: Data,
-                                reliableWrites: Bool,
-                                completion: ((GATTClientResponse<()>) -> ())?) {
+        let request = ATTReadBlobRequest(
+            handle: operation.handle,
+            offset: operation.offset
+        )
+        
+        let response = try await send(request, response: ATTReadBlobResponse.self)
+        try await readBlobResponse(response, operation: operation)
+    }
+    
+    private func writeAttribute(
+        _ handle: UInt16,
+        data: Data,
+        reliableWrites: Bool,
+        completion: ((GATTClientResponse<()>) -> ())?
+    ) async throws {
         
         // short value
-        if data.count <= Int(maximumTransmissionUnit.rawValue) - 3 { // ATT_MTU - 3
-            
+        let shortValueLength = await Int(maximumTransmissionUnit.rawValue) - 3 // ATT_MTU - 3
+        if data.count <= shortValueLength {
             if let completion = completion {
-                
-                writeAttributeValue(handle,
-                                    data: data,
-                                    completion: completion)
-                
+                try await writeAttributeValue(
+                    handle,
+                    data: data,
+                    completion: completion
+                )
             } else {
-                
-                writeAttributeCommand(handle,
-                                      data: data)
-            }
-            
+                try await writeAttributeCommand(handle, data: data)
+            }
         } else {
-            
             let completion = completion ?? { _ in }
-            
-            writeLongAttributeValue(handle,
-                                    data: data,
-                                    reliableWrites: reliableWrites,
-                                    completion: completion)
-        }
-    }
-    
-    private func writeAttributeCommand(_ attribute: UInt16, data: Data) {
-        
-        let data = Data(data.prefix(Int(maximumTransmissionUnit.rawValue) - 3))
-        
-        let pdu = ATTWriteCommand(handle: attribute, value: data)
-        
-        send(pdu)
+            try await writeLongAttributeValue(
+                handle,
+                data: data,
+                reliableWrites: reliableWrites,
+                completion: completion
+            )
+        }
+    }
+    
+    private func writeAttributeCommand(_ attribute: UInt16, data: Data) async throws {
+        let length = await Int(maximumTransmissionUnit.rawValue) - 3
+        let data = Data(data.prefix(length))
+        let command = ATTWriteCommand(handle: attribute, value: data)
+        try await send(command)
     }
     
     /// Write attribute request.
-    private func writeAttributeValue(_ attribute: UInt16,
-                                     data: Data,
-                                     completion: @escaping (GATTClientResponse<()>) -> ()) {
-        
-        let data = Data(data.prefix(Int(maximumTransmissionUnit.rawValue) - 3))
-        
-        let pdu = ATTWriteRequest(handle: attribute, value: data)
-
-        send(pdu) { [unowned self] in self.writeResponse($0, completion: completion) }
-    }
-    
-    private func writeLongAttributeValue(_ attribute: UInt16,
-                                         data: Data,
-                                         reliableWrites: Bool = false,
-                                         completion: @escaping (GATTClientResponse<()>) -> ()) {
+    private func writeAttributeValue(
+        _ attribute: UInt16,
+        data: Data,
+        completion: @escaping (GATTClientResponse<()>) -> ()
+    ) async throws {
+        let length = await Int(maximumTransmissionUnit.rawValue) - 3
+        let data = Data(data.prefix(length))
+        let request = ATTWriteRequest(handle: attribute, value: data)
+        let response = try await send(request, response: ATTWriteResponse.self)
+        writeResponse(response, completion: completion)
+    }
+    
+    private func writeLongAttributeValue(
+        _ attribute: UInt16,
+        data: Data,
+        reliableWrites: Bool = false,
+        completion: @escaping (GATTClientResponse<()>) -> ()
+    ) async throws {
         
         // The Attribute Protocol Prepare Write Request and Execute Write Request are used to perform this sub-procedure.
         // The Attribute Handle parameter shall be set to the Characteristic Value Handle of the Characteristic Value to be written.

        }
    }
    
    private func findInformationResponse(_ response: ATTResponse<ATTFindInformationResponse>,
                                         operation: DescriptorDiscoveryOperation) {
        
        /**
         Two possible responses can be sent from the server for the Find Information Request: Find Information Response and Error Response.

    ///
    /// ![Image](https://github.com/PureSwift/Bluetooth/raw/master/Assets/ReadCharacteristicValue.png)
    public func readCharacteristic(_ characteristic: Characteristic) async throws -> Data {
        // read value and try to read blob if too big
        return try await readAttributeValue(characteristic.handle.value)
    }
    
    /// Read Using Characteristic UUID
