         discoverCharacteristics(uuid: uuid, service: service, completion: completion)
     }
     
+    /// Read Characteristic Value
+    ///
+    /// This sub-procedure is used to read a Characteristic Value from a server when the client knows
+    /// the Characteristic Value Handle.
+    public func readCharacteristic(_ characteristic: Characteristic, completion: @escaping (GATTClientResponse<Data>) -> ()) {
+                
+        readCharacteristicValue(characteristic.handle.value, completion: completion)
+    }
+    
     // MARK: - Private Methods
     
     @inline(__always)

            operation.error(errorResponse)
            
        case let .value(pdu):
            
            var operation = operation
            
            // pre-allocate array
            operation.foundData.reserveCapacity(operation.foundData.count + pdu.data.count)

                                       attributeType: attributeType.uuid)
        
        send(pdu) { [unowned self] in self.readByType($0, operation: operation) }
    }
    
    /// Read Characteristic Value
    ///
    /// This sub-procedure is used to read a Characteristic Value from a server when the client knows
    /// the Characteristic Value Handle.
    private func readCharacteristicValue(_ handle: UInt16, completion: @escaping (GATTClientResponse<Data>) -> ()) {
        
        // The Attribute Protocol Read Request is used with the
        // Attribute Handle parameter set to the Characteristic Value Handle.
        // The Read Response returns the Characteristic Value in the Attribute Value parameter.
        
        // read value and try to read blob if too big
        let pdu = ATTReadRequest(handle: handle)
        
        let operation = ReadOperation(handle: handle, completion: completion)
        
        send(pdu) { [unowned self] in self.readCharacteristicValueResponse($0, operation: operation) }
    }
    
    /// Read Long Characteristic Value
    ///
    /// This sub-procedure is used to read a Characteristic Value from a server when the client knows
    /// the Characteristic Value Handle and the length of the Characteristic Value is longer than can
    /// be sent in a single Read Response Attribute Protocol message.
    @inline(__always)
    private func readLongCharacteristicValue(_ operation: ReadOperation) {
        
        // The Attribute Protocol Read Blob Request is used to perform this sub-procedure.
        // The Attribute Handle shall be set to the Characteristic Value Handle of the Characteristic Value to be read.
        // The Value Offset parameter shall be the offset within the Characteristic Value to be read. To read the
        // complete Characteristic Value the offset should be set to 0x00 for the first Read Blob Request.
        // 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.readBlob($0, operation: operation) }
    }
    
    // MARK: - Callbacks
