diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 7501d059267552b510b5eb1e2a6529b57428e592..6e1207196a145cf6525fbe61067b13e19bd9cb53 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -6,15 +6,16 @@ pages:
   script:
   - apt-get update
   - apt-get install --assume-yes pandoc
-  - pip install sphinx numpydoc sphinx_rtd_theme sphinx_nbexamples pandoc
+  - pip install numpy pandas termcolor  # top import of Ohmpi.py
+  - pip install sphinx numpydoc sphinx_rtd_theme pandoc recommonmark
   - cd doc
   - make html
   # also make latex? pdf?
   - cd ..
-  - mv doc/_build_html/ public/
+  - mv doc/build/html/ public/
   - ls public/
   artifacts:
     paths:
     - public/
   only:
-  - master
\ No newline at end of file
+  - master
diff --git a/Ohmpi.py b/Ohmpi.py
index c7302d1f9a68cc2ad09940988639307e2adf21e7..81ca7339364e9487f2e4e1f813a860fea3357c58 100644
--- a/Ohmpi.py
+++ b/Ohmpi.py
@@ -1,7 +1,9 @@
+# -*- coding: utf-8 -*-
 """
 created on January 6, 2020
 Update February 2022
-Ohmpi.py is a program to control a low-cost and open hardward resistivity meter OhmPi that has been developed by Rémi CLEMENT(INRAE),Vivien DUBOIS(INRAE),Hélène GUYARD(IGE), Nicolas FORQUET (INRAE), and Yannick FARGIER (IFSTTAR).
+Ohmpi.py is a program to control a low-cost and open hardward resistivity meter OhmPi that has been developed by Rémi CLEMENT (INRAE),Vivien DUBOIS (INRAE),Hélène GUYARD (IGE), Nicolas FORQUET (INRAE), Yannick FARGIER (IFSTTAR)
+and Guillaume BLANCHY (ILVO).
 """
 
 VERSION = '2.0.0'
@@ -28,16 +30,6 @@ from datetime import datetime
 from termcolor import colored
 import threading
 
-if True:
-    import board, busio, adafruit_tca9548a
-    import adafruit_ads1x15.ads1115 as ADS
-    from adafruit_ads1x15.analog_in import AnalogIn
-    from adafruit_mcp230xx.mcp23008 import MCP23008
-    from adafruit_mcp230xx.mcp23017 import MCP23017
-    import digitalio
-    from digitalio import Direction
-    from gpiozero import CPUTemperature
-
 current_time = datetime.now()
 print(current_time.strftime("%Y-%m-%d %H:%M:%S"))
 
@@ -50,22 +42,22 @@ print(current_time.strftime("%Y-%m-%d %H:%M:%S"))
 
 
 class OhmPi(object):
+    """Create the main OhmPi object.
+
+    Parameters
+    ----------
+    config : str, optional
+        Path to the .json configuration file.
+    sequence : str, optional
+        Path to the .txt where the sequence is read. By default, a 1 quadrupole
+        sequence: 1, 2, 3, 4 is used.
+    onpi : bool, optional
+        True if running on the RaspberryPi. False for testing (random data generated).
+    output : str, optional
+        Either 'print' for a console output or 'mqtt' for publication onto
+        MQTT broker.
+    """
     def __init__(self, config=None, sequence=None, onpi=True, output='print'):
-        """Create the main OhmPi object.
-
-        Parameters
-        ----------
-        config : str, optional
-            Path to the .json configuration file.
-        sequence : str, optional
-            Path to the .txt where the sequence is read. By default, a 1 quadrupole
-            sequence: 1, 2, 3, 4 is used.
-        onpi : bool, optional
-            True if running on the RaspberryPi. False for testing (random data generated).
-        output : str, optional
-            Either 'print' for a console output or 'mqtt' for publication onto
-            MQTT broker.
-        """
         # flags and attributes
         self.onpi = onpi  # True if run from the RaspberryPi with the hardware, otherwise False for random data
         self.output = output # type of output print
@@ -73,6 +65,18 @@ class OhmPi(object):
         self.run = False  # flag is True when measuring
         self.thread = None  # contains the handle for the thread taking the measurement
         self.path = 'data/' # wher to save the .csv
+        
+        # finish import (done only when class is instantiated as some libs are
+        # only available on arm64 platform)
+        if self.onpi:
+          import board, busio, adafruit_tca9548a
+          import adafruit_ads1x15.ads1115 as ADS
+          from adafruit_ads1x15.analog_in import AnalogIn
+          from adafruit_mcp230xx.mcp23008 import MCP23008
+          from adafruit_mcp230xx.mcp23017 import MCP23017
+          import digitalio
+          from digitalio import Direction
+          from gpiozero import CPUTemperature
 
         # read in hardware parameters (seetings.py)
         self._read_hardware_parameters()
@@ -590,8 +594,8 @@ class OhmPi(object):
         self.dump('status = ' + self.status)
 
 # test
-ohmpi = OhmPi(config='ohmpi_param.json')
-ohmpi.measure()
-time.sleep(4)
-ohmpi.stop()
+#ohmpi = OhmPi(config='ohmpi_param.json')
+#ohmpi.measure()
+#time.sleep(4)
+#ohmpi.stop()
 
diff --git a/Ohmpi_4elec.py b/Ohmpi_4elec.py
deleted file mode 100644
index ce3b060c1a32fa3b78366cd634655f0d701e05fe..0000000000000000000000000000000000000000
--- a/Ohmpi_4elec.py
+++ /dev/null
@@ -1,285 +0,0 @@
-"""
-created on september , 2021
-Update september 2021
-Ohmpi_4elec.py is a program to control a low-cost and open hardward resistivity meter OhmPi that has been developed by Rémi CLEMENT(INRAE),Vivien DUBOIS(INRAE),Hélène GUYARD(IGE), Nicolas FORQUET (INRAE), Oliver KAUFMANN (UMONS) and Yannick FARGIER (IFSTTAR).
-"""
-
-print('\033[1m'+'\033[31m'+' ________________________________')
-print('|  _  | | | ||  \/  || ___ \_   _|')
-print('| | | | |_| || .  . || |_/ / | |' ) 
-print('| | | |  _  || |\/| ||  __/  | |')  
-print('\ \_/ / | | || |  | || |    _| |_') 
-print(' \___/\_| |_/\_|  |_/\_|    \___/ ')
-print('\033[0m')
-print('OhmPi 4 elec  start' )
-print('Vers: 1.01')
-print('Import library')
-
-
-
-import time , board, busio, numpy, os, sys, json, glob,os.path,adafruit_tca9548a
-import adafruit_ads1x15.ads1115 as ADS
-from adafruit_ads1x15.analog_in import AnalogIn
-from pandas import DataFrame
-from datetime import datetime
-from adafruit_mcp230xx.mcp23008 import MCP23008
-from adafruit_mcp230xx.mcp23017 import MCP23017
-from termcolor import colored
-import digitalio
-from digitalio import Direction
-from gpiozero import CPUTemperature
-current_time = datetime.now()
-print(current_time.strftime("%Y-%m-%d %H:%M:%S"))
-"""
-hardware parameters
-"""
-R_shunt = 2# reference resistance value in ohm
-Imax=4800/50/2
-print(colored(('The maximum current cannot be higher than ',Imax, 'mA'), 'red'))
-
-coef_p2 = 2.50# slope for current conversion for ADS.P2, measurement in V/V
-coef_p3 = 2.50 # slope for current conversion for ADS.P3, measurement in V/V
-offset_p2= 0
-offset_p3= 0
-integer=2 #Max value 10
-meas=numpy.zeros((3,integer))
-
-"""
-import parameters
-"""
-
-with open('ohmpi_param.json') as json_file:
-    pardict = json.load(json_file)
-
-
-i2c = busio.I2C(board.SCL, board.SDA) #activation du protocle I2C
-mcp = MCP23008(i2c, address=0x20) #connexion I2C MCP23008, injection de courant
-ads_current = ADS.ADS1115(i2c, gain=2/3,data_rate=128, address=0X48)# connexion ADS1115, pour la mesure de courant
-ads_voltage = ADS.ADS1115(i2c, gain=2/3,data_rate=128, address=0X49)# connexion ADS1115, pour la mesure de voltage
-#initialisation desvoie pour la polarité
-pin0 = mcp.get_pin(0)
-pin0.direction = Direction.OUTPUT
-pin1 = mcp.get_pin(1)
-pin1.direction = Direction.OUTPUT
-pin0.value = False
-pin1.value = False
-"""
-functions
-"""
-# function swtich_mux select the right channels for the multiplexer cascade for electrodes A, B, M and N.
-def switch_mux_on(quadripole):
-    elec_adress=[0x76,0X71,0x74,0x70]
-      
-    for i in range(0,4):
-        tca= adafruit_tca9548a.TCA9548A(i2c, elec_adress[i]) #choose MUX A B M or N
-        
-        if quadripole[i] < 17:
-            nb_i2C=7
-            a=quadripole[i]
-        elif quadripole[i] > 16 and quadripole[i] < 33:    
-            nb_i2C=6
-            a=quadripole[i]-16
-        elif quadripole[i] > 32 and quadripole[i] < 49:    
-            nb_i2C=5
-            a=quadripole[i]-32
-        elif quadripole[i] > 48 and quadripole[i] < 65:    
-            nb_i2C=4
-            a=quadripole[i]-48
-              
-        mcp2 = MCP23017(tca[nb_i2C])     
-        mcp2.get_pin(a-1).direction=digitalio.Direction.OUTPUT
-        mcp2.get_pin(a-1).value=True
- 
-def switch_mux_off(quadripole):
-    elec_adress=[0x76,0X71,0x74,0x70]
-      
-    for i in range(0,4):
-        tca= adafruit_tca9548a.TCA9548A(i2c, elec_adress[i]) #choose MUX A B M or N
-        
-        if quadripole[i] < 17:
-            nb_i2C=7
-            a=quadripole[i]
-        elif quadripole[i] > 16 and quadripole[i] < 33:    
-            nb_i2C=6
-            a=quadripole[i]-16
-        elif quadripole[i] > 32 and quadripole[i] < 49:    
-            nb_i2C=5
-            a=quadripole[i]-32
-        elif quadripole[i] > 48 and quadripole[i] < 65:    
-            nb_i2C=4
-            a=quadripole[i]-48
-              
-        mcp2 = MCP23017(tca[nb_i2C])     
-        mcp2.get_pin(a-1).direction=digitalio.Direction.OUTPUT
-        mcp2.get_pin(a-1).value=False
-       
-
-#function to switch  off mux
-def ZERO_mux(nb_elec):
-    elec_adress=[0x76,0X71,0x74,0x70]
-    for i in range(0,4):
-        tca= adafruit_tca9548a.TCA9548A(i2c, elec_adress[i]) #choose MUX A B M or N
-        for y in range(0,nb_elec):
-            qd=y+1
-            if qd < 17:
-                nb_i2C=7
-                a=qd
-            elif qd > 16 and qd < 33:    
-                nb_i2C=6
-                a=qd-16
-            elif qd > 32 and qd < 49:    
-                nb_i2C=5
-                a=qd-32
-            elif qd > 48 and qd < 65:    
-                nb_i2C=4
-                a=qd-48
-                  
-            mcp2 = MCP23017(tca[nb_i2C])     
-            mcp2.get_pin(a-1).direction=digitalio.Direction.OUTPUT
-            mcp2.get_pin(a-1).value= False
-
-# function to find rows with identical values in different columns
-def find_identical_in_line(array_object):
-    output = []
-    if array_object.ndim == 1:
-        temp = numpy.zeros(4)
-        for i in range(len(array_object)):
-            temp[i] = numpy.count_nonzero(array_object == array_object[i])
-        if any(temp > 1):
-            output.append(0)
-    else:
-        for i in range(len(array_object[:,1])):
-            temp = numpy.zeros(len(array_object[1,:]))
-            for j in range(len(array_object[1,:])):
-                temp[j] = numpy.count_nonzero(array_object[i,:] == array_object[i,j])
-            if any(temp > 1):
-                output.append(i)
-    return output
-# 
-# read quadripole file and apply tests
-def read_quad(filename, nb_elec):
-    output = numpy.loadtxt(filename, delimiter=" ",dtype=int) # load quadripole file
-    # locate lines where the electrode index exceeds the maximum number of electrodes
-    test_index_elec = numpy.array(numpy.where(output > nb_elec))
-    # locate lines where an electrode is referred twice
-    test_same_elec = find_identical_in_line(output)
-    # if statement with exit cases (rajouter un else if pour le deuxième cas du ticket #2)
-    if test_index_elec.size != 0:
-        for i in range(len(test_index_elec[0,:])):
-            print("Error: An electrode index at line "+ str(test_index_elec[0,i]+1)+" exceeds the maximum number of electrodes")
-        sys.exit(1)
-    elif len(test_same_elec) != 0:
-        for i in range(len(test_same_elec)):
-            print("Error: An electrode index is used twice at line " + str(test_same_elec[i]+1))
-        sys.exit(1)
-    else:
-        return output
-
-
-def run_measurement(nb_stack, injection_deltat, R_shunt, coefp2, coefp3):
-    start_time=time.time()
-    # inner variable initialization
-    sum_I=0
-    sum_Vmn=0
-    sum_Ps=0
-    # injection courant and measure
-    mcp = MCP23008(i2c, address=0x20)
-    pin0 = mcp.get_pin(0)
-    pin0.direction = Direction.OUTPUT
-    pin1 = mcp.get_pin(1)
-    pin1.direction = Direction.OUTPUT
-    pin0.value = False
-    pin1.value = False
-    for n in range(0,3+2*nb_stack-1) :        
-        # current injection
-        
-        if (n % 2) == 0:
-            
-            pin1.value = True
-            pin0.value = False # current injection polarity n°1        
-        else:
-            pin0.value = True
-            pin1.value = False# injection de courant polarity n°2
-        start_delay=time.time()#stating measurement time
-        time.sleep(injection_deltat) # delay depending on current injection duration
-        
-# mesureament of i and u       
-        for k in range(0,integer):
-          meas[0,k] = ((AnalogIn(ads_current,ADS.P0).voltage/50)/R_shunt)*1000 # reading current value on ADS channel A0
-          meas[1,k] = AnalogIn(ads_voltage,ADS.P0).voltage * coefp2*1000
-          meas[2,k] = AnalogIn(ads_voltage,ADS.P1).voltage * coefp3*1000 # reading voltage value on ADS channel A2
-        pin1.value = False; pin0.value = False# stop current injection
-        end_delay=time.time()
-        sum_I=sum_I+(numpy.mean(meas[0,:]))
-        Vmn1=((numpy.mean(meas[1,:]))-(numpy.mean(meas[2,:])))
-        if (n % 2) == 0:
-              sum_Vmn=sum_Vmn-Vmn1
-              sum_Ps=sum_Ps+Vmn1
-        else:
-              sum_Vmn=sum_Vmn+Vmn1
-              sum_Ps=sum_Ps+Vmn1
-        
-        cpu = CPUTemperature()
-        
-        end_calc=time.time()
-        time.sleep(2*(end_delay-start_delay)-(end_calc-start_delay))
-        #end_sleep2=time.time()
-        #print(['sleep=',((end_sleep2-start_delay))])
-
-        #print(['true delta=',((end_delay-start_delay)-injection_deltat)])
-        #print(['time stop=',((2*(end_delay-start_delay)-(end_calc-start_delay)))])
-    # return averaged values
-#     cpu= CPUTemperature()
-    output = DataFrame({
-        "time":[datetime.now()],
-        "A":[(1)],
-        "B":[(2)],
-        "M":[(3)],
-        "N":[(4)],
-        "inj time [ms]" : (end_delay-start_delay)*1000,
-        "Vmn [mV]":[(sum_Vmn/(3+2*nb_stack-1))],
-        "I [mA]":[(sum_I/(3+2*nb_stack-1))],
-        "R [ohm]":[( (sum_Vmn/(3+2*nb_stack-1)/(sum_I/(3+2*nb_stack-1))))],
-        "Ps [mV]":[(sum_Ps/(3+2*nb_stack-1))],
-        "nbStack":[nb_stack],
-        "CPU temp [°C]":[cpu.temperature],
-        "Time [S]":[(-start_time+time.time())],
-        "Integer [-]": [integer]
-
-     
-     
-      # Dead time equivalent to the duration of the current injection pulse   
-    })
-    output=output.round(2)
-    print(output.to_string())
-    time.sleep(1)
-    return output
-
-# save data
-def append_and_save(path, last_measurement):
-    
-    if os.path.isfile(path):
-        # Load data file and append data to it
-        with open(path, 'a') as f:
-             last_measurement.to_csv(f, header=False)
-    else:
-        # create data file and add headers
-        with open(path, 'a') as f:
-             last_measurement.to_csv(f, header=True)
-
-
-"""
-Main loop
-"""
-for g in range(0,pardict.get("nbr_meas")): # for time-lapse monitoring
-
-            
-        
-        
-    current_measurement = run_measurement(pardict.get("stack"), pardict.get("injection_duration"), R_shunt, coef_p2, coef_p3)
-        
-    append_and_save(pardict.get("export_path"), current_measurement)
-    
-    
-    
-    time.sleep(pardict.get("sequence_delay")) #waiting next measurement (time-lapse)
diff --git a/doc/docker.sh b/doc/docker.sh
new file mode 100644
index 0000000000000000000000000000000000000000..ba1ccdee08c7b8c407031638c1ae9db692fd28e9
--- /dev/null
+++ b/doc/docker.sh
@@ -0,0 +1,4 @@
+# to test documentation  build using same environemnet as gitlab doc runner
+
+docker run -it -v /media/jkl/data/ilvo/ohmpi/OhmP:/OhmPi python:3.9-bullseye bash
+
diff --git a/doc/running_html.py b/doc/running_html.py
deleted file mode 100644
index 27194cada6965c089aea87a7e4dbdf46e3bb473b..0000000000000000000000000000000000000000
--- a/doc/running_html.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-Created on Mon Jul 13 21:48:00 2020
-
-@author: remi.clement
-"""
-
-import shutil
-
diff --git a/doc/source/Ohmpi.rst b/doc/source/Ohmpi.rst
index 9eb52792be48c3ef115bcbf4ee0caaa0740d2595..5a3cab23f61497ada19f9b7bceedf3980a3155ad 100644
--- a/doc/source/Ohmpi.rst
+++ b/doc/source/Ohmpi.rst
@@ -13,12 +13,12 @@ OhmPi project
 
 **Authors:** 
 ------------
-| Rémi CLEMENT,Vivien DUBOIS,Nicolas Forquet, INRAE, REVERSAAL, Villeurbanne, France.
-| Yannick FARGIER, GERS-RRO, Univ Gustave Eiffel, IFSTTAR, Lyon, France.
-| Julien GANCE, IRIS Instruments, Orléans, France.
-| Hélène GUYARD, IGE Grenoble, Université Grenoble Alpes, Grenoble, France.
+| Rémi CLEMENT,Vivien DUBOIS,Nicolas Forquet, INRAE, REVERSAAL, Villeurbanne, France
+| Yannick FARGIER, GERS-RRO, Univ Gustave Eiffel, IFSTTAR, Lyon, France
+| Julien GANCE, IRIS Instruments, Orléans, France
+| Hélène GUYARD, IGE Grenoble, Université Grenoble Alpes, Grenoble, France
 | Olivier Kaufmann, Université de Mons, Mons, Belgium 
-| Guillaume Banchy, ILVO,Merelbeke, Belgium
+| Guillaume Banchy, ILVO, Merelbeke, Belgium
 |
 
 
@@ -62,4 +62,4 @@ to the near surface scientific community.
  
 
 .. note:: 
-   Everyone willing to get involved is  welcome in OhmPi Project!.
\ No newline at end of file
+   Everyone willing to get involved is  welcome in OhmPi Project!.
diff --git a/doc/source/api.rst b/doc/source/api.rst
new file mode 100644
index 0000000000000000000000000000000000000000..c1157b20f6f7090299118d1b70501ae0202d961a
--- /dev/null
+++ b/doc/source/api.rst
@@ -0,0 +1,9 @@
+API reference
+=============
+.. toctree::
+   :maxdepth: 2
+   :caption: Contents:
+.. automodule:: Ohmpi
+   :members:
+
+
diff --git a/doc/source/conf.py b/doc/source/conf.py
index 803d3705b28c413470cba85220848998b722eb16..d40917b39b5b6356a9a2ea5929b02072cf67942d 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -1,3 +1,5 @@
+# -*- coding: utf-8 -*-
+
 # Configuration file for the Sphinx documentation builder.
 #
 # This file only contains a selection of the most common options. For a full
@@ -12,15 +14,16 @@
 #
 import os
 import sys
-# sys.path.insert(0, os.path.abspath('.'))
+sys.path.append(os.path.abspath('../..'))
+import Ohmpi  # import Ohmpi module to be documented in api.rst by numpydoc
 import sphinx_rtd_theme
 
 
 from shutil import copyfile
 # -- Project information -----------------------------------------------------
 
-project = 'Ohmpi:'
-copyright = '2022, INRAE, Team Ohmpi'
+project = 'OhmPi'
+copyright = '2022, INRAE, Team OhmPi'
 author = 'Rémi CLEMENT'
 
 # The full version, including alpha/beta/rc tags
@@ -30,8 +33,17 @@ release = 'open hardware resistivity-meter'
 # -- General configuration ---------------------------------------------------
 
 
-extensions = ['recommonmark']
+extensions = [
+    'sphinx.ext.autodoc',
+    'sphinx.ext.coverage',
+    'sphinx.ext.mathjax',
+    'numpydoc',
+    'recommonmark'
+]
+
+
 
+numpydoc_show_class_members = False
 
 
 # Add any paths that contain templates here, relative to this directory.
diff --git a/doc/source/index.rst b/doc/source/index.rst
index 7ae7b05dc7509c2e505a4953a7b9efc74767d368..ccbf3e772af1caef76011d907450b62688bddd5a 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -37,6 +37,7 @@ Contents:
    V1_01
    V1_02
    V2_00
+   api