diff --git a/config.py b/config.py
index 0767b2b4e0657a9cd9098d1878c9dfa8fdd58315..f0267d83f50872e7df020ddddc91888e807fa410 100644
--- a/config.py
+++ b/config.py
@@ -2,11 +2,12 @@ import logging
 
 from paho.mqtt.client import MQTTv31
 
-mqtt_broker = 'mg3d-dev.umons.ac.be' # 'localhost'
+ohmpi_id = '0001'
+mqtt_broker = 'localhost'
 logging_suffix = ''
 # OhmPi configuration
 OHMPI_CONFIG = {
-    'id': '0001',  # Unique identifier of the OhmPi board (string)
+    'id': ohmpi_id,  # Unique identifier of the OhmPi board (string)
     'R_shunt': 2,  # Shunt resistance in Ohms
     'Imax': 4800/50/2,  # Maximum current
     'coef_p2': 2.50,  # slope for current conversion for ADS.P2, measurement in V/V
diff --git a/doc/source/V2_00.rst b/doc/source/V2_00.rst
index 06f7a30d0d0d3cc2e9d5a1687e44f6b43650b9ad..2276454a1b5913408c0696594023b7afc34f425d 100644
--- a/doc/source/V2_00.rst
+++ b/doc/source/V2_00.rst
@@ -114,58 +114,58 @@ files (.json and .py).
 
 
 .. code-block:: python
-   :caption: Example of using the Python API to control OhmPi
-
-
-   from ohmpi import OhmPi
-   k = OhmPi(idps=True)  # if V3.0 make sure to set idps to True
-   # the DPS5005 is used in V3.0 to inject higher voltage
-   
-   # default parameters are stored in the pardict argument
-   # they can be manually changed
-   k.settings['injection_duration'] = 0.5  # injection time in seconds
-   k.settings['nb_stack'] = 1  # one stack is two half-cycles
-   k.settings['nbr_meas'] = 1  # number of time the sequence is repeated
-   
-   # without multiplexer, one can simple measure using
-   out = k.run_measurement()
-   # out contains information about the measurement and can be save as
-   k.append_and_save('out.csv', out)
-   
-   # custom or adaptative argument (see help of run_measurement())
-   k.run_measurement(nb_stack=4,  # do 4 stacks (8 half-cycles)
-                     injection_duration=2,  # inject for 2 seconds
-                     autogain=True,  # adapt gain of ADS to get good resolution
-                     strategy='vmin',  # inject min voltage for Vab (v3.0)
-                     tx_volt=5)  # vab for finding vmin or vab injectected
-                     # if 'strategy' is 'constant'
+  :caption: Example of using the Python API to control OhmPi
+
+  import os
+  import numpy as np
+  import time
+  os.chdir("/home/pi/OhmPi")
+  from ohmpi import OhmPi
+
+  ### Define object from class OhmPi
+  k = OhmPi()  # this load default parameters from the disk
+  
+  ### Default parameters can also be edited manually
+  k.settings['injection_duration'] = 0.5  # injection time in seconds
+  k.settings['nb_stack'] = 1  # one stack is two half-cycles
+  k.settings['nbr_meas'] = 1  # number of time the sequence is repeated
+
+  ### Update settings if needed 
+  k.update_settings({"injection_duration":0.2})
+
+  ### Set or load sequence
+  k.sequence = np.array([[1,2,3,4]])    # set numpy array of shape (n,4)
+  # k.set_sequence('1 2 3 4\n2 3 4 5')    # call function set_sequence and pass a string
+  # k.load_sequence('ABMN.txt')    # load sequence from a local file
+
+  ### Run contact resistance check
+  k.rs_check()
+
+  ### Run sequence (synchronously - it will wait that all 
+  # sequence is measured before returning the prompt
+  k.run_sequence()
+  # k.run_sequence_async()  # sequence is run in a separate thread and the prompt returns immediately
+  # time.sleep(2)
+  # k.interrupt()  # kill the asynchrone sequence
+
+  ### Run multiple sequences at given time interval
+  k.settings['nb_meas'] = 3  # run sequence three times
+  k.settings['sequence_delay'] = 100 # every 100 s 
+  k.run_multiple_sequences()  # asynchrone
+  # k.interrupt()  # kill the asynchrone sequence
+  
+  ### Single measurement can also be taken with
+  k.switch_mux_on([1, 4, 2, 3])
+  k.run_measurement()  # use default acquisition parameters
+  k.switch_mux_off([1, 4, 2, 3])  # don't forget this! risk of short-circuit
+  
+  ### Custom or adaptative argument, see help(k.run_measurement)
+  k.run_measurement(nb_stack=4,  # do 4 stacks (8 half-cycles)
+                    injection_duration=2,  # inject for 2 seconds
+                    autogain=True)  # adapt gain of ADS to get good resolution   
    
-   # if a multiplexer is connected, we can also manually switch it
-   k.reset_mux()  # check that all mux are closed (do this FIRST)
-   k.switch_mux_on([1, 4, 2, 3])
-   k.run_measurement()
-   k.switch_mux_off([1, 4, 2, 3])  # don't forget this! risk of short-circuit
-   
-   # import a sequence
-   k.read_quad('sequence.txt')  # four columns, no header, space as separator
-   print(k.sequence)  # where the sequence is stored
-   
-   # rs check
-   k.rs_check()  # run an RS check (check contact resistances) for all
-   # electrodes of the given sequence
-   
-   # run a sequence
-   k.measure()  # measure accept same arguments as run_measurement()
-   # NOTE: this is an asynchronous command that runs in a separate thread
-   # after executing the command, the prompt will return immediately
-   # the asynchronous thread can be stopped during execution using
-   k.stop()
-   # otherwise, it will exit by itself at the end of the sequence
-   # if multiple measurement are to be taken, the sequence will be repeated
    
 
-
-   
 ***MQTT interface***
 
 Interface to communicate with the Pi designed for the Internet of Things (IoT).
diff --git a/example_simple_measurement.py b/example_simple_measurement.py
index 2a5d50939cb20ac9506de31d4cd8bb451a119c2a..ee795b1808cf4fb2bf64484b566d58dc2275b984 100644
--- a/example_simple_measurement.py
+++ b/example_simple_measurement.py
@@ -16,8 +16,10 @@ k.sequence = np.array([[1,2,3,4]])    # set numpy array of shape (n,4)
 # k.load_sequence('ABMN.txt')    # load sequence from a local file
 
 ### Run contact resistance check
-k.rs_check()
+# k.rs_check()
 
 ### Run sequence
-k.run_sequence()   
-# k.interrupt()
\ No newline at end of file
+k.run_sequence()
+# k.run_sequence_async()
+# time.sleep(2)
+# k.interrupt()
\ No newline at end of file
diff --git a/http_interface.py b/http_interface.py
index bc0e71f3db458c1a3ba2cfab94ff949d2e3edab3..9c43bae70f13ba22942f9f5a0273d1a1c0f41d0b 100644
--- a/http_interface.py
+++ b/http_interface.py
@@ -23,8 +23,56 @@ publisher_config.pop('ctrl_topic')
 
 print(colored(f"Sending commands control topic {MQTT_CONTROL_CONFIG['ctrl_topic']} on {MQTT_CONTROL_CONFIG['hostname']} broker."))
 cmd_id = None
+received = False
 rdic = {}
 
+
+# set controller globally as __init__ seem to be called for each request and so we subscribe again each time (=overhead)
+controller = mqtt_client.Client(f"ohmpi_{OHMPI_CONFIG['id']}_listener", clean_session=False)  # create new instance
+print(colored(f"Connecting to control topic {MQTT_CONTROL_CONFIG['ctrl_topic']} on {MQTT_CONTROL_CONFIG['hostname']} broker", 'blue'))
+trials = 0
+trials_max = 10
+broker_connected = False
+while trials < trials_max:
+    try:
+        controller.username_pw_set(MQTT_CONTROL_CONFIG['auth'].get('username'),
+                                        MQTT_CONTROL_CONFIG['auth']['password'])
+        controller.connect(MQTT_CONTROL_CONFIG['hostname'])
+        trials = trials_max
+        broker_connected = True
+    except Exception as e:
+        print(f'Unable to connect control broker: {e}')
+        print('trying again to connect to control broker...')
+        time.sleep(2)
+        trials += 1
+if broker_connected:
+    print(f"Subscribing to control topic {MQTT_CONTROL_CONFIG['ctrl_topic']}")
+    controller.subscribe(MQTT_CONTROL_CONFIG['ctrl_topic'], MQTT_CONTROL_CONFIG['qos'])
+else:
+    print(f"Unable to connect to control broker on {MQTT_CONTROL_CONFIG['hostname']}")
+    controller = None
+
+
+# start a listener for acknowledgement
+def _control():
+    def on_message(client, userdata, message):
+        global cmd_id, rdic, received
+
+        command = json.loads(message.payload.decode('utf-8'))
+        #print('++++', cmd_id, received, command)
+        if ('reply' in command.keys()) and (command['cmd_id'] == cmd_id):
+            print(f'Acknowledgement reception of command {command} by OhmPi')
+           # print('oooooooooook', command['reply'])
+            received = True
+            #rdic = command
+
+    controller.on_message = on_message
+    controller.loop_forever()
+    
+t = threading.Thread(target=_control)
+t.start()
+
+
 class MyServer(SimpleHTTPRequestHandler):
     # because we use SimpleHTTPRequestHandler, we do not need to implement
     # the do_GET() method (if we use the BaseHTTPRequestHandler, we would need to)
@@ -42,60 +90,44 @@ class MyServer(SimpleHTTPRequestHandler):
 
     def __init__(self, request, client_address, server):
         super().__init__(request, client_address, server)
-        # set controller
-        self.controller = mqtt_client.Client(f"ohmpi_{OHMPI_CONFIG['id']}_listener", clean_session=False)  # create new instance
-        print(colored(f"Connecting to control topic {MQTT_CONTROL_CONFIG['ctrl_topic']} on {MQTT_CONTROL_CONFIG['hostname']} broker", 'blue'))
-        trials = 0
-        trials_max = 10
-        broker_connected = False
-        while trials < trials_max:
-            try:
-                self.controller.username_pw_set(MQTT_CONTROL_CONFIG['auth'].get('username'),
-                                                MQTT_CONTROL_CONFIG['auth']['password'])
-                self.controller.connect(MQTT_CONTROL_CONFIG['hostname'])
-                trials = trials_max
-                broker_connected = True
-            except Exception as e:
-                print(f'Unable to connect control broker: {e}')
-                print('trying again to connect to control broker...')
-                time.sleep(2)
-                trials += 1
-        if broker_connected:
-            print(f"Subscribing to control topic {MQTT_CONTROL_CONFIG['ctrl_topic']}")
-            self.controller.subscribe(MQTT_CONTROL_CONFIG['ctrl_topic'], MQTT_CONTROL_CONFIG['qos'])
-        else:
-            print(f"Unable to connect to control broker on {MQTT_CONTROL_CONFIG['hostname']}")
-            self.controller = None
-        self.cmd_thread = threading.Thread(target=self._control)
-
-    def _control(self):
-        def on_message(client, userdata, message):
-            global cmd_id, rdic
-
-            command = message.payload.decode('utf-8')
-            print(f'Received command {command}')
-            # self.process_commands(command)
-            if 'reply' in command.keys and command['cmd_id'] == cmd_id :
-                rdic = command
-
-        self.controller.on_message = on_message
-        self.controller.loop_start()
-        while True:
-            time.sleep(.1)
+        # global controller, once  # using global variable otherwise, we subscribe to client for EACH request
+        # if once:
+        #     self.controller = controller
+        #     self.cmd_thread = threading.Thread(target=self._control)
+        #     self.cmd_thread.start()
+        #     once = False
+
+
+    # we would like to listen to the ackn topic to check our message has been wel received
+    # by the OhmPi, however, this won't work as it seems an instance of MyServer is created
+    # each time (actually it's not a server but a requestHandler)
+    # def _control(self):
+    #     def on_message(client, userdata, message):
+    #         global cmd_id, rdic
+
+    #         command = json.loads(message.payload.decode('utf-8'))
+    #         print(f'Acknowledgement reception of command {command} by OhmPi')
+    #         if 'reply' in command.keys() and command['cmd_id'] == cmd_id :
+    #             print('oooooooooook', command['reply'])
+    #             #rdic = command
+
+    #     self.controller.on_message = on_message
+    #     print('starting loop')
+    #     self.controller.loop_forever()
+    #     print('forever')
 
     def do_POST(self):
-        global cmd_id, rdic
+        global cmd_id, rdic, received
+        received = False
         cmd_id = uuid.uuid4().hex
-        # global socket
-
-        # global ohmpiThread, status, run
         dic = json.loads(self.rfile.read(int(self.headers['Content-Length'])))
+        #print('++', dic, cmd_id)
         rdic = {} # response dictionary
-        if dic['cmd'] == 'run_sequence':
-            payload = json.dumps({'cmd_id': cmd_id, 'cmd': 'run_sequence'})
+        if dic['cmd'] == 'run_multiple_sequences':
+            payload = json.dumps({'cmd_id': cmd_id, 'cmd': 'run_multiple_sequences'})
             publish.single(payload=payload, **publisher_config)
+
         elif dic['cmd'] == 'interrupt':
-            # ohmpi.stop()
             payload = json.dumps({'cmd_id': cmd_id, 'cmd': 'interrupt'})
             publish.single(payload=payload, **publisher_config)
         elif dic['cmd'] == 'getData':
@@ -103,9 +135,9 @@ class MyServer(SimpleHTTPRequestHandler):
             fnames = [fname for fname in os.listdir('data/') if fname[-4:] == '.csv']
             ddic = {}
             for fname in fnames:
-                if (fname.replace('.csv', '') not in dic['surveyNames']
-                        and fname != 'readme.txt'
-                        and '_rs' not in fname):
+                if ((fname != 'readme.txt')
+                    and ('_rs' not in fname)
+                    and (fname.replace('.csv', '') not in dic['surveyNames'])):
                     df = pd.read_csv('data/' + fname)
                     ddic[fname.replace('.csv', '')] = {
                         'a': df['A'].tolist(),
diff --git a/index.html b/index.html
index db104d009f47532bc3ed3fcbdd304433b72c4496..70a2a2da71a4ede06ddc949424bdaa54a2a1a55d 100644
--- a/index.html
+++ b/index.html
@@ -26,7 +26,7 @@
         <div class="form-check">
             <input id="dataRetrievalCheck" class="form-check-input" type="checkbox" value="">
             <label class="form-check-label" for="dataRetrievalCheck">
-                Automaticaly get data every 1 secondStart
+                Automaticaly get data every 1 second
             </label>
         </div>
         <div id='output'>Status: idle</div>
@@ -34,8 +34,8 @@
         <!-- Pseudo section -->
         <select id='surveySelect' class='custom-select'>
         </select>
-        <input id="cmin" type="number" value="0"/>
-        <input id="cmax" type="number" value="150"/>
+        <input id="cmin" type="number" value=""/>
+        <input id="cmax" type="number" value=""/>
         <button id="capplyBtn" type="button" class="btn btn-info">Apply</button>
         <div id="gd"></div>
         <div class="mb3 row">
@@ -163,9 +163,9 @@
 
         // run button
         function runBtnFunc() {
-            sendCommand('{"cmd": "run_sequence"}', function(x) {
-                console.log(x['status'])
-                if (x['status'] == 'running') {
+            sendCommand('{"cmd": "run_multiple_sequences"}', function(x) {
+                console.log(x['ohmpi_status'])
+                if (x['ohmpi_status'] == 'running') {
                     output.innerHTML = 'Status: measuring...'
                 }
             })
@@ -176,7 +176,7 @@
         // interrupt button
         function stopBtnFunc() {
             sendCommand('{"cmd": "interrupt"}', function(x) {
-                output.innerHTML = 'Status: ' + x['status']
+                output.innerHTML = 'Status: ' + x['ohmpi_status']
                 clearInterval(interv)
                 getData()
             })
@@ -375,13 +375,14 @@
         
         // getData
         function getData() {
+            let surveyNames = []
             sendCommand(JSON.stringify({
                 'cmd': 'getData',
-                'surveyNames': Object.keys(data).slice(0, -1)
+                'surveyNames': surveyNames
                 // last survey is often partial so we download it again
             }), function(ddic) {
                 // update status
-                output.innerHTML = 'Status: ' + ddic['status']
+                //output.innerHTML = 'Status: ' + ddic['status']
 
                 // update data dic with new data
                 data = { // destructuring assignement (magic! :o)
@@ -389,8 +390,8 @@
                     ...ddic['data'] // value from second dic are preferred
                 }
                 
-                // dropdown with number of surveys and +++
-                let surveyNames = Object.keys(data).sort()
+                // dropdown with number of surveys
+                surveyNames = Object.keys(data).sort()
 
                 // remove listener as we will replace the choices
                 surveySelect.removeEventListener('change', surveySelectFunc)
@@ -417,18 +418,20 @@
                 // update list of quadrupoles if any
                 if (quads.length == 0) {
                     console.log('updating list of quadrupoles')
-                    let df = data[surveyNames[0]]
-                    let quadSelect = document.getElementById('quadSelect')
-                    quadSelect.innerHTML = ''
-                    for (let i = 0; i < df['a'].length; i++) {
-                        quad = [df['a'][i], df['b'][i], df['m'][i], df['n'][i]]
-                        quads.push(quad)
-                        let option = document.createElement('option')
-                        option.value = quad.join(', ')
-                        option.innerText = quad.join(', ')
-                        quadSelect.appendChild(option)
+                    if (surveyNames.length > 0) {
+                        let df = data[surveyNames[0]]
+                        let quadSelect = document.getElementById('quadSelect')
+                        quadSelect.innerHTML = ''
+                        for (let i = 0; i < df['a'].length; i++) {
+                            quad = [df['a'][i], df['b'][i], df['m'][i], df['n'][i]]
+                            quads.push(quad)
+                            let option = document.createElement('option')
+                            option.value = quad.join(', ')
+                            option.innerText = quad.join(', ')
+                            quadSelect.appendChild(option)
+                        }
+                        console.log('quads=', quads)
                     }
-                    console.log('quads=', quads)
                 }
 
                 // update time-serie figure
@@ -499,7 +502,7 @@
         function removeDataBtnFunc() {
             sendCommand('{"cmd": "removeData"}',function(x) {
                 data = {}
-                output.innerHTML = 'Status: ' + x['status'] + ' (all data cleared)'
+                output.innerHTML = 'Status: ' + x['ohmpi_status'] + ' (all data cleared)'
                 console.log('all data removed')
             })
         }
diff --git a/logging_setup.py b/logging_setup.py
index ad458f3df3b2a439e7086657fa143eb1692cf703..4c8162a4f6aeb6e578fd38d0fa811549d9e764df 100644
--- a/logging_setup.py
+++ b/logging_setup.py
@@ -76,7 +76,7 @@ def setup_loggers(mqtt=True):
                                                            interval=DATA_LOGGING_CONFIG['interval'])
     data_formatter = logging.Formatter(log_format)
     data_formatter.converter = gmtime
-    data_formatter.datefmt = '%Y/%m/%d %H:%M:%S UTC'
+    data_formatter.datefmt = '%Y-%m-%d %H:%M:%S UTC'
     data_handler.setFormatter(exec_formatter)
     data_logger.addHandler(data_handler)
     data_logger.setLevel(DATA_LOGGING_CONFIG['logging_level'])
diff --git a/ohmpi.py b/ohmpi.py
index 1eb110078099c2c1be379da3d06cc4f2ac1db6f6..ff36d078efe9a467b1246e6a41c8b47d2cd3ee0e 100644
--- a/ohmpi.py
+++ b/ohmpi.py
@@ -4,7 +4,7 @@ created on January 6, 2020.
 Updates May 2022, Oct 2022.
 Ohmpi.py is a program to control a low-cost and open hardware resistivity meter OhmPi that has been developed by
 Rémi CLEMENT (INRAE), Vivien DUBOIS (INRAE), Hélène GUYARD (IGE), Nicolas FORQUET (INRAE), Yannick FARGIER (IFSTTAR)
-Olivier KAUFMANN (UMONS), Arnaud WATELET (UMONS) and Guillaume BLANCHY (ILVO).
+Olivier KAUFMANN (UMONS), Arnaud WATELET (UMONS) and Guillaume BLANCHY (FNRS/ULiege).
 """
 
 import os
@@ -19,7 +19,6 @@ from io import StringIO
 from datetime import datetime
 from termcolor import colored
 import threading
-import paho.mqtt.client as mqtt_client
 from logging_setup import setup_loggers
 from config import MQTT_CONTROL_CONFIG, OHMPI_CONFIG, EXEC_LOGGING_CONFIG
 from logging import DEBUG
@@ -53,7 +52,7 @@ class OhmPi(object):
 
     def __init__(self, settings=None, sequence=None, use_mux=False, mqtt=True, on_pi=None, idps=False):
         """Constructs the ohmpi object
-         
+
         Parameters
         ----------
         settings:
@@ -69,7 +68,7 @@ class OhmPi(object):
         idps:
             if true uses the DPS
         """
-        
+
         if on_pi is None:
             _, on_pi = OhmPi._get_platform()
 
@@ -86,40 +85,6 @@ class OhmPi(object):
         self.soh_logger = None  # TODO: Implement the SOH logger
         print(msg)
 
-        # set controller
-        self.controller = mqtt_client.Client(f"ohmpi_{OHMPI_CONFIG['id']}_listener", clean_session=False)
-        trials = 0
-        trials_max = 10
-        broker_connected = False
-        self.exec_logger.debug(f"Connecting to control broker: {MQTT_CONTROL_CONFIG['hostname']}")
-        while trials < trials_max:
-            try:
-                self.controller.username_pw_set(MQTT_CONTROL_CONFIG['auth'].get('username'),
-                                                MQTT_CONTROL_CONFIG['auth']['password'])
-                self.controller.connect(MQTT_CONTROL_CONFIG['hostname'])
-                trials = trials_max
-                broker_connected = True
-            except Exception as e:
-                self.exec_logger.debug(f'Unable to connect control broker: {e}')
-                self.exec_logger.debug('Retrying to connect control broker...')
-                time.sleep(2)
-                trials += 1
-        if broker_connected:
-            self.exec_logger.debug(f"Subscribing to control topic {MQTT_CONTROL_CONFIG['ctrl_topic']}")
-            try:
-                self.controller.subscribe(MQTT_CONTROL_CONFIG['ctrl_topic'], MQTT_CONTROL_CONFIG['qos'])
-
-                msg = f"\u2611 Subscribed to control topic {MQTT_CONTROL_CONFIG['ctrl_topic']}" \
-                      f" on {MQTT_CONTROL_CONFIG['hostname']} broker"
-                self.exec_logger.debug(msg)
-                print(colored(msg, 'blue'))
-            except Exception as e:
-                self.exec_logger.warning(f'Unable to subscribe to control topic : {e}')
-                self.controller = None
-        else:
-            self.exec_logger.error(f"Unable to connect to control broker on {MQTT_CONTROL_CONFIG['hostname']}")
-            self.controller = None
-
         # read in hardware parameters (config.py)
         self._read_hardware_config()
 
@@ -179,14 +144,68 @@ class OhmPi(object):
             self.pin1.direction = Direction.OUTPUT
             self.pin1.value = False
 
-        # Starts the command processing thread
-        if self.controller is not None:
-            self.cmd_listen = True
-            self.cmd_thread = threading.Thread(target=self._control)
-            self.cmd_thread.start()
-        else:
-            self.exec_logger.warning('No connection to control broker.'
-                                     ' Use python/ipython to interact with OhmPi object...')
+        # set controller
+        self.mqtt = mqtt
+        self.cmd_id = None
+        if self.mqtt:
+            import paho.mqtt.client as mqtt_client
+            import paho.mqtt.publish as publish
+
+            self.exec_logger.debug(f"Connecting to control topic {MQTT_CONTROL_CONFIG['ctrl_topic']}"
+                                   f" on {MQTT_CONTROL_CONFIG['hostname']} broker")
+
+            def connect_mqtt() -> mqtt_client:
+                def on_connect(client, userdata, flags, rc):
+                    if rc == 0:
+                        self.exec_logger.debug(f"Successfully connected to control broker:"
+                                               f" {MQTT_CONTROL_CONFIG['hostname']}")
+                    else:
+                        self.exec_logger.warning(f'Failed to connect to control broker. Return code : {rc}')
+
+                client = mqtt_client.Client("ohmpi_{OHMPI_CONFIG['id']}_listener", clean_session=False)
+                client.username_pw_set(MQTT_CONTROL_CONFIG['auth'].get('username'),
+                                       MQTT_CONTROL_CONFIG['auth']['password'])
+                client.on_connect = on_connect
+                client.connect(MQTT_CONTROL_CONFIG['hostname'], MQTT_CONTROL_CONFIG['port'])
+                return client
+            try:
+                self.exec_logger.debug(f"Connecting to control broker: {MQTT_CONTROL_CONFIG['hostname']}")
+                self.controller = connect_mqtt()
+            except Exception as e:
+                self.exec_logger.debug(f'Unable to connect control broker: {e}')
+                self.controller = None
+            if self.controller is not None:
+                self.exec_logger.debug(f"Subscribing to control topic {MQTT_CONTROL_CONFIG['ctrl_topic']}")
+                try:
+                    self.controller.subscribe(MQTT_CONTROL_CONFIG['ctrl_topic'], MQTT_CONTROL_CONFIG['qos'])
+
+                    msg = f"Subscribed to control topic {MQTT_CONTROL_CONFIG['ctrl_topic']}" \
+                          f" on {MQTT_CONTROL_CONFIG['hostname']} broker"
+                    self.exec_logger.debug(msg)
+                    print(colored(f'\u2611 {msg}', 'blue'))
+                except Exception as e:
+                    self.exec_logger.warning(f'Unable to subscribe to control topic : {e}')
+                    self.controller = None
+                publisher_config = MQTT_CONTROL_CONFIG.copy()
+                publisher_config['topic'] = MQTT_CONTROL_CONFIG['ctrl_topic']
+                publisher_config.pop('ctrl_topic')
+
+                def on_message(client, userdata, message):
+                    print(message.payload.decode('utf-8'))
+                    command = message.payload.decode('utf-8')
+                    dic = json.loads(command)
+                    if dic['cmd_id'] != self.cmd_id:
+                        self.cmd_id = dic['cmd_id']
+                        self.exec_logger.debug(f'Received command {command}')
+                        payload = json.dumps({'cmd_id': dic['cmd_id'], 'reply': 'ok'})
+                        publish.single(payload=payload, **publisher_config)
+                        self._process_commands(command)
+
+                self.controller.on_message = on_message
+            else:
+                self.controller = None
+                self.exec_logger.warning('No connection to control broker.'
+                                         ' Use python/ipython to interact with OhmPi object...')
 
     @property
     def sequence(self):
@@ -205,18 +224,6 @@ class OhmPi(object):
             self.use_mux = False
         self._sequence = sequence
 
-    def _control(self):
-        """Gets commands from the controller(s) and execute them"""
-        def on_message(client, userdata, message):
-            command = message.payload.decode('utf-8')
-            self.exec_logger.debug(f'Received command {command}')
-            self._process_commands(command)
-
-        self.controller.on_message = on_message
-        self.controller.loop_start()
-        while True:
-            time.sleep(.5)  # TODO: Check if this waiting time should be reduced...
-
     def _update_acquisition_settings(self, config):
         warnings.warn('This function is deprecated, use update_settings() instead.', DeprecationWarning)
         self.update_settings(config)
@@ -297,7 +304,7 @@ class OhmPi(object):
     @staticmethod
     def _get_platform():
         """Gets platform name and checks if it is a raspberry pi
-        
+
         Returns
         -------
         str, bool
@@ -403,7 +410,8 @@ class OhmPi(object):
                 else:
                     mcp2.get_pin(relay_nr - 1).value = False
 
-                self.exec_logger.debug(f'Switching relay {relay_nr} {state} for electrode {electrode_nr}')
+                self.exec_logger.debug(f'Switching relay {relay_nr} '
+                                       f'({str(hex(self.board_addresses[role]))}) {state} for electrode {electrode_nr}')
             else:
                 self.exec_logger.warning(f'Unable to address electrode nr {electrode_nr}')
 
@@ -456,11 +464,13 @@ class OhmPi(object):
 
         Parameters
         ----------
-        channel:
+        channel : object
+            Instance of ADS where voltage is measured.
 
         Returns
         -------
-            float
+        gain : float
+            Gain to be applied on ADS1115.
         """
         gain = 2 / 3
         if (abs(channel.voltage) < 2.040) and (abs(channel.voltage) >= 1.023):
@@ -553,9 +563,9 @@ class OhmPi(object):
         self.ads_voltage = ads.ADS1115(self.i2c, gain=gain_voltage, data_rate=860, address=self.ads_voltage_address)
 
         # we measure the voltage on both A0 and A2 to guess the polarity
-        I = AnalogIn(self.ads_current, ads.P0).voltage * 1000/50/self.r_shunt  # measure current
-        U0 = AnalogIn(self.ads_voltage, ads.P0).voltage * 1000  # measure voltage
-        U2 = AnalogIn(self.ads_voltage, ads.P2).voltage * 1000
+        I = AnalogIn(self.ads_current, ads.P0).voltage * 1000. / 50 / self.r_shunt  # measure current
+        U0 = AnalogIn(self.ads_voltage, ads.P0).voltage * 1000.  # measure voltage
+        U2 = AnalogIn(self.ads_voltage, ads.P2).voltage * 1000.
         # print('I (mV)', I*50*self.r_shunt)
         # print('I (mA)', I)
         # print('U0 (mV)', U0)
@@ -571,7 +581,7 @@ class OhmPi(object):
 
         # compute constant
         c = vmn / I
-        Rab = (volt * 1000) / I
+        Rab = (volt * 1000.) / I
 
         self.exec_logger.debug(f'Rab = {Rab:.2f} Ohms')
 
@@ -585,9 +595,9 @@ class OhmPi(object):
                 iab = voltage_max / c
                 vab = iab * Rab
                 self.exec_logger.debug('target max voltage')
-            if vab > 25000:
-                vab = 25000
-            vab = vab / 1000 * 0.9
+            if vab > 25000.:
+                vab = 25000.
+            vab = vab / 1000. * 0.9
 
         elif strategy == 'vmin':
             vmn_min = c * current_min
@@ -598,9 +608,9 @@ class OhmPi(object):
                 iab = voltage_min / c
                 vab = iab * Rab
                 self.exec_logger.debug('target min voltage')
-            if vab < 1000:
-                vab = 1000
-            vab = vab / 1000 * 1.1
+            if vab < 1000.:
+                vab = 1000.
+            vab = vab / 1000. * 1.1
 
         elif strategy == 'constant':
             vab = volt
@@ -614,7 +624,8 @@ class OhmPi(object):
         return vab, polarity
 
     def run_measurement(self, quad=None, nb_stack=None, injection_duration=None,
-                        autogain=True, strategy='constant', tx_volt=5., best_tx_injtime=0.1):
+                        autogain=True, strategy='constant', tx_volt=5, best_tx_injtime=0.1,
+                        cmd_id=None):
         """Measures on a quadrupole and returns transfer resistance.
 
         Parameters
@@ -742,7 +753,7 @@ class OhmPi(object):
                     else:
                         self.pin0.value = False
                         self.pin1.value = True  # current injection nr2
-                    self.exec_logger.debug(str(n) + ' ' + str(self.pin0.value) + ' ' + str(self.pin1.value))
+                    self.exec_logger.debug(f'Stack {n} {self.pin0.value} {self.pin1.value}')
 
                     # measurement of current i and voltage u during injection
                     meas = np.zeros((self.nb_samples, 3)) * np.nan
@@ -759,7 +770,7 @@ class OhmPi(object):
                         elif self.board_version == '22.10':
                             meas[k, 1] = -AnalogIn(self.ads_voltage, ads.P0, ads.P1).voltage * self.coef_p2 * 1000
                         # else:
-                        #   self.exec_logger.debug('Unknown board')
+                        #    self.exec_logger.debug('Unknown board')
                         time.sleep(sampling_interval / 1000)
                         dt = time.time() - start_delay  # real injection time (s)
                         meas[k, 2] = time.time() - start_time
@@ -873,17 +884,6 @@ class OhmPi(object):
             d = {'time': datetime.now().isoformat(), 'A': quad[0], 'B': quad[1], 'M': quad[2], 'N': quad[3],
                  'R [ohm]': np.abs(np.random.randn(1)).tolist()}
 
-        # round number to two decimal for nicer string output
-        output = [f'{k}\t' for k in d.keys()]
-        output = str(output)[:-1] + '\n'
-        for k in d.keys():
-            if isinstance(d[k], float):
-                val = np.round(d[k], 2)
-            else:
-                val = d[k]
-                output += f'{val}\t'
-        output = output[:-1]
-
         # to the data logger
         dd = d.copy()
         dd.pop('fulldata')  # too much for logger
@@ -891,7 +891,14 @@ class OhmPi(object):
         dd.update({'B': str(dd['B'])})
         dd.update({'M': str(dd['M'])})
         dd.update({'N': str(dd['N'])})
-        self.data_logger.info(json.dumps(dd))
+
+        # round float to 2 decimal
+        for key in dd.keys():
+            if isinstance(dd[key], float):
+                dd[key] = np.round(dd[key], 3)
+
+        dd['cmd_id'] = str(cmd_id)
+        self.data_logger.info(dd)
 
         return d
 
@@ -962,11 +969,9 @@ class OhmPi(object):
 
                 # close mux path and put pin back to GND
                 self.switch_mux_off(quad)
-            self.reset_mux()
         else:
             pass
         self.status = 'idle'
-        # self.run = False
 
     #
     #         # TODO if interrupted, we would need to restore the values
@@ -1024,10 +1029,12 @@ class OhmPi(object):
             cmd_id = decoded_message.pop('cmd_id', None)
             cmd = decoded_message.pop('cmd', None)
             args = decoded_message.pop('args', '[]')
-            if args[0] != '[':
+            if len(args) == 0:
                 args = f'["{args}"]'
             args = json.loads(args)
             kwargs = decoded_message.pop('kwargs', '{}')
+            if len(kwargs) == 0:
+                kwargs= '{}'
             kwargs = json.loads(kwargs)
             self.exec_logger.debug(f'Calling method {cmd}({args}, {kwargs})')
             status = False
@@ -1040,7 +1047,9 @@ class OhmPi(object):
                     output = getattr(self, cmd)(*args, **kwargs)
                     status = True
                 except Exception as e:
-                    self.exec_logger.error(f'{e}\nUnable to execute {cmd}({args}, {kwargs}')
+                    self.exec_logger.error(
+                        f"{e}\nUnable to execute {cmd}({args + ', ' if args != '[]' else ''}"
+                        f"{kwargs if kwargs != '{}' else ''})")
                     status = False
         except Exception as e:
             self.exec_logger.warning(f'Unable to decode command {message}: {e}')
@@ -1050,10 +1059,6 @@ class OhmPi(object):
             reply = json.dumps(reply)
             self.exec_logger.debug(f'Execution report: {reply}')
 
-    def measure(self, *args, **kwargs):
-        warnings.warn('This function is deprecated. Use load_sequence instead.', DeprecationWarning)
-        self.run_sequence(*args, **kwargs)
-
     def set_sequence(self, sequence=sequence):
         try:
             self.sequence = np.loadtxt(StringIO(sequence)).astype('uint32')
@@ -1063,7 +1068,8 @@ class OhmPi(object):
             status = False
 
     def run_sequence(self, cmd_id=None, **kwargs):
-        """Runs sequence in sync mode
+        """Runs sequence synchronously (=blocking on main thread).
+           Additional arguments are passed to run_measurement().
         """
         self.status = 'running'
         self.exec_logger.debug(f'Status: {self.status}')
@@ -1110,160 +1116,72 @@ class OhmPi(object):
             acquired_data.update({'cmd_id': cmd_id})
             # log data to the data logger
             self.data_logger.info(f'{acquired_data}')
-            print(f'{acquired_data}')
             # save data and print in a text file
             self.append_and_save(filename, acquired_data)
-            self.exec_logger.debug(f'{i+1:d}/{n:d}')
+            self.exec_logger.debug(f'quadrupole {i+1:d}/{n:d}')
 
         self.status = 'idle'
 
     def run_sequence_async(self, cmd_id=None, **kwargs):
         """Runs the sequence in a separate thread. Can be stopped by 'OhmPi.interrupt()'.
-           
+            Additional arguments are passed to run_measurement().
+
             Parameters
             ----------
-            cmd_id: 
-                
+            cmd_id:
+
         """
-        # self.run = True
-        self.status = 'running'
-        self.exec_logger.debug(f'Status: {self.status}')
-        self.exec_logger.debug(f'Measuring sequence: {self.sequence}')
 
         def func():
-            # if self.status != 'running':
-            #    self.exec_logger.warning('Data acquisition interrupted')
-            #    break
-            t0 = time.time()
-
-            # create filename with timestamp
-            filename = self.settings["export_path"].replace('.csv',
-                                                            f'_{datetime.now().strftime("%Y%m%dT%H%M%S")}.csv')
-            self.exec_logger.debug(f'Saving to {filename}')
-
-            # make sure all multiplexer are off
-            self.reset_mux()
-
-            # measure all quadrupole of the sequence
-            if self.sequence is None:
-                n = 1
-            else:
-                n = self.sequence.shape[0]
-            for i in range(0, n):
-                if self.sequence is None:
-                    quad = np.array([0, 0, 0, 0])
-                else:
-                    quad = self.sequence[i, :]  # quadrupole
-                if self.status == 'stopping':
-                    break
-
-                # call the switch_mux function to switch to the right electrodes
-                self.switch_mux_on(quad)
-
-                # run a measurement
-                if self.on_pi:
-                    acquired_data = self.run_measurement(quad, **kwargs)
-                else:  # for testing, generate random data
-                    acquired_data = {
-                        'A': [quad[0]], 'B': [quad[1]], 'M': [quad[2]], 'N': [quad[3]],
-                        'R [ohm]': np.abs(np.random.randn(1))
-                    }
-
-                # switch mux off
-                self.switch_mux_off(quad)
-
-                # add command_id in dataset
-                acquired_data.update({'cmd_id': cmd_id})
-                # log data to the data logger
-                self.data_logger.info(f'{acquired_data}')
-                print(f'{acquired_data}')
-                # save data and print in a text file
-                self.append_and_save(filename, acquired_data)
-                self.exec_logger.debug(f'{i+1:d}/{n:d}')
-
-        self.status = 'idle'
+            self.run_sequence(**kwargs)
 
         self.thread = threading.Thread(target=func)
         self.thread.start()
+        self.status = 'idle'
+
+    def measure(self, *args, **kwargs):
+        warnings.warn('This function is deprecated. Use run_multiple_sequences() instead.', DeprecationWarning)
+        self.run_multiple_sequences(self, *args, **kwargs)
+
+    def run_multiple_sequences(self, cmd_id=None, sequence_delay=None, nb_meas=None, **kwargs):
+        """Runs multiple sequences in a separate thread for monitoring mode.
+           Can be stopped by 'OhmPi.interrupt()'.
+           Additional arguments are passed to run_measurement().
 
-    def run_multiple_sequences(self, *args, **kwargs):
-        """Run multiple sequences in a separate thread for monitoring mode.
-            Can be stopped by 'OhmPi.interrupt()'.
+        Parameters
+        ----------
+        sequence_delay : int, optional
+            Number of seconds at which the sequence must be started from each others.
+        nb_meas : int, optional
+            Number of time the sequence must be repeated.
+        kwargs : dict, optional
+            See help(k.run_measurement) for more info.
         """
         # self.run = True
+        if sequence_delay is None:
+            sequence_delay = self.settings['sequence_delay']
+        sequence_delay = int(sequence_delay)
+        if nb_meas is None:
+            nb_meas = self.settings['nb_meas']
         self.status = 'running'
         self.exec_logger.debug(f'Status: {self.status}')
         self.exec_logger.debug(f'Measuring sequence: {self.sequence}')
-        cmd_id = kwargs.pop('cmd_id', None)
-        
+
         def func():
-            for g in range(0, self.settings["nb_meas"]):  # for time-lapse monitoring
-                if self.status != 'running':
+            for g in range(0, nb_meas): # for time-lapse monitoring
+                if self.status == 'stopping':
                     self.exec_logger.warning('Data acquisition interrupted')
                     break
                 t0 = time.time()
-
-                # create filename with timestamp
-                filename = self.settings["export_path"].replace('.csv',
-                                                                f'_{datetime.now().strftime("%Y%m%dT%H%M%S")}.csv')
-                self.exec_logger.debug(f'Saving to {filename}')
-
-                # make sure all multiplexer are off
-                self.reset_mux()
-
-                # measure all quadrupole of the sequence
-                if self.sequence is None:
-                    n = 1
-                else:
-                    n = self.sequence.shape[0]
-                for i in range(0, n):
-                    if self.sequence is None:
-                        quad = np.array([0, 0, 0, 0])
-                    else:
-                        quad = self.sequence[i, :]  # quadrupole
-                    if self.status == 'stopping':
-                        break
-
-                    # call the switch_mux function to switch to the right electrodes
-                    self.switch_mux_on(quad)
-
-                    # run a measurement
-                    if self.on_pi:
-                        acquired_data = self.run_measurement(quad, **kwargs)
-                    else:  # for testing, generate random data
-                        acquired_data = {
-                            'A': [quad[0]], 'B': [quad[1]], 'M': [quad[2]], 'N': [quad[3]],
-                            'R [ohm]': np.abs(np.random.randn(1))
-                        }
-
-                    # switch mux off
-                    self.switch_mux_off(quad)
-
-                    # add command_id in dataset
-                    acquired_data.update({'cmd_id': cmd_id})
-                    # log data to the data logger
-                    self.data_logger.info(f'{acquired_data}')
-                    print(f'{acquired_data}')
-                    # save data and print in a text file
-                    self.append_and_save(filename, acquired_data)
-                    self.exec_logger.debug(f'{i+1:d}/{n:d}')
-
-                # compute time needed to take measurement and subtract it from interval
-                # between two sequence run (= sequence_delay)
-                measuring_time = time.time() - t0
-                sleep_time = self.settings["sequence_delay"] - measuring_time
-
-                if sleep_time < 0:
-                    # it means that the measuring time took longer than the sequence delay
-                    sleep_time = 0
-                    self.exec_logger.warning('The measuring time is longer than the sequence delay. '
-                                             'Increase the sequence delay')
+                self.run_sequence(**kwargs)
 
                 # sleeping time between sequence
-                if self.settings["nb_meas"] > 1:
-                    time.sleep(sleep_time)  # waiting for next measurement (time-lapse)
+                dt = sequence_delay - (time.time() - t0)
+                if dt < 0:
+                    dt = 0
+                if nb_meas > 1:
+                    time.sleep(dt)  # waiting for next measurement (time-lapse)
             self.status = 'idle'
-
         self.thread = threading.Thread(target=func)
         self.thread.start()
 
@@ -1275,6 +1193,7 @@ class OhmPi(object):
         """Interrupts the acquisition. """
         self.status = 'stopping'
         if self.thread is not None:
+            self.exec_logger.debug('Joining tread...')
             self.thread.join()
         else:
             self.exec_logger.debug('No sequence measurement thread to interrupt.')
@@ -1320,3 +1239,5 @@ print(f'local date and time : {current_time.strftime("%Y-%m-%d %H:%M:%S")}')
 # for testing
 if __name__ == "__main__":
     ohmpi = OhmPi(settings=OHMPI_CONFIG['settings'])
+    if ohmpi.controller is not None:
+        ohmpi.controller.loop_forever()
diff --git a/ohmpi_settings.json b/ohmpi_settings.json
index 8d4907d7a2f3c971bec8dfd529cfbec10aa7bb7b..e839f5fb9a4ddef894a5a8b714757b71cd860c02 100644
--- a/ohmpi_settings.json
+++ b/ohmpi_settings.json
@@ -3,6 +3,6 @@
     "injection_duration": 0.2,
     "nb_stack": 1,
     "nb_meas": 1,
-    "sequence_delay": 1,
+    "sequence_delay": 120,
     "export_path": "data/measurement.csv"
 }
diff --git a/test_ohmpi_mux.py b/test_ohmpi_mux.py
new file mode 100644
index 0000000000000000000000000000000000000000..ed3222f6b4cd2e79c282d71f9e025f1f57455181
--- /dev/null
+++ b/test_ohmpi_mux.py
@@ -0,0 +1,144 @@
+# test ohmpi and multiplexer on test resistances
+from ohmpi import OhmPi
+import matplotlib.pyplot as plt
+import numpy as np
+import pandas as pd
+import os
+import time
+
+# configure testing
+idps = False
+board_version = '22.10' # v2.0
+use_mux = True
+start_elec = 17  # start elec
+nelec = 16  # max elec in the sequence for testing
+
+# nelec electrodes Wenner sequence
+a = np.arange(nelec-3) + start_elec
+b = a + 3
+m = a + 1
+n = a + 2
+seq = np.c_[a, b, m, n]
+
+# testing measurement board only
+k = OhmPi(idps=idps, use_mux=use_mux)
+k.sequence = seq  # we need a sequence for possibly switching the mux
+k.reset_mux()  # just for safety
+if use_mux:
+    k.switch_mux_on(seq[:, 0])
+out1 = k.run_measurement(injection_duration=0.25, nb_stack=4, strategy='constant', tx_volt=12, autogain=True)
+out2 = k.run_measurement(injection_duration=0.5, nb_stack=2, strategy='vmin', tx_volt=5, autogain=True)
+out3 = k.run_measurement(injection_duration=1, nb_stack=1, strategy='vmax', tx_volt=5, autogain=True)
+if use_mux:
+    k.switch_mux_off(seq[:, 0])
+
+# visual figure of the full wave form
+fig, axs = plt.subplots(2, 1, sharex=True)
+ax = axs[0]
+labels = ['constant', 'vmin', 'vmax']
+for i, out in enumerate([out1, out2, out3]):
+  data = out['fulldata']
+  inan = ~(np.isnan(out['fulldata']).any(1))
+  ax.plot(data[inan,2], data[inan,0], '.-', label=labels[i])
+ax.set_ylabel('Current AB [mA]')
+ax.legend()
+ax = axs[1]
+for i, out in enumerate([out1, out2, out3]):
+  data = out['fulldata']
+  inan = ~(np.isnan(out['fulldata']).any(1))
+  ax.plot(data[inan,2], data[inan,1], '.-', label=labels[i])
+ax.set_ylabel('Voltage MN [mV]')
+ax.set_xlabel('Time [s]')
+fig.savefig('check-fullwave.jpg')
+
+
+# test a sequence
+if use_mux:
+    # manually edit default settings
+    k.settings['injection_duration'] = 1
+    k.settings['nb_stack'] = 1
+    #k.settings['nbr_meas'] = 1
+    k.sequence = seq
+    k.reset_mux()
+
+    # set quadrupole manually
+    k.switch_mux_on(seq[0, :])
+    out = k.run_measurement(quad=[3, 3, 3, 3], nb_stack=1, tx_volt=12, strategy='constant', autogain=True)
+    k.switch_mux_off(seq[0, :])
+    print(out)
+
+    # run rs_check() and save data
+    k.rs_check()  # check all electrodes of the sequence
+
+    # check values measured
+    fname = sorted(os.listdir('data/'))[-1]
+    print(fname)
+    dfrs = pd.read_csv('data/' + fname)
+    fig, ax = plt.subplots()
+    ax.hist(dfrs['RS [kOhm]'])
+    ax.set_xticks(np.arange(dfrs.shape[0]))
+    ax.set_xticklabels(dfrs['A'].astype(str) + ' - ' +
+                       dfrs['B'].astype(str), rotation=90)
+    ax.set_ylabel('Contact resistances [kOhm]')
+    fig.tight_layout()
+    fig.savefig('check-rs.jpg')
+
+    # run sequence synchronously and save data to file
+    k.run_sequence(nb_stack=1, injection_duration=0.25)
+
+    # check values measured
+    fname = sorted(os.listdir('data/'))[-1]
+    print(fname)
+    df = pd.read_csv('data/' + fname)
+    fig, ax = plt.subplots()
+    ax.hist(df['R [ohm]'])
+    ax.set_ylabel('Transfer resistance [Ohm]')
+    ax.set_xticks(np.arange(df.shape[0]))
+    ax.set_xticklabels(df['A'].astype(str) + ','
+                       + df['B'].astype(str) + ','
+                       + df['M'].astype(str) + ','
+                       + df['N'].astype(str), rotation=90)
+    fig.tight_layout()
+    fig.savefig('check-r.jpg')
+
+    # run sequence asynchronously and save data to file
+    k.run_sequence_async(nb_stack=1, injection_duration=0.25)
+    time.sleep(2) # if too short, it will still be resetting the mux (= no effect)
+    k.interrupt()  # will kill the asynchronous sequence running
+
+    # run a series of asynchronous sequences
+    k.settings['nb_meas'] = 2  # run the sequence twice
+    k.run_multiple_sequences(nb_stack=1, injection_duration=0.25)
+    time.sleep(5)
+    k.interrupt()
+
+
+# look at the noise frequency with FFT
+if False:
+    from numpy.fft import fft, ifft
+
+    x = data[inan, 1][10:300]
+    t = np.linspace(0, len(x)*4, len(x))
+    sr = 1/0.004
+
+    X = fft(x)
+    N = len(X)
+    n = np.arange(N)
+    T = N/sr
+    freq = n/T 
+
+    plt.figure(figsize = (12, 6))
+    plt.subplot(121)
+
+    plt.stem(freq, np.abs(X), 'b', \
+             markerfmt=" ", basefmt="-b")
+    plt.xlabel('Freq (Hz)')
+    plt.ylabel('FFT Amplitude |X(freq)|')
+    #plt.xlim(0, 10)
+
+    plt.subplot(122)
+    plt.plot(t, ifft(X), 'r')
+    plt.xlabel('Time (s)')
+    plt.ylabel('Amplitude')
+    plt.tight_layout()
+    plt.show()