diff --git a/README.md b/README.md
index 25aeda550050520c27e9e0c7312d34fe0b2b9538..243403d4ef388883a3e09fbc844d2e7e3958e256 100644
--- a/README.md
+++ b/README.md
@@ -23,6 +23,8 @@ We strongly recommend users to create a virtual environment to run the code and
 *  you should run you code within the virtual environment
 *  to leave the virtual environment simply type: `deactivate`
 
+To enable to Python module, we need to add the 'ohmpi' folder to the python path. This can be done by `source .env` or manually by adding `export PYTHONPATH=$PYTHONPATH:/home/<username>/OhmPi` to /home/<username>/.bashrc.
+
 ## First run with four electrodes
 
 In the examples folder, you will find the code "simple_measurement.py".
diff --git a/concepts_and_ideas/Prepa Hackathon.pdf b/concepts_and_ideas/Prepa Hackathon.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..9f41a520b7b9f20f8f930e8e251a9375e9f5a02b
Binary files /dev/null and b/concepts_and_ideas/Prepa Hackathon.pdf differ
diff --git a/configs/config_mb_2023_4_mux_2023.py b/configs/config_mb_2023.py
similarity index 56%
rename from configs/config_mb_2023_4_mux_2023.py
rename to configs/config_mb_2023.py
index 7d7fa2fda28ae3cddbf33f04bdcf9b4c240960f5..8bd970d8096ec3515ced1a1c9d11ea5e2aeabbc7 100644
--- a/configs/config_mb_2023_4_mux_2023.py
+++ b/configs/config_mb_2023.py
@@ -1,7 +1,7 @@
 import logging
 from ohmpi.utils import get_platform
 
-from paho.mqtt.client import MQTTv31
+from paho.mqtt.client import MQTTv31  # noqa
 
 _, on_pi = get_platform()
 # DEFINE THE ID OF YOUR OhmPi
@@ -19,47 +19,24 @@ OHMPI_CONFIG = {
 
 HARDWARE_CONFIG = {
     'ctl': {'model': 'raspberry_pi'},
-    'pwr': {'model': 'pwr_batt', 'voltage': 12.},
+    'pwr': {'model': 'pwr_batt', 'voltage': 12., 'interface_name': 'none'},
     'tx':  {'model': 'mb_2023_0_X',
-             'mcp_board_address': 0x20,
              'voltage_max': 12.,  # Maximum voltage supported by the TX board [V]
-             'current_max': 4800 / 50 / 2,  # Maximum current supported by the TX board [mA]
-             'r_shunt': 2  # Shunt resistance in Ohms
+             'adc_voltage_max': 4800.,  # Maximum voltage read by the current ADC on the TX board [mA]
+             'r_shunt': 2.,  # Shunt resistance in Ohms
+             'interface_name': 'i2c',
             },
     'rx':  {'model': 'mb_2023_0_X',
-             'coef_p2': 2.50,  # slope for current conversion for ADS.P2, measurement in V/V
-             'nb_samples': 20,  # Max value 10 # was named integer before...
+            'coef_p2': 2.50,  # slope for conversion for ADS, measurement in V/V
+            'sampling_rate': 50.,  # number of samples per second
+            'interface_name': 'i2c',
             },
-    'mux':  # default properties are system properties that will be
-            # overwritten by board properties defined at the board level within the board model file
-            # both will be overwritten by properties specified in the board dict below. Use with caution...
-            {'boards':
-                    {'mux_1':
-                         {'model': 'mux_2023_0_X',  # 'ohmpi_i2c_mux64_v1.01',
-                          'tca_address': 0x70,
-                          'roles': {'A': 'X'},
-                          'cabling': {(i, j): ('mux_1', i) for j in ['A'] for i in range(1, 65)},
-                          'voltage_max': 12.},
-                     'mux_2':
-                         {'model': 'mux_2023_0_X',  # 'ohmpi_i2c_mux64_v1.01',
-                          'tca_address': 0x71,
-                          'roles': {'B': 'X'},
-                          'cabling': {(i, j): ('mux_2', i) for j in ['B'] for i in range(1, 65)},
-                          'voltage_max': 12.},
-                     'mux_3':
-                         {'model': 'mux_2023_0_X',  # 'ohmpi_i2c_mux64_v1.01',
-                          'tca_address': 0x72,
-                          'roles': {'M': 'X'},
-                          'cabling': {(i, j): ('mux_3', i) for j in ['M'] for i in range(1, 65)},
-                          'voltage_max': 12.},
-                     'mux_4':
-                         {'model': 'mux_2023_0_X',  # 'ohmpi_i2c_mux64_v1.01',
-                          'tca_address': 0x73,
-                          'roles': {'N': 'X'},
-                          'cabling': {(i, j): ('mux_4', i) for j in ['N'] for i in range(1, 65)},
-                          'voltage_max': 12.},
-                     },
-             'default': {'connection': 'i2c',
+    'mux':  # default properties given in config are system properties that will be
+            # overwritten by properties defined in each the board dict below.
+            # if defined in board specs, values out of specs will be bounded to remain in specs
+            # omitted properties in config will be set to board specs default values if they exist
+            {'boards': {},
+             'default': {'interface_name': 'i2c',
                          'voltage_max': 100.,
                          'current_max': 3.}
              }
@@ -68,7 +45,7 @@ HARDWARE_CONFIG = {
 # SET THE LOGGING LEVELS, MQTT BROKERS AND MQTT OPTIONS ACCORDING TO YOUR NEEDS
 # Execution logging configuration
 EXEC_LOGGING_CONFIG = {
-    'logging_level': logging.INFO,
+    'logging_level': logging.DEBUG,  # TODO: set logging level back to INFO
     'log_file_logging_level': logging.DEBUG,
     'logging_to_console': True,
     'file_name': f'exec{logging_suffix}.log',
diff --git a/configs/config_mb_2023_3_mux_2024.py b/configs/config_mb_2023_3_mux_2024.py
index 78673566fccbe93641fcd012a407b05c80557aa8..7f83b2a4977c0a9fd0d85460d083e51b0d451065 100644
--- a/configs/config_mb_2023_3_mux_2024.py
+++ b/configs/config_mb_2023_3_mux_2024.py
@@ -21,40 +21,41 @@ HARDWARE_CONFIG = {
     'ctl': {'model': 'raspberry_pi'},
     'pwr': {'model': 'pwr_batt', 'voltage': 12.},
     'tx':  {'model': 'mb_2023_0_X',
-             'mcp_board_address': 0x20,
              'voltage_max': 12.,  # Maximum voltage supported by the TX board [V]
-             'current_max': 4800 / 50 / 2,  # Maximum current supported by the TX board [mA]
-             'r_shunt': 2  # Shunt resistance in Ohms
+             'adc_voltage_max': 4800.,  # Maximum voltage read by the current ADC on the TX board [mA]
+             'r_shunt': 2.,  # Shunt resistance in Ohms
+             'interface_name': 'i2c',
             },
     'rx':  {'model': 'mb_2023_0_X',
-             'coef_p2': 2.50,  # slope for conversion for ADS, measurement in V/V
-             'latency': 0.010,  # latency in seconds in continuous mode
-             'sampling_rate': 50  # number of samples per second
+            'coef_p2': 2.50,  # slope for conversion for ADS, measurement in V/V
+            'sampling_rate': 50.,  # number of samples per second
+            'interface_name': 'i2c',
             },
-    'mux':  # default properties are system properties that will be
-            # overwritten by board properties defined at the board level within the board model file
-            # both will be overwritten by properties specified in the board dict below. Use with caution...
+    'mux':  # default properties given in config are system properties that will be
+            # overwritten by properties defined in each the board dict below.
+            # if defined in board specs, values out of specs will be bounded to remain in specs
+            # omitted properties in config will be set to board specs default values if they exist
             {'boards':
                     {'mux_02':
-                         {'model': 'mux_2024_0_X',  # 'ohmpi_i2c_mux64_v1.01',
+                         {'model': 'mux_2024_0_X',
                           'tca_address': None,
                           'tca_channel': 0,
-                          'mcp_0': '0x22',  # TODO: Replace this with pos of jumper on MUX board (address doesn't mean anything for the average user...
+                          'mcp_0': '0x22',  # TODO: Replace this with pos of jumper on MUX board (address doesn't mean anything for the average user...)
                           'mcp_1': '0x23',  # TODO: Replace this with pos of jumper on MUX board (address doesn't mean anything for the average user...)
                           'roles': {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'},
                           'cabling': {(i+8, j): ('mux_02', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)},
                           'voltage_max': 12.},
                      'mux_00':
-                         {'model': 'mux_2024_0_X',  # 'ohmpi_i2c_mux64_v1.01',
+                         {'model': 'mux_2024_0_X',
                           'tca_address': None,
                           'tca_channel': 0,
-                          'mcp_0': '0x24',  # TODO : Replace this with pos of jumper on MUX board (address doesn't mean anything for the average user...
+                          'mcp_0': '0x24',  # TODO : Replace this with pos of jumper on MUX board (address doesn't mean anything for the average user...)
                           'mcp_1': '0x25',  # TODO : Replace this with pos of jumper on MUX board (address doesn't mean anything for the average user...)
                           'roles': {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'},
                           'cabling': {(i+16, j): ('mux_00', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)},
                           'voltage_max': 12.},
                      'mux_04':
-                         {'model': 'mux_2024_0_X',  # 'ohmpi_i2c_mux64_v1.01',
+                         {'model': 'mux_2024_0_X',
                           'tca_address': None,
                           'tca_channel': 0,
                           'mcp_0': '0x26',  # TODO : Replace this with pos of jumper on MUX board (address doesn't mean anything for the average user...
@@ -63,7 +64,7 @@ HARDWARE_CONFIG = {
                           'cabling': {(i+24, j): ('mux_04', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)},
                           'voltage_max': 12.}
                      },
-             'default': {'connection': 'i2c',
+             'default': {'interface_name': 'i2c',
                          'voltage_max': 100.,
                          'current_max': 3.}
              }
@@ -72,7 +73,7 @@ HARDWARE_CONFIG = {
 # SET THE LOGGING LEVELS, MQTT BROKERS AND MQTT OPTIONS ACCORDING TO YOUR NEEDS
 # Execution logging configuration
 EXEC_LOGGING_CONFIG = {
-    'logging_level': logging.INFO,
+    'logging_level': logging.DEBUG,  # TODO: set logging level back to INFO
     'log_file_logging_level': logging.DEBUG,
     'logging_to_console': True,
     'file_name': f'exec{logging_suffix}.log',
diff --git a/configs/config_mb_2023_4_mux_2024.py b/configs/config_mb_2023_4_mux_2024.py
deleted file mode 100644
index 23689580c1aa06dbf6e962c85f926ca5e27f54ce..0000000000000000000000000000000000000000
--- a/configs/config_mb_2023_4_mux_2024.py
+++ /dev/null
@@ -1,151 +0,0 @@
-import logging
-from ohmpi.utils import get_platform
-
-from paho.mqtt.client import MQTTv31
-
-_, on_pi = get_platform()
-# DEFINE THE ID OF YOUR OhmPi
-ohmpi_id = '0001' if on_pi else 'XXXX'
-# DEFINE YOUR MQTT BROKER (DEFAULT: 'localhost')
-mqtt_broker = 'localhost' if on_pi else 'NAME_YOUR_BROKER_WHEN_IN_SIMULATION_MODE_HERE'
-# DEFINE THE SUFFIX TO ADD TO YOUR LOGS FILES
-logging_suffix = ''
-
-# OhmPi configuration
-OHMPI_CONFIG = {
-    'id': ohmpi_id,  # Unique identifier of the OhmPi board (string)
-    'settings': 'ohmpi_settings.json',  # INSERT YOUR FAVORITE SETTINGS FILE HERE
-}
-
-HARDWARE_CONFIG = {
-    'ctl': {'model': 'raspberry_pi'},
-    'pwr': {'model': 'pwr_batt', 'voltage': 12.},
-    'tx':  {'model': 'mb_2023_0_X',
-             'mcp_board_address': 0x20,
-             'voltage_max': 12.,  # Maximum voltage supported by the TX board [V]
-             'current_max': 4800 / 50 / 2,  # Maximum current supported by the TX board [mA]
-             'r_shunt': 2  # Shunt resistance in Ohms
-            },
-    'rx':  {'model': 'mb_2023_0_X',
-             'coef_p2': 2.50,  # slope for conversion for ADS, measurement in V/V
-             'latency': 0.010,  # latency in seconds in continuous mode
-             'sampling_rate': 50  # number of samples per second
-            },
-    'mux':  # default properties are system properties that will be
-            # overwritten by board properties defined at the board level within the board model file
-            # both will be overwritten by properties specified in the board dict below. Use with caution...
-            {'boards':
-                    {'mux_02':
-                         {'model': 'mux_2024_0_X',  # 'ohmpi_i2c_mux64_v1.01',
-                          'tca_address': 0x77,
-                          'tca_channel': 0,
-                          'mcp_0': '0x22',  # NOTE: Check pos of jumper on MUX board (refer to doc)
-                          'mcp_1': '0x23',  # NOTE: Check pos of jumper on MUX board (refer to doc)
-                          'roles': {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'},
-                          'cabling': {(i+8, j): ('mux_02', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)},  # TODO: avoid redundency of mux_id
-                          'voltage_max': 12.},
-                     'mux_05':
-                         {'model': 'mux_2024_0_X',  # 'ohmpi_i2c_mux64_v1.01',
-                          'tca_address': 0x77,
-                          'tca_channel': 0,
-                          'mcp_0': '0x26',  # NOTE: Check pos of jumper on MUX board (refer to doc)
-                          'mcp_1': '0x27',  # NOTE: Check pos of jumper on MUX board (refer to doc)
-                          'roles': {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'},
-                          'cabling': {(i+16, j): ('mux_05', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)},
-                          'voltage_max': 12.},
-                     'mux_04':
-                         {'model': 'mux_2024_0_X',  # 'ohmpi_i2c_mux64_v1.01',
-                          'tca_address': 0x77,
-                          'tca_channel': 1,
-                          'mcp_0': '0x24',  # NOTE: Check pos of jumper on MUX board (refer to doc)
-                          'mcp_1': '0x25',  # NOTE: Check pos of jumper on MUX board (refer to doc)
-                          'roles': {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'},
-                          'cabling': {(i+24, j): ('mux_04', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)},
-                          'voltage_max': 12.},
-                     'mux_03':
-                         {'model': 'mux_2024_0_X',  # 'ohmpi_i2c_mux64_v1.01',
-                          'tca_address': 0x77,
-                          'tca_channel': 1,
-                          'mcp_0': '0x26',  # NOTE: Check pos of jumper on MUX board (refer to doc)
-                          'mcp_1': '0x27',  # NOTE: Check pos of jumper on MUX board (refer to doc)
-                          'roles': {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'},
-                          'cabling': {(i+32, j): ('mux_03', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)},
-                          'voltage_max': 12.}
-                     },
-             'default': {'voltage_max': 100.,
-                         'current_max': 3.}
-             }
-}
-
-# SET THE LOGGING LEVELS, MQTT BROKERS AND MQTT OPTIONS ACCORDING TO YOUR NEEDS
-# Execution logging configuration
-EXEC_LOGGING_CONFIG = {
-    'logging_level': logging.INFO,
-    'log_file_logging_level': logging.DEBUG,
-    'logging_to_console': True,
-    'file_name': f'exec{logging_suffix}.log',
-    'max_bytes': 262144,
-    'backup_count': 30,
-    'when': 'd',
-    'interval': 1
-}
-
-# Data logging configuration
-DATA_LOGGING_CONFIG = {
-    'logging_level': logging.INFO,
-    'logging_to_console': True,
-    'file_name': f'data{logging_suffix}.log',
-    'max_bytes': 16777216,
-    'backup_count': 1024,
-    'when': 'd',
-    'interval': 1
-}
-
-# State of Health logging configuration (For a future release)
-SOH_LOGGING_CONFIG = {
-    'logging_level': logging.INFO,
-    'logging_to_console': True,
-    'log_file_logging_level': logging.DEBUG,
-    'file_name': f'soh{logging_suffix}.log',
-    'max_bytes': 16777216,
-    'backup_count': 1024,
-    'when': 'd',
-    'interval': 1
-}
-
-# MQTT logging configuration parameters
-MQTT_LOGGING_CONFIG = {
-    'hostname': mqtt_broker,
-    'port': 1883,
-    'qos': 2,
-    'retain': False,
-    'keepalive': 60,
-    'will': None,
-    'auth': {'username': 'mqtt_user', 'password': 'mqtt_password'},
-    'tls': None,
-    'protocol': MQTTv31,
-    'transport': 'tcp',
-    'client_id': f'{OHMPI_CONFIG["id"]}',
-    'exec_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/exec',
-    'exec_logging_level': logging.DEBUG,
-    'data_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/data',
-    'data_logging_level': DATA_LOGGING_CONFIG['logging_level'],
-    'soh_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/soh',
-    'soh_logging_level': SOH_LOGGING_CONFIG['logging_level']
-}
-
-# MQTT control configuration parameters
-MQTT_CONTROL_CONFIG = {
-    'hostname': mqtt_broker,
-    'port': 1883,
-    'qos': 2,
-    'retain': False,
-    'keepalive': 60,
-    'will': None,
-    'auth': {'username': 'mqtt_user', 'password': 'mqtt_password'},
-    'tls': None,
-    'protocol': MQTTv31,
-    'transport': 'tcp',
-    'client_id': f'{OHMPI_CONFIG["id"]}',
-    'ctrl_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/ctrl'
-}
diff --git a/configs/config_mb_2024_0_2.py b/configs/config_mb_2024_0_2.py
index f9fa2ec67c1a8ff324fb3d69a975645d4c7835cf..f99c214d235e821f4b55fca27db02d6b5bff2760 100644
--- a/configs/config_mb_2024_0_2.py
+++ b/configs/config_mb_2024_0_2.py
@@ -1,7 +1,6 @@
 import logging
 from ohmpi.utils import get_platform
-
-from paho.mqtt.client import MQTTv31
+from paho.mqtt.client import MQTTv31  # noqa
 
 _, on_pi = get_platform()
 # DEFINE THE ID OF YOUR OhmPi
@@ -19,18 +18,23 @@ OHMPI_CONFIG = {
 
 HARDWARE_CONFIG = {
     'ctl': {'model': 'raspberry_pi'},
-    'pwr': {'model': 'pwr_batt', 'voltage': 12.},
-    'tx':  {'model': 'mb_2024_rev_0_2',
+    'pwr': {'model': 'pwr_batt', 'voltage': 12., 'interface_name': 'none'},
+    'tx':  {'model': 'mb_2024_0_2',
              'voltage_max': 50.,  # Maximum voltage supported by the TX board [V]
-             'current_max': 4800 / 50 / 2,  # Maximum current supported by the TX board [mA]
-             'r_shunt': 2  # Shunt resistance in Ohms
+             'current_max': 4800,  # Maximum voltage read by the current ADC on the TX board [mA]
+             'r_shunt': 2,  # Shunt resistance in Ohms
+             'interface_name': 'i2c'
             },
-    'rx':  {'model': 'mb_2024_rev_0_2',
-             'coef_p2': 2.50,  # slope for conversion for ADS, measurement in V/V
+    'rx':  {'model': 'mb_2024_0_2',
+             'coef_p2': 1.00,  # slope for conversion for ADS, measurement in V/V
              'latency': 0.010,  # latency in seconds in continuous mode
-             'sampling_rate': 50  # number of samples per second
-            }
-}
+             'sampling_rate': 50,  # number of samples per second
+             'interface_name': 'i2c'
+            },
+    'mux': {'boards': {},
+            'default': {}
+           }
+    }
 
 # SET THE LOGGING LEVELS, MQTT BROKERS AND MQTT OPTIONS ACCORDING TO YOUR NEEDS
 # Execution logging configuration
@@ -39,7 +43,7 @@ EXEC_LOGGING_CONFIG = {
     'log_file_logging_level': logging.DEBUG,
     'logging_to_console': True,
     'file_name': f'exec{logging_suffix}.log',
-    'max_bytes': 262144,
+    'max_bytes': 1048576,
     'backup_count': 30,
     'when': 'd',
     'interval': 1
diff --git a/configs/config_mb_2023_mux_2024.py b/configs/config_mb_2024_0_2__1_mux_2024.py
similarity index 62%
rename from configs/config_mb_2023_mux_2024.py
rename to configs/config_mb_2024_0_2__1_mux_2024.py
index e78a44a6ebd3839240ca7d56d03b1ff13e80936a..a9a91e4d052bb426c4f5c9e3765578b0a7f73a19 100644
--- a/configs/config_mb_2023_mux_2024.py
+++ b/configs/config_mb_2024_0_2__1_mux_2024.py
@@ -1,7 +1,6 @@
 import logging
 from ohmpi.utils import get_platform
-
-from paho.mqtt.client import MQTTv31
+from paho.mqtt.client import MQTTv31  # noqa
 
 _, on_pi = get_platform()
 # DEFINE THE ID OF YOUR OhmPi
@@ -18,36 +17,38 @@ OHMPI_CONFIG = {
 }
 
 HARDWARE_CONFIG = {
-    'ctl': {'model' : 'raspberry_pi'
-                   },
-    'pwr': {'model' : 'pwr_batt', 'voltage': 12.},
-    'tx' : {'model' : 'mb_2023_0_X',
-             'mcp_board_address': 0x20,
-             'voltage_max': 12., # Maximum voltage supported by the TX board [V]
-             'current_max': 4800 / 50 / 2,  # Maximum current supported by the TX board [mA]
-             'r_shunt': 2  # Shunt resistance in Ohms
+    'ctl': {'model': 'raspberry_pi'},
+    'pwr': {'model': 'pwr_batt', 'voltage': 12., 'interface_name': 'none'},
+    'tx':  {'model': 'mb_2024_0_2',
+             'voltage_max': 50.,  # Maximum voltage supported by the TX board [V]
+             'current_max': 4800,  # Maximum voltage read by the current ADC on the TX board [mA]
+             'r_shunt': 2,  # Shunt resistance in Ohms
+             'interface_name': 'i2c'
             },
-    'rx': {'model': 'mb_2023_0_X',
-             'coef_p2': 2.50,  # slope for current conversion for ADS.P2, measurement in V/V
-             'sampling_rate': 100.,  # Hz
-             'nb_samples': 20,  # Max value 10 # was named integer before...
+    'rx':  {'model': 'mb_2024_0_2',
+             'coef_p2': 1.00,  # slope for conversion for ADS, measurement in V/V
+             'latency': 0.010,  # latency in seconds in continuous mode
+             'sampling_rate': 50,  # number of samples per second
+             'interface_name': 'i2c'
             },
-    'mux':  # default properties are system properties that will be
-            # overwritten by board properties defined at the board level within the board model file
-            # both will be overwritten by properties specified in the board dict below. Use with caution...
-        {'boards':
-                {'mux_1':
-                     {'model' : 'mux_2024_0_X', # 'ohmpi_i2c_mux64_v1.01',
+    'mux': {'boards':
+                {'mux_03':
+                     {'model': 'mux_2024_0_X',
                       'tca_address': None,
                       'tca_channel': 0,
-                      'mcp_0' : '0x22',  # TODO : Replace this with pos of jumper on MUX board (address doesn't mean anything for the average user...
-                      'mcp_1' : '0x23',  # TODO : Replace this with pos of jumper on MUX board (address doesn't mean anything for the average user...)
-                      'roles' : {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'},
-                      'voltage_max': 12.
-                    }
-                },
-            'default': {'voltage_max': 100., 'current_max': 3.}}
-}
+                      'addr2': 'down',
+                      'addr1': 'down',
+                      # 'mcp_0': '0x26',
+                      # 'mcp_1': '0x27',
+                      'roles': {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'},
+                      'cabling': {(i+0, j): ('mux_03', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)},
+                      'voltage_max': 12.}
+                 },
+             'default': {'interface_name': 'i2c_ext',
+                         'voltage_max': 100.,
+                         'current_max': 3.}
+            }
+    }
 
 # SET THE LOGGING LEVELS, MQTT BROKERS AND MQTT OPTIONS ACCORDING TO YOUR NEEDS
 # Execution logging configuration
diff --git a/configs/config_mb_2023_2_mux_2024.py b/configs/config_mb_2024_0_2__1_mux_2024_dps5005.py
similarity index 54%
rename from configs/config_mb_2023_2_mux_2024.py
rename to configs/config_mb_2024_0_2__1_mux_2024_dps5005.py
index 74645aafee9880eecf27d67c321cfb5ce98dadf5..86f5a3b59abe9c0d1e56d88463b892688749078c 100644
--- a/configs/config_mb_2023_2_mux_2024.py
+++ b/configs/config_mb_2024_0_2__1_mux_2024_dps5005.py
@@ -1,7 +1,6 @@
 import logging
 from ohmpi.utils import get_platform
-
-from paho.mqtt.client import MQTTv31
+from paho.mqtt.client import MQTTv31  # noqa
 
 _, on_pi = get_platform()
 # DEFINE THE ID OF YOUR OhmPi
@@ -19,45 +18,37 @@ OHMPI_CONFIG = {
 
 HARDWARE_CONFIG = {
     'ctl': {'model': 'raspberry_pi'},
-    'pwr': {'model': 'pwr_batt', 'voltage': 12.},
-    'tx':  {'model': 'mb_2023_0_X',
-             'mcp_board_address': 0x20,
-             'voltage_max': 12.,  # Maximum voltage supported by the TX board [V]
-             'current_max': 4800 / 50 / 2,  # Maximum current supported by the TX board [mA]
-             'r_shunt': 2  # Shunt resistance in Ohms
+    'pwr': {'model': 'pwr_dps5005', 'voltage': 3., 'interface_name': 'modbus'},
+    'tx':  {'model': 'mb_2024_0_2',
+             'voltage_max': 50.,  # Maximum voltage supported by the TX board [V]
+             'current_max': 4800,  # Maximum voltage read by the current ADC on the TX board [mA]
+             'r_shunt': 2,  # Shunt resistance in Ohms
+             'interface_name': 'i2c'
             },
-    'rx':  {'model': 'mb_2023_0_X',
-             'coef_p2': 2.50,  # slope for conversion for ADS, measurement in V/V
+    'rx':  {'model': 'mb_2024_0_2',
+             'coef_p2': 1.00,  # slope for conversion for ADS, measurement in V/V
              'latency': 0.010,  # latency in seconds in continuous mode
-             'sampling_rate': 50  # number of samples per second
+             'sampling_rate': 50,  # number of samples per second
+             'interface_name': 'i2c'
             },
-    'mux':  # default properties are system properties that will be
-            # overwritten by board properties defined at the board level within the board model file
-            # both will be overwritten by properties specified in the board dict below. Use with caution...
-            {'boards':
-                    {'mux_1':
-                         {'model': 'mux_2024_0_X',  # 'ohmpi_i2c_mux64_v1.01',
-                          'tca_address': None,
-                          'tca_channel': 0,
-                          'mcp_0': '0x22',  # TODO : Replace this with pos of jumper on MUX board (address doesn't mean anything for the average user...
-                          'mcp_1': '0x23',  # TODO : Replace this with pos of jumper on MUX board (address doesn't mean anything for the average user...)
-                          'roles': {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'},
-                          'cabling': {(i+8, j): ('mux_1', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)},
-                          'voltage_max': 12.},
-                     'mux_2':
-                         {'model': 'mux_2024_0_X',  # 'ohmpi_i2c_mux64_v1.01',
-                          'tca_address': None,
-                          'tca_channel': 0,
-                          'mcp_0': '0x24',  # TODO : Replace this with pos of jumper on MUX board (address doesn't mean anything for the average user...
-                          'mcp_1': '0x25',  # TODO : Replace this with pos of jumper on MUX board (address doesn't mean anything for the average user...)
-                          'roles': {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'},
-                          'cabling': {(i+16, j): ('mux_2', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)},
-                          'voltage_max': 12.}
-                     },
-             'default': {'voltage_max': 100.,
+    'mux': {'boards':
+                {'mux_03':
+                     {'model': 'mux_2024_0_X',
+                      'tca_address': None,
+                      'tca_channel': 0,
+                      'addr2': 'down',
+                      'addr1': 'down',
+                      # 'mcp_0': '0x26',
+                      # 'mcp_1': '0x27',
+                      'roles': {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'},
+                      'cabling': {(i+0, j): ('mux_03', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)},
+                      'voltage_max': 12.}
+                 },
+             'default': {'interface_name': 'i2c_ext',
+                         'voltage_max': 100.,
                          'current_max': 3.}
-             }
-}
+            }
+    }
 
 # SET THE LOGGING LEVELS, MQTT BROKERS AND MQTT OPTIONS ACCORDING TO YOUR NEEDS
 # Execution logging configuration
diff --git a/configs/config_mb_2024_0_2__2_mux_2024_dps5005.py b/configs/config_mb_2024_0_2__2_mux_2024_dps5005.py
new file mode 100644
index 0000000000000000000000000000000000000000..6466bc801ceace9ab4c1cbf89f7b9877b9ee4969
--- /dev/null
+++ b/configs/config_mb_2024_0_2__2_mux_2024_dps5005.py
@@ -0,0 +1,133 @@
+import logging
+from ohmpi.utils import get_platform
+from paho.mqtt.client import MQTTv31  # noqa
+
+_, on_pi = get_platform()
+# DEFINE THE ID OF YOUR OhmPi
+ohmpi_id = '0001' if on_pi else 'XXXX'
+# DEFINE YOUR MQTT BROKER (DEFAULT: 'localhost')
+mqtt_broker = 'localhost' if on_pi else 'NAME_YOUR_BROKER_WHEN_IN_SIMULATION_MODE_HERE'
+# DEFINE THE SUFFIX TO ADD TO YOUR LOGS FILES
+logging_suffix = ''
+
+# OhmPi configuration
+OHMPI_CONFIG = {
+    'id': ohmpi_id,  # Unique identifier of the OhmPi board (string)
+    'settings': 'ohmpi_settings.json',  # INSERT YOUR FAVORITE SETTINGS FILE HERE
+}
+
+HARDWARE_CONFIG = {
+    'ctl': {'model': 'raspberry_pi'},
+    'pwr': {'model': 'pwr_dps5005', 'voltage': 3., 'interface_name': 'modbus'},
+    'tx':  {'model': 'mb_2024_0_2',
+             'voltage_max': 50.,  # Maximum voltage supported by the TX board [V]
+             'current_max': 4800,  # Maximum voltage read by the current ADC on the TX board [mA]
+             'r_shunt': 2,  # Shunt resistance in Ohms
+             'interface_name': 'i2c'
+            },
+    'rx':  {'model': 'mb_2024_0_2',
+             'coef_p2': 1.00,  # slope for conversion for ADS, measurement in V/V
+             'latency': 0.010,  # latency in seconds in continuous mode
+             'sampling_rate': 50,  # number of samples per second
+             'interface_name': 'i2c'
+            },
+    'mux': {'boards':
+                {'mux_02':
+                     {'model': 'mux_2024_0_X',
+                      'tca_address': None,
+                      'tca_channel': 0,
+                      'addr2': 'up',
+                      'addr1': 'up',
+                      # 'mcp_0': '0x26',
+                      # 'mcp_1': '0x27',
+                      'roles': {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'},
+                      'cabling': {(i+0, j): ('mux_02', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)},
+                      'voltage_max': 12.},
+                 'mux_05':
+                     {'model': 'mux_2024_0_X',
+                      'tca_address': None,
+                      'tca_channel': 0,
+                      'addr2': 'up',
+                      'addr1': 'down',
+                      'roles': {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'},
+                      'cabling': {(i+8, j): ('mux_05', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)},
+                      'voltage_max': 12.}
+                 },
+             'default': {'interface_name': 'i2c_ext',
+                         'voltage_max': 100.,
+                         'current_max': 3.}
+            }
+    }
+
+# SET THE LOGGING LEVELS, MQTT BROKERS AND MQTT OPTIONS ACCORDING TO YOUR NEEDS
+# Execution logging configuration
+EXEC_LOGGING_CONFIG = {
+    'logging_level': logging.INFO,
+    'log_file_logging_level': logging.DEBUG,
+    'logging_to_console': True,
+    'file_name': f'exec{logging_suffix}.log',
+    'max_bytes': 262144,
+    'backup_count': 30,
+    'when': 'd',
+    'interval': 1
+}
+
+# Data logging configuration
+DATA_LOGGING_CONFIG = {
+    'logging_level': logging.INFO,
+    'logging_to_console': True,
+    'file_name': f'data{logging_suffix}.log',
+    'max_bytes': 16777216,
+    'backup_count': 1024,
+    'when': 'd',
+    'interval': 1
+}
+
+# State of Health logging configuration (For a future release)
+SOH_LOGGING_CONFIG = {
+    'logging_level': logging.INFO,
+    'logging_to_console': True,
+    'log_file_logging_level': logging.DEBUG,
+    'file_name': f'soh{logging_suffix}.log',
+    'max_bytes': 16777216,
+    'backup_count': 1024,
+    'when': 'd',
+    'interval': 1
+}
+
+# MQTT logging configuration parameters
+MQTT_LOGGING_CONFIG = {
+    'hostname': mqtt_broker,
+    'port': 1883,
+    'qos': 2,
+    'retain': False,
+    'keepalive': 60,
+    'will': None,
+    'auth': {'username': 'mqtt_user', 'password': 'mqtt_password'},
+    'tls': None,
+    'protocol': MQTTv31,
+    'transport': 'tcp',
+    'client_id': f'{OHMPI_CONFIG["id"]}',
+    'exec_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/exec',
+    'exec_logging_level': logging.DEBUG,
+    'data_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/data',
+    'data_logging_level': DATA_LOGGING_CONFIG['logging_level'],
+    'soh_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/soh',
+    'soh_logging_level': SOH_LOGGING_CONFIG['logging_level']
+}
+
+# MQTT control configuration parameters
+MQTT_CONTROL_CONFIG = {
+    'hostname': mqtt_broker,
+    'port': 1883,
+    'qos': 2,
+    'retain': False,
+    'keepalive': 60,
+    'will': None,
+    'auth': {'username': 'mqtt_user', 'password': 'mqtt_password'},
+    'tls': None,
+    'protocol': MQTTv31,
+    'transport': 'tcp',
+    'client_id': f'{OHMPI_CONFIG["id"]}',
+    'ctrl_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/ctrl'
+}
diff --git a/configs/config_mb_2024_0_2__3_mux_2024_dps5005.py b/configs/config_mb_2024_0_2__3_mux_2024_dps5005.py
new file mode 100644
index 0000000000000000000000000000000000000000..5b8f4f56a4aff2271e1be00f89a8ae79b86b68bb
--- /dev/null
+++ b/configs/config_mb_2024_0_2__3_mux_2024_dps5005.py
@@ -0,0 +1,144 @@
+import logging
+from ohmpi.utils import get_platform
+from paho.mqtt.client import MQTTv31  # noqa
+
+_, on_pi = get_platform()
+# DEFINE THE ID OF YOUR OhmPi
+ohmpi_id = '0001' if on_pi else 'XXXX'
+# DEFINE YOUR MQTT BROKER (DEFAULT: 'localhost')
+mqtt_broker = 'localhost' if on_pi else 'NAME_YOUR_BROKER_WHEN_IN_SIMULATION_MODE_HERE'
+# DEFINE THE SUFFIX TO ADD TO YOUR LOGS FILES
+logging_suffix = ''
+
+# OhmPi configuration
+OHMPI_CONFIG = {
+    'id': ohmpi_id,  # Unique identifier of the OhmPi board (string)
+    'settings': 'ohmpi_settings.json',  # INSERT YOUR FAVORITE SETTINGS FILE HERE
+}
+
+HARDWARE_CONFIG = {
+    'ctl': {'model': 'raspberry_pi'},
+    'pwr': {'model': 'pwr_dps5005', 'voltage': 3., 'interface_name': 'modbus'},
+    'tx':  {'model': 'mb_2024_0_2',
+             'voltage_max': 50.,  # Maximum voltage supported by the TX board [V]
+             'current_max': 4800,  # Maximum voltage read by the current ADC on the TX board [mA]
+             'r_shunt': 2,  # Shunt resistance in Ohms
+             'interface_name': 'i2c'
+            },
+    'rx':  {'model': 'mb_2024_0_2',
+             'coef_p2': 1.00,  # slope for conversion for ADS, measurement in V/V
+             'latency': 0.010,  # latency in seconds in continuous mode
+             'sampling_rate': 50,  # number of samples per second
+             'interface_name': 'i2c'
+            },
+    'mux': {'boards':
+                {'mux_02':
+                     {'model': 'mux_2024_0_X',
+                      'tca_address': None,
+                      'tca_channel': 0,
+                      'addr2': 'up',
+                      'addr1': 'up',
+                      # 'mcp_0': '0x26',
+                      # 'mcp_1': '0x27',
+                      'roles': {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'},
+                      'cabling': {(i+8, j): ('mux_02', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)},
+                      'voltage_max': 50.},
+                'mux_03':
+                     {'model': 'mux_2024_0_X',
+                      'tca_address': None,
+                      'tca_channel': 0,
+                      'addr2': 'down',
+                      'addr1': 'up',
+                      # 'mcp_0': '0x26',
+                      # 'mcp_1': '0x27',
+                      'roles': {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'},
+                      'cabling': {(i+16, j): ('mux_03', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)},
+                      'voltage_max': 50.},
+                 'mux_05':
+                     {'model': 'mux_2024_0_X',
+                      'tca_address': None,
+                      'tca_channel': 0,
+                      'addr2': 'up',
+                      'addr1': 'down',
+                      'roles': {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'},
+                      'cabling': {(i+0, j): ('mux_05', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)},
+                      'voltage_max': 50.},
+                 },
+             'default': {'interface_name': 'i2c_ext',
+                         'voltage_max': 100.,
+                         'current_max': 3.}
+            }
+    }
+
+# SET THE LOGGING LEVELS, MQTT BROKERS AND MQTT OPTIONS ACCORDING TO YOUR NEEDS
+# Execution logging configuration
+EXEC_LOGGING_CONFIG = {
+    'logging_level': logging.INFO,
+    'log_file_logging_level': logging.DEBUG,
+    'logging_to_console': True,
+    'file_name': f'exec{logging_suffix}.log',
+    'max_bytes': 262144,
+    'backup_count': 30,
+    'when': 'd',
+    'interval': 1
+}
+
+# Data logging configuration
+DATA_LOGGING_CONFIG = {
+    'logging_level': logging.INFO,
+    'logging_to_console': True,
+    'file_name': f'data{logging_suffix}.log',
+    'max_bytes': 16777216,
+    'backup_count': 1024,
+    'when': 'd',
+    'interval': 1
+}
+
+# State of Health logging configuration (For a future release)
+SOH_LOGGING_CONFIG = {
+    'logging_level': logging.INFO,
+    'logging_to_console': True,
+    'log_file_logging_level': logging.DEBUG,
+    'file_name': f'soh{logging_suffix}.log',
+    'max_bytes': 16777216,
+    'backup_count': 1024,
+    'when': 'd',
+    'interval': 1
+}
+
+# MQTT logging configuration parameters
+MQTT_LOGGING_CONFIG = {
+    'hostname': mqtt_broker,
+    'port': 1883,
+    'qos': 2,
+    'retain': False,
+    'keepalive': 60,
+    'will': None,
+    'auth': {'username': 'mqtt_user', 'password': 'mqtt_password'},
+    'tls': None,
+    'protocol': MQTTv31,
+    'transport': 'tcp',
+    'client_id': f'{OHMPI_CONFIG["id"]}',
+    'exec_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/exec',
+    'exec_logging_level': logging.DEBUG,
+    'data_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/data',
+    'data_logging_level': DATA_LOGGING_CONFIG['logging_level'],
+    'soh_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/soh',
+    'soh_logging_level': SOH_LOGGING_CONFIG['logging_level']
+}
+
+# MQTT control configuration parameters
+MQTT_CONTROL_CONFIG = {
+    'hostname': mqtt_broker,
+    'port': 1883,
+    'qos': 2,
+    'retain': False,
+    'keepalive': 60,
+    'will': None,
+    'auth': {'username': 'mqtt_user', 'password': 'mqtt_password'},
+    'tls': None,
+    'protocol': MQTTv31,
+    'transport': 'tcp',
+    'client_id': f'{OHMPI_CONFIG["id"]}',
+    'ctrl_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/ctrl'
+}
diff --git a/configs/config_mb_2024_0_2__4_mux_2023_dps5005.py b/configs/config_mb_2024_0_2__4_mux_2023_dps5005.py
new file mode 100644
index 0000000000000000000000000000000000000000..5d9b5091e868aa1ed100e349ea2cf8d9b8964052
--- /dev/null
+++ b/configs/config_mb_2024_0_2__4_mux_2023_dps5005.py
@@ -0,0 +1,137 @@
+import logging
+from ohmpi.utils import get_platform
+from paho.mqtt.client import MQTTv31  # noqa
+
+_, on_pi = get_platform()
+# DEFINE THE ID OF YOUR OhmPi
+ohmpi_id = '0001' if on_pi else 'XXXX'
+# DEFINE YOUR MQTT BROKER (DEFAULT: 'localhost')
+mqtt_broker = 'localhost' if on_pi else 'NAME_YOUR_BROKER_WHEN_IN_SIMULATION_MODE_HERE'
+# DEFINE THE SUFFIX TO ADD TO YOUR LOGS FILES
+logging_suffix = ''
+
+# OhmPi configuration
+OHMPI_CONFIG = {
+    'id': ohmpi_id,  # Unique identifier of the OhmPi board (string)
+    'settings': 'ohmpi_settings.json',  # INSERT YOUR FAVORITE SETTINGS FILE HERE
+}
+
+HARDWARE_CONFIG = {
+    'ctl': {'model': 'raspberry_pi'},
+    'pwr': {'model': 'pwr_dps5005', 'voltage': 3., 'interface_name': 'modbus'},
+    'tx':  {'model': 'mb_2024_0_2',
+             'voltage_max': 50.,  # Maximum voltage supported by the TX board [V]
+             'current_max': 4800,  # Maximum voltage read by the current ADC on the TX board [mA]
+             'r_shunt': 2,  # Shunt resistance in Ohms
+             'interface_name': 'i2c'
+            },
+    'rx':  {'model': 'mb_2024_0_2',
+             'coef_p2': 1.00,  # slope for conversion for ADS, measurement in V/V
+             'latency': 0.010,  # latency in seconds in continuous mode
+             'sampling_rate': 50,  # number of samples per second
+             'interface_name': 'i2c'
+            },
+    'mux': {'boards':
+                {'mux_A':
+                     {'model': 'mux_2023_0_X',
+                      'mux_tca_address': 0x70,
+                      'roles': {'A': 'X'},
+                      'cabling': {(i, j): ('mux_A', i) for j in ['A'] for i in range(1, 65)},
+                      'voltage_max': 12.},
+                 'mux_B':
+                     {'model': 'mux_2023_0_X',
+                      'mux_tca_address': 0x71,
+                      'roles': {'B': 'X'},
+                      'cabling': {(i, j): ('mux_B', i) for j in ['B'] for i in range(1, 65)},
+                      'voltage_max': 12.},
+                 'mux_M':
+                     {'model': 'mux_2023_0_X',
+                      'mux_tca_address': 0x72,
+                      'roles': {'M': 'X'},
+                      'cabling': {(i, j): ('mux_M', i) for j in ['M'] for i in range(1, 65)},
+                      'voltage_max': 12.},
+                'mux_N':
+                     {'model': 'mux_2023_0_X',
+                      'mux_tca_address': 0x73,
+                      'roles': {'N': 'X'},
+                      'cabling': {(i, j): ('mux_N', i) for j in ['N'] for i in range(1, 65)},
+                      'voltage_max': 12.},
+                 },
+             'default': {'interface_name': 'i2c_ext',
+                         'voltage_max': 100.,
+                         'current_max': 3.}
+            }
+    }
+
+# SET THE LOGGING LEVELS, MQTT BROKERS AND MQTT OPTIONS ACCORDING TO YOUR NEEDS
+# Execution logging configuration
+EXEC_LOGGING_CONFIG = {
+    'logging_level': logging.INFO,
+    'log_file_logging_level': logging.DEBUG,
+    'logging_to_console': True,
+    'file_name': f'exec{logging_suffix}.log',
+    'max_bytes': 262144,
+    'backup_count': 30,
+    'when': 'd',
+    'interval': 1
+}
+
+# Data logging configuration
+DATA_LOGGING_CONFIG = {
+    'logging_level': logging.INFO,
+    'logging_to_console': True,
+    'file_name': f'data{logging_suffix}.log',
+    'max_bytes': 16777216,
+    'backup_count': 1024,
+    'when': 'd',
+    'interval': 1
+}
+
+# State of Health logging configuration (For a future release)
+SOH_LOGGING_CONFIG = {
+    'logging_level': logging.INFO,
+    'logging_to_console': True,
+    'log_file_logging_level': logging.DEBUG,
+    'file_name': f'soh{logging_suffix}.log',
+    'max_bytes': 16777216,
+    'backup_count': 1024,
+    'when': 'd',
+    'interval': 1
+}
+
+# MQTT logging configuration parameters
+MQTT_LOGGING_CONFIG = {
+    'hostname': mqtt_broker,
+    'port': 1883,
+    'qos': 2,
+    'retain': False,
+    'keepalive': 60,
+    'will': None,
+    'auth': {'username': 'mqtt_user', 'password': 'mqtt_password'},
+    'tls': None,
+    'protocol': MQTTv31,
+    'transport': 'tcp',
+    'client_id': f'{OHMPI_CONFIG["id"]}',
+    'exec_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/exec',
+    'exec_logging_level': logging.DEBUG,
+    'data_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/data',
+    'data_logging_level': DATA_LOGGING_CONFIG['logging_level'],
+    'soh_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/soh',
+    'soh_logging_level': SOH_LOGGING_CONFIG['logging_level']
+}
+
+# MQTT control configuration parameters
+MQTT_CONTROL_CONFIG = {
+    'hostname': mqtt_broker,
+    'port': 1883,
+    'qos': 2,
+    'retain': False,
+    'keepalive': 60,
+    'will': None,
+    'auth': {'username': 'mqtt_user', 'password': 'mqtt_password'},
+    'tls': None,
+    'protocol': MQTTv31,
+    'transport': 'tcp',
+    'client_id': f'{OHMPI_CONFIG["id"]}',
+    'ctrl_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/ctrl'
+}
diff --git a/configs/config_mb_2024_0_2__4_mux_2024_dps5005.py b/configs/config_mb_2024_0_2__4_mux_2024_dps5005.py
new file mode 100644
index 0000000000000000000000000000000000000000..4fd7c4c0c8d55fe865591568382a533566ce3136
--- /dev/null
+++ b/configs/config_mb_2024_0_2__4_mux_2024_dps5005.py
@@ -0,0 +1,153 @@
+import logging
+from ohmpi.utils import get_platform
+from paho.mqtt.client import MQTTv31  # noqa
+
+_, on_pi = get_platform()
+# DEFINE THE ID OF YOUR OhmPi
+ohmpi_id = '0001' if on_pi else 'XXXX'
+# DEFINE YOUR MQTT BROKER (DEFAULT: 'localhost')
+mqtt_broker = 'localhost' if on_pi else 'NAME_YOUR_BROKER_WHEN_IN_SIMULATION_MODE_HERE'
+# DEFINE THE SUFFIX TO ADD TO YOUR LOGS FILES
+logging_suffix = ''
+
+# OhmPi configuration
+OHMPI_CONFIG = {
+    'id': ohmpi_id,  # Unique identifier of the OhmPi board (string)
+    'settings': 'ohmpi_settings.json',  # INSERT YOUR FAVORITE SETTINGS FILE HERE
+}
+
+HARDWARE_CONFIG = {
+    'ctl': {'model': 'raspberry_pi'},
+    'pwr': {'model': 'pwr_dps5005', 'voltage': 3., 'interface_name': 'modbus'},
+    'tx':  {'model': 'mb_2024_0_2',
+             'voltage_max': 50.,  # Maximum voltage supported by the TX board [V]
+             'current_max': 4800,  # Maximum voltage read by the current ADC on the TX board [mA]
+             'r_shunt': 2,  # Shunt resistance in Ohms
+             'interface_name': 'i2c'
+            },
+    'rx':  {'model': 'mb_2024_0_2',
+             'coef_p2': 1.00,  # slope for conversion for ADS, measurement in V/V
+             'latency': 0.010,  # latency in seconds in continuous mode
+             'sampling_rate': 50,  # number of samples per second
+             'interface_name': 'i2c'
+            },
+    'mux': {'boards':
+                {'mux_02':
+                     {'model': 'mux_2024_0_X',
+                      'tca_address': None,
+                      'tca_channel': 0,
+                      'addr2': 'up',
+                      'addr1': 'up',
+                      # 'mcp_0': '0x26',
+                      # 'mcp_1': '0x27',
+                      'roles': {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'},
+                      'cabling': {(i+8, j): ('mux_02', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)},
+                      'voltage_max': 12.},
+                'mux_03':
+                     {'model': 'mux_2024_0_X',
+                      'tca_address': None,
+                      'tca_channel': 0,
+                      'addr2': 'down',
+                      'addr1': 'up',
+                      # 'mcp_0': '0x26',
+                      # 'mcp_1': '0x27',
+                      'roles': {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'},
+                      'cabling': {(i+24, j): ('mux_03', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)},
+                      'voltage_max': 12.},
+                 'mux_05':
+                     {'model': 'mux_2024_0_X',
+                      'tca_address': None,
+                      'tca_channel': 0,
+                      'addr2': 'up',
+                      'addr1': 'down',
+                      'roles': {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'},
+                      'cabling': {(i+0, j): ('mux_05', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)},
+                      'voltage_max': 12.},
+                'mux_06':
+                     {'model': 'mux_2024_0_X',
+                      'tca_address': None,
+                      'tca_channel': 0,
+                      'addr2': 'down',
+                      'addr1': 'down',
+                      'roles': {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'},
+                      'cabling': {(i+16, j): ('mux_06', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)},
+                      'voltage_max': 12.},
+                 },
+             'default': {'interface_name': 'i2c_ext',
+                         'voltage_max': 100.,
+                         'current_max': 3.}
+            }
+    }
+
+# SET THE LOGGING LEVELS, MQTT BROKERS AND MQTT OPTIONS ACCORDING TO YOUR NEEDS
+# Execution logging configuration
+EXEC_LOGGING_CONFIG = {
+    'logging_level': logging.INFO,
+    'log_file_logging_level': logging.DEBUG,
+    'logging_to_console': True,
+    'file_name': f'exec{logging_suffix}.log',
+    'max_bytes': 262144,
+    'backup_count': 30,
+    'when': 'd',
+    'interval': 1
+}
+
+# Data logging configuration
+DATA_LOGGING_CONFIG = {
+    'logging_level': logging.INFO,
+    'logging_to_console': True,
+    'file_name': f'data{logging_suffix}.log',
+    'max_bytes': 16777216,
+    'backup_count': 1024,
+    'when': 'd',
+    'interval': 1
+}
+
+# State of Health logging configuration (For a future release)
+SOH_LOGGING_CONFIG = {
+    'logging_level': logging.INFO,
+    'logging_to_console': True,
+    'log_file_logging_level': logging.DEBUG,
+    'file_name': f'soh{logging_suffix}.log',
+    'max_bytes': 16777216,
+    'backup_count': 1024,
+    'when': 'd',
+    'interval': 1
+}
+
+# MQTT logging configuration parameters
+MQTT_LOGGING_CONFIG = {
+    'hostname': mqtt_broker,
+    'port': 1883,
+    'qos': 2,
+    'retain': False,
+    'keepalive': 60,
+    'will': None,
+    'auth': {'username': 'mqtt_user', 'password': 'mqtt_password'},
+    'tls': None,
+    'protocol': MQTTv31,
+    'transport': 'tcp',
+    'client_id': f'{OHMPI_CONFIG["id"]}',
+    'exec_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/exec',
+    'exec_logging_level': logging.DEBUG,
+    'data_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/data',
+    'data_logging_level': DATA_LOGGING_CONFIG['logging_level'],
+    'soh_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/soh',
+    'soh_logging_level': SOH_LOGGING_CONFIG['logging_level']
+}
+
+# MQTT control configuration parameters
+MQTT_CONTROL_CONFIG = {
+    'hostname': mqtt_broker,
+    'port': 1883,
+    'qos': 2,
+    'retain': False,
+    'keepalive': 60,
+    'will': None,
+    'auth': {'username': 'mqtt_user', 'password': 'mqtt_password'},
+    'tls': None,
+    'protocol': MQTTv31,
+    'transport': 'tcp',
+    'client_id': f'{OHMPI_CONFIG["id"]}',
+    'ctrl_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/ctrl'
+}
diff --git a/configure_second_I2C_bus.sh b/configure_second_I2C_bus.sh
new file mode 100644
index 0000000000000000000000000000000000000000..43e8b4509f750f3fb975bdec257e47c00b62bd92
--- /dev/null
+++ b/configure_second_I2C_bus.sh
@@ -0,0 +1,2 @@
+sudo apt-get install -y i2c-tools
+echo -e "[all]\ndtoverlay=i2c-gpio,bus=4,i2c_gpio_delay_us=1,i2c_gpio_sda=22,i2c_gpio_scl=23" | sudo tee -a /boot/config.txt
diff --git a/dev/test_2_mux_2024.py b/dev/test_2_mux_2024.py
deleted file mode 100644
index a033c7199327f94589ceeafe276a654a9280861d..0000000000000000000000000000000000000000
--- a/dev/test_2_mux_2024.py
+++ /dev/null
@@ -1,60 +0,0 @@
-import time
-from ohmpi.utils import change_config
-from ohmpi.plots import plot_exec_log
-import logging
-change_config('../configs/config_mb_2023_2_mux_2024.py', verbose=False)
-from ohmpi.hardware_components.mux_2024_0_X import Mux, MUX_CONFIG
-from ohmpi.hardware_components import raspberry_pi as ctl_module
-# from ohmpi.config import HARDWARE_CONFIG
-
-stand_alone_mux = False
-part_of_hardware_system = False
-within_ohmpi = True
-# Stand alone mux
-if stand_alone_mux:
-    MUX_CONFIG['ctl'] = ctl_module.Ctl()
-    MUX_CONFIG['id'] = 'mux_1'
-    MUX_CONFIG['cabling'] = {(i+8, j) : ('mux_1', i) for j in ['A', 'B', 'M', 'N'] for i in range(1,9)}
-    mux = Mux(**MUX_CONFIG)
-    mux.switch_one(elec=9, role='M', state='on')
-    time.sleep(2)
-    mux.switch_one(elec=9, role='M', state='off')
-    mux.switch({'A': [9], 'B': [12], 'M': [10], 'N': [11]}, state='on')
-    time.sleep(8)
-    # mux.switch({'A': [1], 'B': [4], 'M': [2], 'N': [3]}, state='off')
-    mux.reset()
-    mux.test({'A': [9, 10, 11, 12, 13, 14, 15, 16], 'B': [9, 10, 11, 12, 13, 14, 15, 16],
-              'M': [9, 10, 11, 12, 13, 14, 15, 16], 'N': [9, 10, 11, 12, 13, 14, 15, 16]}, activation_time=.1)
-
-# mux as part of a OhmPiHardware system
-if part_of_hardware_system:
-    from ohmpi.hardware_system import OhmPiHardware
-    print('Starting test of mux as part of a OhmPiHardware system.')
-
-    k = OhmPiHardware()
-    k.exec_logger.setLevel(logging.DEBUG)
-
-    # Test mux switching
-    k.reset_mux()
-    k.switch_mux(electrodes=[1, 4, 2, 3], roles=['A', 'B', 'M', 'N'], state='on')
-    time.sleep(1.)
-    k.switch_mux(electrodes=[1, 4, 2, 3], roles=['A', 'B', 'M', 'N'], state='off')
-
-if within_ohmpi:
-    from ohmpi.ohmpi import OhmPi
-    print('Starting test of mux within OhmPi.')
-    k = OhmPi()
-    k.reset_mux()
-    k._hw.switch_mux([9, 12, 10, 11], state='on')
-    k._hw.vab_square_wave(3.,1)
-    k._hw.switch_mux([9, 12, 10, 11], state='off')
-    k._hw.calibrate_rx_bias()  # electrodes 1 4 2 3 should be connected to a reference circuit
-    # print(f'Resistance: {k._hw.last_rho :.2f} ohm, dev. {k._hw.last_dev:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV')
-    # k._hw._plot_readings()
-    k._hw.switch_mux([9, 12, 10, 11], state='on')
-    k._hw.vab_square_wave(3.,1)
-    k._hw.switch_mux([9, 12, 10, 11], state='off')
-    print(f'Resistance: {k._hw.last_rho :.2f} ohm, dev. {k._hw.last_dev:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV')
-    k._hw._plot_readings()
-    plot_exec_log('ohmpi/logs/exec.log')
-change_config('../configs/config_default.py', verbose=False)
\ No newline at end of file
diff --git a/dev/test_4_mux_2024.py b/dev/test_4_mux_2024.py
deleted file mode 100644
index 1798a59c8757e118c3402f7c679399146d299449..0000000000000000000000000000000000000000
--- a/dev/test_4_mux_2024.py
+++ /dev/null
@@ -1,65 +0,0 @@
-import time
-from ohmpi.utils import change_config
-from ohmpi.plots import plot_exec_log
-import logging
-change_config('../configs/config_mb_2023_4_mux_2024.py', verbose=False)
-from ohmpi.hardware_components.mux_2024_0_X import Mux, MUX_CONFIG
-from ohmpi.hardware_components import raspberry_pi as ctl_module
-from ohmpi.config import HARDWARE_CONFIG
-
-stand_alone_mux = False
-part_of_hardware_system = True
-within_ohmpi = False
-
-
-# Stand alone mux
-if stand_alone_mux:
-    mux_id = 'mux_05'
-    first = 8
-    print(MUX_CONFIG)
-    MUX_CONFIG.update(HARDWARE_CONFIG['mux']['boards'][mux_id])
-    MUX_CONFIG.update({'id': mux_id})
-    MUX_CONFIG['ctl'] = ctl_module.Ctl()
-    mux = Mux(**MUX_CONFIG)
-    mux.switch_one(elec=1+first, role='M', state='on')
-    time.sleep(2)
-    mux.switch_one(elec=1+first, role='M', state='off')
-    mux.switch({'A': [1], 'B': [2], 'M': [3], 'N': [4]}, state='on')
-    time.sleep(8)
-    # mux.switch({'A': [1], 'B': [4], 'M': [2], 'N': [3]}, state='off')
-    mux.reset()
-    mux.test({'A': [i+first for i in range(1, 9)], 'B': [i+first for i in range(1, 9)],
-              'M': [i+first for i in range(1, 9)], 'N': [i+first for i in range(1, 9)]}, activation_time=.1)
-
-# mux as part of a OhmPiHardware system
-if part_of_hardware_system:
-    from ohmpi.hardware_system import OhmPiHardware
-    print('Starting test of mux as part of a OhmPiHardware system.')
-
-    k = OhmPiHardware()
-    k.exec_logger.setLevel(logging.DEBUG)
-
-    # Test mux switching
-    k.reset_mux()
-    k.switch_mux(electrodes=[1, 4, 2, 3], roles=['A', 'B', 'M', 'N'], state='on')
-    time.sleep(1.)
-    k.switch_mux(electrodes=[1, 4, 2, 3], roles=['A', 'B', 'M', 'N'], state='off')
-
-if within_ohmpi:
-    from ohmpi.ohmpi import OhmPi
-    print('Starting test of mux within OhmPi.')
-    k = OhmPi()
-    k.reset_mux()
-    k._hw.switch_mux([1, 4, 2, 3], state='on')
-    k._hw.vab_square_wave(3.,1)
-    k._hw.switch_mux([1, 4, 2, 3], state='off')
-    k._hw.calibrate_rx_bias()  # electrodes 1 4 2 3 should be connected to a reference circuit
-    # print(f'Resistance: {k._hw.last_rho :.2f} ohm, dev. {k._hw.last_dev:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV')
-    # k._hw._plot_readings()
-    k._hw.switch_mux([1, 4, 2, 3], state='on')
-    k._hw.vab_square_wave(3.,1)
-    k._hw.switch_mux([1, 4, 2, 3], state='off')
-    print(f'Resistance: {k._hw.last_rho :.2f} ohm, dev. {k._hw.last_dev:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV')
-    k._hw._plot_readings()
-    plot_exec_log('ohmpi/logs/exec.log')
-change_config('../configs/config_default.py', verbose=False)
\ No newline at end of file
diff --git a/dev/test_fullwave.py b/dev/test_fullwave.py
deleted file mode 100644
index 6d9eb03ca3b5b34a49ec4a484e01ee4b1636b6fa..0000000000000000000000000000000000000000
--- a/dev/test_fullwave.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import numpy as np
-import pandas as pd
-import matplotlib.pyplot as plt
-import os
-
-fnames = sorted(os.listdir('data'))
-df = pd.read_csv('out.csv', engine='python')
-df
-
-ct = df.columns[df.columns.str.match('t\d+')]
-ci = df.columns[df.columns.str.match('i\d+')]
-cu = df.columns[df.columns.str.match('u\d+')]
-
-fig, axs = plt.subplots(2, 1, sharex=True)
-for i in range(df.shape[0]):
-    print(i)
-    data = np.c_[df.loc[i, ct], df.loc[i, ci], df.loc[i, cu]].astype(float)
-    inan = ~(np.isnan(data).any(1))
-    label = ', '.join(df.loc[i, ['A', 'B', 'M', 'N']].astype(str).to_list())
-    ax = axs[0]
-    ax.plot(data[inan,0], data[inan,1], '.-', label=label)
-    ax.set_ylabel('Current AB [mA]')
-    ax.legend()
-    ax = axs[1]
-    ax.plot(data[inan,0], data[inan,2], '.-', label=label)
-    ax.set_ylabel('Voltage MN [mV]')
-    ax.set_xlabel('Time [s]')
-plt.show()
diff --git a/dev/test_inv.py b/dev/test_inv.py
new file mode 100644
index 0000000000000000000000000000000000000000..b212d585b5f0fa9156a9b84fce37ddf10f95d479
--- /dev/null
+++ b/dev/test_inv.py
@@ -0,0 +1,12 @@
+
+#
+from ohmpi.utils import change_config
+change_config('../configs/config_mb_2023.py', verbose=False)
+
+from ohmpi.ohmpi import OhmPi
+k = OhmPi()
+k.run_inversion(['measurement_20220206T194552.csv'])
+
+
+# restore default config
+change_config('../configs/config_default.py', verbose=False)
diff --git a/dev/test_mb_2023_0_X.py b/dev/test_mb_2023_0_X.py
deleted file mode 100644
index 04dfe529d679a698b2137ed915ccdb97009589e6..0000000000000000000000000000000000000000
--- a/dev/test_mb_2023_0_X.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# import sys
-# sys.path.extend(['/home/su530201/PycharmProjects/ohmpi_reversaal/OhmPi'])
-from ohmpi.hardware_components.mb_2023_0_X import Tx
-from ohmpi.hardware_components.mb_2023_0_X import Rx
-from ohmpi.logging_setup import create_stdout_logger
-import numpy as np
-
-exec_logger = create_stdout_logger(name='exec')
-soh_logger = create_stdout_logger(name='soh')
-
-print('\nCreating TX...')
-tx = Tx(exec_logger= exec_logger, soh_logger= soh_logger)
-print('\nCreating RX...')
-rx = Rx(exec_logger= exec_logger, soh_logger= soh_logger)
-
-print(f'TX current: {tx.current:.3f} mA')
-print(f'RX voltage: {rx.voltage:.3f} mV')
-
-tx.inject(state='on', polarity=1)
-tx.adc_gain_auto()
-rx.adc_gain_auto()
-r = []
-for i in range(30):
-    r.append(rx.voltage/tx.current)
-    print(f'Resistance: {r[-1]:.3f}')
-r = np.array(r)
-print(f'Mean resistance: {np.mean(r):.3f} Ohms')
-print(f'Resistance std: {np.std(r):.3f} Ohms')
-print(f'Dev. {100. * np.std(r)/np.mean(r):.1} %')
diff --git a/dev/test_mb_2023_0_mux.py b/dev/test_mb_2023_0_mux.py
new file mode 100644
index 0000000000000000000000000000000000000000..4dfafdf74eeb36d7368fdfd1c67018929b840e5f
--- /dev/null
+++ b/dev/test_mb_2023_0_mux.py
@@ -0,0 +1,117 @@
+import matplotlib
+matplotlib.use('TkAgg')
+from ohmpi.utils import change_config
+change_config('../configs/config_mb_2023.py', verbose=False)
+import importlib
+import time
+import logging
+from ohmpi.config import HARDWARE_CONFIG
+
+stand_alone = False
+part_of_hardware_system = False
+within_ohmpi = True
+
+# Stand alone
+if stand_alone:
+
+    ctl_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["ctl"].pop("model")}')
+    pwr_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["pwr"].pop("model")}')
+    tx_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["tx"].pop("model")}')
+    rx_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["rx"].pop("model")}')
+
+    ctl = ctl_module.Ctl()
+    HARDWARE_CONFIG['tx'].update({'ctl': ctl, 'exec_logger': ctl.exec_logger, 'soh_logger': ctl.soh_logger})
+    HARDWARE_CONFIG['rx'].update({'ctl': ctl, 'exec_logger': ctl.exec_logger, 'soh_logger': ctl.soh_logger})
+    HARDWARE_CONFIG['tx'].update({'connection': HARDWARE_CONFIG['tx'].pop('connection',
+                                                                          ctl.interfaces[
+                                                                              HARDWARE_CONFIG['tx'].pop(
+                                                                                  'interface_name', 'i2c')])})
+    HARDWARE_CONFIG['rx'].update({'connection': HARDWARE_CONFIG['rx'].pop('connection',
+                                                                          ctl.interfaces[
+                                                                              HARDWARE_CONFIG['rx'].pop(
+                                                                                  'interface_name', 'i2c')])})
+
+    HARDWARE_CONFIG['pwr'].update({'connection': HARDWARE_CONFIG['pwr'].pop('connection',
+                                                                          ctl.interfaces[
+                                                                              HARDWARE_CONFIG['pwr'].pop(
+                                                                                  'interface_name', None)])})
+
+
+    rx = rx_module.Rx(**HARDWARE_CONFIG['rx'])
+    tx = tx_module.Tx(**HARDWARE_CONFIG['tx'])
+    pwr = pwr_module.Pwr(**HARDWARE_CONFIG['pwr'])
+    
+    # mux_ids = ['mux_02', 'mux_05']
+    # for m,mux_id in enumerate(mux_ids):
+    #     mux_module = importlib.import_module(
+    #         f'ohmpi.hardware_components.{HARDWARE_CONFIG["mux"]["boards"][mux_id].pop("model")}')
+
+    #     MUX_CONFIG = HARDWARE_CONFIG['mux']['boards'][mux_id]
+
+    #     MUX_CONFIG.update({'ctl': ctl, 'connection': MUX_CONFIG.pop('connection', ctl.interfaces[
+    #                                        MUX_CONFIG.pop('interface_name', 'i2c_ext')]), 'exec_logger': ctl.exec_logger,
+    #                    'soh_logger': ctl.soh_logger})
+    #     MUX_CONFIG.update({'id': mux_id})
+    #     mux = mux_module.Mux(**MUX_CONFIG)
+
+    #     # tx.polarity = 1
+    #     # time.sleep(1)
+    #     # tx.polarity = 0
+    #     # mux.switch(elec_dict={'A': [1], 'B': [4], 'M': [2], 'N': [3]}, state='on')
+    #     # time.sleep(1)
+    #     # voltage = rx.voltage
+    #     # current = tx.current
+    #     # mux.switch(elec_dict={'A': [1], 'B': [4], 'M': [2], 'N': [3]}, state='off')
+    #     # print(f'Resistance: {voltage / current :.2f} ohm, voltage: {voltage:.2f} mV, current: {current:.2f} mA')
+    #     mux.reset()
+    #     mux.test({'A': [i+8*m for i in range(1, 9)], 'B': [i+8*m for i in range(1, 9)],
+    #               'M': [i+8*m for i in range(1, 9)], 'N': [i+8*m for i in range(1, 9)]}, activation_time=.1)
+    #     mux.reset()
+
+# mux as part of a OhmPiHardware system
+if part_of_hardware_system:
+    from ohmpi.hardware_system import OhmPiHardware
+    print('Starting test of as part of an OhmPiHardware system.')
+    # mux_id = 'mux_03'
+    k = OhmPiHardware()
+    k.exec_logger.setLevel(logging.DEBUG)
+    # Test mux switching
+    k.reset_mux()
+    # k.switch_mux(electrodes=[1, 4, 2, 3], roles=['A', 'B', 'M', 'N'], state='on')
+    # time.sleep(1.)
+    # k.switch_mux(electrodes=[1, 4, 2, 3], roles=['A', 'B', 'M', 'N'], state='off')
+    # k.mux_boards[mux_id].test(activation_time=.4)
+    k.test_mux()
+    k.reset_mux()
+
+if within_ohmpi:
+    from ohmpi.ohmpi import OhmPi
+    # from ohmpi.plots import plot_exec_log
+
+    print('Starting test with OhmPi.')
+    k = OhmPi()
+    # A, B, M, N = (32, 29, 31, 30)
+    k.reset_mux()
+    # k.test_mux(mux_id='mux_03')
+    # k._hw.switch_mux([A, B, M, N], state='on')
+    # k._hw.vab_square_wave(12.,1., cycles=2)
+    # k._hw.switch_mux([A, B, M, N], state='off')
+    # k._hw.calibrate_rx_bias()  # electrodes 1 4 2 3 should be connected to a reference circuit
+    # k._hw.rx._bias = -1.38
+    # print(f'Resistance: {k._hw.last_rho :.2f} ohm, dev. {k._hw.last_dev:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV')
+    # k._hw._plot_readings()
+    A, B, M, N = (0, 0, 0, 0)
+    # k._hw.switch_mux([A, B, M, N], state='on')
+    # k._hw.vab_square_wave(12., cycle_duration=10., cycles=3)
+    # k._hw.switch_mux([A, B, M, N], state='off')
+    # print(f'OhmPiHardware Resistance: {k._hw.last_rho :.2f} ohm, dev. {k._hw.last_dev:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV')
+    # k._hw._plot_readings()
+    print('using OhmPi')
+    d = k.run_measurement([A, B, M, N], injection_duration=1., nb_stack=2, duty_cycle=0.5)
+    print(d)
+    # k._hw._plot_readings()
+    print(f'OhmPiHardware: Resistance: {k._hw.last_resistance() :.2f} ohm, dev. {k._hw.last_dev():.2f} %, sp: {k._hw.sp:.2f} mV, rx bias: {k._hw.rx._bias:.2f} mV')
+    print(f'OhmPi: Resistance: {d["R [ohm]"] :.2f} ohm, dev. {d["R_std [%]"]:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV')
+    k._hw._plot_readings(save_fig=False)
+    # plot_exec_log('ohmpi/logs/exec.log')
+change_config('../configs/config_default.py', verbose=False)
diff --git a/dev/test_3_mux_2024.py b/dev/test_mb_2023_3_mux_2024.py
similarity index 98%
rename from dev/test_3_mux_2024.py
rename to dev/test_mb_2023_3_mux_2024.py
index 8410aa75f731f30a36f346eb6f585b99663fad93..7851dff3cfc86546e4ce88a18db79ea2e27f74bd 100644
--- a/dev/test_3_mux_2024.py
+++ b/dev/test_mb_2023_3_mux_2024.py
@@ -58,7 +58,7 @@ if within_ohmpi:
     #k._hw.rx._bias = -1.38
     #print(f'Resistance: {k._hw.last_rho :.2f} ohm, dev. {k._hw.last_dev:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV')
     # k._hw._plot_readings()
-    A, B, M, N = (28, 25, 27, 26)
+    A, B, M, N = (1, 4, 2, 3)
     # k._hw.switch_mux([A, B, M, N], state='on')
     # k._hw.vab_square_wave(12., cycle_duration=10., cycles=3)
     # k._hw.switch_mux([A, B, M, N], state='off')
diff --git a/dev/test_mb_2024_0_mux_2024.py b/dev/test_mb_2024_0_mux_2024.py
new file mode 100644
index 0000000000000000000000000000000000000000..0f781ec843cf75d4ffdc8c8f641b5b18a29b55b4
--- /dev/null
+++ b/dev/test_mb_2024_0_mux_2024.py
@@ -0,0 +1,83 @@
+import matplotlib
+matplotlib.use('TkAgg')
+from ohmpi.utils import change_config
+change_config('../configs/config_mb_2024_0_2.py', verbose=False)
+import importlib
+import time
+import logging
+from ohmpi.config import HARDWARE_CONFIG
+
+stand_alone = False
+part_of_hardware_system = False
+within_ohmpi = True
+
+# Stand alone
+if stand_alone:
+    ctl_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["ctl"].pop("model")}')
+    pwr_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["pwr"].pop("model")}')
+    tx_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["tx"].pop("model")}')
+    rx_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["rx"].pop("model")}')
+
+    ctl = ctl_module.Ctl()
+    HARDWARE_CONFIG['tx'].update({'ctl': ctl})  # HARDWARE_CONFIG['tx'].pop('ctl', ctl_module.Ctl())})
+    HARDWARE_CONFIG['rx'].update({'ctl': ctl})  # HARDWARE_CONFIG['rx'].pop('ctl', ctl_module.Ctl())})
+    HARDWARE_CONFIG['tx'].update({'connection': HARDWARE_CONFIG['tx'].pop('connection',
+                                                                          ctl.interfaces[
+                                                                              HARDWARE_CONFIG['tx'].pop(
+                                                                                  'interface_name', 'i2c')])})
+    HARDWARE_CONFIG['rx'].update({'connection': HARDWARE_CONFIG['rx'].pop('connection',
+                                                                          ctl.interfaces[
+                                                                              HARDWARE_CONFIG['rx'].pop(
+                                                                                  'interface_name', 'i2c')])})
+
+    rx = rx_module.Rx(**HARDWARE_CONFIG['rx'])
+    tx = tx_module.Tx(**HARDWARE_CONFIG['tx'])
+    pwr = pwr_module.Pwr(**HARDWARE_CONFIG['pwr'])
+
+    tx.polarity = 1
+    time.sleep(1)
+    tx.polarity = 0
+
+# mux as part of a OhmPiHardware system
+if part_of_hardware_system:
+    from ohmpi.hardware_system import OhmPiHardware
+    print('Starting test of as part of an OhmPiHardware system.')
+
+    k = OhmPiHardware()
+    k.exec_logger.setLevel(logging.DEBUG)
+    # Test mux switching
+    k.reset_mux()
+    k.switch_mux(electrodes=[1, 4, 2, 3], roles=['A', 'B', 'M', 'N'], state='on')
+    time.sleep(1.)
+    k.switch_mux(electrodes=[1, 4, 2, 3], roles=['A', 'B', 'M', 'N'], state='off')
+
+if within_ohmpi:
+    from ohmpi.ohmpi import OhmPi
+    # from ohmpi.plots import plot_exec_log
+
+    print('Starting test with OhmPi.')
+    k = OhmPi()
+    #A, B, M, N = (32, 29, 31, 30)
+    k.reset_mux()
+    #k._hw.switch_mux([A, B, M, N], state='on')
+    #k._hw.vab_square_wave(12.,1., cycles=2)
+    #k._hw.switch_mux([A, B, M, N], state='off')
+    #k._hw.calibrate_rx_bias()  # electrodes 1 4 2 3 should be connected to a reference circuit
+    #k._hw.rx._bias = -1.38
+    #print(f'Resistance: {k._hw.last_rho :.2f} ohm, dev. {k._hw.last_dev:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV')
+    # k._hw._plot_readings()
+    A, B, M, N = (0, 0, 0, 0)
+    # k._hw.switch_mux([A, B, M, N], state='on')
+    # k._hw.vab_square_wave(12., cycle_duration=10., cycles=3)
+    # k._hw.switch_mux([A, B, M, N], state='off')
+    # print(f'OhmPiHardware Resistance: {k._hw.last_rho :.2f} ohm, dev. {k._hw.last_dev:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV')
+    # k._hw._plot_readings()
+    print('using OhmPi')
+    d = k.run_measurement([A, B, M, N], injection_duration=1., nb_stack=2, duty_cycle=0.5)
+    print(d)
+    #k._hw._plot_readings()
+    print(f'OhmPiHardware: Resistance: {k._hw.last_resistance() :.2f} ohm, dev. {k._hw.last_dev():.2f} %, sp: {k._hw.sp:.2f} mV, rx bias: {k._hw.rx._bias:.2f} mV')
+    print(f'OhmPi: Resistance: {d["R [ohm]"] :.2f} ohm, dev. {d["R_std [%]"]:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV')
+    k._hw._plot_readings(save_fig=False)
+    # plot_exec_log('ohmpi/logs/exec.log')
+change_config('../configs/config_default.py', verbose=False)
diff --git a/dev/test_mb_2024_1_mux_2024.py b/dev/test_mb_2024_1_mux_2024.py
new file mode 100644
index 0000000000000000000000000000000000000000..2d086183953fa43503ca156c11ad7b6cfa19d09f
--- /dev/null
+++ b/dev/test_mb_2024_1_mux_2024.py
@@ -0,0 +1,111 @@
+import matplotlib
+matplotlib.use('TkAgg')
+from ohmpi.utils import change_config
+change_config('../configs/config_mb_2024_0_2__1_mux_2024_dps5005.py', verbose=False)
+import importlib
+import time
+import logging
+from ohmpi.config import HARDWARE_CONFIG
+
+stand_alone = False
+part_of_hardware_system = True
+within_ohmpi = False
+
+# Stand alone
+if stand_alone:
+    mux_id = 'mux_03'
+    ctl_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["ctl"].pop("model")}')
+    pwr_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["pwr"].pop("model")}')
+    tx_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["tx"].pop("model")}')
+    rx_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["rx"].pop("model")}')
+    mux_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["mux"]["boards"][mux_id].pop("model")}')
+
+    ctl = ctl_module.Ctl()
+    HARDWARE_CONFIG['tx'].update({'ctl': ctl, 'exec_logger': ctl.exec_logger, 'soh_logger': ctl.soh_logger})
+    HARDWARE_CONFIG['rx'].update({'ctl': ctl, 'exec_logger': ctl.exec_logger, 'soh_logger': ctl.soh_logger})
+    HARDWARE_CONFIG['tx'].update({'connection': HARDWARE_CONFIG['tx'].pop('connection',
+                                                                          ctl.interfaces[
+                                                                              HARDWARE_CONFIG['tx'].pop(
+                                                                                  'interface_name', 'i2c')])})
+    HARDWARE_CONFIG['rx'].update({'connection': HARDWARE_CONFIG['rx'].pop('connection',
+                                                                          ctl.interfaces[
+                                                                              HARDWARE_CONFIG['rx'].pop(
+                                                                                  'interface_name', 'i2c')])})
+
+    HARDWARE_CONFIG['pwr'].update({'connection': HARDWARE_CONFIG['pwr'].pop('connection',
+                                                                          ctl.interfaces[
+                                                                              HARDWARE_CONFIG['pwr'].pop(
+                                                                                  'interface_name', None)])})
+
+    MUX_CONFIG = HARDWARE_CONFIG['mux']['boards'][mux_id]
+    MUX_CONFIG.update({'ctl': ctl, 'connection': MUX_CONFIG.pop('connection', ctl.interfaces[
+                                           MUX_CONFIG.pop('interface_name', 'i2c_ext')]), 'exec_logger': ctl.exec_logger,
+                       'soh_logger': ctl.soh_logger})
+    MUX_CONFIG.update({'id': mux_id})
+
+    rx = rx_module.Rx(**HARDWARE_CONFIG['rx'])
+    tx = tx_module.Tx(**HARDWARE_CONFIG['tx'])
+    pwr = pwr_module.Pwr(**HARDWARE_CONFIG['pwr'])
+    mux = mux_module.Mux(**MUX_CONFIG)
+
+    tx.polarity = 1
+    time.sleep(1)
+    tx.polarity = 0
+    mux.switch(elec_dict={'A': [1], 'B': [4], 'M': [2], 'N': [3]}, state='on')
+    time.sleep(1)
+    voltage = rx.voltage
+    current = tx.current
+    mux.switch(elec_dict={'A': [1], 'B': [4], 'M': [2], 'N': [3]}, state='off')
+    print(f'Resistance: {voltage / current :.2f} ohm, voltage: {voltage:.2f} mV, current: {current:.2f} mA')
+    mux.reset()
+    # mux.test({'A': [i for i in range(1, 9)], 'B': [i for i in range(1, 9)],
+    #           'M': [i for i in range(1, 9)], 'N': [i for i in range(1, 9)]}, activation_time=.1)
+    # mux.reset()
+
+# mux as part of a OhmPiHardware system
+if part_of_hardware_system:
+    from ohmpi.hardware_system import OhmPiHardware
+    print('Starting test of as part of an OhmPiHardware system.')
+    mux_id = 'mux_03'
+    k = OhmPiHardware()
+    k.exec_logger.setLevel(logging.DEBUG)
+    # Test mux switching
+    k.reset_mux()
+    # k.switch_mux(electrodes=[1, 4, 2, 3], roles=['A', 'B', 'M', 'N'], state='on')
+    # time.sleep(1.)
+    # k.switch_mux(electrodes=[1, 4, 2, 3], roles=['A', 'B', 'M', 'N'], state='off')
+    # k.mux_boards[mux_id].test(activation_time=.4)
+    k.test_mux()
+    k.reset_mux()
+
+if within_ohmpi:
+    from ohmpi.ohmpi import OhmPi
+    # from ohmpi.plots import plot_exec_log
+
+    print('Starting test with OhmPi.')
+    k = OhmPi()
+    # A, B, M, N = (32, 29, 31, 30)
+    k.reset_mux()
+    # k.test_mux(mux_id='mux_03')
+    # k._hw.switch_mux([A, B, M, N], state='on')
+    # k._hw.vab_square_wave(12.,1., cycles=2)
+    # k._hw.switch_mux([A, B, M, N], state='off')
+    # k._hw.calibrate_rx_bias()  # electrodes 1 4 2 3 should be connected to a reference circuit
+    # k._hw.rx._bias = -1.38
+    # print(f'Resistance: {k._hw.last_rho :.2f} ohm, dev. {k._hw.last_dev:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV')
+    # k._hw._plot_readings()
+    A, B, M, N = (1, 4, 2, 3)
+    # k._hw.switch_mux([A, B, M, N], state='on')
+    # k._hw.vab_square_wave(12., cycle_duration=10., cycles=3)
+    # k._hw.switch_mux([A, B, M, N], state='off')
+    # print(f'OhmPiHardware Resistance: {k._hw.last_rho :.2f} ohm, dev. {k._hw.last_dev:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV')
+    # k._hw._plot_readings()
+    print('using OhmPi')
+    d = k.run_measurement([A, B, M, N], injection_duration=1., nb_stack=2, duty_cycle=0.5)
+    print(d)
+    # k._hw._plot_readings()
+    print(f'OhmPiHardware: Resistance: {k._hw.last_resistance() :.2f} ohm, dev. {k._hw.last_dev():.2f} %, sp: {k._hw.sp:.2f} mV, rx bias: {k._hw.rx._bias:.2f} mV')
+    print(f'OhmPi: Resistance: {d["R [ohm]"] :.2f} ohm, dev. {d["R_std [%]"]:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV')
+    k._hw._plot_readings(save_fig=False)
+    # plot_exec_log('ohmpi/logs/exec.log')
+change_config('../configs/config_default.py', verbose=False)
diff --git a/dev/test_mb_2024_2_mux_2024.py b/dev/test_mb_2024_2_mux_2024.py
new file mode 100644
index 0000000000000000000000000000000000000000..9792e6c62bef60c4ecee59508112d264d807ecbf
--- /dev/null
+++ b/dev/test_mb_2024_2_mux_2024.py
@@ -0,0 +1,116 @@
+import matplotlib
+matplotlib.use('TkAgg')
+from ohmpi.utils import change_config
+change_config('../configs/config_mb_2024_0_2__2_mux_2024_dps5005.py', verbose=False)
+import importlib
+import time
+import logging
+from ohmpi.config import HARDWARE_CONFIG
+
+stand_alone = False
+part_of_hardware_system = True
+within_ohmpi = False
+
+# Stand alone
+if stand_alone:
+
+    ctl_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["ctl"].pop("model")}')
+    pwr_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["pwr"].pop("model")}')
+    tx_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["tx"].pop("model")}')
+    rx_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["rx"].pop("model")}')
+
+    ctl = ctl_module.Ctl()
+    HARDWARE_CONFIG['tx'].update({'ctl': ctl, 'exec_logger': ctl.exec_logger, 'soh_logger': ctl.soh_logger})
+    HARDWARE_CONFIG['rx'].update({'ctl': ctl, 'exec_logger': ctl.exec_logger, 'soh_logger': ctl.soh_logger})
+    HARDWARE_CONFIG['tx'].update({'connection': HARDWARE_CONFIG['tx'].pop('connection',
+                                                                          ctl.interfaces[
+                                                                              HARDWARE_CONFIG['tx'].pop(
+                                                                                  'interface_name', 'i2c')])})
+    HARDWARE_CONFIG['rx'].update({'connection': HARDWARE_CONFIG['rx'].pop('connection',
+                                                                          ctl.interfaces[
+                                                                              HARDWARE_CONFIG['rx'].pop(
+                                                                                  'interface_name', 'i2c')])})
+
+    HARDWARE_CONFIG['pwr'].update({'connection': HARDWARE_CONFIG['pwr'].pop('connection',
+                                                                          ctl.interfaces[
+                                                                              HARDWARE_CONFIG['pwr'].pop(
+                                                                                  'interface_name', None)])})
+
+
+    rx = rx_module.Rx(**HARDWARE_CONFIG['rx'])
+    tx = tx_module.Tx(**HARDWARE_CONFIG['tx'])
+    pwr = pwr_module.Pwr(**HARDWARE_CONFIG['pwr'])
+    mux_ids = ['mux_02', 'mux_05']
+    for m,mux_id in enumerate(mux_ids):
+        mux_module = importlib.import_module(
+            f'ohmpi.hardware_components.{HARDWARE_CONFIG["mux"]["boards"][mux_id].pop("model")}')
+
+        MUX_CONFIG = HARDWARE_CONFIG['mux']['boards'][mux_id]
+
+        MUX_CONFIG.update({'ctl': ctl, 'connection': MUX_CONFIG.pop('connection', ctl.interfaces[
+                                           MUX_CONFIG.pop('interface_name', 'i2c_ext')]), 'exec_logger': ctl.exec_logger,
+                       'soh_logger': ctl.soh_logger})
+        MUX_CONFIG.update({'id': mux_id})
+        mux = mux_module.Mux(**MUX_CONFIG)
+
+        # tx.polarity = 1
+        # time.sleep(1)
+        # tx.polarity = 0
+        # mux.switch(elec_dict={'A': [1], 'B': [4], 'M': [2], 'N': [3]}, state='on')
+        # time.sleep(1)
+        # voltage = rx.voltage
+        # current = tx.current
+        # mux.switch(elec_dict={'A': [1], 'B': [4], 'M': [2], 'N': [3]}, state='off')
+        # print(f'Resistance: {voltage / current :.2f} ohm, voltage: {voltage:.2f} mV, current: {current:.2f} mA')
+        mux.reset()
+        mux.test({'A': [i+8*m for i in range(1, 9)], 'B': [i+8*m for i in range(1, 9)],
+                  'M': [i+8*m for i in range(1, 9)], 'N': [i+8*m for i in range(1, 9)]}, activation_time=.1)
+        mux.reset()
+
+# mux as part of a OhmPiHardware system
+if part_of_hardware_system:
+    from ohmpi.hardware_system import OhmPiHardware
+    print('Starting test of as part of an OhmPiHardware system.')
+    # mux_id = 'mux_03'
+    k = OhmPiHardware()
+    k.exec_logger.setLevel(logging.DEBUG)
+    # Test mux switching
+    k.reset_mux()
+    # k.switch_mux(electrodes=[1, 4, 2, 3], roles=['A', 'B', 'M', 'N'], state='on')
+    # time.sleep(1.)
+    # k.switch_mux(electrodes=[1, 4, 2, 3], roles=['A', 'B', 'M', 'N'], state='off')
+    # k.mux_boards[mux_id].test(activation_time=.4)
+    k.test_mux()
+    k.reset_mux()
+
+if within_ohmpi:
+    from ohmpi.ohmpi import OhmPi
+    # from ohmpi.plots import plot_exec_log
+
+    print('Starting test with OhmPi.')
+    k = OhmPi()
+    # A, B, M, N = (32, 29, 31, 30)
+    k.reset_mux()
+    # k.test_mux(mux_id='mux_03')
+    # k._hw.switch_mux([A, B, M, N], state='on')
+    # k._hw.vab_square_wave(12.,1., cycles=2)
+    # k._hw.switch_mux([A, B, M, N], state='off')
+    # k._hw.calibrate_rx_bias()  # electrodes 1 4 2 3 should be connected to a reference circuit
+    # k._hw.rx._bias = -1.38
+    # print(f'Resistance: {k._hw.last_rho :.2f} ohm, dev. {k._hw.last_dev:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV')
+    # k._hw._plot_readings()
+    A, B, M, N = (1, 4, 2, 3)
+    # k._hw.switch_mux([A, B, M, N], state='on')
+    # k._hw.vab_square_wave(12., cycle_duration=10., cycles=3)
+    # k._hw.switch_mux([A, B, M, N], state='off')
+    # print(f'OhmPiHardware Resistance: {k._hw.last_rho :.2f} ohm, dev. {k._hw.last_dev:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV')
+    # k._hw._plot_readings()
+    print('using OhmPi')
+    d = k.run_measurement([A, B, M, N], injection_duration=1., nb_stack=2, duty_cycle=0.5)
+    print(d)
+    # k._hw._plot_readings()
+    print(f'OhmPiHardware: Resistance: {k._hw.last_resistance() :.2f} ohm, dev. {k._hw.last_dev():.2f} %, sp: {k._hw.sp:.2f} mV, rx bias: {k._hw.rx._bias:.2f} mV')
+    print(f'OhmPi: Resistance: {d["R [ohm]"] :.2f} ohm, dev. {d["R_std [%]"]:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV')
+    k._hw._plot_readings(save_fig=False)
+    # plot_exec_log('ohmpi/logs/exec.log')
+change_config('../configs/config_default.py', verbose=False)
diff --git a/dev/test_mb_2024_3_mux_2024.py b/dev/test_mb_2024_3_mux_2024.py
new file mode 100644
index 0000000000000000000000000000000000000000..5d3a5e748cadb832e1e34626aa285f8719525ee9
--- /dev/null
+++ b/dev/test_mb_2024_3_mux_2024.py
@@ -0,0 +1,104 @@
+import matplotlib
+matplotlib.use('TkAgg')
+from ohmpi.utils import change_config
+change_config('../configs/config_mb_2024_0_2__3_mux_2024_dps5005.py', verbose=False)
+import importlib
+import time
+import logging
+from ohmpi.config import HARDWARE_CONFIG
+
+stand_alone = False
+part_of_hardware_system = True
+within_ohmpi = False
+
+# Stand alone
+if stand_alone:
+
+    ctl_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["ctl"].pop("model")}')
+    pwr_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["pwr"].pop("model")}')
+    tx_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["tx"].pop("model")}')
+    rx_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["rx"].pop("model")}')
+
+    ctl = ctl_module.Ctl()
+    HARDWARE_CONFIG['tx'].update({'ctl': ctl, 'exec_logger': ctl.exec_logger, 'soh_logger': ctl.soh_logger})
+    HARDWARE_CONFIG['rx'].update({'ctl': ctl, 'exec_logger': ctl.exec_logger, 'soh_logger': ctl.soh_logger})
+    HARDWARE_CONFIG['tx'].update({'connection': HARDWARE_CONFIG['tx'].pop('connection',
+                                                                          ctl.interfaces[
+                                                                              HARDWARE_CONFIG['tx'].pop(
+                                                                                  'interface_name', 'i2c')])})
+    HARDWARE_CONFIG['rx'].update({'connection': HARDWARE_CONFIG['rx'].pop('connection',
+                                                                          ctl.interfaces[
+                                                                              HARDWARE_CONFIG['rx'].pop(
+                                                                                  'interface_name', 'i2c')])})
+
+    HARDWARE_CONFIG['pwr'].update({'connection': HARDWARE_CONFIG['pwr'].pop('connection',
+                                                                          ctl.interfaces[
+                                                                              HARDWARE_CONFIG['pwr'].pop(
+                                                                                  'interface_name', None)])})
+
+
+    rx = rx_module.Rx(**HARDWARE_CONFIG['rx'])
+    tx = tx_module.Tx(**HARDWARE_CONFIG['tx'])
+    pwr = pwr_module.Pwr(**HARDWARE_CONFIG['pwr'])
+    mux_ids = ['mux_02', 'mux_05']
+    for m,mux_id in enumerate(mux_ids):
+        mux_module = importlib.import_module(
+            f'ohmpi.hardware_components.{HARDWARE_CONFIG["mux"]["boards"][mux_id].pop("model")}')
+
+        MUX_CONFIG = HARDWARE_CONFIG['mux']['boards'][mux_id]
+
+        MUX_CONFIG.update({'ctl': ctl, 'connection': MUX_CONFIG.pop('connection', ctl.interfaces[
+                                           MUX_CONFIG.pop('interface_name', 'i2c_ext')]), 'exec_logger': ctl.exec_logger,
+                       'soh_logger': ctl.soh_logger})
+        MUX_CONFIG.update({'id': mux_id})
+        mux = mux_module.Mux(**MUX_CONFIG)
+
+        # tx.polarity = 1
+        # time.sleep(1)
+        # tx.polarity = 0
+        # mux.switch(elec_dict={'A': [1], 'B': [4], 'M': [2], 'N': [3]}, state='on')
+        # time.sleep(1)
+        # voltage = rx.voltage
+        # current = tx.current
+        # mux.switch(elec_dict={'A': [1], 'B': [4], 'M': [2], 'N': [3]}, state='off')
+        # print(f'Resistance: {voltage / current :.2f} ohm, voltage: {voltage:.2f} mV, current: {current:.2f} mA')
+        mux.reset()
+        mux.test({'A': [i+8*m for i in range(1, 9)], 'B': [i+8*m for i in range(1, 9)],
+                  'M': [i+8*m for i in range(1, 9)], 'N': [i+8*m for i in range(1, 9)]}, activation_time=.1)
+        mux.reset()
+
+# mux as part of a OhmPiHardware system
+if part_of_hardware_system:
+    from ohmpi.hardware_system import OhmPiHardware
+    print('Starting test of as part of an OhmPiHardware system.')
+    # mux_id = 'mux_03'
+    k = OhmPiHardware()
+    k.exec_logger.setLevel(logging.DEBUG)
+    # Test mux switching
+    k.reset_mux()
+    # k.switch_mux(electrodes=[1, 4, 2, 3], roles=['A', 'B', 'M', 'N'], state='on')
+    # time.sleep(1.)
+    # k.switch_mux(electrodes=[1, 4, 2, 3], roles=['A', 'B', 'M', 'N'], state='off')
+    # k.mux_boards[mux_id].test(activation_time=.4)
+    k.test_mux()
+    k.reset_mux()
+
+if within_ohmpi:
+    from ohmpi.ohmpi import OhmPi
+    # from ohmpi.plots import plot_exec_log
+
+    print('Starting test with OhmPi.')
+    k = OhmPi()
+    # A, B, M, N = (32, 29, 31, 30)
+    k.reset_mux()
+    k.load_sequence('sequences/ABMN2.txt')
+    k.run_sequence(tx_volt=50, injection_duration=1., nb_stack=2, duty_cycle=0.5)
+    print('using OhmPi')
+    #d = k.run_measurement([A, B, M, N], injection_duration=1., nb_stack=2, duty_cycle=0.5)
+    # print(d)
+    # k._hw._plot_readings()
+    print(f'OhmPiHardware: Resistance: {k._hw.last_resistance() :.2f} ohm, dev. {k._hw.last_dev():.2f} %, sp: {k._hw.sp:.2f} mV, rx bias: {k._hw.rx._bias:.2f} mV')
+    print(f'OhmPi: Resistance: {d["R [ohm]"] :.2f} ohm, dev. {d["R_std [%]"]:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV')
+    # k._hw._plot_readings(save_fig=False)
+    # plot_exec_log('ohmpi/logs/exec.log')
+change_config('../configs/config_default.py', verbose=False)
diff --git a/dev/test_mb_2024_4_mux_2023.py b/dev/test_mb_2024_4_mux_2023.py
new file mode 100644
index 0000000000000000000000000000000000000000..ceceb8041908281afe7c1c9df930dc63aad601f1
--- /dev/null
+++ b/dev/test_mb_2024_4_mux_2023.py
@@ -0,0 +1,109 @@
+import matplotlib
+matplotlib.use('TkAgg')
+from ohmpi.utils import change_config
+change_config('../configs/config_mb_2024_0_2__4_mux_2023_dps5005.py', verbose=False)
+import importlib
+import time
+import logging
+from ohmpi.config import HARDWARE_CONFIG
+
+stand_alone = True
+part_of_hardware_system = False
+within_ohmpi = False
+
+# Stand alone
+if stand_alone:
+
+    ctl_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["ctl"].pop("model")}')
+    pwr_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["pwr"].pop("model")}')
+    tx_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["tx"].pop("model")}')
+    rx_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["rx"].pop("model")}')
+
+    ctl = ctl_module.Ctl()
+    HARDWARE_CONFIG['tx'].update({'ctl': ctl, 'exec_logger': ctl.exec_logger, 'soh_logger': ctl.soh_logger})
+    HARDWARE_CONFIG['rx'].update({'ctl': ctl, 'exec_logger': ctl.exec_logger, 'soh_logger': ctl.soh_logger})
+    HARDWARE_CONFIG['tx'].update({'connection': HARDWARE_CONFIG['tx'].pop('connection',
+                                                                          ctl.interfaces[
+                                                                              HARDWARE_CONFIG['tx'].pop(
+                                                                                  'interface_name', 'i2c')])})
+    HARDWARE_CONFIG['rx'].update({'connection': HARDWARE_CONFIG['rx'].pop('connection',
+                                                                          ctl.interfaces[
+                                                                              HARDWARE_CONFIG['rx'].pop(
+                                                                                  'interface_name', 'i2c')])})
+
+    HARDWARE_CONFIG['pwr'].update({'connection': HARDWARE_CONFIG['pwr'].pop('connection',
+                                                                          ctl.interfaces[
+                                                                              HARDWARE_CONFIG['pwr'].pop(
+                                                                                  'interface_name', None)])})
+
+
+    rx = rx_module.Rx(**HARDWARE_CONFIG['rx'])
+    tx = tx_module.Tx(**HARDWARE_CONFIG['tx'])
+    pwr = pwr_module.Pwr(**HARDWARE_CONFIG['pwr'])
+    role = 'A'
+    mux_id = f'mux_{role}'
+    mux_boards = []
+    mux_module = importlib.import_module(
+        f'ohmpi.hardware_components.{HARDWARE_CONFIG["mux"]["boards"][mux_id].pop("model")}')
+
+    MUX_CONFIG = HARDWARE_CONFIG['mux']['boards'][mux_id]
+
+    MUX_CONFIG.update({'ctl': ctl, 'connection': MUX_CONFIG.pop('connection', ctl.interfaces[
+                                       MUX_CONFIG.pop('interface_name', 'i2c_ext')]), 'exec_logger': ctl.exec_logger,
+                   'soh_logger': ctl.soh_logger})
+    MUX_CONFIG.update({'id': mux_id})
+    mux = mux_module.Mux(**MUX_CONFIG)
+    mux.reset()
+
+    mux.test({role: [i for i in range(1, 65)]}, activation_time=.1)
+    mux.reset()
+
+# mux as part of a OhmPiHardware system
+if part_of_hardware_system:
+    from ohmpi.hardware_system import OhmPiHardware
+    print('Starting test of as part of an OhmPiHardware system.')
+    # mux_id = 'mux_03'
+    k = OhmPiHardware()
+    k.exec_logger.setLevel(logging.DEBUG)
+    # Test mux switching
+    k.reset_mux()
+    # k.switch_mux(electrodes=[1, 4, 2, 3], roles=['A', 'B', 'M', 'N'], state='on')
+    # time.sleep(1.)
+    # k.switch_mux(electrodes=[1, 4, 2, 3], roles=['A', 'B', 'M', 'N'], state='off')
+    # k.mux_boards[mux_id].test(activation_time=.4)
+    k.test_mux()
+    k.reset_mux()
+
+if within_ohmpi:
+    from ohmpi.ohmpi import OhmPi
+    # from ohmpi.plots import plot_exec_log
+
+    print('Starting test with OhmPi.')
+    k = OhmPi()
+    # A, B, M, N = (32, 29, 31, 30)
+    k.reset_mux()
+    # k.test_mux(mux_id='mux_03')
+    # k._hw.switch_mux([A, B, M, N], state='on')
+    # k._hw.vab_square_wave(12.,1., cycles=2)
+    # k._hw.switch_mux([A, B, M, N], state='off')
+    # k._hw.calibrate_rx_bias()  # electrodes 1 4 2 3 should be connected to a reference circuit
+    # k._hw.rx._bias = -1.38
+    # print(f'Resistance: {k._hw.last_rho :.2f} ohm, dev. {k._hw.last_dev:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV')
+    # k._hw._plot_readings()
+    A, B, M, N = (1, 4, 2, 3)
+    # k._hw.switch_mux([A, B, M, N], state='on')
+    # k._hw.vab_square_wave(12., cycle_duration=10., cycles=3)
+    # k._hw.switch_mux([A, B, M, N], state='off')
+    # print(f'OhmPiHardware Resistance: {k._hw.last_rho :.2f} ohm, dev. {k._hw.last_dev:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV')
+    # k._hw._plot_readings()
+    k.load_sequence('sequences/9991_GRAD_16_s1_a1.txt')
+    k.run_sequence(tx_volt=5, injection_duration=1., nb_stack=2, duty_cycle=0.5)
+    print('using OhmPi')
+    #d = k.run_measurement([A, B, M, N], injection_duration=1., nb_stack=2, duty_cycle=0.5)
+    # print(d)
+    # k._hw._plot_readings()
+    print(f'OhmPiHardware: Resistance: {k._hw.last_resistance() :.2f} ohm, dev. {k._hw.last_dev():.2f} %, sp: {k._hw.sp:.2f} mV, rx bias: {k._hw.rx._bias:.2f} mV')
+    print(f'OhmPi: Resistance: {d["R [ohm]"] :.2f} ohm, dev. {d["R_std [%]"]:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV')
+    # k._hw._plot_readings(save_fig=False)
+    # plot_exec_log('ohmpi/logs/exec.log')
+change_config('../configs/config_default.py', verbose=False)
diff --git a/dev/test_mb_2024_4_mux_2024.py b/dev/test_mb_2024_4_mux_2024.py
new file mode 100644
index 0000000000000000000000000000000000000000..f25c5d58ef10e6862af0c32d93ac5c9abc5579ab
--- /dev/null
+++ b/dev/test_mb_2024_4_mux_2024.py
@@ -0,0 +1,118 @@
+import matplotlib
+matplotlib.use('TkAgg')
+from ohmpi.utils import change_config
+change_config('../configs/config_mb_2024_0_2__4_mux_2024_dps5005.py', verbose=False)
+import importlib
+import time
+import logging
+from ohmpi.config import HARDWARE_CONFIG
+
+stand_alone = False
+part_of_hardware_system = True
+within_ohmpi = False
+
+# Stand alone
+if stand_alone:
+
+    ctl_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["ctl"].pop("model")}')
+    pwr_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["pwr"].pop("model")}')
+    tx_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["tx"].pop("model")}')
+    rx_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["rx"].pop("model")}')
+
+    ctl = ctl_module.Ctl()
+    HARDWARE_CONFIG['tx'].update({'ctl': ctl, 'exec_logger': ctl.exec_logger, 'soh_logger': ctl.soh_logger})
+    HARDWARE_CONFIG['rx'].update({'ctl': ctl, 'exec_logger': ctl.exec_logger, 'soh_logger': ctl.soh_logger})
+    HARDWARE_CONFIG['tx'].update({'connection': HARDWARE_CONFIG['tx'].pop('connection',
+                                                                          ctl.interfaces[
+                                                                              HARDWARE_CONFIG['tx'].pop(
+                                                                                  'interface_name', 'i2c')])})
+    HARDWARE_CONFIG['rx'].update({'connection': HARDWARE_CONFIG['rx'].pop('connection',
+                                                                          ctl.interfaces[
+                                                                              HARDWARE_CONFIG['rx'].pop(
+                                                                                  'interface_name', 'i2c')])})
+
+    HARDWARE_CONFIG['pwr'].update({'connection': HARDWARE_CONFIG['pwr'].pop('connection',
+                                                                          ctl.interfaces[
+                                                                              HARDWARE_CONFIG['pwr'].pop(
+                                                                                  'interface_name', None)])})
+
+
+    rx = rx_module.Rx(**HARDWARE_CONFIG['rx'])
+    tx = tx_module.Tx(**HARDWARE_CONFIG['tx'])
+    pwr = pwr_module.Pwr(**HARDWARE_CONFIG['pwr'])
+    mux_ids = ['mux_02', 'mux_05']
+    for m,mux_id in enumerate(mux_ids):
+        mux_module = importlib.import_module(
+            f'ohmpi.hardware_components.{HARDWARE_CONFIG["mux"]["boards"][mux_id].pop("model")}')
+
+        MUX_CONFIG = HARDWARE_CONFIG['mux']['boards'][mux_id]
+
+        MUX_CONFIG.update({'ctl': ctl, 'connection': MUX_CONFIG.pop('connection', ctl.interfaces[
+                                           MUX_CONFIG.pop('interface_name', 'i2c_ext')]), 'exec_logger': ctl.exec_logger,
+                       'soh_logger': ctl.soh_logger})
+        MUX_CONFIG.update({'id': mux_id})
+        mux = mux_module.Mux(**MUX_CONFIG)
+
+        # tx.polarity = 1
+        # time.sleep(1)
+        # tx.polarity = 0
+        # mux.switch(elec_dict={'A': [1], 'B': [4], 'M': [2], 'N': [3]}, state='on')
+        # time.sleep(1)
+        # voltage = rx.voltage
+        # current = tx.current
+        # mux.switch(elec_dict={'A': [1], 'B': [4], 'M': [2], 'N': [3]}, state='off')
+        # print(f'Resistance: {voltage / current :.2f} ohm, voltage: {voltage:.2f} mV, current: {current:.2f} mA')
+        mux.reset()
+        mux.test({'A': [i+8*m for i in range(1, 9)], 'B': [i+8*m for i in range(1, 9)],
+                  'M': [i+8*m for i in range(1, 9)], 'N': [i+8*m for i in range(1, 9)]}, activation_time=.1)
+        mux.reset()
+
+# mux as part of a OhmPiHardware system
+if part_of_hardware_system:
+    from ohmpi.hardware_system import OhmPiHardware
+    print('Starting test of as part of an OhmPiHardware system.')
+    # mux_id = 'mux_03'
+    k = OhmPiHardware()
+    k.exec_logger.setLevel(logging.DEBUG)
+    # Test mux switching
+    k.reset_mux()
+    # k.switch_mux(electrodes=[1, 4, 2, 3], roles=['A', 'B', 'M', 'N'], state='on')
+    # time.sleep(1.)
+    # k.switch_mux(electrodes=[1, 4, 2, 3], roles=['A', 'B', 'M', 'N'], state='off')
+    # k.mux_boards[mux_id].test(activation_time=.4)
+    k.test_mux()
+    k.reset_mux()
+
+if within_ohmpi:
+    from ohmpi.ohmpi import OhmPi
+    # from ohmpi.plots import plot_exec_log
+
+    print('Starting test with OhmPi.')
+    k = OhmPi()
+    # A, B, M, N = (32, 29, 31, 30)
+    k.reset_mux()
+    # k.test_mux(mux_id='mux_03')
+    # k._hw.switch_mux([A, B, M, N], state='on')
+    # k._hw.vab_square_wave(12.,1., cycles=2)
+    # k._hw.switch_mux([A, B, M, N], state='off')
+    # k._hw.calibrate_rx_bias()  # electrodes 1 4 2 3 should be connected to a reference circuit
+    # k._hw.rx._bias = -1.38
+    # print(f'Resistance: {k._hw.last_rho :.2f} ohm, dev. {k._hw.last_dev:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV')
+    # k._hw._plot_readings()
+    A, B, M, N = (1, 4, 2, 3)
+    # k._hw.switch_mux([A, B, M, N], state='on')
+    # k._hw.vab_square_wave(12., cycle_duration=10., cycles=3)
+    # k._hw.switch_mux([A, B, M, N], state='off')
+    # print(f'OhmPiHardware Resistance: {k._hw.last_rho :.2f} ohm, dev. {k._hw.last_dev:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV')
+    # k._hw._plot_readings()
+    k.load_sequence('sequences/9991_GRAD_16_s1_a1.txt')
+    k.run_sequence(tx_volt=5, injection_duration=1., nb_stack=2, duty_cycle=0.5)
+    print('using OhmPi')
+    #d = k.run_measurement([A, B, M, N], injection_duration=1., nb_stack=2, duty_cycle=0.5)
+    # print(d)
+    # k._hw._plot_readings()
+    print(f'OhmPiHardware: Resistance: {k._hw.last_resistance() :.2f} ohm, dev. {k._hw.last_dev():.2f} %, sp: {k._hw.sp:.2f} mV, rx bias: {k._hw.rx._bias:.2f} mV')
+    print(f'OhmPi: Resistance: {d["R [ohm]"] :.2f} ohm, dev. {d["R_std [%]"]:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV')
+    # k._hw._plot_readings(save_fig=False)
+    # plot_exec_log('ohmpi/logs/exec.log')
+change_config('../configs/config_default.py', verbose=False)
diff --git a/dev/test_measure_with_mb_2023_0_X.py b/dev/test_measure_with_mb_2023_0_X.py
deleted file mode 100644
index e20f4802e2398c9c939fdb2822ada152e89a3b61..0000000000000000000000000000000000000000
--- a/dev/test_measure_with_mb_2023_0_X.py
+++ /dev/null
@@ -1,53 +0,0 @@
-import numpy as np
-import logging
-import matplotlib.pyplot as plt
-from ohmpi.utils import change_config
-change_config('../configs/config_mb_2023_mux_2024.py', verbose=False)
-from ohmpi.hardware_system import OhmPiHardware
-
-k = OhmPiHardware()
-k.exec_logger.setLevel(logging.INFO)
-
-# Test _vab_pulse:
-print('Testing positive _vab_pulse')
-k._vab_pulse(vab=12, duration=1., sampling_rate=k.rx.sampling_rate, polarity=1)
-r = k.readings[:,4]/k.readings[:,3]
-print(f'Mean resistance: {np.mean(r):.3f} Ohms, Dev. {100*np.std(r)/np.mean(r):.1f} %')
-print(f'sampling rate: {k.rx.sampling_rate:.1f} ms, mean sample spacing: {np.mean(np.diff(k.readings[:,0]))*1000.:.1f} ms')
-print('\nTesting negative _vab_pulse')
-k._vab_pulse(vab=12, duration=1., sampling_rate=k.rx.sampling_rate, polarity=-1)
-r = k.readings[:,4]/k.readings[:,3]
-print(f'Mean resistance: {np.mean(r):.3f} Ohms, Dev. {100*np.std(r)/np.mean(r):.1f} %')
-print(f'sampling rate: {k.rx.sampling_rate:.1f} ms, mean sample spacing: {np.mean(np.diff(k.readings[:,0]))*1000.:.1f} ms')
-
-# Test vab_square_wave:
-print('\n\nTesting vab_square_wave')
-cycles=3
-cycle_length = 1.
-k.vab_square_wave(vab=12, cycle_length=cycle_length, sampling_rate=k.rx.sampling_rate, cycles=cycles)
-r = k.readings[:,4]/k.readings[:,3]
-print(f'Mean resistance: {np.mean(r):.3f} Ohms, Dev. {100*np.std(r)/np.mean(r):.1f} %')
-print(f'sampling rate: {k.rx.sampling_rate:.1f} ms, mean sample spacing: {np.mean(np.diff(k.readings[:,0]))*1000.:.1f} ms')
-print(f'length of array: {len(r)}, expected length: {cycle_length*cycles*1000./k.rx.sampling_rate}')
-
-# Plot graphs
-fig, ax = plt.subplots()
-ax.plot(k.readings[:,0], k.readings[:,3], '-r', marker='.', label='iab')
-ax.set_ylabel('Iab [mA]')
-ax2 = ax.twinx()
-ax2.plot(k.readings[:,0], k.readings[:,2]*k.readings[:,4], '-b', marker='.', label='vmn')
-ax2.set_ylabel('Vmn [mV]')
-fig.legend()
-plt.show()
-
-# Compute resistances corrected for SP
-print(f'SP: {k.sp} mV')
-r = ((k.readings[:,4]-k.readings[:,2]*k.sp)/k.readings[:,3])
-print(f'Mean resistance with sp correction : {np.mean(r):.3f} Ohms, Dev. {100*np.std(r)/np.mean(r):.1f} %')
-print('\nTesting with pulses')
-r = [np.abs((k.pulses[i]['polarity']*k.pulses[i]['vmn']-k.sp)/k.pulses[i]['iab']) for i in k.pulses.keys()]
-for i in range(len(r)):
-    print(f'Mean resistance with sp correction for pulse{i}: {np.mean(r[i]):.3f} Ohms, Dev. {100*np.std(r[i])/np.mean(r[i]):.1f} %')
-
-change_config('../configs/config_default.py', verbose=False)
-
diff --git a/dev/test_mux_2024.py b/dev/test_mux_2024.py
deleted file mode 100644
index da950eb9ccabb20c8071678eaecd9a0c98f20c9a..0000000000000000000000000000000000000000
--- a/dev/test_mux_2024.py
+++ /dev/null
@@ -1,50 +0,0 @@
-import time
-from ohmpi.utils import change_config
-import logging
-change_config('../configs/config_mb_2023_mux_2024.py', verbose=False)
-from ohmpi.hardware_components.mux_2024_0_X import Mux, MUX_CONFIG
-from ohmpi.hardware_components import raspberry_pi as ctl_module
-# from ohmpi.config import HARDWARE_CONFIG
-
-stand_alone_mux = False  # Testing hardware component alone
-part_of_hardware_system = False  # Testing hardware component as a part of the hardware system
-within_ohmpi = True # Testing hardware component as a part of the hardware system through the ohmpi object
-# Stand alone mux
-if stand_alone_mux:
-    MUX_CONFIG['ctl'] = ctl_module.Ctl()
-    MUX_CONFIG['id'] = 'mux_1'
-    MUX_CONFIG['cabling'] = {(i+8, j) : ('mux_1', i) for j in ['A', 'B', 'M', 'N'] for i in range(1,9)}
-    mux = Mux(**MUX_CONFIG)
-    mux.switch_one(elec=9, role='M', state='on')
-    time.sleep(2)
-    mux.switch_one(elec=9, role='M', state='off')
-    mux.switch({'A': [9], 'B': [12], 'M': [10], 'N': [11]}, state='on')
-    time.sleep(8)
-    # mux.switch({'A': [1], 'B': [4], 'M': [2], 'N': [3]}, state='off')
-    mux.reset()
-    mux.test({'A': [9, 10, 11, 12, 13, 14, 15, 16], 'B': [9, 10, 11, 12, 13, 14, 15, 16],
-              'M': [9, 10, 11, 12, 13, 14, 15, 16], 'N': [9, 10, 11, 12, 13, 14, 15, 16]}, activation_time=.1)
-
-# mux as part of a OhmPiHardware system
-if part_of_hardware_system:
-    from ohmpi.hardware_system import OhmPiHardware
-    print('Starting test of mux as part of a OhmPiHardware system.')
-
-    k = OhmPiHardware()
-    k.exec_logger.setLevel(logging.DEBUG)
-
-    # Test mux switching
-    k.reset_mux()
-    k.switch_mux(electrodes=[1, 4, 2, 3], roles=['A', 'B', 'M', 'N'], state='on')
-    time.sleep(1.)
-    k.switch_mux(electrodes=[1, 4, 2, 3], roles=['A', 'B', 'M', 'N'], state='off')
-
-if within_ohmpi:
-    from ohmpi.ohmpi import OhmPi
-    print('Starting test of mux within OhmPi.')
-    k = OhmPi()
-    k.switch_mux_on([1, 4, 2, 3])
-    time.sleep(1.)
-    k.reset_mux()
-
-change_config('../configs/config_default.py', verbose=False)
\ No newline at end of file
diff --git a/html/index-mqtt.html b/html/index-mqtt.html
index 070d4fe13ea81700590c04d23493fbab8dcb512a..7c121aea79adcce5d3177f0c3e56e78e122f244a 100755
--- a/html/index-mqtt.html
+++ b/html/index-mqtt.html
@@ -59,7 +59,6 @@
         
         <!-- Additional buttons -->
         <button id="downloadBtn" type="button" class="btn btn-primary">Download data</button>
-        <!-- <button id="invertBtn" type="button" class="btn btn-primary">Invert</button> -->
         <a id="download"></a>
 
         <!-- Modal for configuration -->
diff --git a/index-mqtt.html b/index-mqtt.html
deleted file mode 100755
index 070d4fe13ea81700590c04d23493fbab8dcb512a..0000000000000000000000000000000000000000
--- a/index-mqtt.html
+++ /dev/null
@@ -1,654 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-    <meta charset="utf8"/>
-    <title>OhmPi Acquisition Board</title>
-    <link rel="shortcut icon" type="image/jpg" href="logo_ohmpi.jpg"/>
-    
-    <!-- dependencies (need to be local as no internet in AP mode)-->
-    <script src="js/plotly-basic-2.8.3.min.js"></script>
-    <script src="js/jquery-3.4.1.min.js"></script>
-    <link type="text/css" href="css/bootstrap.min.css" rel="stylesheet">
-	<!-- <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script> -->
-	<!-- <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-eOJMYsd53ii+scO/bJGFsiCZc+5NDVN2yr8+0RDqr0Ql0h+rP48ckxlpbzKgwra6" crossorigin="anonymous"> -->
-    <!-- <script src="js/danfojs/bundle.min.js"></script> -->
-    <!-- <script src="js/mqtt.min.js"></script> -->
-    <script src="js/paho/paho-mqtt.js"></script>
-</head>
-<body>
-    <div class='container'>
-        <h1>OhmPi Acquisition Board</h1>
-        <!-- nb stacks, on-time -->
-        <button id="update_settingsBtn" type="button" class="btn btn-secondary" data-toggle="modal" data-target="#exampleModal">Settings</button>
-        <button id='runBtn' type="button" class="btn btn-primary">&#9654</button>
-        <button id='stopBtn' type="button" class="btn btn-warning">&#9724</button>
-        <!-- upload button for csv which display the table ABMN -->
-        <button id="removeDataBtn" type="button" class="btn btn-danger">Clear data</button>
-        <button id="getDataBtn" type="button" class="btn btn-info">Get data</button>
-        <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
-            </label>
-        </div>
-        <div id='output'>Status: idle</div>
-        
-        <!-- Pseudo section -->
-        <select id='surveySelect' class='custom-select'>
-        </select>
-        <input id="cmin" type="number" value="0"/>
-        <input id="cmax" type="number" value="150"/>
-        <button id="capplyBtn" type="button" class="btn btn-info">Apply</button>
-        <div id="gd"></div>
-        <div class="mb3 row">
-            <label for="quadSelect">Quadrupole:</label>
-            <div class="col-sm-10">
-                <select id='quadSelect' class='custom-select'></select>    
-            </div>
-        </div>
-        
-        <!-- trace figure -->
-        <button id="addTraceBtn" type="button" class="btn btn-info">Add trace</button>
-        <button id="removeTracesBtn" type="button" class="btn btn-info">Remove all traces</button>    
-        <div id="ts"></div>
-        
-        <!-- RS check -->
-        <button id="rsBtn" type="button" class="btn btn-info">Check contact resistance</button>
-        <button id="rsClearBtn" type="button" class="btn btn-info">Clear plot</button>
-        <div id="rs"></div>
-        
-        <!-- Additional buttons -->
-        <button id="downloadBtn" type="button" class="btn btn-primary">Download data</button>
-        <!-- <button id="invertBtn" type="button" class="btn btn-primary">Invert</button> -->
-        <a id="download"></a>
-
-        <!-- Modal for configuration -->
-        <div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
-            <div class="modal-dialog" role="document">
-            <div class="modal-content">
-                <div class="modal-header">
-                <h5 class="modal-title" id="exampleModalLabel">OhmPi configuration</h5>
-                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
-                    <span aria-hidden="true">&times;</span>
-                </button>
-                </div>
-                <div class="modal-body">
-                    <form>
-                        <div class="form-group row">
-                          <label for="nbElectrodes" class="col-sm-2 col-form-label">Nb electrodes</label>
-                          <div class="col-sm-10">
-                            <input type="number" class="form-control-number" id="nbElectrodes" value="64">
-                          </div>
-                        </div>
-                        <div class="form-group row">
-                            <label for="injectionDuration" class="col-sm-2 col-form-label">Injection duration [s]</label>
-                            <div class="col-sm-10">
-                              <input type="number" class="form-control-number" id="injectionDuration" value="0.2">
-                            </div>
-                          </div>
-                          <div class="form-group row">
-                            <label for="nbMeasurements" class="col-sm-2 col-form-label">Nb Measurements</label>
-                            <div class="col-sm-10">
-                              <input type="number" class="form-control-number" id="nbMeasurements" value="1">
-                            </div>
-                          </div>
-                          <div class="form-group row">
-                            <label for="sequenceDelay" class="col-sm-2 col-form-label">Sequence delay [s]</label>
-                            <div class="col-sm-10">
-                              <input type="number" class="form-control-number" id="sequenceDelay" value="100">
-                            </div>
-                          </div>
-                          <div class="form-group row">
-                            <label for="nbStack" class="col-sm-2 col-form-label">Nb stack</label>
-                            <div class="col-sm-10">
-                              <input type="number" class="form-control-number" id="nbStack" value="1">
-                            </div>
-                          </div>
-                          <div class="form-group row">
-                            <label for="sequence" class="col-sm-2 col-form-label">Sequence</label>
-                            <div class="col-sm-10">
-                              <input type="file" class="form-control" id="sequence">
-                            </div>
-                          </div>
-                          <div class="form-group row">
-                            <label for="elecSpacing" class="col-sm-2 col-form-label">Electrode spacing [m]</label>
-                            <div class="col-sm-10">
-                              <input type="number" class="form-control" id="elecSpacing", value="1">
-                            </div>
-                          </div>
-                      </form>
-                </div>
-                <div class="modal-footer">
-                <button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
-                <button id="saveConfigBtn" type="button" data-dismiss="modal" class="btn btn-primary">Save</button>
-                </div>
-            </div>
-            </div>
-        </div>
-        <button id="restartBtn" type="button" class="btn btn-danger">Restart</button>
-        <button id="shutdownBtn" type="button" class="btn btn-danger">Shutdown</button>
-        <footer>v0.2.0</footer>
-    </div>
-
-    <script type="text/javascript">
-        //let serverUrl = 'http://10.3.141.1:8080'
-        //let serverUrl = 'http://0.0.0.0:8080'
-        //let serverUrl = 'http://localhost:8080'
-        let serverUrl = 'http://' + window.location.host
-        console.log('serverUrl =', serverUrl)
-        let output = document.getElementById('output')
-        let data = {} // hold data of all surveys
-        let interv = null // hold interval for automatic data retrieval
-        let quads = [] // available quadrupoles for time-serie figure
-        let squads = [] // selected quadrupoles for time-serie figure
-        let commands = {} // store commands and their id
-        let callbacks = {} // store callback (might not be needed)
-
-        // function with MQTT
-        let topic = 'ohmpi_0001' // we could change this through a drop-down to connect to a different ohmpi
-        let topic_ctrl = topic + '/ctrl'
-        let topic_exec = topic + '/exec'
-        let topic_data = topic + '/data'
-        let hostname = location.hostname
-        let port = 9001
-        let clientId = 'ohmpi_0001_html'
-        let message = null
-        let msg = ''
-
-        // create client
-        client = new Paho.MQTT.Client(hostname, port, clientId);
-        client.onConnectionLost = onConnectionLost;
-        client.onMessageArrived = onMessageArrived;
-        client.connect({onSuccess:onConnect});
-
-        function onConnect() {
-            console.log("onConnect")
-            client.subscribe(topic_data)
-            client.subscribe(topic_exec)
-
-            // send welcome message
-            message = new Paho.MQTT.Message("Hello from index.html")
-            message.destinationName = topic_ctrl
-            client.send(message)
-        }
-
-        function onConnectionLost(responseObject) {
-            if (responseObject.errorCode !== 0)
-                console.log("onConnectionLost:" + responseObject.errorMessage)
-        }
-        
-        function onMessageArrived(message) {
-            console.log("onMessageArrived:" + message.payloadString)
-            try {
-                let payload = message.payloadString
-                if (message.topic == topic_data) {
-                    // process data
-                    msg = payload // for accessing the variable from the console
-                    console.log('DATA', payload)
-                    let ddic = JSON.parse(payload.split('INFO:')[1])
-
-                    // check cmd_id is any
-                    processData(ddic)
-
-                    // usually these don't have a cmd_id so we are not sure when
-
-                } else if (message.topic == topic_exec) {
-                    // display it in the log
-                    console.log('EXEC LOG:', payload)
-                }
-
-                // let response = JSON.parse(message.payloadString)
-                // console.log('response=', response)
-                // // check ID of message against our dictionnary of callback
-                // let cmd_id = response['cmd_id']
-                // if (callbacks.hasOwnProperty(cmd_id)) {
-                //     console.log('++ execute callback')
-                //     callbacks[cmd_id](response['content']) // execute callback
-                // }
-            } catch (e) {
-                console.log(e)
-            }
-            // client.disconnect()
-        }
-        
-        // useful functions
-        function generateUniqSerial() {  
-            return 'xxxx-xxxx-xxx-xxxx'.replace(/[x]/g, (c) => {  
-                const r = Math.floor(Math.random() * 16);  
-                return r.toString(16);  
-            });  
-        }
-
-        // sending commands to the OhmPi
-        function sendCommand(query, callback=null) {
-            // dic in the form: {'cmd': X, ...} as JSON
-            if (callback == null) {
-                function callback(x) {
-                    console.log('default callback:', x)
-                }
-            }
-            
-            /*
-            let xhr = new XMLHttpRequest();
-            xhr.onreadystatechange = function() {
-                if (this.readyState == 4) {
-                    if (xhr.status == 200) {
-                        callback(JSON.parse(xhr.response))
-                    }
-                }
-            }
-            xhr.open('POST', serverUrl)
-            xhr.setRequestHeader('Content-Type', 'application/json')
-            xhr.send(query)
-            */
-            
-            // generate a unique command id to be associated with the commands
-            let uuid = generateUniqSerial()
-            commands[uuid] = query
-            callbacks[uuid] = callback // store the callback to be processed later when message arrives
-            let payload = '{"cmd_id": "' + uuid + '",' + query.slice(1)
-            console.log('sendCommand()', payload)
-            message = new Paho.MQTT.Message(payload)
-            message.destinationName = topic_ctrl
-            client.send(message)            
-        }
-
-        // run button
-        function runBtnFunc() {
-            sendCommand('{"cmd": "run_multiple_sequences"}', function(x) {
-                console.log(x['status'])
-                if (x['status'] == 'running') {
-                    output.innerHTML = 'Status: measuring...'
-                }
-            })
-        }
-        let runBtn = document.getElementById('runBtn')
-        runBtn.addEventListener('click', runBtnFunc)
-
-        // interrupt button
-        function stopBtnFunc() {
-            sendCommand('{"cmd": "interrupt"}', function(x) {
-                output.innerHTML = 'Status: ' + x['status']
-                clearInterval(interv)
-                getData()
-            })
-        }
-        let stopBtn = document.getElementById('stopBtn')
-        stopBtn.addEventListener('click', stopBtnFunc)
-
-        // set configuration
-        function saveConfigBtnFunc() {
-            // collect values from modal
-            let formVals = {}
-            for (let field of ['nbElectrodes', 'injectionDuration',
-             'nbMeasurements', 'sequenceDelay', 'nbStack']) {
-                formVals[field] = document.getElementById(field).value
-            }
-            console.log(formVals)
-            
-            // define callback to send settings to Pi
-            function configCallback() {
-                sendCommand(JSON.stringify({
-                    'cmd': 'update_settings',
-                    'kwargs': {
-                        'config': formVals
-                    }
-                }), function(x) {
-                    console.log('update_settings', x)
-                })
-            }
-            
-            // deal with the potential file containing the sequence
-            // https://stackoverflow.com/questions/19038919/is-it-possible-to-upload-a-text-file-to-input-in-html-js
-            if (!window.FileReader) {
-                alert('Your browser is not supported');
-                return false;
-            }
-            let input = document.getElementById('sequence')
-            if (input.files.length) {
-                const reader = new FileReader()
-                reader.readAsText(input.files[0])
-                reader.addEventListener('load', () => {
-                    formVals['sequence'] = reader.result
-                    console.log('file==', reader.result)
-                    configCallback()
-                }, false)
-            } else {
-                console.log('no sequence uploaded')
-                formVals['sequence'] = ''
-                configCallback()
-            } 
-            
-            
-        }
-        let saveConfigBtn = document.getElementById('saveConfigBtn')
-        saveConfigBtn.addEventListener('click', saveConfigBtnFunc)
-
-        // make pseudo plot
-        var trace = {
-            x: [],
-            y: [],
-            mode: 'markers',
-            marker: {
-                size: 40,
-                color: [],
-                colorbar: {
-                    title: 'App. res. [Ohm.m]',
-                    cmin: 0,
-                    cmax: 100,
-                }
-            }
-        }
-        let layout = {
-            title: 'Pseudo-section',
-            yaxis: {
-                title: 'Pseudo-depth',
-                autorange: 'reversed'
-            },
-            xaxis: {
-                title: 'X'
-            }
-
-        }
-        Plotly.newPlot('gd', [trace], layout)
-
-        // make time-serie plot
-        let tdata = []
-        let layout2 = {
-            title: 'Time-serie',
-            yaxis: {
-                title: 'App. res. [Ohm.m]'
-            },
-            xaxis: {
-                title: 'Sampling time'
-            }
-        }
-        Plotly.newPlot('ts', tdata, layout2)
-
-        // add trace to time-serie plot
-        function addTraceBtnFunc() {
-            let val = document.getElementById('quadSelect').value
-            squads.push(val.split(', '))
-            tdata.push({
-                x: [],
-                y: [],
-                name: val,
-                type: 'scatter'
-            })
-            Plotly.newPlot('ts', tdata, layout2)
-            getData()
-        }
-        let addTraceBtn = document.getElementById('addTraceBtn')
-        addTraceBtn.addEventListener('click', addTraceBtnFunc)
-
-        // remove all traces from time-serie plot
-        function removeTracesBtnFunc() {
-            squads = []
-            tdata = []            
-            Plotly.newPlot('ts', tdata, layout2)
-        }
-        let removeTracesBtn = document.getElementById('removeTracesBtn')
-        removeTracesBtn.addEventListener('click', removeTracesBtnFunc)
-
-        // callback function to draw the plot
-        function surveySelectFunc(el) {
-            let surveyName = el['target'].value
-            let df = data[surveyName]
-            if (df != undefined) {
-                let a = df['a']
-                let b = df['b']
-                let m = df['m']
-                let n = df['n']
-                // let's assume electrodes are 1 m distance
-                // compute pseudo-depth (assume no topo)
-                // compute app res (assumping flat, line survey)
-                let xpos = []
-                let ypos = []
-                let app = []
-                for (let i = 0; i < a.length; i++) {
-                    let ab = (a[i] + b[i])/2
-                    let mn = (m[i] + n[i])/2
-                    let dist = Math.abs(ab - mn)
-                    xpos.push(Math.min(ab, mn) + dist/2)
-                    ypos.push(Math.sqrt(2)/2*dist)
-                    let am = Math.abs(a[i] - m[i])
-                    let bm = Math.abs(b[i] - m[i])
-                    let an = Math.abs(a[i] - n[i])
-                    let bn = Math.abs(a[i] - n[i])
-                    let K = (2*Math.PI)/((1/am)-(1/an)-(1/an)+(1/bn))
-                    app.push(df['rho'][i]*-K)
-                }
-                console.log(app)
-                // update the trace and redraw the figure
-                trace['x'] = xpos
-                trace['y'] = ypos
-                trace['marker']['color'] = app
-                trace['marker']['cmax'] = document.getElementById('cmax').value
-                trace['marker']['cmin'] = document.getElementById('cmin').value
-                Plotly.redraw('gd')
-            }
-        }
-        let surveySelect = document.getElementById('surveySelect')
-
-        // bar chart for contact resistance
-        let rsdata = []
-        let rslayout = {
-            title: 'Contact resistances',
-            yaxis: {
-                title: 'Resistance [kOhm]'
-            },
-            xaxis: {
-                title: 'Consecutive electrodes'
-            }
-        }
-        Plotly.newPlot('rs', rsdata, rslayout)
-        
-        // run RS check
-        function rsBtnFunc() {
-            sendCommand('{"cmd": "rsCheck"}', function (res) {
-                // update the bar plot
-                rsdata.push({
-                x: res['data']['AB'],
-                y: res['data']['res'],
-                name: 'RS',
-                type: 'bar'
-                })
-                Plotly.redraw('rs')
-            })
-        }
-        let rsBtn = document.getElementById('rsBtn')
-        rsBtn.addEventListener('click', rsBtnFunc)
-        
-        // clear RS graph
-        function rsClearBtnFunc() {
-            rsdata = []
-            Plotly.newPlot('rs', rsdata, rslayout)
-        }
-        let rsClearBtn = document.getElementById('rsClearBtn')
-        rsClearBtn.addEventListener('click', rsClearBtnFunc)
-        
-        // getData
-        function getData() {
-            sendCommand(JSON.stringify({
-                'cmd': 'get_data',
-                'survey_names': Object.keys(data).slice(0, -1)
-                // last survey is often partial so we download it again
-            }), console.log('processData(ddic)')
-            )
-        }
-
-        // processData
-        function processData(ddic) {
-            // update status
-            output.innerHTML = 'Status: ' + ddic['status']
-
-            // update data dic with new data
-            data = { // destructuring assignement (magic! :o)
-                ...data,
-                ...ddic['data'] // value from second dic are preferred
-            }
-            
-            // dropdown with number of surveys and +++
-            let surveyNames = Object.keys(data).sort()
-
-            // remove listener as we will replace the choices
-            surveySelect.removeEventListener('change', surveySelectFunc)
-            surveySelect.innerHTML = ''  // clearing all child nodes
-
-            // add choices again
-            for (let surveyName of surveyNames) {
-                let option = document.createElement('option')
-                option.innerText = surveyName
-                option.value = surveyName
-                surveySelect.appendChild(option)
-            }
-
-            // listener again
-            surveySelect.addEventListener('change', surveySelectFunc)
-            
-            // plot last one by default
-            surveySelect.value = surveyNames[surveyNames.length - 1]
-            
-            // call the function directly
-            // (as progammatically chaging the value does not trigger the event)
-            surveySelectFunc({'target': surveySelect})
-
-            // 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)
-                }
-                console.log('quads=', quads)
-            }
-
-            // update time-serie figure
-            if (squads.length > 0) {
-
-                // transform all surveyNames to datetime
-                let xt = []
-                for (surveyName of surveyNames) {
-                    let a = surveyName.split('_').slice(-1)[0]
-                    xt.push(a.slice(0, 4) + '-' 
-                        + a.slice(4, 6) + '-' 
-                        + a.slice(6, 8) + ' '
-                        + a.slice(9, 11) + ':'
-                        + a.slice(11, 13) + ':'
-                        + a.slice(13, 15))
-                }
-                //console.log(xt)
-
-                // create one new trace per selected quadrupole
-                for (let k = 0; k < squads.length; k++) {
-                    squad = squads[k]
-                    let x = []
-                    let y = []
-                    for (let i = 0; i < surveyNames.length; i++) {
-                        df = data[surveyNames[i]]
-                        for (let j = 0; j < df['a'].length; j++) {
-                            if (df['a'][j] == squad[0]
-                            && df['b'][j] == squad[1]
-                            && df['m'][j] == squad[2]
-                            && df['n'][j] == squad[3]) {
-                                y.push(df['rho'][j])
-                                x.push(xt[i])
-                                break
-                            }
-                        }
-                    }
-
-                    // update trace dictionnary
-                    tdata[k]['x'] = x
-                    tdata[k]['y'] = y
-                }
-                //console.log(tdata)
-                Plotly.redraw('ts')
-            }
-        }
-
-        let getDataBtn = document.getElementById('getDataBtn')
-        getDataBtn.addEventListener('click', getData)
-        
-        // apply new colorscale
-        let capplyBtn = document.getElementById('capplyBtn')
-        capplyBtn.addEventListener('click', function() {
-            surveySelectFunc({'target': surveySelect})
-        })
-        
-        // checkbox interaction for data download
-        function dataRetrievalCheckFunc(x) {
-            if (x['target'].checked == true) {
-                interv = setInterval(getData, 1000) // every 5s
-            } else {
-                clearInterval(interv)
-            }             
-        }
-        let dataRetrievalCheck = document.getElementById('dataRetrievalCheck')
-        dataRetrievalCheck.addEventListener('change', dataRetrievalCheckFunc)
-
-        // remove data
-        function removeDataBtnFunc() {
-            sendCommand('{"cmd": "removeData"}',function(x) {
-                data = {}
-                output.innerHTML = 'Status: ' + x['status'] + ' (all data cleared)'
-                console.log('all data removed')
-            })
-        }
-        let removeDataBtn = document.getElementById('removeDataBtn')
-        removeDataBtn.addEventListener('click', removeDataBtnFunc)
-
-        // shutdown Pi
-        function shutdownBtnFunc() {
-            sendCommand('{"cmd": "shutdown"}', function(x) {
-                console.log('shuting down...')
-            })
-        }
-        let shutdownBtn = document.getElementById('shutdownBtn')
-        shutdownBtn.addEventListener('click', shutdownBtnFunc)
-        
-        // restart Pi
-        function restartBtnFunc() {
-            sendCommand('{"cmd": "restart"}', function(x) {
-                console.log('rebooting...')
-            })
-        }
-        let restartBtn = document.getElementById('restartBtn')
-        restartBtn.addEventListener('click', restartBtnFunc)
-        
-        // invert data
-        // function invertBtnFunc() {
-        //     sendCommand('{"cmd": "invert"}', function(x) {
-        //         console.log('inversion results', x)
-        //     })
-        // }
-        // let invertBtn = document.getElementById('invertBtn')
-        // invertBtn.addEventListener('click', invertBtnFunc)
-
-        // download data
-        function downloadBtnFunc() {
-            sendCommand('{"cmd": "download"}', function(x) {
-                let dwl = document.getElementById('download')
-                dwl.setAttribute('href', serverUrl + '/data.zip')
-                dwl.setAttribute('download', 'data.zip')
-                dwl.click()
-            })
-        }
-        let downloadBtn = document.getElementById('downloadBtn')
-        downloadBtn.addEventListener('click', downloadBtnFunc)
-
-
-    </script>
-    
-    <!-- Boostrap scripts (at the end of the page for faster loading time)-->
-	<script src="js/bootstrap.bundle.min.js"></script>
-    <!-- <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta3/dist/js/bootstrap.bundle.min.js" integrity="sha384-JEW9xMcG8R+pH31jmWH6WWP0WintQrMb4s7ZOdauHnUtxwoG2vI5DkLtS3qm9Ekf" crossorigin="anonymous"></script> -->
-</body>
-</html>
diff --git a/index.html b/index.html
deleted file mode 100644
index b334d18b47d1f8ab64e32028a15fac4c0b513d7e..0000000000000000000000000000000000000000
--- a/index.html
+++ /dev/null
@@ -1,704 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-    <meta charset="utf8"/>
-    <title>OhmPi Acquisition Board</title>
-    <link rel="shortcut icon" type="image/jpg" href="logo_ohmpi.jpg"/>
-    
-    <!-- dependencies (need to be local as no internet in AP mode)-->
-    <script src="js/plotly-basic-2.8.3.min.js"></script>
-    <script src="js/jquery-3.4.1.min.js"></script>
-    <link type="text/css" href="css/bootstrap.min.css" rel="stylesheet">
-	<!-- <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script> -->
-	<!-- <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-eOJMYsd53ii+scO/bJGFsiCZc+5NDVN2yr8+0RDqr0Ql0h+rP48ckxlpbzKgwra6" crossorigin="anonymous"> -->
-    <!-- <script src="js/danfojs/bundle.min.js"></script> -->
-</head>
-<body>
-    <div class='container'>
-        <h1>OhmPi Acquisition Board</h1>
-        <!-- nb stacks, on-time -->
-        <button id="update_settingsBtn" type="button" class="btn btn-secondary" data-toggle="modal" data-target="#exampleModal">Settings</button>
-        <button id='runBtn' type="button" class="btn btn-primary">&#9654</button>
-        <button id='stopBtn' type="button" class="btn btn-warning">&#9724</button>
-        <!-- upload button for csv which display the table ABMN -->
-        <button id="removeDataBtn" type="button" class="btn btn-danger">Clear data</button>
-        <button id="getDataBtn" type="button" class="btn btn-info">Get data</button>
-        <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 second
-            </label>
-        </div>
-        <div id='output'>Status: idle</div>
-        
-        <!-- Pseudo section -->
-        <select id='surveySelect' class='custom-select'>
-        </select>
-        <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 id="hoverinfo" style="margin-left:80px;"></div>
-        <div class="mb3 row">
-            <label for="quadSelect">Quadrupole:</label>
-            <div class="col-sm-10">
-                <select id='quadSelect' class='custom-select'></select>    
-            </div>
-        </div>
-        
-        <!-- trace figure -->
-        <button id="addTraceBtn" type="button" class="btn btn-info">Add trace</button>
-        <button id="removeTracesBtn" type="button" class="btn btn-info">Remove all traces</button>    
-        <div id="ts"></div>
-        
-        <!-- RS check -->
-        <button id="rsBtn" type="button" class="btn btn-info">Check contact resistance</button>
-        <button id="getRsBtn" type="button" class="btn btn-info">Get contact resistance</button>
-        <button id="rsClearBtn" type="button" class="btn btn-info">Clear plot</button>
-        <div id="rs"></div>
-        
-        <!-- Additional buttons -->
-        <button id="downloadBtn" type="button" class="btn btn-primary">Download data</button>
-        <!-- <button id="invertBtn" type="button" class="btn btn-primary">Invert</button> -->
-        <a id="download"></a>
-
-        <!-- Modal for settings -->
-        <div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
-            <div class="modal-dialog" role="document">
-            <div class="modal-content">
-                <div class="modal-header">
-                <h5 class="modal-title" id="exampleModalLabel">OhmPi settings</h5>
-                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
-                    <span aria-hidden="true">&times;</span>
-                </button>
-                </div>
-                <div class="modal-body">
-                    <form>
-                        <div class="form-group row">
-                          <label for="nbElectrodes" class="col-sm-2 col-form-label">Nb electrodes</label>
-                          <div class="col-sm-10">
-                            <input type="number" class="form-control-number" id="nbElectrodes" value=64>
-                          </div>
-                        </div>
-                        <div class="form-group row">
-                            <label for="injectionDuration" class="col-sm-2 col-form-label">Injection duration [s]</label>
-                            <div class="col-sm-10">
-                              <input type="number" class="form-control-number" id="injectionDuration" value=0.2>
-                            </div>
-                          </div>
-                          <div class="form-group row">
-                            <label for="nbMeasurements" class="col-sm-2 col-form-label">Nb Measurements</label>
-                            <div class="col-sm-10">
-                              <input type="number" class="form-control-number" id="nbMeasurements" value="1">
-                            </div>
-                          </div>
-                          <div class="form-group row">
-                            <label for="sequenceDelay" class="col-sm-2 col-form-label">Sequence delay [s]</label>
-                            <div class="col-sm-10">
-                              <input type="number" class="form-control-number" id="sequenceDelay" value="100">
-                            </div>
-                          </div>
-                          <div class="form-group row">
-                            <label for="nbStack" class="col-sm-2 col-form-label">Nb stack</label>
-                            <div class="col-sm-10">
-                              <input type="number" class="form-control-number" id="nbStack" value="1">
-                            </div>
-                          </div>
-                          <div class="form-group row">
-                            <label for="sequence" class="col-sm-2 col-form-label">Sequence</label>
-                            <div class="col-sm-10">
-                              <input type="file" class="form-control" id="sequence">
-                            </div>
-                          </div>
-                          <div class="form-group row">
-                            <label for="elecSpacing" class="col-sm-2 col-form-label">Electrode spacing [m]</label>
-                            <div class="col-sm-10">
-                              <input type="number" class="form-control" id="elecSpacing", value="1">
-                            </div>
-                          </div>
-                      </form>
-                </div>
-                <div class="modal-footer">
-                <button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
-                <button id="saveConfigBtn" type="button" data-dismiss="modal" class="btn btn-primary">Save</button>
-                </div>
-            </div>
-            </div>
-        </div>
-        <button id="restartBtn" type="button" class="btn btn-danger">Restart</button>
-        <button id="shutdownBtn" type="button" class="btn btn-danger">Shutdown</button>
-        <footer>v0.2.0</footer>
-    </div>
-
-    <script type="text/javascript">
-        //let serverUrl = 'http://10.3.141.1:8080'
-        //let serverUrl = 'http://0.0.0.0:8080'
-        //let serverUrl = 'http://localhost:8080'
-        let serverUrl = 'http://' + window.location.host
-        console.log('serverUrl =', serverUrl)
-        let output = document.getElementById('output')
-        let data = {} // hold data of all surveys
-        let interv = null // hold interval for automatic data retrieval
-        let quads = [] // available quadrupoles for time-serie figure
-        let squads = [] // selected quadrupoles for time-serie figure
-        let elecSpacing = 1 // 1 m
-
-        // useful functions
-        function sendCommand(query, callback=null) {
-            // dic in the form: {'cmd': X, ...} as JSON
-            if (callback == null) {
-                function callback(x) {
-                    console.log('default callback:', x)
-                }
-            }
-            let xhr = new XMLHttpRequest();
-            xhr.onreadystatechange = function() {
-                if (this.readyState == 4) {
-                    if (xhr.status == 200) {
-                        callback(JSON.parse(xhr.response))
-                    }
-                }
-            }
-            xhr.open('POST', serverUrl)
-            xhr.setRequestHeader('Content-Type', 'application/json')
-            xhr.send(query)
-        }
-
-        // run button
-        function runBtnFunc() {
-            sendCommand('{"cmd": "run_multiple_sequences"}', function(x) {
-                console.log(x['ohmpi_status'])
-                if (x['ohmpi_status'] == 'running') {
-                    output.innerHTML = 'Status: measuring...'
-                }
-            })
-        }
-        let runBtn = document.getElementById('runBtn')
-        runBtn.addEventListener('click', runBtnFunc)
-
-        // interrupt button
-        function stopBtnFunc() {
-            sendCommand('{"cmd": "interrupt"}', function(x) {
-                output.innerHTML = 'Status: ' + x['ohmpi_status']
-                clearInterval(interv)
-                getData()
-            })
-        }
-        let stopBtn = document.getElementById('stopBtn')
-        stopBtn.addEventListener('click', stopBtnFunc)
-
-        // set configuration
-        function saveSettingsBtnFunc() {
-            // collect values from modal
-            let formVals = {}
-            formVals['nb_electrodes'] = parseInt(document.getElementById('nbElectrodes').value)
-            formVals['injection_duration'] = parseFloat(document.getElementById('injectionDuration').value)
-            formVals['nb_meas'] = parseInt(document.getElementById('nbMeasurements').value)
-            formVals['sequence_delay'] = parseInt(document.getElementById('sequenceDelay').value)
-            formVals['nb_stack'] = parseInt(document.getElementById('nbStack').value)
-            formVals['elec_spacing'] = parseFloat(document.getElementById('elecSpacing').value)
-            console.log(formVals)
-            elecSpacing = formVals['elec_spacing']
-            
-            // define callback to send settigs to Pi
-            function settingsCallback() {
-                sendCommand(JSON.stringify({
-                    'cmd': 'update_settings',
-                    'config': formVals
-                }), function(x) {
-                    console.log('update_settings', x)
-                })
-            }
-            
-            // deal with the potential file containing the sequence
-            // https://stackoverflow.com/questions/19038919/is-it-possible-to-upload-a-text-file-to-input-in-html-js
-            if (!window.FileReader) {
-                alert('Your browser is not supported');
-                return false;
-            }
-            let input = document.getElementById('sequence')
-            if (input.files.length) {
-                const reader = new FileReader()
-                reader.readAsText(input.files[0])
-                reader.addEventListener('load', () => {
-                    formVals['sequence'] = reader.result
-                    console.log('file==', reader.result)
-                    settingsCallback()
-                }, false)
-            } else {
-                console.log('no sequence uploaded')
-                formVals['sequence'] = ''
-                settingsCallback()
-            } 
-            
-            
-        }
-        let saveConfigBtn = document.getElementById('saveConfigBtn')
-        saveConfigBtn.addEventListener('click', saveSettingsBtnFunc)
-
-        // make pseudo plot
-        var trace = {}
-        let layout = {}
-        let tdata = []
-        let layout2 = {}
-        let rsdata = []
-        let rslayout = {}
-        
-        // initialize all plots
-        function initPlots() {
-			trace = {
-				x: [],
-				y: [],
-				mode: 'markers',
-				marker: {
-					size: 40,
-					color: [],
-					colorbar: {
-						title: 'App. res. [Ohm.m]',
-						cmin: 0,
-						cmax: 100,
-					}
-				}
-			}
-			layout = {
-				title: 'Pseudo-section',
-				yaxis: {
-					title: 'Pseudo-depth',
-					autorange: 'reversed'
-				},
-				xaxis: {
-					title: 'X'
-				}
-
-			}
-			Plotly.newPlot('gd', [trace], layout)
-
-			// make time-serie plot
-			tdata = []
-			layout2 = {
-				title: 'Time-serie',
-				yaxis: {
-					title: 'App. res. [Ohm.m]'
-				},
-				xaxis: {
-					title: 'Sampling time'
-				}
-			}
-			Plotly.newPlot('ts', tdata, layout2)
-			
-			// bar chart for contact resistance
-			rsdata = []
-			rslayout = {
-				title: 'Contact resistances',
-				yaxis: {
-					title: 'Resistance [kOhm]'
-				},
-				xaxis: {
-					title: 'Consecutive electrodes'
-				}
-			}
-			Plotly.newPlot('rs', rsdata, rslayout)
-		}
-		initPlots()
-
-        // hover function
-        var hoverInfo = document.getElementById('hoverinfo')
-        document.getElementById('gd').on('plotly_hover', function(data){
-            var infotext = data.points.map(function(d){
-              return (Math.round(d.data.marker.color[d.pointIndex], 2) + ' Ohm.m');
-            });
-            hoverInfo.innerHTML = infotext.join('<br/>');
-        })
-         .on('plotly_unhover', function(data){
-            hoverInfo.innerHTML = '';
-        });
-
-        // add trace to time-serie plot
-        function addTraceBtnFunc() {
-            let val = document.getElementById('quadSelect').value
-            squads.push(val.split(', '))
-            tdata.push({
-                x: [],
-                y: [],
-                name: val,
-                type: 'scatter'
-            })
-            Plotly.newPlot('ts', tdata, layout2)
-            getData()
-        }
-        let addTraceBtn = document.getElementById('addTraceBtn')
-        addTraceBtn.addEventListener('click', addTraceBtnFunc)
-
-        // remove all traces from time-serie plot
-        function removeTracesBtnFunc() {
-            squads = []
-            tdata = []            
-            Plotly.newPlot('ts', tdata, layout2)
-        }
-        let removeTracesBtn = document.getElementById('removeTracesBtn')
-        removeTracesBtn.addEventListener('click', removeTracesBtnFunc)
-
-        // callback function to draw the plot
-        function surveySelectFunc(el) {
-            let surveyName = el['target'].value
-            let df = data[surveyName]
-            if (df != undefined) {
-                // let's assume electrodes are 1 m distance
-                // compute pseudo-depth (assume no topo)
-                // compute app res (assumping flat, line survey)
-                let xpos = []
-                let ypos = []
-                let app = []
-                for (let i = 0; i < df['a'].length; i++) {
-                    let a = df['a'][i]
-                    let b = df['b'][i]
-                    let m = df['m'][i]
-                    let n = df['n'][i]
-                    
-                    // compute geometric factor assuming flat 2D surface
-                    let am = Math.abs(a - m)*elecSpacing
-                    let bm = Math.abs(b - m)*elecSpacing
-                    let an = Math.abs(a - n)*elecSpacing
-                    let bn = Math.abs(b - n)*elecSpacing
-                    let K = 2*Math.PI/((1/am)-(1/bm)-(1/an)+(1/bn))
-                    app.push(df['rho'][i]*K)
-                    //console.log(K) // same as resipy for the wenner case
-                
-                    // computing pseudo-depth assuming 2D flat array
-                    // let's sort the electrodes AB are the two left, MN, the two right
-                    let abmn = [a, b, m, n]
-                    abmn = abmn.sort((a, b) => a - b)
-                    let ab = (abmn[0] + abmn[1])/2
-                    let mn = (abmn[2] + abmn[3])/2
-                    let dist = Math.abs(ab - mn)
-                    xpos.push((Math.min(ab, mn) + dist/2)*elecSpacing)
-                    ypos.push((Math.sqrt(2)/2*dist)*elecSpacing)
-                    
-                    /*
-                           lookupDict = dict(zip(self.elec['label'], np.arange(self.elec.shape[0]))) 
-        array = self.df[['a','b','m','n']].replace(lookupDict).values.astype(int)
-        elecm = self.elec[['x','y','z']].values.astype(float).copy() # electrode matrix - should be array of floats so np.inf work properly
-            
-        ### first determine if measurements are nested ###
-        #find mid points of AB 
-        AB = (elecm[array[:,0]] + elecm[array[:,1]]) / 2 # mid points of AB 
-        MN = (elecm[array[:,2]] + elecm[array[:,3]]) / 2 # mid points of MN 
-        ABrad = np.sqrt(np.sum((elecm[array[:,0]] - AB)**2,axis=1)) # radius of AB circle 
-        MNrad = np.sqrt(np.sum((elecm[array[:,2]] - MN)**2,axis=1)) # radius of MN circle 
-        
-        Amn = np.sqrt(np.sum((elecm[array[:,0]] - MN)**2,axis=1)) # distance of A to mid point of MN 
-        Bmn = np.sqrt(np.sum((elecm[array[:,1]] - MN)**2,axis=1)) # distance of B to mid point of MN 
-        Nab = np.sqrt(np.sum((elecm[array[:,2]] - AB)**2,axis=1)) # distance of N to mid point of AB 
-        Mab = np.sqrt(np.sum((elecm[array[:,3]] - AB)**2,axis=1)) # distance of M to mid point of AB
-        
-        iABinMN = (Amn < MNrad) & (Bmn < MNrad)
-        iMNinAB = (Nab < ABrad) & (Mab < ABrad)
-        inested = iABinMN | iMNinAB #if AB encompasses MN or MN encompasses AB 
-                       
-        # so it will never be taken as minimium
-        elecm[self.elec['remote'].values,:] = np.inf
-        
-        # compute midpoint position of AB and MN dipoles
-        elecx = elecm[:,0]
-        elecy = elecm[:,1]
-
-        #CURRENT ELECTRODE MIDPOINTS 
-        caddx = np.abs(elecx[array[:,0]]-elecx[array[:,1]])/2
-        caddy = np.abs(elecy[array[:,0]]-elecy[array[:,1]])/2
-        caddx[np.isinf(caddx)] = 0 
-        caddy[np.isinf(caddy)] = 0        
-        cmiddlex = np.min([elecx[array[:,0]], elecx[array[:,1]]], axis=0) + caddx
-        cmiddley = np.min([elecy[array[:,0]], elecy[array[:,1]]], axis=0) + caddy
-        
-        #POTENTIAL ELECTRODE MIDPOINTS
-        paddx = np.abs(elecx[array[:,2]]-elecx[array[:,3]])/2
-        paddy = np.abs(elecy[array[:,2]]-elecy[array[:,3]])/2
-        paddx[np.isinf(paddx)] = 0 
-        paddy[np.isinf(paddy)] = 0 
-        pmiddlex = np.min([elecx[array[:,2]], elecx[array[:,3]]], axis=0) + paddx
-        pmiddley = np.min([elecy[array[:,2]], elecy[array[:,3]]], axis=0) + paddy
-
-        
-        # for non-nested measurements
-        xposNonNested  = np.min([cmiddlex, pmiddlex], axis=0) + np.abs(cmiddlex-pmiddlex)/2
-        yposNonNested  = np.min([cmiddley, pmiddley], axis=0) + np.abs(cmiddley-pmiddley)/2
-        pcdist = np.sqrt((cmiddlex-pmiddlex)**2 + (cmiddley-pmiddley)**2)
-
-        # zposNonNested = np.sqrt(2)/2*pcdist
-        zposNonNested = pcdist/4
-
-        if np.all(cmiddley-pmiddley == 0):
-            zposNonNested = 0.25*pcdist
-        else: # for 3D arrays where there are mid-line measurements, this works closer to inversion results
-            zposNonNested = np.sqrt(2)/2*pcdist
-        
-        # for nested measurements use formula of Dalhin 2006
-        xposNested = np.zeros(len(pmiddlex))
-        yposNested = np.zeros(len(pmiddlex))
-        outerElec1 = np.zeros((len(pmiddlex), 2)) # position of one electrode of outer dipole
-        outerElec2 = np.zeros((len(pmiddlex), 2)) # position of one electrode of outer dipole
-        # innerMid = np.zeros((len(pmiddlex), 2)) # middle of inner dipole
-        if np.sum(iMNinAB) > 0:
-            xposNested[iMNinAB] = pmiddlex[iMNinAB]
-            yposNested[iMNinAB] = pmiddley[iMNinAB]
-            outerElec1[iMNinAB] = np.c_[elecx[array[iMNinAB,0]], elecy[array[iMNinAB,0]]]
-            outerElec2[iMNinAB] = np.c_[elecx[array[iMNinAB,1]], elecy[array[iMNinAB,1]]]
-
-        if np.sum(iABinMN) > 0:
-            xposNested[iABinMN] = cmiddlex[iABinMN]
-            yposNested[iABinMN] = cmiddley[iABinMN]
-            outerElec1[iABinMN] = np.c_[elecx[array[iABinMN,2]], elecy[array[iABinMN,2]]]
-            outerElec2[iABinMN] = np.c_[elecx[array[iABinMN,3]], elecy[array[iABinMN,3]]]
-      
-        innerMid = np.c_[pmiddlex, pmiddley] # always use potential dipole
-        
-        apdist = np.sqrt(np.sum((outerElec1-innerMid)**2, axis=1))
-        bpdist = np.sqrt(np.sum((outerElec2-innerMid)**2, axis=1))
-        zposNested  = np.min([apdist, bpdist], axis=0)/3
-        
-        xpos = np.zeros_like(pmiddlex)
-        ypos = np.zeros_like(pmiddlex)
-        zpos = np.zeros_like(pmiddlex)
-        
-        xpos[~inested] = xposNonNested[~inested]
-        xpos[inested] = xposNested[inested]
-        
-        ypos[~inested] = yposNonNested[~inested]
-        ypos[inested] = yposNested[inested]
-        
-        zpos[~inested] = zposNonNested[~inested]
-        zpos[inested] = zposNested[inested]
-
-                    */
-                  }
-                //console.log(app)
-                // update the trace and redraw the figure
-                trace['x'] = xpos
-                trace['y'] = ypos
-                trace['marker']['color'] = app
-                trace['marker']['cmax'] = document.getElementById('cmax').value
-                trace['marker']['cmin'] = document.getElementById('cmin').value
-                Plotly.redraw('gd')
-            }
-        }
-        let surveySelect = document.getElementById('surveySelect')
-        
-        // run RS check
-        function rsBtnFunc() {
-            sendCommand('{"cmd": "rsCheck"}', function (a) {})
-        }
-        let rsBtn = document.getElementById('rsBtn')
-        rsBtn.addEventListener('click', rsBtnFunc)
-
-        // get RS check data
-        function getRsBtnFunc() {
-            sendCommand('{"cmd": "getRsCheck"}', function(res) {
-                // update the bar plot
-                rsdata.push({
-                x: res['data']['AB'],
-                y: res['data']['res'],
-                name: 'RS',
-                type: 'bar'
-                })
-                Plotly.redraw('rs')
-            })
-        }
-        let getRsBtn = document.getElementById('getRsBtn')
-        getRsBtn.addEventListener('click', getRsBtnFunc)
-        
-        // clear RS graph
-        function rsClearBtnFunc() {
-            rsdata = []
-            Plotly.newPlot('rs', rsdata, rslayout)
-        }
-        let rsClearBtn = document.getElementById('rsClearBtn')
-        rsClearBtn.addEventListener('click', rsClearBtnFunc)
-        
-        // getData
-        function getData() {
-            let surveyNames = []
-            sendCommand(JSON.stringify({
-                'cmd': 'getData',
-                'surveyNames': surveyNames
-                // last survey is often partial so we download it again
-            }), function(ddic) {
-                // update status
-                //output.innerHTML = 'Status: ' + ddic['status']
-
-                // update data dic with new data
-                data = { // destructuring assignement (magic! :o)
-                    ...data,
-                    ...ddic['data'] // value from second dic are preferred
-                }
-                
-                // dropdown with number of surveys
-                surveyNames = Object.keys(data).sort()
-
-                // remove listener as we will replace the choices
-                surveySelect.removeEventListener('change', surveySelectFunc)
-                surveySelect.innerHTML = ''  // clearing all child nodes
-
-                // add choices again
-                for (let surveyName of surveyNames) {
-                    let option = document.createElement('option')
-                    option.innerText = surveyName
-                    option.value = surveyName
-                    surveySelect.appendChild(option)
-                }
-
-                // listener again
-                surveySelect.addEventListener('change', surveySelectFunc)
-                
-                // plot last one by default
-                surveySelect.value = surveyNames[surveyNames.length - 1]
-                
-                // call the function directly
-                // (as progammatically chaging the value does not trigger the event)
-                surveySelectFunc({'target': surveySelect})
-
-                // update list of quadrupoles if any
-                let idiff = false
-                if (data[surveyNames[0]] != undefined) {
-                    idiff = quads.length != data[surveyNames[0]]['a'].length
-                } 
-                //console.log('idiff=', idiff, quads.length, data[surveyNames[0]]['a'].length)
-                if (((quads.length == 0) | idiff) & (data[surveyNames[0]] != undefined)){
-                    console.log('updating list of quadrupoles')
-                    quads = []
-                    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)
-                }
-                
-                // update time-serie figure
-                if (squads.length > 0) {
-
-                    // transform all surveyNames to datetime
-                    let xt = []
-                    for (surveyName of surveyNames) {
-                        let a = surveyName.split('_').slice(-1)[0]
-                        xt.push(a.slice(0, 4) + '-' 
-                            + a.slice(4, 6) + '-' 
-                            + a.slice(6, 8) + ' '
-                            + a.slice(9, 11) + ':'
-                            + a.slice(11, 13) + ':'
-                            + a.slice(13, 15))
-                    }
-                    //console.log(xt)
-
-                    // create one new trace per selected quadrupole
-                    for (let k = 0; k < squads.length; k++) {
-                        squad = squads[k]
-                        let x = []
-                        let y = []
-                        for (let i = 0; i < surveyNames.length; i++) {
-                            df = data[surveyNames[i]]
-                            for (let j = 0; j < df['a'].length; j++) {
-                                if (df['a'][j] == squad[0]
-                                && df['b'][j] == squad[1]
-                                && df['m'][j] == squad[2]
-                                && df['n'][j] == squad[3]) {
-                                    y.push(df['rho'][j])
-                                    x.push(xt[i])
-                                    break
-                                }
-                            }
-                        }
-
-                        // update trace dictionnary
-                        tdata[k]['x'] = x
-                        tdata[k]['y'] = y
-                    }
-                    //console.log(tdata)
-                    Plotly.redraw('ts')
-                }
-            })
-        }
-        let getDataBtn = document.getElementById('getDataBtn')
-        getDataBtn.addEventListener('click', getData)
-        
-        // apply new colorscale
-        let capplyBtn = document.getElementById('capplyBtn')
-        capplyBtn.addEventListener('click', function() {
-            surveySelectFunc({'target': surveySelect})
-        })
-
-        // checkbox interaction for data download
-        function dataRetrievalCheckFunc(x) {
-            if (x['target'].checked == true) {
-                interv = setInterval(getData, 1000) // every 5s
-            } else {
-                clearInterval(interv)
-            }             
-        }
-        let dataRetrievalCheck = document.getElementById('dataRetrievalCheck')
-        dataRetrievalCheck.addEventListener('change', dataRetrievalCheckFunc)
-
-        // remove data
-        function removeDataBtnFunc() {
-            sendCommand('{"cmd": "removeData"}',function(x) {
-                data = {}
-                output.innerHTML = 'Status: ' + x['ohmpi_status'] + ' (all data cleared)'
-                console.log('all data removed')
-                initPlots() // reset all plots
-            })
-        }
-        let removeDataBtn = document.getElementById('removeDataBtn')
-        removeDataBtn.addEventListener('click', removeDataBtnFunc)
-
-        // shutdown Pi
-        function shutdownBtnFunc() {
-            sendCommand('{"cmd": "shutdown"}', function(x) {
-                console.log('shuting down...')
-            })
-        }
-        let shutdownBtn = document.getElementById('shutdownBtn')
-        shutdownBtn.addEventListener('click', shutdownBtnFunc)
-        
-        // restart Pi
-        function restartBtnFunc() {
-            sendCommand('{"cmd": "restart"}', function(x) {
-                console.log('rebooting...')
-            })
-        }
-        let restartBtn = document.getElementById('restartBtn')
-        restartBtn.addEventListener('click', restartBtnFunc)
-        
-        // invert data
-        // function invertBtnFunc() {
-        //     sendCommand('{"cmd": "invert"}', function(x) {
-        //         console.log('inversion results', x)
-        //     })
-        // }
-        // let invertBtn = document.getElementById('invertBtn')
-        // invertBtn.addEventListener('click', invertBtnFunc)
-
-        // download data
-        function downloadBtnFunc() {
-            sendCommand('{"cmd": "download"}', function(x) {
-                let dwl = document.getElementById('download')
-                dwl.setAttribute('href', serverUrl + '/data.zip')
-                dwl.setAttribute('download', 'data.zip')
-                dwl.click()
-            })
-        }
-        let downloadBtn = document.getElementById('downloadBtn')
-        downloadBtn.addEventListener('click', downloadBtnFunc)
-
-
-    </script>
-    
-    <!-- Boostrap scripts (at the end of the page for faster loading time)-->
-	<script src="js/bootstrap.bundle.min.js"></script>
-    <!-- <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta3/dist/js/bootstrap.bundle.min.js" integrity="sha384-JEW9xMcG8R+pH31jmWH6WWP0WintQrMb4s7ZOdauHnUtxwoG2vI5DkLtS3qm9Ekf" crossorigin="anonymous"></script> -->
-</body>
-</html>
diff --git a/ohmpi/__init__.py b/ohmpi/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..d1c1cf1ce4567059e2106febb97432bcdad4cfa1 100644
--- a/ohmpi/__init__.py
+++ b/ohmpi/__init__.py
@@ -0,0 +1,2 @@
+# from .ohmpi import OhmPi
+#from ohmpi.ohmpi import OhmPi
diff --git a/ohmpi/hardware_components/abstract_hardware_components.py b/ohmpi/hardware_components/abstract_hardware_components.py
index 4868a0081c309a0e44abb9768cd88c986fe83f95..54c56fb466a19742185f600cff3ace373f3c48e7 100644
--- a/ohmpi/hardware_components/abstract_hardware_components.py
+++ b/ohmpi/hardware_components/abstract_hardware_components.py
@@ -1,5 +1,4 @@
 from abc import ABC, abstractmethod
-
 import numpy as np
 from ohmpi.logging_setup import create_stdout_logger
 import time
@@ -8,28 +7,28 @@ from threading import Event, Barrier, BrokenBarrierError
 
 class CtlAbstract(ABC):
     def __init__(self, **kwargs):
-        self.board_name = kwargs.pop('board_name', 'unknown CTL hardware')
-        self.interfaces = None  # TODO: allow for several buses
+        self.model = kwargs.pop('model', 'unknown CTL hardware')
+        self.interfaces = None
         self.exec_logger = kwargs.pop('exec_logger', None)
         if self.exec_logger is None:
             self.exec_logger = create_stdout_logger('exec_ctl')
         self.soh_logger = kwargs.pop('soh_logger', None)
         if self.soh_logger is None:
             self.soh_logger = create_stdout_logger('soh_ctl')
-        self.exec_logger.debug(f'{self.board_name} Ctl initialization')
+        self.exec_logger.debug(f'{self.model} Ctl initialization')
         self._cpu_temp_available = False
         self.max_cpu_temp = np.inf
-        self.connection = None
+        self.connection = kwargs.pop('connection', None)
 
     @property
     def cpu_temperature(self):
         if not self._cpu_temp_available:
-            self.exec_logger.warning(f'CPU temperature reading is not available for {self.board_name}')
+            self.exec_logger.warning(f'CPU temperature reading is not available for {self.model}')
             cpu_temp = np.nan
         else:
             cpu_temp = self._cpu_temp
             if cpu_temp > self.max_cpu_temp:
-                self.soh_logger.warning(f'CPU temperature of {self.board_name} is over the limit!')
+                self.soh_logger.warning(f'CPU temperature of {self.model} is over the limit!')
         return cpu_temp
 
     @property
@@ -40,7 +39,7 @@ class CtlAbstract(ABC):
 
 class PwrAbstract(ABC):
     def __init__(self, **kwargs):
-        self.board_name = kwargs.pop('board_name', 'unknown PWR hardware')
+        self.model = kwargs.pop('model', 'unknown PWR hardware')
         self.exec_logger = kwargs.pop('exec_logger', None)
         if self.exec_logger is None:
             self.exec_logger = create_stdout_logger('exec_mux')
@@ -49,15 +48,15 @@ class PwrAbstract(ABC):
             self.soh_logger = create_stdout_logger('soh_mux')
         self.voltage_adjustable = kwargs.pop('voltage_adjustable', False)
         self._voltage = np.nan
-        self._current_adjustable = kwargs.pop('current_adjustable', False)
+        self.current_adjustable = kwargs.pop('current_adjustable', False)
         self._current = np.nan
-        self._state = 'off'
+        self._pwr_state = 'off'
         self._current_min = kwargs.pop('current_min', 0.)
         self._current_max = kwargs.pop('current_max', 0.)
         self._voltage_min = kwargs.pop('voltage_min', 0.)
         self._voltage_max = kwargs.pop('voltage_max', 0.)
-        self.ctl = kwargs.pop('ctl', None)
-        self.connection = kwargs.pop('io', None)
+        self.connection = kwargs.pop('connection', None)
+        self._battery_voltage = np.nan
 
     @property
     @abstractmethod
@@ -70,16 +69,30 @@ class PwrAbstract(ABC):
     def current(self, value, **kwargs):
         # add actions to set the DPS current
         pass
+    #
+    # @abstractmethod
+    # def turn_off(self):
+    #     self.exec_logger.debug(f'Switching {self.model} off')
+    #     self._state = 'off'
+    #
+    # @abstractmethod
+    # def turn_on(self):
+    #     self.exec_logger.debug(f'Switching {self.model} on')
+    #     self._state = 'on'
 
-    @abstractmethod
-    def turn_off(self):
-        self.exec_logger.debug(f'Switching {self.board_name} off')
-        self._state = 'off'
+    @property
+    def pwr_state(self):
+        return self._pwr_state
+
+    @pwr_state.setter
+    def pwr_state(self, state):
+        if state == 'on':
+            self._pwr_state = 'on'
+            self.exec_logger.debug(f'{self.model} cannot be switched on')
+        elif state == 'off':
+            self._pwr_state = 'off'
+            self.exec_logger.debug(f'{self.model} cannot be switched off')
 
-    @abstractmethod
-    def turn_on(self):
-        self.exec_logger.debug(f'Switching {self.board_name} on')
-        self._state = 'on'
 
     @property
     @abstractmethod
@@ -92,27 +105,33 @@ class PwrAbstract(ABC):
     def voltage(self, value):
         assert isinstance(value, float)
         if not self.voltage_adjustable:
-            self.exec_logger.debug(f'Voltage cannot be set on {self.board_name}...')
+            self.exec_logger.debug(f'Voltage cannot be set on {self.model}...')
         else:
-            assert self._voltage_min < value < self._voltage_max
+            assert self._voltage_min <= value <= self._voltage_max
             # add actions to set the DPS voltage
             self._voltage = value
 
+    def battery_voltage(self):
+        # add actions to read the DPS voltage
+        self.exec_logger.debug(f'Battery voltage cannot be read on {self.model}...')
+        return self._battery_voltage
+
+    def reset_voltage(self):
+        if not self.voltage_adjustable:
+            self.exec_logger.debug(f'Voltage cannot be set on {self.model}...')
+        else:
+            self.voltage = self._voltage_min
 
 class MuxAbstract(ABC):
     def __init__(self, **kwargs):
-        self.board_name = kwargs.pop('board_name', 'unknown MUX hardware')
-        self.exec_logger = kwargs.pop('exec_logger', None)
-        if self.exec_logger is None:
-            self.exec_logger = create_stdout_logger('exec_mux')
-        self.soh_logger = kwargs.pop('soh_logger', None)
-        if self.soh_logger is None:
-            self.soh_logger = create_stdout_logger('soh_mux')
+        self.model = kwargs.pop('model', 'unknown MUX hardware')
+        self.exec_logger = kwargs.pop('exec_logger', create_stdout_logger('exec_mux'))
+        self.soh_logger = kwargs.pop('soh_logger', create_stdout_logger('soh_mux'))
         self.board_id = kwargs.pop('id', None)
         if self.board_id is None:
-            self.exec_logger.error(f'MUX {self.board_name} should have an id !')
-        self.exec_logger.debug(f'MUX {self.board_id} ({self.board_name}) initialization')
-        self.connection = kwargs.pop('io', None)
+            self.exec_logger.error(f'MUX {self.model} should have an id !')
+        self.exec_logger.debug(f'MUX {self.model}: {self.board_id} initialization')
+        self.connection = kwargs.pop('connection', None)
         cabling = kwargs.pop('cabling', None)
         self.cabling = {}
         if cabling is not None:
@@ -157,7 +176,7 @@ class MuxAbstract(ABC):
         """
         status = True
         if elec_dict is not None:
-            self.exec_logger.debug(f'Switching {self.board_name} ')
+            self.exec_logger.debug(f'Switching {self.model} ')
             # check to prevent A == B (SHORT-CIRCUIT)
             if 'A' in elec_dict.keys() and 'B' in elec_dict.keys():
                 out = np.in1d(elec_dict['A'], elec_dict['B'])
@@ -203,7 +222,7 @@ class MuxAbstract(ABC):
                 self.exec_logger.debug(f'Barrier error {self.board_id} switching aborted.')
                 status = False
         else:
-            self.exec_logger.warning(f'Missing argument for {self.board_name}.switch: elec_dict is None.')
+            self.exec_logger.warning(f'Missing argument for {self.model}.switch: elec_dict is None.')
             status = False
         if state == 'on':
             time.sleep(self._activation_delay)
@@ -234,7 +253,7 @@ class MuxAbstract(ABC):
         activation_time : float, optional
             Time in seconds during which the relays are activated.
         """
-        self.exec_logger.debug(f'Starting {self.board_name} test...')
+        self.exec_logger.debug(f'Starting {self.model} test...')
         self.reset()
 
         for role in elec_dict.keys():
@@ -248,7 +267,7 @@ class MuxAbstract(ABC):
 
 class TxAbstract(ABC):
     def __init__(self, **kwargs):
-        self.board_name = kwargs.pop('board_name', 'unknown TX hardware')
+        self.model = kwargs.pop('model', 'unknown TX hardware')
         injection_duration = kwargs.pop('injection_duration', 1.)
         self.exec_logger = kwargs.pop('exec_logger', None)
         if self.exec_logger is None:
@@ -256,8 +275,7 @@ class TxAbstract(ABC):
         self.soh_logger = kwargs.pop('soh_logger', None)
         if self.soh_logger is None:
             self.soh_logger = create_stdout_logger('soh_tx')
-        self.ctl = kwargs.pop('ctl', None)
-        self.connection = kwargs.pop('io', None)
+        self.connection = kwargs.pop('connection', None)
         self.pwr = kwargs.pop('pwr', None)
         self._polarity = 0
         self._injection_duration = None
@@ -265,7 +283,8 @@ class TxAbstract(ABC):
         self.injection_duration = injection_duration
         self._latency = kwargs.pop('latency', 0.)
         self.tx_sync = kwargs.pop('tx_sync', Event())
-        self.exec_logger.debug(f'{self.board_name} TX initialization')
+        self.exec_logger.debug(f'{self.model} TX initialization')
+        self._pwr_state = 'off'
 
     @property
     def adc_gain(self):
@@ -283,15 +302,15 @@ class TxAbstract(ABC):
         self.exec_logger.debug(f'Setting TX ADC gain to {value}')
 
     @abstractmethod
-    def adc_gain_auto(self):
+    def _adc_gain_auto(self):
         pass
 
     @abstractmethod
-    def current_pulse(self, **kwargs):
+    def current_pulse(self, **kurwargs):
         pass
 
     @abstractmethod
-    def inject(self, polarity=1, injection_duration=None):
+    def inject(self, polarity=1, injection_duration=None, switch_pwr=False):
         """
         Abstract method to define injection
         Parameters
@@ -300,20 +319,26 @@ class TxAbstract(ABC):
             Injection polarity, can be eiter  1, 0 or -1
         injection_duration: float, default None
             Injection duration in seconds
+        switch_pwr: bool
+            switches on and off tx.pwr
         """
         assert polarity in [-1, 0, 1]
         if injection_duration is None:
             injection_duration = self._injection_duration
         if np.abs(polarity) > 0:
-            self.pwr.turn_on()
+            if switch_pwr:
+                self.pwr.pwr_state('on')
             self.tx_sync.set()
             time.sleep(injection_duration)
-            self.pwr.turn_off()
+            self.tx_sync.clear()
+            if switch_pwr:
+                self.pwr.pwr_state('off')
         else:
             self.tx_sync.set()
-            self.pwr.turn_off()
+            if switch_pwr:
+                self.pwr.pwr_state('off')
             time.sleep(injection_duration)
-        self.tx_sync.clear()
+            self.tx_sync.clear()
 
     @property
     def injection_duration(self):
@@ -365,6 +390,18 @@ class TxAbstract(ABC):
         self.exec_logger.debug(f'Voltage pulse of {polarity * self.pwr.voltage:.3f} V for {length:.3f} s')
         self.inject(polarity=polarity, injection_duration=length)
 
+    @property
+    def pwr_state(self):
+        return self._pwr_state
+
+    @pwr_state.setter
+    def pwr_state(self, state):
+        if state == 'on':
+            self._pwr_state = 'on'
+            self.exec_logger.debug(f'{self.model} cannot switch on power source')
+        elif state == 'off':
+            self._pwr_state = 'off'
+            self.exec_logger.debug(f'{self.model} cannot switch off power source')
 
 class RxAbstract(ABC):
     def __init__(self, **kwargs):
@@ -374,16 +411,16 @@ class RxAbstract(ABC):
         self.soh_logger = kwargs.pop('soh_logger', None)
         if self.soh_logger is None:
             self.soh_logger = create_stdout_logger('soh_rx')
-        self.ctl = kwargs.pop('ctl', None)
-        self.connection = kwargs.pop('io', None)
-        self.board_name = kwargs.pop('board_name', 'unknown RX hardware')
+        self.connection = kwargs.pop('connection', None)
+        self.model = kwargs.pop('model', 'unknown RX hardware')
         self._sampling_rate = kwargs.pop('sampling_rate', 1)  # ms
-        self.exec_logger.debug(f'{self.board_name} RX initialization')
+        self.exec_logger.debug(f'{self.model} RX initialization')
         self._voltage_max = kwargs.pop('voltage_max', 0.)
         self._adc_gain = 1.
         self._max_sampling_rate = np.inf
         self._latency = kwargs.pop('latency', 0.)
         self._bias = kwargs.pop('bias', 0.)
+        self._vmn_hardware_offset = kwargs.pop('vmn_hardware_offset', 0.)
 
     @property
     def adc_gain(self):
@@ -401,7 +438,7 @@ class RxAbstract(ABC):
         self.exec_logger.debug(f'Setting RX ADC gain to {value}')
 
     @abstractmethod
-    def adc_gain_auto(self):
+    def _adc_gain_auto(self):
         pass
 
     @property
diff --git a/ohmpi/hardware_components/dummy_ctl.py b/ohmpi/hardware_components/dummy_ctl.py
index 9eeb98e0a4ee56f075ccc061c2feb58e13cfba90..0045bd726c551db024d5362b75182eeaed327ad0 100644
--- a/ohmpi/hardware_components/dummy_ctl.py
+++ b/ohmpi/hardware_components/dummy_ctl.py
@@ -6,5 +6,5 @@ CTL_CONFIG = HARDWARE_CONFIG['ctl']
 
 class Ctl(CtlAbstract):
     def __init__(self, **kwargs):
-        kwargs.update({'board_name': os.path.basename(__file__).rstrip('.py')})
+        kwargs.update({'model': os.path.basename(__file__).rstrip('.py')})
         super().__init__(**kwargs)
diff --git a/ohmpi/hardware_components/dummy_mux.py b/ohmpi/hardware_components/dummy_mux.py
index 21468e2dccbf7385f62afcbb923c5201db22653a..bb8a5a7d118aa5d132ffe6bb6aeaaba33066767c 100644
--- a/ohmpi/hardware_components/dummy_mux.py
+++ b/ohmpi/hardware_components/dummy_mux.py
@@ -6,7 +6,6 @@ MUX_CONFIG = HARDWARE_CONFIG['mux'].pop('default', {})
 
 class Mux(MuxAbstract):
     def __init__(self, **kwargs):
-        kwargs.update({'board_name': os.path.basename(__file__).rstrip('.py')})
         super().__init__(**kwargs)
 
     def _get_addresses(self):
diff --git a/ohmpi/hardware_components/dummy_rx.py b/ohmpi/hardware_components/dummy_rx.py
index 386b193ced9411f0b048305e42910c08d195b2bd..249a2e49170661195662736ed2abe2f65a054345 100644
--- a/ohmpi/hardware_components/dummy_rx.py
+++ b/ohmpi/hardware_components/dummy_rx.py
@@ -12,9 +12,9 @@ voltage_adc_voltage_max = 4500.
 RX_CONFIG['voltage_min'] = np.min([voltage_adc_voltage_min, RX_CONFIG.pop('voltage_min', np.inf)])  # mV
 RX_CONFIG['voltage_max'] = np.min([voltage_adc_voltage_max, RX_CONFIG.pop('voltage_max', np.inf)])  # mV
 
+
 class Rx(RxAbstract):
     def __init__(self, **kwargs):
-        kwargs.update({'board_name': os.path.basename(__file__).rstrip('.py')})
         super().__init__(**kwargs)
         self._adc_gain = 1.
 
diff --git a/ohmpi/hardware_components/dummy_tx.py b/ohmpi/hardware_components/dummy_tx.py
index 067c1465978814876e4dba25e64f182b83d1d150..cef80c8e7dfdfb386487add40f79e50579fda93c 100644
--- a/ohmpi/hardware_components/dummy_tx.py
+++ b/ohmpi/hardware_components/dummy_tx.py
@@ -28,7 +28,6 @@ class Tx(TxAbstract):
         pass
 
     def __init__(self, **kwargs):
-        kwargs.update({'board_name': os.path.basename(__file__).rstrip('.py')})
         super().__init__(**kwargs)
         self._voltage = kwargs.pop('voltage', TX_CONFIG['default_voltage'])
 
diff --git a/ohmpi/hardware_components/mb_2023_0_X.py b/ohmpi/hardware_components/mb_2023_0_X.py
index 3e0cd90466f8095444e1a0f4db87ef97c8ef0b72..dd1933602033173960eb0781119be869bf782d46 100644
--- a/ohmpi/hardware_components/mb_2023_0_X.py
+++ b/ohmpi/hardware_components/mb_2023_0_X.py
@@ -1,65 +1,45 @@
 import datetime
-import importlib
-from ohmpi.config import HARDWARE_CONFIG  # TODO: Remove references at config here -> move it in ohmpi_hardware as done for mux_2024
 import adafruit_ads1x15.ads1115 as ads  # noqa
 from adafruit_ads1x15.analog_in import AnalogIn  # noqa
 from adafruit_ads1x15.ads1x15 import Mode  # noqa
 from adafruit_mcp230xx.mcp23008 import MCP23008  # noqa
 from digitalio import Direction  # noqa
+from busio import I2C  # noqa
 import time
-import numpy as np
 import os
+import numpy as np
 from ohmpi.hardware_components import TxAbstract, RxAbstract
-ctl_name = HARDWARE_CONFIG['ctl'].pop('board_name', 'raspberry_pi')
-ctl_connection = HARDWARE_CONFIG['ctl'].pop('connection', 'i2c')
-ctl_module = importlib.import_module(f'ohmpi.hardware_components.{ctl_name}')
-
-TX_CONFIG = HARDWARE_CONFIG['tx']
-RX_CONFIG = HARDWARE_CONFIG['rx']
+from ohmpi.utils import enforce_specs
 
 # hardware characteristics and limitations
-# *** RX ***
-# ADC for voltage
-voltage_adc_voltage_min = 10.  # mV
-voltage_adc_voltage_max = 4500.  # mV
-sampling_rate = 20.  # Hz
-data_rate = 860.  # S/s?
-
-RX_CONFIG['voltage_min'] = np.min([voltage_adc_voltage_min, RX_CONFIG.pop('voltage_min', np.inf)])  # mV
-RX_CONFIG['voltage_max'] = np.min([voltage_adc_voltage_max, RX_CONFIG.pop('voltage_max', np.inf)])  # mV
-RX_CONFIG['sampling_rate'] = RX_CONFIG.pop('sampling_rate', sampling_rate)
-RX_CONFIG['data_rate'] = RX_CONFIG.pop('data_rate', data_rate)
-RX_CONFIG['coef_p2'] = RX_CONFIG.pop('coef_p2', 2.5)
-RX_CONFIG['latency'] = RX_CONFIG.pop('latency', 0.01)
-RX_CONFIG['bias'] = RX_CONFIG.pop('bias', 0.)
-
-
-# *** TX ***
-# ADC for current
-current_adc_voltage_min = 10.  # mV
-current_adc_voltage_max = 4500.  # mV
-low_battery = 12.  # V (conventional value as it is not measured on this board)
-tx_mcp_board_address = 0x20  #
-# pwr_voltage_max = 12.  # V
-# pwr_default_voltage = 12.  # V
-# pwr_switch_on_warmup = 0.  # seconds
-
-TX_CONFIG['current_min'] = np.min([current_adc_voltage_min / (TX_CONFIG['r_shunt'] * 50),
-                                   TX_CONFIG.pop('current_min', np.inf)])  # mA
-TX_CONFIG['current_max'] = np.min([current_adc_voltage_max / (TX_CONFIG['r_shunt'] * 50),
-                                   TX_CONFIG.pop('current_max', np.inf)])  # mA
-# TX_CONFIG['voltage_max'] = np.min([pwr_voltage_max, TX_CONFIG.pop('voltage_max', np.inf)])  # V
-TX_CONFIG['voltage_max'] = TX_CONFIG.pop('voltage_max', np.inf)  # V
-TX_CONFIG['voltage_min'] = -TX_CONFIG['voltage_max']  # V
-TX_CONFIG['default_voltage'] = np.min([TX_CONFIG.pop('default_voltage', np.inf), TX_CONFIG['voltage_max']])  # V
-# TX_CONFIG['pwr_switch_on_warm_up'] = TX_CONFIG.pop('pwr_switch_on_warmup', pwr_switch_on_warmup)
-TX_CONFIG['mcp_board_address'] = TX_CONFIG.pop('mcp_board_address', tx_mcp_board_address)
-TX_CONFIG['low_battery'] = TX_CONFIG.pop('low_battery', low_battery)
-TX_CONFIG['latency'] = TX_CONFIG.pop('latency', 0.01)
-TX_CONFIG['bias'] = TX_CONFIG.pop('bias', 0.)
-
-
-def _gain_auto(channel):
+# voltages are given in mV, currents in mA, sampling rates in Hz and data_rate in S/s
+SPECS = {'rx': {'model': {'default': os.path.basename(__file__).rstrip('.py')},
+                'sampling_rate': {'min': 2., 'default': 10., 'max': 100.},
+                'data_rate': {'default': 860.},
+                'bias':  {'min': -5000., 'default': 0., 'max': 5000.},
+                'coef_p2': {'default': 2.50},
+                'mcp_address': {'default': None},
+                'ads_address': {'default': 0x49},
+                'voltage_min': {'default': 10.0},
+                'vmn_hardware_offset': {'default': 0.}
+                },
+         'tx': {'model': {'default': os.path.basename(__file__).rstrip('.py')},
+                'adc_voltage_min': {'default': 10.},  # Minimum voltage value used in vmin strategy
+                'adc_voltage_max': {'default': 4500.},  # Maximum voltage on ads1115 used to measure current
+                'voltage_max': {'min': 0., 'default': 12., 'max': 12.},  # Maximum input voltage
+                'data_rate': {'default': 860.},
+                'mcp_address': {'default': 0x20},
+                'ads_address': {'default': 0x48},
+                'compatible_power_sources': {'default': ['pwr_batt', 'dps5005']},
+                'r_shunt':  {'min': 0., 'default': 2. },
+                'activation_delay': {'default': 0.005},  # Max turn on time of 211EH relays = 5ms
+                'release_delay': {'default': 0.001},  # Max turn off time of 211EH relays = 1ms
+                }}
+
+# TODO: move low_battery spec in pwr
+
+
+def _ads_1115_gain_auto(channel):  # Make it a class method ?
     """Automatically sets the gain on a channel
 
     Parameters
@@ -87,25 +67,36 @@ def _gain_auto(channel):
 
 class Tx(TxAbstract):
     def __init__(self, **kwargs):
-        kwargs.update({'board_name': os.path.basename(__file__).rstrip('.py')})
+        if 'model' not in kwargs.keys():
+            for key in SPECS['tx'].keys():
+                kwargs = enforce_specs(kwargs, SPECS['tx'], key)
+            subclass_init = False
+        else:
+            subclass_init = True
         super().__init__(**kwargs)
-        self.exec_logger.event(f'{self.board_name}\ttx_init\tbegin\t{datetime.datetime.utcnow()}')
-        self._voltage = kwargs.pop('voltage', TX_CONFIG['default_voltage'])
+        if not subclass_init:
+            self.exec_logger.event(f'{self.model}\ttx_init\tbegin\t{datetime.datetime.utcnow()}')
+        assert isinstance(self.connection, I2C)
+        kwargs.update({'pwr': kwargs.pop('pwr', SPECS['tx']['compatible_power_sources']['default'][0])})
+        if (kwargs['pwr'] not in SPECS['tx']['compatible_power_sources']['default']):
+            self.exec_logger.warning(f'Incompatible power source specified check config')
+            assert kwargs['pwr'] in SPECS['tx']
+        self._activation_delay = kwargs['activation_delay']
+        self._release_delay = kwargs['release_delay']
         self.voltage_adjustable = False
         self.current_adjustable = False
-        if self.ctl is None:
-            self.ctl = ctl_module.Ctl()
-        # elif isinstance(self.ctl, dict):
-        #     self.ctl = ctl_module.Ctl(**self.ctl)
-        self.connection = self.ctl.interfaces[kwargs.pop('connection', ctl_connection)]
 
         # I2C connexion to MCP23008, for current injection
-        self.mcp_board = MCP23008(self.connection, address=TX_CONFIG['mcp_board_address'])
+        self.mcp_board = MCP23008(self.connection, address=kwargs['mcp_address'])
         # ADS1115 for current measurement (AB)
-        self._ads_current_address = 0x48
-        self._ads_current = ads.ADS1115(self.connection, gain=self.adc_gain, data_rate=860,
+        self._ads_current_address = kwargs['ads_address']
+        self._ads_current_data_rate = kwargs['data_rate']
+        self._ads_current = ads.ADS1115(self.connection, gain=self.adc_gain, data_rate=self._ads_current_data_rate,
                                         address=self._ads_current_address)
         self._ads_current.mode = Mode.CONTINUOUS
+        self.r_shunt = kwargs['r_shunt']
+        self.adc_voltage_min = kwargs['adc_voltage_min']
+        self.adc_voltage_max = kwargs['adc_voltage_max']
 
         # Relays for pulse polarity
         self.pin0 = self.mcp_board.get_pin(0)
@@ -113,59 +104,54 @@ class Tx(TxAbstract):
         self.pin1 = self.mcp_board.get_pin(1)
         self.pin1.direction = Direction.OUTPUT
         self.polarity = 0
-        self.adc_gain = 2 / 3
-
-        self.pwr = None  # TODO: set a list of compatible power system with the tx
-
-        # MCP23008 pins for LEDs
-        self.pin4 = self.mcp_board.get_pin(4)  # TODO: Delete me? No LED on this version of the board
-        self.pin4.direction = Direction.OUTPUT
-        self.pin4.value = True
-
-        self._latency = kwargs.pop('latency', TX_CONFIG['latency'])
-        self._bias = kwargs.pop('bias', TX_CONFIG['bias'])
-        self.exec_logger.event(f'{self.board_name}\ttx_init\tend\t{datetime.datetime.utcnow()}')
+        self.gain = 2 / 3
+        if not subclass_init:
+            self.exec_logger.event(f'{self.model}\ttx_init\tend\t{datetime.datetime.utcnow()}')
 
     @property
-    def adc_gain(self):
+    def gain(self):
         return self._adc_gain
 
-    @adc_gain.setter
-    def adc_gain(self, value):
+    @gain.setter
+    def gain(self, value):
         assert value in [2/3, 2, 4, 8, 16]
         self._adc_gain = value
-        self._ads_current = ads.ADS1115(self.connection, gain=self.adc_gain, data_rate=860,
+        self._ads_current = ads.ADS1115(self.connection, gain=self.adc_gain,
+                                        data_rate=SPECS['tx']['data_rate']['default'],
                                         address=self._ads_current_address)
         self._ads_current.mode = Mode.CONTINUOUS
         self.exec_logger.debug(f'Setting TX ADC gain to {value}')
 
-    def adc_gain_auto(self):
-        self.exec_logger.event(f'{self.board_name}\ttx_adc_auto_gain\tbegin\t{datetime.datetime.utcnow()}')
-        gain = _gain_auto(AnalogIn(self._ads_current, ads.P0))
+    def _adc_gain_auto(self):
+        self.exec_logger.event(f'{self.model}\ttx_adc_auto_gain\tbegin\t{datetime.datetime.utcnow()}')
+        gain = _ads_1115_gain_auto(AnalogIn(self._ads_current, ads.P0))
         self.exec_logger.debug(f'Setting TX ADC gain automatically to {gain}')
-        self.adc_gain = gain
-        self.exec_logger.event(f'{self.board_name}\ttx_adc_auto_gain\tend\t{datetime.datetime.utcnow()}')
+        self.gain = gain
+        self.exec_logger.event(f'{self.model}\ttx_adc_auto_gain\tend\t{datetime.datetime.utcnow()}')
 
     def current_pulse(self, **kwargs):
         TxAbstract.current_pulse(self, **kwargs)
-        self.exec_logger.warning(f'Current pulse is not implemented for the {TX_CONFIG["model"]} board')
+        self.exec_logger.warning(f'Current pulse is not implemented for the {self.model} board')
 
     @property
     def current(self):
         """ Gets the current IAB in Amps
         """
-        iab = AnalogIn(self._ads_current, ads.P0).voltage * 1000. / (50 * TX_CONFIG['r_shunt'])  # measure current
+        iab = AnalogIn(self._ads_current, ads.P0).voltage * 1000. / (50 * self.r_shunt)  # measure current
         self.exec_logger.debug(f'Reading TX current:  {iab} mA')
         return iab
 
     @ current.setter
     def current(self, value):
-        assert TX_CONFIG['current_min'] <= value <= TX_CONFIG['current_max']
-        self.exec_logger.warning(f'Current pulse is not implemented for the {TX_CONFIG["model"]} board')
+        assert self.adc_voltage_min / (50 * self.r_shunt)  <= value <= self.adc_voltage_max / (50 * self.r_shunt)
+        self.exec_logger.warning(f'Current pulse is not implemented for the {self.model} board')
 
-    def inject(self, polarity=1, injection_duration=None):
+    def gain_auto(self):
+        self._adc_gain_auto()
+
+    def inject(self, polarity=1, injection_duration=None, switch_pwr=False):
         self.polarity = polarity
-        TxAbstract.inject(self, polarity=polarity, injection_duration=injection_duration)
+        TxAbstract.inject(self, polarity=polarity, injection_duration=injection_duration, switch_pwr=switch_pwr)
 
     @property
     def polarity(self):
@@ -178,29 +164,33 @@ class Tx(TxAbstract):
         if polarity == 1:
             self.pin0.value = True
             self.pin1.value = False
-            time.sleep(0.005)  # Max turn on time of 211EH relays = 5ms
+            time.sleep(self._activation_delay)
         elif polarity == -1:
             self.pin0.value = False
             self.pin1.value = True
-            time.sleep(0.005)  # Max turn on time of 211EH relays = 5ms
+            time.sleep(self._activation_delay)
         else:
             self.pin0.value = False
             self.pin1.value = False
-            time.sleep(0.001)  # Max turn off time of 211EH relays = 1ms
-
-    def turn_off(self):
-        self.pwr.turn_off(self)
-
-    def turn_on(self):
-        self.pwr.turn_on(self)
+            time.sleep(self._release_delay)
+    #
+    # def turn_off(self):
+    #     self.pwr.turn_off(self)
+    #
+    # def turn_on(self):
+    #     self.pwr.turn_on(self)
 
     @property
     def tx_bat(self):
-        self.soh_logger.warning(f'Cannot get battery voltage on {self.board_name}')
-        self.exec_logger.debug(f'{self.board_name} cannot read battery voltage. Returning default battery voltage.')
-        return TX_CONFIG['low_battery']
+        if np.isnan(self.tx.pwr.battery_voltage):
+            self.soh_logger.warning(f'Cannot get battery voltage on {self.model}')
+            self.exec_logger.debug(f'{self.model} cannot read battery voltage. Returning default battery voltage.')
+            return self.pwr.voltage
+        else:
+            return self.tx.pwr.battery_voltage
+
 
-    def voltage_pulse(self, voltage=TX_CONFIG['default_voltage'], length=None, polarity=1):
+    def voltage_pulse(self, voltage=None, length=None, polarity=1):
         """ Generates a square voltage pulse
 
         Parameters
@@ -212,65 +202,72 @@ class Tx(TxAbstract):
         polarity: 1,0,-1
             Polarity of the pulse
         """
-        self.exec_logger.event(f'{self.board_name}\ttx_voltage_pulse\tbegin\t{datetime.datetime.utcnow()}')
+        self.exec_logger.event(f'{self.model}\ttx_voltage_pulse\tbegin\t{datetime.datetime.utcnow()}')
         # self.exec_logger.info(f'injection_duration: {length}')  # TODO: delete me
         if length is None:
             length = self.injection_duration
-        self.pwr.voltage = voltage
+        if voltage is not None:
+            self.pwr.voltage = voltage
         self.exec_logger.debug(f'Voltage pulse of {polarity*self.pwr.voltage:.3f} V for {length:.3f} s')
         self.inject(polarity=polarity, injection_duration=length)
-        self.exec_logger.event(f'{self.board_name}\ttx_voltage_pulse\tend\t{datetime.datetime.utcnow()}')
+        self.exec_logger.event(f'{self.model}\ttx_voltage_pulse\tend\t{datetime.datetime.utcnow()}')
 
 
 class Rx(RxAbstract):
     def __init__(self, **kwargs):
-        kwargs.update({'board_name': os.path.basename(__file__).rstrip('.py')})
+        if 'model' not in kwargs.keys():
+            for key in SPECS['rx'].keys():
+                kwargs = enforce_specs(kwargs, SPECS['rx'], key)
+            subclass_init = False
+        else:
+            subclass_init = True
         super().__init__(**kwargs)
-        self.exec_logger.event(f'{self.board_name}\trx_init\tbegin\t{datetime.datetime.utcnow()}')
-        if self.ctl is None:
-            self.ctl = ctl_module.Ctl()
-        self.connection = self.ctl.interfaces[kwargs.pop('connection', ctl_connection)]
+        if not subclass_init:
+            self.exec_logger.event(f'{self.model}\trx_init\tbegin\t{datetime.datetime.utcnow()}')
+        assert isinstance(self.connection, I2C)
 
         # ADS1115 for voltage measurement (MN)
-        self._ads_voltage_address = 0x49
+        self._ads_voltage_address = kwargs['ads_address']
         self._adc_gain = 2/3
-        self._ads_voltage = ads.ADS1115(self.connection, gain=self._adc_gain, data_rate=860,
+        self._ads_voltage = ads.ADS1115(self.connection, gain=self._adc_gain,
+                                        data_rate=SPECS['rx']['data_rate']['default'],
                                         address=self._ads_voltage_address)
         self._ads_voltage.mode = Mode.CONTINUOUS
-        self._coef_p2 = kwargs.pop('coef_p2', RX_CONFIG['coef_p2'])
-        self._voltage_max = kwargs.pop('voltage_max', RX_CONFIG['voltage_max'])
-        self._sampling_rate = kwargs.pop('sampling_rate', sampling_rate)
-        self._latency = kwargs.pop('latency', RX_CONFIG['latency'])
-        self._bias = kwargs.pop('bias', RX_CONFIG['bias'])
-        self.exec_logger.event(f'{self.board_name}\trx_init\tend\t{datetime.datetime.utcnow()}')
+        self._coef_p2 = kwargs['coef_p2']
+        # self._voltage_max = kwargs['voltage_max']
+        self._sampling_rate = kwargs['sampling_rate']
+        self._bias = kwargs['bias']
+        if not subclass_init:
+            self.exec_logger.event(f'{self.model}\trx_init\tend\t{datetime.datetime.utcnow()}')
 
     @property
-    def adc_gain(self):
+    def gain(self): #TODO: should be in abstract_hardware_components
         return self._adc_gain
 
-    @adc_gain.setter
-    def adc_gain(self, value):
+    @gain.setter
+    def gain(self, value):
         assert value in [2/3, 2, 4, 8, 16]
         self._adc_gain = value
-        self._ads_voltage = ads.ADS1115(self.connection, gain=self.adc_gain, data_rate=860,
+        self._ads_voltage = ads.ADS1115(self.connection, gain=self.adc_gain,
+                                        data_rate=SPECS['rx']['data_rate']['default'],
                                         address=self._ads_voltage_address)
         self._ads_voltage.mode = Mode.CONTINUOUS
         self.exec_logger.debug(f'Setting RX ADC gain to {value}')
 
-    def adc_gain_auto(self):
-        self.exec_logger.event(f'{self.board_name}\trx_adc_auto_gain\tbegin\t{datetime.datetime.utcnow()}')
-        gain_0 = _gain_auto(AnalogIn(self._ads_voltage, ads.P0))
-        gain_2 = _gain_auto(AnalogIn(self._ads_voltage, ads.P2))
-        gain = np.min([gain_0, gain_2])
+    def _adc_gain_auto(self):
+        self.exec_logger.event(f'{self.model}\trx_adc_auto_gain\tbegin\t{datetime.datetime.utcnow()}')
+        gain = _ads_1115_gain_auto(AnalogIn(self._ads_voltage, ads.P0, ads.P1))
         self.exec_logger.debug(f'Setting RX ADC gain automatically to {gain}')
-        self.adc_gain = gain
-        self.exec_logger.event(f'{self.board_name}\trx_adc_auto_gain\tend\t{datetime.datetime.utcnow()}')
+        self._adc_gain = gain
+        self.exec_logger.event(f'{self.model}\trx_adc_auto_gain\tend\t{datetime.datetime.utcnow()}')
 
+    def gain_auto(self):
+        self._adc_gain_auto()
     @property
     def voltage(self):
         """ Gets the voltage VMN in Volts
         """
-        self.exec_logger.event(f'{self.board_name}\trx_voltage\tbegin\t{datetime.datetime.utcnow()}')
+        self.exec_logger.event(f'{self.model}\trx_voltage\tbegin\t{datetime.datetime.utcnow()}')
         u = -AnalogIn(self._ads_voltage, ads.P0, ads.P1).voltage * self._coef_p2 * 1000. - self._bias  # TODO: check if it should be negated
-        self.exec_logger.event(f'{self.board_name}\trx_voltage\tend\t{datetime.datetime.utcnow()}')
+        self.exec_logger.event(f'{self.model}\trx_voltage\tend\t{datetime.datetime.utcnow()}')
         return u
diff --git a/ohmpi/hardware_components/mb_2024_0_2.py b/ohmpi/hardware_components/mb_2024_0_2.py
index 0375baf575b6903b348fe07b5a3f974db67b9aa2..bd09972fa8abba6e4c949155dbed6ca1830ff36f 100644
--- a/ohmpi/hardware_components/mb_2024_0_2.py
+++ b/ohmpi/hardware_components/mb_2024_0_2.py
@@ -1,67 +1,46 @@
 import datetime
-import importlib
-from ohmpi.config import HARDWARE_CONFIG  # TODO: Remove references at config here -> move it in ohmpi_hardware as done for mux_2024
 import adafruit_ads1x15.ads1115 as ads  # noqa
 from adafruit_ads1x15.analog_in import AnalogIn  # noqa
 from adafruit_ads1x15.ads1x15 import Mode  # noqa
 from adafruit_mcp230xx.mcp23008 import MCP23008  # noqa
 from digitalio import Direction  # noqa
-import minimalmodbus  # noqa
-import time
-import numpy as np
+from busio import I2C  # noqa
 import os
-from ohmpi.hardware_components import TxAbstract, RxAbstract
-ctl_name = HARDWARE_CONFIG['ctl'].pop('board_name', 'raspberry_pi')
-ctl_connection = HARDWARE_CONFIG['ctl'].pop('connection', 'i2c')
-ctl_module = importlib.import_module(f'ohmpi.hardware_components.{ctl_name}')
-
-TX_CONFIG = HARDWARE_CONFIG['tx']
-RX_CONFIG = HARDWARE_CONFIG['rx']
+import time
+from ohmpi.utils import enforce_specs
+from ohmpi.hardware_components.mb_2023_0_X import Tx as Tx_mb_2023
+from ohmpi.hardware_components.mb_2023_0_X import Rx as Rx_mb_2023
 
 # hardware characteristics and limitations
-# *** RX ***
-# ADC for voltage
-voltage_adc_voltage_min = 10.  # mV
-voltage_adc_voltage_max = 4500.  # mV
-sampling_rate = 20.  # Hz
-data_rate = 860.  # S/s?
-rx_mcp_board_address = 0x27
-RX_CONFIG['voltage_min'] = np.min([voltage_adc_voltage_min, RX_CONFIG.pop('voltage_min', np.inf)])  # mV
-RX_CONFIG['voltage_max'] = np.min([voltage_adc_voltage_max, RX_CONFIG.pop('voltage_max', np.inf)])  # mV
-RX_CONFIG['sampling_rate'] = RX_CONFIG.pop('sampling_rate', sampling_rate)
-RX_CONFIG['data_rate'] = RX_CONFIG.pop('data_rate', data_rate)
-# RX_CONFIG['coef_p2'] = RX_CONFIG.pop('coef_p2', 2.5)
-RX_CONFIG['latency'] = RX_CONFIG.pop('latency', 0.01)
-RX_CONFIG['bias'] = RX_CONFIG.pop('bias', 0.)
-RX_CONFIG['mcp_board_address'] = TX_CONFIG.pop('mcp_board_address', tx_mcp_board_address)
-
-
-# *** TX ***
-# ADC for current
-current_adc_voltage_min = 10.  # mV
-current_adc_voltage_max = 4500.  # mV
-low_battery = 12.  # V (conventional value as it is not measured on this board)
-tx_mcp_board_address = 0x21  #
-# pwr_voltage_max = 12.  # V
-# pwr_default_voltage = 12.  # V
-# pwr_switch_on_warmup = 0.  # seconds
-
-TX_CONFIG['current_min'] = np.min([current_adc_voltage_min / (TX_CONFIG['r_shunt'] * 50),
-                                   TX_CONFIG.pop('current_min', np.inf)])  # mA
-TX_CONFIG['current_max'] = np.min([current_adc_voltage_max / (TX_CONFIG['r_shunt'] * 50),
-                                   TX_CONFIG.pop('current_max', np.inf)])  # mA
-# TX_CONFIG['voltage_max'] = np.min([pwr_voltage_max, TX_CONFIG.pop('voltage_max', np.inf)])  # V
-TX_CONFIG['voltage_max'] = TX_CONFIG.pop('voltage_max', np.inf)  # V
-TX_CONFIG['voltage_min'] = -TX_CONFIG['voltage_max']  # V
-TX_CONFIG['default_voltage'] = np.min([TX_CONFIG.pop('default_voltage', np.inf), TX_CONFIG['voltage_max']])  # V
-# TX_CONFIG['pwr_switch_on_warm_up'] = TX_CONFIG.pop('pwr_switch_on_warmup', pwr_switch_on_warmup)
-TX_CONFIG['mcp_board_address'] = TX_CONFIG.pop('mcp_board_address', tx_mcp_board_address)
-TX_CONFIG['low_battery'] = TX_CONFIG.pop('low_battery', low_battery)
-TX_CONFIG['latency'] = TX_CONFIG.pop('latency', 0.01)
-TX_CONFIG['bias'] = TX_CONFIG.pop('bias', 0.)
-
-
-def _gain_auto(channel):
+# voltages are given in mV, currents in mA, sampling rates in Hz and data_rate in S/s
+SPECS = {'rx': {'model': {'default': os.path.basename(__file__).rstrip('.py')},
+                'sampling_rate': {'min': 2., 'default': 10., 'max': 100.},
+                'data_rate': {'default': 860.},
+                'bias':  {'min': -5000., 'default': 0., 'max': 5000.},
+                'coef_p2': {'default': 1.00},
+                'mcp_address': {'default': 0x27},
+                'ads_address': {'default': 0x49},
+                'voltage_min': {'default': 10.0},
+                'vmn_hardware_offset': {'default': 2500.},
+                },
+         'tx': {'model': {'default': os.path.basename(__file__).rstrip('.py')},
+                'adc_voltage_min': {'default': 10.},  # Minimum voltage value used in vmin strategy
+                'adc_voltage_max': {'default': 4500.},  # Maximum voltage on ads1115 used to measure current
+                'voltage_max': {'min': 0., 'default': 12., 'max': 12.},  # Maximum input voltage
+                'data_rate': {'default': 860.},
+                'mcp_address': {'default': 0x21},
+                'ads_address': {'default': 0x48},
+                'compatible_power_sources': {'default': ['pwr_batt', 'dps5005']},
+                'r_shunt':  {'min': 0., 'default': 2.},
+                'activation_delay': {'default': 0.010},  # Max turn on time of OMRON G5LE-1 5VDC relays
+                'release_delay': {'default': 0.005},  # Max turn off time of OMRON G5LE-1 5VDC relays = 1ms
+                'pwr_latency': {'default': 4.}
+                }}
+
+# TODO: move low_battery spec in pwr
+
+
+def _ads_1115_gain_auto(channel):  # Make it a class method ?
     """Automatically sets the gain on a channel
 
     Parameters
@@ -87,227 +66,137 @@ def _gain_auto(channel):
     return gain
 
 
-class Tx(TxAbstract):
+class Tx(Tx_mb_2023):
     def __init__(self, **kwargs):
-        kwargs.update({'board_name': os.path.basename(__file__).rstrip('.py')})
+        if 'model' not in kwargs.keys():
+            for key in SPECS['tx'].keys():
+                kwargs = enforce_specs(kwargs, SPECS['tx'], key)
+            subclass_init = False
+        else:
+            subclass_init = True
         super().__init__(**kwargs)
-        self.exec_logger.event(f'{self.board_name}\ttx_init\tbegin\t{datetime.datetime.utcnow()}')
-        self._voltage = kwargs.pop('voltage', TX_CONFIG['default_voltage'])
-        self.voltage_adjustable = False
-        self.current_adjustable = False
-        if self.ctl is None:
-            self.ctl = ctl_module.Ctl()
-        # elif isinstance(self.ctl, dict):
-        #     self.ctl = ctl_module.Ctl(**self.ctl)
-        self.io = self.ctl.interfaces[kwargs.pop('connection', ctl_connection)]
-
-        # I2C connexion to MCP23008, for current injection
-        self.mcp_board = MCP23008(self.io, address=TX_CONFIG['mcp_board_address'])
-        # ADS1115 for current measurement (AB)
-        self._ads_current_address = 0x48
-        self._ads_current = ads.ADS1115(self.ctl.bus, gain=self.adc_gain, data_rate=860,
-                                        address=self._ads_current_address)
-        self._ads_current.mode = Mode.CONTINUOUS
-
-        # Relays for pulse polarity
-        self.pin0 = self.mcp_board.get_pin(0)
-        self.pin0.direction = Direction.OUTPUT
-        self.pin1 = self.mcp_board.get_pin(1)
-        self.pin1.direction = Direction.OUTPUT
-        self.polarity = 0
-        self.adc_gain = 2 / 3
-
-        self.pwr = None  # TODO: set a list of compatible power system with the tx
+        if not subclass_init:
+            self.exec_logger.event(f'{self.model}\ttx_init\tbegin\t{datetime.datetime.utcnow()}')
+        self._pwr_latency = kwargs['pwr_latency']
 
         # Initialize LEDs
         self.pin4 = self.mcp_board.get_pin(4)  # Ohmpi_run
         self.pin4.direction = Direction.OUTPUT
         self.pin4.value = True
-
-        self._latency = kwargs.pop('latency', TX_CONFIG['latency'])
-        self._bias = kwargs.pop('bias', TX_CONFIG['bias'])
-        self.exec_logger.event(f'{self.board_name}\ttx_init\tend\t{datetime.datetime.utcnow()}')
-
-    @property
-    def adc_gain(self):
-        return self._adc_gain
-
-    @adc_gain.setter
-    def adc_gain(self, value):
-        assert value in [2/3, 2, 4, 8, 16]
-        self._adc_gain = value
-        self._ads_current = ads.ADS1115(self.ctl.bus, gain=self.adc_gain, data_rate=860,
-                                        address=self._ads_current_address)
-        self._ads_current.mode = Mode.CONTINUOUS
-        self.exec_logger.debug(f'Setting TX ADC gain to {value}')
-
-    def adc_gain_auto(self):
-        self.exec_logger.event(f'{self.board_name}\ttx_adc_auto_gain\tbegin\t{datetime.datetime.utcnow()}')
-        gain = _gain_auto(AnalogIn(self._ads_current, ads.P0))
-        self.exec_logger.debug(f'Setting TX ADC gain automatically to {gain}')
-        self.adc_gain = gain
-        self.exec_logger.event(f'{self.board_name}\ttx_adc_auto_gain\tend\t{datetime.datetime.utcnow()}')
-
-    def current_pulse(self, **kwargs):
-        TxAbstract.current_pulse(self, **kwargs)
-        self.exec_logger.warning(f'Current pulse is not implemented for the {TX_CONFIG["model"]} board')
-
-    @property
-    def current(self):
-        """ Gets the current IAB in Amps
-        """
-        iab = AnalogIn(self._ads_current, ads.P0).voltage * 1000. / (50 * TX_CONFIG['r_shunt'])  # measure current
-        self.exec_logger.debug(f'Reading TX current:  {iab} mA')
-        return iab
-
-    @ current.setter
-    def current(self, value):
-        assert TX_CONFIG['current_min'] <= value <= TX_CONFIG['current_max']
-        self.exec_logger.warning(f'Current pulse is not implemented for the {TX_CONFIG["model"]} board')
+        self.pin6 = self.mcp_board.get_pin(6)
+        self.pin6.direction = Direction.OUTPUT
+        self.pin6.value = False
+        self.pin2 = self.mcp_board.get_pin(2) # dsp -
+        self.pin2.direction = Direction.OUTPUT
+        self.pin2.value = False
+        self.pin3 = self.mcp_board.get_pin(3) # dsp -
+        self.pin3.direction = Direction.OUTPUT
+        self.pin3.value = False
+
+        if not subclass_init:
+            self.exec_logger.event(f'{self.model}\ttx_init\tend\t{datetime.datetime.utcnow()}')
 
     def inject(self, polarity=1, injection_duration=None):
-        self.polarity = polarity
-        TxAbstract.inject(self, polarity=polarity, injection_duration=injection_duration)
-
-    @property
-    def polarity(self):
-        return self._polarity
-
-    @polarity.setter
-    def polarity(self, polarity):
-        assert polarity in [-1, 0, 1]
-        self._polarity = polarity
-        if polarity == 1:
-            self.pin0.value = True
-            self.pin1.value = False
-            time.sleep(0.005)  # Max turn on time of 211EH relays = 5ms
-        elif polarity == -1:
-            self.pin0.value = False
-            self.pin1.value = True
-            time.sleep(0.005)  # Max turn on time of 211EH relays = 5ms
-        else:
-            self.pin0.value = False
-            self.pin1.value = False
-            time.sleep(0.001)  # Max turn off time of 211EH relays = 1ms
-
-    def turn_off(self):
-        self.pwr.turn_off(self)
-
-    def turn_on(self):
-        self.pwr.turn_on(self)
+        # add leds?
+        self.pin6.value = True
+        Tx_mb_2023.inject(self, polarity=polarity, injection_duration=injection_duration)
+        self.pin6.value = False
 
     @property
-    def tx_bat(self):
-        self.soh_logger.warning(f'Cannot get battery voltage on {self.board_name}')
-        self.exec_logger.debug(f'{self.board_name} cannot read battery voltage. Returning default battery voltage.')
-        return TX_CONFIG['low_battery']
-
-    def voltage_pulse(self, voltage=TX_CONFIG['default_voltage'], length=None, polarity=1):
-        """ Generates a square voltage pulse
-
-        Parameters
-        ----------
-        voltage: float, optional
-            Voltage to apply in volts, tx_v_def is applied if omitted.
-        length: float, optional
-            Length of the pulse in seconds
-        polarity: 1,0,-1
-            Polarity of the pulse
-        """
-        self.exec_logger.event(f'{self.board_name}\ttx_voltage_pulse\tbegin\t{datetime.datetime.utcnow()}')
-        # self.exec_logger.info(f'injection_duration: {length}')  # TODO: delete me
-        if length is None:
-            length = self.injection_duration
-        self.pwr.voltage = voltage
-        self.exec_logger.debug(f'Voltage pulse of {polarity*self.pwr.voltage:.3f} V for {length:.3f} s')
-        self.inject(polarity=polarity, injection_duration=length)
-        self.exec_logger.event(f'{self.board_name}\ttx_voltage_pulse\tend\t{datetime.datetime.utcnow()}')
-
-
-class Rx(RxAbstract):
+    def pwr_state(self):
+        return self._pwr_state
+
+    @pwr_state.setter
+    def pwr_state(self, state):
+        """Switches pwr on or off.
+
+            Parameters
+            ----------
+            state : str
+                'on', 'off'
+            """
+        if state == 'on':
+            self.pin2.value = True
+            self.pin3.value = True
+            self.exec_logger.debug(f'Switching DPS on')
+            self._pwr_state = 'on'
+            time.sleep(self._pwr_latency) # from pwr specs
+        elif state == 'off':
+            self.pin2.value = False
+            self.pin3.value = False
+            self.exec_logger.debug(f'Switching DPS off')
+            self._pwr_state = 'off'
+
+
+class Rx(Rx_mb_2023):
     def __init__(self, **kwargs):
-        kwargs.update({'board_name': os.path.basename(__file__).rstrip('.py')})
+        if 'model' not in kwargs.keys():
+            for key in SPECS['rx'].keys():
+                kwargs = enforce_specs(kwargs, SPECS['rx'], key)
+            subclass_init = False
+        else:
+            subclass_init = True
         super().__init__(**kwargs)
-        self.exec_logger.event(f'{self.board_name}\trx_init\tbegin\t{datetime.datetime.utcnow()}')
-        if self.ctl is None:
-            self.ctl = ctl_module.Ctl()
-        self.io = self.ctl.interfaces[kwargs.pop('connection', ctl_connection)]
-
-        # I2C connexion to MCP23008, for DG411
-        self.mcp_board = MCP23008(self.io, address=RX_CONFIG['mcp_board_address'])
-
+        if not subclass_init:
+            self.exec_logger.event(f'{self.model}\trx_init\tbegin\t{datetime.datetime.utcnow()}')
+        # I2C connection to MCP23008, for voltage
+        self.mcp_board = MCP23008(self.connection, address=kwargs['mcp_address'])
         # ADS1115 for voltage measurement (MN)
-        self._ads_voltage_address = 0x49
-        self._adc_gain = 2/3
-        self._ads_voltage = ads.ADS1115(self.ctl.bus, gain=self._adc_gain, data_rate=860,
-                                        address=self._ads_voltage_address)
-        self._ads_voltage.mode = Mode.CONTINUOUS
-        self._coef_p2 = kwargs.pop('coef_p2', RX_CONFIG['coef_p2'])
-        self._voltage_max = kwargs.pop('voltage_max', RX_CONFIG['voltage_max'])
-        self._sampling_rate = kwargs.pop('sampling_rate', sampling_rate)
-        self._latency = kwargs.pop('latency', RX_CONFIG['latency'])
-        self._bias = kwargs.pop('bias', RX_CONFIG['bias'])
-        self.exec_logger.event(f'{self.board_name}\trx_init\tend\t{datetime.datetime.utcnow()}')
-
+        self._coef_p2 = 1.
+        # Define default DG411 gain
+        self._dg411_gain = 1/2
+        # Define pins for DG411
         self.pin_DG0 = self.mcp_board.get_pin(0)
         self.pin_DG0.direction = Direction.OUTPUT
         self.pin_DG1 = self.mcp_board.get_pin(1)
         self.pin_DG1.direction = Direction.OUTPUT
         self.pin_DG2 = self.mcp_board.get_pin(2)
         self.pin_DG2.direction = Direction.OUTPUT
-
         self.pin_DG0.value = True  # open
         self.pin_DG1.value = True  # open gain 1 inactive
         self.pin_DG2.value = False  # close gain 0.5 active
-        self._voltage_gain = 0.5
+        self.gain = 1/3
+        if not subclass_init:  # TODO: try to only log this event and not the one created by super()
+            self.exec_logger.event(f'{self.model}\trx_init\tend\t{datetime.datetime.utcnow()}')
 
-    @property
-    def adc_gain(self):
-        return self._adc_gain
-
-    @adc_gain.setter
-    def adc_gain(self, value):
-        assert value in [2/3, 2, 4, 8, 16]
-        self._adc_gain = value
-        self._ads_voltage = ads.ADS1115(self.ctl.bus, gain=self.adc_gain, data_rate=860,
-                                        address=self._ads_voltage_address)
-        self._ads_voltage.mode = Mode.CONTINUOUS
-        self.exec_logger.debug(f'Setting RX ADC gain to {value}')
-
-    def adc_gain_auto(self):
-        self.exec_logger.event(f'{self.board_name}\trx_adc_auto_gain\tbegin\t{datetime.datetime.utcnow()}')
-        gain = 2/3
+    def _adc_gain_auto(self):
+        self.exec_logger.event(f'{self.model}\trx_adc_auto_gain\tbegin\t{datetime.datetime.utcnow()}')
+        gain = _ads_1115_gain_auto(AnalogIn(self._ads_voltage, ads.P0))
         self.exec_logger.debug(f'Setting RX ADC gain automatically to {gain}')
-        self.adc_gain = gain
-        self.exec_logger.event(f'{self.board_name}\trx_adc_auto_gain\tend\t{datetime.datetime.utcnow()}')
+        self._adc_gain = gain
+        self.exec_logger.event(f'{self.model}\trx_adc_auto_gain\tend\t{datetime.datetime.utcnow()}')
 
-    @property
-    def voltage(self):
-        """ Gets the voltage VMN in Volts
-        """
-        self.exec_logger.event(f'{self.board_name}\trx_voltage\tbegin\t{datetime.datetime.utcnow()}')
-        u = -AnalogIn(self._ads_voltage, ads.P0, ads.P1).voltage * self._coef_p2 * 1000. - self._bias  # TODO: check if it should be negated
-        self.exec_logger.event(f'{self.board_name}\trx_voltage\tend\t{datetime.datetime.utcnow()}')
-        return u
+    def _dg411_gain_auto(self):
+        if self.voltage < self._vmn_hardware_offset :
+            self._dg411_gain = 1.
+        else:
+            self._dg411_gain = 1/2
+        self.exec_logger.debug(f'Setting RX DG411 gain automatically to {self._dg411_gain}')
 
     @property
-    def voltage_gain(self):
-        return self._voltage_gain
-
-    @voltage_gain.setter
-    def voltage_gain(self,value):
-        assert value in [0.5, 1]
-        self._voltage_gain = value
-        if self._voltage_gain == 1:
+    def gain(self):
+        return self._adc_gain*self._dg411_gain
+
+    @gain.setter
+    def gain(self, value):
+        assert value in [1/3, 2/3]
+        self._dg411_gain = value / self._adc_gain
+        if self._dg411_gain == 1.:
             self.pin_DG1.value = False  # closed gain 1 active
             self.pin_DG2.value = True  # open gain 0.5 inactive
-        elif self._voltage_gain == 0.5:
+        elif self._dg411_gain == 1/2:
             self.pin_DG1.value = True  # closed gain 1 active
             self.pin_DG2.value = False  # open gain 0.5 inactive
 
-    def voltage_gain_auto(self):
-        u = ((AnalogIn(self.ads_voltage, ads.P0).voltage * 1000) - self.vmn_hardware_offset) / self.voltage_gain
-        if abs(vmn1) < 2500 and abs(vmn2) < 2500:  ###TODO change voltage gain auto logic
-            self.voltage_gain = 1
-        else:
-            self.voltage_gain = 0.5
+    def gain_auto(self):
+        self._dg411_gain_auto()
+        self.exec_logger.debug(f'Setting RX gain automatically to {self.gain}')
+
+    @property
+    def voltage(self):
+        """ Gets the voltage VMN in Volts
+        """
+        self.exec_logger.event(f'{self.model}\trx_voltage\tbegin\t{datetime.datetime.utcnow()}')
+        u = (AnalogIn(self._ads_voltage, ads.P0).voltage * self._coef_p2 * 1000. - self._vmn_hardware_offset) / self._dg411_gain - self._bias  # TODO: check how to handle bias and _vmn_hardware_offset
+        self.exec_logger.event(f'{self.model}\trx_voltage\tend\t{datetime.datetime.utcnow()}')
+        return u
diff --git a/ohmpi/hardware_components/mux_2023_0_X.py b/ohmpi/hardware_components/mux_2023_0_X.py
index be1ec5a1ac30ab4f686e015a0c652fe863e274ee..1e88aef3f757bf1dafd718243fa602694bc72e6f 100644
--- a/ohmpi/hardware_components/mux_2023_0_X.py
+++ b/ohmpi/hardware_components/mux_2023_0_X.py
@@ -1,84 +1,95 @@
-import time
-
-from ohmpi.config import HARDWARE_CONFIG
 import os
 import numpy as np
+import datetime
 from ohmpi.hardware_components import MuxAbstract
 import adafruit_tca9548a  # noqa
 from adafruit_mcp230xx.mcp23017 import MCP23017  # noqa
 from digitalio import Direction  # noqa
+from busio import I2C  # noqa
+from ohmpi.utils import enforce_specs
+
+# TODO: manage the case when a tca is added to handle more mux_2023 boards
 
 # hardware characteristics and limitations
-SPECS = {'voltage_max': 50., 'current_max': 3., 'activation_delay': 0.01, 'release_delay': 0.005}
+SPECS = {'model': {'default': os.path.basename(__file__).rstrip('.py')},
+         'id': {'default': 'mux_??'},
+         'voltage_max': {'default': 50.},
+         'current_max': {'default': 3.},
+         'activation_delay': {'default': 0.01},
+         'release_delay': {'default': 0.005},
+         'mux_tca_address': {'default': 0x70}
+         }
 
-default_mux_cabling = {(elec, role) : ('mux_1', elec) for role in ['A', 'B', 'M', 'N'] for elec in range(1,9)} # defaults to 4 roles cabling electrodes from 1 to 8
+# defaults to role 'A' cabling electrodes from 1 to 64
+default_mux_cabling = {(elec, role): ('mux_1', elec) for role in ['A'] for elec in range(1, 64)}
+
+inner_cabling = {'1_role': {(1, 'X'): {'MCP': 0, 'MCP_GPIO': 0}, (2, 'X'): {'MCP': 0, 'MCP_GPIO': 1},
+                            (3, 'X'): {'MCP': 0, 'MCP_GPIO': 2}, (4, 'X'): {'MCP': 0, 'MCP_GPIO': 3},
+                            (5, 'X'): {'MCP': 0, 'MCP_GPIO': 4}, (6, 'X'): {'MCP': 0, 'MCP_GPIO': 5},
+                            (7, 'X'): {'MCP': 0, 'MCP_GPIO': 6}, (8, 'X'): {'MCP': 0, 'MCP_GPIO': 7},
+                            (9, 'X'): {'MCP': 0, 'MCP_GPIO': 8}, (10, 'X'): {'MCP': 0, 'MCP_GPIO': 9},
+                            (11, 'X'): {'MCP': 0, 'MCP_GPIO': 10}, (12, 'X'): {'MCP': 0, 'MCP_GPIO': 11},
+                            (13, 'X'): {'MCP': 0, 'MCP_GPIO': 12}, (14, 'X'): {'MCP': 0, 'MCP_GPIO': 13},
+                            (15, 'X'): {'MCP': 0, 'MCP_GPIO': 14}, (16, 'X'): {'MCP': 0, 'MCP_GPIO': 15},
+                            (17, 'X'): {'MCP': 1, 'MCP_GPIO': 0}, (18, 'X'): {'MCP': 1, 'MCP_GPIO': 1},
+                            (19, 'X'): {'MCP': 1, 'MCP_GPIO': 2}, (20, 'X'): {'MCP': 1, 'MCP_GPIO': 3},
+                            (21, 'X'): {'MCP': 1, 'MCP_GPIO': 4}, (22, 'X'): {'MCP': 1, 'MCP_GPIO': 5},
+                            (23, 'X'): {'MCP': 1, 'MCP_GPIO': 6}, (24, 'X'): {'MCP': 1, 'MCP_GPIO': 7},
+                            (25, 'X'): {'MCP': 1, 'MCP_GPIO': 8}, (26, 'X'): {'MCP': 1, 'MCP_GPIO': 9},
+                            (27, 'X'): {'MCP': 1, 'MCP_GPIO': 10}, (28, 'X'): {'MCP': 1, 'MCP_GPIO': 11},
+                            (29, 'X'): {'MCP': 1, 'MCP_GPIO': 12}, (30, 'X'): {'MCP': 1, 'MCP_GPIO': 13},
+                            (31, 'X'): {'MCP': 1, 'MCP_GPIO': 14}, (32, 'X'): {'MCP': 1, 'MCP_GPIO': 15},
+                            (33, 'X'): {'MCP': 2, 'MCP_GPIO': 0}, (34, 'X'): {'MCP': 2, 'MCP_GPIO': 1},
+                            (35, 'X'): {'MCP': 2, 'MCP_GPIO': 2}, (36, 'X'): {'MCP': 2, 'MCP_GPIO': 3},
+                            (37, 'X'): {'MCP': 2, 'MCP_GPIO': 4}, (38, 'X'): {'MCP': 2, 'MCP_GPIO': 5},
+                            (39, 'X'): {'MCP': 2, 'MCP_GPIO': 6}, (40, 'X'): {'MCP': 2, 'MCP_GPIO': 7},
+                            (41, 'X'): {'MCP': 2, 'MCP_GPIO': 8}, (42, 'X'): {'MCP': 2, 'MCP_GPIO': 9},
+                            (43, 'X'): {'MCP': 2, 'MCP_GPIO': 10}, (44, 'X'): {'MCP': 2, 'MCP_GPIO': 11},
+                            (45, 'X'): {'MCP': 2, 'MCP_GPIO': 12}, (46, 'X'): {'MCP': 2, 'MCP_GPIO': 13},
+                            (47, 'X'): {'MCP': 2, 'MCP_GPIO': 14}, (48, 'X'): {'MCP': 2, 'MCP_GPIO': 15},
+                            (49, 'X'): {'MCP': 3, 'MCP_GPIO': 0}, (50, 'X'): {'MCP': 3, 'MCP_GPIO': 1},
+                            (51, 'X'): {'MCP': 3, 'MCP_GPIO': 2}, (52, 'X'): {'MCP': 3, 'MCP_GPIO': 3},
+                            (53, 'X'): {'MCP': 3, 'MCP_GPIO': 4}, (54, 'X'): {'MCP': 3, 'MCP_GPIO': 5},
+                            (55, 'X'): {'MCP': 3, 'MCP_GPIO': 6}, (56, 'X'): {'MCP': 3, 'MCP_GPIO': 7},
+                            (57, 'X'): {'MCP': 3, 'MCP_GPIO': 8}, (58, 'X'): {'MCP': 3, 'MCP_GPIO': 9},
+                            (59, 'X'): {'MCP': 3, 'MCP_GPIO': 10}, (60, 'X'): {'MCP': 3, 'MCP_GPIO': 11},
+                            (61, 'X'): {'MCP': 3, 'MCP_GPIO': 12}, (62, 'X'): {'MCP': 3, 'MCP_GPIO': 13},
+                            (63, 'X'): {'MCP': 3, 'MCP_GPIO': 14}, (64, 'X'): {'MCP': 3, 'MCP_GPIO': 15}
+                            }
+                 }
 
-inner_cabling = {'1_role' : {(1, 'X'): {'MCP': 0, 'MCP_GPIO': 0}, (2, 'X'): {'MCP': 0, 'MCP_GPIO': 1},
-                             (3, 'X'): {'MCP': 0, 'MCP_GPIO': 2}, (4, 'X'): {'MCP': 0, 'MCP_GPIO': 3},
-                             (5, 'X'): {'MCP': 0, 'MCP_GPIO': 4}, (6, 'X'): {'MCP': 0, 'MCP_GPIO': 5},
-                             (7, 'X'): {'MCP': 0, 'MCP_GPIO': 6}, (8, 'X'): {'MCP': 0, 'MCP_GPIO': 7},
-                             (9, 'X'): {'MCP': 0, 'MCP_GPIO': 8}, (10, 'X'): {'MCP': 0, 'MCP_GPIO': 9},
-                             (11, 'X'): {'MCP': 0, 'MCP_GPIO': 10}, (12, 'X'): {'MCP': 0, 'MCP_GPIO': 11},
-                             (13, 'X'): {'MCP': 0, 'MCP_GPIO': 12}, (14, 'X'): {'MCP': 0, 'MCP_GPIO': 13},
-                             (15, 'X'): {'MCP': 0, 'MCP_GPIO': 14}, (16, 'X'): {'MCP': 0, 'MCP_GPIO': 15},
-                             (17, 'X'): {'MCP': 1, 'MCP_GPIO': 0}, (18, 'X'): {'MCP': 1, 'MCP_GPIO': 1},
-                             (19, 'X'): {'MCP': 1, 'MCP_GPIO': 2}, (20, 'X'): {'MCP': 1, 'MCP_GPIO': 3},
-                             (21, 'X'): {'MCP': 1, 'MCP_GPIO': 4}, (22, 'X'): {'MCP': 1, 'MCP_GPIO': 5},
-                             (23, 'X'): {'MCP': 1, 'MCP_GPIO': 6}, (24, 'X'): {'MCP': 1, 'MCP_GPIO': 7},
-                             (25, 'X'): {'MCP': 1, 'MCP_GPIO': 8}, (26, 'X'): {'MCP': 1, 'MCP_GPIO': 9},
-                             (27, 'X'): {'MCP': 1, 'MCP_GPIO': 10}, (28, 'X'): {'MCP': 1, 'MCP_GPIO': 11},
-                             (29, 'X'): {'MCP': 1, 'MCP_GPIO': 12}, (30, 'X'): {'MCP': 1, 'MCP_GPIO': 13},
-                             (31, 'X'): {'MCP': 1, 'MCP_GPIO': 14}, (32, 'X'): {'MCP': 1, 'MCP_GPIO': 15},
-                             (33, 'X'): {'MCP': 2, 'MCP_GPIO': 0}, (34, 'X'): {'MCP': 2, 'MCP_GPIO': 1},
-                             (35, 'X'): {'MCP': 2, 'MCP_GPIO': 2}, (36, 'X'): {'MCP': 2, 'MCP_GPIO': 3},
-                             (37, 'X'): {'MCP': 2, 'MCP_GPIO': 4}, (38, 'X'): {'MCP': 2, 'MCP_GPIO': 5},
-                             (39, 'X'): {'MCP': 2, 'MCP_GPIO': 6}, (40, 'X'): {'MCP': 2, 'MCP_GPIO': 7},
-                             (41, 'X'): {'MCP': 2, 'MCP_GPIO': 8}, (42, 'X'): {'MCP': 2, 'MCP_GPIO': 9},
-                             (43, 'X'): {'MCP': 2, 'MCP_GPIO': 10}, (44, 'X'): {'MCP': 2, 'MCP_GPIO': 11},
-                             (45, 'X'): {'MCP': 2, 'MCP_GPIO': 12}, (46, 'X'): {'MCP': 2, 'MCP_GPIO': 13},
-                             (47, 'X'): {'MCP': 2, 'MCP_GPIO': 14}, (48, 'X'): {'MCP': 2, 'MCP_GPIO': 15},
-                             (49, 'X'): {'MCP': 3, 'MCP_GPIO': 0}, (50, 'X'): {'MCP': 3, 'MCP_GPIO': 1},
-                             (51, 'X'): {'MCP': 3, 'MCP_GPIO': 2}, (52, 'X'): {'MCP': 3, 'MCP_GPIO': 3},
-                             (53, 'X'): {'MCP': 3, 'MCP_GPIO': 4}, (54, 'X'): {'MCP': 3, 'MCP_GPIO': 5},
-                             (55, 'X'): {'MCP': 3, 'MCP_GPIO': 6}, (56, 'X'): {'MCP': 3, 'MCP_GPIO': 7},
-                             (57, 'X'): {'MCP': 3, 'MCP_GPIO': 8}, (58, 'X'): {'MCP': 3, 'MCP_GPIO': 9},
-                             (59, 'X'): {'MCP': 3, 'MCP_GPIO': 10}, (60, 'X'): {'MCP': 3, 'MCP_GPIO': 11},
-                             (61, 'X'): {'MCP': 3, 'MCP_GPIO': 12}, (62, 'X'): {'MCP': 3, 'MCP_GPIO': 13},
-                             (63, 'X'): {'MCP': 3, 'MCP_GPIO': 14}, (64, 'X'): {'MCP': 3, 'MCP_GPIO': 15}
-}}
 
 class Mux(MuxAbstract):
     def __init__(self, **kwargs):
-        kwargs.update({'board_name': os.path.basename(__file__).rstrip('.py')})
+        if 'model' not in kwargs.keys():
+            for key in SPECS.keys():
+                kwargs = enforce_specs(kwargs, SPECS, key)
+            subclass_init = False
+        else:
+            subclass_init = True
         kwargs.update({'cabling': kwargs.pop('cabling', default_mux_cabling)})
-        kwargs.update({'activation_delay': max(kwargs.pop('activation_delay', SPECS['activation_delay']),
-                                               SPECS['activation_delay'])})
-        kwargs.update({'release_delay': max(kwargs.pop('release_delay', SPECS['release_delay']),
-                                            SPECS['activation_delay'])})
-        kwargs.update({'voltage_max': max(0., min(kwargs.pop('voltage_max', SPECS['voltage_max']),
-                                                  SPECS['voltage_max']))})
-        kwargs.update({'current_max': max(0., min(kwargs.pop('current_max', SPECS['current_max']),
-                                                  SPECS['current_max']))})
         super().__init__(**kwargs)
+        if not subclass_init:
+            self.exec_logger.event(f'{self.model}: {self.board_id}\tmux_init\tbegin\t{datetime.datetime.utcnow()}')
         assert isinstance(self.connection, I2C)
         self.exec_logger.debug(f'configuration: {kwargs}')
-        tca_address = kwargs.pop('tca_address', 0x70)
-        # tca_channel = kwargs.pop('tca_channel', 0)
         self._roles = kwargs.pop('roles', None)
         if self._roles is None:
-            self._roles = {'A': 'X'}  # NOTE: defaults to 4-roles
+            self._roles = {'A': 'X'}  # NOTE: defaults to 1-role
         if np.alltrue([j in self._roles.values() for j in set([i[1] for i in list(inner_cabling['1_role'].keys())])]):
             self._mode = '1_role'
         else:
-            self.exec_logger.error(f'Invalid role assignment for {self.board_name}: {self._roles} !')
+            self.exec_logger.error(f'Invalid role assignment for {self.model}: {self._roles} !')
             self._mode = ''
-        self._tca = [adafruit_tca9548a.TCA9548A(self.connection, tca_address)[i] for i in np.arange(7, 3, -1)]
+        self._tca = [adafruit_tca9548a.TCA9548A(self.connection, kwargs['mux_tca_address'])[i] for i in np.arange(7, 3, -1)]
         # self._mcp_addresses = (kwargs.pop('mcp', '0x20'))  # TODO: add assert on valid addresses..
         self._mcp = [None, None, None, None]
         self.reset()
         if self.addresses is None:
             self._get_addresses()
         self.exec_logger.debug(f'{self.board_id} addresses: {self.addresses}')
+        if not subclass_init:
+            self.exec_logger.event(f'{self.model}: {self.board_id}\tmux_init\tend\t{datetime.datetime.utcnow()}')
 
     def _get_addresses(self):
         """ Converts inner cabling addressing into (electrodes, role) addressing """
@@ -106,7 +117,5 @@ class Mux(MuxAbstract):
         d = self.addresses[elec, role]
         if state == 'on':
             activate_relay(self._mcp[d['MCP']], d['MCP_GPIO'], True)
-            # time.sleep(MUX_CONFIG['activation_delay'])  # NOTE: moved to MuxAbstract switch
         if state == 'off':
             activate_relay(self._mcp[d['MCP']], d['MCP_GPIO'], False)
-            # time.sleep(MUX_CONFIG['release_delay'])  # NOTE: moved to MuxAbstract switch
diff --git a/ohmpi/hardware_components/mux_2024_0_X.py b/ohmpi/hardware_components/mux_2024_0_X.py
index 48fed4101b90252702ce5430689585ea7d392a81..49c528a19e38c54c56733da2ffa476d46051429f 100644
--- a/ohmpi/hardware_components/mux_2024_0_X.py
+++ b/ohmpi/hardware_components/mux_2024_0_X.py
@@ -1,20 +1,26 @@
-from ohmpi.config import HARDWARE_CONFIG
 import os
+import datetime
 import numpy as np
 from ohmpi.hardware_components import MuxAbstract
 import adafruit_tca9548a  # noqa
 from adafruit_mcp230xx.mcp23017 import MCP23017  # noqa
 from digitalio import Direction  # noqa
-from busio import I2C
-# import time
+from busio import I2C  # noqa
+from ohmpi.utils import enforce_specs
 
 # hardware characteristics and limitations
-SPECS = {'voltage_max': 50., 'current_max': 3., 'activation_delay': 0.01, 'release_delay': 0.005}
+SPECS = {'model': {'default': os.path.basename(__file__).rstrip('.py')},
+         'id': {'default': 'mux_??'},
+         'voltage_max': {'default': 50.},
+         'current_max': {'default': 3.},
+         'activation_delay': {'default': 0.01},
+         'release_delay': {'default': 0.005}
+         }
 
 # defaults to 4 roles cabling electrodes from 1 to 8
-default_mux_cabling = {(elec, role) : ('mux_1', elec) for role in ['A', 'B', 'M', 'N'] for elec in range(1,9)}
+default_mux_cabling = {(elec, role): ('mux_1', elec) for role in ['A', 'B', 'M', 'N'] for elec in range(1, 9)}
 
-inner_cabling = {'4_roles' : {(1, 'X'): {'MCP': 0, 'MCP_GPIO': 0}, (1, 'Y'): {'MCP': 0, 'MCP_GPIO': 8},
+inner_cabling = {'4_roles': {(1, 'X'): {'MCP': 0, 'MCP_GPIO': 0}, (1, 'Y'): {'MCP': 0, 'MCP_GPIO': 8},
                              (2, 'X'): {'MCP': 0, 'MCP_GPIO': 1}, (2, 'Y'): {'MCP': 0, 'MCP_GPIO': 9},
                              (3, 'X'): {'MCP': 0, 'MCP_GPIO': 2}, (3, 'Y'): {'MCP': 0, 'MCP_GPIO': 10},
                              (4, 'X'): {'MCP': 0, 'MCP_GPIO': 3}, (4, 'Y'): {'MCP': 0, 'MCP_GPIO': 11},
@@ -30,7 +36,7 @@ inner_cabling = {'4_roles' : {(1, 'X'): {'MCP': 0, 'MCP_GPIO': 0}, (1, 'Y'): {'M
                              (6, 'XX'): {'MCP': 1, 'MCP_GPIO': 2}, (6, 'YY'): {'MCP': 1, 'MCP_GPIO': 10},
                              (7, 'XX'): {'MCP': 1, 'MCP_GPIO': 1}, (7, 'YY'): {'MCP': 1, 'MCP_GPIO': 9},
                              (8, 'XX'): {'MCP': 1, 'MCP_GPIO': 0}, (8, 'YY'): {'MCP': 1, 'MCP_GPIO': 8}},
-                '2_roles':  # TODO: WARNING check 2_roles table, it has not been verified yet !!!
+                 '2_roles':  # TODO: WARNING check 2_roles table, it has not been verified yet !!!
                             {(1, 'X'): {'MCP': 0, 'MCP_GPIO': 0}, (1, 'Y'): {'MCP': 0, 'MCP_GPIO': 8},
                              (2, 'X'): {'MCP': 0, 'MCP_GPIO': 1}, (2, 'Y'): {'MCP': 0, 'MCP_GPIO': 9},
                              (3, 'X'): {'MCP': 0, 'MCP_GPIO': 2}, (3, 'Y'): {'MCP': 0, 'MCP_GPIO': 10},
@@ -47,26 +53,22 @@ inner_cabling = {'4_roles' : {(1, 'X'): {'MCP': 0, 'MCP_GPIO': 0}, (1, 'Y'): {'M
                              (11, 'X'): {'MCP': 1, 'MCP_GPIO': 2}, (11, 'Y'): {'MCP': 1, 'MCP_GPIO': 10},
                              (10, 'X'): {'MCP': 1, 'MCP_GPIO': 1}, (10, 'Y'): {'MCP': 1, 'MCP_GPIO': 9},
                              (9, 'X'): {'MCP': 1, 'MCP_GPIO': 0}, (9, 'Y'): {'MCP': 1, 'MCP_GPIO': 8}}
-                }
+                 }
 
 
 class Mux(MuxAbstract):
     def __init__(self, **kwargs):
-        kwargs.update({'board_name': os.path.basename(__file__).rstrip('.py')})
-        kwargs.update({'cabling': kwargs.pop('cabling', default_mux_cabling)})
-        kwargs.update({'activation_delay': max(kwargs.pop('activation_delay', SPECS['activation_delay']),
-                                               SPECS['activation_delay'])})
-        kwargs.update({'release_delay': max(kwargs.pop('release_delay', SPECS['release_delay']),
-                                               SPECS['activation_delay'])})
-        kwargs.update({'voltage_max': max(0., min(kwargs.pop('voltage_max', SPECS['voltage_max']),
-                                                  SPECS['voltage_max']))})
-        kwargs.update({'current_max': max(0., min(kwargs.pop('current_max', SPECS['current_max']),
-                                                  SPECS['current_max']))})
+        if 'model' not in kwargs.keys():
+            for key in SPECS.keys():
+                kwargs = enforce_specs(kwargs, SPECS, key)
+            subclass_init = False
+        else:
+            subclass_init = True
         super().__init__(**kwargs)
+        if not subclass_init:
+            self.exec_logger.event(f'{self.model}: {self.board_id}\tmux_init\tbegin\t{datetime.datetime.utcnow()}')
         assert isinstance(self.connection, I2C)
         self.exec_logger.debug(f'configuration: {kwargs}')
-        tca_address = kwargs.pop('tca_address', None)
-        tca_channel = kwargs.pop('tca_channel', 0)
         self._roles = kwargs.pop('roles', None)
         if self._roles is None:
             self._roles = {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'}  # NOTE: defaults to 4-roles
@@ -75,18 +77,39 @@ class Mux(MuxAbstract):
         elif np.alltrue([j in self._roles.values() for j in set([i[1] for i in list(inner_cabling['2_roles'].keys())])]):
             self._mode = '2_roles'
         else:
-            self.exec_logger.error(f'Invalid role assignment for {self.board_name}: {self._roles} !')
+            self.exec_logger.error(f'Invalid role assignment for {self.model}: {self._roles} !')
             self._mode = ''
+
+        # Setup TCA
+        tca_address = kwargs.pop('tca_address', None)
+        tca_channel = kwargs.pop('tca_channel', 0)
         if tca_address is None:
             self._tca = self.connection
         else:
             self._tca = adafruit_tca9548a.TCA9548A(self.connection, tca_address)[tca_channel]
-        self._mcp_addresses = (kwargs.pop('mcp_0', '0x22'), kwargs.pop('mcp_1', '0x23'))  # TODO: add assert on valid addresses..
+
+        # Setup MCPs
+        self._mcp_jumper_pos = {'addr2': kwargs.pop('addr2', None), 'addr1': kwargs.pop('addr1', None)}
+        self._mcp_addresses = (kwargs.pop('mcp_0', None), kwargs.pop('mcp_1', None))
+        if self._mcp_addresses[0] is None and self._mcp_addresses[1] is None:
+            if self._mcp_jumper_pos['addr2'] is not None and self._mcp_jumper_pos['addr1'] is not None:
+                self._mcp_jumper_pos_to_addr()
+                self.exec_logger.debug(f"{self.board_id} assigned mcp_addresses {self._mcp_addresses[0]} and "
+                                       f"{self._mcp_addresses[1]} from jumper positions.")
+            else:
+                self.exec_logger.debug(f'MCP addresses nor jumper positions for {self.board_id} not in config file...')
+                # TODO: if no addresses defined, should abort or should we set default mcp addresses?
+        for addr in self._mcp_addresses:
+            assert addr in ['0x20', '0x21', '0x22', '0x23', '0x24', '0x25', '0x26', '0x27']
         self._mcp = [None, None]
         self.reset()
+
         if self.addresses is None:
             self._get_addresses()
+
         self.exec_logger.debug(f'{self.board_id} addresses: {self.addresses}')
+        if not subclass_init:  # TODO: try to only log this event and not the one created by super()
+            self.exec_logger.event(f'{self.model}: {self.board_id}\tmux_init\tend\t{datetime.datetime.utcnow()}')
 
     def _get_addresses(self):
         """ Converts inner cabling addressing into (electrodes, role) addressing """
@@ -116,3 +139,9 @@ class Mux(MuxAbstract):
         if state == 'off':
             activate_relay(self._mcp[d['MCP']], d['MCP_GPIO'], False)
             # time.sleep(MUX_CONFIG['release_delay'])  # NOTE: moved to MuxAbstract switch
+
+    def _mcp_jumper_pos_to_addr(self):
+        d = {'up': 0, 'down': 1}
+        mcp_0 = hex(int(f"0100{d[self._mcp_jumper_pos['addr2']]}{d[self._mcp_jumper_pos['addr1']]}0", 2))
+        mcp_1 = hex(int(f"0100{d[self._mcp_jumper_pos['addr2']]}{d[self._mcp_jumper_pos['addr1']]}1", 2))
+        self._mcp_addresses = (mcp_0, mcp_1)
\ No newline at end of file
diff --git a/ohmpi/hardware_components/pwr_batt.py b/ohmpi/hardware_components/pwr_batt.py
index dd811385307fb63e0c3b01e4f3f2de8f790f0c5c..dc67c8b88a742df803691446e0474d5f9c557e83 100644
--- a/ohmpi/hardware_components/pwr_batt.py
+++ b/ohmpi/hardware_components/pwr_batt.py
@@ -1,18 +1,34 @@
 from ohmpi.hardware_components.abstract_hardware_components import PwrAbstract
 import numpy as np
+import datetime
 import os
+from ohmpi.utils import enforce_specs
+
+# hardware characteristics and limitations
+SPECS = {'model': {'default': os.path.basename(__file__).rstrip('.py')},
+         'voltage': {'default': 12., 'max': 12., 'min': 12.},
+         'current_adjustable': {'default': False},
+         'voltage_adjustable': {'default': False},
+         'interface': {'default': 'none'},
+         }
 
 
 class Pwr(PwrAbstract):
     def __init__(self, **kwargs):
-        kwargs.update({'board_name': os.path.basename(__file__).rstrip('.py')})
-        voltage = kwargs.pop('voltage', 12.)
+        if 'model' not in kwargs.keys():
+            for key in SPECS.keys():
+                kwargs = enforce_specs(kwargs, SPECS, key)
+            subclass_init = False
+        else:
+            subclass_init = True
         super().__init__(**kwargs)
-        self.voltage_adjustable = False
-        self._voltage = voltage
-        self._current_adjustable = False
+        if not subclass_init:
+            self.exec_logger.event(f'{self.model}\tpwr_init\tbegin\t{datetime.datetime.utcnow()}')
+        self._voltage = kwargs['voltage']
         self._current = np.nan
-        self._state = 'on'
+        # self._state = 'on'
+        if not subclass_init:
+            self.exec_logger.event(f'{self.model}\tpwr_init\tend\t{datetime.datetime.utcnow()}')
 
     @property
     def current(self):
@@ -20,13 +36,13 @@ class Pwr(PwrAbstract):
 
     @current.setter
     def current(self, value, **kwargs):
-        self.exec_logger.debug(f'Current cannot be set on {self.board_name}')
-
-    def turn_off(self):
-        self.exec_logger.debug(f'{self.board_name} cannot be turned off')
-
-    def turn_on(self):
-        self.exec_logger.debug(f'{self.board_name} is always on')
+        self.exec_logger.debug(f'Current cannot be set on {self.model}')
+    #
+    # def turn_off(self):
+    #     self.exec_logger.debug(f'{self.model} cannot be turned off')
+    #
+    # def turn_on(self):
+    #     self.exec_logger.debug(f'{self.model} is always on')
 
     @property
     def voltage(self):
@@ -34,4 +50,4 @@ class Pwr(PwrAbstract):
 
     @voltage.setter
     def voltage(self, value):
-        PwrAbstract.voltage.fset(self, value)
\ No newline at end of file
+        PwrAbstract.voltage.fset(self, value)
diff --git a/ohmpi/hardware_components/pwr_dps5005.py b/ohmpi/hardware_components/pwr_dps5005.py
index 7e4d7ceac90172f0944f78f67e5923565e8b3acf..dd2b0d8e85c638c40b3dd27feea931303ba13d1b 100644
--- a/ohmpi/hardware_components/pwr_dps5005.py
+++ b/ohmpi/hardware_components/pwr_dps5005.py
@@ -1,38 +1,50 @@
 from ohmpi.hardware_components.abstract_hardware_components import PwrAbstract
-from ohmpi.config import HARDWARE_CONFIG
-import importlib
 import numpy as np
-import minimalmodbus  # noqa
+import datetime
 import os
+import time
+from ohmpi.utils import enforce_specs
+from minimalmodbus import Instrument  # noqa
 
-CTL_CONFIG = HARDWARE_CONFIG['ctl']
-ctl_name = HARDWARE_CONFIG['ctl'].pop('board_name', 'raspberry_pi')
-ctl_connection = HARDWARE_CONFIG['ctl'].pop('connection', 'modbus')
-ctl_module = importlib.import_module(f'ohmpi.hardware_components.{ctl_name}')
-CTL_CONFIG['baudrate'] = CTL_CONFIG.pop('baudrate', 9600)
-CTL_CONFIG['bitesize'] = CTL_CONFIG.pop('bitesize', 8)
-CTL_CONFIG['timeout'] = CTL_CONFIG.pop('timeout', 1)
-CTL_CONFIG['debug'] = CTL_CONFIG.pop('debug', False)
-CTL_CONFIG['parity'] = CTL_CONFIG.pop('parity', 'N')
-CTL_CONFIG['mode'] = CTL_CONFIG.pop('mode', minimalmodbus.MODE_RTU)
-CTL_CONFIG['port'] = CTL_CONFIG.pop('port', '/dev/ttyUSB0')
-CTL_CONFIG['slave_address'] = CTL_CONFIG.pop('slave_address', 1)
+# hardware characteristics and limitations
+SPECS = {'model': {'default': os.path.basename(__file__).rstrip('.py')},
+         'voltage': {'default': 12., 'max': 50., 'min': 0.},
+         'voltage_min': {'default': 0},
+         'voltage_max': {'default': 0},
+         'current_max': {'default': 100.},
+         'current_adjustable': {'default': False},
+         'voltage_adjustable': {'default': True},
+         'pwr_latency': {'default': .3}
+         }
+
+# TODO: Complete this code... handle modbus connection
 
 
 class Pwr(PwrAbstract):
     def __init__(self, **kwargs):
-        kwargs.update({'board_name': os.path.basename(__file__).rstrip('.py')})
-        voltage = kwargs.pop('voltage', 12.)
+        if 'model' not in kwargs.keys():
+            for key in SPECS.keys():
+                kwargs = enforce_specs(kwargs, SPECS, key)
+            subclass_init = False
+        else:
+            subclass_init = True
         super().__init__(**kwargs)
+        if not subclass_init:
+            self.exec_logger.event(f'{self.model}\tpwr_init\tbegin\t{datetime.datetime.utcnow()}')
+        assert isinstance(self.connection, Instrument)
         # if a controller is passed in kwargs, it will be instantiated
-        if self.ctl is None:
-            self.ctl = ctl_module.Ctl(**CTL_CONFIG)
-        self.connection = self.ctl.interfaces[kwargs.pop('connection', ctl_connection)]
+        #if self.ctl is None:
+        #    self.ctl = ctl_module.Ctl(**CTL_CONFIG)
+        #self.connection = self.ctl.interfaces[kwargs.pop('connection', ctl_connection)]
+        self._voltage = kwargs['voltage']
+        self._current_max = kwargs['current_max']
         self.voltage_adjustable = True
-        self._voltage = voltage
-        self._current_adjustable = False
+        self.current_adjustable = False
         self._current = np.nan
-        self._current_max = kwargs.pop('current_max', 100.)
+        self._pwr_state = 'off'
+        self._pwr_latency = kwargs['pwr_latency']
+        if not subclass_init:
+            self.exec_logger.event(f'{self.model}\tpwr_init\tend\t{datetime.datetime.utcnow()}')
 
     @property
     def current(self):
@@ -40,27 +52,54 @@ class Pwr(PwrAbstract):
 
     @current.setter
     def current(self, value, **kwargs):
-        self.exec_logger.debug(f'Current cannot be set on {self.board_name}')
-
-    def turn_off(self):
-        self.connection.write_register(0x09, 1)
-        self.exec_logger.debug(f'{self.board_name} is off')
+        self.exec_logger.debug(f'Current cannot be set on {self.model}')
 
-    def turn_on(self):
-        self.connection.write_register(0x09, 1)
-        self.exec_logger.debug(f'{self.board_name} is on')
+    # def turn_off(self):
+    #     self.connection.write_register(0x09, 0)
+    #     self.exec_logger.debug(f'{self.model} is off')
+    #
+    # def turn_on(self):
+    #     self.connection.write_register(0x09, 1)
+    #     self.exec_logger.debug(f'{self.model} is on')
+    #     time.sleep(.3)
 
     @property
     def voltage(self):
-        return PwrAbstract.voltage.fget(self)
+        # return PwrAbstract.voltage.fget(self)
+        return self._voltage
 
     @voltage.setter
     def voltage(self, value):
         self.connection.write_register(0x0000, value, 2)
-    
+        self._voltage = value
+
     def battery_voltage(self):
-        self.connection.read_register(0x05, 2)
+        self._battery_voltage = self.connection.read_register(0x05, 2)
+        return self._battery_voltage
 
-    @property
-    def current_max(self,value):
+    def current_max(self, value):
         self.connection.write_register(0x0001, value * 10, 0)
+
+    @property
+    def pwr_state(self):
+        return self._pwr_state
+
+    @pwr_state.setter
+    def pwr_state(self, state):
+        """Switches pwr on or off.
+
+            Parameters
+            ----------
+            state : str
+                'on', 'off'
+            """
+        if state == 'on':
+            self.connection.write_register(0x09, 1)
+            self._pwr_state = 'on'
+            self.exec_logger.debug(f'{self.model} is on')
+            time.sleep(self._pwr_latency) # from pwr specs
+
+        elif state == 'off':
+            self.connection.write_register(0x09, 0)
+            self._pwr_state = 'off'
+            self.exec_logger.debug(f'{self.model} is off')
diff --git a/ohmpi/hardware_components/raspberry_pi.py b/ohmpi/hardware_components/raspberry_pi.py
index 86c6f00a4a8c3005a0b4aceaecb31dc4e301bd2b..d45ad095c3cf7f7ef977d8a67677378f9d38521b 100644
--- a/ohmpi/hardware_components/raspberry_pi.py
+++ b/ohmpi/hardware_components/raspberry_pi.py
@@ -4,47 +4,71 @@ import busio  # noqa
 from adafruit_extended_bus import ExtendedI2C  # noqa
 import minimalmodbus  # noqa
 import os
-from ohmpi.utils import get_platform
+from ohmpi.utils import get_platform, enforce_specs
 from gpiozero import CPUTemperature  # noqa
+import warnings
+
+# hardware characteristics and limitations
+SPECS = {'model': {'default': os.path.basename(__file__).rstrip('.py')},
+         'voltage': {'default': 12., 'max': 50., 'min': 0.},
+         'modbus_baudrate': {'default': 9600},
+         'modbus_bitesize': {'default': 8},
+         'modbus_timeout': {'default': 1},
+         'modbus_debug': {'default': False},
+         'modbus_parity': {'default': 'N'},
+         'modbus_mode': {'default': minimalmodbus.MODE_RTU},
+         'modbus_port': {'default': '/dev/ttyUSB0'},
+         'modbus_slave_address': {'default': 1}
+         }
 
 
 class Ctl(CtlAbstract):
     def __init__(self, **kwargs):
-        kwargs.update({'board_name': os.path.basename(__file__).rstrip('.py')})
-        modbus_baudrate = kwargs.pop('modbus_baudrate', 9600)
-        modbus_bitesize = kwargs.pop('modbus_bitesize', 8)
-        modbus_timeout = kwargs.pop('modbus_timeout', 1)
-        modbus_debug = kwargs.pop('modbus_debug', False)
-        modbus_parity = kwargs.pop('modbus_parity', 'N')
-        modbus_mode = kwargs.pop('modbus_mode', minimalmodbus.MODE_RTU)
-        modbus_port = kwargs.pop('modbus_port', '/dev/ttyUSB0')
-        modbus_slave_address = kwargs.pop('modbus_slave_address', 1)
+        if 'model' not in kwargs.keys():
+            for key in SPECS.keys():
+                kwargs = enforce_specs(kwargs, SPECS, key)
+            subclass_init = False
+        else:
+            subclass_init = True
 
         super().__init__(**kwargs)
         self.interfaces = dict()
+
+        # None interface for battery
+        self.interfaces['none'] = None
+
+        # warnings.filterwarnings("error")  # to filter out adafruit warning about setting I2C frequency
         # I2C
-        self.interfaces['i2c'] = busio.I2C(board.SCL, board.SDA)  # noqa
+        try:
+            self.interfaces['i2c'] = busio.I2C(board.SCL, board.SDA)  # noqa
+        except RuntimeWarning:
+            pass
 
         # Extended I2C
         try:
             self.interfaces['i2c_ext'] = ExtendedI2C(4)  # 4 is defined
+        except RuntimeWarning:
+            pass
         except Exception as e:
-            self.exec_logger.warning('Could not initialize Extended I2C:\n{e}')
+            self.exec_logger.warning(f'Could not initialize Extended I2C:\n{e}')
+        warnings.resetwarnings()
+
         # modbus
         try:
-            self.interfaces['modbus'] = minimalmodbus.Instrument(port=modbus_port, slaveaddress=modbus_slave_address)
-            self.interfaces['modbus'].serial.baudrate = modbus_baudrate  # Baud rate 9600 as listed in doc
-            self.interfaces['modbus'].serial.bytesize = modbus_bitesize  #
-            self.interfaces['modbus'].serial.timeout = modbus_timeout  # greater than 0.5 for it to work
-            self.interfaces['modbus'].debug = modbus_debug  #
-            self.interfaces['modbus'].serial.parity = modbus_parity  # No parity
-            self.interfaces['modbus'].mode = modbus_mode  # RTU mode
+            self.interfaces['modbus'] = minimalmodbus.Instrument(port=kwargs['modbus_port'],
+                                                                 slaveaddress=kwargs['modbus_slave_address'])
+            self.interfaces['modbus'].serial.baudrate = kwargs['modbus_baudrate']  # Baud rate 9600 as listed in doc
+            self.interfaces['modbus'].serial.bytesize = kwargs['modbus_bitesize']  #
+            self.interfaces['modbus'].serial.timeout = kwargs['modbus_timeout']  # greater than 0.5 for it to work
+            self.interfaces['modbus'].debug = kwargs['modbus_debug']  #
+            self.interfaces['modbus'].serial.parity = kwargs['modbus_parity']  # No parity
+            self.interfaces['modbus'].mode = kwargs['modbus_mode']  # RTU mode
         except Exception as e:
-            self.exec_logger.warning('Could not initialize Extended modbus:\n{e}')
+            self.exec_logger.warning(f'Could not initialize Extended modbus:\n{e}')
 
         platform, on_pi = get_platform()
         assert on_pi
-        self.board_name = platform
+        self.model = platform
         self._cpu_temp_available = True
         self.max_cpu_temp = 85.  # °C
 
diff --git a/ohmpi/hardware_system.py b/ohmpi/hardware_system.py
index f3fe7ba648a64a416e331b2c4185b4811de9c4ca..dde487a0561da4cc8bda3eb476eddfeb34244ace 100644
--- a/ohmpi/hardware_system.py
+++ b/ohmpi/hardware_system.py
@@ -11,6 +11,7 @@ from ohmpi.logging_setup import create_stdout_logger
 from ohmpi.utils import update_dict
 from ohmpi.config import HARDWARE_CONFIG
 from threading import Thread, Event, Barrier, BrokenBarrierError
+import warnings
 
 # plt.switch_backend('agg')  # for thread safe operations...
 
@@ -28,11 +29,24 @@ for k, v in MUX_CONFIG.items():
     for k2, v2 in MUX_DEFAULT.items():
         MUX_CONFIG[k].update({k2: MUX_CONFIG[k].pop(k2, v2)})
 
-TX_CONFIG = tx_module.TX_CONFIG
-RX_CONFIG = rx_module.RX_CONFIG
-
-current_max = np.min([TX_CONFIG['current_max'], np.min([MUX_CONFIG[i].pop('current_max', np.inf) for i in MUX_CONFIG.keys()])])
-voltage_max = np.min([TX_CONFIG['voltage_max'], np.min([MUX_CONFIG[i].pop('voltage_max', np.inf) for i in MUX_CONFIG.keys()])])
+TX_CONFIG = HARDWARE_CONFIG['tx']
+for k, v in tx_module.SPECS['tx'].items():
+    try:
+        TX_CONFIG.update({k: TX_CONFIG.pop(k, v['default'])})
+    except Exception as e:
+        print(f'Cannot set value {v} in TX_CONFIG[{k}]:\n{e}')
+
+RX_CONFIG = HARDWARE_CONFIG['rx']
+for k, v in rx_module.SPECS['rx'].items():
+    try:
+        RX_CONFIG.update({k: RX_CONFIG.pop(k, v['default'])})
+    except Exception as e:
+        print(f'Cannot set value {v} in RX_CONFIG[{k}]:\n{e}')
+
+current_max = np.min([TX_CONFIG['voltage_max']/50/TX_CONFIG['r_shunt'],  # TODO: replace 50 by a TX config
+                      np.min(np.hstack((np.inf, [MUX_CONFIG[i].pop('current_max', np.inf) for i in MUX_CONFIG.keys()])))])
+voltage_max = np.min([TX_CONFIG['voltage_max'],
+                      np.min(np.hstack((np.inf, [MUX_CONFIG[i].pop('voltage_max', np.inf) for i in MUX_CONFIG.keys()])))])
 voltage_min = RX_CONFIG['voltage_min']
 
 
@@ -43,17 +57,14 @@ def elapsed_seconds(start_time):
 
 class OhmPiHardware:
     def __init__(self, **kwargs):
-        self.exec_logger = kwargs.pop('exec_logger', None)
+        # OhmPiHardware initialization
+        self.exec_logger = kwargs.pop('exec_logger', create_stdout_logger('exec_hw'))
         self.exec_logger.event(f'OhmPiHardware\tinit\tbegin\t{datetime.datetime.utcnow()}')
-        if self.exec_logger is None:
-            self.exec_logger = create_stdout_logger('exec_hw')
-        self.data_logger = kwargs.pop('exec_logger', None)
-        if self.data_logger is None:
-            self.data_logger = create_stdout_logger('data_hw')
-        self.soh_logger = kwargs.pop('soh_logger', None)
-        if self.soh_logger is None:
-            self.soh_logger = create_stdout_logger('soh_hw')
+        self.data_logger = kwargs.pop('exec_logger', create_stdout_logger('data_hw'))
+        self.soh_logger = kwargs.pop('soh_logger', create_stdout_logger('soh_hw'))
         self.tx_sync = Event()
+
+        # Main Controller initialization
         HARDWARE_CONFIG['ctl'].pop('model')
         HARDWARE_CONFIG['ctl'].update({'exec_logger': self.exec_logger, 'data_logger': self.data_logger,
                                        'soh_logger': self.soh_logger})
@@ -65,17 +76,26 @@ class OhmPiHardware:
                 ctl_mod = importlib.import_module(f'ohmpi.hardware_components.{ctl_mod}')
             self.ctl = ctl_mod.Ctl(**self.ctl)
 
+        # Initialize RX
         HARDWARE_CONFIG['rx'].pop('model')
-        HARDWARE_CONFIG['rx'].update(**HARDWARE_CONFIG['rx'])
+        HARDWARE_CONFIG['rx'].update(**HARDWARE_CONFIG['rx'])  # TODO: delete me ?
         HARDWARE_CONFIG['rx'].update({'ctl': HARDWARE_CONFIG['rx'].pop('ctl', self.ctl)})
         if isinstance(HARDWARE_CONFIG['rx']['ctl'], dict):
             ctl_mod = HARDWARE_CONFIG['rx']['ctl'].pop('model', self.ctl)
             if isinstance(ctl_mod, str):
                 ctl_mod = importlib.import_module(f'ohmpi.hardware_components.{ctl_mod}')
             HARDWARE_CONFIG['rx']['ctl'] = ctl_mod.Ctl(**HARDWARE_CONFIG['rx']['ctl'])
+        HARDWARE_CONFIG['rx'].update({'connection':
+                                          HARDWARE_CONFIG['rx'].pop('connection',
+                                                                    HARDWARE_CONFIG['rx']['ctl'].interfaces[
+                                                                                  HARDWARE_CONFIG['rx'].pop(
+                                                                                      'interface_name', 'i2c')])})
         HARDWARE_CONFIG['rx'].update({'exec_logger': self.exec_logger, 'data_logger': self.data_logger,
                                        'soh_logger': self.soh_logger})
+        HARDWARE_CONFIG['tx'].pop('ctl', None)
         self.rx = kwargs.pop('rx', rx_module.Rx(**HARDWARE_CONFIG['rx']))
+
+        # Initialize power source
         HARDWARE_CONFIG['pwr'].pop('model')
         HARDWARE_CONFIG['pwr'].update(**HARDWARE_CONFIG['pwr'])  # NOTE: Explain why this is needed or delete me
         HARDWARE_CONFIG['pwr'].update({'ctl': HARDWARE_CONFIG['pwr'].pop('ctl', self.ctl)})
@@ -84,19 +104,39 @@ class OhmPiHardware:
             if isinstance(ctl_mod, str):
                 ctl_mod = importlib.import_module(f'ohmpi.hardware_components.{ctl_mod}')
             HARDWARE_CONFIG['pwr']['ctl'] = ctl_mod.Ctl(**HARDWARE_CONFIG['pwr']['ctl'])
+        #if 'interface_name' in HARDWARE_CONFIG['pwr']:
+        HARDWARE_CONFIG['pwr'].update({
+            'connection': HARDWARE_CONFIG['pwr'].pop(
+                'connection', HARDWARE_CONFIG['pwr']['ctl'].interfaces[
+                    HARDWARE_CONFIG['pwr'].pop('interface_name', None)])})
+
         HARDWARE_CONFIG['pwr'].update({'exec_logger': self.exec_logger, 'data_logger': self.data_logger,
                                       'soh_logger': self.soh_logger})
         self.pwr = kwargs.pop('pwr', pwr_module.Pwr(**HARDWARE_CONFIG['pwr']))
+
+        # Initialize TX
         HARDWARE_CONFIG['tx'].pop('model')
         HARDWARE_CONFIG['tx'].update(**HARDWARE_CONFIG['tx'])
         HARDWARE_CONFIG['tx'].update({'tx_sync': self.tx_sync})
-        HARDWARE_CONFIG['tx'].update({'ctl': self.ctl})
+        HARDWARE_CONFIG['tx'].update({'ctl': HARDWARE_CONFIG['tx'].pop('ctl', self.ctl)})
+        if isinstance(HARDWARE_CONFIG['tx']['ctl'], dict):
+            ctl_mod = HARDWARE_CONFIG['tx']['ctl'].pop('model', self.ctl)
+            if isinstance(ctl_mod, str):
+                ctl_mod = importlib.import_module(f'ohmpi.hardware_components.{ctl_mod}')
+            HARDWARE_CONFIG['tx']['ctl'] = ctl_mod.Ctl(**HARDWARE_CONFIG['tx']['ctl'])
+        HARDWARE_CONFIG['tx'].update({'connection': HARDWARE_CONFIG['tx'].pop('connection',
+                                                                              HARDWARE_CONFIG['tx']['ctl'].interfaces[
+                                                                                  HARDWARE_CONFIG['tx'].pop(
+                                                                                      'interface_name', 'i2c')])})
+        HARDWARE_CONFIG['tx'].pop('ctl', None)
         HARDWARE_CONFIG['tx'].update({'exec_logger': self.exec_logger, 'data_logger': self.data_logger,
                                       'soh_logger': self.soh_logger})
         self.tx = kwargs.pop('tx', tx_module.Tx(**HARDWARE_CONFIG['tx']))
         if isinstance(self.tx, dict):
             self.tx = tx_module.Tx(**self.tx)
         self.tx.pwr = self.pwr
+
+        # Initialize Muxes
         self._cabling = kwargs.pop('cabling', {})
         self.mux_boards = {}
         for mux_id, mux_config in MUX_CONFIG.items():
@@ -106,45 +146,83 @@ class OhmPiHardware:
             mux_config.update({'ctl': mux_config.pop('ctl', self.ctl)})
 
             mux_module = importlib.import_module(f'ohmpi.hardware_components.{mux_config["model"]}')
-            if isinstance(mux_config['ctl'], dict): ### TODO: is this needed?
+            if isinstance(mux_config['ctl'], dict):
                 mux_ctl_module = importlib.import_module(f'ohmpi.hardware_components.{mux_config["ctl"]["model"]}')
                 mux_config['ctl'] = mux_ctl_module.Ctl(**mux_config['ctl'])  # (**self.ctl)
             assert issubclass(type(mux_config['ctl']), CtlAbstract)
-            io = mux_config.pop('io', mux_config['ctl'].interfaces[mux_config.pop('connection', 'i2c')])
-            mux_config.update({'io': io})
+            mux_config.update({'connection': mux_config.pop('connection', mux_config['ctl'].interfaces[mux_config.pop('interface_name', 'i2c')])})
             mux_config['id'] = mux_id
 
             self.mux_boards[mux_id] = mux_module.Mux(**mux_config)
 
-        # self.mux_boards = kwargs.pop('mux', {'mux_1': mux_module.Mux(id='mux_1',
-        #                                                              exec_logger=self.exec_logger,
-        #                                                              data_logger=self.data_logger,
-        #                                                              soh_logger=self.soh_logger,
-        #                                                              ctl=self.ctl,
-        #                                                              cabling=self._cabling)})
-
         self.mux_barrier = Barrier(len(self.mux_boards) + 1)
         self._cabling = {}
         for mux_id, mux in self.mux_boards.items():
             mux.barrier = self.mux_barrier
             for k, v in mux.cabling.items():
                 update_dict(self._cabling, {k: (mux_id, k[0])})
+
+        # Complete OhmPiHardware initialization
         self.readings = np.array([])  # time series of acquired data
         self._start_time = None  # time of the beginning of a readings acquisition
         self._pulse = 0  # pulse number
         self.exec_logger.event(f'OhmPiHardware\tinit\tend\t{datetime.datetime.utcnow()}')
+        self._pwr_state = 'off'
+
+    @property
+    def pwr_state(self):
+        return self._pwr_state
+
+    @pwr_state.setter
+    def pwr_state(self, state):
+        if state == 'on':
+            self.tx.pwr_state = 'on'
+            self._pwr_state = 'on'
+        elif state == 'off':
+            self.tx.pwr_state = 'off'
+            self._pwr_state = 'off'
 
     def _clear_values(self):
         self.readings = np.array([])
         self._start_time = None
         self._pulse = 0
 
-    def _gain_auto(self):  # TODO: improve _gain_auto
+    def _gain_auto(self, polarities=(1, -1), vab=5., switch_pwr_off=False): #TODO: improve _gain_auto
         self.exec_logger.event(f'OhmPiHardware\ttx_rx_gain_auto\tbegin\t{datetime.datetime.utcnow()}')
-        self.tx_sync.wait()
-        self.tx.adc_gain_auto()
-        self.rx.adc_gain_auto()
-        self.rx.voltage_gain_auto()
+        current, voltage = 0., 0.
+        if self.tx.pwr.voltage_adjustable:
+            self.tx.pwr.voltage = vab
+        if self.tx.pwr.pwr_state == 'off':
+            self.tx.pwr.pwr_state = 'on'
+            switch_pwr_off = True
+        tx_gains = []
+        rx_gains = []
+        for pol in polarities:
+            # self.tx.polarity = pol
+            # set gains automatically
+            injection = Thread(target=self._inject, kwargs={'injection_duration': 0.2, 'polarity': pol})
+            # readings = Thread(target=self._read_values)
+            get_tx_gain = Thread(target=self.tx.gain_auto)
+            get_rx_gain = Thread(target=self.rx.gain_auto)
+            injection.start()
+            self.tx_sync.wait()
+            get_tx_gain.start()  # TODO: add a barrier to synchronize?
+            get_rx_gain.start()
+            get_tx_gain.join()
+            get_rx_gain.join()
+            injection.join()
+            tx_gains.append(self.tx.gain)
+            rx_gains.append(self.rx.gain)
+
+            # v = self.readings[:, 2] != 0
+            # current = max(current, np.mean(self.readings[v, 3]))
+            # voltage = max(voltage, np.abs(np.mean(self.readings[v, 2] * self.readings[v, 4])))
+            self.tx.polarity = 0
+        self.tx.gain = min(tx_gains)
+        self.rx.gain = min(rx_gains)
+        # self.rx.gain_auto(voltage)
+        if switch_pwr_off:
+            self.tx.pwr.pwr_state = 'off'
         self.exec_logger.event(f'OhmPiHardware\ttx_rx_gain_auto\tend\t{datetime.datetime.utcnow()}')
 
     def _inject(self, polarity=1, injection_duration=None):  # TODO: deal with voltage or current pulse
@@ -158,7 +236,7 @@ class OhmPiHardware:
             mux.barrier = self.mux_barrier
 
     @property
-    def pulses(self):
+    def pulses(self):  # TODO: is this obsolete?
         pulses = {}
         for i in np.unique(self.readings[:, 1]):
             r = self.readings[self.readings[:, 1] == i, :]
@@ -204,20 +282,18 @@ class OhmPiHardware:
         self._pulse += 1
         self.exec_logger.event(f'OhmPiHardware\tread_values\tend\t{datetime.datetime.utcnow()}')
 
-    @property
-    def last_rho(self):
-        v = self.readings[:, 2] != 0
+    def last_resistance(self, delay=0.):
+        v = np.where((self.readings[:, 0] >= delay) & (self.readings[:, 2] != 0))[0]
         if len(v) > 1:
             # return np.mean(np.abs(self.readings[v, 4] - self.sp) / self.readings[v, 3])
             return np.mean(self.readings[v, 2] * (self.readings[v, 4] - self.sp) / self.readings[v, 3])
         else:
             return np.nan
 
-    @property
-    def last_dev(self):
-        if len(self.readings) > 1:
-            v = self.readings[:, 2] != 0  # exclude sample where there is no injection
-            return 100. * np.std(self.readings[v, 2] * (self.readings[v, 4] - self.sp) / self.readings[v, 3]) / self.last_rho
+    def last_dev(self, delay=0.):
+        v = np.where((self.readings[:, 0] >= delay) & (self.readings[:, 2] != 0))[0]
+        if len(v) > 1:
+            return 100. * np.std(self.readings[v, 2] * (self.readings[v, 4] - self.sp) / self.readings[v, 3]) / self.last_resistance(delay=delay)
         else:
             return np.nan
 
@@ -241,7 +317,7 @@ class OhmPiHardware:
             sp = np.mean(mean_vmn[np.ix_(polarity == 1)] + mean_vmn[np.ix_(polarity == -1)]) / 2
             return sp
 
-    def _compute_tx_volt(self, pulse_duration=0.1, strategy='vmax', tx_volt=5,
+    def _compute_tx_volt(self, pulse_duration=0.1, strategy='vmax', tx_volt=5.,
                          vab_max=voltage_max, vmn_min=voltage_min):
         """Estimates best Tx voltage based on different strategies.
         At first a half-cycle is made for a short duration with a fixed
@@ -285,7 +361,14 @@ class OhmPiHardware:
         vab_max = np.abs(vab_max)
         vmn_min = np.abs(vmn_min)
         vab = np.min([np.abs(tx_volt), vab_max])
-        self.tx.turn_on()
+        # self.tx.turn_on()
+        switch_pwr_off, switch_tx_pwr_off = False, False #TODO: check if these should be moved in kwargs
+        if self.tx.pwr_state == 'off':
+            self.tx.pwr_state = 'on'
+            switch_tx_pwr_off = True
+        if self.tx.pwr.pwr_state == 'off':
+            self.tx.pwr.pwr_state = 'on'
+            switch_pwr_off = True
         if 1. / self.rx.sampling_rate > pulse_duration:
             sampling_rate = 1. / pulse_duration  # TODO: check this...
         else:
@@ -307,7 +390,11 @@ class OhmPiHardware:
             self.tx.exec_logger.warning(f'Unknown strategy {strategy} for setting VAB! Using {vab} V')
         else:
             self.tx.exec_logger.debug(f'Constant strategy for setting VAB, using {vab} V')
-        self.tx.turn_off()
+        # self.tx.turn_off()
+        if switch_pwr_off:
+            self.tx.pwr.pwr_state = 'off'
+        if switch_tx_pwr_off:
+            self.tx.pwr_state = 'off'
         rab = (np.abs(vab) * 1000.) / iab
         self.exec_logger.debug(f'RAB = {rab:.2f} Ohms')
         if vmn < 0:
@@ -318,10 +405,11 @@ class OhmPiHardware:
 
     def _plot_readings(self, save_fig=False):
         # Plot graphs
+        warnings.filterwarnings("ignore", category=DeprecationWarning)
         fig, ax = plt.subplots(nrows=5, sharex=True)
         ax[0].plot(self.readings[:, 0], self.readings[:, 3], '-r', marker='.', label='iab')
         ax[0].set_ylabel('Iab [mA]')
-        ax[1].plot(self.readings[:, 0], self.readings[:, 2] * (self.readings[:, 4] - self.sp) , '-b', marker='.', label='vmn')
+        ax[1].plot(self.readings[:, 0], self.readings[:, 4] - self.sp , '-b', marker='.', label='vmn')
         ax[1].set_ylabel('Vmn [mV]')
         ax[2].plot(self.readings[:, 0], self.readings[:, 2], '-g', marker='.', label='polarity')
         ax[2].set_ylabel('polarity [-]')
@@ -329,13 +417,14 @@ class OhmPiHardware:
         ax[3].plot(self.readings[v, 0], (self.readings[v, 2] * (self.readings[v, 4] - self.sp)) / self.readings[v, 3],
                    '-m', marker='.', label='R [ohm]')
         ax[3].set_ylabel('R [ohm]')
-        ax[4].plot(self.readings[v, 0], np.ones_like(self.readings[v,0]) * self.sp, '-k', marker='.', label='SP [mV]')
+        ax[4].plot(self.readings[v, 0], np.ones_like(self.readings[v, 0]) * self.sp, '-k', marker='.', label='SP [mV]')
         ax[4].set_ylabel('SP [mV]')
-        fig.legend()
+        # fig.legend()
         if save_fig:
             fig.savefig(f'figures/test.png')
         else:
             plt.show()
+        warnings.resetwarnings()
 
     def calibrate_rx_bias(self):
         self.rx._bias += (np.mean(self.readings[self.readings[:, 2] == 1, 4])
@@ -344,19 +433,19 @@ class OhmPiHardware:
     def vab_square_wave(self, vab, cycle_duration, sampling_rate=None, cycles=3, polarity=1, duty_cycle=1.,
                         append=False):
         self.exec_logger.event(f'OhmPiHardware\tvab_square_wave\tbegin\t{datetime.datetime.utcnow()}')
-        self.tx.polarity = polarity  ### TODO: inject on both polarities for gain auto?
-        durations = [cycle_duration/2]*2*cycles
-        # set gains automatically
-        gain_auto = Thread(target=self._gain_auto)
-        injection = Thread(target=self._inject, kwargs={'injection_duration': 0.2, 'polarity': polarity})
-        gain_auto.start()
-        injection.start()
-        gain_auto.join()
-        injection.join()
+        switch_pwr_off, switch_tx_pwr_off = False, False
+        if self.pwr_state == 'off':
+            self.pwr_state = 'on'
+            switch_tx_pwr_off = True
+        # if self.tx.pwr.pwr_state == 'off':
+        #     self.tx.pwr.pwr_state = 'on'
+        #     switch_pwr_off = True
+        self._gain_auto(vab=vab)
         assert 0. <= duty_cycle <= 1.
         if duty_cycle < 1.:
-            durations = [cycle_duration/2 * duty_cycle, cycle_duration/2*(1.-duty_cycle)] * 2 * cycles
-            pol = [-self.tx.polarity * np.heaviside(i % 2, -1.) for i in range(2 * cycles)]
+            durations = [cycle_duration/2 * duty_cycle, cycle_duration/2 * (1.-duty_cycle)] * 2 * cycles
+            pol = [-int(polarity * np.heaviside(i % 2, -1.)) for i in range(2 * cycles)]
+            # pol = [-int(self.tx.polarity * np.heaviside(i % 2, -1.)) for i in range(2 * cycles)]
             polarities = [0] * (len(pol) * 2)
             polarities[0::2] = pol
         else:
@@ -364,15 +453,20 @@ class OhmPiHardware:
             polarities = None
         self._vab_pulses(vab, durations, sampling_rate, polarities=polarities,  append=append)
         self.exec_logger.event(f'OhmPiHardware\tvab_square_wave\tend\t{datetime.datetime.utcnow()}')
+        if switch_pwr_off:
+            self.tx.pwr.pwr_state = 'off'
+        if switch_tx_pwr_off:
+            self.pwr_state = 'off'
 
-    def _vab_pulse(self, vab, duration, sampling_rate=None, polarity=1, append=False):
+    def _vab_pulse(self, vab=None, duration=1., sampling_rate=None, polarity=1, append=False):
         """ Gets VMN and IAB from a single voltage pulse
         """
-        self.tx.polarity = polarity
+        #self.tx.polarity = polarity
         if sampling_rate is None:
             sampling_rate = RX_CONFIG['sampling_rate']
         if self.tx.pwr.voltage_adjustable:
-            self.tx.pwr.voltage = vab
+            if self.tx.pwr.voltage != vab:
+                self.tx.pwr.voltage = vab
         else:
             vab = self.tx.pwr.voltage
         # reads current and voltage during the pulse
@@ -382,23 +476,38 @@ class OhmPiHardware:
         injection.start()
         readings.join()
         injection.join()
-        self.tx.polarity = 0
+        self.tx.polarity = 0   #TODO: is this necessary?
 
     def _vab_pulses(self, vab, durations, sampling_rate, polarities=None, append=False):
+        switch_pwr_off, switch_tx_pwr_off = False, False
+        if self.pwr_state == 'off':
+            self.pwr_state = 'on'
+            switch_pwr_off = True
         n_pulses = len(durations)
         self.exec_logger.debug(f'n_pulses: {n_pulses}')
+        if self.tx.pwr.voltage_adjustable:
+            self.tx.pwr.voltage = vab
+        else:
+            vab = self.tx.pwr.voltage
+        if self.tx.pwr.pwr_state == 'off':
+            self.tx.pwr.pwr_state = 'on'
+            switch_pwr_off = True
         if sampling_rate is None:
             sampling_rate = RX_CONFIG['sampling_rate']
         if polarities is not None:
             assert len(polarities) == n_pulses
         else:
-            polarities = [-self.tx.polarity * np.heaviside(i % 2, -1.) for i in range(n_pulses)]
+            polarities = [-int(self.tx.polarity * np.heaviside(i % 2, -1.)) for i in range(n_pulses)]
         if not append:
             self._clear_values()
         for i in range(n_pulses):
-            self._vab_pulse(self, duration=durations[i], sampling_rate=sampling_rate, polarity=polarities[i],
+            self._vab_pulse(vab=vab, duration=durations[i], sampling_rate=sampling_rate, polarity=polarities[i],
                             append=True)
-
+        if switch_pwr_off:
+            self.tx.pwr.pwr_state = 'off'
+        if switch_tx_pwr_off:
+            self.pwr_state = 'off'
+    
     def switch_mux(self, electrodes, roles=None, state='off', **kwargs):
         """Switches on multiplexer relays for given quadrupole.
 
@@ -437,7 +546,7 @@ class OhmPiHardware:
                     # Create a new thread to perform some work
                     self.mux_boards[mux].barrier = b
                     kwargs.update({'elec_dict': elec_dict, 'state': state})
-                    mux_workers[idx] = Thread(target=self.mux_boards[mux].switch, kwargs=kwargs)
+                    mux_workers[idx] = Thread(target=self.mux_boards[mux].switch, kwargs=kwargs)  # TODO: handle minimum delay between two relays activation (to avoid lagging during test_mux at high speed)
                     mux_workers[idx].start()
                 try:
                     self.mux_barrier.wait()
@@ -453,7 +562,7 @@ class OhmPiHardware:
         self.exec_logger.event(f'OhmPiHardware\tswitch_mux\tend\t{datetime.datetime.utcnow()}')
         return status
 
-    def test_mux(self, channel=None, activation_time=1.0):
+    def test_mux(self, channel=None, activation_time=1.0): #TODO: add test in reverse order on each mux board
         """Interactive method to test the multiplexer.
 
         Parameters
@@ -476,11 +585,19 @@ class OhmPiHardware:
             time.sleep(activation_time)
             self.switch_mux(electrodes, roles, state='off')
         else:
-            for c in self._cabling.keys():
-                self.exec_logger.info(f'Testing electrode {c[0]} with role {c[1]}.')
-                self.switch_mux(electrodes=[c[0]], roles=[c[1]], state='on')
-                time.sleep(activation_time)
-                self.switch_mux(electrodes=[c[0]], roles=[c[1]], state='off')
+            list_of_muxes = [i for i in self.mux_boards.keys()]
+            list_of_muxes.sort()
+            for m_id in list_of_muxes:
+                for c in self.mux_boards[m_id].cabling.keys():
+                    self.exec_logger.info(f'Testing electrode {c[0]} with role {c[1]}.')
+                    self.switch_mux(electrodes=[c[0]], roles=[c[1]], state='on')
+                    time.sleep(activation_time)
+                    self.switch_mux(electrodes=[c[0]], roles=[c[1]], state='off')
+            # for c in self._cabling.keys():
+            #     self.exec_logger.info(f'Testing electrode {c[0]} with role {c[1]}.')
+            #     self.switch_mux(electrodes=[c[0]], roles=[c[1]], state='on')
+            #     time.sleep(activation_time)
+            #     self.switch_mux(electrodes=[c[0]], roles=[c[1]], state='off')
         self.exec_logger.info('Test finished.')
 
     def reset_mux(self):
diff --git a/ohmpi/logging_setup.py b/ohmpi/logging_setup.py
index e2606b024a70b57843cf8ef2890d24b30c13afc7..33927655e620ea488ad99e3e05b1001cf0b6dbeb 100644
--- a/ohmpi/logging_setup.py
+++ b/ohmpi/logging_setup.py
@@ -10,6 +10,11 @@ import sys
 from termcolor import colored
 
 
+def get_logging_levels():
+    """Gets a list of the logging levels loaded"""
+    return [logging.getLevelName(x) for x in range(1,101) if not logging.getLevelName(x).startswith('Level')]
+
+
 def add_logging_level(level_name, level_num, method_name=None):
     """
     Comprehensively adds a new logging level to the `logging` module and the
@@ -72,6 +77,8 @@ def create_stdout_logger(name):
     handler.setFormatter(formatter)
     logger.addHandler(handler)
     logger.setLevel(logging.DEBUG)
+    if 'EVENT' not in get_logging_levels():
+        add_logging_level('EVENT', logging.DEBUG + 1)
     return logger
 
 
diff --git a/ohmpi/ohmpi.py b/ohmpi/ohmpi.py
index c176d901dc21f9c236d11573318ad1d7962c31ec..2035c81d14c505ca8becc2e761880c3a24c8737f 100644
--- a/ohmpi/ohmpi.py
+++ b/ohmpi/ohmpi.py
@@ -86,11 +86,17 @@ class OhmPi(object):
             'nb_meas': 1,
             'sequence_delay': 1,
             'nb_stack': 1,
-            'export_path': 'data/measurement.csv'
+            'sampling_interval': 2,
+            'tx_volt': 5,
+            'duty_cycle': 0.5,
+            'strategy': 'constant',
+            'export_path': None,
+            'export_dir': 'data',
+            'export_name': 'measurement.csv'
         }
         # read in acquisition settings
-        if settings is not None:
-            self.update_settings(settings)
+        # if settings is not None:
+        self.update_settings(settings)
 
         self.exec_logger.debug('Initialized with settings:' + str(self.settings))
 
@@ -162,6 +168,7 @@ class OhmPi(object):
 
     @staticmethod
     def append_and_save(filename: str, last_measurement: dict, cmd_id=None):
+        # TODO: find alternative approach to save full data (zip, hdf5 or mseed?)
         """Appends and saves the last measurement dict.
 
         Parameters
@@ -410,12 +417,13 @@ class OhmPi(object):
         else:
             self.exec_logger.warning('Not on Raspberry Pi, skipping reboot...')
 
-    def run_measurement(self, quad=None, nb_stack=None, injection_duration=None,
+    def run_measurement(self, quad=None, nb_stack=None, injection_duration=None, duty_cycle=None,
                         autogain=True, strategy='constant', tx_volt=5., best_tx_injtime=0.1,
                         cmd_id=None, **kwargs):
         # TODO: add sampling_interval -> impact on _hw.rx.sampling_rate (store the current value, change the _hw.rx.sampling_rate, do the measurement, reset the sampling_rate to the previous value)
         # TODO: default value of tx_volt and other parameters set to None should be given in config.py and used in function definition
         # TODO: add rs_check option (or propose an other way to do this)
+        # TODO: implement compute_tx_volt for injection strategies
         """Measures on a quadrupole and returns transfer resistance.
 
         Parameters
@@ -443,28 +451,43 @@ class OhmPi(object):
         cmd_id : str, optional
             Unique command identifier
         """
+        # check pwr is on, if not, let's turn it on
+        switch_power_off = False
+        if self._hw.pwr_state == 'off':
+            self._hw.pwr_state = 'on'
+            switch_power_off = True
+
         self.exec_logger.debug('Starting measurement')
         self.exec_logger.debug('Waiting for data')
 
         # check arguments
         if quad is None:
-            quad = [0, 0, 0, 0]
+            quad = np.array([0, 0, 0, 0])
         if nb_stack is None:
             nb_stack = self.settings['nb_stack']
         if injection_duration is None:
-                injection_duration = self.settings['injection_duration']
-        tx_volt = float(tx_volt)
+            injection_duration = self.settings['injection_duration']
+        if duty_cycle is None:
+            duty_cycle = self.settings['duty_cycle']
+        # quad = kwargs.pop('quad', [0,0,0,0])
+        # nb_stack = kwargs.pop('nb_stack', self.settings['nb_stack'])
+        # injection_duration = kwargs.pop('injection_duration', self.settings['injection_duration'])
+        # duty_cycle = kwargs.pop('duty_cycle', self.settings['duty_cycle'])
+        # tx_volt = float(kwargs.pop('tx_volt', self.settings['tx_volt']))
         bypass_check = kwargs['bypass_check'] if 'bypass_check' in kwargs.keys() else False
         if self.switch_mux_on(quad, bypass_check=bypass_check, cmd_id=cmd_id):
-            self._hw.vab_square_wave(tx_volt, cycle_duration=injection_duration*2, cycles=nb_stack, duty_cycle=kwargs.pop('duty_cycle', 1.))
+            self._hw.vab_square_wave(tx_volt, cycle_duration=injection_duration*2/duty_cycle, cycles=nb_stack, duty_cycle=duty_cycle)
             if 'delay' in kwargs.keys():
                 delay = kwargs['delay']
             else:
-                delay = 0.
+                delay = injection_duration * 2/3  # TODO: check if this is ok and if last point is not taken the end of injection
             x = np.where((self._hw.readings[:, 0] >= delay) & (self._hw.readings[:, 2] != 0))
-            print(f'length of series: {len(x)}')
-            R = np.mean((self._hw.readings[x, 2] * (self._hw.readings[x, 4] - self._hw.sp)) / self._hw.readings[x, 3])
-            R_std = 100. * np.std((self._hw.readings[x, 2] * (self._hw.readings[x, 4] - self._hw.sp)) / self._hw.readings[x, 3]) / R
+            Vmn = np.mean(self._hw.readings[x, 2] * (self._hw.readings[x, 4] - self._hw.sp))
+            Vmn_std = 100. * np.std(self._hw.readings[x, 2] * (self._hw.readings[x, 4])) # - self._hw.sp))
+            I = np.mean(self._hw.readings[x, 3])
+            I_std = 100. * np.std(self._hw.readings[x, 3])
+            R = self._hw.last_resistance(delay=delay)
+            R_std = self._hw.last_dev(delay=delay)
             d = {
                 "time": datetime.now().isoformat(),
                 "A": quad[0],
@@ -472,8 +495,8 @@ class OhmPi(object):
                 "M": quad[2],
                 "N": quad[3],
                 "inj time [ms]": injection_duration,  # NOTE: check this
-                # "Vmn [mV]": sum_vmn / (2 * nb_stack),
-                # "I [mA]": sum_i / (2 * nb_stack),
+                "Vmn [mV]": Vmn,
+                "I [mA]": I,
                 "R [ohm]": R,
                 "R_std [%]": R_std,
                 "Ps [mV]": self._hw.sp,
@@ -483,10 +506,10 @@ class OhmPi(object):
                 "Nb samples [-]": len(self._hw.readings),  # TODO: use only samples after a delay in each pulse
                 "fulldata": self._hw.readings[:, [0, -2, -1]],
                 # "I_stack [mA]": i_stack_mean,
-                # "I_std [mA]": i_std,
+                "I_std [%]": I_std,
                 # "I_per_stack [mA]": np.array([np.mean(i_stack[i*2:i*2+2]) for i in range(nb_stack)]),
                 # "Vmn_stack [mV]": vmn_stack_mean,
-                # "Vmn_std [mV]": vmn_std,
+                "Vmn_std [%]": Vmn_std,
                 # "Vmn_per_stack [mV]": np.array([np.diff(np.mean(vmn_stack[i*2:i*2+2], axis=1))[0] / 2 for i in range(nb_stack)]),
                 # "R_stack [ohm]": r_stack_mean,
                 # "R_std [ohm]": r_stack_std,
@@ -515,6 +538,11 @@ class OhmPi(object):
         else:
             self.exec_logger.info(f'Skipping {quad}')
         self.switch_mux_off(quad, cmd_id)
+
+        # if power was off before measurement, let's turn if off
+        if switch_power_off:
+            self._hw.pwr_state = 'off'
+
         return d
 
     def run_multiple_sequences(self, cmd_id=None, sequence_delay=None, nb_meas=None, **kwargs):  # NOTE : could be renamed repeat_sequence
@@ -571,6 +599,8 @@ class OhmPi(object):
         cmd_id : str, optional
             Unique command identifier
         """
+        # switch power on
+        self._hw.pwr_state = 'on'
         self.status = 'running'
         self.exec_logger.debug(f'Status: {self.status}')
         self.exec_logger.debug(f'Measuring sequence: {self.sequence}')
@@ -578,7 +608,11 @@ class OhmPi(object):
         self.reset_mux()
         
         # create filename with timestamp
-        filename = self.settings["export_path"].replace('.csv',
+        if self.settings["export_path"] is None:
+            filename = self.settings['export_path'].replace(
+                '.csv', f'_{datetime.now().strftime("%Y%m%dT%H%M%S")}.csv')
+        else:
+            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}')
 
@@ -614,7 +648,7 @@ class OhmPi(object):
             # self.switch_mux_on(quad)
             # run a measurement
             if self.on_pi:
-                acquired_data = self.run_measurement(quad, **kwargs)
+                acquired_data = self.run_measurement(quad=quad, **kwargs)
             else:  # for testing, generate random data
                 sum_vmn = np.random.rand(1)[0] * 1000.
                 sum_i = np.random.rand(1)[0] * 100.
@@ -648,7 +682,7 @@ class OhmPi(object):
             self.append_and_save(filename, acquired_data)
             self.exec_logger.debug(f'quadrupole {i + 1:d}/{n:d}')
 
-        # self.switch_dps('off')
+        self._hw.pwr_state = 'off'
         self.status = 'idle'
 
     def run_sequence_async(self, cmd_id=None, **kwargs):
@@ -789,7 +823,10 @@ class OhmPi(object):
             self.exec_logger.debug(f'tx pwr voltage: {self._hw.tx.pwr.voltage}, rx max voltage: {self._hw.rx._voltage_max}')
             return False
         else:
-            return self._hw.switch_mux(electrodes=quadrupole, state='on', bypass_check=bypass_check)
+            if np.array(quadrupole).all() == np.array([0, 0, 0, 0]).all():  # NOTE: No mux
+                return True
+            else:
+                return self._hw.switch_mux(electrodes=quadrupole, state='on', bypass_check=bypass_check)
 
     def switch_mux_off(self, quadrupole, cmd_id=None):
         """Switches off multiplexer relays for given quadrupole.
@@ -840,7 +877,13 @@ class OhmPi(object):
             - nb_meas (total number of times the sequence will be run)
             - sequence_delay (delay in second between each sequence run)
             - nb_stack (number of stack for each quadrupole measurement)
-            - export_path (path where to export the data, timestamp will be added to filename)
+            - strategy (injection strategy: constant, vmax, vmin)
+            - duty_cycle (injection duty cycle comprised between 0.5 - 1)
+            - export_dir (directory where to export the data)
+            - export_name (name of exported file, timestamp will be added to filename)
+            - export_path (path where to export the data, timestamp will be added to filename ;
+                            if export_path is given, it goes over export_dir and export_name)
+
 
         Parameters
         ----------
@@ -865,8 +908,113 @@ class OhmPi(object):
                 status = False
         else:
             self.exec_logger.warning('Settings are missing...')
+
+        if self.settings['export_path'] is None:
+            self.settings['export_path'] = os.path.join(self.settings['export_dir'], self.settings['export_name'])
+        else:
+            self.settings['export_dir'] = os.path.split(self.settings['export_path'])[0]
+            self.settings['export_name'] = os.path.split(self.settings['export_path'])[1]
+
         return status
 
+    def run_inversion(self, survey_names=[], elec_spacing=1, **kwargs):
+        """Run a simple 2D inversion using ResIPy.
+        
+        Parameters
+        ----------
+        survey_names : list of string, optional
+            Filenames of the survey to be inverted (including extension).
+        elec_spacing : float (optional)
+            Electrode spacing in meters. We assume same electrode spacing everywhere.
+        kwargs : optional
+            Additiona keyword arguments passed to `resipy.Project.invert()`. For instance
+            `reg_mode` == 0 for batch inversion, `reg_mode == 2` for time-lapse inversion.
+            See ResIPy document for more information on options available
+            (https://hkex.gitlab.io/resipy/).
+
+        Returns
+        -------
+        xzv : list of dict
+            Each dictionnary with key 'x' and 'z' for the centroid of the elements and 'v'
+            for the values in resistivity of the elements.
+        """
+        # check if we have any files to be inverted
+        if len(survey_names) == 0:
+            self.exec_logger('No file to invert')
+            return []
+        
+        # check if user didn't provide a single string instead of a list
+        if isinstance(survey_names, str):
+            survey_names = [survey_names]
+
+        # check kwargs for reg_mode
+        if 'reg_mode' in kwargs:
+            reg_mode = kwargs['reg_mode']
+        else:
+            reg_mode = 0
+            kwargs['reg_mode'] = 0
+
+        pdir = os.path.dirname(__file__)
+        # import resipy if available
+        try:
+            import pandas as pd
+            import sys
+            sys.path.append(os.path.join(pdir, '../../resipy/src/'))
+            from resipy import Project
+        except Exception as e:
+            self.exec_logger.error('Cannot import ResIPy or Pandas, error: ' + str(e))
+            return []
+
+        # get absolule filename
+        fnames = []
+        for survey_name in survey_names:
+            fname = os.path.join(pdir, '../data', survey_name)
+            if os.path.exists(fname):
+                fnames.append(fname)
+            else:
+                self.exec_logger.warning(fname + ' not found')
+        
+        # define a parser for the "ohmpi" format
+        def ohmpiParser(fname):
+            df = pd.read_csv(fname)
+            df = df.rename(columns={'A':'a', 'B':'b', 'M':'m', 'N':'n'})
+            df['vp'] = df['Vmn [mV]']
+            df['i'] = df['I [mA]']
+            df['resist'] = df['vp']/df['i']
+            df['ip'] = np.nan
+            emax = np.max(df[['a', 'b', 'm', 'n']].values)
+            elec = np.zeros((emax, 3))
+            elec[:, 0] = np.arange(emax) * elec_spacing
+            return elec, df[['a','b','m','n','vp','i','resist','ip']]
+                
+        # run inversion
+        self.exec_logger.info('ResIPy: import surveys')
+        k = Project(typ='R2')  # invert in a temporary directory that will be erased afterwards
+        if len(survey_names) == 1:
+            k.createSurvey(fnames[0], parser=ohmpiParser)
+        elif len(survey_names) > 0 and reg_mode == 0:
+            k.createBatchSurvey(fnames, parser=ohmpiParser)
+        elif len(survey_names) > 0 and reg_mode > 0:
+            k.createTimeLapseSurvey(fnames, parser=ohmpiParser)
+        self.exec_logger.info('ResIPy: generate mesh')
+        k.createMesh('trian')
+        self.exec_logger.info('ResIPy: invert survey')
+        k.invert(param=kwargs)
+
+        # read data
+        self.exec_logger.info('Reading inverted surveys')
+        k.getResults()
+        xzv = []
+        for m in k.meshResults:
+            df = m.df
+            xzv.append({
+                'x': df['X'].values.tolist(),
+                'z': df['Z'].values.tolist(),
+                'rho': df['Resistivity(ohm.m)'].values.tolist(),
+            })
+        
+        return xzv
+
     # Properties
     @property
     def sequence(self):
diff --git a/ohmpi/utils.py b/ohmpi/utils.py
index 6d2b718d36018af8ab37e49404178c368c07aab4..142d4fb40fa4627bebaeb5f84e578adb52291cfa 100644
--- a/ohmpi/utils.py
+++ b/ohmpi/utils.py
@@ -3,8 +3,26 @@ import os
 import shutil
 import collections.abc
 import numpy as np
+from numbers import Number
 
 
+def enforce_specs(kwargs, specs, key):
+
+    kwargs.update({key: kwargs.pop(key, specs[key]['default'])})
+    
+    if isinstance(kwargs[key], Number):
+        s = specs.copy()
+        min_value = s[key].pop('min', -np.inf)
+        s[key]['min'] = min_value
+        max_value = s[key].pop('max', np.inf)
+        s[key]['max'] = max_value
+        if kwargs[key] < min_value:
+            kwargs[key] = min_value
+        elif kwargs[key] > max_value:
+            kwargs[key] = max_value
+
+    return kwargs
+
 def update_dict(d, u):
     """Updates a dictionary by adding elements to collection items associated to existing keys
 
diff --git a/requirements.txt b/requirements.txt
index 8010a45145eadd15c5e76e6b1f8b836c62734c38..4c649e24d14cd66c5bc3460be933dd9a686ffc60 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -3,9 +3,11 @@ adafruit-blinka
 adafruit-circuitpython-ads1x15
 adafruit-circuitpython-tca9548a
 adafruit-circuitpython-mcp230xx
+adafruit_extended_bus
 minimalmodbus
 gpiozero
 numpy
 paho-mqtt
 termcolor
 pandas
+matplotlib
\ No newline at end of file
diff --git a/uml_diagrams/classes_uml_ohmpi.dot b/uml_diagrams/classes_uml_ohmpi.dot
index 2071ce04e004cfe6ade11f8de935531f3d3e3386..42176748e50c7c3c6eec2fa10fd8d0084f8e38b8 100644
--- a/uml_diagrams/classes_uml_ohmpi.dot
+++ b/uml_diagrams/classes_uml_ohmpi.dot
@@ -2,28 +2,28 @@ digraph "classes_uml_ohmpi" {
 charset="utf-8"
 rankdir=BT
 "0" [label="{CompressedSizedTimedRotatingFileHandler|maxBytes : int\lstream\lzip_mode : int\l|doRollover()\lfind_last_rotated_file()\lshouldRollover(record)\l}", shape="record"];
-"1" [label="{Ctl|board_name : str\linterfaces : dict\lmax_cpu_temp : float\l|}", shape="record"];
+"1" [label="{Ctl|interfaces : dict\lmax_cpu_temp : float\lmodel : str\l|}", shape="record"];
 "2" [label="{Ctl|\l|}", shape="record"];
-"3" [label="{CtlAbstract|board_name\lconnection : NoneType\lcpu_temperature\lexec_logger : NoneType, RootLogger\linterfaces : NoneType\lmax_cpu_temp\lsoh_logger : NoneType, RootLogger\l|}", shape="record"];
+"3" [label="{CtlAbstract|connection\lcpu_temperature\lexec_logger : RootLogger, NoneType\linterfaces : NoneType\lmax_cpu_temp\lmodel\lsoh_logger : RootLogger, NoneType\l|}", shape="record"];
 "4" [label="{MQTTHandler|auth : NoneType\lclient_id : str\lhostname\lkeepalive : int\lport : int\lprotocol\lqos : int\lretain : bool\ltls : NoneType\ltopic\ltransport : str\lwill : NoneType\l|emit(record)\l}", shape="record"];
-"5" [label="{Mux|addresses : dict\lio\l|reset()\lswitch_one(elec, role, state)\l}", shape="record"];
+"5" [label="{Mux|addresses : dict\l|reset()\lswitch_one(elec, role, state)\l}", shape="record"];
 "6" [label="{Mux|\l|reset()\lswitch_one()\ltest()\l}", shape="record"];
 "7" [label="{Mux|addresses : dict\l|reset()\lswitch_one(elec, role, state)\l}", shape="record"];
-"8" [label="{MuxAbstract|addresses\lbarrier\lboard_id\lboard_name\lcabling : dict\lconnection\lexec_logger : NoneType, RootLogger\lsoh_logger : NoneType, RootLogger\l|reset()\lswitch(elec_dict, state, bypass_check)\lswitch_one(elec, role, state)\ltest(elec_dict, activation_time)\l}", shape="record"];
+"8" [label="{MuxAbstract|addresses\lbarrier\lboard_id\lcabling : dict\lconnection\lexec_logger\lmodel\lsoh_logger\l|reset()\lswitch(elec_dict, state, bypass_check)\lswitch_one(elec, role, state)\ltest(elec_dict, activation_time)\l}", shape="record"];
 "9" [label="{MyServer|\l|do_POST()\l}", shape="record"];
-"10" [label="{OhmPi|cmd_id : NoneType\lcontroller : NoneType\ldata_logger : NoneType, RootLogger\lexec_logger : NoneType, RootLogger\lid : str\lmqtt : bool\lnb_samples : int\lon_pi : NoneType, bool\lsequence\lsequence : ndarray, NoneType\lsettings : dict\lsoh_logger : NoneType, RootLogger\lstatus : str\lthread : Thread, NoneType\l|append_and_save(filename, last_measurement, cmd_id)\lget_data(survey_names, cmd_id)\lget_deprecated_methods(cls)\linterrupt(cmd_id)\lload_sequence(filename, cmd_id)\lquit(cmd_id)\lremove_data(cmd_id)\lreset_mux(cmd_id)\lrestart(cmd_id)\lrs_check(tx_volt, cmd_id)\lrun_measurement(quad, nb_stack, injection_duration, autogain, strategy, tx_volt, best_tx_injtime, cmd_id)\lrun_multiple_sequences(cmd_id, sequence_delay, nb_meas)\lrun_sequence(cmd_id)\lrun_sequence_async(cmd_id)\lset_sequence(sequence, cmd_id)\lswitch_mux_off(quadrupole, cmd_id)\lswitch_mux_on(quadrupole, bypass_check, cmd_id)\ltest_mux(activation_time, mux_id, cmd_id)\lupdate_settings(settings, cmd_id)\l}", shape="record"];
-"11" [label="{OhmPiHardware|ctl\ldata_logger : NoneType, RootLogger\lexec_logger : NoneType, RootLogger\llast_dev\llast_rho\lmux_barrier : Barrier\lmux_boards : dict\lpulses\lpwr\lreadings : ndarray\lrx\lsoh_logger : NoneType, RootLogger\lsp\ltx\ltx_sync : Event\l|calibrate_rx_bias()\lreset_mux()\lswitch_mux(electrodes, roles, state)\ltest_mux(channel, activation_time)\lvab_square_wave(vab, cycle_duration, sampling_rate, cycles, polarity, duty_cycle, append)\l}", shape="record"];
-"12" [label="{Pwr|connection\lctl\lcurrent\lcurrent_max\lvoltage\lvoltage_adjustable : bool\l|battery_voltage()\lturn_off()\lturn_on()\l}", shape="record"];
-"13" [label="{Pwr|current\lvoltage\lvoltage_adjustable : bool\l|turn_off()\lturn_on()\l}", shape="record"];
-"14" [label="{PwrAbstract|board_name\lconnection\lctl\lcurrent\lexec_logger : NoneType, RootLogger\lsoh_logger : NoneType, RootLogger\lvoltage\lvoltage_adjustable\l|turn_off()\lturn_on()\l}", shape="record"];
-"15" [label="{Rx|adc_gain\ladc_gain\lconnection\lctl\lvoltage\l|adc_gain_auto()\l}", shape="record"];
+"10" [label="{OhmPi|cmd_id : NoneType\lcontroller : NoneType\ldata_logger : RootLogger, NoneType\lexec_logger : RootLogger, NoneType\lid : str\lmqtt : bool\lnb_samples : int\lon_pi : bool, NoneType\lsequence\lsequence : NoneType, ndarray\lsettings : dict\lsoh_logger : RootLogger, NoneType\lstatus : str\lthread : Thread, NoneType\l|append_and_save(filename, last_measurement, cmd_id)\lget_data(survey_names, cmd_id)\lget_deprecated_methods(cls)\linterrupt(cmd_id)\lload_sequence(filename, cmd_id)\lquit(cmd_id)\lremove_data(cmd_id)\lreset_mux(cmd_id)\lrestart(cmd_id)\lrs_check(tx_volt, cmd_id)\lrun_measurement(quad, nb_stack, injection_duration, autogain, strategy, tx_volt, best_tx_injtime, cmd_id)\lrun_multiple_sequences(cmd_id, sequence_delay, nb_meas)\lrun_sequence(cmd_id)\lrun_sequence_async(cmd_id)\lset_sequence(sequence, cmd_id)\lswitch_mux_off(quadrupole, cmd_id)\lswitch_mux_on(quadrupole, bypass_check, cmd_id)\ltest_mux(activation_time, mux_id, cmd_id)\lupdate_settings(settings, cmd_id)\l}", shape="record"];
+"11" [label="{OhmPiHardware|ctl\ldata_logger\lexec_logger\lmux_barrier : Barrier\lmux_boards : dict\lpulses\lpwr\lreadings : ndarray\lrx\lsoh_logger\lsp\ltx\ltx_sync : Event\l|calibrate_rx_bias()\llast_dev(delay)\llast_resistance(delay)\lreset_mux()\lswitch_mux(electrodes, roles, state)\ltest_mux(channel, activation_time)\lvab_square_wave(vab, cycle_duration, sampling_rate, cycles, polarity, duty_cycle, append)\l}", shape="record"];
+"12" [label="{Pwr|current\lcurrent_adjustable : bool\lvoltage\lvoltage_adjustable : bool\l|battery_voltage()\lcurrent_max(value)\lturn_off()\lturn_on()\l}", shape="record"];
+"13" [label="{Pwr|current\lvoltage\l|turn_off()\lturn_on()\l}", shape="record"];
+"14" [label="{PwrAbstract|connection\lcurrent\lcurrent_adjustable\lexec_logger : RootLogger, NoneType\lmodel\lsoh_logger : RootLogger, NoneType\lvoltage\lvoltage_adjustable\l|turn_off()\lturn_on()\l}", shape="record"];
+"15" [label="{Rx|gain\lgain : int, float\lvoltage\l|gain_auto()\l}", shape="record"];
 "16" [label="{Rx|adc_gain\ladc_gain : float\lvoltage\l|adc_gain_auto()\l}", shape="record"];
-"17" [label="{Rx|adc_gain\ladc_gain : float\lctl\lio\lmcp_board : MCP23008\lpin_DG0 : DigitalInOut\lpin_DG1 : DigitalInOut\lpin_DG2 : DigitalInOut\lvoltage\lvoltage_gain\lvoltage_gain : int, float\l|adc_gain_auto()\lvoltage_gain_auto()\l}", shape="record"];
-"18" [label="{RxAbstract|adc_gain\lboard_name\lconnection\lctl\lexec_logger : NoneType, RootLogger\lsampling_rate\lsoh_logger : NoneType, RootLogger\lvoltage\l|adc_gain_auto()\l}", shape="record"];
-"19" [label="{Tx|adc_gain\ladc_gain : int, float\lconnection\lctl\lcurrent\lcurrent_adjustable : bool\lmcp_board : MCP23008\lpin0 : DigitalInOut\lpin1 : DigitalInOut\lpin4 : DigitalInOut\lpolarity\lpolarity : int\lpwr : NoneType\ltx_bat\lvoltage_adjustable : bool\l|adc_gain_auto()\lcurrent_pulse()\linject(polarity, injection_duration)\lturn_off()\lturn_on()\lvoltage_pulse(voltage, length, polarity)\l}", shape="record"];
+"17" [label="{Rx|gain\lgain : int, float\lmcp_board : MCP23008\lpin_DG0 : DigitalInOut\lpin_DG1 : DigitalInOut\lpin_DG2 : DigitalInOut\lvoltage\l|gain_auto()\l}", shape="record"];
+"18" [label="{RxAbstract|adc_gain\lconnection\lexec_logger : RootLogger, NoneType\lmodel\lsampling_rate\lsoh_logger : RootLogger, NoneType\lvoltage\l|}", shape="record"];
+"19" [label="{Tx|adc_voltage_max\ladc_voltage_min\lcurrent\lcurrent_adjustable : bool\lgain\lgain : int, float\lmcp_board : MCP23008\lpin0 : DigitalInOut\lpin1 : DigitalInOut\lpolarity\lpolarity : int\lr_shunt\ltx_bat\lvoltage_adjustable : bool\l|current_pulse()\lgain_auto()\linject(polarity, injection_duration)\lturn_off()\lturn_on()\lvoltage_pulse(voltage, length, polarity)\l}", shape="record"];
 "20" [label="{Tx|adc_gain\ladc_gain : float\lcurrent\lpolarity : int\ltx_bat\lvoltage\l|adc_gain_auto()\lcurrent_pulse()\linject(state)\lvoltage_pulse(voltage, length, polarity)\l}", shape="record"];
-"21" [label="{Tx|adc_gain\ladc_gain : int, float\lctl\lcurrent\lcurrent_adjustable : bool\lio\lmcp_board : MCP23008\lpin0 : DigitalInOut\lpin1 : DigitalInOut\lpin4 : DigitalInOut\lpolarity\lpolarity : int\lpwr : NoneType\ltx_bat\lvoltage_adjustable : bool\l|adc_gain_auto()\lcurrent_pulse()\linject(polarity, injection_duration)\lturn_off()\lturn_on()\lvoltage_pulse(voltage, length, polarity)\l}", shape="record"];
-"22" [label="{TxAbstract|adc_gain\lboard_name\lconnection\lctl\lexec_logger : NoneType, RootLogger\linjection_duration\linjection_duration\lpolarity\lpwr\lsoh_logger : NoneType, RootLogger\ltx_bat\ltx_sync\l|adc_gain_auto()\lcurrent_pulse()\linject(polarity, injection_duration)\lvoltage_pulse(voltage, length, polarity)\l}", shape="record"];
+"21" [label="{Tx|pin4 : DigitalInOut\lpin6 : DigitalInOut\l|inject(polarity, injection_duration)\l}", shape="record"];
+"22" [label="{TxAbstract|adc_gain\lconnection\lexec_logger : RootLogger, NoneType\linjection_duration\linjection_duration\lmodel\lpolarity\lpwr\lsoh_logger : RootLogger, NoneType\ltx_bat\ltx_sync\l|current_pulse()\linject(polarity, injection_duration, switch_pwr)\lvoltage_pulse(voltage, length, polarity)\l}", shape="record"];
 "1" -> "3" [arrowhead="empty", arrowtail="none"];
 "2" -> "3" [arrowhead="empty", arrowtail="none"];
 "5" -> "8" [arrowhead="empty", arrowtail="none"];
@@ -33,9 +33,9 @@ rankdir=BT
 "13" -> "14" [arrowhead="empty", arrowtail="none"];
 "15" -> "18" [arrowhead="empty", arrowtail="none"];
 "16" -> "18" [arrowhead="empty", arrowtail="none"];
-"17" -> "18" [arrowhead="empty", arrowtail="none"];
+"17" -> "15" [arrowhead="empty", arrowtail="none"];
 "19" -> "22" [arrowhead="empty", arrowtail="none"];
 "20" -> "22" [arrowhead="empty", arrowtail="none"];
-"21" -> "22" [arrowhead="empty", arrowtail="none"];
+"21" -> "19" [arrowhead="empty", arrowtail="none"];
 "11" -> "10" [arrowhead="diamond", arrowtail="none", fontcolor="green", label="_hw", style="solid"];
 }
diff --git a/uml_diagrams/classes_uml_ohmpi.dot.png b/uml_diagrams/classes_uml_ohmpi.dot.png
index aee047e1953c5924ff5078c615f9f46bffc3a7ae..119f5bc7235296e1369cbf350cb60fa17c5324b8 100644
Binary files a/uml_diagrams/classes_uml_ohmpi.dot.png and b/uml_diagrams/classes_uml_ohmpi.dot.png differ
diff --git a/uml_diagrams/classes_uml_ohmpi.dot.svg b/uml_diagrams/classes_uml_ohmpi.dot.svg
index a9efb920da2c9b15bcb79a3d04970923e89b84f5..d352fa4ce181488811b9a734deab1cf59a773b16 100644
--- a/uml_diagrams/classes_uml_ohmpi.dot.svg
+++ b/uml_diagrams/classes_uml_ohmpi.dot.svg
@@ -4,103 +4,102 @@
 <!-- Generated by graphviz version 2.43.0 (0)
  -->
 <!-- Title: classes_uml_ohmpi Pages: 1 -->
-<svg width="3998pt" height="949pt"
- viewBox="0.00 0.00 3997.50 949.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 945)">
+<svg width="3415pt" height="1221pt"
+ viewBox="0.00 0.00 3414.50 1221.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 1217)">
 <title>classes_uml_ohmpi</title>
-<polygon fill="white" stroke="transparent" points="-4,4 -4,-945 3993.5,-945 3993.5,4 -4,4"/>
+<polygon fill="white" stroke="transparent" points="-4,4 -4,-1217 3410.5,-1217 3410.5,4 -4,4"/>
 <!-- 0 -->
 <g id="node1" class="node">
 <title>0</title>
-<polygon fill="none" stroke="black" points="0,-113 0,-242 335,-242 335,-113 0,-113"/>
-<text text-anchor="middle" x="167.5" y="-226.8" font-family="Times,serif" font-size="14.00">CompressedSizedTimedRotatingFileHandler</text>
-<polyline fill="none" stroke="black" points="0,-219 335,-219 "/>
-<text text-anchor="start" x="8" y="-203.8" font-family="Times,serif" font-size="14.00">maxBytes : int</text>
-<text text-anchor="start" x="8" y="-188.8" font-family="Times,serif" font-size="14.00">stream</text>
-<text text-anchor="start" x="8" y="-173.8" font-family="Times,serif" font-size="14.00">zip_mode : int</text>
-<polyline fill="none" stroke="black" points="0,-166 335,-166 "/>
-<text text-anchor="start" x="8" y="-150.8" font-family="Times,serif" font-size="14.00">doRollover()</text>
-<text text-anchor="start" x="8" y="-135.8" font-family="Times,serif" font-size="14.00">find_last_rotated_file()</text>
-<text text-anchor="start" x="8" y="-120.8" font-family="Times,serif" font-size="14.00">shouldRollover(record)</text>
+<polygon fill="none" stroke="black" points="0,-105.5 0,-234.5 335,-234.5 335,-105.5 0,-105.5"/>
+<text text-anchor="middle" x="167.5" y="-219.3" font-family="Times,serif" font-size="14.00">CompressedSizedTimedRotatingFileHandler</text>
+<polyline fill="none" stroke="black" points="0,-211.5 335,-211.5 "/>
+<text text-anchor="start" x="8" y="-196.3" font-family="Times,serif" font-size="14.00">maxBytes : int</text>
+<text text-anchor="start" x="8" y="-181.3" font-family="Times,serif" font-size="14.00">stream</text>
+<text text-anchor="start" x="8" y="-166.3" font-family="Times,serif" font-size="14.00">zip_mode : int</text>
+<polyline fill="none" stroke="black" points="0,-158.5 335,-158.5 "/>
+<text text-anchor="start" x="8" y="-143.3" font-family="Times,serif" font-size="14.00">doRollover()</text>
+<text text-anchor="start" x="8" y="-128.3" font-family="Times,serif" font-size="14.00">find_last_rotated_file()</text>
+<text text-anchor="start" x="8" y="-113.3" font-family="Times,serif" font-size="14.00">shouldRollover(record)</text>
 </g>
 <!-- 1 -->
 <g id="node2" class="node">
 <title>1</title>
-<polygon fill="none" stroke="black" points="353.5,-128 353.5,-227 521.5,-227 521.5,-128 353.5,-128"/>
-<text text-anchor="middle" x="437.5" y="-211.8" font-family="Times,serif" font-size="14.00">Ctl</text>
-<polyline fill="none" stroke="black" points="353.5,-204 521.5,-204 "/>
-<text text-anchor="start" x="361.5" y="-188.8" font-family="Times,serif" font-size="14.00">board_name : str</text>
-<text text-anchor="start" x="361.5" y="-173.8" font-family="Times,serif" font-size="14.00">interfaces : dict</text>
-<text text-anchor="start" x="361.5" y="-158.8" font-family="Times,serif" font-size="14.00">max_cpu_temp : float</text>
-<polyline fill="none" stroke="black" points="353.5,-151 521.5,-151 "/>
-<text text-anchor="middle" x="437.5" y="-135.8" font-family="Times,serif" font-size="14.00"> </text>
+<polygon fill="none" stroke="black" points="353.5,-120.5 353.5,-219.5 521.5,-219.5 521.5,-120.5 353.5,-120.5"/>
+<text text-anchor="middle" x="437.5" y="-204.3" font-family="Times,serif" font-size="14.00">Ctl</text>
+<polyline fill="none" stroke="black" points="353.5,-196.5 521.5,-196.5 "/>
+<text text-anchor="start" x="361.5" y="-181.3" font-family="Times,serif" font-size="14.00">interfaces : dict</text>
+<text text-anchor="start" x="361.5" y="-166.3" font-family="Times,serif" font-size="14.00">max_cpu_temp : float</text>
+<text text-anchor="start" x="361.5" y="-151.3" font-family="Times,serif" font-size="14.00">model : str</text>
+<polyline fill="none" stroke="black" points="353.5,-143.5 521.5,-143.5 "/>
+<text text-anchor="middle" x="437.5" y="-128.3" font-family="Times,serif" font-size="14.00"> </text>
 </g>
 <!-- 3 -->
 <g id="node4" class="node">
 <title>3</title>
-<polygon fill="none" stroke="black" points="363,-594 363,-753 642,-753 642,-594 363,-594"/>
-<text text-anchor="middle" x="502.5" y="-737.8" font-family="Times,serif" font-size="14.00">CtlAbstract</text>
-<polyline fill="none" stroke="black" points="363,-730 642,-730 "/>
-<text text-anchor="start" x="371" y="-714.8" font-family="Times,serif" font-size="14.00">board_name</text>
-<text text-anchor="start" x="371" y="-699.8" font-family="Times,serif" font-size="14.00">connection : NoneType</text>
-<text text-anchor="start" x="371" y="-684.8" font-family="Times,serif" font-size="14.00">cpu_temperature</text>
-<text text-anchor="start" x="371" y="-669.8" font-family="Times,serif" font-size="14.00">exec_logger : NoneType, RootLogger</text>
-<text text-anchor="start" x="371" y="-654.8" font-family="Times,serif" font-size="14.00">interfaces : NoneType</text>
-<text text-anchor="start" x="371" y="-639.8" font-family="Times,serif" font-size="14.00">max_cpu_temp</text>
-<text text-anchor="start" x="371" y="-624.8" font-family="Times,serif" font-size="14.00">soh_logger : NoneType, RootLogger</text>
-<polyline fill="none" stroke="black" points="363,-617 642,-617 "/>
-<text text-anchor="middle" x="502.5" y="-601.8" font-family="Times,serif" font-size="14.00"> </text>
+<polygon fill="none" stroke="black" points="363.5,-579 363.5,-738 641.5,-738 641.5,-579 363.5,-579"/>
+<text text-anchor="middle" x="502.5" y="-722.8" font-family="Times,serif" font-size="14.00">CtlAbstract</text>
+<polyline fill="none" stroke="black" points="363.5,-715 641.5,-715 "/>
+<text text-anchor="start" x="371.5" y="-699.8" font-family="Times,serif" font-size="14.00">connection</text>
+<text text-anchor="start" x="371.5" y="-684.8" font-family="Times,serif" font-size="14.00">cpu_temperature</text>
+<text text-anchor="start" x="371.5" y="-669.8" font-family="Times,serif" font-size="14.00">exec_logger : RootLogger, NoneType</text>
+<text text-anchor="start" x="371.5" y="-654.8" font-family="Times,serif" font-size="14.00">interfaces : NoneType</text>
+<text text-anchor="start" x="371.5" y="-639.8" font-family="Times,serif" font-size="14.00">max_cpu_temp</text>
+<text text-anchor="start" x="371.5" y="-624.8" font-family="Times,serif" font-size="14.00">model</text>
+<text text-anchor="start" x="371.5" y="-609.8" font-family="Times,serif" font-size="14.00">soh_logger : RootLogger, NoneType</text>
+<polyline fill="none" stroke="black" points="363.5,-602 641.5,-602 "/>
+<text text-anchor="middle" x="502.5" y="-586.8" font-family="Times,serif" font-size="14.00"> </text>
 </g>
 <!-- 1&#45;&gt;3 -->
 <g id="edge1" class="edge">
 <title>1&#45;&gt;3</title>
-<path fill="none" stroke="black" d="M443.91,-227.22C454.81,-310.02 477.09,-479.36 490.81,-583.69"/>
-<polygon fill="none" stroke="black" points="487.38,-584.43 492.16,-593.89 494.32,-583.52 487.38,-584.43"/>
+<path fill="none" stroke="black" d="M444.03,-219.87C454.92,-301.38 476.94,-466.18 490.63,-568.66"/>
+<polygon fill="none" stroke="black" points="487.18,-569.23 491.97,-578.68 494.11,-568.31 487.18,-569.23"/>
 </g>
 <!-- 2 -->
 <g id="node3" class="node">
 <title>2</title>
-<polygon fill="none" stroke="black" points="539.5,-142.5 539.5,-212.5 593.5,-212.5 593.5,-142.5 539.5,-142.5"/>
-<text text-anchor="middle" x="566.5" y="-197.3" font-family="Times,serif" font-size="14.00">Ctl</text>
-<polyline fill="none" stroke="black" points="539.5,-189.5 593.5,-189.5 "/>
-<polyline fill="none" stroke="black" points="539.5,-165.5 593.5,-165.5 "/>
-<text text-anchor="middle" x="566.5" y="-150.3" font-family="Times,serif" font-size="14.00"> </text>
+<polygon fill="none" stroke="black" points="539.5,-135 539.5,-205 593.5,-205 593.5,-135 539.5,-135"/>
+<text text-anchor="middle" x="566.5" y="-189.8" font-family="Times,serif" font-size="14.00">Ctl</text>
+<polyline fill="none" stroke="black" points="539.5,-182 593.5,-182 "/>
+<polyline fill="none" stroke="black" points="539.5,-158 593.5,-158 "/>
+<text text-anchor="middle" x="566.5" y="-142.8" font-family="Times,serif" font-size="14.00"> </text>
 </g>
 <!-- 2&#45;&gt;3 -->
 <g id="edge2" class="edge">
 <title>2&#45;&gt;3</title>
-<path fill="none" stroke="black" d="M562.05,-212.84C552.22,-288.7 528.4,-472.57 514.04,-583.4"/>
-<polygon fill="none" stroke="black" points="510.54,-583.2 512.73,-593.56 517.48,-584.1 510.54,-583.2"/>
+<path fill="none" stroke="black" d="M562,-205.21C552.18,-279.88 528.55,-459.48 514.2,-568.56"/>
+<polygon fill="none" stroke="black" points="510.72,-568.2 512.88,-578.57 517.66,-569.11 510.72,-568.2"/>
 </g>
 <!-- 4 -->
 <g id="node5" class="node">
 <title>4</title>
-<polygon fill="none" stroke="black" points="611.5,-60.5 611.5,-294.5 745.5,-294.5 745.5,-60.5 611.5,-60.5"/>
-<text text-anchor="middle" x="678.5" y="-279.3" font-family="Times,serif" font-size="14.00">MQTTHandler</text>
-<polyline fill="none" stroke="black" points="611.5,-271.5 745.5,-271.5 "/>
-<text text-anchor="start" x="619.5" y="-256.3" font-family="Times,serif" font-size="14.00">auth : NoneType</text>
-<text text-anchor="start" x="619.5" y="-241.3" font-family="Times,serif" font-size="14.00">client_id : str</text>
-<text text-anchor="start" x="619.5" y="-226.3" font-family="Times,serif" font-size="14.00">hostname</text>
-<text text-anchor="start" x="619.5" y="-211.3" font-family="Times,serif" font-size="14.00">keepalive : int</text>
-<text text-anchor="start" x="619.5" y="-196.3" font-family="Times,serif" font-size="14.00">port : int</text>
-<text text-anchor="start" x="619.5" y="-181.3" font-family="Times,serif" font-size="14.00">protocol</text>
-<text text-anchor="start" x="619.5" y="-166.3" font-family="Times,serif" font-size="14.00">qos : int</text>
-<text text-anchor="start" x="619.5" y="-151.3" font-family="Times,serif" font-size="14.00">retain : bool</text>
-<text text-anchor="start" x="619.5" y="-136.3" font-family="Times,serif" font-size="14.00">tls : NoneType</text>
-<text text-anchor="start" x="619.5" y="-121.3" font-family="Times,serif" font-size="14.00">topic</text>
-<text text-anchor="start" x="619.5" y="-106.3" font-family="Times,serif" font-size="14.00">transport : str</text>
-<text text-anchor="start" x="619.5" y="-91.3" font-family="Times,serif" font-size="14.00">will : NoneType</text>
-<polyline fill="none" stroke="black" points="611.5,-83.5 745.5,-83.5 "/>
-<text text-anchor="start" x="619.5" y="-68.3" font-family="Times,serif" font-size="14.00">emit(record)</text>
+<polygon fill="none" stroke="black" points="611.5,-53 611.5,-287 745.5,-287 745.5,-53 611.5,-53"/>
+<text text-anchor="middle" x="678.5" y="-271.8" font-family="Times,serif" font-size="14.00">MQTTHandler</text>
+<polyline fill="none" stroke="black" points="611.5,-264 745.5,-264 "/>
+<text text-anchor="start" x="619.5" y="-248.8" font-family="Times,serif" font-size="14.00">auth : NoneType</text>
+<text text-anchor="start" x="619.5" y="-233.8" font-family="Times,serif" font-size="14.00">client_id : str</text>
+<text text-anchor="start" x="619.5" y="-218.8" font-family="Times,serif" font-size="14.00">hostname</text>
+<text text-anchor="start" x="619.5" y="-203.8" font-family="Times,serif" font-size="14.00">keepalive : int</text>
+<text text-anchor="start" x="619.5" y="-188.8" font-family="Times,serif" font-size="14.00">port : int</text>
+<text text-anchor="start" x="619.5" y="-173.8" font-family="Times,serif" font-size="14.00">protocol</text>
+<text text-anchor="start" x="619.5" y="-158.8" font-family="Times,serif" font-size="14.00">qos : int</text>
+<text text-anchor="start" x="619.5" y="-143.8" font-family="Times,serif" font-size="14.00">retain : bool</text>
+<text text-anchor="start" x="619.5" y="-128.8" font-family="Times,serif" font-size="14.00">tls : NoneType</text>
+<text text-anchor="start" x="619.5" y="-113.8" font-family="Times,serif" font-size="14.00">topic</text>
+<text text-anchor="start" x="619.5" y="-98.8" font-family="Times,serif" font-size="14.00">transport : str</text>
+<text text-anchor="start" x="619.5" y="-83.8" font-family="Times,serif" font-size="14.00">will : NoneType</text>
+<polyline fill="none" stroke="black" points="611.5,-76 745.5,-76 "/>
+<text text-anchor="start" x="619.5" y="-60.8" font-family="Times,serif" font-size="14.00">emit(record)</text>
 </g>
 <!-- 5 -->
 <g id="node6" class="node">
 <title>5</title>
-<polygon fill="none" stroke="black" points="763.5,-128 763.5,-227 979.5,-227 979.5,-128 763.5,-128"/>
-<text text-anchor="middle" x="871.5" y="-211.8" font-family="Times,serif" font-size="14.00">Mux</text>
-<polyline fill="none" stroke="black" points="763.5,-204 979.5,-204 "/>
-<text text-anchor="start" x="771.5" y="-188.8" font-family="Times,serif" font-size="14.00">addresses : dict</text>
-<text text-anchor="start" x="771.5" y="-173.8" font-family="Times,serif" font-size="14.00">io</text>
+<polygon fill="none" stroke="black" points="763.5,-128 763.5,-212 979.5,-212 979.5,-128 763.5,-128"/>
+<text text-anchor="middle" x="871.5" y="-196.8" font-family="Times,serif" font-size="14.00">Mux</text>
+<polyline fill="none" stroke="black" points="763.5,-189 979.5,-189 "/>
+<text text-anchor="start" x="771.5" y="-173.8" font-family="Times,serif" font-size="14.00">addresses : dict</text>
 <polyline fill="none" stroke="black" points="763.5,-166 979.5,-166 "/>
 <text text-anchor="start" x="771.5" y="-150.8" font-family="Times,serif" font-size="14.00">reset()</text>
 <text text-anchor="start" x="771.5" y="-135.8" font-family="Times,serif" font-size="14.00">switch_one(elec, role, state)</text>
@@ -108,407 +107,376 @@
 <!-- 8 -->
 <g id="node9" class="node">
 <title>8</title>
-<polygon fill="none" stroke="black" points="907,-564 907,-783 1194,-783 1194,-564 907,-564"/>
-<text text-anchor="middle" x="1050.5" y="-767.8" font-family="Times,serif" font-size="14.00">MuxAbstract</text>
-<polyline fill="none" stroke="black" points="907,-760 1194,-760 "/>
-<text text-anchor="start" x="915" y="-744.8" font-family="Times,serif" font-size="14.00">addresses</text>
-<text text-anchor="start" x="915" y="-729.8" font-family="Times,serif" font-size="14.00">barrier</text>
-<text text-anchor="start" x="915" y="-714.8" font-family="Times,serif" font-size="14.00">board_id</text>
-<text text-anchor="start" x="915" y="-699.8" font-family="Times,serif" font-size="14.00">board_name</text>
+<polygon fill="none" stroke="black" points="907,-549 907,-768 1194,-768 1194,-549 907,-549"/>
+<text text-anchor="middle" x="1050.5" y="-752.8" font-family="Times,serif" font-size="14.00">MuxAbstract</text>
+<polyline fill="none" stroke="black" points="907,-745 1194,-745 "/>
+<text text-anchor="start" x="915" y="-729.8" font-family="Times,serif" font-size="14.00">addresses</text>
+<text text-anchor="start" x="915" y="-714.8" font-family="Times,serif" font-size="14.00">barrier</text>
+<text text-anchor="start" x="915" y="-699.8" font-family="Times,serif" font-size="14.00">board_id</text>
 <text text-anchor="start" x="915" y="-684.8" font-family="Times,serif" font-size="14.00">cabling : dict</text>
 <text text-anchor="start" x="915" y="-669.8" font-family="Times,serif" font-size="14.00">connection</text>
-<text text-anchor="start" x="915" y="-654.8" font-family="Times,serif" font-size="14.00">exec_logger : NoneType, RootLogger</text>
-<text text-anchor="start" x="915" y="-639.8" font-family="Times,serif" font-size="14.00">soh_logger : NoneType, RootLogger</text>
-<polyline fill="none" stroke="black" points="907,-632 1194,-632 "/>
-<text text-anchor="start" x="915" y="-616.8" font-family="Times,serif" font-size="14.00">reset()</text>
-<text text-anchor="start" x="915" y="-601.8" font-family="Times,serif" font-size="14.00">switch(elec_dict, state, bypass_check)</text>
-<text text-anchor="start" x="915" y="-586.8" font-family="Times,serif" font-size="14.00">switch_one(elec, role, state)</text>
-<text text-anchor="start" x="915" y="-571.8" font-family="Times,serif" font-size="14.00">test(elec_dict, activation_time)</text>
+<text text-anchor="start" x="915" y="-654.8" font-family="Times,serif" font-size="14.00">exec_logger</text>
+<text text-anchor="start" x="915" y="-639.8" font-family="Times,serif" font-size="14.00">model</text>
+<text text-anchor="start" x="915" y="-624.8" font-family="Times,serif" font-size="14.00">soh_logger</text>
+<polyline fill="none" stroke="black" points="907,-617 1194,-617 "/>
+<text text-anchor="start" x="915" y="-601.8" font-family="Times,serif" font-size="14.00">reset()</text>
+<text text-anchor="start" x="915" y="-586.8" font-family="Times,serif" font-size="14.00">switch(elec_dict, state, bypass_check)</text>
+<text text-anchor="start" x="915" y="-571.8" font-family="Times,serif" font-size="14.00">switch_one(elec, role, state)</text>
+<text text-anchor="start" x="915" y="-556.8" font-family="Times,serif" font-size="14.00">test(elec_dict, activation_time)</text>
 </g>
 <!-- 5&#45;&gt;8 -->
 <g id="edge3" class="edge">
 <title>5&#45;&gt;8</title>
-<path fill="none" stroke="black" d="M889.15,-227.22C916.48,-302.64 969.82,-449.83 1007.68,-554.32"/>
-<polygon fill="none" stroke="black" points="1004.48,-555.77 1011.18,-563.98 1011.06,-553.38 1004.48,-555.77"/>
+<path fill="none" stroke="black" d="M886.61,-212.06C912.91,-283.55 968.04,-433.38 1007.08,-539.49"/>
+<polygon fill="none" stroke="black" points="1003.83,-540.8 1010.57,-548.98 1010.4,-538.39 1003.83,-540.8"/>
 </g>
 <!-- 6 -->
 <g id="node7" class="node">
 <title>6</title>
-<polygon fill="none" stroke="black" points="997.5,-127.5 997.5,-227.5 1103.5,-227.5 1103.5,-127.5 997.5,-127.5"/>
-<text text-anchor="middle" x="1050.5" y="-212.3" font-family="Times,serif" font-size="14.00">Mux</text>
-<polyline fill="none" stroke="black" points="997.5,-204.5 1103.5,-204.5 "/>
-<polyline fill="none" stroke="black" points="997.5,-180.5 1103.5,-180.5 "/>
-<text text-anchor="start" x="1005.5" y="-165.3" font-family="Times,serif" font-size="14.00">reset()</text>
-<text text-anchor="start" x="1005.5" y="-150.3" font-family="Times,serif" font-size="14.00">switch_one()</text>
-<text text-anchor="start" x="1005.5" y="-135.3" font-family="Times,serif" font-size="14.00">test()</text>
+<polygon fill="none" stroke="black" points="997.5,-120 997.5,-220 1103.5,-220 1103.5,-120 997.5,-120"/>
+<text text-anchor="middle" x="1050.5" y="-204.8" font-family="Times,serif" font-size="14.00">Mux</text>
+<polyline fill="none" stroke="black" points="997.5,-197 1103.5,-197 "/>
+<polyline fill="none" stroke="black" points="997.5,-173 1103.5,-173 "/>
+<text text-anchor="start" x="1005.5" y="-157.8" font-family="Times,serif" font-size="14.00">reset()</text>
+<text text-anchor="start" x="1005.5" y="-142.8" font-family="Times,serif" font-size="14.00">switch_one()</text>
+<text text-anchor="start" x="1005.5" y="-127.8" font-family="Times,serif" font-size="14.00">test()</text>
 </g>
 <!-- 6&#45;&gt;8 -->
 <g id="edge4" class="edge">
 <title>6&#45;&gt;8</title>
-<path fill="none" stroke="black" d="M1050.5,-227.67C1050.5,-303.01 1050.5,-449.22 1050.5,-553.45"/>
-<polygon fill="none" stroke="black" points="1047,-553.72 1050.5,-563.72 1054,-553.72 1047,-553.72"/>
+<path fill="none" stroke="black" d="M1050.5,-220.33C1050.5,-294.43 1050.5,-436.69 1050.5,-538.82"/>
+<polygon fill="none" stroke="black" points="1047,-538.89 1050.5,-548.89 1054,-538.89 1047,-538.89"/>
 </g>
 <!-- 7 -->
 <g id="node8" class="node">
 <title>7</title>
-<polygon fill="none" stroke="black" points="1121.5,-135.5 1121.5,-219.5 1337.5,-219.5 1337.5,-135.5 1121.5,-135.5"/>
-<text text-anchor="middle" x="1229.5" y="-204.3" font-family="Times,serif" font-size="14.00">Mux</text>
-<polyline fill="none" stroke="black" points="1121.5,-196.5 1337.5,-196.5 "/>
-<text text-anchor="start" x="1129.5" y="-181.3" font-family="Times,serif" font-size="14.00">addresses : dict</text>
-<polyline fill="none" stroke="black" points="1121.5,-173.5 1337.5,-173.5 "/>
-<text text-anchor="start" x="1129.5" y="-158.3" font-family="Times,serif" font-size="14.00">reset()</text>
-<text text-anchor="start" x="1129.5" y="-143.3" font-family="Times,serif" font-size="14.00">switch_one(elec, role, state)</text>
+<polygon fill="none" stroke="black" points="1121.5,-128 1121.5,-212 1337.5,-212 1337.5,-128 1121.5,-128"/>
+<text text-anchor="middle" x="1229.5" y="-196.8" font-family="Times,serif" font-size="14.00">Mux</text>
+<polyline fill="none" stroke="black" points="1121.5,-189 1337.5,-189 "/>
+<text text-anchor="start" x="1129.5" y="-173.8" font-family="Times,serif" font-size="14.00">addresses : dict</text>
+<polyline fill="none" stroke="black" points="1121.5,-166 1337.5,-166 "/>
+<text text-anchor="start" x="1129.5" y="-150.8" font-family="Times,serif" font-size="14.00">reset()</text>
+<text text-anchor="start" x="1129.5" y="-135.8" font-family="Times,serif" font-size="14.00">switch_one(elec, role, state)</text>
 </g>
 <!-- 7&#45;&gt;8 -->
 <g id="edge5" class="edge">
 <title>7&#45;&gt;8</title>
-<path fill="none" stroke="black" d="M1214.55,-219.77C1188.18,-292.53 1132.52,-446.15 1093.37,-554.19"/>
-<polygon fill="none" stroke="black" points="1089.99,-553.25 1089.87,-563.84 1096.57,-555.63 1089.99,-553.25"/>
+<path fill="none" stroke="black" d="M1214.39,-212.06C1188.09,-283.55 1132.96,-433.38 1093.92,-539.49"/>
+<polygon fill="none" stroke="black" points="1090.6,-538.39 1090.43,-548.98 1097.17,-540.8 1090.6,-538.39"/>
 </g>
 <!-- 9 -->
 <g id="node10" class="node">
 <title>9</title>
-<polygon fill="none" stroke="black" points="1356,-142.5 1356,-212.5 1447,-212.5 1447,-142.5 1356,-142.5"/>
-<text text-anchor="middle" x="1401.5" y="-197.3" font-family="Times,serif" font-size="14.00">MyServer</text>
-<polyline fill="none" stroke="black" points="1356,-189.5 1447,-189.5 "/>
-<polyline fill="none" stroke="black" points="1356,-165.5 1447,-165.5 "/>
-<text text-anchor="start" x="1364" y="-150.3" font-family="Times,serif" font-size="14.00">do_POST()</text>
+<polygon fill="none" stroke="black" points="1356,-135 1356,-205 1447,-205 1447,-135 1356,-135"/>
+<text text-anchor="middle" x="1401.5" y="-189.8" font-family="Times,serif" font-size="14.00">MyServer</text>
+<polyline fill="none" stroke="black" points="1356,-182 1447,-182 "/>
+<polyline fill="none" stroke="black" points="1356,-158 1447,-158 "/>
+<text text-anchor="start" x="1364" y="-142.8" font-family="Times,serif" font-size="14.00">do_POST()</text>
 </g>
 <!-- 10 -->
 <g id="node11" class="node">
 <title>10</title>
-<polygon fill="none" stroke="black" points="1409,-406.5 1409,-940.5 2182,-940.5 2182,-406.5 1409,-406.5"/>
-<text text-anchor="middle" x="1795.5" y="-925.3" font-family="Times,serif" font-size="14.00">OhmPi</text>
-<polyline fill="none" stroke="black" points="1409,-917.5 2182,-917.5 "/>
-<text text-anchor="start" x="1417" y="-902.3" font-family="Times,serif" font-size="14.00">cmd_id : NoneType</text>
-<text text-anchor="start" x="1417" y="-887.3" font-family="Times,serif" font-size="14.00">controller : NoneType</text>
-<text text-anchor="start" x="1417" y="-872.3" font-family="Times,serif" font-size="14.00">data_logger : NoneType, RootLogger</text>
-<text text-anchor="start" x="1417" y="-857.3" font-family="Times,serif" font-size="14.00">exec_logger : NoneType, RootLogger</text>
-<text text-anchor="start" x="1417" y="-842.3" font-family="Times,serif" font-size="14.00">id : str</text>
-<text text-anchor="start" x="1417" y="-827.3" font-family="Times,serif" font-size="14.00">mqtt : bool</text>
-<text text-anchor="start" x="1417" y="-812.3" font-family="Times,serif" font-size="14.00">nb_samples : int</text>
-<text text-anchor="start" x="1417" y="-797.3" font-family="Times,serif" font-size="14.00">on_pi : NoneType, bool</text>
-<text text-anchor="start" x="1417" y="-782.3" font-family="Times,serif" font-size="14.00">sequence</text>
-<text text-anchor="start" x="1417" y="-767.3" font-family="Times,serif" font-size="14.00">sequence : ndarray, NoneType</text>
-<text text-anchor="start" x="1417" y="-752.3" font-family="Times,serif" font-size="14.00">settings : dict</text>
-<text text-anchor="start" x="1417" y="-737.3" font-family="Times,serif" font-size="14.00">soh_logger : NoneType, RootLogger</text>
-<text text-anchor="start" x="1417" y="-722.3" font-family="Times,serif" font-size="14.00">status : str</text>
-<text text-anchor="start" x="1417" y="-707.3" font-family="Times,serif" font-size="14.00">thread : Thread, NoneType</text>
-<polyline fill="none" stroke="black" points="1409,-699.5 2182,-699.5 "/>
-<text text-anchor="start" x="1417" y="-684.3" font-family="Times,serif" font-size="14.00">append_and_save(filename, last_measurement, cmd_id)</text>
-<text text-anchor="start" x="1417" y="-669.3" font-family="Times,serif" font-size="14.00">get_data(survey_names, cmd_id)</text>
-<text text-anchor="start" x="1417" y="-654.3" font-family="Times,serif" font-size="14.00">get_deprecated_methods(cls)</text>
-<text text-anchor="start" x="1417" y="-639.3" font-family="Times,serif" font-size="14.00">interrupt(cmd_id)</text>
-<text text-anchor="start" x="1417" y="-624.3" font-family="Times,serif" font-size="14.00">load_sequence(filename, cmd_id)</text>
-<text text-anchor="start" x="1417" y="-609.3" font-family="Times,serif" font-size="14.00">quit(cmd_id)</text>
-<text text-anchor="start" x="1417" y="-594.3" font-family="Times,serif" font-size="14.00">remove_data(cmd_id)</text>
-<text text-anchor="start" x="1417" y="-579.3" font-family="Times,serif" font-size="14.00">reset_mux(cmd_id)</text>
-<text text-anchor="start" x="1417" y="-564.3" font-family="Times,serif" font-size="14.00">restart(cmd_id)</text>
-<text text-anchor="start" x="1417" y="-549.3" font-family="Times,serif" font-size="14.00">rs_check(tx_volt, cmd_id)</text>
-<text text-anchor="start" x="1417" y="-534.3" font-family="Times,serif" font-size="14.00">run_measurement(quad, nb_stack, injection_duration, autogain, strategy, tx_volt, best_tx_injtime, cmd_id)</text>
-<text text-anchor="start" x="1417" y="-519.3" font-family="Times,serif" font-size="14.00">run_multiple_sequences(cmd_id, sequence_delay, nb_meas)</text>
-<text text-anchor="start" x="1417" y="-504.3" font-family="Times,serif" font-size="14.00">run_sequence(cmd_id)</text>
-<text text-anchor="start" x="1417" y="-489.3" font-family="Times,serif" font-size="14.00">run_sequence_async(cmd_id)</text>
-<text text-anchor="start" x="1417" y="-474.3" font-family="Times,serif" font-size="14.00">set_sequence(sequence, cmd_id)</text>
-<text text-anchor="start" x="1417" y="-459.3" font-family="Times,serif" font-size="14.00">switch_mux_off(quadrupole, cmd_id)</text>
-<text text-anchor="start" x="1417" y="-444.3" font-family="Times,serif" font-size="14.00">switch_mux_on(quadrupole, bypass_check, cmd_id)</text>
-<text text-anchor="start" x="1417" y="-429.3" font-family="Times,serif" font-size="14.00">test_mux(activation_time, mux_id, cmd_id)</text>
-<text text-anchor="start" x="1417" y="-414.3" font-family="Times,serif" font-size="14.00">update_settings(settings, cmd_id)</text>
+<polygon fill="none" stroke="black" points="1409,-391.5 1409,-925.5 2182,-925.5 2182,-391.5 1409,-391.5"/>
+<text text-anchor="middle" x="1795.5" y="-910.3" font-family="Times,serif" font-size="14.00">OhmPi</text>
+<polyline fill="none" stroke="black" points="1409,-902.5 2182,-902.5 "/>
+<text text-anchor="start" x="1417" y="-887.3" font-family="Times,serif" font-size="14.00">cmd_id : NoneType</text>
+<text text-anchor="start" x="1417" y="-872.3" font-family="Times,serif" font-size="14.00">controller : NoneType</text>
+<text text-anchor="start" x="1417" y="-857.3" font-family="Times,serif" font-size="14.00">data_logger : RootLogger, NoneType</text>
+<text text-anchor="start" x="1417" y="-842.3" font-family="Times,serif" font-size="14.00">exec_logger : RootLogger, NoneType</text>
+<text text-anchor="start" x="1417" y="-827.3" font-family="Times,serif" font-size="14.00">id : str</text>
+<text text-anchor="start" x="1417" y="-812.3" font-family="Times,serif" font-size="14.00">mqtt : bool</text>
+<text text-anchor="start" x="1417" y="-797.3" font-family="Times,serif" font-size="14.00">nb_samples : int</text>
+<text text-anchor="start" x="1417" y="-782.3" font-family="Times,serif" font-size="14.00">on_pi : bool, NoneType</text>
+<text text-anchor="start" x="1417" y="-767.3" font-family="Times,serif" font-size="14.00">sequence</text>
+<text text-anchor="start" x="1417" y="-752.3" font-family="Times,serif" font-size="14.00">sequence : NoneType, ndarray</text>
+<text text-anchor="start" x="1417" y="-737.3" font-family="Times,serif" font-size="14.00">settings : dict</text>
+<text text-anchor="start" x="1417" y="-722.3" font-family="Times,serif" font-size="14.00">soh_logger : RootLogger, NoneType</text>
+<text text-anchor="start" x="1417" y="-707.3" font-family="Times,serif" font-size="14.00">status : str</text>
+<text text-anchor="start" x="1417" y="-692.3" font-family="Times,serif" font-size="14.00">thread : Thread, NoneType</text>
+<polyline fill="none" stroke="black" points="1409,-684.5 2182,-684.5 "/>
+<text text-anchor="start" x="1417" y="-669.3" font-family="Times,serif" font-size="14.00">append_and_save(filename, last_measurement, cmd_id)</text>
+<text text-anchor="start" x="1417" y="-654.3" font-family="Times,serif" font-size="14.00">get_data(survey_names, cmd_id)</text>
+<text text-anchor="start" x="1417" y="-639.3" font-family="Times,serif" font-size="14.00">get_deprecated_methods(cls)</text>
+<text text-anchor="start" x="1417" y="-624.3" font-family="Times,serif" font-size="14.00">interrupt(cmd_id)</text>
+<text text-anchor="start" x="1417" y="-609.3" font-family="Times,serif" font-size="14.00">load_sequence(filename, cmd_id)</text>
+<text text-anchor="start" x="1417" y="-594.3" font-family="Times,serif" font-size="14.00">quit(cmd_id)</text>
+<text text-anchor="start" x="1417" y="-579.3" font-family="Times,serif" font-size="14.00">remove_data(cmd_id)</text>
+<text text-anchor="start" x="1417" y="-564.3" font-family="Times,serif" font-size="14.00">reset_mux(cmd_id)</text>
+<text text-anchor="start" x="1417" y="-549.3" font-family="Times,serif" font-size="14.00">restart(cmd_id)</text>
+<text text-anchor="start" x="1417" y="-534.3" font-family="Times,serif" font-size="14.00">rs_check(tx_volt, cmd_id)</text>
+<text text-anchor="start" x="1417" y="-519.3" font-family="Times,serif" font-size="14.00">run_measurement(quad, nb_stack, injection_duration, autogain, strategy, tx_volt, best_tx_injtime, cmd_id)</text>
+<text text-anchor="start" x="1417" y="-504.3" font-family="Times,serif" font-size="14.00">run_multiple_sequences(cmd_id, sequence_delay, nb_meas)</text>
+<text text-anchor="start" x="1417" y="-489.3" font-family="Times,serif" font-size="14.00">run_sequence(cmd_id)</text>
+<text text-anchor="start" x="1417" y="-474.3" font-family="Times,serif" font-size="14.00">run_sequence_async(cmd_id)</text>
+<text text-anchor="start" x="1417" y="-459.3" font-family="Times,serif" font-size="14.00">set_sequence(sequence, cmd_id)</text>
+<text text-anchor="start" x="1417" y="-444.3" font-family="Times,serif" font-size="14.00">switch_mux_off(quadrupole, cmd_id)</text>
+<text text-anchor="start" x="1417" y="-429.3" font-family="Times,serif" font-size="14.00">switch_mux_on(quadrupole, bypass_check, cmd_id)</text>
+<text text-anchor="start" x="1417" y="-414.3" font-family="Times,serif" font-size="14.00">test_mux(activation_time, mux_id, cmd_id)</text>
+<text text-anchor="start" x="1417" y="-399.3" font-family="Times,serif" font-size="14.00">update_settings(settings, cmd_id)</text>
 </g>
 <!-- 11 -->
 <g id="node12" class="node">
 <title>11</title>
-<polygon fill="none" stroke="black" points="1465.5,-8 1465.5,-347 2125.5,-347 2125.5,-8 1465.5,-8"/>
-<text text-anchor="middle" x="1795.5" y="-331.8" font-family="Times,serif" font-size="14.00">OhmPiHardware</text>
-<polyline fill="none" stroke="black" points="1465.5,-324 2125.5,-324 "/>
-<text text-anchor="start" x="1473.5" y="-308.8" font-family="Times,serif" font-size="14.00">ctl</text>
-<text text-anchor="start" x="1473.5" y="-293.8" font-family="Times,serif" font-size="14.00">data_logger : NoneType, RootLogger</text>
-<text text-anchor="start" x="1473.5" y="-278.8" font-family="Times,serif" font-size="14.00">exec_logger : NoneType, RootLogger</text>
-<text text-anchor="start" x="1473.5" y="-263.8" font-family="Times,serif" font-size="14.00">last_dev</text>
-<text text-anchor="start" x="1473.5" y="-248.8" font-family="Times,serif" font-size="14.00">last_rho</text>
-<text text-anchor="start" x="1473.5" y="-233.8" font-family="Times,serif" font-size="14.00">mux_barrier : Barrier</text>
-<text text-anchor="start" x="1473.5" y="-218.8" font-family="Times,serif" font-size="14.00">mux_boards : dict</text>
-<text text-anchor="start" x="1473.5" y="-203.8" font-family="Times,serif" font-size="14.00">pulses</text>
-<text text-anchor="start" x="1473.5" y="-188.8" font-family="Times,serif" font-size="14.00">pwr</text>
-<text text-anchor="start" x="1473.5" y="-173.8" font-family="Times,serif" font-size="14.00">readings : ndarray</text>
-<text text-anchor="start" x="1473.5" y="-158.8" font-family="Times,serif" font-size="14.00">rx</text>
-<text text-anchor="start" x="1473.5" y="-143.8" font-family="Times,serif" font-size="14.00">soh_logger : NoneType, RootLogger</text>
-<text text-anchor="start" x="1473.5" y="-128.8" font-family="Times,serif" font-size="14.00">sp</text>
-<text text-anchor="start" x="1473.5" y="-113.8" font-family="Times,serif" font-size="14.00">tx</text>
-<text text-anchor="start" x="1473.5" y="-98.8" font-family="Times,serif" font-size="14.00">tx_sync : Event</text>
-<polyline fill="none" stroke="black" points="1465.5,-91 2125.5,-91 "/>
-<text text-anchor="start" x="1473.5" y="-75.8" font-family="Times,serif" font-size="14.00">calibrate_rx_bias()</text>
-<text text-anchor="start" x="1473.5" y="-60.8" font-family="Times,serif" font-size="14.00">reset_mux()</text>
-<text text-anchor="start" x="1473.5" y="-45.8" font-family="Times,serif" font-size="14.00">switch_mux(electrodes, roles, state)</text>
-<text text-anchor="start" x="1473.5" y="-30.8" font-family="Times,serif" font-size="14.00">test_mux(channel, activation_time)</text>
-<text text-anchor="start" x="1473.5" y="-15.8" font-family="Times,serif" font-size="14.00">vab_square_wave(vab, cycle_duration, sampling_rate, cycles, polarity, duty_cycle, append)</text>
+<polygon fill="none" stroke="black" points="1465.5,-0.5 1465.5,-339.5 2125.5,-339.5 2125.5,-0.5 1465.5,-0.5"/>
+<text text-anchor="middle" x="1795.5" y="-324.3" font-family="Times,serif" font-size="14.00">OhmPiHardware</text>
+<polyline fill="none" stroke="black" points="1465.5,-316.5 2125.5,-316.5 "/>
+<text text-anchor="start" x="1473.5" y="-301.3" font-family="Times,serif" font-size="14.00">ctl</text>
+<text text-anchor="start" x="1473.5" y="-286.3" font-family="Times,serif" font-size="14.00">data_logger</text>
+<text text-anchor="start" x="1473.5" y="-271.3" font-family="Times,serif" font-size="14.00">exec_logger</text>
+<text text-anchor="start" x="1473.5" y="-256.3" font-family="Times,serif" font-size="14.00">mux_barrier : Barrier</text>
+<text text-anchor="start" x="1473.5" y="-241.3" font-family="Times,serif" font-size="14.00">mux_boards : dict</text>
+<text text-anchor="start" x="1473.5" y="-226.3" font-family="Times,serif" font-size="14.00">pulses</text>
+<text text-anchor="start" x="1473.5" y="-211.3" font-family="Times,serif" font-size="14.00">pwr</text>
+<text text-anchor="start" x="1473.5" y="-196.3" font-family="Times,serif" font-size="14.00">readings : ndarray</text>
+<text text-anchor="start" x="1473.5" y="-181.3" font-family="Times,serif" font-size="14.00">rx</text>
+<text text-anchor="start" x="1473.5" y="-166.3" font-family="Times,serif" font-size="14.00">soh_logger</text>
+<text text-anchor="start" x="1473.5" y="-151.3" font-family="Times,serif" font-size="14.00">sp</text>
+<text text-anchor="start" x="1473.5" y="-136.3" font-family="Times,serif" font-size="14.00">tx</text>
+<text text-anchor="start" x="1473.5" y="-121.3" font-family="Times,serif" font-size="14.00">tx_sync : Event</text>
+<polyline fill="none" stroke="black" points="1465.5,-113.5 2125.5,-113.5 "/>
+<text text-anchor="start" x="1473.5" y="-98.3" font-family="Times,serif" font-size="14.00">calibrate_rx_bias()</text>
+<text text-anchor="start" x="1473.5" y="-83.3" font-family="Times,serif" font-size="14.00">last_dev(delay)</text>
+<text text-anchor="start" x="1473.5" y="-68.3" font-family="Times,serif" font-size="14.00">last_resistance(delay)</text>
+<text text-anchor="start" x="1473.5" y="-53.3" font-family="Times,serif" font-size="14.00">reset_mux()</text>
+<text text-anchor="start" x="1473.5" y="-38.3" font-family="Times,serif" font-size="14.00">switch_mux(electrodes, roles, state)</text>
+<text text-anchor="start" x="1473.5" y="-23.3" font-family="Times,serif" font-size="14.00">test_mux(channel, activation_time)</text>
+<text text-anchor="start" x="1473.5" y="-8.3" font-family="Times,serif" font-size="14.00">vab_square_wave(vab, cycle_duration, sampling_rate, cycles, polarity, duty_cycle, append)</text>
 </g>
 <!-- 11&#45;&gt;10 -->
 <g id="edge14" class="edge">
 <title>11&#45;&gt;10</title>
-<path fill="none" stroke="black" d="M1795.5,-347.13C1795.5,-362.38 1795.5,-378.08 1795.5,-393.96"/>
-<polygon fill="black" stroke="black" points="1795.5,-394.14 1799.5,-400.14 1795.5,-406.14 1791.5,-400.14 1795.5,-394.14"/>
-<text text-anchor="middle" x="1809.5" y="-376.8" font-family="Times,serif" font-size="14.00" fill="green">_hw</text>
+<path fill="none" stroke="black" d="M1795.5,-339.79C1795.5,-352.61 1795.5,-365.73 1795.5,-378.99"/>
+<polygon fill="black" stroke="black" points="1795.5,-379.21 1799.5,-385.21 1795.5,-391.21 1791.5,-385.21 1795.5,-379.21"/>
+<text text-anchor="middle" x="1809.5" y="-361.8" font-family="Times,serif" font-size="14.00" fill="green">_hw</text>
 </g>
 <!-- 12 -->
 <g id="node13" class="node">
 <title>12</title>
-<polygon fill="none" stroke="black" points="2143.5,-90.5 2143.5,-264.5 2337.5,-264.5 2337.5,-90.5 2143.5,-90.5"/>
-<text text-anchor="middle" x="2240.5" y="-249.3" font-family="Times,serif" font-size="14.00">Pwr</text>
-<polyline fill="none" stroke="black" points="2143.5,-241.5 2337.5,-241.5 "/>
-<text text-anchor="start" x="2151.5" y="-226.3" font-family="Times,serif" font-size="14.00">connection</text>
-<text text-anchor="start" x="2151.5" y="-211.3" font-family="Times,serif" font-size="14.00">ctl</text>
-<text text-anchor="start" x="2151.5" y="-196.3" font-family="Times,serif" font-size="14.00">current</text>
-<text text-anchor="start" x="2151.5" y="-181.3" font-family="Times,serif" font-size="14.00">current_max</text>
-<text text-anchor="start" x="2151.5" y="-166.3" font-family="Times,serif" font-size="14.00">voltage</text>
-<text text-anchor="start" x="2151.5" y="-151.3" font-family="Times,serif" font-size="14.00">voltage_adjustable : bool</text>
-<polyline fill="none" stroke="black" points="2143.5,-143.5 2337.5,-143.5 "/>
-<text text-anchor="start" x="2151.5" y="-128.3" font-family="Times,serif" font-size="14.00">battery_voltage()</text>
-<text text-anchor="start" x="2151.5" y="-113.3" font-family="Times,serif" font-size="14.00">turn_off()</text>
-<text text-anchor="start" x="2151.5" y="-98.3" font-family="Times,serif" font-size="14.00">turn_on()</text>
+<polygon fill="none" stroke="black" points="2193.5,-90.5 2193.5,-249.5 2387.5,-249.5 2387.5,-90.5 2193.5,-90.5"/>
+<text text-anchor="middle" x="2290.5" y="-234.3" font-family="Times,serif" font-size="14.00">Pwr</text>
+<polyline fill="none" stroke="black" points="2193.5,-226.5 2387.5,-226.5 "/>
+<text text-anchor="start" x="2201.5" y="-211.3" font-family="Times,serif" font-size="14.00">current</text>
+<text text-anchor="start" x="2201.5" y="-196.3" font-family="Times,serif" font-size="14.00">current_adjustable : bool</text>
+<text text-anchor="start" x="2201.5" y="-181.3" font-family="Times,serif" font-size="14.00">voltage</text>
+<text text-anchor="start" x="2201.5" y="-166.3" font-family="Times,serif" font-size="14.00">voltage_adjustable : bool</text>
+<polyline fill="none" stroke="black" points="2193.5,-158.5 2387.5,-158.5 "/>
+<text text-anchor="start" x="2201.5" y="-143.3" font-family="Times,serif" font-size="14.00">battery_voltage()</text>
+<text text-anchor="start" x="2201.5" y="-128.3" font-family="Times,serif" font-size="14.00">current_max(value)</text>
+<text text-anchor="start" x="2201.5" y="-113.3" font-family="Times,serif" font-size="14.00">turn_off()</text>
+<text text-anchor="start" x="2201.5" y="-98.3" font-family="Times,serif" font-size="14.00">turn_on()</text>
 </g>
 <!-- 14 -->
 <g id="node15" class="node">
 <title>14</title>
-<polygon fill="none" stroke="black" points="2257,-579 2257,-768 2536,-768 2536,-579 2257,-579"/>
-<text text-anchor="middle" x="2396.5" y="-752.8" font-family="Times,serif" font-size="14.00">PwrAbstract</text>
-<polyline fill="none" stroke="black" points="2257,-745 2536,-745 "/>
-<text text-anchor="start" x="2265" y="-729.8" font-family="Times,serif" font-size="14.00">board_name</text>
-<text text-anchor="start" x="2265" y="-714.8" font-family="Times,serif" font-size="14.00">connection</text>
-<text text-anchor="start" x="2265" y="-699.8" font-family="Times,serif" font-size="14.00">ctl</text>
-<text text-anchor="start" x="2265" y="-684.8" font-family="Times,serif" font-size="14.00">current</text>
-<text text-anchor="start" x="2265" y="-669.8" font-family="Times,serif" font-size="14.00">exec_logger : NoneType, RootLogger</text>
-<text text-anchor="start" x="2265" y="-654.8" font-family="Times,serif" font-size="14.00">soh_logger : NoneType, RootLogger</text>
-<text text-anchor="start" x="2265" y="-639.8" font-family="Times,serif" font-size="14.00">voltage</text>
-<text text-anchor="start" x="2265" y="-624.8" font-family="Times,serif" font-size="14.00">voltage_adjustable</text>
-<polyline fill="none" stroke="black" points="2257,-617 2536,-617 "/>
-<text text-anchor="start" x="2265" y="-601.8" font-family="Times,serif" font-size="14.00">turn_off()</text>
-<text text-anchor="start" x="2265" y="-586.8" font-family="Times,serif" font-size="14.00">turn_on()</text>
+<polygon fill="none" stroke="black" points="2200.5,-564 2200.5,-753 2478.5,-753 2478.5,-564 2200.5,-564"/>
+<text text-anchor="middle" x="2339.5" y="-737.8" font-family="Times,serif" font-size="14.00">PwrAbstract</text>
+<polyline fill="none" stroke="black" points="2200.5,-730 2478.5,-730 "/>
+<text text-anchor="start" x="2208.5" y="-714.8" font-family="Times,serif" font-size="14.00">connection</text>
+<text text-anchor="start" x="2208.5" y="-699.8" font-family="Times,serif" font-size="14.00">current</text>
+<text text-anchor="start" x="2208.5" y="-684.8" font-family="Times,serif" font-size="14.00">current_adjustable</text>
+<text text-anchor="start" x="2208.5" y="-669.8" font-family="Times,serif" font-size="14.00">exec_logger : RootLogger, NoneType</text>
+<text text-anchor="start" x="2208.5" y="-654.8" font-family="Times,serif" font-size="14.00">model</text>
+<text text-anchor="start" x="2208.5" y="-639.8" font-family="Times,serif" font-size="14.00">soh_logger : RootLogger, NoneType</text>
+<text text-anchor="start" x="2208.5" y="-624.8" font-family="Times,serif" font-size="14.00">voltage</text>
+<text text-anchor="start" x="2208.5" y="-609.8" font-family="Times,serif" font-size="14.00">voltage_adjustable</text>
+<polyline fill="none" stroke="black" points="2200.5,-602 2478.5,-602 "/>
+<text text-anchor="start" x="2208.5" y="-586.8" font-family="Times,serif" font-size="14.00">turn_off()</text>
+<text text-anchor="start" x="2208.5" y="-571.8" font-family="Times,serif" font-size="14.00">turn_on()</text>
 </g>
 <!-- 12&#45;&gt;14 -->
 <g id="edge6" class="edge">
 <title>12&#45;&gt;14</title>
-<path fill="none" stroke="black" d="M2267.67,-264.55C2294.41,-349.23 2335.27,-478.62 2363.78,-568.9"/>
-<polygon fill="none" stroke="black" points="2360.5,-570.13 2366.85,-578.61 2367.18,-568.02 2360.5,-570.13"/>
+<path fill="none" stroke="black" d="M2298.43,-249.71C2306.76,-332.39 2319.89,-462.83 2329.06,-553.85"/>
+<polygon fill="none" stroke="black" points="2325.59,-554.32 2330.07,-563.92 2332.55,-553.62 2325.59,-554.32"/>
 </g>
 <!-- 13 -->
 <g id="node14" class="node">
 <title>13</title>
-<polygon fill="none" stroke="black" points="2355.5,-120.5 2355.5,-234.5 2549.5,-234.5 2549.5,-120.5 2355.5,-120.5"/>
-<text text-anchor="middle" x="2452.5" y="-219.3" font-family="Times,serif" font-size="14.00">Pwr</text>
-<polyline fill="none" stroke="black" points="2355.5,-211.5 2549.5,-211.5 "/>
-<text text-anchor="start" x="2363.5" y="-196.3" font-family="Times,serif" font-size="14.00">current</text>
-<text text-anchor="start" x="2363.5" y="-181.3" font-family="Times,serif" font-size="14.00">voltage</text>
-<text text-anchor="start" x="2363.5" y="-166.3" font-family="Times,serif" font-size="14.00">voltage_adjustable : bool</text>
-<polyline fill="none" stroke="black" points="2355.5,-158.5 2549.5,-158.5 "/>
-<text text-anchor="start" x="2363.5" y="-143.3" font-family="Times,serif" font-size="14.00">turn_off()</text>
-<text text-anchor="start" x="2363.5" y="-128.3" font-family="Times,serif" font-size="14.00">turn_on()</text>
+<polygon fill="none" stroke="black" points="2406,-120.5 2406,-219.5 2489,-219.5 2489,-120.5 2406,-120.5"/>
+<text text-anchor="middle" x="2447.5" y="-204.3" font-family="Times,serif" font-size="14.00">Pwr</text>
+<polyline fill="none" stroke="black" points="2406,-196.5 2489,-196.5 "/>
+<text text-anchor="start" x="2414" y="-181.3" font-family="Times,serif" font-size="14.00">current</text>
+<text text-anchor="start" x="2414" y="-166.3" font-family="Times,serif" font-size="14.00">voltage</text>
+<polyline fill="none" stroke="black" points="2406,-158.5 2489,-158.5 "/>
+<text text-anchor="start" x="2414" y="-143.3" font-family="Times,serif" font-size="14.00">turn_off()</text>
+<text text-anchor="start" x="2414" y="-128.3" font-family="Times,serif" font-size="14.00">turn_on()</text>
 </g>
 <!-- 13&#45;&gt;14 -->
 <g id="edge7" class="edge">
 <title>13&#45;&gt;14</title>
-<path fill="none" stroke="black" d="M2446.13,-234.69C2436.93,-315.82 2419.78,-467.17 2408.25,-568.89"/>
-<polygon fill="none" stroke="black" points="2404.76,-568.55 2407.11,-578.88 2411.72,-569.34 2404.76,-568.55"/>
+<path fill="none" stroke="black" d="M2436.65,-219.87C2419.39,-297.61 2385.32,-451.12 2362.46,-554.05"/>
+<polygon fill="none" stroke="black" points="2359.04,-553.33 2360.29,-563.85 2365.87,-554.85 2359.04,-553.33"/>
 </g>
 <!-- 15 -->
 <g id="node16" class="node">
 <title>15</title>
-<polygon fill="none" stroke="black" points="2567.5,-113 2567.5,-242 2695.5,-242 2695.5,-113 2567.5,-113"/>
-<text text-anchor="middle" x="2631.5" y="-226.8" font-family="Times,serif" font-size="14.00">Rx</text>
-<polyline fill="none" stroke="black" points="2567.5,-219 2695.5,-219 "/>
-<text text-anchor="start" x="2575.5" y="-203.8" font-family="Times,serif" font-size="14.00">adc_gain</text>
-<text text-anchor="start" x="2575.5" y="-188.8" font-family="Times,serif" font-size="14.00">adc_gain</text>
-<text text-anchor="start" x="2575.5" y="-173.8" font-family="Times,serif" font-size="14.00">connection</text>
-<text text-anchor="start" x="2575.5" y="-158.8" font-family="Times,serif" font-size="14.00">ctl</text>
-<text text-anchor="start" x="2575.5" y="-143.8" font-family="Times,serif" font-size="14.00">voltage</text>
-<polyline fill="none" stroke="black" points="2567.5,-136 2695.5,-136 "/>
-<text text-anchor="start" x="2575.5" y="-120.8" font-family="Times,serif" font-size="14.00">adc_gain_auto()</text>
+<polygon fill="none" stroke="black" points="2642.5,-609 2642.5,-708 2764.5,-708 2764.5,-609 2642.5,-609"/>
+<text text-anchor="middle" x="2703.5" y="-692.8" font-family="Times,serif" font-size="14.00">Rx</text>
+<polyline fill="none" stroke="black" points="2642.5,-685 2764.5,-685 "/>
+<text text-anchor="start" x="2650.5" y="-669.8" font-family="Times,serif" font-size="14.00">gain</text>
+<text text-anchor="start" x="2650.5" y="-654.8" font-family="Times,serif" font-size="14.00">gain : int, float</text>
+<text text-anchor="start" x="2650.5" y="-639.8" font-family="Times,serif" font-size="14.00">voltage</text>
+<polyline fill="none" stroke="black" points="2642.5,-632 2764.5,-632 "/>
+<text text-anchor="start" x="2650.5" y="-616.8" font-family="Times,serif" font-size="14.00">gain_auto()</text>
 </g>
 <!-- 18 -->
 <g id="node19" class="node">
 <title>18</title>
-<polygon fill="none" stroke="black" points="2638,-586.5 2638,-760.5 2917,-760.5 2917,-586.5 2638,-586.5"/>
-<text text-anchor="middle" x="2777.5" y="-745.3" font-family="Times,serif" font-size="14.00">RxAbstract</text>
-<polyline fill="none" stroke="black" points="2638,-737.5 2917,-737.5 "/>
-<text text-anchor="start" x="2646" y="-722.3" font-family="Times,serif" font-size="14.00">adc_gain</text>
-<text text-anchor="start" x="2646" y="-707.3" font-family="Times,serif" font-size="14.00">board_name</text>
-<text text-anchor="start" x="2646" y="-692.3" font-family="Times,serif" font-size="14.00">connection</text>
-<text text-anchor="start" x="2646" y="-677.3" font-family="Times,serif" font-size="14.00">ctl</text>
-<text text-anchor="start" x="2646" y="-662.3" font-family="Times,serif" font-size="14.00">exec_logger : NoneType, RootLogger</text>
-<text text-anchor="start" x="2646" y="-647.3" font-family="Times,serif" font-size="14.00">sampling_rate</text>
-<text text-anchor="start" x="2646" y="-632.3" font-family="Times,serif" font-size="14.00">soh_logger : NoneType, RootLogger</text>
-<text text-anchor="start" x="2646" y="-617.3" font-family="Times,serif" font-size="14.00">voltage</text>
-<polyline fill="none" stroke="black" points="2638,-609.5 2917,-609.5 "/>
-<text text-anchor="start" x="2646" y="-594.3" font-family="Times,serif" font-size="14.00">adc_gain_auto()</text>
+<polygon fill="none" stroke="black" points="2469.5,-1008.5 2469.5,-1167.5 2747.5,-1167.5 2747.5,-1008.5 2469.5,-1008.5"/>
+<text text-anchor="middle" x="2608.5" y="-1152.3" font-family="Times,serif" font-size="14.00">RxAbstract</text>
+<polyline fill="none" stroke="black" points="2469.5,-1144.5 2747.5,-1144.5 "/>
+<text text-anchor="start" x="2477.5" y="-1129.3" font-family="Times,serif" font-size="14.00">adc_gain</text>
+<text text-anchor="start" x="2477.5" y="-1114.3" font-family="Times,serif" font-size="14.00">connection</text>
+<text text-anchor="start" x="2477.5" y="-1099.3" font-family="Times,serif" font-size="14.00">exec_logger : RootLogger, NoneType</text>
+<text text-anchor="start" x="2477.5" y="-1084.3" font-family="Times,serif" font-size="14.00">model</text>
+<text text-anchor="start" x="2477.5" y="-1069.3" font-family="Times,serif" font-size="14.00">sampling_rate</text>
+<text text-anchor="start" x="2477.5" y="-1054.3" font-family="Times,serif" font-size="14.00">soh_logger : RootLogger, NoneType</text>
+<text text-anchor="start" x="2477.5" y="-1039.3" font-family="Times,serif" font-size="14.00">voltage</text>
+<polyline fill="none" stroke="black" points="2469.5,-1031.5 2747.5,-1031.5 "/>
+<text text-anchor="middle" x="2608.5" y="-1016.3" font-family="Times,serif" font-size="14.00"> </text>
 </g>
 <!-- 15&#45;&gt;18 -->
 <g id="edge8" class="edge">
 <title>15&#45;&gt;18</title>
-<path fill="none" stroke="black" d="M2650.29,-242.07C2675.37,-326.93 2720.01,-477.97 2749.18,-576.68"/>
-<polygon fill="none" stroke="black" points="2745.85,-577.76 2752.04,-586.36 2752.56,-575.78 2745.85,-577.76"/>
+<path fill="none" stroke="black" d="M2692.69,-708.12C2677.03,-778.61 2647.76,-910.35 2628.19,-998.38"/>
+<polygon fill="none" stroke="black" points="2624.73,-997.84 2625.98,-1008.36 2631.56,-999.36 2624.73,-997.84"/>
 </g>
 <!-- 16 -->
 <g id="node17" class="node">
 <title>16</title>
-<polygon fill="none" stroke="black" points="2713.5,-128 2713.5,-227 2841.5,-227 2841.5,-128 2713.5,-128"/>
-<text text-anchor="middle" x="2777.5" y="-211.8" font-family="Times,serif" font-size="14.00">Rx</text>
-<polyline fill="none" stroke="black" points="2713.5,-204 2841.5,-204 "/>
-<text text-anchor="start" x="2721.5" y="-188.8" font-family="Times,serif" font-size="14.00">adc_gain</text>
-<text text-anchor="start" x="2721.5" y="-173.8" font-family="Times,serif" font-size="14.00">adc_gain : float</text>
-<text text-anchor="start" x="2721.5" y="-158.8" font-family="Times,serif" font-size="14.00">voltage</text>
-<polyline fill="none" stroke="black" points="2713.5,-151 2841.5,-151 "/>
-<text text-anchor="start" x="2721.5" y="-135.8" font-family="Times,serif" font-size="14.00">adc_gain_auto()</text>
+<polygon fill="none" stroke="black" points="2496.5,-609 2496.5,-708 2624.5,-708 2624.5,-609 2496.5,-609"/>
+<text text-anchor="middle" x="2560.5" y="-692.8" font-family="Times,serif" font-size="14.00">Rx</text>
+<polyline fill="none" stroke="black" points="2496.5,-685 2624.5,-685 "/>
+<text text-anchor="start" x="2504.5" y="-669.8" font-family="Times,serif" font-size="14.00">adc_gain</text>
+<text text-anchor="start" x="2504.5" y="-654.8" font-family="Times,serif" font-size="14.00">adc_gain : float</text>
+<text text-anchor="start" x="2504.5" y="-639.8" font-family="Times,serif" font-size="14.00">voltage</text>
+<polyline fill="none" stroke="black" points="2496.5,-632 2624.5,-632 "/>
+<text text-anchor="start" x="2504.5" y="-616.8" font-family="Times,serif" font-size="14.00">adc_gain_auto()</text>
 </g>
 <!-- 16&#45;&gt;18 -->
 <g id="edge9" class="edge">
 <title>16&#45;&gt;18</title>
-<path fill="none" stroke="black" d="M2777.5,-227.22C2777.5,-308.02 2777.5,-471.22 2777.5,-576.01"/>
-<polygon fill="none" stroke="black" points="2774,-576.27 2777.5,-586.27 2781,-576.27 2774,-576.27"/>
+<path fill="none" stroke="black" d="M2565.96,-708.12C2573.87,-778.61 2588.67,-910.35 2598.55,-998.38"/>
+<polygon fill="none" stroke="black" points="2595.08,-998.81 2599.67,-1008.36 2602.03,-998.03 2595.08,-998.81"/>
 </g>
 <!-- 17 -->
 <g id="node18" class="node">
 <title>17</title>
-<polygon fill="none" stroke="black" points="2859.5,-60.5 2859.5,-294.5 3047.5,-294.5 3047.5,-60.5 2859.5,-60.5"/>
-<text text-anchor="middle" x="2953.5" y="-279.3" font-family="Times,serif" font-size="14.00">Rx</text>
-<polyline fill="none" stroke="black" points="2859.5,-271.5 3047.5,-271.5 "/>
-<text text-anchor="start" x="2867.5" y="-256.3" font-family="Times,serif" font-size="14.00">adc_gain</text>
-<text text-anchor="start" x="2867.5" y="-241.3" font-family="Times,serif" font-size="14.00">adc_gain : float</text>
-<text text-anchor="start" x="2867.5" y="-226.3" font-family="Times,serif" font-size="14.00">ctl</text>
-<text text-anchor="start" x="2867.5" y="-211.3" font-family="Times,serif" font-size="14.00">io</text>
-<text text-anchor="start" x="2867.5" y="-196.3" font-family="Times,serif" font-size="14.00">mcp_board : MCP23008</text>
-<text text-anchor="start" x="2867.5" y="-181.3" font-family="Times,serif" font-size="14.00">pin_DG0 : DigitalInOut</text>
-<text text-anchor="start" x="2867.5" y="-166.3" font-family="Times,serif" font-size="14.00">pin_DG1 : DigitalInOut</text>
-<text text-anchor="start" x="2867.5" y="-151.3" font-family="Times,serif" font-size="14.00">pin_DG2 : DigitalInOut</text>
-<text text-anchor="start" x="2867.5" y="-136.3" font-family="Times,serif" font-size="14.00">voltage</text>
-<text text-anchor="start" x="2867.5" y="-121.3" font-family="Times,serif" font-size="14.00">voltage_gain</text>
-<text text-anchor="start" x="2867.5" y="-106.3" font-family="Times,serif" font-size="14.00">voltage_gain : int, float</text>
-<polyline fill="none" stroke="black" points="2859.5,-98.5 3047.5,-98.5 "/>
-<text text-anchor="start" x="2867.5" y="-83.3" font-family="Times,serif" font-size="14.00">adc_gain_auto()</text>
-<text text-anchor="start" x="2867.5" y="-68.3" font-family="Times,serif" font-size="14.00">voltage_gain_auto()</text>
-</g>
-<!-- 17&#45;&gt;18 -->
+<polygon fill="none" stroke="black" points="2609.5,-90.5 2609.5,-249.5 2797.5,-249.5 2797.5,-90.5 2609.5,-90.5"/>
+<text text-anchor="middle" x="2703.5" y="-234.3" font-family="Times,serif" font-size="14.00">Rx</text>
+<polyline fill="none" stroke="black" points="2609.5,-226.5 2797.5,-226.5 "/>
+<text text-anchor="start" x="2617.5" y="-211.3" font-family="Times,serif" font-size="14.00">gain</text>
+<text text-anchor="start" x="2617.5" y="-196.3" font-family="Times,serif" font-size="14.00">gain : int, float</text>
+<text text-anchor="start" x="2617.5" y="-181.3" font-family="Times,serif" font-size="14.00">mcp_board : MCP23008</text>
+<text text-anchor="start" x="2617.5" y="-166.3" font-family="Times,serif" font-size="14.00">pin_DG0 : DigitalInOut</text>
+<text text-anchor="start" x="2617.5" y="-151.3" font-family="Times,serif" font-size="14.00">pin_DG1 : DigitalInOut</text>
+<text text-anchor="start" x="2617.5" y="-136.3" font-family="Times,serif" font-size="14.00">pin_DG2 : DigitalInOut</text>
+<text text-anchor="start" x="2617.5" y="-121.3" font-family="Times,serif" font-size="14.00">voltage</text>
+<polyline fill="none" stroke="black" points="2609.5,-113.5 2797.5,-113.5 "/>
+<text text-anchor="start" x="2617.5" y="-98.3" font-family="Times,serif" font-size="14.00">gain_auto()</text>
+</g>
+<!-- 17&#45;&gt;15 -->
 <g id="edge10" class="edge">
-<title>17&#45;&gt;18</title>
-<path fill="none" stroke="black" d="M2912.12,-294.63C2881.51,-380.56 2840.32,-496.18 2811.59,-576.81"/>
-<polygon fill="none" stroke="black" points="2808.21,-575.88 2808.15,-586.47 2814.8,-578.22 2808.21,-575.88"/>
+<title>17&#45;&gt;15</title>
+<path fill="none" stroke="black" d="M2703.5,-249.71C2703.5,-347.26 2703.5,-511.28 2703.5,-598.62"/>
+<polygon fill="none" stroke="black" points="2700,-598.88 2703.5,-608.88 2707,-598.88 2700,-598.88"/>
 </g>
 <!-- 19 -->
 <g id="node20" class="node">
 <title>19</title>
-<polygon fill="none" stroke="black" points="3065.5,-0.5 3065.5,-354.5 3361.5,-354.5 3361.5,-0.5 3065.5,-0.5"/>
-<text text-anchor="middle" x="3213.5" y="-339.3" font-family="Times,serif" font-size="14.00">Tx</text>
-<polyline fill="none" stroke="black" points="3065.5,-331.5 3361.5,-331.5 "/>
-<text text-anchor="start" x="3073.5" y="-316.3" font-family="Times,serif" font-size="14.00">adc_gain</text>
-<text text-anchor="start" x="3073.5" y="-301.3" font-family="Times,serif" font-size="14.00">adc_gain : int, float</text>
-<text text-anchor="start" x="3073.5" y="-286.3" font-family="Times,serif" font-size="14.00">connection</text>
-<text text-anchor="start" x="3073.5" y="-271.3" font-family="Times,serif" font-size="14.00">ctl</text>
-<text text-anchor="start" x="3073.5" y="-256.3" font-family="Times,serif" font-size="14.00">current</text>
-<text text-anchor="start" x="3073.5" y="-241.3" font-family="Times,serif" font-size="14.00">current_adjustable : bool</text>
-<text text-anchor="start" x="3073.5" y="-226.3" font-family="Times,serif" font-size="14.00">mcp_board : MCP23008</text>
-<text text-anchor="start" x="3073.5" y="-211.3" font-family="Times,serif" font-size="14.00">pin0 : DigitalInOut</text>
-<text text-anchor="start" x="3073.5" y="-196.3" font-family="Times,serif" font-size="14.00">pin1 : DigitalInOut</text>
-<text text-anchor="start" x="3073.5" y="-181.3" font-family="Times,serif" font-size="14.00">pin4 : DigitalInOut</text>
-<text text-anchor="start" x="3073.5" y="-166.3" font-family="Times,serif" font-size="14.00">polarity</text>
-<text text-anchor="start" x="3073.5" y="-151.3" font-family="Times,serif" font-size="14.00">polarity : int</text>
-<text text-anchor="start" x="3073.5" y="-136.3" font-family="Times,serif" font-size="14.00">pwr : NoneType</text>
-<text text-anchor="start" x="3073.5" y="-121.3" font-family="Times,serif" font-size="14.00">tx_bat</text>
-<text text-anchor="start" x="3073.5" y="-106.3" font-family="Times,serif" font-size="14.00">voltage_adjustable : bool</text>
-<polyline fill="none" stroke="black" points="3065.5,-98.5 3361.5,-98.5 "/>
-<text text-anchor="start" x="3073.5" y="-83.3" font-family="Times,serif" font-size="14.00">adc_gain_auto()</text>
-<text text-anchor="start" x="3073.5" y="-68.3" font-family="Times,serif" font-size="14.00">current_pulse()</text>
-<text text-anchor="start" x="3073.5" y="-53.3" font-family="Times,serif" font-size="14.00">inject(polarity, injection_duration)</text>
-<text text-anchor="start" x="3073.5" y="-38.3" font-family="Times,serif" font-size="14.00">turn_off()</text>
-<text text-anchor="start" x="3073.5" y="-23.3" font-family="Times,serif" font-size="14.00">turn_on()</text>
-<text text-anchor="start" x="3073.5" y="-8.3" font-family="Times,serif" font-size="14.00">voltage_pulse(voltage, length, polarity)</text>
+<polygon fill="none" stroke="black" points="3110.5,-489 3110.5,-828 3406.5,-828 3406.5,-489 3110.5,-489"/>
+<text text-anchor="middle" x="3258.5" y="-812.8" font-family="Times,serif" font-size="14.00">Tx</text>
+<polyline fill="none" stroke="black" points="3110.5,-805 3406.5,-805 "/>
+<text text-anchor="start" x="3118.5" y="-789.8" font-family="Times,serif" font-size="14.00">adc_voltage_max</text>
+<text text-anchor="start" x="3118.5" y="-774.8" font-family="Times,serif" font-size="14.00">adc_voltage_min</text>
+<text text-anchor="start" x="3118.5" y="-759.8" font-family="Times,serif" font-size="14.00">current</text>
+<text text-anchor="start" x="3118.5" y="-744.8" font-family="Times,serif" font-size="14.00">current_adjustable : bool</text>
+<text text-anchor="start" x="3118.5" y="-729.8" font-family="Times,serif" font-size="14.00">gain</text>
+<text text-anchor="start" x="3118.5" y="-714.8" font-family="Times,serif" font-size="14.00">gain : int, float</text>
+<text text-anchor="start" x="3118.5" y="-699.8" font-family="Times,serif" font-size="14.00">mcp_board : MCP23008</text>
+<text text-anchor="start" x="3118.5" y="-684.8" font-family="Times,serif" font-size="14.00">pin0 : DigitalInOut</text>
+<text text-anchor="start" x="3118.5" y="-669.8" font-family="Times,serif" font-size="14.00">pin1 : DigitalInOut</text>
+<text text-anchor="start" x="3118.5" y="-654.8" font-family="Times,serif" font-size="14.00">polarity</text>
+<text text-anchor="start" x="3118.5" y="-639.8" font-family="Times,serif" font-size="14.00">polarity : int</text>
+<text text-anchor="start" x="3118.5" y="-624.8" font-family="Times,serif" font-size="14.00">r_shunt</text>
+<text text-anchor="start" x="3118.5" y="-609.8" font-family="Times,serif" font-size="14.00">tx_bat</text>
+<text text-anchor="start" x="3118.5" y="-594.8" font-family="Times,serif" font-size="14.00">voltage_adjustable : bool</text>
+<polyline fill="none" stroke="black" points="3110.5,-587 3406.5,-587 "/>
+<text text-anchor="start" x="3118.5" y="-571.8" font-family="Times,serif" font-size="14.00">current_pulse()</text>
+<text text-anchor="start" x="3118.5" y="-556.8" font-family="Times,serif" font-size="14.00">gain_auto()</text>
+<text text-anchor="start" x="3118.5" y="-541.8" font-family="Times,serif" font-size="14.00">inject(polarity, injection_duration)</text>
+<text text-anchor="start" x="3118.5" y="-526.8" font-family="Times,serif" font-size="14.00">turn_off()</text>
+<text text-anchor="start" x="3118.5" y="-511.8" font-family="Times,serif" font-size="14.00">turn_on()</text>
+<text text-anchor="start" x="3118.5" y="-496.8" font-family="Times,serif" font-size="14.00">voltage_pulse(voltage, length, polarity)</text>
 </g>
 <!-- 22 -->
 <g id="node23" class="node">
 <title>22</title>
-<polygon fill="none" stroke="black" points="3379.5,-534 3379.5,-813 3675.5,-813 3675.5,-534 3379.5,-534"/>
-<text text-anchor="middle" x="3527.5" y="-797.8" font-family="Times,serif" font-size="14.00">TxAbstract</text>
-<polyline fill="none" stroke="black" points="3379.5,-790 3675.5,-790 "/>
-<text text-anchor="start" x="3387.5" y="-774.8" font-family="Times,serif" font-size="14.00">adc_gain</text>
-<text text-anchor="start" x="3387.5" y="-759.8" font-family="Times,serif" font-size="14.00">board_name</text>
-<text text-anchor="start" x="3387.5" y="-744.8" font-family="Times,serif" font-size="14.00">connection</text>
-<text text-anchor="start" x="3387.5" y="-729.8" font-family="Times,serif" font-size="14.00">ctl</text>
-<text text-anchor="start" x="3387.5" y="-714.8" font-family="Times,serif" font-size="14.00">exec_logger : NoneType, RootLogger</text>
-<text text-anchor="start" x="3387.5" y="-699.8" font-family="Times,serif" font-size="14.00">injection_duration</text>
-<text text-anchor="start" x="3387.5" y="-684.8" font-family="Times,serif" font-size="14.00">injection_duration</text>
-<text text-anchor="start" x="3387.5" y="-669.8" font-family="Times,serif" font-size="14.00">polarity</text>
-<text text-anchor="start" x="3387.5" y="-654.8" font-family="Times,serif" font-size="14.00">pwr</text>
-<text text-anchor="start" x="3387.5" y="-639.8" font-family="Times,serif" font-size="14.00">soh_logger : NoneType, RootLogger</text>
-<text text-anchor="start" x="3387.5" y="-624.8" font-family="Times,serif" font-size="14.00">tx_bat</text>
-<text text-anchor="start" x="3387.5" y="-609.8" font-family="Times,serif" font-size="14.00">tx_sync</text>
-<polyline fill="none" stroke="black" points="3379.5,-602 3675.5,-602 "/>
-<text text-anchor="start" x="3387.5" y="-586.8" font-family="Times,serif" font-size="14.00">adc_gain_auto()</text>
-<text text-anchor="start" x="3387.5" y="-571.8" font-family="Times,serif" font-size="14.00">current_pulse()</text>
-<text text-anchor="start" x="3387.5" y="-556.8" font-family="Times,serif" font-size="14.00">inject(polarity, injection_duration)</text>
-<text text-anchor="start" x="3387.5" y="-541.8" font-family="Times,serif" font-size="14.00">voltage_pulse(voltage, length, polarity)</text>
+<polygon fill="none" stroke="black" points="2797.5,-963.5 2797.5,-1212.5 3145.5,-1212.5 3145.5,-963.5 2797.5,-963.5"/>
+<text text-anchor="middle" x="2971.5" y="-1197.3" font-family="Times,serif" font-size="14.00">TxAbstract</text>
+<polyline fill="none" stroke="black" points="2797.5,-1189.5 3145.5,-1189.5 "/>
+<text text-anchor="start" x="2805.5" y="-1174.3" font-family="Times,serif" font-size="14.00">adc_gain</text>
+<text text-anchor="start" x="2805.5" y="-1159.3" font-family="Times,serif" font-size="14.00">connection</text>
+<text text-anchor="start" x="2805.5" y="-1144.3" font-family="Times,serif" font-size="14.00">exec_logger : RootLogger, NoneType</text>
+<text text-anchor="start" x="2805.5" y="-1129.3" font-family="Times,serif" font-size="14.00">injection_duration</text>
+<text text-anchor="start" x="2805.5" y="-1114.3" font-family="Times,serif" font-size="14.00">injection_duration</text>
+<text text-anchor="start" x="2805.5" y="-1099.3" font-family="Times,serif" font-size="14.00">model</text>
+<text text-anchor="start" x="2805.5" y="-1084.3" font-family="Times,serif" font-size="14.00">polarity</text>
+<text text-anchor="start" x="2805.5" y="-1069.3" font-family="Times,serif" font-size="14.00">pwr</text>
+<text text-anchor="start" x="2805.5" y="-1054.3" font-family="Times,serif" font-size="14.00">soh_logger : RootLogger, NoneType</text>
+<text text-anchor="start" x="2805.5" y="-1039.3" font-family="Times,serif" font-size="14.00">tx_bat</text>
+<text text-anchor="start" x="2805.5" y="-1024.3" font-family="Times,serif" font-size="14.00">tx_sync</text>
+<polyline fill="none" stroke="black" points="2797.5,-1016.5 3145.5,-1016.5 "/>
+<text text-anchor="start" x="2805.5" y="-1001.3" font-family="Times,serif" font-size="14.00">current_pulse()</text>
+<text text-anchor="start" x="2805.5" y="-986.3" font-family="Times,serif" font-size="14.00">inject(polarity, injection_duration, switch_pwr)</text>
+<text text-anchor="start" x="2805.5" y="-971.3" font-family="Times,serif" font-size="14.00">voltage_pulse(voltage, length, polarity)</text>
 </g>
 <!-- 19&#45;&gt;22 -->
 <g id="edge11" class="edge">
 <title>19&#45;&gt;22</title>
-<path fill="none" stroke="black" d="M3325.53,-354.75C3361.06,-410.64 3399.94,-471.81 3434.02,-525.43"/>
-<polygon fill="none" stroke="black" points="3431.13,-527.4 3439.44,-533.97 3437.03,-523.65 3431.13,-527.4"/>
+<path fill="none" stroke="black" d="M3164.19,-828.18C3144.34,-861.17 3122.9,-895.1 3101.5,-926 3094.87,-935.57 3087.83,-945.31 3080.6,-955"/>
+<polygon fill="none" stroke="black" points="3077.69,-953.04 3074.48,-963.13 3083.29,-957.25 3077.69,-953.04"/>
 </g>
 <!-- 20 -->
 <g id="node21" class="node">
 <title>20</title>
-<polygon fill="none" stroke="black" points="3379.5,-83 3379.5,-272 3675.5,-272 3675.5,-83 3379.5,-83"/>
-<text text-anchor="middle" x="3527.5" y="-256.8" font-family="Times,serif" font-size="14.00">Tx</text>
-<polyline fill="none" stroke="black" points="3379.5,-249 3675.5,-249 "/>
-<text text-anchor="start" x="3387.5" y="-233.8" font-family="Times,serif" font-size="14.00">adc_gain</text>
-<text text-anchor="start" x="3387.5" y="-218.8" font-family="Times,serif" font-size="14.00">adc_gain : float</text>
-<text text-anchor="start" x="3387.5" y="-203.8" font-family="Times,serif" font-size="14.00">current</text>
-<text text-anchor="start" x="3387.5" y="-188.8" font-family="Times,serif" font-size="14.00">polarity : int</text>
-<text text-anchor="start" x="3387.5" y="-173.8" font-family="Times,serif" font-size="14.00">tx_bat</text>
-<text text-anchor="start" x="3387.5" y="-158.8" font-family="Times,serif" font-size="14.00">voltage</text>
-<polyline fill="none" stroke="black" points="3379.5,-151 3675.5,-151 "/>
-<text text-anchor="start" x="3387.5" y="-135.8" font-family="Times,serif" font-size="14.00">adc_gain_auto()</text>
-<text text-anchor="start" x="3387.5" y="-120.8" font-family="Times,serif" font-size="14.00">current_pulse()</text>
-<text text-anchor="start" x="3387.5" y="-105.8" font-family="Times,serif" font-size="14.00">inject(state)</text>
-<text text-anchor="start" x="3387.5" y="-90.8" font-family="Times,serif" font-size="14.00">voltage_pulse(voltage, length, polarity)</text>
+<polygon fill="none" stroke="black" points="2796.5,-564 2796.5,-753 3092.5,-753 3092.5,-564 2796.5,-564"/>
+<text text-anchor="middle" x="2944.5" y="-737.8" font-family="Times,serif" font-size="14.00">Tx</text>
+<polyline fill="none" stroke="black" points="2796.5,-730 3092.5,-730 "/>
+<text text-anchor="start" x="2804.5" y="-714.8" font-family="Times,serif" font-size="14.00">adc_gain</text>
+<text text-anchor="start" x="2804.5" y="-699.8" font-family="Times,serif" font-size="14.00">adc_gain : float</text>
+<text text-anchor="start" x="2804.5" y="-684.8" font-family="Times,serif" font-size="14.00">current</text>
+<text text-anchor="start" x="2804.5" y="-669.8" font-family="Times,serif" font-size="14.00">polarity : int</text>
+<text text-anchor="start" x="2804.5" y="-654.8" font-family="Times,serif" font-size="14.00">tx_bat</text>
+<text text-anchor="start" x="2804.5" y="-639.8" font-family="Times,serif" font-size="14.00">voltage</text>
+<polyline fill="none" stroke="black" points="2796.5,-632 3092.5,-632 "/>
+<text text-anchor="start" x="2804.5" y="-616.8" font-family="Times,serif" font-size="14.00">adc_gain_auto()</text>
+<text text-anchor="start" x="2804.5" y="-601.8" font-family="Times,serif" font-size="14.00">current_pulse()</text>
+<text text-anchor="start" x="2804.5" y="-586.8" font-family="Times,serif" font-size="14.00">inject(state)</text>
+<text text-anchor="start" x="2804.5" y="-571.8" font-family="Times,serif" font-size="14.00">voltage_pulse(voltage, length, polarity)</text>
 </g>
 <!-- 20&#45;&gt;22 -->
 <g id="edge12" class="edge">
 <title>20&#45;&gt;22</title>
-<path fill="none" stroke="black" d="M3527.5,-272.29C3527.5,-343.13 3527.5,-441.77 3527.5,-523.39"/>
-<polygon fill="none" stroke="black" points="3524,-523.77 3527.5,-533.77 3531,-523.77 3524,-523.77"/>
+<path fill="none" stroke="black" d="M2950.41,-753.05C2954.11,-811.6 2958.93,-887.97 2963.03,-952.9"/>
+<polygon fill="none" stroke="black" points="2959.56,-953.46 2963.68,-963.22 2966.54,-953.02 2959.56,-953.46"/>
 </g>
 <!-- 21 -->
 <g id="node22" class="node">
 <title>21</title>
-<polygon fill="none" stroke="black" points="3693.5,-0.5 3693.5,-354.5 3989.5,-354.5 3989.5,-0.5 3693.5,-0.5"/>
-<text text-anchor="middle" x="3841.5" y="-339.3" font-family="Times,serif" font-size="14.00">Tx</text>
-<polyline fill="none" stroke="black" points="3693.5,-331.5 3989.5,-331.5 "/>
-<text text-anchor="start" x="3701.5" y="-316.3" font-family="Times,serif" font-size="14.00">adc_gain</text>
-<text text-anchor="start" x="3701.5" y="-301.3" font-family="Times,serif" font-size="14.00">adc_gain : int, float</text>
-<text text-anchor="start" x="3701.5" y="-286.3" font-family="Times,serif" font-size="14.00">ctl</text>
-<text text-anchor="start" x="3701.5" y="-271.3" font-family="Times,serif" font-size="14.00">current</text>
-<text text-anchor="start" x="3701.5" y="-256.3" font-family="Times,serif" font-size="14.00">current_adjustable : bool</text>
-<text text-anchor="start" x="3701.5" y="-241.3" font-family="Times,serif" font-size="14.00">io</text>
-<text text-anchor="start" x="3701.5" y="-226.3" font-family="Times,serif" font-size="14.00">mcp_board : MCP23008</text>
-<text text-anchor="start" x="3701.5" y="-211.3" font-family="Times,serif" font-size="14.00">pin0 : DigitalInOut</text>
-<text text-anchor="start" x="3701.5" y="-196.3" font-family="Times,serif" font-size="14.00">pin1 : DigitalInOut</text>
-<text text-anchor="start" x="3701.5" y="-181.3" font-family="Times,serif" font-size="14.00">pin4 : DigitalInOut</text>
-<text text-anchor="start" x="3701.5" y="-166.3" font-family="Times,serif" font-size="14.00">polarity</text>
-<text text-anchor="start" x="3701.5" y="-151.3" font-family="Times,serif" font-size="14.00">polarity : int</text>
-<text text-anchor="start" x="3701.5" y="-136.3" font-family="Times,serif" font-size="14.00">pwr : NoneType</text>
-<text text-anchor="start" x="3701.5" y="-121.3" font-family="Times,serif" font-size="14.00">tx_bat</text>
-<text text-anchor="start" x="3701.5" y="-106.3" font-family="Times,serif" font-size="14.00">voltage_adjustable : bool</text>
-<polyline fill="none" stroke="black" points="3693.5,-98.5 3989.5,-98.5 "/>
-<text text-anchor="start" x="3701.5" y="-83.3" font-family="Times,serif" font-size="14.00">adc_gain_auto()</text>
-<text text-anchor="start" x="3701.5" y="-68.3" font-family="Times,serif" font-size="14.00">current_pulse()</text>
-<text text-anchor="start" x="3701.5" y="-53.3" font-family="Times,serif" font-size="14.00">inject(polarity, injection_duration)</text>
-<text text-anchor="start" x="3701.5" y="-38.3" font-family="Times,serif" font-size="14.00">turn_off()</text>
-<text text-anchor="start" x="3701.5" y="-23.3" font-family="Times,serif" font-size="14.00">turn_on()</text>
-<text text-anchor="start" x="3701.5" y="-8.3" font-family="Times,serif" font-size="14.00">voltage_pulse(voltage, length, polarity)</text>
-</g>
-<!-- 21&#45;&gt;22 -->
+<polygon fill="none" stroke="black" points="3129.5,-128 3129.5,-212 3387.5,-212 3387.5,-128 3129.5,-128"/>
+<text text-anchor="middle" x="3258.5" y="-196.8" font-family="Times,serif" font-size="14.00">Tx</text>
+<polyline fill="none" stroke="black" points="3129.5,-189 3387.5,-189 "/>
+<text text-anchor="start" x="3137.5" y="-173.8" font-family="Times,serif" font-size="14.00">pin4 : DigitalInOut</text>
+<text text-anchor="start" x="3137.5" y="-158.8" font-family="Times,serif" font-size="14.00">pin6 : DigitalInOut</text>
+<polyline fill="none" stroke="black" points="3129.5,-151 3387.5,-151 "/>
+<text text-anchor="start" x="3137.5" y="-135.8" font-family="Times,serif" font-size="14.00">inject(polarity, injection_duration)</text>
+</g>
+<!-- 21&#45;&gt;19 -->
 <g id="edge13" class="edge">
-<title>21&#45;&gt;22</title>
-<path fill="none" stroke="black" d="M3729.47,-354.75C3693.94,-410.64 3655.06,-471.81 3620.98,-525.43"/>
-<polygon fill="none" stroke="black" points="3617.97,-523.65 3615.56,-533.97 3623.87,-527.4 3617.97,-523.65"/>
+<title>21&#45;&gt;19</title>
+<path fill="none" stroke="black" d="M3258.5,-212.06C3258.5,-270.69 3258.5,-382.01 3258.5,-478.7"/>
+<polygon fill="none" stroke="black" points="3255,-478.9 3258.5,-488.9 3262,-478.9 3255,-478.9"/>
 </g>
 </g>
 </svg>
diff --git a/uml_diagrams/packages_uml_ohmpi.dot b/uml_diagrams/packages_uml_ohmpi.dot
index d57d3be5340fdd6260e8e1ad79514cddfe78b9ac..4787367abeedbbc969f197112ee8f264182f22ac 100644
--- a/uml_diagrams/packages_uml_ohmpi.dot
+++ b/uml_diagrams/packages_uml_ohmpi.dot
@@ -36,17 +36,18 @@ rankdir=BT
 "8" -> "21" [arrowhead="open", arrowtail="none"];
 "9" -> "2" [arrowhead="open", arrowtail="none"];
 "9" -> "4" [arrowhead="open", arrowtail="none"];
-"10" -> "2" [arrowhead="open", arrowtail="none"];
 "10" -> "4" [arrowhead="open", arrowtail="none"];
-"11" -> "2" [arrowhead="open", arrowtail="none"];
-"11" -> "4" [arrowhead="open", arrowtail="none"];
-"12" -> "2" [arrowhead="open", arrowtail="none"];
+"10" -> "23" [arrowhead="open", arrowtail="none"];
+"11" -> "10" [arrowhead="open", arrowtail="none"];
+"11" -> "23" [arrowhead="open", arrowtail="none"];
 "12" -> "4" [arrowhead="open", arrowtail="none"];
-"13" -> "2" [arrowhead="open", arrowtail="none"];
+"12" -> "23" [arrowhead="open", arrowtail="none"];
 "13" -> "4" [arrowhead="open", arrowtail="none"];
+"13" -> "23" [arrowhead="open", arrowtail="none"];
 "14" -> "5" [arrowhead="open", arrowtail="none"];
-"15" -> "2" [arrowhead="open", arrowtail="none"];
+"14" -> "23" [arrowhead="open", arrowtail="none"];
 "15" -> "5" [arrowhead="open", arrowtail="none"];
+"15" -> "23" [arrowhead="open", arrowtail="none"];
 "16" -> "4" [arrowhead="open", arrowtail="none"];
 "16" -> "23" [arrowhead="open", arrowtail="none"];
 "17" -> "2" [arrowhead="open", arrowtail="none"];
diff --git a/uml_diagrams/packages_uml_ohmpi.dot.png b/uml_diagrams/packages_uml_ohmpi.dot.png
index f69d4ef240878b1c8cd6aa39ccc36c315bb221db..ac4fcbfe2fe7a3e9762808694e8ad277a777f9ec 100644
Binary files a/uml_diagrams/packages_uml_ohmpi.dot.png and b/uml_diagrams/packages_uml_ohmpi.dot.png differ