diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index c5c76ac355545a2697924424c7d9903ee2edd6ec..7e58ed7f7ec47da9a3775722921341e0a2b829d1 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -8,6 +8,7 @@ pages:
   - apt-get install --assume-yes pandoc
   - pip install numpy pandas termcolor paho-mqtt zmq  # top import of Ohmpi.py
   - pip install sphinx numpydoc sphinx_rtd_theme pandoc recommonmark
+  - cp configs/config_dummy.py ohmpi/config.py  # only compile doc with dummy if not on rpi with correct config.py
   - cd doc
   - make html
   
diff --git a/dev/test_inv.py b/dev/test_inv.py
index 2b9d9cef0570b5dc52ac3e5b8b306f531191eefe..e3cfc815779a481f30eeb89e462eb61ce7efb93c 100644
--- a/dev/test_inv.py
+++ b/dev/test_inv.py
@@ -6,6 +6,7 @@ matplotlib.use('TkAgg')
 import numpy as np
 from ohmpi.utils import change_config
 change_config('../configs/config_mb_2023.py', verbose=False)
+import os
 
 from ohmpi.ohmpi import OhmPi
 k = OhmPi()
@@ -14,7 +15,7 @@ k = OhmPi()
 
 # batch inversion
 xzv = k.run_inversion([
-    'measurement_20231014T133508.csv'
+    'measurements_20231130T215031.csv'
 ], reg_mode=0)
 
 # make a contour figure with the output
diff --git a/dev/test_mb_2023_4_mux_2023.py b/dev/test_mb_2023_4_mux_2023.py
index 72cc18ce729df763826f03ee83aa143493c79d11..a10319f3d4b2fc34ca842597d575bc0b3d9a82f5 100644
--- a/dev/test_mb_2023_4_mux_2023.py
+++ b/dev/test_mb_2023_4_mux_2023.py
@@ -88,8 +88,8 @@ if within_ohmpi:
     k.load_sequence(os.path.join(os.path.dirname(__file__), '../sequences/wenner16.txt'))
     k.reset_mux()
     # k.run_multiple_sequences(sequence_delay=20, nb_meas=3)
-    k.run_sequence(injection_duration=0.2)
-    # k.rs_check(tx_volt=4)
+    # k.run_sequence(injection_duration=0.2)
+    k.rs_check(tx_volt=4)
     # k.test_mux(mux_id=None, activation_time=0.2)
     # k._hw.switch_mux([A, B, M, N], state='on')
     # k._hw.vab_square_wave(12.,1., cycles=2)
diff --git a/index.html b/index.html
index 5077a26640f318bf93168c7b2d8e3348cc9f170b..21caa76d685b42d74567744124ced49aaa490b7f 100755
--- a/index.html
+++ b/index.html
@@ -93,12 +93,12 @@ mosquitto_sub -h raspberrypi.local -t ohmpi_0001/ctrl
                 </div>
                 <div class="modal-body">
                     <form>
-                        <div class="form-group row">
-                          <!-- <label for="nb_electrodes" class="col-sm-2 col-form-label">Nb electrodes</label> -->
+                        <!-- <div class="form-group row">
+                          <label for="nb_electrodes" class="col-sm-2 col-form-label">Nb electrodes</label>
                           <div class="col-sm-10">
                             <input type="number" class="form-control-number" id="nb_electrodes" value="64">
                           </div>
-                        </div>
+                        </div> -->
                         <div class="form-group row">
                             <label for="injection_duration" class="col-sm-2 col-form-label">Injection duration [s]</label>
                             <div class="col-sm-10">
@@ -167,7 +167,7 @@ mosquitto_sub -h raspberrypi.local -t ohmpi_0001/ctrl
                 </div>
                 <div class="modal-footer">
                     <button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
-                    <button id="removeDataBtn" type="button" class="btn btn-danger">Clear data</button>
+                    <button id="removeDataBtn" type="button" class="btn btn-danger" data-dismiss="modal">Clear data</button>
                 </div>
             </div>
             </div>
@@ -249,7 +249,7 @@ mosquitto_sub -h raspberrypi.local -t ohmpi_0001/ctrl
                     payload = payload.replace(/\bnan\b/g, "null");
 
                     // parse to json
-                    let ddic = JSON.parse(payload.split('INFO:')[1])
+                    let ddic = JSON.parse(payload.split('INFO:')[1].replace(/'/g, '"'))
 
                     // RS check data
                     if ('rsdata' in ddic) {
@@ -274,7 +274,7 @@ mosquitto_sub -h raspberrypi.local -t ohmpi_0001/ctrl
 
                 } else if (message.topic == topic_exec) {
                     // display it in the log
-                    console.log('EXEC LOG:', payload)
+                    //console.log('EXEC LOG:', payload)
                 }
 
                 // let response = JSON.parse(message.payloadString)
@@ -677,7 +677,7 @@ mosquitto_sub -h raspberrypi.local -t ohmpi_0001/ctrl
             
 
             // update list of quadrupoles if any
-            if (quads.length == 0) {
+            if ((quads.length == 0) & (surveyNames.length > 0)) {
                 console.log('updating list of quadrupoles')
                 let df = data[surveyNames[0]]
                 let quadSelect = document.getElementById('quadSelect')
diff --git a/install.sh b/install.sh
index b63860c8553c9903be2aa8f88bc02f0adc1e05fa..23f8c6649d54481ef5425f778d05ca1e15715293 100755
--- a/install.sh
+++ b/install.sh
@@ -1,8 +1,14 @@
 #!/bin/bash
 
+echo "=========== OhmPi installation script v1.0.0 =========="
+
+echo "--------- Installing additional libraries via apt --------"
+
 # ensure that the libatlas-base-dev library is installed
 sudo apt-get install -y libatlas-base-dev libopenblas-dev
 
+echo "----------- Creating virtual python environment ----------"
+
 # Create the virtual environment
 python3 -m venv ohmpy
 
@@ -15,10 +21,14 @@ export CFLAGS=-fcommon
 # install all required packages in the virtual environment.
 pip install -r requirements.txt
 
+echo "----------- Setting RPi parameters (I2C, GPIO, ...) --------"
+
 # install a second i2c bus
 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
 
+echo "-------------- Install local MQTT broker --------------"
+
 # install local mqtt broker
 txtred='\e[0;31m' # Red
 txtgrn='\e[0;32m' # Green
@@ -33,10 +43,15 @@ echo -e "\n${txtgrn}>>> Broker is installed. Starting now...${txtdef}"
 mosquitto -v 
 
 echo -e "\n${txtgrn}>>> Updating configuration to allow anonymous remote connections and websockets...${txtdef}"
-echo "listener 9001" | sudo tee -a /etc/mosquitto/mosquitto.conf
 echo "listener 1883" | sudo tee -a /etc/mosquitto/mosquitto.conf
+echo "listener 9001" | sudo tee -a /etc/mosquitto/mosquitto.conf
 echo "protocol websockets" | sudo tee -a /etc/mosquitto/mosquitto.conf
 echo "socket_domain ipv4" | sudo tee -a /etc/mosquitto/mosquitto.conf
 echo "allow_anonymous true" | sudo tee -a /etc/mosquitto/mosquitto.conf
 echo -e "\n${txtgrn}>>> Current configuration stored in /etc/mosquitto/mosquitto.conf is displayed below${txtdef}" 
+
 cat /etc/mosquitto/mosquitto.conf
+
+echo "------------- Create config.py file -----------"
+
+python setup_config.py
diff --git a/install_resipy.sh b/install_resipy.sh
old mode 100644
new mode 100755
index 4201bde3df2b67bd44bb737324b3b645be890a50..e204ca1c560fe23eaaf8b5341103d98fb5616cab
--- a/install_resipy.sh
+++ b/install_resipy.sh
@@ -1 +1,8 @@
-git clone -b rpi https://gitlab.com/hkex/resipy
\ No newline at end of file
+echo "Installing ResIPy (cloning git, adding python packages)"
+source ohmpy/bin/activate
+which pip
+sudo apt-get install wine --assume-yes
+cd ..
+git clone -b rpi https://gitlab.com/hkex/resipy
+pip install numpy matplotlib scipy pandas requests psutil
+cd OhmPi
diff --git a/ohmpi/config.py b/ohmpi/config.py
index 49e24ecf21e05b67d55dbc77de65e46ae3c216aa..f3f566ba70a9fb3bf7797821b95578b28d48a232 100644
--- a/ohmpi/config.py
+++ b/ohmpi/config.py
@@ -21,38 +21,32 @@ r_shunt = 2.
 HARDWARE_CONFIG = {
     'ctl': {'model': 'raspberry_pi'},
     'pwr': {'model': 'pwr_batt', 'voltage': 12., 'interface_name': 'none'},
-    'tx':  {'model': 'mb_2024_0_2',
+    'tx':  {'model': 'mb_2023_0_X',
              'voltage_max': 50.,  # Maximum voltage supported by the TX board [V]
              'current_max': 4.80/(50*r_shunt),  # Maximum voltage read by the current ADC on the TX board [A]
              'r_shunt': r_shunt,  # Shunt resistance in Ohms
              'interface_name': 'i2c'
             },
-    'rx':  {'model': 'mb_2024_0_2',
-             'latency': 0.010,  # latency in seconds in continuous mode
-             'sampling_rate': 50,  # number of samples per second
-             'interface_name': 'i2c'
+    'rx':  {'model': 'mb_2023_0_X',
+            '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': {'boards':
-                {'mux_00':
-                     {'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+0, j): ('mux_00', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)},
-                      'voltage_max': 12.}
-                 },
-             'default': {'interface_name': 'i2c_ext',
+    '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.}
-            }
-    }
+             }
+}
 
 # 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',
@@ -76,8 +70,8 @@ DATA_LOGGING_CONFIG = {
 # State of Health logging configuration (For a future release)
 SOH_LOGGING_CONFIG = {
     'logging_level': logging.INFO,
-    'log_file_logging_level': logging.DEBUG,
     'logging_to_console': True,
+    'log_file_logging_level': logging.DEBUG,
     'file_name': f'soh{logging_suffix}.log',
     'max_bytes': 16777216,
     'backup_count': 1024,
diff --git a/ohmpi/hardware_components/pwr_dps5005.py b/ohmpi/hardware_components/pwr_dps5005.py
index 55f833278b8bb56e9934a6e7b524dfcf28980e6c..5f91ac836d9b6e8454c3ab23c70d3448fe27a94f 100644
--- a/ohmpi/hardware_components/pwr_dps5005.py
+++ b/ohmpi/hardware_components/pwr_dps5005.py
@@ -40,6 +40,9 @@ class Pwr(PwrAbstract):
         if not subclass_init:
             self.exec_logger.event(f'{self.model}\tpwr_init\tend\t{datetime.datetime.utcnow()}')
 
+    def _retrieve_current(self):
+        self._current = self.connection.read_register(0x0000, 2)
+
     @property
     def current(self):
         return self._current
diff --git a/ohmpi/ohmpi.py b/ohmpi/ohmpi.py
index 7dafe9f72649c82f82d431bcf8f59fd5c6145e17..46b242dd40e86fbb195a00818b9d4f93fb01f7a5 100644
--- a/ohmpi/ohmpi.py
+++ b/ohmpi/ohmpi.py
@@ -152,8 +152,7 @@ class OhmPi(object):
         for i in getmembers(deprecated, isfunction):
             setattr(cls, i[0], i[1])
 
-    @staticmethod
-    def append_and_save(filename: str, last_measurement: dict, fw_in_csv=None, fw_in_zip=None, cmd_id=None):
+    def append_and_save(self, filename: str, last_measurement: dict, fw_in_csv=None, fw_in_zip=None, cmd_id=None):
         """Appends and saves the last measurement dict.
 
         Parameters
@@ -185,34 +184,35 @@ class OhmPi(object):
 
         last_measurement = deepcopy(last_measurement)
         
-        # save full waveform data in a long .csv file
-        if fw_in_zip:
-            fw_filename = filename.replace('.csv', '_fw.csv')
-            if not os.path.exists(fw_filename):  # new file, write headers first
-                with open(fw_filename, 'w') as f:
-                    f.write('A,B,M,N,t,current,voltage\n')
-            # write full data
-            with open(fw_filename, 'a') as f:
-                dd = last_measurement['full_waveform']
-                aa = np.repeat(last_measurement['A'], dd.shape[0])
-                bb = np.repeat(last_measurement['B'], dd.shape[0])
-                mm = np.repeat(last_measurement['M'], dd.shape[0])
-                nn = np.repeat(last_measurement['N'], dd.shape[0])
-                fwdata = np.c_[aa, bb, mm, nn, dd]
-                np.savetxt(f, fwdata, delimiter=',', fmt=['%d', '%d', '%d', '%d', '%.3f', '%.3f', '%.3f'])
-
-        if fw_in_csv:
-            d = last_measurement['full_waveform']
-            n = d.shape[0]
-            if n > 1:
-                idic = dict(zip(['i' + str(i) for i in range(n)], d[:, 0]))
-                udic = dict(zip(['u' + str(i) for i in range(n)], d[:, 1]))
-                tdic = dict(zip(['t' + str(i) for i in range(n)], d[:, 2]))
-                last_measurement.update(idic)
-                last_measurement.update(udic)
-                last_measurement.update(tdic)
-
-        last_measurement.pop('full_waveform')
+        if 'full_waveform' in last_measurement:
+            # save full waveform data in a long .csv file
+            if fw_in_zip:
+                fw_filename = filename.replace('.csv', '_fw.csv')
+                if not os.path.exists(fw_filename):  # new file, write headers first
+                    with open(fw_filename, 'w') as f:
+                        f.write('A,B,M,N,t,current,voltage\n')
+                # write full data
+                with open(fw_filename, 'a') as f:
+                    dd = last_measurement['full_waveform']
+                    aa = np.repeat(last_measurement['A'], dd.shape[0])
+                    bb = np.repeat(last_measurement['B'], dd.shape[0])
+                    mm = np.repeat(last_measurement['M'], dd.shape[0])
+                    nn = np.repeat(last_measurement['N'], dd.shape[0])
+                    fwdata = np.c_[aa, bb, mm, nn, dd]
+                    np.savetxt(f, fwdata, delimiter=',', fmt=['%d', '%d', '%d', '%d', '%.3f', '%.3f', '%.3f'])
+
+            if fw_in_csv:
+                d = last_measurement['full_waveform']
+                n = d.shape[0]
+                if n > 1:
+                    idic = dict(zip(['i' + str(i) for i in range(n)], d[:, 0]))
+                    udic = dict(zip(['u' + str(i) for i in range(n)], d[:, 1]))
+                    tdic = dict(zip(['t' + str(i) for i in range(n)], d[:, 2]))
+                    last_measurement.update(idic)
+                    last_measurement.update(udic)
+                    last_measurement.update(tdic)
+
+            last_measurement.pop('full_waveform')
         
         if os.path.isfile(filename):
             # Load data file and append data to it
@@ -265,9 +265,8 @@ class OhmPi(object):
         # get all .csv file in data folder
         if survey_names is None:
             survey_names = []
-        # ddir = os.path.join(os.path.dirname(__file__), '../data/')
-        ddir = self.settings['export_dir']
-        fnames = [fname for fname in os.listdir(ddir) if fname[-4:] == '.csv']
+        ddir = os.path.dirname(self.settings['export_path'])
+        fnames = [fname for fname in os.listdir(ddir) if fname[-4:] == '.csv' and fname[-7:] != '_fw.csv']
         ddic = {}
         if cmd_id is None:
             cmd_id = 'unknown'
@@ -418,8 +417,7 @@ class OhmPi(object):
             Unique command identifier.
         """
         self.exec_logger.debug(f'Removing all data following command {cmd_id}')
-        datadir = os.path.split(self.settings['export_path'])
-        #datadir = os.path.join(os.path.dirname(__file__), '../data')
+        datadir = os.path.dirname(self.settings['export_path'])
         rmtree(datadir)
         os.mkdir(datadir)
 
@@ -524,9 +522,11 @@ class OhmPi(object):
             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']
+                if delay > injection_duration:
+                    delay = injection_duration
             else:
                 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))
+            x = np.where((self._hw.readings[:, 0] >= delay) & (self._hw.readings[:, 2] != 0))[0]
             Vmn = self._hw.last_vmn(delay=delay)
             Vmn_std = self._hw.last_vmn_dev(delay=delay)
             I =  self._hw.last_iab(delay=delay)
@@ -1050,7 +1050,7 @@ class OhmPi(object):
         # get absolule filename
         fnames = []
         for survey_name in survey_names:
-            fname = os.path.join(self.settings['export_path'], survey_name)
+            fname = os.path.join(os.path.dirname(self.settings['export_path']), survey_name)
             if os.path.exists(fname):
                 fnames.append(fname)
             else:
diff --git a/run_http_interface_on_start.txt b/run_http_interface_on_start.txt
old mode 100755
new mode 100644
diff --git a/sequences/wenner16.txt b/sequences/wenner16.txt
new file mode 100644
index 0000000000000000000000000000000000000000..dd4d2abe18e622d90ccfe792173fa45f7a2fd0cb
--- /dev/null
+++ b/sequences/wenner16.txt
@@ -0,0 +1,35 @@
+16 19 17 18
+17 20 18 19
+18 21 19 20
+19 22 20 21
+20 23 21 22
+21 24 22 23
+22 25 23 24
+23 26 24 25
+24 27 25 26
+25 28 26 27
+26 29 27 28
+27 30 28 29
+28 31 29 30
+16 22 18 20
+17 23 19 21
+18 24 20 22
+19 25 21 23
+20 26 22 24
+21 27 23 25
+22 28 24 26
+23 29 25 27
+24 30 26 28
+25 31 27 29
+16 25 19 22
+17 26 20 23
+18 27 21 24
+19 28 22 25
+20 29 23 26
+21 30 24 27
+22 31 25 28
+16 28 20 24
+17 29 21 25
+18 30 22 26
+19 31 23 27
+16 31 21 26
diff --git a/setup_config.py b/setup_config.py
new file mode 100644
index 0000000000000000000000000000000000000000..c92871d43fe062481a4fc13e3c7853a458240433
--- /dev/null
+++ b/setup_config.py
@@ -0,0 +1,36 @@
+
+print('This assistent helps you configure a basic system with a measurement board and from 0 to 4 mux of the same type. For more complex configuration with multiple mux of different types, please have a look in the configs/ folder.')
+
+mb = None
+while True:
+    if mb in ['v2023', 'v2024']:
+        break
+    else:
+        mb = input('Choose a measurement boards: [v2023/v2024]: ')
+
+mux = None
+while True:
+    if mux in ['v2023', 'v2024']:
+        break
+    else:
+        mux = input('Choose a mux boards: [v2023/v2024]: ')
+
+nb_mux = None
+while True:
+    if nb_mux in ['0', '1', '2', '3', '4']:
+        break
+    else:
+        nb_mux = input('Number of multiplexers: [0, 1, 2, 3, 4]: ')
+        
+        
+config = 'config_mb_' + mb[1:] + '_' + nb_mux + '_mux_' + mux[1:] + '.py'
+print('Using this configuration: ' + config)
+
+import os
+import shutil
+if os.path.exists('configs/' + config):
+    shutil.copyfile('configs/' + config, 'ohmpi/config.py')
+else:
+    print('configuration not found')
+
+
diff --git a/test/tests.py b/test/tests.py
new file mode 100644
index 0000000000000000000000000000000000000000..797e619c5e5da51c985dbb2a824ef6d2ec414a26
--- /dev/null
+++ b/test/tests.py
@@ -0,0 +1,101 @@
+import importlib
+import time
+import unittest
+import logging
+import mqtt
+import traceback
+from ohmpi.config import HARDWARE_CONFIG
+from ohmpi.ohmpi import OhmPi
+from ohmpi.hardware_system import OhmPiHardware
+from ohmpi.logging_setup import setup_loggers
+
+
+def test_i2c_devices_on_bus(i2c_addr, bus):
+    i2C_addresses_on_bus = [hex(k) for k in bus.scan()]
+    if i2c_addr in i2C_addresses_on_bus:
+        return True
+    else:
+        return False
+
+
+class OhmPiTests(unittest.TestCase):
+    """
+    OhmPiTests class .
+    """
+    def __init__(self):
+        # set loggers
+        self.exec_logger, _, self.data_logger, _, self.soh_logger, _, _, msg = setup_loggers(mqtt=mqtt)
+        print(msg)
+
+        # specify loggers when instancing the hardware
+        self._hw = OhmPiHardware(**{'exec_logger': self.exec_logger, 'data_logger': self.data_logger,
+                                    'soh_logger': self.soh_logger})
+        self.exec_logger.info('Hardware configured...')
+        self.exec_logger.info('OhmPi tests ready to start...')
+
+    def test_connections(self):
+
+    def test_tx_connections(self):
+        i2c_addresses = self._hw.rx.connection
+
+    def test_rx(self):
+        pass
+
+    def test_pwr(self):
+        pass
+
+    def test_mux(self):
+        pass
+
+    def test_i2c_mux_boards(self):
+        try:
+            pass
+        except:
+            traceback.print_exc()
+            self.fail()
+
+    def test_i2c_measurement_board(self):
+        try:
+            pass
+        except:
+            traceback.print_exc()
+            self.fail()
+
+    def test_pwr_connection(self):
+        if self._hw.tx.pwr.voltage_adjustable:
+            try:
+                pass
+            except:
+                traceback.print_exc()
+                self.fail()
+        else:
+            self.exec_logger.info('Pwr cannot be tested with this system configuration.')
+
+    def test_vmn_hardware_offset(self):
+        pass
+
+    def test_r_shunt(self):
+        if self._hw.tx.pwr.voltage_adjustable:
+            pass
+        else:
+            self.exec_logger.info('r_shunt cannot be tested with this system configuration.')
+
+    def test_mqtt_broker(self):
+        pass
+
+    def test_mux(self):
+        self._hw.test_mux()
+
+
+def main(self):
+    import sys
+
+    suite = unittest.defaultTestLoader.loadTestsFromTestCase(OhmPiTests)
+    runner = unittest.TextTestRunner(verbosity=4)
+    result = runner.run(suite)
+    if not result.wasSuccessful():
+        sys.exit(1)
+
+
+if __name__ == '__main__':
+    main()
\ No newline at end of file