diff --git a/src/examples/CMakeLists.txt b/src/examples/CMakeLists.txt
index c0d75282e89e6118752b8d55b42960472a857d35..52d59ce1347b2ad0cc2b591d3d2e58e3905f7b9d 100644
--- a/src/examples/CMakeLists.txt
+++ b/src/examples/CMakeLists.txt
@@ -16,6 +16,11 @@ add_executable(fluke_example)
 target_sources(fluke_example PRIVATE fluke_example.cpp)
 target_link_libraries(fluke_example PRIVATE Utils Meter Com)
 
+# multimeter
+add_executable(multimeter_example)
+target_sources(multimeter_example PRIVATE multimeter_example.cpp)
+target_link_libraries(multimeter_example PRIVATE Utils Meter Com)
+
 # scope
 if ( ${libScope_FOUND} )
     add_executable(scope_example)
diff --git a/src/examples/multimeter_example.cpp b/src/examples/multimeter_example.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c8d31a1be88c828cf0fa0d117879c920b7296aab
--- /dev/null
+++ b/src/examples/multimeter_example.cpp
@@ -0,0 +1,40 @@
+#include <iostream>
+#include <iomanip>
+
+#include "Logger.h"
+#include "DMM6500.h"
+#include "CharDeviceCom.h"
+
+int main(int argc, char*argv[]) {
+
+  DMM6500 meter(argv[1]);
+
+  std::shared_ptr<CharDeviceCom> com =  std::make_shared<CharDeviceCom>(argv[1]);
+  meter.setCom(com);
+
+  meter.reset();
+
+  /* ping multimeter */
+  meter.ping(0); 
+
+  /* ping scanner card */
+  meter.ping(1); 
+
+
+  std::cout<<"=======================================scan result========================================="<<std::endl;
+  std::cout<<std::setw(13)<<" Ch1 [Ohm],"<<std::setw(13)<<" Ch2 [Ohm],"<<std::setw(13)<<" Ch3 [Ohm],"
+           <<std::setw(13)<<" Ch4 [pF],"<<std::setw(13)<<" Ch5 [pF],"<<std::setw(13)<<" Ch9 [pF],"<<std::setw(13)<<" Ch10 [pF],"<<std::endl;
+  std::cout<<std::setw(12)<<meter.measureRES(1, true)<<",";
+  std::cout<<std::setw(12)<<meter.measureRES(2, true)<<",";
+  std::cout<<std::setw(12)<<meter.measureRES(3, true)<<",";
+  std::cout<<std::setw(12)<<meter.measureCAP(4)*1e12<<",";
+  std::cout<<std::setw(12)<<meter.measureCAP(5)*1e12<<",";
+  std::cout<<std::setw(12)<<meter.measureCAP(9)*1e12<<",";
+  std::cout<<std::setw(12)<<meter.measureCAP(10)*1e12<<",";
+  std::cout<<std::endl;
+  std::cout<<"==========================================================================================="<<std::endl;
+
+
+  return 0;
+}
+
diff --git a/src/libMeter/CMakeLists.txt b/src/libMeter/CMakeLists.txt
index 56f8ae1a18d587ac44e3d5e9678891efe80e6031..2c471e9b0eedcf9d8085a6c24ab14ddc1ed677ed 100644
--- a/src/libMeter/CMakeLists.txt
+++ b/src/libMeter/CMakeLists.txt
@@ -1,10 +1,12 @@
 add_library(Meter SHARED)
 target_sources(Meter
   PRIVATE
+  IMeter.cpp
   Keithley2000.cpp
   Fluke8842.cpp
   HP3478A.cpp
   PM6680.cpp
+  DMM6500.cpp
   )
 target_link_libraries(Meter PRIVATE Com Utils)
 target_include_directories(Meter PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/src/libMeter/DMM6500.cpp b/src/libMeter/DMM6500.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a403719bdbe8e850eeb41c15ed135f9ee2835845
--- /dev/null
+++ b/src/libMeter/DMM6500.cpp
@@ -0,0 +1,172 @@
+#include "DMM6500.h"
+#include "Logger.h"
+
+#include "IMeter.h"
+#include "ScopeLock.h"
+
+DMM6500::DMM6500(const std::string& name) : IMeter(name, {"DMM6500"}){ }
+
+bool DMM6500::ping(unsigned dev)
+{
+    std::string result = "";
+    if(dev == 0){
+        logger(logDEBUG) <<"ping the multimeter.....";
+        result = m_com->sendreceive("*IDN?");
+    }
+    if(dev == 1){
+        logger(logDEBUG)<<"ping scanner card.....";
+        result = m_com->sendreceive(":SYSTem:CARD1:IDN?");
+    }
+    result.erase(result.size()-1);
+    if(result.empty())
+        throw std::runtime_error("Failed communication with the device");
+    else
+        logger(logDEBUG)<<result;
+
+    return !result.empty();
+}
+
+std::string DMM6500::identify()
+{
+  std::string idn=m_com->sendreceive("*IDN?");
+  return idn;
+}
+
+void DMM6500::autowait() {
+    m_com->send("*OPC");
+    int ESRValue = 0;
+    while ( (ESRValue & 1) == 0)
+    {
+        ESRValue = std::stoi(m_com->sendreceive("*ESR?"));
+        std::this_thread::sleep_for(std::chrono::milliseconds(m_wait));
+    }
+}
+
+void DMM6500::send(const std::string& cmd) {
+    logger(logDEBUG) << __PRETTY_FUNCTION__ << " -> Sending: " << cmd;
+    m_com->send("*CLS");
+    m_com->send(cmd);
+    this->autowait();
+}
+
+std::string DMM6500::sendreceive(const std::string& cmd) {
+    m_com->send("*CLS");
+    std::string buf=m_com->sendreceive(cmd);
+    this->autowait();
+    logger(logDEBUG) << __PRETTY_FUNCTION__ << " -> Received: " << buf;
+    buf.erase(buf.size()-1);
+    return buf;
+}
+
+void DMM6500::reset() {
+    logger(logDEBUG) << __PRETTY_FUNCTION__ << " -> Initialising: ";
+    this->send("*RST");
+}
+
+void DMM6500::sendScanCommand(std::string channel){
+    this->send(":ROUT:SCAN:CRE (@"+channel+")");
+    this->send(":ROUT:SCAN:COUNT:SCAN 1");
+    this->send(":TRAC:CLE");
+    this->send("INIT");
+    this->send("*WAI");
+}
+
+//measure DC voltage with high precision
+//take average of 10 repeatings
+double DMM6500::measureDCV(unsigned channel){
+    std::string s_ch = "";
+    if(channel > 0)//use scanner card
+        s_ch = ", (@"+std::to_string(channel)+")";
+    
+    ScopeLock lock(m_com);
+    this->send("*RST");
+    this->send(":SENS:FUNC \"VOLT:DC\""+s_ch);
+    this->send(":SENS:VOLT:RANG:AUTO ON"+s_ch);
+    this->send(":SENS:VOLT:INP AUTO"+s_ch);
+    this->send(":SENS:VOLT:NPLC 1"+s_ch);
+    this->send(":SENS:VOLT:AZER ON"+s_ch);
+    this->send(":SENS:VOLT:AVER:TCON REP"+s_ch);
+    this->send(":SENS:VOLT:AVER:COUN 10"+s_ch);
+    this->send(":SENS:VOLT:AVER ON"+s_ch);
+
+    if(channel > 0)//use scanner card
+        this->sendScanCommand(std::to_string(channel));
+
+    return std::stod(this->sendreceive(":READ?"));
+}
+
+//measure resistance (2W or 4W)
+//take average of 10 repeatings
+double DMM6500::measureRES(unsigned channel, bool use4w){
+    std::string n_func = "RES";
+    if(use4w)
+        n_func = "FRES";
+
+    std::string s_ch = "";
+    if(channel > 0)//use scanner card
+        s_ch = ", (@"+std::to_string(channel)+")";
+
+    ScopeLock lock(m_com);
+    this->send("*RST");
+    this->send(":SENS:FUNC \""+n_func+"\""+s_ch);
+    this->send(":SENS:"+n_func+":RANG:AUTO ON"+s_ch);
+    if(use4w)
+        this->send(":SENS:"+n_func+":OCOM ON"+s_ch);
+    this->send(":SENS:"+n_func+":AZER ON"+s_ch);
+    this->send(":SENS:"+n_func+":NPLC 1"+s_ch);
+    this->send(":SENS:"+n_func+":AVER:TCON REP"+s_ch);
+    this->send(":SENS:"+n_func+":AVER:COUN 10"+s_ch);
+    this->send(":SENS:"+n_func+":AVER ON"+s_ch);
+
+    if(channel > 0)//use scanner card
+        this->sendScanCommand(std::to_string(channel));
+
+    return std::stod(this->sendreceive(":READ?"));
+}
+
+//measure capacitance
+//take average of 10 repeatings
+double DMM6500::measureCAP(unsigned channel){
+    std::string s_ch = "";
+    if(channel > 0)//use scanner card
+        s_ch = ", (@"+std::to_string(channel)+")";
+
+    ScopeLock lock(m_com);
+    this->send("*RST");
+    this->send(":SENS:FUNC \"CAP\""+s_ch);
+    this->send(":SENS:CAP:RANG:AUTO ON"+s_ch);
+    this->send(":SENS:CAP:AVER:TCON REP"+s_ch);
+    this->send(":SENS:CAP:AVER:COUN 10"+s_ch);
+    this->send(":SENS:CAP:AVER ON"+s_ch);
+
+    if(channel > 0)//use scanner card
+        this->sendScanCommand(std::to_string(channel));
+
+    return std::stod(this->sendreceive(":READ?"));
+}
+
+
+//measure DC current with high precision
+//take average of 10 repeatings
+double DMM6500::measureDCI(unsigned channel){
+    std::string s_ch = "";
+    if(channel > 0)//use scanner card
+        s_ch = ", (@"+std::to_string(channel)+")";
+
+    ScopeLock lock(m_com);
+    this->send("*RST");
+    this->send(":SENS:FUNC \"CURR:DC\""+s_ch);
+    this->send(":SENS:CURR:RANG:AUTO ON"+s_ch);
+    this->send(":SENS:CURR:NPLC 1"+s_ch);
+    this->send(":SENS:CURR:AZER ON"+s_ch);
+    this->send(":SENS:CURR:AVER:TCON REP"+s_ch);
+    this->send(":SENS:CURR:AVER:COUN 10"+s_ch);
+    this->send(":SENS:CURR:AVER ON"+s_ch);
+
+    if(channel > 0)//use scanner card
+        this->sendScanCommand(std::to_string(channel));
+
+    return std::stod(this->sendreceive(":READ?"));
+}
+
+
diff --git a/src/libMeter/DMM6500.h b/src/libMeter/DMM6500.h
new file mode 100644
index 0000000000000000000000000000000000000000..5995193dc2a1e5c9e434938d746960c6cc85c41b
--- /dev/null
+++ b/src/libMeter/DMM6500.h
@@ -0,0 +1,72 @@
+#ifndef DMM6500_H
+#define DMM6500_H
+#include <iostream>
+#include <string>
+#include <thread>
+#include <chrono>
+#include "IMeter.h"
+
+/*
+ DMM6500 multimeter
+ Author: Zhicai Zhang
+ Date: Feb 2021
+ Reference 1: https://www.tek.com/tektronix-and-keithley-digital-multimeter/dmm6500-manual/model-dmm6500-6-1-2-digit-multimeter-3
+ Reference 2: https://www.tek.com/manual/model-dmm6500-6-1-2-digit-multimeter-user-manual
+ 2000-Scan card note: https://www.tek.com/default-accessory-series-manual/model-2000-scan-scanner-card
+*/
+class DMM6500 : public IMeter {
+    public:
+        DMM6500(const std::string& name);
+
+        /** ping the device
+         * @param dev: index of the device to ping (if there are multiple parts connected)
+         * dev = 0 is for the main meter
+         * dev > 0 is for other parts that's connected to the meter
+        */
+        virtual bool ping(unsigned dev = 0);
+
+        virtual std::string identify();
+
+        virtual void reset();
+
+        /** measure DC voltage (unit: V)
+         * @param channel: channel ID to perform the measurement
+         * channel = 0 means without scanner card
+         * channel > 0 means measure with specific channel on scanner card
+        */
+        virtual double measureDCV(unsigned channel = 0);
+
+        /** measure DC current (unit: A)
+         * @param channel: channel ID to perform the measurement
+         * channel = 0 means without scanner card
+         * channel > 0 means measure with specific channel on scanner card
+        */
+        virtual double measureDCI(unsigned channel = 0);
+        
+        /*
+         measure resistance (unit: Ohm)
+         * @param channel: channel ID to perform the measurement
+         * channel = 0 means without scanner card
+         * channel > 0 means measure with specific channel on scanner card
+         * @param use4w: whether or not use 4-wire method
+        */
+        virtual double measureRES(unsigned channel = 0, bool use4w = false);
+
+        /*
+         measure capacitance (unit: F)
+         * @param channel: channel ID to perform the measurement
+         * channel = 0 means without scanner card
+         * channel > 0 means measure with specific channel on scanner card
+        */
+        virtual double measureCAP(unsigned channel = 0);
+
+    private:
+        void send(const std::string& cmd);
+        void sendScanCommand(std::string channel="1");
+        std::string sendreceive(const std::string& cmd);
+        void autowait();
+        
+        std::chrono::milliseconds m_wait{10};
+};
+
+#endif
diff --git a/src/libMeter/IMeter.cpp b/src/libMeter/IMeter.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3d0c84ff80b4e855fc36f90dda4ab791cb2cffa9
--- /dev/null
+++ b/src/libMeter/IMeter.cpp
@@ -0,0 +1,75 @@
+#include "IMeter.h"
+#include "Logger.h"
+
+IMeter::IMeter(const std::string& name, std::vector<std::string> models) {
+  m_name = name;
+  m_models = models;
+}
+
+void IMeter::setCom(std::shared_ptr<ICom> com)
+{
+  if(!com->is_open())
+    com->init();
+
+  m_com = com;
+  if(!ping(0))
+    throw std::runtime_error("Failed communication with the multimeter");
+}
+
+bool IMeter::ping(unsigned dev)
+{
+    logger(logWARNING) << "ping() not implemented for this multimeter.";
+    return false;
+}
+
+void IMeter::checkCompatibilityList()
+{
+  // get model connected to the meter
+  std::string idn = identify();
+
+  // get list of models
+  std::vector<std::string> models = IMeter::getListOfModels();
+
+  if (models.empty()){
+    logger(logINFO) << "No model identifier implemented for this meter. No check is performed.";
+    return;
+  }
+
+  for(const std::string& model : models)
+    {
+      if(idn.find(model) != std::string::npos)
+    return;
+    }
+
+  logger(logERROR)<< "Unknown meter: " << idn;
+  throw std::runtime_error("Unknown meter: " + idn);
+
+}
+
+std::vector<std::string> IMeter::getListOfModels()
+{
+  return m_models;
+}
+
+double IMeter::measureDCV(unsigned channel){
+    logger(logWARNING) << "measureDCV() not implemented for this multimeter.";
+    return 0;
+}
+
+double IMeter::measureRES(unsigned channel, bool use4w){
+    logger(logWARNING) << "measureRES() not implemented for this multimeter.";
+    return 0;
+}
+
+double IMeter::measureCAP(unsigned channel){
+    logger(logWARNING) << "measureCAP() not implemented for this multimeter.";
+    return 0;
+}
+
+
+double IMeter::measureDCI(unsigned channel){
+    logger(logWARNING) << "measureDCI() not implemented for this multimeter.";
+    return 0;
+}
+
+
diff --git a/src/libMeter/IMeter.h b/src/libMeter/IMeter.h
new file mode 100644
index 0000000000000000000000000000000000000000..a26d98c89b241d7a4b82746eecf8646547c900bc
--- /dev/null
+++ b/src/libMeter/IMeter.h
@@ -0,0 +1,85 @@
+#ifndef IMETER_H
+#define IMETER_H
+
+#include <iostream>
+#include <string>
+#include "ICom.h"
+
+
+/* Generic multimeter interface
+    a typical workflow:
+    IMeter meter(name);
+    meter.setCom(com);
+    meter.reset();
+    meter.ping(0);
+    meter.measureDCV();
+    //....
+*/
+class IMeter {
+    public:
+       
+        /** Constructor.
+         * @param name: name of the meter
+         * @param models: list of tested models (empty means no check is performed)
+        */
+        IMeter(const std::string& name, std::vector<std::string> models={});
+        
+        void setCom(std::shared_ptr<ICom> com);
+
+        /** ping the device
+         * @param dev: index of the device to ping (if there are multiple parts connected)
+         * dev = 0 is for the main meter
+         * dev > 0 is for other parts that's connected to the meter  
+        */ 
+        virtual bool ping(unsigned dev = 0);
+
+        virtual void reset() = 0;
+
+        /** Check that the device model is supported by checking that
+         * model identifier of the connected meter contains
+         * one of the supported models for that meter
+         * Throws exception if compatibility is not found
+         * If the list of model identifiers is empty, no check is performed
+        */
+        virtual void checkCompatibilityList();
+
+        /* Return list of supported models. */
+        virtual std::vector<std::string> getListOfModels();
+
+        /** Returns the model of the meter connected.
+         * Meter classes should return an empty string if this functionality does not exist
+        */
+        virtual std::string identify() = 0;
+
+        /** measure DC voltage (unit: V)
+         * @param channel: channel ID to perform the measurement
+        */
+        virtual double measureDCV(unsigned channel = 0);
+
+        /** measure DC current (unit: A)
+         * @param channel: channel ID to perform the measurement
+        */
+        virtual double measureDCI(unsigned channel = 0);
+
+        /** measure resistance (unit: Ohm)
+         * @param channel: channel ID to perform the measurement
+         * @param use4w: whether or not use 4-wire method
+        */
+        virtual double measureRES(unsigned channel = 0, bool use4w = false);
+
+        /** measure capacitance (unit: F)
+         * @param channel: channel ID to perform the measurement
+        */
+        virtual double measureCAP(unsigned channel = 0);
+
+    protected:
+
+        std::shared_ptr<ICom> m_com = nullptr;
+
+        /** Store device configuration name */
+        std::string m_name;
+
+        std::vector<std::string> m_models;
+};
+
+#endif